pax_global_header00006660000000000000000000000064121750273170014517gustar00rootroot0000000000000052 comment=bc5294252d78fe2c9ab4610660020d14907e5585 tiled-qt-0.9.1/000077500000000000000000000000001217502731700132515ustar00rootroot00000000000000tiled-qt-0.9.1/AUTHORS000066400000000000000000000043731217502731700143300ustar00rootroot00000000000000Main developer and maintainer: Thorbjørn Lindeijer Contributors: Andrew G. Crowell (added additional ways to access layer related actions) Christian Henz (added handling of non-encoded map data) Dennis Honeyman (implemented the nice about dialog and CSV layer support) Edward Hutchins (added automatic reloading of changed tileset images) Jeff Bland (added Offset Map feature, fill tool and running commands) Michael Woerister (improvements to the object properties dialog) Roderic Morris (added many icons and initial object group support) Stefan Beller (added the automap feature, quick stamps, lines/circles support for multiple maps, random mode for stamp tool and bucketfill tool) Alexander Kuhrt Translators: Alex Vega (Russian) Antonio Ricci (Italian) Gornova (Italian) Bin Wu (Chinese) Hiroki Utsunomiya (Japanese) Mauricio Muñoz Lucero (Spanish) Petr Viktorin (Czech) Porfírio Ribeiro (Portuguese) Jānis Kiršteins (Latvian) Jonatas de Moraes Junior (Brazilian Portuguese) jurkan (German) Sébastien Burillard (French) seeseekey (German) Tamir Atias (KonoM) (Hebrew) Thorbjørn Lindeijer (Dutch) Yohann Ferreira (French) Zhao Sting (Chinese) Acknowledgements: Most of the icons are from the GNOME and GIMP icon themes (GPL) Some Icons are Copyright (C) Yusuke Kamiyamane. All rights reserved. Licensed under a Creative Commons Attribution 3.0 license. See http://p.yusukekamiyamane.com/ The dice icon is based on an SVG by Steaphan Greene that I found on Wikipedia: http://en.wikipedia.org/wiki/File:2-Dice-Icon.svg The rename icon for tilesets is a strong modified version of the 'new Tileset' icon by Stefan Beller (GPL, CC-BY-SA3) Tilesets: perspective_walls.png - (C) Clint Bellanger, released as Public Domain isometric_grass_and_water.png - (C) Clint Bellanger, released as GPL2, GPL3, CC-BY-SA3 tmw_desert_spacing.png - (C) The Mana World Development Team, GPL sewer_tileset.png - (C) Blues Brothers RPG developers, GPL Replica Island - (C) Chris Pruett and Genki Mine, Apache (except hotspots.png) tiled-qt-0.9.1/COPYING000066400000000000000000000013321217502731700143030ustar00rootroot00000000000000Several independent works are being distributed along with Tiled that fall under different licenses. You can find the license that applies to each file in the comment at the top, but for clarity they are also listed here: Software Directory License ----------------------------------------------------------------- Tiled src/tiled GPL libtiled src/libtiled Simplified BSD license tmxviewer src/tmxviewer Simplified BSD license libtiled-java util/java/libtiled-java Simplified BSD license tmxviewer-java util/java/tmxviewer-java Simplified BSD license The full text of each license is provided in the files LICENSE.GPL and LICENSE.BSD. tiled-qt-0.9.1/LICENSE.APACHE000066400000000000000000000261351217502731700152050ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. tiled-qt-0.9.1/LICENSE.BSD000066400000000000000000000023041217502731700146640ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. tiled-qt-0.9.1/LICENSE.GPL000066400000000000000000000432541217502731700147070ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. tiled-qt-0.9.1/NEWS000066400000000000000000000311761217502731700137600ustar00rootroot000000000000000.9.1 (27 July 2013) * Added saving of map background to JSON format (by Petr Viktorin) * Added saving of terrain information to JSON format (by Petr Viktorin) * Object Selection tool now always start selecting objects when holding Shift * Increased maximum for tileset margin and spacing to 9999 * Some updates to libtiled-java (by Oskar Wiksten) * Install the automappingconverter application (relevant on Linux) * Avoid using Windows 95 style (was used on some Linux desktop environments) * Removed layer name checks from the Flare export plugin (by Stefan Beller) * Double-clicking an object now opens the Object Properties dialog * Fixed Object Properties dialog not remembering its size * Fixed object drawing order for image saving and mini-map * Fixed some plurals in English translation * Fixed line widths when zooming in Qt 5 * Fixed updating of image layer when its opacity or image is changed * Fixed display of grid in tileset view on certain zoom levels * Fixed save in wrong format after opening a map with plugin (by Mike Hendricks) * Fixed closing Tiled being very slow with many maps * Fixed saving of image layer properties in the Lua format * Fixed escaping of special characters in the Lua format * Fixed handling of relative paths for image layers in the JSON plugin 0.9.0 (27 January 2013) * Added objects dock and per-object visibility toggle (by Tim Baker) * Added maps dock (by Tim Baker) * Added terrain tool for automatic terrain transitions (by Manu Evans) * Added a minimap (by Christoph Schnackenberg) * Added a staggered isometric map renderer, still without object layer support * Added basic image layer support (by Gregory Nickonov and Alexander Kuhrt) * Added display of current layer to the status bar (by Tim Baker) * Added editable combo box for changing the zoom level (by Tim Baker) * Added support for multiple input layers to automapping (by Stefan Beller) * Added option to apply automapping rules while editing (by Stefan Beller) * Added a converter to update old automapping rules (by Stefan Beller) * Added support for objects layers to automapping (by Stefan Beller) * Added support for random mode to the fill tool (by Stefan Beller) * Added Replica Island plugin (by Eric Kidd) * Added option to change the grid color (by Stefan Beller) * Added support for ellipse objects (by devnewton and Christoph Schnackenberg) * Added name labels for objects on isometric maps (by Andrew Motrenko) * Added map property for changing the background color (by Emmanuel Barroga) * Added shortcut to manually reload tilesets (Ctrl-T) (by Michael Williams) * Added toggle for showing tile object outlines * Added support for pinch zooming (by Pierre-David Bélanger) * Added initial (non-GUI) support for individual and/or embedded tile images (by Petr Viktorin) * Added reading support to Flare plugin (by Stefan Beller) * Added a TMX rasterizer command line tool (by Vincent Petithory) * Added man pages and desktop file (by Erik Schilling) * Made the size and position of most dialogs persistent * Respect the original layer data format of a loaded map (by Ben Longbons) * Marked Tiled as high-resolution capable on Mac OS X * Improved handling of external tilesets in Lua export * Reverted tilesets view back to tabs, but with menu button (by Stefan Beller) * Allowed plugins to support multiple file name filters (by Samuli Tuomola) * Allow saving in any format that can also be read (by Stefan Beller) * Fixed eraser skipping tiles when moving fast * Fixed bug in Flare plugin (by Clint Bellanger) * Fixed compile against Qt 5 (by Kenney Phillis) * Fixed resolving of symbolic links while loading map * Fixed a crash that could happen after trying to load a faulty map * Updated Portuguese, Dutch, German, Spanish, Russian, French, Japanese, Chinese, Brazilian Portuguese, Hebrew and Czech translations 0.8.1 (7 May 2012) * Added MacOS X Lion full screen support * Fixed crash that could happen when painting with a pasted stamp * Fixed zoom sensitivity for finer-resolution mouse wheels * Fixed issues when using quickstamps in combination with the fill tool * Fixed stamp tool not to miss tiles when drawing fast * Fixed automapping to work with external tilesets * Fixed crash in automapping when dealing with broken rule files * Fixed object type getting erased on pressing Enter * Changed the license of libtiled-java from LGPL to BSD * Updated Italian and Hebrew translations 0.8.0 (11 December 2011) * Added support for polygon and polyline objects * Added support for tile rotation * Added support for defining the color of custom object types * Added a Delete action to delete selected tiles or objects * Added random mode to the stamp brush * Added Flare export plugin * Added JSON plugin that supports both reading and writing * Added ability to rename tilesets * Added a mode in which the current layer is highlighted * Added support for specifying a tile drawing offset * Added a shortcut to copy the current tile position to clipboard (Alt+C) * Added a command line option to disable OpenGL * Allow custom properties on tilesets * Many automapping improvements * Improved tileset dock to handle a large amount of tilesets better * Made the 'Show Grid' option in the tileset view persistent * Raised the tile size limit in the New Tileset dialog from 999 to 9999 * Correctly handle changes in the width of a tileset image * Worked around a long standing crash bug * Added Russian translation * Updated the German, Japanese, Spanish, Chinese, Czech, Dutch, French and Brazilian Portuguese translations 0.7.1 (27 September 2011) * Select stamp tool when selecting tiles in tileset view * Enable anti-aliasing for OpenGL mode * Small improvement to the Lua export plugin (incompatible!) * Fixed a bug in the Create Object tool * Fixed reading of maps without tilesets but with a tile layer * Fixed position of tile objects to center on the mouse on insertion * Updated the Czech translation 0.7.0 (20 July 2011) * Added support for horizontal and vertical flipping of tiles * Added copy/paste support for objects * Added merge layer down action * Added Show or Hide all Other Layers action * Added actions to select the previous/next layer * Added Crop to Selection action * Added a Lua export plugin * Added Droidcraft plugin to read and export the map files * Added option to turn off grid in the tileset view * Added hand scrolling while holding the spacebar * Made the object context menu available in all object tools * Display tile coordinates also when using object tools * Various improvements to running external commands * Automapping stability and memory consumption improvements * Objects that fall outside of the map on resize are now removed * Fixed problems with watching tilesets multiple times * Fixed several issues related to restoring previously opened files * Updated Brazilian Portuguese, Chinese, German, Spanish, Japanese, Hebrew, Portuguese, Dutch and French translations 0.6.2 (2 May 2011) * Fixed object layers losing their color when resizing the map * Fixed the tabs in the Tilesets dock to use scroll buttons on MacOS X * Fixed window title to update when saving a map with a different name 0.6.1 (3 April 2011) * Added ability to open multiple files at once * Added Ctrl+PageUp/PageDown shortcuts to switch documents * Added an example to show how automatic mapping works * Fixed bugs, crashes and leaks in the automatic mapping feature * Fixed starting point for circles to be the click position * Fixed a memory leak when using lines or circles * Fixed layer opacity to be taken into account when saving as image * Fixed endless loop when tile size is set to 0 * Fixed crash when passing an empty string as command line parameter * Fixed problems with the tileset view after switching documents * Fixed tile objects to be removed when their tileset is removed 0.6.0 (26 January 2011) * Added support for opening multiple maps in one session * Added support for placing tiles as objects * Added automatic mapping feature, allowing placing of tiles based on rules * Added ability to save/restore up to 9 stamps with Ctrl+ * Added an object selection tool, allowing moving/deleting multiple objects * Added ability to run external commands * Added support for drawing lines and ellipses with the stamp brush * Added icons to distinguish tile layers from object layers * Added "Move To Layer" submenu to the context menu of objects * Added option to use hardware rendering based on OpenGL * Added a T-Engine4 map export plugin * Added a simple TMX viewer application (BSD licensed) * Added a New Layer dropdown menu to the layers dock * Added a checkbox that enables snap to grid permanently * Added an initial version of libtiled-java (LGPL licensed) * Added Chinese and Hebrew translations * Allowed dragging an image onto Tiled to add a tileset * Center the map when it is smaller than the map view * Remember the selected layer across restarts * Changed the default layer data format to use zlib rather than gzip * Store the tileset image width and height in the map file * Compile fixes related to linking zlib * Fixed the current stamp to get updated when switching tilesets * Fixed the maximum sizes of the resize map dialog * Fixed build issues when an older version of libtiled is installed * Fixed saving of property when clicking OK while editing on MacOS X * Allow Backspace to delete properties to make it easier on a MacBook * Associate tmx files with Tiled on MacOS X * Changed the license of libtiled from GPL to BSD * Updated Czech, Spanish, German, Brazilian Portuguese, Dutch and French translations 0.5.1 (2 September 2010) * Fixed saving of objects when tile width is different from tile height * Updated Czech translation 0.5.0 (30 June 2010) * Added support for import and export plugins * Added support for external tilesets * Added undo for adding tilesets and ability to remove tilesets * Added error handling to the New Tileset dialog * Added ability to change tileset order by dragging them around * Added option to draw the tile grid when saving as image * Added a context menu and tool buttons to the layer dock * Added Latvian translation * Added an install target to the Makefile * Open local files when they are dropped onto Tiled * Allow changing position and size of objects in the Object Properties dialog * Fixed rendering issues with tiles wider than the tile width of the map * Fixed eraser and fill tool working on invisible layers * Fixed a crash when using some tools when no map is loaded * Fixed compile errors related to detecting static builds * Fixed the Save dialog not suggesting any particular file extension * Updated Japanese, Dutch, German, Brazilian Portuguese, French, Portuguese and Spanish translations 0.4.1 (14 April 2010) * Added support for saving tile layer data as CSV * Added shift modifier to bucket fill tool for filling the selection * Added Brazilian Portugese, Japanese, French, Italian and Czech translations * Made values used in the New Map and New Tileset dialogs persistent * Fixed drawing selection highlight where brush is not painting * Fixed an incompatibility with Tiled Java in 'trans' attribute 0.4.0 (30 January 2010) * Added support for isometric maps * Added automatic reloading of tileset images when they change * Added Offset Map action that can shift a set of layers by a certain amount * Added a fill tool * Added ability to duplicate map objects * Added support for choosing the tile layer data format used when saving * Added mouse wheel zooming support to the tileset view * Added an object display color attribute to object groups * Added ability to edit tile properties through a context menu * Made writing out a DTD reference optional and disabled it by default * Made translations functional * Updated Dutch, Portuguese, Spanish and German translations 0.3.1 (22 November 2009) * Enabled undo command compression for stamp brush and eraser * Fixed reading of maps with non-binary-encoded layer data * Fixed a compile issue on Mac OS X related to QXmlStreamWriter * Fixed a crash when loading a map while holding Ctrl * Confirm overwrite on the right moment for 'Save as Image' dialog 0.3.0 (13 November 2009) * Added a tile selection tool * Added support for cut, copy and paste * Added current cursor position to the status bar * Added keyboard shortcuts to switch tools * Added scrolling the map view with middle mouse button * Snap objects to the grid when Ctrl is pressed 0.2.0 (1 October 2009) * Added support for zooming the map view * Added an eraser tool that allows you to erase tiles * Added ability to save a map as an image * Added support for masking tileset images based on a certain color * Added a slider to change the opacity of the current layer * Fixed the minimum row and column size in the tileset view * Fixed stamp creation when not dragging topleft to bottomright 0.1.0 (1 September 2009) tiled-qt-0.9.1/README.md000066400000000000000000000052221217502731700145310ustar00rootroot00000000000000Tiled Map Editor - http://www.mapeditor.org/ About Tiled ------------------------------------------------------------------------------- Tiled is a general purpose tile map editor. It is meant to be used for editing maps of any tile-based game, be it an RPG, a platformer or a Breakout clone. Tiled is very flexible, for example there are no restrictions on map size, tile size or the number of layers or tiles. Also, it allows arbitrary properties to be set on the map, its layers, the tiles or on the objects. Its map format (TMX) is relatively easy to understand and allows a map to use multiple tilesets while also allowing each tileset to grow or shrink as necessary later. About the Qt Version ------------------------------------------------------------------------------- Tiled was originally written in Java. In 2008 the Qt version was started with the goal to replace the Java version with a faster, better looking and even easier to use map editor. Qt offered many opportunities to improve the performance and usability of the user interface, and has a more extensive feature set than the standard Java libraries. Compiling ------------------------------------------------------------------------------- Make sure the Qt (>= 4.6) development libraries are installed: * In Ubuntu/Debian: `sudo apt-get install libqt4-dev libqt4-opengl-dev zlib1g-dev` * In Fedora: `yum install qt-devel` * In Arch Linux: `pacman -S qt` * In Mac OS X with [Homebrew](http://mxcl.github.com/homebrew/): `brew install qt` Now you can compile by running: $ qmake (or qmake-qt4 on some systems, like Fedora) $ make To do a shadow build, you can run qmake from a different directory and refer it to tiled.pro, for example: $ mkdir build $ cd build $ qmake ../tiled.pro $ make You can now simply run Tiled using bin/tiled. Installing ------------------------------------------------------------------------------- For installing Tiled you can run `make install`. By default Tiled will install to `/usr/local`. You can change this prefix when running qmake, and/or you can change the install root when running make install, as follows: Use `/usr` instead of `/usr/local`: $ qmake -r PREFIX=/usr (Recursive needed when it's not the first time that you're running qmake, since this affects nested pro files) Install to some packaging directory: $ make install INSTALL_ROOT=/tmp/tiled-pkg By default, Tiled and its plugins are compiled with an Rpath so that they can find the shared libtiled library when running it straight after compile. When packaging for a distribution, this Rpath should generally be disabled by appending `RPATH=no` to the qmake command. tiled-qt-0.9.1/dist/000077500000000000000000000000001217502731700142145ustar00rootroot00000000000000tiled-qt-0.9.1/dist/make-dist-mac.rb000077500000000000000000000056461217502731700171730ustar00rootroot00000000000000#!/usr/bin/ruby # This script generates a mac release from an already compiled Tiled.app in # the bin folder. You should compile the release before running this: # tiled$ qmake -r -spec macx-g++ CONFIG+=release CONFIG+=x86_64 # tiled$ make # tiled$ ./dist/make-dist-mac.rb # Dependencies require 'tmpdir' # Get various directories baseDir = File.join File.dirname(__FILE__), '..' binDir = File.join baseDir, 'bin' binAppDir = File.join binDir, 'Tiled.app' raise "No application at #{binAppDir}" unless File.directory? binAppDir # Get the version from the info plist if ARGV[0] version = ARGV[0] else plistData = File.open(File.join(binAppDir, "Contents/Info.plist")).read version = plistData.match(/CFBundleVersion\<\/key>\s*\([\d\.]+)\<\/string\>/m)[1] end puts "Version is #{version}" # Create a temporary staging directory # This directory will serve as the content for the dmg file Dir.mktmpdir do |tempDir| # Copy things to temp directory puts "Copying files" ['LICENSE.GPL', 'LICENSE.BSD', 'AUTHORS', 'COPYING', 'NEWS', 'README.md'].each do |file| FileUtils.cp File.join(baseDir, file), tempDir end FileUtils.cp_r File.join(baseDir, 'examples'), tempDir FileUtils.cp_r binAppDir, tempDir FileUtils.ln_s '/Applications', File.join(tempDir, 'Applications') #Symlink to Applications for easy install FileUtils.cp File.join(baseDir, 'src/tiled/images/tmx-icon-mac.icns'), File.join(tempDir, 'Tiled.app/Contents/Resources') # Use macdeployqt to copy Qt frameworks to the app puts "Running macdeployqt" `macdeployqt "#{tempDir}/Tiled.app"` raise "macdeployqt error #{$?}" unless $? == 0 # Modify plugins to use Qt frameworks contained within the app bundle (is there some way to get macdeployqt to do this?) Dir["#{File.join tempDir, 'Tiled.app'}/**/*.dylib"].each do |library| ["QtCore", "QtGui"].each do |qtlib| #find any qt dependencies within this library qtdependency = `otool -L "#{library}" | grep #{qtlib}`.split(' ')[0] next unless qtdependency #skip depedencies that are already using relative paths #macdeployqt seems to fix some of the plugins next if qtdependency.include? "@executable_path" #if we get here, this library has a dependency on a qtlib with a hard path on the build systems disk puts "Fixing library #{library} dependency on #{qtlib}" `install_name_tool -change "#{qtdependency}" "@executable_path/../Frameworks/#{qtlib}.framework/Versions/4/#{qtlib}" "#{library}"` raise "install_name_tool error #{$?}" unless $? == 0 end end # Create dmg from the temp directory dmgPath = File.join(baseDir, 'tiled-qt-' + version + '.dmg') puts "Creating dmg at #{dmgPath}" `hdiutil create "#{dmgPath}" -srcfolder "#{tempDir}" -volname "Tiled #{version}"` raise "hdiutil error #{$?}" unless $? == 0 end puts "Done" tiled-qt-0.9.1/dist/make-dist.sh000077500000000000000000000003231217502731700164270ustar00rootroot00000000000000#!/bin/bash if [ "$#" -eq "0" ]; then echo "Usage: make-dist.sh " exit 1 fi name="tiled-qt-$1" git archive -v --prefix="$name/" HEAD | gzip > "$name.tar.gz" echo "Release ready as $name.tar.gz" tiled-qt-0.9.1/dist/win/000077500000000000000000000000001217502731700150115ustar00rootroot00000000000000tiled-qt-0.9.1/dist/win/FileAssociation.nsh000066400000000000000000000110461217502731700206010ustar00rootroot00000000000000/* _____________________________________________________________________________ File Association _____________________________________________________________________________ Based on code taken from http://nsis.sourceforge.net/File_Association Usage in script: 1. !include "FileAssociation.nsh" 2. [Section|Function] ${FileAssociationFunction} "Param1" "Param2" "..." $var [SectionEnd|FunctionEnd] FileAssociationFunction=[RegisterExtension|UnRegisterExtension] _____________________________________________________________________________ ${RegisterExtension} "[executable]" "[extension]" "[description]" "[executable]" ; executable which opens the file format ; "[extension]" ; extension, which represents the file format to open ; "[description]" ; description for the extension. This will be display in Windows Explorer. ; ${UnRegisterExtension} "[extension]" "[description]" "[extension]" ; extension, which represents the file format to open ; "[description]" ; description for the extension. This will be display in Windows Explorer. ; _____________________________________________________________________________ Macros _____________________________________________________________________________ Change log window verbosity (default: 3=no script) Example: !include "FileAssociation.nsh" !insertmacro RegisterExtension ${FileAssociation_VERBOSE} 4 # all verbosity !insertmacro UnRegisterExtension ${FileAssociation_VERBOSE} 3 # no script */ !ifndef FileAssociation_INCLUDED !define FileAssociation_INCLUDED !include Util.nsh !verbose push !verbose 3 !ifndef _FileAssociation_VERBOSE !define _FileAssociation_VERBOSE 3 !endif !verbose ${_FileAssociation_VERBOSE} !define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE` !verbose pop !macro FileAssociation_VERBOSE _VERBOSE !verbose push !verbose 3 !undef _FileAssociation_VERBOSE !define _FileAssociation_VERBOSE ${_VERBOSE} !verbose pop !macroend !macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION !verbose push !verbose ${_FileAssociation_VERBOSE} Push `${_DESCRIPTION}` Push `${_EXTENSION}` Push `${_EXECUTABLE}` ${CallArtificialFunction} RegisterExtension_ !verbose pop !macroend !macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION !verbose push !verbose ${_FileAssociation_VERBOSE} Push `${_EXTENSION}` Push `${_DESCRIPTION}` ${CallArtificialFunction} UnRegisterExtension_ !verbose pop !macroend !define RegisterExtension `!insertmacro RegisterExtensionCall` !define un.RegisterExtension `!insertmacro RegisterExtensionCall` !macro RegisterExtension !macroend !macro un.RegisterExtension !macroend !macro RegisterExtension_ !verbose push !verbose ${_FileAssociation_VERBOSE} Exch $R2 ;exe Exch Exch $R1 ;ext Exch Exch 2 Exch $R0 ;desc Exch 2 Push $0 Push $1 ReadRegStr $1 HKCR $R1 "" ; read current file association StrCmp "$1" "" NoBackup ; is it empty StrCmp "$1" "$R0" NoBackup ; is it our own WriteRegStr HKCR $R1 "backup_val" "$1" ; backup current value NoBackup: WriteRegStr HKCR $R1 "" "$R0" ; set our file association ReadRegStr $0 HKCR $R0 "" StrCmp $0 "" 0 Skip WriteRegStr HKCR "$R0" "" "$R0" WriteRegStr HKCR "$R0\shell" "" "open" WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0" Skip: WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"' WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0" WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"' Pop $1 Pop $0 Pop $R2 Pop $R1 Pop $R0 !verbose pop !macroend !define UnRegisterExtension `!insertmacro UnRegisterExtensionCall` !define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall` !macro UnRegisterExtension !macroend !macro un.UnRegisterExtension !macroend !macro UnRegisterExtension_ !verbose push !verbose ${_FileAssociation_VERBOSE} Exch $R1 ;desc Exch Exch $R0 ;ext Exch Push $0 Push $1 ReadRegStr $1 HKCR $R0 "" StrCmp $1 $R1 0 NoOwn ; only do this if we own it ReadRegStr $1 HKCR $R0 "backup_val" StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key DeleteRegKey HKCR $R0 Goto NoOwn Restore: WriteRegStr HKCR $R0 "" $1 DeleteRegValue HKCR $R0 "backup_val" DeleteRegKey HKCR $R1 ;Delete key with association name settings NoOwn: Pop $1 Pop $0 Pop $R1 Pop $R0 !verbose pop !macroend !endif # !FileAssociation_INCLUDEDtiled-qt-0.9.1/dist/win/README.txt000066400000000000000000000043471217502731700165170ustar00rootroot00000000000000 How to create a Windows installer for Tiled =============================================================================== Prerequisites: * Qt is installed * MinGW is installed * NSIS in installed * Tiled executable in release mode has already been built Tiled's windows installer is created using NSIS. You thus need to install NSIS first if you want to build your custom windows installer for it. You can download the latest version of the installer from the NSIS project website located at http://nsis.sourceforge.net. Another requirement is that the project be already been built. The installer script is called "tiled.nsi" and is located in the dist/win directory. In order for the script to correctly produce binaries we do however need to modify the shell environment from which the script is run first. This is due to the fact that a successful compilation depends on some variables like the actual location of the Qt or MinGW installation path being correctly set. These values can not be automatically inferred by the installer script. Three mandatory variables "QTDIR", "MINGW" and "VERSION" need to be set to the correct paths of their respective packages. If VERSION is not set it will try to read the content of a file "version.txt" located in the root directory of tiled into that variable. The commands to set those may look like the following: set QTDIR="C:\Qt\4.8.2" set MINGW="C:\MinGW" set VERSION="0.9.1" Optionally you can also set the program architecture which is then used to deduce the resulting installer filename. It can either be 32 or 64 and defaults to 32: set ARCH="32" After setting the above variables to the correct values you can compile the NSIS installer script from the command line by executing the provided batch script "build_installer.bat". Make sure the nsis compiler is in PATH so it can be found by the command line interpreter. The resulting installer will be placed in the same directory where the nsis script was located and have a name similar to "setup-tiled--.exe", where is replaced with the value of the "VERSION" variable explained above and will - depending on the actual value of the "ARCH" variable - reflect the architecture (win32 or win64) for which the installer is built. tiled-qt-0.9.1/dist/win/build_installer.bat000066400000000000000000000034641217502731700206640ustar00rootroot00000000000000@echo off setlocal EnableDelayedExpansion :: Check for MinGW if not defined MINGW ( :get_mingw_path call :get_path MinGW MINGW ) call :check_path "!MINGW!" mingw32-make.exe MinGW MAKE if not defined MAKE ( call :check_path "!MINGW!" make.exe MinGW MAKE if not defined MAKE ( echo Failed to detect MinGW goto get_mingw_path ) ) :: Check for Qt if not defined QTDIR ( :get_qt_path call :get_path Qt QTDIR ) :: Check for the Tiled build directory if not defined TILED_BUILD_DIR ( call :get_path "the Tiled build" TILED_BUILD_DIR ) call :check_path "!QTDIR!" qmake.exe Qt QMAKE if not defined QMAKE ( echo Failed to detect Qt goto get_qt_path ) :check_arch if not defined ARCH ( set /P ARCH="Please either enter Architecture (32 or 64) or press enter for default (32)": if not defined ARCH ( set ARCH=32 echo Set to default: 32 ) ) if not defined VERSION ( if exist ..\..\build\VERSION_FILE ( echo "file exists" set /P VERSION=<..\..\VERSION_FILE ) if not defined VERSION ( :get_version_number set /P VERSION=Please enter the version number to be used: if not defined VERSION ( echo You didn't enter a valid version number goto get_version_number ) ) ) :: Starting the compilation process echo "BUILDING INSTALLER" makensis.exe tiled.nsi endlocal goto:eof ::-------------------------------------------------------- ::-- Function section starts below here ::-------------------------------------------------------- :get_path setlocal set /P EXE_PATH=Please enter path to %~1: if not exist "%EXE_PATH%" ( echo The directory you entered does not exist! goto get_path ) else ( endlocal set "%~2=%EXE_PATH%" ) goto:eof :check_path if exist %~1\bin\%~2 ( echo Successfully detected %~3! set "%~4=%~2" ) else ( set "%~4=" ) goto:eof tiled-qt-0.9.1/dist/win/gpl-2.0.rtf000066400000000000000000000400031217502731700166020ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1250\deff0\deflang1033\deflangfe1060{\fonttbl{\f0\fswiss\fprq2\fcharset238 Verdana;}{\f1\fmodern\fprq1\fcharset238 Lucida Console;}} {\*\generator Msftedit 5.41.15.1507;}\viewkind4\uc1\pard\nowidctlpar\sb100\sa100\qc\lang1060\kerning36\b\f0\fs28 GNU General Public License\par \kerning0\b0\fs16 Version 2, June 1991\par \pard\nowidctlpar\f1\fs14\par Copyright (C) 1989, 1991 Free Software Foundation, Inc.\par 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\par \par Everyone is permitted to copy and distribute verbatim copies\par of this license document, but changing it is not allowed.\par \par \pard\keepn\nowidctlpar\sb100\sa100\qc\b\f0\fs20 Preamble\fs24\par \pard\nowidctlpar\fi142\sb100\sa100\b0\fs16 The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. \par When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. \par To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. \par For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. \par We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. \par Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. \par Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. \par The precise terms and conditions for copying, distribution and modification follow. \par \pard\keepn\nowidctlpar\sb100\sa100\qc\b\fs20 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\par \pard\nowidctlpar\fi142\sb100\sa100\fs16 0.\b0 This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". \par \pard\nowidctlpar\sb100\sa100 Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. \par \pard\nowidctlpar\fi142\sb100\sa100\b 1.\b0 You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. \par \pard\nowidctlpar\sb100\sa100 You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. \par \pard\nowidctlpar\fi142\sb100\sa100\b 2.\b0 You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: \par \pard\nowidctlpar\li284\sb100\sa100\b a)\b0 You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. \par \b b)\b0 You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. \par \b c)\b0 If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) \par \pard\nowidctlpar\sb100\sa100 These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. \par Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. \par In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. \par \pard\nowidctlpar\fi142\sb100\sa100\b 3.\b0 You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: \v \v0\par \pard\nowidctlpar\li284\sb100\sa100\b a)\b0 Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \par \b b)\b0 Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \par \b c)\b0 Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) \par \pard\nowidctlpar\sb100\sa100 The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. \par If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. \par \pard\nowidctlpar\fi142\sb100\sa100\b 4.\b0 You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. \par \b 5.\b0 You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. \par \b 6.\b0 Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. \par \b 7.\b0 If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. \par \pard\nowidctlpar\sb100\sa100 If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. \par It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. \par This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. \par \pard\nowidctlpar\fi142\sb100\sa100\b 8.\b0 If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. \par \b 9.\b0 The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. \par \pard\nowidctlpar\sb100\sa100 Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. \par \pard\nowidctlpar\fi142\sb100\sa100\b 10.\b0 If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \par \pard\nowidctlpar\sb100\sa100\qc\fs20 NO WARRANTY\par \pard\nowidctlpar\fi142\sb100\sa100\b\fs16 11.\b0 BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \par \b 12.\b0 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \par \pard\keepn\nowidctlpar\sb100\sa100\qc\b END OF TERMS AND CONDITIONS\fs20\par } tiled-qt-0.9.1/dist/win/headerimage.bmp000066400000000000000000001032621217502731700177500ustar00rootroot00000000000000BM6(9 |  PE/]ߗ7$nKR7J<'Ãэ. qMlIlIsMr5$khFĄ>pLmJmJmJmJlJnK1vok4#nKlImJmJmJmJmJnKnKfE"ؐsySH0nKlImJmJmJmJmJmJmJlInKlIJ1>E.[bEP7oLlIlJmJmJmJmJmJmJmJmJmJlInKfFb7'eZ[CZAoOnMmKmJmJmJnJmJmJmJmJmJmIlJmKmOrWSEUD*) X_WU;+*뤛飛뤛륛꣚頖鞒雍蘋斅擀}yvx|芀鉂臃烁肁rBAHHHmmm666.!!履짞咽﨟吏xё꣛颚꣛飙韕靑蚎痊疆撄撀}{}鋁錄ꊆ釄{zA10```===uuu((("咽륜lg6%#[?<{桗ꤚ颙蟕蜐蚍昉旆蔃蓂}~茀芃銄vrNMM---hhhNNNrrrF1.褐yTPmMH뤜꣛뤜栘|v [?;頖盐蚍瘉症蔃撁}{y{}}HCbbbccc TTT999SSSRKKbC@T;8f`즜뤛ꤛꢛ룜쥜ђbD@ um虋瘇畄擀zvst2дNNN___ppp222OOOWWWYJI`[2"!xr履륝뤜ꤚꢚgb-]>9瘈똆~wswaC:-ۮUUUJJJ===gggmmm HHH^^^tttiQM{u=+*Γ륜륝룚렗vmL2,>(#k[|UF`bLWWWFFF)))---MKKfaѕ/! 觠蘒꟔雎ꚈgA8 X\Iddd+++&&&cccFFF 2..yꨠ"列霍瘉֋y~js_/hМ###1#"Ж쟒€t 蔀yuploA442%З }}}EEEhffB-+vOKZ;7ꛌ藆~xsnjہdpW*~ivvvfff555ZSR]>;#wr획ꖆ摀ysnkjoqdOG485XXXbRQ_[㣜 ݛ횏ꕉ瑂|ustw{~|PO`HGccc777((([[[ooooVS{uup?-,얌ꑅ莀}~爀腀烀x=<]]] >>>!Ӗ픍됈ꍆ댆鉅膃ꆄrpD55SSS'&&so颛픎푌쎊ꊇ鈆ZY[[[AAA }x쎋6""ddd}}0Їfeecc nffF'%օ9tiled-qt-0.9.1/dist/win/qt.conf000066400000000000000000000000661217502731700163060ustar00rootroot00000000000000[Paths] Plugins = plugins Translations = translations tiled-qt-0.9.1/dist/win/tiled.nsi000066400000000000000000000237351217502731700166370ustar00rootroot00000000000000; NSIS installer script for Tiled ; --------------- Headers -------------- !include "MUI2.nsh" !include "FileAssociation.nsh" ; --------------- General -------------- CRCCheck force XPStyle on SetCompressor /FINAL /SOLID lzma !define QT_DIR $%QTDIR% ; Qt Installation directory !define MINGW_DIR $%MINGW% ; MinGW Installation directory !define V $%VERSION% ; Program version !define ARCH $%ARCH% ; Architecture 32 or 64 !define P "Tiled" ; Program name !define P_NORM "tiled" ; Program name (normalized) !define ROOT_DIR "..\.." ; Program root directory !define BUILD_DIR $%TILED_BUILD_DIR% ; Build dir !define ADD_REMOVE "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tiled" !define PRODUCT_REG_KEY "Tiled Map Editor" InstallDir "$PROGRAMFILES\${P}" ; Default installation directory Name "${P}" ; Name displayed on installer OutFile "${P_NORM}-${V}-win${ARCH}-setup.exe" ; Resulting installer filename BrandingText /TRIMLEFT "${P_NORM}-${V}" RequestExecutionLevel admin ; ----------- Icon and Bitmap --------- ;!define MUI_ICON install.ico ; TODO: find suitable icon ;!define MUI_UNICON uninstall.ico ; TODO: find suitable icon !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP headerimage.bmp !define MUI_HEADERIMAGE_UNBITMAP headerimage.bmp !define MUI_HEADER_TRANSPARENT_TEXT ; ------------------------------------- !define MUI_ABORTWARNING ;------------- Language Selection Dialog Settings -------------- !define MUI_LANGDLL_REGISTRY_ROOT "HKCU" !define MUI_LANGDLL_REGISTRY_KEY "Software\${P}\${V}" !define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" ;-------------- Install Pages ------------- !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE ${ROOT_DIR}\dist\win\gpl-2.0.rtf !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES ; These indented statements modify settings for MUI_PAGE_FINISH !define MUI_FINISHPAGE_NOAUTOCLOSE !define MUI_FINISHPAGE_RUN "$INSTDIR\${P_NORM}.exe" !define MUI_FINISHPAGE_RUN_CHECKED !define MUI_FINISHPAGE_RUN_TEXT "Launch ${P}" !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\README.txt" !insertmacro MUI_PAGE_FINISH ;-------------- Uninstall Pages ------------- !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ; These indented statements modify settings for MUI_UNPAGE_FINISH !define MUI_UNFINISHPAGE_NOAUTOCLOSE !insertmacro MUI_UNPAGE_FINISH ;--------------- Languages --------------- !insertmacro MUI_LANGUAGE "English" !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "SpanishInternational" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Korean" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "NorwegianNynorsk" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Ukrainian" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Thai" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Macedonian" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Slovenian" !insertmacro MUI_LANGUAGE "Serbian" !insertmacro MUI_LANGUAGE "SerbianLatin" !insertmacro MUI_LANGUAGE "Arabic" !insertmacro MUI_LANGUAGE "Farsi" !insertmacro MUI_LANGUAGE "Hebrew" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Mongolian" !insertmacro MUI_LANGUAGE "Luxembourgish" !insertmacro MUI_LANGUAGE "Albanian" !insertmacro MUI_LANGUAGE "Breton" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Icelandic" !insertmacro MUI_LANGUAGE "Malay" !insertmacro MUI_LANGUAGE "Bosnian" !insertmacro MUI_LANGUAGE "Kurdish" !insertmacro MUI_LANGUAGE "Irish" !insertmacro MUI_LANGUAGE "Uzbek" !insertmacro MUI_LANGUAGE "Galician" !insertmacro MUI_LANGUAGE "Afrikaans" !insertmacro MUI_LANGUAGE "Catalan" !insertmacro MUI_LANGUAGE "Esperanto" ; ------------- Reserve Files --------------------- !insertmacro MUI_RESERVEFILE_LANGDLL ; ------------- Installer Functions --------------- Function .onInit !insertmacro MUI_LANGDLL_DISPLAY FunctionEnd Function checkAlreadyInstalled ; check for already installed instance ClearErrors ReadRegStr $R0 HKLM "SOFTWARE\${PRODUCT_REG_KEY}" "Version" StrCmp $R0 "" 0 +2 Return MessageBox MB_YESNO|MB_ICONQUESTION "${P} version $R0 seems \ to be installed on your system.$\nWould you like to \ uninstall that version first?" IDYES UnInstall Return UnInstall: ClearErrors ReadRegStr $R0 HKLM "${ADD_REMOVE}" "UninstallString" DetailPrint "Uninstalling previously installed version" ExecWait '$R0 _?=$INSTDIR' IfErrors OnError 0 Return OnError: MessageBox MB_OK|MB_ICONSTOP "Error while uninstalling \ previously installed version. Please uninstall it manually \ and start the installer again." Quit FunctionEnd ;-------------- Uninstaller Functions ------------- Function un.onInit !insertmacro MUI_UNGETLANGUAGE FunctionEnd ;-------------- Installer ------------------------- Section "" ; No components page, name is not important Call checkAlreadyInstalled SetOutPath $INSTDIR ; Set output path to the installation directory. WriteUninstaller $INSTDIR\uninstall.exe ; Location of the uninstaller File /oname=COPYING.txt ${ROOT_DIR}\COPYING File /oname=AUTHORS.txt ${ROOT_DIR}\AUTHORS File /oname=README.txt ${ROOT_DIR}\README.md File /oname=NEWS.txt ${ROOT_DIR}\NEWS File /oname=LICENSE.APACHE.txt ${ROOT_DIR}\LICENSE.APACHE File /oname=LICENSE.BSD.txt ${ROOT_DIR}\LICENSE.BSD File /oname=LICENSE.GPL.txt ${ROOT_DIR}\LICENSE.GPL File ${BUILD_DIR}\${P_NORM}.dll File ${BUILD_DIR}\${P_NORM}.exe File ${BUILD_DIR}\tmxviewer.exe File ${BUILD_DIR}\tmxrasterizer.exe File ${BUILD_DIR}\automappingconverter.exe ;File ${MINGW_DIR}\bin\mingwm10.dll ;File ${MINGW_DIR}\bin\libgcc_s_dw2-1.dll ;File ${MINGW_DIR}\bin\libstdc++-6.dll File ${QT_DIR}\bin\QtCore4.dll File ${QT_DIR}\bin\QtGui4.dll File ${QT_DIR}\bin\QtOpenGL4.dll File ${ROOT_DIR}\src\tiled\images\tiled-icon.ico File ${ROOT_DIR}\dist\win\qt.conf SetOutPath $INSTDIR\plugins\codecs File ${QT_DIR}\plugins\codecs\qcncodecs4.dll File ${QT_DIR}\plugins\codecs\qjpcodecs4.dll File ${QT_DIR}\plugins\codecs\qtwcodecs4.dll File ${QT_DIR}\plugins\codecs\qkrcodecs4.dll SetOutPath $INSTDIR\plugins\imageformats File ${QT_DIR}\plugins\imageformats\qgif4.dll File ${QT_DIR}\plugins\imageformats\qjpeg4.dll File ${QT_DIR}\plugins\imageformats\qtiff4.dll SetOutPath $INSTDIR\plugins\tiled File /r ${BUILD_DIR}\plugins\tiled\*.dll SetOutPath $INSTDIR\translations File ${BUILD_DIR}\translations\*.qm File ${QT_DIR}\translations\qt_cs.qm File ${QT_DIR}\translations\qt_de.qm File ${QT_DIR}\translations\qt_es.qm File ${QT_DIR}\translations\qt_fr.qm File ${QT_DIR}\translations\qt_he.qm File ${QT_DIR}\translations\qt_ja.qm File ${QT_DIR}\translations\qt_pt.qm File ${QT_DIR}\translations\qt_ru.qm File ${QT_DIR}\translations\qt_zh_CN.qm File ${QT_DIR}\translations\qt_zh_TW.qm SetOutPath $INSTDIR\examples File /r ${ROOT_DIR}\examples\*.* SetOutPath $INSTDIR\docs File /r ${ROOT_DIR}\docs\map.* SetOutPath $INSTDIR\util File /r /x .gitignore /x README /x README.txt ${ROOT_DIR}\util\*.* ; Shortcuts CreateDirectory "$SMPROGRAMS\${P}" CreateShortCut "$SMPROGRAMS\${P}\${P}.lnk" "$INSTDIR\${P_NORM}.exe" CreateShortCut "$SMPROGRAMS\${P}\uninstall.lnk" "$INSTDIR\uninstall.exe" ; File associations ${RegisterExtension} "$INSTDIR\${P_NORM}" ".tmx" "Tiled.tmx" ; Add version number to Registry WriteRegStr HKLM "Software\${PRODUCT_REG_KEY}" "Version" "${V}" ; Add uninstall information to "Add/Remove Programs" WriteRegStr HKLM ${ADD_REMOVE} "DisplayName" "Tiled - Tiled Map Editor" WriteRegStr HKLM ${ADD_REMOVE} "DisplayIcon" "$INSTDIR\${P_NORM}-icon.ico" WriteRegStr HKLM ${ADD_REMOVE} "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" WriteRegStr HKLM ${ADD_REMOVE} "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" WriteRegStr HKLM ${ADD_REMOVE} "Version" "${V}" SectionEnd ;------------ Uninstaller ------------- Section "uninstall" Delete $INSTDIR\COPYING.txt Delete $INSTDIR\AUTHORS.txt Delete $INSTDIR\README.txt Delete $INSTDIR\NEWS.txt Delete $INSTDIR\LICENSE.APACHE.txt Delete $INSTDIR\LICENSE.BSD.txt Delete $INSTDIR\LICENSE.GPL.txt Delete $INSTDIR\tiled.dll Delete $INSTDIR\tiled.exe Delete $INSTDIR\tmxviewer.exe Delete $INSTDIR\tmxrasterizer.exe Delete $INSTDIR\automappingconverter.exe Delete $INSTDIR\mingwm10.dll Delete $INSTDIR\libgcc_s_dw2-1.dll Delete $INSTDIR\libstdc++-6.dll Delete $INSTDIR\QtCore4.dll Delete $INSTDIR\QtGui4.dll Delete $INSTDIR\QtOpenGL4.dll Delete $INSTDIR\tiled-icon.ico Delete $INSTDIR\uninstall.exe RMDir /r $INSTDIR\plugins\codecs RMDir /r $INSTDIR\plugins\imageformats RMDir /r $INSTDIR\plugins\tiled RMDir $INSTDIR\plugins RMDir /r $INSTDIR\translations RMDir /r $INSTDIR\examples RMDir /r $INSTDIR\docs RMDir /r $INSTDIR\util RMDir $INSTDIR ; Removing shortcuts Delete "$SMPROGRAMS\${P}\${P}.lnk" Delete "$SMPROGRAMS\${P}\uninstall.lnk" RMDir "$SMPROGRAMS\${P}" ; Removing file associations ${UnRegisterExtension} ".tmx" "Tiled.tmx" ; Remove Procut Registry Entries DeleteRegKey HKLM "Software\${PRODUCT_REG_KEY}" ; Remove entry from "Add/Remove Programs" DeleteRegKey HKLM ${ADD_REMOVE} SectionEnd tiled-qt-0.9.1/docs/000077500000000000000000000000001217502731700142015ustar00rootroot00000000000000tiled-qt-0.9.1/docs/Doxyfile000066400000000000000000000221131217502731700157060ustar00rootroot00000000000000# Doxyfile 1.6.1 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "Tiled (Qt)" PROJECT_NUMBER = OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = NO REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES TYPEDEF_HIDES_STRUCT = NO SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = YES SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = ../src INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */moc_*.cpp \ */ui_*.h \ */qrc_*.cpp EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES HTML_DYNAMIC_SECTIONS = NO GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = "../tiled-0.1.qch" QHP_NAMESPACE = org.mapeditor.tiled QHP_VIRTUAL_FOLDER = "tiled-0.1" QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = "qhelpgenerator" DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO USE_INLINE_TREES = NO TREEVIEW_WIDTH = 250 FORMULA_FONTSIZE = 10 SEARCHENGINE = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO DOT_FONTNAME = FreeSans DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES tiled-qt-0.9.1/docs/automappingconverter.1000066400000000000000000000012021217502731700205320ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "AUTOMAPPINGCONVERTER" "1" "July 2013" "" "" . .SH "NAME" \fBautomappingconverter\fR \- a converter for old Tiled automapping rules . .SH "SYNOPSIS" \fBautomappingconverter\fR . .SH "DESCRIPTION" This converter is used to convert automapping rules of the Tiled map editor from version 0\.8\.x and lower to 0\.9\.0 and later\. . .SH "AUTHORS" \fIhttps://github\.com/bjorn/tiled/blob/master/AUTHORS\fR . .SH "SEE ALSO" tiled(1), \fIhttp://www\.mapeditor\.org/\fR, \fIhttps://github\.com/bjorn/tiled/wiki/Automapping#wiki\-Converting_rules_from_08_and_below\fR tiled-qt-0.9.1/docs/automappingconverter.1.ronn000066400000000000000000000007631217502731700215200ustar00rootroot00000000000000automappingconverter(1) -- a converter for old Tiled automapping rules ======================================== ## SYNOPSIS `automappingconverter` ## DESCRIPTION This converter is used to convert automapping rules of the Tiled map editor from version 0.8.x and lower to 0.9.0 and later. ## AUTHORS ## SEE ALSO tiled(1), , tiled-qt-0.9.1/docs/map.dtd000066400000000000000000000062261217502731700154610ustar00rootroot00000000000000 tiled-qt-0.9.1/docs/map.xsd000066400000000000000000000171051217502731700155020ustar00rootroot00000000000000 tiled-qt-0.9.1/docs/tiled.1000066400000000000000000000016221217502731700153650ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "TILED" "1" "January 2013" "" "" . .SH "NAME" \fBtiled\fR \- tile map editor . .SH "SYNOPSIS" \fBtiled\fR [\fIOPTIONS\fR] [FILES\.\.\.] . .SH "DESCRIPTION" Tiled is a general purpose tile map editor\. It\'s built to be easy to use, yet flexible enough to work with varying game engines, whether your game is an RPG, platformer or Breakout clone\. Tiled is free software and written in C++, using the Qt application framework\. . .SH "OPTIONS" . .TP \fB\-h\fR \fB\-\-help\fR Displays the help . .TP \fB\-v\fR \fB\-\-version\fR Displays the version . .TP \fB\-\-quit\fR Only check validity of arguments . .TP \fB\-\-disable\-opengl\fR Disables hardware accelerated rendering . .SH "AUTHORS" \fIhttps://github\.com/bjorn/tiled/blob/master/AUTHORS\fR . .SH "SEE ALSO" tmxviewer(1), tmxrasterizer(1), \fIhttp://www\.mapeditor\.org/\fR tiled-qt-0.9.1/docs/tiled.1.ronn000066400000000000000000000013501217502731700163360ustar00rootroot00000000000000tiled(1) -- tile map editor =========================== ## SYNOPSIS `tiled` [] [FILES...] ## DESCRIPTION Tiled is a general purpose tile map editor. It's built to be easy to use, yet flexible enough to work with varying game engines, whether your game is an RPG, platformer or Breakout clone. Tiled is free software and written in C++, using the Qt application framework. ## OPTIONS * `-h` `--help`: Displays the help * `-v` `--version`: Displays the version * `--quit`: Only check validity of arguments * `--disable-opengl`: Disables hardware accelerated rendering ## AUTHORS ## SEE ALSO tmxviewer(1), tmxrasterizer(1), tiled-qt-0.9.1/docs/tiled.desktop000066400000000000000000000002541217502731700166760ustar00rootroot00000000000000[Desktop Entry] Name=Tiled Comment=Tile map editor Exec=tiled Terminal=false Type=Application Icon=tiled Categories=Graphics;2DGraphics; X-Desktop-File-Install-Version=0.1 tiled-qt-0.9.1/docs/tmxrasterizer.1000066400000000000000000000017631217502731700172150ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "TMXRASTERIZER" "1" "January 2013" "" "" . .SH "NAME" \fBtmxrasterizer\fR \- renders a tile map to an image . .SH "SYNOPSIS" \fBtmxrasterizer\fR [\fIOPTIONS\fR] [INPUT FILE] [OUTPUT FILE] . .SH "DESCRIPTION" This application can be used to render maps created by the Tiled Map Editor to an image\. This is very helpful for creating small\-scale previews, such as mini\-maps\. . .SH "OPTIONS" . .TP \fB\-h\fR \fB\-\-help\fR Displays the help . .TP \fB\-v\fR \fB\-\-version\fR Displays the version . .TP \fB\-s\fR \fB\-\-scale\fR SIZE The scale of the output image . .TP \fB\-t\fR \fB\-\-tilesize\fR SIZE The requested size in pixels at which a tile is rendered\. Overrides the \-\-scale option\. . .TP \fB\-a\fR \fB\-\-anti\-aliasing\fR Smooth the output image using anti\-aliasing . .SH "AUTHOR" Vincent Petithory <\fIvincent\.petithory@gmail\.com\fR> . .SH "SEE ALSO" tiled(1), tmxviewer(1), \fIhttp://www\.mapeditor\.org/\fR tiled-qt-0.9.1/docs/tmxrasterizer.1.ronn000066400000000000000000000014751217502731700201700ustar00rootroot00000000000000tmxrasterizer(1) -- renders a tile map to an image ======================================== ## SYNOPSIS `tmxrasterizer` [] [INPUT FILE] [OUTPUT FILE] ## DESCRIPTION This application can be used to render maps created by the Tiled Map Editor to an image. This is very helpful for creating small-scale previews, such as mini-maps. ## OPTIONS * `-h` `--help`: Displays the help * `-v` `--version`: Displays the version * `-s` `--scale` SIZE: The scale of the output image * `-t` `--tilesize` SIZE: The requested size in pixels at which a tile is rendered. Overrides the --scale option. * `-a` `--anti-aliasing`: Smooth the output image using anti-aliasing ## AUTHOR Vincent Petithory <> ## SEE ALSO tiled(1), tmxviewer(1), tiled-qt-0.9.1/docs/tmxviewer.1000066400000000000000000000011641217502731700163170ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "TMXVIEWER" "1" "January 2013" "" "" . .SH "NAME" \fBtmxviewer\fR \- a simple tile map viewer . .SH "SYNOPSIS" \fBtmxviewer\fR [\fIOPTIONS\fR] [FILES\.\.\.] . .SH "DESCRIPTION" This application can be used to quickly view a map that was created by the Tiled Map Editor\. . .SH "OPTIONS" . .TP \fB\-h\fR \fB\-\-help\fR Displays the help . .TP \fB\-v\fR \fB\-\-version\fR Displays the version . .SH "AUTHORS" \fIhttps://github\.com/bjorn/tiled/blob/master/AUTHORS\fR . .SH "SEE ALSO" tiled(1), tmxrasterizer(1), \fIhttp://www\.mapeditor\.org/\fR tiled-qt-0.9.1/docs/tmxviewer.1.ronn000066400000000000000000000007311217502731700172710ustar00rootroot00000000000000tmxviewer(1) -- a simple tile map viewer ======================================== ## SYNOPSIS `tmxviewer` [] [FILE] ## DESCRIPTION This application can be used to quickly view a map that was created by the Tiled Map Editor. ## OPTIONS * `-h` `--help`: Displays the help * `-v` `--version`: Displays the version ## AUTHORS ## SEE ALSO tiled(1), tmxrasterizer(1), tiled-qt-0.9.1/examples/000077500000000000000000000000001217502731700150675ustar00rootroot00000000000000tiled-qt-0.9.1/examples/desert.tmx000066400000000000000000000013161217502731700171100ustar00rootroot00000000000000 eJztmNkKwjAQRaN9cAPrAq5Yq3Xf6v9/nSM2VIbQJjEZR+nDwQZScrwztoORECLySBcIgZ7nc2y4KfyWDLx+Jb9nViNgDEwY+KioAXUgQN4+zpoCMwPmQAtoAx2CLFbA2oDEo9+hwG8DnIDtF/2K8ks086Tw2zH0uyMv7HcRr/6/EvvhnsPrsrxwX7rwU/0ODig/eV3mh3N1ld8eraWPaX6+64s9McesfrqcHfg1MpoifxcVEWjukyw+9AtFPl/I71pER3Of6j4bv7HI54s+MChhqLlPdZ/P3qMmFuo5h5NnTOhjM5tReN2yT51n5/v7J3F0vi46fk+ne7aX0i9l6If7mpufTX3f5wsqv9TAD2fJLT9VrTn7UeZnM5tR+v0LMQOHXwFnxe2/warGFRWf8QDjOLfP tiled-qt-0.9.1/examples/desert.tsx000066400000000000000000000042151217502731700171170ustar00rootroot00000000000000 tiled-qt-0.9.1/examples/isometric_grass_and_water.png000066400000000000000000003145741217502731700230340ustar00rootroot00000000000000PNG  IHDRB¼sRGBbKGD pHYs  tIME#Yw IDATxy$Gy&DDfVfeU}TsfF3ͩ˒8Vl~j, ??~x>keɲ`d. GH33=S]]ufUVҠ[ꨈx'x7x^E{^E{^E{^E1ۑsƑsڋ3sÎܝc/Ϟvg. #ws^,>=`g.8g<_##?09|.{/mwO ʂ:Aۢ?Qy6I/O˘U щ}}H ?+ׂRYl~n;sbSx&JCmԠNPgqOaV}uw=[(wʌ f|*n3ץ폄aä?5o+!1vߎfiN;Z K5}{ iL+ ̰m?ry@kflEŏߗߕ^ 4]Wٙn 8v<"G@v|oߟ[2hس۷_j|sw\?MR9h+Fӧxa,~/lB=ҿ./cܙHt끭k<g‘t%:mexM B:1_C%Z1~˅ dZO[2&YI V֢k`~-<\(ϲ'%bgD1rmO$Xri%Hwn#$G?}5~GϺZES aAZUuà#wRGΥ^@(7ݷ9*㟾 >%ˍ_Zkɡ"#e>TR'𝽮@zΟt2Y 3-qG%핿E 5\M$fܶmUWwnv4J2q΂}\q$Mߕq;2}_]fٗc=ůƦ`{Ƕ nc|Vj0QkҺv\X1c>WaQ<:ny t'w7{j.U›&2]['O]w?m`H?{y,i8r0MƮPn{-qvd|q]xlduNKѫO Fɞv=ad_0.8]e6h܋2q×鲌P<6hdT.m7NΑJ $>.~f'1J|\tE[L)xXZQnU<ƕz;Nm5{`34͋-:BzC\N.V/~s >WR@և>W[_+HX6db&l@vUGWV ;=r׿Zr=]:bV]]oM.ǜz_{[Gw~yPC  Oͅ7Є1 \H w$߿KG$;~Aqm}:ᑬs;Uܧڭ|{?ZDAVb9]V:W?5;@)ƎO+-5_>yn&R{E{&zOr=J>[)ch4Bs,4z9Ys?r ?4~tO}bC~Dt=n@?up EWY OnݶcYveh'OzῼK Ax076>yk磁ِTJg8G'H<?ٶsysq!ҲW-4󷞜;6epVP]{ꢩ1vsp^ȡqrz+~jJ J4vݍ)+$ZfRLRdW}q iz=p&&7oIN2OP/to3ɐ`t{/j\zgһkr{iY<(~D 4^G#w__3ͳT}fㆡm0<|\qk*5G0_{d??&P.-/R̩ڡn-qG漝U>e%5jѳ;SOܫMu]߰ 䊱m/N qWĘI3H9{n|oιb~\ gVR¡6=lA۳򚣏L o)ع.cƯ ,? -f8fè>؏(L7&C;rOa~ ,TK:tдMze$aOsfzT^ӃZƌ%uMeUr/dzcC ֛KG{f_VHN47l`3p.l8k3?8SGOo)-RWNG]7nN5_78@d6o]:haNءVn95NOzV[>3/KbP\}.mxCwIyjǑ*&:j9*Q(8$AmY]}d.(mLݦdW͉dJ#A;`gܛ<s%N gxn}}h[}'1'\<8ۿ@8ў."[)L˒-Ǻo{"vd}ih&װi?mLd~UF{rπsy iEP߰.h-1]`#ȊIg07o c/J%` P$EpA\5pXaKᦓOȨo38(1fg?ǜvp ||}UR$#ww>$Gp~H&..1$w%x"Vs'˫JS;#![YfNsc䶻r&YFG4]1b`VtAWQis/4J=:㴔FgWMQ Z2:\s|(QP;8E;j7X) 9>fUjdҲ$:xs'00?Dz&|[zz#g.y\,JP>s9+SqM{0Q1h7$@?.`SJcqxwigS#z|qAL@p21fVG䙊s'ŧ!;\Soϵ?s'9p7&#"9<;fj+aᵚ|<Ňl+knV(ܫ0nf\C]KF-X` ZG@ 6uStX*E]o:*5B92QJd4y(![nDM܎hũ A)ƿ\\-91Ռ0vcJ emzk4& 8priN^[;%Yc1,Bh)A>8w$B)+Ua9q;_qv+첕q<ϐՍ:}_]ԼgH ;ɍ1FQlgנu^ӫ֢?ܨktĶiLI\_|_D d|BGsc={E6HnߙMV7Ef;gB˧Rkh%Ia'Ay#!"iw,Av-T`g^(d]=7:J}4ptߓŹR~o78ڮi`4QF(>RfRbcpew)cz]N$ɱW*ڇIsm)$VLW6J4 lEL@]^ 9'2}jlo^}ċ؅Q]fM[ҸKQ):." ÐbB #kQR/iOL-hEFW~qNi#C +bݟY,_t[ؾx>dS;W0Mzx>٦40*"s; .[~ϛ훘1^д˅//r70&6Vyg1Uum{V4A n3B3)@ XX㙮ZdFP}޼]yb{T7RA'Cյ\u{ uYU]Iޭ_sC:p>(N307 R*uZ( h|)&BINw36vR/N#;ߏ| 2d~J &k@AEq!C\F[M&* {\#պxUa]!8*WԎOD,"D3(turEx;a!tSڬښ@qGOS6 9j':ZGjh6 ue{D`)@ ç^& %X%I2Rc :AtkΆ!7>2pj:h8w|Ԟg5u|g+ג6n_^f]G]O[ʫߔz\vM8ɯձ R.W(K^;~ObbxQs0 Bj`X <  W::dٔӡE8}n1Uj!oeæxI઩6_]75IJ fgc<-UsNv;"bR'FGp0H 9i  lCJDG2/1J/i2K]tY%7w8T8:ظ5`1l٢|Wq8>G` a|#XAѬ2y V׏FFRt4pt~S xxʇrq$*kSٍ?yv)B$2)?JkF>5Dr+X,zK}C}hG>xuQGm -^_ӵ8Y# Dz!2R(/ 0¯SmE :x<$A`IiJFf-%,M}z=ommuIm5s=ei#}_^K=uZΗqR}҅Jm׎eGFNڪ %~&7n$}Gw\t~K+plqEKbj])J3Э\Ug]i8ded" H@$S')x0ff ̢[ $S:Y%_]hArz{<0fr{Nɳa0Ukufmܢr}q1w]q WD>Ua!ڊ:XI:J{.bTeV=G 梭 ]uuZ KQf=W!W` CՍPX=V ()̄=iR6m/{cJLe6B~E }u·kMyC$ٵxLVB3ObS&^:4@PftevcSOȟ{iH hE29IS++Z (d I̜ ʢbX]pmRBBfuk ɩ+6ˎv18u'|̀n_N]T9Ƴ+g>;=ȩuU+$ :FֶN#$te,o>[Z pJ: Ar40\ )"0R`H:D|.NME\F"8\e6l}HԌ3JVC&Gt-ӗ dG TO0O%-|G+А09C4͢B b"x265";%w}GoM$򬞍jul@YyUοW1:$ bөԹ%9vSany5:@aHCcUlU ̆>d1BZOd) Y$ND1PW1Bt ytԦ$w-e|$fVU[jnB Ǣ;+krd)J򲰹U~T|Z^"C/ۖbn8:eH aX n`CPZZ_H$ p+mj] 45`dAtvC> ]HqMKʝ#7+ OߣZuqte*/jux}-`1 C6f(<&9.08#gt">8D:p  ! d/R3>Ln.Y#{--d{&w=)CI}w}jf8ș#Ù~}4HDz;o:s*k,[0;:N#lm,Xb2g"1d &ldS^@LMGz:3T"&@EAR:rY]+ꨬHdAaw% ErH!5lYm &jYxҶyOέVm{} P\I@v}9H810X` ЦiGPP i- LJAS>?B.A@*PK:/DmZt(6R$R4VoJضiI ԓDѲI}ضڢ4qR{_3 Ș舆T5@"=k = & AJJp!vQxk{i6A SctRֳNH,F6TY!ҰBrW:q>@βD$0BF y@C̑Ȅ0$ARo:@b Rh8 `|HC(7B&&XK(A;kI)LΫqMr`[@=[K d݆.]ŁupW9aEv*76+تS@ #t[ .D@Pjx$pFLBbguf lu TP0$PR"oSd@ $)|" 0;䴾DJ1YuȀnTcX*Xc!KZ8'A$l!he#Z҈mQ8]upA)b(UtqT2&0%H)!QX U@J X^@iDp%tf"2^diQf#hVyM|mp4<"I&&} ?1;nm….l t>M3V*d!pD8c=̐Y%0 m衄IL˃k;XJk^d&.$EĀP_06Y.RBlI;~GŮvWx\#kҜ`&`=UHc,d{`0o~Uʆ)ߑ* !-?ݨZt 5D*M k1Q4ۖH1cb#XE U#H Gq!ˢp˫:ah 8`"LMyBXQC2^5=l4Dr0K6( ۢ-W˸QQǭ&ɆRY8#|mͥ?y<B՟~з~fkݰhAC32UE!3@Z0u"Nز@\!0PY?@׉c:D$yCSX9t5B S8Am^bAO3tx&Bו6Ȩ"@ӑTL42}0)D2jTAwۯ-p&A5%X'1,wSFL2-( U{6!QeSe&$ΑTpi@z}a#n'`6TM1\ Q0c硂N|Y$bn/4 w}[~bot.42,b;c FiCJcaݛ3q$#VD2TJ+>$E>r,*HlڂV"! zkHL/* =dh" T{(0O%@yv" a\ia8: k}{6(/Wdluz](~8ŚފN}| *JE&Ő$a1"pBF^CA F{@<'ap4"IfFAa2XC*%U59[Z[c;lv/!Xs]QO4vx.s6T68i3CI@X:(ܾh6`5 aHH b;a@Gqm*zѲgzbT&^VgnIܢ p٠0RZM h2d 3[lpFq ep /0>aıu @.E@bH|W !88Ghu(aS8BjbðN(!juQH0E:mTyNXia;a$^B)*J!>w'{y8OGJI )8jYW@VHKF$ xCg@uMRF_W4l@He),GqXC:k i x &4B2T[vIdvcYDP k;׷rBz*v 6j_bܴV,&i`)V}^,`, ̢P3 1!ҐHx1Y!N! @G$%aJP=F[ M]@{%BpjZۦf|ss#{0лE)JYm%ӵe\aMl`W9ACqX(nKq$ ɰTv y deSU˕K׏ȆP>稕% X Mj!sM xL!CUXX(((I((ZϤ >5.gbgL42ѻۍ(kTsR/!P='5 A6zzO"Pa@ni!(/*a[0INKj-d&11`'ju3r~$*I Whh$)FƀB 1)VPP,!""dF,f9࿪@@1R$.eg2ϦŬ͈#}B HB"T  :" 8_I: u 6d)! Z+$ i1ub- p 2 ї!cN!Ӡ4 $PB(VkV@narD-,ͯy ;v'G4AeIu$lL^bA< 59FFJeͺ=L, b@1)P]r5."96KB٨GshUK|LZe!DBYDP.RS` })P)8a@PTz!D PvJ "(n-3WWhw%$M`(`qdK1B\) ru9 ) P>#yCLĴ8icF)>pdFw Y%Ғ@O,"-M! D:F=&=Vt[K |@6B9C"H}Qj,@S0Ț r4w2ɁL! e6 CѐjuBDP Tc NaLBV*Zz[&0Dp äh6$ "402EP9`錎Μ@{hKy_ի]UE ̀Yc,ݓ>S$& MZYMq`@tO `32Pj86%ylU8߉ <+)Ȓui wQu{`&2)UGeyK zH+, 2!GرW0ɱNЉR,-3o1*fsS$FYjPB.{D>\b5LU1uaA(3I֢fN ӊKRoiI" ~u|j7Jȋ0:ZC:C$uTb!Eևou,-ZB)Q`c`e#T 92Vd<ð`<%'}C$GT>h ǡPr~^ r,Ok{aNg!k|d "BBaC'fOQ74(eG%&і#!r(H1*{ĎezDA;GuDQPҏBl[u.4F$yR-Y xIa%{uNI+R1Egn}|㨦?VxM!ί9 B?5Ȓi(=Cm'%Ŕ2ngeÈhOR Ն"~mH q N{ $1bL ؘ3UhMLDAcPJ8ZuF'#XM%\BfW`T9/\ E+-g8S_(]8PDXoϦ5W$'|V7,.{u 3YDE0,,-8(%7dܴDaFIet%P[M-"?|J}G9 1ce=B0l%'|E$))lnnUSrHD@P^D@AקI}zѨYp$P+80SS5JRk{C60A L<uK%*zYv8tP)} mCm\1U0ؚ#(TGQH)e5dbRS9o#} hÜVF{^It4%r*,T%aa,0j٥80Q A%iߡ >'\)1X0 a&$a(pFR|6ڐu=zYFⴅ+ 4CInF_w!\P,el`mTr&)_lA:')*vyw b (~ Ȝ%B 2 < %X>Py!?t'@5P%AC G RTa;>EMoN #饟[+ҧ(M % _2%͜v?DmpMB7T>~QhT:D;gǎ рHۖv cew JUKkt3Gԁ R8q9i*l+cDހ$">+e\A)Q%ZrR~yL?ql5ҢGzJhaE. $ K.8"eY_2̟HVW(3rh)>%k;~+IUR5VeNBg#Od$!b73吪*1Wܺ=ËG'뽀>Mo}vSDU-E#Pz }VnZgsօF6-K 0)F*tZ2I#%#$/i (M -G*[^S $&W4 Z5Z Y]&e PvH\P6cMڸ˾8x- +q .{>1KfJtGrE(N&Sd/n *VsL +i$"K&vJRRH 2:d)aOó!3q`ZW, Y ˀ΂ Тr*D"(*SmԔB"?R%ƸKe02K@ﴢܴtV5~z z.'Ӳ@izajhYܜ`M`Saib|G屹!e4W4vX\"SE}*'ސXCaXhg;Fw{n 0ce`GwcKl –BUldzAMqݑMyf^q\#e7/>\@=-rHs\Nj֠xCëPMA.T Q#r ! -gg)P.*, o=84ī !׊BYh0Ȁzii;yM"i{\Dɷ:>wUWݱm-m/d<);s oYenjz:vdbY!HZ/cczD>IGg1. IDAT?R'gHPl1EGВa`(x{ {}69)B\O2X$9#JʙTj8Ah*5}ok. B C}o3u]KQ!jDXШJ_(VB@Ҽ8h;2b1dn` ґ+AhKXXIA/2 *v/!wxPR!9abQAP`M1mqpʹa {~ޣ/)X1I 8')ΈSx,4uTjA9O; 9] +dWX]0<")/pK9b Af(C({8c`-ŞA>b µ7Z(!0Z㧿A&gwN:?$XA#ad\&}o%;GA\NJ:K9 zk9wk(Afȅڐh(X4ICPy8 TPd|,w, ޺j?]+1S7CcёA$f 1~$I|h+D9]>g e MIkO[04&4^5?)XX .>Z)9i/|39b((&heo{KٿV 鸴.]A]24.w\g^j(3TI3i\Tfu3v,uSͩ22 b@XM%堀ٔȒsHZYEbՁ"R(J <Y?!/9UgSfoW-~0t*ogjLkhc\`7!Uƣ vt#CrOز%f<7,,B( V=XSbK9md+o;$Y{҇Zӵ~ݍ֜g22j]oi& ?wTsI`L1P$,u "$]3m-nn(=Ǿufv:ήeX_qג(%Ԍ ٦C Q+Xd -0_dHjC -hq%ޱ K&Kj,N)J9x/@:E9 2(=9Ъ,Ь%9̢#UEBFQ jѱ@ }TQ(OG}IDzq;%0+J0 {{\'ao)xK C+, M!גs(]थV-8և9@3Tc/R_w`b/+T]2ߊFԠru$ztSA\AkA j{v 9kK!K3Yr1Z ďa#`2bra1hH*4S#O]/}I=]Q?$oyI, !$P$}Ң;鐒/i IU1,l bO*{$Rю8t-Tn򷿴uO_f}_E( P7MhD~Ւ~I#Hɘ*hmG<̩WYbC˜5 i6q%OD9X |J vQ:w<i}7pջG-R[Ԋ -Yʑ]b&,҃!C2P䓖'iXCq4?(G[h%b0&epR%D "PGO^[e=ǧG3C߿$8bl)T$Ɔ%v@<3hg$7ڐTH,JKblЉ,VaP \ARԒfAb S7ikxJ|~񷦈B/}Dc'%H/ z9eXcЗ2%$:sqNU/$*+"OlL-STV\ѱ -\GfG<W8n?W5{BS8\)'!Aϐ'aۢ"@\8>2Y/(%0\#1L{[YQYJR|) `I;V/} \7Y&ʭ=ث2A"^d1O$qbP5:+ѩE 9 Do 6\2=+)3pGJ>9X[rqj*@FRKɝ`,JN2k )u_K$^川il'jzB?}XKVW ]$[118 QeBF5b(T;`[!FRfHDZk^uBV#}T9p{|+q[CA")$@;KqÓҘ$hZԒG!0<*h3 Z5s|ߤ{-g;o]awʸ2A*i8|Uac3" 5V9r&z+[3[!vX&xW.}Srpl{c%6|/)c%>SLWYzp4a2uE4PXpx۪\2c+o]Q/w*>ssqQ}R:l-Џ@i7%P3fBV\Jqi+y$o|C\e|PBBf>b}՗:o[>N>[]gS)|@ qdqLsJ}N[oMAiǖ]ius [BAF)z&cSmIG}_}c.\GB^#I ! }Y_[K$I[[_Aʙ%=)_$t%ԐЫBIxN@PHqNI)~B9^wbHGO\jCRge*H0D VHmG[TnY~_|/tI)_hjGBHFiͧV,@ _,x׻'!cBVk;K!J+ `gDb$7@=s/0te# }+3MZ%E$0JR6`h~ '+G%U 4 ORF5͞GX2c?.3o[ݾ7lգޞ5qh 89#`1(ҩC3'dե8 )H ,heӕcr\c׾@"%+tFԟ#PcHR<57~ݭ+;!E5%ő ؘO޶6g5hmnO_<ӾG˦+8ۊSd87~L? ?ww0oEEV뿰UpeMVG+vT/bGW/y3~T?>]=v|#;fmfmfmfmfmfmfmfmfmfmfmfmfmfmfmfmfmfm.0n6kv|y{[oķ'o~_Kj"@d=C>ԏ^)?Pw{xc߻֣i9-_[;n e[fbQ CBߑo~7>4/ESAUy`yݔ{0.=wޗU~כ;&wO/q眺h}~t..U- S5ofOe6Y]E~⌾5١y3, f3̾l%ZuV}W\.vK:8Z+W"ه@[m{ CԅR;ͣ#2nOs|_ `;]}C]=ySݾRfr']g]uI~k /?iϺnyDc=4P&8ct{vBhSqs"eϹLj],;|)|jyڴnZ6v?ۼhmD6u'N }~~Jӏڈ77)5PG+/ikt7Us.gs@L~"s'GZ)7zѯVw=.~ٽBnl=Wf Kvⰼv}pvA]߿]VoR(r'ܢ~-hJ{yu#)0rz.!ؘ=JW}iq?x3m30?yh<7JaWҲgYX/헭D1W]9`ϑgwos.<L'ه' Gx ;?7Z&IB.T/ԬYRjg 13'}f?-O}=:^PB~*yfceQ8=vM͹>0y"s1CI[A/ߟ]חDZWlɛ>:ꥅyw߿c we_'9rp;)/H7;^7V-3^0^G&^QQ(ί#!n%qWN;=[]$1QN4 '}{v9H)Uf6+ĭw4N]cF^OϿ5ꩌ oV>-&/y*?lo% 1?l5d\0BqH?Wꁓ@E%EE=sT<[%GKŞg149*};n@1FJJVUrJ^tv5*_9QRӳ=p?sF 4*;<`~I'q/:ks gŕ{.* F>%mW+\4W: f]vh{c麟kk0]ܻ?1ܒ$_2;z??^ciDne/ɻ ^xp*7s~'g}-4Hᜁ0,';^bv؛pC{M5^ 5 T]<ߎB!knǶb鳝pQ]/hOapw'37^^7zxa]EyЍ D;ns➬xlTX5cXH2{H.D/f@hm_}@7:ߑ237qQXw}~{*ap׏y->ș?F:9a"fGueK SJxTz+)TMJ&ͫ\[MustqoC}>xJ:Hzx1djxy8EyJ. k ݆<ٓckefa-{an}_{ǵzRcBE183O?h?&j#ScVՁUa6j!|ķa(ZGWgnv/F=0*҉}hj<U,2{~`0(8@zh$kdN<2Y{Eu)<)7hJI)/wğ&_O⣟O^/" (&8NDsB({$ћo=@Q-Z=_6$I"/)*j'ӌ0&Z/©KKO] x^@beGy/+5Y66{F'KMW--kJR1>wC,fB)?r53qޭ%! fi59(k xW~?gR{b?6U_*aĂD}T9Wqm|O<9&;巟k.g# ș}N4ˑz^p{$0T =2DLLFyo4E MⰦjmGWVV7׽t=X]akտYog݃%KOfgz[ٷ3h$0 yØ]vYـm`mm0y Fi,#! f4tOwYVթ:uy2sB@qi|w>}>?'僅{ܹV{G[\{Ys;W$mxq87;sIV{}H aRPldf^b\^şgW]v/=K{~[;xn) 測}|/Fн$o]zr@P@ATkwI=ýR#Q0Z/$rsN·j~? iν3J7S>.mri:;o@˾]l UUSj*rK$$AqyA- 'ÌqZ,t۞wޢyr" !_klr<5Qݑ+Oz]Zgt~g\xɲKV?V}Nݾgxk[?3/;BmSU߫$x3 `oZ WZ-t8ǯ]ô r1l fGPWș wZGX{5V`1O=]uCE߹o؞3| y>:J??1}t~@w%ݾ]BzҼڲ:EAf zM184DƎ Jz-A4bR7E<7?ΣHNӉU#pS\7n=G/yߗQvf}s`?}x~tUݩ_Օ oC#ėnj/4$nIJI%$@1V0ցagb[qGIml[7үxas6;Qz3/`cs-̧9r1܁w|RD 8A=B'g4Gc*mlGS? Oi[?wz}#O쫮_ iVMb珫f6\7?|Fo)j'wj~:ľyGckyyXBȍ;5wb<6$0;v#6?h}w5O\1wszUjR i`a˖v'ݞY~_ nԟV_:ȷi8`]c6[ڧ= FTWU}W*㶌_^xA]]35Tݐd*8m}dbپ0V6}J0dd;|-Q*^_'nϧ4? ݛWݭξ龸iyg4C2X,=zE\Hqk9:r_VZEmdCq`8˖.ՎouA]9‰⤶޶DmC 8  c~$ 0ȈQd~^{\k5g_trR,Wo啖|c>oOܾg?t{#6.\~ݽQtԂ+6"5PɁ14=5Q1Q9G\G)Y4` IttΗ'a`,sjqKI[u kzz Nxe囗\i xG;Gw|fqn#T|}$get+4Ϩ}wn 3̙- d!ڏl޿z[ 1DWkP^V kgǘeMuK/ɶ{U]>}j?mՙ'>O{+7>~ pN?\@TZ`kB\ޑm _*c*$]4'-F[*\p%q%PPR5v*996уI3x I"ԱOXHVqj{\;x PePEѶ!ڰ"&=QࠡlŊs uaZM<}?g>q#_M_O@c~O_zfn-i+owCެMvk"+ʨ\i/}mo7c}K:!JvN X[OZp[xz/ ԯF>|#ܟUCIV/Z=9=% ~o5V[1Yo 'o᡻)`l›-|g[yE\xTntX-FojeֹPZ?e1 )Y}Z zIA+b5 F3^PqѤ''J%=m_w|++r<[ng6:C[9GW8 Ia 4 CU"!ԡI#d'[Qg0 Цb-1T仚EpUw%AƝm{r7w~.+ߚO<f^}޲|_oOSz{u?Oܨtkl/iW!-[!mҼʵխlYPwqKK@FH*6 ) E5v#!66,_5x;|gC)r2ROJy'Zwk קvWO(\Gr+p73_ܮ$fR]ɽ;ʦ lT2k㓊ՀL'eMڬ ISenV\c*,EK@*{ FʁNf o,D zd|ݚ='/}-A,ij /Q%EXHUV0}AX$V2/ Lw`A0PEU%Ñ@[K8 TShILd>2BxTrDNc(FAPJfwQ ?XKD.7"|ݒsv7vd1wWtvVi.2iFit!Yiǂdrg4S,(ٺe]`-._z#z]I]B>R@TH0VpGq-4`ǀW!$IhIJR5}F(+C5"ȴG}\c4=j?_xφ+gZ3ӣc3 `g{׮`ch}b+H"r[WzTV{uPMVa桳 ^x٘RdE#PTy : IDATyǟ.;ŸCy@C,ԂWHtG$A@k/]]ֻѫB»Sk4$"f GtU7ڷ()ξz>|a4v[SGw욓HsJCd AAө% U]MB xMs>r@llT| )i5BY  6B 㔦>RX!jzKSYҮSYQ&Fh  佚0\,,b"mzM61wS<D{HV +F.UI?kese:_@iTInH6;ԖTsr YgJ#YWcKR5z1_vTz՚ !YpV)'gyM^]z=!zW,B!nC&fu5g^ -\U;X ByƬ.>Hɺh-ohQRt,flgk-ZʬilZ1htxl2̤[Xvj 5̋ _YB)i+2 OƨRL,iJ9@JY&ԍ4v 2dQأZ/k]~R{F(F(T,g$EI"!hdP}ȴrRRQ5gEE"#j2~t#g~S1x +56;#F'HU @1 fFg3) Gb|dz˃w7ʟ՚n#G+\!UehJީ@" ̰iy$[X+ U3ms9{~O"KXxQW\'Lmd4RtF R Yka N%Rjch@Ւj/G~ 1T3F%( sRS`P3MZ yk};;hM4rZL}\fda0d/S(G" CkAD5TR-Q Xь+9H;PNah;̆3sF*tcvCvb3A#WW>=A6,&az5,iH'CsL ̒(`~Zo.(isnVX^SbvJj@1t^z>1(~JIҰؒf)3G,58J9"55dx` 6%Nl\ST#pV13ai:sI<>-# 9z>?UޛwT?5JHJEMS$q&F0LwMlu}NiF0548ilQȌe>T&-])[0Mxd?2 N,KDŖ MIZatNXn;}EVv%0xuBa4ّB"Я$ƧZ]?>s&èCd]ZvkԴ Iy"'xdO w " Q@hp;e,)PT ^r±( {T(_vꡏg^K沑 A\XtY(-zjOwD}L%q' ST Q0(r] "=rc + 9c0EO-Ր騼/AYY`Go"u" OIVh"lk=g^ǵ>ٛWyS KT(!5㝂Pя#jB'N eb5ܛg4u`aݤ]||8Aw) JO|'PNhZ' UH"%I qlYL*zDK/OG 6 _'?7BKdkq׼0zN:gÚ4X!ѮČ, [C %eqE2=QKOl(yl ͥEfv-Q౵8K,3`X^ Bf%=L9[ߔڽf???٬Ϋ^ W bШZ-gxWLPXK83NAS'R!vrrˣ҂;5^ñ䇤wK.>st3OQ _\nR 'Y:(g5 RG9#%J`گnC{Yuֿ_m\W|!e(ƒʫ)S陶dRrU ڄ,8k8 pfka`<]մ|A6 P}3N%SWRFS\Z-V8epVfJ#@;^ԑJ9LMHSY&&vqF#lHҬ=Nzd_5dӊT ER~p^WIzQt`m+3!NYk-Z<^h$zW=d-$O!`i<0e_լu<6mK䥊k[GE6*kƪeqKyţpG5R&^HT|8x$}F Ls*!ŁwiY8ItxD2蔊ܠ|͡фd2$ˎ3#"<`sXS3wcq&d9ޕ4DFKYM>A)?xhdj TZVS*H}0o~{iyٮS@x[_k] a#EXXH:#AkYaH=nWP0,r `_Kk(N@! 6r4ĶGD">suO !%P߈vkeV!wOm5˲~7A:ڲ-I0%'Sڲ~9bvYc*cQ׆,|#Y[E[V?mFJgH|%%jl&qRӮD5m{f?;+TG g qj4qNIyqgOVx8oFB>&'X@c,JÚLmɒ:A+i@fpkN nM`=јK+N&#P3ˎѬEp2FѠkC(S`/>r?hu}C^O5 Y1=C56#:t5*wQOY`7jV a0 P +jA Ğa %hV:֣g$6K?7yu߳{6:/]m.UDi02`-f,hu=DhJ`i ac|(3G҇u#kLb z:4.K/X'Ӄ6D 4pSSS'sܧ? >uvrxui;EU# =uΊ(A$~FGzܛq$ Cjkk퓜$O2]Th4&,R @ (5BB4|S1 x'٬-.⢒gO=/ sXkR!"P݄ 2BI? Sx-Emʳvd£8VL嬯z\OyB{QeŪF C#2ȁ|} YkvrJg"CJZSnHYЯC, \š@h1(iM_BxQ~ĤgL,P,ݴZ6P!5R*%T"KA“|Ks~eXxh1ӄB3*CNgM0jAXނxdR G( Au$ZIRk1(+q|(JH2H%POգonItU:oA*P狝p"i\sG,I[m m`R\A%r@hA)wD"?){ϴ߳7ӡ@u-X5.,L(E/i'z 8乧7h=pcGrtiw4#BJ (-Jg]6M޸}wDe9ߑTfTϩ(H!!ԑ9,I^b(8 AEjy,GkEDzrȬ9GJ dZ<ya<ԇ*i^5;@0_] EgE%2D;Ya"U7PҋsJO<*IREԗv *3-O5h#Ɖ7~l*Wάά>0UVفXGr@(iԴrV@S!*$S:qIJԓ+EuբsǨUX?j}ε8ۊ/tҥNvo(M$#)h ݒ΢dgM&4:T[" i,0VVgNHXA$&(wx8BMo\.-|COUӕ}QK+$B q:T/ gQiUs4-j͹6%%$$yF$J+{$On g M9ZOv/|<2agUJhNȸrX'%Owy^Tzw hl .O-ȞI<\4K (ֿK:NBq^\f4Tܣwʂ %HeIL&=y#G[O#ԌF8>;5{8k"h&.?S >$? 1 Ku=2XxF9wPKtĒzHJSc2>rA RjVHYvw ꞿ |[n1ʀTWIUH:G:PK1Gv1.EHG!UT9RW IDAT8eQ))YAR[KYwJ❣lO~L{CΡ̝:*49Z^ЍO ~K7~vD)eЕD=h7bFTh,鋆r/j(>߼ [BjYR$z* dŰ25(p֑'F_qFJP/RFC'id c}IG.HxgٓF}}?@ q[c:H7(eGw!u+{SI"]-Bޤ¦1Z9pĶؘ !‘!PTZm, 2*cF%M3,4.Zֻ}vxF/1qD8oٻ,&~ߕ}ި;}hnĝJg-Ri>K ]ZT1C ߲yTRPmӻ>W'{bk?ReZ*,Dٳk}VzBxqz> Gog}N?Ԟ>tpR&bnBlKx *9Y~Wf6}I}| |Lu]|t=kx>x蛄x.ULbHu&eLqqsօ|^b\RGw IJIx[, U.Zt҆ks4f wn,}Y>ITNiC^e@K_|.y9Z{O?9^Uyt¢?uϽTᗊƟZeq߫/Ȩ[ؖr|N*#OX!91GuxFs*lsx"?yjoؖK:k?;?{kgdzkx>8fwLV-Y'oH'*_JJ+4ʮAP_WՆ+*T-0zJRn򻚄O32Vcެ Ԅb%wO#j^]wݿәY7'oZ[½E7ްq Y0U4_x-lF䳮vbd6P"$K1&^{ |x{%;>9`[b͋k=)Ƀ.zS;??tmzޔޞ_͉*5;y})8"^Թ L>a$ /~4`1]y#dD?xfrU÷F'q/tGey?-o˖:xȟРċ~Өv]p.*-gyܶZGiŋ=EZnψ5Yڨy^)n ڢ`z.qjA+SH$"Nd鷛@tSeMsV&ߜPAaa͟T Yxy۝3`'vy7[3`E+ZbvaX50k֢𥜊:zBWwuQM9#k^\x>n];8[Jm@H (=uwi-<;2&|43[aKO*ES9<80gqD>{%zVOՐ_Q}7Kd&k\K[=+0>BC4C)I)T&$tHd=KcrYjYX>@RNAp>(kߚ N:o:9u[M\}hd+bO>j]棸u\пfYj!&\eQBRwu'/4YG'NŇGb?w}G @}+y r6$'=z4sZ;?4|UsfIt}Ï۫ΰA2?(R8iSQěh߱#ח3/SrUp`M VՏo}A/v^0Qb *: "^w% uo%#kz)AO2^*q4biams^_Ӈѝ>8w#,ca2'.1/FpLs4UJ+Yg"ovdLJW_Tཫad'O~ߟ~&x+_2Jb6*4=W)k;sw_K̳[/KIY˷_~̛W՛~:[}N=nO{>}>a نd QSJ߱G&ڼ;yͣ{+J3ՔgG_`AMKz%A>ƫDZ6-t2V+Q!e3@3Tjɜ'ݥ2~M`!ZQ_Uϖ-{wBrKnޏJ,HPAp9}ڷpeK+(]$?׏4E #C9Ta$v'FH_?O^:e_Z`ӧ^Vo@|MmRB3axgW_K)s);n9sw!d,cڹWs %v?LD|:Ou_о@ʹ 63%J,o17X[O1oBa]4'<{tZP$UKNLL%Wocsd8'Fl9 ߷#t)yl FmAf|8Jon]H{c?]܏{6OS[͕M>FLJYj^^xm*m7^oT>91 =]:<ڊ#?^4ZWv_/?r&tĩ׿`${Zw?@m5.a-327&nU&T\Ȳet+wC{?kf_03gJSϖZN@e3M&j>GufTsf3M?v/MsJJ)2wx)J JwҜ>t3W_]Wâľ$@L?ɿzZr!0"9+>\2 =2ԕ)ıƖ[&[)[b5Ns b3E+VG#߾}J>f8 zt Z*[j3^zov4E69;oӲji,x9 L\Zbbb T>/zg#3ةd; ݾxzZ}RoR { S8 $HrJWGޱ?9uƎ|y6'Ί%!(}d!ݽ ( 1q|{^V Ij_A}`L?JQ/316pG BC0[ۜ>|G+lEg i]~ڟi%ЀւN$N'fo%k]V[]V{1 @"b똩U(Xlu3K$ǦjeWZ_꟒^r{})J9n8k9669f)fnTw??TID lnDi"im;om@_Mߞ19:j̗|eLw>~&Gz_&9t rK.Yg8H@J9u9qE)(eշn>4Br-zܝdk.*l*ZSutmaɾ̯Y#F\DTgTo3=?;XaY@ ft,G((R6<`tI?&pxsɝ# I+%bkQth>V#Hݔ^7%(R]3,("?rl|`;sWJ rn_;PQT&xd{4ȍw0|!R+F6$ h^&W_VeCc5?n#@P>_y;g4?gge=?Vfi.>M_Fɗ֍x\6/r$%Rpd@z6lLI.B(ќ9|;{25%?GW~_/~ș^4n8J%UktoOcuv!$GӤ8!LIƽw1 A8*!" Jk2HR"B( { J >#w\g=n SLM%H$4em5eGF6#%CP6,!I-F*Tݰ^gZd΁PJNNUMҮKuz?3W_wGR[^eq :ZnZH5)$KesQHTv MJ9*qlCV1\AWpl͵7H)|P{s?vWse˹e~V>SrdFl_Z< p:;OqS qH%H͙Go~h -?ΤV#%gy^˽OyI) ?pnTLuHIU€s0Zs`^Pi iLwzsћGw>=x.6wOq,$stmFf=H)AIr,cWQ~(8eΛXS5 DKRP3Vac[Jn3Qml }7VmIg63y QBl>fB.Cs#>>!FB| Mi}E[{-50ꕆ6sQ^ԶIH{[|U1,jp#>1owK.TēC.# $I DXb$)]Bcd RH8gB*6MoNnKfw+<~Wb0!w,&c(Hfy=]EIgl &aQNUz E%^ho5Iu4G Q96F;Ph> !RƜRIj,/Ϫ(G73W@iǑ]SGnYWH*NQQk%릜IҜhƄaG-+=JJȬ#NsSҚ#Hg*!9r}2sy|r{k · ʓRuT,tx) E?Ƚ#PF8/HLp-Չdt{Z gg"^HͦIqtYFӋl|QoRWzo!ByLP+/H'$%'P.BFtlT "K;gP?J'Eɓ:{֢!Z0*KDg˸"VjUbZ&&0h5[F!|z0IuPTby2&|{Vܖ}o[$||[GFyUkqVL'Ț9c*]tLW5^jN iɱD8eWѦ ړ6%;t_(l~x@Q;wCTA6YPJ0:᝻264﹧t>Z `_/KIc#4) <uJub]+#2S"!ԌD!r(fNI3J[P8!H:iu&(!B2{kR*eyRؖԷzJ-mݵJZim6X.)zpv S!73@KYo2Yv 3`U@.9 _XZu }ŖgH@PO)8 Am{Y(w=ތ[t٘ydFDžvӰRfmÉ;z&W \@0:N`HF!0.dk˳%fR\>_!HyJLp;؄s3[Z+9 Fg&:~5uz:cbFǷ.xV2UAѳLcL64ŽZ:K1I4e7Y>Gl)` dN(Dh@ *xZ\G)cN4:>4fvӾx!+\r6tN*{e8QG :'vR4"Q%z ab$OYF7S *o꬘ .SQz~㐛AVTHLQ#٤?]VG<j3rtqriҀlQ_u{=^(;ӭLJ IDATd.bYP`.Җ]\C-" Ң3Gִh6 Q mY9b`cOY=0(r'ы)A3# PMNnS&:DžY;_Re1i+Ghk6&w=TAFY:ck,.kI&rbF饫JS8C=0M=KvT"*^-m;=ܳvo8{*?!g"CIAuyc*5WEHtB=%48([R\AXYpźjyHYdyOH3h$ĖwrrP.nL$7 q#jmNجHJ=>tmy=;g AUb sC:=$֓XOQƒQJu@[V*.*8]ӠˬQ/򐳎uX ox#=9Ye!axY{Kub[رY#fJܳ F&ח$w v)7!/f8IrKOAf;`ω#iZJҵʨ!_u !} XwSL&(!#Ҝz"R0Go?s1Y-*' YQħ`ZB-'p>%w}|߳mODE0`KntY$I]vN:#OѪD}Hvzdj8IqSTI}K}z6us&C_le,C*GdKO7IPiD^: {g1ĽZ[h (9"d(@D>Q ŢP!M6d!p6/'RܖLg^#ZIǝ:87ΥY*!yG/( ~La-qt3OIz9Ĺy~C`r}(pKFHmZR:7pMZYMIq͠_\)\ݭR#=~3=V^ $ 9) -D[zmK͌s҆ 7 !FT$RB 6I=ǧ~\Ki}Y'T6mÆ;AUd1H! .w~SňܤڬҸO2[fyzQB4h-+ӔtL$ooηo`pԘqОށ̅Aw bR\%9AB!]ԸxPr ȓݳ$Z,L (m8wZPD{l̦c.H26%vj4UBڔ %87WnL兿ybmMZ{)ynAEzO= y.ILN*uw,wX TPll6c#os!$2(EFʢ|J)e g-靹[~[M 7~D"4lE倰/l[{xS͝XuiYdgrZk}\K,,[cǶl+q !! !.tMr d悘p}\rQBn01!ĶlYJ5WSgZ8J؉ȡISZ{{{.)FH}MmSepWlgIхWJН,c5&(Gyו>&IH87NӳhJGT71v1sͽEuM.p=);gzIPN*sO3.fIM# "Z; %'YVl `G)v-:ks-%$:DI Aѩ<D1ТiNJfAڌ!Z,-k7J"TU KU^q6qIF1(% Zd! t6n1=g]yz\s|p{Aml6xJ)$Ҡcð4$A9g(n{]1kW ==@\tWUcإv kCwO#1 ^S{Lt1|8~1m&ؼUUBl0BLw/smB(Ct{Bv&ʬ5n@V]ǬVN:ŦqίJb1Zct3:@Ylvۇk:ڮ\DuNtW7ƚij}__BkZ~i Fw! 1@ِ<&WTD!4 Ak4 )IV|$M$rB]@<=ea=i&XہܶC\Oeܣ2ꪥ4 ]ʗ4ۚ Cf*$U$a_ִK'הۭ3$>>9⯔YD-X7O 4JBE@(<+I)AvR5Wt;LPd( ;a %#wR^GSp=\/hj]=7y䈗;"Y"U5M iXȸͅ*mz0L"Y;T¾GB'-XkN;Yr|VJN݋O?Ț_=krRxpw@h{|ct >wA4%T9|k B Ʋ& ģ 0%1Vܙyٹo)(d)QU_Eu"Gج b ar]ms9$lRrJɱQYTM_4H$$ |c5fuR(AI{Gy!-TmfJ= I Jha6Q:]ӤR{kbBTʻX3( :Ce?I#;H] IM{G.4-4yq|GN.U%.ud{6zUs©#b(Ip$n - m5K',p߁Y,Ju(Foj,=sb[+ ? XsPr3Ccz" $DW\+Ԉl~2s^˩E\]c Id\*ѡ5X򮃣cNbG'^]]\h,y~x# tv;vMM&Xu)Ҭ.`H 2QB ւWHr\+qxac'SKetJw1CcPN]>Mi| ^Hu,n'[̺n @GQc[|Zs߈/d`ra IʼH)%VaH;AĚva:Z= -tWfyME[matEGoy \,}'%r>": ,~Z).2]\u:.ǵ:zD pt X뎢3 8d5tR=#/(힜(;܇|d%;Vf| Gu\q\%_ bK(ix#WNExkhrv]g_Ooq;Q&uA⵲NpiOә{K 4iurDHyؑ^LW,=dw ^^fWĐ' f gYcVw}WE]l4VF@J iEO|c7~G^=$tnv=!O2Mҹk O4kܚd5i#HQBR*"#9o}?pg''AqaD'1Ѻ8@P)UFƆu<␙,-%?B*#%ZQ>4~@i<%XR2>Ru:NqNat#j8kLB'~1qjS+ۥ:9#R>+PJ SޙMwy:/㲠 MfB+Ȁ%-u|%7MWCr'$j3ѱZʰW<3C.М;؞d (=Tsqˎ#yK64_l+;H|_,UgD ݎ^eVb0#M{]Nj}N|CÃXsD*y)0Iza[T4 {{F*1]7z%< "y+_Tpb{VSw*Aw *W-yti颪oy3x$5*Ĵ3_myc|ϵtRU' 8*jk 3UZ&$* 10ImbH_b3Z%lus % #a1'|R,p9pLiDQ~G^-g}bRoQދz}\/9$>*/ 帇1ݧt zPP;Q^| (T)z E{[ ǘKU#Ra!{ 7-Z0IyZ/, d ~7#tNuUE> >^q=}ރQ:xyN޼xr?- !`uȐdq 6񏞙N139Tv\ ǤyK_~)?oJ(A$􏊯հ렓kA$.Fo]M =E^͘Gk\tx&4V= $}E ޢU j EX9Mz-_=`(;Op]Jӱ-w/8ԅ]kPٟ5sgA7^O׏{fX^ 6(C9/@+f}٬'o͟CЄ0q$蚁Tlyg 7rs=e Ifxqc`} :'yR2{Yu~W']@/{V+Fuʮc!xb 7~kNOxPWr2mv!Ҵ^fD?8( (WC=}}g~ĵ&ءΆ /lvC)>~l=ȣ'|+US7:lckG;_B)Jз効ܯereEg RQyJȝǟippTʾޒWμ,/+2y Ϧ麼W6`䗗np*f8s*vn:-~9g;6x;^׵mfWrZn޷UZ05uyUUƓSI_Z^MwXq?ц#rL6zElQU=I_d>xK۟:^dQȍ =Gگf{-sсi[pr*ٸmP=PZ$I]OGE݃XK ৏ί4.oqL<!%yݤw!^},;o0sx<5Wo<3ku]Jwmo"z.Թ9ȿޗb fe~?i4`b IDAToۜH|rwm+{H^ӱaG;oxG^M?xB^?]7OϥfҤaQ7~Am_o rr}~0 hf3|_lCB|]O/c:Qɪ W;'TF'nZU{-奣 ;8MovnE07k⃭.UW>Im/ 4nyhu7io1*j zdΏYhיVgr#_5Zoi}ӳXthfH_~O]Vy9#2"8p}j[/sl@(m{!"q.ň{t:ġJL,w+R͎}+}y.(.,1  -w [.<9{XSn- UvCaNǞNx`4}@A;?%kDnʪ4=8=o, `b^SAoNY69;8;oJB!veW5 O^vg׵p(OkX+addAFYcP߇-x@אyoU sڵ%e7y}6[z{fZ 'mn5: >k qKI.%Uvl̊ѹWgZ,\y)@HSD<P7][$h=!(&lNyt3uV[4d pv[*oUΝ蠝 vm_JZڵMq3g21|팝 {Iz)/6O/v_¤Ge1L/H(C_'V{}ۉRjөDrv'Ҳi]c=<⍬}+?7'Zn #s޻O'$B&qBdy]~)<+kf/;.*pTol\mƌ uI Wj%[7q6|e@]x҃^[汩`|v%Nk2'qF_g VB毭xFwѮ I]_V[պݚr|rcbx3ɕ: #gkѡ~w5]H @n#!73mWF0S[\{(TK8-*skVtv %*xI94o ̹iWeUKVe^"͟'\dP)z~#z79b(2 B4z8E Nh-e3 M3]-SպwgdG 5sK2QZ/(C.2p`q.g3 qgO>鈜ztEĂv3:g1u/k-s׬B iaS2#pJ+2",b aW>8׮eF^ln?oONM{͈4#U$ ^>ZX*ɸ;lϵDAra0 e&`.  D Kp:̕9ЎjΠ>,!/qSlLW"7rJ~|za,7Y]/p_",%!8dxlm{"*-4FknƲC/q'$߇܊T9wfGLvnk.s޻AMS+:w~onU!OA4ٷiѶ+kH}7nr16  xB0v [)<ԥ3 sfj:^oIKfkwz皫!Vl>gñJSV۪+:ֳz+_Ngj5bj2\OFԆb<0$ı&I5鈿vC.&78ebaPe$ũ tD>O~Y1)zJ21 čhBhiCiģPQχz_59s<|i0^ϞcObUeVW;]x)Bњ6=50cKrawo{-wN>! _}*2m f[|ޔQχ7L# CYcGGgK֯gV\C\44d:я[щy%zx{8=8G+AkefCC)Po;VӠg-՚2NdOW~^%d8sL|1)I!Bg˗Wvx[8@%eȐws~߾qh岡=SHn_(aD$$nV Q7f! z-A+ eiu:eME휡^CyBR@**J;^O^5V8:v\72V͗ϨWSbvѩZ8ʚm!PA * SC*|Ga+IKIӾxT,C#q#b,}MvKUsSS'?x 6A-:j&+>=ewɳmAT5k yt\=DJNFB^MTJ\ϵ^Dlۣ?}$%d")v]r(jw] G>̶X:b`L2*8x ~Yc2FXC:o C2c=lx.oxtp-OmV)=qNa&F)hcEK#zOwJj2WҾLx_^uj-=XOzl{n3W&g+'Nm?NறטwQ^vn+|H; ,=1+c= v~|#bcKٛci)\$a0Z#]@i $I B*2#ލ4G?556xy欑y;-S?9p;;7fNiB,5FB)h CפS̆+(i : 0fk1Ų@YW7Pº&N4/] s%߷MO_^|ǮWr/cqDB`!҆v$D1_ "ɻK""T4B`Aa%kīf)-2Jzգ^ (YM-f18 f xz춁Ge'"Cٲճq߼x,CP8^V=+ M|BrWJ]p?/,n.ĸAqCѠq0ȗ=gmrU%|sQ-WH>-~'np͖]VBFVUeϜLػG9&;7/ږS'ȣ18eIcss[WL-ߙpp<ֶ5ze.Mʑ1}zrU~xiiN3P 'W"(1e|o+2P6uP{ÕmMGfEesguK>tIʨj-SJɕy`Q1hd)>QWPh˒|aii܊b)"q*6I$(lhC$ؒ!@6C%hl!P$Hj0Nu )PRjMx4F5BZ2jюcQZVr^ZyRSwHՖ+2goLӤ&]a5ƅּTJnn3f5Vw~pr{Cy % @I8*Eںl`(d RdbL C1EVI?y??GrfS vRs_Gfj[ϴ"R5_1}6=||*_PuTMk@y>P \%zSڳl6uuuI!1 Elxq[-{˦:\zLYJ4 rKdIIu29$`$Iu⸎%CS^:nw7VLA P''g^nECEqhJIiZS Q|H@D=Pq |E> [T&v;趡ގ纐WD֐I=Ri۞p'l>p,[ݸ8NU-IS0))qm JC&ZxxF1%UO74i#\L$}hiCDSX]FA45ӨWfo[e͡My}4Í'|C1'GvϽb%(L12HtBFZV3R(LLE'_cD}N^dR. "~N`R$̜<#R5RJcӝi8hdֲXWlAiDfbgZ#Eym-_8>5#v)˚RpIR* y0Q*8^unB (wxͪ\ʙhJth[:Wqi068eñ9) vT$hDmi1X$R)y CcQyö _05sF3;c@UxnQRH dx@rv!]ЃzxFTiyma<- a"z*X  $ŗu%F m)9\B1`%- lnO|ax,?ZK/i|!Yj1%ŭhi(MXL-J'E`%h,#Ehxy2ҡR5`ޭi) 5D%TP$Qu`5Zƨ(ChA% G;Yefo,{dJfc1fd X$:Ƥ]VՐĒ #F02a! *x,!V̜I$P^$vs.?˭Ke{|JO#o]hYiK 2 EvnS!U1Ҙ3K) LSnjI`yV3aI@̶] 3gd ϯP_YȸC!HEHdW(bH1qB63cR`AIܩhTPc'm;[3e&#P&QRe5#5%M5, h XbqYOFCa_KX)(̶[hmH)PBƚ8Ğ`#Cia&kYʲ;`RsIrc_,>V5+K5I1[F4h1) @w2E 2"`uaǣ!;gܠK{(YN"6,Q ni Za7NhCqa&dޠ@xlOHTe)-Cb6)5=z_X)ʡ-{ ~V!ԩ!UmG;!qG7<۷/YhbZ>uHaz1%N RAsՐݨټ'Ԃ|!)Mqfɥ!XmAKų1Dmb[D& IDATnM ;Kٟ0ꕢmĪZMZXkȱ 쳴OHm@y SW$]66PsQ⡄G:uP('G}e_u`8)9;N@ G;4%vR!;@ Jb!RsN CcQahCM)7I>i'TО1h)ȦbeK&E`YN\O C+%M-C aHXY%c2؊"_S,!X 9RZ6EtPV3 d ư&EXJ:;t*m(D6ԿYJ}Lέ&MetLjs78ZP -J ĄS00xXmiC^8hQ43g6,ϡ&l+8_k39:`I#D% mi fԥ&!v%>~ːg`ףϳ~yzq:n_Q<^:(zc5,C$P`Cu1p9nXd"v/#p(d"r0I$'=})utClHdZJVvO2^ihy XiZ%IþdRX>k ˖J@Jփ2&X;JUKx^f2gVp=Dx%,oBH8kB*/Ƽ::5&+Ԕ?:Ig]D#@ zG' 9AC*xITU2b# Rr*=\ T%Y"ϧX64*asjGC9Β4^PF%Ħ l\JkcSߒ㺃rb5zL*PyEX$8b&I!08Y&.qmQLg9#p$Z3- זH*{)YB )>jL1)#zXFxH`P#j?PHL}?Y1S]]oAҸ?&qyz(bz1(]ыD;tŠ4 6B$W?߻Kg֯WR]3ۂcLHʞ%B)Js=E0g1 U)":f.ٵpҎ4]axr@|!M^c48DahɱlYKf)wK^ۯ {v'uS/8P2e,An8iwX4J +I7Xňh_르PH|Ofvi˜~yR FJ!ȣrDQ*q0?Wإq&,BwOI ԴyBis[P}LCHQ?BT!d(~X&M(3iKcO*cv8ċ D֙]IВ)CiF=N8LU]43qNk11H(ۃ0\P=GXJ 5X9]aRţ=~MiX#K[ziw )g=R\ B @kXD+SAݡޛ+#Tܓu;l;T=(i8oWHCl)e_%"m֌hfVRF*ג8X +yF"M{A JI,ÄĹ&srƐ8E`>X#wN3J j\#/@'q"d}r ;LVF;i/He7BwzAiSʵ.@bB* x#Q(!qm Xe@y^O+ʨԂՊfZR>Fe !an.儩ZHh siIx͗ά\ѧ(Yd1,(ؕ*V!! C ϣ iS(08\\ !:L#=Ow1ygi;n+}0y0Hǡ8g4"rk}_I grv XM3PGB!w]>#@7ƶEoʵ&ma_P،AhaL^P~|N``9ꌡ(^fmF@ϣiÔ,yWRbeWfR\91@)Ɍ%Zޗaixu3 :W#E:I2vgA*V}Zg_5*Y b-/8ZVA-J*b-7*%UxiC70 &Iϰ4Hۖ|K]yXџXO_i|Akj#5 "acۋߐҥK!n6k0X,G(>0xRkÕ_ ˑ<5zp8@aQ>GJ,FFHg *ҝ5(|O\.j;7E$Ȓ,zsw8R <()1Xg6#REDCL_)hEbe1w0RPBqfRaa|qq|%a)A B |@8"ϛg}mpJLo:/V,XRK8&4O1sǔS,Ce{@e,d,L^1M{Ck@1GsM<'/9.)L'h Ԣ k쏑E"G/Lvr^| *o(!٘ɨg28o9c*̞ͯRI-X$)Z֩G`bG4; Vc]Q+$k7u)(r P*@xRww~T73Ĭt{ϭu{'vƐ @AXNVq*gc`zT:΋>JGUPehk͌Sg8)զa=cH "[k^p䫿YjR^}R1§ЎFXXFY*ZI ֖X[s9\!΋1H;(_,v 7 P(B>RBmz[]*8RcLA%h!6K C! ק ֥a̮Qq) Z/%,hF*EW^Ɍ!_z$%AE)JF8g?F^0wHA!TqPE]!}dC0@]mg6QL*IjNNTF,Y-K- t4bTOcTQc|Z~龥}\xe#qνt *?&1F0i+!a%*n\[+C.Μ.N@0 JFJiλh{?7V Kfy/?%hPwujՊYFLR c1ڊR_>-.|or9P>ʏp&=0RݹO8oxޫ`!H ߜ9;/:Eb@žm"xqK ơEBBjB*a02=TU7pFRBuҿ.a%BB=GTC%"|e0¡bApIH$ȱ@A+n*P3BK_8avE<ɉ9Y0!J6UW4%X> +4&.G*y EN>B*#ԷE桑GEK2I;FZFXTD`LY$͔y3F:GzeHN1w.=r7}#J!dž`t֏dyy#7;4ŀL_IObQELAH5(kϻ& >S,S{ NI )L}a4nq 1\/1P)Q |LYGڵPR|4>˨ "a7?|iv< $/zoR_R@<պWwv BqϪ745YL}{l0 ?,rćw.=75o\=WRb:L0r':8[!]!Tyi|VzZG;Oq^TRȥ2vmx=dñ/n5z~yO4rR! ?8T棌cg龳/,Upz9M z[ls!}%Rzܗ=|;G_n>,}_|څН8%e`/JnF 3b 'VC4)Eaoҳve?W8T"a΅}Ko,A!&e cS,BCP>U~cRI%[3J_w/gSؿ~E4bfخ,͹  $dn>۹PJ'J`bMiv RB2Jrg\} %bxE1;0&![ ǿ`ޫ3Aו Xtm:T>Jy+as͹r,5NTQ*wtkF&Qh$9.RA|3(.t5Os9q\0KoS*݇atvXHu9l5N~7u<ӵֹCD! |ꥈ8ӟJ wϷ@8QR{#%v(+ Fݧo8~d Gԁ`!RGo}A>;(:TQC@sbaۨ/tʦ&09k♚ :  B,F' DyFgS nla #{<;(4gЯ[tvܹpѸ3=^pPAl4yFla #8ppP%Ҡhvyk"Fw? B.槳sk [8S<{ߖƵ-la [-la [-la [-la [-la [-la [³[;ܚ-lq~[` [-4 l` dY }[6:!5oT c0dɞǟ TJVc 'NJ+suycR' IDAT@ +~A]kw>{'N뻱^O^eB[x#۲ ϳw! q4F(eL(Ko/|vϘ7uYAeq]o#d@|+Qu ؃ښAzUR2ゲ;/91_O}9B'߱]pww|HSFW +w9˴'F~PyZ˹؃P1v5x۟ңUNNgO wtN~qj@U-'Y.9wWԥ7n >Ū/ .>{78xb{ti/8g{is_?2w}LPG/,[ov>O.k2ݞc'M`-ۧĕ9m6׫IAcJ,?׼m}nrGOtcyr>uCZt}Q=Ғnjp>3&]g7o-0g7~5H~KqwgJa۔աmyi ڕp gDݮ\vyᑣ7^\"LgM F~=~tqI;VWŖ<3:&ج.HzMͦ}Y~MndfWkM~RjS@ѩes`]FnϳU/97Noqi3Di$??2$ϿW#ə|fC*yt68-FCzsNLn+V(7׳P^ӷWbW}qu8}ݏw+w=;}v y;\SC_>XhZX_cldycV2Fթ_$4:"XG[5%u~$<;Z{N͙9l8tEo.X]xQ-/\-OWO8F{#d.dɄ}֗ =`G_{\s{7sCU[m誰.9}ƅã8ZfUW\V۝َ7-H5;:]lXͯ|ծq5?dGIu+@\wf}PҬu$ UT,k?33n4Tr;74J|mʑo͞jhӏDC-sr3֑m^GU6S_t&/&@=q-0q^xx?FtRz2'K8"dpί2ئX`ۖVbkfĂg?zdVZ1.=uVmn;2JͦE]i:2VN3N-g?7N_++.=UV5k km$f& ~G>rhU.8]f2xdye,f&'j|m$Ҩ`UrÕF+m&:'N羯 Wt[4x{?us̢;P׿5B5=vJW :8.&60@q0 bk ;<,1oK6W4ˏϑ[r^s8?m>z켫1>F6Sپ?{pu3@Ym-3HK 2*O=yz{#'C>pn˺Q/$u\7-~Uuoe]{ҿ+_7``GfZ?1wp]ҪkT~$4EiiH*G%ە$ 9JCZ*d XCrtp$1ǥGx+gw\*ĈPuAzK`b~ղ2~S.su2$9۽wM/Vln^U7ZV#oً%/xSɦ8lvl^scy\ddDij_-)Xn|Ϥ;8qy /ىf3P7ӁO컶 :2QJE-1)u%N/TNNMoW~HT6n] _ܙ$$)NJq 8ȳ^v#yZD_Ļ?>Ry *7{6ox3੽Y\gѿ-v~tZTs` N:5ܠ4RAw=e5ъ3>eʍe&EZz嚤ΑXCϗmC2TKt"@ )L ˊN,G V# EC3*McV3 ڋ # bG)tQlYYIf4BV[鶡q 9}vv.__/|:jU3Y1Zwq`&u zh慪f$ǒ0w7z=0gh&t&M|u% B';ߓve?zžQ_>rpk?27μԉ,ƯC?˶wݧw9'ຫ\N.SyyRGfp6)Ajwtmfw2qQj wPkY I FЌ*ra\a(/*r^&AK9F޽Vb'Hۗ>|\ɧB(|[e~.ۡ2ر+[a,QI+g0&ǮTBpq L`]Gh%/(c8BGA& J<_e"?{ 7j45[}9m6iK5UDڣ#5VJ]!x 2UрZ.;LKcֻ 9$cx{j?{omUy~gsI%Kd 񄌍 !(TTH:IЫ:鮕Tzi(hRɲ˩D*IURUJCIHA `cl˒-ӽ3?}W1pkiIz7|odSlxpYOj_kf}sg ͋ .FL\Str,9r}Itv!; SZAiKJ"mr u2(㬗ᇔo_ڴq[_=/{nl$G|T=j^j~"d7'$Eט:uxV?xMK|.}wK;-Lsu:ͣ+}0 浪M) EEMq[wG-p 8J[yKRa)%)QQs#sGNmltXgN^92Uc 4K V!I;kJh̖bc%Ҏ%Y`Ԗ8j,04E(ԛb]CZ~LOK}X^/玞ߟ)JH)%;wHԐJP"J#FXP~Bs܇t<16_('4k 'T?mX5m?fզR1.>:۱~HԢ837,_̝?wx+y>S츅q}qݴy ٖlY [yP{Nv{Q?v+g')! ↥ށu%E==(!dA]'6y͡$!14'J/ f VAo@ f%q@$IVflN c ӄd/pӉ_oJf%Լzb%<5 pd~,8=+E{M6 ϵmNLLֹR,+-Sh+jM{}^뷬Xa+=~Yѓrȉ;dtv @* DBDQB]T 籮,Tpe.z[O\$l}b¹lK(CK\2[[ZG[# [gu~kP~t(_lSc|0oayX-ʒ² &v,-\#ԝ>yO֓U)Hޔ8]B-h3syH(l$Cw)M9ɑPޙ;zm{u،U.`2`n>=Rv!='#^+Bo6< Y^! *>n92!E@4A*}ey!Q]Hm,oo,{[_qdv#q',Vc'm8F)Q[l\ŤJ@55""֚B;tlQ4\fcQD;Ҵ$ Pq4 ؉& XՓdG6IUG"q x#DeZ*jۛy{nݵW,R6&gܭRB yTY rRl!D HD=x%$޲Mxm͔;Zt%sqQSD\דx ;I\?e揝?tΝOe{nauwEz5/@*5LT8,q;+s/rmx_Lc p"Ձ"D > St7pN`l?n!rLhF- iv6Cz]f},9\`B;쟟e;+i7b W@?îV2"!mEޘe -zݫ\'ݸ(o[1*N0*ݜ@tFR |uf|.%"$XL^L1e3dSئN*+I,d!*rM[RK9`猪 $#x\6W[3{=\( {OJfܡAo8YGh o@!J *Z 5aiLo"eV2AO= IsG')"bf.ecHbJ=>䅧H@KUeS#v]:|j?"~jˉYJtbX9gD mlHU+[PiraꊭN_>ᆣcdclmb !,hM΂> fX[x :Kx\7ucJ.DH98K!T AҬb2* kIޘ]tU 87_Exq աif(shCy vJCȰb³iVjā4gIkӸ0dBRHD]1H*,+?TA B NpQ%/htx[7f޼7ljY{& [QU 2jfMjXe1(  7)q:n"{H4UW 눨AM#$ k ЁB VN{bP( TZ[>}iKy:g_jM͟qtNAmuU0eXi |+VmO>(MW0t}FJ| HGv.H{Ll%zFIIH\,hQMP$v[(=JRi#58rr~lKb}k~k_=}q:6 C.ZA)VC\JpDZ`Ī3-7)^Z'> ͽdk4F9Y`cm7Y !n(AM@T*Nn  6ksڥ~@S7ԝ6 m 46'oRdR 7g!gk3NNfI*BꝫlS@QgdsSuϴ\5cFQξlex" .%@\˩zXcM8 0*fQN5ɪ$;[d]ю}tIDXI$ӳ~UI-\1%¸?"w'>s_?}{}˽7;&C)ـ@!`jGK  SIhKRa:+-tw4BdSg}G1z, 6a@YXaM U5T伭lsPpC@1QL7>qB-|%>c,VZ:yB3do4HDHGg⺡ T]{RpdV8` _ :"ˏ-]"ٓ3Uwjo}wA@SU~@1῝ K\UHu\ B "ݝ>Y%&yNVZR ѳo}[_^M6V~1= * PHdPTzۥpIL6K0̎HNHZ NQgT2mYtv$sa"HYr<Ҥ*:= ZcYoIZF \I~|:|o6C G0hQDfT DQ%Ut=/4JzEI-,>ըh1 O[G @PfeGU Jaǿyow]r8'VPR3a*9zVh`y4Fwl?i}"*qII@U0sUm{P !>$eA! D28S־uϽ<7WWߩM}9{P⠔Rr*`';xn~PUer(VB*2.*[]]ٶ㘕^YzYlF;(7_j;Y_^M֗>9o0:*U&TJ]ϒȽt8<yI5^1DBL,="r #a IM{%q! X#-'V`][O.z]8 \-f\l9Z $mC4k1M+)8llu,ZHGGY%[Tg_f1T:F[g5o]yB;zT+â8Nji_iVB΂T0SJCf,j06#/Amѕ \:ĶB cPCQ&Rj=}u- +a֘3/sƣû,5PVCA OJ1TB[S%H%jV_~+B;o~5RA*K%gX{g\~ܑi)) **ߚ)o)j Yy!/ה:LaBd209JiEi2#JGaJ\D+`cyH;<\j eN+L(dM=Q {AcYH TAmiT@9O.w?7j/NG*NSb%4D,J. hje$؁z],0aÖ揽KB=ٰN%ĉV?C(AkM)4=Y4dTv{p?lSBWA@j AnbU_"Q5_dgw=гZ䢐T-LC W"1.=AQ~im",iK`ә?=wdqi)AoYMyys' okaoֲ Y]& eN,w8]%c84ꝟY>7!n-Hhrᘙڒ} RSvl:@(!?w}ɝ0&g^]GJcs}{]߻WEz"%D~#{"RO'l+{ǎ#9o,K;%e]S߷ܽqy\)qZEC@lf%K%#-+N` g?uޣۓEgH^zн:qM7w|/,uLQe*UIa׬eJYa" je-+qKsGO. N)ƕ;S`MqHuY~P6a_$@jdֽ{/4]:W;f7J7@-[mhl72}|j9qZ|$ƲE](Omֿ]W^5/jQbO !N<+0a1BB7F 5z4g_TAxZlQ fTF[Ȫ\v@v](b=c̷fҷԻoATzӿ5ѓqZju ;_/?[_ ݬr|Q9P0\wlQ SB l">ԁ!ձe9JCFUw{"RVB AZ_ 4X^ !̋3Hٿx7bw@SJ< ڹd5b0ou#( ݴ=:%]2~Cs%֔g_yڈR[;8 e6T|/|O ĕ?Y(D{+5SBլYz!V#T=Ew N )ز'eׄPKG7/:k !/~#aFaFaFaFaFaFaFaFaFaFaFaFaFaFa^Tx'wiKPaEpRC] [4߬]Ow{Zn{ơ?o=b{#p]:?5G~uO]CqUJ=r5ɫg nF/d7o{lK+njʱmJH adoD9 i/n3yO|EfQ4ʉ9t{^nc#޶c++e[}ö-v^WxbVLܑޞCÎVǾ `1șqB נ(o ?y7hg^U_qļR.4&0wRs˨ cANq% xָ[Kq^g]]q1]ZOh U{rlfdj2&.6UWx.e&~.7]wߦkgk>=јƛRyER"5xT9w|E2g7虬վz(4𔼎i%160P%RUD#?wtɋ*M .=ut&PCx,uaxR~]nC-9:~M .[y.բ0{K-z6 ZU]}cy%:~' O%}ØK`MCp.Mm~lxA~G\ =/^ua}X6bյXR2AC4߻ZK6\27ݳsmZx`˫05G%d߬8~p_c5s4mtl}ݿy-^ͯA^0ûSr-wVv7*5o{|29|m{_¨ěo_]u+dž ̌}5XIRNZatFl Aנ q256OgD)~1 U۬+GIgaι;t R{7uDR1L^ҽD;E0lA{)%E*+W* ގ A]R'~/5;F{ KwoK޷13aHԐ0HN\^ $eyP-bJ.*%**)%=*fI,4i ,6}.rq{b%ݧЃoGy'&=q9c RJeޖER*Q؞?q摅SgeGgf7~ѩ4o!%Q,NR:`gJF(C[ʾCZIDTK-UHe2&̃wX frΖ8p8p Z*NړpC3ڪmNGرۜݰļlp6 6'ī+fK9+SNyc}83_z˷yп֫u/>-Nӛ>9)O>*tZ{T*y ނ)4Y<q[NF'˅g˺ۅGNE-KxXx1yT_S\w#f1 4&p^$?3 q:Uy B* rpT/Bjd:-,yHu>Vg~6}/=}u;d&rrHW7Mӱv`Zu'/hnc-]ދf ۫.n=wƗJ5)g?ſݟZCn3Ʉ#"/)b˘Hh ^zcfaE"8ØOȅf^CcMb&wjSəAM~ÏStؽOMɯZ^ec6Л:yAm+L98~ ʺً&qv7HZ91ۑvnS_ S@@HR-ǃ) -fچ~mCG$bNıFK*r(`W"`,hM+vL~eovz"g]"輳F:c*XT2l`@J R*HwPR4U`#҈!p!Rq\4eq15xW\8uJvMGygt W;\z/<1L?>w 8sl-|EZsEWK9=Dwcż 93%g9}%c=Q 걦TYУlY .[OVxX2"𤉦/P=EQ04+`'p s/לYىoMbg'c@{˵pVg( c.N]u75ן^a!`0B59KCj=[!%R& pΏ 1|U0H !DE:k Bbz?-kSW הVqNZc.}!,PռBQf{MTc6\<o ZRJ'E\p|F&$kQi<x>Zҋ5q|v<Ɔa\b)#8>1Ccΰ3i}B[EA~f䐊Ly oN52QB M%Z%JFE__i'K?=8N IɅYh 'uszdfkA ~\3 =Gς@I&ii, :JkNCX<,#2MK&ZPpG@.s FSKXi{OIv6r\С/䑣_ah19pZaWrWͷ 6lAB! ]d$+ 8_Mr`CJT_tԘYm蹏tlJw) t{h‡*%{| .~H|r UBW%B]E)T]E~9=/~bgyK +6\S>w(pz[Pvlݲ01.Ҩ YIyuj[1=YN"Nh}$+ΐ參"dJ]A$;4DTȟx!jy0 2v:QdXsH_ou+Pa$ fS[cpO>YǡFřb2z_6"m&ey]:m`,2b{VAlFS-D-P'+vIfͷoov^N:~EE鵸6qBVJ ?T*$E@ZR*J5Zc>y)L<*\ENBL9LBp!D> !kJPi&U*9HW=&U ![Ri]eHj f?qɻv{'MYTF JȐs/to"eN2cHtSi .Q{,=%)"c2Dcnx2&s@U]Hm]hi^d j;Ә9FBkC&P*ڭZW*ɠJz^b}A!!$]kiLF;XIKe$WZ :QZ .ڭϽ/:'3WqMEy@:A +#kCqy>T+v+TjB :x Sf"ֆ/FEPzK;*ΎlIDAT|Olz %{OʱWT\Gɀl|բsC!/P Àx^/!!E|ŤFȰp`?/KGT!#"n[7:a$A[F_s;BJT>J"cJ>, A*T/:._P}$ai۸)YRtw/GwEM٬SsV}7[X2'k;,(%HU+Kot4'~X sCޏ(Rv/ϕ?'7ᤐDVQqgvͫUMJ*`;o8Rà1EqE;{ mpe9*g@W$rdnz582޺&QUeFC^ YŖ#{8U]ei8ⴁNQd+}`&=H\ 2)w[~&T`u#gvf;"3@W;'ϠSTUh@RRZ{ḽ4-V(+]3a1<4A[OgV݀AtlYKT-$.<ڨE_6b g nK*B 4W0G&زXκYs,/,v XTh 9" 97^VPzL,?ehqqc]J(> yۧI3/IdL Q E2)p$/Hfư(\,מ7ͿcM-Q3w}JPB%Rı" yYsU;0 * [7' 8$n*M=,^[ D-̖1MhgYWn>էO{z )*.@a%U 1wԐW!'eU}(1Aݑ[0>?wATn=TH WFe/]^S{O-WU\JpdG>;;ȃ #ǚr8/9pH ]TTw.7(EYZg=M>$WU f',[ppqAhRnPAOMm' RLiaKu5Epg"9͋y"@(AGz;}`O$[B˷䓼 Ce'V܊KZV|q Lo7 eD*^] ::siBAoF˥L,QT(zm3pΰvci[(pJ`|k/<Ã0GJr`z{ Q{@Pt#G0wdC*U5qC`PBo*'HϿ]7>9bi (]QP,xDu! [SHBQRd=݄a͹].>P߭v$u! >]7|k? T!& %mFJao~;흚ɇ\J]6ZgZx<1ڄ8r-v w F6ǫ S1QsXƹ7ۺNpZsUE S{:d:oQzÛ|{[Uv;%*=J{%UbzYFx]J^PJ;d6Wnֶw+2/%JJX6%H6,5%eM1U?~kʿ8sG"'mhMv޷߱QZ7',;)B1TjMw^5ޥrC,#AKZTn۳_Z=N]JHy1kpkO; *Hka[QT*U{r(޶,o?wtpFJ^DȈw"huh_kfL[BFşo+/g?7)WOc1lO;j[|E2#<#tL@1+L޷w͛A>u.{@o%eY 6zؼ\ϕ%{l.ChЋ>9tD!?O+~7pܒRpزVW[5# 5 " P~w B[?? r9ʊ=!iliڳ=_RزXYz˟py?h]o驒vi劸Xwɕ KVygR82t!+fw jdװ*V nYI{ )Bʰ˷'/:使T2φ,g]8>3pAhkw ij%g(IИ4<+00H2"p@EJ܋0e[EɇG~NG;Y?>6?#@cU*uA]ugBqƖH>T)2kK,J!ء*]2P_Ά"Wibr,)e{B`l؃ `qk3MSFuO}h~:i_}ο'd{zIFU! nH yۻcZ ︼'|?ę%P-wa={t԰JZkv_ŁU|,.|k8%W C[ڡP@=;W-M!ڙ2˖mON%O/IU TvX'^T@)$B)*/( BŁЈF0symn;@]rx$0r6 1$IHz,TtƓ_~G濟5(D>Wef]3Dp%઎ \ek ToXSKHy5 Bl]/F8dfe8O4p7[d{SbYcWF%oko d_-e$מ{'j -* >|@'1R%X[. .Wޕ'W.(+[Qٲ7mw.8҇W+eD Uw(/-!D۳ P] ~TBѥO}􇖬-~kw'AD@WHuc?c?c?c?c?ǏP1mIENDB`tiled-qt-0.9.1/examples/isometric_grass_and_water.tmx000066400000000000000000000037741217502731700230550ustar00rootroot00000000000000 eJx1lttywjAMROVgyqVtAoFC/v9L68xoh5PFPGhIYktrrVYyS0QszZ7Nvpvd0n7y24L1Q7MhrTSreN/le821HZ7lv9qYa6sdE0cYs/kX7PXYwtfaevYp7WDrd+SnHByjYr/npP1zZ4/elcuM71rjeckdc5KNHX75fMwc9s2uzb6AsYstJzwrv5/Tz89SLIZy8v203llV8xl7yMU+462/v81OqA114/UhrzUxRqwprnh6ZGzp2PNQfPqRu/X9hnMV8F/xLg1L42erDf2oaa2RI2qPtbgbhmw2H69nMUxx/gVccXdC3AW/o/HV60vW59Lhu8arDxmfGIPFUV1qbLVQEIs4PlOeHQxqVjmzr5mLYsmf+5Qj5yM1r3Ne4p1D5VcMh3qWZibLx2fYkBhPYOv81I9wbrGd45zFU7zrndpwDjkHXXfej9zHc3EG+D3AWcCZMJif7hTnVxr6i9edtoBDz8N7kxqbY6sN9gJnsnqIOqCme7Un76579sIV8dccHvHqZefH76BP9wjzkVapM2rL+5/8cR6QS9eh8p2AT12y5oO9+7yh5hzLZypnHX29/pzB9PE7bOg8Mza5KvGu4R7mp/89zqvr7x+TnxEn tiled-qt-0.9.1/examples/perspective_walls.png000066400000000000000000000071641217502731700213400ustar00rootroot00000000000000PNG  IHDR\rfsRGBbKGDC pHYs  tIMEnp IDATxQ8 EmUĚk5kb>:b &[uTEUfǕ6;,G1bkߏ@JǣFbYK!֋~m Dј)*~P'E~P'E~P'.C!Z"mq?7 S'֜鼘sF/9QMR]+T1Ƭ {} E@ws|"@?p @9("@?p  ?4,%"@$~ IB?~'$ OHW#߷KWSϾmj̷̷*O룦bq> P@s=v|r}X<h8Z4py%C9Oj `S2@>i -}R@;D@i)y/MuF q<"m;,~>L;c!p TXc|rmp@g%Jg8_û#@{RK$@H -Km@; @-@}H @9c8:D[&Zod F49  " PbI$@?ĚH @=E@:"!E ;B&@@9H "e!@iI$@^Hk"#{^mxKvϬ&[kU\Ϭ6|\37?)}1F]l"isX>?y47~m"h}0G%[9󠩰2Oڻ>-!ʫzz$/$}ǯ~! #?~ I@_Hȏ_BG~>W$/$}ǯ~! #?~ I@0M]oQγrߒ+NǾ*˂2cI- UB! @ _H~/$_$CB0 %ZMs8:o= /$~@$ B! @ _H~/$_ I@$ B! @oC{U^m8*l1 I8s '}U۩ Ba;_jtwx]ט82*qi}w`L~BOm! '~_H - xrS$[23!k0'}a[!{O=U_b⦮g}`q"˲{}0 !.ڏzd&i0 J )u (z N@`G  1Fs!|.-͓ iw~`xx%^E~GO`1dlЙ8X2ηwre1_='*i ð@xmL@o/z @x @ 60sX@m{cX% ðj> 4V[ pCP{lx総 HqmR{n=\_Z[?{*z5xnOW' ^F뺮Mx|PKV@؛)6S0!x 6*L @(;y  @ @5ċ=@C~   ]v *Wh/w7rDO6Ub xvyx6r1-  j\e@C@[\+k;鷡|^ {Q~=r,,g]8akc0Vz垀a'DOn_r`@Ø n=2AN{s kz A@waaPn47S,i}!lCR_}ąTIENDB`tiled-qt-0.9.1/examples/perspective_walls.tmx000066400000000000000000000015121217502731700213530ustar00rootroot00000000000000 eJztlOkKgCAQhLfb7vd/24SUBqMMNevHfDB4LczusihCCCHkTzRay4XaDP7rxf1882ZpzBqTp/Wo4c7ulRy96LUKRx3EFYF5+GpESpA9V845pT/Oxijn+luIw36k8vf1Rpk1xPepx9vczV+O3GL6n4JJ9vkSOf8/Qwb/r+vHP8ZVzFwTQshf2AA5owLB eJztzgEJAAAIA7CD/Tsb4yJbgiUA8MO0AwAAhy0rhAAE eJztzjENAAAIA7Al+PeMgz0cPK2CJgBwN98BAACqBS0QAAQ= tiled-qt-0.9.1/examples/perspective_walls.tsx000066400000000000000000000007341217502731700213660ustar00rootroot00000000000000 tiled-qt-0.9.1/examples/sewer_automap/000077500000000000000000000000001217502731700177425ustar00rootroot00000000000000tiled-qt-0.9.1/examples/sewer_automap/rule_001.tmx000066400000000000000000000020651217502731700220260ustar00rootroot00000000000000 eJxjYMAPPHHQ+AAAJfgA3A== eJxjYMAPPKG0F5T2JqAeBAAmbADf eJxjYMAPeKG0DJRWIqAeBAAMkABM tiled-qt-0.9.1/examples/sewer_automap/rule_002.tmx000066400000000000000000000016451217502731700220320ustar00rootroot00000000000000 eJxjYCAMfLDQPnjkiTGPWLUA1AACYQ== eJxjYCAMvKG0FxLfC0neC00dIeBFgloAz9QCVQ== eJxjYCAMtND4yjjUSRFhFgwoEqkOADFsAIk= tiled-qt-0.9.1/examples/sewer_automap/rule_003.tmx000066400000000000000000000021451217502731700220270ustar00rootroot00000000000000 eJxjYCAMfLDQPnjkydVPDgAAZj8DRQ== eJxjYCAMPAmIYZPHphadpgYAAL88Abc= eJxjYCAMmLCI8SCxuQjoF0Lj8xFhJ7EAABfQADk= eJxjYCAesBKQ5yXBLGoBAAmIABM= tiled-qt-0.9.1/examples/sewer_automap/rule_004.tmx000066400000000000000000000020341217502731700220250ustar00rootroot00000000000000 eJxjYMAPfKAYnU0rOVwAAJk3BME= eJxjYMAPPKEYnU2pHLodhAAAb/8D/w== eJxjYMAPePHI8aHxeYiUIwcAABkoAEI= eJxjYMAPOND47ATU0woAAAgwABA= tiled-qt-0.9.1/examples/sewer_automap/rule_005.tmx000066400000000000000000000021251217502731700220270ustar00rootroot00000000000000 eJxjYCAP+BDgk6LHBwcbnxy19ZAKAMzFByE= eJxjYCAPeBLgD6QeYsyj1BwAd2cCSQ== eJxjYCAPiKHxRYjQw43G5yRCjzAaX5AIPfQAAE2oAGM= eJxjYBgcgAVKs5GghwdK81HZLcQAABQQACU= tiled-qt-0.9.1/examples/sewer_automap/rule_006.tmx000066400000000000000000000025431217502731700220340ustar00rootroot00000000000000 eJxjYMAPfKAYnU2JHC4AAAEbA5E= eJxjYKAMeKLRyOKeWNj4AABbzAG3 eJxjYCAfeBLgkwoAKZwAkw== eJxjYKAM8KLRlAAAB9wAGw== eJxjYCAMeNFoWgEACrQAGw== eJxjYCAM5KG0AhFqKQEAGSQAQA== tiled-qt-0.9.1/examples/sewer_automap/rule_007.tmx000066400000000000000000000023351217502731700220340ustar00rootroot00000000000000 eJxjYMAPfKAYnU1Ijp5mAgDb0wTB eJxjYKAMeKPRtNaHDQAAdfQBLQ== eJxjYMAPvKEYnc2AxsbGx2cmLj4++3ABAGBjA4U= eJxjYCAMPNFodHFcfELmEeITax4A5OQCSQ== eJxjYKAMSEJpaRL1aUJpbQrtBwEAMlQAiQ== tiled-qt-0.9.1/examples/sewer_automap/rule_008.tmx000066400000000000000000000023761217502731700220420ustar00rootroot00000000000000 eJxjYCAMfKAYGwAAD7AAmQ== eJxjYMAEPljEcAEADVAATQ== eJxjYCAMWICYF4ccAAHoABI= tiled-qt-0.9.1/examples/sewer_automap/rule_009.tmx000066400000000000000000000021541217502731700220350ustar00rootroot00000000000000 eJxjYMAEPlCMDQAAD6AAmQ== eJxjYMAESkCshUUcBAAH2ABN tiled-qt-0.9.1/examples/sewer_automap/rules.txt000066400000000000000000000107421217502731700216410ustar00rootroot00000000000000# rules.txt # Copyright 2011, Stefan Beller # # This file is part of the sewers example for automapping for Tiled. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see . # lines starting with # or // are comments # all other lines will be parsed and treated as filenames. # Feel free to comment out certain rules, to see if and how they are working. # So have fun experimenting with this bunch of rules :) # First apply only one tile rules to fill the regions # # We mark the place, where rules are defined, with the RuleRegion layer. # So whereever there are tiles at the ruleRegion layer, it will be used as a rule. # In the RuleSet layer we can define, which combination of tiles must occur # to trigger the specific rule. # The layers Rule_Ground, Rule_Over and Rule_Over2 define the output of the rules. # # Which tiles should I use in the regions layer? # Actually there can be any tiles, but it should be the same tiles for one rule. # So use whatever you want, maybe a colorful tile so you can see easily that these # tiles are used for defining the rule region. # # In the very first rulefile used for Automapping it might be useful to define # some map properties: # "DeleteTiles" = "true", "false" - This property determines if all tiles in the # whole region where Automapping takes place # are deleted. # That is useful, when not all layers are covered # at all places. (Check the Over and Over2 layer!) # "AutoMappingRadius" = 0,1,2... - This property determines which regions are # automatically remapped at live Automapping. # When you draw directly into the map, # not the whole map is remapped, but only a little # place around. Here you can specify how many # tiles at least should be remapped. ./rule_001.tmx # Setup the right transition tiles at the border of light blue stone tiles # # Basically it is the same as the first rule, we are just using bigger rules. # Whenever there is a light blue tile beside a grey tile, one of these rules # will apply. ./rule_002.tmx # Now put straight walls # # Note: Compare the regions and the set layer! # Not at all places where a rule is defined, there is a tile in the input_set layer. # This means that there can be any tiles except those used by the rule. # So there are no black tiles allowed, when there are no tiles in the input_set # layer. ./rule_003.tmx # Now we add inner corners for walls # # (No new features introduced) ./rule_004.tmx # Now we add the corners for walls # # (No new features introduced) ./rule_005.tmx # Correct the error we got in the two previous rulefiles (overwrite it once more) # # Here we can see the use of input_set and input_NotSet # With the input_NotSet we can define which tiles must not be used at certain places # Important sideeffect: the empty regions in ruleSet can be anything now, including # black tiles as used here! # # So when you use both input_Set and input_NotSet, you need to specify exactly what # is allowed at which places. ./rule_006.tmx # Setup the corner stones for light blue stone tiles # # Hey, there are multiple layers called input_NotSet! # Yes, when you need to specify multiple allowed or disallowed tiles per # position, you can use multiple layers of input_Set and input_NotSet ./rule_007.tmx # Setup objects now # The objects '1' will be placed. They will be placed based on the # Over layer, which was created by previous rules. # The placement of objects is done in south-east corners. ./rule_008.tmx # Setup another object. # This object is based on input from the Ground layer. # It will put objects to borderstones which are near an end of that borderline. # ./rule_009.tmx tiled-qt-0.9.1/examples/sewer_automap/rules_sewers.png000066400000000000000000000004721217502731700231750ustar00rootroot00000000000000PNG  IHDRH0<sRGBbKGD pHYs  tIME/#tEXtCommentCreated with GIMPWIDATh 0DёpInBq]¤ʐKAnKyS'$mg=yz= @d @ @`,ە1h->qB$&1ILb|$&1ILbHLb(N eeIENDB`tiled-qt-0.9.1/examples/sewer_automap/sewers.tmx000066400000000000000000000014271217502731700220100ustar00rootroot00000000000000 eJztlUEOwCAIBLH+/8/eDSBQ1jTNHuZiDKOGxSEigxDyCWYRtPsx6HBH74hyn2r9zT2T7uj+jPemW+sfrRai16K1ut1ebtEZ8+aFdxaU27qj5a7O1FOfRd1v86XlZc+T9RZVt3aG6FrX/0UIIVkWa5oFDA== tiled-qt-0.9.1/examples/sewer_tileset.png000066400000000000000000000533511217502731700204620ustar00rootroot00000000000000PNG  IHDRgbKGD pHYs  tIME  4@ IDATx[q&Ed99} 93$EYz)JbY$w5 7 0h iWKQpȕ4953"WLpN5:zzN*"3##ˠ˗/Yw7?< xn@sz>|<7= xn@܀|<7=G0~}—'aӾS7q_@0D9#<BcD0" a3u7vBNr ;`vu/u"{ n۾vQ}y)fH^0fٴ[ն,QDβ (%9S]OG7b+tgp_R}# !p+挈YUlm;GUBHJ&eSaXdgYE$  sȲ̲2k(f>}(Ff9 TQ%JITc3|{#D0s˲R޼xE$V\Žle)uwk7';͚~H>c菟8eu~X|_??|q'ox:8wF߀Vw9B6_aLyN= p>gaB ~pTWD͋NWðdV?S;_*1w", iRLY(9VE)ʒ$hFD.Uy\^qs 1y޻q{o<wHnD.eZ)5Y6#Jy+yUi5/H:\ev;;WbL5w㯫|{2ʼ2`Cpn֔|YMSe\>,˪m@zg(;oF;sbwXHy""F0텐3{cښ`3O0 > }:#7*l:1熤t`s*Y@ Xbc6M1dry>gٷ4|)B&RU9׹o}ƭ@7ndOϞsi_|0' S۶;Q.ϋmBpy|r>id|}c{o_\:d0IJ(,[5cnxCp}Ϫ2 mQd݁">0t]_U{CVQL}}),(QUm1<޽lv7f)jǁnնY0,~|:#'rY/*Dn;"#""sNU@L{v"2&Vfd0{Gnsps!_]cղl֋g:wy@<96syw>Gqbs"mW#vDn^gW.qu41R{ݢ(wnV[[4~ZL&[nl_Lc>Vs@S7fYi&"zq (ƺ(򔺦y@y<2MwyQc8|{(te1BpmwyKUM6^t:_,ny< b}>jޱij%! w6 |!.S1fn:u[߹|/-Nx8G/tC$݁,'ʈ/WdYQSۻ}߭VI>,K_TCYVxMU\Um9Gw\/ yM RJ¦2e>F/2^g6%s7lwǵdV!fjާ}EC ES1Uu fwq hsӔsL`Vbj0v Q8m+]wP 떪vq =i<`)s<>%$0St]}PMiH^!{3M*"c!"\MIј6|(fQ7\sXU] H!t3z<۷|(vm۵O0'{*Ӷ_|iϹelzpqw D`R"<~ksΠ !m)YCYȽwmb r9E@1v!87Dpq Hu;¹uV`fa6穪fuòh6͉19$*0|px'Di!=ZރOߜN>@a1D"6f޹"0tyd2޾}cL}dYp 5bqeYєTι$ceEӴ"q6v.Lj.h`C&]3xG9 cwEADž(8+i4E6:b`pV[7 G]0\׀lKaGyz#9[%aM#T 3\.hND16]wQ (fy=^tg:wy|,>|Bybr|8D0b݁T{չuFRU*eMTM`D9Um}Osf9NI7Xaw.yr߳`֟^gBdXz{8YBvJCLd1;f9@7]AJٹ504)ufN9wUTcm$3Qf"cZ1DѸ 0y5KQ{)8ib0,NBL)u]\?Pj)adS!3|r qvc<!q5/{aKe9my|rʬDv}I$@DfzULBw,-˒6m1 ̕LD]\rڲ D9?wy&2"L&`L2m;0O$"uΕTg 26mߋ;wluk@f(7;ŹuFLfJa&f9bNIDR'llt*/PY^Qs3q.;׹㛦_=wjǹR[Й]D5^YzO&lV}TI!W5hTMDLXU3&BY3 T"Cm.tMtJD5@]3x@=GѶysLC`2f}4Kf0M#"†j$:LUU["f]3xU#f "}٥4}A$XaJE6b EB`esY{s躣IsdFY$kMᓿ$YUͦ)-gݶ2![.HPJ2cv")<,_?_/<*ﺡm?ilX=z*gCui26c qbdH,bLL2mk,lm%H)Cv̺acYnL(f1)y}D\u6}|&*c>,tI2dټjәemȲN5ER\?*ER,U%%23f 7MMa"cv}f!@ckp{_{c\uЫ_]<;;xmk'\~?=MY:h RHȹE,+fv!xU2KDL8 Ȳ67JeFf!7aHu,2%1ZQFfK}DRF Oi1 fq1%Y@# #,OvXwcYWuuA8ʲ(2"Ld(ˌ4c{su]_yJɹB`UKIz]}YD_Z=E 5 261sgf*cy"r9G$D6m]瓵A۸HD6q܅Yw2f㟊 dD1q-7'a]}5f郶q4 їa<6բbL'3Qs{w'?_3bk_ GU(A14L,clo 7.p }RϜT$3O㳱':6Fh~?\^oz"ڿ[G?zk2!~- s9oxUˋ21[,x^:q,? 4aCpRR|_UrVNZ1mDb"h7uҩ'33;C֢׺oxa`o r{5313#fJdU`{fN)oZFd]!3#i-L1@zU!Z^ZMqRRUYJ`޻!`p"c:y)&C$0іcL5D4@T7.l]#H(}.i<@k]7¼)//,\\n\3O)&Qv{6M2 92Lj*D,˲< F7R* 2  Ar31QұgދyƺY= ;lL`֏ #vqg}0!*T))Li9Q8fL;+ ff^:`J0pșy6i pDIm]zk6hXS&"9wRdsZ`JTRL;"6qclp%KwP- ,x!:s89L혉r^̘3UzƦ˜؁64<~z2_Ώ vb'!2(U +P31;R3fj͈:1O:>^˘d6掎.WWc< s䅩rץfEAD؇L j 0B9bZ=%!< }0)sc &D6g 36NP:6p,D3֥NǼ,߀#^$bJDFDc2 Q3191Y{/XUydcspƄ-=NTs0i!l|I* D@LDS%ϺoxaԔwvg S3 wqHf .9}qspy0m3EATy׋10UޙI&0 Κg}>І˜h>YU SAP˱C  @3 q2I!8%*h0SfDs0^TkQh扼' V]^{l/8z=b_,G"ͩN>$5SI!&IkWzќݔi˱#Z5Itc֌P'M'b^^ZS^9˜3(1S"(N%ΛFscT1Ù\9ǪJo N+Baq ZE9$~ęŔ ΂cA,slfDw0^ `|s^Tc"s3 3:"xhC0vߋPl4V$0d;%!g(&3rϽHm "}0#Ib@gll 9cfysĹZjѱlhP]%Q03]^#/`#/%Cgd4d!s4 v*w "rcfn1LD$HjV9P1-" yfGdC7t76QqCyaY{ߊJ D옐g@C@|1cRæ8PR%fgfc5!@::3G&&j6Sk}xOIhgfF4>x lh0DR !$$-π(%Ĵ9LRySZ&QfDC f>"/0S8RUcڏwD @Z$9v إ4ံ“<95l>IFӟ"ǫV)&f>ΚSJk+yn_ (NWNi`f1!Hd=Lkg6DiUF7 }o2B&Y`"-&瓘ڻwl;uC(+`uR0ALg.k^^aMxGά"7lon:r}?$սXDRfdly 53km[Qe&%%s; D`&"jm>dSfȟeͧϨ׺94Jf_۶n\:Ãa[L>x$ 񅽽|n:ԝ9GGK aն0ԷoNASXm&1%;O$LAy<U|gct>< fY^嗾J 3)·_zj5۞ 0V eYwpLRtp/\-~nhӫ/̾k00E m=Xbf\)fEygbmzI&K; zm ;>0d|2Fޓ&>=ú<[^ItE+s'?^ws|[T+pMgbml-d^|o|ܒt\z޽{Ⱦτ3| ˂'9&by1 ! '`=a%ظt,ҢFNs?>&FY^뒇X,?fՏKҲ7߼qƥKN(T~Z=|9(/l' P$)*I(4AEyspLu]ּ.%bqi7nΧ(0cc8=ժ~zy,γ [5Kusi0 >|뭷~ڶ}6٬at38Uu兩*NUK FF?J1.y|c/|9WmwY>5 XlT.yR-]>dYUk6 GTc)%Uiŧp.s>Ÿ!0@0R03Y^O!dWiGÃM}v3Z/lp:0skήYT3z pL̼wkEk+) a|UU Sev4 7y|J,;N׭rzrԼ"*bf;҃cHFTec{w޻̃cʲ,.ʲ`b(yEyg'6W_bg?;s uQ,ݽ,F{ 5oz2/O~nd”wJ)iMŒw`v# o"ius ׯ_4PU~>EIW_Dʕ+V}vgVzـm Iay][u('h3#κ|y&ITo/eYey: 9vw.Of1e?_/|*0:w{fyaky*4zS5?7[7o8\eʃbb80@K|[zɄx׿ |L#~/۶~7ߜJQs=Xޏ/Y{pfm`823f23kx KB~"o~s\>涗7|Υ=&}ֈYDe9MB8ߍݻp-@7jkBDcrϱ0ֺVHLc1J$/C ieY0W_}ã(WСwG~vIMI;'𧺏O-?ğe9qVKiyX_ꗖܷ8?~g2,2]huԵ]6M۶}w}}[o|82gojGLHNѽ\S'7I N<c [eYtz \PQva 8̶fGCWb-qޣ?i兕 "p" @sy pzi!C8dTLNgEͻ+rG>#80#0 qq+"] L C/2,/, ̶+0Ph؎"ˡ@᪊UcKNi /#8CL\3eY6:k$HCXZVΆ0 BY6i9]:GFp@;d1.ITr68(gǼPE}?"7݀U^xX2=rJfKN"]-c JՖĘ̳"J|n޼_yEe2g3}4Mc':=<D?ꃂtz!pxrcX>#f " )|oɲMqOvlmTl1jA{ҦA,/l\te15Fp+t8񟗧/0e3ͦR}Z,G*U`:9>j7Lڮ%<ПG;OD?c(b D[.7n}t6o߾똀Rzrlcb2K`lvDTuU 2m4M~a .0 UըtT%Ghh&([V-ZtG6#mI$ޱ@vTlzVձ0.PQ]5PH]76`IuVSޯ|7gSSN~srbE!~9Nk~o)"/{z`RU@ZPt6Xwl481z]UZj͹Z)2jY6VKw?sMӬV/]}rqV,K6t:þ%E S0t@WM08WI庚 ȉ\o7zVyajz0l' :V:212)Kiw: 1ĸ@0 nݺuG^r~ ;oիWGޕ+W޹qr:4MTnPbE LXbߧ| Y兵mwس!* o-wDhԗ=ZU1I{if>d%GWb#ʁ*w٩κUՂWPһ_DB؝;Ɖ)m7f(lڇy7}5N`J [qBapVƲM,˗^><) ".rUg"KHRJ; }1˦zfya h'61!G;59 y~rþ~Nc?ϲl$)1]GG[9tXE;Krob70;l;vY3{{~8bZ Q1wvG^$I$ə(1sSw1&ՔH"8=%3$¦:fcOFd!DN,@{:~~*Uݞ7 id1scġ BQߏ}]N9:`W}ww߯px!d~ګj:ۚ:ͮ0=%uS"CȅPˇ0 'z>y𡯪0VU㈤<-?-εү?; |q~ڵ_ۿۿ{^jy4VEQdYgl[U Y9βO__.h-t;_V)ţ53kl6cM8n_Z/}LRz, XJQ;'=QO xhZyӐeQMD·h2X$Ɲc?_~Z+|耙wMd/_?|gW/‹۷u]FJόm[UUiwgko}3+٤^V퍛7o<=f?W6e9&3KQD"iqP3|Q8vah9dL9s#"63Y"?Ej;G_~ /ϯ^|g]<!+_~]ׇ݋1-cf\1뺾}M</J9bu+/bu0!c䔀DNyX{<,&>~tʟ:wb/`!jP wS&ˀ |˰zZ.1r\.qem֫zڶ}"?_sUG)]15u\.?Z.KPM=q?,VFДstbrR'gmmN.ϽO}_ <7+po(S v\L&bqXU|6[m[]\R)%!{fn;5<ϧiN/Ms*cr"T3R#P5߬EleBUTͲofլHۣu~?>ϿOWnmUlrRV 5mjۈQgaAC繈[(ʲرU-y (x?+K۫ OsUU>WUWC?DJ&awٹu22 B3- @Q-hM1Gp(XW)h=p gBfCC+jyQN&}4?¤:+W+O߻ܱ nmm-ˮb1%ry&^-Iթ\ŭ[Cc=k ⸏)gNs4̇e{cuw=U}qǎ$pIHexC#a$f bx?%C@ü h`$386}9wٷa.WUWe.~gUo/2˲ZkLBeYVB`QB(B !A  EVXRB*uRŸJiP* `=܅* ŨBH)93LYfF#3imhbȵQF2)!eYwT{?fVZy^ ;p%JmJ)cZmbp\ <$uIA꤄?v;TK ]+ڦ7T9RRXD)dBIϣ Pˢ1bm98MjۖawMZQpaFHyNJS =M3O,$R֚SYum'RREQhcR%uA+줔Yf}&1}J9o vE[MAl:/ urŸB N:.}Ddy!гfbbŬw1F@As3q9罏1B:w]}! p)NP=MRQO`/N&bf3}eYh! |1HL$APr1H{Dn]63Z;?_1BxA5;QR@4b,2kh/etenxCC^b D#N扌sC- ;HuXNNS=m niN DUEMc$ cohK*ˈĄ!$4x爈B &e%@vc;C/s=Egm$3{ڶ۾}pduuR>:@9iAjQ8 (G 8!.6Z@R[[Dl)dd4aqmv~CĮ* :bm40Rضv{sgt~ 7:)H𑃞DØ *Fb}~ {`K-.󒙜M9Rd٤,u@;8 3{g{1B}Qr"ĝ;t=P:5\uR1:!R0i#6XE]x< ۨt ѝ[Rb/e2CR*Q;D1 0sR1@o{Zho$(9d&uƝLF(꤄?|t:ʉ(24xX2f7AN)o]쥂g>^iJq6 #N떤JL$0Fnc"+Nqߡ:-^t 1DuR>:4 H1D ,ӼTZ.u}5'8Rw.>~L,|ȑ8ˣq *oNS+<!щh{t"@jBv.ZB @eL&`k=Nל֑eɕ*2:uݤkjZ=B#b!2@Y;pBdpJYVLvxf8~%Ie|1JpW31QecqޑAb#[ۏnÛp?3H)$qrƊBTXU F#(%Ku}O1DB{ Z{7JN(K63J-w#1$EuLQ  XM*]ltzN>l(H!Fd"n|TDlQLKHP{)# %bZ!#)3-ʳ';V(ʡ}on2NʳUq' i̺.j=_sBfF3px P* @DԱAJ΁"% RD Hk wMeEKml8ܺ5m5:a`Z ry}߫@d ,dCۦ84D .kp/:ɫF{imOYಐh/t63ܼUuewy6@DHLL8ݙy6]5s#LTJܼ= ,RUV"|h\>r# o&z:PLf %'&=(Ijs۞\F0ٯn ɤ4`dՔw( $#h|1wcf A dq:DxVژ<3.k]oBN}Ԁ ZT1' znmR=~0T/D, }LIYz +ݼd|ljB x@2ZP! GwN;EQdyKvY:m>ŧ_/ݧ UMf, Ц.AVh۞dX/5F Rvoq݄FT/e\sdLpgO_Ͽث^IDATi"2y\d{< 2 y!xh<\%օVZckmܮ(T2K-pw:`vWl3bv`w`J0̧͈ݸ7CG! Qhm]HQJBιgyٳ 3677!PK%4y2CD)eY6M1Ʈ׷6/B^f*XAr2,m.W..pd7K`*#o*Ue)X%\ۂރČ^$>5:DxJ<3!x"JF}'4->0 1%\*b"b)T$R?"J,\3{s>8(j(Q(BhH,2efR0tn_Rd'Gݯ J.v'Gvˊ *Sd,!͞dBDιb"2B40NB FqhOޘ*Vjkk 1*>BHcV VeID12TP׃wN`eb0n'/U'594wάa !]Fm1D)EX2R \rT)W__=Dxʒhk+)=,;)RtR֚)+R=(aKż<w;yx|R_rn-wV{2A c&BTN 5ATp1y)6C: q?} z,yY.PGB)|t}'M*b`0~{ک~So= =>, wu *aZ)"q:Vwhw9y|R_#M`rHpS5tgOc/? yש/_n&MQSQҖO|=~~< nRTqQI./44ꟜHĮA#=2~`=jpn8_8N&x4׮5M@oM۶u6yt/|emm- D8ї孭dmy~,X{`|b_B1+n`8I~@7?^?0ٟ]z1RZ8y1I9!ݠ(<S0{}0bu}џ~}ec}p{/=}Im;SXs a"?)/۹#A38n쁓EٹjDֹ QupPx( > 7~w~[[ׯ^?)/"\zb.LTb6lQ:(?),;= JZmL'н2b}ng̬LFDV㚦e6 RB/ vu J i0td?cMb L^#_R.c3Å,  erG#徛ֽ6MxHuBEQOWo_yʭ[wNmc{NZ{d7! :?JX;M/ͮ|{dbtVV¹ܰ6H](F"͢ǣ LKi C% hm&}g)Lc*)b0W1vtK\̥_{}2_oV= nʈ]f] <:\oRf Q$bf)δzs!7ooJOd|g!RcZ|ⓟgH2^C7E; }ā)`o_sQ3EAp_L[}fF[#TïeYi{z='&dT6 ЙAw}h|Y)1 "ʙḁ2-C9&>$fӾ^ןi3b w]Xl ϬkiCD|!˜Xb ⭭ c2#ʪJ:޷m;.~G`aMm1̀U]5,/ ò@fe zYd` &-f,+}ޒ(TB JsD$ֹt!R @Bc%>@'ъ]4B:Acf%5dFhLE]11!Dl#EDl/D `}xmvu5dIy)˲ k]^bpJ)7n6:/|._B@ᓟY]gJ'+`@cLADcU8)& 97u{B:g΢GD<7kE<3++ob nx0>?Ȳ,<7gb$Άh  H1Fʪg -3&(Ȋ"BZTXJTϝ>sgSD"*mߛM߭UCkFyQɍ)[ ):kJ졇h̀g Kޖ߼2+{~2:kj37k7_Y)<sqXa9,@]{0>6XDT )<cׯ0T[/^>ٹay;^x=q/M+WdY6 \v,Qk\,~qTEn<@RK`0c^1ǣqy^=R^~0߹:K_nݴho޼)ܼz x!]q ;1]~0>f;Jꤖ~Q,[s*[o+SO=eL?NYK}y晕7oDȏ>z8,'&Їj7]F lmm}{{~?9?vq(V_gou]u×W_1;w͛ L&.I3z.q&ZiCjo`Gc~[lm;o@۶ =[]UcloBumW֭1xN'ι7H̭7hexry'N9>$΃ ![Ο?wwTr.X\8\~Od)8ۯ.~|WVV~2`P9綶+|ǩ*3_|j9&9wڵk׮S 333I3glm4tSlݽGY{ $ܢ(S%p`lj`ZI z/E-fG/ln93/*ɔ %㜙YXqq>\iie`M2"+Wc/?UUљ!|᩟RbjPZDN8m{=qa)g+V<{F/.#d[B)5TUe֚vw9/}K/RӪͭsgם67V>u[)SՃ}e] fpvf(666-.g;fvvyO\Jq~8b8\Ҿ(ze2x7.iXD.>?IxPt777_וj~~1˴snGu˗/?/"]}j2vƘ3DOVh.vS[?b$/T/_}G~(jmc? ;.\PEQ Ku=H_DC+jv uWZ "˥sj= xT-nlXc̀LwROZm:0cdveI!w0\!&K&3KlBpipյpavTJ3ַ̅y`ҿW߼ۿ߀p.7uk9aA׿ a&;6ڨ;w밴}@LUIENDB`tiled-qt-0.9.1/examples/sewers.tmx000066400000000000000000000020631217502731700171320ustar00rootroot00000000000000 eJzt19kKwjAQBdDim0sFqwguL3Vf/sP//ySnkIFhSGrSdEnxPhyQxqJ32kySPMuyJTkZC5KLa2dyNEo1Lsds3wkl/4f+7V/0/f+Y40Je5GpUn2+J5NiQCdmO/HlMyYzMI3JwLUJx7drIsSIFWUfk4FrY3GvGuHb8vr4jcmhNcnAtpIflmmar3VA5nqaWMUKeQ1d9dyht59iRPTmMPEdpua8Put/Frh88P5q84zFkv/NZP2TOlOaH7Hc+64fMmdL8cPVbV9+tcn5MzpTmR0gv12uDbQ4MNT8AIA73Uq3v3hqLe2ldTx4D21k1tf2ubw59Vk1tX+KbQ59VXfuSlMn9SJHV70tS5jqrYu8BAAAAAAAAAABd+wIHfQq1 eJzt1jsKgDAQQEELtVKvYuGvEDvvfya3MBeQQAzMwCPdsum2af5lL71AJv7xL7X+Y46WtzXayq7z2RGd0f2+V6a5bdRFfaZ5pQzRGE2lFwEAoArpDk7Veg+nOzjlHgYAAAAAIIcHvboDlQ== tiled-qt-0.9.1/examples/tmw_desert_spacing.png000066400000000000000000001117061217502731700214640ustar00rootroot00000000000000PNG  IHDR  sRGBbKGD pHYs  tIME8 RtEXtCommentCreated with The GIMPd%n IDATx{tՕ/ztW?zXd;Bp 冗3!q@eoM5_B ΚrɊ,;@DtO IBc[֫ϪSuUndu%9{*ʭʭ\.o3PA&> U0 $k Ó)y]bj?\͐UP% , ~y[6 $pȞy Pu*l*D:>6d̛: _1H7@<cld fO ;?dS{=]I~k=] W} VL=߼O@AOޟi`(@ 9Ԋt|0t>T2qx> EȤp C#1x! `*DO0C#1f%>@U3 9u‒1 2;[}Oq:J=X:ݽ 8*qw>=>Ȓl{z p}Mz 5itw#_ZPp0EOLay(D~T 55鱓SqrtTEq~_6:lz1lv*(m߼O޾rWk9MMGW: .8?z~!{>, FWR$⤳t_!~VLÇ P0!h5 -$0,tN1hא'Oޠh{AlJ %#d>rz#V^Eڏ>=L1=c[00ptˢd-4UayE"ì :0<|mu^YP?$1CO+P`7,O֛oE۟ў]=96,RLdQðeC+Q,{\ ر{no/Ϗ-( e}XjtjP(rbj- ~`^@0p,P5"!r*&G/PUe )ۀ^/Sd Hw㏕1.`pϔ^`kJ1YBL} ѿND38f u7]s1_/80N2Tͱs}9!h%é U0> ~Y-0;:C!(c0y7,m&u]8e15TF,o#Ƣ1̦uw]0ƽ㥒/ޏ^LAYs|/]6(l3A+,{ײ\ۚ]14Cѹ*dG}6#P74 E!Fq1ӇB0pۍv0@V 8 Lp H$EOEU}&KT cIC@IH$ /8(2}\jmxo>,UT} !&=} Ջ`lr'#E1u,j[;g=QP>-*L%Y,L:d? ߌF嬣,tuL %  =jg@HO zg8 8Rr jR&#gv mo{yWYiHɒ0f chlŽ&'غR&/F#}Um.4CmJA4Gd@D \ j'DjTTלZ>3k!3kg~kg@e=Щ1&{tWXfXDOwquc'x|/Qs1" C)A7zrK];v7sg.badRɱ l02]JP>0M8 Zb!Z-`Xn0V E1+.8v r̳b;^K ;b>5kY1B;,L$S1,od zʦn@QtsO89G"F0`;N8>ǖN:}tL ~BcI>Ce+}Lc{ާޤG ]-7Fi Ӆbd;.h~,1:3qQ߾bF F91\^ y1* (&B1  k)D0/@l [3Gib<9z"dN$N~y$'2=/NH(a3aݏ,v p,<4.+ A ˱$ojgu޿~pfcjdSkYC'iYe5 ꃎ ،Lj MUA U~c)ァPY{-[k)\o 0[6;x'Nt#IVLlM\ұxxv>$GGc9 }ywn}Eb`!d (Y(쾘1(J]!LlEuc­-g$}p` E&Bdڸ Ak#BeUEC\~ JֲK/ s$tPmP { +k0Y)jhkQ秳>;} k6jj,s F!ut 5>(@LsK?R&oAJ(w Ըʂ+JP>I Caax|e>1̥>ı=C\F"D%S Eΐxgܖօ䫭 ['z[[}ڛ'~>v >]B1kf7͚̰%}m|:[:ͻ| D\6nl"Kr7!(Aj$*݋51=}yZ2oc }ȇͪxnA:tP!EoSPFOw9+9и\u(SݿDGKh>Az7^m7+Y0  ;؍t|L2F101&mR=Yt70E$a.! C1Ѓa) ~ꄔ`BFl!N9h[,X8<4`0gO C1"ņDbAu[@Owz_C] s$^nVnz` xBUd0 E[uTzO#)Z13v7Q,.­- ~G۲m_X0J˞˶9iJSYzq:ºRA0cHG,@W9qHG빓Wa6{?X ;0Z.}--Wd!@Rgm Z?McI,m搚h&)9_o6 t}"p_>$ o4eHN 2x  YIQO,ʑE:>{KP>T/^#kYJ1$~zy9zz =]ޟ2`H'0q|E0bepw.zOY a>.rP5g!jaȠS rd)A1Ǔ}ۿ [({[/V@#+0<UcHNFu'zøD F'NGcdFbh[ ?uxPs$FhTN*C[C%c(Tf!4]pCA_Gl18y=VH0,}-6l4x_g>d鳈,iWY UY(*4D CcdE~ %Ƹ.rМaq P,-ԛ-k|?lLay(2I@jjc'!h5Oi6I+ 7ѝv"`psP%8cNR!ݽ}ITQTd C> (H!GKBP,z`]hh?(JhKz\냱H_A T$CG aTSd;WN>IFdan)H)(߃‹"%ɠ2tihгlF 4Yp C> 4jzR&ad2yP,zY0aI |-uA0̥>pq"k@Zchi V<ЬR[[à?|y2|D)E*_sې@G@B>:{HDC:> ^ZH,?yVDo4Lyp1`|'Iyc6^≟W [6R/C! x+_ K>x0̵>0v3*P,4Zy6 kz8x'> ]]E"x.mmu9'd~xxn!Jٿ7;J|,xY㕔@ T zjp-Z Υ^Ks?*2I>Rc3Z,RSH0 R$AѸuc'b[y°Ea(DZ)6tЧOP {ud0̗>xǡUU`@!Yȩ)!"6QʒD dI=0̇>pyqfE1 O r#q? CIglXA$é܄3fa\#>t}[6' U,N>H' {Y`(Y4 #Kw>2!g`(F3X09uksae! T1 0,<Ԭ%uXưjt I:s!tASnIcflпC?dJ_j "KH0wl VC8[u_,m %a(Y4 uiàgXvw߷00* vz~(sQz/ s௨Ft"AΦG:>ILARY(PiO:cfnwA؟n|3p`oCdr(LX=`<9o1;+-LÅYlm>C#1dϐ0_P=<4݊HOO!B;׃!PՔ# {BHRnc an-p- tBjsV1 !e|Ub }p`a.}΄a6@+' IDATMٌBl]e b*F*8оֲx D?n,f[}k̯|SY|+$°ӊl֜{lЊ-ZIn{$d12c(U0Xlƽ޾C1x]&X(I"eYl!ND3X|cm\)6KS{)#7Q50ٷ$!k)Sǟ+QXKrBӖR&Q2R!lNE`O2̓3ea2 b10 ˇ'u]8e Pa8O":%G+V+6:@EvT%Bad֍.z}JW`I3C~F  מzx]s!8,ok.I'9FeM'Σ>)D \7c>|-zʚOb\n3"NJ' ȗO$M\8ԪNd>臢Ȁ".g=pu]zq.)]Z,XVRjӧPx#gQ鈡}h켌aDZu#3z'?NN23a,AЯۼӏ 8"a -h[!^ԨuÕO\;A)އ[dvY;XM_AIǤ>(p@6pp;LG"^lem,__ݽ <YiHIxmhlUJ7 =1'$TXyzy1Fx}At'fb04InWT #(b&C>jU': Y)Mu+*yC͜:0m}}2 9t΁ ;o͛vzK4i8tjS~:W,3`Ȗ? ݢn?He^0bE9bhX]]F ˫b޴}Rݛ3 ,d P,QЄ+(f 0YmS?<`g@V"(VHivVLn|ynw x<豑J\b"SaGϦmȂa9- h;3b P/ӧYQ4_|7~}O ,d2n }Ө.kEpgq ]J$BNŌb'`6^'ݭV9EL.d;-濝Bu.ݫ%q cL E֔90z*3Y9tM1L_~4v\Jf<vy-٘`{-땓4[I@y1#uR}:'(ΖkXlz u-Kqh0()hЉa7kdz.Ruqx0+Y|mzk7o4 [64bWW5bI$G|- 'NtcQVLl\ұx8MM8䈸s4L<|f╷E__7kl0'y;ʡp`t_A3s$/&޳}/T bUHO Z+Y'0afP,LM8A;yICC: 6@U˄彆9KVv<';g2DGc<윉h "smݓ} F!ut 5>(@L\nAM$->Hɱe|p V2 0|W5 8+s,sjKL} ` xX/?+UOctRǐO3bYȱ_}8Ic8`x ˬ2Qd l$B3`8?|fφ|v9z N}EOP!fM&3,)?|& S~֞:0FDyGo2!ew۾0EcP ]\0ǖ'k,sJI.Wt,`k Xq(D'-{:ޯq"z@ [c{ q Oհ׹pRw]3bN9|ƒaOЗTU/PI{4R&AB2I^ y}3֋HUd 2<;P,Š%/AgI~l#PNm,tB17$g˙QdK, AD'5x nCGNOzKɃOb0}Em;wL"z$ T(pMc\ܼb*n J(RrV4E$(غ-eYwem!`(E'Y%4\Y|ơ NPN28 1GtK'\v?3)2jY 7;%x.6v LjjLbI[ TOx= >p}* v.蹗ꄔ;1ma.tRDWY|\ơX q(D'Fb6zIJFb#\Zϯp乼D'炰(?͓^ vO\tt  ^@PBVcSYofC:Y)g}M-M?tw5$9P0)`Β0̅Neqb12ꤵ|[}v( `BUF8?칸 9s6 L5 v#2$+ԶW756ՁZt7I&mo5 BuX$ Eb*Q!ƣ0* @fʊ)7i#ѾN"2VO:cbڇ{ RjNRPοE%EJA_~R$ KipDž>J\t ZN@)Yͭg5| HzY0aI {-Eg C:ѭXBOO T14auW+BhIpjŐ왥"LA}I/):0<|mu=I$OjC:> ^Z"J 61Yyp|'Ķc6^≟z~4Uc3F-_]rlXJD Ö tP,ouA0N2v3*P,4Zy6;D<1E2>6NzL _Gi7o;LrdsPh+%UCj R`Zµ}AK, Dq C) f-`9".lIeʂ<,z}DzD0-V k 8&a6:VUY$g! ‡pXE)K}0 SGUUFE>Xt`Ǎ'QІ%KBowWko_k e<]< ʓ(M'TBda0f@rHr;onXN1}|kJEJ0|`\ӭ>߲0!80, !TH\IaQ$f%<,2T3'Na`Q-0tnFA}8=p{b[1c<ͫ6,AVT@"\'jIxn%Sאߗ,Yt~t~E: |`]žEJON- DoQogsiJIHEB6qX"N[CttFW~ޭR/LZ%c`8?h:C'c4W#/TAfo DrWv>7 t~m>@"N rwؼb!@Q]1;>D3n:S јsF`0 R$maj_bI478fb0 `ɒ\N}Y^_l!d22!+*(BL] f5^s=m&M,6߹^!ha;lJfNx;ڥMHMTX1;[o`Cd!q`աg \$ RdL[ΥR'T֛c{iu=UuO,'Y쟷OJ{^}c=s&|_l35Z6AX:]SA!sW1X4.3-bnBeOW{.1TdиSph fVe1L]G :j.1 r,ALY%Xڗ4"8Zvz/usJRo KqťWk[ Kqڍ9&R10vàL$ Қ;"&RCRqz`ĺwf҆ieFDr)bfcڙ,TtknE> 1r zb09/|:hF8s$UT_StĮ[?yrbNNf7i}ts; a8XyO%c)f&1$aܓtNNq9l|΅й:bbr 2B' sPtũ R,,f`? In38 uUS@eBBx"7٥Qꂠk"Q%+8w792afSJo/:Ԣ16X&n,p6`3\qݼ Z) "K`y[, 0vBWvPpy!eݯJ&isǡVuBU( (""x[M~jƛr<9wfac|o0ڕ~Y%BrZ۪JE}EV3 1m0D3SBbjRj^G'jS cP2 >-c8=<ɋ:n\e}X8]QU+TCF±=P:0Eu9_Y C& tW,/ DW \qpkNAan9-p00vE8g\`00 *T [ dS)PE4)!ЙQhE,n lts]v|؜|e}X8q#!tcpXXdxrD}ESFe4x!"vWVCN)[U=W|=, < FD7d8|&%Kr}J$+0RXZf2xW]UiQ?i[//8~yֹ e}X8 C: 6@UD:'aRڶ˩QHTε[ %3p gX@8r(7`o >ep09.j2r(B,Oh;9k4y`@6 4OCr ?ua"oS/8`E8ݫ6Qև'^(2)(Y0 ~  ٢xXU Ts)ID_Axj< z:x8z9:y1pf>H2^bMU$rV4F!QINAL'!+(dlYEE9ge+c0K1 H`k|91̻'eYg!\\v?3)lvV[kYl_n^l4P|tQ,Wd0YҐ`TEZ 4e 37{>OǶ!-ۜ#_y>, q7 nSݿDG ʒ&v Jxqp\ lԈdFnAr$铨GF- DCR\A9 +ͩ wYⵎ5_mbz_FV*-BPLC!L#md NmEbsO慸k":;S%A0˭ʭܜv,6dP AjV%mUD5 YU( -e b $EkPբ*ڢ`7^qO`+P)B@TSo0?o IDAT!{-@UUQiH'݆BVXyS V"9=fXLU<cld$#o;. j,?z=Nؒ@`v0fje}X"!Z)b|d 6}@`xR ـxHFޡ9{PV fh$F H 9@JC,Ig.6Mݸ^.x<6e}X2XYQ9?"K$σM Y<3̈́E )z:37l Nˈa5܈T⢏L1{>s=" xPU##TGBUȒ`n#vc&+3-1詩LpHFi1o -9$WB[s/5xq!/68rwe}õ=j*j͵"k$4U˨hPTp؃u[_LIg(8! <  VGVLÇ ڃ>ݲ0˿BIR&a!xu^C~l =qՇ/×_}~ ƫ 5UAi{I @+R1 ĉ4xzj,ˍg!T<@ x  ԁsh3J!7{}H' Ay`Tl\qMkk.}o—_} Շzփ̈́e-5hPqv¾ ן_rN0Y a kѢHNf+>75v7[g@  9 15jb*nX=M N!CGa8jUVCUkׂq.L2k[h™:?X,Xy7vr"}\a oqn */2 ıS+ymY n\{_}_~~xwoaQc8?o{F,]p XKZa44,oEJL#DWBƏ8zXp o0,ұC8DpM_O :# Ys?e d Z2jj\'#﫸vܰ'ɛxf"fn\c,v`ǿ,YvLML` S{\10A:><_rC"t|t^>oHSE"["SL:[oHV`lΘg=.~n v>}q/7O{A2J:ٛN*eg~qbFSĺmv<>g;VՠzQ!X="W8s<cg>Bmb4ֵ"p抁!_BUI*׬$`BD2dӅ͚Sɨ$IALC .qR@ b ,VO<Pb載ohs#pC;qMo8~[˝{oʼnS+nD}ȏ/d3; %Λ&K/_pcg!JN?UU ?>LtY/se+j$=l:=bU%&KK$钌|`{r AO/\cҫgJ}Qٲ3{<>2i;=Xq瓈Im/P{+?8VZE|y|z ,mЉ4~µ*~+Ѿ {umȤxﭏH`֊K盠 N*+;BUx_R0` f{_܇nƂ!1猖FV.W˫kt Hb[ubb}q{(rUM졑"M&#aY?9.m"+8?.b123-`B~ú}ᚩN,oqt/I"~<:::0K_']jӞ B2Ǒ+Ap 1pl--8wjum`<*^~~?ĹYşKK(y)P.Ǽ00g ;Eܝ{\ OM;~EN%Մ5/?P<⁇ B]=yI@1$S1`ơA NBfjQW7㧀7qf=8~Sth5w6|{7ԧ` ( )-A0H3WwO}Im A#GqQd,gz|0X̤@V fi3W bxRbO܎NM|~mGY}Ggg֭[5zl>7Jl ( W' Aub( 5; }Um99, e(Ww;.UahVvPpjIZci~kT39\'=n<ˑXT[Ux~2k4z*d{}R&@=HIqU CT!և`?=AUA&"7(HR0/#URZv(2<+>Ç'{= 8s])6HآuA`xIeۮo7H>f1XG{cMv3Kn3)95)O%9a%3FNN"3F"`@@UU@{C!9G: r 15 )5 F}Njc;³L܌-㈎9v^mfMjd7\R ?lo\w5al9R;ܺ4D"#ÈVCU ݶ*]A,@8jU'Æא韡=pcZD/ND!TLU0f33# ʆ/+`åpjfTCɧa͌ܔQVLzv(Ֆm|[*e͆ 6z}8x`CYDfj;N;]]ݐe V1{֍N; YVy_f**jel6v\sځ`djMC! #J֔^2 GnT< A#8xq<ϙˁpЌ1<هbs|=qii.L0YM[T *fK+у3a=|odA/)a(E߽y\z o{wauO`WXS( C.#MMXMHknF{{/l6/_  t `юZh`fC~<9}ԩup9mQl+V@x,]MpÇNՍXFɦ͝p0::8f^4D(8Vt DͿ#i [f_Mn#CJ"!fߓ7GY1>á|Or vدDI˚ņW5;5BC%ty7R0kf/Zѐ^nб`* Spgm.V D[6 9%:E{W&p8v99n}Jpp  "` Rk+d@__/*+If ^*+}%ݻw),[Y Rʫ5,| g}aZq68,*R)I0{Eee%v%5R ]8' jH"%ho?;~oKA6{ύgXy{? ~ǚT!6NRNRu\dhjMA/ڊ`jM͘ZX Q*&s I p"<EHHJt\!D\%x_$Dp(*cZ{ÿ竂%=Nv|~88@3jj9.;ΝK?Ϗsx &.(ѣn|*5$… U VfYI|,܈FGG +Y`XzD!#W ֞: ?[X~)MUѕQCt(hp#u#:2$ ;! ݍsk0wn :;afǁ9px^-9V7g^ȿ99Cq)DpaaGwwΝk]/+6{Sqqjuc%G8=y4IZ}-ޯWOӟBe840rDIIs^R~ݥ-?hzAY_RO.a 8Ћh8#Tq.!K20t(LeG BJbi2RBkfxX0dn8s -(A$ؗ, *Q &[h%\~mţy>+2]vK q"ܺo߿2Q=SxF'Μc3pD0xNҧ\GcFA<@0BOQo_x&ttb"|eYu/꜕ +Qr`6 J a?}_ 6 llF$,r?!?5(ʧ'./Epz . ;7֬&֬_5dCq|OB$ ,%q%@<\!܂aZ19sPg:7E~FF2=w< vhݻ1eu`9#qypmU_k9L$I@69 cSgQ [ q98KNe08$ȇrʕ ީ m~ Wt! 8 Ŗ.zaV +[gX00Xi__?ADQLYB!4TJs"L䯵&5y]NgnWB: z 0\i_0j/'% 8h2Q$\c{[JPxNjhgۋ CCFq"1XԀDs #4xfͥha1d\ۇCt/x3_yxF"};z @үW On^TB_sH Be&IZ.FAXQ] NjI>YYc0X[pUtxu4Jua20XY/o60V\v2{85܅b_!]uuVsӯGK~ gᕝ)Q޷Y#⛷jA9v\qe }2Y HB) *@x](2dql6-t1)[~}ЫFT60 'U9(v0#L!ÚG_E-wJ5~my.2`;:~}ʾx-4G3эt74=Gɢ-T7oxk+UZ(L >\ڇD øuӟ6[UK7M:{ج`. O&CXb/0;jL IDAT8Gtt6% IBwq݅j~ cͦ3P+DOwlm. EЯ.{ʋ<W7]N;jz%s0㖻P-7k= k/GC.-QCa,ްU Q|qW?͈A*xB[rf`+vZN|a)$DOle'bͦ3(YdcSP2: z`M 7ҥ(W+wW'8E'48`ds Xa+%hA!DiC{)ӝ pFP0-ּuJ-41ГKЪgNn ^$=!>3M(KvYz ,c͚5*!2"U&ɦ{f)اk)YkFR完sb(Xv%wm{=j~ݥ 4PS<*Tҥh?x{5~ $}pڹ`1 zrlT e^߶V,ΡAHgIB|F ɌWv6co=/]]GCs?|nYr dtݡTwUmvc9RtлMzc t:0wn %^7\o7z֛F`8t0~ZPtF.r  D~ϟCZժai1I?چKVx,-\w-"f uނj#\1ߏhEP_w)/= _k ! SȂ^x%v$-ԯ/)r N͖Oѽ*~bXCJ/PArjqDDA"!&)/E ` yFZ(O-W}20X($vvM| IFhvk/ʅ(0<ƾ}`A-jkkrUWcœOаgYT|hB`1?ll'ŒK[x~'/];81YbKqݩx}A<6Î+IJаgI)T>?TlD=D'7[5rPxI E $Dv8p 5j1C?#f $jnK):DJ7Ju_h 1etxqA_`i N$i(Z6ķAqlJ*vbFHg4mh1mZ@N]]=܀ngF"ڟ*a=?W̊ſ|L`k67~>.Z]h<^|% qtFR ͅѾ` ?H X'Qb^j$U|?i"GɭJ}2[5 IxVy |kA. q,ް5Ɂξ.wz ]^wԔSB-FplF]]=***LZ ,NOR{F5zfD`0imVL@V0t`^#fsƇW/>*n4}JۇoRcoA1'K]x ^|!5۟/`HG;F⯯ߗ"ۋތآD,T?dsbb%C})9oiD[{hH%㕝͚ o؊Wv6c9trR⢜ -Zl* N> ,}mtoOF!]΍Rt#2audÊMeAor` VljZFdLOmǎ6z kr߽cœO`x{q݅hBv/O53B$/>G]#]3B&`݊$  9'_͂%Vn=6cgpɪC5φB1jM6_Q W#< tKKByyFE&2!1NR͈|,Bz:vO2U zVH30د ֺz444AeTT(UB]$zkj5} B.k6A 6*b6}o|PJh֠Χx|k?u 0OI֠As(,ްD.\#9$iA«J|-Q,TyIw뛱v>\N"vcm8+Lo`Sn1Ɂu扮|#D,9;}\4jQfB^Q$^י8fC[[+\.;xL6zqRK7j/ og8B\ 4~q s%skz`1럏ϧjR?^L5ۮmQtu~-=EA ,S!wxюh$ gU~ܘa*."0O0#>H vr :v"sz wOWH.Ŵ! 5XlANv.5ƍX| JFDA75*XU;,}*5{ȣM(NC$JՎK(\q;{G&F̻> 5!P+BX þ>2 Ck"ذ'kVm\TWœם11XIzOQ698P}2P&T ǍbS[>mG˟AXBF"tM&N_/>jn O=  N_$7aj4k`,Yhx~SsrT~ )/BI*H+h?"\emϦ)$Ɏ]ٸ.߼PҕYq`ƣavrԨ)j{ daRfl# ! @DWdO͌ԛ۱ \$]#Mg_ w HV~)d;y~x2Uxbkr`z8:o:wHYqzmh milG|u!ϟmrn9 K1/ړ^ho@v;#`3*jύȴ Nj!>R|ݥfq=NHA@P2NJAo(!FV1u=7ӳe?NJ[hҪI{_jDGg_ 9!K !Aw Gxl"Bq[UBgNc?R(:{bFlz-7IvC<C@ .?GqŕT'JܖOC5_XCSor7޼huv%"GԅvW\Hģ4ٟюO\hW@Ob2:#fo$BV C&9h9CCGdN +aXƺ2]6XVm> (~E žB K)ر$nA$ìS!IJjj( y- oxs{*>xvb4C% ۣuQz570< 8^; addT}CuZe%FqE%9Gð;)r8}6@*gD2ǶjgWb=OW_7޾her[Pl:Q3#ǛMT44$k@YٌF=˥19X1>#9<}{(z+ƅiiҘ=?./ZhjOXdxRb`d v(5._F5P.x0Sjq"9po|Z#5h6(?%H8a)A](e;Ϟ=ZAjY#2]cZ X s -[CcidaCKK3f--C n~p$$hF_/&&Z# >h[ 8nTi=1nLM"epvctKD@ڮϑ[ jfB|jUIl cz@_IoUa2CU]:ZHîo9h0PF( 5 `9~m1K~G >6k!?y`VOޝF84̣rF.lrxt7%|IDAT<b4̱n3L1 X L4lL%=8da0LzPa:0.Z1PhT)50`0X 9^XE3b?J62υrU |G(ͅ!u Vc _h4I$yA$$ѡ cI=%ufkȢG5HH΅(\ʯ)o򃗧B$`w(՝GUGݩLxNYdI=#GzQT4mm~5}7>H6;xdH z9(uȆz3ǔp`ГArTM3νre *S cΜsa!`5 #rxF (5:'H3nZϼ=J~1W_Y=pՄ`TkxՏqh*Rr$Pn MPٍg =v{\?!-)n3sa2VUҧ$IQc21 =h(wM| IFhvgBM:SN6K[:;;;p pa-vZ-ݚ4VP3gd\S9֭\5X2fvu kBco.p*\?5ع}% /)2*p%.%Ul5xaXU_Cuu5x:۬~HH J] ƩDž!c&lB5#*V`$2TVV"hղhM5ȁ%55E4Dcjj[9W^'ym1MzJrO^x|;$'Sr"DhHCފ_hn59 VA ]F?5d9ܾKP2D신rŋSK;gT@u)56Q5+$X{,@}7m3GKę6Qpi'X-8FӝOΌ 'SaJ+;5Vh8d.0N'!}]czR3;:<#Yty2n|!bع}Ity ux~S BJ\@tPK-@^a.@vo IYyc!gL4tQXd¼ax33oHdY$Isװ6dAS"9\7ϟgwI]Z7ycd8sS |me.0D@d5 Vw(D !B ,X%iE?Q9Pȫ " _Aa0lkadtte1"> IJRg*lG>0ҽFHHؿ\dTVR4:kR$a -@逜n.  @X$7`AOs3tPV'R5 YP"{hC:7aHJ UG%Wk*<@4,9D/J[ M zͳL0h&!rP? "# 3pz$IH$p8촴I=HRMeUaC[[ QT45#fcFF<ϰwPZZleجYݭ:4~Z 掏`8b '0aNOΓL6Yp-<<zz02F[[~v㬳D{{dە7\ggYpVaɒSkZrr\(+&W;`Ô)yo9DYY) ]]X^y/_Ǵ&nD@13FuCdS#M: cNA;hrChtm O02bn%څKV%/eIlC91vn߇KV%ޥkL1tG.z{e^ԘKO ֯t4u7%j`3HyS7B1:jCn,H3zXj2Jۉ'.NƳ> D І\10ЧEESC!T!ksa0/cq8vDCaMÌ1XZ$j=)ITDsЦi+&:%`'N(vDh4mxmʟR^@ qڱzjgxǕD Ќ .XYr9`%qP7|*3MpUʇ@ bkLtݱ΅~$$ݻ0 A ǍxBe*`}=xϸEГP mth05ׅPGB$6_N_jzRЛY5KSδ$xDy>jБBHKϟe͍ht|d0RJE<{q : i(D xO2HC$t!/;BU?h yoy^x今&O`Z|7%j-cyxn vjE2~/!9 LQgYu I1x49Q|KJ=GOh9t2BCGdN1n>U82rfofIT*`v.Rr$.pv;b0xAv⃷߆ãj|o3(xDx(^%D,YEy8xx!2(!,0I@ppmGJ\Q!ɈFAv)eps9\5zv8sYU -(A$ؗ,yanyے6divL㫀!۹x3 RF0緑F1oɘI[Ո#=pz=8: -HȊH$ȉl6JQ45ϣѼү,W{u  lL#iWT9)+fMC<ԯnAsCkT,La7lV7'ʙ0BCU,[\koAD \89ɇK1@J6_4!A,2>B4DhBBj{ݕ Oq 1f4J!71k)`D&Bp|=|u0KɮԱ .,bwy"z:/F9 =wCŘVg84{a8k1h{q|H< >IENDB`tiled-qt-0.9.1/src/000077500000000000000000000000001217502731700140405ustar00rootroot00000000000000tiled-qt-0.9.1/src/automappingconverter/000077500000000000000000000000001217502731700203145ustar00rootroot00000000000000tiled-qt-0.9.1/src/automappingconverter/automappingconverter.pro000066400000000000000000000021271217502731700253140ustar00rootroot00000000000000include(../../tiled.pri) include(../libtiled/libtiled.pri) win32 { DESTDIR = ../.. } else { DESTDIR = ../../bin } macx { QMAKE_LIBDIR += $$OUT_PWD/../../bin/Tiled.app/Contents/Frameworks } else:win32 { LIBS += -L$$OUT_PWD/../../lib } else { QMAKE_LIBDIR = $$OUT_PWD/../../lib $$QMAKE_LIBDIR } # Make sure the executable can find libtiled !win32:!macx:contains(RPATH, yes) { QMAKE_RPATHDIR += \$\$ORIGIN/../lib # It is not possible to use ORIGIN in QMAKE_RPATHDIR, so a bit manually QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$$join(QMAKE_RPATHDIR, ":")\' QMAKE_RPATHDIR = } greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets } TARGET = automappingconverter TEMPLATE = app target.path = $${PREFIX}/bin INSTALLS += target SOURCES += main.cpp \ converterdatamodel.cpp \ convertercontrol.cpp \ converterwindow.cpp HEADERS += \ converterdatamodel.h \ convertercontrol.h \ converterwindow.h FORMS += \ converterwindow.ui manpage.path = $${PREFIX}/share/man/man1/ manpage.files += ../../docs/automappingconverter.1 INSTALLS += manpage tiled-qt-0.9.1/src/automappingconverter/convertercontrol.cpp000066400000000000000000000070721217502731700244360ustar00rootroot00000000000000/* * convertercontrol.cpp * Copyright 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "convertercontrol.h" #include "map.h" #include "layer.h" #include "tileset.h" #include "mapreader.h" #include "mapwriter.h" #include #include using namespace Tiled; ConverterControl::ConverterControl() { } QString ConverterControl::automappingRuleFileVersion(const QString &fileName) { Tiled::MapReader reader; Tiled::Map *map = reader.readMap(fileName); if (!map) return versionNotAMap(); // version 1 check bool hasonlyruleprefix = true; foreach (Tiled::Layer *layer, map->layers()) { if (!layer->name().startsWith("rule", Qt::CaseInsensitive)) hasonlyruleprefix = false; } if (hasonlyruleprefix) return version1(); // version 2 check bool hasrule = false; bool hasoutput = false; bool hasregion = false; bool allused = true; foreach (Tiled::Layer *layer, map->layers()) { bool isunused = true; if (layer->name().startsWith("input", Qt::CaseInsensitive)) { hasrule = true; isunused = false; } if (layer->name().startsWith("output", Qt::CaseInsensitive)) { hasoutput = true; isunused = false; } if (layer->name().toLower() == "regions") { hasregion = true; isunused = false; } if (isunused) allused = false; } if (allused && hasoutput && hasregion && hasrule) return version2(); return versionUnknown(); } void ConverterControl::convertV1toV2(const QString &fileName) { Tiled::MapReader reader; Tiled::Map *map = reader.readMap(fileName); if (!map) { qWarning() << "Error at conversion of " << fileName << ":\n" << reader.errorString(); return; } foreach (Tiled::Layer *layer, map->layers()) { if (layer->name().startsWith("ruleset", Qt::CaseInsensitive)) { layer->setName("Input_set"); } else if (layer->name().startsWith("rulenotset", Qt::CaseInsensitive)) { layer->setName("InputNot_set"); } else if (layer->name().startsWith("ruleregions", Qt::CaseInsensitive)) { layer->setName("Regions"); } else if (layer->name().startsWith("rule", Qt::CaseInsensitive)) { const QString newname = layer->name().right(layer->name().length() - 4); layer->setName("Output" + newname); } else { qWarning() << QString("Warning at conversion of") << fileName << QString("unused layers found"); } } Tiled::MapWriter writer; writer.setLayerDataFormat(map->layerDataFormat()); writer.writeMap(map, fileName); qDeleteAll(map->tilesets()); delete map; } tiled-qt-0.9.1/src/automappingconverter/convertercontrol.h000066400000000000000000000026641217502731700241050ustar00rootroot00000000000000/* * convertercontrol.h * Copyright 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CONVERTERCONTROL_H #define CONVERTERCONTROL_H #include #include class ConverterControl { public: ConverterControl(); QString version1() const { return QObject::tr("v0.8 and before"); } QString version2() const { return QObject::tr("v0.9 and later"); } QString versionUnknown() const { return QObject::tr("unknown"); } QString versionNotAMap() const { return QObject::tr("not a map"); } QString automappingRuleFileVersion(const QString &fileName); void convertV1toV2(const QString &fileName); }; #endif // CONVERTERCONTROL_H tiled-qt-0.9.1/src/automappingconverter/converterdatamodel.cpp000066400000000000000000000063051217502731700247060ustar00rootroot00000000000000/* * converterdatamodel.cpp * Copyright 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "converterdatamodel.h" #include "convertercontrol.h" #include ConverterDataModel::ConverterDataModel(ConverterControl *control, QObject *parent) : QAbstractListModel(parent) { mControl = control; } int ConverterDataModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : mFileNames.count(); } int ConverterDataModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : 2; } QVariant ConverterDataModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int rowIndex = index.row(); const int columnIndex = index.column(); if (rowIndex < 0 || rowIndex > mFileNames.count()) return QVariant(); switch (role) { case Qt::DisplayRole: { const QString fileName = mFileNames.at(rowIndex); if (columnIndex == 0) return fileName; else if (columnIndex == 1) return mFileVersions[fileName]; else return QVariant(); } default: return QVariant(); } } QVariant ConverterDataModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { switch (section) { case 0: return tr("File"); break; case 1: return tr("Version"); break; } } return QAbstractListModel::headerData(section, orientation, role); } void ConverterDataModel::insertFileNames(const QStringList &fileNames) { const int row = mFileNames.size(); beginInsertRows(QModelIndex(), row, row + fileNames.count() - 1); mFileNames.append(fileNames); foreach (const QString &fileName, fileNames) mFileVersions[fileName] = mControl->automappingRuleFileVersion(fileName); endInsertRows(); } void ConverterDataModel::updateVersions() { for (int i = 0; i < count(); ++i) { const QString fileName = mFileNames.at(i); const QString version = mFileVersions[fileName]; qWarning() << "processing" << fileName << "at version" << version; if (version == mControl->version1()) { mControl->convertV1toV2(fileName); mFileVersions[fileName] = mControl->version2(); } } emit dataChanged(index(0), index(count())); } tiled-qt-0.9.1/src/automappingconverter/converterdatamodel.h000066400000000000000000000037741217502731700243620ustar00rootroot00000000000000/* * converterdatamodel.h * Copyright 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CONVERTERDATAMODEL_H #define CONVERTERDATAMODEL_H #include #include #include #include #include class ConverterControl; class ConverterDataModel : public QAbstractListModel { Q_OBJECT public: ConverterDataModel(ConverterControl *control, QObject *parent = 0); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void insertFileNames(const QStringList &fileNames); int count() const { return mFileNames.count(); } QString fileName(int i) const { return mFileNames.at(i); } QString versionOfFile(const QString &fileName) const { return mFileVersions[fileName]; } public slots: void updateVersions(); private: ConverterControl *mControl; QList mFileNames; QMap mFileVersions; }; #endif // CONVERTERDATAMODEL_H tiled-qt-0.9.1/src/automappingconverter/converterwindow.cpp000066400000000000000000000046501217502731700242640ustar00rootroot00000000000000/* * converterwindow.cpp * Copyright 2011, 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "converterwindow.h" #include "ui_converterwindow.h" #include #include ConverterWindow::ConverterWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { mControl = new ConverterControl; mDataModel = new ConverterDataModel(mControl, this); ui->setupUi(this); ui->saveButton->setText(tr("Save all as %1").arg(mControl->version2())); connect(ui->addbutton, SIGNAL(clicked()), this, SLOT(addRule())); connect(ui->saveButton, SIGNAL(clicked()), mDataModel, SLOT(updateVersions())); ui->treeView->setModel(mDataModel); QHeaderView *header = ui->treeView->header(); #if QT_VERSION >= 0x050000 header->setSectionResizeMode(0, QHeaderView::Stretch); header->setSectionResizeMode(1, QHeaderView::ResizeToContents); #else header->setResizeMode(0, QHeaderView::Stretch); header->setResizeMode(1, QHeaderView::ResizeToContents); #endif } ConverterWindow::~ConverterWindow() { delete ui; delete mControl; } void ConverterWindow::addRule() { QString filter = tr("All Files (*)"); filter += QLatin1String(";;"); QString selectedFilter = tr("Tiled map files (*.tmx)"); filter += selectedFilter; QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open Map"), QString(), filter, &selectedFilter); if (fileNames.isEmpty()) return; mDataModel->insertFileNames(fileNames); ui->saveButton->setEnabled(true); } tiled-qt-0.9.1/src/automappingconverter/converterwindow.h000066400000000000000000000026461217502731700237340ustar00rootroot00000000000000/* * converterwindow.h * Copyright 2011, 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CONVERTERWINDOW_H #define CONVERTERWINDOW_H #include "converterdatamodel.h" #include "convertercontrol.h" #include #include #include namespace Ui { class MainWindow; } class ConverterWindow : public QMainWindow { Q_OBJECT public: explicit ConverterWindow(QWidget *parent = 0); ~ConverterWindow(); public slots: void addRule(); private: Ui::MainWindow *ui; QString getVersion(QString filename); ConverterDataModel *mDataModel; ConverterControl *mControl; }; #endif // CONVERTERWINDOW_H tiled-qt-0.9.1/src/automappingconverter/converterwindow.ui000066400000000000000000000037071217502731700241210ustar00rootroot00000000000000 MainWindow 0 0 576 264 Tiled Automapping Rule Files Converter false true false false Add new Automapping rules Qt::Horizontal 40 20 false Save all as v0.8.0 compatible tiled-qt-0.9.1/src/automappingconverter/main.cpp000066400000000000000000000020311217502731700217400ustar00rootroot00000000000000/* * main.cpp * Copyright 2011, Stefan Beller, stefanbeller@googlemail.com * * This file is part of the AutomappingConverter, which converts old rulemaps * of Tiled to work with the latest version of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "converterwindow.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); ConverterWindow w; w.show(); return a.exec(); } tiled-qt-0.9.1/src/libtiled/000077500000000000000000000000001217502731700156305ustar00rootroot00000000000000tiled-qt-0.9.1/src/libtiled/compression.cpp000066400000000000000000000111651217502731700207010ustar00rootroot00000000000000/* * compression.cpp * Copyright 2008, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "compression.h" #include #include #include using namespace Tiled; // TODO: Improve error reporting by showing these errors in the user interface static void logZlibError(int error) { switch (error) { case Z_MEM_ERROR: qDebug() << "Out of memory while (de)compressing data!"; break; case Z_VERSION_ERROR: qDebug() << "Incompatible zlib version!"; break; case Z_NEED_DICT: case Z_DATA_ERROR: qDebug() << "Incorrect zlib compressed data!"; break; default: qDebug() << "Unknown error while (de)compressing data!"; } } QByteArray Tiled::decompress(const QByteArray &data, int expectedSize) { QByteArray out; out.resize(expectedSize); z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.next_in = (Bytef *) data.data(); strm.avail_in = data.length(); strm.next_out = (Bytef *) out.data(); strm.avail_out = out.size(); int ret = inflateInit2(&strm, 15 + 32); if (ret != Z_OK) { logZlibError(ret); return QByteArray(); } do { ret = inflate(&strm, Z_SYNC_FLUSH); switch (ret) { case Z_NEED_DICT: case Z_STREAM_ERROR: ret = Z_DATA_ERROR; case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&strm); logZlibError(ret); return QByteArray(); } if (ret != Z_STREAM_END) { int oldSize = out.size(); out.resize(out.size() * 2); strm.next_out = (Bytef *)(out.data() + oldSize); strm.avail_out = oldSize; } } while (ret != Z_STREAM_END); if (strm.avail_in != 0) { logZlibError(Z_DATA_ERROR); return QByteArray(); } const int outLength = out.size() - strm.avail_out; inflateEnd(&strm); out.resize(outLength); return out; } QByteArray Tiled::compress(const QByteArray &data, CompressionMethod method) { QByteArray out; out.resize(1024); int err; z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.next_in = (Bytef *) data.data(); strm.avail_in = data.length(); strm.next_out = (Bytef *) out.data(); strm.avail_out = out.size(); const int windowBits = (method == Gzip) ? 15 + 16 : 15; err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); if (err != Z_OK) { logZlibError(err); return QByteArray(); } do { err = deflate(&strm, Z_FINISH); Q_ASSERT(err != Z_STREAM_ERROR); if (err == Z_OK) { // More output space needed int oldSize = out.size(); out.resize(out.size() * 2); strm.next_out = (Bytef *)(out.data() + oldSize); strm.avail_out = oldSize; } } while (err == Z_OK); if (err != Z_STREAM_END) { logZlibError(err); deflateEnd(&strm); return QByteArray(); } const int outLength = out.size() - strm.avail_out; deflateEnd(&strm); out.resize(outLength); return out; } tiled-qt-0.9.1/src/libtiled/compression.h000066400000000000000000000052131217502731700203430ustar00rootroot00000000000000/* * compression.h * Copyright 2008, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef COMPRESSION_H #define COMPRESSION_H #include "tiled_global.h" class QByteArray; namespace Tiled { enum CompressionMethod { Gzip, Zlib }; /** * Decompresses either zlib or gzip compressed memory. Returns a null * QByteArray if decompressing failed. * * Needed because qUncompress does not support gzip compressed data. Also, * this method does not need the expected size to be prepended to the data, * but it can be passed as optional parameter. * * @param data the compressed data * @param expectedSize the expected size of the uncompressed data in bytes * @return the uncompressed data, or a null QByteArray if decompressing failed */ QByteArray TILEDSHARED_EXPORT decompress(const QByteArray &data, int expectedSize = 1024); /** * Compresses the give data in either gzip or zlib format. Returns a null * QByteArray if compression failed. * * Needed because qCompress does not support gzip compression. * * @param data the uncompressed data * @return the compressed data, or a null QByteArray if compression failed */ QByteArray TILEDSHARED_EXPORT compress(const QByteArray &data, CompressionMethod method = Zlib); } // namespace Tiled #endif // COMPRESSION_H tiled-qt-0.9.1/src/libtiled/gidmapper.cpp000066400000000000000000000104071217502731700203060ustar00rootroot00000000000000/* * gidmapper.cpp * Copyright 2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gidmapper.h" #include "tile.h" #include "tileset.h" #include "map.h" using namespace Tiled; // Bits on the far end of the 32-bit global tile ID are used for tile flags const int FlippedHorizontallyFlag = 0x80000000; const int FlippedVerticallyFlag = 0x40000000; const int FlippedAntiDiagonallyFlag = 0x20000000; GidMapper::GidMapper() { } GidMapper::GidMapper(const QList &tilesets) { unsigned firstGid = 1; foreach (Tileset *tileset, tilesets) { insert(firstGid, tileset); firstGid += tileset->tileCount(); } } Cell GidMapper::gidToCell(unsigned gid, bool &ok) const { Cell result; // Read out the flags result.flippedHorizontally = (gid & FlippedHorizontallyFlag); result.flippedVertically = (gid & FlippedVerticallyFlag); result.flippedAntiDiagonally = (gid & FlippedAntiDiagonallyFlag); // Clear the flags gid &= ~(FlippedHorizontallyFlag | FlippedVerticallyFlag | FlippedAntiDiagonallyFlag); if (gid == 0) { ok = true; } else if (isEmpty()) { ok = false; } else { // Find the tileset containing this tile QMap::const_iterator i = mFirstGidToTileset.upperBound(gid); --i; // Navigate one tileset back since upper bound finds the next int tileId = gid - i.key(); const Tileset *tileset = i.value(); if (tileset) { const int columnCount = mTilesetColumnCounts.value(tileset); if (columnCount > 0 && columnCount != tileset->columnCount()) { // Correct tile index for changes in image width const int row = tileId / columnCount; const int column = tileId % columnCount; tileId = row * tileset->columnCount() + column; } result.tile = tileset->tileAt(tileId); } else { result.tile = 0; } ok = true; } return result; } unsigned GidMapper::cellToGid(const Cell &cell) const { if (cell.isEmpty()) return 0; const Tileset *tileset = cell.tile->tileset(); // Find the first GID for the tileset QMap::const_iterator i = mFirstGidToTileset.begin(); QMap::const_iterator i_end = mFirstGidToTileset.end(); while (i != i_end && i.value() != tileset) ++i; if (i == i_end) // tileset not found return 0; unsigned gid = i.key() + cell.tile->id(); if (cell.flippedHorizontally) gid |= FlippedHorizontallyFlag; if (cell.flippedVertically) gid |= FlippedVerticallyFlag; if (cell.flippedAntiDiagonally) gid |= FlippedAntiDiagonallyFlag; return gid; } void GidMapper::setTilesetWidth(const Tileset *tileset, int width) { if (tileset->tileWidth() == 0) return; mTilesetColumnCounts.insert(tileset, tileset->columnCountForWidth(width)); } tiled-qt-0.9.1/src/libtiled/gidmapper.h000066400000000000000000000061371217502731700177600ustar00rootroot00000000000000/* * gidmapper.h * Copyright 2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TILED_GIDMAPPER_H #define TILED_GIDMAPPER_H #include "tilelayer.h" #include namespace Tiled { /** * A class that maps cells to global IDs (gids) and back. */ class TILEDSHARED_EXPORT GidMapper { public: /** * Default constructor. Use \l insert to initialize the gid mapper * incrementally. */ GidMapper(); /** * Constructor that initializes the gid mapper using the given \a tilesets. */ GidMapper(const QList &tilesets); /** * Insert the given \a tileset with \a firstGid as its first global ID. */ void insert(unsigned firstGid, Tileset *tileset) { mFirstGidToTileset.insert(firstGid, tileset); } /** * Clears the gid mapper, so that it can be reused. */ void clear() { mFirstGidToTileset.clear(); } /** * Returns true when no tilesets are known to this gid mapper. */ bool isEmpty() const { return mFirstGidToTileset.isEmpty(); } /** * Returns the cell data matched by the given \a gid. The \a ok parameter * indicates whether an error occurred. */ Cell gidToCell(unsigned gid, bool &ok) const; /** * Returns the global tile ID for the given \a cell. Returns 0 when the * cell is empty or when its tileset isn't known. */ unsigned cellToGid(const Cell &cell) const; /** * This sets the original tileset width. In case the image size has * changed, the tile indexes will be adjusted automatically when using * gidToCell(). */ void setTilesetWidth(const Tileset *tileset, int width); private: QMap mFirstGidToTileset; QMap mTilesetColumnCounts; }; } // namespace Tiled #endif // TILED_GIDMAPPER_H tiled-qt-0.9.1/src/libtiled/imagelayer.cpp000066400000000000000000000050601217502731700204540ustar00rootroot00000000000000/* * imagelayer.cpp * Copyright 2011, Gregory Nickonov * Copyright 2012, Alexander Kuhrt * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "imagelayer.h" #include "map.h" #include using namespace Tiled; ImageLayer::ImageLayer(const QString &name, int x, int y, int width, int height): Layer(ImageLayerType, name, x, y, width, height) { } ImageLayer::~ImageLayer() { } void ImageLayer::resetImage() { mImage = QPixmap(); mImageSource.clear(); } bool ImageLayer::loadFromImage(const QImage &image, const QString &fileName) { if (image.isNull()) return false; mImage = QPixmap::fromImage(image); if (mTransparentColor.isValid()) { const QImage mask = image.createMaskFromColor(mTransparentColor.rgb()); mImage.setMask(QBitmap::fromImage(mask)); } mImageSource = fileName; return true; } bool ImageLayer::isEmpty() const { return mImage.isNull(); } Layer *ImageLayer::clone() const { return initializeClone(new ImageLayer(mName, mX, mY, mWidth, mHeight)); } ImageLayer *ImageLayer::initializeClone(ImageLayer *clone) const { Layer::initializeClone(clone); clone->mImageSource = mImageSource; clone->mTransparentColor = mTransparentColor; clone->mImage = mImage; return clone; } tiled-qt-0.9.1/src/libtiled/imagelayer.h000066400000000000000000000100351217502731700201170ustar00rootroot00000000000000/* * imagelayer.h * Copyright 2011, Gregory Nickonov * Copyright 2012, Alexander Kuhrt * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef IMAGELAYER_H #define IMAGELAYER_H #include "tiled_global.h" #include "layer.h" #include "tileset.h" #include #include class QImage; namespace Tiled { /** * An image on a map. */ class TILEDSHARED_EXPORT ImageLayer : public Layer { public: /** * Constructor. */ ImageLayer(const QString &name, int x, int y, int width, int height); /** * Destructor. */ ~ImageLayer(); QSet usedTilesets() const { return QSet(); } bool referencesTileset(const Tileset *) const { return false; } void replaceReferencesToTileset(Tileset *, Tileset *) {} void offset(const QPoint &/*offset*/, const QRect &/*bounds*/, bool /*wrapX*/, bool /*wrapY*/) {} bool canMergeWith(Layer *) const { return false; } Layer *mergedWith(Layer *) const { return 0; } /** * Returns the transparent color, or an invalid color if no transparent * color is used. */ QColor transparentColor() const { return mTransparentColor; } /** * Sets the transparent color. Pixels with this color will be masked out * when loadFromImage() is called. */ void setTransparentColor(const QColor &c) { mTransparentColor = c; } /** * Sets image source file name */ void setSource(const QString &source) { mImageSource = source; } /** * Returns the file name of the layer image. */ const QString &imageSource() const { return mImageSource; } /** * Returns the image of this layer. */ const QPixmap &image() const { return mImage; } /** * Sets the image of this layer. */ void setImage(const QPixmap &image) { mImage = image; } /** * Resets layer image. */ void resetImage(); /** * Load this layer from the given \a image. This will replace * existing layer image. * * @param image the image to load the layer from * @param fileName the file name of the image, which will be remembered * as the image source of this layer. * @return true if loading was successful, otherwise * returns false */ bool loadFromImage(const QImage &image, const QString &fileName); /** * Returns true if no image source has been set. */ bool isEmpty() const; Layer *clone() const; virtual ImageLayer *asImageLayer() { return this; } protected: ImageLayer *initializeClone(ImageLayer *clone) const; private: QString mImageSource; QColor mTransparentColor; QPixmap mImage; }; } // namespace Tiled #endif // IMAGELAYER_H tiled-qt-0.9.1/src/libtiled/isometricrenderer.cpp000066400000000000000000000471471217502731700220760ustar00rootroot00000000000000/* * isometricrenderer.cpp * Copyright 2009-2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "isometricrenderer.h" #include "map.h" #include "mapobject.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include using namespace Tiled; QSize IsometricRenderer::mapSize() const { // Map width and height contribute equally in both directions const int side = map()->height() + map()->width(); return QSize(side * map()->tileWidth() / 2, side * map()->tileHeight() / 2); } QRect IsometricRenderer::boundingRect(const QRect &rect) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); const int originX = map()->height() * tileWidth / 2; const QPoint pos((rect.x() - (rect.y() + rect.height())) * tileWidth / 2 + originX, (rect.x() + rect.y()) * tileHeight / 2); const int side = rect.height() + rect.width(); const QSize size(side * tileWidth / 2, side * tileHeight / 2); return QRect(pos, size); } QRectF IsometricRenderer::boundingRect(const MapObject *object) const { const int nameHeight = object->name().isEmpty() ? 0 : 15; if (object->tile()) { const QPointF bottomCenter = tileToPixelCoords(object->position()); const QPixmap &img = object->tile()->image(); return QRectF(bottomCenter.x() - img.width() / 2, bottomCenter.y() - img.height(), img.width(), img.height()).adjusted(-1, -1 - nameHeight, 1, 1); } else if (!object->polygon().isEmpty()) { const QPointF &pos = object->position(); const QPolygonF polygon = object->polygon().translated(pos); const QPolygonF screenPolygon = tileToPixelCoords(polygon); return screenPolygon.boundingRect().adjusted(-2, -2 - nameHeight, 3, 3); } else { // Take the bounding rect of the projected object, and then add a few // pixels on all sides to correct for the line width. const QRectF base = tileRectToPolygon(object->bounds()).boundingRect(); return base.adjusted(-2, -3 - nameHeight, 2, 2); } } QPainterPath IsometricRenderer::shape(const MapObject *object) const { QPainterPath path; if (object->tile()) { path.addRect(boundingRect(object)); } else { switch (object->shape()) { case MapObject::Ellipse: case MapObject::Rectangle: path.addPolygon(tileRectToPolygon(object->bounds())); break; case MapObject::Polygon: case MapObject::Polyline: { const QPointF &pos = object->position(); const QPolygonF polygon = object->polygon().translated(pos); const QPolygonF screenPolygon = tileToPixelCoords(polygon); if (object->shape() == MapObject::Polygon) { path.addPolygon(screenPolygon); } else { for (int i = 1; i < screenPolygon.size(); ++i) { path.addPolygon(lineToPolygon(screenPolygon[i - 1], screenPolygon[i])); } path.setFillRule(Qt::WindingFill); } break; } } } return path; } void IsometricRenderer::drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); QRect r = rect.toAlignedRect(); r.adjust(-tileWidth / 2, -tileHeight / 2, tileWidth / 2, tileHeight / 2); const int startX = qMax(qreal(0), pixelToTileCoords(r.topLeft()).x()); const int startY = qMax(qreal(0), pixelToTileCoords(r.topRight()).y()); const int endX = qMin(qreal(map()->width()), pixelToTileCoords(r.bottomRight()).x()); const int endY = qMin(qreal(map()->height()), pixelToTileCoords(r.bottomLeft()).y()); gridColor.setAlpha(128); QPen gridPen(gridColor, 0); gridPen.setDashPattern(QVector() << 2 << 2); painter->setPen(gridPen); for (int y = startY; y <= endY; ++y) { const QPointF start = tileToPixelCoords(startX, y); const QPointF end = tileToPixelCoords(endX, y); painter->drawLine(start, end); } for (int x = startX; x <= endX; ++x) { const QPointF start = tileToPixelCoords(x, startY); const QPointF end = tileToPixelCoords(x, endY); painter->drawLine(start, end); } } void IsometricRenderer::drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); if (tileWidth <= 0 || tileHeight <= 1) return; QRect rect = exposed.toAlignedRect(); if (rect.isNull()) rect = boundingRect(layer->bounds()); QMargins drawMargins = layer->drawMargins(); drawMargins.setTop(drawMargins.top() - tileHeight); drawMargins.setRight(drawMargins.right() - tileWidth); rect.adjust(-drawMargins.right(), -drawMargins.bottom(), drawMargins.left(), drawMargins.top()); // Determine the tile and pixel coordinates to start at QPointF tilePos = pixelToTileCoords(rect.x(), rect.y()); QPoint rowItr = QPoint((int) std::floor(tilePos.x()), (int) std::floor(tilePos.y())); QPointF startPos = tileToPixelCoords(rowItr); startPos.rx() -= tileWidth / 2; startPos.ry() += tileHeight; // Compensate for the layer position rowItr -= QPoint(layer->x(), layer->y()); /* Determine in which half of the tile the top-left corner of the area we * need to draw is. If we're in the upper half, we need to start one row * up due to those tiles being visible as well. How we go up one row * depends on whether we're in the left or right half of the tile. */ const bool inUpperHalf = startPos.y() - rect.y() > tileHeight / 2; const bool inLeftHalf = rect.x() - startPos.x() < tileWidth / 2; if (inUpperHalf) { if (inLeftHalf) { --rowItr.rx(); startPos.rx() -= tileWidth / 2; } else { --rowItr.ry(); startPos.rx() += tileWidth / 2; } startPos.ry() -= tileHeight / 2; } // Determine whether the current row is shifted half a tile to the right bool shifted = inUpperHalf ^ inLeftHalf; QTransform baseTransform = painter->transform(); for (int y = startPos.y(); y - tileHeight < rect.bottom(); y += tileHeight / 2) { QPoint columnItr = rowItr; for (int x = startPos.x(); x < rect.right(); x += tileWidth) { if (layer->contains(columnItr)) { const Cell &cell = layer->cellAt(columnItr); if (!cell.isEmpty()) { const QPixmap &img = cell.tile->image(); const QPoint offset = cell.tile->tileset()->tileOffset(); qreal m11 = 1; // Horizontal scaling factor qreal m12 = 0; // Vertical shearing factor qreal m21 = 0; // Horizontal shearing factor qreal m22 = 1; // Vertical scaling factor qreal dx = offset.x() + x; qreal dy = offset.y() + y - img.height(); if (cell.flippedAntiDiagonally) { // Use shearing to swap the X/Y axis m11 = 0; m12 = 1; m21 = 1; m22 = 0; // Compensate for the swap of image dimensions dy += img.height() - img.width(); } if (cell.flippedHorizontally) { m11 = -m11; m21 = -m21; dx += cell.flippedAntiDiagonally ? img.height() : img.width(); } if (cell.flippedVertically) { m12 = -m12; m22 = -m22; dy += cell.flippedAntiDiagonally ? img.width() : img.height(); } const QTransform transform(m11, m12, m21, m22, dx, dy); painter->setTransform(transform * baseTransform); painter->drawPixmap(0, 0, img); } } // Advance to the next column ++columnItr.rx(); --columnItr.ry(); } // Advance to the next row if (!shifted) { ++rowItr.rx(); startPos.rx() += tileWidth / 2; shifted = true; } else { ++rowItr.ry(); startPos.rx() -= tileWidth / 2; shifted = false; } } painter->setTransform(baseTransform); } void IsometricRenderer::drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const { painter->setBrush(color); painter->setPen(Qt::NoPen); foreach (const QRect &r, region.rects()) { QPolygonF polygon = tileRectToPolygon(r); if (QRectF(polygon.boundingRect()).intersects(exposed)) painter->drawConvexPolygon(polygon); } } void IsometricRenderer::drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const { painter->save(); QPen pen(Qt::black, 0); if (object->tile()) { const QPixmap &img = object->tile()->image(); QPointF paintOrigin(-img.width() / 2, -img.height()); paintOrigin += tileToPixelCoords(object->position()).toPoint(); painter->drawPixmap(paintOrigin, img); const QFontMetrics fm = painter->fontMetrics(); QString name = fm.elidedText(object->name(), Qt::ElideRight, img.width() + 2); if (!name.isEmpty()) painter->drawText(QPoint(paintOrigin.x(), paintOrigin.y() - 5 + 1), name); if (testFlag(ShowTileObjectOutlines)) { pen.setStyle(Qt::SolidLine); painter->setPen(pen); painter->drawRect(QRectF(paintOrigin, img.size())); pen.setStyle(Qt::DotLine); pen.setColor(color); painter->setPen(pen); painter->drawRect(QRectF(paintOrigin, img.size())); } if (!name.isEmpty()) { painter->setPen(color); painter->drawText(QPoint(paintOrigin.x(), paintOrigin.y() - 5), name); } } else { QColor brushColor = color; brushColor.setAlpha(50); QBrush brush(brushColor); pen.setJoinStyle(Qt::RoundJoin); pen.setCapStyle(Qt::RoundCap); pen.setWidth(2); painter->setPen(pen); painter->setRenderHint(QPainter::Antialiasing); // TODO: Draw the object name // TODO: Do something sensible to make null-sized objects usable switch (object->shape()) { case MapObject::Ellipse: { QPointF topLeft(tileToPixelCoords(object->bounds().topLeft())); QPointF bottomLeft(tileToPixelCoords(object->bounds().bottomLeft())); QPointF topRight(tileToPixelCoords(object->bounds().topRight())); const qreal headerX = bottomLeft.x(); const qreal headerY = topLeft.y(); QRectF rect(bottomLeft, topRight); const QFontMetrics fm = painter->fontMetrics(); QString name = fm.elidedText(object->name(), Qt::ElideRight, rect.width() + 2); QPolygonF polygon = tileRectToPolygon(object->bounds()); float tw = map()->tileWidth(); float th = map()->tileHeight(); QPointF transformScale(1, 1); if (tw > th) transformScale = QPointF(1, th/tw); else transformScale = QPointF(tw/th, 1); QPointF l1 = polygon.at(1) - polygon.at(0); QPointF l2 = polygon.at(3) - polygon.at(0); QTransform trans; trans.scale(transformScale.x(), transformScale.y()); trans.rotate(45); QTransform iTrans = trans.inverted(); QPointF l1x = iTrans.map(l1); QPointF l2x = iTrans.map(l2); QSizeF ellipseSize(l1x.manhattanLength(), l2x.manhattanLength()); painter->save(); painter->setPen(pen); painter->translate(polygon.at(0)); painter->scale(transformScale.x(), transformScale.y()); painter->rotate(45); painter->drawEllipse(QRectF(QPointF(0, 0), ellipseSize)); painter->restore(); painter->setBrush(Qt::NoBrush); painter->drawPolygon(polygon); if (!name.isEmpty()) painter->drawText(QPoint(headerX, headerY - 5), name); pen.setColor(color); painter->setPen(pen); painter->setBrush(Qt::NoBrush); painter->translate(QPointF(0, -1)); painter->drawPolygon(polygon); painter->setBrush(brush); painter->save(); painter->translate(polygon.at(0)); painter->scale(transformScale.x(), transformScale.y()); painter->rotate(45); painter->drawEllipse(QRectF(QPointF(0, 0), ellipseSize)); painter->restore(); if (!name.isEmpty()) painter->drawText(QPoint(headerX, headerY - 5), name); break; } case MapObject::Rectangle: { QPointF topLeft(tileToPixelCoords(object->bounds().topLeft())); QPointF bottomLeft(tileToPixelCoords(object->bounds().bottomLeft())); QPointF topRight(tileToPixelCoords(object->bounds().topRight())); const qreal headerX = bottomLeft.x(); const qreal headerY = topLeft.y(); QRectF rect(bottomLeft, topRight); const QFontMetrics fm = painter->fontMetrics(); QString name = fm.elidedText(object->name(), Qt::ElideRight, rect.width() + 2); QPolygonF polygon = tileRectToPolygon(object->bounds()); painter->drawPolygon(polygon); if (!name.isEmpty()) painter->drawText(QPoint(headerX, headerY - 5 + 1), name); pen.setColor(color); painter->setPen(pen); painter->setBrush(brush); polygon.translate(0, -1); painter->drawPolygon(polygon); if (!name.isEmpty()) painter->drawText(QPoint(headerX, headerY - 5), name); break; } case MapObject::Polygon: { const QPointF &pos = object->position(); const QPolygonF polygon = object->polygon().translated(pos); QPolygonF screenPolygon = tileToPixelCoords(polygon); const QRectF polygonBoundingRect = screenPolygon.boundingRect(); const QFontMetrics fm = painter->fontMetrics(); QString name = fm.elidedText(object->name(), Qt::ElideRight, polygonBoundingRect.width() + 2); if (!name.isEmpty()) painter->drawText(QPoint(polygonBoundingRect.left(), polygonBoundingRect.top() - 5 + 1), name); painter->drawPolygon(screenPolygon); pen.setColor(color); painter->setPen(pen); painter->setBrush(brush); screenPolygon.translate(0, -1); painter->drawPolygon(screenPolygon); if (!name.isEmpty()) painter->drawText(QPoint(polygonBoundingRect.left(), polygonBoundingRect.top() - 5), name); break; } case MapObject::Polyline: { const QPointF &pos = object->position(); const QPolygonF polygon = object->polygon().translated(pos); QPolygonF screenPolygon = tileToPixelCoords(polygon); painter->drawPolyline(screenPolygon); pen.setColor(color); painter->setPen(pen); screenPolygon.translate(0, -1); painter->drawPolyline(screenPolygon); break; } } } painter->restore(); } QPointF IsometricRenderer::pixelToTileCoords(qreal x, qreal y) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); x -= map()->height() * tileWidth / 2; const qreal tileY = y / tileHeight; const qreal tileX = x / tileWidth; return QPointF(tileY + tileX, tileY - tileX); } QPointF IsometricRenderer::tileToPixelCoords(qreal x, qreal y) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); const int originX = map()->height() * tileWidth / 2; return QPointF((x - y) * tileWidth / 2 + originX, (x + y) * tileHeight / 2); } QPolygonF IsometricRenderer::tileRectToPolygon(const QRect &rect) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); const QPointF topRight = tileToPixelCoords(rect.topRight()); const QPointF bottomRight = tileToPixelCoords(rect.bottomRight()); const QPointF bottomLeft = tileToPixelCoords(rect.bottomLeft()); QPolygonF polygon; polygon << QPointF(tileToPixelCoords(rect.topLeft())); polygon << QPointF(topRight.x() + tileWidth / 2, topRight.y() + tileHeight / 2); polygon << QPointF(bottomRight.x(), bottomRight.y() + tileHeight); polygon << QPointF(bottomLeft.x() - tileWidth / 2, bottomLeft.y() + tileHeight / 2); return polygon; } QPolygonF IsometricRenderer::tileRectToPolygon(const QRectF &rect) const { QPolygonF polygon; polygon << QPointF(tileToPixelCoords(rect.topLeft())); polygon << QPointF(tileToPixelCoords(rect.topRight())); polygon << QPointF(tileToPixelCoords(rect.bottomRight())); polygon << QPointF(tileToPixelCoords(rect.bottomLeft())); return polygon; } tiled-qt-0.9.1/src/libtiled/isometricrenderer.h000066400000000000000000000057351217502731700215400ustar00rootroot00000000000000/* * isometricrenderer.h * Copyright 2009-2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ISOMETRICRENDERER_H #define ISOMETRICRENDERER_H #include "maprenderer.h" namespace Tiled { /** * An isometric map renderer. * * Isometric maps have diamond shaped tiles. This map renderer renders them in * such a way that the map will also be diamond shaped. The X axis points to * the bottom right while the Y axis points to the bottom left. */ class TILEDSHARED_EXPORT IsometricRenderer : public MapRenderer { public: IsometricRenderer(const Map *map) : MapRenderer(map) {} QSize mapSize() const; QRect boundingRect(const QRect &rect) const; QRectF boundingRect(const MapObject *object) const; QPainterPath shape(const MapObject *object) const; void drawGrid(QPainter *painter, const QRectF &rect, QColor grid) const; void drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed = QRectF()) const; void drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const; void drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const; using MapRenderer::pixelToTileCoords; QPointF pixelToTileCoords(qreal x, qreal y) const; using MapRenderer::tileToPixelCoords; QPointF tileToPixelCoords(qreal x, qreal y) const; private: QPolygonF tileRectToPolygon(const QRect &rect) const; QPolygonF tileRectToPolygon(const QRectF &rect) const; }; } // namespace Tiled #endif // ISOMETRICRENDERER_H tiled-qt-0.9.1/src/libtiled/layer.cpp000066400000000000000000000054741217502731700174620ustar00rootroot00000000000000/* * layer.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2009, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "layer.h" #include "imagelayer.h" #include "objectgroup.h" #include "tilelayer.h" using namespace Tiled; Layer::Layer(Type type, const QString &name, int x, int y, int width, int height) : mName(name), mType(type), mX(x), mY(y), mWidth(width), mHeight(height), mOpacity(1.0f), mVisible(true), mMap(0) { } void Layer::resize(const QSize &size, const QPoint & /* offset */) { mWidth = size.width(); mHeight = size.height(); } /** * A helper function for initializing the members of the given instance to * those of this layer. Used by subclasses when cloning. * * Layer name, position and size are not cloned, since they are assumed to have * already been passed to the constructor. Also, map ownership is not cloned, * since the clone is not added to the map. * * \return the initialized clone (the same instance that was passed in) * \sa clone() */ Layer *Layer::initializeClone(Layer *clone) const { clone->mOpacity = mOpacity; clone->mVisible = mVisible; clone->setProperties(properties()); return clone; } TileLayer *Layer::asTileLayer() { return isTileLayer() ? static_cast(this) : 0; } ObjectGroup *Layer::asObjectGroup() { return isObjectGroup() ? static_cast(this) : 0; } ImageLayer *Layer::asImageLayer() { return isImageLayer() ? static_cast(this) : 0; } tiled-qt-0.9.1/src/libtiled/layer.h000066400000000000000000000150331217502731700171170ustar00rootroot00000000000000/* * layer.h * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2009, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LAYER_H #define LAYER_H #include "object.h" #include #include #include #include #include namespace Tiled { class Map; class ImageLayer; class ObjectGroup; class TileLayer; class Tileset; /** * A map layer. */ class TILEDSHARED_EXPORT Layer : public Object { public: enum Type { TileLayerType = 0x01, ObjectGroupType = 0x02, ImageLayerType = 0x04 }; enum { AnyLayerType = 0xFF }; /** * Constructor. */ Layer(Type type, const QString &name, int x, int y, int width, int height); /** * Returns the type of this layer. */ Type type() const { return mType; } /** * Returns the name of this layer. */ const QString &name() const { return mName; } /** * Sets the name of this layer. */ void setName(const QString &name) { mName = name; } /** * Returns the opacity of this layer. */ float opacity() const { return mOpacity; } /** * Sets the opacity of this layer. */ void setOpacity(float opacity) { mOpacity = opacity; } /** * Returns the visibility of this layer. */ bool isVisible() const { return mVisible; } /** * Sets the visibility of this layer. */ void setVisible(bool visible) { mVisible = visible; } /** * Returns the map this layer is part of. */ Map *map() const { return mMap; } /** * Sets the map this layer is part of. Should only be called from the * Map class. */ void setMap(Map *map) { mMap = map; } /** * Returns the x position of this layer (in tiles). */ int x() const { return mX; } /** * Sets the x position of this layer (in tiles). */ void setX(int x) { mX = x; } /** * Returns the y position of this layer (in tiles). */ int y() const { return mY; } /** * Sets the y position of this layer (in tiles). */ void setY(int y) { mY = y; } /** * Returns the position of this layer (in tiles). */ QPoint position() const { return QPoint(mX, mY); } /** * Sets the position of this layer (in tiles). */ void setPosition(QPoint pos) { setPosition(pos.x(), pos.y()); } void setPosition(int x, int y) { mX = x; mY = y; } /** * Returns the width of this layer. */ int width() const { return mWidth; } /** * Returns the height of this layer. */ int height() const { return mHeight; } /** * Returns the size of this layer. */ QSize size() const { return QSize(mWidth, mHeight); } /** * Returns the bounds of this layer. */ QRect bounds() const { return QRect(mX, mY, mWidth, mHeight); } virtual bool isEmpty() const = 0; /** * Computes and returns the set of tilesets used by this layer. */ virtual QSet usedTilesets() const = 0; /** * Returns whether this layer is referencing the given tileset. */ virtual bool referencesTileset(const Tileset *tileset) const = 0; /** * Replaces all references to tiles from \a oldTileset with tiles from * \a newTileset. */ virtual void replaceReferencesToTileset(Tileset *oldTileset, Tileset *newTileset) = 0; /** * Resizes this layer to \a size, while shifting its contents by \a offset. * Note that the position of the layer remains unaffected. */ virtual void resize(const QSize &size, const QPoint &offset); /** * Offsets the layer by the given amount, and optionally wraps it around. */ virtual void offset(const QPoint &offset, const QRect &bounds, bool wrapX, bool wrapY) = 0; /** * Returns whether this layer can merge together with the \a other layer. */ virtual bool canMergeWith(Layer *other) const = 0; /** * Returns a newly allocated layer that is the result of merging this layer * with the \a other layer. Where relevant, the other layer is considered * to be on top of this one. * * Should only be called when canMergeWith returns true. */ virtual Layer *mergedWith(Layer *other) const = 0; /** * Returns a duplicate of this layer. The caller is responsible for the * ownership of this newly created layer. */ virtual Layer *clone() const = 0; // These functions allow checking whether this Layer is an instance of the // given subclass without relying on a dynamic_cast. bool isTileLayer() const { return mType == TileLayerType; } bool isObjectGroup() const { return mType == ObjectGroupType; } bool isImageLayer() const { return mType == ImageLayerType; } // These actually return this layer cast to one of its subclasses. TileLayer *asTileLayer(); ObjectGroup *asObjectGroup(); ImageLayer *asImageLayer(); protected: Layer *initializeClone(Layer *clone) const; QString mName; Type mType; int mX; int mY; int mWidth; int mHeight; float mOpacity; bool mVisible; Map *mMap; }; } // namespace Tiled #endif // LAYER_H tiled-qt-0.9.1/src/libtiled/libtiled.pri000066400000000000000000000000451217502731700201330ustar00rootroot00000000000000INCLUDEPATH += $$PWD LIBS *= -ltiled tiled-qt-0.9.1/src/libtiled/libtiled.pro000066400000000000000000000051101217502731700201370ustar00rootroot00000000000000include(../../tiled.pri) TEMPLATE = lib TARGET = tiled target.path = $${LIBDIR} INSTALLS += target macx { DESTDIR = ../../bin/Tiled.app/Contents/Frameworks QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/ } else { DESTDIR = ../../lib } DLLDESTDIR = ../.. win32 { # With Qt 4 it was enough to include zlib, since the symbols were available # in Qt. Qt 5 no longer exposes zlib symbols, so it needs to be linked. # Get the installer at: # # http://gnuwin32.sourceforge.net/packages/zlib.htm # greaterThan(QT_MAJOR_VERSION, 4) { # If there is an environment variable, take that isEmpty(ZLIB_PATH):ZLIB_PATH = "$$(ZLIB_PATH)" # When the variable is not set, check for common install locations isEmpty(ZLIB_PATH):exists("C:/Program Files (x86)/GnuWin32/include/zlib.h") { ZLIB_PATH = "C:/Program Files (x86)/GnuWin32" } isEmpty(ZLIB_PATH):exists("C:/Program Files/GnuWin32/include/zlib.h") { ZLIB_PATH = "C:/Program Files/GnuWin32" } isEmpty(ZLIB_PATH) { error("ZLIB_PATH not defined and could not be auto-detected") } INCLUDEPATH += $${ZLIB_PATH}/include win32-g++*:LIBS += $${ZLIB_PATH}/lib/libz.a win32-msvc*:LIBS += $${ZLIB_PATH}/lib/zlib.lib } else { INCLUDEPATH += ../zlib } } else { # On other platforms it is necessary to link to zlib explicitly LIBS += -lz } DEFINES += QT_NO_CAST_FROM_ASCII \ QT_NO_CAST_TO_ASCII DEFINES += TILED_LIBRARY contains(QT_CONFIG, reduce_exports): CONFIG += hide_symbols SOURCES += compression.cpp \ gidmapper.cpp \ imagelayer.cpp \ isometricrenderer.cpp \ layer.cpp \ map.cpp \ mapobject.cpp \ mapreader.cpp \ maprenderer.cpp \ mapwriter.cpp \ objectgroup.cpp \ orthogonalrenderer.cpp \ properties.cpp \ staggeredrenderer.cpp \ tile.cpp \ tilelayer.cpp \ tileset.cpp HEADERS += compression.h \ gidmapper.h \ imagelayer.h \ isometricrenderer.h \ layer.h \ map.h \ mapobject.h \ mapreader.h \ mapreaderinterface.h \ maprenderer.h \ mapwriter.h \ mapwriterinterface.h \ object.h \ objectgroup.h \ orthogonalrenderer.h \ properties.h \ staggeredrenderer.h \ terrain.h \ tile.h \ tiled_global.h \ tilelayer.h \ tileset.h contains(INSTALL_HEADERS, yes) { headers.files = $${HEADERS} headers.path = $${PREFIX}/include/tiled INSTALLS += headers } macx { contains(QT_CONFIG, ppc):CONFIG += x86 \ ppc } tiled-qt-0.9.1/src/libtiled/map.cpp000066400000000000000000000146551217502731700171240ustar00rootroot00000000000000/* * map.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2008, Roderic Morris * Copyright 2010, Andrew G. Crowell * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "map.h" #include "layer.h" #include "objectgroup.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" using namespace Tiled; Map::Map(Orientation orientation, int width, int height, int tileWidth, int tileHeight): mOrientation(orientation), mWidth(width), mHeight(height), mTileWidth(tileWidth), mTileHeight(tileHeight), mLayerDataFormat(Base64Zlib) { } Map::~Map() { qDeleteAll(mLayers); } static QMargins maxMargins(const QMargins &a, const QMargins &b) { return QMargins(qMax(a.left(), b.left()), qMax(a.top(), b.top()), qMax(a.right(), b.right()), qMax(a.bottom(), b.bottom())); } void Map::adjustDrawMargins(const QMargins &margins) { // The TileLayer includes the maximum tile size in its draw margins. So // we need to subtract the tile size of the map, since that part does not // contribute to additional margin. mDrawMargins = maxMargins(QMargins(margins.left(), margins.top() - mTileHeight, margins.right() - mTileWidth, margins.bottom()), mDrawMargins); } int Map::layerCount(Layer::Type type) const { int count = 0; foreach (Layer *layer, mLayers) if (layer->type() == type) count++; return count; } QList Map::layers(Layer::Type type) const { QList layers; foreach (Layer *layer, mLayers) if (layer->type() == type) layers.append(layer); return layers; } QList Map::objectGroups() const { QList layers; foreach (Layer *layer, mLayers) if (ObjectGroup *og = layer->asObjectGroup()) layers.append(og); return layers; } QList Map::tileLayers() const { QList layers; foreach (Layer *layer, mLayers) if (TileLayer *tl = layer->asTileLayer()) layers.append(tl); return layers; } void Map::addLayer(Layer *layer) { adoptLayer(layer); mLayers.append(layer); } int Map::indexOfLayer(const QString &layerName, unsigned layertypes) const { for (int index = 0; index < mLayers.size(); index++) if (layerAt(index)->name() == layerName && (layertypes & layerAt(index)->type())) return index; return -1; } void Map::insertLayer(int index, Layer *layer) { adoptLayer(layer); mLayers.insert(index, layer); } void Map::adoptLayer(Layer *layer) { layer->setMap(this); if (TileLayer *tileLayer = dynamic_cast(layer)) adjustDrawMargins(tileLayer->drawMargins()); } Layer *Map::takeLayerAt(int index) { Layer *layer = mLayers.takeAt(index); layer->setMap(0); return layer; } void Map::addTileset(Tileset *tileset) { mTilesets.append(tileset); } void Map::insertTileset(int index, Tileset *tileset) { mTilesets.insert(index, tileset); } int Map::indexOfTileset(Tileset *tileset) const { return mTilesets.indexOf(tileset); } void Map::removeTilesetAt(int index) { mTilesets.removeAt(index); } void Map::replaceTileset(Tileset *oldTileset, Tileset *newTileset) { const int index = mTilesets.indexOf(oldTileset); Q_ASSERT(index != -1); foreach (Layer *layer, mLayers) layer->replaceReferencesToTileset(oldTileset, newTileset); mTilesets.replace(index, newTileset); } bool Map::isTilesetUsed(Tileset *tileset) const { foreach (const Layer *layer, mLayers) if (layer->referencesTileset(tileset)) return true; return false; } Map *Map::clone() const { Map *o = new Map(mOrientation, mWidth, mHeight, mTileWidth, mTileHeight); o->mDrawMargins = mDrawMargins; foreach (const Layer *layer, mLayers) o->addLayer(layer->clone()); o->mTilesets = mTilesets; o->setProperties(properties()); return o; } QString Tiled::orientationToString(Map::Orientation orientation) { switch (orientation) { default: case Map::Unknown: return QLatin1String("unknown"); break; case Map::Orthogonal: return QLatin1String("orthogonal"); break; case Map::Isometric: return QLatin1String("isometric"); break; case Map::Staggered: return QLatin1String("staggered"); break; } } Map::Orientation Tiled::orientationFromString(const QString &string) { Map::Orientation orientation = Map::Unknown; if (string == QLatin1String("orthogonal")) { orientation = Map::Orthogonal; } else if (string == QLatin1String("isometric")) { orientation = Map::Isometric; } else if (string == QLatin1String("staggered")) { orientation = Map::Staggered; } return orientation; } Map *Map::fromLayer(Layer *layer) { Map *result = new Map(Unknown, layer->width(), layer->height(), 0, 0); result->addLayer(layer); return result; } tiled-qt-0.9.1/src/libtiled/map.h000066400000000000000000000232751217502731700165670ustar00rootroot00000000000000/* * map.h * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2008, Roderic Morris * Copyright 2010, Andrew G. Crowell * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAP_H #define MAP_H #include "layer.h" #include "object.h" #include #include #include #include namespace Tiled { class Tile; class Tileset; class ObjectGroup; /** * A tile map. Consists of a stack of layers, each can be either a TileLayer * or an ObjectGroup. * * It also keeps track of the list of referenced tilesets. */ class TILEDSHARED_EXPORT Map : public Object { public: /** * The orientation of the map determines how it should be rendered. An * Orthogonal map is using rectangular tiles that are aligned on a * straight grid. An Isometric map uses diamond shaped tiles that are * aligned on an isometric projected grid. A Hexagonal map uses hexagon * shaped tiles that fit into each other by shifting every other row. * * Only Orthogonal, Isometric and Staggered maps are supported by this * version of Tiled. */ enum Orientation { Unknown, Orthogonal, Isometric, Staggered }; /** * The different formats in which the tile layer data can be stored. */ enum LayerDataFormat { XML = 0, Base64 = 1, Base64Gzip = 2, Base64Zlib = 3, CSV = 4 }; /** * Constructor, taking map orientation, size and tile size as parameters. */ Map(Orientation orientation, int width, int height, int tileWidth, int tileHeight); /** * Destructor. */ ~Map(); /** * Returns the orientation of the map. */ Orientation orientation() const { return mOrientation; } /** * Sets the orientation of the map. */ void setOrientation(Orientation orientation) { mOrientation = orientation; } /** * Returns the width of this map. */ int width() const { return mWidth; } /** * Sets the width of this map. */ void setWidth(int width) { mWidth = width; } /** * Returns the height of this map. */ int height() const { return mHeight; } /** * Sets the height of this map. */ void setHeight(int height) { mHeight = height; } /** * Returns the size of this map. Provided for convenience. */ QSize size() const { return QSize(mWidth, mHeight); } /** * Returns the tile width of this map. */ int tileWidth() const { return mTileWidth; } /** * Sets the width of one tile. */ void setTileWidth(int width) { mTileWidth = width; } /** * Returns the tile height used by this map. */ int tileHeight() const { return mTileHeight; } /** * Sets the height of one tile. */ void setTileHeight(int height) { mTileHeight = height; } /** * Adjusts the draw margins to be at least as big as the given margins. * Called from tile layers when their tiles change. */ void adjustDrawMargins(const QMargins &margins); /** * Returns the margins that have to be taken into account when figuring * out which part of the map to repaint after changing some tiles. * * @see TileLayer::drawMargins */ QMargins drawMargins() const { return mDrawMargins; } /** * Returns the number of layers of this map. */ int layerCount() const { return mLayers.size(); } /** * Convenience function that returns the number of layers of this map that * match the given \a type. */ int layerCount(Layer::Type type) const; int tileLayerCount() const { return layerCount(Layer::TileLayerType); } int objectGroupCount() const { return layerCount(Layer::ObjectGroupType); } int imageLayerCount() const { return layerCount(Layer::ImageLayerType); } /** * Returns the layer at the specified index. */ Layer *layerAt(int index) const { return mLayers.at(index); } /** * Returns the list of layers of this map. This is useful when you want to * use foreach. */ const QList &layers() const { return mLayers; } QList layers(Layer::Type type) const; QList objectGroups() const; QList tileLayers() const; /** * Adds a layer to this map. */ void addLayer(Layer *layer); /** * Returns the index of the layer given by \a layerName, or -1 if no * layer with that name is found. * * The second optional parameter specifies the layer types which are * searched. */ int indexOfLayer(const QString &layerName, unsigned layerTypes = Layer::AnyLayerType) const; /** * Adds a layer to this map, inserting it at the given index. */ void insertLayer(int index, Layer *layer); /** * Removes the layer at the given index from this map and returns it. * The caller becomes responsible for the lifetime of this layer. */ Layer *takeLayerAt(int index); /** * Adds a tileset to this map. The map does not take ownership over its * tilesets, this is merely for keeping track of which tilesets are used by * the map, and their saving order. * * @param tileset the tileset to add */ void addTileset(Tileset *tileset); /** * Inserts \a tileset at \a index in the list of tilesets used by this map. */ void insertTileset(int index, Tileset *tileset); /** * Returns the index of the given \a tileset, or -1 if it is not used in * this map. */ int indexOfTileset(Tileset *tileset) const; /** * Removes the tileset at \a index from this map. * * \warning Does not make sure that this map no longer refers to tiles from * the removed tileset! * * \sa addTileset */ void removeTilesetAt(int index); /** * Replaces all tiles from \a oldTileset with tiles from \a newTileset. * Also replaces the old tileset with the new tileset in the list of * tilesets. */ void replaceTileset(Tileset *oldTileset, Tileset *newTileset); /** * Returns the number of tilesets of this map. */ int tilesetCount() const { return mTilesets.size(); } /** * Returns the tileset at the given index. */ Tileset *tilesetAt(int index) const { return mTilesets.at(index); } /** * Returns the tilesets that the tiles on this map are using. */ const QList &tilesets() const { return mTilesets; } /** * Returns the background color of this map. */ const QColor &backgroundColor() const { return mBackgroundColor; } /** * Sets the background color of this map. */ void setBackgroundColor(QColor color) { mBackgroundColor = color; } /** * Returns whether the given \a tileset is used by any tile layer of this * map. */ bool isTilesetUsed(Tileset *tileset) const; Map *clone() const; /** * Creates a new map that contains the given \a layer. The map size will be * determined by the size of the layer. * * The orientation defaults to Unknown and the tile width and height will * default to 0. In case this map needs to be rendered, these properties * will need to be properly set. */ static Map *fromLayer(Layer *layer); LayerDataFormat layerDataFormat() const { return mLayerDataFormat; } void setLayerDataFormat(LayerDataFormat format) { mLayerDataFormat = format; } private: void adoptLayer(Layer *layer); Orientation mOrientation; int mWidth; int mHeight; int mTileWidth; int mTileHeight; QColor mBackgroundColor; QMargins mDrawMargins; QList mLayers; QList mTilesets; LayerDataFormat mLayerDataFormat; }; /** * Helper function that converts the map orientation to a string value. Useful * for map writers. * * @return The map orientation as a lowercase string. */ TILEDSHARED_EXPORT QString orientationToString(Map::Orientation); /** * Helper function that converts a string to a map orientation enumerator. * Useful for map readers. * * @return The map orientation matching the given string, or Map::Unknown if * the string is unrecognized. */ TILEDSHARED_EXPORT Map::Orientation orientationFromString(const QString &); } // namespace Tiled #endif // MAP_H tiled-qt-0.9.1/src/libtiled/mapobject.cpp000066400000000000000000000041611217502731700203020ustar00rootroot00000000000000/* * mapobject.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2008, Roderic Morris * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mapobject.h" using namespace Tiled; MapObject::MapObject(): mSize(0, 0), mShape(Rectangle), mTile(0), mObjectGroup(0), mVisible(true) { } MapObject::MapObject(const QString &name, const QString &type, const QPointF &pos, const QSizeF &size): mName(name), mType(type), mPos(pos), mSize(size), mShape(Rectangle), mTile(0), mObjectGroup(0), mVisible(true) { } MapObject *MapObject::clone() const { MapObject *o = new MapObject(mName, mType, mPos, mSize); o->setProperties(properties()); o->setPolygon(mPolygon); o->setShape(mShape); o->setTile(mTile); return o; } tiled-qt-0.9.1/src/libtiled/mapobject.h000066400000000000000000000151731217502731700177540ustar00rootroot00000000000000/* * mapobject.h * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2008, Roderic Morris * Copyright 2009, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAPOBJECT_H #define MAPOBJECT_H #include "object.h" #include #include #include #include namespace Tiled { class ObjectGroup; class Tile; /** * An object on a map. Objects are positioned and scaled using floating point * values, ensuring they are not limited to the tile grid. They are suitable * for adding any kind of annotation to your maps, as well as free placement of * images. * * Common usages of objects include defining portals, monsters spawn areas, * ambient effects, scripted areas, etc. */ class TILEDSHARED_EXPORT MapObject : public Object { public: /** * Enumerates the different object shapes. Rectangle is the default shape. * When a polygon is set, the shape determines whether it should be * interpreted as a filled polygon or a line. */ enum Shape { Rectangle, Polygon, Polyline, Ellipse }; /** * Default constructor. */ MapObject(); /** * Constructor. */ MapObject(const QString &name, const QString &type, const QPointF &pos, const QSizeF &size); /** * Destructor. */ ~MapObject() {} /** * Returns the name of this object. The name is usually just used for * identification of the object in the editor. */ const QString &name() const { return mName; } /** * Sets the name of this object. */ void setName(const QString &name) { mName = name; } /** * Returns the type of this object. The type usually says something about * how the object is meant to be interpreted by the engine. */ const QString &type() const { return mType; } /** * Sets the type of this object. */ void setType(const QString &type) { mType = type; } /** * Returns the position of this object. */ const QPointF &position() const { return mPos; } /** * Sets the position of this object. */ void setPosition(const QPointF &pos) { mPos = pos; } /** * Returns the x position of this object. */ qreal x() const { return mPos.x(); } /** * Sets the x position of this object. */ void setX(qreal x) { mPos.setX(x); } /** * Returns the y position of this object. */ qreal y() const { return mPos.y(); } /** * Sets the x position of this object. */ void setY(qreal y) { mPos.setY(y); } /** * Returns the size of this object. */ const QSizeF &size() const { return mSize; } /** * Sets the size of this object. */ void setSize(const QSizeF &size) { mSize = size; } void setSize(qreal width, qreal height) { setSize(QSizeF(width, height)); } /** * Returns the width of this object. */ qreal width() const { return mSize.width(); } /** * Sets the width of this object. */ void setWidth(qreal width) { mSize.setWidth(width); } /** * Returns the height of this object. */ qreal height() const { return mSize.height(); } /** * Sets the height of this object. */ void setHeight(qreal height) { mSize.setHeight(height); } /** * Sets the polygon associated with this object. The polygon is only used * when the object shape is set to either Polygon or Polyline. * * \sa setShape() */ void setPolygon(const QPolygonF &polygon) { mPolygon = polygon; } /** * Returns the polygon associated with this object. Returns an empty * polygon when no polygon is associated with this object. */ const QPolygonF &polygon() const { return mPolygon; } /** * Sets the shape of the object. */ void setShape(Shape shape) { mShape = shape; } /** * Returns the shape of the object. */ Shape shape() const { return mShape; } /** * Shortcut to getting a QRectF from position() and size(). */ QRectF bounds() const { return QRectF(mPos, mSize); } /** * Sets the tile that is associated with this object. The object will * display as the tile image. * * \warning The object shape is ignored for tile objects! */ void setTile(Tile *tile) { mTile = tile; } /** * Returns the tile associated with this object. */ Tile *tile() const { return mTile; } /** * Returns the object group this object belongs to. */ ObjectGroup *objectGroup() const { return mObjectGroup; } /** * Sets the object group this object belongs to. Should only be called * from the ObjectGroup class. */ void setObjectGroup(ObjectGroup *objectGroup) { mObjectGroup = objectGroup; } /** * Returns a duplicate of this object. The caller is responsible for the * ownership of this newly created object. */ MapObject *clone() const; bool isVisible() const { return mVisible; } void setVisible(bool visible) { mVisible = visible; } private: QString mName; QString mType; QPointF mPos; QSizeF mSize; QPolygonF mPolygon; Shape mShape; Tile *mTile; ObjectGroup *mObjectGroup; bool mVisible; }; } // namespace Tiled #endif // MAPOBJECT_H tiled-qt-0.9.1/src/libtiled/mapreader.cpp000066400000000000000000000755151217502731700203110ustar00rootroot00000000000000/* * mapreader.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2010, Jeff Bland * Copyright 2010, Dennis Honeyman * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mapreader.h" #include "compression.h" #include "gidmapper.h" #include "imagelayer.h" #include "objectgroup.h" #include "map.h" #include "mapobject.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include "terrain.h" #include #include #include #include #include #include using namespace Tiled; using namespace Tiled::Internal; namespace Tiled { namespace Internal { class MapReaderPrivate { Q_DECLARE_TR_FUNCTIONS(MapReader) friend class Tiled::MapReader; public: MapReaderPrivate(MapReader *mapReader): p(mapReader), mMap(0), mReadingExternalTileset(false) {} Map *readMap(QIODevice *device, const QString &path); Tileset *readTileset(QIODevice *device, const QString &path); bool openFile(QFile *file); QString errorString() const; private: void readUnknownElement(); Map *readMap(); Tileset *readTileset(); void readTilesetTile(Tileset *tileset); void readTilesetImage(Tileset *tileset); void readTilesetTerrainTypes(Tileset *tileset); QImage readImage(); TileLayer *readLayer(); void readLayerData(TileLayer *tileLayer); void decodeBinaryLayerData(TileLayer *tileLayer, const QStringRef &text, const QStringRef &compression); void decodeCSVLayerData(TileLayer *tileLayer, const QString &text); /** * Returns the cell for the given global tile ID. Errors are raised with * the QXmlStreamReader. * * @param gid the global tile ID * @return the cell data associated with the given global tile ID, or an * empty cell if not found */ Cell cellForGid(unsigned gid); ImageLayer *readImageLayer(); void readImageLayerImage(ImageLayer *imageLayer); ObjectGroup *readObjectGroup(); MapObject *readObject(); QPolygonF readPolygon(); Properties readProperties(); void readProperty(Properties *properties); MapReader *p; QString mError; QString mPath; Map *mMap; QList mCreatedTilesets; GidMapper mGidMapper; bool mReadingExternalTileset; QXmlStreamReader xml; }; } // namespace Internal } // namespace Tiled Map *MapReaderPrivate::readMap(QIODevice *device, const QString &path) { mError.clear(); mPath = path; Map *map = 0; xml.setDevice(device); if (xml.readNextStartElement() && xml.name() == QLatin1String("map")) { map = readMap(); } else { xml.raiseError(tr("Not a map file.")); } mGidMapper.clear(); return map; } Tileset *MapReaderPrivate::readTileset(QIODevice *device, const QString &path) { mError.clear(); mPath = path; Tileset *tileset = 0; mReadingExternalTileset = true; xml.setDevice(device); if (xml.readNextStartElement() && xml.name() == QLatin1String("tileset")) tileset = readTileset(); else xml.raiseError(tr("Not a tileset file.")); mReadingExternalTileset = false; return tileset; } QString MapReaderPrivate::errorString() const { if (!mError.isEmpty()) { return mError; } else { return tr("%3\n\nLine %1, column %2") .arg(xml.lineNumber()) .arg(xml.columnNumber()) .arg(xml.errorString()); } } bool MapReaderPrivate::openFile(QFile *file) { if (!file->exists()) { mError = tr("File not found: %1").arg(file->fileName()); return false; } else if (!file->open(QFile::ReadOnly | QFile::Text)) { mError = tr("Unable to read file: %1").arg(file->fileName()); return false; } return true; } void MapReaderPrivate::readUnknownElement() { qDebug().nospace() << "Unknown element (fixme): " << xml.name() << " at line " << xml.lineNumber() << ", column " << xml.columnNumber(); xml.skipCurrentElement(); } Map *MapReaderPrivate::readMap() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("map")); const QXmlStreamAttributes atts = xml.attributes(); const int mapWidth = atts.value(QLatin1String("width")).toString().toInt(); const int mapHeight = atts.value(QLatin1String("height")).toString().toInt(); const int tileWidth = atts.value(QLatin1String("tilewidth")).toString().toInt(); const int tileHeight = atts.value(QLatin1String("tileheight")).toString().toInt(); const QString orientationString = atts.value(QLatin1String("orientation")).toString(); const Map::Orientation orientation = orientationFromString(orientationString); if (orientation == Map::Unknown) { xml.raiseError(tr("Unsupported map orientation: \"%1\"") .arg(orientationString)); } mMap = new Map(orientation, mapWidth, mapHeight, tileWidth, tileHeight); mCreatedTilesets.clear(); QStringRef bgColorString = atts.value(QLatin1String("backgroundcolor")); if (!bgColorString.isEmpty()) mMap->setBackgroundColor(QColor(bgColorString.toString())); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("properties")) mMap->mergeProperties(readProperties()); else if (xml.name() == QLatin1String("tileset")) mMap->addTileset(readTileset()); else if (xml.name() == QLatin1String("layer")) mMap->addLayer(readLayer()); else if (xml.name() == QLatin1String("objectgroup")) mMap->addLayer(readObjectGroup()); else if (xml.name() == QLatin1String("imagelayer")) mMap->addLayer(readImageLayer()); else readUnknownElement(); } // Clean up in case of error if (xml.hasError()) { // The tilesets are not owned by the map qDeleteAll(mCreatedTilesets); mCreatedTilesets.clear(); delete mMap; mMap = 0; } return mMap; } Tileset *MapReaderPrivate::readTileset() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("tileset")); const QXmlStreamAttributes atts = xml.attributes(); const QString source = atts.value(QLatin1String("source")).toString(); const unsigned firstGid = atts.value(QLatin1String("firstgid")).toString().toUInt(); Tileset *tileset = 0; if (source.isEmpty()) { // Not an external tileset const QString name = atts.value(QLatin1String("name")).toString(); const int tileWidth = atts.value(QLatin1String("tilewidth")).toString().toInt(); const int tileHeight = atts.value(QLatin1String("tileheight")).toString().toInt(); const int tileSpacing = atts.value(QLatin1String("spacing")).toString().toInt(); const int margin = atts.value(QLatin1String("margin")).toString().toInt(); if (tileWidth < 0 || tileHeight < 0 || (firstGid == 0 && !mReadingExternalTileset)) { xml.raiseError(tr("Invalid tileset parameters for tileset" " '%1'").arg(name)); } else { tileset = new Tileset(name, tileWidth, tileHeight, tileSpacing, margin); mCreatedTilesets.append(tileset); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("tile")) { readTilesetTile(tileset); } else if (xml.name() == QLatin1String("tileoffset")) { const QXmlStreamAttributes oa = xml.attributes(); int x = oa.value(QLatin1String("x")).toString().toInt(); int y = oa.value(QLatin1String("y")).toString().toInt(); tileset->setTileOffset(QPoint(x, y)); xml.skipCurrentElement(); } else if (xml.name() == QLatin1String("properties")) { tileset->mergeProperties(readProperties()); } else if (xml.name() == QLatin1String("image")) { if (tileWidth == 0 || tileHeight == 0) { xml.raiseError(tr("Invalid tileset parameters for tileset" " '%1'").arg(name)); } readTilesetImage(tileset); } else if (xml.name() == QLatin1String("terraintypes")) { readTilesetTerrainTypes(tileset); } else { readUnknownElement(); } } } } else { // External tileset const QString absoluteSource = p->resolveReference(source, mPath); QString error; tileset = p->readExternalTileset(absoluteSource, &error); if (!tileset) { xml.raiseError(tr("Error while loading tileset '%1': %2") .arg(absoluteSource, error)); } xml.skipCurrentElement(); } if (tileset && !mReadingExternalTileset) mGidMapper.insert(firstGid, tileset); return tileset; } void MapReaderPrivate::readTilesetTile(Tileset *tileset) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("tile")); const QXmlStreamAttributes atts = xml.attributes(); const int id = atts.value(QLatin1String("id")).toString().toInt(); if (id < 0) { xml.raiseError(tr("Invalid tile ID: %1").arg(id)); return; } else if (id == tileset->tileCount()) { tileset->addTile(QPixmap()); } else if (id > tileset->tileCount()) { xml.raiseError(tr("Invalid (nonconsecutive) tile ID: %1").arg(id)); return; } Tile *tile = tileset->tileAt(id); // Read tile quadrant terrain ids QString terrain = atts.value(QLatin1String("terrain")).toString(); if (!terrain.isEmpty()) { QStringList quadrants = terrain.split(QLatin1String(",")); if (quadrants.size() == 4) { for (int i = 0; i < 4; ++i) { int t = quadrants[i].isEmpty() ? -1 : quadrants[i].toInt(); tile->setCornerTerrain(i, t); } } } // Read tile probability QString probability = atts.value(QLatin1String("probability")).toString(); if (!probability.isEmpty()) { tile->setTerrainProbability(probability.toFloat()); } while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("properties")) { tile->mergeProperties(readProperties()); } else if (xml.name() == QLatin1String("image")) { tileset->setTileImage(id, QPixmap::fromImage(readImage())); } else { readUnknownElement(); } } } void MapReaderPrivate::readTilesetImage(Tileset *tileset) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("image")); const QXmlStreamAttributes atts = xml.attributes(); QString source = atts.value(QLatin1String("source")).toString(); QString trans = atts.value(QLatin1String("trans")).toString(); if (!trans.isEmpty()) { if (!trans.startsWith(QLatin1Char('#'))) trans.prepend(QLatin1Char('#')); tileset->setTransparentColor(QColor(trans)); } source = p->resolveReference(source, mPath); // Set the width that the tileset had when the map was saved const int width = atts.value(QLatin1String("width")).toString().toInt(); mGidMapper.setTilesetWidth(tileset, width); if (!tileset->loadFromImage(readImage(), source)) xml.raiseError(tr("Error loading tileset image:\n'%1'").arg(source)); } QImage MapReaderPrivate::readImage() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("image")); const QXmlStreamAttributes atts = xml.attributes(); QString source = atts.value(QLatin1String("source")).toString(); QString format = atts.value(QLatin1String("format")).toString(); if (source.isEmpty()) { while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("data")) { const QXmlStreamAttributes atts = xml.attributes(); QString encoding = atts.value(QLatin1String("encoding")) .toString(); QByteArray data = xml.readElementText().toLatin1(); if (encoding == QLatin1String("base64")) { data = QByteArray::fromBase64(data); } xml.skipCurrentElement(); return QImage::fromData(data, format.toLatin1()); } else { readUnknownElement(); } } } else { xml.skipCurrentElement(); source = p->resolveReference(source, mPath); QImage image = p->readExternalImage(source); if (image.isNull()) xml.raiseError(tr("Error loading image:\n'%1'").arg(source)); return image; } return QImage(); } void MapReaderPrivate::readTilesetTerrainTypes(Tileset *tileset) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("terraintypes")); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("terrain")) { const QXmlStreamAttributes atts = xml.attributes(); QString name = atts.value(QLatin1String("name")).toString(); int tile = atts.value(QLatin1String("tile")).toString().toInt(); Terrain *terrain = tileset->addTerrain(name, tile); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("properties")) terrain->mergeProperties(readProperties()); else readUnknownElement(); } } else { readUnknownElement(); } } } static void readLayerAttributes(Layer *layer, const QXmlStreamAttributes &atts) { const QStringRef opacityRef = atts.value(QLatin1String("opacity")); const QStringRef visibleRef = atts.value(QLatin1String("visible")); bool ok; const float opacity = opacityRef.toString().toFloat(&ok); if (ok) layer->setOpacity(opacity); const int visible = visibleRef.toString().toInt(&ok); if (ok) layer->setVisible(visible); } TileLayer *MapReaderPrivate::readLayer() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("layer")); const QXmlStreamAttributes atts = xml.attributes(); const QString name = atts.value(QLatin1String("name")).toString(); const int x = atts.value(QLatin1String("x")).toString().toInt(); const int y = atts.value(QLatin1String("y")).toString().toInt(); const int width = atts.value(QLatin1String("width")).toString().toInt(); const int height = atts.value(QLatin1String("height")).toString().toInt(); TileLayer *tileLayer = new TileLayer(name, x, y, width, height); readLayerAttributes(tileLayer, atts); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("properties")) tileLayer->mergeProperties(readProperties()); else if (xml.name() == QLatin1String("data")) readLayerData(tileLayer); else readUnknownElement(); } return tileLayer; } void MapReaderPrivate::readLayerData(TileLayer *tileLayer) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("data")); const QXmlStreamAttributes atts = xml.attributes(); QStringRef encoding = atts.value(QLatin1String("encoding")); QStringRef compression = atts.value(QLatin1String("compression")); bool respect = true; // TODO: init from preferences if (respect) { if (encoding.isEmpty()) mMap->setLayerDataFormat(Map::XML); else if (encoding == QLatin1String("csv")) mMap->setLayerDataFormat(Map::CSV); else if (encoding == QLatin1String("base64")) { if (compression.isEmpty()) mMap->setLayerDataFormat(Map::Base64); else if (compression == QLatin1String("gzip")) mMap->setLayerDataFormat(Map::Base64Gzip); else if (compression == QLatin1String("zlib")) mMap->setLayerDataFormat(Map::Base64Zlib); } // else, error handled below } int x = 0; int y = 0; while (xml.readNext() != QXmlStreamReader::Invalid) { if (xml.isEndElement()) break; else if (xml.isStartElement()) { if (xml.name() == QLatin1String("tile")) { if (y >= tileLayer->height()) { xml.raiseError(tr("Too many elements")); continue; } const QXmlStreamAttributes atts = xml.attributes(); unsigned gid = atts.value(QLatin1String("gid")).toString().toUInt(); tileLayer->setCell(x, y, cellForGid(gid)); x++; if (x >= tileLayer->width()) { x = 0; y++; } xml.skipCurrentElement(); } else { readUnknownElement(); } } else if (xml.isCharacters() && !xml.isWhitespace()) { if (encoding == QLatin1String("base64")) { decodeBinaryLayerData(tileLayer, xml.text(), compression); } else if (encoding == QLatin1String("csv")) { decodeCSVLayerData(tileLayer, xml.text().toString()); } else { xml.raiseError(tr("Unknown encoding: %1") .arg(encoding.toString())); continue; } } } } void MapReaderPrivate::decodeBinaryLayerData(TileLayer *tileLayer, const QStringRef &text, const QStringRef &compression) { #if QT_VERSION < 0x040800 const QString textData = QString::fromRawData(text.unicode(), text.size()); const QByteArray latin1Text = textData.toLatin1(); #else const QByteArray latin1Text = text.toLatin1(); #endif QByteArray tileData = QByteArray::fromBase64(latin1Text); const int size = (tileLayer->width() * tileLayer->height()) * 4; if (compression == QLatin1String("zlib") || compression == QLatin1String("gzip")) { tileData = decompress(tileData, size); } else if (!compression.isEmpty()) { xml.raiseError(tr("Compression method '%1' not supported") .arg(compression.toString())); return; } if (size != tileData.length()) { xml.raiseError(tr("Corrupt layer data for layer '%1'") .arg(tileLayer->name())); return; } const unsigned char *data = reinterpret_cast(tileData.constData()); int x = 0; int y = 0; for (int i = 0; i < size - 3; i += 4) { const unsigned gid = data[i] | data[i + 1] << 8 | data[i + 2] << 16 | data[i + 3] << 24; tileLayer->setCell(x, y, cellForGid(gid)); x++; if (x == tileLayer->width()) { x = 0; y++; } } } void MapReaderPrivate::decodeCSVLayerData(TileLayer *tileLayer, const QString &text) { QString trimText = text.trimmed(); QStringList tiles = trimText.split(QLatin1Char(',')); if (tiles.length() != tileLayer->width() * tileLayer->height()) { xml.raiseError(tr("Corrupt layer data for layer '%1'") .arg(tileLayer->name())); return; } for (int y = 0; y < tileLayer->height(); y++) { for (int x = 0; x < tileLayer->width(); x++) { bool conversionOk; const unsigned gid = tiles.at(y * tileLayer->width() + x) .toUInt(&conversionOk); if (!conversionOk) { xml.raiseError( tr("Unable to parse tile at (%1,%2) on layer '%3'") .arg(x + 1).arg(y + 1).arg(tileLayer->name())); return; } tileLayer->setCell(x, y, cellForGid(gid)); } } } Cell MapReaderPrivate::cellForGid(unsigned gid) { bool ok; const Cell result = mGidMapper.gidToCell(gid, ok); if (!ok) { if (mGidMapper.isEmpty()) xml.raiseError(tr("Tile used but no tilesets specified")); else xml.raiseError(tr("Invalid tile: %1").arg(gid)); } return result; } ObjectGroup *MapReaderPrivate::readObjectGroup() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("objectgroup")); const QXmlStreamAttributes atts = xml.attributes(); const QString name = atts.value(QLatin1String("name")).toString(); const int x = atts.value(QLatin1String("x")).toString().toInt(); const int y = atts.value(QLatin1String("y")).toString().toInt(); const int width = atts.value(QLatin1String("width")).toString().toInt(); const int height = atts.value(QLatin1String("height")).toString().toInt(); ObjectGroup *objectGroup = new ObjectGroup(name, x, y, width, height); readLayerAttributes(objectGroup, atts); const QString color = atts.value(QLatin1String("color")).toString(); if (!color.isEmpty()) objectGroup->setColor(color); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("object")) objectGroup->addObject(readObject()); else if (xml.name() == QLatin1String("properties")) objectGroup->mergeProperties(readProperties()); else readUnknownElement(); } return objectGroup; } ImageLayer *MapReaderPrivate::readImageLayer() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("imagelayer")); const QXmlStreamAttributes atts = xml.attributes(); const QString name = atts.value(QLatin1String("name")).toString(); const int x = atts.value(QLatin1String("x")).toString().toInt(); const int y = atts.value(QLatin1String("y")).toString().toInt(); const int width = atts.value(QLatin1String("width")).toString().toInt(); const int height = atts.value(QLatin1String("height")).toString().toInt(); ImageLayer *imageLayer = new ImageLayer(name, x, y, width, height); readLayerAttributes(imageLayer, atts); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("image")) readImageLayerImage(imageLayer); else if (xml.name() == QLatin1String("properties")) imageLayer->mergeProperties(readProperties()); else readUnknownElement(); } return imageLayer; } void MapReaderPrivate::readImageLayerImage(ImageLayer *imageLayer) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("image")); const QXmlStreamAttributes atts = xml.attributes(); QString source = atts.value(QLatin1String("source")).toString(); QString trans = atts.value(QLatin1String("trans")).toString(); if (!trans.isEmpty()) { if (!trans.startsWith(QLatin1Char('#'))) trans.prepend(QLatin1Char('#')); imageLayer->setTransparentColor(QColor(trans)); } source = p->resolveReference(source, mPath); const QImage imageLayerImage = p->readExternalImage(source); if (!imageLayer->loadFromImage(imageLayerImage, source)) xml.raiseError(tr("Error loading image layer image:\n'%1'").arg(source)); xml.skipCurrentElement(); } static QPointF pixelToTileCoordinates(Map *map, int x, int y) { const int tileHeight = map->tileHeight(); const int tileWidth = map->tileWidth(); if (map->orientation() == Map::Isometric) { // Isometric needs special handling, since the pixel values are based // solely on the tile height. return QPointF((qreal) x / tileHeight, (qreal) y / tileHeight); } else { return QPointF((qreal) x / tileWidth, (qreal) y / tileHeight); } } MapObject *MapReaderPrivate::readObject() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("object")); const QXmlStreamAttributes atts = xml.attributes(); const QString name = atts.value(QLatin1String("name")).toString(); const unsigned gid = atts.value(QLatin1String("gid")).toString().toUInt(); const int x = atts.value(QLatin1String("x")).toString().toInt(); const int y = atts.value(QLatin1String("y")).toString().toInt(); const int width = atts.value(QLatin1String("width")).toString().toInt(); const int height = atts.value(QLatin1String("height")).toString().toInt(); const QString type = atts.value(QLatin1String("type")).toString(); const QStringRef visibleRef = atts.value(QLatin1String("visible")); const QPointF pos = pixelToTileCoordinates(mMap, x, y); const QPointF size = pixelToTileCoordinates(mMap, width, height); MapObject *object = new MapObject(name, type, pos, QSizeF(size.x(), size.y())); if (gid) { const Cell cell = cellForGid(gid); object->setTile(cell.tile); } bool ok; const int visible = visibleRef.toString().toInt(&ok); if (ok) object->setVisible(visible); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("properties")) { object->mergeProperties(readProperties()); } else if (xml.name() == QLatin1String("polygon")) { object->setPolygon(readPolygon()); object->setShape(MapObject::Polygon); } else if (xml.name() == QLatin1String("polyline")) { object->setPolygon(readPolygon()); object->setShape(MapObject::Polyline); } else if (xml.name() == QLatin1String("ellipse")) { xml.skipCurrentElement(); object->setShape(MapObject::Ellipse); } else { readUnknownElement(); } } return object; } QPolygonF MapReaderPrivate::readPolygon() { Q_ASSERT(xml.isStartElement() && (xml.name() == QLatin1String("polygon") || xml.name() == QLatin1String("polyline"))); const QXmlStreamAttributes atts = xml.attributes(); const QString points = atts.value(QLatin1String("points")).toString(); const QStringList pointsList = points.split(QLatin1Char(' '), QString::SkipEmptyParts); QPolygonF polygon; bool ok = true; foreach (const QString &point, pointsList) { const int commaPos = point.indexOf(QLatin1Char(',')); if (commaPos == -1) { ok = false; break; } const int x = point.left(commaPos).toInt(&ok); if (!ok) break; const int y = point.mid(commaPos + 1).toInt(&ok); if (!ok) break; polygon.append(pixelToTileCoordinates(mMap, x, y)); } if (!ok) xml.raiseError(tr("Invalid points data for polygon")); xml.skipCurrentElement(); return polygon; } Properties MapReaderPrivate::readProperties() { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("properties")); Properties properties; while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("property")) readProperty(&properties); else readUnknownElement(); } return properties; } void MapReaderPrivate::readProperty(Properties *properties) { Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("property")); const QXmlStreamAttributes atts = xml.attributes(); QString propertyName = atts.value(QLatin1String("name")).toString(); QString propertyValue = atts.value(QLatin1String("value")).toString(); while (xml.readNext() != QXmlStreamReader::Invalid) { if (xml.isEndElement()) { break; } else if (xml.isCharacters() && !xml.isWhitespace()) { if (propertyValue.isEmpty()) propertyValue = xml.text().toString(); } else if (xml.isStartElement()) { readUnknownElement(); } } properties->insert(propertyName, propertyValue); } MapReader::MapReader() : d(new MapReaderPrivate(this)) { } MapReader::~MapReader() { delete d; } Map *MapReader::readMap(QIODevice *device, const QString &path) { return d->readMap(device, path); } Map *MapReader::readMap(const QString &fileName) { QFile file(fileName); if (!d->openFile(&file)) return 0; return readMap(&file, QFileInfo(fileName).absolutePath()); } Tileset *MapReader::readTileset(QIODevice *device, const QString &path) { return d->readTileset(device, path); } Tileset *MapReader::readTileset(const QString &fileName) { QFile file(fileName); if (!d->openFile(&file)) return 0; Tileset *tileset = readTileset(&file, QFileInfo(fileName).absolutePath()); if (tileset) tileset->setFileName(fileName); return tileset; } QString MapReader::errorString() const { return d->errorString(); } QString MapReader::resolveReference(const QString &reference, const QString &mapPath) { if (QDir::isRelativePath(reference)) return mapPath + QLatin1Char('/') + reference; else return reference; } QImage MapReader::readExternalImage(const QString &source) { return QImage(source); } Tileset *MapReader::readExternalTileset(const QString &source, QString *error) { MapReader reader; Tileset *tileset = reader.readTileset(source); if (!tileset) *error = reader.errorString(); else d->mCreatedTilesets.append(tileset); return tileset; } tiled-qt-0.9.1/src/libtiled/mapreader.h000066400000000000000000000100101217502731700177310ustar00rootroot00000000000000/* * mapreader.h * Copyright 2008-2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAPREADER_H #define MAPREADER_H #include "tiled_global.h" #include class QFile; namespace Tiled { class Map; class Tileset; namespace Internal { class MapReaderPrivate; } /** * A fast QXmlStreamReader based reader for the TMX and TSX formats. * * Can be subclassed when special handling of external images and tilesets is * needed. */ class TILEDSHARED_EXPORT MapReader { public: MapReader(); ~MapReader(); /** * Reads a TMX map from the given \a device. Optionally a \a path can * be given, which will be used to resolve relative references to external * images and tilesets. * * Returns 0 and sets errorString() when reading failed. * * The caller takes ownership over the newly created map. */ Map *readMap(QIODevice *device, const QString &path = QString()); /** * Reads a TMX map from the given \a fileName. * \overload */ Map *readMap(const QString &fileName); /** * Reads a TSX tileset from the given \a device. Optionally a \a path can * be given, which will be used to resolve relative references to external * images. * * Returns 0 and sets errorString() when reading failed. * * The caller takes ownership over the newly created tileset. */ Tileset *readTileset(QIODevice *device, const QString &path = QString()); /** * Reads a TSX tileset from the given \a fileName. * \overload */ Tileset *readTileset(const QString &fileName); /** * Returns the error message for the last occurred error. */ QString errorString() const; protected: /** * Called for each \a reference to an external file. Should return the path * to be used when loading this file. \a mapPath contains the path to the * map or tileset that is currently being loaded. */ virtual QString resolveReference(const QString &reference, const QString &mapPath); /** * Called when an external image is encountered while a tileset is loaded. */ virtual QImage readExternalImage(const QString &source); /** * Called when an external tileset is encountered while a map is loaded. * The default implementation just calls readTileset() on a new MapReader. * * If an error occurred, the \a error parameter should be set to the error * message. */ virtual Tileset *readExternalTileset(const QString &source, QString *error); private: friend class Internal::MapReaderPrivate; Internal::MapReaderPrivate *d; }; } // namespace Tiled #endif // MAPREADER_H tiled-qt-0.9.1/src/libtiled/mapreaderinterface.h000066400000000000000000000060601217502731700216240ustar00rootroot00000000000000/* * mapreaderinterface.h * Copyright 2008-2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAPREADERINTERFACE_H #define MAPREADERINTERFACE_H #include #include class QString; namespace Tiled { class Map; /** * An interface to be implemented by map readers. A map reader implements * support for loading a certain map format. * * At the moment, Tiled only provides a reader for its own .tmx map format * through the TmxMapReader. */ class MapReaderInterface { public: virtual ~MapReaderInterface() {} /** * Reads the map and returns a new Map instance, or 0 if reading failed. */ virtual Map *read(const QString &fileName) = 0; /** * Returns name filters of this map reader, for multiple formats. */ virtual QStringList nameFilters() const { return QStringList(nameFilter()); } /** * Returns whether this map reader supports reading the given file. * * Generally would do a file extension check. */ virtual bool supportsFile(const QString &fileName) const = 0; /** * Returns the error to be shown to the user if an error occured while * trying to read a map. */ virtual QString errorString() const = 0; protected: /** * Returns the name filter of this map reader. * * Protected because it should not be used outside the plugin since * plugins may expose multiple name filters. This thing exists only for * convenience for plugin writers since most plugins will have only one * name filter. */ virtual QString nameFilter() const { return QString(); } }; } // namespace Tiled Q_DECLARE_INTERFACE(Tiled::MapReaderInterface, "org.mapeditor.MapReaderInterface") #endif // MAPREADERINTERFACE_H tiled-qt-0.9.1/src/libtiled/maprenderer.cpp000066400000000000000000000053761217502731700206530ustar00rootroot00000000000000/* * maprenderer.cpp * Copyright 2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "maprenderer.h" #include "imagelayer.h" #include using namespace Tiled; QRectF MapRenderer::boundingRect(const ImageLayer *imageLayer) const { return QRectF(imageLayer->position(), imageLayer->image().size()); } void MapRenderer::drawImageLayer(QPainter *painter, const ImageLayer *imageLayer, const QRectF &exposed) { Q_UNUSED(exposed) painter->drawPixmap(imageLayer->position(), imageLayer->image()); } void MapRenderer::setFlag(RenderFlag flag, bool enabled) { if (enabled) mFlags |= flag; else mFlags &= ~flag; } /** * Converts a line running from \a start to \a end to a polygon which * extends 5 pixels from the line in all directions. */ QPolygonF MapRenderer::lineToPolygon(const QPointF &start, const QPointF &end) { QPointF direction = QVector2D(end - start).normalized().toPointF(); QPointF perpendicular(-direction.y(), direction.x()); const qreal thickness = 5.0f; // 5 pixels on each side direction *= thickness; perpendicular *= thickness; QPolygonF polygon(4); polygon[0] = start + perpendicular - direction; polygon[1] = start - perpendicular - direction; polygon[2] = end - perpendicular + direction; polygon[3] = end + perpendicular + direction; return polygon; } tiled-qt-0.9.1/src/libtiled/maprenderer.h000066400000000000000000000137271217502731700203170ustar00rootroot00000000000000/* * maprenderer.h * Copyright 2009-2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAPRENDERER_H #define MAPRENDERER_H #include "tiled_global.h" #include namespace Tiled { class Layer; class Map; class MapObject; class TileLayer; class ImageLayer; enum RenderFlag { ShowTileObjectOutlines = 0x1 }; Q_DECLARE_FLAGS(RenderFlags, RenderFlag) /** * This interface is used for rendering tile layers and retrieving associated * metrics. The different implementations deal with different map * orientations. */ class TILEDSHARED_EXPORT MapRenderer { public: MapRenderer(const Map *map) : mMap(map) {} virtual ~MapRenderer() {} /** * Returns the size in pixels of the map associated with this renderer. */ virtual QSize mapSize() const = 0; /** * Returns the bounding rectangle in pixels of the given \a rect given in * tile coordinates. * * This is useful for calculating the bounding rect of a tile layer or of * a region of tiles that was changed. */ virtual QRect boundingRect(const QRect &rect) const = 0; /** * Returns the bounding rectangle in pixels of the given \a object, as it * would be drawn by drawMapObject(). */ virtual QRectF boundingRect(const MapObject *object) const = 0; /** * Returns the bounding rectangle in pixels of the given \a imageLayer, as * it would be drawn by drawImageLayer(). */ QRectF boundingRect(const ImageLayer *imageLayer) const; /** * Returns the shape in pixels of the given \a object. This is used for * mouse interaction and should match the rendered object as closely as * possible. */ virtual QPainterPath shape(const MapObject *object) const = 0; /** * Draws the tile grid in the specified \a rect using the given * \a painter. */ virtual void drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor = Qt::black) const = 0; /** * Draws the given \a layer using the given \a painter. * * Optionally, you can pass in the \a exposed rect (of pixels), so that * only tiles that can be visible in this area will be drawn. */ virtual void drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed = QRectF()) const = 0; /** * Draws the tile selection given by \a region in the specified \a color. * * The implementation can be optimized by taking into account the * \a exposed rectangle, to avoid drawing too much. */ virtual void drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const = 0; /** * Draws the \a object in the given \a color using the \a painter. */ virtual void drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const = 0; /** * Draws the given image \a layer using the given \a painter. */ void drawImageLayer(QPainter *painter, const ImageLayer *imageLayer, const QRectF &exposed = QRectF()); /** * Returns the tile coordinates matching the given pixel position. */ virtual QPointF pixelToTileCoords(qreal x, qreal y) const = 0; inline QPointF pixelToTileCoords(const QPointF &point) const { return pixelToTileCoords(point.x(), point.y()); } /** * Returns the pixel coordinates matching the given tile coordinates. */ virtual QPointF tileToPixelCoords(qreal x, qreal y) const = 0; inline QPointF tileToPixelCoords(const QPointF &point) const { return tileToPixelCoords(point.x(), point.y()); } QPolygonF tileToPixelCoords(const QPolygonF &polygon) const { QPolygonF screenPolygon(polygon.size()); for (int i = polygon.size() - 1; i >= 0; --i) screenPolygon[i] = tileToPixelCoords(polygon[i]); return screenPolygon; } void setFlag(RenderFlag flag, bool enabled = true); bool testFlag(RenderFlag flag) const { return mFlags.testFlag(flag); } RenderFlags flags() const { return mFlags; } void setFlags(RenderFlags flags) { mFlags = flags; } static QPolygonF lineToPolygon(const QPointF &start, const QPointF &end); protected: /** * Returns the map this renderer is associated with. */ const Map *map() const { return mMap; } private: const Map *mMap; RenderFlags mFlags; }; } // namespace Tiled Q_DECLARE_OPERATORS_FOR_FLAGS(Tiled::RenderFlags) #endif // MAPRENDERER_H tiled-qt-0.9.1/src/libtiled/mapwriter.cpp000066400000000000000000000535651217502731700203640ustar00rootroot00000000000000/* * mapwriter.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2010, Jeff Bland * Copyright 2010, Dennis Honeyman * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mapwriter.h" #include "compression.h" #include "gidmapper.h" #include "map.h" #include "mapobject.h" #include "imagelayer.h" #include "objectgroup.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include "terrain.h" #include #include #include #include using namespace Tiled; using namespace Tiled::Internal; namespace Tiled { namespace Internal { class MapWriterPrivate { Q_DECLARE_TR_FUNCTIONS(MapReader) public: MapWriterPrivate(); void writeMap(const Map *map, QIODevice *device, const QString &path); void writeTileset(const Tileset *tileset, QIODevice *device, const QString &path); bool openFile(QFile *file); QString mError; Map::LayerDataFormat mLayerDataFormat; bool mDtdEnabled; private: void writeMap(QXmlStreamWriter &w, const Map *map); void writeTileset(QXmlStreamWriter &w, const Tileset *tileset, unsigned firstGid); void writeTileLayer(QXmlStreamWriter &w, const TileLayer *tileLayer); void writeLayerAttributes(QXmlStreamWriter &w, const Layer *layer); void writeObjectGroup(QXmlStreamWriter &w, const ObjectGroup *objectGroup); void writeObject(QXmlStreamWriter &w, const MapObject *mapObject); void writeImageLayer(QXmlStreamWriter &w, const ImageLayer *imageLayer); void writeProperties(QXmlStreamWriter &w, const Properties &properties); QDir mMapDir; // The directory in which the map is being saved GidMapper mGidMapper; bool mUseAbsolutePaths; }; } // namespace Internal } // namespace Tiled MapWriterPrivate::MapWriterPrivate() : mLayerDataFormat(Map::Base64Zlib) , mDtdEnabled(false) , mUseAbsolutePaths(false) { } bool MapWriterPrivate::openFile(QFile *file) { if (!file->open(QIODevice::WriteOnly)) { mError = tr("Could not open file for writing."); return false; } return true; } static QXmlStreamWriter *createWriter(QIODevice *device) { QXmlStreamWriter *writer = new QXmlStreamWriter(device); writer->setAutoFormatting(true); writer->setAutoFormattingIndent(1); return writer; } void MapWriterPrivate::writeMap(const Map *map, QIODevice *device, const QString &path) { mMapDir = QDir(path); mUseAbsolutePaths = path.isEmpty(); QXmlStreamWriter *writer = createWriter(device); writer->writeStartDocument(); if (mDtdEnabled) { writer->writeDTD(QLatin1String("")); } writeMap(*writer, map); writer->writeEndDocument(); delete writer; } void MapWriterPrivate::writeTileset(const Tileset *tileset, QIODevice *device, const QString &path) { mMapDir = QDir(path); mUseAbsolutePaths = path.isEmpty(); QXmlStreamWriter *writer = createWriter(device); writer->writeStartDocument(); if (mDtdEnabled) { writer->writeDTD(QLatin1String("")); } writeTileset(*writer, tileset, 0); writer->writeEndDocument(); delete writer; } void MapWriterPrivate::writeMap(QXmlStreamWriter &w, const Map *map) { w.writeStartElement(QLatin1String("map")); const QString orientation = orientationToString(map->orientation()); w.writeAttribute(QLatin1String("version"), QLatin1String("1.0")); w.writeAttribute(QLatin1String("orientation"), orientation); w.writeAttribute(QLatin1String("width"), QString::number(map->width())); w.writeAttribute(QLatin1String("height"), QString::number(map->height())); w.writeAttribute(QLatin1String("tilewidth"), QString::number(map->tileWidth())); w.writeAttribute(QLatin1String("tileheight"), QString::number(map->tileHeight())); if (map->backgroundColor().isValid()) { w.writeAttribute(QLatin1String("backgroundcolor"), map->backgroundColor().name()); } writeProperties(w, map->properties()); mGidMapper.clear(); unsigned firstGid = 1; foreach (Tileset *tileset, map->tilesets()) { writeTileset(w, tileset, firstGid); mGidMapper.insert(firstGid, tileset); firstGid += tileset->tileCount(); } foreach (const Layer *layer, map->layers()) { const Layer::Type type = layer->type(); if (type == Layer::TileLayerType) writeTileLayer(w, static_cast(layer)); else if (type == Layer::ObjectGroupType) writeObjectGroup(w, static_cast(layer)); else if (type == Layer::ImageLayerType) writeImageLayer(w, static_cast(layer)); } w.writeEndElement(); } static QString makeTerrainAttribute(const Tile *tile) { QString terrain; for (int i = 0; i < 4; ++i ) { if (i > 0) terrain += QLatin1String(","); int t = tile->cornerTerrainId(i); if (t > -1) terrain += QString::number(t); } return terrain; } void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset *tileset, unsigned firstGid) { w.writeStartElement(QLatin1String("tileset")); if (firstGid > 0) w.writeAttribute(QLatin1String("firstgid"), QString::number(firstGid)); const QString &fileName = tileset->fileName(); if (!fileName.isEmpty()) { QString source = fileName; if (!mUseAbsolutePaths) source = mMapDir.relativeFilePath(source); w.writeAttribute(QLatin1String("source"), source); // Tileset is external, so no need to write any of the stuff below w.writeEndElement(); return; } w.writeAttribute(QLatin1String("name"), tileset->name()); w.writeAttribute(QLatin1String("tilewidth"), QString::number(tileset->tileWidth())); w.writeAttribute(QLatin1String("tileheight"), QString::number(tileset->tileHeight())); const int tileSpacing = tileset->tileSpacing(); const int margin = tileset->margin(); if (tileSpacing != 0) w.writeAttribute(QLatin1String("spacing"), QString::number(tileSpacing)); if (margin != 0) w.writeAttribute(QLatin1String("margin"), QString::number(margin)); const QPoint offset = tileset->tileOffset(); if (!offset.isNull()) { w.writeStartElement(QLatin1String("tileoffset")); w.writeAttribute(QLatin1String("x"), QString::number(offset.x())); w.writeAttribute(QLatin1String("y"), QString::number(offset.y())); w.writeEndElement(); } // Write the tileset properties writeProperties(w, tileset->properties()); // Write the image element const QString &imageSource = tileset->imageSource(); if (!imageSource.isEmpty()) { w.writeStartElement(QLatin1String("image")); QString source = imageSource; if (!mUseAbsolutePaths) source = mMapDir.relativeFilePath(source); w.writeAttribute(QLatin1String("source"), source); const QColor transColor = tileset->transparentColor(); if (transColor.isValid()) w.writeAttribute(QLatin1String("trans"), transColor.name().mid(1)); if (tileset->imageWidth() > 0) w.writeAttribute(QLatin1String("width"), QString::number(tileset->imageWidth())); if (tileset->imageHeight() > 0) w.writeAttribute(QLatin1String("height"), QString::number(tileset->imageHeight())); w.writeEndElement(); } // Write the terrain types if (tileset->terrainCount() > 0) { w.writeStartElement(QLatin1String("terraintypes")); for (int i = 0; i < tileset->terrainCount(); ++i) { Terrain* t = tileset->terrain(i); w.writeStartElement(QLatin1String("terrain")); w.writeAttribute(QLatin1String("name"), t->name()); w.writeAttribute(QLatin1String("tile"), QString::number(t->imageTileId())); writeProperties(w, t->properties()); w.writeEndElement(); } w.writeEndElement(); } // Write the properties for those tiles that have them for (int i = 0; i < tileset->tileCount(); ++i) { const Tile *tile = tileset->tileAt(i); const Properties properties = tile->properties(); unsigned terrain = tile->terrain(); float probability = tile->terrainProbability(); if (!properties.isEmpty() || terrain != 0xFFFFFFFF || probability != -1.f || imageSource.isEmpty()) { w.writeStartElement(QLatin1String("tile")); w.writeAttribute(QLatin1String("id"), QString::number(i)); if (terrain != 0xFFFFFFFF) w.writeAttribute(QLatin1String("terrain"), makeTerrainAttribute(tile)); if (probability != -1.f) w.writeAttribute(QLatin1String("probability"), QString::number(probability)); if (!properties.isEmpty()) writeProperties(w, properties); if (imageSource.isEmpty()) { w.writeStartElement(QLatin1String("image")); w.writeAttribute(QLatin1String("format"), QLatin1String("png")); w.writeStartElement(QLatin1String("data")); w.writeAttribute(QLatin1String("encoding"), QLatin1String("base64")); QBuffer buffer; tile->image().save(&buffer, "png"); w.writeCharacters(QString::fromLatin1(buffer.data().toBase64())); w.writeEndElement(); w.writeEndElement(); } w.writeEndElement(); } } w.writeEndElement(); } void MapWriterPrivate::writeTileLayer(QXmlStreamWriter &w, const TileLayer *tileLayer) { w.writeStartElement(QLatin1String("layer")); writeLayerAttributes(w, tileLayer); writeProperties(w, tileLayer->properties()); QString encoding; QString compression; if (mLayerDataFormat == Map::Base64 || mLayerDataFormat == Map::Base64Gzip || mLayerDataFormat == Map::Base64Zlib) { encoding = QLatin1String("base64"); if (mLayerDataFormat == Map::Base64Gzip) compression = QLatin1String("gzip"); else if (mLayerDataFormat == Map::Base64Zlib) compression = QLatin1String("zlib"); } else if (mLayerDataFormat == Map::CSV) encoding = QLatin1String("csv"); w.writeStartElement(QLatin1String("data")); if (!encoding.isEmpty()) w.writeAttribute(QLatin1String("encoding"), encoding); if (!compression.isEmpty()) w.writeAttribute(QLatin1String("compression"), compression); if (mLayerDataFormat == Map::XML) { for (int y = 0; y < tileLayer->height(); ++y) { for (int x = 0; x < tileLayer->width(); ++x) { const unsigned gid = mGidMapper.cellToGid(tileLayer->cellAt(x, y)); w.writeStartElement(QLatin1String("tile")); w.writeAttribute(QLatin1String("gid"), QString::number(gid)); w.writeEndElement(); } } } else if (mLayerDataFormat == Map::CSV) { QString tileData; for (int y = 0; y < tileLayer->height(); ++y) { for (int x = 0; x < tileLayer->width(); ++x) { const unsigned gid = mGidMapper.cellToGid(tileLayer->cellAt(x, y)); tileData.append(QString::number(gid)); if (x != tileLayer->width() - 1 || y != tileLayer->height() - 1) tileData.append(QLatin1String(",")); } tileData.append(QLatin1String("\n")); } w.writeCharacters(QLatin1String("\n")); w.writeCharacters(tileData); } else { QByteArray tileData; tileData.reserve(tileLayer->height() * tileLayer->width() * 4); for (int y = 0; y < tileLayer->height(); ++y) { for (int x = 0; x < tileLayer->width(); ++x) { const unsigned gid = mGidMapper.cellToGid(tileLayer->cellAt(x, y)); tileData.append((char) (gid)); tileData.append((char) (gid >> 8)); tileData.append((char) (gid >> 16)); tileData.append((char) (gid >> 24)); } } if (mLayerDataFormat == Map::Base64Gzip) tileData = compress(tileData, Gzip); else if (mLayerDataFormat == Map::Base64Zlib) tileData = compress(tileData, Zlib); w.writeCharacters(QLatin1String("\n ")); w.writeCharacters(QString::fromLatin1(tileData.toBase64())); w.writeCharacters(QLatin1String("\n ")); } w.writeEndElement(); // w.writeEndElement(); // } void MapWriterPrivate::writeLayerAttributes(QXmlStreamWriter &w, const Layer *layer) { w.writeAttribute(QLatin1String("name"), layer->name()); w.writeAttribute(QLatin1String("width"), QString::number(layer->width())); w.writeAttribute(QLatin1String("height"), QString::number(layer->height())); const int x = layer->x(); const int y = layer->y(); const qreal opacity = layer->opacity(); if (x != 0) w.writeAttribute(QLatin1String("x"), QString::number(x)); if (y != 0) w.writeAttribute(QLatin1String("y"), QString::number(y)); if (!layer->isVisible()) w.writeAttribute(QLatin1String("visible"), QLatin1String("0")); if (opacity != qreal(1)) w.writeAttribute(QLatin1String("opacity"), QString::number(opacity)); } void MapWriterPrivate::writeObjectGroup(QXmlStreamWriter &w, const ObjectGroup *objectGroup) { w.writeStartElement(QLatin1String("objectgroup")); if (objectGroup->color().isValid()) w.writeAttribute(QLatin1String("color"), objectGroup->color().name()); writeLayerAttributes(w, objectGroup); writeProperties(w, objectGroup->properties()); foreach (const MapObject *mapObject, objectGroup->objects()) writeObject(w, mapObject); w.writeEndElement(); } class TileToPixelCoordinates { public: TileToPixelCoordinates(Map *map) { if (map->orientation() == Map::Isometric) { // Isometric needs special handling, since the pixel values are // based solely on the tile height. mMultiplierX = map->tileHeight(); mMultiplierY = map->tileHeight(); } else { mMultiplierX = map->tileWidth(); mMultiplierY = map->tileHeight(); } } QPoint operator() (qreal x, qreal y) const { return QPoint(qRound(x * mMultiplierX), qRound(y * mMultiplierY)); } private: int mMultiplierX; int mMultiplierY; }; void MapWriterPrivate::writeObject(QXmlStreamWriter &w, const MapObject *mapObject) { w.writeStartElement(QLatin1String("object")); const QString &name = mapObject->name(); const QString &type = mapObject->type(); if (!name.isEmpty()) w.writeAttribute(QLatin1String("name"), name); if (!type.isEmpty()) w.writeAttribute(QLatin1String("type"), type); if (mapObject->tile()) { const unsigned gid = mGidMapper.cellToGid(Cell(mapObject->tile())); w.writeAttribute(QLatin1String("gid"), QString::number(gid)); } // Convert from tile to pixel coordinates const ObjectGroup *objectGroup = mapObject->objectGroup(); const TileToPixelCoordinates toPixel(objectGroup->map()); QPoint pos = toPixel(mapObject->x(), mapObject->y()); QPoint size = toPixel(mapObject->width(), mapObject->height()); w.writeAttribute(QLatin1String("x"), QString::number(pos.x())); w.writeAttribute(QLatin1String("y"), QString::number(pos.y())); if (size.x() != 0) w.writeAttribute(QLatin1String("width"), QString::number(size.x())); if (size.y() != 0) w.writeAttribute(QLatin1String("height"), QString::number(size.y())); if (!mapObject->isVisible()) w.writeAttribute(QLatin1String("visible"), QLatin1String("0")); writeProperties(w, mapObject->properties()); const QPolygonF &polygon = mapObject->polygon(); if (!polygon.isEmpty()) { if (mapObject->shape() == MapObject::Polygon) w.writeStartElement(QLatin1String("polygon")); else w.writeStartElement(QLatin1String("polyline")); QString points; foreach (const QPointF &point, polygon) { const QPoint pos = toPixel(point.x(), point.y()); points.append(QString::number(pos.x())); points.append(QLatin1Char(',')); points.append(QString::number(pos.y())); points.append(QLatin1Char(' ')); } points.chop(1); w.writeAttribute(QLatin1String("points"), points); w.writeEndElement(); } if (mapObject->shape() == MapObject::Ellipse) w.writeEmptyElement(QLatin1String("ellipse")); w.writeEndElement(); } void MapWriterPrivate::writeImageLayer(QXmlStreamWriter &w, const ImageLayer *imageLayer) { w.writeStartElement(QLatin1String("imagelayer")); writeLayerAttributes(w, imageLayer); // Write the image element const QString &imageSource = imageLayer->imageSource(); if (!imageSource.isEmpty()) { w.writeStartElement(QLatin1String("image")); QString source = imageSource; if (!mUseAbsolutePaths) source = mMapDir.relativeFilePath(source); w.writeAttribute(QLatin1String("source"), source); const QColor transColor = imageLayer->transparentColor(); if (transColor.isValid()) w.writeAttribute(QLatin1String("trans"), transColor.name().mid(1)); w.writeEndElement(); } writeProperties(w, imageLayer->properties()); w.writeEndElement(); } void MapWriterPrivate::writeProperties(QXmlStreamWriter &w, const Properties &properties) { if (properties.isEmpty()) return; w.writeStartElement(QLatin1String("properties")); Properties::const_iterator it = properties.constBegin(); Properties::const_iterator it_end = properties.constEnd(); for (; it != it_end; ++it) { w.writeStartElement(QLatin1String("property")); w.writeAttribute(QLatin1String("name"), it.key()); const QString &value = it.value(); if (value.contains(QLatin1Char('\n'))) { w.writeCharacters(value); } else { w.writeAttribute(QLatin1String("value"), it.value()); } w.writeEndElement(); } w.writeEndElement(); } MapWriter::MapWriter() : d(new MapWriterPrivate) { } MapWriter::~MapWriter() { delete d; } void MapWriter::writeMap(const Map *map, QIODevice *device, const QString &path) { d->writeMap(map, device, path); } bool MapWriter::writeMap(const Map *map, const QString &fileName) { QFile file(fileName); if (!d->openFile(&file)) return false; writeMap(map, &file, QFileInfo(fileName).absolutePath()); if (file.error() != QFile::NoError) { d->mError = file.errorString(); return false; } return true; } void MapWriter::writeTileset(const Tileset *tileset, QIODevice *device, const QString &path) { d->writeTileset(tileset, device, path); } bool MapWriter::writeTileset(const Tileset *tileset, const QString &fileName) { QFile file(fileName); if (!d->openFile(&file)) return false; writeTileset(tileset, &file, QFileInfo(fileName).absolutePath()); if (file.error() != QFile::NoError) { d->mError = file.errorString(); return false; } return true; } QString MapWriter::errorString() const { return d->mError; } void MapWriter::setLayerDataFormat(Map::LayerDataFormat format) { d->mLayerDataFormat = format; } Map::LayerDataFormat MapWriter::layerDataFormat() const { return d->mLayerDataFormat; } void MapWriter::setDtdEnabled(bool enabled) { d->mDtdEnabled = enabled; } bool MapWriter::isDtdEnabled() const { return d->mDtdEnabled; } tiled-qt-0.9.1/src/libtiled/mapwriter.h000066400000000000000000000071001217502731700200110ustar00rootroot00000000000000/* * mapwriter.h * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2010, Dennis Honeyman * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAPWRITER_H #define MAPWRITER_H #include "map.h" #include "tiled_global.h" #include class QIODevice; namespace Tiled { class Map; class Tileset; namespace Internal { class MapWriterPrivate; } /** * A QXmlStreamWriter based writer for the TMX and TSX formats. */ class TILEDSHARED_EXPORT MapWriter { public: MapWriter(); ~MapWriter(); /** * Writes a TMX map to the given \a device. Optionally a \a path can * be given, which will be used to create relative references to external * images and tilesets. * * Error checking will need to be done on the \a device after calling this * function. */ void writeMap(const Map *map, QIODevice *device, const QString &path = QString()); /** * Writes a TMX map to the given \a fileName. * * Returns false and sets errorString() when reading failed. * \overload */ bool writeMap(const Map *map, const QString &fileName); /** * Writes a TSX tileset to the given \a device. Optionally a \a path can * be given, which will be used to create relative references to external * images. * * Error checking will need to be done on the \a device after calling this * function. */ void writeTileset(const Tileset *tileset, QIODevice *device, const QString &path = QString()); /** * Writes a TSX tileset to the given \a fileName. * * Returns false and sets errorString() when reading failed. * \overload */ bool writeTileset(const Tileset *tileset, const QString &fileName); /** * Returns the error message for the last occurred error. */ QString errorString() const; /** * Sets the format in which the tile layer data is stored. */ void setLayerDataFormat(Map::LayerDataFormat format); Map::LayerDataFormat layerDataFormat() const; /** * Sets whether the DTD reference is written when saving the map. */ void setDtdEnabled(bool enabled); bool isDtdEnabled() const; private: Internal::MapWriterPrivate *d; }; } // namespace Tiled #endif // MAPWRITER_H tiled-qt-0.9.1/src/libtiled/mapwriterinterface.h000066400000000000000000000060101217502731700216710ustar00rootroot00000000000000/* * mapwriterinterface.h * Copyright 2008-2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAPWRITERINTERFACE_H #define MAPWRITERINTERFACE_H #include #include class QString; namespace Tiled { class Map; /** * An interface to be implemented by map writers. A map writer implements * support for saving to a certain map format. * * Tiled provides a writer for its own .tmx map format through the * TmxMapWriter. Other writers can be provided by plugins. */ class MapWriterInterface { public: virtual ~MapWriterInterface() {} /** * Writes the given map to the given file name. * * @return true on success, false when an error * occurred. The error can be retrieved by errorString(). */ virtual bool write(const Map *map, const QString &fileName) = 0; /** * Returns name filters of this map writer, for multiple formats. */ virtual QStringList nameFilters() const { return QStringList(nameFilter()); } /** * Returns the error to be shown to the user if an error occured while * trying to write a map. */ virtual QString errorString() const = 0; protected: /** * Returns the name filter of this map writer. * * Protected because it should not be used outside the plugin since * plugins may expose multiple name filters. This thing exists only for * convenience for plugin writers since most plugins will have only one * name filter. */ virtual QString nameFilter() const { return QString(); } }; } // namespace Tiled Q_DECLARE_INTERFACE(Tiled::MapWriterInterface, "org.mapeditor.MapWriterInterface") #endif // MAPWRITERINTERFACE_H tiled-qt-0.9.1/src/libtiled/object.h000066400000000000000000000051411217502731700172500ustar00rootroot00000000000000/* * object.h * Copyright 2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OBJECT_H #define OBJECT_H #include "properties.h" namespace Tiled { /** * The base class for anything that can hold properties. */ class TILEDSHARED_EXPORT Object { public: /** * Virtual destructor. */ virtual ~Object() {} /** * Returns the properties of this object. */ const Properties &properties() const { return mProperties; } /** * Replaces all existing properties with a new set of properties. */ void setProperties(const Properties &properties) { mProperties = properties; } /** * Merges \a properties with the existing properties. Properties with the * same name will be overridden. * * \sa Properties::merge */ void mergeProperties(const Properties &properties) { mProperties.merge(properties); } /** * Returns the value of the object's \a name property. */ QString property(const QString &name) const { return mProperties.value(name); } /** * Sets the value of the object's \a name property to \a value. */ void setProperty(const QString &name, const QString &value) { mProperties.insert(name, value); } private: Properties mProperties; }; } // namespace Tiled #endif // OBJECT_H tiled-qt-0.9.1/src/libtiled/objectgroup.cpp000066400000000000000000000137571217502731700206740ustar00rootroot00000000000000/* * objectgroup.cpp * Copyright 2008, Roderic Morris * Copyright 2008-2009, Thorbjørn Lindeijer * Copyright 2009-2010, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "objectgroup.h" #include "layer.h" #include "map.h" #include "mapobject.h" #include "tile.h" #include "tileset.h" using namespace Tiled; ObjectGroup::ObjectGroup() : Layer(ObjectGroupType, QString(), 0, 0, 0, 0) { } ObjectGroup::ObjectGroup(const QString &name, int x, int y, int width, int height) : Layer(ObjectGroupType, name, x, y, width, height) { } ObjectGroup::~ObjectGroup() { qDeleteAll(mObjects); } void ObjectGroup::addObject(MapObject *object) { mObjects.append(object); object->setObjectGroup(this); } void ObjectGroup::insertObject(int index, MapObject *object) { mObjects.insert(index, object); object->setObjectGroup(this); } int ObjectGroup::removeObject(MapObject *object) { const int index = mObjects.indexOf(object); Q_ASSERT(index != -1); mObjects.removeAt(index); object->setObjectGroup(0); return index; } void ObjectGroup::removeObjectAt(int index) { MapObject *object = mObjects.takeAt(index); object->setObjectGroup(0); } QRectF ObjectGroup::objectsBoundingRect() const { QRectF boundingRect; foreach (const MapObject *object, mObjects) boundingRect = boundingRect.united(object->bounds()); return boundingRect; } bool ObjectGroup::isEmpty() const { return mObjects.isEmpty(); } QSet ObjectGroup::usedTilesets() const { QSet tilesets; foreach (const MapObject *object, mObjects) if (const Tile *tile = object->tile()) tilesets.insert(tile->tileset()); return tilesets; } bool ObjectGroup::referencesTileset(const Tileset *tileset) const { foreach (const MapObject *object, mObjects) { const Tile *tile = object->tile(); if (tile && tile->tileset() == tileset) return true; } return false; } void ObjectGroup::replaceReferencesToTileset(Tileset *oldTileset, Tileset *newTileset) { foreach (MapObject *object, mObjects) { const Tile *tile = object->tile(); if (tile && tile->tileset() == oldTileset) object->setTile(newTileset->tileAt(tile->id())); } } void ObjectGroup::resize(const QSize &size, const QPoint &offset) { Layer::resize(size, offset); foreach (MapObject *object, mObjects) { QPointF pos = object->position(); pos.rx() += offset.x(); pos.ry() += offset.y(); object->setPosition(pos); } } void ObjectGroup::offset(const QPoint &offset, const QRect &bounds, bool wrapX, bool wrapY) { foreach (MapObject *object, mObjects) { const QRectF objectBounds = object->bounds(); if (!QRectF(bounds).contains(objectBounds.center())) continue; QPointF newPos(objectBounds.left() + offset.x(), objectBounds.top () + offset.y()); if (wrapX && bounds.width() > 0) { while (newPos.x() + objectBounds.width() / 2 < qreal(bounds.left())) newPos.rx() += qreal(bounds.width()); while (newPos.x() + objectBounds.width() / 2 > qreal(bounds.left() + bounds.width())) newPos.rx() -= qreal(bounds.width()); } if (wrapY && bounds.height() > 0) { while (newPos.y() + objectBounds.height() / 2 < qreal(bounds.top())) newPos.ry() += qreal(bounds.height()); while (newPos.y() + objectBounds.height() / 2 > qreal(bounds.top() + bounds.height())) newPos.ry() -= qreal(bounds.height()); } object->setPosition(newPos); } } bool ObjectGroup::canMergeWith(Layer *other) const { return dynamic_cast(other) != 0; } Layer *ObjectGroup::mergedWith(Layer *other) const { Q_ASSERT(canMergeWith(other)); const ObjectGroup *og = static_cast(other); ObjectGroup *merged = static_cast(clone()); foreach (const MapObject *mapObject, og->objects()) merged->addObject(mapObject->clone()); return merged; } /** * Returns a duplicate of this ObjectGroup. * * \sa Layer::clone() */ Layer *ObjectGroup::clone() const { return initializeClone(new ObjectGroup(mName, mX, mY, mWidth, mHeight)); } ObjectGroup *ObjectGroup::initializeClone(ObjectGroup *clone) const { Layer::initializeClone(clone); foreach (const MapObject *object, mObjects) clone->addObject(object->clone()); clone->setColor(mColor); return clone; } tiled-qt-0.9.1/src/libtiled/objectgroup.h000066400000000000000000000121361217502731700203270ustar00rootroot00000000000000/* * objectgroup.h * Copyright 2008, Roderic Morris * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2009-2010, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef OBJECTGROUP_H #define OBJECTGROUP_H #include "tiled_global.h" #include "layer.h" #include #include #include namespace Tiled { class MapObject; /** * A group of objects on a map. */ class TILEDSHARED_EXPORT ObjectGroup : public Layer { public: /** * Default constructor. */ ObjectGroup(); /** * Constructor with some parameters. */ ObjectGroup(const QString &name, int x, int y, int width, int height); /** * Destructor. */ ~ObjectGroup(); /** * Returns a pointer to the list of objects in this object group. */ const QList &objects() const { return mObjects; } /** * Returns the number of objects in this object group. */ int objectCount() const { return mObjects.size(); } /** * Adds an object to this object group. */ void addObject(MapObject *object); /** * Inserts an object at the specified index. This is only used for undoing * the removal of an object at the moment, to make sure not to change the * saved order of the objects. */ void insertObject(int index, MapObject *object); /** * Removes an object from this object group. Ownership of the object is * transferred to the caller. * * @return the index at which the specified object was removed */ int removeObject(MapObject *object); /** * Removes the object at the given index. Ownership of the object is * transferred to the caller. * * This is faster than removeObject when you've already got the index. * * @param index the index at which to remove an object */ void removeObjectAt(int index); /** * Returns the bounding rect around all objects in this object group. */ QRectF objectsBoundingRect() const; /** * Returns whether this object group contains any objects. */ bool isEmpty() const; /** * Computes and returns the set of tilesets used by this object group. */ QSet usedTilesets() const; /** * Returns whether any tile objects in this object group reference tiles * in the given tileset. */ bool referencesTileset(const Tileset *tileset) const; /** * Replaces all references to tiles from \a oldTileset with tiles from * \a newTileset. */ void replaceReferencesToTileset(Tileset *oldTileset, Tileset *newTileset); /** * Resizes this object group to \a size, while shifting all objects by * \a offset tiles. * * \sa Layer::resize() */ virtual void resize(const QSize &size, const QPoint &offset); /** * Offsets all objects within the group, and optionally wraps them. The * object's center must be within \a bounds, and wrapping occurs if the * displaced center is out of the bounds. * * \sa Layer::offset() */ virtual void offset(const QPoint &offset, const QRect &bounds, bool wrapX, bool wrapY); bool canMergeWith(Layer *other) const; Layer *mergedWith(Layer *other) const; /** * Returns the color of the object group, or an invalid color if no color * is set. */ const QColor &color() const { return mColor; } /** * Sets the display color of the object group. */ void setColor(const QColor &color) { mColor = color; } Layer *clone() const; protected: ObjectGroup *initializeClone(ObjectGroup *clone) const; private: QList mObjects; QColor mColor; }; } // namespace Tiled Q_DECLARE_METATYPE(Tiled::ObjectGroup*) #endif // OBJECTGROUP_H tiled-qt-0.9.1/src/libtiled/orthogonalrenderer.cpp000066400000000000000000000335761217502731700222550ustar00rootroot00000000000000/* * orthogonalrenderer.cpp * Copyright 2009-2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "orthogonalrenderer.h" #include "map.h" #include "mapobject.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include using namespace Tiled; QSize OrthogonalRenderer::mapSize() const { return QSize(map()->width() * map()->tileWidth(), map()->height() * map()->tileHeight()); } QRect OrthogonalRenderer::boundingRect(const QRect &rect) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); return QRect(rect.x() * tileWidth, rect.y() * tileHeight, rect.width() * tileWidth, rect.height() * tileHeight); } QRectF OrthogonalRenderer::boundingRect(const MapObject *object) const { const QRectF bounds = object->bounds(); const QRectF rect(tileToPixelCoords(bounds.topLeft()), tileToPixelCoords(bounds.bottomRight())); QRectF boundingRect; if (object->tile()) { const QPointF bottomLeft = rect.topLeft(); const QPixmap &img = object->tile()->image(); boundingRect = QRectF(bottomLeft.x(), bottomLeft.y() - img.height(), img.width(), img.height()).adjusted(-1, -1, 1, 1); } else { // The -2 and +3 are to account for the pen width and shadow switch (object->shape()) { case MapObject::Ellipse: case MapObject::Rectangle: if (rect.isNull()) { boundingRect = rect.adjusted(-10 - 2, -10 - 2, 10 + 3, 10 + 3); } else { const int nameHeight = object->name().isEmpty() ? 0 : 15; boundingRect = rect.adjusted(-2, -nameHeight - 2, 3, 3); } break; case MapObject::Polygon: case MapObject::Polyline: { const QPointF &pos = object->position(); const QPolygonF polygon = object->polygon().translated(pos); QPolygonF screenPolygon = tileToPixelCoords(polygon); boundingRect = screenPolygon.boundingRect().adjusted(-2, -2, 3, 3); break; } } } return boundingRect; } QPainterPath OrthogonalRenderer::shape(const MapObject *object) const { QPainterPath path; if (object->tile()) { path.addRect(boundingRect(object)); } else { switch (object->shape()) { case MapObject::Rectangle: { const QRectF bounds = object->bounds(); const QRectF rect(tileToPixelCoords(bounds.topLeft()), tileToPixelCoords(bounds.bottomRight())); if (rect.isNull()) { path.addEllipse(rect.topLeft(), 20, 20); } else { path.addRoundedRect(rect, 10, 10); } break; } case MapObject::Polygon: case MapObject::Polyline: { const QPointF &pos = object->position(); const QPolygonF polygon = object->polygon().translated(pos); const QPolygonF screenPolygon = tileToPixelCoords(polygon); if (object->shape() == MapObject::Polygon) { path.addPolygon(screenPolygon); } else { for (int i = 1; i < screenPolygon.size(); ++i) { path.addPolygon(lineToPolygon(screenPolygon[i - 1], screenPolygon[i])); } path.setFillRule(Qt::WindingFill); } break; } case MapObject::Ellipse: { const QRectF bounds = object->bounds(); const QRectF rect(tileToPixelCoords(bounds.topLeft()), tileToPixelCoords(bounds.bottomRight())); if (rect.isNull()) { path.addEllipse(rect.topLeft(), 20, 20); } else { path.addEllipse(rect); } break; } } } return path; } void OrthogonalRenderer::drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); if (tileWidth <= 0 || tileHeight <= 0) return; const int startX = qMax(0, (int) (rect.x() / tileWidth) * tileWidth); const int startY = qMax(0, (int) (rect.y() / tileHeight) * tileHeight); const int endX = qMin((int) std::ceil(rect.right()), map()->width() * tileWidth + 1); const int endY = qMin((int) std::ceil(rect.bottom()), map()->height() * tileHeight + 1); gridColor.setAlpha(128); QPen gridPen(gridColor, 0); gridPen.setDashPattern(QVector() << 2 << 2); if (startY < endY) { gridPen.setDashOffset(startY); painter->setPen(gridPen); for (int x = startX; x < endX; x += tileWidth) painter->drawLine(x, startY, x, endY - 1); } if (startX < endX) { gridPen.setDashOffset(startX); painter->setPen(gridPen); for (int y = startY; y < endY; y += tileHeight) painter->drawLine(startX, y, endX - 1, y); } } void OrthogonalRenderer::drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed) const { QTransform savedTransform = painter->transform(); const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); const QPointF layerPos(layer->x() * tileWidth, layer->y() * tileHeight); painter->translate(layerPos); int startX = 0; int startY = 0; int endX = layer->width(); int endY = layer->height(); if (!exposed.isNull()) { QMargins drawMargins = layer->drawMargins(); drawMargins.setTop(drawMargins.top() - tileHeight); drawMargins.setRight(drawMargins.right() - tileWidth); QRectF rect = exposed.adjusted(-drawMargins.right(), -drawMargins.bottom(), drawMargins.left(), drawMargins.top()); rect.translate(-layerPos); startX = qMax((int) rect.x() / tileWidth, 0); startY = qMax((int) rect.y() / tileHeight, 0); endX = qMin((int) std::ceil(rect.right()) / tileWidth + 1, endX); endY = qMin((int) std::ceil(rect.bottom()) / tileHeight + 1, endY); } QTransform baseTransform = painter->transform(); for (int y = startY; y < endY; ++y) { for (int x = startX; x < endX; ++x) { const Cell &cell = layer->cellAt(x, y); if (cell.isEmpty()) continue; const QPixmap &img = cell.tile->image(); const QPoint offset = cell.tile->tileset()->tileOffset(); qreal m11 = 1; // Horizontal scaling factor qreal m12 = 0; // Vertical shearing factor qreal m21 = 0; // Horizontal shearing factor qreal m22 = 1; // Vertical scaling factor qreal dx = offset.x() + x * tileWidth; qreal dy = offset.y() + (y + 1) * tileHeight - img.height(); if (cell.flippedAntiDiagonally) { // Use shearing to swap the X/Y axis m11 = 0; m12 = 1; m21 = 1; m22 = 0; // Compensate for the swap of image dimensions dy += img.height() - img.width(); } if (cell.flippedHorizontally) { m11 = -m11; m21 = -m21; dx += cell.flippedAntiDiagonally ? img.height() : img.width(); } if (cell.flippedVertically) { m12 = -m12; m22 = -m22; dy += cell.flippedAntiDiagonally ? img.width() : img.height(); } const QTransform transform(m11, m12, m21, m22, dx, dy); painter->setTransform(transform * baseTransform); painter->drawPixmap(0, 0, img); } } painter->setTransform(savedTransform); } void OrthogonalRenderer::drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const { foreach (const QRect &r, region.rects()) { const QRectF toFill = QRectF(boundingRect(r)).intersected(exposed); if (!toFill.isEmpty()) painter->fillRect(toFill, color); } } void OrthogonalRenderer::drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const { painter->save(); const QRectF bounds = object->bounds(); QRectF rect(tileToPixelCoords(bounds.topLeft()), tileToPixelCoords(bounds.bottomRight())); painter->translate(rect.topLeft()); rect.moveTopLeft(QPointF(0, 0)); if (object->tile()) { const QPixmap &img = object->tile()->image(); const QPoint paintOrigin(0, -img.height()); painter->drawPixmap(paintOrigin, img); if (testFlag(ShowTileObjectOutlines)) { QPen pen(Qt::SolidLine); pen.setWidth(0); painter->setPen(pen); painter->drawRect(QRect(paintOrigin, img.size())); pen.setStyle(Qt::DotLine); pen.setColor(color); painter->setPen(pen); painter->drawRect(QRect(paintOrigin, img.size())); } } else { const QPen linePen(color, 2); const QPen shadowPen(Qt::black, 2); QColor brushColor = color; brushColor.setAlpha(50); const QBrush fillBrush(brushColor); painter->setRenderHint(QPainter::Antialiasing); switch (object->shape()) { case MapObject::Rectangle: { if (rect.isNull()) rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)); const QFontMetrics fm = painter->fontMetrics(); QString name = fm.elidedText(object->name(), Qt::ElideRight, rect.width() + 2); // Draw the shadow painter->setPen(shadowPen); painter->drawRect(rect.translated(QPointF(1, 1))); if (!name.isEmpty()) painter->drawText(QPoint(1, -5 + 1), name); painter->setPen(linePen); painter->setBrush(fillBrush); painter->drawRect(rect); if (!name.isEmpty()) painter->drawText(QPoint(0, -5), name); break; } case MapObject::Polyline: { QPolygonF screenPolygon = tileToPixelCoords(object->polygon()); painter->setPen(shadowPen); painter->drawPolyline(screenPolygon.translated(1, 1)); painter->setPen(linePen); painter->setBrush(fillBrush); painter->drawPolyline(screenPolygon); break; } case MapObject::Polygon: { QPolygonF screenPolygon = tileToPixelCoords(object->polygon()); painter->setPen(shadowPen); painter->drawPolygon(screenPolygon.translated(1, 1)); painter->setPen(linePen); painter->setBrush(fillBrush); painter->drawPolygon(screenPolygon); break; } case MapObject::Ellipse: { if (rect.isNull()) rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)); const QFontMetrics fm = painter->fontMetrics(); QString name = fm.elidedText(object->name(), Qt::ElideRight, rect.width() + 2); // Draw the shadow painter->setPen(shadowPen); painter->drawEllipse(rect.translated(QPointF(1, 1))); if (!name.isEmpty()) painter->drawText(QPoint(1, -5 + 1), name); painter->setPen(linePen); painter->setBrush(fillBrush); painter->drawEllipse(rect); if (!name.isEmpty()) painter->drawText(QPoint(0, -5), name); break; } } } painter->restore(); } QPointF OrthogonalRenderer::pixelToTileCoords(qreal x, qreal y) const { return QPointF(x / map()->tileWidth(), y / map()->tileHeight()); } QPointF OrthogonalRenderer::tileToPixelCoords(qreal x, qreal y) const { return QPointF(x * map()->tileWidth(), y * map()->tileHeight()); } tiled-qt-0.9.1/src/libtiled/orthogonalrenderer.h000066400000000000000000000053121217502731700217050ustar00rootroot00000000000000/* * orthogonalrenderer.h * Copyright 2009-2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ORTHOGONALRENDERER_H #define ORTHOGONALRENDERER_H #include "maprenderer.h" namespace Tiled { /** * The orthogonal map renderer. This is the most basic map renderer, * dealing with maps that use rectangular tiles. */ class TILEDSHARED_EXPORT OrthogonalRenderer : public MapRenderer { public: OrthogonalRenderer(const Map *map) : MapRenderer(map) {} QSize mapSize() const; QRect boundingRect(const QRect &rect) const; QRectF boundingRect(const MapObject *object) const; QPainterPath shape(const MapObject *object) const; void drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor) const; void drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed = QRectF()) const; void drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const; void drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const; QPointF pixelToTileCoords(qreal x, qreal y) const; using MapRenderer::tileToPixelCoords; QPointF tileToPixelCoords(qreal x, qreal y) const; }; } // namespace Tiled #endif // ORTHOGONALRENDERER_H tiled-qt-0.9.1/src/libtiled/properties.cpp000066400000000000000000000033251217502731700205330ustar00rootroot00000000000000/* * properties.cpp * Copyright 2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "properties.h" using namespace Tiled; void Properties::merge(const Properties &other) { // Based on QMap::unite, but using insert instead of insertMulti const_iterator it = other.constEnd(); const const_iterator b = other.constBegin(); while (it != b) { --it; insert(it.key(), it.value()); } } tiled-qt-0.9.1/src/libtiled/properties.h000066400000000000000000000032421217502731700201760ustar00rootroot00000000000000/* * properties.h * Copyright 2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef PROPERTIES_H #define PROPERTIES_H #include "tiled_global.h" #include #include namespace Tiled { class TILEDSHARED_EXPORT Properties : public QMap { public: void merge(const Properties &other); }; } // namespace Tiled #endif // PROPERTIES_H tiled-qt-0.9.1/src/libtiled/staggeredrenderer.cpp000066400000000000000000000277701217502731700220450ustar00rootroot00000000000000/* * staggeredrenderer.cpp * Copyright 2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "staggeredrenderer.h" #include "map.h" #include "mapobject.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include using namespace Tiled; QSize StaggeredRenderer::mapSize() const { // The map size is the same regardless of whether rows or columns are // shifted or whether they are the odd or the even indexes const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); return QSize(tileWidth / 2 + map()->width() * tileWidth, (map()->height() + 1) * (tileHeight / 2)); } QRect StaggeredRenderer::boundingRect(const QRect &rect) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); QPoint topLeft = tileToPixelCoords(rect.topLeft()).toPoint(); int width = rect.width() * tileWidth; int height = (tileHeight / 2) * (rect.height() + 1); if (rect.height() > 1) { width += tileWidth / 2; if (rect.y() % 2) topLeft.rx() -= tileWidth / 2; } return QRect(topLeft.x(), topLeft.y(), width, height); } QRectF StaggeredRenderer::boundingRect(const MapObject *object) const { // TODO return boundingRect(object->bounds().toAlignedRect()); } QPainterPath StaggeredRenderer::shape(const MapObject *object) const { // TODO QPainterPath result; result.addRect(boundingRect(object)); return result; } void StaggeredRenderer::drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); int startX = 0; int startY = 0; int endX = map()->width(); int endY = (map()->height() + 1) / 2; startX = qMax((int) rect.x() / tileWidth, 0); startY = qMax((int) rect.y() / tileHeight, 0); endX = qMin((int) std::ceil(rect.right()) / tileWidth + 1, endX); endY = qMin((int) std::ceil(rect.bottom()) / tileHeight + 1, endY); gridColor.setAlpha(128); QPen gridPen(gridColor, 0); gridPen.setDashPattern(QVector() << 2 << 2); painter->setPen(gridPen); for (int y = startY; y < endY; ++y) { for (int x = startX; x < endX; ++x) { const QPoint topRight = QPoint(x * tileWidth, y * tileHeight); QPolygon line; line << QPoint(topRight.x() + tileWidth / 2, topRight.y()); line << QPoint(topRight.x() + tileWidth, topRight.y() + tileHeight / 2); line << QPoint(topRight.x() + tileWidth / 2, topRight.y() + tileHeight); line << QPoint(topRight.x(), topRight.y() + tileHeight / 2); line << QPoint(topRight.x() + tileWidth / 2, topRight.y()); painter->drawPolyline(line); } } } void StaggeredRenderer::drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); QRect rect = exposed.toAlignedRect(); if (rect.isNull()) rect = boundingRect(layer->bounds()); QMargins drawMargins = layer->drawMargins(); drawMargins.setRight(drawMargins.right() - tileWidth); rect.adjust(-drawMargins.right(), -drawMargins.bottom(), drawMargins.left(), drawMargins.top()); // Determine the tile and pixel coordinates to start at QPoint startTile = pixelToTileCoords(rect.x(), rect.y()).toPoint(); // Compensate for the layer position startTile -= layer->position(); QPoint startPos = tileToPixelCoords(startTile + layer->position()).toPoint(); /* Determine in which half of the tile the top-left corner of the area we * need to draw is. If we're in the upper half, we need to start one row * up due to those tiles being visible as well. How we go up one row * depends on whether we're in the left or right half of the tile. */ const bool inUpperHalf = startPos.y() - rect.y() > tileHeight / 2; const bool inLeftHalf = rect.x() - startPos.x() < tileWidth / 2; if (inUpperHalf) startTile.ry()--; if (inLeftHalf) startTile.rx()--; startTile.setX(qMax(0, startTile.x())); startTile.setY(qMax(0, startTile.y())); startPos = tileToPixelCoords(startTile + layer->position()).toPoint(); startPos.ry() += tileHeight; // Odd row shifting is applied in the rendering loop, so un-apply it here if ((startTile.y() + layer->y()) % 2) startPos.rx() -= tileWidth / 2; QTransform baseTransform = painter->transform(); for (; startPos.y() < rect.bottom() && startTile.y() < layer->height(); startTile.ry()++) { QPoint rowTile = startTile; QPoint rowPos = startPos; if ((startTile.y() + layer->y()) % 2) rowPos.rx() += tileWidth / 2; for (; rowPos.x() < rect.right() && rowTile.x() < layer->width(); rowTile.rx()++) { const Cell &cell = layer->cellAt(rowTile); if (cell.isEmpty()) { rowPos.rx() += tileWidth; continue; } const QPixmap &img = cell.tile->image(); const QPoint offset = cell.tile->tileset()->tileOffset(); qreal m11 = 1; // Horizontal scaling factor qreal m12 = 0; // Vertical shearing factor qreal m21 = 0; // Horizontal shearing factor qreal m22 = 1; // Vertical scaling factor qreal dx = offset.x() + rowPos.x(); qreal dy = offset.y() + rowPos.y() - img.height(); if (cell.flippedAntiDiagonally) { // Use shearing to swap the X/Y axis m11 = 0; m12 = 1; m21 = 1; m22 = 0; // Compensate for the swap of image dimensions dy += img.height() - img.width(); } if (cell.flippedHorizontally) { m11 = -m11; m21 = -m21; dx += cell.flippedAntiDiagonally ? img.height() : img.width(); } if (cell.flippedVertically) { m12 = -m12; m22 = -m22; dy += cell.flippedAntiDiagonally ? img.width() : img.height(); } const QTransform transform(m11, m12, m21, m22, dx, dy); painter->setTransform(transform * baseTransform); painter->drawPixmap(0, 0, img); rowPos.rx() += tileWidth; } startPos.ry() += tileHeight / 2; } painter->setTransform(baseTransform); } void StaggeredRenderer::drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const { painter->setBrush(color); painter->setPen(Qt::NoPen); foreach (const QRect &r, region.rects()) { for (int y = r.top(); y <= r.bottom(); ++y) { for (int x = r.left(); x <= r.right(); ++x) { const QPolygonF polygon = tileToPolygon(x, y); if (QRectF(polygon.boundingRect()).intersects(exposed)) painter->drawConvexPolygon(polygon); } } } } void StaggeredRenderer::drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const { Q_UNUSED(painter) Q_UNUSED(object) Q_UNUSED(color) // TODO } /** * Converts pixel to tile coordinates. Sub-tile return values are not * supported by this renderer. */ QPointF StaggeredRenderer::pixelToTileCoords(qreal x, qreal y) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); const int halfTileHeight = tileHeight / 2; const qreal ratio = (qreal) tileHeight / tileWidth; // Start with the coordinates of a grid-aligned tile const int tileX = std::floor(x / tileWidth); const int tileY = (int) std::floor(y / tileHeight) * 2; // Relative x and y position on the base square of the grid-aligned tile const qreal relX = x - tileX * tileWidth; const qreal relY = y - (tileY / 2) * tileHeight; // Check whether the cursor is in any of the corners (neighboring tiles) if (halfTileHeight - relX * ratio > relY) return topLeft(tileX, tileY); if (-halfTileHeight + relX * ratio > relY) return topRight(tileX, tileY); if (halfTileHeight + relX * ratio < relY) return bottomLeft(tileX, tileY); if (halfTileHeight * 3 - relX * ratio < relY) return bottomRight(tileX, tileY); return QPoint(tileX, tileY); } /** * Converts tile to pixel coordinates. Sub-tile return values are not * supported by this renderer. */ QPointF StaggeredRenderer::tileToPixelCoords(qreal x, qreal y) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); int pixelX = int(x) * tileWidth + qAbs(int(y) % 2) * (tileWidth / 2); int pixelY = int(y) * (tileHeight / 2); return QPointF(pixelX, pixelY); } QPoint StaggeredRenderer::topLeft(int x, int y) const { if (y % 2) return QPoint(x, y - 1); else return QPoint(x - 1, y - 1); } QPoint StaggeredRenderer::topRight(int x, int y) const { if (y % 2) return QPoint(x + 1, y - 1); else return QPoint(x, y - 1); } QPoint StaggeredRenderer::bottomLeft(int x, int y) const { if (y % 2) return QPoint(x, y + 1); else return QPoint(x - 1, y + 1); } QPoint StaggeredRenderer::bottomRight(int x, int y) const { if (y % 2) return QPoint(x + 1, y + 1); else return QPoint(x, y + 1); } QPolygonF StaggeredRenderer::tileToPolygon(int x, int y) const { const int tileWidth = map()->tileWidth(); const int tileHeight = map()->tileHeight(); const QPointF topRight = tileToPixelCoords(x, y); QPolygonF polygon; polygon << QPointF(topRight.x() + tileWidth / 2, topRight.y()); polygon << QPointF(topRight.x() + tileWidth, topRight.y() + tileHeight / 2); polygon << QPointF(topRight.x() + tileWidth / 2, topRight.y() + tileHeight); polygon << QPointF(topRight.x(), topRight.y() + tileHeight / 2); return polygon; } tiled-qt-0.9.1/src/libtiled/staggeredrenderer.h000066400000000000000000000103551217502731700215010ustar00rootroot00000000000000/* * staggeredrenderer.h * Copyright 2011, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef STAGGEREDRENDERER_H #define STAGGEREDRENDERER_H #include "maprenderer.h" namespace Tiled { /** * A staggered isometric renderer. * * This renderer is meant to be used with isometric tiles, but rather than * doing a true isometric projection the tiles are shifted in order to fit * together. This has the advantage that the map has a rectangular shape. * * Only one variation of staggered map rendering is supported at the moment, * namely shifting the uneven rows to the right. * * Objects are handled the same way as the OrthogonalRenderer, though they * snap at half the tile width and height. * * At the moment, several Tiled features will not work correctly with this * renderer. This is due to the way the shifting messes with the coordinate * system. List of known issues: * * Fill tool: * * Doesn't work properly cause its assumptions about neighboring are * broken by this renderer. * * Stamp tool: * * A stamp only makes sense if it's placed at an even y offset from * where it was created. When moved by an uneven y offset, the rows * that are shifted swap, messing up the stamp. * * The circle and line drawing modes of the stamp tool won't work * properly due to the irregular coordinate system. * * Rectangular select tool: * * This one will appear to behave somewhat erratic. * * Map resize and offset actions: * * Similar problem as with stamps when offsetting at an uneven y offset. * */ class TILEDSHARED_EXPORT StaggeredRenderer : public MapRenderer { public: StaggeredRenderer(const Map *map) : MapRenderer(map) {} QSize mapSize() const; QRect boundingRect(const QRect &rect) const; QRectF boundingRect(const MapObject *object) const; QPainterPath shape(const MapObject *object) const; void drawGrid(QPainter *painter, const QRectF &rect, QColor gridColor) const; void drawTileLayer(QPainter *painter, const TileLayer *layer, const QRectF &exposed = QRectF()) const; void drawTileSelection(QPainter *painter, const QRegion ®ion, const QColor &color, const QRectF &exposed) const; void drawMapObject(QPainter *painter, const MapObject *object, const QColor &color) const; using MapRenderer::pixelToTileCoords; QPointF pixelToTileCoords(qreal x, qreal y) const; using MapRenderer::tileToPixelCoords; QPointF tileToPixelCoords(qreal x, qreal y) const; // Functions specific to this type of renderer QPoint topLeft(int x, int y) const; QPoint topRight(int x, int y) const; QPoint bottomLeft(int x, int y) const; QPoint bottomRight(int x, int y) const; QPolygonF tileToPolygon(int x, int y) const; }; } // namespace Tiled #endif // STAGGEREDRENDERER_H tiled-qt-0.9.1/src/libtiled/terrain.h000066400000000000000000000072741217502731700174570ustar00rootroot00000000000000/* * terrain.h * Copyright 2012, Manu Evans * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TERRAIN_H #define TERRAIN_H #include "object.h" #include "tileset.h" #include #include #include #include namespace Tiled { class Tile; /** * Represents a terrain type. */ class TILEDSHARED_EXPORT Terrain : public Object { public: Terrain(int id, Tileset *tileset, QString name, int imageTileId): mId(id), mTileset(tileset), mName(name), mImageTileId(imageTileId) { } /** * Returns ID of this terrain type. */ int id() const { return mId; } /** * Sets the ID of this terrain type. */ void setId(int id) { mId = id; } /** * Returns the tileset this terrain type belongs to. */ Tileset *tileset() const { return mTileset; } /** * Returns the name of this terrain type. */ QString name() const { return mName; } /** * Sets the name of this terrain type. */ void setName(const QString &name) { mName = name; } /** * Returns the index of the tile that visually represents this terrain type. */ int imageTileId() const { return mImageTileId; } /** * Sets the index of the tile that visually represents this terrain type. */ void setImageTileId(int imageTileId) { mImageTileId = imageTileId; } /** * Returns a Tile that represents this terrain type in the terrain palette. */ Tile *imageTile() const { return mImageTileId >= 0 ? mTileset->tileAt(mImageTileId) : 0; } /** * Returns the transition penalty(/distance) from this terrain type to another terrain type. */ int transitionDistance(int targetTerrainType) const { return mTransitionDistance[targetTerrainType + 1]; } /** * Sets the transition penalty(/distance) from this terrain type to another terrain type. */ void setTransitionDistance(int targetTerrainType, int distance) { mTransitionDistance[targetTerrainType + 1] = distance; } /** * Returns the array of terrain penalties(/distances). */ void setTransitionDistances(const QVector &transitionDistances) { mTransitionDistance = transitionDistances; } private: int mId; Tileset *mTileset; QString mName; int mImageTileId; QVector mTransitionDistance; }; } // namespace Tiled Q_DECLARE_METATYPE(Tiled::Terrain*) #endif // TERRAIN_H tiled-qt-0.9.1/src/libtiled/tile.cpp000066400000000000000000000033051217502731700172720ustar00rootroot00000000000000/* * tile.cpp * Copyright 2012, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "tile.h" #include "tileset.h" using namespace Tiled; Terrain *Tile::terrainAtCorner(int corner) const { return mTileset->terrain(cornerTerrainId(corner)); } void Tile::setTerrain(unsigned terrain) { if (mTerrain == terrain) return; mTerrain = terrain; mTileset->markTerrainDistancesDirty(); } tiled-qt-0.9.1/src/libtiled/tile.h000066400000000000000000000100631217502731700167360ustar00rootroot00000000000000/* * tile.h * Copyright 2008-2012, Thorbjørn Lindeijer * Copyright 2009, Edward Hutchins * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TILE_H #define TILE_H #include "object.h" #include namespace Tiled { class Terrain; class Tileset; /** * Returns the given \a terrain with the \a corner modified to \a terrainId. */ inline unsigned setTerrainCorner(unsigned terrain, int corner, int terrainId) { unsigned mask = 0xFF << (3 - corner) * 8; unsigned insert = terrainId << (3 - corner) * 8; return (terrain & ~mask) | (insert & mask); } class TILEDSHARED_EXPORT Tile : public Object { public: Tile(const QPixmap &image, int id, Tileset *tileset): mId(id), mTileset(tileset), mImage(image), mTerrain(-1), mTerrainProbability(-1.f) {} /** * Returns ID of this tile within its tileset. */ int id() const { return mId; } /** * Returns the tileset that this tile is part of. */ Tileset *tileset() const { return mTileset; } /** * Returns the image of this tile. */ const QPixmap &image() const { return mImage; } /** * Sets the image of this tile. */ void setImage(const QPixmap &image) { mImage = image; } /** * Returns the width of this tile. */ int width() const { return mImage.width(); } /** * Returns the height of this tile. */ int height() const { return mImage.height(); } /** * Returns the size of this tile. */ QSize size() const { return mImage.size(); } /** * Returns the Terrain of a given corner. */ Terrain *terrainAtCorner(int corner) const; /** * Returns the terrain id at a given corner. */ int cornerTerrainId(int corner) const { unsigned t = (terrain() >> (3 - corner)*8) & 0xFF; return t == 0xFF ? -1 : (int)t; } /** * Set the terrain type of a given corner. */ void setCornerTerrain(int corner, int terrainId) { setTerrain(setTerrainCorner(mTerrain, corner, terrainId)); } /** * Returns the terrain for each corner of this tile. */ unsigned terrain() const { return mTerrain; } /** * Set the terrain for each corner of the tile. */ void setTerrain(unsigned terrain); /** * Returns the probability of this terrain type appearing while painting (0-100%). */ float terrainProbability() const { return mTerrainProbability; } /** * Set the probability of this terrain type appearing while painting (0-100%). */ void setTerrainProbability(float probability) { mTerrainProbability = probability; } private: int mId; Tileset *mTileset; QPixmap mImage; unsigned mTerrain; float mTerrainProbability; }; } // namespace Tiled #endif // TILE_H tiled-qt-0.9.1/src/libtiled/tiled_global.h000066400000000000000000000031441217502731700204240ustar00rootroot00000000000000/* * tiled_global.h * Copyright 2010, Thorbjørn Lindeijer * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TILED_GLOBAL_H #define TILED_GLOBAL_H #include #if defined(TILED_LIBRARY) # define TILEDSHARED_EXPORT Q_DECL_EXPORT #else # define TILEDSHARED_EXPORT Q_DECL_IMPORT #endif #endif // TILED_GLOBAL_H tiled-qt-0.9.1/src/libtiled/tilelayer.cpp000066400000000000000000000325031217502731700203310ustar00rootroot00000000000000/* * tilelayer.cpp * Copyright 2008-2011, Thorbjørn Lindeijer * Copyright 2009, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "tilelayer.h" #include "map.h" #include "tile.h" #include "tileset.h" using namespace Tiled; TileLayer::TileLayer(const QString &name, int x, int y, int width, int height): Layer(TileLayerType, name, x, y, width, height), mMaxTileSize(0, 0), mGrid(width * height) { Q_ASSERT(width >= 0); Q_ASSERT(height >= 0); } QRegion TileLayer::region() const { QRegion region; for (int y = 0; y < mHeight; ++y) { for (int x = 0; x < mWidth; ++x) { if (!cellAt(x, y).isEmpty()) { const int rangeStart = x; for (++x; x <= mWidth; ++x) { if (x == mWidth || cellAt(x, y).isEmpty()) { const int rangeEnd = x; region += QRect(rangeStart + mX, y + mY, rangeEnd - rangeStart, 1); break; } } } } } return region; } static QSize maxSize(const QSize &a, const QSize &b) { return QSize(qMax(a.width(), b.width()), qMax(a.height(), b.height())); } static QMargins maxMargins(const QMargins &a, const QMargins &b) { return QMargins(qMax(a.left(), b.left()), qMax(a.top(), b.top()), qMax(a.right(), b.right()), qMax(a.bottom(), b.bottom())); } void TileLayer::setCell(int x, int y, const Cell &cell) { Q_ASSERT(contains(x, y)); if (cell.tile) { QSize size = cell.tile->size(); if (cell.flippedAntiDiagonally) size.transpose(); const QPoint offset = cell.tile->tileset()->tileOffset(); mMaxTileSize = maxSize(size, mMaxTileSize); mOffsetMargins = maxMargins(QMargins(-offset.x(), -offset.y(), offset.x(), offset.y()), mOffsetMargins); if (mMap) mMap->adjustDrawMargins(drawMargins()); } mGrid[x + y * mWidth] = cell; } TileLayer *TileLayer::copy(const QRegion ®ion) const { const QRegion area = region.intersected(QRect(0, 0, width(), height())); const QRect bounds = region.boundingRect(); const QRect areaBounds = area.boundingRect(); const int offsetX = qMax(0, areaBounds.x() - bounds.x()); const int offsetY = qMax(0, areaBounds.y() - bounds.y()); TileLayer *copied = new TileLayer(QString(), 0, 0, bounds.width(), bounds.height()); foreach (const QRect &rect, area.rects()) for (int x = rect.left(); x <= rect.right(); ++x) for (int y = rect.top(); y <= rect.bottom(); ++y) copied->setCell(x - areaBounds.x() + offsetX, y - areaBounds.y() + offsetY, cellAt(x, y)); return copied; } void TileLayer::merge(const QPoint &pos, const TileLayer *layer) { // Determine the overlapping area QRect area = QRect(pos, QSize(layer->width(), layer->height())); area &= QRect(0, 0, width(), height()); for (int y = area.top(); y <= area.bottom(); ++y) { for (int x = area.left(); x <= area.right(); ++x) { const Cell &cell = layer->cellAt(x - area.left(), y - area.top()); if (!cell.isEmpty()) setCell(x, y, cell); } } } void TileLayer::setCells(int x, int y, TileLayer *layer, const QRegion &mask) { // Determine the overlapping area QRegion area = QRect(x, y, layer->width(), layer->height()); area &= QRect(0, 0, width(), height()); if (!mask.isEmpty()) area &= mask; foreach (const QRect &rect, area.rects()) for (int _x = rect.left(); _x <= rect.right(); ++_x) for (int _y = rect.top(); _y <= rect.bottom(); ++_y) setCell(_x, _y, layer->cellAt(_x - x, _y - y)); } void TileLayer::erase(const QRegion &area) { const Cell emptyCell; foreach (const QRect &rect, area.rects()) for (int x = rect.left(); x <= rect.right(); ++x) for (int y = rect.top(); y <= rect.bottom(); ++y) setCell(x, y, emptyCell); } void TileLayer::flip(FlipDirection direction) { QVector newGrid(mWidth * mHeight); Q_ASSERT(direction == FlipHorizontally || direction == FlipVertically); for (int y = 0; y < mHeight; ++y) { for (int x = 0; x < mWidth; ++x) { Cell &dest = newGrid[x + y * mWidth]; if (direction == FlipHorizontally) { const Cell &source = cellAt(mWidth - x - 1, y); dest = source; dest.flippedHorizontally = !source.flippedHorizontally; } else if (direction == FlipVertically) { const Cell &source = cellAt(x, mHeight - y - 1); dest = source; dest.flippedVertically = !source.flippedVertically; } } } mGrid = newGrid; } void TileLayer::rotate(RotateDirection direction) { static const char rotateRightMask[8] = { 5, 4, 1, 0, 7, 6, 3, 2 }; static const char rotateLeftMask[8] = { 3, 2, 7, 6, 1, 0, 5, 4 }; const char (&rotateMask)[8] = (direction == RotateRight) ? rotateRightMask : rotateLeftMask; int newWidth = mHeight; int newHeight = mWidth; QVector newGrid(newWidth * newHeight); for (int y = 0; y < mHeight; ++y) { for (int x = 0; x < mWidth; ++x) { const Cell &source = cellAt(x, y); Cell dest = source; unsigned char mask = (dest.flippedHorizontally << 2) | (dest.flippedVertically << 1) | (dest.flippedAntiDiagonally << 0); mask = rotateMask[mask]; dest.flippedHorizontally = (mask & 4) != 0; dest.flippedVertically = (mask & 2) != 0; dest.flippedAntiDiagonally = (mask & 1) != 0; if (direction == RotateRight) newGrid[x * newWidth + (mHeight - y - 1)] = dest; else newGrid[(mWidth - x - 1) * newWidth + y] = dest; } } std::swap(mMaxTileSize.rwidth(), mMaxTileSize.rheight()); mWidth = newWidth; mHeight = newHeight; mGrid = newGrid; } QSet TileLayer::usedTilesets() const { QSet tilesets; for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) if (const Tile *tile = mGrid.at(i).tile) tilesets.insert(tile->tileset()); return tilesets; } bool TileLayer::referencesTileset(const Tileset *tileset) const { for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) { const Tile *tile = mGrid.at(i).tile; if (tile && tile->tileset() == tileset) return true; } return false; } QRegion TileLayer::tilesetReferences(Tileset *tileset) const { QRegion region; for (int y = 0; y < mHeight; ++y) for (int x = 0; x < mWidth; ++x) if (const Tile *tile = cellAt(x, y).tile) if (tile->tileset() == tileset) region += QRegion(x + mX, y + mY, 1, 1); return region; } void TileLayer::removeReferencesToTileset(Tileset *tileset) { for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) { const Tile *tile = mGrid.at(i).tile; if (tile && tile->tileset() == tileset) mGrid.replace(i, Cell()); } } void TileLayer::replaceReferencesToTileset(Tileset *oldTileset, Tileset *newTileset) { for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) { const Tile *tile = mGrid.at(i).tile; if (tile && tile->tileset() == oldTileset) mGrid[i].tile = newTileset->tileAt(tile->id()); } } void TileLayer::resize(const QSize &size, const QPoint &offset) { if (this->size() == size && offset.isNull()) return; QVector newGrid(size.width() * size.height()); // Copy over the preserved part const int startX = qMax(0, -offset.x()); const int startY = qMax(0, -offset.y()); const int endX = qMin(mWidth, size.width() - offset.x()); const int endY = qMin(mHeight, size.height() - offset.y()); for (int y = startY; y < endY; ++y) { for (int x = startX; x < endX; ++x) { const int index = x + offset.x() + (y + offset.y()) * size.width(); newGrid[index] = cellAt(x, y); } } mGrid = newGrid; Layer::resize(size, offset); } void TileLayer::offset(const QPoint &offset, const QRect &bounds, bool wrapX, bool wrapY) { QVector newGrid(mWidth * mHeight); for (int y = 0; y < mHeight; ++y) { for (int x = 0; x < mWidth; ++x) { // Skip out of bounds tiles if (!bounds.contains(x, y)) { newGrid[x + y * mWidth] = cellAt(x, y); continue; } // Get position to pull tile value from int oldX = x - offset.x(); int oldY = y - offset.y(); // Wrap x value that will be pulled from if (wrapX && bounds.width() > 0) { while (oldX < bounds.left()) oldX += bounds.width(); while (oldX > bounds.right()) oldX -= bounds.width(); } // Wrap y value that will be pulled from if (wrapY && bounds.height() > 0) { while (oldY < bounds.top()) oldY += bounds.height(); while (oldY > bounds.bottom()) oldY -= bounds.height(); } // Set the new tile if (contains(oldX, oldY) && bounds.contains(oldX, oldY)) newGrid[x + y * mWidth] = cellAt(oldX, oldY); else newGrid[x + y * mWidth] = Cell(); } } mGrid = newGrid; } bool TileLayer::canMergeWith(Layer *other) const { return dynamic_cast(other) != 0; } Layer *TileLayer::mergedWith(Layer *other) const { Q_ASSERT(canMergeWith(other)); const TileLayer *o = static_cast(other); const QRect unitedBounds = bounds().united(o->bounds()); const QPoint offset = position() - unitedBounds.topLeft(); TileLayer *merged = static_cast(clone()); merged->resize(unitedBounds.size(), offset); merged->merge(o->position() - unitedBounds.topLeft(), o); return merged; } QRegion TileLayer::computeDiffRegion(const TileLayer *other) const { QRegion ret; const int dx = other->x() - mX; const int dy = other->y() - mY; QRect r = QRect(0, 0, width(), height()); r &= QRect(dx, dy, other->width(), other->height()); for (int y = r.top(); y <= r.bottom(); ++y) { for (int x = r.left(); x <= r.right(); ++x) { if (cellAt(x, y) != other->cellAt(x - dx, y - dy)) { const int rangeStart = x; while (x <= r.right() && cellAt(x, y) != other->cellAt(x - dx, y - dy)) { ++x; } const int rangeEnd = x; ret += QRect(rangeStart, y, rangeEnd - rangeStart, 1); } } } return ret; } bool TileLayer::isEmpty() const { for (int i = 0, i_end = mGrid.size(); i < i_end; ++i) if (!mGrid.at(i).isEmpty()) return false; return true; } /** * Returns a duplicate of this TileLayer. * * \sa Layer::clone() */ Layer *TileLayer::clone() const { return initializeClone(new TileLayer(mName, mX, mY, mWidth, mHeight)); } TileLayer *TileLayer::initializeClone(TileLayer *clone) const { Layer::initializeClone(clone); clone->mGrid = mGrid; clone->mMaxTileSize = mMaxTileSize; clone->mOffsetMargins = mOffsetMargins; return clone; } tiled-qt-0.9.1/src/libtiled/tilelayer.h000066400000000000000000000204721217502731700200000ustar00rootroot00000000000000/* * tilelayer.h * Copyright 2008-2011, Thorbjørn Lindeijer * Copyright 2009, Jeff Bland * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TILELAYER_H #define TILELAYER_H #include "tiled_global.h" #include "layer.h" #include #include #include namespace Tiled { class Tile; class Tileset; /** * A cell on a tile layer grid. */ class Cell { public: Cell() : tile(0), flippedHorizontally(false), flippedVertically(false), flippedAntiDiagonally(false) {} explicit Cell(Tile *tile) : tile(tile), flippedHorizontally(false), flippedVertically(false), flippedAntiDiagonally(false) {} bool isEmpty() const { return tile == 0; } bool operator == (const Cell &other) const { return tile == other.tile && flippedHorizontally == other.flippedHorizontally && flippedVertically == other.flippedVertically && flippedAntiDiagonally == other.flippedAntiDiagonally; } bool operator != (const Cell &other) const { return tile != other.tile || flippedHorizontally != other.flippedHorizontally || flippedVertically != other.flippedVertically || flippedAntiDiagonally != other.flippedAntiDiagonally; } Tile *tile; bool flippedHorizontally; bool flippedVertically; bool flippedAntiDiagonally; }; /** * A tile layer is a grid of cells. Each cell refers to a specific tile, and * stores how the tile is flipped. * * Coordinates and regions passed to function parameters are in local * coordinates and do not take into account the position of the layer. */ class TILEDSHARED_EXPORT TileLayer : public Layer { public: enum FlipDirection { FlipHorizontally, FlipVertically, FlipDiagonally }; enum RotateDirection { RotateLeft, RotateRight }; /** * Constructor. */ TileLayer(const QString &name, int x, int y, int width, int height); /** * Returns the maximum tile size of this layer. */ QSize maxTileSize() const { return mMaxTileSize; } /** * Returns the margins that have to be taken into account while drawing * this tile layer. The margins depend on the maximum tile size and the * offset applied to the tiles. */ QMargins drawMargins() const { return QMargins(mOffsetMargins.left(), mOffsetMargins.top() + mMaxTileSize.height(), mOffsetMargins.right() + mMaxTileSize.width(), mOffsetMargins.bottom()); } /** * Returns whether (x, y) is inside this map layer. */ bool contains(int x, int y) const { return x >= 0 && y >= 0 && x < mWidth && y < mHeight; } bool contains(const QPoint &point) const { return contains(point.x(), point.y()); } /** * Calculates the region occupied by the tiles of this layer. Similar to * Layer::bounds(), but leaves out the regions without tiles. */ QRegion region() const; /** * Returns a read-only reference to the cell at the given coordinates. The * coordinates have to be within this layer. */ const Cell &cellAt(int x, int y) const { return mGrid.at(x + y * mWidth); } const Cell &cellAt(const QPoint &point) const { return cellAt(point.x(), point.y()); } /** * Sets the cell at the given coordinates. */ void setCell(int x, int y, const Cell &cell); /** * Returns a copy of the area specified by the given \a region. The * caller is responsible for the returned tile layer. */ TileLayer *copy(const QRegion ®ion) const; TileLayer *copy(int x, int y, int width, int height) const { return copy(QRegion(x, y, width, height)); } /** * Merges the given \a layer onto this layer at position \a pos. Parts that * fall outside of this layer will be lost and empty tiles in the given * layer will have no effect. */ void merge(const QPoint &pos, const TileLayer *layer); /** * Removes all cells in the specified region. */ void erase(const QRegion ®ion); /** * Sets the cells starting at the given position to the cells in the given * \a tileLayer. Parts that fall outside of this layer will be ignored. * * When a \a mask is given, only cells that fall within this mask are set. * The mask is applied in local coordinates. */ void setCells(int x, int y, TileLayer *tileLayer, const QRegion &mask = QRegion()); /** * Flip this tile layer in the given \a direction. Direction must be * horizontal or vertical. This doesn't change the dimensions of the * tile layer. */ void flip(FlipDirection direction); /** * Rotate this tile layer by 90 degrees left or right. The tile positions * are rotated within the layer, and the tiles themselves are rotated. The * dimensions of the tile layer are swapped. */ void rotate(RotateDirection direction); /** * Computes and returns the set of tilesets used by this tile layer. */ QSet usedTilesets() const; /** * Returns whether this tile layer is referencing the given tileset. */ bool referencesTileset(const Tileset *tileset) const; /** * Returns the region of tiles coming from the given \a tileset. */ QRegion tilesetReferences(Tileset *tileset) const; /** * Removes all references to the given tileset. This sets all tiles on this * layer that are from the given tileset to null. */ void removeReferencesToTileset(Tileset *tileset); /** * Replaces all tiles from \a oldTileset with tiles from \a newTileset. */ void replaceReferencesToTileset(Tileset *oldTileset, Tileset *newTileset); /** * Resizes this tile layer to \a size, while shifting all tiles by * \a offset. * * \sa Layer::resize() */ virtual void resize(const QSize &size, const QPoint &offset); /** * Offsets the objects in this group by \a offset, within \bounds * and optionally wraps it. * * \sa Layer::offset() */ virtual void offset(const QPoint &offset, const QRect &bounds, bool wrapX, bool wrapY); bool canMergeWith(Layer *other) const; Layer *mergedWith(Layer *other) const; /** * Returns the region where this tile layer and the given tile layer * are different. The relative positions of the layers are taken into * account. The returned region is relative to this tile layer. */ QRegion computeDiffRegion(const TileLayer *other) const; /** * Returns true if all tiles in the layer are empty. */ bool isEmpty() const; virtual Layer *clone() const; protected: TileLayer *initializeClone(TileLayer *clone) const; private: QSize mMaxTileSize; QMargins mOffsetMargins; QVector mGrid; }; } // namespace Tiled #endif // TILELAYER_H tiled-qt-0.9.1/src/libtiled/tileset.cpp000066400000000000000000000253351217502731700200150ustar00rootroot00000000000000/* * tileset.cpp * Copyright 2008-2009, Thorbjørn Lindeijer * Copyrigth 2009, Edward Hutchins * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "tileset.h" #include "tile.h" #include "terrain.h" #include using namespace Tiled; Tileset::~Tileset() { qDeleteAll(mTiles); } Tile *Tileset::tileAt(int id) const { return (id < mTiles.size()) ? mTiles.at(id) : 0; } bool Tileset::loadFromImage(const QImage &image, const QString &fileName) { Q_ASSERT(mTileWidth > 0 && mTileHeight > 0); if (image.isNull()) return false; const int stopWidth = image.width() - mTileWidth; const int stopHeight = image.height() - mTileHeight; int oldTilesetSize = mTiles.size(); int tileNum = 0; for (int y = mMargin; y <= stopHeight; y += mTileHeight + mTileSpacing) { for (int x = mMargin; x <= stopWidth; x += mTileWidth + mTileSpacing) { const QImage tileImage = image.copy(x, y, mTileWidth, mTileHeight); QPixmap tilePixmap = QPixmap::fromImage(tileImage); if (mTransparentColor.isValid()) { const QImage mask = tileImage.createMaskFromColor(mTransparentColor.rgb()); tilePixmap.setMask(QBitmap::fromImage(mask)); } if (tileNum < oldTilesetSize) { mTiles.at(tileNum)->setImage(tilePixmap); } else { mTiles.append(new Tile(tilePixmap, tileNum, this)); } ++tileNum; } } // Blank out any remaining tiles to avoid confusion while (tileNum < oldTilesetSize) { QPixmap tilePixmap = QPixmap(mTileWidth, mTileHeight); tilePixmap.fill(); mTiles.at(tileNum)->setImage(tilePixmap); ++tileNum; } mImageWidth = image.width(); mImageHeight = image.height(); mColumnCount = columnCountForWidth(mImageWidth); mImageSource = fileName; return true; } Tileset *Tileset::findSimilarTileset(const QList &tilesets) const { foreach (Tileset *candidate, tilesets) { if (candidate != this && candidate->imageSource() == imageSource() && candidate->tileWidth() == tileWidth() && candidate->tileHeight() == tileHeight() && candidate->tileSpacing() == tileSpacing() && candidate->margin() == margin()) { return candidate; } } return 0; } int Tileset::columnCountForWidth(int width) const { Q_ASSERT(mTileWidth > 0); return (width - mMargin + mTileSpacing) / (mTileWidth + mTileSpacing); } Terrain *Tileset::addTerrain(const QString &name, int imageTileId) { Terrain *terrain = new Terrain(terrainCount(), this, name, imageTileId); insertTerrain(terrainCount(), terrain); return terrain; } void Tileset::insertTerrain(int index, Terrain *terrain) { Q_ASSERT(terrain->tileset() == this); mTerrainTypes.insert(index, terrain); // Reassign terrain IDs for (int terrainId = index; terrainId < mTerrainTypes.size(); ++terrainId) mTerrainTypes.at(terrainId)->setId(terrainId); // Adjust tile terrain references foreach (Tile *tile, mTiles) { for (int corner = 0; corner < 4; ++corner) { const int terrainId = tile->cornerTerrainId(corner); if (terrainId >= index) tile->setCornerTerrain(corner, terrainId + 1); } } mTerrainDistancesDirty = true; } Terrain *Tileset::takeTerrainAt(int index) { Terrain *terrain = mTerrainTypes.takeAt(index); // Reassign terrain IDs for (int terrainId = index; terrainId < mTerrainTypes.size(); ++terrainId) mTerrainTypes.at(terrainId)->setId(terrainId); // Clear and adjust tile terrain references foreach (Tile *tile, mTiles) { for (int corner = 0; corner < 4; ++corner) { const int terrainId = tile->cornerTerrainId(corner); if (terrainId == index) tile->setCornerTerrain(corner, 0xFF); else if (terrainId > index) tile->setCornerTerrain(corner, terrainId - 1); } } mTerrainDistancesDirty = true; return terrain; } int Tileset::terrainTransitionPenalty(int terrainType0, int terrainType1) { if (mTerrainDistancesDirty) { recalculateTerrainDistances(); mTerrainDistancesDirty = false; } terrainType0 = terrainType0 == 255 ? -1 : terrainType0; terrainType1 = terrainType1 == 255 ? -1 : terrainType1; // Do some magic, since we don't have a transition array for no-terrain if (terrainType0 == -1 && terrainType1 == -1) return 0; if (terrainType0 == -1) return mTerrainTypes.at(terrainType1)->transitionDistance(terrainType0); return mTerrainTypes.at(terrainType0)->transitionDistance(terrainType1); } void Tileset::recalculateTerrainDistances() { // some fancy macros which can search for a value in each byte of a word simultaneously #define hasZeroByte(dword) (((dword) - 0x01010101UL) & ~(dword) & 0x80808080UL) #define hasByteEqualTo(dword, value) (hasZeroByte((dword) ^ (~0UL/255 * (value)))) // Terrain distances are the number of transitions required before one terrain may meet another // Terrains that have no transition path have a distance of -1 for (int i = 0; i < terrainCount(); ++i) { Terrain *type = terrain(i); QVector distance(terrainCount() + 1, -1); // Check all tiles for transitions to other terrain types for (int j = 0; j < tileCount(); ++j) { Tile *t = tileAt(j); if (!hasByteEqualTo(t->terrain(), i)) continue; // This tile has transitions, add the transitions as neightbours (distance 1) int tl = t->cornerTerrainId(0); int tr = t->cornerTerrainId(1); int bl = t->cornerTerrainId(2); int br = t->cornerTerrainId(3); // Terrain on diagonally opposite corners are not actually a neighbour if (tl == i || br == i) { distance[tr + 1] = 1; distance[bl + 1] = 1; } if (tr == i || bl == i) { distance[tl + 1] = 1; distance[br + 1] = 1; } // terrain has at least one tile of its own type distance[i + 1] = 0; } type->setTransitionDistances(distance); } // Calculate indirect transition distances bool bNewConnections; do { bNewConnections = false; // For each combination of terrain types for (int i = 0; i < terrainCount(); ++i) { Terrain *t0 = terrain(i); for (int j = 0; j < terrainCount(); ++j) { if (i == j) continue; Terrain *t1 = terrain(j); // Scan through each terrain type, and see if we have any in common for (int t = -1; t < terrainCount(); ++t) { int d0 = t0->transitionDistance(t); int d1 = t1->transitionDistance(t); if (d0 == -1 || d1 == -1) continue; // We have cound a common connection int d = t0->transitionDistance(j); Q_ASSERT(t1->transitionDistance(i) == d); // If the new path is shorter, record the new distance if (d == -1 || d0 + d1 < d) { d = d0 + d1; t0->setTransitionDistance(j, d); t1->setTransitionDistance(i, d); // We're making progress, flag for another iteration... bNewConnections = true; } } } } // Repeat while we are still making new connections (could take a number of iterations for distant terrain types to connect) } while (bNewConnections); } void Tileset::addTile(const QPixmap &image) { detachExternalImage(); Tile *newTile = new Tile(image, tileCount(), this); mTiles.append(newTile); if (mTileHeight < image.height()) mTileHeight = image.height(); if (mTileWidth < image.width()) mTileWidth = image.width(); } void Tileset::setTileImage(int index, const QPixmap &image) { detachExternalImage(); Tile *tile = tileAt(index); if (tile) { QPixmap previousImage = tile->image(); tile->setImage(image); if (previousImage.height() != image.height() || previousImage.width() != image.width()) { // Update our max. tile size if (previousImage.height() == mTileHeight || previousImage.width() == mTileWidth) { // This used to be the max image; we have to recompute updateTileSize(); } else { // Check if we have a new maximum if (mTileHeight < image.height()) mTileHeight = image.height(); if (mTileWidth < image.width()) mTileWidth = image.width(); } } } } void Tileset::detachExternalImage() { mFileName = QString(); mImageSource = QString(); } void Tileset::updateTileSize() { int maxWidth = 0; int maxHeight = 0; foreach (Tile *tile, mTiles) { if (maxWidth < tile->width()) maxWidth = tile->width(); if (maxHeight < tile->height()) maxHeight = tile->height(); } mTileWidth = maxWidth; mTileHeight = maxHeight; } tiled-qt-0.9.1/src/libtiled/tileset.h000066400000000000000000000231751217502731700174620ustar00rootroot00000000000000/* * tileset.h * Copyright 2008-2009, Thorbjørn Lindeijer * Copyrigth 2009, Edward Hutchins * * This file is part of libtiled. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TILESET_H #define TILESET_H #include "object.h" #include #include #include #include #include #include class QImage; namespace Tiled { class Tile; class Terrain; /** * A tileset, representing a set of tiles. * * This class currently only supports loading tiles from a tileset image, using * loadFromImage(). There is no way to add or remove arbitrary tiles. */ class TILEDSHARED_EXPORT Tileset : public Object { public: /** * Constructor. * * @param name the name of the tileset * @param tileWidth the width of the tiles in the tileset * @param tileHeight the height of the tiles in the tileset * @param tileSpacing the spacing between the tiles in the tileset image * @param margin the margin around the tiles in the tileset image */ Tileset(const QString &name, int tileWidth, int tileHeight, int tileSpacing = 0, int margin = 0): mName(name), mTileWidth(tileWidth), mTileHeight(tileHeight), mTileSpacing(tileSpacing), mMargin(margin), mImageWidth(0), mImageHeight(0), mColumnCount(0), mTerrainDistancesDirty(false) { Q_ASSERT(tileSpacing >= 0); Q_ASSERT(margin >= 0); } /** * Destructor. */ ~Tileset(); /** * Returns the name of this tileset. */ const QString &name() const { return mName; } /** * Sets the name of this tileset. */ void setName(const QString &name) { mName = name; } /** * Returns the file name of this tileset. When the tileset isn't an * external tileset, the file name is empty. */ const QString &fileName() const { return mFileName; } /** * Sets the filename of this tileset. */ void setFileName(const QString &fileName) { mFileName = fileName; } /** * Returns whether this tileset is external. */ bool isExternal() const { return !mFileName.isEmpty(); } /** * Returns the maximum width of the tiles in this tileset. */ int tileWidth() const { return mTileWidth; } /** * Returns the maximum height of the tiles in this tileset. */ int tileHeight() const { return mTileHeight; } /** * Returns the maximum size of the tiles in this tileset. */ QSize tileSize() const { return QSize(mTileWidth, mTileHeight); } /** * Returns the spacing between the tiles in the tileset image. */ int tileSpacing() const { return mTileSpacing; } /** * Returns the margin around the tiles in the tileset image. */ int margin() const { return mMargin; } /** * Returns the offset that is applied when drawing the tiles in this * tileset. */ QPoint tileOffset() const { return mTileOffset; } /** * @see tileOffset */ void setTileOffset(QPoint offset) { mTileOffset = offset; } /** * Returns a const reference to the list of tiles in this tileset. */ const QList &tiles() const { return mTiles; } /** * Returns the tile for the given tile ID. * The tile ID is local to this tileset, which means the IDs are in range * [0, tileCount() - 1]. */ Tile *tileAt(int id) const; /** * Returns the number of tiles in this tileset. */ int tileCount() const { return mTiles.size(); } /** * Returns the number of tile columns in the tileset image. */ int columnCount() const { return mColumnCount; } /** * Returns the width of the tileset image. */ int imageWidth() const { return mImageWidth; } /** * Returns the height of the tileset image. */ int imageHeight() const { return mImageHeight; } /** * Returns the transparent color, or an invalid color if no transparent * color is used. */ QColor transparentColor() const { return mTransparentColor; } /** * Sets the transparent color. Pixels with this color will be masked out * when loadFromImage() is called. */ void setTransparentColor(const QColor &c) { mTransparentColor = c; } /** * Load this tileset from the given tileset \a image. This will replace * existing tile images in this tileset with new ones. If the new image * contains more tiles than exist in the tileset new tiles will be * appended, if there are fewer tiles the excess images will be blanked. * * The tile width and height of this tileset must be higher than 0. * * @param image the image to load the tiles from * @param fileName the file name of the image, which will be remembered * as the image source of this tileset. * @return true if loading was successful, otherwise * returns false */ bool loadFromImage(const QImage &image, const QString &fileName); /** * This checks if there is a similar tileset in the given list. * It is needed for replacing this tileset by its similar copy. */ Tileset *findSimilarTileset(const QList &tilesets) const; /** * Returns the file name of the external image that contains the tiles in * this tileset. Is an empty string when this tileset doesn't have a * tileset image. */ const QString &imageSource() const { return mImageSource; } /** * Returns the column count that this tileset would have if the tileset * image would have the given \a width. This takes into account the tile * size, margin and spacing. */ int columnCountForWidth(int width) const; /** * Returns a const reference to the list of terrains in this tileset. */ const QList &terrains() const { return mTerrainTypes; } /** * Returns the number of terrain types in this tileset. */ int terrainCount() const { return mTerrainTypes.size(); } /** * Returns the terrain type at the given \a index. */ Terrain *terrain(int index) const { return index >= 0 ? mTerrainTypes[index] : 0; } /** * Adds a new terrain type. * * @param name the name of the terrain * @param imageTile the id of the tile that represents the terrain visually * @return the created Terrain instance */ Terrain *addTerrain(const QString &name, int imageTileId); /** * Adds the \a terrain type at the given \a index. * * The terrain should already have this tileset associated with it. */ void insertTerrain(int index, Terrain *terrain); /** * Removes the terrain type at the given \a index and returns it. The * caller becomes responsible for the lifetime of the terrain type. * * This will cause the terrain ids of subsequent terrains to shift up to * fill the space and the terrain information of all tiles in this tileset * will be updated accordingly. */ Terrain *takeTerrainAt(int index); /** * Returns the transition penalty(/distance) between 2 terrains. -1 if no transition is possible. */ int terrainTransitionPenalty(int terrainType0, int terrainType1); /** * Add a new tile to the end of the tileset */ void addTile(const QPixmap &image); /** * Set a tile's image */ void setTileImage(int index, const QPixmap &image); /** * Used by the Tile class when its terrain information changes. */ void markTerrainDistancesDirty() { mTerrainDistancesDirty = true; } private: /** * Detaches from the external image. Should be called everytime the tileset * is changed. */ void detachExternalImage(); /** * Sets tile size to the maximum size. */ void updateTileSize(); /** * Calculates the transition distance matrix for all terrain types. */ void recalculateTerrainDistances(); QString mName; QString mFileName; QString mImageSource; QColor mTransparentColor; int mTileWidth; int mTileHeight; int mTileSpacing; int mMargin; QPoint mTileOffset; int mImageWidth; int mImageHeight; int mColumnCount; QList mTiles; QList mTerrainTypes; bool mTerrainDistancesDirty; }; } // namespace Tiled #endif // TILESET_H tiled-qt-0.9.1/src/plugins/000077500000000000000000000000001217502731700155215ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/droidcraft/000077500000000000000000000000001217502731700176425ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/droidcraft/droidcraft.pro000066400000000000000000000003711217502731700225060ustar00rootroot00000000000000include(../plugin.pri) DEFINES += DROIDCRAFT_LIBRARY greaterThan(QT_MAJOR_VERSION, 4) { OTHER_FILES = plugin.json } SOURCES += droidcraftplugin.cpp HEADERS += droidcraftplugin.h\ droidcraft_global.h RESOURCES += \ droidcraft.qrc tiled-qt-0.9.1/src/plugins/droidcraft/droidcraft.qrc000066400000000000000000000001321217502731700224660ustar00rootroot00000000000000 tileset.png tiled-qt-0.9.1/src/plugins/droidcraft/droidcraft_global.h000066400000000000000000000021471217502731700234600ustar00rootroot00000000000000/* * Droidcraft Tiled Plugin * Copyright 2011, seeseekey * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef DROIDCRAFT_GLOBAL_H #define DROIDCRAFT_GLOBAL_H #include #if defined(DROIDCRAFT_LIBRARY) # define DROIDCRAFTSHARED_EXPORT Q_DECL_EXPORT # define DROIDCRAFTSHARED_IMPORT Q_DECL_EXPORT #else # define DROIDCRAFTSHARED_EXPORT Q_DECL_IMPORT # define DROIDCRAFTSHARED_IMPORT Q_DECL_IMPORT #endif #endif // DROIDCRAFT_GLOBAL_H tiled-qt-0.9.1/src/plugins/droidcraft/droidcraftplugin.cpp000066400000000000000000000075501217502731700237150ustar00rootroot00000000000000/* * Droidcraft Tiled Plugin * Copyright 2011, seeseekey * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "droidcraftplugin.h" #include "map.h" #include "tile.h" #include "tileset.h" #include "tilelayer.h" #include "compression.h" #include #include using namespace Droidcraft; DroidcraftPlugin::DroidcraftPlugin() { } // Reader Tiled::Map *DroidcraftPlugin::read(const QString &fileName) { using namespace Tiled; QByteArray uncompressed; // Read data QFile f(fileName); if (f.open(QIODevice::ReadOnly)) { QByteArray compressed = f.readAll(); f.close(); uncompressed = decompress(compressed, 48 * 48); } // Check the data if (uncompressed.count() != 48 * 48) { mError = tr("This is not a valid Droidcraft map file!"); return 0; } // Build 48 x 48 map // Create a Map -> Create a Tileset -> Add Tileset to map // -> Create a TileLayer -> Fill layer -> Add TileLayer to Map Map *map = new Map(Map::Orthogonal, 48, 48, 32, 32); Tileset *mapTileset = new Tileset("tileset", 32, 32); mapTileset->loadFromImage(QImage(":/tileset.png"), "tileset.png"); map->addTileset(mapTileset); // Fill layer TileLayer *mapLayer = new TileLayer("map", 0, 0, 48, 48); // Load for (int i = 0; i < 48 * 48; i++) { unsigned char tileFile = uncompressed.at(i); int y = i / 48; int x = i - (48 * y); Tile *tile = mapTileset->tileAt(tileFile); mapLayer->setCell(x, y, Cell(tile)); } map->addLayer(mapLayer); return map; } bool DroidcraftPlugin::supportsFile(const QString &fileName) const { return QFileInfo(fileName).suffix() == QLatin1String("dat"); } // Writer bool DroidcraftPlugin::write(const Tiled::Map *map, const QString &fileName) { using namespace Tiled; // Check layer count and type if (map->layerCount() != 1 || !map->layerAt(0)->isTileLayer()) { mError = tr("The map needs to have exactly one tile layer!"); return false; } TileLayer *mapLayer = map->layerAt(0)->asTileLayer(); // Check layer size if (mapLayer->width() != 48 || mapLayer->height() != 48) { mError = tr("The layer must have a size of 48 x 48 tiles!"); return false; } // Create QByteArray and compress it QByteArray uncompressed = QByteArray(48 * 48, 0); const int width = mapLayer->width(); const int height = mapLayer->height(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { if (Tile *tile = mapLayer->cellAt(x, y).tile) uncompressed[y * width + x] = (unsigned char) tile->id(); } } QByteArray compressed = compress(uncompressed, Gzip); // Write QByteArray QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { mError = tr("Could not open file for writing."); return false; } file.write(compressed); file.close(); return true; } QString DroidcraftPlugin::nameFilter() const { return tr("Droidcraft map files (*.dat)"); } QString DroidcraftPlugin::errorString() const { return mError; } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Droidcraft, DroidcraftPlugin) #endif tiled-qt-0.9.1/src/plugins/droidcraft/droidcraftplugin.h000066400000000000000000000034701217502731700233570ustar00rootroot00000000000000/* * Droidcraft Tiled Plugin * Copyright 2011, seeseekey * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef DROIDCRAFTPLUGIN_H #define DROIDCRAFTPLUGIN_H #include "droidcraft_global.h" #include "map.h" #include "mapwriterinterface.h" #include "mapreaderinterface.h" #include namespace Droidcraft { class DROIDCRAFTSHARED_EXPORT DroidcraftPlugin : public QObject, public Tiled::MapWriterInterface, public Tiled::MapReaderInterface { Q_OBJECT Q_INTERFACES(Tiled::MapReaderInterface) Q_INTERFACES(Tiled::MapWriterInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapWriterInterface" FILE "plugin.json") Q_PLUGIN_METADATA(IID "org.mapeditor.MapReaderInterface" FILE "plugin.json") #endif public: DroidcraftPlugin(); // MapReaderInterface Tiled::Map *read(const QString &fileName); bool supportsFile(const QString &fileName) const; // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); QString nameFilter() const; QString errorString() const; private: QString mError; }; } // namespace Droidcraft #endif // DROIDCRAFTPLUGIN_H tiled-qt-0.9.1/src/plugins/droidcraft/plugin.json000066400000000000000000000000321217502731700220260ustar00rootroot00000000000000{ "Keys": [ "notused" ] } tiled-qt-0.9.1/src/plugins/droidcraft/tileset.png000066400000000000000000001566141217502731700220360ustar00rootroot00000000000000PNG  IHDRxsRGBbKGD pHYsltIME IDATxyeיٗ$}!BTm=x=*^Q^Wz縜Aq ( ,a˾gLYתGuUwϒ_^C/UOꩧ]?_oPw*Oƴ#͘$ 6~O?LO|!utt BrhiiA4;F<SUF|>LǏ0m4^/B:;;CUUN8A8F"']`FBw kyy@F@BrxJO~)y=K i*ǿ}Jkt/hJW)y CBB0 a HYȪ, $yQ=,8NPUs:(B__TXEQ4D"A__d]z8ND"!$ 04 Il |.{!@,7׆~SF|j)__^=  _JmUʔp122z322.! FH )8tT0L@YQb1 `*dY&LbFbNp000@"@Q멨@4E!Ni=F4dK0;4FBYQAJT{l"I`+cj.\HKK 7?l+3xFN>C*,/R[ Z8~p@GG|ǻmO5ٯ  2~9p`?ipe`_HF52I  1&@RS@R$iT^V!sb14p*N'$L&Bֿin ànjkkq8Ȳa8l4 ˅&JRV{,~]zi$M ݟB =&q* s}sHB5X(JMu5dkYǕW^ ٳ}CON'zxI!Cl$'еj2j HFY`G{Lǻ-NR\8M`xy=O8U.! a9|'O檫Q7d􄌒<._ٗ@BJH) >.$$Pz{BQYY,6h;xH&\.xl3@uEAQn7J@4%ɘ1쵑$H?5V@2~*j)0ٕhUZŠ`0yDx1} NK.eʕ8{[ccp γn#(/O#3!kܳ1dW̯Hx"jAH꘽$;VD!Q ~k*i']3 >L+\/sq8]nV_~EY4 !%s [j ٜE9*VQ3I!Pͼl8Y+rrH&j+tv WUmƊB*L&c$ Yq\!l>$u62ĎFd G> #iHv&XGq+HH=l@?KĚ5Wq \xᅣOIXt)K._raȣ,cɈ㉾mEd-4 KmV!jYΙ3ɤ3h/x '^}o|8NN?AUu >}ie`t7}n9 @aQ) #!M MU T CT*Ȋ^aСC$ 9qT ECC$9~KD(ؾg>EQ%p)!HӤ<+|IvqWk}&"~*jR]IR}I٥4"cVK_ ;/k׭vV\9u8yG?5v$>DBC0}QK @P|bnux<,]zfv߲0@P! RL}(v8@( eߧ[An32j-Ivٟ$Ii$3~:Fu[)!"~!ǃ,LV`X Icd d p:smւyՂ9yyİ7o\lYQ?O|;8% ߒ8tAW|xId=NjGBR WlQK U 3[ G t: Elf@ ̧md`0\~ڍ,d]vt%`޽,\3fW 9̙TWt:f͢m֬WVF$UUfHr܀~9bQ 2/YZux匿ˮ$ Emm]&h$?Ͻ5QO"(DZGq($hNK%0p$w?[gi0Ĉ( bBUW+9njjSN8=ig#Ѫ0/n|=3໔g4q4W!鞍'琍UWestvv& ~;z]%ƶ*:8qɉc̝7T2E}}}(h׏gk7o++#ZՕHqIf_2 ;j)|=X$-VEQXX^uӉ]uT*E? 8!!;Jz%0F@q(Uz=?//W&"1ဦ9طoߤ)o{Oz^7b)ek ѪW+xOSٻ%"]BI ՏT\}芏%r[Rό .(mpپ#?;%l_Μ>Mu$z<9300ۏL&i5t*ԔX@f0 ""TJ037 3sa$I;:\."]l!===ؠr6mLx _*KKkk61:iSijm>==Dzv(?'|}z9edY$]׉D"tww6 0|>L~S1H 4XXEtOũ87CY a[VgV"t2$IB4֭[7s=ׄ0U¶ HgӯTjqDM2[A}!2=bU#>yRd}iKc~#/`0X6G)ffϭ#PP :}:m3HK446Dy\r* sNJED%DJFrGa{ jV.+ȲMca&!JeJ[Qd2d2D8Z UUuI$&:H8jgV"2[?mC,Q9/ _ nb{O.Y?\0wey2Zg}2Dr 7\3 տ?F`0XN6Ģ1z7wP(@-huPVb$Fd@mDb1 ]p€W fl Vf@Y|L&B!Ml6?l~+9t:M<r D"ߟdL86@q+zNN胡I{9B07y-Mވ7\9ﯢšeduxCOy5ÀoiGB8_3gi .ȇ#x^;i$io?J*by;KWG7({¹ Xf^+2:Iexqi.AeIkk+i$IBbFn~.zzzz^5558M{Yᰝ/a;dGONtIٟ V PHʽ|R -]H,{H+W؋:ˀ% | k?0};[o,Kp}:UVs%Nٷo/;^~zeG0 CPxdQ1iى(+Css3466ڍ#dl@0s2^0,\޹>%.jm(rm}$H~*V8D~썺e˖xUhUH"Me#xCMZauF:WyvYt ?s4<4Utwu *˘%LeXl$$>g˅#ڙH^u*E!HPYYiTWWDMMMA2먪G#9dU _ } Rq   ^[2/+ Е K鯽VGez3~Izs/6mMe?~pF/_ҋ.-}D6ں:dIw_cTe\sנ''ɐH$p\ %Gav )`QaJP%Kˏg! 'ؗ of sPV0Iw+ɼg5Jf@+q'T׫`7|Fz{8|aE4LqY83f]d9bp8p8$2o``NLRL&C$$ T*Euu5v}<[76JV aya "`pJaիW2_–XJho&휆Faf~FXS#[; 457hbdYc#z\nnd"1J&ts ȲCeRN:h4J:8PU^*++ Xd  x|\.bDQSٰ z5;nVp $̕Je`!8pşgnV;D gb.C Q{v U {td۶gȞ=y'H| /_#GUŐc5#}Ã],D"!hmmEu:;;qve@*ɓ(BUUg%Iv]455c'l?;/|?O y¼H$B2`ٲeCnbʩry0-td=d RqBRU߫qj xP\ٹsմN?E[,|>Vf…=sfX`#`90:jU 3 Pq< |(bg[؄ Bhnl$hb ˲b1$uuu2mڴKVTy^Q@geb08gSSO=5i`~x3d9pLt:m”$*>}:ӧOftXUU6!%g$-r|_`R^_Hx&W !-/<1h2m5^U2ræ<ͯK/X x^r`2)8+PPϮv(tuu|w{#Ցv4cla6yKٌB&_oA$G6P.[=fpI2"z9%]GB~nfUxD"HDss`U d2B]]]b1[p:D"Μ9C<'NE__`v#;/K+(@iD̙ݻ۷m.j i0}M$" g{%Af>_^jf}Mn7PcKxf~\[ tU IBR9 pI&vBӷۍG(*3EQz~?Lۋtg3 644U&ȱ2L3A!o SHg0` <ȣn:jk=?˼緗@1d'IF2#/f !)eW_K03߷~#zy}FΝSO># pi~sKkg9r0/hfSNΌ3 @1De2*ညDcWz{{1 f2L>x8{,d.4ndln ðY>ٳg%"ou߿/B0d`o@w^Ξ=/"z+\pv]s]F!݉bUIx`Nb(ZY/杦 ̌[oUBx<\{<㶗S\j5s.K80nlm3t[3²P/j*nN`.ij___v& !d2h4J4uºAY9}KWBe!`z#ʃ  2s̡H>}Ξ=S^\H?,R-dZi5Ye]}7rf.fȡʘ/x=zjjsz%I&H"2ӦD<>?}((x9(P= e^0d28EV`?X kY@8tIzTWWۍᠫ0.D׷bU?C\h+L&C{QkOJvE"$N-S T@ɄI;m7t ]񛚚_)J@_)VZZ[tL8rR[[颮%˨^q(+hj~ IDAT$9$>g+p~]2$(9rp\JD;J 0bob1k/18Jo-o4RƋY=5M#g'\@2 ! 3}%;/KV$ JAY__Z Pׄ8Q?)twa p5 `2e%?W}L]F㹷s"CZ` N?BYS]x5cddE (UE@&v59 4lwJw)wqG9`:`" ٘|e%uLI" q$,_ Y6CY1RLPTPU:@˾ye)KY(KY^jxgY9 'q~5k$kg@SA)b|ԁ@@Q H=۷nzccv{衇qMܓS%ߺM[Nܱfa2Kd5 `6Tj*V_~<2n羚/;OrequOf۶mL6kגNٶmۘOd@IpѶLWF%'k8 :qihj b*ep-|pw߱O@5h`0 M42k pic!C&ì*mĬ-@.졕 )T v3}T;f_A~+r[V zՑUg.d=q#lhhm\M>H7?Ot Fj;(.5}4^l0,!fMR͛޽Us;-܂(\|\q$-{yγ::{ۚ3˩ӫ Yu /g>J*SK'X@ WɌlyQvEWgiIϚōo,bޙ7 rNAtX}ۺp8 V&wz+kmitXϏϫU`0go`VvӒG}u/{waݻxOc뮙UUT2: $FPs-xJ;I; a`F~f:9Nfؼ8 1&=ʦ_7q2(V[yI}g_X`_JM  .ytuuq]wIg$y}f42l'9p Pc6Su/fwH)N>,`p9#m Xt)x(M_r ?߼eI^7s o@~`0^q-ZݻohήNEO'$v0Z%۩P"' \ X\q0vbDfo>KgW{>.ihŮn7'|Tmyv_V +yn1?To^kyIgjƢ(5?ZO@)v-So8Rd YWe_  [6ρ=`p[ XI.H0y_x<֮]K[[}}}'dooK~,xnYT$=3=g:x2 SRw>5PT X V1|x<^nam۞-Vl)# @6oW6m3~#/op. qYs4 ]B`Y?[L{SFL@.L| * z2,[ EDEA_s۴ؿ "/Ap95?ZO]-9Y }W|KQUXoztӾ%U$~5vdy_?|.vO/QsEo~3ӦMӧO\s5vCr5p饗>#2lgpɩcYli΋.yO ^ fO:dUOκ1唙ؤ.Ro>Yc'|W`f>|cQ~qc1.nN*~yh?#b2;'>4߮e^ɫwhiYJ9 [Y`P;g';LyI]}E^w8uj)ծX+gvNJW,~Ѭp 8  J#>juY3f=bk׎tRfϞ=̜{2Zc{bDl^}dR.>*l,I̼9u3 Q̶Y%׾'s|!*-s:p Ѕ* 9+)&ĉ5n8+ZܚԤ ;svׄƷXdR*sT\F_،־J^iRD`|SWoORTJy&}رϼ\$*5З~l;<"B, xM-wOSlK?]) 9fU |GaJn}o~I|<%qn_ K+n^1G۪z'j'-gR8[Ny?fXV_~2 @WWO?4<2gr=|˗Q׿9%ȩx77̔Z=YgD_ /`-YvES<([3o>~{I5ês0yl<^,澊 MP &~gaf|qFyS+ΰ1)!c2o 2PIe~u^Me7W35|w= {8n & ƯvrҥsZrW) "KMˏ{>SG@Us 7ceu=?U{x<~,cR_7Jig-ioWXK;XaxǀGZOf2΂%~3qq$I8N4^p{|c{mq%7v\9 LibOu<~1YĿA(p_OaAc?w|կVB1H, mkF]P3qPU̿d(`yo_&s*G.T&t籰+]4tV69Mt9Tv'4QϼOJo\ɟ#Gc+VͿ'@Us;NWudn"Hx<ӵ9|ωPgtuX=3qoI`F r=/LarqGVo/ĉG?flRpM8B4ޘ+/w/.~߱&*2/̂SbuOa2\T~_u.~A ЇXJXwifvxFaHGC#{-nz?gU|om K<%*I%iNܠ?} {8~I>.:ݬo;ė>';d?)v藍pߡTVU 7RPUUڵk'99 ՘6LbֿW~9gA{ jug3kߪuց@Zg?/9teYf#tPU /IF$F,\жG+Zwu'?hS-/u(A|gѬoKrځbm{+/+/cD$NM׳d:{T}2/6K 1ߧ2>y'{]:M4ӧ0PYV9@KLR#w1$vuƉ?>e:gb7V f%э*$~ηמ(E1yzjL\a\٠ oM*_)H$zc\.8ǁ۲k7v_tXO_vro灯~ (U%OqWH׾5Λ7*Μ9ÓO>9>ndž{B9}euϻL@)Х#dCZO/ZgK Wc%o&ӄϽŋ?DDcxer.i,mpd 8QeiMVjC-[nfڏ2Rc#´L ,µoIڡܚA8)wtxLJFj|:v2d~z2|i)fpjk~/LGAZȈ cJVX G8{ 'dcà8J@=c͙e˖3f㓶.\xTYc<9ٵXMe*Z gZ`Q\/4d@qߗOrOD<~Pi$$FU,ïd2ϜTxdqU1x/w\rZ|{{s; 'slVJ_c; %+y]CYO6ϟ:H4o?)4Rcd.Y-_oLmm-'Ojdڴic$+q'^Y"0ܴ/lqi]PO=/j]nk[5̜ 3Kfrdw{j"qQygbKc <,R?|CAq߀ W&m]2޽I8UKy9(6p5&?[C.jXP%/0˟₪+Ҭ%cHC\N$gc>>6<ַ{H}4\?-?@lCr,WifY(J]y啎۷sL9֭[(Ì3waD]C6p9&׫#qMIѤ+Vs=pd2: ۥ&>7w8!d=  ӥ:Ϝ]-'tW?&LQD y!bS%!DWf ߔ?@l~XW EʽW%)G$Kf93[+6ЄbeHc;UQ^aUR0l91v??fבTg,6/K <9gŊ6ssu{W;^O}hkpz&"fFJBgd@Y!̤oG-Z\R뿘L\ +=40\cf aE*)1: x&79XfНߙ6_̵\74i,E`8b|u_џ~ePŸWrD?fo YH2Kn{/ IDATx<-zN]Q5d{[9%ܺЄW]=ToBsȩgOM kvWc|@CNLv0@ZU~?3f`Νʕ+Fú::t;we/=FS\l,:'%𞆣D\*.h(Ο=eu)>yhN(fko#Д&cMĭǶ*C'rw=SV͕ NMx}-]|C0*>+V0|f̘͛K.gլZG7|GN{9'POBIK3rǺ3t7E$ s|./gs|dKfs3QBS s܀վK'_C#!^mBk^ZXI^ZΫr|5/f;J6-8_!(|>kjkkl;TWW~Y?ZcR['K!MxJܜw.CnhԬhllu+++G?륯Yy{B!VZECCTjDK9#NH n1D6 2OD? D1&` Ɍi'RKо-7T)o!`0SUͰA_s9gEÂh~Z]]]… q .,X _Bf de f%;g6?It{&o?fqrR?O}|Sb#b10 .䗿%Nt{oGu^xsv/E{h<߯=ʁ%'QZy`^ey4\~# >~#-չf"%b3Sg+b?-/"2-``L%UUU!"o}[vjСCttt`#H TRμQ\C~~{`F sa²S9vIǗQWﶷ8YNc% ;GNn,O|;)M ~Xv}NdZ#6Fl<wބan)/")Hjsse rN浥ox.BഘPbD@?o)/&r&R{~N&=tڥwӦM.:˅HWVV*eYq %ou+#靝tvvN?꒎\‚ss.,n\x; aX^(I@f3;*{9k! "jjV[Vz{jֺW[mojp+ *JY9 $$$`:33Ϝgw6VqG/@/$82(~V ~:#8hp$dߑrn͉_zi&zu IiDeQuAqG,W>{[S?won] & xG]?B\!7rb'+`s  xQd(֜B LUq`eާߕm{@_N01qH SPj:!;W`A\ Qktn y^}t!+D0q|$= MM529&+Gw#аM̉!^ liS"88T8e,{F(((YO\:uã 'Z{[G~qk W\uSϜJ4G h6Z6o;By9H`6hԸo8ԡoa2)#?JG 2w.ơqTtpDOҿoǐBD t/]edn!Xn%MepawlH>/ߥ{σu~i.ZC A+H7$O BMhH)17vХ5m;v%͓#l,iyzPPP0tv*1:H 'ځ(,,${wmuũ5U iQ0􂥁.!ل#:iR}YA!-S_r`Ff_J rCAAi mZ \;-poJ^AtdU 3Fy],b[W<{-_47NB *#s2^(Hh!--(53<Ƴi pK+.4XBk[Z8 ԕ&n͈e?Jc?vۆ[_(((S6= ܵmZ[Q ;{ `2o)1BžuEYGFDt-1y<i$qՋ ??A ..;۲d{6 7dxr4Ԩz_!up^4>̆pIyzԂ!7e>:d0hd|/CpIr$\0iͶ4>^o}J;lXXM^Y6C~y6ˇUEܕ1Ԛsw=w 2F3;NKF6x:j_ .p]9Dp,Y𦁑uhl@d7ۇ~]`-aƐ*8Аv\PiH<2ժp`KA zК!7vRmaa!uuu̝;7}~-`&?я4hPӶ̙31cƴ=-L6zL |zDH#OXu~xk, B~ 5`T5I:^ ~?cH}Q:dŠBU IIW@j5>yO|x|0dÐ7jz 4M;-_QuPblޖѬ5xV?#q/n?CspAmV>ګ /v<.FUqu;9U,ʿLVNWRI\ YNl&䧿+0!m"4Ep@RZ1QKH&zuOCkfٙ6m;vUB֭[yyǎwڴiTVVslB:j-v] j1 w9':އ~)Ay6%wG ] EsVx6OO]1dO_Jՠma s]iNۻȃpArp鳦siJz3lXoLl'.r v~Ar\BctrLTաέ\ mJU J  dH%UkI$HLQ`^If_IA`noIQq^Y2 o,j=uK3}nMD ({Ude.]pҍ?:[1h~ | *b~?մ `BhC`vR.AFkU+;b\ ]hʗ@e)a'HOpcwш PC8-/ V|9,Hj@n\31 MgΜ9檾ZoпƏOVV1ЅLzaejSmTs':Gv10 jVH@p1 vO?J߭;-:(s7,|w 6~Y&OW)KgvڎY HPBJep9PU/Ahs~>c`h,ثR6I1jӏ vn8}&LIJ )lKr%>5c $9f/Tvx68U71 |r4:Oh=3R)pf RDCx1[,Xo_W˘ @8x3F À@ iWUEdC"q!Z.~md,kNC WRjfWOH Jb.¤-) x@IiH1 C: G vX>C&} ~/*3_~5k`Cꫯ>{g B# a̙g믿O>p++`Y7_t4S(#O),,4?$TӎoPŹ;wp$޿ͽK3O;.jЊK/y/rB-߿\7#9X67aWj^z%VScZlJQWza>Jؗغ/e8`\범aLLPTAZ(2J6^Rmv+M&8, I7T2qCrdNAA鐛/9÷56 d,O`]=e D[xգR79kkHkU 1)_Ѐ wI 5g2Ds,v_]x\g\ZT/cSMsQDP7;a4#ju~[m`ɘdOzq*ÈRfLseF3d>ޭ*FX*->^J]^e8gp7eRRߎD( ^t܄/m*Q)J%ZߵWRb) E`!gyok#]!\"'E3 ٢8_̅^i\G rW^y%eee!HLL䪫bĈMA(;>]& 0ґWPChwlYY7hŵꂂ->l"9q ߾[d1y~ڜ[_)3'LKkuɓ>ϛODxǎ;M\ 2oh[Ųט.X as2>pnH;SMl~INg>0>GPC0bL9ۡdg9ᄂSSN0@;lHV($|a=.#pܒK[غK&qDžIUc ;TA'>;SԔY!6;i\;  i/K2DpϮ ZU?ZC+۾M=kaO:gK¶U}SdxnZ !nF5V\a;i76hB2ӱ)A#۸7#ί ( c8x_Gr?ӉbK 8s\#K<%% O@CW 6jA-5wxѿuRK*;=?( |GC.:}J=V4OXUܕB@ x?jkkٹs'̺tCg;+xW ; (ߍe[F_WV*B s*P [h U2m-ط]÷l/A7 ?|ΡnV !~0#G UsTo d콱wN#f$f+IzNOcwjҤHݩ+?wr0KwIHs[d0 !6!r}VlC̛bױ0'TDي|H5«y[v`'Ү{{0BIzamN> Ēh}n.((X]GjMlYݰ 3V"Kjnp!O]B),,駞gz.q`w lOUfԾc@_ݥ R x4؞uc.m r4̵4>zGF/NymgoϒfE.rIoTڟE;/UXI"A:.i!|) 2[4bkCH $L@ qmGsL\ZBq\ >vdu `͗$ʮqǙw{0kj .0x~/pTUkϾWkBkg03D!DyV}om<=o믳jx~.D^xr$]ʼŵ KTC|%>8^kiX1CZ#2ʡIm̓ڸ1A*?{_`n7yHIƄ;$}Wg2/:-h@8~Ϧ)1سoɢѱؾ^wj" j ZoX7k ɘr)wQT]S[xυ"ә Ί[{Pn2T@)ĒuMD]6;M~$- mJII<{cRᑶߝz wXof7ķǤXOcZ&}Ѻ>* uuu\wuvi:^4%z5Faw}R$wj| )((x<LWPP:L \ԜI_GrS;Ӈqi2+3Bq3Cܓ~a'PJ<'nJT:#T ]Ӡ֭j4RBMWtl[[u֙8a(Mku6ڣ)@ q6~1%05b:B'e^V )3h`fE8_r!HljF]iɆ^(R8i/GV`;kegL.׸Ts IDATpC VAF0(0`?|w[mo{.jADAZOڱM+MBÅHL ub *c.ƺc,.i1Laz-NK*))$54oMUUW\TVVMVV6mc|M e!4oA羧ߵ&ȷ0XPPT!q"+`bF\DZ:{uh$rUMXpxo,cݰWCZˮ@eU{;!dMv'܃#%mm('Xƺ PCJ Z Ӿ yC$"=5Ѭyk H WZ]LiSo*{rwy}CR]6ggSu5Mw)\J1yQc?"<:A|OU'kN)^Aݰ9w;zkΒccX@ HR`Gm.B | k:Ŧ\1Cr 9N6*R~~>?0m=3fikl߾D͛GVV'NdĉG>q`/922d]KQ" 4cgo),, a0* ,uv͆ 0aRZZޒtNҎyBZ3xp {θ_|lW:]uio15H҇+0~/K>Er Uǣ+xUHȊ[gbJsp-{TBOfsc N,`胸 ސۏӏӁp;&Im)[-xc!AT򿵎FcJ o^c00xh exv*((U ɲɷM;oD < ߌ_iGo74S ,1X0kSPg}Fծ ^*'+V h9\pYJʮ]l 9jY_ 9Sai,VVW+3d|9:]pEnsMYqdcO 68b;IECO[@3/`._p[I,YG8{X8QYhv~rBWRmklz^W%mN:QB1)-!&>#z"@ ].['!OAb^f/q9ޣ-gJ$c\TsG|sM_0u+o7#߂ER*4UdDAAB8Ɵ?7bEzw*N p%#진!0e1n8:|ehwuqPmip~Uƣ.UOWe*L5pU@+*aD1`jg8e5KrH _|Pm XJNz)Y|"bSM"( ̀dPP썸0 87G= @FU W"v 4 ,.@t4> 0d$tK?F#:M73d_O v'uc?dɒ%m6^z%}~_i&V\ʕ+[~a}Qz){96n}󎃭y^ UKUzp~#/gs򽲣[XX>n:ppgƟ:WO0 yxH`?ɔ!B=n߿5i`q6FB>썸6O)^qT6!ވ1aE/_oMY.; ;RӗB[iM7;L7&QZ&h* ?bC_ 'wuIٿ"W|\wuM͞=eLn"9Nzz:m"=,|bEt,Ksȝ/.WIbk)a!׌n& Z˔o\Om?\sGpx<.[tW]{XCR0FSyH=L.x%~jo쀩eIqc`8n 96WuJJ&I\n0\0hô k iYWO4LN~%~sMlMou!c4&T GbrhRez=85LS'$a\hT/v?݌;+$, 6G_4_#MֺP߿rqff&E/ąkbVYnWApw]MK3#.|$u*{X < khcQE Uo`C0.v(`g Ɇ-iJPVv3`*,l([k(Xj?]rhçTQy9xK<(9GÌ*+quU* l(-}v:h[Ͷ9+QOK!2=,$O`jMuc6&xċ'ƕjKH\ |H0G|2`Kp"'VΉ~&NmHl0ҎGd׌+J'lS  J[֩u|B2SO?X#`sxe /4XM- WmaSs4kTd1>vT}jf X,YѶq}4n6O[oo:xÆU:_vHݰ ңʜ)]vI"5My|4<1J E^[ 9ɦj*{ 3b má{f+ /Ҳ$;$J>xKdF$ o x-4ʎtlR'cK)j=jکaܕ:AG{9 aUJS,fo$nux}gz{-:<գ Uȟ\K"D# I2{h{Q=Oį54&*&:d"^_L'=iʟX2Z V+;vn>xYqGe{#L<2N$D'$*,"]D⮁^t1mee%唕a]GGF;d|g,83p}O/*X>nI鶛4x*௼⸎Ky}lnyn{4 7ZɆgp&~JK[VR?BY$MJb•U6Qkro^h3i;PRƎ"A,:WPm)XdѤJ\dTvbɷؾ 3/UsT^Q 瀀W&ͮAU}Z-Sףu'%_dp!#n]8jLc<@5xMxS(sWzKv#|i L7c=Q\ʮ]7i$fP^{x%GuF7_/Y{q^ >J,f&}!ɿ/pYgrofmKSL_:$wgƣ M+v(`o" lscVǶnoi̹f~MI한66ذJ# i0,!$4ԈNErWThBz@#3/QQcfڸ+[.h(PChIpxnb6ӣL1]V ݳr~=XVtcHX=eyDG5D݂co-]# \pj֯_tv65Oۻ8\m](XqĔQD[42-qI{z7_\}֯_Ϲ>`Zt]oQ)pҤI 2)%Bk,Z{cc3pb&{@GI'wTUvMo]n&N-߿xCGKE ?RY=&G la@ix-x5., 9 {5yQp96#$)54*[㨞ٵY}oX\:Eyk.z@-t9/&SrFd/ߨuQ}{&p~$au7#+TSSb"0%=PwP7h8X?hTXz &0Аh8%&+B/fǦX[uЅNLŁ+ -ݏnw ֨m~iw>r8~k^*!, v&߸(ʀV~(?m݂DI9 dO뱯C.8wNN' ~1EPO佃CH&nvq@5jLLvr]=u`4*CUE{߮Kڸ㚿%tFQC<$c"ETZ4rK`#-́BSTitt=57n̓KaL'GռJh44B9Lđ6tp$艘.C^OMu =Ps!ǟj0G0-ۅ[Yw6!۶X,>UJjҗeR OIwޚߞ6<X Etpa\MMG3,Mav!቟iS_s3CIiSq"Z-mF$&ŕBĎ/{{O {~ֳˇ}{ŸPA=Z|)M뽑^,U߿y@[ i8A lȗ;vN(K_D͂˾JWJݗer㮻:ߓR_^ _fE/zqVMF<^μXkW=ovVu[C@} (TT9pCUt-P1HIE/8FsNOee%`)%>}ljӋ^]G Ym= oUߚR'R9Wk!tp{WS_o_=O>$~#GqyU^GÑ#`AZ'#μ\ ׏XUqh'lneڿ]ɩi9O\μ|Uwֹ~oJ5; (ugnC`/ƧqTq>0զ7[WZ@FMJ;Œ~ 6nߘ?&!mu#amLI1ߘu,oޝ55tIzaaa9_J[oŭފ_o?~-`wٴr b 3dhYX _~$⏩i-Z믿NCCCm+FU>b_Fɛ ~o]|{~<Wk]y E5Δ#})}v*Ϳj`+' zfk$.qM NہP\ښo67_8ZOZmkJm+X~~~qdߑ{Iߞ]ѷdׄc%?8HJJv75,L$##X,+tMZ|iSK_{o9g֓> E7ŕqy@k5ܯݾlbmѣYzQ@s,7>:kW=._=8j8ߛnxs!Ny@n[StT| dk[@z)BϏ9'Q}'4l+`W.gsNoE[߸1gOOP.'O^X7qoHn4SRSSCii)@E5Ϥ$|>q6~KKEjj*K.eСjB y'5k}W FfӦ"OV+ʛ4QFw&~i?jR#6Dp$xt"pig:Ae>/풒yWk{{Rn^Ep* '[9v :4ILL׿5֭cӦM̟?g}<L0֮]˴iӈD"]>ŋ1b6D>nhi0M!@^{ 3f`ƌ /HKKs% |e:=1JWF Q@f4T~;qmYI7@^T cRցV" \ʝ=*h]Rv`G"\pk׮e!x<8pI&nݺc[|޷Oԙb -ͬkGX*2'ut{xȧu4Ld}a˖-3UV5iuuu׏H$Bjj*a0uTYv-}ŶmnW@FH 0my3vX^WD"MϞ={[f <|k_N`wY&ͷڣ?_EqRrPćK1 owb׻bV$IGHM(>&DmoKAmLeKH Ed3ga0c H))**?䢋.jW,s$] ƶ౸U硸}_Qӿ!n1I|? < ,F_OdtF%cqqގ-gYElx@@bJRL´SaJ3PRzs?t 4e#[r޲ۀ߼\l>|P~b"~Dɟ/VV.WO lmދX hooG?QtttoK//zTUoUCz+P^O%zW#V*\k:y} b ~}^ @;Y2XmSSSd2 Ya&A@Xmۈbؽ{7vڅg}iPK^T^BS oqg?//EA{{;|A|޽x;.<Ýwމ7MxЇՅ\}{=߭OlSQ?=s[:ox$dew,'VV_[֜Y~ތ nsOـ宼ЮU@5:r;---|2.^FH{A"__s#p |Yԓ,kX8(Wwx;t_hđ$ ssslqe$  tk\g!IZOÖaŋO~8{lwM7wwߏ߁$I򗿌/| 0Mԧ> cTq||<TUeTCVN|6`˟|3FH_rH2c\_/ GY2pfP4TZ9[nAKK b1D"WBQtvvP(Օ~ˊ xaUVXq}gJի.|Gi XF( RvYvui$IBT,ˈD"me= )* mۈ4|Ÿ@e9s&,_3(x $ À(Go6>O~7q|ӟ~xN]W&gE^f~WƊ1B'M~]ՈIة-||jǫZh =яz(H^TZP4AfeOs]4F)Կv?${mPU5\vߵk:;;199V8dW芰E;/Z=zԧpe(Ox} tw(xbk =^|EtwwСCx1;; 0p*sW_,be% Y >z<bB=V6+-$*U ء~o>[~+JBaNP^D뎫G?xnB4Cێ?yg%X g Oï~[u?Qy}킷v?lpDQ(owvvbffiqXՔ%jt+aہ;[oى'D"$,'?I!.]OS;v gqw㮻Bkk+^~ec=K>{q|իWw^LOO?GFonox$&,o2O,{c6- $h,;}yfhe=~o௑z=hdFK_GvI:::0>>o,̙3سg4MÏc>}X y{f?Z>@CCwuWM ._2N]‡>!9sT ?OQ.nV8tv؁;v`xxo|x[Ѿɓ?-M<|,H/*S0Y.]]޿P &@]~Dp->ebXk~V G"o$xCc?_((?z5 hdFv \sG?~Յ9ض__ٳgO"gr >ܹs/D"> G?8X }/&''m Y  ۷/Ν;08ܹsFhmm]w݅~ݛ/w^}r+hkX8 ow@BgWJ% ˱ߟ>{}9KU?447GyaxߖN3u.e| |G/^%x_e@oϕJePN\fďogd;B_qxgyO>$.\> YiR$Ii(4 "… {ow ҄ އ3VTò8%WPW?>%*Ž]=;}94oߎ*]KAwAxse,$^:p$?[WZC?>__EWW^y<䓰mO<>a޽a݀|]w݅__N:;Ǐi_E؍^g]& J#P4"hSG[ (r};ξSKe;?)lM67lwnݻT e~7,W}cH&O~}s={ .] ⡇BP~=zgp}W_ťK|;nÿKD./ W|P=p@`羏8#Vq?ǞM߿[ewG;oz~AqGO>?/7ϣv>*=Ve{l*Mtb8(l?xBGgkS_ߺ7? /l&[yzkx۶m|Ư_*ẃ x]qJW\m|3A&.$/2\9 ͘/ v;:V,>r"o?\>mmmv /l}w-FN>/ש T  GPu 8enߝq#Gu&DT~vmpw;\:;wv?@E {X8gތA%I|s_OulO>urv{ޕJ] wVX~Kz?%MkO%P*/Ӊ_1Kߪ\V/q`5uhFgogbEoo5z2Xxehz|{V@{,~9[_''{z3˿$VU^#@AժwT.~:}kw= & SX>ovbzzN>]H+ٓ=~C}*^=\~]-dd'Ch']V6紕k=w|nߺ~ b[7|3 ".^۶q-|˗Æ\,G1??AŽr{Akk+'oiY2= 2dT>MmS5݁k_ym}7qBֳo> O~@\+JpP~VmD"C4HyJ%#LBxQa6< [k;=`{^NB EBA2ʎ†4,<(e("KyLOn]ׅiaWyXX,H$qpʕI8PmB۶iZ/ ߙQR[t@UDT <ի, xՋa @B5=˲ ].(fPWd:;BPgމD$aff/n6q(VLOOT*#C4p?χ?Hq8 $I D"Y=  y(m* X~@0,6r\˅nMBt#™~fBJ@:a@$nL/ᄒfz+˯mm§VAcIT\_fQ$E+hjP6dMEH,cn~w($f?qʲ~@+Ű]\EQ¥yu,[.]yp]b1| T6= ope̟3!T]C nɅ=c4QD⍭Rpy.2`Oj`BkA$K%bD5]^1$); yvIFiN yp"Қq" J$ɅH߼# ;=f,E?Tn(0._t>z0y%s#``Ϟ=PUb1,u r5 (b*сd2.(( E{@ii0^@[a])*AI#Rհ.-g mo @~d@u˳<3/ʄ?Oۇ?vv6n" pBM6w$XX~ VTD3Br"%bWY)EȪE-l(ͫ%Hua ']_4'׽%0U\%{,߻d`Wmmm,+F, ?$ ǩqɲbRRQ( 4Mib(JAgggm_X.JW 2\ۅ<<ǟ+IPwPvh5xP*› XFHxb9 p+~ h[ IDATu?S9S[e̙Q|?zw!RUSg"g!gE%Ē1V3۲¿[ gxB]Dk/ 1< AbuQɃ$ """% &DeWv(\,˂q2TEA2X4(aꕙN؟yP---0MxmmmP$sssr(a`sNp_X|>RT2ri-U- T][گ}o!OTQ/ /Dc;;;GF%`,eUB:.DE|B -i$6MwwIry:L\(wV( 8p`0c 5xQEa\a[?o@Ԥp@G|O<&JCY(\l#  ]S8C(Ku(iPWv k%)KbV~JU:UK"rK ,X));J.Yg`jzzռ۶6 jmm)d@4\?;; 0zHR $[?H.emC+3:<(@گ{ },/\߭/J՞Z x:HFH&iV/J"dYڒCu`Uiq_ %!+WLs]wI7%%"˗qq| UUEW*.W Ah4ZrȲH$bX,V8DQ AF>sW@T~ꙺWy0 A~1@`qU|J`Qb[g@X)׏'JlU87k./{U++u t<~}UZzW.1]XDu]VS%<ǟH$"D,LNN\.iTJX mmm󘝝EXD{{;8l}5jp=1l[3_6@ __`Ȼ@3 d<<ؿ>_mUME.#A%%Dc-?o1>c@\ u' ౫rM+ PE"$]%W*tE#?Smj$IB<|> d ^$IDPOݻwqLOO#χP(®A,C$A.åKz EGBA0ٯ:FWW}¹lA씁)[ W&&ĵsggCIT2vx<vDTfggw^XASY(Jy133&+Aa؃r=+O(O21*֌VE 5~V+5JE OU~&}aC4<<\1&! v_5$(2✶@] @E/YD\hu &]__nT(s͈b̼2( mzjPblGG4M(H$hooHRBGGGTTD"عs'ى6BءY. e 1!s(](ra@uaUT^TBR Wlf?W o6-ȫ e krh[FT:073\!Aʰ <!R\w-u"( HWE@] S}nf$ՁU9 `6^x1< "(Ҵaffb1lD%|>d2 `F 8- 28N JdXX,bΝ6<w! P uo,B#ROJ6` @uu)ɻ .V ޼ K3V {%fo! ¶lί9hMMN# ٞD-H,)sŕz%cLVVg!"XC]p bi"dHo$@\% |8y_&@8Dam`/>@:dr|>BA0;;`~~33ttt,J%#[ O%]μKy@[Pn|g$@=ʗEWE;oʅ8nZJ}7&y3C!_DhAEUЦbRnC]DE#**K?$\JĆ)/<=hWM^87] v&m%W6C\p ]Uݔ𖤃y3MA4+}Uꕂy?Xi$i<0j}A_Kv킦ism;l$2; "fggT*a׮]#B^ey_IX(gR\.'gC!U|D@qi<xRZ$^gB6bRZTEиZV*5gtK %b/" 7*zHH Uic" CZq.w?o]4O’E(0Vw$ xY xk,*JXwqaDvb1EJ@Cze@9P3P(077D"D"UU+˗tBLByg e׫ 2  B/81`@i硘/5jԆ8~@Ձ(:f!H lps %2hrZe *@$҂x<_ BEyAP(`YvY4"@)IJ!*.m d@쒪N,*5\;Ƿw#9? !E]huC1p333Z"bL^@uuD"bUUkN9^+9 ᩄ`lPӾ'cbQ=/t /.jGh}oBZ+q- G,BP@"EXmѿ oq o@Ӵ0X* UAO^߫YLֿ᷄%m%!wpԿ|/!du _~~KzO&ﯹJ0<< 0066%TO?_>s۶tt:]냪d2fFooo~F駟~t7gy>%#3`7g7@B1 u{Bnv-|^?!48yy=& }{B_ `g7ֿۆa@XZ2H$iZxlV5O?׆Ss2uŷ=ߏL&S"J|=Ju5Ġ~駿Mon̗\.#G@U՚BMQPX:֜g~iM2VBDXڲSccc8uԒ(`VO?׈ ?yhhzןL&166X07Z6E:;RUGKUo<4qiO?7] \,W ز,L&l6nYO?O? CCC!rCJBB!0 Be+.9a n$ h[U\O?Os5>zhxn#L&l6nYO?O? CCC!rCJBB!0 Be+.9a n$ h[U\O?Os5>zhxn#L&l6nYO?O? CCC!rCJBB!0 Be+.9a n$ h[U\O?Os5>zhxn#>>>>>{8yS$ʲL :zW&@""""""b ""dns^Ղ]BpW!tx}{{ۿp$*BUURrRWWWZ#ޫ:]__3ZDT^]ι gExݎ6mǽ?۶G Q,Ktss3z:JM+v웰֪,K5Mo 7}uޱ~]?g4ڶۭٙ=SD dxb4MtO:y;u-c...$Bޫi sϻ%*EQ4MUZ~al$""b """"""b """"""b """"""b """"""b """""":i_ukcXAl1ZZczss*\Y:UUKhj6key?M4M#km񮺮DZPAu]AJY{^tzg1rΩѭB?DQ,2giZvg \zon3즐b!T2|8a(RQrW۶l6{SL>>>>>>{8yS$ʲL :zW&@""""""b ""dns^Ղ]BpW!tx}{{ۿp$*BUURrRWWWZ#ޫ:]__3ZDT^]ι gExݎ6mǽ?۶G Q,Ktss3z:JM+v웰֪,K5Mo 7}uޱ~]?g4ڶۭٙ=SD dxb4MtO:y;u-c...$Bޫi sϻ%*EQ4MUZ~al$""b """"""b """"""b """"""b """"""b """""":i_ukcXAl1ZZczss*\Y:UUKhj6key?M4M#km񮺮DZPAu]AJY{^tzg1rΩѭB?DQ,2giZvg \zon3즐b!T2|8a(RQrW۶l6{SL>>>>>>{8yS$ʲL :zW&@""""""b ""dns^Ղ]BpW!tx}{{ۿp$*BUURrRWWWZ#ޫ:]__3ZDT^]ι gExݎ6mǽ?۶G Q,Ktss3z:JM+v웰֪,K5Mo 7}uޱ~]?g4ڶۭٙ=SD dxb4MtO:y;u-c...$Bޫi sϻ%*EQ4MUZ~al$""b """"""b """"""b """"""b """"""b """""":i_ukcXAl1ZZczss*\Y:UUKhj6key?M4M#km񮺮DZPAu]AJY{^tzg1rΩѭB?DQ,2giZvg \zon3즐b!T2|8a(RQrW۶l6{SL>>>>>>{8yS$ʲL :zW&@""""""b ""dns^Ղ]BpW!tx}{{ۿp$*BUURrRWWWZ#ޫ:]__3ZDT^]ι gExݎ6mǽ?۶G Q,Ktss3z:JM+v웰֪,K5Mo 7}uޱ~]?g4ڶۭٙ=SD dxb4MtO:y;u-c...$Bޫi sϻ%*EQ4MUZ~al$""b """"""b """"""?>]GQIENDB`tiled-qt-0.9.1/src/plugins/flare/000077500000000000000000000000001217502731700166125ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/flare/flare.pro000066400000000000000000000003161217502731700204250ustar00rootroot00000000000000include(../plugin.pri) DEFINES += FLARE_LIBRARY greaterThan(QT_MAJOR_VERSION, 4) { OTHER_FILES = plugin.json } HEADERS += \ flare_global.h \ flareplugin.h SOURCES += \ flareplugin.cpp tiled-qt-0.9.1/src/plugins/flare/flare_global.h000066400000000000000000000020401217502731700213700ustar00rootroot00000000000000/* * Flare Tiled Plugin * Copyright 2010, Jaderamiso * Copyright 2011, Stefan Beller * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef FLARE_GLOBAL_H #define FLARE_GLOBAL_H #include #if defined(FLARE_LIBRARY) # define FLARESHARED_EXPORT Q_DECL_EXPORT #else # define FLARESHARED_EXPORT Q_DECL_IMPORT #endif #endif // FLARE_GLOBAL_H tiled-qt-0.9.1/src/plugins/flare/flareplugin.cpp000066400000000000000000000303111217502731700216240ustar00rootroot00000000000000/* * Flare Tiled Plugin * Copyright 2010, Jaderamiso * Copyright 2011, Stefan Beller * Copyright 2011, Clint Bellanger * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "flareplugin.h" #include "gidmapper.h" #include "map.h" #include "mapobject.h" #include "mapreader.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include "objectgroup.h" #include #include #include #include #include #include using namespace Flare; using namespace Tiled; FlarePlugin::FlarePlugin() { } Tiled::Map *FlarePlugin::read(const QString &fileName) { QFile file(fileName); if (!file.open (QIODevice::ReadOnly)) { mError = tr("Could not open file for reading."); return 0; } // default to values of the original flare alpha game. Map *map = new Map(Map::Isometric, 256, 256, 64, 32); QTextStream stream (&file); QString line; QString sectionName; bool newsection = false; QString path = QFileInfo(file).absolutePath(); int base = 10; GidMapper gidMapper; int gid = 1; TileLayer *tilelayer = 0; ObjectGroup *objectgroup = 0; MapObject *mapobject = 0; bool tilesetsSectionFound = false; bool headerSectionFound = false; bool tilelayerSectionFound = false; // tile layer or objects while (!stream.atEnd()) { line = stream.readLine(); if (!line.length()) continue; QChar startsWith = line.at(0); if (startsWith == QChar('[')) { sectionName = line.mid(1, line.indexOf(QChar(']')) - 1); newsection = true; continue; } if (sectionName == QLatin1String("header")) { headerSectionFound = true; //get map properties int epos = line.indexOf(QChar('=')); if (epos != -1) { QString key = line.left(epos).trimmed(); QString value = line.mid(epos + 1, -1).trimmed(); if (key == QLatin1String("width")) map->setWidth(value.toInt()); else if (key == QLatin1String("height")) map->setHeight(value.toInt()); else if (key == QLatin1String("tilewidth")) map->setTileWidth(value.toInt()); else if (key == QLatin1String("tileheight")) map->setTileHeight(value.toInt()); else map->setProperty(key, value); } } else if (sectionName == QLatin1String("tilesets")) { tilesetsSectionFound = true; int epos = line.indexOf(QChar('=')); QString key = line.left(epos).trimmed(); QString value = line.mid(epos + 1, -1).trimmed(); if (key == QLatin1String("tileset")) { QStringList list = value.split(QChar(',')); QString absoluteSource = path + QLatin1Char('/') + list[0]; int tilesetwidth = 0; int tilesetheight = 0; if (list.size() > 2) { tilesetwidth = list[1].toInt(); tilesetheight = list[2].toInt(); } Tileset *tileset = new Tileset(QFileInfo(absoluteSource).fileName(), tilesetwidth, tilesetheight); bool ok = tileset->loadFromImage(QImage(absoluteSource), absoluteSource); if (!tileset || !ok) { mError = tr("Error loading tileset %1, which expands to %2. Path not found!") .arg(list[0], absoluteSource); delete map; return 0; } else { if (list.size() > 4) tileset->setTileOffset(QPoint(list[3].toInt(),list[4].toInt())); gidMapper.insert(gid, tileset); if (list.size() > 5) { gid += list[5].toInt(); } else { gid += tileset->tileCount(); } map->addTileset(tileset); } } } else if (sectionName == QLatin1String("layer")) { if (!tilesetsSectionFound) { mError = tr("No tilesets section found before layer section."); delete map; return 0; } tilelayerSectionFound = true; int epos = line.indexOf(QChar('=')); if (epos != -1) { QString key = line.left(epos).trimmed(); QString value = line.mid(epos + 1, -1).trimmed(); if (key == QLatin1String("type")) { tilelayer = new TileLayer(value, 0, 0, map->width(),map->height()); map->addLayer(tilelayer); } else if (key == QLatin1String("format")) { if (value == QLatin1String("dec")) { base = 10; } else if (value == QLatin1String("hex")) { base = 16; } } else if (key == QLatin1String("data")) { for (int y=0; y < map->height(); y++) { line = stream.readLine(); QStringList l = line.split(QChar(',')); for (int x=0; x < qMin(map->width(), l.size()); x++) { bool ok; int tileid = l[x].toInt(0, base); Cell c = gidMapper.gidToCell(tileid, ok); if (!ok) { mError += tr("Error mapping tile id %1.").arg(tileid); delete map; return 0; } tilelayer->setCell(x, y, c); } } } else { tilelayer->setProperty(key, value); } } } else { if (newsection) { if (map->indexOfLayer(sectionName) == -1) { objectgroup = new ObjectGroup(sectionName, 0,0,map->width(), map->height()); map->addLayer(objectgroup); } else { objectgroup = dynamic_cast(map->layerAt(map->indexOfLayer(sectionName))); } mapobject = new MapObject(); objectgroup->addObject(mapobject); newsection = false; } if (!mapobject) continue; if (startsWith == QChar('#')) { QString name = line.mid(1).trimmed(); mapobject->setName(name); } int epos = line.indexOf(QChar('=')); if (epos != -1) { QString key = line.left(epos).trimmed(); QString value = line.mid(epos + 1, -1).trimmed(); if (key == QLatin1String("type")) { mapobject->setType(value); } else if (key == QLatin1String("location")) { QStringList loc = value.split(QChar(',')); mapobject->setPosition(QPointF(loc[0].toFloat(), loc[1].toFloat())); if (loc.size() > 3) mapobject->setSize(loc[2].toInt(), loc[3].toInt()); else mapobject->setSize(1, 1); } else { mapobject->setProperty(key, value); } } } } if (!headerSectionFound || !tilesetsSectionFound || !tilelayerSectionFound) { mError = tr("This seems to be no valid flare map. " "A Flare map consists of at least a header " "section, a tileset section and one tile layer."); delete map; return 0; } return map; } bool FlarePlugin::supportsFile(const QString &fileName) const { return QFileInfo(fileName).suffix() == QLatin1String("txt"); } QString FlarePlugin::nameFilter() const { return tr("Flare map files (*.txt)"); } QString FlarePlugin::errorString() const { return mError; } bool FlarePlugin::write(const Tiled::Map *map, const QString &fileName) { QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { mError = tr("Could not open file for writing."); return false; } QTextStream out(&file); out.setCodec("UTF-8"); const int mapWidth = map->width(); const int mapHeight = map->height(); // write [header] out << "[header]\n"; out << "width=" << mapWidth << "\n"; out << "height=" << mapHeight << "\n"; out << "tilewidth=" << map->tileWidth() << "\n"; out << "tileheight=" << map->tileHeight() << "\n"; // write all properties for this map Properties::const_iterator it = map->properties().constBegin(); Properties::const_iterator it_end = map->properties().constEnd(); for (; it != it_end; ++it) { out << it.key() << "=" << it.value() << "\n"; } out << "\n"; QDir mapDir = QFileInfo(fileName).absoluteDir(); out << "[tilesets]\n"; foreach (Tileset *ts, map->tilesets()) { const QString &imageSource = ts->imageSource(); QString source = mapDir.relativeFilePath(imageSource); out << "tileset=" << source << "," <tileWidth() << "," <tileHeight() << "," <tileOffset().x() << "," <tileOffset().y() << "\n"; } out << "\n"; GidMapper gidMapper(map->tilesets()); // write layers foreach (Layer *layer, map->layers()) { if (TileLayer *tileLayer = layer->asTileLayer()) { out << "[layer]\n"; out << "type=" << layer->name() << "\n"; out << "data=\n"; for (int y = 0; y < mapHeight; ++y) { for (int x = 0; x < mapWidth; ++x) { Cell t = tileLayer->cellAt(x, y); int id = 0; if (t.tile) id = gidMapper.cellToGid(t); out << id; if (x < mapWidth - 1) out << ","; } if (y < mapHeight - 1) out << ","; out << "\n"; } out << "\n"; } if (ObjectGroup *group = layer->asObjectGroup()) { foreach (const MapObject *o, group->objects()) { if ((!o->type().isEmpty())) { out << "[" << group->name() << "]\n"; // display object name as comment if (!(o->name().isEmpty())) { out << "# " << o->name() << "\n"; } out << "type=" << o->type() << "\n"; out << "location=" << o->x() << "," << o->y(); out << "," << o->width() << "," << o->height() << "\n"; // write all properties for this object Properties::const_iterator it = o->properties().constBegin(); Properties::const_iterator it_end = o->properties().constEnd(); for (; it != it_end; ++it) { out << it.key() << "=" << it.value() << "\n"; } out << "\n"; } } } } file.close(); return true; } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Flare, FlarePlugin) #endif tiled-qt-0.9.1/src/plugins/flare/flareplugin.h000066400000000000000000000033631217502731700213000ustar00rootroot00000000000000/* * Flare Tiled Plugin * Copyright 2010, Jaderamiso * Copyright 2011, Stefan Beller * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef FLAREPLUGIN_H #define FLAREPLUGIN_H #include "flare_global.h" #include "mapwriterinterface.h" #include "mapreaderinterface.h" #include #include namespace Flare { class FLARESHARED_EXPORT FlarePlugin : public QObject , public Tiled::MapWriterInterface , public Tiled::MapReaderInterface { Q_OBJECT Q_INTERFACES(Tiled::MapWriterInterface) Q_INTERFACES(Tiled::MapReaderInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapWriterInterface" FILE "plugin.json") #endif public: FlarePlugin(); // MapReaderInterface Tiled::Map *read(const QString &fileName); bool supportsFile(const QString &fileName) const; // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); QString nameFilter() const; QString errorString() const; private: QString mError; }; } // namespace Flare #endif // FLAREPLUGIN_H tiled-qt-0.9.1/src/plugins/flare/plugin.json000066400000000000000000000000301217502731700207740ustar00rootroot00000000000000{ "Keys": [ "flare" ] } tiled-qt-0.9.1/src/plugins/json/000077500000000000000000000000001217502731700164725ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/json/json.pro000066400000000000000000000004501217502731700201640ustar00rootroot00000000000000include(../plugin.pri) DEFINES += JSON_LIBRARY SOURCES += jsonplugin.cpp \ qjsonparser/json.cpp \ varianttomapconverter.cpp \ maptovariantconverter.cpp HEADERS += jsonplugin.h \ json_global.h \ qjsonparser/json.h \ varianttomapconverter.h \ maptovariantconverter.h tiled-qt-0.9.1/src/plugins/json/json_global.h000066400000000000000000000020651217502731700211370ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JSON_GLOBAL_H #define JSON_GLOBAL_H #include #if defined(JSON_LIBRARY) # define JSONSHARED_EXPORT Q_DECL_EXPORT #else # define JSONSHARED_EXPORT Q_DECL_IMPORT #endif #endif // JSON_GLOBAL_H tiled-qt-0.9.1/src/plugins/json/jsonplugin.cpp000066400000000000000000000055131217502731700213720ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "jsonplugin.h" #include "maptovariantconverter.h" #include "varianttomapconverter.h" #include "qjsonparser/json.h" #include #include #include using namespace Json; JsonPlugin::JsonPlugin() { } Tiled::Map *JsonPlugin::read(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { mError = tr("Could not open file for reading."); return 0; } JsonReader reader; reader.parse(file.readAll()); const QVariant variant = reader.result(); if (!variant.isValid()) { mError = tr("Error parsing file."); return 0; } VariantToMapConverter converter; Tiled::Map *map = converter.toMap(variant, QFileInfo(fileName).dir()); if (!map) mError = converter.errorString(); return map; } bool JsonPlugin::write(const Tiled::Map *map, const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { mError = tr("Could not open file for writing."); return false; } MapToVariantConverter converter; QVariant variant = converter.toVariant(map, QFileInfo(fileName).dir()); JsonWriter writer; writer.setAutoFormatting(true); if (!writer.stringify(variant)) { // This can only happen due to coding error mError = writer.errorString(); return false; } QTextStream out(&file); out << writer.result(); out.flush(); if (file.error() != QFile::NoError) { mError = tr("Error while writing file:\n%1").arg(file.errorString()); return false; } return true; } QString JsonPlugin::nameFilter() const { return tr("Json files (*.json)"); } bool JsonPlugin::supportsFile(const QString &fileName) const { return fileName.endsWith(QLatin1String(".json"), Qt::CaseInsensitive); } QString JsonPlugin::errorString() const { return mError; } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Json, JsonPlugin) #endif tiled-qt-0.9.1/src/plugins/json/jsonplugin.h000066400000000000000000000035751217502731700210450ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef JSONPLUGIN_H #define JSONPLUGIN_H #include "json_global.h" #include "mapwriterinterface.h" #include "mapreaderinterface.h" #include namespace Tiled { class Map; } namespace Json { class JSONSHARED_EXPORT JsonPlugin : public QObject , public Tiled::MapReaderInterface , public Tiled::MapWriterInterface { Q_OBJECT Q_INTERFACES(Tiled::MapReaderInterface) Q_INTERFACES(Tiled::MapWriterInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapWriterInterface" FILE "plugin.json") Q_PLUGIN_METADATA(IID "org.mapeditor.MapReaderInterface" FILE "plugin.json") #endif public: JsonPlugin(); // MapReaderInterface Tiled::Map *read(const QString &fileName); bool supportsFile(const QString &fileName) const; // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); // Both interfaces QString nameFilter() const; QString errorString() const; private: QString mError; }; } // namespace Json #endif // JSONPLUGIN_H tiled-qt-0.9.1/src/plugins/json/maptovariantconverter.cpp000066400000000000000000000246011217502731700236360ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "maptovariantconverter.h" #include "imagelayer.h" #include "map.h" #include "mapobject.h" #include "objectgroup.h" #include "properties.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include "terrain.h" using namespace Tiled; using namespace Json; QVariant MapToVariantConverter::toVariant(const Map *map, const QDir &mapDir) { mMapDir = mapDir; mGidMapper.clear(); QVariantMap mapVariant; mapVariant["version"] = 1.0; mapVariant["orientation"] = orientationToString(map->orientation()); mapVariant["width"] = map->width(); mapVariant["height"] = map->height(); mapVariant["tilewidth"] = map->tileWidth(); mapVariant["tileheight"] = map->tileHeight(); mapVariant["properties"] = toVariant(map->properties()); const QColor bgColor = map->backgroundColor(); if (bgColor.isValid()) mapVariant["backgroundcolor"] = bgColor.name(); QVariantList tilesetVariants; unsigned firstGid = 1; foreach (Tileset *tileset, map->tilesets()) { tilesetVariants << toVariant(tileset, firstGid); mGidMapper.insert(firstGid, tileset); firstGid += tileset->tileCount(); } mapVariant["tilesets"] = tilesetVariants; QVariantList layerVariants; foreach (const Layer *layer, map->layers()) { switch (layer->type()) { case Layer::TileLayerType: layerVariants << toVariant(static_cast(layer)); break; case Layer::ObjectGroupType: layerVariants << toVariant(static_cast(layer)); break; case Layer::ImageLayerType: layerVariants << toVariant(static_cast(layer)); break; } } mapVariant["layers"] = layerVariants; return mapVariant; } QVariant MapToVariantConverter::toVariant(const Tileset *tileset, int firstGid) { QVariantMap tilesetVariant; tilesetVariant["firstgid"] = firstGid; tilesetVariant["name"] = tileset->name(); tilesetVariant["tilewidth"] = tileset->tileWidth(); tilesetVariant["tileheight"] = tileset->tileHeight(); tilesetVariant["spacing"] = tileset->tileSpacing(); tilesetVariant["margin"] = tileset->margin(); tilesetVariant["properties"] = toVariant(tileset->properties()); const QPoint offset = tileset->tileOffset(); if (!offset.isNull()) { QVariantMap tileOffset; tileOffset["x"] = offset.x(); tileOffset["y"] = offset.y(); tilesetVariant["tileoffset"] = tileOffset; } // Write the image element const QString &imageSource = tileset->imageSource(); if (!imageSource.isEmpty()) { const QString rel = mMapDir.relativeFilePath(tileset->imageSource()); tilesetVariant["image"] = rel; const QColor transColor = tileset->transparentColor(); if (transColor.isValid()) tilesetVariant["transparentcolor"] = transColor.name(); tilesetVariant["imagewidth"] = tileset->imageWidth(); tilesetVariant["imageheight"] = tileset->imageHeight(); } // Write the properties & terrain for those tiles that have them QVariantMap tilePropertiesVariant; QVariantMap tilesVariant; for (int i = 0; i < tileset->tileCount(); ++i) { const Tile *tile = tileset->tileAt(i); const Properties properties = tile->properties(); if (!properties.isEmpty()) tilePropertiesVariant[QString::number(i)] = toVariant(properties); QVariantMap tileVariant; if (tile->terrain() != 0xFFFFFFFF) { QVariantList terrainIds; for (int j = 0; j < 4; ++j) terrainIds << QVariant(tile->cornerTerrainId(j)); tileVariant["terrain"] = terrainIds; } if (tile->terrainProbability() != -1.f) tileVariant["probability"] = tile->terrainProbability(); if (!tileVariant.empty()) tilesVariant[QString::number(i)] = tileVariant; } if (!tilePropertiesVariant.empty()) tilesetVariant["tileproperties"] = tilePropertiesVariant; if (!tilesVariant.empty()) tilesetVariant["tiles"] = tilesVariant; // Write terrains if (tileset->terrainCount() > 0) { QVariantList terrainsVariant; for (int i = 0; i < tileset->terrainCount(); ++i) { Terrain *terrain = tileset->terrain(i); QVariantMap terrainVariant; terrainVariant["name"] = terrain->name(); terrainVariant["tile"] = terrain->imageTileId(); terrainsVariant << terrainVariant; } tilesetVariant["terrains"] = terrainsVariant; } return tilesetVariant; } QVariant MapToVariantConverter::toVariant(const Properties &properties) { QVariantMap variantMap; Properties::const_iterator it = properties.constBegin(); Properties::const_iterator it_end = properties.constEnd(); for (; it != it_end; ++it) variantMap[it.key()] = it.value(); return variantMap; } QVariant MapToVariantConverter::toVariant(const TileLayer *tileLayer) { QVariantMap tileLayerVariant; tileLayerVariant["type"] = "tilelayer"; addLayerAttributes(tileLayerVariant, tileLayer); QVariantList tileVariants; for (int y = 0; y < tileLayer->height(); ++y) for (int x = 0; x < tileLayer->width(); ++x) tileVariants << mGidMapper.cellToGid(tileLayer->cellAt(x, y)); tileLayerVariant["data"] = tileVariants; return tileLayerVariant; } // TODO: Unduplicate this class since it's used also in mapwriter.cpp class TileToPixelCoordinates { public: TileToPixelCoordinates(Map *map) { if (map->orientation() == Map::Isometric) { // Isometric needs special handling, since the pixel values are // based solely on the tile height. mMultiplierX = map->tileHeight(); mMultiplierY = map->tileHeight(); } else { mMultiplierX = map->tileWidth(); mMultiplierY = map->tileHeight(); } } QPoint operator() (qreal x, qreal y) const { return QPoint(qRound(x * mMultiplierX), qRound(y * mMultiplierY)); } private: int mMultiplierX; int mMultiplierY; }; QVariant MapToVariantConverter::toVariant(const ObjectGroup *objectGroup) { QVariantMap objectGroupVariant; objectGroupVariant["type"] = "objectgroup"; if (objectGroup->color().isValid()) objectGroupVariant["color"] = objectGroup->color().name(); addLayerAttributes(objectGroupVariant, objectGroup); QVariantList objectVariants; foreach (const MapObject *object, objectGroup->objects()) { QVariantMap objectVariant; const QString &name = object->name(); const QString &type = object->type(); objectVariant["properties"] = toVariant(object->properties()); objectVariant["name"] = name; objectVariant["type"] = type; if (object->tile()) objectVariant["gid"] = mGidMapper.cellToGid(Cell(object->tile())); const TileToPixelCoordinates toPixel(objectGroup->map()); const QPoint pos = toPixel(object->x(), object->y()); const QPoint size = toPixel(object->width(), object->height()); objectVariant["x"] = pos.x(); objectVariant["y"] = pos.y(); objectVariant["width"] = size.x(); objectVariant["height"] = size.y(); objectVariant["visible"] = object->isVisible(); /* Polygons are stored in this format: * * "polygon/polyline": [ * { "x": 0, "y": 0 }, * { "x": 1, "y": 1 }, * ... * ] */ const QPolygonF &polygon = object->polygon(); if (!polygon.isEmpty()) { QVariantList pointVariants; foreach (const QPointF &point, polygon) { const QPoint pixelCoordinates = toPixel(point.x(), point.y()); QVariantMap pointVariant; pointVariant["x"] = pixelCoordinates.x(); pointVariant["y"] = pixelCoordinates.y(); pointVariants.append(pointVariant); } if (object->shape() == MapObject::Polygon) objectVariant["polygon"] = pointVariants; else objectVariant["polyline"] = pointVariants; } if (object->shape() == MapObject::Ellipse) objectVariant["ellipse"] = true; objectVariants << objectVariant; } objectGroupVariant["objects"] = objectVariants; return objectGroupVariant; } QVariant MapToVariantConverter::toVariant(const ImageLayer *imageLayer) { QVariantMap imageLayerVariant; imageLayerVariant["type"] = "imagelayer"; addLayerAttributes(imageLayerVariant, imageLayer); const QString rel = mMapDir.relativeFilePath(imageLayer->imageSource()); imageLayerVariant["image"] = rel; const QColor transColor = imageLayer->transparentColor(); if (transColor.isValid()) imageLayerVariant["transparentcolor"] = transColor.name(); return imageLayerVariant; } void MapToVariantConverter::addLayerAttributes(QVariantMap &layerVariant, const Layer *layer) { layerVariant["name"] = layer->name(); layerVariant["width"] = layer->width(); layerVariant["height"] = layer->height(); layerVariant["x"] = layer->x(); layerVariant["y"] = layer->y(); layerVariant["visible"] = layer->isVisible(); layerVariant["opacity"] = layer->opacity(); const Properties &properties = layer->properties(); if (!properties.isEmpty()) layerVariant["properties"] = toVariant(properties); } tiled-qt-0.9.1/src/plugins/json/maptovariantconverter.h000066400000000000000000000036241217502731700233050ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef MAPTOVARIANTCONVERTER_H #define MAPTOVARIANTCONVERTER_H #include #include #include "gidmapper.h" namespace Tiled { } namespace Json { /** * Converts Map instances to QVariant. Meant to be used together with * JsonWriter. */ class MapToVariantConverter { public: MapToVariantConverter() {} /** * Converts the given \s map to a QVariant. The \a mapDir is used to * construct relative paths to external resources. */ QVariant toVariant(const Tiled::Map *map, const QDir &mapDir); private: QVariant toVariant(const Tiled::Tileset *tileset, int firstGid); QVariant toVariant(const Tiled::Properties &properties); QVariant toVariant(const Tiled::TileLayer *tileLayer); QVariant toVariant(const Tiled::ObjectGroup *objectGroup); QVariant toVariant(const Tiled::ImageLayer *imageLayer); void addLayerAttributes(QVariantMap &layerVariant, const Tiled::Layer *layer); QDir mMapDir; Tiled::GidMapper mGidMapper; }; } // namespace Json #endif // MAPTOVARIANTCONVERTER_H tiled-qt-0.9.1/src/plugins/json/plugin.json000066400000000000000000000000321217502731700206560ustar00rootroot00000000000000{ "Keys": [ "notused" ] } tiled-qt-0.9.1/src/plugins/json/qjsonparser/000077500000000000000000000000001217502731700210415ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/json/qjsonparser/json.cpp000066400000000000000000000327141217502731700225250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (c) 2010 Girish Ramakrishnan ** ** Use, modification and distribution is allowed without limitation, ** warranty, liability or support of any kind. ** ****************************************************************************/ #include "json.h" #include "jsonparser.cpp" #include #include /*! \class JsonReader \reentrant \brief The JsonReader class provides a fast parser for reading well-formed JSON into a QVariant. The parser converts JSON types into QVariant types. For example, JSON arrays are translated into QVariantList and JSON objects are translated into QVariantMap. For example, \code JsonReader reader; if (reader.parse(QString("{ \"id\": 123, \"class\": \"JsonReader\", \"authors\": [\"Denis\",\"Ettrich\",\"Girish\"] }"))) { QVariant result = reader.result(); QVariantMap map = result.toMap(); // the JSON object qDebug() << map.count(); // 3 qDebug() << map["id"].toInt(); // 123 qDebug() << map["class"].toString(); // "JsonReader" QVariantList list = map["authors"].toList(); qDebug() << list[2].toString(); // "Girish" } else { qDebug() << reader.errorString(); } \endcode As seen above, the reader converts the JSON into a QVariant with arbitrary nesting. A complete listing of JSON to QVariant conversion is documented at parse(). JsonWriter can be used to convert a QVariant into JSON string. */ /*! Constructs a JSON reader. */ JsonReader::JsonReader() { } /*! Destructor */ JsonReader::~JsonReader() { } /*! Parses the JSON \a ba as a QVariant. If the parse succeeds, this function returns true and the QVariant can be accessed using result(). If the parse fails, this function returns false and the error message can be accessed using errorMessage(). The encoding of \ba is auto-detected based on the pattern of nulls in the initial 4 octets as described in "Section 3. Encoding" of RFC 2647. If an encoding could not be auto-detected, this function assumes UTF-8. The conversion from JSON type into QVariant type is as listed below: \table \row \o false \o QVariant::Bool with the value false. \row \o true \o QVariant::Bool with the value true. \row \o null \o QVariant::Invalid i.e QVariant() \row \o object \o QVariant::Map i.e QVariantMap \row \o array \o QVariant::List i.e QVariantList \row \o string \o QVariant::String i.e QString \row \o number \o QVariant::Double or QVariant::LongLong. If the JSON number contains a '.' or 'e' or 'E', QVariant::Double is used. \endtable The byte array \ba may or may not contain a BOM. */ bool JsonReader::parse(const QByteArray &ba) { QTextCodec *codec = QTextCodec::codecForUtfText(ba, 0); // try BOM detection if (!codec) { int mib = 106; // utf-8 if (ba.length() > 3) { // auto-detect const char *data = ba.constData(); if (data[0] != 0) { if (data[1] != 0) mib = 106; // utf-8 else if (data[2] != 0) mib = 1014; // utf16 le else mib = 1019; // utf32 le } else if (data[1] != 0) mib = 1013; // utf16 be else mib = 1018; // utf32 be } codec = QTextCodec::codecForMib(mib); } QString str = codec->toUnicode(ba); return parse(str); } /*! Parses the JSON string \a str as a QVariant. If the parse succeeds, this function returns true and the QVariant can be accessed using result(). If the parse fails, this function returns false and the error message can be accessed using errorMessage(). */ bool JsonReader::parse(const QString &str) { JsonLexer lexer(str); JsonParser parser; if (!parser.parse(&lexer)) { m_errorString = parser.errorMessage(); m_result = QVariant(); return false; } m_errorString.clear(); m_result = parser.result(); return true; } /*! Returns the result of the last parse() call. If parse() failed, this function returns an invalid QVariant. */ QVariant JsonReader::result() const { return m_result; } /*! Returns the error message for the last parse() call. If parse() succeeded, this functions return an empty string. The error message should be used for debugging purposes only. */ QString JsonReader::errorString() const { return m_errorString; } /*! \class JsonWriter \reentrant \brief The JsonWriter class converts a QVariant into a JSON string. The writer converts specific supported types stored in a QVariant into JSON. For example, \code QVariantMap map; map["id"] = 123; map["class"] = "JsonWriter"; QVariantList list; list << "Denis" << "Ettrich" << "Girish"; map["authors"] = list; JsonWriter writer; if (writer.stringify(map)) { QString json = writer.result(); qDebug() << json; // {"authors": ["Denis", "Ettrich", "Girish"], "class": "JsonWriter", "id": 123 } } else { qDebug() << "Failed to stringify " << writer.errorString(); } \endcode The list of QVariant types that the writer supports is listed in stringify(). Note that custom C++ types registered using Q_DECLARE_METATYPE are not supported. */ /*! Creates a JsonWriter. */ JsonWriter::JsonWriter() : m_autoFormatting(false), m_autoFormattingIndent(4, QLatin1Char(' ')) { } /*! Destructor. */ JsonWriter::~JsonWriter() { } /*! Enables auto formatting if \a enable is \c true, otherwise disables it. When auto formatting is enabled, the writer automatically inserts spaces and new lines to make the output more human readable. The default value is \c false. */ void JsonWriter::setAutoFormatting(bool enable) { m_autoFormatting = enable; } /*! Returns \c true if auto formattting is enabled, otherwise \c false. */ bool JsonWriter::autoFormatting() const { return m_autoFormatting; } /*! Sets the number of spaces or tabs used for indentation when auto-formatting is enabled. Positive numbers indicate spaces, negative numbers tabs. The default indentation is 4. \sa setAutoFormatting() */ void JsonWriter::setAutoFormattingIndent(int spacesOrTabs) { m_autoFormattingIndent = QString(qAbs(spacesOrTabs), QLatin1Char(spacesOrTabs >= 0 ? ' ' : '\t')); } /*! Retuns the numbers of spaces or tabs used for indentation when auto-formatting is enabled. Positive numbers indicate spaces, negative numbers tabs. The default indentation is 4. \sa setAutoFormatting() */ int JsonWriter::autoFormattingIndent() const { return m_autoFormattingIndent.count(QLatin1Char(' ')) - m_autoFormattingIndent.count(QLatin1Char('\t')); } /*! \internal Inserts escape character \ for characters in string as described in JSON specification. */ static QString escape(const QVariant &variant) { QString str = variant.toString(); QString res; res.reserve(str.length()); for (int i = 0; i < str.length(); i++) { if (str[i] == QLatin1Char('\b')) { res += QLatin1String("\\b"); } else if (str[i] == QLatin1Char('\f')) { res += QLatin1String("\\f"); } else if (str[i] == QLatin1Char('\n')) { res += QLatin1String("\\n"); } else if (str[i] == QLatin1Char('\r')) { res += QLatin1String("\\r"); } else if (str[i] == QLatin1Char('\t')) { res += QLatin1String("\\t"); } else if (str[i] == QLatin1Char('\"')) { res += QLatin1String("\\\""); } else if (str[i] == QLatin1Char('\\')) { res += QLatin1String("\\\\"); } else if (str[i] == QLatin1Char('/')) { res += QLatin1String("\\/"); } else if (str[i].unicode() > 127) { res += QLatin1String("\\u") + QString::number(str[i].unicode(), 16).rightJustified(4, QLatin1Char('0')); } else { res += str[i]; } } return res; } /*! \internal Stringifies \a variant. */ void JsonWriter::stringify(const QVariant &variant, int depth) { if (variant.type() == QVariant::List || variant.type() == QVariant::StringList) { m_result += QLatin1Char('['); QVariantList list = variant.toList(); for (int i = 0; i < list.count(); i++) { if (i != 0) { m_result += QLatin1Char(','); if (m_autoFormatting) m_result += QLatin1Char(' '); } stringify(list[i], depth+1); } m_result += QLatin1Char(']'); } else if (variant.type() == QVariant::Map) { QString indent = m_autoFormattingIndent.repeated(depth); QVariantMap map = variant.toMap(); if (m_autoFormatting && depth != 0) { m_result += QLatin1Char('\n'); m_result += indent; m_result += QLatin1String("{\n"); } else { m_result += QLatin1Char('{'); } for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { if (it != map.constBegin()) { m_result += QLatin1Char(','); if (m_autoFormatting) m_result += QLatin1Char('\n'); } if (m_autoFormatting) m_result += indent + QLatin1Char(' '); m_result += QLatin1Char('\"') + escape(it.key()) + QLatin1String("\":"); stringify(it.value(), depth+1); } if (m_autoFormatting) { m_result += QLatin1Char('\n'); m_result += indent; } m_result += QLatin1Char('}'); } else if (variant.type() == QVariant::String || variant.type() == QVariant::ByteArray) { m_result += QLatin1Char('\"') + escape(variant) + QLatin1Char('\"'); } else if (variant.type() == QVariant::Double || (int)variant.type() == (int)QMetaType::Float) { double d = variant.toDouble(); if (qIsFinite(d)) m_result += QString::number(variant.toDouble(), 'g', 15); else m_result += QLatin1String("null"); } else if (variant.type() == QVariant::Bool) { m_result += variant.toBool() ? QLatin1String("true") : QLatin1String("false"); } else if (variant.type() == QVariant::Invalid) { m_result += QLatin1String("null"); } else if (variant.type() == QVariant::ULongLong) { m_result += QString::number(variant.toULongLong()); } else if (variant.type() == QVariant::LongLong) { m_result += QString::number(variant.toLongLong()); } else if (variant.type() == QVariant::Int) { m_result += QString::number(variant.toInt()); } else if (variant.type() == QVariant::UInt) { m_result += QString::number(variant.toUInt()); } else if (variant.type() == QVariant::Char) { QChar c = variant.toChar(); if (c.unicode() > 127) m_result += QLatin1String("\"\\u") + QString::number(c.unicode(), 16).rightJustified(4, QLatin1Char('0')) + QLatin1Char('\"'); else m_result += QLatin1Char('\"') + c + QLatin1Char('\"'); } else if (variant.canConvert()) { m_result += QString::number(variant.toLongLong()); } else if (variant.canConvert()) { m_result += QLatin1Char('\"') + escape(variant) + QLatin1Char('\"'); } else { if (!m_errorString.isEmpty()) m_errorString.append(QLatin1Char('\n')); QString msg = QString::fromLatin1("Unsupported type %1 (id: %2)").arg(QString::fromUtf8(variant.typeName())).arg(variant.userType()); m_errorString.append(msg); qWarning() << "JsonWriter::stringify - " << msg; m_result += QLatin1String("null"); } } /*! Converts the variant \a var into a JSON string. The stringizer converts \a var into JSON based on the type of it's contents. The supported types and their conversion into JSON is as listed below: \table \row \o QVariant::List, QVariant::StringList \o JSON array [] \row \o QVariant::Map \o JSON object {} \row \o QVariant::String, QVariant::ByteArray \o JSON string encapsulated in double quotes. String contents are escaped using '\' if necessary. \row \o QVariant::Double, QMetaType::Float \o JSON number with a precision 15. Infinity and NaN are converted into null. \row \o QVariant::Bool \o JSON boolean true and false \row \o QVariant::Invalid \o JSON null \row \o QVariant::ULongLong, QVariant::LongLong, QVariant::Int, QVariant::UInt, \o JSON number \row \o QVariant::Char \o JSON string. Non-ASCII characters are converted into the \uXXXX notation. \endtable As a fallback, the writer attempts to convert a type not listed above into a long long or a QString using QVariant::canConvert. See the QVariant documentation for possible conversions. JsonWriter does not support stringizing custom user types stored in the QVariant. Any such value would be converted into JSON null. */ bool JsonWriter::stringify(const QVariant &var) { m_errorString.clear(); m_result.clear(); stringify(var, 0 /* depth */); return m_errorString.isEmpty(); } /*! Returns the result of the last stringify() call. If stringify() failed, this function returns a null QString. */ QString JsonWriter::result() const { return m_result; } /*! Returns the error message for the last stringify() call. If stringify() succeeded, this functions return an empty string. The error message should be used for debugging purposes only. */ QString JsonWriter::errorString() const { return m_errorString; } tiled-qt-0.9.1/src/plugins/json/qjsonparser/json.h000066400000000000000000000023201217502731700221600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (c) 2010 Girish Ramakrishnan ** ** Use, modification and distribution is allowed without limitation, ** warranty, liability or support of any kind. ** ****************************************************************************/ #ifndef JSON_H #define JSON_H #include #include class JsonReader { public: JsonReader(); ~JsonReader(); bool parse(const QByteArray &ba); bool parse(const QString &str); QVariant result() const; QString errorString() const; private: QVariant m_result; QString m_errorString; }; class JsonWriter { public: JsonWriter(); ~JsonWriter(); bool stringify(const QVariant &variant); QString result() const; QString errorString() const; void setAutoFormatting(bool autoFormat); bool autoFormatting() const; void setAutoFormattingIndent(int spaceOrTabs); int autoFormattingIndent() const; private: void stringify(const QVariant &variant, int depth); QString m_result; QString m_errorString; bool m_autoFormatting; QString m_autoFormattingIndent; }; #endif // JSON_H tiled-qt-0.9.1/src/plugins/json/qjsonparser/jsonparser.cpp000066400000000000000000000342341217502731700237410ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! #ifndef JSONPARSER_CPP #define JSONPARSER_CPP class JsonGrammar { public: enum VariousConstants { EOF_SYMBOL = 0, ERROR = 12, T_COLON = 7, T_COMMA = 8, T_FALSE = 9, T_LCURLYBRACKET = 3, T_LSQUAREBRACKET = 5, T_NULL = 11, T_NUMBER = 2, T_RCURLYBRACKET = 4, T_RSQUAREBRACKET = 6, T_STRING = 1, T_TRUE = 10, ACCEPT_STATE = 12, RULE_COUNT = 17, STATE_COUNT = 27, TERMINAL_COUNT = 13, NON_TERMINAL_COUNT = 7, GOTO_INDEX_OFFSET = 27, GOTO_INFO_OFFSET = 37, GOTO_CHECK_OFFSET = 37 }; static const char *const spell []; static const short lhs []; static const short rhs []; #ifndef QLALR_NO_JSONGRAMMAR_DEBUG_INFO static const int rule_index []; static const int rule_info []; #endif // QLALR_NO_JSONGRAMMAR_DEBUG_INFO static const short goto_default []; static const short action_default []; static const short action_index []; static const short action_info []; static const short action_check []; static inline int nt_action (int state, int nt) { const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) return goto_default [nt]; return action_info [GOTO_INFO_OFFSET + yyn]; } static inline int t_action (int state, int token) { const int yyn = action_index [state] + token; if (yyn < 0 || action_check [yyn] != token) return - action_default [state]; return action_info [yyn]; } }; const char *const JsonGrammar::spell [] = { "end of file", "string", "number", "{", "}", "[", "]", ":", ",", "false", "true", "null", "error", #ifndef QLALR_NO_JSONGRAMMAR_DEBUG_INFO "Root", "Value", "Object", "Members", "Array", "Values", "$accept" #endif // QLALR_NO_JSONGRAMMAR_DEBUG_INFO }; const short JsonGrammar::lhs [] = { 13, 15, 16, 16, 16, 14, 14, 14, 14, 14, 14, 14, 17, 18, 18, 18, 19}; const short JsonGrammar::rhs [] = { 1, 3, 3, 5, 0, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 0, 2}; #ifndef QLALR_NO_JSONGRAMMAR_DEBUG_INFO const int JsonGrammar::rule_info [] = { 13, 14 , 15, 3, 16, 4 , 16, 1, 7, 14 , 16, 16, 8, 1, 7, 14 , 16 , 14, 9 , 14, 10 , 14, 11 , 14, 15 , 14, 17 , 14, 2 , 14, 1 , 17, 5, 18, 6 , 18, 14 , 18, 18, 8, 14 , 18 , 19, 13, 0}; const int JsonGrammar::rule_index [] = { 0, 2, 6, 10, 16, 17, 19, 21, 23, 25, 27, 29, 31, 35, 37, 41, 42}; #endif // QLALR_NO_JSONGRAMMAR_DEBUG_INFO const short JsonGrammar::action_default [] = { 0, 10, 9, 0, 6, 5, 16, 8, 11, 12, 7, 1, 17, 0, 0, 0, 2, 0, 0, 4, 0, 3, 14, 0, 0, 13, 15}; const short JsonGrammar::goto_default [] = { 3, 11, 2, 13, 1, 23, 0}; const short JsonGrammar::action_index [] = { 24, -13, -13, 12, -13, -1, 24, -13, -13, -13, -13, -13, -13, 1, -5, 2, -13, -6, 24, -13, 24, -13, -13, -2, 24, -13, -13, -7, -7, -7, -7, -7, -7, 1, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, -7, 0, -7, -1, -7, -7, -7, 5, -7, -7}; const short JsonGrammar::action_info [] = { 14, 18, 20, 17, 25, 16, 24, 0, 0, 15, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 5, 0, 6, 0, 0, 0, 4, 10, 7, 0, 21, 19, 22, 0, 0, 0, 26, 0, 0, 0, 0, 0}; const short JsonGrammar::action_check [] = { 1, 7, 7, 1, 6, 4, 8, -1, -1, 8, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 3, -1, 5, -1, -1, -1, 9, 10, 11, -1, 1, 1, 1, -1, -1, -1, 1, -1, -1, -1, -1, -1}; #line 29 "json.g" /**************************************************************************** ** ** Copyright (c) 2010 Girish Ramakrishnan ** Copyright (c) 2011 Denis Dzyubenko ** ** Use, modification and distribution is allowed without limitation, ** warranty, liability or support of any kind. ** ****************************************************************************/ #ifndef JSONPARSER_P_H #define JSONPARSER_P_H #include #include #include class JsonLexer { public: JsonLexer(const QString &string); ~JsonLexer(); int lex(); QVariant symbol() const { return m_symbol; } int lineNumber() const { return m_lineNumber; } int pos() const { return m_pos; } private: int parseNumber(); int parseString(); int parseKeyword(); QString m_strdata; int m_lineNumber; int m_pos; QVariant m_symbol; }; class JsonParser : protected JsonGrammar { public: JsonParser(); ~JsonParser(); bool parse(JsonLexer *lex); QVariant result() const { return m_result; } QString errorMessage() const { return QString::fromLatin1("%1 at line %2 pos %3").arg(m_errorMessage).arg(m_errorLineNumber).arg(m_errorPos); } private: void reallocateStack(); inline QVariant &sym(int index) { return m_symStack[m_tos + index - 1]; } inline QVariantMap &map(int index) { return m_mapStack[m_tos + index - 1]; } inline QVariantList &list(int index) { return m_listStack[m_tos + index - 1]; } int m_tos; QVector m_stateStack; QVector m_symStack; QVector m_mapStack; QVector m_listStack; QString m_errorMessage; int m_errorLineNumber; int m_errorPos; QVariant m_result; }; #endif // JSONPARSER_P_H #line 103 "json.g" /**************************************************************************** ** ** Copyright (c) 2010 Girish Ramakrishnan ** Copyright (c) 2011 Denis Dzyubenko ** ** Use, modification and distribution is allowed without limitation, ** warranty, liability or support of any kind. ** ****************************************************************************/ #include #define L1C(c) ushort(uchar(c)) JsonLexer::JsonLexer(const QString &str) : m_strdata(str), m_lineNumber(1), m_pos(0) { } JsonLexer::~JsonLexer() { } int JsonLexer::parseString() { bool esc = false; ++m_pos; // skip initial " int start = m_pos; const ushort *uc = m_strdata.utf16(); const int remaining = m_strdata.length() - m_pos; int i; for (i = 0; i < remaining; ++i) { const ushort c = uc[m_pos + i]; if (c == L1C('\"')) { m_symbol = QString((QChar *)uc + m_pos, i); m_pos += i; ++m_pos; // eat quote return JsonGrammar::T_STRING; } else if (c == L1C('\\')) { ++m_pos; // eat backslash esc = true; break; } } QString str; if (i) { str.resize(i); memcpy((char *)str.utf16(), uc + start, i * 2); m_pos += i; } for (; m_pos < m_strdata.length(); ++m_pos) { const ushort c = uc[m_pos]; if (esc) { if (c == L1C('b')) str += QLatin1Char('\b'); else if (c == L1C('f')) str += QLatin1Char('\f'); else if (c == L1C('n')) str += QLatin1Char('\n'); else if (c == L1C('r')) str += QLatin1Char('\r'); else if (c == L1C('t')) str += QLatin1Char('\t'); else if (c == L1C('\\')) str += QLatin1Char('\\'); else if (c == L1C('\"')) str += QLatin1Char('\"'); else if (c == L1C('u') && m_pos+4= L1C('0') && c <= L1C('9')) { if (!isDouble) { value *= 10; value += c - L1C('0'); } continue; } break; } if (!isDouble) { m_symbol = value * negative; } else { QString number = QString::fromRawData((QChar *)uc+start, m_pos-start); m_symbol = number.toDouble(); } return JsonGrammar::T_NUMBER; } int JsonLexer::parseKeyword() { int start = m_pos; for (; m_pos < m_strdata.length(); ++m_pos) { const ushort c = m_strdata.at(m_pos).unicode(); if (c >= L1C('a') && c <= L1C('z')) continue; break; } const ushort *k = (const ushort *)m_strdata.constData() + start; const int l = m_pos-start; if (l == 4) { static const ushort true_data[] = { 't', 'r', 'u', 'e' }; static const ushort null_data[] = { 'n', 'u', 'l', 'l' }; if (!memcmp(k, true_data, 4 * sizeof(ushort))) return JsonGrammar::T_TRUE; if (!memcmp(k, null_data, 4 * sizeof(ushort))) return JsonGrammar::T_NULL; } else if (l == 5) { static const ushort false_data[] = { 'f', 'a', 'l', 's', 'e' }; if (!memcmp(k, false_data, 5 * sizeof(ushort))) return JsonGrammar::T_FALSE; } return JsonGrammar::ERROR; } int JsonLexer::lex() { m_symbol.clear(); const ushort *uc = m_strdata.utf16(); const int len = m_strdata.length(); while (m_pos < len) { const ushort c = uc[m_pos]; switch (c) { case L1C('['): ++m_pos; return JsonGrammar::T_LSQUAREBRACKET; case L1C(']'): ++m_pos; return JsonGrammar::T_RSQUAREBRACKET; case L1C('{'): ++m_pos; return JsonGrammar::T_LCURLYBRACKET; case L1C('}'): ++m_pos; return JsonGrammar::T_RCURLYBRACKET; case L1C(':'): ++m_pos; return JsonGrammar::T_COLON; case L1C(','): ++m_pos; return JsonGrammar::T_COMMA; case L1C(' '): case L1C('\r'): case L1C('\t'): ++m_pos; break; case L1C('\n'): ++m_pos; ++m_lineNumber; break; case L1C('"'): return parseString(); default: if (c == L1C('+') || c == L1C('-') || (c >= L1C('0') && c <= L1C('9'))) { return parseNumber(); } if (c >= L1C('a') && c <= L1C('z')) { return parseKeyword(); } return JsonGrammar::ERROR; } } return JsonGrammar::EOF_SYMBOL; } #undef L1C JsonParser::JsonParser() : m_tos(0) , m_errorLineNumber(-1) , m_errorPos(-1) { } JsonParser::~JsonParser() { } void JsonParser::reallocateStack() { int size = m_stateStack.size(); if (size == 0) size = 128; else size <<= 1; m_symStack.resize(size); m_mapStack.resize(size); m_listStack.resize(size); m_stateStack.resize(size); } bool JsonParser::parse(JsonLexer *lexer) { const int INITIAL_STATE = 0; int yytoken = -1; reallocateStack(); m_tos = 0; m_stateStack[++m_tos] = INITIAL_STATE; while (true) { const int state = m_stateStack[m_tos]; if (yytoken == -1 && -TERMINAL_COUNT != action_index[state]) { yytoken = lexer->lex(); } int act = t_action(state, yytoken); if (act == ACCEPT_STATE) return true; else if (act > 0) { if (++m_tos == m_stateStack.size()) reallocateStack(); m_stateStack[m_tos] = act; m_symStack[m_tos] = lexer->symbol(); yytoken = -1; } else if (act < 0) { int r = -act-1; m_tos -= rhs[r]; act = m_stateStack.at(m_tos++); switch (r) { #line 334 "json.g" case 0: { m_result = sym(1); break; } #line 337 "json.g" case 1: { sym(1) = map(2); break; } #line 340 "json.g" case 2: { QVariantMap m; m.insert(sym(1).toString(), sym(3)); map(1) = m; break; } #line 343 "json.g" case 3: { map(1).insert(sym(3).toString(), sym(5)); break; } #line 346 "json.g" case 4: { map(1) = QVariantMap(); break; } #line 349 "json.g" case 5: { sym(1) = QVariant(false); break; } #line 352 "json.g" case 6: { sym(1) = QVariant(true); break; } #line 361 "json.g" case 12: { sym(1) = list(2); break; } #line 364 "json.g" case 13: { QVariantList l; l.append(sym(1)); list(1) = l; break; } #line 367 "json.g" case 14: { list(1).append(sym(3)); break; } #line 370 "json.g" case 15: { list(1) = QVariantList(); break; } #line 372 "json.g" } // switch m_stateStack[m_tos] = nt_action(act, lhs[r] - TERMINAL_COUNT); } else { int ers = state; int shifts = 0; int reduces = 0; int expected_tokens[3]; for (int tk = 0; tk < TERMINAL_COUNT; ++tk) { int k = t_action(ers, tk); if (! k) continue; else if (k < 0) ++reduces; else if (spell[tk]) { if (shifts < 3) expected_tokens[shifts] = tk; ++shifts; } } m_errorLineNumber = lexer->lineNumber(); m_errorPos = lexer->pos(); m_errorMessage.clear(); if (shifts && shifts < 3) { bool first = true; for (int s = 0; s < shifts; ++s) { if (first) m_errorMessage += QLatin1String("Expected "); else m_errorMessage += QLatin1String(", "); first = false; m_errorMessage += QLatin1String("'"); m_errorMessage += QLatin1String(spell[expected_tokens[s]]); m_errorMessage += QLatin1String("'"); } } return false; } } return false; } #endif // JSONPARSER_CPP tiled-qt-0.9.1/src/plugins/json/qjsonparser/qjsonparser.pri000066400000000000000000000000751217502731700241260ustar00rootroot00000000000000INCLUDEPATH += $$PWD SOURCES += json.cpp HEADERS += json.h tiled-qt-0.9.1/src/plugins/json/varianttomapconverter.cpp000066400000000000000000000330621217502731700236370ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "varianttomapconverter.h" #include "imagelayer.h" #include "map.h" #include "mapobject.h" #include "objectgroup.h" #include "properties.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" using namespace Tiled; using namespace Json; Map *VariantToMapConverter::toMap(const QVariant &variant, const QDir &mapDir) { mGidMapper.clear(); mMapDir = mapDir; const QVariantMap variantMap = variant.toMap(); const QString orientationString = variantMap["orientation"].toString(); Map::Orientation orientation = orientationFromString(orientationString); if (orientation == Map::Unknown) { mError = tr("Unsupported map orientation: \"%1\"") .arg(orientationString); return 0; } mMap = new Map(orientation, variantMap["width"].toInt(), variantMap["height"].toInt(), variantMap["tilewidth"].toInt(), variantMap["tileheight"].toInt()); mMap->setProperties(toProperties(variantMap["properties"])); const QString bgColor = variantMap["backgroundcolor"].toString(); if (!bgColor.isEmpty()) #if QT_VERSION >= 0x040700 if (QColor::isValidColor(bgColor)) #endif mMap->setBackgroundColor(QColor(bgColor)); foreach (const QVariant &tilesetVariant, variantMap["tilesets"].toList()) { Tileset *tileset = toTileset(tilesetVariant); if (!tileset) { // Delete tilesets loaded so far and the map qDeleteAll(mMap->tilesets()); delete mMap; return 0; } mMap->addTileset(tileset); } foreach (const QVariant &layerVariant, variantMap["layers"].toList()) if (Layer *layer = toLayer(layerVariant)) mMap->addLayer(layer); return mMap; } Properties VariantToMapConverter::toProperties(const QVariant &variant) { const QVariantMap variantMap = variant.toMap(); Properties properties; QVariantMap::const_iterator it = variantMap.constBegin(); QVariantMap::const_iterator it_end = variantMap.constEnd(); for (; it != it_end; ++it) properties[it.key()] = it.value().toString(); return properties; } Tileset *VariantToMapConverter::toTileset(const QVariant &variant) { const QVariantMap variantMap = variant.toMap(); const int firstGid = variantMap["firstgid"].toInt(); const QString name = variantMap["name"].toString(); const int tileWidth = variantMap["tilewidth"].toInt(); const int tileHeight = variantMap["tileheight"].toInt(); const int spacing = variantMap["spacing"].toInt(); const int margin = variantMap["margin"].toInt(); const QVariantMap tileOffset = variantMap["tileoffset"].toMap(); const int tileOffsetX = tileOffset["x"].toInt(); const int tileOffsetY = tileOffset["y"].toInt(); if (tileWidth <= 0 || tileHeight <= 0 || firstGid == 0) { mError = tr("Invalid tileset parameters for tileset '%1'").arg(name); return 0; } Tileset *tileset = new Tileset(name, tileWidth, tileHeight, spacing, margin); tileset->setTileOffset(QPoint(tileOffsetX, tileOffsetY)); const QString trans = variantMap["transparentcolor"].toString(); if (!trans.isEmpty()) #if QT_VERSION >= 0x040700 if (QColor::isValidColor(trans)) #endif tileset->setTransparentColor(QColor(trans)); QString imageSource = variantMap["image"].toString(); if (QDir::isRelativePath(imageSource)) imageSource = QDir::cleanPath(mMapDir.absoluteFilePath(imageSource)); if (!tileset->loadFromImage(QImage(imageSource), imageSource)) { mError = tr("Error loading tileset image:\n'%1'").arg(imageSource); delete tileset; return 0; } tileset->setProperties(toProperties(variantMap["properties"])); QVariantMap propertiesVariantMap = variantMap["tileproperties"].toMap(); QVariantMap::const_iterator it = propertiesVariantMap.constBegin(); for (; it != propertiesVariantMap.constEnd(); ++it) { const int tileIndex = it.key().toInt(); const QVariant propertiesVar = it.value(); if (tileIndex >= 0 && tileIndex < tileset->tileCount()) { const Properties properties = toProperties(propertiesVar); tileset->tileAt(tileIndex)->setProperties(properties); } } // Read terrains QVariantList terrainsVariantList = variantMap["terrains"].toList(); for (int i = 0; i < terrainsVariantList.count(); ++i) { QVariantMap terrainMap = terrainsVariantList[i].toMap(); tileset->addTerrain(terrainMap["name"].toString(), terrainMap["tile"].toInt()); } // Read tile terrain information const QVariantMap tilesVariantMap = variantMap["tiles"].toMap(); for (it = tilesVariantMap.begin(); it != tilesVariantMap.end(); ++it) { bool ok; const int tileIndex = it.key().toInt(); Tile *tile = tileset->tileAt(tileIndex); if (tileIndex >= 0 && tileIndex < tileset->tileCount()) { const QVariantMap tileVar = it.value().toMap(); QList terrains = tileVar["terrain"].toList(); if (terrains.count() == 4) { for (int i = 0; i < 4; ++i) { int terrainID = terrains.at(i).toInt(&ok); if (ok && terrainID >= 0 && terrainID < tileset->terrainCount()) tile->setCornerTerrain(i, terrainID); } } float terrainProbability = tileVar["probability"].toFloat(&ok); if (ok) tile->setTerrainProbability(terrainProbability); } } mGidMapper.insert(firstGid, tileset); return tileset; } Layer *VariantToMapConverter::toLayer(const QVariant &variant) { const QVariantMap variantMap = variant.toMap(); Layer *layer = 0; if (variantMap["type"] == "tilelayer") layer = toTileLayer(variantMap); else if (variantMap["type"] == "objectgroup") layer = toObjectGroup(variantMap); else if (variantMap["type"] == "imagelayer") layer = toImageLayer(variantMap); if (layer) layer->setProperties(toProperties(variantMap["properties"])); return layer; } TileLayer *VariantToMapConverter::toTileLayer(const QVariantMap &variantMap) { const QString name = variantMap["name"].toString(); const int width = variantMap["width"].toInt(); const int height = variantMap["height"].toInt(); const QVariantList dataVariantList = variantMap["data"].toList(); if (dataVariantList.size() != width * height) { mError = tr("Corrupt layer data for layer '%1'").arg(name); return 0; } TileLayer *tileLayer = new TileLayer(name, variantMap["x"].toInt(), variantMap["y"].toInt(), width, height); const qreal opacity = variantMap["opacity"].toReal(); const bool visible = variantMap["visible"].toBool(); tileLayer->setOpacity(opacity); tileLayer->setVisible(visible); int x = 0; int y = 0; bool ok; foreach (const QVariant &gidVariant, dataVariantList) { const unsigned gid = gidVariant.toUInt(&ok); if (!ok) { mError = tr("Unable to parse tile at (%1,%2) on layer '%3'") .arg(x).arg(y).arg(tileLayer->name()); delete tileLayer; tileLayer = 0; break; } const Cell cell = mGidMapper.gidToCell(gid, ok); tileLayer->setCell(x, y, cell); x++; if (x >= tileLayer->width()) { x = 0; y++; } } return tileLayer; } class PixelToTileCoordinates { public: PixelToTileCoordinates(const Map *map) { if (map->orientation() == Map::Isometric) { // Isometric needs special handling, since the pixel values are // based solely on the tile height. mMultiplierX = (qreal) 1 / map->tileHeight(); mMultiplierY = (qreal) 1 / map->tileHeight(); } else { mMultiplierX = (qreal) 1 / map->tileWidth(); mMultiplierY = (qreal) 1 / map->tileHeight(); } } QPointF operator() (int x, int y) const { return QPointF(x * mMultiplierX, y * mMultiplierY); } private: qreal mMultiplierX; qreal mMultiplierY; }; ObjectGroup *VariantToMapConverter::toObjectGroup(const QVariantMap &variantMap) { ObjectGroup *objectGroup = new ObjectGroup(variantMap["name"].toString(), variantMap["x"].toInt(), variantMap["y"].toInt(), variantMap["width"].toInt(), variantMap["height"].toInt()); const qreal opacity = variantMap["opacity"].toReal(); const bool visible = variantMap["visible"].toBool(); objectGroup->setOpacity(opacity); objectGroup->setVisible(visible); objectGroup->setColor(variantMap.value("color").value()); const PixelToTileCoordinates toTile(mMap); foreach (const QVariant &objectVariant, variantMap["objects"].toList()) { const QVariantMap objectVariantMap = objectVariant.toMap(); const QString name = objectVariantMap["name"].toString(); const QString type = objectVariantMap["type"].toString(); const int gid = objectVariantMap["gid"].toInt(); const int x = objectVariantMap["x"].toInt(); const int y = objectVariantMap["y"].toInt(); const int width = objectVariantMap["width"].toInt(); const int height = objectVariantMap["height"].toInt(); const QPointF pos = toTile(x, y); const QPointF size = toTile(width, height); MapObject *object = new MapObject(name, type, pos, QSizeF(size.x(), size.y())); if (gid) { bool ok; const Cell cell = mGidMapper.gidToCell(gid, ok); object->setTile(cell.tile); } if (objectVariantMap.contains("visible")) object->setVisible(objectVariantMap["visible"].toBool()); object->setProperties(toProperties(objectVariantMap["properties"])); objectGroup->addObject(object); const QVariant polylineVariant = objectVariantMap["polyline"]; const QVariant polygonVariant = objectVariantMap["polygon"]; if (polygonVariant.isValid()) { object->setShape(MapObject::Polygon); object->setPolygon(toPolygon(polygonVariant)); } if (polylineVariant.isValid()) { object->setShape(MapObject::Polyline); object->setPolygon(toPolygon(polylineVariant)); } if (objectVariantMap.contains("ellipse")) object->setShape(MapObject::Ellipse); } return objectGroup; } ImageLayer *VariantToMapConverter::toImageLayer(const QVariantMap &variantMap) { ImageLayer *imageLayer = new ImageLayer(variantMap["name"].toString(), variantMap["x"].toInt(), variantMap["y"].toInt(), variantMap["width"].toInt(), variantMap["height"].toInt()); const qreal opacity = variantMap["opacity"].toReal(); const bool visible = variantMap["visible"].toBool(); imageLayer->setOpacity(opacity); imageLayer->setVisible(visible); const QString trans = variantMap["transparentcolor"].toString(); if (!trans.isEmpty()) #if QT_VERSION >= 0x040700 if (QColor::isValidColor(trans)) #endif imageLayer->setTransparentColor(QColor(trans)); QString imageSource = variantMap["image"].toString(); if (!imageSource.isEmpty()) { if (QDir::isRelativePath(imageSource)) imageSource = QDir::cleanPath(mMapDir.absoluteFilePath(imageSource)); if (!imageLayer->loadFromImage(QImage(imageSource), imageSource)) { // TODO: This error is currently ignored mError = tr("Error loading image:\n'%1'").arg(imageSource); } } return imageLayer; } QPolygonF VariantToMapConverter::toPolygon(const QVariant &variant) const { const PixelToTileCoordinates toTile(mMap); QPolygonF polygon; foreach (const QVariant &pointVariant, variant.toList()) { const QVariantMap pointVariantMap = pointVariant.toMap(); const int pointX = pointVariantMap["x"].toInt(); const int pointY = pointVariantMap["y"].toInt(); polygon.append(toTile(pointX, pointY)); } return polygon; } tiled-qt-0.9.1/src/plugins/json/varianttomapconverter.h000066400000000000000000000046131217502731700233040ustar00rootroot00000000000000/* * JSON Tiled Plugin * Copyright 2011, Porfírio José Pereira Ribeiro * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef VARIANTTOMAPCONVERTER_H #define VARIANTTOMAPCONVERTER_H #include "gidmapper.h" #include #include #include namespace Tiled { class Layer; class Map; class ObjectGroup; class Properties; class Tileset; } namespace Json { /** * Converts a QVariant to a Map instance. Meant to be used together with * JsonReader. */ class VariantToMapConverter { // Using the MapReader context since the messages are the same Q_DECLARE_TR_FUNCTIONS(MapReader) public: VariantToMapConverter() : mMap(0) {} /** * Tries to convert the given \a variant to a Map instance. The \a mapDir * is necessary to resolve any relative references to external images. * * Returns 0 in case of an error. The error can be obstained using * errorString(). */ Tiled::Map *toMap(const QVariant &variant, const QDir &mapDir); /** * Returns the last error, if any. */ QString errorString() const { return mError; } private: Tiled::Properties toProperties(const QVariant &variant); Tiled::Tileset *toTileset(const QVariant &variant); Tiled::Layer *toLayer(const QVariant &variant); Tiled::TileLayer *toTileLayer(const QVariantMap &variantMap); Tiled::ObjectGroup *toObjectGroup(const QVariantMap &variantMap); Tiled::ImageLayer *toImageLayer(const QVariantMap &variantMap); QPolygonF toPolygon(const QVariant &variant) const; Tiled::Map *mMap; QDir mMapDir; Tiled::GidMapper mGidMapper; QString mError; }; } // namespace Json #endif // VARIANTTOMAPCONVERTER_H tiled-qt-0.9.1/src/plugins/lua/000077500000000000000000000000001217502731700163025ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/lua/lua.pro000066400000000000000000000002421217502731700176030ustar00rootroot00000000000000include(../plugin.pri) DEFINES += LUA_LIBRARY SOURCES += luaplugin.cpp \ luatablewriter.cpp HEADERS += luaplugin.h\ lua_global.h \ luatablewriter.h tiled-qt-0.9.1/src/plugins/lua/lua_global.h000066400000000000000000000017371217502731700205640ustar00rootroot00000000000000/* * Lua Tiled Plugin * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef LUA_GLOBAL_H #define LUA_GLOBAL_H #include #if defined(LUA_LIBRARY) # define LUASHARED_EXPORT Q_DECL_EXPORT #else # define LUASHARED_EXPORT Q_DECL_IMPORT #endif #endif // LUA_GLOBAL_H tiled-qt-0.9.1/src/plugins/lua/luaplugin.cpp000066400000000000000000000312021217502731700210040ustar00rootroot00000000000000/* * Lua Tiled Plugin * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "luaplugin.h" #include "luatablewriter.h" #include "imagelayer.h" #include "map.h" #include "mapobject.h" #include "objectgroup.h" #include "properties.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include /** * See below for an explanation of the different formats. One of these needs * to be defined. */ #define POLYGON_FORMAT_FULL //#define POLYGON_FORMAT_PAIRS //#define POLYGON_FORMAT_OPTIMAL using namespace Lua; using namespace Tiled; LuaPlugin::LuaPlugin() { } bool LuaPlugin::write(const Map *map, const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { mError = tr("Could not open file for writing."); return false; } mMapDir = QFileInfo(fileName).path(); LuaTableWriter writer(&file); writer.writeStartDocument(); writeMap(writer, map); writer.writeEndDocument(); return !writer.hasError(); } QString LuaPlugin::nameFilter() const { return tr("Lua files (*.lua)"); } QString LuaPlugin::errorString() const { return mError; } void LuaPlugin::writeMap(LuaTableWriter &writer, const Map *map) { writer.writeStartReturnTable(); writer.writeKeyAndValue("version", "1.1"); writer.writeKeyAndValue("luaversion", "5.1"); const QString orientation = orientationToString(map->orientation()); writer.writeKeyAndValue("orientation", orientation); writer.writeKeyAndValue("width", map->width()); writer.writeKeyAndValue("height", map->height()); writer.writeKeyAndValue("tilewidth", map->tileWidth()); writer.writeKeyAndValue("tileheight", map->tileHeight()); writeProperties(writer, map->properties()); writer.writeStartTable("tilesets"); mGidMapper.clear(); unsigned firstGid = 1; foreach (Tileset *tileset, map->tilesets()) { writeTileset(writer, tileset, firstGid); mGidMapper.insert(firstGid, tileset); firstGid += tileset->tileCount(); } writer.writeEndTable(); writer.writeStartTable("layers"); foreach (const Layer *layer, map->layers()) { switch (layer->type()) { case Layer::TileLayerType: writeTileLayer(writer, static_cast(layer)); break; case Layer::ObjectGroupType: writeObjectGroup(writer, static_cast(layer)); break; case Layer::ImageLayerType: writeImageLayer(writer, static_cast(layer)); break; } } writer.writeEndTable(); writer.writeEndTable(); } void LuaPlugin::writeProperties(LuaTableWriter &writer, const Properties &properties) { writer.writeStartTable("properties"); Properties::const_iterator it = properties.constBegin(); Properties::const_iterator it_end = properties.constEnd(); for (; it != it_end; ++it) writer.writeQuotedKeyAndValue(it.key(), it.value()); writer.writeEndTable(); } void LuaPlugin::writeTileset(LuaTableWriter &writer, const Tileset *tileset, unsigned firstGid) { writer.writeStartTable(); writer.writeKeyAndValue("name", tileset->name()); writer.writeKeyAndValue("firstgid", firstGid); if (!tileset->fileName().isEmpty()) { const QString rel = mMapDir.relativeFilePath(tileset->fileName()); writer.writeKeyAndValue("filename", rel); } /* Include all tileset information even for external tilesets, since the * external reference is generally a .tsx file (in XML format). */ writer.writeKeyAndValue("tilewidth", tileset->tileWidth()); writer.writeKeyAndValue("tileheight", tileset->tileHeight()); writer.writeKeyAndValue("spacing", tileset->tileSpacing()); writer.writeKeyAndValue("margin", tileset->margin()); const QString rel = mMapDir.relativeFilePath(tileset->imageSource()); writer.writeKeyAndValue("image", rel); writer.writeKeyAndValue("imagewidth", tileset->imageWidth()); writer.writeKeyAndValue("imageheight", tileset->imageHeight()); if (tileset->transparentColor().isValid()) { writer.writeKeyAndValue("transparentcolor", tileset->transparentColor().name()); } writeProperties(writer, tileset->properties()); writer.writeStartTable("tiles"); for (int i = 0; i < tileset->tileCount(); ++i) { const Tile *tile = tileset->tileAt(i); const Properties &properties = tile->properties(); // Include enties for those tiles that have properties set on them if (!properties.isEmpty()) { writer.writeStartTable(); writer.writeKeyAndValue("id", i); writeProperties(writer, properties); writer.writeEndTable(); } } writer.writeEndTable(); writer.writeEndTable(); } void LuaPlugin::writeTileLayer(LuaTableWriter &writer, const TileLayer *tileLayer) { writer.writeStartTable(); writer.writeKeyAndValue("type", "tilelayer"); writer.writeKeyAndValue("name", tileLayer->name()); writer.writeKeyAndValue("x", tileLayer->x()); writer.writeKeyAndValue("y", tileLayer->y()); writer.writeKeyAndValue("width", tileLayer->width()); writer.writeKeyAndValue("height", tileLayer->height()); writer.writeKeyAndValue("visible", tileLayer->isVisible()); writer.writeKeyAndValue("opacity", tileLayer->opacity()); writeProperties(writer, tileLayer->properties()); writer.writeKeyAndValue("encoding", "lua"); writer.writeStartTable("data"); for (int y = 0; y < tileLayer->height(); ++y) { if (y > 0) writer.prepareNewLine(); for (int x = 0; x < tileLayer->width(); ++x) writer.writeValue(mGidMapper.cellToGid(tileLayer->cellAt(x, y))); } writer.writeEndTable(); writer.writeEndTable(); } void LuaPlugin::writeObjectGroup(LuaTableWriter &writer, const ObjectGroup *objectGroup) { writer.writeStartTable(); writer.writeKeyAndValue("type", "objectgroup"); writer.writeKeyAndValue("name", objectGroup->name()); writer.writeKeyAndValue("visible", objectGroup->isVisible()); writer.writeKeyAndValue("opacity", objectGroup->opacity()); writeProperties(writer, objectGroup->properties()); writer.writeStartTable("objects"); foreach (MapObject *mapObject, objectGroup->objects()) writeMapObject(writer, mapObject); writer.writeEndTable(); writer.writeEndTable(); } void LuaPlugin::writeImageLayer(LuaTableWriter &writer, const ImageLayer *imageLayer) { writer.writeStartTable(); writer.writeKeyAndValue("type", "imagelayer"); writer.writeKeyAndValue("name", imageLayer->name()); writer.writeKeyAndValue("visible", imageLayer->isVisible()); writer.writeKeyAndValue("opacity", imageLayer->opacity()); const QString rel = mMapDir.relativeFilePath(imageLayer->imageSource()); writer.writeKeyAndValue("image", rel); if (imageLayer->transparentColor().isValid()) { writer.writeKeyAndValue("transparentcolor", imageLayer->transparentColor().name()); } writeProperties(writer, imageLayer->properties()); writer.writeEndTable(); } // TODO: Unduplicate this class since it's used also in mapwriter.cpp class TileToPixelCoordinates { public: TileToPixelCoordinates(Map *map) { if (map->orientation() == Map::Isometric) { // Isometric needs special handling, since the pixel values are // based solely on the tile height. mMultiplierX = map->tileHeight(); mMultiplierY = map->tileHeight(); } else { mMultiplierX = map->tileWidth(); mMultiplierY = map->tileHeight(); } } QPoint operator() (qreal x, qreal y) const { return QPoint(qRound(x * mMultiplierX), qRound(y * mMultiplierY)); } private: int mMultiplierX; int mMultiplierY; }; static const char *toString(MapObject::Shape shape) { switch (shape) { case MapObject::Rectangle: return "rectangle"; case MapObject::Polygon: return "polygon"; case MapObject::Polyline: return "polyline"; case MapObject::Ellipse: return "ellipse"; } return "unknown"; } void LuaPlugin::writeMapObject(LuaTableWriter &writer, const Tiled::MapObject *mapObject) { writer.writeStartTable(); writer.writeKeyAndValue("name", mapObject->name()); writer.writeKeyAndValue("type", mapObject->type()); writer.writeKeyAndValue("shape", toString(mapObject->shape())); const ObjectGroup *objectGroup = mapObject->objectGroup(); const TileToPixelCoordinates toPixel(objectGroup->map()); const QPoint pos = toPixel(mapObject->x(), mapObject->y()); const QPoint size = toPixel(mapObject->width(), mapObject->height()); writer.writeKeyAndValue("x", pos.x()); writer.writeKeyAndValue("y", pos.y()); writer.writeKeyAndValue("width", size.x()); writer.writeKeyAndValue("height", size.y()); if (Tile *tile = mapObject->tile()) writer.writeKeyAndValue("gid", mGidMapper.cellToGid(Cell(tile))); writer.writeKeyAndValue("visible", mapObject->isVisible()); const QPolygonF &polygon = mapObject->polygon(); if (!polygon.isEmpty()) { if (mapObject->shape() == MapObject::Polygon) writer.writeStartTable("polygon"); else writer.writeStartTable("polyline"); #if defined(POLYGON_FORMAT_FULL) /* This format is the easiest to read and understand: * * { * { x = 1, y = 1 }, * { x = 2, y = 2 }, * { x = 3, y = 3 }, * ... * } */ foreach (const QPointF &point, polygon) { writer.writeStartTable(); writer.setSuppressNewlines(true); const QPoint pixelCoordinates = toPixel(point.x(), point.y()); writer.writeKeyAndValue("x", pixelCoordinates.x()); writer.writeKeyAndValue("y", pixelCoordinates.y()); writer.writeEndTable(); writer.setSuppressNewlines(false); } #elif defined(POLYGON_FORMAT_PAIRS) /* This is an alternative that takes about 25% less memory. * * { * { 1, 1 }, * { 2, 2 }, * { 3, 3 }, * ... * } */ foreach (const QPointF &point, polygon) { writer.writeStartTable(); writer.setSuppressNewlines(true); const QPoint pixelCoordinates = toPixel(point.x(), point.y()); writer.writeValue(pixelCoordinates.x()); writer.writeValue(pixelCoordinates.y()); writer.writeEndTable(); writer.setSuppressNewlines(false); } #elif defined(POLYGON_FORMAT_OPTIMAL) /* Writing it out in two tables, one for the x coordinates and one for * the y coordinates. It is a compromise between code readability and * performance. This takes the least amount of memory (60% less than * the first approach). * * x = { 1, 2, 3, ... } * y = { 1, 2, 3, ... } */ writer.writeStartTable("x"); writer.setSuppressNewlines(true); foreach (const QPointF &point, polygon) writer.writeValue(toPixel(point.x(), point.y()).x()); writer.writeEndTable(); writer.setSuppressNewlines(false); writer.writeStartTable("y"); writer.setSuppressNewlines(true); foreach (const QPointF &point, polygon) writer.writeValue(toPixel(point.x(), point.y()).y()); writer.writeEndTable(); writer.setSuppressNewlines(false); #endif writer.writeEndTable(); } writeProperties(writer, mapObject->properties()); writer.writeEndTable(); } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Lua, LuaPlugin) #endif tiled-qt-0.9.1/src/plugins/lua/luaplugin.h000066400000000000000000000043611217502731700204570ustar00rootroot00000000000000/* * Lua Tiled Plugin * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef LUAPLUGIN_H #define LUAPLUGIN_H #include "lua_global.h" #include "gidmapper.h" #include "mapwriterinterface.h" #include #include namespace Tiled { class MapObject; class ObjectGroup; class Properties; class TileLayer; class Tileset; } namespace Lua { class LuaTableWriter; /** * This plugin allows exporting maps as Lua files. */ class LUASHARED_EXPORT LuaPlugin : public QObject, public Tiled::MapWriterInterface { Q_OBJECT Q_INTERFACES(Tiled::MapWriterInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapReaderInterface" FILE "plugin.json") #endif public: LuaPlugin(); // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); QString nameFilter() const; QString errorString() const; private: void writeMap(LuaTableWriter &, const Tiled::Map *); void writeProperties(LuaTableWriter &, const Tiled::Properties &); void writeTileset(LuaTableWriter &, const Tiled::Tileset *, unsigned firstGid); void writeTileLayer(LuaTableWriter &, const Tiled::TileLayer *); void writeObjectGroup(LuaTableWriter &, const Tiled::ObjectGroup *); void writeImageLayer(LuaTableWriter &, const Tiled::ImageLayer *); void writeMapObject(LuaTableWriter &, const Tiled::MapObject *); QString mError; QDir mMapDir; // The directory in which the map is being saved Tiled::GidMapper mGidMapper; }; } // namespace Lua #endif // LUAPLUGIN_H tiled-qt-0.9.1/src/plugins/lua/luatablewriter.cpp000066400000000000000000000112461217502731700220400ustar00rootroot00000000000000/* * Lua Tiled Plugin * Copyright 2011-2013, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "luatablewriter.h" #include namespace Lua { LuaTableWriter::LuaTableWriter(QIODevice *device) : m_device(device) , m_indent(0) , m_valueSeparator(',') , m_suppressNewlines(false) , m_newLine(true) , m_valueWritten(false) , m_error(false) { } void LuaTableWriter::writeStartDocument() { Q_ASSERT(m_indent == 0); } void LuaTableWriter::writeEndDocument() { Q_ASSERT(m_indent == 0); write('\n'); } void LuaTableWriter::writeStartTable() { prepareNewLine(); write('{'); ++m_indent; m_newLine = false; m_valueWritten = false; } void LuaTableWriter::writeStartReturnTable() { prepareNewLine(); write("return {"); ++m_indent; m_newLine = false; m_valueWritten = false; } void LuaTableWriter::writeStartTable(const QByteArray &name) { prepareNewLine(); write(name + " = {"); ++m_indent; m_newLine = false; m_valueWritten = false; } void LuaTableWriter::writeEndTable() { --m_indent; if (m_valueWritten) writeNewline(); write('}'); m_newLine = false; m_valueWritten = true; } void LuaTableWriter::writeValue(const QByteArray &value) { prepareNewValue(); write('"'); write(value); write('"'); m_newLine = false; m_valueWritten = true; } void LuaTableWriter::writeUnquotedValue(const QByteArray &value) { prepareNewValue(); write(value); m_newLine = false; m_valueWritten = true; } void LuaTableWriter::writeKeyAndValue(const QByteArray &key, const char *value) { prepareNewLine(); write(key); write(" = \""); write(value); write('"'); m_newLine = false; m_valueWritten = true; } void LuaTableWriter::writeKeyAndValue(const QByteArray &key, const QByteArray &value) { prepareNewLine(); write(key); write(" = \""); write(value); write('"'); m_newLine = false; m_valueWritten = true; } void LuaTableWriter::writeQuotedKeyAndValue(const QString &key, const QString &value) { prepareNewLine(); write('['); write(quote(key).toUtf8()); write("] = "); write(quote(value).toUtf8()); m_newLine = false; m_valueWritten = true; } void LuaTableWriter::writeKeyAndUnquotedValue(const QByteArray &key, const QByteArray &value) { prepareNewLine(); write(key); write(" = "); write(value); m_newLine = false; m_valueWritten = true; } /** * Quotes the given string, escaping special characters as necessary. */ QString LuaTableWriter::quote(const QString &str) { QString quoted("\""); for (int i = 0; i < str.length(); ++i) { const QChar c = str.at(i); switch (c.unicode()) { case '\\': quoted.append(QLatin1String("\\\\")); break; case '"': quoted.append(QLatin1String("\\\"")); break; case '\n': quoted.append(QLatin1String("\\n")); break; default: quoted.append(c); } } quoted.append(QLatin1Char('"')); return quoted; } void LuaTableWriter::prepareNewLine() { if (m_valueWritten) { write(m_valueSeparator); m_valueWritten = false; } writeNewline(); } void LuaTableWriter::prepareNewValue() { if (!m_valueWritten) { writeNewline(); } else { write(m_valueSeparator); write(' '); } } void LuaTableWriter::writeIndent() { for (int level = m_indent; level; --level) write(" "); } void LuaTableWriter::writeNewline() { if (!m_newLine) { if (m_suppressNewlines) { write(' '); } else { write('\n'); writeIndent(); } m_newLine = true; } } void LuaTableWriter::write(const char *bytes, unsigned length) { if (m_device->write(bytes, length) != length) m_error = true; } } // namespace Lua tiled-qt-0.9.1/src/plugins/lua/luatablewriter.h000066400000000000000000000105111217502731700214770ustar00rootroot00000000000000/* * Lua Tiled Plugin * Copyright 2011-2013, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef LUATABLEWRITER_H #define LUATABLEWRITER_H #include #include class QIODevice; namespace Lua { /** * Makes it easy to produce a well formatted Lua table. */ class LuaTableWriter { public: LuaTableWriter(QIODevice *device); void writeStartDocument(); void writeEndDocument(); void writeStartTable(); void writeStartReturnTable(); void writeStartTable(const QByteArray &name); void writeEndTable(); void writeValue(int value); void writeValue(unsigned value); void writeValue(const QByteArray &value); void writeValue(const QString &value); void writeUnquotedValue(const QByteArray &value); void writeKeyAndValue(const QByteArray &key, int value); void writeKeyAndValue(const QByteArray &key, unsigned value); void writeKeyAndValue(const QByteArray &key, double value); void writeKeyAndValue(const QByteArray &key, bool value); void writeKeyAndValue(const QByteArray &key, const char *value); void writeKeyAndValue(const QByteArray &key, const QByteArray &value); void writeKeyAndValue(const QByteArray &key, const QString &value); void writeQuotedKeyAndValue(const QString &key, const QString &value); void writeKeyAndUnquotedValue(const QByteArray &key, const QByteArray &value); void setSuppressNewlines(bool suppressNewlines); bool suppressNewlines() const; void prepareNewLine(); bool hasError() const { return m_error; } static QString quote(const QString &str); private: void prepareNewValue(); void writeIndent(); void writeNewline(); void write(const char *bytes, unsigned length); void write(const char *bytes); void write(const QByteArray &bytes); void write(char c); QIODevice *m_device; int m_indent; char m_valueSeparator; bool m_suppressNewlines; bool m_newLine; bool m_valueWritten; bool m_error; }; inline void LuaTableWriter::writeValue(int value) { writeUnquotedValue(QByteArray::number(value)); } inline void LuaTableWriter::writeValue(unsigned value) { writeUnquotedValue(QByteArray::number(value)); } inline void LuaTableWriter::writeValue(const QString &value) { writeUnquotedValue(quote(value).toUtf8()); } inline void LuaTableWriter::writeKeyAndValue(const QByteArray &key, int value) { writeKeyAndUnquotedValue(key, QByteArray::number(value)); } inline void LuaTableWriter::writeKeyAndValue(const QByteArray &key, unsigned value) { writeKeyAndUnquotedValue(key, QByteArray::number(value)); } inline void LuaTableWriter::writeKeyAndValue(const QByteArray &key, double value) { writeKeyAndUnquotedValue(key, QByteArray::number(value)); } inline void LuaTableWriter::writeKeyAndValue(const QByteArray &key, bool value) { writeKeyAndUnquotedValue(key, value ? "true" : "false"); } inline void LuaTableWriter::writeKeyAndValue(const QByteArray &key, const QString &value) { writeKeyAndUnquotedValue(key, quote(value).toUtf8()); } inline void LuaTableWriter::write(const char *bytes) { write(bytes, qstrlen(bytes)); } inline void LuaTableWriter::write(const QByteArray &bytes) { write(bytes.constData(), bytes.length()); } inline void LuaTableWriter::write(char c) { write(&c, 1); } /** * Sets whether newlines should be suppressed. While newlines are suppressed, * the writer will write out spaces instead of newlines. */ inline void LuaTableWriter::setSuppressNewlines(bool suppressNewlines) { m_suppressNewlines = suppressNewlines; } inline bool LuaTableWriter::suppressNewlines() const { return m_suppressNewlines; } } // namespace Lua #endif // LUATABLEWRITER_H tiled-qt-0.9.1/src/plugins/lua/plugin.json000066400000000000000000000000321217502731700204660ustar00rootroot00000000000000{ "Keys": [ "notused" ] } tiled-qt-0.9.1/src/plugins/plugin.pri000066400000000000000000000017241217502731700175370ustar00rootroot00000000000000isEmpty(TARGET) { error("plugin.pri: You must provide a TARGET") } TEMPLATE = lib CONFIG += plugin contains(QT_CONFIG, reduce_exports): CONFIG += hide_symbols win32 { DESTDIR = $$OUT_PWD/../../../plugins/tiled } else:macx { DESTDIR = $$OUT_PWD/../../../bin/Tiled.app/Contents/PlugIns } else { DESTDIR = $$OUT_PWD/../../../lib/tiled/plugins } include(../../tiled.pri) target.path = $${LIBDIR}/tiled/plugins INSTALLS += target include(../libtiled/libtiled.pri) macx { QMAKE_LIBDIR += $$OUT_PWD/../../../bin/Tiled.app/Contents/Frameworks } else:win32 { LIBS += -L$$OUT_PWD/../../../lib } else { QMAKE_LIBDIR += $$OUT_PWD/../../../lib } # Set rpath so that the plugin will resolve libtiled correctly !win32:!macx:contains(RPATH, yes) { QMAKE_RPATHDIR += \$\$ORIGIN/../.. # It is not possible to use ORIGIN in QMAKE_RPATHDIR, so a bit manually QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$$join(QMAKE_RPATHDIR, ":")\' QMAKE_RPATHDIR = } tiled-qt-0.9.1/src/plugins/plugins.pro000066400000000000000000000002311217502731700177200ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = flare \ droidcraft \ json \ lua \ tengine \ tmw \ replicaisland tiled-qt-0.9.1/src/plugins/replicaisland/000077500000000000000000000000001217502731700203335ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/replicaisland/MAKING_TILESETS_FOR_OBJECTS.txt000066400000000000000000000024071217502731700252400ustar00rootroot00000000000000Replica Island has variably-sized objects, stored as individual images (and sequences of images for frames). But we need to represent our objects as a single tiled image. If you have a bunch of object images of various sizes, and you want to make a tiled image, you place one image of each object in a directory, numbering them sequentially and putting in transparent placeholders for unused object IDs: 00_player.png 01_coin.png 02_ruby.png 03_diary.png 04_trans.png You can then convert these images into a tile image using the 'convert' and 'montage' commands from ImageMagick: # Trim transparent borders off of images. This is necessary to make # nice tiles out of characters who are 1.5 tiles high with a 2-tile high # image. mkdir trimmed for F in *.png; do convert $F -bordercolor none -border 1x1 -trim +repage trimmed/$F done cd trimmed # Shrink images larger than 32x32. mkdir shrunk for F in *.png; do convert $F -resize '32x32>' shrunk/$F done cd shrunk # Center images on 32x32 tiles. mkdir fitted for F in *.png; do convert $F -background none -gravity center -extent 32x32 fitted/$F done cd fitted # Assemble tiles into a single image. montage *.png -tile 7x -geometry +0+0 -background none objects.png tiled-qt-0.9.1/src/plugins/replicaisland/cave.png000066400000000000000000002522741217502731700217730ustar00rootroot00000000000000PNG  IHDRŐgtEXtSoftwareAdobe ImageReadyqe<T^IDATxk\Օ[qܙ3j=Cݶ~*CK EƖx/A!! =) $^ѸʒR!Y,ƌ=37b?̇?tܘ1qs'W:''OVReĎs{}ز/lq>ڿ>w ?Goh?{}]?tM[7wO]/yy[yb{{}g"]׳~g|XskY:~C#Eߜ\s+؏_o]:zYсۿǓ/7X{jLߞKĺK}_^џ^/m8;v]ۇWzkߗn_1Í_\fڦtۃO}{ }=#|~mS<_CM_Nǁo?k-|.lu':{~x>gn9:|im sW֯$_|@?yi`yzڴ_mgs W7~={w_^k׬u}Nq KU8o֘h& %"zaʳ򽘇bPܓ!x{0X ڑ6|aQx \d(ڿNyF +ߘk|پχ 쳩mt /L7/ |<,FBXu_!}-=tO>Z<; `,ƫS4xn'=T_DƭbA s-o^3nVA5x\YmEg{A0~H/{Ksh BidQr(wYz/L;b޳Cc&x?מQ7|'&o\MJ `gnyi~|CgI9͎]7>[ 8e3-X Zge€>1wg@_r^O:P&}'ƒ3`! }~XBijk~!?e?olAg?ǿ7@/ǿel5vr >7Qw ᦝ>硸7 }$٥Y=M:d8P/F9G?l43?}&a-oΘU8KL"`nhZ~'.YeCgEBQX:_97SEǞF@e_b?طxAK>Ul:d(c߸u>Ot֨] _Rk6 +o?=n9g<{SǮ?#鍧EnY=y[ϭyRkƼK &:0(96>H<1أkݚ&s64Q:\.KvqGoA?Vk& sKnzE4ebpYl8q';#t|(hA Sv鳘@.N}dD?:b3)젘G=Q6i8K>C+ᵢKRh-)D=!U[urs&ޝ^観Cyv^NX]Ӷ F (04:!p d{rz(Qȇcހ>!a.EZ\dn"dgvB-$ j`,V".z=4Af< L,ILa~g @֪NEg<$>sp83>;-'Yxf,1ǥcOu\ϵB6_ P]UcN58ew(~Prk%Eқ?]3Q*Y4tgc_]/n>ŻR|`XKv~jw ߔy!8Q\W$X"^&sNg($M n0` >>4Q @AQ"R;ev\ (kѦW/0n0XB|_ro20(qhJ{M@mw\مeGo6caK τ0T O e`s)`zvEZoriՏA?>?Px_a>gr$„YL"Сh ;p9$s0 T c,Zh~7 \߇;.}e d^-3(!DI-~[E `,a@X[#/F?YП]pvvzX`x=F/,:ќg@тg xlV+$;.wcIk"_gl|ڟgG׬+CtU.t]t_FW /-o]l5jK ҈ϊ+p#e@FY= D5 J}XG} nGgH z5.@IU b:_hkg+JPd6$əFl^7Da^fT 0v|:wMiNH @A ;~#7_xv4|GcTI02W( ~?>}gsxѳmM.[0Ӣe?]q_G߹/o\97>?@/?0c3IC;@);KImPp|3*u;Ifv]}b%KjBt/$ *I@^6ߚh}04[Q a̰W\2/U~)+}uO\Ed;s$B>N@$}_͗}ѓk myaZEqi^lԈ `lxֿi5_nfgDw,n*gDn\hՖEё+<|I)tl:!nPkiw7Mf w~ls!- 1Owq/ȥ?gb4:ᙵmsqYVؚ5x6?/|C(@^-@*}ns:G/'8daw?;2ߟ90VYXhho> Z9mxeM 0;^ DoGmD[6}%:[ODGw.Na`ΓWz}amw`vYNT8mW!Ztɲ;HяC*sgCºUrafUaB w%siN|2 !P)L*+&v4wse3q{5.ዂCneX(f@5qѭ)ߧ&P< R^D)BXʾils`744}& Mww}1iύ&v^=#[.zmsg&:}wDO^m#PPܬIW lf6i-m|+YN?qfg1n0ɾtFq%{gL xmX_p3!&jI{5{ ѺUf-'U L@'NRr/A&OS. VR#> kDu6rh|עWSܳu_^|{і~;:g-^o.^Peq5ڙ2 B< ' P;Tf7q鬭Ƶ/^ $ptsM2Qe? ǟ=uI0@NݿY,ⅳ,cU h~f?7bvj;^nzw5aW|lP `(;*^oRl[K 1LY"q4P ѾD~jtrkB?']ߦBx=ҋt-Lυo&y3uW]*jݻRe7Y<{cMr 媟w[`O"kΈrϊ.?}νOs\{'Dmw,\- 2C21S(wzt;e4S&VjS6H$g)[EB}k3P32{  W3NE_o`ܓq܈}hhet!}5qJkA͈3_p^n7%2P*:(8e<4#3Ղ|yŠ֒I- ÿG^|=,1'̵_)6"_Z,H Diҷ _N8hz/|1܎uo;&a?ڿaA'u81xgeE4xŧo\s3o?qE85M?c8q9W!1PɦKf<M= rj|}4YH0G=SޱyFX~{|$gKGzWrͪ2 U66=?jYbr?gLZ4` @`J#WJ]*†Eil@YdqÖ1Ϳ̒ ̶R%ۘQT#_5qƳ?s×Ʊ8Tl$gd|6N"m]ɫ٩%&0L+uk}DA- ߁5kb,32" M7?(pSA˦A:GUpN{.}&';cREhr;aSݖ? 1ťw`QoYѝ,hƓew'E>=6f|2z'27I{$ʗX.\$0/7f+eo%b'KUHF׀ &}܍01xLgl8F' ߆y>GNV*kR Β55fWy.2Y[pͫBc/s%[(z <@9WM 2 {сۿ:d1_a@ HmlÄI򻑜P d:KnX<PT0hE~ztMno[ןX.ыe!s9OV8a^u`<Qޜs6!^t&0 MH_#B \84p9Ϳ=\+fkQ~]!Yv;.?yp^w+TߚAC0לT*|(o\~bP]yo_X鲓bVdNI;`{{89hͧ}/zqE73sL9qFe~*^W P_@P>O*S}F ]yZf9&" e"S' J_1*&_BQgP3s̒a\Yg-8CSRN΄~gȅa\f,FY_H Fm?a]4@@qv/;9r&~O~~פȎon}@7r|hG:kweAnxlNϮψ~o{;)zphFOlFt`ѳP9[*ic8dž{qiL /9,7vZ=7FfΎy`E\b%_} vy߈>m4mDM}Es#I!?i6ӔlHN>("Khh3sӜ2!м.T^k[%޵Sg Q x4?'+M8!w2/JLctSpf??'`@jV/Щvewn<<=q>s99Il%ZqW9׏$i]U:P6ys睳5R֡Iǃ|EߗBLXvǨyENxnyچP!U@pmKv$"sU1b k@8lZ˴Hݭ+eU uv݋|(B; 9!YB9Q@=\$ܺ'͕_ea箕n7!z~W[ASe?~)Yv[8#Ta JelhMf}ŕ ˵L!s0ЧMʼn_C[ބ_>QB\O^9ې>; l3V?d21?:(yuܻזF4?>ݬ.aP!޴QZ(a9%'uT` W~7s_ (Pr%mP S$Ӓ&#d@^TEVLJd9R){O-鐡zgp0$Mf%շ-9'p?N„{àoΟ(ͷ"3 {n'?.޽~uS9a37!)+o CKOEs5lk$0DʇaL  csl_؏\%jBʈ@%s(-sJfHSIUKKm`VX}-4uF`lB1gwv0ͣYal܋ K/? 9ؗSn짳_+©0f@` K nҍ|v +Py/ XF"?.ׇN):Hȓ\UX90swsaP` (iS H+u@TK.PA D1D+ul팢e36;@mik a/ZBQd"}d~AWmB\>k6}"UY-[Bl%2 \7a)%@77柴-Oͷ4j b}UL낆aUY56ywX]3Đ?UmSB8t^F|7 jح-_-kZ9c9Ӫ6 kRYP2BIx $a{z: :f¤DlKfT<&QKH%liJB*?+rKr ͢u TQJz**Yk\gg꯫jd*I32gfcYC'x{/N'a@e-9q;>ccs@9Po`\d(`fڊ+{Pe˃$*L qZ"*2^&cߋ~wmڵ@?TjwѿܯL*kije^Xvv\Cs8)|n:dd$Vm>pOɕF0ĀNeFD&<֐ [Zx 3Ϥ`O-' ,"3+ %+G{h'!}@+(Kjuov)':FZK`?ӣߏc9lK)mYf_}gVqiq%eϙx>Mѳj_ @! _ێp;WgK~Kٸ4ZO}z LAasX 5%sIe.O88ZH2nYICx)tbSVf;&C/=?E t~mqx2i}(=c4p8@vr.a3i4*v15MU_&'!M)OϠRzެxrd3[ 7!~Ed]oLE9fǎEw=h^7=Jkk_FR?)?ẑje'yP4۟0Xa_⹧l;o >- NfٌƍQJ0 TN$lX)΂EPΏsU0LB"bK_ls4㡁Y惝 MCX6Uq|-T3؇>ó$XE؅ȣ[4REh #le}s}[rf}z& @AΏ G76LHπIXVDHq"b;{eՖѕEt¿i`tCuYv/WoV\tOE(JbÕNߗ8$]GۢNT$9 sV<3GRE^7|>_c@_ίf@x3PH1]!W٭dJ Ȍ#8=Jx0 ECxc€0ҀZAnB&Tx;'-Bݗ<˕ 0+3)W}V)) ]e=ctW ʑ%ȡ'IK]d,kf':V*rO!bwrk`Ḿw ,e<AǸ0a(dx΁0xSzXAnN.}Y8_hckO/Wcxhq]6c2zb]QDHDI?p|Ӯٺ/Z=-h F+wQC7!8(4Zc %[. Ozki]E*R?+=O\d޴| kF""A+ɪWl3d*|K*K Qf#5YY% dKuR-qi(*G9 TKrSL&zRjI[8؊}R@F@ $E~PB;xz݄BA51 bt8cM\/LR8FsTͪ/Y4%WnQ\CѵɊܑe kvq IՈ(k,rtˢϼw[߭)vVW "oDk?r,vh#K p-tZMywsȲWO*djĖZ"eBɆB8q%Za(F#P贫Q(^=2G`u 8vE~Ug4[ dԲ*x;*lR6!v ܸMM7^#KCo|9v(8ݰ}Y@5 9*dsk#qm #f_yUq U P29zzB%?I2 GG8uO,Lót @lK EVBG+I ~(r0|Ћ:?9wr@XL(!}~$ qjYе,<1<0Q̦B |&,%6hx$dD ŸNfm=Odd{1?o>?_aK)9`Bȸ'G2(ʬF1@g ^J2z[Tg4ub|q* sYdYn,^E ίEK؁zpŽ?Y ⎧_+#9BA_wiM,ˁ+K:Y[U~F4adj)+0?Hbk1z|sBI#>VB\q|]̖v'߫!aTЧ@*їCއ&2;ϩ]\C_1 .:gPSGP"RRaS xz$Qd??:Bi# `I Fٚc@Qy"y~='I?-Qm.j-aljǻ0kj:8~L/SG.ky^&Tůn~Jf0遭5Á ,1vΕ2u7?)JP[;&pf)AaѮE;B~eN@Hx"=xg)~>((@xOs%_e`]),5\J?2)Zҫ^أa95$R`=R3['Uw0/qsT/M̳Z؛;-SKA@ V [Rb!Y7~y B{X+@߅o5{m 㭘1ЊgW\?OTP«ʌP/좼hEʶ__@Yl[5 /GK+ZMW脾Smt߳k:*tޣ-(#QYeO)zEٱTj6K d=y$klxxW\gYϬr,V)EB´j,?Y-OyfpoC$<1gѷiYÝ ( NvA|i@ Hw:wbje W!gPh{ -usIY?D33?/7E;o4cBŃcplH&@bhWHZ\LP_aǽ{1 LPA<evV_NaOGtq'zYg1ep.`׮]՛njժl)}K3f(5Zvce7Q>qn'r_ڄY\{)X@z_ׇݜkPt֚O%\uיE" -_RQ1~i}¤Tؚsb3vE_׈։-/; ng{g3['p \ g,Y#d.OR|oe?c 2ॷK4wzqX Bd@ZP̰$i3S x[ߦz[peZNHldT) ޽;իWG&JycU$O/#+kŽN l^``QWqR*67ݷȜtJ\l/+3j0 d@%n;[U LVXrYeΧY&J1@xƭE-4<VD(2MaRNIŠuOD63~Tv c}R4MʮDS3W՝f87eIWK?gl4w0 :hL8VhLlYˠd±zvvOp@Y ׅ`xC ( $}bzaI(BK o.F4T5Qg'}(sp;WdMYA%]JH1*{ !+ |E٫-ub)8 ZEʌ,tHkkT[24(f^eڊM82e5x݆SESovjcS/oaR񟄶M;xsiv3q] =_2hærV&6-c,!GxWTahn: /~ORD}R$5UDV}?ifiZS7B]鼭Q8ɧl_WA٣M'4^dm< ZK(I# Tζݙ2yh/Nu6e `-5բy[/-Ad7&(g_R@4?hbeϫ=Я]͵/'"&X77#5$USOI݅U/c^aXs; D(%U'J$T,3/9/'F#fG !d||k{j~?a`Re#Qb@/sH`.r1x{T>? p.,0nR k.ƒBV\̜YQ5g0[>f ZfB23oAj&}[ŸJ)؇JXi ѹCmfM !s Lhl>s~XUCA P:T|49zQ wϳ5JM*E>@Yv^0S8a0OU> ϵ2C2/\'5,d2SWM2$z?Q?xFjJz. 970&Pcܐk4R.p.s pU0GMȿǜZ ߫O; WPPyNUdwJx{zIQ5 @YJD2볖%m2J3CLj64[y D RKSS<UBbTS5%\Q<[ = ȶ_B2rv*EM*)el:򬊻_MXGBIa(,b y3gi_%3A LŠ4R8"RlIa/GQ!{$ױMwi!Zl>˫G:aJR$>j80^+p!"7ʠ/ſG{ߨS輷~|}~ Phv P( P9<yC3ZSھt=WRP`mMjZРvLx7?LxX㷱-?pSq :k(:oCZء` 4> mxj۳vs ! @ga6='toϛQsg[[K_G F)K'9 9H_hi38ly)*`"`Q&vJl@Tu^P)\?a52KeÔ~ЏvE"1_2~_5ekƚ9]sW$1E6tF?⶞{Syɉ5M5ܣLN;( d^Y@Zvm _m4\WF:LO+u-(]ݼJsKbx <" h(;z֑c^6<(o?,:')oX* mݿ] N8 l}"`j ۳p ];FvwX XQErkgWزP -K,p@۰ +,f%u:gTɢ@6M'7r 0x-˺4Iu'gm<`>p1W7قfET?)jy8-OVf)A=F=6ņ K( E3w)9ي٤Vz o]'X/lR@n=ALU#cYRT~@IMzhDc p^V mY&[v^Vd%a/N[~μɭO~YYud b4a<=&dX+:12/<6 ib7 a0YeQP2˖@f p ֍se*ܥ20}{]؝e@U@:Mj'^f $ :Z!ܩB?b+E P $݋9՚1PN@<,бB䤗@4B::cX*`₻M(9(dS)}@xv`%_wDE2:({EYC`xF~+,e7_4b[1r+wu"'z1jِkn$|ITF{ig'|J"qn 'Rgqrڲ^u4@%h̫2eY̫s^<Yٴ!EF"F?Xy=di <bkK` 僕B^:>,3 jg\;}^󿼰B+0Ro@ׄj(\]@_d(kP~?JD8 Bt rkhJ0?J\hY5qVɒDFRcuA#G+7K(1: 1GùY X7n0DN a*+upWQKy8O1p %d/kH(DL̨M/Lxn͸a2axGя00͢3Ph ݗ۹VJ^]0ԏކ/y{ iuGU'1˂\Y_MUT^Eˤ>J<_H cGYpkay' ZMa ~-1@( 웅 (}*^^8KhNut{dQ?v7- "QKV7-@~g,ɮ|]P<胀t6o&U\26"qЧLR@`6'TH:drO"ې.< P@ !k>&ܷ`[roweqs1X%['@*O;5xV 㨥0L@6QE@(U{8QR$!8u?ڐ"[ɯ\ M?Ddᑤ&:8n^y}R FKIy-/܀ 'a $$j2ۅg"x`@$' P#—NUbZr# >5sj ?#DKNyJbCeJϸMoW5R<'@&S3g܋gPԆvv% B<*l3=t oE5qbS"`)iЂL {' ]jY` HXT&})2>}ާ^yاK e+pX+BgEav́_gci͖R? "! ⷵ_&r^J3[/v_QMYQ}}Qʟҷɬveoϙ0rw KPn~'@rql-QFVH("NZ 3bHQ(h" 6V-B hW$.@I)Y]yt<`#ؕqOnN#(dɩFsY&Fz+>2I!nWa0“ 9b|`Vhl8#-xLt}ǵ3V %*Wd4e: NK;-Z|ar~ 8Y-@H[߀9ka~L+yl"^D)ZP̱$BE[3YkwEP&}f}1xv(ζxOumiwYpK%n-]4d< Kd%, L@z ;;- vYpSyus(%-  ǻxG>fb1a|J[;vec9!m^D  }d$mjgbGܱũr5ߛߓzۓBIN?,|q{% >%`}Cci2i pR|j~`|`*b gmAݲ|P@iVw_sz jn]QyQת;_EeҗkWu $~AN>kX@ohʺH;n+l6tux\d#* 3IЄo%ӧ2 P:{tb=uVP a[Rٖ>H83b,63ҵYR zN ˣޗW!PB"*g͜#Z}iRߔ(:^xS|N&ST]_KG/sb@?{ݨ7WNL*iL$7FMaYŜƵg~>x\[T07Lie=$:HS.qaff$ 0혅mrr+;/z6 ,[6?س9vRܟqRJáocɯV1?9:DȷG:LJ}Z֚o t N]+@&k_u4S{p;֒vlS: {~1?_{坥NR]eot;.I~s? 8 }8zwLj6#HX*jk۝̙|.-Լ8i=MVQO1rHVR7>Vm9`ק٠oh2MjU<`7VfO;?k5 !MT ?ㄈ?ϽspȄ…9ȵDUAkKQtBfv"sRcgw/ᆟ@wAκ}X/\GA~tq2mKꈇ 88d[#:]j;urJv=]p,Hx z@}- 4^ypCy ğ#]R兛Uf"*vi'˵)k&V=$ұoYe_Ef/Kwtm Gt'Y/Sb퀕G!w$"c{@ch `–;@& 2#PbCĔd-$t!0SiY/d7UC^%$;H%,q%fwB Ї&BSCzU2#5XVQY95Ϣ=hvI_C{]~+/s);*ׄNIowd(9vrzY{osp&DHɠtdW1#DOƕVN@}Odgl&ů46?:`pFE7=Q(yBſ}ƵWN?4zzWo{G}4ڶmxif7U<ƴgzB 3e$ >0*?~ Y,p+arg)@ /_S#QP+gIEZI\339cvJ`@C<6BOXy +2%gƾ?Rі ,}xCzl_hUff;ogM1H8LuAVA{)DR"%)EgeοB0cP*u:JEϨ#@[Ӊ{ MQtU3+ xx&Y%eٽ+uEٝ[`w:-,ꬳK/ʙv: m`*XX-zWf-_J\ω7mNKG\W*d&? aZ<,4qzm)2e0釣˂T7˔VH&`IO_=p}tX+.JZT&9 e,Ч<;'E-@gj񣼱pZ(~oI/;]G< x^{HvwO_tE :8([)vYy<媰gj炕LrQVKKUBy@Ty*0RU9^a>9&J=P^| UswlAزAYu:_>s,P O[>jJ_ ]s P<uRp`r|bKW[`C;8ߵ p7:_s(,"lYNukj=zt8wuWtWwt/k@_ ,DU^v;P yCL2sO v\68~Ӹ?Z!L6%ma`>дgEƿ#$GXw2(U@a6&N* I{@AgR蚧:SSWAYG׺EdG(OP}tP Vtz@peϚଢ଼+?.lէ6n8]!P[9xfM2*:d@NŬve3 &۬8eGgRǬ8w{{WQcvYgRri. Iltg,;[Ӻ6Y+ #<@,6^`!wa >GQ6ҷbۿZX\J_(fyEQ9?ϵd5| w[6M{>qɓ`;.,٩D@,>д#Lfl̔={ЩUȘkðixQ~i"fuɗDMAYBB?pE?K[[W"_P 8>dƿٌ'G;?ܾ:?cyߔ6́ryd90IHŦ._ϯͿ"|E? uQ mG)@:8qS/{+@;pɭOD_kn LZJQ;`hh̛,[Ȏ @!Y{QF3ںJ۩ 6?*$Ⅱ -ү+>ğ3%صS}v+<*\ggpB7vs߈; ǿ%>MNXLc<-%Jοe eUsԁ?MMf bJjʁ5 01تG(_Q/ܻHƐUe[O{u'?<ӗw?oP+e)e7΅߾|}|?Hƕ]c4ߓ.ϿXδ DLB 'T'ߴp ̏b|a;U&Wi ,X6 hɵʱW'Vm$qV1ċҷ){9Z3vƿSW-L`# B I_m=E?b;grX`sv}hP!ԊiO,p'?iz7>R P?5 򾛍(7#\f~jwY?O;/ǭ,3 *zRS8v=,9(xl{,}iFL ]-5^TBg@ UY4q@b0{Z 2bQXYʨp%sb`kY'k O,ei$2c1KG;*4\ }@Yl$h (H!}g;j(W{GfH4y ѲOYffy9ۢ|(齔rvE:ʒL89PbD,%* ADI!ͣoc#܃khtOgV ?ΌX?K(p< LJQGoOPXTY-3(nQЎ?`?!rl=YzC>ʏ39eaSxG5%T^W~ 6lgc-,"w߷ BRz}6t`уc|d!$yL8 1ol __-Z|e[p7y0@0"dP";,?*G(5'Rvk#~rĜTIfjc|Q2Ya|{*w,~VX|C8%y=op}LsW1Z`3ʼn߰S{P e%'z΁YYGEY@s[.3XG}.,<"',@gXʑ2RE(Ƒ? mkBR$G_kN;|>` =k"X9>:KbuAϧMUក$Lj R `Cz$g/EnKt[ _/kTl%n;Jd[g!}Q_ѧON;~;Rmʌ\%ϵO`w n?αeJJ^wRcƯ`"_}"|h49 r톔)̆;Pڐ*y,%ʑ^(4~kݛ3°4Bπ*}lH7 <( y _K$7TE?wiO^RW{JJ?[g+ݰGVaYA e}ھ+PS+4|*re*L3Ϡ@}6ؑpA>χKH_:~6_~UHU P=1-c[g! ߁`ս'8uU%Q4ˡ*au^C8M~{,V"6 F}e @s[G}Hm|Xo^UQRVa=m ,v"L}8}.l~CU#&*@YˤyޑF p<*;vbijC;^tHYO?7L@(qLϦߓOFZ94^9(K :T'_ѷU}#?xlwP{o~}ѩ"@/>"@>m([6v@BA?@0SN֮'tR CO;y>vo/$7\ҙR g`'keʳ;V^$)2_E/';E7hGH“s Y{ar^dO^Z,<#k*<Gt}#(և8dB ڀ3f(O? ?L$k$+ ;|+@΃n Z\/PןE)7&w9)vvCfR"U^5o|pq\PJ#y`JH*W#oCswh64Gh7L$Losa,:;"hv΄U⢘m"k7y^QKPQ\Æw-dkkHBҗZ艿s Q?I2+ߘF_z+~v4>à4h׀\6+r ߽F9 S]c߉VUg? DZ>z{e cz[$e+k@ J3ܨ2Ya7b[[m~dBi,/Ūʞ\{-J*uNp6iT-<4%MIߔv_eЗI=kg.-ҟvmP%dP+TK} ،r8V w? 嫤<:NP]=rQʞ X?}u pus G~Ay.rrٻ]+sW?EPnK??>&pİCZ=0 ^nW27C~o_,+otC['( H 4yg,\&ZY[;E{4"_ 503LʢC_d@DdNױ߱sUZ!Q"E@*y@ ǼZצYܲ|c7{+5 @:$m@sC`9()+) 6͒ BcBI;rordzUa֟ZqşߍzmXo=۾/?<8波xndQ5”Ⱦm +Fvʷ.mrc6ן3mB`݆~)|%Zy'6?7[4=om]-~}hӽ_nhwգ?xftߚ{@|ޫǓwqt>,GA+iNaj'xW><8-6³U%&Py"hm6 gEHaJ rb7ɸXY [@P zǼo'gǠ/Oy Cu /&m׃5᪏a>EpFNs{\@x`>(o5{aapj's@~ʟ  \]E"G_߼uk}tӽg{8/{wշooK4ޮUUvW}bzSkO^?xxyvע.|+l!S׽;3oFjta^xhX~hSVs- :o~_ W@/(6Ρ`$)z;t?vIUǰdm9"e݂~}ULKasbk}.A+j,tL>ݣ_/$CNG&-4w 9 _E=?J(g[_ c[gRfN\]T-ٽ*z}tӢuWk76sp_dEoQtqS(q9Y7MOwtٱGlbqۧD.`.v|F{燝kju^8p`{]p.YbI,_Iq0~*E8y.HM_`3Q' j)Mr;mS,Sq'Ξ9Y)J7:f#J}DVm? 5秎3Zv9QSO3"Jn~@,Ȫ&@8(3dLW ʿ>yeI!TM@X#l~Kt-E|0; s.8t`', w{+.nL>v/lńd=`QlRyr/sqa-̆ɞFY0*aJ1JI^mpMXDh0&Y̑g;ohru(7?)׆_Vopcݸ`SI?<GKm`Do7sx:O?\$fzk]Zl[-_YpF9j#B1nz3'ɣxq+0&Գ\}izlq̣*vyǼKfL)ϰg~yQ9\!V8ú)ǚl *=N}7/v`̈́ b|?;Z@{@8I+DJw)< PY8DM$,oc1*`4g`M#(\$l,|eS:_`w$P:_ԾԜ-ITej[S ڹrƧ˚e0!X4Tf? 5ϱe9w_> -Y -rBV h \ 0쇣Lk8aceUQ=:zK ˤnļE&{ZROvsOe@?+#<^381_I;v8@I"ʤ0C[aLOr*LS\gk*yP܍K㇣҃%! ˋ >4O[L ^8z2z֩*z*Eh1P=p8?D<"9}p|W;VSaBeoJPPgFlsjQ&~p )pRźsnp fSUNkNMt's ^~|gcX&32;U {Tඔ-gp<@\]\gkD$Vm]$'ڶm*JȂW%61-Zcc_ #8p2B*8@<_-9y*^ 0^Ir$R%㯊*1)!XvT-Ρ{6~NN_ng7b}CG $Ϡ]83e}T4y-ıG0u84툍aflWQո _^8$J Iy'E”B8!)2]yoޛЋxo\d AJy#םyEE1K<'m)*⋹?n~#Sd,?Ʌí,V5\ϟ\?C+ 5#=7ρ\t=]|+)VNDT,"Bu@OH?M. /i&b6TPn'X74EJWzh (yN)Ik!et%[^7BHd^c&>!Gwą@];ג6,ʁR]>o_~ I7K5#=q}0xfuKzyc.`7 o"y٪*/_Me IC})8iE sJ9 QKI=Vo#IdfX ןXDq]ݴ/qj>a'l1 zz"|p[g?d0rxbMCkݕOպFx`gGtgjPLC$9HR1$HN_I d.?>yqT懜$tE,R,./3pty>H]#W9nwr!zY*>]D$˻!n&B:!JqCǿ)/)P}?x6ڈDZZ"##0W/z)]8Hn7<4` @* A Iy>T?).ǝ޾4/f@4R/>2)u\-< Q qt-+iƯ;/E\3ϟTsg 1J'&&8yct[=b|./<}E| duumimZz=Rۺ'" oDZ^n+ ғ׿ǧْ+U6'h)"2>!_ins폎s787 _ϟN>,gG8` Vi1^ Ǜ^GRxy ujy RCzoykr9$}}ZX:9_Vsq0޼4Q_p$m6R!lۗKsw-]nS7f'?A@EZ,G|-quvqIl|Z5a#-7>~2>Pᕬ)8QB .`-~{jbt?NxO`_$eQˉMzsw翷oo+]@_ߝʑ*,?tچi٭Bse3`Pi[R%`<4Dr%ܞcxz/?bQrë a,~ l3%Gwl3I1Hc #{%#Բ1<}0P2/8ٝyļfN%åG Kr݀qxwvL4= kbuu=/<тĬAe4翷oOV/cr5X@e]YHG:U`cf%f9fs.54翷;/fNTd|lP(bv F q:>c^vi2ݬ ` l]L_ʲ'_ȕGj{&."S/'Y"?mQM51D7YIF'a(/nѵc|_Ԃ$hij!)$|X'̗i hj'>q !&w| ^3zPD=bJ\ߧk_#qFZ'w?|F.j ') h.$)z:lC6A_'O3Y$b^v5>sҚ^1KyG?0H:ӬoG_:pCxuh`?\'1y+b_qz7ݦ~39KӣI#r9ncNXtq2ߩP1|}cCBqK)c. D–h>O{{˩7￞LvlV~h@⌀6Зy̌#˟f|8'&t-,ϟ] A|ľ!OO^(ߔ;':{. 4y2ҁz\Le+l @v)T\ر6,?͕._I~s@<" '<7P/RG9yJc LS<>_%H]cˋ7iVUQ\<_O!0GT˻e3s1?j REn?4H/=L~2=֜Y<66=z q[@kwR߅T(@4iyf`i*p $o=MlQ̪W͗)s[w#$7;t@ΛW@g\w \$qx gח5<.x/5—i6lAL\믎Ө(i_/1E` ԆVoG+=c u6 3g%=ȴ?Ϝ>ЅG0ɤ X7jT`)8wy,x/!mmmx/p ~!W\=8taׅVp̞=Æ x" 7aeJOf0pT##x `b"O~z 0# P@?-KNSE=`3j@ r'!7vҭ_o%,Au,1hb޿/z|]`Y'S.1c"h>K yLӟv= :GEG_( !ODb,ڭDrJqBA`H_j&t`522\|7T5Y޾0>5\`t@O(s'l5}j<ҟA9 _{DS:QA&ei1+sCdD^,T ,C_N~c2),/?l/SGOx%u/Fs^i 5g=M`("fUw"fZd/N6<7LqDmjOzG4j:qے `~(fjErd/j|y?O IWâ=EO@@M0&Y8 o38y$T78p@֭[bŊneٲe <=|E~w 묩0}һk2?'}6}7K{`7u2w c0>yc!] Ucc¸ѿQ^w?f<_ǘ<6yh!EiouR]ߏ'/}swm{{&䙞P.'O^G̢Al?gm~MsYQ}#8M% H= }X.c,Que-[~c)O "]G#Lu| йO[Ctܗ9_Ȋ̛1]PI_H I}ǧ/2<X0}Է !p/H ݪ\ot.z=EoK}Ϸ$ǀA<@^{<Ӌ!~r_<o,!PR7 @"z : WA@}F-c q܎l>VO?^o" [0XCd?W#G}߿!j%yφ?+,i&F=:ab3!pf%64< +ENx\$~P[ru_7sFH (0߽^}pS*BL4&*kN>ә}cU%"f}0[_3pu / 2lV .#^eyN)9Y=eo//fT[ں#I6 V!H >v${xŰ8(7Ŀ+?0}z"V%z&L?IZNtZTq 8UQ Qj`}A8B$!rBdx/KwҏǨ8 /'v t˲z`3W"ֈz|m?M>I'| $@`,QvʾnObHI~#fg|>P($9P⥤D :*Fkߜg&XsJ)e2}W@]^'2{9^݊`[$ǫk\ee{#Jܤ(a- .? L>E.;E3*jQVWy;~?5@;j"KD;1I$5 p,ͩSjEjƏ}X|=r$~HOf|(p! ,!)_}Ib< '^!:$ :JpT I 3e0V@I7./å4*}]P3&U`'@ :#4Le}|o)Sc:`A_ G+r8 /EUA?>X%8L/¿vV4}\?mCV4oF"{ 9hLKuo;kW 4Gt/O! I0'Vom}|˳~:_w(px 2<`J/OS:A]<iޙX =!S;ד|qvWq :_e_Pe/Cؑ,Wﳋ{,pPc"/,y1-د$>C6CەhS@r> dLGƈ4ւ}_5\^xIvB %@/,Y̙9%8ߏ1;X]P`_$֞u3J"6/EVԼ)x$@ǡZ?E xEq },ccrؗ @Xe5+.SۂE{RL\G6Lf' 3){㡒[aˮ͜[hsvE8ۆ}M7<~trʢoÚeCEjn͌ S:LB$SD/d ,dm)6m_+_<_$i~7>>%?(I6@!sg w5~/zB"4dC,ձ:pbĺT%aj Jq:):#spFEd׋7x]eںS3=i񼸐!y]/\BUn9vBuZ9waKF]|-lL `í+ͨgqD GQRkۮʖb\%"xaB@fm_O6I_A}{+EӦ="@RB0dp')gY# u@|FA5K'MRCG1ڴ3 ? >Wcp[xiE?AIW 8 n)tzOHj1J#?[- .$A sӰ@:Bl;,%oǀuk:wgzӛ􏙲ܚ\\V i}_bTD܂55q6TGՠ2LV\Y rmjLIžOW@ӃۧbG]! Jl D1w6c"B@ 9*z}[Ii! 3 ӱg[|:Zf4 bD6Yd0 eZtEz P8^G~<~`n Dfw|?Ob"1cen:V)W`e x6Bv!XX*<&wX/EY`1>9Ó2%+Σleo+CUt9l1VآkQ^;+l(Ph:݄3MvF9\ǩ/B|VP͎Ծ%Zc^32 ﻲp{f}3Ј@E, nCfAB~Ȑ wzi(m"4# /֠=',_/c\ fH\#ғ|$/@1~^_"{A?Ρ2(.^_Xy 7@e`ݡLlTÝ; NF O@gzO_sT/|O ݏBǖvX)*v܁-֊Z$5a_3rZaMQmQ`wζ߆+Q0 F~ahG ~oV# |H4 XdVt !tkd$rҤE  qǂ?] 2 dBz] @@݄]Tg]$L"u4x궍/~J_##±@t8nGG2tYe*LPd{ 3=iiTEeolB(+Cz-eHrTŔ.Zr G`׊oPDq[W V]đG1- !΂ Rϟ et t `}pNXL ` M޾#up|3l4~;*"]|KAH[ l~Lz7z+H|Ht@O1krftӁ49xsy/fazj|ئ>#fb,$Ƙmpݍ8O@gzm54; 8E7P ܉(E^+Zϵޅ6t@[z3*V'QώjTBj|V-e:~z.7 !Q>?rJV.`'o\]b7G?-.{Z\®?]h-ЏWh4SL."Fnߢ߬ %0hSߟf: #R?;(E8;~> _ N LlڃuM^[kĹGQCq73UT"h4@+Pa>,nG܄mO lGjP{ ! yxѢw6Pd>:+ ɟ?- LoW_CSھ;?4fLE #>i_':` :>c7sCORg[f %w Q@Tr:prBhi }\')& qu-m|OSץώڌ?~~JeݩڟU S`"+@RS6e#83օ `W2BZ0/62[YM_,-6G]D ʷrYbԟ.C]~5Oף\ηMڡAn)/8rqPu~#m渾loHZHOTA2YB.c`ÇSx^^OGFcO? žvS:6VM5awgδ1e`̝1]4(x  '"^G B}#$p1u3q1[D:bB"@tiFt @OSohJOi t9 _wn [11#Xo 9Iv3xYeMEfaR&oZ;]YLGW.k-;.(*^xu'o~|ѪPކըlCWB|ފŹşqW{.?ʧ}  @s6FRO D 0`Qr# zX?đ2دOS?|H{g@ {'Q|jShpMi^SIsVtp^8YiCM9XtKI_K06 EtI. K]J3OoNL Zi 0pV Mƨ$KO”dLoV gYPIPN*f[RUSi*6a4ղ@Lߎi[[=噞ŔzҰIͨcCy \OŰE'k\hGF4:6 KQ8ͧ`ͱ8sx{"Aȥ%lAl=UsW19ORt_D{GQ)4k+R43_E {,܁x<,;pPD%Bf?q}s쮼!@!5k?'=nEU=s\1Az$>tP+t q_'.'fzFH^s<%.%h:!f,P?| tAǀk0T} Ǥx'=)K-`{DmXHK8E&+0=#s21/#~*76]GUr5Jy@NL= {rϧɵÞ {V ocxO7MOS0<'A}r+'Sȳ()p n$fRqa)O1-- zÛ |+ =E\ zJ#_z q 0L0,ܼo d&`fc|6݊;#> @j+HL%>)' %0#6Ӣ1%" 6m%OOO]c' 3دz^5 JѲ u6Wzf}rCv_B;j4L ǛX|w>wWq/(bPm7qqey#s]6߄&-$ c~j@N l@>}أЪ@uϫ3a ɀ(@~Z(0f r nPt|i{R"WjU~@ܧ~CWjsub&z܃ C?Eh# 3)};f'nwNOJ4w%!Him؄wm '%cYInF`^")j_('&mہaW++1Gb†5xB<Wm MjįGRImw=zE'}{Q95+p>ߌ{|NɆ:ZXqg6wJH1>_VdYY2fv4oD:64lFoA~  `  ;8 *@76_ž@՜] b MXhOBt#bB'H꿟Sd}\יh2 a #cFOq?y-lѢ^[X@?39X/SeD8hK&+@ӿwf2fOZ:$+1FbT !aAxC`<発# z+JtP35 {?e Wp&8y_b%c7\؈mnѵhXaG͌}T `K<e;o|B/{r52,;XAӧvdl vwo9I R }x!.=fxCJ ̓&nOX3oŢq]`]B&{<&|…Q-H/pb3`&#z!;`vp\e0WRa&f3 u#Uhw0,wQ8+7 Pz wP\dE06աj_5OJd%_ 3\ߜ&RrNO!hfС `O 5i?] ?Ν1F 1@P >ݮA'I3 ŪuK͕ * u_Z>в>B>z:~uezVE"ᗢ@z X' ksSfOJ'۩>~9JW7bC #YDM  qLKd1d2@v8@F*&[0j[Qgzd8ߵԒ+h-nCۥh)l-ɎGQw7;ڲvm(^9'g܆C+F]M\;h/h-jT Z[и &ܜueaWY+l5PcAɇVnzѰɎ5EhYqgs)l;`Q5(TaT,qvW>ϋwu YGb_ro ٱM$iL*pۿ >E W!`}'KڟY\5}]1@ @$XO{=iΣ@:"{fחJ9N9n3AI#>O}PĊ!޼Xf}3d~n5FGl‚h,N@ȞdS dw2hUo": e!jQ@If2q ̊ىuxrLXCWcE('()O>1!e%_GQMGźT&TeT|gZ3jq? 9,yЃ KQ[PUӎeH\-h׊E6bM+ [RûW[==zY^7Ϩ}o޹bSA.tdP<+y`6 `0ߓb_gX'.7#Vhif+-: hR+~\u>~]HPՀ]b]ZS߉_ \#nmX)X KbtE|ȱ`i Wy)JHظς4J!lU'߂O`zf&%a8O:gzd8~c{/PnlԠr }֝RƳF_K 6fd 9,C/\Z.rhk?Ռ\Eд\/3gWtu-D#]nٽ ;`ÝWbCV}qڌFCM;ڀr ) PSt?9f|Ic?s4 Ic>  y<P 29 u&G7#%fЅoVyw&Dup6K`7wY  p )A-sʩ w^/˃+1dcѩoZ\@Sczb,|0~$ R0GErX PD(Iz"$ 4'Iu3Ʈ U n-[ItLLJ6 }ނV(QtY]h' .{q } tgH9*=&9ψSS1C yF,8G.num2mW0rՌP+J\G7! e.v Q[CJc?Y뚃 h;Ҋo|_W}_;."uQ{e1e8>[f+<x}3+qsԟKh8X FTO%W܆"x\%@ P_|9,|:s_ db70sze ́^zw"Vj#t 85r@?fZvR RKvӈVzA^5{:НY_Oct,ݺg7|u1xGyJzYWW,Ǡk0=d!(;A*,]VǠ-_ٝ2fXOE) VCq`I1$0#Ba֭H`DSmn1laVAYYBY;.! eQ:*rPt-u_[o9Ó2q5ߎV崡}W;QAx͑ZF`id}} mPv)Pj󯣭-_CyT #ja2ą|0=k>$HErm VCnCS@@aa RP@ r?oKm ?IXXM69z-, N|X=^oQ|~gsg"K! p19#z"@;X- 2օ`͞DߗeX9ɘIB04631;;?K24O:{Ef2Epl]A|~hZ0'# ,LΦ~p' 7\DÙh7qa94iBYϐ?wcM 6Uϕ"ZQ&7ǗA}}?}_j!K)D}., g <b̜i1}|iPקSR29#01< QXE ed`lx&ST@Y} (Mh4p-Zζ* %l;9+FVTQ0=sPTۨ>XJW$T%ZB7@f&%BwiJQE-Z(K],H)Z^vPwPEvYz59p q>Q=1B_Hc?MtHN8YsFj>I|V@:20`0g_سm0Mu|bz jP"L~G!ue,`*Y*#`D<8'h j`C=O 1= ?¡el ޸DMhX}p S`;$`A@+"! 7uutb @!J߄Q!9~;4)ߕYيD'{gr:^GchseGڊZev5.}.ym#WUxy&B%lobo *ra-Cݱr |іׂ{{jQ@V[kPu-Za;d yU.83PXh:gj-n-ц +n?2c&9PWo_T(@V1 >]H|3 z#>|GW$ej f~[W* Q1/e`N^Z" Ǜ[086 ;-ެ+  XEҰڿ\~eE6dZ6owfxmق1XNNxnyѺ#.Y#eh9{~3"`Q;nʻa[]&=ƧByv1n\ByF1l7P.kv[q/ vGKB+AJi;]oSiW`;7pf4'HR' @'_HW2.u< .zBfzj$zZtK\E@jB(t 2Y@ͩ& 4 4w\C_2wW.흁9-DBs ˔)?Lq-،9Q 5R阑-LּpgKN-Eu54^B6OC~34֜2K¾ΎKsOBqg_`IQTih m' ihVh[gFUfNՠ]F}A9wӎUz <_;QG@4.2!V Kr8hEaʔv i ht zE̦} MPgT㧘)5+59>|:\ t .)rfZ&k'|z9__~'j+@ Gٯ/ݦxgm[a Xl:f76n/"b08!lNA,ߝ5{S1`A"7~'c;Jtʹ/ͿZ:*El`/GК{d#8%Ѱh G`X  3$P&v)CwgYۗ Ds ''I@: w4)[ctd8"c 8x'DuƦmxk{,Ƨ` 4d#p9(_' y$a? @`J4%b^r<90; ^ilz Eje}m krkQwԎoW'AsYPư¥gqb^XE6XoNU*F24VY]P@~77]f4*׸_H@}4|a7@q;Zv7-+|Pu8MkחƭCE!VGKU? @q%hHW Г v`ĈBL4)Jc` ƍnHTNYBt- @S.H@rmO@3 %5V-#CI~(d tB\ <.N|z U_-̅{g$"K l13xq3!2ˠ@!+0` U`pLwNO[laaXӒ,ς5ҙ@/NFHVaߒj|ElQ ;aqr4ъCI)4Ljct4l}( %Ͽ<;}%WMZP_.3 wr `U]49P[z\F|{k|[-> ڏ|FT@ܻ~6|k[[%9x[Shhƭy7\؂ѷpf4,9 84vQ pқF)C) (@|'W@^xc֬<끿?DRa`ܞ2+{() @3;' e<0hJz5לnvv@ˀ b's)z5>y0v䛆3XOjCM>-p|}3Fbg ۄW6joǠ '"X:"g)U?:ki ȱ`]zCHEGn쭝`aN)0/%3-ɊH@gn*I,a~ 8|u,K7h8֩+Eh Rpr'R jUmTT9*V%7'AۡVXa/T  ͗[`+E"woD%Z6lG+Uh܆+<'Owר;t-_l=_ @BpG I4Ztb@RXi 3fBi tVOX^"`?j'|َ c .25~HUn"Vǁ)5q=%S3. >ߺ+@V$S P )jLd)l"@öl[sR 3Wv` ?{$ՙk2㻇Zx!Fx'lwEy}Uw>b 'ZBV!0%nػ1wvbc"vޙwds,y"HwD xc\u#f۝Xl–vr{(E/yQ 'y(9Q׃,~*ö*ĹaeS#Vy\XnwanYcDXGԯ < \z +˸{'#iguf>5~U _9}_uJDK| | 8q \]ߞ~vۈz6zǙ'qe?r_;O[>CqWE_yO>šWOQw._ęXpw]™g`4b#0Ɨ?^`g`Tp@Ss]2~0̯ыi1L!$Xd0-p;.DT=E[ -gG1:3#za [-QyȣCyc`y-g:!Q9H->RH? Ll*ǢlFGxLRZ`@1&Lit`vc#VljN7JP*{HkiB݆<&2j 5T!Qo@݊^5 | |p32 ى>Bׁ8c\x+\ۍkWcUGНtX t}ǫ>O^Y}$ .t빅~Q67۸M\+=V?_ *73m}ëqrG1+g@5|nk=c»9oೃ\POT7ݥؙFo57߸K.$w?E\E7Np'] tǻ_^k>GQu_H@(Fv  g8mZ`kF,X@(S*hmRJ[Uq[`s9 G]PQPU@u%?<A}P@g&miPt$.EdKUjcgbՉ"KNlhmB ;p Մ쨮Ca`X]TSMHlw#R[w7۝ɨ~1+'|b>EU |\k_>oW}Tc?s ?k|s~u7޸W= o/n~_$N:nM\5\H|nz8j t5|WAW9}%Ϊŏ?Ӹ X:&R=3oE{9WBƎqzjl<^=sb!sp3YᵕIh=&vo~wpv ]9ЕswN!{ \R9z/k~nQл_< 8 7>>3F/!ku:ԅWS>xLԇ8qgg=}6w\ow;41ON;P7Aj~8Xtߞ{4ujC2υ?˗z|U/u3i?^ 9ZzC`@*a|-`+ M>FAe!^OP}@_B (OyzA$ _)>(Ūz 6GjӀ(10xBip Åe"}ne8ى GRV$lah<ue$4ۑˍ}-XDV:zҾ4]&q\x4ng8]\ʼ3YqQ$a/|y._EϢ|ߞxn} 72?Qе .ƍ7qK _16|t:'^ŜrG)ݘw6қW v?S޿**wD81y (0M^L ݫm}E? gM@δAS{` ;"C ҇_pn(+^y t uJi ϛ"@?#6Tq pSh[Ȇrl#׈b22r 1 l+ۍ->׃"<(93j,Cxg8mH,Cؙe AZmo83FE𻋷"?Gw|uX m#{ܸt O_q=8%uߜc7N<'œ?8ы|%_`l n|U\*'8I{ ;tNΠ7P@Fg؞r~z{*4GTޏ1 92Т cߚ5ض"։.ǯ?MrkXCEbf0fQŇz*B6G j,0\~W12`$P@_,_:@}gx*رo ,`@-o"Î1IF "P^&,OBvGߋ>BR=:!ވ-ey'&$afF >l> omei y??={n{? aB|w Gs?™NↀB#^ŕ.ǻuᷧN⷟_wokrkۅ_y^-\Q;g󛫸Mygw~*>BkK\n\}?;v?;E\?g{g.?ʧ7GV+^X .5K~?s'0PFZz<0Xw覶>-@=qZ,D\ 9+)ne ū>|eJ uzs*VDc@6@n_F-ӱUHOi!Yh?(5 J0_raωV7:]AUbՇ}h<ЂN5!aö `Jj&6Td@' cN9Qxs*kF.?ӱ¹p1Q31N_G<ǯqN_}҃[?[oĵ]qsΨ=~\?f9\]_֟˴q8q8_#}Dg'ϊ~GQ)K [BW)V?;m,s<D0v&b`=ٞ8L=gA`j` cXWX9v%L}.?'GP`"G M0>_~L @o#I06:II9x[Hm_?g"qZL-Ĥr̬"˘ַŃmnd2׿_@@fTlFX^J]ؐae^oH,DVU N/R K_,Npi{?_x_GW⯌@ /pi|8Nz.Q p7>lf*x?o>oBwk7Nu?s N]NEp"N?S罳9tW]͊)ܪ/?0GY?4"d@W{T\hX˷}2_uP3.Y9H#.af\_K/=t?,G(|_(l! -?:p|sogSBn 0hD2wٱYə٘W kl2k=&|8FC T T²ۍ;MFLdAD1"eo3yoߐl~pq&n|r)PsׁP})S VAx~`zA F 09gƮo"\6~k5C>U:`_oH)PA&}Üadި߂ } (qy$x`[f.`Rr"2vz_o ?H3*b0[FbBf6bvM ؛ZMD{}rq%D!V$55 ш\ q)b97) _cOpqQ"3~r/q^utzO;Ӌ \5;+埞ƕw7Fϫb+|nʯ'A_=vXO5<|??ٙgqM !PO/Q/QPs t6r@SO_竊T s@ն`ci]*jǔÂJ7 j Pm{ h"n6~E:O0|c;K,ŶV*z{Gon4FoqT->LKe_[Nִxŋv)g[^Nd;4)1-i|^r@,&ŧ`Zz6Vbu] >?t~ھƉ=s:kwx7>Ɨ+A/ |=k@9/\c7NؕΠ5?@ҋnozN7u=g~^2: !~qӀ`ߥy]KY?!6@*6cc@{ѳxLf*`iM 9jD'lŔ,LlK96#z>lw#C݅N10kn6yd→ MH6sS0n[@*e`vAGU|rfza TS 0>C_e,ԧ7vA}Na!? ˂L"m?v5{Imzb7!`n\c9DGYWl9Cz* `s ^G`HtFh3@'ti?}O4@{3͠ğQ`T`ɂ1 w-$#5}AkfaviVYo ϧcZA!"ڈ6hEBǘߟ!⟹Dž  f\$QSϟ)t@a1VT"+v |lXoş-@H?T!Z8+GBf820@D=T%h(TޏPKg} mAPP[)ԬW z2 w4?Wˇx@_% +řyz?@=@X/SX8gzmE9u;2*_$a\V:b]c~`21󫪑ۃuM/ϜHDZmMڍ-v)M6#nԝ񘔠@e%z@x,el䱝Sy9f ^9o1][stm5rS5@bAv⯆PݗUg2pSN/YrWٶC~S\c?}#] !;<̑CSPsuz_[Xd?|Qذ!  @mo >j}^lN' ):z"؍O,_Yþ~ -n/&cm\}3rEQ rv lxr 5#s_3j1cC8Hmm!i{G@NJ1~{<&'azf.Ta&p!`ьUuaO _2eIc)A0(dz P<{jYl4{4xo~: Pڟ7+^{L?<}7?3ga͚>,_ J~}X(ǎX?幺G{ !!a1) Г' kz=.x}0cʀ3@)ld$@ L=_lŗbq:]}g{sb36T`ju"6lA^ѿ %U*Zx"%kف-p % m^$巈K]vd7ۑ)|z?-р4y)`01*H @Q UUa1>D |<?_ْ1!@قQǾˈJi|n8m^ng. v {.oz'$blx%XPח,./8/!arn`fbr^TEI iMHiYT4jPf$s^ D^Hs c?ׄkQ8%^#)31 /W`y~:rWD< ?zs? (c`C7eb j07;6ag狨g_{؇EU%}[-RDȳ" Z'gcqeQ(߄]N<EX^WYuXr"nv7:H46[(Ǽ* =d j6D6{+X~؁fune|࣎i]jpşϰ~ =}2TF4-c}=eJSwĂEAFN,_}\͟@ByqP3OOW BŗBm d ؠ  0”>yVKF{ (]UN]_c6;)`hL0I[)(0rlMxE>_ߢԻ0(wcc4Xlf>ٍ^K1-S 0 Q s LHN7O,*Ŝz ۚ]m vyh9vd`5s}v$k(=)| kZvcfQc>?q_?<;gsR=@E(}Qϕ=  OOc!X+k?&N3ρC4UOR#V`z?搾< 1 40 TﭾI뭎#֟t{k1Nc K_ϟ?@Ą;3.뛛0)7lʋvDY0X< X`fm#@p"S~K;@@A*K0)6e~p4aσEu+|<mmQG.@y,"Asj [ y=* l۴"#҇`$CcܢEqc1mQ <P5AۥD@>DX/Ӎ)`P9~%\D&\Y>͈l31zM`V (@){K޼G r0B P<¨@v ow)S1K=Mm2al٣WRPjcvi$bݎ S|@a.@nuaqi1f`NU5^_'`g(Lc@?YحH7V`}I!f&&cbL2far,cWrGnt$Ny@[5]O W||+`={yS@O`6 ,>j1cAy[ŋ;oPn];KV[8oyA 9'A@|Fueϋ\6.1SՆAog3K` 5`QO!"|/Xj'@f03:  Pj^붑❯<W`;c}:l1{tKH E))+P.zz`F ԰!,+b4rz*Jy>g`0⯢ 3 6`$ 1=k-waLn;X)o6C,2/~թѮxj1-'K*003bA6X ,*jC~5x/7aa?cwQ}H5V:-?-bmuE<dymX-iƂ0}J#R!j! 1lyNGϏ}C/|u/!}GcLq|*PU[P"h)PK}"fyF_z`@Ӱ,J1(_i@bk# I cM]%V#z7;]D?ÿ/ui8xi'v"Z+@Ѐ m/% )ecV~X*J*mw> hlOKL}@ؗǏ3iȪ+m]„k6 0 =*iNg\䣄j0郁tзe@_`{QtF\H#HP`GZykT9|zz=qU#`65.*M# 𞃉L{RUMF*\@}]>v8T_O5#E T Ҋc[*=k˱{= {ۜHpY1') JKaÜJ[`R~MBe%]iTy"VE9ro " u5X%Fn=>1~c!`ύ 'oojZlkT, = ʦt U_~gd$NxHˮ@l)FEV z_0PiB^D=.0O~6OCp sHnOB@(oߞ{e;8mZ{ "A@?G:w>5؇ПP3u rzBj+dECPPy9hZe"[ ᛽uy0`_UPC8x(Ԑ'mQՍzPÁc`a?A :9#}!1h{}R]5* ږY5j5qP^0]LH.,B@cX@x]43Wb2a `[ mH޸^-~GBAY/v1_UΟb ~j?Gh|su`h}{r40'  08]ۇ38fƌ#PhC0C*4 ljsmaO(&@ 2{mڀPl (j`:_u ;]r@"`-[f!** %00@Ŀ+|#,cт;0=k T^ޣ20? Ra > ׿"}Z}V oC.ᒞ`8mnZz"f?%(ЏO07 kK-201ޱJ+9ŘnĆ&}ԖcsQN;6>+f=H#[N*zlzk TaIc"Xu`GI{F #Y'No?C 'Og~a_Ž?PA.eUn:KFX<g\V:MzzCcH]_W{ 6s`OmDCVGc^D8E ש"t%]8Cɂ (@0" G_J7O 'zgHar}MLlF `4$w 0!]wB%g [;)ri<_!@]<߼E5˧ %+ "-"5} ;ωR)վ@ 3 IJ{e5X"͎t/ksaCI!UbˆŕEWו__=="#mFG=V/c*NĴ{˿(ˀ 作H4!ha"q6&Yv`Žt*n,9>8,؄~0 YBF`BA֭^:"_ |SY(?={ s[4sJ λ`4Ut(Y <eBM_kWVu JQ]3zBlX 1G *į<} @ gyR U` ':~{q;LVG ff2DG TWM`*@ ECP(ށjb5Xa[`E5bwSQ  :T ˛7 ** "P.` ş-'ilDL\Zg30!"߄}"o/ČDYkjeU ZwW}lo& "Xaʼn61(gO3J: <(h,W`#ZF`0rfdc.WW`yK><4䄟m~y89pS*ZC!8sk9,z !80W@P??2X @Q*]`^K`$a 𫰿('HΖB|vnU P#MTZ#0 a4IIo9+k-Qb:Wax{\$xlQUQX^R(H— DԔ`Y6T:RfbE9c@((EA\lGӆxNd$ Q+b+|t"^Lθ5 xq3|G9%4W8SWE~k 871B`F6{0|g%PFl#|,79N@Dk Ptcz%ZB.yPF Krs0).΂EF Q]vTOi|1^։[/@L˰vR=(a`ϟhqۍ?DWk0fDL!c^V̢ƈG}+iiiFg(៺ꇘ懘1]O+Fg<s7`{XjzQ 峟lPaBs:5Wu;j oKv(Ҭ׋t0u2ohsAd`!Ezl|J``7qp(hPy30*"_ѰQ*I_"b!>YpbgoFcϊR#kVו!U=T)zT0}E7m??A}>fwxQۋb?H@0fN$cYьcm5MXa,sgHdUNÂ? #!r؟Oaޏfa k/cC-L߃S#,"u"VQ>2yP߃n$¾PD7[/Pa?Tkvpxڱi?5#9] p02R HO6"EMT^c0=) 321;cW֎yaC?UC)PBo"?A#W }U"AyL (ЫUa8HU0/a}v@s Q@BjO0T*S}!BEB݇ߕb;V/߫G~-%6FQx'8osjJr@RK#0_TH4F⥔T`V{rwYx %Ċx5D\'@т3X`㷉ېkm"lbFB f?/'󋊱Kcš0vMYZly~]&+@E x>t_3A 0OV=kg螏f`ELpa!"G1 F zżQu ˥>\Ч,"^΀`^w^>DT  O_t)/ִ9p0@HN 8plF*vk/`ˉ vUzl)["Ho`h,`Φ{bGrKϿ`+,r%,ZP%{}ׂ\"yb6cQ@F+ r0/%󳲱 /`qe%˒F7o6- obOA7)||.#x`vZ|嫔>305V+3a Af3߷m1`tT#r)% * ;kS4z!"h0Q`0}/^yUŀf[͂*z!@>a= sY9y9FN(r@OÆjĸjD҈]M(G2[HHY.<Q_kG(K` ܨO!͎"~|GXPv;5"zW5 | MƦ$߸ X OsG`|?} 3__ G vdjǔ ş2L2D`}Syx8b ZG#RЃ0oԧEB:8Mn)~50]eeX\Y ^]/݁VGzk#vQu UXDv2;};؆"~5" I F;7b\HԾ&0I9M_T}hu#^E}8'a"%T c5/>4 *_o *׮"^* ;Զ?XG@*T1* 38`У" CJo:އu^ \ NqZ2X b.vGϞ/Ҁ|aDXGlLwd@zh"CA~zQw~`]i@)Trza%"²&0'q̜? :'X>C."mҒxfAp11ET4Q`\s,gD3F%SP)*@Gu> 1.e/~ uP+bx 5_#퀺@PQc=CF'N+E[+&b}6`K[ԖFq[D^k1Blj@ IkAV3ZZQ' `@(vhA>j=* # EVCdqV ‚ՕXXv+" pbviOK(}>YC9?Cg9~=v탶zGS\*5@D6*@DX0Q)"JSYֵl\GV}PQP~! b؊`;u{gNSBLSU l븸M坛*jF@@n `jK X^:+˃ 4 QfT#5*bV, "a:vذZ`- ?i-sC):P?OO^ymXCJy _#0f鈾Q F iȣ@`N @G0`k](!UKvRys}7< z)\x3_MntqoZ <} |mjQu 0 6I) ]ٝ;!ֿ*Vg҂n$ tޛ0_{uzc@#[H @@@:}(E^QZFwE+7 ,,++ h&+VX̒ΰ!@خȪv~VpxZ=z (?h7KL_9A|ĝqF Q@D M< E?PjtR( 5G6@s*`Q&\ÈMN!wPu/Az[Kliu_Vo8m01Ϩ5`1F$Sv{8PX_iC@ (TL)M xsQxi3m+81}H:Ei<2ecǖlK^xˊd-ZSeɦd{^]Ҳ3E8E_^^0 kJ;:)WRSn̷hÝ`TPvi¯eTO0?"Q-y8/za/ x foz?ХDe*u6F7 ݹܖOB4K˦S׿yzǩ*`DJ] AoM BQ,sDd`H\6*o4( D=3V:pH"iH@`1dPhMd.^\RW:+TVFFQ<+kGxЖY,Yp=P \"q* ='1 2. J*@³x#N >R%[B?NX @Ta3}ι͙a'q*OR7S++ﻤw_= ័E1 !@cOgMU6E@}!hh- T By=7lj P:F6vP@ut͐0/D{X@xx ۜ~ yPP';(\/Et-i9K/ X ,@aLΥ܊K.ل\}*}`55pz_&HO|[XJ(وЯwﶩˇ?7t,g !]-yH4=[X9oW 0J7BFܲ]x/ 2#q5O춡/ߞU]wG$@5jnjk؟'h#c- z3m@j#e@{% ~v~?1vPϼ7-C(ٴ g^|t zQ'@NdMQ8M0 ?دA~' {ǩOq9Op}':VӒ uu ߋ '}}{ NKK_GI{zn2\?%Àj|*(}H-wF1a'9nd-&t!S̆s0Q덣_FD 95EgzN s>jV=6Dctqbt}<=! (@}F: Q|t > c>CV8AA8ϧ#NJP0s`N @4 xPVf8w;v? &YYKUx,T9u#= ޱUCP!x#(٫ " j\`t6@|3^'' DEw5ފT >NvSEeCt Niم;?O*An9S +" 0Mcg`daJQx'@H? )' ,^ 9iiMHIyfF&KڵYS?xwN~MB,m\@g@D |K IO/Y)oh $ ˳ k_qw#Ϭ,`hq+|) x»>㦮FGMnB}20UC"#Ľy~Z=4 -LӰQ@?Р0yxA O|W- QqPKCs-TR.dQAyč]-RI@dw0C,Qn. n.b~#poz"or=j5omZ] 6@" ߇~ Px&P"[ዩ9 ) 1͖;M:8Z P(nйI=T҉gR8בi|@Kp- *1[o<]'lL@; `ҦmR賳G/&+\%%2g>ȬDH_-.\PSvҢ r~(/]y%*dP[\{\{"CT"x_u1w$gB}#j*_£OB»tjɤcQ W^Qf*we]q3{^ z /b{T\l˗7yy/:W[TxsTd͛@ߺ^$!J!?Uyl=gn3y^F׫i5" G*F @ξuiguЕ)u)[x :;u %@X\tQkp{:H3Aj)K@7@@<O<`>HOr;xp@묗Zk4б~jZRS,О-3N9׶sN>FGMV薹ڼZ$ /< >mhh0mٌF.>#b^=zD=/DCV~߱cЉ+Px ,zl9WHUd$K5PZT72wDleHD;w!΍3ܛ3/Q*LiP R #KX=?u{K=u6Xt4vI,B>RZRS?߱ʺH?U? z'Dy=[C!j~ોTP j Pk<3@ouOPg7O= ZF9n_K*[Y<Μ*(1h:vi>j4 aPS`X@cq u="@oRhY*n|:o a/i& ;sk)~ɂk2ߠ7%D&x]`T7}_W׮ӄ-~˜ڵH`к?t?O?, 8KcЦ{Ԙt>pi::LQHQQaZmm5ѿYޫ_ CaGt%` i"\\W57/[K>yaq~DA#B<ؐ otm~g#Ux#6 %w]Rkj q-1pi38?LRimw4IENDB`tiled-qt-0.9.1/src/plugins/replicaisland/collision_map.png000066400000000000000000000024261217502731700236750ustar00rootroot00000000000000PNG  IHDR99tEXtSoftwareAdobe ImageReadyqe< PLTE~OtRNS AIDATxM -}aއ#N9 n`"݌O3M@ ""=N 3Ƥ8&9zxmSZ 'X131R^Ia&o7~oHdL༗D3:eW'7"&8, @X3q4 ?%u g&pu7&pߦO7 p3/!n&&%B % ,@hA8xWړ/#`y++Lw$p?`# L l8/o C@'21 `.A(soNVP'21 lue4yMN{ .`]!EPwB&R.b6r>~ FVK:YڋZYzY 9K99E Y[<-6dZlh+`7`_N&CSmg [nz+̤m͎4lw5ދHIENDB`tiled-qt-0.9.1/src/plugins/replicaisland/grass.png000066400000000000000000002267751217502731700222030ustar00rootroot00000000000000PNG  IHDRŐg-IDATx Uչ?x CH!P㽷QQP͋>"P2CAPLTU , RCPc RF3*{__կ{~~gϝorګ{gO@@y2060%azy^笶@3;ft,]w=VcZ9- dqCbCX]^"^4Lyl?w6}EsU_uV?8D|#7du/G{k __vR}:*wxwss ר6?,.cwy}:Ow`Q|)AyFxoKH|'e d[x~ Ʀgΐ\C|ښ1;$8bWV=ȋo/pܶl9ϧykLg܎s¾eU^B{\YjK 3}N1:,әUKnj{Z0^5DغJLFio cxoo KG=sϳ6zaegF9@q1Cby_yG$b# $Ԫ|IXwmC?=r޷7{O+.ϯX$*'Uu8w[f,p{ڠ꣌Q?={[]߽y3ώƺ@|ߪ'05z9ϯo`)al??ޞ@U'M-v% |?Eۋ;tr~8bINs:+L; k[Bs1XJ'lbqВs"}d.>B@@:M GT*.4zӀ4 } d Kp~{(nWN(dAk&+Az4}7b oo,>sݿCCMar9~{bcbpn; Erz8o붸"CU׾Zko;rv040%Dt$ԏ*jHd &U]maz@?(Ϥ +RVQf35A_V=)LU wTb =@NbT Yݳ3\onº~NG g/-ph맅pf+bb9="̷"Ph_HtZIaU>},0 Ip DăKhNpS%(>7\&lF\D  /T$7| ,$T-1Lv|Sڠ#r5 3ĠnVu6X^WJUu;w@8R@"{n&{TX\?[gD@g2~{7;݋(r |o xgJ{ϥiKSM6kN@s`F@'g̷뺮uzsu\Q'w^ ꃅXùD+kL9O Uhv]xxl;C@Q)rh}?ﴲ:MA-3  0d{G`H|=5 ~*pb7r$W@HBvAU%Bxw6qG@]F睮02@8'O !kˤbXi5Do46b /T6m_ 'GGL dMߌM gc_]S$5'gH l S-2ng ̶~6_jUy>98ܣEE:D@T̿1g7 Uы 1᬴[C\bv|\6@K̖ `E@~C*7(hH~ڰ$ru96 gBuaQD^ZUkAֆs֯3 "G~op?I|lp"]6ܛs[$HUP2 Ӟ'3~=>~0lGAZpRwH z33n"|w7ځnįGr71S%h`3@#U32  @d³l|Yd|ˢ̷Dm|̉~6Odߪ~GeEsiXnK:qpr;Mwio"RJX]1 0jb ^`} c<$m j'MY]ƵMF=tc|& p?OE߯ &!d*1NutM_?|0az~?ޞI,dGojdh{;L* ~07~fɁ'DIĊe7Ȇ?'^ḌO)b:Y`v x{tP@ e\q¬'ʣψfs8/ð9(eJ<${x>ƌ')Uωfxa"'c(qpgGKG0n(NUp H U?!dLlP'T3HHv'?WVҧMMZu4tW$n2|p03L6oXtU`;vetf2$98WX Oo3,dDӋiĨy1Z0d㦱_1ET%qGxau?{޾o;;.p){&VSkQgGz {>cŻ  ׍Mk5?ܖY%#`[T}ۿ!Gd0D,cL?=oa > 愘&@nx[V_/|LҪ~q([q1B*|}D=VWmO(ECץLmkĦE3?g늤4F1ogOj3~GybB-4v{dɻD{cş^ω+1"hπyu?iKH:jRub@Kek2֙Z}} ,j$Z B@3U BU=:~:u94>!=qMŏ}J,ʹ~?7K Hxf2q牌T`X3KZՠ9JՌ&sZJd.K2WN.rBo LnBwi1>8MSQl y7Y7wX;OYV(sYt$$-I-sR#+L ߢ!ԂPs[ D~pU9ޖyo|T?JO鰖oYbTyfX20~n:?E2%JA05T+ceC_X/'gD~s駁J_+BHZ3 nc?.l^~kp-l8AS&*/i=;8=&cbM٭U?319ϠD ߏ ӎآ^{8c}xZ1U=cm`6$]rot>L9Ω)W9F|)/;ܪGG_U?@63? ˴I PvJď@%Op!IP!Z |ڣU|~ӓo'*&/_uCOzR/xoket/ɪJIaR9οEx-ur.-ʔPSD2P|W"tH{l 0/7 tpM6*]<+80$#dzS?]7~HWIio~ĕK>j!>~rS՟#/d#sZOdd`~#u% y@ׁ 0٫}EYX=詜r :Ն3:FZ-+'U]wwUmϝmJUw"mxZ0T矴I~))/gE¶vzM]̟]p do/_⥪N=rUZnD5$Oo:&?@'٬XK|9PǯJP|^uX~$JJ_MCwtz%uX(# \ UⅵH{2Gw^8P_Q\2z AG_s_.YN>?9Ɔl>֟AtaJq>'kWk"3_sRgtczmFp\ |"2Q*(ƀwDP\/ml&SoOr\R*遼{U&)\Ih#@j]xL7-(%Wxw+å*0>jb߬-0 <ߣ_"1:yKU1y#U}?˫V$But Eh, j&B3HHnp^IyOj:tg;;4aGhBjo6-sHL_ mBucTo ȼk:$}Á'^t I l\_NM `,rOTz\eHLiE1$>;2$wFϿ |WhԉNTL0-rXQg19_C!Au^)\h{uM'WiM6WFL7m;;`Y,'Ey9}KtM4dUxY` jbQq<ů.ߗ,$AO*7D1@RP[N[C<yw@ypLJ> p)v~D kJfϋ}z~z?4nh_N_/>՘T?9zl cf{eHM*}<( 5Y%ڙc>3^W>LւCL?nLK=WO[ sZp6&7z7-k|q=ʓKd槵kjkt)U-sS7qjXjI@пI@Cs4\_| HXR"*ǩC|U{ak;Q!q:?t?F!&{$$']*ca+\ptyIrB Kd1LP>=ȸd֟'KR?~bn/! sÃu[ %JO0v9Te~UA~~\4)R3{ehoE3qqaTR5 [dU;?MV $!|E7:?$}"ZH{|!ts_Uc)+_B\@=y>:V6P1IM ]h i-qPI@i9b\˫ !P\@E~jMg>]5jmCt)tO fj+@@* `bCC@ &4fr -P(({H?~iɡBPi&i|+iGHXr6~  _Z0xL[w F4Ag>0*K[3VMʗ@P`?8- !9h\u6+tu!x=4U Հplt”BM B&Q0wʁ_\YdHeFr %NY$knw 6j!fiwsZF$nv ܮ)_o='LCkGJ颹0|DeY.4E{9- Қ3U Gv_h t/(_4̃tE9/COZP@ R~^L?&X>lWk~}m |ɮ?ϣ+>`̷K[ pƽ^IuFXԚ +wcT-HEn0ũy%?$rMxż3c[P#@yiFt_VJ; %z}V6>L0wûXhiK5kJiT oW @{R Sw L}?%&;AR 9i?󶇺XL sa R?ld2n辰U7a?֎|*ӵ\=V.~5B `7_QNVVv:?QɎ'!Ze& ,.%^P@:HE4?  @*J7!/QvB 3ґ{^e[f'~{eߡ;3]ƺF:> rQmN-Zysuw d?4dSK xmլW>?nTpE{JC`ɷ@_d{<`5k#<- g{Z- @ӳw_O~޽{ѣGe۵k qFHе;w:,_\<쳲-\0bI;[ᅻTf5RL)u4Вb퓅[cyzrg 6äUd]wun8qC2JŅF1@UmmBu4:$~`NXVEA$vgz7d% f<2A:q]-iR?S qȥ̒C1~1k.6O}} D ^r;1tqgKt?Zw qfe1g98x{ǎb˖-)~ȑ#.F(}/sqI&tMHMuL@s:F*bH?\9 _$fRp*0Kӂ(I,`\\ѿ7ٷ&dǦ(skzM*!MEbTyP=vQLpuwAy ΀UN}م!F j/ӳjwuS~Wľ;vҘ,-KcS}[龖TGR8(" 4M~;^<'/`զL3wWIiᦇm߂;%3k~R=h$;\ 3Ado6en@ 4~0H&$ްad* $Pǽ:-A/JRʝ|5P:J'+ dúQ q&<]w$"~"=#ZVKYqKHD-Xo6m H/Ԋ{T&UR}YxM$BdX;DM@E@"?beUS@,Űn< M.#n@'FO-K~v|gz LHϕ3)z^$m-XO?BTdFBvl?^JAR?gpRx&p(G'*CZ$rM(<:c]U!i'ұ@5U+,YD &8`̙͙_zL\ `JMNo97 ߢfьfO Uhv]x8+'oJ92+CxDLtW't8E]c4\O  K. dCk Pď\Dn{q򇤯:-X@  ~>pfbh}P=XG)=:t.L $ʸ],@0|qjUiCĹYyZ PPm+;gRp>ّq*VykC̞Hވ~P ܼ_\2Sj Pkht<AKIJX8<hUm4l]QF?WÃ9Qڲ+.s'HpFg 7*~۶2wź~禕aCՓ?BTU?]f,܋OQ̀?/9}Q?i97c>73\ٿNa~d ^}5|ha&"0#  'Q=W zV0j{p"$oW6lbz@P&]!p3?;:ԥN59Wp/`}5k~6mkJ)0鮿m6[To,?ZC׉ww Gv$!NĢ*"0N 9 _x:*뛈iCb.]*nau>֭'~B0~|jm5䛃T})͉oKq}pIO S5 (K.?#GgZG P\{P5ś%+!$S sEM=]CD_][+=T;KK*o׆*Hu?g2rƩTv *#+~z|TM sru7~c}aVZGDxء[?&tgf ȸ=@ N7ޗ苺h߯qS0@0u,#zj<>,0H`F-R"oxc]mKr>3g?󶾇& Diք<~g~gNpx DSta*8ujq<2= A흴g eI{G إPEX~3ʮ럭x?97o QiC`"H=gD(gPիL@O0?͛@OdWK/ZLR`+; \03!gHsCd@̚8ڞ 8)tx=v%%KjJ? > r{ޅw߳lW!$q. 1 ^&s1".5K9 >ڝ߿q3r҃l] 2 #)oi0S*vz ~]Yԅ}?7MC};#?!G/,ep?|XNDT__J6}U@Ҽ5q-$~5u0}ʕL2%co,8Jh'>]22\/Q?i9,Y^P ʌC tMRvG#I A U +3%H|J $=97$[0xP-:DP'W:NvFAgZ33~%[?GpDF:?oZU\tMAm68dOe#i@mcܩ5|Cptyt2b/^zik_@|޿Lp~{@@Bi2C=I ¾aFtn$izOlJjelp K`9hN6V||`X\ɡę6 Tr/$q5ΩKK':EjyS 8wxk?{>GRhlދD~\s8$sߧOU~l/~`\O"tŮh~ښn1 r|Δ_o OС·WyCh`ۧ @6@ la"S..tF)46^z;Ҿ]PjJ$D|3 + 61)z`uPܭ7I`*tASRM9]0~PҥxL8_DJ`hʭ Fz ht PoA*Y%m[gK*+hAѠ}HĉVR 󟪁IO0熤tr 6O7X(H>]B 0y40}R  0c=92HMF&]LS$#sERId켲lI$}0}7aw92 ?tE )2@{?%`@ RB/sࠖt& +r%QY`Sj$|!FU,kk =*`tș`^,.P^.W V4yVqFH;x?A{1  P݀@Xm3Oȁ^S$k;wHZA> !tu}UkFK{mwt2O|nn`ccc)923ɡ`JTVVJ`ٲelfGm<j ALS2 JR>^К8XZ^8] \q"ۓɗh[ɨ)dDuR@R?1 s ׹IQ!T54?U L:B^Y0eN*%GߛKK(w]b`k@ PQ0uɯk#s=R1hqM_2r( A 2{ dGq`_&6ڹ<ڏ9@'o.JIBK:5'Q"PSy"~NhFgPDVmV":˝BEiyLVezB kK;ꠣU'JUU-jHL|U/? Y5ZtE@[cR:H LZXר`3ӆL[x$ BC!|(04uჅ1g;=M;|a?։ŪEY)~uYLg4% joG#6uU7^ig$8ڜ`~:*yUDR('舟z@u6xWUj UW d#&ʝtφ>+yT|Ip HX_HdV\RYÙ?RE ԁ'V:_25HMy>yC2('___? ~I)|Hh's=(/Z2(S _W Zf@ONpJjJ- $*`38/?n ?BăH%#*'*_uysI]ƨ# /e+7Y\b&Z?<״hk s [ ?`ȃxU?^LHeP1@7|f QH]]Ό}H6Z[nR\J=U,:g~?yҁEt $t" G$}2<ŀ滫^ *ӧPm3yh(䏴< 9(Hyc?a/Ü0bR,{j׿K^m|&N>Y*nsI%Nߴ0T \st_|$+1+$}m 8j65>dS~~%)AOILa|nIkG}bsߴ#ciر֦=t@/j_(J62r0uu,  gV=?1FՂ?H@yAJhO?@#ŞU^bY6d濾󜖩oH:nbYӭ)1n D}N 4t&.'6P8;ȩJ\@.)l,K 1CLƁ%%o8$ė! *V.DEjFcA `aH8yo>9b6ٰ;!;" x@"v")xPPk(ãyX9!?lݐr52UzcwT?WgrsAQڦay~!T>*v $R?zV&_Ki_UV {u6A10- h?1l~Hju_tA@矴O|qOK;$pe0$dj4`lvi3νh |9ZɁEv,0.F}pMD HsNphIةTĥX?* ix'iWU@A$T"*w:Zi#t!8lF~8\ꡊcW%v0=φ,4 lqT )!x0Tjn>=4z\_]{Vn=:c(LA0]?󗪟 3Nk:iCѶO?QH0sb "-0@ ( :裏f]F-LOj? 5WmWl'y~0|Zh 1mݽ*!~Zg>0Nvl GcCR" nꑧe:yTl`/ lʪ"n yy#^uNN/=W`R3xrhNė&Ԑh/BykK~SŞrvJ/G>Ǿ_7~n+FH_=ő  c\?^3@%r" ݑ:bΧdKlWQ. nKJT(&/qG9(?HM aY/ MF!|5-/i[)ԬӚmz粡"4E~̹oDj(8ꍙakO@XN*oZry:F͔1C<3^y ڄor1BWV]4i /?i vKj}0;!<Hǹ<NptR?w${-5 8~Whho}] ufTxm.Ngznsj3GC+$@ 6M綅\>7G?y0@;S#0SL9sFcrY kZNKPZŇD*g&Ҙyp1WmFqӱ w\(j {;)@CK U}Sכ2q)T&%MEM/w\C{ ۍ޺i1|Ï5xsSmCy>6?"䔰 h-[ݑO^Z;ēEo5$A$ZSGHCR?qWJ_*f1UӮ߯7Ƙm?Au{{ JrȯR_3~: k~0|@7M^|R:gc}6%3io`j w!O4+dM(NOHQ /m,aq%0 ը%:~ /D5l0R^j\zOlYa"0@z?<6~_=$*/=:`v-Nu ?TN} Zdl0P\Z%r &yU/;* 2H>|ڠ"T-ZRQW_5@F+W۹q-h~_ѷrr9ˤ7{?>O/Mx*. }?6XcMOOz=\W:$VmϜݳng_/~bˆb[yb5b>u/Zu=W(Z{ff[s:/oIh+_ :_gdS?6֙XWܲG'Dt|: rGn/V>(c">@AAj ^=tLId` ῡv~_@ $ATH.v#ڮL׽ja5 \~eVڦW(:L }CClo#ڇsȣ⦇rjoC,:|v9$LT< b z(P}XC/5VLIp*uϙ4=NozLd)@:ʙEv|U\ (5kL]KKBZ?&CSzU{^čvW?u \_$Î:Q!iPͩϜ%ҿz}#j_sd`,Z;,eߙ6];?ig/X6b/ pI@*wC<0HkPqQ;%g>'yS>Ӧ7 IR?aT08s}p Hf](`-}uIpïKH8@vxd >7 J\:-R(.vJO+(t$wKZ3$Vyp0q:B~|`0 <1[d@hj=ӄZ`}ԽxA>y'ጻê뺮W]/V@T,$L*ЌhGRPٓ-=P&I XOmg튶hٻL>LO-ɾNHdTP04RҿOU3jj:D:e]w$;\c6E90Pgжj}*X: $8`1ShHCPAD I]H*$n̛3qfK]$Ͳ}WUw߮U!~-VQ5 ػj[ !PCcP@>{9&b<`Kfng춴 |>b^s@{:P/uXʍ(/ᄎr_߿"HoElMX㚛[!9 rjԡgD\YVvkPKWJbה*6.̲u8a:M _l'.奠a@f s 4)7%)1x7Κ;23uCGfLBT Pdos… SO=%&M$:~~\Gς`Μ9 J;̽N\mo4:;BKzf} ]ӣwj ՊgtYLI?_I ?pįώ<0(R`ﳽUnmjb,b@?/A?;Q_;a'Til֒ˊ:ќQ-5"F4 e78R8T'XcnzFnϜR%'SI7IrWӫHwpuyy:| ص'@TLYl/4 1mSȪC淤 8CF3<#l`i߁O8SݏIM?XI'NZWi ;֖3ws4'͏'.yNĖT}T;]4\7<')̯yDNt`L(Jm3Y\-7Vha7٥+z&,8bN8ۀ"l>5na#A&U#2JAIu=l]㫀 (&`StJg(U]sAjAX}o aT$}RCZ'_Rf?o<=9vfɓ'jjj`„ =Gd5XRr8sSeWo䄭&T9 J`O ?S"%S7s/LцnT^izSǞ/ [44^5A, &# : Y|u@jyBHK*=V~牣3=1q8qB޷~$@} j r`vuK-lZk RX~vmFRH<yx5NJFtxnڿn Iy ZR'n:dL ~kַZ1:[DHp% c`'5_G0˻RQ#|vw2. \ ?ꡮ?K)5lʠY P KZm}ɓ'&i&uVǫ_ 4}?Q|;{-1G|}Rw@"0M+\kiSH| {}m0)HϦkTW$sտI'?|)v"1`{$یwЊ%A3g~uV/ Jb<(={$?@ a" L:_)7fu}!Λ):{??W T|X8cw7?[['?JdT?%?(8Ŀx|>%I1!$S뾬{ƴ* ~Zc-UOtSIF cA#`<`kOtT؁w](U^][,xlV6x뭷477;L}0qI;w^yv-C3 "@YSXW|n\gn]2Y=$H8 rJhIOz~Ƚሗl}.;F]n pʉE@b5?ēI@eߖ<{:$"6U^xh$ 3zTdQ?a2%e7L!\6dzPLCNx{"Hf;ULh_G+c"4a)>~xxccğyK/9̞KpW@- :@-yHˣ(>9]nh+ Jl[-G=.оX@Ïk_6G_?Ye_ @Ċ B78!:̸Q;”@."@i 9sof@:Iު6=g=gөN[A!pD2:Cef#2莪͢2QgS 9?JY_|km[CR0iݮ^3ѡRyW|̱k ϟky_<0ܨZE;2ˤPwzoTmB'n;< j+) $i>R5s8wd%v|~N4ڼ^S0W7[i;ZB<׵a_w05Y Q0Ȍ 0>e%G@ / PoH`0 @& df@4L(P'x"*|\ʀW1vQw_%3 W~͵,d&E.5Yg!60phXWCRQ"NOe5"X .$MEGX4&-b靄@8w!U ˩7K-$LdQ 碎:%DʎI93P̞f'⌟h8pD8e}eH Ɵ0x20"mpO'QmAC94HI&M-oinwۤ!Ph)'xty'fc?#Hf RRLs N{_nMD-u? rQ__TC@H0s\ %"zis,=1qdGpCDz 5TkN |Sakn Ό+'iؐ/4@BGHB( OlӉ+zN ye$G8rVaーtOX*D=.U4} b78IrdE^$r dTȹ#? f|44aռjP@=+O9ɛy `: A䔲:$8>Gӧ;)gϞ)pV@TG,g]t̿>k:oI0oo/^Kgvg[ i:Y&H`֡ Xj p`6_ 3n6"s3ϻXh[xNSHyKA;©R*7k!^s?يm_j?G;U@ $~3& A^zwzmxEəd%Mm0''4?u@U?dސwK PR'HfO}]2JG۞/%5lj2Sz fn գ![<O<~l 0iµp]aFO?t( 1w^%. sdA U\4#\ Uf5J1T8ks܁,Hh|gA*&K=-F=BCE2@{{٥̜x <~b'y.JMuR0uB{3'N Hs?-799?1>GvܟJOcyqĔFfل^|XQDr$-ceRa|ARhj:Br?+A*]^~=׏.z h В%3~~F_7- N}Ԡ  <&s̴ro׆<@C '@d %#9?NU_PfTI\'.{NO5 ::?ٟՓ<o+E P#z?S08MUΕ3_HL ;Tڸʪ|k>|'TUk-h\E0c-ZR HCkِuVI+Q%@zGgcG$ТE=H9fN p_{Znqxd7-0?=JgqL x\ʺ ̆D ̙9~!k %BBI< ?(P+=w{c/PA 4Kp8$-p!0>8=ρ*Oe蟊lQ]Ο }W,^m-Xu=*:z1jvKxF`x!~`<ܘg|Mtؑ4GvP@@O]9,J6kH@ OmgC~wKaXKc~j(KsM`B Vww㟹I`-AWLhҐ\ Zdɕ}Z+|hpC6?S`'AD BgbȔ1LIp &# o\%/Y͍ sÕvtEO"`␪; T8.0V6g-&m1-0%RB(>ZfQ3@xq];0zYEE"#z&Ei!8#@՘IҸI!dtHKM ){,k'0VN$ 3J2oNH\S%0n3 B)0va߸ף8dA~Rɸ~8bҝt%پ?>A'̎),I8vaGq֖Qa1UHeb6̱!0a(Gւe 0}tKm=Q$6>,ȱP%EuL89H0rip!_?Z_dPGY6SglF{Ӓ,TAyt8~A  'ވp?M0o*I0-R_@6jçΙۂFHLÝ%d"A?"pF' T#o ?֩ic4({;:%I06zTD5ih0  N $&NtuH YX3Ёq%8 @5E-v&d*.Wt6fCS{?.C[a^N~:B@ 5?@mV dH.Q͆m5s"uϜs&UQ.J܆NsH>Al`Dv|F߷e&Im?'gּ p?ӍMӽ}8s&&~V][@ -K{Pp\ 6~Ny58#_b30YhPǿgnjM@hftl9Yy[$}?4r;s``$e r4C@~"U|JQmݹwy jy 9LjI b@Ec![ shq9BHGtKuW@ ?:A@fu9;Rhmivo,dc`eFgAWDn/tMiպF )X>`&ف `9BT)6X@E:vy|N/EnzRh, dyBl- a-@12x[St[MUfΫŵFJ٪R7!@"RO5I |WH+b\:@:!t@g>,m\S6? a|yNk˰=D̟,ph".)\ߗ9dL˼U )4UpxikƛOg R sL?o.S_j (u9)鏯?&? ?oo͍ ljۗ>6Cn周, s흁kBC@ 4x @@䚡&B , q::{>öYڻ 60 8Nӽ0{BGDPG- )zD6OZ] A=nNXO'@I'=TS=Fd}|I;!E?z=b'^=/'\u_Np;ߒ'*+.c_J9PD`-s/OˆŇ '!Ϗ2.^( ceJlsN{pkׯ?e 6NB;~" =߮nPM>}|~;2?tN{;] B/^**la-6Q>@w>6OѴ(;o!P귥ɦ@f 0ojjd@v̀IB&u?|W9z\D6(ŒO?Aۀj?xṧ?wE  O yn['W-qhdE}<:bE@'*p6? pSCW/ٕު@88dαuUw5q!mz~YD ,T={9+ieVpi0m7{'=O}}gs{,XG_:s:>ֶXj :>U7<:_x/` VY&<7vLmh&d2wPޗ=˶2e@ͱ!a|mVZjL2@0(g?i~1:2h1xF?b@jXX$%W :=rpFtD <7 .0w=H/ Ke8@^c`ܮ٢[ B ILZDl~LTA=L@ph@Ă @U0JhXNK rC Lp[T` ??x1%%}itCts0?;~$-<"Udu-:;ݟ"0$"@G24XmWfw#[s6:+UE!W'b8Y'Z6P8ߜ~͹>Їt"+w_ju:DfI<&/~F)Ш_!yC}Nb"FOOW%g둫3[gm-Zwh Dv-\ _n&`(>>uϭAUZ_~R%T͘@g WX9P@j(Kb1+E?ӈc=G'x!EH~+m"֯i@!$qx<"4~G0?b(C])6E*Nf .gՑ7*/҇_ka vr|v>`r.)@ܖ,^`; )) ə?(IŸ6ߋF_o;897@AUե|qSMM_xMȵV[qg -!휠V]kyؔna}ӷa9gngf:N=5?bN R`i#lď~C `9?b~=eAݨq,؍zi6zFd48&~D>8'#Fw9lDmݽ}ӴNƝ !!&T>i|\Z g4iQ-P#byn<Zi=r+08=?r?~]'%@m0}W9Prq @eX :zsAHQZL=RV!'!,Џ}Q5ȹaD$ qvb[A.泄sy㩤Hk8, P9F5wk?:9uzPWf-]1 (_*ɷ{6#m٩zj c} l1l΃ OG0u=fiĀq)@֍z(;G9`|ЌUYȃ',0NF6 qDL>ڙ?0`d'3y8@$=thnA;I^P]ƘGg @@Kdl֥|YBT[N35-st#sy.˱`?Xq܁Vmoõ}6h O7 @ߟq˽g 9lnÈ:A GIMG纙`ylu-L6A'/{z8TҒ"C-C YaGtP-o4DѻW$hu4>yv۾tv#z/>[̾ſP[{g"W*7\V@{VH1?y)8qwWyST4B2[e q2uY#zdkLU.V^)r%ظ9U IjP,N/L_{Imk\{<ֳ?tx]tQ9K`JPb HNBȿ*=b B^wOsu.D?9[`[}~!/ōĀqCv׸ mߚ5 d(1TÛ&20Q~Q*P< := )NNwXW :jM<Aď 9d-=x+ygJl:*$jQ.8w"on&D`!P0oxxE @v !Ou7 ;W L1R\O<`_d@ٓA6|zﱹ}|V$38X%Y3?![=ikl#Ml6[A#,s@ `W#~!iVwDۮJRnUΠ=qdcx^l?-(x[s,Ph*i.0l8d^(.W 8=7P"!Sd,0c<&cu< d̠g4Ύ !$vo'nt|-'hCپ`ol;$IsmwYC!Xֿs?uBh~ӧƪ<4~T2?.X\Rv :'`%obR@8<8s߮r,`A|\RF_X`8p‘FL/s<H>Q PBqz֫)@9~0mzpPqYz\߿]'dirͪ_6 *G24^6.`)񹵹O^N4*ZDjz1bQ;޿2wvx+G6+#e_/ZT蜠; 9hjti w@IM& T=#t8ǿ?Y] d}'w / ɿGUsU㟇M:^^6J/kO/_, yl xNǑl}A/2~S `( Xqc s w|C^}:AL:UZJ/ϰw^ ˃X޷Ofj+r<@/K(p~*{,Q pr; {D9М +NԿA q ^R?+GJuMف-afo//߾Ppɭ> ]1?hؑ'l(O*aեڽRTI4h,P _xg}7}gvF?B՟]7_q Z{;G"@# 1Z^&:A1%` v [" +;(j2?|G!>"ne"P੯@KDȨ+-eE9Jާ[ WYCbsn< y 3N`x"s ||AV.;sCOzuƙГu9CArC@yּ0^Rv#{G 1TqT5K_VEYug %+?.5S#ǁu^x_,#ug$55ˏ;1T{MPP)# Q3 $s?~\ׄ#BT(̢\ :@L=@$pEJqТvgr>~}$WK][srVMJc0B-'-i Xx&VUm7jp&k7Al u1%Ksy$hKP ?|^O~Ÿ6? ! ٶٿipSE,_.Y/yaGI5U\(OPm>5ɓ7 %@]7qd@ 0g3N 1#fcXG 9C٦Op@ߋ</|DD0TdW mv" l$}${Xٿ* ՛{M}n$m ':UZ[gB(, x~f#"G;d"c:1wrtq̺9L 'OC~D_>:9,EI{mB+;AP/Dk$$CBd3cϝ&83!~fKcTx=I<#Fr}!Ь91RX`G%āY&@xlQv#|FG-'|F.?? `]w ?5{ΌCS**K;.l,eBƏ`8JsA9 2L>jX+N~lx<?zdG5|M,@t},/W\Ix:z>3C<~78J1 Ź/_B^'l59^ړlmX@9@} S F9JD;`c{MǍn' *׏籱́t 0ʮ ƮNV ZQ)s' ŸKIy83E=8'x;g0c@EZ/_;nF^_vر_~{7}(}a37`,:t#uƨzi`s d{vҿV8|vA nS>hE0`.g^?ܞHH- _y,Mm˦,4[4OsOu˚tf@7F |J B4r ei8=l}KW}X ITva׍םӁmHf-1?PI殺l>m7x˒>"1 H"`qP8 (K}8am7zWm?"`{@DX_`Af -)F/{Of;Ȃ#"Nb#!8#?mݎEF}Q xdVzH_zw}7#xvo#Ǐ_U.\C4776b/:iJe'Ł^/2[H=F@Ŋ I6Odf)w{~v⁓rDZ'!8qYC)ont |94CuniO>Tm@+ m52U|g@L0AXN7 noWm*X P'@rm^Զmwo`,?{#!LrQ%DzhsO,@Zhִ ,T̃0lhZkJVp1hчvxL/%]`lxtLW \bAcT  EϬ2}hqغ}[,)̙3!"ϟ@0ЖOe#f&l$I_:t?6_(#ܷ0 \D/i?p2KKw"^$ \_͹D'X#}.'FG@p\[2+iLd fϠ\3:fR@s B7?˼@a-?7\ >k̷Y. {T>E Vt Pt}zwB_%tJT7~PA,3 B:]4Fy ZlP`[UCfy}1#?||G ݨ2zAJ0:)ɥSdK ^'U0pᦧwӉR?2sd TWu!N" 6}&/_Z5blZ> ',z_[!=\(;<jgC} ?뇳YgSr,9^+ nsb

m! D~*d-hP9@X= Ac @$ Zt|*ˡs{&`U`d$27d/p#?O{@&2C pYmJ'@̰r?͙mO'T|{Y1$ǣ$)<0Pl&;Q?n\1FI\QkXBU-&*^8Qf [9^ ~ɻ5 3G|f'5/FPP?_/籯ՀR֊/dn D>TSf5'Kg"k_8.z"at 37n&;jOWl霊+cvuja-yySBXy<{evtYOC 1`!SwtO}8G`}H42%7ŀ4@}HI.J?׵}prEAΘGjog'MK@Vb$h8?zz ;;;K/"9Bd ~c.}rRɟ?䌐w/w$4RO@(PЎNcA~`:z \=ʀJ[Nt(%={"+^gK1/Me>Zb)8_~8#]ctO#0zDX#4"W`U l9dG`k;dF bMZ̽>JQ9G_ BEĨȓy|(98AdGxwuu rKĸ"6A \heV)l'p?W>'1*"[L|^;dϯ[uJVb 'K]k"kOf?eks#s:F\h+y )͸0)+ٟ^o@vfNG6_YPi[Cop={bɾ{4 k{<%J:B`~^Wőm?{_Պ~{8R'F4_Y>{>=CbəB@. 6 =^rOJQX,(*j}߂Fp ICƆ+nO,8w'OԎD@n 8|}wNAء6ٵ,tFϐf2~Α m:;N<5~/@ݏt!@n~;]dE%{pI0^8P^eo?>~MاO8g~vI@tB7RurFEt=?f;eZSךnrxߝlKlc=rwx"`kh `lQ +{㓹'ݭvHo @7:/?.~9`ZM f$'O/*>i Xmp'`*}&#Gu=իWE%@G@1ǰο.>e pp<fi^>: S8Ou'|Ha_uNcGc"Ci;q:]#+DZ%w/|Lym[|}|b*|[}rhG ُNg_0|D|ϊ~rkfț~ >υo^6/((Ma-uf]v$-&K&{'=+^}wT-}g}X R`_?FAg\'{ %-~ w'[+89HbM)l*wlתɬeLq[?.9pĽIl౲$G*۲$q0A9Ss;`6]:ÇwiqΔs 1dQr ~/r n {,4|`^nۻf N濛vڷf  k*  Ã2quwDYvl.4:&^O)#ej@-3m W"W|wGUkS9HaR2ki쭁ݜ([¶VOc[8V;kX$JRYSU :cTXŜ1l]82kېs ے;RDs(ĮQ}Q-h)gz$t8/gUЧc%6Gt !q0N '`D)$' oa["2u3Yۦf`U(ïۺm 6'ä#o1${dh>VVnE[~p@ͼ{_AZ|Ѣ:z#=S2OÂ3 @[F; @\ONAK yEI' l҉ؓqPOgOd^ڇpGuh ^;oIQ(kʒ_$D Zl'7s#5F/}ndwWzp%s#&_͵*쭝Ep2@` ` 5 025U&̿mlOtYVMS$@k4Aۓ`''jê)h 0!' v`V;G>!oӀh%c^<@?NsEG %P7Op+8)HE59`?I'{qֺ!8ݑ p'0QT7{1L0|F^BQ4o,mTWoTߞ;ųn-G߿:V85AĖdV~'[;-5F2oTz+Y<ڦ hJF}e6fٜ3/4pP ₱>7bE"~b~{Fq8 @1gS J%, <Ni1ϳ{V @߻v>`65,HD@D +3x6id#G!2rB% c YA<.. (_05WdO4e-%;KWE_4[ʌǿp)4z \hsekq@=Iz~ [x2k^7!;-A v^??Y ~ꄎ>Wn*?nӟ*@'b:D+ 4byOe5NbOc{W:8함$)05,Mc-kj}>;kSEeaڴL=6c T)ZM~cEh@9~2v@,-Zc 0z " 7-TG"mSTGp JeϬUXƧOzځ+-7ӌ[0p۷nZȟ.0ش>X}Y2VMf۫'9]uSL~xX(<+f[M1" ($7VbA[rraf!^pܵz dEO|JRZ0m\S\ ]ܿW~(;"J~gMO`Ubٕ>'♕'XoIX$U銾pUgj/:>OT&zYSq0 Ϩ&I175LL #$"onKexUO6mf6/0Q'UIH*m*P /v Pp {̂&ms/΋ ޓ4T@S՝LJA:կ?8S|*:\-ljY}8_Q~X+bGŽ~7RACy8*΀â 㕂>=} Rc<'Pթty[rS(<.L~`oC2guT2&1oˬ=?Bp2a&IDϻ"/ ea~^uVz7G~+DDnG :cB['UTW^o/ Gg*Ґi#A6/بx%Ht7Z Eq?/a <__);__ΫӕzE~<,l߲g߿hRE?vh`Юim,/@5Ds-@̳(ҭ@E EG nUYcƼqǿݥ~n.b|z4ەm37:㏀@}(msBAKs}q`K!7mTcҨ`IԿw3oËu[G[y@>Tt ^6ëmqz-FP`ɿK .J?'+y oN{ޚik~6@}Y*QeY3ߜn $k_='G@ϱ"#$`U HA$IO fg%&p [ ~.>U;;_rS$,ͥ??neZpg md琟mMwF:ŧO9@Rآx:}ݧ@@VB$/3^k/΍(Y& :[Y([/z(Kl'ggo?xgON>LbNV$W;Ķ``$-Bh k`f&/ Sϴ~"tdLS#'4T41{P-u7'@ų(/aaHֿx 'ْ+ڑwg\p NnU<_=/ .P^ >)\j&8o[lfr$Վ8,8wH hHY>x"+;_Ej_:Y!o胳=7p_SVq.C?)8h20SVODSU#_eKN.~$Sp1)ȌjU a`W1@ԿyYo̽ʺ26;|ySՃ ^qg1?Yʁ~>Hs{MQ ( tK=RQԚ "|  |/m4-D~r>߽0/ q_fErw*ߣT$F剁 +IeME_2ݦ"(/'sE>2Ib57cXKkdXȑm ꏤԎ!2|T*@,/gϏ_O6b 0jK٢ߵk'W/5Ji!()B 5v?Q(mY>5LK~@vզu*%M =Z JtSz[Ԛ@挫725qv<%v@@&g*D3GrZN8@ˁ0jlϲ{γH@>Sr{j^(b/+`.ܛ5A gn}7\Hg -`;2IٶTF"Abl o\oV lU *Ill&@ր#f'8*ӢQfH~%Z L9f[<-2pTfܨ p_qjǺJOP+,~X" jɆ@pfIՏj@@~=j8=JD cA.5籋& @@7|=hn''?P7 퇖t[(S[3~  Tĺ}**OeK! Lem* qX6,Kcmf   zV6M%~`GOv@ctnVI$:3$K og{ ,7@ElKxksdz#̻߇=ic;)kvnO'? pF/_4x!ɏd  Vt7 ѐ[D Zrߨ1 ~~+T5P?ed%y_]~)Xb-^k]%l@WO`$Q)+M̓f*C@/Q?}D|rQеf 2ch~@caR~4/{AYwuarX"Dq-h9U^& p{u@"}%p: p{|] RA`, ?:=靹:}qm<u]%J4<f)*4[NySܜZdG ԙ#' M#hJ]$gCw~A>+%BG .[DzM @cm~oF3 ^6{aփ? O9N`́E"vӉ@}ǗE|zw|KE6G<7_]&:  \ o\leMZHvT% eI۷ `G5X9A Ll dQQe@~F҇=}2Xf` @}}{z/0zZㆰU/eau'0/;MF]/܇ dv;윲s7uT>^^r}Ќ8O1[@%q/`$AVMw5'L㟝s`:[fI ipCm]$&,z(3L8ȫK nɏM0(2AN-ec[;K~F[|;Xܱ=%tȕkGogҔ p2zlAȇu,?#s??JɃOgD/ٺ[|ѝܧwB0^x ?wdU{}~:q`Dgɀ7Ni]B[־+MaqmVLYTAv: C~c(H=HYOs}b3>Hӗ.>_6nϏ& ?5@{( }ϧ"ˁR]==ˊ`XNk?^wH{r`[~P{ qg̋~FO!F&-A&,6=͖UwțL d.O# ؠpS 8DL|3`On!~Ȣc6Y~X}1gTA(c[ܚVq3h.3zNJ?*jI_ @`H{j(#-hKney/RwDI@Ҹ (㵩Sx@q}-fs,0F%wͮ2XA#d}ߟk1@y }f`pL`2Sطn*k۔n7L X;^3ULd@lE Co-Yr,xiooh%; NsC_䵾T7;)+sTHv*4N)ٹCAh.ٵx~hks/D+C%!Bɯ?oKOea`XמS_EIУ !^Xm+R`˺B8]ު$ִ"Q6bkXmQ[[l9NdyPĞ̾=7,8Y@PG,2,-ǃ3}(l0 CL{|>n&3vvl: 7(@|H=|0 {|M9U 1RU@}?.-%eC0SGm?Jb1ⷻ6uyXr3A2v kX25:?}8Hb &$v-HdE)?Z P0n F`!\c<ƖWW%-$9 P- F1|\YX`tNAXPt8׭+6PmQ)kMߋ0/ Y f, US F-щt_W)P} AAŁ航~''aoO$S<`y?x)ߤ}T'k?e akUOf%ms0I-$`uD,427&&B<_*roaOR*ZbIbEcD|Nkw@ߡ%Ba7`b+G~xXu~/wB[AJXTԥ@}$-|ؖїkz?PþwLdjSnG*\XJUQGח: CGe{t-2'Q&JD*WŽ3 ^ ) 9`\ zi쾍֌ũ0W7]l۷v x ? D렱<3XYcG=WAۨ8'I ?YHVkn4v$ Z`hڔ3{b4pM-~X-"4VfvÍ # P'? RˁTKt&tI5"~' /bBPN $?y,8`DOv6Zd+`̻Ypw5-) ɪluႬ~q!߾uSDߴd`΁[VLeAALC(D?x>6ǀo*3cPgn 0j%ҶH2"?yP7 /"(sGҠ#g2UpĬDDO_?B{d}\*nۋ ydwX8m@#$GwX(u]GVoV/HbKR53m5:%r uG@Č@81Ih s;q_ 0YhVPD"2؎SX4 ް$5' ٪lG LA6A2O?>b~A8ظ4yh Sq֒?ӊyJUpE "9T'tĂxi nX,#j' X/,-CGzvύ)Ép[յOlO;2j@ %D7W /x5@n!\JN#[ f-|pDgh :@I5ؼ]FzSr˶~տc[zDdz t|Q0F@<.6[Ko:#)t"bjpPwMsC[ fhAdTaB uHx]]L*9q"Ƚ{u$$幖:㟁 K ܕ.6=h *@ĝ0}WAտӕnQM Mo"AMIamhZaח& kMf;WMek&l𬿲 Y[~O_RYIV.>_RQH Rt?@c}9rB= 8(/4Cvސ0HW_@i?uA^ _בw-!`[fD̫z #d es jS4)g$bp~k8z _n)3zP( k{#n3S D5ᵤ1?8]?= Ylj`Ll[;ILԗ&j @db~ wG3~׬TGLX21E *S5Xd4 ]a܏Vow+۷ht`O DnSOSlFvJn>n#?Anvu U8~z16 hN~27\i,_P{@ފJn Sv/:b96Zjuی>m``0f5d-7<`X nQ0 X0U@V$D2x<.d_$ed T?`N$R VIyS՛ZqBP:c|o;B9w Ub.w{v":w]4l 6¿_!2Ε$@gN ;5Kd=NHپ| "'&G*G' $okl V@Xp:Xk! dU[^ m[v;WՂɃG(t3?Պ%/J A/I!KS@ 0UD2͙Pxژ{ X0+-*ÒxNħߩ\geEw̯V2p&h/_<]+VFe @U1Lvea`Dr)"|DZDE@Baۺa'@+梅hpXj'Qh(OF ߗ 30 ǟW 'πgEb&9L D00 ]it: ΔuP^>~8 _7@}*S5AX" V,3hY ֻW+//5F0~TX (:|=Ѽl(wԤ}k< եɖ)P\@w~e֓,28*3II*'S{IG!(W"NeyG tB׿R@3QEz;kŹu<'d_0=zC:^ *oX]MԮXV^QVv/ht1E.GNrAD$h c+L@XSvVWjU$x'nۚnJ%R(# &BC $w@(hb?Fd! J nwٿ'ढ़>Q@6%@\;GJ}77۩e},:2wl\ݽcS=hڸmY*lPV.`˗-[HtPWXW[97,ʋ"6/(wT%]?7Ƕ- > ܫRD?/I$x`zY\/4 a_$=3:oc[bm@{ݑ7mj"7@n[Š!9Fe }OV‰@[H5 TPCEG͹\" O ) |2.ۏ4mZ]cc}[ct{Ql,_%*+VWɷX_6%oQF hXUӄd-0"![G_e=&Wxl'* j({n wͲV=t)SOOs,odECP؉`1'coUmtXE+yp\7ѿ"3 -OwHvۉsCX!/R/{s zB`h P5 !X T]Pă8:.DCUnZ[6];Uy@0d߼n ֯\C}cbU}4j/v,F+j6:""t3C`Or ]15%?Ocya\_J'&_x/go 1 ^Efe b0=!FD<yii$,upXhp8dF@;_D Vwmpm5l?R -C}nE}>ɕ]pٍ ; n,K〪]q${*VbyDf3Q[[S)u+z+PoP|5r\;D4lC 0X~Cyr ll)  uV@͆DA6-1@A71c]'Kx1q9ӕ o`EHT+5 @{ Vdi"``lb / #Gi /gKu쾧ne@V0VT?7ÝӚ1˪}qOsw Z-X] Eo)׭҂,ۄ#1&Hף%@3~^ Xp1[\ZZ~]h^^3Sl{U q;lCֿh@7yĐaT B;P6QO 2?* P֯dzy2s5&Yd6xtAoύ;6jD-͚G3z#h10{|:2!J*H t= u"@{=P -(\ bqIYuMh X(I}3Ih E,Y?V_4[,2QWd'9b9P LoP/QUv ;g<:u$Ce}G k^P;zn}t/INz|u `+Íȡƒ hxWxiPkU,dɀ`zD97^)Kf5Mw]Z`7m\050և,L!A?Zo 3-P'uN3^{wΚ3']?H*d!:U{%_rS( /rk)::{clm6[%mY4־[%[-زCHLB@ 84i8 U l !CH9٫@NMiwoEsfe{\)\h(v\$U2s@վ 2m7%{&H}yߍ\U }U#\ 0  l΃ˠB@?lcE0`7#t\nh!HP%ғIav:?\Á\|f܂%dnY$P׆ol@@]k{djg :Aˣ$p[wVړ&֯ Y #H)w@{pۅ,,T"7X?U |{Zl(dF8jkۍ{ Н?l޶7"΃ "+#$cW9V|vbvj wt^{m^CK"')c!Eqi yO^xx²? 9 '¸b }Jz̀er|u=p5Lۜ:&DSedl[Cw @[M\u̡Ў@\8`PTu<1T̖".T#WeV#V}χ/@[CCԏ2Nh*H*#!P>ǏC @\য়.i\Nz١08HÑ铻~7;+2P~dS? g@Յ +ib X}FT2>9gPrQ`0iUcD@ @f\jwTl&є,rIN pC#?x}ٿ`[F^V+ x, \`Ui PྫtG~.~2 0AC[B ~3N 0Bؕ+g(bqNdZA؊ 33C*xݺ*jS Y+֬eh`0 k ;dIW~]|.+?ݗH}+fm{= GZ ?'1|Le w^!@''/f TV#p&f#-T}Oƪ;=v ik6uُTG1޸;@n!Ŭ_|$&@O_$Cd#x*uA ion<0AA;p彃7^c*@,?(THU(u=u+ ݸɞ`/n]n<ا߷iܬmIO={ﲐ~[R^ܴxrv1a ;sۗõ߃ki!;g#]}9d_Z`Gw #  `Gwu&;!@IFwg Ç/ rڔKkJzr f$yʮ` ޝ}ֽjWk9S ,=&u+z8ߍ~l. /z#Hc (EZ98o^)I*S\~Ou#p#X4Dr9) 1o{H@ j?JςM?Fz@T{L~_K6;1JF$ dC;@b37-+?dFdG6h)h }3FȭZyF!~`}n~Rn FVL}_ n \ `,;J ۆ_Gd` _+po#qPmOQ=DCH_ }x@/χ U;`H l* ~9z @J}ۂ"CU   I i}P_Å{5?֛Nek *d0X,vw-@_q"fynL{jQ{1_aO29sRv*6vP웊6lwe*;ZxJ}sܹ@ r=Y*}[#`.%E_;->,$c॑ @OG Ҭ\B\#l5Rh͛-IyI`b=` @ ?Jx_%S T 3Y/o@0D Б+C xUM$:YY3EEaIe3 -~>o=8UK:*AP1U||+Rk߻0D(G[‘%SAqJ~v] s\v;/Y}+`SqIQ召9sUfO?յҠXH39?pѪbw\c\цU?߮ B2(" ; j  1Pk_ރvVesuhN `{ֱS{|x7 0JEBhhk#͋vE/' bh׊P@$BiFaT0i7*EZfo&7&[_+AZCPn}E_S˟*=u| k(h@X̭}qE"0f`eGCBQ@fO49cSlZ)yO$_ԀRs<bAXh=_  Q{{c߮NǀU@@Ɔ0!;OiZvj ?L7󏒾=o{;˚ :6_}H+ 0,EmdivwMfKĬGNh) 2ui@P~tߢwo3㽹ltw;ٻG p믃/峁u<6jqpgun\ei.6 {B <33Wf(1Jޕlo@K@q^U6aT B\j{ ,ћI"`z#Ui `XxQi5F͜q@q?ស癀]4\VU}T.Dwm\9A? Y~.~ 6CŰL̲x9ɤ;-83@2V2w܇ OC=8ґ?ҙ=^v¢|>ήo Q{GUu'U(9pC C[Pli'<)˫̆@._ _ŭܫ1NE.#}58aʀS ͏ 1O=}d5=[~魞C[1%"7Ƴgׇ LT^ #VUwJ[`I?_q"(,̞* C58&86& @ӃwwB' n=7f,%۩ǜ`IO#*,( ƍX{Y ]lAU[ $ޫ0qp!b=hm,|pP|OuU@pt V%{ 2*Z ZO$A6=ؚ.Vt L} MNc-@?v_7Q9#f8(Dz9R%Z(ӯ~yފIXpt?Z&@d: amm{kOپ\nq$,d,͎&@U^"Nx_ɞ9^<}:1C eHi'ca: 8Hd@ ē@jчw*Sm sVXh$h p[*qF <Ԛ vץqk_?*WN/,1#VJKq<`ГaFh\Ѐ&=z׳`?_;:o# \ ̀Nߝ;`^LB8( 0샃("`O^vg#?-R }o j2cVKOg;k7x;SO3"$"p"xiON!h0I 6\w`j ؑtK"0jg}{0)7 O*XhJKb|K\KJ릑 {ejyv͵:pۦ*@5v8[ է`C`}᪰|.}06"+p/`TOY! JC*蟼*Xk u2y]VoIW Pc!#Npj҃sB#"NE N|(0 04(I{lNTv#ͼߓJ{@3Xf [Q d]|U0,+(V1YEK~jgJF@3^o8(* ,OUDئj@mҕ˿7vWP AB«]m㭋ۆ\[}p\cf{TUqO,0A;PS־ %@"Zs9zY%X 4c* z|F '5+zy6Pcjwզr7@ߟ7HO')hnI *àFyeF{@ `=2^paF,ېpA,h F!SHy.wUߍ qEA oãa "t؟cp$7̩/n_H;!ob-g9QaIbྞ];fJ-o{,m$H)5۞NC[i  .lw;ҕzj2 @,wg`{Ÿ%d0[  #Y;=UY$cU+NOlqMD|A{1/9~twk_XO, @gKE /٣ ?@UH_ `ڏk{ު`/H{Z~쏖>it ֽ-Wd+ aJ=k_xLABes2BD/X,#>N\.x ~j} @ʅEZ7#_y|5ȣf@=^Xsv-bȇQyzqn x~8BE"P@ GN"&{޷32jObᡷPuj p ` %|z?dl@̘Gsa@};2٩6 +a~!/ ۞N x}ސMxbm< !_io*VR( xV_.m.Ⱥ$ 6> *ߜU=dTj[2 "Vs8 f"@+W@Z $ʐ @mCg?U*"i`4 @x="Ba: Hp=Ӎ~*_{[79ڵ.߾լ&]ghe3{Ad|W@{o tUesع!op. ~qc.Pۘ <Q?b@s\oKLZsB~r6;LwfN8o5}`E[[`=l$t9=*VA@ ='yuǐb9YEz.fC捠4G?9VdPH&Cͺ OX_qړϏ?0ԥq ~o45sAz Wv"ӛi`):JhpaayȡPy.wܿY ^'b@'oV} }B%5x`wv0pw˘,5!B@3_|}\ B>NjϞb7%B +X/ " };r/zkRԎ\jxN v\MhQN&kmOg3pW^7zW ֩.rӞbg a~ [) 9}/؛w0*v R0s w3#+uHR?BUw7#/, -jeU}eS`fP\i72i={^4v"3bq@7@)yZ>z"8e},lzi_U=q\U 6D>₠$H\gg'z7g-Κtv;ݸTLAˠZlNOb8Vތ/"HGQ.V?e@EU Yˆ>N1x:H-}hK] *D|ȈK;@46RH-<șL| T8!fpe w s x  ̕DJ P 1f@[Ru?<_:;ԙ+#8֙ڞj0x}FJ%*4/h P5 IտVS*0o4?T^|fdłؖ@ "Z } /q Nb *`2@@ l*#wk}KM$| -X@~kmtvEwMYomkJgru_#3@c]A]``2MÅc%  ` 1@@Q$᏷Z.MKwhV{%f>JX> ^ ,Zg>p,͖ z\F2ojȠ Chփ{Z*>#H& s67{/j쨩 Hq Mp!?0E$/ N}vP':vόx_t;I9bf>'`k/@?/ tf>w..AX- $_Td" E2$I}N&|#@Չ1B@$sRUGZG`5oU!<!o2wdP5jLH{x_9!/FU>JC/i_jj\}F 9hF*G*Yyȋ HZl>D}#t|ka`4[+T3%PWUGIp>^Ed o8Gҹewa uvaPHN,,{88 pCpZc^`6@~~/#O ЊD}Zw"x߷.nU,Fw'?q&d=Wmm@5fS4':k(ŃkD"!~LzD@hg$ 狴Xj-"[T-RX^3DY2zWٿ2yuS@#{%A ZŁ o+Z,o爵pM\OѡU"xLqhr#x!{,z -V<w@ yP'=J u3tmC*@?I4@_E=ZѢ_&-* 2"py<@ģ&?7Vʦ!`iyw,Pe@2-1Pa,*>`*{#Dk$d G\2l9Pҕsl~Pf e[ lHf6FP80z.2x+D@!B@q>3Y]_x?/w?/'~on6@%"۳:Up 99<"i %ee5Q4@pa3qRM{Lg]uM" $"4{ˁq9 -ǃc*R:"Ȣ (jT0IZ8ZQ{@-BCew|?K!?|ny>7y-h5Y?VrWT U/ ĉL tJF<ӻ6,V]vdGA4E*wWC'1`,sMPs %3V#;=_nUJ߶f6G]WއkDK}@6@/7቟o!$Hz!{/vNOOQ0XzI\Uwu'W]XX)m/b`CկIUf?f %P[RV2gi+ /o|rR_JD4H 2qQ4@طs, T=$B}LIMCO"}s?#2@_7pZ/ځi ڙ$NxG]LG2hJ X 8sA82XJ5׏48'Wo6#sw%ĖZG+k2RmB 8\$ i_n-q$0IB@?i|}]p ߪ8(%;naWԜe0̾甖óߞ!OM~Fo޽x:-ҍɧ_g73T T ,߱) 1^Dxd=%K.fP; T8UM `2`^Hv] УDzk!`"Wx }w }ܬ57jr!<'[o /Oly,%sT#.1`+O={J> ]@X}W;w KCeAP94U0 @$`ݮuS܌H#(.; (( mI1`~ 0VOB>d+. %>ϹE* S߳+ -e&0o'U7gd7@BD$_t{DػOrE lOP Q?_۞eM=gpM#fMa _*# 0# 8T7iQd}d9wi{{0_ %'gvM`1n'S ~I1+ppm&2y_|ĒP}H͢F[@/& H@u_9=/ERTA&"XHK KD#z$pLt#x~sAGyҷ9]۬*׵ńh"ǤO(90+gI1 \OK&JC4@/.B+z"+@֤EJˇ*'e ~`٢&G_xDMrN|("Fe} Tw PQM7`6I0 Cހ$p ( &tNyoBps?=;J{>ZE?Zcwm394'3}e`7#'/?Yf@@UyepIX4@6"%+) b@+֭0\UQA"0BHu@4Dt lMs8Ufk0!`߀q$ @P)#b_{w}iRDt >yq.SK}d>>0^%tVX8 !y B\[+'#%wzb@q: 17Ry > |yATo}ʲZ+wn%ѬN?=I0 *={k}h5.`BCϼ=_xzylLd!!Rou@@lvLU7D@sXwu#y`wK3ۿ{wZ:$NN:[cx=IOMO{KO~'}k A ȟ}>߹o~ 3O}ah8 zZ稤͗$^1k6L@$r-_??z"MvF޾"p# y+a&;]3[EC"HD ܭZD$ahEM^W$'ƹ3 @`?w?5DB`cP)>B%[0Q;|!|*` P$I)`ՕV2pvO|rֶ.9w/b8&V !@m&6X=on-~gK>N- %PDV ;%"$ ċL8< (u}_D'~:Q ^;|#y@ĀK0<$ϵجRУj|PT&$e(S3#˂|5~[ 9E;0<Čλ\'58h,&;;K&&?x"t/|$՛>??@@AABBCCDDEEGGHHIIJJKKMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[]]^^__``aaccffgghhiijjkkllmmnnooqqrrssuuvvwwxxyyzz{{||}}~~=_ IDATx흉_}j,G 9 hjնikb466"m6QzV 5xpjl@E^P3{컻yy4` 2@RW]g>ѹ\}ge\Wmf@M|C㫞@0v?gO;%|%w,RɎz'|tsԍ|*58ҁY8hOvziTmQ&e D(<Ȑ.n'B}+,0b+,͎ Ahȑ  D(юwBlaj} s&f $c }RMoE~ a,9MO>Ѡ4Q_F`M~dLIp7XAާi,t缭Dy˳_9 ػ 5HUkz..DwLOMqM9`t";n~>J6r~_߅cnPܯfsەSlLEUۂH_#kcɚ~U ^9X%{ n4tymh 8Ni;5mq <A[wd$'4q>qn)J++ߠ?)pp}pq弇:Gc9y~|~lQ9'sQ d pAh>}TL@֠V~^rYyG @M{˨} ;kVC:[T]s@^;@rS  `ijTS"f5J9@G~Dp!HC(`qx@lBQ>j3T_cSc7kU&2Xz 6k> 9۸*?'\qO U1M]<7V,}ר-'Xpr^xyhO&[(pƯf"*ΠN>bk崧'oa\JMkPN"3('1qs 'YFn Am7FҒLN\`>͋P-{ym a?1:^E۷$J/}B0ظ#m1ψlr"hB5($II;OX3'taL P$b Lj4Ij3 T,>A Bm xPnx&(O3NCmȍk^⚡{DQyB̖΃HBT^'PA$d 2@ dbMڋ7Or?PT;9ǥUޓ$Q4 壾})|gw/=T;9ߔVuC_N @S{xqDuz]qai+3<3Qa  9 װg >?vr!U-+ Sy6{5=,_>tߥN j )Xo{lVKT;9  @SMbf5E8 n\ӳ,>r (4u',Ms6ѭ?ZGUEu$,M+{)mrg\FvQ=r]ۻb#gGK;l]349ZCd-΄F'OʃÞ=R#v;MLަ?Jko"3}2Z|Q` ZtX 4}G쇇X d 2@ 0^`%{_8 Z}/pvb{{C&z  (Fꄉ]jl\CфRޘ_kU;h)IbD@) 73 aV2@ d 0hX.\OO{SI]~C]3]z5Hv.G<7NWR] pxW|ه $%Tt?gF͛f ٽTtٯ  =ɮx 'M3]|OBf2R8{~=f3(H}ΠP2 𾞓IN[(ž+Wilj;Ik ?<MDn 6 w4eQM& ; kx&ymGh[jNG$}M;!ymݕr&*KUc ٝmPyHď ȕo0V땓W.ч=ix\@Ay<~hc̟>ⵧU ]dix>('UW}DσCgWY, d _|T^sw߬4M';moVTYtRS}жf:*ߩ>h[}uVS}P}XlNwZlMg `$*'dY>sYdY粝jOoWoqk] j'rZ3(_`Otyވ+z`b'rR@@o_eONd7"u2om+NJOo"{&cڹvu>T;AQ5->h/@lWz; M 2@ daCЩt&F0" 0:#kw*;Sj 2@=R d n6`ʺM0-0-0-̓ 2@#?zIENDB`tiled-qt-0.9.1/src/plugins/replicaisland/island.png000066400000000000000000002227121217502731700223210ustar00rootroot00000000000000PNG  IHDRŐgtEXtSoftwareAdobe ImageReadyqe<%lIDATx] |S(ΡSdžB;mI-(K[Pp(oPQ(ABmrQ`HDюJ(jE8闓sNN^B={X/髒85|5RzQ϶gpCy՚|IOW }.I!I=gI'̯ٗ)9_kݙ#S/ʜRQzic{k^,K ' ?mSZ993+3{ig8c1wr|g[ϝs&{peF}n揊v.p_o|Ǭ;ICm(I/yEvYsc[zI?9dScifgĵzQ׸,^cۦZbaqu]wU-Zj5C+V䷻vlNN]}7޽=;e.Y=ݻMKKKZc3ڵk3*::z%*==}_nV>&ܹ鳸ܫ/:v "cAvzV$XO軲tْt̓;ʍ~y&6u/m(%eZ"c K79/[< ѵij" ŪGl!,wo>]4]Dk,&j 0|qU^X*un>۾T)1Ң1/MfOO0<>[0*Q|C_4F!];$x}m~?X'l^wBu׺|qLyi?+Aiэ} |{f@>+=1"@JtkW^iђHyQ˖-"#prͶ@vv޽{ׯj߾}~ue뻤tʸGQݲbZov\LL{]t9ؽ{uDJLL| ))i!YYYyyyB v-R B#256TonR"}9/}GNn:dk@=.pp= Oz(.@(&$H+I@6'vȟaܗIZpڋ)$''"]v@Hm!4nݐ4ƴW4/4hPzh \W mӦͭ {H?ҳgϭԳ Y]6KIN(HMI~(+3kV戴qVk^'Z 1D簕oGvע&,&Jm}B~.\C74#/*:)=sthmogHqlBD||ϿտRts\g歧3e;K+3f}WHxc"@YP-Ҟe!4"nB_͚wܗ{ ,vb Ӷm[P,LA* ]':1N{)]2'065sA:cmVΝ;-۱^zU"Sh>!!>019;=-u~fSS;wq J޽{ٳG9k6"%{Ai6 {zDBTxwMҙgD(?~tòƨi:vsdu13 eX8 btO)a"gS9ZP0O J\/([oQZUezѮ85EqM0`BG/?\S 7F0o{i篥r|OSb}H#c[=#}<3"  +ߑ'ػwk kF_|lrҀ"DZĐt\GZvӡC^?q,jt:/]QQ[SNK@8D`-SRJJJ{ΤpKRSSvHyh\f"D>#W*:մge]vH2-, 43,U+fq/?# U '޼оH0F,Y:Q+*OF:_jX-FCxoY$ozܮ9kȞPT,j0@\wW\T-s0v\xP 6>[+TZh/0|H /;>7{SI~`?[0.k hvys|>Ogo=q_}<^,g " vkVxohW&''_Dx'r4 5N'{k*wD,jB`}Yv~ӪeKZұS'QW^Dbd1g' go`Y_@dNNݻw,!J?D+C& $^$8_ G?tiii7$ӕCC!$&/dn?!/Dӽ| Ń*f%%)ĄۏH`B8%JLEnLY7퇣W §6>/,"K^=V hΟ@Ga`> 26կ)Hq($S P#?9o1J 83II4i^|W6K-]vI&89B i@r1}I Ii1~.%oHbQijq]|TT;.755v:)ؽ,$Y^111Ӷ2N:<zbRbMVV7=zCxkqY&Oڼ?S"o7㻌xs ( èiv?7e98,cLҵW (0?3co4ޅ)wqο'lL۰cǎrN2#- H\M҉$4絴Hj&%%M6kZ:t4ηDvf`ȠOnOXJFFԔ{ND1: +m[n&2pl_ww1:hWS\VDnyˇ7TFRьi ؀p H?Lʉ j%x-Ltt*b͔"3*2סYī>XIXa/<ˠ|^?b+D`Qg2VL_+;>k!:/ѯ?Au9Na,}C&We|_tqi0809@' ]w݅$E] iQ$lwY,w?$dffZINۛI^OnwVެu[hqmj˖-Fov_|ݫWw @c%cn"-qݾ!-5ekf2v";N7Co'McHKK[Ax?[kWEzot^@0q;ZD&k ,,-XV'~橢*Sbwt|OA]^@"Ũb,F 9U kst#`>cۦt\|ě8'+ UkwXA$'^1qm/aкpC9@#6?e69 %z?QF=L?3d<3?G䪨#FhD }OXF,$&H o&v{+\4Rdxum֭[lq]&{۵k/t\Kh_wgy]allSY]i۶;v|#!!wDD8Hǵٟ}>+h"pNjvN f($@Aa"f8 \ @eZ2c[rexS_gjZ<)d ]\m9{pUO܁6+j(K7.%iV:hl¥*,T {9V-b Biz#jQL"~WPƟ8*=xS?.fuN-(bwAsX7P;u?js ҂hw}7 d(I. KT ei-l~9ݻw%  $$%'_Oݱe9-[VA)C:Dgii;2'f3Co|I_@@t<9DN썬5WE:HQrx f&qf"`V?/LX~uP34J ٩]j`!( Zr&Az/-+8)Nv揢%jZK8S /NI;}KT]+P0(oXT`tNH<`_H  ,:U {WV39,`_ V(?,Z& {+ $,K}ILLIR{i7p>~Kb" ծ=r4#e#0wXF*꣉<6}j{~=Hp dgg{CtU$0(2@#  2JDB1ÄXCX 9,jௗ\$\DWWPY(h-H#J`RhfiӌT6oD٣9ծUՊlUήTY@GK\W#/OC}X*埗_d|1o:0ᦘ`C>Z3]n|"RH^<3 /MKns_@ oFz `JG[_~7кv %y_K҅I.$M%{& PRZT @N|ٷo7'}O'ާ珶ifm-igWD4Jr:D-ovPEj~? `/d4e"p H Pv(h^<֢ ZHADf G6vcD~5GoroCӇ Zzɕ˜L8M @ie}|ܔ:.ρtN_$J0:>+xz>~o9 esgFfrM+d3X'0Ҹ'@#Џ6O mAҍn"'$].2};vL}jvma=Dv=Iclt:e욕}&}>}m"!/"!uϱtncfPax8k-@ n~ՊW)}3'S%Z9pLl7iۊ}@B9V,)2JmD+ ; N{yjt{/~>Vg_1wZX}zA @CX E8n![Q<|?8vqf'_ҡ61͘\޲k jhZT٠A܌2. D$&NW6\ qƖ wC" -Iz8!0>{>Z7ꪫb(&$ sZtttwއnzUa"Rպ"@- 3;t谜ĻDJ#?"A>}m#3p9y!&LK:h@}/HDAu9a' 9CJyW| 2hPI,ΩUJK)XqRZ!E iiZZ_$^KU:6fpN׫|w`YrǴ^=k2By;1Tp,!NQ^ ;KO12X6W鬘0} jd{YO(a55^8K|z/"2~[j$i֍@$ 0޽ L0˭k1'++ߩg;@@:?gV}##@D25Q<` 3Pjv # Ij׀v5og< br#  XVW9~*͋솴?,Q >=g \h]ŅDfY冒G^!0daŴI)qr"eADk/nrdc]e`G,Ag/\3 @;#ʞti͋#L˻~o}/gv56lX#]jժ }v=p?Gi |op4߿EM p56fjtbߊ>!k}l% C:ޯ֭.IIIeVt s9KODq&PN-x3& +!U s$ױ=HXd"_wդ$饩i>MJʉXؼFDS~e~\|ݞ1L' Vlh|aG o_mY;MCbFd욤r.Ee8PP{q\ke(?3_e%N]ap?݃:ujFٙ@>w  X/44iRo!6F _:"w&`Gp:5]~ h~?ONJ>MJ r&>9ྯ]Ocf{ D@ܳgO D$:7q_ӯ_Q-m>8__x}C8$;`DEQ[@I]0F$( ֺ"mnW thj"( e\ i}XkSDcpUPKJWPl֋`mOė BY#@GlR@1C*@-7|\sA_ʗ]W̲e]ٴX s-nSxg֭[7#LҔH>O 0w} o֋@/%}vYvve"КL#}I'Gcp 2d ^Աcdž#hժɉNe6k&" ,is`rr:o/++ocNB;~:C}ID~}|OO>h3%"dƗhXd@ۜsCm-tLBXZ&@6.x"9xк ? "O\#Ň.K_1 ҈0v1R;!,x<%Cm@ H>V%xq!`zA49IE,; WP.Z<[p̞ϖ:wC `C>_ f(cZ>OZyH%lJ7Cx^EssT K`}o{"7f&-%BvkP7tSc" b!-~YrRGkqwď>K#1RIO>C=zt?ۻ9={|NllL5q""G_{ȃ,pocef\`a!e~~} f$^HY#_-rrx.*IΉ󉺇GC a aGCBy}vg*1{YG3` R 5 AU.&D֮sboAp\k ็/?LŜ!a]{k׮]JLLXiii3ޯD &2h$ Zdw"77))i].]%𾟶A!Ё% Z_zw64ik׬Pw{||DBNBwJJO?8ۻwOOO;.g{!>O޽z~~lLa:Xc OI+VdQ[`/f8/$u;JKƏT W2o$l %e>̋j> y<ɚt.,$hˑz@\``0P߽Nx!gX%|AD*$VWc׽Qh)\j0ծ??=ph1 YVd5 4ow[N!K4x֗Cߙ/ru>}ZFF߷%SNxnXE<ϣmI$Ѵ󦅅!"%zfO-.oDq%3@;w4%?]d%S]7cD`>k׶_:od@)weg#t,6hDAqJK@8Fr\':OޡWb}84R.@X9PkzJَ|jetxek"}z!@PِȫF_15ի}tŔŽŮj+ nxAͿ&yVbi`,8Aޟ2v,N 8_Ҿ}qD^"0ޚ"83I~ҋ{%%$GPGEGڶmw+k HHI"W 4ȑ#7n".mWC:'گDÄs>?g&svv7 ?d}y@ , ݉ҹ|O}PPP4EE_l:챠+%r H:>jR^ӢHW"]/4Q@l|5p> (!QE,+;[%"|pJ  ?%7L**@rѕ@'Ws^D.hժUmێ%_m"P͞$h6߿%y}^ݷw"}"@OHKKkKm[tttm>{$݉L ͠ǀPoF}=99 ࿰hrrzԵkOD~ꞝS}!"]FFڷؘo۵k.LX~&W^ @YN>D/ƂB4OmhnE ޥ~ ~C.zXߊ}Xy7? q7 +LZ;M:na"Rd0ӺhܣV:<w%7Xn. >\}p柚OSȴfDՀcV&-[LnݺN:!ٹs-A@5?V '77i׽" m#g۶mMH : w%0OI  ѝ4^Mbb?HzUʔy"RA~ԷO%#6lN;JDh  +/ѵVM\jH$ uOd9;8WRPl9PA{4wAukJ?wDs-hg6]#AD>rq!G-xm~\|,jtb/u#R%0:u=hvXx`{kZhtױcI#t. mO@ڄߜ@?w^Y~=[ccc 'PN >N^z@$aPFFF4gkw*a#~="iZ=ÿ"(У{w|]KqqvԱ6::Aqm8@A!8UR'*e哢XlX&XID" /)w.F $a<`_$>78@,[Vim\ cts[s*!ͨN|f8e@ 84 ]r\qrt""!kUV#I~4=III @!mVW幹9#n9=H}Opڮ]{|W>ߡv?KPRSSo&RCEi7[w|L~C|G%%%PK>)"Dc8E;:~y:Z" ;}4V8=ߟm!?0|{JTm0?W>;۪"3۔m+a;]6Ҕ.KrGXZ&iQ1cnkcݒ9o%SvKEm0rheZطCypWZAEGN-rqmϲ ?\Htp"(#i zd M)'X~lYFX4ЧFXb ^Jgʨe%`%XBl@#@?rC':sˆ[j C,|J$i"W\2ܒȿ=`bX>Q̪`Oմ8T{wqU\v? yg,Mz߅ks=4+\:%->23RB!vudlȭxs߀x܃wFz7K7[pcl+gS]>s!5J|ϙ$(>;U~$j[Vfc>~w_u֋j̀_pHAC,5ן0(qhDFroi D%2Wa|Dž؛ .%;ˁ5yE̕7g`42H3S@xe@_щͮK _8jءǔV({a]5Sɋ1ZPOщ&L3 |Gt `(@ӰD3ؗ<˒7>$v*h~;;, \Jus- XOLA!좁XVe1d%焹Θ4xD"M % ~r?0IRF8#cXE80 s2u'p>@xA bxCk \<6!QUկلNm|ύĂp]'HtBqfh&AXj~w$ 4Kf+ A<UH)glZ9Sihz2d}A&~ۼ۽N-&zLD=W'CV% `y$zyt;oE6SAXPO$ 4K_@h h9fqlmO͌  `O4df} !_ӗZW f-+8EQM(@Ƅ#.\G*rjh4*O$ óHX; u0a5|N$ċySN(Vܬ@2 z_y$V±Y"VF$K6KG7ɦ2!aWt1* cV;r蚍3l{sE@kQaKK)Fs0J.ݯUA婲,ScQ o\5W*++/DϪq? J:l0d o!O$ No ߃P2E &|/bٙb7=x1^}<^vߝC:Ɯ'-!F U+LvAST @$ hԼvlwpb|2X|@c !+{$*֩pMQhin}Y8_Vcs0V1+o`!&}yRu2Q&s̑Ǝ7hD*C%%nlG4R^%{а:blö(#je@ؾ?x;X7 ML~3 $o^00"IX|^Z1~9=e5pə>A64MH&IDu8 D(OkCYfKVͳ?z -s2T}o }6G7 ؾU+| S+y`$Ke:`4_plXM.< !Lt< 07Dp?@ F6 /[ṔF旖[]!MC*2WDsU9UN(Jk-4G#R'47uL+ rI`J71A#z |u,rJS+Vմџ{i#IoDTo"xK!Z @XpA.ǁB7tO\G cW tS@HI(L (?@O~@S3ns"6b?4b=2_ƫ0bf/,vfbFTrp$";mM7Ȁ$MPjf0WN,fN1l[PĨn9roifxT3 B$ kw"[؊C1)+;c"{haA>Rѧ@76NuT-S&fN#G\qWjif b+э9sfY| Ug0oS(%AދAvn/UK x.F XS8Y>{TOgc\ ͗>z%?@+w V\ l+@ָHTKl#% |gipEG5)+)5nشq %UD LJ7QPcL@_.nK\>vT1յEw͠['Fx\ j!,'ܭ B,PGp]~\X&Oq=+ -UgܗXptҘp)8s@SXGHb&/>h;ԊE+@,pao:'`7ly.Yuy5Ő/`ј0j/U&2!?Pd;+@i`9sAլH," e#Zjl8h_R{ @WML @C )w({T䀣#Ȓ1] Ч yQ7v)\%tЯ`!_IyO'. r6枚OYX f\,Z31ҔBw[l5a4WJ;/̀` Ff[Pn_M̨)M,T@ #*(F? Sj f["eh-˛X8^*3AD\]2B*>RڹntrHSH#>7Y[ Ĺۊ}< 8WV 7a? ld#FA u#GN+(wUe̗,M'WkPeImviyޫڌVf<PW^P[=N{^.7X."sMJŃȁb W*2Oc<A{VO.-^XZ6wt3,BX9̀sw_=s.F\[4"$ wvi~ c]\o~[|8W^z%Hٹ'+D7׻ )7 X7\tuB"eΦ˜J? rE:XPPT} ?p3~M"įb̝;S܇Ikк#BAf)DvU<όO`P59%+`:PE?'rQ {Db jBLAj>~%DlY?= |ona!RgyLXCHF=ր"~B FX@5%Z2uѐ8yp=Yorh/ynBB%- 5ޖ,K_>w9bYtj>;Wko\kz$4WI i8P(N1XZ(m&`01P`@[iV[I?*yCv/Dp3IW*[rC/bW%+@Mj + 0JF* ɓ>{щj)FܢݦW=!\~bC1>J#$b:* @KSӼHZ'3 3>fDv@#bi`/\Vg8AxhuD,+`P@ZL 4@1Bo߾W }76Y!Z%i4{e !Q&rgS TD ??,4?Z~ fE!uYD72u2RV1Dm~Fg?|byh3Ejp"Wqϰ@~ +O*)$>'R  _M&KH#wIR% P=& ^?l„bP# x4^uɇRy@&\%DQ2̞"fs7B)LqM%@$~8X!{ GiaIC]91hQ 5L7D( W}1Ab(]qo4Xk.}%}6`k@$仪AMQ!.  2wjlA#I@T!YN 댰m{ӊO+u P|9jfꈔK!T0bıQ2cwπ9$"@Ɲ20`,K,2EP PyB^'LQZ?Aka&)@ۄԻx"a Y Wy UfPR m!(z@i1pi4Vl} >;C)1}S5k [oxLFkiYm[[!BaRTC}D@7 @Po_KA޾3$@ֹcq )6"\J[G ű A³I廒#/-V(Y[912Jp3X7m.(~>KG 7)[[dSI9(5"DЯ h}tJ5K ji‘Z@"6 \a-&&rլZI"0)Y4> GQF>@f@EP>.yH:a&{qx^p3[IYs&^- -~oaZ&=4*\n%=2'ZJXʞp~p^Cv\7?z )clfDO T _qr,DTK'FI)| UQꬄ<3.rWVp Nw6,w[$ _iT3I2eDp`&É%kŎu8Go܅m/Ir;B@ZRϼl 7oSZ着\ G+(:Jҏrx1UPSw8TF$$ 9^C eMpek]<v N9c}M!A;xRK +/yHRb JI[@vNr;ac !cK,r+x`;oR /@qw|,wV߮rU])C±+?~ {G;#IN,OH#Zʡsv'9\Y}uJOK$K=:WN*o#!'Ot23# u6$!aKJ WCI,(ͻ Rُ77H&>,n%'JJ>KVBQ:'* b;`_A*(g`DYz2@-$Юr')>uF14nUNA@&hO$p]瞠SdFLȖo@ֹK.~d޹::IK_HJ1QDkf+Ifn(-|&=.?OҤ(LHM3]XEDz{aQrEOC@оu=[y鞫cD&nwg$ѣM-Y"}> |Ujn 9z@LWIbwj|Y&ՁKgt. ]1+AjK Ł0ξg3%.de`+3 / e mj~? + p>׎='eܽ`3g+]Hb`d㲭yrzR7X<=^8(fI[ܓ?&HVNmw]}:ɱ`7"ۊm c½GGu^y*A&:<_L+] pLz/O[䱒tKO#ђL(໲RMVVHu O' kֿ<'aQ C _=~ A`FPJ4~Pk% YH``oje:kT1)8g0gzZ:M ,~ǽYt bb/s|3>V YXG]ہ o|aAЦRWoKGz4r~lI:pzNv$H_p ː gl]B?)ɫZkujAE^ HG󤈖g敊Y<6Nip3%)ܧZP:"mQx-hLqX$ynZN0ɱ 鱉AfNq|~d?iiw_*ܚK?EYv?@Qҩc\_o`?@E  Ɂ2GXGr%4i$M"! @7NޘU"A1xm\T{6揊j*PA36%IP#  ،s|(J:0V{5`RWyFz`nz[)z-?I-\2>QPo~yqjTfѥ҉r,ÇU.8L#-;T)՞x[&OXΜ9#=!#"=Z"99 +H!n)$Djퟁp1F?&,yH:` B̥ja{s|he(\ A 8Xb)j%_Ϋ>)nT\\PIn| f6n Q1+Iׯ S&?tւ,wq|M8F87UUUJUUUҩ:uɪ2Ǫ~tmU'XfTb  OV/I?糼}@HnխKsYʍd wpvG@0 ؟WHYX @'B'OS}Zju`& p?o+:軺6FL<)A-YC( 0{h^myc#%HFj|-tCk{m%{LUePxVuAv7KU3&RMME B!( }Իjq /M3xz)\GY|w,-}$N$ 9$^+> j9YjsdϲlMg.mMneBX;[D` 1=)pr~!Y,|s?^g  Wg'H'okօ_ͯ%y/@2GRM;Lyrƚ$!RUe?΁4 к#4"Ϲ܇t(qJ8[ 2VVοWgřF7wHc>iYF\!"Ъ_/@}9%W?ցE2  Ke芠w"-s9 ҇z뚿y? rq~6Gw#Hwv[|b d(0݉', a!_ͯÝ!Gu(}H̓~?4D@!I K.9oOqKV( bd`U~!k xICa] !P"࠿ѩ(I糼Vo,^Vy5Qf] (j|qATjB7IdMb;&Ug;p?P_?[b%g!/lgG%Nʖ[,mxH{o霌Q#*G&\UHI 3QwCoQZf(Po "M(LD_QB@N6RK@~҄fb)BךIilz٩QU˻X_e3 ~F!n갸e'c#eyX|EjOwnlj<^K$ͪͩ^UZ ৌ3I$bF`_4A/W3+k `zzH(p\!gKW%qH'[yW VsֿzB[)Fb):"#jo4 <Rgc낚6}`yn]$guq. C4F'6)8Z7Xt 4 0i&}%׺򦥬e5 h%pJ$^{RuS\vun*"OP nK\__@%'z^\*geIw-LU&V9* `_m1udUZԑ>J^&mD2X]~c~)sANo@;Z.k+l/Gv (9C`E}#O.цd A[ V"*F`"NZP/ +%u@|;K1&쀣t2KT(}DiD;TZ|,R#CDTASySV0B޹8Y4hF|Wv3ۅ򥱅F#<,Ы"ǦIa6ظ%K\=ћh1V`lU:0E56: bvg'$cG_&Y&b`(hwYZ-MEtE @#I> "F" v3@i((.>w"oJ-DB"D̴¼_.~>a)>Nm ijx}8* ^!@1*'9.-@nw_B BR~MrAC#=\*_ :`ϟ+c-HFp@̽!f|aMV5 b{!.6 7p28R|OLQ@q`=B\@~g;-Q"pK~YrsRK%lKwk %w:g4ij=?ze0Fӱ;Aފ,9")`.IQgp%CORj}j~à }hj`߯rk4zI@CbɺҤ RU@c/@ s9T+seU9ه[2a[[bBC<~$$Eehoo+,Ybkcw  Wو m" l?KHh,v68Em.PZD qP/e|9>qGo@XLw񎑎?1& GLֺQܨVfƮ%۝HC_U'@s0I(4fV'ګ?i>"ã&\2]JHĨ ;X  ZBhV"QEJy,|h}M*A y"[-hF*P tR:Ǭߖ~{H#bP"%7]@Rӻ,yjpƼY(5\%IUO&$7RR0>7;Y0*[<Arf6F ?|M_*}`K ?ȔMs[}4{{[G0½73#YMxy<\-Y&7W 3 ,i(Q,SkB%l7B&4 N|81Fi-gY7[ZXaq~=p&R8R@5|o--~ bHA{>J-q}HlIYK[dɾ%e_~ϹZvKpBY"3q0 Z3sd49;땍kYDd"qb>=YJWOm|4Rs _:.ͻח@P$ PoI*!<1p4,xzLax{7tYs9S0.f! \/NyF߳$CzaYgnWr)ho0(FZ<&V.d[v@$zڿyh$@ܡo}J9T˘XQ((zi¤AJ q2,co1ĠlՂ&_K^#^O(T1LؒQ0EY`mI@r#K-~ XȔ/wώص@A~rpcKOS% -ԅj ld|2>Apͼ3>Z._ cLI dqQ. 5*(_"}&mgڰCK=L20mC[:!!^%gw,n;$!jđ8*ǎv6@Bf@(-!jYZZjJh30}=Gh,':\-l3"=3(ӧ @ݷj I 2Tk<}d,O0EЈD#j۔}'#/@=t|h i?9?BtD(S-kj,kaE5<&Pw[r6_:(Hm| v9ew=5]j@!:L)"  !?襙Y\ }y v!zrh=p1Ns(ge c]vn}Cuh}xqNx@QGlaH\ $@G޿f$%Y#@2s߯#UŴNw<|^+ IMw@7"d&fӵnW^#w__ D!'j fy({":A@kgx!@,DF}EB汷̯]OP* qJp( /`HG"h:}ݓ-*, 8O5ym~_o]!Py2z:xG.2zE8l["K@zťlF[ywjq 8=փp ɉ_@bQb1!pmHԏd?NΌcfsEa}GJ(D1><MsQ8Ժ"IŊz IG7 )(_"눱/*N,x5d5T7y(P* O4HVHP 6̲*DzkPD "Xlku dz.b?l~ߧ"}@o x֥I!ܟGǑ!8o$1N.c ([|h捰"}~[\+{]o1#@-{^x^ KCgq&9M Gȋf[TbLډ* !?7ixwK|%`0 CohT/'c$IJZ~}17ZzzC**ON֗S"o `*8GF4(R kKSao 0; o`$ir` `oN$^pqSZՇ];RCvj!P|sM% ݗwkndwqHLEC D@ީkDR*FϐɜQ~`D&}$PiKߢ7\kq~I/@MШxPSxLhw)[AtqdAxyebPm>/%_,\ygDR ȣ%}  v6fFp%&ԺBuI.-JpTxJl}Fc?}h}`ky  -&ոDDR=ժuFd'?x` kf (Po +u6,KBM7!De9п-?snʻ1Y}Ok ȥ#4@*U?G Jd~G^~t \+/ AbEAz"kr~_B@}MDf#ea_PHJK`r$S*CܛG`臧<@ İ<B:JXA5zu?33 /J6-(`ŎT0='VbjB&`$tSvXPullR"^H_3o wݭHOc:ܶ-*|idEywi䁍H 5:ZRN;߿_dmKV;t|̵xVm#-"Uv(j~k DA}~1`FxBYRkP~3;`]dVDk]u,k 0?m{gۊ|D@ܙZ}=GrZG=~Pjd;@4/ qOQ3O4 y8H?aR6&o Va[+_s'}^vW-Ρ)DR ^i?H}{-@RAZ|18_rXt2EdN F~ @*fрtF U(E~A<@(3{:@C`t $ Q1K _4(VY;ٺ$~m 򱵦$giO2֚B.!HSDD`#3 #8~m@c&F9?<+ h$e U~p=d`z䰼ԑhvq"tPa+ 䯙1~40 sB5hnh5(Lg")X7>@ľ)+T媁<@2 HdM *}Z@.\8ctA?frG^dw]+ੑ=~Kmo߽gOu q=sC|b;4 8o7 8ϽKJzx{'G$a8Ey)005o``*MiMHk(axd OeN6]EŎ0yJMSʷIDgӖ)@*y \ &5}o #b J[ uF?${Q[d`(*f~ C{o/Ok`Nfc\U.Ba0j{~khmK?>6p⟙@@dĀb1sSRdF5ƌIG(HԘI'o.SKGMKD%µ+j.Ra*Rɦ4ObRIf|\);VR$ˁpm'1<\G}*j["w#V joiصEWm7#( <;=6Wg[템%J =t|H  fш=! JGa HM?DP/'H`^x7{y@>icIЛkwĘ`PY@$4;ZD"'1h$RR*K_Q( H<"8cH>8}pfݯt]ї8= ֮߿`&7΋~ќWsf$  =ls@ʇp[Ofmc(-!2JDF ! Js 4RԡB#oʴR:Tvޢ$Qݹ_ 88x"u;Ţ}Hf3k" zg.9//L 2zߞ;rY*PoŐsa82GfC lAT<SAf Hx`1=$ъF&מЋljTsFX (O銼؇[]q{AS "U̘8?"|if?%덲ĨXH>UzV |ӣ_2j$+#I_Nu҃!5A#D#r@ H\0{Ư/,y+a\[? Ah8f:rA?pH?@ K"7 x{CO.Oa?93(pV{o@2>|qZ@=R$Jr4ŴY<4 U5Є@DP)g'=a$I&uT0sxXZsRir ٻǘތ$SH"wje džuM@2HO\Y^8K^Iȇ:}b5Xh I??0-%C`~fBL7b#oWb% s ӶcJ(r`s)&imzPvb$XWy@}ĚPU?!=F]RpC"">Z`n?f@_Ok  t% *f~ڼ yTL4tiCXSG%{VAuzdeV ژq>]5 /LݦDz?73,À40)(>$Ї_,L'pi Wg]W;pp Yhh!FX_l}LBh{{c Ykϖz"t'RX H$k-2_]pJ=>fA}Bι//L倯'Fu7(œպߝOub@@_,Sȃ7R7ao+7gG4A |HޝqUAa̳j# j~zo,4Qox?Fʝb%*(FWӍOlynDN$ 7?z"=@gO O |j QXe](%`TD#) vr$p?Z_o4~$ sضaC:*4y=ٮ|p©E4<?y+Z"@" hjs෭.|?C.6NLSf)7hqBzTS@}Q߃@% @H5HǏ[xO:̨kI)|/ $J'|u?g~ǽ{?LQt,UFޤ~gXf6I2q%dH j/o_!]ӟ4 Tw YF}_w!oI$[ix<-=P,8%_Qh myӂbMх(@|)Um'¡} zrpyKB Y1H@*xzf  .$2zDH@)Gz'u7 W `Iq(Mrx j @KP)@sFqG NU>Pu)|x}A_ .G!l6~{"? /<>h"lUqHP[{lIa!_$R[ؽ=ׯ1K޻G$x(nv~-+Glkڂ3 y.]sÅ"M~!z> a{ 㽖`BǞH-ldZ;=, -Ԩ\[I$>7t bD1 Z!n*. FBNJuo3>-k.:^}r?"4"A?& ż>O@}O^aCz>]ؒB4Q 5S"  @ B+҅hΩ|_ez?ȃxRg&g[ ɮ(sx{^v /NR'>,AQ(<~}? J&W~{eGcF^?/8̲#ޚk4Dc^{w:a~Nׂ 5pow ">0 PuV8iO`Buu=>?g?v{>ۯ7D,H[aW8 ޿0^T.% v<ό=%]?˗EoD%069  ~j-"@O4 YBf O=! i*yY E,6"\ q礻R*?>;#s^D}0߷(fMgQ@߿jv\GX:wjT :OE`J{/yA-E@P"JR2eA=!H4 `tN<>6_0j bjҢ# %DL>tgu'PvRGDeG@#{BPg&hh$;&uCb}SA@DII(?vm/m~&>fT' B(H3lSA*}$#~dׇı#{ xu 1v允:+ABžB: wbf@L .^>~-|#B ú,RaK//hрqΞB0+a}TO|Ė@3@Q}`*I_;RBNZBӃ@~@H-R+#mJk_,߿|`FAe0q *1* <ZSɮop wg|`|R'xIYڵ%~gk鰸R#p}ץ3 }}YV@^w__ rV{x幙)NJ.~R R&F_vl$WՊ$ /A- }>r' }^;R`UǏg3pt&Һe}FܛW bu(D)~Lu4N=lT Mgo/cOf}\Wr{pwJ@HGQUBdIޱM؛:,[>k-z&W197o0<*>=篶ss!?վj:@\2 :N P|OיA p|K&b-Ep^Zm}&BA-b-@J_ ݉\Pl:lfj}>B8ͮRBTHG]@"`Nz86t?ZXm$DzcU!1o?/xQ2 jn ۥcIǔKzK[,>x_lqK^F$참9wl($fHj3 ^x];g_n2KUp:q+#G^M)PZT|ɢړC8spd}xmH#Ba0/ uQ5}AޮFyC8$tWw}Ha:G7ێH7]!XӔJI ɑC6׏ ЃL[4@]9b ܽK:>̥7Ibp6 % >wJkn|%x k(x,mBm iB]o<s~B3_+:"1 Gn tPAū<Zl'գ?|i>[CKG58 Hwe9p>sPP8Qyҗ(-0+=Ñu!i"ԽooBd@7 ~ОOb~<^I}NTwr€@\1 U3L>#)t؏H( FcIǡ^@ Zjf_ɼ0Vh2џkĮ~5G u<+" xQ+}1@Uk{ `SBZd?_y=@Okd[DuHdt%Uvg !9 YjLlujv)Ua$=ŪݕmnCMϽk I<}СCv4E6mܖR)> "`Xw;6m=$69AKk ~qo}%Ǟ=\%({[ sX] Rǻy<K}… L1Gr[䬛~gʶ;C9t\@GɩU٤x @/ }Ag}oz@?b`r'ލa̙3O<;u["u ܖr >3@:^@wUj[h禷'ӯf|dχ1~zC|.n*`'+صm6vigJ<,?J_ZcҥM4͙3͘15;YKK ۷s۰͛7%BC@"Y$1*+e/ݞ/M6Kv-|Ѿu]!h >w(^|o8{T!ÓkH Pc5Z<R0e6~x6lذ`yyfVVV4hj*++]<:H 9r[nj &6EZ -ɗ=m?kM$C9V1+2Ǣ;v̎v8xm'k猿&v7C 7GVso8txL}lo Z y_d*'TzQn{?uW^a]n1ۘS*]z=渳=wrۍՔX` kjzp4w}gVuEƆquQs.sUݢnjlֆ2kZK6Tk%8 LKgR5,V9?=3]=X(3-TkcHS@رcĉҥK*k>HHF x4]y] i7V1ц֍cz}jSy/+rF$6٬_f_,Do怸}!l۸r`]r(|f- [4lFS%u=o!k'͚~f-o_`NŶ/dǭsɷewWDa>{i5Shf,{a.ؕElBVfD( `* ( nkJԩSy56jٳYmmV^SbMٖ-[K .* 4m-C;k;n]_hƤ8gZ Pŭy2dV[#۠ Hbܑϼ2|uޥگ齿*k`p[5u}hoͷsW{ݼ= ϗ* a'E kgyŷԼF@0j{ܧoӌXo-㑃_f/nLð6-yv#_xGc!TlHQ eee*3f 6mT# tj6nkjQܖRw6k;Ԏao]Xzy{؂}NV2 Z:Hj:r[[W-ay>xn!I] +Z Q[ɁEM\ ]oov/ٞELG]\RME|Xe<wI!{bu {8S@x>MoŠ6"\= At.n.^;"{EІI^UUe/))[ޭHCn: k: 4?9՟ݓs14I ph|@S&yh亪~UA.$gQQb{qT 3jWAړcO#|;Om7?o^V+qQe ,#i=pψ;l,wc=C"텡b& CıW?xvϢDVt)ߏ?liڢ㽨9l~/#kj#=],+YהeuQe1` sL!Ho(lVk5Z +@^?Rnki( `˜taJ0U$T ?Nd׎ )ZAzo=W,Xh (CC?@P?qt𵵵@"]`I{c/fc>ID 0& m92mag}5~w!?HB9 ?l2Dz. Hie|}o޷YY4SzO[ȘyCO-Mmv^b 69D0:joFAx GVX8|^?g46rH^7p@ QpCjL8Z_=|3Xw; <*6bwa3{~s` rP ϿAƇ )t ^?1 )tlr`Ƀkz WmYYG \O=#M[Ш/Rtͣ팦ݑ!O)j«{mШ@$TuZc"Z#>Z-Ol}Qw0|1OK<ɓ' swk;ttuN>w7g:آ3'.FȖ;{o{]):=PY>o'J/\luY Xcca#Kdݲ&g=@D@E`恋޿o;fb{oʣF늵OB"}<<pqLxyOѨ_Ӹ/; @De@\ʇmz{F %5A4l9ٸ[X孾\@ȣG?a9?yJT7#$wVf"8fTQ#vֹsAsͻ|ُ͜-ZT=xgcg߂XH~1yz\i˽KC"¼5{H rpeg Qލ{>4)߾Aq5A D%@REP_<𫯾ItU s{BAnT;6Dr& a(CϿQGYVݛ{q8o>P6ZP0m˂ j$2BjmkpbL `A 6[KߖuQm}bqѺtF;v:yd?:<$E`A:db{1gE ~C<|Yצ9gF>)q9 5sPL뿶'<믏2BbŵU? }wH\p@ZoGMl1]MlA`GwE?R/G`<_Pi8?}*lGF$N$CȫU Dpa4ےps/.<. G OU$fzM}}!|3<= /Ǣ~^' K\TA~ yKx@ uH@̹iG+ f*( {P:yU"!mPk:\_ "`_f.dvsb2BE/$q$Ax9ܛ"f-n$f00>Nз>a ?s>$zX "׋(Q==/V FP(OŐ*Hu@5t<\Oi ԩS{/Qu70ЧO&?ªWgvyz^Ky|0DD%샖\)oD(BN\x,6ۈ%p6t73D r - >=N '  `x+ "2D)u6^TIb'/`(FRZ(O?LiS8ȏظfk$G$!?:HM"kćC$@|KDsGFZm?_:O1mv Yte+}f6bo^ ]0a"HZ8@Ȇl#SO &A}Q<B=4 ާǐ #-"4QH'Uֺ^d?ǹKa}εr *`gک5 T|P8p'y2Kt `ZQ0: $üА:j?%laȊe\) `(EFpc@z[_%lb c׮B+'~vvfw@i 2J\[ *iH6=1 }7amJĪfL!w   %peBܖbn+5LsьGd"o5j! "azDvod~g}( ~zXzQH@K -^X %PiYVKWZ }ɳWPȶ6VddXc~55.?LQ?q f(-< Mn y1-1@ޢ/zoNyf=SHǣE@>}(|Cޯ5 ~$, q qeTx~}ci ŜElDUx}5bFXF(C?I ziAcz9`" ]zʶjܘGL-GӇb mC7'A *J>:I]D@Yi0ٸC:G׀8$UmToꉕ~ +kMZ68_>Ѐ(!R}>*@QJ%"_} 'JvH@I믙_ƶ5Tj$uE|gP ".b'- dXvnL@^ Y4GiCMżB] PO`mԮ@Ł~ nzgM . ם{]L ykrϴm jʹό m@ \X,8$7;K;J<`>uЌ"sk- :t֫k=>+0Ӄ-zut忸=h $~E?HHC:8 0@ޡ41CxEzHU:" )`l77BJlƇ)R@1HadWׂliDJ}JڊBzEߌP^/Ռvf$Օ)NɅhm4"أoa sd)m<5 O F3[-ATE=KhP}uO =xi=ȽS3F}Ncx`ʠ q?H`O4PCHd  9 *C]+|{&u!D~-0 ȅQFDG|P$"Hy"0HHo4gp;D; x}mҹh X6P0%qH75F (a *',Yei?W;d=}Θ z F ͘ >dn}1@v N_^|ηWZz@2!>N- Da:3A  0ZA\7jВ7M CaZ{K.U֭[O/_ޞ?~!:12@/W% 8Qd7B~|[Cb3X657hܧx帿|3(H QF|zr@1!`}50۪Ul*QϏUT:=7B(aeee^}V\X>W)mw畞{ݧx^T◴{dGN1D8) 8q1"@e;y}t?x}ֿY52[^Q_97i!^+M_^7gu:DG\ ABqY?Y71\|mҏťM;[ ʴ= .9"#Hm I$L+{BՒN`w : cz?SR*)?Vdih$`kE[+y2]JassELOJAc@@|7c,S-I\eP=Z,A eU!ğq хpyKIFիW@1oϜ9d&z TzމpFj`#`Ma׮`5+x__b`жzM+" JS{DWȏ^ųr=<94I=0п4Ty+-ucmXR3piEP?-0VP[ku_v}Ȑ&hDLB8]r[ҟYPj*}?EFwR) 1 7G6~'}"I!L&D< jMq i !lps: )8֯_% HϞ=;LEa!7 xR DRـU+Y!Hd`fVR& e79um#mFM Cta PgگA= e:FSWooJB@1 шD8Zk^c%2#jhZ h?ɼi-L5 _Q#$ 7SDxh},cr徙_|sܳgf@[@sEU ~1m)OR'ԩSK.u5551g{o&zjTGxF Q%U myV9F @3(%"j_okVTS_Z%OD( :VX b1u}xߛEH3L$5/#aOQn" iKFۇ a9<G6@Z rK/1`Ns @O_">65ԩS'x"(N D ":( "= >*@lC6bvc>MXfk~kEi#mQA* 0I-0uʁd$O2๱@d+؄#ԩ@3o:"2I$^d*(!`HZkUPfĚ1 qc|Lf<0@ DCiۭ*nٲ} yoy#܎tk؞[܎nelHc3Q044sFZhFOhzvP8cnʧ)U 8"NQrML\ D#K+DFH"0#> z`)$ WG>@ z=\mw̜U{ɫwC3%FQ|IUÙ~@^+ 1!PwG1W1Bh/D:K`x`c(`g4=~<.N-3Y&vȳl5lz'dYi[=뿦 k_v T04&(]ȻoiU<=7#F#g_A$yy@ӟ.4#ukx D`8pΜ"u-v lhE@5")MhSaT8~V$sy{")âUlx&i"D H%?&#ǔ0eŝ2c38YF>H`yR`_w.eaOxa Gi.ihΤcMꔴ;GkX'M6)rDQA _x_7-yhP($U@?/wDHVT`0H,y-NC{{yL9+.4o^q ? }Maɿ𲕢!zw+{SK^{93 *FVH>l:p$ @D"U:Jycp"y #"#]R-B@8Akg;Vso"% dg:0!ۛ§>={[Z~=1rE*D+ăwwt_P 0Nhfb<0ulkJ y^{{q>7"4#paA0YmdXP0C$(= W'[Z̘Y*'IձgRpxSFD@ 7R[sD("o(O:E  쓇}&Ο9$"|>"HW*vWB*H V*xˮ½&v\'}:q|ҵ lP4HFPAF"^P&8?@7m:hC_NP\RHB@)PSceӧgϾ#L@l3N;]D|A3ųOx&O=0p!@ =?nH6V~3*@޿Q_OoF DT0Tx( ?dG_YYZHY v&CoZsOy8 *hwlɐhmmT%U "fih?Н+&붽q@ˢ*i^*Q;vĘ7iohP1G"=TESy]qP&A2xG &ƒ c&z We G}hy@*71b%H}v)zt(LUt0?UUUǎ[Xn#Yr|s[À}>&?T+@[8)^>@ZZBD]H,vk[i:}d&>=$ttJy{^:M__gތZ(paq5ϙSS%y #} lpKOfGwq'ıJ^O1GA$uŅ]ŁpZPȇ=F^ >(K?C>)(-N/?WW4'+ؑ&c`I ܹs^<[9#o 쯙)\gvF;4T~`~YN4U RFs@ml(R( Dqb"zFN~lehcg U'"S'G+IvZ/-= JǎΚi7:^ϻY`m_do^>jq7oh-V69ټEA v5c A S"TA?-@R@$ZUCZvO)O*X@aL&0}޺ʧJ~3zFU<:njr\`]x9=_??SAO1/ `gQŀH~E#@nZ8ح7סoͼ_')]TGQ/P_D\]ڶ0s \ @s=\d# q.Ja7 'QQ"He9dDQ?աm $jP'{X%c$$1cnCߴi}{kKdOڇ?GDps\"/#Ր}V6hJ eZaHuA!Tإ_TPy(,% 4~ @ H T *a{T>FM]Fp?@VS_Q Dȥe0f_ oh`ۜVgZ'klpG<ZoNO^k0jgz>m ;+ cג$*+:~VBt3iה{(("FnZ7s&"/]ұf6I>!ȒSGfV-2#?jx91"Æ4 Iu#SB~|ʞV;3NC/?/LwND0rJ·6mUP6lXpС\ 7NUѣGg>%U{es^ZЧ H ?:h0(ʑof_lUSs.m`֭` pfN=\Nc! |)a ;Zz8@֍KOM ؾdLC,@A&=QޞO|7EP~(yj` id {K>v>7H%f?:uVvUwǛcG Ԉng){P"ɴJ$΋wApBE/lԾzM _|N>G s:h?gy݁Oc?4!(%R%|u|%ug{A T ?7} Hf@BG?vaS!'8 $Aq3-IĠM j f Y 4+`\7߁)%/m;TzpRC(#`-i 5ܜױ+Ս+Yilfk4P@f.L:ws i* Däc ޞLUFɀiUt &յYҟ ,p9w*Vǐ( 0Ol(DCq)bF?3_X?2/T%`,>/ *~G Hk-? @Ap{՘xZa~x=z`GTǂ=.?y1yLٳg/\%3,~pѕuC&M}2BQҾuJ~9d,U0Fgm;[Zj._J|>PNj 7;TBQ*b#u@Q Q28V!__H}RAF R!WQ삒^p{٤ID`%%BüHvABnAW,^^!PLZ@ǘZ, z"\m8'|=yH -Ǐ >ZN/%K^SW;{$osM ~J:r]ְo:}t@g_hSp?$"jd:B{sk]Mq~[\%9lZXp3ԍ0T+Wޮ)Xmj=33ά"%M`^n]w_NꫪѲ2ZٻDA*D^dC#|T?q7T 5E(u 3bkV:L@ޏ"R uk*j&$ 0!;(nWN8o:w}L!r 62Y#'O{ <-{p3j`T =w;T*RqH|þ~4o>ϙ=Ys\?yJLG?:%vH`d  =90ZGjBXA~2to gY엏n E5]L czP RF7Z}#T^1?隣:(`{VMVdab@2>*>Lhbf=„DY}ʐض*4 P>['6ii A"@W,]7lŊ;@͚lƌq*|,6A*Te'͞ 1w= ᑾk Ep*ڇݓY[E, A#;~^0־_J?rwW;nҺeP.ll6E~}wTǫfy_@ͯ|}Q|$Tֈ*"aãjN=:1c VY,uzvM@{!]p`Wf@r`,pŊes?*@΁99O '<JU@@[~z9Je#!׏xa޲`{8\e~46y#Ge@^~=tYԖ13l:?O!/oޖ7ZMsdG?J^"( m!, f̘i=j`#f0D;l_ {`vnVȪK#`Sum5 QH|]UŵꂟjC ~Ы&@T[0 \W_:Q-] T-!{Qp!0h_%'ܼUL $w_EFf/CGۇk|1nFV Ogcm폗1<}?+V@©}}TG&/Iзy ;u!?}t@_%ڙ 6O rRYǚK=QNUTj%vfA &S]lu) E\?l\r5Z^M7OmT.Jx,̚yu$0TIp^gIpeOIŘs$nInIﲫ%L9%\xVR–tPZetX$ܨV<E{^Ȅأ=[ځ*u-W̻z|j֯yVm[a*jK Xծjh''_BE5D|eY% qNd0Vi {Qc6L̈́?( l{~W ˆ8Zڽ4 4RgV=uuČKشcs YޡE\9scWVjǾp)s )p}%^" qy*^NT~bXgؔNl%Aty QugjFFW­5{௎} \@]MMMz^W<}Yd"!1c/*{A>iO9%@B< `|}${A0ڇg0[!B}` `Vxn D{>'x NʊrNʂpDUp-Zd?({яz% WWqFM _-so(nKx2|M," pMiͰTSFac%@z@Jwd 'Ϧ knJ +Y%lUwƩY52t$ "懰 Ӧ+!  &x"D FQ|_wspΙN!{p`FôQ;m9P \YYv~mCO@}?۷J<r|w|d q\R0p2܌3^d͙C<9DC_zQ"Ci'".~eong-?WɂW~M QzgE‹%bY?/! Z疳g[D wND!Y`jf~uDcb?qm`P%I\d:?<5g2co<h:&@Do.KJJ8h%j쇩%~F2zdʱ޶m`'Q̞=5T&=;"9"' ϹWgj@_%3mX}@,ߔc TA"V %w{bZ'zfJ՛I~?zaڻ%% @/4HY;)ߙ6dDe=ރ$^c6knpN"Ke0,#SDZ%Xğ/wY}  gr?Ap,2mDɽ e,rQ]b/|,h@mB@P[8 /T볥.Ug`&lgyuxZ.k*gw|@G%{gyDt}w~>΅ϧ@f*QjꛑDiT:7 2s5Q09 ɀ1Qzd y8^.9So~|ٯsRTFOh(vo(2i:l<] g"@A` `zB>s}ŞB|t_4m-tO-e6Y[V.Y_})>1 *8aF,c `Bi 2QIJFlեlL̤zv|>zEρ}5uV72 #{z Y{= pˣwF Y葀X>V˓Ɍ(P~UXY?e]sqdFsacM6[T1,aG*zE?>>T1(B@"t.]˗/̝HZPA7E曽RZ 8. &aoR+C̿$XvT}ժ<'aBM`՛qZF']~0%Cp&'cڊLA_3* /7أ["b:_<: `1?'aٲCe# &= 4T'0%m2vh2y~=WCq`-_my c?K{6,##@?'T.5BK%KwykƌS?'VO8g@> ȟ6CLL}hljaT'@AVjIFA5VH#!?M=.A܇l8B%ļ(Ua2x>~ PwQ;fr@< D!x ğ:"8:Ϟ,9i yLEocnD `J@ܱly){z-;fikr\s׏*2~/;#}(DR=UP!*nLw.TP CFǮ2O`j]:Q1T7?B V :YVPGӖ3g|v[o{~L p3OOz@^A,GmZ {"rr-CKل3{McZq߹=wn7Ί룄/_y~:y.<nUKD&j⤀BrYsf'Og pM&C77ADIAP% ƄuݳT =G?'ՇVqllL(6b4ŕ}G&Fg+ GCB 1(ٿ~H!:(3ضN4b660 3QaT]E= [<}|}jV`o*,yU:ˀ2#~&l &$ \Gf-ޮ9Y!C XoR:.l҂i>:u1k21pt>B\[5Thׅއ@ZA>81?A|ޜ? "}i@dfwN?ʏ`-҉p{!UDd,I"`|;Vb`ҥAd +Ȓ ^/-- Vmo][]?o.oٞ(ӫV!A`, :e,7 83'z C& [.˙hK)ݑqSwɒE{JJ#nz-=n_ j&9 ~)+ )@N?F-K͍Ǐ0mӽ8?"~8u䣵ײkr ֭^]r-vSxᮘx-eXWfN$$@Ѱ9>~r,Pb*hQֿrFw?`T qzXrb4y2 )h $93\Xxd@(&vD~'H,a{iwg.[>k:%lƉeuSED?aT2v?ed,WWWs7;vl8{,0qDH10 HX^յ?Twyd L-|hi+_E"_f`$@ހhwy?,|=%Q=V%>?4?ݿd =bc4% /3gx*C@A|"~ebsĀUNX~cu$KkrY-WJ/ae%Yc˘|~)[ (#_{ΠC O& $Kh7>ӞM1nh-i[dHPt? 0nL#~!^@C vk#=uI12MY e C*  _OHH5dd5`ٲIɮS3{tOkVot}  U5BCdǣ)U*]ĪVf9lUH?GP4CII%>2O] ik(d? .7i@%$8+rxA9FmT O TU[bXk,JO$pkW,DϵzI@wgPΖK8kThĖЈ-S2';~1 ~ve`sC_u@r|:'7=^6c#_?{ ll{MvD_.X8k#~r@uh@:`( ((V^/os~q-X,ĖL'i@}$[re9sobq.Vl۵nbϭ$Sd|h[,>@2VL>B S@ xe8ʆh$ ~ (H]# <\vf 6h}׎^{)]7ducnf@ܽ"Az{}0Ef "U[فڰx`M֔5@a! #UA>\E?hؐ\4UݸRe{.oH'Я? ͅP@=)@QyID$x .ON0Ľ=ٻ\{L`JpslME@c}K¿.NĬihabVdeծZ -t)A>\':FhI:81Fm+X`lgK-_'KQQ Ձ35bQ,nL}+Czn˔@&nԷۤ(U5\#=i,?r۲a V@]6{o]j6X*-o1&4' !… u`K,}ok?Ͼ?ܽ Ԩ5hqu*m<;O~Kз0g5A7% khh=vFX 6k\=lFGD@&E@'vض)YJ|k#kUeTG3VqDEV'"mSl%*\9ΐԪ%;eI)n(4G? \(O+P/}p TT&hԐ/ н=7O7m" ۧ|O]i ͫ}gu6Aׁw4͉(} ' C# v^,JL}ͫ) e>w+~[Vϻȍ,3}rQ{ ?O79} >]@R 7p 4(^c;YǙ5WVD  )Z qy>:v̰|݉/UД{=FQT|zE+UOV"@A pa$k0?J[U :AGQ=ǖ6MGivɮz 4 2!O`b;X [_@qg8$q`mm]X@>ZUE0 npWжic::(FD H,RM?0 vX5Id3 )=9@dQH|DKM#yeE.۲ϣ4Q!pǙeQ "wNd*^*@\Pafoht/ꊟe Fj|zd"a?m&Q x祬 P_dO"D+{8nu]zAv{ce21St$ "@kxA*H=$L(@A&ƑZi GU`Ūlb>lgGIĒR_7ސqzsܿYx4 _A@P ?< |;+`fH/m'@yg5?mVdj@ }!E2a*P·8H6.BD4 †DVNNS@rLƎzޤpF+ 6D<>vHL (4a[׎2m+}WFHsq(G/,-cU翁K?vUTc_7|;ȫW[X7 Dƭ0ќ,i-Dw mTq_"D *\$(ӿ\vA֬`VhfJ !{Z*"@Ǡ*`lqTRߝz`xZK(@RHC6@Ȩh-o+ζGS SOzEp2,>9YuUgDYU/7_D$/LsfB9OuZٹ/^^(È(W@m@ G&$o/w&\&xNb*bl8;T1L! ?`OzG|v j|Ym$6=fP&k"C&$@Ӿtݳ=:OaZصu].+ʵF[ZDxzz[ngF tcuT >?#w8( !ޓ/a5b9@tr@^<;?LЯkuڒ,?4]2@uPE.> ب-ڵqKh`Ӣ^t:0`k.(;l*So۰/ K.՚ /j_7Z,h3{UdzD G]l `jdӒ<>5E-hdDFDB=EU rDE4nޖn͔ٿ/1@A 4"n!ƴ6MO}g }9+6br%uH];Y"7i{#`{w%P!n5d# @ J@<`3#TMK޻]f@qA0@`jۮYp{8H'uO~/f؝ 1 @y=UfK]_7[fiSzB@,yH-8Iq@2xJz&CGu ĉz S|o  VWVqy&-#3N#NhFB9f=qyf$»- -:_sL.c[-Qл׀wwO! u:ԋ 'kjwVh)frP508!Hʊz~clk6;Ezz׾pQoPe'lD-/pQĖZ*ԱTjdp B9@rSb0+p?FbDdM>gׁA!pÉpf?g @5j^SS@L%v488"\*`ofƲ-9I8(oa]u:D_g۵mewzu1v 0e -6j8SP5> DGd׳ 2Om$"@^wPǿ״i.}˟ ty%#*LY\3"+7ml?qJ"@m $ԏ VF@-2U3"uPߘx2GrLF{=N&D0@!0hB= X?ZH=)!{% ?OLȒk>D"5bQL!@FxK p&8[*@xĈ誱݇[#Ci@*3%KQ&:O`F>^Y?H&ۏEXYsl5AB7n=v}f*RV {E @_/@7߄sUTpo6" T= jg uS"5[ӫR'4o@v*ΣR< ,kz׾SgB)! @HP^&bI]nxfc4:AV ]h0 /_v0jā 1zov P߽Tҹ@>ۿ[_?ܼ?W^'G|ةC _h8 1&:/<ïD|ͯ(NGBIZ pfeҿ[˖*i.}by\O'@+߯x4B.%5jTzf?۷ IAc纚,yfK;Ұ+#rnj_򗈨Ɵ^T8?}7P }7dɀLtv(`( DOe?xӻ1=婀w6ChSfB@1gor ?+(ÞSHTgF(8Siގy|\ùIl(b?G[Os󪶶VIWO,X^^nD%%%qe/ ְ661Tjo6 =byfB??`rfսjfg q~;~_W@_󊁪S0%捈@$F ,y6ͤ@L ,jt7n{#\ {.o4%.[6X$6 XХX[(9F`TA{ŊybȽRV/P̯?H{{j: .'0 ݻ#F@5N;_}UY_yUOw<=+׎ꑩ z$A"r,0cPEoog>=l .ҁ@uVzATA"׾@bDˁT04<D?*^W/6HDB>U-dJ'!D7۷m qg+** ݙ2DOP4)$nu5v>c ?do#<//D+dTI I^GW08Wpސ|^/$)x^t ϔww!p7:6'@ {0!HK@"Ck6FaB$"S@e{:@V^$/AhI T@d# {ZZ#թ}"`M>]=WS{lҤI<˗/gX(I#Sm|I'_xQ~3gD;~d?QW_#=~&U!sY@KA@|'}}ˀ,D|W}~1sD-Jb}?N[,SA# 0D{v+@o/ZW_*5+А'șz}P@9>UyyiHD޵Ō}Kz׆@0Jϭ5ltr'!Grj:`=`3~<&hwM@oDl [`#pQX<m=UU(@kjjb |<[/8TbW%_G>N1%8PTUU'ȚCP,c7BP_DR! JO>k/^~1@f8T/ɵQ%ЩuܺW\o+i]@ϵzk N?o ā@mq')\'"Nމ- 28 J^>7O. Wx맆#2]g$?5+FPyޞzݮy\'P @ZkHYii)s\ڨ U蕪^۷odq H<[JDYkZczٿwk8؏HP裏гcX-@^xc1 l_$wB{"yi(+5%F(Nff tr،O!#KeXPoA>|=uȋ=FW_ b@U*8Wle bsf! Uzَj}@vf_ ~]]!r#dP]Wd`OkrP_rl {cQ2{S>?q{En5 9qSG{,ǽV Uk >zٸS=P\ԶXVn(viwm/y J"PJ`z=*"Zqt/3 +`^DVabQu <A{X1, @R~@0pWe&;7B9^GGG(> h ?~uwwj?>^T,*Y~P-Q\C91r9c~C;ﰫWr_FT%M5{}I3@[zyp2;[Bv8%BpumZтJvB\Z$#+`׏?3@& NO|gFPzGر697L^mM6qNN Xd"Q (UB IQ2 6@n%VUG'kooA/ ՛WgeVnD_U~mFO?Q~ҥKϹq;y$G = "©ۚJ,l2b'Gɢi N\:{6-zˀ(CVh8'3#t,[[@ڝ;wV^[x3%'u}QiӦ1*hѢVϸ(𛛛gG d nc5%FORs~2y*~) fN5~I S|wdF?:=*Jb{y^zҸ_$Rvrs %=F`޽{+d'@*h!h#ԏJF\{p]5S*? nMdPB?\ [9v,0256.|u5w_臯FOmkc 4% 6qfߥe5"r@@#]``wG}dbiY;tׯ>m+tq;::By *茤IENDB`tiled-qt-0.9.1/src/plugins/replicaisland/lab.png000066400000000000000000002004561217502731700216060ustar00rootroot00000000000000PNG  IHDRŐgtEXtSoftwareAdobe ImageReadyqe<IDATxiWy:u8?#u-u?.!50\B.8 @l6ƖlY-njukV[-vd!1Ii 8|w=Kl^}v-dU~ߚaE1K._\Mo:_Ncy[Rmo+|w^0ſ~۷o/nZ/+,G}ػwo?^___nݺzᇋ 6a<߳gIp CB3A?'ikΝfH]vGrܳXp⾻.-6Z_v~}ktp`1mG/It-,u=Ь7Q9ϝ;x|Aַ5 n}Graa`l Ł{,o|2cl޼9xM|˖-} 6` /B;lB7m׾ ꩧo(կ~5~>p7߁+_uۿPjZs1.\xxla5{hW808=z4g7o(?,3Am۶?e߽kwkz=Xmb}׺E}꧟CVWt_f϶0?箻ꪫ̗O~EMȼ?Os;any_^+?7M2/3d!$ŦO~RGD2`lFڤ~ eb?S؈ 7M6.AGf&Ά]_DUa,<3Yk:3ڷco#2AC|V@:1C/~?Fk?BVg"dC`$پ6XyW!}WgzJqhߡbuEK2;@q$ûe&im_fkL8ω_+o;r, -)@>F25=:f͝Gs{WqNiuӱPo @=3FzdD,X`Nl2id_ K~pwV`0o$PO~zI|?lx66p.`/KVfsHwhO][h5g?Hʧ N?pq 9e~ƥScc(yM2}oo}<ڤ )?q쉓 }?/53jiW_kfjq׶M5g|jl stuϝ/TZtB$>w 7NVi $Q*Ω!) L &SDg(#wsRWuYsz@\?L8U]"lM\\{i+ 5J!N|? #g!`8D<q{>s j=Z>kW>?7Ylݸ%O\ ܴ=#mpB^xE7]glhpYSsxvؑ;kv۶l϶67_K.Ilt6H~,4&M*q A:52jd¦JԍP5ac)yQ_~cC0 i?^#w5rBQi[CD0́gg!c}okdWwמbp`}Y>hĴe3ZtU y}&@{@v5cL—FI?cp g}8kc-u~ڒ>0ȓ5~MO31Ŗ/kwӯ"0:a<~w߿gD7c>et7SEO_3_lhCL6&9W|ʏ2ˮhYmOR<'B†a9x\5/1Zmلl|W+;|)~" +bUU; 6mjJn~0};g>/ @xZ;£LLO.{"2[C##uL8O}~ִ-e>#\lٱ2 ! ac`3BYўꞶ;/FGMxtsMu:o:98ȸ?kqЖ-  &ykܙB@rFR- -OQPO`%)é_8~箂 xh+t8[]FP"e*!>Q= )s\q߲➛.fjӥż 1Q9_f7n&"ub&?pN?vl:qIz D}cyM㜛sYcֲ }OgkhW^ 5}q#$))T5j^j`탃>;"$PRpKUdG i ,&-0=zVB0=h( 3]vjOe5u'Ue'?k-$k$\y@aZT0a~I¤s 0=Yw4 )}@Ӗw5_Bޡ.XuF8 }B@{UXU2֪;dPfiwqw3`l'2 qO}[rKaoQr26U*n`\ g؃y"0>Dl&굑mŞ۲o?S&cAK[pYr.WmE8O{U4()<|J7;ᴗ`x!N|g`+B7v;l\ {]*WTϜv-ljWo8IsYfLlO$"[-B&sCXq? p0LPm E, oų3t;\nUPNXLbܡXD?ר}`FS7jo 1:Ҧ4'ِ$bκ< I"l紮RՐ0Y.g3Xs὜>߬39]-w?[~x9tZٕ0Qiݦ`"=\qv77&y׿dHfe /mM ws@6[zb"7kxWf~E0:>ڧ~և2}4/3D-bl?g})>#:Ѻ,رPnۓӳzmnLַܿܟzh]■wN'[!\+_̻UO_J _-O`'ZM '8"lXx631ſ9P֨ē8L f~}' %|ׯg<ݫ*|g.m D[.E'n0nP:Ya g# ̨̦E)oǿJ=֭9i~?~_X\`_!֟S:s p_@C^ӰkW)._;`~xc7"0XeȾZ&bdfտDj_t?j?Wڿ̓{huG ?V{$_ѹV?8p6f&fS35ZC6t{5dn_Ŀ2)R͌;iv&6sIߠ T _ T W]*7CͣM7ʓrBac +3nM3>=3* ۰~CVႀ^ Zc-\($B 9+A27~0%pN`Hl`?(u_m GAeU_@γ_0_y+YaN }QGQ Ͽ{T#[#jpܹm$3x.##11n* 3O/?GheʶœOGsOP?k}Kځ̆¾aQ$zDx3#h&?M8Yy_FoµgN]bMoS_tŒEKN %>dg6әZ[ſ>"]*0ptT,iʧaaj`s*!ҕ;tuw}mw[$I%IqF.g֭JfHx@4SOBE6K}،Vm S?fDa#<>D?1(}>P_ꥫV,Z1Yxpu:gמw>v`}$b\4}{S}xk"8w;gkQ7% JǢU/S@oXԷ$rh3EdfDʗ]Т&E2ƙqp8LI4> ߏq!_;_r)nlE:X23'Huך3hP]tqmnzܲeJVͲ1 p}7@8?39AO4?S9bT~=c쾎kn)fcM/Icl$GOg1~p 8sY{?4Ki *,ܙ?N!6y>Ou $ʼn Ry<5R! \ާ^7sY`W;;:/:gkK?R㿕vVQ)CgWJ #;FNb(w~1_;0cpO UpX Zdg~&W+  mkTM톪{6wrTpWL^O16|yWfc£Dzޖu5#ghPNj#d_w9ᡲybcͽ$ ϩ`l44zYڔq |2&]pe*4 u?X_70ٵPq 8 :_`=}t7S̙ٱy8Pe,acaG88w^ X jg"P !}a`p U3+s DjS.۹v/4O @?25AheF &*Lb\ΧhH9< g3:Bϩ2۾ *}*X &|&#jpP~ؓ 1Mhu$2zUt=N؅.e_QȊg~oό'An}嫳`ƵC4?-b"l^|͊z?\mh†8p`FTxeRnw#;}{-~e*H|Q}85 c}ډ?p& p'O:]Rǭ'9pUIyկ~Ur>xҬ>cţ? wصxgx*ZHNk8xzbЮz`kz `,'k< /9v`V{3imƜQE?LTWB쑸o&AnҮUNqcE2y=P}7h3'5e+_qo:ןG:07CZ }i |?@[ (SgeU *|&~-_1H_$~~`r2x>ڗU# ?Pϊ\`ͅ yׯ߽jx؜=9~ޥR5) ɼnO?t>yA| XN0=d;3 ^ />O0l_6 ֠f\֖ʹVE?XWXE?g¸C׋:?,:Z<{-~/ }Og_i |Ŵ-@gV* Еڷ\aa~2 hh t6iU>urr>h ~y: Ø5,(ɡogONp| 3eGoM%}O>ǒ{DsA4q;&ơ]ֵ\jԏ n+GN!9Aȱ`^k{^#= _1N1ϧE8gu94Eod ˥qNln2=~WP` ԥpﺸt~p/ÈQzoXg>y8ƅ9\ %O Pf_ ˚lϿ v'Gm:]m VU{1-ͧnRh?)Fx']n /BDWPwec62m#MJ6 Dxn@E)u7TwiizxGa"}ޓW{«ڧ UÃ'ԇz-_.sNHk3JaXCa@:c?bnrȆ8kC9f:FepRoY(d{!+L1h?lEk\qo)cE26sf̡&6,~61oNa.ywLZ;?;e ?o΋/Xo<ت|Z["aU像 *|d1<;m `ó1~ 232H?0ڞFCޛ|N '.dKl$,]0C2 fQ%@X`N{648ү>p> Яl1 ~٦~^G#ys90[(L]E9A`ԯ93z @_{UV| ?7=b6T}rC4fZ|O{v^#PFEp/:YcY|<' 'O}9KIB _mn;C# &ѯO3MKFּ\!1V }*q6?;?M<c4nBĿ!ߜkUsHR7OUО.D r,'L3<=t{˗WwU"ZHWKtO0v. }cMy >0r~ûicQigO#{7zVz;py>m|` \izY1:~-1tɲs]mŢe5`zV'8ƵG?m1F;ɷLx o{ Mҿ~Szy:,*֮^[3Tj_n]eۛA%v 3~`hD}!v\7uζ@1@_70!D4?\q\uj@GL>_di+µRDAq JYP=OJb1@ |JsB${;m+ ĤKup6{s{K|?Rai?u[}k>Xz|17\rIqe[V>Z,|,|{۸MղPkܟ/}C_r饗NJko/޶pxSŻ/6g… |3ysG {a5ÔS7tO0K]zW^C拹z;!U/Q=t闏U~w꾖ƏWL Uзées8ڐc1Z:/ }?0=d<^}Q>qU:sT}y;*sE>M$PM+rqT99&igF ̥W0T>};ٞ 8nLl~&qO־)]/CD=F5=hdp$\^h Xٲ7`ϱ $y6&268o&??)>яV b>xl 0mp y7x n^󌼵= bzH6YCpx+߁е3\;k{7ϙRњ&g ܸ148ccSA 7rr/CAC'^8!R.S:1 [e`bZE-3H*GMrDeCH@oh |qKcJֿ=dUMTDk WVa>3L  1 F30E$|΀0Pј_|?և`O !h=<o≪]Vo}flp#[cpSW2Ac^nKYkPCD;0z7Znd= O}X?CǢe7tS3;}}Uk1?ٻ9 ؓyO롆#}Y{{2J24k|_+3c=|VG72sXCc7˚4<vyFޚ8 X?8=k2=@;D5bZ4<(~lRAk$_| FaڷL3WGr(Ew/,>wk6A#`C/׆1+9ND&jӖE#ϪDVW18s Z1qq-O_)ޣ{FKoL[mPWn7'sbi!8DLk=:|ߙ0d^7\f4{O@g6Y_7~i×;̧C_< &|O$=[ ȿOMTG,3|Ɯ#.s~h5\Ǎ8*Д]=9!FRjHRAbEhM V6tu/*F7* sf͚LXK8Wn5GWw~tSorLK`\-#c5jNXvq4]>c_OnFi$?LkX'mF7?⅐[&.L5Z c/ Gkx=ތ@ߘc \:^3'Eb 'į~g#=: ^&/znQo7T TAr|ͺlEVAN .P}̘_m` ,d{w.{7գz"@ /3ﴡ=gƧիWb192[SآvB沊+PTx4%zYЫ̞;0J"^=D'>qYא)"lt)EFSZà,YrJ@{bߪV^m~O"FL>2B<ӞnJ@`'\sQp|&JS#1?EY<*GVGZT|ľ(sz=OX`N(xĪ|j:拦惎pbGsZ.{$^߾>]%i.\7驆߀iǻj:ZN?+5 e :ឺGKy"lP0 Q 1@!Jn}ׯ9,cWj>bw~#o|n@[6_t'.Y&w+rhi!ܶeo綾 39yET333M}'̹!RX*ѿ|땜;U˺N -5s{́ _Z!k/ƠKslL .=bwW׭܌'?^coJ}!02oT] ̄@\?4b ȁ^ h[-9;lx;nUu('~0`O0FǘZ gxw0buD5J2174swJ>h%^Q9yw=AĘHڀЮ gxߘ\ga 0^t.ZYxV{\ g.`FB 7wVHu\3UbȮ~wPW+_x>R7m,w[|[پxEfz|tm+eoGK^ Αyd;cؽݰZۿ̏~O#R'L#t-gd L7%>tG`l\MO=\IgXWiw-Wxo}咕5O~;<50gj|1oрz<_ſ[Փ19LIkIΦ~#0Z#A8jo1BlrONZ{ 7ȣ}ZboZ}3.|}н.6s%uRRQ,e 4oʧ}>(Cf~> s{_ c1djfkhG:3Sſov[ :ׯ^W;8ֿ?k7{SrO 5dٟHN(Y_UkҌ:7^!y8,}Ec|J'##Fm}[Iؿo&{w'CtGK{0{>k0N#ۆAZ_wC"ٹU(b_믟2VNOHtt#+ /l)P?O'Ef* ׳n[zo4'2eZ! ښ?7t{S kP]oXpf ?F:`\jPJx́ s/]pY+ׯnsʼnC8 W[D&Q*?\Jcj~ܑSpqbν`bkч{EM׿nOQKp޻Fch63hhJ6Oj28ƸQ^U} ]/{/1gp#h&I{^+'31nHW~&"'bEW,]wA`h"s TZ`5 ` ڀqauE'p~p V=}= z@bj}>s}=P|F 靂"}7sg­F⵭BW^+)i4:6=x9khbA>_}Uu**QD9.@ZU4V M7jr>wopʧ 4tG[ǻo-55S?cJu?N?!]5z⫿x/_<狁֊I~ɴQ>VFr>|bܽ'3}Ojh8z5s`{D??,)|G8}DgL%njt{cmp1nj6[O]2m^ Y+##xP629 TWw|~^|_ȩ)N'}90>a:ۯX׽7M}an::}7-W_+Ŏ+xG+'2soU"9_GM~y}@Ɯy |+N%=)y)|7WeN7L:gg Ğa}g{gL]AC9 xx9T@. o_+h&l+UtKgggQ@"s$MAH*}'B5x^{E7|؊+'w YOJU%G2?ZGI7Ӹ 2?|`k?U<˿|x/[~zq'|7ު`U׮a߱e~@cCr4{ݹ8Wu;`*Ҷ0#p/]b^TQpڪ`9.'&VO2Of.?\3V/}gAl}m?F8'=m$$ ^Up G7cW3 ϘO2<|;I'i,?Q|bg+_):GsoUH} G9lsdgs=xxɧ;)Ga0|NPC}4XS8Os֯߼g}k%cΞNKF=eT7؟Up`?m /㟞9$5kPn`";>U}Έ2F:o$q@OxֿG?O2<[nymŊWD~wd뮻/~/αz.Wԥg B ƅa}{6X*p_Y/ј\eY88 ^k?pnRm} pC;ia I̗ͤI߿~F&]m‚[&߁3 Йo/hGk@* 9A N"ϩaxp2&S" ACt x$zW,_6Yߧι߉2Fp783Ʃ;_eӯ߄5qg M "~ٖxƺFc#3. F+)oߚ˫6_n.?u>d EX|yO<1?96_>}_Lxb?>HG3 Gcbce3p͠MyBO)`?^gR]P bz[-n ^/_妠U4-NB*Nպc clQL!џ~1Tї}:%}NGKcF>WoAտG'\q ~0*Lj1yG/؈lkǮLd`ǭG[FInHuU}_C'0Lu7Y|p`˸}^ = ^@d^U~w)xo !۰(o{Lb:f(X̜y "x(. }sbp`sѻYεuT?7^?vsr5苆_-Z4!>/%!8k8¨볟… c˸6Ǹ膫F.c\><*.@G,rh)oJA#ҰDj#QN4SbQp8*3Q{"ǜP9ֽ끀bضBr-B1Q>Si9 6$ҵ6pbpg>ߔz,ꁒ&qݛ?}=6Jlxda_ᮯ/.T5Ox6?m|gZ s^,o‚ >G?m^? ރa]gzw(:@Sbx_hhUp׫?z c||7!FRS I΁gbyFh:Fܙ`,c>w&p*豨G;Fa/9|y|UgZؾm{/=)~e'=]0^gR(sg E( CHYѯ ^+t7?"0VwڿM o~YM.+75[+:px_qd_-ho?Ld1)χ> .k|uQbcG>SKpb޼y2 YOax tNT]~T׍g)3 FnO$AA9 QĦX> }gx߼׬N8O%J9=0;gXX<=} Ϫ|]thr>xgocXC<8=P/_}~vYX IXj3g֕@6JL#h2}[ aq)jhw Y*!k*?cdN¿rmV}R!h 38 HJQ3`'jRߧ,/1w%8яLOͧmlqOo!"Om>m?Eur~r>x-Ͻ=,b>]7~&vp߰j]ϙ:3vIN-s֯_[eT;5շ߁eӯ$. 3? X ޑ^z-K2V&É"\!$. <0=EF/7ImS^A]ݤw?Y>m5 m/M޶57"dыB/ 2>ekd!~vG+,E-[r07a~l֡z5˺ǶtmL 쿲MU:g-2g~'+N ;iz~ zpD/ $Tv#mNX:2~MOYZ!ډ5%P1W!tNQCF6t]W}+vAQ7߈?1w]|;ڟO>;}Eb??yQݟ=B{  Xx~W3GjؾZ 13bO{^g0vԞ81Sl2T_|q1.̛% !s#{ƫ AB`}_;^gV|慙iR0.J1#WJ]ņl ^;"?#poL;(H\.02c`Xb}uoe'YsKzo&yKaHLRΧFZUkuVG&Sʞ'BbQKB͞]3/o ٗ9>#Qwsՙue,ɻgZ-oyK.| !3Ia17 tM%\Pg|zWc#\cS6b ohBe]/ho 7w}JTdKk?𾞾)at aښg _~wT.鿋!օN$?" 8C aR0`|q1( s$[g_2~ro|c>r)K|)[ЩR·Nn{?~&-3o>Lt3xL[a"gSL a\͘]zWq饗̙d1QHԳB֓= _”^ڃV%OxČ5fnvI!Seu!xǨ܌Va a|?zLRbOӾjiswTrֿf@/ZsZrg  In35F@P r !H Ȁ K & SŽbcs|.N%~o~S} t POaF^32ҳ{>Txmj&x}AACpܹsOb_~yqm EKPd~ +{&FFqgC`̽,QHdz-F(@5r2`CD4\|3)E]9Wn~ :kI_؛m`DQﶶ}ƀp4yв}s@;-g`:H=C"m3Wt;/4> Q׈Olp!ktyb3Ռ~%|O%5E?c:`?UFx-{744 mq9; ѦRmP&N3x:8| !+D3|0TBdЍ m!T {:L):O O57x0>pn喺Vt-D1(Q37xC+1HldGxl&s de M]lU\?O"FX}^"G[+F ٯ (\Qt3a_`ǀ$u rcj@;[=!mɧ~lp*l*hP>隞% ҡmtm3n( 4eHeYpo 722SD6`dSg-DRY}9PTnb y9 Pq_^;'8IB൭%L9}GDY ֑RhJ+4< Q#hK16t?6Z=@1|0cCR;~kFH] 5OUǫEߺς `«to_ձiwL8zgyyWZ\@7c@:崡64ُFtw+)"' Oѯ|liE/^:DkaP`,dԹ́S2;`ߍD5\.aQu\GejBe פ~a~Ι"ߌ)̟%Kd) P1LS1rj|X_`| 潗a4]bEHP@3/h'د{pUp7&C@0,`mժUτ@tOgap|‡3>0\&}?[?Z$e˖͟?FgNbPm 6O9.pA}DȘ^`{9„zWM?|<óQ|taMnG\ٴ Tj%0Y>y` !0lxwox"µؾ1!KХʹPq7#F{3hҳss~aM0) eB = ~1~n6<Z .&~ +C K Bhk!{MA?>hFߒ`̫4d]1~c˶I{O`ͲO^=w܎QFż]{/zT[ D~\{t+Ҵ.|_1p?Y>jى?, t}8V崏o>uAfZ?@#揻#8>;-0vDEUq/&`ƾG`2_X6X_OFk!= ͸[[| )7!ÔԼK09o mVH[h#+?yK3l] c/Rvý㿮ŝcO~ZsP=i9b`}oUח  y 2DF "(40DvSg /~6e0IȘi/~  'Nrp)Bk'7:]g HA٫282X( J9^QqQx2abXIcƔөNtIԹ[uk޵ֻnݻϼ4we"Ze_il(o@8/X *M6!1Ct8 8w}m47)"@;_1SO-Y9__/sgh Pl0ah2ESޫAsWhO; ̾N`+6-l_e;c[~cLpZ9'|޲ AP`"M>l{W̿7/l̔*`咧,("ZrpCm1[dܫίxxẃ;#G9wV b'ăg}!9³tvyWn-A=jl!yoy^ZgdA҈`奎D1F+YZ!`># CNy:;~`?m F* $Ժ**v#LTM${罳y`8(qOaj|j*\V5|3W@Nmvg5D)e "p #J?ǹM46p#\+wEʣGNIwP;n@/MF7 rȳD Z qu|iWi.۠ n]@"v.kXpne-%7OL&nhO S備s:ڙg"dk (,aR \J/$j=qos}>o'. xIs͗0\y˗{LtDp>R*u  (ԮmgzT\k Kqwkr+$㬦\+;Ld GN]ZKKzQ؟rGq ^Q>+1Y?5cs-W owg{˩AL^v]q{$aڴiu,<\ YT3@{$L6v1 O3⻙`Zۗ4 O[&0&\er Iq VR0Dz8?DDUܤGY m ] Y Jy.xz_yv|Q8~;n$-]Cpb)eD~(Je0>;C=7/v%B_v qڵ'߱Eogִl9Gvh,c66Fz3~;S Ў  <ϙ57m MO@Zڕkq`=yi.86<^}5p> Y/}֝_R備\|*緾9M[D\Y5t`l HÎ]i.?j%/-vE0q5z-u|iъWMʴ,DmN `_9i"J|OeIz<v;@[.q|[>-!,I !A6%i< ;wklMPt~p~_3iR~ X39+S f\D996TB .39092G REk(dN1gm'TJ]{;x&l( WC.W ~C)/P^ +6s_LZ&غvc󒃪y ;or|WEH0휟m1#qW_} `_9C칇aϔ7j};K`yw=U<<4ZEr5+^]\/srlAp!'V7OTG8$}nz/_w5s=gN ~<[zU;fNkn~eŕ;+soo/R[7UqѵIMww̹ݿw=~yCќG8cw_Ƚ{9&r>_dOpƻc{QϚb@L*ƄvXCWV E˞jW  vUTOyTMjv?"%*Vc NN lp|C]D% V-)蜀 U)c}2%5 d{,쯂?I)G\xe؟AUxFq?*v_Zyi޸nqd8W-~ rRN(Xi61rC!cw1<~e\4V @l Z+w(E[w39sfU xbh` Z5Y7x䞇<4G>\qMklK_ܺ +Z;|/x֗Rn$on %;_-۫$ھ˝vt勇;^ yiK_ǣ 1(oʇxϞ2myo}n+{-/}>tƄhSLvLS<_o֬YcniC^ 7 |HAy1b po ",*̙SsL.<*mAQ&\{yx"1,qw*BRA4k嚹v{ី7Q zP4i*J3X3fέP""J.&Pҗ92NGGɋҵڹM@; Y}>Z'@rz`BQU7Op6o Z^[kX^i3z ?M,~jvOS?}lsDcOZ)|gR/T5@e^öuEg _r0l=s(&E&'e.S=4bM**Mqu0DK(6yaG?Ǿ?d1CN: ޗ"+F}0✍Vxw\!'N4&'!`biӯ`A%\!&ZUT>w[q˰,IiiW~^z[e{]kCEh1̺9M'-6z߫Jh>z{3x`f={[K4Yʺ덳W>2c97D # @8)'KJJ;oU.mpgb! ë,^rq }AN Z"xWs0K*0mlo… J|?$@\҄ 2>m้=!A_}今AxobzӎojB)'>LG8 ~ T fRDZsH7Jo@ yX0a&&?R(QGUYPM@K7KL*'!rbR8yy ̋*܀ OR: @Cg0'2$AÐg>X[S78Ț%eR?GNEFEk-wفC3~{D:"퟈\Ɵq!Fb 5?IKU\R*&@iBh祍jD(;Cx7 YVy+m/~a/%1mW?}#@p!H6Ȗ~SN>Sژ4A1=y#ni' U?4!g<<3{qǟ13U<pVf k$J p@[kSQ%N򧈊-K/oz~_Zղ4 (VXYl";_FXe~i)^ a 3ίD$sA|I35&~Xp/6lIFH8?#',8W0!pyZ _6E6<.I>oׯYغ#eXp ~֪ [/[/]l )eIK#BϟWE@;ڄ&{4C>IMExXkrd@a-8?fbc?UT]CGm1:+(j^{ z,_~7)'j+Ja{{RAI{?SKQ*?bUTM'1Q2c B@]V cHIJ!O=TId@W>ŚS"`Ge"?Cy c q3hS8W@aT4t]W¯үQ)G+oϯ{/{vy k* m=ڮW׿)L?o9Ua1@,!pA_ІϤB9$28JJ@n—|duw3(C$^p'RL>$7%YΕG gqƟj,Axތ-WeT c˫8WP6S='v[7l1ϗ=}}%SW>fMe ȳ?JBP7 Içj3i(X⎙84TU&z %hh& $X Ev4J&_]>zGs!Xx؆c/q[%ٴo'هPɹh18sV#6m|Mg7p)i[f~Ҁ>m&@~$?Վ`W1yK[gR^&)B ]"?$&3LĴs^`b8OKNt" 3I&yV~>ـ i?"Kxw)]xd"g=ŀO[%qs6L_i DDZ͡1B%gp /@ ymYJ._-h .3ؙ R"M+ևF5H&vLRr 8yؗ J*,&Mr$ O 3㐦QdI ɻ$S3nijD .qjW4Dߐ9|ȌFjqc h}& sVq\|!\8^r%!]tQj@402+&18΀8114H\?r/Uk+dN'Y+\53"z>:k_kIImi18šD"8?I#, %3񣍬_,`WA>*=a_ec?ڴdv>hc(ρ<7mڔv䯼)%Y?iN9?DUc д)2}ݗ? IxCLghBeq ?@$r@ xC -ȑi5"LxUZ3i#hL~z0|y16e doڱBHCp4DΣe(H#R`VI@ME9s+V3s{Nm{_/̚6@'@c vGׇZ6σSchN5g1vx>ϸ" c2 y3xvj<@7 䢾%3_ѷoì;7$뗌GVkc̾ ɊYjDPTTTl>}zFg yS%9Bvoٴڼeu%WrP_t dL2/-39Ę7&& &p&l⽿T=rژtpJɅͧ2@*&e& H@Ƅ$GSU˜hH"I@. |qq5/__E!  ;x?L?L4"0fCI) hF{mj"Gv?cc1YP.H:Ono3b߿{wq]7[mZϞ>>wں7ݿsloyG ~;Ÿ=.^]Ϟu0;/`xl^` =Ly% S&9  2o?< 0>~ann:mTImwǘ~{1T}׾կ,Tj{wZ3 5%qԉW?Юׯ.lpEAU0"fϞdSlW' K)j ?HdLLך:L@ld@/?,Ni}αx1APqLO"g٣39vʁRP&ܿ4N'*O6ҟ=v=z frC߀Lz?hh:~pl$X<ɎhJ"W;~vym!Ø ;DS@ 9~j`bV/+i{.ogndNs17{@f/i`ZϟG&ဥOz+.Zleǖ{|}5^|_lE%{bOWg4c> V~(\)ʫ?JS'M xL".9~Iޏg )`rRX`dεZ$TL*%,'&m?ĒfK@&;}N}׭[r ԗMN|g$C(wZѸ>Υ9tQy<@doXONA9󛞱"kMcctَCHV9[bREz>L퐋{>^ wZ>?k /U3Ŋ+=xymhsh"6c#.] ɖ[عV*/\O\i {t_Jڣ0@y# Abҷ6Dod1= Rl jOڠbӃ}<"-)O@ـ?]>r}GZ>ВPO*~nQ%z?h~qN`y"gz ??CcgD?)Bcg-@GK3CQZbl.?# 4 L8fk @ŀd}f睙8qb @xt :8ۼxZU)f׃ozz-,Yp׮]UxZE & mxۡ h'q4cЪ _&gnc|k g⼴K;da⡝sBX&Zܣ@LfG$PWTO:g}WY?v\awm1w;b!-_YW|4Dd 6^d#3\8_D nſl_~=P)so/lνA)F.VxZ`gCRh SKT LN]϶+W$1ŋwSOBx 70v@Kx+P^1LiKK‹@"/ /01P-c =T8ONC"00HOD9^{f?KDAc}nL]:aT+hZ7vw ixLCd\hGm@zkLڞ۰l&.fZ8vai;6c6zyv_ѨW\g?]3hO`a V"'SEG KQ Z7@~T=彠 1 <dL}?{?)Jm!Y8S8Vxq߬_Q(JgǬEnu\h8aLvU?t+sE;0Y+L`\קv`ap(7<|G@LW P1yaI=H"M'򴔩J]d ?5!8իW2&;<_uOh4y d?~wHq؎vaS۩];Q@yǢyhTlóMOw, MF?^D q篸_|=yF@xW4?@ydѵًc)/I6z& \vJW ٛ=W[vpB9Vnѿ%kOv+8fSݯtz/Qń~Fln@^Z}JԆ&E‚`/Б"T__%}G̉x>kdۅD2yŸ!/ K]* ,6hv;oցlȎ+W5E '0-+ G, 6 Ѧzp!k~?L\;&\Bdoƚwx/Q 鑃`/@#]qNG df=#JBFyDJ&8`}Bq?"(GD 'k=F 篼Qh2sh15s7|h@pSO;ؽi[Io,!p,{J/;ԼGx[5&̠%(hN0w"R"/l~w=&,Ay17mHd ,W`( hވ#7//[ӊ`⨘3gN%ns`B c)(~ ?sF˥mlL.mlCcM9/J/T1b.X~:~ &a&u(ϟ_*^RЯ""7 /cO.G<]GNm-S *%qhcO>ކy<+7!k* @19u伇,z˻ޗ2;*y^۷o7:ϟh }Uqo9{sn*xϫ~oMޢED"䞃gP:S1K0ӳV'$SO8# yGǕOapŊd6/J\}Օ|\s'b Atĉ/#8i*!ǟgsU_&M<X@kO{-'%_y*d+@vnbnSq} xpIcX7ݯcr(wX:: @ ~גfG|>-HCRtI@@$:TH:b:pb4|QMqo'NS}__u{YU󮺭tmW[[8Ĭy+tǔq1G",9j z:ZVCF񍃼TP:[IU+6$cpcÉ'!kg5[T|{ ˶?. MC8[KGult0!-I@( H@ z:tj΃x_1yEn|8q$'_$ŕ~@~ϛ:q @̫:.]]-G[h&D`E  `] `T `7::t.wߖ0H2u;NI 8q$Ϫ)Uﳪ*@+&ڭ xk@yB] ~:D l Z p`V+@gC:vTp ۦ$ޫW/J3 Ɖ'N%[^zǏ!x֗DNѶM @pI~ yLs̉kԶ%KZrߡC/HTh't'vjC "aРAnē_|Ν;azꊺ]v>ur|M^ >=6&Px7-|& Yϗ/_Lnzݎ$"{-5`/dZ Ho|Pn" tp[?gϞ=a1q tV̀ㅇwX=jӣtb.ؾ9 cˊe7xL׼sTK-,qId"I嬢κ& T )zSD@ր8]tKu#2F&H_Мx}~Q_W#Xl [ N>XWBoq%.q:Rꏝ5/;Qvk;ܻAG"u|xi :VC1 }WU5FwIF?ak}) ~ @]w8PԜ0P6w޽$.05DoG~G,Z? טc߸hGu.ȻCf+Y]{O<~B OZY0L:t7rQd"ݺuYE zծ'z.oZlH5+n/j u>v5]+KĕO#Y#ŕV%_h -sw_o{=z{|ݐ O7km+@eZ8vr7;tlr D &ٞ %zǐdHP9_^|(׼j![=7:+V~׈W#%V~U>"8B @iWTk]juY-{ԧZNVV1^Ǫ}ݷkK';-[)KdT$Xp ʉUuhfH@.\I,)ۺǏ+k(}/t=ɵ[*&-pRNK˘'?ү3.vl~ȀxQ %CPQT #g#}iw ͛\AI@`Bkk-uHƥ%nNuM_S˼ҫK"o/KNW$uB YY4[I|JWAw~=e8:0@#r$:7E_@:P `[O:56A >;4 0U`" B\/9Ա~KIǔ8ml{.@S'<{:Z /SJ'eZM3K<`?H/n@YxA0}=\رFk "P /nɁvT@6", sqk,_LKuɕ)I&9 ?āz ǜ8zHp 26Eۯ0l%~B:a{h&ֿMp0bVWd@x0#"|82:ir[-o[ؚ-E޿-1o U̞ 9. Do,OP!TJ3fӦMGѦ\[iSi&0Mۯ8+6/o޷ 0YTR}wg䕊T$)껉^{hp'ٶx`#*`IhMgz*N<&3_^s:pFH IK"WWVwr @a`9 &%)7s [7Kg*/Gki.nj ƻm2xכhF UL'hQ,kop(yOqE ޏ_?I"~iy?vt@C(֤p"2^+@D"ŁD@i @r Twީ|q@DNG$F@؟`̘1vrVi_3kI 1QQ3\s: g@,)- 'soT.+{{'׽D*{3OAYeRsO*T C5%k,1Җ!%^z%\נAZu2\$@]60% _K" ʁN"d+ƐK_Q|zFW%W^:gekWwu &8ÓeJ9FqS (j@ @h?E8@/n(PWC xa޽{EH5ᆭˤmB8@NuvoI6.It9rT>]:OIc$7qT.@6ee.@Vr r@^V6Q-p6@۲N!m-((:c; &|"+EGK H=Ғ(D0*m=cv(n N*ws &Mp Od 5_]Oiy7g9OE:@\FokҶE6k,L*}pkKRAf9^D_~f8ii 87"OY3Μ7DFɟ4遌2k1$]gq`i@LV$ x@5 .5o4_#t5x9綗B h˕ae4koT#6`NXYۯLy8\G?!|/@k #NZ Yл?4O;?}O?>}[L~2yo)?`"YH耽D E:6B L+cyu=Ϣt>Q6Ȣq}ޗd4>e "OrrC8$>Z#8iY$?KL O}#idxK׼^|J ޖ!-Q?=q#CA2% k @5+jZ4w7-`Y}GgXBJj+3r5 G4- 9E$)^^xq$rќ垑w1A`ő?a‚@Ȩ^V#7_u. iz}zssCJ@TX`:K@ -]k=tGyLh翆-P-liAAAtlG}&ut@|lJGpVvrǜ8^#9czygǜ74pw$9r?hPaJO?"_ iE:u:kB@xdѫfyK'va!^n"JS,p]Qٶ&r'Ϳ0`g_2Q >2oٗU u/77aB_Tt+_u]'LA@8JF,CJ+θ) #-PP{IQd zjQ"VN:.p3t1vgkQ@V>Zu-GpbIsҗ>b}O\ķEOXԛL0-::]67+M KϾ<`½>`,;yK8SEJT䡾m|Ppp:%~cGƸ_}i$ Pª:+k݆w[K2 j8U]|Ä/2i_GE]rɍŋr}谩ǏZܸo/e0<}2>7 8(>޹6 ]ҐRW?y҃:4[U@۳LHuGp~5@AZҷ8Bwulg|o˸?<}Р S7kB&_YGץ}$Gg$;Yr@L?C&l?_o;kuP&NtY?s3]:}O|*rmabr'vůޮ(J~X+ Wƃ3ȑg@`nj)J 0la21`$֬ :Ek6~N-6K/ٴٚOw?bĴZPi:րLD Qg4gQFUcH;Xo"p]5WVG,W_)zגc_a$OA9Ҙ)t޽ -,q~Ẹ>-ML8eʜf?r<ʧ\|V~cJF`?` _pl}%xy:D/Z=z䑲/\쿗&4C'YRornc7++@Ks%i@62pdN_8|ha?;Os!;S@QR8 S"Y-m-NL8,~|␣zdd yBšGu[2ii7E޼Gӂ?&z? <016 ޟڗkZ̹߳Ȍÿ鑑|T;~~}N{>/@ X:߫1}NtRIeb֚{ݑ''p 'q=pn40XS0U0PT=,"vv%S]K &%^K9O@ude$Z2 LL8:ڮϟ8kgmə]y5Ͽχd pA?uK.)^ѡ?If{,W\qט{rrS//~e/?pljg_@[~C2"NusϠApYpI ~G&+&<;o[w7Jp;+b 7oG~Yݶqlg9A>}. $%vq$o?sG8[_*ao_%Xa:2eNF\OyQulX|&DLp9'Z@= oas!C^ðaSRI~XB  \w~Sj,4Q O:lF@߾}+jB~)GpR @uE,]Id<#KwVJVNfY O=@z棕ڞe0|f)C`)6ɒ5͐.~?hpA.66uptYf/F5%{+k<#8$_2z‚.L䞢sݓ{%c!w%+ 9 ?.L'瘬y ѓ%8{2&šqRֆBX/=#] 06X+V" ٳ?¿}W ϝc5`?oī CV`?8p#m$p \@7 5 #-<#],YhM@ 3JL‰W>x%\7+O5`, w@5R ,l0nHZ."@΀(>|96s1`TK'!^kpd~kGtݛKL<5oI"qy "C]lBOՀ)wVbڿ>o߽V0C>#F\d@`0*їB!@z[hC& 1m4ɕ󴧉z,9,#.}xA>+w֐]2?~_&RՇ< hm!@~@H( Baд1FߧϹu?hYvĈEh9mbjSϨ~{W,$>?tB_? 8`כ|Ď+vVZEI ^٤\?w4u6,`+P=q90g9á:tJ bQϲ}GZUX8qbZ\p-u,Ȃ#жśKn[ū?R/ 7ݴNnA7n@ Ot/ OIe !C.0ݺg9#m,3QF ٞ؟p:1%GC^@2SMI;@2lUzե^ =gvŰsfM%Ӂ?){߷n@;#5$AJɓ'ӧOO+!=OLPuw(l#hOoyߢ}m׾_KjfRzwﱵ5lv8&X\ W<U:hZاOBbFʭwoOcD}6 #4y:μ,q張W]9yeޕsV8sUfdҤMhFYlOT?~FxlW -?SD?`pQ@Z.Qr82uTqk6Zl_u'[_{rbpxv[g7),ݼ'YiOi{TL"37R"Iڡ{I q??`K~{_УL8 J-Ϛ=zZM}9F8#rV~@+?O"9j !gL>䑲@+g_ YjN\dxdC@$8q~Ta 7N>|쌴fA:P9P{)GrJR<'=/yk{߿)͚E.Zm'?ʻ6C7n_Ϫ~'붽_s:׀'oY߶] a&$a T瞧~o%q9ką!Yg W˂#%eeeM^ Bm71M{Y쓒ot?MgO/7(DG3Ϻ&mM*0jkSjTԶM8?W˂#/`s4E8VKsM34̹c#Y{$3>Mf"~DRk'ޛ MK3Qa$@ey%JV*+ 8X'_#QBŸ9=n-g)qve бN?f'7]q?eQM*矴?}b#@a&)ϼ JF ),@q$Cҁ?y1C@;b$Qd$/ pi7CL4g/7?p/47;@&ЧO& 9@k$qפ'K׼Q9!e߮Lf"^($Gd&"7c\27Vƌ۲??h8 8ެ7;v@y%H#6@|~Auo/x<j߾]4H껉:`ą)sϽ<)iU'9̩ v@>-pʔ90@Mp awh*_Xo?c({357-^P {N]-/D:`y#(++S>8^u]Ƒ~#%q3;4 70b B.`B҄\AB HY Bǀ@efلdV;xfk:֮+qǩˇv眾NzC}^#}$םkON&W5 6;b,JZ??Gno̪Jx> wcUݴik p(spDyO|Zڞ?Qu}}V R?L3fx07w`Ub&91AuEcStXo(O0_ :x*ź&PZPZP_~MR./<..Q۴V%#Ჲ'| 5* ;#yi> &@w88ÀBf8xڣE%T+7b0د*G ?"5$ I]Gs!AW/ -WjqNj B N%*<li>}Q t NHTm\1ʗJI)eq FŸVQ&C{J--Yf೮Bnor'BwBLh0Nʸ}b XP\wԩ/v4$@cY9t>mRrr!@ч:?zEzӬB`#c^ Q4LDzT$.k5dX{,1wW0>;=JЮ7]LqRW{z~!;6 81!|>>L `nen&V+ -PTk~we4kNj:>Ŀx焋?O(xmTD@Ci|j MO/k)DJ1R~}ReN|OeVSuMv7/`csNz L5kJP '?PG@ZV 0  Г}-%9O]q;0.\3֛D`[V$?oqyDH05 ""~[nn.@Pſ4/TîO} w;K48`@2j?~F 5pFÓ&DglmlA[ʪt| :#W (Ƀ|- ss\V,{ScJ1߼b_ښ55`)=˩>&@אX6n;DL"@ HG!/@zG@〭ǎ87F\ҥK~3;d GG?tT!-~ w.ENl,9\zBe9, T|?9g` ssWqfE}ZXn^vWAOJعS`MLqVX:!^hVOy7?ogתK-_-fy :Y>dO~а-=@6Uwx<w5f`\gm \+2Vn}<Њ?x t|p[}xPD?B7纬17񇻟 :'L $fϷ3@~ 3IyhًR@?w++ \O+H u0L?Q447fn-1Ԕm➛r.nVw[37kזJiiUƍ&Z\ڣիpcxZ na= zb<,Y@9 I3]l 5|9ځ}B -];wn=;3g\IU((E71OT xV3vs@|{P"m[c>wXYN$rǵ!55ODylH6,@`MXܴ4[EEEAA/_-722-orZL&m (C?^x@O;({z _^_"ʀO=? &Y\7u/Siro1_;zZxO =U[[7O8VzaC0#y̑!y ,UuuO\8kr"Я?U1`L]CFgQ9(gP{-z9 c-,@؅'|6w/;K4F]o,i6z)vH%-.QPrcPVxR5014=?hnc⏵YX<-+W Ya\|Jtek]jV]  >#apIݓ\f6Pd`[_'[WCYjk=Ĵ1{%/goqCP187/rMD aK{2|p7G'ؚ7GB wr*3^o$x/>c'fU---MMM6M*ŗ.$ZM@y2̙3*;uxyN]vr(t@t@KG}sTΟ L"c4߁8A<GS}#V0z-8=Yж%t/@(_D7a/ : ߸nS@-P?SJ5k Y _dJJ|)..&x& g?G&.InhMcH0 ZRL|v;ON ~!c r ҥ9 tځ9S8{I#Ώ:]vH liS/sæ ]|_Py?jYrbnUr)V+-~uҎ ׊?BӾRGW`l x6r载?Ajmm{R{R27 *wXiz|5@I1~> ;D3xRM*-+YKԇ?JֿU"@zO0om=Ȅ&=))E,_~d''BBL7 40S@nӿj;ݴik8+í*R^~Q/ 0dw$:)¶Isayt3 b1W}ߣH!y\ 5ztC' *"tKe{*9p!fM0F߿Wn-{+k$[dLẇcb)~b}8DAϙ[ 0l& %G`j<!jk;I _n$&&?vlƍN0 @ Dg$jimݺ l& 0v'N;Cvax}!wn, jq {x0?%^8)İ/1<bҒk=qR'@K:3xi# 0uV xolzppe}?YH&@Hi' yx9JU!ytTP(y-huUNǷt<Ma:q ̧jCse{(LJͅ[wt L\dD>QG~ XժpC%٭+*4 BOӊ?Q+[ & Hzp/mf/mߌѦg_.චphL!xF/RQCe1[!V4=f__/RjUvm}^)>> ʕVſx' HHc#"6HIIyҚ5?L65Z5b}a7a F +-IkN[/ SXt?NKdxGs0YsarKow?H#Ȩr?CI ZZ/O#WA4oK!ˢ6zKp` @}}TSc@ccMjNS 0Kvt2 BꔅU\hÑQbTJ1..I\( Lcb`eWaqbT*زQq l 12,""#Fm|%=).-`&la(lğ~Al~*ʢLi >}Źyv86g8 6kٺ~|Ǯ#_ϵ.|06X Hl;M]8'b{ XIdXy0~-?x%# 76O%eXiAIENDB`tiled-qt-0.9.1/src/plugins/replicaisland/objects.png000066400000000000000000001665531217502731700225120ustar00rootroot00000000000000PNG  IHDR`u pHYsHHFk>bKGDC IDATxXR]P((E"6@%nXb)DML31 |F!yٙev~sBFҾ5&`E ˄+OD,3ۺآ,_>2=z.}T3M4UpƦVIrޝz[WHW:G|w3Oijc%rPQbY$rŧTk7OJ"V;[v6GBK"?n3˴m6j xǨQPh]ڨ[.Dt?N'+[CCJ{{{8|0۷CYY(((ܡ{Ҟ∱$0h)]2JC!-n1?SwwNQYp!8 -[Q莽ǵXX*;oK5 BvAXpmL4 !<`(?d3I$=iWPkد@f&@L 4yBHN7oB}>|~-/3E-22B{voK#.{L!nt\΍G@\zu ~%j<`%p#44͛Lꄸ))Tݎ Kय़wz|gȸgAq KO0lDwi0EGT m@K22eiθ(xtZz^t@r:uS>T 5+Z^-dKxCBZ|2+$Br [>10;w S:n)\VQ~g6E E!!8bm>~@ۂགྷMBc/~,'wa)?nf{)dS~tɓ'piرc̝;q[n1\Hwzݰ}'EM*qt8O]O uA~3'=ZiWr_Li.mp=0hyr^x|~Nh] ?t9\rr{SC6Џek8! _ )SGkh):PFFqI>--z!|MK&42sd% !fhofWgFJU:{-*Gi50ೇƛh}P#g@h8 U i B _- BI@~=^)Pˡ-<&R ''OUcܒ.۶ׅN^2?tޫsDnˢ[Ma|]T C}& qy9VR 9jJ376ӋϮO"[WCtW 4}s:}W<^+dh/!!qGHj'm4̤Ӫgtt4Sty _EQIk}o{VhSZW!iXIQP)"9Rb 6.z>* 䫀~o1̫#`~ l窂Z=X1{Bw'lFƚontax=w@˝D5h/lƥs0RRG jkkʕ+Ӷ`k^~R5z;_v?  }Ueڊ4|պZnO5X 5 7$uezTQoƒesZ!?K^& mn<;cN+t?4/5;R7X*79 }}M2//C'z/W8bQ%ގ & m* _N ,r\RP^vS6XSSw<1i([?r>!?'wZhQOG)&Tክ ޥQN->GiMyRJOPYY9LC}(Qх&r?+>>9`ua'nhh~^Q|Is{-P4w%oO$R^ڽ-k?)w.vK >-P 1Ȅi6pau{Vz'u# >@Fs=xۂ*t A  _߿mEm][5@y NkXH!VYY gφGE].GNj*"V>EaADHurJ̵kHGdHAi`۶#m F!K_逢!q4P1#Cq-:(,<Ɯ+_ H!>Sp@J@ 2S)))?i;- S)8C^>y_1~`Fk'2QHW(=㛇oh?"}ˡikGӝه0Le`!1tO0<^ZhB]yL Ę<-Gxp[: vaT.S- OSINH9߶869#:}zTˬ&3囎}-E"#-Юy @n|TmW“MLG)x2p.uJ? :} +!!0CzV~;PEO$ UUã=UP Md 8߆@ ^h֌eѥܥP`(; vG;/#"nܹcśs[a!ƒp <^Ng"|t65 T-*OY3QmD=qu~;B,{ EuAlPdG#)4R E:chF~LHt Q9slh2v4o> ר(큏>e@n&.VӎxuN:N8t_eK xah<4Kx8:6!x"@$: _!/[_0CѰ4/OQvBMAA۷;Ey_OKa_L `GuH:MvCP)QE7N:+թZ (x4!F}}FP/΢!.(x+;[tyԭ>ӻs-"Ln* w'RqOU5sE'nHA@I.O ݱ4vMvz:}(GPi L 2Bi9 ( էmpTAMa{6au0ϲBO !̥NS{1Lm ^0w|7y@RZ-aÄ4ϣj'-Nz -yHг _+޴kBGCR }=]WAHs;bQ񆮇:_άH&F>d9K,҄jh;UvԛJeggÝ{]үxMmU&\٦(Cm\RhXJj4~mA+30rWRU))4ԵD{[ӐX=`l:ꅂGm= !uE"IDGHFW4_ef3OF Ao!6BQu pbP7XE-//Ỏjl#)F-*0>N =V)F/wϊxt$V7:w=j@1Gl0~*)&DBLz1H<@ꌽ{(+;VrK.sH>J4pbF$e8b I'ADtx5Gz[oNghMɦJ 'HՑD梻,IiN}` z[Om5wpK=@ j$TixzzM0!jɒ%-[vlF9h me$J}辞8> ,F]rIЛeP1|oDޕoMn WEEE6l>{,3fUߖ xk=Yo>kɾk:N둄+yg \.n-dm>eK%pQqZFY{̺}.Uo7)sK644̣Q]p>oMc 2XND퉶#9@+791.;>f<>&$$bklBǨ@{ Q( :Dqݧ*T\8^ڴڻO>o[[֭cNQڳklYT ;*G{[7}e6˷a(.v8L8Hx/#$yfƠA˝R)=Wh)0Pixozg{<{(X(.j@G/EŠuiڦKljo]>Y Õ:Ö D*S= AP8v늆p?;%%4RX`%Yyְv|%32xRiiq+& \m燄hG .@GdD<*ww)@"#'v./GxLE:5huvԞp7PXy0sZXg8y-G }tdkp(IK`d Q|T6gtʿgxKl$QlxOܾ>zҎNM6wt1 C#YHO@ X>zs(]]@vvL&BYTDQa(7,/7M˶)S&e폖8|sDzp4ֻN>]m ?qɯUx:~nsVja= @B0Y9+'imY LcUږM[L9Pt}[O32qq;ٖ} 9e`fΎ} 3?Q* WMA8(WTPXXQz"!bj>uL4 -| <;>z바W@ gg QZÎ _KL'%>:gO{wAqyĠT/v& 0SWWSd+z6ޖYY:rڛ!hVЄx/p/a( T0,@zPdT"nTT읦;\h|G|R2^p@2QpvPГaD,ܰʂW| ׮߀K7Iaڜbclު_Hkii]= |gɑ#89X[ [4xpeڭ~*]@ *\yM[fr?P( mo0ƳՎ\. Lr+h"WV7/d~ǽl|)w' _ꋆî•WaH͛+<>EA 2Xh'tAS4o.耟b\z+ؓYOxc|rCL(*aظi,XpaPyGAccc@W^?hԷU4l(Nj*CHu?X`0/r|(wJ ) e9Jy3 !'/rwnn;f:NVjl[ccB΋!p`985ŗs@xv9S W$RF]ʿ`C>}#:eUL-"PTTT~TSSc@n%WIf5|g?b=}\^^ _5?( <|?ee(@;R{J_-u.9,' 1q\-Jikkj6m|"53?e(!9Pګ}@tICϱ p֎yWW^^a%₯S}}үPNa?a8R8=y$~gFT2QqV* dWw@ !|-g43lIB!=vvi‘y Ƽ> ˕7ygEXG|z:::󼼼dgfd H9>[# |{; j :"p%X%%e 7L4^o:B>AlxW-tNcbb!"#UuI8%*ACsd ͍V: N &uݽ̔ ޔ۱a˪U=M;[.k /BЕ@l{+ӕ!2; O.z`e~}q$5voIT~9_ NՃVXtfDL$#AuY[?vQhij5ɶ' 2d$HԴuLhO 'js:y]hp$\bB// Ri12f0VwyVE_쁌 uwylSRGKN:NN؅'ҤT >_GKtHZ |'5 $z@V*aB" 8Nu;NC3_]CYSCl[Nԑ醳.YOGHdIPpI y-L|*&P =Q2$e/gHR*ϙNFrrZ;_ҁٶxQ:moIݕF}*)Wnu'7$ts#,6L e0?f;A4 #?}).rY _)/+ {Z!uzD\F7XQ=->J^=şI9H@AF=W]"uW^YEWB#9g߾#3؇z!XKStaQF^}_P p@7wtwkN6B; bb.ns_:mcŜsJf 72&33!p۞VC IMݐgL>vgK!=qU22p۽yR+xúЁQ dGH u;jPZ؂9`zg(]YK%HUIr|&mѧj܎/E h:嵐`"7D#v]E#4#/{9i4%HA:r!:Bu9:eso(22\]_0#i^SS~۹^ rm6ba-KN/Åe|&D#$Fىa' mePk~w۟@UMCZIYe#,*cXO)ԟI2j 2TMNzhx:P:mw" b)|vQ#jj5a]2ϹD$ҥo*K_0g`-ʣ`Eoc}n?t8Ίm`(0ܯ @%p +&x$pf/ u"uL=F9|少ph\h1vuF$#vmgqpJgòtV 19}8,Hr~]Qؓ%ɼ}U67| Swp$A#Z0ȾvYkߠg"L``'066ln*7ƻMu׍Mh~+38Aj?p.\3X=Y_o,^xL0.l'5|F>!oz:੄Nx{>wTwG[~ðOo]~t fAsE n"m?e`m[C!Х)ġ>Sڞ肭*%^ 1';o5Uꀭ0_##O!ٯs0# \c5giAA5φijb?rth#xծlڂtn~ :fWUv6f9{S-amĊGwJJʠo𳌌E%%:圝[":wQTi`nLONRG׀3㙊ٱ"xh"g$!@#N,phg"6k"5w%rM c}iXەTDdt>~v{ ^ J͖?%%P))ǃG577wzKѾ6!(|<3Sȅ1n$n "0`_7C7k1NeJQMN6ډmbwpjs>l,5]QI8 ɕۗ91$lB[tI[0#| _#r; +bLc ? 9[llP8xt,]]&w3NN^wmtoąuVP*+jRIAm:q{F6! :@a\]]=TUTw(`4SPUyֹkldڿ&⚊ad; 7#@^Z\K KM_)@^& ՗&cX-SY0-@p5!p՞#dҒGQ4kA'j5Doc*ZJ?i)蚨*+*虫vTOHl`bz9^YkhIٱihʥ svrzΕᔖ$UdrUP84L'҉DO⵵ Sc nT8T\&A|+?Bs[s@1 9gϒӌGp)?Qg;MSI^R+jR(*Ȓ[X-˛H1Y+͝;7b4UUUUƍ=7olpK#:SBeFel{ӕUWٯ-URAt*T Ņ@D~^llʒ/JKKLMM=A<6e49k%[>< gAٳttKVc|0phbU(kr'LiĈ(449..~~~ _z?yl'Vň3ٙ\d|5{7.rټXk &u9ЅiVraWzgFhdu]>E5}?۷Om۶=\ٷn0E!8se0tH<~l!ҶC ,2#!S4>Łuֱ" \ɤI߳gOWaEpځ:rkpJ{Zl krvwq؎z C[CC|> H?`x+^`GXtUq׬p1C, u u4Qc(vgK7S|=,45TE (Xtċs^'fff)hѢb&PiD撗s}}=sGNxر% PsMŐ&,|j8u_ 7o|!uիW7zyyE7z&E&1Rۚ!w/ GOB:O^!tF9C C'eL-3ٞe0iҚTÃ52N(sBH;;"q\077(((r]vu"ކRna -m|.99Hw)iC-g26m> aذa@~9 7WWq o4vO--m{@S8Aw 9Y1,?!hJB~B k;D>}:s=||{ùsLꥪM9dY[|&!Q޳WSDȘ30tRX SAЪw~X6 K1 Z;I&](ָ>A\^qI3'uM>l-5qbN]]|OD!@NNN/H([[GTt愴83a„kXM?H|/9߅u7iobW -6MkkNr %}}}̛7?tDdL̚5qD́8GЄBwL4nw 4_U Ym}{a}`_\ a쁌K`փk.ט*A8%c"AH" #xZ`~[k07nRHQ.$]7ڍ3Pv@z\d ',ZO88N,3%tq9}jj74Cii)cxY`{$Ϫ|:FO RՁƶOV]b<[R ֹ@9tbJ[<%_^ e`]&s`cr,;\?ฤ7nZ{YčD |wH UK]gu4wjƠ9Q㤬s [Ž1'N#o`>x'K-v%5z:ػ-?:!>/N2ϟ, 555̴̘1c3/o;ʈ)@ kȽc% a'vOֆCە~6ӆkt!'ݕa \Ltt^br͛7޽{o7nٳgXJFrF#M3qΈ ׬13'}d$˙EAwTIAK(ľ!,\I@aHam +d i@K %WK{Qqq>RC2%L &k *ӗpµ|Oq P}̘1;(pRѪ(p~? 347rwwgÃYb(G<եϫ; ,E{#*ߧC^4V;̏]v|GYƇC; ;)-ogDYAm[iQv,7n((GpÒ$}2̏^mN )Ώ1!PnGҥKGmEQi؞J :C n|y F28Os=fdӥd?(3K6fi;+qY2.u׳#-;vI"2Ks kinq|N[~~~3-K,i?dȐbbbFFǀ6PuqL ,ݴrFeՌ`1u>M3A/whMRP<TŘ9R cqH ")?FKlHB a[Z':lPUZMLG\&UVO<(.̴npׂWuL2&y2cdE"G?>!!!w;Ex/88w4~0:;{k_lPu%p2K}4I2g&Tz^ GcޚnY:Yn_7 #Q)/Zx,"Q1/B(%X"cX`Ae!ɱ( 7Liz/C/N>͚;w #/uۈc.8 x3ZҐU4hIo]-}. -\AGz?QO|ԕ5-)'j= umRpՇֺ?_޲2,Kz7D(M %7IE676&]ZzXʍT QsϑHB6i&?j߾1}1OP2h VA,5 |]<i1cԆ^%4vo AH9~B& aN8pd$jRaX'Uc\5XO${Ca}"₍@& Wy=tODNC8s1+b֜9sO>رc?z?FDD\7-^8QZMnP>o6B[m_n-uΞ=4l)zҮS=ע=_6i`aB1PZ\%8wi-.N ȩ=piÈ= n֕MwT(i20I 7l6B҇B/(ip,n$weBIr#ɒpB*hj*MyKH Ƈ?8nn sx:Sp` 4+l@{#`/$әraq򼼼h =iVSSSG_r O?eDرc5k񪮮E$+W1󆑋]ۄ|T +37E z 1wd2{8 +Qia3h%9€?6^V;2:X47Z}d]an bSCK'>?Ei>kKKL]9.6/-2{*χt> v>͸^[Qk@_>LŁd`&Ϙ(pjRD&2d(oN.rܮC#`{5]A yD}MM^~i,O[n2%~3XY9 !d{L| ] #&AM`7xf7!9K9_*]v ֮ $4n`/ޟaߓ MgR=-LQ&S(myvOߛ[K}v!af:|h.HG 26[ Re4HCK^nқXA][ 5v|3a+l%Ѯ~Cd֏5xgl2uXCpY/,-%-IMImt56> 02d@s fG>Gfg݃]ǗՖѣf*4vbl3hǔ1%l WE`1f j@ \&O4J.qJ/8} ya(P a uXDY,: Ԧ`(ˉöRpe3h +GIc+S&5)3@>m<"{l~e9 b耂\/ֱ}[y2haEU"I洨[@ŋlZu4IՀ#s iD)oՃw[n|E%lȕcT<;Py07恖nK36`I <ȉ0!QQc>Jf5K@g8#)"M֤BؒYw I'OA̞]oQ=(>V4JD61b28d]b ٞ+r!h!&AG`bxӤ`h*TZT<~0z?Z E3w캩Ge n355mb:ԩг7Yի]>ٴ<</vJ/0e<0I`Lk6)pX~Y#rzA渽A]ynt%>΍&&&LH!AAAM'O>WWW7dƍ]&;Ê҆19:EhF.^1~AA` l&1\i_K ̊g1埢ui\(shF/ 9!P$Hz8/\7,gv(Wn)`O(É̱N?/Yj.jpNNe:ʅhi{;uB dRR=tɍPڱ$*!:%N=r3Vҹ:]L_x"WH-Oc50d`l/l*J_#S}19--h'"7YNbVl4dЖĤ'!>6ߍ7i`yŁjkk nF1EK.6?ǒ?|CSTիI\h‰'2d/O D۶gS⃋/^V]]=3D͛78qaj$ȸ9CeT'?[) F)`gz^~dL9OrX56;c?H~dkQdo#ԨwgRhyݙ8,r'ՀMB nsVG͛7oraaymt\h5u@zZS)г~}n\쪪*_ WB#,R@z%´0s0]%5FfTP"[E>DQzd]ݱ_WDZiDڙIJ $$耫1>N9K?,$R칠?CW4'bo~Ӑq)8]!U]=OAӱ4fo* "͎3aB˳\xՃ70e;8RaxbC˻5;>@,̨]F P$ 1 MNlKݙMo8[}T?e_/U=by-"s y'%؂nMDDD#u/NEoIE(++\'$ZˢQP)"kal쫘#:'K1iPN( BYvMRzA:B|HAӞvgE)wn}HCS:w`U\M*ػ1"Wi;(U@@DIU Ш-1W5 b}yryw=C 4{ށWÀHaY/=?sj)5ѽI}鈜W ^I~5< UW i3vZHM=[LʎIwin&^pEEEW`$ɈҐ)Y ғGϞGcl߷[h&ƚv'e8~T,|Y2^?BR4n/w1U]MOw^\1k--3m 'KUe+y2x3\2e/^l;̀o4ڭw _]:+|{/d6222js45uttUUnjޥsB:n#J:d޽eҲ%5Wuz&ޚa>J#sBĭ8#[ajBW ׍)vS-skfOjyjyZ| USךeim?QG|bRҲQ1/憫j]A$$t ]H'ҍt$]Igҝl [&(?XZZ={asZ_[E^'IN}DHovIZa`3)v P0l'-#kix;_hM$h4}յc+yEV<kfG!1% b-m؊wcg~صoFrJC7IOIH&&H҉t#IWҙt'l# UGO(Ϻ,)gVؿf%`Y%c 3yvA_Isj0pY{#t,*BX ߢE,,1 cWN@K a膙0:lzm MðpwG'xpŠO;ϳq]EZ*`jn7ab-eT~ t?q =}|Śiӷ=j綝d,II҅t"HGҕt&l"F?m_`(/FYi Ңjb^L&>6SMDT\VĪ+j*Pn@ZhX^^tzt[K:C󴖸!rsa?O;_2rںW1%0s*j~јy ӱ 1ͽ;?f 9ʈl_[,Jl]Lw1ߠƗ0OWP,YReX}*--E@@O2'+˝se>SVVE9] Å %`xH;z=/'ӥ[- Ko/1Fs n>s?`p\!ybP\U1/tvAvns++[s̄"$dLM:.F:3N6-dF6dsso HY>s<*"=hr(Vm|{0ޯm3AKJJ#)0`0n8s$OHA""΅PMA^p'/HB)3 y=:ܡL6do*:vXOuɭIR+~:1%xʌ." ۻGKiYf&LM٩XW +U %p&r c붝+Vm@ج|9II"$t ]H'ҍt$]Igҝl [&l$[f9#BWij*"mP]!.J!/"4x?&!dV4 >Hii; \'iih)<i9~XBL Ãs5F'@cmh[f l5'0@~RaZ.feM@1FGc::*;׃3c2%ѣF WASYWȪ N_W}U6:zEgcXIə`X7egG+II"$tS+p:3N6-dF6d3ތzfr,Jy;"Q[A Bt"fDr^CpqcZ 90(WDgKVgg5F#c<߯iK!c]0QIȑ#O11uTaucCuFvD .S0sdL0* 9 2P;7PS.Lutail ZÔy9@\/H^Mz8~pCr1t{=oܸp;vH`яmuqdbg>|DnG{_t kFKCQְwwQrV;νџ^W{\?ׇjpf dd .1=|`1iBOM2H$٤B:n#J:dB6md#J6 |OԵaGr^cᗨ<{.?նʯ5tZ?q`\B"2iSU:E<7 Y$d D+L 7g6md#J6 |ZZZo͛8|0֮] rׯ_Gvv:iiS@ODGG% PuEy8 d0TԅsDZ QsmšL kw#/u;ۓ'[,&Ɓhr/{@%Ή -ۮ3:蚂k3W ^ez==|̾3g{4cFlS55@e03DƊ: JdʹP@oi! @JH0H!(QQ8*"yEjk7^qoy׆ \bVeyl܊h2 eRܜ[q;~ρVD9WVr]p9On{c_c*.4MPn'.mw%Y.;k/, m=S _!Nck۷o|˞ڂ&M k,o]&#$$AHOMX9s1=p2Os¢xC$҇ sPcUY Zpds2e#l ,}MN@S!2r|z<)$} \# @y=;//KR6'LQp'nǥy L"m.%ܵȞ=SyM n3y8fTIY g0*! #a^aKX^|AANn xXZYgaQ֭[sss۷oMuuu'kSYz%ċxoAH&&H׺+L d Dd+g3 BϞ*WBOWO`UUC}}}6惀i0eTLƎ"}]yiFr3AxhsVPm8Йkyd:+)oyqCr|p- <"tH5uCpmѣGmFR{cSylr.y >k1 AKYH! iޙSXb0PV0>s3}H+YJ*߯!uPhBY8{g\8 pr{.;1o'MD:za$_UMj]t:5f+lfXXH1/xU:t C sCѓH'"X#-uqcXFIf -5.5:كπWe@TD.^P7v>ҁCrOy=  >>>ڏ ZmAd$>J,e{20`^l'ԱeƁz<`3?x0eswbC@D/$e53m^I(XbZׄDedJ.]\{g/a Mm`5M#;io8p`{S;0G!^Eg"ěd,II҅t"HGҕt&l"FlnFًk]r W\yFiYa^V0c2&O%v<9zZ%+XŜ a*#ZZ0g 'S{Zst][Ak|Mm&A㌋Ij5Qg(X7K `d>|- |v z#6{8|=n|?1Rp3d^֤+KL݇Wi+{k9F{8\ -v()^jK֬4I2uUeaT7QR59Ե'@UcMhvdmfUVVnfalZRYz%ċxoAH&&H҉t#IWҙt'l#4гg. iѵk8􂬬{@O IϴXHhmN9$'Gji.I lf5 u hf c3:gƪhk˜R-O[GwhMz_rԫW{+^K?@:Λ.j8kMqzG)_Wcsgiֆy5U~9m>+8(uGK_t,0e9@b) Kݽ? 䴊K*a< IeX&˼6׆f^ER eZ3JXC$響 !1T3, ^ēx E2I6@Ncr:3l"6H~^!Gz6͛aOAhy0Tf֮wlje%*Yy,a䢬 CM*Xu<&SZfg!մ`ie$: |j,S4\J&I; B֭[Ԣ.9WDmJfhDY{ k]'OAT  Uq`i{ ^Ɓ}8x*$C1A:˧Ś!uV-`|UZVymm)F׆Na8`9k>%hi?:! =CE<7 Y$d.Ӎt$]Igҝl [&9N=c!FAAGr!(GX>=>8MSr@4ug@L;&03r&#g2P91rxh9s5 ٱtL8pkw+y [G덮c}(Ć]nL<`]'$s6Žu) p4;#7GT.Ѧ\qO*ۃKޢMUXwa}FţrCzQ#%{XJ膤 9;%d4/KH+aXR^^b~#_5\bnddd%fZ!,=CE<7 Y$d.t$]Igҝl [&^3X_XXXUUm۶q3h Y,s,ֶjJ=Q1K52ײ]^ ree;(؂ɳk Z֯O]Y5s FHkVBmP}:+ Gܧ  %am Y_(Wir<{)~ [}N\z!h =k;U.ƺEUTm8%wbgE;V0pzT9/M^ ;#)HGH衭ކy+rs ]fZ4MLL`ggOOY(녑ZP*KгăxOM2^{$t ]H7*G fΝ;2k P}4KeaSҳRɼ!#ڳ߷:v׷*k`pJy2[+?`YLAo$=aazqg}iqc#4LvWCu1~)>_/i(>QJhvcO^'y8䶐yDܜv8Gr-o3vn;{Z} {.Үؽvm>a[~l+چOp~ֱɑE?Ga6'\ZZZ/h^ldd$mm.KII9vݣ2Tg"эy{QbҮ]nAÖ-[hVVQ6^1–N&͝1cnsF^HICP|V~5V~(Kqk6YS#; M]g1ڦZCZwamrӦ:wlx8::Oi6Qee%pR`h &~'O_(<~.f\YQ+q{1kq*${`OR)k: ' jTGA[QCMKsUf^3f3JjB!$E T2PE :T}BDtL =K<m,,w^zKMT+**Wzv$trN܋h :Й rǼ sG /M.ٌIX7-P3a=3W7eCI^ٙ_5ّ2Z f4@x{Gί'UeNyi'=>YDͱɱϯ,z`ldnxr? 6MOm&z|'I7+^ꌥ/G^a=e,ԉg{ N];Gr_^aD05Ge,=C/XvrrJ'G17nv7j1,ǩ]hV=$lJFc M_SQ1DÑ2lrIG<(9C )8BA R{AlG8kAB0 vRPIυO^*v ڥ'#< SJ'1UXa Pz|dd Yc#jABq5rc ~PUM'LAB\]6x7o͟#R{M 0ۛ 蘮'zgS}W1#[&Pd uo(+ \-Z8w)BsFnv8D%}(8t{?LBK.b]f|,xu lgStx5Zkx6e-NZfM[vj]3?\wVM1|F]]]1|J 忟rYh$gҷih\~{r,ͱW{FoRGlrBc-X[3 ܶ -n~4C?FffWx^,^}ZsOZ-8Uxś1#&&Ӛ$?Z{Ȏ.)\Qm+o94Hb''"""=133㖕ٳgC__( GƼX^=7EZZѵebʝ(.ߊel5Dd,]w|'eG{(?܆ W8kr81HNmp~zhߪeKק|s~\<}_iky/ūt!,Y{~Zwy)Dزu+hkeπ@,Z#Oˏ'_ +qlre,BZt]-]2n߶M`7Qh֭GwTuvaþRPr*L*?c* %Y q'?O_ Ash[x`X^Ms..].Z_SW(?am~ 1X:1ӱf6X _YcwNJ,  Z gPQg''Z#Qǎ_XPX[8RUE_d>xUH~韓_o#{Pudk6?#WprW=TJ$O[d~= Uذn ~$v܅EːlxɈH\mɟv~3#['b/"'_t]|׻=zujQaڈho!U?^ *t]ǎ:a_q;4!7/@ ?iۏ BFfi[y?& xmۡ[ j0bYKvz.ܾ刊NrowV bcQܫi""н; ÑS-0&+x1P6]b3 +m£>gӘ `!zA}_Aht5 "+ 5ZuDXci9ˌ^ w$/TD9#)*^k0="~`SSSȌ;pJt3j`~lFut`B6ih;|<l1Hҳ5666\i4!EV22Hgod& MI>iiIl` d$gd3:)ʐIWg/F/HشZkIK聣Fjȍb!,O- \Q'Fа_rrr'j>NJJgԻV:w\a&* :@-D/u/t"_ },L)'CNTij `G 8=JBIKS٦'oA\--^,_Q8& MCTb%.{صyL)8; XaLk]k[Rp=0+sזǟD2~4XBmmH 1ItSz D#LUѕP~ H^?)$C r9UqRHSWnbZ]:>j ^R$in2*eg  B4x?viu$O5MmPZ~YϔX*(++B,?I+**#Go~ D!4"!hnp(ίE͙ ؞ضm;nݺu~PBʍ9;3ӹg%cBf̘u8ׯŽOOݿ40]ݦxZ%UsbK_H> Hwh =د'vCH z(Cb@/|ק;:Es6MHHZQ;u@@3Z Ik|# y&>~>6Ԁy/@u(Szy9h ZN䑞ۛqu KqXYZ8Ir($s3$kdP Iu:clPcuDUot"ݿW_tዞ)0k="Hs sk1u1ѹꖡ<'(‘̜5|>e*&9'iy 2 ϏWT=ؾe#nZ7?sQ1wOH Dbس0qmǍ|=l7@G TC!@oXSsM?&ڪb]^#&8zK[to#waEDm*>\B}v{;5M6㢞=jPX&!.]ʵo>[~DZ2$ZY' z'̅" ^ P6Bh&U4gҤzQmOxbold3l"?+3M9L&?L~|\ 凓؄NuV xV"ڵC~e9۴_E~~kM-[btj^2l.:Rm 5a.o섡'Z*qݹN)A'DvV6/ǴmCLt k5]'BYbl (F67$D3xcy[mnH7 ؜}p?l+*$m6N1ܾ8-UHlkx-) ZB_~/_mHCHEǞ\ض~Ďvh&;YT N4:!9JIJ`x-z̛$l$/*oX8I`Y]5c<}pl hۦekVm;oͧ-[,FxCEzH}ӮFw ~}a8?whvћ^ 8 >VE^ykldYy ( &?7y?\=}'Bϫ, 5g5C;Áep9G~C`;tuPkk[V{*&7K~4:'=p+F-[h@׀<BBzu+÷ 5&=g^a)uf9H!\4&#О>I ]/7hj/?64kk 4SuY5Xio1~PwQ6-[~ëmRb`%3`!#K;Mmd#Upl廠IYʁ)xs8gA>"޺Z|f-W~MBBrX[Z:0 ϔX%bعkGq|R|.U u0V=Ut[ei*(˰k.n\ic6pM6l%y %hں^3!g5wnd|RCZZeCU:zn00/L|o4Z:PVams9CPY~z!2NOjyz'nF&5PVkc \9ovc:s l#V![acޜq 爏a)y>)y/ln8%{Z:.ox̅h<)қ8%gRR +q)SRgլ ;BxҺ%}.cFBqؿxLGNWҙN6-dF6-/9 Dtn(Yl2tN@$ I&G&~RP ={Q(JMke/tD.Cn&,-B)MǮy{WMW:z͜A R[Ž`Z*P}_I'N7ґJ:dB6mdȻZQ<'Ҟl$M{ˋ&φ6M^A\SNgR*,)ۉcCq|JL ʆc I}YPoʱ~m™sF9jŋdh kbV2R3c:pNL7ґt%Iwl!6>m < u@HW<TSfi}21aN~ 膨X%Q~h|>>dQV?]GhٝpђyE3΄#ߔ?D{ w$|F4`ۯ8N?d 2y 9b͆Y\O4~&#?zR:Xg~‚~îOpE]FY-6 )GDyd Ӊt#9]Τ;@MdW$Eۋ O&rƤ,{ |~ac'cO,mXU vU΋q:aWG͝M/A{;c@uc|~/~U΃tb ?lsυF§v~;bǪWp9χ >l|榳_Ie|x LyQx+ąK?skϏ/ق8/dPV7r䐏gqҥKe/^nbݻpy&!49;b#[a*RϮ}I"$ӁB:n#J:dB6mvyY1 /&{qg]P;IY87 OT4N,>3lBsl_xEaCfUxLAo=O@?6*+L-B>9k-'.ڍvč F_ njݻ12YU?{ݩ3},;}#c?BiGycO_Tu•AQ0%V7Vir1ěd,II҅t"HGҕә d TH^= 8PQh9%d[D&u*>u0ڻ PG#нذ/<0"N5QA;b]W1wGIwcc~6|㿿tnv T>b^hE k <!{4J~t_lǹ==1CLJ|]Na:ahWp(׉A\*%#nnr&y'#6ǹ 7p}p+CLrLJUװ)%%]cd|BUcɃG/[uat0uC7wc\~1Owf!8̷J~=J@OZ/II"$t ]H'ҍӑte:dB65#1ga!C-QhIu8{t.U% Ѱxy?bVt̼ @ګj[|QÑ{6//֬={=uj Y;pO{Q3.$JEŐK^Vg^$ŷFg#GnޙgXs}霮׶k= eOC4j`9@xRD\ 69xY u?<y8tÉkpnOtk/tOQh Cm0 '"1(6D_ Mj kgq=M֡(P"5Px d 5M-2ʍJz"p٨1nm zQy|5ԖA5X*|!ƀܕU~'i)B>(35Ѵ+:E{|xY 5T3, ^ēx E2I6@NnLGҕt&lj7|0\'``K  ͜fTtkם0Ew2Fml\+>SS^$xV0﷘00"8{hH-^_vѧ83+x]?"'!pCxb>D 1ɟ|].^Swtl6qx#,\XJ%٢PLr{^ -&,MDblveyI81O [n.(s7 ̃Z#+f }w^.ob<ObYX>;*,t DiwpciG,۽zD߶ƌ` kUZ=*CeYƃxO`H&&8]NHΤ;@M `*PA1ǫ `]x?-C7D0\? ""~W7ɀE9=12iρKwdndE]<`r7dCRqN ~r`3=wY-a|9sa!YaEqʄM23!n1ؿz^oU>|nrزڃ]j+G]αxm/FQc"a(d0d12:ed$5,x*BZ_pX玨( 3g#lV b#>Be?9VޡyCw.Jv)!>="CIfC$P*: Lb/vLϋn;5Ge,=CE<7 Y$d D+L d D54_H@bA9 @A/>z8-.Z}mZr $b&{ hG1c3k1(02J+\b^Ko׶񘧣pBR.\-߭rTbG!bsv@xr1r!bDX,eڍKE.1vv-QI!)GCTSisT `C * KT$}8FGuke5My,!aā89I7Lē'KpN&L2j C2 ɓC^+s8h{WֆgA.BBQD,TLr  @Dl؅yf/rQW>;{̉9g&$Fh]g4ϟYQ!xyh49 h>zӾ;,kC5Q^mC>b/u/sSV >4>y8/_r\es\us-&nms/'[V8nϴ-8(42/Sr 2aD@D?mKD&\|.jDSɧZ9?vR#2=|HM"%+ 0qftg^RtL.plfz wNOo~ m¡a[Ovk1MGB~P$zGb>5ݟ+HǟuEJ_;Po)ipmLӆ$mX%ӃH~J.N-tr8=n/I Uõuqލ7x30' Zx]Ͼm˯!k'BD vm=O3 1iUhA0h1@7g }D2 E.DI0s@yB|i|p^2,.S,ຸNm6q۸Vn3}>q2# U:tG1|)y8ڙB3% _ؗwFIBlD| B_4;E$ȷ%V| UTË``t'b'ERfc󒉶"ŸEwQA>NoA [}%iOy4Fpn.؇ u&k66/h.j ӓk}PwiWLگh(=7@ݿ|2jg/->|%l.E ul8sf->L۷,GʕSs7|zxD7t _xqrO%q# xIpYS.)OG9=hʼ_fdr6[Dh#(6!Lجdߎ,X+'Jw"Gcpr_(a\m !Sد~DIȚJѷ k:֊9}y;Nc3zAqO~mCIvmFC__֋bR$iYɾXXӱVO+5q"mӃkʡ0#a (O1ktlԬǛ6N(H3\[_w`Ϟc^l۱W/0Lx2)q5NzY%4jg&I1n'މ5axޝ.x "Z=-jS0wHkKLj]e{OmQwYy_ y8/_r\es\us-&nms/'[R=C 8z2)\j{ph||=r2%`LLLXʟd&cs}:ngd<$^2CNnX ZXd_ nau00@ES/H"dlNO'qY٘E(5| jۈ#עZESytN0h0v}ҭsgL"dlNOڭY٘E*q5NR ~k{m tXT m#lf a:(7Xh]햳8zKGwLt-0yy brD.®pX%<>& t !j4RHx.|| Gv]sƼx ^(T䲹亹 nm6s۹-mLSʚ ZqfkL+ J&o覍"g_~'S׿qQw(gןTk}ķy]^ /TBqK6s۹ Ld^ߙBEy ")OiBoi䷋? +ןT|3f kr}>q߸z5L nƫzu7 Q5L.:nnm6r[6#0',Er%hCvpl)s8 M&3OOnGl1sOj#A䄿WVw WD@p\Vf;~yrdӢ-dp5$[k!֗觋S8RQ+nMW=[ESlLO'S+PcNtE.䲹亹 nۘiWsFGHY?٧KK [X1)SV3dH@f͚5Cxg` hjժ7Z[W\Ex2bZ`* 7)&4$v1fn N=7p\VF?\^g19yಸL.ຸNm6ežpot3=3D39XMv&VS1ϚG鿰XY԰xǥuGݚZi)c8,/F;7ʍ6%ɡ6DXh[U6ediڈQCǽȟRpw-Ö;fW3| _epY\&up]\X7mJFv}>}>SSiߒiVnIn7bir.)*_\*Wѱch аqx61y4rt (^Ψ_ tM5;}1+i_Eb|>HqA~$ *CD5< WiVK`~u,wPC~Qrڍb[aYC? \1yoCjh`:Ƥ_-k .䲹UίkfF/JjʄkiwaB2)_TjLN&uAmo6A$S2:7oID$o"ڦBf,;rʴK# dZЕi~<)v BQDA 1r&WQiI(&&24 ddz(rJrEB t*#q*n?;#$S$?!͞1 z^UBVUV 8OI0liTfHe)."I}#E1vTLjٌQ[$5JA,kшPZ:W=[1&DZLD9SJ"$d,|0QJE(GhYm}v tn)RZK:ׄ'22-Z6Q PYz2d% dFU5$JˉcX1q,I(Jl^ZӲEVutծ!91!](#itme҆e$dbS3 jI[J+JsA&ȉ4kvsxjUS}뢟=4}t8]Th2 lO HD3P%|LQ\$_;2/}lCu0X :#HǜӈjU%i!lr`b x&2cd=A%J{ iڂ[69>VڒcfcM$#\0Z-1Z#Zӹb0 Y9>!f S2A-de&1$J[k?Plzr%qlvv'mƄzD7I5ǜFP!MkXz9YI ̰)Z[.(VHb9A=ii9LMAv^ʩk,6hUA՘|cۋd3 0Ӱf8,xn~ J>a얢9Z5,y|HuZD61(ɏ"1E&Ï}B&j9P|?ךkVclZ?6쁹9bA"q8MOynIXn4\D$3 {Q Q-[џlQF4CyUM2yB@63ӓr;(!&xXlE.8aCɟ k9ɷh4rZG "MO4GY r`keF"6 Ö<ӈDMZpawL6 2]4D7*@-y( t8sk cM$JUC dSpZ~FaXmLgnR5e&Q*#ey&he1VIev ]]\Kl>4b*pY2AAOA[13S30ljF~ _Jfۧ L_e5 oh1A tM; B!5 /N9SLCL>҄襜ui(fȻ#>&9s_5!_-~JW.CSL[ y2}r"'ٙ%NwNNc)&]D&lW1c%N'L?S7d}9~!%7 s|k5Tw6 KXc.E- >f5OAlĥh)]Two҆\I046|Lhlzhʏ` ZwYB8„cQ.c;cӓf]G(.Ρ\#=7B"$$ ?p4;q/`@*F1H؎$c c3Y܎TR 姨ijǩb%-ؚ( 嵛Wư9ؐ[[\^ؐPs|lv2q|ul%M{+P?Mt$]Acyć+& ?f7MVe<`kUaU e歞L3%J"ɿ [Da&+6"^ͤ] W0ljȁvr}>6;Y1xQ]PDu%S8pL#IwDd'yU kFG Rc֫_SLh%ʩ%a%^'ʏL&#/)U91Yy])Os|7TUi>WDMP;ʏ*d":bai%L<֠Z,W2Pu$J_~Lus҈ɜ8y2w4)_i|<) 7][BA/g(y XA#E$;FD Eѹ1!_O^nҲ==H#p)A\. B˵6 d8$r\k:7Zmu \o*b`ꍊ¬Y0{lغu+΅aNxڭ.M-6aܹbpL4 Q8Nާ} 6oNL4M'4s?_?S3dWmkznCb3]AxRЍ19 Fn<Ү@$!;[_ǻkKxw"j|c 1?/qq={=?hNB: x"_.8̿?Ԩ]tnO1o_Ӌ3n0_`Xp#mKvW'zXfcU[*pQM5! 'u^R >y(|=KaƉ*U_h5RB.zi ܃Owu/Dǣ&x$pQɉDҹD&ڵk? >"Cq,W.hq4n(]`b9sl:w5rM8&[_`J̿.`_Ez8r+&/\%nZƞX0WO4,ˏ[ v5ǵ+ۡw!# Q }ϊ0FNĺ;up^N!oUE~9q=%<q3CGD=bk/+$L{4IH~wgģeK2?G۩=DF 5BVF} q!!۵k'@qfΜӧcZ kk̠_Πs'*4@zO>NM0X130L3ggLGi`f??3gȗSF2Cc^c,{xu9i甦:/<:?mzN}MK\W'M"_x0iCY}$xBo 8|"O(^n||'/6óDGDyJ3Hz <MI4 '/bĉ"D:7oW0C?Q'??P4^ n&HSa&G\h0 :Y'ȧ=v06#^OpY1‹ф',="r2Aq[dE)~pX(x! &H$鐠AُskVI%\9Ҕ;#Y</ _l0Q/}Bgln.1;&MH=qo_{~߭z'0J4UEܴej;b-]& C IwF~SȲ_~(!TO59OgIBÓ5exM9*L,DD~{ W0ƹfCeW"\WTDAZq='oµ p:i*o=q0/75Ex֩xP! ߂!򿛆C!D:ˊZbB !eDD@;K) B`BrkDN[^ xD(\Uw"ǘ:TFr7sib˾2^6Ѹddn7ŃjH:ʏV 7| >^<|q,64 ' L &DD"m<$R^_ *\/[N|ݓxA&IG$nHxjx6p]+ݙ(P_oѮI9U W#3Gqۨ+&|T Rë}ձoQ<: I]pg&G@RI `w"`"`"`"`"`"`"`"YFΟDY#qz~˜׫|by(x뜫!`ni QBE!0Ś˔U'u~!Lsgmk/V pF ֐۰4u']%H|؝h"S?&!^l2xJ'#?_B@_"/ЗGoƾD@_"Y R}}F@nN)`b^ v[eݟ-Tp5VP0aiDܮ +@1907P j*ԓl+*8S,z ̆dxw4) H~;"@ʶg;\e rіjHQH wpZ)vc73݈D@KEdnJBJ=k$ &hz$dddvgsȤJ9OKcջ:YClv:[`V$!.f#n5& Cx|L0 %ݜ2\lG4^lEPN'}E1%!'tyC4y괣km&DvG,\mrml;" v)DvYk((#"|h!6iFx޵W Q@A" S/7yDgz9c4qCvS|^v K0&D>[&$?"pcn'Z{+JJx!|x1v &K\Xw^oչ5uh?~cǚ29RѣZfyСl{4JsNwvR /}6LZVSÚR-U%aDJT""`˯7䦘҄pC.9)ߚCht%q˨kMOFM "co"cgT^" \TLiX b,EG V_I`ay+2q %p1#?/Ǚ ~_I%.~ogϞ͵cǎʕ+͜9sŋDGGO_būe˖5k֥"4&^F 7ө3'# 9ZbDs")?+npruՑ/33J8Q9KH^ZT]1 =x-/2O5yD%X A',%!;hw ^o&+D zH hعx49jxiǡ֓kߴ͛ׯ_/aÆ3f 000X]vGDD|g >80wQ f(9.WCYZ/~bf@yA?s럗|.||\M3AF8nW,~R`HI3AC^# l*,9l*֛و gCdI[=PX.6xu" 9>r&~/P5[/Nt6lٲe˷/ ~є)S0bĈk֬AB2nj@yNxGDwrY:mKpʒO_=2GаyQ uKYWp ׹ p0&| dks v=r8Ԟ4H$w7G8%^uqgQî.p|Ly\Džy vIg |5" "`o0~,?p1U|LQ1;? E-]Td1 Ա[Z&wJm>GЎ:;%nrd3i 8O'O02 *# 5aE 0"]xKV=#Ȓ i+mڠhGR#;9 n'߶mH>}>!R^Z6*U|< ˇsrm:W|FZU7+7y 9>&"ND2"$O4x($TrziXu1Vo)W[wvqkwIfg{Mk;UNڕ3zɊzv.*m"Vٙ)/  =^)&\4Ez%L8{v ̮u""~7: Ph":`'P_?/gu*mX>)~=u~Zeҭo4@ }$D.~gSIu4 E: &J"ɷKk`9k]_ZdȲ@7!`IP}l$:΄ UQ" 64,o XaVKnƀ3: w%Cإ^%9_\/F3nL39%R &P6`s3B?T&D^L*)af_p" RytX=~OO9\tocR.nMYit#![F* kT!4w-NTiU UR\*v~D> Ӄ- 'Qn }"`=f>*#|NT"<XuCE_PFtΒZY`/PYNگG,$yCd S v~a1 v!=xDͦhUe%˼d%-gZe2O\G~/=WoleżvNaT +)r[,O$ w\WDMc4L+';mQN.R/;?Te?-=c^AԂ!_ :8,)EfS"4#-\zV+k~j4?B ]yUL:Q(׆%`\jg)=3}*)WW$K 5 Թs< yẮ"j. {+CA_`p~tri/D%!2.WOD"`Zy 0xfi㊕3-i#gPMJ"IF&TͰ(} S/-*r˼d/6KIHr~ _mzW_E%C#-/\I$Ɋx Sk-6NbR1 3^D<-aK%q/^(TI$:uIXvb~H9?mxD|a9>k_h:*tS%$f)8htQs`):PS_͕ʒ$䏐+<3O?Z]y VB~͑D)&s:Rmdb^ XeĜDӟJf(J"J`]3cv%v{=+~Ğ@a8ZՀB! %DeRѽnn{m>v G}0sچjݺ[}% >lAFtt B2%['ha& "IYtkv]kl=pYsPۡ6SѾ.[ ֛@s  8j/g m'"IY >lZI>q1t6kR ]l' ]Aym҈npsEꛬh@D 9s̑?~?y?n%Fm!ƢCcN'=< 4I;Ŧ80V̏^yYoJ@Wn,XЙUP! E?IC f-A]H E{P4Ocf"z {B`dLʫBf={I¤*U\/899Zj077G…r78ڈI-nE;~-jMG h<{:-!߼l <Τ%۬ۍWhwJ|s2,#Peo>[n>^~;w`„ y"A*UBѢEY#h~&E;W=d.BQ` F1QmLԘAN#Pwrcb|mBנ*򑂥wMR"pt*D(н|5j'?aaacccpncB(aO*w-B5g E|}a(]XQ(g4v3q߼l=(=y: z5qG5 %ٺcTumڴ Ƅ={Οhee%5! ,&x}F+USWWMZ]l6fS|a1}&,gBpT?銍g?:܇0<8Af){k((wנ=҃gA+Pe~_gKKtDÆ Eխ[%J*A"X3Ic̎/9ϫ"j2?ǐ&!Sa:) &MV~t(ʍ2&q(w wPk t?{ͮl| ZwB6w.2@6^/$}Tv,0icc˖-˾` .hMtpQsڝ{ԬeB}wװemQm(v+={LB3}Z )o;Z(ݡlkOIHiO}?kM~"^…Q\A@QӱcUsLsS()WP[g+mEqBZ G{]^G_X{AIOBr_Bvg`9:v(N=0ի'^R<_&xqw^*}"* ArR]V3"&sV%8%j%E!%3f D35?i=;;;T\8SL@e*} jl8̜k֊+d|Nt2j[r7 FκRR!sҥ?,l֪UKT&'L *x|b8E °STD kxX8&ǢXC;yߡZlU(񩉖^&4xLLLRȷ<2 Ļ*>TeSiDJn]sz3# C+a6p@ ~H>րNK7$:[N&J(/dI2&!tL8`gy&#WKҩ}BVJw#:EG+`6w:z Cg~$PM0,E~(/dI25kD&[jpkA?o3fm|o ]"F=1i8_iFlؑl=2MF} &BZ{ziTrV%Zt5"O-[75ڮh f}ðq_lkĩU#zwֲX/qנB֤_ ɒHI,:3B%JYP(߹P_veN03tuV|b>!m*W5es_Xh[ߥ7$K"'1,n5l7h;U짼ƳҀi OĜIڮPɠAe&W3%}ǡPdeP&`c\&oHDORCl@zu >Sv(Sj־ɯie:,=t@RKPc"hD0u7 C$TӰr~YKgTBQ\f=ٿ)VXFVg蹯s9 8 zXηA"$?^9p{ùpW\FгnW(($*|HfhZB5tS$gOvl?,^~mBEMD)V6~s4O*yn$H"$H"$A]$D/$RwK!wjղlcʇ?5+6lUA_?t$D -jgĴz[nnc~r]c;ŊIwNIx9&|9>Q'tي\~|eнy *fK7t#&#QbG:.II$*n/ ۴&nS##Eʘ@&Pro~`= }!=xI͖D~.t}j. ԑ$d" =ڍ0HMw1{*k*j`h(dbYQ}){:=c'Q9>!fLIjDdW9t{h7cpp~8x/6A{C 11ߘt8d4 K䏾a`Mj1P$d t,u;1nh8N^.%*q?n "/>ÊϰSDǚpZ4G'dSQnB1sC.K"I«Z屴ӨYobLj{qu\}c螫9ʦ*f8:S.O)#jRQ?7^z軯q{yO߉$lޥ{ZՀ~AchBvmqҞWh73n$dD{9?cvmLY^N CGך5" Y > B371\yhh^Qgul\,$ߠ{=.]w5mZ&ds}q!>,NM ^&i@I$ɢȄ)kY sΣ72܆䅨hSAAV?hO8|@PQ.K"IQМm๻FA~Fl8)N?0 aOȁ1$GK9 8![s/WP٤,$Iey /!{ ZOgnQ" _ܳ GGGtI0z#.U5: {yK7XI2l \zZB"߆"UP 2 l{9lxi,8ӁV̀2(v ހAeyt!; F@@41o940Kp[4 /x ӎ?/; /2e<`|:Ym4LG( zNMFGWWݷ5hvu#U1(83_>Ѡ\ #aSh he:ܹۧ=i˾4ЁN̷6=G(a״S-)㴌2N%fږB@}Q0 F(`Q0 F9L(Xx5 ?IENDB`tiled-qt-0.9.1/src/plugins/replicaisland/plugin.json000066400000000000000000000000321217502731700225170ustar00rootroot00000000000000{ "Keys": [ "notused" ] } tiled-qt-0.9.1/src/plugins/replicaisland/replicaisland.pro000066400000000000000000000003041217502731700236640ustar00rootroot00000000000000include(../plugin.pri) DEFINES += REPLICAISLAND_LIBRARY SOURCES += replicaislandplugin.cpp HEADERS += replicaislandplugin.h\ replicaisland_global.h RESOURCES += \ replicaisland.qrc tiled-qt-0.9.1/src/plugins/replicaisland/replicaisland.qrc000066400000000000000000000006051217502731700236550ustar00rootroot00000000000000 grass.png island.png sewage.png cave.png lab.png titletileset.png tutorial.png collision_map.png objects.png hotspots.png tiled-qt-0.9.1/src/plugins/replicaisland/replicaisland_global.h000066400000000000000000000022701217502731700246370ustar00rootroot00000000000000/* * Replica Island Tiled Plugin * Copyright 2011, Eric Kidd * Copyright 2011, seeseekey * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef REPLICAISLAND_GLOBAL_H #define REPLICAISLAND_GLOBAL_H #include #if defined(REPLICAISLAND_LIBRARY) # define REPLICAISLANDSHARED_EXPORT Q_DECL_EXPORT # define REPLICAISLANDSHARED_IMPORT Q_DECL_EXPORT #else # define REPLICAISLANDSHARED_EXPORT Q_DECL_IMPORT # define REPLICAISLANDSHARED_IMPORT Q_DECL_IMPORT #endif #endif // REPLICAISLAND_GLOBAL_H tiled-qt-0.9.1/src/plugins/replicaisland/replicaislandplugin.cpp000066400000000000000000000245651217502731700251040ustar00rootroot00000000000000/* * Replica Island Tiled Plugin * Copyright 2011, Eric Kidd * Copyright 2011, seeseekey * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "replicaislandplugin.h" #include "map.h" #include "tile.h" #include "tileset.h" #include "tilelayer.h" #include "compression.h" #include #include #include #include using namespace ReplicaIsland; ReplicaIslandPlugin::ReplicaIslandPlugin() { } Tiled::Map *ReplicaIslandPlugin::read(const QString &fileName) { using namespace Tiled; // Read data. QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { mError = tr("Cannot open Replica Island map file!"); return 0; } QDataStream in(&file); in.setByteOrder(QDataStream::LittleEndian); in.setFloatingPointPrecision(QDataStream::SinglePrecision); // Parse file header. quint8 mapSignature, layerCount, backgroundIndex; in >> mapSignature >> layerCount >> backgroundIndex; if (in.status() == QDataStream::ReadPastEnd || mapSignature != 96) { mError = tr("Can't parse file header!"); return 0; } // Create our map, setting width and height to 0 until we load a layer. Map *map = new Map(Map::Orthogonal, 0, 0, 32, 32); map->setProperty("background_index", QString::number(backgroundIndex)); // Load our Tilesets. QList typeTilesets, tileIndexTilesets; loadTilesetsFromResources(map, typeTilesets, tileIndexTilesets); // Load each of our layers. for (quint8 i = 0; i < layerCount; i++) { // Parse layer header. quint8 type, tileIndex, levelSignature; float scrollSpeed; qint32 width, height; in >> type >> tileIndex >> scrollSpeed >> levelSignature >> width >> height; if (in.status() == QDataStream::ReadPastEnd || levelSignature != 42) { delete map; mError = tr("Can't parse layer header!"); return 0; } // Make sure our width and height are consistent. if (map->width() == 0) map->setWidth(width); if (map->height() == 0) map->setHeight(height); if (map->width() != width || map->height() != height) { delete map; mError = tr("Inconsistent layer sizes!"); return 0; } // Create a layer object. TileLayer *layer = new TileLayer(layerTypeToName(type), 0, 0, width, height); layer->setProperty("type", QString::number(type)); layer->setProperty("tile_index", QString::number(tileIndex)); layer->setProperty("scroll_speed", QString::number(scrollSpeed, 'f')); map->addLayer(layer); // Look up the tileset for this layer. Tileset *tileset = tilesetForLayer(type, tileIndex, typeTilesets, tileIndexTilesets); // Read our tile data all at once. QByteArray tileData(width*height, '\0'); int bytesRead = in.readRawData(tileData.data(), tileData.size()); if (bytesRead != tileData.size()) { delete map; mError = tr("File ended in middle of layer!"); return 0; } quint8 *tp = reinterpret_cast(tileData.data()); // Add the tiles to our layer. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { quint8 tile_id = *tp++; if (tile_id != 255) { Tile *tile = tileset->tileAt(tile_id); layer->setCell(x, y, Cell(tile)); } } } } // Make sure we read the entire *.bin file. if (in.status() != QDataStream::Ok || !in.atEnd()) { delete map; mError = tr("Unexpected data at end of file!"); return 0; } return map; } void ReplicaIslandPlugin::loadTilesetsFromResources( Tiled::Map *map, QList &typeTilesets, QList &tileIndexTilesets) { // Create tilesets for type 0 to 3, inclusive. typeTilesets.append(NULL); // Use a tileIndexTileset. typeTilesets.append(loadTilesetFromResource("collision_map")); typeTilesets.append(loadTilesetFromResource("objects")); typeTilesets.append(loadTilesetFromResource("hotspots")); addTilesetsToMap(map, typeTilesets); // Create tilesets for tileIndex 0 to 7, inclusive. tileIndexTilesets.append(loadTilesetFromResource("grass")); tileIndexTilesets.append(loadTilesetFromResource("island")); tileIndexTilesets.append(loadTilesetFromResource("sewage")); tileIndexTilesets.append(loadTilesetFromResource("cave")); tileIndexTilesets.append(loadTilesetFromResource("lab")); // The titletileset is also known as "lighting". tileIndexTilesets.append(loadTilesetFromResource("titletileset")); tileIndexTilesets.append(loadTilesetFromResource("tutorial")); addTilesetsToMap(map, tileIndexTilesets); } Tiled::Tileset * ReplicaIslandPlugin::loadTilesetFromResource(const QString &name) { using namespace Tiled; Tileset *tileset = new Tileset(name, 32, 32); tileset->loadFromImage(QImage(":/" + name + ".png"), name + ".png"); return tileset; } void ReplicaIslandPlugin::addTilesetsToMap(Tiled::Map *map, const QList &tilesets) { using namespace Tiled; QList::const_iterator i = tilesets.begin(); for (; i != tilesets.end(); ++i) if (*i) map->addTileset(*i); } Tiled::Tileset *ReplicaIslandPlugin::tilesetForLayer(int type, int tileIndex, const QList &typeTilesets, const QList &tileIndexTilesets) { if (type == 0) return tileIndexTilesets[tileIndex]; else return typeTilesets[type]; } QString ReplicaIslandPlugin::layerTypeToName(char type) { switch (type) { case 0: return "Background"; case 1: return "Collision"; case 2: return "Objects"; case 3: return "Hot spots"; default: return "Unknown layer type"; } } QString ReplicaIslandPlugin::nameFilter() const { return tr("Replica Island map files (*.bin)"); } bool ReplicaIslandPlugin::supportsFile(const QString &fileName) const { // Check the file extension first. if (QFileInfo(fileName).suffix() != QLatin1String("bin")) return false; // Since we may have lots of Android-related *.bin files that aren't // maps, check our signature byte, too. QFile f(fileName); if (!f.open(QIODevice::ReadOnly)) return false; char signature; qint64 read = f.read(&signature, 1); return (read == 1 || signature == 96); } QString ReplicaIslandPlugin::errorString() const { return mError; } // Writer bool ReplicaIslandPlugin::write(const Tiled::Map *map, const QString &fileName) { using namespace Tiled; // Open up a temporary file for saving the level. QTemporaryFile temp; if (!temp.open()) { mError = tr("Cannot open temporary file for writing!"); return false; } // Create an output stream for serializing data. QDataStream out(&temp); out.setByteOrder(QDataStream::LittleEndian); out.setFloatingPointPrecision(QDataStream::SinglePrecision); // Write out the signature and file header. out << static_cast(96); // Signature. out << static_cast(map->layerCount()); bool ok; out << static_cast(map->property("background_index").toInt(&ok)); if (!ok) { mError = tr("You must define a background_index property on the map!"); return false; } // Write out each layer. for (int i = 0; i < map->layerCount(); i++) { TileLayer *layer = map->layerAt(i)->asTileLayer(); if (!layer) { mError = tr("Can't save non-tile layer!"); return false; } if (!writeLayer(out, layer)) return false; } // Overwrite our destination file with our temporary file. We only // do this once we know we've saved a valid map. temp.close(); QFile::remove(fileName); if (!QFile::copy(temp.fileName(), fileName)) { mError = tr("Couldn't overwrite old version; may be deleted!"); return false; } return true; } // Write out a map layer. bool ReplicaIslandPlugin::writeLayer(QDataStream &out, Tiled::TileLayer *layer) { using namespace Tiled; // Write out the layer header. bool ok; out << static_cast(layer->property("type").toInt(&ok)); if (!ok) { mError = tr("You must define a type property on each layer!"); return false; } out << static_cast(layer->property("tile_index").toInt(&ok)); if (!ok) { mError = tr("You must define a tile_index property on each layer!"); return false; } out << layer->property("scroll_speed").toFloat(&ok); if (!ok) { mError = tr("You must define a scroll_speed property on each layer!"); return false; } out << static_cast(42); // Layer signature. out << static_cast(layer->width()); out << static_cast(layer->height()); // Write out the raw tile data. We assume that the user has used the // correct tileset for this layer. for (int y = 0; y < layer->height(); y++) { for (int x = 0; x < layer->width(); x++) { Tile *tile = layer->cellAt(x, y).tile; if (tile) out << static_cast(tile->id()); else out << static_cast(255); } } return true; } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(ReplicaIsland, ReplicaIslandPlugin) #endif tiled-qt-0.9.1/src/plugins/replicaisland/replicaislandplugin.h000066400000000000000000000056071217502731700245450ustar00rootroot00000000000000/* * Replica Island Tiled Plugin * Copyright 2011, Eric Kidd * Copyright 2011, seeseekey * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef REPLICAISLANDPLUGIN_H #define REPLICAISLANDPLUGIN_H #include "replicaisland_global.h" #include "map.h" #include "mapwriterinterface.h" #include "mapreaderinterface.h" #include namespace Tiled { class TileLayer; }; namespace ReplicaIsland { /** * Read and write maps in Replica Island format. Replica Island is an * open source side-scrolling video game for Android. */ class REPLICAISLANDSHARED_EXPORT ReplicaIslandPlugin : public QObject, public Tiled::MapWriterInterface, public Tiled::MapReaderInterface { Q_OBJECT Q_INTERFACES(Tiled::MapReaderInterface) Q_INTERFACES(Tiled::MapWriterInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapReaderInterface" FILE "plugin.json") Q_PLUGIN_METADATA(IID "org.mapeditor.MapWriterInterface" FILE "plugin.json") #endif public: /** * Create an instance of the plugin. */ ReplicaIslandPlugin(); // MapReaderInterface Tiled::Map *read(const QString &fileName); QString nameFilter() const; bool supportsFile(const QString &fileName) const; QString errorString() const; // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); private: QString mError; // MapReaderInterface support. void loadTilesetsFromResources(Tiled::Map *map, QList &typeTilesets, QList &tileIndexTilesets); Tiled::Tileset *loadTilesetFromResource(const QString &name); void addTilesetsToMap(Tiled::Map *map, const QList &tilesets); Tiled::Tileset *tilesetForLayer(int type, int tileIndex, const QList &typeTilesets, const QList &tileIndexTilesets); QString layerTypeToName(char type); // MapWriterInterface support. bool writeLayer(QDataStream &out, Tiled::TileLayer *layer); }; } // namespace ReplicaIsland #endif // REPLICAISLANDPLUGIN_H tiled-qt-0.9.1/src/plugins/replicaisland/sewage.png000066400000000000000000001657621217502731700223350ustar00rootroot00000000000000PNG  IHDRŐgIDATxYl[W.Fޤ WTepq<$q8r(۲A[ ' ++k{xi|zCU[<,r-oHII8^ͮ2L5;e,==4>5V;;Ç!u_^˕X>cTez=N-IV\gڎ8_cSSi^\+uBϞG33QVP*ϠĽ^;%x%;`iŻ0qhJL{"ZLH~=0YeߘOb_f/lcѥ8cy\749;7x({+g^Xa*Ӕ b`||6b V ߬vk${%D[}4m*F (+:Yh(kD31^7; _;*@\J=;Lw&T(^Q(W$t AT;6'X<!}h7w^So%`!0GJbgHFooVviX@ C41V4 @zh@umm8dxI>I =)J&x@8)(_Z>7}mGIR,VcqZ@gL8כn /dkP 3=۽q%}j6PMVs7ou`{*shtʭ׿e z.,w!&?!pbtG3 3Zvw~Ǐ l舠goi1 ;~ 5D;R W3}'̄fb|pUI{oo庆{~jf&bϪ\_&o-$!f'Q>P۽w Hӹls''Vuja$9Sa.5짬B71@ IA!&ZTd߳vG_###;>=6|ONϵV[vM9JL4:jdREumʿ׋'Xwjg*ߣUg+?߰zV\w2 & bTvE 9cޗtԖVYQ.oY\c'XS.2=pd2 o>˗ҥK\?NN(:M~@?~f?06ޘFͨ?kRbMW3=)Ι{\j'SзTSF:EyyJڳaK*L(0 t8Z~[=-ՍNYj)Z-c5T5Kc!?0+-Ӵw~+ E8Qg>>ޟ3KZvzD۠znoj/Ӵh!CDΌsRBo)"L7YMɊ`ъ W6`ނ?ޅj>8T=7MeeTPP@yyy\233upܹ5H;@q[V$B$[vob$|S"hs8Fnsfh`J3)QجGhdfFf [d+cɡ<*@no?Vjqm[,+}30zߘ]k|spN(SvGLYe`& (@|r<܎90z` YXpԜuV]?9U:%3xҗŋ;Zq6_ j mMۀSŸ]6a ăpmt?|zode"#.WzohzkjwyHTO6Rѥ>MqX 6L80YpS/*D&R NlEwWMc` -HjBL~ G ./Y`4iKʒ>чeSOg`^PS{8pBMx1LwJ'c XLB˟+7"@>\.y2 ߟfuM]4l/L7sŚTv44kZԿp<+yG6w73b_n?,xo>Ȱ}e4oXT@G$RE'G. 8(@,>N-X pePɈ0IT@ha;LՁW,bueuYlglJuMvc`~eQW@ʷn\Bgmӭ*:[ò&ˠ!mfs0CCjzP>uOՅlyK&C꿕\KMja^J/5uF+gΩe}E?*s`D\/ߧD 7%`1Yӟ,`$)da6  İ"+ی^S%|3Fƿ+W?==}ڵki;o5RGfy G6v|()`4o±@'[Z6Gcz4000N=Mʢ&W Cl`V(_h0qZGJ1({hsc{m\G#@&0)&qTUDxX:p lO3%8Z'2F{Qf,Q->r[ސ>Jr\e`&o*hdcQ:`7p9` )0/a>VR@]l@.fwӀ\t+AVff yI \ho}V!@#ZsS423NSCܑ`B_p3@(6Ip#"B]LbG& B(Ep3ke:I;1' #1bc9Mdn&W$0'O(#7V2A1qSY{"r:b/v ֶ|jfkF)(C& qlЂ<1xhl Dli4R~A.7o&ļÀت#50o>/('CnCHK)(u~ `by҃;&lYь&Uֺ7-GO6!p/ĵU?2w##덭5v)eoE4 5 >T7KwھSVTJ/r)I}\PyϡtPm!MTRZ{TޛAA^Fn -70>J>['}HQw?G+z( TڥđI鍇4  xF*9-Xp7Xh4(J์A2Ē>` l T*A,cA"X1Esn,̌aaWA҃{ `vTk"̏Hk߱z?Y=;~/RBQ>DXEf ~fݨ % tRNQaO)3T l]{ߡY!D/Ӌs4ᛠb˦vwyɵ!f᭤nn1VW6xᪧ\.Ȑtmkd\I: 4PT~_ҟA5T;\/I ?uwکeNA@$}VQEi0VLֈx,E[bc5߈pgcJ0:=A%WL=rq6d;=GlWKhrƧcSSHʲ7f`Q0υ?/Ѝ} Y>Xb)NsD(Xo"*$p"209t ˦QѝM壗vSʔ~1Mws^蠑N<|\%uWnlqS. Ph9SǸ?Pp+Ew:Өr ]Q08Nӥ_B7x*"&눅ݽFG:"X-|UVZ`TnC },'P ̊+2dT@s18ſ4?!.=m:-7v}_Ή=͝O1*;MƹMo83sp<v4P#mݻ~#'Vo!#a@g_9ROx!B?9 FiXH'48:Cv&0?1ixjn͝_Ἣ{i?AMPFT=| ~ g\iOmSea?,Cj3Y$L0cSQdز*v9QW T9N|? z<;Jl0 9,L+ڈ^âΎrEXdC\t>wS OJɹ (πr3$1r83 4ȻY@hj]uӌwf4: e]5uz49x+APpOǨo!8pLni @EK>_^y*f֣I&w|MW>)ao|}nw NV3- )g0wtɴVh*YLGaGSS2BPlY6O4V̏D={h yuc#ɸnf1HI}%45NvX8s/_&߭=:OBHwdzqf:pQ' lDo~Oi$@ӭVfƔiݷgKyufT@a)Fi3.aR`buUsqA ^ovơ sC P`41P@ bxE/{rf_ˉl`^ 4BvޒLe<:9 򃈜$+ 88wW)崟t%r/#AuoluSh^Xcxb i+.#lvObGjSdʿH!m2w58Ӆ+_2W4rKt%7ͭ Uq :"Qnԥэhn5}MOҝtݭ?MYu|N)8ƥ^bWݟSv'~{vl`l3XFʨem7\?M9^9] __'wr`\H#spn,fJΟDK: '@"2x rUx#QP?8ALU NI[zIXCINhk#l6,MD3ʚ&R-+,M^=SHkjYcAM[$A$Ś5t* eo`^yH(58uZ&Ji*רj0FnpuOP-:y4Av?_H @L'=O9St@>J~4 ɥ+o >[M,L:XO\lxxA-/ iLUFGkv/& Z>SCÑmQpu</E rHPF,yc},琓,VTPYe%'E\dhJ͛ ߟ[^~w"v$3h5}wܯ[w+5OgfZ H.ݽ'5]!"-v-qȩKhhz@/9ۮ?PO2 [<2bbOuBk-GΓ}+} ^[ z`lz4"YPrY0< -9t :BAdf1A&h(~[MLG"1,@X00@d/bH,򴽚BHn{_86(x4kfK e'CgpK߿s'ɘɟb{{,g@Ts<2akkb,Д5TVw':of+jd|+s! 08%P1Vn|;NT^|L~/@f/³ِO†i^lT'-.V[ҕ*5!9[v4rrsvV֚xV?f?~9I5ˍp>5#6bo\NO k&7 g/,H @R-Q(;`./z3IJ0>=`5ޔkZ& },d+ʍ?uv }s=:}M}AMqcޗ4oFM}7B0^W/k.2<3[ `W$`7U;n&,D  %H0<-!,(?\ W 4F7)G@40u8n`KB8"޿ ~ܹneeQ}}}vY7cAbcy4Z f]Yd#'ord?OVhz^";x&+ N+(hom:+m0@( < P` _u!'vrIZ|gҢNAx\XSGU߾V&m[Xʟuаg<Ћc=01ceM=&tfwcv⪝Ηk װ"iۖ *)H W}{{WUF;[Z~̵sQVPdg(U E7cܣ6.Ap٭H{vZU}!QCBm9vnV=q/Y&P]QhVT~,z^y)gBNJWNk4%&bLb ],1fc2b!b m/e7<%L:zo.\s׿yEb oREdeGzv+((j53ؤ0r4Lha?R{-J\jeY{|cm; #?^q~߿3s_¾K`m5?V;eINmyQ,Fplɴl@qw=` HR,@v9oL7o3ɵ8#Wfƈb͡)yf =%(FE&Gy7, YkEjb"01X|e񖷃4+ߢ {%j`Joi rOv[8&~ʾJػt-#F̦܋"9>Nu z3 ,Uo#Ȁk;3nW<} v& S *Nnh;EHHIy4U ϮHl@4Yl(PI >yKkn^[^0@qg %n~rDiО?J4 16m"k1ӟq s-!6ymJёd dLNC[te|9!*/'#zhl1fɗxg VA6?D\'i ` Z۱ݻwh$ׁnWJYeotAÅͶs>{>~_\6~&y% Pp}SuͿeBUTIKEe_SvcESAd|vފ4e9:jϾm [%-11ĸ8<!bd_*5)m`dPА2!"kl_=H0-oDaR͸i;e3A--J@~2<>QΕ 7gkl gU3%%0|_#VnÍ Oc1RG[+c'Z}M~1_I8I!8@O!ح;KW~SG?+{owԘkf!ǝ}ϏhqmSVö?a8Z ˕/3/@ ȿ` bA}T-ETNDEEE{T!; лt>]s^ w S(`ʒ>N/oW\xo.3,vƥefH/@pXy¹O7?>'QzZF|j/A}w(:o}|]tOݷ>\ :g.rw2zFMu2Idy=x}!EiǪYY<^r2:7LMA!3TRRrSrzc̽a_8 Gh牙qERY]M%unx Dw1dG @F0Kn,=~|0k]|gѧTzk/]=n|@@^:I/۔.e{Uq?^E;׼UwS'2` ԬAb!b,ͯ,@0`B<jl׮:Rd迤18ÔŰ)ەY-bXL,PPNKy3> Z[U Le`M65ʘ:\ } pS-E>3x5T]xxTT1mN]7?`  ޣsy}?FS|d}!|F$1?BM DKwt@@NA6X/[SU[4d-ʹwK$ 0j ϏD^^hB @w[b⚟mZ\\'!OE$˘5RqFUmb: V#1S{Dy`010H\% e'3=H/O9_4RqG):o^bg4;Tًy$Kz@-fڟ[CPk~999! >M$K F5I!h'Od-X ?QKa$p\!h(=0 Q H+Sl P_"1K6*x툶m25փk-Nuur_$ 7Ѧ|5cti ^pH˝`UD /k7~]gb!ٙg@3<]O:s}ʻm7;x '#ތ:>?O\߻kjga+tm_xU^Afe))++Wd?vd?AŒuGWzzBGJ\S!l'-/^ʌ_{Ʋg+oEJ1ynt)]AoRgzqi)p6>D#>ϰ#UsC6P?Nڿ[ߘXCVR| V<* w.m͛O*g*y>hO!*n @PjPB0G$Nivvc>Ācr՞ɺS4p k:ߦ;zO9Թߒ: ^[wD2><4/ ?hk={ `s8Ǯ߸A|!€&Iz4ݛ,"\@.vd<PKPwpk0x yVT 5;p(pAꃅFWTWsWQAt-]D^nZ?t\V&Q 0Q="2uvqNL@-f-NXĵhھt]7L*dj33h9/>@ݨ;v= "h:"1 ~/_cܰi̱z9IWܿfe=QЯhk/(@<8pUI*NP֑A`H78 "pjf)P@H7AW8h|ڥ<%Jc T$phSjRɈw$YO"c.;̱bA.. XEg&0aD,FyOuK0J@uo+,ȁ؂O $_dmLAJ(:.jdފEJn-X/ǧ6XL1Egߪ1W Ud4^vrޔ6@>@w-(=/1Ps@t_dlN7"x)np~:h+rk1<_4A⟼)h)F# ۙ,N1?WmȲ&L853ǿ(W\8oRh],;_-(#7ڟ`t{(W67Wr?a;|+q_zT-w% P\9[XcJtEXugX>svb&C%)Lpqfɿi@fV\BZl0:!% @Al&#}'oϕꗕUc70S`~/Np/MDfu7K ng?Gs"e)'.g@ xw\T' @aև\7&.}F>o),f"h|M@_BV؉a|07I˳xvܴ0 9,L])`ken,Ś'>Wި,?w I;crIQ,Pwba<7 f >?/ʇL Ôr{2ys_h[j9:%r0-'?^@4Qrw B`[J[h7NK^%`lq ~t|R؛g,'n5 EȕbgVo~/b13h(7L*0?B1*۷o>&a&uK b~'d6f2,|T+dV` V|MHS}jOegw|Wrտ~g߷_}5 Bwe硫OB2Yx1i biAНr\Y/N*059cvZf(0[xÞshN&r{Xf E5ARSU v2AӞ@Z;'4n!₍< r"=V,{;EX?8^^k?ay9y=?Z @3)}G9ޗ)17<פPVQDoӒ CEKAȂ11qS oIHgvoXci!vK"xz+/7cp!HN9C'OTʅ %/3o]"+mQa!*pc͓DͿxݰ VʗǺRqN> ~yor`ť(o(]!ꇔyw/-(<q l ?Adوojk p@DW%Khlk[$Kٷ)_7UY, 0AhEU6{Zhpr9#c9gNOY$JKcu;X辳S6gtwt__<t_4Щ(^ʻ]?6e}4? F^ۇ^G KDFAT^J@Ie ]VxkdpdFruIBbܗ-ovm YC&Tv[&c R<3$\_!_K~TUWs$$I8fD;Sy:_7@\fe$B{FD$>>3/̆{{Wa{1Xi#Rrs};Z2e0`V_ߧ܌}*ǥg~E9p @KQ_+ " |!8-TsPN>oc ogH)l%oKH'o,3bDβ(~V뛳B4#H@&F8A;06}&;Po߾CEM`%b @baBbiN9=EC<$9 3x.'gKs,Dj+nOegwQ79uxd0>6}?;*I{k{s(€ 92b+LY8AX`@23d4cMNq#t!Xb.emP%)2Sw^tЁ]q~wǝlq^ͭ#"J*@,PXYb!R=EGej%gExGh?~jKǝ LBIgſ=jw{ș{T;mj(<cvRTsac|F{upO6@+쀬Ř!hwp 7n0HCCNH{@4ԨQKUi<ȼ3.iLls;2sGP݅7T|UG_qБ. đu̡ )/+WFFFXv[$B=S &ci=IcJfao?@NfghO)> @bR~7tv ~ + (:Mn:"W2G_ ?필|wwU}F`2KKKS-feuQ%.2y~;} @xYɽ=Œ33R%pB+#)sNmDa~*/{&JЉzM7/tm4YrQchaחGWt*`}8~)0Fzs hl9R 0IX拏ѢR#kt PsdKw?p떪p4vb'uBp8\zd}t)Q=LM6e#QaMŝQ;_^|>y S˵)K/~(H~f(g_<vZkEs4n^C(!k!+Ilя??1d N$ՄZ.7Yȓeޫ!mܤ?__91[\_q.ddgoYT"K>,8@w;FG1#X ™'5Gl hū@#x@YV!խ "MjVq?+@ b}/@E333)'' gK$>w7oߦ;99QE'}i$9 L#8˿L4^ C/Хwcʚ~$^onJA ?q@k͗.mg!4c&H"2DޭȲ]ʯoiVɬf(YW5v2`?dQ{  uLh3!>f ƺXY:`mH"gS g3ZhȐ!leכl}_! oMWW/Z??x.)`ボQ&6''S8k~7%. *QJ?`f\321(1XmE7m:fSQKa,Ocl*3(dԂXL(;eo0K~Ҵ_Q= Bh&@Ya?Y\\nh|A(7!|pJ` 1?~AƃY?\$7^wrE?1/RԬE^ Ekˆ1[|{";+k4Vghս#=֊T|\r/灛=Ʌq}SJT:H_95^ٳx]%@79@6 ,w"+AlIn{ȿIJBuX+RɄٽ(RU43nG2,21H[PFP*6@v/[vvTK3_@g<c*j~T}4fgy~jjj[,䧇 Gqj /[ $j.*5zp @sq#,nߌ;0)#``Ϋ@b$rqQZG\בxwS JH$-.CG`?q?—96Uk\(3nyHK^ &ޡqwwwӟ(lWK3AX΀vA !¥#JJ8V20זg黎I.Ȫ9,9"/dn~@%ޣ`G4_}%g ߋ#w, CZ4Z&ٚ49Їҵ֑QӋOу}Fnⶅ}ww  Yk8sDf\M{QDJJÞ'xgAhmg٪d?,4:(H9?@e _qÍ#r}u/}:=?(8J'߼OZ;.@YGiTًq({>шL EZfiBe +TZZz0fpcLּCTrU*&<]|3||[2?Żr0|x'en,Wަh@82@G6ls NsOJyo3oي!ݲmcvzzdȽ׻NdG>Q=b)*f65q0e@x )r "Xr8bb׍)"=0$3K?gxQ|P.N#D6mK!J<ʊM=WDVI0}mlss㡉/;+VEw}|+7L!" P\J4@)//\}F6 Yetq3@7Z9\];r (;\_:>Xps=J3oC/\`,=N `~#J!eN_RuT$S%(![R v1kX(|"|0r9S@BH:>j,C{!_HS` $Ȼt-brF2H"QS vr#\6E GU6`7| &scCտQqRݤ[x$ <39A(%LXɓ$)`N5Рsp3pC,ؽCf5"%Q0Z9{(_e0Rr̮da-ZІ=?ZߐHoa/D}v|#"b-LB4}u8^q:9h 'hOL@D0 >k(UdOgvEgÙo1 8 Q7L?R?bڂ QF" `ͣ@,"Y 3ZɌ&X*i&"IAUn:-K<Ǽ"S ĕ@(\¼|tq8g̲3훧97--loeȞzYEMYꚚup{܉)?\{sq0}`ds:Ok  ʒei3)`T@= z<;!,,͹hfaZI3vE&h˟Q*adW)~9M\xqYU>EYx<xL㧲 ߸#V BvV<DXD % ŋ~⛟@P|6S3V,.>o~~:|c)8=ʏԪl4Eb¡6鶶X@Mpl^copG 9;D @_-c ol:[+,Q3QY72nGs d(czFB,Xb1I4 X!O x˟$Pĸ()| \PeڵU|{QG Dq!0<_SZ{vxӗUR5SsƟ8 QM #Cc *@?"40^u*ϽFgwɀ`@W5g`|\1u " 3C)GQ33 ܆V$Sj!dj.왝p Q?=] DCzzLPpTB1}+p 駼ř[ ݘ^6<|ńk^u3 EL|P^KK@HKˬg=կe 1pV(ӱ?7@._;fK((mp?LUIemm` =ӗ?U' 逵#S8h=Ҩ i/+aAP qJ7}szԏ PХٗr܎`lj*MĢ8SB)v݌pB^$D," /UqÛ]۷ueͥ ֊-3 NXaU t7/O]l"묎 8܉1((Hm.h "C3/W;ҁQH rGW,{iʙ {~*ƙ aM/LeL EԿ\j>5pm&ll? d~@PpTk?yor`ť_ӝP޷B@ݕy*:o ac;sO $\[㸐CH&)V_6>4Z%Y+LV#\D-]H&V)a;6RVkwEU2.s |i Jby/PAѣG$Z"X?RvӧOKr/AaLW{;J ?ʋ(^ʿ]?6e}4T)?b}Ef[-/¹F a0UKs-D`WL#/TrnSݧI wRͅ|߭tpa 9"b<)z{".Z1e]?^;^;)\&~'.j*qwd-D,ZV*Sa\)h%J3 ygb;փ Oe_ W] 0\p @Ks@"Gl:lT՟{`2?~n|uysk3H@qЇ 4]{7#pj7e}+x4ОG_ 1~$I';kffuljjU/iߝ_X8p8{ g{ߡt(a{;k>? l4 )pA' 6Go(pOd(gGNlq 1K4_=]zUrw~& B6h04PBً3@+NhTA?,im%_#hQ/U|?RPFr+.r8+*?x`ČVAC, .GS>xݥ_3%o oR5S7/qjϿMEPaj[KAw+z2 F\? 89Bx0VYvs"900dU&|@A3żέv饉W9 ` =)~L?s?[η҃ X %~'!Ѿ`F „6:ʼȍ7-z8 UVW떄22.7=6P!Z*]+9IvlW.!qd3bUb #EtOECj8 ƿ e~׆F6/%'Z%ݣyCs;o^^ ?&s?¯w@dC@4@ 0]b" p 䧽XHDîRFH $L]kÀtL)P3 6aW##0k  1_>x۹H@(r~\s\D83]4u2ecWE xgE%%%qqѣ ɟ)қ%@y+GS;o^B%mbߟ w;3~H #R`$ owUߦtg,%@)uf_ڟ5?USO4sP;9+ymמ 9~`Ho`15H7 v!ߡ]5j)ly9Yʙdق0 Q"albbvlᾷr =fNh ,?kmsUrݿN.k/L:Oeȯ/0&941Δ֚dBs UR?ʪu^X^ю;R-ֻe &v7/0~/W<?ZR)xVyA(m4xY}&P XxܿwpŪV3&>D(z]P_/Y,uqR W, =S/iֶ;%2"Dh,#4I{_QQQ $A񋾂Ґ3u:BGFVpG)A/Ç4`i=.ɘ|^x./ VjyxwA€L2q+|gElg+ˊE@@@r01Hu&)~&6 av,;]u^׸pDkrHX\DG`Į^ )rVJr P*bHK/';?gM棊uc2P<^gB"0FPZZjO & T8ȱW\1gs.0I1n JJ7͓i`@daY+dz)`~=Jy9X ~}8YI9ەwlȲ8t7gz pUiuf[#x+oX?a?NӦJ(}U}~_ԚvU_k OHĔ.y 1 !_ջJ/@J%c#RnH3!b#o,uq)n `Fo1q4vKge'ܴtC'_ᅕB`@w5 kxFG?PnX\m} y: {qh5̸ K3?<߿׀8 ֻΨXN 0Qd*`[FX% U]-T7F#ZoJU,W2vݻ{;rGȉ}sZ*jNY ~U Oc%DWEbuڡѫsߑ=Yq4^X:?<^ɗt={_/ZNZ,$V6}>VX%-ժ[7uϱc*Pa?:W>fHc?7؞Vx|t޵{4$ ;W>u .>|K/6/[`w\- JrDY;`QTJᾦ9c(4ė)?sƯNY,3 '_=BjE}?p<'}^=[ZΗԎ%wgbC37uuD'9x1y"\pn"dHEF(Og^ڷ?>zαБ#t+tiMG?Ҩ0nYèQD/s{xfbBnH~g&Q F~Cb70e%Cʕ+$PfW?~{NA(WG=!G"2ԾT;~ Jʑ?«O^|ѯ9(g*ǐ~|do8D1h*"Ws< cPΡadD BQo&, EԵ~=`;ܫ^8xwbȎX Za_ٷu+\r< &6T d߽ wo$`>0S3ZBW:¸(_S/B7/Fa%:{x^l}mk۟+jd^L><(M9%[(~>UAM{~ވ؏W=3lC ï ȌsF|7,-<6}Wd7&E.a au}Aoju%62㈜KzynH]je_'#W* 'l4*# 0 *k]%{EJ3>Ͷ׳2VSޣCpƌblFX&ٗaRi4"[8t{aᶝ8t1=Vn_32͵,4RLczV5W~ }1|1W3_6b9okξ(y"QJL AQ[- .6M޼ b?% *VG۽$Mx{jj +c7 >a&Rh `0 \>FWK:Rz`ޗmٶ7T0}hࣜw_v(K, p pʾ|/3g9bV]F}oMhma<Oًi\0*) {ax%_e_} ݺN_u~czMzT\<>7~Sz%&b+enӸ=t<6W1B}:UK>ˌnAޒ;3K^ub F}at&F)ukAr #)^;L'رc.V}dðό}Hm%:-s!J#> uiXnl1:|5Gy r7LJo.`e݅zpifFo~o|a R+̀WeO/^HGHFOcԖ@c.Ç[@{0gS_t)xo5>w q&_,=f_QG@u*_H&RF\e&b" PiZh)Өofk.Eb{ _ػ\g={7Dɽ|zL]C/$ pTtz5Jq|EVY;vL;_z {~8c>8|C d{ ZUA,K`Dz>|ϭ0{-xiy \ }:gTy :1pg?][E]whsQy `@9~$/`\Q..Hq]mc|4w0}׮]Np0/eyA{@׾6x mIU}%?.=p|uyMZhp<ɽ(*0qZsG+Kz{suO5*wh@x;pnb{Ʌr{>F|l PIC> 7 hIERbN__\f7}kpc/9FW`~X 9cui/1S9` }sL"[rYPVZe*`1zrIץW_<6Hԙ};=5"pb]9_yC3gH+k* 08_Zg&@H\$8@3|w13.VxW~S׮]AM̸=g ،|c/>J@"g,+xpb7v(ogbny}ꖼ 0.5"Eon|y ~c>_s G,+WT< d>&}@Z^kNd#zdc)_9Da@Ҵd(=iCeeyt|zEwQ)YʜS1)Ք _Eo~QV[w2JO?\#0HC R d x C/$xE_:ziTK2 *<RnsכaE_2e8kȥvU w)ֿ[ 4A0Ò?H2qp ]."D:ļL/ {Oo?~ 摥קH9p=j0q!iM@YOϙO# pܗ'Zw"r2߿,{W`*@OvZ|Yτw^)?~uӴ<;d`/N/\p3Fhʧ w(dIO9`K6YAo$p#v!4|@w۠g#t{&2< tzL}r1\kPEzXtb&2 F0T@%?rycQߏ*n^=ܩx%ˁ;VR[ƴszK {ds/\"KC|JaDa tWGW@Dn ӫy(U'V "}z?KO 5z!L/ gN˗\cW1'@9U_;ǤGbԏG1_>Wvcd+<Ѹ O,:@j_LȁhkrOF8GZ1`FBﱛ2<jLS9aPf4OOΛlr^|qqb{\}{`5I|y.ZamsqϤߟu \6Ԋg N.r_ZN`<"(?_| ^vޖ84ьfPԩĮob- 5Smc#XV] <$641 OK&$$Xkjj2SȜG1B-ٳg)<<Ҩ}^J9 ->%KXՎIIIIyG?ee?m^>Gk(˔I`4QUm-7m+V؟ogVJ T@Cc=e%9%. 11)c!ȣ0hF3]]BV9E\ a? Z@ 8Ԥ #R=$e12Xc>̦;&M1Y!zTQVI`4qaCfF`DofRJi Q '?(IQ͛")

-kl/3dqg}0=M3hF3'sm'ums5L۩CVuڶs@bmu/ysft u`4>`JWnUxV} hRjAA8̛YiҥPvvqM[ >OS 0ьfbїkD=¹ &N@ p0}1PPP@B f$*KShF{\i~&W4@{jWO` 2' ?LT\\LeeeUZZ:)xvQia6 m&+rG?^/ HOZzlq%Mۨ}Q i]rePYYI :D˗/:m57R˲fZ޲&ֿ 77PjaiijM̵'v_}Q[xA0F!8*1Bk[rljcL j4=~ *(#Ń*ʫN6 sȚ&4@^^ edPzvR(+++==2ӓ23'Bi122(#+)3(-3R'73#czt$/΋?+3~P&;s's[N'`-Dž1^ݔFO-Oޕ1i:T~.8ؖLV9RyC1E^ r{o9 ƥfGyrn}[Z_W<Bl/>|a@b\8%]@]Eɔ@dd$ E1))^Rl~Ȩh+ZIpR,g\LBQFfp'EF'21 M gO(!&;猔Ms#LX)iCG+a|"5jԒLy[ь\"=y@ ~j iƀ/ !:bB@]P_]I٩iv|BCC),4C5^vra!vaW(?&,bÃTηRUpX]1aA0^'xd_9{,#$@l.zo] PkU%F3ڣj+9HK4/ 6*CA  ` @zj2%6 ,hKhda}c@~ɚ `fÃI ()8`!1,^D/v+l@u. 9 'KTW@@KIB?RXwa/ov<:ÏQ?˵3ҕ{B+:iVZڦ5n ,_:%MQ $$B(v2Ph/p6͛;̙[^h|(XE fܹs]u8m+ {bsK評%tzLY?_ e-F3# x챫Vw.-Vhw^X١ZDH}̞@\,FӒQQ$͛K ysZltΝzjGuD9K4~m`'V S SHkm:w?pֶ*/-øhȟ 7hM`X4A9u@  LS 1 EEDX>-BCC]* i|Nxe-q]Nי/~럪Q?L_g㩮bV!"h3'^>ïN)_z:@@.; ?v'B-.Ɠzjz gVO_ hLh3|[H^GK>j¾`i~",zܮVcG{h s*kXVҷkSj: %h3{S(5TM@3DGG;{N@l0 %&&NJfyFwD =Ion 3^Dw?[H',=*l|ڽ:o >l4,[9FMWۡv*%4@VJGF R@,TUUEu߫\mHԴk5,TSK]i&jlw[яvO{ onEs_3;Tl޻6ʆ ܈[ь6cUHYHpAO ƹꪫ(+ @ȸâ JR~IW*(Jť˩#QQR|,+u[h?eO:o*~ЖS  ?< }gB@#3hF{@>[P0HD !R!%$3\FUh+ BO8\*@tD8xdivX0 @NN TB)iɺL))n:kȕΎJL9H)fdK[_=xr^yY1ݸnS-KuN0ק8/ 4nF3ڣջ\P]E)IQ!RZбSJjan,23 Zx͚;@bvlS 9.bc.H0+Ĕb6; 's%%P9AIѪJHӌ8Wljc/׆~S_<7i?)nʘ_ KbܢfG ?ePUIiIlml pMDdB-b)b@AQ\1\!eJ vx/>2LRW;F 0E0}|"Ùg k/Wo-=94OSt\|߮(=&op0BXIH 4nQ81pVQzfE&R\TY b"B] k9$$$QtT D8d .\@P [,`\}N f @%煿]tx"Ojt0kȞ$`ꪊ_?-y~.d`4?=$Vӳ-=0)*.CE 9)(N  (0 f͞M>3DV=_5n[yАﯸR` |fD 4J ӟgEs}i)E>"@B"8 K| $m5͖@VI;6# 1gP+(BC. 1Ă>cO/}$]4|xj׏?`0Úﷺ{7A@?O>1A|000-۩kFZaVZGm+ePWW !9 0 pyi)WPYϪX[s,ez+yaOŮIi$o 1YƹAʀ0{T ?J>V./:4F06$;DRި,f0rfhnP{ vEwk޽$|) O??x a;W4ZjXA- J S hX>T9T |(PkltPC#VMkԲfZ|Zw[{oeߛ\mj]ƎZ꩹jʘJU\[Mu=o+0o.Fȝ%VW;Y2Û 㿽#*l#h {L42Pu~;;fv.P5;B`4'Fee޹֮clXx2զrY99md -3yJǘjP~~>#73Ӯd}L,tlRR'MI)9JeOzcSnF夙'%o+HD.}m?d`s+B(.y!gưxs@jN0>4Vn30##ML[p9BAgqO{z?.o[Im]\U7O:z<ZVvmX>*+۪ʟ|@emmSl[_R7PWWk Ҽ XS:BX'9). df琠R(11}A T7q%@϶E_sR2%Sbl *3~X-/~'%eU44y-+SYe=aR`&;7̘o;u1ҹm_I@z.ΜdhZ9s|8_eqCCPH OAΊZpd \ZaJ}9k ZY!wF5k0ppcc5ZK 0=|YÌ2B @ISDZ+UZQGՍnh; p;7ëQ1PBL`@,3w\fђ48- Bfx|Gi!ӜBJZLs4oτfS|Y OM[5Gz.jLYk<y"nͥ%^WDo ^e-!ZFsSaIK{m Z-h-, |1,@lt,EsD%x\ga7o.?O ^9ٳgG_^wrߜ  O{_HV6iXA:W K1%})%3gR^A9W/-"o>> Ό| / )INmņR3lNH3DkDEDX-BCCdTUZFn&u#]O`{ݻjf4kUeC_0aJ*PWBEQ%/u-3vZ`jaaPmC55QeM bfEDϳHλR)1@!>"@\~ Qt~]|k(``))]M%F! \ 5?4O^׌)șFȱRN)QϗSlD1u@jf"fhB^<%'F.+r%A;:#Օ84 Y* i B5A yS." bf).> @;b%3  ![97HrJ|qLд8U&%nC4w令G 3[+XXJfjB\ +DZ-۪]~B@Ki `>5U=D{ x{ 1S qA鱲g'Ȍ]_#>ZMJrbVmEahKK迥d$F3@h0(bi E8o5hj```ހumiyELLL61mI7Ka{C~RľN5 Gd *=OoX40Кu\ms%*|VϮG?~?߶mٻ+=UEe@zF7z #7k1ڠSp@7Owd/-??@C繭94>׿|fݫ,C' S/;2!k Vh~dsjwΉ^g;R;WUx\>39lD2T-=ٶ*D~lf~5 =ţ|GˆB aPQ]61`ժUC)))(8P2 !X$Mը k!_raJk?Q 7xpT Xbsc.6_Z>0P\90_i{xc;l"E]eZuBU%=*NPWGu\@t' ˥>Wȼ}K \Xa=z-tiglb^2!M Gu;,'Ҕ@,5? /3^|L:\3g֙?{=6o;w|#>ۣc?rq_K7u4Pc{ښ 0m%j;W%Z1 nvSV-wud-?//k4!@0g>rBkg;Q?agp@d>#LwF "6ߕf{`Mͱw?b >Wy;0kl9 ){H< k5|W~Z}XX q!Bx̺ 3g4ݨP,0w:JPPVmƍC~[ wG#_۞ v w  SSrUl33X2)!)""($$ӂ)0 myK 2HMMXK,E|'44t4222?)+#kA@b~ \S]XHyMZ?4:vܬ' PvvVǺ4X#bWX@fTciYNUJ 2h@f4 :3l/:F=+@> @-PW_5[ 0ȗFp :ŋiܹHǰ:>l{kQQSVVs~QQ֥K֋c#((<%|g (xZm/rIFT\^OcsӶmۨ(A;G,S#/V4ь,!ooN*xu@6``=2lt㜜̍32VDDTVV>0(a%vb8Fhh~t;M]Hc]x‰yz_y酃 (9)GOL޽{: t?TQQZ2hF{ { lաff=n Vvoޚme`eFfeƜ?CRR08a#z'J>&"b|'|{c[G?q Li|gZ4.@"߸z^|0]8G-Il`Xe|:`&JRniz種?2hF߶~f\ڧec˖-S* 5ĬɹWbcc7ӤDCESyhh躔~ ת PhgǍ\ffy !… rc Xݒw9ļEqysgRoo/mܸmG:}{Ŋ+YpTS; yL#j6 L7` ҥKEO/^ P`ʃ je> ra_ >Ar]?t=T=Kc0@su ` `OcZ)1|q/p8:]fy3VB{O…3tj]p~oMgZy6;:#Rqzh0Mr)ڷo[ 0L7L-pc\Q)ø;^Kڰa_ 'q/vV@@@}ee寚BCHHH9|~$a HJJo'#GpaDDD?7]vAg#>;M]s.h]M/_C/Cm5 bgp@qq0=S-Ottma|$>.eepʈӕKtAh}<~ސi&^{iΝn?$lڴy6#6RG:2H[~5, }^Z!4aQGs".<*)BA =0 F pB?cRDcPm"/!&hMSA+s|,1y̌7_ǽ$G5k y555~z~on7 YSO=lݺ7@HW"+AqE :ѣ<[)<'@Att$C|N%1cB~E0\̞Zr~,3LgMislKj o_$@ u1i@Xh`+FO ]Pb,_(v"X5P9u_m-*qYcS0Ծ42H!2s޿qm?]{8q(wx2*8PzZlwRr%Q('0@CCK(((DZ!48@QP (.]Ot8z}lkqDZ'/5 <]cx5u@rbJ!'QFe;ַ.+)Y *3 Wq8֮]K[עPJGjN@Br%?w(_zZkk@䩓`4v wYJ;Zdb6+FHTLֽGJv0$1j C$0Z?aa] > 9- ;#@Od_Ov;nf\2mc*N~!@J^~uPHX]x|kT:@ b"ƵtqFp XZ).`FSiHSQM6Z>~+_\G++&iv^[` c׾2Oj. q6׬A|N 0֏+kȝ`؞[ ]ڍ~^C/)Ql6^S/:m@k8Q4ߟ/ 6% :lހV D7 R;p,#/@zeL9ΰؿ,F[`P3m.3`ѮaJ䨎'z_=@Ghs},_dx1Ǩ|k45sRK~Jd%&-S e`4+o`NW-m+#fg /q@o{6x.}m^#4+j+]v/HS_ܰ5`J94XB]ܵP X+@_%3@YA&UZhe\ٺ=5π)s s^9ƧJ`PBWv?W-@ZP'tGD*n0 6 y?T?1{x>Mʘn s.GL ( Oofv _;R `Z-n=̍3䧙= (SՅ<Ν4qiq/eUs76oNjr,S7^F„LZ.pb?G:dѴVF[8y?BfD {u؈#DXd:1u.׿\ B)@fv>+/g?K /`Y r5Fv7u׾4ǪH[=/ln}xf)\Exn[ @snbFs.Dҙz)6[x(%Й3giߜl(>y-#|/,7r[Mn^V g4s_| 6j^V )))?1IF He;[Gft^3{JO2(+5—io.p|p|ȏcQ7`apI r4]-,Ǹ+ Yl?[Va1V*Zj͞=݋Еi=< 7**Mҽn +{n@ڤ#wFMNOғ͕pF?A}m̠0C.T Ìq4y Q;}= ZFF"inݿCý{2 02bU10(GnX{&o)p1}A*QS ( :::?f|?::ڪa !xؑ}m.o ^J%VM<{4hz2MMOTr Cgtnz+ 5 7b>P ];^WXTߗJ؏cC>mpnl p>lAOk{ŅϷѵTV=*xxu\\IUiיu@\lXlEmmG9P>e'hooDzb؏x{O`p6CN) vp{cn2 .))-k5?V #F?AgZW^=A? r$GnHPS "<0wH^F el> %O@bF0*P3A(F+87w_/ޅ8 2sgmEYct 7C|%?**ʱOW?63h;'v랴Cޓ+rxώM_j6xi7N~@߿\g Z2)#/=(t(--()$#E;ȃ( 8i! ӶiA&n{O8-V6yc/&`4vpC(v5A"y?Uj`4 ^^wkLSݻF (9=mvM=_C )1=NE,y qatJcl?iuE9ETQ.x9_Lp+虭[xA:Yӂ祅. Ӯ]x@CCCFSkV/ylwkA< 1L"%oH3|7` @`l6O02Ri+`umѐV/YO{F =5;"Q!D{)IOi8,[kGі')%% oo1ѡ\G;w<`f/`Va~ 3g (ԴVU;pe\Sf ۼ/@PYY9YwY H$pڧԘ{7i1 ȧ篏1Y5;Lz-l;,jE<0(*o^ۻrzHj"ƒ+i.>/#jÇmq .`4l{ -%W9HU?̧ 'F픚)ڭlP@ku)8ӯ" <A@L ܻW( Q902(_[7T]{hD,4VיgЭ1nj1{ f SC +R#5f 1բb@7ᰰo`ǫP/p$~M#3, 0mmLcSB(q(ctxS_+bd8%?媮;Og'PlY 4rK# cJwv"t'Vٳ*//\W\81i~*ڣHSJ7W^OG ޴`iMٞY^= s%Ep~_n]yaq_Кs ?/KpKHD!X I*F]~.̈k@V?}+>a `E D"@^A|]m*shQ.Mx9{=}ʖ._ȧ:@~^ {c3+,,DZaFSjylwZYڊ$~.:fB@-YC[O|f#‚F{V**.kz& r* TSJ]c_;q-HOzGۊ=sh3Õ $#;fΝ-+돩]c'yZ(d9{7`ߊ= I[-|feee  0l2]M^^ Qy?hf+g@OEԼJǷӂ}-Ut2~&+;i5GD:! ݻg'Q?۳?޿et ` wdT7ۗV4`{A~j!P f 3o p!Q(8:ًh?#ezJҾt m?^SxfSMaS_%?k"r?`*Qto"j0] B8F? |uKO!]_A/ 1=j+4ӫK = ͔*׬Y?m`m##ht4^8.JvVM~ i*/\>JE {]t{r]H. -oh+F;997%x:!P 0m_^iE ;-O,D! |9tfg!YDʁjvA@kmȲ0(//OҖeSM9s~ßŌ|&O8v_OX&S\A~ l+[c`ۊeeez J"@H۽{7XTT'QW,FDZ_`ΜهD-3 %<< HĔ~"JK.GP&oۖ[lԄ회xi1@^:bJI]>' ~z7Һ|M`3-\$&ӯ  |hK*8dUz Ɲ;vp@aa?PxLZ{C x6!`gz` YSEʞO6~jKY4_ '+afwQyHo2{ +)pmڴ/#V:׈%\{e3)))^,^ @?ڵK5m+*+٩ mQ\СZ%<~>{58vz<Ooa}.=E.ƕ-\K鵋O%nxF0 L2ExJ cHO=dgm+TVVmauܹzꂯÛk 8 X,P~V-m>uVrJKK\_O}vqPY~f_u5_"~ڿȢkmsMUYNM vC'T7,k>{58pUVV\j+е_vڹg 0Osvq^wW/muH.^J7R;dGa اLG?u\/aԘWĊv( ƣA۷oOC \0F :x Qx<GvttX_cG^o\`<>##c  $ql0v0Ij6 ;vc0 lXݮжjnVyQBhMtUߞs3{I裠>gAd''$۾%H/~{9`ۖ322ZrЮӑtߢqEOjD, &o-|~]u/_?0pKpB@  MCxGaP+Ӊ%{̆aۋu@Y8{Ga},(!" VUUmE(//{]P X\\X%<@7 j;{pAd''$e&IUE{/]]],Uu }ffnUK37gml s7U* M[;iC=L.Rc_rp}|[ Yn,B-Mz [0na kTQQAGz~\r7f . Pqih lX+bE$N8;L|"ow@FaDg) Jr/͸/Htb&/~S > c6#A5Â9rYc|Q @ٜ2w=)cB;y^yΫWVwU5ԇtR6ou4ӱ lf\x#<7&_igUVV"}C_ʂ-OC`,++pC^]]gj>CUGhY'x(@,g``W𙓖WMox)r+iHK*V+1{ f8lƮ׵񠕟^<#SYɓ'V^^n KcLAgѡ@h֭>QSc#hHLL P|/0xS*[ HG?>-DPߴ5K1 y(_C{G`4  -jjj;vRRSS0S4uIr>Z4[5EANg \~= Px~ٷQڙjޟB8= |=H0A/~Pp @K0YP(ks1SF-Y:a1*x0ٯM+OcLHN3{o'$ ћMgN1f. ؝Vi1GLF.3liYin=E1 9uسFu_Ҽ T6&@m׫K鵵cVW X2# PP_)q$wi[qi; >PZ.f@ @;_3n\{8erj?y(}0n kQ؄퉯1Gh|r]Gi<^;l2bϏL[Ʃ)X f,'B@=>/ L:}1 t3Zǒ}w%>e_KM?';+7^ [-6579{tq[__f7?_BՁ@ߠZ7g 4JOOF O*WǏ'\WJP!9!cíhಲ*; 0R@m_o}(aZ7_eɺV8wo"z}àI7c4RÀ@WZXBϏ̹e%@\!)ؕM0mxy]%$=}DL L_ZÀ[0\_;~[Ҷ=,>x'(&/QA+! j;QgѵkD=^|􍇋e֭@ ?D;hE Vܿz~R@EL~DP5l`#zߞ!`־|fM,hcE$u i! E oGZ`נڪ -ʡPBҚ'* PׯhAzWD"$9=Owo5 2ss70 %zoՁhs.>PtPSS_b !c `!,؜O̽G/d#'B6m,+HZ> }*AWҡe< JRW{p*2}z ^~=}S)]Eq#C|߻R1[e9)*~Q9>uDҰ & K \JHīx%@_ )++J/ Jrfp:;eaMX%x@9w"=_U~ g5!'pq^zJ~k7@/?0b$'d#Y^!kh@pj}.J}tôy[RR螴{d >>n`>^_UP ŇG὿ph '|w&k+M||?89ŠeyoYA}yG;n7??*)XIENDB`tiled-qt-0.9.1/src/plugins/replicaisland/titletileset.png000066400000000000000000002034441217502731700235630ustar00rootroot00000000000000PNG  IHDRŐgtEXtSoftwareAdobe ImageReadyqe<IDATx ty&Wwc_ @p_DRܴK,oHdG^cَ('ɱs̜LcOΜ{9y쌳8&fEj!ŝ A o]WU)O]{o_ADaJpC߅|Iчwqqo (Z`x]U` vL8/  'A+7|/[|y. ( (E~?&3r.B_Wƫ~T@ (Z4 ׵{/i!"Cd M {|?q7 P@P@ i>xu,5 囚lQ O ( h1-91{fpe0={i gQEat,P@-) ϥ_5G4[S-a5~bθ R 漟t* ( (En^moy-Aj _ߘZ.@4}1Eӏ)cjT;3wS (Zp @&?W|uY"# `p@s 0=~R~LjT8Y:t  (ٯ 3 򴿪 "u0@[._ }K2_{N [ ]@P@Ky ;gk _Wҷ-;^lYg\^#3 J~ן<ȵ*O̴x98^z ɔjDkE vvh~Q~G?OEKNh?iў%vPD;"1юvJ3:hmNvṵ9\ӧN8}>%gLO;c|9sΜ3G:sv3w:sݙΜHPOZ*dX,M,=8*Ef׏yg"}גM<)ߋ>w/'ߜ:~K&d2?: 78w6@0{YLd kE_39O6O?9Z]8oӭy5>cM<=3Ȯw;~!LyHo=PM,y ];A=Is;Ebz5N+HD 4]݇jX^A7C:ґMϦn vOp.lPmip~>IL伪n2u3uc=>[fm~XkzS662->S>'Ꚑad*7yV>P3d?9F9~\O]C{&lFE~?[ ?J9Vf8lz?PhB;5"\2tl}n&T`!y}=*3;ғpMg@@@ew|HgԤ>87o0;si~//s)-խ%e?-G]]:#|h ff? f7u?97ku5΅M >'x ar3]PIm/A7߼`&Ix䳇>G fCз-qiZF??T@+MWAjp]U_禹c/}/JPu %:c"T׺O{RO'̑$[QǪ&H' fK΃|>#:4&hxz?f?\ӭBL6)J~z:nZsѼ#S=hOVz brPS^fĒmЗ rh \d.Ⱦ\U&t&'9uۍ EsA$0q7sq9х\Vߪ86[ ~Vu~TVs zԽKVf?i-[ ׺.]in~" "\-BVԹs[ϛ ϯ>~4|.ƃAϝO"} w44c[_=\\cC|^{uuLRU7Y,k-#J9keC(M+ VE+QZ򷐒ȽtTk\|Md`Wژh# +euڈn'uPH$t-ͤ22H7j_x6dK&Ga)l/ٓʧfS t>>{ ޲!BR ?_ `g#콾 &4 !02P@4פ҅[~:Ӹ_:Lݲ4cw+=\< (Ԉtݿ%B@ ]?ZdO3G~svWu ( h,nˇ [lc pd  P@P@K.܄wT_je4c+XP@Вn{.BPmXfL0h ߠ!P@P@j _:__h p_q @P@"pkT_@=2 (3B?*eSP (tzb/ml@@@P@K鱙% 13׀ (Z@/\m0ߵ ! (y \ 5  (+>U (;yZ P@P@EXXׅἍSOڋ> A_y/ܖKC|3<auϢJBkiMt@@~o~3q7[u57_syjiiYtdY)O<Ē YL 0@T }׮]cǧ{7XA s1Z =4}/U!1RnVJP`h7>СC # M߃9R3G~TUm c6*S-7YZsy3Rg `^Ǻэ!?CK*#{ՄhZ0'ԊU:j1ó$ϝ*(-rU3ֿӒXTMU6:O<͔pL  Ч3d&}¸oyިsu_3]3sᚸ <-ՠlzcx%G_W_53YoL_+hhESD{XGEh"Dh/hvVѺEmH1&D-&Z\4Sioj_19cwΜ?9sߝ93;s|3[uŃo$F~{-ZXp __e1$knZ[[=)C[I'ONM0Ք|ig{C<ztOkJژɜ[Fgc[9=S? s.ӵ^{5#sk\ΙqA} J.Q T4j@Q@RkvjV:RWp>tVd`&y,Ɓy5UIυump""IZ>0f6f;~X*&3}͸ #3//>Ipzw93 @:X6CÂFj-kK䎿83Ow>Ciаy7-U U^Y;M׊0g^ߣzjԹ9{WᅴglL܍0}&ZBZfz ׾ uzqMC=|a.ƫ[TqAkzӕBV67pI=dbNOQ z΁ʼ$cxyW=okA+zژɜnhLCմ R;]8:y.\Y<^h >oM7'<&Hf5Kw-an?~dEj@+U9- SK PE9q g0Ow~kE`6JYB_EcAd|&56.~'_}w)ոV`LRźgKEsiXW\s*o@1JP&E~$.NvAˢ6((Eiz& 3X& :c3;s#gwnNdTF2J^E[WEMӣƞRݼI@i Eo럞]@.)n+oewd6f23_ZB@3g~vg e!Klƫggzsi3 ,e7AܬvTUKBw;/-]G~# ZXV~xi4X46ӢKA|Y7cMTKkZźgM~B#4hَ?>}LD+ix]*Zheh9@|:-0!X.7!fd}q6A!NJ04>XUMF1ȶjBU͋Km&Ml3e69_{v=W=`p q \TnƝ6광kP@tR,8WSZ*ԋ/ݸ,9Q-`P@T *!ܥ$>ǯ xe ( hIXtպ 9S;K7&sE@+ (m7]NC462 hiӦMtw>馛(778r4M2\W^y^xyz3YQq!)dkD}wW݅P@-"A_oB `Ecd o9!QNNnV瞻~.^Ld@פ "U'PHX>րd.o^AnuW@@-2544<@CMcBZ7 ``(77LyTTO<W?&5k@җK9z_^}) ve&Nj E,ÀZx{ھ}uuuQ0Dirbod :fT\) L*-+;VX9SSStMe_)ÊGs>>׋x>B~1Fw1D]*ǝ0I:<,ÀZXڷo};|zH& oזM]ib,JW8SAaY֭]G`ZR(*>O L!|CU!jSWOjiA@@- m߾,4>b׎7e;? `NFib|ˋW Z` hrEQ_4iz\Y}7=~]O(_wL9璛%}%X0T^^NFhlԴ &~[˷ #hX>T,$8{ UTPeeo&}>qa?innLkS /\ZoM:(<^ 鹴u>ܨǴߨsP@-0}K_Zf?|m-!>&>ڕMqX3d R$Z{>:punЎ;{ݻP]mm?422LP@OS^e0`aEAgPf5 w.s`~ke (BC|ՒGϟ9ϧ7r*7B *8]0 @Y/5\J |S)JuzοlaE\/0Umn $ڜ9@@@- }Wo{hGMRj>ӿIł2kO4}7?]H)E@ZSFh:+.'.~bѥξ/V7twЩS'B ѣY& h0/4e/ WWht/dA<``|n Ch  Ao&ʫQSSGYC¼'‚B*tx49Ar,SHEeb9~n*l !xm*,>M@Y.WE"XVIXTI|D0  @9__b}\vػT(* MvI%K~j˨Y(:Ile1\ʉ cY΢`K\ Ѱmk;if})ra/5 .݂Tvn?A~ KCWKʂ~ha\[Q9JJĄ̐m&p~SYU! tǏ??3<2wBTVV\ƞ+<TTTJУ>F >s"9P‘HʕS[@zuq9BHT+WZVկo>/Ћ/{&Y7Bo|sB:BM2٨5t^ |Z3xr"f)icZ -»1/P__/kSp~J }I"FF& hYE55#"L{PDOtПB9)," D\#94('7"T+qwy7@KzI/Ft*J7lgV Nɜ~)Ê@ {Y `R2@ .Piq̅FHMsO@0뮻wwuI<4B`:BjbfxG3MF"j0R|McqҨ$d_XCk]+alA_E laA_SǾp8WG444s RS]˱06Slb3:.(qB?*j%f 9]mӞ VH)ѧQ*-)MhGlmoE۞0H(Ktww̟ 3,L͌?ăx3X(XV+";hJeq}3(JBcbbL!+|DܗH.]|d^@:*8EҺ$;Ɓ07''O|/ѩSDz:---Bxʕ^Z:1GW7@xߏMPO_.ѿW}} 0ĺիPGGu 0 ] P)=U) +yXt7@@cn2㏻Uijr#eA|w6a?1ay63*KɴBs0gSwʹcBG rXwCO<}[ߢg}v\ +5jKXhQG#K.d#QO%>'6ɔ!$3sνeY@& S?zO|6Oճ>G XP,R,n}> mk0l[d‘g"o *4B0t̅@!M@o'ii!'Z QA~! |.rx,bcWd?X`-/52&c$` `$V,<~u3կl丌'%IƿXɎB}[PRA*bA^+ p&?0Vs6TPz <8RYطia-rhbm?8v2 Q݊F:|-7`z B?~?[><1N8A/2gXxV, % ҟٟ3(w[n𯨨 Tc}ĽWFvqbh30-_^N7! #Gd7!jÇߦ~ȯ֪ݖ+&\:pОƝk@rFh1 7,E]B˺L7֬]Kf̢\錄"?U|:[~GhvGHw}O2zۭۚD\,Irǹ3o ڨ؎_1Ď1`c YXP (&+ɲH#4\c/5s\GV sHe-B K_yy!:=⸑, `x1`UzdpapΌ~\'QU}}ļ3xI X5R9,CCtmQӪIq 8Lz5P1Kqྯ_`e@H֥_-#5?a1^>kӠM:`&yWS K`Z)N96l-\WKaf nle 'B[)NŝToʼn$;)+B3x &)sc4%22Y1A0@@f~ b| 0k+U!>E~jS8Hw/2=c~gI!DDZ_R}ܑU.aScZrΔZ*[/,*E,tL5H0UJh=U~@˝>J.uя7 ;` .; Qh"NTj1}8'L( \)9yBF94+19Q׊%<}BSg-eJRh0ΘG|Ǹ&05 +!%52…v?g!_aC}x {\T[Ś+1 x ̙~ /^Xg.1[SIXŕ:gH":u(r_PSs{/\ipX f 9!fIzqz[tɘf276'Dr7yTf`AC02%^~l~ߺX6P7kcaz{{p6^Ξm]+²[3O4-@@YiYSd{n  h>UF_ XM y (.BOB%-+fdDBOQ)r`CAҲe},SXC ա !kㆭS30tw W:vGАҊz6dDOؓV@M}NEʇ-.5aGGSR&> ,&/#3-lh9/_cǏ HMͮb?L& 7_͛QN@0NW'˿[o63ĵ$POX@{)}}T_h 8){|d}[L!`ndtVo]Kmm4XUҸ>Fcw@f6|ĹOAȰu:TtzoA@!`# hX.'Oie- ~v6_-:z/Bu*nA5A HKMe˪hݚ t5%5^cEUT 0hJABl0]JqMhzTS.4wyG! Lzٍ;v,5rUt{mMTN>A+WְyYK5JG&ΚZFͫia͒_PJ_WD/CbrJ$Dǡȍ͸8ǧ!P NYvU0 K(v 0qHD?W_}^ųF:&`.#a{~b󝙆kc_+96:A͍kh޽\yu xPu,{(柭8 xiE366R v H >\1^T;׭[[X=yXWOfe(TI1(Bwb-q5@'ni>Wǚp[unLhړSY%(E'(/'U Qa<\=<Gw%HnWJ``8bp) Š@(Y-0RQ~ܳ ]-@U*u៶/hooo__}{nc_K0kV+@ ~b<U'ϲ}}֯NTzgvC{/>(L[ QN֭4>q%>٤7/-fJohA$Ћbu>C\ 09gB6-MM-w;a'?R(5_d F yy [+SĈ։Ȱ' _u~ȱ+1ӹ4'o7qQڼq#UUf=Іm=nZ+`ҧedc~ξ͉І驧~B'icno?w=qa붌hJE"`P$@v?Fi|lW7ǰ?2TArάZnѡQ.H5u8?/,à zj/RkGfдAT#+zFo@*8B2G+d"CcZHMZ|qXq+$@T^5?UMdkd5ƵTQWV pg JfEʼn #Kʪ+aԞ.=|S佺Q=44FOHo<@;oJ3/P;'`IK>v|aFZ!1`P8 [~~D0Ut-q'z{ZP/]/Xi&%bNlH.@sۣ4:2AcWZNjj^4O )S>z6۩B xʾzDpeϢt8~ m42(WrWr̜^)@qp-&~Z7]jDԵ ;x6'&a$? 7O;[.=-J}o8&|Lso|K /]ꤧS6JnF-9fJF)x :"3ĵf5Z,H߭iys`z4}OCثn34S;<'ž=fTEI6lX?Mո1CT3L/ !l=U|´ך%@^K0:|8<8&R!$̙S7ixh$PN֭H}F@`fvGo!lvƬOFSahڲdZ/GV>ZfN%,t&u73h&$AVۅY+$Tar [^CNm˲<:7:uKS@+76Y6 y{Gk5* XEr/&4twfqHs@`^JϷj]L@ۅB׀%{ި[޻&:-ʕ+8}|`b&ta;1ʀtB8bdAeP(!Xdž._ooT+],5]0c덃?oiyA M4kƹs?ȯAd{{ԸʶYk;4=]$oq|`k^}eP˚::y&fHl0lw=syLƼg.ڶmKT4{W,c>(7ĺYq7P!>_?KK+}s$mh5[h"=\hDmj8~-Jd!Xz@GNP`a,q?d`W,?vjhS IweЯoCvjP-XӘe%GQۅVoby L_۸[(Pǜ[ML3;PE+{>(&=іMET9F`aC]nݺS$m޴7C pJ$2F*~Wļpmk:X,%INSt2uK5 ^)洛MO?N<,xE.F@aLM{4"?a ps]^i}nҖ.@`(8=[_mMS]( &$tC-p0K rg6lڸV;)ej6a{W;a5nHucr18~c40ˀY zîU pᒝ 8YݼNa;5{1!>/J=K?{'ѯ}䣼ZB&c4bi>ȑ9XOtiv)d8ljR6Ae>"t OwiD!k#fbM@w~=pC6kNF!΅pE(k`λcN{q gqq9mذ=U• mYL M1Ϗ;!BK =Z(rnR?Gܧ6]B^=QW%*8S̱9/',[Kjjazqs逺 p;.u "iZ.`@'wcLO];ob_ږv {jV&3~R'^CS)+)]do i2oJn=Q(RRo8$4p-vOt>GbAg70_7Дؚ;ǹ3ٻ-Zv!n^ZYWñ jS2l!R#L5L@??ySh]}Bsgrm6`MZ\햗)OCCtwGoDNu-QT"LʪrZ֮kNOw>OwN 7596\6,Q}cPAouuWEDjx[ [ 125h)6+PV;:p2hRRie>X6VS[[577,Iծ5k+@sΰnü@JOO}_>@Og Xo.u~B, @B&4ә3gxq}]D7mۼyo%w }=.啼yK"PF&)95ϖ$Ў!W00-MAҍ1l9'6ermܰqLyeĝC?h֭~NZ`\Cqϓ좠Qiy-XmGΞv HZ MȀЄ/9 ; ߰? :W[f_S+P(p&' k&'{۶m!=آCUwFuonuk`}\IʩtyYT,e`)t @& M_\Z^z]O26F7l;ZMJSaI &ת}]< nC) HצTZ *{ IbqV mk#fͺDfflNDtBȰ9w^7Ɇ=6F`hؗ={GXajqFQT(xĜzt^ѱa_(Q( Lx!Wg۱ckzoLcGneV;Kc \G>NO=MeBml]xhALtY؆k.խ79>&=Sڮ[jVΛK)E2<ܿ +|[R|TDg&u~i ,}=wǩKTC6ziSM},u2***f$VfNE%ܼN>uݧF4 <7P/DFmT߃kIu />'K;?ڴ\H#ݰw{,n2OȖD¼&ᶉg|p, ?8q~gKb=vϕQ^&@l^r6l ƭ@6z̓}-ݷ o-(oܱ5T &Afɲk8 ;yL-0g(=q ,39wЬΝ?G۶aӎ|OW^//Y0&j!D|VYQ |?-r)N{O#c#tw  Lhyŧbl u-k'`oǛ"x;xg+h ;ye"|g0~/\ΏͨL6 o1KB٪&j4zu#-4!""_ c,pC0Z296H`&lzxlI_xŇ5 5ilt|Y:A_rU G`LyW78V<ϡܐRKHn$AZBmE,[򦂸dz&ց0Vg5ӧN +ajoMqεM-B_޻[*[^E{nтtu[[kZǎ@ 0q0G[1Nz TE?F= _#SP{:Q`v6;Ik_J.dW|+--J3~"ϻƝ ;}jVԱ}{nZftBܟ>DUׯRFh\L`r(]7lTqG?R 1@D"i+}Ga6Q~a~bꌲLsi ^@5-mǓ H:_O" v U "nlgnnD  k^-*gy̌磽]0)^EEt݌WӖ7Є@㓜 3x8 V75` :fZ+N@:~(+/`! ~I%!B#Gip`^~Eڲi CH1 <7gBu%ieE%o nSs'xaV%wt|+57$R${L *v&4䶮!Yf@(3$GCwUWr;짋~hd_~o'YitHr\`swD *ItdS(TT"v.ӝ^!fN8vlƶ8VLf@ FGoc*~&N  aKu53ۥXSd&M8Pl-ZVj3}@먝27b>W7@$c@&Zy>ޕ54489jkD Mzt&&P(ЙṆJ0Sv1i%S6&vr=톓AV966; 1L}`tIQ ͆Mffz0^vƖqFÎ4lybFa :6u#X x튆S!(/D?}i9!,s.w}ڻ;1ETZ>ƄOxvcf^)v\װ$0Mq:|GAOdF:MMTTT$]邓Ӏ]j Jl w-t {G>R:;q4+j$5#Mg;h#MP !*\؉eNx>DkֶpQק8BƍXn W l- :/|ȫl&υtM9|ټ_88ogTU]q:te4OsihZw:2 0)*F:v^}oUЯ= Fsv~q<Xpy'ngO/s!%X}ⱘ'qX =!0jg7҆ [6uyȀd%\Ũp$P^_GRCο~-(Ҋf7RX@7"!X)CT fuBར=.,,rQիK0>W:c\G#9-fM0.\NWodndcj$2 @m|b>y6ljs9\if@-/LsԐ}|V}ۅt߇?D͍knY60wux` Mrbw4xưG'^vM !Ny8OJhki$X̾R_6nj2E˥BઉCoPSl sj p|`eD;kBʨ7rؤNĞ H!,<'3Gwg&E18ף B-ڰa==ohc#rJTB|`kQQα6z] 0 sP "kQ?+mv& `rM7ea5PPx'ڕ`Lcۗ| 67y] _پRXSV֮f8L/vPo_7ܹ/_wìٳma#5eXˇW $,1 Gt43/Wu`9P+e0rd@J+X{Cty.E>:Ex9kkBmIa5@NbdDمfW̫_@WWM:a))^&$ $l9өhXl IÍ;q-JIR& uUT>q)6P1'%|5Ʉq#t=k1'#k%ga+KB:Zv=%et`Hj@h mٲ˪(lDDPѣB3G~ӜJ'#kafWR_z)v ?ô]r`]+W h;nOf6u3 =<]ۡݴ%QkQ踐q-.ḻU;~N'|'K_?8,pRX~ 56b! "h`?bU&v|ravzX_@#-_s>D%S)zEyHZ{âV ֹVn +asSΞriP?c1g Po/4줭a [gz1wطk+If1\ 4$'UYj@O"؎1d*|j8 QC@fp%9`U&shYeX Ŝ3`_ɴ`@f讀Wk"D5>{,^彾.wёwwߛ\( *GYI) x_5TVVAB+KJl~x]*4m=;O@_%Bh*yD"N>錙(RmDԸF<@_+Q t^G 6PV[Wß1d0||Ѱ'0pꚇdZA^p!ỏGN!ϊcA?u]:'` saѐki* b|n A7IFZj赸ֈ/4#q1x"^96HOBK@SfdJֺƒ(ױv+iDU&21Bn_BX'X8[Q_/?3&ƣ Co`crrsik&¯ B @εe+- "**Ӛ\݆FXPBa;nXȅ A@Шo (t5+o7>J+Z?ΉWr] 흣8}{)S2l߾[IdV8p:3 U~֐Ue 7t>^ݺN~'hNDYv~'DŽ*,Ĭfg.ӥg( $ Ldʛɚ(ךHJI.ڻ}@M_|-^=d󂈁aa#ˋt+BN۷v6_'ehuwJ H\OH$Po*M r=a4!ZurWڶLj6K\5Ƨ:җ.{E`a*h<.O|s4$)`U,id8MŌwJ{gKhm2{C ?.Yp w1FqUQP"Ur3::YW))S_}1(BN~^,m,@X  '?LЮ]idxTU*~0PC R{ I~^h!%RdŋdE'Z;щ41qAYo؎]O};6˿ۊ{4;LI_+f@pXxLƆYkd)@f#tlgٹ< )g3.5xpHa\ndBvv)͜G [QƢQi0ჴmt;}䱏p4j (ID[ sߡjkq#x,&  xΛL]K/'کҔelW]&)$イ}RoԳv}'veR%UfDXh #tsʚ&T/KI_U"VRլQ:cB-u峯~/uJ\)`h ;55-wf7}8FN 5j8*% f#0,߻ nr{]駟t *;B<4;}28>O#<(N}&h뾠Erّ䄔Ξ;eAzǨ/5vuVWJz% H2 k@=:|#t@ ǹ4#q'm:*ͷj ~c獱Nk k\8Hd2oE8np&Kn䲔+v<ړԑD{%KP+F3 (ͨJRigt|LO%Eck}OPOw_Y5JKl-d:1IeJ91|Ki |W7ϵ`Z*]2GZ 5{ OP޼}HkboPָ^M"pGIϛ*U8\`c;r:9'%n鄐Ve5n'9@ҝ8j^ueJ_{4NOPCd/aϠ{%{c;=CAdΜ4pS: fff8 k$n# dXF,W"+&o-'V$)f o<RYDw顶;̞Zr4G \wEڳ{?q®?wɼVw?/qtD`"H8 IAD!s5IRH0ЈG4p1FU'{nK] 2JzKj!;9S{ޒyE YM 9B*> |k+F^LEƔ̋_+zu\sS^|tb@Oꡥ%җTha~hY{σWkt^Q3-9??+ֶrtuu4tΘ0<$m@^x B dhrac/lIeT6I##Ҁt0Iw]ptN >a _8Cc/m6{9ڠUMQPo_2 뺯iv$*@mΛh-"ݴҍU.v*^ïѿ/Jb9GYFMgmK)~riY iOhz6ڵk@$s[,ղ띌CAV %48< { #{!W9r]!+*ϋѲ]er-p! 28ɗb/mM_ʗE-Br`HibP ԖV'JZ lDAӕIff,IqHc?^M 5NBg'h0,LqɔBv:[31 i[dlo߀RJVhOOeG? ѥ Gjh*v/آxC?K㯑}s㑯,*?fjRwOV8P/=W*%ԟ=ZW/c#ҤSgGhnz[) pa& ֒yw9X#ʉ%49N8@8EdsIqlJL[0eʏPXjf`~qerwtp9M Z؄vFI+ CE"L#cq8DoyE=me%=|:1gdpk"Q4"[ hqB9K/|oA> ,ji'S63u/Rd4q&S=.Ts8H^y;YABN1q3F'OӉ'd-DsdА&uUkgvW&cZCPn:Fs Rs}e j1Wg b|fD\6 ]iduXZ롞(oauyqzk!&8y[-S_K^Qo4R-l٦u 511I6P~uIxq)JW&ŋ8O8Ps:=oGXCbhau! 隲D[)o6 y334=='HX[ur k"QXE ԾCґ^~Qfīa@ֈL&GǏ_A^FY9PKv=qMoiZQk#yJjuf8idj T:4z_0׊1$Gӓt)WtzE ԩSt1-8zzzѣbqq}lunnӥse@Qut xLKBq>t3׸n%8bN|L\cj(+YR:EH'X1+}ǎ%2VԨL^3ĨfP#2 { X!i;Yu0MfWסր)Ӓ_yYiˋ(F~~0Br#wuwVr,iDRJ@wL~Mg^ kE|c7!?' ?$ϥ%h8~5'#OJ[(* ^eL:U[K? `e0~\?}˨Nv@KJ0Wknm;}Ӟt.8v=^poߥ &_ʰ6&9 *i6I&v ٹS/3uo."A- ȼx"*z|#*]Ν@vF& xAe='g"q~p}нݭf9,'e:Q\4 lo .^ڻ'En,9D&ʡ~keY0O WX%2GxnT%UB(9F~SW F8 nLso[2(h繧Y\NQQ}--" h}ٸ^x>ω(<τ{1(0WD`.PaikM\p42[ʊȆ -? 5N諍m}rnj| _71;[lA Yy~@Mȶsf:˒οZ5@8Kܔ08#[wpQC?J>+%-q"c&XͺoRq/-勴4+N;dJ_#GODq>Y%*NuPK u!B,T1p:BН %ܲnLAj( ZZU|o֥Oχs/4<.yѡCV5 Kl_%쉔j φSўz+ dp[2zBS]_dL0F=k /oڴu%ڽ{80y{y˴8%܀ղu FV I6QoWz'%bt)8~JbQ2jRgR^&s\A9D3Jg8Ȥ;}F_(bޞU4ʒ U#ƚnX\IDhEj0Lc $u{3]wu.v.l dT&0 4SY7g4Xs t8EG*Ydg˗l4?;C'8ptXClP-z!2MG $+-H%unZ #Jv9q]Ǟ {#zLȞP99x+Meϐ{Ͼ"]Hm6 V}m80ICyt (^ދe_=t d >[]x/\.9 hܡQ\Iu_Anoݐ;Bx Cҥj%ptt8>{{0*rh+˭֫|K3mi;vyv2^6Cu#W4W8U"퍍72H)q0UW7z=㑢l/al&=LSc[ @:ZΟ?/dᨩņ;Hn0NẀtB?R͹5D r!L;Frgc0ʼ WX3#aT ZVR? q25 @7w 2#(_jʡ.mnd&.JJ9Oe!N>(Rfܣ*Jj>qX;9~s>51MoutC=Lv~#o48C{(kQSJ6ޖ62zT[xu ? U^*D"Pܬl \6ph æUjRWZa$yFI0(@-h%[8Z^^r*_ ( {eXP :l7qeZF&PUOHV69vkoZE#:80(Jh- `&pcQZye=-A-TrI*'i5@@AIpKW^V;yU`udo3CF7a!2la%Qo0V]&JD9=ۨ(]^e!}nվADQM4C$+N)aMZ֪E slV?fmUp`Rb?!m%R$AaR-hǜZӟQ%5Rȗa<ԏW_nxRG?| s(A}ne&'.+%(G:}Lvv霁ȐS)Z4emÐ_D{ή6׃;w}{(oޣ 3Pp`f*ΚcOp`JvvRש+#Q$P{,]4KM75C.9ѿ= mmr#M]Y1n ˒6 D"*-\Kv^A*`xr;i2 .~'R!X[wvΝ{e"`!I/Gٸ c2|ܤjÜT@)K^ _@+=7c:RFCx^4< b؇|XHlV٪9X~F]-BZ :iDˆIt"OFԹ1>tTZ9+TR + *͌괾 4sfؕv(I)NAJdyj`[myjM]G[>ƣoȵ Х52&ٮ2H3&&9ٺS6ɓIK徛oWN?_#1iCT{G.]%@zG9L:?dA [s<*$d!q Bc36ZPj*ϘQM."Bh d3de^/|Wssi:r~swt)p TPEt&A程 DBdꫯJ$Qe"4<ؼ7$yQ x9QU#% Y#$n.􌭾~5Pi(S !Ej|?>vpnHV^?ݴg;Ӊ|}mלs(xJH3veߣU VC_.t`{$>j ~Nf,Sl491/Z\ p~Ww*YQ33y6 )?+o0s1{ C %~a9<G i1عmOdoh\9{]ꎪX͏Pvx_3?7#S쐮3TE?+fqrMuu ؎cʆ8SB!#46~zӇ'`<,B+żA/J- 4G t ޯ$G7￙n1!*v۷ʱf"9OۨmPK& ĚGU] MG.9"pQ{gYb*ָ>\2Jt TZ<ٮPwÄ0R BDZ.Kf0z]f;)p9LG8zV:phF63^ in?vL1Ct]}e"5 GvU)![`HGf+gӿXS5㙍9&j(;13̔*ߨd*9"5gF̯х ctR,X 2]')eNiiND7j )_¯v.q 6P˒ԺeL/ > "%ZoV8ʌD`0#GFqAag?}+Mº'@Nbp`2-cy]{~λ鮻3{;L{?88@[mY0-kT>fAc2j wg5eSfxdg~8 /z+a<X`dJ( LF0Js-ʼ/!^eJSzp}}r Q*7&T{TM.ep= Xǚyz3sEw?o^cTWmU 5[[V #r&iD><_Uy*v-Vx}A R,@@"}@$*j^X81uIqrcQ6/ɜ]Z1}OKO%vWcx Z7!Rw{)~#[Jb{.X7K[6,mI8o֖)EJ7J ֑ 7$XEB `kfO#/ y8! utQG466uSY !Tfz)3Ϝ9`9DK9~sЋnLs$\UQ`B",R/)`o|ҋ/=%)pƶ[|GKCѢ`,򍘹?SO}^3әPíJOvg}%#p<7#Vm7ѹ}6UzD2aN/ՙ7c,-Vɸ >;4'f|Dv3G݌}u~(T5X]Z)I*-7jV@DKc(~cg }="Iݫ(\S+&usQZȒ@zU3YS.>Wv\ixkMckv4`ѮHXmϩBaԝIR@ȟK `o@TِQNר) P1]Z zg? DTg,L+Yg,K)T|4"=1i/"/.-mJnw t*KidxFFo$8zxN$K˜_ kTWQD֑lL+I8 Xuɹ)?oF` V3R0ZΌRZvMRچfiU^Or=S?Tŗ^Nf{-U:^%_jwbn6)Wc B!'2M_/_k'N8!5۷K#A}j?G5Q[ F&5j3l궰Kdc#V](P[VLRqjQppm;5Mu9~h̜ 57V-]a%^,ddzլH?1.B ^X>S|ND4ݜh4*+LåW]A.z`(`XE?_ S>^}ڧrLJרz^0Ge8mZMU>dq%2r8U&/hŏPv6.h-.K>y JFoإ. 9} a`90"]Fq뾖s104[mD^rWmd[rؖSP]uXXV@9~ Q/9ldZ\Ld zv&ծ}{i&xW.ڂOUb*z]X+Q2տF@pCq*'?.| Жd~VU̶?kI(PUD ѐ]ty!-(sD# CmGj}[쵊 *NZ2Nԓ$wT+/k$s΍Ԛ2 6YJbjbdWhyyKr&6ƠSTG)Rr צHk8&g;Gdps@Y":yeՏBVJ>RƋ޺ J'(^3B`I fb;cL}5 l]b['S2@R\0 PBO*?s4')5T$a2@Id@ټnM.]@VY9m~NjX]ƲBaAkG?}NDÌUaNk btnతBB0e 6QJPэn&\.1.JWlt B&[.YR3{Э [MŒ҂G< i^,,iA hjiL3s@XtS,LCM^y]|uuHsکNv].tmV'EfI֏[4w^d2H 00o|42==ʦtt>E+? BrȖL=V3-̎Alo86d3RRԭd ߋ%% l%zF(Ȭ 7>'GP]x~Q0[4 \M\oӚ%3)LFizvRع0JZ joK˜(G&^'!&%d}^_yBa9!F cwh@sskFi Y Դ.elUY ߛmg*rj?sȃTa)PIj" K +Ǣcg h -2̙4i0;iL$7&YD]vC?RE.;Ժ'DoM8KTj,`8^[G`G@n.P.*h}Ns3~Fޭ^6ɜN:4{>O#⦗*,ikL/{n:sQ:Vk̯ m1`O|LK+Gωhr:/##:bN+JY6I]"7*AM\M.YpzP! eҥbn^ڹs7rk~o4vh"h=ǥ?t-wѶm;UwB,j_0Dp8^ny׾fbU o! %4 d/CQdU=QQD3'|SR[ӡu">SP^GX}jahW4>3ߝ)op* չdlZ6R;vQV'yV/g.F#{\(K' Q|ۘ-Aj"tƋJ4^]! SoXV^[{̚2=v2ʪu*aB*^>arQL}æcF"D%*tugC.7;%/(v9cs5@sC wD(XGzoHNN^[*#|cՎ*l~_H]i}ߡ q!pͮ8 ЁA4FѣeMz.h`㯙s/JBEH:"jc\mv_Jց-T@ ,l}>;arH]gN#DuA5Ji\آ.||g p,OjC߻ 6J]JShuKN'EwoGL~O;%uk#k/ &%3Rd))Ѧ eP4[qłk_i J ~=PCG)|GZԉDh"FV X9ғiJLP!$6h{yPb8&D+-5TDtfTҺfTJѿ "`Pc~i:{&CdY&#nsZ`tF6Okd,"f,DO&5\Jħ:,fD8+4X !xf"d[K1oZ;ArhqHykn.p?y ;?fFȸ;D/'h tQ-Yz\`wPE^lz饗EH3;?,^ݎij%$zl} zkN N.M(^ޖ4OATgIYȞ-Qv%/%R-F}P;z͜ P-y!Bcʴ5@sm zḍ_/7*0Vˎn_I;.?L/LpUQj2 Ug=ۥõ^׊s쎰8S ,\ux;:OeviZM S3r)ņuf\7 ܣf`)($&J.W!:y銥.Hæ5tֱe5eF/ײg8ԮvX͹~;1~V  w%31{O=~wih7`'D[xR(YmTt5㡁_ .N?mLR\F Wzoӗ7N,\ ɍ)_Sε6M]BLs{LdȔ*#GNWUQW5 FIzQtK480Lo唨5DS WEJ?IhaqbCws X3 O')Ƞ\!/xRsά?c*cp)zN/y^v.ayJ *22tv:,w:u!^tALcGg'"y6pEFH*g@v@Z?(B@)JNȾܬI#9&+ch[= 6`\i}[ߢ?.^'07?)u|IyUuͥiiyA8G°Fo}"j>6Пe:Qw;6)LP!fPM{nS'<pٟ"&(QJ,X$oeg.: 6zfY 5є{K A29NXN9k fqpdwRNEL`D{>WQ r`6(^$&o=l~Q UfN2Nr4?@^N^ۉv~A?lqRF'_ʕSHzVK_tùMIǃ碹 :,Sh.'}.gNT};I h"oկ9|a;}ߖҭ R{}=NShFK{Jk(8]Nt:j0+3Л5(xSG̽5luRը!z! 0@K::/;Zչ}_QOP:g7vMAx: 2 ?̘ 38͍L.Q:a߼ZohTf'(|mSM.XΝc˒ӟ8/k#q08=?o=}+B/P[[7[8ϒ)f#?e:ur=V,U+ş@npp#*Da,̦h9M J]Svҗoǯ fr!_ [GSYt)RjvZ\pny8;l \NzNu4_YȦ`/PTh܊}ep!s)"V;:6^CxW-$sѨ:k ~P0lQ'-͔vR#43L9p"joN\Xz|zk{[t;tWrhd1ҫE$0s?r}4" 9FEP65eWʀ:mo 188||V Go F<-JSrVx]ǩ5H.8G/Q{un_[hPD@IHd~q>A.JkwI#eYFc7`]7j^s S'ia2TAcpC.:;|hw9ϳUce0ϫѳӜR:[*;y\㊢|c;'/ug :@?}jf1@BR9NOmAiP"ӷW)OQTGk{GKt6#W (ղzK~thٝ0vG$r ㅾEM:zN)Ixײ.-)uMD TOCb`Sg_+TtLv`^$wQJ't8o6CLQ29XSQ6)šV=d! ޢ4xAgP+ntYA ܜ}9f pu+?Eħfh ~2L|?(;6GQ"L xy*{a{SR\OVk/CNF/d-n5L9̔z M.(, y;8r gu$ʱSϲoA`t!sl lu cizjyŜPA\K<]D-.r1P%/ HTnC2gNR)yC]6;n\I"{c$#N\츿CO%O`'Q4 ؎S4mvRz:-(v0in$e W[Eګ꾁zi{n2љH3r?h7 lWFZ\y*HWYtxP6sڜ|_=DwxC4WhCf@Z(Ȩ: 2%>')EQ1QV]/Bjcs_/ "U2<S^ h+EҢ 9/ PYG?BɳrUq k rp^BSB/wI4ZlMc@c uBR:p{8(s 1+QX25$6dtȋDWe!0z۵t Q-aZzag6.iZZ4;H:&\ y8;DS(B e!oS\%7˥I'+{\檣뿢-ezWkUq. ۋgL/G)զ!;g{fJ9 |H2W i)VR[螽-r8zP lGRc\zPV@@eѮC!*`REV+%o̽cwۨޮUi G CJ76PHD*  =N=U( t`زS`wL)lT L2AN3o% ώLN)OFxz~2B_.;>;#+bt3ŤGӅtm1ar&NNYNJ_HR!y[!WDLG8?l'AqT$"sE:rtvݏPp,H?_%JKȬJYZjCP)wު,2Ŷ}.̗he֠x)N3S唄o/o3s-$)*e}c[=Rt'zmy咎BD+++XI>J PAtD 7RxTD֓?nwhɤ5LG_Aee]6*GLB=ƂIv9fGoեf7C w7~:4HOSTmnP3D}͵{b8fگg2I^]3eGBkf"lI8r.IgРI[.:'It6W"ڂ_** M4e7PX*Pn%GN9eBɰ0 ߈`&DŽ+׎-e^lbgop˧5âWs&i>o@BΟFꠃ8}[s1K:( K-7͑/N(;DSIZ8@օVVy9BWI yLK>"{GT[=Ѯ ,—w2|exx4Ő7-DCQ|{Ȍ7".g/h3)mНldmS8X1'\зLTq]U XŞ$ֶPp 0ΕjV٪9[ ?nkfd0y-OΡ FD8Yח3˲q=}dI_bP3'b4ߦ6ƆpnṈ( o|{j6\zt -\fͿMM9Z~}y7t!.N{Iy+WXVP~R6MM ".WiK<Zh6Jm6y\vQuM>gĎe*#t zi-,G9*,9hlWX~TrLf\}ߪ7K{{A?E: 꼿&9)r~8. K͕-tG0H ʌaX)P` qPc4Rjʙ"yv޹lm=4\)&S0s8]|Qq>-jMOBo[k4u>,@u'evQ/k鴧iK# Qzzi:rP4"ͿjArIO9#6z u+ӗeI#uGoQ;k~$uxW'MdsӦ` 2 J8\t%e-}Q[@2S!mBtBvzM EpAAڶr1qs{#A~IlwO k|@/tRLJ&esV3 5ʙ#nbPއ9`5jPQn~,U MǧUݿ?i5MqJg3bJe ޽ ۪BTFn%^X -ԣ;gؚa5Hjlƥh68Nrdժ}ǮitZ4 z!F=d\L 2!{J&(,q6A *&\*??C \N؁$ߍ_nkAIՎfe\_7}EKyIIGZXShzD}}ޙq:XAGzȭ\ Qן'n'EZ ""[Z_/~ vw]n<;0L4cyO^;*RE{RqqUhlj$U!Y5I%hq\]X &f|} kk@DZK"B"hui(N|olӾmmgE.>9D.fL|-Hwv[j)^ƏST:4@s5&.}^ǚ v#%Sr3GY /^X G8r6B" )V2Wi1At _ }m2Fs9B[FdCZ(Sj EY Gh,-Ng(8@$LB:%| F<^^'s h:*df?P@8LA)v>?xxNMww3~AT&Kv_É-dŝfgH,H_0vn>tw8 r' :)sY"Q΀+~0 {{֠-df;{7{=|)WPDR0Na!RpޖSq:<9ܳS٫Z|9@77ѿ՝t΍[e:QE+˚N~_ 8clmU8|Ҧ{";tA Z+?h0G.$i(|u6cD ?z],=oص:b$1~j _>vQN\a/Dy5F+I ҩtͤ'V;LۛvqʱLնʢij&B#0\qzLR37Z.&ߞf[d|?Dq勞o?ںAkvT&kƈz\ mPt,rV2=4}@ f:9f`<;":]p NoMqrA}MJ_QR_;:GtC^^RDqTO{5(=Hsh @b tYPHu 7&pweOKM3GZ۩E)D(nּ酲3H#RArN]JTRL|({vJiptڹC.k[-҂e"Z?Nc|F <|5ŹN Y*Qe[-U6s-D 4Cqr~V/t1ٵ52)]M\u2 9.2ri.ڟF 뇇"4;hD#MS$oH+bD|z3 F?H)x@zAAmK4=634Cl 䃦\ؙ@ù7꼷&9R}h(@kb>G/po-2ve5ZoTs 8`{),M Äv7u2 ZziNʉox+ݹ6'Hr>XuVfO=QR^jZ;6BO7; J* J]1"}Eimqqot}};v M\um:m9d<͑~Q=H ?bOV+LC[q.԰5S9"SyCvu-s}1C)vVy_-D K5"qXF>;z\\CmtK0T>xqL,VG (>檴6ʥ*N6. !Jw烍4N_P>AS~ZS(o;j>Cg>KJcrloeБ5X{d7WsmyM3p)YDNlաW߀s ,Ǥ 9,PuP'AҪh_WJ&LْTs /YD-v4>ry\\(YA轞1pSdB9ٳC1|v̀3J 备azw3HtSݫ>/RRD%F1R 6ZWPoF]?s$Ǐ\],(ӯmПT~ʊZ t.#xK4E>>r#1) ~&&e`35Xjv{a.7禰!%~i7+(P.d b`hS]Yzڀ-GYYBT>T8ޛ>mo/]OMP2Vjjk8GlMFQ!!UN$:9i"/8e'o0ɮUUչ{{rh" I 'y6Xְ6`a6cmc`&I! #8:ʹ9W{f4$JS]?/ÿ:y1SiNWˋsصLY%F֢>hZgtD ȮR&6ٰ}}Q]=-"rh5l573ْ [ppTs}\Me8yV=xX=Np_+_ Uo ǿ:эKa,u蟟*D/ݯj/7yzy w-,l ty\?ҳa 97%8| b[Bl.~La_$~<R<-n@CM/1K^7y{_p.mZ4J\LPooYj>Ğ]8Dyy Cj8$f@%ub*@~~7$+sqC?B#4~/$7y99 %EUr4 "ZQfm09}Z ^~itTL~u|??r([\39zcaȩ=}6nAmlМK˭h8Kwl:_Sn_|w 7uU`Xs,k?nh4M$꫒sO  ]DbHC|諈_ `Ix [ueC,G.p ?BQűql*oΨ;oVq~vvr,˦c6mǮ]C|`AZAvnUC(_ܹ9^_˭3-OD#Az!Tf! ~L'}n}z}NdV[bUݨ͇TTS2lwFxs_y(E\Yoy"{ze(r{"pL kfydQǝ/q2}R:w'o^tuC$˃  Jʊz[B>8Fk:T2qXR65nt1>[RXa|Ta7gZQmg[}JkoI]T?z"}8z ;6mŸ?y v` @F^)p6WVHqλp7^ޅ]c浇#T/'Nv)(UCΫ*5TM:OfEaX؋wނ|Gy|hvRŪxm]"&+֐V1 ;4FcկC%(нGț|xy(ѡ^GTSJ Z4TB =CQld_ǥ]q|ozŊ",!kAXsX)JZzYq/`]IFg7y5ݝ-:Ɵ|da٢:u}8rڼ8^dxIJa_22ZQPKK\eJ:L9& >ϏN| 1: XC\U!m՞+5(-tOo{pez_Dxy|STSxX#9uHV׷O⓿KE}̚ dx𩓓Hqp&Zvr%YA CDxk"±"@w=Ks @и }jmaW+H5uJ >!׹oz;&Bsj>h)j>_+D߫S4v&cDrۋ˪0GOͨ) lz4 ' ¹׏^Tt2z!IE疫0z o_l*S_;K.swԭ3]c*T{-b}LM^v-g*ϋ9S?#ֹѪLQdnZQ] ?-^~ӥ 2Lw[~F|:gg{Xe0n>^щ1jx́F~xMy6>8UkY|MB*+ 9 x{?.srw,-@њ׽ݍH_Dg3Ѡ9>HF su^gF/ܪ 21T­(VlnR(.ʽ7V}c_UA٪pFDBAVMfoRFBc4Q_/룅eAQ:ЏhK<c_J-p&G"Tzy@Wt!=KOd&tykn|xt=űT(#qq=6Uă71 `5=s.em##^f^9Z?:w&%c$T#mn,i18*ͅq|w[{Bit3TϹ .DTj{m A$!?2'U`Qg\'bs ٝ{6Gy7-Jڞ~2k]a`9ZW`ElЍnVJ΋4W9ȧTD^Sn ʰ,{tNA5@R}`ꨎR1g#1i?e=^yzni'NcVSQ>4Yz K'+#W`CTQɄZ>eG~إu{.7kz*~ LJ{IhvƊԂZC'Om,B%pW^D~gC|T +:7vu^h܏+k>.S}rDnu5VwV&nb)]qT(~e,ܒ N>Y߬si]9W}Cc-_lX*-a6?kD~dl>PF'~h j "1–PG#N*Oz䌍\VՑVH{9TWh~=Nv?'ӤWGl*QAwkpdGc(./׬fG6<8xX;l5EOBu]9źRLw< &*kmcqu~a5?]**g(m xD9@waก D8HMNԚe}Y s\WeRRhdVTꪵ"ym;3<y+k_}u7YUwfV Sj7ᅇ#&gpF>= Tu+[ۇNj%\#d\-s)p)ϥ4Uq|#\־9EXr=mS ̀7DTMW,12o   nx1gu-A;WEb{8"ų1:'R` zުU/L0n%pWnQ=wD8h eraנP3~;8t%s_|_5M^VJ|yw7~a_Ӗ 1L׫a5}=|}h,Y#VD*.G8 T$Ԟ7'](@.}Ë{89gB Ŕ60\@pN7;&:Çsr<)g|ky\\1/>'W7JQ2g2,@ﭮ"[WKI@k9YѮvWMVKwG}i5'2.-8R&2c*k~Nv0Q@iz9Lg*LZ+/M$B::LE-aaݸ%Ӆ k{hIjj vLɠ1Eb:]k/;L3Uuqk}z\I8,44Trvħ-6Τbi|[8CW"4n<Z^m BbE.LCUӜ `қh޻pDZ_g]BeWcێ!旰R^E+p 1 sX7̊ y]Uoq9is4#'O̮t^dsĶSDܭQf;86 @<$aY2uxϧÿ.w21_g-s-zQQj^%bC (aU6d5؏Ȕʨ +nXjgStSO/HTS4JЍsJ{hЂ 1j4U]QZ<~w5Ըqq?؃ 8z/SIմSyР5t؅{`ʳŚ)冃P}nqUbg1M}rrYwfj'.s / tL/'R9-xM0[ ~i^,VQ5q4'=-W4#&ֺm׿sEDmCj̴?7A|se/yt* !'r9JyVN]&k1K@Z]ZdƳbjG^y*g5\Smb`)]m&) <¥;W̬ ˬDÇ9<ᄪ.-uz2hU؜å{5_\6cO$+}1dzDjZQbz^EDk/t-/B7Z_eQ0QE3zL>Vt3Io(ν 1-eT9*ϐ\E/U沺u# G^w8ڕMWCxa<<'t4a6SeK2ie8kּm`'>??iܸ&l8 W7}*Iž8һcߢRr/Vs`zGDNgFzkVaг%xϭ[Ub"k<1nNBmE͌MeXTR@p`2[LX@EX3"WPc_^PO0J1Ӭ۬ OzUZ1i>] $5/&ɛ T)'΅L-:vSwӛ LQ mlWζ~y{+uY*yjEyyxr\rEk=V]{ QΦۼ^>*ٍo/,/jU cW(t)U=dzq,TF߶6wYǜ6=_R!emZâ/s2:ص)}X^.7u1?⡣.e5mưsznˋ#\+EW+X`[`0%(R  `dć#Π@]0HDkɭ1m+{#hpZ@wPώ0oKi'Sgd y@g֥#(BKi8)c;"m$`N%ԗI|V\R%|D4Mhj T_#oUKSm@4J^Vo]:aYlKlݚ|}65,tY@p>GfQ"ЋuW'p*z?׵w*tM9ë,Pk/ٸAG+ {s0b=yH31$bhySl$8qr2]V־y"DcfVƳځU_H@@CC e0s 98~cUPI˅q @_D۳huY^~jjynv}qχh"1\ӍzzT#G01e&pۭL43ѯA:۳7tuaaa^-!NLK$%"nU-o546矮5URq"fƋ8H0mᅻm$`<`(*iIҥf#7v#e,LԔm:|~5K&Fxver߼g nI>V߷U F7ThgcuB: 3G`WT+dl8^BpXəPLJC<[\Y/F˧ 9hk:JRsW,UCy=^3"@ x{{ Gl|'/e%G j݃UۈqԪ^ZUI'hJǠTgqG*cjܫO1]81B٩M[iQv+ %X+"?߲Lks(r!"Jlͺ%CFnUKz O6sh-B@TnUBګPwp,ZSVI9d;޹~}|E'<$0:6SO>rT3Kgwx2l[j3 P-(Rp5hݮs *wolGqb>>pnye+ϙ-}ܑ>{|vc|X r" 9]?qڭ\!P]@+ zMoJFD!h` (s.CLAD$[77Ap4X 2\VeLf(o/S5.4}=l ~-}=shWd2yR'ۏfT.U!@lˆy]P5εJ@%,:GeN̒1jy\sөY7*2_@j|lя}~]qF/ =O5S'2`-5z5%}rjL%/;{O;Bs1'bl`mul,NΝ~7wm+I`K` S%dz>BŝLE*}Z1 q=.e{Y2\Nj6/蝆DwGvINN㣟~rZ%#r3jQnguz]UMzZme%w %kwud.[u Y"歗[X r3 3}x??#U_g}pDŽ l8Vƕ#UkR kn`_6)n}>zᖸ2NdH Dwmۈ׏'@ /`4xZÃr-ؼc.COgՃ&!'7βg(}+w wv\4ћt5: 'qh5̖\'x< mw<~-c,X!Y85> TQйԮS mߛÍw5-gj?pNj?QOk΍kNu؜Oi܊VT5auD;T}Ew*4y۪3~bs%BRsGWLD6ggKaS euo|( DBt2Hos}0t##L 2 yI`G<fR(53aϟ5GT>k;sfrg!%bko΍1KVDtZWƵl;KǎU_v~뾧gS;G<D2Yԭz.mx"kIL%C넎*4^isțxb_\V3k-*xi* &TO`H?|w=\ZI$*z<891 MXj%%=$'>{Pp ֈL{hSW'4ȨFOE"2I%M}ĉIk$D DB>"L*ˤ1t2%xa~l'pQK5c(֪(qA1o%j@dM'O/hpJxQꇧ86ݒRz ߒg[Yy'tn_oMO4h+ lzEemcÑ,BdD(+!@ݍ Ru NhW4pwW_lv5lj<9o2_rIc6-$: b2F X"MvTm>ZW-t׫ a TAåCHwFJq:g_~t{,>r]qhIz^eZm~wƒTUN.p|lcsO5DUN>|s7zfk(T+~Fs_Um|$uWUw' P&n"~ pōiK4[,Ųqh<6΃x6C+@@ ntݮ{|x+p`dsQ\/K1O$)W0]_D8pm.קθ s׏[U?*Vp}"|5j]){ M zkuؾV%oo^?'VQ/<>[½4W @ {gS[* {9T9MbϦ𪊕lXP?Eqå]D(U*q-jUe$) L繬ȥV?3*9u27G@ w޻go'OW)92UzV͞!FBd=p=XI@ 奔EVt 'K?_Q!B@p\6}>$D~}uD}~\Gr2U,J . @ !@@ !@ @  @@ !@ @  @@ !@ @  @@ !@ @  @@ B@ @ @ @ B@ @G@  4 @  @@ !@ @  @@ !@ @ @ @ B@ @ @ @ B@ @ @ @ B@ @ @ @ B@ @ @ !@ @  @@ !@ gk$kIENDB`tiled-qt-0.9.1/src/plugins/replicaisland/tutorial.png000066400000000000000000000300771217502731700227130ustar00rootroot00000000000000PNG  IHDR\rftEXtSoftwareAdobe ImageReadyqe</IDATxُer}z{+(H? A_HJ/ `-)I 6/i;=Uu͕U9a"{2+2~ yrbŊqӉ[8.88 8.<'~|>_zOa !kx>~ƏS_?_o =o>$;O'/Z\B\\NG..{BpG..{BpG..{BpG..{BpG..{Bz|[(G+}Bkx!O0 xwtNCDp{..{.{\\N({\\N({O!+.= 8El( upQ2Q;Eѐ bQ )bQ )jq?|^G~\y 6?Y ~;g υd{xpʽ]N( {]N( {?1m{OO۷[([=?s? ̞].t0Y>a 츿'xqN?sXެo_BxF'}'txpCc(4z&id%N@pҢOs3N' r##1/Fi~Zy)~){SO E= ó]s)'8Fv\swq\kmqXcq>'8nQ{8Eswg$! p8Huq<((S<)?&Ը92JC(Bӎ;n'L>(ӊ;n'T>(Binn'\& pF$Q{7|9ww38Q{7|9FTww>G|O3_g(23==&E sWqyr=wN\{7|n)Q{7| , yvgܛJ11w>W{{ߝ_?&FIyK.o< s*^t!pp\\\Džq!p!pp\\Ng<9Wǡ號?8ϣ!c?c%K8 8 8 8 8 8 8 qǏ;O:x_2:֯۟ qq\qpqq\qpqq\qpqN?Y%] Ƞ6hH%90a]FvG̔-M&<6* [d=h£"v\O:%,wΌR&]m;]i;[I 0] Cip`_w}Q̲@0fsw'yeg'ۻ!ɵ]GlTlQ6(1<*M_8.Oapn4[qwӼv~i>^^Y:rzo7dDV'I/ouvZWZyXبDyP\q@뗊qfgj']vznr+Uk aeu!v1|]Jٮ魯GNp\aUq0؀o:.G4\qiѴ8db.VupYT%j⯶.<<[@5Ux0M:!рp/[\wzyܦӉ]|ya(_:RcXM`ϣc>r ˣV_kvu jYk\%Qg٫Ӧ˓Ftє/_XY 8CP6׻5S3ZjYhݦ];~8' VS3Mƭ^셦^XvCH~ژȫ#UQAUԸ `䗐 +S;IWS0չ~|>l$}O _y\]*xEq0MΤ:^\/_>[o`)RExV6kg[Ykv2Οņr q?|RsK[9SA͛~)9R̰0ڥIwy:^[W_8W yc-Xν4<j\j] kKXkLP ፫6 F RTiPDy@sw V_@z\+맔m|x=}D4u4H ;p0 Prܶk:^2KrWNHG yʹyrmw B+!j9Y<.@v=֫izq{7_#vRu?ƺ[?~|. -U#"(ưg%  d M HOZ{~׿y;w㩾7ׄ;sư/Ns3[nFׯ^!gGX?ѿ}P LUc0V|`k?i1|SKeXJ@h ҥ _?;}4#UOKWp.<|ۃ!o'.Nخu^|8n_SGG!!@x{E"XA,"AE hEbYY!x?y[9n!ˁ\ET/2s$AU[a9)_z~xmy!@;1u2A?ҿF355rH@@1JP" e5՗%c64 *B.߻}O_!K0Z]Xq7t:a#8ϣ0uXMճk*v~z`(?_~'ꠡl/O&(b Ȓ'(ro9[-? C(a2I uE}}{sC@Juww/u%D]rgq(|)>^E2Vl+V35C#3}'o+u,,{^E! 1i!A ;E P=` - ŨTdK@h)^fc6 +`VEh@glRl`g;g㸊\&Y)%|0̷VsŔ8WQ?+;X 5-n#MYн%0 倏LKjػuKY_V +MKYprDύXֶ-׎ U8lNRmU̜,UXL˄[Y ʞi"T'65)-ƟjR:]{uU 1e0%c@.dh{O ٘f)F{alcRTD#]9d)@3q\.ܙ.E̜,BJyMy~8W*>,o[j B+)޲}N_oh@ ͳx91-_U!,Ҡ>oPr A`B+ M%ӥNbB0T0CY^L )@<{7lL˂fje=1%{{ +4RCY8L@VϔH |o5Bg4){8C;Iҩ0%[H;'RBaK&G / JG6zKPalF3̨rOBAkKD P@KwJLd̝1g2) LTFST(Cif03j?S$, P)'H|}4 Qt sa\bS,.*)\J:4XnCGskƏBӰ58ۚJū[ր~hY6Kfka94a 9s6hHbF&{dqJ},ErYP!پ^|Cr8kICt.e)08'MT۔0(*?晔Yf x5XF;PTYCٶ]m62d? +T/74D$"*%E,RYh_ 0dڴ-cq.fq a#!rB||)YYYɶUɽXI Py3R{x /j"Z}b17Ue#$g M^oH/xH2p2H`1 T1PvpN^0Yt AUtMo( , 3b0Ԩ l >&HhYNG쟯>-0#LABĦjBܳ3OL @Um2@ @V:6!#-$afkPX(]ek0{`%U[6֛Zr%o3֗[1r֢J5 en` ՑA͢f~9'I,gZcZZÒY7nqY,Z556ijBa:͎ݟۇ Ҡ ^pAP`ZD0K4hƒ' RECESCz}="V0P4r6#f]F4"0x):s+?[%V? T,{`_`WATn0i˫m< |̖"TR?))ƚmx+&ijV[fSGf)uUbI>Ilk3 jU0/oD, ?XR  `B)G)^lILלfEzH*]sH'S4kN ]6$C71dK0mFs:2iӄFB`}Qc4Gzc@0 (v&ީlzϯ7#S5)SEP "J`r4OJU. 1%rJ*| ,DJŤD͐l9e!lʒFg/:'HN:LCހD>YzRa6P"3LbDȄPMHYV3RHl%@#/7USͦ9C"Tr+E 0Br&9"pc==Y$`9 뵟y9~4$h$)B V| <$@N"\hETe)_j "%D` 0A,CAFJlbC d!F+s6h5 Ry`ξ&=RfAJ!AQXQ!!(!ƚ M[ސC#RdD@Bf@(ʥL#:͗= I}~9')jf.i[su [t=K,)P QŐ޸1VU/h(`` }rk K4 Z;˜ &dB AjPrvKh0X]QF.ih999a4`C"fe i"&H I1%Q(!PD1!U0g /*L(ve1sIj HL)?fJq/k$j0+"aII&~ 9I@IK]1"f[k3z ?~˟,@ !e_:0$}.pМ,МL $*2i6ja9h@S #!pvf(vڙ!HˀwNR:!U@bP'\*ۂgOBlR&x]<)S~g?/yc>04~ŐL6ެ% }8"% X $!O3w&`\ +I!A8X19IDBT9%dU\Na/f:=#dqO,) e "Fc*su}^ǒiAʼ DVPHy`EUꋲ} t,eLH!Ȅd痛sܐ'&@!pզmO蓿yl,( Q(cfgfYL U@4)Xr2ndVs98KF7ebV p{e!fiuBf  С`"&!ԌVxbM;C@0e~Z<2q(ߝ}p?XEƪ*dfĽ/P|w_ރJU A&1`7pNrʠ|k"Ŕ͙1~xh#QE2F{OF?)_H)B s}`K&2g o`Od*Bv`vV$ܜ%\;im><;_|E iʌ3J@ d mVoC=-aG3'Z?xdN{ft(4!,el|˼+!Q؉T:FDI%#wc ;qe2&kewCkgg|,ec[۔3Mr7MI[flx3{ˆBwf$/\xge6- ۻy X޳/rN45c&vV-D{Q{Y5)[߳ƟeRW"7R@toA4%}Eq^½9ݴ2tܨDȭXc/3$ `l !㭴ծYyB97i) "?x698F~ހi5#f8aaS@Fԑ1r3FnC9Ve!"owӤvoweq,/BP3Rb3ZSU "3Ojc6L 0!}DR 9C~[~[n%Q]~ّUzdi/1y9hQd>in!I.W'?^/{emmIaeS뇁Skfe4m1:ՐEf}~Bޯ|oաUX7J6 uFDr#ZxQ]F h3&^?oko{^7NU6qv䄤d`c+ K0nKȿ_]oI}e!_"|ntk鵇Iج+nTl6ܯ ՗. T9Q̀+bU145^P %k@|02L34' +,g*4 ius/f߻7x+pbޞo@/u`o֑ul`֨ӗoL#M^_}=|d$"@_Ϧفl )[x7^w |Yͮz8jy|0l½&ۃ7w݋6_pu.Msiac0a[A4?*9}9;qL$3ۓ4=XQyQ[mMA%냆5υ;p/!0xFt6;˗V_Zpr >rA75 5߄;soH{ L4ťtz&]Wn*>h*y4r~ovq>$2MNw-Ώ[;ӥ|q8痪Tm q <~F?c+ͤNrku-k(k:ޙH8.hj&0m6cu2N\S8\;}D * * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TENGINE_GLOBAL_H #define TENGINE_GLOBAL_H #include #if defined(TENGINE_LIBRARY) # define TENGINESHARED_EXPORT Q_DECL_EXPORT #else # define TENGINESHARED_EXPORT Q_DECL_IMPORT #endif #endif // TENGINE_GLOBAL_H tiled-qt-0.9.1/src/plugins/tengine/tengineplugin.cpp000066400000000000000000000320551217502731700225330ustar00rootroot00000000000000/* * The T-Engine 4 Tiled Plugin * Copyright 2010, Mikolai Fajer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "tengineplugin.h" #include "map.h" #include "tile.h" #include "tilelayer.h" #include "objectgroup.h" #include "mapobject.h" #include "properties.h" #include #include #include #include #include using namespace Tengine; TenginePlugin::TenginePlugin() { } bool TenginePlugin::write(const Tiled::Map *map, const QString &fileName) { using namespace Tiled; QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { mError = tr("Could not open file for writing."); return false; } QTextStream out(&file); // Write the header QString header = map->property("header"); foreach (const QString &line, header.split("\\n")) { out << line << endl; } const int width = map->width(); const int height = map->height(); QList asciiMap; QHash cachedTiles; QList propertyOrder; propertyOrder.append("terrain"); propertyOrder.append("object"); propertyOrder.append("actor"); propertyOrder.append("trap"); propertyOrder.append("status"); propertyOrder.append("spot"); // Ability to handle overflow and strings for display bool outputLists = false; int asciiDisplay = ASCII_MIN; int overflowDisplay = 1; QHash::const_iterator i; // Add the empty tile int numEmptyTiles = 0; Properties emptyTile; emptyTile["display"] = "?"; cachedTiles["?"] = emptyTile; // Process the map, collecting used display strings as we go for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { Properties currentTile = cachedTiles["?"]; foreach (Layer *layer, map->layers()) { // If the layer name does not start with one of the tile properties, skip it QString layerKey; QListIterator propertyIterator = propertyOrder; while (propertyIterator.hasNext()) { QString currentProperty = propertyIterator.next(); if (layer->name().startsWith(currentProperty, Qt::CaseInsensitive)) { layerKey = currentProperty; break; } } if (layerKey.isEmpty()) { continue; } TileLayer *tileLayer = layer->asTileLayer(); ObjectGroup *objectLayer = layer->asObjectGroup(); // Process the Tile Layer if (tileLayer) { Tile *tile = tileLayer->cellAt(x, y).tile; if (tile) { currentTile["display"] = tile->property("display"); currentTile[layerKey] = tile->property("value"); } // Process the Object Layer } else if (objectLayer) { foreach (const MapObject *obj, objectLayer->objects()) { if (floor(obj->y()) <= y && y <= floor(obj->y() + obj->height())) { if (floor(obj->x()) <= x && x <= floor(obj->x() + obj->width())) { // Check the Object Layer properties if either display or value was missing if (!obj->property("display").isEmpty()) { currentTile["display"] = obj->property("display"); } else if (!objectLayer->property("display").isEmpty()) { currentTile["display"] = objectLayer->property("display"); } if (!obj->property("value").isEmpty()) { currentTile[layerKey] = obj->property("value"); } else if (!objectLayer->property("value").isEmpty()) { currentTile[layerKey] = objectLayer->property("value"); } } } } } } // If the currentTile does not exist in the cache, add it if (!cachedTiles.contains(currentTile["display"])) { cachedTiles[currentTile["display"]] = currentTile; // Otherwise check that it EXACTLY matches the cached one // and if not... } else if (currentTile != cachedTiles[currentTile["display"]]) { // Search the cached tiles for a match bool foundInCache = false; QString displayString; for (i = cachedTiles.constBegin(); i != cachedTiles.constEnd(); ++i) { displayString = i.key(); currentTile["display"] = displayString; if (currentTile == i.value()) { foundInCache = true; break; } } // If we haven't found a match then find a random display string // and cache it if (!foundInCache) { while (true) { // First try to use the ASCII characters if (asciiDisplay < ASCII_MAX) { displayString = QString(QChar::fromLatin1(asciiDisplay)); asciiDisplay++; // Then fall back onto integers } else { displayString = QString::number(overflowDisplay); overflowDisplay++; } currentTile["display"] = displayString; if (!cachedTiles.contains(displayString)) { cachedTiles[displayString] = currentTile; break; } else if (currentTile == cachedTiles[currentTile["display"]]) { break; } } } } // Check the output type if (currentTile["display"].length() > 1) { outputLists = true; } // Check if we are still the emptyTile if (currentTile == emptyTile) { numEmptyTiles++; } // Finally add the character to the asciiMap asciiMap.append(currentTile["display"]); } } // Write the definitions to the file out << "-- defineTile section" << endl; for (i = cachedTiles.constBegin(); i != cachedTiles.constEnd(); ++i) { QString displayString = i.key(); // Only print the emptyTile definition if there were empty tiles if (displayString == QLatin1String("?") && numEmptyTiles == 0) { continue; } // Need to escape " and \ characters displayString.replace(QLatin1Char('\\'), "\\\\"); displayString.replace(QLatin1Char('"'), "\\\""); QString args = constructArgs(i.value(), propertyOrder); if (!args.isEmpty()) { args = QString(", %1").arg(args); } out << QString("defineTile(\"%1\"%2)").arg(displayString, args) << endl; } // Check for an ObjectGroup named AddSpot out << endl << "-- addSpot section" << endl; foreach (Layer *layer, map->layers()) { ObjectGroup *objectLayer = layer->asObjectGroup(); if (objectLayer && objectLayer->name().startsWith("addspot", Qt::CaseInsensitive)) { foreach (const MapObject *obj, objectLayer->objects()) { QList propertyOrder; propertyOrder.append("type"); propertyOrder.append("subtype"); propertyOrder.append("additional"); QString args = constructArgs(obj->properties(), propertyOrder); if (!args.isEmpty()) { args = QString(", %1").arg(args); } for (int y = floor(obj->y()); y <= floor(obj->y() + obj->height()); ++y) { for (int x = floor(obj->x()); x <= floor(obj->x() + obj->width()); ++x) { out << QString("addSpot({%1, %2}%3)").arg(x).arg(y).arg(args) << endl; } } } } } // Check for an ObjectGroup named AddZone out << endl << "-- addZone section" << endl; foreach (Layer *layer, map->layers()) { ObjectGroup *objectLayer = layer->asObjectGroup(); if (objectLayer && objectLayer->name().startsWith("addzone", Qt::CaseInsensitive)) { foreach (MapObject *obj, objectLayer->objects()) { QList propertyOrder; propertyOrder.append("type"); propertyOrder.append("subtype"); propertyOrder.append("additional"); QString args = constructArgs(obj->properties(), propertyOrder); if (!args.isEmpty()) { args = QString(", %1").arg(args); } int top_left_x = floor(obj->x()); int top_left_y = floor(obj->y()); int bottom_right_x = floor(obj->x() + obj->width()); int bottom_right_y = floor(obj->y() + obj->height()); out << QString("addZone({%1, %2, %3, %4}%5)").arg(top_left_x).arg(top_left_y).arg(bottom_right_x).arg(bottom_right_y).arg(args) << endl; } } } // Write the map QString returnStart; QString returnStop; QString lineStart; QString lineStop; QString itemStart; QString itemStop; QString seperator; if (outputLists) { returnStart = "{"; returnStop = "}"; lineStart = "{"; lineStop = "},"; itemStart = "[["; itemStop = "]]"; seperator = ","; } else { returnStart = "[["; returnStop = "]]"; lineStart = ""; lineStop = ""; itemStart = ""; itemStop = ""; seperator = ""; } out << endl << "-- ASCII map section" << endl; out << "return " << returnStart << endl; for (int y = 0; y < height; ++y) { out << lineStart; for (int x = 0; x < width; ++x) { out << itemStart << asciiMap[x + (y * width)] << itemStop << seperator; } if (y == height - 1) { out << lineStop << returnStop; } else { out << lineStop << endl; } } // And close the file file.close(); return true; } QString TenginePlugin::nameFilter() const { return tr("T-Engine4 map files (*.lua)"); } QString TenginePlugin::errorString() const { return mError; } QString TenginePlugin::constructArgs(Tiled::Properties props, QList propOrder) const { QString argString; // We work backwards so we don't have to include a bunch of nils for (int i = propOrder.size() - 1; i >= 0; --i) { QString currentValue = props[propOrder[i]]; // Special handling of the "additional" property if ((propOrder[i] == "additional") && currentValue.isEmpty()) { currentValue = constructAdditionalTable(props, propOrder); } if (!argString.isEmpty()) { if (currentValue.isEmpty()) { currentValue = "nil"; } argString = QString("%1, %2").arg(currentValue, argString); } else if (!currentValue.isEmpty()) { argString = currentValue; } } return argString; } // Finds unhandled properties and bundles them into a Lua table QString TenginePlugin::constructAdditionalTable(Tiled::Properties props, QList propOrder) const { QString tableString; QMap unhandledProps = QMap(props); // Remove handled properties for (int i = 0; i < propOrder.size(); i++) { unhandledProps.remove(propOrder[i]); } // Construct the Lua string if (unhandledProps.size() > 0) { tableString = "{"; QMapIterator i(unhandledProps); while (i.hasNext()) { i.next(); tableString = QString("%1%2=%3,").arg(tableString, i.key(), i.value()); } tableString = QString("%1}").arg(tableString); } return tableString; } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Tengine, TenginePlugin) #endif tiled-qt-0.9.1/src/plugins/tengine/tengineplugin.h000066400000000000000000000034251217502731700221770ustar00rootroot00000000000000/* * The T-Engine 4 Tiled Plugin * Copyright 2010, Mikolai Fajer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TENGINEPLUGIN_H #define TENGINEPLUGIN_H #include "tengine_global.h" #include "mapwriterinterface.h" #include "properties.h" #include namespace Tengine { // ASCII characters between decimals 32 and 126 should be ok const int ASCII_MIN = 32; const int ASCII_MAX = 126; class TENGINESHARED_EXPORT TenginePlugin : public QObject, public Tiled::MapWriterInterface { Q_OBJECT Q_INTERFACES(Tiled::MapWriterInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapWriterInterface" FILE "plugin.json") #endif public: TenginePlugin(); // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); QString nameFilter() const; QString errorString() const; private: QString mError; QString constructArgs(Tiled::Properties props, QList propOrder) const; QString constructAdditionalTable(Tiled::Properties props, QList propOrder) const; }; } // namespace Tengine #endif // TENGINEPLUGIN_H tiled-qt-0.9.1/src/plugins/tmw/000077500000000000000000000000001217502731700163305ustar00rootroot00000000000000tiled-qt-0.9.1/src/plugins/tmw/plugin.json000066400000000000000000000000321217502731700205140ustar00rootroot00000000000000{ "Keys": [ "notused" ] } tiled-qt-0.9.1/src/plugins/tmw/tmw.pro000066400000000000000000000001661217502731700176640ustar00rootroot00000000000000include(../plugin.pri) DEFINES += TMW_LIBRARY SOURCES += tmwplugin.cpp HEADERS += tmwplugin.h\ tmw_global.h tiled-qt-0.9.1/src/plugins/tmw/tmw_global.h000066400000000000000000000017521217502731700206350ustar00rootroot00000000000000/* * The Mana World Tiled Plugin * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TMW_GLOBAL_H #define TMW_GLOBAL_H #include #if defined(TMW_LIBRARY) # define TMWSHARED_EXPORT Q_DECL_EXPORT #else # define TMWSHARED_EXPORT Q_DECL_IMPORT #endif #endif // TMW_GLOBAL_H tiled-qt-0.9.1/src/plugins/tmw/tmwplugin.cpp000066400000000000000000000047371217502731700210750ustar00rootroot00000000000000/* * The Mana World Tiled Plugin * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "tmwplugin.h" #include "map.h" #include "tile.h" #include "tilelayer.h" #include #include using namespace Tmw; TmwPlugin::TmwPlugin() { } bool TmwPlugin::write(const Tiled::Map *map, const QString &fileName) { using namespace Tiled; TileLayer *collisionLayer = 0; foreach (Layer *layer, map->layers()) { if (layer->name().compare(QLatin1String("collision"), Qt::CaseInsensitive) == 0) { if (TileLayer *tileLayer = layer->asTileLayer()) { if (collisionLayer) { mError = tr("Multiple collision layers found!"); return false; } collisionLayer = tileLayer; } } } if (!collisionLayer) { mError = tr("No collision layer found!"); return false; } QFile file(fileName); if (!file.open(QIODevice::WriteOnly)) { mError = tr("Could not open file for writing."); return false; } const int width = collisionLayer->width(); const int height = collisionLayer->height(); QDataStream stream(&file); stream.setByteOrder(QDataStream::LittleEndian); stream << (qint16) width; stream << (qint16) height; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { Tile *tile = collisionLayer->cellAt(x, y).tile; stream << (qint8) (tile && tile->id() > 0); } } return true; } QString TmwPlugin::nameFilter() const { return tr("TMW-eAthena collision files (*.wlk)"); } QString TmwPlugin::errorString() const { return mError; } #if QT_VERSION < 0x050000 Q_EXPORT_PLUGIN2(Tmw, TmwPlugin) #endif tiled-qt-0.9.1/src/plugins/tmw/tmwplugin.h000066400000000000000000000027021217502731700205300ustar00rootroot00000000000000/* * The Mana World Tiled Plugin * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TMWPLUGIN_H #define TMWPLUGIN_H #include "tmw_global.h" #include "mapwriterinterface.h" #include namespace Tmw { class TMWSHARED_EXPORT TmwPlugin : public QObject, public Tiled::MapWriterInterface { Q_OBJECT Q_INTERFACES(Tiled::MapWriterInterface) #if QT_VERSION >= 0x050000 Q_PLUGIN_METADATA(IID "org.mapeditor.MapWriterInterface" FILE "plugin.json") #endif public: TmwPlugin(); // MapWriterInterface bool write(const Tiled::Map *map, const QString &fileName); QString nameFilter() const; QString errorString() const; private: QString mError; }; } // namespace Mana #endif // TMWPLUGIN_H tiled-qt-0.9.1/src/src.pro000066400000000000000000000002111217502731700153430ustar00rootroot00000000000000TEMPLATE = subdirs CONFIG += ordered SUBDIRS = libtiled tiled plugins \ tmxviewer \ tmxrasterizer \ automappingconverter tiled-qt-0.9.1/src/tiled/000077500000000000000000000000001217502731700151415ustar00rootroot00000000000000tiled-qt-0.9.1/src/tiled/Info.plist000066400000000000000000000024661217502731700171210ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable Tiled CFBundleGetInfoString Tiled 0.9.1, Copyright 2008-2013 Thorbjørn Lindeijer, GNU General Public License CFBundleIconFile tiled-icon-mac CFBundleIdentifier org.mapeditor.Tiled CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString 0.9.1 CFBundleVersion 0.9.1 CSResourcesFileMapped NSHumanReadableCopyright Copyright 2008-2013 Thorbjørn Lindeijer NSHighResolutionCapable CFBundleDocumentTypes CFBundleTypeName Tiled Map CFBundleTypeIconFile tmx-icon-mac CFBundleTypeRole Editor CFBundleTypeExtensions tmx tiled-qt-0.9.1/src/tiled/aboutdialog.cpp000066400000000000000000000034541217502731700201450ustar00rootroot00000000000000/* * aboutdialog.cpp * Copyright 2008-2009, Thorbjørn Lindeijer * Copyright 2009, Dennis Honeyman * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "aboutdialog.h" #include using namespace Tiled::Internal; AboutDialog::AboutDialog(QWidget *parent): QDialog(parent) { setupUi(this); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); const QString html = QCoreApplication::translate( "AboutDialog", "

Tiled Map Editor
Version %1

\n" "

Copyright 2008-2013 Thorbjørn Lindeijer
(see the AUTHORS file for a full list of contributors)

\n" "

You may modify and redistribute this program under the terms of the GPL (version 2 or later). " "A copy of the GPL is contained in the 'COPYING' file distributed with Tiled.

\n" "

http://www.mapeditor.org/

\n") .arg(QApplication::applicationVersion()); textBrowser->setHtml(html); } tiled-qt-0.9.1/src/tiled/aboutdialog.h000066400000000000000000000022131217502731700176020ustar00rootroot00000000000000/* * aboutdialog.h * Copyright 2008-2009, Thorbjørn Lindeijer * Copyright 2009, Dennis Honeyman * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ABOUTDIALOG_H #define ABOUTDIALOG_H #include #include "ui_aboutdialog.h" namespace Tiled { namespace Internal { class AboutDialog : public QDialog, private Ui::AboutDialog { Q_OBJECT public: AboutDialog(QWidget *parent = 0); }; } // namespace Internal } // namespace Tiled #endif // ABOUTDIALOG_H tiled-qt-0.9.1/src/tiled/aboutdialog.ui000066400000000000000000000064261217502731700200020ustar00rootroot00000000000000 AboutDialog 0 0 432 460 About Tiled QLayout::SetDefaultConstraint Qt::Horizontal 0 20 400 200 :/images/about-tiled-logo.png Qt::Horizontal 0 20 true Qt::Vertical 20 0 QLayout::SetDefaultConstraint Qt::Horizontal QSizePolicy::Expanding 40 20 OK pushButton clicked() AboutDialog close() 460 347 255 185 tiled-qt-0.9.1/src/tiled/abstractobjecttool.cpp000066400000000000000000000170331217502731700215410ustar00rootroot00000000000000/* * abstractobjecttool.cpp * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "abstractobjecttool.h" #include "addremovemapobject.h" #include "map.h" #include "mapdocument.h" #include "mapobject.h" #include "mapobjectitem.h" #include "maprenderer.h" #include "mapscene.h" #include "movemapobjecttogroup.h" #include "objectgroup.h" #include "objectpropertiesdialog.h" #include "utils.h" #include #include #include using namespace Tiled; using namespace Tiled::Internal; AbstractObjectTool::AbstractObjectTool(const QString &name, const QIcon &icon, const QKeySequence &shortcut, QObject *parent) : AbstractTool(name, icon, shortcut, parent) , mMapScene(0) { } void AbstractObjectTool::activate(MapScene *scene) { mMapScene = scene; } void AbstractObjectTool::deactivate(MapScene *) { mMapScene = 0; } void AbstractObjectTool::mouseLeft() { setStatusInfo(QString()); } void AbstractObjectTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers) { const QPointF tilePosF = mapDocument()->renderer()->pixelToTileCoords(pos); const int x = (int) std::floor(tilePosF.x()); const int y = (int) std::floor(tilePosF.y()); setStatusInfo(QString(QLatin1String("%1, %2")).arg(x).arg(y)); } void AbstractObjectTool::mousePressed(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::RightButton) { showContextMenu(topMostObjectItemAt(event->scenePos()), event->screenPos(), event->widget()); } } void AbstractObjectTool::updateEnabledState() { setEnabled(currentObjectGroup() != 0); } ObjectGroup *AbstractObjectTool::currentObjectGroup() const { if (!mapDocument()) return 0; return dynamic_cast(mapDocument()->currentLayer()); } MapObjectItem *AbstractObjectTool::topMostObjectItemAt(QPointF pos) const { foreach (QGraphicsItem *item, mMapScene->items(pos)) { if (MapObjectItem *objectItem = dynamic_cast(item)) return objectItem; } return 0; } /** * Shows the context menu for map objects. The menu allows you to duplicate and * remove the map objects, or to edit their properties. */ void AbstractObjectTool::showContextMenu(MapObjectItem *clickedObjectItem, QPoint screenPos, QWidget *parent) { QSet selection = mMapScene->selectedObjectItems(); if (clickedObjectItem && !selection.contains(clickedObjectItem)) { selection.clear(); selection.insert(clickedObjectItem); mMapScene->setSelectedObjectItems(selection); } if (selection.isEmpty()) return; const QList selectedObjects = mapDocument()->selectedObjects(); QList objectGroups; foreach (Layer *layer, mapDocument()->map()->layers()) { if (ObjectGroup *objectGroup = layer->asObjectGroup()) objectGroups.append(objectGroup); } QMenu menu; QIcon dupIcon(QLatin1String(":images/16x16/stock-duplicate-16.png")); QIcon delIcon(QLatin1String(":images/16x16/edit-delete.png")); QIcon propIcon(QLatin1String(":images/16x16/document-properties.png")); QString dupText = tr("Duplicate %n Object(s)", "", selectedObjects.size()); QString removeText = tr("Remove %n Object(s)", "", selectedObjects.size()); QAction *dupAction = menu.addAction(dupIcon, dupText); QAction *removeAction = menu.addAction(delIcon, removeText); typedef QMap MoveToLayerActionMap; MoveToLayerActionMap moveToLayerActions; if (objectGroups.size() > 1) { menu.addSeparator(); QMenu *moveToLayerMenu = menu.addMenu(tr("Move %n Object(s) to Layer", "", selectedObjects.size())); foreach (ObjectGroup *objectGroup, objectGroups) { QAction *action = moveToLayerMenu->addAction(objectGroup->name()); moveToLayerActions.insert(action, objectGroup); } } menu.addSeparator(); QAction *propertiesAction = menu.addAction(propIcon, tr("Object &Properties...")); // TODO: Implement editing of properties for multiple objects propertiesAction->setEnabled(selectedObjects.size() == 1); Utils::setThemeIcon(removeAction, "edit-delete"); Utils::setThemeIcon(propertiesAction, "document-properties"); QAction *selectedAction = menu.exec(screenPos); if (selectedAction == dupAction) { duplicateObjects(selectedObjects); } else if (selectedAction == removeAction) { removeObjects(selectedObjects); } else if (selectedAction == propertiesAction) { MapObject *mapObject = selectedObjects.first(); ObjectPropertiesDialog propertiesDialog(mapDocument(), mapObject, parent); propertiesDialog.exec(); } MoveToLayerActionMap::const_iterator i = moveToLayerActions.find(selectedAction); if (i != moveToLayerActions.end()) { ObjectGroup *objectGroup = i.value(); moveObjectsToGroup(selectedObjects, objectGroup); } } void AbstractObjectTool::duplicateObjects(const QList &objects) { QUndoStack *undoStack = mapDocument()->undoStack(); undoStack->beginMacro(tr("Duplicate %n Object(s)", "", objects.size())); QList clones; foreach (const MapObject *mapObject, objects) { MapObject *clone = mapObject->clone(); clones.append(clone); undoStack->push(new AddMapObject(mapDocument(), mapObject->objectGroup(), clone)); } undoStack->endMacro(); mapDocument()->setSelectedObjects(clones); } void AbstractObjectTool::removeObjects(const QList &objects) { QUndoStack *undoStack = mapDocument()->undoStack(); undoStack->beginMacro(tr("Remove %n Object(s)", "", objects.size())); foreach (MapObject *mapObject, objects) undoStack->push(new RemoveMapObject(mapDocument(), mapObject)); undoStack->endMacro(); } void AbstractObjectTool::moveObjectsToGroup(const QList &objects, ObjectGroup *objectGroup) { QUndoStack *undoStack = mapDocument()->undoStack(); undoStack->beginMacro(tr("Move %n Object(s) to Layer", "", objects.size())); foreach (MapObject *mapObject, objects) { if (mapObject->objectGroup() == objectGroup) continue; undoStack->push(new MoveMapObjectToGroup(mapDocument(), mapObject, objectGroup)); } undoStack->endMacro(); } tiled-qt-0.9.1/src/tiled/abstractobjecttool.h000066400000000000000000000046171217502731700212120ustar00rootroot00000000000000/* * abstractobjecttool.h * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ABSTRACTOBJECTTOOL_H #define ABSTRACTOBJECTTOOL_H #include "abstracttool.h" namespace Tiled { class MapObject; class ObjectGroup; namespace Internal { class MapObjectItem; /** * A convenient base class for tools that work on object layers. Implements * the standard context menu. */ class AbstractObjectTool : public AbstractTool { Q_OBJECT public: /** * Constructs an abstract object tool with the given \a name and \a icon. */ AbstractObjectTool(const QString &name, const QIcon &icon, const QKeySequence &shortcut, QObject *parent = 0); void activate(MapScene *scene); void deactivate(MapScene *scene); void mouseLeft(); void mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers); void mousePressed(QGraphicsSceneMouseEvent *event); protected: /** * Overridden to only enable this tool when the currently selected layer is * an object group. */ void updateEnabledState(); MapScene *mapScene() const { return mMapScene; } ObjectGroup *currentObjectGroup() const; MapObjectItem *topMostObjectItemAt(QPointF pos) const; private: void showContextMenu(MapObjectItem *clickedObject, QPoint screenPos, QWidget *parent); void duplicateObjects(const QList &objects); void removeObjects(const QList &objects); void moveObjectsToGroup(const QList &objects, ObjectGroup *objectGroup); MapScene *mMapScene; }; } // namespace Internal } // namespace Tiled #endif // ABSTRACTOBJECTTOOL_H tiled-qt-0.9.1/src/tiled/abstracttiletool.cpp000066400000000000000000000072401217502731700212270ustar00rootroot00000000000000/* * abstracttiletool.cpp * Copyright 2009-2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "abstracttiletool.h" #include "brushitem.h" #include "map.h" #include "mapdocument.h" #include "maprenderer.h" #include "mapscene.h" #include "tilelayer.h" #include using namespace Tiled; using namespace Tiled::Internal; AbstractTileTool::AbstractTileTool(const QString &name, const QIcon &icon, const QKeySequence &shortcut, QObject *parent) : AbstractTool(name, icon, shortcut, parent) , mTilePositionMethod(OnTiles) , mBrushItem(new BrushItem) , mTileX(0), mTileY(0) , mBrushVisible(false) { mBrushItem->setVisible(false); mBrushItem->setZValue(10000); } AbstractTileTool::~AbstractTileTool() { delete mBrushItem; } void AbstractTileTool::activate(MapScene *scene) { scene->addItem(mBrushItem); } void AbstractTileTool::deactivate(MapScene *scene) { scene->removeItem(mBrushItem); } void AbstractTileTool::mouseEntered() { setBrushVisible(true); } void AbstractTileTool::mouseLeft() { setBrushVisible(false); } void AbstractTileTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers) { const MapRenderer *renderer = mapDocument()->renderer(); const QPointF tilePosF = renderer->pixelToTileCoords(pos); QPoint tilePos; if (mTilePositionMethod == BetweenTiles) tilePos = tilePosF.toPoint(); else tilePos = QPoint((int) std::floor(tilePosF.x()), (int) std::floor(tilePosF.y())); if (mTileX != tilePos.x() || mTileY != tilePos.y()) { mTileX = tilePos.x(); mTileY = tilePos.y(); tilePositionChanged(tilePos); updateStatusInfo(); } } void AbstractTileTool::mapDocumentChanged(MapDocument *oldDocument, MapDocument *newDocument) { Q_UNUSED(oldDocument) mBrushItem->setMapDocument(newDocument); } void AbstractTileTool::updateEnabledState() { setEnabled(currentTileLayer() != 0); } void AbstractTileTool::updateStatusInfo() { if (mBrushVisible) { setStatusInfo(QString(QLatin1String("%1, %2")) .arg(mTileX).arg(mTileY)); } else { setStatusInfo(QString()); } } void AbstractTileTool::setBrushVisible(bool visible) { if (mBrushVisible == visible) return; mBrushVisible = visible; updateStatusInfo(); updateBrushVisibility(); } void AbstractTileTool::updateBrushVisibility() { // Show the tile brush only when a visible tile layer is selected bool showBrush = false; if (mBrushVisible) { if (Layer *layer = currentTileLayer()) { if (layer->isVisible()) showBrush = true; } } mBrushItem->setVisible(showBrush); } TileLayer *AbstractTileTool::currentTileLayer() const { if (!mapDocument()) return 0; return dynamic_cast(mapDocument()->currentLayer()); } tiled-qt-0.9.1/src/tiled/abstracttiletool.h000066400000000000000000000073271217502731700207020ustar00rootroot00000000000000/* * abstracttiletool.h * Copyright 2009-2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ABSTRACTTILETOOL_H #define ABSTRACTTILETOOL_H #include "abstracttool.h" namespace Tiled { class TileLayer; namespace Internal { class BrushItem; class MapDocument; /** * A convenient base class for tile based tools. */ class AbstractTileTool : public AbstractTool { Q_OBJECT public: /** * Constructs an abstract tile tool with the given \a name and \a icon. */ AbstractTileTool(const QString &name, const QIcon &icon, const QKeySequence &shortcut, QObject *parent = 0); ~AbstractTileTool(); void activate(MapScene *scene); void deactivate(MapScene *scene); void mouseEntered(); void mouseLeft(); void mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers); protected: void mapDocumentChanged(MapDocument *oldDocument, MapDocument *newDocument); /** * Overridden to only enable this tool when the currently selected layer is * a tile layer. */ void updateEnabledState(); /** * New virtual method to implement for tile tools. This method is called * on mouse move events, but only when the tile position changes. */ virtual void tilePositionChanged(const QPoint &tilePos) = 0; /** * Updates the status info with the current tile position. When the mouse * is not in the view, the status info is set to an empty string. * * This behaviour can be overridden in a subclass. This method is * automatically called after each call to tilePositionChanged() and when * the brush visibility changes. */ virtual void updateStatusInfo(); bool isBrushVisible() const { return mBrushVisible; } /** * Determines what the tile position means. */ enum TilePositionMethod { OnTiles, /**< Tile position is the tile the mouse is on. */ BetweenTiles /**< Tile position is between the tiles. */ }; void setTilePositionMethod(TilePositionMethod method) { mTilePositionMethod = method; } /** * Returns the last recorded tile position of the mouse. */ QPoint tilePosition() const { return QPoint(mTileX, mTileY); } /** * Returns the brush item. The brush item is used to give an indication of * what a tile tool is going to do when used. It is automatically shown or * hidden based on whether the mouse is in the scene and whether the * currently selected layer is a tile layer. */ BrushItem *brushItem() const { return mBrushItem; } /** * Returns the current tile layer, or 0 if no tile layer is currently * selected. */ TileLayer *currentTileLayer() const; private: void setBrushVisible(bool visible); void updateBrushVisibility(); TilePositionMethod mTilePositionMethod; BrushItem *mBrushItem; int mTileX, mTileY; bool mBrushVisible; }; } // namespace Internal } // namespace Tiled #endif // ABSTRACTTILETOOL_H tiled-qt-0.9.1/src/tiled/abstracttool.cpp000066400000000000000000000053131217502731700203500ustar00rootroot00000000000000/* * abstracttool.cpp * Copyright 2009-2010, Thorbjørn Lindeijer * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "abstracttool.h" #include "mapdocument.h" #include "mapdocumentactionhandler.h" using namespace Tiled::Internal; AbstractTool::AbstractTool(const QString &name, const QIcon &icon, const QKeySequence &shortcut, QObject *parent) : QObject(parent) , mName(name) , mIcon(icon) , mShortcut(shortcut) , mEnabled(true) , mMapDocument(0) { MapDocumentActionHandler *handler = MapDocumentActionHandler::instance(); connect(handler, SIGNAL(mapDocumentChanged(MapDocument*)), SLOT(setMapDocument(MapDocument*))); } /** * Sets the current status information for this tool. This information will be * displayed in the status bar. */ void AbstractTool::setStatusInfo(const QString &statusInfo) { if (mStatusInfo != statusInfo) { mStatusInfo = statusInfo; emit statusInfoChanged(mStatusInfo); } } void AbstractTool::setEnabled(bool enabled) { if (mEnabled == enabled) return; mEnabled = enabled; emit enabledChanged(mEnabled); } void AbstractTool::updateEnabledState() { setEnabled(mMapDocument != 0); } void AbstractTool::setMapDocument(MapDocument *mapDocument) { if (mMapDocument == mapDocument) return; if (mMapDocument) { disconnect(mMapDocument, SIGNAL(layerChanged(int)), this, SLOT(updateEnabledState())); disconnect(mMapDocument, SIGNAL(currentLayerIndexChanged(int)), this, SLOT(updateEnabledState())); } MapDocument *oldDocument = mMapDocument; mMapDocument = mapDocument; mapDocumentChanged(oldDocument, mMapDocument); if (mMapDocument) { connect(mMapDocument, SIGNAL(layerChanged(int)), this, SLOT(updateEnabledState())); connect(mMapDocument, SIGNAL(currentLayerIndexChanged(int)), this, SLOT(updateEnabledState())); } updateEnabledState(); } tiled-qt-0.9.1/src/tiled/abstracttool.h000066400000000000000000000114501217502731700200140ustar00rootroot00000000000000/* * abstracttool.h * Copyright 2009-2010, Thorbjørn Lindeijer * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ABSTRACTTOOL_H #define ABSTRACTTOOL_H #include #include #include #include #include #include class QEvent; namespace Tiled { namespace Internal { class MapDocument; class MapScene; /** * An abstraction of any kind of tool used to edit the map. * * Events that hit the MapScene are forwarded to the current tool, which can * handle them as appropriate for that tool. * * A tool will usually add one or more QGraphicsItems to the scene in order to * represent it to the user. */ class AbstractTool : public QObject { Q_OBJECT public: /** * Constructs an abstract tool with the given \a name and \a icon. */ AbstractTool(const QString &name, const QIcon &icon, const QKeySequence &shortcut, QObject *parent = 0); virtual ~AbstractTool() {} QString name() const { return mName; } void setName(const QString &name) { mName = name; } QIcon icon() const { return mIcon; } void setIcon(const QIcon &icon) { mIcon = icon; } QKeySequence shortcut() const { return mShortcut; } void setShortcut(const QKeySequence &shortcut) { mShortcut = shortcut; } QString statusInfo() const { return mStatusInfo; } void setStatusInfo(const QString &statusInfo); bool isEnabled() const { return mEnabled; } void setEnabled(bool enabled); /** * Activates this tool. If the tool plans to add any items to the scene, it * probably wants to do it here. */ virtual void activate(MapScene *scene) = 0; /** * Deactivates this tool. Should do any necessary cleanup to make sure the * tool is no longer active. */ virtual void deactivate(MapScene *scene) = 0; /** * Called when the mouse entered the scene. This is usually an appropriate * time to make a hover item visible. */ virtual void mouseEntered() = 0; /** * Called when the mouse left the scene. */ virtual void mouseLeft() = 0; /** * Called when the mouse cursor moves in the scene. */ virtual void mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers) = 0; /** * Called when a mouse button is pressed on the scene. */ virtual void mousePressed(QGraphicsSceneMouseEvent *event) = 0; /** * Called when a mouse button is released on the scene. */ virtual void mouseReleased(QGraphicsSceneMouseEvent *event) = 0; /** * Called when the user presses or releases a modifier key resulting * in a change of modifier status, and when the tool is enabled with * a modifier key pressed. */ virtual void modifiersChanged(Qt::KeyboardModifiers) {} /** * Called when the application language changed. */ virtual void languageChanged() = 0; protected: /** * Can be used to respond to the map document changing. */ virtual void mapDocumentChanged(MapDocument *oldDocument, MapDocument *newDocument) { Q_UNUSED(oldDocument) Q_UNUSED(newDocument) } MapDocument *mapDocument() const { return mMapDocument; } protected slots: /** * By default, this function is called after the current map has changed * and when the current layer changes. It can be overridden to implement * custom logic for when the tool should be enabled. * * The default implementation enables tools when a map document is set. */ virtual void updateEnabledState(); signals: void statusInfoChanged(const QString &statusInfo); void enabledChanged(bool enabled); private slots: void setMapDocument(MapDocument *mapDocument); private: QString mName; QIcon mIcon; QKeySequence mShortcut; QString mStatusInfo; bool mEnabled; MapDocument *mMapDocument; }; } // namespace Internal } // namespace Tiled Q_DECLARE_METATYPE(Tiled::Internal::AbstractTool*) #endif // ABSTRACTTOOL_H tiled-qt-0.9.1/src/tiled/addremovelayer.cpp000066400000000000000000000036041217502731700206530ustar00rootroot00000000000000/* * addremovelayer.cpp * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "addremovelayer.h" #include "layer.h" #include "layermodel.h" #include "mapdocument.h" namespace Tiled { namespace Internal { AddRemoveLayer::AddRemoveLayer(MapDocument *mapDocument, int index, Layer *layer) : mMapDocument(mapDocument) , mLayer(layer) , mIndex(index) { } AddRemoveLayer::~AddRemoveLayer() { delete mLayer; } void AddRemoveLayer::addLayer() { const int currentLayer = mMapDocument->currentLayerIndex(); mMapDocument->layerModel()->insertLayer(mIndex, mLayer); mLayer = 0; // Insertion below or at the current layer increases current layer index if (mIndex <= currentLayer) mMapDocument->setCurrentLayerIndex(currentLayer + 1); } void AddRemoveLayer::removeLayer() { const int currentLayer = mMapDocument->currentLayerIndex(); mLayer = mMapDocument->layerModel()->takeLayerAt(mIndex); // Removal below the current layer decreases the current layer index if (mIndex < currentLayer) mMapDocument->setCurrentLayerIndex(currentLayer - 1); } } // namespace Internal } // namespace Tiled tiled-qt-0.9.1/src/tiled/addremovelayer.h000066400000000000000000000044031217502731700203160ustar00rootroot00000000000000/* * addremovelayer.h * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ADDREMOVELAYER_H #define ADDREMOVELAYER_H #include #include namespace Tiled { class Layer; namespace Internal { class MapDocument; /** * Abstract base class for AddLayer and RemoveLayer. */ class AddRemoveLayer : public QUndoCommand { public: AddRemoveLayer(MapDocument *mapDocument, int index, Layer *layer); ~AddRemoveLayer(); protected: void addLayer(); void removeLayer(); private: MapDocument *mMapDocument; Layer *mLayer; int mIndex; }; /** * Undo command that adds a layer to a map. */ class AddLayer : public AddRemoveLayer { public: /** * Creates an undo command that adds the \a layer at \a index. */ AddLayer(MapDocument *mapDocument, int index, Layer *layer) : AddRemoveLayer(mapDocument, index, layer) { setText(QCoreApplication::translate("Undo Commands", "Add Layer")); } void undo() { removeLayer(); } void redo() { addLayer(); } }; /** * Undo command that removes a layer from a map. */ class RemoveLayer : public AddRemoveLayer { public: /** * Creates an undo command that removes the layer at \a index. */ RemoveLayer(MapDocument *mapDocument, int index) : AddRemoveLayer(mapDocument, index, 0) { setText(QCoreApplication::translate("Undo Commands", "Remove Layer")); } void undo() { addLayer(); } void redo() { removeLayer(); } }; } // namespace Internal } // namespace Tiled #endif // ADDREMOVELAYER_H tiled-qt-0.9.1/src/tiled/addremovemapobject.cpp000066400000000000000000000054171217502731700215070ustar00rootroot00000000000000/* * addremovemapobject.cpp * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "addremovemapobject.h" #include "mapdocument.h" #include "mapobject.h" #include "objectgroup.h" #include "mapobjectmodel.h" #include using namespace Tiled; using namespace Tiled::Internal; AddRemoveMapObject::AddRemoveMapObject(MapDocument *mapDocument, ObjectGroup *objectGroup, MapObject *mapObject, bool ownObject, QUndoCommand *parent) : QUndoCommand(parent) , mMapDocument(mapDocument) , mMapObject(mapObject) , mObjectGroup(objectGroup) , mIndex(-1) , mOwnsObject(ownObject) { } AddRemoveMapObject::~AddRemoveMapObject() { if (mOwnsObject) delete mMapObject; } void AddRemoveMapObject::addObject() { mMapDocument->mapObjectModel()->insertObject(mObjectGroup, mIndex, mMapObject); mOwnsObject = false; } void AddRemoveMapObject::removeObject() { mIndex = mMapDocument->mapObjectModel()->removeObject(mObjectGroup, mMapObject); mOwnsObject = true; } AddMapObject::AddMapObject(MapDocument *mapDocument, ObjectGroup *objectGroup, MapObject *mapObject, QUndoCommand *parent) : AddRemoveMapObject(mapDocument, objectGroup, mapObject, true, parent) { setText(QCoreApplication::translate("Undo Commands", "Add Object")); } RemoveMapObject::RemoveMapObject(MapDocument *mapDocument, MapObject *mapObject, QUndoCommand *parent) : AddRemoveMapObject(mapDocument, mapObject->objectGroup(), mapObject, false, parent) { setText(QCoreApplication::translate("Undo Commands", "Remove Object")); } tiled-qt-0.9.1/src/tiled/addremovemapobject.h000066400000000000000000000043351217502731700211520ustar00rootroot00000000000000/* * addremovemapobject.h * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ADDREMOVEMAPOBJECT_H #define ADDREMOVEMAPOBJECT_H #include namespace Tiled { class MapObject; class ObjectGroup; namespace Internal { class MapDocument; /** * Abstract base class for AddMapObject and RemoveMapObject. */ class AddRemoveMapObject : public QUndoCommand { public: AddRemoveMapObject(MapDocument *mapDocument, ObjectGroup *objectGroup, MapObject *mapObject, bool ownObject, QUndoCommand *parent = 0); ~AddRemoveMapObject(); protected: void addObject(); void removeObject(); private: MapDocument *mMapDocument; MapObject *mMapObject; ObjectGroup *mObjectGroup; int mIndex; bool mOwnsObject; }; /** * Undo command that adds an object to a map. */ class AddMapObject : public AddRemoveMapObject { public: AddMapObject(MapDocument *mapDocument, ObjectGroup *objectGroup, MapObject *mapObject, QUndoCommand *parent = 0); void undo() { removeObject(); } void redo() { addObject(); } }; /** * Undo command that removes an object from a map. */ class RemoveMapObject : public AddRemoveMapObject { public: RemoveMapObject(MapDocument *mapDocument, MapObject *mapObject, QUndoCommand *parent = 0); void undo() { addObject(); } void redo() { removeObject(); } }; } // namespace Internal } // namespace Tiled #endif // ADDREMOVEMAPOBJECT_H tiled-qt-0.9.1/src/tiled/addremoveterrain.cpp000066400000000000000000000043661217502731700212110ustar00rootroot00000000000000/* * addremoveterrain.cpp * Copyright 2012, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "addremoveterrain.h" #include "mapdocument.h" #include "terrain.h" #include "terrainmodel.h" #include "tileset.h" #include using namespace Tiled; using namespace Tiled::Internal; AddRemoveTerrain::AddRemoveTerrain(MapDocument *mapDocument, Tileset *tileset, int index, Terrain *terrain) : mMapDocument(mapDocument) , mTileset(tileset) , mIndex(index) , mTerrain(terrain) { } AddRemoveTerrain::~AddRemoveTerrain() { delete mTerrain; } void AddRemoveTerrain::removeTerrain() { Q_ASSERT(!mTerrain); mTerrain = mMapDocument->terrainModel()->takeTerrainAt(mTileset, mIndex); } void AddRemoveTerrain::addTerrain() { Q_ASSERT(mTerrain); mMapDocument->terrainModel()->insertTerrain(mTileset, mIndex, mTerrain); mTerrain = 0; } AddTerrain::AddTerrain(MapDocument *mapDocument, Terrain *terrain) : AddRemoveTerrain(mapDocument, terrain->tileset(), terrain->tileset()->terrainCount(), terrain) { setText(QCoreApplication::translate("Undo Commands", "Add Terrain")); } RemoveTerrain::RemoveTerrain(MapDocument *mapDocument, Terrain *terrain) : AddRemoveTerrain(mapDocument, terrain->tileset(), terrain->id(), 0) { setText(QCoreApplication::translate("Undo Commands", "Remove Terrain")); } tiled-qt-0.9.1/src/tiled/addremoveterrain.h000066400000000000000000000036451217502731700206550ustar00rootroot00000000000000/* * addremoveterrain.h * Copyright 2012, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ADDREMOVETERRAIN_H #define ADDREMOVETERRAIN_H #include namespace Tiled { class Terrain; class Tileset; namespace Internal { class MapDocument; /** * Abstract base class for AddTerrain and RemoveTerrain. */ class AddRemoveTerrain : public QUndoCommand { public: AddRemoveTerrain(MapDocument *mapDocument, Tileset *tileset, int index, Terrain *terrain); ~AddRemoveTerrain(); protected: void addTerrain(); void removeTerrain(); private: MapDocument *mMapDocument; Tileset *mTileset; int mIndex; Terrain *mTerrain; }; /** * Adds a terrain to a map. */ class AddTerrain : public AddRemoveTerrain { public: AddTerrain(MapDocument *mapDocument, Terrain *terrain); void undo() { removeTerrain(); } void redo() { addTerrain(); } }; /** * Removes a terrain from a map. */ class RemoveTerrain : public AddRemoveTerrain { public: RemoveTerrain(MapDocument *mapDocument, Terrain *terrain); void undo() { addTerrain(); } void redo() { removeTerrain(); } }; } // namespace Internal } // namespace Tiled #endif // ADDREMOVETERRAIN_H tiled-qt-0.9.1/src/tiled/addremovetileset.cpp000066400000000000000000000034771217502731700212200ustar00rootroot00000000000000/* * addremovetileset.cpp * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "addremovetileset.h" #include "map.h" #include "mapdocument.h" #include "tilesetmanager.h" using namespace Tiled; using namespace Tiled::Internal; AddRemoveTileset::AddRemoveTileset(MapDocument *mapDocument, int index, Tileset *tileset) : mMapDocument(mapDocument) , mTileset(tileset) , mIndex(index) { // Make sure the tileset manager keeps this tileset around TilesetManager::instance()->addReference(mTileset); } AddRemoveTileset::~AddRemoveTileset() { TilesetManager::instance()->removeReference(mTileset); } void AddRemoveTileset::removeTileset() { mMapDocument->removeTilesetAt(mIndex); } void AddRemoveTileset::addTileset() { mMapDocument->insertTileset(mIndex, mTileset); } AddTileset::AddTileset(MapDocument *mapDocument, Tileset *tileset) : AddRemoveTileset(mapDocument, mapDocument->map()->tilesets().size(), tileset) { setText(QCoreApplication::translate("Undo Commands", "Add Tileset")); } tiled-qt-0.9.1/src/tiled/addremovetileset.h000066400000000000000000000040461217502731700206560ustar00rootroot00000000000000/* * addremovetileset.h * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef ADDREMOVETILESET_H #define ADDREMOVETILESET_H #include #include namespace Tiled { class Tileset; namespace Internal { class MapDocument; /** * Abstract base class for AddTileset and RemoveTileset. */ class AddRemoveTileset : public QUndoCommand { public: AddRemoveTileset(MapDocument *mapDocument, int index, Tileset *tileset); ~AddRemoveTileset(); protected: void addTileset(); void removeTileset(); private: MapDocument *mMapDocument; Tileset *mTileset; int mIndex; }; /** * Adds a tileset to a map. */ class AddTileset : public AddRemoveTileset { public: AddTileset(MapDocument *mapDocument, Tileset *tileset); void undo() { removeTileset(); } void redo() { addTileset(); } }; /** * Removes a tileset from a map. */ class RemoveTileset : public AddRemoveTileset { public: RemoveTileset(MapDocument *mapDocument, int index, Tileset *tileset) : AddRemoveTileset(mapDocument, index, tileset) { setText(QCoreApplication::translate("Undo Commands", "Remove Tileset")); } void undo() { addTileset(); } void redo() { removeTileset(); } }; } // namespace Internal } // namespace Tiled #endif // ADDREMOVETILESET_H tiled-qt-0.9.1/src/tiled/automapper.cpp000066400000000000000000001016311217502731700200240ustar00rootroot00000000000000/* * automapper.cpp * Copyright 2010-2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "automapper.h" #include "addremovelayer.h" #include "addremovemapobject.h" #include "addremovetileset.h" #include "automappingutils.h" #include "changeproperties.h" #include "geometry.h" #include "layermodel.h" #include "map.h" #include "mapdocument.h" #include "mapobject.h" #include "object.h" #include "objectgroup.h" #include "tile.h" #include "tilelayer.h" #include "tileset.h" #include "tilesetmanager.h" #include using namespace Tiled; using namespace Tiled::Internal; /* * About the order of the methods in this file. * The Automapper class has 3 bigger public functions, that is * prepareLoad(), prepareAutoMap() and autoMap(). * These three functions make use of lots of different private methods, which * are put directly below each of these functions. */ AutoMapper::AutoMapper(MapDocument *workingDocument, Map *rules, const QString &rulePath) : mMapDocument(workingDocument) , mMapWork(workingDocument ? workingDocument->map() : 0) , mMapRules(rules) , mLayerInputRegions(0) , mLayerOutputRegions(0) , mRulePath(rulePath) , mDeleteTiles(false) , mAutoMappingRadius(0) , mNoOverlappingRules(false) { Q_ASSERT(mMapRules); if (!setupRuleMapProperties()) return; if (!setupRuleMapTileLayers()) return; if (!setupRuleList()) return; } AutoMapper::~AutoMapper() { cleanUpRulesMap(); } QSet AutoMapper::getTouchedTileLayers() const { return mTouchedTileLayers; } bool AutoMapper::ruleLayerNameUsed(QString ruleLayerName) const { return mInputRules.names.contains(ruleLayerName); } bool AutoMapper::setupRuleMapProperties() { Properties properties = mMapRules->properties(); foreach (QString key, properties.keys()) { QVariant value = properties.value(key); bool raiseWarning = true; if (key.toLower() == QLatin1String("deletetiles")) { if (value.canConvert(QVariant::Bool)) { mDeleteTiles = value.toBool(); raiseWarning = false; } } else if (key.toLower() == QLatin1String("automappingradius")) { if (value.canConvert(QVariant::Int)) { mAutoMappingRadius = value.toInt(); raiseWarning = false; } } else if (key.toLower() == QLatin1String("nooverlappingrules")) { if (value.canConvert(QVariant::Bool)) { mNoOverlappingRules = value.toBool(); raiseWarning = false; } } if (raiseWarning) mWarning += tr("'%1': Property '%2' = '%3' does not make sense. " "Ignoring this property.") .arg(mRulePath, key, value.toString()) + QLatin1Char('\n'); } return true; } bool AutoMapper::setupRuleMapTileLayers() { Q_ASSERT(mLayerList.isEmpty()); Q_ASSERT(mAddedTilesets.isEmpty()); Q_ASSERT(!mLayerInputRegions); Q_ASSERT(!mLayerOutputRegions); Q_ASSERT(mInputRules.isEmpty()); Q_ASSERT(mInputRules.names.isEmpty()); Q_ASSERT(mInputRules.indexes.isEmpty()); QString error; foreach (Layer *layer, mMapRules->layers()) { const QString layerName = layer->name(); if (layerName.startsWith(QLatin1String("regions"), Qt::CaseInsensitive)) { bool treatAsBoth = layerName.toLower() == QLatin1String("regions"); if (layerName.endsWith(QLatin1String("input"), Qt::CaseInsensitive) || treatAsBoth) { if (mLayerInputRegions) { error += tr("'regions_input' layer must not occur more than once.") + QLatin1Char('\n'); } if (layer->isTileLayer()) { mLayerInputRegions = layer->asTileLayer(); } else { error += tr("'regions_*' layers must be tile layers.") + QLatin1Char('\n'); } } if (layerName.endsWith(QLatin1String("output"), Qt::CaseInsensitive) || treatAsBoth) { if (mLayerOutputRegions) { error += tr("'regions_output' layer must not occur more than once.") + QLatin1Char('\n'); } if (layer->isTileLayer()) { mLayerOutputRegions = layer->asTileLayer(); } else { error += tr("'regions_*' layers must be tile layers.") + QLatin1Char('\n'); } } continue; } int nameStartPosition = layerName.indexOf( QLatin1Char('_')) + 1; // name is all characters behind the underscore (excluded) QString name = layerName.right(layerName.size() - nameStartPosition); // group is all before the underscore (included) QString index = layerName.left(nameStartPosition); if (index.startsWith(QLatin1String("output"), Qt::CaseInsensitive)) index.remove(0, 6); else if (index.startsWith(QLatin1String("inputnot"), Qt::CaseInsensitive)) index.remove(0, 8); else if (index.startsWith(QLatin1String("input"), Qt::CaseInsensitive)) index.remove(0, 5); // both 'rule' and 'output' layers will require and underscore and // rely on the correct position detected of the underscore if (nameStartPosition == 0) { error += tr("Did you forget an underscore in layer '%1'?").arg( layerName) + QLatin1Char('\n'); continue; } if (layerName.startsWith(QLatin1String("input"), Qt::CaseInsensitive)) { bool isNotList = layerName.startsWith(QLatin1String("inputnot"), Qt::CaseInsensitive); if (!layer->isTileLayer()) { error += tr("'input_*' and 'inputnot_*' layers must be tile layers.") + QLatin1Char('\n'); continue; } mInputRules.names.insert(name); if (!mInputRules.indexes.contains(index)) { mInputRules.indexes.insert(index); mInputRules.insert(index, InputIndex()); } if (!mInputRules[index].names.contains(name)) { mInputRules[index].names.insert(name); mInputRules[index].insert(name, InputIndexName()); } if (isNotList) mInputRules[index][name].listNo.append(layer->asTileLayer()); else mInputRules[index][name].listYes.append(layer->asTileLayer()); continue; } if (layerName.startsWith(QLatin1String("output"), Qt::CaseInsensitive)) { if (layer->isTileLayer()) mTouchedTileLayers.insert(name); else mTouchedObjectGroups.insert(name); Layer::Type type = layer->type(); int layerIndex = mMapWork->indexOfLayer(name, type); bool found = false; foreach (RuleOutput *translationTable, mLayerList) { if (translationTable->index == index) { translationTable->insert(layer, layerIndex); found = true; break; } } if (!found) { mLayerList.append(new RuleOutput()); mLayerList.last()->insert(layer, layerIndex); mLayerList.last()->index = index; } continue; } error += tr("Layer '%1' is not recognized as a valid layer for Automapping.") .arg(layerName) + QLatin1Char('\n'); } if (!mLayerInputRegions) error += tr("No 'regions' or 'regions_input' layer found.") + QLatin1Char('\n'); if (!mLayerOutputRegions) error += tr("No 'regions' or 'regions_output' layer found.") + QLatin1Char('\n'); if (mInputRules.isEmpty()) error += tr("No input_ layer found!") + QLatin1Char('\n'); // no need to check for mInputNotRules.size() == 0 here. // these layers are not necessary. if (!error.isEmpty()) { error = mRulePath + QLatin1Char('\n') + error; mError += error; return false; } return true; } static bool compareRuleRegion(const QRegion &r1, const QRegion &r2) { const QPoint &p1 = r1.boundingRect().topLeft(); const QPoint &p2 = r2.boundingRect().topLeft(); return p1.y() < p2.y() || (p1.y() == p2.y() && p1.x() < p2.x()); } bool AutoMapper::setupRuleList() { Q_ASSERT(mRulesInput.isEmpty()); Q_ASSERT(mRulesOutput.isEmpty()); Q_ASSERT(mLayerInputRegions); Q_ASSERT(mLayerOutputRegions); QList combinedRegions = coherentRegions( mLayerInputRegions->region() + mLayerOutputRegions->region()); qSort(combinedRegions.begin(), combinedRegions.end(), compareRuleRegion); QList rulesInput = coherentRegions( mLayerInputRegions->region()); QList rulesOutput = coherentRegions( mLayerOutputRegions->region()); for (int i = 0; i < combinedRegions.size(); ++i) { mRulesInput.append(QRegion()); mRulesOutput.append(QRegion()); } foreach(QRegion reg, rulesInput) for (int i = 0; i < combinedRegions.size(); ++i) { if (reg.intersects(combinedRegions[i])) { mRulesInput[i] += reg; break; } } foreach(QRegion reg, rulesOutput) for (int i = 0; i < combinedRegions.size(); ++i) { if (reg.intersects(combinedRegions[i])) { mRulesOutput[i] += reg; break; } } Q_ASSERT(mRulesInput.size() == mRulesOutput.size()); for (int i = 0; i < mRulesInput.size(); ++i) { const QRegion checkCoherent = mRulesInput.at(i).united(mRulesOutput.at(i)); Q_ASSERT(coherentRegions(checkCoherent).length() == 1); } return true; } bool AutoMapper::prepareAutoMap() { mError.clear(); mWarning.clear(); if (!setupMissingLayers()) return false; if (!setupCorrectIndexes()) return false; if (!setupTilesets(mMapRules, mMapWork)) return false; return true; } bool AutoMapper::setupMissingLayers() { // make sure all needed layers are there: foreach (const QString &name, mTouchedTileLayers) { if (mMapWork->indexOfLayer(name, Layer::TileLayerType) != -1) continue; const int index = mMapWork->layerCount(); TileLayer *tilelayer = new TileLayer(name, 0, 0, mMapWork->width(), mMapWork->height()); mMapDocument->undoStack()->push( new AddLayer(mMapDocument, index, tilelayer)); mAddedTileLayers.append(name); } foreach (const QString &name, mTouchedObjectGroups) { if (mMapWork->indexOfLayer(name, Layer::ObjectGroupType) != -1) continue; const int index = mMapWork->layerCount(); ObjectGroup *objectGroup = new ObjectGroup(name, 0, 0, mMapWork->width(), mMapWork->height()); mMapDocument->undoStack()->push( new AddLayer(mMapDocument, index, objectGroup)); mAddedTileLayers.append(name); } return true; } bool AutoMapper::setupCorrectIndexes() { // make sure all indexes of the layer translationtables are correct. for (int i = 0; i < mLayerList.size(); ++i) { RuleOutput *translationTable = mLayerList.at(i); foreach (Layer *layerKey, translationTable->keys()) { QString name = layerKey->name(); const int pos = name.indexOf(QLatin1Char('_')) + 1; name = name.right(name.length() - pos); const int index = translationTable->value(layerKey, -1); if (index >= mMapWork->layerCount() || index == -1 || name != mMapWork->layerAt(index)->name()) { int newIndex = mMapWork->indexOfLayer(name, layerKey->type()); Q_ASSERT(newIndex != -1); translationTable->insert(layerKey, newIndex); } } } return true; } // This cannot just be replaced by MapDocument::unifyTileset(Map), // because here mAddedTileset is modified. bool AutoMapper::setupTilesets(Map *src, Map *dst) { QList existingTilesets = dst->tilesets(); // Add tilesets that are not yet part of dst map foreach (Tileset *tileset, src->tilesets()) { if (existingTilesets.contains(tileset)) continue; QUndoStack *undoStack = mMapDocument->undoStack(); Tileset *replacement = tileset->findSimilarTileset(existingTilesets); if (!replacement) { mAddedTilesets.append(tileset); undoStack->push(new AddTileset(mMapDocument, tileset)); continue; } // Merge the tile properties const int sharedTileCount = qMin(tileset->tileCount(), replacement->tileCount()); for (int i = 0; i < sharedTileCount; ++i) { Tile *replacementTile = replacement->tileAt(i); Properties properties = replacementTile->properties(); properties.merge(tileset->tileAt(i)->properties()); undoStack->push(new ChangeProperties(tr("Tile"), replacementTile, properties)); } src->replaceTileset(tileset, replacement); TilesetManager *tilesetManager = TilesetManager::instance(); tilesetManager->addReference(replacement); tilesetManager->removeReference(tileset); } return true; } void AutoMapper::autoMap(QRegion *where) { Q_ASSERT(mRulesInput.size() == mRulesOutput.size()); // first resize the active area if (mAutoMappingRadius) { QRegion region; foreach (const QRect &r, where->rects()) { region += r.adjusted(- mAutoMappingRadius, - mAutoMappingRadius, + mAutoMappingRadius, + mAutoMappingRadius); } *where += region; } // delete all the relevant area, if the property "DeleteTiles" is set if (mDeleteTiles) { const QRegion setLayersRegion = getSetLayersRegion(); for (int i = 0; i < mLayerList.size(); ++i) { RuleOutput *translationTable = mLayerList.at(i); foreach (Layer *layer, translationTable->keys()) { const int index = mLayerList.at(i)->value(layer); Layer *dstLayer = mMapWork->layerAt(index); const QRegion region = setLayersRegion.intersected(*where); TileLayer *dstTileLayer = dstLayer->asTileLayer(); if (dstTileLayer) dstTileLayer->erase(region); else eraseRegionObjectGroup(mMapDocument, dstLayer->asObjectGroup(), region); } } } // Increase the given region where the next automapper should work. // This needs to be done, so you can rely on the order of the rules at all // locations QRegion ret; foreach (const QRect &rect, where->rects()) for (int i = 0; i < mRulesInput.size(); ++i) { // at the moment the parallel execution does not work yet // TODO: make multithreading available! // either by dividing the rules or the region to multiple threads ret = ret.united(applyRule(i, rect)); } *where = where->united(ret); } const QRegion AutoMapper::getSetLayersRegion() { QRegion result; foreach (const QString &name, mInputRules.names) { const int index = mMapWork->indexOfLayer(name, Layer::TileLayerType); if (index == -1) continue; TileLayer *setLayer = mMapWork->layerAt(index)->asTileLayer(); result |= setLayer->region(); } return result; } static bool compareLayerTo(const TileLayer *setLayer, const QVector &listYes, const QVector &listNo, const QRegion &ruleRegion, const QPoint &offset); QRect AutoMapper::applyRule(const int ruleIndex, const QRect &where) { QRect ret; if (mLayerList.isEmpty()) return ret; const QRegion ruleInput = mRulesInput.at(ruleIndex); const QRegion ruleOutput = mRulesOutput.at(ruleIndex); QRect rbr = ruleInput.boundingRect(); // Since the rule itself is translated, we need to adjust the borders of the // loops. Decrease the size at all sides by one: There must be at least one // tile overlap to the rule. const int minX = where.left() - rbr.left() - rbr.width() + 1; const int minY = where.top() - rbr.top() - rbr.height() + 1; const int maxX = where.right() - rbr.left() + rbr.width() - 1; const int maxY = where.bottom() - rbr.top() + rbr.height() - 1; // In this list of regions it is stored which parts or the map have already // been altered by exactly this rule. We store all the altered parts to // make sure there are no overlaps of the same rule applied to // (neighbouring) places QList appliedRegions; if (mNoOverlappingRules) for (int i = 0; i < mMapWork->layerCount(); i++) appliedRegions.append(QRegion()); for (int y = minY; y <= maxY; ++y) for (int x = minX; x <= maxX; ++x) { bool anymatch = false; foreach (const QString &index, mInputRules.indexes) { const InputIndex &ii = mInputRules[index]; bool allLayerNamesMatch = true; foreach (const QString &name, ii.names) { const int i = mMapWork->indexOfLayer(name, Layer::TileLayerType); if (i == -1) { allLayerNamesMatch = false; } else { const TileLayer *setLayer = mMapWork->layerAt(i)->asTileLayer(); allLayerNamesMatch &= compareLayerTo(setLayer, ii[name].listYes, ii[name].listNo, ruleInput, QPoint(x, y)); } } if (allLayerNamesMatch) { anymatch = true; break; } } if (anymatch) { int r = 0; // choose by chance which group of rule_layers should be used: if (mLayerList.size() > 1) r = qrand() % mLayerList.size(); if (!mNoOverlappingRules) { copyMapRegion(ruleOutput, QPoint(x, y), mLayerList.at(r)); ret = ret.united(rbr.translated(QPoint(x, y))); continue; } bool missmatch = false; RuleOutput *translationTable = mLayerList.at(r); QList layers = translationTable->keys(); // check if there are no overlaps within this rule. QVector ruleRegionInLayer; for (int i = 0; i < layers.size(); ++i) { Layer *layer = layers.at(i); QRegion appliedPlace; TileLayer *tileLayer = layer->asTileLayer(); if (tileLayer) appliedPlace = tileLayer->region(); else appliedPlace = tileRegionOfObjectGroup(layer->asObjectGroup()); ruleRegionInLayer.append(appliedPlace.intersected(ruleOutput)); if (appliedRegions.at(i).intersects( ruleRegionInLayer[i].translated(x, y))) { missmatch = true; break; } } if (missmatch) continue; copyMapRegion(ruleOutput, QPoint(x, y), mLayerList.at(r)); ret = ret.united(rbr.translated(QPoint(x, y))); for (int i = 0; i < translationTable->size(); ++i) { appliedRegions[i] += ruleRegionInLayer[i].translated(x, y); } } } return ret; } /** * Returns a list of all cells which can be found within all tile layers * within the given region. */ static QVector cellsInRegion(const QVector &list, const QRegion &r) { QVector cells; foreach (const TileLayer *tilelayer, list) { foreach (const QRect &rect, r.rects()) { for (int x = rect.left(); x <= rect.right(); ++x) { for (int y = rect.top(); y <= rect.bottom(); ++y) { const Cell &cell = tilelayer->cellAt(x, y); if (!cells.contains(cell)) cells.append(cell); } } } } return cells; } /** * This function is one of the core functions for understanding the * automapping. * In this function a certain region (of the set layer) is compared to * several other layers (ruleSet and ruleNotSet). * This comparision will determine if a rule of automapping matches, * so if this rule is applied at this region given * by a QRegion and Offset given by a QPoint. * * This compares the tile layer setLayer to several others given * in the QList listYes (ruleSet) and OList listNo (ruleNotSet). * The tile layer setLayer is examined at QRegion ruleRegion + offset * The tile layers within listYes and listNo are examined at QRegion ruleRegion. * * Basically all matches between setLayer and a layer of listYes are considered * good, while all matches between setLayer and listNo are considered bad and * lead to canceling the comparison, returning false. * * The comparison is done for each position within the QRegion ruleRegion. * If all positions of the region are considered "good" return true. * * Now there are several cases to distinguish: * - both listYes and listNo are empty: * This should not happen, because with that configuration, absolutely * no condition is given. * return false, assuming this is an errornous rule being applied * * - both listYes and listNo are not empty: * When comparing a tile at a certain position of tile layer setLayer * to all available tiles in listYes, there must be at least * one layer, in which there is a match of tiles of setLayer and * listYes to consider this position good. * In listNo there must not be a match to consider this position * good. * If there are no tiles within all available tiles within all layers * of one list, all tiles in setLayer are considered good, * while inspecting this list. * All available tiles are all tiles within the whole rule region in * all tile layers of the list. * * - either of both lists are not empty * When comparing a certain position of tile layer setLayer * to all Tiles at the corresponding position this can happen: * A tile of setLayer matches a tile of a layer in the list. Then this * is considered as good, if the layer is from the listYes. * Otherwise it is considered bad. * * Exception, when having only the listYes: * if at the examined position there are no tiles within all Layers * of the listYes, all tiles except all used tiles within * the layers of that list are considered good. * * This exception was added to have a better functionality * (need of less layers.) * It was not added to the case, when having only listNo layers to * avoid total symmetrie between those lists. * * If all positions are considered good, return true. * return false otherwise. * * @return bool, if the tile layer matches the given list of layers. */ static bool compareLayerTo(const TileLayer *setLayer, const QVector &listYes, const QVector &listNo, const QRegion &ruleRegion, const QPoint &offset) { if (listYes.isEmpty() && listNo.isEmpty()) return false; QVector cells; if (listYes.isEmpty()) cells = cellsInRegion(listNo, ruleRegion); if (listNo.isEmpty()) cells = cellsInRegion(listYes, ruleRegion); foreach (const QRect &rect, ruleRegion.rects()) { for (int x = rect.left(); x <= rect.right(); ++x) { for (int y = rect.top(); y <= rect.bottom(); ++y) { // this is only used in the case where only one list has layers // it is needed for the exception mentioned above bool ruleDefinedListYes = false; bool matchListYes = false; bool matchListNo = false; if (!setLayer->contains(x + offset.x(), y + offset.y())) return false; const Cell &c1 = setLayer->cellAt(x + offset.x(), y + offset.y()); // ruleDefined will be set when there is a tile in at least // one layer. if there is a tile in at least one layer, only // the given tiles in the different listYes layers are valid. // if there is given no tile at all in the listYes layers, // consider all tiles valid. foreach (const TileLayer *comparedTileLayer, listYes) { if (!comparedTileLayer->contains(x, y)) return false; const Cell &c2 = comparedTileLayer->cellAt(x, y); if (!c2.isEmpty()) ruleDefinedListYes = true; if (!c2.isEmpty() && c1 == c2) matchListYes = true; } foreach (const TileLayer *comparedTileLayer, listNo) { if (!comparedTileLayer->contains(x, y)) return false; const Cell &c2 = comparedTileLayer->cellAt(x, y); if (!c2.isEmpty() && c1 == c2) matchListNo = true; } // when there are only layers in the listNo // check only if these layers are unmatched // no need to check explicitly the exception in this case. if (listYes.isEmpty()) { if (matchListNo) return false; else continue; } // when there are only layers in the listYes // check if these layers are matched, or if the exception works if (listNo.isEmpty()) { if (matchListYes) continue; if (!ruleDefinedListYes && !cells.contains(c1)) continue; return false; } // there are layers in both lists: // no need to consider ruleDefinedListXXX if ((matchListYes || !ruleDefinedListYes) && !matchListNo) continue; else return false; } } } return true; } void AutoMapper::copyMapRegion(const QRegion ®ion, QPoint offset, const RuleOutput *layerTranslation) { for (int i = 0; i < layerTranslation->keys().size(); ++i) { Layer *from = layerTranslation->keys().at(i); Layer *to = mMapWork->layerAt(layerTranslation->value(from)); foreach (const QRect &rect, region.rects()) { TileLayer *fromTileLayer = from->asTileLayer(); ObjectGroup *fromObjectGroup = from->asObjectGroup(); if (fromTileLayer) { TileLayer *toTileLayer = to->asTileLayer(); Q_ASSERT(toTileLayer); //TODO check this before in prepareAutomap or such! copyTileRegion(fromTileLayer, rect.x(), rect.y(), rect.width(), rect.height(), toTileLayer, rect.x() + offset.x(), rect.y() + offset.y()); } else if (fromObjectGroup) { ObjectGroup *toObjectGroup = to->asObjectGroup(); copyObjectRegion(fromObjectGroup, rect.x(), rect.y(), rect.width(), rect.height(), toObjectGroup, rect.x() + offset.x(), rect.y() + offset.y()); } else { Q_ASSERT(false); } } } } void AutoMapper::copyTileRegion(TileLayer *srcLayer, int srcX, int srcY, int width, int height, TileLayer *dstLayer, int dstX, int dstY) { const int startX = qMax(dstX, 0); const int startY = qMax(dstY, 0); const int endX = qMin(dstX + width, dstLayer->width()); const int endY = qMin(dstY + height, dstLayer->height()); const int offsetX = srcX - dstX; const int offsetY = srcY - dstY; for (int x = startX; x < endX; ++x) { for (int y = startY; y < endY; ++y) { const Cell &cell = srcLayer->cellAt(x + offsetX, y + offsetY); if (!cell.isEmpty()) { // this is without graphics update, it's done afterwards for all dstLayer->setCell(x, y, cell); } } } } void AutoMapper::copyObjectRegion(ObjectGroup *srcLayer, int srcX, int srcY, int width, int height, ObjectGroup *dstLayer, int dstX, int dstY) { QUndoStack *undo = mMapDocument->undoStack(); const QRect rect = QRect(srcX, srcY, width, height); QList objects = objectsInRegion(srcLayer, rect); QList clones; foreach (MapObject *obj, objects) { MapObject *clone = obj->clone(); clones.append(clone); clone->setX(clone->x() + dstX - srcX); clone->setY(clone->y() + dstY - srcY); undo->push(new AddMapObject(mMapDocument, dstLayer, clone)); } } void AutoMapper::cleanAll() { cleanTilesets(); cleanTileLayers(); } void AutoMapper::cleanTilesets() { foreach (Tileset *tileset, mAddedTilesets) { if (mMapWork->isTilesetUsed(tileset)) continue; const int layerIndex = mMapWork->indexOfTileset(tileset); if (layerIndex == -1) continue; QUndoStack *undo = mMapDocument->undoStack(); undo->push(new RemoveTileset(mMapDocument, layerIndex, tileset)); } mAddedTilesets.clear(); } void AutoMapper::cleanTileLayers() { foreach (const QString &tilelayerName, mAddedTileLayers) { const int layerIndex = mMapWork->indexOfLayer(tilelayerName, Layer::TileLayerType); if (layerIndex == -1) continue; const Layer *layer = mMapWork->layerAt(layerIndex); if (!layer->isEmpty()) continue; QUndoStack *undo = mMapDocument->undoStack(); undo->push(new RemoveLayer(mMapDocument, layerIndex)); } mAddedTileLayers.clear(); } void AutoMapper::cleanUpRulesMap() { cleanTilesets(); // mMapRules can be empty, when in prepareLoad the very first stages fail. if (!mMapRules) return; TilesetManager *tilesetManager = TilesetManager::instance(); tilesetManager->removeReferences(mMapRules->tilesets()); delete mMapRules; mMapRules = 0; cleanUpRuleMapLayers(); mRulesInput.clear(); mRulesOutput.clear(); } void AutoMapper::cleanUpRuleMapLayers() { cleanTileLayers(); QList::const_iterator it; for (it = mLayerList.constBegin(); it != mLayerList.constEnd(); ++it) delete (*it); mLayerList.clear(); // do not delete mLayerRuleRegions, it is owned by the rulesmap mLayerInputRegions = 0; mLayerOutputRegions = 0; mInputRules.clear(); } tiled-qt-0.9.1/src/tiled/automapper.h000066400000000000000000000253041217502731700174730ustar00rootroot00000000000000/* * automapper.h * Copyright 2010-2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef AUTOMAPPER_H #define AUTOMAPPER_H #include #include #include #include #include #include namespace Tiled { class Layer; class Map; class MapObject; class ObjectGroup; class TileLayer; class Tileset; namespace Internal { class MapDocument; class InputIndexName { public: QVector listYes; QVector listNo; }; class InputIndex : public QMap { public: QSet names; }; class InputLayers : public QMap { public: QSet indexes; QSet names; // all names }; class RuleOutput : public QMap { public: QString index; }; /** * This class does all the work for the automapping feature. * basically it can do the following: * - check the rules map for rules and store them * - compare TileLayers (i. e. check if/where a certain rule must be applied) * - copy regions of Maps (multiple Layers, the layerlist is a * lookup-table for matching the Layers) */ class AutoMapper : public QObject { Q_OBJECT public: /** * Constructs an AutoMapper. * All data structures, which only rely on the rules map are setup * here. * * @param workingDocument: the map to work on. * @param rules: The rule map which should be used for automapping * @param rulePath: The filepath to the rule map. */ AutoMapper(MapDocument *workingDocument, Map *rules, const QString &rulePath); ~AutoMapper(); /** * Checks if the passed \a ruleLayerName is used in this instance * of Automapper. */ bool ruleLayerNameUsed(QString ruleLayerName) const; /** * Call prepareLoad first! Returns a set of strings describing the tile * layers, which could be touched considering the given layers of the * rule map. */ QSet getTouchedTileLayers() const; /** * This needs to be called directly before the autoMap call. * It sets up some data structures which change rapidly, so it is quite * painful to keep these datastructures up to date all time. (indices of * layers of the working map) */ bool prepareAutoMap(); /** * Here is done all the automapping. */ void autoMap(QRegion *where); /** * This cleans all datastructures, which are setup via prepareAutoMap, * so the auto mapper becomes ready for its next automatic mapping. */ void cleanAll(); /** * Contains all errors until operation was canceled. * The errorlist is cleared within prepareLoad and prepareAutoMap. */ QString errorString() const { return mError; } /** * Contains all warnings which occur at loading a rules map or while * automapping. * The errorlist is cleared within prepareLoad and prepareAutoMap. */ QString warningString() const { return mWarning; } private: /** * Reads the map properties of the rulesmap. * @return returns true when anything is ok, false when errors occured. */ bool setupRuleMapProperties(); void cleanUpRulesMap(); /** * Searches the rules layer for regions and stores these in \a rules. * @return returns true when anything is ok, false when errors occured. */ bool setupRuleList(); /** * Sets up the layers in the rules map, which are used for automapping. * The layers are detected and put in the internal data structures * @return returns true when anything is ok, false when errors occured. */ bool setupRuleMapTileLayers(); /** * Checks if all needed layers in the working map are there. * If not, add them in the correct order. */ bool setupMissingLayers(); /** * Checks if the layers setup as in setupRuleMapLayers are still right. * If it's not right, correct them. * @return returns true if everything went fine. false is returned when * no set layer was found */ bool setupCorrectIndexes(); /** * sets up the tilesets which are used in automapping. * @return returns true when anything is ok, false when errors occured. * (in that case will be a msg box anyway) */ bool setupTilesets(Map *src, Map *dst); /** * Returns the conjunction of of all regions of all setlayers */ const QRegion getSetLayersRegion(); /** * This copies all Tiles from TileLayer src to TileLayer dst * * In src the Tiles are taken from the rectangle given by * src_x, src_y, width and height. * In dst they get copied to a rectangle given by * dst_x, dst_y, width, height . * if there is no tile in src TileLayer, there will nothing be copied, * so the maybe existing tile in dst will not be overwritten. * */ void copyTileRegion(TileLayer *src_lr, int src_x, int src_y, int width, int height, TileLayer *dst_lr, int dst_x, int dst_y); /** * This copies all objects from the \a src_lr ObjectGroup to the \a dst_lr * in the given rectangle. * * The rectangle is described by the upper left corner \a src_x \a src_y * and its \a width and \a height. The parameter \a dst_x and \a dst_y * offset the copied objects in the destination object group. */ void copyObjectRegion(ObjectGroup *src_lr, int src_x, int src_y, int width, int height, ObjectGroup *dst_lr, int dst_x, int dst_y); /** * This copies multiple TileLayers from one map to another. * Only the region \a region is considered for copying. * In the destination it will come to the region translated by Offset. * The parameter \a LayerTranslation is a map of which layers of the rulesmap * should get copied into which layers of the working map. */ void copyMapRegion(const QRegion ®ion, QPoint Offset, const RuleOutput *LayerTranslation); /** * This goes through all the positions of the mMapWork and checks if * there fits the rule given by the region in mMapRuleSet. * if there is a match all Layers are copied to mMapWork. * @param ruleIndex: the region which should be compared to all positions * of mMapWork will be looked up in mRulesInput and mRulesOutput * @return where: an rectangle where the rule actually got applied */ QRect applyRule(const int ruleIndex, const QRect &where); /** * Cleans up the data structes filled by setupRuleMapLayers(), * so the next rule can be processed. */ void cleanUpRuleMapLayers(); /** * Cleans up the data structes filled by setupTilesets(), * so the next rule can be processed. */ void cleanTilesets(); /** * Cleans up the added tile layers setup by setupMissingLayers(), * so we have a minimal addition of tile layers by the automapping. */ void cleanTileLayers(); /** * where to work in */ MapDocument *mMapDocument; /** * the same as mMapDocument->map() */ Map *mMapWork; /** * map containing the rules, usually different than mMapWork */ Map *mMapRules; /** * This contains all added tilesets as pointers. * if rules use Tilesets which are not in the mMapWork they are added. * keep track of them, because we need to delete them afterwards, * when they still are unused * they will be added while setupTilesets(). */ QVector mAddedTilesets; /** * description see: mAddedTilesets, just described by Strings */ QList mAddedTileLayers; /** * Points to the tilelayer, which defines the inputregions. */ TileLayer *mLayerInputRegions; /** * Points to the tilelayer, which defines the outputregions. */ TileLayer *mLayerOutputRegions; /** * Contains all tilelayer pointers, which names begin with input* * It is sorted by index and name */ InputLayers mInputRules; /** * List of Regions in mMapRules to know where the input rules are */ QList mRulesInput; /** * List of regions in mMapRules to know where the output of a * rule is. * mRulesOutput[i] is the output of that rule, * which has the input at mRulesInput[i], meaning that mRulesInput * and mRulesOutput must match with the indexes. */ QList mRulesOutput; /** * The inner set with layers to indexes is needed for translating * tile layers from mMapRules to mMapWork. * * The key is the pointer to the layer in the rulemap. The * pointer to the layer within the working map is not hardwired, but the * position in the layerlist, where it was found the last time. * This loosely bound pointer ensures we will get the right layer, since we * need to check before anyway, and it is still fast. * * The list is used to hold different translation tables * => one of the tables is chosen by chance, so randomness is available */ QList mLayerList; /** * store the name of the processed rules file, to have detailed * error messages available */ QString mRulePath; /** * determines if all tiles in all touched layers should be deleted first. */ bool mDeleteTiles; /** * This variable determines, how many overlapping tiles should be used. * The bigger the more area is remapped at an automapping operation. * This can lead to higher latency, but provides a better behavior on * interactive automapping. * It defaults to zero. */ int mAutoMappingRadius; /** * Determines if a rule is allowed to overlap itself. */ bool mNoOverlappingRules; QSet mTouchedTileLayers; QSet mTouchedObjectGroups; QString mError; QString mWarning; }; } // namespace Internal } // namespace Tiled #endif // AUTOMAPPER_H tiled-qt-0.9.1/src/tiled/automapperwrapper.cpp000066400000000000000000000103301217502731700214200ustar00rootroot00000000000000/* * automapperwrapper.cpp * Copyright 2010-2011, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "automapperwrapper.h" #include "map.h" #include "mapdocument.h" #include "tile.h" #include "tilelayer.h" using namespace Tiled; using namespace Tiled::Internal; AutoMapperWrapper::AutoMapperWrapper(MapDocument *mapDocument, QVector autoMapper, QRegion *where) { mMapDocument = mapDocument; Map *map = mMapDocument->map(); QSet touchedLayers; int index = 0; while (index < autoMapper.size()) { AutoMapper *a = autoMapper.at(index); if (a->prepareAutoMap()) { touchedLayers|= a->getTouchedTileLayers(); index++; } else { autoMapper.remove(index); } } foreach (const QString &layerName, touchedLayers) { const int layerindex = map->indexOfLayer(layerName); Q_ASSERT(layerindex != -1); mLayersBefore << static_cast(map->layerAt(layerindex)->clone()); } foreach (AutoMapper *a, autoMapper) { a->autoMap(where); } foreach (const QString &layerName, touchedLayers) { const int layerindex = map->indexOfLayer(layerName); // layerindex exists, because AutoMapper is still alive, dont check Q_ASSERT(layerindex != -1); mLayersAfter << static_cast(map->layerAt(layerindex)->clone()); } // reduce memory usage by saving only diffs Q_ASSERT(mLayersAfter.size() == mLayersBefore.size()); for (int i = 0; i < mLayersAfter.size(); ++i) { TileLayer *before = mLayersBefore.at(i); TileLayer *after = mLayersAfter.at(i); QRect diffRegion = before->computeDiffRegion(after).boundingRect(); TileLayer *before1 = before->copy(diffRegion); TileLayer *after1 = after->copy(diffRegion); before1->setPosition(diffRegion.topLeft()); after1->setPosition(diffRegion.topLeft()); before1->setName(before->name()); after1->setName(after->name()); mLayersBefore.replace(i, before1); mLayersAfter.replace(i, after1); delete before; delete after; } foreach (AutoMapper *a, autoMapper) { a->cleanAll(); } } AutoMapperWrapper::~AutoMapperWrapper() { QVector::iterator i; for (i = mLayersAfter.begin(); i != mLayersAfter.end(); ++i) delete *i; for (i = mLayersBefore.begin(); i != mLayersBefore.end(); ++i) delete *i; } void AutoMapperWrapper::undo() { Map *map = mMapDocument->map(); QVector::iterator i; for (i = mLayersBefore.begin(); i != mLayersBefore.end(); ++i) { const int layerindex = map->indexOfLayer((*i)->name()); if (layerindex != -1) patchLayer(layerindex, *i); } } void AutoMapperWrapper::redo() { Map *map = mMapDocument->map(); QVector::iterator i; for (i = mLayersAfter.begin(); i != mLayersAfter.end(); ++i) { const int layerindex = (map->indexOfLayer((*i)->name())); if (layerindex != -1) patchLayer(layerindex, *i); } } void AutoMapperWrapper::patchLayer(int layerIndex, TileLayer *layer) { Map *map = mMapDocument->map(); QRect b = layer->bounds(); Q_ASSERT(map->layerAt(layerIndex)->asTileLayer()); TileLayer *t = static_cast(map->layerAt(layerIndex)); t->setCells(b.left() - t->x(), b.top() - t->y(), layer, b.translated(-t->position())); mMapDocument->emitRegionChanged(b); } tiled-qt-0.9.1/src/tiled/automapperwrapper.h000066400000000000000000000033241217502731700210720ustar00rootroot00000000000000/* * automapperwrapper.h * Copyright 2010-2011, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef AUTOMAPPERWRAPPER_H #define AUTOMAPPERWRAPPER_H #include "automapper.h" #include #include namespace Tiled { namespace Internal { class MapDocument; /** * This is a wrapper class for the AutoMapper class. * Here in this class only undo/redo functionality all rulemaps * is provided. * This class will take a snapshot of the layers before and after the * automapping is done. In between instances of AutoMapper are doing the work. */ class AutoMapperWrapper : public QUndoCommand { public: AutoMapperWrapper(MapDocument *mapDocument, QVector autoMapper, QRegion *where); ~AutoMapperWrapper(); void undo(); void redo(); private: void patchLayer(int layerIndex, TileLayer *layer); MapDocument *mMapDocument; QVector mLayersAfter; QVector mLayersBefore; }; } // namespace Internal } // namespace Tiled #endif // AUTOMAPPERWRAPPER_H tiled-qt-0.9.1/src/tiled/automappingmanager.cpp000066400000000000000000000145671217502731700215410ustar00rootroot00000000000000/* * automappingmanager.cpp * Copyright 2010-2011, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "automappingmanager.h" #include "automapperwrapper.h" #include "map.h" #include "mapdocument.h" #include "tilelayer.h" #include "tilesetmanager.h" #include "tmxmapreader.h" #include "preferences.h" #include #include #include using namespace Tiled; using namespace Tiled::Internal; AutomappingManager *AutomappingManager::mInstance = 0; AutomappingManager::AutomappingManager(QObject *parent) : QObject(parent) , mMapDocument(0) , mLoaded(false) { } AutomappingManager::~AutomappingManager() { cleanUp(); } AutomappingManager *AutomappingManager::instance() { if (!mInstance) mInstance = new AutomappingManager(0); return mInstance; } void AutomappingManager::deleteInstance() { delete mInstance; mInstance = 0; } void AutomappingManager::autoMap() { if (!mMapDocument) return; Map *map = mMapDocument->map(); int w = map->width(); int h = map->height(); autoMapInternal(QRect(0, 0, w, h), 0); } void AutomappingManager::autoMap(QRegion where, Layer *touchedLayer) { if (Preferences::instance()->automappingDrawing()) autoMapInternal(where, touchedLayer); } void AutomappingManager::autoMapInternal(QRegion where, Layer *touchedLayer) { mError.clear(); mWarning.clear(); if (!mMapDocument) { mError = tr("No map document found!") + QLatin1Char('\n'); emit errorsOccurred(); return; } if (!mLoaded) { const QString mapPath = QFileInfo(mMapDocument->fileName()).path(); const QString rulesFileName = mapPath + QLatin1String("/rules.txt"); if (loadFile(rulesFileName)) { mLoaded = true; } else { emit errorsOccurred(); return; } } // use a pointer to the region, so each automapper can manipulate it and the // following automappers do see the impact QRegion *passedRegion = new QRegion(where); QVector passedAutoMappers; if (touchedLayer) { foreach (AutoMapper *a, mAutoMappers) { if (a->ruleLayerNameUsed(touchedLayer->name())) passedAutoMappers.append(a); } } else { passedAutoMappers = mAutoMappers; } if (!passedAutoMappers.isEmpty()) { QUndoStack *undoStack = mMapDocument->undoStack(); undoStack->beginMacro(tr("Apply AutoMap rules")); AutoMapperWrapper *aw = new AutoMapperWrapper(mMapDocument, passedAutoMappers, passedRegion); undoStack->push(aw); undoStack->endMacro(); } foreach (AutoMapper *automapper, mAutoMappers) { mWarning += automapper->warningString(); mError += automapper->errorString(); } mMapDocument->emitRegionChanged(*passedRegion); delete passedRegion; if (!mWarning.isEmpty()) emit warningsOccurred(); if (!mError.isEmpty()) emit errorsOccurred(); } bool AutomappingManager::loadFile(const QString &filePath) { bool ret = true; const QString absPath = QFileInfo(filePath).path(); QFile rulesFile(filePath); if (!rulesFile.exists()) { mError += tr("No rules file found at:\n%1").arg(filePath) + QLatin1Char('\n'); return false; } if (!rulesFile.open(QIODevice::ReadOnly)) { mError += tr("Error opening rules file:\n%1").arg(filePath) + QLatin1Char('\n'); return false; } QTextStream in(&rulesFile); QString line = in.readLine(); for (; !line.isNull(); line = in.readLine()) { QString rulePath = line.trimmed(); if (rulePath.isEmpty() || rulePath.startsWith(QLatin1Char('#')) || rulePath.startsWith(QLatin1String("//"))) continue; if (QFileInfo(rulePath).isRelative()) rulePath = absPath + QLatin1Char('/') + rulePath; if (!QFileInfo(rulePath).exists()) { mError += tr("File not found:\n%1").arg(rulePath) + QLatin1Char('\n'); ret = false; continue; } if (rulePath.endsWith(QLatin1String(".tmx"), Qt::CaseInsensitive)) { TmxMapReader mapReader; Map *rules = mapReader.read(rulePath); if (!rules) { mError += tr("Opening rules map failed:\n%1").arg( mapReader.errorString()) + QLatin1Char('\n'); ret = false; continue; } TilesetManager *tilesetManager = TilesetManager::instance(); tilesetManager->addReferences(rules->tilesets()); AutoMapper *autoMapper; autoMapper = new AutoMapper(mMapDocument, rules, rulePath); mWarning += autoMapper->warningString(); const QString error = autoMapper->errorString(); if (error.isEmpty()) { mAutoMappers.append(autoMapper); } else { mError += error; delete autoMapper; } } if (rulePath.endsWith(QLatin1String(".txt"), Qt::CaseInsensitive)) { if (!loadFile(rulePath)) ret = false; } } return ret; } void AutomappingManager::setMapDocument(MapDocument *mapDocument) { cleanUp(); if (mMapDocument) mMapDocument->disconnect(this); mMapDocument = mapDocument; if (mMapDocument) connect(mMapDocument, SIGNAL(regionEdited(QRegion,Layer*)), this, SLOT(autoMap(QRegion,Layer*))); mLoaded = false; } void AutomappingManager::cleanUp() { foreach (const AutoMapper *autoMapper, mAutoMappers) { delete autoMapper; } mAutoMappers.clear(); } tiled-qt-0.9.1/src/tiled/automappingmanager.h000066400000000000000000000076771217502731700212120ustar00rootroot00000000000000/* * automappingmanager.h * Copyright 2010-2011, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef AUTOMAPPINGMANAGER_H #define AUTOMAPPINGMANAGER_H #include #include #include #include #include class QFileSystemWatcher; class QObject; namespace Tiled { class Layer; class Map; class TileLayer; class Tileset; namespace Internal { class AutoMapper; class MapDocument; /** * This class is a superior class to the AutoMapper and AutoMapperWrapper class. * It uses these classes to do the whole automapping process. */ class AutomappingManager: public QObject { Q_OBJECT public: /** * Requests the AutomaticMapping manager. When the manager doesn't exist * yet, it will be created. */ static AutomappingManager *instance(); /** * Deletes the AutomaticMapping manager instance, when it exists. */ static void deleteInstance(); /** * This triggers an automapping on the whole current map document. */ void autoMap(); void setMapDocument(MapDocument *mapDocument); QString errorString() const { return mError; } QString warningString() const { return mWarning; } signals: /** * This signal is emited after automapping was done and an error occurred. */ void errorsOccurred(); /** * This signal is emited after automapping was done and a warning occurred. */ void warningsOccurred(); public slots: void autoMap(QRegion where, Layer *touchedLayer); private: Q_DISABLE_COPY(AutomappingManager) /** * Constructor. Only used by the AutomaticMapping manager itself. */ AutomappingManager(QObject *parent); ~AutomappingManager(); static AutomappingManager *mInstance; /** * This function parses a rules file. * For each path which is a rule, (fileextension is tmx) an AutoMapper * object is setup. * * If a fileextension is txt, this file will be opened and searched for * rules again. * * @return if the loading was successful: return true if it suceeded. */ bool loadFile(const QString &filePath); /** * Applies automapping to the Region \a where, considering only layer * \a touchedLayer has changed. * There will only those Automappers be used which have a rule layer * touching the \a touchedLayer * If layer is 0, all Automappers are used. */ void autoMapInternal(QRegion where, Layer *touchedLayer); /** * deletes all its data structures */ void cleanUp(); /** * The current map document. */ MapDocument *mMapDocument; /** * For each new file of rules a new AutoMapper is setup. In this vector we * can store all of the AutoMappers in order. */ QVector mAutoMappers; /** * This tells you if the rules for the current map document were already * loaded. */ bool mLoaded; /** * Contains all errors which occurred until canceling. * If mError is not empty, no serious result can be expected. */ QString mError; /** * Contains all strings, which try to explain unusual and unexpected * behavior. */ QString mWarning; }; } // namespace Internal } // namespace Tiled #endif // AUTOMAPPINGMANAGER_H tiled-qt-0.9.1/src/tiled/automappingutils.cpp000066400000000000000000000051161217502731700212550ustar00rootroot00000000000000/* * automappingutils.cpp * Copyright 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "automappingutils.h" #include "addremovemapobject.h" #include "mapdocument.h" #include "mapobject.h" #include "objectgroup.h" #include namespace Tiled { namespace Internal { void eraseRegionObjectGroup(MapDocument *mapDocument, ObjectGroup *layer, const QRegion &where) { QUndoStack *undo = mapDocument->undoStack(); foreach (MapObject *obj, layer->objects()) { // TODO: we are checking bounds, which is only correct for rectangles and // tile objects. polygons and polylines are not covered correctly by this // erase method (we are in fact deleting too many objects) // TODO2: toAlignedRect may even break rects. if (where.intersects(obj->bounds().toAlignedRect())) undo->push(new RemoveMapObject(mapDocument, obj)); } } QRegion tileRegionOfObjectGroup(ObjectGroup *layer) { QRegion ret; foreach (MapObject *obj, layer->objects()) { // TODO: we are using bounds, which is only correct for rectangles and // tile objects. polygons and polylines are not probably covering less // tiles. ret += obj->bounds().toAlignedRect(); } return ret; } const QList objectsInRegion(ObjectGroup *layer, const QRegion &where) { QList ret; foreach (MapObject *obj, layer->objects()) { // TODO: we are checking bounds, which is only correct for rectangles and // tile objects. polygons and polylines are not covered correctly by this // erase method (we are in fact deleting too many objects) // TODO2: toAlignedRect may even break rects. if (where.intersects(obj->bounds().toAlignedRect())) ret += obj; } return ret; } } } tiled-qt-0.9.1/src/tiled/automappingutils.h000066400000000000000000000025121217502731700207170ustar00rootroot00000000000000/* * automappingutils.h * Copyright 2012, Stefan Beller, stefanbeller@googlemail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef AUTOMAPPINGUTILS_H #define AUTOMAPPINGUTILS_H #include namespace Tiled { class MapObject; class ObjectGroup; namespace Internal { class MapDocument; const QList objectsInRegion(ObjectGroup *layer, const QRegion &where); void eraseRegionObjectGroup(MapDocument *mapDocument, ObjectGroup *layer, const QRegion &where); QRegion tileRegionOfObjectGroup(ObjectGroup *layer); } // namespace Internal } // namespace Tiled #endif // AUTOMAPPINGUTILS_H tiled-qt-0.9.1/src/tiled/brushitem.cpp000066400000000000000000000075671217502731700176660ustar00rootroot00000000000000/* * brushitem.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "brushitem.h" #include "map.h" #include "mapdocument.h" #include "maprenderer.h" #include "painttilelayer.h" #include "tile.h" #include "tilelayer.h" #include #include #include #include using namespace Tiled; using namespace Tiled::Internal; BrushItem::BrushItem(): mMapDocument(0), mTileLayer(0) { setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); } void BrushItem::setMapDocument(MapDocument *mapDocument) { if (mMapDocument == mapDocument) return; mMapDocument = mapDocument; // The tiles in the stamp may no longer be valid setTileLayer(0); updateBoundingRect(); } void BrushItem::setTileLayer(const TileLayer *tileLayer) { delete mTileLayer; if (tileLayer) { mTileLayer = static_cast(tileLayer->clone()); mRegion = mTileLayer->region(); } else { mTileLayer = 0; mRegion = QRegion(); } updateBoundingRect(); update(); } void BrushItem::setTileLayerPosition(const QPoint &pos) { if (!mTileLayer) return; const QPoint oldPosition(mTileLayer->x(), mTileLayer->y()); if (oldPosition == pos) return; mRegion.translate(pos - oldPosition); mTileLayer->setX(pos.x()); mTileLayer->setY(pos.y()); updateBoundingRect(); } void BrushItem::setTileRegion(const QRegion ®ion) { if (mRegion == region) return; mRegion = region; updateBoundingRect(); } QRectF BrushItem::boundingRect() const { return mBoundingRect; } void BrushItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { QColor highlight = QApplication::palette().highlight().color(); highlight.setAlpha(64); const MapRenderer *renderer = mMapDocument->renderer(); if (mTileLayer) { const qreal opacity = painter->opacity(); painter->setOpacity(0.75); renderer->drawTileLayer(painter, mTileLayer, option->exposedRect); painter->setOpacity(opacity); renderer->drawTileSelection(painter, mRegion, highlight, option->exposedRect); } else { renderer->drawTileSelection(painter, mRegion, highlight, option->exposedRect); } } void BrushItem::updateBoundingRect() { prepareGeometryChange(); if (!mMapDocument) { mBoundingRect = QRectF(); return; } const QRect bounds = mRegion.boundingRect(); mBoundingRect = mMapDocument->renderer()->boundingRect(bounds); // Adjust for amount of pixels tiles extend at the top and to the right if (mTileLayer) { const Map *map = mMapDocument->map(); QMargins drawMargins = mTileLayer->drawMargins(); drawMargins.setTop(drawMargins.top() - map->tileHeight()); drawMargins.setRight(drawMargins.right() - map->tileWidth()); mBoundingRect.adjust(-drawMargins.left(), -drawMargins.top(), drawMargins.right(), drawMargins.bottom()); } } tiled-qt-0.9.1/src/tiled/brushitem.h000066400000000000000000000050051217502731700173140ustar00rootroot00000000000000/* * brushitem.h * Copyright 2008-2010, Thorbjørn Lindeijer * Copyright 2010 Stefan Beller * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef BRUSHITEM_H #define BRUSHITEM_H #include namespace Tiled { class TileLayer; namespace Internal { class MapDocument; /** * This brush item is used to represent a brush in a map scene before it is * used. */ class BrushItem : public QGraphicsItem { public: /** * Constructor. */ BrushItem(); /** * Sets the map document this brush is operating on. */ void setMapDocument(MapDocument *mapDocument); /** * Sets a tile layer representing this brush. When no tile layer is set, * the brush only draws the selection color. * * The BrushItem does not take ownership over the tile layer, but makes a * personal copy of the tile layer. */ void setTileLayer(const TileLayer *tileLayer); /** * Returns the current tile layer. */ TileLayer *tileLayer() const { return mTileLayer; } /** * Changes the position of the tile layer, if one is set. */ void setTileLayerPosition(const QPoint &pos); /** * Sets the region of tiles that this brush item occupies. */ void setTileRegion(const QRegion ®ion); /** * Returns the region of the current tile layer or the region that was set * using setTileRegion. */ QRegion tileRegion() const { return mRegion; } // QGraphicsItem QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); private: void updateBoundingRect(); MapDocument *mMapDocument; TileLayer *mTileLayer; QRegion mRegion; QRectF mBoundingRect; }; } // namespace Internal } // namespace Tiled #endif // BRUSHITEM_H tiled-qt-0.9.1/src/tiled/bucketfilltool.cpp000066400000000000000000000231131217502731700206670ustar00rootroot00000000000000/* * bucketfilltool.cpp * Copyright 2009-2010, Jeff Bland * Copyright 2010, Thorbjørn Lindeijer * Copyright 2010, Jared Adams * Copyright 2011, Stefan Beller * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "bucketfilltool.h" #include "brushitem.h" #include "filltiles.h" #include "tilepainter.h" #include "tile.h" #include "mapscene.h" #include "mapdocument.h" #include using namespace Tiled; using namespace Tiled::Internal; BucketFillTool::BucketFillTool(QObject *parent) : AbstractTileTool(tr("Bucket Fill Tool"), QIcon(QLatin1String( ":images/22x22/stock-tool-bucket-fill.png")), QKeySequence(tr("F")), parent) , mStamp(0) , mFillOverlay(0) , mIsActive(false) , mLastShiftStatus(false) , mIsRandom(false) { } BucketFillTool::~BucketFillTool() { delete mStamp; delete mFillOverlay; } void BucketFillTool::activate(MapScene *scene) { AbstractTileTool::activate(scene); mIsActive = true; tilePositionChanged(tilePosition()); } void BucketFillTool::deactivate(MapScene *scene) { AbstractTileTool::deactivate(scene); mFillRegion = QRegion(); mIsActive = false; } void BucketFillTool::tilePositionChanged(const QPoint &tilePos) { bool shiftPressed = QApplication::keyboardModifiers() & Qt::ShiftModifier; bool fillRegionChanged = false; // Make sure that a tile layer is selected TileLayer *tileLayer = currentTileLayer(); if (!tileLayer) return; // Skip filling if the stamp is empty if (!mStamp || mStamp->isEmpty()) return; TilePainter regionComputer(mapDocument(), tileLayer); // If the stamp is a single tile, ignore it when making the region if (mStamp->width() == 1 && mStamp->height() == 1 && !shiftPressed && mStamp->cellAt(0, 0) == regionComputer.cellAt(tilePos.x(), tilePos.y())) return; // This clears the connections so we don't get callbacks clearConnections(mapDocument()); // Optimization: we don't need to recalculate the fill area // if the new mouse position is still over the filled region // and the shift modifier hasn't changed. if (!mFillRegion.contains(tilePos) || shiftPressed != mLastShiftStatus || mIsRandom) { // Clear overlay to make way for a new one clearOverlay(); // Cache information about how the fill region was created mLastShiftStatus = shiftPressed; // Get the new fill region if (!shiftPressed) { // If not holding shift, a region is generated from the current pos mFillRegion = regionComputer.computeFillRegion(tilePos); } else { // If holding shift, the region is the selection bounds mFillRegion = mapDocument()->tileSelection(); // Fill region is the whole map if there is no selection if (mFillRegion.isEmpty()) mFillRegion = tileLayer->bounds(); // The mouse needs to be in the region if (!mFillRegion.contains(tilePos)) mFillRegion = QRegion(); } fillRegionChanged = true; } // Ensure that a fill region was created before making an overlay layer if (mFillRegion.isEmpty()) return; if (mLastRandomStatus != mIsRandom) fillRegionChanged = true; if (!mFillOverlay) { // Create a new overlay region const QRect fillBounds = mFillRegion.boundingRect(); mFillOverlay = new TileLayer(QString(), fillBounds.x(), fillBounds.y(), fillBounds.width(), fillBounds.height()); } // Paint the new overlay TilePainter tilePainter(mapDocument(), mFillOverlay); if (!mIsRandom) { if (fillRegionChanged) tilePainter.drawStamp(mStamp, mFillRegion); } else { TileLayer *stamp = getRandomTileLayer(mFillRegion); tilePainter.drawStamp(stamp, mFillRegion); delete stamp; fillRegionChanged = true; } if (fillRegionChanged) { // Update the brush item to draw the overlay brushItem()->setTileLayer(mFillOverlay); mLastRandomStatus = mIsRandom; } // Create connections to know when the overlay should be cleared makeConnections(); } void BucketFillTool::mousePressed(QGraphicsSceneMouseEvent *event) { if (event->button() != Qt::LeftButton || mFillRegion.isEmpty()) return; if (!brushItem()->isVisible()) return; FillTiles *fillTiles = new FillTiles(mapDocument(), currentTileLayer(), mFillRegion, brushItem()->tileLayer()); QRegion fillRegion(mFillRegion); mapDocument()->undoStack()->push(fillTiles); mapDocument()->emitRegionEdited(fillRegion, currentTileLayer()); } void BucketFillTool::mouseReleased(QGraphicsSceneMouseEvent *) { } void BucketFillTool::modifiersChanged(Qt::KeyboardModifiers) { // Don't need to recalculate fill region if there was no fill region if (!mFillOverlay) return; tilePositionChanged(tilePosition()); } void BucketFillTool::languageChanged() { setName(tr("Bucket Fill Tool")); setShortcut(QKeySequence(tr("F"))); } void BucketFillTool::mapDocumentChanged(MapDocument *oldDocument, MapDocument *newDocument) { AbstractTileTool::mapDocumentChanged(oldDocument, newDocument); clearConnections(oldDocument); // Reset things that are probably invalid now setStamp(0); clearOverlay(); } void BucketFillTool::setStamp(TileLayer *stamp) { // Clear any overlay that we presently have with an old stamp clearOverlay(); delete mStamp; mStamp = stamp; if (mIsRandom) updateRandomList(); if (mIsActive && brushItem()->isVisible()) tilePositionChanged(tilePosition()); } void BucketFillTool::clearOverlay() { // Clear connections before clearing overlay so there is no // risk of getting a callback and causing an infinite loop clearConnections(mapDocument()); brushItem()->setTileLayer(0); delete mFillOverlay; mFillOverlay = 0; mFillRegion = QRegion(); brushItem()->setTileRegion(QRegion()); } void BucketFillTool::makeConnections() { if (!mapDocument()) return; // Overlay may need to be cleared if a region changed connect(mapDocument(), SIGNAL(regionChanged(QRegion)), this, SLOT(clearOverlay())); // Overlay needs to be cleared if we switch to another layer connect(mapDocument(), SIGNAL(currentLayerIndexChanged(int)), this, SLOT(clearOverlay())); // Overlay needs be cleared if the selection changes, since // the overlay may be bound or may need to be bound to the selection connect(mapDocument(), SIGNAL(tileSelectionChanged(QRegion,QRegion)), this, SLOT(clearOverlay())); } void BucketFillTool::clearConnections(MapDocument *mapDocument) { if (!mapDocument) return; disconnect(mapDocument, SIGNAL(regionChanged(QRegion)), this, SLOT(clearOverlay())); disconnect(mapDocument, SIGNAL(currentLayerIndexChanged(int)), this, SLOT(clearOverlay())); disconnect(mapDocument, SIGNAL(tileSelectionChanged(QRegion,QRegion)), this, SLOT(clearOverlay())); } void BucketFillTool::setRandom(bool value) { if (mIsRandom == value) return; mIsRandom = value; if (mIsRandom) updateRandomList(); else mRandomList.clear(); // Don't need to recalculate fill region if there was no fill region if (!mFillOverlay) return; tilePositionChanged(tilePosition()); } TileLayer *BucketFillTool::getRandomTileLayer(const QRegion ®ion) const { QRect bb = region.boundingRect(); TileLayer *result = new TileLayer(QString(), bb.x(), bb.y(), bb.width(), bb.height()); if (region.isEmpty() || mRandomList.empty()) return result; foreach (const QRect &rect, region.rects()) { for (int _x = rect.left(); _x <= rect.right(); ++_x) { for (int _y = rect.top(); _y <= rect.bottom(); ++_y) { result->setCell(_x - bb.x(), _y - bb.y(), mRandomList.at(rand() % mRandomList.size())); } } } return result; } void BucketFillTool::updateRandomList() { mRandomList.clear(); if (!mStamp) return; for (int x = 0; x < mStamp->width(); x++) for (int y = 0; y < mStamp->height(); y++) if (!mStamp->cellAt(x, y).isEmpty()) mRandomList.append(mStamp->cellAt(x, y)); } tiled-qt-0.9.1/src/tiled/bucketfilltool.h000066400000000000000000000065301217502731700203400ustar00rootroot00000000000000/* * bucketfilltool.h * Copyright 2009-2010, Jeff Bland * Copyright 2010, Thorbjørn Lindeijer * Copyright 2011, Stefan Beller * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef BUCKETFILLTOOL_H #define BUCKETFILLTOOL_H #include "abstracttiletool.h" #include "tilelayer.h" namespace Tiled { namespace Internal { class MapDocument; /** * Implements a tool that bucket fills (flood fills) a region with a repeatable * stamp. */ class BucketFillTool : public AbstractTileTool { Q_OBJECT public: BucketFillTool(QObject *parent = 0); ~BucketFillTool(); void activate(MapScene *scene); void deactivate(MapScene *scene); void mousePressed(QGraphicsSceneMouseEvent *event); void mouseReleased(QGraphicsSceneMouseEvent *event); void modifiersChanged(Qt::KeyboardModifiers); void languageChanged(); /** * Sets the stamp that is drawn when filling. The BucketFillTool takes * ownership over the stamp layer. */ void setStamp(TileLayer *stamp); /** * This returns the actual tile layer which is used to define the current * state. */ TileLayer *stamp() const { return mStamp; } public slots: void setRandom(bool value); protected: void tilePositionChanged(const QPoint &tilePos); void mapDocumentChanged(MapDocument *oldDocument, MapDocument *newDocument); private slots: void clearOverlay(); private: void makeConnections(); void clearConnections(MapDocument *mapDocument); TileLayer *mStamp; TileLayer *mFillOverlay; QRegion mFillRegion; bool mIsActive; bool mLastShiftStatus; /** * Indicates if the tool is using the random mode. */ bool mIsRandom; /** * Contains the value of mIsRandom at that time, when the latest call of * tilePositionChanged() took place. * This variable is needed to detect if the random mode was changed during * mFillOverlay being brushed at an area. */ bool mLastRandomStatus; /** * Contains all used random cells to use in random mode. * The same cell can be in the list multiple times to make different * random weights possible. */ QList mRandomList; /** * Updates the list of random cells. * This is done by taking all non-null tiles from the original stamp mStamp. */ void updateRandomList(); /** * Returns a tile layer having random tiles placed at \a region.The * caller is responsible for the returned tile layer. */ TileLayer *getRandomTileLayer(const QRegion ®ion) const; }; } // namespace Internal } // namespace Tiled #endif // BUCKETFILLTOOL_H tiled-qt-0.9.1/src/tiled/changeimagelayerproperties.cpp000066400000000000000000000041651217502731700232550ustar00rootroot00000000000000/* * changeimagelayerproperties.cpp * Copyright 2010, Jeff Bland * Copyright 2010, Thorbjørn Lindeijer * Copyright 2011, Gregory Nickonov * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changeimagelayerproperties.h" #include "mapdocument.h" #include "imagelayer.h" #include using namespace Tiled; using namespace Tiled::Internal; ChangeImageLayerProperties::ChangeImageLayerProperties( MapDocument *mapDocument, ImageLayer *imageLayer, const QColor &color, const QString &path) : QUndoCommand( QCoreApplication::translate( "Undo Commands", "Change Image Layer Properties")) , mMapDocument(mapDocument) , mImageLayer(imageLayer) , mUndoColor(imageLayer->transparentColor()) , mRedoColor(color) , mUndoPath(imageLayer->imageSource()) , mRedoPath(path) { } void ChangeImageLayerProperties::redo() { mImageLayer->setTransparentColor(mRedoColor); if (mRedoPath.isEmpty()) mImageLayer->resetImage(); else mImageLayer->loadFromImage(QImage(mRedoPath), mRedoPath); mMapDocument->emitImageLayerChanged(mImageLayer); } void ChangeImageLayerProperties::undo() { mImageLayer->setTransparentColor(mUndoColor); if (mUndoPath.isEmpty()) mImageLayer->resetImage(); else mImageLayer->loadFromImage(QImage(mUndoPath), mUndoPath); mMapDocument->emitImageLayerChanged(mImageLayer); } tiled-qt-0.9.1/src/tiled/changeimagelayerproperties.h000066400000000000000000000037671217502731700227310ustar00rootroot00000000000000/* * changeimagelayerproperties.h * Copyright 2010, Jeff Bland * Copyright 2010, Thorbjørn Lindeijer * Copyright 2011, Gregory Nickonov * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGEIMAGELAYERPROPERTIES_H #define CHANGEIMAGELAYERPROPERTIES_H #include #include #include namespace Tiled { class ImageLayer; namespace Internal { class MapDocument; class ChangeImageLayerProperties : public QUndoCommand { public: /** * Constructs a new 'Change Image Layer Properties' command. * * @param mapDocument the map document of the layer's map * @param imageLayer the image layer to modify * @param newColor the new transparent color to apply * @param newPath the new image source to apply */ ChangeImageLayerProperties(MapDocument *mapDocument, ImageLayer *imageLayer, const QColor &newColor, const QString &newPath); void undo(); void redo(); private: MapDocument *mMapDocument; ImageLayer *mImageLayer; const QColor mUndoColor; const QColor mRedoColor; const QString mUndoPath; const QString mRedoPath; }; } // namespace Internal } // namespace Tiled #endif // CHANGEIMAGELAYERPROPERTIES_H tiled-qt-0.9.1/src/tiled/changemapobject.cpp000066400000000000000000000034301217502731700207570ustar00rootroot00000000000000/* * changemapobject.cpp * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changemapobject.h" #include "mapdocument.h" #include "mapobject.h" #include "mapobjectmodel.h" #include using namespace Tiled; using namespace Tiled::Internal; ChangeMapObject::ChangeMapObject(MapDocument *mapDocument, MapObject *mapObject, const QString &name, const QString &type) : QUndoCommand(QCoreApplication::translate("Undo Commands", "Change Object")) , mMapDocument(mapDocument) , mMapObject(mapObject) , mName(name) , mType(type) { } void ChangeMapObject::undo() { swap(); } void ChangeMapObject::redo() { swap(); } void ChangeMapObject::swap() { const QString name = mMapObject->name(); const QString type = mMapObject->type(); mMapDocument->mapObjectModel()->setObjectName(mMapObject, mName); mMapDocument->mapObjectModel()->setObjectType(mMapObject, mType); mName = name; mType = type; } tiled-qt-0.9.1/src/tiled/changemapobject.h000066400000000000000000000027331217502731700204310ustar00rootroot00000000000000/* * changemapobject.h * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGEMAPOBJECT_H #define CHANGEMAPOBJECT_H #include namespace Tiled { class MapObject; namespace Internal { class MapDocument; class ChangeMapObject : public QUndoCommand { public: /** * Creates an undo command that sets the given \a object's \a name and * \a type. */ ChangeMapObject(MapDocument *mapDocument, MapObject *object, const QString &name, const QString &type); void undo(); void redo(); private: void swap(); MapDocument *mMapDocument; MapObject *mMapObject; QString mName; QString mType; }; } // namespace Internal } // namespace Tiled #endif // CHANGEMAPOBJECT_H tiled-qt-0.9.1/src/tiled/changemapproperties.cpp000066400000000000000000000036451217502731700217150ustar00rootroot00000000000000/* * changemapproperties.cpp * Copyright 2012, Emmanuel Barroga emmanuelbarroga@gmail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changemapproperties.h" #include "map.h" #include "mapdocument.h" #include "objectgroup.h" #include using namespace Tiled; using namespace Tiled::Internal; ChangeMapProperties::ChangeMapProperties(MapDocument *mapDocument, const QColor &backgroundColor, Map::LayerDataFormat layerDataFormat) : QUndoCommand(QCoreApplication::translate("Undo Commands", "Change Map Properties")) , mMapDocument(mapDocument) , mBackgroundColor(backgroundColor) , mLayerDataFormat(layerDataFormat) { } void ChangeMapProperties::redo() { swap(); } void ChangeMapProperties::undo() { swap(); } void ChangeMapProperties::swap() { Map *map = mMapDocument->map(); const QColor backgroundColor = map->backgroundColor(); const Map::LayerDataFormat layerDataFormat = map->layerDataFormat(); map->setBackgroundColor(mBackgroundColor); map->setLayerDataFormat(mLayerDataFormat); mMapDocument->emitMapChanged(); mBackgroundColor = backgroundColor; mLayerDataFormat = layerDataFormat; } tiled-qt-0.9.1/src/tiled/changemapproperties.h000066400000000000000000000032521217502731700213540ustar00rootroot00000000000000/* * changemapproperties.h * Copyright 2012, Emmanuel Barroga emmanuelbarroga@gmail.com * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGEMAPPROPERTIES_H #define CHANGEMAPPROPERTIES_H #include "map.h" #include #include namespace Tiled { namespace Internal { class MapDocument; class ChangeMapProperties : public QUndoCommand { public: /** * Constructs a new 'Change Map Properties' command. * * @param mapDocument the map document of the map * @param backgroundColor the new color to apply for the background * @param layerDataFormat the new layer data format */ ChangeMapProperties(MapDocument *mapDocument, const QColor &backgroundColor, Map::LayerDataFormat layerDataFormat); void undo(); void redo(); private: void swap(); MapDocument *mMapDocument; QColor mBackgroundColor; Map::LayerDataFormat mLayerDataFormat; }; } // namespace Internal } // namespace Tiled #endif // CHANGEMAPPROPERTIES_H tiled-qt-0.9.1/src/tiled/changeobjectgroupproperties.cpp000066400000000000000000000033631217502731700234600ustar00rootroot00000000000000/* * changeobjectgroupproperties.cpp * Copyright 2010, Jeff Bland * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changeobjectgroupproperties.h" #include "mapdocument.h" #include "objectgroup.h" #include "mapobjectmodel.h" #include using namespace Tiled; using namespace Tiled::Internal; ChangeObjectGroupProperties::ChangeObjectGroupProperties( MapDocument *mapDocument, ObjectGroup *objectGroup, const QColor &color) : QUndoCommand( QCoreApplication::translate( "Undo Commands", "Change Object Layer Properties")) , mMapDocument(mapDocument) , mObjectGroup(objectGroup) , mUndoColor(objectGroup->color()) , mRedoColor(color) { } void ChangeObjectGroupProperties::redo() { mObjectGroup->setColor(mRedoColor); mMapDocument->mapObjectModel()->emitObjectsChanged(mObjectGroup->objects()); } void ChangeObjectGroupProperties::undo() { mObjectGroup->setColor(mUndoColor); mMapDocument->mapObjectModel()->emitObjectsChanged(mObjectGroup->objects()); } tiled-qt-0.9.1/src/tiled/changeobjectgroupproperties.h000066400000000000000000000034461217502731700231270ustar00rootroot00000000000000/* * changeobjectgroupproperties.h * Copyright 2010, Jeff Bland * Copyright 2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGEOBJECTGROUPPROPERTIES_H #define CHANGEOBJECTGROUPPROPERTIES_H #include #include #include namespace Tiled { class ObjectGroup; namespace Internal { class MapDocument; class ChangeObjectGroupProperties : public QUndoCommand { public: /** * Constructs a new 'Change Object Layer Properties' command. * * @param mapDocument the map document of the object group's map * @param objectGroup the object group in to modify * @param newColor the new color to apply */ ChangeObjectGroupProperties(MapDocument *mapDocument, ObjectGroup *objectGroup, const QColor &newColor); void undo(); void redo(); private: MapDocument *mMapDocument; ObjectGroup *mObjectGroup; const QColor mUndoColor; const QColor mRedoColor; }; } // namespace Internal } // namespace Tiled #endif // CHANGEOBJECTGROUPPROPERTIES_H tiled-qt-0.9.1/src/tiled/changepolygon.cpp000066400000000000000000000030341217502731700205020ustar00rootroot00000000000000/* * changepolygon.cpp * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changepolygon.h" #include "mapdocument.h" #include "mapobject.h" #include "mapobjectmodel.h" #include using namespace Tiled; using namespace Tiled::Internal; ChangePolygon::ChangePolygon(MapDocument *mapDocument, MapObject *mapObject, const QPolygonF &oldPolygon) : mMapDocument(mapDocument) , mMapObject(mapObject) , mOldPolygon(oldPolygon) , mNewPolygon(mapObject->polygon()) { setText(QCoreApplication::translate("Undo Commands", "Change Polygon")); } void ChangePolygon::undo() { mMapDocument->mapObjectModel()->setObjectPolygon(mMapObject, mOldPolygon); } void ChangePolygon::redo() { mMapDocument->mapObjectModel()->setObjectPolygon(mMapObject, mNewPolygon); } tiled-qt-0.9.1/src/tiled/changepolygon.h000066400000000000000000000027671217502731700201630ustar00rootroot00000000000000/* * changepolygon.h * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGEPOLYGON_H #define CHANGEPOLYGON_H #include #include namespace Tiled { class MapObject; namespace Internal { class MapDocument; /** * Changes the polygon of a MapObject. * * This class expects the polygon to be already changed, and takes the previous * polygon in the constructor. */ class ChangePolygon : public QUndoCommand { public: ChangePolygon(MapDocument *mapDocument, MapObject *mapObject, const QPolygonF &oldPolygon); void undo(); void redo(); private: MapDocument *mMapDocument; MapObject *mMapObject; QPolygonF mOldPolygon; QPolygonF mNewPolygon; }; } // namespace Internal } // namespace Tiled #endif // CHANGEPOLYGON_H tiled-qt-0.9.1/src/tiled/changeproperties.cpp000066400000000000000000000030421217502731700212060ustar00rootroot00000000000000/* * changeproperties.cpp * Copyright 2008-2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changeproperties.h" #include using namespace Tiled; using namespace Tiled::Internal; ChangeProperties::ChangeProperties(const QString &kind, Object *object, const Properties &newProperties) : mObject(object) , mNewProperties(newProperties) { setText(QCoreApplication::translate("Undo Commands", "Change %1 Properties").arg(kind)); } void ChangeProperties::redo() { swapProperties(); } void ChangeProperties::undo() { swapProperties(); } void ChangeProperties::swapProperties() { const Properties oldProperties = mObject->properties(); mObject->setProperties(mNewProperties); mNewProperties = oldProperties; } tiled-qt-0.9.1/src/tiled/changeproperties.h000066400000000000000000000031451217502731700206570ustar00rootroot00000000000000/* * changeproperties.h * Copyright 2008-2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGEPROPERTIES_H #define CHANGEPROPERTIES_H #include "object.h" #include #include namespace Tiled { namespace Internal { class ChangeProperties : public QUndoCommand { public: /** * Constructs a new 'Change Properties' command. * * @param kind the kind of properties (Map, Layer, Object, etc.) * @param object the object of which the properties should be changed * @param newProperties the new properties that should be applied */ ChangeProperties(const QString &kind, Object *object, const Properties &newProperties); void undo(); void redo(); private: void swapProperties(); Object *mObject; Properties mNewProperties; }; } // namespace Internal } // namespace Tiled #endif // CHANGEPROPERTIES_H tiled-qt-0.9.1/src/tiled/changetileselection.cpp000066400000000000000000000030221217502731700216530ustar00rootroot00000000000000/* * changetileselection.cpp * Copyright 2009-2010, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changetileselection.h" #include "mapdocument.h" #include using namespace Tiled::Internal; ChangeTileSelection::ChangeTileSelection(MapDocument *mapDocument, const QRegion &newSelection) : QUndoCommand(QCoreApplication::translate("Undo Commands", "Change Selection")) , mMapDocument(mapDocument) , mSelection(newSelection) { } void ChangeTileSelection::undo() { swapSelection(); } void ChangeTileSelection::redo() { swapSelection(); } void ChangeTileSelection::swapSelection() { const QRegion oldSelection = mMapDocument->tileSelection(); mMapDocument->setTileSelection(mSelection); mSelection = oldSelection; } tiled-qt-0.9.1/src/tiled/changetileselection.h000066400000000000000000000026341217502731700213300ustar00rootroot00000000000000/* * changetileselection.h * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGETILESELECTION_H #define CHANGETILESELECTION_H #include #include namespace Tiled { namespace Internal { class MapDocument; class ChangeTileSelection : public QUndoCommand { public: /** * Creates an undo command that sets the selection of \a mapDocument to * the given \a selection. */ ChangeTileSelection(MapDocument *mapDocument, const QRegion &selection); void undo(); void redo(); private: void swapSelection(); MapDocument *mMapDocument; QRegion mSelection; }; } // namespace Internal } // namespace Tiled #endif // CHANGETILESELECTION_H tiled-qt-0.9.1/src/tiled/changetileterrain.cpp000066400000000000000000000066661217502731700213530ustar00rootroot00000000000000/* * changetileterrain.cpp * Copyright 2012, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "changetileterrain.h" #include "mapdocument.h" #include "tile.h" #include namespace Tiled { namespace Internal { ChangeTileTerrain::ChangeTileTerrain() : mMapDocument(0) , mTileset(0) , mMergeable(false) { initText(); } ChangeTileTerrain::ChangeTileTerrain(MapDocument *mapDocument, Tile *tile, unsigned terrain) : mMapDocument(mapDocument) , mTileset(tile->tileset()) , mMergeable(true) { initText(); mChanges.insert(tile, Change(tile->terrain(), terrain)); } ChangeTileTerrain::ChangeTileTerrain(MapDocument *mapDocument, const Changes &changes) : mMapDocument(mapDocument) , mTileset(changes.begin().key()->tileset()) , mChanges(changes) , mMergeable(true) { initText(); } void ChangeTileTerrain::undo() { Changes::const_iterator i = mChanges.constBegin(); QList changedTiles; #if QT_VERSION >= 0x040700 changedTiles.reserve(mChanges.size()); #endif while (i != mChanges.constEnd()) { Tile *tile = i.key(); const Change &change = i.value(); tile->setTerrain(change.from); changedTiles.append(tile); ++i; } mMapDocument->emitTileTerrainChanged(changedTiles); } void ChangeTileTerrain::redo() { Changes::const_iterator i = mChanges.constBegin(); QList changedTiles; #if QT_VERSION >= 0x040700 changedTiles.reserve(mChanges.size()); #endif while (i != mChanges.constEnd()) { Tile *tile = i.key(); const Change &change = i.value(); tile->setTerrain(change.to); changedTiles.append(tile); ++i; } mMapDocument->emitTileTerrainChanged(changedTiles); } bool ChangeTileTerrain::mergeWith(const QUndoCommand *other) { if (!mMergeable) return false; const ChangeTileTerrain *o = static_cast(other); if (o->mMapDocument && !(mMapDocument == o->mMapDocument && mTileset == o->mTileset)) return false; Changes::const_iterator i = o->mChanges.constBegin(); Changes::const_iterator i_end = o->mChanges.constEnd(); while (i != i_end) { Tile *tile = i.key(); const Change &change = i.value(); if (mChanges.contains(tile)) mChanges[tile].to = change.to; else mChanges.insert(tile, change); ++i; } mMergeable = o->mMergeable; return true; } void ChangeTileTerrain::initText() { setText(QCoreApplication::translate("Undo Commands", "Change Tile Terrain")); } } // namespace Internal } // namespace Tiled tiled-qt-0.9.1/src/tiled/changetileterrain.h000066400000000000000000000041011217502731700207760ustar00rootroot00000000000000/* * changetileterrain.h * Copyright 2012, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CHANGETILETERRAIN_H #define CHANGETILETERRAIN_H #include #include #include "undocommands.h" namespace Tiled { class Tile; class Tileset; namespace Internal { class MapDocument; class ChangeTileTerrain : public QUndoCommand { public: struct Change { Change() {} Change(unsigned from, unsigned to) : from(from), to(to) {} unsigned from; unsigned to; }; typedef QMap Changes; /** * Constructs an empty command that changes no terrain. When merged into * a previous terrain change command, it prevents that command from merging * with future commands. */ ChangeTileTerrain(); /** * Changes the terrain of \a tile. */ ChangeTileTerrain(MapDocument *mapDocument, Tile *tile, unsigned terrain); /** * Applies the given terrain \a changes. */ ChangeTileTerrain(MapDocument *mapDocument, const Changes &changes); void undo(); void redo(); int id() const { return Cmd_ChangeTileTerrain; } bool mergeWith(const QUndoCommand *other); private: void initText(); MapDocument *mMapDocument; Tileset *mTileset; Changes mChanges; bool mMergeable; }; } // namespace Internal } // namespace Tiled #endif // CHANGETILETERRAIN_H tiled-qt-0.9.1/src/tiled/clipboardmanager.cpp000066400000000000000000000072171217502731700211460ustar00rootroot00000000000000/* * clipboardmanager.cpp * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "clipboardmanager.h" #include "map.h" #include "mapdocument.h" #include "mapobject.h" #include "objectgroup.h" #include "tmxmapreader.h" #include "tmxmapwriter.h" #include "tile.h" #include "tilelayer.h" #include #include #include #include static const char * const TMX_MIMETYPE = "text/tmx"; using namespace Tiled; using namespace Tiled::Internal; ClipboardManager::ClipboardManager(QObject *parent) : QObject(parent), mHasMap(false) { mClipboard = QApplication::clipboard(); connect(mClipboard, SIGNAL(dataChanged()), SLOT(updateHasMap())); updateHasMap(); } Map *ClipboardManager::map() const { const QMimeData *mimeData = mClipboard->mimeData(); const QByteArray data = mimeData->data(QLatin1String(TMX_MIMETYPE)); if (data.isEmpty()) return 0; TmxMapReader reader; return reader.fromByteArray(data); } void ClipboardManager::setMap(const Map *map) { TmxMapWriter mapWriter; QMimeData *mimeData = new QMimeData; mimeData->setData(QLatin1String(TMX_MIMETYPE), mapWriter.toByteArray(map)); mClipboard->setMimeData(mimeData); } void ClipboardManager::copySelection(const MapDocument *mapDocument) { const Layer *currentLayer = mapDocument->currentLayer(); if (!currentLayer) return; const Map *map = mapDocument->map(); const QRegion &tileSelection = mapDocument->tileSelection(); const QList &selectedObjects = mapDocument->selectedObjects(); const TileLayer *tileLayer = dynamic_cast(currentLayer); Layer *copyLayer = 0; if (!tileSelection.isEmpty() && tileLayer) { // Copy the selected part of the layer copyLayer = tileLayer->copy(tileSelection.translated(-tileLayer->x(), -tileLayer->y())); } else if (!selectedObjects.isEmpty()) { // Create a new object group with clones of the selected objects ObjectGroup *objectGroup = new ObjectGroup; foreach (const MapObject *mapObject, selectedObjects) objectGroup->addObject(mapObject->clone()); copyLayer = objectGroup; } else { return; } // Create a temporary map to write to the clipboard Map copyMap(map->orientation(), copyLayer->width(), copyLayer->height(), map->tileWidth(), map->tileHeight()); // Resolve the set of tilesets used by this layer foreach (Tileset *tileset, copyLayer->usedTilesets()) copyMap.addTileset(tileset); copyMap.addLayer(copyLayer); setMap(©Map); } void ClipboardManager::updateHasMap() { const QMimeData *data = mClipboard->mimeData(); const bool mapInClipboard = data && data->hasFormat(QLatin1String(TMX_MIMETYPE)); if (mapInClipboard != mHasMap) { mHasMap = mapInClipboard; emit hasMapChanged(); } } tiled-qt-0.9.1/src/tiled/clipboardmanager.h000066400000000000000000000036541217502731700206140ustar00rootroot00000000000000/* * clipboardmanager.h * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef CLIPBOARDMANAGER_H #define CLIPBOARDMANAGER_H #include class QClipboard; namespace Tiled { class Map; namespace Internal { class MapDocument; /** * The clipboard manager deals with interaction with the clipboard. */ class ClipboardManager : public QObject { Q_OBJECT public: ClipboardManager(QObject *parent = 0); /** * Returns whether the clipboard has a map. */ bool hasMap() const { return mHasMap; } /** * Retrieves the map from the clipboard. Returns 0 when there was no map or * loading failed. */ Map *map() const; /** * Sets the given map on the clipboard. */ void setMap(const Map *map); /** * Convenience method to copy the current selection to the clipboard. * Deals with either tile selection or object selection. */ void copySelection(const MapDocument *mapDocument); signals: /** * Emitted when whether the clip has a map changed. */ void hasMapChanged(); private slots: void updateHasMap(); private: QClipboard *mClipboard; bool mHasMap; }; #endif // CLIPBOARDMANAGER_H } // namespace Internal } // namespace Tiled tiled-qt-0.9.1/src/tiled/colorbutton.cpp000066400000000000000000000032741217502731700202250ustar00rootroot00000000000000/* * colorbutton.cpp * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "colorbutton.h" #include #include using namespace Tiled; using namespace Tiled::Internal; ColorButton::ColorButton(QWidget *parent) : QToolButton(parent) { setColor(Qt::white); connect(this, SIGNAL(clicked()), this, SLOT(pickColor())); } void ColorButton::setColor(const QColor &color) { if (mColor == color || !color.isValid()) return; mColor = color; QSize size(iconSize()); size.rwidth() -= 2; size.rheight() -= 2; QPixmap pixmap(size); pixmap.fill(mColor); QPainter painter(&pixmap); QColor border(Qt::black); border.setAlpha(128); painter.setPen(border); painter.drawRect(0, 0, pixmap.width() - 1, pixmap.height() - 1); setIcon(QIcon(pixmap)); emit colorChanged(color); } void ColorButton::pickColor() { QColor newColor = QColorDialog::getColor(mColor, this); if (newColor.isValid()) setColor(newColor); } tiled-qt-0.9.1/src/tiled/colorbutton.h000066400000000000000000000026401217502731700176660ustar00rootroot00000000000000/* * colorbutton.h * Copyright 2009, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COLORBUTTON_H #define COLORBUTTON_H #include #include namespace Tiled { namespace Internal { /** * A tool button for letting the user pick a color. When clicked it shows a * color dialog and it has an icon to represent the currently chosen color. */ class ColorButton : public QToolButton { Q_OBJECT public: ColorButton(QWidget *parent = 0); QColor color() const { return mColor; } void setColor(const QColor &color); signals: void colorChanged(const QColor &color); private slots: void pickColor(); private: QColor mColor; }; } // namespace Internal } // namespace Tiled #endif // COLORBUTTON_H tiled-qt-0.9.1/src/tiled/command.cpp000066400000000000000000000135601217502731700172700ustar00rootroot00000000000000/* * command.cpp * Copyright 2011, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "command.h" #include "documentmanager.h" #include "mapdocument.h" #include #include using namespace Tiled; using namespace Tiled::Internal; QString Command::finalCommand() const { QString finalCommand = command; // Perform map filename replacement MapDocument *mapDocument = DocumentManager::instance()->currentDocument(); if (mapDocument) { const QString fileName = mapDocument->fileName(); finalCommand.replace(QLatin1String("%mapfile"), QString(QLatin1String("\"%1\"")).arg(fileName)); } return finalCommand; } void Command::execute(bool inTerminal) const { // Save if save option is unset or true QSettings settings; QVariant variant = settings.value(QLatin1String("saveBeforeExecute"), true); if (variant.toBool()) { MapDocument *document = DocumentManager::instance()->currentDocument(); if (document) document->save(); } // Start the process new CommandProcess(*this, inTerminal); } QVariant Command::toQVariant() const { QHash hash; hash[QLatin1String("Enabled")] = isEnabled; hash[QLatin1String("Name")] = name; hash[QLatin1String("Command")] = command; return hash; } Command Command::fromQVariant(const QVariant &variant) { const QHash hash = variant.toHash(); const QString namePref = QLatin1String("Name"); const QString commandPref = QLatin1String("Command"); const QString enablePref = QLatin1String("Enabled"); Command command; if (hash.contains(enablePref)) command.isEnabled = hash[enablePref].toBool(); if (hash.contains(namePref)) command.name = hash[namePref].toString(); if (hash.contains(commandPref)) command.command = hash[commandPref].toString(); return command; } CommandProcess::CommandProcess(const Command &command, bool inTerminal) : QProcess(DocumentManager::instance()) , mName(command.name) , mFinalCommand(command.finalCommand()) #ifdef Q_OS_MAC , mFile(QLatin1String("tiledXXXXXX.command")) #endif { // Give an error if the command is empty or just whitespace if (mFinalCommand.trimmed().isEmpty()) { handleError(QProcess::FailedToStart); return; } // Modify the command to run in a terminal if (inTerminal) { #ifdef Q_WS_X11 static bool hasGnomeTerminal = QProcess::execute( QLatin1String("which gnome-terminal")) == 0; if (hasGnomeTerminal) mFinalCommand = QLatin1String("gnome-terminal -x ") + mFinalCommand; else mFinalCommand = QLatin1String("xterm -e ") + mFinalCommand; #elif defined(Q_OS_MAC) // The only way I know to launch a Terminal with a command on mac is // to make a .command file and open it. The client command invoke the // exectuable directly (rather than using open) in order to get std // output in the terminal. Otherwise, you can use the Console // application to see the output. // Create and write the command to a .command file if (!mFile.open()) { handleError(tr("Unable to create/open %1").arg(mFile.fileName())); return; } mFile.write(mFinalCommand.toStdString().c_str()); mFile.close(); // Add execute permission to the file int chmodRet = QProcess::execute(QString(QLatin1String( "chmod +x \"%1\"")).arg(mFile.fileName())); if (chmodRet != 0) { handleError(tr("Unable to add executable permissions to %1") .arg(mFile.fileName())); return; } // Use open command to launch the command in the terminal // -W makes it not return immediately // -n makes it open a new instance of terminal if it is open already mFinalCommand = QString(QLatin1String("open -W -n \"%1\"")) .arg(mFile.fileName()); #endif } connect(this, SIGNAL(error(QProcess::ProcessError)), SLOT(handleError(QProcess::ProcessError))); connect(this, SIGNAL(finished(int)), SLOT(deleteLater())); start(mFinalCommand); } void CommandProcess::handleError(QProcess::ProcessError error) { QString errorStr; switch (error) { case QProcess::FailedToStart: errorStr = tr("The command failed to start."); break; case QProcess::Crashed: errorStr = tr("The command crashed."); break; case QProcess::Timedout: errorStr = tr("The command timed out."); break; default: errorStr = tr("An unknown error occurred."); } handleError(errorStr); } void CommandProcess::handleError(const QString &error) { QString title = tr("Error Executing %1").arg(mName); QString message = error + QLatin1String("\n\n") + mFinalCommand; QWidget *parent = DocumentManager::instance()->widget(); QMessageBox::warning(parent, title, message); // Make sure this object gets deleted if the process failed to start deleteLater(); } tiled-qt-0.9.1/src/tiled/command.h000066400000000000000000000041041217502731700167270ustar00rootroot00000000000000/* * command.h * Copyright 2011, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COMMAND_H #define COMMAND_H #include #include #include #ifdef Q_OS_MAC #include #endif namespace Tiled { namespace Internal { struct Command { Command(bool isEnabled = true, const QString &name = QString(), const QString &command = QString()) : isEnabled(isEnabled) , name(name) , command(command) {} bool isEnabled; QString name; QString command; /** * Returns the final command with replaced tokens. */ QString finalCommand() const; /** * Executes the command in the operating system shell or terminal * application. */ void execute(bool inTerminal = false) const; /** * Stores this command in a QVariant. */ QVariant toQVariant() const; /** * Generates a command from a QVariant. */ static Command fromQVariant(const QVariant &variant); }; class CommandProcess : public QProcess { Q_OBJECT public: CommandProcess(const Command &command, bool inTerminal = false); private slots: void handleError(QProcess::ProcessError); private: void handleError(const QString &); QString mName; QString mFinalCommand; #ifdef Q_OS_MAC QTemporaryFile mFile; #endif }; } // namespace Internal } // namespace Tiled #endif // COMMAND_H tiled-qt-0.9.1/src/tiled/commandbutton.cpp000066400000000000000000000074051217502731700205250ustar00rootroot00000000000000/* * commandbutton.cpp * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "commandbutton.h" #include "commanddatamodel.h" #include "commanddialog.h" #include "utils.h" #include #include #include using namespace Tiled; using namespace Tiled::Utils; using namespace Tiled::Internal; CommandButton::CommandButton(QWidget *parent) : QToolButton(parent) , mMenu(new QMenu(this)) { setIcon(QIcon(QLatin1String(":images/24x24/system-run.png"))); setThemeIcon(this, "system-run"); retranslateUi(); setPopupMode(QToolButton::MenuButtonPopup); setMenu(mMenu); connect(mMenu, SIGNAL(aboutToShow()), SLOT(populateMenu())); connect(this, SIGNAL(clicked()), SLOT(runCommand())); } void CommandButton::runCommand() { Command command; QAction *action = dynamic_cast(sender()); if (action && action->data().isValid()) { //run the command passed by the action command = Command::fromQVariant(action->data()); } else { //run the default command command = CommandDataModel().firstEnabledCommand(); if (!command.isEnabled) { QMessageBox msgBox(window()); msgBox.setIcon(QMessageBox::Warning); msgBox.setWindowTitle(tr("Error Executing Command")); msgBox.setText(tr("You do not have any commands setup.")); msgBox.addButton(QMessageBox::Ok); msgBox.addButton(tr("Edit commands..."), QMessageBox::ActionRole); msgBox.setDefaultButton(QMessageBox::Ok); msgBox.setEscapeButton(QMessageBox::Ok); QAbstractButton *button = msgBox.buttons().last(); connect(button, SIGNAL(clicked()), SLOT(showDialog())); msgBox.exec(); return; } } command.execute(); } void CommandButton::showDialog() { CommandDialog dialog(window()); dialog.exec(); } void CommandButton::populateMenu() { mMenu->clear(); // Use a data model for getting the command list to avoid having to // manually parse the settings const CommandDataModel model; const QList &commands = model.allCommands(); foreach (const Command &command, commands) { if (!command.isEnabled) continue; QAction *action = new QAction(command.name, this); action->setStatusTip(command.command); action->setData(command.toQVariant()); connect(action, SIGNAL(triggered()), SLOT(runCommand())); mMenu->addAction(action); } if (!mMenu->isEmpty()) mMenu->addSeparator(); // Add "Edit Commands..." action QAction *action = new QAction(tr("Edit Commands..."), this); connect(action, SIGNAL(triggered()), SLOT(showDialog())); mMenu->addAction(action); } void CommandButton::changeEvent(QEvent *event) { QToolButton::changeEvent(event); switch (event->type()) { case QEvent::LanguageChange: retranslateUi(); break; default: break; } } void CommandButton::retranslateUi() { setToolTip(tr("Execute Command")); setShortcut(QKeySequence(tr("F5"))); } tiled-qt-0.9.1/src/tiled/commandbutton.h000066400000000000000000000024251217502731700201670ustar00rootroot00000000000000/* * commandbutton.h * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COMMANDBUTTON_H #define COMMANDBUTTON_H #include class QMenu; namespace Tiled { namespace Internal { class MainWindow; class DocumentManager; class CommandButton : public QToolButton { Q_OBJECT public: CommandButton(QWidget *parent); protected: void changeEvent(QEvent *event); private slots: void runCommand(); void showDialog(); void populateMenu(); private: void retranslateUi(); QMenu *mMenu; }; } // namespace Internal } // namespace Tiled #endif // PREFERENCESDIALOG_H tiled-qt-0.9.1/src/tiled/commanddatamodel.cpp000066400000000000000000000357301217502731700211460ustar00rootroot00000000000000/* * commanddatamodel.cpp * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "commanddatamodel.h" #include #include #include using namespace Tiled; using namespace Tiled::Internal; const char *commandMimeType = "application/x-tiled-commandptr"; CommandDataModel::CommandDataModel() { // Load saveBeforeExecute option QVariant s = mSettings.value(QLatin1String("saveBeforeExecute"), true); mSaveBeforeExecute = s.toBool(); // Load command list const QVariant variant = mSettings.value(QLatin1String("commandList")); const QList commands = variant.toList(); foreach (const QVariant &commandVariant, commands) mCommands.append(Command::fromQVariant(commandVariant)); // Add default commands the first time the app has booted up. // This is useful on it's own and helps demonstrate how to use the commands. const QString addPrefStr = QLatin1String("addedDefaultCommands"); const bool addedCommands = mSettings.value(addPrefStr, false).toBool(); if (!addedCommands) { // Disable default commands by default so user gets an informative // warning when clicking the command button for the first time Command command(false); #ifdef Q_WS_X11 command.command = QLatin1String("gedit %mapfile"); #elif defined(Q_OS_MAC) command.command = QLatin1String("open -t %mapfile"); #endif if (!command.command.isEmpty()) { command.name = tr("Open in text editor"); mCommands.push_back(command); } commit(); mSettings.setValue(addPrefStr, true); } } void CommandDataModel::commit() { // Save saveBeforeExecute option mSettings.setValue(QLatin1String("saveBeforeExecute"), mSaveBeforeExecute); // Save command list QList commands; foreach (const Command &command, mCommands) commands.append(command.toQVariant()); mSettings.setValue(QLatin1String("commandList"), commands); } Command CommandDataModel::firstEnabledCommand() const { foreach (const Command &command, mCommands) if (command.isEnabled) return command; return Command(false); } bool CommandDataModel::removeRows(int row, int count, const QModelIndex &parent) { if (row < 0 || row + count > mCommands.size()) return false; beginRemoveRows(parent, row, row + count); mCommands.erase(mCommands.begin() + row, mCommands.begin() + row + count); endRemoveRows(); return true; } void CommandDataModel::removeRows(QModelIndexList indices) { while (!indices.empty()) { const int row = indices.takeFirst().row(); if (row >= mCommands.size()) continue; beginRemoveRows(QModelIndex(), row, row); mCommands.removeAt(row); // Decrement later indices since we removed a row for (QModelIndexList::iterator i = indices.begin(); i != indices.end(); ++i) if (i->row() > row) *i = i->sibling(i->row() - 1, i->column()); endRemoveRows(); } } int CommandDataModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : mCommands.size() + 1; } int CommandDataModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : 3; } QVariant CommandDataModel::data(const QModelIndex &index, int role) const { const bool isNormalRow = index.row() < mCommands.size(); Command command; if (isNormalRow) command = mCommands[index.row()]; switch (role) { case Qt::DisplayRole: case Qt::EditRole: if (isNormalRow) { if (index.column() == NameColumn) return command.name; if (index.column() == CommandColumn) return command.command; } else if (index.column() == NameColumn) { if (role == Qt::EditRole) return QString(); else return tr(""); } break; case Qt::ToolTipRole: if (isNormalRow) { if (index.column() == NameColumn) return tr("Set a name for this command"); if (index.column() == CommandColumn) return tr("Set the shell command to execute"); if (index.column() == EnabledColumn) return tr("Show or hide this command in the command list"); } else if (index.column() == NameColumn) return tr("Add a new command"); break; case Qt::CheckStateRole: if (isNormalRow && index.column() == EnabledColumn) return command.isEnabled ? 2 : 0; break; } return QVariant(); } bool CommandDataModel::setData(const QModelIndex &index, const QVariant &value, int role) { const bool isNormalRow = index.row() < mCommands.size(); bool isModified = false; bool shouldAppend = false; Command command; if (isNormalRow) { // Get the command as it exists already command = mCommands[index.row()]; // Modify the command based on the passed date switch (role) { case Qt::EditRole: { const QString text = value.toString(); if (!text.isEmpty()) { if (index.column() == NameColumn) { command.name = value.toString(); isModified = true; } else if (index.column() == CommandColumn) { command.command = value.toString(); isModified = true; } } } case Qt::CheckStateRole: if (index.column() == EnabledColumn) { command.isEnabled = value.toInt() > 0; isModified = true; } } } else { // If final row was edited, insert the new command if (role == Qt::EditRole && index.column() == NameColumn) { command.name = value.toString(); if (!command.name.isEmpty() && command.name != tr("")) { isModified = true; shouldAppend = true; } } } if (isModified) { // Write the modified command to our cache if (shouldAppend) mCommands.push_back(command); else mCommands[index.row()] = command; // Reset if there could be new rows or reordering, else emit dataChanged if (shouldAppend || index.column() == NameColumn) { beginResetModel(); endResetModel(); } else { emit dataChanged(index, index); } } return isModified; } Qt::ItemFlags CommandDataModel::flags(const QModelIndex &index) const { const bool isNormalRow = index.row() < mCommands.size(); Qt::ItemFlags f = QAbstractTableModel::flags(index); if (isNormalRow) { f |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; if (index.column() == EnabledColumn) f |= Qt::ItemIsUserCheckable; } else { f |= Qt::ItemIsDropEnabled; if (index.column() == NameColumn) f |= Qt::ItemIsEditable; } return f; } QVariant CommandDataModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); const char *sectionLabels[3] = { QT_TR_NOOP("Name"), QT_TR_NOOP("Command"), QT_TR_NOOP("Enable") }; return tr(sectionLabels[section]); } QMenu *CommandDataModel::contextMenu(QWidget *parent, const QModelIndex &index) { QMenu *menu = NULL; const int row = index.row(); if (row >= 0 && row < mCommands.size()) { menu = new QMenu(parent); if (row > 0) { QAction *action = menu->addAction(tr("Move Up")); QSignalMapper *mapper = new QSignalMapper(action); mapper->setMapping(action, row); connect(action, SIGNAL(triggered()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), SLOT(moveUp(int))); } if (row+1 < mCommands.size()) { QAction *action = menu->addAction(tr("Move Down")); QSignalMapper *mapper = new QSignalMapper(action); mapper->setMapping(action, row + 1); connect(action, SIGNAL(triggered()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), SLOT(moveUp(int))); } menu->addSeparator(); { QAction *action = menu->addAction(tr("Execute")); QSignalMapper *mapper = new QSignalMapper(action); mapper->setMapping(action, row); connect(action, SIGNAL(triggered()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), SLOT(execute(int))); } #if defined(Q_WS_X11) || defined(Q_OS_MAC) { QAction *action = menu->addAction(tr("Execute in Terminal")); QSignalMapper *mapper = new QSignalMapper(action); mapper->setMapping(action, row); connect(action, SIGNAL(triggered()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), SLOT(executeInTerminal(int))); } #endif menu->addSeparator(); { QAction *action = menu->addAction(tr("Delete")); QSignalMapper *mapper = new QSignalMapper(action); mapper->setMapping(action, row); connect(action, SIGNAL(triggered()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), SLOT(remove(int))); } } return menu; } QMimeData *CommandDataModel::mimeData(const QModelIndexList &indices) const { int row = -1; foreach (const QModelIndex &index, indices) { // Only generate mime data on command rows if (index.row() < 0 || index.row() >= mCommands.size()) return 0; // Currently only one row at a time is supported for drags // Note: we can get multiple indexes in the same row (different columns) if (row != -1 && index.row() != row) return 0; row = index.row(); } const Command &command = mCommands[row]; QMimeData* mimeData = new QMimeData(); // Text data is used if command is dragged to a text editor or terminal mimeData->setText(command.finalCommand()); // Ptr is used if command is dragged onto another command // We could store the index instead, the only difference would be that if // the item is moved or deleted shomehow during the drag, the ptr approach // will result in a no-op instead of moving the wrong thing. const Command *addr = &command; mimeData->setData(QLatin1String(commandMimeType), QByteArray((const char *)&addr, sizeof(addr))); return mimeData; } QStringList CommandDataModel::mimeTypes() const { QStringList result(QLatin1String("text/plain")); result.append(QLatin1String(commandMimeType)); return result; } Qt::DropActions CommandDataModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } bool CommandDataModel::dropMimeData(const QMimeData *data, Qt::DropAction, int, int, const QModelIndex &parent) { if (!parent.isValid()) return false; const int dstRow = parent.row(); if (data->hasFormat(QLatin1String(commandMimeType))) { // Get the ptr to the command that was being dragged const QByteArray byteData = data->data(QLatin1String(commandMimeType)); Q_ASSERT(byteData.length() == sizeof(Command*)); const Command *addr = *(Command**)byteData.data(); // Find the command in the command list so we can move/copy it for (int srcRow = 0; srcRow < mCommands.size(); ++srcRow) if (addr == &mCommands[srcRow]) { // If a command is dropped on another command, // move the src command into the positon of the dst command. if (dstRow < mCommands.size()) return move(srcRow, dstRow); // If a command is dropped elsewhere, create a copy of it if (dstRow == mCommands.size()) { append(Command(addr->isEnabled, tr("%1 (copy)").arg(addr->name), addr->command)); return true; } } } if (data->hasText()) { // If text is dropped on a valid command, just replace the data if (dstRow < mCommands.size()) return setData(parent, data->text(), Qt::EditRole); // If text is dropped elsewhere, create a new command // Assume the dropped text is the command, not the name if (dstRow == mCommands.size()) { append(Command(true, tr("New command"), data->text())); return true; } } return false; } bool CommandDataModel::move(int commandIndex, int newIndex) { if (commandIndex < 0 || commandIndex >= mCommands.size() || newIndex < 0 || newIndex >= mCommands.size() || newIndex == commandIndex) return false; if (!beginMoveRows(QModelIndex(), commandIndex, commandIndex, QModelIndex(), newIndex > commandIndex ? newIndex + 1 : newIndex)) return false; if (commandIndex - newIndex == 1 || newIndex - commandIndex == 1) // Swapping is probably more efficient than removing/inserting mCommands.swap(commandIndex, newIndex); else { const Command command = mCommands.at(commandIndex); mCommands.removeAt(commandIndex); mCommands.insert(newIndex, command); } endMoveRows(); return true; } void CommandDataModel::append(const Command &command) { beginInsertRows(QModelIndex(), mCommands.size(), mCommands.size()); mCommands.append(command); endInsertRows(); } void CommandDataModel::moveUp(int commandIndex) { move(commandIndex, commandIndex - 1); } void CommandDataModel::execute(int commandIndex) const { mCommands.at(commandIndex).execute(); } void CommandDataModel::executeInTerminal(int commandIndex) const { mCommands.at(commandIndex).execute(true); } void CommandDataModel::remove(int commandIndex) { removeRow(commandIndex); } tiled-qt-0.9.1/src/tiled/commanddatamodel.h000066400000000000000000000116431217502731700206100ustar00rootroot00000000000000/* * commanddatamodel.h * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COMMANDDATAMODEL_H #define COMMANDDATAMODEL_H #include "command.h" #include #include class QMenu; namespace Tiled { namespace Internal { class CommandDataModel : public QAbstractTableModel { Q_OBJECT public: enum { NameColumn, CommandColumn, EnabledColumn }; /** * Constructs the object and parses the users settings to allow easy * programmatic access to the command list. */ CommandDataModel(); /** * Saves the data to the users preferences. */ void commit(); /** * Returns whether saving before executing commands is enabled. */ bool saveBeforeExecute() const { return mSaveBeforeExecute; } /** * Enables or disables saving before executing commands. */ void setSaveBeforeExecute(bool enabled) { mSaveBeforeExecute = enabled; } /** * Returns the first enabled command in the list, or an empty * disabled command if there are no enabled commands. */ Command firstEnabledCommand() const; /** * Returns a list of all the commands. */ const QList &allCommands() const { return mCommands; } /** * Remove the given row or rows from the model. */ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); /** * Deletes the commands associated with the given row indices. */ void removeRows(QModelIndexList indices); /** * Returns the number of rows (this includes the row). */ int rowCount(const QModelIndex &) const; /** * Returns the number of columns. */ int columnCount(const QModelIndex &) const; /** * Returns the data at index for the given role. */ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; /** * Sets the data at index to the given value. * for the given role */ bool setData(const QModelIndex &index, const QVariant &value, int role); /** * Returns flags for the item at index. */ Qt::ItemFlags flags(const QModelIndex &index) const; /** * Returns the header data for the given section and role. * orientation should be Qt::Horizontal. */ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::EditRole) const; /** * Returns a menu containing a list of appropriate actions for the item at * index, or 0 if there are no actions for the index. */ QMenu *contextMenu(QWidget *parent, const QModelIndex &index); /** * Returns mime data for the first index in indexes. */ QMimeData *mimeData(const QModelIndexList &indexes) const; /** * Returns a list of mime types that can represent a command. */ QStringList mimeTypes() const; /** * Returns the drop actions that can be performed. */ Qt::DropActions supportedDropActions() const; /** * Handles dropping of mime data onto parent. */ bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); public slots: /** * Moves the command at commandIndex to newIndex>. */ bool move(int commandIndex, int newIndex); /** * Appends command to the command list. */ void append(const Command &command); /** * Moves the command at commandIndex up one index, if possible. */ void moveUp(int commandIndex); /** * Executes the command at commandIndex. */ void execute(int commandIndex) const; /** * Executes the command at commandIndex within the systems native * terminal if available. */ void executeInTerminal(int commandIndex) const; /** * Deletes the command at commandIndex. */ void remove(int commandIndex); private: QSettings mSettings; QList mCommands; bool mSaveBeforeExecute; }; } // namespace Internal } // namespace Tiled #endif // COMMANDDATAMODEL_H tiled-qt-0.9.1/src/tiled/commanddialog.cpp000066400000000000000000000074211217502731700204470ustar00rootroot00000000000000/* * commanddialog.cpp * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "commanddialog.h" #include "ui_commanddialog.h" #include "commanddatamodel.h" #include "utils.h" #include #include #include #include using namespace Tiled; using namespace Tiled::Internal; CommandDialog::CommandDialog(QWidget *parent) : QDialog(parent) , mUi(new Ui::CommandDialog) { mUi->setupUi(this); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); mUi->saveBox->setChecked(mUi->treeView->model()->saveBeforeExecute()); setWindowTitle(tr("Edit Commands")); Utils::restoreGeometry(this); } CommandDialog::~CommandDialog() { Utils::saveGeometry(this); delete mUi; } void CommandDialog::accept() { QDialog::accept(); mUi->treeView->model()->setSaveBeforeExecute(mUi->saveBox->isChecked()); mUi->treeView->model()->commit(); } CommandTreeView::CommandTreeView(QWidget *parent) : QTreeView(parent) , mModel(new CommandDataModel) { setModel(mModel); setRootIsDecorated(false); // Setup resizing so the command column stretches setColumnWidth(0, 200); QHeaderView *h = header(); h->setStretchLastSection(false); #if QT_VERSION >= 0x050000 h->setSectionResizeMode(CommandDataModel::NameColumn, QHeaderView::Interactive); h->setSectionResizeMode(CommandDataModel::CommandColumn, QHeaderView::Stretch); h->setSectionResizeMode(CommandDataModel::EnabledColumn, QHeaderView::ResizeToContents); #else h->setResizeMode(CommandDataModel::NameColumn, QHeaderView::Interactive); h->setResizeMode(CommandDataModel::CommandColumn, QHeaderView::Stretch); h->setResizeMode(CommandDataModel::EnabledColumn, QHeaderView::ResizeToContents); #endif // Allow deletion via keyboard QShortcut *d = new QShortcut(QKeySequence::Delete, this); d->setContext(Qt::WidgetShortcut); connect(d, SIGNAL(activated()), SLOT(removeSelectedCommands())); connect(mModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(handleRowsRemoved(QModelIndex, int, int))); } CommandTreeView::~CommandTreeView() { delete mModel; } void CommandTreeView::contextMenuEvent(QContextMenuEvent *event) { QModelIndex index = indexAt(event->pos()); // Generate a run a menu for the index QMenu *menu = mModel->contextMenu(this, index); if (menu) menu->exec(event->globalPos()); } void CommandTreeView::handleRowsRemoved(const QModelIndex &parent, int, int) { if (parent.isValid()) return; // Reselect the same row index of the removed row QItemSelectionModel *sModel = selectionModel(); QModelIndex index = sModel->currentIndex(); sModel->select(index.sibling(index.row() + 1,index.column()), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void CommandTreeView::removeSelectedCommands() { QItemSelectionModel *selection = selectionModel(); const QModelIndexList indices = selection->selectedRows(); mModel->removeRows(indices); } tiled-qt-0.9.1/src/tiled/commanddialog.h000066400000000000000000000040541217502731700201130ustar00rootroot00000000000000/* * commanddialog.h * Copyright 2010, Jeff Bland * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COMMANDDIALOG_H #define COMMANDDIALOG_H #include #include namespace Ui { class CommandDialog; } namespace Tiled { namespace Internal { class CommandDataModel; class CommandDialog : public QDialog { Q_OBJECT public: CommandDialog(QWidget *parent = 0); ~CommandDialog(); /** * Saves the changes to the users preferences. * Automatically called when the dialog is accepted. */ void accept(); private: Ui::CommandDialog *mUi; }; class CommandTreeView : public QTreeView { Q_OBJECT public: CommandTreeView(QWidget *parent); ~CommandTreeView(); /** * Returns the model used by this view in CommandDataMode form. */ CommandDataModel *model() const { return mModel; } private slots: /** * Displays a context menu for the item at event's position. */ void contextMenuEvent(QContextMenuEvent *event); /** * Fixes the selection after rows have been removed. */ void handleRowsRemoved(const QModelIndex &parent, int start, int end); /** * Gets the currently selected rows and tells the model to delete them. */ void removeSelectedCommands(); private: CommandDataModel *mModel; }; } // namespace Internal } // namespace Tiled #endif // COMMANDDIALOG_H tiled-qt-0.9.1/src/tiled/commanddialog.ui000066400000000000000000000046041217502731700203020ustar00rootroot00000000000000 CommandDialog 0 0 479 258 Properties QAbstractItemView::DragDrop true QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows &Save map before executing Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Tiled::Internal::CommandTreeView QTreeView
commanddialog.h
buttonBox accepted() CommandDialog accept() 248 254 157 274 buttonBox rejected() CommandDialog reject() 316 260 286 274
tiled-qt-0.9.1/src/tiled/commandlineparser.cpp000066400000000000000000000115601217502731700213530ustar00rootroot00000000000000/* * commandlineparser.cpp * Copyright 2011, Ben Longbons * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "commandlineparser.h" #include #include using namespace Tiled; using namespace Tiled::Internal; CommandLineParser::CommandLineParser() : mLongestArgument(0) , mShowHelp(false) { } void CommandLineParser::registerOption(Callback callback, void *data, QChar shortName, const QString &longName, const QString &help) { mOptions.append(Option(callback, data, shortName, longName, help)); const int length = longName.length(); if (mLongestArgument < length) mLongestArgument = length; } bool CommandLineParser::parse(const QStringList &arguments) { mFilesToOpen.clear(); mShowHelp = false; QStringList todo = arguments; mCurrentProgramName = QFileInfo(todo.takeFirst()).fileName(); int index = 0; bool noMoreArguments = false; while (!todo.isEmpty()) { index++; const QString arg = todo.takeFirst(); if (arg.isEmpty()) continue; if (noMoreArguments || arg.at(0) != QLatin1Char('-')) { mFilesToOpen.append(arg); continue; } if (arg.length() == 1) { // Traditionally a single hyphen means read file from stdin, // write file to stdout. This isn't supported right now. qWarning().nospace() << "Bad argument " << index << ": lonely hyphen"; showHelp(); return false; } // Long options if (arg.at(1) == QLatin1Char('-')) { // Double hypen "--" means no more options will follow if (arg.length() == 2) { noMoreArguments = true; continue; } if (!handleLongOption(arg)) { qWarning().nospace() << "Unknown long argument " << index << ": " << arg; mShowHelp = true; break; } continue; } // Short options for (int i = 1; i < arg.length(); ++i) { const QChar c = arg.at(i); if (!handleShortOption(c)) { qWarning().nospace() << "Unknown short argument " << index << '.' << i << ": " << c; mShowHelp = true; break; } } } if (mShowHelp) { showHelp(); return false; } return true; } void CommandLineParser::showHelp() { // TODO: Make translatable qWarning().nospace() << "Usage:\n" << " " << qPrintable(mCurrentProgramName) << " [options] [files...]\n\n" << "Options:"; qWarning(" -h %-*s : Display this help", mLongestArgument, "--help"); foreach (const Option &option, mOptions) { if (!option.shortName.isNull()) { qWarning(" -%c %-*s : %s", option.shortName.toLatin1(), mLongestArgument, qPrintable(option.longName), qPrintable(option.help)); } else { qWarning(" %-*s : %s", mLongestArgument, qPrintable(option.longName), qPrintable(option.help)); } } qWarning(); } bool CommandLineParser::handleLongOption(const QString &longName) { if (longName == QLatin1String("--help")) { mShowHelp = true; return true; } foreach (const Option &option, mOptions) { if (longName == option.longName) { option.callback(option.data); return true; } } return false; } bool CommandLineParser::handleShortOption(QChar c) { if (c == QLatin1Char('h')) { mShowHelp = true; return true; } foreach (const Option &option, mOptions) { if (c == option.shortName) { option.callback(option.data); return true; } } return false; } tiled-qt-0.9.1/src/tiled/commandlineparser.h000066400000000000000000000101061217502731700210130ustar00rootroot00000000000000/* * commandlineparser.h * Copyright 2011, Ben Longbons * Copyright 2011, Thorbjørn Lindeijer * * This file is part of Tiled. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef COMMANDLINEPARSER_H #define COMMANDLINEPARSER_H #include #include namespace Tiled { namespace Internal { /** * C-style callback function taking an arbitrary data pointer. */ typedef void (*Callback)(void *data); /** * A template function that will static-cast the given \a object to a type T * and call the member function of T given in the second template argument. */ template void MemberFunctionCall(void *object) { T *t = static_cast(object); (t->*memberFunction)(); } /** * A simple command line parser. Options should be registered through * registerOption(). * * The help option (-h/--help) is provided by the parser based on the * registered options. */ class CommandLineParser { public: CommandLineParser(); /** * Registers an option with the parser. When an option with the given * \a shortName or \a longName is encountered, \a callback is called with * \a data as its only parameter. */ void registerOption(Callback callback, void *data, QChar shortName, const QString &longName, const QString &help); /** * Convenience overload that allows registering an option with a callback * as a member function of a class. The class type and the member function * are given as template parameters, while the instance is passed in as * \a handler. * * \overload */ template void registerOption(T *handler, QChar shortName, const QString &longName, const QString &help) { registerOption(&MemberFunctionCall, handler, shortName, longName, help); } /** * Parses the given \a arguments. Returns false when the application is not * expected to run (either there was a parsing error, or the help was * requested). */ bool parse(const QStringList &arguments); /** * Returns the files to open that were found among the arguments. */ const QStringList &filesToOpen() const { return mFilesToOpen; } private: void showHelp(); bool handleLongOption(const QString &longName); bool handleShortOption(QChar c); /** * Internal definition of a command line option. */ struct Option { Option() : callback(0) , data(0) {} Option(Callback callback, void *data, QChar shortName, const QString &longName, const QString &help) : callback(callback) , data(data) , shortName(shortName) , longName(longName) , help(help) {} Callback callback; void *data; QChar shortName; QString longName; QString help; }; QVector