fritzing-app-0.9.3b/000077500000000000000000000000001272363601300143025ustar00rootroot00000000000000fritzing-app-0.9.3b/.gitignore000066400000000000000000000016401272363601300162730ustar00rootroot00000000000000.qmake.stash # QtCreator folders and files # ##################### phoenix.pro.user /phoenix-build-* /release* /build* /debug* *.user *.user* # FritzingTools folder ##################### #/tools # visual studio folders and files # ##################### *.sdf *.opensdf *.sln *.vcxproj *.vcxproj.* Makefile* # no longer keep boost library in the source # /src/lib/boost* # Compiled source # ################### *.pyc *.com *.class *.dll *.exe *.o *.so *.suo *.pdb # Temporary files # ############### *.py~ *.xml~ # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods #*.7z #*.dmg #*.gz #*.iso #*.jar #*.rar #*.tar #*.zip # Logs and databases # ###################### #*.log #*.sql #*.sqlite # OS generated files # ###################### .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db .bash_history saved_state fritzing-app-0.9.3b/Fritzing.1000066400000000000000000000012561272363601300161640ustar00rootroot00000000000000.TH FRITZING 1 "October 03, 2010" "Fritzing" .SH NAME fritzing \- from prototype to product .SH SYNOPSIS .B fritzing .SH DESCRIPTION .PP Fritzing is an open-source initiative to support designers, artists, researchers and hobbyists to work creatively with interactive electronics. We are creating a software and website in the spirit of Processing and Arduino, developing a tool that allows users to document their prototypes, share them with others, teach electronics in a classroom, and to create a pcb layout for professional manufacturing. .SH AUTHOR This manual page was written by Enrique Hernández Bello , for the Debian project (and may be used by others). fritzing-app-0.9.3b/Fritzing.sh000066400000000000000000000005261272363601300164350ustar00rootroot00000000000000#!/bin/sh appname="Fritzing" # dirname=`dirname "${0}"` # new dirname procedure from Volker Kuhlmann # use -f to make the readlink path absolute dirname="$(dirname -- "$(readlink -f -- "${0}")" )" if [ "$dirname" = "." ]; then dirname="$PWD/$dirname" fi LD_LIBRARY_PATH="${dirname}/lib" export LD_LIBRARY_PATH "$dirname/lib/$appname" $* fritzing-app-0.9.3b/FritzingInfo.plist000066400000000000000000000103171272363601300177710ustar00rootroot00000000000000 NSPrincipalClass NSApplication CFBundleExecutable Fritzing CFBundleGetInfoString Fritzing 0.9.3 CFBundleShortVersionString 0.9.3 CFBundleVersion bda4e CFBundleIconFile fritzing_icon.icns CFBundleIdentifier org.fritzing.Fritzing CFBundlePackageType APPL CFBundleSignature FRtz CFBundleDocumentTypes CFBundleTypeExtensions fz CFBundleTypeName Fritzing sketch CFBundleTypeIconFile mac_fz_icon.icns CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions fzz CFBundleTypeName Fritzing shareable sketch CFBundleTypeIconFile mac_fzz_icon.icns CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions fzb CFBundleTypeName Fritzing bin CFBundleTypeIconFile mac_fzb_icon.icns CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions fzp CFBundleTypeName Fritzing part CFBundleTypeIconFile mac_fzp_icon.icns CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions fzm CFBundleTypeName Fritzing module CFBundleTypeIconFile mac_fzm_icon.icns CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleTypeExtensions fzpz CFBundleTypeName Fritzing bundled part CFBundleTypeIconFile mac_fzpz_icon.icns CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML fritzing-app-0.9.3b/LICENSE.CC-BY-SA000066400000000000000000000503421272363601300164100ustar00rootroot00000000000000License Creative Commons Attribution-ShareAlike 3.0 Unported http://creativecommons.org/licenses/by-sa/3.0/legalcode THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; to Distribute and Publicly Perform the Work including as incorporated in Collections; and, to Distribute and Publicly Perform Adaptations. For the avoidance of doubt: Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 8. Miscellaneous Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. Creative Commons Notice Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. Creative Commons may be contacted at http://creativecommons.org/.fritzing-app-0.9.3b/LICENSE.GPL2000066400000000000000000000452721272363601300160240ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE The Qt GUI Toolkit is Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). Contact: Qt Software Information (qt-info@nokia.com) You may use, distribute and copy the Qt GUI Toolkit under the terms of GNU General Public License version 2, which is displayed below. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 St, 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 Library General Public License instead of this License. ------------------------------------------------------------------------- In addition, as a special exception, Trolltech gives permission to link the code of its release of Qt with the OpenSSL project's "OpenSSL" library (or modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must comply with the GNU General Public License version 2 or the GNU General Public License version 3 in all respects for all of the code used other than the "OpenSSL" code. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version of this file. fritzing-app-0.9.3b/LICENSE.GPL3000066400000000000000000001067001272363601300160170ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE The Qt GUI Toolkit is Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). Contact: Qt Software Information (qt-info@nokia.com) You may use, distribute and copy the Qt GUI Toolkit under the terms of GNU General Public License version 3, which is displayed below. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ------------------------------------------------------------------------- In addition, as a special exception, Trolltech gives permission to link the code of its release of Qt with the OpenSSL project's "OpenSSL" library (or modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must comply with the GNU General Public License version 2 or the GNU General Public License version 3 in all respects for all of the code used other than the "OpenSSL" code. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version of this file. fritzing-app-0.9.3b/control000066400000000000000000000017661272363601300157170ustar00rootroot00000000000000Source: fritzing Section: electronics Priority: optional Maintainer: Brendan Howell Build-Depends: libqt4-assistant, libqt4-dbus, libqt4-help, libqt4-network, libqt4-opengl, libqt4-script, libqt4-scripttools, libqt4-sql, libqt4-svg, libqt4-webkit, libqt4-xmlpatterns, libqt4-xml, libqtcore4, libqtgui4, libstdc++6, libc6 Standards-Version: 3.8.3 Homepage: http://fritzing.org Package: fritzing Architecture: any Depends: libqt4-dbus, libqt4-help, libqt4-network, libqt4-opengl, libqt4-sql, libqt4-svg, libqt4-webkit, libqt4-xmlpatterns, libqt4-xml, libqtcore4, libqtgui4 Description: Easy-to-use, electronic design software. Fritzing is an open source project designed to help one transition from a prototype to a finished project. Aimed at users who want to produce or document circuits and experiments, one starts by building a physical prototype, then recreating it with Fritzing’s graphical editor. From there one can generate a schematic, PCB artwork, and PCB production files. fritzing-app-0.9.3b/fritzing.appdata.xml000066400000000000000000000033651272363601300203000ustar00rootroot00000000000000 fritzing.desktop CC-BY-SA-3.0 GPL-3.0+ Fritzing Electronic Design Tool

Fritzing is an open-source electronic design tool focusing on makers, hobbyists, and everyone else interested in getting creative with interactive electronics. Document your electronic sketches, learn from example projects, produce your own PCBs, and share with the community. Fritzing differs from other tools through:

  • a real-world breadboard view that is hands-on and easy to understand
  • a visual component library including the Arduino, Raspberry Pi, SparkFun parts and many more
  • an intuitive user interface, with seamless switching between breadboard, schematic and pcb design

The lively community on fritzing.org exchanges projects and tips, and there is also a PCB service "Fritzing Fab" that produces user designs.

http://fritzing.org/static/img/fritzing-preview-bb.png http://fritzing.org/static/img/fritzing-preview-schem.png http://fritzing.org/static/img/fritzing-preview-pcb.png http://fritzing.org/static/img/fritzing-preview-welcome.png http://www.fritzing.org info_at_fritzing.org
fritzing-app-0.9.3b/fritzing.desktop000066400000000000000000000007511272363601300175340ustar00rootroot00000000000000[Desktop Entry] Version=0.9.3b Name=Fritzing GenericName=Fritzing Comment=Electronic Design Automation software Exec=Fritzing Icon=icons/fritzing_icon.png Terminal=false Type=Application Categories=Development;IDE;Electronics;EDA; X-SuSE-translate=false StartupNotify=true Categories=PCB; MimeType=application/x-fritzing-fz;application/x-fritzing-fzz;application/x-fritzing-fzp;application/x-fritzing-fzpz;application/x-fritzing-fzb;application/x-fritzing-fzbz;application/x-fritzing-fzm; fritzing-app-0.9.3b/fritzing.rc000066400000000000000000000032371272363601300164710ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "resources/system_icons/win/fritzing_icon.ico" #include #define VER_FILEVERSION 0,9,3,0 #define VER_FILEVERSION_STR "0.9.3b\0" #define VER_PRODUCTVERSION 0,9,3,0 #define VER_PRODUCTVERSION_STR "0.9.3b\0" #define VER_COMPANYNAME_STR "Fritzing" #define VER_FILEDESCRIPTION_STR "Fritzing" #define VER_INTERNALNAME_STR "Fritzing" #define VER_LEGALCOPYRIGHT_STR "Copyright � 2007-2016 Fritzing" #define VER_LEGALTRADEMARKS1_STR "All Rights Reserved" #define VER_LEGALTRADEMARKS2_STR VER_LEGALTRADEMARKS1_STR #define VER_ORIGINALFILENAME_STR "Fritzing.exe" #define VER_PRODUCTNAME_STR "Fritzing" #define VER_COMPANYDOMAIN_STR "fritzing.org" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION PRODUCTVERSION VER_PRODUCTVERSION BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "CompanyName", VER_COMPANYNAME_STR VALUE "FileDescription", VER_FILEDESCRIPTION_STR VALUE "FileVersion", VER_FILEVERSION_STR VALUE "InternalName", VER_INTERNALNAME_STR VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR VALUE "LegalTrademarks1", VER_LEGALTRADEMARKS1_STR VALUE "LegalTrademarks2", VER_LEGALTRADEMARKS2_STR VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR VALUE "ProductName", VER_PRODUCTNAME_STR VALUE "ProductVersion", VER_PRODUCTVERSION_STR END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END fritzing-app-0.9.3b/help/000077500000000000000000000000001272363601300152325ustar00rootroot00000000000000fritzing-app-0.9.3b/help/images/000077500000000000000000000000001272363601300164775ustar00rootroot00000000000000fritzing-app-0.9.3b/help/images/assoc1.PNG000066400000000000000000000442521272363601300202450ustar00rootroot00000000000000PNG  IHDR]dlasRGBgAMA a pHYsodH?IDATx^w]Em?tޜ^5!  5@ !-t*"bhtbC ,ۿ޵]k̙9IƵ¼ggy;KÄ`[sg\E"Tp>vϑσeHAwҥŋooyb"#mre/)jDD2e 2A LZԣ D2A7nn D D"(Q"FЍZN(]]dvre @Qˉ2e2A̎ZNr DA7j9Q tQ"vQˉZN(t#F-'@.@.2;j9Qˉ2e nݨD2 d`ԨQɰa2=GNF&EЍ .DM-jjk go[bŊuTדtem[hq\ec=裭 Ѽ QiAw8䓭Z2:f5AsIn'0KyTKtUWۭdoVM7!WOw6ky6tO&x`kgy&[Ұ'M}c=̘1ΑGM^xM^X'[o57|3]r=}7v>`g?k}uAQP CTronk@w}=m{fH~ݙ3gf0U~ӟ&W_}u~ɟvMUswvȜC;2gNҿN>U8S;vw\'xbp6hV{(@Zh?4.h<#G ҠBfmoϠ9ll{U`=[.gܣ0@xm<[A*t!N(;]w]Kկ\9Yv]wm㏷C{ 1ҷ[D-ЯܫK/%ÇwN;wk/97"oرcӃg . jV:T@\2 VЫN o[D,lDL\yA҆v„ ɘ1cPcĉ~d`H] ȘF~I'MW˗fwN0A'?IpI^et{3FYWt]nUO54ݣ:%X ;qQgE]`<݃Y(!H>˵^kEͮn}Q$&De$+`BDG*{?:(7)@7/˰7N; f064n^]B~DM>Y4YT >Z~SaΜ9-&I#!;b^u. ~_[@7VjsB{Yi% ߿GgY+&'xʳ~]LJ!RӍ.NbEc njDž Afփ.ǏfȆn6df^։CHpRQM\Bǰuڼ..Rݒ?UtyH uHczB.ZMwҥmLAUI$dJƄ~Sjj?CLYᡇԮfrJ'{z'|24/K/4{キ%z"/ W!Te5/o1xChX ëA~oP~8z&#Xdž\FG}3t 1N[mUe{a E7AWM@1MUON)V#2!TQMj".\UYif: m]Ja&p ^=$̆;U#H,)S\j݀'E5Uۣ9SWUut\&2?|+m=U:j[-@[JH%d'jDҺn]&d[y; " 4qp@PnRtAn3%_Z՘T$D𪄚O/bNmllTnShHC_<$D:#*}l yzl-Uh ]좧(YBL!?ͯutHuW/ \8w^duKHз,!0*etr{!|#f‰;nH$G,[kWT7AbA6]5v.oH/2/c`3?S#xit El3VvQ9P(}N_U#SNfFI>-]evI&!<ۨOTů~nVjL] .&b-9~=x]p*<BntaˬBn*jrD(C=i1W9n=PDWvQ9YvTlw4SIs9rdiA_I|LY9DiÜ!\I^ 4ڱ6X#dfʤ5fo4X"IjR@^oM& rvV:^(x/c%] Ƈ6Q]YB@ Uv\?_f\5zqAgֱ^*N"?ɺU-Jm*r1gYR.t^+R~)Qxɰ[uV 4"Ʀ~<J_Ew(YާEBԆ9ҋg~?|r p|;;!饀w}yZ.U-$Pv6j-t\NeB't;vw>(,̼-wܑ x\*fvwTxyG,󼇹!pxY\I3k?x6N!n:nvL|^Q4Ѕ(#`ex{ rY+i \IsV+4c3ܼK =3|K,Iqx\nҵ{y@}4z!hrϘ1gѢEcPu. euA*\ ʤV^ms(0eҌl\eL=]&y^rAeҌي͇j.Ϛ)v!u&9W7Kp,n0_fɩ)yUDr37}.\7V%~֬YV ]b+SҌQ]M qEp |\e8_L4/\,̼vX6 pq^vp8e2 _YԜ_N`~ t"4d.asR`A m&*WJn^Mp%:x{<+4^rC/+p/KؖI{p%sVJ/2@W|暌43Q>\I]ȍ蚀׀x{ y׀x{ y׀+ixmaSk2E~o]xKܺH?,o5:iw՘>nw' ^e_)n'O[IOXCv-F'1HxoPB$A6LJ;NLƤ1W!oi+hO!hLii~g9D-pܦ,hMiCs}`>f,dHez۱I}&d'D6|/2|H, '4fXaAs“uTҘYd)&y&M1.SB+BZSf:in4dz ݓc ټh^m*49Y o1 /vQs ^L zR&뛎 I)k6;g,[mbM5$կ&;^9+i6UlMctm]!6[#J̒beOG-nu!>p6h>g\N yec`ٴ[6\k o6m\޼c`[˷I5`.5ѬO|LxԿ?O S^"B61{ !Z^L 75Ѻp4NWs^-4Kt/H4g;gJs';Pn}S,ϡp5ƂʡO?`?Zp78 X5ۢ ^"L '2hn~jjR۠i94.[px$sO&''g&#ٱiKɜ _Ncy}?򑏴^T{ T8wpc M1eIpFta +^j_\>.YLETG#r>[#1Ӓ(lL8jjbS.F|f}|Cmh.sWoöYdRt<5}|d$ *?޻k. f]Z^ӱ6WnNع\6]oㅠ 6`cH_Ǟwa"qib/tYp8\Tc/ H^rtl9&&/D:qDe+quz LKMO?ǖI[ɤB}g:h)wK_t4n,Zhx#5>b_[ 3ZC"2QET_@3 U7҇T""OLMP`9ܓDIgN>T`ج4϶g.3E*MI>stx~J34OUE {6j+Ŝ5W(m!qrHp3QOKMGT t+}s2~(bKMk&!,M\hjB+db3O mxugvme҃}f!ǙƊm!ۋMI NLg !9.ޱ^+f!8yzf>>inۚf-de!6'Oo.lIecqIgc'̀)؄97 {oYЄB#r4 Y;nsQ҄Ol!6!,֠!;02!;^E3+"i6h[Լp56dO򸅠yR3Ma5lo(l6Хd>rQ28\bB6%xXh!9grq|`Z92:bH2@/E G؅40o Mfga"36[;«9{;PcHl 3) 6ѵ&yNicb/$tK6\ߩ͝447 v"Da9i&, wJrfXc5g@wOdf( VVUxhLB*6J-;XB1qDoQmN!bҝ .<ɅhV&Xht@ւ@h,0_eص፮m:4ӬfMu Y3[oK!5qm,1eAsFf]kOFR,4wG\=$VwWrUgNUЕ4Vb6Nk}hP1ٵMWۤOskYIOZmhbBSm"2{Bsp 4l,6 Fn Cm[9"4o! zN &uՈMXU/{f6Ox)vmסIy,|@ %ƶYoxm/ۘww 8dqZQ{o]Hw{]쮡Na"m>+'dc,h7 }&waDkxy!{MLtN0 gHjWe.M#+bnGŐ1 8yi|lf};Ms Z]֮ԉyaᝧfuXsT>nLhZ&< K#1 )N3C!ikSXh-: Cxg`ّnC m45lr{(]B,*N]"/뤓kYxB*&8լ>YxjQ!I0K!69؈I*G#Z" Ǭs 6 -$ᛡkj/  @.)f1%J /ed^h3bMtYD54#QJ4OqA@x .SQ"0L+{eݐE Ϫ77y᧘dy&w?r_r5$Exۖ{mz#- ~*GAH}-ءA+`P1d]X 8"/ľRJbvF9޺Q!жR,N&vYwEAsQ.NaYL=٪OAqv?u|W%oNZCK>T!ȇ>w]??,[o&|W ̕k3 :AX9ZcMEyMr6\Y'GQ]n~*\ _YPe(8dgI. 9MYO;O1iͮI;6]yAv9^"'V0plF&{f38x+9\daKnCbj YX>>Юb2Vp8,k"n4&_W\n17yKDuly3p2LKjdkj7-o]7Ic@L/fXK$Ȗ i{mќNx rn(1aFZ$͎R30YQaa"za%T4=`ia7Q"u[y?2rD\5pٖ^O։~lC*1̭.d 1Ѯ:.\ɑa h.kؤJi}HZ"^H4)p[e1."5V@"I.canh(Rn^$Uӵ.L pH5+[,޵ju"bVysdYmxh[Vɼ4S4ߖVD>@7-YviG2ux;<\c꒩b ݣJ0a q&'"MjG|ckZ Y2xXڣ†2)}d)W3Ϻ(Ry YΙ㎷бG ǥa*TPUl3&d]t,BsjL3w94c;.SOߌ15z.=ChVPڬ\"Igu,Gfw$3UTrя}4FrDQBR1jȨռ,r ܐ? tWY r7H/7oS0䜋& զoEǁ!MHgQ%^xaonvRu,c\s4KixGmQ m| 9쳏fTnSBJ6Y\'=D8(4OSXzur!숍nYaNk,f!+BGqD½h"Z.;ilp8lLq>k)?xl9 6! k֠ !#B΃؟kv϶Ҝ|{:N."ʫ$NZ?Ae8 \ib͟?:f'貨N3 kmmh1Ӆx1tN)_iSO=;ОsTֵV>{D:ꨣvu)s|uN! &B;蠃:… 4tς'.K78c廍CpۮBd6`B[;U=ua.|+\sO͍z'C=tAjD!zΞ-Jلk!7YM^>裍;2?k\>3z37Ϝ)6>sNEoK)%vyg+= X.v}s|s,lTYp;ąۭ9pBCl@U9-*-lfm_|fXdI{}JsmRұ_8L"t}駭$ZAonEF6ՙj] 'm{ pAstg ^pMm{^@lAڣqb]l)<%Y $}T! TfYM^.oxww%hf޲ҌrqᇷY!/s}AsMB|AS./r3r1}lF`^, Ќ\g,k& h"+hƿIH{pi_җ.}o"d}׬Uӕn$ D|`5ޜtf\a-xs oNC \=c iyT \NS6|ǛSN9ZFx|TG~k\Jx320  p;4oV ^D>.{V ^,Bݬ+%ꫯni{ .% pQ~wx>Gȑ#[rd]%,7xnƻ9zMF-,H\7/xcƛpC4^ibW\+I7 6@b+N;;046\dL x1ImfL|*`.S;e]V*09>iUW]U*_5O.|K%(S6XP~4ۀ9X*͗^zi{ǥj9g\d 9TÕ#22!$DUam!mܹABDpC4޼tdy/ hY]mo:p}E^pmo:&*%t|H/A&9m a|>OulNWt{ yp^o5\Ier}WnV^mվ9Pܶ :4!SՌqӾ0J%D$dea &S05u]]q\&/| ]W*D袋JYg|<h!}U[iƋ(h!GP>s=4\G}e*^|(7ЕwEPXT MT}p]M>XTY鳵B3h.Ge/9o+4S (JmWeV탵8_E=֋!gh*[Qo'>Ҟ;!Lf͊WיJ$M3ќDeZ39$H<(S6~))X6/B|L.&l`Nh;CʤYPZBPL޲ifmCi Ik[]{xKݼҒ_/4@1@GGcD,vZu<*Iޥ24\!"wI8&7//).iee>GTPN4Tz*ij&L(M6q߾Pt(k3tt]AOi$$3Щ>w \4B-Bs@WqtuzL5 |s 5Z:Z,J?4cǶi+P_AW $ =#hBmХPX&aI{EL<Ȋކp٦۠˷Mz56/b۸ >`[Q&ݑJ:n.i:T僌G C{]\Axxn^[63j^[un@nӍn5BAwS*jf"zA=-FMu~/-zA@A7/|k_tꜪ4 yᤓNr!Bێ4yXPyA//:7kU7/PC}0We^uμOMlMK[:0{m$1R@O}O<6a8s*Y/wgt|*zCc(:U_KE)XOAظAe>?O~|+e&o:L:2"&q^ ۑ6fSFU4GUG4P(ZҽW_T6̝ lm4ю2~fjs^BoMq3gN7Jׂ j"TgE8`1V]TR5'F O6.Sz\pa MB0F@[!-_zSQV?U`1)ZMZ"#;LsuJ3M*\:BCֶ|[iSpe5E9tWd޾U3x*c\e.[_EVt`#-4Pa0e=#Jocn*oUQP;h@=zt43^Hݼd˜$E. ѨtQO]fAkU 8qi(|Ƭg9&K߬4dmbQfqY*0)wQ`վewG;U 7kG=f1Fӆx2'Ixfb\МB"Qck#W+iBEr6GM2f :T<'}p2j/woᆮ\/.{+n06+v|3ێ[5&tS٧eI VZT/DŽ6] 87L* (8E `47*aYs׍~^+6Ute9*p1j&ȰIq{+ӦMK.%Kҋ{@FYc-Q6T8Z7Sw5(DdptTc`CWU4Dl!'$Gwշr`}5 C0LEӀo\*RĻ^P7Z2eU-8 %mQvY7!xFiFlU/R6$U=鉿얖-}sZ}'"Uǜ΁ގ<"g䡫602/P>SRd@-u0}6=&&;Y^򽇭uF]^CX1蠫 ];sρ3l /S]N}<)wݹʜC^C1&Eb+UVN/<`Zq)n}{E)˱;"q٘AX/:)tz{ȠS.nK %GU&e_zj=o<9GVeBzwU9a(4NwV]EfeCJYPuDZ>_Ӏ, 8Ԫ2$J03.^وr: \`>ʖIHjTtmWcfZeԅ}t|F?샅A*io[s )4ag#&EWXPu-Ɋ=<܂~뭷f#7Wt8W pkbrbboTnX{jshg h gRdtm[D: 9PL+ +u1HY"meo(R9^RIJA&y'X\i#AW:פz•NApcwQE, tіB­2rA.&^g6b> 6:+83}d U#>TgYhW5«k# 2իuc77o<:7@@7P(MQ$8}Tx_1֘kbqc')(]۲~/ tٱmzVAD-KC=PLߘp56)N#7*Gw߿]e  ؐ,fޥҥK+/yAt:sNo(nK7m(ILË@ Ƕ1݋L0ѯA<蠃$øQ.qTFAݽ S#x+@qA(17B|b7h\MEKC~L$ wvk 2.j5SK]O"~'LCyE OX$KT)M7irYPxMA)!^L~`8Bm3 "U1fU7Aoվ*>)Iz Z;:wѱ~F=t{: !+  F & yVY@BÖO{ў+ k ml)OhM 6`\zc߷q{l=Dpr a`R4RPy[[d 0-2te @e n9sT DЍ(Q(A˕/|(rerrV'޲GnC_ǟpBX\gnˀt< J&o鎷X*Qq/Tw˷9n3yˆߋ2W ARg"6U^%(R0jq!TbQ%K1 JZ0k [U5a; .E'n)m{ݼ; w w8QZy̟p{|s0\)>'3A&Ϝp{hbK\ȃ!99j.Utg_8Wq ^t}I 9@~^xE}\o̎F2eDya׸+.(QL2]~3Qˍ(QRZK`x!izmQO(mIu( vtsF2e_e _,IENDB`fritzing-app-0.9.3b/help/images/bus1.png000066400000000000000000002677561272363601300201060ustar00rootroot00000000000000PNG  IHDRF~;bKGD pHYsodIDATxw|E+J 'BDE@i`«*v ( Ch^ەݝ{K! >34V/)*,..,.*,(F`0 oF۸qc7wwuqQaAkW&L`EBuA_U[b2i:Uk SSԷ {08Uh'$%EU*USbpOOɓ'^ j0oOҚUʌrЏ[^h… _xq|``E’~^:gΜ\zuÆ f-n˵6N@PX &8%ڒ$DO@8ZR=厹q Bgk["Tnt7Z{ߋ ZX!mݞ29uejg_~e [mv!rͦ q2bN"hMa96doOq\AVKv9#$߀kTpW+d- r?$cہq(ЬF k1$ŒcH,c}:`9H E }+Wd.dm|72{F!_ 5l[2 !-Jne-h%~d1WkWx4޻|ӜAӴA_}lo>ˣjQ d9$}]2y_V[,+%089 l ?ى X{SRNSgOit rih4JM]iid2L&̞}IΧߦu܀~i4 (ʨ\s+iV h@WnSttHoYu ,g0E1<]6}􅋖 o>qLÎ";2@lIV_^2@Ռğ[67XabIG,ظm?ogտE|d^o g 8^abV~:<ds2ET(&#0_uC~ 6^W,!꾔PɧkF CV_ 懘L& x ,:(bh4 dΝO2͈Sz Hid4!\(XӱeH.WҳzN SM%ݫ<&3yWQ&:6?WC,*=qx4P$рg'Pi&#EzW<@6!IRR:qțn"2_$p{ލ$cXQe+yS2)dU:^8λxr91GQmp9H~`xw&;@d>fee%''܊ Y )ξ Qz={py~D.… EQTѵ%n/ÏGF۽_u>vnMS|"/7"'n}=bOFl802E>QGo-}&Jfm5>cEI yROo4B7/; ޼q\=_xg3\dx:˿$IAXSOtZ5~1sV KBť'vO0%kG<6xY Zj0EǒXvi6jJ "KNB.Zon+JbS^'ٞlsZu5 l; H/r|XOI:&Bl dNaf Svg3]qvz8'ܥ9l4e4 KcH 10DtϻyXE4ӯBC0uё 44ϤN!I ::!|{!{-] 4era2zLp@VeI$Y]0qڹ75md#|) I/T7fpgFFE|!N8罖/x6ڧ"ILlI*ֲD'*#1e1*޼_:7 |?D9HX J =Fi6?5%&\4rQx+߱snvOg B$!Ѩ<pP^e'8hԸp}5>wʭ@1t2Hj:WB='v?{@dq&xb֜w Sq?5A AI-H:]Tx$A$K X;g;l͜f͜+.հҩK vRү3&nahV#8{䥼ERKY3 N~ý6vڊ#`SI|(Ԝgg6_d4rγ7_802*fpgGEG񫝰ث Hoe{BiLW_,1 SzO>0#+^r̝ݺuh Aj¾^'=pg`w[g[+IC "gކ^ZjU?SfSVK?w""2!dm7n͚5g0#*4"HTV: 0$͇` x!{Y_67s^TG0`Y Ey)cRADzqjtNNA0Tyh@Zpt?s.}'#=*<2* y'>=qz?Hgx1<|8:mc~9oNr/m^g*{pHyudҕ7ߤMmJ}l$\yٴQ7vGׂ d;7B,M;{VG~ewG䟚7f> |#j,?^v^^(8$hnުҾ+K3_rw[ C=A?j|ys8dm 査ћ"tbSaaf5ėY3X e̓z6"sSR]p~_`fc )>lAefg֯/zgo7^6x<7 _7w&zޚC/~ ^>>nV껋~ieN&Dхo|/oܼ v/͝/~8kW_{X4&%?Bxq|fp(@y%Ժ4Kɾ9qk`f3YLk#>PKn7O~ASfnV0b@YB"5Wna/|kݾ}+0h+}ǰk<̾W,_#Sn)AAwB8}jqɡS?ak0v eꁬy'^:.em(NVk'NIY=OdUK4J& $HZ ƉI׍T X*BVy;Pfbkq%mr9fr]`pu2"ΞC@"RŘ݀jn $7& ҪIsYr/O_ZAeZنueW4>_M Z:Gx;rT4D%N,Y0: >KH(rĊז؀;#r?f7]Mrn~߼{ z|Mb}숴M&/5i{g"v;l驵tB`Ңt{vhRB^ #_$+]Bg c߱^cwu7O?y 3e/g d8(e[o@º/\́/pAaç:( NuG2Zl:+<[f 9nw,?WqDYc mJ?B;qdwZq'ӻ/XJ>Nq;_P@ha]N1.r[{ݼ)PO:_|qE敵 -̷4Dl y8(Yu ;>t| !-[ v ̄d4$dLS`aÞS$ӹ9eH tR9?U* RJ j>,40̺V9JVkIVFX95 @ H* @j`! AP8 +rvcg7@(D(D@ vxP V.jV4*LF}ETd?⟇(ʤѸ1*qaot+_4.I8|-sl4JsBJ-#gO[2i_ix77O8?]75?4ͬ6.]{}PCv"!>>\^N^=FBw!qE`#n?k||`ET\s=!ɋ#Y[㯌 J/hKx\F`6/*heQ%DpuCnVF 궽 c :YBByUheG /~Ẅ́_Q/k)ʸ`I'tК5*P嵀rd AȠf;S+ù\rElVs,Ec=G]t>jƭԇWq#pZt  Ձ芋c' 6b8ÄrNh<==+7 < FVkjN<(A2j4NjË0&]G" TyWv0[-UH˒%$ cO Tn6iS&z6n9]+F=ӝ[#T!dS#w⣗.Vy`+7|ˉusov5mDx4 cס)}KA ]us?;2BIah<FM/< ,\ 3 p+g 3/Mҷ<^{gV $04x~f2_WF}{O ׃w PqD^=.`FAkoVu<[9ű3N/  Nǵ_1:Jeh)9Ø3E ?uΘMf5fxw"!h':~wSG'I [o:T/A{+:S>[^Ef|7c[,}0LPޚ=a]ki4M@ b#~[f|)sLkxU> B=zw9k@'s zm0Y;_o\ALQY&xn̘5~圷TOZ{0b^M;ǽ3=K 4=RR"&vfo:Wy m >rEm.puK{Dy|ߕ\reرf2<;h2Jv=ss4iܨC6Z8;i= @lyn & H뉰jϋuҙЈz㹴̿֯ǟ^*wJJƍl}'lZl9fωr.3hygȋO٪1:'#J~3.WfJKi(O`f@ - C*[  ~P s ]!@?'cGl=E4e0i!s !Xu~uEC®UPR`ے [rU~E/M,"6ԸHj΄E' n)(.ʺyLJxx!>u,6m$P2\쑵[?2H!piј~쌡Y܄P9t_tpXxI.³Ƈ]DIjWB/oɈ))L`M |^JO3Vos%Ƿ곣<>/W#Uh-'J:v?OQge& -FbSO&Y_P@E:U~goxG6$ 8q񕄀]&Jb) ;^hڦ;Gբ77c v-: ($-$)yyykI&w%1𙢤&8;U*vk_oɟ iC8KzQIuJ2IhQ®ήήNI"1AhY~>$D $mƅݯ%)'!֜rjJ#jB2h;Sh!(l82,2Dsc뫈l ɆK ͋f%*d-[>0q nTU>AW~r q~F\abG]e-FƁ%(XWM8|v$+l4wGmm1(d$ű$/jV2y i*ʡuHF%dN:}Sۭ"x $:GOOT2GCo̱~)6ĉ=փA 8RR 9WHKo JJuZUg( ץci߾N{țe %hvcɿc=!QyJVrj[5fH!uNj M'!/<ٚUN:X6:>l qWIRqK:ܞ2?@)v%ՑtpYq٤sۼys-`0 #G߿ت6oޜ|u`0 XѾ}'|Rns!w`0 Y)@=_{?I;?N_Gbx׆z녟{NR{?AOk{AO̴$Kpݻaا@0W-"˩ZK8 7X#H _FD S_,=5?zV )^nOgTjtԪVjh#Jٺdb5jW`0mI8Nfz~j] ctAjj>AO2+5Nm3ܓVg.N4}RS>7Ay_ֹW9#yp+<4 z? ;n@'GY7qm׏z!Za]g&1?zj8?zX WKqpߨ|eJɪQf~`0  ,6Wۖa>͞ڽLt˰<jmO/ cCD6:+>BY9Yh|.{X`9.}UGpNoK8WKO:p{:%PJɫ`0 lܛfǫw\=~^@fvqvDIGKWHt.U3#WIoZsrFp >߽iŵ}m&8|fW>(\k@O:EJ\ӗL2U!`0'70猓K';Y:~xh|8#އvFc]-Jn?;!gUUt'jBK^$Ѱ+RcC9޳:ɳ*F~g{Wr|pzK`Q[d&lޔn.UEѫ'/.DmU;LRC.D@ V`0`ڵk;Գgϗ_~yCu6`0 o퀾]V^}1.g 1չ.~F@e]煭uRS[U1 t\jVZFvia*P}db0 ӠX: `0Lgv1ũk1 `0<֙S+`0 _:i|J]`0 Cz]:$Nֵu@lT7X`0̎rǟ}Y]`jޙV=TfI]`00uyl.bxr=-U`0GƋM: >s=@(V.8Ό)uB ͂edsaDQ ؤ`+,,u688Օ~HfffXX8<׵ 5ˣZz[z.QU#60CNNMӶfggp?]VQQ!i:===""KykW ytׯ;ڀH=ylu;sBǎN^ M|m@$?16{q=6U?wd =6{iEq1EϞ==<|9OO.]8;; c6 kʎW]ͦ^˟`qؕ\^W9h'Gu=vO&Q]o!NG8MȾ=1n??ZB~HsY O|z}Y`(by:;}{5v%֘eү@`f}r~H=%]BƬ7(.?͞Ta嵾qV']vyĔwZ7o{mE\*U`ι'u_jb#{=!wfJ[y0+\\@&c[J}'8, <,իW^z60=zr02^zj;Gp[a[WF\lWۡz3 rHp.AC{3)7 t0w X//2v}xxzzF{.榾Ox"&zlEz[Z:Nͤ.a mZEj*)(t(-#56)z, t OPV\\zd߹g aˀPy0_qq1@^Oӗ-!EQr2Ϭ˗`͚Fk,T-T[RSOy;sIدF kVW0OfmsBt-y^ύ )ͯl5MCEHPәW.J1?s&\^Y :8aes7򋗅n"؍uQڻ@:/Н'"9aZ2O,w)_3yY-Eq1'33333Y!dgggggsg~~~Am5!؎˻6쉯vLkcI>=# &Jt!p} a6Ӵ'7n(84S=N_9r=7~\= ;fqyUhh>pN sbm-dү rIVus 0 4H:shB)`B~cO?oGl~US3%Qb}b0ux`lҤI~~~ii)_$IUv :TXp Owee,!ilucY"?ۗ%D:T\RѳY3'DJO*,O=eqpǙ{>:'=Z"b8RJ<[SzOcTBZ$#Y@Kyk?v:pqug6>^^^5|}}u:@LK +}~EOOBӅ!Irl( )(X+>ȓОe`o YF@8s3tܫ1M~ mGn2/:+}U8+0zC؉˥.%;g<UY.h"?-mJ86NPia<7賳cS_cu_|kݙZsAjsUԘA}=lCU(*K~ugqAlR\E}kTb@vUg:b0ݻ|uk֭[ǭY׫WW^yeؗδ.Co<9//|bh-?koXwjUrdN>QJ N"uˎrljO׵: ̝UV;>l\\\ MxTWvhP^dzDG?i5m{8bɅn Hs VH4qڐ2w=Ԣf]aDf??[TjWl=GS.Z-l=Q`UθJrxc۽X?pYlMRjԓ5pjeյ"UK`%8O\5f>rIsssْg+=v1P^ߨQ#:`0''Z7 `0RƛX: `0Loɍk=1 `06apr{b{`0 A +{`0 i(H`0L=GR$0 Mu yW yD`Ν;$| h 0&8; [T*Jj֬Nh98&F%vu30 쳻@V+WA+Xy 8$OӴ`h4 M&dR44M;T9uW18`pJƃG^8$yGpsf;@U@Vt9}=8SvL}ԤT?$ n@NZj$:Na777Vr)W˳L&h4*7E(h4 8;;+gW@urrR(hc B*ZB...ʵxBlʕxlSOn<=Iꐜޝ2|g}B]k%MK'o] `{A%~`IG$<`0"`9.a9fN[0xr\V 0pSD! XM'.zH$/n`eVoXӆb~S6<{6/;m5IKwL^KvirKf5R*5|m$/}ʁB6ɒs3'~#[asw [I+Su^yX%imBZN,9Vƹr*ph5"SqL 8V \_m[94_WŠ(CYFn,R >JJ^:mEF-RVd{T3o6l($!h[Xo_ζ 1y0-0)m; yr-\>{ g.΁Csngu{ ]}fC*bskm*g̽9f΅[Nٖ-9f"{a۰Bٶx +X+ٲu*+0gMvkv[QB%o `ŝH+u K9G< !-]XaIn:W4@8(K_s  .D44vVܤ {ĭ[h NlV6JWEm 1aGW H0%Ox OX2i洧 =VR~!+C{jQ;|gfb`+&d9cBs' 0tTSl"qY`!7Ay,Q~|rţ1|S+-5 ^)RpwRUXaxJ7]u٘m;5ly{{lܸY޽+2zygZRRELZJQR(..vh)"FmaaBaaJKK=<<4]^^Pס`ׁSEEECP8ZĤġu(j ===&<$ƣI˖`AF}͒3 L $&>PDŽޝ=K08xv9WG̝UV;vy晸8x]: `m3qKjõřOeW`]*Gu`0 a0dC  S,:/#vJ+V`0 +H܉ `0 |V<`0 S'W+t[b0 `?3^%-9ɤog2| *Jvi7-ZV^004IVGɫT*E9`(!}(R(̾˳oe&Qyƾ/žpr'L&FSOk##_ۍ'Ch< 됋RZ QeD<*g2 #~&(_jZQyaZ ] աhT*0 (i˛L&Z\^V;Z,PZ,A 6CK 4jxBU>n:5?͹Kn )+/?{0N9=Yߤc԰CQ0 QBjijMfue FʐкN׵FE8;'n-@&T$E: <ߔX/Bs ⌐un9K\1 S{ {>EW K`0Av0CMQ^XR$0 A`0Cv=%0FR_e-)6%'9j*$/iyPϱ mZ\ΝITRz1 hl3\LbFdjg#?}1vH^0r֬8KJJje!=i+&r`!/-YJR5kָqɓ'/_, 'Τ4l2:^D>}c}=<;Q=ߢE6m.\pPS9a5][u  IwL&I;DWぷ~3K<~_Og8 0yU=>Ov_jZjx5oq椦V^Kh1rp+)~Hظ2Zit|S;-F#g -6^^)[F6K eTu0&L0jԨXooogggNwС^z^:n=0 >\oYPRjuo<lG{}ҿė"yрv~hc)#lێ|E'':Ϛ| eSgMHٲ<)/WSTr.fה-K49kr%-_ o?up_f >K8bYyI˗rf%0e˼S F+e.xiz)V"^:ΞV`jw‚|Ng@N@S,` Āh_Oc'沒1bC{ŷY/e~r'[4{p~^vM}c3! M-qS87l7_K+|yq[yewM2F;^ifIZ#J9`0Àx:_養$==[7 `$^ njsQOHl K s3h>^ID֧L4o~[w6vZpN`FTЉhtȣXyvqo4ϬBaai!yd45r)R^,sٚw2m jj!k p㑗!47\˛q#6̓.^NmB+$L+g:4A_Hjf-"6f|{ݧX {LM$?vVD޵[/4\ߧ~SM[Do%K:t7K#zn|7*Sk8楫y#;e)R:MAԪCY?XYy/*]fʩUyaGvcpb*)ƃG^!4r}0چntN`l瘦W}̈?o"ej]6  jV[K t:\\\?XM&C3 \}(7L*J2G3/kف!:BE=)o* FI=iA bPyiahWxyzܽwt.5̅t  ^U"KV:qBQ t'=c_IC pҽ(\* 4pb5h4QE34Mzfq/D+?< 8}[>Xz2Z5F ׵.Iٟ\X&ohbs4P4eF bAmgt %jjߨ`jC͸@1 `0&E`0  w vhkE `0L]٬`0L\:e`0o;0` b>c A1G^3aa{`01<})gҧSv0yWnOtz + nXg60K'.ߪSjaړZP`0jn_[%z^Q+f!y FQ(o2FJR(o4)rrQ^9z$IT10 6 $O6n<2<>IBQ(u׹²F{:phzweܹQl05;)&qٍbXX%<&t1 ͭ:L3a'<|{N{7=Axr`0ih?T׊4캠"ɑO>φcg2bϊ'/|2Iƨ~rTئ<ح(LEQ4:|GI:N@IIɵWCBl%0Zj UqT(NV۝:ulݪcDu}01Lms)_ijWX׳LJSүg>;Ŝ`~ݛٔ<.smjϨ`ޅ$I Hgf;0LYYWl a26m߲۶1ϯMC&!H}G7Y3NN<[o4Lo~`Moi~uKc)ic7>v@4@$4}be۷_혾L& U&aaaGϓf^6;^ىld{|||<~}3(Y"B%%~5չ\XXXXXO"ztјhsE9QCVg <ܷ"I" @$͐%a0[qfZŌ~q?j{߮2ݦz*$8[0/fʎgSp,~ajpVJ+X##h pB/כYY))))))@d*aHB,#79޽{OoN:)m߼u=vwwܩh%zXh+5ItLޡ~.\`79sk&MQ㬦 z: Ff4ܜʕ7:h N_禭M}hFeٌơZeT._J<ĤVE$M)OܤuhyG@V6:TeH(Ð}; __uKDqZ&`^L̔XX?/1@xjǔGy yaFZs\v->>:޾sK $)! KmXl+ÜvZu0a~o{*5_o1;r-W&¡ Gt(}<BF,qm$Ψq-+ O2it&N\QH8xrH3bJVl*cA^b4 PqكaKFOOBSS7EK`C 1 80|gUvB e+moe Bݿ_-o#r!}!$A=I: %m)z0;gZ&Jb}ZQ>A;G:'BEɹ!PH{WXUw֧]\ l .AӋ ԸX:$.Ch(== M)"T"E%R^4E)ȯYUAoZٵX|lKyJ06|}||KQU^^f4J6n,ە'߿MQTYYEI89Ho,!(*))4,j_#|qW6L%%WOOO<0z\SŅBHU*_RIs0z>>b}ʌFB}h.++5=Xʧ(D0RT4͈%V]\uvv'0h,--wssswOӴLcj|f6A}5c0gs\]],xxxuAPSv qq AA`9M vE-'1WNvBrg#>> MNdo_nK$]\\> II!77wV7n" +Ԩ.1G\9*Eb@!h%+U"gggVb3NN]ތ Fu&wrr>.J]uuu$٤ITdchm\\'X 77-%+㏔;g;U`0zɤtNА(Bd2$<=m-ϯEW]cś4nܬY zG1! :i-ܺ~TVdHHhSRCdj@/8Xwnlj(:++ؼys[[ ] ]\DnR;wu;T*н{w[ʫ՚p[h4$$OOOwFsR}@ qwwџ$U7"!!&&$U.^o}6?UV2ˢ|u:tuq ξ-p4v͖Z7=_,1L _xmpsEɤ'ADf"##KKK]\\Ď^v;;(6 IdddHHHqqN3;]DjaFɁ),tLdD`!@EF!$0@^~X^~MyJ萼^wyggg׬>+/76+Xc&ɡF;zq1 D`]:h&Nw" ?tFprrB׮]"),,<{\zFF6mĝt׷oD cxZ9ʧmc/ZNuLΖ T=t,gU !M dB/\|ݭߋ&%=x@^ 𳮲=Q.U&0G:uq g `:|Mhq KW5.]|<Eߢb޺O+v4.1 i&/XS)mrt( MӧOfNYL5pt = `0L=ttEEr#BVSV\ l4ynUyv+:[>>>zf @yy|BWV,LDlllVs$ٮ]:i6WS} ]lMJkaÆ M&!:^[!|0 v(DRj ~~~58˂2!>>M6@al]\$t"XD1$& wo ]\~ f>UTTԷ>1L=sś˪T*ۡTz%oUyZZzzdddm2S~5v) oTt~0NW\\,6D<<<$vh4ŢNg'V-nԨI911jD+A7V$(jWK V6a (twrrbQXNNNEE5>ݽQFbyV+8\ш/.4nXr}>[I& Iʓ$,y$)2L|qN'yqy5tf~i#B<I^;O\ N.#, e&ZGjuɳʨjZTwܩr\PPPSr,pT W[''sKih4oSta+}V__Үwt˻Q<}`#,+;I|_I2|vF:ZG|qu:k(wvv4l0Lpww񆊳l#j]bAs1H2浕yeػyΐ1!lGbv?y2O(5=6`;OL:̣x{5{ Vu8ztW\+ wvl9`SmOd# 'C/]N;AW$P g_x3 㐼Է5:@%,/'x9=yB5<[w+, mdÞ㓸ƂK1[w,[6= MS_ΙGt@ɰ.֚҉w~|*\ͤ2V^J^-YEJ}ԏ -Ld)ic7r$v1ӓt1 bYnV҉ {!IfIMӫ%6ykŌB]ͷ]*2802EM\T4m95ZR^F:|&(9?l S1̜gB_]LgñXyaԺ.$Sz]|Vvx8e;jKœv;ԯ{cW'5ALer d/;lqiFш!I  a̍J\j`0uFDqZ(cJ̌Ȏm]GPl%b0 u@P`WV{8 'S$Z;>rYGbؤ`ޝܟ=6=c3b]@ӣr3.?\LގchCuy3gՖoq{QƘZe|ȼ`0ugɒ̼˰9 Fl7s3!B-&%9:I788!%z+iTd`W'ͱ;yeU~n>.0U9!qATV&ӍWd t+3*.Өۇ6vqtAFO27'mǰ&NɌ;JWk皲CD Hrc?`0Lz=xyyhҥKʣtv u#:56#0VtLgno8^5әTai>='W$]ר_5|GӲ|ͯoޕ&n.م%_?{Mمr%hU={%_ ̊m`%?2zlR]w^xoA(/u_#i +t=|~g=RViǰ?>-לW)AiQ|sr/,˻:iD@^Lu+);GU`8I8s,a\WҹS'''`.s{jBʈlխP/O:4p5 ܶOf͈'f'vkE5``E >˾v^X|v!}f'OuOioZ6v_y?_}d6y Wc9U>nS"ү;XMl;m].lcX>vvMSZ<*T lķcX淗6֮I`05g6Rpt LjҹXHHH=\]b{]WmT$ QWkEǧgt˖7Şikn#*Eiժowg }]W{tak9k]"ݰ?Ék !ư+onثBK1 $߼.ˆ S#m\kó0hLj ːVvvvvv~~>cj4P8ŕ!R77~f}eourHsD1C[Ŕϯ[R]-3qcv}DA7r}sMw_TCZ#aZWV|fFMGI;a]՚7o]Ěh%zN^>^~``/E%M<\y  K8o U5f'v| 24m(׳nŲM42H?O.9|VFXV2v8rmd̀w֜0J}^+1kDoqj |E͝B&]yyj5eϏRyajUW):gގ%%%...b ААaJKM&A:N޺EE:$RUTTZJ#m19WT֐I);5otaK oA6ɭp1Rg|[Q^{ﺒZ#ݸ{^a76tLUv4tD:j' =EoYǰM mJ&|dIkiKc$:sނ{Un4)*/3BYߑ qs~Lihd Ww`B5pѪfWY|ߒ66O/%kWYдoc{fF>3θmR?zwnE`M~LaDCϡ, o9999jtwDi޹s'%NjbMQe.м !FEf,v*|rG."}uSou˶bptp6<x$xUf^Ⲷ!'2I^T Y}gѨ$Fj; <0} _+#xZͷٗ/&~dלⲱ{,;wLHNQkweڛŴA}`]X:y.;A y\mR;E`0K;ig@U/&@~_vP"U(Yp\_?ӛ uiԁn/wmrVpmVc5-0xy%CF^Is)M~yPT*;ag"& A/$;GWHS|D ]?1 aR2+F!FE!>m01ۻn;#oTVQn;|RbsǵfkSh.IWMc鷏ߖYBK]՜KDi'J`"LuCxֽ_U.vn/8A*4nЬHRoz۔3zh_G8;?tv+C~mAC~CE*4*y-?]Onr/|I WT0M!ufIDAT]H1x/r2;b0J֭[YY{׼yةSֈI' f͔gi(Iv"a&Mr<T3&Mdg[SQ4Yk&]ڒ `o,/J/>o{~}#&JӨm7w(فYIӥwLԞmo@mjwn|Ou׼DBਉP8B@!Ǯfꕉ2}hP :c^[WX[K Sv,fa'v?#͚zSaS44a;֔,7tu] AngFbciƍ^^^-7$ra'CbzHir@<&wn t-1Ĥ!:t&-\u"kN1!|QJ ,` :WXȆ='qٍ]oӓZ6t.qdt= yiիWi&..X/$xmph$iڤ0ߚ@@D֭"#\]]_@ 7kZTXK ϟARQd"[!Iˢh[+>4馡MSZ($^g{u^|1"vva 4]Zޫ+%:N5v9=hEb0˖]T$9ID?9댭;%ʸR_:hEa05 awѬYm@QѴM<PeFp4ld8RZ0\O_o6p ?uFnͯ8 0ANmg'bڭSnq jHcl1%fFvaJE$b㦸r5bqkL5zn-w\+ Ci^_)7m$Azh@4*WZmczDk x=vJK md'[D=uja?ȹpڈ.yM.U"2c}>D~POK&s r{~N#3|]w)-?]UTa4ls_}&; ZZt#/!IhzW]r֨s/CXNS]z7r~>_XM<<78gz/;z^~̌~q?j{߮}jl8?ˆxnO슩KX&1x_dbBPZZzNo_˲c > N\^pru z^i;v-yebp3^uZϤl 1hS ]4ͥo5>(/ V(Nn1Ffޜ! &JN̹WRŰ^o,0@ Į; $hM$+LqV[a2/X3[.y:; iӱZO.|ݜ|X?|5onw~>lЙ ֠~̉#Txz@ d}p;lu# sDҤZޖ}[ .pjSw9~a/<0w($)rw tx`2@oLZ\8՗A:ʵ{#G'|^|p[DScK- ?]?'I*ѮV~ % o:`N+ݿ]7ĥ7;96v~_l_y EQIW`M|N|L 3E_NhExSvg?uU2'bB6\"ğqRz1qs{DŽ蟣ݬϮ:t Uj9'vf3\j(7R0s`3\+-wRm5I.3k-l/)ukv/vԪUܝjw_wfeeYvwxyyEGGi]ڇdI\2a`$4:H<|j7!T9N TRs@*Fb7 #F0*Z@wxٹ%=GPMl|; en`Fe M^NH!7&|wcoē,$sQ7kicr}~y_0Qэ.y%{sE>a=gf'˓Iϵo6n/a{:~빷68qGR>E>~W/-2qgN~YYY;wak^0>FcaaQZڍ0_X`0"y+g5L,Zcj ddИLa35~>a6K5{ں6|5SR>;gx[e֫Y%M&nك9N6Le% -N߼IVN59MX׵Xgt_tE1̶7n]w_zxkvH!-'vmM:okLW}23&NCj_OI4 ^nܟR߸5n=vٸInn.k!haT^if+%^wݭvnBZ6 `0nJ/䌌R&TTTT*Vi׃eeeEja$:mc)]$N:Z\QQ?:Yvt+@y3[RniEާs =hYU&A}"%}=}=dfNh7ݯ)Vݮ)7~_*= :=)lw zLZkg'{_WE%4n27k;;K׬.;/s٢Oln\ţo8>*A^+sjx_=Q4oY2yGQwRa=c*-"_N'޴,vˎ+F tOΎzL{P8g(NC!Lƽܗ콖1%RXa7.-`Vj5(}3UKE z^c  ֞i) 4bJFE?06TDo/kV^ R /%k_Jezv{%JKHGo?IݣQ4^ *ICA ajKװ05[%U*JQ㥕9w&rJٻyghhta47%^t̢g*OW_oZyyWg =uYr,]`/v\12d^7&h;E$nO%]%`Huzs6Le H}ttqVh5GhfMg\Ev> _}lӓЍ[ 7K Qe>eo9 S<:^:$clR$ 0*ŵd/=rƘ/̹SY5lScͷ bģfSNlye+G9y9̪[ ҸgLȚ{%I i}g&J:cdzfw[ h˩˙{/t~64p]c}oo{ƭR `7N參=gz\L:P1rNE@}M<]];8|?(6Ƥ:Mفm>[=S q{e-?zM8XyL:8sv]Ozk /% JƖإͶ]dSY9uJuhĥjv6s-krV!:s2+n@|U+cV>@:X:DTѤCPňNtӰ)VQoZ%QNʭp݊jztHi //8pzOڈ2㠌|ѴU'. ]=.VAɘiz^odΓ*՘,yvv߿϶;؟O^| {oo>rV>Û])e(BR[5艮^κWs3<>ma"Ck22o(kM1ǔGKWu0(>ws7kTFb2j'' P$17Qkn$zwBvax& }wж Jh^m;m/Q}ʗҍ]T딜m3Cg7!~3.DE0ᙰNd2#* 3=V*MMxg>y{NQi{G&9?;xʌԇYܰ} =4ms^^u`C>ꤙf GVN}5Q$Vm7G …˛qإe`+wh3G.|d[ cjAD}rS^aY}$s͜"&?$@ZF`TU<{ J,g|<-SVmr%ژxkGSK0U'P7c& GocP8k%L;Ee,jo^E=`&6nߗ5#QsjlL[=$ _oç "B|[5m"UciG-{~$/]z@0t+'w o֔uWiOdޑ/R?q֪p$5h955KˣiGngdhhOѴwj{

q ܻFv |h9#V}}.νp;4*e`AlKC&G-"!INRc2:$@*Z284+o)ic7@ijmq$Zl ;w S,IX( $Np*G`Hd\> X=.$@Kܤ {ĭlۅ6+}K"[w Լ;^TڋBX㒭O7'+WA\ehB5&9T4idj]\*џ[PP8cbi6}$AClO[t^:lp_ mdslP$>9gQ!ݔ~ꖿBO͵5}`sa[`]CAEQ*ʁjɡm5h0N\ַzfTrD6e> 'H n?liYL3K\.9GRI3n$(gSeEon@y}\Xuv:^S a,05 5"0!qQXƝ1t 1,D3'3,8pZf)>o]nA"Ԫ.=|.ᬻA%"[d` GӲ-yI4b;^cr >jM)-dY%K$s/^tGѹ[Q_5dωW,H55*캪DEgVrޱMkge'FOK N׭*ɺ~ IfO-"txyݫjv=3bHN~de_?S풢0$DWT߉#8A!Q 褤1&~`<Ì5 NC{ ~ooWw'0K{3_#@pl|%Κxv}!W /?yQg,[19$7pzj9K?2-DE«n8?1DEkVv8]<81WX骆SG$)Ṛe -r$!"EQ$IꮌW`0cyפYa4o s lmsI⛢AGK=ѡ7Y5d#o~+agcF/ tMK#1 MflĶ}l)3/⺦+^1%iǷxNϭs =/?^:=&ܥsiIE, ]%m  KW,0cJyof *Ov,1ۖ~(~I׏\h>Yr/'gM\35e۱uM{֯&w@F [=|g)Cn'.ovƓYk̚)ߛK>Y2>zŠȺw\u`0^xŌ)"hDV!?&N}Z?tZgY2 ^voj\'K W궮aߗ%U0#&|^BZ$4y"B+~_dEhYS[G/@SK>pÉ,I7]kHDo ȁK"`0ƢƜ)a3:"l !:$)0f*ةIGVPX]@^"9˴gw֫ ݣ7k ,Et:{G?~R|Q@Hә} dZ)!08 +`/ $"[+?Tʺ2-">|#gSqlK.{ֳ ~'>,xxCCxP<<9f+_~UwOO@?E2vmZ=徴8c"KT/||)&%z"}8Ax>{ȹ*>}91yD(7'ΫI+Sj=|Od@Sz>#-ǟ}#g4tX3!>V2w➤:ucJ%lC qvs}MQ|p%{沜A#3)"$ " 34Fy.EjrZC^&{X'ʉ;oˤnpN]1ຖNAǁcYòz!&1#<g@3w3TM\%U YWB <) @єtʆG |*{OA-_]Sbn|T/LKhG%r@I $tȯ7 H& ~.A p8Tщ$9KCA\QAh|b%S2sŒshspiCeLXzEEwOxrt!A(Cc9 IӲ HuNDP)Brtok4 ƧB1 f`IN2ghE=(D"<L6EirZmE^ 1_ m%`0Gx4ӜWQ Pa`YdW 's=? Vvlg& X=^1c@9@YB"EH*rdb7\w7.(~;'gENΊqKB@*0猠M5ĸ8 T7hu,g ϽfBM` Nhǖu V\4pm[A;z u+O)s097 _N-9 / /`3nߖ⣱Ì#3P_ڻzG}#Cj=/DlȒQ?ϕ՛. `8l7oqFԡ :}Ƥ(2HTtڴ3Y v-^ i"OѢEP 2 ozpi*"`Yc0bDUg5^?1w#h].Oխڋz?wл!NNȎn~QVwEJSAP_٥f(ո#j}:/4uv+|qVGK jaçGMZ;7|zD| | nڶ1pATO?)FwR١^_*ۻ 8'EI&XTvp@U 'lUVpEkX5֬ylSun8rpwAg- *m׳(^ѣHxa_E7k>pfcԶwtfվKe]n|tѲ[ㅏ.M+l&h|t'!Pp 7+Z:Vo(0#6"#*_O-gQùխյFJ֚ukۦo+;N*ʹe$[fgP֩vi F"30@taR'u,"OU2WPP! tF#޿\Ѣѹ"/W.x_Ad(Jc-EfШoK2&m+/mY|+y} +rmU #e9r㝖 r3W]bKu;r q+6l"·,uXaaA;Q7خ[ ܬ1b+a'FuU ,E{桼^{uw3@$9%ی+V__ۓ3݆ N [Vx˳d } f` sU% L`?1A崚&.-/ľwi+OkɋjdzL=I1zjѲ[:CKO"{q/W:GΈ qˁ3b'F?:dKGxs%V;Hu`9a_߬ill ܼNBS!:N0.K٤@hW`Mʚ;LwFbN Dkemv5.[vf__LYD:u`'bT9Z[,.OoEx~cq$b8F61?Wb:Sb]pInKyCnޔE)fс~pFܫI!LOt4 R5zP۝k>>0d>G7>Q6`v˜Z9ffY̛_wRW/vDLǿu_퓅lp?sF:;ib0G0eA6`yWeP] 9,틿?mk6hbyք@nuOδt\yv}XD.ISW !H |lF)a0R| -ێ^PdvR%37-O[EحnVKh*!4m  t  C9q)iRĀ)axJqX?Jlt$rqʹ_;$A$GHi5+Z:**\B}eBdK]ɥи`͌ tʽ*^ɋ/Ρ޷gϞ9!R]>|ɕ[ێ^\珛z4ߚ3-uK>P$͉`0}X:̘O:a)AZ3k|JY~jTfǯHĚ]Z׫)93շMNϷ)O==7{'$,'PF, ǟ6;>{nuzDcy=Sֵɯ\Ѣ~ڵ=8I_iO7rߟg|z7_])~3h,:jjۻ>8wy53 7wLiGzCu=|7./9-:X `0XaƔ. n.q82A; Ac=Vbv\"AȿZ).:OП=꾴8hࡌO/6tF>8)|XOl#-6O!#ab} ?|OnA{J?Xj4ЋGw+j̛ϧYƪ2掋&wfOwr"&JM0ȓ? ӢA ]EgD"dn?qG,2-@NԞ N q5J-?}^Yh=VկsMYxALOkpSS7X1tV&2[+ qՎ͍W|qX/NkI~'f%o'վO _M=^^a4td֤216 o}|(nl_SVv*!` /͙ +vZmoF .u`'IIhYEcA")b0O ƹhI=$X2QnTWdֲdeCS&tQJMg|\#[W]z:4uK>UYoTcp?gg)gYwDE$VMMwIqj9MJ]j糧eDl3o;MJ}@ի\U}TFaHD鴑>]\ z2H/nf~s^.I@D ChS‚>ü v)/?`*}|X5%ɉsl$9QAs#|X^Doj[j`0`t83ԑl#[Ol࢖{><$-a|<83'^r%j}iq9R%'52c#T-GO HH B[MOϙ<9*lI7/1jԔ>?6xt4v+Ξd"y1& SO@"P>xx:&ojt1; arcLJ~4$Eȡ>~GӮ%̎S/9OrvjJI1+w\68c0øi7+Ri,6&X=5{^3'H.]B`2r5NrolmݬiV3DIonޔH>KoM I]k}6V@($K Aք_}+7Tm_)vmG/#7=8w>rIvU)>?1"&q U:AqۡAHb63+ND6_d}2RY4pE?))0=jTc~^O]Q~&.d,`rDQOQ/Oe2 $*Ḳ$Kn2:wnn:Xϙp|c0L4Ax3 tR.O.wd Q~/WFn_=9R:VG7 31SJ;Y^ډLRX)O GC} j@o'֡=Vܣ3Y2B@M ,IPʥO'Wjo0 u~.(jDP2'p>8ag gMbe1 f(GY()SQ_"%Hdύ0W*t`/jBjj2CY#:YQg;fDiPS҉6Gg8QQ;R|$4å5ß}p3'Z=z g$X?h:jo'.EWOMZ@իɻY#7β2Rz JvRb-(Ӆe!ED6Z#e˰A# Љr`0Kq1Av DTgs 0eM_/I{#gѬ4&Ik]rloV+v) Ymq[}V5'^ )曲[ϽTt܄/|pDBS$/I`@Iqo=v[\Eɉ'%:rc8³*E/}ʭ}yI^9^{?ØA,'gM]8m?q Hd:V"}Ѕ񊧦%3՝=Ȑϙv Zmq_3wj|IHfr>++=@tި69,K@IS'Y,]vH^(0u~bt]șbU!}xh%D@cw_CM}>8_<#m -gFc[ON|P:煏䃯,~mՂ^m( h-Ìܭ3XOi-ח}خ$/a۟~m!"(y?Xj{21IdBfj15\ב7}_;˞JC}e|3GfsFۭ`6f:Rd`Xaaq$O[97 5)KcV|VvviƬ_?'/6>5{ҜW!w\kh~砪Fvve|OwYM{Bݿ'Ǿ{#BZz5Kkz.sZjU{Tv=s_+Z;!ȯ[g8]P;B'߳~ucf'[֍˿Zq3'D$++4Kbl| #ev @DSVH/-.&O@z5"BW""_sie{.h (vXۘ*Ѧ֦=Ӎ]|$+.: (x3$bP(;M\_qNLzjJSD =o|ugV#u~Dg \\a:A5ALh9_ԴY7mfq?QQoCc l|AV%U_ҹzjJRXPˠRݯ<3m]uapZYmqmMLQ6w6Ls)M=NZ6 ӡk,d$rF{ DczoN}ptp߶OME|%|e"O)cLH @B"<~_Պceu=+v٤ Q/}~KKu-;ft.U}/=rƐuvz{{`lc41q.3~#Xh~?}L#S-iOXcGW .1ݳ·23kGϬ0yK?}vhO]E>gWŇ쩟>L ?!7U+$܄O]oo{Y鳫fNg#`S3w9#y-4|޼Ҡt4y`0b /bƔ*y8TѮ}__nxL~3jw^}=gTElsĨ.xZ?{ZLqGia?p}$E@,HIu!c ݔQGn@lARt]GRE\^&ϊ4Fhl]cZz]KkQեƮMM +ljba}?966%fLQq5-9E6zU-Y͝@:A{c_~}:'^ 闾PFR~Ea<n;q@u[)Vow^YxŨ_0ڴǍK77RpWiz/!Ґ[zNշXCKYNb5] HN|骻 =Xaƚ}LH.6fOܹQcIiofzrٞe?N0/bk a0Ц5 q-fPt괩!64uu;#)j09L1rXa@uR"mRuI#h;yI˱`Xa'<$$ sø^ʅ1ݏH/=ZB's|4 `BBtJEh|b%scm`0J)0v53$MDP)BJ`0)ø (;!$)bHc0 rrV] oش_nl Aw[Bmc-x3``X-'njBv-a|r9{a3.9xLwa^x[hc x3}V^mإ2 ׸yg%͙4S2z:"vf0trMT64L[sJE_cZzT lfز @[W+cK#iîҾHEu9K=gmxuB DK6$ ̨\?IrS@U'!Vjp`^]zlbּ@;yץrnn^^ʝ{TL[3/ o2PfnŶlm۲m{Ympw25`0BZt748 :)U'$ yu=6dYzܹ;c$-mK6?*:PZS7(VW0.ycaWܗ?!r8BʍG~z[,*GW0\Kg\˯⤷CH/1Jl"tp/?nLe[LUf;Vl9l8\cؖ'U~x:.n8.??lbeN)&T},_E~`P߾u\ZmNN/6sR8hoVUC(Ji%b[v0b[Ӄ:{0.@Nn \t`?'"I#_OH:8(*O.ǒ3:ۺxh}O};νFe@֜p0@f+Krx]ie=vӆ[r}ivnkNbiX;Pr~`;}0㈰Zej1wιKIgp%0 f:N&GB 5(]tEne>!z}G0^ Uc?^kޟo{[לWkwּAh2?SsH%t2[$H}%l@$e!H6lR]= :mw71h͟4/7=E;4MIMOyACNs?c0!3^WJ$yR)E0#4rx$Ȝ$(U̍IR'f ^b0 гڝdx^7 a'^*(B_DѡKI %-.'5)p~V|hhpʵN5{ Ƌ1t ";r-kۿ܂V`FШRLˇt 21CVBM+6g5\E V.N*A~nNӥ$D#j礟% cd@jIuad:DytE*v Ӈcvg@s$9eYDΠ :eS9Ngc0`a㽋 h2_WE@ DQiW 9:dg 'Ӈd빎I_^ Հ^g ^uoZw{XK7^ !"Vo`DFGNnu;$%#Y 8W]M g 93~ вk!S~ȍ_vTwnT  a; A@5[{"$3`0~sG+8El2S` ," (X^qޮlN29#(nSv $8H98")E>6΋'jk̞_Yinz(`0q`O`06k# ړ@:$Ia(xzqXYy r$tR~R6c qh[ ߹!GcCkgk2L(pθ+1' ^,,|f>>>RƱieYGIRU]G]RUJ 3 `0^+}`62+$ND|r9 :(N~Yz3]&扒` OS֗A$fI ]7CXy@RY0 v`Uq5"Mrw%U7ܵ$Tq%Vs071XktyaY\_QP‽%&ZlzqB]C0ShV >,!Ì)3"]gam/b1-ŴgK1@tܤ)pxF.~B ak7H: _ BB&QC|S@ m-J ĺr$cx>nt"ARlR%=87Jۀ@ #"P@ ޿8pݸ@$I4b@++k0X;'j2T)"NkWB@R-}s r6IR+IA7ƅ= [ϢhtTe}{@v=z$q.^ D1옻6 ,k`YVp \(S_)Qti;REZXE7Yܝ$=j1!WD""%i-Aη(BHE/ zRcŖðtsAnMMͷSRfGDD@SSSUuuyyEDxm^ʕYcu1Sߴ 6n]pNy7"I褣xDiNK )qu.Te2\6%@;A!Pߥ˯z#dZhXqhD}z)z"sFSՌ ;P>-o ƻ~([ƅWFk6[ruǿϰ>ܟ9q;[t|v9w閳*=;o'sg^z׾!`W\.JY֎vEQ*4}9^" =gD(=+xRψܔ+pq7e0砤sDDvFEZb|;r=poo9 Ci9;6l59(w֯-(~;gwZ2n[W  X_ط:%NgEtH('f [[27KUtR tjޚu~Ӗ}-;퟽q ZEehIX}ƦpahY9 rWBu5j>g׮r E*8>;O-Xcr.~;go\ر=c\(u&#PxYa"Uh}FPܦnG;>p@3HQꜶթ(Z+.a@NVw@dU>ra؟g~Ue#9 E766p@k@rr ՞u m̰;70}ŪT`zx&&?=POJu.OdT>|J: /p4-v_"@ ^Kw@dMx@'.zUnT5ԦŮqd֬K?pX i^K؞²А d,2 C^moogY"/,/*;.9xwm8Cx./ٱ"gqoyݠ7Ϧ 'lUxOΜ8" J-1n.g/ty_\zyN'jd6k]tDV' tʿ*;$~q~ڶ_}+/'"1P۠2oT)'gŦabqʾʥsm򌘝2{Fu/5gsqnWd-H8| @zX>T]{':̭񫬪: rg(G5f13Q FxiAo-"J+Rm S ഷ^dY>ԩS½e2Pwcbe8<+7ߴaEs.ۜ2;b9lKWl9na/ʹcA[nK Kwn9 8Wr3ż6ҿ蜶n0t~~ee2tʔ=ݽjeJ/_.++RZ?S0 ¶C徵r%(4FggbڸtY@V9V)=^1(:O.ǒc62nq[W.U'%Zx|1/EÕ[ ,2Rޚ[Z>2h+-E1@Vn^A޼AZA!V* 8 y rVK7ƪ겲!2\Ί-qR[urgΞr00׶QV15y{{åGWG$XyQ˞ :HгRqy1iO `ʹJP@x%Hy!݆8DZ2YiJ+ɵv=z[&EDv|Ol`<!71p9;cx>w-N8I>K`8^ǕUwnV4kbnIUz 'X?Av2^a9+nD/V I pTH#ts)4$DvSQ5w;_sy`@׻{ k,5lBHsczV*s!6@@:lt5 + T)U ͌+pJIkq(?X:zFR@#hfY[/T*iG q5(JOZJHQ>0Te2\>n$)#Ļe^7aZcrjRgX&m f *H[:a<^p^ CP fp+Rİ1-Wwt8HQ^;5G$7Lq071Xyن :ăm:3឵>3l ru͏^IuZwObqz8e+7{a Jh-"IN`W~U56C 8Fh:%QrjՄVPqowSF3Hw x%mX:W@ \(ch_=($PK_<07P<4r^7=0o'.w%_)I╇>m{>gB#;`aT[bwe>][|Ԅݶ`ƚ7&DJ[W/ﯻK/OO y%{Œ.⭦MΡHB5Z*QZC%DQ]oyxwۋ %:mjxH xm]=׍?XY)bbT.t:EQiiN'@Wfe|!)T_.FΒW1Bδ_ ;'^/j [Skj]t=0 <$x%yw)cg&boQc %aШc7'c~b<ϴ+^cϯOq_] 1A~ݻQ5?1tTUK$D"HlMie]ԟ8DB4xP |R9tYxug?ʼƶdRXPX0k`0' 3zaB-m`vg]: flnG:XnC0wSvNΊ;6嫜:jK99+6jԢ9+rr.rN/p%QJi} L6MvnȩۜbV|#oK:/?Ceww>_eu՚y,]*O'cH^"Qj>Z>Z Ɲ *W*iXmfW}}хۘ'ڿæYs'U=sw)Ib[vF?[w0:xg[n>ĨTܭY =cs-D1iZKej5uvAW3c0ï ż6ܒnFxӔifdm-;746f !eϥf@֚uwI#j6*mL#0_> kJN|lR3X"& FF IeXxT@$_CW dC[VlVdd2U5IFE.1 eم2s rl{D t)+;v[8~z̵mq:!]#!Nq 4 <Ű%=]'cIc;Z ͞$o,DN8JWO tLUە1$^$P T/\d Ծ3Hd8l#+>? ndpzmz̜w̉iZkԎR)_T$yO,SY|hAx#89Z(*Z-M`u` sL S#KhŕSm `+l5b0 ƕX,bI8:B݆`0 nK: t@{N`0̘%'BB)`0#ʆ@"DJp Ķ^Z(>!=1^f!I3ϣ V V=8u\Hcfġ @  qdovIDATy /2xh,""@B q ǹNF Ic,P"]#1cCkggJri}򱼮ZtzyEUh`V, ,kyO8Ccvk"{s&OS^5Ruhtv.)o6wjRZ3=jC2;{v&HߛVZ2{]0+.[wW(%vARLFG Μ6E&tcZQ.eN _)n1 [5r ʫ3gz΄@QL+!ҙkEjrTb3HB'>E9ya>MI]3YXO<@e$3b^^6/!$ /߻>{*<2=Ә)a$I4M۷/Οw-Lؖh͍`Vd2&HߺsS? gVL -՘`/m>_>0g9-сH[=,i I3k4 r^z\А? bj[λAJx,iZMHuZ꤬SE9[8_ۘ,%%PTU[gTeí%U+&'~\tsV\+_I&D5Әn֜l-d~8#󮔧͎Sd[=ti #z^=!9,ѝ"BuM'~׿ "Rڸdf?0zHyb|;`0bIherg鹾1 B+s6W DJHRSK_k/N͛8#&lmusMNaAс~'*+Hfm{Ȑagk2<9k_|wݼɩA&-ա@bh@ek'gs¸+WoI".ؿ˨nutJXio8Oꪔ#}ndz~b0K:oF&($RGTV*Xg MEG6髦8t` + ͏8J9"S5P3>{L}~VcѭFizÃo;zGgLa0i+-%w|Zi99澓%gC{ #>G^[ឣͰգKrO/N?{Iq B9l"0= l ,%{HRc,#l! Rڽڨ)XΖzYb#zTUzutEayq+fVZR)v4/2 S(@E/{x´G-)*^ m iaK۲[oMW/a8I}jkcCd7;, WS g`5^"; pFX~qEr43ζ`nSG݊s-+L%]Ke@hx]]FHzh͆p m-Ίa,;6 8'cIτ5/m˶Fm+<>8w%7 lR6g@i[k3޼G3=Qn;˓^I/x"EjC&10nMA]~;4"I k]*"X`ƐK'xpڜJ c;8=s1 ZBH5'Z1-v`)Jb7oW[f~l;v&7Έ&Br8ˍf`gI\f'd =?w`,<D~%@pG9"AR`EҹYk̅6KgX,R|5L@=㭄%?dȗӞCFɤg;Dh !rbj[h&EiE#ja }%L愈)}U.6VvZ=˒ *tU7H5*m f<=%V'L.y4U!B(( b?Dqʵӧdg;VW|Z[[Gh@V,<"j C&7VuarLJx_L̾6v(IZU#$aIhp{z89rxIyAD  иЀ1 wbj[h~%vCv.L^+Zn@\]\ *덁t#Cuxbf-7Tm'سSczDk;\. vY/kG=)AD`t!Qju`@oBðUߪUx8ࡿMv~_\|v=/ o4dy$+lu\oM7-GϹNMHPU[ZWQG,JuMeۙ^Nq5MSdIrrThurԝW'G2m񸊏ǭL<:=I .8ح}pK1OxyI,VNI4 %cKfޗG[G/j {ݛ'Ҧ(ugU}㒙Pn>w]g OLoUk*u|'BP...ԩSm#9vb0oK:ȩ!L ׉H-X.:S-2B>#6gVҫ}y}W+OΚ cR5TItzWw!3b÷=vʿ~t 1{=GcF~ys>5:ɉF=%:lvvS /mW5UOjJ{@"4Gft=Ggg_ٕGrտ.8 i~v_ַY`2 Jiq'*6O^:1>14FDŽ׶w;WQ ]Q<&&&>>eY,~ yca0qt )"4f0Bpo}O |rV{谯~v>uxS?{DEɊ?^HN=oRGMmu/{,FٞcnL%ϩ,fNxyG%䉙ێzM=}$=1zd`~bȐi_ߧFJgsE(QW8[=zCCWMx!7k\1ehTD sxh(Ý@e(YInΰ37 :75<(W+O J  fu} c׵u}"leS;KQ\[?8WI=&쁒y0?1dE0ҁ~^,<<9kek_OJ(lwrjtX%DNωW,Jw7{3XDUZ^;xWuQ,h](n[4`0^y4&I(/)ZDw_KF<_Η@UtU]&Fɤ L?[2tuk`&+[;8VáUA+$X:+.2#$AMOOZY/r֤O.LOuZp~Ŕ$Sh/⩷h/8piͪ)^WvT$lLҥ@\#wî>- nIՖnN;Ii 5]Xa0р%pgxp#@./}~ϏY%ɨ@_'~YR/eRZ9%(2ڻoy;[s;,Ț!t%i/Ɋy Q^umK´]] R+$Gu<ݷP_٢ řR6ISWL*Oޟ\(GmsY@񻋕"CVMM^!zcmj핆P;ّsWDEck;cKfZʩ<ȞϞWd0+&'Y7|rd?M2ﻅW g$惃>9?MSX񯕵cM~*=PR،`ToTvnWL_n;:tg .a/Lc~;Hr`% Dwl@ r9hv  Ǚ]SÃhmV#jjT]RZ={NKR'O?=,:zeuO9t:5B3mי%nV[8`qʄKu+z;WZ1tV /ԪnDX_:ⲟfjtXA~Ηzq$ݢϾxȲ掵Wz^WCU'뿹+tUXd'-zҦ~/}]J̲JwിTrŔzt+_^4z\SmW}iǟ}s=I1ىсr+֘[ O]k[vJ9WZ7]@yg&))#+-v˃`#HMivI0\.w8h tg`Xր(Q$FJ " h q mH$ERIA$ݿ2OKJhVWu6s.jO.p}*"C T ^^.0fVj%vhr/">=ԭoqujsТnz\p, 7[6Xq1zPj5>lq骆Ӄ` ,j>} 5m Z'bԾ{C=P畧`0 N,$kuԸY43NN$B㕌%;^r>Y) h̚P˥QONk[̿MlO-߰p7e%ЦD-RޱVߥs^Ptb` 4DOݙ}q);&xalHz_EZ;*q,<KzroѴhWeK(Q$t% q cYطJ^<#;ɶ>4Ӽ]BS 8 `nNzøiw|QE3~wܔةjI?)J"G47! |e#^ ifgC7k7ި6̉Wܟ3CL̉k3B}eu=W+vUg\g.g<'/!gO<񳯍Z'Kf^ihYjybfL3*ޢؘ@?X̍Wl\23>$kSb٤S|kں~EaYs-Hi9><=ҖߚpFfbj9ܛ`0v%f0!Bʢ\jbD 0>N6տUbIr~bԚi)${.) aqoڄwO]n{gO5Ifg /D^[_=~wzLR_Jf"HE<49 7@JSs߬o|("d4҉L {ּ(g~US>Xd("}} K O㗔/3mK˧#^$OgM}F,'&Dk|H=>1ҊWC[}3{ˁ37TmÄ9o=v҉ҫ=]հbJ,Ll:W#;1BmQB0;.[O\x}ҳBBճ 'ݛ:YaHʽW$fjyͽ><]P:`?Uw6;{GN(ohS۽+ Y|`ϭUrs'm.8e%ON۷]0ފ t3~8]~l9)#ߡPy[S6@nʦC_.{pR¯JeS?\on%:WKQxQtI xYOoKg bzLqig_KȆ_\<_HE#C~׮/tC\z?IXNDGYƼgMy)߈k,ӯTY|Nb㒙p~ԕayyA tin_beSl+۷')TePdE(ŴUEG3UaWnfzmARqZDI'߳~9o㖬4I5ˌX㥲Gl/JFrHM  .Ì+ ˛$rElQ IPFڬ ҫؿ&gi/-"Ϗ.)^#}(#m}){.e#~Jjۻb 0X0m*껫ɁYY"bF-ݩʆr?92bQY2al+$4a'. U $Έ +.ׯTԭ~jN{WmðGNWrLðaV`0; DQm݅V&u| $1#͏,*==mjTb§ń6P1X/IWo0%ybēwKeK3]Z>1~YR#_^bI ߲I[vi ?\ٞjx6̏a^~ŏJw헧y)/NJo4qp9pkAktimjCkKJm(skX:!WC(yj}Gψ~EႤXn(3Bb0t! *m.hhfY[q4N焚ifH8?:thpPE>xF|F}# jžp:g!b 5m];N^6o)#.OkE_m!6[j-1+򵲯I:en66R;g[jj.tNt͈Z;X7twwǩ&igcFKuhK4-{ 1O*gmt/b'&Fۊ_d^" >ղw^8;`03Oұ,+Y GN&@21_ކ\Pb{. i_oM{OQmg Louc[߽FS:4p@ Oq:^,O\a{^P;6z}C}{o=zDkD2ņ8?~VxLU7J^n'f7V*}GV$/N>]b}mW߷ "LDE/X="2Oo:c}:=ÍU'f[Ki)]m !'Fhfw )1!߁իjv9|1o9򳶮S^)2N ˚茙UOSHuk܄y,(E =g`Y%$Y %( 4CۺFP-= ^7L j͐`r`5FZ !V3T$*Zݫ[_(*00ȼEZ !i_yWxRMxJ2___f Uvpg_~~~,;`0? XmgwAVtCElBi4~q-I}[nLٴ*mN [7?N4/|nP#(m{0frRqFq1S+fE{wv9#۽~oLϕF^,-PuEZÃxntsϷZH$>>>8_gji(3fB/fiFIGPFHvLkkhhT*3|Đ#XԟJejj$I244i}EA,+j5V$ |R0hP0A,+0Lhh(YTADegd]KAzoY&{8mv*Ӷ88xX K7ٽ~n.U ў d:*QFAM ;%ʚJJ˲Q5Т%jj >48\Munt29Ri;N$If`1dkeR˚K:ig(NTwFe$ɭ h.H'$I _QQ e:hEdQ;66i⏏(jW>BH*YLI:\SKԐ;}EgBH&57 dY600p@>e2i``PRi[ۀRC]aخ'44T*Z}3P@h4$xtt40CXju^?ŊbD"궸B5ԋe옐_O겱} {W>ڰm=Tޘ 45= t`ӻ C[Zk!m͓.iimiVLW]s(73 ^D t1&-Ѿ)kb:k6ўZWkp,N": ]>"k1Y<"cZtV]iꈘJdqYi} Z1YuI(m /*.Zta˔=9ÏC}SkZCI #-R$ ӿc@~||PB*!%%ekjj`/ hkqU?mbIHpZH(㇅M0d '[œz uyKdd0[ZZ z- l9;>7{iCu;%'SL޵V eNeۺu+q92($4 \]#s-gss^tL%i `vPĦ6 99j2@zeqd2=9>Ɓ@HY:bBA؞soEۼn{ Ց$)Q=3^A[:0 ಻LfD+ &26v6em=]p ٴ)NV_깶]wLS9}sq9K_9W</뮙˜1Tuʸ9N+ W)Kӳ[kZ5e(lx#EQ$) Vf /tXfDh58* gO,*=, +P =ƒkIݤvlyys+ZZCh nS_ы [jskִ^w탡v+)2׬߽XE3UD+wWM*mcūO ,e* ;u AA;SD*6n`v-p=g% 1-ڜwZ9U`\(@}e\Ș3(fgaE%3Kf9wOTWdUQaiQA]CEy̎J"ciP hB\ǕҢFciC8=yyD"05@@Q%$Euv`^1y6a@#h M$@-R +912r|"7Qyy#tN[ii[;fipGhU˝_x.X⊙W4kͺO6-Zh(, {v`6X? ,A8!+bSiUW- w19z #H:S-@KO1o$b D"i͋""nCN*e >3!VE;Ù)AY" 3XZxxxXXyL6\$Dlllthç_$&&Z ?##"e١:1oMÌk!!!v&L`25qD S%!T;wEGK1{wjӦ m+tuJK?N:k ls@`Cg:}?[i}Ύmy/K-8nZdFtZ|VK4o(8`xZg%{wY | EQQrEz@0\.j-SsF4U۫ b=DY4"zzzP(kQ-ʲ!I2&&-yR阘aJ:0 E#BLtV_\__V_\ |X-m/I_,PHIzZZ]d(Vn-{Os췳ɳ\OfIkGYyhAY~-!0t1|ȚN"hJՌZՐ"k50ePHQ 4yԆ*z&R4$$SB40*'00ja8gD"8B( @.[/KR:P$q|eI$qP/*dE1 czq}}}m90ҋ5uHH/Qj"ܖgq1^Kg@HQ2܇5i7v x6R>38axHʼ @s9!j ՟ya0j@j^۽eAtvv>YE5cQ4xD{zDQt Q,^&b\8t@ iEY^"!ED A$A TW `̷ @D"Vr c/ݚG KmvƏ3B \A!d(B(4Ż +M@1vDيe$m{dE;"/jO{jrԮ jGHP1G$bGH{AIسM[`%L{fc{Fu -vBR6A&ز` ص}_*5%g,[o>Og╻>n8e 1`'1P$H>#Kd?FgΜst49sk!@z̲ɬY fV=O fKi!@CH\m^d|ڵ\Gx]zu g4_zUݤ8_@Yb,NNNILv].W'%\.-=8NM '''J Ȑ>###FግeggTRĔkyItLVY@j;٩') 饗GH/11qҳ,++Kzzs'r7~ A=Be9NӜyxG牜>ѝ'=I;DrtJ1 *^+J8Ό Yw: Bz9eYY酋_*\((+|Y{˒՘r;ҫT*YO\y'rDw{Y<Rr^^I)n% 7=˻˲yy2n%vdrIqjP,C!3ɻ ɻEB;B A$<$\:4|U+B:$$ڲ'?Mv)Oe㕛/KgَVݖ7mbHܳEHRlGJv)ÿND@`p.@CH=ti!@CHt1?["}NR)5s:Iv JUN;NB!+Jϲ.tfddH/VޘPT*JRVc lj'<9};=,tO(< b~D2˲n[b^,*gYVz݄K߱ Hd5q M/4NBs'+= ,H'<9,ti:$\1;JH-#;:333A9Ζ>6vsssX].Wnn\8Nzz"=RT -ӫT*rrr$W(J2++Kbzx̔>IJlNN􁖄v!^A 'ѝG$ѝG֞dv=ti!@CH=ti&JҞ'y^4 H& %`.xH{~t{HGHW8K0{CzBJԑȩ)8YY̔r9NZjt:nJ^Yd5Sgy8U*&<9};=,t9?H#QvⲎBTK/-C:Ys7ҧfgHЗ+7AeZy9t… ‹ J_1JH-1/jYΖ>rrrrt:srrdHLq˲ ӫjJ%=Jr+J㤧VʒXng8.333##CbzZyx^Yt'It瑵' t… /_iiii^^x.^288X^^^\\,es8`~AH0;pǹsnfSNMLLaY˗//**9tKȐo<y ߇mztKoZ]wUPP ^bۯ^1(--]dovرcW:`~ . k+n}+Nu7VqoB:ZW!iJ`,((xFGG%k֬)((r-Yo?vؒ%K֭[w"yK.!tKpHw}]W+*ۈ.XxbÏ?b#D W,/~b親{*,*rq"YrիWhtttd3r(aɅ ~uu]W\9v옐ڵkQk{]7/^|_eqlie^$G0J}1;w+++#".z%tK 7%o[ñ|u+ON\-䨰Kw74ӳò^/B|ssszcDŽNVctˈ +.pb9-Do[q Nek '"BH͂ z-LFFFFGGsrr|?Zv/^,?;o#"qp/?)ns)qD%X+_+/%-,GHHe gϞddd?^ܹsD$*"t'\ϝ_!ROk"f}_zܹsB$']hB@H~̎ -Yŋ׮]Q* .2JNxga@BH0;,X` ~EEEEEEFYQ:mRYnnﵬ_CtkHM*JR/8.1& _r. orrRREL<g+J322SNNNF#q,NNNJr\+t:Y^٩)Y䤔s9ve}`XIPy~rr233SbSbAZ}̙3g\t) ÐL-[lٲd 4){łd3RvaOjՈ!䄪:cq:7XQTgΜ0 ҥK}}}+VHvAB 物T*%J3kmMv8SsUGDצR (&&?pIrZUء9Vm2ʪ2]Q],71⹯/RpHːe9mALUYf .~q?8X" CS*wy'ZG&_p i=o526 4e}\D9ӑ^jD97(UƄVt.Q/) î}rh1PuθhcuKݨ;w-0oD &s֭ۺukĄ<y~ժU/oy4\B'T)1>i`llOJDs0WtwEHtM}=99(X!nc$P"LizA?^C8;@e3"!9%ߗN:ZVTzUV\q @箿<4,ˇHM #RJ8єx2MaG2K"+T*fƛ[mOE ^z9*67m|rk@Dxb!uD퍾x$6PRRRRRq:0jw|91 ]j=QF0j= !H{@n{Q: !HWWK0J(<d_ n$^PJ`[q`eKLp.$m6 5Cs[fÜ8J"JJҎhGZn9VmQԐŷj'[E/WMng"W^E">GeެSGys85MZmc8j6WvEӔ~)4ϙbx ,b:*Tg6 {`5Mh >5c<_`iSj}iJ* nh3>M]S.QᙃԵZ뢭f*ifSMgyusHKoc(nSUqQOG6\ԐSoM-}sjVk lfUl/:l'Py!mcS9mc,M}/EʲjhzSR@!:k8՜Qfn*RՖy^D^.Tjh!}|gq.H) :%g p7狫ns%,/ƪ=M[1Яҵ=Sns5i=|URg5*^u0T(C8 i7Kq1]CMF|cFs(."bի#g7'w.]k F>^--lS礴'1fq<'A¤ d8X_qeP#ȹsD䂅_&;87S +J}-)) g0|:1UGK4K!fo:EW5LVMUjJz1'Wb艘φc(Dg1tĺ#zoN x' %Zx^:s)QcԔ j 24*}終6-ss\4%4@MeѲv 'XL5D*U5-#/o#*θPc "n])*Kalmޡ, 昿?Yiۛ),WE*w^^堝u [g4H2~!]g0J~_x&Qt0fs7yIm RcI\Cffds!֕~/U ǭtԔTtUyn`1B.S`/%vXj n& ot LD8S_g7ff[<-s4c,Nk&^ <\Z5YjKK<=:"[ /jEz^;,5]%K_S68Uj m:M〡=: E g @mi/aab.ò{:blLը/zPVpx+B:̍F:l "Z.DvaO/U5wvu Сmhns5inV16%Me]˅̉D͛ۗ-5†zh>#giI6*LwWoV&ƶUn1tM}jӫ3.BMi;T=x"Ϡ36s㹭B3- qs6kY( W˺}p;: "BB&m5~P0tto:8X_uN {2FW czf DcGO 4:=:`kje24j:Z5R dlp)Lta}d8x,?ݦTm![C^D߬t&Z>XE,=J'jdl@QSR>簣n%͆Jj[BDgMiiP+p?֋3?hgj5hs$j6}&m[Zٺh's@VM7V=Lgﺾ˴:i+=cTkh8?Q /;tB5zd/1r™R_B 8t0c5McyZڦ%U=F]֪ÑЧ{kJh*4ޣe򞕥767j:Uޫ)5ܾ 0:5`mhdlmCtKYt %Bq{{\`c,{ N"\ ھfs}+;/`I2{z"q-zbbޞ~(π.GR!ϥS~?3l{~?q-﮻ O?i"'x·Ap0td5i*jiL s=~zLS#U[{R!sXjZ<?KSTt~!]*']k4'<+J3q[jV㏿ 'v_b~sql4rw[6l&"z<+ ֖$D`[Ab_@S۶>$Z!J,۶=Y۶s`kwTvol#a_Sqavƻރ{ktg{k}i%~MvGST /" … X,Gڮ_hw_gVo]vKKB&- Υuǃ/b|5(b`+hNʊ>$~}ÃuA.qppx>Gw?vy͔(l /zK=/nxݡCIŗ/_]hѺudOȖgYeٙ<1;.?{=O|ҖCqiICtC|? /oXׂή cʕ b||^ya|N8xxy_dŋYw}wA~~<_\Xx…oO:OӨ- B:HOkxb|Uswj^$avjYέ[Rn??훐w}%+WyqŊݶ?|58ѧ' # ʄ'Ȳ/w=x_>wv^z/$R8njj'z衇&V^*jj[QFvBZ* l?y0 4={\󭾀!:*#""_6,nkzo_OLtrw⿿%VJ>Og)dEwo=UHȻᆱVn7qf9dә=55544Xr>:ta˵t?J\$d'"Zt~*)YQ<@KY}|k'黛WjBE<-oryNw>uP|؊{prKߵeɟ?JDFug$gdd$77ҥK7H%[eSwՇ|KTǴ-/LU\.ߒZOH;- oADD'!}_V v }o\q\`O_ٌW^y=yFLPp >lˮN" J tBWfID2[ZZWX!58NRi'N:th[k~.k UwܹsΙ<1!@(JRhܙ}ڵk}wD#nn{}Q;SE0S~]y&C|[T}C%QKWϠ+y۶mW.;s̳>D(V`(pA/@TOr">Нپgٳfڕ+Wћoy U)ٕIB8Nmͼ.#^ Bxj]RxSo93gfrh`&MrT* )~D>%.f>YaY;E<]%\)r,@] 1R¾ˣvzHƇAA^ ;H!jʀjRn,3\%qɀ}r0͘"ysp=wD%%%({)Qb"+Pa[8$T"Q) |$uc"@nCƠGvGh1.%y&L, | KžvnK$c.w挪)3'O~_GIQ1pQ@Q9Үr"ڥwDu8<şg2# ᤴḷn+ov`.مMSI79)ll,''WU]vqΪ˗/_t)ܚII 0 ;˕JPfSl_?-&4p mBR"19Q\prp!NpDJAL SW3-_|˖-.\xgneߠX_X2Ź'X,>//#<'7薸DJ/3Tꓳb2 (sܮO=aH9r Wñ1vߐXTX'ώ.^$R]EŊ뮻.;;''' IENDB`fritzing-app-0.9.3b/help/images/highlight1.png000066400000000000000000000557411272363601300212510ustar00rootroot00000000000000PNG  IHDR\bKGD pHYsod[IDATx]w\Gٽ;ޛt;6l1Kh4kEcG{Mџ%%bE r}ǘͺ7wsm}vv0**_VD"чUf1b|(1Ӈ+300 3>.I 3xfR0 3ރ0Ì`&30=I 3xfR0 3ރ0Ì`&30=I 3xfR0 3N0 3*kkkJUnaI 3>a޽Jf…;wԵМ%i0.]i!WhlS0ÌO!k޼9q0FqqGIҸf͚ioty`0=ZN,Ξ=kπurh43g:th%0tٳgi^dɣGRRR}R3f_T*߼y#BwwwTl2=Κ5뫯do޼k VVV̙3f͚ۗ}=_wǎ+VPųfͪ_?"Zy?T*upB[[?~O>fffj4ɓ'-]k׮o߾E:99]~}Ĉ[!5kwM]SÇoڴ Bk &;v_~K,s3g߿)&&fرz>|U6D.]֭[mѢEOx???iӴQ'O=z4… khu˗G… B;p̙3+y;|xg}V! .0`7..Pۯ[N#V\٣G}ቮ i3`&y=> #l90u?#Ec0߿?MOOPuÇCN>}o߾}6nݺGBQԪU|||6ln3f P[Zn]zȑ#/_nK0̅ РA&M$͛#""x+3*jڵk#zEfaaӧ03fNBԩS3WVCkF3a„r̨"RhӦͬYط:66666OTTI&˖xQFx!xO?Z"]\\M@YFx~mMLII:thII [fM|_ 6w @X\^ѣG2w\֭[YC{P*DFXz5`/^LNNfO-o߾f ?Ruox´!_9.hVպ"Olll$ G"HP꿮D"%|Xpw:t5M\\AtRjjj`` ӧ?n޼677״Z- AQP !jժUVqW^4ܹsΝkeaee{`֝;wΛ7rܹoC\Rkn\Po8LNNԩqgB. F;wH'j "(&&Ӳ!b@DDDΝy+[=BcbbڷoO+ **J[o8}||r@@ߏ]t4lp˖-/i95c[7|2qD =ydϞ=K0N:5} bo̘1aaa\#c||;>DdeeuܹA<"k߾=v⸸3f>|ovvv={B{ٵkWe7o[:~oݻqF[֭#""k׮󐐐s]~ RrttzԩSPرc+:u`SƆ V\oCbȑSNA%$$v矼U/_޳g{͚5#F7sСo߮_^"jԨQF >3gf| >}4o5n8MӋ-bIŋ/>n(BFFF8yڍAH;;;=VO\T*6QBYYٰaÈ~r_LOOOMMż8s̏?URRkv/Ȉ 6O۶m?~X{ƺ UB+&wLMMuww$%%O_5jŋBl8::֫W[!C0q/)))mEիU 5c1666F%ɆNd'u㦠|pǏ?~<ӧߺu֭[ZXƑm۶ճL,;IUhqeb>O-lقƍoٲeȐ!}-[Fhҥ˪Uۮ]e˖M4Cߨ)R 5"Fq ];|Dn 66v֭xBcʔ)B)UW25bP N<ɍh4([C@ j̙3]gO#C,[[[k\lݺ0RSS agag>},Y k---]L5EEEzt[naϞ=3.\xLsVຖ222t`P%D"Rj8zjxkkkvoo[O^Zi$\̮?*r֭-Z믿FQWWaѢE۷oޭ/L,TxV"7uz̙ÇOKKu,ۅd`3mڨCPP`"/PpBC:tgΜIԴi3g \c=6,,,6n܈}ZmڴrC|> =p=!-V\\̍SB͝;(!\.9rd~~}Ο?cCꏿܷo/W7~鿕oIB֭ۘ1c ֯_oH;N:U&q_B___[[5k=zTϱ};vlaa˗/ٕ^^^۷oS0jԨdggs]~~~666WUBXdI- [[[\x7|i&]EG0[j~@KKˍ7z{{?{L& IRӧOYƱ w|7n#={ƍhq=F1dȐWWWOOǏ7ԩSG?T0رc7o~իW$L fa{/ s6 3L3)a}0 3*//͛s&$&&^zU;lS0Ì ԨQ#!!3-GDDp3)ah>~ktt4ԪU[&''8٦`ԩ9s Ärd̤`6 ٥4MsWD" n_Z5 EQhw3)aƧyݼ{.\Е ko5`gg7o} [[K.WHΝ;$J/^hBSqfffǎ'\h̙3l:vQXXPzD'OԞhm&d!۷ok /fffPzZr%71GWׯ_AA޽{--- ®}6{BAkk]vvޠ|~gg}ן;wA666{1!#VZeRtuyzz?lBF'$$T:M$-_܄޳gA[B̙c6Rt߾}z)+L ݻw[YY 6,%%E mV~}'ѣ'O,𜷺J}U 7o@^ٱcG:uL.F׮]W\)R͛qӢ]vk׮ l5QFqqqy3B=L. x>>wΝzxa݅,h֯_a1o<;;#Fpm&X[[/Q5J/`F 5jTIbtM/`FhժU%Ѿ}{! Hbcc;tPIb4o|֭B1Fpp^x`m[lὐx999 `-/<:θJvvvvߎ^z-_ɓ'L0[5jԥKɓ'=޾}ϙ3g/նm*o߾xb׮]aHH%o/gg焄ާ$(((8sL.]&뻺?SNuЁ;BPPP``ɥcL&;yg}՞֭pDFxIT*O8ʵ\z{{8q˒"hŊZ}&MZ:q%+?]v`` >;y6K>|`ksѣ4}_pa ?޴iSʿ+/T*#UΝ; ,٣G_TF`QP\}A#DDDTX:<>W_н{wbRt֭zJe dCM0/XXX{ t@+! PA T%F t ! @5Bj@LD^ؿٳa Bwb4P* = a8X@k T.W =2%QŀAT @G ;;;vyAR; t6b@Ca\ (WraÆɃ'oGdەAb8YP[͛7dJYYـ1E6;؋E}#//olȦB߿?hHwETw_ uaaW_}>JEHS +@L,=W.GFFa uL4EwZ BeY ׸qVZU\\LըZdBbdj͍7T*S "1&nPL~{UB6hPP_͛իW-tO_]vXSR2 ` kSZͥ,Bp VZE,]u)LJs+~+2NkxXC1Y [!=&&F./_h?uDr:矇BCZ̸IEMܡDCE0Y999Pb ejN5$RMQejf +b) qR2y4&M$HVZEL%P]T1TVUŠKqSdJEi%/[YYM0aiii> m 鰸1U ,PP h@AFL!kk :P`NX$lML85z ۚKj9i J@SAԌdcf*7,uiOu$|ޑќ)N/?v Jg԰B-͛A%V*59B܉>eA/k C+EOl*srHEgKʤX!-j؉[Wt8ͯ<:a/ ֹAC.]>MMjdnRA0=Qzi2U01f $wd]tQk=^ EG>n֠ t(@ϵY:Z! =ͣP&{(أbh.*o@r5F *dwT#-Qkt܉A@^a!_= aĀ7ȼfC!etcdcCE"n BQk(mRB'depB@C@c+Ř D -V&(Q1JHNc+6p1r׿)Y`3?MU0HCWL@r 6 Gy S͚1>e"D#P'D"#?[:Gy:[#_J C4V{*UY ϫV @Wh?-t}g9eAs9_ Ze}A]FkMAh*9)\-(g-ϟ2mCȵ*w)eBB'E25(󂨎t(h BB8:]+!C2-()W+a N4?w\(FTm/U\jKd Zd %0 N)V4RҴ MPwLք Fp qgHSj:Kzi'ܠ!b&.JZ*(*F%!WXMPA )Д$&< RI[W+ CYtjX'V t!ƛ=gdsvR#rt Dz:vB^H%`M)7bM*=3`w%V6hBݡGT^0= F- [1jJP!P{A:Q6ti#jzb@xIV4vV[CY]{:0v`""lj*ӥ5]@P ]Rho !&3uѨQӧO'%%]~]ƨQ0R(R2O UګL里TĔp5$$1wk &~E5K$;]'5̮[$jϼKP+1)µe(QDŽ" ,n DLY3B|_ +4=B[Y3'j:BI@!ڲ1wb]-]KZC6$tV D3/ }rVװӯ/[PuIJ ڃVׯ/5BGj"jSB{_kP4 L44J6qCB'J B4SPMT'2(T#6^N^N'B 2"DFj\H 0Ws2qsOeWBWCAMHOԘ0 %򈨡 2B2)tUݼeԈIKd]_ʠ=1#M%PM:U3ˈs4r)s)(P! *kӵMCj.,--{mA;񿛇T*͛wy~PM& ]CY σ%JAHD!; HE|W%%%SLy&sK5&)`od250HL#G ]E+0͹ DP9r ٳ/&)&ӾvN+=ȣJUZVMs>pi$ʕ+9CF@Rm; T+ "%T3FBsҶ+Fb6ѥK3f4nݺ{7L;49(rajuh%UV .LHHذaayX@5v&[`F.2V·^"#'RM!tuum׮ӧm}ܽ{DdAD"޽+W@i%PYAL9d) <_~Ϳ(GNyYCA\.d17j-VHԥKw獵o2%լAS7DaϞ=I :uJOO{T(_$`z^x:8޽뽑ÇǎgWXaii !.ZѣGXAըQ#>>ТE ˙K 22NT=ro(%I#Ҵa޷:333""B q8N۹nѲ ///""\cߒB\LIEwH=$22Ν;"a#'SU'\3s^a=B.1"))MLQ`,7!]0-~ DF`P(曋/ jT~1oDFRaF #a.I O+צJ@j֗cC͘1 o?E]| 'Ǐ?~<^3BhڴiO>m֬N}3]oJ>W" $4 (]|^}xڌT*ǎ+իǮԩ/T J/a֡~}@"[%#sڌ0̤I^~ݤIvN}FE)A@LB%Z\APF1?ƍy0k֬;wpgCש/`1^1wAiB%H/ane.dWD73~ .pu=`(Cw[9 (y*P [ysE f͚cǎq'֣/2e@OE%*𢔹˜Dd1sέ[!tsKW{?P_RF@ ZPJEy9Z?PXYZ/00yիc5aΝW\a)ruu=t?AS@M:ȑ#I&}  QjiPyDFoϞ=KQԢEK.D@À2MIDF8qc ӦM;dȐ52 *.Fطoo0awSqqСCBE.Fزe `ᬗ C&EEE MK2i^z 5ks) % h "F!!!aΝ;Y5ܸxc箝׮]+@$!D)P('L\N:ÇO>NNNW\a {9SNu֍|M911Q"`FЧ/pP2@J\F Ν;5v`Pbt0{ekkk`eeեK/ga刡III p nݺ%&&3 !E?#n߾]\\bqnnܸA o "#Ʋ~wy;>} Xpxٓ'O:w4v`~|v} 0*5Pj}{O?=4o Ç_}Uvؿvvv;wܹsfj׮A .\M2eʴi>= ٳF00xʕ+9 E_!Zzbcckݻg$/.\5x! KKK3#9rdƌ\3!_Ⱦ_AAA5WΠxJxPPPc!Q 0t… [jE6m6mT.#`y*  ؾPA^ʪ ^x!55Ocƌ_=b˗/ݻx6=sI 2l߾}娬ѠA] a--^QTTw^v_CQv^zǺm mBYYٞ={޾58_# o߾x]B۾P(ݛOyi]B۾R8K` #k׮YFQ_x1wZ>rEggAY[WfB#Ɖaoo?h ?y 4B>!dÇ_|BkS(gY?zhn\D^0!e5y(0y(0yXnݚ5kPym^0-vرhѢr_~6m~FHJJbmڴi\#FN<ۻw }D 8T@8TB #8„40!2޽{;v 12 #L!޽{Ϟ=L&=qr0k,///'Oއa{{{s 5jصkWF #ŋ[h-dg!Xb :!tĉ.]M͛onQϟoݺujBBŁ􏜉HLLlР8~ =͛AAAAAAW޽{nnn77oo ٳgĄdɓ!={lG+➯_:t(^i:''BQ!!!iii ooK.ULݺukww/_-X,ҌYfi Hw'O-F j׮75"7%%hnݺZmd}QeTȺ\8;;K+Fp)6.BXNꊋ]vEl<0z,ƅчV֖XD  bW#7p]Ar]v͝;bD 5jԨOVڵkmb˖-чGFF^rbo WN;vl0U4fyM4\^тq BcѾ!ܡ7\!TVVK M1R)7K*rU)IDرcǽ{ NU**D -[~W6t$$$]+VуX$1cƁؿ\o믿p^ \bɒ%[ne2dΜ9xy޼y;w˭ZJHH<1>|أG[ݻk׮9::xz<~ _z?uɣTl߾u֘ڴiz,XW_UF2Ǝ;azzj~+Wu^~=h _I٦`5>|سgO_*bPfQ..Ui2%)R*o@\ m *F7.T} MUDX1L&HĠ"v݂k3|w\|/ O ܘ+Sm ^Z*wDF^7 2 "m^ pM%El tw8҇2~6pu9{U Im;=[Xk:c;ѵ`yiY bpʪ 1xa`nn烈EL.Zfo\\ޛIE777vTbMemmeInpvvfT*eTnڒ%zzzr*"xl0 CEͤy/g"[`RxvgK#&L.J Fd2D$Kaڵk>f ֶ/5j$[hѯ_?U0[!g-C/!իWhhX,nРADDY+#w nTciiyXXXXX\EiG BT0 ի~Tb;v EQ?@1rssk*1.^XXXhgg!(j„ ~p>+VJ ÇGjJ{7xa޽ÈbwWcqtݾbz-gk5 @1 ߛP?,_Oh]ɊqL&8p`׮]&#66VRRYv ɓ&H@bb"8!$qOμ#G]Dcco? j=#G" Ŋ+{)aj5W 2ӧO/-CW\\̎B?:/+ ^HLL\h1׻&|m|"^޼immݸqc.cްa Μ9CӴRL*c~8**j޽E~W\is5iˋUA$jzر7o~+0??߾}111& .ԩS' <&O~daQQ'LjZ1޽Kt͵{]fΜy)777___U*Յ &Mtyӊ!ɮ\ҩS'sNR-ϟ?]h6iҤ]/׾]M*y|ٞ_~˿9/,/Bm66ABhoo/+5e*88X,?~ݺuIReAAAedAu6p&MH$K._;!Hlll+51AQQQT*UrrmO"޾{СC{>ۦMʑFѮ=oBʌc3gz:~&0,o{pK{4lub *Zy B(HT*M U*UէJRaL iӦa7o,Yb:.Ɓ( \^iӦǸsC-,, ŇM e{L=E}3wK|wb?7 _:aۨ*$lųV֨Q,P(222|xԩ?|ѣGM2%'ɪR~嗕+Wz666zwS9?fD͛jժN:NNN8OTzIII.\QFttWFŋ7otua~mҥ&~-X@$a p.][R=z >4I-]HHH ?p… &yڵ͛7Ǘvw[b_|(++g87򢨨h„ OV7lذnݺ*†fggWIAƎ4vd7k,,,\~}||<1zR0&~wf ') o׮ݰaøCʊ{K,R(իWn] g\|yE bCCC'LVT,--wڅuͦz---,Xrh!7&..nl: 6 ƍht1to/;cѰذaC۶mّ 1׃vvvӧOo߾QT`͛twaI\lll,X:Jě{ٿ>>>]t1<.z uqU,1cqT*վD"!}3lWٗ-[ֵkW&n/^`'#߿]2ab֬Y8kwfJ-V\Y Z\L Cusswh>s-[ iѢEN l2d<:t`'peT?m O߸qdggw___JJJZhP(6mc  1kp͚5]tiapoa899_@zq)TzyjԨacc3{}bZAD;|i7f;x3h6%%}lmm;IQv$q]vl ǏPUVVְaêSMW;∎6m^޳g!QQQPBB‚ NSem )HR}:><((ݻۛiPrr2yyy7nhٲ%NI8{ٳg*ޖ6JJJNz̙R%K>Yf07ˋ]޿?コuVNǎ=z4;MxC 9xT*zm۾KHBǍsrrͭ[l!dԚ5kBӦMS(lJQF .) CEFF᭞9ؔዺgܔ蜹uIx?^Ndy_~xn #6BȍauA$%%,K5h` ;q‚xЊ+ϟϊݹsMN8Q4[jdggTFOq-^1zرcmۆ_իϛ ["2t>gΜf͚-X$2HĒtnci4kO[6o׮koCd̚;Qq'O ;Sk=Ѹaoȸ )!0SV-vs*ӨQ#)ںaÆUTdV6p] GGD n2o:v!;4H$QQQx5<<'~2ʽ.]4|7nϫѠA Xnݺ䙙5S;;;mk+͛7gK h׵344j:S`GfV;xd)(J<0,,,&&"&ݻgaaѣGɓ'㧎3@L܉E"֭[qp1UR0IF+!X7)))̘1zb3fٷo߆ N:SzgΜٹsg\\qױcGE"СCt .O>5m 8c8L{+իNZfMB7f̘۷c1 |Kkש3=5n [H{FQE3L1`KԸo v߿޼yx޽T*}}}_E777F1_&iGN2{Fԩs7ndeeشhтP$ 3xk RyArɓ'gddrc  At;vTjONNN !tss۸qRDYdB> .T$JTEV4h[PTYYY8[*ۻa-e b "/1bF9}Upk8_MN:kX֭{n'q6Γ'O/X5MR߿ٲesttlԨUZZڃL>fsd21P200p̘1aaa59aS+?Z4hРo߾mڴ%jpYYYϟ_oܸu*GGG7aFUZi//@++sB(&ϯqjŋW^5yԫW5WFp@XZZbI ݻsV*ر\\\4M^^3nkUJ(++-÷~1U$30R1iҤ#$?@|saPnS4** d0c^.WHL fgcMY0M^vɤ`eePZ5 t,FੋO)h45kf8 2&vڭ۴!FhYܯg%4ϲn>??ڵk2R 38걒)|ٿ?wʀcXXX۶mO?~`F*QƨV6Ri>}[5|0 #X4nܸ:fAE6TҴi *~30T5j fV0U~+G%7 3lJUU0 .)6 3XPYZ0f|*LX&8~(faÇ+0p8qC7f/(ОݻKJJ>O:ٳfaƿ^.z*v %kIENDB`fritzing-app-0.9.3b/help/images/highlight2.png000066400000000000000000000522431272363601300212440ustar00rootroot00000000000000PNG  IHDR^˦bKGD pHYsodTCIDATx]y\eAn/@H*3+ODAWf]ifGfj *^%ewy"x7{i}v]о~0gg=|80 H1b8Ĩ_1{ޛ8 opgjppP8@pp588 A 8jppP8@@=85^D"dږ4M;cDFFn۶MPlo4.\es ӦMKHHpD^:s'OXu Bh̘1Ν#޶mppๆ@ڵkG<71l8K&%%% N ""o;N6ݻ}N3LV/\O>uAшw*JZ}aÆ}{G񎺻+ʋ/ǛDrr2>=zYL׵еkoBh;So>!C,^ϝ;wǎCӦM2es'LpzD^VZ(((믁Yf*,ON8Wܴiӽ{4mӧOdz=qRRRΝd 17))>3mVfȮ]Z/-[֯_? " ,ѣ7x2/V^m乀R6طk&''[@N>3Fyذa{}bP͞=+W\r=ڼysBQ׬Y6mɓ١?222أaaa:uXbĉOf~gC Ü8q-Zhݺ5@"[n̘1y&MlݺUR333w V{!J bfΜ9xj9tГ'OxܰaW_}3믿;t5t>`+W\&!!a{wyA#?x֭ΕH$k׮}WsssӧOgyh4ۇ!9s NNN ^qnk.\;6ydM!22r...!Cܽ{wٶmۙ3gLSN֭ҧORLs Ett4 k֬1@RRהо}{ >oroGEE )/9kPu' ,/ܻwLmiiiqqqƍ ӧOҲe"vB,27od3Yc7 tD^Xb`Ĉ{jj*#؂o >Hg޽}z??*>>~oi׮]WH$P^s>~, 77ѣGy뭷4M,5xsӧo߾[Ο?hLu"x7dȐOx we~Qڂ ^kj]3Epp0oq`0̘1̃F}Al1o1?[o}@)SuT|||_T^zO?tƍ͆ h"`? : @eϝ;wYYYcƌ1w.g_g !܎͡&aaa @Q… MOyWqԄ ΝKԽi7o/ƮqT*DRtڵxslFݚUsN"/XFhݺu]`WFݥK2pر͛7c=zm___d5jngduD ?3a۷_|c,XQϝ;.7?p=0l$7a'ML$-_{dee=~{Yv;&&ƲgGץ(j-Z1i...ɬv]vIII6Bl 6.w}ʕ 1bĈO>u!фƍcǎ]f ^k0`@n޼Ehh([iCkAw^BBBRRV ǎۭ[7388,TTTgggm!裏TeƌCeB/mNNθqbwF k׮qFaa!+3CN6M8P(RRRRSSMmvn~mۆGz*\80uT[l,~ĉ4vNV5kʹi&jb馦(ooo_].'MTwfȳgώ5jxiڌq9.;4lؐ]{Uέ[WL)((eY7o^AA^R(ՇW$BhƌUUUFD6mu5ѣ7n܈- M֭[ϟϵ`6kT*Y%˗sq"b0fϞmu)"##?S`aB`c\~}ر|!sZ,&OZgf6kXʕ+SRRZ<ٌ݄۬¯[Ch4'Yhsq/`Yy^W}֪05|G999Y~ɓgΜYUU}\\\+ Z8fʔ)%%%>dT7:uܹ?׈#rssK+V0I^C̙36 rrr|ofAA.=h';ر7 bf̘1k׮{nUUC YrL&s;...o9͍ސwzC -Zg~ӄq9sUH-b(p׷]vlgϞ;w\ ݼysee%B3p9a?cǎ#G4=akp>w7oLVw]57ż46<<<K￳MA䅬_~tpss4h'sv"/lҤܹsw=R~G8h V$OOϔǏ'$$[?~i$IFF<wjϞ=">|qFA ɓ4mL䅛7on۶9mj xI/\w^Sv Bff͛kKRT&$$^Ks@GmܸB?c"/XxjC&7δ@䅂Қ!ǎ{--)/%&&bOdk@'v0`_~ym>MN&MtIg鯿zVVJJ$..6xP^^~֭\#00P"ܹs)>> /222ù%X}}}]\\xLjZM6f:lР׹R{׮]kܸ1W~:jڵk!!!\EWRrsɂ0׮] *tJ2444--Ǖ Xׯ{{{s:;;7nڵk<\{޽{)))lo.n޼޽{m۶ 6mXQ|2;*/ ΝCjuZ ,jXd\ݡVyݡx@@"^s{`A, (%M \Y 4 TTTXlJ(Y $@uy&Epo͊aYwD6R&,V ˺F^UUR:q`ݡQF\fu M\PN ZÂp5`#/PdV !tRli J ] $Έ I%(%d/xȨrI h*PFCV'$$U2m^ ꘜJPL>;d2xZjD^u0M6ܤK&50@VB%y晙QȀ"S 4ėӜɓmrwV .b-2 ZAUDVvE j){e,Z^ K]BPerAفK!B\BPK ";`ܥTA)3wKAdrssɼ&)01*̽2P@xƈ%^pP\Rʀ2P.(d/FJ&L e'? ׯ__ZZj ś4W#,/(+ Q }PLb-"<<;,/H(*\ r xfѼyss+fyALQn0 1f{8,/6s) PJ"O ^[ea(Ra@BrB~o PsՌ󓒒@*T [#r"FE:K<7qg .TS7.2&E:D8۷?5PЮw\5kffyAFSM*Re(_۷o,/8jG. n|i򂂦Zy.JްfyAFSжAqCh_bX` (5TBK hw0 bjݲe _ZZSMI(l` 7K蕕III^QT w+wspSnQZZ~zu:"HwSuֱfp@ P5' )Mp4;֭[ǎh$ U<) Qoذ}ЦMp=a2 _~\ךrssڵN;z( 6ud% ̃RXt `)FB!p4FQ&!_.--=z(Hpd2FV(= nR L`nGU]t:z(H'YsFZ#*CὑUT*u>L4SM\*FRXPEL(+ʮ@w<''nݺA9B;P%3cH60ƇeTG`d %tY/(6-zAѣGA0V @$h4B ODҭ[7\~N0l c.ZJwo)$\ xxxL6g;v^`TK !b:W#5+3ĀQPA Һ6s#8s.JaIL#ÜlՋHB[Ţ|ȏ7:QJ~P*a0q[P:J֙Sa M!LVGR+-w b HSHɫhK+R)Y`juLaJ/M)ur+`( (k t ,.rAC<%I`Xa8#c& ( X*,(g@ 57VX*TyB 7Egai2Nbh!$ ] Jjeᆿ"!pq T܇¤@fCf&j /t^C|Dm Yφo Q]/,†،Gg |Hb >SŨ{ErImP" MbYyDm%!@ntDTG$5^bBf Dd}$B,2Jm`Pbئ@B AB5CϘR*R >BŰb"a=b`C7{=#tjZA!7cVe2{2D F0}^ A12@#)Mذ9!B 5BR212묇։ DNR!xq[`B5@ FdTOy9i)lzt6RmUFSwYfTG NgY~&<Z'z'4Y/f]ڤ1t]_Ni:UB#J.6(C 6+,&b@ mʭЦb$o@]B?@K!fɷ"E52 t(YR#BTA TP31 DzA ` 1'PBQbjW%T =Drk"=HVFa*13 !ƏQhcr{PPf2o-¨!Ly9|iNo#rHr)?DȴBG!&DIXe.7 "c A20(ϚOL\2;2dsn&~ BV IVtp! &Skȯ2Ag1HSJrɏR%d"&D)+b0cDEE?ѣϟxꐓ&M:7 茡1:TS,~;S~£J_R= iT3uUQ#BP%ՀS=Pi/ Xi(HB~+3Ak UW;(fßTZ``cW(uN?:4 @=X)MY⢥-H@F5%h@u?!jdUZ U5RՄc%:CƐ!C6S:t0bSSYF\ТB\JgJFiSjr= snkVAHKJ7hIPe$>ՉQJt"CUnNK J%Dyyh52jPUEJiH``,@~JRIkb0Fi#*V=,'.P(J%̿@J'oF7&g`Z_I%(@5pQn"-\=b*PԕnBNKkr=?jԨj 3 3wu_4 Uv*#WC tV2C^j H.:S1Jnݺ f݄\ez2,Pr!7t9lz%)g"b";v4 {J#YF&ꔻˍX  (dbeUBTtb4E;vF*" o^Pj$)'٬eUFĈgϞ8qd,QLRb*҃ |X2]aKݻ77'ӳK.GpEQRn/i߿T*-Z}6!^*DB1Mˠz)Rɠa91Z_yDP(ZnVYiҬXݤ"/!dм W Hu:wܻwoQQQtc%z"6]IsiݺuLLL&YYYEE&ez( s{CAˠGGD=iӦ#Gd2y999yy&ل* PLˬ E =B*CHHرcrT*mҤIQQQNNQᥠrh!hm·gҤI B*6jh4޿H c.6%Tˡ(6z{{nݚhﵽic͛ aW浌GHlkh֬Y~{Ǎc*'QlŎ*DiӦK.EM0A6z!C%A9zHx"""zxI/y+92bbbA@%bT`e2S1rқ4rHÇĸ]jᐌ I퍁n$ =qb-J/6限}ӺwEGS3>̵"S J:t(w@FPPP۶mYidΝ;T{qz\qFxD;w@y(mEʉdܦMRi322@UY^)q"""Nۡ[,GDDܺu1!A[3r"K37'i:22޽{|R e*Fv Pyb  mU9@O*QFͻňxQaIʬB-ƪ*Nx!==='L+!l޼yAA!sTIBäkUT ek׮u5$$$ ?FWL՞>}ۘ5 o~쐯AUFX禮5%D0Kd]ȤD^H$inVsyC$PUF&.anlg&,;jQ$t/7՜}}}ǏO,fuD$%02N)Wf*b-/KPGEZ&"A^]j*n @NNo3@ҥKr9B駟޼y^ BCCyԠjE"AIh @Eտ:#zPn==My%z4V Bgq܌ ,!&n6fPGO`eF1e0,C~CϠzqP,M-Z ^A:)+ЍbaÆ /1aDq%J/&"%Z=D::)A9U:yNK.m޼!īK/9B]pavvȑ#1YdwrWԐHQ=\Vٓ˭mtQ%4 M LV9] uDS^0 ۷o 6dwZb#ZT!@%BʈB䅔b;02| *CwJ@1Y "/!!!K%T +<(ѭPD"/|X /`9svԩSVq^᷻/^0; ۲Ǎ7`СBh Wקŀ\] B7ۗiǻ=rp^lb/*^ػw/!ޮx tsB| F2'O$ws7pEZj!@bjπFO|k,Qlo?1J"eh,+[nmܸh,++sqqx #pqX ((HPX{L82jsq 777?8R9oz@?o\ @ӦMQq q}|zYYÇvѐmZÎ;xGVaȐ!W\oo+W\re6lآEl>}֭qD5|CCCk.G4m9ׯWl>}رkڵul>СCSLNr; l/oFwT QKGu! r+rq!dмtzsbpo!ɸ$U1xK1L/ .|<Fо}{1mڴa=.]گ_?bJ`Μ9)))@u/kIŋccc͛ϟeݱc͛7מ v7ʅ xQ}vDQQY֭[B&pСF^p6nةS'L ;wf ,0.شi… b vM[8@pI 攱ڛJa.FtE֣u!POha F@ \iEvvY'moPq޲/xoҼ쿵1(|ئM{O$YEϞ=_̜9sĉKJJ/^|Yfb?|v{k֬F믿>}׌3LjagE t>s̺ut:̻4olRǰ\\\Μ9#xy)m70̀e ԼBL\/cҥA D&"a^z"<<|޽Vݻۋ(R3gJ%)e?[e:jېf̘1i$!?#{aP}7l(v?sNȝ4"y /d8}4/#RRRnjG1V\yn… <>h4'gaaĉ tcx0 c0z'ڋZvҥO)i޽+ٳgۑIIIeee{!t֭Tng~vjßԪU+C׬Y`_A4MtGv e"//͛ݺuq"]re„ vH$!!!R!hѢo־ %%%W\޽; K [n%$$|wE/-[fw'cǎn!aʔ)֭uV``'￟6mU'N4k,885OzToooaiiΝ;y\vvڙ>[n;wCh'Nxn̙3ݻwZCH?;v-[t:]pp0S޻woʕ~i.Ori۶;u/]YXT*KJJjumg„ ={Fݽ{wǎ[ne{BVYYjkv)KP&Lݻwްanyi4Z @i:&&fᑑbV%1d2\./...a>}FٺukDRYYyիWsJ$gg皤-Z$$$JRקnذȑ#EE"km?qqq ¤wo()Jh<vAMx]޽{̙m +>}9X^?,[;;;ڵ/^駟쵤,C$k׮cǎ͚5 pww.:8;;֭[ϟ?q3B6RCPPТEڵk]ՠ{~6!C,X]1bŋ{j*@߼yč7222jUaۼys0SRR.\Ƨ*j_ݮ];| .9ҴҥK_{5@eeg}(--}w9 Ú5kVZU<<dȐ?xǎǎ[jom,E /lٲ/'˷n݊_yĖ[fOY`}Sq-)III0p$@$[~amwqqqƛoYKWJׯ7o &ƌJrjjpvv^narMXߏy5މOeչyz⵩ %[Ν;z-^9lbaaaAdWH$]Ř:ujdddm}ɒ%{ 1cط^fرc`LJ>;b rcvP-[V^⼼}ݻwO2%66?|lڵcƌٽ{7vv(^~aa?nݺ}˗ձ j̘1.^>u>006%Hbcc?pf͚ Xܵkf͚5pyqS76bg˗/>s~ׯ= Ì;60~ ӨB7o_ŀ#Ffff.]t={l߾}6mڴiݽ{q-YƍlW_}վE}նGB ѣ%jJ={(*//jm۶ݴiT*ŋmڴ嫯իM İOS$ B~۱cGxx82!44?L32ٌk׮B777)))a=XHSUYYٲe˺\?|0;'9&L0k,}O>IJBOAm޼yugϞm۶}wYf{ `+Yd2Rȵi޼ysʔ)6lبQcjqC%$$X[͛ /,X. j"߻Vͷ\InxNk`#BB5ZbىC&M؝Z{ DEEqNNN-[/LVvq CPTɣG\gW_}{BNY%IB_Տ===cbbpʣGj-ɓ| \ZJNNhѢKL٧O{DEklZ c@NsNvlٲӧ?~8$$kDЦM`0lڴV;?wwwPڵku:BIqN8QK{r9|jjj-poO8qĉz>''d2WWW///led 7+Fzz}j߿gAY>h4=ztؑ^R]p0%%%996b(m۶9s& 0 ;vXt)[A ЧOGx۶m8 o۫UPӧOXMرcɒ%STQQQ "++ƍv]1UUUD'ːɓ'sGQQў={֬Yc3-Z (mwn}w8PTTͪU&NEăzh}4 N'&s0Lnn?``ԛxIENDB`fritzing-app-0.9.3b/help/images/highlight3.png000066400000000000000000000533151272363601300212460ustar00rootroot00000000000000PNG  IHDR]սbKGD pHYsodVmIDATx]w|۟rK%=$JHQ  UJ $BRUH ( " EC BzO.erIٝ}vf<3Pv1bhX1KޛvjhÎv^;v.`vpa;찃 ;/a\y; vav^;vE/;j NWc3X\YYIt-EEVr9a;v0B8y䄄zJ;xQqi///NAEGG?x4"""99 vBTZz 011Cģ~~~x v񢢬̊RibbK/d|ӕ;^\|͛7UUU򋩖-Z`1 sٷoeӦM`/*ؼ^{L㸸>sƍ1a„wyG>yL RtK.5sܹsz뭪'O=B777\~С9s9wݺu+//gݶm"hܹ_}/ tiw㏵ZfjzNNN .4h~j?~l0NBVoݺ5l0cW_|ѻw|程BtR||HJJΝ;sԨQ3g4n\Bn6n!4`ҤI&2dȒ%K9sC<4y'9wر'O~hXkڵ?9s^`ڴio6ԩSlf͚+$Æ ?xO?wY=#f͚Ō+W\mѢvPrJ__3G'O~v.]̝;y׬YfNYf SNٵP󈈈֯_oL D_ ")s+-|||v6mBȘ?JjWWz;0 )ܽ{w56.ŕ4iϜ9;0G֭_YH$*((5n޼l'lٲ1޸qo\~}VK$UVo]zl57& s![dž@ Rb1.%d Ph***_W,;99HRޣ`S6︸e˖ D.q̠ ۷_/ή۪Ӧq>0& +W\b0ϟ?|[/\ܪU+;v,XsF2o$CX vuӦMl+56O#^z522Һ_D|.4“Vw|U6m7Bɓ_,rǎM ::gϞnݺY; !!QQQx;00ay@^֭[o޼yG2:„ ^,jHKK3!\jU.];yiE]֘<==x.mp>Bo7jh͌&x7ّr6 z1nܸaÆOr1\]]LF;v>`yyyC IIIE0h |lj`k5Ƥ-Z())nV19^?}ׯs-15jѣGƔQ8y{キqFS W8q⋢+Y};vð5 O>0/0ӇI9DAAAllO? X`X4T(f/.nuTjl2ѣG{effbvaG6,~>,!!Ç~???DׯϷn>Etttn222cR}!wa^0L  ?O?EݻwJղeKLFjѢELL ;!&55S0hܸqF|кu?S'ъƌcxGfU{J#11~}@ 5k˗/_رcqDƫj, AY̙3e?7oƪP۶m7oitRbc^V\5߮].]tԩ }NR?7{}zUUȑ#B0<<[]]=jԨr9>;w.XmOT{n+ F}`ʕx>30f͚eTn1Zj'Sd뱱fz!S1X&L!Giz̘1Vx1c͚5)))|ZE<{iVVVaB͟?@Duuq㊊̴9y$0Gg~7 _)6>*)&15& 1Z-!!$11ѶA2]v4iRUU;Ϣq6S߿qJKKF)dSɠ1F=x༼< aݺuo߾JJJY_XXm6SyJ0qvL TrJoo̪*>׵ |}} Dr]toܸ1c 3_?!4S*>>>/^4NT>|x~~>ёO?p Nf̘1n8k׮?W B!l/kзlvԀ\[.ƶK$'vʕ+VɊ.]$$$IÎp>'˫ va/;찣СSxܹsϟ7 vGpp+++@>cf>Î8W_}%JO_̙3cٲeS`qDFF̛74X\\Ltxx8qx@ ` ̞M&%% v^Î_}Urr2۷SL-uL,_T*0`B%RT*?uH E~d2YNN ǎN \޿'''OXdɓ~vTگ_?RYAqpppss+((8|u]*Vj,i 2ݽСCMA 3??괷Rã￷#gϞ։!===~b=sѣ{LH@ xIE"QLL\xA"L6 /g}1???+6+S !V~zKA*N:Նj*KDD)SVcŊ. &MrvvYM~2EQ>Λn+1.]j۾;xMhҤvJIIyw'Lpq$700p֬YU%ILLLZZǦyD"IHHmMƍu0n8ۖ߿Ei$رcmj޲hճH$=z4&pvv9rE=, GIVdLY>(jСb uݿ&M Əof5Mbbbhh(.hl&OOd\>zމ4y!!!bDDD 0'5H$1c0)dlaÆsAӦMGBÙ6o\\OjPɩ<==SCM% \\\25GKJJpJQ,[XYX`B7^k bw"HbccD5fŤPwL3227ä`*avuL 7#1\]]_n~^#FѼy:Wdn"HCP8#Rppp(t)w8tRt.\9sfEEŜ9s͛S(_tK/)))Bq :ʕ+&SI!''ᄈ:p@NZ~L")deeYm(Aqj\pСCI")<|СC HMMMII12I!77oVd(w62I!??o)sIIIzH (g<[n5c`6&Ocý{fhv!&&R|_[6gS@$'O$&&ZJG6g"5I`˖-V{~P1)mٲ%??6big ѳ322LQђ۵kWr˖-uSTs>>>Ǐ?}43mڴ~;++KR X~bq\\\]<###44ŷoftpp|@lB -[kz{{;99q)4כ5kVHP)t:ݵkך4i֡#^vZPPېIP)hv횟[35#Rb\~gB\v90Zk=zx޽¤f͛YYYqЧNZnݻw۷o2ydX^#!!PFNIAZC:%Rt̘1>[kSR`Gk;R`f͚a}45 9xyyTtHd%)@!J pAG@@o~VQQaFk B,J1P)1k VC)I poLa^k V\%P)b(Q ZJR $,1Й&bqlf $P)N"( Ìp5^o%)0Ő dRׯ{xzdK^h XϠ^z2RPI H`P=dQk?,#W)!*1Ĩ6j: P '_f)@zCkZ:O'j iiiR2RX'322hn֬{g P2"b᨞F -TqgffVVVr@ bKnR  tN(&4ǏoLbHI=eMJFXrQ~ 1P(s7 Ji OyHA-#-} D"5p` :A[ @̑ rr~AZ79RpPALG)%8ݲe b8BG-gH #g1 (Ā( ̊|HID5V%?14U⯏ow޿ĈV^sL9  (T2Plْ=`$)) 9U"dB%4~'EXLZ*abHLqBIR@\IY4(Ci9bt+IR lL83YzȀ\ SSSMOLL$`lb" ɠixBk*$)@fJ-Rb-gAg|w͚5c'"6 6CVQx#j]o΂x6|SׯGZEܳH2?b3U J(@| T`j#6~u\H[.jfd5j D-3Xx:DFF!:t`BJ(x 7ZJaf^FbA57`uN% E#*91AoGL !r &~AgWFŰ4Lv 8`rw,tNBhtkUcE먵pњA!FwT;&VjYoBJu +h`rk,V^b ڨ1t,\4fU?9GF J&+,C h*|ĀbMb 0Ck@:k$o@t|?@C!4O[``(ǑGZvb@Fc1hzCK!DXU9k)b dȢK 6Vjxay;[R-!tqq pqqQ*IRSJdAKqdw |hkA6[ChL8i|fZ A g^c0zԂtx D'ꤧQ%}TO%(WKgRHF4B Qt}!r%L"Kȯ2AG!MByVXRHCItQi\ąty۴is…'N\pҥK9?¿HLLOEUJC!m#C 5QJ t-=#BHP۸Ih y;2NUU:E J'SS~$Rtb{E> R!.ňah"0 : XF m\+ψѕP\)hBΦA߱JJbVqT 5QouCm3gASr^Yk  #GX9sӨSN^?|0T2LxR J:  "jH/Fgmݻw饗O(UC!)c*2}VS$JBrH#"@,&+ҩS'^ANu<4ЙeirNȀ̀H*9PѵkWpk׮qk NlBxR HJ]zH"9;ǭRަM=z?~eanBRR<(j} XgĀŸ[FtlٲO>E.\6 :jEx(v*`@ˁr4iFeDeqQQQ"ܹsN2K@SmՄ/jJk U X䔓Eb(TѻwovnN[׮]O8a,$eH$2,,,֭[y:(J EE ]J MmiX,ڵKMM4jVJXq(P.ŐQJ)4XR.]F6m YJtId2rί7Ү](T*BCCVLh2rE Y? jp͚51bT*J-Z6Ce*/6RJS %( AAA2L"4mڴ)U T]Net *F_i//rDҸqcp=n# PP-fOJ(sqWfP t>dob0o,,4J~UXYM[oرc9 -Z((( T|*=*k&VөEX_INN|9ԪgK/B~7n_1Fsvc3H^25W*T- AЍH Xw%Rȭz-@DRŦU }ʍIC(PU[NK7A6IX 3TVV 5LUbhQ;njc\b-*Aز5.Ewˌ@eJuHb Ih[1n..ɜ|'ND2e2B'ܼy7 9EؼhB:t&PGQX5F11Ԙz&YIjP:jfe\f 1Ԙ8b@=|zDR0G zK TEA1C  ,,L ŃtLRzeWDZF㉤a Q0^bzTҊEC~;R"^`_7ݛS "pBX˟" (֚Z6YYOHo7nժeA-*62UQPEb(|f>BڀJ؜I&#c~GONeLp܁@%ƽ֣2*Ҁp.<(yyryΝ;_} oZHR 8.<(P1%k_{XSh믿ٵg?bRB$IĵWPkQԳF99T={"G$e]YYYܟ0pԕw鳉ݴPHcjK3(MZԭ۷?W @)@b@ZֺCbE~G?\pQ#هZ6iyzi+ ! K H~<<=nvv1{ry^~7b2Ka .4/3{$I>}Ξ=Zf`6osʕW_}#sҥG"5kְ=y7`^z]vI!))il ߝ;wnݺճgO2!ѣGVVMjFIg&Vi|g*yBBB !!!III(۾ps >Q0LQéS/KХK7H Dj۷o')`o߾S0LQCfff-)̚5)Zj5aEn)ydq777NxEG$ .BjC,[lӦM_rK.#11q|Fׯsl 6AJJO>2'NL2FR̝;:zq! ޹sg6m8#_B-: ouIw}֭[e6mpo| HIIٽ{gXɓ6ܰagm޼ȑ#6̙3K,6bXD5⯿3gb;v̊>4[nM8)tk+BE l1g_ <<<(((++b H|||N>]uΝ=<<y 6.]y$vɞYvBBgϞv ;v|fk…ͭl߾}厎+Wܴi.,srNa#33,rjSm 776T*1 OZtyVصЬYXoܸa ???hwaլprr"fLቲ2Aj={[}!Cfw>T|ΝϯB/vpppraZf͚Ym`c͜!66ٳ[n9w88vqiC^ga/,hzv}5~ם;wƼХKh"S=mm۶-^_~V8^ohTzݾ`vpa;찃 )fDZRw*Eb0aۥ5Qb xD ψ0b?',ݸ N1SSmx94{e+1ؽQҧ%bXo׌}iNBߺB!?L ‰` f^Sb0ǃ?,rJ%ܘٖ$晰:Ò6'#![ v7BAAA~v7[9`ݝٶ(φZfL*2UZZj+myy9rsٕ)Eq/iRUet_nݘ I&yxxHҘ&&af9&M]veFZ"0fСC۴i#""" 䓆uFK ڷo?f̘Ν;;::t?s˖-nD N`^^^cǎٳ'BΝ;{ٹs'ӭBRYYYhjw)sPcǎݻ޽{ߺu+B\]]]+ATT[oժU+HTZZzɵkײ)HRLV\\\w }1bDvbqeeӧ׭[K`bGGǢ:]?+( Nwխ[?~P(tvv9..!:N `CuuNłRiV`̙ɓ%K$u(J"TWW@o`0dddB(H4M.e_uahhhϞ=_z`vFyэ7Ο?ر:}=ccc0bĈׯ_~=33vl߾SNXK,..9s'"##ܭ[Ϙ1øͰa^3g|6)tovŊq߾}߿tҡCl~! ;tЩS͛8@V[\\q…SN='a /|:t_|M r0dȐE1~Ç_tW^k׮ t:͛71Gܸq#==N۷w o?%%e̒:Z/;t/}#F7[|o Ͼ~&M" <~8ׯ_vm=gLpuue sssN#G8q"93 ơߌadݺuIII=#xЃ2hGzS?ml}.Yǎbv\H$jٲ!CϟgϞWk׮ n(a~ۖ!B&ܹ \L,Lh"f c[O+01P{}GuEŝ*dc ̪ B1k֬O`/8::nڴY`Sðm8kGGE1hǖ6jԈBՋӦ.oo{{uW0a„HR+b,>ݾt޽{sFtt4S~gf;!!'s!Xw6ߘ+V:.cX qqq& 'NGݿiaÆ&JQ+4M/\{ʕ+ϝ;g|J=ۜqo\_>oZb8&&8p`zH$֭[͙8py؉G]R1̕+Wrr={vcccB S>tP\>o<~~~Çg\|1cz/GDDDFF=zҥ>oF] 'NQF˱)QU^^hڷom6D]61^W^騨(pqqb1jgϞ-[YV 9`?3av ,ؿmm۶e;{lxfTsrr:|EQƹƵkטI{aaRğ(fD?RUYYٺu\OTq;v̙3].\hj!LHH o߾hѢz ,R)v"ϩXZZa=:::IKV\]*W>>>!tU) O _ݻw׳P^^>cƌ &7T*]dڵkm@ۛ޳gպ|2`֔>|Jb bbbmq֭:СCX \nG/rgff23gծ#88FYxqDPbb"if:X0e֊B١2 ~)__ .]5&t`(ӳκ `'%fM!/_pBF={o~ʔ)K @7|}}R)cd1o޼9qĭ[bNlܸqbbbll,_[SBB#n޼y/ҢEl> 5 EvzXùB!D.Ӷm[f/4mڔi+ǏM6l6C֭RN:g}**))iԨQ6̩!db8!!oEEE&>ӧO/\m&%%qe1԰}XR(W":t%0vd^jRG'Of/)ѣt &H7m4^?~ƍ۵k0=cߟ?ի줆Փ'OUd}T3fW8Çyvϟ?Nd>}meqqq۷oWTy> && v^`Sl(dUӧOȘ:uqg) 駟~Ĕ% p?s֬Y6_xn)Y! bj0N8rJ$R.^XXXT#۷1c {Y|9S ЧO&899.۶mji|MFYt{Yt)~T*U6mryVV֍7l>fGTUU(&LУGLEEEX~&-ܥKSrrrN|||mvذa ; ̈́3۷ k; =*v<:{ιޙe(_ee2eqiIQ~@AA6EcҦZUFҒ%Es}ɓkI!j9EUbWJ7z { o8]bbA[ yvd óC #J?*F3W:d۶!!!k׮Zs\rh0ЙTf!?ڳ|觏{[vGry~ŋ4ʕ+۷og1BHU}4`]EQ }0 8(CGηi4 ð)؈dc@5~rl3|ҋ/^+~YqS=+12ӗMߗ p *~I>$/4Ms?v}:&!cRy=#:%w֑0Æ qƭ[+zѣ;wT˭dooR*;UQZPD4 f4Dw/ m5hv!&`Vݽ{#11q}ٸryiIѣ[ɋ/ҥѣG%/#k '5D%M!+NߨW5 )R",&.W4V.vt$Tk虪Xs5U$ mfDn[Mxb4r_!Z|x?k۠oYFٗ/.?qDVPPǖdu8'mL)lN~6ah~/"N7ȫ[ QIY)EQjVQUu MUr `R&lzZQ G_zŋGFF6?-eL&DcI զh}7z3Q_FU$t/QgIZЧLiJeC8f.V򎈺e+dfJӤ3~5Ô_ z܀]$~)a'օT, (iA&0kbCI"hZJUZZ*hJ:JRdp1m :flH6j5)-#2FYu (iJ3]g{nj !jՉlQ<1GVi;^q;*cT*yyyF/`oo/y䏋}*6XVTUZ|(@hh lg ͙`|Vm5 cƆ(W=)$E1@1ԠEa,Q*>"#XAT[<*+~R"@*xX`in%k85s|U'vp阒@pHpREw%/__9 $kTl`P(1 I=eyj-ivF[_0~:8$LZ`|亞y+ÿ;}wouh#}`m~ Pߏt ;g.6A- 1}—eCPߏN-{3Cu6ϯMZ8/EeyUJSӿ9EQԽ7xãHsi?%R~}] wU)9û$S ;RlYSU| "s>K %N'{\ ֭[\NNzX(XY|)))a7.^?KJ*۷n՞̽=tis^>ԍϧwfw[&Gd 5kn'Nlko'΋~M'beӌXqfㅉ)nSwK5ĕP4m6kS:md2;'~ѭڵu.|/Ǘb?86k2Q=<';.='hjݯuۇẠ̀@SiU:"/tlԜ}\ t,3*vC_~b][ĄҕCtl<-.3:AcF#ĎqAKW$Yž\UVJrb!E( Ekqrh{AEpE,bHf]4|!:J̀'G@t+J|=Gɾ>P LI48}?? 1}sԸ7,Z5k1mqoSlmxPGeUr^sw.nxG !+'jyl@/9yyסg֎4 yESES,-ky[<~\;^ #8]^Vk|h5i]iMiAS)7%k'Pbg;Όb~Zs\-D3/(:A8aS& /Z# SlPW1@B.% I ?~&@s+ssL20o@ωS;] CPgj\!GmYX]M^+@J(k) P@aE+o_MԂDK]7l+@ *9STwsJ Tf A/ܸC@^P)S==J\^mX׮iSaq>Si}V~^S/ǂvJfYbٿ vӋ> _~G E=H;oΆu;QWFo:3pQφD(L_;Ԏ~Gݩӟ/G!Qo v arZ;VUg#g@QTog8kmHoC୷~G&@Hvg?q`ʽZ_"P(;͔C΀E89}4L>n[ј#(*s`Եk?'6M]ҘQC~}i (NP4Md@(o2ݷn K]ik )w0a͛`B!.PwwO͛s\\3npỠ^+wi. .x[cm OϬSnw9/xM_WO C\[eh7i![ad͗ǵXxv @g|L+9gv"4{n훴0uvtzj-x{5tT7}dBw_&>' ?_+QUg@0L MA)O> vI i?r/i|#a7d@X&hZx iZF4E6$D҈Lr9m`Wdlks24@oL5ǃsQ{_nbW5ǎ;u㩁{=Bڎ~ӎ L$[gcω?zR=xߏpd{}i6QS|Zfu>R70ǁz HoB6aubb+3K%o#>$}XW>$}?Fs_M>my;zh /wL>n+=o[2`ZXsd=_6oa/ܮvg;ݣDo&x. _~<  Py3,ݰU;oyfso/@j<>lܴpf`]qՎb=9uo_\vس.17_\S7wQl[&~rGڴ|Fo'd~g\\7n 6{"8X? v,]+M}}6!ޯO_gGiw^ ^Yk᛹`nnw;'7so u)WZp jS5V_fKƍM8w򷻸|ƦOޞ-&,eCm./|<t2Λz|%`&.((Wo݅wb{cg+߼o5q6֕MŸ8>oh}9w[YY7+\\[ `[=k6ؙ}&T֕M@ZG᜞}/r+ldsV!8\DnjS_Mct '0j`zt#2H4PI*xK4O1<~6})DcBw<^W~.%'N8 ri Kn!L.\܌"]q&nd.E&SP29M,{]]He E&Sfƚ99 @hPd2ȁ|X|.*BT(5l"4崕\f&y %wzJErZ^VX }qT*S(hRAYč(7ؖA{H%FHK9V<>W;V?u{$F`#?rG0D;=B[VY1o>Zk=9v?;J{46r/fsT+.ꗿp ىDc$ZѮԏzyn:mフtבm!6 T6-W@:ہ|~;{ZudH!{x{ŽW6[ctn76+ &ZjL-[ mM(ÝAn{P$O6Wstm1ێ&W.4qƷD;jڜN_5;ʅy7|{OAns¼0Ug<n(\"mrՋdKoF$`kEWoUu&v 0"1.9.T 'e8,Gz{9~"y8`aaAd666%oCP \ann!(Lyr(fq &\G9 4 ּ(IҴ'Yvƒ5fXzT%2Fw6SuqnINTރ{!g\"0lIS+s곩EÑo|s?XBH>Yvc;GVNqo%o~V"@ƾߣ-?FK6CÕouiŴzWƞay}s}-&hk̻uڊaG7->g=gB4DഹĒc $}=w g4j$%0vn_>:8-;EpMHAvbkwӲegXHN.x5X OSvu @PcF}Vm-[DH`,!7 3R Q A6.>|غuppth\gna!ސL43773S)dr9,h+h333k[Nac+5VX[+VVr++#gfmmQnm-5)m6fֶf666rk {{u蒚۹Sg+P㕢(+  KOE4P(VSXr6S{z o2{:.h= }W=V辬+a?= @ƾ{DlaaV]ҺkMs:O=G>9J8t؏|[}}HkXE{=H脅 5Y qc$ۨ͑ON@FMwO~s̈&>]&&f(v'f7z L2:=a ceߟ1&}ܯ'`Վ|`m.F t;S~C0/࿘w׮f7n6 Wb@w8>;!cga4 og[Buq:1e Hel:Nx߅OTi5c1ts}K*'cL^0~ڎ6 c3wo]8y[0wW͝]{<1/hӖmllG dZ^2~"alT?g6gƷm.swFs$3jh` wS!P-ݯbz3Zg0WKv/p ɏ pOn&sjիWǏT*G6΁]̈́@|qX+5mݧ92dn`nfҵ˽i yW .(4pԄ}+^Hؽu'?z)))DΙʲ+m2y" %L!X4h=WOo{#/ )y{Rvt[t7زP'Q5}v8;D.2emqv6|x>:w\ ܢCt>0fˤWEFqote_VVV^&} _&~WKJ=lג [}fSjdOC+@/0~q)]Q8,AN''nݺ酅`7,m33s/pu9O@Q"!4phj2{24hfiG~׏M-<~833377ިQ<==%&M'TIV1DULEUr6CKLƧo|nh}mW!!!Odk{mKՕmmEBPkI~I웻b oŦ/Q/JH:SnEŖ :/ʡR$s%Eϛ=站là(-ssBgW6 ?Y0=4XiLD J2Ś؈lM R񺳄ZD%-f}w4%5Pl7N_2334iQ1')DJSEW^ɑ4wz4+}l`ld`aPӤdUH^j']t+SgKOSgl#;pO1]bJȀ&3"j៼!~ޗr^M|cbs~BJ%2DANv 3=&w>kUW3EBMqU,8v:d.](ľ'#z{X_Jb1?133333S^xG&Ԟ0FmJDS=>/--M?,|IĊ)A jF-]: ڝlxO*741d8wS`3 $sz+V1T0Ӆ˻ `wݯ>nbaW=4/QLwE Mɗ2`UPX >yJIn; "I޶NX>htUTuzF]~M ;^g̫o]eOp+r9t+lJlKm]gMM=u4K-i:t!wE=Cѝ-v>DXi΋EQ1УQ$@3b >GAMqp_(V!k;}5/jv7_I~S]SZ߃GQZcF&qK~f2<}Mxv8G Hxȭ kb/ŀ\ p.*F |4(h?MDF|wp#Y5`[lWDBTB8±"O|ὒ7M3ס3s} z^G-y])q i\H.wG/%~d}-/s!餁0xVu`Bį 7t5OAtoJLIkQĹ-%r7|$7@@&hC}~Iz">8[}]KI[;O***!je ?4oJ6QUvD'3:10*@.~z G FFV4n'P'_F$("zT?WUhXsx=lXvzKՒߎXI~\lGPS[ϔo%*PPMt5\P#nJ]|r֊fA3'y[5CoLH3&BݯFᔍ>Y8'yDOD\a/~+:9k|u(id♳|UẁYt:2EՁ fF$e$>fJaJZ0i$>RR_q+()zNDU47AL/Q3O6cz$mwm)9VTU"/p4Q`onW|M? t/ dp@QWbWŃ8  %‰Z{;O5:nt/9)D;Fe὆RЧ STa+ZAI,e*qC"2Ý`^ TU]𡺨ը/s=>Z0~ܪ^*} 뫑`[!!H )”t9%UuUP*ϟOx饗:s*  T/AĪN"JgaasΤs3  KÆ ]/  /&\[2AA:\wƆ^IRCACV%wtj5v" !L. I*>eZ!7Qg{óZ'(YN)>f`oǙó`ʗt=[A)ڂZsE'~+wW7pڄT595o.W Zz] wWCW|݀5Jyo/Ssv)qТCxYMH(: ]>'8x:g7$M(ut޺E<~ (*=Fm5hห:qP=Uڰ˩|ۤV???{ :3^ ߏУ(nJM3;:F6PAdze?p:?x:%6*ۀQ0?!}ginSoS3^ =jS ߶D}OyŰwE{#7nhgW^Aߏf (~Â[Tj[0~noPENEEmsu6ԍyV  /,VU`a) C,(7rKDJUEp APnuqHK X[V^Rv<ASXF&jD @H0yz8 G'=Mi}Д?6'psu1 1',35Go J"/}Ç Kaon F?ʡRVmH ѬHpo斏LnMO |';th ̘-Sf^R}Й%^_n\%t(HCi((#_&vWgoxz~4ȵSm3-Vg`ڂCA ")4:k׮m߾Kݻӷ:Zn#|~vAA-fnݺ'OrGһ.'F~q{\3+gw|6-@Qz+AAb{M#xyI x\3|YuZ  τ,b gȗwԶ Ra!D"&AA|u&or}; tAA]Mo+WjOAA1 i.m'k6U7 RHH/jA"& mjmA9 Զ T?4yqY. RΟ(.vCP8::~/Զ ɲVnj݁:֨rDtR3ym|*G'8{p+{ 549aK!P:\"ckkOIJJz1w3fgglٲYf.\PTвeKgg'##}rf 8.5o2_'h"֞q\]5_@XJWv$]7m?09go=IK??ae 椦2n@0 5F٢S yo=1$tPk# ~ mznӷ'\ͱy3Dfff/_.,,dS:t`kk+tPVV֬Y_~i֬Y׮] r͛.H8Fy=4k/YXVeI ~L m]۵z=Ԏ# /r'? 1 `mM>غ0ٜԴnZIKcZK%hԛd"!+Q vvMTB&/7q6ޮۀnع<==?~ 99sYYlʃ.\еk>}XZZ?l'OiJ>zp nC ^4~*F*n"Z/^&rsuuUX0sC3Jӊը9m 52ZMzQx 6ml 7ZMغMJ!@jg^zS bQ1=xg^ mVtyɑWT5!6669B݃6uΝ۷oX(++g@W7č+';zFEGP~+)I+ÚW./h"!72)'''''g[T~ATAVʈSkEW1L*`-'Gg"?[痿YV]$$}޽I9VcU&O*ݻ;sm [Sk$y}FT;ֶsܼ\y`ghiv]ڧZӟ=zڴiӦM*]sאGh iɹ 0v]FM6mڴTe;NV bkk[XXnOv;LJ?'řHH |~x?M2:B<,doo ~B|oǹ$$xC/K׳ 0wrt*/ύ. I ;rE6MXܠ]7vke !xwOOl"&@FHZL@4؜ٜ$-&g?i0`lry t6%Khj-?[jY%Al3ļkj#T<G®}iȑ'jbD'瓳@'WB4y$&4d# AptjSj+Q[W7 Iy3a&|r;>/O?x-ɂ-ͩt ĥπVZS(g90Zemm}/^xի/_>v7#+ݚ6mʎ;v؅ ^~eH f%$5ꏛKD˜7~襑vz6wڧ¥^?<}XpWn 5wW@-Wld >H4n\ 0ikz8ؚ} _x}^v2C/q@Wgzξ9Kٍe?:do6ءyW Y60d9boHCB~Wm(6t9;-qvZD.m/=B]#}mR8^f髇x`lF1}vh2]{.tl|Opy!}9@1tr%, MOG8ĹIWYt%|@LjtYKqy͛NH+kTWMm8r>ny6ŭ_om6ͭߤ>.0yLofBH&iwj'xt\.iٻ#tp˸.FnX:n#pmv፥Q(gg޹s({{{UJ5UggǓn@{DNFylANkV#7E}pda`/i33~WG|]{PR1[,%ذz{j6ʢuLf3zWݵHVCQaa!-`R STx!weS{6ST{G12׽^v`5ԭcKޚzx/cGA E7%Q:$Nbaa#a5'?|K[(^= [84iS ,;,׸열>-'XFȝ+l쑚4a7;ÀD6VˏbS:0 J7Edddddd]666?sX5(J|]u͑^秸{v<9pi\OB0{vKnqim@f?jOlV~n׭dpٍ5Ny򄟇iGGGUNUmzDX:+\-p. Mcp_Q򕎽<{5Iڔrۃoq@[ئts؅G7{qq|Tgζk+d~~w`_WBy$!ەYnMP\ug3|0W[rt£lGlnRm|ԑ׀7$$G^_I=5#9M4vr%VKΣIMj•,q5jkdIH%?~ID_ / ֍ßboo߲eKn 6oBM߭-԰_'H0儡 1P$)eGt+]o:\\~-. LڴK?&4ye QɣɠpG[ر! øL|klۺG¸ӵ} Յ;[/sI0k$cec6х1KT27yԟ-ֱt&3]ؚe+wj>1DĽԤ>FyMJej,E눽Uݳ% HmUktõk׶lо}Θ1c z(_/[5,rss[zdϱu QƵHH |ADF~v'Nv kUM#^ZwN5BA~G[;^AA>|U'\G #d2M èAE H=s={iZ&ն;(٦t8; R/d2]vfBU /8:#" uVuرC ֭[tt4:yqbp\$Ns uL6eʔ`OOO XXX=zo߾ Զwg7GZSf/𨑰{~ؚk^3WE&E̎pdN>{F^3 )fx*5{A>+KLa;sMƎwì^_^,vm\ `kB ΈfU;N.i@NSs ֹ Yl%*I_eEITB0nA)n'PRLXkĞ(V$ōH'. yEQnѾ>QI AĞ8N9xF&ʠ}.Nnm.̊_ڹ:aIp)=qNx]cu%>/N-\SNe@qW}8Oҕ^=DRbxE>Nِt+0R5WNu1݌@<*?KW{ֻ+ֻYwzr޾+ fp9[NNt⥋K  r<)kd4piOՏOhܞPH  {iyn,;jO/Tw<2&< >bĠW&ԱBoޣzpj d:rܙ=FJ髜 ]̢U#]:VIxWADi&lEcr̊d)4;J)r;K7s`Ɋٝ;+nϬ;n)'h):MUPyq ۷f:> -WYA:]e/fp'Q?fs ߮aA5Ł †D@],[/O=2xd>iQ& l6vS X33h ״('=#&e42u8k?KǺ\+=;t頙 J^-Lnj%ڍۀcK86|v*fH','ѕëE ;'W,};vᦝw_w׮]ۺu+>}̜9s ;w>[ջZ:Dh A};v옝-h|xQURRҸq㯗`^p_5 Eawt5A=457_DF~vړ'Or7ѣU3  - ?+cGzVZt# ^ZTiSA4 ApgD z5a~a47UWK  R@BO@SF Dh*yaGℋ`AAßnAAX:\jAA^ xΌWaP! bZ. FU9\&ecwk^AI"WX5C^λzI~̱PSJޮuҊ./+].gcAgξ(գ}SZ׬^uRm077wjrZ)|QAmM?Z6"&4M3 YA.y2}+Pt]y4o,}FնGI%%%FsA4 zFuuGm'f֓@H1PIIw]u0e{s|ynh'1C^5W]ٳSY:dr7p,엗Fh A4h1054*|W~?{,j? b,(XU&]M&c@0ٖ3nc"3pGp{ LVg6bOBrBjͤ"P(2L&k׮iӦf駟U t4M蕇 `Hǫd$|I5KM9ݳ[[HdɄD* Wwzcǎ:tڵkÆ á+7 vw@:AY`4lj>ݿ~?r\4FL^`&"XZN:pi:i?6by 7UCz {m }yus{L#|Q{YHx;*|H {ܦ5KQ#Lz(UG:J+:Aک\+K +ތ3S[i&2\0=g5 i=!i2Z\ R k^z a/iK=~8_)EeVw9M_ڬt?c+jqdam^QTXXXTgS\Ba!|YM>ŵ .:pZk# LK^35׊VVVPjzݺuj4ov Àq50w`G#I?ptt>#>ʸI滫,L*O2X?n>~^ȸy AV{bQRR>| `Lxvrob*CIsYhE0]c!Lqdݺ#{qf\\=`Jm*).>d/Gan[MtSM-x (OO/iv w1)S .7tߒar0 C4-Hqf Xt`^  ,o Lm>5H J5_] S%SKTzlt T;Sw&:TRg 6Mv{ʋo8M%tR~fffNNXYۻlٲ]CAؠ#G+UBעE۷o׶/HkϏ>z^"^윗P(*7.33gϞtBaVKKK nTQI"$ TK+JS^>mJ4 D0X4)K4lzԋ()([;w|k#$ viKQFTmi.lVBj! aӧ%jZ0j𱽝gR\*@ RkauvAW{R1[o貲1 Baͷ>4YܒQZZRԌZR0j]\*YXXURRb`SrA4 zNrNfX~xnW et3ɩv-unHNa͂{j5R Ckxr8Ay>4mG䙠[:ZAApz}(AA٦t4Mk$vAAP<! '#zCA4~_t,! *<Xj6I'A.~׷|oŸBY? EQ[%%%n A4`U "|ݵZ/p,U 3?K,҉׬CAZ%!N(SS!^ K5 ]6DXq;AprRXO̝++n֓sIHeǕ8y^4?UvtCA ~X grC@T`BȨ]cNgAL( RWI~"E< ;^_ !Nn3 '+28.aOAnU % Rd: c.5cIv{^y.a[AWYAAj 5Ҏm 0U_;^t  utAAHCA,UAOiŎWA333srrEB-[myVT&J\AA)*J())1 A4Ѡ={tttd?BXk 0Z]ZZ_v%-QjQ%Ņl?oJS(dEAcxT*5V%Qյ& @φEA!SZEMR>"l12  HՑdQXub` RwI|ㄡ}%PC/`8<3d2_QQݨQ{zzVcAz{~FԫD^dj'Lbiiټy;6o׷Ct\>@Aʒk EFVu5iҤk׮^^^KbF9ABE'DQEQ~ѩ剩ڍJgojRXY۞#/"|5[hL&k׮3s|Q:A.srRXO̝mWFBY'~ 8 _" 2L!C%0 SBKZBTJUm  R_e8ύ+'OAe?yNzEjVSoa,l0NO<9~8& rr &d5@B=9ft ^pݿ oiݺ5ҥK\ٝ;w1ziRi˧MɃ Dh_ zu2NT*bBvv J:v,W$LA;yZ# #alB7{ Uu|Igaa\IILɉ DhkȨ/EGv… q:QiRt:}AA>"LJAAޡtAA/Tu  (t   uW ! GAA>tK,:AD͌dkQ۞" #ټ Ay5s ?ֶB18aCt >:!H(J+/TU5ҩAʡB0(:A.??_SJR*PRRb4'Dh [rB!7Sݻ';+~AXl xM$G ʏ m!K}mEq>Ϫ5D( eaL|4Dh A "4M)r33B.cr%ń[' bbXFFڒ8Y燭qJ]ET9ƫsMǟ$  bhh67)rLFff =ZhDOh(#pmvSؔ e/?5{Bu9c e5(/,/L[Uv@DoWNb?ŕEm][1c?[MpA*x\=DvCA$e \f[+r9h ݧȠ}ˤl,+~AtO3Oȥ  "h{(o!I1AyⷋumWJ1J3'R܂$ńźFbTLR(Wb?[V7i" F'mEgZAN-"[oYF> ##0J L.0W B!i[&{jH"_7n&K SQrrt./y,oxx9gI$.kf7Oo5G9KX}˂#)9p "/6tQ A23\!d2LF4P(3&H#PP`0+~Y }Y EZlM9Z\o'bG> AS1S䬞c%M ˊ_ݾ5%vW(g, ! @V>ɼR4IE}sRPV|,иi^w+pd*FAS07~dnn0Siɤ$_̠ Ciu]5sApMr?<2h_mhXURy `aA%eķ`~XpgEq]^J@Dd5|ĕ^'[[1cluSS X33h . {Q1i,:ADV$ӈ=)N#F 4CGm; /[;BR%WVbѭB0 9A/SdxEAͣ]@q*].!  U鮸0  H}A8N_ǫK+JS^>mJ4 D0 ȋt  H2{ +h A4+aANq  R8=AAQ.P! #:(:AAzp,  R_(J0N.ȁ:A?jA_!CAȫnAAIī cAA;! *1|AG`CAg?H.??_߮ST*PRRb4'Dh DE{+Igoo_,엗Fh A4h&yEP>ͶPgUPVh -$,;*E H! =?l[DoHY >޵^FFE59D*ԡCAti89Y bS"xg/?5{BD)N['H ZlؖcSR ,(kZl=EDiɊ_}%F| AS! /] R'ğHq vR&ńźFbF  "h{pQw]ah_Vx(ߤ bO'ft&/OJFU{=V9kiAjΛM0P! |ޫf.YwMZ~&b7"-cx\T #:qQ9Y 5vM"/ śki;mշ#fc[nMIXiQ>gtb@P'^'fϳ~'ǰ~ Xblf ` OM]M.<;/pO~شϻ]֭R K:o!iwRސdmR3me2LanoSKN#z$ V8mzG`/'~0k泽 @yN<AthmmmccSN@DЧ^@RLPkĞ(.H6 %NPAGR[ x($N3Oh:^ I9ϜZ2C':^-#ٕ4"'+x H'ݾQ>gDӨAѻ},U6id}Aksq]u;lZ6tv'/L,5}6Cw--өBnah`nPȭ-s99)ԄPUۚ{?䜼"hW҈P \3l~;^A |lӧv O:deOpNd|tT(g9fd̈́}A`decwOؗx*9zxd;wx F+]P#= KdlJʠXXjGeiW^~BaY 3333++K9eɚ ek  }䜳fHJ:AzF]x]#WfL/QPQ&w:SkkSR̳ts܁]10(LE:cnm?e\33s=M iJIYm|[\B(JTX[*p(|qgAqU{zLNpa|- HݧC}yW/cN[٪Բ-d'f4|,q~?ݕ5E1+xO"#):fYRng/§^2Rɧ@b>~|\ԩɯIKOɌ1D:RԤQZz{Q<S-ιE*JV[ZZ,-eO\ܢ=qWbq:xf[)Ǔx)u9 4b޴3z 3"|悰AuX} 󁬻& z kZl"C 4aȠ}N#c#.BE0'nHm״Xcll_b/߸%^ٴÅ _YȬ:S!L`שSDžO) Hqڊs:mg//ЈSAaה! =B@~~]Ψ/))1 A4Ѡ)bǫ9戨=\x9idTp["2"]@>q{BkDc f$J&8mj6i~KJZ:߿ϩSׯhڤ7J c5o4R5s >Υ%_u#޾{Y//? DhC ܹfQ XZZbd9b҆\.ouAjjZII1X[XY[d2q*)9/y|]:LA ü7o~xE([[{[[JՊ.8AޣV9WLRT*uiiim HaDeddc#8ر#FsA4 gcǎFBEFG҉;g͚U" p޽vA:_U   4  \R&m֖ߠA<}ryF*By.Nuoaa!K(R( BTvu^sssa߾} 666[n]֭҇[W.T#ɜ ̀I4iK/ܵk_R>SVk߆ D899}W)=AE c,--iD sssLfʋn*P(/_~9ڵkסCe˖ (ʃ l[B۵k_F}k֬۷]4ii&BQ\\ܬYӧ?x`ƍE7o|ȑEmܸ/m['IDATq#GsN˝4iR\j =44433sݺu˗۷o3O?qrr***z7srr*IIIɣG> vqq9r$;wΞ=c+++KK˨͛79ϝ;Ƕðanݺu~իqqq׮]ӧw۝;w^t)8pǏ9s-Cmڴ?ڷo?cƌ^z}ᇿKjj*|M4ر|N>|xfff^^^TTԥKnZG~ wN.N`ؒمg{Ǝ ...^l˭[ {nA=zV&M xEQ'Niz=ąd tڕi\>ƜΝ;P(ǎ;{7nܘ}+ v_.((P*/_t^333{dtmƌ%%%o>xȑ# !ƍ޽{ƍG8qwss{nFFF֭5kSkkLV7nܘ<ᆖr͛GQߡC'OܹĩS~u=tvv\2yMN4oo sΝ>}?ܦMܖ-[Ξ=;99yݺu-[o޼[oեKӧ߾}ҲYf3g<} V?^Rݿ ;w܍75jٸq69sepp… ====z`ffoPyǏ?~I&bg$СO ~'lٲSN:thܸ25j7ZDXMoee5vd6qɒ%ׯ_ *--mժ޽{/_Ȯ]ܹu)S\~>&O0LcDz? ѣ/_\]uGyq3"LY\P;Eɩs.r<%ZZZڳΞ/(OOϭ[Bd2СC SN=z(4kl{vttܿQzӬYN:;O<]pXܼysԨQ0nܸ wxxxIII@@E|}}#""{?G:ѣ=<<Ν[VV6f̘_e˖+++0a’%KSN͞=-Z駟6hDGGgSN5%SRRVZUPPp>ƍgΜ`ƍϧiڵk&M{[om۶(kkI&9;;weРA?NLL֭[7V_P;TBӦM-,,&L9(++ى7o޼rJz~:u⢼EC'''B1vhС[qqqRRD^3tZ9hiu4M>swFZ-Gٮ][nێjQ7~7۵koaaj:55z골2{Fꫯh0a)J~zIIIQQ,!(M!r"׮]cr4M[@mX[[{yy) . dffBKKKAg\RR^'ܶʕ+/^ +VN>ݠA6\.]k׮ݺu sssnܸ{Y\naaR ˇnԨє)Sh޻wiRSSَWL8yo@^^^AA;-رcgcFq)w9ydaaz=M4>۳wGÆ %Fg !7=ŕmѣ_ߋׯ/..mAt02=Z5C:xzz5lp2#CFx<==/_hذܹ:;;s)2344~t3x׮]`СEEEeeeeeencMd2wwdG4V---̙7lP&B>||qN4M[XXc~me2ٞ={Gqww_~m믞׮]<{JJʃ^}UB} >h E~ZPP0qĔK.]4??CB:t]TTp7S?/"//o<Ǥ4h0`M6Kgii9v؋//AAAQQQ 6lڴ7oҵkWFUN=={Ux{{߾}8,,YYYҥKO<),EEEǏsNff!C\]]EEE~!~kٲB`WToFIIIqqqǎٓRVX{I'?~<{{kذ7|]c_k &"1=tjuտǜtܙmtuN:;UI?~_q;v0OE!.Z0_e033?~ii2f̘ 6ߟO>|p„  R(ӦM[|y``k,X Y-;W_-..#""N:'N9sf~>l_~رcG]ZZ矇?|?5j4o޼߿?&&ݻ$`;w̞=G);w̝;bggT-[|駶~iAAݾ}nܸQPPMOOizݍ7^hǏ1֬W^yNMM]dqU/ |^ĉkLE 2gϞt__ߞ={nڴ* m  Bx`\.o֬ caggXVVfii-=miiFvlll4iT*---SSSrM6EEE Ɋ>|?ӧlq*JKKZ 9(гck׮4i!Dfj50j5_P*ST*J]ZZT*lNc^oܸV}0/ BBB8SzW#ڭ[]|ADF q2DF~dɒkr)_}Uݍ/bRٹ ;u|ݟ~Zm4֭[ڷo֭؟Xz 4wqq>qH666C ={6)) -Y?ݻ]v4MƪT;wL0>;;ӧO/,,LJJJJJFߟ~ӳcǎvZJU( @R6:ZZZrׯ}EM>,==װaà ?VMۤIF, H-Wnlk^V7)**M_XXWQUY2 eZwB陑@ܹ͛VZ6jhժUaaa%%%J(jԩ˗/>}U 0+W477(j>>>'Odgxyy]~]PVR6m>f̘,®>3{ȑ)Sݿ;z3RRR믒ZMa>܋/]v֬Yl:[l bѺuÇ7oё?ftذacc8pwFoZ!77^ 4PWdggtMrݻw@W_}w߽tҟ ^qtt7n͛'Lceefkk͛7so߾&LXf 2?NIIM{{cofVVV bccm&x߫b ׯ_dddjjj||<XlmmǎiӦ7xΝ;fffVjَ֬;233{1dȐw}'o]rFFYttK\\'8!W_}u!k5|r===o{\`ATT!{ѷn[7m4a„4\wyxx %tg-ZҵkWvj"88x͚5&MzL&/I7gtqذa_||mww۷o+J;;ooӧO/^xŊ'NhРAYYYF:uTVVȮ <󳱱9w\QQQn5uttСM>4hPaaEAA?f֭x۷gE3ի-Z7nٳg%'{{{ɽ/_N "/|b;hтKQQѮ]񆇄5믿?>CZMY T +ܹs͛7o1cƷ~۶mۉ'^p(&P_>?۷oGGG:t]e UCvرy_uƌݺu:ufS/>w\rʊ+?o+J0)s`*l۶_ݴiԩS?_~y̙|\Eޚ?>>}~:wܬYJJJ@ASf@5y-[lٲeҤI^̙yYj 'O 7oMIIIW>s̬YJKKnݺyfy',,};*ؙQr58uTvn͡C~oƌO>e Rf6mĞ'Ι3gȐ! @ٙA}YX=(ߜUb_0ӧOϚ5Dn( T3r/fڵK.zjnIiF9k 14SN2etT{JFi &=zpoiӦ8L6{ÇS-(r٩C|ɓg̘TYL5;:kQ'?9sTtfT#$)s-[h^x1ք)S/7;;j 1ܼ̅s&OaÆ;v( kl!dܹ/}̘1cǎZ=hV7oz }@9{QN>.]'OrͿz^{Geccg.&*MbϞݥ4iʦ]|yݺ_D&ܥsvRxSPF_/:vضm[o߾si}4}}|l+JՐXȨsYڿifZn݂ Ο?O{уyTL& Bͨ(&M̝;QGAc (USwr%ݷXЙ2OyK:uje# l $z\JXR.ֲn7{ww6m̙3TU*J5k@Tj4Jֻc{i~㔋lXK(%4n@===t2k֬R6™jiETz 2jaEjV'L4i111ZXFM*O MP-dTe\ӳCfRFP-,@f!ɨV_S?y+W>ۻwJYf&L֯_޽VZEFF;wnر7oޜ4iL&l)?W_8q"v76lPw^;vW6mZZZz…v eP!ICg-􎥫B !233sssj5ݽ?5?|OQ&VV-@Zŋ5c4ݧβZ@K5gڷ1siXt؂d[1bԒmLR_v@F}ڌ*ӌ{6ac_tgGY)8pWXeZ IJ͛7_z%syhoMQO_iB~M{osS.vTK+Wce[]IJ{4)4%;bZNjBefw Ut(oIwmH޹s4%K6KP˩◛Pme4Sy[f\<" JZ([ҝGu]_3JF?iD}Jh׮]nBCC#""D"ѩS BmtZh"77)Sfee;v8z(EQəi\?~3CCÓ'O722 =s挶ۣG&L JIҞ={w߼yw^u͓'OkΝ榦}APЙ;QǽXUMߕAAgE̺T1RYJ>/NNN$IRPm*:)RdH@;Pi֬ݻK.7ӈnTbTVHj !`iiٿ,TY ?8A x6^:UիWYhǩRȞT[z&E'^(Pw`uTE`d-T pV$0 l ~$*ggcǎZ*00/mO,krUp`͚5M4ٳ**3Aʔ dkc)Tڞ>}z;w%u%"C.()f_aÆ={TE"dgPA{{SNmڴiΝ)dAd %K<<=t`ʕooo}T#6&9.[^.yi?>.i}inaݸQ%[EI3mvݼ_Ky]񶳟{i"˸kTd pzڸȢTYemX;] ˗#le_kke˖Q'qT.p (+(W'Yuٺu+YbEfDF8#˧@A͜9cǎ...k׮ky" TK ANЏ\.wڵï3Bfp4FB~]@BͤIwi&G#rP-#+(f*}/n׮]|>Μ9:uQ!S83*XS;o߾۶mxIBD+(V};j޽Bpݺu2d8#C.NiAjzߺu+åـp {ӧOڵ+N,N'lTHù#FVVV;v0RET}'*H LT9}"2H'c0du<g+/H$ݻwїP)Ѐ쌐hhAu-l޽FFFG xd(11+4pb{frںvԉ9Cұcݻ6vN)'g.:j{PЙy= j))-EL8Hfn:}uZ3G1[-c^Й -B6 dbCTaAgu~ĘuۏӞ٠ƜY׾uctڋSwr0^7KLVӳ |Z˗˗/3fPWV@Ml`\NVڽ{#?~L#S>I!4cƌ_uٲeׯ_v)SR4dזpttܿJ۷ŋwdx &<$$r0iҤI&]q &(K#S`ۿ?A}7oߍ74^ 88OlecG߽{w۶m=zV}/Vb 35C omذaĉW\bj30(`ܸqSN-[n:fTk*00P(zyy7nd Sc!O/dG\zuǎ;vNg)AQ9 DrXܿ'_~.\~y`@2-`/ a*#-_vgΜѝ|D2dȐLooQFڵk߾}JNN57xxx,[4yu?~ĉb!C󽽽SRR|||vܹk׮9cǎ:uZnSu„ 6m:~xPPPC QTC IHH3f֭[nzmof˖-WرG)QY2dȫW&MaÆ7?~<::cǎݺuS;khȐ!?M4iXJmݺw}k׮%K0ܹԩSO goYfAAAL]-͛7ի.\H#sCVY 6006lXDDČ3Cu 6iҤO>AAAgϦi ƍgdd 0ŋ`ӧOwҥKhӦM~J)!!!BcSh9NzO]ټy~D\;;Ǐ6Μ9S&Jۂ tK5lpذa%[[؃ٺ^AJR1OK-h-Fz%.W!WO!}ԛk=/H:{K6R=l=ʼn'lmmNNRd9ʕ+Æ !.{Am .,YdΜ9MFׯ_JJ r˗/2D w̙ 60O ,^~5HM={d>T1c\x~*m˫Vb.}׮];uW^"TT&M:}?PT.˽t͛7g͚r2 3..J` \7o{M:uͥ "<==EVNz63:44o׮]SLA1hР(jƌuٳg O<5k;w,H{Tڱcǵkצ26w\Se###]mx?55566Vi,f?wyh8L{7zӳۥ#h1aqE^J?cPYYn$+KW cIHDmٲ)$2o-mҥKڞӧONlѴiӀ&,$ٵkW&. 4vm 2-fff/^d"I̭\QJ\vm6mBZ `rM/2qD-` b۵kW b EQ׮];wne&*su}_BBBԴX[[9si'g%V0`ٲe;vd|c fffGہYgpT w.,,NKKC pl~a\QUweOO"=#bdc %}ʕ'nذacooDd-DL1*_*g۷o?{!9 ;CLH8"ƍ~~~vqqqR@~]|?~xfoԩS/^)f녉MA7qbWi_MIБڛ 5U&9;<^).h >'׍YW): >E,,+Fr>|I@0o>^Tٓ^:,,L.ϛ7&99pgϞn+sr|y*T*͛jq-cc fff֝իW79 "..n޼y"( !!:X,Yy1KQ:o<ܹ?~$Ioo45ߑhhj!7=^5*))s[lA>gp-"DD+sΐ:<<<=GxXFr5!{ؓoLL̀6mڴn:l%Mv.2Ҝr1UVsDk g2"\Mvg-%0aӧO7n`ay?|0&&!k'GRU<9/_J^uV*ի999Oׯ322]V\vMV_v-11= E]r%:::;;`r233/_\XXɓ\:u*--hlb%EIyMHrŋ7o \F_Ƚ9%Șwʕ}Ȑ!DCS(^kz P-#B:tf 2BbRkDtC7nL2HϹ栦sU!;R `񂂂\c&ۦM;vΘ1L2ƍ{coiЙ!:^I\X?++ϏI$;wJ$fy "## ׯ_]vIIILHROON:D={ԭ[w߾}A̞=e˖+W=%++I&H$ZhX,>|nyyyr|̙suuݹs'M%N2$&&?C^e7n133۹syHH{a-,,Mּys\\\Nmվ}{##;w֫W̪III主]6//43&M̟?hɒ%ǎ+sJ9s4iRN;vT<<}jggz=x >ټQ,,Չ7JP(N=1\dΜ92l%rX0;wݻݻw?}tPPPLLܹs =׮]+ ,Yo߾[n1u ?cfT AeƮj,ښK$!!޽{evh4LAi&==IqR0&%!ccLP(庢mժUV[n iZRM2e̘1/_,s_PPRr'|}}O'L0o޼3ghkk;jԨbo̭>}fggW'OE 9v[)\%/ƫvgSuԩE Ç׮]th4UV_B/K:;y,Ybee5eʔڵk_v[n3g\pan"hΜ9W\Wƍoܸjժ={26tٸqKllӧ/\`cc#HŽ;{UV <ӧO߾}wiggׯ_N:@RZ(޽ rs[uرGaN*իWwܹD3fV{iii9|x)S,[vׯ,haر~zvvQ۶m׮];vw}|| VP@iucx49jkLo>n:"Sub2 h@e/'EF(B8CQ`#S1Go RuDt()U&\ (P6$irt.YYY->>!tȑttF̌۲eKnnnXXXy|y\|U.~2}.Z.^ͦV/^T*_^رcG4^a۶m3@``x&MǏWL"~]Uem}PxaOOҫŋ 6ҥK\SZXXܹsw_%݆ tҬY7 6r---第ܡCҒ566vΝnnn .,a̙~-BݻW^}5Xx@BHw'>z_* td^;wJ(o=ĉFFF-E.##رc$2W߿Μ9;wvsssvv={vn񅅅G LDEE1LLLfΜٯ_6#GHR>eggy~VVV$I99̡`cccƔ[ߺukΜ9ǎ300tǏK$I2??wΝϟZv d2Yh<Da G^ٳ999BVZI5pt|<Ez+uV,$b innnw֜K5ԥ$Uwa͹{N{FyDc3[2F*ߵkWfb?}.[ EGQQ7bM6۶m+((۷o$ڔG4rʩR\ȑ#bړf̘4o޼pɡSN9rdF=z_;jcxGATup8kݺuFFƫW֭+FYT*~'vjܸqDDF*m!pBCC?~ FFF"U#GT|GFeeemذ޾v&**t8!(((,,~033ϟ;wn 槟~?zh&M dy'\ݯ_GEEY@.yl47oׯM~~9sm^ޱcǾ}*^L r ~O۶m]+WΝK޽{/QC11t9y:l۶-44!00;f' ₂±tt۸qc .\4uV͡X)IL, rmڴٹsӧO/$Nq @Iѯ<.YfiӘb*ӓŋu{Ν;wh)YXXXHt_f|~y)aV\٣G.99yΝ<o ^z <رc˗/ܹk׮t133۳gK#xnnnjpĉEi*66vKzz_<@pBy/T[2y%::ZX)˃7p͛7oӫ$Xe<+&%%w _WT(@ ضmϝ;wϟ޽{#)?=w1c8pH%QIoWy<ަM<<<&LpڵiӦM>Bdtb\XXT]R ]vmnݦLRf Ϛ]K|~n޼٭[ӧO[*ifFFƮ]4ɓرxڵkG]zsεj333BM6ݵkիLMWIڶkgooo+uvD<;wWlD"mL׋nYd\\ݻwOTT/rȑo"qbȑo̟o醜ID3fR|Ր!C;v 8\r%L6OwǏ}||rׯ嗣G^vĩP&O9/r<|ccGh E=MLL윚?̜9L 'kOuT*%ɤIJز|~߾}¸\w}k fff&OfYp׾$1Vnذa6nؐQ''1c2<+|})T@6nҤmv֯DY*ŋ&L8p`Ҵjպ>cpB@qqq1cիq%\?}T1^rL&qpի7{lmׯ_3fȑmllbccY=QQ^ꡰy''VZݻw=3zkN0!tGGCnх_x ѣG$n]Eۃ*̚1/Dg%avVаcǎ2o˫¢yl*w}PekqKsc 򋍍 N,u[h.((( ;GT'%rJeeFuTO WUaƍ@&W[5yT˗ݕ+W,--?KJ60KOT-vډD/ ‘#G޼yӧc}1nެ[.|-/_^vΞϣG5;;MT# Eݽ{7kS???KKKޝ;<ФY?)ЧWaA~~ i=Wlf6 ӿ 6 ;yljjёY}8p`^^^)X>T*mj9::[@ $%&U_֮YSn]+kkgsM[#175qqqeViWHHӴ[{.,,|꺸98ԯ_}V?G4ߗAD6mfLuޤiFTyy R0M_xQdl5ܹuխvڬcaa2#t-_~Օt;qq؇iŋ/^xP(|ҥ I*Ν;fԩӟ)n߾=yJLR!5VߧM8ᇟx엑8\.@ȃ~Ag``aJaö;q$&V="55ի?XT￉C;_&Rg*R79⚖aÆ 6tu„ waN .!ݬ{A8qԩSPXX㓒2|;;9sL4wޡ6m:pǭ[=`hh(t32O AT`Æ ӧO~$bÆ G~)www$FHHd2w}׼ygffّ$q 4k Q&LD\ΫEE8 \׏j |>U9ku\\LMM{6BgB6ҷ,,,, IznݺDÇdu,P$+ P8o޼f͚SQpTd%h4$###۶mz .{E;` ѤJR\adhhnaanaoban) +$=F{׮rr^≅7m 藉\.7H\nV5 ˧Bo.))I[GK,_jzs ;99ڵvHܻw#G7֭[7!!_hѱcJ=/<VqĘ/tú9YY 6453ӶrW¶͛kߺ_OϾs#-=_|A,ZkOvI99sL^Fߎ藚R,GDD׿0|~|'BaPPsX!**ݻ}fffϟ?x"sUHv'sbŋALA5^333O5 Gпo:[nݥKRRR=j]d =3T 9jZa*Sm㌩SWc!UgM.ͨ{ܽѽ[Rތ޶EWE% gOQDpuup8^3W͓?˜MA}ym=^-t1/ =CJREYu=m1NN{‚*\.'g1~gܱ _o (I8̢%22W^ڧji{zRh)26.} E7QMAt_,Ҳnw&ʶPboQ쳰|ޝ i4[ NVWoiZ{xch4lB^[U8 -͛7յxM>}{[QQXcaaaaS^U%4Mc\b899%'&V&P'H$I-4]`a]۷Dߊ(Çwѷ,U FkN:iF 0w\[>h+չl]&#d>]p6 }b$6iD>\~daaa2BCCN|RITrnkkKQTBBǏ .5MBH((JIclnn=#H<:ty"5%E˭쬻*˳B{ZjTW3|𸸸0#vͪ:U'*T.,, yH!k֢X`ƍ]Ζ!CCCı处̽̚hΛ|Mҹ]tyݵkzꅇ{NXjU-AQO?VYWTa;uҍq8s .9ʊ~xXXXXX:}kddddddqjXX‚JevVV-&'{X,,,,,5St5(f` 9eD+Y*TᰉXXXXX@#Nۂv횾'ci-98-RQ~eaaaayO{XX>-4x4-- 7m^|~ wgOd2 j׎XXXXXj"NM᧟~۷T*ݼy3w^ڤysR1`D,nߩL&x̪Jcaaaaz`%]`Ȑ!SL{nTT֭[?2coo?}~(ϟ{_;v찰 j:lycaaaaa\QLgϞ}m(L~ >[Or 8ٳ .dٳgƌl {[XX`7o۷ot}],%5IF #Tbg.\hoo믿fdd|Դ{7>p@ \ҢE .T,,,,,,,5O!ST]BBB޽14MwҥuYYYyyy۷o711!IFFF<ې!4MoРbq=:hiiU~xuRReaaaaѼG^Xa(k0U$l֭M4i޼Çw1)3fBHJJݻɓ' "J=m,`­[رw_%I}{ccΝ;ϙ3ע,,,,,,y"QTL8(J}`TTE/_LAO<`$I.YsrM{Qs IiӦwܹ~˖͛7o3-[_>==ys+ IVI|,JR}UJM:ĄylaaΟ??iҤVώaAAܹӥKgϹӫ8˧.«R*F*S.P(*HVT*}~uҥwaaarСCӧO/g˖-O:ոq={L6M6eQ)ڶmŋy<,Y[S@Զm[Lܵkf͚Tϟ{xJߣ+Kͤy&N1sUԩoXXX4JJ \\n}JWCJbu3Jرc>|X^+V-Z8991⯿:x`n|>ѢEݳoӦMVVԩS߿Ϯ+VfeeR =zV)Jsmڵk֭Aώ?B-Zӷ]؟;j%Rh8N a1Vjk8QʢEH3gׯ_pS?r% G4,{Pz233捳633+҃Z޶m[ |||ZnrW\Yuֵjj͚5-[v9SbK` V*BqyU Uu켷#B(}z |>c6{_eKgb{!Bѣ6mbn޼1cxzzZ[[GDDlڴ)((1AGn݌c"oM't=Y&&&]tYt'MT*D " j8p={|6mi5 >5׮]x!m۶=ZP "0W@``޽{YU0422*>s\;}s?M&*(XNLļ _ H!uh8{$;#oa>yj!/wHݻ3>xYeRQ/@dT^֪Uܹs&MRj5/䪨ധO6lL>ѣGߺu 8:N;w._;w=ZQbsUaÆ۷̙3 JHH|iСD] I~  ~߼mo`WvnpoFW'3ވ8sKH/1Pz0:"@0CM]SfJw/ݗ IgccS=~߿_߳Qh4tuue69sFQj!k!ИN)!^ԵgϞmڴiƌgϞu$"zB8Oj"WJ97w~xѣG+TJҳ5B$ 1nG ȧSLٳgOΝ? gaЙ9)5d>>DGP FT*?`ZѠfZrdn(SlȎ֚px}c O jCRl VT B"[Rr$sHŖ ^| 3*]9"tnED#$@|I6V c]D=MوDH߳1.`+尰wD050\# #.bIWۂ8 R!hcP, TQȘi2/۷BM!$ɅBA5''2!Poi8z{!=zؽh4Jo9|6Cۏ#m\mXMl\ڈ 8z2 2&\P.T/RivHӖ͚꼯\ {*fS;Q/^}^wHԤVҕG9:6hanm7Tb1.FEp 41 c  u @+c@ڒe qSocaa071~*+'B#*ՋcW_Wz]'^jJU9"%ꘀlJ,|9JNt r :, #k@geRpIp:TU-BSuanbڷw~1֫/߫#6oojMPŭ4#3I;uXs".@\'F"P4aĉ1ƽzrssóXAvF*c>NiBرc |d18t++kO}`aaaaaa2RFT+rvGK\ٳgϟpBl2)4N4ݿe˖ݻwذatL>X cN6m˖-_`D$44NW  }CΝYYY˗/kժСC 9,X~722"4cƌu3 jZV͛7/''G`aaaaԔt_!/_\jUZZڂ GM!Oͼڼy͛7T*ooaÆݻk E2t~c%KFWC~A77;v 깚sTߦ׻wo/]x򊉉޵kM\.w‰'>z6{ĒEȑ#/_ҥnݻ ++kӦM"h0`@zzΝ;Ol۶S `\́YYYծU òef͚9tPP(-[l2W5 K9Nm۶9885*%%EI&>}z֬Y:Z*88[M{|>u?~|LLnӧ9rdԩYpppX`L&{!>}z…ׯ=zt^^ƍ.\pX}RCa^I'OׯѣoݺMLyXMBͫiӦ4|z1޽{cƌQTh|O2˲h׮ĉ\Bp# i,c̙1^x1:tHs/'L~;Jd斜|͛I(uYXXXX*t]vƍ[n"-p@D"=ztll1cTXCq@#iÆ ;qℛߕ+Wo BBs6aܹ<orɓӧ(5kߵv<8~xz2&44$??ݺupܹ/dz"XXXXXJJ:   B@vlNb!T#Sd-$Q'_tɓ*2\, h(!nlFty X|d'zI2xYɞ׫W߿$c(Zj%͛7^}ݻ'NԽ,,,,,B$ALD>d1-T|ܳ3@v+bbn P:ZTB\.722!|XdW 뀬!@V{_/fqXXXXX*c\XXo>dcdTTċ\%F9FjZ"k!:z[%Tќ9T[kd#8}vԩ% Bڵ ׎L&{%I-''իSSS=>J/  Xq:D[Ѧ)$()A`RgHԷeCGB!hjiڶ-WJ=fIYRiswV[>uJs¢>/x$ KQBDPٔT7:&r6.ۘ!,xo5!sTu\΢"2BV?1S ShS:9)IwTѼx,DFFμT*MMNNLHܰ荚#IR TF\P(>櫷zKICM[+9 dOg+q|!+p ȻrC3Q!EY 5-X  "NFtK9B"M#36Ĺ*eXKO+PT _AYꇦPl{/ -jaaaaaђ$) ?2Ж|533 ƊjG:4fR s$N?G1T)$ GZYYY[[ѠAVjc֠/&)R~t9]jlc066N*"HT"qU`aaaa 犆G5sW\),,5jT:uLx"g*p~'rTǏ0`@hhhƍϟd+N:N*ę 2:OJ ILLܻwo 'YW8*g*p~Iř#GN29p@ij|GX}J{Vrush(mm=,}RCtUUWbu bر2d˗/q\τ Ly5kx{{/X.\HJ!T9~)//o޽{6lXDDu5Yȑ#̙dɒ5_Aj,T(:׭[z;'''/7WVs\ccSSWmrsĩvmfͽfBtMvc1f`L4ٿXXRjB=P3UH!C8qٳ:tԾxo߾}͚5s8sڎ=ZwhW^U*aÆ=z4(([n :gΜ~VH[^vqJJJlt\^Lԩ[Fsa$5iTj˟c5 EQ4ihFCQL#I*v:TzTF_ة_D"IHH+A^^^Z244,((ڵh4E/^4nܸYfsqqJII)xѣG͛7711aILh~iZJJd2'O2324nEj5EQ5pgiNNH$"`L)0MӅbeaan!颞=NI=V ڷonݺ{3FRBȐ+SN%&&ڵkݾK.رq_@x$j0E˖-[bB())iΝaaa>>>2 Ci0VP/_˞hh>uTNs_Tstt4baaaa);tR?նTik4 @o?KM#P8ɓ'$"2xa$egdm@2nXXXd2e*$T(\6%x>uCY{*2K*5з,,,,,5l g ;i,5 "rEp S%  !Y$*.fA adF$AaDr {?H"X )j{͘(]IP BH4\ s%ҕLxRx / GR}u((1oiF4&N vHd5 XAژ F`LeafR566з%\#e\%]ffΜ윔4gΜϟ :6h.Ѵ6\M%U4(m0MB06Q  U+ @-*@݂QaPZK3}?X>=F&}| Oz 9q;RψWկ_ӧ{~vշoߌjv!C1؋(IFr6GR{{{idGkwQ 1+(|?$ '~,%\P1ЉH(\k'Y8KiZߔw-$IRU$IZg#XX>=togّ_:ujtt1cHuѣG}}}{bb"JDW[dgx4~AdZYYM8gϞا4%r j2W9: h^ڠZFt}CD"YnRdν\CZq^U䲰|$M+QI/,,Ln#eyo㋁ԭ[722R߃-ٽ{?>tPkkkTگ_Ҩϟ{yy 0`ذa˗/N1Aq TU`` -[v!C>#s QcF9gΜ]vϟ?,X@IAqD" 411߿ttbԮ[W_q7ccc:u, K:$swwiӧ~-[nܸ{iӦΟ?ÇcƌB##QFr9s<{ٳgIIIfrrr:yɄ 2dȁ 6ܹ?d1xG}[$D"???CC_~s5$[?z|U'lxexظcͿMo/>ӑ =. (n}m=#_5R)y<߆bFSPPǑ/]400󅅅666ӦM300*w&M~iL`<;w>|xrjgϞqqq J 知\ts~+++??Ν;FQT>}X=WCT*.h<>u۶1/^$ǗYrpt⢛(iFr?,B&Լ"9y|'OS߾|~#M94o׺K} {Me^K\#QDŁMI֝|m ےz)=\TO j۶mppiӎ=:hРׯ޽ۻi e77?xK/޸q W-Z߿D'RUR]vBė=)[j՞={JTyyyR qqqUe$I֯윞Q\5 811Fc$5jXd&b\ԩA fz3o츑`gNGQCԩAC,5n' רvRmSꎴ >HQS18~~-Z)Q*T!rs6`KBQ_xs]v]~}РA_nٲexxv_ۺuk6mF}+bLecׯ_a.332d2L&B,zAP2bV5 Paa{y=jҾ6rr減+\t:^3g2RTTIż y;L t9 S0!N:6KSh3$:{{4\(s\Aw%FZ&Dܽq @ə¥(>oa߀(>M9-ƽ[pR WDť(baRjc´T&uڭIE=Fhq8prІK_uKM~cSN ?u:wk׮ׯ_۷oѢEohO81z;d{-7D".2Ʀ* ΤItsn`Jbn桹Ǩ>,Kҽ!w/uQ#Ւy;8A{sj2a)Iq[/*~y=.-Z̃'S*hsJR|P!"Ii޴im1ߪaӦM7uedG& ݛ6uof'( Evܛ:e(+R5v%yGι^( .V W(% Fa&ݽ{w޽ץK$ɾ}[PP[nm- 2"$yd{偗Wn߾;/.,,WN>}ԨQݻw|ӦME8jb ăt̅|#qB5hJ}Դm۶wQ?}&Mիgllܶm[Ryܹ޽{GEE5htq)JF5kֺuǏmڴiܹ͚5cNJrƍnnnwܱٿСC?㝍͚5kݻR,XݴiSss'';v̞=q{ݺuڵ/f̘ѭ[{L64M{{{[[[[XX4=w:2:!>^ה{*)1Q=-Tj_;'{IFZ %,_fqv;"8[{ K=UXL]ujG[5EdzzӏHIrlzN VZR"nEqrŮ*RY3Zjر$IWW׽{ݻYf#f͚2iҤ˗Xt2 9Fn!^y^'N LLLѣgBBBƍ .\Vk׮.[655uĉ#G|ѐ!Cwqww9s#bbbƏ_ [hMMM'N\`X,DLnݺ*''SNp8?~|XXpfϞ=GNN֫ԫ^&6-*rqs+q#aTRu<_8:R2qumw;8+{|ßzтףGu0{pjiOyG!=F͜9O1R;3Kjoq.dG& JSʜ,MMY9-QAO@XI#HuʼnE&''SVaƍ?oFFFmݺ!JQsLP4r8{Ǎ76lآE"ƌRjullСC嗸]6277ŹE1q1)))z( ӓ'O=ҥK...LgϞ'Oܶm[LLLNNXXX̞={ŊV+V(M6εkm޵~-[jEg1޺u-\o׾VqfV(7s WQQLĉ,y;`(ȼ}h4w߸]rmҰaq̿<@`Ӻ[w(chvѽyP+O2mG|.apIa%JwX :)ZȇaaÊdKG=߾T5Y"Dr!;;Ύ$I777&˚GsgϞ^:**ٳKfׯ?yF?~|,%(#vI@04M_z5//O>ڵr:u4rȻw2[nuѣ^^^7333fxzzs8++Txo\n.]5k6iӦ@ffs^ڸqcXh@ f #;s-[ 8pĉݣi˻{nxx;|uHҬݻwWu݌ؗ/ &[`*ۋ/M+ho\%WQ%cR^3=|:x@d fzzzE-] ϳg' 97WBTlذ'OTH$yv6n]tuVdddllӨQf͚|r<990++Ύ9zСCbxȑUӧO?z#EQ2L辧 -j۶'gbbr޽gOV,};_{O\=gm05e(ճr$Cw3 @U8R(ke7VHm>jUPm#r.nԴ̖_["3<捋WWB,zs&&&nnn.\(ݳ:ub6Nn:*z;r<''GwPT>|ѣGpرaÆ>}Zeee^ΖJ9cڵd۶m۷oߨQ5jb =:77wҥKݻwСÃVXvZ&pȐw5b?ӧO7hРŋ'&&龧Ru>}N:Adff޻wc`v*74.^;AK}ߋ*ZJʈUښL5I:##ݻ׮] ܻw>y^srr,Xp?޽{'NT(A\|9sfʕy5nܸ-[/)_D= ,%}/;vȑ#:T ]v-8x`A|||-O[;jd۶m۶mm x(77wԩRlݺU7&WYTT7|<>HRi %,Ys T*[jOv,M|xnn.A ,hժݻwKrF>z(lڴIфΕH$ڵ޽{ 3gǃbbbt x<cǎƍU*UXXF466.apܹA *;vi:,,LD~m``!CR)Bhڴi]t ,CcsNpttdp@AA!Cbo|p...?pȑ#njb &D!Bh̙f_zu*jРA% :992 4iҤׯ_@kpԩSL2UInۗQ9kt,,,IuH#F>>&&&[l9tXh:SD44%:a3gNHHMӿT*%%:X!(52=Qcȑ#GDڵ IӀ@"Hٳr9csdW[qq 4\@r@M#""FillgϞ{"sGgd)Lgʀ@D11… o߾M$#:Y1Ujd'#KbbbA\\ܶm@#~pBEgɁjB ȥK^vVDd£UPLyD7[E\\P(}MRp[(2Gp?U5r0"<,Æ ++4xr@FζD[K{)!(:w7Ն%";ZS}}} M̰^K 5mVt\~\\\ }0_`.9#((;tP۶m=:nܸY Y {+F a98.P]5Y_ 4TH \m?;0@JCajx7Sي\M{'B \BuDBdƒ4ྔpoY@% reR!@"=B$;dd!@f|0Qah7Rƨ "~f2) ASfA3>XpRQ-S"WǠ8"I)d.9J"Bw#khkd,f$B xY@<UvjLZy*tC4PRY|2A cА P-VdP7S\j1,,,,,,yGJj(ÇSz.]~Wy-s%''w_LLٳ7mڄ1bDhhhQ?f' Q=&DȄPX2]Fɩ:&ȄklB)rxRP @"nO2t% V2S +\*KH0QC"]NyM72cS@љ R{Iq;"ɐ8_irBGsd4)qJ d2]l E2P7%]A2 d$v3sqƛ;9,!(1F@HIF2p4.60n4@QRdkd? e)FÒs|\4Q$1 hJu<,2h"ˍF4I%XXXXj8t) E1j(;;;//#G|snYJ(fNe%n)8E|&:mDP(E-([YW+R @nAYT󩆦u &@MCmce,4t|~ӯ@Ҝ)jlU[2PRAc5h!'t5"s9%(Pe¥KJxrS`kW^-̐!usAK52+9TkPQAU33d)2]_D5hLR̴WR%hPQhycljD<o1oDfTmQ7h䪐Dh(o|XXXXXޱjek̾x@````mm w.]V^[Ɔ 1u'f .&>$ l}ݔ)SZll2}&C.nhĈG9$@m6hܸqfpl>}) +(T1hlNuiڴiM6]z5 3 86pq3o:*:6o`6m|")7M$`g2 u>'zxx%jUUCXXXXXj&UI>zKT6mڴi`˖-<S|A~~i&a_Rx;rH$ɚ&gg]vM8W^Yr%,gjVs|Hdxx|O?nڴ ֮]ɽA%33sر˖-[jsԌ CCCwqCQx75?i=칃@ :1##C +˗/]=S\6PԐ3gΔd[n_{2pvțdU}zz#f !P8}cc#7$SRR`͚5Ç׍k~1#>>n…sNV[Vk||?b޽{orӻ! $$^o֭[Ga뭷|}}ᇖK\\7,\믿޷o|r!c`/oFj֭xk)Pprrx/3g^x?/ܱcGiia?/kjV{ 4諯j)888,_|111Ǐ?ydVV_|ZRRҲk>=C$7oqTUU-^w1avvך5kf͚e"ûwKY3x"뗘| I/?fɒ%x7^~]"P?e˖iӦ1S.[P__tիW3b<ש?o3#Fϥ_ZaB=z:fWVuMg͚|}|>/N$jժT6`zuuuonGy睌 کƦ/yfHH+mbsqqa;o޼jhh0Lrj=c OOOiajjj4y]fbƍ޽{h8Iwc=ֿ~? ޽ߟVf_~寿Jpp8篿/y'i:99~8q3C.[^^_R__?gΜӎGGǛ7ob<שF+ 4Xdo>Oy999]:2LH{Çϟ,rTυ ]~02ڎ>|˗/ܹ.%%0ׯߦMq3 ܻwV]]vZM =xkh)KJJV^=dZ}̙m۶1XUWWWWWϛ7/""޽{_|E3뒔DKV[[5eÅ[lqƧ~jX$={tPSS3v^x0^r-UV󙙙:::|>$sm۶Qѓ'OH$|/BKvb.+lmmoܸe:K/-1(NNᥥeJeI'B7uƽmΜ0WWgv?<< 6v50a!8@ pwwק b?~ĉF1i$]O:Ob}VW_}5dJuƍ>5%K555Νkjjڲeәlll1eʔiӦyxxL>}ٳGCM:uד&M8p`PPV}衇\]]<<<~a@`x{{{xxx{{? 'OfM1s̲2 BCCwŚlԨQ|򉝝ݕ+W4MrrrDQnݺ VTT;믿vpp.9 :ԏuc=~||httՑ9=j~tcَ?N] [nuqqaMp֮]X˗/b/,XSr\_~ɚlܹMMM v&Lrpp 8O>yf;^xᅣGҒ,X~c.]}mvA߷o>eʔ`cNNN߿ƍWXaƇ:tdee]z?uSkÇ_}շ~֖c{ذaǏ?*eٲe/^\d vӦM ,79s֯_?/Liu}3k?{0) 7(7򈎄e=gLs{po===juSSȑ#޽{̔w;v,˥>aÆUWWjܸqT`kk;p~_~#.ft55-W߿s\FrMUaҤI .bU -E6eeeZnV{wss믿BBB֯_c?6om{{{Bq…>6̪Β &GGS?NNN@nn~TS] zx&L^jV(mU]]3`@ckے5Qw'/o߾% 3V_7o6553ŋ̔yddw}WVVC~555Cuttrss_y9׮]c {a֭+W5kuc8@Mh4rիWGalVYAAkF˗/_T*YSʰa󋋋M_p kkkY{T*ܹsW^j[֤٭[&Nxug}u^{ywwwWW:ӉD:88:th…HuuET*S}}[oelDO?MHHsvv>w\tt2gC+Vpuumhh-++3vm۶ZO:,,l߾})))˗/ϛh[Κ5/kg}V$͛7o̙˗/7VTT?uaaa'O޼ysII u։'6mԯ_?GGT࣏>-Ϛ$I\cƌavZv9[[`R)JY*ʕ+_z~__~=TaÆ'[?Xl;s]B0((hٲe\.{wY#ǏS^|ż}m遁&LXr%3u/_>hР3f|'?No޼IݸmΜ9O?4_|e2B.VXA'>\UYY71A(6\JY]y UUUeddܸq+|q 'I?ܹsYӸ 7n3f̶m>'|r„ ̔Ə/O:fӦM.]Zlً/2cƌE17~7iw]'p8>>>[nepv?;w$IO>XS?s, lܸqӦM6mZdMVV73ٌ3vekkR^^_f/;~:Y1;;[ 7nΜ9 .ܻw/?W>q<{)S|wWr8aÆozNNk]$S76tͬ,ӓ2B'ҙzj%޿+ak׮mܘ4waÆ;;;֪T#ްaäI4MJJĉ^p8G 3g?L$.P*w%ə3gƏϚĉOjeee/z7oև鎎}?sss}Gkonܸq̙UUU$I_pu^`rr23\R[[ZaÆ'N899rZ۫+W\r .0`@BB¢EX~@`ccyС肂!C0Ç?3111gݯ۠A"""Fo߾YfOWԩlyǏPZZ?fB=DFEu&.$I';>4߽\2z~RW TVVR7onڴɓ'3X,NIIj}?_]*$rKKKwLvg~ʋ/6w1xNҥKG=ass'3]~{;/Z5Krb={:uj֬Y:{B`Cڵk~۽{RiSS޽{'x">>~Ĉa؜W_yzzcII3w_rɓkkk/]2u1{j_{5___ZM{ޞ={GXQQQRRWQ|PPE}w)  y~ .($I*;lƍ3gLKKM_peI>h663͛RZ71Q$:thڵZvŊqqqQQQ))) !^C:>tj{r\p$iIoaGք3gΘNuԩo\'du 9rW_}cǎMmm˗,X0{lXlΊ~ݙ4 宔ӧO/]zs!vԳ%.n 0#Š_y啝;w:888p0õC1;wLLL uVr׮]E.] [|94B>nצgK;ҡ>BPlذaʔ)~)Ao|򉏏ÑJ<_xqX޽{yyyԅr2|W!Bؽ{ٳ=zaPΚrܹgϖH$BkŧnԌ>LHgY` n +**۟9sfƌ&2Y2q= 1Cк"_޽avԝͦ 1C3 ۔!B ޽klս{,L0K$b!f)C3ҙ3Ѻb!fbm!WB!3aHB!aHB!aHB!ᭆBotwBևt!Bt!Bt!BΥCe]P.ǐ!VE@Yt!Է`HPOv-ꅝ L<^O[3 1C3lG!B]֭[w5pIII?cӒa/B-!5h4֖5Jŋ4ڵkÆ "I.19::[u}k-3 1C3Hm];ϗt=5*G ׯpT;'~}y+} 3gtss3\P(_ƿwOVwqښtB3s2K0jwN0KW<0ڕKGWػxesBv0R`ѐB}۹s窪%Ǐwsst4xӧ=zt'Ow$y!CQKćQB̐J|B!xOyl ^+/_X+s8^ 02*Ǟ a\L=ztuu5TUUUV:$IׇZr֭ӧOOx#BoaWs3 sU?y']~~?6] o߹hLC B4MUUP(4\x%֯5Y(=zlmm~0CybCȑ#` Boa{xL jpLރoȋ9%sJ!B&TVV.k(--0swwy&ukYYP#C:Bx,)[6qx!ZWWQEۛ`HBnnn̻:|횚4\.˒QW! ׯ/>qrr!P߂!B=u!Pcccccc\j͞n !3ݽ{*!f544ͦ 1C3 -&88z=nܸѣGbCo2QQiZ e1C3 1CK2h4/vvv2dȄ  2m4곸]Bmh4͞={8yd>OszaHB5m.11fر!!kPVr8'| B}t!kp\ÈM~!sM$I@pBp!`B^BrYix!;B'" z([ IR5L0KB߿ݥVD4#yV-Rr[0%2+DI'aCl=qf.믿ڔ5`nqƍ7ܹ?B懊PA+sСCr,|ܺ$RQJ2,A' r -Bba;O=B&J,^>%0&&=%?A~LFr-@~%Ms9QU[k:s,<$6=9gQk(dI1y<"G*2(-n[jV&ZoSo,G#>c}47n<s$Bȝ;w92|.HB \.˵nYrnOci"iNzrdr:#0"w$3 AGu4%zB֡< yrm0ce( | Gy4Qj09%R[k$2\L|\~lT8gR a|[(/@[I'% @!mXm.<{kx)Ot:;;6爽t޽{Z- AҡL@Vϵr - M:Ft E!Ĥ8-?#WϰCNz"5C(,<ުkZR"]Haİ,yJ+=ijm*)UM! dlTD'֭,/SQ s3zE1)6@QT( C0"Ri>@A룋t&ӰA}3uo LH2<}| UY, xeҮFijj刽t$ZQ=wPVejJVՒjPO]r\VV"{ !Q/TD3R<&ձ뼉bf̋-y 41"rTgIKnlTžyr*VW$sKEȩA5eh"B R!&K>i D׺%ϖFI H4^ BĦ7oi5ZEOI2 DArFΊj>a>,@& P %W#Uq9 EFZð^<"wM*N5BqzlIˈ֥aBH<'tQӚzWKxnҥvbt$h7Ggoo3'_ǏO9sz5C:jIV\3~w[[|T*C"0]c9cSQ^P7ǔQ~E@~yfג+~ϿUzh@~ bZCFqq@`XRB,*c1yK[ +rDr^(d5)?&#%3Z (zé{$ᙆ #h_fz<ԍc2J2K)i&  R,,L,cR=庺:{{fe[%l2s#64QQP{nw{ッJd@0ӹPC}#Hh4j{Ex~^899i4֤j5j IjܸqC 3fF.Q2G+›2JYxTW_8Wf%WXX݅s+^j5֢j]]]i/Z-QIX=z?j5M@LiK_8V!.88;M}}}}}갖1c'{|phFkV]|t]PC: oMԄwz UUZF Z.B@ꮩ'{” BdVÖ_Hߐ d-޷oWzxk48! Vu+a@w!C1(L mc3:ȑ#_~.Bgoo?h 0x !d -33cx+p||||||:BݮP` R371 s!ݽK:`(0$_!BB#1C!xC!m@0B!/y!B=vi=t!B=-sΥC!]tc8B^jB!z> B!z=+m B^/Ȫ40iz|ըRJE2E6RDkr;n܎gB9RQH.Kug0L$Mɲcms]Vd,\d4P TvZXI52Gc5:P覯Ws\r*wH^IK˴2ba62S2lÁWB(OKB&ږ+Lٟ yrk$2Es!+խeHkɡ+(egJIEF/!rS% X"K*DI 3ѴaP;H5t"W-F02"R͕շLw؟ 1m(gbc+#>uBvB rȑy+EQAX' ᗔޮL[ PU)*ɕtQ %E rdf$GEc&4`iKOՋFJӔ+}ϓ$CEMP2$p*PtN2%{̆UǤǙhZӭ&tg>Z5E-frx|xCXr ¢#Ԇ-)C< i2)UN"W\`cx1o5`HB&-鱐RgӪ x! |s/#终谂"XAE%rפ6TLB^Gں1() I2ǦsUFY`\'O+66GkXEQLh-ksЋIJTuB [դokD#T!g@?bn֌(/OAU'O+H<UYyۓM@V! ^d|(E&#mI,r8\* Խ Kh9+:])(4R,8EU^TbEH!=9MRF nضk`hq9P嗽_%xi(j-X8=ְe@@DTl? vmxDy WgT  hMy˟C E' 9<͵k)*>~S*)'@hl}`sBYp-[(d.=EϽ9"r3A,o%a`hb2 SG$&Y9Toc<DNvIlxmnU_("e%~Qi%+ BɆVM )'@вGoxHM Ƕ l JUБA^a`hbA!L?b2Zk[(UY}v""Ѓ , tLOP#rN"j߇Z6MjH9vU[X_3?iJ |UusŎ8ӗ.#*jd$D"fǛ2MK3!bi#Vj@"M KcjciE~<] 'I3W"CÓDgp3?ar)5:&&=%(B}:4::+~lz8ݼy_~_aÆSb/B(?4:B) O*^H1XwVc:Đ!ЃF [#+\\J҆U B!z=|+B!Pt!B.p8aHB!a/B!PsB!zVt!C!p!B7a 0C!p.B!P/t^:Bއ$Ië 0C!0C! ;p.B!Po}^:B^C:B^C:B=FR }3#x]3#MH^G2aG$KBGw@!D"Q%i9K@H!d!2F!G2|_R yrk$2\,4Lfz-P^Y3GJIt'z0 B1LUX<DTcYckcrO(牉(\C}.SBm (beDy ?630yL(}k_H(牡`r(/2Y 4рIKBHO()eᢤ11),RΒ4浥(/E-ɵxԈph\t}!B!8O.&r"UXwT&* 231ߒ4JJzwƍ_~%$%%M:^B'&w/Y!y0C!d@AY q8}!B!PxE!dU?&U&D2eV%r׈d ,\ۦvZ]ঐggP$*$Q> >~ xZ=ɾ2̂ |jwhuuyϠ?[%D432 uM4sw YR!rZUcۖ꺬xd&2"5洭 X ڴ4}@X}-_ ~lz4⬛Zu72MTlm!hJYxR<**DIi8AD8=D-[q<0Mqb^L1Ml4ʐ2tz㬙2IB^@.{EV1-v5Զf?VRY 4ZRsɖ4Vѡ#:@y 4HLoP]}@b0!9 RF@$J (L $Ic^B)dDH`܎r`cGFQ'^^hӷtri4%x0yLDei{,}gZUXA~(}3l[ ÒbPYTD#> )<HAr#m:.BMMLXB7jII ΓWCjHS+#-JwW X'oqv剡ryFLhl<9( دln Nf/ /I]rIHulLokEI#Ǡ[Ec%xFж`|@<QWN#(%byF hUeS{G$@4EW]оWZTk|!bNE$EB&! KGé%-OEF'#cDNQ?(#b[́k}o4 Bq1$fDGh-MdxhTPFx説)d,adƊ21<`^=$"Q2#4Nq<== oOxu=7VK&ZD!U%nٖj ,[L|ܧۖQXLCIJzwÆ YYYTHp6o, Y.0qOB{2@^B4i5\plln9j*Ƞ $KJJ=YGm-ZXs+hm?*,mƋLNUb%Y֛ ohlZ$v͚5)))_v $= oذAixghcG=ht܎z7jp~w prrJHH#Ͼ7&YOB.]Dj=K4 ԛLzu{~ urs6-_g\|=%򰈯p{?Y2~S`7bq]]I\.5HZ֖/YZٸj73Ç|j 6̊-ֻ|Wƶ Y[>>>>C- 8vaBٻb^pna׉ӠXV{u7D. oIsXP!bZ!b~ΫӍ c/*OPl,sТV:h_z饺ZN3h.xyy-ZH&j6;G>|Xii%IrI'Lvn{Z-z1/W;_zD>7 :r_3C|q-عq`\##V1k%q[wǵZ[=?|by{" 1n8OOϻw644߿'Oly"d5F`#z5!CmHԙ3#mVk߷b0x5y+w<ºSek~pNwĪ9[yT` svL\۲ſQ/GΏ1`rSM=}7벳7oI4hP n>G\]YsQHtwe&X[ŋ$I~gf[!ԧB:3%pCDiӤW:q^;]5sm"狋g۩2@P|Fw^C\.u:Ns횑ӻoU<]rZΜ9LSOQ60=Oy9v^3:-? Z!ҡO8:2;OF3h]t v+4g)=1h \U̺Q%_t?gϞJiJJJYm핲!C755 2_kZcm{o'C:ԋ\z--|gxV"tW+54_ևcJ.:Nen%b:qk>gkj US&ҪXi^,i:w1RT-Ÿ<ϛ_q`K6;X[njj/ik#HB:|+]ע6k[F__OLn盓JOOؗo[_ݘ/K U۲_^h~h۷xJm(i7n ߥg@nj.Zamyj-zP? 2쓒N5Cc@Ǐm5YXi냵ljt-jժUVuB | BR\&Mruu5|Lrܹ 6~UD[Ӗsm4C;t<.-ꥢ 1 !$^{nܸqe...7nظq㧟~k`Ga#n޼Y\\ `g:t!3j]mO4zh8y䍫׼H1ȁk}p`#رf9!dKD pnPv۔;XCa6ϻkR/hod^1WPӛN-LّZt0v>V;b3ki,i.ʘݝav* 4M6c~#!ӪT*ݗk̘1K.yfII XpHkvL|ݼ[)2q6X@|Z%1qjd~fŽ2ZVl&?zݙX&Om:0X=r2w.X@hh]_Z.;Ƴ[sNQoOP+_1m3gbh)u#c_KcXJEmVmZaZ7. ZndgaAE;tYif ]@=Yhh۽pW[[{ٳg[+>֣lŌ:^ZK僃khyxѩ?SKéU:YiMjIZ+c~Yc=Vb3g : Ve#z\pvl=؅ jkkmfϞݿݻw̙3ݻw5}gmLï]gkؾ[̣teïյ0Τ]VUYkcdF b:ٳ肒.Æ ?[֭[7`.7߼wޭ[>rgy{3CHO0svב誇=ygbC?;ɻ}@?رGwss_͛555'OB"B!3N6dȐ~;{QIENDB`fritzing-app-0.9.3b/help/images/pin3.PNG000066400000000000000000000447471272363601300177360ustar00rootroot00000000000000PNG  IHDR[σsRGBgAMA a pHYsodI|IDATx^i\E#" yIB @BB= H !5VAEd_e`UYUAP`qy3/f}/T[]]ݺN@[ԩS:ubŊD-[~4csl(nח' @(@۸6@@09j j l#F&@@0vHc T+l#F&@@09j j l#F&@@0vHc T+l#F&@@09j j l#F&@@0vHce1bD2tLȑ#zQE`,,bc_;7v!裏gr'l% 6*8oKϟ]Gl#zd]_qkF[ߒ=ù"F`e`?- pV^F m]hQc]{5йկ~w]_p:(㚊`6muVt]fM0/N>dVM {n%իW'0祗^JTeKsW{;dӟnxc[K.$Нx]j7dȐnH^xd ,H{nKB/r?O~{*onz;~'Z-h!c][[xr)9 ea 8CZm=Xo]tQp- rqtAM>!ol@}6{)W?я͞=;h"=Xh42eJ7nSKQE&n馶+>_U7Jxua}c Hzc9kqp`u};NoiӦ%l!@+۠-Bp2v…msl׿>>?OZsN@uȧF (^{w뭷z衶}ٖv~abT kC߽ fJ]\5\"/w}w-8;y߿ɜmޛ)im1`{\`0N;݄cdʕ+~?馛6۫GFҥKI\~-G>|x*(k{^[=~Mfe#ƞgєl/EApk: 8Ta q.qS߹u]wZ9I-v]viSO5C[ ҷ[FUey 69;c` >- w: GNLN2*ZR@XX$.Z(®:+8oqUWoHbq%FC[ӆB!t6x1NY*V ͼyL jcM*f+%+ϺҶ`2wz)yMRY؁: .%9U:8$8mvsOy2v|Eh of PnU`k`/FmTD "K`{'gY[eE[&fz+ҟl? 67[Bt5d`K'O`kߘ2dg;qTTu}?EF|䥃-Z[jq6]^-9sڰĚnGy$2!U->$l >l[6%Uq$T%6xz5A3yF>}z+j/Q-1ҮM::ÌYL)U1*P&dc$!U`-j&Mj 9?-@l^[E]jAd |@m/䫨j~,[=|j>Fo!IOIU-4AJYP]es!3><@S8[eR0'{7$LVTeE}Fu3& Ad<`8Ϯk/" s5i ddiS5h![Me"w޹ɼ^[=#dPY. $}k J =BS|/H??GGY+&筫hY[eSlUY-!YcXTFu&Au梡[w}cY@+RȌlBk5ؒ>v FM6٤db^ŖHoD҆ g:zful3LOӦ"`˻Dȍ[Kt\Zxi+Vh p JOzO25(S"Uʚ >hv3SSO=I^AF駟_'. lʟUHP;&ABE5٩uY@'xg& AhwxH~>|W֟6CBxQo{_/'?|T՘2 /"*9fE7Ƞƶlٲ](<Bnst`kWBnjb:D!%aC9]1K)nPFDQW6 Q9wHS dYs7XbJ$BN-֬YCj~`6j9QiBM8Twa&L` :(uj`Kq;!=N0z/ )¡)!9)0JN\sMi2nRT,2i !'m,Th&EfdˤL:K Oeȵz{̺0g43e|uץ2qDQZ [2ifMYmY0'xb VljVK^"BA-6ܹsi-C<1wb!@8F+ҩ6B hQ-RU=Ǵ!͔R6 f(26 J[.׻nj ^6>Uь6J]eK֭5hЧWNS (:oi`KNKQ"ͅݩo\64[S$)QXtLT*KE>(6̑^=]zurU h|;;ݐ!饰g}yZ-U$(v5j-lZ=|6'l-vw',ȼ-޽ޛ pZ*XvwpyGG,Y!vhpYZI3k?p6N7՚nΕ`6L|^0B  Z9欀M4gn9+bJfnm^&f\Yx=Lmh&_S$+tm^^G+ j\$GnmiҥK]ҡZ ˤkyK UChI3YPu-^ˤ*C7ʤzM .'ˤYyp]@]5S&o} 39D/r=X!3`;qdɼz^c຀nUٳg[-u\@}LϻJ3r19x]&r,éb8p]@] frÄˤڐΊ^>u-ЫҳҎm/຀RUWELC!+ $.̘HFe+yi``\tP%J ymBiU*Y[@+iZIsu-7)>_*VstRQ'd'D6|.&2|H,d㥗W'5o+b}͵F="nOi[#͓׷M23/:s2)`/)"4al45?}Ɖ3>?T`ygl]A6ZA3VciͧLo6g\I21iCmR6OsFuFg&CM)#RA*?>w&Ƅ׶^im[TUk 7%Sտ|欤qO69g,[mbMNN.|~c͛.Κ4Blb n´Bli#Ʊ[#J̒beWG-&nu!@6h>gZNyeIm[6\kH iFK|3IxԿY |$ȹ pm bmJN*뱱jTuAhZiVVŐ&Nˮӛ p(ټ|UB2@;4kB;[cpm!Vd{vx`Լ]ZI}DV >m$|i ^vh=/ټ#d(@"KM.Zswhy;M5]NMh烇Ԛ}7-pqX3|[nk[$}7WZIo8 Yuq4Wѡ *CfO˲ F(lol6aOH}}C@\b{ń#h@.>CrkV8fz7.Ǐ،u`K,l0؄7B >4N>b 7L[oi3)@hC6e/oOI3R߅MchZ UvIߑ~3Om$k ^]mxΎMPC3kqB@&g#%Y?`Srǥ@fSKp Bs gn/KcIbN>,_8Z,زlG}Eld4Sޅ/93 X'dLKMO;džm0li%uY|s=F3EYqtXa72 {g) e(oֳ׿6GU/g8.@&e'P_ r4{(-p6Ne b$Կ$q&M$>Sr;#$k&YiFk9.>xŽ27q,3GE[ٽl7J‰*ba9D`}Mc.BB3l;EDw`Bv|4'p+2i6f'n3]R 3*)͒bE{B FUX.͟4FZhlgNSBޅz;)"©\}a6,=?k5ZOhko[uQ|(=;iߤ!-2! ;Cp%-4G0-Զ1*& 3\fjj:lq8f6oYH.>@P_~r1g ͆$} X-ܠBeV/l\ ZtT 1XP;nz/0641̹N 76爋,c_!4`+ifĚ̱ zl㳂CAe pqdo铝ƥ{hŤov'yyz&Ƃ73Oo,Ms8x!Ω%WVj#놶U5]XM!M)N'w,b\&s `Ϯ7_ғ&k3>ܲLc@ԇ64uiY7L iLbN..rO+64Ti>Tl]fJ)`QYY6hFCo\84yo vs Hn/%;m8rh[ڧ4 Ә49s;ܠ1å@KsMfb-[֮8T85m`Vqv5FmLkXSy%L6;0?! ֦6Ih6h?c[s ac6 yd>w`+Ni|EɌ^`+x憘#+pyNi&3J3ќü4IO!Mf9ͰEQ+-HefOvN6Vg3Q\FCDcSg^ڧ6> O}^M\>>8jXFV:z  ΁8j&3Bߒ ,VxUHo*ؔtDí<J|^IUIkg~7>e `݀D[&l[ ՎciW}]p%)Js:.䘓Q6f偰_@[T-ń4O߸fWTf#n;KOϬ +`jݡ0>mJeΥY߆"2ŧ6Y4kv[9 @Nl*m@Y9YцjWa F3lB SoR<1M29E,R<t'me%K.9&yߦ6##x8 GnwV;mCy{]&ΜO٤yL/̀'9i'AHmU|  \mp@qX $U?6Vf]o鿻lFE3TLHO!6[=A'M̍i$c ْddC з̓pl2H9փlb-[i7o]''p;^Ͼnɺ{j9hKw8HwCSXK}PRtl6}ufiXD}leLA;xMӼ^rP;5 l9Vi9en][1ͮ:.d\45lUx*fZk}ZA{"#km'#NZ eb`Kk8yivTtB4;ͦ1-&n]41܌Ci9cV4ٖE&d2/*jH&ئŢ,iil pdy8ٙ SeqYNze hI Qfc,D#gqlY~V^%4,gF*8K36}֏2к\`XMم2]^ #2fsZɀK!2j,Nd2/{ΪHD!.M8 CqwNs^BKQٌ$jҖ'sկfG *:RM %o/BW΀XHeEX[l Xt{mS0DξsG M6<bzޣmJp:swk,4ҔhQ !*2auF Çls% l>slK9F{f+ӶQcu4Zml6YZ\U!$0/r^w\|'3l1&]&}% R8HupjhNEC_jodPmqpQ%,N-ӧU56I s[r6.I[4016WiHiF(g\3Ff[yؼ|.`Ɠcγfk[`Gbߡy:؞5SlOS| QwH2L86Z>a_9䤤66&1D>IճZ𘦵?e:H!m ޹n=5KIѬ8u?᳠>zE9>jP)NBj*M6p,&Y)򦼪Y%: 49+SKK3f,}!NcxCim'w%Z4sZ4K3 bJ)2Wڧ1׎nwi J95FREv͒֯8}e-7R|-`-2ߞdSO=5yw -Z47xvj`}"4&Q:.OEK0/wM/e]4;XL ,BvڢZГtg$\{o'.;݆m2AyaZ*5k$|ώɨfO,f!+UJ)z\QνcovrGXiNoj8hAқwBJ5C3mh{M tP[o>ܲ!,dnS, ~a2y7! <}}ym 9㦙nGMeIp87|#h4,vT6xlUm1Ťv]BG-v/Mx} %^΋r"-@)Qxiy[ [6I-r+=Wy \`cڤmjw88qlu_qkެwOl>2xyŊ67dCD~GXvmr 2\9A;dJh|g8Z%OO?tܨw=` 2ʎ%sΜ9v"&FY4w&24KDp֋B-TJ>sY hY [imf\1?ڔ%mk*ܸnl\B2R2-@m6>3^#c U>MSXKpw784оKInFR=3w}[j(Æ k[-`L+ N`gݠ/." h^E[c' u`Zf\R&qm4X8;P5d 6H ٠ɣ<<% he˖%W\q f@(8,H@.}j:Xreo|H3&~Cɀ6ju0M( 8J]@KQ6&Bp 9Me$#eCrcJ#$vN  M4F `bΙ4 Y'l7Ȑo3O^wu ؘuC70pBi)F-8xj}$Zh`K/E}Fhex iiַgaڏ ht6WuPV4>C=4>]ӫ5Țbw \ZI p1;&V'C~{'6~xІ;w\c RTL|-ɸ hTҥKf֜8y^q͕myOz.GFK_ac;DX5Rm㚀 LAά@\enNCIs(V@C hYz{l! 7/{@+)psʆhxs)C5(o ؚVR5܌pC4Z}Kp39+І.0=+І.}nVup]@{545Z}\IW^y-T";O` h.!)ݤyۀhC5ܙ3s֙ 6[xXL Un^&ZI1q 7/ІhҔ擯| 1WF  py]uU駟^shqInI02i; 8B˥c^~La C Z _}ե| 7XqB2ea I7L p[nT/첄: &qi.'je)ɂ1j~d@Fb%d/˦Ɵ @z~m,e 7˷>-BGwp|H /z m6-BGw<3,tc|H;#t Cp:>׾MIr-t pm7<@m棏>:hZI=!fZ%^VS Emː!CB&V.!- 5ؘI?Ahfܡllw:JT&sw>;n.CVC /vi 3EQpC|{2iOXLY鳵;B3h{ %|M7ufZI-a'YgDM}PEбBJܮp;n^ .o~PE$oM3 AEi߇*L7|"ҁ/ʔ h_Jg7(fxڎC2iVe.v4SlY۬PZv*8ԝM}'4;HM,y"O wx@Ra}ި<']wx>Ȩ$?qBMh__uSr] f_e>S@N0Tz&| qƕNMLٳ}XFte󴍝e"fTlˎm`2gRv.(cVغ]3^lI$Z5[lIuS}i`룙1hf[nN \j`kɵVu ѵ959~_h GnN(H.NZGj>zB3V`K]K;[hj`]O:͔Smm: |ۤ Wwkkk`k*<X %cQWt`dbA=0KD-ꢛ"ͤ"VgfcmQF8#~lYFPOQfmmJFP@&zsπ3#PgTY7#8'tӌ *͝vrj^7ΌZPf-s8s1#<-4qvZӆu>`A|qkvN8E=Jiymô ^zKxUw= z$4!+j$poBqq9/9}]5kVM?OZ֪^CWzϟ_)o(GnV+liC ;=cn)RI=EM2nFPO⫯)S*e&o^,:AL:2+wqk^ ۆ6&SU4?U4PY=iҤq曕ߕz!6۬&QNOLSu̫^w}w²;}ީt-Y͋/ƙow6*8~Ь1}Pj^0rUguV`|hӟ4a!ByJeajgU̓J,80Q f긫,*Z<{[hZAA3- \0I]h˕,l~m9&r 2y5:&bѤ;6QK"_{:t=Wp4#8Ql89U;,4dm˷UF>ՍVQ#$P[d흓JwJO1 Z՜ASeg*t!=," yW?BQ?޶D9&m|x)Fwu ȑ#+Bꦅ< 3fƔ%)P{Ffr|22ґZM@83^>1_f~)&nR:^|WUQ5L[vsQuA;ߪPV}tLcCh4mMg-kr~G-,Y.,256{oFS.׻hyd :cCU90̓a'K"kO}oyRlphal J& XP>bGe\J7E}Z4Әn KcB{. fRm@&\ʫ.'Eә:Njg:?IM[߸ܰ(BIW:䷗_~9yx-*c+lJuA}nH̤;Q:OgG/S?4~Ot;!v]HD)"4^:#!U(f,ٯ~8\g\ #6YXhQ*`Ɩ( Geß +isw}ӢӲr/cM7(E]dBf̘Lu)o-}Mݜ5ks; e-N)cČvL 90 Զ8պ$& Ȃ!:SOm"YwP]4Q)t.of6AP8z@In$םT:6T#>TuGl ݤ:0cI7֡Qu[ J C~vCe;])1&@BTۂb \2[e `J%u1C+1Pi6͍˚Nft[b-ˑk}IU3͑݋QWZu Zi>}zeߗ/_^m 42jB_XxqGB jI"!‚#"bо81>ⶫ껬1dM')rO4^p"^Uj҆Gؕ)j]l*ÖYN'J4bksL!iŞđNdJ]nY .tMZ}Unj΁ގ k<"'䡫6/2/0>2JJg#d+Lřh_v zY987lUzk޼y]\DF貁Ƨf;wu)s z} [hyNj-TS:h<5 ť㛋`D~lm -$ðSED; P,@iFp>,xi&!? 0-<14G5k,?lԝ\LxU>`j2Dg)b+]d`K bI/||~> SQӫԡhe9S֨rXVj_lF oj,tu,[5sjJhr#@C4w 3O)}-ժJWvݑZA/Hz7c"OLuN.:vrGtNؔ>df'Sj 8\q?F'd;7 -* Զ˶sgpw~5''T-Zno۽md蕬K/lp MـzĪT]Tϖc6j&֪ l,~S[)R˘b˫Ў Y.B`NڳBe#J V 7 Zo@Z ([Z%]Q=vZ]e'Dh}bRR^=)j;мBH \&mw4t*>녙d*-gaE;<  8H`*xЈ+:(h"t/3G8t/:M6VPye^` 0."&0T(eHilx}[w$|ߘ@XyxEii̧0;@t\kÔs ]l.髦DYGĽK9/ߺDX̼]DQ~ -%lJ߹Iǥ`tH&1 4`a-hi `az|'.( `K1[E*e(vl^' a[qW-/m7o\QNDqV~K!*q=kwuU{l'^ M20K.)%,-Nt<<2]trt M20//z(م0,l]A{l7VdQ ";  Ljm6=ua׎ci I t' vlkjde @@3] `$Ru,GWn䚋8t;2ȝqGfnZPIENDB`fritzing-app-0.9.3b/help/images/schematic1.png000066400000000000000000000640251272363601300212350ustar00rootroot00000000000000PNG  IHDR=*bKGD pHYsodgIDATxy|uPUoEQD@D9U]PqUODEYW]Xϟx" y43ɧ|gF6-2%Ӕy@snky?{LS|ʝR1h\ViEp/>"o&tܪ?tEjx?}<u||\*b_i 1h\oi#KcI;z 쪘ۖmݖ ?bb YΐmQ^Q܏+x8,x\*xs("Qaӆ WgY H cfiGG%%򔔨DRJ^y2}L2M޷-׫st?ӱ m҇V9Nýw^޽{H{j"y<%*--[*_d4o_?-i mמ=r; ovkWAviWAv ^w:6 m(?ov]zsSn 6t)Fj|y:`gUHFeIUɿDsVp^J9^3xR-/׏之 ?_wWw߭3t2[G8BWVYbŘ賌5ME7_u$LW4 C.+}4V;)=kX\W*3HZV9Bdauκ*gUO_ye/k={v+?ot}j۶>裤/Ɓ&ku\ 9O·͡ZeK~Mcg\D5h/xrdy/EnNOc%Hsم.Q=-ْ뮻NVljB#˭x8"יAԗu%DMƫNvNmr}6H6X8Ju%ρD&ӱㄪL5-VrpE3Q|KH 7<7rYgd*w-O}RYݻo?ӢE#I*&\D9+++a%?D5^x&*XI]qVd1r|5׈Lʬl$q~'گ"ITE&ґdXI+Zg\Nh>:)SRŪB+VnQm ćxұwN56ykQby?=*]"LeȔ)$C[Uf- JT5;4J#k5wUclW5 $J%ҡۭZwej#[Ւ%/^Z@bTx5̉UH |Īe.-- ~&d:2XƺI39]t'$d*D Ir[]#yK˓U[F+i3哩=y%2%ըUS)iSQVVV+yUl%Ql#U$va,hܥ 'S,䓡>y]Rc[LLI|R˞htj٪SerVV=xn(dEWMw/t%=<;K珓&~:6~n70Sw7Nt&S-!%Xθ\G+v|{ܢgtՄYաusD1^UǘT^IF0" gw4WԷb+HL'G?/LZK,+W;k N#׍q zƖEY usgP ZM:0N=w֡+I?O=B3uܟh}H#V>{l;4k+tʧIX7 FNkf-V|DܻY\[ݤ'GUN4O˧G]TF֗/ė{$@jF.?LӔ ~QC,;5Ρ5iJ')2M6[W:$a+q.,1u**1rcu U+~Դ,CO;vSZf:a4vsN;y瞮K}4eai{M1"M~.]9Io{?ːi%+<"eUj>/xU?"cu2MNuhdjgA!wFgee:F~`1CNʉYv0$i}5SkBl͚ݯll}l><]BNԼ}z\msSV`t_=yzRYy%u)6Gڼ`nn&f6U>._ߣ^8T-q~PmY8F ǫaz1/K^^ɺѫt\Cqqj(iˢae(T'mN+:'6ҷ+'{Z;Tzw]?y[u&j0&cmVWa-f~ 9\geeo swi(([~ =D%(4 W]E?[$?/҈#^Sr,]EE9!6l,4w`ź袋tE 0y B(+?t4eCz{DS6jm?BIw;}t}&RHO?msN%?YN5F>&Ηhq~Z?Ш$5˱2oYԎu a޺)?轿 șm:4%M4=iW_Eg#s:#5qjxm ?{xgl;i#zճByZeLcxuNh(}MIկ$mҼA_ 'W1cYjMؚ4D:fhȚ5ctaaj_\2CmCml7k%wҨa6ה/ij!I_uvi__.yPfIݬO7Vu9:[7NӍgzXsn.)Oapo=1aؕ|v 5I~I/m$#5Fz=O4IZ ]#mZ5Cdp=>O{>#YJtE@ ލI3ɆKuNǀg<#^Y{;ˮHLʹ;irIrgԣ~v](iϿIF[VnS;EBnKdRM{^S\gFnlSyixtsGMpvHKe2+ѼL7OMJ T^P=0󄐛lnylMt(yr&-:TC;Քmլ}迷iPt:t4^$]t4_u3?|athhYFۢKW[1A .yR˧ Kxj[3fV'54aFo釙Ґ 2쟂f:z4|)I/̐I:mL3G Jf׎҇;ԯy63P7M/S7t qfpiVl:[ w|;uץQlҊ4VBbtJm'IÆI׵9ކ|X$;Pnw$t>i"LnH+ђ,GTErS/+no-QiG5jԕ˨OY &7Zq}gcn6}q'0_Kx͇7Iuo}*G7VNp2R揤+Oh)ϧ?׽!L4;vhx/׆|>?ap1ďo2=:Ųpc٠YWu\^Xlڟ٤:K35'>Ozg`SxL4ywY6ݎKTըaxB5?LԦU3~ĸ#_9շO7N26l5$ҡIu3C_W5S?lvŰs>Rjհ9r4v-dc1[6|ȑ}}8>$a0}>TzdܨGp'ߡGMMooLz%uխy4/ ;K< B PS^ҤI_hȑ__">.ïkUƽxiؗNKZ2GWk9vgwdM ֭[ ,wvyS xO OrK ը@^ۻGY+Z_Y-O:.+_\hp1N/O PSk(CO~ 7?ZrRy狨 f|4?Mk.?nԇOұITLk5b{Sդk5?H9zt}O<̟DM^r]r{?eǟ~^-t!=Lpʒ4%5T\{4B>*1fz55%MM^ ],n*&tСZRy",}:ŚtU4>5b j , yGj˶9q)Ie4fJO< /t;4ןHGt~nc^^n/8|μk^x%AjzQlI!Ը)4hO)K,uYYftS>f3z^uٛ^Mج:_90ICxE}:衒֪kFm$}q:Ԕ+6ȟЇC(G@5aV5ՙX?C HxmcBGE.'W綥2<1j5{lˉx3jkQgh;UaI ^^4 Vm6tRTozLtz ~-a(wlzS8rsfz5d\6H4~Ǘp ^x3oBkb-7ϱ#vS)^"8,\w+s?сq S_oMܫtYώo=ˁ*- y^y< "D't:>)TDj9ɬ31^Hgn<)))J}"TK{Uث_KgrT {jHW:8h@œZ%m7O6yD~0Z]ӎ f?CKd+Q^ݦ#eXb%ˉg?GKc:aN޳%O@,^I*@V\ZOZF=ˏ_EaV_4Na]nXi7?eQLqh*2}l{Lm?d9VVLeyN[:VpMkg.ժSMZY+Y[f\uUX阋V˓~6@x ݁vwpP"_ݧAJ+“(V0{W %f:ޖi4q-s641-Hlk2j''/Tu׫'ˡ˩L#96ft"ž'?vWh˪nkwR*Tij,՗Hʪ-w>D͎k>M#0,G[bՎ/>UYo [^IIIpEb3.fvkCB#vȊ&vc#VSZFckTsUlU9:\0C=7~Y%=e#ʴ$IՙyΫ}8LҺ"qfggnX. )Pz-csIudщTͳeK~I&LE|vxݴ9NlgLbZ_ɶ MUsTlS*ŪQ=zً5"ϕ Uy9rki s:@aFyhQ:Tq۹ƻZ]C{ L$ˁڪyFE~'9ÈuKC)1,QYCoXˊXJ&D$ԡ1ŋnk'N/xTz3ux5ѡEnVdZ=4ͨVVǾKMEnNE}W7HvL;1^w$14d6.GD$9ϵ%X7,&ھD~9F&΁vk/=Vx u+CZ&⎵-V$V+z$vYOe}%qjYNg2V-tsT4@Q.y3Q&ǞJDg"+ /E6o˯ȸXM6]ms&KKD7r5ˈ/9e,2+[O"j7 LlҟɳM6Yme4D7&G:y&qK_VJ'k؝&drGW Dd3 &V5KՕX}A[$YsG/xVeVX]UlUKf)>s;GU}H;xTv[R 'F9ݚl$41Qhdl(a&y쉕&돝ebL;)^w N>dժURKE$ ]_25Ę(N4}u'Jp$⨬HͳLRM6".H) 2xׁlBh+;}Uo[JxUO>Y/֫t8תUK3q}Ss1ж$%7JR7[KS vV\Myz) +v:k:źⰅjb]qX6':3+EOX,=|޾Uy{tмs)[o1,>J\}ecc:mn5Α82Qw-}_ I(FonF)ϔ)3VqX/$Bc\xP#TC+_y>SmqVzS?l8]=pFOZZn.O7A'1Wg_XĘN;@qvM˫]{32TuĚclJ'xCoU]$ItqZU$mjy}ZjvMF^xo6X㳯l,r tQʺɆQMŸm6Mtlqa_\oͅYMtW~-zWZlx<O]&{I}ӳbԛZ!i7tmfּd6,;Z-ZDPbn_hcGnk6VVx>>[/>^xŋW\v]5/K=xVc@d"߿zCߡU;?Ҵl'8n'F`}hQqrh:_Ъ gVA<[ɿm7]:3$Y4rI y2ZV%1/~e1wMǭzr/ﭗ77XFh}|FmIJ0Z/o||xŋ|f0ل#Ctw5yd~y9WRs{4 aJ ű)W& Z+}Uhqy؅26W/;86W[}yZYϓz?ܪJzp]{[40j#=F:͵c |_u-W~i7*Wue8JWUdhY=k}.]Np|ZlW-94YN82Ȅ3< I5memJ8IQŊˈТzJ-Ybh-,b?ZF<_Kz mJ:z} MA-D+^ C/>˹~a5"tM1_0|9 ]z&"Ur*B rj&T~۵}vm>zi7OE^^~%1OÃYi=l52&z;#CMVkYs:΁is5s|;O_snKeaT[5?W7Jh$}5^6UӦMtjMG|mmڶ0Ӎj̶mڶm}_o^֛Z/۶MfO'|O7vfRrW'_L_GLńt{2c.*>>ϖ-75#KhM!em֩V+U|6~:I:u.6}`y5}I+n+ _ç]#p\Po~uZL昬9,z )pLmۦGz:NCm6xJO&ޖ#OtYe:o[jԾ}mly뼴WaX급/lzF׌?Jʖ3먵@ #DRĞXvkj.I^ԀЩKvkxHڴJ+5C32#dZYP^Ԁ3ղ@qZBZv=X^TX(y֩MMOvWio;tZgYyi7ܼ>us'kByr'sYO[+Ԏ (,{u_ݮsm\X}> u OxD;plz[yC))8 [4W;oqR_b^ҠSY/W9o#ϑdXM_^,}j?Wa&Pk߮(>n-14=X5^Ow)8B\F&ͱn70|hsl6D{}R۴h: Q kϢE|Zܔ<NUD#wVzڴ^iCpi#j%3G ZדDL6گKth8{GoH☬gi}h躭NԣkfuFƘykzM~IJ+ڷ>xV|]uϺuZjVn>vD)\HڵR-f}zn[skT6|}1E,BM[s|:zZ_p|uruԌsYOL+xYS֑c=af+YY|͆Ի`: i:&,Cυ@k\M[s~_8mqiCv*9z͆ ։7u䳵9|d>k}kyt /؉Ys龞*\Wh<qWg֚5k4k֬]t_oGv&]s0ONǝYD\ta#O_nNS-3%)c΁L:_gےMЁf8U-L5c]nx? Uk&\/c_TlI R!L5c]amgϞ8wU]v.rY%V=yr{<?a"8@ k֭[c5j񨨨H 4p:TTT$I]ӡd|խ[Wn[0a,Y={8aի^zi_t8A5j=Uvsn)$~ծ][7nT߾}tHXڽ{^}U}Zd<@C$ @_,]V;$id_U>aE=Ǚ;̑i֬^y^&Mjjذӡŋ;VXi4@Fx<1}ZlZ?thi6LaCrI _U:ԺMIw֭[ cjj}F&ذa>`Zp$~i_M;q@f|]_ ]^J~qtH @;! 8O>$m.LT~~rrr%#nݺN٣,լYP2R~~ׯ/Ug|>vޝ'VX .͵%sh-35$q`ոqcCHxǙߜ'Zf9= KC{ P I3i4¹:@βƙ@jyg8S DJIra6ՠPgPBkqqs `C5$@mq6MZg eYi&:@AT#@D( `OllE(:A`Q,\DgPh D!f(<\FP sU~!fρ|mr1{(oA3Pbv́4…%ΑO 5Hr1G\0qLi s[0:@FgPx-shlӱiò{jj,HEiŊJkv:$8:jH'xBK,$ 6,܎|U~'|t$2q&iPL?{N{^!4w@l&M5T^ziٲe*..v:*T^^I3`%2i&PYcǎΝ;Û7oOcճgO H~ZW9~ҤIa×\r 3 -rDJC;@zWURRb{;2MqPYr!P%,j0gGX@`(qP.n8~t@ qt9!mѨqlv"(Ľ9D vƙ(Gg3?86}33G qlGE"Qb ,MI8(8jL Ds$(lL Dg ql8H~ai7 쯂i*-- I gϧ4:`L 駟v:> -EEEZjUpa:㜎H am^oHt|@ZpEv }$ $ $ $ $ .Pܕ_$zi&*--UÆ պukկ_@z7?מ={N:ꫯ9#0Hs=W^74i>g}hԩv:t,8He˖jٲLԚ5ko͝;W 6ԣ>tX"qRzF .@rH4M[kTPP Iz4bwqN @g)Qvmz뭺TfͨaU ,5k3 -C;@1MSA@C=zH8]Ϟ=ժU+_^::,;@1M3D@*c=68m6CH+dC3G=aT7 r`Ű*'nݪRu:`ثc֔)Sgq:ꨴ;G8w+W9aU\:tAJ2q޹s$Ⱥx<ڻwo.޽{%Ivҫ*˥͚52BiiN_-7y^]~*,,$ըQCrvbUPqqJKKSطgeeeY>Gdy^޽;IkX|>_|7$87nXԨQ#@*񨨨H 4p:TTTB %'''x҄ ^r)No%4eF}Qqq<իt(f͚RZ%# 8WU5$ʋ\.W\] L4I_|aiҤIꪫr:,md}7:##Gm;> K;w'|_iM(6lK/>#%Ik֬ .PoR VU\\,I:5k,~dj1N8u-fmÆ ߿B6lthBKydQFߏ3F?!7_~ںu$o/Nm$(?t]w&F#77W}/"I:ꨣ4o<xN@RhbnF 2D~nwXH5ku]Wm\z9`MsvkcH32ANQUnjK/_kǎN;MgVT39rd0in۶z!m۶M۶mK8oVV7o&M(R!ڲ|ruprssps `H;hnԨ6R_t:M5HHHH\t<@Z @4gg M5b&:ql ql ql5j:@ggggggPLt: -Q @( Q5Φid@S `LM3[giM59!f ZXshL w5˧j@:@!i 3I4T e 76#;@ E" Ŭq6 tl@ڠ!9ΡxPgsE4vB B;@`k@9wh(i0`L *l۶MV^?C%%%]5k;ꤓNH!w K/W^ѷ~w֭[W^x?ƍ0i\ 0@SLq:lbg:@PU PǎեKhB.K[lт 7ԱcGo5*v/׵^C9$jqOo=Sz(qmڴc9&4ak[oJtRCHmM PΝ;wt:t, y&M8q>,3p:,B;@+QV-p N%n 6hԩ-Iҳ>VZ9HT/Xڶmm,?裏SOu:DbJ*q...VII <Op!y%%%z?PZ\I!vR lTk׮՚5kǫM6i{nɹ[ %%%x< *))QVV߻8w#HV˵/у*LLGR 8HyIŔ@mڴ$uI7VIIv6mz$:~`ձҼy4oXcǎu:h(q^n>|*߇ 7kb-3?ըQCwիw^q駟$I[n |5lذj?^ԤI[?YYYԩS @2b6v 3ґ;'deeU[3{OԸqt饗N:11c^pm'T'8H%??_ӦMӴiNv:@r *;;͚5o߾N@L8r5js=G}T>-_\7oV~~\.8۷.լYRJ tw:*HHHHH!0 ҒPĢ8S D9S5b&:qlpegg;ql qlp}EcҞS8hL @ 8686D%4Q @ 3qPggggggggęOk86D%:\2*r3PεS1ij6868686868686<%%z  Hh9YYYQa4Mc;wm&Ij۶:wtHXrA 3hܸqѣG8Oyov: l8S [1&a*--u:-[h <ѧAN-QTwmݺUt]ww:,l@())_5kH.p Nmƍ;}ikdIRcq |H|P3gΔ$yz饗TF  ).t a&N(I:ꫯrC #|>_XG}QFIj׮9sCu:,*9$*k͚52d< s=v9NfB*;;<t()Dϧ_U˖-Svv~wCEEE !={Rںu.HNqzN4UXX5k:JF*..*+++\}rr,9Y^W)'e޽qqtV,neeeq4s2MS=y]:鶹eeeU|>|~gI҈#tUWżf\t9tRV-լYSj"q#ϧZj9JF2MS3gԤIe8+T~$aÆ:#K/Ŝ>UViƌ>ZvӻCث~ٟ! ׫,_]y^y<{~ 5 #m7 L:|_=%Kz+&q@tLww iFnݺ(WsZjA)///O>Saܑ!4 >_~_|g}ӡŲlܱF<Jsx [os:\+H~j߾aPa<P\^jԨ1iPT ϧ;w@^WuUӦMth —_~͛ըQC.B 6Lkv:Lb6ntǣoFƍSNb C 8㓝;w*//OT޽{ w^IR^^VZjԨFthV|UUgVF r!jԨUTTM6'aWoZh cUPqqJKK/g\.ժUP2R^^|>\.ӡd׫ݻw4͔g׮]q:@iذ^6lHx<eggANUPPט1cK:>~jذa,k1bZjoFǏ~o;b|U_7x*ѣ:*:w矯>H'hժU:AirV׾aUg'LJ:fZnN:餰orF0av,*q@S,@~g=O>Y_M(g)SNڶm۹q' 7nt -m۶U=rʰX ӉajԨQ0q޽{!E($]w]1c;Ȟ={֭t8Dqx ds=W?0ecvء?38n@tLsWhZd ?tHH5k_t(Q^{Ν;;Qz3.rrrԿ([n%ӦMSfԫWOٺuxGN8@x(}ڵk5p@uQSN/64M}ӧn,;x& -% $kպu7Sfd֭[9l.Lg}!`) VoذA6l~Ѻ{qsw﮷zKK,Lk׮>}kUvS rԣGCTPPM6)//OԴiSjJV, Pvڥݻwv8W޽{wTTT$IZtNS|>lCۧ޽[k׮2l6rtj:UV-IΝ;o޽{%I:t蠓N:]dv-:bTО={r/{n\.\֕,׫ݻw¸}>vdsNN|>rrrH*񨨨H 4p:TfM}w̙2MSiVz쯊xT^=CH5jPVVVN$4M5lؐĹ^R~_~.+mE(&LPqqa I͛7;[n?~0.x2ի]\p$n$=caQnXcǎl.KGyӛ@M5H;0xTPPtH!??_uUƍe}_IҪUg%ӑ^.]$|<8{N<̰nᆘ̙3G~ipxȑN@ %n&N^fzKmiҤIarկ_? O2Ӎ7ިG}TrJSWzᅦ}>#bD3;5qİNQvڥ~-,i߿-Z4ig)eF;O/>CmڴIGxڷo!CSNq:T"qP-9r-[ `CT(Q @( :9',IENDB`fritzing-app-0.9.3b/help/images/schematic2.png000066400000000000000000002310171272363601300212330ustar00rootroot00000000000000PNG  IHDR[bKGD pHYsodIDATxu|G$(mqw~ҢHq+-Nw!9wEy~Mffgޙy 0SR)( J͡RPTj$I$$I_ xzxqtp ]:{x䵭!iץPtGTT R HB9XCBə7ɓ7{tsN9x꽭!i0Ϗ>l2[%B8ok@o߿ `ƌ8;;3fzikMJ[9̥gI oM'I|Il)UUNL&KpDR>I8?|ӧO_ۛsD%( >okSm̅m|=?ëjŕLj]ٚ W>0ΟMߖ5_Oo~&- sL}b%Skcb )іq|IX^0ֆM5d;̙ïJ9{,Jamm'&lR5Z% .e8sc;XJ\xK,)Œ22ZAX{_%e7q]>qk[{ER(BII8q5~[Ӹ[9ì%J䓺uOKYΔ/ ocw5n$WABØ gߜR` >4L_EBa3W( d +5JAB,HjkP" {A<T*@MRO܉SJ%R ƿU`_,tEN8qr`oJ0wnʂ7)"j8jNLWcofR,CT539$=,)攊1~+ 枵ʕ\&e_* cR\ Uxxx4ȃB RTr$THH8;Qv(etCٺ293XH 717xAqm/ ]j!k!?4% E(aѷdE&3+jRSr9* LJ2R/)LFoup PpPrvhH FB !8;GEо@)ӊ` Ii1HL3*+mA}殏1658R\qL˯5Zs|KP G|s&q:0(JBT*-ӌ%A* IǜSj5r@1PG+QءTF%NFB-Ij$(TjIB)sAr"׭EEⶳ3+Iu?>~ݟ\3`mß33[ rNA0~Y3~"ŗҒ,}EL+pVQ##4JJKb6hIHr+%g6dPw3٥ rej{ƜK|Jδ/l85"?4s'f@.L3wLuXD[[jSS >2HVA䳙:K8M\oNA0ɏi7Ѱ\az9s/kGS0-ͥo.L,9|eC)nt}t 64!t$/&5n܂nx.8i%e1aցX<..?R={n̋㗍_密h֒a̽:^M?L\3 0x6}J­?1O+[hh>W,I~b`$Fš;?C ss`NלG7O'&nL^e0J3 ׿i[:֏}Y nCC,!'EZŜ|j^G\FD\&S[9"Sɰ_J%jr.N3cf5@WTcJM&lBfoT檓Y$ 2lۿsrdɱniWHPO.Gn"-?Y ?Sa%^XL22j2iX*f'ۛүNmM~+*\{w[3OIrЃY6'An8м^gc\ח3G)98x45=\t,S5yP2*r3ahX E"X2C=#X(/ cl(sW=&‘b+i{%+U5` ۏRPOie^Z̝Pɫ8š$V6xr%q{: A9syUv-kj<2hBk,zߕ ^f5* L `dWyj^жcflA RQsX$IK૷ s ,2HpEAec7!h<-ptX{uu}E&]^2p`=܌lľ֔|ar ӨR?ָ|יD-(7GS)1YNfZOq`!̖C%jyy2xd19iOB!W'*C`dnyPL|뤭/tjYOgZvg9!*ʯM.'xZʫg,9щ {~=;ͥʵ)7yf;Oǿx$s%ɛZda/>RU*^PC:xG/'HJ"UAT/0x6mOժUæjU#gS5Γ`^7VV*Ucjժ1?2MT LwiSuUF}@suW&Lz}.Mdғ_FC]ok.l }6עV-_֡ο5/LWk^W`c #ffo4kڔf͚iO4EՒ-Zвew~=g'U]<΢uִiӆ6HSj~]vk׎?=D*HAיa.׃1] 3ұ~dȞsNt]:t78ص ^3ux-@ LrS)0Ո\KQᑣlTV/vޝ1-"/I i-V "yaF &F)ݲfҥcp8CdK ,'k^/AMl#G07}Ey/Jt :,/Qro.d\ЙΝ;kɂ1oi{Ns#(:+&`n3fw[~I \?\BILln q21l)L\MT-z Fd(j9ё(ъt\BDzImA8> 5cʹe8[42񈍚 (dU"bI;?{1j&1nXXpjU6`4=竒 6VʱbnK%(Yk6}ûTk@.LۿLU2J>ksK9NlG+~ǯ]AO|o d1s1dbÜوmطf7qh*V𧴀ݻƑUNZO;WuY96?EvmWucxȵ98O#)=(3dcKR3wH}F3/\n s}6Yt0:*XĠ2ꥡD").VEW$C]Ʈ˴uxG.0vM, /AZ1@7 ykqU vi>v X0Y^ Mkh\hׄSet׍ ``iJa"! C|8hf-8dޢ0uΤ?i>@}֌%$ ǰ9_5YAL0ټgK ^F `V}&M> +ԞU}yvvhUC?N_aG?rDm7J8b|S1?h2M\wY/0׃WKEx]Ѓ[[Oc:'^$SMwd%t?ɮ; .TEZ|TY'Э&7]7%y^0U6Y&\ߟίV"sޅibH}~̈)m(7y9Х ]*`FucpQRJP3v(p*?E@7n* kjUSz{Tt]&3G^o֮&v}Եpw=tj:y)ֿ ϻU?ܧδ*xP3Q"ﲞL+yхy\\n5IzƝV"A V*YS ݺAy5aem~79ܔBaRXKiٛ@w"Ȉ0(T6wn:R!r3;#vjgqj QZ$ߒ^^Bޜ8KjٌPJ-?ug`RtO_~s})~?&D4 %FĠ!{HG΍CQWZmt۝uaު|$GiQ]KfD -g(cA^ ļ<2Rػ.GR=)]FoSIƌYL] jW^Pj^y~~(她g@ZBv2wvd+o8"/&PQXƅDF )NP52-o-*_crv*̃ZmtO׹3{.A$)[}1׬䩖/`P);FKXKhQ-vذRsWMv+EՓ>3lIȍ uIO9} yh'0LȈy҉ ?tbkKGO]83XJ̋MOSkGi{]aHZ2ck %eFq0~_KCbH1b !Q;&!.tiY3٭%!(<2FTxaȑi^Jn ǎ 98Qwm V,mw;Z9辬b~?z-Qu^= GͩU)kD L2Ry*A1n<VNG*WΈ5 cA^#k<u0q],u~(ؘ:hH{Z4*Vǣ'Ø/!j2-V-XK^#!2m{0і̜#sUF/B Rk~έ 4i3:F۹K.PTT1JZFɖ9?c>OLVtCҔkL -zyv-AmvL:!K%Ī*1 U2'A7KȤ%pU !h}4rV1^RugQAL zjkܞqMi\֖.MըծTź44̭FMC}Đ ٠1'kwNȆ'}~4H^ eT&wù ]s4̈_$\aZ紏YL/l¸0T8K7-I,) X\K"_JFM7ui[2X!Qd5 |&U9t_qv[[p=iok,BBR 'L,/PKȲfSRV-n:"b5lVH $peDPPՖv2~[+XMeg |57yU*1o$Ϝ0{X-}i%R|՞7 XCjkpS Wt/6nhXBkDP W'QD*U8Y%X +,/!aK"5v/ݲ47"*#A~[ B1H I* Fz`N^ci*S$`)_qP97}k)XQ0_mr!>?}[3V &/46ǚ}ʵO3sˌgI1Z +k5 -)sךJϔ!X,Ib/"H=^%*1~&~vBR |R C]s^0)e~ïOo]0$k\qlBMqiu[0:_Hḁey|k K5(a>{ȔiJ7V-=ޥ94!ɛϒ`jrfSƵ)kFS$D1ƚ~Xj_$Md_6+*K\cR+ Sl*dU0n2z-)q}=yH*ARv/j-n~`)sBPʁ9,?kvqǧkk}S&KS9e!>Kn[{nگP2ċ/m"ϭQN| ERJO 2sߦ&XImNZ7}vc{$Kk,$Qajl@,/lDS[MNKE3AJ8z(۷oOr@`V9H>=۷oʕl-@ :@ hE-!#֖(UB9 -aWJt+@S9V#2fK)h?+mcX#2FK@re%]73p1POƤ0gŞ>z[<@}-y;a_7/#)2ĽO,9C -,UQz4UgJ-!jDRTzzЗΠQ>J&i*Ȏ.Mjnr2;oi?wЇ(5OݥB!9%}beF)@ĩE"(TDD04̅Kb3̚PaךԛeoFP-wo!5t] gDL(< NC/.|Mx_ g 0%&M|^i\n5N>-QT٘(2I3et]JFvr x2 )Lbc_- 1竆Ȝ.XQyo#I|>I9!q#en>3fDa);Gsǟ;ՏJ'~r LX Iol[bՀ_|6}ԟ;S$<ݴ>O5y;sd55l'հ;[ф?rp&'w +T Id_LǴvpg5nЌZ2S3|ܝSXQކ~wNo='jvF2C6'^>qC%@8@F% ய01}HB)\}lE:c.镛hyN;Sr'U5; =D2#W} 3GS\wI"c=4}l^2^'xz )JwfϞQ} bd^}R(l$i"e$?8aê--#092ύO*C&ń0#\>7Iho^&d0[4>/6ojcX4ύJ$_Ks _6'1accdȴe-1O  Y-$}u^(*zlU9LU"WIfٔ\u@xlyh=0|&7KVrtXq%jL~ ?c~⸟ccz4Ol KAú)8명iK8A\C8Òb֙ͅwtވ'#T{hqG VCoǕO4Ѵv>dxkC9*,nMyxk?tzkãƊ+qGA4P'L,Nͩ4ny4R"qU0('&c(rAYCG><=={avՋ;Nں!|ff|)t0=l-@ $=qs ZDCF̈^|/8LSȐ!@%bR90(iWm-@ $B7 m:$lz@ r`n:㝻wm-@ $Q!om-c3[(R0EA q*3~!UŶA zUė 9K ֽESiHo[˛j=wEHU|uK{??w9S)(#gi+f+nk_ srvvN5fSQ.Ǹiy@%nI.RyTeI>z/[jyR)(#(mrWE>P6=|Ӱb|S*x\`|Q]o|l]G42Uyb:{ikySMmN9VRao၇1PrUXޅ76}uĐB9 k5C=!N&^V#9?FP+Cx(Fw1 *&r`~٪Nrʁx9$ Zm$S營~Wrgb|-]ø'uҋ5 Et-o-ksz'5{{ j`}<]]ևy W]:|} LF,F2&>ׯMiFxؤcW('N |Wo_JŊS7]GBfIB؝wf7w13Tb\?!Ϣ5$3rTR&WX[fI| *.-dh&2eaIB]wLdʔLY⫹wwA 㸌Z-GxmK>mzɮm7i*!3hi}W)!mGO].㵲MnP&`_$IqmS>L>,;˗Q!S&96#d{hޚy'Ʈ7%n4n*˃ mbR9T!,W$x*eL̙S׹?lfS_:°J>|)D4n>acQ/<$aXݨ ?=,Ը_/()0Mg*75YG/g/Ʒ[bTymb Y!;wGѬPLXsm|Cc ɔ@+WðKgbb:ʁ,muf7WO>lƚK5%>E/RfߛB8=p +N?ϱx\,֯CsT1.V0K?(cX!r 0C9P*mA>g|U Lp0YÚ,k.`2?[w&sU\.nr>, @UΛj5ySnnߊ2qɮc*0J/8 OZ,CZ[#jsyd<.gS,]31B7"XEfG9UT_d[;С2t˗a4,k 2,|=4~h͕&VݲR9(x:dQLV)2b H S*5F'E>ⱱJ q#Iz7K1nQ5*Sכu̹7~sḱ҉LWͦnճ}:{iu~ڦPo_|8с7Sϭ}̅6.ԯ%wSek[[dPbңGSB >+1ؤ`JF>3?k!,8/LB2zS~fIV}hJi\'˹/=e:{< hyq hTćt:~}c|Zwl.6_ޮi5?Dٱ[q^sϽU&s`~kP#>]cEٚ7 kdț6 UW!m"ݻڵkO^zL0 ׿Ow>}jkS{[6[̻0H5@j9%e&veL[,2 I\IA TRY@@`r`HA Ϲ^?gYS)(#(mb -HRSJMfSQ.isоF9g/@ êED#'HND ޤ{TT d 88'''Re )vHH'LÇ=r6غ8R&s޹ì8ABLe`)Axx8jZ;((ő[JRԎTFm@ KE@ i9 @ >kD@ inѵ =|HDD!1[A H;lڸB^^Gv!@CX4ŋPUг'v@1΁h@ˋ~!._FZXz5eKdO Z899aggGxXE6Be81Q Hz{gn._D:uhզ , a=1(oӦ1\֫G.]X~P/,,OV7`PA,@ hQ6n@>}ؿoݛRŊѵS'޾}kk0΁@ ?Yr% iyxҥcușÆZD@`# @( AJp1 u%]t6a֜9T^:QT)=y+%JdѢ_Gr6k>[ $Z`2(J% & !22O[VtH)M`` j\Itt4(J=,, {{{-ӗOV랣?2}Thظ1͚7ZVcI  2id2 ɉ̙3T*uad2odd$jh}pp0[ ^B V,_A^M7AÆVP*l-pr9ӧImE[mM˜B%E"prr T6ɍ&jmDEEjr߀r]]oi7<|ݻv <&͚ѺMjծmNK+{K+x7i®;hԸ1vcoogxQTdȐ&e!C8z;eʖc}{wn&OZhٺ5M6((Jmcggg3@Rn3@5)W h|.*mʻ-\{|7_X1+ѣyccqHdU2CN)[VV䫯t~6s4rN[e3gN׫Ջ@qQNzK?$I%IBTd^%ֻ|^^ ')H[b90>æ9qۋϙ>u-O߻scϙ5 _NMl-@je9B1RpΙcef=|gϏ:k)N9>u*޽ӹx3g=GTJѢEm-@j0k9K-xݻvѪM=}36e wnօpQ<}򄦍Fbm$B lӧ3lH,w\NU9a" Թ_p_F"$· f#gN[g*hqgMQvm]EpqgVkDGEѠaCMʟ_wBɊB;8o%$8tOUiߥ9s@RezvnϞHO_/`Rro10e56Att4JҬrP( RVP(lw0ܷ>ӏ6}Z68T*IwZͅg׳[o0apppHW|x};w=GN .ȓ7=GҥKGtt4O|}mQ "K,<6v(ɘ믌?Zmdɒ䄓s%KݼITTTS׭6Ko=q&/98Sl7oHpSfΦiVDFDnjlްnj4j "#"7.<\9@HHDx8V$qÇqpi߹ ㉯]iʹ9sSUqլU '&Y^y(VxjaAH/P>'yEŊq ݤrTg;9Ѧ]{6oX۷hԴ9s}F6$$X7!{Ա΁@͗sBCBpqueq 5ӡUs/=zh,`~fmJ3*W]ŋپm^͍1|Hr 3àڽ;:vԹa;o͛9vhXAΜ9Yf 6n4KY#W{ht$3JfP u4dӓ?7[B9H&ʖWT(Yj3orhptL2`I}tڍ:v&EӾ*Z._&W_ O.vRؼ~:wuzb 33, w73fPt}usrvf6TJHTb'42H۬+/HEǏlۺ5j(?gn"""([...YBRqizDGG릪>y???jԬ`(@`Vٲ5[8!vpHG9L6ߐ,k%ϞUѢH}[gKO",,'Oa:Znkpss3y]_N~7j5'ObJ899QvmN?П~ŋTZ`gRI>}tct_xA]QL)0xxzmϝ>ɣGشk/6c3h۾NNwLJܾ]Xf՛R ,croS`Μ<_>J8wkgW_Ѭuk6^E:vbidʜ[7Rtk >_^۷oɔ)3fo=΄Ō>7FUjծ͘Q{'OV:3f h׾A<܏ ӧDɒܺyJ_V̦|Y\:Px "#"8s3:vo^K޽}o#Q._@oRF 94Sfӓo^˘Q,] X֣(uڝ|~i޵.vr9vdg`T*F:Du2fĒU#?{hL"Eeb;[p_-J֬Yx.^dԘ1:w777:{= s, 9p*/ɓ},[ P~/qttdO#KoB*d2=<(׺ @>v<3+bimL.?{Az17Tٲ*[ uuucϿ5lҔM:;$eV1{w+fիV[7P&Qvm @rPrԭkc72y2.]RJlEbgoOJ)_r,?R &W哝VS,]BY(Kn(] ߶iÆuyG7 &MbNݺ=sժ,S^=;kh$ܱ0וo\ Æ \xo^􋌈Qbvf yo GGGvS:y2o޼ŅzsĉXvժS9rpIu#G{D!9rq ˕+G pww'{:wLoNٷw/[u~Vs9K}޼y!t>g"9v .{&cTQgO5[v]@ܼEdXMXAիqqqqaL>]M`ܹssCoC-9s4ުukZnm61` JK/?Cn&˗*FHͺpqu'ʓe =nËg(]mnT\ cVH" m`I1$=<@3{~Af`i];`Ɲ{衃̝Ǿ$%H̎9ʁ@ ğ}w2{zܿwnߵ:x\VTT,cNY>]еAVKS1gE̤KAt*TF@ /vdˆkWW֯Zukp1_+8|ێ+/Ң~]֬I9*\ٶߵfz,_8wP^}FJ}$:vP( 5=[Sz ̌@vm^0sB&Nή#h݄sigo9sx ٲg'{~8-{=GN[gj2ès .Z[7d7ww_KKcl ,ˁ@ ď *.]:M> #Sf-d2*Wh٥%J|^<}ݺak,IP@Sf`yd̸E0fѧG76IX>Y  kӮ=n>-[+NZͶwyhrk~ߏv:q2o ̨aiqwٙ#FR*}옳df5 Q;(Q Cٱߨ1s_+ؼ~s֍t-<@7熣#seppHG9uK'gg dTi1I(f+AhQ%,,kW.3L5'U7.v+)7ؼaF:/Xu!!,?`ʖ@vIN Hr=x@|,hQhtqu!,,TB*]'GGRquܽKppӧ'W~TfGLy8BBBQ67|y /_xTHV*xd+Ÿ6]͢?~{x;o^"y1(WwRe2oY&a`o6=YmMʶ5fV0JkmU.]:lvl6|z`#{;wn{knJ90KȨRjm;݈ﹿ`ըT*޽yCYٻc;Murq@36%-$I6wjf/ITPd2.}Sr%KCT2Vm[iҢ%K;;;ݵsSf}%G6`SvA//3d`ӺC#2::ZlԔ̎9NJ. 7f ~~~v_.ٶu+wJëT*rx?{Fb^#Uנ@Bq!ܺIV-ؼ{/Yeu1 &|9sR~Yn=uW82?y3 ™lXӢ7(?+ʝ;V$$IJAqrrbĨQf;$ RJf}̙3>k ڢiSΞIF3-ڴiV?rzݻC*I ~tֻf掿i׭WUk&Y^\ )[A_FAڤFAஏ>Z,?5sezMX1޿{KP` oܠd2iЀ: 䱯E5kt:wI,l۴oސ5{6x7!\xׯ!C*TBšͫ"##ٰzѩ{"##ٽ}>'_>ЉNN6)/'r]@eŋX[ƪT*kWOs7(0o^}/Zs'ZEDxdDGE͑4niԉѪ낂ٳC Y(׉+J-KDx8cfB;}Nbooϛׯ۳ 4ӥ ,Ă3)]V3#_'!Tڕ+iHeZClٜFXd NJ%# V[\vՊe4kٚlޚv_~NL13E}$~2-GΜ4mE)XK{XʔԝK%,6guܸzѢA]*VBz''^xa7#wH-_|IPP c~dm)^{|OͨTUL)_B)teм~d/Ǵز9 ǩUGy6:2ׇ'SNW?|ߧ/}DSAC)t|n%R:kggǼ2;}pN?+ç{*M4cdF -Glو3TQb,YݼGrj&d5ɕ q(r8q0˖Y֬[růΌɿRjXS ˔+5zIA {2e֭f \t9XE[uw;nr޽}$+t(i:d`` ],]P`џNNLGDEGլe6|=mڵg߳dIp,%K6eKyI3fdBve?Dس%JĻo(Q4{vlrXyеMQo5@`=woߊ]ٳȕ;75a"OSSOp,^<FoM˰u?:qKlgҴVٳc;CFJ$jjs *lg>i{{7jlkTGdDo߼CYDq݆ m/ikSOһGWIdd^iFǖ سc *ժr&܉ga23g3sdd2 kT]?qo4hml~QUG*F4iي۷1x(ӧ秱1ER%K|ȕdn[iKs>~#3eҵ9ۢNNYpʵXea9ɖ#;7o+?!6l7PQc{l T5jڔZu|b.}BgOY_z}=$]tij%]t&;8`id3 Ͷ Ok2A_{r[/@LLR\eʕg햭ڷ;{{z7᫢ ¼~]5r IH%(ZY:ZDp~._8O,Yح;o^CHH*u44P^=7jZ]j,}s@ ׮g:J fΝ[7)U&0A IA*UH>=v쩓&(TϟR~?jׯB _Ta$֘ )3޿g{x \pwo=y-A&1sf޿LJr"[AɈٍ @P`@Y)[~>߻sQti&+(W0q ""y a|!Y  Y9wV$}d̔jjq sgquscB͊!+\\]_l ( ޿}KRlCt9ʂ@ ZS^}z QaXaX)SIK H&]Wrwqu%)[jogϜݻ&[ 4V9{ gN汯/O\sSlYկE D@L=riSwqq3zX߽k˗.5y߫WT:ĸ1cylz7h`9,bւVT*Jťj5/?#80LY#gϮѪuk]߼yõW e\p]{~ݺ_5g 7&Oxk*\rV$lLxxEI(޾C=BIH>TNݺ3wnk._8 }ONAc{Zns OH;l-sbHV3֯^ɵfVr9 fbꕱ߻s~`äwry)S&XekmJ~*CyHHG cƌٿ))5}jN@9&lj݃DEET*Q()^`!!!DFFijmoH)M`` jڤ_>T*G0QJUX0k&/?[ܼ~| P7ml 7de9سrAתly ssRG _AAA%g%I"00=vGFUlߺ ҲU+<~3r&NDcook<~ʿ H%i߱#1M'3f4)ˑÇٽs'nnL6 RɎmX`.\]QSR%z|A</=wpΐ *?PxX}S3W.FCXXA֭ϺJpju+O"m __rCړˋ0ܼxfʬ;)ǻy eρBC:~LꗋBW} ֤l[c0 cƌ&ݣP(ldfgge6>NF_w!00eCt%f|  YrG:u ^B t#[.^D?dȐAw@*T'Y@'|ƌqqqa(XHWoj́=PFG[fBI._Ŝ,YdȫT _:Id>tHw);كUV֭ ^@s6iڵ 2$͛7>tv۳`XϢ~:t+C֬Y3sݚ59 6nnnݹ}ڶ۷:7^x;},>~d=dɒ_'O01NCThYّ>}zoް^(Uk77ƺf5ִA<==Ͷ *Ф )::ZSzNh.1FVx-ٳg:ɓ&h%Z_ɛ/+??ΞԬŞŅ>%Ț-A<_)W˗2 B˗ߠA}?N'''FC TҺ0ObЀHD?p K ((];v0e$6oD̙:}zMm]{vfܘ1\?qrrM6$$[ʕ/)Z_b򯿲>FJBŊ?ύۿ?kɓ'Ib+ߧ;˯)޼~̓{co<3fl ćÇSDx]ۗ3f,ԎP0Ο?OڵUI]g>|pc`o4qÇ̔sdܤf}U1_-FrS˗/9z5kՊ׵_"::%K&Jgΐ5kV8AnJ$IL7I]V@$2dȀ+ 3cFò%KJ...)sf[y`4cX@c5or'i Rhۮ'k_9̍OLL^o.Y$;wN(JT*U1hݶ'{мhRtQR%Ο?o|ׂ6|M9{ 0wl_@`4XuʁZf8rLSݼEy\H[ ܜiڬGH&3AEАDLxИ]]]Ύюxy?{;ֹɯW}KGʟ?EN ǃ{x9!!2|8ǏP!rvT6ik\+V#xY@ X_l==asO#\x\$Ea׶!%[Sʧ9J\T:CRa*~?{t ҝ| (!!f899ǕIj5׮\۷d͖Re3^Qd)rʅ~~*RX wŊ>=wo'*EEaFL[L[&HBcG>gMot֝>z9T^*]/\{wɛ/CGM/:P%n^gOR:bu>UѢ=umafF̶]'sqݺS:/_a*eʕlV:_\Ǻ~&oLcBppYW2-Y>|`5VϞ=ʁ9sf”iL=Zzşf3|8FWy~LР7_s_çżoĩcGw2Ǭ]C{pynنL&c1d˞LF:1$!_MϞ{؉=;w0pp+Te 5&ֵ;nւӦ|pٲQN]7;{;rMF)Wνt򸹹53~tܙk5?5'}߫sԮS'VJXJ?2"f,cUR0rʥ 4mؤ)3rΞųٲ~0a*dȐBǏdϡnǏ}twd\[7Y֭)I&Miܬ9}wa:?<,#F]&[ 2??/U*FX@#J %UV^;z{0zHLӷ *DUp<,0&ö[y=֨v&Jɓxׯ]3a4^&ѹkW~67ҷ_?|&S*[WR* D$\̎#i؛ DR*wg>thՂmNdɚG(faQXX(cdϑ#$If5-~ هĔ/Iص}+5'W~<3fKuj,YC~>eYoP` sfLgѶR@$u5"Xxn}?-Z,?gp-.]?60d =L>{{{Μ>M~;DFF[\!бsXS*W[{`A8~p>~Ȧܡ+5Ι=pBCCyν߀"**f,^[7oc]ʆuֹ3ǎ]=vdʜYX5%ό}=`VWm҈5Ӟ%IBngcZkWDDӾsGbH_ׯ/=d02?gv]߽]mf؏A)22ASV _3 \ f…f9r!ŹgM[:˖+_ݳ{ ۛYsrwrrb ׏/_ҶU+ t33'>wܴjݚ;vvj֮֬_F v,¶;hU7j,_-ʤ3YrV._F畟>>+azsR@>~0yoώܻ6`6%JS(_{oW$IDAT|ӡAAs2kQ&˘Q(UJT(>V}IBhڬ՛h\y?z6s}8zk׮T*...ș2eҺMifV.hݦ e˖eyX{+hqrrW2Zr岘 i| fbʼn3\.ׯƻUk@RT* ]f4v 6ma/X_ƌ҅/==Ջ0apUs T]{vS뢔.WP߇B`ɼ4lҌ9r$kjMl$ټީT*l=J2VT6/{tt< s3N+::HAR%i9&e-MxsT*Q*6)0m@ji*dif } $1e8CdD>(V$kf_+)]Λ  I/?359.l@JdzRl2m[]5hڲU0*j׫RR$IJr>-5 Z>C #GPOnT*NNN888xfk؝CCCYjɚ~dd$&/_iM8::ɓ7=K֬dȐJ)O´hz_$I!WJ I6wvvvT*}lUJlwД}5ϗ1AAdɚ-V(2d`Է">zs$YJ%jtY׎;3g$K?5f-b Bel߶b$߇l\r*+.Z;DFFrUV._#ɔ9 :$I"0 7o0|@?Tj_+NN]^ˣ=s-xQe jˁ p=[hvn.Q4-1xЕJ%[vw`h޺>k̛ˆ+u+Tq㉌@ЌMđ#lٰKl]# UAl F(H]4bDFB\E4_1?tHXh.\.fT^9rȄG Ro߼{ի888c4iђx/Ǐhxm5H\#bl3S|hْdM'00777:kDž ںLGݹv5M{{/_h̙׮)8u([7ns5Ajy6*[N;W;8w y:qehҴ)-K2wO.]@5&2nOt]kVոT Xϟ #hԴO/kE H.*VJ˶X#::4pww7pM.]-QQQDX(d., QêVy7 7:$Iʁ|9JŊq)joswCi|eɚ-;2o^ӴN- kؚQH&N,4WWBCB|p\C9y4(hR-2 "/q%]ʮc°wpЭY9K\=Ye㧱w;nslٰ-ZQ~Æsp.?G5ؾeYfͨ+C0 pY,@ocώʁZfLu"7k@쟖  =)_2Zi&ۑ+wnrk8u/BCBqrr@B-_{,R"|x(q(Q0 ,cF_$9sQ^} .lr|ȑ3' 槁7xd2UWիtޓ{wnӡk70vvv*[[7SFM޼~UXi m"A`ώ4nڜ:0@tt-~֍k뿴߽}ie,YZ=V2+jrR9ssQj5*Uid2+on-^)3g12@ddNα89;Zױ1n<.}ds&4$ŅWUK\.g_X0gAAȔ93Օ[6(qLAw̛9lW/^謖dEޑ9KoDXXjsOܻGrF(_8G'Sٱ WboV.^u7d?pt4\j|ױ.{\xNN4jڌׯ^oJUq~)w@R9K-\:LVٽc;CGAco  kTӧd3xOiݐ9LoVV^1}Ν>Ŋ˖ҼͧO.^|ɿwT@]ؽ}M&MQd1BEPHuꬋkm䮓/yt>5չ;8иYs}!Lu"YtnR=u^^PYό=GNt4iD2pe=<6ʖՕ[ew 2^4ndZW'o>/[g9#/\ܥbm+ܣ׏|{{{͙kMd̜KȘr T x%kVL c5lD,^P( i7ŷ::_B9ɑ3'9Lg=GN]AF t]_+Ŋ:+@ HAĀD@ ˁ@ ȈF LtT$^2n_9x'''/on,YFwysz2vvv4n֜6&b$=B9ϘoDb@^//[i gub<Id:v+.׺` yP(l4VϘu+  bH޶1N *S'yP CܽnUS[s~]GqŝNq/.-Rwwww`!.}s$I<w6Ⱦ];tr% B4z3~۽"GNo^]/J1##'Ld VKP` Nq9kV޽}a.J!+ đǎ+eʚJQڧ ^$gR~#j:vN;}*iӥ}XNR~޾EM2g. \|)g\ΚM[f٦jL&n 243 ĸ|Mܿw ĸddڤ TR5Vttt''ONHHҤe֢ ۛub2by^W]\yY@k{,_6ﻘڦE :겷~j5z>h4bkk+`0V,tG|R*:GoZq\FZܺi%VVV71h4قs/Jhל\|p_|FnˆWSR% 'K?>]:P(bFKnF1Sg  RL7Q*aoocp9]RgDk^ㅘ.DшlBWĿf2d;vD#S 8?x/BÇkBs9P*?Nrj4dʒYJ dS'¿o".zsۿi|_tlR)(RD Z7j*[BEJǍ|H{WK_,;X ޾y-#щ~A C\1͘Xzf؈4k<Ύ5Ab"4b0ܾ-w6SsB|}>Jfr.׫WTUq ć-[#Wn>ޤ3b5IV.stt*RyMɓ?|G0h(5X!44QaB߷/ 5~b DM,|woX#Ȓ5iԫ.y:\\]6f^ɝeAp<{7^I1#R^Jcopgk^<{/]nfuf꿥Ta AߐːH8;;.'1A"4"fNO8'/ӧNnѶ#((ޝڙ533G?N1nnn"8~Fq`n)NmwdXLY CTZ޸nW [;;齋+[7P!Zi]:uj/>¶G^5O DpIYsq|}}qvv['O~/@5^;jd%"uy7s:fNGalر-о=x S`A=xlMlԼ9ETl1B"`Q[F^iP͙7oh$G{@Vc)ZWٹ> l!EJϏ0-$Aϝj͚RO 3]Aģ-Q2N7nѯL=MX1X̭#=/sg|]{ z=3~Lz [iZrG\-KI*5֋^t1??kWsOɑ;7zUO=b¨n Uk֒t IT@@3yZe̡}{2wQҧOI*5Hbߴ;nlB^{y"UJnGҥٽ}GσJfjB7bf 30f33LOo$bղ?z 4%yM&6βnihǎb8zUv _[EZ2eX** WdX.C*$)(W/x'Y)J l|Qk4X)Jyswd~Z-=:ߐ-P'Thڲ>>Uٱy,ޞ_ N :c97Şٿw7U?>~ܻsMgw"-K׬un)Ӹy *fggeZ!MڴTZFMXH4H ز[6GIҶʊs7n(TpLM>;{{z MP(Ȝ5)SIcCTRfuj.j֭'u wpl~--afEp mNAJUY.c]888!бe ׮GM@.]Cu\_+l9r艄9&2h(Ksկn?cNkkV,YL>;UjcRf&A߁yܲk3wt:$gk܄9ӦDDi,BTN{x_RhXJΟ=CeXx!bjȶmڈ T#X!{pwoU+Yj:=;lذ[>4Q+.Tnj~~̟5%+I wnחeZ?r kAQbEZɔ9 KW[>]GO-޾y͊ A àףj-w /@DcǮm[ӠT=WZuf6CYg@!Ø2~,YŸ;|Qjx#%l͛X^ 翵"8twxRϐ Kή,rMn=48`9tKӢ~]>1QsFve5{v)}ʿ8sd5p*WN ]y hCz~ fͤTrs9 2+0f`zdF{ڋߐ=W.´9x9rJCy /Yd1Ԭ[7~׮C-+.{-wL_B4i,zs!#uKR( ;FFATRvvv;8|0&4+۷zz-P|q(4$i&P\i׭#{L]|>]:h +ΟE"$8~%s֬uWWW' E|'b a7kFziesȹyrI~hYgg߮kȴW4! B(הrȔ9 k6ovYcFmgo'>9nJfRt ijNzоy)f3..zE[7舓@!M-p)Tj5KQ2jLw.=GЁׯ^qp^ YPz iV?Χ].R/ +++6mFi.R&9=A3Uk֢RjQH3%W/_b]tރ`ccˣjT^ϟ1__>t]lÇ}ߵ+RjU\agX4H taa8*MJ.UxMɞ3]z8] ػskk2f$GblA g/3ZѮ)s^|hڕ˔X Ʉχ&ciۤ._Hhs  +RIͰ=:ƅgy :»oػsw`ܔz^>ƲE _hJ8 Bûo&sw'_|X~UܻK|رe\+Y~#c2 E"%z@AWgObTjRJE8q(S80{R |H)Ȓ-gjTjƌHAxUBEVhVJU*Ve ,?ًqcAA,@A ⱂxݵH!m$O"d2q)Օ\y]AwΟ# @zT(pqu#s֬88:Ƹ/\pb89x{suouΝOR-E2̈́`mmj} ܃wn["l6s%GJU*UQr쌳3E38;ygȐJFm61L}18lZܫjT* JPHy*Tcgg3&o{`opppRwzr\Ȑ1W/_BR䄳s7\خ!.z CX[[SD8/${MW[6yDf͘!w6˳'O|ޱ ϘQ%rzrBCCprv|*T\E^S0 =u֨ ߴ'M(WKϋq_FqÇb4kvnMy:{ M7dZXnl Q?۶ɝd\̟+ϓ/?S̓~)IvymHM IFHp0=~i'WTtӗfHivXZOܺIhܼ[6u>"?N۞ݻt=ى)ɝf@2wwl>V!޼~i'bЇ?N(V4*WiVtՇ: J_6m3rAB UԤLZhdE۵C̟ ~#ϑnLר!`XAJFCZ3 dܾu=bjիhdќYXco.jo@l٘0mr,Spi*3LJvs~75jE CuP'#Q )V)S}qŊqVƯis`kkKҥcW!&Q2~v "Eq|}}qvv68X0o71*JE՚Xt ܡvѮ;_>cOAHj\(\/?' osU-yŨA4}f`d)SzqV.'5Ѷ9шdDΝ>-vt )x1)SX/G@ζ "8(K1V۩*r%ɣhZ'zj K D,[mcc/0JK^ֻnݨ]Uj䱣YShQwwylx!**[_O?W/^Ppa=/Pb%cKޞDmܪa}ܺI Xo\]])lߴ^^HU.Cxo48r7]ER;_~*TgOy1V /gP 7mQR{$-֓9k6,(" ̮m[qvqE~6] /ח qݼ Ov;80i 2yf{w歹;tɄ33,"g\hέܻs[ *)Zx :l΃{Ȕ93o߼yTZ-۴?"e*\8o/wnb!M&G 0 "08z &OKr3QRs XH|_ 6c-.V\ZVi/Lm"-ɓ'H>E-TjӤX&ϘxAAڒ=GNԬLgZVᱝ=9r&k1nW|y\>|xS;w'z(Uh` }j-2wTת';[PN֨Y3:f4ZXvиVuN?Fr-Ա<{)hٮ=Ye|f, |[dmآ $5,Oɗ?VV]GzMHN -~ػ=z}608ǏbъU LG=uz\R#,:8oN -` K=x@Zn2fȨ1L8Ng}mv>g7aaZjo Y"8Bs F]F<{[DY\JdxHAbɕ'/ k\:Nb R{l ݋]۶h0c,4$sz2}⃷7aԄ1x<[0dh4m_hU˔{,96#wh1󲵳yڲc&jo@9q*y v <)]>0dԘ(uQTtbY=PUxf H+αÇy:3{ Y)Czv@ CR2gԪW/ӺqCҦKh͍ekFð1>W6kxd R1Bb"9kViȲEҳ2[[Uz_X(AF9p o޼ƖRIBA}i߹+^a"eUk֤b~rǎGU7dc1έ A!}MU.эB;+   ,AAHD́  D́  AA61e  %AAH}  B%ͭAAh2F^AAL,0._8?H%]X Ed2qpOH-wqr%5R`;c:Ξ6]!E[A̫ ۷8z$e+.c8XJ  b@\ZFh4F9O_&.["dۛ}v돥kwt:S&⹳6jDŰw $$8V޲k4 Cf3Mj- -oV'|ŧ`>r]R}ju'!(Jn߸~ryH}}sWĽ&a5NJjfsj5jZ+ َQv@ThO?KcTY5k,/_l۷HOrAf/^B%rf;B$}o9~HԮ :ӥ#aaadɖ )8s~ G?{E{M;֏bSPT*e2Samm-[4&[#NիW̚1?x=NCDEul?F&4$DJϐ1#666:v+]Vϗ~^}z#߿#J9Eܗb2|HY$ *U4ϝmPV-N<\/Oٲ !!ݹ{`e?uڴ)_,Yɣz2/?A#GQQx=F5Xzu/C|?;{{F2d[h4^ ѻ~ +\ʣ\J@f>+Pz F#:u5LL&](^4zkG qH\b& aLef4oz"|>gYC&O޿{+wAۏYޝ;x-O<֍ߴk/suj-eqP 3ܺҮSguLQ*\r%;Wts.W;]^Hl9. ?u2)lް-1>ܾyb;bI*~ 55nB)rL4DDRPa)MPPn=vnBܹ-ַb蘱AJkױ3;w]9kVV(wt&u A IZ\:Uqy_:E޾siDZ,WT\X%*"8H2g; oK`eeŮ#-,S֬'XYY:M%?i*`!̟N1 v"kkkG-+ 2f8K*5dĠt,YΒ |@!Tms1"L ı179Ac"0~v@!X1'nnrgY%A8r5zu%e k0j>k4˨ h@PD5OAJs(] #Wnr+wv!F@!Thg-=sS'LJFШYxTEp jA׫W̘<ҨY z/Rs AjYRV9rϦ-1SAlءCL<OF a.?hGWV"'ggϤl惷7aaaiK"w$3 ''g5oϙ3mj֪Wroo_*KAN} )_k[dΚYk`߫Y6rg_HL&/gT)UƎ!y?^=g.{-=&kHN@PG$8::Ic]c#%kVY$"8H.X@BCGbmmǫHU_k TY Ϛ];Yx'7_'5kަmY>'O|4nޒ}w2n0֬EZ!4mFE)S!RАuPP::]%kVjשC\1X9MvDp988лox?/ZxwJNٷk'W/_ z=)QL[b2UjFMQ9q0;mkۅ7|Up5j%=}kWA^xJ#Vȕ+cǏH  bm.BhX* 5h5+y*O̗֧ԨIæd gܼ~t2@x{ܺ~=ATSǺ7-c57z7 PK=x@ԩٯۛ.ȩǤI'Zo%rF۷SvmRICi9w洴d2B}X#{NrI~X!}q=GG'nHgg~| :Ť3x,oސ͘F_:2xhO(|k:jAbs&;l^&0 ز9JMRNeCC~<^xAђ%nާ/ ^J4S1{o_M念 1RZoVo^{]DA4HLĴ9xKQHQCͺ,[˕g-ԩ@J߷k'Y%E3CFf¨ܼ~)sf ^}ݹmI$#'Ld;{{[3h(?NO]:Fú;0M4lbLLpTb8!D4m ),[[P*Q&4k^^ұ[#?͚ݝsf1j@e)SfQl^l. F).Nb ģ_kŲdL"[Yr*SRe޿{˗1Yܻs}vXYؐ.C,e7ޝ;8w~~{xPb%ʔ ]!I{Ʌg퍍 YRBEhi1lflSf#KlWѷk'߿'] EƓG돥.~&zڛ gZiԬ'M8 6)a44V7}e*Tmh(Gg:]Xc|Ν>-wXfs¢t:z}NCTb2& N'k2_Qa2bu :GxH7n\G1y(J+3uQj5֯βy:6kN]' l63tjaB1 {J%}W߸zƎl6cmmM.])P0~~8zϞ%[vxCQ]yݹHz]L5s Iks[Gk툋E& ^I?$fsQ/mQz f6CCrǫ/u{W77lx.<`x9==/"Χ;шFF,"8ܫT*[xt&LNruTh`0`ӵ^/_Aؽ}TOc00r8F)Sk]l^_SvqI]h5 z>d2akk+dh4VEzXX[6o7^m_޾glΑm\ܤ4i@SqSRQpxh4<'/]>we9~ı娹0 ( νB@RaccChH gɖjjvۑ't<͛͝t;;(Y`<|*kJ%888Ds8+`h4ꚳ~Æڽ&ګK4 ߟGl|S>϶M(["iӧh\c2~0;n0`RJ-w!^={Dm+P/U%J 7\5=-^-CFap^S䚃7npc0߫VqQq?FAb"t́=coS ѓ~ܽ}K z@ξ Ļ@鵳KR$aC0 >i|9ի>ىA=zH1ɟPt n\m€ݸ{*Yp,Ϝ.[cAAH(6А8˸s&i_\ӥ[7|s=K[>z Duŋy-۶Vޱˋ]۶Ҫ}-^Wf?kɕ7/aⅬXl9rRv=т iJ?-,?WhհO?ųgtmۊPb/rL93ʫ0 }q][[RJgNcD*Yd$K,^닳s71z2f^@x;<г_ɋg8gD-;2gɣ\kNekcEI2a/=#e: ]l!D Di޶J]z=[6VL9KZwԮ׀*5jry#V,]m ̘-\ܨY7L<׮ѾY2gKĬa;}{v(dŅqhݳc-u`]'Ջ ^"KڌN{RfxׯmF=}-ƌ3QA)J~5'F!ETk$mlpvq勍pvqI HkOI-Q.eW`|ƊzqZ6[l۴U+.;C: f??=\\]J:5RGSHZb| $~~?z^ω#I!Ōl 3 _:gХg/f1F IAH|DDn߮dȘ/pa<'gǖQ˒-u7dތiLcYso9AxغbHexx>2dX{_iX*.TV`_RŅP% #Qs=}ϟSLY)MRQv]vme}GGb1Zڿ/sOeX_y8 9HX(u֝o^hX*e7 u4xf iY6-Y2|(V,  bn1B<9ɓGIwus BEY,S*.V"-gf2yf8 BAA`  D9  $m>V  $]́  Dp   `A D;AA@  1΁  ̭ Q~6!*\!zΞ僷76d͞"Ŋckge]??.=@ђ%qvv .?۷ ř;_>($ 18o/G H!] Xccc#-q*k@BPTrGG Q0)^BJ{{+l*/g򊲯2+0b$+$hKA'(0"-(0r"ϜbъU( ܻG]tҺ>>,=o =VBjtmךw`@\у̝GZmL9h.}P/XG''^'eu>M vaaazL&, DףdZ`2 Wjhh(z^P(P*k5 k'؉eΞ:[7I>/DӡT*8m&sd\8{uVdȘ)FejL`` j<ZшhFYo0 Tpp0*J WT J WܿwɄfH"%޾q}Z8;*M^xAZ>cuǖ$&S6FT*Q1.o`z^GaaaFD+XJz#g.Uơ >L-_`TYcҤV Μ8NFQ AP`4e;~5'Wp`6e+dBRacclf֭2~Tu=d3K 3 @=aќYX[[[`"L::CLDNWTqzĽ&a5WNHD' Ph4`ǗCįO5F__REEcB9PT*4 :WOdˆFAUw&gw7Ͳ~F#JRk^@Pz5tmx|)/Y ɒvIgrEJ-ʣjц%,\ [D*88~+TL&fs~=[[[3p`j|ڽF4HLϥw}XfM??°g88:<9\zV   }ƌ_\ΎRej߿͎-5eؘqP? 7nv~ụgd{]?'OWIX!޾qjL.R0XG )R~Ihmh(wo{EgI+''ܩS 9JJu2MST))(Xy @ӱbb.;KToӦ:v _Rz"E!^ܺIiO6m.+IB8z !!^#g.BCCypy'g MEI:5Z3'NuՈPH|bݕQYnӪ}n29w7_c}RUKH& y)9x)'M⹳؞o;{6P|t:ڵ]%8|ܧ[D Xbk//^x/_;R/ڡUD1:b!z+$o^{lm[ګՕٳj<ԹȘɓ<9wϜj{g^5iҦbTQSzP(pqu%4$[;;dN ۠!*Q)$! bہC8z[ׯ3fDnS|y\>:H 9Uqu޿'-/^9sG D|&oRjԨ]{Хgo&i٠u4"!S# zuquӧ,Sej][[A<'_hStJ.#wEH4NL)Aglמ\y+O^J.g,8z$T%U4;tPJ7 eĉ)^"A2 A"v (H,5oQ(de cƒ*u?BvujW*OӺf”irKAg1έ *%DIӠ!u4`Î2fd 1" $ Qs   `A  X@3AA `A  X2|x  IxIA ⱂ  ,cAAQs  DKI I[AaxzK-ҬmlH.=YeCREl#&m:*e/qqBCCɚ=;jF" Bk^6FQzoeeMLX*׈ڶi#˗,ųgh4V)_!`e-B c~2# vYT?e*JHjL9;Pa(&f .a߫I"xέݹ\y2gx$O.-۲a=F:<۷X?]$!cbLUK&I'9#=r?:fulyɱxڋ}zIlll0LxzŘ!N:uːYr!>yQ#*>rݓ+o:gn߁P>ąg}we jzgg֍mشv-} \_Sv}RIB~?0 C4h4 ~lD@@JRZuG 䱣serͅg4z$L7^^zcg59K͍3ezN;۷I&W=88ZuwL@@@YVd2a0d9~@@:ֿm`&J%aaa_m2ww*V@z}7ؾe3UKШE 2xzӓM#C.]c|z-~/qI{!0z= xORakk+LTV meO HP` O=<(ݻd?vJV-Z?d;.Jp o?ȝ_<3k_&: 00PzmF_=ArUdP(w (0@KUaژiU1w 'ONW w[)ɜ5+)Sk [jg="E ,$UfvCNh_en0?P::LtJ)#` ėebԩTR8ELΙ=|+RCϟKSI Bba2c|}jȖ3v +7.:FiٺuYI>9 # [QD.iPz>*ܻs;ql6sh>W-Z$7^~ =t@ZPxɷ h|>|lZڰw(@*Uȗ)k7O=d-ܽ} k`($h$ ϲE ٶy#{ t:.H]7d]UjW={z+O^&L.mF]y5KW|Of wOQWy۵0~iьYr`xp`A!Eн;ثEZ|pR7x(# ųg4Y]ZmWvoƿVk=Ӽu7ijX{g5o^Bf-hަ-˗,cRMe.ݨ\dG X! 0L޾TSsYJ.--sttbۦo܄LD חX߾ұ|"̛˸ߧI}>|H9=R`I,\`L=}"u]L*5蹹c؋@TD٩X*Uk֊҈VF͙͛^( ,Ȱ1H27cX4w6L'O|>x|IR- *L@@/_<烷7R;O|\ɝuA;@֊u4^o޻d 1Ɗkb2H 8!A1ܷ"_cemM Ș3~~8zϞ֨WJ(B8{KϵH&MXr*Ge &M``e+TqBW0g=-۵VrYݰ~}1Q3fCPf6w_,5Vk~ҥT#$? ?_^͒lް[2j$%gOb} ghy:~?Njemm[d'߳M&`{x>Nh4bckKR0LcW}1jԩK9 fؑ)Sƚb"cFc zf;[7nr9 c'LhhlVhQRhh(J: s/WU*7 s_pf/^lڕˌ_OHH6-iӧ\t#GL@? UAʅ3g qD|}z-~/qI{tuIhަ= Y6Rz/MֲlL&[q?'Ob (Jl%wAv  3` z=۷l7jނ S 3ὗ&P)Y "ƈ6>MckkFIcL&t:le}||Xh~~~z\VuӧOJ2V(PH|~/'SҸy AmQ.׬@RY[>\|cckkƵF0h4Gl6je) FlǏ90Ld2Ra՚7|(FWX7o`kknbqVvшdw5;u"g?kDDw8)jloM2wws\\] q?iҥc}j\(ww{6u˜<&ogoO>?.>y²?|;oVE >W{ ^x!wۣ w|PbU,9v=AHj"cj74nPn߼艓qH'&o^F>g~nǎo^|A#RLel63}D@ACQ.>>>gƂ BF;VAJU݇88;;GPV-N}[*[Z![ڷEq`1;8+O(  uKH mpx>~E,p>V @>}kzXzu7g-18"ró #1ʘɓDI7޾SƖ'Qj5Yh69 &Mg\mm ֭%#W+K>y$ܺq VJU9kV)}]d͞LHigO$L|*ՆqZ4h[dq߇q)Yt=g.O |{wn3e8nݸA.]ح;62vr"kGz0l8RJ g:ql-ܶEzo2X/2f^/?gxS~Q7oW8RjUА.G6H:uǤB Dt ogEsf3nPD{vn?3ڿb/?c,]0&NRzҎ![ɗ5k}*!V2aٙMҳ_Y?| ]<334l\Z絗6/?%zklXHŪհC\:NZ⹳4[ J-OY>"3m ׭;YcR!j\H ٷk'-ڶ{xom7[ׯzǭ-g5} fI_Cl(( Tj5iҦr&F۩?YPHT<@H& c (JJ%۴bqrdb7s:^r+ɖacI.) Ț=;=3Gy|`AFBBBH 1$~ҍ{HiSdlر;wĈa8foq9>yg,=Dp߽ReQn=f6"8 aqڙJիlmW }<(#Fg]_ժMZz;GG'ݼ *>5o4&N!ׯ\a%K8v޼bP^x{[TzrGUY4gY!yE\-VH՚ZVX!z!n\l6̟ysErg7dJt[[x-_y-Z6}zg?ҡyS=|@UptrKǓGůAr%uF)Z$#o_,ܼv,ٲE۳h0 BAm;}*Y ;}[ɩ\:F`ʜ899c2 fʜy뱷[2K]DK'bW/_BR;/ۚus(5 1IDATjgQ 3vvYq4:LVrJ*5p iGX "%$]nnhNk_µ˗9}8` _B 6Dwo8W9zEj*A#Fo|}}qqu5ё)j 9Zuv]DO$bJ2ڮRJZBvBZe~Rnl6s5NO%پyY~P)RjZtݛvPQc\ݒqy޿}v. cZ#y,#E vCF@bՋغ]۶s͚[I>Y~0:W.s)Ξ<7H& %KeQ+Y2ʘ"XMf-А^'5ԕB"'ŋx>W|ءxfJ.W F(ALf^[pusB*<~)S|hdϙS$ ⱂ`^ҭ;.nݩP*O<&eTt̀1ͼxcFӱ{FO̅sgٴ~Esk^Żwoܳ'tN\!@Bx'Ffhдu7ڥdټl2 :fs!?!ARoX8gCCղkVڎ:d"}ƌmhEID́`#E 4pP(e4oӖ's]G6mɐ1 G_5\M[z";naʛ^Ohh9,(]]q)Ο=aC O|+Q%J`XܭY5֓҂dKͺ}>|C۶3Jj||+ZVRH"8ZyڋwRL)مJ%Pb =PZeUDm_wEH,RMKfiج9&s)._-!B2,*!g5޾XRT\E=t.2l8UZwNZЬujիȝ7gkVѣ,SWW$ W{ )Roo)]m"4 5eڼlرyrY(J%3@!rG}ث'+~6ƆTS)s^$gf@V٤׮nnڶG1'sR$G4iђ=P(ș'=*wA6W/_GvhZ\(^4CGXmx^/!ҮSg<'ONԮ@b~.m[&mZ^xAe఑tЎYȔS:ϛ9W̉Jׄi 29A@e+TlѮWvSռ-zi`ѠIS(ٯ?J&kz~?EJY66<*k]ԯUTlŊa5@T@TN3 Ă{rifFRR>[=,$-Yg'kܺq]-ʖ#yO~]?UTiAUfшRKTjuT*ܒ%x)gB8?^zefckKTkeWZ4ґ@hI,Ḓ ~Zx fD`2bΒ?)^T{V5k=xj_kd4JmɐɓURFM׳uںoI *L>}I۲@a>~EIW/Y!Hc*D0R̚lnFL:]Zۛs`ngEK Q*D˂X`>. ̘̟9]jJ=.<A}zcS,}y ܻ{Z/]RAx,J5jL?wo[l?eƌj1ܺɯݻb4R 4Q,_ ;vӡKWF#{wl $5Ѧa0d{VMH&j'4???L&S l"^,8R&RrU>|fܺ~Wz2fnj>Obgo/t=` z].Ԫ[ &#y!/^twK`$`;x(3dn6m VdB]NC` $$$Ơw899hnkgNjϱ刚m;uL4CBBlK"suKFيZ6SƏܹu-F} lБ@hh(1~"O%5 { bD4 >wn[0r JJkФ)_5é8uM[>~XiTO Jq%+ {{WDr)vl,/Z<ӤmO M&aaa-,,#GҡcGY~'Ab"6mZ߽U˜<)szgDr&A5L@?NZ̅1~"%JCٍ˗}vrNTdyX3_Çx=NXXVVVVؿo|'ϘEz  b)lZMRT)֬z:G,s98BR#B?|(7GҌMZ]ܼvkVg].}zWg5LXҥkW&"8H;~|7uk MZdܺqtЖoߒݝ?ԉvEbd2d<}jc\WPH  ,ȼ ,r7HLjDp |Yxu鵭 WxdzM-!1́=a߮\07BTR/d2QtiRLV?s,Y^Rh4re޼#92RdzW7,AA\<BA U;"8H"֬\έ[w6-,,zU*ѱ[wi+;~0*U.MH*5F $8S~̩  .nW,gJ WUг9BR-۵O࿆ ${w0>iy `%Rp36ӣnR޷77;wl6+o^<|HPP &5wCב;o>޼~MLh۱3۴PѢ8880i(&͘XBA`6ٺ? zn\'OҲϛC5| 8'ث'2d/E3'#O@Rѣc6mXGI޾>V0hH9hlݼ~Mz=}bDGPXLQ޿{N3KftɱiF;3Ojx޽}#O4%j?NT!^zNZuHsp1idޞC1u8it<ҦKUS DQ75VV,R*Svm*w5Qsȝ9u?__֩ lAjFl\/׭K6oXǩsѧXhy$ʊ'-G)whCCI.=xzEhHHcXYYQb%ț [5hH--Qsڶ!pݼ9{~) ysCg5  #Ɗk?,`^b9eWё򥋣lȨ>o⯤@$b?rJiN,]}vQqI:uYj4i}߸zǍs^ 666TU[b 8|$:Bɕ7/<ׇV?P&ME.x,sۛe`eeͮm[Ȓ-;z2pH z.={GiܿsRIOiٯ?&[gΒ^Z:yZ Q D+;k6oxzE*(RH!gˑq|}|pusPّ"U*^>#7Dpeϙ9sEI/X+aؘq˜M+BEQh1dqdAOT*(ullm)WR +O^1|r B0L5n/Ğ9,̙6W/^H .L94Cpp0%˔],;acƢjY8{ Z"Z*ٍʊJUѼM[TB̙цPdi~M4 c,7G RreZw(w5d]aJ[73 f-\;߽sf&I D` BoĚ[llmx)&IcFkV4h`FF#*zT*y"8gⱂ`[;;T*vvX[[ڻ)RR,-w_dϙeʝuA~`iӥc?jQDQ*4oݖ#p *LBp6|FHkkk?|VSqZ'޾yMQ'\y-YM΢ ?gм)gOޞ1Vvlٌ;)Rd:gNբ;@"dx6J<̜2N'w6Aоs.KEY~fIpP> 0 ?=/ ֺ/ 5O w5~ _TN=\yrLPP NT*ܓ'^,[˝UA~P7o"[\p,YІj94O?fs9NVVۋJ"}Lj]㓨9U %JY֌4ޝ;r`n(@æ 2$ B}c\[kσpvq᯵H"%ɓS~CiRоvReYwusTrQKB  D́ B"oNmlȘ)=3jsOG''r+wq"q56_gfccC(]<2fv .O`` Q24$;qI^{yR̒Sx ,n(2fϠTV=mo^Fώ1LdΚ?V+wq"s!e3~Dh۱E'У;O?=E:&uj6ׯ\a7d;w؂ qKP|}y zdؘq>t>TeD:81G`&IGWA"]՘f9V(BE6]:>x{s L&sOFz9~KpPdP(_{ELvͦ'nz@i ;ދ{x HG *!t i*DD)$e3)@%9svgv{1j z釺!e|чp8Xa0 wދȘVEq'˲s>1Ve~n7$I6kq?z)E#1qHz Em LӾ7ۯI$ ,k#~&y;5WWZryGkqZA[n7V9LJ|^_2>JށzNݺYYq&) 7FuX07gjVmrչ{lAAl_ݎtr JQXVR/l6dYv+jN\.UwI(0{nobִ)p9Xr9xj_~'>@Hh(~<&8.-^kJZXr&$duPyl2 מݻQ[,E/IWQzL&5<'Ľ5j 00l֫7F[ܧ'8U@ZQ߼Jz^H`( dYv Ȳ ,|EQ`6%A$HtGfꍗfN, 9Ӂs^,xz$į]"ϙcJJ oVluw\e٫sh4"Sߍw"]ksP͘6 /Y"z7Ju8\IKîXz鋨9>Uǫ"{n9k8op q/ޗа0̙ܙV/6mP^~gI7\̉oa18(v%zDqM0~:!!^m>qTժ ߰j |rNŋ`4œy<vW({pQNqri#ޅ֪m[} ::tA 9m[K׮;)m۠ѡcGÇq Bڡjnyg維jBm-p?8سcءY?%8c_ժr>5prL&4hذ4rA9UfMԨYD@PPPA@@@SJZj]l)G&@cڔ)ZjӉ;v`cB^?Ӧ!alܲEӜrrjpwx1ڴm ۍ3g E<5n5܂(AϽ`0!|$4y'r]7!^_`r!;;;'>Y?_e!"apPA$_ @me,#q.4mʕ'ETTwܸaAF.^m[rвMTQ|HHKMEkXg{z)/Сsg1q矪俷={01}L<;yzVYYYu###áCXz5 zFFEaׯǓ#G"88'p̌t;zQ9.[Qpa'P-:o!^'\xXrfX,3` &L.u[&??\~ q0po1o 3oZ[Z1Į >X&2˅cFa4.{o9>޺i#<_|?bp\cColtmzvޝ9.#g^o#zfO-[GѿkgthwlԬ]vPBųкML2E^-+ 0Of,8܌NÀxcBv.lV+~5I jթ?XC喽t"ܿk6mWq$:,PYL؈c03, ,sƒƖ fa]h׾vm߆^υ")H܋/#?O?ƿ ;D14ŇW!<<r}{EQO07 Ca섉p8X OP[2&"s*hܴ)ϲe408܁&MWXGp`W^ĚM_mz_s, _Z\өntY܉:cD2IAXXx}gƆ9o'2*6ND pg.Q5tE^S._kygϜAZ:y0L^eA'IR:բQ-:4jӍwsa5X,8q8wAĴ)S6>{=oΜ~6Fvtd4nflfaYrJZ߇Ԕˈ_QؼaC&|//%Cフ10r;_Rtu hD겸Ne6xqtxmؿR3|͜&??4i [ g-^"Ӡ> vo/α[񓧢U۶rzulS?FݺPɀN]tygPpե2f?WGTTN273M:kVBu4dH8Nt$^7 /49nX,hԤ Ngz'7ɀ=Yӧ3^r% UE_TzcINJZ*oJ]マ}bxzdF N)㟾W| ڔ:ucp5t*S$6+׬c0&F4f3^;#s=!!jZa-eKU"4gk#~*lݼ TVBuz{Ė  AƱt Q8fIcpPUG+V'Dիd:s/7ߴ]Ќ h,ѠQ;Fʿ;c8v(a411/y0p ׯ #} g":wd<""*ح@DDD D6m*zH-Z V٨UKd,r ww8( l6L&\.n bw~}'Gf $vpTqVFcԬU C~X>R`p@%`0#JlDQcgggC NEAFFBCCݓh6}}uMpĉЬs5a'{JlτDQIzz:BBB8Yn"""`p@DDD^unVlȲNZnzV,I_OMOc3AI{ӭPc8cR';;V? zi-b| Oz^ykM^k>)TϢ.z^&lϠ@THQ yy-nľ$Kދt:Z$ ?E֔n^]7Kt²v;*U$3 v@Xt:EVw)4!Qq( dY._ g]{r Is^T5Ww"""`p@DDD Hi08 """ DDD4"""`p@DDD HC JbU"""Ѵ( ci08 """Bv-U\l9 """ DDD4"""`p@DDD Hi08 2oKD "8hΟ;cGQPR%ѻDT!18 2}VnUx^EͷY| "/ u"տ,{+"=o:8(tn,+++ 6 zvݸ~zf-mp)%BEAff&t:vȲ !ׯp׷>].nܸ-7n܀$Iº[233@ȱkM^k>BCC ]nt:_;V&I&IBBBjL&u:vBn0:sl6Ȳ ,|I`6*UBPPWT $h4 )BBB֔޵w+"""`p@DDD Hi08 """ DDD4"""PZQ"""`p@DDD5cQƖ"""`p@DDD Hi08 """ DDD4"""`p@DDD Hi?SΆzIENDB`fritzing-app-0.9.3b/help/images/schematic3.png000066400000000000000000000505221272363601300212340ustar00rootroot00000000000000PNG  IHDRoԂbKGD pHYsodPIDATxg`U𳩛J( PH^CAP RUHE*$P ]I@z}?<+ $;wvv7IݹIHNܫ7"JIIi] 6mQ/;9;㷣 p/yuu 吕Bۻq`41Wvشm;ʔ-kW-]Ks/[c ńQ͋}NB):?=:uZ۶X}^9 ._CSUJr2Wu//o^ԯMDR?))0pwwu._;#+3S>Ѽu W`cvNZ@;uI =PJU-_܉G *ZgRb@aܤ)ѩ=  œ/a+!9QWj$q O:kbsrr+xz"o|(X0/үE??M=fGe0c?¶aߞ=8w:<ΨV&hVm·`Aj1`۷HNC`fW/Fض ?jII\@ꍟ_oG | jk3hڕ+e˗2k$ۯ\1uX:˰9|}ѻ}$'~*߻Ǐbt ^-XEGPBz&0`عTٹä57ilk0 ,$>PտR_axkfap;oI+QB%Jlq&KdQvԬ]GR쒃AJrI{&Ma^|ynݸ.{[@J~6A֮\j=r *JϿ}-&$6$Y# ;, mul ޢji&m:wA媯K~~ke$IlCb G׍1yaҮwsCæd l^}g6dfdVozzI&L*yk&3#sOhb?ɛW>\]]eo<~c%"2- hҼKR/V _7j9tPzR$+V]K9t~M=}"%l?>RϤY ;&HW({D斋Ϟܖ2t'/RosgLC$<WWWaoc%"2r\2}#Oo۷lo8Uj6ou͋!>싌5TIJ?Wзl2%KU(Y~[V.H89q^"~ v&9) Hj;̉-W?L__ d۷o^Һ]d;GHd^ Dd 0ݻ$_̛/j|rsfD޿OivzAפl_|)Dn4`ʼ!ʉÜzq Sv6f)w>,8kq:9;cIoT,= .Vey$332!܊9^G=DDdM0ȃHp? S~ *RMoWF8rDؗ yE jQKFD$۳c;22Lɛl |I.0N;^&J%$cق+㙆MJݽKѸR{÷P!gH= 6n{Hd{MYo vz}a![r8:4oz J 7y^$Pf!fχm[~k֪ gH 6ݻ IM]HY?Òt:N$F#'!JOO4l"&BvQTiL"8"1HM^!C5`Q9-˫̛"#G@tJcԸ c'Jr~>tݻ`4%?c4w.Iٶc'׃gQaNTR%JR} 4G\]]d('T)""ʬZ_U. JɭAG _ +]{R4N@J~Q%OP 6Il,< ׶S'A$vnCfFO\oB^hdfd 9) ?wUy~o=g4`֗^/eSJJ5h&55UTgFQcmJ@A_|Ry!M(E5$'%!=-^\8z=gҳo}Q Ç1n0*EG|ȓRPau*ٜ_~FrR7mJq NΘ1o )bWÖ Ccsf†.;W!zWbfoZ -^靮zO~ۄb3g0wl "шŀ==NBxlL9[LI_|LF#xY(>Sr=+/M'ѿFܿ{ǎƶ-q᯿~$mۡj9~poǀw`}ZADT~h4 -5 O>ETdR>iQy+%++KZr3 ٣u D4@Zjx|C#ϟ׺""]|==c*cќYhֲE D[f1~.^Ǐ*Vnߺ̌ K"=]\\.EҌ'tLNj%KQx|v [F>` 'ڲ:at:ʖ+i]JդcAE >OO/  6n#1!~#z+%!O""25j`HNJ_M!ĀADD}Mkh?~] 0HPj}$)zpuu5{Ȉ$'k}$)J)#}b ""!ϯhǜhЮ0`OJ?[(iHgses3{€ADDBDG?P%~5H$D*|HC 0H?SO޼Z ĀADDmu1u )rԟG>>ț/ևB1`"#"0ǩz5t:rhDb:xɣuyDdqǎ2AV>#r"zt`#;j,Ct_wGUoƦL|4q_z>;ty>P}#M?#m*PC#0r`t܁.ݔ*EVtX|)Zmŋ︧qIvb㑚j$6V}ףG15m+?g4'.ܾ Zuϟx\(+3;ö0^&==sgLUk4At,= ?NRKLH k]O.T2L2HE LK %bZ-n@;#|r8:9a'c0g2\#0o˟+ɾ&7NDqrrGå4u~\bdצkQ˿z5݆ߊ!2W__A-+K\/ܽ}ۤFTI^:gH ]*dʥm**ժ;9;c30~8:|vRx٫rh ۴+TԺ0\"LH-wM{{EjLLH3M,ٟsfYrvv%ː/myйkw ;wn?m CjK cwiHл~FE`CXD[Ue & .ً+T[QHк\"QZw$ϙp\kqqOMڛhVJA\|]n\2e˪ZgRR5~'yKaִ)1x FXb%j׭粲2.섣`999UI/)ݤ='Jخ8۷n]^] +QB-i>DDւ [I?Vn0N;j)))/ X:0\hv۴iA]n.Q$xQc4nB[݌gϪZÎݹ} ˤԕݮe ɇ%;[rpsɫ-0sdv "B vl{Hd{m,]P^=ԔR*+~ lY< v`0 LHWn(sDnLy>D??ɾ!S""cSgOH f:ܾYVpv6-qD?R,l^&J=rYH; v*Tfm==Рqcvш۶Rh|i-Q}/J]DDs v(55;wH#,$a@ :&M|~h4b0 fLDD1`ء5 *׫85IpEMdӯd߹3g}k&uы0POz4mRR I8x(| [4g5<~G8$#Ϸi/}dg趗&7ww'f@Xd&uc3JNkNgmvޢ!""exÎ\zբjm;-_z;u8vos~,DD$Q{1JOO}R |K-=II&zw7Mk&" 0DVVojݒOO/|>'}" ŀaD#}A; _$woCZZ֧o +I[ EOO$"{ 6nǶmKʩռukɫ" 8֧0yd_zz:~oY 3+u씣S 4 vNgV]ޖ;7y%= vEܼ~M/HVm:Hz9|b?}2FV̩.1`ذ`/*VBʪ߆M#OL?,-NyRs_Htj/ݻb#&q ""KcRhKSG"ɾh5˟_vQ&m-XPzF?Z,?)1 &Re}^9 B<>4Fs1Cu3zE.Cq=4: kgyddgၼp?/Ye˕GOOK DD6(==Vm|..puy<GBQp!8;h}r'8!>y"LٲSM4i5jՆևf0lPVf&~nfwpp@"EPk(_*Tj5j֧طg7'EVV1o\׮֡`ܭy>\7j]VRRRnnnZBk>Hs%k}hJINFjU.DѤyKJHIh; 9) 6nuG_|?^q[3 X^"<0F;` 6i N'`8;cO?bղ%xŏٿX1L3ka}[EBDD8}F|8u/>w=ӧj. = }1Tv֭Y/ݬL^Wj}0 ,3+W  ""R]VVϝ3=}`ݚU0 Z -պ 0`lu3fN,/Σ[q Kʥ۞Z  ""_7-?hvnx@yiN˰8 ""y_0{ÐadK||͙uǀADD x;GÓ۷XĀADDm \@wkWk]E1`&233,4۹{x`5_:]PZ {khӾ*V_;w 1!Ax֎kB @3HNJGp\" }x9ޮp?,Zz0Y ^VAmخ=QKvzO ͪ4ܿA: =)55U4R)ldjKOKKlSz|^ҭG1v2v"dHgz%Ν9Ą89;xʕ/c>6n_|=xy{?o;z77x/j1ÆڕˊѬUk8dffI_0 ("Ϝ1ʔ->A޽1i묢222p-4sa&-ZbX|+?۸Ys; -E+Wn~eo>u yvoZ"ڿE~ A)EȣTi^}U#TR?1޾$/вM:=<<0nd<1"݃NV}g|ȓ̢wsçS+'cEt:|:u:VnWFMl S.iܬ9 S4FBjv DDdk@eggg\~E>oګ7|Qq 7ԩ__eVO ""Rf:RO޼Xz-1~L<լG_xROJJ^5kDDbwqqVKboɓGfMxC"<֣fLeU\f V,33ZADRii-XPCȑqWzV;S~ٴIȆ=:)aMڋyEsJTd .!Xu%Qj  iG}Pxq]ѬiS_lv- o//\xd]^6k[ j}9rutrև` jٲ%]WWWؿxS|@5n5V=5}`;sK{n燱dZhR_\S>ת]jhEð]||yzmD?k5a 9fa&-[ƈ#.0PDd >)RF~g_;+1%9U1{2eb]Xp>j_l|h4]2ډ1Ç}wbTZ!O}|d=w Y`Ufo/~4lDȑ!oԴև`1E'Nz.&,?*iiiXtz{ߦnDFD}fo7NJfK04d4AoBh\|ǏƮP-rdk`xWi-UfV s6|պ "1*`Ыݾy YHKMCbbDptt9OeIa)t:aXF;ݺi]RYYYx@}45ި)ȑDFF7k K0,35ry+V sɲDD0eSRr{/|11[+>6j[# .|9#6v{n})f%ȶ.s:u.%Gb?*#}GPQC8 "&&.\ŋ_*Ν;۰ԩSHJNF*@[ ~=Fs`0`Ҹ1h>fև Nm&=j&N&D[Dٳٹˉ~B:uzpttT|0_ɄOѻ5 T^]l "8}$eʖպ;|/Zh*ժǻi}(CiJԸDD`0#Xp>nnlYwKq}<\ѫz_̙o7֊C# Br ٽ/ch׬1B6"tN b>hRCC^^^puuZ$DdO?zWCBB<QiVgtEv&&&b/`ԈF{!YCPV^^b·‡<0~8u⸢:Wu6,j 瓤mպ5vR2|oh232.YYYtNJE| [$rttĝ;wPNCzZSO#<<} vց˜Q@z^+?g0LjRz>y-Ő}MF#fLxN,j5iv,ֆCC/_ <8p ␐̬, ǹsЫW[M`0`ޜ9 1 uht\r۷ۊv*tJ<,[0?f_~|=!`0ŋ8wJ)3Ni]ٰ.A;[2ѳqB3VTi7{{]C1g4=}X:tGsX0kISЭw X_ ڿ+ 2RSŀjJ3ػ"#"ᇏÀ){#1pp[,;'WcddcypLtq Xݶy\9tMH'Ÿ?N9nCǩV߭1{HINV4N0el88 b(^1<(q##Q*-Ϟ0  ""B[ $6VxMc{} J8-`ptQŀADDB(m$Ą y Z dsX+ """)I "%IMMňſӼUk\ DD$ăHe!D~pϓiֲf-\pa& ""R'sP\`pp>E4m1\(ADD1˧h{ш3cGVE4i/Q4;)1`BFQ8Ŋ)U+Ѹy YB ""R$|>yB q^BQ 5% 0`Ν9)ZnEsf)0w2 8cH.e0s&.XIgefT7++ 0U8}$^H=} ! N?ʖ@ZPPaՎH X2oWA;7L?VѾ6iKŅB$\اIv8/:U*ʮfعkwLbj^d1~ܤ}gW~Ǝƶ-"|\t)U:t;/(Zv ߇ATdqѴEowq>Tʨ>lÅ 0r`v;`f uC*U5!"#"r"ҫ3r. _-Ӿ@7k(DEb{}^9<Ń(a=Ӥy ˟?G\78VF{Ƶj?6j4z}O}[F.];eCŻl)bшYSໟ7[XrYRq>Cq9hRMjj*9u>oߺESSR49L3%ӛTüq\imnִWw}UK rb,[0OC$R]-P%xN1>yu oygBB,,7ZuH9~ ~e˗G2!_8;;!)17og^zyU+QBE jB1Mv ZN<s;Cbq.MX'MѬ9;4\,U=VmdNHKe/Μ:6T4??i .T[$̮m0 /|.cFQ/?lٴ/P&y3y.ӣlOO/C I~'ظ[H^}c.TĀH=Ra#_proX`iS`BK...Xb%(E`lٹ GGlUTi,[d-~R;5S 1`"W/_PNԩ_ߤ}W>c R%%Ϝ;CYɛK||c)x)|5k2eˢw%b>ĥ >\"!#Eo"QACoȤ==={w|հ < J-=I%J,нѭwův[vΜP_aب ʹDVVU?,]6|3lkj4xU˖Zp2oB( wM,:-Y˜I#8y(b>4ioФ)t:<=$Z<}"߷HhӮjQKp5˞8 +U59H[l?m ôss\"ti j=0nKY4{S$NR.([`بA\H?\ 9) w͋7j~ZI_ /gX @^%?seB,Wqrrֺ4"z*UF1u>NVmf{I 3F'ziSTCǎbŒ}j5jj]"0PhKy e˗8C|LDEԉ[__ d۷qݷԕSaAT0b8K$"RM #8:9e }XfU)zAe%V/_QՖqqO1l{ԤO憹KAk]&" vݻ8{d_r8m];5&NKMI95+;0AN5G6<.vfg¶J_YGVf_=cvJr2 jUZm'9/ǮPt Ӥ69<)?㑲Wl 1oxDFDvm/4xfF핼2{Tr89Yh4bӰwNNo x]DDj-;rpaDGkv !$n\M?lsj[|)6m^~Fl=CDd0숥4 )3[Kj"<1iwswh먕Tż%2 ;:!rBtt>dv777sfN$WdI,Y⁇0ġ#!!~*^oMZ b2SBt([ v=oFDd|NY7l$d¶ڕ+]R|:GBZ3N-!ϧݖ T J.ワ]?M0Xqwɾf|0 &;C1c->.1r"~Obuu }?RSRL=WWW,Y+WToll^^hиdS?13*l|"y+9U-[!4Y3 w }d_PNԡd=HLHtqϜl'\py$ o4t1w>4n!" '7sgQ. @4i*y --MvJKt;qF3L^/-t2/8}Y2 f0=$DM9,z=Z $Pd)}ɾ :_HVV>d$9"ɄOVnZ""caNǃHɾ6۫_r DFDh}Z>BH-=S>]ٻ{dCws8*}`arGިSZu 25YU 7ww?AOj s,_nQǻ}1hOf0lTjj*!׶:Gqtr} ,$Xөe Ֆlݷ}:wO?eDq-?|RMڝ]<0ЌsMu&woƅBjմ>Et7q2uh,aܿf|! Wcz5 *cĀahܬ<=T*UQD ܻsǤ/,dU (S,鋍6W_YՎUD-!̜ԱEjtysWXӵ:=& . `756cDʼ'6xz.(WaArr j fJB5JPxF "5 y޹lA}{DJNo&N@||%ӻпt 2+}JIIhz%S  ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ ǀADDD1`p DDD$ uDDD7^ŭ7h<} HIIh8::^^^_A%Zr(Q3;xlPjj*F pvqnȓ>ȗ?? * ?-^#qiDFD(Sj5jv7Ѥys*ևitFJJJ MRTc)t._CSUJr2Wi :EQ>*V5@bшpE[BN73=<,~֌Hk>g05 )z774hA:a&prvV}F/?l?`0X==Ы_?} Vo0`?xDs Xk7Bګ*RF#< ſ>\@E/РqK"!""UDGcвw:0_ Մ ~aǼ/g +3Sr4ŀADD0i)+##+-DpaMu`!HOO׺0`Eo~}dF5+Ձc>kADDsϓdGfNzk}9s^B24 V#*IENDB`fritzing-app-0.9.3b/help/images/start.png000066400000000000000000003214751272363601300203560ustar00rootroot00000000000000PNG  IHDRYLbKGD pHYsodIDATxw|ǟrMwҝeYr7:&0%'@  KH(!!!T'jl*uߖyXiwj~a晝ݝ>3                        1Ia5FAAAzvGNN \.v뭷^s5CxRQIB&!.cO0J0! CN("Ne+ 0O[G 7]w6x)T.r_꡿GOej7x{{ ֌믿~}z꩛o|g7^ǿo!Sg?<ӟ~AAps=g}}$qgQ;0U Tck(ʆvCG{i\f܍HZ-bTz4 ש+Y#-f}%p]VPZIhM:VD+('}H ?%kjMxj1n1&h|# D^幣1rS8|0x$ 5_Ʀ=Aj'0橏"Du ޫ2^?݈T*r/4|+f{8- v(v<o }E_Q} Fn(SDV:L9ZE$SGBg罺~> ]Ir?ϕK{%I5DB~_$e^/mc?R~L-an]STx:Zbi7ьN-fQu4)0ֶYY>zD`7ބ)r1Kn9G|̍x2ޙA1SЏ"02YWѵ]fQ7xfшnmwDAA@`ȊK$L@))ez"Ȣ$---k׮;O]?ZGFRIAO>hD 0aj 5[_`D TM8:EH9*OAaT:n@SjI+0zt.x~wrs>e푪]hDN"IR8H_'6QQ O>[J;yҨE D'bXE1=:) "Pt`Ձ/tDK_j0)e$䎅2 KLA?(|L5ct:e5]Iyc23SuN ?u~ddFuW11xҹGU%[JahAH5(PH_,@@Eʲ "LۿkKn8i ʡ09`Ξ{ 08 !=C8QsdI) YR$za?!ϳ .x衇I|+.oM~ *fF`yNlfxl ML'aD.::∟Hr!tӾ-0ё1E FJJhqIɍЁljȲZaQ#jIcq_s7cwњPtB鴾>((BS&,m׶~W^_7$t Fhc:QW:Ѯ3U2AyiZghdX'QUqI{{>Ԓ{nP(""DP(q B0 ."(e(_2@չd^t kϨp%7$P 'PB{R$] Ǖ ړT}IN^lV! @!2P)Ltx:lf\a UE ˲CC]|fsqDFk/"E+`#5"cF6^Hp#,^H3Zԁ‡:4Tq:0گψTW`IFD4_a Rj: t+`uLTȉN~bA#W4X}'T7ֿ~э3ǨE;gBA=FԊ ehaĎǥ!Ţ SWCu<ҧ r%7D -z/I@,ɂL  Ȳ"P T;lʲV $f$A G =T *@9$$*K +!$`ɖÂG`E1$L%RY2PG QR!,( 0B *!JxctJ{O'@)vu-"]BA3$^(x5lCZ=ΈUAe2(TEC{VA6?p?. 9`'D>ܱPAÈB"IhAuxvFl4TZt^ Odw̶>: j00"DM'jxY#p~0ªUv=%E60z Ae[y!պTB~4רڒJUYp;G wH@]>BTqKnx?(!]tȂ&AЬ*A~%H^RP%)=`&!Me ސ,19akt| px-W+'&ɒDR7s.IX%}o?7+QkpG޺nrj[wΛɌڽmo/z;;S\~W 0;;u3k.*3dbª7}%eQJg*?Ooӝ-pݟnIK0JվlJFaڍz/Ut4B7O7OJa?8]dp萝 7qwDжYd^ׂԫ` Qg޸jg !MUo?}}TϹ\-|U>z+^(:wozmo_~҃o{3W5#'f[߼x 'c{1cc}2깣!Gp @`O-QzGpMɋ/*޿z)șS~b63&q#c4IH=:5~eU[p/Q-E*˒E(Pd!DN'cۆ?=jO$_m&Lw(PJH/ɢO݇pԮwZ 7_QAȚ{) G?z3Ie֬_*;M<{W3)$4W "a=^`cNq!{}h1#tS=Yۮ[fC:'=3 Α'{mF@ڶ}`Na7 =L~~qI1@G6YxM5}zP{G/ͬ(kI@JD ƈ6{'l~Ii-#PA#Yc IMm\"}ZWt#^ *ӏ_c]j@T32,@dO@ex-0"ુ&ewG~-aB{>iv=/.1NyΈP%@@ug 'Et]k\cIw*~J֏<|w> ,rN[M0@&l8mLX”D7oٶSR(64:gz ?]2WZ5kΛ ~W^Yt9 ϝz)ZSiknh5|~?;#䞊}VqyzNxq;+^}Juٷ'c,_+Xlʕw;;'m5kF8OΈlE1jTE3 u{_etnkG]A^|q/(]muJܻT'@֞svޥTW=bF={Koq^w/^s3?+=^0g%;v-.)l׸>[Ϧ:ckГZKӷNJgj߻⭼'n}QB땣V\C tRp  6:fWU'~<}}c ) >Bc0&1?Is_q<хY k`?^c0aĉlHҵ'.%)]Nn|5""@Xa!߯_l%L{H ojJū<) ` de^W_6ҶuVv/&m޺?g͒#|Nx}5.8wYspkâ>LpVVO3}TVƙ7q=\wyffO)])8f%e ozO~zs[o@PZ7;m Rsӷ+˷8=R|K^:^zP2\;N&r%ު_=jj߹usJVGW*w.w' [>V:o{c?B#^ .}SP+kdUpJ;j֮ZW/BHݻ7<<^۞f܇ti]/ګ'Mtyy֩r2P={p P&Is]'zyphGi E߇&0g-^;|Mc֏N)hmã8k~icD{&~16g]^" jEoޗ!j fP,˱ L[8ֻ~T'AӦD r14!ʧ,t/&H1lxIRdymӆ@MȪPe= az:cRP9C E6ij z}mx i@9uGC +/?%mb!֞6?woiX\voV-=n~~}g^gP ֧鏞R3@ٔn:ӥ)9ڿ~{< WD?(훞͘?=~?Vzc|PVP4+9wS2v;dn;We]Eʆvhm^_uezcxbXdP99BGga/eN{q1`@pգSŧjO,zF]=oq q]bݹz;⳨#)pܾvqFg.W\/U?}hgbعbUW#8wg 3@z et]!xބ/eԍ Hc}D4Tm*=,"r8aŦ~X8ܢ=^k],cYdD/,+,{pL9%C YPde 3ƕb g@nHH@d "P pfX$. pBW'qJqvVOc2+Y3WҺtd6w'0^zvGލj6)-GcԵk|vqO\xwvѽ(mU<ʽ9`-|O y JIRJLuGeIOJӜRP/㈃tĹvnoѨsOB7/?) 2Yj-`W9)(S,2bґΟL8j_>өw.ykS]ݥ$Mk`Km+[xgTspJPk,ZCرeX9 P@O+bm xhǖ;+̿$wmwT)TUf*cG Bdtp2S*)BdC5,OR_QFau޲G\2~B|ի5yF,'_XJ^_rǧj9(l,S乛zݽZ)u;UibSn$OZNVR=?{Iٽmプ3/_&ֶs#%Y嶯e?!so]{+::h{ZD$Jg[]ܽϿ'8ԼQ6+},xmnYkc#k;.jzڀRu^YIe]+c~|kуmo9 J2(X@e1lvYsC~к9GVAYe}˿WF+ǍeӞ69[ݔҚwohWvmݚXaAIFFɼ/.;Bݱ =,#xqxxR\'7}5 KDNHjTQu<ImDID@[11>SOpmU[.-9扃ƚz ԏnmm-++{wFʴlfd6&lV e[PJ^&x"ãn683ϚfGH7f{f7;#dO7|zɑ;9Gnqii&ݤ 19MtΑatәW=~&[L"qG 9]A_ut1@( ϧK2x%? i,o-~^ѭ{? + /|FңN6N3\u/1/1#'Lx}Bk]Mt :5.f!&x\1:5ڈGcGԆ njnaKFΚ@5sh Y)eYu{M ׭ʊgSGL3"e]쉙bӌYx5N`\vm?UXᰑ4sOySK|Ї UG[G]ߗ$Q â7TF4[ȱ9NPyMjPgGUq:Kxzې̭;a>(^i~ A?K߉yS z8c}/m. SEZEQuuu:ou8]Ę^b2fy3K̍x Y)6xK[Al-PWLdYٔr wTQ4YYUէUwW: ~e\5A|=Ohx17B*'oVOG&r%<i#%=C t~d@UV?Zէ*ڊyH >38>4%`ь3NfPSČ}碽[Fd7f?OLSR1}4DcL-jNpnӤ_qy 5!8])w11)+"N&T%zEebS[?Ѫ5s1i_|P&h:fy¹1h=W*jP~&h!<=DGS餬5E\\ĶN"%bxeWFtuR f>w<ѣң J'lGvtv\G(a%0 >40ރ&r%,s=$$J*S:GeCgxAޔ~L B s嘽0aJ}yҵ+Ӧ qnODIxk{H}5k'   ҏ\tEvZj0o-˿M6Q#  ̘1}"H3AAAC@짃IOpd#   i&ƎIe1(DAA2b.^oTKEAA2()+ Kq~x{/w|Yo|w'ח}x{?G++^?rzኍ\ɺ)\>Q;޹?_Wǁ$s՚}.F?鴄d֒lTԠ!  D;@4md7"5?Jp[7PI@i-%"͞Ԭ?eGjJZ^9픛ݛKzvϹHrDzsJ9Wn֫?xxљՕ{`\wC9ܿ{FO)F?5?~l~rQkT_բSQV  ȷEvtRPtwD @ xΤLɢT/Yy.nO)/{ /\~1cYܽs^@Wwwqw87~?G)*ιs&T\rc_+9i+>YK]ŧFaFs3RZ[C}-xu@+)f澊s^\ ~0)Yfի-_;0f0~th׀.5UZ5/}[R$ ) F愊 { h7>}ׁ]ó+uR3Ͼ Jx}ׁVxbu]R^ݩխxtմ !0]wxW]Y]#):PSM|8w"nWcBõՄ@e)d]:jcuR] /=K߻.]bmB٨;p6:-a|bBe(=BJmQƇK{3U?*QB?ٸur%dWkΪ5z+[4zjzrF{ξ>_ g?ZBθƏ?V}}0g(笻?£ud:u7. J~teZSCHi%_ߝq3-uRb6.:>Mq.yG%   '8G 졦2m|<>֮l'NQG]9R%׬>Jڧ/8_ƨ,@gÍkגa%(FՍ<n\[WQou; s]v$)=u_#w/= Bxxõ{WXW`==g޹kz71OGtq_1GS]T  r_ik!}u;kF?ڒE)?fۛr8wM ;D8tѿU`m}3գq0ތ/B)5s᭿3KW_2=/,Z2p"u^Օ,ZB~9%~HK(_xփdAg\y#lڃՔ-)P  0ן}4t`?W_y }SL˯qw/0R86Ֆg֨7.s>AAQb.k޽/2gP8 5]:ou?H}twFKAAc WEs/zҥKGvuvWAr32e$IxS  ҋAj5mLT-EN(ynR硛K7{  VRhQ9.y[k&:|P>  r̡2?ЈLZ jE%   2h5ѡںqEAAAVPeC}   Qc]\gdH0MhBЄ&486M080Gg&DW w} H0K  /Ȳ33S'$bC]  (D#0--MR[[ڪ )..v\}/LgEAAr" IRl3f[NQ AAW_tQF\.jX֮];|Rt8aukrp֚IT٥hޢOntk3&C{r[/H/.qmqt! nߺu2dej#q() Ν;?md-<  P"IˣɮX֥nV78N* +,.5k^ J~V2WL~`wzΩZJA^c^6D:HQB錸x|(  I8/z/c1m*XW?(LA ʴ :AAd>T܆<'"n5kZ.Uk˚{Ɍ>n&LmVtmhUF\Vq4biimY#].7P¶Za[K. ;܊G~T JDI*~ W  Ht;rss{U_rUqMFg0Զ +S dh  8466ֶi{PNsȑ 2eM=]+U*ݡX[[[g̘tMUa~efh; ,-x7љڶSN FwG"ygvR3 'Ḳ MhBЄ&4۵sT~*]`0(@L)$)wttVU7"9=gEQ|d;@M6%O\ ==ҳO;mmXYV _7^u{昄8K7ۆ쬬6;{icN:4k%Doj]APAA$&---J,SJ$ɲ$J`Holj4lYyYYyD,236m$bONzٍS#fL?DEq 2'NA%QIL2$DAAA) ,IR8,BP( Bvr\.um >c eAE z=ޡA$[RjcDA9Fr$YDQ TAATkIAAA%P ")&;)  2h '&ZD@ee  X,),, q*TɖGЄ&4 Mh:F;짜zPA$$ OAAALkI)'A\bMhBЄ&4 U]PAaVKqy " |9nPeڀ,1AAAIk80  әT_YYŋ;aȍ1xFY;B^[,:[oUVVV]]8$}x[Vɔkkk9s8a deeLҜGm6[ZZAhnn6|0V5$IPH$0L& `^LG;;;`߾}VuҤI)"3ֶ(1bJ]p(Gf]R\i`#GLyy_贙}۷e'̜a |^o}-Rvٶg[s!LSdژ_qeeL3,;pׁc \8}lA^nUuuѝbW8},qELV B~IJj6;KXuv4YK]ONUR$Ð,$q:9L&i?SC!`/7䤠R  P ,!fxsTAW,EQQ\EEE'f4!--`1$) —~_@8eYfED./Ϙ5-nra՚t~ Hϲ:Rı2$=orƹɓK U! ҧ{چuMLΚ7c暒'L(S3=qf64mm$[3#ve5li"O R?DѪAAƌ8EFdw  l9w$IJS$q,ˊd8DQT B{0edYV~z͖&!fMcǶlٜnXAH.Xtvf2wRKw+D kRwxtghmCv;dI9[o`Ǝɷ. S.+$e  +VSYfd=wAnw^^2LNp8 <ϋh)@RlZvG$7ñ#G,]]+Lff%%A$cZMMW_Ijo0+^ .uϑ+"!eYQٞ1T+(ITI V5!he~o+'} JZެ:̼jjgĸONGЄ&4 Mh:Π LT}JJq42] 4?2cFsZ(J $B1c`iճ'BuuuYY!mÇw  s\e} H= v,ՙGyetE:}vԸo9[SOׄ'ݷW20Fiʕ_vwT t HAR ov}}%K9E9ʋM.?L޿c֍@g36o4ٟϜ_; &˚=l6wtu{P&˜5ِӏBOS0YzT q@zV/kw&!do2  +pgV*˒$$5eYE&Bg8aB 7r333:::Uh(QJG|_d 꼋j/WP(@H! i$w8CA.Zt6˅we̝pQ@%_O<g3B̦ 4oں< 6ނSԭg0eeϘ݃K0$UGVezyI-%횹uss3蚱ysPYQlyʚ]>M̊{MeeADɞ%RļḲ MhBЄ&4gzjW_1!癌 &/rr|`e+ǁKP(?? mmm6---M8eFt:)1bQ$cx$/ò߿j{9K/uuvuvt*qڛ_x7_̴6/fkzUܶw TVkii6"~_(۴EokjKZze5-7[Zlv{ñtR%Bt3oQr@>8ḾY-M,@d1)p9k25/];8ɩ7WJ1`Ya+ل${\IHA\TAA丧759,V#á#4Wf*/NݶQiiWy)&Zuu:q>_9aLHOHetGMC#7Y7ԙ;6Zj7LA&ޱ9|Y*M-aIv``_>+LXeYvرyyy_> Ԡ?e٠L+  6[iYuu}&JWᐆLrYre9dEQt:~СC]]]&~ SPJ6n؏RGYl9ڴ2uNVtxqvWV W^OϽ ,: /{Yt~rgvRPїѳ㈲t˔iԷaqF/dA>P^!O%ڲ'4Lg>#8=uycteH.8.'L0~)SdeeO[،D5  rHO߻hQI0mxSwȡPpܴi(#G(5eU|w999@ȑ#vmjj1'3RQ!++k[Ծ@; W!`6L="e˛LyR_W=R`0'kz Q?%Y%e1QT_G^+GVjh8.TCkkodP׊JiiFJkMKA?>Y,0T$I,Pi՝S@-1:.1&4 MhBԿtuuVWWC윜 :|;6={ߞ=<]0MeeJ դ^8rrrrrr!*,+++++Sua!mj[򽋗|b܉/r=k]lȝ8cw^\jjkkjk`̘1~Æ>t:Z3JI~ss< E2ޖh<hmkmmkUwW>lkmkkm6Mh~}pɅnƌX~.]0МGY#1Iz1 9AAd hmi2e2Sw{uL2 f16n\w!+x<ev4i<233O@/ىy qWu  @ dJn]@A㏈IGuHny@5  20d䤧gٳ`xlBee_|vuJȞ=353mϚ5wucƌSC2:G fYY9D)  2{wVwV~. O;/>_ ==s)So.t(Ӑ f͚L$A; <0kz$ZAAI윜+6§7v--S(K. BڡcAf@BcGЄ&4 Mhw4сanLvMyWڛnM{r6%$Ò&Q!1 9SP T,aQfIÒ Hd:?:q޽}LdرyyyC}(H\  u6AA9x`n!Lwqb8d8%̛;>_8V4kXf(ݓa\:R_{vd2IA<0:{ɲ,L&A|>߿]dx^W'n/((ԉC)-++3L)l6[,IJJJ:;;>~~fVP]ZP %ф&4 MhB[NKalc}fʖz @L{CMg| vfs{{"8T)˲feYEʲ,IR($ `{{{SSWGGG~~P1 !d(:thĈ&U &mkk3Leeejjj:na<  beVͶ==NSAxջwx=@#=}„ }6u&IZ,laxOKKy>++8##:;;kjjѨ /ommU sӣ|„ (;'_"--RZ3f̨6mĉ ,hiiѣG/^O?ݹsg z3gܴi(^ e>"l]á ~o ݻw„ 9U\~?קlV[(~e\QJ+++:tꩧ>Hd2-Y^#2 @A<2e9jS0ຂ  HrXcaÆ_\z^:=p~+}̙}3 322\.%\t:?;v4L&L8ӧM믳,[ZZӐdY\slķ)&IҤITva$B>-t7fn[Z! M8?>e@4)7](jkk[z fs0ef)+/" i!7fBCs( RPU(AAEdB:׮]jտ^/0hzۿoOKv]]]P(fŊ7Z-[o~p =\[[!t666a ň~87>o>oԨQ8\w:+dIzk^{ɥ-V=zyLp5yϻVjz뭝;wp Sģޛ8q⥗^q\WW'|~!`GIHX&62a+P DAAIμxtג\Çʫ5>e9BP Paa=+e!4d6m6W\qUo˖-{0l~N3.nOj_B2.2ݎAڛ;rZeY^OJAY;;[[ VwG՚ r-_>ͽ?~ ^{#F(ӦM7ofF]P1(RJ1` 2 pEA9!&!$ dP" !$(}=\9*_!N: y睄PJz뭛niΜ9y8MJ;ޠÈ2+Ħ3p޺袋RzE{1 Xm%r^Փ ÜsM-E~f w'/]@Z[-'|2bĈuuu$3^tqy4 MhBЄo;ʊء-ɾ}Xca@~{(!aXY:;n:uԔyd2r)/RPԴ&Jի.]aÆ>NgJ)8tP[[[kkx?ZZe8NJYRa e"$ ._bر)h6G)%UIKKS>H^WkjjDQ,//OmQ߿yʆ Vk0T5LpyQ9$I 333'OӓZ7|@V{L$Iǵ*08dڷoB!ep8(rFF8t萢3c B)T$I'ڒ`6 =\Ԟr\bMhBЄ&4 > I/nݺ{V0JR`"9s,oܸ8J''B#G{cǎ=餓.d}e<ϗE@eqذa_|ň#\.J)///ߵkWRɲlXyjI1gkl}oe[f/rÆ wyg ?thRI~aNc3N>y=#!{aE?mۦ, /˲ΒcǎU6&L8iIwdggwuuɲ ZZZ 6M&ÿx℉sν7egZ"&]]]ʼ>ǩrRp1cZbzT&.*  ݻc@3fPblVZkѢ(oRJEQ.x_~ecߠ,C̟&Qvkf2.@̬ò,wttpA1-??ߠGNEr׮]999L ܱcGGGnTK4m,DYW)zF7p~ ͦÇq͛?[n㏫@-^xԔ%%%'XƸoO "  H_={UW]~z}T1i$ekҤI/@2( *2B~&2BX d2߿_)eFH#lݺuʔ)<+ZZZԵU,ʷs궢6)jlٲ\ezL|"f4hngBDO?RmɣG6~_}=w0 ,|?uv= 2,S\Rw_k&Nbd+YK hii={,g( (bǦ2   19s~_| =e((JbӦR +VvdXUjK.8'H T(ᙙ>axFF21<[osuי}UUUuuuuɲJDI$I2999cƌ?~|ZZڑ#G6n(IR)'Z/lM %EpB-oqw~qikk5윜=<[~Co7tsDPPPPPPn(JMBnĉXBuWfOBAA:u+_oٲe߾JA Tф0;vXp!@ !a8Q8iӦ͜937772=% !$77ɲp8;vEL1cƜVU "aem6b&) z<Ԥ4pB ü 5ZѮ[lo@*$Ip8p8B|q勳gƙ3fAݻvO2/՞I:񻃟Ad]A  lٲW_}U,LB"6uY핥,˵^ۗ|Yhի ui8pR0)ھ`{!PWAd:_I6 IIZZZ K2(*:P=rȴ={Dڪ Ij-..zN!''a={x}’V ,gᓯZg:]Ww O b-ulpy4 MhBЄ&HKK[xoM)$aXB% I+$I9sF_P_  7ܰ{n#ϔM4驧 ~+8T]0uE0t,޽{ܸq<ϟ{F2U틯_](tǑe'QcǎݱcbSZ:,F_|oˈW/裮& WAAP~ ʔTBY(,˭ڗ~FFoiiyw._|:^r%}>u% klsbmM絷1|dYFEqذacƌןFKmmRڶVUU;N]Z[[}>_8ESN$|Ϟ=Vܹsذa\EEE~ag7r\BiS(5(D %ф&4 MhB?y篼VuJ (Rqyf}*#EK t]uU_~_|tbYSO=餓}ݝ;wvvv*k 85PZ,Lu$ҹ I<.UUU۶mf3YgeX*U2f̘1ct;].׶m6o>%-qE45YNo*{k/:"1IL0*C   JկoeP(("!<[V+s=`˻Ǥl6O6mǎ6m:r\0{ʺv***|&Ln:u%a#([r-SkxLotֶVl޶}[zzzNN.vڿ(-R.lmkuؽs1q뭷>)/1?nܸÇ@eey I$'h"2m@AAd@yGٲeeYVeYYsn#G&JDKKˆ xwݢ(>3'N;wn[__jժ`0zeY[[[kĈ/FFKN'ޒ?Xo~HpI?! W4RJL:fu;t(^8pٻwYgp8 ( xfo̼AH %ANejW.`$~Dq1 AAvEYDAte,KÑ%pX0Z]Un@uU+rD4˒*0_WIU=/}M:mTN?i<;sU6| N88p@;Tp8 v].Weeekkkʳ*+.AALj#fs½`3^o)i[0'Ny~۶mp! /@n #Fp݄B{F@sj%8.)XV\yĉ^k0W_8 N"DAA\DQT>*anZ 3 ?NYe.KMD!Y)ލ9X}eI&-~QPJn2 0L0 (jE%,˝},==}ƌե R{{;0<ϛL&͖תɄMgN<Á:{ ;0mڴuօaw[DŪ?ww}{eY-_|x<3gqƴSCCRx$<d2}٧3g̴ZeDሊ w{Ϟ=۶ms\555'͝QX4:?aM‹/xg,#\iKA.@B"fIH*^Aԁ  DNNNKsR`L 9rp!bh'pB+clFHF{;W< 2dy,sopƈٳ'--m͚5z^CAxw-[VQQU:43<*vvsrJm|f2^A%ueEH%\bMhBЄ&4qev0ȑ#brWTTT]]]ZVaRr\L%`ȇ|$jcc7YLeJ)Dz!d3Y(2Ê$QĚ¢ 9Rוis%), a19Av_ dgHR1q tv)))=f1c7o\\\\ZګmܸQ_OG쯽1}ݾc Z)hpn$9cAA}Uբ(&`u@ahO1<0@~a,cb{aIA%MQ(6Μ (ʲoED@ 4rt !IҾ};9g͜UPPzumڴi֬^R~90 C$e=}Nu@ʙTTv%ф&4 MhB_ZVȤc0"\0 !cҭ*&r2x*101 "p52Cda"DD0dBLI.HzG;Lĉ1bz9֐,%D$)~wرO@$IE,/|Rzg+R@oeYNv*  999uuu9Gݾm;TWWvդ8 TG3NϋauM4M 0ȵȄr0d(92MLkHa!D`,u#.%|233`ffb$СCI>l0$w:R#Gu<⵿=TcKC'سgOaa}r9|DgܜRO /|_}_}ed2M4 :ǎ ;LX蓬FK+I    2]0hDNn2- LVY581c O5l> 5Km?g%ke6I X{{zLݞ ;vl.6V*-B30^{={(mٲ9/7Ogvm/}o+rg@fiG`+1@AA/ Ќuj5Hqw׿@p $@@>{l>2@甌R^^[o+ۛ7o[ VR޴i֔;h\VB96%@-&N3G    bC}  H?PWWYc֘qvhqU   _&$sa\bMhBЄ&4HZƬ1dGq&7@4_AAAdQZD@6GЄ&4 Mho!>_g8j0kz>ɮS   Z^x`֘ d}2K   )R\L  ' LJo`nd"4ffc|B]9)5˼E LR3G,$Jk.|0^<q1g&BS>zjk2J F=E)  rBqaHrZ>O徧ff- [J\1*rl8!].#v^0! k:| H2#3x, C`&P ߒ%KXzC]|CTOs";;y O69BfBi(L$8\<1 ™x1.RVGЄ&4 Mh7}{ W"Qk$b0=\ .aem >Be)}O%MŰm&"gfac|8׳,e}lIfsvvs̘1 $[9l6`] 0 ȱ,GlrF4=,}사-6f=,&L޽;lM8vIœ `i ? cS@2d(H844^2q-@AAaT0L{{{KK Zp:tH"E1@+H}@LYN Vrʭmr3!t%i-Ȍ_QJf3Kp0f$I`e6[- rf_(dfl%VQ/2upi#6.u)/ɚjWB xJ<JL!z !f9pDZZZZZZƍܿfSq>+**wItdXuAHcc&  -q::TQ)MMM_|EUUdr8bU TXm6Q" u=Úd1DA =OF&19M֪֟np}H82,GV`-,1KAf3cM$G2Tx̼#ȅLpᆥ6o Ba ՄV:Y4@wox%ꗨ Hعsg0~7,ʲ:;;n=4 ʲca$I B@)\stU!)xq]AAA(Tȑ#8v eY9[weff;v wqr='Y!gkR68+  iRbZefxpu>Hʒ,ȔP$aI$,+̘B$xZِ/, N3K[2GrgL'a("GheH: ) WYq 8&@m㧰|򒒒¦ڂaÆẺNg*,,4L G)..;tPuu?/ 'P`L8F S{&`1  q:@eY8vXBUϰX,eYض/өutLe;=],(J%fn8d2q`a;mofL$eC("2g9'dRʐ\IbE*~™(quMv;Z6 Nfdd49, |Gk~Kz $[֚@ 0K;$@dJ J!ĂuyHcP9hġ0D)@qy4 MhBЄr=ݭPiN g}Z_X I iM Kw.K/6س ò,n<ut; m͍R8$gXA LBx(Y @ (d y> cv{Zl khrm@{]lΙdf.|nvwvvl6_^^sF=&1rP /e5cyH fh[k:F'=hR#  ƉЁZ=nE õ55ÇsWFB2l9j*YYlBQP (.L|iM|iE&pt4B)Jkڴa.+33dY?p@8.)))(( t}^d ʬ1d°,XOtp äBxygddB$I< 0'DM!$^ 3D\bMhBЄ&4 /vEeIٞ,k=LH˽pq{VՁYxϣ =™w8Ä lPJ)TY0.탯B+ h$3eq7R( a(TD9`90  #28V0bd'jh?4Bn 7OX_51OҊXi# k[kkkkyE\.P .Kd;!˘t8.+gJ`0(nl^wذauuuEEEL`a.phh 1g  q8^iTĠ@A*:pT(pJɚ6:,g{:Nw^SRDDŽ%+i l!*6!e3RdG {ҲOtEsò_v|DfC.ջ7.Bsssss3TTTX,`0hXB F S  fj&8p(R_$VeeYX,NSLa?B hUSe N  2pbL(`ee&%@ qjP  ,uydd)-1%m,e ~[<)X0duI#PnC_ $kdgC_bI $ Q)BavniYa?;êRȶP(D)miiA ꊂdB{HnV:k(u >ZZZt0F!,C DA p-ݳeޔ% 쒴ץ1Lw {+Wz<322>l`ksm[E醚&vB(Id@HE0qX"b,geE $i@pJ$l zZٴ YeF>s{{llQ5DeJp @DJ X$=M&S  S 3 1iD"+  2{jGx@  4˲/u͔~is/hoop-L&vuu u%;9G&v݂"93JTRA | H<@eAL7n5vz*7632! L (X'G5"),˲,+;RA&-a1В⬷`~+  /=?5Y'YRþp*CC$QVlEQ& D!Kp&%]R+FMM֭[Gep۷o̙sZ4Ėn)nBL{^Y"y$* TVݙ%h.[ͦSg:As2dUt3j$!H4$3%i r`uaơE ϖܜO<^! (/ $I8.,6f;]9ٙy̠~^K C}  Y7㧌 3>eUFM@ٺ*#+DYHpé69ob *++%ITœrLi*,kaϚ<`x%1cCmHQi aD̶o;*s5rxYYYxCc"͔:9o&f lC ,3V]]M)6mjo!&L(//w|„ 7lSʲ|Nϳgo߳en:Teaf9mpIjRq֎o9kHG#+Ḳ MhBЄ&4#378#):$YEFPsb0E)t83؛$pLFm`,ev8ma fYJ)c)P"0zC*&m2a ~4w8Ҝhx^!$'͚EW|)R"/0PJyi,1#tX2Ҥ<>~/O:$U,9H):u¸qɒoˎlNjؿea<yJ ʇn}[ 8`BϹO!AA$!%B|m&D|tuBQE1\ .Z3>#h^^޵^PCG`7{,^TRa($a l~ZO Ghyi"P%]^5 Ky G e" Pog͚2n8%d6m ʚݻw$%L`җ^6iΜx n>|K=^eHcccmmm[[6P_Cv:#G,,,N!]6FTITGЄ&4 Mh~et(-IV6aB8G'w*Ypqg>p-Pܕ

yϺiZÇk $/M3`g^< ,@XNJDh6U%EtTeB***_^~@3gNffSy e()$ᎎΪ1 W8zAAc,QX$Kqt/p[[[AA)h[[[3)eBvu6v8r}bcv"0$VŞj,[8 gz MB~3ʳ +p8:::-s6B2@RJcfH6 !he$j.ҋ.hڴi % ---J,SJ$ɲ$J`Holxp]AAA`Yv…F߮e$vd yWvQ|X?(4Hm.q/6WyOFઃ`afy !Ҁeu/_~M7͛7?2wkMF=Guy(kQ%YD)ʒܿðVPe!  q$IyA|>_DqH㷅B]%/[mcϵc QIJ 4 &`B,6[ 6 dHe|"ma@88xAPm\W^y7߼K;kF5Z[+y$PXcc R*Tс$؃Dee_&%+  ɲqdO;$,H#Wigde=n}`^#_'H<fA@T>dB>?T ;Nj 5Y"R ޽QeH$ IPkU1U&Fi{4 vmz֊z1m=~5ZM5-jj$\%7Xr%\vYL6%˲.z>|˼ga ۦhrnDH"u䝢UzQQQQQ͛}ѧz96,sDwÃ_P.`X{fqqB!py "N>WӉ9y|(SeJj59 ":$u+I8x$yRiq wl41dsyI"Qs R$$ʻDb BVZm۶w3MoΎtzVr% #RfffKKKGG+(BZڬDv}E0!vO.;v̘1!@8sάIf •D*Jq(ixC$'$:=vcT^$+%e JMIRs)VV^"B8y)oV^+|ĉ~w;z(\WKG!B!yƌ#u$~^w 3mwAD *"HStƫU[/X<i_TXyN2*HuzŎ:c'$"$Xc٬PM>cbbTfelQtw@ r^?|W4ilO>dӦM;{۲bŊnQ8 G^ym|!B4g ٳgO8!^:nܸ?~{Pps.\r"u>={BЌ3^yUV т -^Ornr%&&/߹w(skM:K2*>^tx^,Y4=v=SO=}v GYYY&L0訠$Ir5@*Z@p'Džjy.<O#(rFh*(~o30XH\]]p4a(x<&Hh*hג$3g|YYY=$Tp|3g @R>}gt%Ч$I|Y[g& %IsN:JB!vǥ%#.]HD{=r]y啹&髯t *D$Ir8G׳-XbW_}u"2 ~kfXF+F?_!4C`{eIN??#~[m`0hnqqqDti|Ɏ"***?~<=tݻ#݅hvMM@qNby`2Wnt"jOb]CVEQt,|ܹH VZ.yHw̾Bfjz eDtm-urwnDD';C `$)'' E{_~HwBc~B*g-`y,'hm7ȹ_+?l#"2̻H9qǹn"я~{UUU>|x>}}Hw <ONq\zĉ푾,.**]l"jp.ˉfg^ ޑN'V"Rhq\NNN\\B?^tO 2|gݺo0zU !<JLLEQG>8r^(:/4^7-~[3{<'^ (1? B$y^^\`B̙3111111 㸨P4T]wv?1<(c֜wD 14p1cᬌީ #r00Atp:lI:;;EQTT.% tԩD"R(YYcg+- h^?ߩ6{oHum;oW,sѽz:Λ%"2̹V}o $Ifbn(DxAkAQjUDVcccyIhа ~A~uǽ.a?X  B$Q% ( vvv\жQDDr~/X "#v^/ȺwՌ~f ndfJuɧˎ(@y`MSN;v,ے:iҤ3 }[X ߹I*JѨjV9KbX,[7-_[nk>z>%IBQ<ӧ/\o|7c̴ ! 3l eŴDHG[,H Đl(el  MMM$qD@!4"CSmS~Q MyUD5k{iY>cid y  H .QIv&Q.?lZn3!FX(?jUyR"*b`Y[ڼ\0̲sj*JDDeKUo,_\Ra$\[Xn*(8c j; ͡k_VTډUǎ#"B1{l=^۷O"2eK8͛7 `Q㸷z5岴{J D4i$":ydAN'%pɓ'qGEd]~ucv.v 7n*^WvGsȴ}k(;J7Hx @hkkKJJgz"R(s?~l6`~FayH[[I$n_yy9o"vС^QA.\W"Dt>e[w:_5M>w4TۣHM6ς \Gu9.YƏ/\( :!J :i#J$I[^ z~Zy"JDƍݲ2ZKDuuu---'N d@oٲr\~w`8$(RW9 DJ y"JDA*J9z*ݮT^rD: XŰ?.Y3wdX zeӅ611! Z: cy;l:F :6.{(u\R8\*^+A%xd)xlk ?~b׳Ttʕ$-[ދmrؤh˥axmVhvvN;w *+rĉs̑$vĉ{ʥ$IgϞe<rjoUPYi:q➻kފ[nvo{y3}[ڶm]wݕޞ.\O*v9s&=='N|%I'pN^lϦ}KDwxMlNVѺ_V""^75{rSNyֲhTTl%s-XX*~ŋ۷a@M^WVnfwN"x[F.vڭNuYYY/~ӧO;gdd( bcc ׿CWb^;SRA!h{%}YjN;yJ^HMk텴8R)̜9I~_Ojii/ׯ_OL.3MJJ;vl{{ܹsY*RHPk4澎jVwCD$I{$It/$ZM xk8kh}c[{VGD/,A災pJ5{l^n~hѢcǎ? h4ui4y.h{{~J+N.*U*q,{UK7x?S\|~%~NO>$333>>>>*nollͭ""OO---TPRHU{ϺuXo׿ٳg԰&LF!`7\r~e&ƒ KI %ҽcuDdKЪݞ }2ZE7Sj$IC.{!'g~7w/,,?>--mݺuǏMY].xY2v~F R Z^QQADYYYΝϳ/jiiiK.mll߹?>~8v>Ç(..nY7#r@BKK [*v߾}wܹsNymǏܹs׮]?'Ik:|&3'ON-GD(65 3:W\i݈*m-T*|v$UGY,eTVʒ\uJSyaцd*/+,(YV|[eYAUnͧiVyQUmN2G?:604w*n*'&gU}U8N$˞gQ" s"P(؂[7e'O 7,\?x<٢ $'%}sP4="u9e rX\{:aTPclߴ(Y׈edoZ3WWKUy]Vd^NaMZ[g"2vo3+lgMuTƒuUG뉲rkLDj&2mJE}{sD0L]n.灂Ri;e/:ʠ@"r:lDNOlv v"ƍk~8Q#G|߯Ԕqck}C3 A4=H7)O˴lv#{0p53.-K WVa)1U[,eY粣$_\D)[L7V*zHd<-"0b(Kr36%Cfbzsn[ EɦJDu >>~H[$IbE(u^y75k2&M߻ŋ- 5kFUGQ}=KW/(?ZED[=+jpu s).wseVuM5ot \*eATqS2Wod\\TsDEkÑ y llBjJ@4' &IDU*U\\ Z_ Dԯ~n D?#Cl:"rÉH8N#lH::a(5tu`*_KR0yJDe5]/I.mlKz^{١J>}aXubkQMpqqUi\\T{ h=p!5:19,Ɇ )C"wǞ(A.["*IM$IDQ=(ZC}/-AKDg&=J"ɇih }M3Rq:a0سg  ";;;???Ω:WVm=*I(QE)0Pb!@8.b+ $Ivjȿ/r>;}tχj{6ND L(ʯ:b$$$gyA.\PSSs)"OMM h>`DNHHX~=Fo: #Odcye5 DDƒ-G'}uO -(+o\56K)8?0褬/g|BhX֚"pnCSSg}Rbbbry<y믿>~FQ(D٦%IjiiAh:'y rJ#B \|K*,%۳Rr^[oByB 9yH.df̘b "h4on;N8eʔ>~999<`_|œ'O?~x"ۼy]M7t7;I?<Ӣ(с|ͩS=C=G}# .Yȑ#  / 0uիWg?9sVݹs} 6lPYYP(Gyd:X6`d3vsS{gZƴ[ j(55Um?~bb"IwC:t뮤&Vzɴ~Fj}0aBZZc=6_g%"+be111o={n7K$Izgx/--}7 E[[ƍjhZղxu:{%2Bo¦ǎrVkZZlv8(~dw` ߲10ޟR5o~ƴt1u]GDov{CCM4g'%%͞=P\oBBµ^ɓ6JN~,YuPu#55ܹs6mzG?n+++M۸qz7ڿC^.M_'|RPP7rRO>m9 / /{qqqlرc1 y8bرA4MD^{qڶmvq"x<֭+(($'Uj.۴LVE<?_RRڥb{m7|gX2FD6giooߵk3hkW6ԩS999mᬭ=v /3R !P% I(rMŔzJ`鳎ht%#"Su~~7_3fxxөT*.]v*5O<9rJ#L^Om۶իW!I& LD ,/lr}>6D7ϹsN8qWd#iIJ%<@ֿ_W_Cشj3ga„dߦ MDĀP{` KLLu\HQ#--g?zkӕ+Wz?mmmZ@g@MO2-(s=g6̷zK^Ptzm4d}7i$9k%%%Yؼys]]ݲe9cnWݳP]uϦu:^/9tMϘ1;y^?{2e  k.]*I<g?^'O??+&M_Ņ7DjOC aX""w?pԒh1ZJ5SqV'cyrSNW㦼Ν;z-Q;OǮv5tvv{WUUu]w?l67pN k4/O<:t7߼kFc]]ݶm۶m&tӗM{6R? uӾ5$SSS-˔)SX}H]n0}]ɱz8'u:+r4iRQoa%p|Rցi?$IjZ<(HSS>*b>EQ;vMy~֬Y z>##/WMDaΝ t*U+?~}:tMߜ6mɓ';V__&L'g;}]믿j㸏>($r@vƨ 0p kڗ AEQ666v֬YDvfnonn&"V>vttN";vl\\`PDtjRRRV0trH+bt}Y^I%Ո0 L}%+VNrJ^{"Zd xX,cǎEx4(TLKsXXq[`=lpu9K.]t)rY, B0 $vʦ`qh@fEbaMtoyɒ%l6#"k:11v8M]tWFEyB(zCcھx]H>ʵ9[./ꚍHyGug#zՕk7Qx7R1mڔŲ+.}}$4z^ODV^Ni4=A<+" dZ.wn{ۻ:\{%}VPSɡ0DVkծ݇kqgߙgLý},6PLJ3gDI`yG!7zNYđ% I(>/.wmõ[z;&spQAhd7no=^fy捶!P % ah0VkeŇ n~CkuԘc&Nծ9sfcA4z"R(= j?~l6`Y{Ϟ= ԩSu:Pܓവ%%%1ec$IP Nծn p--A#Xuu(l0A ]zb@qZaۉf- iZy iZkz Fþ$f!A"jiiq8D{ }B*]N>=$ĉyyy{{;\}lc[[ۡCvMDs̑… 555N"T9^pƏF^$uωP F# 0Μ>cƎPBDǵ JG>3JCD.C_5B H߿Hce!{lqpDb8RI#ĸޔ]HkD\7;ߞD!UD RA ىʵʮ=f$rqʮוk7QUi^wȴ}kU2VT][}0{s.~Fl-D4h5 vht.W_=cƌ; / /رm8U3gάY|wBѰJ(ϟ馛>ɓ'+":{lZZٳ&L VucҹgW1;ISn)a xIԒa +tcQb4._FSya֢KXARFl^h28+r{rSH6忩VUX,Fm/@Ƀ~V|2H qN aLLĉzLLA!n;[rt:*++'N/ǩT*"x<=3~@UL äI-[T*JgϞ$ .,Z<d:qDFFƀ{piD'qBgCT,罺8o .rAƐ |7#Kn'ꙵo*^Wqi~3_WKUetD:Zk/Z|.¼RGD;N7-wVjDDQ_uucƌ"v;m-V=+jG}ԯf(F+[n?=11133S*RGMRW̙3&LP(W\qEuu5 lmmMHH(**"> j̘1+VQ_΋?U͞$mnۤ{8eWR8NaUJLuD|MLLlkk;'zfkkk }8t'Ox<,Vًɓ'o`$"NlQAK=rT\z@AYWJ(y^j^jg]%w{J8Z-I$Iʙ3]klMΦ|077WPx!3|bvw4ݻaƌƍc+HA}3S2APb!@8mk Ç8`0ZmOZT%Y Hs QAy`MSN˾N4i+#ݵ^pV Eoϡ&b$uvv €RARic.&( "GjōnA"6yB @]v"M>k KVN{R8N$|pESӱB4rYW.HDdSO"pD1]K@BHBLMD#ݵ9NX 3E36Medd Ѿxc_ ݏ(JDGDصIyz3H`А  gt:hCW\qENN<:%%m< oX,eKni~mπ%@oR$$ sN>wZ;wܹsV+ΝJ0 l$Ia(6 Qe5jUD+#n5@k<hT*U[kךp")))'u'Ϝ>37u:]~~~_ɓ'sܹs#}``AKSya^iQK%%e7Qρ V2&L8yd]])z'N8x [69I8HDЙ3gzT5 Pb!P\Ra$\[Xn*(+UaB95%^4FZ\?:PbIRɛ#tlMl"IdOΞ0q㑏bV5V7o^GRQ%T(EdMyRxx?ޣHr[ 0*ty~'gPR,̿*?GH Ei!P"}kU:#zLV]㤟ac@0/ Dêi(DT+RV@D\ڴUA ]6 ]0Pb*횝d_AY͆¼i[,e=Cˊ/^6g4=zF&,{r COwSRRƥQT&Nu@C*@De>RcIL,}.9K(Q`4tV-))I%%%]hR&L`Zt9ysh43gljj""sSSS]]RA,L"&{rvRr%%'eOΖC==Rξ[QAx&ѺuD4mڴHÀaEM|+ 2lh[[[]]ݸqHDdi433ӉFWcГ>=N =`XQ 8 owU" "L e mz= n#bD Ez}n:~mXrB"J>H_ \0¯yյFQ TǹB|#}Yy.Z e @l7Go/ ll:#)8# )en&>#b9e&HatJOOIxI!B4z5T2T zCZ$u IJIYFAP#uA9Ni{nAj}8KHHx GYB%B! B VTw/ߴ/씽uޏK<]oC! o*1}$Z*[焪zg OOBrY֞u:]L̐/|lll\\ez/Dq{{8h4CmP@y<+# DW?Yw;>>@DUTl]/y #=Gx,E?,_<IU |爐0:aXN ߞ|!mX%![SԩSjzH{ޓDR]_qqUW%B0%o!ϲ13sKoIE/e.qԕr}ϟ&?~tՠCBϟ`3fVUTDzVkkkl˅g` Ow3DDd:v8* "2_Ul0'\SHDTp*zDaڵ9%Dŷkдuw}SɒtۋYz#x'2U>0f]n[cVRn_s}_L=:%* %Ț_[gEQ D1=gHg(oO*=w?(R`;vRJLLۘ^WW'$ d1GYΒ$F^]eP MxjIH3*ύ=^.nɺ~Xar-7yc Q}]-2L/]N[7QWþ$Y08Fko |OyigLzY*wnK:fhRRԊ(!glNW411qG N"%OQI꾟-"Iߩ[{n|x(u&KΜ2~o, 2=M=:`:v8ѱNHw/*[縯/>T?{9S~o>ښWqv؜ wcR;Ӽ`u4-u.kfCقkpJ,zyٴxZIz4FM@dNR8IztnܣDD4n|㾔q?[sDKo{nܣo7],AUp;=G%YtqO-ܸ׷̝9f.[˳$wnݓcp~8ΰF"OfՆS}˧KF۽LYf3ҙ/y_27nOFٝ{kfZmQ7dޝ3G_8܀䎎ĆgϞ3gN?&)?BK0,&k!YUϞ=aWVS (HU"x\ ;ـG}DyBhHCxTpc}_s?|׫ "I*VȢ7~w(Cd=u=pI+[__j溣Y?K:ۢOUe=;%h 53^g~ }=7 p$ xO}Rtc>c0}Nϱ5O=І$IJII9{ٖz-ɓ'cccǏQƏ܊q B}\+2'\4oXNoUv!IU*1L&)666&&fYkimu\hW(S $1DQb! C2A4T&\?}~oM"=04Hm9ӓ]\9,:$H ѣGsHKKK?ka߮iŋ}Gͻiִ8b#lt:?QssgD[[[F"TP DDVn8FA>? U-) dϞN[+؜fٚSW\{;WNv^+.ѺFl}b111*ntvv7r\.( j0LPfh!hR[ D A(<HY5f}0xS:/{l{zJ{㩞j`HJJ@y9Oz$[Z[:N^{fy9/n5|\?Z51땺ˮ󼼀OGa$_Bkm6{=f̘)SD,$ED@ؤ%>]Ǫ{*xR$ #PR WKncE>v|~cnw[[FlGsM{ Q7u+'nw(hb]4O2&SON`w \ZrHO;wť͘1#---???y <DLv7*ܡ.zC) Gj Ƥ!| pe~E(ض~mvv:z ~/b?_;῿0&^^L}kR ЦDqfE[yO h0m4dZ%qPNϜi9()y.jxob@4`k&mڴ={vVVִiDQa_u†hrwrr>sn^1qb{;/@tq:g51Io1IDATui>`khht) KF#aܸ/l0n?|r!hG'Z:7`c,| 7gZVy`T+ FVpfSێ?n _\7"EߗL]A$!'B_ }뭷9m?.Y~U.Ivoci"h43fBtƍk>q"l77rvA:::.\ šCgٲmV=*Up [ٮ-n!hτ/IJ LP~[?. n߾_dҜDnOQ~rUTTݍd"rl1z}SAV偩CS {C?yVTZ|R$WW z$V }$?N8AF/!XtB)}?!9msy u,nlb|E:̙3&L8r,U>=1ubl۶ }ޤP(zh4v#HA" &YEqlƍwa{yi>TOРy 8SFʵRVGpcNMEoDZ¼EAB/ZmR.!ARq#1,7Ȳ˞vʕ---¡9.ٍycV6ګ3g455mh43f0GaLL̙QN:NQ 6V1koj?=)I5]ӗF Hv=-j٘CK7i*X[\LMK*,%aH[, Z{jj؞8G^7 !2xiV"5k.x^Kjj"tQ5׳i":uo~6-c-oi4MD #=6/n!i7ÚiV*Zn7E/z>]ZmZ%&&]sݒ$s'a_G3x! [އe L䂕kJsX*XNGY $Sya^aBWUb1 V/=b)ʵU#o0`GGnjl 'F]t@T*``KzT*8K̫j2ܷo֭[HTΟ?_eR(t޽{|CBuޢ}e Q˼Lu^, "2+.HDouT%pƒuť.I7Xb'`Y1m3Qn0C !A;bBINNFaQp:}4{аavvv_G34 {_}BҪ*ӗ[UqTWK,W<: &$"xY::=aXA(1dzpB%Inwϐ( .5Q=Cp@' yn=[ga LMGJ̇90*n.:"xq΍sH\<&Qo jmmkl$b}(i9(%ιƮ>\((ɘ("eO:mA> Dr)SyVkmijjڷoٳ.\_:;;#T0`1zFC~R lz}|+I DDD.=D۞~ܤ=mmn 4Q֤tA'(dAгK%jtEI>̺L( ""I.\@D*J.ct+LQ"x< &ਠ$%^9[uo[+oJ"VH"ȐwD^ֳ,f&N(Qb%{ZmDdHLsαIjΛ7wc|Ge@7o޼/eJCF:Hȉ"' _b}DDRͮMf"O'M0ԘzN2eX|}ϙى.~ڿ擽n2wtɯ=̳Lƽ5f"C$u7f>nx* Vw^ZEjj4TTzޔ|5\A*ا?44DԩCw~J(wܗ_jZ]R>!!NR :F{?k!7vkbR18q}^$+9]7Ql7]""ޭvx̙ZOe|:Q$".=ABמ;f|stEswj*N ػU #}'\kkr/Nb &&&TϳDBl(qzP((p A]Aݹ'M˃(Vcc^ۙn4ƥ:5!ZM:R^/(QQG3gZp= ؎!&sςK"|O8qVvHo'tO&8'N8q3qi_3H#ļ1`n6ǨTH(J{;bBA@0t:G?~|[[[gg><'%% ~v(aTq111UVh0$q.] RA@j~v@z9T(B/ƠRAFPb>̡A*}_ D޶#] DS#%K577+JZ:q$ֺ88_^ 4<( w IhQAf8M4"NkK`pΟ'"%IblDZёh0Knxޱ))f"X2J!6!# RѪ\d_0tv;GfԌn^ lPs` J'P(mzRnwN' BL䒟p^rw#668}RRHFLllLll Q)XV|cpyUDL][x*-%x+ʵ9[.lk7*7R1mڔX@D/ qe;lj MJQb>!`yW_k." U68g}kYRd7㸡h0$%'T*uLL󒣿D>Rse1kE ,5E['t#hXW>a^ibl]^Xneś|ںDD[)ofWj6Pj^AbX,[7m~~6Ee˖bPc+}MTZTvxF0 T))ƌ[0 W BPL6-66G?_B%xr6#,+:th=QVN~mr[mѺ"ںD[h4VMۭǁ}X&LuTU׵cw0<kٱcGH`T{i֋+3fRwh*߸kɸvsG{Q2mZUgUph˜K0/`[ā>fwc qY?8p^XAO~N)-@Aشic=rŋ/\0??Eq' $pQ\\NK'dZv;B (>-#Qs) j6=7Tt]\D[^4׵[jz ]W `͆yyR/.ed`Y}-=pz_#6~(G6#;|뺶-x{Y6-xǨoA60| a%#|. \jvMD#h uvvVUUq7o;yl!=vmzaqꫯx訪5Ql `/GDt]=}"7?Tp?{J&"QJܳg֭[wp~m9$a~.! w"wnY]]׏Cuu;C} vܙ3JDAH4(1P48NDDsDA<+xyIcƌ7nqcƸ\.y{VVVjj*{f4B }jƴ4[4CI////fPļER|&xo~<<`ǃ[vx<}5t(Fp= U((Q 0-s ߨ Ja D{000IwgOr 錉"4! H3y>~ȷ?~>_7?> 6n^)scfj/G]Bh!h%?!ޤti-YԽhUlw#7O<껱 z>;;{ĉA7QA/gF}n?(}xroh֛;.}'/o7ќ=M N>@knnfy$I (AE+8C;tOX*NiLԑGyFdhzq엑x^Ap8EA]0墧CrD={Pb> {8LKJV?~('N0ayyy^vbbbت bΜ9 wBt# 1qUuQb 0B~эv#S}J̇-*co͛0aqѣGX1ӧ!qtADDӦM2e;}qϞ=Dr~UD+5 }^¾!B C׍NQ7//^Gݻw޽{>m?}g}k.ݵkg}vmhhϾ+=v1ȎEM%V `tF咅QRbfV=JB(1=%C [݈x7>~y|Cr;;;ܹsDL<{R/}E?*!j^\83/~!%()"B!B(1PK̷EOg[[7.}٩j9p+JV"%~HV5WQyZ#!t_&"Vb~ƍC!%EB!B lx rC4ٰ>dQ5|\}֚Eٞu R`<+GF3Iв1C f_V@DT_=F>k| QAF҃= Ium=lmtslj+W ,"!B!D(1?tM_= _=y=L2ɓ_(7!*z&/2XU+X^<uXڄvdbhQt[(y H%@y /^#66-|G{߱-3gΌ+E1DW< BVVsв\@Ġ|!ļJDIJJNl߲S!o"B E`xFjm(J!uL %ayQn劋kn|;yC9hMw$]u :yI%IJJNv:rA!0 Q C(Ro`xyNVsgnw_lJȼ. Ԩ()1/45J2$%{F[M( |%E7" H755ZwB0BBTdɒ%[nҞ_"J"1B'DT\\5EB!B%#<ϳ$IJNN&nn?~ɓ'8[T666l K̷[l6;v:v-))9Yb>^jH2?oϟC(J#B!HjQJJB`G}G&SO=^ƲHJAR RT(Y7$|D2&ԼpQ+&&8nhWDRAQd0B!BJ̏X\||<y$DQD!x{78n#pgUGs( E`8Fjm(J Եh(/ <?2c!*1K:p*;08JJ̷&$$xo(XP?Pb>B#{t%PPb>lPA"fc9Ve+F$ E$BV|R)bTkYɳ|g !*X!0 Q C}9$EvX" ]WL(g̘4%ۘmTzKqϽ~;"s8niZa͌Yf"l:^!jjGI%棰(1(n D}B0BwP! DY!JT*J̯_^R\\裏|F!B!xh(Jo [>q\^WP f K[,1~|H{gg҇h2*ITV|sRzB+|ZV)JO935'/tɲ1=p{g/%"ѭU=^ADD?'ޖZ|S'{7"yo(/`(t O2kc Pb~&ޘX4==aZ Y Hw-Te)+@5|ǻ^'_zZhkx߭]o\1psm/(\8?,v&ڶkJ2\ZeM֊>ACwJ"}+ Cl Lm!ޓdg 6Ta{֖VÆoWia0fYB1A4T$DR˷]fgC|KskB4檕߹dܼ2~cϕ퓳/=ߔs|Ufkx9|T[Q2+˛d;s%%][kP>L9t1L{/P6QAa+bz~{약oџ%Qop7   b07 ( F%2/" $-Ĩ D< OvADqqq:N B#&dZv{OnxT8O!de@U GIk爨jl<ϙ38N7Cwy>///GQbb␆npB}ě3oNnxT' (|ggg_?(B@//4;2GvyZT*EJr:?=8={[DP(͛[R Qġvc y44އzC ! 3 _%o{y?~{8BMGLLJ""rB!B(zB=y^vE/a1c+j*IAAEA-^zv\ w.GM̝95`t[d@%y/eSO=ꫯʳC}y~ rB!B Ѵc[/ӗRz}P4txhYK^gFkV}[Hlfn'bT`xHLOtryg׬%] HDÆ{yMqiW>fF 5n^lKu+ҩ$cDD~+k2JvV|G)YU+@ti8T$@%EB!BPb~thWCDsjͿmL53hǶo&"(yY;K6^{Eꇟrڝ%?<㻫$ySK}&+;:zҕI@tPB*J/Ng}87o^ς'D=-#Pc諯ynwB8uTGGGUUU_' vC奉+0IDkguok>h.,gg_zt?~ߙ命W9gї5԰`)5Tp7;yv11{y= #7p 0B1D~G竫q={sh7|y\Ba]bcu[BD&RSWk4"b3E^^n C~`hְ`٘SuQdCDQ_  Ե]$ ,1/x >|ϟ==zȐ!L6nD7L 7i{RxUuQ"%E/u/Gƍ7}zo AjK[,/1ǭ+ 5]mMm)U*W"a,B{ڋEV3G.‡"` f8@.6Mbӟ[RSS<"(BP"ay?.1 , eo}v˲oo%,""E(B <ƍ:uT=v/G-&}Ghļ"nqRgbQ.~(RAW%?x+:y]u iQۢk^9KfT_fJnZm׈hiqDTmQ˶f}DmT\daE(BKTI㖤QY6z[ȍ>>_MdIs,͢UKA+mw>{͝;7&&GٚgA*he*˲bD٥VE"yǢ%"K^ЯRmavѨyAK+0!eI\%DE4j^J]?'/QCu膕>EG>mM=-HK"κyks_$ 9|0Z@fTzORBv-z23I=-Qɡ:TYE?DeW@]E"^XbϊD3%i(-m.w)I. %m%uDۮ7G#$?W8⁕νw浰Q'o$Q˶L57SFVgK :L ҨzW/?߹Ds7Q'zvڍnFѧMMs[Ŧ$lBTg8KX" Ե]$ ,1/Df?ߝQvGhKDRi@@tO?= 8l芪j"ssd-MD7n[.~rqwz:wQM=,~X|c(l9k7wlx/ L+>s>-lP2'ybgz" d} X%Q"EwhC/; -;WY,=έ!y],=[>Yo(qnU[bnOnQ=M8|f 7)h~'L*/.b`."Xo} ? Ե]$ ,1O}xpļ"JITI]djuCAw%Ef?|=[A`rPwLUYY=mnnFP",}xAQSSKkbyE%dW_v>XZRYqE.F"W\d5sa,|xa>. G|50%-ayE(BQ.2%Z%(Y%xwy/ԷaÆ8,1@.0P"E6RA~֥e  wu2#fLF^[/[8ʜԘ111czQ%11cbb l h4֯X2j0aW˲0eR،ȔbbĤe:;)jzF2N>NtQǚc%n(Ȣ5cWP[SKD4j^܈C (0!eIʎ~tJGJY/v<3|B#G5]8+R#yRy1Zw^H)2ƬUK;THE"ZU|ޗδ.efhxcy-'0M;v츽{w,_C@ayE%OmM ˲Yaxh4HIJ^ H$μF$pkWKyDJ$y$_77-m.qOet QяV""ˎ+~&mw-57(_QDޱݱ3}#{iftܑDeyKu*w>έ-t 3mFN*.D{.3?ݓI;}7޼p'nIzaBC>!/00Pnnng0mmm=.]yny徧hkcW(677{y{;% RٮT BPīHPxi4z,T**U!N|MvD0 Fsqhk  wR8ﵡT*wQ謫VҚ{JJIOR""JjKH^{DD@u5#pLm`]ׯ-ڞ}H ]`)!Dd -R`yixg,1o3MoKQXbo\ZhXqvvs [N \ XNV|-;z t Hu,hpW$ (qWC< ZtwYjsv0DE9vB^Y+ Eg7 CV ` bYlVeֳWG0eV"rww_վ,0zxxmE!B),f-5hY}h}ֆ}ܹsDx<___"e'Olg{YM ڽR-$%jZߔ>(B7BK }#0z!`1̫鉽idFsF t]jrY 68AgemQ9ŎXLBP\p!77B<7*]wD!Y11+hᖂpRl,uL׷̍ncՁf-\q4/}Z"'\1C9KM}@z(: KeITMQ;v(-̺jjŋg۴ 8ǂm_Z7UӌJҟ~O~Kjj5klbTUI`Yj,1ߏ"t!"tVRmmmcEǏʲFh4a4ARjZޮT*}}1oEk֬ѷ6rW566655o0WIJl||"{}V4H"t᠍й`Х;miw&n(ܡ@D& ZW Ex_MD|>h}a"]"th"DA°~/;}V<Ƹ"_# ~x"BDjk60 0,0FGð 2&ր5ļŢļ+,mDžk՗E0¾)cy,1%歉VXb{jFaa5wFm3c XicH"D"2p_!BD!"8h|Mbb77˲MMM Q2 R\|}كd^ٰayw3m ň"BDKRjj7tPg +I#H. JqǬ3`_3ZW2 0 (#Wa۽pFؼs/+svvCΎKΛ>qbCs9yٴmi6LjtEpa$<ʬ͆TK. K̋D"'jKMA+ҲIr ӓ^Ar`SSSSS5'FP"Es$up=Zz <\b^18GP"E(M :q/ `P3SA%I[G-\ G凵EW/V8oZzh/vhI2rGoi۝68޷zVnW>7->ӾSμu -Ov{ lidO-ۚ(a}=bnkQo)?{a,@nQDDX6 O&>{F-{2d/Μr#v|u~me=v*ݿs[M!4o-wj})m<"lkn_Nxgtg$`0mژA5(8aQuUˈdI)s?u:Ne2Lh(.iA~V"ڶb6m:%̣J>HDm]|Z}{ጎ lZ_+3vɂÈSz0 O}ͩk4'%Ifa'>HI{ 7mh{.EΚۃaf(ն7 KXbðgLowo\0^rٽ̴uYrm;gen^{s[9scpg Y`b}Ӧ̡s ጎ>A{=6 ؁)7d.0&^-EI H;fKq4%-~Cc0PՊm‚eqIm1 $p`/1"E(BP%>:J׻'D d ,uB8Kr GP"E("Wb }6 @'I 'װ @l]TeYt wt0m @`0+ȥXbE(BP"1 ~cae aXa% ayE(BP" rv!{,j4Zh4 2 VjZ6oZ+]tY81go3TZ__X>X0,q,0---~~9Pz唔̉)hov]oС9 7VU zÇ|~.Ot,h*8 TX,"#lhPf(;;_uu*-@0uԖq``رcG0v;w%]$puVk6ḲE(BP"hN:zxx?>((hH$YZ˙ dDDWG/o9޷zByks_#޿j͔ܴMXI)=b1 F!/bQQQcƌaF8h?(ࡔR,"2BDDѨyis#{>XHD4m7=^ԨY\>IreY6:)1f^MOt5AƦlT=MCt`![{Bn̰)g:Ht0𼵹OVzOffnɌ-+% ؍@ Ч{1aǤKmqDtTOr(mG4b$TGR"ڢg(!9q)(( ;?XJe? Hު{2zQUo͕QULDTSSfʨzqpʂ TQ0ŽITX{Zɰe\;)iW6<D]̴CsKu1apc >oqI  |~E%%"H^vo1o,!iD%Gw "CfIګuU_U߼Ooy]k'oUMfo**5IݓG-#"`:YFTMZNm|DQ!ξ^ƌfRA+@20toH;Qv6q7IQ#O0 )5+"nw{чϜO''iݜF˲d\ְ2zCs8|䷈hN3|v26wsi|]˺8b疟ux}yks;XD %|>t#>uC 3InF_ĽGDy l# 7jw+bn\eIwn9^`LvZ1C9KC}}aq ЭVgN7-jVR;;~P( vAp-+ Gdh!a؉W"Tjۘ=k8\16.?$>#O<i^F߫|>|>BX *DV4lhhJ a2^A#eFw_E x\/-"l]WYa"H h4JeW, ØB0 aL0cVf|סgӾ<O(|aj5Oϰ,ҤT*H"qo9^.py>44IJFpus7nܹs_y?j a58|~bbb^^P(hJJJSw1":2baÈΝ;eeEEÆ30dNŽ<066vVTgE*`YvSN5zò,F[ܷ߶@C---ovl²읻&L;v\aqD pwTp9Hb&޷Cz9s }:@Ը-P L0aܸq#G>˒6ě)te!CIF||})9;@pu ð ò4aÆx<XqĈ董FpG@@S+/]:aܘֶ۷x&[.;SN6lXllT*~[[[[[[{JV;:ѮbcFV{x@w=HP\Z%%bϻΝ;Q%%fRAWeCua"9k9|{j://oŊh40]<taZhz>7]``㣢ƌ<&vСCTEB7]"^2Tٮj­7M#$y'0d"s*HDYYY_ܺ˲\^AFd^^^F5@4 QkX)رcFhDwM,5<==i'@Y(g˲~P }N:meWPV;ta0ooo\^i3#G|h4l"Kݻ e*w_/X,x~ӻ4nR\EeB!HHVk4˗<;T*U+ rڕ퍍M a<tGTXb9=z"w7 b[DbH(rssxEAg8@+38j4`VDB3<>! trB.Ɉۥeeׯ dIDǒX^ }ļۨ5 >貲z'^#lbaúdMA+ YH;% EI<=/.6ޔnژx}~/Lb <فQ/SAkn ^6? TQQQU8;AJ?/A*!t 1B Tu 6];A655w@@X, pvh|fJnZ|7ժ^\ sv-7m5+ȯ?.yeY{#Ј$}'ێ~ۼaKe;O}^p֭[MMMn uwwpv?Ae[3dW}|vÝ[ָ d9kkcYVͲܹW>\%W`_Q 9xxLQM@D3gOϘϪ-uWMw [Xǰ#g lݻnDNDDT*])u~fܴxߜ9׶rU_!13IFDQ%LԹ^$}@Njh\$",NfUq{5' }˻sS}]OWئ9CȅCd~@n Mvwn(77j*[jo5ޫj{^5{,u~"WvLԴ?|Dž޽r޽7or z*Cs0q]\픐{ ~|ޢDf;DDD;!"GDL8f57SFVg':g9;{ D8scDT 9KS(:|`j[#FDGFL6lݹsx %svxk+e7U1 _0_p")GsT֡of,] +Q5F.󭿫`祸UK<%|cYe맾nm׎du K%cҲ6@ Yb800Mfw7x萉C%wjGW,s9Z,~BaDDi4X E[[/ sQvD}sɂè9 ywF n+쉮[xxy6""\wǼ 5.Jpscޡ7u;,޹{kb̄cTpu<"J~r,}b啴-626u^I[Z:gkiviҨҒfIJ)yBevUh4w=}=>u/_O]. 0*J7Mݹ$#"GEPxxxX93Oy{{{{N_5*^~{sϘ UUUJ^VT R!"'xzz,>tPJڪh|}} &w}``OZz'/Y!>z5٧lgΧdgL$¡eGY9-CSJD:ergc^k[|qQo1h+qv͛L576K<<mjFoox BUXV0 n~n ) ~w@(W]unKuaYV477kTj :s7lw5_.+}7oY.]J꬚vB͛7 {܆ V__eD4agȐ!UUUbqppH${ (//["?I)aɺ9܏gAld""> 3~jY$s _=?-.ki}\K'\iyvFdFLv䁝*K/PIR*."YI_KscTdş ~}䥕iLȹzuOz_7&*2L*Xo7Bv˷Ҝd2]mN 6:Tܤhnjl>}Fqe征ѱղHDJ9K֬b:^q+oܺYԍ0_.lmmD*BdxI=}D^n^>>>>>>bw7]|eqwaÆwrnޮGb***zyP///PX]]u)jLP(j>fYaH4dȐ BaW{{7BBBr. q/˒6&Q4BLDWgڒ,isf^̌#"J<`gfsL+뼋<(woCP"ݗu1+vwJ[̍yg .A"R+}G$|6*,Frڿ-lбQX><@n'o{{/[!mГ^AkFE2M[CCoWJ$I pG I~`T s}t7ϳ˝Ʋ1:[ej3,I<< w/D$O;d(-}=BH$v# ex7V x,巭|ՉC}||{|DsM<^__/JܹsСCkjj@Zjhh0,m^][ҜK;NKR n4v5١;H }G+OY"7XXgԴx}vpнFZPFI$>^~BX• |zan޻?j{|+M|xD|vXt/" cbYb5 "G,KĶ{}}N_yW =>gKKK3444BFMu J$JK{q9:d 2s8; Xr޽L,H74k%ޑlbL29pQSf &T46*^R/.FVZOGW*HDD>XbWDw)߃XMkELpD=Q|ԺPU.0#nj PCX4o{kkk^W[[[=<<Z4dȐn*s}<[)2gjܙ%$\.p$/OҸIq[-K"|^643^Ύq pڴ1G3ֺ[7^$nmQ; O+x/ko'1 t5 OOՐ@64I2*Z0#fH$7o܇voiiA4T*===awwn"° d,Ѥ#ygBu‹^I3_~:/׹\H޾ׯ=4gJ_*BF ^x Yc P8fq V[y oT<7@ yܪRܫ*( "!nJb5D"X "1ij{vq ]$qOϝ;w-BVsfzիWmq QԲGdm=qլ_o߾s-Jf+΅ ;~kJ2*YJѠ}UԬlc Tp칳bfM}|ҴxÆOd 23LXtQ9WR8\Vvďa4Ѯ@ IOC#ee}6 vZimmt,R4@eYw7{Z~S .\v9b?wrGaI\qN>%"=n;џcUl_غOW}OEĺqk uD'׏4ٷxKbkb7s~SD!LXgY3!(B'ׯ/gY_hCkN?5ƠA#f]4֨Y3z&?[#P$ uDh#"772:gn|FG<^˲|>_$7(pɞOύk>DU;[ HC Gz4j4~.jii&nS[˭kxpOSN_e*ʊbK"˲:}NlNK&LdEDD&5ƾ[Bhd,񖊲"ڳg] **Nr*8Phx4e>?^7mݺο*- =/Vv96Y3bY@HIICx<"G Z=,M rcDՈ44ޭ!&e ; yqD'9}7Ao_o׿`bYvdcvT[=}?˖r/}x}lݖ7}iGTneC/ɌhQi[Ԭ:Akg7GU*c<y,e<~ s5"?H|* lVԴv釉j{{5-v攱֧D3ea*Դf"rD6 L.}&⋓KӧQC=בSNKsEDdfŖԱf9K+BU81=}|4-Ԥ.NBSyEkYx.]: G:W@"V5%jHչOc0ILO7L(1m5O-|S5G UfNsk7"F /NSŬY&>>>((ȎFDDk²lΟ0haV͐_o¬t vcŰ},5n6-Y1aODR3!($'ɐNQk[{@S3F(Q`…ۉfgĉpZ*ȭyavD &8@~tH~yb"^ӄ1D2"zH@cQ RqA3ѝ;w4:=0:r/]AD-8''׮]ȰKH^(22l͕wUp ! ^S^JIJlȯ~:FEIJԹac"-FeiKZ`@,a5-] +`2|7Dz[*T*ڬޕGdn AsZ D"Wݸ3dx םwK :(w3&|sJe19QХk7o߭kϘ7U,dj~ȑ#=X,f=""ʈH(x( IS aaaD/pk & T*RJJc=ە " 酌q"dTHjTFw$)$Vhܽ{w>DŒgLԨ:vjW3jy/zrܴH(zyy|k}__ߡCzlTSSz߈w 0 hqi4 0vN;wX0o54j dEBs.j`@G$9\gZnin|cDtz6Ek7FϜ2v}L)--UT72c .9rdAKR}w$ !C444ܼy͍t3oKK q]۷o߻w M vK?ɨauV.H&# [{nJX"bE(}}Z3Ę_IB"5|Q9\ޫVjիWY M!sv]w{hXr6P-O<== 0 jZ,VUU544h4ne$kV p L&#vX5h+۽b3L{5ݭknVƣǭKG===fC>ZEv"F?j@yM]-ijWޒ7MsT*BP(JZ?~mc-?Sii) P(D"X,ܭ}<,Yb܂BaSSӽ{Ԕs !Я*\.'"???__HR05w򅖖fOH$OO &ٱM!kڭWP(|D1DRЬlI*kUB,Ko쾴FͶ+bРOC,CKʅȪP(j˗e^y'O@ H$Δ$~iȑ)))VF. t=FÎ+g}}}}}}EE*J.\</}偁Ύ%|W.y rcE"g KRj)@=%⺿ @ Јe'/DmWk_)n7U*E"gTP1E"w.uLrZkkkaaXm4Z-Ú#r` +Z[[ K7mļ>-"H"p boLK˽{%y5-&HB> 4{F(](VaZSD Un,[Y/⋕K$O1^"IDATO!Cxzz1G5$ܸ 99\*箹@ ˲?#[k~U?cYbY$k^U}c}O__թ(fKucYp{_mv 8j(B˟ѝa遍g{^fxXX w5DM~hQ5]Ma i؝ejgAOpeh-l3?I!x<ƲGXqܘ59D99LC׉=m_|K999D3!tG#=r;P<)e 8w!SՍΎhQjν0d ,nʹ*EyI /,nil$Fs*8EPKcc#}9q64ʖ%%E\~0v!"z`]I:"]piWɮ0nGҪwdWU|h=0KQ߾?/8?h+yzzpp*xsiͧ;,Mp@d0#_=Y! IE,K1OP3""J(5^WSDHH*acCO~;aXF莝wi@q.,1QQd84{+xs)LDDWItw{4 ^A#ZW|`*]Oi.>T%*2SuxDV%0U?S0Qs~ZnO uLtj(A;>2gV%$gK;N.m@]0x>-cnN2Wv}2*jn!r4Hຐ  .&1Ǐ'nqS_[ƞ'Én}1lPg;>Y8ctrTh7@+iQT"vXeY}~{_\Zve028S-v}Ҥ0ر]/S07ϐ]S/ DDUeRr`e n!W<<ջN??v3=ǛOǛO. (dK QBp>Ȫg&,K,sM )Ǚ=2cs`Дa^N,ݹS%wx0C$7n8;"E"ck*4eF3<=دI-pCГK#f.}D4nqFM/ԩ'Xqzad]8_Ǜ;yܑtlEwf}lwMfY\:G{a=8ãhw}0L~d d 6:č/vN2A0믿^hQttcp _۴u1 Rx g}6==}}u4P@1EbuAٵkF4)?BѦRKDnnqq?{ BX۹4p(x w~''33֭['˻1 ``:|ӯ\H<çLX?L ˩vvGem/GWC#rv.)E*0 tz ~*g_!gM@_(;}{Η܎ٽ``A*_wڜ:O%11cbbd6ČIQ,& xZ' FaS{_=8GNߢ7?,IDta!E?{ܥvsǓWnnXXZ<,˲,0DtONNmfHYjF>":H*NKO* -# C-$_O5E۳p5I*xB 8@{ޟADT5mRaZv^µ[YJ$%J4iȺfBoqŎ=m{g^hߔd>-;ϊyvkfƩq_<,m<^DtUʲ,31 $BpԨQo3΃n|eH`Ǐ""`ؠKύ4嗉w?d-te%7#HH"6d'\v3ti˜R#,+Xl;H$˟嶙sy%Y?M2M&e+?*W2>fB7 `PҢ]DDr./JyM(}<#iQ6++ Oe\ OJؓo)i%kMgܬpcjQk<#{wyFҢ}~=)N%"*w*+2*VUǭ0oZ[[[ZZOl_:;;{D.! uZFe '?1bʫj"R*^nIMؽ=;F`,#]UFqv<%*vVTK盺oZ啳H7J?Ws^O:c3rNt. >f{\VqqnKw`ADz{|1R&+.uS1[4uŮȾSqϞF/Z}GM`ȑ<+. p,+?eoذa)!>Fm+Ȳw.2{]Ʋn<@@`fnA/w?GD^Wv-xzco%G^ ƒZ.`K܇S{SfzcѾ\f]wtj5rJ hƔUQTuPy;|2Wdk?.-QG-Zrj1|pyI;.3|%N ^JWT߸jf'%ӳ0_(=S>W, .Z>*] SV0zyK"{?<}E]ݑuk_Jc̵6胎z˯H]7l1'lkq޼;e݃f/|aJ%VH$?8˲዆#)5B!1)?.PeѥK+o7s@ :ujKK 800pر#GLKKsT0kr4#x\,?E)%#i)1:;+;?>M {>.:JphbWgNt%9jg&ga=޶'.K,ϝg1qpWJhieۯ<p*&U:mneGq=+K8'S'h+Dc#L{lo_h8Yw))tr/\Y-z⡪DBPV3 zjuܔJeEEŰl)TUUR{>ׇ0{y"?h4Nzg=<<Ə4yd/FE^RA]*g|v6mcZ\]D)D&2 j΍rͥ#Fz\ z; 3W?K3^۳)[5r tkWFJtSM:d)_I#gWvK_H^39|MH;d`m:7U/;MkT_>w}`:m16}}?l:EDtW>7ןC.wQ֭[C>k/.N]b??ݽ\t5WSJoH%%%o`p=/֯_5fan߾]^^ާmkTo$2{p3S!ZY}y%YhG6N6WX%hZ+gS+c)mFLI#ZlSuyUi FW}?;1*MѢԤ/G19[[[MUbT㹻:tƍC4"wfYۈ"5/@f(3b847+;.h43gqz[Jϟ׏s`YgMOOnqR>~x)I\^?2θ6j&l׿XTTd xŋ>>>/^nkk?P 3Tξ:Y@o'S7qĒR|>С.]ZdL-t3=d;zA U/ǰ x0 Տ DGG6ƜX_1+ډLQCٳ׮]SO;ӳrƍ[lDRّ4`wFy 5kF_0n|Qb_8+/׭;qȑDO?U0 W(ↃrF=Q {@4<~s;DJ*Ϧz?-Hݶ`+1K#FqFQQш#[zs.)|kkی3Z[[[zs.bgp}a*}(Szy6>}zSS{0,,,LLL|[LW̮kc@{bpkkL~g_4W`ZsPYoAp+_zuӫdԨ5׶e>\'1ӯbez%mK$` _Lchjj*.ԝ0L[͛o߾]QQE7,M?L[XaW>P/ܽn}5 #d]hw+im҃׀_iNGb#H[L_FZz[z/e=0 cߕ{+cS V6=KӃ {}7Mg ՚:؃c qzjwwnƎo.݃J !!!>>>V꺚K~_1te?O6R*oHw/E[ط7+ٛR/EٗNRj֦---W\yG!H$(ZӾN͚֬\颎ݿ|;jX;+ð /Xk5n/_ڬ?G;V3mO},RX Kf\MeGU٫K]2 mNh/}i8]"GLt/ӦMtRKKK5]TPӦM;wn}}}}}jNutchg=DkrԜqP#N u( fDY=.jڱ-]#Y9f׶g-t]\W5iھTߡot.7k~\!Z;2`J{OZS/^BCC***֭[}6.J===W\O: |F\MhZ..C]G+`,w5O ^m%NїCnϞ=}QKK\.~OT*ui@'V&r?Ug IENDB`fritzing-app-0.9.3b/help/images/tools1.png000066400000000000000000000504461272363601300204370ustar00rootroot00000000000000PNG  IHDR\bKGD pHYsodPIDATx{xՙߑKBIc$$V !A5 1 (1 oK jnmjJq^q\Н֤M6u1*iKEH|殱-iy$3gyϙs3gf>::JA@dL0 "FFD 믿vZA`8/k׮ַxbz ^i /´t^DIWhfiFp$PBH\OP@!$%/Ǐ?c`c.\d Pn^:> MӬrIËIj?-O@g*n(&%?P;e[ *(!,l0ݖ>2=S 0b_:J6+ƑR(E7渑5LJ/f9Qp:B 5[ EJHI|VhD"l[᥁Aоԃ"yDvWۘP<6|5J xyw-pdh=H $r yF+j]KΣR 8!??Db*֯D}%S&qD !ϫC\$@#(bsr;P+D{%] Hzxƭ'ے>QOn0<dd>H L@wY10'N;zr^v… %"QJpF^=u1Q$0BE`u7#IS2_2^5CC4BXz\c>^rz| L0P*빞N.?4]ITŤ]6sۍW64Cx$؁o]H\B^ xu; q݄)ڰB`Xhfe +|*oTcC{[Y35t!nu ?%,XB|dU6?dAj_B[(]w]9v{FzZFXj#ѱ|ו#h')]7g T(LBF0Sb"*}[C;}e芒 ?kDr KѢU)S/*j Cq 3,r (#n'=:1 Dal "@jS81qŔm)z,:kV[ش8ۊlZ<w_w/ꍻuORD$AZ+VTPR1N\O!QށF@K\P5weW2:;^ f@unr+zvm|w? ?Kxu_L@Sۓ$5FIt$Fpmyjʛ&uad5/P'a-D3*4vې ~Sh Mb4duz:Yeڬ6rW9+ ,|=<`Šbj6B~{| umĦ74|k|/ ;qr],; Ȣ֞  Ub윿{wn~ks{?wRԝ@"ɁbB:Jrm|ںF@ɕ_5'"Zq&Q\(6 F` dAePqkk-JW^) 26Za"43aȸ-#S1Y( T~J)%HB `qB\AAKDթ9qAS&%[Q" ig$rYAe"7/٦+)E]TZZ*,Ea qu Ӛ\(θ'FT[4zTbwM&b'v Q7P6ZO[Rq&ڡ~ۅ 1o<_X$dh1v1 ôkH>ٳg6AsUڻw#D6lذbŊO>DnTjXP AD( "BA`^ l@Q@Dv$( tEA.( lh ԓ@d AD('%a$)bKS&h&$5 h&ywIWФ7Ru%D3tNXXS]^U4݇?@VSE]'ISS?v`dI(3Shk#ۢ \:0,/rUGr~M\DpI*HUW gQ \z2Q.ב;NOE,.O#ۜd},T5Iq rE s5v$뵫]x2t" YCڡ2yn03_Y] fm⧯Po _!/.2zziEMcU hA)PTag7: Kp;HJWK6f.2_3VKDVV Eq$I bSH 4]qw`h&[66l(((~饗 ͛7gggsAwmM3_J{tl&J%kinT!ɀ ^7'|Aԗ3"2}$vT_܊ aM4Af'ɽpc SO'6јy25 3'Nlt%6gW ){s`xx_'$k2g7N+ ,(D# "pAQ(}!jZh|@PC1QRZNhG@فD=]|@;+ۙ|}bhF AtbZӭVk2d2퓧ϮXDm~e;4],хjX,^{bI. L)fXT}kk9QPLDlHZm h/LVj]jUff֭[ ROSyjpJX7o^ztPA2GDDyyxezSdE|R py =߱h$D"ѱ1_H$\$h5z Qŋn͡-[34!P'QngP%ǜ qYkg%M8vDi3 H*$I Çi Cy.cN<) ЬŋO=GɣO)B;v?^9<ɺ@Br A-`ttl||#t/3cAf 4*B$BGƣDaIT^A͉b $ul! QR#Ţ@/%ulY}, ױ\:6JN/Jʶ|2Q_ܚױzXH"eOTױTَM#WFZA[ǡloulBkj&|l(bHf {<k( 3( )aW"#:4ױ) :r)a8G"HD1v S$I"(؄FW$mN=4dgg>}ϷX, c;wN$B3: MgϞͺ0+!9*?`G$ullcҥӟN:-¬.(Q*фB/^lٲtY~xxx9J(Aԟ4B:6<6gADN4m@f^Fےtγl()l.wن% Kcb)+ʻ`m꩙sp\[sϏ822bE.222-K:78z-k.H}M()ӯ~a)Diʤɴx,X`E.>={nL۵f?1CDA`OatY-=U1a <|YYYΝ3"D9996mK5a f7檬)&D 1bll,''a1<<366h\V0ī G_)rK< L3HbyiJP>ß]xaKr&^ϥsKfh0rzͫW^淬|b :PWL*3f "(Qr՗@T5f?J×%ėF7~YD;(&hSͷo4ƒ,^c]W-WkűJ-(iem Jm+am8W-(i|?7T/g--@ct` z_Uk/=:ɧ#?ߵ*@w6$HWt w#f﾿&x|]umM7B?= eJJUnW=sg7E/zzw/8Οs}g 2rvhhH ӽ ާV<mwp;:;ow) AMoo ؾ'?7ۻ3wٺ߈l_՝{7x󎉍I]-wy0f?:qj\{%Rut0@/ƽ 治^-Lc&TҲ|~wO0uÇ߇^bŊ_}af:{!H-Y_w=?E`d}?>̥+upǼ<,\ÂIMq!0Ç;giX.`k"6:.0EEmcewýƖ:-b=z+}pS=[3u&fx#!\diP,yٻ<ӳ>{ҽpH0HeaG=M%i]um_xV_lXsKb%c!w7J#ڐl ЯzKK{Sg9n9zR'y<2 C+ZSYȒη#<'!h譂 ~[vKI:JƤ&z!u:vBA)f3._|ݸuQ9{\vQl|~ɿ/r8 yX룹;Ve)ʄرcѣG6$i|_m[i~%R5e˖m߾4i^o~'l6w46>Vbx23PA28CO8*b&XE߼X}MsQx~_ %M\qouxK17Adv|Oz"Bu?dZ d DMsOVڙ&a@I'd[ALsRiԗg7{A?ʞB|ɽjAcQh?|@drH&-PAf!=AS@Dz 9Az F G'kG'NvBՉl֯];O>it "E)$. !"-͚fB>)F؍6AP($pZx1fӬʱj}1Wzn;.=$@3Ym6!GdSX4-=ݖfJ՝ETڼSSFur(ŕ9SS"dObXՒnKOlVŒݱ.^:T0$JP䠨6->[ ݾ2E_w'1߀[ȭʕ)/,>%Z emw@[[PoE4s}Vi?(DE@f $!/1_X@3`_^=EYyi.!$@TDyĜtqrNR oZLn0 Y~ \nh9qߡd2h&kG?@#/5& Vv%1 8 \'nJ d͓Ӆޒ4 ",*Xc4AK>Eѧ"5Fn屇}G}K_*駟-Iə>l޼~-m۶!O>dvv6hDDlR%Ap!jAxDApNA( @Q@D "P AD( "EA( @Q@D "P AD( "bޣO1duaezhC&l5 ͿhCG' }rh+,>" )`4Y$ a ;0;wnW#)PTW$Ids$IF 힟b]0dN+0DG!Rj (cn x j*$kw# HJx饗f*tq8sD<Ka6p({u Ô3)nI4[W!y!|OoE$xػɹQRPp *s ,F-^r\^&)ө?/UW[MՑ$+&jEk6*C J*󹨫}2ә;ws羰a"ԅ_J(=1KQ^.8CU(qr:DlT"qT2`uA1<ŖDah\3.[Mʒ&i2\d ׅ+b* Q O4*!2$v I:3qA mL>6dha&L D օ6A-^BĬ[җ b~fAhDDz @Q@DN4""=AD( "EA( @Q@D "P AD( "EA( ^0 > aᑑ1HKKHR+ٙ ?h&ci3^zK.9g]xŢ OR\8Em='B_[MdIkCbK)Nԕag|sW_w XyUW͝3]Qs}9!ϾPgvl=ON`_b>(|&z2Q5.4sBJKW^zSŹNz?|oy}|0'3S#@s#TPpB+7Ν;| GG[Uq 4wF?S6jgNjNB19R;|\bEӏEw(o./nuP䠨6AIK('[BPMRN[% \^/gXn3U^lA67kc4jA\ X?+Wh_jmQpyk\wz\.lv;oY?;Y8.1{j qd-^kޤСpmS9 m(= u_[JTEydBZM#mt%;fBݝPTas 9mumԾ wwe;ݞVCld/Phl|.zWeXKwͣmS_xx\k<.p=(wQz-3#mNFzZm9E4e I.Z6gQ/4/4WscD!BQ\tY sr?EN+PN_%!}F՝E5&B7u;\e 3T5Iqvo^600𳗏7<2s23#Qv+OFZ9s2d0pyskrBؿxαd FkD~A^8 |29}Ho8oRrf%ϼcGW)lں-$huf;q>߯l% 2c^+O:e4TP=Z}WNf`~6=>6yiiiA?oVv6d_|vU[YTO1f{;$IE@ D9pwgPo!8*R J%?'Ԋ ,*;kEԍƹ=|xͥ+0s~!FP>bQA\0ąqqg##x$;wfvl.E|>wh7/r;Âb`TՔeSAV̌GynqiGq.M)sud"`0j9JrA{T]7wt> T}^m5U[9z9{oNf]_+=f-|}Ao{ege)900 TwAڵW }2gliq )sl=;n'b!&Acc#ACCC{U;ve6}-a\ve,[h"8|{ァG<یf7 'ؼyTaÆm۶!O>dvvlSmNj鄉cX=3:: ii-7̎0OjN>E~;'q3gΜ9sRSe7E0tfcBAD( "EA( @Q@D "P0)x G). AD( "EA( @Q@D "P AD( "_ܚ_=z AfYYXS#BbZ3p]q 6i|t'R#* R߮P ADvQτ9S&c20Sp(Ig&K(Jg'P)j(.nʼnZND)oMIq$ jk7@Q>Dhl AZUP tiB ł>)Uv+xcJT8L1 j$YG ?bkatL=:(s1QKB7uEHqt\,0  Gաh"PLDK1#W43/)s]"n~O-!> nc){/) ;zݹzaL%=v> Fa7RAp;w1{./7%b/-0{|3#;ЎnK()q(8td)3} \^>!pQ> jb 3 Fc}^JIghpy>>s.imyb2x>^PCҦ|>#HrH(UePq,@)Q$~o8€D B悌b.%tT9*غ5="%Ih#D$9-؍IMʒ&i2npGD(0 C,UBcu 3Nkēv>0r<;G6,ZDs )I& Hb "P AD( "EA( @Q@D "P ADpG"‚ "b PÇ6ɟmA % %ᇝ9&f6FH5Ojb&bcK.D_>e$% 9$kmm/ d)ggQ'Y9@ij2DsS2t!vU⋇-5쯭,jܴEU@I&7oN.Ė9@E /!_|g:Πv@WΠХ}T u !R[OV$IWL!1AV8ne?H>cQ.j: '588 0 144w^c>\h86o|hA{/lذ{۶mlȓO>m I3 DFAxD˜6AGcCDy A"OuAf!S@D} : 8|@@ 8|@D "P AD( "EA( M= GFFRuFFFffԈcyfMwRDu] ,H}={tJgfLn޴% ß}iӦsΥ>M6ՙ<3frm`9aCr3yfMk \20wӚgfLn޴-N4]3m&7̶em3yf-ŘkN%\S2_z/zgN 9 5rJbV̚\%Ѽ,5-U'`֙PL' 3.f}09D͋ò|?WCGK]r/{'_iI4y偫Z wWqghyZ˗%˶crQ}%Ys73 XazKJqA^e|s5l ^Mn{ 2 YU]w|oTWL&Ӵhު|o&&YW-ŘnNa|||hhq/83wu7Wg]}ߦ??~sR8iwy0O-l>lKʿfB$6#V|soKF"8;Tf\}æ^+t'nT-ܷ4 Ӊ4mEhro8z}xE}8|ػi%˯:BtMbK#K,WӇߝd aM>71px&ex+ w? 1mRUwïoTgJ8Q`A@OA ( @Q@Dv$( tEA( AD( ""@hDS@D "D#AS@D "P ADc0Xi /Zt u቏ӑbh1i& _cBP&txg{L3QH1}:c~nXd$1(w;wN#ܹsV"# E!qy@ga!x!>?mt,r_Ḱh&ZI >ԨVG0L$JUܵU5#f^%a#Q^sy)j,|4A5^=zN%Ƞ0}=r\atj(7㷖Qaw4_} ƣPfF( K',R}08s/H.,YDaM0ќB3I5;՝E5j`?Ô@Y4v<1ȴ[JKs񡵜(nWUnOJ Ov6;I_K@3ْ7 6gQ mw@[[ܾj*nU wy< . HY>850/ԩOͷǮѾ.t0IH. @n.yJwT c=ao=Y[/)=GZ[Z_q=!(W\ xckۉJʶȯlgJű|$S&^ ]m2j9|YT4ؗWOQd]#nK+T];}#50Qѳrrph;?8,$ ΧЩSbj$b8Q` m{*jI?+tp-l^Εg`{>v#u(6$U x].Ί" q 6 77EWA@zN6: #9GG!tҵcj2 N EӉzڡbgU]keSISITqu|M= 0MEAGTatt$=-[r>$5 B(Yjc u|]d@{ spj(a6@~e{4J*+ۙJvop*#= v%OϏ 2vcܹS)l~0l_t=zO>g^T*wA<>o^:觟͉MCЈ122&Lց砪bm} 5+H/oֻcd,-UT);ޜ%_?/IM{{As5)Evݓ\n :MJOFOEJJ[ot .XF:<{n4VCЈthNKQܖ+FIbgl\mOp+M8Q|rGKTKKGAQv>S*%[<^OM إjI\/S%Kb٣3Oáyl t4Ǫ$4 D ׅP0M€vF]])(GGcZϪ?S)F3܄r/ɺ>ޛ)F| |n?O1DS0'ڷ%fԤ|ӸvO A:" N#D"'uL,,2"TwL,|e,,2"a)A"h+9zD6&>p~"#3أF[ )f =ADWTA@2|@]@=AD "L>A "p "pA8ш > "'xQa?tGshĿ 2N޽RV52;a߽{w“Mۜw޽yW nvڕd$ HFAD( "EA( @Q@AA( "b2k_HDaNfYD3\k_@H)ɯh528ш A MA$4  > z 2Q>H4Y,2ME$D#Ml`S¼yA}PZ6nI~k_tt$|Cz,DvW$S҉3FZ.1^焪q$*aKIB Lq*IgԪo $PIl*B+jrLXa!IMG:j/YwO4E-_2 &T;mɒ9;Soa )N#dB8 ~D5&(p7x'Sf / v}ddhc1-)+IENDB`fritzing-app-0.9.3b/help/parts_editor_help.html000066400000000000000000000411161272363601300216320ustar00rootroot00000000000000 Parts Editor Help


We have decided to release the new Parts Editor in two phases. The first phase, included in release 0.7.9, is already easier to use  and more powerful than the old Parts Editor, but you still have to do a lot of preliminary work using an SVG editor like Inkscape, Illustrator, or CorelDRAW. In the next phase we hope to eliminate much of the need to use these programs.

How to start

The new approach is very different from that of the old Parts Editor, and explaining that difference is the purpose of this document. The first big change is that you cannot create a new part from scratch--you must begin with a part that already exists. So the best thing to do is to find a part that is pretty close to what you eventually want. If your part is really different from anything else, then just to try to match the number of connectors. For example, if your part needs 39 pins, then start with a generic IC part, change it to a SIP (single inline package) and give it 39 pins. Though finding a part to begin with may seem like a burden, it will save you effort as you go through the process.

You can open the new parts editor by right-clicking a part in a sketch and choosing the 'Edit' option; choosing 'Edit' from the Part menu; right clicking a part in the Parts Bin and choosing the 'Edit' option; or using the Parts Bin drop down menu.

Six views

The new Parts Editor no longer tries to display everything in a single view.  Instead, it works like the sketch window which has multiple tabs with only one tab visible at a time.  In the new Parts Editor there are six tabs: Breadboard view, Schematic view, PCB view, Icon view, Metadata view, and Connectors view. As you might expect, the first four views are for the part images; the Metadata view is where you enter the part's title, author, and other properties; and the Connectors view is for editing connector metadata, and for adding or removing connectors. For the latter, you just type in the number of connectors you want.

Loading new images


To load the SVGs for your part, switch to the appropriate view, and use File > Load image for view. As before, you can load SVGs in all views and gEDA .fp files and KICAD .mod files in PCB view only. To create an SVG for any view, you must use an external SVG editor (Inkscape, Illustrator, CorelDRAW, or a text editor) to layout the elements that will be used as connectors. The SVG example below uses <circle> elements for connectors.

Loading PNG or JPG images directly is possible, but since these become SVG images with only a single element, there is no way to place individual connectors. We also  discourage the use of PNG and JPG because these are raster-based rather than vector-based, so they don't look good when scaled. If you still prefer to use PNG or JPG, we recommend that for now you open them in an external SVG editor and add connector elements there (see below for more about 'connector elements'). Save the result as an SVG, and use that in the Parts Editor.  In phase 2 you will be able to add connector elements directly in the Parts Editor.

Since you have already begun with a part, it may be that you will only have to load a single image for a particular view--you do not have to replace all the images in the original part.

It is still necessary to prepare SVGs for PCB view by grouping elements in layers: copper0, silkscreen, copper1, etc. (Layering will be handled in the phase 2 parts editor.) The best way to understand the layers is to open up one of the core pcb svg files, For example have a look at the file crystal_hc49U.svg. Silkscreen layer has four while lines, the two copper layers share a pair of circles as connector elements.

<?xml version="1.0" encoding="UTF-8"?>
<svg baseProfile="tiny" height="0.20306in" version="1.2" viewBox="0 0 46684 20306" width="0.46684in" xmlns="http://www.w3.org/2000/svg">

<desc>Fritzing footprint SVG</desc>
<g id="silkscreen">
<line stroke="white" stroke-width="1000" x1="1000" x2="45684" y1="1000" y2="1000"/>
<line stroke="white" stroke-width="1000" x1="45684" x2="45684" y1="1000" y2="19306"/>
<line stroke="white" stroke-width="1000" x1="45684" x2="1000" y1="19306" y2="19306"/>
<line stroke="white" stroke-width="1000" x1="1000" x2="1000" y1="19306" y2="1000"/>
</g>
<g id="copper1"><g id="copper0">
<circle cx="13736" cy="10153" fill="none" id="connector0pin" r="2750" stroke="rgb(255, 191, 0)" stroke-width="2000"/>
<circle cx="32948" cy="10153" fill="none" id="connector1pin" r="2750" stroke="rgb(255, 191, 0)" stroke-width="2000"/>
</g></g>
</svg>

Sometimes you will want to reuse the breadboard image as your icon image. There is a shortcut for this under File > Reuse breadboard image. You can also find the options: File > Reuse schematic image, and File > Reuse PCB image.

For certain parts--like a breadboard--there is only one view image. For technical reasons, you have to tell the Parts Editor this is what you want.  Use View > Make only this view visible from the current view (one of Breadboard, Schematic, or PCB) to make the part invisible in the other two views.

Undo, save, show in folder

The next big difference from the old Parts Editor is that undo is always available, even after loading view images. Furthermore, you can save your changes at any point and keep working--the new Parts Editor is less like a dialog and more like a full document window.

If you start with a core part (i.e. a part from the Fritzing distro), saving from the Parts Editor is disabled. You must use Save as new part, because you cannot change a core part. Save as new part creates a new part in your local storage area (explained below) and this part is added to the "My Parts" Bin--it is a copy of the original part (plus whatever changes you have already made so far using the Parts Editor). If you are editing a custom part that was in a sketch, the part will be updated whenever you save. If you want the new part to display in the My Parts Bin the next time you run Fritzing, remember to save the bin (use the drop down menu at the upper left of the bin). If you start editing from a part in the My Parts Bin you have a choice between Save (which will overwrite the part), and Save As New Part (which will create a new part and add it to the My Parts Bin). If you copy a part into My Parts Bin from some other bin, then probably Save will be disabled.

On Windows the local storage folder is something like C:\Users\[username]\AppData\Roaming\Fritzing\parts\user\, and SVGs are stored in C:\Users\[username]\AppData\Roaming\Fritzing\parts\svg\user\. On Linux and Mac this would be ~/.config/Fritzing/parts/user/ and ~/.config/Fritzing/parts/svg/user/.

To find the SVG image for the current view on the desktop, choose File > Show in Folder. On Mac and Windows this will open a folder on your desktop with the SVG file selected. Under Linux you get the folder but no file selection (if anyone has advice about how to get selection to work, we would be grateful).

Associating a connector with its SVG counterpart


The next task will be to associate each connector with its counterpart in the SVG for each view. This is handled very differently from before. For an example, I am going to use images from a part created by Shunichi Yamamoto (who graciously gave us permission to include them in this tutorial, and who also helped beta test the new Parts Editor). 

The new part will have 39 pins, so we begin with a Generic IC part and drag it into an empty sketch. There we use the Inspector to change it a to SIP and give it 39 pins. Then we open that in the new Parts Editor with a right-click. 

starting up

Next we load the new breadboard image using File > Load image for view.  In the Tools widget at the upper right, you see a list of connectors. Choose one to make it the current or active connector. If it has already been associated with an SVG element, that element will display a marquee highlight. Since we are just starting out, we will choose pin1 which is not currently associated with any SVG element.

pin1

To create the actual association click the "Select graphic" button over in the Connectors widget. This basically puts you in a "mode" that lasts until the next time you mouse down. If you want to escape the mode, click anywhere outside the part or use the escape key. You can only make the association when you are in this mode (this keeps you from accidentally associating a connector to a graphic when you didn't intend to).

In the mode, move your mouse over the SVG. As you do so, different SVG elements will highlight. It is often the case that any given mouse location can refer to multiple SVG elements (or groups of elements). Use the shift-key plus the mouse wheel to highlight other elements above or beneath the currently highlighted one (in the z-order)--the mouse wheel without the shift key will pan or zoom as usual (depending on your preferences setting for wheel events). If you are already on the bottom layer then the next wheel "downward" will make the element flash white; a similar white flash will occur if you are already at the top layer and wheel "upward". In other words, the flash signifies you can't move any further in the z-order in that direction.

Here are three highlights from the same mouse position, using the mouse wheel + shift:

highlight1
highlight2
highlight3

The first highlight is the one we want so we mouse down when the wheel gets us back to that small rectangle. Now the element has a marquee to show that it is the current pin, and you also see the terminal point as a crosshair which defaults to the center. The terminal point is the place where a wire attaches to the connector.

assoc1

After associating pin2 and pin3 we have:

pin3

Adjusting terminal points

In PCB and Breadboard view the terminal point is usually the center of the connector so you don't have to do any thing else.  But in schematic view the terminal point is usually near the end of the connector. So let's go to schematic view.  We start with the original SVG image. Note how the connectors are already showing their anchor points. Pin3 is the current connector.

schematic

Now we load the new schematic image and start associating pins.

schematic2

Here is what the Connectors widget looks like with pin3 as the current connector:

tools1

Notice that since we have selected graphics for three of the connectors, those connectors are checked in the connector list.

You can use the buttons or spinners to adjust the terminal point. Here is a close up after hitting the W button, and clicking the X spinner twice (look at the pin marked 'AN2'):

schematic3

You can also drag the terminal point directly by mousing down on it when its SVG element is highlighted.

Setting internal connections


An internal connection is when a two or more connectors in a part are already connected, for example the GND connectors on the Arduino UNO. You can specify and edit internal connections by clicking on the "Set internal connections" checkbox in the Connectors widget--it's beneath the connectors list (you can see it in the image of the Connectors widget, above). Since we are talking about the Arduino UNO, here is what it looks like when Set internal connections is checked:

bus1

The green lines represent the internal connections. To remove an internal connection, right-click the line and choose "Remove internal connection". To add a new connection, just drag a line out from one connector to another.

Known limitations

The following feature are due in phase two:
  • adding holes
  • mixing THT and SMD connectors--for now you must start with an SMD part to create a new SMD part, and start with a through-hole part to create a new through-hole part.
  • control over SVG element layering
  • direct manipulation of SVG elements
  • setting up parts with bendable legs.

Bye for now

That's pretty much the phase one story. We hope you like what you've seen. Stay tuned for phase two.


fritzing-app-0.9.3b/install_fritzing.sh000066400000000000000000000073421272363601300202260ustar00rootroot00000000000000#!/bin/sh # # this is a rough beginning of a linux install script for fritzing # sets up document icons and file associations using mime types APPDIR=$(dirname "$0") # check if user .mime.types file exists, otherwise create it if [ ! -f ~/.mime.types ] then echo "creating user mime.types file" touch ~/.mime.types fi # add mime types for fritzing file formats grep -q application/x-fritzing ~/.mime.types if [ $? -eq 0 ] then echo "fritzing mime types already registered" else echo "application/x-fritzing-fz fritzing" >> ~/.mime.types echo "application/x-fritzing-fzz fritzing" >> ~/.mime.types echo "application/x-fritzing-fzp fritzing" >> ~/.mime.types echo "application/x-fritzing-fzpz fritzing" >> ~/.mime.types echo "application/x-fritzing-fzb fritzing" >> ~/.mime.types echo "application/x-fritzing-fzbz fritzing" >> ~/.mime.types echo "application/x-fritzing-fzm fritzing" >> ~/.mime.types fi cd $APPDIR # install fritzing into mime user directory xdg-mime install --mode user 'icons/x-fritzing-fz.xml' xdg-mime install --mode user 'icons/x-fritzing-fzz.xml' xdg-mime install --mode user 'icons/x-fritzing-fzp.xml' xdg-mime install --mode user 'icons/x-fritzing-fzpz.xml' xdg-mime install --mode user 'icons/x-fritzing-fzb.xml' xdg-mime install --mode user 'icons/x-fritzing-fzbz.xml' xdg-mime install --mode user 'icons/x-fritzing-fzm.xml' # set the default application to fritzing.desktop xdg-mime default 'fritzing.desktop' application/x-fritzing-fz xdg-mime default 'fritzing.desktop' application/x-fritzing-fzz xdg-mime default 'fritzing.desktop' application/x-fritzing-fzp xdg-mime default 'fritzing.desktop' application/x-fritzing-fzpz xdg-mime default 'fritzing.desktop' application/x-fritzing-fzb xdg-mime default 'fritzing.desktop' application/x-fritzing-fzbz xdg-mime default 'fritzing.desktop' application/x-fritzing-fzm # install image-files into user mime system with specified size # ~/.local/share/icons/hicolor/*size* xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fz_icon128.png' application-x-fritzing-fz xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fz_icon256.png' application-x-fritzing-fz xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fzz_icon128.png' application-x-fritzing-fzz xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fzz_icon256.png' application-x-fritzing-fzz xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fzp_icon128.png' application-x-fritzing-fzp xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fzp_icon256.png' application-x-fritzing-fzp xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fzpz_icon128.png' application-x-fritzing-fzpz xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fzpz_icon256.png' application-x-fritzing-fzpz xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fzb_icon128.png' application-x-fritzing-fzb xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fzb_icon256.png' application-x-fritzing-fzb xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fzbz_icon128.png' application-x-fritzing-fzbz xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fzbz_icon256.png' application-x-fritzing-fzbz xdg-icon-resource install --mode user --context mimetypes --size 128 'icons/fzm_icon128.png' application-x-fritzing-fzm xdg-icon-resource install --mode user --context mimetypes --size 256 'icons/fzm_icon256.png' application-x-fritzing-fzm # update user databases update-desktop-database ~/.local/share/applications update-mime-database ~/.local/share/mime echo "installed fritzing system icons" fritzing-app-0.9.3b/phoenix.pro000066400000000000000000000172341272363601300165050ustar00rootroot00000000000000# /******************************************************************* # # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # # ******************************************************************** # # $Revision: 6960 $: # $Author: irascibl@gmail.com $: # $Date: 2013-04-10 12:15:14 +0200 (Mi, 10. Apr 2013) $ # #********************************************************************/ # Fritzing requires two Qt-provided plugins in order to run correctly, # however the QTPLUGIN syntax only seems to work if Qt is built statically, # so QTPLUGIN is included here only for information purposes: # # QTPLUGIN += qjpeg qsqlite CONFIG += debug_and_release win32 { # release build using msvc 2010 needs to use Multi-threaded (/MT) for the code generation/runtime library option # release build using msvc 2010 needs to add msvcrt.lib;%(IgnoreSpecificDefaultLibraries) to the linker/no default libraries option CONFIG -= embed_manifest_exe INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtZlib DEFINES += _CRT_SECURE_NO_DEPRECATE DEFINES += _WINDOWS RELEASE_SCRIPT = $$(RELEASE_SCRIPT) # environment variable set from release script message("target arch: $${QMAKE_TARGET.arch}") contains(QMAKE_TARGET.arch, x86_64) { RELDIR = ../release64 DEBDIR = ../debug64 DEFINES += WIN64 } !contains(QMAKE_TARGET.arch, x86_64) { RELDIR = ../release32 DEBDIR = ../debug32 } Release:DESTDIR = $${RELDIR} Release:OBJECTS_DIR = $${RELDIR} Release:MOC_DIR = $${RELDIR} Release:RCC_DIR = $${RELDIR} Release:UI_DIR = $${RELDIR} Debug:DESTDIR = $${DEBDIR} Debug:OBJECTS_DIR = $${DEBDIR} Debug:MOC_DIR = $${DEBDIR} Debug:RCC_DIR = $${DEBDIR} Debug:UI_DIR = $${DEBDIR} } macx { RELDIR = ../release64 DEBDIR = ../debug64 Release:DESTDIR = $${RELDIR} Release:OBJECTS_DIR = $${RELDIR} Release:MOC_DIR = $${RELDIR} Release:RCC_DIR = $${RELDIR} Release:UI_DIR = $${RELDIR} Debug:DESTDIR = $${DEBDIR} Debug:OBJECTS_DIR = $${DEBDIR} Debug:MOC_DIR = $${DEBDIR} Debug:RCC_DIR = $${DEBDIR} Debug:UI_DIR = $${DEBDIR} QMAKE_MAC_SDK = macosx10.11 # uncomment/adapt for your version of OSX CONFIG += x86_64 # x86 ppc QMAKE_INFO_PLIST = FritzingInfo.plist #DEFINES += QT_NO_DEBUG # uncomment this for xcode LIBS += -lz LIBS += /usr/lib/libz.dylib LIBS += /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation LIBS += /System/Library/Frameworks/Carbon.framework/Carbon LIBS += /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit } unix { !macx { # unix is defined on mac HARDWARE_PLATFORM = $$system(uname -m) contains( HARDWARE_PLATFORM, x86_64 ) { DEFINES += LINUX_64 } else { DEFINES += LINUX_32 } LIBS += -lz } isEmpty(PREFIX) { PREFIX = /usr } BINDIR = $$PREFIX/bin DATADIR = $$PREFIX/share PKGDATADIR = $$DATADIR/fritzing DEFINES += DATADIR=\\\"$$DATADIR\\\" PKGDATADIR=\\\"$$PKGDATADIR\\\" target.path =$$BINDIR desktop.path = $$DATADIR/applications desktop.files += fritzing.desktop manpage.path = $$DATADIR/man/man1 manpage.files += Fritzing.1 icon.path = $$DATADIR/icons icon.extra = install -D -m 0644 $$PWD/resources/images/fritzing_icon.png $(INSTALL_ROOT)$$DATADIR/icons/fritzing.png parts.path = $$PKGDATADIR parts.files += parts help.path = $$PKGDATADIR help.files += help sketches.path = $$PKGDATADIR sketches.files += sketches bins.path = $$PKGDATADIR bins.files += bins translations.path = $$PKGDATADIR/translations translations.extra = find $$PWD/translations -name "*.qm" -size +128c -exec cp -pr {} $(INSTALL_ROOT)$$PKGDATADIR/translations \\; syntax.path = $$PKGDATADIR/translations/syntax syntax.files += translations/syntax/*.xml INSTALLS += target desktop manpage icon parts sketches bins translations syntax help } ICON = resources/system_icons/macosx/fritzing_icon.icns macx { FILE_ICONS.files = resources/system_icons/macosx/mac_fz_icon.icns resources/system_icons/macosx/mac_fzz_icon.icns resources/system_icons/macosx/mac_fzb_icon.icns resources/system_icons/macosx/mac_fzp_icon.icns resources/system_icons/macosx/mac_fzm_icon.icns resources/system_icons/macosx/mac_fzpz_icon.icns FILE_ICONS.path = Contents/Resources QMAKE_BUNDLE_DATA += FILE_ICONS } QT += core gui svg xml network sql # opengl greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets printsupport concurrent serialport } else { include($$QTSERIALPORT_PROJECT_ROOT/src/serialport/qt4support/serialport.prf) } RC_FILE = fritzing.rc RESOURCES += phoenixresources.qrc # Fritzing is using libgit2 since version 0.9.3 LIBGIT2INCLUDE = ../libgit2/include exists($$LIBGIT2INCLUDE/git2.h) { message("found libgit2 include path at $$LIBGIT2INCLUDE") } else { message("Fritzing requires libgit2") message("Build it from the repo at https://github.com/libgit2") message("See https://github.com/fritzing/fritzing-app/wiki for details.") error("libgit2 include path not found in $$LIBGIT2INCLUDE") } INCLUDEPATH += $$LIBGIT2INCLUDE win32 { contains(QMAKE_TARGET.arch, x86_64) { LIBGIT2LIB = ../libgit2/build64 } else { LIBGIT2LIB = ../libgit2/build32 } exists($$LIBGIT2LIB/git2.lib) { message("found libgit2 library in $$LIBGIT2LIB") } else { error("libgit2 library not found in $$LIBGIT2LIB") } } unix { LIBGIT2LIB = ../libgit2/build macx { exists($$LIBGIT2LIB/libgit2.dylib) { message("found libgit2 library in $$LIBGIT2LIB") } else { error("libgit2 library not found in $$LIBGIT2LIB") } } !macx { exists($$LIBGIT2LIB/libgit2.so) { message("found libgit2 library in $$LIBGIT2LIB") } else { error("libgit2 library not found in $$LIBGIT2LIB") } } } LIBS += -L$$LIBGIT2LIB -lgit2 include(pri/kitchensink.pri) include(pri/mainwindow.pri) include(pri/partsbinpalette.pri) include(pri/partseditor.pri) include(pri/referencemodel.pri) include(pri/svg.pri) include(pri/help.pri) include(pri/version.pri) include(pri/eagle.pri) include(pri/utils.pri) include(pri/dock.pri) include(pri/items.pri) include(pri/autoroute.pri) include(pri/dialogs.pri) include(pri/connectors.pri) include(pri/infoview.pri) include(pri/model.pri) include(pri/sketch.pri) include(pri/translations.pri) include(pri/program.pri) include(pri/qtsysteminfo.pri) !contains(DEFINES, QUAZIP_INSTALLED) { include(pri/quazip.pri) } contains(DEFINES, QUAZIP_INSTALLED) { INCLUDEPATH += /usr/include/quazip /usr/include/minizip LIBS += -lquazip -lminizip } TARGET = Fritzing TEMPLATE = app message("libs $$LIBS") fritzing-app-0.9.3b/phoenixresources.qrc000066400000000000000000000606611272363601300204270ustar00rootroot00000000000000 resources/zoomfactors.txt resources/ratsnestcolors.xml resources/properties.xml resources/vias.xml resources/images/splash/fhp_logo_small.png resources/images/splash/splash_progressbar.png resources/images/splash/fab_logo_bar.png resources/images/splash/fab_slice1.png resources/images/splash/fab_slice2.png resources/images/splash/fab_slice3.png resources/images/splash/fab_slice4.png resources/images/splash/fab_slice5.png resources/images/splash/fab_slice6.png resources/images/splash/splash_screen_start.png resources/images/splash/april1st.png resources/images/splash/splash.xml resources/images/AboutBoxLogoShield.png resources/images/aboutbox_CC.png resources/images/aboutbox_FHP.png resources/images/aboutbox_scrollfade.png resources/images/watermark_fritzing_outline.svg resources/images/welcome_kit.png resources/images/pcbs_2013.png resources/images/dot.png resources/images/fritzing_icon.png resources/images/fritzing_logo_background.png resources/images/breadboard_icon.png resources/images/schematic_icon.png resources/images/pcb_icon.png resources/images/icon_icon.png resources/images/remove_prop_enter.png resources/images/remove_prop_leave.png resources/images/helpImageBreadboard.png resources/images/helpImageSchematic.png resources/images/helpImagePCB.png resources/images/splitterHandle.png resources/images/part_lock.svg resources/images/part_sticky.svg resources/images/cursor/spot_face_cutter.bmp resources/images/cursor/spot_face_cutter_mask.bmp resources/images/cursor/curve.bmp resources/images/cursor/curve_mask.bmp resources/images/cursor/bendpoint.bmp resources/images/cursor/bendpoint_mask.bmp resources/images/cursor/bendleg.bmp resources/images/cursor/bendleg_mask.bmp resources/images/cursor/new_bendpoint.bmp resources/images/cursor/new_bendpoint_mask.bmp resources/images/cursor/magic_wand.bmp resources/images/cursor/magic_wand_mask.bmp resources/images/cursor/make_wire.bmp resources/images/cursor/make_wire_mask.bmp resources/images/cursor/part_move.bmp resources/images/cursor/part_move_mask.bmp resources/images/cursor/rubberband_move.bmp resources/images/cursor/rubberband_move_mask.bmp resources/images/cursor/rotate.bmp resources/images/cursor/rotate_mask.bmp resources/images/cursor/scale.bmp resources/images/cursor/scale_mask.bmp resources/images/icons/Toolbar_Icon_Order.png resources/images/icons/check.png resources/images/icons/nocheck.png resources/images/icons/TabWidgetCodeInactive_icon.png resources/images/icons/TabWidgetCodeActive_icon.png resources/images/icons/TabWidgetCodeHover_icon.png resources/images/icons/TabWidgetSchematicActive_icon.png resources/images/icons/TabWidgetSchematicInactive_icon.png resources/images/icons/TabWidgetSchematicHover_icon.png resources/images/icons/TabWidgetBreadboardActive_icon.png resources/images/icons/TabWidgetBreadboardInactive_icon.png resources/images/icons/TabWidgetBreadboardHover_icon.png resources/images/icons/TabWidgetWelcomeActive_icon.png resources/images/icons/TabWidgetWelcomeInactive_icon.png resources/images/icons/TabWidgetWelcomeHover_icon.png resources/images/icons/TabWidgetPcbActive_icon.png resources/images/icons/TabWidgetPcbInactive_icon.png resources/images/icons/TabWidgetPcbHover_icon.png resources/images/icons/toolbarExport_gbr_icon.png resources/images/icons/toolbarExport_gbx_icon.png resources/images/icons/toolbarExport_jpeg_icon.png resources/images/icons/toolbarExport_pdf_icon.png resources/images/icons/toolbarExport_png_icon.png resources/images/icons/toolbarExport_ps_icon.png resources/images/icons/toolbarOrderEnabled_icon.png resources/images/icons/toolbarOrderDisabled_icon.png resources/images/icons/toolbarOrderPressed_icon.png resources/images/icons/toolbarAutorouteEnabled_icon.png resources/images/icons/toolbarAutorouteDisabled_icon.png resources/images/icons/toolbarAutoroutePressed_icon.png resources/images/icons/toolbarActiveLayerEnabled_icon.png resources/images/icons/toolbarActiveLayerDisabled_icon.png resources/images/icons/toolbarActiveLayerPressed_icon.png resources/images/icons/toolbarActiveLayerBEnabled_icon.png resources/images/icons/toolbarActiveLayerBDisabled_icon.png resources/images/icons/toolbarActiveLayerBPressed_icon.png resources/images/icons/toolbarActiveLayerTEnabled_icon.png resources/images/icons/toolbarActiveLayerTDisabled_icon.png resources/images/icons/toolbarActiveLayerTPressed_icon.png resources/images/icons/toolbarViewFromBEnabled_icon.png resources/images/icons/toolbarViewFromBDisabled_icon.png resources/images/icons/toolbarViewFromBPressed_icon.png resources/images/icons/toolbarViewFromTEnabled_icon.png resources/images/icons/toolbarViewFromTDisabled_icon.png resources/images/icons/toolbarViewFromTPressed_icon.png resources/images/icons/toolbarDiyEnabled_icon.png resources/images/icons/toolbarDiyDisabled_icon.png resources/images/icons/toolbarDiyPressed_icon.png resources/images/icons/toolbarNotesEnabled_icon.png resources/images/icons/toolbarNotesDisabled_icon.png resources/images/icons/toolbarNotesPressed_icon.png resources/images/icons/toolbarRotateEnabled_icon.png resources/images/icons/toolbarRotateDisabled_icon.png resources/images/icons/toolbarRotatePressed_icon.png resources/images/icons/toolbarFlipEnabled_icon.png resources/images/icons/toolbarFlipDisabled_icon.png resources/images/icons/toolbarFlipPressed_icon.png resources/images/icons/toolbar_separator.png resources/images/icons/dockWidgetEmpty_icon.png resources/images/icons/dockWidgetCloseNormal_icon.png resources/images/icons/dockWidgetCloseHover_icon.png resources/images/icons/dockWidgetClosePressed_icon.png resources/images/icons/dockWidgetFloatNormal_icon.png resources/images/icons/dockWidgetFloatHover_icon.png resources/images/icons/dockWidgetFloatPressed_icon.png resources/images/icons/dockWidgetCollapseNormal_icon.png resources/images/icons/dockWidgetCollapseHover_icon.png resources/images/icons/dockWidgetCollapsePressed_icon.png resources/images/icons/noteResizeGrip.png resources/images/icons/combobox-arrow.png resources/images/icons/segmentedSwitcherXActiveDimmed.png resources/images/icons/segmentedSwitcherXActiveFocus.png resources/images/icons/segmentedSwitcherXActiveFocusHover.png resources/images/icons/segmentedSwitcherXInactiveDimmed.png resources/images/icons/segmentedSwitcherXInactiveFocus.png resources/images/icons/segmentedSwitcherXInactiveFocusHover.png resources/images/icons/WS-new-icon.png resources/images/icons/WS-open-icon.png resources/images/icons/WS-fzz-icon.png resources/images/icons/WS-blogLogo.png resources/images/icons/WS-galleryLogo.png resources/images/icons/WS-shopLogo.png resources/images/icons/WS-fabLogo.png resources/images/icons/arrowButtonUp.png resources/images/icons/arrowButtonDown.png resources/images/icons/arrowButtonLeft.png resources/images/icons/arrowButtonRight.png resources/images/icons/toolbarShareBG_icon.png resources/images/icons/toolbarSharePressed_icon.png resources/images/icons/toolbarShareDisabled_icon.png resources/images/icons/toolbarShareEnabled_icon.png resources/images/icons/zoomSliderMinus.png resources/images/icons/zoomSliderMinusPressed.png resources/images/icons/zoomSliderPlus.png resources/images/icons/zoomSliderPlusPressed.png resources/images/icons/searchField.png resources/images/icons/noicon.png resources/images/icons/parts_plural_v3_plur.png resources/images/icons/parts_plural_v3_sing.png resources/images/icons/asterisk.png resources/images/icons/partsBinCombinedMenu_icon.png resources/images/icons/partsBinCombinedMenuPressed_icon.png resources/images/icons/partsBinCombinedMenuHover_icon.png resources/images/icons/noArrow_icon.png resources/images/icons/partsEditorZoomInVerButton.png resources/images/icons/partsEditorZoomOutVerButton.png resources/images/icons/partsEditorZoomInHorButton.png resources/images/icons/partsEditorZoomOutHorButton.png resources/images/icons/fabquote_button_hover.png resources/images/icons/fabquote_button_press.png resources/images/icons/fabquote_button_release.png resources/parts/core/wire.fzp resources/parts/core/cmRuler.fzp resources/parts/core/note.fzp resources/parts/core/rectangle_pcb.fzp resources/parts/core/rectangle_pcb_two_layers.fzp resources/parts/core/ellipse_pcb.fzp resources/parts/core/ellipse_pcb_two_layers.fzp resources/parts/core/roundrect_pcb.fzp resources/parts/core/roundrect_pcb_two_layers.fzp resources/parts/core/logotext.fzp resources/parts/core/logoimage.fzp resources/parts/core/silkscreen0logoimage.fzp resources/parts/core/breadboardlogoimage.fzp resources/parts/core/copper1logoimage.fzp resources/parts/core/copper0logoimage.fzp resources/parts/core/schematiclogoimage.fzp resources/parts/core/newlogoimage.fzp resources/parts/core/newsilkscreen0logoimage.fzp resources/parts/core/newbreadboardlogoimage.fzp resources/parts/core/newcopper1logoimage.fzp resources/parts/core/newcopper0logoimage.fzp resources/parts/core/newschematiclogoimage.fzp resources/parts/core/copper1logotext.fzp resources/parts/core/copper0logotext.fzp resources/parts/core/silkscreen0logotext.fzp resources/parts/core/schematiclogotext.fzp resources/parts/core/breadboardlogotext.fzp resources/parts/core/boardlogoimage.fzp resources/parts/core/boardlogoimage_one_layer.fzp resources/parts/core/ground.fzp resources/parts/core/netlabel.fzp resources/parts/core/netlabel_left.fzp resources/parts/core/via.fzp resources/parts/core/pad.fzp resources/parts/core/pad0.fzp resources/parts/core/blocker.fzp resources/parts/core/blocker0.fzp resources/parts/core/dcpower.fzp resources/parts/core/power.fzp resources/parts/core/power_label.fzp resources/parts/core/jumper.fzp resources/parts/core/groundplane.fzp resources/parts/core/hole.fzp resources/parts/core/30.20PerfboardModuleID.fzp resources/parts/core/30.20StripboardModuleID.fzp resources/parts/core/30.20Stripboard2ModuleID.fzp resources/parts/core/schematic_frame.fzp resources/parts/svg/core/icon/iconPerfboard.svg resources/parts/svg/core/icon/iconStripboard.svg resources/parts/svg/core/icon/wireicon.svg resources/parts/svg/core/icon/rulericon.svg resources/parts/svg/core/icon/rectangle_pcb.svg resources/parts/svg/core/icon/silkscreen_logo.svg resources/parts/svg/core/icon/silkscreen_text.svg resources/parts/svg/core/icon/copper0_text.svg resources/parts/svg/core/icon/copper0_logo.svg resources/parts/svg/core/icon/copper1_text.svg resources/parts/svg/core/icon/copper1_logo.svg resources/parts/svg/core/icon/schematic_text.svg resources/parts/svg/core/icon/schematic_logo.svg resources/parts/svg/core/icon/breadboard_text.svg resources/parts/svg/core/icon/breadboard_logo.svg resources/parts/svg/core/icon/jumpericon.svg resources/parts/svg/core/icon/hole.svg resources/parts/svg/core/icon/via.svg resources/parts/svg/core/icon/pad.svg resources/parts/svg/core/icon/blocker.svg resources/parts/svg/core/icon/schematic_frame_icon.svg resources/parts/svg/core/icon/groundplane.svg resources/parts/svg/core/icon/note.svg resources/parts/svg/core/breadboard/ruler.svg resources/parts/svg/core/breadboard/wire.svg resources/parts/svg/core/breadboard/30.20perfboard.svg resources/parts/svg/core/breadboard/breadboardlogo.svg resources/parts/svg/core/pcb/rectangle_pcb.svg resources/parts/svg/core/pcb/ellipse_pcb.svg resources/parts/svg/core/pcb/roundrect_pcb.svg resources/parts/svg/core/pcb/logo.svg resources/parts/svg/core/pcb/copper1logo.svg resources/parts/svg/core/pcb/jumper.svg resources/parts/svg/core/pcb/hole.svg resources/parts/svg/core/pcb/groundplane.svg resources/parts/svg/core/pcb/via.svg resources/parts/svg/core/pcb/pad.svg resources/parts/svg/core/pcb/pad0.svg resources/parts/svg/core/schematic/netlabel.svg resources/parts/svg/core/schematic/frame.svg resources/parts/svg/core/schematic/schematiclogo.svg resources/parts/svg/obsolete/schematic/0.3.schem.netlabel.svg resources/parts/svg/obsolete/schematic/0.3.schem.frame.svg resources/styles/fritzing.qss resources/styles/partseditor.qss resources/styles/newpartseditor.qss resources/styles/programwindow.qss resources/styles/linux-fritzing.qss resources/styles/linux-kde-oxygen-fritzing.qss resources/styles/linux-gnome-fritzing.qss resources/styles/win-fritzing.qss resources/styles/mac-fritzing.qss resources/styles/updatedialog.css resources/fonts/DroidSans.ttf resources/fonts/DroidSansMono.ttf resources/fonts/DroidSans-Bold.ttf resources/fonts/NOTICE resources/fonts/OCRA.ttf resources/bins/my_parts.fzb resources/bins/search.fzb resources/bins/temp.fzb resources/bins/order.xml resources/bins/pe.fzb resources/bins/icons/Arduino.png resources/bins/icons/Arduino-mono.png resources/bins/icons/Core.png resources/bins/icons/Core-mono.png resources/bins/icons/Custom1.png resources/bins/icons/Custom1-mono.png resources/bins/icons/Mine.png resources/bins/icons/Mine-mono.png resources/bins/icons/PICAXE.png resources/bins/icons/PICAXE-mono.png resources/bins/icons/Search.png resources/bins/icons/Lilypad.png resources/bins/icons/Lilypad-mono.png resources/bins/icons/Snootlab.png resources/bins/icons/Snootlab-mono.png resources/bins/icons/SparkFun.png resources/bins/icons/SparkFun-mono.png resources/bins/icons/Contrib.png resources/bins/icons/Contrib-mono.png resources/bins/icons/Parallax.png resources/bins/icons/Parallax-mono.png resources/bins/icons/Pololu.png resources/bins/icons/Pololu-mono.png resources/bins/icons/Temp.png resources/bins/icons/Temp-mono.png resources/templates/jumper_copper0LayerTemplate.txt resources/templates/jumper_jumperwiresLayerTemplate.txt resources/templates/bom.html resources/templates/bom_row.html resources/templates/perfboard_boardLayerTemplate.txt resources/templates/perfboard_connectorTemplate.txt resources/templates/perfboard_connectorFzpTemplate.txt resources/templates/perfboard_fzpTemplate.txt resources/templates/generic_sip_connectorFzpTemplate.txt resources/templates/generic_sip_fzpTemplate.txt resources/templates/generic_dip_fzpTemplate.txt resources/templates/mystery_part_sipFzpTemplate.txt resources/templates/mystery_part_dipFzpTemplate.txt resources/templates/generic_female_pin_header_fzpTemplate.txt resources/templates/screw_terminal_fzpTemplate.txt resources/templates/jumper_pcb_svg_template.txt resources/templates/jumper_pcb_svg_2nd_template.txt resources/templates/generic_female_pin_header_schem_template.txt resources/templates/generic_male_pin_header_schem_template.txt resources/templates/generic_female_10thin_pin_header_schem_template.txt resources/templates/generic_male_10thin_pin_header_schem_template.txt resources/templates/generic_female_10thin_double_pin_header_schem_template.txt resources/templates/generic_male_10thin_double_pin_header_schem_template.txt resources/templates/generic_female_pin_header_bread_template.txt resources/templates/generic_female_pin_header_bread_2nd_template.txt resources/templates/generic_rounded_female_pin_header_bread_template.txt resources/templates/generic_rounded_female_pin_header_bread_2nd_template.txt resources/templates/generic_male_pin_header_bread_template.txt resources/templates/generic_male_pin_header_bread_2nd_template.txt resources/templates/generic_sip_bread_template.txt resources/templates/schematic_frame_template.txt resources/templates/0.3.schem.schematic_frame_template.txt resources/templates/screw_terminal_bread_template.txt resources/templates/stripboards.xml resources/images/icons/zoomSlider.png resources/images/icons/zoomSliderHover.png resources/images/icons/checkhover.png resources/images/icons/checkactive.png resources/images/icons/smalToolarrow-inactive.png resources/images/icons/smalToolarrow-normal.png resources/images/icons/smalToolarrow-pressed.png resources/images/icons/spinArrowButtonUpNormal.png resources/images/icons/spinArrowButtonUpPressed.png resources/images/icons/spinArrowButtonDownNormal.png resources/images/icons/spinArrowButtonDownPressed.png resources/images/icons/TabScrollArrowButtonUpNormal.png resources/images/icons/TabScrollArrowButtonUpPressed.png resources/images/icons/TabScrollArrowButtonDownNormal.png resources/images/icons/TabScrollArrowButtonDownPressed.png resources/images/icons/TabScrollArrowButtonLeftNormal.png resources/images/icons/TabScrollArrowButtonLeftPressed.png resources/images/icons/TabScrollArrowButtonRightNormal.png resources/images/icons/TabScrollArrowButtonRightPressed.png resources/images/icons/toolbarNewCodeDisabled_icon.png resources/images/icons/toolbarNewCodeEnabled_icon.png resources/images/icons/toolbarNewCodePressed_icon.png resources/images/icons/toolbarMonitorCodeDisabled_icon.png resources/images/icons/toolbarMonitorCodeEnabled_icon.png resources/images/icons/toolbarMonitorCodePressed_icon.png resources/images/icons/toolbarOpenCodeDisabled_icon.png resources/images/icons/toolbarOpenCodeEnabled_icon.png resources/images/icons/toolbarOpenCodePressed_icon.png resources/images/icons/toolbarProgramCodeDisabled_icon.png resources/images/icons/toolbarProgramCodeEnabled_icon.png resources/images/icons/toolbarProgramCodePressed_icon.png resources/images/icons/toolbarSaveCodeDisabled_icon.png resources/images/icons/toolbarSaveCodeEnabled_icon.png resources/images/icons/toolbarSaveCodePressed_icon.png fritzing-app-0.9.3b/pri/000077500000000000000000000000001272363601300150745ustar00rootroot00000000000000fritzing-app-0.9.3b/pri/autoroute.pri000066400000000000000000000034521272363601300176430ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 2948 $: # $Author: cohen@irascible.com $: # $Date: 2009-05-15 17:05:14 +0200 (Fri, 15 May 2009) $ # ********************************************************************/ HEADERS += \ src/autoroute/autorouter.h \ src/autoroute/cmrouter/cmrouter.h \ src/autoroute/autorouteprogressdialog.h \ src/autoroute/autoroutersettingsdialog.h \ src/autoroute/panelizer.h \ src/autoroute/cmrouter/tile.h \ src/autoroute/cmrouter/tileutils.h \ src/autoroute/mazerouter/mazerouter.h \ src/autoroute/zoomcontrols.h \ src/autoroute/drc.h \ SOURCES += \ src/autoroute/autorouter.cpp \ src/autoroute/cmrouter/cmrouter.cpp \ src/autoroute/autorouteprogressdialog.cpp \ src/autoroute/autoroutersettingsdialog.cpp \ src/autoroute/panelizer.cpp \ src/autoroute/cmrouter/tile.cpp \ src/autoroute/cmrouter/DBcell.cpp \ src/autoroute/cmrouter/search.cpp \ src/autoroute/cmrouter/search2.cpp \ src/autoroute/mazerouter/mazerouter.cpp \ src/autoroute/zoomcontrols.cpp \ src/autoroute/drc.cpp \ fritzing-app-0.9.3b/pri/connectors.pri000066400000000000000000000030241272363601300177640ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 2948 $: # $Author: cohen@irascible.com $: # $Date: 2009-05-15 17:05:14 +0200 (Fri, 15 May 2009) $ # ********************************************************************/ HEADERS += \ src/connectors/bus.h \ src/connectors/busshared.h \ src/connectors/connector.h \ src/connectors/connectoritem.h \ src/connectors/nonconnectoritem.h \ src/connectors/connectorshared.h \ src/connectors/ercdata.h \ src/connectors/svgidlayer.h SOURCES += \ src/connectors/bus.cpp \ src/connectors/busshared.cpp \ src/connectors/connector.cpp \ src/connectors/connectoritem.cpp \ src/connectors/nonconnectoritem.cpp \ src/connectors/connectorshared.cpp \ src/connectors/ercdata.cpp \ src/connectors/svgidlayer.cpp fritzing-app-0.9.3b/pri/dialogs.pri000066400000000000000000000027561272363601300172440ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 2740 $: # $Author: cohen@irascible.com $: # $Date: 2009-03-28 07:54:52 +0100 (Sat, 28 Mar 2009) $ # ********************************************************************/ HEADERS += src/dialogs/prefsdialog.h \ src/dialogs/pinlabeldialog.h \ src/dialogs/groundfillseeddialog.h \ src/dialogs/quotedialog.h \ src/dialogs/recoverydialog.h \ src/dialogs/setcolordialog.h \ src/dialogs/translatorlistmodel.h SOURCES += src/dialogs/prefsdialog.cpp \ src/dialogs/pinlabeldialog.cpp \ src/dialogs/groundfillseeddialog.cpp \ src/dialogs/quotedialog.cpp \ src/dialogs/recoverydialog.cpp \ src/dialogs/setcolordialog.cpp \ src/dialogs/translatorlistmodel.cpp fritzing-app-0.9.3b/pri/dock.pri000066400000000000000000000021331272363601300165270ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 6373 $: # $Author: cohen@irascible.com $: # $Date: 2012-09-06 08:39:51 +0200 (Do, 06. Sep 2012) $ # ********************************************************************/ HEADERS += \ src/dock/layerpalette.h \ SOURCES += \ src/dock/layerpalette.cpp \ fritzing-app-0.9.3b/pri/eagle.pri000066400000000000000000000021271272363601300166670ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 5721 $: # $Author: cohen@irascible.com $: # $Date: 2012-01-03 16:53:58 +0100 (Di, 03. Jan 2012) $ # ********************************************************************/ HEADERS += src/eagle/fritzing2eagle.h SOURCES += src/eagle/fritzing2eagle.cpp fritzing-app-0.9.3b/pri/help.pri000066400000000000000000000023241272363601300165410ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 5721 $: # $Author: cohen@irascible.com $: # $Date: 2012-01-03 16:53:58 +0100 (Di, 03. Jan 2012) $ # ********************************************************************/ HEADERS += src/help/aboutbox.h \ src/help/firsttimehelpdialog.h \ src/help/tipsandtricks.h SOURCES += src/help/aboutbox.cpp \ src/help/firsttimehelpdialog.cpp \ src/help/tipsandtricks.cpp fritzing-app-0.9.3b/pri/infoview.pri000066400000000000000000000021311272363601300174330ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 2740 $: # $Author: cohen@irascible.com $: # $Date: 2009-03-28 07:54:52 +0100 (Sat, 28 Mar 2009) $ # ********************************************************************/ HEADERS += src/infoview/htmlinfoview.h SOURCES += src/infoview/htmlinfoview.cpp fritzing-app-0.9.3b/pri/items.pri000066400000000000000000000060161272363601300167340ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 2846 $: # $Author: cohen@irascible.com $: # $Date: 2009-04-21 16:23:36 +0200 (Tue, 21 Apr 2009) $ # ********************************************************************/ HEADERS += \ src/items/breadboard.h \ src/items/capacitor.h \ src/items/clipablewire.h \ src/items/dip.h \ src/items/groundplane.h \ src/items/hole.h \ src/items/itembase.h \ src/items/jumperitem.h \ src/items/layerkinpaletteitem.h \ src/items/led.h \ src/items/logoitem.h \ src/items/moduleidnames.h \ src/items/mysterypart.h \ src/items/note.h \ src/items/pad.h \ src/items/paletteitem.h \ src/items/paletteitembase.h \ src/items/partfactory.h \ src/items/partlabel.h \ src/items/perfboard.h \ src/items/pinheader.h \ src/items/propertydef.h \ src/items/resistor.h \ src/items/resizableboard.h \ src/items/ruler.h \ src/items/schematicframe.h \ src/items/schematicsubpart.h \ src/items/screwterminal.h \ src/items/stripboard.h \ src/items/symbolpaletteitem.h \ src/items/tracewire.h \ src/items/via.h \ src/items/virtualwire.h \ src/items/wire.h \ SOURCES += \ src/items/breadboard.cpp \ src/items/capacitor.cpp \ src/items/clipablewire.cpp \ src/items/dip.cpp \ src/items/groundplane.cpp \ src/items/hole.cpp \ src/items/itembase.cpp \ src/items/jumperitem.cpp \ src/items/layerkinpaletteitem.cpp \ src/items/led.cpp \ src/items/logoitem.cpp \ src/items/moduleidnames.cpp \ src/items/mysterypart.cpp \ src/items/note.cpp \ src/items/pad.cpp \ src/items/paletteitem.cpp \ src/items/paletteitembase.cpp \ src/items/partfactory.cpp \ src/items/partlabel.cpp \ src/items/perfboard.cpp \ src/items/pinheader.cpp \ src/items/propertydef.cpp \ src/items/resistor.cpp \ src/items/resizableboard.cpp \ src/items/ruler.cpp \ src/items/schematicframe.cpp \ src/items/schematicsubpart.cpp \ src/items/screwterminal.cpp \ src/items/stripboard.cpp \ src/items/symbolpaletteitem.cpp \ src/items/tracewire.cpp \ src/items/via.cpp \ src/items/virtualwire.cpp \ src/items/wire.cpp \ fritzing-app-0.9.3b/pri/kitchensink.pri000066400000000000000000000033561272363601300201310ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 6409 $: # $Author: cohen@irascible.com $: # $Date: 2012-09-13 05:05:28 +0200 (Do, 13. Sep 2012) $ # ********************************************************************/ HEADERS += \ src/commands.h \ src/debugdialog.h \ src/fapplication.h \ src/fsplashscreen.h \ src/fsvgrenderer.h \ src/installedfonts.h \ src/itemdrag.h \ src/layerattributes.h \ src/processeventblocker.h \ src/routingstatus.h \ src/sketchtoolbutton.h \ src/viewgeometry.h \ src/viewlayer.h \ src/waitpushundostack.h SOURCES += \ src/commands.cpp \ src/debugdialog.cpp \ src/fapplication.cpp \ src/fsplashscreen.cpp \ src/fsvgrenderer.cpp \ src/itemdrag.cpp \ src/layerattributes.cpp \ src/main.cpp \ src/processeventblocker.cpp \ src/sketchtoolbutton.cpp \ src/viewgeometry.cpp \ src/viewlayer.cpp \ src/waitpushundostack.cpp fritzing-app-0.9.3b/pri/mainwindow.pri000066400000000000000000000027271272363601300177740ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 6370 $: # $Author: cohen@irascible.com $: # $Date: 2012-09-05 17:51:33 +0200 (Wed, 05 Sep 2012) $ # ********************************************************************/ HEADERS += \ src/mainwindow/fdockwidget.h \ src/mainwindow/fritzingwindow.h \ src/mainwindow/mainwindow.h \ src/mainwindow/sketchareawidget.h \ SOURCES += \ src/mainwindow/fdockwidget.cpp \ src/mainwindow/fritzingwindow.cpp \ src/mainwindow/mainwindow.cpp \ src/mainwindow/mainwindow_export.cpp \ src/mainwindow/mainwindow_menu.cpp \ src/mainwindow/mainwindow_dock.cpp \ src/mainwindow/sketchareawidget.cpp \ fritzing-app-0.9.3b/pri/model.pri000066400000000000000000000025301272363601300167100ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 3727 $: # $Author: cohen@irascible.com $: # $Date: 2009-11-14 12:36:34 +0100 (Sat, 14 Nov 2009) $ # ********************************************************************/ HEADERS += \ src/model/modelbase.h \ src/model/modelpart.h \ src/model/modelpartshared.h \ src/model/palettemodel.h \ src/model/sketchmodel.h SOURCES += \ src/model/modelbase.cpp \ src/model/modelpart.cpp \ src/model/modelpartshared.cpp \ src/model/palettemodel.cpp \ src/model/sketchmodel.cpp fritzing-app-0.9.3b/pri/partsbinpalette.pri000066400000000000000000000041011272363601300210050ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTIC,asMainWindowULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with Fritzing. If not, see . # ******************************************************************** # $Revision: 6486 $: # $Author: irascibl@gmail.com $: # $Date: 2012-09-27 10:12:47 +0200 (Do, 27. Sep 2012) $ # ********************************************************************/ HEADERS += \ src/partsbinpalette/partsbinpalettewidget.h \ src/partsbinpalette/partsbinview.h \ src/partsbinpalette/partsbinlistview.h \ src/partsbinpalette/partsbiniconview.h \ src/partsbinpalette/graphicsflowlayout.h \ src/partsbinpalette/svgiconwidget.h \ src/partsbinpalette/partsbincommands.h \ src/partsbinpalette/searchlineedit.h \ src/partsbinpalette/binmanager/binmanager.h \ src/partsbinpalette/binmanager/stacktabbar.h \ src/partsbinpalette/binmanager/stacktabwidget.h SOURCES += \ src/partsbinpalette/partsbinpalettewidget.cpp \ src/partsbinpalette/partsbinview.cpp \ src/partsbinpalette/partsbinlistview.cpp \ src/partsbinpalette/partsbiniconview.cpp \ src/partsbinpalette/graphicsflowlayout.cpp \ src/partsbinpalette/svgiconwidget.cpp \ src/partsbinpalette/partsbincommands.cpp \ src/partsbinpalette/searchlineedit.cpp \ src/partsbinpalette/binmanager/binmanager.cpp \ src/partsbinpalette/binmanager/stacktabbar.cpp \ src/partsbinpalette/binmanager/stacktabwidget.cpp fritzing-app-0.9.3b/pri/partseditor.pri000066400000000000000000000077641272363601300201660ustar00rootroot00000000000000# /******************************************************************* # # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # # ******************************************************************** # # $Revision: 6448 $: # $Author: cohen@irascible.com $: # $Date: 2012-09-17 23:36:33 +0200 (Mo, 17. Sep 2012) $ # #********************************************************************/ HEADERS += \ # src/partseditor/editabledatewidget.h \ # src/partseditor/abstractconnectorinfowidget.h \ # src/partseditor/mismatchingconnectorwidget.h \ # src/partseditor/partseditorconnectoritem.h \ # src/partseditor/singleconnectorinfowidget.h \ # src/partseditor/partconnectorswidget.h \ # src/partseditor/connectorsinfowidget.h \ # src/partseditor/editablelinewidget.h \ # src/partseditor/editabletextwidget.h \ # src/partseditor/abstracteditablelabelwidget.h \ # src/partseditor/partseditormainwindow.h \ # src/partseditor/editablelabel.h \ # src/partseditor/partspecificationswidget.h \ # src/partseditor/addremovelistwidget.h \ # src/partseditor/partseditorpaletteitem.h \ # src/partseditor/addremoveconnectorbutton.h \ # src/partseditor/connectorinforemovebutton.h \ # src/partseditor/partseditorconnectorspaletteitem.h \ # src/partseditor/partseditorconnectorsconnectoritem.h \ # src/partseditor/partseditorlayerkinpaletteitem.h \ # src/partseditor/partseditorconnectorslayerkinpaletteitem.h \ # src/partseditor/partseditorview.h \ # src/partseditor/partseditorviewswidget.h \ # src/partseditor/fixfontsdialog.h \ \ src/partseditor/pemainwindow.h \ src/partseditor/pemetadataview.h \ src/partseditor/pecommands.h \ src/partseditor/peconnectorsview.h \ src/partseditor/pesvgview.h \ src/partseditor/petoolview.h \ src/partseditor/peutils.h \ src/partseditor/pegraphicsitem.h \ src/partseditor/kicadmoduledialog.h \ src/partseditor/hashpopulatewidget.h \ src/partseditor/baseremovebutton.h \ SOURCES += \ # src/partseditor/editabledatewidget.cpp \ # src/partseditor/abstractconnectorinfowidget.cpp \ # src/partseditor/mismatchingconnectorwidget.cpp \ # src/partseditor/partseditorconnectoritem.cpp \ # src/partseditor/singleconnectorinfowidget.cpp \ # src/partseditor/partconnectorswidget.cpp \ # src/partseditor/connectorsinfowidget.cpp \ # src/partseditor/editablelinewidget.cpp \ # src/partseditor/editabletextwidget.cpp \ # src/partseditor/abstracteditablelabelwidget.cpp \ # src/partseditor/partseditormainwindow.cpp \ # src/partseditor/editablelabel.cpp \ # src/partseditor/partspecificationswidget.cpp \ # src/partseditor/addremovelistwidget.cpp \ # src/partseditor/partseditorpaletteitem.cpp \ # src/partseditor/partseditorconnectorspaletteitem.cpp \ # src/partseditor/partseditorconnectorsconnectoritem.cpp \ # src/partseditor/partseditorview.cpp \ # src/partseditor/partseditorviewswidget.cpp \ # src/partseditor/fixfontsdialog.cpp \ \ src/partseditor/pemainwindow.cpp \ src/partseditor/pemetadataview.cpp \ src/partseditor/pecommands.cpp \ src/partseditor/peconnectorsview.cpp \ src/partseditor/pesvgview.cpp \ src/partseditor/petoolview.cpp \ src/partseditor/peutils.cpp \ src/partseditor/pegraphicsitem.cpp \ src/partseditor/kicadmoduledialog.cpp \ src/partseditor/hashpopulatewidget.cpp \ fritzing-app-0.9.3b/pri/program.pri000066400000000000000000000036441272363601300172660ustar00rootroot00000000000000# /******************************************************************* # # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # # ******************************************************************** # # $Revision: 3854 $: # $Author: cohen@irascible.com $: # $Date: 2009-12-10 17:42:28 +0100 (Thu, 10 Dec 2009) $ # #********************************************************************/ HEADERS += \ src/program/highlighter.h \ src/program/programtab.h \ src/program/programwindow.h \ src/program/syntaxer.h \ src/program/trienode.h \ src/program/console.h \ src/program/consolewindow.h \ src/program/consolesettings.h \ src/program/platform.h \ src/program/platformarduino.h \ src/program/platformpicaxe.h \ src/program/platformlaunchpad.h SOURCES += \ src/program/highlighter.cpp \ src/program/programtab.cpp \ src/program/programwindow.cpp \ src/program/syntaxer.cpp \ src/program/trienode.cpp \ src/program/console.cpp \ src/program/consolewindow.cpp \ src/program/consolesettings.cpp \ src/program/platform.cpp \ src/program/platformarduino.cpp \ src/program/platformpicaxe.cpp \ src/program/platformlaunchpad.cpp FORMS += \ src/program/consolewindow.ui \ src/program/consolesettings.ui fritzing-app-0.9.3b/pri/qtsysteminfo.pri000066400000000000000000000001401272363601300203500ustar00rootroot00000000000000SOURCES += src/lib/qtsysteminfo/QtSystemInfo.cpp HEADERS += src/lib/qtsysteminfo/QtSystemInfo.h fritzing-app-0.9.3b/pri/quazip.pri000066400000000000000000000026771272363601300171350ustar00rootroot00000000000000# /******************************************************************* # # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # # ******************************************************************** # # $Revision: 5721 $: # $Author: cohen@irascible.com $: # $Date: 2012-01-03 16:53:58 +0100 (Di, 03. Jan 2012) $ # #********************************************************************/ HEADERS += \ src/lib/quazip/crypt.h \ src/lib/quazip/ioapi.h \ src/lib/quazip/quazip.h \ src/lib/quazip/quazipfile.h \ src/lib/quazip/quazipfileinfo.h \ src/lib/quazip/quazipnewinfo.h \ src/lib/quazip/unzip.h \ src/lib/quazip/zip.h SOURCES += \ src/lib/quazip/ioapi.c \ src/lib/quazip/quazip.cpp \ src/lib/quazip/quazipfile.cpp \ src/lib/quazip/quazipnewinfo.cpp \ src/lib/quazip/unzip.c \ src/lib/quazip/zip.c fritzing-app-0.9.3b/pri/referencemodel.pri000066400000000000000000000022721272363601300205720ustar00rootroot00000000000000# /******************************************************************* # # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # # ******************************************************************** # # $Revision: 6309 $: # $Author: cohen@irascible.com $: # $Date: 2012-08-17 11:16:43 +0200 (Fr, 17. Aug 2012) $ # #********************************************************************/ HEADERS += \ src/referencemodel/sqlitereferencemodel.h \ src/referencemodel/referencemodel.h \ SOURCES += \ src/referencemodel/sqlitereferencemodel.cpp \ fritzing-app-0.9.3b/pri/sketch.pri000066400000000000000000000031761272363601300171000ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 3727 $: # $Author: cohen@irascible.com $: # $Date: 2009-11-14 12:36:34 +0100 (Sat, 14 Nov 2009) $ # ********************************************************************/ HEADERS += \ src/sketch/fgraphicsscene.h \ src/sketch/breadboardsketchwidget.h \ src/sketch/infographicsview.h \ src/sketch/pcbsketchwidget.h \ src/sketch/schematicsketchwidget.h \ src/sketch/sketchwidget.h \ src/sketch/welcomeview.h \ src/sketch/zoomablegraphicsview.h \ SOURCES += \ src/sketch/fgraphicsscene.cpp \ src/sketch/breadboardsketchwidget.cpp \ src/sketch/infographicsview.cpp \ src/sketch/pcbsketchwidget.cpp \ src/sketch/schematicsketchwidget.cpp \ src/sketch/sketchwidget.cpp \ src/sketch/welcomeview.cpp \ src/sketch/zoomablegraphicsview.cpp \ fritzing-app-0.9.3b/pri/svg.pri000066400000000000000000000041241272363601300164100ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 5721 $: # $Author: cohen@irascible.com $: # $Date: 2012-01-03 16:53:58 +0100 (Di, 03. Jan 2012) $ # ********************************************************************/ HEADERS += src/svg/svgfilesplitter.h \ src/svg/svgpathparser.h \ src/svg/svgpathgrammar_p.h \ src/svg/svgpathlexer.h \ src/svg/svgpathrunner.h \ src/svg/svg2gerber.h \ src/svg/svgflattener.h \ src/svg/gerbergenerator.h \ src/svg/groundplanegenerator.h \ src/svg/x2svg.h \ src/svg/kicad2svg.h \ src/svg/kicadmodule2svg.h \ src/svg/kicadschematic2svg.h \ src/svg/gedaelement2svg.h \ src/svg/gedaelementparser.h \ src/svg/gedaelementgrammar_p.h \ src/svg/gedaelementlexer.h SOURCES += src/svg/svgfilesplitter.cpp \ src/svg/svgpathparser.cpp \ src/svg/svgpathgrammar.cpp \ src/svg/svgpathlexer.cpp \ src/svg/svgpathrunner.cpp \ src/svg/svg2gerber.cpp \ src/svg/svgflattener.cpp \ src/svg/gerbergenerator.cpp \ src/svg/groundplanegenerator.cpp \ src/svg/x2svg.cpp \ src/svg/kicad2svg.cpp \ src/svg/kicadmodule2svg.cpp \ src/svg/kicadschematic2svg.cpp \ src/svg/gedaelement2svg.cpp \ src/svg/gedaelementparser.cpp \ src/svg/gedaelementgrammar.cpp \ src/svg/gedaelementlexer.cpp fritzing-app-0.9.3b/pri/translations.pri000066400000000000000000000043371272363601300203400ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 2846 $: # $Author: cohen@irascible.com $: # $Date: 2009-04-21 16:23:36 +0200 (Tue, 21 Apr 2009) $ # ********************************************************************/ TRANSLATIONS += translations/fritzing_de.ts \ translations/fritzing_ar.ts \ translations/fritzing_bg.ts \ translations/fritzing_bn.ts \ translations/fritzing_cs.ts \ translations/fritzing_da.ts \ translations/fritzing_el.ts \ translations/fritzing_en.ts \ translations/fritzing_es.ts \ translations/fritzing_et.ts \ translations/fritzing_fa.ts \ translations/fritzing_fr.ts \ translations/fritzing_gl.ts \ translations/fritzing_hi.ts \ translations/fritzing_hu.ts \ translations/fritzing_id.ts \ translations/fritzing_it.ts \ translations/fritzing_ja.ts \ translations/fritzing_ko.ts \ translations/fritzing_mk.ts \ translations/fritzing_mr.ts \ translations/fritzing_nl.ts \ translations/fritzing_pl.ts \ translations/fritzing_pt_br.ts \ translations/fritzing_pt_pt.ts \ translations/fritzing_ro.ts \ translations/fritzing_ru.ts \ translations/fritzing_sk.ts \ translations/fritzing_sl.ts \ translations/fritzing_sr.ts \ translations/fritzing_sv.ts \ translations/fritzing_th.ts \ translations/fritzing_tr.ts \ translations/fritzing_uk.ts \ translations/fritzing_ur.ts \ translations/fritzing_vi.ts \ translations/fritzing_zh_cn.ts \ translations/fritzing_zh_tw.ts \ fritzing-app-0.9.3b/pri/utils.pri000066400000000000000000000077561272363601300167670ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 6796 $: # $Author: irascibl@gmail.com $: # $Date: 2013-01-12 07:45:08 +0100 (Sa, 12. Jan 2013) $ # ********************************************************************/ # boost_1_54_0 is buggy BOOSTS = 43 44 45 46 47 48 49 50 51 52 53 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 LATESTBOOST = 0 for(boost, BOOSTS) { exists(../src/lib/boost_1_$${boost}_0) { LATESTBOOST = $$boost } } contains(LATESTBOOST, 0) { unix { !macx { BOOSTINFO = $$system(dpkg -s libboost-dev | grep 'Version') BADVERSION = $$find(BOOSTINFO, 1\.54) !isEmpty(BADVERSION) { message("Boost 1.54 has a bug in a function that Fritzing uses, so download or install some other version") error("Easiest to copy the boost library to .../src/lib/, so that you have .../src/lib/boost_1_xx_0") } isEmpty(BADVERSION) { BOOSTVERSION = $$find(BOOSTINFO, 1\...\.0) !isEmpty(BOOSTVERSION) { LATESTBOOST = installed message("using installed BOOST library") } } } } } contains(LATESTBOOST, 0) { message("Please download the boost library--you can find it at http://www.boost.org/") message("Note that boost 1.54 has a bug in a function that Fritzing uses, so download some other version") error("Copy the boost library to .../src/lib/, so that you have .../src/lib/boost_1_xx_0") } !contains(LATESTBOOST, installed) { message("using boost from src/lib/boost_1_$${LATESTBOOST}_0") INCLUDEPATH += src/lib/boost_1_$${LATESTBOOST}_0 } HEADERS += \ src/utils/abstractstatesbutton.h \ src/utils/autoclosemessagebox.h \ src/utils/bendpointaction.h \ src/utils/bezier.h \ src/utils/bezierdisplay.h \ src/utils/boundedregexpvalidator.h \ src/utils/bundler.h \ src/utils/clickablelabel.h \ src/utils/cursormaster.h \ src/utils/expandinglabel.h \ src/utils/familypropertycombobox.h \ src/utils/fileprogressdialog.h \ src/utils/flineedit.h \ src/utils/fmessagebox.h \ src/utils/focusoutcombobox.h \ src/utils/fsizegrip.h \ src/utils/lockmanager.h \ src/utils/misc.h \ src/utils/resizehandle.h \ src/utils/folderutils.h \ src/utils/graphicsutils.h \ src/utils/graphutils.h \ src/utils/ratsnestcolors.h \ src/utils/schematicrectconstants.h \ src/utils/s2s.h \ src/utils/textutils.h \ src/utils/zoomslider.h SOURCES += \ src/utils/autoclosemessagebox.cpp \ src/utils/bendpointaction.cpp \ src/utils/bezier.cpp \ src/utils/bezierdisplay.cpp \ src/utils/clickablelabel.cpp \ src/utils/cursormaster.cpp \ src/utils/expandinglabel.cpp \ src/utils/fileprogressdialog.cpp \ src/utils/flineedit.cpp \ src/utils/fmessagebox.cpp \ src/utils/focusoutcombobox.cpp \ src/utils/fsizegrip.cpp \ src/utils/lockmanager.cpp \ src/utils/misc.cpp \ src/utils/resizehandle.cpp \ src/utils/folderutils.cpp \ src/utils/graphicsutils.cpp \ src/utils/graphutils.cpp \ src/utils/ratsnestcolors.cpp \ src/utils/schematicrectconstants.cpp \ src/utils/s2s.cpp \ src/utils/textutils.cpp \ src/utils/zoomslider.cpp fritzing-app-0.9.3b/pri/version.pri000066400000000000000000000026401272363601300172770ustar00rootroot00000000000000# /******************************************************************* # Part of the Fritzing project - http://fritzing.org # Copyright (c) 2007-08 Fritzing # Fritzing is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Fritzing is distributed in the hope that 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 Fritzing. If not, see . # ******************************************************************** # $Revision: 5721 $: # $Author: cohen@irascible.com $: # $Date: 2012-01-03 16:53:58 +0100 (Di, 03. Jan 2012) $ # ********************************************************************/ HEADERS += \ src/version/modfiledialog.h \ src/version/updatedialog.h \ src/version/version.h \ src/version/versionchecker.h \ src/version/partschecker.h SOURCES += \ src/version/modfiledialog.cpp \ src/version/updatedialog.cpp \ src/version/version.cpp \ src/version/versionchecker.cpp \ src/version/partschecker.cpp FORMS += \ src/version/modfiledialog.ui fritzing-app-0.9.3b/readme.md000066400000000000000000000066701272363601300160720ustar00rootroot00000000000000# Fritzing The Fritzing application is an Electronic Design Automation software with a low entry barrier, suited for the needs of makers and hobbyists. It offers a unique real-life "breadboard" view, and a parts library with many commonly used high-level components. Fritzing makes it very easy to communicate about circuits, as well as to turn them into PCB layouts ready for production. It is particularly popular among Arduino and Raspberry Pi users, and is widely used in education and creative tinkering. * For more information on Fritzing and its related activities, visit [http://fritzing.org](http://fritzing.org). There you can also [download](http://fritzing.org/download) the latest releases for all platforms and get help on getting started. * To report a problem or suggest improvements, use the [issue tracker](https://github.com/fritzing/fritzing-app/issues) or the [user forums](http://fritzing.org/forum) * If you would like to help with the development, please take a look at the [developer instructions](https://github.com/fritzing/fritzing-app/wiki). This includes information about how to compile and run the Fritzing app in a few steps. # Folder Structure * **bins** - Part bins (.fzb, aka part libraries) are collections of parts, most importantly the "core" parts bin, and vendor-specific collections. * **help** - End-user documentation included with the app. This should really be moved back to the website. * **parts** - All the part definitions, including meta data (.fzp) and graphics (.svg), as well as some utility tools. They are kept in a separate repository at [https://github.com/fritzing/fritzing-parts](http://github.com/fritzing/fritzing-parts) and only linked from here. * **pri** - Submodule definitions for Qt * **resources** - Binaries and definitions that are supposed to not be touched by users, such as fonts, images, special parts, etc. * **sketches** - Example circuits/sketches shipped with the application * **src** - Application logic! * **tools** - Utility tools for making releases, converting parts, etc. * **translations** - Language translations # Credits The Fritzing app is maintained by the Friends-of-Fritzing e.V., a non-profit foundation based in Berlin, Germany. The project has grown out of a state-funded research project at the [Interaction Design Lab](http://idl.fh-potsdam.de) at [Potsdam University of Applied Sciences](http://fh-potsdam.de). The core team consists of Prof. Reto Wettach, André Knörig, Jonathan Cohen, and Stefan Hermann. Many [fantastic people](http://fritzing.org/about/people/) have contributed to it over the years. The Fritzing app is written on top of the [Qt cross-platform framework](http://qt-project.org). # Licensing The source code of Fritzing is licensed under GNU GPL v3, the documentation and part designs under Creative Commons Attribution-ShareALike 3.0 Unported. The full text of these licenses are shipped with this download. This means that you can create your own variation of Fritzing, as long as you credit us and also publish it under GPL. Similarly, you may re-publish our documentation, as long as you credit us, and publish it under the same license. You may publish circuits and diagrams that you create with Fritzing and that use our graphics, again as long as you credit us, and publish your works under the same license. A credit can be as simple as "this image was created with Fritzing." Look up [our FAQs](http://fritzing.org/faq/) for more details on licensing. fritzing-app-0.9.3b/resources/000077500000000000000000000000001272363601300163145ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/bins/000077500000000000000000000000001272363601300172475ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/bins/icons/000077500000000000000000000000001272363601300203625ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/bins/icons/Arduino-mono.png000066400000000000000000000005361272363601300234430ustar00rootroot00000000000000PNG  IHDRDz56PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCI tRNS@`0 pPibKGDH pHYs  IDAT(R0#!/46j+u0<cP)BĹLX$a%cw; H5[:і7VԦ]<ѕ)Qᅨ% SmpI y&ڟ<{w[=iS묜^x[qGGk?g>Fo IIENDB`fritzing-app-0.9.3b/resources/bins/icons/Arduino.png000066400000000000000000000005441272363601300224740ustar00rootroot00000000000000PNG  IHDRDz56PLTE&tRNS@`0 pPibKGDH pHYs  IDAT(R] кI=DyD|SNNS  oZQnWl7զ]yzSW;Q`O)`_VQ:h:Jv^reN~yLhGS׹]μxa{b/f[wԿȨ+W>̍`(X4u\g [ -dY<C{ɕIENDB`fritzing-app-0.9.3b/resources/bins/icons/Core-mono.png000066400000000000000000000004361272363601300227310ustar00rootroot00000000000000PNG  IHDRB`4-PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBC 0tRNS@P Ͽ0p`bKGDo0OIDATc`r@HIHɀYI)%QP]P(%!PV f`he`XP^^^ 10(24H3@'p @ F >Cy R R[Hv1IENDB`fritzing-app-0.9.3b/resources/bins/icons/Core.png000066400000000000000000000004401272363601300217560ustar00rootroot00000000000000PNG  IHDRZebKGD̿IDAT8Au0߾0CPT R.)e0P.c4OY6lxXOXywYR^E) |kt^:tnv+eJI:~5beYҔJtzÆ9[?IENDB`fritzing-app-0.9.3b/resources/bins/icons/Custom1-mono.png000066400000000000000000000003561272363601300233750ustar00rootroot00000000000000PNG  IHDRB`4PLTE0%0%ooo000___tRNS ;R\bKGDޕz pHYs B4\IDATc`;` AA(_4 |70/S`|07-]7|7(?grR!|V?<~ GwE'O3dIENDB`fritzing-app-0.9.3b/resources/bins/icons/Custom1.png000066400000000000000000000003761272363601300224310ustar00rootroot00000000000000PNG  IHDRB`4*PLTE0%0%0%0%0%0%0%0%0%0%0%0%yD tRNS P@π`9bKGD a pHYs  UIDATc`>pc8?rgN @ 3P~j{m U]?C?ޫYsr)IENDB`fritzing-app-0.9.3b/resources/bins/icons/Lilypad-mono.png000066400000000000000000000006441272363601300234400ustar00rootroot00000000000000PNG  IHDRZebKGD̿]IDAT8Uɍ0 R N v4}a9lӤxPYu{TRyggt'bIf (i~xD u'91h\T`k(_*8=7CͺVC&e~]d]Oq_J&sMdԙ%s.44F9TzsSV_#?3 @cb>4xmCyp3 HfR"8[vЮJպ-3i}.^o9WFkYoҙ#r"IENDB`fritzing-app-0.9.3b/resources/bins/icons/Mine.png000066400000000000000000000004371272363601300217640ustar00rootroot00000000000000PNG  IHDRZebKGD̿IDAT8q0/ pq .A t@:pҁ@.p`E 9A}zZ𴼔 6~%]'=:Ʀ( b I/ GYcpҢeY_? C'ȹ<<{qk=Mvvwmp1;MWqjSZ֔ၕ(kTXݽjveX;V5Ms؈: jlʞ2XXxf,PIk}RIENDB`fritzing-app-0.9.3b/resources/bins/icons/PICAXE-mono.png000066400000000000000000000005261272363601300230120ustar00rootroot00000000000000PNG  IHDRDz5?PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCbFtRNS@аP0 `␡{-bKGDH pHYs  IDAT( `F47JFme?p͗ ;Ұ`#s%+$qߔpC2 vIENDB`fritzing-app-0.9.3b/resources/bins/icons/PICAXE.png000066400000000000000000000005271272363601300220450ustar00rootroot00000000000000PNG  IHDRDz5?PLTE")")")")")")")")")")")")")")")")")")")8 tRNS@аP0 `␡{-bKGDH pHYs  IDAT( `47JFme?ۛ i+[%,X&dLJ;#pOgy 8IENDB`fritzing-app-0.9.3b/resources/bins/icons/Parallax-mono.png000066400000000000000000000004531272363601300236040ustar00rootroot00000000000000PNG  IHDRB`4-PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBC @ tRNSР@p`0 ]4bKGDo0O pHYs  ~IDATc`7`!H - j}|q(}g 7Y=,^ꖛ |}Qgu]k3 ᯼bK#>7XgK3=CE8LAIENDB`fritzing-app-0.9.3b/resources/bins/icons/Parallax.png000066400000000000000000000004531272363601300226360ustar00rootroot00000000000000PNG  IHDRB`4-PLTE()[()[()[()[()[()[()[()[()[()[()[()[()[ tRNSР@p`0 ]4bKGDo0O pHYs  ~IDATc`7`!H - j}|q(}g 7Y=,^ꖛ |}Qgu]k3 ᯼bK#>7XgK3=CE8LAIENDB`fritzing-app-0.9.3b/resources/bins/icons/Pololu-mono.png000066400000000000000000000005521272363601300233120ustar00rootroot00000000000000PNG  IHDR"tRNSv8bKGD̿IDAT(ݑ.DayB(J6يV5p@q Z"q6*HV8笳iy*Q #J"SĖUOdiUNi\%qؾ/>-_^:Wbl]Nzݣ碎#fXWEԸU`qy]p29w'^a0h2|Ž=u`m-IENDB`fritzing-app-0.9.3b/resources/bins/icons/Snootlab-mono.png000066400000000000000000000005671272363601300236270ustar00rootroot00000000000000PNG  IHDRDz59PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBC!LtRNS@ pP`0bKGDH pHYs  IDAT(͒ DA?ۈe:܋1Y}~4G>
GtC'8Yy$mp*^mârɉczSna;\en%tp5_7IG?XyQ[,na};f<IaǛK0_ƙ6lL*C#\z= IENDB`fritzing-app-0.9.3b/resources/bins/icons/Snootlab.png000066400000000000000000000006561272363601300226600ustar00rootroot00000000000000PNG  IHDRDz5]PLTEg(&3Ѡ{tRNS@ pP` `Pp@0@0bKGDr + pHYs  IDAT(͒Q A=1L3-u3F-}cCJ~IzPh)aR {Th KAs9QXUnmE#&C>'"C){y@AAX'GW,݊ikmk ͭހ zk;]ڧ xrS8Uxw&$gCϣ@ŹIENDB`fritzing-app-0.9.3b/resources/bins/icons/SparkFun-mono.png000066400000000000000000000004621272363601300235710ustar00rootroot00000000000000PNG  IHDRDz56PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCI tRNS@P` Ѐ0pgPbKGDH pHYs  yIDAT(K Ђ? s LΒ4&;'3pB*V>|W[ 3zA5(y xNX6xMbqrk!LXqc|U0R[ \jIENDB`fritzing-app-0.9.3b/resources/bins/icons/Temp-mono.png000066400000000000000000000003771272363601300227520ustar00rootroot00000000000000PNG  IHDRB`4-PLTECBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBCCBC 0tRNS`P0 p@q2QbKGDo0OfIDATc`j@HIYI5UIȏ`Z](tyS8 ?Zri\ϳjS?y$QlIENDB`fritzing-app-0.9.3b/resources/bins/icons/Temp.png000066400000000000000000000004251272363601300217760ustar00rootroot00000000000000PNG  IHDRZebKGD̿IDAT8MU@{HD*!U@$EA) a]]}wP+Lf (mO`1}7u(JA̺3 ( "5z9~C#f4s6J>I"bjD=l/*In%)_Vz˵J/mxebQoIiwGz:ΛTk쬬^yR? 'AIENDB`fritzing-app-0.9.3b/resources/bins/my_parts.fzb000066400000000000000000000002401272363601300216040ustar00rootroot00000000000000 My Parts fritzing-app-0.9.3b/resources/bins/order.xml000066400000000000000000000013141272363601300211030ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/bins/pe.fzb000066400000000000000000000073521272363601300203650ustar00rootroot00000000000000 Parts Editor Parts fritzing-app-0.9.3b/resources/bins/search.fzb000066400000000000000000000002401272363601300212130ustar00rootroot00000000000000 Search fritzing-app-0.9.3b/resources/bins/temp.fzb000066400000000000000000000002341272363601300207160ustar00rootroot00000000000000 Temp fritzing-app-0.9.3b/resources/fonts/000077500000000000000000000000001272363601300174455ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/fonts/DroidSans-Bold.ttf000066400000000000000000005650701272363601300227450ustar00rootroot000000000000000GDEF&?4GPOS?TGSUBlt LTSH[[ OS/2`cmapԢKcvt KRQ\fpgms#Sgasp?( glyfA f "hdmxt8Hhead9d<6hhea *t$hmtxHUlocaS_maxp nameh0postP'!?prepeq֊Zbx_<2S =  m  y y/ZT33f @ [(1ASC m ^ Ju+-hb ?R!R=\?hXR?=HuNh?h\hNh9hhVhLh7hHh?HuR?hXhXhXf3#w{dwB9HND w w 1^d)jP1N 3BJLVfff)jqqqffybP/Psb P7hhXJuhhRh\hhjd/RhX=dm\hX/;L =qHu\9T . . ZB333333`w{{{{*B6/D w w w w whm wsVVVVVVVfffffqqqqJfffffhXfPP3V3V3Vwfwfwfwff/f{f{f{f{f{fwjwjwjwjqBq!qBqjBqB9qHqHqxHqHyHDDD wf wf wfwmf y yq yS1^b1^b1^b1^]d)P/d)P/d)P/jsPP17P17P17h3V`V wf1^buqT 3Hu H^J3#+9{P1 wBND?R wmNd)\m76f3Nf Nf3Nff ffyff%)-fwfw{)+w1^B69)3#+ {9^DDN wwd)\mNH=Vff3Nff//PZfb D{{JffbqqqP#TjsjsjsPRRRJR?%?!{5{bu ?!RRu w9fh#hRhB?)7f : ; Z C;9 )hXd%qLhXhXhXhXX))hq^^N) T-;-+Uh%TTq)3N3V^rXy))Hw;f\{DfbwH))D)3m wfjj wf^wFfbwfbwwfhu))-/'/7^3NRhZw fwfd)//ZZ )/m{m{DDB9 bm{ ?B3V3V`V{lf7f7f9^3Nh9h9DD wf wf wfHJPPPm{''/ b \f\/fNJ N=`w%f)/X5N;3V3V3V33V3V3V3V3V3V3V3V{f{f{f{f{{f{f{fBqoBq wf wf wf b wf wf wfHw;fHw;fHw;fHw;fHw;f\\\\\PPPf- -11111 V.     -  &&&&&'$0 0   ----&&&&& & &&&&&&"'    ' ' '$$$------  $    (" 0$$( &( ( &$.&&*.'&.      ,*"&+. "2(&(,. $. .&.&- +# #(*-  $+0&$&   '& +&&&&&&&&&       (((((--&                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           ~ # O_?M   " & 0 3 : < D  !!!!"!&!.!^"""""""+"H"`"e%  #P`>M   & 0 2 9 < D  !!!!"!&!.!["""""""+"H"`"d%aI1vhcb]gD Xz} Bvt %"iOS`xlhijkopqrstu45]^   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a rdeixpk vj0-s12gw'*).l|!cn,B/(m}b  3:45y KRqNOPzSQL~ # O_?M   " & 0 3 : < D  !!!!"!&!.!^"""""""+"H"`"e%  #P`>M   & 0 2 9 < D  !!!!"!&!.!["""""""+"H"`"d%aI1vhcb]gD Xz} Bvt %"iOS`xlhijkopqrstu45]^@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@3 P07GW/@P0%5UeWfF@? F33U3U0@`_ 030@`F趴 F@ F/?З/?OόgFt&n6nUg fffee dd'^7^&]\Z[Z&Z6Z3UU3U/ WW+WWV"V2VVUT@!T F SSFO7OFN7N[k{ FH FGF@1F F3UU3UUGU0oTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssss+++++++++ssssss+sss^sst+++s++ssssss++++ssssst+s+ss+s++s+s++++sss+++++ssssssssss+^ Nu^{18L+ff6?/!ww6? ^}TLyJx71%U****L ?gb]r :  Y t F $YKz!`B0GvNp&qg3z Y r ! >>??+?C?Z?s?@@@@@AA'A>AUAkAAAAAABBB)BRZqȿc #=V !8Qiƒœ |Öî(@YqĊĢ/ASi(DžzNɲ-ʖD˧Y̞͐ͤ͸1Jd~Ηΰ%>WpωϢϻ0H_vЍФ*C\uюѲ1H`wҎҧ &@Z~ӢӺ,D[sԋԢԹ/D\tՌգջ.E[qygع ك1aڑ +@  p@  H?2/3/+]33/310!!7!!IhyJhu]@B/?wfxi ` ?//]q3]]]]2]]]]]]]10#!4>32#".3Z0@%#?00?#%@0/A((A/-@**@B/@ _ ?33/]3/^]]9/10#!#))))-@P!   ` ` p  H      T 0 @ [?O   /3?399//]]]]]]]]]33]2233]22/]]33/3/]9293/]]92393/3/10!!####5!7#5!333337#/MNLJ/!MMNN/Ljjiib%3<C@F&.@#@@?@@:0 44+4{444)OE==$=t===@? H-*AA&9.@@@@&%@H%%# &@&P&&&&&:P`@/]33/]333/]]33/+9]]333333/+]3]]2]9/]]]323]]3210#5.'&'&&'.54>753&&'4.'665%5ifBpbY+*cjm4 [[-9j_WdeA>'_^. +;84967K`>  &>#L^rHK{[9 (+)#J\rR$ </?0? "-A1 H?@ H: H5 H@$ H H( H H#8.Y""V  !!@#!!).C +=&3"!?/??/99//883]3]10++++++++32654#"#".54>32#32654#"#".54>32;-21/`2-)V[UW,(UZVX-+p-21/`2-)V[UW,(TZVX-}|{}lv??vllu>>uHJ}|{}lv??vllu>>uR-9I@A'G():::HG#$-&DDDH67;K(( @" HK.G 67??''3-$#G3??3?99/39/3/+3]9///]]]]9]9]10!!'#".54>7.54>32667!%32674.#"66aPtxËL%Da<&5 =mZVi<-Lg;#5=)6F+ 9M-73#.R$JqN$HjENqJ$1}]2w^Z=d@  ??210#>54'3d$JqNEjH$NqJ$1|Z^w]?V#@@1?2/]/]]]10%'%7)u!㜉'm)hy9w)hpX @  /3322/222210!5!3!!#ondq? V@A/? < L y   *  &6F/?O @ H /+/]3]2]]]]]]10%#>7!'/36z|{8=}5=V@ ?/]]105!=u9&@/? `//]]]1074>32#".u0@%#?00?#%@0/A((A/-@**@D(@ ?//833/8210]]!D!J?)!@n#n tt??/10#"&&54663232654&#")7y|=7x~~>JVjh[[h5I.۱ffgf@~\1%@n `??/]]33/310!!4>7'31 NIOP! wN'!/@!n#@P`   ?2?39/]33310!!5>54&#"'>32!'+XAjL*YKOP-bvXiv?54.##532>54&#"'>321UsCEٓvZ-dda+VrD%Sbhf\zIai0SG;*ctLl~EoLy[=`xC'($ :Q0-I3!9L+NX#4'/Y= 8@ n u ??39/32/29/]]332210#!!5!3!54>7#=m# -//i 1>B<- *^/V(b@#%&&&&"$! H!{!]!m!!!n*@ Ht %s"t ?3?9/]/+9/]]]+933]3102#".'32654&#"'!!66V^xDJՊ7lcY$#\cd-;94{7 U:plwF  #oylq BL+);M@/" ( !  7/n =7nP`2u*t%u??9/]3/]29/]10]]]4>32&&#"3>32#".2>54&#"L;eّ230&U+f+ 9L_;_i8C|nlO)C1Y[.L63KmiпyE Cxf$?->vowEM?`Bk{$:H%3eQ27'-@ s??9/3]/]2]103!!`H!':N@j  %  #JJn00nP@@n######((nP` 66EE4EDEdEEEE-v;v??9/]]]]99/]]3/]]]]3/]]]]99]]10]]]]2#".54>7.54>32654.''">54.5[zH(F`8:oV4Hmv~A,Lf:1V?%I|v3L2ih#7F#,H3!9)+9 8+*:,XYBkWDL_vI[h86d[Kx`JIYlAWY,(C0cQ*C903=H8&8#*=/%&1>(#8&?);G@* "('  7n=/n 2u*t%u??9/]]/29/]10]]]#".'532>7##".54>32%"32>54.;eْ230%U,f+ 8L`;_i8C|nlP)D1Z[.L63KFiѾxE Cye$>.>vowFM?aBj|$:H%3eQ2us'0@)))/)?) `#/?/]32]]1074>32#".4>32#".u0@%#?00?#%@00@%#?00?#%@0/A((A/-@**@g/A((A/-A))A?s k@P < L y   *  """"/"?"  `  &6F/?O @ H /+?/]3]/]]]3]]]]10%#>74>32#".'/3/0@%#?00?#%@06z|{8=}5/A((A/-A))AX`@A4Ddt5E&/O:J) 0@/]q33]]/]2]]9=/33/210]]%5H}X2@ II @/]]3/]]/32105!5!XH'{X`@A;Kk{ 5E&/O:J) 0@/]q33]]/]3]]9=/33/310]]5X}H=Ju';8@2((' H= -7M?3?9/9/3/10]54>7>54&#"'>324>32#".+D0*:$MOEUf+emp6fr=7S7*5 0A%#?00?#%A0J3SKG&!438%9J:*-#1^V?cUO,!1,/ 3232>54.#"32>7#"$&54>3232>77&&#"-\^&C8) 2>K,SX.>tg-`YN #4"LmQFʄ8vuq3^債h6f⁞ l>L?*=' 6;R3_[#//$9iYg}E  ]**6 6\{EȊHfՑK"*1g|ßq=epc'Gc=2Ri3@ &6Fv@?H )9IyH `p@_?2?3]9//83]]]3/8]3]933999910+]+]!!!!.'de{^  ]\D`@Rcd#54&##32>54.#ЌG:S67_G)Fv6:N0ir=S35V@'Wg>lR7 -OxVdm:s*?*TI4J-)C0w#B@+e  !!Z!j!! g%%%`%%[f$_ _??]]]]210]]"3267#"&&546$32&&%Y]0+Y`Yi0^bg;NZmddREsu}A(% lo70':# &@ Zg?Zd__??]10#!!24.##326#ec1]Wr^\zt8H F@*g  Zd _L; _ _??9/]]]]]]22210!!!!!!!J W@:g  \d _oL;_??9/]]]]]]]2]]210!!!!!!Fw''D@('' %\)`)p))[ f('_+"_"_??9/]]]]29/10!#"&&546$32&&#"3267!D:vNZdu]gD^ft>+\eB[(5  ai2(".GqlH 1 G@+ Ze  Zd _L; ?2?39/]]]]]2]]210!!!!!!!65w=B ;@#  @ H0  Z _ _?2?2/]3323]+]10!!57'5!gRRRNR9R4@# Zeo '_/]?]2/10]"&'32>5!Ab"%Q0.O;!6IR  1R>~w8 r@N9y  I Y  9IYy di  E U  Zd $ ?3?399]2]]3/8]]33310]]]]!!!!7!t6zNX-`V@m)@/?Zd_??]]103!!6J H @Y HG6'I; ) H] eX 6 F   '  ^ d  ?2?33/3]]22]933+]2210]]]]]]++!#!!3!!4>767# <P{\V%NLFX^JBJL$T[@, H9I +I H^e  H @H6 F   $  F  @ H ^ d  ?22?33+]22]]++]+]22]]+10!!#!!3&'.5!w >RMLA9PLJ CC>w'(@[g)/)?)[ f(#__??]10#"&&54663232>54.#"OOOO (S~WYQ''Q~XWS(ݩllkksDDssDDm=@&Z ?_Zd__??9/]2]10]32654&####!!2=wO:٠Gͅ@humh`P?uw/W@8&6&[g1/1?1[f0;K[)  +_!_ /??9/83]]]]99]]10!""#"&&54663232>54.#"'OwQir OOO (S~WYQ''Q~XWS(wѬ,mJlkksDDssDD ~@P  ZD   Z d gL;` _  ?3?9/]]]]]]]92]3/8]39/]9]]310]]]32654&##!! !Tpx~O (CW0oX&G8-gdhXyKz_G54.'.54>32.#"CyjT0bee23I-#?Y7.reDAxj5ecd5d-NJG$NS7W>K~[2bo<,, +")9")?74Dedbk8&  SE%924!(Se);>@' o 0 @  Z@_??2/]3/2/]]]]]10!!!!! 0@  ZeZ d_ ?2?]10]]#".5!3265 EԏϋH5 ?^?uNrĐRMziQsI"X@+   `po@   ?2?3]/83]]3/8]3]93310]]!!!>79899JP``!!`_Pj6&@m 5)595I5f%v%'%%i$y$($ $&6Fk{ H dt6FV%k{9IY* dt@> H-V%%%%Y$$$$  -556`6p6666688@&5$ { , < L    -?23]]3?3]]]]]33/83]]3/8]3]9333]]3]]3310]+]]]]]]]]]]]]+]]]]]]]]]!!.'!!>7!>7!    1        16DJF;;EJD81=LTPEDMRG7 3 7GRMDEPTL= |@8    `p o @  ?3?399]]/83]33]]]3/8]3]933333]10!!!!!cV N^)+q@  0  ?@dtZ0@ H?3?9/3+3/]92/8]]]]33/8]3]]]10]]!!!VNDDP\Z/1 t@1iH  @ ` p  P O f@HO __?2?23/]33]+]]]]3/]33]+]10!!5!!!k}s"@??/]210!!#3s B(@ ?//833/8310]]!!!J3 @ ??3/2103#5!!3qT=7@+   ?3//23/39=/3310]]]]3#EDJNH//3/10!5!NRLP! *@E _o_/]2/]/33/]]10.'5!"_\LV+.0SXQ"QQLVu.a@>    @ .FU0O0000&G0  Q:N)N??2?3]9//]]]229/]10]]]!'##".5467754&#"'663232>5); !BNa@DtU0PHHEcTpe=T3D7*H5-A*+W[ TEB*#/6/A(F;9S6w08@ .GW2&FT1+M  M?2?3??2210]]2#".'##!3>"32654&Vf99hX8WD331 6GZ03G,,I6[UUsJ؎ْJ(3{!M!''#<-%JqK!Q~U,fs<@&   !!/!O!o!GV M M??]]3/]310]]".54>32&&#"3267qxIKvVKXBz7oddkWJ%FGMBٗ:*&%-# f=08@  %HU2.GV1+M  M?2?2??2210]]".54>323&'&&5!#'#72>754.#"Vf99iX6ZH9 1; 6GY76L/.N;`Z[J؎ٓJ-;#'("M!f"=,%KpK!Q~U,fDs)e@A  %%% HW++0+FV*9I(P|ON ??9/]]]]]]2]]]9/]10]]"!.".54>32!32>7dQk0H xʒQJro}CV%C_=3[VT,(QZhrz3V?$RFבܓJCz@gG& ! )HJ@,  H/FNM ???32/]3/32]]3/9/10+#!#5754>32&&#"3Ϩ4`U\~,HD.<1yyRRkT# M7.54>7&&54>3232654&##"32654&#"=6jj6 *8QY0Lrr9*FY/)!$6"Xg8lh21* \0N9bg>4#nDEH@?I\3"I*Ud7%%LuQ_k9+QrH=Z@( *3 4-(&rZc4/$XJ?* 5f[dd[Zjj6@!FU0 F TM ?2??22]10]!!4&#"!!36632jKN;P011`S`4yy0^Y*]'.,WM/dl/@0` FTS??3/]22]]104>32#".!!-=""<--<""=->1+9##9+*:##:^'E@,  FU))0)`))S### 'M?]?3/]]]22/210]"&'532>5!4>32#".f0f"6"-!1'W6-=""<--<""=- 'A3)Me:k+9##9+*:##:n  H@6 H0$ F T H ??399]+?23/8]]]33399]++10]]7!!!!pXlw1`TRJ @0`FT??]]10!!!1s*q@4  Fy8& #F"U,_, F T+@ H'M # ?22??322+32]9/]]]]]]]210]]!!4&#"!33>323>32!4&#"`HM:M.)CPZ.s+DR[.HMm\yy0^Y^+>(OU+>('yyjs;@$EFU0 F TM  ?2??22]10]]!!4&#"!33>32jIP

(/dlfds 6@!  G W!_!GV MM??]10]]]]32654&#"#".54>32^ji^^ki]GwoLGxoL1ؔMM،ؓLLws08@ .GW2& F T1 M +M?2???22210]]".'#!33>32"32654&7WD4+6GZ7Wf8:i3G,,I6[UU(3#7;J"<-J؎ٓJ%JqK!Q~U,f=s08@  !&F#U2GV1$" M,M?2?2??2210]]%2>754.#"".54>3237!!46767#Z7K./M:`Z[Wf89iX8ZI8 6GZ%JqK%Q~U,J؎ٓK-;#9 !"=,Hs@@! HFFTEU ?3]??2]]3/+]102.#"!33>  ;cG'-8EWsCmO^+F1bs7c@ !!' HFW9909.@ H.F V8.+N( H($N H ?3+?3+992+]]+310]]#".'532>54.'.54>32&&#"@vh7^TN(*]\W%):% .YKIkE"7*L=GrR,LXX,"%!%/"!APgGNuN'..$.,&!'=Nh/L\  H@4 HO_oF _ o  NM??332/]33/2]3/]+310+%267#".5#5773!!f-Q*+KI~\5X@"UlfA>d^;@#J FU0F T  ?3?2]/]210]]!'##".5!32>5!{)ER\0R`41IP

7!3>7!V   Z/q  zPu  u+?OY,gwwh,ZO@}^'moc=FI@1 \icbqp' X^ @[   iZ9I( FVf'7  @ H  /   @ Hi Z 9 I *   @ HfT6F$  ?3?399]]]]]]+]]]]]]+/83]32]]3/8+]3]9]]]]]]]]]33]10]]]]!!!!ZZ};#PjP^@.8 7  ) 9     @, H  p  &6@ ?2?33]]3/83]]]]]]3/]]3/8+]3]]]]]93]]]3]]10!3>7!#"&'532>77N PF>֡4L@#0D1# ^4v/8:9u /B)87m^ |@IYiH   @ H   @ HFVf@H@ HNN?2?2/+]3]+]+]]3/+9/33]+]10!!5!5!!mVFʹQ(N@2 ( H% H!0''oI!"??9/]]]9/]32210++4>'4>3".5}>aB!&c(A-sz-A(c&oR+D0>JiC  6+ջ# n^+6  CjJ;/@ 0@p?//]103#!(Rس H@) H %(%$o$$$I$$$??9/]]]9/332210++#5>5&675&'4.'523"&c(@-zr-@(c& Ba>}-JjC  6++^n #++6  CiJ0D+RaX'}$\@,(H %  @ H&   H @ H ?_@H/+]33/++]3/10+]+].#"5663232>7#".%9/*><93N39F0&90*><9e39Fh !,67  -m u^i@J[/?wfxi` /?/]3]]]]]]2]]]]]]]]10_]3!#".54>323^0@%#?00?#%@0^1%/A((A/-A))A)c)@ %%%%++++n@ H**?*!u(v  /]3]/3]+]]29/32105.54>753.#"3267\h88i[&LG?V587B\;rL23|E KljˈK    (SW%RB&@j $$*$ 'Wn!k{:   @`c @w  Gst?2]]?]29/]32/]]]3/]399//]]]]3]]]]]210]2&&#"!!!!5>55#5354>nP]Gu?CKN,5*C/?o0"#M_ۏ7Q:(*:R:qd.\ "6@_54.#"+f36`/},c68c+}53D'(F55F('D36c,*g68b-}}{}[j'E33E'(E33Eb@ O@<  @ n  @  @ H ?3?99//]]3]+3232/]33]2]292/8]3]933/8]3]9310!3#3#!5#535#53!59<\Zݲ/$@0@p?/99///]32103#3#j)CV@51E11&120B00$0:J))@ H  H$@V H$H'D HRMM(MM!!:'XoXXXX@ HDD'DD00?$HRHRHR 05, ?2?299//3333/]3/]3/+]23/]99+99+10++]]]]]]4>7&&54>32&&#"#"&'532>54.'.7>54.'y%0?F:k[fURDNQJ4Q8GuS.E8>?>renF'Z\Y'8J--QBLxR+qv 4[F %,K?2(vKAkL)/% 3.0*''DSe>d{%(iJJwT.)&# *'&* @Qf[?a3 $, 631$,%%@ #/]]32/2/104>32#".%4>32#"&%33&&33%&43''3>2HH$?]?]99//]]/]]99//]]310]]"3267#".54>32&4>32#".732>54.#"aj`k999vMkk64iiSDJq}6ahha66ahha6`ޥ``ޥ`򔃇D|jg{D,":ha66ahhb55bhޥ``ޥ``/.@ H  H H@D H .00000000@ H%O _  @H .((??2?39//+]3+]22]10]++++'#".54>774&#"'663232>55(qD2Q: +QwKZ;6(b4B>[DeB!&/ & 3$n:@7T9F="&$'7 $R^\ [@2 / _      ?322322333]]2233/]33310%R55=wwwwX?@ /3/10%#!5!#l=VdD 4H@W ?@ `  ??'J55     p     ::.DD ?]?]9///]]93/]]99//]]329910###!232654.##4>32#".732>54.#"MB/B9/ `6ahha66ahha6`ޥ``ޥ`^npR9B#. ha66ahhb55bhޥ``ޥ``?/10!5! \'@ )#?104>32#".732>54.#"\6^~HH]66]HH~^6*9 9**9 9*qG~^77^~GH~]55]~H8**8 9++9X D@(   @ H/+]3322//33223310]!5!3!!#5!onq/JR@3 H H   / _ Ee ?2?3/]33]]2+10+!57>54&#"'6632!y.=%0((W5{AmBmM+6T54&#"'6632QY3J1LABIJE&@0p\4@$ 23/T9e>g>jM,Ed *7B$y##(265&'&2&(/>!7!L0/*V *6>?:LQQ"18;82j^9@"B F U0FT M ??2??32]210]32>5!#'##"&'!!KQ:N01+ #iK6Z1yy0^YUU.,*+%T$Jq5@ H   /2?9//]9/+10####".54>3!=U_m32#".u0@%#?00?#%@0/A((A/-A))AK@0( H??//9/]9]/]3/33310+#"&'532654&'73HwW-H%'%+J\N:-9Z>! #%9= "/=\JH7@$/_p ??3/]33/3]10#4>7'%3HNm-J=>4 =9Y H @5 H H H!!!!!!!!@ HO _  ??/]+]10++++#".54>3232654&#"-TvJEuU/-SwKDsV0L7><77<>7\W]11]WW]00]WdeeddccT^^ ]@4     /_   ?322322333]]2/_]322310'7'7^55#;w\\w9;w\\w9.'&{<*@`p`]5]5]55?55.'&{t0@pD`]5]]]5]]5?5Z''<u @P0]]5]55?55By^';C@) 2((' H @P` -7M/3?9/]/9/3/10]3267#".54>7>55#".54>32+D0*:$NNETg+fmp6fr=8S7)5 )0@%#?00?#%@0^J3SKF&!438%9J;)-"1]U?cUP,!1,0;V/A((A/-A))A3s&$CR&#%+5+53s&$vR@ &V%+5+53s&$KNR@ &*%+5+53`&$RNR@ &)%+5+53V&$jLR@ &&5%+55+553 &$PLX @4.34!%+55?3/559@%FF Zp g@.H_ _  L ;    __?2?29/]]]]]]2//3]+229/]332399]]]]10]!!!!!!!!!!#3X[\a\`Nw&&z ,$ %+5s&(CR &%+5+5s&(v\R@  &M %+5+5 s&(KR@  &%+5+5V&(jR@ & *%+55+55*s&,CR &%+5+5Bs&,vR@  &` %+5+5,s&,K@R@  &%+5+56V&,j>R& *%+55+55/#g@BZg!?!X;Z'd _oL;__??9/]]]]]]]322]2]]]103!2#!#%4.##3#326/ce1]WrRd\^Tzt8`&1RR&* %+5+5ws&2CTR(&-4 %+5+5ws&2vR@ (&I(/ %+5+5ws&2KR(&/; %+5+5w`&2RR@ +&,: %+5+5wV&2jR7&(F %+55+55m  n@D V%5Y  * :  V%5Y*: @ H_/]+]/10]]]]]]]]]]]]]7'՗-1-ӕ-+ј-՘w$/2@(%[g1/1?1[ f0'+ +??99]9910#"''7&5466327&#"4'32>OHRa]O[AFP^] /EaWS(+9"P0YQ'ݩl=u^d(k!o`cݸs+DstD s&8C=R&$ %+5+5 s&8vR@ &_ %+5+5 s&8KR&+ %+5+5 V&8jR@ '&6 %+55+55s&<vZR@  &R %+5+5mH@,Zg?_ Zd` `   ??99//22]]10]##!!3232654&##m3u6|u9Tyxjsh^OByizlg+Co< H H@7 HF11GHF**9E  GWEE8F9TD 4M?9O?3]??99]]99]]]10+++#"&'532654.'.54>54&#"!4>32+?K?+5R91L5b<ELP"PX)JI>)dalkJusH@aL:0*"(3&AM\<"=>**1"$@?E(5N>45>(?Nahsmj4+SzV!&DC/&Ǵ4; %+5+5V!&Dvm@ /&/6 %+5+5V &DK@ /&$6B %+5+5V&DR@ 2&$3A %+5+5V&Dj@ >&+/M %+55+55V&DP@ 4&%9/ %+55+55Vu4CL@^   G'F55 *H HHH/%WNONoN;G VM59'I'(''PGGG|GGG,(DDO (N>N,N/?2??]3?]9/]]]3]]22]2]]9/329910]]]]"&'#".5467754&#"'6632632!326732>5"!.E+Tb{RD{]6PHHEcTpm~o|CV%C_=^X(QZhe=T3D7*H5Rk/Hei6M3+W[ TEB*#/6Cz@gG&+- /A(F;9S6rz3V?$fs&Fzh@ P((  %+]5fD!&HC*&/6%+5+5fD!&Hvs@ *&l*1%+5+5fD!&HK@ *& 1=%+5+5fD&Hj@ 9& *H%+55+55!&C& %+5+5!&vE@ &Z %+5+5!&K& %+5+5&j&"%+55+55JH#'7v@Q $  (GW99/9_9990GV88!--N55N_K/?]]]]3?]9/]]2]]]]]10]]]]&&'77#".54>327&&''4.#"326"N*`I9dHlG$HvoLAua` eBe1K3l]1L5j\0"E&jEyܘODzzC?1X8h/VB'?hJ)j&QR3&- %+5+5fd!&RC &%, %+5+5fd!&Rvf@  &O ' %+5+5fd!&RK &'3 %+5+5fd&RR#&$2 %+5+5fd&Rj/& > %+55+55X+`@7""=M} +- '?_  H//+]/]]/]]]9/]3]2105!4>32#".4>32#".X&32&&23&&32&&23&d*:##:*(:%%:*:$$:*(9%%9fd#,@S''w''#'3''&,< % '$$8$H$$GW._.7GGV-@% H& H&*!!8!H!!M**7*G**M?]?]99++]]]9910]]]]]]]]]]]]]]#"&''7&&54>327&#"4'326dGw9h09DEOGx>s31>?F: (9i]%i^1ؔM^ZoJۏؓLO`bHхVA@1Nd!&XC& ' %+5+5d!&Xv@ &P" %+5+5d!&XK)&". %+5+5d&Xj/*&9 %+55+55P!&\v=@ &c& %+5+5w$58@  3G W7+FT60M%M?2?2??2210]]>32#".'#!!"32654&7HY7Vf98fW7ZG613G,,I6[UU#<-J؎ٓJ&2 4;yB#%N%JqK!Q~U,P&\j@ .&= %+55+553&$M`R@ &%+5+5V&DM@ 1& /0 %+5+53}&$NLR@ &!%+5+5V+&DN @ 4&/9/ %+5+53&$Q= ++%+5V u&DQm CC%+5ws&&v3R@ $&$+ %+5+5f!&Fv@  & ' %+5+5ws&&KR@$&@+^+7 %+]5+5f!&FK@ &0'@'`'p'6'3 %+]5+5wf&&OR@ 3&r$. %+5+5f&FO)@ /&Q * %+5+5ws&&LR@ )&d/$ %+5+5f!&FL@ %&E+  %+5+5#s&'LTR&#%+5+5f&G8;@ 6\<<%+5?5/#f'8q@G  #.F:6GV9! 3   #(?2?]2?9/]]]3]]2?]329/10]]".54>323&'&&55!5!5!3##'#72>754.#"Vf99iX6ZH9 ;1; 6GYC4G,,I7[UUF̆΋F,<#((#Q$3ǡT"=,"DfEJrM(&(M#R@ & %+5+5fD&HM'@ ,& *+%+5+5}&(NR@ & %+5+5fD+&HN@ /&4*%+5+5I&(O!5& %+5+5fD&HO!@ 9&*4%+5+5&(Q!!!%+5f(Ds&HQ9??%%%+5s&(LR& %+5+5fD!&HL@ /&5*%+5+5w's&*KR@ (&V/; %+5+5=!&JK"@^&`ePe@eeq%%+]]]5+5w'}&*NR@ -&G2( %+5+5=+&JNc&`hִh^%%+]5+5w'f&*OR@ 7&Z(2 %+5+5=&JOm&^h%%+5+5w;'&*9 R(. %+5=!&J:7@ c&d^%%+5+5s&+KR &%+5+5j&KKJ@ &%1 %+5+5f@:ZeZ d    P  _L   ?3?399//]33]]]22322]]32210#!!!#535!!5!5!͹ʸ65w-j$m@G"" FU&&0&& F T%    PM  ?3??]29/]]3]]]232]910]!!4&#"!#535!!!36632jKN;P0Ϝ1;1`S`4Pyy1^Y/ǡ*]'[WM/dk`&,R@R@ &%+5+5&R&%+5+5B&,MUR@ & %+5+5n&M@ &%+5+5!}&,N>R& %+5+5+&N &%+5+5B&,Q%+5j&LQ` --%+5Bf&,OPR& %+5+5^ @0`FT??]]10!!!1^BR &,-@e!!o!O!!]]]]4)&LMJ@'UAAAA0A A]]]]]449Rs&-KR@&' %+]5+5!&7K@& ' %+]]5+5;&.9\ %+5;&N9` %+5^@)9  *H@7 H0$ F T H   ?3?399+]23/]]]33399]++10]]]7!!!!Flw15fR^(['.,s&/vR@ &%+5+5&Ovg@&@| %+]]5+5;&/9-ڴ %+5x;&O9 %+5&/8/  %+5?5&O8@  \%+5?5&/Op%+5&OO8@ 0t%+]5 9@"/ ?    Zd ??932]]103'7!7!Eq6u)oXĞX ?@* 0 P ` F T  ??9]]32]107!'7!FuGq1+ph+p-s&1vR@ &G %+5+5j!&Qv@ &T" %+5+5;&19´ %+5;js&Q9u! %+5s&1LR&# %+5+5j!&QL3 && %+5+5d'Q@ U)_))]]4Ry'@9% I!! H!^"e)))@)P)vG$F@ H^d(#@ H )!   '_/]??3]]333+3+]22]]]]]]+]229/10]"&'32>7#!!3&'.5!Ab"%Q0*P@,;=DzR  (C3NMLA9PMJ CC>Joi3js*L@/E ))@ F%U,,0,,FT+M ?2???22]/]]10]]"&'532>54&#"!33>320f"5","NK

(/dlMe:w&2MR*&() %+5+5fd&RM)"& ! %+5+5w}&2NR-&2( %+5+5fd+&RN%&*  %+5+5ws&2S-R@ (&v(? %+55+55fu!&RSq@  &\ 7 %+55+55w+q@HZ(( g---?-_- [ f,g_L;g_)_(%__??2?2?]9/]]]]]]]]229/210!!#"&&546632!!!!!"3267&&-a5OO6b-EWS((S~W >>H&&&&WDDD,G VC(88O9I(P==|===!)55M/M!N?2??]9/]]]]]?]2]]2/]]]9/]]]29910]]]]"&'#".54>326632!32>732654&#"%"!.RwHBqoLGxjBBmo|CVx3[WT,(QZh^ji^^ki])E3 0HKPNMM،ؓLOMNNCz ! E:Y=3V?$ s&5v}R&& %+5+5V!&Uv@ &`"%+5+5; &59% %+5q;Hs&U9)!%+5 s&5L!R$&* %+5+5S!&UL &&%+5+5^s&6vNR@ :&:A%+5+5b!&Vv @ 8&b8?%+5+5^s&6KR@ :&AM%+5+5b!&VK@ 8&?K%+5+5^&6zNB:%+5bs&Vz@8%+5^s&6LR@ ?& E:%+5+5]!&VL@ =&C8%+5+5);;&79%+5/;L&W9  %+5);s&7LR@  &%+5+5/(&W8 z%+5);m@Do0@  Z@  ??29/3]2/]32/]9/]]]]]9/]]]]10!!#53!!!3#Tb/L!@  H@ H##O#_#o###F @/HI_ o  N PMM??39/]322/]3/2]+22]3/]+33]10+%267#".55#535#5773!!3#f-Q*+KI~\5X@"UlƽfƔA> `&8RR&* %+5+5d&XR1@ &- %+5+5 &8MR& %+5+5d&XMD@ & %+5+5 }&8NR&" %+5+5d+&XN/ &% %+5+5 &8PR@ &" %+55+55d&XP1@  &% %+55+55 s&8SR@ &g/ %+55+55!&XS@ &o2 %+55+55 &8Q~--%+5d^&XQ //%+5js&:KdR7&>J6%+5+5s!&ZK4&;G2%+5+5s&<KR &%+5+5P!&\K@ &&2 %+5+5V&<jR@ & '%+55+551s&=v9R@  &_ %+5+57m!&]v@  &i %+5+51f&=OR@ & %+5+57m&]O@ & %+5+51s&=LR& %+5+57!&]L@& %+]5+55@" HpFTM ??]]]3/10+"!4>32&&=;14`T]~,HD-M<\kT# )i@D H0  ((?&F@`)N&" ???32/]]]]]32/]9/]9/10+]]#"&'532>5#5754>32&&#"3#'Wc0f"6"-#4`T]~,HD.<1yMe: 'A3qRRkT# M32.'5>7!4&#"366ig+$A\75\E(H^  ]60/*VL\_#6**7++&2-IJ#:W8X=!!=Wa!KYY7;>5) 043 494-33-+33V -<P\@&++  G =@&&&.F, QQ= WW@RG=GG=,U^O^^0^4G0.Q)ZaBTaL0LLLL @ P BL LB:%%"N)7N/??2?3]9///]]9//]]]9////]3]3229/]]]10]]]5>7!'##".5467754&#"'663232>5#".54>324&#"3260/*VL]^#; !BNa@DtU0PHHEcTpe=T3D7*H5{'E\67\A$$A\75\E(6**600*6043 494'-A*+W[ TEB*#/6/A(F;9S68Y>!!=Y77X=!!=W8-33--44s&vR&e%+5+5V!&v@ M&uMT %%+5+5ws&vR@ 0&I07 %+5+5fd!&vf@ -&O-4%+5+5^;&69:@%+5b;s&V98>%+5!6@!) _ o  _/]22/]3]]/33/]310&&'#5>7!!3n46h3?A<d74\UXX/0C,$A3"+K|Y20X}M+2 1(@ @H/]]/+104>32#".-=""<--<""=-+9##9+*:##:TJ@ H  H H@ HH_@1HO o   H_o@H@ H_/]+]]+]+]]/]]+]+10++++#".54>324&#"326J'E\67\A$$A\75\E(6**600*68Y>!!=Y77X=!!=W8-33--44 8@'** :   ?? //]/]10]]]3267#".54>73-# ? J2:\A#1B%FN*(  "=T3+H?8BmH@0opH _ o   G_/]3]3/]]3/]3/]]310"#>3232673#".% (AX5)NMK$$ )BW4(PLKB56PtL% '!46OtM%!'!!N@3 H H@H `!_o_/]22/]3]/]2/33/+3/3++105>7!35>7!0/*- *6?>;0/*- *6>?:LQQ"18;82LQQ"18;82/! S @8 H,<L| _  _o _  /]3/]/33/]3]]]]+10>7!#  R5MQP!DR #{@W<L* _o## ###  "@H@ H"/""/]3/++]3/3]2/]3/3/]]33/]]104632#"&%4632#"&'>7!#G:9JJ9:G#G<9JJ954.#"JkOOOO (S~WYQ''Q~XWS(fllkksDDssDDB,.@ &FvH @HHW g  9  48 `p?@H@H   g 4  @ ;  ?3?3]]3/8]]3]++]3/8]3]]9]3310]]++].'!!!RqP__!!`_PJ01R U@5g  ? _  f _L; _ _??9/]]]]]]]22/]22/10!!!!!ZRJsdw=Hw21@Ze ?  Zd_??3]10!!!!!84Lm3N'h@D   (H@ H   `p?2?2]2]2/3]+]]2/]]+93]10355!!"&'&'67>3!NZzC &+G61,("  =);7<\3 .;@]....^;    t  ]%g====_=o=@===/=5]f< /`;`- ?22?22]]]]]]9/_]]_]_]_]33]_]_]221032>54.##5#".54>335!32###"33 MrL&!EjH%n\9Bӑ11ӉB9[n$HjE"&LrM 0UtC5!D=֘//ֆ="'JlF>kN,#ׂˌJLHʂ!XsCBrX7+@z   $$j$z$$L$;$($$$ )[!!--@Hk-{---/-?-_- fvC4'[  , !$ # ?32?]2/]]]]]]]3]]+]2/]2]]]]]]]10]]"!!.54>32!!>54&>fLsHvR-TT-TvJw|Mh?Xm),}gWWg|-(nX6V&,j>R& *%+55+55V&<jR@ & '%+55+55f!&~TF@ N&/DJ3%+5+5N!&T@ B&n8>+%+5+5j!&TD@ #&B %+5+5!&T&%+5+5&U% 2%+555fqC@  ? G33*s*T**@A I* **EEEEElEZEEH IE/EEGVDoD09$ M ???M?2]?2??]]]+]]]]]+]]]]]2210]]%2>754.#"".54>32366733267#".'#Z7K./M:`Z[[j:;ob7YH9 ! & ')4RA09I]&OyT Q~U,J׍ٓK*?*#S!bwD".  '@0"=.;s@I!  9F_o,GW==0=333#FT<2M:333)MM??9/]9?29/]]3/]2]10]]]]]2#"&'!4>"32654.##532>54&gGW`4@wg[9Mc'E47:9pm)G^6H66N4b1bb 8^]ls= 4{s7;eN |p?V57N/a_N^@3G)9 H X    G @' Hp   & 6    @   ??333323/83]]]]]]]3/8+]93]3]]3]]]]]9/]10%!4>7!3>7!# %u= >>~4*y?R)ll] S_f2fd.@g@?" , (H44*/G  WB_B9G*VA4 444 >M%N H ?3+?9]33]2/9/]]9310]]].54>32&&#"#".54>4.'326)K9"=o`Aqgb3lTO%2 ?^>IunL8a.@'$OA+2H.ajGUc9JsN()-7$47<#^ȃ?=unjsLn5YJ< 1RvQ0S="Ns7@x # ;,{4++++W999_94H!!!!!!!!!! *FV8O}H /N,& NF%5?3]]]?39/]]]]92]3/]]]]]]]]2/]]9/]]]10]]]#"32>7#".54>75&&54>32&&#"3 v;]B9j`S H͂}:'Ge=liI}^6nh_(VBV.K4r{ID, #*/UzK>ZA( dPoF 1 'B7f3k@$ 2  ##%  *  H@ H5p5*G V4 /*!5# N!?2292]3/+]/2]3/9]10]]!>54.'.54>7#!5!&16) "@4]f7Rf485ywH& #B]:RoD%-[YR#'SNE  Hqe^߶p©|i,JZ5 4AOjs<@%EFU0 F TM  ????22]10]]4&#"!33>329KP:O0)CPZ0s||0^Y^*>(Efb+!n@G  GW#_#G V"N}$I>OO??9/_]+_]_]_]]]]2]210]]]]#"&&546632267!"!&&b8{Ê~>8z?f\uZidZ\ mm))lk^=@  Hp@I0pFT ??/]]+]10+3267#".5@5-Q*,KI~\5^A>"Ul^!4@2 '444%4--- H/- -- -)--@ H6@6`666666/643  @5--$???9333/83]3]]3/+]9]33]]]+]10]]]3'.#"5>323267#".'&&'#!)0:%2 ',,\aE ('&# )01;XA/\%  !\5@# 0\V@O,  5! !9 3I-3%]x^>?;MfH@:  F%21%1%9999:: ??*??H4F  & && 4@! H44JpJ*GVI'P*/?$$9JN?229/992]3/+9///99]]]/2]]]10]]]]4>75&&54>7##5!#"33#"!>54.'.f)Kh?ir"FnK@7/*! AIf<7\E#B]:RoD&16) "@4]f7Dr]G ~g7UB1 ?_@+B-{{BP1 4AO.-[YR#'SNE  FkfdsRd^d@A  HFO0@F0@N ???22]3]]]]]q9/]/2/10+%267#".5!!#57!##A1;? GrO+6  &O|VBuf31yXs-M@5 )GW/_///// FV.M$M?2??]2]10]]]#"&'#!4>32%"32>54.X;obH3GsjI0F/%c40F,,F/ؓM+"+.'_-הMM$MxU++&Q~YYQ%fs-i@%& $$$H@ H/p///GV.$.//((. M?3339]]3/+3/]/]]2]]]104>32&&#"!>54.'.fKvVKXBz7od$B^:RoD&16) "@4]f7?*&%EW56DR0._\V%*WSH Lsf`)S@8  $!G++0++++++G V*%MM??2]]]2310]]]#".54>3!!!32>54&'#"uDÀtJS݋!%A1+2N76M195+Cb?iLHӋڋ@'YhxHvT.+NmC{K$R)^w@E H  0@`UK.F/?Op@ HNM ??2/+]]]/]]]]]3/]3]10+3267#".5!57!s@4-Q*+KI~\5yA>"Ulf^H@0 )9 FW!!!!FT  M??3]]]]10]]]".5!32>54.'!uo-2\b3N4 3 CQo`*\jB|z}CC{{HJfw 5@c- ''4'HEu%,I%%%y%$%%%H/W77777DI_77HV6N* $N4??32?3]]+]]]9/_]_]]]]+9]3]]2310]]4.#">.54>74>32'@.3:?fG'pQ!=U3'=*%AW3:fTkyA]pNBuY3Oa54'!!.5!JlG"-#)%Xwx˒R#=cI+S`܌E&A֚3WyN(;wH^=g@B%%%H$I$$$$$$z$$$0H9W?0?@?`?HV>3$+M ?322?3]9/]_]]]]_]+]9910".'##".54>7!32>5!32>54'!?bK5 6Jb?ll6 *%A<+D/*8!!8*/C,%+55+55fd!&RT#@ *&A & %+5+5!&T3@ *&/ &%+5+5wH!&T)@ H&L>D9%+5+5V&(jR@ & *%+55+55)#t@L*:H Z_o@/ Z %$__`???29/]3/99//]]]]]29+]10]"&'32>554&#!!!!!!25h..Y%/$FP/hq; =5YG^5f]s&av\R@ &M %+5+5w$m@I ""Z"j""!g&&&`&&[f%_oL; _ _??9/]]]]]]]2]]]]2910]"!!3267#"&&546$32&&%K|]: ' Yi0^bg;NZmddR1\V(% lo70':^6B,6V&,j>R& *%+55+559R-P*7@ / H @E H^)$ H $$)+Z )))) ) 1Zg9_987_ +_)_+_ ??]?9/]3/]99//]]23]++]]2]+10]]]32#!##"&'532>7>7!32>54.##lȃ?Aґy !:Z\*J 4,$ %)/T8Y?"%Da=?Bvalu>^M`g5 DrV2%H0N8:J*V#g@A Zu Zg%%_%%% Z d$#_; _ ?3?39/]]]]322]9/]3210]]]#!!!!!!3232>54.##VAґyL65mǃ?T8Z?!%Da<@lu>w=Bv0N8:J*)]@:Z  _ o @  /   Zg _ _ ?3?29/q3/99//]]]]]210]!4&#!!!!!!2}FP/hq;YG^5f]s&vR@  & %+5+5&6#R'&,"%+5+5V u@N\yD5 Ze ?  Zd _//3?3]9/]]]_]_]]_]_]q]]q10!!!!!!!|{66VL3$mW@:Zg?_o@HZ d_4_ _ ??9/]]]]2+]91032#!!!32>54.##lȃ?AҒyJT8Y?!$Da=?Bvalu>H0N8:J*%a VS@- \ Z \ _ _??22/33/3/]99//]33]3/103667!3!!!! q9k[J4GW1r%VLTV^jm(9ݹ@# H%  H  *  H *@C H%5     ]9     0P@  }} ?33?3399//]]33333/833/82]]]3/8]33/839/3]]322]]10]+]+]+]+!!!!!!>?!@=?@<<B^9[@8#)\Z g;0f:i"""""`##e#:#J###/,`5`?3?39/]]]]]]923/9910#"&'32654.##532>54&#"'>32X3[}K\c4JՌW+iml/9j]vhpW#vseL4o[r|B`IxX9  9YwI`t@)&$ pg5K21F-KYA6%;*6a@, H9 I   +  I H^ e  H@H6F$ F@ H^d  ?22?33+]22]]++]+]22]]+10!3!!46767#!RrL>CC JLGJ9ALM&6R&" %+5+5 g@C ) H  d o v d E U &    Zd ?2?3923]]]]]]3/8]]33310]+]!!!!!@6J=<Bn@  H @6 H^ H @Ze!!__ ??]?/]9/]]]3]++]2]+10]!!!#"&'532>7>7! !:Z\*J 4,$ %)J^M`g5 DrV2%0+w2nm3w&);7!g@# !!dt# #P#@" "5 _?2?9]3/3/83]3/8]]]293310]]#"&'32>7!3>7X+Z{x3u31u53G2" H   is= /?$++$ %*)y\3s;V C@*\ ZO_@H @ Zd _/?2?3]3/+]3/10%3!!!!!66`Lm6@!  Ze?oZ _ ??39/]210]!!#".5!3267!:jda2]e65TdJZ65" 3a\\jk%% f@CZ     p T d  Ze P ` /  Zd _ ?33?2]]]9/_]]_]]_]]]10!!!!!!6"68JLVo@`\Z     p T d  Z;K[@H4Td Z d _  ?33/?22_]]]]]]3/+]9/_]]_]]_]]]3/10!!3!!!!!6t68@`L#`@? ZZg?_o@H_ 4    _ _??9/]]]]3/+]9/210]]#!!!3232>54.###Aґylȃ?T8Y?!$Da=?lu>Bv0N8:J*5k@H ZoZe!!@H!?!o!!!! Zd _ 4    _?3?39/]]]]2]]+9/]10]]#!!3232>54.##!!mAҒy6lȃ?T8Y?!$Da=?G5lu>Bv0N8:J*ymX@> Zg?_o Zd_ 4    _??9/]]]]2]10]]#!!3232>54.##mAҒy6lȃ?T8Y?!$Da=?lu>Bv0N8:J*H$g@F Zj [ g&&?&&&f%_oL;__??9/]]]]]]]2]2910]"'6632#".'3267!5!.XRbdUN:hb^0iY&/Rt8'07e賩l %(V\1.d@?[ %[g0/00 Z d/ _L; **_   _?]???]9/]]]]]]2]9/310#".'!!!!>3232>54.#"J瞑ۘU 6Z֋Jr#IoLNoH""GoMLpJ#ݩl\ÉܛTksDDssDDH|@' ZZ eo@%gL;`_  ?3?9/]]]]]]]33/83]29/]93]10]]!.54$!!!#"33,YG-z`iejt1>`ZJ1VdapVuDfj%';b@> % !-G W==7GV<2N(M#  H  H ?3++]3]?9/32]210]]]]46676673>32#"&&2>54.#"f?ؙfq"1qrj+GiH& 6La;\h8KsuI*E2(A.+M>, 1Tς )Qh<1 >zz׏HZFrTClK)&6;OuG^ (i  @: H%!GG W****%6FT)I8P%%%$OO??9/]]]9]2]3/9]10+]#!!24&##32>4##326jpo;_C%4p}]tAXX&?-ۼTT9Z)AT3H{Y2^EpB;"5f8f^.@ HFTN??3/+]10!!fk^^o^@ H &6f@0H Ue  IF@" H`/ I N  N ?22/3?3/]]]3/+]3/]9/]]]]93]]+]3+10%#!!!3>7!3WM:N^7U=( ٤opU}fDsH^@U@  @ OO  F     y ` V & 6  @ H@`/ @  ?33?339933333/833/83]]]3/8+]33/839/]_]]_]_]]]]]33]2210]]]]!!!!!!n<uu;?77FNs7@Y&*6*%)5)$(4(; 9 !%77'I2FW999_9 'V86O777}7H777-N$N??9/]]]]92]]3/99]10]]]]]]]2654&#"'>32#"&'532>54&##5^l$QRP%Z-an}JTwGr_2WA%>ÅE NZd49cH*f2C66  #IpMew &E~_8#" 1$DA^e@ H HI  H U@ HFHT@ H  H?22+?33+]22+]2210++!!46767!n;^N#r8AH*r5>A^?&6q&%+5+5^ >@%  0 FFT ??]33/]833310]]!!!!?Pu1^7^L^P@0 H6FIt3& FU N???/]9/]_]]]]10+!!##"'532>7!L9[bjD2,)#! yd L^ @O: 5   HU""""0""HT!@H HH H ?222++]?33++]32]]]293310]]]]]]!!#.'!!>7!"ٺ   qSiu7 6ofT^#6o_HG]j4o^ ?@'FU 0  F T NI ?2?39/]]2]210!!!!!l2^R3^fdsRZ^+@FU  FTN?3?]]10!!!Z^y^wsSfsF/^^@+vF 0  @ HN??23/]+]3/]]]]]]9/]10!!!5^yP^\f$/@s  + u f  H     9 *   %GW1111p1_1101GV0(** N'++N  ??32]?32]?]]]]]9/_]_]]_]]]]]33]_]_]2210_]]]]!.54>7!4.'>ԐJGՎՒLEד#B`=<`B$#B^;=^A"d \uuƓ]  ]uxǓY DtW8  :XrCCrW7 d 8Wt X^[o'^ B@+I F  0 @ ` p FT /?2?3]3/3/10%3!!!!!11^y{b^C@+ F U0FV O  ??39/]2]]2103267!!#".5@5KB1!KU_6Q[2^\H?( %-ZZ^ o@FI@6IfW= I FU P FT N?2?33]9/_]_]+_]]]]+]+]10!!!!!!!1X1X1^yyo^@ F  I  @II f W =   I    FI  PFT  N/?22?33]3/]]3/9/_]_]+_]]]]+]+]10%3!!!!!!!+1X1X1^yy)^L@0F_  FWo O NO ??9//]9/]2]10]]32#!!54&##32>4p} XX&?-^PQ]2yA:"6^ Y@: FP FU_o0F TO O ?3?39/2]]9/]]10]]32#!!4&##32>!!ч4p}J1%XXuy&?-2Q]2^A:"6^d^ @@) FW0F TO O ??9/2]]10]]32#!!4&##3264p} 1bXXL`Q]2^A:AJs!@ H H@G H  H H GW##/#O#o#O O   } H  NN??9/]]]]]/]]3]]++2910+++"&'53267!5!&#"'>32cDBQ\qi ?t0VJV^2fJC %".x&7ڤߒGjs&d@@  FO  !GW(/(O(_( F T' NI$M M????9/]]2]9/]310]]]]#".'#!!3>3232654&#"jDra}Q1Q}cjIcT`^TT`^T1ؔM:w{3^Rms;L؋^w@<   FUOo0FV@P O ??39/2/83]]]]2]99]]]]]10]]!!.54>3!!#33#"J!+L8 >p^w\J"2!:Qg?TX-bGN%0fD&Hj@ 9& *H%+55+55j4t@J F0U66066'!FT5!P'M******** ***M???99//]]]3322/2222]910"&'532>54&#"!#535!!!366320f"5","OJ;P0Ϝ1;1`S`4'W 'A3nn1^Y/ǡ*]'[WM/dkMe:f!&v@ &S %+5+5fs  H@ H H ""/"O"o"H@&HGV!O}HN N??9/]]]]]++2]]3/]3910+++".54>32&&#"!!3267qxIKvVKXBz7]dh b]WJDBٗ:'#$z}˃}-#%$bsVL&j&"%+55+55M^(@] %I%F}Vf$4` FW*/*O**** H$$O%N M%O???9/]/]]99//]]]]]2]]10]]]]!##"'532>7!32#4&##3269[bjD2,)#! 4q|XXuyL`yd LPQ]2`A:A^o@HF   FW/O_@H FT OO ?3?39/]]322]+]9/]]32]10]]32#!!!!!!4&##326?4q|I1=1%XXuyL`Q]23^RA:Aj$p@G FU&&0&& F  T% PM   ?3?99//]]]3322/2222]910!!4&#"!#535!!!36632jKN;P0Ϝ1;1`S`4Pyy1^Y/ǡ*]'[WM/dk!&v@  &= %+5+5P?&\6$&) %+5+5o^ z@ I   I @=I   I v U e  FU 0 ` FT N?3?3/]9/]_]]+_]_]++_]_]103!!!!!11^yo+4@ ^  p Zd_??/]?]]3/10!!!!!g 6R@I_@' H  0 P p FTN??3/]3/+]10!!!!!@^1js&:CR7&6%+5+5s!&ZvP@ 4&d4;2%+5+5jV&:jdRF&7U6%+55+55s&ZjC&4R2%+55+55s&<CR &%+5+5P!&\CY&$+ %+5+5R?/105!R\R?/105!R\R?/105!R\1N&@  /]//3210!5!5!5!NRR1 E@2/)9I v 3 C %     ?/3]]]]]2]]10'>73%'.46z|{8=|5 C@1/ y < L   *  &6F ?/3]2]]]]]10#>7'/37y}z8<|5? V@A/? y < L   *  &6F/?O @ H /+/]3]2]]]]]]10%#>7!'/36z|{8=}5 A@//)9Iv3C%  ?/3]]]]2]]10#.'7=4.'5|<8z}y7u x@Z)9I3Cv% )9I v 3 C   %   ?32/]3]]]]2]/3]]]]2]]10>73!%>73!(.4'.46z|{8=|56z|{8=|5u x@Z<Ly * &6F y < L   *  &6F ?32/3]2]]]]/]3]2]]]]]10#>7!#>7!'/3'/37y}z8<|57y}z8<|5? @_o 0y<L * &6FOo y < L   *  &6FO_o @ H /+32/]]3]2]]]]/]]3]2]]]]]]]10%#>7!#>7!'/3'/36z|{8=}56z|{8=}5{ <@'  0  0  /?22]]/]3210%!5!%77777LB_{V@:  0p 0  /22?22]/]322210%%!5'75!%%oK78L//L87K/-yxb)G@-`p/O_  `p0H/+]/]]]]]104>32#".b,Mi=;iN--Ni;=iM,VxL##LxVUxM$$Mxub9';j@8(2222222======`=O=0== `@ H-7#/3322/+]]]]]]]]]]3/]9/1074>32#".%4>32#".%4>32#".u0@%#?00?#%@0G0A%#?00?#%A0H0@%#?00?#%@0/A((A/-@**@-/A((A/-@**@-/A((A/-@**@? "-AL`@ U H: H^ HZ@ HP H?@ H5 H1 H H@+ H H H#8.VY @GPPW)......b?bHMBWERJ\+ =&3???3222?/]]9///]8]8]3]3]10++++++++++++#32654#"#".54>3232654#"#".54>3232654#"#".54>32+p-21/`2-)V[UW,(TZVX-;-12/a1-)V\TW,(TZVX-x-21/`2-)V[UW,(UZVX-J}|{}lv??vllu>>un}|{}lv??vllu>>u}|{}lv??vllu>>u BR^b+@?322]22310R5=wwR^b+@?322]22310'7b5#;w\\w9u&H*@11111`1O1/111]]]]]]]]]]w?//833/8210#+Jf:@%/O  0 ` p   ?3??2/]2]]104&#"#33663232#4"Ǣ "aDL@0Q<T32/#Z@4 n t?Oo t ??99//]]32/]3/3222910!!!#53!!!!ϕFRB,@k **+* * n'# k {   %!@`c @.!""%& &_"&&/&_&&"&"&(t?2]?99//]]32322/]]]3/]3399//]3332210]]]]]2&&#"!!!!!!5>7#535#5354>nP]Gu?CK>>*3(@.?o0"#M_qs0G4% &6I3ssqd.3y@] (&&$H/+/O/"/ / /-@' H5]%.P+**(++__??9/?9/33/2/]]23/+399//]]32/2]1032654&####!!2267#".5#57733#>wPk:٠3mͅ@+G$)vEDoO+X )humh`P?ur!Oa>lg/!BZ5@W&)4*33 !27n!(  *!!  ())/)) ) )3t/t?3?399//]3232/]3/322]]299]10]]]]]"!!!!3267#"'#53&&5465#53>32&&ij:rO<9^+vt[xfPbExɍ  }v"  {‡G*(#?#A& H, H H@ H  H H@3)CC@ H1>8);$$$5.! /??]?]/2+99//883310++++++##".54>3232654&#"".54>32&&#"3267+-TvJEuU/-SwKDsV0L7><66<>7dJ|Y12Z{I5j.7(L"E>=B8^'"aJW]11]WW]00]Wdeeddcc8,XYbW(kZZd)+5a@< ,,),,,I!!7?772'V'''HP&P33 /RP  ?3/?9/]3/2/]33]2]22/]10%2673#".5556674>324&#"66o3@)TbT`3&]31[*/[ZM{V.5fb )? %*KBcfai73dd  _V(-TxKc{f2)D1AJL?*-9¹+ H'@( H! H H.$4;  H^@ H;/;?; @C H  ^  ;K[ 7)1oFV4DT ?33]3/]/]?3]3/]22+]9/+]2+22210++++!!#!!3&'&&5!5!#".54>3232654&#"+J V-TwJEtU/-SwJDtU1K7><77<>7KK@<DF<@}JW^11^WW]00]Wdeeddcc ҹ @H9IYH6FV@gHH d0@P"""""@H"@ H @ H?333/+3333233/]++]]]2229///]]]]229]10++]+]+##5!###33#4>7#}4?<"^/## 7vfH"/>@"#1///?///) ?2?399//]]]3/]332210".54>32!32>7.#"yƆE,Lfv?qŽQ@MX.Jt^M"H$Sn;M]53WI<#^nc~\<Oу," 32&&#"66322>7&&#"b!Eisx^&6V|iiT c[GLN&QWm.8aN9N5-I:) .iѹQFuQ=e=)sy)+/^P_44)DYab+HP9@N&6Fv )9Iy @H@ H?o@ H_ ?3?99/+3]]++]2]93]3]10]]7!!.'!9^i    4:2 2:5 7H*@[e @ [d_/3?]10!!! 7})7 h@AdU1(  P ` p     ` _??9=/3/2]23]]3/]3/9/]]]]3]1055!!!)? H7Bo Xd?//105!Xd%N@ %(' H H/2++/9//3/39/993310]]]##5!3Elq{7#/>@ +$@ H( H( H@3 H'+7+G+(:8:H:&262F2)#9#I#2#:@@@@`@+@ H0&2# ?_5  /32/]392/+]9]]]]10++++]]#"&'#".54>326632267&&#""32>54.7.TtGX?;OFvV/.TvIR>;[GtT.-Q(&P2.!0PWQ&U/. !/C{]7VZM]0X}MFz[5P^PZ1X}BEDC&10%AH&20$LG@.% * |fW 0@ o  //]9/]]]]3210]]"#"&'532654>32&&3<6dV8a*#R);37dVmV#SHAd\,IEd\+(X]B'aa6@3333P3`3 303@3]]].5Xd@<        I I @  /]]233/]3]2/33/322/39910]]'7#5!7!5!3!!X;Pu\PT۪V٪X= g@D4 D d t     :J) 0@5 E  &  /?O/2/]3]]3/]3]]9=/33/32210]]35!5XH}X= g@D;Kk{    :J) 0@5E&/ ? O  /2/]3]]3/]3]]9=/33/33210]]35! 5XH}H>JXP N@    ? o @ H /3?39=/3/+]]9=/33310#3P=r=r!fg)&IL @////333P3]]]]]]55)&IO@/P]]]]]]5h3?J@1 H H      _  _/]3/]]3/]]/]]10++#".'!32>739sl12L5,I6"?P^42]S9H),G5^6@#  FU0` 'M?]?]]2/10]"&'532>5!f0f"6"-!1'W 'A3)Me:^ N@8<L* _G  O  _o _  /]3/]/]33]/]3]]]10>7!#^ R5MQP!DQ^; J@4<L* _G  O  `  /]3/]/]3]3/]3]]]10>7!#^ R5VMQP!ERN! N@8H  _  3C% O _ o  _/]3/]/]33]]]/]3]10!56673 R5MQP!DR)5 U H@1 H H H@H !!!/!_!!!! ??]//+10++++32654&#"#".54>32%-12..21-)U\TW,(TZVX-}|{{}lv??vllu>>u J \@<5E/_ 6F 2B@H ??39/332/+29/]33]22]10]##5!533!5467}} ᗗAͤ*]1 +-*T9" H@# H F   $$$/$_$$$$ @* H 5 E U & gwbr ?3?9/]]]]/+]9/33]3310++2#"&'532654&#"'!!66CtV1G27366322654&#"*R|RL_5J˂CF}dH YJt;><9%1  /mDrS/0ZQom'3AR5+/G?6D'7);J9@%/_?2?/]]9/10!5!TMJ)-5'4B@ % H H H@ H@@88..(( @U H H #@.DDD/D_DDDD8##( 2I222;;G;;;G++H55?]?]9/]]]]99/]3/]3/99++]]]]10++++2#".54>7.54>32654&''">54&ApT0'4"?11Z~MTW-+:.#1Vr7:9;<@9 05u+-4&  *9U8%>2()4B*9bG(&E`9*B4(+5>%8T9n-99-*;;/)2 !/+9$4A@*1666/6_6666( @H+% ??9//+3]210#"&'532>7##".54>32%"32>54.*lJ13SlA %0=&<`D$.VzLN]35B8;/-3`V$AY5$%HhBCpR.4fGA7A&6*T #'+/37;?CGS_ox@Y4, 8WQzpotgdd~kpQQQQokkoQ]O_D($ ]PK`KKKA=1 *BF>&2oygppz`zoTHHpooaoo@% Hoo`ZNNx`/`?`o```, 95!/^]3333/33339/]333/33/+]]3/339/333/3/3/3/3/33333/]3/33333/]39///]333/33333/3/3/310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3"&54672'2654&#"32##32654&##32654&#532653#"T/0momImmmm0oowoooommH??HEBBmp--83m^{B.$)0;J1%&4 %1}h_=0oo/mmmmmmoo;mmJoooo/yhhg^^ff^^gCS1D D:QYb" "+% *f$2r^dT#/P@(##$ *0*@***O$$$ ##0---''/3/33/3/]3//3/3/33/]3/]3/310 54676654.#"663232654&#"TV!LcM1[Q+ZWR"RD~8?>'REJGFDGGDFGVW/,>:LYEkJ'#".:/1DA5yP;>II>@II!&7L@@0x%+]]]55)`) OY@59GMQ`QFC1H$M+ >NM++p++++6NH6M??99//]]32//]3/]3210.#"#".54654&#"'66323265<&'.54>323 )9I)2;"N~:]qZ! 1H;[Zg(?-~D/beLU\/89-WD+]]38]w?5i+* "4^X?G 8*XXHzW1W׀ @"""" "0""   ?@5  ) Y    Z0&Ff ` ???39]]/]2/]]]]]93/8]3]]]]]]]10>7>32&&#"!!T1233>M1+F" %AJIBPTByl+&:'  $ {Z/3^ :@R222H1I111111*1z111!H 0<<0<@<`55!326?bK5 6Jb?ll6#$7l,U^*8!!8*^T7N00N7F|a]f]a|†F[`^]!@]<<]@!u&0vT@ &l% %+5+5!&Pv@ +&_+2 !%+5+53&$[H@ &'!%+55+55Vu&D[@ >'9/ %+55+55r&2R\:@CKKPKKK@KK %M_MOM]]]+]]]]]55?55XN@ H  H H@ HH_@ H @HP`@HH/+]]+]+]]/+]+10++++#".54>324&#"326N'E\68[A$$A[85\E(6**600*68Y>!!=X87X=!!=W8-33--44yh+#5?@ H# H###O_/]99///+2/+]104>7#".>7!#y FoPIE%-%?B4&Z07="D9+ J%%-"4CLRP""MPP$)&I'IL/H@2MMMM0MMII`IPIII`0]]]]5]]]]]55]]]]]])&I'IO/F@1999909955`5P555`0]]]]5]]]]]5]]]]]]w1D@'[([`p3[ f2-_#_??33]]3/3301#"&&546632665!32>54.#"OOOQ++-2HaA> (S~WYQ''Q~XWS(ݩllkjacTiNsDDssDDf ,R@  G'G@ H.!G V-*M$M??33+]3/3301]]]#".54>32>5!32654&#"dGwoLGxkB&8%-/Q|^:^ji^^ki]1ؔMM،ؓLEE 4J]6KnS5!>5!{)ER\0R`41IP

32RP K6*"(C (.3NcG 3% " oR%} //014>32#".-<#"<--<"#<-+:"":+*9##9s&(CR &%+5+5s&ChR&$ %+5+5fD!&HC*&/6%+5+5!&C& %+5+5wK+**@B H**@ H@Z5= === G\gM5\ fL>> @==B:_++0`%*%%` ?3323?332239/9/]]93/+33/+]301"'>32#"&'#"&&546632.#"3267!32>54.81,lLSV(ӐJGoJInGJӉ(VTKl+28=dG&*S}T-L"6BdT}S)&Gc (bwHKKHw!b( ;vuyF'tEFyuv;^$@a$"#z##i#;#K#[##$$$$G!"( ("" ""# F&G*### #!$""$?2223]3?3399]]]/9/9]]]]9]3333]]01]]]]]]!!3>7!3>5! !u@  d9 3I-3%]x텍^9{1:;;l>?;M}#<@Z%Z #_/`_ ?/32/]/3229/0132#!!5!5!!32>54.##5mǃ?Aґy5'T8Z?!$Da=@dBvalu>d0N8:J*'C@$F!F NOOO ??39/]2/3229/0132#!!5!5!!4&##32>34p} 1):YX&?.yˣQ]2yA:"6-f@: $#/,[Zd.$'_# _,L; _ ?32??9/]]]]]32?3222/32299//01!3267#".'#!!3>32&&#"!3[TYi0^bg;Y 6kmddRPK|\: &wTi<(% \ÆۜU70':-U}Qs)_@4&6G &&+ FNNH  '&&#N?29??9/]32?29/222/99//3]2]01"$'#!!3>32&&#"!!32671 S~]2^VJV0t?X\i!7K.QBD3^Ri-&snEa=."%  @: I<)  2%  @6 H H( H  ^F  `   ?22??39/]322//9/]]]93]+3+]3]]]99}3]]]]99901!#!#!!3'.'X;={  wwDdZR[Z"!Z]R^ G@   9       j I Y ;   *   &FVf4%      @Q H% 5    HXJ(8I P  @ P `   ?33?39/]3223//9/]93]]]+]]3]]+]]]]]3]]]]]]]qq9}3]]]]]]]]qqq901#!#!!!3'.'%/2oLZZ^uC ALMLNB " @U ] G D&XY;*$ !"  "!  5 $      Z^  d# `_P`  ?333???3/399//]32322/]9/]93322/]}3]]]]93]]]9]]]]]]]01!!!#!#!!!!3'.'p{,-6yCDwww<Rcd#75!!.'!!)@Pb=53^RDjj {'&6$?$7()/dn@DGZM0M0?` jjfT\p@gff[[$\?M`;NNN D`0:`/''[ZZW` ggaj` ?333/23?3/329/]9/33/32/3/93399//2012&&#"#"32>32.#"#".54>32654.##532>54&#"'>7.'53>-8 ./Y&NyS+3[}K\c4J֌CS,7H2RHB!BG "(,Vdo9XW++f9j]whpW#vsdK'OWa78953b632&&#"#"32>32.#"#".54>32654&##5^l$QRP%Z6sE0-*3b6;IX7-7 .&M 4YB%r_2WA%>ÅE~_8 $0(  ,PqFN}Y0C>DAmDuw)5@%[g+$[ f*/+?+_$$__??9/]2201#"&&5466322>7!"!.OOOOpMtQ/ j 1RtNIpR3  2Opݩllkkh4aZZa4/ZRRZ/fds!C@(  GW#G V"_#OMM??9/]2201]]]]#".54>32267!"!&&dGwoLGxoLW^ {^VV^ _1ؔMM،ؓLL%ttttqppqT <@!  ) _?3??32/3/9933301]]]"!!>7>32&&,#99    =RlKB_50G.s-LFE'&IHK)bi8 fv@ H9@@HW&Fg  GWF M??32?3/3/9333301]]]]]]]]]]+]]]!3667>32&&#"!?\6MiHU%("^A~33wA@OxQ)  #Ts&vR!&8/%+55+55!&v&5,%+55+55w 'F@p4444Y4i4y4K4944*4 4))))w)f)E)U)6)%)))F6--(5<<)(([5[ fG45-FF(=@M<94(#__???3?3293/39/3/33/93301]]]]]]]]]]]]]]]]]]]]#"&&54663232>54.#"!3>7!#"&'532>77XEEEE^GuWYuGFuXWvH M OF>ա5K@#0D1"ݩllkksDDssDD4v/8:9u /B)8fs >@o,,,i,y,X,),!!!!!e!u!V!G!&!  >%% -44!I Y i   G -GV?,-58M41>&,  MM???3?3?32/39/2/]]33/9301]]]]]]]]]]]]]]]]]32654&#"#".54>32!3>7!#"&'532>77^ji^^ki]GwoLGxoL M OF>ա5K@#0D1"1ؔMM،ؓLL4v/8:9u /B)8w1:i@>8 2*^ $ /[g<[f;8:5J55552_E'6''''$*_ ?32]]]?3]]3932233301#"'.54>766326632>54&'#"&'CюG5pҊCCԐD42DҊD?`A?+(?A_>wB+-Ayݘ{9:s{zB,,Bz_yS**Sy_(&%%&(f=_@(; 3+I % .GW? GV>8@ H83;M+(@ H(%N ?3+3?32+932233301#"&'.54>7663266326654.'#"&'=sh E36D bt@=sj A/+A ewB"5%3313KF"4#(97E@1yY5:;6XyzX/##/Xz>kV='+-'=hS<&8/"wIbx@fs/s?shhnccssVWKW' &P&&&;;;WWE\gz1\fynvih#vvWS]bLL?WOWWWWWW#'',`&& #@;6_?3322?3332233/]q3/323/299/933/]33/]333/]2/]10"'6632#"&'#"&&546632.#"32>732>54.#".#"#54>3235>54.5463281,X,%-%NGNT />bwNKKNw!b>/ ;vuyG!9M,,M9!Gyuv;A3;1]k6&-&3VA'V 5:ZfRIdz@.u/u?ujjpeuuVWKKW455/55555   P  @JH E =WW*G=W|GV{pxkj xxWS_JKK?WOWOWWWWWWW 4/N5 8 % NEB?2222?3332233/]q3/323/99/933/+]q33/]q33/3/2/]10".54>32.#"326732>54.#"'6632#"&'#".#"#54>3235>54.54632\xCJfM,%-%NGNTGߗڃ7 'S[aX)N92$)Xa[S' 7ڤߒGB.)4;2>W<#&-&3V@'V 4:YwBHV@`'&&;Z::D\RSSTNOOQPMKLLPTUJ/JJJU UU9<<9UgX1\fWJUUVNRPLT#::#6'',`&&#<99?6_?33223?33239/222239///]3/]933333339/93/33/301"'6632#"&'#"&&546632.#"3267!32>54.#'##'##'581,XbwNKKNw!b>/ ;vuyF+#1Fyuv;oYggggY$2-@J"9"+"" "I* H #""!$$$$$$$$$r$d$S$4$D$"$$$$3.//0*++-,)'((,01&/&&&11=114&112*.,(0##XIn#[#*#:#J## # #""?223?3399]_]]]_]+]22223/3299//]]993/]933333333]]3]]]]]]]]]]]]qq3]3399]]]]]]]]]01!!3>7!3>5! !#'##'##'5u@  d9 3I-3%]x텍#R8127117P^9{1:;;l>?;M}XffffXw 9@Z   "[f!_   _ ?32?3/2]29/01&&546$32&&#"3267!_NZmddRPY]0+Y`ABlo70':Esu}Afs?@$F [ k O +  GVM M ?32?32]]]29/01.54>32&&#"3267!u>KvVKXBz7odudKg/KΌ:*&% hy @E ? 0         /33/33/3/33/3/9939939999/33/33/]322/]32/93901%'%7%7%LG㴁FGJ{J;{Z}9IĤ{{@  //3/01#"&5463!6632#608349708569+3G88=-1=69H !@    /23/3/3/]102>32#&&#"##5Nwp:-cQ5 +.5=G[tJ&-%6k]1;4X8@"s d E U   0 /3/]9/]]]]/014632.TNGN%-%,=qU3IY:5 V'@VX4@@ H  h   /3/]9/]]]+015>54.546322Vp>,%-%NGNT3V@'V 5:Y)';Ocw|@;iuuAM%aUU99-}x~M(<73>73#.'5.'5>7''>7.'7.'7B  #a   #a &UWW'*][W%Z'UWV'*][W%#OTV(E%ONIbJ"PTV(E%ONIb('%C343b)'$C442b1'UWV'*][W%&UWW'*][W%!  #a   #a E)'%D342b#)'%D342bB#OTU(F%ONIbI"PTU(F%ONIbV1@UdtCS&6  y j [ J 9 *   [1 ^  33@ H'&&^d2  _1',! ?332?3/?323/3+]2]2223/310]]]]]]]]]]]]]]]]]]!3!!!!46767#!#".'!32>7RrNN9sl12L5,H7"L>CC JLGTL9ALMP^42]S9H),G5o?-@osdBR#3l|$IK<. H -#"".   /HT. N-##(?23/2?3/?32]229/33/]310_]]_]]+_]]]]]]]]]]]!!!!46767!#".'!32>7n+݉; 9sl12L5,H6#^N#r8AH*r5>A^P^42]S9H),G5/m#^@7Z 8 X ,   %?%O%Zd$#___??99//32322]]]]]9/103#32#!#535!32>54.##lȃ?AҒy6T8Y?!$Da=?Bvalu>!H0N8:J*dP@-F FTPOO_oO??99//]322229/0135!!!32#!#4&##3261;4p} /XXL`5?Q]2oA:Am"@UZ$$?$_$$$  Zd#$0% _ "_ ??9/]]33222/]]3]3/322/]]]3]3/9939910]'##!!23227'7654&##m.G3Xs-c9Gͅ@=  9R)wO:peW"}p ?uRou5Zmhws#9@N3%52!3 EU547GW;4f44,3<333* F T:$M! 344/M ?2/322/3???9922222/]3]]3/99]39910]]]]".'#!33>32'"3227'7654&7WD4+6GZ7Wf8G>V\4z3G,,I6X=U(3#7;J"<-J؎K{p%JqK!Q~U,oXMm/ >@"  Z d _ _??9/32]229/]01!!!!#53?ʉTd^ G@*/  F /?T N#  ??9/]32]229/]01!!!!#53=ω^^'->@ '\/ Zd."__  _??9//322/29/01"!!!6632#".'32>54.\8FAw1BuW3Zk7XLE#?~IBoO-%@S[^oAhȁS  9eSYX6  L^(>@ H*!FT)M$$ M M/32??9/2/29/01%#"&'32>54.#"!!!6632LOeA~A5993XA& ChH)>"G ePD׋C #LwTDqP,^IV@` 6 f v   % Yi(  ]Y6)  \     _?33?3/339333/]]3/]33333/39/]]]332210]]]]]]]!!!!!#!!>?!@=70ը?@<<BLo^@Q9* 6%@VvHyyH6' / I_@ H N  ?33?3/339333/+]]3/]33333/39/]]]]]33]22]]10]]]]]]!!3!#!!!n<uu;?77F^&KC0 %+55Ns&BIA'%+55VV@.\& Z d   _ ?33?39333223]]3/333/301!!#!!7!uմt6zNX- L`V@mo^a@;  t e F  HT  I       N??3/?393/]]3/3332201]]]]]!!!!3!Hto;7^%@=++!- K< %Z @b u` dL; |XI]O=@#HSc1A   ?2?3?399//93333]]+]]]q3]]]+]]]]]]]]q/3/3]]]]9/]]322]23]]]]]01]]]]]]]]]]!!#'!!73!†t6|X-`V@b)m^@}j]I  } i ] K   DH      5 T  | m [ < L +     ra3CS! /?  ?2?399//]]]]9333]]]]]]]]33]]]]]]]]/9/]]3]]q22]]]]q23]]]]]]3/3]]]]]]]10!737!!#'!T};}T^{< dy@f  v g &     Z/?  L = +    _%5E   ?3?39/9]3233]3/3/3]]]]]29/]222]3]]]]]]]3]0135!3#7!!!#6zNX-tʉ/m`V1@V)   vUD6'F?H IT  P ??9/32?39333+]222/]223]]]]]]]3]3/3]]1035!!!37!!!#1;pXlwϜsTRB@!u  Z   _?2?39333/223]/3/3301!7!!!!{{NX-u@m`VR^ @@#F5 H    N?2?393/]22]]]33/301!!!!!o;u^7yV?Y@6 Z\` Z d_ L ;      _??3/?39/]]]]]2]]210!!!!!!!!+65 Lw=o^\@9 FI` FT NI   ?2??3/9/]]2]]]]210!!!!!!!l2^R3^Z R@3 Zd Z`_ L ;     ?3?39/]]]]]/]]]2210!!!!!!!Z6{Lw=^ F@+F0 F T NI  ?3?39/]]2]210!!!!!!l^R3^H/J@((Z))-\ g1?1,Z-d0'$_)+_.,)_/32?3?9/32]99//2106632#".'32>54.#"!!!!K8CuW3Zk7XMD$?IBoO-%@S[^+ K&84# AhȁS  9eSYX6 L ^)B@$"FHW+o+FT*M%%N M/?3?9/]99//210%#"&'32>54.##!!!!6632OeA~A6993XA& ChH&ePD׋C #LwTDqP,y^IwDT@16PP;%%5M&MM^;;;)E9EE^ gV.Z fUH`@@ H@@&)_% P3_`?33/22?329/+]2/]9/]]]2/9333013267#"'#"&&546632&&#"3267.54>324&#">{(:C9%?*//2|32&&#"3267.546324&#"66'46M-G8+hHpF=ƈ985DM&7!mP   Nm^51..15?@s0uTdE&6? ZjzI:)  Z,  0   _ ?3?9/32333/]]32/]9/2]]]]]]]]]]2]]]]]]]]]]10!!!!!!5!VND)+DP\Z)Z^  @^ H% 5    zkJ)9 D%5 FN ??39/332/]3/]]39/]]]2]]]]]]]]2]]+]01!!!!5!!3>7!mP   Nm^51..15?V{_@2    \     ?3/3?393]]3]]3/333/3/3/39333301!!#!!!`ծcV N^ L)+ o^@m &68)& )  I y j 8 X  tUeF7 N ?3?3/3933]]]]]]3]]]]/3/3]]33/3/3]]93]]]3]]33]01!!!!#!ZZ;#Pj)VG@*Z? o ?  \ ZP_  _ ?2/2?32/]/]/]q01!!!!!!!!5+NTL/o^P@4 FO_?O_ I0NN ?3/2?32/]]q//01#!!!!!#5\X1F^lyymV?@#ZZ/\ __?3/?39/32/]]]2/01!!!#".5!3267!+:jda2]e65TdJZ6 L5" 3a\\jk%%{ou^KF@ H?  I @ H O N ?2?3/9/32/+]2/+013267!!!!#".5@5KB1!KU_6Q[2^\H?( %-ZZm`@5 v"2Ze5 &  Z _  ?3?93/2/23333/]]29/]]3201#".5!33667!!#X1/]e65Rd*Y361Y,3a\\jkH J5 {b^j@=0)FU!v 5 &  F@O  ?3?9/3/2/23333/]]]]]29/]3201#".5!333667!!#-(Q[21@5 }"C"1C%}Z-ZZ\H? !*@Z Zd_ ??39/32201!>32!4&#"!6:jda2]e6TdIZ" 2b\jk$&q^1@FU F TO   ?2?9/]32201!4&#"!!>32V@5KB1!KU_6Q[2H?( ^D%-ZZ`5@`@:<[////B`B;1[%O$4D^1_;$$ 6_*_ ?32?9/32/]]3/]32]]]2/10%2>7#".'#".5467333>32!"!4.?Nu#*hge ?Q{S)  %)dH'4]-FsV3"N 093'\$C`=;h*"% ݚRo߲G\e6-V~QP}W.#s.7q@G3H  W992 F%+++++u+P+`+D+5+'++$ P2'N/O??329/32/]]]]]]]]q]22]2/10>32!3267#".'".546733%"!.NUdo}CV%C_=eW(QZh>rX MzT- ,4Rk/Hqq9Cz@gG&,, =}6U<-X 6%)rz3V?$V8C@O \ ?[2222E`E"$"4"D"""^>4[(O4_'>>>>>>9_-`  ?3/3?9/qr32/33/]32]]2]]]2/9/10%2>7!.'#".5467333>32!"!4.?Yl "RcwHvN ?Q{S)  %)dH'4]-FsV3"N"18-'bm׀$C`=;h*"% ݚRo߲G\e6-V~QP}W.o#s/8@QI 4H  W::3 F  %,,,,,u,P,`,D,5,',,% P(33N0O??33/329/32/]]]]]]]]q]22]2/9/10>32!3267!.'".546733%"!.NUdo}CV%C_=eW=PQc<MzT- ,4Rk/Hqq9Cz@gG&,, $Oyg6U<-X 6%)rz3V?$B,9&6NR& %+5+5?&6&%+5+5\-I@&  '\/ Zd. ` "_?3/32?39/322/223/301]"!!7!32#".'32>54.#U+6zNX 9vZ6Zk7XLE#?HBoO-%@S[^ @P6\‚S  9eSYX6  ^)W@1%&%f%%#$$H+0+%"FT*"%M#  M?3/32?39/322/22]3/310]]]%#"&'32>54.#"!!!OeA~A6993XA& ChH/201uXK_JD׋C #LwTDqP, ^ HV9#@  H  @C H^ H """""@"""Z[_%0%%_"__?3/?32?/]]29/]]]3]++]2]+10!!!!#"&'532>7>7!M !:Z\*J 4,$ %)J L^M`g5 DrV2%ow^[@8It3&HFNM N?3/?32?/]]9/]_]]]10%!!!##"'532>7!L+މ9[bjD2,)#! yd LX@4ZeZd_L; _?3/32?39/]]]]]]]2/2201%#"&'32>5!!!!!K~dG ?FN.AdD#65ZݠY  4_S= o^f@A*JJ(FU0FTM NI?2?9/]]3/322/2]210]]]]]!!#".'32>7!!l2Kk&C>; 6w41P:"^RƂA  FmN3^Vbr@H[ Z@` Z d_ L ;     _  ?3/3?39/]]]]]2]]]3/]3210!!!!!!!!N65 Lw=o^]@9 FH 0` FT NI ?2?3/39/]]2]q]2210!!!!!!!l2+݊^R3^mV:@Z\eZ_ _?3/?39/32/201!!!3#".5!3267!:jda2]e65TdJZ6V+" 3a\\jk%%{ob^W@7F I U0FVO_oN ?2?3/9/]32]]2103267!!!35#".5@5KB1!KU_6Q[2^\H?( op%-ZZV!@G!6!!!'! H @? HI; )][! _##  ^ d"  _  ?3/323?333222]]9332210]]]++]]]!#!!3!!!!4>767# <PM{\V%NLFX^TLBJL$T[o#^$@Y#:# #5     #HH&&&&&0&`&HT%@H H#H@ H N?33/33++]?33++]32]]]]2293310]]]]]]%!!!#.'!!>7!+މ"ٺ   qSiu7 6ofT^#6o_HG]j4B,3&$6LR@ &!%+5+5VE?&D6@ 4&59/ %+5+53V&$jLR@ &&5%+55+55V&Dj@ >&+/M %+55+55Vul7&(6R& %+5+5fL?&H6@ /&4*%+5+5 +P@&[g-`----'[`p@H'_ !__ ?32?9//+]3/]210">32#"&&55!.2>7!Xl *ggYVH4^-FtU3l"N"28 6,l髮ho!H\e6#,V~QP}W-fDs)=@"GW++0+$$HV*P O$N% ?32?9/3/]]210%267!2#".55!.#"5>FQk{0H xʒQJro}C%C_=3[VT,(QZhqz3V?#FבܓICzAfG& ! V&jR;&,J%+55+55fD&j@ 9&*H%+55+559V&jNR@ !&0 %+55+55&j@ !&0%+55+55^V&jRI&״:X0 %+55+55N&jG&8V'%+55+559$A@ $$ Z &`$$`?33?329/2/3/3/39/310!!#"&'32>54.##9mhs>EٓvZ-dda+VrD%SbhZ\fCo\`xC'($ :Q0-I39^#I@&## G %O##M?33?329/2/3/3/39/310]]!5!#"'32>54.##nj|k0Eٓ-dda+VrD!PghZ \]cMP$ )G]38dK,&MR@ & %+5+5&M@ &%+5+5V&jR@ '&6 %+55+55&ju@ #&2%+55+55wV&2jR7&(F %+55+55fd&Rj/& > %+55+55w)U@7%[g+/+?+$[ f*H_$$d$K$9$&$$$$__??9/]]]]]]]2]210#"&&5466322>7!"!.OOOOpMtQ/ j 1RtNIpR3  2Opݩllkkh4aZZa4/ZRRZ/fds!o@L  GW#_#G V"HO{&69(M6M?]]?]]9/]]]]2]210]]]]#".54>32267!"!&&dGwoLGxoLW^ {^VV^ _1ؔMM،ؓLL%ttttqppqwV&~jR9&*H %+55+55fd&j@ 1&"@ %+55+55HV&jR4&Ǵ%C %+55+55J&jv1&"@%+55+55&MFR@ $& "#%+5+5P&\M@ !&  %+5+5V&j;R@ 1&"@%+55+55P&\j@ .& = %+55+55s&SR@ "&k"9%+55+55P!&\S=@ &e6 %+55+55mV&jPR@ '&6 %+55+55{b&j)@ '& 6 %+55+55V -@ \Zd __?3/?3/]013!!!!F+RLo^ 2@ H IFT MN?3/?3/+013!!!!>^y5V&j%R/& >%+55+55&j@ +& : %+55+55/& <5^& <5q&;H%+]5 ^&[ %+]5@!$    sf@ Hz i Z 9 I +    z[k:J+ veTE$4    + `poI_  ?3?39/33]22]]3/]3]3/]3/3]/93]]]]]]3]]]]]33]]]]]]]3+]]3/3]3]3/]]10!!#5!!!3}mV Nj)jN X^@    @ H @ H    f E U 4 %    H H   % E U  Y *J / N | ?3?39/]]3322]/3/3/3/393]]]]3]]]33++]]3]]]]]]]]]3/+]33/+]310]]]]#53!!3#!!5ZZ-PJ=j\3@e?_Zf__ ??9/]2104>33!!".#"33\?ȉl5y҉A?=aD$!?Y8TavB1J>u**J:8N0f=G\h#2I@' Z22\4**Zf3/_%_ _??9/3/32]9/39014>33!3265!#".'#".#"3265\?ȉl5ELNE1@vg2cWE*vpo8?=aD$+<$HZa}H1BAfq-ib.$4?J>u=2Q>5K0?=f/@F@%+H55F B>GVA;M +0MM&&?22?2?9/9/33301".54>323&'&&5!32655!#".''2>754.#"VuC9iX6ZH9 1JNNE.?ug=_L=LX[$6L/.N;`Z[J؎ٓJ-;#'("M!fiKFfqib.!4$2%%KpK!Q~U,N;I@&\)) Z2\e=12""21,`7"`##27_??39/392/3/39/33/013265!#"&54.##532>54&#"'>320UtC :/OE1@wg%Tb\{Ibi0SG;)ctMl}EoLy[=%E5fq-ib.-O:!!9L+NX#4'/YN3s7E@$IF00 #F%U966 6O$77 M+ N ?32?9/39/33/9/33/102654&#"'>3232655!#".54.##5_l$PRP%Z-an}IUwGq_r{IONE-?ugfxC%Ea;2C66  #IpMew pa9Afqib.%IoI5M2V!/A@"%&&Z \ \ % `&+` _?3/?9?32/2/2/3/301!!!4.##532>54&#"'>320UtC+%Tb\{Ibi0SG;)ctMl}EoLy[=L-O:!!9L+NX#4'/YNos+J@)I%F #I!! !!** *O++ N%""% N ?32?3/9/9/33//]3/2012654&#"'>32!!!4&##5_l$PRP%Z-an}IUwGq_2WA%ׅ2C66  #IpMew &gFNID1b@8 ++20 "^0Z00000"\3"_0#&_"_?332?9//99//]]]9]9]103265!#".5!#"&'532>7>7!DMNE2@wgdzC !:Z\*J 4,$ %)JwHCfq-ib.+_k=^M`g5 DrV2%^(X@3I'F5' '''F**N' MM?332?//]]99//]]]]1032655!#".5##"'532>7!LINOE-?ugexC9[bjD2,)#! yJCfqib.+_ld LmV@2Z\eZd_L;_???39/9/]]]]]29/3103265!#".'!!!!!EONE2@wgdyC65}KFfq-ib.+_j=^C@&F F UFN IM?2??9/]]3/29/310!!32655!#".'5!!l2HMOE-?ugdyC^RHCfqib.+_kV3^w*C@$** )[g,[ f+_*_/$_??39/]23/39/01!#"$&546$32.#"32>'!?ﯣ_cck"O[h:hd0+\eXrC5{nai32.#"32>5!=2RzlђNOیqM\EOT(TnA:^DD^:]NnO,JՌەL1%  3\LHwW0%=M()%:@ \ ZA__??9/2/]]/]103265!#".5!!!ENOE1@wgdyC}KFfq-ib.+_j?/^7@ F UbF NM??2//]]/]10!32655!#".'!5IONE-?ugeyC^KFfqib.+_kX9]@5'' ;4Z/\//:&!_*4`K:'* _ ?32?39/]]]q92]2/222/2/104>32&&#"33#"32>7#".54>75.HrW{l4Ldtv#Wpiw]j9:xsi+W욌֒J4c\K}[3`Oa6$7%6AQK-F12K5ga $&)@t`IwY9  9XxNs7M@+_+94I!!FO77|777 ,/N+& N?32?329/]]9/23/2/]2/10#"32>7#".54>75&&54>32&&#"3 v8Y>=obS H͂}:'Ge=liI}^6nh_(QCW.K4r{ID, #*/UzK>ZA( dPoF ( 'B7&//%+55s^&J&&%+55R3&$g@ '!%+5+5VRu&Dg@ 4',/9 %+5+53&$fR@&&&%+]5+5V&Df@3&>@>/ %+]5+53&$wR@ &2%+55+55V&Dw@ /&7J %+55+553&$xR#&o2"%+55+55&Dx;&J: %+55+553J&$yR@ /&d7%+55+55V&Dy@ G&O/ %+55+553b&$zR@ 5&=5%+55+55V&Dz@ M&"UM %+55+55R3s&$'KNRg"@1'&,6%*%+5+5+5+5VR &D&Kg"@I'/&,DN %$6B %+5+5+5+53&${R@ (&-#%+55+55V&D{@ @&BE; %+55+553&$|R@ 3&8.%+55+55V&D|@ K&/PF %+55+553X&$}R@ @&E;%+55+55V&D}@ X&6]S %+55+553b&$~R@ '&,"%+55+55V&D~@ ?&/D: %+55+55R3}&$'NLRg"@2'&-7%!%+5+5+5+5VR+&D'gN "@H&4'/MC %,/9 %+5+5+5+5R&(g' %+5+5fRDs&Hg@ /'*4%+5+5&(fR@ & %+5+5fD&Hf@ .&(9*%+5+5`&(RR@ &%+5+5fD&H&R!R"@I&-& JX%.<%+5+5+5+5&(wR@  &'%+55+55f&Hw@ *&2E%+55+55&(xR&}'%+55+55D&Hx6&xE5%+55+55J&(yR@ $&r, %+55+55f&Hy@ B&mJ*%+55+55b&(zR@ *&2*%+55+55fD&Hz@ H& PH%+55+55R s&('KRg&@ &' &@ !+%%+5+5+5+5fTD!&H&Kg"@D'*&?I% 1=%+5+5+5+5B&,fR@ & %+5+5o6&f@ &%+5+5BR&,g' %+5+5R&Lg'" %+5+5wR&2g@ -'(2 %+5+5fRds&Rg%' * %+5+5w&2fbR@ ,&7( %+5+5fd&Rf@ $&"/  %+5+5w&2wXR@ (&0C %+55+55f&Rw@  &(; %+55+55b&2xVR4&bC3 %+55+55d&Rx,&j;+ %+55+55wJ&2yXR@ @&WH( %+55+55f&Ry@ 8&_@  %+55+55wb&2zTRF&NF %+55+55fd&Rz>&F> %+55+55wRs&2'gKR&@ <&-'@ CO %(2 %+5+5+5+5fRd!&R'gK(@ 4&%';G % * %+5+5+5+5ws&_vR2&Դ29 %+5+5f!&`vf-4 %+55ws&_CTR2&$7> %+5+5f!&`C29 %+55w&_ffR6&A2 %+5+5f&`f1&u<- %+5+5w`&_RR5&6D %+5+5f&`R1&a1? %+5+5wR&_g7'2< %+5+5fR&`g2'd-7 %+5+5R &8g^@ '" %+5+5Rd^&Xg@  '% %+5+5 &8f1R@ &' %+5+5d&Xf@ &* %+5+5s&avR#&z#*%+5+55!&bv7&h(/ !%+5+5s&aC=R#&ɴ(/%+5+55!&bC-4 !%+55&af1R'&+2#%+5+55&bf-&*7( !%+5+5`&aRR&&'5%+5+55&bR1,&,: !%+5+5R&ag^('#-%+5+5R5&bg-'(2 !%+5+5R&<g@ ' %+5+5P^&\g c) %+55&<fR@  & %+5+5P&\fu@ #& . %+5+5`&<RR@  & %+5+5P&\R@ "&#1 %+5+5f&B:9%+55!>@# /?@H/22/3/+]3/993/9901.'5!!.'5!F;>?6* -*/0:?>6* -*/038;81"QQL38;81"QQL-9 J@(@ O _  @H    @/22/3/9333/+]9901#&&'#5>7!'>73#3l46j3886;697Y$" -{;"T00T"EIK""KIE+07!7!#&&'#7#.'53/886<6873j65k3X><7 !$EIK""KIE"T00T"047!70+-,a@4@ H @o''&  & /329/322/3/9333/]9/+3201#'>54&#"56632#&&'#5>7!!B; #/ %+' 1 ^qȢ3l46j3886;697`;D =t  H)"T00T"EIK""KIE10b@3-,""& & %&@H  ",&/322/3+22/]/3/3/3/993393310".#"#>3232>73#&&'#5>7!7$KHC(+ q2F.%MHBq2F>t56w;>?;0<!12XA&!1XB&I00I?BF"E;1 ?@"     @/2/]/2/]993/016673#%#".'332>77@2794\UYY.sX*I7$*l3774NK|Y20X}MWS,?(1 !=@! @!!//]2/3/2/]9901#.'53%32>73#".'872@$7J*+J8".YXV\44773l*3(?,*@+M}X02Y|K1.e@:@ H @.#$$.#.)@)/]29/22/3/9/9/+]3201#'>54#"5663232>73#".'1&k #;* 4Vd$7J*+J8".YXV\4- )n  )hC(?,*@+M}X02Y|K13[@2$)$)/ !!.// ....$)!@ H! //2+2/3/]/3/3/3/99331032>73#".'%".#"#663232>73$7J*+J8".YXV\4$KHCq b\%MHBq2F"1  1%CnN*,OmA1 dy!1Q;  "@ /3/32/3]014&'3#"&'5326NF&A1#A]92J ? #-4mB8?H+3T="  ()/@ H M/22//01".'532>5!31,6","''U  'A3TMe:)@ H M/22//01".'532>5!31,6","''U  'A3V}Me:J4 4>BM \hNv g .?*mM h   4 1 ,O {  (3 [ 8) \a \ TDigitized data copyright 2007, Google Corporation.Droid SansBoldAscender - Droid Sans BoldVersion 1.00DroidSans-BoldDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0Digitized data copyright 2007, Google Corporation.Droid SansBoldAscender - Droid Sans BoldVersion 1.00 build 112DroidSans-BoldDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~.nulluni00AD overscoreperiodcenteredAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonek IdotaccentIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccentmacrontonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352 estimated oneeighth threeeighths fiveeighths seveneighthsDeltauniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLC  ,latnkernLZZ 2 "  >$ ~`!6#h'L(h(h(n(h())0)6(h(h)H)V(n)))***L+++.$.$+0f2 "4544$$$$67D7D7D7D7D~88889(h(h(h(h(h(h)H(n>b>b>b>b>l>z>z>z>z>z)H>>>>>@$ 2(h 2(h@B(h "(n(n(n "(nA(h(h(hB(h(h$B$ )6C(h(h(h~)H)H~)H)H(n(n(nD(nEEJ ````)#h*J''OR 2(h(nRSS SNSSTTUUWZZ[[\|__`abbfffgFgxgh8kk(hlkldljlll(hmBmmmmn n"n n4nRnn)Hno)H)HoTp6q,t~tuu|vZw,wxhz{}~PZ F\f>萢d^ؓZ^ĕJܕ@BDE)G)JLNPRTVXZ\^`oRp)qRr)sRt)-{{{^&*24789:<$&,02468:fmq_al~IKMOQSUWY[]_acegikmoqs7qrvc &*24789:<$&,02468:fmq _al~IKMOQSUWY[]_acegikmoqs,79;<$&8:qEGoqs;) ) )&*24@)`)fm) )_l~IKMOQSUWY[]_K$,79:;<=$&68:;=?C_iq(Xr!#%')+-/13EGoqs9= = )")$9:<@)`)68:C_i= =(Xr!#%')+-/13oqs9) )&*24fm) )_l~IKMOQSUWY[]_9) )&*24fm) )_l~IKMOQSUWY[]_c &*24789:<$&,02468:fmq _al~IKMOQSUWY[]_acegikmoqsK$,79:;<=$&68:;=?C_iq(Xr!#%')+-/13EGoqs533$&;<=q8:;=?C_i(Xr!#%')+-/13oqsK$,79:;<=$&68:;=?C_iq(Xr!#%')+-/13EGoqs 7$&q R R")$&*2467DFGHJPQRSTUVXYZ[\]q  !"#$&79@CDJ_fimqy{~R R(XY_`jlmr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt#$C_i(Xr!#%')+-/13R R")$&*24DFGHJPQRSTUVX !#CDJ_fimy{~R R(XY_`jlr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnf f$&*24DFGHJPQRSTUVX] !#@CDJ_fimy{~f f(XY_`jlr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjln) )&*24FGHRTfmy~) )_`jl~  68:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`R R")$&*246DFGHJPQRSTUVX[\]q  !"#9@CDJ_fimy{~R R(XY_`jlmr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt9) )&*24fm) )_l~IKMOQSUWY[]_ = == =&f fYZ\79f fmprt) )J) ) ) )) )[]@f fDJDf fY"$&(*,.024R RWR RR RIR R6) )R) )`j  JLNPRTVXZ\^`= =I= =2R`j  JLNPRTVXZ\^`qx)qrx DD"&*-^24789:<=;IWYZ\^$&,0246789:;;=;?;fmq^ _alm~IKMOQSUWY[]_acegikmopqrst DD"&*24789:<=;IWYZ\$&,0246789:;;=;?;fmq^ _alm~IKMOQSUWY[]_acegikmopqrst DD"&*-^24789:<=;WYZ\^$&,0246789:;;=;?;fmq^ _alm~IKMOQSUWY[]_acegikmopqrstf f DD"&*-^24789:<=;WYZ\^$&,0246789:;;=;?;fmq^f f  _alm~IKMOQSUWY[]_acegikmopqrst9) )&24=fm=) )_l~IKMOQSUWY[]_7) )&24=fm=) )_l~IKMOQSUWY[]_ $C_i(Xr!#%')+-/13V $,-679:;<=@` "$&68:;=?C_iq(Xr!#%')+-/13EGoqsB$0=D;=?CD_i(XYr!"#$%&'()*+,-./01234.R R = )")$&*-02467'9):@=DFGHIJPQRSTUVXYZ[\]`=q  !"#$'&'679@CDJ_fimq'y{~'R R(XY_`jlmr~'''''  '!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt   o oI[]@ a= ="FGHIJRTWy~= =  `j  68:<>@BDJLNPRTVXZ\^`= =I= =d &*-24789:<$&,02468:fmq _al~IKMOQSUWY[]_acegikmoqs9 "@ EIRKLMNOW)YRZf[R\R])`\{HH)R){f{\qH R #%='=)=9f@ -f:) )&*-R24fm) )_l~IKMOQSUWY[]_8 "@EIfKLMNOW=YfZf[R\f`\{HHRR{{{{Hq\ { =#%)')))9f@ B) )0679:;<Y\ "#$&689:q) )mopqrstR R")$&*2467DFGHJPQRSTUVXYZ[\]q  !"#$&79@CDJ_fimqy{~R R(XY_`jlmr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprtf f "@ERKRL=M=NROR`R )=RRRRRR R R = )\")$&*-02467'@=DFGHIJPQRSTUVX[\]`=q  !"#$'&'9@CDJ_fimq'y{~'R R(XY_`jlmr~'''''  '!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt) )&*2468FGHRTYZ\ ",02479fmy~) )_`ajlm~  68:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`acegikmprt3R `j  JLNPRTVXZ\^` b)fi)mqrsux)fms _dglpqrtwx$m_bfisvyz{}~qrxy}~)O &*24789:<$&,02468:fmqrsux _aIKMOQSUWY[]_acegikmoqs79;<$&8:glpqrtwxoqsq &*24789:<m}$&,02468:_bfimsvyz{~) _aIKMOQSUWY[]_acegikmoqsR R")$&*24DFGHJPQRSTUVX !#CDJbfmqrsuxy)R R(XY_`!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnfms)F$,79:;<=$&68:;=?C_bgilpqrtx(X!#%')+-/13EGoqsfms)*) )&*24fms)) )_IKMOQSUWY[]_ ")$&*24789:<DFGHJPQRSTUVX !#$&,02468:CDJfmqrsux (XY_`a!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnoqsfm)F$,79:;<=$&68:;=?C_gilpqrtwx(X!#%')+-/13EGoqs633$&;<=q8:;=?C_bditx(X!#%')+-/13oqs)) )&*24fms)) )_IKMOQSUWY[]_R R")$&*2467DFGHJPQRSTUVXYZ[\]m}q  !"#$&79@CDJ_bfimqsvyz{}~=R R(XY_`!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt-m_bfimsvyz{|}~) _bgiqrtwxfmsy|}~ _biqrx,) )&*24fms) )_IKMOQSUWY[]_R R = )\")$&*-02467'@=DFGHIJPQRSTUVX[\]`=m}q  !"#$'&'9@CDJ_bfimsvyz{|}~)R R(XY_`!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt)y~y|}) = =I)= = = =y= =yz}~)R Ry}~)R R ) )y~) ) = =I}== == =I= ==~)[]@~)[]@)R RI}))R R[]@R RIyz~)R R[]@y}~)8) )&-=24=) )_l~IKMOQSUWY[]_=  mv|} R R\&*24789:<$&,02468:\\=\R R_ajklmr}~q\ \  \ \\IKMOQSUWY[]_acegikmoqs) )) )l}~m}7) )&*24 )_l~IKMOQSUWY[]_4  v|} 1  v|} m}m) )&*24))) )_jlm}~)  ))IKMOQSUWY[]_gR Rq)\\RqR Rjkr})))))\  \)\ &*24789:<$&,02468:=))))) _al|}~)) ))))IKMOQSUWY[]_acegikmoqs*|} !79;<$&8:voqsR R\\\=\R Rjklmr}~q\ \  \ \\B))))))f)lr|}~)) ))))1= =))))= =l}~))))))&) )) )| L) )&*24=)))) )_l}~))))))IKMOQSUWY[]_V$,79:;<=$&68:;=?C(Xrv!#%')+-/13EGoqsE= =33$&;<=q8:;=?C= =(Xr  !#%')+-/13oqsB) ) )&*24@)`)) )_l}~IKMOQSUWY[]_RR R")$&*2467DFGHJPQRSTUVXYZ[\]q  !"#$&79@CDJqq)RR R(XY_`jklmr}~q    qq!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprtc= =)qq== =jkr}q    qq*rv.) )=))) )l|}~) ))0)))))f)r|}) ))>=))))=))f)r)|})))))))) )))) q |    | #vv m}!m}$= == =m}){}  ) )) )j}  = == =m}!) )) )j}  "[]@m= == =j  0R RIR Rjm  $= =I= =j  []@}j}  )))f)})))=)f)})))qm}= =q= =m} j  ==!m}m}) )) )j  j  R R\&*24789:<$&,02468:\\=\R R_ajklmr}~q\ \  \ \\IKMOQSUWY[]_acegikmoqs7qr$,)7R9R:f;)<R=)FGHJRTW)Y)Z\))))R)))$R&R6f78R:R;)=)?)C_iqRy~)))RR==R=)R)fffR(X`jrv==R)R)RR=R=======  R)!#%')+-/1368:<>@BDE)G)JLNPRTVXZ\^`oRqRsRE=IfK=L=M=N=O=WfYfZf[)\f])pfrftfE=IfK=L=M=N=O=WfYfZR[)\f]pfrftf   $')) ,, ./ 257>DFHKNN!PR"UW%Y^(mm.}}/06@EKW]clmpqrsvwxyz{}~  !$&',,0022446;==?@CDJJVVZbddfilmo|~ )  *((+XX,``-bb.jj/lr0tt7v|8~?NWYgwxyz~ !!##%%''))++--//11336688::<<>>@@BBDEGGIVXXZZ\\^^`accffhhjjllnt latnfritzing-app-0.9.3b/resources/fonts/DroidSans.ttf000066400000000000000000005631341272363601300220660ustar00rootroot000000000000000GDEF&= GPOS=@GSUBlt< LTSHɛ OS/2˕`cmapԢVxcvt 9~>Lh fpgms#^gasp= glyfž?qhdmxBheado<6hhea S t$hmtx.'loca)Ljmaxp* nameXV0postP'?prep!e Ys_<3S = Zbm d Zz/Z933f @ [(1ASC@ m J '7+3h{fmhRh=hRhf?R%hbhh`hRhhhqhZhjhj%%?hfhfhfh%m}y9}R+H}}'h'`7PRmm3B)J?^qqHq%%+qq1Z!# R=h3hf'hhDh{hhy3dDRhfRdm{hf1=q%#?BT?,hD}9999>R@y/}}}}}h}7?^?^?^?^?^?^^qHqHqHqHqoqqqqqhfs  ?^?^?^}q}q}q}qyqy/q9Hq9Hq9Hq9Hq9Hq}%%}%%}%%}%%=0RDRR%+HffF}q}q}q}Hq11`1r'hZ'hZ'hZ'hZ'!'!'!7 7PRRPRRPRRLh?^^}s'hZu!m{%BTJ%9PR}R?R}BN'7#h`#hN@7qZq oZqq#qq qqq1qqq9}'hR@+HHu39\HZ}}'#h`H=;?^u3`)HqD^qq) q#m)7y?#Hq3qZ 37 RRRJff?f?{{o Zf7NRNR jh`hDh?hw%NfS3Oqb) Jhfd%whfhfhfhfmo}% ;%/1!Uh%TTf R+?^Bs3VV}q/HF19Hq}o%'/d ) ^9#h}q d}Xq?}q}Pq}o}qhu))//3#\HD J+/D)\D}q}q')77=#)HHHw=3w=3RJ-jqHR?^?^^9HqqHfqHf\HD\H}q}q}q;7   H3/3/#`#qqNFPNP/}q)\o\Z^?^?^?^??^?^?^?^?^?^?^?^9Hq9Hq9Hq9Hq9'H9Hq9Hq9HqRbR}q}q}q}3}q}q}q}q}q}q}q}q/////7 7 7 qjjdsssd1( +    &"  "&   *. . * .  ( '    ' &&&&&& """"" "& . .... &&&     ..          $ ...."""*(****   ...... &&0 &"*$&-  &""" &"& . . -(0&""0$  .* .    &+   *" &+  . &" &" $.  .&&*   &&"""000 '&&&&&&&&&&&&  """"""""""""..&&&                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            ~ # O_?M   " & 0 3 : < D  !!!!"!&!.!^"""""""+"H"`"e%  #P`>M   & 0 2 9 < D  !!!!"!&!.!["""""""+"H"`"d%aI1vhcb]gD Xz} Bvt %"iOS`xlhijkopqrstu45]^   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a rdeixpk vj0-s12gw'*).l|!cn,B/(m}b  3:45y KRqNOPzSQL~ # O_?M   " & 0 3 : < D  !!!!"!&!.!^"""""""+"H"`"e%  #P`>M   & 0 2 9 < D  !!!!"!&!.!["""""""+"H"`"d%aI1vhcb]gD Xz} Bvt %"iOS`xlhijkopqrstu45]^@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@ `@+ F3UU0U0o0`@8F/?O_o@FQ_+s@ F+|@ F/?O@ F/@|P|t t0ttt oooouooKo nnnnKnU?gg/g?gg@fPfff?eeedd@Od Fa_+`_G_P"[[T[[I[;[ZZkZKZ;Z3UU3U?WW/WVFV FTF@mT FRP+?POP_PHHHeHVH:HGGG;G3UU3UUGU+oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss^stu++++++++_sssssssssss+++++_sst+++_ssssssssss++++_s^stsst++++_sssstssssststt_s+st+s+_sstt_s+_sstt_ss++sts+st+ss++s+++sss+^ NuJU}qq}}winfoxG:x Zy mmm{Too****o)q)M|D[?e?g = y j 4  t 9 I+|"f8M;Ypik_9w"9'euHH j!!t!"]"#J#$$5$=% %#%~%& &&&'+'e''(*(z((()m)))))* *r******++++D++++,,,6,--4-K-c-|--.x...../////00070N0e0~11$1;1Q1h112 2222223Y3q33333444/4F4^4u4444445i55555566&6=6S6j666666777;77788,8C8Z8q8888899,9C9V9i9999::&:>:Q:t:;;%;;;O;b;y;;<>%>8>L>d>|>>>>>>??*?=?Q?h?z?@;@S@i@@@@@@AA&A9ALAcAzAAAAABBB2BHB{BCDDDDDEEENEEEEFNF{FGG2GGGHHAHHHII(I0I8IjIIIIJ=JEJMJJJJJK)K1KyKKLL LMM)MBMXMnMMMN#NOOPPnPQ Q^QfQR0RrSSSnST7TTUUV.VWW2WKWaWwWWX(X?XXXXXYrYZ/ZFZ]ZZ[[ [[[\\\]][]]]]]]]]^g^o^w^^_;__`=``aeaabebbcecmcd`ddde@eeeff$f,fkfsffg2grghhshiiijSjkjjkLkTk\kuk}klTlllmmTmmmmmmnn(n@n[nvnnnooDorop*ppquqr]s*s2s:smsssttptuvvwAwx~xxyyAypyzzbzz{{-{||}}`}}~~-~p~~~V['ۃNZb $;RfzGt|ۈ>cω.F^u)eڌK@ӎk\ pЕ8Rl,BȗFJ3HVޝ >aD$aϣ3Ѥ&rاD.ЩWkά%hϯ6Og/LlĶwqAɸ'/7Ndʺ(B[t»,ûݻ(08Qi˼.G_%7Pf翚f¢(û(ĄO&ƇljǝDZ '@ZsȍȦ7Piɂɛɴ)@Wnʅʝʳ3Lfˣ "9Qh̘̲1Km͑ͨͿ6Me}Δά4J`xύϥϽ0FZНKѺ/z)Ӓӿ +@  p@  H?2/3/+]33/310!!7!!IhyJh:@ H@  H ?//+3/2]10+#34>32#".Py3"./""/."&5!!5&%5""57@# / o  ?33/3/]]]9/10#!#J)s)-)r)3@X!   P P  H   ?O   /3?399//]332233]22/]33/3/]9293/92393/3/10!!#!#!5!!5!3!3!!!?RTRNA+RR%TT#@}TTHPPH{-6?@34/))/!!p/<753&&'4.'662]T2f`T !W`e/YV*1[OdCB8JX[.+F3][(B1YSFrT7 !BUnJCoS5 *)ZBSkH!7-&b$9/&qYf3 ';?]<>@3<><>(2#(AA  0?>%7!-????/]]99//881032#"#".54>3232#"#".54>32#GPPG$JsOIpL&#IqNKqM'GPPG#JsOJpK&#IqNKqL'՞,JHlv??vllu>>uJIHlv??vllu>>uJm}!S@M'JI,IH G6AGB B6B6B;54.#"2>7%4>7.54>32>73#'#".!4$;V8/B*Vd:bTH }4P7#B`}(MoG<-2^XS[02Tm<`+" )5A'1`l|Nis="AAC%#>@F)$=,Y(6!?HU86[A$NzdV*$MWc9KwS++SwK@m]O$73#.R$JqN%GjENqJ$1}]2w^Z=@ ??]210#>54'3$KqNEjH$NqK$1|Z^w]Rw$@?2/]/^]]10%'%7++wo`f`Fof )@   ` ?32/]]22]10!5!3!!#}}{?y 8@ +  @ H_ //]]+32]]]10%#>7j'/36z|{8=}5RBy@ @//]105!RѨ5@ 4Ddt H//+]]]]1074>32#"."./""/."o&5!!5&%5""5@ ?/382/8310#!Jb'&@o))o  #ss??/]]10#"&&54663232>54.#"3qvs93o~wt:BkMMlEElMMkBݱfffe貖KJᗖJJ5@!@n~ @ ??/^]]]3/3]10!#4>7'3ǰ`+baY"y{+`#<@ #o%%"o! " s"t?2?39/]3/3]3/310!!5>54.#"'>32!p^KvS,"?V5_Ef(\jvA`l;5]K}QL;Z? M54.##532>54.#"'>32.StGAʊmUW]\W)5bYQ~U,$B\8kJ\&]n}Fln8`IxX9 `t@"-.2(JlCDa?(Jf=4R9C6}6)6a? N@, Vn  w_ t??39/322/]3]]9/]33332/]210##!533!4>7#?հ]{  eHH0d8{uf"11.*N@&o,,'$$(h#Y###@ Hs't$s ?3?9//+]3/]]333]3102#".'532>54&#"'!!66!cHDŀ3c[R!!Ybc*O|V.??9Z7' i7lir~C $ %NvQ 9]q +?7@ 1n "AA;o 6u,s's??9//]2]2104>32&&#"3>32#".2>54.#"q5\ƅ./+#X+ZdC* 9L_;_l;>tfdJn#####  h88Y8(888H88C&CVCCC-s;s??9/]]]]]99/]3/]]]]2/]]99102#".54>7.54>32>54.''">54&5TqB(F`8:oW5Cyfnu=-Lh:1V?%Cr DhHFkH$'If?~j}#>W30U?$~,XXClWEL_vI\h86e\Kx`JIZmBWX,5Y?##A\84TH@<Tje9R@34BT6ejj)=5@9o??/n   4u*s%u??9//]3]210#".'532>7##".54>32"32>54.5\ƅ..,#X+f+ 8L`;_l;?sfeJ%.;rjrDNG(TWFoN*/K`0CkBf'>@)))) 4Ddt@  H#/?/+]]32]1074>32#".4>32#"."./""/.""./""/."o&5!!5&%5""5'5!!5'%4""4?f a@/"""" d t P D ;  /   +  @H_ /?/]]+32]3/]]]]]]]10%#>74>32#".j'/3"./""/."6z|{8=}5'5!!5'%4""4fN@0@@o0 Pp?/^]]]q33/]]29=/33/]]10%5d!ff\@= @ {hB9/o/]]3/^]]q/]]]]]]]]3]2105!5!fdTffN@0@@o0 Pp?/^]]]q33/]]39=/33/]]105f dBlfX%%';>@!2(('F F=/= -7Q?3/2/^]9/]9/3/1054>7>54.#"'66324>32#".'B20D+9U8SF?Qa]h86P64B&"./""/."%9\PM*)CEO50O94"*;3`WCiZT/-C?B,&5!!5&%5""5mJWho@?X`'''FF'N1 j@j;@NN, [d@6S@EI/3?99//^]^]322/]]q9///]]10#".'##".54>3232>54.#"32>7#"$&546$3232>77&&#"%9La:-I4!6GY5MwR+;ob-ZRE"+.F/V{ZO=wod+V؂fv7jeU7N2M*Je?>}qaH)2A#%B18eVezD`5D(=hNݘOoR&,fEeՅw-SsE :^x@$FFII@ H/@_ H?2?9/9+/83^]3/8+]q39=/99]]99]]3310!!#3.'ߢg;Dj4 Z$*[pg1111$Zd0 #`y $`"`??9/^]]92]]]9/]]q210!2#!32>54&##!2>54.#ÃB'JmEEyZ4A{oTrF XwI !K|\'Wg>lR7 -OxVdm:J;Y;xh(He=8^C%}#L@@H ` p  @ H %%[f$!_ _?3?3]3/+]]9/+]10"3267#"&&546632&&k{C;vvYN'NUa;LWlON?'QډۖN#ln,* . &@ [gZd``??]10#!!24.##3 `_B~uɢ ^\ՊC$ B@&g  Zd _O _ _??9/^]q229/]10!!!!!!! =< p@@8 H  / Zd _?o@H@H_??9/++^]q2^]3/+]]q9/10!#!!!!}+7@++ )Zg--[ f,+_$_$_??9/]29/10!#"&&546$32.#"32>7!7pvKV_ oXH$SX].zB7x,I>73 ii,*Qډ؜V  =@# Ze   Zd _ ?2?39/^]2]]]210!#!#3!3պfVhRd W@& + { T + ; K   Z@ H  ?2?2/^]+]22_]]]]q10!!57'5!df))ff)h)H{s/@`p/Z    _/?/^]3/]]]10"&'532>533L"N-%K=&;i{ 2XD^ie1 d@- f   /Zd  H@ H ?3?399++2]]]3/8^]33/839]310!##373=yr%3#@Zd_??]]31033!Ǻ=/@69 H9Z@ H H eO  @ H&  Z d H  H ?22+2?33+322]+^]]]993+3+2]+210]]!##!3!#46767##EAJI?9XJw4=GIQ@)(Ze'   Z dH  H ?22+?3+322]]]]2]210!###33&'.531MLA9LLJ CC> }q'4@ [g)))p)/)_)[ f(#__??]]]]10#"&&54663232>54.#"qQ훣LLQ4krrk22jrrl4ݩllkk뫉ۙQQۉڗQQ3F@,[(8Hg@Zd`0@` ??9/]2^]]]]10###!232>54&##37~Ϙj~54.#"q1_]+Zyg3)LLQ4krrk22jrrl4݃ⵄ&^54.##d 1Qh7Z~Q%)SW\W]>q\#EgEHd@h3B@'Y##Zg555`5?5*Z f4*'_$ ` ?3?3992]]]3]10#"&'532654.'.54>32&&#"EsoA"W`f2Iz]YU)@tawJCAXzFsT[\/aj7#"xp6PC?%#ShTX_2-#+q`9SC;!$L`~^@2  O  0 Z@Wgw@  H_??2/+]3/^]]2/]]]]]q10!#!5!!q^_/@ZeoZ d_ ?2?]]]10#".5332>7BɈąDYR(LrĐRMzH6bQ l@ `p@ H/@ H H @  H ?3?33++?3/83+]3/8+]39=/33103#3667'*.Ja[JJa*߶HH@HHH@/H%D%%$%D%T%%% p@ H,o,, ,0,,@ H H %% H% H%?33++3?333++/83^]]]3/8+]q39=///]^]q3+3+3+3+3+3+103>73#&&'&'#3>7)   ~  8pi^&&Zcg1rJ3l/7437/p6\.cb[&%blo1` @  7  8p@ H   /  @(' ?2?399]]/822/83^]3/8+]q39=/3]33]3/8310!##33`ZLN[{/L7s@  @ H@@/OZwO6?3?9]/^]]]92/8]]]33/8]]]]]3+]103#3TBB/R 8@ g  ? O f __?9?92/2^]22/10!!5!5!!TM:ۑ9&@??/]210!!#39k1!?///8338310# J3$@`p ??]2103#5!!3jϕ)%?/3/103#)f%d!NH//3/10!5!NR! @ _/]/10#.'53x#RM?+.0SXQ"QQL^^#2T@)G#U44o40H @ H H V3P*R$P??3?9/22/++^]2210!'##".5467754.#"'6632%2>55%!BN`?EtU07Q4SB@Jdfa0/=hL+ZzI a-A*'Q{TECZ70"(8)Yb&MuOc 9Q3\V?/8-HW11@ I%GT0*P  P?2?3??22+102#".'##33>"32654&^m<32.#"3267ReJLfN268<:Q66{?Ֆۉ>"  %q04@&GU22.H V1+P P?3?3??]2210%##".54>323&'&&53#%2>754.#"T;M`<]n<32!32>7"!4.`nHBxecn;L3WQL'(MQW`r 9XJ҇֕NGnq ۜDqP,p@N`?OG/ OP ???^]32/^]3/]322/]9/]]]10###5754>32&&#"3-U|N;c'/I((:&?KD`kT# 0SAh%^?R^@ 2SG7/`7p777/7/'HYG@M H  0@`````@'@ H'2 7&&5467.54>3232654.##"32654&#"&/_],!)8]Q$A͋kj5'BW/*6@E+G12ba%O@;aH7ZA#L?)\lcdgidcjJq#mEL^5  (!/Pm=Xa4*PqG<[B* R5=Y*?Q`3Yb4 %@.sl.:! ,M`spow{tx2@GU` G TP ?2??322]10!4&#"#33>32\ipQnC ER\0Â4f`2+?*3u%@  GTS??3/22]10!#34632#"&d=-'?,-=J)<6 +:98u!.@# #G  T"S P??3/2/22]10"&'532>534632#"&B0?6#.#"Hm=-'?,-= 'A3M{W/_<6 +:98^@ D@ H/ G T @ H H ??399++?2^]3/8+]333931073##3V%om7i%RZ6d@ GT??]10!#3d^,e@?# G   g w  G,U... .P..GT-#P( ?22??32232^]]]]9/]]]]210!4&#"#4&#"#33>323>32diIfAciMh? BOY.x&IW`2Â/[XÂ4f`J+?*X^/D-3^0@GU` G TP  ?2??32]10!4&#"#33>32\ipQnC ER\0Â4f`J+?*3q-^0@HW!@!!!!H V PP??^]]10#".54>3232654&#"-C}ogGC|ogG'ՑLLՉӑKKӈ?^06@.HW22& G T1 P +P?2???3222]10".'##33>32"32654&;`M; :M`<^m<754.#"".54>32373#46767#5LiAAlQf]n<H;?hK)9GX^3_QJ+P=%Z?^5H@-%GW7?7_777,G V6&)P," P?2?992]2]]]310#"&'532>54.'.54>32&&#"?:m`m;LTY,A[95\HHsP+7dVaH?AGfb8^FHqP*-PxQ(#");$212#4./7#".5#5773!!-*# (04>jM,Ni?  Ne}QNabJ0@GU`G T P??3?3]210!'##".5332>53u ER\0[\/joQnC+?).bi=4e`:Jm@ H H H@ HP/O@ G  ??39]/8^]]]3/8++9=/3+3+10!33>73w  ǼJ!hl``lh!cJ/ù/@ H/ H' @ H  H  H@ HT''@ H[  H'  '-.H.@ H...1 1011@-  'fv?33]3?3]33/83^]]3/8++39=///+]+]3+3+3+3+3+3+10!.'&'##33>733>73   翃  Ĭ   h-24:>?:2j%J-ig[Wa_!k"\_XWhm/H#J @ 6 9k{W:JdtX5E   6@H@Hk{W:J  0        ; K (    ??/]]]]]]]^]]q]]]++]]]q9=///]]]]]]]]]q3]33]]]]q10]]33##u3fL J"d"H@ H$$$$P$$/$O$@ "#P?2?333/83^]]]3/8++9=/331033>73#"&'532>77  ǼNAVtP4L@#0F4%9J(XXR#Va^!c'QZ1 ,@)R5J l@  H@ H ? _   HH?@ HO HO?2+?2+/]+33+]]3/+3+]310!!5!5!!5 }D='@@% '#  #_)??9/]]9/]332/210.54ᒑ4>7-A(M_6}}6_M(A-wssw0=# !GnNNgVVgMNnG! #=0i{ zj-@0@p@??/^]]q103#閖3)@@% $$$# ??9/]]9/]332/2104675&&54.'53"5>5wssw-A(M_6!A`>}6_M(A-;jz {iL0=# !GnN4H-VgNnG! #=0fJZ#<@ %%   @H  ? O o  /]3/+2/]]10.#"563232>7#".%7-)<;8d27C/%7/(<;8c27C !,l  !,l ^A@ H0@ H /^]//+3/2]10+3##".54>32y3#..##..#H&5!!5&%4""4%Z@%F %'@'H 0 @  s!s@ H??99//+33/^]]29/3210$#5.54>753.#"3267vnLWb45aVH.58<;Q6 KljˈK !  %D#(u@ o# H@0 H**!@ H)!u /"""""""""ts??9/]323/+3]3/++399//^]32102&&#"!!!!5>55#534>jBB8K0RY@+ )DaCՉDW_2{#7@#. !p99 $@1 H8  ) 3   ? o  /]]]2/]93]23/+]2]3/]9/]210467'766327'#"&''7&&732>54.#"#b/l<7&&54>32&&#"#"&'532>54.'.7>54.'-:KU7dVaH8AGcf9_FHqN*)4EL;l`l;LTY+E]73^LIsP)?eH#)!AlR/&)3S@-&rT=bD%( ';9.,/ANa>4UD1&mNGoM(! '3--1>NdY%?:7 $.8"&@;9-:3 j 5@! @P 0  /]]32/^]]104632#"&%4632#"&38('::'(8w8(#:&(8s6015522560 &522dD%AUj@C"""&L444WB& /`p-G;Q-?/99//]^]/]q99//3/10"32>7#".54>32&&4>32#".732>54.#"{=^@!=_C69815<#fe36id?;>4a6ahha66ahha6meꅅeeꅅe,SxKNxR+  BzgexC!ha66ahhb55bheeꅅeeDB-N@/-///O///$ `  .-'?99/]2/]]2210'#".5467754&#"'663232>55'/8#+H4c=80Z*03u<}w3D)2*":+R# 3M3flH9d$jz:9+3-,A,1Rs `@ P`   @! H    /3/39=/93333/]]+299//]310%R5uu6tt)NNNNf9@$yVK8 ?/]]]]]]]]]10#!5RBydD:N@}R  E---P;/`p&@4J&??99//]^]q3392/]q99//]^]q929++]10]32654&#####324>32#".732>54.#"H[OSYF-9C5*! _騞6ahha66ahha6meꅅeeꅅeHEJ;0K9( nW%G8`}ha66ahhb55bheeꅅee//3/10!5! {V'C@, ))0@ o #?3/^]]]q/]]104>32#".732>54.#"{2UsAAsV22VsAAsU2{4F((F55F((F4AsV22VsAArU11UrA'E44E'(G55Gf :@!  ` ?32//]]333223]10!5!3!!#5!}}}{1Jm@@ O   @ H@H ??9/+3/+]210!57>54&#"'6632!m9H(B63]-N6RUC;"A@2&^0A!?[92VU[79h0a@<2_222@ H''@ H/_&#, ?3?39/^]9/+3/+]3/9/910#"&'532654&##532654.#"'>32NQEXX(S~VF{9?5bXk`bb\T#/;a3E=DL,EiF#NjjN73#//*?MQ#yLQQ"QXSJ7@" G U `pGTP  ?3???2]21032>53#'##"&'#3djoRnC 0gHj#4e`:ST.*&(#U*6qf7@!0@P    /2?/]]9/^]10####".54>3!fxy=U_m32#"."./""/."&5!!5&%4""4#9@ @ H //9/+9/]33310#"&'532654.'73-1GP.?%Zy9":+all+1# s):?J4@!O@ H 0 ??/]]33/+]103#4>7'3&^J<<8(I`B.@ H!! ?]+10#".54>3232654&#")MmD?jN+)LmD>kN,:KVUKKUVKmSY//YSSX..XSwyywxssTs V@/    @     /3/399=//3333/]]299//3]10'7'7tt6huu5eN\\NbeN\\Nb?&{'J<0@?@@]5]]5]]]55?55,&{'5t3(@@p@]]5]]5]5?5&u'<?<@'8p8P88333d3P303 33L]]]]]]]]5]]55?55DwD^';D@2(('F == F@H ''-7Q/3?2/9/+^]9/3/103267#".54>7>55#".54>32P'A20D+9U7TE@Ra]g85Q64B&#..##..#%:[QL*)CEO50O93#*:3`XDhZT/-C>C+/&5!!5&%4""4s&$CR&%+5+5s&$vR@ !&l%+5+5s&$KR&%+5+55&$RR@ &,%+5+5+&$j!R@ &)%+55+55&$P}1@ P@ %+55]]]]]]]55V@* Z$4T  g@  __ _ O    _?3/?99//^]q2/8329///]]]]}32310!!!#!!!!!!#V%˺=ul;<}&&z O*$ %+5s&(CR &´ %+5+5s&(v?R@ &J %+5+5s&(KR & %+5+5+&(jR@ & %+55+55>ds&,CR & %+5+5Rs&,vxR@ &j %+5+5s&,KR@  & %+5+5@w+&,j R@ & %+55+55/]@:[g! !Zd _?o@H``??9/+^]q3222/2]9/103!2#!#%4.##!!3 /_`B~uP %\^`ՊC$5&1RR@  & !/ %+5+5}qs&2CTR(&.( %+5+5}qs&2vR@ 4&X(. %+5+5}qs&2KR@ (&0( %+5+5}q5&2R}R0&1? %+5+5}q+&2jR@ 1&(< %+55+55-{ H@HH H H HH@0H@  P   Pp ?^]q2323/]3333]10++++++++7'i=Bh?fg?i>gf=g}q&1\@:)*'[ g333p3/3_3[f2)*-"_  -_ ?3?399]]]]9910#"''7&5466327&&#"'32>\[^Q훽NZa[L^BP.0C0rGrl4jX/rErk2c޷lGNd*k*&N QڊTQs&8C=R& %+5+5s&8vR@ $&H %+5+5s&8KyR&  %+5+5+&8j}R@ !&, %+55+557s&<v1R@ &c %+5+53<@![g Zd``    ??99//22]]10###33232>54&##37~Ϙ~54.'.54>54.#"#4>32+?K?+'F98X=!8eUa5AHL%8Q4+H8?U5)>H>)!W~Q'#"-@($;8:#(DCF*6O?6:C,*>)0SANhU%&Lt^!&DC3&93 "%+5+5^!&Dv5@ ?&39 "%+5+5^!&DK@ 3&3;3 "%+5+5^&DR@ ;&)7#"&'#".732>55"!4.^7Q4SB@Jd+3gal9`1UNJ%'KOU1>"L_tJG{Z4aO=hL+ZzI n 7T3ECZ70"(8U]U]Gnq rs6U;'Q{R\V&MuOc 9QcDqP,qo^&FzB /&  %+5q!&HC(&.(%+5+5q!&HvR@ 4&v(.%+5+5q!&HK@ (&0(%+5+5q&Hj@ 1&(<%+55+55g!&CU& %+5+5B!&v0@ &t %+5+5U!&K@ & %+5+5%&j@  &%+55+55o-#'9t@F(H# """ W;@;;;;2H V: #!!-P07P??99//]]339^]]9///9210#".54>327&&''7&&'774.#"326-C}ohG?vif+xZJ(U/FAz;JCoO,"FnKMmF!!GmL=ܘOBww~A;<vQr7{ H,quAݰ8kR2.XUL}Z1&QR@ !&"0 %+5+5q-!&RC &״& %+5+5q-!&RvP@ ,&N & %+5+5q-!&RK &( %+5+5q-&RR(&)7 %+5+5q-&Rj)& 4 %+55+55f+`@0-"Vf(8@( H'  `?3/^]3/]q/3+3]]3/]105!4>32#".4>32#".f)*  *))*  *)#/ /#!//#/ /#!//s/$-\@;'(%H  W/@////H V.('+"P +P??99^]]9910#"''7&&54>327&&#"4'326/C}o}bDP?FC|o?q1DP>EK-D'rH-'ՑL5mJHՉӑKlIIцT3џc{!&XC&! %+5+5!&Xv`@ '&W! %+5+5!&XK@ &# %+5+5&Xj$&/ %+55+55 !&\v@ /&g#)%+5+5? 18@/H W33' GT2,P!P?3?3??2222]10>32#".'##3%"32654&d:M`<^m<>%+5/q'8U@1-G& "&U::6H V9'%O" 3P (P?3?]3?9/32?]29/22210%##".54>323&'&&55!5!533##%2>754.#"T;M`<]n<32\ipQnC{ ER\04f`Չ+?*\5&,RR@ &#%+5+5r&R@  & %+5+5=|&,M"R@ & %+5+5*o&M@ &%+5+50@&,NR@ & %+5+58&N@  &%+5+5RBd&,Q %+5DB&LQ%@ o%%%%+]5Rd7&,OPR@ & %+5+5dJ@ GT??]10!#3dJR{)&,-8@O@H@H!!!P!!!@H+]]]]]++]]5&LM.@ 555_5@5 555@H+]]]]]]]]55H{ds&-KR@ & %+5+5W!&7K@ & %+5+5;&.9s %+5;&N9!Ĵ %+5JI@ D@ H/GT ?2?3992^]3/8+]339310!#373#m3oQhJ5##33&'&&533M"N-%K=&7d{ 2XDMLA9uAA8}4 ^ie1^(:@" G$U**`**GT)P P????32]9/10"&'532>54&#"#33>320?6#.#ipQnC ER\0"Hm 'A3m4f`J+?*M{W/}q&2MR@ *&() %+5+5q-o&RM@ "& ! %+5+5}q@&2NR@ -&2( %+5+5q-&RN%&* %+5+5}qs&2SR@ 4&C(< %+55+55q-!&RSF@ ,&E 4 %+55+55}*[@6Z'' g,,[ f+_O__$__????9/^]q]29/29/]10!!#"&&546632!!!!!"3267&& +[0LLbT>Vrl44kr4Z&&Y lk<QڊۙQXq^*6?d@;?H111 7H%WAAA@A+H V@P?+????.<4P .P?332?3329/^]]]]q29/]29910"&'#".54>326632!32>732654&#"4.#"`A?ƀgGC|oy?r pmmpLՉӑKojipGnq ;bDqP,s&5vTR)&# %+5+5!&Uv@ #&@%+5+5;&59w# %+5`;^&U9%+5s&5LR"&( %+5+5r !&ULp&"%+5+5hs&6v;R@ @&p4:%+5+5Z?!&Vv@ B&]6<%+5+5hs&6KR@ 4&"<4%+5+5Z?!&VK@ 6&>6%+5+5h&6z5:4%+5Z?^&Vz <6%+5hs&6LR@ 9&?4%+5+5Z?!&VL@ ;&A6%+5+5;&79ش%+5!;F&W9]$%+5s&7LR@  &%+5+5!&W8o a$$%+5s@L_ 0@ ZP w1  ` _?2?9/32/^]]]33/]]922/]9]]]q10!5!!!!#!5^_'ٻ3ߤ!b!F%j @7 H'/'?'#G"O OP ??3329/32/^]33]222]3/+33]10%2>7#".55#535#5773!!!!-*# (04>jM,Ni?  NeQNab5&8R`R &!/ %+5+5&XR@ #&$2 %+5+5&8MR& %+5+5o&XM!@ & %+5+5@&8N}R@ &" %+5+5&XN @  &% %+5+5&8P{R@ &" %+55+55&XP @  &% %+55+55s&8SR@ $&6, %+55+55!&XSP@ '&H/ %+55+55B&8Q  % %+5BJ&XQ !!%+5s&:K;R@ +&3+%+5+5!&ZK@ 0&80.%+5+57s&<KR@  & %+5+5 !&\K@ #&+#%+5+57+&<jR@ & %+55+55Rs&=v9R@ &^ %+5+5R5!&]v@ &` %+5+5R7&=O)R@ & %+5+5R5&]O@ & %+5+5Rs&=LR@ & %+5+5R5!&]L@ & %+5+5%@  GT P??]3/1034>32&&#"-U|N;c&/H((:'kT# 0SAN+J@*0-)G 0@`)) #P P??9/32/^]3/3/32/2]10#"&'532>5#5754>32&&#"3-U{N =9(;&-U|N;c&/H((:'D?kT#  0SAKDkT# 0SA#1>@^))**0118    V#f##Yi2o 7 G W  8HX @ H@@@/@@@@ @1 _# H <55<`1 1#<11<#) ?3/9////]]]]]333+/83^]3/8+]q39=/3]]3]]3/]99]99]3/]q3/33/]10#!#&&54>32.'>73#4&#"326P<3Ӝ3: ;R21T># 0/*?MQ#y?21?31 2?F`#y`F3O87O_}.6;<6.=A@AGB4<<43;<^ 1@T`@#UAA/AAA[K K K 7G1Ub?b>H(@: H( H((VaXPP ` p  PP^@FPFFF'$P+8R+2P/]??3?9/23/]9/3/]2/++]2299//3/]10>73#'##".5467754.#"'6632%2>55#".54>324&#"3260/*?MR#xR%!BN`?EtU07Q4SB@Jdfa0/=hL+ZzI a#=T12R;!!;R20T>#u?12?981?=A@AGB\-A*'Q{TECZ70"(8)Yb&MuOc 9Q3\V3Q88O33O87O45<<55<<Vs&v%R &H%+5+5^D!&vu@ ]&qQW%+5+5}qs&vR@ >&X28%+5+5s/!&vP@ :&L.4 %+5+5h;&69ڴ4:%+5Z;?^&V96<%+5!%@_/]32/9=/3310#&&'#5>73y3l46j3yDC;;CE"a77a"LQQ""QQL!%@  _/]32/9=/3310#.'536673EC;;CDy3j64l3yLQP##PQL"a88a"Zo@ _/]/10!!?o!{/@   _/]]2/]10#".'332>7{,MmGImI'l0C,$A3"=eJ)'If?+2 1(u @ 0  /]]/104632#"&=-'?,-=s<6 +:98m1@@-?O_0  ?O__/]^]/]]10#".54>324&#"3261#=T12R; ;R20T>#u?12?981?3Q88O33O87O45<<55<<B^@   ///103267#"&54>734"-@dd/8-+qhZ*K@48@#/  @H@ H /]23/++3/^]]10".#"#>3232673(OLF -0h!5J.*QLE-.i!5J#+#5>73#%>73#//*?MQ#ek0/*?MQ#dLQQ"QXSLQQ"QXS! '@ H@  _  /]/]32+10>73# !-4lMQP!NWV   )f@$$$$$$@ H$$ @' H H  '`/^]]]329//32+3/+3/+]q10>73#'4632#"&%4632#"& 08= R8)#:&)88(#:&(8KOQ$ MPQ%60 &522560 &522&$T0"""%+]5?5H^3@ 4Ddt H//+]]]104>32#"."./""/."&5!!5&%4""4M'(T.H@%g]]4++5?5P&+{T1H@%eo/]]]4++5?5`',Tf@0 @/%p`P@? @H@ H++]]]]]]]]]]5+]]]]]]5?5&2RTG@0-..p.P. ...$.. %PPg777]]]]44+]]]]]]]5?5T'<TKH H@% ?/]]]]]]]5++]+5?5&vZT5@#5 66666%PPg???]]]]4+]]]5?5w&U@$@$ $:%+]]555$%C@ @H/Oo@HZd_??+]3/+_]]10!#ú%hX@ [`p@' H/_o@ H [ H _??3+/+]3/+]9=/3310%!53!&&h»^*.}}1J[ a(R=+}q+S@4"[g----?-o-[f,`'__ ??9/^]]]]]99//10!!%#"&&54663232>54.#"Q훣LLQ4krrk22jrrl49Ellkk뫉ۙQQۉڗQQRd,. _@/@H@ H@ H  @" /O_ H  ?2?3+/8]]2/839=/33+++]10!#&&'#3.*aKK[`/01R c@> ? O o 0 p O  _ _ _??9/^]/]]3/]]]]q2/99//]]10!!!!!5ZRJsdN }q21@Ze o  Zd_?2?]]]10!#!#!{33N Z@ [ H @&H  / o  [/? _ _??9=/3/]32^]3/+99//+10355!!!Nn}Xgf%77<h!.;@P"gZ;!z([g=/======_=0===5[f<";`./`!!!!??99//3232^]]]]]]qq9/]]]33]2210332###5#".54>3332>54.+"33I~<4Txe//exT5=~…J][.)SW99WT(.[]˴^f={nR00Rn{=fę^S;iXNh<32!!5>54.rj2#Q_b@oP.Q욛Q.PnAb_Q#2j)DudA0o^^oǨ0AduD@w+&,j R@ & %+55+557+&<jR@ & %+55+55q!&~T@ J&>D/%+5+5Z\!&T@ F&m:@-%+5+5!&TD@ %&b %+5+5w!&T"&"%+5+5=&U,B%+555q^=8@8G/(??HV>" P8/,P3?3222?3?]22210%2>754.#"".54>32366733267#".'#5LiAAlQf]n<=qdp1 !  2"%A"&?2$ ;M`/bhek7͗HԌՐISU#R!hy~7]<3 '@0"=.u=Y@49H3/333,G W? ?0?"GT>2P333"'PP??29/^]9?2]99//]9102#"&'#4>"32>54.##532>54.w`vC?xm` Jc@@HP/OH@   @ @   ??33?3/83/8^]39=/333/8]^]]+10%#4>733>73`" %` Ǽ>~4*y?>(]ZPR]Z!Lo-2DQ@.F8.3H$WF@FFFF=H.VE$B 88 BP)P ?3?9/993^]]29/9910.54>32.#"#".54>4.'3268]C%3\}JDq`Q#J IQ[3*<'=A)1l~XC=unjzSQEoYI:bgBoQ-Z\^9Y@7#-W;;;;?;_;;4F##GV:P9y9999 1P( P??9/]]93/]]]29/910#"32>7#".54>75.54>32&&#"3)F`73\QG;mql5&?R,+F3:iV5ZRM(?KGls'Hd=[^3E*")1VuC>X>) +>P2FmJ& "&MM-@'qoj39@.F 555 H))V4.P?22/2/]]23/3105!#>54.'.54667#~L( -TxKG_:"++"0[MY\.\l+/(`/{vͳo0Yl@#.;H*-XRK HHC%"Blfl^1@GU` G TP  ????32]104&#"#33>32\ipQnC ER\04f`J+?*Gq+'[@:%GW)))))@))$G V(P$$$$$$$PP??9/^]]]]2^]]]]q210#"&&5466322>7!"!4p~vt83o~vu:+IhE#!DhJGfD#3  mm))lkJߕߕKE͉wH5@#0   `pGTP??^]3/]1032>7#".5Z?H-*$ )04>jM,Hab  NeJ!.h@*") ) )P4D 0`000.@.)PP??9/3?/83^]]3/]]]99=//339310#'.#"566323267#".'.'#5 +=+"2B#JiO?H.&&7%';/%  53)C1 *Y]669 (@.!^aWMOJ?@& G U `pGT P  ?3??33?22]221032>53#'##"&'#3djoRnC 0gHj#4e`:ST.*&(#U*6J;@ GW ?2?3/82^]]q9=/331033>53# ZrB%\uJ!cg\`qojF~@N2;;*AF8--$ F H?H_HHHHH$VG<28P9*IYi8$O9??9/9]]922]]]/^]9/393/310#"#>54.'&&54>75&&54>7##5!#"3#QW--TxKG_:"++"0[M/Nf7er-Ok>"'!U.>7IsG FrQw1UqAN`;".;H*-XRK HHC%""ǵMcC uHfI1&NuN5Q7q-^RJX@8 G/ G/@HT PP???22+]3]3/]3/9/]10%267#"&5!##57!#P/J0qqm݉'/  PJPNF7-^'9@!%HW))@)) GT(P"P?3??22^]]]10#"&'##4>32"32654&-?uhK6AxiawC;6K{}'ՑL--+.'a.ӑKK31qoo^1Q@6''F   3?3_3333HV2(-Q7 (8H $??9]]2]]]23/10#>54.'.54>32.#"-I~aG_:"++"0[MMb7IjN268<:PrI"`yN/.;H*-XRK HHC%"AvI"  9pqJ)6@!$!HW++++++H V*%PP??2]22210#".54>3!!32>54&'#"->zuk}DM΀%A0!GnMLmG!OK;_Z+oPF˅ڊ>)]l|6Ui;7bNY,cfJ:@$   W+;GPP??2/]]]^]210!32>7#".5!57f->$*($ '.5@uY4׆J;R3 MhsJP=J)@GW`GT P??3]10".5332654.'3\d) BfF PmXVc6G}ut==rwKq%^%5^@<1%G&HW7 777o777@777GV6+P 1P$??32?3^]]]]qq9/]3210.54>746324.#">fmK 8N-%?-3Xt@Zf7VhO6I+1%H{Y3C՗S}7`3hr}Goa0^PzِJ&%be35WA;k%N(@hxg w  v7@H4@ H**?*O**%%%%@)'( P#P???9]]?3/833/]^]3/+]3/8]+39=/]]33]3]10233267#".'#&&#"566-I:1{s&4%.9(C_G3ƠG5$>N>\=J &B3 &FfAj>OY  q'L@/G&GW)0)@))))))!GT( &P???32?3^]]9/3210>54.'3#.53VN_4WmoL6ZxBs 7332>5332>54.'3#"&'#Z`2/ /6K-.D,cQ-K6/ /2`Zk RyQHIRbe4)Hb92΁4ebRIHQy˒RW[[Ww&j@ &2*%+55+55=&j'&޴2%+55+55q-!&RT@ ,&= & %+5+5=!&T@ *&$%+5+5q!&T!@ H&`<<@ H=554&#!#!5!!!2/FD' @3!i{^Z]e55_ 3WEst#o1^Yie1s&av3R@ &> %+5+5}&_@@H#p###@$ H##(([f'_ $_  _?3?39/^]2]3/+]]99//+10"!!3267#"&&546632&&_xM t@vqYN'NUa;LWlON?'AzoɊH#ln,* .h6Rd,@w+&,j R@ & %+55+55H{s-*5%+Z @SHZ#######P#####/[g777@ H65_%%%%+_#`+`???9/^]3/+]99//^]]]^]38]+2210#!!#"&'532>7667!3232654.##<~†!"5OnN#J: 0>* '+m~7ww.[]Xap=qQgo; ?bu69:BqFa< !S@2Z ###### Z d"!_   ` ?3?39/^]322]q9/3210#!!#3!33232654.## <~†Hm~7ww.[]Xap=VhBqFa<s@MZUotZ0@Pgw^?O_ _?3?9/2/]]]]]3/]22/]]]^]10!2#4&#!#!5!!#Z]e5i{^1^Yst#s&vR@ &" %+5+5i&6#R@ '& ,"%+5+5 c@AZHG: Ze o  Zd _/33/?3]]]9/]]+]]]]10!!#!3!3Z]$3K@._[g_ Zd_   _`??9/^]2^]9/]10#!!!3232654.##3<~†~7N.[]ap=7!3!! dq/VMA0e.=FM''UijLȷK(@Nf v 7 G W  G  Ziy8HXWgw  p  @& H  @@ ?33?33933/8333^]]]]]]3/8+]q3339/]]]]33]]]23]]10333###%  !<<<H9h@?'[!0!!0[ g;;p?O_: `!x!!!!/,`5`?3?39/^]]]93/]]]9///910#"&'532654&##532>54.#"'>323[}KW^2C͉nU+`cc.Ϻ\a2%D_:nK\&btGmq9`IxX9  9YwH`t@"-$ 'He=6S:C6}6)6ak@ Z e 0 Zd H @# H  &  H H ) ?22^]++?3]++322]]]2210333#46767##ɮլ1>CC JLJ9ALMFi&6R&" %+5+5 ^@!   /   V  @ H Zd ?2?39923+]]]]]3/8^]338310!##335<Bb@;HZPZe!!!! _` ??3?3/^]9/^]]]38]+210!#!#"&'532>7667!!"5OnN#J: 0>* '+qQgo; ?bu69:/0+}q2n33}&7!o@!`p@ H### @ "@ H _ ?2?39+]]3/833]3/8+]393310#"&'532>733>7T&Spo3Z%%Y45RB8  7]{H5V??  !hs`;q ;@!Z Z   Zd _?3/2?3]]]3/10%3#!3!3H@Zep/ Z` p  @ H _ ??39/3/+]]]]210!##".5332673sb]e5i{ZpV,.1_YGst((3 `@BVZgw Ze   P p  Zd _?2?33^]]q9/]]q]10!!3!3!33q@KV  Z  g w   ZZe PpZd  _?3/22?33^]]q2/9/]]q]10%3#!3!3!33R@3 Z[g/?__  _ `??9/^]3/^]q9/]210#!!5!3232654.##<~†7N-\]ap=BqFa<I@+[Ze Zd_  `?3?39/^]2^]9/]10#!33232654.##33<~†~7N.[]ap=BqFa<VJ3H@.[g_@H@ H Zd_  `??9/^]2++^]]10#!33232654.##3<~†~7N.[]ap=BqFa<;D$Q@/ [  g&&&&%_x__?3?39/^]]]3/]99//210"'6632#".'532!5!.^?NOlRR:aVN'NYq Gs'. *,mm#oz@.^@<[  %[g00?0o00000@H Z d/*_ _   _???9/^]?2+]q9/]310#"&&'!#3!>3232>54.#"O喗PV UۑO1glmf00elmg1ݩldVZk뫉ۙQQۉڗQQ@ @$ HZ e? @ HK@ H@&[@Hf`` ?3?9/3^]+]3/8+]3+]q]]932+]q10#.5463!##"33B}7cK,uJsO)'NwQ\>a]J\>aDBiJ(^^Du!#';I@,-H W===@==7HV<2P(P#  H Q?+?9/32^]]]]21046676673>32#".2>54.#"u1hq}f!2{}u,>dH) >Tk@ce2E|hk{B4&#!32>{k8aG)/eq9Sg;8]C&ml>\?5hu$=[?CtV0J@k\H*E M@$< J/@PGTP??]]]3/]10!#! Z\PJ)7Jy@F /? @9     GF U0/FO P?2?22?3/]]]]2/99//]]8]]310#!#3>7!3!#7NVAcC"$:Q4}_P$gSq^HJ@ F     H@$ H0p @  ?33?33933/8333^]]]q3/8^]++3339/]]]]33231033###3XCDX5--5DR^9_@:F99 4G!W;;;;o; ;) V:8P999999/P&P??9/^]]]92]]]99//9102>54&#"'>32#"&'532>54&##5q=dH'slGK?(MRZ5Vi:3F+,R?&5lqm:LWa37`F)'@-MM&" &JmF2M;) ,BZ>CuV1#"*E3^[;J Q@ HFU   H @ H F T   ?33?332++]2+103##Z  JdjJ;&6%& %+5+5J A@#   GT  ?3?393322]3/8]333103##3aKJ-JJL@1FT GU/ PO???3/^]9/]]]q10!#!#"&'532667!@_V17YE3TiuoJ ^@?FU""@"P""FT!;K[ )4DT& ?333]]?33]]32^]]]210!##.'#3>73  9;6P 1;>JG"C<21;A!;J [@GU 0 @ P  @#H G T P/ ?2?39/^]]]2+]q210!3#!#d!߶J9Jq-^RJ-@GU  ` p GTP?2?]10!#!#!ddJ?^Sqo^F)^JX@<  p _ G`pP??2/]3/]]2/^]]q]]]q10!#!5!^5P J\q#")t@K' F I   #HW+ +++++o+@+++HV*&P'P  ??32?32?^]]]]]qq9/]]+332210#.54>734&'66!qLGwrLGy X}P%9Z U}}ȑV $ V~}ǏU < =g^#J[J D@+GG   ` p GT  P??2?3]]3/^]10#!3!33}JPPJ1@ G U`GTP  ?2?9/3]21032>73##".5P-PLM*,PU^:NzR+Jf,0 /TsDFJ f@CG  t g  GU   o @  / GT P ?33?2^]]]]qq9/_]]_]]10%!3!3!3hJPJw@N Gtg GG @/GT  P??22?33^]]qq]2/]9/_]]_]]10#!3!3!33}JPPP)J\@>GWP`?G     P `  P P P ??9/]/^]3/]q2]]10!2#!!5!4.#!!2>H/eq9!A`@7]D&M|X/1B()BPJ a@@G p G UG TP P ?3?39/]2^]]]q9/^]1032#!3#332>54.#d/eqb춶8]C&!A`@M|X/JJ)B01B(J @@)GW pG T P P ??9/]2^]q10!2#!3!2>54.#d/eq98]C&!A`@M|X/J)B01B(7B^&^@> H"W(((@(/@ HX' P/QQ??9/^]]]+q2]]29/10"&'532>7!5!&&#"'>32BY|66RItR0# ;<85@HN&fMQ%(T\   >۝ԊC^&r@KH O !HW((/(((G((/( G T'$P P,   P???9/^]]]]?2]]]q9/]q310#".'!#3!>3232654&#"@xjbuFﶶ HvcbyD)~~'ՑLB|J9tx>Kӈ#JS@GUo@F@HVPP ?2?9/2+3/83^]293103#.54>3!##33#"#-TA(8dS%AZ6kh 0LlGNyQ*T0F.f_q&Hj@ 1&(<%+55+551S@2 G-U33`33#"GT2!O#P)) ))P????]39/322222]910"&'532>54&#"##5353!!3>320?6#.#ipQnC{ ER\0"Hm 'A3D4f`Չ+?*M{W/ !&v@ &E %+5+5qo^"O@1 $$$@$HV#P/Q Q??9/^]]]2]]29/10".54>32.#"!!3267ReJLfN268<:# Q66{?Ֆۉ>"  %Z?^VuL%&j@  &%+55+55uMJ*k@E F%G $  GW,,/,?,_,,,,+$P% PO%P ???9/]3/]99//]]]]21032#!##"&'532667!4.##32>.eqb@_V17YE3+!A`@8]C&M|X/iuo1B()BuJ!S@0GG W###?##GT"PP?2?39/]322]]]]9/3210!332#!!#4.##32>d϶/eqb1!Aa?7]D&J9=M|X/J1B()B!S@2h G!U##`## G T" OP  ?2??]39/322222]9]10!4&#"##5353!!3>32\ipQnC{ ER\04f`Չ+?*\!&v@ & %+5+5 &\6@ (& -#%+5+5J Y@=GvJZ GU  ` p G T P  ?3??3]9/]]]]]10%!3!#!3d}JD@,Z  Zd_ ??3/]]]3/]]103!#ú-- 0@G GT O??3/]3/]10!#!3 Z?J?s&:CR+&1+%+5+5!&ZCL0&60.%+5+5s&:vR@ 7&d+1%+5+5!&Zv@ <&i06.%+5+5+&:j9R4&+?%+55+55&Zj9&0D.%+55+557s&<CrR & %+5+5 !&\CK#&)#%+5+5Ry@  H?/3/+105!R\ѨRy@  H?/3/+105!R\ѨRy@  H?/3/+105!R\Ѩ1N*@  /]/]/33/310!5!5!5!NRR1P %@__o  ?//]3]10'>73%'.46z|{8=|5P %@_ _o?/]/3]10#>7B'/37y}z8<|5?y 5@ H _oH?/+33/]+10%#>7j'/36z|{8=}5R +@__o ?/3/]3]10#.'74.'5|<8z}y7 b@H_o_o P`p_o ?32/]33/]/3/]3]]]10'>73!'>73'.4'.46z|{8=|56z|{8=|5 b@H_oP`p_o _o ?22/33/]/]3/]3]]]10#>7!#>7B'/3H'/37y}z8<|57y}z8<|5? ~@Q 0@`pP`p_ _o@ H ?22/+33/]/]]3/]3]_]]]10%#>7!#>7j'/3H'/36z|{8=}56z|{8=}5{h |@R o  0 @   w j T E &   6 /?222222/]]233]]]]]]]3/33]]]10%#53%h77777K_{}@q @P  zdU6F  P   /222222?222222/^]q333/222233]]]]]]]q23/332]]]q10%%#553%%1L78L//L87L/y"xmF@$/_o_ o  @  H/]]/+]]]]104>32#".$?V21V@%%@V12V?$Gd??dGFd??d'9@v;$;;;;;;;d;t;K;4; ;fv($2222222r2f2K202 222 Td -7#/3322/^]]]/^]]_]]]]]]]]]]q9/]]]]]]]]]]q1074>32#".%4>32#".%4>32#"&"./""/."%"./""/."%#./""/6Io&5!!5&%5""5%&5!!5&%5""5%&5!!5&%5"Bf ';?I]@\@TJ2#(>0<@<<0(@(><((<>EJ_?_O_____  0GYCO?>%7!-?????/]]9///]]]1032#"#".54>3232#"#".54>32#32#"#".54>32GPPG$JsOIpL&#IqNKqM'GPPG#JsOJpK&#IqNKqL'՞,GQQG#JsOJpK&#IqNJrL'JHlv??vllu>>uJIHlv??vllu>>uJIHlv??vllu>>uJ Rs<@ H?//9=/33/]]+210R5uu)NNRs?@( ??//9=/33/]3]/]]10'7uu5eN\\Nbb' /11]]h@ ??/82/8310#h՝+JjA@` @ H  ??32/+2]]]104&#"#33632??-A*i @QD4WAXeP`v@H   0Z `_?o@H _ ??99//+^]32/^]3/]322]3/]99//10!!##53!!!!$ܳ'D#0@ o+'###@> H22)%@ H%u&&)u* *o&***?*O***&*&*ts??299//]]]3232/+33]3/+399//33322102&&#"!!!!!!5>55#535#5354>jBB8K0RY@+ )DaCW_2*5y@1on$@H@, H777@ H+#n$|6u+s"""$5s%$u ???99//322+]3/++]99//32210%2>7#".5#57733####3232>54&##;$#P?5X@$Ak44|̘/{:][.9 "FjHRMӉVLN[M9mg GqQ?J9(6@K H;;,## o."c(((:#`$$,`--_$$$$$--/-_---$-$-7t3_?3?399//]]32323/]332233]3/+39910"!!!!3267#".'#53&45465#53>32&&Ev^CA"K;;[sXXraOP3w'4c[  ) Iy.}ʐN+1++H@ 9F@JpFFFFFF1"J@111 1111B,=6' ????/^]q]q9////8]]833310##".54>3232>54.#"".54>32&&#"3267 ՝+n-PpD?nQ/,PqD>nR/3&<+*<%%<*+<&EyZ45\}H3d !U"g_3Z##cJSW--WSSW--WS3V>##>V34U=!!=U&RZ_R&k tvkw{-:U@2.p#@H##<5(n 06v((0vu  ?3/?9/3322/]329/+10%2>73#".5556674>324#">o 9.d&FiF:jQ0.a14_-@hK6W=!5\|G$7cf ) 6N3w2R;SZ0%Tc y;lS1*OoEch&0Q;!!2E*j!Nby+7;@rZ99,"0@"" 2::/:::g=o===@=  Z d3232654&#"5!v)MmD?kM,)MmD>kN,BHQQGGQQHcMLA9LLJ CC> SY//YSSX..XSqssqrmm%P @] o0@P ğ""/"?""@H"@H H @ H?333/333+323+3/]++]]2229///]]]]]]2210##5!###33#4>7#h @{ell%"I'/ ## NvfH"/;@!#J1/JN+/;///)N N??9/]9//]2210".54>32!32>7.#"yƆE,Lfv?qŽQ@MX.Jt^M"H$Sn;M]53WI<#^nc~\<Oу," 326454&#"5>322>7.#"; AemjS"2Qsb[-DCACKO%}c*5^QC5% );I*>fO:%-FjԼRBnL32>32267&&#""32>54.1+MmB]AFNS+AnO-+NoCU>DOW0BmM+{?l41kE(@,,A|?k73lD'@-.@?rW4is0O8,RuHAsV1kp0N8-RuWa^Z3D&$B2jWa]\3D'&C1#7@#%%   ??/]3]]2]]]102&&#"#"&'532>54>"K=$3B'2Y|J$K>#3E*/Wy 'AT-^V) %@T0'^V(f{%#GK@.AII.  @)1C   @H ;.C?33/+]323/]3]210.#"563232>7#"..#"563232>7#".%7-)<;8d27C/%7/(<;8c27C/%7-)<;8d27C/%7/(<;8a27C !,l  !,l   ,m  -l f@   @O    @{hB9  / o/]]33/^]]q3223233/]]]]]]]]3]299//883}310#5!!5!3!!!'^>yIi{;9f R@2 @ @o0 Pp?/^]]]q33/]]29=/33/]3]210%5 5!d!dfqf R@2  @ @o0 Pp?/^]]]q33/]]39=/33/]3]21055!fdBjfXm? ]@6   @ P  /  /?99=//33/]]]]99333310#3 ?L 1&IL$@P/@//]]]]]]]55&IO"@P!@!!]]]]]]]5?@) 0p 0  @ H/3/+]3/^]3/]]]q10#".'332>71]dgX)2L5,I6"NvQ)'PwP9I)+H5dJ#@ G  T P??2/]10"&'532>53B0?6#.#"Hm 'A3M{W/y @ ? ?/3]10>73# ).`MQP!NVV o;u @  //2/310>73#o ,6bVLTS!MWV!}! @  ?/2/310#5>73 -7bLTS!NVV!%9 *@ O@ H ??/+]1032654&#"#".54>32GQNNNNQG#JsOJpK&#IqNlu>>ullt< J F@*  _@ H   /  ??9/^]32/+]9/33210##5!533!5467}y}  oC*c1 %*(;7f$J@!"" &O&&&@ H @H !??9//++]39/333102#"&'532654&#"'!!66B=kO-?y,<=;U__Y %'%C!9m#DeA LXMU+{%9-8@  !/O///@ H) $??39/3/2+]910#".54>7366322654&#"*NoD@nR/E~n-O`@ cJ6Z@$DTMG'?--=fAoP--XSnm'm@O`;+1%Hi\VRZ)7,I5/Jd8@O@ H??/]+]9/810!5!@b5J{d19q%4Dw@"8!B =2!!,FOFFF@ H&@'H =2K2[2k22225====)5??9]9]99/++]99//9102#"&54>7.54>32654.''">54&R4^F*'4">0*Mi?*8.#+H_bJHJK'7!AB8? -, A7T8%>2(-7E*9]B$s*E8++5>%8S7h;FF;0' "M76*$ #,67!9{%5;@"1 7O777@ H),&! ??9/3/+]9210#"&'532>7##".54>32%"32>54.{!Z@6Sl@ $0=&<`D$)KlCArT0DVJL&?-*>B`[} 5Wr=$%GfABoQ-/a^XLX)3$H;$T #'+/37;?CGS_ox@Y4, 8WQzpotgdd~kpQQQQokkoQ]O_D($ ]PK`KKKA=1 *BF>&2oygppz`zoTHHpooaoo@% Hoo`ZNNx`/`?`o```, 95!/^]3333/33339/]333/33/+]]3/339/333/3/3/3/3/33333/]3/33333/]39///]333/33333/3/3/310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3"&54672'2654&#"32##32654&##32654&#532653#"T/0momImmmm0oowoooommH??HEBBmp--83m^{B.$)0;J1%&4 %1}h_=0oo/mmmmmmoo;mmJoooo/yhhg^^ff^^gCS1D D:QYb" "+% *f$2r^dT#/P@(##$ *0*@***O$$$ ##0---''/3/33/3/]3//3/3/33/]3/]3/310 54676654.#"663232654&#"TV!LcM1[Q+ZWR"RD~8?>'REJGFDGGDFGVW/,>:LYEkJ'#".:/1DA5yP;>II>@IIU!&7LP + Ou@GM C>;GCC;GOQQ?Q3G "P)>OONN))0)@)P))N)N8PH8P??99//^]32/]]]3/]39/999910.#"#".54>54&#"'6632324&'.54>323j 9M[0LV.j<@ăoV" 6%%d62E, 0N:C'QYhuKlt=XH8iR3:s7_}E(]YK-! 2D(#V]a-*J620>KxQCpR.TߋH"@$$$@ H"!!@!!!!@D!!   H } k Z O + ;  ZwO! ???99/^]]]2/]]]]]]+qq93/8]]]]]3+]10>7>32&&#"#3=?</5@)#+  # (2662BK,(=' *#Un</`J<@Gg//G,,,,,,7H>>T>d>>>>@>0>>"H0@P@H--'P 2'P?22?229/9/+^]]]_]]q3/]9/_]]]]]910"&54>7!57!##"&'#32>55332>54.') +&ĵj (6K-.D,bR-K6$9}~{8JP8{~}9W[[W6vyz:bN!)Hb9!Nb:zyv6/u&0vyT@ '&K %+5+5!&Pv@ :&z-3+%+5+5&$[ %+55^^&D[ =3 "%+55&2R\@?-5 H!@55 %P@]]]]]]55++55?55s7:@(?O_0  0@P/^]/]]10#".54>324&#"3267#=T12R; ;R20T>#t@12?981@3Q88O33O87O45<<55<<h B@ 0@@ H+      ?O_/]99///]]2/+3]910>73#%467#"& 08>Rxz<9%2/:EKOQ$ MPQ%xNsL.%'F&I'ILD8@&9p9@9/999KpK@KK]]]]]]5]]]]]55&I'IOD6@%9p9@9/999=p=@==]]]]]]5]]]]]5}% 4K@, [+[ g666p6/6_6![f50_ &_??99]]]]3/9910#"&&546632>532>54.#" (?Z?+-Q훣LL爛Q)14krrk22jrrl432>5332654&#"-C}ogGC|od?.4-?V9 $'ՑLLՉӑKGD 7K]3Jx^CA^N$Q@2[#Z&p&&&_&&&Zd% $_??33/3]]]3/]]q33/10>53#".5332>74> 2U[BɈąDYR(2Nd8GiJrĐRMzH6bQy'T@3J!!G'')))))o)) )0)G T(&& P??3?33/3^]]]q3/323/10!'##".5332>53>53u ER\0[\/joQnC4> 2U[+?).bi=4e`:{2Ne8GmH! @ _/]/10#.'53y#QM@+.0SXQ#PQLH! @  _  /]/10>73#H0.+?MR"yLQP#QXSF8@#/  @H@ H /]23/++3/^]]10".#"#>3232673B(OLG-0h!5J.)QME-.h!5I#+#5>54.#"56632y-<" q%<*$242)':)o! l[1}  //104632#"&1>-+?'-><77<+7s&(CR &´ %+5+5s&CBR& %+5+5q!&HC(&.(%+5+5;!&C & %+5+5}^Eq@H9Z%666666A[ gGGGG GPGpGGG.[fF773)_&&" H<3_?322+?33329/^]q9/]]]32910"'6632#"&'##"&&546632&&#"3267332>54.h<^*L;Qw}@?ƈoIKnƁ?@}wQ;L)_733>53#&&5w& w ZrA%\uw  JVW3:AE  !cg\gT 5 !@ Z  @ 'H   H @@ H [g##0#P#`####P#!_ _O` ??99//^]]]]32/]]]q9/++]_q+39/221032#!!5!3!!32654.## ~7<~†<y.[]NBqWap=`@Fa<m'!@ G  @G H GW###p####P P@ H0P ?/]]99//^]q+]32/]q99//+]32210!!2#!#5353!!2>54.#/eq9+7^C&!A`@לM|X/)B01B(-X@/[""**/!Zd.+_'_"!!! _?3??9/^]3322?323}/9///3210"!!3267#"&&'!#3!>32&&m_xM t@uqYN'NVa:RVclON?'AzoɊH#dV\,* .q^)d@9 HO   '+ GT*Q P   /  #Q&?2??9/^]]3322?323}/399//]3210".'!#3!>32.#"!!3267TaO U^N268<;# R65|:{J9r4"  %/ @Iw   p@H@@ _ H  ??339/9+2/83^]]]+]3/8]q39/3]9=/339999]]]]q]q10!###3##'!'.'9;z#5 VJ0LCA%%ACI- ZJ @89Yy+   @ H   H   H@ HP @ P   ?33?9/92/83]]]]3/++389/39=/]33+]+]9999^]]]]]]]10]]#####3.'``q#% %#JJyU`^""_`T) @w @]+{/p"0"P"""" Z d!_  H  ?3?3339/9+3222]3/]839=///^]]]]]83333999999]]10!###!#3!3##'!'.'祁;y#5VVhJ0LCA%%ACI-J@1w  )Iiy     G@ H H @/ ?   T H@, H!!!`!p!!O!! P ?333?39/9322^]]]]]3/++8399//]8339=/]33+]+]2999999]]]]]]10#####!#3!3.'T``鶶Tp#% %#JJ9yT`_""_`Tj#&@"Y% H&@, H% $$Z&     @; H(((((@(P(p((?(#a$ O &_?22?229/^]q332/3^]]]]q3/+]]q39///]39=/333++]]10]3>75!#.####"!{6T|_IaV7z{&6O==O6&{JoUb:狋8bV;H_9H9_H?Z J#&@ v% H&@ H # $$F% & @H   H@? H((((`(((O(# &PR$&$6$$$ O_?22?9/^]33]222/3]]]q3/++q39///+]339=/33]]++]103>75!#.####"! {,BgSShC,{{$0D55F0%z T=eM1 dii1Lg=P6H+ +H6-(+@Y ))Z * H+@3 H*+   (p@4 H----@-/- Zd, +_ !a)    ?2?39/^]3322222]]]]3/+]q39/]]39///^]33++9=/33]]10!667!#3!5!#.####"!3{){JaU7{{&6O=32.#"#".54>32>54.##532>54.#"'667.'53>32&&#"GZ4$=/AaSQ2+B1# 6M51GJ\FhO 1flYQ&9gX\a2%D_:nK\;g762y3a6;CK*/ "00.R~U+3[}KW^2C )$  )H`76S:C6}0KA?:"a8$F8# w)8! ?\vEJwX9  9YwH`t@sRRp@q@ Hhhbl lXFl__R3l3lR_CMGWrrrrr0rr&FC@ HCQPRRRRRbHhR@H@ H^l[P b4+P>HP!??3?33/++39/^]]9=//+^]]]]9=/////]933/33/10+2&&#"#"32>32.#"#".54>32>54&##532>54&#"'667.'53>/ !..-7Y?#3F+,R?&5lqKY/":,2c]S!BG "(, Udo9Xp@"Vn7dL,a=dH'slGK?9kA10,y3b6:CKR w&31EX52M;) ,B[=DtV1&#   .GV)/YF*+E3^['@-MM&"";72"a8$F8#huq}q)O@2%[g++++?+o+$[ f*`O$$$$$$__??9/^]q2]]]]210#"&&5466322>7!"!.qQ훣LL駱Qjk9:kmil=d;iݪllkkGzz†GCuuCq-^!O@3HW#@####H V"P/PP??9/^]]]2]]210#".54>32267!"!&&-C}ogGC|ogG# > 'ՑLLՉӑKKԲDZ@)   /O_ @ `  ?33?3?/83^]]q3/]9=/]q3310"#3667>32&&$5*$'.*=LgK#D5-%HgBa[ONaqb+  Rm@ H H H@ H!!!!P!!/!O!@ O?2??33/83^]]]3/+]9=/+33++1033>7>32&&#"#  V5CX<" &/:J!jna_lg!@^uBPXs&vR&1%+55+55!&vP &4 %+55+55} Z'J@ [8J//7)(@#((@@@@@/@?@@@ 67777@4 H77LLLL/LOLLLLL[ fK8/JJ(DP=6(#__???3?933^]q3/+]899//]]]q3/89=/3310#"&&54663232>54.#"%33>73#"&'532>77LݑGGᙐݕL#/agg`..`gha/C  ǼN@VtP4L@#0F4%9ݪllkk뫉ۙQQۉڗQQ(XXS"Va^!c'QZ1 ,@)qN^&R\o / %+55}1?R@30[gAAAA0AA [f@ 85;_(%_ ?332?3322^]]]]_]]10#"&'.54>766326632>54.'#"&'I|J7;H LJEEǁ H;7J{ʼnJ-WSC51ERW--WRF15CSW-ݕ}?66?}{?13=}tb0&(.btsb2))0aq7T,HW99H9@$ H HV8 2/5P)&#P ?3322?3322^]]]++10#"&'.54>7663266326654&'#"&'9k_ <8:;]n=9lb;:7= [m732>54.#".#"#54632356654.54632?<^*L;Qw}@?ƈgEGgƁ?@}wQ;L)_32.#"326732654&#"'>32#".#"#5463235654.54632Qm++jP_J?mT@<4?)++piIs)19B)ip++)@5   4    >۝Չ?$+$.4#le$*$MsL)3  &&F}^ S@b#GZ3DDDDDD+O[ +gUUUU UPUpUUU<[+fTEEA7_04  ?40J# H#A_&?32+2?33/]339/3329/^]q99//9/]]]32910#'##'##'5"'6632#"&'##"&&546632&&#"3267332>54.R"21#12"P<^*L;Qw}@?ƈoIKnƁ?@}wQ;L)_733>53#&&5R"21#11#P& w ZrA%\uw  gggg\JVW3:AE  !cg\gT 5 }#T@Z@H ` p  @ H %%[f$!_ _??3?3]3/+]]9/+^]10"32>7##"&&546632&&k{C;vv-(%LWlON?'QډۖN bln,* .qo^>@%G@H !_!!!HV QQ ?3??3]]9/+10.54>32.#"3267##JLfN268<;>h#?Ֆۉ>"  Zhy %@g               O@   `p@M H 0       @ H/3/+39////]]]3/q3/q3/93/9]3/2/+]39////]]333310^]]]]%'%7%7%LG㴁FGJ{J;{Z}9IĤ{B  H@ H   /2/2/3/^]33/3++10#"&54>3!6632#,08( %-08*+9,338% -1269)B@ @ H @ /]3/3/+^]3/]102>32#54&#"##5Nwp:m|:.*eyUf$+$el#4.$+$5 @ ?O?O_/]/]29104632&&D;/2%97'>737.'.'57>7'>7'.'7.'7  #a ;  #a #&UWW'*][W%h'UWV'*][W%"PTV(E%ONI#OTV(E%ONI+)'$C342j('%C442#'UWV'*][W%&UWW'*][W%  #a ;  #a )'%D342)'%D342"PTU(F%ONI#OTU(F%ONIh1@H0&&&& &0&&&& Ze333 303 Z d2&@ H,!H@& H& _H H ) ?33^]++?33]++??3/+]322]]]22339/39/]]]q3/10!46767##3333##".'332>7d1׮Ïڦ1]dgX*2L5+I7"8ALMF>CDJLhMwP)'OwP9H),G5%@:4    H F U'''''''' 'P'`'p'@% HFT&@ H   `?22?33??3/+]322+]_]]22+339/39/^]3/10!#333##".'332>7 {zG1]dgX*2L5+I7"jJdP}NvQ)'PwP9I),H5/3!R@,Z  [g#!_ _` ??99//^]]32/9/39/221032#!#5353!!32654.##~7<~†;Š.[]NBqWap=`@Fa<!a@;GW#o####GT"PP@$HP??99//^]q+32322]9/10!!!2#!#5353!2>54.#d/eq97^C&!A`@!M|X/)B01B(3"@O [(8Hg$$@$$ Z d#" `0 @  "` ??9/]999/]39/]9932^]]]]99//39929910'###!23267'76654&##3/K6nj]|j~32'"3267'7654&;`M< ;M`<^n-HԌDN .^^)ek7Lj/ @/@ H?Z  @& H _ / ? O  ?  _ ??9/^]q32/+]^]32]3/+]q9/]10!!!##53P} J n@ 0p GH@! H O O_@H P??9/^]+]32/++]32]3/]9/103#53!!!!\Z&f@A "[(((((@( Zd' _` / ?  ?   _??9/^]q?32/]]]9/10^]"#!!6632#".'53 4.%<G-WRv1ND=?HwC{ \O  y~@ J#\@9 "")" H%%%_%%%GT$ Q PQ???9/^]329/]q9/10]"&'532654&#"#!!6632)Kj.+jBvu>\Z;fL@o % 5J>۝Ո@@W@H H @H H  Z Wgw@ HZt@' H 0`p @  _?22?3393?3/8333^]]_]]]q3/+]q33/+8329/_]]]]3323^]++]++10!##3333#!  ð<<<J@l'7)9& 6 ' 7 '7'7'7@H H @H H  F @ HFH@ H@ H0  @  `?22?3393?3/8333^]]_]+]]qq3/++33/+8329/_]]]]]3323]++]++10]]]]]]]!##3333#CDXXOŮ--5e}HB&HHB %+55DBR^&HB)!%+55@Y@H H*@H H*0@Z Z d @ H H   _ ??3/?399++2]3/^]323/^]]8393]+]+]+]+10%3###3735l=yr%3+J@j@H H*@H H*FvmU+;K  G T  P ?2??3933223/]33^]]]]]]32]+]+]+]+1033###3aKfKJ_}-J@]HP`"2B@ H Z4TW  d/   0@   ?2?399//]99]]]3/^]83339/]]3]22]210+]]+!###3733wwAdbce#J@ H H@W H &/? GT  0@`  ?3?399//933^]q3/]833329/]]]3]22+10++'#3733##mm}P}{J{F I'/@@f@P Z d/_  H@ H ?2?399++9/32]]]3/^]833/833229/]9]310!###53533#73=yr`{3@ GH@5 HT  5  O ???9/32399]3/^]]833329/++q29310353!!373###{%om^ui%RZDW@3 Z 0@   _?2?399]3/^]8333/9/]29310!##!5!73D=yr%3)J g@@ G ` @  P ?3?393]]3/]833]3/9/^]q223103##!5!`LJ-c@?Zt Z @ Z d_    _??3?39/^]2]_]]23/]q10%3##!#3!3gVhJy@RG  G  $0@PGT P/  P?2?39/^]]]?2]_]]q3/]3/]210!!#3!33#߶!J9P}H ]@; Z@    0@`Zd _ _?2?9/^]3/2^]q]q9/210!#!#3!!!gVhJ h@!G G T  @ HP/ ?2?39/^]]]+]q3/9/2210!#!#3!!!;߶!J9ǚ'_@:  Z!!%[)o)$Z%d(`/??%#_&!%_ ?3?3?9/^]q]9/29/10^]6632#".'53 4.#"#!#!I.VRv1NC=?~IwC|kK3{\O  y~@  uJ$w@N ##Gw H &&&O&_&&&&GT% Q PQ?3?3?9/^]]]q9/]]]q29/10]"&'532654&#"#!#!632Kj.+jBvu>d:?fL@o % 5PJ' >۝Ո@}>Rz@PN2I[  5 5 ?[gTTTOT_TTTT+[fSD`N:: :`:p::::&_!20__?3/29?39/]99^]99//]9103267#"'#"&&546632.#"327&54>324.#">o'AU-6Q%FO&2|32&&#"3267&&54>323267 *9H8=>ALE9(bGuz?73Fq  ǼqN.d^OO^d.17@@ HZ w @ H  @8O_ _ ?3?9/9323/^]]]]]]]q83/]]]839///+]]329=/+]103!!#!5!53TB'ٻ&B=X3J@GH@  HPO0@O??33^]2?3/83]]]]]]3/++839/]]329=/10#!5!33>73!Fs  Ǽq cJ.d^OO^d.1@GH           0 HZ  @ H 0 H @@ H @('   _??3?399]]/8333]+]_]]3/^]33+]q/+]q832+]q9=/3_]]10%3###33wZLN[{/L#J9@%Y V  H= )    F @qHjzEUeuJZ@H;K' 4D(; H@I H`euJZ ;K( P  ?3??399/]]]]]]]]]]3/+3+]99//]]]]]]+]]]]]]]]]]]]+9=/32]]]+]10]]333###e3fg}L`@ ZZ@DH/O @P`Z   / _ o  0 @ P  @ H  _ _ ??2?32/+]3/^]2/]^]]]3/+]q3/10%!33#!!5!!q^_)mJ GGH@3 H/O` G`p@H P  P??2?32/+]3/]2/]^]3/++3/10#!!5!!!33m5}P1c@  ZZ@,H/O_Zd_ _???39/3^]3/+3/]210]%###".5332>731:ica1]e5i{-Z^f8V" 1^YGst Ja@  + GG @%H_G TP P??39/3?]3/+]3/210^]!#".5332>733#H,PU^:NzR+-PLM*0 /TsDf+P}p@. Zep/ Z`p@ H _??39/3/22//+]]]]29/^]3210]#".533336673##P!]e5i{ 8vDBw91^YGstX "JV% Jk@B P   $  ZU` ZT 0 @    _??39/3/22/]]29/^]]3210^]10#".533336673##/NzR+}/[32Y2}j/TsDf-0##3P@6 Z e/_o Zd_ ??39/]32]]10^]36632#4&#"#Ǻsb]e5h{-Z_e8--1^Y/su 9J?@" HGU` G TP  ?2?9/32]]10+!4#"#3>32\-PLM*,PU^:NzR+,+J/ /SsDZ=+6d@>2[ g888) """1 [p7 `1O++++%+%+%_,_??399//^]]q32]223/]]210>32!3267#".'"&546733"!4.f [ݍֈ>%?tp[*[i|L\  g[nA%V9[kEz†G,  auz*B>#aA~xuC3^,5@+1H'W7777770H @P@(H6P0+ +    "-P"Q'?2?99//^]]]]32+]q22/]2]]]210".'&&5467333>32!32>7"!4.5kK 11LtZcn;L3WQL'(MQW`r 9YFǁor'?:"/1lu>Gnq ۜDqP,=.9r@FZ"5[ g;;;, %%%4 [p"""":" `4O..?..(.(.(`/_???3399//^]]q32]223/]]29/10>32!3267#.'"&546733"!4.f [ݍֈ>%?tp[%NXd:ĊM g[nA%V9[kEz†G,  smuz*B>#aA~xuC3^-6@0G 2H-$W8888881%H   @ P  @+H 7 %P1++(.P(Q-??33?99//^]]]]32+]q22/]2]]]29/10%#.'&&5467333>32!32>7"!4.BLTc8 11LtZcn;L3WQL'r 9Y5 #uToor'?:"/1lu>Gnq DqP,Rd,i&6R@ & %+5+5&6@ &%+5+5%g@?![  @ H ''@' Zd&_`/ _  @H  ??39/+]33?323]9/+839/10"#3332#".'53 4.FA\(5VRv1NC=?HwC|o<XU엪O  yv: J#r@G "#### H%%_%o%%%%%%!!GT$!Q "Q ?3??39/^]3323]q9/9/q8310]#"&'532654.#"#33+^xE?oWLi/,iCvu#JrN U)j@ДՈ@%j`- PJ V#s@FHZP!""Z# %%%%%%%!_` _????/^]]229/39/^]]]38]+210!!#"&'532>7667!3# !!5OnN#J: 0>* '+Ï٥qQgo; ?bu69:fJX@8F`p4 GU  P OP????/^]]229/39/]]]10!!#"&'532667!3#@_V17YE3Tz{iuoP}K@+ Ze Zd_ _?2??39/^]2]]]29/10".'53265!#3!31ND=?HgR  VhݪO ;Ji@ GU 0@P@(HGTP/Q?2??39/^]]]2+]q29/10"&'532>5!#3!3Li/,iB;[=߶!?o %-aiJ9Ո@R@2 Z @ Z d_    _??3?39/^]2]2229/310%3##!#3!3٦gVhJv@O G  $0@PGT P/  P?2?39/^]]]?2]_]]q]2229/310!!#3!33#߶!{zJ9P}Y@!ZZep/Z` p  @ H _ _ ?3??9/3/+]]]]22/10%#".5332>73##:ica1]e5i{-Z^f8" 1^YGst J'J@@%GGU` G TP P ?3?9/3?]22/10#".5332>73##3H,PU^:NzR+-PLM*0 /TsDf+69@, H H   H9Z! !!! @ H&  Z d _ @ H  H ?33+3??3+22]+^]]2222]+9/3993+3+10]]!##!3!3##4>767##EÐ٦AJI?9Xw=ABGIJ$@Z "##F$!  &&&0&@&P&&&FT%";K[ + P4DT%?2]]?33/]]3?2^]]]]]2229/393310!#.'#3>733#o  ٷ{z9;6P 1;>JG"C<21;A!P}Rd,i&$6!R@ &%+5+5^&D6@ 8&%=3 "%+5+5+&$j!R@ &)%+55+55^&Dj@ <&/3G "%+55+55V^D^i&(6R@ & %+5+5q&H6@ -&2(%+5+5q)I@/$[g++++++@ H%[f*%_/_ __ ?3?9/]2+]210"5>32#"&&55!.2>7!y[*Zi}LTQ薙ׇ>@sC[mB&U),  l髮hkFzGfA~xuDf^'C@)"HW))?)#HV(#PP Q?3?9/]2]]2102#".55!&&#"5>267!nIBxfcn;3WPL'(LQW`s 9X^J҇֕NGnq  %DqP,q+&jFR3&Ӵ*>%+55+55f&j1&(<%+55+55+&jR@ && %+55+55&j&&%+55+55H+&jRC&:N %+55+55DR&jUC&ٴ:N)!%+55+55H r@-[[ @H  [g""p ? O _  _@ Hx` ?3?9/^]]]33+2/]]]9///+10#"&'532654&##5!5!sGC͉nU+`cc.Ϻ{=;5df`t@"-$ ѦuJ$d@>J J$$@H!$$! GW&o&&0&& @ H $!P" H"P ?3?9/+332/+^]]]9///+10#"&'532>54.##5!5!_}IGss:N[f6GwV07cTmAulnI$".W}NV}R'}횅&MR& %+5+5;o&M9& %+5+5+&jR!&, %+55+55;&j%& %+55+55}q+&2jR@ 1&(< %+55+55q-&Rj)& 4 %+55+55}q~q-^}q+&~jR@ 3&*> %+55+55q-&j@ +&"6 %+55+55;D+&jR.&%9 %+55+557B&j80&ʴ';"%+55+55&M'R$&"#%+5+5 o&\M@ %&#$%+5+5+&jR+&"6%+55+55 &\j@ ,&#7%+55+55s&SfR@ .&Q"6%+55+55 !&\S@ /&Z#7%+55+55+&jTR@ &* %+55+55&j@ "&- %+55+55 d@ @5H / O o  @HZZd _ _???2/^]]]+]3/+_]]q10!3##ð J N@2 0  p  GGT P P???2/^]]]]]]3/]103!!3#\ZJ}+&jR@ '&2%+55+55P&j@ '&2 %+55+55/m& <5m J&  <5m&;#H%++5#mJ&[P%+]5`@'FVI Y 69  0? @ p@' H/_ ('   ?3?39/99]]32^]3/+]q833/83/833/8399//]]9=/33]]]]10##!5!33!LNL{^/#J@ 6   9k{W:JdtX5E     6  @H @Hk { W : J  0 9k{X5E O +[k ?3?39/^]]3322/]]]]]]q]]q/]]]++]]]q9=///]]]]]]]]]q33333]]]q99//]10!33!!##!hJJ{f1L@@)Z e[f_?Oo_ ??9/]]]2104>333!".#"337Ϙ~<][.WqBhJ=p_33332>5#"32>5;-[^r+?Rg?Aזzga,H3][-~C[8PQf:aM&?.gr<hpi9P2CkM%>M)qZ2C\@9,8GP...GUEoEEEEE@EE/EAHVD3>P% ,3P ?332??39/^]]]9/]2910%2653#".'##".54>323&'&&53!2>754.#"ob/\[AgQ=D[yQ]n<jN-.^_)ek7ND?@V [ [+%  ++ 3Z6eAAAAAAA A0Ap?O@%`444x0_;`?3?9/^]]]]3/]93/]]]]9///9104.##532>54.#"'>3232653#".)5e]\a2%D_:nK\&btGmq93[}KW^22O7ob8fYWl<MmF!'Hd>6S:C6}6)6aOJwX9  9YwHJhAw-ib./dP^;@#G F16111'G U====0='@)H_'''<6P  &#P,P??39/^]]]3/93/]+]]]q9///91032653#".54&##532>54&#"'>32R1P;i`/_`\d5a=dH'slGK?(MRZ5Vi:3F+2S<"L'I8!5ib.+T{O^['@-MM&" &JmF2M;) )>RN1@T[' " " Zp....Z//333333 303p?O20'`  x   `._??39/^]]]]9?3/]]]]]q3/2/]99//910!#4.##532>54.#"'>323#8g]\a2%D_:nK\&btGmq93[}KW^2CdA 'Hd>6S:C6}6)6aOJwX9  9YwHP^-@ F"G))-G *0*@*`**@H**////@-H_.+"PO_ P)P??39/^]]]]9?3/]+]3/+]2/99//910!4&##532>54&#"'>323#a=dH'slGK?(MRZ5Vi:3F+,R?&+^e'@-MM&" &JmF2M;) ,B[=}1@eZHH1ZP$Z'e3O3o33333 33%%_!`, ?32?9//]]]99//^]]]]3+]+]q2]10!#"&'532>7667!32653#".5 !!5OnN#J: 0>* '+cnob8fYWh:qQgo; ?bu69:сww-ib.,`lJ(\@9F(G$ GU****  P O#?32?9//]99//]]]]]]10!#"&'532667!32653#".5@_V17YE3T1P;i`/_`\f6iuo=AbA 5ib..bi-\@7ZZ e Zd__???39/^]3/2]]]]9/]21032653#".5!#3!3bonc8fYXh:fww-ib.,`l-VhJl@GG GU!! !@!P!!!!!!GT P/ P???39/^]]]3/2]]q9/210!332653#".55!#d!1P;j_/_`[f6߶J9=AbA 5ib..binJ}R*G@)** )[@ Hg,,p,[ f+*_$_$_??39/]]2/+9/10!#"&&546$32.#"32>5!;BޜV_ oXH$SX].zB6pul])V[ii,*Qډ؜VBwdq{^(@Z(( 'G@ HW**$****@*P****H V)(P@H +"P"P??39/^]]]]+]_]]q2/+9/10!#".54>32&&#"32>5!{9xˈDLۏoM><[ae4)V^QtJ#JDȉGOӄӔP%'):mb]p>-PpDe@/Z eOZ@Wgw@ H__??9/2/+]3/^]]2/]]]]1032653#".5!5!!qbonb8fYXi:^_ww-ib.,`l)oJm@J GU0@`G`p PP?2?9//]3/]]2/^]]q]]]q105!!32653#".5)51P;i_/_`\f6AbA 5ib..bi5o 9][4@.H/ ';; [/f:4`x#`&*` ?3?39/^]]]9]2/99//+9104>32&&#"33#"3267#".54>75.;smH{iZ&\Kg9_D&,WWÿ]e5lSSÃB1_WJxT-`Oa6)6}6C:S6>dH'!CeC1+&%7#".54>75.54>32&&#"3)F`73aWML%+5+5q-&RR(&)7 %+5+5}%&_g\8'5;%+5+5q&`g0'-3 %+5+5&8g/@ ' %+5+5J&Xg'! %+5+5&8fR@ &- %+5+5&Xf@ !&0 %+5+5Ns&avR1&%+%+5+5y!&bv`(. !%+55Ns&aC=R%&+%%+5+5y!&bC.( !%+55N&afRY:%%+55y&bfV=( !%+55N5&aR`R-&G.<%+5+5y&bRN1? !%+55N&ag/('H%+%+5+5y&bg+'E(. !%+5+57&<g ' %+5+5 J&\g2#)%+557&<fqR@ & %+5+5 &\fJ@ )&8#%+5+575&<RR@ & %+5+5 &\R@ +&,:%+5+5q&BҴ:9 #%+5! -@@P?O_/]32/]]9/10#.'53#.'53d#QM?*/0d#RM?*/0SXQ#PQLSXQ#PQLjR@1 @/?O/_  _/]3]29//]99=//]]3310#&&'#56673'>73#d3l46j3e3u00t3N!-j7e"T00T";EE;-.4!3232673o3t00t3d3j65k3d$GE@(*]0A*%JE>(*\0A;EE;"T00T"Z"*20Q;!"+10Q;!s 2@ /   _/]322//3]29/10>73#%#"&'332>7^ -j7eo+MnFl0D,$A3!,.5!7d8j- +MnFl0D,$A3!,g<!5.,7'#'6654&#"5632+MnFl0D,$A3!+".V 8A9,' :dc7'".#"#>3232673+MnFl0D,$A3!/$GE@(*]0A*%JE>(*\0AE 4 Z fNo g .8*f 4 h    *  ,D p  ( F 8 \L \ TDigitized data copyright 2007, Google Corporation.Droid SansRegularAscender - Droid SansVersion 1.00DroidSansDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0Digitized data copyright 2007, Google Corporation.Droid SansRegularAscender - Droid SansVersion 1.00 build 112DroidSansDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~.nulluni00AD overscoreperiodcenteredAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonek IdotaccentIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccentmacrontonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352 estimated oneeighth threeeighths fiveeighths seveneighthsDeltauniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLC  ,latnkern:LZZ 2 "  >$ ~`!6#h'L(h(h(n(h())0)6(h(h)H)V(n)))***L+++.$.$+0f2 "4544$$$$67D7D7D7D7D~88889(h(h(h(h(h(h)H(n>b>b>b>b>l>z>z>z>z>z)H>>>>>@$ 2(h 2(h@B(h "(n(n(n "(nA(h(h(hBR(h(h$BX$ )6A(h(h(h~)H)H~)H)H(n(n(nCB(nDLDLA````)#h*H''M8PN 2(h(nQQJQXQRR&RST(UXXYZ Z]^__`d@de e~eefpj*j0(hjJj*jjjjk(hkzkkll6lDlZlDlllll)Hm,m>)H)Hmnnodrs4sJstudv*vxVy||}JX$~v ڏ 2t,(^ $J(n"h(n(n(n*48>&)))#h*ڡ 2 2t$ (yy􀒔$(~$( $($($ 0ڙ(~(^v*ڙ(ڙڙ^v*$(^ ڙڙڙv*$$sJv*ڙڙڙsJyytt$($($($$($($($( ^^ڙ$((ڙ$($($($(ڙ 2 2 2 2 2 2 2 2 2 2 2 2(h(h(h(h(h(h(h(h$$~)H~)H~)H~)H~)H~)H~)H``#h*#h*#h*$,)7R9R:f;)<R=)FGHJRTW)Y)Z\)\))))R)))))$R&R6f78R9):R;)=)?)C_iqRy~))))RR==R=)R))fffR)(X`jm)rv=)))=R)R)R)R)=R===)=)=)==  ))R)!#%')+-/1368:<>@BDE)G)JLNPRTVXZ\^`oRp)qRr)sRt)-{{{^&*24789:<$&,02468:fmq_al~IKMOQSUWY[]_acegikmoqs7qrvc &*24789:<$&,02468:fmq _al~IKMOQSUWY[]_acegikmoqs,79;<$&8:qEGoqs;) ) )&*24@)`)fm) )_l~IKMOQSUWY[]_K$,79:;<=$&68:;=?C_iq(Xr!#%')+-/13EGoqs9= = )")$9:<@)`)68:C_i= =(Xr!#%')+-/13oqs9) )&*24fm) )_l~IKMOQSUWY[]_9) )&*24fm) )_l~IKMOQSUWY[]_c &*24789:<$&,02468:fmq _al~IKMOQSUWY[]_acegikmoqsK$,79:;<=$&68:;=?C_iq(Xr!#%')+-/13EGoqs533$&;<=q8:;=?C_i(Xr!#%')+-/13oqsK$,79:;<=$&68:;=?C_iq(Xr!#%')+-/13EGoqs 7$&q R R")$&*2467DFGHJPQRSTUVXYZ[\]q  !"#$&79@CDJ_fimqy{~R R(XY_`jlmr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt#$C_i(Xr!#%')+-/13R R")$&*24DFGHJPQRSTUVX !#CDJ_fimy{~R R(XY_`jlr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnf f$&*24DFGHJPQRSTUVX] !#@CDJ_fimy{~f f(XY_`jlr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjln) )&*24FGHRTfmy~) )_`jl~  68:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`R R")$&*246DFGHJPQRSTUVX[\]q  !"#9@CDJ_fimy{~R R(XY_`jlmr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt9) )&*24fm) )_l~IKMOQSUWY[]_ = == =&f fYZ\79f fmprt) )J) ) ) )) )[]@f fDJDf fY"$&(*,.024R RWR RR RIR R6) )R) )`j  JLNPRTVXZ\^`= =I= =2R`j  JLNPRTVXZ\^`qx)qrx DD"&*-^24789:<=;IWYZ\^$&,0246789:;;=;?;fmq^ _alm~IKMOQSUWY[]_acegikmopqrst DD"&*24789:<=;IWYZ\$&,0246789:;;=;?;fmq^ _alm~IKMOQSUWY[]_acegikmopqrst DD"&*-^24789:<=;WYZ\^$&,0246789:;;=;?;fmq^ _alm~IKMOQSUWY[]_acegikmopqrstf f DD"&*-^24789:<=;WYZ\^$&,0246789:;;=;?;fmq^f f  _alm~IKMOQSUWY[]_acegikmopqrst9) )&24=fm=) )_l~IKMOQSUWY[]_7) )&24=fm=) )_l~IKMOQSUWY[]_ $C_i(Xr!#%')+-/13V $,-679:;<=@` "$&68:;=?C_iq(Xr!#%')+-/13EGoqsB$0=D;=?CD_i(XYr!"#$%&'()*+,-./01234.R R = )")$&*-02467'9):@=DFGHIJPQRSTUVXYZ[\]`=q  !"#$'&'679@CDJ_fimq'y{~'R R(XY_`jlmr~'''''  '!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt   o oI[]@ a= ="FGHIJRTWy~= =  `j  68:<>@BDJLNPRTVXZ\^`= =I= =d &*-24789:<$&,02468:fmq _al~IKMOQSUWY[]_acegikmoqsf f "@ERKRL=M=NROR`R )=RRRRRR -f:) )&*-R24fm) )_l~IKMOQSUWY[]_B) )0679:;<Y\ "#$&689:q) )mopqrstR R")$&*2467DFGHJPQRSTUVXYZ[\]q  !"#$&79@CDJ_fimqy{~R R(XY_`jlmr~  !"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprtR R = )\")$&*-02467'@=DFGHIJPQRSTUVX[\]`=q  !"#$'&'9@CDJ_fimq'y{~'R R(XY_`jlmr~'''''  '!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt) )&*2468FGHRTYZ\ ",02479fmy~) )_`ajlm~  68:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`acegikmprt3R `j  JLNPRTVXZ\^` b)fi)mqrsux)fms _dglpqrtwx$m_bfisvyz{}~qrxy}~)O &*24789:<$&,02468:fmqrsux _aIKMOQSUWY[]_acegikmoqs79;<$&8:glpqrtwxoqsq &*24789:<m}$&,02468:_bfimsvyz{~) _aIKMOQSUWY[]_acegikmoqsR R")$&*24DFGHJPQRSTUVX !#CDJbfmqrsuxy)R R(XY_`!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnfms)F$,79:;<=$&68:;=?C_bgilpqrtx(X!#%')+-/13EGoqsfms)*) )&*24fms)) )_IKMOQSUWY[]_ ")$&*24789:<DFGHJPQRSTUVX !#$&,02468:CDJfmqrsux (XY_`a!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnoqsfm)F$,79:;<=$&68:;=?C_gilpqrtwx(X!#%')+-/13EGoqs633$&;<=q8:;=?C_bditx(X!#%')+-/13oqs)) )&*24fms)) )_IKMOQSUWY[]_R R")$&*2467DFGHJPQRSTUVXYZ[\]m}q  !"#$&79@CDJ_bfimqsvyz{}~=R R(XY_`!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt-m_bfimsvyz{|}~) _bgiqrtwxfmsy|}~ _biqrx,) )&*24fms) )_IKMOQSUWY[]_R R = )\")$&*-02467'@=DFGHIJPQRSTUVX[\]`=m}q  !"#$'&'9@CDJ_bfimsvyz{|}~)R R(XY_`!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprt)y~y|}) = =I)= = = =y= =yz}~)R Ry}~)R R ) )y~) ) = =I}== == =I= ==~)[]@~)[]@)R RI}))R R[]@R RIyz~)R R[]@y}~)8) )&-=24=) )_l~IKMOQSUWY[]_=  mv|} R R\&*24789:<$&,02468:\\=\R R_ajklmr}~q\ \  \ \\IKMOQSUWY[]_acegikmoqs) )) )l}~m}7) )&*24 )_l~IKMOQSUWY[]_4  v|} 1  v|} m}m) )&*24))) )_jlm}~)  ))IKMOQSUWY[]_gR Rq)\\RqR Rjkr})))))\  \)\ &*24789:<$&,02468:=))))) _al|}~)) ))))IKMOQSUWY[]_acegikmoqs*|} !79;<$&8:voqsR R\\\=\R Rjklmr}~q\ \  \ \\B))))))f)lr|}~)) ))))1= =))))= =l}~))))))&) )) )| L) )&*24=)))) )_l}~))))))IKMOQSUWY[]_V$,79:;<=$&68:;=?C(Xrv!#%')+-/13EGoqsE= =33$&;<=q8:;=?C= =(Xr  !#%')+-/13oqsB) ) )&*24@)`)) )_l}~IKMOQSUWY[]_RR R")$&*2467DFGHJPQRSTUVXYZ[\]q  !"#$&79@CDJqq)RR R(XY_`jklmr}~q    qq!"#$%&'()*+,-./0123468:<>@BDIJKLMNOPQRSTUVWXYZ[\]^_`bdfhjlnprtc= =)qq== =jkr}q    qq*rv.) )=))) )l|}~) ))0)))))f)r|}) ))>=))))=))f)r)|})))))))) )))) q |    | #vv m}!m}$= == =m}){}  ) )) )j}  = == =m}!) )) )j}  "[]@m= == =j  0R RIR Rjm  $= =I= =j  []@}j}  )))f)})))=)f)})))qm}= =q= =m} j  ==!m}m}) )) )j  j  R R\&*24789:<$&,02468:\\=\R R_ajklmr}~q\ \  \ \\IKMOQSUWY[]_acegikmoqs7qr$,)7R9R:f;)<R=)FGHJRTW)Y)Z\))))R)))$R&R6f78R:R;)=)?)C_iqRy~)))RR==R=)R)fffR(X`jrv==R)R)RR=R=======  R)!#%')+-/1368:<>@BDE)G)JLNPRTVXZ\^`oRqRsRE=IfK=L=M=N=O=WfYfZf[)\f])pfrftfE=IfK=L=M=N=O=WfYfZR[)\f]pfrftf   $')) ,, ./ 257>DFHKNN!PR"UW%Y^(mm.}}/06@EKW]clmpqrsvwxyz{}~  !$&',,0022446;==?@CDJJVVZbddfilmo|~ )  *((+XX,``-bb.jj/lr0tt7v|8~?NWYgwxyz~ !!##%%''))++--//11336688::<<>>@@BBDEGGIVXXZZ\\^^`accffhhjjllnt latnfritzing-app-0.9.3b/resources/fonts/DroidSansMono.ttf000066400000000000000000003445201272363601300227130ustar00rootroot00000000000000`OS/2˕h`cmap}V6cvt DMrfpgms#gaspD glyf"head%=6hhea [$$hmtx[locaBmaxpwH name{'8postP(?prepAڂ~?@n_< TS =#s m#R/Zc33f  @ [(1ASC@ m J -=')=!uqTTf}!5!f!of\sR`RwNJ+\VBo!!!!!!TTTTTP}}}}!-ssssssRR!!!OufufufufHTsTsTs9fff}}}}}}!R!fffd!-PsF9+!e|z!DfT!qoTf!59J!yyR==sjJ+PZsZB!qTf5y=`Db9#!ssyRJ`}m+q{s#jR#!R{N=wR`JR yJJF}119%P9o197 q\!-BBTs}Rb=b{ \{u9PTs?R?RFPTsRXR^))/=;;+jTsfy!R!R5`qs!!-TTTsTsTsDRRR+#`q5`5`11#+'Py{uq)!!!!U!!!!!!!!YLTsTsTsJJTsTsTsTsTsTsTsTs}}}}}}}!R!R!R#999#*~ #?M   " & 0 3 : < D  !!!!"!&!.!^"""""""+"H"`"e%  #>M   & 0 2 9 < D  !!!!"!&!.!["""""""+"H"`"d%aI1vhcb]gDXz Bvt %"iOSVbrhijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu45]^   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a rdeixpk vj0-s12gw'*).l|!cn,B/(m}  3:f45y KRqNOPzSQL@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@o/o3@3U3U3U@h F pU3/@FSJpМ@8 F 8 0@SGŽ@vutPsPoHnGUa`e`d_y?OFO_o[[[Z9ZZ3UU3UQP@ FIF`H;G3UU3UUGU@oTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYssst+++++++_ss++_s+sss+++_ss+ss+ssssss++++++++++s_ss+s++ssssss+ssss++++++s+ss+s++++s+s+++++s++ QuJs}wUinfoxH/NyL' JxyZmmsexT9zo((((Zl'Bo-JiMUg4z K a 7 o 3 n  B UOeG6o-O,z]M_1hW`bW{:  ^ !)!>!!"("""#=#_###$D$s$$%3%K%b%y%%%&:&L&d&{&&&&&' 'Z'q'''''(7(((()))]))***6*T*l**++2+H+`+w+++,5,K,b,x,,,- ------.A.Y.q.....////>h>>>>>? ?#?W???@.@@A AIAnAAABB8BQBhBBBBBBCoCwCCCCCDoDwDDDDDENEVE^EEF_FFFGG(G>GTGmGH+HHI9IIJAJlJtJKKkKLL^LMMGMMN*NO3OOOPPP.PGPPQQQ#Q[[\\q\]]]]]]^(^0^^^__E__`4`s`a3aab b bybbbbccpcxcccdd:dRdiddddddee&eZԮ] iձ>dx ôڴ !;Tm׵(AYr߶+BXoз4Lmи+AZrֹ+KbuѺ,BXoʻ&9Pg}׼~þgؿOMG '@` p?2/3/]33/]310!!7!!IhyJh 4@ /??]]9/]9/329910#32#"54y3qD.@O 0?22/]/]]]10#!# ))))-@s    @P      ?O? O    ?3?399//]]33223322/]/]]9//////]9999999103!#!##53#5!3!33!!@RTRNARR%TT#@}TTHPPH"'@$$##  $pH&X&x&&n@H@P) n  s&#  _o/?_$s H/+33/32/]3/]3392/]3/]+2]9/33322210]]]]]]]]]]]%#5"'53&&546753&'654ĩBǡQoF\@ E> ZDqNV #+Ϲ" H @ H H H H@! H H Hg****!@H@Hh&&&&!@.H!-h g($ ????/]]+]99//+]+]33]10++++++++"&54632'254#"#"&54632'254#"#'#w/J8v=%-@'& $@0H@$4$$GHO;G 0yG@ H/)H@ HFFV7@7 H!PX X&8'X')'('& O,Q ???9/]9]]]]]9+]]]/+3/3/+]99//]]_]]+]]+93910673#'#"&5467&546326654&#"32ZG!5}كdccQWcˆ]yݦֻfAOwJJ\YQv{i@ ?/]10#))' '@ ??/]3/333103#?Da) '@p ??/3333/]10#3@s wwH$@     ?2/39/]10%'%7++wo`f`Fo3 @  ?32/]3210!5!3!!#}}{?@)p@ H//]9/+]]10#79L#Cy @ 0?/]3/]105!Ѩ-@0 ?9/]102#"54f-@ ??3/33/210#sJ9 6@ oo @ Hs s??/+^]]10"!2%2#"d+n}=; +@ n  ??99/^]3/310!#47'3'Raz+Fy{+-U@o@H@ H  st?2?39/]22+]+]3/]10!!56654&#"'632!-q^|lfv}~qwԳw"_@: o _ o$sh s s ?3?39/]9/]]3]9/]3/910]!"'53 !#532654&#"'632ibr\/O`!}fwy}ƥ=f V@ H n @H@ H@ Ht  ??9/9332/+3+9/]+332210+##!533!4#fհ\{ $6eHH0dfNR@1 @ H o  s st ??39/3/]3/3333]9/+10753 !"'!!632#"PYZ7'kf;`:&9]3!O@@ Hn  #o@Hu  ss??39/]2/+]2]9/+10&#"3632#"!24&#"326G_ _hΪQ4Z hݰ;G@0yGW'(t??2/]2]9/3]10]]!!5!N31)|@  &nn n  +!nH@" H9Yiy* #s#s??9]]]+]/+]]99//999910#"&5%&54632654&#"!2654&'5鲸f~nj}cl~Ϻߡ7gejjeZ}yuZJ3!N@ H o#n  @H u us??39/2/+]]29/+10532##"&5432!"32654&#"Cc `i2󑜣gޮf-@ 0` ??9/]32102#"542#"54fffD@.) 0` /??/]9/]2/]102#"54#67m3}>&f3a@=gwhxv@? /]]33/]]]]]29333/29]10]]%53e5fà3@@' pO_oP/]]]3/]]]3/]32105!5!eTf3_@  ɷ& 6 F  @H        Hy X   H H HG Z`p  @`w&6Vf  ?2?39]]]/8]]]3/8/]299]10+]++]]]]]+]]]]]]+]!##373>yr%3.@@HZP`p_??/]+1033!{q\@ @% H'  H:   ) @M H5& H(    @P Y  ?22]2.3?33/]2]]210]]+]]]]]+]]]+]]]+]]]!##333#4#XJwf!Ds @NHE U e 4  %  HJZj;*  IYF V  ?22]?3]3/]2210]]]]+]]]]+!###3353DBL/e T{ $@ [[/_ _??/]10 ! '2# hyx",/F 8@"[ Z`  `??9/]/]2]10#! !'3265!#jƫ9TTb{U@0 H [l| H[ /  _ _?33/3?/]2/3+]9910+%&'# ! 2# ^Uy\$('y@",/ s@K+ H)   H)    F [@H Z` ` ?3?9/9/]2+99]3/8310]]+]]+]]#! #'32654&##ue夫\aiq\|?8@Z!Z  `_??99/]3/]3/10753 54&'&&54$32&# # ^е@ۃ1VhpMIˢNNf|GXfd<@$ZcP_??2/]_]_]_]]]/10!#!5!!û^_}P'@ Z Z _?2?/]]10# 3! P3'L\}! @g H) @; H&hHX*  GW%@ H?3?39]+/3]]]3/3]]]9=/3]3]10]]]]+]]]]+]]3#367')/>Jaϑc@T)9''&6) 9 (&6Hh)9@!HTEw@ Hx H    i y [ J  g & 6 F  $4d  ('K [   ?3?39/99]3/3]3/]3]9=///]]3]]3]]]]]]]3+]]]]]3+]]]]]3]]+]]]3]]10]]]]]]]]]]]]]363#&'#367= ?F( !˪X".X56JDoil'5 @$    @ H&  H  ) @< H& H )      ?2?39/33/33/]22/39]+]+]+]+10!##33ZKNZ{/L!@$ H&6hxZH)9' H@JHYH{PZcP4?3?9/3]3//]_]_]_]]]9/]]]2+]]2+10+]]]]]]]]+3#3d}/fd D@(&) __?2?2/3/33/]3/310]]]]!!5!5!!d-3@  ??/]]3/]310!!!!Z1@ ??3/33/310#sJ!14p@H ??/]33/+]10!!5!!!Zϕo%^3@ H/]33/23/229=/+33103#of%d!H//3/10!5!+!@ _/]3/310#&'53+yjVXَ{^!B@& !G#HR PN??2?39//^]3]2210!'##"&5%754#"'6323265%M?Ļ˴ĤȓaKLER`knD:@"HGPP ???2?2/]229]10%##33632#"3 !"T %ksQ;%^+@  HQ Q ?3?3/]]2/10&# !27# !2%>vi"#9cj>=#&/:@#GH P P??2?2?/]]2210%##"323&53#54&# !26qt #+ -!)ZbH^G@,HH    PPQ?3?9/^]/]q2q210!!27#"32%!! HPï=LK+ 7#1ZF@( GNP ??3?332/]3/]323/9/10!#!5%54632&#"!{=с{)hg{X?n!`#dshff^$,6@/4$ 'F_8 0?O_+F@ H"%R.)O".Q "2N?2??9/29/99/+]3/]3/]293/10#"'332!"&547&547&&54632254#"#"! 54&fAվ7d`YfV@EdJqU8MT:/kpP%e ƮX=-<@&$4G G P?2??2/^]2]10]!# #3363 wͶ gr2o333@ G V N N S???29/]322102#"54%5!!5%yj?+k`Xr:9sr{E{{V9@ G P NS???3/]9/]329102#"5453265%5!#"kkj u\ZΤrssr)'re9{Av@ )9@>HFV  +; H` G `   ??3993?/]223/]+33/39910]]]]+]]73##3 V)%RZ6+ -@G] O  NN??29/]]33210%5!!5%`Xz{{{\s^ k@EveV7GG z  G   "G@P P ?22??3299/]2]]9/]]]]]]]210!4&#"##"#3363236325>WO}TH C2LtÒrJ3-^6@!G G  P ?2??2/^]2]10!# #3363 wͶ grJo3sX^ "@H HP P??/]10"32' ! b670Η`\D^>@&HG PP ???2?2/^]22q10%##33632#"3 !"T ns!+6d)/^<@%GHP P??2??2/]]210373#47##"3254&# !26y qs)+ -)Zb#^1@ G/ Q ???39/]]23/]10&#"#336632#1{eLv1-Jwf^<@"F!F P `  PP?3?399/]3/3/10753 54&'&&54632&#"#"byƫJ`k*:JrUp' RdJ F@ Hp @ HNN?2?2/]]3/3+]2/3+10!!5!5!!+y}Db@?  p _@      /  ??9/]]]9/]]]]29/]93210$4!5 5%pJRpQKNMKQ) )@ O??/]103#`@>  P O  /  ??9/]]]9/]]3]]9/]93210475&54&'5! 5665qRIq;) )LQLLQJ3Z/@   p ?  /]]]323/]105632327#"'&#"cdGlwcdFlJl?8yl?8^ %@    ?/9/9/329910"54323#fy3H%N@F 0  H@HP Q?3/3?3/3/+]]29/]3210&# !27#5$%53碜>vizN'89bk=;EJwVR@1 o  u/_ s t?2?39/]32/]3299//3210&#"!!!!5655#534632'By!ٵyHىN1ՉD'^@ %@ H O _ o  )@ H"?Oo/]3/+]]]3/+]10'76327'#"''7&542654&#"/berrccCCacrva`AxdecbDD`erx_`BD`_vrdefdN}@ H H@<Hfv t   g n g    @0@ ?3?99//]]]332329/]]]33229]2/399//3/]]]399//10]+++33!!!#!5!5!533dZ.Ҳ-!@O??99///]32103#3#'2}@+ (/-H**I F4G//I& F&&@ H&&3(- -( N N?3?39333/+]33/]23/]999910]]]&54632&#"#"'53254&'&&54654&'Э8xcwVprq+PxHCt6N=>qXPAR9P>ThaLmYo00Ik?J 7@$p  Oo/]323/3/]102#"54!2#"54``````fggffggf ,o@F)#/? . +&!$!o `!p!!!!!! ??99//]]]]33/99//]310 ! 2#"#"&54632&#"32fCAkrܿ~=nSy^hjXWU..Um2>6G@% /0  ??2?39//]33/]]2210'#"5467754#"'63 32655!hު}p)#rxhi}vtg 3n:D?4KZn[s G@*  `  @0`   ?322/]3]]210%5uu5uu)NNNN3,@ H?2/3/+]10#!53y #+@^"$'""``"""" - $$+o "`p ??9///]]]]]]3]3/99//]]]9332910 ! 2#"###3 %3254&##fCAv^EoHTYEhjXWU..UPu`}J;?//10!5! +V @   H H@ H@! H 0  @  H?+]/]]10+]+]+]+]2#"&5462654&#"fOrsNOqp˻pNQrsPNp3 )@    ?32?/]332210!5!3!!#5!}}}{\oM@3O_ `p H  ?2?3+9/]2/22/]3/10!576654#"'632!oo=oQSNuvFkfve9sLVgxhBVN@O O_`po  H H?3+?3+9/]]]]]9/]3/3/]9/3/]910#"'53254##53254#"'632Tn]HF{aGZBom='t2|A}hoFV]m^+!@ _/]3/210673#^PpyJ=@ GG@H P  ?3??2?/+]22]21032653#'##"'#3jڔ `NjJ:XW6B7M`@%H"2  @ H Q/2?9/3/+9/]]+]10#### 63!7yy=U]3y3H^"@0` /9/]102#"54f^!C @! H0 H   _  //993/3/]993310++53254'73!"!/[x9"l\J's%o !@   ?9?9/33/103#47'J )!FIE,3X 0@    0 ??/]3/]]10"&54632'254#"dws G@*? @     ?322/]]2]210'7'7uu5itt6eN\\NbeN\\Nb'{&<9Y<@'P@]]]]]5]]]]]55?55'{&tZaD@-   P@]]]]5]]5]]]]]]5?5'u&5<NY8@$&&&&&&!!!!!!]]]]]5]]]]55?55w^![@7!@P# #G Q0 ?]]/]39/]9/]]]]9/]2310"5432327#"&54676655'YvT>kwAH%wTate]qZjãlWkZ!s&$CR&%+5+5!s&$vfR@ &f%+5+5!s&$KR@ &%+5+5!5&$RR&%+5+5!+&$jR&%+55+55!&$P}4@P@ %+55]]]]]]]55@T H7 ZO_ _ _ h    __/3?299//]]/329///]q323]]99+10!!!#!!!!!3#s`Rh#Jwj;<&&zP (" %+5#s&(CR & %+5+5#s&(v?R@ &' %+5+5#s&(K#R@  &  %+5+5#+&(jR@ &%+55+55s&,CR & %+5+5s&,vwR@ &x %+5+5s&,KR@  & %+5+5+&,jR@ &%+55+55{ I@,[ Z_h/ ``??9/]]32/]329103#53! !!!3 !V=ar|` C4D5&1RR@ &%+5+5T{s&2CR&%+5+5T{s&2vZR@ &Y%+5+5T{s&2KR&%+5+5T{5&2RR@ &%+5+5T{+&2jR@ &%+55+55-{ \H@ HH H H  HH@ H/]/]]10++++++++7'i=Bh?ff?i>gf=gP!`@8 H H [  #[/_ _ ?3?399/]329910++%'7&!27#"&# 3245\yu|\}slPzPJNŸ`iNɼxD^V",}Ps&8CR&%+5+5}Ps&8vLR@ &L%+5+5}Ps&8KR@ &%+5+5}P+&8jR@ &%+55+55!s&<vLR@ &M %+5+5F >@$[ Z ``??99///]22]10#33 !'3265!#jƫ=T*r@K F GO _     G&,G P)P???39/]9///]]]]]]10]%53254&'&&547654#"#!2#"ߏ[b{dمT1:z^ȷ1VQf=NzWujjZNouJ>!'HFV  +; H` F `    ?3?3993/]223/]+33/39910]]]]+]]73##3 VR}Js&/veR &H %+5+5+&OvN@ &A %+5+5;&/9' %+5+5;+&O9 'ݴ %+5+5&/8 %+55R&O8sf %+55&/O %+557&OOf8Y %+]55HD }@6  y *v%  Z@ H  _??99//9/+]33/29/10]]]]}!'73%!}Iƻ+N{N}}X}F+@i f     H@< H o    G@]O N   N??99//929/]]3/]322/]210++}]]'7%5!7!5%NäM`XP\yz_ly{{Ds&1vbR@ &c%+5+5-!&Qvo@ &o%+5+5;D&19'ϴ%+5+5;-^&Q9'%+5+5Ds&1LR&%+5+5-!&QL @ & %+5+5j7@G G/ P??3??2/32/]210#6# #33663 2~<(`㶔 *gnJrJNZo3{DbH@6HEUe4% _ ?33?33/3/]22229/10]]]]++##3353#"'5325)B Ȼ^>MQ^-^<@#G  GP P???2?3/]2]9/10!#3363 !"'5325# V gQ46=JomrT{&2MR@ &%+5+5sXo&RM@ & %+5+5T{@&2NR&%+5+5sX&RN@ & %+5+5T{s&2SPR@ &N%+55+55sX!&RSR@ &R %+55+55I@)Z Z_h _ ___????9/]/299//]210!!# !2!!!!!%&# !203.RHrg:I5F < X!9^$)l@) )G@Pp%F@ H +"G@HP))' P P?3322?3229//+]+29/]29910%#"32632!327#"&%2#"#"XYTIl`^{W䨪ZNO{} 3 +qLIv!1s&5vR&ݴ %+5+5#!&Uv\@ &-%+5+5;&59' %+5+5;#^&U9&@'_?%+]]]]5+5s&5LR& %+5+5#!&UL &ݴ%+5+5?s&6v`R@ &&` #%+5+5!&Vv^@ &&^ #%+5+5?s&6KR@  && %+5+5!&VK@  && %+5+5?&6z ,%+5^&Vz ,%+55?s&6LR@ $&( %+5+5!&VL @ $& ( %+5+5f;d&79 'ߴ %+5+5;o&W9@ '% %+5+5fds&7LR@  &%+5+5&W81Q %+55fdY@3  Z     c  P  ` _?2?9/32/]_]_]_]]]39/2/9/10!5!!!!#!5^_&ڻ3ߤ!bop@CG  /?N  N P?3?33329/32]2/9/]]333/]922910%# 5#535!5%3!!!!32z MiJ3] X^1ۉ}P5&8RR@ &%+5+5-&XR@ &%+5+5}P&8MR&%+5+5-o&XM@ &%+5+5}P@&8NR&%+5+5-&XN@ &%+5+5}P&8PR@ &%+55+55-&XP&%+55+55}Ps&8SLR@ &K%+55+55-!&XSP@ &O%+55+55}9P&8Q%+559dJ&XQ5@%+]]55s&:KR@ & %+5+5!&ZK& %+5+5!s&<KR@  & %+5+5Ry!&\K@ & %+5+5!+&<jR@  &%+55+55fds&=v9R@ &: %+5+5!&]vN@ &O %+5+5fd7&=OR@ & %+5+5&]O@ & %+5+5fds&=LR@ & %+5+5!&]L @ &  %+5+5d .@GO_ P ??3]9/]10&#"#4632)izw\}`wNR@*! o   ! u s s?3?39/329/]33/299//99//10#"'53265#5754632&#"33F;8\?™ad/OA\?D?fqKD%fq!s&$&P}vHRR@0(`(P(@((DP@ &)%%+55+5]]]]]]]55?]]]]5&D&Pv^H@1@@@@`@@@""""&@<%+%!%+%+55+5+]]]55]]]]]]5s&vR@ &%+5+5-!&vq@ :&p47 %+5+5Ps&v9R@ (&5"% %+5+5sX!&vP@ *&Q$'%+5+5;?&69@ "' #%+5+5;^&V9"' #%+5+5!+@  _/]293/329/3310#&'#567673yU~Nx"%%;82&OOC!+@   _/]293/329/3310#&''53673"##;xRzYy&LLC6;FoA@p@ H@ H/+]3/+]3/]10!!F?o9 L@ `p@ H   _/]23/]3/+]10# 3327 mOh L1}J@4[B& Oo/]3]]]]]]]]]102#"54fkkjrssrH   @ H@ H H H@*H H  H /@ H_/]+]++]+10+]+]+]+]"&54632"32654&bfyyfeg1?p2??tcctv_bwH<5q<55<9/8@"-=M0 H O_ /]/3/3/]9910+]#"&547332/ISw~Bqm^l}q`3@    /]323/3/]10#323273#"'&#"fh:gf"Pi9dd(R88r98!C@* @P O _ o   _/]323/]3/]2210673#%673#^PpdjWWpev! @  _/]3/3/3107673# c=mkrPS+ W@9  O o   /]329//3/]99//]310673#'2#"54!2#"542)]zRa8)`aa`lf16gffggf!&$TED%+]5?5H^"@0` /9/]102#"54f^#&(T!@     %+]]]5?5eF&+T!@     %+]]]5?5&,T Ŵ %+]5?5|{&2T%+5?5&<T4 4 %+5?5z&vT%+5?5&UZ %+555!$q%/9@@HZPH@ H_??/++]q+10!#/D չ @HHH) @ HG& H @THW G %    ` HX H *    /Oo[)9IH _?99?3]+]]/]]3]]]]+/]3]]]]+9=/33]]10+]]]]+]]]]]+]+%!53!&»_3%4}}1n #(fd=F+T{ a@@t/?{ 0X  [W[/`h _ _??9/]]/]]]99//]]]]10 ! '2# !!heyx",/,.! @H  HH )     @HG &   H   @DHWG%HXH* /H H ?2?3]++/]2]]]]+/3]]]]+9=/33]]10+]]]]+]]]]]+]+!#&'#3?-+ǍȆ`q\0D1o\ G@)/? 0   _h _ _??9/]/3/q2/99//]]10!!!!!5eQs<T{2F%@Z Z_?2?/]10!#!#!FF3H @W7 &   7&8)   _ _?9?9=/39/]333]29/310]]]]]]]]]355!!!n}Xgf%fd7!<'@Y& ) )& Z'   r @ P ` 4   HZ/?o)#@$ H#Z'` P  ``p?2/]32?3/]32/+]+9/_]]_]_]]]332210]]]]332###5#"543332654&+"33//! ˴>6SջѬ5;9u@NZIrP`4D Z/?oZ  w` ??339/3]2/]]9/_]]_]_]+]3210####"&333332653嬊B!ZJw@N HO_  H@PX[ W[/_ _?32?/]3]2]9/]]3+]]]2+10%!5!&32!!5$! -  +8\S褓 E+&,jR@ &%+55+55!+&<jR@  &%+55+55y!&~T.&&* %+5+5!&T`@ (&] $%+5+5-!&TP@ &O%+5+5!&T@ &* %+5+5;&U!% %+555555y^%=@"G '!H #P P??2?3322/]]222103673327#"'##"3254&# !26^ &3T4?( lUg-]o )*ZbP&Q@.* !HG( G  P P$P??3?9/9/]23/9/]910]#"'#46323 4&##532654&# }غu燚%^Fr=0?4ҷyP+~vRyJk@%HD T t   H 0@ H/? @ H ??33+?33/]3+3/]3+9/]93310%#433673ne7G$H?)>hiF"O@1+  F!H$HP P?3?9/]]29/9910]]&54632&#"#"$5%3 4-IJVTi%'˅mjI@?gdt,d^^Q@/  !GGPyPP ?3?39/]]9/]3/]29/910# !27#"&5475&54632&#"!HĪا?VKK BLHo+@W *  &  + HF  H{mI;-ykI;- P?2?99]]]]]]]]]]]/]3]2+3/3]10]]!#654&'&&5%#!FtŦ嫗yygBx׉v'eob9A# -^1@G G P ????2/]2]10# #3363 wͶ frJoG9 O@2 ;(H  '7GO  /  PP??9/]]]/]]2]_]210"32!#"!32d3+n|n;mJ &@G  o P??3]9/]10327# bpe]J XJ=!U@3 $! H/?H  PP ??39]?/]+333]]99333103'&&#"5632327#"&'&'#=5"VI80A>FF%A2ARWE$S;hH )o PWz-J:@# GG P  ?3??2?/]2]21032653#'##"'#3V a̭K:XW6=JJ t@F  H 5 E U $  )9  c s   DtG @ H ?2?3/8+2]9=/]3]]]]310]]]]]+3363#=n JEro+,@_)F"$_$$F  .H{mI;-Hy k I ; -  #O/,,,%"P#??29/]999]]]]]+]]]]]]/]]9///]]]99310#"#654&'$4675&5467##5!#"3LyycJזh>Eٷw[i8 dod5ADZ* ;r3 qhsX^RJy@5K [ k : (   G TE6'G @ P ` @H    P  P?3??22999//+]33]]]]]3]]]]10%#"!##57!#324Umފ&Z.PJPN};^5@H @H GPP ???22/]22+10%##32#"'32654&# TרqpFa0dto)^g@B FH{mI;,ykI;,Q ??399]]]]]]]]]]]]/]]2310#654&'$!2&# yT7>rfoc<;GZ;9H#BJ 2@HH  P P??2/]]2910!#"!#"3265+;;ǨJ>п j9JS@ G  @H= /   @ H_ PP ?3?29]2+9/]]]+]3]10!327# !579%kTT†J#XsJP;J3 F @H G GP ?2?/]3+103!2654'3# ??>JJ^\@:VfGI F IP/?P P??32?3/9/]]]9/33]]210$7!2$#" >xaD 1:ZΆ!^}&o+N@\&IY& )FV )  H @+ H  P  P ?3?3?39?399993/]3/33/+3/+399933]]]]]]]]}103327#"&'#&#"5632}7_.P@,$B4fG۴DT"5LQn7P3rYZy|> mPi@V f G  I FF@+HF@P/?P  ???32?3/]]]2+9/3]]210$43#&3-??'Ӭs&'ZZuJ@VG6@cHW6FV f G  II?!YH9HX9II0@P/ P?2?3229//]]3]]+3]]]]9/]]93]]+3]]]10332653323!"'## åhCKO?iiߵ2 2Jo~2΂2ز(.&j@ & %+55+55;&j& %+55+55sX!&RTH@ &H %+5+5;!&T'@ & %+5+5Zu!&T3@ (&1 $%+5+5#+&(jR@ &%+55+55}@IH Z  F V 7 /?  Z _@_ _ ??2?39/]99//]]]]]3/]2/+]210%532554###!5!!!2#"DCIνX#o/s&avmR@  &: %+5+5D@'@H [  _h__ ?3?39/]/]299//+10!!!27# !2&#"LuŒf6ܣNN:;iZVN?6,+&,jR@ &%+55+55- @X HxWg H x   H Z`G" _`_ a?3??9//]99//]]23+]]+]]2+10#"'532667!3 ##3 4&##wp8( $#5)'Ϧ- Fa^ xBC@% ZZ@ H_ h `?2?39/]32/+29/3210!!#3!332#'3 4&##%¥>')- Vhܠ xi@;H Z  F V 7  Z _ _0@  ?3?9/]29/]]]3/]22/+]10!2#4###!5!!κI#s&vR@ &B %+5+5i&6R@ &%+5+5F C@*Z Z Z _/33/?3/]9/]10!!#!3!3Fv{I!$o 3@[ Z_ _ `??9//]22103!!! #%! 4&##+R<^ܠ yq%/a @ H  H  H @ H[Z  @% H    Z _  _?3/322?/3/99//]+]+]10+]+]+#!#36!3!#űq Pk'Xjk#(@Lg w   6 F V  F V 7    Zhx 9IYg  g@ g@2hh   @ P    ?33?339933/8]3]3/83]/83]3/82]9/]]33]]]]]23]]10333###u]Vs<<</#Q@.[!! [%  `h ` ` ?3?39/]]9/]39/3/9]10!"'53 !#532654&#"'6!2cvɌv]O`}gzy}ƥFb@@H5EH:J )  i&  ?22?33/]22]]2]210]+]+333#4##BլpJFFi&6R&%+5+5 @`% *'  Z`p)  )@`?2?399/8]]]2]2/83]/]23]10]]!##336<BFo@! @H | Z j - = M   @ H HZ@ HZ_ ` ??3?/9/+]10++]]]]]+!#!#"'532!FuGB98fn~/iq\0F+T{2FnF3&fd7@    H@YHIY8) FV7&O_oe6%  _?2?39]]]3/]3]]]]]]3/3]]]]]]99=//++33]10#"'53267337TLx~hJU]ny6  '8b-gz?ms5; :@" Z Z _?3/2?3/]]3/10%3#!3!3 }F/@Z Z _??39/3/]210!## 33273FgnۻVZsGтePyV \@@eV7G{Z Z? Z0@P _?2?33/]]]9/]]]]10!!33333V#=@Ye V 7 G {  Z  Z?Z/o  _?3/22?33/]]]]3/]9/]]]]10%3#!33333 u ;@! Z[ _ _ `??9/3/9/]210!!5!3 !'3 4&##FD#^bRD z`m S@5I  0p? Z 0_ `?2?39/3/]2]9/]]10333 #'3 4&##3`/JNR/^ܠ yVJF <@ [ Z@H_ `??9/3/+]2]10333 #'3 4&##Rƪ^ܠ yDLJ@+ [ /  _h_ _?3?39/]3/]3]29/]10'63 !"'53 !5!&&#"M0YԒŃqٜV{k;:bn@+/O_@H @ H 7Zf@ H_h __ ????9/]3/+]]2+9/+]310##3332#"26&#"qj_`il^]V]"qs;eh9 q@   H Z   H[@H` ` ?3?9/23/+]3/83+]299+10]]#$!!## 33b}tʤ\b6J\^D;#>@%H @H !HPP?2?9/22/]2+2103632#"4>! #"!s tܺ>h% _#CAҪh? j9wlTJ [ @1 HF FGPx PP??9/]]]9/]23/910+]#!! !2654#!254&#L5%r>F#ߕJ@HT>\H#J.@GoP??99//]]10!#!˶PJ!hJ t@ ( H@5 H P     F GO _ o  GN  P?3/322?/]3/99//]]33++10]]#!#3!3!#hVP$}+P$H^HJ@iyK[)9 fvDT&6 V f  7 G   G   hx H H@Pgw H  H @   ?33?3399333/83+]3/83+]3/8]3+]3/83+]9/]]33]]]]]22]10]]]]]]]]]]33###3^mo5--5^ W@3 F F"`p!P  x  PP ?3?39/]]]93/]33/9/9102654#"'632#"'53 54!#5?ɖOWHL9 AϗEV-J H @P H  H H iyfviF f  F    ?33?33/]22]]]2]]210]]]]]++]++3##L  JdjJ-&6%@&p$ %+]]]5+5J @NX HveFV5&X H   G@H ?2?39/+]2222/310]]+]]]]]]]]]]]+]3##36ݴJ-JJ@ H @oHTd2B$ H )x9Ii     H ) 9    0@HGP O ??3?/]9/+]]]3]]+]]]]3]]]3]+10]]]]]]+]+!#!#"'532!%>o$/ sZJ@HGH@7H H\l|:J)I@0HScs5E& I  @ P p    ?333?333/]2]]]+]]]2]]]+]9=/3+]3+]10]]!##&'#367673Z Շ<00 dU&yJpM '"-J ?@&G  G  Px ?2?39/]]]/]2]210!3#!#V!߶J9JsX^R-J)@G GP?2?/]]10!#!#!VJD^S%^FyRJ<@%G P??29/]3/]2/]10!#!5!RooPRyJ\J ]@;VfGI  IIP / ?  P P??32?32?/]]]9/33]]2210#$%3$51ϝ#Z2*$-  )s<~!y`jJ[J 8@ GGo   G P?3/2?3/]]3/10#!3!33}JPP%J/@ GG P ?2?9/22/]]2103273##"&5NͫȠJfs}RJ Z@?FVfI 7I? 8I0@PP ?33?2/]]]]]9/]10%33!333+JPmJm@IIFVfI 7I?8  I 0 @ P   P   ?33?3/22/]]]]]]9/]3/10%333#!333vVP}JP+J C@) GI P P  P??9/]/]9/]2103 !!!5!3254&#J\vZRqZJc@BI Y i  I`p7I88  I7 P P?3?39/]/]]]2]]9/]]]103 !!33254&##3\ޡv¢JZRJDJ>@ F G@H P P??9/]/+]2]10! !!3!254&#\9vJZR%^i69  H@1 HH Q/xQ Q?3?39/]]]3/]3]29/10++]]753 !5!!"'63 !"_l> )>\=9;{^f@!II @ H 8I@ HPx PP ????9/]]]/+]2+9/]310##33!2! %2654&#"#LSg]\jh]]J9ŗs J o@$H)   G  Ww@ 7G PP ?2?9/9/]]3/]83]]299]]]+10!#&5463!#!%!"39#̭CfH&Hj@ &! %+55+55-"b@: G$ G  P!PO   ??99//]323?/]32]9/9105325#"##5353!!363 !"6=좓{ jQ-DՉo#!&vh@  &6 %+5+5%^]@:69     HQ/x Q Q ?3?39/]]]/]2]29/10]]]]&# !!!27# !2%>p]# #9Ú>=$$^V3L3&j&%+55+55VMJy@N  Hz   H H HG`I PPO P??3?9//]]99//]23++33+10]+]3 !###"'5326!3254&#3\Ѻ!4\`>)1-#7v&k T~ZRjJV@8I I8I@ H PP ?2?9/]32?/+]29/3]210!33 !#!#3254&# +3\բo7vJ9=JZR-!&vZ@ & %+5+5Ry&\6 @ &  %+5+5-J D@+G G G  P  ?3?33//]]9/]10%!3!#!3V!}J#>@@H ZH@ H_??3//++]+103!#s--#-@Go GO??3//]]10!#!3߶!?J?s&:CR&%+5+5!&ZC&%+5+5s&:vRR@  &R%+5+5!&Zvh@  &h%+5+5+&:jR@ & $%+55+55&Zj& $%+55+55!s&<CR & %+5+5Ry!&\C& %+5+5y@ ?/]3/105!\Ѩy?/3/105!Ѩy?/3/105!Ѩ1<@% & )/]]]]/]]]]/3210!5!!5!  ^ *@&o?9/]]10'636{>& 8@() 0p ?9/]]10#77yA$ C@1) 0p /?/]]9/]]10%#77yG$ (@&o?9/]]10#'7(<|5 J@2&/ &    ?329/]]3/]]10'63!'636{>&{6{>& \@A )  /   )@Pp  ?329/]]]]3/]]10#7!#7)7yA$7yA$   ]@@ )  ?  )@Pp  /?/]]329/]]]]3/]]10%#7!#77yGq3}G$$ /@   ??229/]332210%#53%87778L_7@   ?22?229/33322210%%#553%%K77L//L77K/y"x{R *@P`_o@ /]]]102#"&5fmo|N_@=4%4%4 %     ?33229/]]]]3/]]]3/]]]1072#"54!2#"54!2#"54 #/9E@vC3338-'""o"""""=88G :$@*5p0 ??399//]]33222/]3/3]99//]]]3]]10"&532'2654&#"'"&532'2654&#""&532'2654&#"r~q?*/:;/+ F)r}q?*/:;/+Ps}p?),<9T5#f} dF3}`j'j@ ") Z  @/_%_ _??99//]3232/333223/329310!!!!!27# #53'7#5332&#"BI(zA^'ۿOzx>:; &x\J#+" H @ H H Hf****!@EH@Hi&&&&!- P  f($  ?33???/]]2]99//++33]10++++&#"327#"&54632#"&54632'254#" RDgIJlcQ'!k!!k% JBl!j@>n  pp?? "# v vu?3/?9/33229///]]]]3/32103!"&5556746323264#"urVkHyn~g53Ly(yQk&*@$$++ @(Ht V f 7 G 0H{Yi8H'$ @o*@H*, @1!K[@ H(V(f(v((a'D T  H ?33+]3]]?3+]399//]/22+99//]2210]]]+]]]+]]]]!###3353"&532'2654&#"5! C1rrx?00?@00u/d\Zysqrmnqptuk@?   z  ?333/33392/]3]]]]99//]222210##5!###33#47#/ { ell%/VWxJvRwH<@ / ?  www H ?3+?9//]322210!327#"532'&#"w{Gaۗ3,y|{){D1Qwu'{&@VgT@7  d T D  P@]]]]]5]]]]]5]]]]555?555 'u&@jgL@1(((((((!!!!!!]]5]]]]]5]]]]]555?555'=&@jgP@4"""""""]]]5]]]]]5]]]]]555?555'?&@jgP@4 k [ K 4 $  ]]5]]]]]]5]]]]]555?555yR9@F !HN PP ?3?9/22/]29/1076&#"563 ! 432&&#"32 BLŻ^(T>{Ui2J ȵ H H@D H& H)  HH X *    _o @6HG W %    P` _H[)9I?3]]]+?99/]3]]]+3/]3]]]+9=/3]3]10]]]+]]]]+]++73!&'!Jw;"-+qEoȆX1@Z Z@ H_?2?/+]]10!#!^J z@R7 &   7&8)  @ H_ _?9?9=/39/+33329/310]]]]]]]]]55!!!JGMs+r 3@ ?3/]105!.@ //]9//2/33/39/3310##5!3d( iF'_ H@2 H H H #)&   ?/]323/3992/]910++++#"&54632632#"&#"32732654&#"bvwwltvqRmAPPChVkAPT@gmծլBkOQemNOg)@  ?3?39/]3210&#"#"'5325!28:N7<8JX׮'b{3%#H@) %$" ?  P/]]33/]3223/]32105632327#"'&#"5632327#"'&#"bbJmvadIpscdGlwcdFlm@7yl?7l?8yl?83n@A     pO_o  P/]]]323/]]]323/]3299//393910#5!!5!3!!!'=yJi{;93 e@?gwhxv  @? /]]33/]]]]]29333/3229]10]]%5 5!3e5efà3 c@>hxgwy   @? /]]33/]]]]]39333/3329]10]]55!6e}VV=f=ݖ}P D@    /3?39=/3333/]329=/333/3310#3 P;L>L 11+"R@+ G $GS O!P  ?3?3?3329/]/3/3229/9210###5754632&#"32#"54#3Ùad/O@\@!j?+kŷ?KD`%erhr:9srJ1B@" GGOP ?3?3?32?/3/329/910###5754632&#"3#3Ùad/O@\@{?KD`%erh -@  _/]23/3/]10!"&'3327ì Tw”fUVJ/@G  PN ??3/]9/]91053265%5!#"u\ZΤ7're9{A @  ?3/3/107673# T=`ktN_;@  /]3/3/10673#>`CcVR!@  _/]3/3/10#54673?[IbY99 "@ 0 ??/3/]10"&5!2%2#"d+ќ97FD% V@4_ hx H    ??9/]93329/]332+]23/]310##5!533#547TyT((Vb RZI:PD\@9 @H O_p   ??39/39/]]3/]99//+3331053254#"'!!632#"Pfi4I>#=v|D'}nm998@0   ?2?9/]2/3/]92103632#"&54#"326-4 =p͓Jb]KDSmn\G~zc> IaYMDUH};b~ͺcW8P{^7%9EKQW]aeimquy}@.bze}z}L]?E %!_!o!!!%*7O _ % %`%%@dH@7P77 %77% FUUvrn]Fkg_Lthp`xlh`ll`hLG)99&~~ H`%%%&o&&&4-<B--XSXzLScG/33/3399//33/]333/]]9/33/]39/+39339///333/2222/33339///]+]]33/]3333333/3993310#".54>3232##!##"&'532655#32654&#"%!#335!#55353#35!#3#3#3#3#35!32654&#'32654&##4N66O33O66N4XX%#WK HXE+ ''^l58933985/oo0mmoo9oooommmmmmIu;&*73%!%/X@##@X67Y?##?Y75B(6&AH4MPLR),SSJKSSK0o/mom-oohi;mmz" P)9N@&))*q2P2`222****7-))--/3/33/3/3/3/33/]3/3/]]3/310 54>7>54&#"663232>54.#"fe##5##HFAB7f-52 &+ A2)  )2AVW+"!"!9>G.|!)5*($%36;$7A:/ "/ 8!&7L o %+5  + O]@7FCC;GMQQQ3F>OONN"N)))N)N)8NH8N??99//]32/]]3/]329/10.#"#".54>54&#"'6632324&'.54>323j 9M[0LV.j<@ăoV" 6%%d62E, 0N:C'QYhuKlt=XH8iR3:s7_}E(]YK-! 2D(#V]a-*J620>KxQCpR.Tߋ H @F H& 6   YT )  ZcP ;;'@ H??9+]]]33/]_]_]_]]]2]]9]]2]]++10>7>32&&#"#3dKNI.6@)"+ ! ># /:B?8K,(=' /<Sn</J @_@<V8f8G88I77!I B,I@P/771' P <1P?222?229//]]3/]9/]]9910".54>7#57!##"&'#4.'!32>55332>{OnE ## EnO^o o ## #4$$6#O?$4#DrCtnrBJPBrntCrDW[[W>rpvCCvpr>[R&)Hb9˂&Rq\u&0vdT@ &d %+5+5\s!&Pv@ '&~!$%+5+5!&$[@ '%+55+55^&D[@ 1',"%+55+55 '2\-@+`3P3@3 3333%+]]]]]55?55HM@H/H @H pH/+]+/+]+10#".54>324&#"326H#>T12R; ;R20T>$u?21?972?3Q88O33O87O45<<55<<-h0@@@  HO_/]99///+2/]10467#"&>73#-xz<9$2.;D5 08>RNsL.%'F%KOQ$ MPQ%B)'4c@7)'')& 6(  P  R( &/ N ?33?32?3322/]3333/]9/33333910!######5754>326632&&#"!!3547&&#")ˣͤdd(MpH1W"&f?4[%)E%&7$o!-#5#55A>RjT#  4W@hfsN4W@B)'4a@6,))&6( PR( &/ N?32?33?3322/]3333/]9/33339910#####5754>3266323#&&#"3!3547&&#"Hͤdd(MpH1W"&f?0.&7$-#5#55A>RjT#  4W@hfsN4W@T 43@[+[6![ /  0_&_??32/]2210#"&&546632>7332>54.#"{@ȇɁ<<ˎ|B  !1D.**$O\]N##N\]P$ݩllkZV4?I(4cWJ[鉉ۙQQۉڗQQs +5@J&H-!H  )P$P??32/]3301#".54>32>533 4&#"XJogNIoaB&+*9K1&*ם6'ՑLLՉӑKBA8HW0Er[CBc}&6@[`p%Z(Z & _??33/3/]2]10>53#".5332>5/7 0RyW-cslc.6YACV14Ma6EiKfrEA~z3WV+0ZR'WJ!!!H!@#H!G&)G  &  P??2?33/3/3/]222++]01!'##".5332>53>53 2U\+?).bi=4e`:{2Ne8FnH+!C+!vR!.@ /]9/3/3/9/3301#'>54.#"56632!-<" q%;*$242)':)o! l[}  //014632#"&=-,?'-=<77<+7#s&(CR & %+5+5Fs&CR&%+5+5H!&HC&Դ %+5+5-!&C & %+5+5R{<u@   @ H ::'>.---@" H--3/'';;8* .. 0_*::8_"?32223?32/29//]2/+]39/93/+]310%32>5#"'6632#".'#"&&532&#"3273'80C*NB7)/"X0"N^!6.+*.5!^O#0X"/)8BM+D09%+Ye %% e%fgY+J*z@.  &!""""I_I ,*@ HI& ""O?33?3393/3/+]]q33]9/]3/]]3399]10!33>733>53#&&'ᠣ" Vm?O-BqXT jJ\U38AC!`g`!hT8>u!A@!Z [#!__` ??99//32/9/3220132#!!5!3!!32654.##^7<~†+wh.[]JNBqWap=`@Fa<X'!C@"G F# PPP ??3/39/2/9/3229/01!!2#!#5353!32>54.#/eqN+7]D&!A`@לM|X/)B01B(b-V@0"@ H*/!_!! +_*' _?32???329/32/29///]+3201"!!3267#"&&'##33>32&&DrU7 )-U}RBt9;@I,xx> LpT32.#"!!3267UrF KrS"D@9/243{ \$@]=Hs0/n:{J9r4   \T(% @  @@ H H    ` _ ?33?9/322/3/9/]93+]q9}3+]q3]910##3###!'&&'p s7'%3<VJVDniKK\=J @l G  G I    H   uFVf7% HP@ H R   ?33???9/+322/]/9/]]93]]]]]]]9}3+]]]910#####.'#3ch## "" JJ\Z]VW]["b@   w  YiVf  x  Zj2) `P@ H`  ?333?39/323+]322/22/]]]]/]9/]]]]]]]93]]]9}]9]]3]]]]01##!#3!3###3'&&'/'}-( &VVlJVJTaKK[{ J@z IfTI4DjYE3   d    PP P  ?333//?39/323322//]/]qq9/]33}/]9/]]]]]]]]]q]]210#######3!3.'#l::'\ JJ9ǐV`^##^`V#&S@,$ JZ(% (&#'a$ &_?22?9/332/33/333/39/]]329011>75!#.####"!q1LrVYqJ/qq"1F66F1"q%Ub:狋8bV;H_9H9_H?` J#&u@D$f  IzYK'&#'%   R$ &P?22?9/33233/3/]333/]39/]]]]]]]32]9013>75!#.####"! w*BdPPdB,wv$/B33B0$wT=eM1 dii1Lf>P6H+ +H6-\(+s@@)   *[+Z (/ 0 "`) + ?222?9/22333/]2/]33//]3/9/]32910!667##3!5!#.####"!`8 ɦ:AS4 77 .&'- 8BQl(Vዋ8aW;I_8H8_I?Z{ J(+@[)I       u h   *HI+H`  (I* I""P) +  ?222?39/3/2/]22/]3]3/]/]3/9/]]]]]]q3]]2910!667##3!5!#.####"!sI բ =N3 II )! !) L)}T2C J9^ii1Mg>P5G, ,G5-uF'v@DbGQQMYYl9[gg,[rrxNMMMCDD"22Z"Z]@VVNC>abl2a33QGDb'` _?2/3322?3339/922/2/3/3/33/]32/3/23/93301"32>32.#"#".54>32>54.##532>54.#"'667.'53>32&&#"G[4$=/AaTQ2V^ 5N51GJ\FhP1fkYQ&8hY\a2&D_97aVO&\;f762y3b6:CK*/ !10.S}U+3[}KX_1B )$ )H`732.#"#".54>32>54&##532>54&#"'667.'53>}.\*;bE&2G+/S=$7yJY/%@06mfZ$HN $+."\lw<_zE#UnQ}U-y;oV5yOS?>vF/0-y6e8=GSRuK> .D\;2N;) +B\>CuV1&#   +EW-3ZC(+E3^[ #B6MM&""983#`8"F9$9uPT{)+@%[+$[ `$$__??9//2201#"&&5466322>7!"!.{@ȇɁ<<ˎǃ@W{O'g(P{XUzQ**OxݩllkkGzz†GCuuCsX^+@H H PPP??9//2201#".54>32 ! !XJogNIogN e#'ՑLLՉӑKKdDF?!O@/7&8  0P` a ??3??32/3/]993]3]]92/01"#3667>32&&#5*$= -* 7>32&&#"#R f5AS<, ( J?FJC9]ge%@]uC(A.?!s&vR&m1%+55+55R!&v$&8$%+55+55F'Ho@DHHH HH/(7AA')))((( (6 6677 H/ID=6(#_???3?33//]]]9/]3/]]]3/99+]10#"&&54663232>54.#"%33>73#"&'532677j8m]]m89m^]l8y .#$. -#$. d j)>X>%0/1?ݶcbee貉ۙQQۉڗQQ8=?>@<aNxS+ WcP^>@dX>h>9>I>(>%>-I666XhxIY X,I,8,,,,I-fvI %>:P3,PP???3?99/]]/]]]]]9/]]]]3/]3]]99]]]01#".54>32324&#"%33>73#"&'532677s&Gf@=dH'(Ie<9dI+{;9u<9td j)>X>%001?'݂44ݨہ33ۨ΃8=?>@<aNxS+ WcT{)?^@5% ;+50[A [/;O8_8885_+@(P(((%_ ?32/]2?32/]2/]9933333301#"&'.54>766326632>54.'#"&'{0acJ6:Gg^,,^gG:6Jca0/K6C30E7M00M7F04D5J/ݒ~>55>~}>12;~le-%'-dnnc0(&.esX!6X@/$ 5*/,H8"H5_222/QP'''$*Q ?322/]?32/]2/]9933333301#"&'.54>76632663264&'#"&'X6bV <6:;Qc85aT :9*Qd9׿ 8309 c` 9037 'u\6,,6\ts[6( $[sG&&GJ#*(GR{='f@k56?6O6_666660<(F" F"" "P5#"'6632#".'#"&&532&#"32>7Pr^&+7vg6jp}Jwz<8$2.;D}$Z30C*NB7)/"X0"N^!6.+*.5!^O#0X"/)8BM+D0-)(ˁ$*$-5#ke$*$MsL-%&F9BYe !! e%fgY .Xs;Qb@)////@ H//)I! 7dFIXR]]UU==H @- HI X`VU``HCL<==HHHU.,,Q2/&!Q?3322?3332232/]3/323/9/2/+]2/2/3//3992/+]10"&'#".54>32&#"32>732654&#"'6632#".#"#5463235654.54632>[ XIDf_.("#(/^eDJ:23#^-FmK'3Z~iPn`.+7vh>llwJxyt$2.;D# #<כۉ>>۝Չ?$+$.4lq$*$MsL)3&&FR{ J@dH; ;;;HHHHHHHH5  !L   A5II8F<<>_8;;8   8HHF_+&0?33223?3222332239//]]933333339/]]q3/]2/10#'##'##'532>5#"'6632#".'#"&&532&#"3273R#11#11#O'80C*NB7)/"X0"N^!6.+*.5!^O#0X"/)8BM+D09%gggg+Ye %% e%fgY+ 8@//#0I888&8F8V888@fH8000000)0I0Y0i040)(8 )I  *: P  I#04   )?3?3322293?3/]]933333339/]]99]]q33+]q33]01#'##'##'533>733>53#&&'R#12"21#P:ᠣ" Vm?O-BqXT jgggg\J\U38AC!`g`!hT8>#4@Z! %[!_  _/?2?32/]3/39/01"32>7##"&&546632&&k{C;vv-(%LWlON?'QډۖN bln,* .%^5@G !HQ Q?2?32?/]3/39/01".54>32&&#"3267#qđSTzZB>:O1^W5U"?Ֆۉ>"#ic0h^o @K ?O_ 0 @ P         @H @ H/33/+33/3/33/8+3/9939939999/33/33/]322/]32/93901%'%7%7%BG㴁FGJ{J;{Z}9IĤ{@  //3/301#!#"&54>3!6632+9?,08( &.08*P9),338% -12@  /23/3/]/102>32#54&#"##5Nwp:m|:.*eyUf$+$el#4.$+$5#@@ H  /]/9/+014632&&D;/2%9FI!JCAEH%F>>FI!JCAEH%F>H?GJ  IAAEI#JC5M> "?LK> "?JO>"#?NO>"#?N9O> "?NO> "?NNA #BMNA"#BM)b +6?JU>@+$ 6JD5/ !%U>OOO:/]22//22/01%#66736673#.'5&&'5667''667&&'7.'7 -Od -T d59:@>9L.u8?A<6o,?784F/q3@)m7Hw5=%$ GP2;&$! J/w6Wr#0u6?>: T d -R gi4;$&#G7;J'J5o-?:5/H5o/?:6/Hh1\@21'&& 3@  1',! _?3/?3?3322/]229/3/2210!46767##3333##".'332>7׮B٦a0]dgW*3K5,I6#9ALMF>CC JLhMwP)'OwP9H),G5%H@` H%5 H H* :   %FF F '@F %   P?3/?3?3322/^]229/3/2210]]]]]++]++!#333##".'332>7^ : {{%0]dgW*3K5,I6#jJdP}NvQ)'PwP9I)+H5/F!A@![#Z !__` ??99//32/3229/0132#!#5353!!32654.##7<~†N.[]NBqWap=`@Fa<=D!I@'F#GPO/P??99//]32/3229/]01!!!2#!#5353!2>54.#/eq98]C&!A`@!M|X/)B01B(F#l@;[% Z ` #` ??9/32/33/99399/]22/3]3/9939901'###!23267'76654&##F3R:hj},b9~<$ ;^mv<; =sfU N 9mgL"y]D^#9x@C2!3 547H;433* G   $P 2!3 3544/P ?22/32/99399???3/^]22222/3q3/9939901".'##33>32'"3267'7654&=bO= _Tkkt DDOmC DoT"jlo\+: "7+6#=-HԌEN .^_)ek7Lhd 1@Z  _ _ ??9/32/329/01!!!##53dP}J 1@  G O P??9/32/329/01!#53!!!!#q&8@"[( Z _  _??9//32/]2/01"#!!6632#".'53 4.%<H-WRv1ND< ?HwC{ \O  y~@ XJ)5@H%+FQ P Q/??9//]2/01".'532>54&#"#!!6632)A7104=&FnM( S# "N*}ДRL   0ci/J>۝և?r@@ /?iB   _??33/?339333/3/33]]]]22/]32/9/]332210!###3333#1`HAw<<<Jb@5 I I    P/33?333/9333/22/]]]3]29/3322310!##3333#àZJJt--5e}B/&2, %+55B^&/)%+55x@Mu6V Z`p W @ %   (U6$ ?3/3?393/]]]]]2]3/]]]]3/]22]]]10%3###33+76˦<BJM@, F  G   P ?3/3?393/^]22/]22/310!#333#ݴ-J_} Z@I & `p@`      ?2?99//93299?993/8]]]33/3/]9/]]33222201!###3733wwBdbceJ@K I  {`G`   ?0?3?99//]]9399399/]2/8]]]33/39/]]32210'#3733##}6}J8 ';w@J Z `pHX@`_  ?2?39/]932333/8]]]33/3]/]322999/10!###53533#73>yr`{3;z@IveVE G  H`Yv   O ??9/32?393]3]3/8]+33/3/]3229/99]]]]10353!!373###;{ V^uߘ%RZ_@. H Z  X  _?2?3933]]]3/833/83/9/299+q01!##!5!73hudN+J \@6(8 gVGG  P ?3?393/8/9/]2]]]223]3/83]]013##!5!`LJ-N@/ Z    Z_  ?3/3?39//]]2/^]q201%3##!#3!3 VhJU@5G F   O o  G P  P?3/3?39//^]2/]]]q201!!#3!33#F𶠰J9P} K@/ Z?O@H  Z_ ?2?39//^]q2/]+]210!#!#3!!!aVhJ ;@" GO  G P ?2?39//^]2/]201!#!#3!!#ǷFJ9ǚj$D@&"@ H&!0""! _#!_ ??3?9//]+9/2/106632#"&'532654&#"#!#!1b[+3^T6U+&I/sdgp3a\O  J)N@,I` I%%%+IQ PQ/22?3?9//]]9/]3/210"&'532>54.#"#!#!66329<\(*R82F,/L9-F0N]40Wy /bkkf1-PJ'9ݥׅ<TASk@=3OOL[8 888#""B[ U+[/G`p====&_O30_ _ ?332222?/]/]3/3/2/39/q9333103267#"&'#"&&546632&&#"3267.54>324.#">{6G'7#;D E>)U;~ĆFE2]&8="Q|T*.X~P%8&)PsJJsP* , , 80.js%  ,4_^ D㞝݋?6uV|<7vVY-4`TO!e|ss^P@9H4CWH 9Y9H999$##HCNNCCR,H NP>>>>>@ H>>1$'P# 1PHJP?322?329/+]q/]]2/2/39/]]q]9333106654.#""&'#".54>32&&#"3227.54>323267!+9B ,>?RA|9&S/oGCv9O -8*QuK$'MsL "4$0TrAAnR.0@%'59?4[M@-k3T: zh%LхԗS  8lgai8'PZg=^Y+'VcFwcO B&&v$ %+55B%^&F75"%+55fd J@, ZcP_ _?3/?2/]_]_]_]]]/01!#!5!!3#û^_yRJ 6@ G GPP P?3/?2/]/10!!5!!3# oo}!<RyJc@>7G&H) G{cP ??3933/]_]]_]]]9/]2]]]2]]10#33>73H  HJ Z]VV]Z o!ʹ@$ H&6hxZH)9' H@THY H{P Z    c  P   _ ?3?9/32333/]_]_]_]]]32/9/]]]2+]]2+01+]]]]]]]]+3!!#!5!53d}&ڻ'=X3RyJ@ v  @G H y HGcPO ??39/332/]_]_]_]]]3/39/]2+]]2+]]01!!#!5!33>73 H  HcJ Z]VV]Z o5@GWH h x    @ H  &  @@ H&  H  )   H )   _?3/3?39333/33]+3/3]+/3]+3/3]+933]3]01%3###333GZKNZ{/L`J@$ H   H H@ HI  H  F@3H   FP   ?3?3/93333/3/333/]33/3933301]+]]+]+_]+]+]+]333###w"#u-P3fg}L9@0Z Z _ _  ?3/2?32///]01%!33#!#5!#J?@$ G F@ G P P?3/2?3//]]/]01#!#5!#!33y}P=@"Z  Z_  _?3/?39/32/]2/]01%3###"&5332673 QT2R=MT?&G9W:JA@%GF/G  P P?3/?39/32/]/]]201!#".53326733#F'NRZ3RX/idNI% /TsDfXV-+P}FQ@& ZZ   _    ??39/333/32/22/]29/3210!###"&533336673FAm5!2R= 3q?V$ 'G9W:X $%J_@7I@Pp G GP  ?3/]33??39/333/3/]29/]3201333673##5#".5Nid}hi5e7}/RX/JfXV-F%3/TsDF+@ Z Z_ ??39/32/]20136632#4&#"#n_ĻduVk--/tt((95J;@$G G   P   ?2?9/32/^]2]01!4&#"#3>32idZT,W\e9RX/XV:9+J/ /SsDZ{-8T@/4[:#&g&w&&3[("`3"(( _._??329/32/3]22]]22/012!32>7#".'&&5467333>"!4.}]$`$KxZ/OGA!!AL\<~v< f !0WeZz†G  ]rz*B>#a[A~xuCX^,5^@81H((7 fvI0H/ @HP0 "-P'"Q(?22?9/32/+3]22]]22/01".'&&5467333>32!32>7"!4.lK 02KuZcn;L3WPM'(MQW`s 8XFǁor'?:"/1lu=Gnq ۜDqP,{0;g@97[=&)g)w))"6[,%"`6%++ _1_??33/329/32/3]22]]22/9/012!32>7#.'&&5467333>"!4.}]$`$KxZ/OGA!;CN1ZV. f !0WeZz†G  }orz*B>#a[A~xuCX^-6q@BF2H8"%f%v%%%I1H)/!@HP1!(( Q.P??33/329/32/+3]22]]22/9/102!32>7#.'&&5467333>"!4.cn;L3WPM'BNTc9 02KuVs 8X^Gnq  "uToor'?:"/1lu=DqP,,i&6R@ & %+5+5&6@ &%+5+5X%X@1  7 g w ![' Z a _?3/32?39/32/]3/223/301]]]"#3332#".'53 4.9R#5җSRw1ND<?~IvEs}<XQ휪O  w8 XJ"U@0"!!""H$ 6FF  N!Q ?3/32?39/32/]3/2]23/3]801#"&'532654.#"#33jdOJfLi/,iB4aUSKb=ϕՈ@%jb.LJ#K@* """""[Z_"a_?3/?32?/3/9/]9901%3####"&'532>7667!Ð٦ !"5OnN#J; 0@* !'+AqQgo; ?bu69:J?@! IGGPO P?3/??/3/9/9901%3##!#"&'532667!{{@_V17YE3}iuoFN@. ZZ  0_/ _/22??39/^]]/]q3/2201".'53265!#3!3X-G=68n?IJ  VhݪO -JH@* GG PQ/22??39//^]3/2]201"&'532>5!#3!3Ki/+jB;[=߶!?o %-aiJ9Ո@T@1 Z  0  [ Z_  /     ?3/3?39/^]]/32/]q201%3##!#3!3Ð٦ VhJI@)G G  G P  P?3/3?39//^]2/3201!!#3!33#B춶{{J9P}F=@ Z Z  _ _ ?3?3/9/32/]22/01%#"&5332>73##9e^\12R=-VY`8" G9W: J'%JK@-FG G   P P ?3?3/9/]/^]]22/01#".5332673##3o,W\e9RX/idYT0 /TsDfXV:9q@< l|[:J)      @XHc s T 5 E  &        _   ?3/3_]2_]?333?/]222]/]q2]2293301]]]]+]]]q]]]]]]]]]qq!##3333##46767#򚹰lJI?9Xw4=GIsJ$@$$$$$$i$Z$H$)$9$$ $UeF7&   I$$I{$$P??3/3_]3?333/]32/]293301_]]]]]]]]]]]]]]]]]]]]]]]3###.'#3>7{{{ǃ  JP}EJDy>IKJp"QK>=JN",!i&$6R&%+5+5&D6@ $&%'"%+5+5!+&$jR&%+55+55&Dj @ &&-(,%+55+55-^#i&(6R& %+5+5H&H6@ & %+5+5T{)?@$$[+%[ 0/%_ __ ?32?9//]3/]201"5>32#"&&55!.2>7!#oO&P]oCٓKIЇz7b7d0M}Z6c#Kw),  l髮hgRzGfA~xuDH^'I@+"H)#H#PP Q?22?9//^]q3/]q2102#".55!&&#"5>267!-wƏOHolx@8aYT,+SW`d Bg^J҇֔OGnq  %DqP,T{+&jR@ .&04%+55+55H&j@ ,&.2%+55+55+&jR& %+55+55&j&%+55+55/+&jR(&*. %+55+55&j%&'+%+55+55B K@)WF[ [ "  _a` ?32?9/2/]3/9/3/01]]#"&'532654&##5!5!JsGC͉nU+`cc.Ϻ{=;6df`t@"-$ Ѧ;J$F@& J$$J F&!  N!P"P ?32?9/2/]39/]3/01#"&'732>54.##5!5!HhNL{z>!Tbo;M\3;l[w=@rlnK$"0YNV{P%}횅F&MR@ &%+5+5-o&M@ & %+5+5F+&jR@ &%+55+55-&j@ & %+55+55T{+&2jR@ &%+55+55sX&Rj& %+55+55T{~sX^T{+&~jR@ .&04 %+55+55sX&j#&%) %+55+55DL+&jR&"%+55+55%&j&Ǵ! %+55+55&MR@ &%+5+5Ryo&\M@ & %+5+5+&jR&"%+55+55Ry&\j@ &! %+55+55s&SRR@ &R#%+55+55Ry!&\S^@ &^" %+55+55F+&jR@ &%+55+55%&j@ &%+55+55+d 8@# Z 0_ _?3/?/]q2/10!3##d#J .@ FGPP?3/?/]2/01!!!3##ˠJ}`m+&jR@ & %+55+55qZ&j@ &%+55+55mdH@& Z___` /??9/32//322/9/]01!!!3#"&'532655##53dPn}'?:"/1#ل 11mJH@% I GP O  P Q??9/32/32/322//9/01!#53!!!!3#"&'532655#n~&?;".2ل 115m&; %+]55`mwJ&[^P 6^ %++]555 @ H  &   H  ) @ H& H )   @ @ _  ?3?39/3322/833/83/8399//3/83933333]+]+]+]+01##!5!33!KNL{`/`jJ·@$ H   H H@ HI  H  F@7H       O ?3?39/3322/]33/3/]33/393333399//01]+]]+]+_]+]+]+]!33!!##!)"#-rs{f1L*@Z [_` ??9//]2014>333!".#"337Ϙ~=][.WqBhJ=p_33332>5#"32>5CmO\m"#za1hpRAD,XPB^<N^0<# PTe7WWO]gr<hrg3S<CkM#@!(F%22F?;F8P&-P ?322?9/?2/]9/3901%2653#"&'##"4>323&'&&53!2>754.#"XB;"DeCn"+54.#"'>323#Ż8g]\a2%D_:nK\&btHmp93[}KW^2DcA 'He=6S:C6}6)6aOIxX9  9YwHP\^/N@)$FG++/F,10P$P0--+P?3/?39/]923/3/32/3/201!4.##532>54&#"'66323#>fK>dH'/RPP-?]scq=3G+6T:+/I1'@-MM '%$HjF5Q;) ,B[>}0S@. 0 00 #&&2 _+$$_` ?32/?9/?/]99//]9901##"'532>7667!32653#".5-  0N>3';BD9*NmDCnP,tQgn: <]q6;9сww-ib.,`lJ)I@(IF))) F+ P O$ ?3323?9//]99//]10##"&'532667!32653#".5- &0:E)1%=0% 7HA:$InJGqO*ՑY'uo=}{5ib.%WhyH@*8   G__???39/3/]]2]]9/30132653#".5!#3!3>EE:%JoKKnH$ꦦww-ib.,`l-Vh{{JP@1IGW IHXIP P?2?9/?/]2]]9/]301!332653#".55!#"7H?:#FiGClL(ޢJ9=5ib.%WhJuo(I@*(( 'Z*[  _(_/_@H"_??39/+]2/]2/39/01!#"&&546632&&#"32>5!4oyҍHP؈sKH:^Vd7-YWRmBIii,*"2QډٚU7osq\^(A@#(( ?'G*H  N(P"N??39/2/]3/]39/01!#".54>32&&#"32>5!{2qȄAGЊoM=<[[\/%S^QnBJDȉGOӄҔP%'-:mb]p>-PqC@@&Z  0Z0__??9/2/]]/]]0132653#".5!5!!qR\\R2]QP_5^_ww-ib.']s)oJ<@# GG PP?2?9//]]/]015!!32653#".5)51P;i_/_`[f7AbA 5ib..bi5D;I@'1'' =Z [116a&#`', ` ?32?329/]9/]3/22/9/014>32.#"33#"3267#".54>75.:H+) O&##$7P ,P N ?2?]2?9/32?/]3/]2/2222229/01!5!##".54>323&'&&55!5!533##%2>754.#"/h=Nb>aq>>qa=bO= {OmC DoTK"=.F̆΋G,: !7+,YZ'af4! 0@_/22/3/]3/3/01#.'53#.'53d"RM@+.0e"RM?+.0SXQ"QQLSXQ"QQLD@#?Oo @ /22/33/39333/]3/01#&&'#56673'>73#+d3l46j3e3t00t3N -j7e"T00T";EE;,.4!3232673-3t00t3e3j64l3d$HE?(*\0B)&JE>(*\0B;EE;"T00T"Z"*20Q;!"+10Q;!9 -@  @  /22//3/]01>73#%#"&'332>7% -j8dn,LnGm0D+$B3",.5!7e8j-!,LnGm0D+$B3",g<!5.,7'#'6654&#"56632,LnGm0D+$B3"+#-V 8A9+' &cd7'".#"#>3232673,LnGm0C,$A3"/$HE?(*\0B)&JE>(*\0B \v \ T.Digitized data copyright 2007, Google Corporation.Droid Sans MonoRegularAscender - Droid Sans MonoVersion 1.00DroidSansMonoDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0Digitized data copyright 2007, Google Corporation.Droid Sans MonoRegularAscender - Droid Sans MonoVersion 1.00 build 112DroidSansMonoDroid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0ff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~.nulluni00AD overscoreperiodcenteredAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatdcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdot Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonek IdotaccentIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacute Scommaaccent scommaaccentmacrontonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352 estimated oneeighth threeeighths fiveeighths seveneighthsDeltauniFB01uniFB02 cyrillicbrevedotlessjcaroncommaaccent commaaccentcommaaccentrotate zerosuperior foursuperior fivesuperior sixsuperior sevensuperior eightsuperior ninesuperioruni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni1F4Duni02F3 dasiaoxiauniFB03uniFB04OhornohornUhornuhornuni0300uni0301uni0303hookdotbelowuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0483uni0484uni0485uni0486uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABuni030Fcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCcyrillicbighookLCfritzing-app-0.9.3b/resources/fonts/NOTICE000066400000000000000000000247071272363601300203630ustar00rootroot00000000000000 Copyright (c) 2005-2008, The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 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. 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 fritzing-app-0.9.3b/resources/fonts/OCRA.ttf000066400000000000000000000355601272363601300207210ustar00rootroot00000000000000pFFTMe;TGDEF9 GPOS;$0GSUBꡗ9XOS/2nxVcmap(gasp9glyfY#W\.head 6hhea*4$hmtx$loca{0pmaxp]X namel.4post 80q\Kg_< ̊3S̊3SW,|RR,Z|uZ@1 #*PfEd@ % 8ZRoqqqqqqqqqqqqqqqqqqqqqqnqqqqqqqqqqqqqqqqqqqqqqqpqqqpqqqqqqqqqiqqqqqqqqqfqqqq7qqWqqqqqqqqmqqqqq7]}$B%% _$@%%{*r  !"#$%&'()*+,-./0123456789:;<=>?@BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`mnpqseorfAa0^: T~<j0\>fHz8t*Dp V  > ~  0 N z ~  t  R,pb>xN F|Rtb&P^ %546;2+"&46;2#"&'1113**oe 475!#"&5465'#"&'o * )r*$ %  J2qc =A46;5#"&46;54623546232+32+"&=#"&=#"&735#q111*a*1111*a*1aa\o*xxxx*owwwwGoqc 16463!5!"&=46;5463232#!!2+#"&=#"q[[*Dxx*Dwwqc /747632#"&546;2+"&546;2+"&q  scc)cc  UUTTqc.6A754?'&=46;27632#"/+"&7;7'754&+"q\` Y>`>Y}k3!47Jb>YeO8kNUo`EOfE>YY>Ekr,  8;? Y>0rB*[^.T 46;2#"&ccT  54?632#"/&+u uu u+&E8""8 74?6=4/&54632#"& uu u++u3""8EE8qUc374?'&54632546327632#"/#"&=#"&qss  ss bb  bb qc46;5463232+#"&=#"qq**d46;2"&=#"**q)c5463!2#!"&qs\**9%546;2+"&ccUUqc 747632#"&qs3  Yqc 7463!2#!"&7!!q<)((==()U&* mqc -87546;22654&+"#"&5476;2#"'#"&726=#"qX=d(.- 67DIiY=9*,8=Xe(1>X -#$12jH<=Y''X>nd 7>2#"&/##"&73n"''R3Kqc "3!2#'326=4&+5326=4&+q(RxFFxR)<<))<<) xR%Y>>Y%Rxe=(%);2+";2+"&'&'&qeK- g g ,IJ52$/<0$>3qc (746;#"&546;2+"&732?654/&+q11,IfeK-$ gg $3B0$4f4$/G"qc 3!2#!32+!2#qZ *qc 7!2#!32+"&q*3*qc *74?6;2+";26=#"&46;+"&q*-KcZ>?XE6:"g*>ZYqc 7462!462"&5!"&q*(***3!Y!qc 746;#"&5463!2+32#!"&qs3B1 75462;265462+"&*_*Z>_>ZC>YYqc 7462632 #"'"&q*9`*3   qc 3462!2#q*Zqc 7373"&5#"&='"&q~{z*ba*3ٯ'Nqc 73462#"&q**3(&Xqc )4?>32#"&/&632?654/&#"qk?&'?kk?'&?ke lkl l52))4f4)).W<"qc 7!2+"&326=4&+qZ>ZZ>*e3Y>>Zpqc !@74?63232+'#"&7;7'&54632>54&+qF /2J#,A%K"-2Hel%   "{^=I2)%" $=^@I2\^  $  0qc #7!2+#"'#"&326=4&+qZ>Z[=:E*e3Z>4>Z 4qc ,7462;25&=46;2"&54&+"+"&q* C.7Q* tA/7P  &-BP7  "(/CPqc 5!"&=##"&5#"&q*ba*"t"qc 7462;265462+"&q**Z>>YCC>YYqc 54625462#"'q** "# %z qc +74623>7463237462+"'+"'q*))*9b az`0& C &0Ea##qc #747&54632632#"' #"&q3 ::   qc 546275462#"&5q**sgDDgBpc 7!"&5463!!2#!ph5A@g@qc 7463!2+32#!"&73#qsd003GBqc 4632#"'&qsY  qc 746;#"&5463!2#!"&%3#qs]003BYGBpcb7632#"/#"&5p  qc 463!2#!"&qs21 46;2#"'&c  qc8&47546;24.+"&46;2#"&5+"&7;754&+"qX=  =W J =Xek8>X! *U<+X>?,qc )C74626;2+".'"&7;2>76=4.'&+"q*DCK 5)8K6*eV K K V3@ .D$J  J qc8&7547>;2+";2+"/&q4 3!: VV 9)V4xE- 1* J xI $J-qc -H75467676;25462"&=+".'&7;2?6=4/&+"q*')8K**6K  4e KVV K0; $Y +G   J xJ qc84A754>76;2#!3!2#!".'&7!54&+"q )8}   5   4e(<}#<$  .D|    +J6 I $46;546;2+"32+"&5#"1hJyy -cc*1*"Jh- "*`q,c8BY754>76;25462+"&46;2>76=+"./&7;2?6=4/&+"q )8K*)86K4d;K VV K  $$*-E6 JNJ qb 746276;2#"&5.+"&q*`&+9=W  G*3=U<si16*746;#"&46;232#!"&546;2+"&ccbc223n*`**,16/4632;265#"&46;2+"&'546;2+"& bY>b8V11M*>XJ5/**qc 7462%632#"'%"&q*0*3q  *1 746;#"&546;232#!"&bbb3Bij8.746326327632#"&'&+#"&54+"&i)-=$!*/HDK*3#0B/o:Aqb8 746276;2#"&5.+"&q*`&+9=W  G*3"=U<siqc8&@754>76;2+".'&7;2>76=4&+"q )8} 5)8} 4e }<}#<$ .D$+G   6 q,cM,J462>;2+".'"&;2?6=4/&+"q*  70  5V)97 *e   WW Z )&&D.J$   J  K [ q,c8-H75467676;25462"&=+".'&7;2?6=4/&+"q*')8K**6K  4e KVV K0; $Y +G   J xJ qc8746276;2"&=4&+"&q*w c=Y*Q*3;fY=qb8474632;2654'%.5476;2#"&+"+"'&q& "*%&5K0 - &5Z?I>] " xC'3$&? + xM.?Y5qc*46;546232+;2>32+"&5#"q1*d  U7d=X1*ww*4KY==qc8 4632;7462"&=+"&'q G**a&+9U;qc854625462+"'&q** !?! UJSJU ~fm8+46327546327>2+"/+"&/f BB ( 317814lrrrrl#ʪ__qc8#74?'&546327632#"/#"&q  3 q,c8#46;7#"'&=46235462+"qgC" * * "*S UJ,JU qc87547!"&463!2!2#!"&q`u<s3* R**qc *46;26=46;2+32+"&=4&+"qa=(''(=aq*)<9,,9<)7 %3#"&46327e3!!qc *746;547&=#"&546;2;2+"+"&q'')<bb<)39,,9<)*)<qc &5463226=462#"'&=4&""&q]<:X(*\=<+,(*oAVX?4ooAU+.=4oW|%!W%>7546;2+"&3qc 4762"&=47%5'#"&q*$c*1 UU&* qc .746;7#"&46;7>;2+"32+32#!"&q>EC P4;Fs3*-@*qc )46;5&5463263232+#"&=#"q*G   * 46;2"&5465 c"3(!d546;2+"&=#"&2c*qTc 463!2#!"qsq**qc 34623!"&=#q*a,*b1Yqc 7!462"&5!"&q***3!Y!qc 462!462##"&5q*(*T!z!qc "%1747632#"/##"&5462"&3'75462"&q "# # %% #**K**3    __ gUUtUUme #'7.=46;2#"&/##"&35#3'#m#0<)(=0#'( F48$E)<<)E$84CEhqc (+7476;2+32+32+"&=##"&735qbbbbe2 #@3 *w!qc 174632462#"'"&546;>32+%#"&q6**]3)XK+%+%qc &2BN547676;2+".'5'&5462"&;2?5'&+"5462"&qY#D$R5#%+! Q**dv vv  v(**2 ~(5 *q 2 L9&  $qUU 3 ;UUqc 1@P74?.54?>32>32#"&'#"&'&#"32?654.'qB!k?&*A B#k?')A e " l W"l  3 q ?#=52).#' q @$=34).#'h  ,CD"  qc '37462;265462+"&5462"&75462"&q**Z>?Xc****hh>ZY,UUUU7R37eeI&V$!p a'    B,       .v (7   Matthew Skala (2011-12); based on code by Richard B. Wales (1988-89) and Tor LillqvistMatthew Skala (2011-12); based on code by Richard B. Wales (1988-89) and Tor LillqvistOCRAOCRARegularRegularFontForge 2.0 : OCR A : 27-9-2012FontForge 2.0 : OCR A : 27-9-2012OCR AOCR AVersion 2 Version 2 OCRAOCRAAlternate hyphenAlternate hyphenAlternate commaAlternate commaAlternate apostropheAlternate apostropheAlternate question markAlternate question markAlternate periodAlternate periodu  !"#$%&'()*+,-./0123456789:;<=>?@BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`  bcfgh uni25A0 period.alt question.altquotesingle.alt comma.alt hyphen.altuni2440uni2441uni2442uni2502t DDFLTlatn$aalt&ss01,ss022ss038ss04>ss05D&.60dhlpt."( ghic"d "RB"] YY ,.DFLTlatnmŮ3P̊3Sfritzing-app-0.9.3b/resources/images/000077500000000000000000000000001272363601300175615ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/images/AboutBoxLogoShield.png000066400000000000000000000041411272363601300237640ustar00rootroot00000000000000PNG  IHDRd g2PLTE2"?0eYXKL>u뙑范rgKV b/ xM@σ[Pj`wOD]VSndzRGbYph]]\wZRa=2ǵźҁy8+..-2T]n|nBO/71&*:'B/*BZИPek}]qǡ׼ĸxϮ5Nq#O#*"($/ ,*0 1 2!) JIKbKGDo IDATxic6!Nfn4k-}eMܤIv;_$trcχz!^h 0L,XF E,űy˻r:N%OߦD!4.<,6z]:'%`L:`GwY_DJV&rfE\SKEP6$ceG"W+{7a?-eTV-8>F4d[PDyL*{Ϟh]@$K' 5%FYq~5jHN|TPm٥3?㏑fDhbW|؋-*Se`7~ ~&^C1b2 0N9d$=,O-,^cG+PI t˧gC3{?yp>U>aD!.1t|`8q3ƘOX1.+RBvf-f~nAE*y  k Ur:x#C~<9"Õ)ewuT6\2Ib'TXAS:ي w{gg|gȋQ ş~ŏ5)uB?d:lR:,߿7b2d^DbOUb>@:Ij߂V($RJFW Hn( W[ɭ%/o6LZ+$A\4Bj. ]6($_$gHn(&L\.}V%Hn/yIk6[,y6KljL|6tw{$HY_$H~_?Ͽ~ {οl @r=?D''IENDB`fritzing-app-0.9.3b/resources/images/aboutbox_CC.png000066400000000000000000000011301272363601300224520ustar00rootroot00000000000000PNG  IHDR/,_bKGD̿ pHYs B4IDAT8˵y0FOxo@:t؀v x 0M w!Iҹ_=$-<M&g\e=|NjQ9x'JZlhg\SRpb@.i4ۘ<x6*p7rphH$H#+h,4Q]Y83yH$5MlG;Wl'(pt!а'Yg?iه ͘TPe=j >7 WΕ5R|``i(! ӕY~ۊ&GW0<0aDg:c)yeJ':0wfr}]8L+ll4_<:gj7=OnM7n%x0㫓]htܩǷёO'ypc4y3h[.Q5=M\P3ʕ63ބ<<| JFdIENDB`fritzing-app-0.9.3b/resources/images/aboutbox_FHP.png000066400000000000000000000006311272363601300226070ustar00rootroot00000000000000PNG  IHDR)!bKGD̿ pHYs B4=IDAT8=K\QHD$D81 &$W+{BKI )D.— a7zN2BHb.N5 ;N#nl欚d?A|gACv?Ձm̧ 1Y#c W7++'{eoHt|#bq^YD4nX`#^w>JՇ {?8?f{8*JمO~&xhKlD-GqTIENDB`fritzing-app-0.9.3b/resources/images/aboutbox_scrollfade.png000066400000000000000000000011271272363601300243110ustar00rootroot00000000000000PNG  IHDR?;H6PLTE333444ŗtRNS,bKGDHIDATxv0 ЄaHv v;{sY4{ݷzroOҮK,pA3?R|OOC9l GҐ MC{ILW㻔9Dz,~6WDȻS\m-RncǍq ({r5e51g[M.GL,͍yϗ4K.!{q,Op,~6WDȻS\m-RncǍq ({r5e51g[M.GL,͍yϗ4K.!{q,Op4˘M_筥n1!lIENDB`fritzing-app-0.9.3b/resources/images/breadboard_icon.png000066400000000000000000000015001272363601300233600ustar00rootroot00000000000000PNG  IHDR226@:bKGD̿ pHYs  IDATXOHQﹻ(HF]:TIt.(P]̃AXz-(P!CA tMLblrTj/F| w>(:uڠ~!fCGB'ڛ#IENDB`fritzing-app-0.9.3b/resources/images/cursor/000077500000000000000000000000001272363601300210765ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/images/cursor/bendleg.bmp000066400000000000000000000003001272363601300231670ustar00rootroot00000000000000BM>(   Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/bendleg_mask.bmp000066400000000000000000000003001272363601300242020ustar00rootroot00000000000000BM>(   ϟfritzing-app-0.9.3b/resources/images/cursor/bendpoint.bmp000066400000000000000000000002761272363601300235650ustar00rootroot00000000000000BM>(   fritzing-app-0.9.3b/resources/images/cursor/bendpoint_mask.bmp000066400000000000000000000002761272363601300246000ustar00rootroot00000000000000BM>(   pxp0x??fritzing-app-0.9.3b/resources/images/cursor/curve.bmp000066400000000000000000000003001272363601300227130ustar00rootroot00000000000000BM>(   ##Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/curve_mask.bmp000066400000000000000000000003001272363601300237260ustar00rootroot00000000000000BM>(   ?πfritzing-app-0.9.3b/resources/images/cursor/magic_wand.bmp000066400000000000000000000003001272363601300236600ustar00rootroot00000000000000BM>(   Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/magic_wand_mask.bmp000066400000000000000000000003001272363601300246730ustar00rootroot00000000000000BM>(   πfritzing-app-0.9.3b/resources/images/cursor/make_wire.bmp000066400000000000000000000003001272363601300235320ustar00rootroot00000000000000BM>(   `   Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/make_wire_mask.bmp000066400000000000000000000003001272363601300245450ustar00rootroot00000000000000BM>(   `{??Ϗfritzing-app-0.9.3b/resources/images/cursor/new_bendpoint.bmp000066400000000000000000000003001272363601300244220ustar00rootroot00000000000000BM>(   @ @Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/new_bendpoint_mask.bmp000066400000000000000000000003001272363601300254350ustar00rootroot00000000000000BM>(   @8pp8χfritzing-app-0.9.3b/resources/images/cursor/part_move.bmp000066400000000000000000000003001272363601300235630ustar00rootroot00000000000000BM>(   Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/part_move_mask.bmp000066400000000000000000000003001272363601300245760ustar00rootroot00000000000000BM>(   πfritzing-app-0.9.3b/resources/images/cursor/rotate.bmp000066400000000000000000000003001272363601300230650ustar00rootroot00000000000000BM>(   @@@@1~0fritzing-app-0.9.3b/resources/images/cursor/rotate_mask.bmp000066400000000000000000000003001272363601300241000ustar00rootroot00000000000000BM>(   @9~8fritzing-app-0.9.3b/resources/images/cursor/rubberband_move.bmp000066400000000000000000000003001272363601300247230ustar00rootroot00000000000000BM>(   $$Fl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/rubberband_move_mask.bmp000066400000000000000000000003001272363601300257360ustar00rootroot00000000000000BM>(   ..τfritzing-app-0.9.3b/resources/images/cursor/scale.bmp000066400000000000000000000002761272363601300226720ustar00rootroot00000000000000BM>(   ??fritzing-app-0.9.3b/resources/images/cursor/scale_mask.bmp000066400000000000000000000003001272363601300236710ustar00rootroot00000000000000BM>(   ???????πfritzing-app-0.9.3b/resources/images/cursor/spot_face_cutter.bmp000066400000000000000000000003001272363601300251200ustar00rootroot00000000000000BM>(   xFl|~|xp`@fritzing-app-0.9.3b/resources/images/cursor/spot_face_cutter_mask.bmp000066400000000000000000000003001272363601300261330ustar00rootroot00000000000000BM>(   πfritzing-app-0.9.3b/resources/images/dot.png000066400000000000000000000003201272363601300210500ustar00rootroot00000000000000PNG  IHDR Tg$PLTEƌƌƌƌƌ򽆙}`fXIYOD333?{tRNS 0k)bKGD IIDAT(c`b@4Xb$ Vўj$jմ $ӪU# vZ" C1pӇMIENDB`fritzing-app-0.9.3b/resources/images/fritzing_icon.png000066400000000000000000000024541272363601300231400ustar00rootroot00000000000000PNG  IHDRkXTfPLTE2"XKeYL>范?0u뙑rgĿϏj`KV ypﰗm>0[Pb/ xM@ǵ !@bKGDޕztIDATx[{0@j'nHls^a;IL~iL:%I|^/a~euW8٢럯+PV ʕWUH_ Q?w2鰇tnnPRu ]Auԁ\:x5_<$A/ ˿N F*A WD62 ?& | ?@<'Ac" kiO&U{yTth\6inJV:h_:qi6p<ZJS4u*z]G^(tJDžaľʅ @X&~\| ;e72H,Mr%BNx 7T1oB^P "8GՀHҋ@5 ? K/F=ha#K/w^8 xS?" ~.0 p!ab? Ps= OWY3rk5$I^J-TեB @ @ @ @ @@eVwbӲy<\ܙvpDgg'qsbӝ-Tk(JkXOIvqz!wuK+c$}ߒ3Qh.x_)lbqv{oEYWhJb_8<mZ.R8=?9^l[NNW q>"xu]MO|nUV/ ޯ~Prhjw48'if;[k`ire,\4 ¹At @:i 4@H HAt @:i 4@H >:IENDB`fritzing-app-0.9.3b/resources/images/fritzing_logo_background.png000066400000000000000000000043001272363601300253370ustar00rootroot00000000000000PNG  IHDR,՘[bKGD̿yIDAThypol-"QxÈVXtFkuRBUqjhGΠ2t +xET w;l|Uj:1=fxގqrkr@A*iQfs}.P~iZ-^%CGVW&K{60Ii^}>I[j~أ}q&iWB8NZP@ 3 >Tk R20Wgu~ `(煨v1ŴljTCA.^o-"$>Bm8TN5v\okUo6z4Z}>5A8RҎ&e %>4MD СIDdtn-"ֈxţXoN2y;Qtk#fg<޷ܡlvqպ5fNIq <ϐ4F&Qc|Ur]fGK+iCvݭ{s,xVMhli늭"F2 q{Ux7Gs/EKؐ)iQѢ\ufk--e xHk4JԙwHR"7Z񉛽*4\j{M-&n[[+]ި:>&M: _ \䡼:ZeaoFy޶n90VYYך^lQQ<,fGJ ,,܅B2biU̥))uX]A 85(zp)r'jUjY^tvsaI՞S9`aDzI֊hҧd)aAA˞ېNqC%fkP.iܺ㥲O"Xr)r -C%#Lui#7oc_t岼CˤA(ѓY:,G4p0M-l "|s5 }6 LJ,]B0[@ÓT+#nE얡tD-B3Nv&)lVv&9QbBS:Gh "γ:{ Įޑ1ݱ⹵DK ]Eu Y^Fc-Xi7'!j%6)k\cC'6_EE-T:ItSoi_i#!#^FIcC)-*P'l-PZ'=G4hU}Jv|_9/j[y-TF;fe YZs-RD>pǍMPs7hg{&\H2na^ﭙ&yWrfv4ӾRBh^9g/)=ש*gy߱V**eFgN`W(X vrqW%{m+kg[9Gc R`Erg8@R_or? 2Ʃc I&0ؗ @()f{^xm֣Ln-͵on6Y:3d@g{YohL)0EoDdt/-R% @5=ߩSWߪ߯06*Z40|͒@Y|-}E!B c0qcm">Hh4t:)P(f 09 awQJ15=M0diiNT ŕeunߴWWPJ"_|9Qa6U(BJY"sAtm8F6X:M m5 hh iBaJ෿-7o$c9»Q۲͛ʁ_ &={rd|ˡyV !ep 0СC۷(d2{:.caa7o]R_}<rܾs;L,br\.7Gr7@t8_}ŧ~}133Aqty qpl!Zk,"͢ފ8BrC~ٮq0iaiAb v b5rb Q@~[7Q'f), uQJaA)H`]1X9J\߆6'[c;ꫜ?5qs1.]D6q !PM9 8N,`RY]nu5ܹ(lsҥ?33ŋ[m'Op…`хضMR᫯ns1?1NC6CxoxḾC/r?>&Ym!?$K.s:333;v !цѵ h6=zt$8`ehNd2dYlNﴲqA[rBjqE/  0 Nu"!MO-K,J 83q<˲R˕ =gVhlh6u|GJ2q3==]j:DqL%#Xpmu2qL"X ~E$c6?AFʳRESSS# }JkwAS_%릲F1aZCS=ch.Oj5;a@tX >'Ci@{};c\ʕ+\z9y$@mhх1V|T*ZW^e~n󺋐MwYA't(IH!1X'IL$&#m3QuhI4Qpu+T75Rոr KKKZ-]Ɨ k$W֩Eϩao7NJu? f_y!$eYt1B`]0d\9@M뿈R<HG'NԩSiX3==ٳg~0`+BkUuEٝJ`9H<5gϒ垨99yd:}ӧO1m*Uދ$xMSxlGRuX IeQ^N|t:5Ơ(fggMyD7T'–oݚqt(b&C'p,ե+?D&@}D7a/JմӳӨp wŇ 0ܠBІ䱂mގMzI)X]'' d#.rd}J)RO7CG8( 𳳊uM?;ܽ,s?ŽND"tctmg](D<qS_Ep#sEIg|U^>)qi#:t2Ra;$1ֆ㐱,l)Ѻ vw_pKqX8XHb۶( #aA&hx\Ϻ]PU'ְ$( fEe2Kxv3!0_Fk2P}fUWV^xF)ʀ}d Vg}_LW+d{ E % fŗЪ$fqa*GH}ם`?z$e"_QM .iƤfzfl DZ,̣D<ĴC(A1L퇱w?!k+2: ~pK-!Kuݴ&۸=xV%^ܻއ8z7H)vB`, $0 69 N0G%_=+'\ܷ'' _ ݡ0S(`{3Q. JӘl$42PF ))Dq1 c 侾l n.T>,6MR{Kd3s#o/mŇ18&*OQ/P&~SKxGȕ(IINNq1Y=`xNcXora%9x ˕&,=b@F=d/@/2XQ"fOS~5VYӟ*>+av7ތAi$Xa-2 Qqx>I myljf fsO AO H~k)q3~)fz^o qh44y0n3==8F_VmI'u::Vv#LA,/Gs$I(:tw~Gڵk!QSTW 0lHnɕۢj} 䩓LLL_0==M\&I>c*G15Brxz&I6tFӽSû3gP(R 5ܸo,-Qf]BZd8wr9}Gϭ[ZtHcIopt$XśoY\\LÌqOR)(" ç0FYa[,bS,fH)iZBt6b1$Ii!q8p`h׮^MS)X~]~ F9Y$z'$ya1&mi7ҥҋzc/o:yP^m(-۶Bxg !@;uhݿ-NBfJJ;hYcccLLL`YT :ak5vN)P뜬fTbTI][wǸYj{w[_5R . $&ˑ$6BܬG[FkYI֥!;Wyr=.I2n*oxaʳP,r./7[oΖB)E>g^'<\2\7h5y~j6T*$ސڏS14 'np=!祙0nU{n\.CC&N9ǶG,qnw e˦iƃ3ͦX j 7~y)%aEn 埄sEa8lJEQm.qۗ,7n@)ÇRuŭ8}| B>]~KqZUZƯkPJ@Xܹs۪pe :GJB V2nGnuuP(`6>CVkt: Bq*q۪p033C,Ķm^y R)U,*p*q 7>>juy|2Lf[nRDTZdя~ďd 1a'f8j= nջR&n_yض֨œė?@n-L۶Sm6\WDیQM}5iT!^??;^^${1+e,1 fk%Ū 5ö[*KIENDB`fritzing-app-0.9.3b/resources/images/helpImagePCB.png000066400000000000000000000002061272363601300225050ustar00rootroot00000000000000PNG  IHDRGNPLTE._tRNS@fbKGD- pHYs   IDATc`.P"uIENDB`fritzing-app-0.9.3b/resources/images/helpImageSchematic.png000066400000000000000000000021161272363601300240030ustar00rootroot00000000000000PNG  IHDRaa?PLTEhhhsss$$$vvv񘘘;;;OOO???醆GGG"""wwwFFFڜ^^^٨,,,\\\<<<///+++---ooo zzz֭ iii'''ͱfff UUUMMMrrrPPP}}}:::@@@પYYYBBB444TTTnnn(((gggƝ%%%222DDDyyy666qqqؚEEE把ˏ)))!!!򰕻 bKGDH pHYs  IDAThW@' נDP1ZmY,RTP@Q[dq_ źNX}¹7yȗsgrO  HF6lA6#:m6²7l߁kعk7 VI޻&R08u5 .6BR> M5NjaQ嘿M29w2jpw ؆ pG!hࣷTnv,S$Grԁ3g}h`P-{[h =aÈqcF. +r4-ҮtxUkI!LPSUV w&l3w R\ k0(@$%)Cl~a/=y#E\X|>RbxoFئͽ}-cizeGyy,U$\R0I' MnR)q~ΎELb 6jjf&1ިerɟ[|s d A3>de@AA `09qIENDB`fritzing-app-0.9.3b/resources/images/icon_icon.png000066400000000000000000000012001272363601300222200ustar00rootroot00000000000000PNG  IHDRnMbKGD̿ pHYs  $IDAT8ˍ=Wy?VGQBBDXB0H \,J AL lFaĈMT )A^}z˽ssncu eg#̌lJQIdw̦vtGү."{&E bY?9wjoSR"D+!CoSsB )O2tS7׾g RCoA23V8+} TzaLzaDw$QN5PR~>IYCF(lwJf$t*i:q!ոG2_KfՃvL [|5Pȭ\!M4{vYq8y&/\2+;oZg`)u}1LKkru"Ժ ~w^t| <YM\YɀCqIό VK(.t҄Ei[yu1|l\.G[eLR{qWY:sX,2RMz2%ĬZIENDB`fritzing-app-0.9.3b/resources/images/icons/000077500000000000000000000000001272363601300206745ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/images/icons/TabScrollArrowButtonDownNormal.png000066400000000000000000000002611272363601300274760ustar00rootroot00000000000000PNG  IHDR bKGD̿jIDATӅ (d2BFr8TIFGa4 w~TNW4(qD.)ifԾ[3S@.k /reϖƣ,/âIENDB`fritzing-app-0.9.3b/resources/images/icons/TabScrollArrowButtonDownPressed.png000066400000000000000000000002721272363601300276550ustar00rootroot00000000000000PNG  IHDR OïPLTE       B;WtRNS0`9^@bKGDޕz:IDATc`r (2A # pɅ%L }%PX  J vIENDB`fritzing-app-0.9.3b/resources/images/icons/TabScrollArrowButtonLeftNormal.png000066400000000000000000000002551272363601300274640ustar00rootroot00000000000000PNG  IHDR v&bKGD̿fIDATc` Ty E(<~  ,0m  `LڐڐU@>dH_pM!#I~LaI #DIENDB`fritzing-app-0.9.3b/resources/images/icons/TabScrollArrowButtonLeftPressed.png000066400000000000000000000003021272363601300276320ustar00rootroot00000000000000PNG  IHDR Qs$PLTE          ( tRNS P`0ʣbKGD 6IDATc`@ d`]$@* ɺ DI0e&J$y@)g IENDB`fritzing-app-0.9.3b/resources/images/icons/TabScrollArrowButtonRightNormal.png000066400000000000000000000002571272363601300276510ustar00rootroot00000000000000PNG  IHDR v&bKGD̿hIDATc` Mf=7N_$KęJT_d K:ՁB P aLN3q]xӮL&N|8/IENDB`fritzing-app-0.9.3b/resources/images/icons/TabScrollArrowButtonUpPressed.png000066400000000000000000000002711272363601300273310ustar00rootroot00000000000000PNG  IHDR OïPLTE       B;WtRNSP @1,bKGDޕz9IDATc`@Pzfv/13X BT @ d@F0}H C IENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetBreadboardActive_icon.png000066400000000000000000000004231272363601300273650ustar00rootroot00000000000000PNG  IHDR$92bKGD̿IDAT8M@EZ\:H `**X:`;q@1FF?}?[j/9__W'2%t2:t'nIAMΤ6j=Y{E60 ĐPf&[{VWAH>Pۀ5؂v9V[;F` #%doN-ϯ)IENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetBreadboardHover_icon.png000066400000000000000000000004251272363601300272370ustar00rootroot00000000000000PNG  IHDR$ 6PLTE                2tRNS`@Pp0 ϯ4nbKGD=qIDAT(@ہa+=ZN /cI -(TJ2I{"r8'rs"3he6ꇣ(}Q2Wu^. ʶg ||HmmaBIENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetBreadboardInactive_icon.png000066400000000000000000000004441272363601300277170ustar00rootroot00000000000000PNG  IHDR$92bKGD̿IDAT8UPt %ȱC V nܾMb;H .pC l9sgΝad䟘˰0nW7J.nt=J,vjSkd#IENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetCodeActive_icon.png000066400000000000000000000002451272363601300262140ustar00rootroot00000000000000PNG  IHDR$92bKGD̿^IDAT8c``pp+?yd7ʽ;6ױ 3sx5>JbR3Mq3|jGlBn 6KIENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetWelcomeActive_icon.png000066400000000000000000000004261272363601300267360ustar00rootroot00000000000000PNG  IHDR$\9PLTEϼKǵeW tRNS@` p0߷*GbKGD-rIDAT(ՐI C) X ΍f_65r-eaEd0<;S!Dcݑ#];Ӷ$i -eFmvy~>iYӓuIENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetWelcomeHover_icon.png000066400000000000000000000004311272363601300266020ustar00rootroot00000000000000PNG  IHDR$\iYӓuIENDB`fritzing-app-0.9.3b/resources/images/icons/TabWidgetWelcomeInactive_icon.png000066400000000000000000000004311272363601300272610ustar00rootroot00000000000000PNG  IHDR$\iYӓuIENDB`fritzing-app-0.9.3b/resources/images/icons/Toolbar_Icon_Order.png000066400000000000000000000137471272363601300251230ustar00rootroot00000000000000PNG  IHDR@@iqbKGDIDATxypם?{{H/EuZg%.o֕&WjʻUQUIUS^[eH$ J$@$0w_/ .<,ڪG hvPS(r̪AVfv@[X`%㖣e Em\,TYZ,P|rF*= (!-:c8JkK)W.౹ygϞ@M.?HjA 7nDunʩ/8©Y̡׺WeҖ{yꅋ~#߿@)[@,Y@po!.ݹ,[E˫]42KOtTYSHltz2开 frx0< y~B|8(}_=o~LBd0YartiV7JBacs̱]Gfr9ci&12h?4MP*竲 \-'wlLQVI;e\t]QUW&Dea4qqHe1cQǓE&x lEU˹ꚫh4)zJ6! hXȆ BN~(ʤ&:@YӴL{@1R8"yP4wjhQHY`0s~?e@@gsLeF͋A?\Xr+OE>c|{˚ujv p]QgU=en E&*)&ep42N_Q0L, /v*t!'q6n!icʛ/$ja"ڠeΞV.Q0b&/3~:K-DA*D}C/YHH+cTVUyU"e‘0@7r7uS4(9c!ku3CWGG7~VW}D{߲Z`oYoXxPOe39r_tPQ3⌮ڻ |#QBjJ0Ǘm[uymǏz\\KQ6Fn!"jThئ5.8sl&ϐ3pWKgsg/|i (V㽗>]zAKѸQ2bSCwO=-<̧Sqz%N/n1!f&K/ΐ߇ǣ C=e/lm}CyHݱ7),5&䋷w(Y`}1(L J'il[nw_B`{ ]tp)N@czBBm.R%&7kT7q~Db܎3;Eo c'xU0A#bTVP8& b <Oq3=LBb(8CyYPRؽDQ#ܬ ;@H݋ƕ W>xHS"m'7+ku k[Qrn*'qrgL:7]r2 56B+H1E :2"8%LLl\Tpw_ .퇥R8r>RȊ$2߷Z~%$0|jP& MSZPhU̹;wJJĈK~_zE+|G;b7Cd[EXD80 4MШ;!Q8'(H[MU he|b<5Bl4عo4Ix\pPl{~XjLfsi1P"$eGZ@NMNyu*Q-b׵5,3Oef{WЇG:(|>g!a_y+\5|j6ňq{P^zY3A*yi4 0|( yt3De;N.T<Kz:;0r Sb+d=+sPr?K̼E}}7`@( (g{W2=Ifsi3$;m=QtlR*Jx9 )zCmz55#u5W7뤧 gT<;d睬b=O$nPs,v zfkBB_`8^޿֪܍ݳ8KƲɑLcӭGIsuo@3L3)z?嘩$`  ZXECoڭJzǺik)%P%@6d TSANW%gQcz}$[@3T&U8L7-z<r˴+եIS5*W>?ȳ:: jIENDB`fritzing-app-0.9.3b/resources/images/icons/WS-blogLogo.png000066400000000000000000000011131272363601300234710ustar00rootroot00000000000000PNG  IHDRc iPLTE2"'BPe]qu范XKBZ׼?0뙑L>eYrgϮk}ǡx5N O#كtRNS@fbKGDaIDATHn0 E1- Bu{u@ M1JI |`pƯ2"*F1 o{ Ycxta1ˍ7Ȳ{,yFNX͹etP˂, #kFگVǾn ՐK^$79=flx_3~} A>)ؤ0>HF[KIENDB`fritzing-app-0.9.3b/resources/images/icons/WS-fabLogo.png000066400000000000000000000011021272363601300232740ustar00rootroot00000000000000PNG  IHDR]0`^iPLTE2"_MJ}nlzwscau范XKҥ?0뙑L>eYrgüiXUƂ ;/+stRNS@fbKGDޕzzIDATHm0 ` ȊP/w䵛s~:4OKY ¥7Q̱z' SQ$7?(v7 iy~S(}'I1`4PitP %q wFt떖k8<j{N d^ Gzfm5Ka %tRxHR܏w1'a4;1zҧ~^Upv26:Gaq)uZxe]&xg'-wq=Lt%\DNf q'4~l$Q#禵H]SV>HDrMN5O&]:YK'#Z(B} ^IENDB`fritzing-app-0.9.3b/resources/images/icons/WS-fzz-icon.png000066400000000000000000000003211272363601300234640ustar00rootroot00000000000000PNG  IHDRy0PLTE렅렅2"렅eY?0范L>K 9uxtRNS;bKGD@IDATc`0! 1 40,h30,Ӳ"f86C6h"65@ 2IENDB`fritzing-app-0.9.3b/resources/images/icons/WS-galleryLogo.png000066400000000000000000000012611272363601300242110ustar00rootroot00000000000000PNG  IHDRsĤS=lPLTE2"\f@0{u范XK ppͣ?0뙑L>eYrg`ǙӮPظ h8pEtRNS@fbKGDޕzIDATHے0 #l1`N,߱MaMM;jFleฝcvBJ`& ӐQeY;y<)2$1f4X:iQŲ-_2جt-JLbh@wdh-% '=SJr.Ca\(oZa7%DEoa6M--̜J>4Q': Vڟ:kHgG5\T9a$F7zcbb&#"fHysG뼋&h3YMEHʒ\*=rwo$BQވQp ] p W~/ 4C$ IENDB`fritzing-app-0.9.3b/resources/images/icons/WS-shopLogo.png000066400000000000000000000011701272363601300235220ustar00rootroot00000000000000PNG  IHDRf#iPLTE2"@0u范XKҀp?0뙑L>eYrg ؏`P f4tRNS@fbKGDޕzIDATHn0C Im!o׾& -MT:JkӎGfCF`1&[-mx/o1GsGӺMG4r?Ɣ{US/P8BC r:'y(Gn1JL13wyô7v#dQ6C;2yPh*g`zR ,9[ Eiv[F s}{pQ3fOj?]yb ˴?l 9ELgaX 5ctpU<'T0tap&a12FD <,ftu`>+k5 d"F,`g䳋Z`zc.."%6q>mng-R$CkQ1``X ذ:L3ɸo`^W~oanIE#dIENDB`fritzing-app-0.9.3b/resources/images/icons/arrowButtonDown.png000066400000000000000000000002611272363601300245570ustar00rootroot00000000000000PNG  IHDR bKGD̿jIDATӅ (d2BFr8TIFGa4 w~TNW4(qD.)ifԾ[3S@.k /reϖƣ,/âIENDB`fritzing-app-0.9.3b/resources/images/icons/arrowButtonLeft.png000066400000000000000000000002551272363601300245450ustar00rootroot00000000000000PNG  IHDR v&bKGD̿fIDATc` Ty E(<~  ,0m  `LڐڐU@>dH_pM!#I~LaI #DIENDB`fritzing-app-0.9.3b/resources/images/icons/arrowButtonRight.png000066400000000000000000000002571272363601300247320ustar00rootroot00000000000000PNG  IHDR v&bKGD̿hIDATc` Mf=7N_$KęJT_d K:ՁB P aLN3q]xӮL&N|8/IENDB`fritzing-app-0.9.3b/resources/images/icons/asterisk.png000066400000000000000000000003341272363601300232270ustar00rootroot00000000000000PNG  IHDR bKGD̿ pHYs  ~IDATmQ0DU PD" A% !uPTB$# q/`Ji")ʋ' &ZFAfSn4U2Q^Y|ga7ih|9dIENDB`fritzing-app-0.9.3b/resources/images/icons/check.png000066400000000000000000000002251272363601300224560ustar00rootroot00000000000000PNG  IHDR ';6bKGD̿NIDATc` 0r3$2a]u!E(9Ma˳B i aB0 ާpDAw)IENDB`fritzing-app-0.9.3b/resources/images/icons/checkactive.png000066400000000000000000000002611272363601300236520ustar00rootroot00000000000000PNG  IHDR Ҏ!PLTE         ɇ0i tRNS0@ `pW2t7bKGD hV(IDATc`@ RȁQ!d`T;H6ZN=^{IENDB`fritzing-app-0.9.3b/resources/images/icons/checkhover.png000066400000000000000000000002611272363601300235220ustar00rootroot00000000000000PNG  IHDR Ҏ!PLTE9&9&9&9&9&9&9&9&9&[* tRNS0@ `pW2t7bKGD hV(IDATc`@ RȁQ!d`T;H6ZN=^{IENDB`fritzing-app-0.9.3b/resources/images/icons/combobox-arrow.png000066400000000000000000000002401272363601300243360ustar00rootroot00000000000000PNG  IHDR ';6bKGD̿YIDATc`020\ $!=a#|$Df-|,B;! .20000AU82|`fLxFjFCHIENDB`fritzing-app-0.9.3b/resources/images/icons/dockWidgetCloseHover_icon.png000066400000000000000000000002311272363601300264640ustar00rootroot00000000000000PNG  IHDR ҎPLTE9&9&9& tRNS`A3SbKGDhQ)IDATc`F&&#ee%#@d!*W0:k[IENDB`fritzing-app-0.9.3b/resources/images/icons/dockWidgetCloseNormal_icon.png000066400000000000000000000002611272363601300266340ustar00rootroot00000000000000PNG  IHDR ';6bKGD̿ pHYs  UIDATm 1 9#Ie!Rˢ6b`R~If_u?oNOے]aIENDB`fritzing-app-0.9.3b/resources/images/icons/dockWidgetClosePressed_icon.png000066400000000000000000000002311272363601300270060ustar00rootroot00000000000000PNG  IHDR ҎPLTE   7tRNS`A3SbKGDhQ)IDATc`F&&#ee%#@d!*W0:k[IENDB`fritzing-app-0.9.3b/resources/images/icons/dockWidgetCollapseHover_icon.png000066400000000000000000000003331272363601300271640ustar00rootroot00000000000000PNG  IHDR bKGD̿ pHYs  IDATcπ0e`a```HgP0#~,?0\Ș]X8dB L  H V0 o gvn`+7 *QIENDB`fritzing-app-0.9.3b/resources/images/icons/dockWidgetCollapseNormal_icon.png000066400000000000000000000003461272363601300273350ustar00rootroot00000000000000PNG  IHDR bKGD̿ pHYs  IDATcπ0e`a```"Ϡ!s#- X4~`(Yȸ$0n~ `}p $ c0|```X000|b8Mz󲝷,da X MZ1<{#]$+ͤҧ!FDBIENDB`fritzing-app-0.9.3b/resources/images/icons/dockWidgetFloatPressed_icon.png000066400000000000000000000002351272363601300270120ustar00rootroot00000000000000PNG  IHDR [APLTE   7tRNS@T bKGDhQ,IDATc`F%%%&#%e FJXIP  T[ IENDB`fritzing-app-0.9.3b/resources/images/icons/fabquote_button_hover.png000066400000000000000000000002211272363601300260010ustar00rootroot00000000000000PNG  IHDR,~NL PLTE%>>>Y bKGDf |d pHYs  ~!IDATHc``Q0 F(P@>HgIENDB`fritzing-app-0.9.3b/resources/images/icons/fabquote_button_press.png000066400000000000000000000002501272363601300260140ustar00rootroot00000000000000PNG  IHDR,~NL PLTE%>>EtRNS@fbKGD L pHYs  ~(IDATHc`" ,0oTߨQ}FV }Ӊ5TIENDB`fritzing-app-0.9.3b/resources/images/icons/fabquote_button_release.png000066400000000000000000000002211272363601300262760ustar00rootroot00000000000000PNG  IHDR,~NL PLTE1>1bKGDf |d pHYs  ~!IDATHc``Q0 F(P@>HgIENDB`fritzing-app-0.9.3b/resources/images/icons/noArrow_icon.png000066400000000000000000000001641272363601300240420ustar00rootroot00000000000000PNG  IHDR7n$tRNSv8bKGD݊ pHYs+ IDATc`!3IENDB`fritzing-app-0.9.3b/resources/images/icons/nocheck.png000066400000000000000000000001531272363601300230130ustar00rootroot00000000000000PNG  IHDR YatRNSv8bKGD̿IDATc`Lp&UoLj`?9IENDB`fritzing-app-0.9.3b/resources/images/icons/noicon.png000066400000000000000000000001271272363601300226670ustar00rootroot00000000000000PNG  IHDR$${5bKGD݊IDATc ɦ1_gKIENDB`fritzing-app-0.9.3b/resources/images/icons/noteResizeGrip.png000066400000000000000000000002031272363601300243460ustar00rootroot00000000000000PNG  IHDR<@ PLTEOZ`bKGDf |d pHYs B4IDATc`&A$UCIENDB`fritzing-app-0.9.3b/resources/images/icons/partsBinCombinedMenuHover_icon.png000066400000000000000000000002431272363601300274650ustar00rootroot00000000000000PNG  IHDR <PLTE9&9&9&9&9&9&9&9& jtRNS0P hbKGD ٥IDATc` :HI4,\abl0 -MIENDB`fritzing-app-0.9.3b/resources/images/icons/partsBinCombinedMenuPressed_icon.png000066400000000000000000000002431272363601300300070ustar00rootroot00000000000000PNG  IHDR <PLTE        TitRNS0P hbKGD ٥IDATc` :HI4,\abl0 -MIENDB`fritzing-app-0.9.3b/resources/images/icons/partsBinCombinedMenu_icon.png000066400000000000000000000002071272363601300264610ustar00rootroot00000000000000PNG  IHDR %7bKGD̿@IDATc`+`DHʟUAa¬B&0000082gЈ$%i zb8IENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomInEnabled.png000066400000000000000000000004031272363601300263060ustar00rootroot00000000000000PNG  IHDR]bKGD̿IDAT}mP %\KѢ "RA($F"Μf5lrt`2jJgֻRreYWNCeuX y+{4fB?mgT(@{ܕo-?6ڂd*9Vqۉ@N>_967o;CŊ0nIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomInFocus.png000066400000000000000000000003601272363601300260350ustar00rootroot00000000000000PNG  IHDRy*PLTE9&9&9&9&9&9&9&9&9&9&9&9& tRNS`p@P0fxbKGD a\IDATc``T>sX\LXN 00H``0UAYV`0c1 N .Qm7f2CYy5.QB.cMIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomInHorButton.png000066400000000000000000000004031272363601300267000ustar00rootroot00000000000000PNG  IHDR]bKGD̿IDAT}mP %\KѢ "RA($F"Μf5lrt`2jJgֻRreYWNCeuX y+{4fB?mgT(@{ܕo-?6ڂd*9Vqۉ@N>_967o;CŊ0nIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomInPressed.png000066400000000000000000000003601272363601300263630ustar00rootroot00000000000000PNG  IHDRy*PLTE             tRNS`p@P0fxbKGD a\IDATc``T>sX\LXN 00H``0UAYV`0c1 N .Qm7f2CYy5.QB.cMIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomInVerButton.png000066400000000000000000000004031272363601300267040ustar00rootroot00000000000000PNG  IHDR]bKGD̿IDAT}mP %\KѢ "RA($F"Μf5lrt`2jJgֻRreYWNCeuX y+{4fB?mgT(@{ܕo-?6ڂd*9Vqۉ@N>_967o;CŊ0nIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomOutEnabled.png000066400000000000000000000003761272363601300265200ustar00rootroot00000000000000PNG  IHDR]bKGD̿IDAT}m@o- ( 0'+\㞜A(a;H a2̼ћhШU6v֤]Yt2Z|owI!E_rwhUEoK*a+fMޡ@~҆[!T/v\EI4Rej**>n>-TfO IENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomOutFocus.png000066400000000000000000000003601272363601300262360ustar00rootroot00000000000000PNG  IHDRy*PLTE9&9&9&9&9&9&9&9&9&9&9&9& tRNS`p@P0fxbKGD a\IDATc``T>sX\LXN 00H``*,b+0h6`S gΜ9dE j pLfh5+1brgIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomOutHorButton.png000066400000000000000000000003761272363601300271120ustar00rootroot00000000000000PNG  IHDR]bKGD̿IDAT}m@o- ( 0'+\㞜A(a;H a2̼ћhШU6v֤]Yt2Z|owI!E_rwhUEoK*a+fMޡ@~҆[!T/v\EI4Rej**>n>-TfO IENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomOutPressed.png000066400000000000000000000003601272363601300265640ustar00rootroot00000000000000PNG  IHDRy*PLTE             tRNS`p@P0fxbKGD a\IDATc``T>sX\LXN 00H``*,b+0h6`S gΜ9dE j pLfh5+1brgIENDB`fritzing-app-0.9.3b/resources/images/icons/partsEditorZoomOutVerButton.png000066400000000000000000000003761272363601300271160ustar00rootroot00000000000000PNG  IHDR]bKGD̿IDAT}m@o- ( 0'+\㞜A(a;H a2̼ћhШU6v֤]Yt2Z|owI!E_rwhUEoK*a+fMޡ@~҆[!T/v\EI4Rej**>n>-TfO IENDB`fritzing-app-0.9.3b/resources/images/icons/parts_plural_v3_plur.png000066400000000000000000000001271272363601300255640ustar00rootroot00000000000000PNG  IHDR$${5bKGD݊IDATc ɦ1_gKIENDB`fritzing-app-0.9.3b/resources/images/icons/parts_plural_v3_sing.png000066400000000000000000000001271272363601300255420ustar00rootroot00000000000000PNG  IHDR$${5bKGD݊IDATc ɦ1_gKIENDB`fritzing-app-0.9.3b/resources/images/icons/searchField.png000066400000000000000000000004041272363601300236110ustar00rootroot00000000000000PNG  IHDR7bKGD̿IDAT(ϥJqϨB mJ1z*1hM Hf&E]xrr(8KAc5'c`Nj *t ݂?@ݷ z%PÅs,.3j2(įw{-M2Ny9" R"67fh?/ޭ*WD 226R0(. W (7IENDB`fritzing-app-0.9.3b/resources/images/icons/spinArrowButtonUpPressed.png000066400000000000000000000002431272363601300264140ustar00rootroot00000000000000PNG  IHDR bPLTE       B;WtRNS`0VRPbKGDޕz#IDATc`F0^" kyyJ///c:ZrIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarActiveLayerBDisabled_icon.png000066400000000000000000000002761272363601300277440ustar00rootroot00000000000000PNG  IHDR%T)CPLTEffffffffffMtRNSp߀PibKGDaf}FIDATc`FUE$ DCC*)X(k(0Uw$5 0wG^2yc\u_Ҙ$iA,,IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarActiveLayerBPressed_icon.png000066400000000000000000000003631272363601300276370ustar00rootroot00000000000000PNG  IHDR%T)C!PLTE     GtRNSp߀Pp߀}bKGD hVlIDATc`F E$ $::hX()XG0p4U-9[Nf1`3^3Orǭ]iwc(;MIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarActiveLayerTPressed_icon.png000066400000000000000000000003621272363601300276600ustar00rootroot00000000000000PNG  IHDR%T)C!PLTE     ,tRNSp߀pP߀H>bKGD hVkIDATc`F E$ $::hX()XG0p4Ul!=_8~cP{H$Noyl! hGG']oѳH$R`Cr4]e|>ē-THܟT=N4EJ1jBݙ;K_nH><u\ ^b@RAjqp<{^=?pgx /k`G_@Ұ)竟+S.>>Zk,gZFDȆt ecDmQ_Fr2(7F!b@Y{A Rů{|o<`_m~g(-p^lB@ 7 Jfu=Mw#j?JwWW^w[oD&(A3S@NA6B|cNW+v(PHR-5pƉ(ܰBjA!W}X{$M@)SRJ%baRbhA97{`___B)T*ukٷo뢵}*+++XkYWۿm.DkIdL'LwwZkBl#F[֚6&dD٣PC!Ҳ#ڣ]B #GNϰ)%RJ<uoZ#b.T'4بB,P2B(~x 7K|EqdgJA@GۚHI)/B'udZLWWב|>D[[[Rs bXzMԶqc rJBZ%hYi@*<8zzzZ\**J tKT|ߟzP-d8P( 0Pud2&/_\y ;snwHb8u]bH&ܹsH)>E1ΜF(J> 044đ#GX,rZs1T*I+I!c̙3 033c RJ2j{Z6 թS!uYYYYBwj-ݻx9}4}}}d2LMM-jkROLLa~9f&&&.koB:.L&ǿ9qī۷o !zbs59z(8pΝ;<ϻ777t:}6Ł7n6 LMh_J,.. qRtSJPJ (֊Ç_Z1@֭[~:>@8~ׁ@uW*<`\S c6t}@'kU')7חR_ 0/htJ&ҥKf?5M=GqhtUiNLLL&_b^|hjM{Mi؇.cdYgzzz(zmrrfenP."Ο?8'Q"= |dK0`Q ;IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarAdHocPartPressed_icon.png000066400000000000000000000034011272363601300271260ustar00rootroot00000000000000PNG  IHDR szzbKGD pHYs  IDATXŗ]l\G3wf8N⍳1KꂂHrH *(B<""T4O!(ie'N`;^wݽ{vIGi4sΝaiܹsbu6%T}6"67J|Г/`w|&N?}%R'#{7ڣglEc*%x%]j?]#dSXX$%,͑ȑRkI|uHEU Ǻ_d/xˠ" CkZz YKN$M_h䇞EH$$(#~ KiCUlР[Nk^HdQc]Q@7*d_WH$=e}qx <}M˲ZS*(T*º̖eY/޽{[˼ەNkM\nnSZ6` 0 ˷;h%Nul6S/_ޤXƘf0Y;smmmxmۏuf={)%===r\l||ZNbLd2CCC?~^<ŋLOOɓcL#@P'I !X[[Ck0ccc 0;;15RJZ8_TzCCCC㰺ZBDIZsss(Ol6"|AeQR듓/czzz:::}vrrr3$1::l&{oa։'񸽺B$ŗ(W<ϣJBZqjooopwyG*9^ŏe-8%ԢRemBEk 7"y:3$ ڻ7obV@3  9BQWW_wm / F fS%JAN4 ?Xex~S, - =\y堯#sM!DMMMPӴVI%INyy9~]IRb1ݻG*Oh9.$`[ [oI|}.26-c|dkH)VUU'Hؽ{7x~$I¶m9[g0)&|=Ev}h^+!TRR4 941Mz2fJe^/w$I«Y=˲"TUͫP:yz"x|e133,Xt:mHB lضB]˲bYUY]ZZ\Г%dYFQxAǟ "PGcc#EQٶ퇲,cvv#TQU͛7 LMMz_ NdY!(m]!"yyy444PXXHww3---9r;w299Iss3]]]N{{;cDT*_U\e1MVN>>]PP}fRU};NaKFdYz|+Wx'N:a۶L&'ɍ@aHf___^mm>0 nݻ׮]d,͉ dwTaŒV ؐ!jjjI$q{zz q=Ss]cH7ciX^udlh+=dre!"2 6t/] d$ZSpzL 3L9·Κ AͿ\UQiwU/ k.y׏~q-:?1KVrكzUV pl/p! & IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarAutoroutePressed_icon.png000066400000000000000000000005211272363601300273100ustar00rootroot00000000000000PNG  IHDR dre!"2 6t/] d$ZSpzL 3L9·Κ AͿ\UQiwU/ k.y׏~q-:?1KVrكzUV pl/p! & IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarDiyDisabled_icon.png000066400000000000000000000004441272363601300261540ustar00rootroot00000000000000PNG  IHDR%JB3PLTEffffffffffffffffffffffffffffff_5tRNS0 p@P` bKGD ,IDAT( EQo $(w<666xܷE$qĞnSԧ2}cBT&GzOUCHA^}S7_(RFuʦL"Ns x j(PIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarDiyEnabled_icon.png000066400000000000000000000004641272363601300260010ustar00rootroot00000000000000PNG  IHDR%sbKGD̿IDAT8ё0D_n((A+8+PKJ<+tpCK`_6X} >f  hQ)_3$ekjT7'H*Mk *$] \TvmhF2Y(a , )Y-TݞG{%D~i%KIMc6e>C]ӋP* }Y:t̵]/gf~kYm&?$ "˴/lkNA09`a\ VK֫AP-#Mg#XFM#3Bh3(:_Vj8wE~h6YfC"P[W 阃@TH|6HB @ͽ  oZ- y3b-LL*(|+.,M@Tx\|)+&{.$`/>AG~6^`YY/Tn@+kLfU;CТX"  XcxII4'6c$ɎRZ9ALU0* wM}$VV,px Ld$F?)f:Ǥ:& 84::*ba9L,vq5>MOBx_0 !(Q > , Oϟ,%"L?Q*^*Y[^]P$5! ]1M) 6S:CK~Z̻$Cparb@;Z.(e R6d ȪS/9.!\dAm`B?N{Jqq~p &L^X%?|Jڝ/ .cL;q39ʞ9z_ѵmW?:wS`=7+J!\]2$8Abvi|9, V!1>)&Cxz}:~4m}3FO0 ͙^@;6ѧˣegCZb$B`S ؚvk#$_`Ro,곻=sBIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarEditPartEnabled_icon.png000066400000000000000000000021531272363601300267650ustar00rootroot00000000000000PNG  IHDR sbKGD̿ pHYs  IDATHǕLUe?{{tEd9ΘKtƦ[,iEkmVbd8re k)4"b8MDgÀs\ A?g~ (p 5xkGJF%s Ljxc)Dg!k\xbDSUݭ#@J<c;/b"Ԭ[G#*A`_jIƯ6=͗Nځݧ܇@&1eC@lzQDٝEF1G(0 Ic{t!hm@p_uBvD=)Ě\Q휡@"hM8>1>(֮[)P>B1Lȗn"$;rEIF5D"h`99L u8 @]0B48H!(g1 %c2u? sZ|Gh\)c^y**+Cc=9k&spAFӨ!.!*F&?ph"'mgfDBx*K@`qf88{TˮADYAE## cXO!CTrl!ium"?u xqQ O2K ( ٽny{~'$1|L,BPM1he$׸Jxm@[EI4-'kh!P9Sb!8J8enbnP?;y46fH Dps+ T-+aXJ|"Xy !m߶PNZ z8qOtRNS" ,( 8)%AE' H*I.d9EGC#<2 5DOUXY;bKGDH pHYs  `IDAT8˅Ӈ_Qq8F H X$Fl/ȝQF{=haY=;|}}>q8Kas2R7HKyrB!uyۨL+dY1@--+cceh WsWI`,̃ ^C-lk 9 ZRg_~=ɍM]]',/9lݶ]IM;wjMfH u欎Lf|9wޙQ0Ka`dW:kSͩ`xnL9Eܹ{[8@g_|wXxgWÀ޾q@{}w^4| h?^8`h*,  blOA/ ETvCP<dh`mLQ j٫ "ztbAI)JKb艄eUK*+˄"zkju M˰Ե5{*3iIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarExport_gbx_icon.png000066400000000000000000000026271272363601300261250ustar00rootroot00000000000000PNG  IHDR DsPLTEŴ¿"C:97!A<72.*y%i W F`YPyyyuuurrrqqqpppNf;E^7(btOdOcMa1$+o|% *%/&-ݞ ' 7 'Zc( 3)0ˬ)%ǩ*w$uqtqspl#+o$οȺ` t&TvS֟P?bD'Q-мձíGJOtRNS" ,( 8)%AE' H*I.d9EGC#<2 5DOUXY;bKGDH pHYs  bIDAT8c` YX 0(8YAhXxD  H蘐ظxq I! LR,2@ Ir|\"R` @SVƦ+*q*HfdfeGA_!"VXXT\RRV[^TSYU]S[W_QQ+*$TM QAa1a2@-pV hT`؎ A%"L,j&W1,%EatPMe^0V9$bs.qЍT3D^b0fqSɵzdgWxngJk)mR#'W^CZϺ֎cS r6[xcKsf8U&Nھc{z鴹3Z싶,G7hP@0`Cۉ| ӎ;zH`Y̓ǑN$ofShq7fA:{ΒfwᢇJ/ź|l̹u(;o7oݾs;x'O=^wy}W!cϧu/_AD}4BdM2,t%\(Ȱ, X3G >!(\( 1@0 " TL~ 3 MGiҢbϊ ՓJJ+*"UVZ67,Bj1VW2|fǠIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarExport_pdf_icon.png000066400000000000000000000025771272363601300261220ustar00rootroot00000000000000PNG  IHDR DmPLTEŴ¿"C:97!A<72.*y%i W F`YPyyyuuurrrqqqpppNf;E^7(btOdOcMa1$+o|% *%/&-ݞ ' 7 'Zc( 3)0ˬ)%ǩ*w$uqtqspl#+o$οȺ` t&TvS֟P?bD'Q-ڮƷeROtRNS" ,( 8)%AE' H*I.d9EGC#<2 5DOUXY;bKGDH pHYs  PIDAT8˅w@q(ʨb"n^nI%*VܻuޣuTҿ\ p-{//%D$_bD"ɛfPJD,!*C=8Ѽ1JJ!:6s4Hiwp1Ղ9DHh.\&{Ag GMz ƗZT:,(]~ɍMm-m'Y<[nn%)bõs3I)S޳w6=SBS($e%ןm2*90HxN8)r`d'NQ8…Z 9>u &ޙ%s'A~ =u5̻_MtzlyC@(/}>ryx/^z-uSgW@a@~7Da aHaSofOaSb0 ˰,;,< 3ķ<`iAA2!004bb (RV\Bbz yiYyEeRʊRBSm]}C2ƆZSM/zPKIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarExport_png_icon.png000066400000000000000000000026211272363601300261230ustar00rootroot00000000000000PNG  IHDR DsPLTEŴ¿"C:97!A<72.*y%i W F`YPyyyuuurrrqqqpppNf;E^7(btOdOcMa1$+o|% *%/&-ݞ ' 7 'Zc( 3)0ˬ)%ǩ*w$uqtqspl#+o$οȺ` t&TvS֟P?bD'Q-ܵض载yyOtRNS" ,( 8)%AE' H*I.d9EGC#<2 5DOUXY;bKGDH pHYs  \IDAT8˅W@JAbْ$ؤE{41@'yl}^^>,EUm(XƩ5 pLRۈDNHGbu GF ¨L1 !8ƠU2HKL\Œ% ͫc3'`&5kױMwnhmnj8>6EiAfڴyV $1l w L4y]ݷ.V稷NM!v CY;J5h ~x#GeA4`ƱPOV fC3Ob:MC̜)YgκB f:\ u鲓s uZ[ []v㦃,\t?xhAţO>{o0;:]>w}<?~O7xy! t=}$zxO"' OG.@}4 f~ 3^79Ar ;# x_$ML^>gy1UE%JK Tj=MU5Kj*+Le޻{ZIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarExport_ps_icon.png000066400000000000000000000025461272363601300257670ustar00rootroot00000000000000PNG  IHDR DaPLTEŴ¿"C:97!A<72.*y%i W F`YPyyyuuurrrqqqpppNf;E^7(btOdOcMa1$+o|% *%/&-ݞ ' 7 'Zc( 3)0ˬ)%ǩ*w$uqtqspl#+o$οȺ` t&TvS֟P?bD'Q-ܺհ~OtRNS" ,( 8)%AE' H*I.d9EGC#<2 5DOUXY;bKGDH pHYs  CIDAT8c` YX 0(8YAhXxD  H蘐ظxq I! LR,2@ Ir|\"R` @SVƦ+*q*HfdfeGA_!"VXXT\RRV[^TSYU]S[W_QQ+*$TM QAa1a2@-pV hT`؎ ACH?r)@"p !l@)J~V`o z\lN.nHсK=d<}|;3,xh[IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarFlipDisabled_icon.png000066400000000000000000000004731272363601300263230ustar00rootroot00000000000000PNG  IHDR\6PLTEfffffffffffffffffffffffffffffffftRNS @p￟`0P0bKGD=IDAT(ύQ Ci)nnt&%sƇ)v!'a.i@"s",g&fDZP!"_Ç`ߣbKGD-IDAT(ϭ D)b?MJ9 }r^3a!5rT(.i# ㇜d+8:oy?(t mxz}zx_e%nRa>Y@W&62mFfzW-oIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarMonitorCodeDisabled_icon.png000066400000000000000000000002031272363601300276420ustar00rootroot00000000000000PNG  IHDR PLTEZZZZҷ5tRNS-Mc%bKGD LIDATc`=`\5!kJ$JʊVIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarMonitorCodeEnabled_icon.png000066400000000000000000000001521272363601300274700ustar00rootroot00000000000000PNG  IHDR bKGD̿#IDAT8c``h#~bE`(a(Y`fIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarMonitorCodePressed_icon.png000066400000000000000000000002031272363601300275400ustar00rootroot00000000000000PNG  IHDR PLTE  7tRNS-Mc%bKGD LIDATc`=`\5!kJ$JʊVIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarNewCodeDisabled_icon.png000066400000000000000000000004021272363601300267450ustar00rootroot00000000000000PNG  IHDR5uֳ6PLTEZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ tRNS0p` @߿P/6bKGD=^IDAT(͏A U$ SO:u'D[R\$mgp+ny؃NLm]h{ձ?9/7- b/s9$~IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarNewCodeEnabled_icon.png000066400000000000000000000003151272363601300265730ustar00rootroot00000000000000PNG  IHDR bKGD̿IDAT8A A E?( +a$$PH XA%ǁ \Ȓ}IJ,{]hQFgഭ8k Wh56cnG0pft45]zI(XZ>F;QTӻXIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarNewCodePressed_icon.png000066400000000000000000000003711272363601300266500ustar00rootroot00000000000000PNG  IHDR;0PLTE              v8tRNS p0߿P`@|IabKGD]IDATc`  9pY]u>G{EO(2{n6ϝ@1eV``0Bw)Ex9 80 `{c;P5qd1IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarNotesDisabled_icon.png000066400000000000000000000002471272363601300265200ustar00rootroot00000000000000PNG  IHDRJ~sbKGD̿`IDAT80D%XJoB:GHp<XY$$͐TC2Cf x]QMb[}.@.Ҿm(UIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarNotesEnabled_icon.png000066400000000000000000000002471272363601300263430ustar00rootroot00000000000000PNG  IHDRJ~sbKGD̿`IDAT80D%XJoB:GHp<XY$$͐TC2Cf x]QMb[}.@.Ҿm(UIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarNotesPressed_icon.png000066400000000000000000000002701272363601300264120ustar00rootroot00000000000000PNG  IHDRY PLTE    HtRNS0_hhbKGDoDIDATcq' pe0 R02$0`q@v8`aA "#AeIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarOpenCodeDisabled_icon.png000066400000000000000000000003511272363601300271200ustar00rootroot00000000000000PNG  IHDR;*PLTEZZZZZZZZZZZZZZZZZZZZZZZZ~ tRNS@ﯿ0 p`rMbKGD aUIDATc` m Q@Up #1!ǐx{]8zf@@`4N=3(AH a !G!IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarOpenCodeEnabled_icon.png000066400000000000000000000002671272363601300267510ustar00rootroot00000000000000PNG  IHDR bKGD̿pIDAT810/(@ HE p$q40O&[/5n!ӟ2FHasݣau$hԦ2-}4ըe8s{B&')r֯IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarOpenCodePressed_icon.png000066400000000000000000000003511272363601300270160ustar00rootroot00000000000000PNG  IHDR;*PLTE             tRNS@ﯿ0 p`rMbKGD aUIDATc` m Q@Up #1!ǐx{]8zf@@`4N=3(AH a !G!IENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarOrderBG_icon.png000066400000000000000000000004351272363601300252630ustar00rootroot00000000000000PNG  IHDRPJQ:bKGD̿IDATXQ 0E;IJ$ 8A8အ Ⱥ@H]%ML$j*kCWP[;bM&NHëXsYpc7Ae͏869@M |l\~\zBG圽o[F!h-Fif{ǙuP smן]h]mSk}q9s\ 7vn[(mA;pCDs8'X'4CݾKt'Z!RH_RH +,H;78uzu:ނXE43kؐXR!i<ШWZ< E~^uHc7oIG>'$O|q٧Wq<ߣ A!jo,hj.(pqm?W 2QC>F^GPh֙$K) @bʚ@:ώDG uT t yկcHr04rW&wt8f$ff0P/P$YXX&:A榜\TS Fq `)PmʃB ajGdrU<[U<I?}D pHiA7bKGD̿IDAT8˭] sSP P 8X%T0  -[ ny#/?G_ˆ0\IL+1bW8J- =OOV*= a^+t[3RKDZ2TА,7jf ZsM"b _ JN2ou읔Gxqy\ 6_LIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarRotatePressed_icon.png000066400000000000000000000005071272363601300265630ustar00rootroot00000000000000PNG  IHDR13PLTE               ]tRNS`P@π0p"nbKGD ,IDAT(uY D]B\ (^4TYè† u#% Ng )%j%㫌j+^P{@+(-ߔҿIUEbziŜ {Se!:<nb`MЌy'%9KU?U=eIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarSaveCodeDisabled_icon.png000066400000000000000000000003531272363601300271170ustar00rootroot00000000000000PNG  IHDR;-PLTEZZZZZZZZZZZZZZZZZZZZZZZZZZ~( tRNS@p0`Ϗ-WbKGDo0OSIDATc`  : ` 5PBfkvYܹP޽- h{ sl` P(CIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarSaveCodeEnabled_icon.png000066400000000000000000000002641272363601300267430ustar00rootroot00000000000000PNG  IHDR bKGD̿mIDAT8A0 TJ$  HI8OL龳K#,& M:H29I 鬬$RV9fyPfb5ۗ{_ߓia-NIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarSaveCodePressed_icon.png000066400000000000000000000003531272363601300270150ustar00rootroot00000000000000PNG  IHDR;-PLTE             @GJ tRNS@p0`Ϗ-WbKGDo0OSIDATc`  : ` 5PBfkvYܹP޽- h{ sl` P(CIENDB`fritzing-app-0.9.3b/resources/images/icons/toolbarShareBG_icon.png000066400000000000000000000004351272363601300252520ustar00rootroot00000000000000PNG  IHDRPJQ:bKGD̿IDATXQ 0E;IJ$ 8A8အ Ⱥ@H]%ML$j*kCWP[;bM&NHëXsYpc7Ae͏869@vcߗSu]3f>{Mw-Y U%?0f„  ʉjZ  C= 9mDVbIdQw]/LY MHe@w-X4+o-hM]lJ:zhBղnHb*g{QՋWՊNZ^oFFSb[d z߼;jg.A荑DV"Sy㱧qu JāPQߌ9V+g]u89}-+5w!D( [P {\e8d-ܲ?kXٜRfD!ov֒ k:CK4{Gx|%F$+KHӡyy sÇc@l"2B"o{bRrkBQ2my eSVzq @^wj-xcƁkTa]) aEKG5y5>Wt\7tUW5bDUŸD;T"yqMZP׊ Ч誷קᎌd;ĶzKFnШA'D.xeuuI;.(4"=]sfԔP#1!@-2#̪8}t%ڲd &$~q?@{ڮ*%ڈ+LmֺkDB,9ysvT`U HFNkʢgiLʺlɂAT +0ih#A<uILiTτ#ӲhvUz&V~t*)x~&RɚpcݡFնݳlN3=Gxa*Jt^7um=aM1%k(`x5L{ٰӊ+h__KW48FZ6[=]ۦߨS˖|&T+)&5SKwD[SoxbIMCJQik/kUS &c6g?64%tR IENDB`fritzing-app-0.9.3b/resources/images/inViewHelpCloseButtonBreadboard.png000066400000000000000000000002651272363601300264740ustar00rootroot00000000000000PNG  IHDRAZbKGD̿nIDATӅA   ዃR|~:@ T.:)xܞF'uS"a3(pbt9acXds<"ȃD'R8=1}Iv)SIENDB`fritzing-app-0.9.3b/resources/images/inViewHelpCloseButtonPCB.png000066400000000000000000000002641272363601300250520ustar00rootroot00000000000000PNG  IHDRAZbKGD̿mIDATӅ0C?Nv7rn-@/JYs@T< WTthiV*&٘~Sw]۱lбT2De2 ab)H;3Q'U]`Z4IENDB`fritzing-app-0.9.3b/resources/images/inViewHelpCloseButtonSchematic.png000066400000000000000000000002651272363601300263470ustar00rootroot00000000000000PNG  IHDRAZbKGD̿nIDATӅA   ዃR|~:@ T.:)xܞF'uS"a3(pbt9acXds<"ȃD'R8=1}Iv)SIENDB`fritzing-app-0.9.3b/resources/images/inViewHelpPartsBinArrow.png000066400000000000000000000052251272363601300250230ustar00rootroot00000000000000PNG  IHDR}CsbKGD̿ pHYs   9IDATxks_wM$AK V'-vvŻIUjT^j??I>~ĵ-Is. I@FF3bF @`l0y1Hn9y|i}_'>S rI;qdq$$;@Aєrߥ!BPA*Bd$'{C\=?9%y)k+K1"DB BRT'J9=#~|^ZQWIx)lD=jU55ej"BP%T*LvL0P_&0ç;Y "7*.wHلu'LfjEFukdqQ$LQ*Xͅ 9[FDvEvl;/qKv+㲪 SiThTz% O^8JI(PVh&Ȥ[E-*zVtT\7g^G閎Sǔitk,nZfSnޮmHO=$* M()Y(G sҘO*Ym7u:wn8N:-tQ -;bM;nb'4bӪQ e;B^=1xQQG ´CB?c"53UU*&,i('7.fؐYW6 rC,YTuʴQ լMUm$qA 7iRͨkYGzi+[UjhD]QYsr=EQMQ]ھˎX2ള^;3>uQ?( -\quu${lL#b+%?tSo\v-\j[K$ՒiD$8H'%y\' T%HEBE&4))(QK5M?U⿬HO=( {Rt>RuU*sX])z"yҋCy_<@A_ ƞ\QEO.E Q0cG`Čq'QsJϜ|a1@k3*LsҶcf63_h lDE>Aɝ$yF6"N:q,P0!P6um>B#R'uu/;+r^AӪX]SJu:qq$ E*)tLAɔYE?W7cqsZrmUIu?šPdq7{퍂akv8f%]͖ZA/A~ToAW?&:ԄColꄚeUWP?Yԯ[K)zu^>6jl2nSmKU #E#MNTrM[r7IoJq#G_pݮE5=9ʾN~lt%Ȍߪ/En4<_H@.UV]9%ay7D/IEƴ?u; m'g @Q{6tL8骺DGkGBgJStOLfW޺2]#tՔw5u=ǹW BG9^rΗv]5ȟwy$ǁiMnԊ:Ç,^1eĄ~a75Ek^ǡaaŤ#*TC`.YU2kymf{YŝwwTm ]wd\?^jƽ i'L!ёt?+PnO̹jSG8|=.DI)|a5ޙ0c߅""U+Nɦ[Su՚DDrAIEB-Mm$ٝe-~7zdI*vrGڪ.Y==5$E`Ĥ{EQ (>>QA-j,'dɲdw|]/{\+fp!cI!ٳz&pjd6]$++yMrKa0 -kM2ţJĿP{n\הy[nO)?򖞶DAզp9B#vjBwڼƭX󉪅d#EM*k-XRT+ZpF&li=?8sʈt-tT+ft Fյt;Srs>-s 8'r睱7;z./N6wFj4,i$Y&~oB!\hܜv̨ت- sInlmSNřĢOmqc6jr/ ;>t*hYK\T/Bt߇"?V2+|jÂ6z^{ʁI^uÂ[ꪃ[/GzC⪋e/סt]fَE8PZ$s-KZ')LwľǏw@߃|{uEkxFUiCCϔ3nkxU5/RĘsǼr':8,ifϝ5eC$rfL9 oH-k}X{E ^rưEsvLؘߪcnxtQz0iJ!QU&ڔ%icZZSؐ!/uŶU#VeW=*D(UC./+QQXziE!8kS!+uŚF5 U ;y,C>{ .Tb{rOg_ ud `iK)2J(+ҊRY%R >YI+Ɋ4*WAkİȄw8溗Mue?N[JJwLy٨]hIOi tZg>A߰"՜0M-kXms`?{$QVԽ>g>hŮ<{eCF%^qI+[r[Sst=Rew3בcGGDbX J 1aZ1kWwO%2e /cfҞҮ.SӆiNRy#>fc5--<ޘJ%Y' *bU32__X>&G/UumMec&? ϩpєkb'4Fgv]GmoN~u ,+ӞHa)Y\u߶]J-NvӼMo-`Y)'-5bu -ڗZXGTll[&y^mLݪk깧+c,Y9:I5qۢrUMcvAUf͊|Zr/f<-tU(5kM}yƲ4z=9X[)UDaz*+Ș)?7T$G)["M+Z{*y;*2{NA96B't%qͨ߫[MebgMnٍvȱtuE&?ZJw"! *ꮺ[V5ԟP m~#V:mY4k{i+~w2Hۜ 3&-i(Ԍb]gx`~Qw/`4"ziBJ-f AhkEI9vP &} hYSUW ˞ z:b-q]0`u¥&QzE7oK'*Fu+*jZGuyIMHC6. +xɒYԼy EVFi%m]CZhIѳ.'L^ K+Nݛ]u׳fԔb ְꮖ·r\[ _ZžKa^3%ao=N{P%l/QtXU/l\b{|Fu5gaܲy;z"u,ZҲ*N"umTb=S%(sƆ96bɲ" YyUj&\w0 oD"2jj|#~M&{WΑȤWMYYִJHPuڸQu}Qik?q0ZR* =MyY & Ң_v#X%-N#9K~cmkra/i `4=_լz߲e]Mb=}p3 Oa)^=7LªyG̏4pi~ܱ- V>l _N 壈Tc*b\yShڱ#w6 ~e̴O4oHӮvV ꁯ,> 1YF(N$W}UUkUR[öփc1yKKkrY0ϸijUFqP|h\%J`w5m{5JZ=ȈUD>U65(Tωj4a-+{{OwS$Eʁ%VUm [*r[=d: e<5@ jƴ Mozm(ԝ'$J|=WHJVrPk/$bBh3n¦i?oH ,7iӺ-mZQ#m fritzing-app-0.9.3b/resources/images/part_sticky.svg000066400000000000000000000021401272363601300226330ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/images/pcb_icon.png000066400000000000000000000012131272363601300220400ustar00rootroot00000000000000PNG  IHDR226@:bKGD̿ pHYs  /IDATXKQϝ! Ȋ Z0]AA - E6Aio-)J2\/6h3 -{8s}=F_zhfZn{iocsZb x`̺PE½3#u#@L^'Mu^M)jK-Uo@Ҁ#H^~bM^a5Cz0ZQӶSWd- : :W7 :<3ٌkZY%nz4y_ѓ/m38J+O,X.N&DV>qX] >4U*BJ6=i@AB&n-zX{SBbl.NNW"c&l2]75\U[IENDB`fritzing-app-0.9.3b/resources/images/pcbs_2013.png000066400000000000000000006755141272363601300217050ustar00rootroot00000000000000PNG  IHDRѥ8bKGDIDATxDwYvsYYYtWwWn7 D@"%K1ؐ"v q#V -we,  0q=ӾWeeU{eCUF||߹o= a*l,1 Z`, H 0Y@h ȌFsd>g~R3.*̰ў`BTTQI0`uYwP.9aSR)~GV5l(5^ ?^_z3{][f(tjLhĠS`" XD&f -J)r>dk IFda> ͏MIA6Q. :ĽッK6>}h7J*ZVlpaZk@Ll )h"#bBP`I0 0H@HL $ bDl"2@dF4l,v8yM4'Ν=k?υƓ[7z{azA'y?oV篞t-E<4U`Ѥ(X(W?88q] Xӧk4N/<|$q~>=lGOnl-^&fe-Ĝ{Պcudh [lrsV?G`D\m @ d,21   $EGQ( J/pH^UV9G=.Y3ؚB9I)YR5$#J# 'Q)VJ҂'Z_}tH;XĆ3EҘ>So2Y` &i50`s\AFF 2"Y dF`D Z Y:L<^:JL|4 :!,0JDր.`AҚ9s^x]xx , @ EЌ̌PFD01 $D,kED!$JA(H HJ!`!@$|I"!H X df@3L  rc mqh~/_^Lp}k:`{÷3AUTpJkf|=!j{\90FЄ0kN_~ᵋhOć{n)-`611yLs4'kҼ񃏴/<&伱l̟ƍ-! 1l$P2YeEk|+7&yT-JRrp`^og9O+D5׬Nm,iJȋliZ}&ÝGu鳗\~}~L)ypOQvt 9*lOη"Y^Om1#@@B0ZIT2" EQ+T0soy}FXÅsXVhیJڵl4jSg^SsWȏ CIcm8ApzY! OƯvRg# zDf.Q$( 1"# r:^B `Bzhxxxx ZHh%I)3}G'\ LIO\g)$ET0jK7&gI1 Z,sd\&h D `@p|4iyh¢(dRǩ  )k&!-Xm f$,#>iOL7[Ssӟ}KvÃ~Յd$ğ~HLeEA d "I!$ @ABDBA$HHAJ!H)%H rH(!!@&B 5!X/-IbH$!yqǣ>\sg¯ߺ5&gO 6Vjab8l7'/F)f:$)jf2W ;S-r Y)s'g''3 鳕Ɣ'>qq3ϴ'+ȯ^]!J'{w?'K7O_Ng|bL3ȬH !-Xdrb ]6:S7{KhxAy֚PJGlVgzCl\ߩkͱoxҳEk: Ze]8ׇ̙K\k=]rd* iykkܾfYD‚B$BDD !329H @B) G+˷2?>ٜ?{"4dptq=MOWfiOիP.sA.=ʝUZnXu by+I),~kz<մ͑2 "@BFL@h51#2Zdv -8*P*ʑ%dE \0~әPHJm &+'Dl k`5ۓ2ȶZi':)a K$2 h ZmfPb hdOK9rA,% JV0%a\GNQ\~woXg ڣ>"ANRhr`ڲ6YY^hd;={/(T Xfd &D,"_# ! D@HB "B5 D%HI)@I$H $\0$d#,0*!H 435ښ$yQ؋g//|!e[Ǎfe&'/V}O(f9Иż69U~`k?*lGyʭ+ fÏ6VAΜ cNXMa<I:y6b2Irx8^0MLtƦ Յd0 d4@hK@ !ey ?{)ˠHcmwwsɷ4$ٶƶJdeUN :J9j%HJ=l6N;㍠&_{xJNi~w|x|Sdqʖ<a'3 0""Xh]'AvV+ܙSs"@V5+^Q̟=uZ@5y*t}Wzq"߭UvW㕨Hhqw7oCQ E' L$B`-!BhYD -X87`}a h,!hF{LX@l "dQ2 :XA u(Y%!$HF:??_o_+/L\|3J]{9%Ihrosc{nn i/,xɃ$׵Ftc9( +a+p~`'nc$HYm,aUUJ zy>yxΝw[;u=|ɣpU,vz3Dh'IZtPp/8=98;ArQy^ H1 `rB-`"TX K!G>d}nf 3㝉jo*Ͷ_ 4?wI5fU)^Q28)+^XB0=/|W]ϭD" ˄A fh;^;<K`O- L#! DD$ IGH)P$B D@ QD O\fdd! (D倆 1 &$Xm6i&L&ǫڑ곗.δk[ɳ}ocS"J΋ Ig~ڰ"oE͵;xg2NG R!*XubƋ"G)om[?:Z>&޿49\̳$)Q ٤$S !œ-2@kN_yM,/G"f@eF$I PHIJH]Dfˇ `9Yf}2l  31Il4u#BW/^_Յ˯nz;Iarzx"g _9;p ͉Saw|:7v FQ 8{#'ySoSmMt4C5pO h/\VRoNLN/^L,V?5&+*, =$`9cu'ϜU/}͋'w<\)% F* 3ͣkg=^}xxcrʰ?1w) gi`9sy㕵7Y=i&\G:JIHIJ@D KH eˈL k`+-A)etG|G++wm5:hvvw<$E" j3#Hrigb8 {Fs/l~ER_sPBh/t0dKVcά]i *y LhI32,!!(Zh Hȳ(lz;ݴF:|%ݰcfm&)Й:#'<8H>|~gzBֆc!\oj=@H $%YBd-DipSuBbt @ J G$Qxb (4 S΋3x   ZZ3DBV@: ТtPHD]Mh`ZmtmSB:`d(H*JR "Q< Jn(P @B,v( ZBDTb2Giy_[+^}kNݜrԙ"16˘ G`Yhl7?7܍/%Y8w^u_|ٕʽwv7o5O5FSx'GM?q<sXk,Fzm|ȓpuQIX vV?xEZSޚj[w>zϫ㧷W>ݺ+eLך9N~okw{;֚ +ᝏ[z-\pU`@RjVnT c1=KW/=~xsi%7\ CERMݽ飏/{ tv;f/45wF`AB&/4Zݚ8}}W7?zfƍw߻deKLVJ(O)IB іDB6!b @`)ʱȌ")g3Ɠ& uFH'<`w;hYXI;?|iOzA=hu}t8S=TFc괃bq1j8>,8]Wo~e:N㡔n>8dW*<01 \4 Nz>$ZAH[,   Da:a= 7?|f&}d16e ɩ (UPTP"fґJ"ZEiSi/Ͳ,,M~6ZW-fDdle"il,$I AwjFnd (H əB l xop=>w?yokkO JT(Жl&ĻTDXX e@"`tBGNAr5t?=r&ӧ\,}G?O_yuGFgrYש7v!5ǧ㳕hZѠ/}#j39slezd3Ν{=>.]\6He=`6::yo.=}|+}W>7noAzS@8# W>w'h;}NM<'^}5y ={xx >w;ѽ?z%EV J ,BT$BI@p'$QB %$ J௨*̄̒$ZUA%)jLW A&YjMhK)I=ZuvYQz{OVݦzGvPQ?G2H.:KbC`Y"[D D% rTQ4@)Z"*rq7jeHGt*06"(ckaYX|n^@("L$>. m4Y[ Q a|h%S̞~:6Q ,]E"*Dfs,uW\b)Qh ƂEɲy' 8n) b2! <˙DA$$p29@A[o "'mtJ޲2>?7:oxauɟXVԬ@/<x87זĎCzbrzK^Y֣[oUNԑN0=s䚂Ɠ&gϵggkNq%TJ<ʧ<֨ON;JadgigfvlvHeژ\"q9Y2EamgO-˭]ޱ&QDjpB6=jk,^QRP>x'?{?d߹y8gi ""! ,ĄHĢTY 79Ec XDlٖ#+13Fc|壖`Dv)%^[^]}<ꮵ:Gt=E'G;E{QSU,k)_ݣ])mxƎedLh=G&Mà 5!h lt|\lmlffB&9*Q~ -XcB@4-E"u9+!DoODJWx%X֤V_1vPXP8RZ+RE'='qܬZv*(hT Hו*$Ah,P4jӻ+zQlQRFNɅ@ 3KF ~:ʲ^GU{㝵BRh !'i64Jyqw?5 !A˂H ȷ@pjjoY,/te{< pyQ2|*AĀ $!@YaF4 l6 |? At &_{Kggv;1{\}b̲[ͅ/^)o;?YlvQ03_tXy8* B:&E٨RERNEZ֞B>}Wʍ'Q.eskm 8"Um) .%w3ϼ)Q$.ޞ[xA= qo+S @yBF,~ΙYQ&v66W)4rڜg-n<67|3685Ń 33 뺮#A9Bapt<woyֻ;?7.ow{,M%8QJ:JB  !BHIDB)K}ui)BTJ2XY 6E3Zd&"I A)$UxKy';86O6̲~SkuUzG^=M 3 Er~zu9~(j vמMzZ}jJkzN׊",ɹ? Zg̕ d`ohowgŋܢ?~pgwkK t(2={ny˒W0  ]䣟|kmcљ<#$0-,cSs?Fn{W_z{OrsXTVxt?`iئF6JT0x%@$Py{QKDBPR (#@[*EihCd@&d$\²cFh@d)JӨ GJTEp׻7?y NcLX#k4˩=?" #\/1ru1uNwOyͱI"ѰNBm fKDT Ѳ-pIȏo#h-/Kɳpށ HA(4rBAd)Ab^3BJ2Z]( 9F--[9eB0 ,X˅BZ>V_(AidDFB@3A2Eb@6U ?Q qYxDJ2%&}J f0A KB[4I"q8=W_ƯoP|׿yWޘYb8ac3cMi<0y̆q᳟/N_v76W>|?~[X{koٟ,Tk-T٘k Z=>yG`bj>pdk|f畄l8JjsBϪ°.PRXo/<U;vY+Awݕ%Z2,HZ$"@`@@ VBd4fpt4+?P0Ym,gU%4&%`5yj|!Hck9!ǘB&0pFkF[BhXd'f&=x`7[DV}+3Z^Wg±+/j.}]rE=ٖh;@1vf#"*t.M2P)J BcK/-o- XFˀY0K̒QX0 F*EjekT8 "[6p4ÕO'è F/͏~['t]itv;=k -mltW3**H(_=!1"($. 7Eܕ^u߯K/ 5q2HGCyHZm3(ؗBHmf!m#hYV# xhv܈oAEa!et2p bw֓tCdRF~qH9ȎG}]%Id y`M*4&uJȰhTfl棾*`NHY|8THI&3 DfcP/^n㣏o֑2-\ Ȁ ,@^hf^_Ϳo_ksdje0"+4`3d+WI޿?tԩKJcMiHo} QVAp?Y˝cUh:N:;>܏-ևqݯk+QX{ahnT)r;ywgNTǎUt택 囙 rJҘɒc]b-aw}|B1蓷㣝sLFYݙ5APoNnFzX]T:JUExবλD DĴkhkA=^pj齏Zb$0HKX2& "dP'ɞ23-}Y/#O|"0A10 OB[ =vBP"u@De Rԇ$%RqA;jm:}Rٍg7֞lI&}\q,͋"֣N6/ ԑ-,.,h Dp+MxVksj8h/ ',&wrFEfY2)J^)u q̚ɂ aI8!~T^}p? wᘁ d ,#K ENq,LMxx\6,Ba6OMhO>Uk;@[8A֞knZrsaB:Szz5ں|4~JiJ:XEI &H+,CII"Y(,Y@:Te&@K}ɡe2"XgȄlAhB.X$”<2R@N=^^[x6qݻ'}T{[TNǣ="Ex40ں^UPad$5e"7!cAx~ǽ=!<:X~ Fã:7B#'O֭NBenDP2p:"O:d`s09f(rD+,sЏ&QqFk@}.FRK>*O5`L^srq {A}ʯNfqǽ X֞JlFGPFuS$cs6V[ښB: s;\nTDzDJpؠ5) Me,#Q>ܑamރ} 9㧹V h8@~+wƝw?Pt\X03Btl!C(!<%ħΜo^Jc."" D5Ǐ%JڬFH0jA3u]-?~r{w`xqUX%_zsv,\,7\~-uEV67w%I B(S.yJa}|nbbklm?<huza1UYh42S`% ExJ3¤Ʀr<EM/ r|$%#@hNRu= 6SwGvg!Lp8~(ח*41f_7ƌYgQm@:0g:O,;D rCb5kܹÇo10U;'C z_zowϾZa3J8&C]/TN%h$_}楕'+w;x VZ/^zVoIߚPyޘ^rtgJxHg緲MNA5nKɓ.8acTl&ڠ{x\>>l]yr;QtjO5AYWoQ:9w^pjc39?{[&qmm1ɰt'oML& s6i/K8hg$Y},U gF($mn';v^AE6GCWzv0j_[Nn W:$℅+LV EO-fI#8N@C(d #.\a'g'!-S_2,&('\DY$ٖbl̷b, pdpx>zcRf]R6ZFdu.Pژ('?f&h|_9 3ZFc !YHbScGz{\c&#na:9wdpk:$]R|«J @Tf5'.Չl4G=WSᓳ"B:WRzk>>1+ ׄmN6HJlrM:M%#x~PC̚P6=ߩ~=Bn,m/+E [d)؞T&R0RF)uwoII7ƌr\ e̅<!?/͏9H$2˨Y]~}׾/]􃷿qݞhvA)W]]~p U[pkQmɽO~pto4~?V+Sya ;_{pݵGwQv9rϝ#3>_UhlJl;Mna{Rɳzg*>zw^"Vjtvf. RhX:A8?X{ŋHVњ-H/ C&wַoݧfw3gyD5i۟|C\?ܺяY>::}965=ntn߿ {jY9y?ںw5íF=Z#tQP)ՄZVK ɰBؕTǛzsg)3 㸮[^̅՚R kDY>~QM(%њΎ\ 5*FZG;OJrD*ŜɭI alG<9$ʤ2r@u?\}síǟnhtfO_|ՓةF{<ʃ 9v7_^V5%F,ob43ƃ}tGN?}z=90z̳!:6[{(KG4c=j/$1\K,Z:};sg^9DծUf[.lQ< Gʓ¯rD-O2Hi0}C nR*u (@`8gIڦ1M<7"k P]Ә_z!RWkJ2#@`ECPfUg;[6/j)/$J*AM2c#ZI \ !$LBI|8n̓w&?Oy2-li:#黎 l7N;TgLBV9F}vt=JmXT@o"Z'`$X"Amd3wa4iv&&GO6T9 谿ښ9;YLuLxtu&:)4:N0sʩ׏n}MWto̝2ϊ κ(H@{|3uVAcǕVԥFq;㧶ȟ޿#s+ci z{Kwn ;v;狴nkNqn{K~?Nv~[/za8~~P[TrazowwtIa_~Օ7>ɩ'Ovwj Ah,dY )29G~?T3ۿ׿^O6z~}{jyFhJ6KOZ6$A  ($#b|y`VI!R+_$ JH˦40D`>uZ$@R_\PWʳ:B ušeFAɑ(6]'f,o%[ @Hk IWI7E0ZcQ# +uP:Ǝ᰿;GY FN#I8rSp="9BUҬKj' Ry_ +d, 6,J A"֔[~95gcuhDr 3ZÖ5c <`6'ɰ>5SvJ>5GN\)6:!<}x`l~:<ctƀJ)G:B )"$%3Zɢ`KRWկ~mget*ôv@JF6hAP~h{GK/GѸ\dAcy+$L,QUR«zY:LPFsU=讍MfؘQzf-jꭩ z\>=[ixkһߘrh|rڪSAhBAfUQnnD 619qlB?-Pqoɳ*k5VǤJ$Idʝw6 =\y?$F2c>·Fg./[JxfL9ޙ [˷xlf&ZZ:脑;<3J3yDؘ[mH' Z`lh icT&׎f83'BȳXUGd3Rʍ{ʭYڞ9=%aJtG2nP!1Qk&Bi>j3wn+[fuQ rgɀY`>YbH*r}cwj=qB(gnܹ+ADCڞ֣{)> 6%;S_ze Ͼp}:X[deu=  X$d#1(@%jXZʴQhOBm($dfDOl[%jLH4nCZ/V32bVx^SIj <E~X  m_E1h(P0K!)H˜"M0lZE7A?/R?da2ݍ(t{rZ1-*D, `6Fg{C%AR( (#4\bTZ(x7r*szK$$DbSw]$h :Y0!@= "XdY+F%d=ߍ|ׯ hHg"{(eMnEpX :;<ܹwJ*Ul˂E.c 9<`lo©nԖ e (\7:JɣCT^߻ɏzt5OoЧz1986}NAyz4k4wGG{ NΟGX=QMjP_pa'VpӤyk@hN-(6/Kc48Xt(F<} tTC,è? k&Zv1>ޙ01, mStaԙ0tXitƪְ[uc{mZmM^l:Vk[ƪA=}??[ȫW+Jȼ(ޑ ׯ|K>g>g>?7ZEUkH3^xL. .>l?|rR lؔ`b.r˓"2N1rHW,p](pbj @ ~?ND>sW޶rBa&.B~|g1 #U8ut@*EFY V[ABInZ#P!`1wW n{Qqr#ڢ* _GؼF5L*5CΜ#955 D d,fRlLӢRi5Z3~ _G@2@$$PX. ]`2""{m/jp_TJЙM.bϯo(Z ⣭k٫G;K[ޮTjgpiґdP7Zpu #l6&ktcɷ?U/)οJ OL?YFڏ\ o}3}QwyB{b2 QERҡJ1?Z^wj"YrVSuDex8\]s|* ?W 4o {O& ݭ-D3Gx`Rj`{?]^2xhՅ+u51WkL~o.?XA 6{*nseΣSg-惼9P•?{|=bJTmkmfs΍T䧃QM]*F#69<ݍ[;O!3x>Qn X}V?CTgZJ |]?Z;ۮWBpI8tiv~^_^ܥ~M[G;4Z< '\{/͟TPiw7W/>sUv=]9 I*%-|-j>RRPe" aXTt60 KTtΗ!`˱45' k,iW3_>>kןoES=cZm8cpѾU͑`DGhol=/4%sFa=5Xqa3>qj>tR>ʐN !rZi88]_<[m77o J-Ý)?}ȳ'`zBgl`}Glk|N9n v=F{brRo ݽƞNO]p+9fڌahv#Rpo4w3s&FwGAHgq} /=Zۙ:- &:j'NHO|x?8vOU.0[Kv70 *s瞫7Yn ޣG7*a{bVj5y9%G{Gՠѻq]񩩰0A8 l[ֱDs3>W3]OGzrO65v|{5JgҊӕG6>4Ʀպ.a9^׫Kw6Dzּ_~JZM֙͟~VJAw~[dZ}bլU${GG0 .|+__mu&'-Aj=T[GOq`K_i.,}ˁF;&3_A a!H)'n6^_-ݺňJȌh-20`KȇFRKȃJ ˖lFTNn$T#3A]y&-ǻ<}7޶onvMN"ő({gB%cbFv4$ޙ;e[4hFWU({s27O.@u}plW oZ&?/^mpcvmwT6JR&SJe-ΞxXo^^~vЌl6,P׵jٖgoW_ߘ;vۭwWUMb04<)AWdc{U;(L=vzsRađNeתRMRu"B<<2wHyCӽy~m&-I\0Ǹx8bЗUs D93gm(z!BX1BdʂƵ;#Gfm*L>B J[#XN-b%ɭ}{gk#g&dBNUH$GQ̅*Z033 1Rq Tia=9$M&˄ GA!LoFܕ$9F3* .|vD<P(J n!B!B#ؑ`a)膤<7̫/3ƃ#瑈Byۊd3E3V,MR@(B0&45+NKD<"(n}g$ +2QL!P,r#!ުn߻Go߸~IUEojG@|1~b,3YCSK(+ŻI=q3=Ddde sBD8 #n@&:kYlhnhV7B)nYq3:54 wo7UI5 SMЬad(%L.U,iU/m6^QZulruoYA4EAB)'TN+nnܝ>#r"̗Ul!MWyzuo]Q\ahpyveo82ear&ge$\yD"լUvw+%=vyUdIדᩩ¨eVcoWg;٨لk #Lddzln[\go_—31v~ƧIԴNR==sDqhZ3Tm,,޾3ɡX[^婓ں)8ໞςؙХ6 $˦\@cH`0,S7?>gė.K9G[p% "@Ը*a`Ɔ<UWzE>bPd5 k*%@@QāDg\DbI u.liF0a"Dpp@%"I0!"E9fBp -"bLpIb IhuFeu{hHҤ]5R/ǾD$(^qOZozX$0&8bO?/Bq 91!08 & BpFBCH^ߨ/N٣'1pIR2a+ žL)7kf%.xvwwl(dHI[;ة7v#Txݥ~~(?2yvdɺNUGL:x#IEIfs# #TAXXWuФeؙ zW8rlaUo8^i(w^CӏYV&) ?6;۬'rV:kCNLS_6EX6jfpXf{u1""=zc##SzB+ M<ɖV}eGy+V(R2\.L!" ln2=:sO|ϿgzG|IO-\^^V <2q霙/OXB R|=B|;7>2;cP|a~a`*AؓT]*DpD|Tѧ:6ooot"!s:0 n@ 8 qӘp? PW*T0jYfI|cu^qHư8z˧Ͼ`RB O(Aȣ(d"1idlUYZ"\>X_?X 抐D&Jru=&*FB΁#BE^ȏ36}*rr#K|w{SǞdsSbΏE5nx o0LDG'ΎN} QQMA4^֨H|/+kMy(VdZ,eN=[8Gµo_=_::l Ǽ^`NQnpNW{+KottRap8:kf3gN Pݾ}O^|?/^{/ FsLL-A젌LJJذ[5DL+1NyEkRI#@^Es;u} kV*N p$)=P ˔$D!U@1XD.D]$*"PʀSH \!`!^ܹaCi~1EQ_p{VR+ MX4!HWdKqs@X00ñݑ3q3I9:tiqD}L xuY`EUljw:uIgq-ń3Έ,D-\u{|~َn'˪^NfKok-]J+_};N;"P62W>d,JFIgj&2iֶ{ݽry(ϕgk7=N$SEH@S_twO+U=*ZApU깷|[޹ril6E4Um򩧞~[__xTL{U<)O%dBKO>rdޫ5mW¥tT(^Pq#XD]h37ӣgn>onU@o TU&DX,j|u2W~eeoOR 50a0"c1om.`"q Lb, ^kxyYkq`Y ȯ}nsHp{ WOFMԔx[GJE_ځ^.YDRzG1>c>c,R:.Qq0"@)* D$qnNbPA Exq2Tq`d&Pe ƘPUEUt"lP ?Sp PXp""f qYT܎cX3fH "(Ɠ $ѿŬT%bIÒ$8b@o-)$3i`L[[\x7~{?AJ01EBIgoQ@B %;Hпq]eD Iϧ3>:fcs׶nuݦ0V<&ytqhhy.4{bF!$Ø#8Q UoWIЉ,#٫U=a]f˾mK629]d1_V0F%UM+;+/#' $3ݾ%!A9@f(/DBjwk(R +85Lbcy(& jCx$XQZIY087S0w"eDַ;!DdI II IRd2SY&nus-I5#  &4 'ˮ1RDaVa @0n#дD:EQ0CX -[Q,(r%ʢ& t&@8 ,bRp$eBT"Ɉ"ZݐaSE6]6dhf5BX><^q?^_\Wnݟj5DPIxMP,8 "^`""e% qH TEdms>?g>>01 ; Wn88 ^Ia"]֥Шd[?vZCce``UK(1H*MZ'Nw0/}M&-& vd=El(] XTb^:3v[Z(^ݱ}E^}giptfpD~X½OW#vZYJ33m9e #(]\%:8qU߾ymkd:zaMSvgϽڏ?kv[I USZFv!o:{嗞گo_~/˚sڌ{;k>TU-=><~2SUe5SO^U7N<1{`x&[QT2_Az/㄄#QJ(24JhVLCCB#W :NptjVQɊ&8}P CdžϞ9:ܭveFh0P@ o±Oc@`Dĕ!Gc@@a |nV5s^`wvcza~NOF N*x{N&m_r#_ẻ:ĘU!"&cs, Dv6lIlJDp,QzMB;(lˊAq@DDf) $ |qZ'H9mHSXQ9p(pvYɚfyخmSYь&8H N"B<EQn8D2RY98GyfC(D1R"ɑA"ݪfv 쪒*H sյO_'Ź?<'׮oVv#FL%*ӘEGg1  Ce;8`ky#D, {D&loVpTi`RS.c#oz./]a͝,ƴd3w{k7ð#A`gօLxl*WT!lfҙb.ukT&MET3LVJ2ɰ\l Gr|02r۬6qD0Rdvph08BU#20Qdl.8Ԇf P(LϗUE\']xu}LSD.aZmwFVB~g} _x/q S%Ui߽wՑcsgMϷ#Md#&npO>SfpGeQLS[w0N?+Țf*ơ dsΥśk*9T*;tu=+zeE~>=42CԴ$[z:S۽zԑH )v\ckmgTSɤx0X=owew_~ڛ}pLg2TR4^=CSF'{}3_?_;𣲄kZݺu⇯TbѩYcrIA ܎HLx|ht$ˍ~eciE%BX@Ĺ8B0m4#r\۪t+ی{a(, eMLD%zF48}TzݳY~ǂjȒDQJ4Cf$,~3p{!'Kk{{@! [1#AG6}D1C , )ۮ1AqA\fa 1"` MQ_7p BpOaljF]x;){Bvx1MUE3,M6  Kջ!C"S3[1U[SfhwjZ(oGO=+O" OeH[d1̝̎nXwv+\t0ȏN ) R;NEUIo,\ȠLF&TBQqЫU6oW{w[ GQl.fUUGz6`IYǎMs/_Ǟ|`@@'@9=B?ѩ\ݹ6Tʖg9(B(#JT{6L.DžW):~ ӭ, i# <8 @WDIO%M40GVGHz*!V-QYM*(DBM33&ԶH)FT6#Mx݃ۗ?~ӟzg탽e/Bpl:3 }@ !0Eba#q|/8Nux(&8D1$  ž@'I4j׿t z'wեkMFOɬ*IF=(Mԑxsg槟 redINea֐{[[{"g2]/M&OVn|vm찬Dp(K7?w{|yC/'e#3fi&r#ᓕյo38pitF2;<[9<>df`j4:04ȼsѮ{ pӬzT 32C= JnyD $" pީo6D1S!M്Eǭ[nfX (XiN˵jsX zBUt4rwZw>ykws8>aXirR_kG#ǛWW&BF Y\=19DAaR'tl/#Sg\kl^Z[ N8C[Z*Ѵ4=ĠDnQt̙5zݡ,9<6.;͍c[   G~iZ,-BMB01  "aI}G=+- kϹW$x(0!" } TN uSBيbI*9@$! `Q( UQkwE$US$h^:k;+7o]>?yo/ݸ5un, @AJbL0"E$@# Q0F >0A"KHP"ƃ 6@nZ*4X7,D }AS$c"B +%.?urrDnT-4ҵ|OA1W cU7ڡ TB3MARt Lnl,]}PÓd2_[Xq cgTE* 1wڝ=Ln*gM[j*gdS̷{ Ӕm7LJ&f8s҅'>[Ru6[wO_{owonl*k:pnnv}g>/?ܗF$U 7>WzSgLg@$$E"T*3Xr`<2>On\wXiw:a8\1&00K?|j/$!N`c `"IJcJ1D 1„|@c!1\>*p`#i$at+f#>=5<ه>2PZ{?n˗FTx3VdeJTQ€1ƴDHg8ף 4UDBd:߮gQeggnV$-I g ct ܃E׵Ctf21w^t zF*A)C4-=0y~rhQL?) 0+C&ڍC !]Y2yI hmnoߛݼq?:ׯ_^.ݾ]k9!P쓏y?]{}~ɈDcY8AQBTS*")FA P0h`A0`u`*G.80ocC#(ź[;v7pj2+7pla ;8#a +˗#rJ,2y&g:gfZun{fvuldQۙtnݕDʣ'MšT~]I3Ikwt4qLtn{P&#gsši,^Ż?Λov^7dM7d4S^ϮۍJan O}׿[>̉X$߮ta,ߺ\^H&d~8X.OeKV*':ذHTBg'k l*IyZ/wX^`gi0>W( 0ddL )T6(n-ܯnVùg9HؤfӉL#ssi~`0iHLܽwTRaESeS7\ ##LQH:DAQ X&tA?tfnl`e}q{'`4 (ߜ}%B5)e 0 " ~-?d"Fmpo8S5J1SOK#M$Qi7d5VeT.A<[O ,BL(m4톝겪 q."PP C*=t'%CSz""0A`T8gE ޓ2rS(EaU"7,-?vD7 39mIP G4nZ{ӫ~q⻟|t.k͝FD*Z& E]`3Dx{ ]5ߖGc8ҁJ0˜c=$ g@om@1o #Ɨ"F߾To=1R<+sivw6W9 M)Z#Bxax0^ 4=O$%JaMjہ)mIӵD)V4T5)SӬv#dMp1w.w_2YY3#S܀k~OWko^tcsڵ"'ɼe%L\i6fwbx'?ReӥfKw߼r ;W#SG\AO%EIfK|<;~3(d4lܻ=r?:weE3N?y)jc42nJm{u{k(uF"Y{=cNweC9=84HT-! LD')ln{V`TD(r{03e@(^=L3RJN:o{F:&D #{F确u3'ktdvT>c'ZVV{~[AH15 47Ӹq J$ 6# " 6`"n#p?ʗbnQ=lz=UM!9d% s I{^4N&{;nF sAeʢyp(QLgdJ0B#@bd%n紛y6眳儢$dEdT̩@  g|i59e$2 0P2G\RM]OInM ",zvsv۵ZekksﰾtKx7,[X>EIjz:M$ӮW,s.$J)"X,y@%, ~yq/zX&" c'1]c;b )H qsӓqNB3}Ր@>@ ǀ%Bd ɲdvG[̎ ۗt%]dkkfk$V&K)`~ s{{v370DG6~|%~I׬;mܿEGf Fz-t-ow8Ȳl$ ^fF&әR1 ~|[ånח5#.^ p;݆kQR̩ݿۿWxlyQrr7j^ٹG|np|i&=gQڌH "/eշmI~ {絯|ѹR\)ﶺkD7'^we+qt۵=͠^}}{mqlP:)*a `H+ﴝ%}IQL~m}tʣaٮ"Y:~rzZ[9 TET5D454sRt"p  eJWLcOj啚*8Bȃ) p>_oBOT޽ϣJrG GJ?ڍV &0C@X !Teu'.-t,v͙oY9B(rdY4٨zm'U %!DbauY7sHznjIA.! Q $B<"TK 햿\HHJ"8p+ȊFکW{ B ^J!`uOIF>~iѸ3{r<8|x ITJkKw0~0@DNF$ uz6BRQoP6Vk`hrxrS3=L%<\yp{%;2N_w% ,7Wn||Go?x'o_vxؓ#˦j\@yvNҏNs_|/O=uۯ^-+޼u3l{xӏ4LR>y{AeU3ҦE3NǣTYz(W^G&sR2_EVwWN=[(ܪVtSʦ9+ !=l&99޹rn{oeEQOKf+?B3jzBєd`rݻ?t=u𘈢z`?5+/ x 3)!c@ * D8WT+W++aϕ&7ɇ|i/.՛]EQ%YH cx p-@b#'͐[W>v+7ޙ_n$d+ OJ,MzN}# E(C@cfXNV w2i-u4yH9jjBw7CkӪN3_V L*dj5jLexU$+!@AQL#+HfߪmD0 1%UUBP(0tCAͽ;Wnֽ n]]ZlnԫՎgB FJ4*ɔH2UMSd Q 1la3FqP}M<¨/@HB@1q!$` ! "/ ߮nAvˣDznX 3[Y[\dg!Su憦5=E(nݻo?w_=JQ"ΖeeoMcCy?ÿů}} DT{[aՏL}싆RӒ6|.f,TC_L$Kn7Pk^_40qD6۩lRByʄ-0 ڭvso`l*=r㵵3>QΕ33NPYpzm84Iy! u371i0$EWͼIEz"idtZ_Q;- MLmc#G"6NiuucfKC޽ulnڰr0%)vp洛z=76s/ q^)iL%bb_FG"~Z_N!`ƹ8ohrd$\DDADi4:25><3T.Y tѹg 0sϰ}0UT@HD.3KyPݽ-Uҏ=z&W{v v# "O/,/Ϟ~4:`vUUOfƘb~00ȑ`gu5ɀX|jZulZAV鶺aDQ I*#,"~׾v_#$TVsEJ-/H&gooysﮮ>c?sҽə6Qj?iip6H,E"lؙ}b TNKn 8L|\ӓB%H 6+b~zUӥ b"' b m!@s&cYXAfjxv|lv̩Ζ&OkDB/RĜ3b2#` nlUo5K+kۛkj$d6Sy!7 e 1 Zo:@Aj{=mUn%\pws;B4:xg5#„(%"+ݡفDj0npukih򉡉c~{oO3Ós'St#N$k$V^YxG][m]ףXUTCQI9 vz=tO}f>+ %+ee_?O (O U65CJZWn^ 9P;'Y\;Dw 9$;(cnd{xxe#Y+sZvk^cF~v !#WȻ=[M3yf6?$$$32syP@wk^1i@jqڛ?wji೿W&/?{ŧ[ n$ٱgD~wBkoqL\_OM'fnCJUqwk2cs&|q{==8uC Xn]_[T7F929D8a0(BH@ `o W#1c1( 9!cAFDAEEJs?Wԣ_8ۭbD$|( 8C* c}<$ɔ*<T޾23s#ÓL\ BHxv*K$J "$J.+@1nۮ׷["& " Nu 0L Q#((0%$BosaCѨiRx B>wv!ǸIԧƸO$,ADLMP"#0 >˲ '9:35P&NZ+{[5ins3;;+uEɖ"IFegWՄȉŭٯߺ{FǑe= #!(@Lx)Y _qgyK^1O|!+fF3m{[ݻsbe}.dXK&p^݉Hr>;ud؉NL0ԤkʽˉD!y7?~ȗyHD&geDsΕK>YEnl,]sk./\;l0mn1I-PA\\"=.zG\Sd{z3OjA,$ϟd[valn䓊1v6nߺ oGM$I,YQWGfaժ\Sw\yW!7οY9=zd6%*^O|advUp1煪N=t븶9X/z'FFF$ B` f3017 @ ,~9H>_ H$a"I$2")QDNt7=|gtk0bI]DĒg")#YƼe CF:Ev %J MTMe,+nZZ$fxO\\_\l./"8 B;Nm6,T@4z BC5A@9'!IePiPŭ[k I_}p[*cL(&`,dEz O}щϾsϿƇ ;y #0&Iyp}ѭٲuN]W+竆ɘO}gǞyjvzʔIY;zZC",~į|3_/|74Q7`AҬ6vTS_He K7.+r3i|w΅9=cYk4gN*8 ?ŏoxfmkϜzl<^BJbn֛snC?Pԛ?/GZtIkq^_8^j*葳sT;!Zߨw얢d.ϧCT]tőѩ+̝yLQM3i<> 7>NJ١I]OFvSak}dòfK-ͮFPL,cb???o>c)Klm*ED3q;#i f+@B>!W $I(DR%*ImZ񹓑D b(ĘVv/ܻ:MM)F"ajaRUpS @$$MQBBe Q)qF`kwic;W{v.|& ETMM*ZP5`jfڴѱ\P$C*% GMOD" _zLrY`07X޼yVE,cŠ~$F0yL$I`Br3_K1"TC3v-q<Ճm@3M}D1LWW%H,- MQI"wN0`ӭ;N2?l$-#N9y`Dy9PYVX . "k9o@\0ғ/<=8M#:q7W[u~kzX>j8<~@ݸSŁ;w]*Ke8^;[;1ҮYxBO[NyVOÝRmW[S}?v>{kGR_Q"  $9"| y (6Q{ 5|GvD$4ΟGjnmHhr25 I0BX圅^KU͡cš0t(S ɲ(D%*G{_^]wkt848:<<)NZ<xf}ogwwըO饙cOUMn8C 49a* Is xu#ɇ"6k-偬lAcݵ#C>J,ɒ$΃$w91AqBs!||?S,UyOdjZuQpBK rlx( XRʗY(Rۧ=y"1D:-?w7}k+ɒ$I1j1s$CX`$PejQEsfv0΍hHXɩ'N={O2\ݼѪw}q:2S'O<>5zwa麌`zioFFnucp`r8Tbzt # eIe.xgS=qkA'hW0̔tۇ6|Gejqf"! DjeNk4f JRqp(ˆ<E1cKաLbkcuk\S D_o'C)T|kr 7ޔhsةlqvoV^Tf #%iHDUnf]@Q ]7MIw#wc_W+/?O?Ael*40fBp14Nk_W翠7_}2Qe+WeUn]T1|ÍFVYqeXsieeUH" ʧ?g^=r[֕[:uVS$sd1_pLUy,LM=iln-fFFUCI& *Ϟ~ _ɗ$IYr{~ <[6d2/im U8#Q n]H8Ώ`dG4 svp{&(&z/M̞,^w]c{ةkB8z~e=,bh꽋z왩a25=;0^תoUwV[v@J*Rt3 J*% dMt-;0vrq ѫoGT"0p S@!qLD#B0*Z o?+کlDzk{CgövTGg9k٢b)GoD^T@X7weZɒu0RT2W mF5ѩ?\4lR~??r шPcB F@9 ! e0K)a$?k//l.g) f, ;m%4HD&0B5/-|W-70t+( 0G  䶽}{?Rbv,Oǿݟ5B3}o?|0W][9r/$Sɕ 7ν{ݷ|^ִbqx3_rW?x3XWGn?f_& ￳{W:q|xprjc#[B!wאx_NZZ;^t}\Bє|>'aCq fc@cΑAj1ʁGcN}0Iӹ !CGyyY 9-o_nDQsOW͕wm-0-3LӔX"jPX:{1 ԀH>$LJVw kQ@O=]dT^YXeh0p8 YJDL)bZ*mf%L;ctv(%R1#tZj:wz%6NoS兹+IVoprrf̉7~;ՍɁoJ&/\>~rܺ³,,y띷J~~ZjF&Ɠ <3O?=9w8~٫H0%!@J aL S{ʝߚ[j6w!䄒=Z4IM@Zha>Kbs5J!v\׵,kNYkV2aJu*dGwKGҾp '=+DǾZ}_Vh4f{^4}"Ԛ}_J,J|OrY hJ;I#͖ҪjT,ϖ[Z&p @*՛MgzMRUw,;?0H hAfPZ]k:4{r=S;lR9}⭳o\YعpBND2)ų{K[~xOX$8"PЍH&BX -Arnq/_/յyPߌ{Cєm f@"fDx!9'l~i;QӉ:k bԚ m|OH;v^!tjVS`!t|=}w<, ~s+QҮa @X4,PI Qid&h!B[XI/O}|ʕS=;{̉3N؎ڶq K07YZZ(BZQ*4+ccu cO筰2^<ɣF)3:'#$zza鷮9߻c|ǞTvԶ{TX3?dmec咕Hl6Joy6)D$:#Q5h?` o9"3R^[-0Wk@ŅGjZ߹chǞXӪ/]Z^bdog`ĐVz͵#/~(L̤Vf\==|`CKw^ܙm;Gz`w^|ժ&{J뉉&vߪZ~jծ}ks~eM-\9ʾ&ƥm~FvZ[/n}iK(TumuYOL!Jh4 X]iz?}w0ﻵf-5SЄ t䕳'TD @f{b ScRiW.n6+;ݖLeݶ;+u7L2V3C;\ [7_y_[^[Z^Z}@h(نA X1⍍#spe0A58Q<TXIUh6[M3"'M}}{q=Ȁ5GbVZ >+X1WDwǿ~fY_[[\X^XYZ^X_ZYY]_=C2lRp7dT[kK+g kkqf:;:46;<Tk WO_P*"91c|瞡lby}¹.8sϡ;<:y̩ס|wko=7>)'aϾ03ofm)j8W.BmԷ߿pandװ bVRh~n?;碡ajwa9Jog:M_q3:YdqRDJ65an}yX(;ZA|Vkz.˰H&M#=%p"@X~y[x '{Zt&鲏ԥ3kfA(sJX Ġmhvʺ)}Bs[ouJ$Bx283"dAD́4!BDaj$߇|V/<ZceTqeY|aveĎ[z^-j42:52-;T..|yskar?bpⵧ\<ώ Nrf5&WF}DOO|K//WALϴO.ї&{ӕs'gG=2~:Nm4nGƣh/.mm1jVO{}#sgխ\f6P SJV\]l6*!+$CπBDqh҉dFv3Nl :z}Լk緶Vn~FHe^׉:3V͆u@wsz_2{3G/]ި`BXs}kGyezX]:sµZ$:d476{l}߿p/=Q^[ ѩ[ogbbw,3 Z/_:㯯/|2cޛo<*6GܾvtSUW_ۻ{?}6V"?ⷾJ5̶a4^ zpvv %20 PjZuhCp7JF2%@*[oפa͕kŵK{WK;n {7gOdnsx2;inyz*= Zjcu> p+!-ӊ+n% @rU JYZ&v4-l6RO~gNcD:4`08b\FP}`Qi H5kx)[/OU"J$$G/D дt|0CGP}%ͦ #]k?z /׽Ź #Lp,UZ- zS;nv9;+([ǯ征H,=k32ۺ_>w5طK(V/_nýcMvgFFفz\\CfDi6V~n0;8w^Zr2st̲HTTHO&ƯO+ձN> թP8?kVJ~(l>0N1C1kwjjqۥfIڪ^;vg(=1njX)n66]:sҙ>vR2earX!q}07 ۘFTx@HԨYhACzSkTZ/H:އ>,*\:,9`3V>! mV)bd_w|@!A+֭׬w{Z(mkxXޕեsW?vKo{,?6?70I KΞ]67sݾ7@a dإʅNqd}n's]|F,}W^~=nc:zO{TrJW{z&J_ܿ[ȡM'[Oif̹Z!+fŒD-EGf&BO[k?G?tcme~|<'X(j)SoNMƒ: w/ pb`~6;v/<+XXvpY&;6][8ntL+l+*[䳽WV]Hg"/Mcɯ|=dXkH]7A 4fź[4) [Ҷ *ph_f ,20 F YAiM@41 DTv76%+;=R]ڸh+`WsϜzcc}idzbpjmj{u;ܽ}[8זfw26ݭ.\<)wxlrt۾TnP[ kܥsZg`r[󋉾5uFoJLΞre v쿣xk홰lLDmcv-2[*\8<Z}]H"뛵չbx`8&!+tgν|fdO:N0s%7ߚӾjתf$#,Utdqٹl_/gR4N$L~C|jlO八gN܋_?wxv~faئBll,=/l-,On+l>~s#};$ӛ/|_Oya 7bf,R*n_Koᶮut2S׿}?ٿo_y聁 N\!2hJ ́@xxDs-gbz}=|8!+jYY;] J  ) c +Fo#X,cl)xfm#iӕר ⼴Փ_|qS*C•%ӈ3f8Y{2P(a[_|ŷ]9=Ԋ;k]͊H@RJ 2 "A@LC08|E$gf`b` t$G52h͐ o '}X 9E/[ھBlxJ[F69*}Frjw]rW>7:-uZ9mvfVZʵGmÐ/w=#Hۯ{=o|ok"ӗLĽZAa܎KN4Jfqȉdfgމje\St7L*842Oݳ5jfF !u=7(ρDֹxiȑO\](֚+\Z^~vFvH: ŢS+o _Oɇv:{_}B{C8paYoS՛oG?P$9յ'hţO{_?wɛw%b/]0ͦb[Ƞ@`n,ز]"0 \SoO ߏ;J]X'Ȥ2Аi4v:6Sv'CT8 Dສߔ"T.Y*/-_9f+qRԸ)މtB'UYG" j:!6فp<>wnmXxg~ID']E T.G`Mu#@r $8>F^Fp'J*R`׳w~ an\2W.joK1o+M"lۉ޾Ǫrfp0YT6=4s= PXqU+#]=lznZD,/̝{ZJ%6֮?7i;r(o\_]ٻ'0d(Qܘ4ʆ060od[us2DW_ʼn݇^ rR-]C8O)DVqa#_"!'"D8Md{D̯wMwg{Ǣמw H'rcx*wg>/G_Y\1'DcF<wX#"`$ &Ap2)'=;=?8gw7/l->>mp_k6MGdL7hƀNI0( _A/ lЁ2)fӭT[RjK;{γϼrlTu]ŀ rOW^SS'=cs/|o|}ݏ᡾|W>7 ;~wNm9s_Ur{fH$}~+tv;߿CdX(;/M8h&}],:$nUmCZUкB, [vՊ+K*HsIZvZ<׶|m N\;~[쿾W^c'N;aکfz__&i41׮ӹk0r{HpV3NO;]6_`<Mb! SiVat+kn6hFH@ ʎY趽Z14{&kV3Ο(nbŒжBf_ndTE+$ "f@bBZ3&Z`V+Yw=.ֈ$OY&&`Xު/仫8)(8}^*nk"~#WO>?7?@oQN:4wro!ިWꞮv+j!K nJRܠ$2iOC~Zee6M=̨M٬W R}X'4R`DB*[1ho"DPK 4ppi FP X 53# @  U9pwN@WB3=0{.\uQ\˗=d7fMn?fyfgE8Ju%Lho ,lT^ivy^qpf.3cʉk?ƑgN,,l a$pرIf>k p?Ai QКNbp:F Ȍ*hmDObLXm*uNTGȎm7d JD$+ћMC ; 5_OEB0jA[FI !8IG$[x JiZ&wN^zșՕbGA[yd4 g'|;ЏĶ+/~^C?0qx/<鴓bI7>>pubhf"|~~{i>>/IibV 0 L4̚P u]n;x-q;OW1]²긡h M"kZ&6,Kf}$tlkg?#GNȊqltJ_߻gzm |1 tSs vюčN"&-[J)?iVcf$na_Bp^?{J$9= ; ,u1nsRW-`D$AW0h@D@R#x](:i.'B1T"DhH;/;y$mONooWfĎKWh<N&S,4P.mن v]]*Fv]2;sގعH<j$3sXueZluaRLQZa[+Wu(4Hv3X`IDATKBvjR1)bFݬ(L@QZW-qBvV- q{dyc٩Kg/z1N }Y^d"N2r1MX0r;7@O*O2t_̧>/}kr#$2DĶ$k*4uqf54k@&9x!($d 4uh a4SgʈןܿmCSf-_)gsP8J "x&7b Rt""7d(|ЉA A J G]K7 |$AV= @Aԛ޹ />fNsɶB)g_{\hU볪݊oޯ~ԝ=cV%N$3Q}ۿ{T.zSR+.'+5CWJ3i$-PH@: 9P @n s{o:x{z5M:C#m3bȄN8x(wbP(ڬFЮXztL'Jf6[Kե+]ٛ$32BnL={'vzy^/JˉdZQhd.`$:MR:$#'fLimLnuxmvv%IԵSP#ƅ AEPݯ`aҭ 7i0wE:GP)!n} QA ^݃1 8ň mӈF#+G=7Lށ*f3>Qm3> ΟB1یyV0kvFܪ۔iWknaFdh#2;)xtQj'BdZ2Hn?;| 30.LT/^r;P8K Oo}X پ(ҥޱ[]ݿgMu$Jiv6g:%7Qsg4NIJ҈7`ptth,:sicu%K{_3P"ZFIHtH]{974  bfbd"p5Ekm ?X3Q 6ABW`FPHV+%V1ffQûW ĈLQxyq+?~Sՙz5VpΗj=OFχi0QR7c=}T 8fpq'OVM 熦ksoZ?#cG O<5H҅UW!\Og~gq}oʌHڵZ~4\]⺝e[ {`4tjV\;L~e /_:r'{Y߭ o'>S_XoymK 9Nؒ x@2*_KA Zր>:@Ҩ A *Aܥ0"*Bhl0b[ OzV=]F%.;90=og|5ٞ$`@@F-E EQk0Hw/@ֺ+~@5S&[s Ai@kI@]e@DD2ޑ _ZHB D+`6 2 4֍zmqsjH;L$N$dP F؎B!֔ u' 4s$.XR v кGY9_('}KkGRay63wڝVw=T}Xί,_(W[D&v1<7ONvvσ./GKf='2Sml3B>qbki+vxn@u /\>S_V DIL zс̈ E FA}o^b$c`3Sj0*DȚvAt8@"JAw$PDfCP(T(7Μ\*zǞCN毜* ZI9PP:V42q;\_4q1mTJ6f&Vԛj4afdXB27J`ێtZ.1mk`h25+$aؖ V(ju.yG &H4ờͥmM ksׯ_<_knoy`|`:3(_/K~_yf˵m+! !`o_gb>E(ns&)`@ FQ,%n6 fy97o ܕ3׮ΤnAџf.ke`!4tL-G VD Dg!$I$@K) B]t M)ö#$* A$H@ !4$$I"eH2lǶm+deضr,6 0 ئD#P,b;6 H)Xo Q"_0Qf"FԤ@h4[;vؑp4H(SX_ˍQō1StϤ ;,̸CRD *f~Gj+[+3uZ~9HfJEaNhm QhhJiMZHb@.y2_-e{{ i05a`f]&`A3 IRtj] $qc:8 =?WVp[:`]\/Dpި%` " 0Y }qo E5+WF{RL(cJQKP\ast]D!Yy}~t\-9{ՍvMf8|*O6:FT\\]C I'^9MfɬR:41V%R..wUmwozL3bґprl鄥v}uTDӥrىǖ.6;kZ"=844{xhttd;r?83FcT"Dli&Fh !`B`:/X]k` MF6@C"WA$(P ۃqDh*kYV:wZGviFYö[z4P\ vT̐V$N[jyͰ4fӴj.Ԧ ;VӪ;Kkg}'D!&|󙧞 +Lh]E hi"-$[UF<2(M):Ѩ{+v| I"hDo$" ]D$$۷`@`"4C`) 6UfDY+i¡j?yvu~trdtflW}tICNBY/-/m%i8v$KTgO榄KWfO8JHAڐf"cKG7PZdDQXi窥zu\Xux:_43#Ed7L! !u"ZV.^|X-9&V%l@D@4SK/|so~˕V4Hfbdr̠C5 @c P IVQP`TDJ)"hbT+uӔw˳U1 -4hN؝ÃEibjr*ٹwnμbތ^w` ݽYH |CEh}\V $iiHa 4P(! D־nwN:ڳcC}wrksmGa)PhҐmض4M) ) I4M2-a!fD&B BX"`$vB P@/|BO&cKC~;*,C0 oruUn7Z4D* >^$o|͍&x<>?(@- mj Н֮FH.Э`S+bǍP϶"bUJ$̸3R\'lje] s[ժ[+V8ڬŲV4KI$ 3}eƢnOL$Zk LTʫ>K`dS1]H@ k%v͕{w}#''cv^5zv}3JXm]"SzV+F!D10i܈w,bSD*2^:@D݄;Q |̀(h@Y3gμ}?`RtnxhsO?tf\'f^ڬywΩ݇V܉gK}Loߜ_XCnUiTc0$%(rj43t K3-./IӉ\,WV4s7FX"\]n١p;O}>o_Y"LgSL:  b , Ca4 n @@.N AvR\o6|!MGdlȠhd܅1 @wD=k}FF4DBrk~~4{ñlXf} @QM+`^8ȁgo͂&B)!!IŢJR횞xߣɟ~?ӿ\TںRHl䷖J|fcؑ#z7!d) 4uU bZvUОRnrmPiH(f7R P }51(aD@ E )((č$LȈ}a MFq+QKRqu|"Kܚ6ӹhg4iun<KԚ۾!өDh2[U7V S"lҬU6\%+ ^'ñv -3& |]ZV^5+EDt 8f8{!CBM8~4"Bdb͌ ;Asbq# =>dž&o[[o>B=wi~n};y$fVW676!lnwA!sZЬ5`$&m\|v2tC*雡_yIE>3]oksnҨؓܠIwZ}!9wZDȬJ-H\>B:4#HV+[REJ'5<410FimU'HfT ׯ_x;_}˧wLNMez5G/{W_2Lt2uPց@w@ "W DH) 0Zfh4}Lh8R/-Zm,:02,+)eȈDߤnܰ" F}0Ĭ@*؎i@H$Lh^˟zLa:ރ$$|ń0*ШA2.fg"f^ vQT"ԓ5{oM8+˭Z0",a|,}_ya7gYrnk۾R!$0FɌ0ڙxjޛ/<7qIw- ju\!j?iBA=k@҈UAAEV DJk; Rsh[ K/UW7opvCҚvrȳЎ$Ē[KkV!G%@advZY+-90~6.v,GR;NeET:$;JR+ 2遉h,_٫x,i" DYD=!/f'M_ nY,^ >~^.~+.O}Ke};~/̮H.ʕr*q[^FޞbOC>6DH,BO>J FŬ4hx]H"!!J b^ߴ~7꺲L2l4מn"|z˛[[ fPy_'gILOo|rx=BW~Pط/6˫z&-_ck``j_b|t%飇xf!ӯҩ7[b("a)@T@,8U4`F re-L55Jŵ:ߗIXkJǷ`Ol.nj;c?wv[U8sb4d^ڝQkdMIx# ]PL]MU Ɩ?ԍujf҄@ju|@dq HPyvlNÖl&zh϶;vo߽wxrjxx*o#Zuy_m3&EwWV(Չx՘pNl$7;<6ˎY`ޙgct,ٳٟp~&&cc)Y!(DEހ_5(o6XI% LOGEb @-_%5 XRSgVކ:7؝7:H:nwBiiQܲzaOR͚zA@:L=Fą0}%fiQJ3ѯ7 >qJ`H oVj(fOD ]%F%O>=h:F2XR@*X Zo[Rުwmj͵zҶp(ddV4p"n HΓh:D~졇KϿrinqؖ{ꨯ~/|b. Y=ocK$VGC*I'3f{61@ $ܕddSq|ڟ5v8nXs}٫66)wc?|_)S`_|mw冊h-|puW;802/۞"r}Xn4:}ûs_Fܵ#lŭ\=\x޽OLT󅵍-]ϤtsۅrX&hͨ"j HRQWv].3k0bn~򅓎}=9iZۍz\ضHLo.^_XY^\,mh,4smv /o.Rh^ZGLôIڡCޞX'3vїV<Ȳ Owcln aD< ;) 0t0" bZFrL>{ghw?s?c:s[n_Żo=4sk_^=;wMo,\$"]˩ mp>B]@Adf`] d@(lȀZ"y 6D F)L$H.jv]y,srV<¨ݖh[i+}BH@3u&ҁnOH[h*cܷg b5 Pjv*e^[ 'Xr#+W11z+$f@ѓ5A)rA=FYt\A5F$C1TfV+ܮKQɅ);lI۬˥=DYoC.dǢˋ W&<6NȲuLZr$٢ OzW_RJ4aaD i ǜ۶RR~o}_} Iv< ;Fn< k`A,RO2?^rrS7 a+&ѯWDkns% KkJ-;z}{n\_ٱ?>Էw| 'cO-gߞm\H#=g{ytr^ܳ/{$gXX)9Pw'뮡d!( ?Uz?s'KuۏܯOn9,˿W>}rP_:<~gb{ƶkތ%>'c򫕲\tY:t=nPqsz)Į]:'=w׿'|zk0OgcQ'$k}?+@&̂@kF  Fіj}׾??~'.ݴNls8LƦ~i򦽻zb%P eow]BEJB]tA +f$|P jFXH$5FZ) HD_6gDs}& g\kҮZn(iZR÷]S kni$&R$,&3[ 3d. ̺Ѭ=FZŭBvz'̰Ԋ(돍^z˵z4vv\#? q,!k9ێfS=oj'sف$a֊XiOkE jbMޯ.Fas Ԏ+fn|{߶s=;}wc8m7=10{ dI1u`B]%)gdt,+֊K+WxSO?7yr2>?4V+j}z[nڿ>se~e-__;w:#mϜGOnUܰ찝HerCtl|z_]>fakQDc{rX"?."Ω|'oW/.뜈'{zҩt`J{()B*i%ZuǠ !-3)LVh6ۭNnj$4V#qlmlv랻οʥe"AK |h F B+&+W7^w{kl:{l~v_{CO.;u}~%_9sAXACʕ`r;o../ܗnGZN5t,Nd">3Ŋ=T*Xb>8p/n5FFefU([']O>O__[NG,e$ᓯ97>wq=UՅߩӇ鶛o:;/]:[ B>_Ie{&LJ.[Z/i˔(A#u$ 5cדMde 8R_4ȗ&b͢?#4X*"!iSFR(V y@O{vC#;&6ZMTȗy1yD,D( #idf Fs]D*Bfrjk$30k~I1𐱧K G=}y~ks}nzYm6[G  P#jMbP}AFj#>hԌ!mifUUVn̞~}B|>̘ɎBimmiC _9sD2 +cFcj@^7XuL;K"Q4'7[X̅cG_ΑQ׫za  {"g8Ivl:0NBYk l#m~Gigʯ~COD5( TmUU# [(pm=U,njPDj[z+71uݷϝ=&G=.b$`t@ 4 _+BDb~}-޾paP,b:{9zΝ;WΞ<[y7V/%#cx*N .?wXm?xykO׿_'㓯qvc*x:da&"Š )fAw C*72 jl \'2}{!Wy '7ܱs*޻GB׵J(EySѱ;`,^6ת5˲Ɂhoж6X" b7ZYf'*ܠ0k`d7$DBFff<}7kvm5zU3OMnwpS>-I.)Zif ޤ5 .5h *b*F`fdM`JgL~LdIi! JP(d5"&P귞~;Ģ{|Ś)1ǧ7??lp;D.k[2D`h O>jHLZ&TIbW?" 3`xVmdD2IE#=Do< OrˍpN(J.n-nD A CɌa[`S*ͬjZFuceq~u~n̕zjźUȱcm9B5@Gn9s>kM گj2"ˮێLB4-i@@D"Nfɬ,o u q #0v*BD1t 4L $ 2{ |D7Ѱ˿џKG`_ ۶o_{|jǞSH?[Nm+/`?řgφzwkjx(\~g_y'li:cYȒdҞ G f 1%1uxaF{aeE}?_&넝ޙpe}칆J:iGP[fVg-fYY osiSN6wwtˋMb۸t]=!i=/OJ0s}9IĮ 5 @ UA?@ 2@+ 5jb%XNҀ xp!FYo6DJbپ={`|z<7Idl ٢Ah+BH2TK)O$B@P< X2y$%zA(&z A\ (Ŗy-qmaiAL>if H̚Q${>;/78vϣoٮUKDfw^!ÆEQ2ƷOv)lTPP"H<"ZK&Ш$0 ~@Xx@,wۍT[x^=kBE+qH$Ŝd*%Stn?=l =DÖ}Amh4b!o[[|^W \ Y!0 `ZA "L "4&@ C8нdfphimQk(6%Pf eT%2n.pNfh(Q _w|!{BFJ2ܮ{n[0%"*&J757)dĘ,V3h5 d&FV^pw߈cn@w3e2`ݸBHYmfX*}󕣧{?{o3tfj`/mٖ#?ޓH_z&ǷVp&;0珝8'vm1^|#c9e(ɨ4yLHP] PUxkkwѤɐRʗjPϯ{ɉ 5WSG)uW*bҥU2S-hY/4*h:d! evrmFNӃc;wc;n~T*K̗LO6"hUJ&@H:( ľK`0[U6^p/ڹwgΑL KZ՚{ncjwj!#Z` 2D0} d @r5'4>3V^LDbFd  OAwP(T~C ”"L/%H}066`+7,! RD=^[%m3ogx<1ۣլo@k`V(FT BQa!hJ~=ϼ|T롻x=ۆ/y?>b16kHqkGa[Y=s巎;yAf4D!20(u1YHQ+ҁ-u/g$H^pY0E RѶ G~gjf37`9zKJn![N8nJauTSd& j%*\=BĢ8/-,Uf,=zͽh}sX*:X43&BVq~O|ՌCb$!twc1I4H%F|}پ'>==mHzh #j:y++jRXM[޵5,dG+}D_O̤=:j@%)|+ $ Ui!.tZ{I:D8#jj1IT4V㯝}H6sce:{蛽ז.ñlftG5Æz //c!i5+ 5BZw t"`&,5BMG{[v]ec9W9|NRs1$7gkr M7 4 c6IlKV%U*שa9?.=Gϣk9Q*$IVDlh l[-$h7Q’#\@D+Z:UJ)4cQ*CIkٶ,欲l08`dMH@b}k~>mmY3%m~iDd3e)H#KژФ DX6<0o HANYDqdAOm1 `0 T H=61h%8ٓ~B:Q-"T, h\D3 -\~Okd[?)u7__}Ɵ{*N;p_\Kg^^Yo8y': 9-H kͩk̑ l⏜! 5W2Hв=Oţn{Iy%boC6S"ǎͭf4*rT:$Q^]|i-c7:z[{ܳI.L\u냢_׾WVv K tvwdQDd4/ckbL h֚`Yؽ#)SmZ:0lh8tյ@n?8Jm^zt3{2"M 41@JęXd6 4";+p96F!I 6+ ` 28T)XdjÂ0{qRi˰l{*k2E-T'IZ{F9viJ[;t}zw |dKrkgS.nไcIbɠ2Ny@ﮙ ) IIB ri$Q0i@!D(~g< 0DCFk %98ؖq3^IP FlF3"bQQ7~pD5IQG $bh:>Qx&B6C JE*l(R/$m`uTGÈD1@$ [v b amˍd/.]}3&j"T*%dҌ:0dckWfbθl?Cz;#_SO>~[6׻+~gSPaw: Ϟ|sϝ>q哧`Ig)02ZYr(5q(2 xҖb`av(te`0zϻS뗗?Z|v܉HG%ɨg!tg+SN4\v,b^Aw sv!l.Ev j17q{VX:aڨˍ^ anb?c p3[I#v(J 2*("*, -4" &̐FϼO0 ϯ]!kAnX[=Si-/Ws󥢇dwu[vO۸]|2wZkRڕn a0#L!R"|IF>B$fwlb)$$m6q]2b8ߛ QڱEyZEZg} +FS(h;e$b@AAzy1p ml+Nkc>)FH*D '~J 0f a0@cqN=KݰS(kY ) #=񡕁U,Fu5$MO tRk$`@4REߑϜ6?Y ¤ZEehATd)&R{c4!6dUbLAAtQ3L@m $a t A*e'Q?x~E:%2QCb)t2H8^.AXt|m͕ʠ Gi ˿_;٩REA dŠ " ̤0fdO`YSKȜ5al2d$ B30 @a) I@d ,`(,0cpCG̾?s~w~}Tj[AJeF~ Br$+lRcR6TŽ@v%QzR<%(', Q!D0؈ $4 RH "WsʠL2j.I8:RU(NO@o.t<v!)hԞ%//̰NM A5]b"Hc@DF`suGw5?Tf:vY.*s W^  QZ sקfg]UP3<)g(BB8 D"da{JeQo\dVY1fH*"v`J.?JӾe ۱h@dl @ R tBB*DdM1}ȶ@ ~z!$a<4l/9afkbc4;;;~7~/f@`;2M*Q7O$YtB,H##,Rqo8TIDV`S?%IY)`BJ!8viT(؀1Z1"8n N[ETij,=:S}2W a_skq|{Ol#&-[lm8Kc$5D#\9{`ݾUVks<}|im(rJB"fFy3W`1 ̂Ra&C5g./fu}sݺ7C?# C~C_AgckhhI wڭe>spf~@z [*J0iOLO6CevZlұ C)euK<'7_|ţ\u8I _}tT.4;=˶ 9J`78,!$tolw:xOGDÜhWb=R6قY$6р8כV&iqD![ `g2,b$-ܲ#+^e|8ƲWV-RB@$Jpt6F$t-0agKp] ~7mgꈬ8MB@$ qz|=u솫ڿwϾh8` -9fyu&ʖcy3~ C`@*2 1dʑ4F@\CH̠)+dDø}vH,C ;e/..}u;o2@7I;F)˩g(ahNIpA R✓9Ԃf2ьl`E&`gIi{(D~BjܡJ}r4w۵r6NۗN}-)J{ɹ^^7N a6 RQ" Sy|yyۛ.|3<3$bH#|R&0x%(m mW g'ۯO/WxW>O~;[nKe`m[ި^]A\tش=/(M$ci '%ƱUc˻6-/_ɗef]N3bZ JB15;VefB:^{s-U}u#vIh13TJ&q/3aH"Z1xcJFyɂL ɜL4KоR/y7emciZxZ8d[Q6!AVj5jr%?WtT*,U$2*am k6Z@ 2,''< c,Dz&Qr e鈭˧z͝bm堿/ʅwՙ]ʌ+-Aj h`:zw}5,յ(mǖR2K0*2;H,zY?'{eyh bXJ$FfjY\M=Q#1#y(0e{MЈٓ`kͭf=~#;^XMG0;=oo:w4ȔF v{ܠ9° @MJvKbJ_3o.]v` jdbe!fNf/T 4Iն®=:+4VN|hFja[FyW[nwVV6M ZJ zv:vpOG~[nWI2 Ȍp fԤJـќ( Ludb@4mE e\`06Fe`6iI""BHLML²-ZeDZl_)rS/wDb :t6v,H> !-0P8I4v=Q0D2hsHYdݝ׮H+p!D *ܝ࡫˗&&Ϝ=}? +X?3eR%B84D)o5&qSedmD0f0L 1",Nqv:{O\bⵕ#$![B̳$!=a;QuMZc>aJ'_~;|?oh $Y Q CʠH *NSmm;m2da\N h.3ĬA[(/Hzvޱ^sҩ;Khe]{ocǞ~'/qD(H:%E:nkf"<_:~^Wg!*dԈUM |R|E$4g^XL(%Zqo5nOZX\Nsں" \<7ݩng8 (bخRCBPEXRvN4IH0JpL\n9+IA։](XR՗;S/*Ճ[߸tb_y;gʗ'}kk9s$;w;{ǪgN7G_xO{sc:~ J Fm` s RDP k# "k`Lul9\UIe}`2!Y)5B( -qeS ZHqkk)KQ?LUksJ;VrBz*LȲ]0葴K6$@['Tl[xn?jHagG}#RYe@cl0 3Fo~+M劕az(ڝm;Š gPw`، g;0pyc`McϺ1Q069#+{DL CWAH4(ÚDш AFh{coyO)-;#a$aAtHXeezbI /?? [Jv/DP&w L8Cdg!4Z ^{;AcI+ h-AYE X[:k6hփn\;갿vg^n۹x馷/au{-u#xhe=8vl:a61Auiz@B4Fk++ήW;deÈD"6c*' Ih؏ı$o[?~]wWwzymP/<殽pf8c҄%m'˗3`8h.v^kh^Z<|ͥj!6-I`{-[ٽD6KpФA~K惏^x"vn?yi™]}ٳΞY[m]WZ8 |?^u×=Mm~ګ773[[ۭT-sCa| cۙl @:R~"3>"@@$yJoļXgzX0kČ,vmi!Ū^_:mҨX1m1ٞ+B m RmL!hƠp&VjO!k-, Dt2ח@,;h,9SQERn.Z_,r^6lBTL5Yp`* j%4iM ~t0r BVH̢q"Y.7?|.~o#7)a{!%)Sl JnsuhOÞ-Br9֝$IYT.aA`{?wQl/Wmk ۵ ͕0t]w]w۵Kq2d@!-c3-{nj6һY@A(4cYn gK±1e< n7A.E&[}gΠJbt{+W=rtWMe7_&/g.m˷κ9>CVNinm,hr>aBtrf,~ ('gv 41HRYtPY>sgv˕ܾ#s_~GR}aadQUMo~S׿/tgO]tg7wx/_Iͯ_Wn˯ϝ8{bb>19ƄnDLZ|?A1dG Y.frR!0"1U%%Qَr {|ε<Ԇ,¬vVeN{[||51:79v4*JyN01˻dIx:!AX8:Z_Yi>ͥz lkgsq<:p FY`^߶zHBkS$1JpqkgC炂?_!+0 흵T\ϖRh `1`ض "31+ΆT&re[/SK.w6\.u+uJZ6i┴`XJO/k j`mKFMQ/LE1wξxVm7W7N?}xf~u◫I<($1A*HdYnj֦'lBc @%8UJx{ $)II}C \.fƹRX˗N>Ot7.w;||uPٮɨv\Dˤ(ȱ]_X60QqDskJbV)M\=׹;p]PՉ _IF},? |Z*DL:11fy,O r\|_W6vcaoûo{_z}ں!A}`QlH 2bƪ/3\$QY5+k_ȯ\}^uÇD|oo Ffw')Z6ƜcҰa$4hZ]D?9?~K/|MwTꮟ}p/>zϗ]4ѨRe/-Q׶C5_1|fuSRZnob~2WYP&UhDLk&;VQ.Jtr9wx4va?|ћ͕fF&qZQ3+& 4j?^skj{+\$lχ-$ː@!hhA.(AΠlL쒶ˆB)LV/kٮ!<} 7 m7*u/?=zYZ.Ilؼ f%d&lf8a $^0k$&sR8f̚Xee욚5fM,9+ǎSo;RT{us/ِE秲:!fkLtW|m7R"u B:::3*D pũ\6hx9J Jif@̚Hj`I+ 2i2'F-ł/J5f7=+戀,HL IIBHKwFv}rV8 Õ'+Be5۾$l˥͍_$xzł9OPLhD74`-2lF0 HmNxg"x@d1C eCc+d"iY폽\kT'*AWd$;SG` +hon|?6N29]k}/0ҬNteѨﷶWVa9vz[*N8~pakkϖRe:WlPQ~/8ϓ/(h^{M ϳ  g_ /p}Pl Z81i73iFm1S؎(ʹǟӣ( H@2!ePCs%ϕY ? Y[tZ3?Sxͫ^y?o+'y0}4GL/r(ώQX-%"XĈMh+sjCk ڥB$\7s"'hKB\{*(U{pU\w}\yw{* Ǯ/yGag׾MK~\`|1_ԧ'* _,)wo=2D% l`n.zTVwwꫯW7T|o3{~Y _YÓ"gY4n^'Nc-@HSѰGA( BJGh!lmt> 7H>ʙ]/W*a^0PV0l.oo Y!MZؖE 9m p#*N@Dđ-vABzR8Rx8FVubB؈@ 0:Ks\ȥ=w^XAc~' Zp8Xh1N$CB"7` 2̷j(Ch#+iM͘a ## abbh2;fW'01vF TϾ?3˵JcXZQ1pl0wk*v8¾6T%1k-eX;Rg{"$ҡNRdyNw\4=lzu;pʍ\>Ip?FN(%q7~klcn:ţ^v/5.~v\(3ufmٖd`$p Tt&&ڀɤW Y*&&e/9`# j"SfK ;įG~?0'T;/׽f-GꞫ+ 6\c\8+`S39NP xCK I4` \y7/%A>+$[ rRP,[Ѐ /FrZ—sF_O/n֦9xtbvˏ=|ҁ&g+?d}ζ(~y~#җ%zj` P\ /,<ޱ뮻M $ѠFE ;vg@ R(f}AkͦؖK:_i"2F6²MD;*Ui;:I3KP,x3Ekݥ։0ŗu6Q ig PnFNC0aSD"b8I#J 9e- FtVe)ȶiی0wۛ۲f6p3{v6^>xݍ<]/?r<( RcHژƣf`mL `$,r66gq O e`WL -ͬDFJ!fDah[!;FHP8 Wַ)_]>{koTz݁a=x\̕*&I\Y8|jT4(4TjZ HDtr~8i:m W3A^~ED'|QPNJڶNb08\EŠ((څ'r20XH4&fIN#pR+7Ͼ[3,DW qTz~hppV; [ac*hd +Tn2hdL"|ҦG}?cUh Q[b7mZnaKU)I)cӱ4gRm{}r[pJXQ?'Q:A2U+3=0 Is}<}K+7x|ţ /_>oto]KgyeiKW R@t0?y(saʕRu_7;0/j%2L Ay28Sb}m(&7(KxԏiͅlMXJ0,6Vi0Vؽ4=={{JӠEʰ $Z~>_t Du :$T%Aö_uzϰ֒Fcƈ~iU&g,'dc[6f}"'zj>i/[3?8x[BO7w烿v}U^=sK|L/ WsYA#33diD&W73# 6i D_y2!pE-Vջkv͍7^\9Ri._Z{}o,}.(|=A~FFaMSDB`0ɰ9ZcwZONILAX;R,vZI/泥X+ |Z $p3Ȗe#Rsm:}sFǒ`[k±RA"E Un,]F\VϜt G %I q\v/ą|ݷyDef)O@$h<:r ƉgDlIN +IDAThoy͍gl \.UJJQ&,dZ9-,[J#ct/报$qry.p*N,J( r8ZzJ}K75wUej`26-74fO?}~fב]w5}_xᕋ뮿W.x=y9;:s|5HSۮ%9U|T/V͵s=,(JJ5lc8=˵vr+}3{lR׵Em}fҶH1$J+]3b`(R6z:3+[Ai-xk!%cHʼIV,LJrRmfjׁ H0 kScVͨSdfl:t'fvק(&THKJK`f"!Y+ubiRe{NڨMλ-Z-_p9s';T:17c}_箻Z=s=Ji 6̸gB##$f2`B Pr17e'D R %q a&pkjy_~uf7G൷7sO~s]N7:HҌƶ\lZPD`,-|bcگRLL8n~hwaaӻ^ 8]IBvr"MFRR8Aog߱B0U#!WiT$AD47/ۃn+ G^Tczϟ~[A6{խSMe r+i4 je;ȫP[[;+֛ wvO{#{pR0cYDW`fyHȠd" F[;բяү7g_/dr+`)tnk+ AxJxɨ'r7XZVPQGke\U84fo+s"K=zͫ1>?gZ!{.j{2RPQscc'u*lV8%V={j4*N?o|ʦ8RYcDVKUl'p{C V.|g=G8[sx?d @3( IWzhHhGRnnq|r1W!#0Q8s,"h0mUjAuI&t2La/W*M /Iy+A\J Xk1:唈`*M(#w PX&\궗[4`pК9`IYHI [K_ 7W +Ϗ;G[϶[>T[YݾWWl_f?c8a2%@B$_0!/5#hFIp%tP`Fnl1F,K-ꚫwCk,/m^x(Unt/ EHzF{gk KyK:&P5@jKnydql/|y"_A6+E)re/6*̚-'ŗz\(N]9Gʩ?Wv1ol>pa{ 8.fK O4?t%)7g:Ʊr=@#x|3ŒXJ!l*y})&"A (ǖTksx:ESm۾V&vACYl`v@RD2n/ސVsyirA\lըH.ox RRwsӷ";t3K0 ^,zņ/0O+d82bחDT!c L[\T# DRZ؀bKv8;94V\Pnmy~nseU{GQi_e~83UWA`$e j*1I&2lͽ2mYL@!(TAfLa,Q [HNnm"GWr7|@_oL/zw$ځja=趒tR/}{k\9ӴOFrw5GW/l"[Î:j~b°GԱϹ 'pyrO7$bיT3# dUQfCWlUFfEa[vc{}1Q$ I -J v:Z\non\SQ?֚[<' %-sCz-z~PLi'uNJ\i4QM9yҹw|+O?d鑃e{Z|g^<ښ_jTJm]IJa*㠿,AL0Y<+7zo\~ŧW.^[BdNbvC+H4n~bdu+l?# ,.c9f`A%=B%u&8IF`b4rf9m;}􉹃GrZ<쐔*e-dGh0pĶ{|_mc4^;u /-1ueoԫLfagg $:g$R_Hpx 0 $r%*ynʱcW/>O75MZ42I,XTQ(DxǏك=|4.f_~cϵ{j B$ 4mfA9F#ǻ e#06&3tX ц YBbn?tQ=skzaɗX=w:Niu4fNlbi|kku}e{&s'MrVW7W/O x~r|B Q[Z7 A{h@:~#9’FΝܔ^0 il{ݷXdP*%/M;±HxiC@@`GY:in\>ʕ:H]ök(+lȳ})ބHD T"mLFbe'wyϗ7]OTF5Mƀ&\;1+li +zj'pOO~ҘUi\]tLW.hr| $H9]N._ADz=2Inw i%6l4DЕZq---{hyyOMTè713wxQ DR}|[MQ[gHvݏҸLJ;u-|qaV?~Bp{}w=|u8<Ҫeۅd"D8Kc1d.X*o͟Kk~=?kt*%!A0FD(2&UQs}ȑ䤰<2Yzku41Qڕ$au[t+!8QsZޞ^\QwD>bܸ\%㖪0X=Hc&znv7ffJaF~8`k{c>" IL#GnIے/Sh˥NʅgpJ|4Vbw4Z_t-k8~𳟨n17#_B{n"d1:j 3hኈ3j`V! h( # B- 1MT4`QA>yG^s\ri¹s'JKg.-w7Û۶`bFe$B7.vt\#a(M˲mGQIض](AqˁeHH+d:k{sJ};Įki\QWO<6/+|rCu#/i$$0f<4 rŷ$,>ZUwGXadJx4oQ'(²)rIHBQ@>myvo|NCiAYqsSϯ;#e5A5jͨ,M̕by^Vfr }"|.ieo^>vwSy,XڹN9rjvW6 ZhTL$m+gَ\ȇȊU'-FQحfsg~a̾q<$+&Ο<^v9_ܹ=rˍrm' 8łse $ɔm7xe4dKsblbF2q6* 윇!Jޠ?td~uxG'AC?RmK|ub6='|ZnۃN+j5_is-RM%ұW(Eqz6%ֶ+ٞ}O=y\v@ K:6U4Y Q+@&^gss*j AXNmb4Z:V< ~%mXF.{qgBeb!_ΛX7~cjζ.(_+kBa/ JW(&~.ϊAl6 cs)D&eMv5M=w{eO~B)p-` P,eBN=c?K;^wSm4YC&*KFc{Acz._,8"^#wLhTjt4:P )DoGmPi&)Ҧ|PjRȗSsszl=WZ<}g<'.7A}f6?UBޠS˶<7Wז^:(aW^ٽkvfyvuM2Sǩ\8x'd+cxR iJ@2= Ͻrj%ްZq֪ wXy7pmR`M^A[A29v8DÝu[babv}Rio.v7.ʵ=G*^/MJU<,BMBβpaXrJPE? UFq؞ڽp<6=]iS¶ωf-BQ)hT4"a$IRBD q(]U*Lm˲:εw]5p)icyIOJVfp]Q/ӻM9NaY:fkl/-T15,OLKvT?·/xZHkb~_XUO+Z|+'8{R>I F6ضNظt:{~-ȗJp8heYb`BA\ τL *LNgo9v|8pm>r/= *K]^lN -鐥bPX 5Hc,P3`)0-4I|)ek:.Iڟҷ P#gDdgw;~uoyW.I?ڈ[((6a:AIyyv.kiNA[y+hڹW.0l(EP'~o;H)Ա1Zv$] |ߒpmi[^!f+z 򮸻O~'; sП5=1`mn+:ީSΜ9GBJ'f 5|ȻW靵?oW\ϛ,fA$dֲxh@ 0ED`km2Y01Fύeޟx(b6CZ%:JAeI=h`{=P,zs@#;( ق;bqzߑ=GL7&unӎ_(ԫұHxw+B0_MV|jr%كG^}4tT kkIdm4a3DL'bAQ2 B"!#V d8RX~0v1?c9㸃a+X3 {¤zF:|F7?v0?O3{Gp< bC ,2-1  3 |z#Cg"!1mlDhH~O|c_wY(uGfksi4OMܾ=q:֨^/w6ZZ!Bcl}6;ٽBmƃ~sC (y~<1gNjIԝ\w;& sN+`w׾1)LP$Ms'yQ?@sUޅzj^?9hU7ZwiŃΠu gv(Aل&E O?+/^:J̘@R[_@ d$@ !նA`m;}niExAB(o?ǻtnىڬ'퍵on.!"s/K/[RM#,ffڶ[|, 8թI YA&^oKʼnƔ&SL]^׿/~Kg_s=޵ҳ[oqk\{֣_ƽyͯW~/8wí9}_Z+Ei тEvFAeJ2L`P` `",ǔMϐ5 ]nQ8llml zɉwg>W1~H_}2rpFI<"JؼtJSYN!_fK ¨;L͹q66Bw&篙9x60wmr(l-zR%jR*c\c8Jw\UHOlLMf137eugC_E扱HY,%`6Y mǶ V޵𖷾僿??p7_'}jPyA~bz_6e84q0 Q^NJ%)˶SL~:=\:1_wV|#,EdufZ}-8n-t}6UYo6YT"H/X~:=dj'f %41F4`K^XhZ۝+U-FQ-,gҰQj;^o6NPFDMRKfDWyjU^fx7F+ 5V~[uɐPHD:QiZoLsϽgO=+FTI^]Zʗ_5W_#-ҠMJ\[zn{r\jX B7쏒xDF+ ֪SkUeؒ.s."%IRnT+`gk+!9]{?yFn-\ٽ?~Gki\|wFR6sݍή.@0`YR!@H-:l Yd&ed Ĩ0(  f9րfކ]k!ڒ0km j 2Cb(%P2\H4 l.j~^Oۺ^)}yO?ZTU'+gFA?0{_RҖq _׽MBo|܏O-,ĩQ1VrMG68F0 r%IHqK^nz  |Y>sjh3?ǃQuqݭMﶻ]ᠻW5J{9|?P׈anxa~~א~snl,ǒD%H$BXc6Ҡx6sd,< eN{]rbn7'fEIk{T [iTgA)Iryf^&` JSAN}j~z4u'IwV # APꬭٹ|ca:Y^+B!0M󕪓۫O0;?Wl?fvK3-JB/(צwe2 BZNZdP@F U<ꍺ 'p(,۱}KD+ph9Q[3ńdGS/}yܹV6e6I;MYwV^ŝvϞ}[O>/nL&zHB51tIf@Rm[خҺڨ&4N Xdlt`ҔR)ᇟ9plT@kqj<{ჷ8=ȷ.9%O~_?zeicuX9J_xqyrEB˕&D&Ac! IR"H$Ca}|!1>f#`cr-g|6e/F!2a7 E&AXsK`P ,XHJ#.'m+kJ g&Byb{{3y9w,qsX)N+FM, `mDݖJ85@ʴy? 2z{fBD`a51JDIb6HZ֜8d%Q(-H,`7ZQZv~voL޳~>g7/x6yqykiMN2qט jb@!Ȑac7!l!Hc8L(UDٖ}Wo;n8}cO>$̙S'_ڷxOwRR3ӽv:2{$쬲~uИL\>{?VܻgN{Uw[& w]Oj/CozIRJ3a:v/ {/[V.Pm_SBְne{i uPPP&mojskݻQkn.m5x5aj9%afxS1$$M 4$*굇VW+avPFJ'ד׈)팆[{p؅hXK0iQ,/8^I8oy{6Ryjdo7w+x1Y`iFȌހZhf 8EFfHfaS ENu,/(5&%ws^hJs޲MLDBzژ=wN.o$0 \?p]1*8aͬzrk=0b,*b-2 @ h┲&"!ȐT`$@ZRC"J#JfqWJpNsS^yɅ=TO͓//l\(I!la@ L ֬3-26amTa_;0톽խ_3~#~;_[/؞kijC~ng:5_.N7SrOC8ٶ A\P&ґBpgg- CڮADF:HiuaX]~gYwP) h+j3$񇿱t|}fPcaٙsh}퍝=n^8|t,*l^^mo GTHș=GJc*MNII*JRNJSaYqr~GBmvHwwփR<9TZ ZXO>zR+s8ںM͍DEGkz]/SBU( HI%KmKr[%eklϸ۞Ytحnٖe$A$ ӭrx9xV- *<D#]+_{}ZkY}_i>'a_{`_*ei«ˋ{fҪ74ڞ]vlw\lfEv-BFږ ITʲYk֑ӧ ھ_yg_y}1>?7nf zۈ`ۊD;pr˖ J$)!$ )%eN"a$2H ,FHL`%).\ 08bo3! @ BI$Lp:Nc@m0!AzAUj5dD^ ‰JN/,=Wx"&sRP'ṕRsJ"dBc16&ɲ8EdA@0#C(Hb1>b@La+iY )`2Y)֙U f Mfp홵ZXNfe ;;;[|Z>ôl4*F M ,@`"Ĭs-Jo 0# bqF~X[\O?g?s 06{'Oz~fjvUTQ7oE߹}9;rB(sg6T_8ھh(6ZyVsva=[ׯ;مRztggVOa$R|7k4έh-v*zqC&ޟ 69w^,2`nskB²0(& Rn$$! AR(#uiFda9sE0s8dBwLsm~y hDP",GN6@SWO})=+)dap86Y|mLD8AAcyol$ a;b##, 3$J8D '4cI*@"!a$$iHddƌTs^fFA!a!+> \ ectYhJ2Da 'Ae ol??/OKĄUoTK$d !DL"8$HAlet76ǽB|>CG ⥫>>Ïzj5ބ@IuDµ{nη;wn 5`oΥ?*mr?Mp͛;7o֡3)ύ'2Zٖ:KAjg&?k hrxy4Z _p Á6IZj̯FPu}g߽s|ϝ{K7nyŚ2 HbR /zs{s3˝Qdskó^10Y%7S8/YY8ڐt??=':۷Z `,DD r)aYD)H(EB RB S]zΙ)s ) њ%2h@!Af|9I#JґυmBH@H$*fd6 I&/~JR(W3 drAcZK :łC\hN',._qw_rQ3ƈh0_4"R,DL5\@N 0w3;d;Byi+q6&!0β4\b;8Kks}<BBJŒ+ʽ(8FTLl&4$lis߸K#>O`Y&_i-̜|=\1Wi ,$,5'ƍ$Y~j|X;[h͵܊c'7f[Ir,/Hi֠3` 1n2v(JHl[Εgfy}FQ *-nNqRR%i\Kڌ&iQ0 VGWv6m-UjRu鄣QN8)[ Oz*p4rֹ}+M˗j5-I DxTEP:9$F4`tGWRf4[#&|i9IJA{}T7g=Wtwݯ?+I6Z$H $xoqc\?˒8K'Ѩk Ԗ8 hl{R}Q8&ŚP}S+K6[?[g{aăss29թg/K>/ \]v[~> ;̙ʯw1_Q"!HIQJ!9OH"A ,P2pR~[$"" Y&Ch0R`0?a+Ȁ5c 50l4Sod ֯DVhYRFR䖛?UDnMF+ n)^ɯf'I 3 ~KQ!$`̩#{sF @\dBBP9߄j/ ޮPc%& er)iEI2 Dnn,Alv2}V*Wy?|vlV) S] OИs1,8=vg}ٵH" H4SE@B!Ir2$0aR(KkjR[_FgWK_]hM)6Qͼ}a~Rid:5:F|$2&:VUW. {]XYK:Cu:؎+S,_+gYc+ɒ$LQ%]NxbP,Zڬx2 )+3BѸ?HW(̐n2}atωcSJA RNx8*:1p@:KHrs~fx5/$& D$8MI,bZ,WK^~k_|= "c@FD(-|+߽n(O`Zۺy%4FRcXXҭ0 %U _qnpxu@'p? :^QcL}+-M鋯N@fb "A H[;sƠAN(% ,Q'?r•ͤ]fW]XVg@vͷ^{}v۱TW drG urKssSa Hp73=Xmmnq4*l*Mq7Gv,I4([9ꝍ-/sI-m_"6cM@z*8w]>wβ\]`@#)D ITRB)1&,-Vo̺EֺmqwT=r$B1wQA'BLrPdw6̭^yo1 ҥç*s k$zt ^q0x0̒XXln^ z^}i:^cnP$A 7}GZ¶  i,׳eIa&6cu,$`J"tcK/|_|͋7{]ۑl@m8UJzπ7n~lp{fτjI84:0jZlc4 h~aenP$qWz~{,7[o_y7[W^~W6fo:JJ)D0O]$B !AJ!L<:~bCS& <'7 0 PwfDNr3-Ǧ!2ɅɯmO?ǿ;\lVk3 W$ u+BɌY q#JbaL "D Nv~q=[)\n:]/KG!e k L( hm-MdXdrȌ9,T@SrIԬ}HZxЄI2PFΨ=hDQ{qdsk[jTc $DB瑘 䍐da5s*Hۥr-4ag?ޓſ܋olmw8lX,)X Fa@=i;{?g<|TiqGgi2&6I[ld@Q D.DDB*!T>Z ; 04}I"]Z@ӓGaQnH02Of@gAKi R` F$ g?B9K^q~‰zjLv7Aj[((Bs!wNe U`{NJB$ $a=bʫt:{02Ɂ-vн lWb!kHҒ:3:2B f̀@ 3 0(:\EbȀc⨽usUs]Mǖtd<uA?[/q׮Nw{p`YNZ-TI2z~]K,O)3V2ɰw"XFgs< !vq\6{[5+$<غCyPmpH*ȥR C)YN]d+5% DlnЉs ޭ;aH[8,/׮2k(b2di8EK4 ~)ydduٵǼbqn]Yw v:e&ݾ7_#>i&,r`DIof,]i/mfn.fmaX-twNQV7wߘY9|rU. elMFƬ]F` tZI S(%+xIܼv׿/~nD:-1/]90a`R{g_Cm얫Խ,Yiqu{k˭V,RJ};ȸTӘ~4>o< z` R a)%(&B(AD(0 I Ȅ8GDzb`֙iq@̟5 (Bٍ dt` !I )cؘ .B7]ޘ%Ru%GN3c=' (%mٰ{;%qߒ`n]9aP-_W~?ڹV&IZZ*\$,&f6,0HXD@&Hd$`R޸w[s+Ko?ؒ*N9X56*J"*p܋t5nkWnp+6w6JT,"\z3KfHy%eL%)&Qp4C`81pouM\n-7,a))ʮ;n JmX+:[:jsY0*/|= GϚ,,$H*Mp!:~P,8e  gA&-THHd[[C{/)^ŵ{q0R5Vtߺ}uYm--xrl4w[3kG- Lbݽp$x'6g<7!$ N?N`o4T_Xmwכ +Rmk٭hwnq\kEU(dF"`@' Up ^^w j?x/?ۗC *XpʇHrg>-a6eTVы/yG=GܹxWj|-{ňe{רWJ( 6//ϔ+h8&͹֕7~l&<[ ,$Y! RʄBH$@$($9(D:˔@B@H9"҄1? I|( I4C^u6̔倀P(fag:M8-R"'ty{ ǩ44Ӭ Pn*~`YPA#j&d2(" Jt߾&B Ƥ۟ZqllJip䨴\sS݃w`[KF PJF0,(dP2dP ؠU>4T$,+~-έKkQցA0To8l^[7T}׵dBb$"bb! k%2<{2U|W~}nfDRQ_xWt277&n*ʵV{[:N3 GN2O}O_xުn#e3G|㩯lެs'-Z( f @ f)@:KIhh & P 0`t]T#~kH2(SGf/}o}<8jnZ_[8|_/~\oeQ?س~/fʅk~r ژ9Ibޭ/_{-t<)7f+j-qGَCPEm[%1 hq@vҘYl4]??'^'P%BcD D P#jE" MƔ~d^/}geYENN_v '[+Ѹ;H)%NLu)XJY7 ^Zxܹ{q4VI&ݝI Q`HG)QHA* ВPq0%@d tk̗L1zv &Is 1Z$6.!$M6e4w;+ԪIh!`mȧ{?ɒ(C 8%1iP zBa-mRtlZ8ʥ3NSǯf;`ͣc={j N`9έk߼o]y6Z9NBa2`̋&L\$lR,iҡ4!ɺ qҶaҜIن 볋N_|=RP g9""$ʼ;r0u6̹ A iQ ijxG2Glb!,M4/y:wx[od칕Cb3I4aH,&57/w6o.9rCZa[ ^ٍ&x~\g\[rgVnUX@4@f@@`  +6))ТN 9 Y:q/շ].ϭzVYؙǯUKy( ^ʜfq+D>R𽭫o_y%R+C l Dyq}ٕ̤R{kosòED^+d<*ssչ oJrs&Gʶfݼ_ӷ|•[B),s״ߕ~4:C9P^c0ݟ\OMB ]y_壏RpڵHskye8lx(6_* '5o|/ϽvPko?7no$Y"6UI/DI$ 7 L]hA\w3@@Mh$``_Jf@#PAo{wI<@y}sW0YGo/|>|ܷX'Nwq e V lR X@ $!!I"Fl)W%IKuIJ 0HYU;s|T9 6'ޚt6Spҕ+/U 0dd) 04д[Qp2o &$Avrm˲] gCGȴum'̼I4'VsU7\__} M9RJ)! mD$):6!$G2Kf)=v)SKxƜ7[[c٘XQ5A7ǂܕ{[XZv%3SWv3h47RQz^TR) {rI1ՄA!(f@L8IJ9uv չYu$j+'Or<w3ëGO jݽhMƣZ[W^{=A}B0:%RB2tuY'볫V#G(/_8/7曳siWa,H*([7mm˫'R)Trtϟ{_/ٞQ I)&<y"r:0&"BB .(p$^x7E={*ERkP%j20kkP+ ^wooG oŝhYnU W+%J$-"KH)% )7g $`HtB^&sS5@\ 4ߞaѤt3̂hn-fZ/|~zk+%%JdB )g5"-$"PKR*E030$à5,bw6\{knv]ƈl HE$IfVouJ9APVsv\۸qsz<*f0V i;BRIi :v|OIgghGl4C"L],F5qfC9oXpZ~sۍŵS> '"[咴]B@_߸ur06ܢzLs$bDehjZ͔\k~P*X`ADagvU]*vb0^y7б{YYz|z?/o}k0N2 @hr48e? BHʫy hZY ;,{{k_^[X>R,$,W>_zz9f9jq_O=smckssʥ/HH˲DJBJ !TBZR"$zS4Ḏ(X G ÚO3 SA}B($Q2Ah؏k|{VVVgVW[ٲ[Hsg0zO'-nXՎ{{衕fsavIoN˳ βo'wH oؖE)ThG^oՋRJj1ᤷ]lKҬcwΝmBJ`~i2lZ&((V+r fI,, ,&trJGE$ @p~_F#0aIBDQVkBV˗4h,1AޜWg?+B[Y@!$b,r 0ha"!LsFhr"* ! R1vA2څ(( k{ZfVk8x4exhydƯ/(  3(9 MŲpAdQLzh_*U3THI$4-$J@n&0 ɴ1 U^߲ $_QФ۷r(TK&3v2Yngsgƺ_/QFKK _*zk>:v,Gr\ײpݘ_=|;w@OFANX+̬[L6+NϿrc>Cǚ媛g?/|{ꅍ JD NB`pZJM bDQ=3 v充5g?S?U?zq㠻{AKJA(XB%U~FC!$ ,sAFӢNdj [E%%u|3! {$L&Aqqq t{]k}07Y][=r澓:u|\,:ņ~d48k8v7wHgZYu%+r.ƌÂH:3ڶmg`@ 0ƹWεVNf ^y@Y$ sG<^Xg֫½/x&qع}lgf`: 3BkFA`>4hAH4&0,N,Gϑ/>tDq8&F<NRuo{{Q,!IBR$dB #;$ dfLHc )@ۜÂ@Ţ nBc( K}ɠݺm{#cyMQmTn:v3StbE:E$ấ2E_nnv\k{^\'qʐ!ɿm4AH A| gWW[G'u/Bhܑ$ݒ־r/]9y3Q5jf݃I=_C n>398셣XGdN5[`۹D׾՗Rz39\\~os__|s}k{ J!%-At7f;#CX@"筠jLHLh@܎#%p5BÆIJj4 z3K_y͌B=vCןzc6~' #lR9LI %YRLP~9)~ȃQ@ [Ř@Wra1؀a6"oA٤ $#=3L^CufUɵܑ,RDBA.r7o^v͚Lgk-f tUgc~#TΗ*bկ~uBi2߸6j--]z;tRse2ot rދ4` c`3 LP#&BBQEPY5S,js(L$C)HQ޸3u=u%ѓqFQ(+7=l8I-݂Tt2v %QJۯ̰IKv`na\cyؘ:S$sW?+_Γ/!RК?!N7-|Kk $`~l'J( b> 0$”H2Ic@IϿ{g3?w}s7G )zW66AHs) K)GI[ &e !,bØs@ hx 6C0eK`E)DhXk<ab7~>w}gNڑ sx-x…W~7o|{?|;јHY =lFqG²Ȓ#Kڈ&cdQb(FHT(DY ܽsݒecpҿY*Rm-e ve)Ŭ`l7timߺj̬,YvX-9֜?&l-mu"b4G]RĤon^*ER]vme%Ky~[ 0&3%"3' I1#&4 9$ڠ6@.$HKz5k Β0!z5֮m;a)! /fhYL4e"#@b6LZk==@m0Mq'~*_xklcD:\[sxzr],jv@{c杍`< tʕY\t=|ԣD޸vg[8F80B @/iD4(`Bì4o2Lvɹ'psVZ-k<:c8͌gZv>y_RGoeamٹ8H!Vf0ΔQ4x/gRF}n'~ʭ+o/~;/=k^~w߹ۉ˜s`6։5E&3^R %fB"J$!HZR$%Q@,PH$"2LJ^O|o~'?4?;z.] '_([ϾT(‡?/}g$MsEŢWp]۲B$&RD"e}\ DAlI)b&?sfh@D#df Rau:=T)e+ڞo>cj7$X$l ٓeNd2gIB ؾ핋ɰ -Y`+K(`traZo:r٘ڸ'I BɶE;lo0WqݛI#["{BMI4 DM {($xo;~- j0Wcmmn{/UXzqշUVIg;&i8 .,x~QpNqʋn{V0>x֭w޾};Wݛw6wI2 Q4:/1No&/+fY{R<*AyjÒِ )FGaNh82傫4ۿ ?qJ'>G??}+nVmWs 3;~Kz@P @ضHSMFbÐijcha6yEDid "5k |eO _- & ANW9C8 "Ê:-g J RB`ȜkW60liQV@& mQicmT9Cveؚ[v;vڷ0EXHz6__5<iAx-l7f[JjRlcaavi>;ږWb,+Ќ&@($g) iT$L{b3Z۲36EL:,]ի $BR 2)d#<أ5h`ΏP 1 @LBQ2O3.{NoWKSVr APS@ÌL @2F a  2qe_53( n:[KGp0̶~wX-ZJfK,1(\q6KL!, |Yh[F0`sװ6HR8Ȇ$NcI&o( E/c2I@1(3`0r^Gc8iy2,|Rxfߨ6Zcxxܷ>bqd:Mx ڽgx^{я|#yyɧy?|w]vzs}?NpŒ{ݽ͕7®Fʳ_z{ s`k`<4 9} `DJ^1KLqJBA,IPp8=V_\Nέ;B`zq‰ePjxyzgBJTunw`< /=v1*-CZ=~uaڜ#\vooa<AowsGw& 9Ng4-LO2$rIPJ8 FZ&KH ~ifaay~լ^9v< տ~vQɃ(Y<^ivc>z77_z{j/ Gcw]3̀54L@H@TOWh(diW@  0C3;}2N uƆMf2HtWm?xjpNfcP2RFn2Y`FN-G@^ $g&%)EiY,aa4Qٖ K@Ǩ I%A[oVG,#q/tوm/:Ko*e%Ϛ;t5VW(E_YN crw&"$&eb#&&)$v Y ;[xR4W?p;s_2&YY֣n;a/bA=q~{B: 8tJߜnG˿K_ӧ7Y9~|~ĭwuw*':m:s+W/JO۹q}f_@#N% xZ)@B2h&]Ks˳k x/M̬$R`=6[N$`ffB3GiD^667@U8M˵87>X>~`KO~ "IDAT)Q{W._xƵPo='+W=ʫ7o)4&A )/6 [s|.5C̅ P7O"I٤qhٳ'8s|}a֪z#^kiuvynnvV'xoa\>t#Jꭖ_qܷ>ͥ#Ϯ̧p$Jϖ, b<2 /ѧ+`ґw  dH d6<;RÄqkkz1`U(z]pӟO|{{})V1 ix"B LYRم4 4cHbJ=*0J'р@"cmAHȶl函L&@i$[J}aԽ3l  )[vk/U::R:fq5KcDiomV*6h2Hy &MAI.b  fMPN `ZBz^Xwkk n/.,="k cj$L9 [ L Ώ1帵RehYE;K;7zo|_yߺyCO>c,Ed2 aejur,ڂTȆ d&`vS`n6Y [^'|8{`ЍGCP ~gwĽYA F'b5tJ?B»o\&sG*߾!KY`ܽsɰj ~axMZTkf#  ioo+K*3g:Ȕř+N,ؘ;\O%5'מGɰ/n\0l'm3rD/?t3 C _ӟ93vtĽ9ͧ_xygy;7yNi$IJє,("B~|D!iWRJ)(z8 &a8 r=y~7~Wί^VVo4}KW^Λ%w ԙ&e 4+$)Tfg:% 'HJk&JdЋ&h&R(ˏ?nw9i3Wtm0"<I+orSUQ"Li@qMt@S5 ڝ_b ?8#M\t8s~h͈@FLB,H,2l0,HJVLƠ0ER:LX^m)Hrfe(՗OY}Z>YՅkWͅ3koDm)kfL3ki<\=q̫.%1LPd^(4V(YKB Ԫu0‰3jqY[KN{S6H0l2GkXbbﺮ;h,7.ݺr~nwKg[n?uBux]+A/t~ova?z3z4~Y$H d 3Cc^8!FdAr_&#!reY{SY=~ϵ`y{BaćY4 #8 /f2.>4stqtD`h.ܿy VP׎ ^ܲRB(jpJn%qH*uJ4obRs0Җ:Kt4;K%>#'Oc1kIm޴Ʌ'nmXDi6I r^vZ+kJ=?@O_{7^xkʉz8ػ|7zAoBX@d=#Q <ՐYr"0II-qŮm>zӧO=sZʖ_)ՄģPY:hpJMh p0ZG~4T3`4:"[:K:1j/}w0>>|}_glhe)s0lArd oCAMy@̜;8 1MHYR:c4ja{xǖe:+d! flaϯN!1 cPhRa4 [R6hlPcq!FĤʖJI@ „#͢\6n2'0^)VPmgc3  Ң= g[z*IP- Ҝ!d&m 3Bڂ'ӚY4eI !"p@ YdH*6ڮ9YHB#8&Q\:l"SLF֮W(8h rLB3&6@cJ}mv^}_}߹p澃#yC=rC?YQ tf87nn\ gi -4|:լ[wv9〙J<4sU`  0ĹZNW($, &i~wwag:7dY$b&ILe[A8,?eS0IVK4KuCp tV'Y3ieP!jRWiʌ;{9(5DβdH^xRfFΌ"4 o7bIjkahjةÀQ`Ydd)ؐl 0@ 0 L&Gmd~d|{,1S yl SrIĉv=, h;ŕRwuOԒ0g:1IaY痄+=ھsk ~^=ڰ,kz}>u53c)Y8Nlnog?|P_;u--$zqXrz1@yzaFO'&TqjQCڹR)(Mvkt,)uhɰݼV_\q-M$QSki:(vnltjZV,HHԚTkz.! NϘPx_.n\JK'? reOzq`<:Ų,͒$R.5f&at9h0 zBPhҲ,.Aۯt\ &ڽvT_42ۚ 6 q;~5n{=i͹m(@JJi`mbF5ҝ[;&6Κ IEA"- BRJIg\o׿ŷtP_l;vbm vGX5sʵoӠ,O[3kJi*31I"cd2F$0MKYN,Q=rihH &e9Bt'R3w 'rLl+W$̰f$TX 1b`ɢ8Kj3($EB',|~m,]ÂI[3d{Ze8Rb=꾻&U1%Ӝ+I21zF)mOfquWda И @2BDa4ACȢ_= :l7m{buT`\ld;;(ݭWyg_}ꇗ^|`{@|#?=f5h< YʒBf_}n]>Z(~8;ݗ/dLwLtWʈl  hA A1y}t&% vsV `s%[KOre6/m]=^0VVѯTj B02G`ISj,^β,Rm-Kƽp8I0Zp $e0#Iin. ?'aT\դ@)T07wڹ_½}WZ󭙺$|~?3sm _[#_^]c@R3fbJI J!@AB Tbtdu잳[Ngx坷2e[t2Nij\4(̶l׎F,ER~26P{Zw+8&vfQ{9{vœE@^Jfc/J}X B"01 {B0 (W$3mD`2TZ]?r{;G?{mˮM&Rd TtiV 1 9a :Ś"$b#eVIlYP3$Ik-vAq3D1#]i~qu00hC,JRPR|wA9IekȸS68Z6X p#bfᔔKB$)S~k_wA?(&b$ӉD$Ch3D")0I1Q,,/1v냎"՝ oa[+Gr9 iBm2;Ld_wՙFR'4IFݝ;=\[("֝ooSgO=퍋zR- FÎ[Ug"3,! *3vR)BhFIw藋so]: {ԽKZ[^+vo}[_W?귳___=z|uW?'ϼ,xvPX on|I)p]KyQ ťRr,KI"$3(A]}{}&(-T'Njx؞n^&]' (˭ԗ֛/lZP.[I;Zjz{@[r|JIu疄I2|dqe ;{N_]>r'`|~K &qe&S;:A0`Q0ܱ LR 0!`m|C?zcȏgN-̖u6'~e> ǖpPd;ae%=Je'(&LX3#`F@xrOza0981`UH8H-(֋[Tlleimt!_, d< \(޼"`\jHPJ.Vt0f,5 P3ka1:Mb}h0qE$lM DBG bPZ틯|ے`yeFA Ȟ;tVs1yמy{_};ҫ^c??|⁣V؎=hpl~P= &aw_g~7_|Ϋ 3CY4)rOFe8s?K!r4+,Ζ zOn]zG=xX" &4Λ;COl߼-Hc;gƃqWgW_}泟q,3`` *͖Nu.d0D8I0BY]Xٖ#*e`o{;菃ʍMz hP'i-`z2dX dqR$$!dIYl΄^.'+fA/]v8G,B,dSɧR͕ꂎw\9FcISЖU!hBD 13@xre L6&\%cؠ6²Jr(lBZ;ʉ~ !/՛MoWⷿu%ѳ~G<ꡣ_HCɒUz(J3v7o]/>ғ??(N޻N9wF *h[=$q=9mIDɒ,Q$L94:w9{ΩpwHT=ϱ^<[ӧg/fNapl}/O?}rei5j!fr D>pEXOh:Fa4hL Hs@,' .-.L}tgx"vkgl+ҿi|_j5+\: |͟$8]WKQIsm18ёJV͏v g%tض}rwvک$bן}rgD4hkΝ3 pn0n`Q \ঁB0FPjs@iD +-}v@* nbWL#amBqnm[ge๵Yl+˒t6;݅It!ţ<{̉r4U{%+ח Ì%cȘfDh --͚Hw2$=ʓfL޶]՗J,:D< ,T4 .4zO!-d@nsm;o9m.1m0я%xoW YpX"oU @ЪTH+_b9guNڞ "딝\azWV<P_\]Za[k˴=J$ E{@Đ+P GzIc+4pn@tfhk PlSnh4Lg4T.]9wg~~~Q#m޲ȡN2h"jǒN<McI2n^|ܛo|շ_|?>[WfkUu.ضuϡ{ommG78ԗH#h>ׯ]8ĐG!q\袄zBBe c!1L3y 40jZ閇|denfm~N{2ɷb29<<5v[*PMJt:7^q;GmVvey`t`:wKfk7S}I'_~ilooPe RB}Ƨ.u &c6*|$4LrfBȅ1dvGy)Ƈ7oOb ;jǞO1g0|3?3wu/ձ~=ƩR}Tڑx&˵gϬˌ M$ah&ĸ"c `3 9s!0 ι`0Jlc>,!BRUɊ rQ;ni}]&73']L_yaVT&8c Kh8h"ݮKނl2ϯ^;0&AQue˶`|^. N8yh&epT.sA7-vcuJ͕~<7מڀkoBDIXՅ룛zۄf'm8-]QJ bJZ*+rA>ɔ k< ZiiG !"Q0;d -2rC[P4&),dK-i& ą2Ngҹ1ñ5Q2j6 D‰9$J)"p%&B i$MÖFH{^fܮ+qqiŽT*IS1a~_+-~~+?~[oWK%Grm=6;ܟ䣉iF@kg=҉^<ګg~kb'&mݺg}65e[axp`ӦAöș/e% .ıW^;uMGXKTíb``k@(4?΋aU@HBp++E_-;EH8_W+6lov`kuI@I!D2̈́*Bpќ={Vz-<[ f$iNcoL7~f>};Qg`b֊AVT1Mdjuy'+."fy^ܵ7m:wz?ڱZu`}nUoϜ9uZV֗$}CtV:" !3ƐclmΙN uod /9Qk]/XZ^JO4lˊv4OED~7UWʳ/s`&7JI?(/F4MdTՕ5ɾa0'M&㵕ug }=٥sdzVLi[d7t:+3m;TͽfڪW:ZZȐg ߼k^Z h4ҰBu$+؀eƮϮN_+v>^_@0-8'QeNezV[]`L) I M@Lyy$\7@ F2)a(} VꛊF-Qꨈ1k啅 [Ɓ"BP V#ヘn<7,8vxmLW~[*V'0U 2Y ELkB 8\1:wRz[(\p;ĢT<2X< j̹O+_~̉cդڲyρGn;x߁1jM_p‰Sgy܉9?wLZ5%Rᩭ[vnڳw}S;wLRˎ#C3,-xر>9w,w"WtMۦF0?O bbi>?[#kGL`TϏ= |Қs6X`@qyHm$}sMF23"M$7EhnVזf/̯/ӰRԁCMlzskbiR_tfl{>iN4ʦS\6NH2aEMD TAS@pgnk;KW& Ɍ0 [XCZ+䰱T;jЄ!OCB"Ҡ" .!*`kը:Dϟ>SmspQf3׎|%x/2s;jY[ݳnw}D@5n:7<2y~1j她뗻Rn=rۏ=b]aX6e@2(7jˉdvEF7SD 3';S{R49F @qq,43a0w &4&=~U\Rj(!@d \1FZiba6Zi Zptk9Zo*B1,}Pw=xX4l࡚`x{W.;{]tԱ_x_X{>Zs-c[N>7T󦑩I:]]\|ätk}4MN2sTw`rhD*o~Wgʥz*?9疩ݘoؿ0l3^z̉sD=tts'O:4];|o׮]v;a.).2 % qD34}` & e:<"؉vD X 68quc}l&S KH\vK3WVzǶ3եW_{ŌUsΔNpQړ* , !4^301Ү]:,66obwv fz jw1JIJ+{cF2s+'_z7=}악o5V <׾.-+J Q!hDĐum7q]{84;{'әa%)FZrNJQ:Q &C0 U[9fK PB; daHhEdbT'8H զOysB ʹb$T̂.cH$uQ;.NcԛչsԀpnkِ<tN,6u񗞙?wJm?ɍbzWrf Sh Mi6k5n'ng#ɸ!-˲S67cFcnҕ 痵Xwtl}:i؉ݩL87SW:fZٞzz- bRǀidp92uCͦTrm]@ROjt+:f} 4r|%2pZd DHViUXgT !axRi o=Y6$2hRRzG/nټsӖk^ѷ{qס7[N/8Zv,ęKGn:in[^#8iA4Ң]MslvbjlZ676@z2#;j]y؛'~M{75ܽ}7=yK'.hohCZ2ܺgolQ[QF&aGMʏwNwSk'XZs;rFq}B][I4c 08gĐ vC$ >iFT d$mh"Ri@OVJ2ǁ ڌ[L[>ogWR6ў0`05%]'JJ֕RgCP]vΫ#v-]5Uk Ǐf=#=ZD5gq+;<罅v9Lx=sD|6._S͍B0[* 6m;ܴ߶q-]^H͟1uC|NZAG$q"kx*LO`۹Fk:#ќim4)&<ZZKT wUI'IelTOsz,[a3- cQ3P˭ΚEҍXә/ s_vnH1ՍpF5 ҢCD+5YJi mGh".x/r7Lƭh\IV^2<{yJgrOV,ʕK^iT&Q3O i''C=xJ6 19r!XI*T7|٬6UfڝVvZA=*Uf^Us\O>3gz J+<r EsaFbL# 1=D q;&ZI!cneҨ7lan}ݷ+~'M.KSVO?={eoScoimi /?u&:h49}xo7O_>qw}g׮LiB`1tq G82؀Q0 NB8ӡ[xFQ*dA h Gsnjg;s=+צ:ة6{8pvdal|R ɦl^ ]u$/ݦV&m i39x2srcg=6u dN\OcnY_Z(FR<6JIuMLConYK 5G4sEc]G7 fk'<P2>{yh!=QqmZŅfi.lEJ!3!1lK&p Zh@ ]Mm0mZȀ[H)k3ɾMxO}mZ׈&l& 67[]tZuD~Y.3"$56jG_<$zolW*eDò1"-L;Ec--/O9?73?P.n aFFj{$y#a;D&?:IBo"ئa ð 4@He]o4No\wdnZm6n={JJI2ל[!h4ZgG:ZH#u[5ϣƱQ|vҶ0mb T!H Z` Ps b4pVH,j2NZ3᱇}<;fEU oGׯ|O`"f+Ïgyf˶=/_<܅ׯ\GZ)p|xtjJ^Of{{Gg[N;曏?ۆwN aj_[(':kgN۝t*k+O9x+HkkΜzss"laxbﮭv,?9w[!˶\8+Ot]x\9z{R{{;/1ӗ/׌s @bwD!0,T#КMn$ #bZK`֐4cL b?=gϞZ$Lqgor'7~2dsg/+zFd]kwnw5ۛ @D"A*+E3}Ԏ@~4s9-Hj[t._Of3;omc/bL Z R١P?Vz26C:0 A$wr=w>og*aJ.y'@7`||On,*t;m13mӌRu3Q.L6L[("Q:=DFp@MCh"N&5+jmu ZJ-MzɕJm7ʞ&֚ Xx}LrxpJ)ɘFfj͑c,<֋ ^mъt9XҰ#2V]-,]r k--%4hĉӱx&&X,&l2$h2n%!jmwzm]jvFjjv^]KK7 EřcM!,#8E-njcF,&xD< Z[+V+_e1M&80( npq6g GI!9D!b1D TkV4=#6:|ٱmGPo̎TZSu{r˿K o$|-Uс={oggkkb<:1q>>>9U|J6ж.sN,;J/ D..Nmf3@ y;vk/o޳y])O)O"'ϝ:T H&Gmv&kbI"1̓ȖQ˺~ ?ޥ3mw nv g[l:viz[n{9rS_-5W+/|m \pCpd1&X(p=a A@3B @q0mD~giy4҆$iAqt?wїM ,-ͭ..='wlNDH4qKc;vnwt;k_ogT_N-Jq h6IEhlZqm{7&7~TO*NzJ4W66=qwϾ⦡P8!n?S)vʍf]*/e##DTRebr=3wJ5jHY_lgbͪM9,tIW|bct*5O=e&D}yA6pOuYL lzRQy a@ t<Ƣ~Ю{mq-'OKWdJ֚ 0P838 U048 ǎ'2p6+g--WJȰ@>)d `"ft҉Dⶓ۰L.WZW+[Flnu;x]]HOJBp0 ag*\wM[;n a@ B~p0O+b.]r啊! fi0!G@@3B6XBja87Uk H4em~Ƨ6%3)hz^ghlwݝJ˕]?h;yf٘Ě-^{WGF{m291>0Rt d1]FҸx[{ϙ_={Gv][tٙGn:̱OӹׁlKϓ]Hi|bh&EQ#O_>venYl3+>yޭ|[%HVW[􈉭S'_{+kϑ=_4_nڼ}:7cOJ9z݈EM!# ! 41@Ӏ   'PZްmC""% Ԇ.6LNsD4ibT:iSϼr_U338~S:ܱW'&+ݶ<"&"ZtwbNr災cۦ,+S# |XvŋWKˋJWQ$.{==T6ecF*HԶ $Fh˭fpFY鴛iFa1apnXD 0`B3&`} |b1}]/HfL!84i#;Ƚo-D#X&ș`&- `k`J 28 #N"y*o=i恡~ Z]טze7>v"kD4gƦ'_31*jv h[ ׯvn/F4e/?MO:⹭gf^T|MWNL_zm#;oMI |T:PX*l4ݵg8sjd }GƇ}@xk+ztǞ!踍J;oxJy>;_pgkXO >5۟O/ɍ׿ +1m>sՈcEl[[cpf 2AbGIh#6m05N^Gíjmym9% |OmpnHUFҨ4iarU5n:_ۖgsV{wg&n}hc'Wd|dT: 'nΜ=Q+6V4, (I1;Q\_6#M]>Neuհ}M1 t^..^m֋ȶ{ #F$jVVٺg B:$|QqX"!GzzJ Ҳƈ&7֠@2@| f>gNl4 t3nڬaDVǭ(J)7m,L^Lu8PH`^ .tTD~˶̵ $`]]Ӵ- 43;%m Sc6Z%M?˙@u{?V[_:rgmOh$Ϗ%땵}؋oz;jիޙ0x}znzzZˉųḦ&AjW.$tڭ[)5+Fݔ]e 3aX4 Ӷ ,aYipKr,#jr̰ &bW.3ھxਰEfƥg㜋L:iFřbaB8% P#D#] J<'PZiN~p_ߡ[o=z}"Msfk KXMgO\_z3Kkھs=#[xҕ_?u.;>ջeg:HK?x}g߶>wܩၑ.8ywHƓ';w,GuU 'v #1PnD:z#^ǫ5L؇? '2ģ1΍Lvr-1u3Ou:~{?.wNvo5 *=@bpB#J25*+cUZX|rŠ?=ODg|쬬wmgM+NzqfLiyA' ;og?q'(?d@aC0AfNم_۲yjQblLixlTT/h^f9q RAiNUZ! ;EK6j~fbNhƣQMR[>"Һt^._6,Tc;s8#T() p≠SDDcotS;Z~GNL&殝p䱅+ skUщDdO6.חK5%{^v]eh ȘaZz=yN]{ V|- "m;n񾑽;)ޜ7~N;@>i2wyjZp͐Q`H E4S'|~{ס|\F@"S@J_` FT2_])O8qp[5m$Ts݈Dm:_9; 1 8)I1Dc]Pnǎ%CDʩW0CFโI;ekRe2 iđx}v˱)hT(-zJ>:ʾSfy72`bJmLcmKWyY?v\.Q@V C 8!j[2X/9S~ĸ[93 tŒD 0ch9%z{ٮ.]/<Խ[Hծ3DsF'OIe{:i52cS>i3tki9meL ܀ _X1NJhr ]:fp0P8kKr,^O\gIVKݶ]_-^}gks۶e3_}rǦn[uݮ?ο+k#=pXuqNꔾ/{V)2 'c` @2r30iYan[]Y_,n{b|s?~'NIVg͔i]+s+ pvBFwV;wA>ZQ-_}DL@D*F! 3f[ov+ŵb#du$/}SToXȶsh,-]?O>~~ "C[4qm/ygz'w) kŹgGdz#'?3?kĹWN9z˽>T__x׋`߶Gnڵ֤m=7?硏:sO:K0,<cZC%8M -QZi  ?wz^dX\*[B)٨7 #Gﺻ7?KN43 V!jN0pߓsKޜLD[ڛ?Sz''vFMJC"WHmQgyj&+N-??06yۖw^xܹ3@Щͦל;0pJieXWO}]/?~N:Ik )ZF\ )T#Dl@\-^t1]Nkqd婦di8I髠َg ֪`FD;m7IIF,.Te J$H$u Psb8tC dv&L` "A QP8@& ę *"n&4ڮ*0)seѨ3}htǯv'pgFب"r&4j%)`ݗr#N*9#KXt1;>;`G" : Y<v֖g3t2+v[Ծ)P, }ý-v& y$45\>+?k`lP9Q 3֛ݼ}[UyGϜ ! chR@DJK JR2$G{޳"ƣH<,.\?KM{'[5ξz];H1&̝z-/̑~+^x~|Je}'}C pq<{}sϜg=+ T2J*m )t H! R+m/y?}hnk`\-o4>Ort7JPĐIԜ1TZR'{ovfcwCΟ9"܎TWV:N[̅sNnG3/%92I@M,D3@* gȄF^:}潟$of!DT:ųkW 2s^sZ{RT@LF3iAԬbd{WgkN\LN mƓN^[_m@7! $ LD)I&hWR mlxf jSD0@3jb 3⠁(ţ1RJR9|ߗ۱sW3}frw{̩wɡp&]&o~=iSScމޡM;wJD#U JFT[7 "8 qn2iBoӁF F\*U:ĒD03,ƅ0#JWNz-wtN+h5JDPi@Qx4כ?}//py|'j'!Ы@KR UWk HJjd Rdd%Ga%O170QU.]x./cdf1;hZ>{/7\o|b{m޹Y[}^x%euϼZV\b/itx|l8_}rl?ޱ'OƫirYsR!V`!ƑrJFVmbw|{ZQ &'`H = oh0wN??v-m}"Ji DD@@Qi]6|C{_T&i'=ãz_ZooMߩLcL6]={faZ0@QV^1N*Fet=2Pؕ|U򿜝ĜBB+5i HqCG;i@D!To+ NHZV?0\hv!Cd)LN*džSSnibn2I d #@/~BqZfdnsu]1nRq'V-.Fc=f?SNotb<ߛslVr)`i%jD4!y&c+$ ĸa<).a 3(  jM!F9B2_۩T*v QR\> H#FDl>f+kzլW+f)M粩OWjדμǏ_/>];DjmmեZH3dˤcX6﹦IV.v;h"m8yCA5hι*E[m~㍗׊%r  E#j65i&.l9"BMϕ.L]:}}\2x}ܶwetT+lY}߮g}'ak퓯;InGoyC=rL&uo}^|la=[&.:7m>GΦ~ݖpKݡ-?sCoG_ں~KnM$ =@04I*( CIѨWZ?|?G[+&@Hda@6)@mv}/lTdĤ uT́!୎7}򶝛6O./j Nx'oODOt߃V׋VW|X>uoWOgLxZj/әt*\zi^{/L%≾B`m|5afQ7 c0m0#k3o߲3ۀP0[k_* `FLNHNඪL@JY6FҎ/_9wmdψ%-nW~ᅷ;V >{ߑCCDjnO=ww~{}mͳol}N ԯ n{GLBϾ+/󉘽A wSzzH)5 Өymwp{O|R\g4@) q8#b:F&-n|sW.%3d,I@E@TxZ7M\^ne[wõ O$7w8˝9s+K+톯RThˆ1bP(=Ԯ=R<+\8q'.@>I"Q*,Tg5ƛCH)NjiI猛vW+?;w`: @i ȰLA6c$AٮVP9VBa&kײ[Zk~6 P 6+ 3u\ 1VWJ+NuM*749ibF[VQ1 FH)ϧ3ZVZ("5n)ņ 6o1†?Y!@x^1GJ8F~r@c?|cAC$f 4m dKj˧/^+BX%8gefxIa؜5'&860 g F|[o[(_W'o]v}V񙉝{>_C'^}+k+{?ч>p$jN%I+x扯GwR+Z |_O$RIqZx(e_Z"b =]^+k-n;zo/|v|pxufqivIh[1doDDy"!(à0i*T5w}]g._4=HKXM!iBVgt_oϑwtZͶ'ٱ!3_[ ˝Sg{.^24^w|tzѨzVoTF)$͝;6?ߺ+_/}K\l*m ̘@Ф%;نI v`D 50 %jJ&ҕɫm&v+A5LD0C#ѬkW AUXCNK'V svnw83כu@+) 37 YrjmL9H]202Zwh6rSJiTH)$ ii @"@7rH 7tgc&50R cg;Lmz}{¥뜘sj"L0DLj!co@  ZiQ" !q$h- 4FĈi2tެZ;{6mF-魮.Wբ҉Fc=v0PAWIe# jQ^Z]ۭF$Yt?;;YY7֚djec$<.hd qo %M~g)( G* 5 $j-RZ4ޤJI)B_OoREw۱hAS/#_;~ᝇ}05}aO}ӫ;o>M]?~HlUݎ$س_{[v^?ԥAҤ?TXxg"D ax]oyaԡ拿DJˋz I 5pJ0(hbF#R@Zi0#XT??Oq>>LjD "@vS_{nbMفLf+gF:tsT0NlB f(9Qo;n\]__*EtdD84 hHkT  "T4HiF"`hB @HVVzͿ97XHv5bI CWg.Vd9S*KN"Y/%Q /] Ko6k+; ]i4Ne"jReUň6l)Qԛٮ0Li$f0b}tLtD@ZiZ$^@Դqc HC, 7qՍR(EM6LA =mq>2ؓJVˍ2g P}8hHG"d(D@IB rb B!W77 kuWLgǞ-w}w6ۇ\WjQyA09㶓E"i <\kvZ-(eڇL/W=;^dN*b|Dk+ D@PIڝ"R;V/Wl U(W$&5H@"F5)EV Tdw$~prj#H3Xzzzfz׻x~*i,.^y﹮?&_Wg8t˭Ƭx04r_ av|뵧_R[0r @! .,WO>񉱉FY*FPFh"i.7§ $2DR3QkM\jͤG_<;S-d)*|4iR++=t~SWkgϞ9uy֕]qu8AB00c\ nTTHI9)Zf(F d@L1Фb@w0pCbpYI0P)WۼЎ+zӾR4, 6*2m!\8k$rى=*^`3%&Dgx̅fbUrپnil^ZY̎ L:pmH֛(hV4'T86ׄA&Czelc<ɺ6n4 Z*)"p.F#lTB/ 8 $9!SAF+4b 0b"#1 `!Ih5gJ)9G0!Q &A 3_Zi`|wmڻetlԈE:n^V!lӎ[x dđt@J׭{0tn ;.MxzZ>6[nq|WkOkFh0Sh`DIcߴ"S#]8O>ȀJR*l &"RK pDJ$RZhEZ1et{3x<bc$wmVV73o|\n]vv_~閻n={S/LOtl_-?S׿}sVNoan+_h#ҁFP㜙xJ'>񏌏OMWZFseDi&`_p  4( LaU+sLgeOz̶Aە,|Gk T.UC>%^ZU[J۷3К iȐs?Ƒ#dȔ(Iv5CƉ+JЫDӈ0s0b0An'VQ]YqˌETsh2x4H 6j,_܎`Hd$ﮯnG09/]<_lgsTOczLpry& }P,J]bRU+^жcvw ;;4eGje}Xp@$MI)}Z*E\I%IJ~D7BЪBav#b!ׄ7,' H#0T:F1|"2 [(&"@:&P`{2p^c:}]{vسg{vTRze}uI! ۉE16Q80"v̌:0wL'i&ƹ!a3bsOb4۽,;(L\zƢȦ={5j >r!RhlGЀ4CD*=?tW~~741" 9 נafIdp$MJi"Cx @ES1OXv^Mreev~ر]U۟9ie4VnATx& -25ӝFQnĝ-wSKwswUy'g׳#c[tib;8&+Vض[t[m}@`0T>A#0ĕf(-fq߫{զs"60,_=~R*ؾw_e}oň0ҙ4Tg M`(P0ƉB$$VT5"Iwp CDt-L)"F\gqj3+&0~#+s@iq`DL 4 g 9 BPiƄfLi2suv\[@oˎ9תz3tZUsnHaWb D$(Zf{}s gkXAz}#c Nv&&WW*l]FS`z78ZiL6 ~<4io^!%Ѥ4"rA"4e+ZݗP, '$`RNjiE LjTP8t5wر23f?ƪ'?KB&tW.lT>Ds!;TPƽE0eDD{* xK~h^ ˧^MGhcIB%(XaN(L=;\x?хrRQʊ ,&T`Z fn`XX et*P! 4IL@ VAN{BJa")` `@9vgnD ۷?̏ObxcNN=ǭw|ࡏ'*Akee™35uuZs'/66bxQF)lmJzn֮oƃX(ʗsq/QZc=$_v-ƮuQSp=j`px4HIR/)W6ׯ,:|,n+(U.Ș$I$(H (0+Ou;T`=sWK KNj`K刄HJ+"J+@)Zk_B/uxSJ)TDOA{A #?HDRk͍͌>/}Woƥ~~ǥsBR [ L)L~cy--;sa~ֽ]xYmKbrݰø_Y<=Q.Zda- |ؤݨ~ש]9c_ܮGt DQ.&LaFv_vY pDabP>4J܏yܣq &AGtc2xgh!>ސ\QP#XvC!.WR$ZAyD͋Nf2zÕ~ٱnGu&gM\^=w zssyjmccckuyksec}yiʹ͵f6=O3de4);:A?")5>^vD!MU!kI)"Z)t-MK!06#$1^[Y<;< FT P:+0\s=fI zNgQ&o7~G7-, Kϝxu}c T7Vۏ^.t_OVPl{skVǦd\q-Rk" (KFP`I|9#0!!  kTڸT&xK? Oe:?S|ɏmD}a [aaPۻx^xbX ڑgA[_ \NOύGl~t|bVR>a$,:"0(8ڝN ^$Xz9}}WWSa&*DvGCQ ?&`ũsC7Ѻ|BYwfvCDXH73"!! [BHaAXPBD.n5(sn~ w[,[ ~ҬV7~h-pw:\.X( lyY16f:Nnl뛫[[Vsծ:rG{6U6͗˹ёL$E4k (%r{T-;\W_!5n B  DEh-(0ij^݈;z̓GRl?zPdX>muu+A!Eq,PIhُ< yjtj~(Ֆμ|]e?K'SPp-) CK+ v#f1]AqxhܝF!@ fapsXnp%BF2,Fi1 G"b60ȵR sW7/<|^{xoe~Z0vɠg+=mI}#ox,e W,Gsbej22>Q k/ K m%IX]z/ʋϟ|3/?.--67:~?M٢AE"(Á4+' H$>oߡn׽7snnTז, =Şɳ:souw=~uw%^9uNJY?ʁPEd Be]ܛ5*1FP3(w$&C B E1ZbٲXyk}dìI䑗N?^%ISEJtʚS P-w}3/Gɠ* X" akE88I`p#-Aa<:ixhQHϢUt PJجg׾o_+ko>ůf^w?{xǮ {˅Vcmubv{OAܪWfv8>S9^6[@tlvi)W_Dq[3PJjNXR߬W6 M8s/>by}㞉˕"00b9`̚+18o@bMFap&vWv-^ ("lYxH""AaHm[CՠI,Rat@J0a~@©B$*b0c^ˇmßrvOcuQ;\;Ʀ޸3Wo/mn^zы.\&y^F)")MR0F04M_Rd AmJ{: 37D2`0Q@(@Ad]@܀AT:0,?T.zDXAUDY!nll^y|?'/tąbib f{=-GL&s ""E5cShR* OIO UH%X{a*ZcWҤO!eJFX9?)?]jMLO[7tCaf b-*V%'[.*on9;8n@JEtZ3&n5c43*tIE$ l-#"`AAeS@q8I J Dbс6y!4D CVygafϞ ?!8t/]\ٳ/ӋĤ8$Š$Қl\m:BԬX7vf@ 0kna3l@P,ĢDDXSt:~+Qn垟s] NKlO:]|[سOn$H96~WO˿<}G>㯹Ӊ(rty|"IvЊ[kWm?6Ww1eHl( `K 9Vrex@~~:87{[6T[0j%!FlǽL̏+_/4c+Q;j* W7s!%TFW 8ĴXQhtz<ŶZjOjcrG˽oڅ ~|\1~O\:3;|N5ྞV&aa%, Auo9=:uzfm8x'|qtTȋמ"eB&LNو ,Tlҋnwm^?ǃNgjI0Q~idl,|2uP@;+q3&HD(ŁXbGluqSA]lta+wFZOMLȱ[ow-n/M rޭ-5A1,5ݳkl&wxZ_ݾ=3HҢ 0%NQc>)&-hSbC賐k@ rB>iB ,(PLDM:Q1{ZԾ"k:YkB+t|kߺV+'&w_)ݷfae؞1 V@) Wv-/[[(FAF.lncAU܀kk mND["zN#L6s#ox^{'FA혴W[ⶣI`JumÇ`"?r[#_O~oUf{W[͍Krt|"]|I$;xtr1zi*i[I(TGtݚT׶˓$ Ĭ?dri͐4;Z: R ALMGZ~t$& H#z,F",pzAbY gPb]b྽n)Ӄ4n]-P9[nio4j^߾Xܤ|Ӡ&/{hyXB1B q8s$!4;EHʯ׾Jn~;_{'u+kK'\~z%ӷZ^k:^oq&)V$i  |ɆH!(3J٨׵H @ז$(m8DP4:Ȇr"$(5 ]DZr_+;nrν]-qrt{FѪ#?tLq0Rj]|RڲGnڷgn.]5ZeRwm(`eq)I  ((Hv<~H|յ;FeU7lPo)anaG=ӏ#]Ο}[}n{^ALft6W7uƷT5`Xnu[=B% Q: {?/WX~AflnP .7ڗ(X@Aȭ=@H;qBq,'AfFR>x(\WiT`n4z4}?sn:|-7N/9g{Q^LZe9Ffᣫ;9>i*8| eY +|q)?zo}ͳٽ Ъ^ nk<2>fƶ<ϗ+(d0 f~OƯoΟTJ$M6W6:(7(U&Tkˍ&=aQvZq?҅)gL17;:1vgj՗WNRR>tdv~E*ð+fO>~_(_3iED ؂Qʡ(#qIak11 (dXIS&-f2;tǝwo::1>Ɔ{ʅV٬yk,< ^x4s{:W/#% ,ZLFhhtRV+hk3|/s/}G+/>导J3Irg0t/G#l*Hb8ZaQZuceZoU۝zMr)ں\?{ N-]G,bn`-X Q@D0$@2 "R`@Iq?$l&w߾odT ~.SFٝ۾}bqWG\429q?I16q3u6TQ$1J5) A4VrMDpPRAD Wt,>˫E H,*ٲMX!%IHԘ/^8ܫ{T tΞt=ǎ%vf > tY:o5o]1 `XPH03,Q,*B # Bfv2vJ$ jf~[n;~MGo[1IAemiuszl^:s˥F`iwD<*5Le{e҅3_G1m~u007ڍ#+3|~J:gc0s"+A_, ˗_9&wW7沥$_|/ʩsK'W޲#U ^}36@k/eC_kx(5!ZPle41,d@Mҏbtmu#Gn=wGjjB,Zх cN>rݷW֞r|ڝڿ}ݲr+};;p![&gჟ!Qk7'F~m,jgʋ?y>Lj#PL6 Ё*zʋ2~Y%ݬVjwyF֨v:Wl;kKO>ԥˆS$VEISD\[@ѐXF5A*b(‚IhrW Blwڵ2{{||v 7WW74Q1?Vsc}+,Nε[|~=y/ӟ)UQqX,T&+6$$A@Ehڀ s66(LQ1R:M!!x*mŸp!džbD"?\n Nʊ B`LW>ex&5J0:zWڱׁۮA+' ]ِ}PPh@0JBA1sfvjl_tXI &lfWrCb`A&T :^ڈ;56y=wyDP]y}&ʫ_^?w!* 39I߀h$|`شbaqn_ד>7+k,X AXxvrƫ)J#s^[n[~FnWH[W]yP>Fn#8SA^ulMklVf' J5׾7gϟ_eTa =MJ eC!+d+q vk_ZW, 'G`%HnV[]+㕉{{|oJnmuSY#-e~qzt兯=h4H[u o7=rwqlo8jq' XR0{:z _C; nlV !=/hgfdݮ]m`2lg+koԷե+j3m3&ي7k;wo/?_%& $ !7C8@hqRB"ʂXdRF) E ֚_۪;ho8~}=L]_Q1۸j7b+3ej<>즹5c?6.La (l  XAD/LXѤ6!j 1Ȥ !5F\tȘTdZE "Z,!1[N95 0 2(EdER`PUFLͫ}4sLYc*`M }?^cݳ}kjz6Mg^xatfdr)- > `4KD8>1n;}*OFvjB``baeـEGf'M'Gnuwpr#ASTlo\]Y^0Vff}/[$n `m:?AԊv$IL.&fV*TAы{@܎ɱrw`Ff/|82/Էַ.Dgʓ x s^%VϽpXedblos/rFl:L.(B_k Dv +ڈЁ5K̀X"ҚxYotc}kcǏF8&b'-r#G7_yg|~urs >zs;Av}r2?7uyϰ`p)ba:!!_F0ij뭾ozm|ewibr|y4Pd^juV6.l.o4k^>*˥R.>ŝٵs\V7QGE*v#+c.D@A.tΠAHHQly(‚"qܪ[ a/8z[o[=OZ4IۭD~d♋O=?Mוgʤo}tf(Cf""8IX39KkX) fP1]B4CHX|A,ߚHU2$(F ]A Bl YbWKfFm T61}a#Psyٽ*(*vwcc3{ gdLH412!z`l?+}OOUzD08̔2oB`Rjs}c+cz}|ۭҙK/L0{fs'B|v[پXEѹ(3[ $A@8$8v Azycٻ'VϞ^_2:տ[>{z4/>{y}oD6Mʎe@,bHFAaJĶ yn53+.h^Zkٍv&`7*R6L (S r]~y;_~+ngP&K3$0?Lݣ"TCtZDD_?V+3Gz;~C?}?s;AխV5|/e.:G'G3liz~fqjtnwXsBa8"L~}A@B@ڵ0Ja #TDդ mZGl˂BZDQZC`4 .""XHlj? [4|25ܣ/n\yxATKQ6)[$K% D"H}Ξ(LjC# Wm4~ǝG򦷼~nʙO<43+OQ;\X,z+O}_\$O\&=r Bd5=16^nMH +A!ы$7ڵZ֊Tsׁ[}m?cn-tڝ ڳ6nKc_X097>@T٬ mYϋbK?l.[pL,BH@Ĺ!bBPZA(lA|0X c@5; ?໅:!dBѮKDb[ 1En PȆʼn^lQ/~ 楋˨(CĝvVZM^(12婩щBJA'8{حF27w?믿/HzW/ Ҿ1}ψKcJa^K34;%igK̮3mug:;{ϟtmeX뭞_z] +W_={\&RbXZZFCD3i5BH$L+V5գS3؝=xG5c趹|lPG]8s_׿am|22R.d|c MjjlBhԤ " "G(N-{qj7J8rt]/=skw^]|ŕN]zA-ϝرܴ *;͏C/HS&=9^l;c!$@u&I2 ZDHU0H` Ь08 ~\ǎV\ @(L S  "+qd;쑟^;]T m۵M[?O|Ja6 -"#"&PbAl[Ͷo{o|z߭w8RΗF*?zăM㹝{ ۾wmw}{~x^EjjXw|D^ *$;$q)Tr1JcmSves+/]"\LBc}cxw_j{9`senV\K6P[ؿMwg_<&JRev&`kR~|6n4,4[f #hЫ,.h啗ϜQhtlKvqtbNJ~敫cw4[ξ7;Y]~n2 Xv-r=w:2M`0xxƕ3Fޮ{Pݨ~I0*NNMMwZ??w^ު\vr2^%l̀IĢ!RtCDD8AaЮ[3huFݮw>K?Jn~˗m^ R6KϮ421K6S힟1-Q j $H5(6pV.Vt ǧs+knm{t Ҍ hRt$V# HX L1{gY5J(dEvl>d~NFeY BJA0(wVhb g*͆PRkתkkV߶sѻnKw CilJYi*.O+#ӓ~>J=fqSӯ\wkp[ǞyO,;~.@ 28?Y |/ʭlu~~uбFf۵ufwիo_ëj)3M21%TdxXa^M0=(gj$vgeyQz?~?_?zLJwÍ7'ʍΥg.;׫4pܩklW/ پ}*3V<ҹz\_ݽ8^V  @TX+͋nVc` {0Go> cO0iwlnflf[ ;0\0Y!(y900a =Д ٫BX u5<`i7vwvfx߯ oz3't͑q?4RH&(/W aXPN,;f+1+F$ږI|*I{QWA1FJfsĎ=|Nҁ/a2Uo^~yMcTͧ6WlptLֻO?tT).^w]|T$ۯ;z=TV_(B+lId`!;)@11eOt[?;8pPedqeۏ?mJR5(pR#lPT@JMO10ZFDi֪K/TJ{xO|?^VWϭTTԊ[jPٟH48zWiDvaLy*Wu H&W )I5BV$¼sVl"2'8w˥mETfDĂd8N]UQ`&I!j0-=]IAi/ @yhٶ Zb&-&JJf`4MPؿslۘF̂,D >B M<2f F@Y@ E8NF'&9OQGc !#GS>95od@ByZ_v {gOG݃fʏD21g&FF*^wZ>K¶  'LF<1rV?oc:fcks aQTھ߮/;o[k582e*y3xOO'wK4Ƨ<{꙯|_֓/hЯ2V)ͫ++[^?ݿO~'_s]xsol*?6;6;Z[H T H+/g#`D!jدo_lXH&_4^މFպ5`1Rnb:-3&9f/Pٱbl B e/ k[f#{cͳϽضY;0{_M;91w} 3a׫g4_k %+< ז7ˏ=뇟~rr)B d\*I8% A#(51ZqrGV&lZխZ,{oy>>GeQV[zee|U2`r~6AY_85_ɔFĶ -"G1&1f Q)в 1D7e3(CjlEO960Dvf4HH, "# Fe-A$IK KJȲ:5l_ r B%V+Hl$f\y01)DKX&aQƀ  PMWyM@yBHpG b+@$MS6۷~$_6;$>Nzfb!^y'ĎyP 0 $EPALAtE00 (EC)g#(PQ43fq兟TW.l<7۩]]Z Bn4/זW_ﱶ__\@ SAFĶ_Sկ}<3{~LJ} , ?o|o3gwN\XeRT*d2vZ_]#C>ɤ^q _658 ebE)#CylP`Lm4mj{~in)% 6zF=+#sSa!JS97،I-)OVfW.Ԡ_(Mǽ4+MeKz2vryǶ֫o;ڶ} t/fs(2 @ Ê:O ډN>KV1 FJb6fFpJp$H-(dq΢h17fiSq=?Ѓo{Y0|rTW,VJ"$/ Ҵ}S°X۶ooqbl! Kj AcRU(6eLE4j$H+ b+w["\aNꩭ= :ZM.2qN,uj5!"0NQ)f }"@h@4@@KgPv_so"_|$4յUeReCgӏ>"$VAV jqbAKɋ @#r"7 h@H _Gb߿٧~ą˙l.uGu`Z /O?NA^r^.3RBnvZio<5! ^\Pl64 {ր 'Pb,jUb1T/_X޻gtaS|q:8ӯ`lGg'\ȂQ~j|R9tzK_O_Zm碭|o?WO.Khg3t;ݭ͆@w7M#閣^rY?;R~V8wukXLDdqCZ "q&DlDY }Ěsz̼ҔrA E8$`tf26M[pYsddn&m.>D}Ωm9nU7e&-oo4N?T۹;',|Z ԐrHֱe =lؔFB!R>XiB&0r "*W 'n*NEAֻ&Bxw>|䝯y=w|l4+K'#t+*lYP;]Bޤ=@0,B- I{Il@_xꥫ+kr)F@MHp򩧿o~楹ÇG~{fNxG7__?wϜXBfbllbt4 2iFm%\ew>w>g^p%`2Yʞtsi~iދ{噩L0:;k.v*PC7 1~hD{~HiʗR yd$"ɠX͌֊k$q&&[̏MpׯﷶJ3Jeky WˏggS`kdjo}6gKgN97s(`떴1:cK"!Le|6&̈H)ۺl/2nզ[1:L&v 81:yM7|>߬6W4N1ՋRqrq_n;1,0P\nzD6'Ӌг&O!=(ihC2Xg"p=IA߱Wm 9H;b 0 Y|=俠0T" #[$ j2z'L1kEyCf( G,jnh :-Dyb**$qT[BBN`D `WApgr@(G ؂XP^ v@.Nح>%MP^sSer|p?}lW@v(LsCAPy*Ȟ8wt׻M4FF&skg^^9zool]ٺp>FgT7kLƏFvӫonQG&HGPEP8}qbM9 pnKN/M3:5꧞~[|y/7O|~#;k_|_g?zimSp|23Q(糁[czA20Q6 >سkq{bʕ4lI 8Y9y9ĞEۨVVT0bEAT"\az5l-+̏ot+s;: AH)i_yfA $hr7ʩ3}{ͫё]v ҹ]ʼnRuN9rm߰r wC<ڂxaQ"(bX9H!X[68P3c(0:oqϻ?~oΰRX___[o@yӞ3*S-s+W@hFk_)EhX0@"ǦD ,=A"ZQ^{mZ9M@,yX$!kR=RB? z/|-!`@ d;Q+ E!*V)TL;>0Y@h%fBHZ!hQГ4tzV%hejXƤI6ED D1Ȇk !+DAaqL O_9q #33t6EKZ )/ Eiˆk7ϝ}_|eum7/WJw/94cG&R+__?ͭ~{oj[??'_}ԙ}X+wZc*{^>_O|A췪˛ڠVmBe :'S+Q꠷9|go[q7KVAjQHE9%29$!8GC D@d&{Me` FZ|}{n۾mG.0BܗA܍5 D hIadlm^ 2$wIP#+`V"0YdKh6Ji+k xJQViO4& @(cl_G;̪<9Ι$.*BΊqɈBЊP J Y gQ )DO M@Z!@h,2#"hː)C#vR__N4[GƃlD@  "RM0(Dh`j1@0 BAB0 M81ۢ8[+q{QdlRk8<]d|O[D $Οh8.́9y<ƃ{_ W)]ܻXpplL/n:rfM=&U*ƃ`c#mvT.7A\)eY֤(@$l{Cb{S t˫g^6hrsiFgfw96V>=wqn+ssc3AV/__l4:/WPjfa|&32e HD-*3:"nb.6 EkAxў(@Ũ W聰) `2(TfS{G>{g➱}򠳎;J͕W=D+M͖FG76{**03s獷Icr=34* j$>G )EPD(BrVNRJ,"< fU&ߨ}s_~ 3Cry 2ˣL5F\M Q;|?ua_"з&a֤lv7`1V#QA&HN^kw \&KVPFȖjkE՚E-*O##ZQ9P Q5Db$`Q"DA86!xbPygO43 7-]8/=qB"VH͢(j6;N)[o˥noL6o]>u/hX߶φ_|jkuc䯽LՉmsQ&ܸrabiZŌHPS4~[_z㭵3c3!5؏fvضgqz6 ͟~+ow௼#3/??OW+vzB!Tb6tu-Fs~ׯ?|{nȢmoWW6Aˏn,OZVVj \}aT?Wy^]:yvaPB}by]$@ &p|` _hS<̈́jXF4d%VFI̱|0֭U*(F'KAʹ> ڋ 1Ϸ"B1Wr\js3y|߻hQk7V tMoYM4;N8:PhȨM?ULuucֈ7~Io}bjMLɓ>M:~' "!%\TY]MZif. $$ a.!f@&O#5F@@+TZL"" VҔ Y %֤}R5^- 3~, P7^#" @,:bѣPchE"E D  {n: "cfޫ5a.fHX,+LY9h25 :SM:H#/c5€@CwL,#RdҕK]Qa4cޝv] Qlm-\\_/|rv*w[l%M/d**{mWۭzqd}'0^<4 su&ZJ:3/zjq8NBFlEHya"f+>4yvv$r\}_ޱksϿ#3?=oy-v\<+W|T4V}>66ƷO~;VU6LJe~>ʇ&n6/5 LKzKW)O(?Vʏ0RE%N i:E<Ȁ)5.+L@-D>*‚)퓲`b )HKh$#& c#m'sSu!Xib63IZÅ\&_/=+?x2;;=ҙ5&UذTwKZ DB'hBFR("J@lWWwu׭G7w׫o} +J爔5atx7&$lA SrwADe$Mb;6=U-_][=s1Db"uB rt+ 4Ҧ"@$$ T)J M ЂYߏc8jZ Rl!)FbQ+`qv$T D $5&LaA@ 1Ģ :ǽX"L$A *(" `Ӹ/"tSw"JMwǿZ2aZN l|k[^=oܞ}Ǽ j6ͪ~1|+gVrѹdnĉn&t1+ulitdj[itSt{fsŲ庭֥f;M+qf^X}hhumo=o?̍hsꋏ*8>R(sQ}x ԫ\_շ|d|R[ۺz~J85-Tl\ߺPɍƦ'LdjŽVQ]zltɘX$aP>vpk!|BJXODM<5 yJk5[ 9 (d |BPf|Nq\JgG3/ظoqD Ժߩ_=y+O=Ϝ={׾[5{Sg.]ID\_:,'N HQvDHiiZju]عms7:u~Vc֛n;=8i4mJ.@Pi ti즂' cDQl %:⎯"6b,"A$)t(IH lHk4=4A Fa<H2`IRRt|dTc?5ˮLp>A,IQ% DJtRF:4Q+4 EIpa GP峲*9g3"ͽy^}}@B&Ś3e4p@FXsF{1)Afް? t:js!VS3h8c8r 5TD$".gBsRStIAd'` !T>87CDQِf^ϼlX>v~;KG|\_znD(aQY =+67/?sÝ;L U퓫6Sͥ7^m,:'ƱQ7.7.j2::$O+2nw Qtv~dYtXX6v;rԙr3Kٰ_}OoQ}`X+j(hY J{?wGG~Jv}2uַ{[7mTm:B?{Q0͈4g imRbM- (d :t0?9)0HX(I yE@Ey(/ F@bTi,20F(gيEeFLs 鸳+Q}- D?{Ύ;6a۱Ƽ$7y:rtAhD(+7ywCݾuɇ?~=g<}g^ws|GXs;VQd (m ?*\w_ypOtIJ/a\^:O[ 7_{oMSj Jmaow;x}KѠ?7vx͛ӁQH{bqF"@( "E -"Zg[k'Omnrpo~6)'9)u9NiW_yww~auGz߻oy;/da8Qh4(@0R"Dm]~xϿyʙҌ{w_wx?ٟq:<ۏŧ~T="@N +NgvՅ~񷥎?ꥇz^9{,D^ןx~ŋ+ Ǣ[orTmٕ'ϝh.dw_q4nlZ%78 4s"Rt iWm~j:}?dO& ZMkuXTl\?IR-5QUPM3qp+@t,Rlo0H9(ORHZF"Q))GʠG[ot4֪vᑸwo""WΑR!m ÝϮ%GEA#p;YBCyGf~mԛ$Ve2/|k7.Z-WZ8GTD>0ڸ7^y}OM3>;4J(vN@DQ#A C"Tj2 ƫ8y˿n=p.qւΜ^|{сR8d@BVڊ]!̓ ΖZ.T.#h"[ 2W$ sW6A" bAˇi$l@$uA+) 8 `3HC\M'΢¿(Y!'`!yѡ<tv8LRB`fҀA!"E3(D h)% @9R~+Vs~::Li -THJSDAobG> B5O-L]Vέv8r{[[^P,nܺppP]\R>?*XdM *汓K/`|c}o2N'Cˍj9 BZ`4q"mPQQ`ha$f:vMFSm?)C9 Ҩ)%"@B*bC 4*+hE2H^ŚItqˏN{CWݬKa-A7[i.7XgY#txa'waRk|2R&FNf(#A&{ׯMx A 㠼tf+[QRn;זrxToO?g{}_]}ݽc}oi}(|Y\3Tdt:MgK[ڋ.,˭rc%XggVd6wǛӗY# q1#CUx*",F [̳jk)ϥX!a, hQ O!5*E24ܡK QتcfJQ1( D 6^ziz뇑"ǹwO۞2@,(~0.&tѥVmy% %:4␝0h; ͥN! *% x$"YE!`^M @Ȳ X/RS7ۿ^bo\9wO yP&N1D`hkw2ϕ[ Omy嵵̩b+0^ܧ_IJ0GuZ8Kk7n8Ѡٽukx3+;v wϽp:A1J\#I(R R6Z)';[NqZQÅFmNEJH F@"=lMک{.`BZ 24ַ[XYm-`%gJP|h{/'~.CB˅ˆb !T@&F9 gHx{w^uQs)7б2FL{'^\+V+;MST.yrK{ۺڮiA{6giLA{oU-ksE˭"`8tj%Y,'O#}.bmn˦E}P!d Cܹ1"i(JK)"-9 x Y\PEᨫ<AAa%Agt9k`?:w$͂RI= _+mg.RL?d B aQq T}Ђ @sa"aeH(SJ ѡ~+|ky}vq:o]~;pp{gxy^Fgj#lcyU[Oo_޵gIRJ(#?Du"@D :!"M@Ҭ;|&~Wwիg P 4{vڱxi9wfH.rko~^ +\;:ͿM(B?7yC0O1A^.[IP2QT*Bp{|o|`]v8銠h@PL~/RPnyѱy7m|gS<̯ۙԫ F "IhZ*DǍR{-sA!jX_lFlʀlqh^3;fY(ipΦ>*-Vr)C/ Ik+~~{-߸7S !VNACsd9rIDD (Iޫhf n@yV덛[hGu5:"5ob1'9sUA_U*&oC ($4BQ%K, Rع)T$  vHFoiXj J!-P(*DЌ3x#=pao&-.? KwڸrQ T:<\'>;"((L0c Aэ&P!'8L;~&x̅{Μlydyxԅs'׮ڛ7n?= ϲ|lIJzZ]' KM4W`x{?y0(oJR (l'\yqk`RK6i/0{7߻set0m4[LFtЛAV?~^@Q0wr\Pd0ܻ3MGdZEr+Vx0GWqW'Qgg[{aXLfAz6WmEZut27$֍W;=yZaa⦣\OOs?ޛ^}_cD"Mr̩[ol\{/Wڹ^$U 4( ѰfbpfomYT;ߛ D *B"4$y l;V RWA U*"TDPDB&g(R!JT)H8 7R@G %P RM d.π-A֫kgTWNVtH1sդتAwŏև[[uUY^h5K3Nn۔ (M`I!cbJq^{Ky%7{gaERs),AQ?v//|_ZL=/?_G=D ?M8CR4kBa.?:8&~O=?˿|RO^8vqW\_/'UM"+inFbgJ9͢~+_O_\|gNZ.Hc"a< ^&J$҆l7ev֛Tϝu]>f,0*RߺMǹXa(I,@yT'nJKmL&p&6rId¥So|g:V!megW/VΜBu_yTGaz+vL=}o{#??3=8xW:YGVϨluoxQN]l8\WIf^bQ-EȅHxdڍz4.r4@-Jp(lSc0I:0%Av69~Z6`1-0Ix&HJ8ECG R$&a$H("U*+ =dbkv|qًg/6ZW|N\ʉ"ev7wKT];jB\{;?7?V=|$F$`^DRάdͅc]>l6zRRnZլnnaPT-Zri.) T"'ýhJK-B̧#(9\ Zp:z}s}XjjƹskQ߹1=DM8;Pq=1~xԘ\8h4+KtvwުWڏQzQD3fP$F"n\s퟾t-Lc )!*`ZXh iQrccmǓ!ΦJ 2C $ J3`V¨"lӢŨ w)8T~g_<8Bޚ:Qa@iXLQ6Z+tgs{@ﱷҽqo2&3.;;=@R-/_;sknRW<廇IoU+Q~9/,[?{TRI)]m9hHl̨# .`0e|/)#ਲ਼{Zw6,/d =|f'l2FRme% jT0LQ`†Ĩ6 $+k޺٬^*Υ鄼@JVpc{'ctj)Fo~G?}x?IPo*j\w0՞N%Qz֤<;k/=W~GϞ?i= dkIAw?~3@$'錂0<(4dNN`T }Cz`kKH ȉ V*I8A0 Љ.wƏ]κ:&)7'~I@QXjjY+Sg3dT8"{7BRk% K6MTmehBFB/Wx2ܻyۭյ )yO(,-.(˯H0jvn,7kGګW..5o{klݺ}g?&b& Xhhxh}hͤ / ͬwN P#Q˭0 p!U o\~^z8J]Z1L O&AlܨnvIXd3RKE Adɏ}c$ԍFcժWK~"I[<TF Eov^U,\3.T 2INm?G>O~|;{\QmJ 90 JQo|R,Hi-ٴvrKk'N}?K+?㫋lt:V!,#9}T[]0zpƻ5"xٗC?4ehL cKHzK̎FcR!)_{EJ(#`]h~AfE$wڋ$帹h격8$SrRIR53IA#p6Q>xy ݭuSXYtNz=*ʉ Í;jR&ֲMI"RE[Ͽsg{̹r H:, κo|;kATͲ r}xU·o~͏Kqb¨u_lFٻN/ܾHRWI7*={5qʶMnJ!jq!EY0aP^:?t‰3)"GB` E{rς8 lo@\ւdJ ,J/'D 0HDS@P2sV!J#AD%Ry"lmƓxpxLF=i4i]*ZN>RLd2f'Rh A#EH,"ʠ҈D2#!@J t4V RV2q믾zd%T^R)RZ+A(9A\F/OI%ATk'2[Y4 JITJO}?|?/<}h<*B^B-,[vI!tIb95'B$(Ȁ,(]3"8O~?wQ/tXMG?(xzۻ}=p:;}B"t?O_鵣~✨5vǎwWŵѡh&Ҏs"B`D<F.~H&H!jFx_ԘL#ZC3;IlСLOJXHkkKgqBAT?~ t&JIZpx:N,j+YN6<8P ,`mӰqTj(#wZs)*'|_7x5h4%{^)TaűJ{Cp8y;Csg_~;Oz\*]zAmn^ڹ{8Z!P ZhATXB/z=*qoQ ( WSs.s!am:MF;䌁41 9 8& ځu)(g3FC p.#1p@=!t?8,RjIuι4Azۇ7^x15"9A!DEk@raQ iŨB' Q22D 5A$4^<<84,7fѰTi8-") (@O,Q-l4 8\'rMY/ܳ|%|0~N|?Hnܗo?]<ت\n7e_mI",z>T(sKP%"vNlE!UB@ E@Xc(Mw0{;/?ՙVتb9)z8CG*wG'NDx4 zBGѯ?tngsk֠;  ` @2d8FSVFhpaR<])5d;0/Zաߺo{Rs1(W̹RDF8q l-(%Xy$1Pwl>A4c'KpX)m,l Pbaԡ" 2? M/J3 *Kn<>XGw1DFYg-bn.6ʵݼ~kz#?IW˗MbuA Q gx8=]i$In(8v;N,ܷ>?ej0?a0 B' ܾrKRȺiPJ[ĕWW{G3+k|#"@ŊE2 |Q4p @1#vQ6폏H=:ǘ16DBv9eNБF};d LҾl>ui/WkKRs|]FWޅh0t=F#vQF#Q@ LfƋ&^w:LZ\Zij~ȇio8O!ړtS5rE8cD @ , {n|gSuL2wr'`H':*jJ#lp'J5^^n.u5BG # P"LP͸ -q(Q 0j T\i[J w'$Ůlv:F_g+䯽oqw{0 i6%f,(D9t6GɬZF`@,.%h3/ giSwBj%W?޿xjm2@.DZMbD9 \B9k{6lPo:XZL7yn^G啗Okc85s.ĥ+/=}ך&~iɥ79swx(bJ#j" 䔔Jttў kWZS.)2tTJv/Pa:̂׃/Gnvooz#2!q}zGoBO~C{،k5B珀S=޾q=&sr=L\)ڭ -bV@0>|mVt:]@KhH @._8GRY,6IGuM^XiOA_ Zk5H朙}aB@sS"JRWY\08"ʷ/odfeѬVbc0FP9Z"' @( Q aDpƲC]x 3 R@E /@,$ ̀Z~E`{wduhxp4n:AWM;w^~IVVgs?@mH:α rQt\Ƌ5`+@aQϿ4MzJ3HTh]_8} SG 1AF\ Cq3Iollx0>:(5WÍkAH~yI9nV ;ZlԳOҾge'Div,\iK阼8n/ 7_;%jTֻGՕR1IQfWgͼjU{N%j :iP.՗o:w6ƣv-Kye`??]+q! .T\, 8=Ãk^ޙ Wdg}R KMATX 5 ;onn} au(nJSqTb sTn bwFhX$ O \8pDJG9&(.:ۣ}eA\뭤^G9  sVAeMIt:4/[D-h̆3b+7jo\ۿv@Eo DhR LQƱ"PWA4#j iU(mX+f͗?'~䣟ϬIf5)J  B@+D-=^\ F"D`ABqH8_+ ER! eV\pd>C ,v-EGZAWw7~O^8et:{;du~uΑj!/<)7A`字Ȅ5`@*ex4[:qGA^N3)BP7dU>g̉{[:{|I2( =P\G`7;0SƑGLՓV1VD2h/DًOtA,qJux / :ȑ%R?K'Qk_KG[Յvn6|DA`ۺ)pZ[hٜsRı}{'[Kvi|pxٗo:|ok_}_/\T#FS4FR<R{;o\19ylcd8H-,"RH ph{='V.>-[s2)%$A$(/1bjo߾/|Wl_믽q[wn^qQ\',YDER] (H@,hw7<_-T-6wiBR uvwjKSJ9B"%<~+)e Rw~ wtsu*~ֈ4'ӷ.,Vڵj)g$(#a(D2wq|At'hku@!Q!o+Hdʒ J@PDJ*6&p<dxo_՟ը{{[{i6M6l49D?w^sy::ȩj}34Z=ADd6ZgӠRGe79[kj6Mzq388Ms:SPoTZ٨=} "_)$6督gN_l޸_yv ";"$aA҂JXs jm0)U*Z/H ZȠQRPjawwm "c0 {LJ?YhtBHAlhҟhSJJ{\mlq)'t 3*&Py@ #+dTt@Dj:ܾ5*|% jk&n,S=cTHVN(L©_Sc'#ʧgllAT/!9(@YТ :R H#`LH~1m (Doj~~#|oQ-ZZəjX]@q:*,:Pi3 Gv-QŠ*B-y00ݥ"1̍ErҨ#`t4A{xOgpt23b7݃yQ$,ۯXYn_x7n:dK')[cեG7cgʍItfA\( fGtp0 9OHa+5#eZ++Izlk͍ DFg9oN:G6ګԃna3U㣝^0tm6 Jg6Fe:,ᠷwhZK8&UyrFa]*-WNdQD%ߋ8e`@'D' rQ9!2^&n`=ُ+A5T8+@63 *1>Eq<p}ş?ͧ^ͱ~sPm_{WvfR\FQ" R BYHe< (b! U  B2&8L6oܚwT{f%g6 kR^9Jw<kǧ12!"X@,&vDdRqsGӤACԠ=rƝ^gkmrPnU +i:NADl=(`2 4(fMiEH8olon~1-m_|Q9E/nFp)`grlgW^7Vʫ+A6u gDz.4d|鈀R\k@FG'n{pqTő!ֱ 0A$'&v]E@*nEQj 1+$&`! "J(Aơ+^q/D )TsuFo{Wc?õreټNHk0>dn)Q3yHt|K_:ڞ6Z+N%:aJxJ9*vۧf~g5ꋋiO~'w3SAW'ƲP+J'p%ѥrvrt[;FgIio'͕8޿{Tʷgvǽ3DбF& 0Ad+@DZyiѫ-WAEA?Q;y9I!I/}|5SA!#@DA jW+/f REʐXj XDJ1"I1*@DI`^Q+Va ,ND=i1J.__呇^# DŎ؟! |v@HyceH %(Ѥt+mH )mx4fýt0ӦUJV '9Y^˒^ZЀA ELaTH6t`ů~3_Wwl6ʥ80+&Q)=j $$.*NDHBр(hl 1jA̹`XOZn@9@!%B`xp}=s3g]HH F0`ڊҾ"|s٨1nm%fЊ+UC, k`N46CGT h`D *'Y:m v7#gg~yjbTi]'CbA&sD|AĖ (7.OF( k&Ѵj;~oڙ|,9c! ȿt̨8FsO?|+|3r=i5K8TT, !HQ:+a(NwsLCs, **PNn2fpQ bOI ( X I/R 8b:UD̦ӭ[`=~77֪Q=Aw 1Z yKJiArLI}`AM.Kww!Z\[Z |_yJQV^XY6: HlDD}NR۟kv=='֯\>GjGfaij020@ #/L[5˦ɡZlX8GqfE b{U H!1ȅn4a Jy^[hgBlE\"O[?/|/+wccԩgzjt$CAd<(P E|B` ӌ0gmyyȚS/PzA!], U-B!РJqru}Kf )TWrYG8mv 5b$"@lIbk]-kG7^x꫟G~??/luŨRRU$,b!-nUwu)()Y/BB@ƻ/vsP@ZB\삐 M -4xw?#YX\"Pt4u({_Ggre1>~>+gVÊ?eYP4KF-#8о6KbU Bs9$QQIZk.X5FٔaΒ28< Õ+:v9|Uk]ma)"tF$%Zwߺw$I'4fr ^ۍkwO?phY:<YڢKDF8gdTD 2ª%if: C>fT\$=/L\N,g:aF(v#(XtPP$-`E$^wlhpsX>ed2*Z+Wxxn;"k. ˭Zzjry|?OOkoA7fDF+3HA p =|09&MuFUƊ|&" ۄwC$Bwe1Ʊy<+;n7|7o=g;-L ɘS¸> j\[?ֳ0ˬ8ȕ$sZ+'OQ?H,c$!$C$<u^:$IX{Fnnf4j/' \NI\~gQHh867p7eʥ^+e̲IڌЯu|@@`+%<NI?fJjO.,&X40#AA*b6 + G^R*5p  hty?Xhw= 72Ւ }3o?\TIT ` L\PhuBE,रA8,]@8PKfg!Peca`$"̊Y!Zk {焍K'XGn>X+3;= l&,Ix$vj٢HmDdԟ7\.]JQ1+ը\QH@.H7ArB@!(rFmwBcaMQYY>ONJͲ3b-)"*FRC7x_G??/}P]Y[n-Tߐr9@!EDE.jrR SEȄs^C`/RlA ]nbBP87IGls4t̅Ԛi.ݠFAP)Pz@~*7*YX Y3iD,qXXÃlUaTA9(@ xg  #Z@PdErA%H XNMGToY+Ϗ`0}.O\=Z5+77t5̧'۟瞿zusY A"| 7SQLBZ", JWE# ! P$=C gN}3L+>ĢT0ϝU~9##d.i/#F Ҡ4Ϻ~!*WLa3 /FBh%18,<#ʒ b'D)aR`.xY9帼Զ:o$*('Ϥz 1G:P~&tsOCG>]jmyPDNo(օ1DD^58$pDRx8cq7O8_t $*ݲ[0첀)DUxAxΨ+zX =o?lK3O=Mim^$NΞ:y8%JSg=jX[aFN:Tъ@oKnٮ4D`5[7_y\[l#T'p6C ŠA,M37\9M\m.6[ P˜OD,"(@*3f@ۜVXKs\4NR\Ӏ0 X CP,fFO*`!Fj'  8ja-ɧ=hrtu[IS,J~"h*)#• Ruv~Q+)S7/S;)E8n6^dts;;_ko/ۛn+ vܫz䉴Gz5Yh@t?vZG>qHJ(,@Gz|_}@ g8Puʠ(h*!*qi*l'Z URQ\sg[^sݏp}i?y-KIw]|rToEiNVTB`{EI}V9ONj8hm¸6?)3]Gl&E<ZsYl;v2x}pzb F즣'G<ɃݫpZ `Lxz\쟼EϣZf A $.У7hԭwH@uNTЖ9 -!mw4 X$q~Rr@ ST`#? ;S8E>;9Bz'Iv퟽ϯOP!pw`2i4F^\R`/ P0ƅ3f2 TJkb%%ArNA Wˢ,HT ?A*&imGֿ-T˼ UF *;) :(d-  tUX tQ 6& 8!q @%?¢l)dݮ5lxrst75]׻wn== mQUˢȭZ:~?J[NOY^obPe::*;Qty`2Y]qL)N dQQj?sQ|0<ڛL9Fl>,^\{QhʄDI~xxr;7E1Ga:K+)ݴԼD%i=a`z<G$, 0)reQmd,rTA&P@qB(AΆ頶zRVc;~ӵwgV.>{s-,FۏܽsAF-1J"JH}DHlEdwS'D@dF\-EU}:V`YOaB}"ZaIAcV*"spHl>[󼜞c#U(`)ɨԡTՊP(%dLX) Vr7κ0( :a(Ƞ%Hi%WQR"vΞqDޓ7~WW^yR Di3J hμ( s^^ENZ:dNjrwoů}nߟjV٪I ;𜩔^DjZv.XTFI Ձ%Vrq_ T1 X?݁(1?Ȩ+VfjCprOa?"H-$޽Nj_8•l?ꫛbt<|zOs2LB!jhr/q uN p6:ceϊRdRLJo3zx#/U]Nc6qq2kV]Jb1 n"/ Ԓ_KN>>y2=)0 ^}_ka$O'Rn;qk$ؘ͊yAΎN 0Ln1OGȳ^'J[.Q AD4%E\Y(pXY-_X]ƛk?΃ҘޅK_sNܿzlVO$ j.BPp}aUB3bYPp>H^`+%A%3kg!"_ N!1i~W?vW' Z  Ty pD* ucB@f|tpz?OGbv:fd2 `6E)0. P X:z  -A-'Vgi׸8ھwwŽG%ۍ4EQUb u(( TqQGȊ+!sx6σ @pd$JW=C2DgU+: x;Kzz3W^$@u(&Ѵp~?W1aQ "%) u+I}ڏջ(Ã=ɔ"4a-禣s'%`!DZF;"hjzj0 9.mg_|ntڍ[8'b8'GaZ̖z+OG_Om^lgS(IBZ0(ynZNAֈD|6?-&Ւb "":F%F2(vVitއ^zzڽc+/,~o|tw`8L(IL LJсLF=^+, *yNuJTI'jjRD^XI-#PjBkMEUkC8|//|aNv ~ D'js6rANhB) TMj qӧq`B'QFI cѢ5ӜM$ *Y(ʧworGvu4)jZM<^Y*,P-Zp >M>"pBsFz즵fG7~Ϳo|;{Jĭ~{jQ@WSUa%" TU\fJ\Z~vv%$TCʢP.PQ^`T0><@_<ُ(P:Rh(ʕ5G@$Iέv:|SFixh6[Q&[ R[:62[|zzZ# 8's-QF8}0fd{'i4kXMDuq+by}?+Qd$N"T@_uN(gd`U ?*Xe s!Ys++ x@E9"x=B5V`wOr/M- SBA,j ,R"P`1ZF+v46:KKRl,dqjQLFyi%*AC{ǣD4&ԚtB.X_|Y'b2ĮeeDY9lHfD gĉS05ZN3'P̎{~oߺzpZoitSEd.B S݀"ygg]ŠhU*dd3*W+[&VSW .;o OL/zֳDU=?^* 2>jh}N Edߺq?ɬO f!AG%r7Lh8۟rQTbxwzrYs7=<<ًb4* n\NĵJ͇cW Y8 Nʨl `I1]XK&g׎brܕBUN5|Qn|i~7qaE&b$pq>̧v+i)EV&N K h|r_y}B4TӢIXo F1Q%FlGOooNեz+ dxGs[qգ(a_5 ]^ $/ZD̦ 9ReI"V=Q*SwER_u+V"/ zg^X-0(:/yJwf"J,zQ'KF ihC8+E"0("0HLLW<';-s. z]@_|&hU&&r12aY]ЁuKedZe9qV e@S@b/f`EuH'ajQ~?h4VWN#2'snccfp||훷?xCZa`}fOJJ8 B"1$+*tP"CAT<CB *(J "BdT=(Ek5WWC2o{Io1YwtvR5,D@J tյGUqrz8=>(iȡ85oK"7-h52iuYK+agm2xOOіb.`(Dq4ked1EQ,UHZnzs{hRլlrtɓ[}Y%5&2LDd RU]S%QsD^ (J>G(~_aMEOHU^G@Z3a/"IENDB`fritzing-app-0.9.3b/resources/images/remove_prop_enter.png000066400000000000000000000002311272363601300240150ustar00rootroot00000000000000PNG  IHDR [APLTE9&9&9& tRNS`A3SbKGDhQ)IDATc`@B @JYH1)+DTzv5IENDB`fritzing-app-0.9.3b/resources/images/remove_prop_leave.png000066400000000000000000000002501272363601300237750ustar00rootroot00000000000000PNG  IHDR |lbKGD̿aIDATӝ 0 x4 XI FAW/u~ÐM^/3T)pp4.h1ԏjs?\q<܃J%=}KIENDB`fritzing-app-0.9.3b/resources/images/schematic_grid_tile.png000066400000000000000000000003161272363601300242510ustar00rootroot00000000000000PNG  IHDR22;TmbKGD̿ pHYs  rIDATH퓱 _XZT;nd:C$R*$Gu$IvK Y?bgVtJ ۼY?|kBc^|?(ݿH1ABIENDB`fritzing-app-0.9.3b/resources/images/schematic_icon.png000066400000000000000000000016201272363601300232360ustar00rootroot00000000000000PNG  IHDR226@:bKGD̿ pHYs  4IDATXýKHTQߝ뤖dYdXԦeQ"h"iF"$hZIDdEE(HR!e=-4ޙ{Ͻ3ubνkζM Ҽb ; Ku@޶P6u2* p{1=KE&+w?5._KEV P,ԩǘ&Rn*n5 ,ńC ('jN2pԵ#*C qVj@xC 肔;(;ot htrOw9X$sRÑ9C@~ jCp"i4jBљ昗#kD9m,ihCW' B)Hǥ+/sZ5! xFvi]07TM~ϩdp`msy+9RX5(SĂ:l%COPr@fbcSE_BlL=[g|V+d{ Vyb|ƥ88%8Ů|)rcq c O7$Ҋ7e{͊^J ]:Rj@i#| Cn: '<}!:TXÆza,# DfXKuĿb/ KV m>0߰j`xM@yp[P|XN]VSJ |tI:5Kµ]]\I71d5'HHGJ)lA4phǵTIEvvuYMҁylC>0(*:4EQeK_DXw4Ir/C=PG\{.A,4/*'B]qxBZ5NǡШk}ϮPe׼ĺ-&h3".*"O#q#-!~#0 *,1!./ 8IibKGD ٥dIDATxyc- IH ۑ8Mݞmnv$v}|P 7 N_007eÐt4Qh;ΛKKbۢ0D,3qGMGLg>>eD3؈@1I̝4D#q5d*Dk:deS8spS Uٝڴ6A!SMߓ)i=XORUGӫ6^jzxdp>`WR)"bFud-:OhCTK1Ӣ&?^g\/ B.Ӌ脙ECL?h7f\KIb^=Za'*_<Ә954031iYtͫӘӸsZC0t䘩Ły28+ O~2 SxÛxÓ81r+%vM1T 6.Z 1xm9!B7f0Q?2fhHEN{CǺVbtIХXMxf,KqcɘN878( J:r]Y @R%1 lDHЬ,8c&5 {yJjthL ]kQ.-s2B~(U9'%i8zNOM0i}?agzҵ,}7) LƘ)썱XrX1E1ۡ8zl.#!6`V1*bKl# CWIcLSXfLqa`v1%1ۡ[Ɣac;E)%L,]2fRJfFaLmBv[# Cb1aoCFNaLH(֍.Ѻ%obM(J>qtaRw$N6 ([&Jþh T6v"1;Z.=c6RTY?%L~r{eo4nL+K1 Kc/V61-ooO UFF\A=-쎕aKq3Ƨ%e/NsAǦ0$HJ2(hwƨvm!vla東O/%Qn(G_*f"7S7iqanh532a$K#*a̦rI7W53 mLZBϒ'7F'{<}8GɿkYr*$uZsO"gZ!MV/|aOOW˜_z_>MbQnsJ/ZC`v9&U,y2/жe ژ7#j#5WNHxȘilڛUKcJ>XaΕK*kQژ_2\3zgoinC 1M%l~3Fl \*ǐ^[* Ӑ1fHhwY]%;I)#G2:I@]z16E@y C!?4Q5|l_2F806cͽVJRX~VO!MvM8Kch"2FT$JfO/ycl-cUAo>7if[ADRc*io׌ 7iy[yGkcDr2 Z .DЩ3  %h1vݡp05~M@s%h1a oqBZQd8TCi:h"1@uY?ŧYe_o+!~ΉwuL* P捡#-ta ycã`TOycFe ô 1Wk_ ?DEbHyKU چMƽ"/^{r65&d>Ptc.3tdX:٘WQ}* fVz1wietqUCw10%tn`L!f̋mX=1`L(ޅR6}L&jI=0]_ wo7'4cIn7a+[Ƽx`lǼIiKJ_:isn`LsA36 6Tc.yN3g `LsC+c%3L&L6Ė-l 70{%cNat1o2?l fc ;0soă1ƴj. CӦҹyc=j҂S`v`v`v`P7lA)Kӷ Jc0c0׿ߛePn-R`v`v?uߦoc0c0c0c0c0c0c0cf NcHIENDB`fritzing-app-0.9.3b/resources/images/splash/fab_logo_bar.png000066400000000000000000000065611272363601300241650ustar00rootroot00000000000000PNG  IHDR1CZĤ%PLTE2"eY뙑?0XKurg范ϚypǵnbaըwvxM@K[Pwmk꼦b/ ߋĿm>0V bMiTG5N;렅~f9()/!MDB o#6/-~bKGDޕz YIDATx휇vܺ'!*;$YDֽכKeP ܦ?Dž? fER7G/S_򪽴*N+1*N+1*N+1*N+1*N+1*N+1JI0NN}/U !wRja?981 4 s%N"#$ﺰd%<+r1[5$uMHib͌9peubP݂x7n3A)gzIE py"ߦl31d JJg_(ĉ6\DK Smh#z914"!2>.Hb^%}1_w!2VnF41ٳRr%F>xqb PUf,"uy<轉U?b D%=,19\G e )%PMB+0JVr -:d *Nvxg% ˅ 5MQcDZmʫZoAr mIe¯ؓ{5~&/Kz)b DqxbdPmP]#sf#&e0H" F41qNjK5YF[Y,X=1auQلOm %Ql8VΠrEƈcÄE֚6='e-DgccxW L!|b`Jw^1a`}OԜGڿ~8ݳ!'# ۛ14&u72(M .xP8d.$@؈GL@GI.O/Wn݈y#>7Q(,2x(o%#;l$LLoĴc\EEቱ,44BL1谉}c=ݏ'{<{('B|۰P:-((5 _)63GƐ/1~ΫoHLc<ܢ9ڗ#Fl!1#ļG7|$1 1Q=h:י} hJgOIz䬬PL]N3qNބĴ(p\+ ,J|bhFZrJ?l7`1T{s8F$j_.tȺ "JJDqPR&pa8Aqy!{!Eea};ML_#= 7nZ Uxe|L,1`6Trhr$Ey~j$D[L8/b/ƉyJ}˵fb>5JqL#}Oؔr3ۡ%F[(VwKUqTF"$`}3v 3+cĸ9iƷ6xV9 ^6(?W#&!&QG8Ш2(%9nzUd]iGbސ5rd\`JG yDB~@b'bLAno߃v'ٯ%*1WlYĄk';nE׮w#F5U,lb,=dz7bg>1HQ/ُΗ"&W^\o*Lg*cS3gF(~[Qq*q \ʊW!^:;G7SMye@Ε;&<'193SG%x+×w̟dUTd1&'c +sBQ}u'tufZbPc$̖7y:x+FcUmx\m޾SY;63ߢV.ڝk}B{#gJ}JvFUlcJG#&5};sׯF%sØ4h)%q9ߣ!ٛ7" '˗h7rj”xի],p"F胊|3U5_VbVi%fUVbVi%fUVbDGӿVb6ߗs4w%fN J̥ \N J̅oJ%IENDB`fritzing-app-0.9.3b/resources/images/splash/fab_slice1.png000066400000000000000000005727201272363601300235660ustar00rootroot00000000000000PNG  IHDR1LbKGDIDATxEP1( *"J l  qKd(@F0"_r@TAeB RD<#szdƠwE B`C@QEʲ0d4bE!pLR1 =B@@U1Z" D * 282|~{}uf~niv%QMWm~?Oz?C^8}$I_o-տ?{o}/?x$/ďgaieh< /<姟}ajiox|;իTtqq~zvQ E0xb D(AkbK(F'OEI땥j >o"uyZ|곡]i̱e6X3W\b}f`ܹ by>L!#"DŽ Pl6٨(DT-1xffWlWQP!(h3HP1@D4($X6̪'{<k@HF9XPD@U"T !;We^ru1{9tUko-udʁ. ^kM.kN~䥓\^3n2fӧ~bh.N*!kd0 ""Wee)A0FF@@€Z-2<~coD6ѱFVTSK]cݻ{Ϛu㽵j>/MN.wd (H`((K{g{^:urV^{??~rbb /^~G<}n`Pu-?ؕd~zZ0syfۮ^_=ʫ۶nr%EROO ,u^Y4GQ=XC'@Ɛ+2DlQ!6D PGAP 6o۶'h@F@ZCS‹cJ_ rPjH0̆yE&Pꝺ"S1.8 !NX QO/b\=PdHH*@,F2+}rbҼ!ą`ӀbtR#aF h6'uAA,*5,|K3^;.N\!J=Mb-LOݸm?_ 6fm䂏ɡU~M6ŧ/=o >IlGg(;P4zyqTm\wo_Wڂ Ȇ9(5$"hX1(y|Ť*!+dC& ( (((RE$51*ClQT YkXTTbPQ$$B" RFV/^wNQ"E^J2\%0Tj [3r|3inز;v.3i:{kΟ>sヽv гje&{|n AQ&J=, @4(Ԋ \[iq ML]]V;;w칓/9WlꞁH@__{e~d|hώӗV1 Ia) (AC %"RԠTC@H  2"UUTDO*J@L(P J*RhĬI|i٨G@"G`/D8%E S$ DUB`82"B `5DT6BD#1!|YvڭxϽp/}^=aѠ s믽O>e2z25=>6[kn_=ƻ7GLk֮5q߷MgN}~䉗^7z7}ۯk'1\Yo6WV8"5|)&iz{{ٳ<v^=؞[ذq/#?c6PK `Cc̛o껶{㕿t?޾f K+' :3`}6lt5Mڋ/mڱ}eeR]ZZTOC` " D + (U DY2s0RWqٱ^47aȞԹj'}0dB [^D5x p",s!hғvH9"$ո; J@B9Pkdy; Z-KJEJT4Lf"R\2E[VM5"q*Qdr#RcS/Ħz䅣'tUkaDD#R#itry޶eX2o|淼#vmLǦ[w;3+3bϪZ$iw\x}wg+ʻھoxpڨVLZt+q]@A1֨bD01(@QH ɪK`"4LTPE6$"{ DTBPU0yemllPؠadAE]ҕNZ4T8NU5 ֽ\s ۯ3<4?$wSN?r닗/X FVU@"(*,@Wx"h K6lT THH5Dlc x; ʆ#cTPʘ"B$`RdUYQd |+"Zk˳ |!wU#߿?(2(Ν;$/|#|=Fب\>s7sO_DlٵG?[o}Ӿ}|^2J[APV+iY8- ]P༯#C'_yd "nV uU=G^x믆7{b|>C?=} qrnamyן7~@_ bPpe?x_}ZeRIN+[ JA4\^fW gC#,$Ha $bjTC BdA"lE=$%炢&D 4) ($TPȐ9P7):!LA2[@ 'B@ HCX#kjHH,2O5HiP"ST @h ^gG}_W1ٵ-O,3"sν;yG;~ 7Hp)+r9kmߵfK=k/x̙Nf~_30 073b&D `;iT#HXj lQ_疗{?(Ν</l_gqف6??y'>0ןZ,NMg{ֱ-nvߍKsG<;i}ʋ/w݅s*AcDK8{ڭ~tho%]K*r%Q\@@k^,5\   W0H, !QﰄZ;1k3δ['EYV+I88i ҸbL+5sQB^D "`qE $"&ľ̠ haV) gR[DeRKQzW80U$մ EP_M(N#c0 . 5e"䡤m̈́gND gDƗDQ xEYWgg<{#2dWo_+6߰q<ԓ|(M֊ 8Ȕe)3W佽}֭SNxG|:z,"voq]V+!#˭ŕ(1JĆyO ""zPAU,#0Pl [D"@FP!3DʠdD"RJ`c@|PZDc '@p0 J)$ʼhuȑ ((F`0u J e/sYrO:tqDYo}[Fށxj.Z+KUFqƁU}C#B)CJHTPP$f<(bDD@D$%D$dgO|G_hB 7l[~pM2z gt.]e=Tz_^}ޡTIn:~KQ_xbt^hwd TQF("EtUPq@,ِdD"2T(`Q@TQ(@PQL !˃!W@Q Pij() J @B h5eQ0QR5I^%PBP̫kLVEQ<z'@HU˅ףN|hJ.:7n[=>5]`qn;|[oynz7xsoi@vV[!M"/\l6;mkrĵD3GFСSgy7vGw+{_` 2;')(  |xO?> :a˚NWo}?xW_[DYL":!~_t6 \T[ˋ|鋏=܆UG o(D @I%( ڽN!* A< aRyQ˻v~g.nڲ! A yG,/&fE ^0& h酂hr8Iؠ>g(6LCFr8 BH0 .xCdLp@^YY8۶exVG9g+2W ثޫv6*e)899}a_~uz֟Yh|LdBaEL9C4" D HPddQ&B";8ab;!P5f1"Bp@UàHeƣ>+Q D92 "ؠ  I#y DF hJ {FA8%HkYI+X.@1^ U ^I"CPvzdժ_x7nCgFg#W"}׍?]i|;}<Uo}]o}[ADTEn睕v TƂs'\{ƶ\nw++E&:jڃÃ׬?uddzrZ[e;F-?GZ__^sժէCBfJne~nw7E%mU i/l}ŗyg|ŏԏF/IpHBǪ""ăHGʈ. tf"@(6 TW:o]ukպQbsxile+1Q'dVvP#_J EʎZ+ #]mhD[A}Ҩ 8+d5JTFIG3X1+*U3TX;.(X5@hRS++mS=Ϝ"՚FZ +,\K/GXF1m[w2wQxզ ɩ}[Y=0nӖѵiO.fg._|'/'+V'W/B@ 2, $䕈'I%.J($v՞V"p!],N64zK4[6=v3Ǟ8tǛ4@Ӊ PU2JhHDN]n;bP Hwl* () }%S@DU`$tIE`=; DG*1; H>@Ĩ^,"#D:',JWx-]0TkV*jZf947y%ow2WZ^ "^,kPabVD/(5S?ѷ~p铇㳟?m{׮dnna;}rC߻믿1aM?wݷuHJޕy';\(1d|wv''6?q|6}ׇ~VMO]qEK3믞[vWo_~j徵n[na7K[i3֮X\8ibd`b"Mkxوw"c={o뮿淯d+|j#T!ŕ?/X+7v~Ξ7-;v=vJ71"lDيO+AWX d,i {L#0vkتm[wqœ˲6/&#iqA96&B@5I|˗$S#eXED p[ LZ;]ܲW"l&I%A1  Ay+ H\O,1VŗE&>PKX(=& feheErd">1$8%pq5>r[v𖛾~ᣛ\njC;ٜoMMۼjmo~ş=CS׬];ELN?|{\<56>ޏ|}{{Tj/5Wlph"#d))!3> "DQA H P e2̹IoړVfJ9BBd@٫KEb(^\)!A$M hVdSxWzq^X E% fdtC wE ! "J.WfV^Kj5zm%=5+$ԛ$WoٺyF/8V22?;.L]\8y|ytDi-*(vŁ*]?*E B 7o{C*i:vXR^S^:q/UB$! eP.`LD@U8{'yxvi>peQWlZADQge"[|mɳ$DeY hV &ard@01}|lh412΃;YRZ`@V21SFQR`k-e {@PQDFf IEG 2V{6m\_~1 5*>OMM//Uڴq^Xi-7Kˋ;wvzʤEG[׎O /V֖ vgS6FK0PO ]=zXhesitȪj Wd|avryB&/]:uҕsA6~H64;%&b;IUf^5  @BԃQ vF2|P 1% +J\nQ1@!A*8%0#tUm`ETBd(@@GrC" #h5}WbzF+>ȽB9+SWWG޹w SKKyEA PUkxĉ !CiYD۾'|ǥo`|݆f4%𦷿}֗uÞ=O}{iV:vSfsKo{{~?s0w~=#o?5ck//Fi20u-[?>q񵑱_z kYމuozs+O;5n_Lmz˻O;hύ++J"4reqʕoe=HI sA9bGFfmZ \Xx(B!DDPЅ!$*0؁F JĆT ȉf֖M7硙UgD\eHTԲ ⼠Hr+jY*:rXC ,01F@p^/|iyQ88iY6NS5WZr&2jPp8[KɂJnY)Yj>x&:P Lt>WU2*&qQYz{"}̬c6Jj5׬"uk/^ (* Z.HQf^J8 RSRݽsNx([v]01Aae)_)[ sNr+=)7zo\=8z5f'-GUFTCT J`bD h,j3 QZyܠۯJR2ϖ.^x쩳gިd8_\8s5GAƴoxr.+gmj8{zpSԪ(FT;j?O( @UfD@q*K d4wLlRuHV4x&AqcT !AP0(ӎ'=WH֬:59usϿJZiܵCO+5/#@>`*|+=}޵_.ҕ+k˵>ÑsEY֬زS_߼"@/]9|~o~U-:ګFWfg>O6//|oWvم"xHl,}@ED;"*G\Dtug2se.+HQJW&/`/6Jw칾hbZX=ڕ\I(HP4ZJl&ʼ(J_JWLaDVy{yn>kwY)Jf4NJ(kI@|JW h+(YE%&QI&h#;7<;;m";n۶/+SS{Of zqĮoٰa5#cށ\5#OԳ '(^P| BVQ1B`A%/3zN;zk/>ԁ_:;X4Ɔzzw씹2>q҅sV|[eMEZYEQ 6b"]z?O""wOf@BKPW80-A+@@TT`%dQ`TCJ\ ] 3 *`T%@4 {HxjV^8Վ6酩zz՚7/>tpxd]Yt("EUԐA Ƞrscbcѳ_Ο?;8gǞrqɁpd+#ccc/5Ptsz'^}.Ԫ;n{ 3eصcǫGW}__9=gj+O]^zw Z)\*Zρ_z? S?w{'֮7,>xm;}}7օ;nbxO^zg:{W?`-CFQw R׬xKW>S;gl"$0\Eu{].J奅~硝jb !bDuncÆ )j@4/^ 31yF,ÑW.,5˲IRMLŒ A@lD3ILDMLH6́0Ѕ1LN ^շVBf"Ʊ(WF8Qڥd./:IA4JDu6QJ\k+g'/4AˢDƸ \׋gyV "PD8PPsABYl@2:~L/ɓ~W>w[sgNT]vӟg?voȳfRo1;Q*vݍHJE!|D G&,1ET IA`O>v^ˣ-#tTʥNsni͚ko׭PKS3_?rKg\p.GKP xŮpm@"@2DyBT$QU-ĈF0ɲ J(XAXDd4V<#FhՋzUA_%2 $k@(}T5I+տzՋ>7dz\ok>sM^_/[mT d&*NQD+ 8ĜO/ojĺřjo_\έuVZIBVc$$$D<iSGs>t0pa9/^{#3gNع_'( 28IJ~wr-7܉֗Ygai7)`"! >LNϬ_s>}DEOLlܸko\Wџ<7N^Z\.o}qυs*Gq3[ BdӢXg5XZZ{V% U H4=-;Gt箽߁]71Zd !?TAveF x,QISdT0(XV|Ֆ۶lxVTRe+@̈́ `F]+ UB&wt:e%MdJ ^I+9)i$o"X1feDR1ٕ'sOV]_g %>f>}ʖ׌y'g<&Zc k^$lQ! ^aKc UD\DU$D $R&K[_:>}{nF9coͿ9[>ޞY;nv3V:#IsE0613:EN8J1G@!Gi"jȪ9DIdRWeYH J#Y'& u8U ˲"sQ_Vٹz'bwtt&F#qpyZNMczWW̖NhՃ(a躷(@B((@LR/^A= ,/.,y`;Ĉjkf{`1x@ }xDD!& c2)rLl4|k͞{I?Η !G #CZ03#32 zʢnId(1{.]~=]sp0ݺ{5c}= ,]Z8ڱ@cĦ@Q{8wC YNA UA!2IDt՛7\5k'GM;vpd1/vmߺ}]j\Mzbu++'O;wHr%Jѱ=^}G_}7}ҵ aAUAZ@Hu!Y`WPBF{1x@"!j̙!("0"@PH?|D#!t.!D8E*Ӡ>t,QVj4}δ_ٟ8sīi2<8<0?K*͕3C}{6c&A kPP b 40#,,~oc|c7E$P !hC_5M?g<{3?3g+ 0DQk\Y>YYv5N;YVrÍ5Xw'#Ʋ%$͛֯}k5aannfvR݉GwnEօRAT5m4&Libx@cZr<}o5볎[l.FqXYZl~?Ujrlb4DDA!4z3uk ۶ZWmx]oG`PDU)#`\`Bd@  d@t+wͽ=I)2 5J Z( Cd"SPˌ֢N`B=I\1ZvAAWe3W=^,/&}uT!T(i7[62i5)VZY\ZrVv6V)L_WP)T-jveUSRvE L@ rPhTK/OkGFWyn뎩F^T\4qZ)JxJ;)*fw(0 faKW1A!($YQeEcd~ysشWse{۰yG{v^IC1]R`_23{ J@lwfl(psG77[ Oo^~~ǖB i5k!Z|k_w:>8c# BEs3[6ng?Oz$R_Vɞ];;8+猱 QEk-WK.9p`~Lkaˋ n LHQv H"clL665D*| j5h ղ*[mEkLcú_ VZŅŅYfRil>0<Vٹ|G>ɖȔ,aֳ8ywعUU $"B7F h`Kf8X'v`fozaZ7]PSsC-]<}N^8[ ޴a5++(:>}ͮb9j/M7U` ⅐t<=V ޸{~UnfF_"NJ-z8& Bj,u]&I 5qJI-&&WiiqRV‹lmD$)w5Ė!TK&/;iZ y;JRd$V 'Q\Y[EcQ ^B(|x~Z3}{(V*fE(2Y7L C5!z`us5"ZܲE)'"b&.GyGA !([@{~MՇoX7a-'fPѨ4"(Wzz'F'pt\kqiu:0(5 zdMj8NeQ;%PQlӸ,Ngk$ rlccҴRqL}(; hDĩ+he'+W2ɋ8(M8 ׫ge9YY9sq6`#ca]֬GN^xıק'O[ۼk͛7wt{anպХ!D ("v @I""[ژ6jQRm.,,NM͜}W0yOӤ3s߳m=}/>W^YY[oZQ5JR_Jl-AE@XZ` lĹ u5Q(H h T$jx $ ! kM5xa U ;X2 D eY%w_Mm_ë*-/w_!>SNȖ-7ucY(yck=/_L*BN۶d򑏼)J^Ya|iSv:5g?{WvŁN*;[o |ݛ׎T~vU6}cuwZ3dFj wz?8ۨ5s WmzO=oqZuY"  ҴF~_ԕfyѻ _yЕt7]gUϝ8wUk\IWoj7uϧ>z6<01H+FF^Twƿ>V;qڤ x"(U,1R12Eld! PU0 :IlٱSypF@e"j8;cälڄy3 m4} `zܘ(XT" B`B c/-!YXRbR*RIRM;͖!XĮI5BVxRǦ9bF\KQ-[mh!(DZ^#CVRJ  LZ1Q7( 2wQb Ɗ (t;A1 .0u lPnb1@QdQ52#/ <121;[MW(lQVIir殛g ehlUTNON6IV̕'O\g8rۏ~3]}긥e`T1+֌A "ށ`7HM] hbse11qıIv{ |p/U1$ yVeCJg81'Tm!#LꕅŅ|׮3@,W/^8}܅,A/7ݸ}ͺU㨕}}}՗ٙ{v4;!N5 #AHȆTnH PJb# iu:~50PW{RV:ٺr]>/\vCukW,,s Μ28~5kVOSI-%ED /rW?O tDdT* 0x/*X."@2$*F4(h(V@XQEeH Еz#3fT D6 1h z]v|8O?܉+VOOZj>sϏm}ڹ(}snn3"Ȍ2"V:iĐՁ}W\e-+޽GFL=T]V+_yKN@ELK;o[7n8QWRQD_ wHCDBDQaV( ' hYYe H Mc@ jɡXkQD+nA YK6)& "VVk6y㍳qw>b>S/~o?#VtT?LwG(&!%7zZ)Nek&&fyi8rla533_iiAnڳiarS-Q8gggjĺum\wmw<~̯oh…鞴^ 5y9?P?>DSsp@?xdĚ Wm^ZZS-˿xŗ,%|ךKWi٥o=3Nn.;qyVV>XDo-EM :+}W VsϿ]{ɚ-p *j D@*"LL$+k _*ŀ ͬ49.}m`8"(l%t H"XʀUju,FLJƃA,,JT䄔PFAxUaNJsy9!EK}( 8iņ2N^qŕĉ;9R QjEXHbS 5&e uJh?$Bdu5g*1 uMh# WdU`Pe⠡,]v E"B֠**R|\KkIERGWV{E1J҅…#qFzѱ$MoWh AD\fDؤURApKO D!1Y6WYEQ\ڴRV  y)X[0YAh6+KRI+J\Q'6d h}K#}IZ:uxWy# 1n^zۖ덯_ M6W׭^6(ɲ\\Hȩ("13Yb%` !ϡ"Uat{9Docۆ7l]~b|}V魭޾7pӭl-4yC?oO78͖ĦZRqav^ÿiĦ ^ TX0#2kHLĢK`@EnuS@D"DF Lݲ)jHDP"TAEUIEd %{ FED HHD!ti߾q#kng{jG>]۷^rX\󼧧 X$ * Ҁ j77_m֝qP^tٗ챗<2$Z+;N2Td9 !BH:1,N "\ (2( qCրEH H 4⃠ф@а()2(v^2"Ơؐqxba6h-lذnenknmt{x`l,6qZ.%%*@Nv!e6 (! ԛG7?yͷ5W;m5wj駟}W_ ϼpОBY[<%7l۹c;yn$2#jܭV$?}%N_S_Z]4vۦroOHX7nټz٪MN\X7 442_Vi oV{MsΛ\H.-_wk/(2>/@e|jᣏ=۴aS(ڙ5FIE>#?|W^qݍ7VLZٸu(Yʤ2C%P@DTAHT !2Ej@FE&F^@;{IbB]!BVh(&O GQ` 4F[ h2E1u)*!XDDR 1G%%&͢e",eERyB[hURhfAB,/UZx_&.S16""RΛyċ+,爍L++Vo89;( @F"J+Kl*HlMd)vrA;KQ Qc-#H' $E qqqR#f{Cx/ev4`3G5C絕%5Qoh YY(FDLnfh q2*ڕŒU4IJEy"O+rO¹ڋO>s=#3G.-rjQT)GqljIbP 'AQdSƔJ=c?kG6l^mˎMQezȥs+Km?qtxww%J*T5q|GO+|ԺމM HDJ !"{FF#E I Ԥ=ۋ0!Z(w|5L_ѢY3MS6/>ܓ{'&(nRR2G*j jW)I}Wئc6^Y^ePeZ 8Z7 DNՉԁaԄάHQ P@ARc8(*%dQ< PJ{XTWU%QEe@ "%(˄BT@ C'}dY3;UbK$n󢵺yk}ɟs"iwegh41B) `(ꑀQ  nnb놭[wne vuON~xǖRW>NK5kZ+MK$t/(r!4O}w{CݲܱGګy=I^I1ܥݿp=Yy7޾ v}w}O:?|qs]#}^~oy^\;չ zM[чYY]5fFA In7CuuUW.-Bfɷjך !21)"* bX} ANQjg*$"#df^6m@Ji$IZN%c "$ 6G 1qbcˡE$ G1\@JV 1D5 V)NQܬ5meY-ղWДʭUi啾ZV}1Pvv\>xQS1fY{؅Ŧ[]n;Z\sxe`(Era4gk|Ws}]~h[{LJl\_PCT;>U Pඎ;1ĎW `\BbJ]vW /s+K˗fffe-vmq׏ J-?Qτ"[enn%2L*o jt )(v;AB!SU *<^@D7}ad$)Y>J/ױ{'بW%A)ΩPb ι6MxZ3~˺7]uϞ|}m&᥹972ۙΪ: A.Hf{DDe`d˳kn&u%_JɱX qy%^YYٹs+O覛oyǭN }.h_<7I<,_ivd8&6xg&G$jm41>g~O=vY#?喻Ο?샊 k!x=}+*᪫z៞>q|W\4=\vn'N]xz}վ;/z鱙8^f:"\\Z:|?}|rٵF6 u&t"͸vZf D?ZG^x v!Bh@œ1`kx;4b5$Q4[9lMQH y舢-hP 璄PĒZ%%*-qђ0QbF# "SERH(+ˡYJ82VؐjtwQF'|574zs?~94㓱A`{xuk~ee85l4sEDbc*e!$#AJQظ `^^ >PZп\l6٣w߼]KO=/ʿzC6d""EpއPL@T!nc_^to^SUzlDL\+ W3}F@2vblݺPﰬgW:2ʁ겂RLlH`T3 -P#Bb!`v Y1}dp`pyV,-,]pc.,¥uk&=M[?1]) 92wԡTɮɍ7J▓Bn1kZm18) /VcNUAG" ^  $,;w,3 )i؀ e$ YU:„60 UUzV$ D(2 ZJ^rܩӇ֍G>U8=?;k/ßxoܢsc#@l6V  a@ h'cnG)u{RQ&>_{mY3QJRBx{㳯njFb|V^(Q *3g<<#<?|7xKZNKg[́sE6v5ַ KK޽C'|QldE\NL;^8g~ӯxQ8j=w=k ӧNf}7 ozG߿߻ԙӥnSX5={AXV!@zIld#Zw~gz˖` f3L*H .A-bp:-!R\I,Q^Faʘ(8v3"=E+/mi85\`u5osŎuc1T(2(N CT*I$RhMEZ÷ rA0xc)au57R]_ƒ*(:1-󭼣+Y,3pns+V}$)(dyZ B#E-T H'~݉A$^jzZy\wpidl3|㳳K= o]8ǚlg8NbP8XyY.h~a)gIR,42@LV&u 5XTd%2VEx RtvTQAPV. H2P'22QP@@j-t,  bbP "@URlĊ++Tb?0˷_8O_<qy?זyh0F6ϝ4{h)(!AJ_ zC_wӟ\wKR6ffIf/,/}Sgw7X0G<[710gmiM;G ؽkSO=uٱё3N$QM{_xWKq::>9>9qًCs,ƮpA]~wY[>񓯼Y޶o^7TLIDAT~ozhÖ-O޹klߴ]k[>Q<2^UEAzX WVȘR΢TF 1` VJ@H@%tD2:EHBAbZE2qj.͖%$F!1&X2Շ7Q#XS)](%ƲqQbB;k5] 0s4 T9bf4&8MVWc&f#EM'&Q[4V֢}< 5VbPt#i |ǏzC%"8x DW/ Os08c1FѫN ܃CZf HZ\YچJVqy^86l|'^~o~M3g/:]oEq7nX;;paf6RRReYr QlxL ,8@ XJy3x >R=74:5ő)ݹ^z虛xdmV5lDqZƘ@`uݰT*_XX?}nO=w,k-U{ҳvkkW"Y3ą6oNW{z)\)X!DOLdC$x/Ā6^h-A=#E (@@NPԂZc A %&`*/hH/I A"0l)RÄ( BClzd+|Ts~7hذcL{w]ƚ"x"R-G*+8ͳLIP̉Bdv\⎛~JgϞZ0gV̩J$f7v35?z{z.]\4dDIHճh.ʪ##/qtl72fz5W^|Z}r _~睄=W~3֮_O|Wܹc-ݕb注*N>٠: rw_\_~wy}sEpE"` %DQvGAV *JDɘݜ[X^nm~`qqQAcJbk!i)U| :_a&v DJHBD@b l[-lw.RW@P-FQj|AjIj[bhWו5^܅(Fc\jTS֩N@V) )hD2W' g|e˪`RAa no ;sy;<:s3uM+ k1[ _Ĥq C{KFvp )f3ˮhӊVJc=Wn|򹧟ynC >h䦫۰nRKؘ1-&$J:4*gX& ѐ#!!C*1e*Es;xhq~ejxTMVK\X:|؝wogϞ9nql}-z+p5E4A\pT 1dzlNu왳ϟZ>w^ʹV7_wfQ[]vÎ["4IZ6Ĺ҅oe"HPEA ~:(QgV@ 9.G䚩A¹.Y 822epΜ>qf|#/ǎ]`_G}}SYJ(`lD$s"!KgN]}K--PCI HNW6^l.o7EddļO{J5/%"=گ-;Ϝ͚];v>|'Ҵ"Fwd77s?{oX³StYku]˥Rdna~ΔTu/ #ؚ d}}կ_֗nٹKKKmTT ģG"QHzT 1eD6h<թ)I$Yy.@=-EJk]͕:HkP]j"bDB.AX }QBp>lzI(|Qy=3iOWj0⠜K2L\n˱ .o6 'J"AT=J)hbhdL$fW=y&imAK@V%&N{L/ & „Eu }[xu0-#R-7'g_'jt[lsϽ/}iƝ}z^4 gk|;f]E.cRXűvWdbӵJtWr(k>wiK(*#zw IRcvRӊv,V|ABGFP)0J! Q³HC*XYe[h4F&[yaӇfU}I2YS[5b_x5QT@z !BTm/>3TU3}{f/hKVkvbd..6N. h(KϜ:~護x1G*="tp @Pt > "E"YKR^csWT۳cdpjq֛sI OLOT{._:~ؙ'O9]_PZBw%vhhd{xq:""NA^";G4 @/Q}Р7*#*j5܊"b⑙}PTk" T)y`aeB2wDӨC 0(1 Tc5NC䉣o÷w~;n_}PooUۭV{wqΝ+h7"L@UsGq+>xA!%@O@I fZ'|GGiۊKڅ "'P_?˟~'+ejWJ}3gλ a21暫z!Sdl[iƽW^G>}GR}]λ/z׶lejm]v  ΜKRaM_{=_|nyylt[ow{۱}0.5N=05UT ͜YJr SR9|cSS;w8zn [ڸ}b^%LDR@%rʪQI z4 p "&'$1QCh6SkwmPd(MC^c8 !Ħy].ʾUQPkZc5}!TIZ`M Xz.Ld#LxV5"+5Al)\:!/$JB!.Q {cE%"RMӖQI@5Xp dyiĔ">XkKO|GQG[=H$z_ǟf#!'>_~C?L:۬7b0BcI.D`JƂB@*$FD+$d yKՠZ/elK ɚ$*6^]ouvx5{zJo}\D 22"G_^}J_{( ދgE%HZ]a֭O=og.~ᡮE-LM{~={g (^V+?o~Ko޾0*P@jkodFOΑ,/}ɓSkzz-V?ȣK T)sk߹spΚs?{=S?n֍NŅ$M yYcmΣF7+˟ڻʆB^*IIC ҰzīRHG1xD P|@bH*J(έ.,L]wŕyfw3BHgB Q 4ES% A"Rdy;ij  ' 3DZV$t8r0BƱ5o{ȂABB`q)YeEK6}lm B.zqx/Bs͕լhc9*W.Ghf/<[zkan6I"VrCQ^IwbPV{5_Gmz_1$qV Zk]֑@TVYD Z@J@34=q'#S.w~_ms5A S &KjP|#¢$$dlH xGUeYd0TTaVWF{֭~g_aoʅxmdwT{~5ſ7^wO.oq]OOY#>%+BEy5(( 7hʶW̦Vr}afL*[n}pb…P[MFsSo|dcM  &wnrr(AZh>N|˝@WTFEQ^ctlT왙K>t왖XJ#ӓccs` X?fm-3 {D8xUPP N,L bl$8T"2Ȅ 8g3;}kraF; 0h:P\+0B#~_o~W~Ol\;{Y(sd;Hgp֭[?765ګOiu;3@X\Xw|G?p j)f{Gn۹rLX]w~_y楧񆑱jlr`=s؛oxjmۼny5WONLӧAE@֫_XZn9seJkѨEő@"E՛Vk=ӓw;{&IB yKJ(xF)1Gd8[fQ *JJV)E "j9R/LL߸܅9Ϝ9.|__S$6o߶//Wʊ񉸒/gݤ7,//.#4$> 6o[;7o}e e6?_}W= _;|O\l߹ųoپoj/!j,*iiNTĥ T%"[6Z-'Z&&3O&FFQ #* ┩{`" 7__\Y4qdEt̂B IR}!#@.wDU2΅@DGD`EyVUPo="˚ܹBjk7Lq|^%6f!cLU3|b'"/TD"5NR@Bf0vTwTwoUiԛ%5qɳ|i)3󗚵م#OIKsnݵ톅K&e`."c;jB8g,YC=xEB"ယeYDBP-*!Ö:*[FdPDCBөA#Db4ZxEu<!żBdccFEyI^;NVmSڸeȶZΔ^{U3s?ǃ KdLm~\vtEi!"(!24);/N|<~3~ȑͼy|]Nn/t'έƘ]ws{ҴwŠxG 2$Ed-z@De+ұ>3*[Rk?ǟG0{ Ƒ;v\~ÕjhҹO[^u{Y3>5<:ij>q깧8q~.q*&4Ke6ƀKH4H@ ۮSCPA"v(v/.N:rwLoݴm6l( s30& YPBH(Z@%,А8 `,t`@T(jIF"(>y̹c{_8agu?8z߱7;wNnr &+_5mOSky]v*FlI\lAE0>ȋ`wCA]V\d  JZq*%NNbb%("tp׊Bɐ:T"Ha U @].kl"J ɊNF*. Uwwk+2<6UZ4GP_+U%*"TEQB@DbȃMqB~c͝:WW=Hexddk`ifZss7oX;5 k3 M{"q9E [+D*lXE F@WÂ!J{Q8~֭5=q8-a2p.v tzSohGmy7w7:t'W/]ܽm<6RڔC☭\ChGa@@IAtv&雽pWrӭJ"+5("@XU:-"U Qч , 18 `@އ±Ɇ&\w_]RڬQ R"cg>'x7C.\  ZAD@D$Ks={SG̋@LI?~o|w8wLT* ‹<¾k͟.ՕvuOyocyy:fƛn?ȉ#;]xbfUdbdloxg 723{d"K5G}v喛o{^kM [K-)BERUa N9ꅙ'_~Y!ȝg[Gs\9 Ɉ y7@CM/M??xhwo " t #BTjh5Qz& *d<__غ69~D JQ(p@7\H x(k۫5.%#VlƾVCsp 2SԒJb1+-|RԒXծj}PM{X4r0(mBEdyA<_4ДbP!/\H/.`R.YWdBpI<>sҁ/nZܵژu> )[n۽['ή0O|}7l^Y?3 1"|`Æ18Ad ;$bB&P%H@ڮZM2T1H66"ޑE@ xE@I-*@P Σ"sj4n[y;K"4QIF >DIE95iM[!\8o KI *ud>X}+ X14sc?шq8xZWr푃'ڕ0q]^_w uov\0wȑ#^]&{ZK:12F@t^'`_A(\% dcS;\ <|ܛi̕ҵ綍C=}̽7x#l4׽qlhOˑޮ. $ AA~ / q&]9>>QKɩG XfYQP^P EpHYTDs@*l@e[DN-iPTܷZ#Cyofc;se۷n}|(̜i7>{bqڹbB BY-1zP`6:333f'}=UO䮸K~7w5ϋ4\~ٮv-] H΋8,޻o~;wzw~eyqƍ7tWՇ|dǶzI;fij;}/pM7[2 jpyױw_禛nTYx=$bP(Rh-8gDϔ{*ˍ|_VNV{10X_38@dF޵UR4Pt :|cZ3/|k'7,VRkD މ5 !@ũ2!d<TUC:> cҀhx3ibK=\*@@輈QlIkȋ%R` XIAȤ&1Ag xDϛhJq"MȖ(2eH*HIYj/68A*qZBOd &*۴\ad,'p08^zhY&$- 8P˚>zؓ$c oGik:2욑R"ZTծji-wNϞ80;yjו/~w>3[$RdC}Ǒ lQZˆՀ\N;+RߑK(*D (! Jph9d9fmĔJC59<'kU Ȝ$< !U@(]! ^,CvA-VĦUd16e S5u6 O՞X0^uFT:JV=7wlne+F'[o(% ]Z-Ǐv[3,,΅>еvi9.ucUt F9Q xQX8*ĭԗ7nܴnb]3!@A@ !kE$/{(1jQRYz0.%)c"Gw$HP8GqZbY8]N|܁ƜQ(Z !qO6n;g /ϜdC .+cJԇ(qY^ Ͷ6*8yP %"R,K/^ۦ B2(AVJ۶OO܋>} /xշ]|hrA$M xuޱ0(2wPÜm\<2YE 2q% JQD "( b"BTGCT=!r^q"UF9crȕ(a@ĥķ2_`ph69g .D"*C$E;Kj($vTUvf$@lT z"EuCTܲcFu.@Vㅙk?Yrbev>{h>g"ߺgw$˭fio8܉4tR? ""@Pe8j5,-.X'Ͻښcr<',F#}}CC}}}CS;BEU1(:rlWI[Fbd2hzQߙ~FV}xdKaݻv{V 5s<2 T0ЙQ :{AdFw3xj|w=Y9_d9&`Rr!׾vԍ7[?lwF=ogD j̅mjե<1}wWOWw啗xr\&KL"yj;|R V!I1i@Dk0˳ޞ>|IOUcg]<} l^{?/K>r#jE0-+'_x}N?/G'?.ZB6DT<@B>`-,v=A-jjH%0{WdYsbԅ3yT*-(JjJIT"ޖ#$HeVB0|'19W R0FG 8XJ!`s1=E&ac[-\NJRHZN*"TNY\ƕ  W-WlνWGibcԲMN1fVRQ V rc5+ڵ5k6g+Bpmm_6ᱵӻػwf}rPT{Z)P 1a $'&(A*%BCNp%x =#( H FRB> VUP;z)T1*aAM! `M  2r Q֨\Dư(vkղJhE]0[k"=Z_x@tr!P;B ! F"CAt (!2Q)2K/6ZT]kSguy)k===Ѓx1{tϮܺym;&F V#&*ԫ"$NH!NJRiD*i_~vۓF(e6\Om߱uێ;w 37z'珤֎l0c[DS g/Dn|+soh,EƟ_%RxDPED#ad:E,I$)ݴyd5<4:::6\=#.OmؔV:sI`D 21ݍEB^A&,VYEA+Y@@_=rĉq2G6tm[{?V-:u * JDD #\7p|u7|LMt\cnzQ L4fKV;uFG͏}#y atlȲ[,*DsjTw|KV[FP# lpHt"E*@,%k$2v 'iɧl4woزig>1k_d\*ٹKAOtHaGkw^}Mذvk# H8b N@:aT2) 2AEbpH"/U046:Xm\D5&J1l=jXdLZD$>EQJDq$)@@B"6Y5IRv9XLml8qI9E<RV* GA5pR*r+Q.PT$ycMRSE%I T8*GCBQ@ppl,q\;.ƍƆ423[`rPm$o:YkrtjϞ<۪zzh3A|rkF c,GE\VI&d@}Q@l; jGp&"F VB0@KU#b bC;8ߎRdlȤPy缷8N@D1Pm9y䈌)'qE Vd@A}`FVTPDX:`Bm=gj6=S}=}Іf-_Zlpquinqq:}`,#};.ۼkt>=zYoXfA ! `>xBRm7jwRU鯾v˗_{͚M K+Rr<5.Vkcǟ}'{'?sG7n߸iM7];'N..K=#}]kǺ*P)EVW?X@lQ$@HdA%ꠐFI-g;k??;t'mٹ{zyD`H%Ub&DX@DTB%F@!0v" j)9 xbkyg )0/r;o۱H v}~7\+. (B9$rJy^0  ]ծ˿#a3ܩMvZQ<2]Z0Ůjb/? ,\dsx绪jfhtCZPU#aǂ @( i*r`XL1sn'mޱKNvч{x_]XR)Em~S8V!&H(^P73>>922s[jknf!&P`$~'"#(Hx|D(S@PJd #EB`G&sν~Hъ&1aGX.$.Yc,c^d ]YDHFckS@hfLe-[N $UDQV+_#{\xQUPELl1F (we@b&pT s@ڙ;TT!й"+bԃ*{tXyˆYUr·"q PE [TD AURfk ן~)zי>|BUw?irAI0Y"- TZ+IER $$ `rxBwW9 `>sN(2VX@FJAc3tK.jN[H|*}{z]:37u&KݰwcgKIgoOgЂeb3J BT| (9z`hx{gv?}Sg^pʕޕUO?Ï?mg{zU֬hi)imvӣrj7 /[u9}bc҆/5 K$D 92A$%Vތ+3_cG{''~ؕW^05&WT$` ōDP `,wD\\R, P /Xm6m4˘(PVʷqG_g[鉩B޹]\.wtTT9D$AjiwwW]{qoBYRpV9R) w޵``oQEkf`xCvܹxm_l(Zk46ZQ9YjcBi~ș߶S|f-'ORȻ:#2Q2 SS}CS$N[s[&HDT#*EJ*yAdDC$,f; DHb M|ZB(%P&ȐՈ,W d˶b9 52͝C($F!UEAsT)IT4Q3O"̌b10Flp&>dY@}RSj5 bllKIJRlUI CA5Zc"} Rի;;:[i"DD*A@ DrRyį~=vѪCKE ,\4(7  Rŋl6TJT1BqAP@@ iQ@…H BxuA1!uJ1h@`E k|#爍a6Hd@Q"HO+EBQڔBeQ@7@Č]AsZ9p]<=GFZ2ԹlP`:ϴ8G^?=5=W*=Ë\jٚu jQ6[F=)jPB @;;sٯSO<19/O̜\{-XܹS jQbJđ/]~ɲ ׭޲yڵv9{Xz٪lZdIsgͦLO߿g;{~.Lj=^T0` !3!w@T@A "JC?'?oۼ>K_:rS?vM7VI+OI|NP9 {1`TUɞqaŤP@G82ŷemޮg}ŗ]{5-3h=)X6n7qpEt^IApR.eYF"=}wZ]u$MLC}&Kmr[G秇k}If$y* Ooj#4(hLONNL}51F{78W_}u6=3e,Sՙg?ł\wSӵWPI2QEPE)s ! PLr-Dm݃\g]COś/f] 5&z{(Jy>;u4VJ", hȐ% %4*^(@[xC $ d&G|w3{ Yw $ A ggjДR">("& *HVcyY[P8V\$453٥՛jl5S%%rT0DŃ^1DB (Dd#enΥ.B4fQiú5 .i3laA΍Nݼ}'k7߸%?_^4MADXKA ĉHc"2f!#C #* 1(+UDHJE\4#Qc`UcY@[.3@ByEe$b#ADwZ|U@K| Q`H00+A FZu{Ό?#O=^~13YpKWZ7Wϣf=ݫ7ntEkojs{^`+Y>|Ȑ((j@Ylwַ]k~MVk ctBjfFFVK.\oimxjFvCP*^m\wȩm4e~f{^ݷw?:;ݽQF}`6A€>(*2[e+aTJΞի6ndA֘u+&GNj&@ !X`bR(d>PH$CB$SA"%F1i(ȯldbbnpAc[o߷iƠU"KzP<Ի ZUqG?r7^{K\9r}T4h[:q1f``9v~s'5y~q/<ܭ3A(#l~vn˅mXnK/ ̎ODAitղۯ|GGxf͆'O^[ij,_}O?w N=D L\1B T aP@ IE9sؐ(f<95rie>.ؼl~`^l/˩zP$aC"B\!4VHBNQ0C@ƫ jHsh@sI y9R@D/k9筢F&T5E]'-Y`'O5=M2C$!2$!xy@$1*`p@*d炈6c262j.,o֣8.wuhRE!e.7 PlMzc1R(ng jةsi.&Bᬍ0ZC.5FD4C{Ah!(rȔϜ? .s*jJ#!jI׹~˯ĥ{~pO'sl Jp.( e% *0e1"%ID5"A1'>UoȠ(WK{(MW՘ aX(8̀4Z!). ưI` 4G2%P- bQ A(xCPjB񊦳Z}<;8޲b}_ww5‘ggÙf(u.[waEK{-1x/gG'N=x͑Ϝ9utE6q!RD/ʨBhuxz-^3N8jl}r,O՗^TcGͬ\۸⁁--M7Gy+c΃+z;]JR97ð/P 2 ! ,Ae6l6zU'yt{pي y6\mXapɲV{abD $R1̊ *JN$@RA2*n(,k5z[,@!1FھpxjW^ILVG*"X)ں:GG[g'gbly|gk8kΞK_0ӘU6Egg΋/ٺ~ƴޒFBR.U _m~ȱ#㻟Yv^Y"HH]Dj  "DbH#"Q!BYjX#!&,IA!DBD)HH ˥gNճ dp*Q1*"H* 2f≍"#H5j]m{v>h67\E|~n>7b M#S:* GBRD2|]WTQ(Z LA"2[DE ,fIR{yߋOx_jc,)խ߸Çutz:;v=D}w庍wE'rȎzo"[|j߮?~>ygڸqssdLs_6qߢ_p 1˱!gGΜG>&b y78>}oΙ k#4Įʃ~w 7ܥ.eV&KZZq$,Tk!k)́T}`0x X 9ճEDo47lXxV$(tԻ>ѯ|Uʕ,٩A@H1h 'rA"9NhU[/Y ) x@ ʈH4_8+R|g݆͗]v 2!J_o豣bꮡVkI[ x6XT,IJ*2 ߃Dda EW "eAD2 ,A@U!QU?44rK/;8in02Az0T%qE}KiԪ-A#j &"'9f2A Jl3B b@DĐڨ .ZܩiƱM>r[W/NxgJid8*z'g{j4hطrDLEYT0rQ@[BfdDRP)8 "eT*#G *֢e(boPlP!ƵZ"kml;r)ZD *.<*j&016 h@|'"c "F16$H#SSctUW\vڵ::ٱc^==_yv`Yuod[fr"xV]Yi%PtγaU eZТ֤?GgN?42znUBg ._!f8x`SO>ģ{_|r̛K캥 n^hr[g'Ӻ0juhx+ׯTv5 EE-j)|?&6"R01P SfAIv#5HQ =uo;y;^L@H5-bsHDP+[A 4( )a)jq@2K&DZTK,Y{뮻\.ɰEd34?F2~]o?78pbt, ",@Zk|uܹ޾V/_wpvv A@4Tr ~i'@Dᨧgܹgv>[l}LO3GPOaꋶ]:$G>8jok?q#;vl袋\837Cr7G~z]v-7>z悭wtNL(0&* 3@q$)2RܟU*xMC?\5?;C+ov1[hx8X``h ˕,̀(xGL!%2* 1)( .;ؓ2"1$js/}jE=D⛍mR)m4%0.xkJ%>uA"_OI)1!OPdH5s.ocD8LY%eRGQ-]5)421rgΩ* ͐6>dYbȅG`D4k"W6hvr&^dx0Ϝ!9~đ%Sj{mlbr|fW~r[ҊRWObwLbɈfX#(bq*E0 "hQբ(@Ģ𼞚P|1`+81N,Dl)4'a+≸eDpby0lBFR{G"i(0"ABL(a'q$Dr/v[ݰ^\oǧ|gy#/oXmƍk7.^hj^9{vz\뺛nZh[:9BE BlǞ۸e=vv[ Y݁y=zg|Gw=ck[8Ծiق˗XkEi:xhFlݪ͛.Zj}F}Hj-D@T` 0I B.^ 4i4UݖSӽKMw?wuh˿|~#Mȉ+dSLAp b9T0ćP ATQ#lXQABA6]ݯykW_sH\J@Xnͯn~;-ĎϏ|þ_|[n-[>;B!u6O$ZF }CCYS(mRw=3~k_dK\.+v>{=$£=Zp}OOs1O]o?k k5JȹҪI o|-?ԒK-^R!E(ރ!A qdɒK[O>Џ~_F_ٻ{/m/b8r;n}jnVAهPZlYR+9yjO?ww^ٳk+::V ( yfȐ1؎"R@$$@@* <}ۍ?122h$V b2pF`S oȰaUkQ9ql%6Cd-D'A"i$!!,XmĈHHӆ6,1@T+R\)*XZW)X4ȑH f*1!LdE| q@Xϋ8;"!ZtFȼhRab\KlX6(7rՃI7Nͷڻ_Kn᪷OMMO2V|]Z0&>8 ^kZY8̓aT(G s ŚX"sQ"Ղk$zS^=L *OJd"X£(>kyBf4wR5-k(@%kH8&|P(. +({|7]MOύN:ws|9rpiϗXr冋n`su9<2q4Ht-nruzzJ ARe& h@}?y;>/^nۮ;fM|-̞#_n.]vɦU+/XԳxi[Δ+mnzKljjfr|F7fgf&g q(@0+ЃA`I)UW?kxᢓ4&ֽo؞##K- .0@bũ$NMd !H d3@H *H$HG@`L$QU|D3`U>t;w-W*UUa bDesM=w/?~>sǿ϶ZK}+O<1?> -bQ L|TJږ-X85?;߬CK#6V^;#nڴ r6[麵knj /\suj|N*ܲ%/پ}; A+.䖍Ar>6>R6B"hCV Yj4(1d bI+h"TrbFD0]qi3mj[3SoqM6(Jl^ RqɊs, # #:DW{y嚡kw-(#g^Q^.[mIVB\3sqYf"xo F*b]3x݋׷Mmmd?ϠqT+!]C U;jmӹl&-Qچ]vfs~|1s9;3VKD/G! N|4PI@DPPZċB-_/Ku=m~򣣣,L*P[H ǻ- U2!"eA@@f2pj L$Ieْw뮿sfj  *כVZ-P)_e6"f=ɱn99JHLZtvnR^ٹ޻Ƌ uwwvOSN~u=w[} !nͷv<8%_3s3UOL\pK/C4m!D @{G#>a-[.Jo-7gΙ?/DL"2hJ-x$JA0jm[ۯ>ۮmfaoz/rVj7Awglt~c\}ᆋ};/@zc` A*@%԰Q BAI0Y/ *0: FbCԨf6p8wa {Y;7mHQi@獨3ԖL yoJd4y&HIV˵Tbdr{uByR,}RUB+ϛ-YVOʑ5#e."NBqW 2kC^Ԗm!ΫR (rYDF!B$'MVͅvu4[iBl*+Rymn ͨl] ֿOΞ?|7ةs†5 jm=jq 3*DAB; !+`q/*D4S @æ@<)@9"2E ˂1JQT/@A\yn%c @$ 8KqgW}6?wث{_z;!!Ҽ8"}G7QO|?U^;z#i>~ʫZd䤪0J;?v[oloN;{z=rv@\ @u=DyUWۨ49>j5Xg׮/}3W|_￝}>߾?qc#}!d ^E,0‘-J?vw+__-OSi55=]koT*{[;sSS H}~샿onK:dQ$"d1(l-kH5̆e9C %b轔JIZ9|GϏN8ZEc@1*ꃱ6LϑBPJ(1}r欉rrrAI2y!#B5aX2URsebDD|d- xՐB.0"r4EAU"Dd$͑1ԒLFJ>Y=3J׬Yb冬YFcA5xn[bUЊG[g6olxŦ|~03֜kLGHyZXVEVAX.DHSCL JX&q\.WkssEEly~"( - 3+Er0PI t^Ƙ)(YBxR XľDIDzӚs&2r$$5h5#)a(<*&l#cmTJkrIb44P@Ȉ(d" 7Ikl LQoevjZj~;ʫ4wzvu[ff\_j[||[w}{>s{OyP ޱO<V,s jKSN_{mWMiQo z3O^u5֬-%Z[,EQ&?gOp5 /sG_T*jm5Wr7>?Hg D@$ 20aJxXpyh= .pNMLN7&]y4O]o߼`Fc޻\I,LJO^b5kBӝie1L""" b$-ZBJY`( xEI<"|`p /nkos@`ȃxs405Ġp^{ ҝ*Ay%)/Z4tϽ9v"Q|p+ի~~dZ#;W~]/>{t%Y=׮mcECV1~珮UK>Գu lݲc ZՒ({7lQ}k(҄"jky#9śvNMXkU0ӡ &'W} ]|@@Ogw[wW'{<w?}=xMTOd?1r/,/>Ɓ7ϬYP477Yd<(`VafTA6:Mw}czY+Zd-ĹMݕK/,jT;ش""`@ϯ'cE 0׀Ѐ gL`E1Y朋k M*f$e@> CEb"sj!T nX Recr7][1#dK6 JYd>$HLJbkJ:T'V)I81DcK%alf12.ix[g@fcgk.^0rdBVO}=6ZX!\ukJԺ;+)h@1jPbVFڢ+ l8 9UA|GK>X|#X6]zѕo;~bkvSRlϰAfyY|#m4 "rlKrmR&k{N<ܞ%oڶKWw[z~Oxhvu&֮߰~M YܿȑSsm,}3LR4h}ly  j]zk]}C_:ogQʛtclw7㒯?{.^9خsbɠG%u (  Z%@ 鳟SfL^ *)x dH  (\$g9;}dJ[?kVD̃2T04o,^!w-M{Q<Š HlABgLӻ.ٺ‹9v"U .Z{W\dEfϦ&M\?ulkGؒVk>*` Rm<59yRLop7fsr*Ͳ(  ,V]=;w?wK.|ճәsdH8xk;{CQήnDxǏ'>Y^޽g?o[/'//~ k׮?wN5s[.ݶxңZ®g>tT3pЀ FS3[4F!k5ZO=Իo/Nrg GY*JWGכczjGgSW]ɔJNTf` Q"ҍhP PR$"*Fj(.s m ؚr R@@&l >H6&+4vy2-@lm$QԖpgiʢR¨XT L6r$O5()+ʹ573G((a0.[F%I,qM)hRr{W4~oRnyDH !hE 6TUAhѢo.ӆ ˊDcbF(m_}韽>?>KgNZ[K_|ե/Yo6"|V󡅪%p8)U)E1%LtEol=h'_ӏ<|M ÒëׯYjs_{odM:=9ztq 7oZ}zbRűEaPDQ@) W,g&GN=ν=uG8vv_۷Ϸ?N}}Ӷ{nuUs#OLjX̄(1y(ZyD!8>Qd9 ABD2i -(?=@ 1!c{[^+ MO?;뮝 DBP!cQD%El[ye139qv{~y/^oxfn굟/]uv$dEA IC%)IoڼKs?pٳŠ"#RGw#>:h۷r3Mc[;曻vZx%.4ob }}{}o|'f_׾|#׭o[9[J\,ovvJ:d?oWWde)u!8 l$( ) '&ၾo|k^\ELl<|E|\M,Yb`xQ=}yeBV[DDie%"RQAh4F(hP2B==}S^|Ow'06P6{L;%$X Ę4B"d.&5sAȀBl Vt4$.riTKTKeYq3Eɓr qgۈf-Z:W1Pu6xW+"+(@scݝ7or (2dD#@|K&ݯ=c?{iϓӷ&'F ,inO5Yr[$dL&ф@pyZ(R2f3066$=9?_ߞo=Tݴef*r L@H1 *q"C"TV@2)(1"WS_pZUD}G_Ъ-睸c<˗-X޷\)|#̍OY ETZ9I"%I9ذ7 mxDɓg;v?+/=}fr{/_⭛WmCOml8R Hh Khʫj/ggfsgj |G~aӚΞ*AU Q *ރA0h &ex=/-F-!hT@^?7=Y;k96H+{'Ė6VӷJqdذ#!"2A|:C(28Ʋ)yߜ$);d4qBlMbBfS. ,:DH;1( IZfiZ"kՃ7B$J(tƺf 7M Xh Ł{;=[[V}છ^{dOG_ݺ|1=c TmnDY Rbj>K3j4 Wtuv՗x猑%zrEC_ycj~zi1BPHD@ X@7Pĩf4HA 3^0Yk|'\CmQ \)m/7y}W.]\ k.ۼ|y®ZguxɚO8.WʥrRN*Q\b&ԻAeޝ==~pϡ}/}stV]m%eͩ& nCk/?^k/5 'csѢWRso\܉;=zlE΁V:;>?v3?[7\K7O=}l8Md !""!,B dbK @ {JY[oUE8#J ؙ3x6,Yy֏}d|lL AXזx1rhL(7>jt~әŝ}|tG+|淧ΌT(2AB *!GQSX= T^x+X)rT@FB%1B^t=?_J)EI`K@!KcfpH(c;%dE+I iӻ@{ X.y.O}I=k1\e.X!YE&Z66qbUy -﹭XF13`!(hʈ13IR%ݬ t8=vm؀zP"+qfM5SdD2TF^Ӈ1I/ZV-m='M{ňh%]mj(21f#N:zh/kwM Z-nf=cgsm|j}ժ_z¡j{HOM@XosEX,@@QU(</A ,т|D\D98ʄ @Y]PRܩyXO(*i.K]kkJGy8M؈"@R{9.Gz}CsosV`؉jW~ó'FVhɢ֚j4L@5H ##yJ>t=v>˯v_W'GGG>q57ȣg6LHzQ1I2LH$6&E":m(h QF;&~[nlC ;;gg˗\z%iZ‹9:~g \]af3ݚk|˵ .o5J!y]d\UZYȆmdJ tӓ٬Ouw7]&^ (LUZyΊ,HP;6zȡ[,ZM!IO_.S;~3&&F_~ťy'/캮SSwDl[4E Eh0  /ha0BAR/O]}՚.}K6r[oyx`&[cwoܽ YzPIWH 1 !A #q>#NC6vYY%H+;w{_T!k>q d %{R> EW_>:1]@jJ[k0)K^y7ύ=Q)Mvu5V.\ֽZv!Ey'N?ONN nhe+6ot87yhad5p |@LLDȔ sE0]0( h@kE=*HL h`J[3_31rʲiz#+;=v}W'|vuu952?d>6ܑ/lU*j@ʥӬ *\*zW{ oo~//wz^vw妾yKO?S1>345E,F~ϊ̠&6ĠӓWZ5C7n?]]ƔFɲ1j$)DeFAPd@2cPPe6l媬y"?ï^]m=9=17,]xť۞}i ^|ǵ7Et /!??=>(@4B!s3|4>>hև>Cl۰cvv63QQlĝ]]=':vWn]yegG{hi9;5cK6u-B$6!82lx((!2ssӃ.ܴC"2heRŇDQr`تJ 0E䲍o0,id/"Y@PeQRj䭐r.- DjK@ĵ#qmZ-l%x [KADs80F#.ȷ\VJ_+o]\rG?zëmoDNqw}/.YӋ%iFEEΠJp!x>L!APoв1VGBD@D"If̹f7h(3&)厐lbsO6ߴe#Oxo^dal~~^J?;5AT@1YJ[y}T!IT8&]`h}ƥtuv ,췦dTarl#=vġZȻvJRĨ{E/9t櫯y쁇G*_p;.ktt@c~csp QĀ$$2A0hC*.`eEd zBp@B`CQQD(ǥx_=wOM`5Vچ_YmP~nw"7s TR!QD`Q$xXക"$*ww|}}N"V\sMm={'>lmB442Hh֢"@\Dk5Z=K,lUǟz5=A$RE a4D BD=j  W;<Мml`?{ۦ f+mg3S!jM7pc‹UkVwyn/;7q4{ 5pAc "diJ^}Uwo3O>=q!k+Zz|5WUkY #,m-f`tƔ,zP`-b %P ۯV]q?^V*[ͦlKNOLuw?ܺak5 ,曾Wlw~K4-ZI'A)gx شhN "\CF  D`R0@$I2ZrUkzr+hRՊ˜KSd"h5@ResP]#(N;1xȽ!YV+ed De͙d!# A)0"ԉE+  UqDMifY)J.dBl;a;GjcgOZN;NG/ >6uf*~pТs4;;^z[fS/w5V"v}aFl[%[f!v0' 4MM'$N(d3Ks霳^|ż3^~ ~?ZUvά9խolݿg[_n{Xdm}Z&wOL'?{}iÅW9t)jCUh <j0A5EXH7\{|x'6o'>YV '1>؈lcryOzzEĝ-Z,lG 6)ԏTLd@.M|z `9W(A)0#1/.x|kXF02>:~ztM*Q{JssgϚu/q߃<#wVUk}^bKD%ٿ &i0FHlы4($AQbF4!VJAU=(  `PT!bT6h|.Ϟx ͚sݰz_fnzrPlݼto?5Vw΅gլk$YBX2)+'^|Y{k jm,op?WҞccc)ŵj0K 0AHA"@j,c2UҀ(ZWm1+z7Tn혨NU1RP5֊&#!QƦ:T02VkH⻺,b pmϞG}}k/T*ܱ]'k;y.8FQ8"$!Kl)7qi$IooE^~֞s1F6 %n$ ZoLVUߨ1QQ1j0 e!PƊq`Y7d 0/Y y4D$~E+nzM`ӣ'ι`~;f ɬ՝{EE ,3* !Q d ɔ#SeTA"Up &`tBM52'^7{K^ Tr3bFFx pa.hȄH"\)i@&6%@󄅂F:؂ɒ:l PQ(qM4(4DJѐzQ's\;"Trފ,E5.9ΓT-->8iڐ`1Sc㫉Q:Q>96FUTY0H0QA4"ZڰxlwxaYOKsvEe3f\zwqǶ/>|C&&._]]T48( 02lI!08NzM7~^5f̘qZRˇBePrk BPԐw,Ed K :eJ$A8<=т 7ч{ k7b.p`ڻ?|y[o]>sy fbd"%\sA5&bq^ uG5brᒄ"ɗJYi HH)@Hr>1ؖKDG1$5E0$uiQ5/.u.EfXI R7e%r{#o۶  B@2!xe/gͻs~s/,]صpC+V̇]{鉦kZE(r{V{Ug퟾Qv?s!T{6ft[/=kxxy?_룟~o߱GՆsO>;T؁ݾ í~"a XE j5%D*"mB^Y^&dJ̑ec'O K8ozjzbx5+;{=̶%Ir W|hDLbЊJ)4T2Y.t8r؉COlyȡ,LJ }]=PnhbM]=B~;q˷}i ٢?\n}ZmYݍ;UOӠ"QClbmhTLrG j܌620@M>N)Q T?DAClKܷSГ?~7듟r f"䩉F-&O&FNKȂy 7^vɦ+>}mn}+VϝU*6[1"2DL>J.ʗt_\|?ދϻ`̙/?\vs8fs` a"A *,0ppiRER ˜[˥-+,ZtŊSSB\jkkILL}@Ħ> QpIBPTR _g睿oon?r<|x7l:;{,]}/mN:}z j)qA|OTD4qk/ҋO=9#M`ƈI4NCӚ)hh*R!dH-$dieIj{ȻudazzZKWggo* y4( D9Vj&d a 1X%.qH3w>Fd,Wmx -KdAqTHrggE"6%CyaV X,kC&ֶwģ3cM,ƧlL1 4%6BѓOV,]7f ޻B>^0{l'֭]14}$j)^3Y[&eȧo͙ۿ~å"fb5KV.?w[nl9VV+ Q!BOT(P`S@D4!͢G2'.[6@RP:UR3N(.dkB.r3Kr#cޥDpzdT$WLk9Qqd"HX@,ud}Ͽݯ;} j,)tz _bfGOz-_0M_?{G?7ݴ|ٹDɱ'(e6d TAQdmrK9@TTŠM/ LhC4* ΋J0Ke`B ʀ? "Q ,<\we[Wn29~jdZ6\*NZH& %62q;o-ろI#5c@$ JD`mT.N9:cR[S_ze˖j̡ v4#5m )1mi7ػTFr6l!&De|.eWjmsE%>#duY 2Si-E "`̇/bsnx}YQ,?4K[Z֭Y/y?5gUX}1_IW"$-m-b!=Ϝc߾cwOu9"/O aT>84APH@c^Ȑ0M BaIw}Ej7#rIԓOع+.14:2Jd-E$_OMW_O^|R֦H!$+(Դ 2jM@U$0{djNՠGB%G8xPk[6tB}lZ|SņkU%(W{(*[zAY `b@2zD< #K#F0 L\.M>BI8MF=%|&)˪D<#ksQLĘM]!%9$و-޹FcpfߪU+ud8|6995888{;OSZ`e+_/]S'2 4[>3 eZ&κxr?{$)^߶EL}鱻>2z`z x8ξo3ˮ.>k (5rD 4Bp`r5A@H$@k6ȱ$yCeH@4FH_TDAGju|TΕK91c8{5͂xl "{C9?h5˗-\:0}z$ FQ, K4w9ޏ;9}O~뮻ӓ,0DI yPQ$ED|o!3!/&L @ 2zE  3@QlP4thzd 8s۳sC%2 HQlP4A&!kA ,I+څosG{Go&iE68'7gܗ ba$BlBQ7?''[KkiҘ3kqT*FT*wt◿_r1 r.Jff$P9445:{ƩW^|=z՗ݵ}Wwu9spKF *04LW]@@Dԣ%mRU%FTVm ͞Y߮:% iH\ 1*Rp'{/[jzt̫*Ҝ~]{kV/>Sy 7 B`) AUD-1YRH M Y z/@A@ GG h9&K(bp J*1P-Qdra.&^HI|f@c u>x睉s.M%ճeCp h|#sq1#I5͒:S\O3 Ӥ^ `ZTцL|>q. u@/i͚,8H R,[67s:}Z47^޵?d. D *ʬR<*k/Z_z{?zc^|c###|u˛mfm\~֟>v:L٘ h)giZ 1 14x."bAT8xTLAfF* l,#B"ll $Hd 1V;/DPmm-G|C@$ KUҔF 1AF2U޵uuW,gҕK TH}\:cܸCVwtv μ{??}ꓟX;q*hAFC "agsg$b2$Fc{i#UA$D̄46!#A'y#D"ʟfhR͚7k6h8~63JdP\LadLH-򑍔O ϟwM|{..%je $ RF"ֹlYގ-w?Y/;wؘϜN;1q|ػ{U+g̨MOגZU  AԹ{uw~gouvNT_9sWn<7l6Uq [_ [%AFQHE=Hh@"UD 3cSF*Yw=3~M2@1zK\/bRkT-\T:x#{?^T/Z:95t@Ҽ8HP"K2qpAAH#P Q$AG& < c!BQy7jj\,)ICމ!@4đ1@3oȢaYְLl1xAͲUYb#(jLMzL>.k4ːa4i%K dU'IbcB@@Q+b0r$ adl S|/8jRLl3Ϝ98H ;W׳̵_luslahMW]O`&m Mq8ps~vvMWٱogo~iﭿ}hNWk< J:csut|{u<'FjIȑ,MW2FA@iU"B\J9U$4lT@SϠj3^Dcz@D{/91geDjIV+(ZPmmd+d`qT EUU`fPzRL,Kc5A$<6 +׬1kMeFk[oel\š_}KnZNfӣcGJȲ9*MJæCDEf&X&Uߜ *5j E;%BzsQfK@+Q&5_B[k}_WkSGE&, *!T*q[JGh ycbE2=]5l.o]}O8I i20|p!zuk7]3711$䋥YgvXb:W?^\*/Ygި TQ۸c{֦^ urkυ{ޡ% ,q0k*JPP@VbmqO?뱣G%WϜE^jXg _~Eӵ,uΓ!P$&T|TF /PNr-{Ϲ$h-m}}9+6ƉSמ{A..0"PecW@@& Bg잆-0`pΈ? uu8}gkdQRPl-wtun޼瞻Kg_x8)Y;_[koz_pMWMM֪Q>Yf ""WBTCqo[ !2x$2(J֠e Bfݽgb j\ @43Zj xK%0̨SB 0`X3|Ir墜8j& P4^ka!da%]Zm\(>uAAQ+0Fl XkZ#d+)(qf.i%T.k42WSyqC B,jjŒesg H道:JZ̗*S@L;߷oωzhIs/J+{msdޒ;EXmySn/{}׎o;[,95Rnm{ot50{ɒ߾[sgŗ/W5ȟZhٜ Ʀ& 4dkOqׁgb̡45;|Q`ݺZj,%>%E۔(64"ѠQ.|*?ґSI&JRҋYpCs@Oĉk)͘_"KbFH)H`"QT$%;u_2wW?qρǓ41ݾgR6uX{k&U$lNU E``4X#P"`Ezg F7}Ѭ5眛 dӓ9^ e֬^~&&hϜ18?E׿F9?0kv3c g@ش+(b3򃖔TA!436B!B@PFư8!*e^[؆E+l)B=r.$Hp2獍\.sދh4q! x|ASq66 RkѡȒK\̦IQYb*T 4P !K5Q.'*>͌Rx ubi h sf6]A6Dɞ>4ZN- 9M}pJͪ_wyH4(f&VF@`Q"LMg>{?_xV!ڨ$B#h#[?ɱħvն7FNTgZi>tX#Yy;-^zcQ4vO ]:Mь@ @D`"D 10"nP|٘  1>( YjY&+ij 6PhbȈ$ɩR8`\0" H A(r+J]Ѱ"^rF,PAZ-ޡ >%dVTXirdlQ Yb33+o}y˾};[z rՠ‚ fRHAdL  *B&C02P3'K "":@4"l8iR:;Nnx&wl"#" :m0sW4dhLҤhcfnxpHӗP,۰ab3|$4 )1Wՙ6꿻7mɓ-hM\(ŧRo;|:zwp _wի.#3H]ʄ$^lJ|`͛_}>܋OTkNNOJfSL<`٪eRJĄ Dl  ir"&&g.G\P,ys~ofiz嗱iRn4Vm˥ήgyy?UT}ђ|ei61Ql[Z;ⱽG h9?׷omktUhJiˮ499 *L\OS4j #|hݡQVF! %Q% RYT˛_}7ǍHlsB70˟2 /215d݃s̚بVԳ'@vvw1GY#A$4!V=hG%5DoFըLAP-(pCQwV mBӀqeYHX& Dl刽(A'32PER.e]!R@ISyOҔ B֧\Qc6# ( A28[Qz |s9*Ź1 P6FR1x&MD& STcZ%s^|pYje \gko!wg:r/qirbB s3f 殗|K/LLΜU(z}Sc޹E:/X{nRqw~?:m7W2Il\WѻhO_u`6>z*+pĠl@FWI+`DDl@Ѱd#*b% *HCPM>#K#DXD"@d (llM^RC䅘y\w& YB ѰMUPVȵNNLP.# @Dh@Q5H򶳧9}296႟1k}ntêu8~"c/N -9hDLD\[Gԯog~zCW=_vOo۱c羃\g)ƺo|ࢅ s6P'F1ĄgjJJ,7o@Ydvmqx߁5k)ѱvOkXa5)#x/m-}:o~ρo/?ϻ|ei# (tvvNOMrc/RT#kTT?~+7ٳ~ㅥbk6P:+A0LHHĄ)F cFuo馹3&F!ʉBڵgK[d'c6Zdoo>=v'} 3SWQ%f g!10!*D@WlJQ h#1$*Hֲ:˯uKŸ Ifr( Hc&fk&%u Li͋GUR F(m: L).˂ Ḧ mZ/7Qzq\uK1h)%A"FMe "LG]@hI^jFUE@g0 ]}1 ?~LGq^I-<Jhԃ J\hk)zMhޞ[l{}G5'rAcyzj <@bga玻zgZbYR7Բ!F/A aEPwvdǁK۞z瞾Ϝ;V犳 Z-S}Z!u4Klxڴ^ !Bږ~n_OR,\0sZ^{B4UeepPjDW, zZmۛwٓ޷?/Q\UF?sA][mZ'M$PAT̂!h-9G?/G⣯l}XhūWV}'f/7wao?_og_w~݆sW/]`Vg{[KG wб#'=Z־zsfⓧFeW^5s`Ђy36?F$` T0!P#b !!#b!̜*3 ^Rk asfPASRc 1+"bPw`mhPbD*$ 2H& HBAPe&IjȰ(*! YcU/@caC]=j% ZJ+ԃLm/ Q4849of}poS*@f?IA# a^΄U42T$d@eUFa@hTATzx$@"2$^LV5gpR%Gl1!*7T($ ϕ=ݿ*Zi-GzyK^;c[8v׈H#+#Coo~;ݒK?~dJ`@GuvWvTH QC4g(2#h@ɢWlQɰE- v:z*64uYKvbzbㅗvwOV&([~н.筟<5zoQD DIUA @ Y8'!8% c+  (1#) &ZeΜ-;x@r4sKHH Q! )85`r$ `(BJdGJ5+ƹʑA#)xG̻Z#Ybb x$T2D9t:Aʒ4X! 9o"yՀ`\LjiVh QR̖lHȀ!l|'7{'>{];9wu+VrLwj]}I9pom{kUkIMȁwy`dۛ;S',̙Kx⡗Nxۍ7E456œS\4ÃkN   D(jD0 @@T$ 0Ĩ DBP2k #J&.< @'p̃7#A 0!jP*(8Bo-bdXmY$s bD̶ #\TȷK'GOMSl DĉTfFQākRg;w(c1M "W@T `FCZK (*bX q2t5?^mo玟ɉ|lpzHH&IT/ڈP!>IUUgn~> Wt߷s'l=vrwh3GOE6W(G?Wlz{gW8"!0#@LD(4eU~{o׏`e9Cgco~7{Z{9}YT;?088=NU&,[n8-,yG3 Ͻ421W/_x8yܡ=vZ0o1AQ$U4!xI9H˷Zb'fY{|k797/,N8ϲ, wd#O:2>ԣ/^} =ݥMחr$IHAX¶GO>Cȟ}xݢByɼEևz%W_kwϿ885O>ݹzAUB7yRkkZw=o/< CVO3 /-0w#/^0]~wC#K=,!rl,YCJ F@ ʅΎή֖\8=>^h)-|Ų5/oU&9 |cb=kΐV:u|С8uԾv}B9oAAQ*ZZO|=PNw}՗tN>qpskozV\Y:A lPPBC!A&?^Eם{w!mKFjK[B /O=H(J!Jsfӧ_yIn|jd-Yf΋h3T] hX@1`*w# FLH_:i f K~J#D4v_O\dc !'F9[.aaȲ555gQ#Oi qk1g,ZY& 1T}HB2=brYMR$ g|dhys!B-Y  H *Rdz4I-o;/\1lo۷wǁ s A=Q465Mgμg7IJp>_,B " QGNLO9ח/s.Z2]qROxu^z%'O( CФZbP)R|7w7^}2vpߡTfյ- T?l{~ٛ/w[WtB>(wt>}g{{6^d?7_z57.ܴGȦ]}}fO1!2@j>!q\ȷۺ::--|O=S>n[J=՛wvuGӓs8 /~y՗xrK #e]}x%o}RK.95"]gڱkC?96rP>R˼ Z5<׿=h^EdrĹ8ȀA N's &\Y@ E/L,. !_( ]ongnzcSs93{x6?|}oyey]rĹM[062B!|JB"4ȨJ LE (!3!#6*PE`Tϴ-0wx'FOe&9fQ GqPl4DY !`@2& tVT&țeYϹiĀPq!Aу&zzLδZ@ > ]P[*`&b|&As&.S1dbɤFm0iprBMgs>fZ%9K-@t#z * {-4zpYI=k6my%yτz?q!E4CXrϝZT `^rz/ۺcW- ׿i $P!EFRT%=moʉ?x&ߑ9j!"i1 E"ed\2&*ikY<" R`;/g6&2*! @J0"()!8glsM͏:r0Qioiu_ݵcƜ%M{&G"4H@{KE^IO]z!sh UEW h  V&@ՃbsEĊ9)TeRBD ?.S@ёV74* GjGO/n~鉉J+UӴV Yexg^|dQۯc 9c ꄵTS#TU(DhECgQDRQGFυ~E4IY.`GKI42vgZJ'x9+UW\uۮ߻mۑCG'FF9RDI)\~/s/[@{>_z`ǞCW^qMRȶ:8n<~0)[}%c|r\9_oo‹/Sӓ]=_9qg߶#<<³O=/ .wd_kWשmt]uӕٗ_6\?O:]eCB +H7yŗ<2Yyܾk+.7w=3;q<0giL|@1(Cc% Q@&I!P?>>}揷ܵkx }`=oeV 7tQZc4MFG{z>|+ZR$ W!VD(0 aAPFī @%A QTţ6KY9,A2U?o=jm5TUSEmjI5@"^ #,8msZwa MV+Gh1ƚ82Rv$xe5@` Z*-%_Hطh'W['-Qg,ǐUB3ڀrs]vriHH 'W[@!x0eED&ь@`DclR̜9ss~{k~ԡ}GO>t<7fk'鱼iugoɒ~G|b]wqTc+7oO_ߩD|[{թtr|^BHHlXΔ%Ժs='O_|EG$tw=5+[o$iKkkNdno2]<16KWET]Fc͜=ŗ>«4Z6Y8k_=M+]kpśVY\\[/%H[ƛ>³tȡR[]u{l߮.@Ll߳{_W.D֞O~v\OK/&`ද~zuCO=Y$˯UG.m?F5X<8Ԛܙ%˯ BzОSǎ\f /9kn_`;"B#RڮQ NX5 ,ʪ>$ @d+"QȈ6XM>k b2/osT~wu h)QG@YҠ:/b'هHHgYSIGV% EbDQ(f(F=Hٚ )mC,D H2'1PHk pG6dYHU$BPćLrq-K($5i&KSM`00(Q*YTnQ!Z@ 36'Ն0֓R͹Zhd޻b|wB  x|K{wmۿwwuÆPeI6Yy(*q}^Կ%GIDATv\&-Qw\$;qxԸ[sӦs['Y)EZ >8@e68f2 \PDΤT E)R yBA8PoaW:w Q5[/=qTd/o{O/~+lS#泗/'ߟڍ7t,(Z>e'g/Yh6aI5LG֣GF:Z\:G9fE3[Uީ(k1yx?郻_~㥖vﲗ_=~o7}ÇdLOW[Y{ޯ~wpY{wC}~?DqV\ WvoοҢuk~}ۃ5{"8O,[E A,\Y~F4|U$g](`:DZ(3jБ@b& )Jt\J8p AL&VHmP63@NY1(qrRI_7<rSF'GN/E×?Om ?8qdX %LLDa ʲ5}{Yí}=qǜûtv5ںZݕwvuϝu̓Oa!b3bT,XBIA;"B%QT`DDEaBU"f?s3 ⋏[}{xgWۏ#'bW ~-/=4щ֖R{ۣ?zt5?;gǫ)K-{'st}/9r'5@Sg@UI@Xh֗~qwoGS[^ճqwf ̘AEuɒU1VD|/g ?p(bLWc!E !_*碨`NjrW+..|E]]}ML QTl)s'vOo~u}|Ьwzu 7]i`?i.ӟbOw':g]o_cW5X*\~ر|YpV˯|3g?Ek[iks 6D,\Tp>1551:B`Ä̪ Q4"&W#+#z($D JƠaA%QVl9@rC׿^pᣏ<:9vK/moڵsWEcLD-54o,L@P&ٖ}4u|BH+:k)M\CD1IQ%ǎڶMc|E) DDP42q洖%j DQ%88&1F=l,x0+6JyCF9X%i48 DQYc*:e2֩ >XB @,M5,+K*q.)s>s% >W(͐'114bDV#(1)ƪUK})GFtι4 L}ljуŜ|=py_:]O*[+!0sQeŗ_}~mu4E8g_-?瑇_ڶ؉]-Ѳ+?|wwQeuTVH%bBhr 2Q' !1kLZK AT=1Dsc4F9gms 0 j @SF3͏>{ǖXל{7[(ΞL8QOW}2r`dyrڑ/e_zeKl4e``gŝ3[Zfz0 9dx@T4, Ϋ CƐj0jf%ӟ&6zz=O̝`_Jm}U*#}^x)R8D5KZYV4]=oywScc /Yj|bJ8/dՏvjF$,%`cHIbgϝ={[|$|/js֦5lW}qq&y/Bʱa%@Yxe=\hʞ#SjRwiR.?SO_ŗ_.wc'>YʱW|;o},\{W\yYڣܳx]w~ ?q6kxCZ֟vxyÃY=;?ov톋=>:6:~xW&2c $*.M1@qTU8'CRAB >xUl6 1)Td@M$>K.x Ź#w5眳zYOVQ]|l$fPD "**#afB/MY  F!8l:3B0D-`QTAB"EkZTu╗_qIhm-`dc&cњxE$(!\l"-yTG !Mc,zNrLFcd|& r\mq,(!DԻ H.rdihcFBhA(Xk$@2Հzb Yc2H)?oԦfX0Y1iTmdQ%l`9{ZZN;&Щ#S8[3U ":;F7| *ow'};.HH?gϡFmlU%.zs7m_ᆷEPOCl\,#~~דMceA)BDf!<2QhɲJp."0`LTБiBB"Q@&dCCգM7#L A`4L 2+{!i[wmX{_=-[Քf:֑ۻrThKl\\>$ j/]|+> s68e &ɶgt{f^ʪֈ30*GP13*# 2*!!cWi*9 1a %!B\>'"҉#Wϻm/y՟.Yݏ.e6]eJ\58ov^Z,ĘΡocdtrB._l)ϙ躷]7r Lʨ1eiLnV͟gxcx`1W^t9G?7_QcRU-CR6Y$jebŘĖh|LZUF)AȚ4m`3s Ԣj$2,A,#]s:m[{gY&AkQ K&TIC2>4i6K,N&ƻzmQY[J\p% AYjƌ[/1kReŒDj#K&wR6DĠwuuqǍ3utJY-i7w=鼍VN[_~Ky5o{gxu,Zivg߾?:ܦK.ݍF-c!qBԢ$['wu[DSQ KX\Z]UQEV@Qؒ 00#sx+*F ^ < h /)^OOOZfͺ%߻ĩ37oiGOΝ^g6yjmn41iz+|o#D\z{kd\.SGFw{Uffiچ 1 )0^M#gdmˋ_:|rb o6c9VyhuΞ[L&G [%TdggxlLk."6 o'|u۶^rɎv\:۽w?s;O Y3gF܍M7::,\t9W7o}o~e7e`zbTIJλ#Gw:Yxf#\T*m'0 :|Ҭ:0(>U F 5 B$&E 8B.*oHAZ"bʪ0{B46 F(0Jp aDLW-Zd͆ w<dŠ(2#x'hP!ƀ (@d娸 0MP3R;9;߿yg~jzj"&2 il1%C%>o!Kc@. !6,{S ddjZRͽw ML,7 Y%+z_r,{ۓfu[|zZea #pSV+e(3B^(a+$`8r[e.5rA `MRJ#'ퟹ (%BJTwђicNΘ(NgN՘]X=-n]}]3&jz;zxYsp޺-I;͝ꅩC_׼|hDHa8(1haĂk5 XkpHsjGF)xox9FP2VVaYU5F@q$Ug }+sljo7{j`:>s;z^qZaFj;jkF^8=̤۵|2쨭ݼlo?qRge* fF8sٱ/зp;"kBl*R٘jL㲍rw9l8547j]T5NJHb@1"PtBPe"(&EВA/P16%Ap w E*Ef2" tkIF]GYv#Ƈgޡ"#[rBIa :R4H!0 hEATaPUPb1 mPIz"1c} 9O :EQ+( ""h06ɅD`& DA H%w"F f$j4Mej)À`5q9҆#rNKjp%fi3$De<{ |D}3ӦAL Q͖%[HTI(Xiĝ]^~ꙧ1Y,˂ "HцNFx}g=;O]tx'#I\ݳ?t%f1˂u >{3P_~Kf=MgoZvϹ{ݡ]݇ _Y#?F^bݱ$J}sgM|~hK뛶='c`nSg9t:EWwɩ`wJ7km:UdLJȲQ?N ! y @D!2 .H_w}>}c`ƞ]vV箘sՋ_t[׽qbr35f,á-ѱ8o  F0L$%43JRʽszyGx'9>ry}%׭\bEmm5RY6>zv`pٳ#^k3?6nk``~S_=g{zێ*1`8kybD`Q< !#$f}`461`099151ӓK,Cpx2Ā"A $yZC T \-H=(@ -[Q_؟U-#Xk$IW༭=wu.(4%cQp Z$KDƨj'  @{Zɽj$UƂbBPE B``jVk`yK wKWUp"J9fyFEBSZKb+u6hb%Y j V6 .66!GqhYrlroz,3oX1s&MR)36q4/̹3%e”`5e..uQZoXR*gy3$.'qs؞_sYOAYjȔH˓J98GFQ?w¥{x[|}x-iS|^93g;xdӧgt'/fM=|50|^9xLw>Nk板›o#>7ftBge'6"Ƿ:@Cmcѫ`!1UEK D18J˲ܗ"IrD58O!8 G\*a3 ˾ 2(A| u之jC&I;ǖ;YƐ9{fղY_9d?O}E&&'#1knmѿz/ް1l O{cpЁ'͜=՚lrƵ(Pj3F k4 @A% FcAD(0Jg#d ++ j DPU ="(o牧޷Mv?΁{E[.?\y͋N?ճcC6kaKƚye#Vԉɳcg 3Qo`+k~῾pU/h'6EFE!*BPlADD#Bi&qoRՉy BO ATb F $/Uv?;_+/|nƼ~Sh$sm-.Ʀ'&$9GwpE3_ʗ}|/xc2p̎9# N׿9s:~2*%5 < >x[" XcCi nr2Zm/WA@ "(.ZB"!}HD@E H( ,(z5LA-[\^LUA<!3T!0 6RK%O^!·w۷-_k>57A9GL)0e BR J"%шY) Q# !V AQ@9N 8To5'x3Id.ۈTyb6D |dSHLh#!F6̑1N<3B!+ᤑK\() V+w*62Pz[)-RY;DǛn`TdH T@ ($sO@qLdD[Y2 D`DŧPf eM@@D>[ R9!j |&)>|lΜ_o~O?AEC'%!OzJPo>[=~̌=3.>yt{}TG>zjG7hb8R =QQQUD%j $"PT`C^4zbc pqO$€ QYTkS<3wWmU;v%X oxșSHq=6NW\^oVYfZm夭\ɼ_l-Ԙl2HjMPmX"wYs)25FNV#1*CT >w8NJ6mb{{fvtt>p㿿~{vϘ5o-#~-yǞ};١]xIR;o|ї():;ZVww{%)O՛\sc'^}߷\|ꕫGpdla!xAd"b'RGY̳(KjիS!B[FD11rZY!1j$9bbEH bBPEb> lKq)˥(N(H,O,(EY>;wŽjޘf˄"P s9}_:ן/?߻v3f;y`xhy^GSc^[~ /]nESϵa2=uz-K\Νԩ#Ju``E_:7lѢ.lղ5۰lKx+_+NMw 0)DŽ JpR@ /DVxH%pL,ܩx Ll8DHb.֐) H062Dgn֜3#'[tɂȩѡھ􍯼+?o7.~U>151|mfDhh^#0!K.O;{r}.L,Y4k56X6yIꃂzBB^eWEf)*3zB"ȁ2i @DT=2 A aP2XQpī_e.]zݚ3-[j o|a֨;/3%(:j 58"v\8hko5}͛l};6rfLdTCUUQT2 H \<1.yeA0&h幍,#sUB@Q/ |#cJWwWWWg[m{n>ozK-]>}Kpufb!7qgOwEzO9'kL5FGǯyf<_GOLկoE^v|thIP*LĢdlRVyGư(RD 1HTw|Y'q+Z"C^dfc(d.ckXAETHISqFQ$h(:*`w$R[#Q/.]!xa&hilo"tɧgw=^fSaU'QqD* (LLZ B UPAEUՋ'eT@QBUURR bI5(QjUʡ^x l$i.d#1e5;pl2ر43Y'@ r" "Z液If+E/YQkRRf#T5z/HF1ؐP4kD c &D-܁H F60=$̓x2B>oX>spΙgF1=6#Nx_7ݛ~g}# o*wٸZt{%$#/}ٵm]{;~e}9ujޜ.'QgW}GxGOM'[8M֑$C>EB #JvsOJs ZYʨ3"O0!M p2Y`$(+@!@(^TC(ZGّc= ]zֹ;5W}3mS%+W NMO3"rE21 A5@f"PȒh SP,P(QD (hgAdΜYO~'v]z}}F5cpVW{+V$Cqe 0l"l8؀Z7&,X{xᡳ& H`B=wx*('ab-LhlL⼏XJs6&"|]oo߬sj#~ۏ/߸Gjuo|g?}#W]}@ O~/ח_f9kƆ9׮[{ .&Tֿ<+/~ౣ'N;KfI3OlX <Ȭ%fwt^8q;KDwBLQΝYmo=5\b&UL>B1!T*UJ[CEG ڳn j^ HhXQKG"PvArb-7 b(޵̮ۨhW {|tʙW[\vi Uŋ18H\CP $Po4 5*(.hBLȈ-1#1IpTEb!0s(o<|R>夊dRuHY L7Sa!D,>ZY&ccLD+'BPQ@%R@L'c,lȰĕ8/P@ سM1 f'4$Uf]FHm"f<,D\d a@"@T*#b,w1 Ω >k @ mN_`%sQ)JQZzϑ} }ej kԑ/ڶVjjʹLOOR)Uwٰi;z7_c+nj[7Z̢>jtwML}umtbf^lm>9oyZ3!(b*֊prD\,Xc9 L Ҝ8*H!6"A"G41! B@=bQoժ .myzh[|U?7ׯ{婡QpӱD&d)!-#w񾳧ްn%KlZayS4kSSc " ]}Z'fs{yQU& `T`#A@->0CA sG?2% \!"{N`T2}]cpy-gώ~tt$qmT;:lT x4ʚ6WXq۟~?ktIH-;yUsA0PUQѢϧ`" =1\4ԁ"*"" \)Ϟ;L??_];#z? _O}?2vz>ݘ:}|׮[/3'wgN-\p5{늕[:;z;O{C~՗os%U >31VRܳwяܸMFAf (BPo=w|AC+܍o~|ƎZm>%T.[k :8wڨS߽k??#=t5]p̰,}lP1iO6BŠ[U9)l4P0cPUs9sp|GG?~?vp9U.v\G/^lYʲ D5kUTh "JhV DBy $ ^1p ȊLо3(@b5FTo'>u\0وZ[ LdI!h(kN5,Z.EЫFZf="882AĴW#qOsBjɢ0lJQ35&Rj5JCzoqD#NS2Bb]KqrWJ*ޅRkL< DR@.9AȜsN琥 &4Ԍ}˖-2ٚJJ5" 歳+.7~jGGtRik4j_ykƳ# ^A6upͨ(VBD/TB!oV(S@ :#& Pl_{»V4x k" BH;w~ї_"Hdعll,Ӷ^58DD!LCJ֮\Oo?LML֛FV šx/*\{-ň& WYAB\qlDY37>ߟ7|ko|?nJ Zir}Fl+mm}= 7|>o7xwCG9[k9s\}Б*]W wߑ'O>zO7ҹs>y8nNM]K/ ?۱㒤R:!)H$jɣZB*z0hT+JOoϾ??L{/:ui~PT MJ;=O<oM'N9r/h n-]ruD&R(""* hLbFQB(nEwF=r__/җwO*E4{ǎiVgy+gU c* "1;Ѡ($hؠ ă@q Q @(PaR J@EVU2keIR[gkQ ޠ2%" .aP.К(f 'Nv_z{۶mdj<~=7~swuojer_ZY`": CB=%`C y9"BS@^:g *AAdH}n8"`1(AР'LCK|He@FsT 1*6R@V|s/duRmzd9'1)Y&N#c%h->s .822*^DKq@[/lDIᎿ>p۝77eS'1C"&,(EB@|n E @PDPA @z{Aو!kg zv~f歃Ld.hzz@>kJK|7;:{/X AU) ]B ( v H ư ViDZm`` &z~{og?闿O8rUW|pݵ׶G~zoq`c?ŏ艡M_Y6&&Z]ɏ,-?wY3']s5ާ͑#g<[ϫU1֝m/I?vo+/[v$.1qre.@(!D#1EsfERk ko۞73/JHP dy;:tw?#{-׺,[5t|{:6^766VrZ蘉еZBL HDdH" 0 "YbFDY3g=Tio^PnQU۟ݳБu6>kdeƭ,'(2 # J h}D@LAzEO G@1Kʬ:_Yw8uArHnj\0&r5 f.< XCղȰ"ɓƆ)rH0r`49f Bڜnlcy6޳y,gV丌}TPӳSt=Dđ I %XE4 \+ $6[♳.Z52!F!Dj8?prF]hϾic3Y>ֆMK^tßY~ݛubldc{gC-mݴb˟=}ׯO>Un>|SG]`cG5w?ڗ?؉r}3w9s,_14DAwN(&l2lkd ]+TP A."*AX0 s$4gFҎXMJ% JP|FTW`q<(iά@gΘjvՏ>OJĭs(6SBdf.FE%H jbUBPTE8h bv,A  *Jd c < FQP@DL%ոVJ껟̓O?S/x\{_+{Y@wO6o܉Ѣ{}j-Z>=9JeDb@WG/Yv٭~s,"'NXfիnm}Y ZbuVqgmՃ3W^U^ڿҗ{:- T#&(j@ \P3g'lbÏ }򱝛6@N:ʢ_'qmV?ildծoccZj9rh'\#0[!)<* 0xP5HDʌAC@d)˲jR{Ԝ<~ݫ^=5{_Ξ NpdgWǩ#~|I9β\EJD)vB^@D%PC Ph*AB@CTQ0QlBe/[hɡS=pY̫ ˉhp4OЩj"(/ ͔|q$ `7[U<iBZ%!ǹ!ʀć#XS!H[J6iO4JLbb (h Zd^h`$] Ֆ@ 5ɱ]xζMr&Y#ó˖-w\ߟ>1V/=}2ox/O߻OlS'J Ld&&+}Cy[|Zyrj{}r-ޏ~#_߸xWy˂9 mjpiNj%Ktwv1D 0(BE*^qP6TZRVsPU"iKK\2ENrc" cf,m]ȐXJFV}(0[ eiFѳq|r đ 9l22dBG{M[A]PES7gX;ж4ٔ~Q'ɱ39% !X G"%Ԉ(`F'>FHl@!{\_E EQ4.'=]op E) `M1 RtuϚ151ӟ|c >Ϯ]y !hFBYUQE 1FD!Rd8NT{z{{}˟}Ͽo˟?g+|-,JG~eWDozިjoϷe=k5wtؙ;K{K 9G>y?5=ù9w6[yM]f+7YRfkrrrήށwCC_0o>E5dA(*%__9~{fK_ھ/}K6߰nSszJxk FUň?w|񡳯xŋ];<=|ᩴnc9cyqtu2ہ$h5"<ŀ 3r "3 H@^ErWg#???;ujW;ƧFg\nбSJ-%4 KS TPpaIU@&4d(dH@T(0% ( (dsPPH<y$'Ozh}}r!QIL&"Ƙj`A&QӐNLV,8I*l5`#08*fĶT, RM,+3ZEQFDրqc|V\5ʽbW%-qPE lU&PDP;^sc$/_xάSu5㈂wYjs2[wh'mk7|uYyǻ}׫^u;O+(ID6[v}a_888{ͻ`[oЮ#N Ge]q]Ϝ?x뜅sk^kU/6JN޵zCC$ ReP-f3xa(1jA YMo5 @L3^cl$k, q%B |+%b"Dk6*@*4V((d" 9  *h8o5Z.hLR8.hk+q Bn|0%;#!G&q*@p!W  / JUx;{Q5hxf֛~ۮgv;g֭[?8hKf3T3&-wvVm=]nEx{z_|1)ʢ•E)/GA) 2E%t>h\.,IY77|?o~g\w~cl199~vQ;FGxv#O}{@V( ηG۵G?}6r7oNJR<=2j *hFT8"UPf sg#䮽䬅7ݻs42N4z}R %{fhbr[ y*I !%B"B RIZĄʚkEF <;UT"4a ;" <xܪz%}w2Ʊ 'J؇܋\.jV`MT: 7ldb&N` #K%SaFNsgKq ι,g9ZZ-ƨZ!qI`1Q,!sՒFRhI6}S&6kjA +1+5#1*hLd-|*ޣqMN.Xu(2 ^(Sʳ}ORh;W6:4b(5hE l$&rb3c֬R|*g:}?~vMs׿kIijb2)Ό˜>>] 0ɲ93[<6S{Z'ַ|\eo~+O8Uf0{EVTk]5ZSciA\+-_8/qf  * P(v\&1;wಫ_K</s^>gƬ3#9)Bs_w7b{ɦ?͍S=D.ڢVޘ={6Oώ=45_ꉧOo3L]++PPPQpBNHjRr"&Fƞ|T[CjY-Y<ev;p3V\63]Q^<0DdRP*xHDAŃd@T01 Au,Y@lILL*z- jW{{>>mR%D $l*a0hBؒX |V3c$ ɛS(*%r7$V &kԵr|P>6%r">dAr3R@1J86r6\c)b#c(&̱\J<2 lLNuV[QniML C@k u[m{[G#g[K֩;CGN$5s&%%[o?e< _]g>/o/%^u=̬|َ+߼[?ӼyKqZkD6~̽wݷ{k׭L[0ā9%QbQ@rOUb `"b RP5 !(h=+,!  Q*j*!PKPѠh0Zc`cZM )ض(V0}@$DP&mbiX|ݵZ0AVrH0r!1SuyYnkDg58b$TAZQA$"&Q`ca_~&5lAţw$`PȖW/$jsDE?zV[s3O~/|}Eyŵ/mL; d#g]Y9^u+-[/&'B2d = J]mZMbۨ7*Iq٤T~?>KDW5ǮJ{٣yᅎg-_sK謎Kmn~{`}ϻZoں~f̝1pi7{c{.w$~y ֗}w8歗6F}j9wLNսwlAʚiT|<]P񠚇&6[vu]l{˱3wvR>q`hvvĕw`!ct&^>t("ry["ʽ#҅ ot޳SVDRaYdЃ8Q`bk<}M9O f/Z0:Skot[.f)6rrD|Țb ZDel`& 7uâSkEP@1 (clɰJ a( *I0LϜ9kǶOzsVX=eRRʛfڈ U6 8zl˾hcPUf#͖q#cﴕHM(R' A=W"R(\Rj=[ēNps[{6^o壆;}w[HzzJb<20֨W;ۑI0+!f'DX "@-Q1rZ%92~{߯A0[ "Z;El6[##gj+Wڗ]{͋==ao_&Z ?־'wLԟ~sVկy[~U/^H E ƨ [D*y赳}Ϊw|߇N<995608+^pLSkE%4g/\/^0oic*ˌduy]lFZ1r{De^m+G׭ݸy١3$A@ɰ%A hpQotv:(I*}75V,YҪX#Xd@UP\:uYvs}V,YPKx;70nݶm%˷o;ޘ9GdLIE##$"@J$> YƋ9ZVyjڦZ ,iEll`*#0N%٨T q;O Y`[(-J6#c-2"$t.<:ϪYF7FU|YVZ-g601>6ko⥆B)A1n'\'gϜC/<6txX5aFXtGWym7f9ݵrߣ?\{ptTOm/59|\v|׮g-\7\^~Em`F)nt1h(ZDsc* @XuZh$@ 1eDMѲd!8QR2< #(qAUIȳM,q$HL*(YƒjdBP0Z!dk `ͩY)阬ן߷#OGݳ]Жf-FhKIl>=6v4x-X13k4;; /=\ftxJT+O7c/뭣G4Ύ/G7^r5kihEH|  2&"+`$WmoQޝwثv\NC7<6ҒL'_zɥW:`6d,1A !#1ŎZ 2 M lG`UlAO"VDDYȭ!!5Yoh>urZUP*dq͐F֊XL>s6B# K@T) & [l6\ 6Xc$\1qlKU\؅J*3Y4f$%|jئ !(b6jc}#}.>!tJ5tk@RdTe|l*f^|q촙k#rHh #*;{w'vs|_>pq:96>1tzbxdpn_[GS?٢3kǎv5ΎU*z w>;giki¹^tEp]~lff?[{֜f pHc{g-zP:rysؘ5V AI"CNO˥0@lL$-'u2'.k5DJRL 2#02)# A @P"CA{PD-8=.fdf"jPȅTP8UrD>u:jNO7 kԛ8Y0{N>U ͍鎾?39qǥmٺdҡSClb⢅d]3ηwuT}ꖻJkv51ٚkU?srtb֋/vq[5w~|| CظnmGo޷'.ܾm༱![ `7# "EΉ,p+ٳy۶mjF}gѼ9 Vesr4&DS8"T]3,dūJ&8#(J}Ld ^0Q6CK*+-HhoN硶JR- b aR R2ʝeF62(y@6"g⤕f zH3gQ'+ӻ\ĵ:ykg9kwEE9A֙FIڨ[4DKsUbV4`q)bS$y `Q)YaԔ&qiII'Ys{W^✦ ) r1r|ՀSQYbmlpÞ'}v{fҹS"A|[GwGv=r\Q^}Tڒ`ٟne{s͕oO5+]˯}pւF={rtkFMmg`x3* :U%E(J &2!a`O  `< .GF"@ 0Θ("/"0D,!"DTp"rdsH |ٳ^@\Sޒ9ztzdfuBQJ464Lو6kVGV9rFondJyAH((wbDQ $@EI @,*@":湬F{;@Xg6#<(")!qȢ!H8{vT\ޜlLMLd޻R睧($cY|'qyy[*%I{m``Ooos{oo׿oqC',Y5ozSDiZ$IF{rP;{O_׾~K{1vjx~+wםW8^pg~'Ԣ6@""DD@^2)=3jm/1ufؑqwז+<6n[fŜT}Q'!k歩)?:IAs:BQIdAU$!-  B A@@DE A{o -G\D}Z{5T~ VJ^U-GQRTn@`V?r&y'jπ-"x0ʈ@[J0@Y6!t2yKUɋhLJ 6kdyN5weÄ!mdXLs[(ZU(2Qh E h y E&VN:j.pR.Z MXxYT2Q U uztT=~m[B$]ቆVS"UJzRH^[#{D->ij#B L]0h3 !UmlFc7E%q1v۲gnc*5D{Gݙ\mV_VDV*k=V1gv[#xBa=b*Pb1R" @hP F"RPT HC L(,7!14qB@H`3%J68beL96`ؔbTZŰ}~=w Yj^uK_ _/><30 y;F=T*I)DG11otǼesos9f"fY$$(!G bӚLL"$&͝qTM| [4@s 1"bdF#\"V @ʶ"b,╌H]0z'58g/Xzz2Ζ;"iyq8JG{\󖋺;Fؒ  Y@'& өcKśf/P.3xE eDٲ!3$$ (#9]8gUPF--2HŠ*x!DM% !pd+!q xӌ"vvjZ3p{{jgΞ%;s7 lúu}}[;n3+6;qT*=]x+^3>زү|@R[׾dyٷK&pk.nn]?s\~oXTv=W?û&[.8쳖.]ySW=Оɲ%txxhՊDn\1ܹ515:lfPe iWBb@ @Q iJPDD&@$*LI1 ։w3=}TAb`LTۋ,ݱn;x}v6^s^y)'tl5[-7K^Ww=Oǟ}I];F.z;#[zұ#SfJ @$3}| TLEM)%aG΢*3$LhT ;+*jDb9Vw~ۿ8ӗ-^~NM9) p|طo~xg\p᫊^gѥKhlh3G:PY7?SJlL5]o}랛ok>YRC "DT@H%}qhrL4IwNV7Ρgd."hY()!$qю <:\]7|k?;0jƬ3;=Թn[5\V̧Ҋ#kV~7gO޹fͲz_tZXrqxdQ, Bz|lxh`} yP30@d5Q$C05e$0CE0dJ$T8 6crT30bNNf~HbFhpi麱8 V;Cbkh!v)*s q092 * ֯9d|̑cfЋ7Wsw=ܶNV%;E 0hq>ldzooj~EWUE e2DhB@DȞTS2xjh=v&)P]iA)kLj$cf&"%@4EDV,ת2ʒ$cr|8q?/~{+VwsXp>U591|uow9 /=sg9tIDATu?_{!1k9mvG>ǭm'V;ҙ<>s/T^hx8қ[dɆumqeՋڷkelwL6Ɔ^Ŵ l EbHhQK1F\pA@!s$0f\ΉD Th2""Ed6j6Otݦ㤈ǖܱSO?m'?75Q+W8Ǟ|j)Cm=t{ڵKe![6/@sz,ã#]s}w駞>4Y ip[5dUynȤU)"@5Cª(UM`O%ijz{˛TDf.)$ٹS{b.cJ0F%K5وSe`5!%&uW3V0R: 8)QHI BJ}o|Պv{PXvp?wZ5<2F^xѹg=44[n~uٷ@+֮ʿʊb`q{9l,g?|ŵK-[:=7[y yuϸ_Z(&)u΃b Q18cM2'ef#8r.J15fZoU sdPwRe)x$P'NDwj*l)11@A) 8:U ZdE\7k4:~(%@sWvg$R+S7v*4b*R '@PΝTUUPtI*ϳv *@ًЏ280jVvpnYܑV}BH5JɢT&*1qcH9cesQl7* K,*Ean=g{h㱛+-q YF$jfË>'yf|ɗ@pڊcοaP*l6nhwS' ν [Fe0p`#Pۖz,L LU#@8JFqmbA X^$F ༖1pN0S'e"&2~?5B 9#Vv")DCb܈HeV)B@{!虫l"}W1ƢrLRDТ~P!P UQZ>xk^skN r[%qL@L I-W4U@0;~J`TU U {z+J*2*F$"*[2:v!h'?[rK.?+.sdg>s~cxllUYKяSO:8lxNoz+V >xڙo}w.(CS竷lz;sh IT,3RUI^ٟ_^g`d͔1&2 T)%K^a}mw  3aMTLЀT L`$j-uODMA ,GT1Va2STgY#;2=126w55gw|#LБz{?48Cկ~]{ի33=b\lY+>pwgw)|G晙#@2~k֯{Ƿm{ݰaCYI"C=29(59 kkQ|k, }#oL윪:Jb%5S#DE_A9j""F? a 'PHj"hj*պOKQ`vT*cO>qxj*wgNTAjgiwFS s C,eTl4s*M-+΅<)Լeg bY%fF5Q_Ly+90r% Z@Lj#MP1$H"8LfG B=##a FI<8,]ҟO[{| :n핧z[̎/'>ѝW\d!icNݰ]w+^{7g޵sYj,s`dXXT5д~32CyMRd"`3cBFB${Tc3&os{/bT7 sΒ!1J/qRn4ZRTńCKQ#dT ]hTr~v2Rl3Α6jYf:Sg0mw:pC2~ۙt)Vy 8* p "JAC*F 0{nDJf@v͌bd tfA7 %KÃSsGC?w}_g:9'xgMLg>u?:g ]/?-"5v~ܷvoEO:y*勯|Cw~駝g΁vs9_nܲ~|ҥ+C̽.f3{30ʠ\M*6q%5bU9#5bb l@@*ȠDk zOUcBcvy xdI=Kl; _|]Y/=R: #cDմtٲ_-p[O9!eK|{3s3O >.4OP*M=@ STILPްb݆T4ZE@sem=xd.jqm=c?W^'`rP##GcUT`{IN;S;w>r79Kb>Y@SD$U3@nSy&Xb4F2e &\JA3LvTʓq0<*r1( )#I$%D"D*"D'3mnj;WKeƲeK}gy\TC 4 P:b!09bј#!C@|WOnnݓ/n6Gw{"#V٥+Vw֙C?-FG{ܧ_a k??{lxw?wzmo~#O>N2<;)={&Xϳj6|RQ@0tXսn&BS@5WN#0GQfˆfA4%# "B̵KU,wĬ)qbp$h) sY*#!1yT2UEpdYiߥOF +$ctH{a)6(B,#/Ts !$\j)xYKo=a%##+׭ݰ]ҺOs\ N^2BBݮD(ȢD"**DcCKu{ E׎oᨂN(C450f:88fv{p/;?WߺW_[?6yog/{鹐e;o~3/8<ݿ'}eNW^w?[n|l|vg;> ~anm-_bYV=ɿL"yfr,Vu '.Z|xt|Kΐ?yoszػ{񫮾zݚc\w~^ߘ82|aݻCV< O?֭g/:UQ,m6.]__V_re&Z;hHHST@2EՒAJba875 KɉCf,+Z%`G qء$CHu *0ըG.7D_iUM@WtdJG/f! }:ӍϽ32h@DIؙJjSe'Rƪ/޹!Eo#0P$uBWh4ARD(uVFY#3؋U:{^,Aͻ 2ʈ[/{}RdV" M!T!4 F.X, (:48͋}DueNbZ=vǞ{ׁ52^q~l~ncE1֑`'y69EQ5WTBQa"CLG#1(2)*J"4cBd&ւ8Ṉb" cUzG#b@DTŹ@^hD⢌FB!X"DDk+c@<*l/Rbi2^jc=}k5H+׬h ֢lg;ݹ'z|ںѳ.;gKbY}dbOXj>T gDU4@}If#0ɬX-8c0(ʀ`CT&%`@311[4>:;{6;?z :xE933[:1IY˗~>|n~Lwߞ:ygo{/ݻgϣo[fc{{ﻷn:={)f(V,3o_R4D! C_͔}e۟єJ.PDj"ʈT_{ @3Dbgn+L&'~aC^: 1f\BR0 аV*3@{>eP>k9pNJU 8&6B&bJi@3U! ``{׮/aebIJ1ܷwl|-k9oۉS+WE SY.[>f`&̀  UDD, ě"Au6A"TɐQ S(*@r(Vf33R$$PI{栌(#[Ne?MdbS, E"!B25#`TDϒrabi.RGjS1N"AL^kM:seʛ'l:řqhWȪe =>q뮜{Cv`.8M*j2 bʄh"#S $s &d"#* <"J$( *g<9H0˳<4<J"$q̙gQ(?}{},̯X;͛8lvYk6μ߻+V{y~߼ẟ|{pΕyֽ;wh{?{|:sƕkA|Aٛ}~Ö}MOz5/ڭF;rFAWtfۃy{hQ* HfF(Ud!`&B ?808{phu7?r]cV_}-^{o{?~W,]5,z]aI27\$jSD`$-H H {WH߳sthT_~i%K|G}%U5s$)auܳ÷r^{C{p@ TJ!MllΧo9ɢ@D% $j>#RpBƢ ffQRbB3e@URI , M0 &ʲ>XF#G@D3!fuTЌ+C_&1PB4SMD\L)TJƀJ>$ARuUI0 A"h$H*ƭ[O-U/O-ƙnWSbzW`=y^U~ C`%+lZon{FO_P Ebݺhld8nO$rs0.+TK ! h(<RU9TREsG .e)*P87[bWJDđ~3.4*$5Y^h  2;?{Az#щCV{ γ I80S~GAQ04\VPC2pf^ ~G4 6$3B"GRSLF1aL*c3Ԉ1khqFy SCQaOcUWAu0I"FEI)VXgf: k7 MS`kGT$‘7%k c=^n݊-Խ,:2S!)R* ΃aMG!jJ/HjFPMBbʪj&%s 7[M(G53-D,JZb_ٓ{:ٹ^w~٭\ugN;l}Ow\{򪋠YvKO8=;7NNA|n9 ̯z 뷬X·OxE;~lxtrtnc^D,%ˢGcC6<>26zx*%Kl{Wͷկ}ҥN= QQHRJ MG5U31#`6U-38{ϥ\П[thQ1&J̻w;vf矟ziϿ6{p4vfNغOzwؾz͚u/sW]wzw+/JLc뮵ko>nl굫6Cyu!  Pc @*HLa= M@P1 OiJZ'pB~{8P;FʤP 01Bl*I-%ZOjFir˪h֭?ө*-%lqR˲*eڃZIJ*UUfY=cU !͹μe<Ĝtѯ$eZT$UEeAdNA310kLIѳa 9A1,HY6`ehr#eäL^q[NLyeJ ;2BSYț- -tG&fiHLTĬ'S;N U#wNME1Qhd¢$9&$bg & 8$5)9 TG$ KF@ ϳB$+5!ϗү!o*c (} р4ckn=)HKH!S]L]?U&{꺯95hrb T$@4~eWcG[߷wUORҔ*c(cFHeQ!9@LɌ %4S5Qm4USǎ4*IBҘT3!2@1~#CCK7Fٲի~Pzgw5+W~w9׬^ VnG|Υ+6mټ, _?YkM'iǷ=3hΗ^k~~WUWm9i7i5' ,j5Ǭ?8ƙ#y˪HĪEceFIF%$$SHLEy9{r?_a|{^|qQ^|扅#G.ɉ#HIPP fX90@TVT}wqZəD"j*57W{g}_¶O>)n DheY9f_7_g/Y3<ģï 1;a{)_O} g߳;k6Vj _Ohz{gfbAr@`#:GHbXQ"` Ff j0Sqv:hL(1%5`&R0U`P;$RtI!9!HA"y ~l=m[2}Z Ԛm5z;J`ye,Sk,Ϥxb"D;CZQ8vI0+U#hŎ繋4D=ҍ*ɋ9r.xs^HdV+VUQh\5rX;BVR,%70t[!X>DժJTb䒥O<*2H`Ή~YEE%&dpbnzs[lX!8$SC"GΡ*Tz `PU5}#C#C#C#yX ya00.D0@HVC"$BfbZQ{2ef@.A=XPaUE\撐 JU q=x&GS,KkfI$3"1 VU,8B&D4bl"  \8ͪ$b(z}L@`F֘ʣu4bJ" !!bbUEF(xGhJ#)T3cd@D fn՚U^x'|_m\G?XݛEe?v 'zʡ?W0gͧpON2cl4_}Չ/6l\4~dzm|qϋ{/ OpMOYϾC=j_B:V'OvBf'qh f Dr+DE$S!*H*d(m}鳷=tߨ׽ +ON䅩O>wjկW\137c-_o];w uF`t?u׼N_OYʪ[@v1jTC04v f2@$@ɣYe(@} F IJrb&R` $s`F* DR2Eqĵ!(I$FC&b&@y3C#(`1Se`ggSO<Q9d <IKc0s*$@Ǝ\L\4 fu-P!H0[2Ԧg:h!@,#AZe* SbhBH,G1cUhR7toe:_@)j!" f1_1 )V9RidYWw6ݮj(>dӍJRa` X9WU+%*갌~%nl`@ **QT &I$VDybl#0GJ@)NN3APPP!|Ю#E=#Da~KHjF`*;C5Ϧ ƃ?-?S^~٪e+NɉV#hd믿ŧ~o{iF BNz=<6SNAIg~©s5.څgzn̔џ|hvfw>0TjnnA-I3[a+"p.U֖&t!CJ(G2%sj c}Ǘ/[jDt m=x#=:SB06xfvJ{ov +W1hђ#U:B@ CS$dcwaa%y7}{.:9 F` yn07]uO: ^YwaGC comWXr^=/)Nm9~݁V]};zkCwj_._35g)dƌ Q 3B@BLXI}T);a4_أ y$Hb&D L5i-cCI-(cHuR`Eh+2*2L)z"D._=Xh6L4r3T2xD( HEG!Xs*gY8eYFchpdhd7w'|_4?-B,s\Ed2A`VhFFFDHhHP3ԺT$`̦{#!P:"292#B>{tY.*s[MIp,R\kX䱾:5 PUSadh;O(Fׯ2B" 10Y}1KFM!<(0#1Cc4B%CQS0u\ 5Jܴ^53GLOvνgxk? oWǖM7OqIz?M>vqG&F"`rIbY <ߙ[XS,o4PA H)$A:r?اccִW uk:믿qӦMC#NwlbbG{ǝr [1{ + +"!ELcG5..b-gY;ܽsE^n݆ɩ< *1!$WΧ_4O1K^7LZ0ށ-/@"0Hlk jԪtWf~Sa(eQ^537dtw^xyirg{΃Ѓ-t@-_v/n䳿7>3#]rGfo{=OtzvOkᙼ9tūj/=4V\}u7{ҕyIdL)>t@+uΧ\V),V{jkYj-} %DTdC QUYf(U/f:w._c724qn{t;3:/y^gݛ:g^Ku{x饢Ӣ(r}ʊ >#gƘQ{pp[~Sjf۟_@ !ס(@9CfV@42 9GιB"CQ(9@KQ aQR jT  AUJ {_!BSVID\vjzJ&KB㣱E4!v*F PD$L"FfLQIMj u/>w;{#;sɅwտ>'xg@2( ;>jR=eoz͵_/v4f0k ;|xwW]yǷxh ˏYqBEܿw`߯LE E*J-YF}UU8XQ* ,"XdPUUɹk94?`x?ͅ4qZǬ܉'l7r~~˺}샓Ӟϳ9 v}DtD Xsj@ Y 40  TbdhTMLp/;\742RNqĆ1Uo`1f@Mɺ~˿-[6u9Nh{ן|VԾ}NE7nҕw߰oS{Xәݾw^~3NkZu \f t{`)A`&N1&3ȉD$"!!֍ghJHB&.09GL l8ׅ~VGwD2%"LH(-FI4EMTA jq X&t켪 yIE-AtW%bUӔ3SM @-3R$P7t93vMD;2,(!BL ;zt;^XX(7u7ϝosͩNcA^7};/}vL=O;VȦv>}׎3:=9L7l)9,zs,2-Y976>N.k[)OOnYy╗_o?3CCEJ)ŪsεW-?<9w>6[:=kpx=w/O|<TXD5*rMA,@c0cNb(bhYQB0#.KzKdZց%$vJILD z, `S&#`IԈ1)Q0S}H4%)8,8kͦs۞G[#Zle?o4[Cݹi3 ҒbLAI}@tyfʔJəGy *4(0z3rHD);ٶo6"I؛XBtDhh,VDm FA*b 9\( Ib,ejUӱ'"#!1/L!ˢDT=ĄbMtG`v.e>kOLm{3?bfv2(*oPѳf.u'=SO _q oYgW,~vaO>c /Su79zݢxْGWl4*_$R)V(v4ٵ}<"cf!>D#J=9Bh:4s[U[R yY$VȪʌ0#'` ]p whb9r$I=yaO$U U Āctg$  TPg~b3}*x/~oc?IlB1l_~]{Ͻܹm۰jŞsG%+Z ]it쓟ב  3,;3sʲJ)Tef:v`s3y9| Z""H2,B,o/.̅<_bŽyb_9 %;v6<8PvӓG!"ڲ77oaldLT,b2qMA PJ9FF %D6J*\jf-r, u t=419Szn'Gj 갚*@pDmx=wWoqSN~_}^fޜCqhjpd>?}{xnjC#C)&T.A%up9bV51C BʂT9UE!D>Gj@h*MAfs yI!E&N*18b:lcȣ|Y w|gG|3|W C=Bû T$0ͳ2 Y@d !dA!@+zs]4̇rUF8s@SpNsBV˜R2ĦP}/U )jFypț@24~lIQԐ56|HV~ϤB~)@IdD7a[*iGmL&I^=RTFl?~ 5_׾O31`eE<$bUcS$XDkâcKm:::Z|Y͏{{–Ɛ&!@0eTTIT լrG+PV@cBX1BJ)#*HGG%!;C(u#^?r㒕'.>Ƽ{|.Ç=CCyE̙~ /?3{>ڷOk,v}6_pYo|>n^taj.ڃXt:)VE+Gv[ɤ$%ӢJ(,%Kj0(eDv{I >#zp՚Q?ɪN=+n$W/[/|3dR* ewaCn윪**:,p@) ce'" գ AV B9مfFАELy077\vg535=>%I4$ VOƀW[]bՆ-ǃɇ۽wwxVv )YڴG^~,Vx2POKz2 5c$5Ku}RP#\`X4J23(Q Ȉ(`B+o6y[#cCRf!!YJBVy9 E>d<F݅Nn6|02Ҝ~~Kה@BSQJ*9@*cbɐ52$R1 bEH.j?;~/=]n^qL.:m9u>V6O:[n^j\wx̉_$ԢI" bjF5(TPy@"bQ ꀌ'rDjPw P"*9hxl<_|`{cطcŭ`qLcc#dŪF=h9<3s<}=}OCr* 9$A"s&@LHz*%Խ F(8ϵvU,C$2bF )*hTڢH~ 1)*gFu"hr& |qϝG3/{so=}շnxwz VC yӦ}?Z|jNj?wx}#%_xrr>񿙝8T<(;睲τ0C.w*ZYl*IL>ѲQUEUBr劤.z|dbNj7oyIQWc3sc7̊%DT4E3Rhw7~Η|/MoWn>O9;!5Bj?'/KҲK~[?yx=~:2=导;~8ث5w`ev1  1TW` Hf[:4dƈ"VBft4 bhl0"P3""T)y3R䃡*A}4SG\cR5`@7,4hM?T%mXV (lCS,JUԣ/3n'Q|#-ӔJTjU=4gsSni oFS \K!LMv@,w~U%Dz@2WV0Ue2@KJ 8*URMsFhUbpgXUJE6@@dΉT,ٵڃ!3ko`\_X|9?e9;K^#/=3zr=ҥ]wʏuǍy%G&l{.{|LY+sJP# 8P3$z; >3RQ!$DLe* !%2 y Je-ʳx?>_׿vUOLN 6[/g.T᱑[OKcϳ3-, *j 3@`j$!):%Dĺn^Ƞλ$F##$Q0&6%PaCώ,*- Q22"3PUQG?ܽ{{c4첳m\*f/ykv|G??z\JeBYx=q=m\xgzqg̗ '|ֲŭ|68h:<18:zxz\^o%UV4Lh 6Z CFZ1xqg?3;ieKWthGO~tK.YݯOW\E7>5O֪cuMX]ZVgzԀD9V9gH,&3Fڧhh=IrC $"V%FnFǙȅ_tFg߲8WeCv\f!1XKP_ѷ>_|Igm7׾#KcȢݷY㪫vz+o|[oLЛڳkיoY[YtkLQjz\DSQI AA%0OΤ"#,2tlf jIr2h*cFd:&L!*9GA5d@BUS2fa ț.C f$>E \%a<8Mobς },JTBbPjF B#>y*o+MKy>^?}~~ç{сvsێ_r]Tǜ)0:&2!5$&U6bfVLj;c4o` ¨.sf68ZfM5Ǿ߿o}zz,]2|fi=ϾtωuұW^ݳk- / q3@"Ȅll& @FDOQ<9`D ʈꮖi_DH@fd-0CdBq_` *!1(LNX^|O=o/Z߹sѧ_s:_׿UVwggb4<47}Ǘoش .hͶ_yKs;\11j'y=ѐ7fH\jR/WCCY>vCW۷VXٙ嗿#ɊeKKy'V/ʯMO e1nO'z㚭[BnlhH:E PLf""U3OF Dt.EԱI4H*eYȂ#Nq3-@4p4KSo~iqeW f ZLF3_iþgӿ.ΗEE7 glAP~9<`M\pԩEK9Wo{󵝙_ݧN9yدZpκ׽^~ ֬ jĬo$):2 L 9JYSDq9SČw/(2E Ȝ"IVSR@b33Qr k!!``D A HthȱOyNf$fȲ<}>::…itE yq(%1!jŲ@(aUESs-TeQOIRӌe9j74//z,ّҥ7$M*RDʢ(ޛZ_+TNw^>y\9W{a~]h4mX~ÂD wfj ru:L ŀ"suhN DҼSgXg|[Z_G}̙ŋWT+Owu Xyٿ<­ܷSw3 AE 4!8Π"z2pf8ZFs0!2y&22CpdR "$Q9&FUFN)ȳSTwL\~sl?K5=/ݲ~g?_V3̵Rɉ㧮[<'O.=?ZCC4#OK7n3ub3 0g R`fV)fG,Oy$N?yųSπz_r<}fꣿof!e^@''&G|Ox#N\xM#>uNM(;ǵUS#H$ "!s1iT }h$BGB"E'엊63@,D$ Z}W_vG!19(`Ռ(ЭDvm?5_~ï/2C0@ucdЄ[I/:vK_/b]dlzjjщxCTK3HF2X-|MH+ Ud j}  1!y@,E H2PUNUc44"3huuȔ920 ̦FjBRq 45vD.IOfԒ1U=u:'NO@>>p0[EEa&C.$+DB G@#[͖Z@{` Ī J*|4De1jɫhӯ $J3oS # Dlzvϛ P(b $!5Y8_3DP閡(K 8o3\% H4 Q8sր4̉S>wLʾ, !ĺPK9I{~cD!F$\lЁI]dH8tWu:ΕW#7ΟvgΜy#GNl|HUR2SR&3qZ#DI"#5w,׮9x/Daju>sMo|w~w tzUXp+ə쌨FPU"jHF903TT3ZY{67MӡѱO>\~Tc~oʯe?+n\z{|˻~㦍Ma,[~;hPzIa=;J>J ChѳRfgFP$Jеs7;"C$MgeggF1gR*@/'~hh?rbvGd캝bu79z5_ڌo|MNҦ&-(F(L9ΒRoj3R4_^-2i(1QгHd$V 1 jFg*O]44vlisxb'j&=s=v#>ĩC(`lxhŸK$dF SU= Jʔrt<{cq0TR+ƀb)~td1:XjvGϴJ%M\d+UE w{ΥXLBs!TIŽtf o y&1h>q 7r V]I=̓FBY$fK<@++)B<|Ypڕ0Vc"0 *8b0h RG95xي᱁f>099 [>ݴin`yM/^'}-o_hӧYs3d>*VefR@00Ǭ`ꈑ\U1p `A`"$WO>|8K?k_̓y/\XZ^vI9<`r!93_qȽ=+O?>33_p0($YN[PE"$$,)!#tHj{@SdUs=NIP Sj@Ҕ D2YѢA*HycONNKB2+\q[ro|;Z_q*,$Kvk_驟˲0a4WU(g 3J DX'CPGfX{0* T!۱LP&ڛlzrf|钩K8|TmK._joxXu{.uNO<,z@"CPXЁ`#  y4!P\:Ԣԅ*J?^QcZ(AcL*ΨEcSn~;7߼zE.l$=]~s]3g/Pcrf̻āLLXs_h˘zon޸~9ҙgpd8IqiWatxxxxꕫF/}K[RΞ_/޳3=5z?{toAEEȢ")% ѐk1AaT"g7w],2D` Ш"P3`$0B3!+cohF#ԋ)4K4˓v̳v{ i4y&ĉg}w?8xP?,]lsnJqȊP XidTS Ru WBp(c56ٙ^\}#FQL*'B\r+1b2 P|9(AL4-@艫*F5ivH_/٘$yRvki9UY)i9qΓ'1uc!")Qg1,6[F5D雪R+RCXTAI+eɛ݇߼| gKV\xq(4 I},.Ⱥ+=4UEЌnuqtrjVWiLQ "k!2*`#ILexg~W3oz57\g'{=s֓{ +|iG|,7_wvu{sg(.fY]pC;**12AM D3B D95 YO9# sv_oUS{㧎~;O}fxԏ,_]^t [.u7ZI.՚GIb*@JΠ66d&BF;&hO>\qH43;h5_|aU "aů[\T2_c##C hHuLO9ZMaĚw;`ьQ k0{%灜!+ 8+0S'YlZv5$QN?{_xU0h, M \U`73SNf^"E/$K"dAb/ze#*$t; k+Py<Ҏ9DU%o5(FF~f+3(Zz,PALȧ^51J hHYEWU ;fVbt `Dб+=F]>2U<U .2I/i=tի[7$j6eQj?&{yՕ_+׭Cw׬Y??7tG_}=Jo_ kV/ -w]D>JcF> o|; LYLM ֬9&Cv5ABapMɀY@J(ƀsmvZy޾'R 7TG{ӳGDnto9.&G}S/Y-8Mw$K$ocܷ祻ncnel蒫'+btl|dlip}EjuXА@Q#QQ1qj1!E#HJpL Ad63T@ CButg @(C޵mتX.߰g?KWl -[܋zhh`58qtyڶcLϓLU]4[w#>%w>1$qD4F@c(D5/aryfDT eK{=wݿ}N-_b;:s3 ֭YgXΆ*$iEj,!1J !J"!(.l7 %V4> *(jQ*JBP/]w޽O?#=ce-_7h rۭ_-;~zc|VY6{v?~i5uy[C瞿k؂/xFJLk0!DpFh`*Fu|VͣKc'X@5~$bf}D>FIn Y;<>t}>=^ %y\06x|1yFU1y=^剓$E@(XF:ty ĢR!Mu} ̘)HL漊9 dDǎ 1 ,'AXY̙aUTCy[E§'^(0竪brwxfJi]By8JCc/^Ԃ3>7yȤUT䙷fmUo}i|{1FC5;{" ^/9"48K,#P2[;߼;_GOf|S_}ns>kC'=Zh$.X0zaH[?7-ٸ}g9v\8̩7oڶu4ͨ 9&f usCOY6EcuY](5(3^M 9n#mv>dղ kb3ϜsKG_رeǙ#'=w |V-[loߺ}W\. U(ӝμ{DDP"V)ؚV]%U T@CP&PDG0EfOu !:B8^L @bF `ML1;z /[=dh+96,ZT,}'&-^|eW/oul3UdSz}hsܜ 8/[gxl,B20PFC7tß?3mޙ[L,Y-9"U2%W\~{G fo| ?~=xɄ aT[chɓ5w|GcX?vfyέ6=|{?5;{؂Kf3&%U"*j5[+4P/I@9E32LD0JTFB S1Mgiy);@UY3~GN$v6\phA˧/;KJ U#W8cRN}13'|YUٹ!1՛G)<\E}+<$*CIFTA3#4Mbw*KA2Vl,zrͼ26y֜!Ǎ!' D429G S, Bɑ+guIIDATF^kiQA@*ܛK2'%;ul<9dc({.TJ)ҙ!4G(f[6\,O{MG.;'1Ba>szi#w}^1o~}}^vv^|k|5_+xǻ뚫19i͛7]һg;"@;'E1կf&g F˗,` W~794~zCBLh&.BX TTk7 h))8AL"/cBB|ݺ7} }鹃+Sv̗9yaW-˿Q'yuޗyfuMu6^ d=Qt,M)9@j6ՊQS "A3ĩ UQ9'BSSUy=L T Q24)S`ToH&Jؙ2PՌcM-Zyy,]wO9ѝDeC׿(10QT~냟M[.FqU9{n1:<#Ͻ5S#ǏX}Mnٛ{?}>C嫷w񅗰ee03T#U1zTp|ܝ~~.~nL!D&f&@hx$GevfzѢ%v[zx`;7b>u E8ی:VCPVu`G5)ae  ;NP  8҄p3Tj ٳ˚@sj4hޱO=v?畣g`<_h% n)rsD`4g+KgV2QfPƪӟiY}p2DPL A- O ԥinbG J4ytyfHAiw^E:ebdc&^b說4x#h2G3B44L1 ihJUu+{i+bNBr n(<{3KlظC}$Yb b˸3Y|/mïwͫU,O/X6K~S0Y\lѷ.x%\?5=yNUWmٲvyn޲s+׭^d坷?ph7MC# gf9H\JD y1oKf{5PFyp&dbJޙ{C(#gP&f,Z2z~Ѣe/Dmۼi~_<0>n|e2K&O*đcSC_0̈́0V)8#@0$3 f*.Is( "Q3 )(p 3u3yջ."`Lj 3K=>dhxTf&se˿47?2"D"2*B4C 2p@QU5H4:xȒE? o޸f#>C*j45Q3"I-Z+ysL4l6͖VE!VF"K\/| $VKs׾Կ3ek6-_0-vfYzɽ?܋Kn0\%]`ᢣ'ݏ޸tٲzx^zu+f@#1S$Nwltl˶>EË>E,UW8Ob"(@ WPM*}b(Rr%ؘ;g%#ru MPj$ f j% 6r{fj!213쒤n 4-tzjy#mڋ4sβT(*wi™30OW;2E;*< SC+msNeL Js%I%f3K72 &_, *TU }",KԹ3p9kTȈJzmdD"2DL(+l$"[ԁETJ 6Z-"b;N@?# 1pdA(O{JU\Rj#HYf(t;*J P1Usy9%S|Gvz_=pb;fOnmWoٲ9hhCr%G/x|{Pۼޱv卡cKC̲[qH0I$j^Wo2yj,+)8$C"0"$=e sjIUflZf7V mYy'[mSN{N97ԉK>yF5nW,ZtE3Of q\{1l_4 k$!{EE#eDf` `b KLE*CBc2U=g: LU l#`uJА Y1s20*]qm~Ư~z؂[޺m|=|G\y@gf=s "tc9i!9UQ:Puި90P UiP ^!B1T CU1jP1$NT5R1aFx4u9Or`©ӧL3{|B ` 01 h$!s @yJ( gIvacKphvi?]lSg9pӃ1XtW>˯?K|pÊ$aV,,sL,_j^)+CA6:P#EQ!!XbD`P&&L|f͜)DLkN=pCXE$ugH֥(*8rD 6E *U滽_Hs#F3o`ъ/<~x{} u;u$ŌFFfOw=o\YV.əD!C"`hyp,ETFglZD3>vlnzJ c3ALFh;Ӛ6@j0I2x9`ef^T9 _QDZCE_zoO{FFGƆPŊ=5Ud3@35(k 0QBS6T3z@B;f6fT 82riPh4C3؞GyxlC' f{`3ϲ|M.{< v=-]kz>kow?`O?tϙ3/K/~n+Zy)djY*wFAX1%w+@EB8&rTU&C͗}ɟ.[;zaŋ,\?[-oġcO_KK~O-"4[C(uӳ\tL4"BQ=QȤҚEA 9Q!`t` !Fk!`$gabSu"5y#ڭfgEE\l驓G}䁵kmzܩHuח``d{GbUp ' `@dJV#H $tlbJ\Q_ 3%ӈp6 mFPoLCI L4yd69I5S"B'v&l-\0]w_#/~]t;E/%?x-{{}$񍤱xa{|dW^qwx]o>qL5|酻,{ivE6%c'w׬ٿgw?{{[)󱬬6S fd 1Xq}_'/CG1;U:{ ހsUˢX)Mͼ!:QcijE3` S00H@ jB N 5hvl7ڍ'tԁW~GNp@-h^UUUQ#,M%J*VN8Y@T A"V D(c RgI}"TUUslPR@u99O+0NZ9$3^Igu,AbY$$A`01Wee"$Гڳ# !9i9ch$ױgIO344ԡ#"T´Tgi *YCLBP%y$ ' *Ū}]%yzeщM\tE<ԛ- CGԮ+6/:g鲡ѕ򋷯Mj͟w8vJT@?cc nCOuWK:˯KݾqÊ{t態>xpŕ7;wzk_Œu;W }vV !`hOd1pΜ3VGnכIxe%Z7,[qל[%}\rܩ'~x7-\'*YzsVeI+KΙxć?x+x3Ӫ'TcCB UPk#d23򌂦΀U * VBPUTA SgLuISc%n?!dYygLN֬tdxdū֯*bpيs^~{𪫯]qԤjN-P1c2 g@4!3&V3dFB@ԨBDu Xo$0c$B߈:@C3"L[Y啄~jQ~Rɰ* @$m?>𖷏-ܼ1>i>Գ^pb[|%?phg7l^im{ة[\zَo'?>C\^n⫗}33swdo}>~4OAZLT\.edŒ'|/֯4W]{"ꉀYTc$BbLD>I|5YE5C $*!{TgcfPDu(xfΘPA"9F$Bl5h 5`{5OOLy{x={^z壧&z d͡yX bf4ϣ 8DCE4JQ H<PI LQg\!P$9,5%"On=@e!K22z@sQbf $ƴ!GP`eZnTZm1hc ] [˓NK.O # "^H B f7Ң2t1JcE"HRvAȈłEТFIuV]5RVd ]LJI{:n~Ö_t޵9K7>?Oɤf9Qe`KǾ/{EK>6_1_W9jcs7s{NN>򋏾]o]d޽O|ٕ.ꪅ ]9)!"**3#%D6H]BΥY"vx} zcӧ ).?on޴bFs<#&&%:S7-|{_}z향۷ 5G}<=rƁPgrjlS5%KϜzϿ >-N|쾻'=sg<ΎZnx&W|u·:`1j'/xj+ÃCO:=clڲ~;O;+V}ZxD4c#KԈIA=Œg ,Y_OgŹ'WoHZdƎaT@&"6ECUq>3N}]%upAL I"j6c5+ڂ83(8$:Ȝsf:<2nIs6urÏ<{}h/HklS};.oU#4PEӦOR rd A%eK,ʳcA+X(ju@DjS+""ƌTbbbFDH'ee)L<5&VB  s VTlQR "J1۵bIT<0zWYv, Pi/|`4OT@⼫jZ1sb*ĤRyPc`,$I(*E%#.ްu[ns.!$ "uf݆EKy?ywz͒5gsrx=wƪlؼeus hjhV1+2RRr~HȄ 18o/‡ӛC$z݃f SS36G";0eG':JH )H&Aj@TTUzÃa.]stz" l?} oso GGp`i Yzp-lQ䢫/ݸzES%-|_yei&y_g?G_~і_G=;ӈG>}"ݹμ+EcEPvL <{8pxuʵf#c#;mJX9vΑ){E& T%32*(XT3E;SzgMlD \gNc L ,cN[큼GGGZV#k9/ҽ=r]?}Ǟo 3)!IJOއ:yE `"]3kIS4& gIffUwb,"ssK,c$6UK zUOc09ضN=3("̓Zh:D !(g/}plbP{e)wYž˲* hZ ~9CK Q>{7py]XufCջ׮~aI#Պ@20f_yl zPxG鍿_?୷ۻwtܭ]~k~Ͷm835u|Աcѡ6Y+:g+yAU`cnz7)m?vOײXx?4jdp"Q@ 3@NErEՙh ל1Dvl)f"+D4MC b \xfO$҄A ӳ'?=xrT7dpxpE0F1<1F !cUQLz17ʲ"ȎL P 1: p DqHL Zi<ӄ b'bLҾt'󝹲_s6L*s=pyIU$3K+F11b%IP_(X(#h%)ht,"<7pw%kK JϊeJGμ/zs@yz4'#r(U},`fMQ Ct(=I+)|Ӌ&cޱWgtZURLZiee jtHf/g6lF՟͒P><2}btf>m~/01u@0V@Y#(!ƞsLS$YcJ#Zu@**"s V#zlynH.v )C_Ϣٹc%E :=c%!/]B1TKLO<ĂVYBЬ~Fd!a. D`*m,MczE/VC\F&⽯v{Ȫ1~?V5ؚ:5}Y(lTErPTSuU#F`X*D%Ȳ0U"1fG*J~L|w۝.?o5?72Au5{(̴q tPs=UQGG-J|>?W(Dcɩ=<`j;֍#Ͼ:1=mZU?Owkv׿둇_xW߰e[ R H 5"1d@LX-tbF`j)6DL>DLD 'w"'uQ%˗%i'O~qXvΊ}Ͽ5b [*ZC#g *f@\H1[ Z$VU4 gʀJ9EUQB)I$͒%Hpzz/?_Ta 1kd~ ]VA8㤣%!i(`,y@D3~+-`dj!b"1"`d \'N0s\AL -3y]DC' wz<ڍܧZ J,cuBR5sNhQ+T$dly#3%JvKX˹H+q iH !k@J`f ^LͺVڬ]t2$ptS_> 4 FQ$$B%-+gnrrfv ԇ6mݺlE(׌4m}l;?slv0.Z}g^< k? W\u߸ֻozݻ ȑ(3"0Y09H&HUլ,]H{`Bd b>M5Hy[oї/}57\zަ .h>}*솛Wp|[>}W> YGٓ@hλi놝U7<0@4M@ sFœh ;oGvnw*Vk-F($AMAIStbN!b̝^ֳ?jR)+ZK$dѢC e|K/s_=w7ܔ7\>J45cr-&I%XSuЌ R(W@ 1toާT/M" 3{PMD`q)Ȃav?z77¾ro7o}GWZyÖMm;zK6fDl%cpys|qrgs7NMl\r%W^~ŕAfҹ3^;ߝ+B?I{`311@ j5L#JeDRs"'r}_g~?^vUŠL#p2 uYdB4"BS3@G5DA'02IdѣKyh5bFd7==ҫ쾗?Ut -;g͡;733g z $z$Q-\U ~%~#M ~ 쉐}*J\*S>U\$Ɣ@7tًQy$wBAJ4XU&BJH4ahLۭnShU-I%T%),\D.PJp@03h%>-DU-D$Mb *+\P?#Lbhԋ=ph=?7+ ӝ9*Kt P # 1te'<]`=C;\b!V!G`A( G(pMy?6yݲqK=87?-SQ ,5~WƝر^;{}+(̙驕kW{Eo*qx*?{ Xܰbtuy\yŹSqFD3d jcb `diݪ4ufoE1;?.1rj _uVchxlptAsp0r"7={o~O~:d8,sYJϽz4,r%*p$4 fM M("$K}3z$ -*ę)"*0e:vN:2ɐc ‚̤eSJ%V9*HHF)ʎyN 2sgn.Tu/?o/\rW[:km>,}챇6|3ML4O=;W}DFk7nTogg;Z&,6$xE"BD(*W"r[yYΕ.LMUc_|Qd"20=jA: Ɣ+ΦOMDž&Ȅ*!( J(R R4QZƦMg]/L/t: UBDKBX'!) sQFDWT'l@DAE +r+8+x !VƆx%_Ȑߴqptt~qjn8&;Ӥs/{s/{x nW=g^xv!_7}pK.?sמKLoks~$ɳ~ska͊w\|y]r^6l6߄ֺz&H@^Tb wdx"^ QycJZ*# *F{02:䯞\ot,bўogysd|qNb$"ZE Ql *ǨB*Bւ B1lc[TUc"^>{BuEQ5Իu0 5J!MbgjWEEKD!/"saD >/#u.X˱  _hbf P!5t:cle‘] jVϜQ⃸<+&6wJyܡJѺ̥]1 $l|^VSwfH,k+r55LW+H \BH*Dp#j)bP `P/^M 1O{q|w- <* DY6ZG$c}$(ULDz(,ĈeR$+ :\潋89)@,ۙXqI eesk{֎26Au@@"9c_wXyէ?G٭cKwƏ-gbm{{ӳYmޣ{_=rz_v_{內/#-zSgz n>ٙ 5D3Gy4AՀ. ɦQd"CdVS+e~q^dy&)A$J_ֆɽ{_>r`og' ,: )4ݔ)802S edA;8q͟d9xEr H)L$R"DD$ "EEF/;+WzIyj$QTPh@HAUlkdL^V2l$"˄VR[/KsѲe]ݺz٣/O,E۴ޭ$eK =}=+&W_~Usa~x`kW_G7[Z񔭏إ87 RgGOY{كGLZ?ȾəZ#" :5lD EѰ2*; Jd@J@| ķ E }~DYI *A"ғbDq9 LPMcc,3"8m×jϻxŲkKztڵ'׺횝C=6B^l>׭:vx{uևJenؾC/}o,[m 7yOڶmPkn/]vgzU]})NL=PveF JԦRE.xy;bTmn:$f(PGւ A6؋f٩|?7 /˯oCEEB5J . Dbi`$t^WJ(APUDV-Wz"y1u|n;w~sɹ㈁7Il@¹vG&NlE¹ZTld pjvt3 !q#Q&bhDHyqdMnDH8!] [,].X%&JPJsם$.{|vD,oC'.x2&81 *BL.oSJ[4ueIX7)B;/@jZL/Zo=ؗK bAB|ۄE@QcE.3`E"Q E HlQR82IAzg5`,9hhMؔ /R& LHZX"Cw+6QPWBYJY5Qm-}-.NW 1KIP*JsMyl_~a)WFmWm`{<GI̼SW z7Wd*SNU~n壍fc5<<~̽>٧^Ɨa텖-ڍo \vm:Q@f$@/lxo 1@ $X %מ?~[]\bw̓SлZ_?k-ݔ̃)Բ'P*ډ=e6l"2wԓݿvI_x|r-j,aϛǟJmalxdP rCv S{5GۿSU=Kδvl"ϗ^6yxj))" {Dk %-2G A\P96Zwj]U˱!+ʅ酽LL&p޴b5ECVI5o7E&8 !H@EF+8 EF*0oy.e;r&v> VأBB^bdSJ"ҕPD!(jDI@ZcPJAQ%aIJ% Sb]VK10E\^0 $q:l2X*%X#""2#vw9T@M`t 'JTdP//YqŠ3̃εvy*>a Qˊ@!$ ! l!XWQMDh4}i9{_Xz/׎m>ũӳEhmVv{.}?>]wӗk]}JU6bdNla1GR/D U3?}/&vXUAd3dxj\$4"[|=iE˗.W޴n᱊ 44WTA!PT*QD"J`DiJ$ʀ@ JZk7܉,9wGn֒"* 2aa .(#1THZIjՠZdsz_*z\SL!yBٲF;rk-ݿ{3'lWPȘ "#IB (H:  bkTT3jˡ,F1*vhyͮᩣ|yGN/ SI 5`*BF@: NArRMl45?590לiړtSp@Đ.(1saTԤ*/]nV"ˑK٨wAQ .M$!Q|H9r瀪sD0dI BE< 1BX.:}$g+ͼH(wGkMf bUf$2(ŲRd2űDVqTBWyHMTSd(,J rڙAj&N l$DDX 8X5xQ lJ]JDq%\-Qؒ"QxUjm/wEXh6QD/bHvad8M/fH[2!xUPW.UEE&NM-ds\(FBylƛ{7<ߵt5/$A4K*ֺgf6-\lg$wzh',1veRF+/_?9]DTJ<˟9}rP*dyy_p9'?xN9Ʊ?me&gmشN/=6l-T0(CP%A*x }D &FFCMU=F̀4j_~O}wLqڵϽc뻺b#V/hnyIsduã}rˏlNt偃M}jKWV#$&N*GY @Pf d5}['?{;?K,"{wUcԡjn5JXZ]wƻKwlxH_WϹ'^zie]9)#e;.7lA@Q vjAOeU u L+d@$HX$N];2tjj UQl~uuΗFѨh@ԨYf$.#P eݽ!s CCsSs?~h"j2<>y#clO~SSN8"5Z'zj%MWZWzm8ٿw7w~ qx|fff{>[}ݽkzo醿 .rjCG*UF}`R2@PQU ^+*FˠH ;vT HcVUE(2hi U ր$D@;ݻ^灻~쓧_}9慩r B% y^ɦ?48X$?zpbǎO?_?įػwoᲤWzz8T4ogTHU#e$A4NDR Jdc DZ!ADjQ5j-[@U2\ +=ݙ/jGư]E!fGPSDEN)]!ljJP2⶷yߞmn؂YXwSθ{cˋ ꮯ_7">>cp]z׶暭g,>{w|~_O?e#&֯ta+_8: ICTKvۍiq4XӻjIc/عG{&_ٻY_'Ãb^$J% "ɉ\ uyO@;BFA Z{~u]=Zlm߷{=ؒ8sZWZ H@JَېlVg`(+]5:2?qͭX5ڮݿ ;Ny}KƻP2Ju駟zg^|qƿMO|1]p7-^9cf&eͦU8JkZZCHFYY[DA:D?8P(PkH{U&bDDT%WWn^rCLt{_ܕ]+$^sW @5k>06[[;w=szꙭ7LsZtUjU5DD U *pTI !̤E4J$2``HCb˄ PrFuPp|@2 @T`UW䠁U11PBPݾ [|^a, !$5&B_XU hQ=SS=YK1ZK qB 2+m4)Ȳ06Yp* +"g |ᘘ]15HL(*MJl}˲D}ݮӥ+F\Qw&ba.xboϛ #-ԓo x οDļ G$-g[_tWV7z/vգO;cq-g|c']vժk{t 0@ A@T;Ɏ;Z2PDc( 5@˼SIVRλy?thOkW+e+O=˯Mnxi쫏<Ԗs6rʙ3 0~`̞]wu vmw/YhK+ke FSVmڸjMKbbS[罟|w-^ZM8?3*  S{r1;:5;6t5O8k\n;w5'}˖T^!X& HD>É*'+JBvC(AE@kJT渫"[\ !WҕY0)y%A,eGvK/iկ=h5"ݶ=}В˖eƶm/#c?ﬥ+lʶ/?G?{hhtSpslLdW,[fjNkwyw{p1E?/|駝őǧ CIRIbcAUˢ>X`$u"&[;d)VfS"vɒ'O Y"DBPX!!UVU^1Mg" ڭz{ff#Qg_9W_[&U 14 Ӥ0k~/o۶órG޴v9xW:gM̆+2PW/-J$&4 % `@ [!3vb&Rzd džDUmdH^E  " !=w%Z֨Ȇ*Ubb( A!B t @$R v;QY ÜTY,.͋gytW[ 6) s ^ Jއ%KFW^]@ ˺zW֮žCG s՗>7/]v  SVo`۶/u|7ު~#|'~nCݕ>@',{WB97\_JZW%^tEo}lT`|/UPQ*0h1Q",*u^$3{[Cs~itUo8/_8w!iWHw:su75Uz;m}b=|M/<]|s䛯~#Kʆ$6&ddk>Rq;}4BЈ,JYdz#G#=GνVv#^\^2#",pp7}}YE 5t %"eB(c=6}}CK 7vK,{'w{Ζ^Q/\!Czcmkzpy;/!Z.o]xm[gǛns&׬[_Ւ{b^x瞾p|:_a?5+7ǟ|UYW^_ߺ߾_{w׾/_gsgl>GjZ s9GM162IETT$)XDB^pmx :]&dB *C`ђj  1xR"UӮ3{S,Qu  &zQ|s3ϽOnݾPI`w^1ƤAvőĕA3fC^E12*,XHY\.FILi,YӰAQk Vj!/1e5(D "Q1b؀ I@U-SNAA6)FE!b^9kT\ tjk{hp ٤)J̡,ij' Ez%P(A%6FJ4I UAk۔.sfJ{9⾡e(ޟq9˗0?j.۪@LБM׽t7G&^k7?xuz}q><9=sw߽чkCO߹=]Di<>wl0uJ W$%E` *dA9R48Xc s@Lӓ;{6=s6X׷b< v /{8|6j6W/z}:w=^yUBn[lo~x>'yb "DZ1Iq?yg :7-&qiQuS/=UwׯS +FQ勾'56E905;lgmB #T:5'o.S P`@ s6]:󏿼qPgB:z'5rO)FcQ,wMμsw_H046A!/p؞ݹ^]_S={[C%cgu f{|WV<_" >֋/מC25ؿz-o_>xKV~#WMNTk3SS;_}#sYZO $Jƨ Hţ(ED%@bj !A:$lT@UTBo (`I V@0xH 1B 20bkzΥWCCiZ˳bテ^yGk/=0VVIYeQW^0QMR>de&IZs %G] |s$q&ĺP0>X#Xڨ0e$`\JB\zMI At Fe^ [[hPAZ3c9aBG LdĀ1F&YaQd Ph]1``IENDB`fritzing-app-0.9.3b/resources/images/splash/fab_slice2.png000066400000000000000000005253361272363601300235700ustar00rootroot00000000000000PNG  IHDR1LbKGDIDATxLgeY ?os}b\]9rDH ! 1`}6^k1 ˜ @@inux{9p[jw_UQD@"@9{pFdR)Hq,)3b )|>(ۨRHI`'a4jApL"Y)iU22XT%`a7H Ho1ubD$4 *$;ͅ:W6 Uǩ3O^Dn8"== k2NEL5 =BRH³,Ȟ)2aݴ z,"cJpΙ"h:tXXXA"ӑT˥P=s(tS"`ɸ/ f G !S#&BpI".zucQxaZW.HKY-b άԇ`3f5J)jd^x0!Efep$RȒUCcV_:_ 0lnA$TLH- u"0^S^~䑻Wʕh=-LJ$@pPD&3݌D@ w='L*|C/__¢Py˧>gni3їٷ1_9o\"I3}W~ɛ'noow~wgG>m_9w;>S^ϜVDtsD=\J "5=)CE'ع~)Y,c7z_SoY!@S %#8Ɋ J2rdH )o_SO>HB 3"Q (ل8`r c!#X !nA$$@]FfK20PA@ZzD }}˯"G[ ((L$L\ak!]zi՝G9uNLj9(Y")'uC1B Ser*]3eIO֫j0B"#2͘KHu. p Y2RXHp2(h%8!), D.E&7 19uE [Ɉ=Z-j (#U `,蝘@9e)ݜ52,ĜU[P'S2'bL$Ko YkŠB1- *L{S[_ tONZ ާ#JaDj}ju" C@BfQJ-yc3IVhA_.[xS^;z)uWaVZkIYtjJע;4,R %@eVR^Ӏ6MkNĢf,dܑR$<( @iJ ֊RwpJnrufP0ڨ¼H -F-sܹȞPC7>x]1pq޵;;ԥ>ì̢N&t&F,f&F$Otӕ>W쭯_{6u[?_8s}k 2K>||8>wm ҙ%3Vhfn:sKէ6>ry{Vc1#sO|J[1F-#}P(1] Ibt z2 I`N@.5(1M*D ')?򾿹tiE͛?:C%\TY!%EG=JtxdYR8A.AT)(Dhaf`r!%LG'"T*ѝJqEiMs)#z LBQbtoz&!!Q2"pWȻS):l`K-SI=D Vx$@2lLYɐpph(Ǵ);ɚMM䩳^pTؿ*eV" _{ɟvB~~c?'"bJC(" FFX@,C>۾Of)-לC;|C$bAօ5oz.lطM|b#|u#@RN5o;}]" UxuglPqD 2H'}bͨ{gfcֈɅaAfO%Ӽ+e"I4z@\8cq8ÝaP%HbɊ*C+3d\Ĭ^dZM)r2e!@!kk那AIO{Ke'X83(G7.#Ghus1Uw $#Bp$ F0Mjk@ E2", #%CLHeYD(C2p$sP ů7_?_z\vmb+ϝ{=|v[Gn>W|o?|rbɓg7=uWY+2FfİiwӭW0;pɣn9u읷־Gxrwo֬/ ҃_G>ɠkÇ@ "`"(V\|u×ME/{W|÷ X2=Q!c[8 4kN\jE ST 윩L$Z!$sTG+I)pf!$8{CIA5Bn" S7!>-E% ($# %1^=DWuUgE2m$PJQW @% ڦ^fc CX JLڟ K(; -z$!\uR<ݓH 0A;*OQARy:[sn@ Pfz1đ̋> "[:%7({7E'X?vԱo:YjY@FZp[8+H bܝ3D޻7Û_W_w*=[ nz%W^iDRFUnc<3Zi) 4X2 Z'-zS$ݝeLN`ZD ddU BR\g5&"V䢇0$$eF)Rz3U |}wYn)m{;W}mgLMؽ6;N,Vj H$s X4e9}mW﷟clKGx$;(<VlTdllS % d0p933 O0S"pp!()r]:=ln{b|/|]7=tx,ʠi9*eXi?v?;/ړ9Qj7BFA_3u =TX''basX&h _>hD83S8^ⓥP#bb 8ˬVmtiAE[7`֥J6ת$@8A4N13Ӵd8U!f +*  xrn";ZmETSi!Op1[sE%EQ88`NJFKpbLLRT6U5$Ug6j&fHH)m$հ^fi꼂l6MY iE+QJYa`!JF#H'4cO\ļH'1G02+iֻ%@NP14tDG&q:zE4; $ɠf٧I۠uKj1HlKj>ll:pȉv7_P2;z-͗163)=J!`Hd|udB23Bp0ed`+RADp9L⭍[s`uK "bgȎhiWj MΑݽ?^7=/t}H0!VWN+Xm@|Y)ʙ̂FHV^{ի^ׯzZ7A( @U)A - LIdÊhd `yXGNb` \`v8VqXhEkZ$ItJCxwD3Kx0{$ 0%<{gxomgxG_PGk_OٹD2:p0F 7);D %w%{""LVZSPa)9cI3$2.)RrI  S;D58hJlm\.Eey&1yiUCFEݝ+kǿ?=WfbB_TE<]ez'&%ZD PJGX8# m2'krω2eV-:T`ND\A d3B2qZ<̨w޽;/C)ܭS^jmhh`'G8;# B4tVa@‘ qdX[PJXN#Q@VDmZ!fq2x&D0~_ f`g^8˿s]oD%V܍F$aeJ$' pwQLfH Fr (N$U$ G0q a"Pwg&0V fcNDpw "$<\AAD&G"L!6Z$٢D[Pgk}8}=|ϜzM֛Nmm [3LH#LY3(2um:#'(<"O6W֭ͪ [.ʹ0%lކRZ۳Jbc_,|mKIjDWND="3Jܭ1#ԣQP[+e 1O֜@3pq6 DJ儰YĠHG@9368}G+674,=3|JH㮠$Lɠ!hGֆE\#`9_hR"IR-8SU2a $)܉$朙QHK.2"%wa 1A"TNE:A2I ͧH(D_a׮]g(]E+Q,=IGfԧ^Izrg,<CVJQq2ڌf +'|/~;r@kftWU]>Ы^{j 8B$'#͜"# k0w\4J@ ! r31 AfF0Smp$HSJv@IVf CƔpe !hDPIs-5OnB2EYYt(#AisGGi\%VR{s_ׂ#mߡCmn㬔ڛ9kAEwZT#M$\ Ԋ'#M]6Le7K"a&2陪L<0N *"eGz*ȳV.54FHWHV*>bEz2U\& a1nI;217 fuDWr^keC`2\+қOZJ[,rPDF%jrr٧H[ FU m 3$Ā/,gR,8,Ƞ4!Ɂt/̝X+45omMY,mDfڵLwX7:vC;zp{c#sae6uNK $HDɜZ0, $j̫\sҕO<'{䑇?3WkCz>Wm2=)Hd""$"rwqOm~װ1DRXx"<8_\*U&T | exXbD*=PCGwK''Q:=)Y][L3 IBTxD0if:zpJ.LDlC)Benyyج,r9&!$)UA@њ ZT:On\Q5<E+#BbP=‚ij<lJfԩ&A kY-v$=JXAHELIEoo1)lz);,"-EѻFZ̝HF"Yt+>WFY2fP/E(Wc&0HC38!w@&hjlU֢ {$esVm2,Lrip+uWH:!f4-/Oݛ;yO{4zǠ#-&h["^V[}J2L$#STeP "e #;.E2)BdvlV|n|]5$ g4#p9`J,։}T-`9Jf38<\j [Lrb h]YB IԡJD kEe"戰*N=91{;߸Q]SULʞ'VS$\`)lm20$IJ*RZkdJB>y&(2Ԗ#Α,d" 4X| )*݇apsgA<oZBD}ADlI3ÒXcb{SڵRJþvrMg9s7ąܺeZdR5۹p͊2[[(D BY-!݃DՙPR6.}?ɏ=>Λ78{ -o=u|VO:Mxc?qwH^A+WЊ=JXI3(@eӷ~3~ OD}j mnuwuӉ?wkW*Dq232 (X4D+{63 ȡţs Leġ׷_ll&&-9xxa>u؜U<\,kj*s#R^xާT#-$&: QK$yעq9[KrdPݽTErja%̚2Q@Dn\*GvJ\ Hg:Hd*Y=6y'e4J19XD@2S"YS}.\X2pt2+j"@"?/LˬiL1 se!$cJR: `O+' A^EC3eSD3z $!sRYlRkFC]׵gk8yfbo7R4lrq+]Y\2MAz}r)ӕIk֍.d-X$F+8 H/GdVJaFx-u;'Ie,a6ePT˴lrs3yaͧj݃Vs@0gb\r*DcDp)D"4Y fQ4L`Δ>EGCZN%d䥤IO"E'e)uwwWKHomTS_W%@OL"Qm>wkXz/U!=AH 6hI]2Œ(eP Ÿf SiJf9Ȑ V 7Y !phˆZ `e[{:,\6"f"dxXASuvСNrC7rrf:h _Y\̪ZfsqO=W}衿_;-ve ݂Uef֖ןt?G~g{4_:u}{oGNؘgcƝ½dxqY'~p=т!솰>V";fdaGOͭ'n{-1f>¨ YgICf03^qPj{)|roZIH"RygXdr&*SD_eۯ{ }q~ڢi`{詛c}]SJRplIN $)MTj=ؗ6|cPGkn}AILӨE"e5@y(IHROSHP: ArSgQr%%(KK-2a{c&7{og*`Dvʸ N֯]gΟ=ssOD( p@Gr+ $" Y=dDG'd&F7t{"NXcXQ38gA2DLf<0X19 sfy -l ~W\Fr睯esFaucz۷!eCeRٟoon3pK"B96Hi 7` V%kdJD:5$tRƆ`hN0 rfc^Es@Lp7ZQ2ONpqPf)u Sxkʽ{$UfΘ2"{Hf AEhdnGL PόUQi͈EU}')"Dmj`ꬃ2Ԧ*LJ#3qw|zdT&cdRPHMU(̓8d: QQlFt$*3Y %%"{O{"R=D-=WvZ.=AܻeFxD oDw/9BgC9sց}'N;xѣGؒ69}j4ͦ)u&q{/]|矻ԩhc{ͧ~k? e?_ϔes.\zУx܅{Љg8׼n:}b{{&rli9K_b n sڬ;W/[?GV V ̂ppgT&_~Qm}GW[=u@K'Wl{k:{y|-woxٯ;{_.}'G282Ed`╩;C#_:Q{_{wwwyxHK-LԉL ȰAʴ99TJlKZޙ5̑B<*%R%D:DN=J4Cg8ˬBU9X,Iv *1u'8ٰ1Е$ Jxg]D=l C͟{g* VZH$\Br%*#:A) @PP$GR+`[Yn,ǔ0S@dQNF I,@~D0Gpg!D4Z BXzSr@BM[u߸o:{E{̀V `[{sٸX|7nb1iB1Ԛ+G $ JJ'# c*ZOAkWYR9W.2GJUYĸ\vuV>td$B䕬3X9`qYf,( b`Pz =3t[+k w1 u':iV2C)XkC) |.$ݵ- @‘6:Ҭq87ϊALޔ%7t#&*:_L{E r\(u-#]2ZK!J j֘#Z2$2#&}i֗BJ.Z(o|ѩSxn=q|>ۿEiz׮){̆|c8v^|ʳ_t#ʼnO?^|G3˿ק_/z;n;HTpen e$g*3ghw#Rģ' i%FZhʲYn:̦~=C" 6\8}` .-#$:+%0O*!"3jX$R zJ^Jޫs32I Y{QI9<ģ{}ADU\2r8 ?oX\$̫ĺء ` "@02ԓYi+DH1)̓ΠJ@xe\Cn$Za]EʦW)k)h 1%"Uf$G|>\|voo޿}o|N߭+`L³nۻQvY+ ѭ7B:js&Ck#2''C:ԑPj@}*`b`Ovdr)"q2U2۲a`ffw@W%^u^[#74:8in8RB#Ŕ{((27L*23sep-$N ${B2ٲWR =Y|Is膢B"S$D .ADI# 9*J* œ}:{ZԌwBuZS%9gJqF듉t$nQxD9Q,D{Zc{DS#9r#7zcǏgN uH)k!}wg'瞹38vnyC8۾mԱ3W.>Wgǿ{>>+?v{~뗑Çprs^æo_usmj~iATj$bZx>{t}/}ѭwN\~%x5(,F9qck>\LgK_[_~ۛؽG׎e;|KlZ+˶􊗿ҵsuXS#=d:J%V"0:Py y}fUdĢW_zp瞻Z[.nmrؾ+ bVA`iwVfBl=M0fBSx KrBd-ʬS3sc%^'wB0ݡ"DHpo$(8dFfHJ0GgJAnF",HQާ% t!LC ##l>֬TIHQ tgrkU .: (݇Z!=[I R"4,9`!ЁCo׍6k'N\)4f.‹r_W_uɶuzVw;zȥG*n;W%gims@"F65RJ;&*!P)49(3#{::P9=*3'ȧVC*<(M0NeF3Й GYcIY2G3dlfE]bY95>M!1:^i݈%'w"fZ#tc.C! /JZe4`sc%' )مw nm kKmN8{䶃3WƒbFw=e6Ȝ.<|晋?߮:o>u䉗}t|;Kgm{~ n?}{%q_}9uϒk׮];wݗ.^Bq`鶰և|w<tЬl]I3QƝw]w}{'%ؘ;~ܳϝSϵ8qGN;uGڢBBeww41ZxO<3?vdžCG_Iv܇o+^ݫ?/?şwR!'~??>=懆)_r_xo?gfOӣz^~Psaz[L/Oޣ'WԬΦ6! >v[ܶ;^=Tˉ#;2 $`Z ]/|o#67gFZrer&_?Wܾu} /0t׫>:;:P-)t J"Wr8PXg#I CHɸz K e iZ+,rRR3!uTUgXTH@ѽmePbP b'x|^\kiDx|ŗ#ӳpv3AzoE$?z`pqjn8Uq sY"Cb"= QFɮ0Pڗ8[AEj=Z%BDzFaN```"͘(K:p*dRAwbfe= b]og/~n?,VZͮ$KKA$%OחZ<ӢNk260ԁ=066mo2DFNBuZc= *p\ |") ,2Lɉ15gL$_&.zX"abNY#H4g}l޽[283|>XqS'>S?<ޠڷ>_7}}]-Zb9 ̥ڠB"쑱BxߛZfgoz'>#}}&ӷol-w+N.IDʿ)̤;&l5w;^th߅Z J gsĔ(;R,CVID1!"` pr`\E ka"YL}ۂSwjՃSauX薅YCzE_UWƑ"TMM\Gr &{[&s%݇Z5y&OФ" Ѝ9>LHPᒰE!1%- Aؐ/60*tœ "w0)BݥHU#94W. CeuI: r$[kӴ _[#=t reP'Se_ەaNw " $E4s)R#Sȡ{WRND:1F.,Ʉ(׸V9D *P !aUNb*UW{uAmj…ҢeDef$"7!A<]3,һ'\z+8(ɳ)Xxn)Y$& r3,@U{nUɁZJ4/AJ^@ރJ%DW&ZKUQa92<+g:r{D R۪+zdٝ -wƅi}C'M/yKkoDy,{qZ)ڂHȌa>fg'/>uܹ+]ݘ?pgO9{k_yGvγJ/Gr|]g~~׏;҅].|ksW[nҶ8yo]r;uݯ~OǟZrW.GSgoSsg ._vM÷7O|}G͖wvW0+W{?ijuVi o#, Z̃VFFP&q2DXEOGII Oͭw}UPv7J;WN9׍2bR:ɝ/:"We.\׏lls28ȒLMꪩOŖ RiU) YwJX,[t^*ܻ"p Xt04 *'W 3;  Z#H[;1BNFݦ"f8Q) 20[B(PPiww,%c$ [3#ʎ QD5XQ3ZsZ`ZYhQ2blpۭ%UPJLTC8:(ݸ[hw_2NdX #D[mZv-ݐ.)LB8"%L/3UQD6!L5,GLHjj5A )i֖Ji=*Gfz&L(-4JXGFѳQAfdmtq9l@7w/E)&_d8t'PY_;4_*=/8}NooXvfb{ sr$c/?5_s{y+^YW/]jW}?/}b<]v'~kw5o_K6[ػ__Ϊ7'GzϽ/_=|֘}e͕q~qާ^j5Ͽ'衇~CC>~*V]+<"AHXLH( ug04-]³<{Sﻶwm]L0䓖}λlon뷜mwwy-wd{}Ꞟ٦ʳO=U0of$Mwfja:iRuk֖F uװI%٬JJ ^W3SJx(HbVc"fd 76,̢1V99ĵr1EPVWY͔4(HM'YGkْw(QqG?W޺nVn$H> n]ٕY2%l{ðUfiww<*ΫLem wH:RVpb\  @T[C84D߁ Vf2uё+Epbwf d!NNPFg32}֣E9 ;^|G?O}x*ly秼P=-]# bnʬ$VeswLH kxC$SqJYՉ[F"+7!"Fo@YYѻZ9E<;bVIfBּ77"]"@+d-Jg&SSe*LPhBǑB"ÈD4%YD2X٦,9kINRdnZ94RSx`{I<+INI1M#@мVHwC8eD_KJbwRact7Qny}k 3G1AgU6쾸>cO:q#7 NV}k?+ ~o?ꗽ=_/x}?8w|rǾN{ᏽGէ;0FlϏ:ޖK? g~ oEWu55}7 Ε׹}tqgwwo]?~3o|m\fTSLa8)9`J?ї{{y<?d_osX-h޾Uky*/vm[r8)ҙf#O]z=pܳtQ.h)w?p?EGϛpo~~Ν{ݽkSG2KrFY,ɏ<ÿy\!J)Gxv3M[s…#7o_3zK_3̒(#{oyU]xFޛIHHaIJղ,$2y<2#׸iWݛDCV|xz^ӏ۟8x䙳έw_=əq 9r&@9&W!F`m~KgV7ZY~O8w 򆑑: ÅzS 1Ճ>̈ajn9͏n4}[S͞#.2Hyxmgizf/𪅳 ++<]HBE!jG; K++GϜCwk) ֦l<[?-DAv;ssF~dFԱc+g.x3#%j$@PWv/-qe9,S)g#-Q-hr%y#j!Ts=$+82RvᠭQӑuiQ2;Y[ʱSք) N\[1S@mhCd uS!?=>'}ᒫZ_;S*G KY])X`Iġ0نr'~3<Κ\׀5Ţ97DC y7s|.;ݻEsV*GZ)66fTU^i=szͫ^WnTuuwxV7e f8vxxee5Pعy?1XX?ѯ[_=8։c#;/jjjfzLodHk6sadY5DR[S-7l}̉%Slԩ,:j&B$đBYX4'S A" \,Ţɝq2H&".,{%:W!FI;o 0&l%eU#3-YY Y 'c)t 9$5uN);Lp*4EEVew+~Hbty9n'nvAnU40s; 0-0&j*.Bp,2e$'fwV`>bG6eZ`D*D-! juę#G~<|‚Dٱ{W^vn~~l'A/ :A l sƁś$jNIrY QM!ԹU˙cLf5nF)[$svFuФDB$9[h=!D+ũXlRDdh_W[((k$"7ע,RYYB&poe#rv(H̲:u\"ܼi 5kFXsJJ!xSCD"A,9 6c{f⩳O=r™ Z( zbd-?ֺZ1LO+_nyUػ7>o}B"4 Ƥd6:zΣNΎ>423i__}s^zTsno4;(^x˕OJiae8&!l۶m]r'G଺p~cT_yV((&"`Hڷ:177"s=9zkH=s4P  ,- Auveiiyyqy4̽щ-{v?ȣ|3XXJ쮈BogWJ6ny?z☛K,:ů|_vk}߾yw}<} }ռEV92˯|O/Ș,%g5Uqv25Mne6  AIyD K䶜ԇFhRةg14ULL\ vuwpH RIRb S J鍌"۠(x!U,2DAEdX"T9sv暼-ǎO7ఀ dMN_W4?̅oj@ .+vjЖ!-01x`!nEG3Bɝ GcjjY,"* l0#sun,`3`rdsf&"W% JHkk'N9rƨ71cKWulE땦ulX!^ LN-j!i(j@%ZQtD@̔H(379y Q7,"D~_@es2#E3 ;)B EjamH mP!P eRc踴P`A4uD\ըe $ra"ALɵdCJg .jrrSu17,k(CۻuǖG&ʑ I(PX)5SFraaS6N=ZWq;Ogn۹sh=o;*}tީtߣy{޾yhdfllϦ=ySM3 N1R[_]:cYg-[woe Q_ wO;sj ;޸ifvvf(?qf}mmI T:PʺѯE9BH@oe FdFd222+9{=nx䦵5!JlVלL/...^XZ^Y\dם{#^Ogi׽>rlVXo}gFuXMI[).$kA &'"~ Qv| jP VWWNgq AkjrͲJ1P^0'h֢1kY'?p:fb%bd09;qrvwP.MB1DA wdebe7Lb2ˉXdbJr͡Dbэɜ{iJe7DLNDk2IjPjQjD8WƍNl`,}K}ՌDln+0CBlčw:[^/|t~ݟ؞}OlU0Ls,D@m p,A%" "8 Q cf,%[f&f@ AM$S6T͝ò!8`1ge1C5v㓛_v/ї?ٷxW:P7zP/פ&#bX%'fn:<ٙH-Q57u,j6bbꞥVQL+'TL !)(~Pԍ ܍4(kU2go&0f\%8L \g ܘYUqȬd"Y{2#22j6QQ)CՍIfSˤjJ3k]K,S2wJgdO.x)5 Om_qe7ONLoE<צ^ %WOxsgNK[^󇗾h^?_ffWVWϟ_x}KK{vο}zo{+۟;sB6lVח:奥n3A(kLvFFypյ׻ׂmpjs}_MN]?gp ϟ;r?qםw^~k&%h@&4::FB[`+@-"+<1`HMdi}ٶ9T:H?vjv%wGF7m۱Bؓ%Sp"\pAfv~dsj[I-[XJms 7A0E !Vՙ7i%*zY !rgYP74`"̌Ebf C,P(@d +S3af&8#3e1><ʢWϵPxv# p\siě;kf~H)p ꒍JEH hA̘L$zҞ,s`+)2;TLS(؍5Q6(Nڌ)W{4' HY@LМfO?`3aS o};w} |ն'_6}ί>? cV8*1qu"Ww+TiH`&dј٠YEjdf gV]%P,7@lYk &dRr$ &"ʬEdwҔ'f'r6M56V.,: `ddp@jDݽ)4`N& . R,ZY M$<@^p4B^U-)p@P7)յ8!@bg 6 Ƙ%sb7waq ᅺdX\4SyN1%icmŜb3s`Xjbn@ i`HV5S] z6ܠDI7QZTggu{JZ%ccc'f6Ln4inS1Neu]\ ,{Ҙҙ'N;zs.m;r~WVPow׮I u Ix۷lC, 2AH*Fi-]8u董ORYՑ2k  J+#pc%t2}HVD"", Ujh}&''o_7ovΜ{~/_{?2d~;_ 7>/[n6O9n,-qM~gw5ǧQiki{}W>ǎ>v"4![>'#u=75+/Ir8r g8~G|CN.-.-.al͔Wǧ'z\FjcYxuҗFmY$WJI3IIĨ.tn{!;#YA%T5MDLgj*oo nҹxxOY[>Ï.T/K)3u;(s?QOXsȉ}m?zWګv-͓3{w>9o:YUw{H"p5gnaFABCjD`9`bipaIEQ h`J@Dہ -p03l؄ܽ;'rS&b""YEԝ[BY9 PG o2"9NG,;Z`yd2Kh`Ȫm68Y4KP D &B4B4M AbrSk#BJ7YC!T3r&[(Q ErܘySJʌLں5.aTpDuB!-ȳdfBBj$}5_?_`ayۋ_۞79TU5HSӱ899b+gϝ;sܡGO[ﯧ7_wC_\8}wIMj,_~GVwyW\yßt۷?r _6M6NW_~J]g^W.???eU;\?7Aӛ>x̓U̝"M3~EYt&FNu_}:p|؉j|+uw[FÏ~/|HMo@D֙*+6>cg.ۗ9XԜJ.aDBMȘ0'䀁P#bS'&sj`Fá^?~2R奥'9t=P{ۻmeW\Էbέ3N.;3sG}kPMӳV[6>dpwfvSkf]HN2+Q0.ΐaB]݄3fFR " rfjDŽ!P,BN qHh.;26M +Gxo?t;{'N[Z;JP/QH}W:uf!:YRD9ل@RfM, MM`ǒԕAjbAMl 0"q=D1j#Q"!} dd yШE ==9m# Nfwy䶭yhyٗ_!z:i0p#W3\^=O,.Te־ft8~w|DŠӗn:[e߼z=&??{)4qxd~~WzL;7;y=a$Ρh91t3HrG U\g P9l B<61wP/,ON}⑇׫陭! _u5! C1* ͜]2.^ p0A-^Sv3bc5s >G^ ̉CnV9T7 ,5S'η;w8W@:[ onvR-S DZ6AՒS7MHQꙙHZ(ffD$k2w8`wRΞ\s ֪5թVI\ AQXU!fuaσRJ2ڝC sD '^),XH.Fl082y3cFRwX)|䮻r_ L=(KϼU/x+_6(KY__3gXV ; $l 2sPR jٲ\PسBZk41 \( 8 +-T8$#B&5B h?4SͲX 1SHaRzpvU9 ĠΪ &`g7o'0p\ié$@![f*ԫ̱@nnNV  $P(,"S"G(D%fi v&boE1 +~! -67#;EahtbQ2Uܨ U'K@bq;24RCpĘI RxF>0&I)PR3h*k7+omkoC p`}]WoO4v_m[wu__/Gw즫 Ãz/}}~]wnݶd(47lݼ+]ipح7~o]6kn ,vz}O>qLG459mˎ'6>612YY_9tas12JjNb]_9sݮ_ɷw;4:]¹b۾IDATlm]td\?{ӟə؛~==yփk!Lmۧf>3rFDQe@~C)*/N rӔb7a.B(P(90TWz}죟ۛ67?Vuz#'?/}tXBRO&Ne_=4x͏c.;_Ǟmҗ>˳e8pJ.ddg% 'W%;Z`ysun0hD6S6vm 힗܉ >6:926J!t~3X͜pךwP:h&ܞAdB,'DfHs"<lY2 ԖQ6V ..&3QCPck1o7Mc$&Pt[h!=*z#,96pvXZM/7_߅;at~ <(ՍǷnu#tמy ;W.\pbᱥg.9sVNMط曮.pǿo}{o ev(+ʬ#s7Ou~Fu/x̓ ˫_G^_/_-o nMWlvY vl>I–͓#M>GCCQ,3-@`mdJeYZ.;"xdxr/4 MWxM>c0XoVCsN?~O/wtÍcC]6m3LJF_ʗO.,lҩ>ݑZsuO^lPQ[8YTxdiՀҕ T&`2$p_2;ŲL!鹹zy~~h}s?qlA^Zݑ5ڤD0`d67i!?q| ] b>Z.%  $yn\;N) &DDL̀AP)L$a*%E[WM 5DyBi˟|[>' ɵ{.۲gmĦ 29w ù[_c?WSs%3ѧ?g&wSj{t_us[LO `ܩǏ?p}==߿GΞX3I 1"ln UMsӳ}Oӿ}#9}͞ͻ7o>u %m[?wwnԍo/?1>2kv';>443sPRwue{Amm.]"n#`쉓{wr9 M ^sk~΋ouO;>^L9P?/9u򩉑JeqZ3UMXn 9YAhK$ &A0]6,\;(‘`l J?1IhE]~֟?@?vg槖WF0=ڧ/Ï=dm4%*"MW|{Cj v`!Wc'Y\^A''I-F **;8fo Dde2 !:MN,T##KxI`l~B)Rf BdK1aՠmtX &v4Xfb'Ĕ5W !n85:2'X=~r(9oA];Ias.CѨ<*gͦ(B ej`F6S(n"$Zp=;9ynə%@,Qi&s5Uyc~Cfbj'ΡCN>X#CcSwxSȞ/9r7y'-Y\u].k(.6f/~"D `#޳o>7yG8Ν_ԩbuģصo܁/Ae( W?k^́lԖ`C4a|˗>ϝ[ɝH[d2;XĖ)g{ U;^ [8D3Zf; ۶o;Ǐ=QmN(N"IM2-;wm$&O{|Xv8H.yצ])7v1jp D2dUiȦ袖 pbd#Lvw!qvOΰίj,8eBLk[{g7Bg왛|/zF?-aɑ)fLl 1?wœ^]7uMW~?o}md' bbFZ^_n/FBPE )E,ټn"p67PnH9yDK`O 0%oET[(D#CEQ UӔź6"& #g6:5L%ZAE mt r"BzTuʟy_occ -;KQ 0ݮl,6ueH&oɵ)5&g@N,jF܀dH|579g7{e2 w˫+!J+tiqlp׾G?'ܻR `mSκYa1@d0cӅ$ 50jNCeg}w~}՛}j#ݱM=?K07=otPWۦ&G]=uӋX\yzB݈`J&R<}'O~t6-w,_%9S ;g/۹Zp٩:\3Ru+Ipv1bqd8GlYb26^E,Cjee "ku9T5uB`dsO kpTْ5;T !TjܯbA VMf$E(4Q_EYX{nEX\L0`ԛݿMk $A5Cv!Uv0X$UZt PE&NpE X`Y D0d ϸ$gL'] eᲶY5$k,LnPcU-;.m>|Щ6`rQ۫67G6 'RW!8X_x[kc#MzYvu e7LI*RH`Z`D"@ќ;`1Br&O%A$U#3sf#BT3we8sn8dMw$Hl1dM¡Q"CJQ-l"(;]I@f^JYXW='ʎ(DN,QL5nN9y}M/:[bA y`Wj5/~ꉭ'4e{3TO{]Xgf6?9ifr|vHj̹#s_z:x[v瞭R`p mg-_ܺg%brޱwVƅ5JNb Sp#4:M{^7/bn,bdt۷/t{ooݽ?t9W߿K.5~0085E&g>C'qRel0B[05 o} >oXZ3Ay^P'YBC;OK0ԥz_ͯ{kDhڟ+SP3U  9 *nL.v9XZ ޞEpgfa"-̝%ȪD 8,![$ atbu5> ER345{<(rT78l{֓ygV+9 $ e%s2N@Q8;̬%1i3 s;S~pZSAP_o߶Czo߼푻>X>tȡC>|i356<1m?W؎sݡ.rZn..k7s`RDsv ,oھ~汢mpQU><5:LhWX({nQ|uO Vݻ"s3WvUg2k`1bVQ 4?USFֹMCCu`3`h e@ZwAqຩփյmvVhq""FcPtzP`$Z1;zbl9)8^)FR\C=&uJ;q<f&$#H0hn}7ky~P^Y8s/8p؅.%/{3^O-[fg>??H9tĿ˛~ݷ\w~lw_:eRUܱ{[~^mu^Z~u?7ϯ<3YzvG8mA -!gRMg<׽S\zٵ^p7LM&33_Ե^+⚳_{SO=~˟ӞwE!$ln[fH5MO\v ݲkfnTrࡇ0af1-Q-% t̅=\g\Co5kktWh(!5V*~Qٖ u"]gESGYvwϼo/*06:l^>w$LD Qћ?ٷe[ ֚7 o~m!tD"Fz/|Oit 81kBUc91I) Lf1v}%*PReDƨk'XCeYL#z R59svM-)fX(ѵ E nQ6Z7E`e%59 !9Y*RĦBEZDIBc1 X6$ hUxW3fdYumG2y}uS%#)25p.Iݍ {'zB02A8圻eSloFPR" Ed3L!8M(ԆC#!R p 18$t+U7` X ,029@ A-S* xݒ)+Guɼ6+92EՖc,As1X=ݥbo$`,E[5A@P3rĝteFG=cO>u`Y^oxlIW>5Vu g=E_keu;|سʫ={??"ujVG;C_}o^}Cm5wm۶y{'ƊMgNNE760̴nG'j6+OʙK;C&㾑᫞{;su.ʤkf X4 C C̞/ZNyv֙y18Ԍ.d`3&$}^ᅀF (ԔA üX;8kWNmp7caq$GQ;X[~▧\y&FS3@P-;~$tnў}>l h`Er)BHe,?}fvz'_ =Ȑ6I$*8sL< :kzJ4qkrV{|.]WFf2Uo$fn!k"uSuaC*،!eIδY J`B@FQYb",&3"br9I<Ɗr;`J"T (- gB&לA "usY&BN"w_^|$PX_} W[8A$[ wNH8(Nm[f`4pd eXV(HEյG&rw(XTeL-$!EE:CAMG{WG?cOif&Z]nrdn6/~s7+^}O5 gWr;=5&Ƨv_ris1Hkɳ"fqGilHCm]͹2"@'XI8%ddbGQp!7S+^Q@1!k2,?n!'nΙp Toȕ@’̹,("ƔRdNH$⬜#sH8Z9@l`"WWVʮ it` a3cL6=9կ֟qť_֟|Z>ݛu=r㿏Mw(O\}33n0>1unh=sf^8-^}ضh{nxHgG :xSǟg?SY0)\ nJsfʖ%PC+ӻ>rOO=}z~vnbr|Z丸raz/|ee9+bq3-dYC@`nu7/l#٭;42 q@RF5'g&&6XXHI!k],csV 9IȌ8gZ#^&I m<˿ `|gU#F ]g2+`'VwH%"_ D;1%]@ @`@$Ꭰ"e3!&NO:m ,^ppW.$eLJ J*U 늢Mjtݳ*dB':Qraoܡ\4DuS9[PVb"%ECTǮ5Ird6h4\e.M ^3G8$k,NNSq"bRC'[0.;]f@9ܻ,;dH=ҫ^~sn[:{p7Y6ggZwf."#Y&A5#gz@ hMLS1y"hBKÙSj-[ fMs K*Uf]) ϯ?K13)ݢf:ox| >+';;&o C+,Bo|qW\697dZ5rWtBsCж2ad`]2K0eg 1(y`$WPp ?x LEmOQiunEs,nLfhC#)E* R(,' Lb3B8q61%x[$$\:̒9" *ݔt1nllYS-8e͕/k67=[SpP"[OÎKk;F:]M-?9gwg]~巿)৤wLnR[6zխh`_c{vnϹ \{Kwݴed5;=|?~S KLw=y<ΜrxQtf"u""0spw9uzO28;=7=;E!U !fXח/,M*}N: #1hLk+'LoٶmK/y+DfWcbwgshV0(;pi4FmڎAIܓU8Y3&rB;#ZݥU SnwK?]  D<ՊƇݴ^#ܘC~J'wg0Y!RtE ,2@)A E $)\ꞒM &g&zKiӹƛW8bdS99bp7:"q4@1:Rk3{ N\f\M9! Jmk.@Y5 CE&rز  Z6S(,DwuӲۭsFn׈@Y45a\WV^WQ2&T_w/M U36|ѥT%U}֚sp B%cz݇^"F/.q"zpH7}duu׭Gz_W-^=@@!ֵ+VFJJ m)DFHkss̪@o*D) -!7"L-ĖNU1NIleue5BG@ „sJ=*䝁\L% GHк&MB&yϊIFV,A$tETLLn1'ڬ))\tA-+kD"(5IqZV/^\޶{Rx.A A>O;|Goof4M<|~?zMז.~ 7n?XՃY߱my/y]>^Gxiˍw9Ͻ鋗Ν?y{75 ,, :}۷#۪g\[~Wf^;>iT1EK$bSP9@P/l0vI;EoY<4r>TM=8ykeXGKʔA,bl)؊uM3ȹ+b&TpnLJLf'sĸ8 URR"&eHXwJAհ[_Qo*ԝt~Sw9#@1ypȷ~X].3N,A\,'as'#H;yx/lRߡpz 0D230ҿAs݈RPrhRH3sͩ~'{0?(AT]RVV_ew[σeyHj812v&)8N{Po%y]uL; Ȩ_A"D02JGTe0+1DFIؽ';:KHkBqDDMi'`;G9ƽ@fM[*U.9A")UU)s.Ez`J+ TTjUJ֜b4ikyj9JJIHm&6kq7SbqyGY!1*m9p?{kOӃ;W/}7\4nWvlZ5z۟|/S6=9tjWM2/,=Ͽs̉Ͼ?9Z^znƛ_/;p[Ex^:uҶ O>voΜ5gV$[;H("U̐܎9Z+OM;x\o~ObGicw㶙 NXDVq3y4L#'p-,R pڅ1+̭gsbX(QM6# 0%Ɯ~xfy–{w}}u+>(ٜ p#?ܶF5sx-_x /?x̌2q!ܙ-̾J%Mlzd:,ܮ7_uxօrȓIxɧ/}}%<[`U ꦓ-_|3C{wsܹuox[o︲x90O>Sr,m#@(|H``%w w~{Wmזguaim4jGmJMBƒRPNU!XFH,>]W$IjSфC_7N\Q!.=&f୷MT^"*RLij-C)"Z M#, Y=RG %(#1Y♳~vŗ.- 9bi c뷋ff834zٝ11gcw896yriӔ&=#BF,p SOSx{{Rټixoط3gOj06Kxt&4\ţkJ]Y`\ n]i',UJfNDHߍMu3R8qgN( *IBRR(g#P u4`)riR:U$I"|_۵ufjKcMoV22yxff բ*3ffA->wC7xsWZ"0.@h4~]:_oٳkק>IeAY.\W‰^_4Z][Ͻ9?Çvڵu<޺m뗾-ӮǣA '-œ`&XTm?ʭ[wܳk랃M=A x~ >}''T] ,F)}? y/6kϚ&]TMv?8[S+ٱcaн;x}l o]][w;[ܺq#nU3 F׉` P6g&/p/JdR6S|G^Uc$3/ZS + Ϝ|νDw+*H)@6ujgd3 r.ĄvTnBK+PsԃuؠIqppe3(N d@g%UNܙk#R0U+-0ɡ#z$g$ĥiXH 蛅`۠JUI:drMF7D¤ʹ˿+aǡ/z+/ l 1=K8 !.a*b F0X_ I &諒,Hp(@]zm/uDXD{O| n3'o`K\FQg.&n眉U4 qr&(e( YkDTq㑸#8!˜Qa3AfnVLx.J6VA)J1ZhCq #DZU]`RBbOrj*`PK.D"0}Zr]%rA`*E9Y=39!*#r\NI9甪jET 'ϓ$5N?x 1,^w]W}%_5_7fՋW?t^^\ۺe7_ytޝs]=y邭wQ,k+˅Sb24>B{"Pǟ91.^/Zfu7"|"@Q7Usl~醗ڥ ;wܾS{ڽ])o\e/W/_.Ir+%tdf!p 8")감JƏbmm,*, sr[P (I`7 ]h3K sM  ) <½,z2B݉p/!_%s_[>Ūj6:t߶ooz[WV>~i#[wlb}5)OfdvtBA*( eM1+^1qXvRڼήVX#A֣o$,O'0_onڲsx}q?h{ 0pWWS*R`Q(`$#0U-v-UV f]U33sb<"kJ)Cr7TZպQjs몙N&հt s7 Πb<,y[ͦdJIR.f1 tܐf@"襬@p47HS 궝٫'N{ۮp>즹_/ [pӑ÷^[|!QqD/R4!ucQgka#" Dͬf>\̆bg%GlF#³pNmfC"j-;vOs}3O=@~)\(^j=r/>G |-̅Kz/z+ UGC@jwn깢D֟8݋P`P 3"\"@qJم9ٽeŽٓ'?6Γn. PtV!]T#kTjTuBsO+ш rp/a咤.uX)) $n 4QEԒt@.p KIn 7&"Ռ\t`[+ *Մ9Zo>;[3/Ȝ*98_6z8t/p8I1M+ϕZ5v\rAH8eG;ZO뒤AUN9E7P6 $83zf?<:眘ͣ 1\X(sqhb")%֎i D' MD-9(2:F.J(jVGzn>UZ+SGk?}w~M+aS?{x;E0DxC=9}*b7:1w" PPv~lmBP '~W~|e3̘NZ3  f J̣n:Y^zg[R7"daf8;p߼0ma훒TXDP\E2ٜV&f6{$1Y/iK|Y$+r 2kܬR0O*lTkjs榲(ŅHXrD*58#]QfOBv Ԗiu<]$U"+`d'"gwGXnڻ'¹+}Бnk˯;{f0 6ʢO Tⴻp"+A\w6͓~3oZTmHPr.6oVn#@`氰QR?b~Q`?ёy6gx*^GVW@A RjXnĭ+%pa}"Ul۶D @" 7_\BQ( ` )b?."nF(VzAh)Wk}%}kW{ݎG/3ʨt! 'RRͿǿ_i~ ?rzֹkO_>uO|{Ҧ- y(6h𥯠nyn~fȖh`j<; W]:$&Wd4.,H5+7o_bHb UxniXGZTvMsKYfigP#2V)t1i%b7iA5-Ѷ]S) ֥P2UHsٴjTrNMJ;វ:'*aY*޹TJFI)]Se "w0D$ao-jdTZX /,}ػF@܍,^o?{]_tuэ6*=[`D&3Y &8%Di@"A Bƀȣrf ΀}|~.!"e /i*DaZ 6):ז?u'yصWv,p⋮;|x=;oO ^e+K;J(gaeod6]xk2\8{//iFRrfgf Y(NԤzC0Di30*1bVjXP8؈DGH9EvIM]ij8`%*NnpIԑ$"Ldi{emBsk TgU!ވXբ.ܵDUݹQu&}_wF#^z-%%:iMU$Ε=Z*ur1 %I`RN\w.i婲tvqmu͌!L.3]da뎝&WO>/y` eNfIifO^:p\: Vnٺ=ܻ^hz_qG|KcC#Q QB &i؈pqwH5g0MYیa݃`(pU ֠8{JrFXF# E[\1Ń/8610IO'aAL6w%1zlv}/>g2pX0ۆC=jNaU3k'-.^"}[V5gO~ϟPIBB]ŸmnYG&ۺua(9S'2¯_uc7ZN U_j'\pJxa^29ggn=3sGv޵mZm2nƬL@U>~׻?zŕ$J1Gby܃( gq*@Y4N WbB\(UE3 UkU9Dy-f38kiW䮀Ri˥99 gmeg+M*-4g94O baӶmjp RUA١있O)$J.1{Xj8cI`._x=??/~Tsi}-w`=/ɺ{0wsyRLz_CJq,"lCoYoTm{CLJhLIt nj`&x}׮]ԓMo_C}۷nZ ɸ䕫Lf\n<:{PܙZou@qզ߳i7-سg֭ [7 gJq++6vZڱ3pF#8A?qtD$s7xTC+,.$z}2n2(H"ZhpձU}2,3I,QKչiTL1)E@m[$Ps RjbX@m"5w2)({,=*#5$1dbM{F 7֟~O'J<4^'vlKDi~D1΅ G>^};VDvn}a-XY;0ôgpx)(T): f|2=0I<&㖠^#;_?E!;F$ʥ*Vrʔ*~+M _XG?ӟ {PY_[?r}+7ONvԎya<$Hp\{%\ʠuჇhdHVJ-[n߾wnio&# s)kWWz?̪s! ę@ 1Xdwf؜0{XU e\(5Gf:rhxJDkIÂ٬#/> M9&Q8aG!ӖIjK65r۶+#+"U3]M3ܩqS'fP pꅪzhEEܜhn!)m߷{׾y9{V$=DLCD }bmEIR81,;,B_dz=GQ0e%w%J'efvS)es%9)pDR9SJ !$b,oYe QX8x ("2PY%vF[ٛ yafy,~>3k߁8tr?Diҟ{;v6٦o;gO<~g?ÃO=޷>ч?ť 35 gSqZ]_JLVVQQ݈,NB@t83-_/ٹP/HEeu;?S8eҦaYKd֡ԓTjeJkws_B]b3Kp$w7߻g^~ăf:q3 o+ v8o3(Uyt."IP1@ÙĉW3>zP ?y{^tN>d[3\ޯxSkO_KǞ|7qrvlg:j_i;f7'/ʬ\ׂY0|2F#>> omfVwܲyf*¦VbWFp~a|1*9g7;ج/rr08sH GB9uQB*H-BU)6W!YUE]Mɥ,au A*њrb34[W@Ζk)i `L{kExBt0DđO ."S)<ǬHs!f0cZp̧~OO~/8}KoO|ⶽ{g~dmfvv vҮ~ع?ēg._42CGپy>[ud<] Jnfԏf"B+[r0Á:ɮE/ؓPE)}=4NGT)I3 LT*x2d_*N2 9<# +JS((DT7۶23;!D(NR7`f YJe&ƥK,$f9(!a N,l}G6w,編}ܻg $ݬW*=!:E0(Õ8%E婿qRvb 7r6v S3&gn:O%;U@0Q?))2}JpSp!G0'[ʀ Yb2m%fH"S"۠{7L@gH~G+k{Cy2fvk9˦7|L|}ז1s8ut,0c}[v~{헖\a57r{?'N}?C7pyi}A"PYL$M7ӷ||}}ܿ~y WMZͱ'N]@U;^V[Hx}mtCpw޺n:pp4t&#ڕ% `a6ssqmiTsI53Y1 "UbcpI9,JtR5uή /NfLaQdѶaAu FJ1*tI*dAdԧXD;*`X_ϖ5UF0%f8pJTIZr7`26n}̦tLX\?v$1.ܶQ<͍zRj#?t_~:N[". W ƒ{#@̝e>x,>GD2[2gE'9)u=;;ٟp/.>U;mȒy7tO\={ϼG+WmYUma{~}{^Wd}ru'}܉ϭ,s-;XWA22r""`Mb/P.^6@"a?B̐I ˚}赥|S#.HB8JR`0̂) X̺\IR t*"g~ ԲQun ,}|*lʼnż9MXDo8a}eu-Ϝx,Hs  ({~vGH/`rA`6x#I3%a3b7Jpq0-8UAPgٽ)SOn$G! f9+ų`i U;Y /E íʌ 3'}?rHD%o9`PF;z[Jjit ~?ϝ?,<[iw_={ffva~µ+W~g}^)x_rdˮ:n,l^yrM$M\k1OڵŹ9&qa% }|c&fKդs'Nk%Gq lExefL4'-'8 {L:f2x6I:$ %q"% B9OarJJɝnjmݠ,9 ? ȹ- Z.u Ip+3L*8sHuDy<.FV.\>re}<'c gIރL9 b6ޏwo~ /y "GH0DAœ= CLΊgLPUa]53N_{?_+ά.+bgk\5u37Ggg>vut-i{f_|s]T뛿9'}ٽl];Jۖ.ZffP<@uwIIӤ #LFfnv6%{GoVdy}~üTdcK T%T%PRX znEf]QTXh$]XWi0˥kY)99ef{)+ z80(@ 7g\Τ`c2Y6pgSCu,=п ^z502a(#Hx³/  $] $f=>y05IbZi9wd!DPQkSR*겨F="J3[L1Tb{Sjhͼ :Rpg&PM){qGhBIM=]I&Tۋo|p_s\&+z;m޳ htVrty2򍯇OgΟ;~nveŕ*UrӝJ;-.VB]וI׭^rJP#2a@L8CGM3#AC/.%©Q ]$J{M+ mRNEp62VґPh6TEMєK /TQ,y,2YH h,x֎R% 4)neIXT%1(2L')Y25W!p*L|vf~:֪fC xxLM+J~,arb"0!~ܺx~Q8m>{G?'ٶu[=I۵{hTKR]]5*aLra w~v~O<++g?fֹp"@xqQʸAӷ{;NjM.Wmz[nyym՚bRDF+QEanN̉ҔO?ovC;>q'nK/Y<]no캼q6$Z%lʂp#HJ?'H=O=y4pD09$JFKD &aŤR! 0$`ڟ!U eY[(;ɉ""("UsĥZUĞpd9HĻ.9f ׻/<EO 0ƶ N4DjabbBBpr7v8#9w\ _x_*D^fan3XɝSrܠDnp ,9‹6 F}" 8X;y Cf_l%È܍&i R3h3M>mY WW4u "Iu2׳Ϳ}M w [-OkNi…O;{=3UozK;f/_:6(P܅ ~~(*%f0vMK,lR&RR5+T3jycz4iH܍jpKQƥ|/z^=w;wh7u˭)5ǓE)&V n5s8{sgJ(8*6C\uu6Vph x07+U]9 2 X4*];Rx笙A]W4Kus hDVrqBScPZ-_vq,y_` a@0[n e a7>85*Wgs?#?/{uf~?Cru7Wח_}§S]ĪDH JTy;7~D)YTXWC󍞒;h&c0Ps?UPP olݘOSp?`&cp'-o37{b 8G{ћo5 @OmPNo╯GgNxsOc=}~9;~Go_^[ '=׿cm߲iF)qX 13؝@f'BPuOWV9T';K_9F)(sPbS$)an _G!~)̰Ѫ4u)nJ)S!Sd抔j.D ֬Uu”4n38 ^4!Jp.QZy0hZ 9$+mZI̼uV uRJRL(qonc2n Ғpsg"b aԨTȣsWԌI C7` 0I 3ҪrUCЭMW|x+_k.6]{9wi#ཟSxbݻ6M -T\]\vmqiieu%e,񾣇on8zlեW:AD[U7Ri$xd3NSUGDHYڥU6 JsʅzWsW.^luυr^GY-Nֺm{ҙ'Ν<믻 y>dRPxvAT䥘<É#z6IDAT'BB읃b}Iƍ%r]R5<&+"fNevUX)MM Q" fbX*])P "ٝtyʒĘ;bX$5š=#gd9yd,]R"4XHVb§qJ<@p19yI'vc!(&G Q2" ( XU={+oGć??_;+gO_/ݵgGgN+SOFg0C<6">iq\ɅLwgؖ*'a iB@(1e7rsgB.D)w7UUWUnȲyώP* WUNE>~pFh=?okkp)Z1?ɿȿtu+_E/Z:_&]ɏ/rĬ./+sVĚ< b+K;lgd<&vBuޡ0sOAV'JvO^)R"Al%6E$ 9BG>"'EUjfNZ'e3̮\<#{{c?SLg#AJL(0hRI^I).w’ئS[9|>1CEw4>& jD2qE҅yVQSe9 rʩ{dE&dRJP綰0SҰ̉ មΞ610sG;L3F U-LXS0*G#`0.+Bif%UY[^ZpرOSgϝ|3{Iݻ}{^_5uUGz-Gku/xW}45pN'k}<|;O ',˥{[_7O#S8‘#̬0DDzU5ܝAQWNnG]oA &zyx6fh(Q bbMu]%RaĚ`f&u1ys{ϟtk0 %)FQ듕˛w^_-* i6wZ5h@#<%iQb6:$SW}>t : 1)+L3U 7֜D(]!A;-Gkf 5yU]ѦUq;QX+mc4@UM_8\jƃZg֊#B%Qε~;eA=>ne4vy|9׺|-;Z`´8y=^G>/WyދСF1s]Y)vfv6;J892sh1cf%K2GP^ 0S)ԓ$zNBឌ"Qv &vTo: ̏FM7M.?y'|Ξ>ue>Сo{ݿv]Zr:/{ŧ>k xQn{'m,K^u1oW.][]9]T^:a \[arj oj& -QJݤ@Qi2JlUkyud(Km'г5~k w{U*c(.R'1ӀX )uwQ"È*UjeinքHRj)ŠƵ+Q0€V܅RsϮ|{<?}L%$l ;ng>/}[DL'xw=/y[]sa۞O_umoܱuáŕḴ׬\f c"V!U'b 2fO(ͲpQ5xڙgΜ?c猚?&}˷}o6,O';}9׮ɷ}_pX A܌Y2܋T.+_fرkMAw2fsWO?пO'G?_~b8HϒGf1pHoQ8##DAs݃qz\u,{/ #rE7’_#ΦZ=ii=c9{e"TUA ;4E>8u\5avigʉݒt9D~Sg!Eh*]N@k-̰B 3K^8OJPuS'!^YƢ3-'D&[DYvbT#܄fbdIR[<.}',8MTCUzuYTq[FC1 ,oxoߥ;g|S@ ۖ9{v^_W^ܪ|,il`@ޑ" `nZkV\Z:O?75f^\>y|Ϟ 0Awg=뉧-?w_.]߾?p o᰷S՗åE4ܩ&Y'ȳ` ^YZ~|iD>I,FV#Cnܶ׽yώ%Z<;!S "1F!yb@5HE"G?뮼[̾.پ[nzϿ݅f6:>$E&UeeEv`'?tk""T+|c'͜9_qstC~կ(L5 c#T0U0= Ĵ# JDXH XjO*(*ULء9@&6JŀzRC (ѱV]rif|KϞ<3}`?F]` fbR *iBb;f!fPBpTh:*e}ԊRXV<ȎJl4ck7jLLQFË&Ae9r`(h1s-IC73DJ.I@B̠L{pDȜˈJ}iL"94 2$#FH#k; O8szɍEQ a Z(V/3VnT\7W覭 3'.zns﮻o|̾e=_oN}|͞]۾.=Luۢ#sHēDDPk@A(-ST(AC@R7"ZĎh,k@yC<ľ~C/.:-juvƟ}G;)f: n~n7b0=&GY=vٙ~5vnݾuVo޲k_=ߝ`5$CVIRl6@@Q `ʜiL;OIr(U%eITyK5 Fu@@^21.dj;Tfc&2LHCW3v\U"y$IiZV3˺  X`&@$(FDrŚ#]od42PPc]U&?7^q߽: :w,qwvU ~D G_jF& IBZJԈIdf> Λf;ݵe{us^yU-L{gj!BESYڥWxF~YB"V4BiTĊqWYw|o/~_ڋnCӛ6IwvV̈́'U!!P]?û6m}7̩ kW^9z/|/"LtBJnˎgfYyy#?Tj![ TmrTUYUa\cq# #"$]c^~! vsԃ}:2j^|Q{Ӟo:n>u'ODVXDyUۛygnq}O?uF>{*l$- Vp/E+kaI2vI %*,!e*ɹQ%3T LXE#[Y4A5:bDy0b`Vb*`$f%V$̇R06B013/~e!񎽑Rvtlje|e݆M3Hor nÇNo䚑GOT_>~Yg%PŊduP@ bMDS.T] 7rΞ=̾'<8;;+Vm|/xÛn^q˲jǎ hٳ̆ n, @F讼(€*=˥'N?~O ͕!mX+o+LuZD*aޠ藋(CN(uМcr )s oOvH Q\D2M 5%dNس:@$&T@͡AP8GRDPbUqkpD ] @D]-Y5l%18.cK UٹUcEY8G@Ă0C6.pZ1kSgαpy_}}^zmo/b/JsRen,k-~9菍Ob3OR@e!U$A  ˲DF4@VC?o|_]y˯[7O̓O{~zU}5ktACM=~翾+w}'?kkni3{Z*7[bF<79:xAQM5l˚mi/6 7}׿} nz~CVO/ FJè(3356~n_\4_??9_ V|~K_9/֪r0wy˟w|kyE{/^ih}ֻ~?1ՐC+133Dٞ[J$sYL({o`L-BUV231Rsl$aY!@ZƠ)729՟=ʟz=T8wlgtPLO]ZD` mZ2tD1Těk}Os]{7=x.}5+3, bq! !-U┼]櫡APcdϚQJb*YbJz4 #!hU)y1(c,Rę5ђAi9 Y^U&TV(o4j,ABXYOg-"*-#MD$TZiy7ɻ<³9ylتÕ6.[m%_^|+[ヹu+<0Țݹz>[n9:ɭ$ 3RD4͘REURUEdfd `VJN$g& Ce ={}wA11=fͦ=ϾE?NGF:V/KfCϸ,Xb06cSa@$BBkݯ<}3=>ulٺcnѪuWPBeQz]F[G&HnťU Q10fP"Qq`bD蕢%2qDcA1@f"6'0%|T1rx[_woܴ~ݺdUR,u\q|_@`g"Fk.^}yN;|ˮ}gG! B#FPv67{yrnz#c~}PHF;MOEKÁBd򥖸l|",ɩ'go_ͳz&5x??o%WWb(?+''?՟:~? }[ Vv\& IM12xUԖ c c!h7[o?޸eˎzݥ:?O:ʤ*兗llCދ_^vt۔Q9oX@2R;b&W:_{+^*_m/nyXԺ¹'ce5 PRAPj.*KV(##p堪ڞM#)*U t !$2*Ы zj1 +1dSr+&!xn悵{nwKmoZrU?aKZD`P9:j&nSHD1ԍs@ d,yoMS/b{_o޹wݪUce9(APU`8,5IALq؍1dJ|{p$i t'O]{o%oY3=192{߫bgXJ="pu9  p|[@:Ҁb-1GL1 G)!f{X+V4R3bDEHP͂4*d2(13 TUƊb9 `PPHd@A14h4FLyK/-#4ud.4 ar ™s_;vبo-o~},@(& c~ GV bBӷB'`權\  G. "x$"L+ ZNٓGвtaGC=q< t[W6޵n?uΟ\9b z`x陳_S{Vfʐ S*׬ڴfzJs5aX6h3|=3w598B\I0[ ϿmOۏ9Ž!Ecf+J܈ޑ[᥹@LIU͈mEDOv̙:n~m`E[lԹaLsI@ &"Huڠ `PTg "EO+VLU 7~w?z{}Yu[9wu' Zy+̢uVO5/~޻m#j؊i@ȸy9%!C-ӫ kW]. gΰ& HC=0yZW1{JAR* l*.rJ,}JzrBJ@cfNɔ/c遂j$ ȫX"߰$)ErE6f;2Z@pb$"ʲ̘#h 2?u %'JBY2CK hΈH̓':2 Lj@ Ue!/˒HB!g BQ"̴4s;IPƲjrCY,ɓ%QDxvXhD29gCSr̉&5.!OXai~~bbx߽w?cn$ TS20UPTd@O6y?aWp^g={v} x˧}ߚxދ#cNgD6A`ˈrT:|C?< &` /:j7b7qY~ܙYrN= bŒfR|v7#j6hKɋ:}461OUkt+_ڳV+LuՆ S0#,BڵP@SO?x-Қ[WfE{?)Adc#U%YHQg3b>~}?hm۾37ڻ`D3NU#h旆"'S#;vm_ )cccK=ǡRui5?3C۵uc{mݾW7V:dpٝ>-D4W&Fa Ȟ%dVD822u̵REReCB>+5CARUqphWPl09 @AS)! tEQ7H+CRȸ1fQ ;0 cn~vaf0g\N^z͞]c'qiiчK.ƌѪJ;#W P ԛ P=gdɔ AA4F dƃ~ ( nomkoګ1s% U ^%MZ!K<el8!r3fjAUĝpVqc  Q$9tX!x "4,F%G AJ#c!;bUęz dЀ 8Ye*QoHUJjJ,Rs@)hQ;@"#rXAHLNTF%`Cb廷c!)J Tk 8S@.NxlK̀}{~ F^vѭ FyzE4Z*s9s,`B8q3[/HDF>̳ǃ b͊2+̳HLPALAPV+._W| 5@d* &# BJR13 %+?xDe,TU3w~ы/b-غ{농DZ`+mخ**! <; 0eO<^HgV\?kw[YN@&`F1ظiͯw?M` 4}nxyPʉ=+{~H* 3g@1]Ѭ!䚤 X@3E(D-V̳OQ $+).j"D5ʬH4.B)%cFFY Eh{p4o5E~聋pӫֵGjRșr)Vj&@5`R@D"!$m82v J%@<,+@5 ѫ8`?ܳɇ/Y+5>(eBd@RDgPơ>VB%b@PCJ1ޫ(ךF`u% H0&P6;1IpMjIfK L"&DD҈FβR"hbV` uuCRNj#I|,`H) @S 4,}!VHBHZ`a9pRQ/3!;lgx5SvYgf`U6aA34! -$Qd5 ^l4#>G=|b!?xO4c 1{|7_8{gPA'W'㉕?`>?O@211[W_対'9r6޺z+P9;;3_ /v^sٖ'odN)Hmbރ"&$rddb=cB䉢H뙣OS?Y"0 ̦ f-y]9/ O/bb6@^H{\ZffnoÛ&'W@*? k>x>~Fe;Q=e/tzʴR 7O0><z]/}LL1G$ 'ϮvM ͧ(@2')1): ٯsnNs=iϾӧ;hBF-g sf4 %&y4eGŽ%F#HնUeJ SH TIBChʞ5%K)VsD0Js V2eRTsٌDB&PE93'1Ŕ2,`*KD %VfEpˢ0Z _1B3 eC\T U1k^ kAj8ם`䙼oSnܹ)k4RJj$M)*FvCYy5 v_z=UR,JM vl.5C  f۷N:x0e6n9?p=xnLwld1==(;l }o?ɿ} _zͫϜ?GOg;6m: ̖5~VRt34Kh _Sow Mj'JvT82㥹 y럿so?Gܵg{=0,-M+]eU ?,-,1H2UH, A!Bkm8uѣrӬ{ΐ|ԙ!㑐;" QUwn~捰0yёO=7GFZ@XĈXKΗv]kc*D,gZ앃l40!(`jeYU"W09{BP4HA$Ȉ.ȧke7vjt:5L A)) 8Ti7IT+2b9EM?fi2G}h&=$3dC1- #U\g*OP{g/]7eUEUphal:Du1DK1%b4䍘 C xɧ=-i`?Y=2lҬoGd1"c@<_O;|jfq~ѺkƦVWoXije j-F J XA1P2Բr₣L()82$X k#  ɲ^CsEY &HL LHJM.1eDIV`ԑA33N,;Q !6ټ26AyӔY$MlY59:jhʕk~ßn7-4j8='y#zGsaG YNHc*׬|qbpk}9׽oK.u>w=jTrx>r 7o/9^55qo}>rl,󇏝z/λ>_Fî׉ݿIwv1i͟^+u.!zHE F(˧KUQN UX@ -+(0C%DqEUcVȅӳVcnPsuBLUC2fcSɏ6Wnk_K Pv/}KŁw% sKӧOܲjSp_|%w,:ڐ+&'O>Ћ^9V 6%R;=!*DDP;P>d|_R-IdÉ!($cdB0)"$ $pHuxckw%"(_3!R)s.111x2MF}2$X*4DՠH$B`  p9Be(,C-:DhQ38bQ w(BΣANF8"Tf_S%EZm+(Er~e98f=gŹуxe-a"S22r ^/u|5S6nߑX|λJ+b4FL(zSk+\q6(ll4L=ԁ<~iV^BF q@AE k~i-RYTTYQQUZiֽhL-(\2{bU?29Sm^q;>?^7RY j0omju?Bw᥯SDl6%D$XrMPA(Dt c 88BRS+4DF_1޲$;ړ (1)!k$yH,UMX)hD8 AZt h$Eg{J,`<&*YLx 1ELQ" Mʈ}5"630I*ѐعxia7Uskm?z|Wl*V9O( o.3cdSgSbϻjύYX(;9=5-E/ڽ6:26?XFمŌLH@ $)Vq|M_}ɺ`&|^^=_3KyҵϾlǙF?{o5׬Xٶij'+WN,g<j63(R%{g_8?pa+7pffiĝvs #_~/] _=O|[u~=o_~駏?kӊU"亚UA4yBVQ@G5TT фDb\Fyqr芑g=?o۔;;3|!RR,r};6B2&ĴNf̫?jegtN+u:+V<H822}˯zأp'!uATF ,c.:af"wyuoh@@HZV*JxD\vtg;plo=jTDA AN!E2$W3|$RT5yheqjFvW㧊j9;LZ A0rC*NDUEA C9s|5jtbfbB.8!Ub;) ޢUfQjeB˝$d&2 bB/* * KҼPY1S8oqࢯɘ>|@~]"b i^^| ~odllc{vu}_Z?^;=ztE_wyN+֬Wފ> c|O>so\}nkj1:̣I? ?'W}oPB())j{l(xuffdc;jFMoeԟl} t ޻1&˓?w#o?-[}V*Xփ2ΌUȈ `ǞyrgN_wCGx(UP d)hΫ :*.*J{tt|(jH.̙X+V c缁äJ ƄHU&F%  W !f]"eOI,q!wV!Ќ<}RSQ|9Bn7 ggYbXW75+:Ǐ'jq:" ̈́cHӯ^vc/:;~p5+{죜uSNw&WNJ.9wkc4-61/ f`v1G1#V_};퉏_]ݾz]^tܴn޵kճ.8i㮝>e/kl[ksq'`Q6 Gz1 6pcrnX{ɖu+whۮ/zh1.;;KeO?&8812GEM$5 D̽~b=xϝ=c{ٹa~m=];5c_GTb3Z~p+Q PYFnعh_"x"%bԔ3b^ַAol]tç?΍捨F(L>UF Ik^21E )]{k|6[g(ԆGp(gUPc1UU LDZ)TJdA0DiLHh NW jL!a!TÂ. !*s0&C5d6;KbHR  yOZ0 D-)w 쒒Qe)*e!cT %z$TRv}*)9!-,na:ĥK3D* 9U28sKfO۱⧞wW_tM8N:| ]~;a*'[nr[wnݴe^IJCXnXdz5}<DΚ۞ G'$F@(;z6t\c )!&4V&%by5rBՠ"UCB`N`Vis*K,!!&Q0LUd'^8!j)JDdOQLDX\IEDLK hhb`pfnɴNP$f `j +\<[zk޲SӫΜ1^߲}_~hQ&Ow/}ud]o[[E,\- A4Ha1+Fذfk׽m]ѢRϜ=~{}'>|Gڍ{ShL䲬C"yC%K^q?Ƀ߿ssD 4x/];es/"k=hݐ]f(۹}ˍT bXJtMP@ D2\1:BU"\QN0R҄8wVČ}fR@}kF+/*Q@1Lbt@XGCτL%ULdHM&:rS*$蜫D Щ$8HBw9!"k<)K~tj] " , t`F6Z/};[nZgVMNvbܑK r$,zц/}NXՑm-tkϽrhd[*ϝ;u3:ҳ/qg_U|{D|@bg^v{p>I,sER<9pDU ,B$2Ϩ.z ) :`CQ0 &I hȪUEٰ2&%$ c,v# ELIeQy $:9H"U!o;> FQ$y'ҍEQ*ccaÆmgs'{O;yb*7CyȎ~pTV/*U{r rGO1T03kDP"Oh.2CruA(fRaѻϝ\+۲m#aw>0"$"ѱsN}|+ % s F{jQ-Lગ4bȫ:f*B 6& Deܸ{칹"%U7Ҽ ߚre>cGI-{&,dDqWodڶ]aLX%O*æ̔@ )@%P%@ 1 #GTV$.y.A B+5``BTN C YR%Hj"2)ā0 y/`e`#HRC˕%Jz*3M44ѥ8xhqaqַoʳϟ -T;H^TUZ|Y]unwg|[>ey|Molx4k>~?F+K1>wzfg65fݖML\i֭۶O^ig|qafVQڬb؛P&r5/ߴn=w;%Y~ס0#eE7lڰ>kd##7fr*Kg YAfR۝V#_XbP=+taƋN?sة `HS &WN_|`ۮUJ;W7g/o " D6 aP(dJQISIAW%Fgf<.6AC6>2rffqƄ VN=~U;B$&( T="*BbX*CB䜷4LġLL1L*=Qs*Iah%.&J|ȒBD\4 Lfҡ @<$qLe!Z hHT$b{3eb֤, 5Cr֏va|0VU7L,R ILɜi y*)]lw(QݲmK.1s k#W7?ϜӣSp0G^0笳_jNN!RD1X;i7l>Lεkw퀮@C`Ыy\EJR趱M8 Nș jDM .hEGё|m۴|_LD \O^wݞwi+Z);plp9R4C*j"Bޯ99>)rLgEPVaM:XzϥZk"`,e Lɑ脰R%AݸRTg"3LUt>"*J<$OcYRD5;q"ox?|a4Yp舜gI Dk2Ceiaꫯ?|tե&|96^Kpn'gcw#+L<{rj`T[T&E<0cʽFE|p"j eQ Uh` ڛ_o8 JIEAy+Wn]q݅'/ZsCT DΉ&gBQ{S*]mӿ?ԟs! RӹSk̑_qE(HbΙ怊)N|Nn}% F2CT24==1wƵ<%E_;`QAJʤb`U"{1fŘD:(R  3rJE %RYyȐX4"r8DH%8\#TE$hf,g 5̙ he A *za|Fj':f 1&n\\M,1"QJDX/}'/ڸvEgX\x[w&w}PRw.߸zuؽq[0nzv"%+^ox|.r%;*+L s<; ٺm޼N_zMLsA6y>O۰i˯;Ѷ&|3k<{PΖf*7+W@1n4[-+hyƆE^ "6a1_Uʵӣ?|~<fQQ@T)&$;O?v0UTMҰwyFvQcC^MHjP28hci0 1".]Z{?wg-nci/9Ub#z]+{3źLD`H+]ZeftIwjOtc14FĘw/%k /߾qBgU;uyXx ν1s*V$pc#T H<E0ɀXM!AӠ'* Ohch5Sj<HLA郓*:v10,J 1:`OI57J(I+O䜔TA"cvDV *V5|%#q̆h9a"2[}BRQt*46Nn<>Lϗk5+q熍[$Yv Qak"2/G)U58'PҘbV0=CUV*Ik/ZB.} Rt*_|߾~翺`չE4e( &Gucl;^:NZkNIB$aJj(Z]hvn9~}abz|D1̅[ d< 31D `HI=S=) ; ʼnN UFe,9 uCáZQP 3%&W2oBB .E{VEA+DI.QʙU e49G,SD@H(Bh8`vDx(f d Z'@*{ rhoضntj|M[7o߰u^~2eVp.J#^o5\hǪaY0Gycsgg#[vZm"EQI٪]8?]=/߸?`{(/Ά`ff~fgoܰ'"c-ۮ[ "GZN6~\h{ ~vӓc0]Z?i֡ea{t^3?-\s 9)b`դ:j/-ѯ}1+W-LXյW\z]?"B"<$bD5zZÏΜ% !,gWNdD#D *!|L!102@mSc")JSo5&Fmڰm.b:{pΕ.p$DCCĘЇǟ;--N^fs&UsɷL=6:wDo0 ,o5[W<>fHCE(gÅF󙳳͔Ό`;`\b4v@4o6z=2Ni0^/pkf.*D2ʺK*afa .N#6;765AIX0Q%S L댃bOmk7Bfgd2*gѶ_l( kXϧGz z]wKݥސCoHQ{]X+o[o5|czٻ;nt‘ÇϻhP@iCuԑ3sO~ B/f~:9~Bᚹv}36qr$:c!s X1WeVUYc }Bߛ hDnLdzV`8;;Gﮘ^Dkbo8$ )cm_ RMêGczm//ϼqivfK_\}u Qaee?P:Cn:@7MFŀ߫c4cTEf19>99rZSUW]WU]U{?J>;~~eUtO>^+ș*2z}]c }G|䙗>}z~kk#ݙpA>wzod 5s_t###t)%icT`J; tT}o?yϬ>#2y2"Ԡ'k-Lvz |[S7O_~ˮ߷}O8)zQvj `+|kA2! Dղs2E=L9H"Πbr`YA )(`VB0ԂT會N(~UϪQTIZ~0˘5X#:穤ՙYF"y-VGlRF)ԪM~! j`y_ꎄV X5;u~ugbt #samì.~7>[ A&!&Jf@̘Y .eA'Hj8}əgO.,>7.s'qWUc/zԯYf㜳/0;d lQaǍ׭'ֆkH DL<3ST=GXRӧ0'ǧۚ5k+k:hrdaqqd!"Uj.$P!e"1"Qd B4ɲ fT)U@Azhqknn|dbyj]w-~l*"fS0#CD3ͅxFRG_ܿZ[{\gr!·fmu$t4G Y?#xRcT-iNA̩!# *))HB%TC 6\ǜB-k@qLr. '@&)d$fD9eIpBT8dBTV$ rp$IruUE5t$Rڵ|J(pe --Zd"Wx+91kn{_zk;LQ(xً#ZU]!oǮ;wyG`lpgw}2sϜ:92aoU^_[huƈD]!. ȩ?$кC/-7~n{ԍn43UPU9C!ʯ/~o೴h_flA7LL]F8"`uaEag6O氼0wByofM&}ԭɥ^6ԑ!lňJgN^s>Z%3k"6;D62ݐe_nq8UOϯ.m4 L0 O|^q/{w?#'+lmuՒ14|K<~c{oz;7n݀S-oyk95zc߻8Lh1s#5QŀdP2h zgρ_f/FM輚aAXww0]hJ=7=/6Llٺebӆ۶w{壝nbUkIڛzaVMU 6} b̑lez19h\ȵ@Ϊ&$Ie l#GG.k͒1y ԈɵEe#!\2V)+Fr eK%+5ԩB(WlfN K:rӌ*kYM(bы_u6nߴ]o÷>wkg~ykk|Awܳ콹Ƶ4r[9V\UTԈ> %&OUoyAF` TLHrI: KD-l^4 jNԃv[$w$Y!835CUKYHSr Wј*f9͹DԛGlb}JurĬu.;cjvK`*-*%@E:!(RRCOV*a0l B97*ɮڰ(?,MMt&:ɋc/9+ xW -/``E(Iҵz+y}oO=rM#drӖ[u7ފ0ZcLܤɐ2ZsPוX;\ubjhhӛfzQ"$I/ʗ>w\ni1a`-ARobc'>Ɠ;Q^^ZPhǎwf'ijæ,zS6R[Z/osˋHk<88 1XyLuq&#/4BVu D(X#bkC?{-7nt, [eRJ1Mo|~g_5 n%r]*w~l[ +HHݘv׋Hg}чo>B.;O<Dݏb=_}_ׯ=}%gwk{O|i/2]BRZ[^h{n޵sϞ&g98Oȱ__ZmDp'$$Z\ZPc,BsdOL$e4#"Z:f@M.3XePT,{o1D!&j h[yjPC`4"l&Zs#z0(b<%U'I/&ujg.l,fe!3D$Teskɩ02fߡ>o}oJq+й:~X(@QIU4 wx@ Xb-C? F1EdJú E' +d$$B"hI$m:MP9!$R0r4C}Bbʉ}Q$"w c5JUdjj`j0)ۅ I) )ccpc(P+bum)fMٷijܾaۆ<7'L ԀW\~ܲaz@v:eO055e>@T2 |w껿=U{u1 Ĭ`7ҝup疝㛶Om?53StH9'(RgQ,ܨիi`**ړ<҇~w};յ:j`qa4Q[x0ƍSGO ̷3vGF`qe-<>5fxݝ[YXgzC6i/`fFNl<}Z[Ήő8{>2Sf 3Aj6 ~k{8׶qԉg+VFG_,U*Y֬/peGPC4NlC_"(`J6UgYT}Yٻw߁}zXzxGᯯAf-v.xB8ҋ/ l]|_Sr^8?{c,vrEubbW|wˋg] J-IBI޷2s6DVђ: F2@đC)Y(2$D8d U@E,CTuZzUj:u&8s)@bk^U,)vcZq%G>Bј bHUvyʅ9"$䈖3<ܓ7 P ]3: U-cu}_?~e۷mb~l*@퓳ǷOڷwg_ ?ڲqױ'sZWӇ=2Q}Wr׌=qX@\:q)ڥ-t )fv.ݯK7kP3I=TR44Hy4<b뮛&|7M6"Ȁui9V+UlN[WgߕSôIX>HM0S4l1g%"s.3L `ٓ& 1nZ洼6WS1lc\) fA& u4gccdA8dQ(V8ɢ2:%tbh`kte͆rpCG |`Jٲ+< L 0 ځ`eBjd(fuD,ġ`tmqA߻r@M ShU~KF)Բ)O&FT@klX_^8{,:zP )UuL fJYum4&Sԝ׏-)Ub@z;m&gZ ڝΆɅt ##6WtzcgLYn_ؖcc{GSUMJAFL 4O?e W!t9g̔Ls7ik.xÿ 0ƴgiP56r¨ y>׾S/sgsM\}Ӻn]}ՁO(cSӛ7o8ˮ8ṕvwgg3sn.rS~~=ugHH؈rʆ2z-V+/^xNt싟'zKu.-\0yff0bYye'\0/-.[r۶t7w*{}Gm6@CqDĞ|G޳28nHڿqM3{rTE,83Ft+ z~vP1,3vKPDJPCTev  %c8kG* 2Bu.#xՐ;ojL"JX"ZNFF4"Ȁ <%\GcRvLcrbQ<:c-z C#uo{/_=sz~hɈLM+7]xKI;~锣uK D-_<ꌠ*'&d&0(3fsD.1{+3c0 ETѱќ0lWz˖S[wML sgNoٰ}fz~vyzŹehs8۪;>tjǛ:/fTb$ fC ֗W;ǟ=/~ߞ{酭7@T 5 *2w[s_yDwl_AZKq}mIRjadB/Is 7'(jٗŹG抢hd RwbbĖMcSͻy.8JŞjX"Bp9h·Ѥ2,;-.^[0LU`@A'KST#Eޒ0"H- !52[ ku b 0 9#-( Z1Cr[DdWQwG^<4`Y5{ LM+bԛl/S/n㶭eY__شxoʟ>~>h9wʻVV$d!V#ئt櫏~}sZ%R`FC1LT4;arΙ Gv ŕSGGG:n7\Y S[r#gOzCw ί1Lyb<| =)򊚏t,\6劘m8T6vMl\,|WWҵ[7M&n&8u5_XXݿmSN.q^U)\MH`+k_h+/oxݍܴ0֏cx-&FkáGlff"c3Sf&>v[-fD@@Ti~+Rc%ƨk Ȏ'6lx޽o` L$XOLdr}Eȅn[K/mkj)g҃鴸 Nvw3|^>v`X:_|j윚yImy?5b&0sws .Y-R6}*3$H'Ui8ȹR(]&FB_Ɯ*Wd06P+$chP5fZY7?~=wTqdlSO?{-Ll<<~TA\yD\NUW 1Vʚb.h&E$Ns$2C&fHAr$LIaȢɛEA0B1eM&Lhh.'+gBSQR2s Tx$BkF"c)J$&IZm+Kr% 9G2ķ](WrBR0H]G}Cn|6OȺyӋK ,&&Zb@:9r 2H13[n ƫԣV!`@ٲ dapB4s+韩ݠ^^r-eZ۷m޵k[J+ 7smJŲS޷o?{7|#9\KosE520D2zmU*Cº#YTC2aV*:ɷ\+w;^___{&WT 01F RXrF:1!$Z# )D𚣱RҌ3*LsY5% ,s;a' (ޥ*"ĔPKjhFQ{V"! ] (@V%U%U$uB\o޵Ǟݺqgo\sUBǾɡh#Bf@dbJU3SG͕."&fG_:|=*)(tB=;OChCjU=}.t;o#G6mxqe6θN}˖i_KΝGiçύt4D- )HɨpDH 3Kf#bȆXU|YߺS˪D`9VƗ͗}|O|'~Ǐݺu뷿ݏ=䭯Өj&wrұfE_;T4$`&3kE zI}k̔al* M-wV3F4I /Rl9yjYS3-3^<ij1_(۱ rΓ3yb c?&f5]`9b6:VD4u<86TڄɆэ45ITɐ8}.}%,;Q?\.\8{`K?VyBw 42FL &0R@ZP h͑Ș P-p񣭲M΁#`@sD9hD xX o>v3'o~f?_ٶ5V5"G/_zݷit<0o2@Dl^*FzFHXP)b!ƀ&u~D02D$F"v/_#?a;~#5oطg}V6Mn8>{{}~zHWgN>@ [HjhLQ?3s28@#Sp'ЬD֏"[jh>{/Zk,Cf&ZRUJYb]SQ E 2"&ň0I̸(FNs T00hUGL5 с֎X<#CS@ƣXРdh8\Y߹g}<\X_[Y[_Y[]]]_ZJ.Vڀ=ú/q`~iafzs]\n=qMۑ@(TD&dSv夞82HFTH^QTT@!Z811arsha.kdyMH(4$$u ;b=g 7ًcggϖyчzV#ё,"ph9DV9SC ^]"*MncV3n@\nVBA##n4fJ/S52i./C{[w:T5uGG|w*,W$8xٷ[ziuhRj۠]bX#n~[33DRDfɜE!ښ|ۍJY }3!9!9RMi|QMH461Ĕ:cg=G|)^u]aLUẅLo0:C[g{8 loK4"VNY %Q#v8%͙S&>ei43 fUdDZHMUoum0 9&T"T38@2dL왳aV˫kˀhuy{,?|%~w~=~VC"wIG՜@qY  (^I-̾ekP]\pґfXq b'&f}_\_;駞N,{ tc?|յ+Wvl^Ÿy{<`"ꙣ7cb rR9uo͜WMQDr1Wٕ>Z<8L}"q_?`3p C s r\ VNRXї3>c^"H!lɠlXQ6k(`9g2BPy5Znz);M"pbr~nmTZeM0Lo۸mÎ]"yFƱSG_:/ !f22̖T1;O$Z{o? MU鸶1TO%<ٳmjoYu; `ekk.$T%SBJF0 (Q)" eB}ة+$̨`Q767KU3UUMp8,C ԱWSc@JJ"bP4+bDCJ'EjfМQƪt>T^ػӳ ~uE/g;p\8\-,?v|+v(^@;EwgVcxN{o\^Ռ ̑QTԔ)5F|)`3d`\IBa]N.V`Q1н]Զ-[:##i鯵GJ:EHjJ R&I U+XK+`*ـL@\V!F&T$Pg ͤ xl TWG;>K-b.QRuՌLt90!"_qVO |;7nݸcӁ}7?͈DM62tU%rfHU2 LGX8gH@$<70[^YBpW][}-\߷kzZgDbK63ev*" Uʿ`};v]אBkȠI1AeY3Y"<2rfu-J*E( 0*aN޳dB2aXiJ.TՆ)+[ @LeVFVST I&ʐeu.LّwRbfLтYsAp.x#P3@S.ePS`,fLR%}/|(bz<{WqCs5-˥i@j H!8&,VWVE֝[kضe{۶^<|ı4qey}7FjT G3 -ؗ˫ ˳/_ȡ?q7v:'ڝ;v^}}:w\ʆ1c#㦷3TT8_Yvڃ^kϜ;aQp.P\LXRg|Z]3bPQ";ө!1`F f= amJ I{(#ghVTQk)Z%dkX %9DVWrVVP٨E?TҐ'C@As#g@J*Q}L$0-nwזa6B-|`Mkf@&2Ź':qcKOxu˧/kNIk;ozki.7W>_>?0fs#44KusD%"#"葠Ө(!)dkW\Ww'rs//.}iue[ػov#_{ s@ʎTETMsdPD_L7l}Cv%.X=s ι} #3"S&T#{-‰b6luJVR rjJF`}9[)H2F_R˒ @pDYb6Tc`Nhe"C g^5:9grn(IɐfBs1gAt -ED mթE,תT'D):BTJXuBk%SX]?uyG<p~O>cE`na@EhI 3Ԣj43X931>|WՊev1u/LH5z&0M A 7uΝy驱-c#%DII풦<\ ;Џ$ ڳedzO3 !妰1e# >0x%5RJb@MjuYL'>G^xs{kbii O a;vŕ /w+o߲i7gz}5գOWK?o{\oK!,`\ؗ&QRtpW+SSӯᚑpPzN).;oy3ַHUfxd37FR2Pc$Pm\ي1`ŕNhanfZ@쎎}gЙ'SVwjbm_->ī捛w|ÿv^+r?yo421121c+k kfJIQH"XvHDT 0dB(b:6ڀk4*[u;^Ee3$H59Pp[])a($bN@(25XʂD̥Xf2jVdBdJER 9 R#SMàߺykX%"f)m>쌶s ވ(, t!y_TWc8 ۯc_dCy< @|;PT`λ)՛`8Vq%0†M@\F"Dl``X8@fV{@23s`aRCr`ձƱژ>͑RbPS2ut9e $L#`yiaь5+7/CֈLsnuw89qǶ?G.:qٺ7\_Y&/hTU gu("Xܥ~ mmmut玟_]Z]{kìHI٤֜N zQz ] -~߿896>{ϝ2c~%( h&RDŽfk"h`fFGWT8Ϗη/]?}n ;g~o?¹s;&*~L€@֩RʭVs}RS٢@LHX<0B2P$H$FlUeY,ON,4G;T< q !昤*Si= 0E#`CQT@őSUA徒a\ˇ%Al>={d+Q GDXR&fMS>nص}ñi?AIN=m㞍;=W]~`cϦg~X{k_][^yiv3flc/Y 0&AdQ3 au^ϟ:5 xk΍v}-^ DU1g&8&^6$.9_?nGĜD5..Z޻PO>Z??҉{{˷ [7 sRVRD,V`n$kV  y z >AP8U81bYA .RMsޠ*XTN27xue 8g1#p1D(JRݸFD*BR))HJRW(QSXN r"LmޠZ8?J92"RS>ORoeU9=/t9Pt KYE 2QBl%rg2lTK/r 335aP$R014Z# HL("}nhB^ )*Ĕ1 Dՠ !dʌ G!+(0Y L"hhs\~ۭWO\ydvSI6T^VAh&ܸ={܉#owȏ~êhnړO+X lΞ|Cgu/۫@@@/6TK s?ꎎu (ןP\vŕ&7zPcOop 7g|tin{}C;KF$@aF#;9CF]_봱[\8|'-?_;~]‡~j[S۟y/w>q-;j$ |W{~0U`ϱnqC2ĀQk_úSۭnjp& wA$g2jP )ì9'"\lJ Ԝc1a9D2TKey:P_.\]E% < gp"l)ѻlꦵ ۶l:_֢y[߼v$筗_[X[bBl  @tBBK3笢Ͳ*3dgdCAtFڧWt%EE9*7-[7lڰgZZփOUT{SY)!", _١X ޺Nw{ \{UHA&fύ]DUù ZHl)VCrcWfAQ (ffD,SlDY<5hYtDS&u.q4C1AE>K|P Y2IFvV ȡ#cVX#@#u." 4'}5bUU`蒈"3ʌ;`"[e@˒eǻ5e##Dו-3ӭD׍g|y]lc㝑v1z7f`1YKZ);-tX{ZYTl:N P1Ii"&2d]c&7 ML4 4f/PXǺ35>NqI@bH΂P5d F( LP!i FJʲ`l۱˦7o~xβ?i瓏<=+@ b%R묮-o8Re&z++_8衺= gwW!"w6?|/ m@ $rk/@ vW\{uU//كRRC^^\o7~{>x1lun'~bbtbƹw\ؑ^u=УO=w~{:{A%C<#á񆑱ցTۿxȑ[Oǟy~#?ߪStna>؞W=Orn[nnہoٺE_JoyimsX+K*KAD0Pe. 'ĞLU 2d'UrBH @"#T Nsle0Y*.EHXM8i6ZSpj윿D9Y #k gM~`$L^!+xjHi)*!D(pH` dĬ9e O9k|IPB=Yه^9նomMn9:152ZVU nslߏX@);>ﭐ8(5j=9 hwrB%밇e3 &zT̚QkfdjN5* -a%"3 yO<Ɨ`ȑlLĈ@P"C9 " aXwƩCGO떱bay>Sɫvm GR"h-ʪS[w<<3^ G'{Г?x䑅Ƿ]u'?/ч>_yۏ\Bt`Rv;;v/W]#*I@6m[եrٚ|mG',FU=Q}@Pr 0ig[v¿o~G ccA?LNwO?x߮{Oo|C_P>x߆NS;oyǻVgg] T:᧟O]#GΜ۹sŕMK =X|m}\{F&&z~K|+72^{=7~' "r$nT<@Qf%*UD\ 9rhI):BP|pRXx5/YnuߪZk}ι[VeɶlYr6!cL08 ``ÏdqFؖl+t{{ط<_/%s{߽VU}IdB8ԥ,(pEFP,Ոa&XF1+ܨ@RPi6X4 r#jP,R:9 @捥b~egfk(vP?;eA9ѣG Wl֎9[%__mn-3:q`eJmRX qR"Vrs'˄(fdb6x#NU ̆ ;azvsKx䡏>N W4=E/|ˁN<#x(6M!bH@fj"s!W'tZ _X_Z?5իM,m TQ=6D!1[( 'gw7AT9#"ݡ,XC vsu=ԧY@j*LYꬖKn'n 2"r fqI h#M~2gҺUQ<[`GE j(" ebl*KCHԄS&ڌݘky"$Yjrjwg(ǀf60K4o!ǭc|ff/O^݉t}BoD` `` 1LS; ڑ RNٻ: w1(e%frڒR,#R;w;{=<;^r;+l12iw]sH}^7\g:׳˛ySz؁=v=V\wݑS8SE1g0OPD뮾=|fjFܔUyfu뮿?"67f0lF@V×xU_ػ׼~}/W[9=1ٽ;=7?zo_6}ۿu8>ukЉU'z1wkBy~S~3}~旽t'}w/~=4KA+KK_>dC&ˣKV,^MO3Fv[$J!h=FT7% D<%̀d2Dblqi;#MHlDm *JX8UKF̸68GE8Y#S_='Jqʊ. U,-< ,=/e7HLV H}/;h WK!D3r7&:j_ǣ^0R.=pVɣ-vɃ*M gJو"LᴉJ2s`e`F]f] 3vlF Aop՛/%H?5;3wv:TܬO _֏zkcQ9\1́?z莡S=9ުBMФDUʞ^[0@)3n9S@r3F3L"[2a1!1 E(R,1ѐIR 42Er e2iI6Y2*fHT A y;?#k 3MS brIAȉ s 2{\jpVDz.Cٙy<OpVvڶ&#ۦٙve,b>_/ݹgnznPJ¤/ݺ7vc sC|H`3O0Ԝgܻ_l ?Awwu BUcPj[b17 n:~Bٷgٹ_y ocvjX~,H;?[pqcŘٚpqs|KN>t_yF+__}<~-/Hszrs?+. {?L]wfM"6Msу;w|5[/~Jⅷ !{tǞ~|vzު7~rOs..o̕SqsXGCGl vi}ss1rHC&ݯoϿsǪȦbjͭřl3^emE\R~r~~_UO=Tn>څyD'vҧkVQ"96/lW;r[~~54;x]\u󉶩_^/7̿xc\6?BR… j6s~#|7VwmӨܻo?uq[_zmonmYٽVV.X! @?3;['Nw*b6cw/ ؃rfn:Y`)"VeQZݼ!Bm' S -ʲцCuF:\USֽ *'# Q3)c5SlLA"SdMs!u3ɝ `sY')uufȜI%Ir"e9i'b{?cŪ(H~e. k;7[tSF:nAAdX1"zjM3QCb,mu !2]fGv6W[ʹaC@fFmNHYrt"1,6i'$QMM#DfRRMp,Sl!FQP (p" mkaUg0Wp!5J%(fT5Fd{93KzaCb铜\dPu.3u]Qn_zuW773SFhǣpk5MZUs2;r}}S`b;3%ufv6~M/]3$`b!dDf (ٷ@W_{_ʛzjWٽ*j+{^pZѭI)q>`ik(Y^o"X_lw$|`ϮIL2WE  i<\^YT=eCf<|En<9@ S߶9\$NjaBLnmi, 6lǷU~/򷽙GѨnR~巼{O=ܩgg9u_ͯx˿9|w/yYug X;^z+).LIroSJgޮmS7{Su54;7}3NKѭlc<:EȩFl fdt 9E 3z&1F1:wφ`b ʚK9SͭI"= P7P( AI:K Dd @h&5EOUkH#:P$b71'VᖕRJ\PkB bEY1,P*Z^X>w> lۨIDfb5jSw|;%_~tisimV;?\WUvI`fjJ@of{O檸4>Lÿcﬧz'}(;H 7( ,!n.mmM)KY-7Y^vH1*p--rԆ4l.&~߯ʹg6VgzqЏU1L=xKo{ǯ?}n TdhM!uƬe"3rfzP7f"܌w1a(,ky j5VXbH+E,9YJTDlh֚&S !Fܺ <'^s}4S66ZH2+IRQ`sk=L25 EVMB"g$% #2K`Mm)Ae/ Ta̩ݼ%DF!0R™ṣ%M6!'E㩠9qx Q]#` `n)i-ynG"B 5E1udhЉՒ6ǎr E8KLNxWX;{a8m>HHg{I͍vߞS6%5dqvn(^U׋óqcnеIOsxq4za܎{ŃM ^x:yۻsO~nGC" !v+ ib:S*z~~֐c)I d$ bẅ́SЂɘ B ڔA7f QӸUXy♃9,[ASPI )PTxJ`"Jel3bJ5DYtw0ypj@ uEz=FIs!hQ"&HJm $Dz!0lӏ{h=Y7Rj,D>\tsfR*Se5Qxq,П3{>>^5߀~'v:oSSWEq"S3y$o@N .`sgWV;;v,,FE{O~}хn&m\xxO2(= -'**M0'oGQw؈{2(gu,s#6yPhG mJ6H ,\"ܜB Ps9'r.M\2('hY5Behrʙ]$܊8"z&#F01 ۺ'QCd/"m#k\#EEtԢpP/뺥lbLA\2'3XRG`eP0a;`_bYCԈ90XsQ8SKHB*nbN*-w qmܶcug@Ĝ fl0y}UIüc- עm;=3`}mua<7j&Ft\7:Zy P57/O6y 9,LΟYLOyqv_򒆽͑RQ/عorص8=߭s>~/(`7M)Qd5o?Mx^sEzhpH bݷp!woGWe`k}S$p47tS&auZ͠vڵk5Uy_ps= _}ɫ0F=;Q,&u_;]7|󵷿rS_uK72Y=SD`@0lNI)pATA,}}pǮs;W3o V~ޏMWږm˾U>n,Ao籝w~[|{s=nV{P29Dkdw-`$KF j@bSDBXrԽldgIR)!Tbd[ya9@ًBN BSrʶN~p` 1pPAUaP"G &u];CLUFM3"rQ8`nf* @B{D[HQŽOpa%1F9w6ADa>* 3 ~K.X5%P7Ndvi-jFjD%j7kf2swt82|Yžr6 ^ܽ{s[Ve铏 yq0 `}mQkg+ 9:.S:}`wdЛ.;~ܔsv5^uyh<`4; N?_'_عsv0`"6ϚےȬ;>{s0w}mO>ӧ_W3 8Y "3a34LELBBBlmmx-56MMƸZ_XY:) ^ ?]sܸjSnܥ;cLBÁK@fI=(g( V/]di9uAߓ'[Bkz옟b\Ziʂss|7̩sfp"տK}~a?Km]{Y[Zɪ]c3 ۿncx=?NtYpՉY{>}?y|euei R3;]y즅SaУVhfj9VVꖾ6RquxW3+bQJx? gBl?O}ǯlS–st}?wDĽ.Pҫ;̇HApGfG_5%?/zw|G7`to~}k@Qxݛl.s`; jjF!ɿ_qًXNMX_)C:8 sbdkg|vm) w`IWYIcV8X( -$D)cGkLbm^ᚺW610e֌ȣP,ɈQZOseo#&g-Ip(k2f bhHa4LZئDA\I`mhRB`5(Pr*ԘY@Ղ2D g KC4IBmEڜDR#e.]ۘnOzTlNؙN.ИPSj[MZ1njmfry`$0cU+bDLUs>vO~???8ˏ_67wsvnԶ6fnM]{;<%N R0dngEL5P0jj~'k״?;3ݟ =+i4r5S~\^% Ȱuz0,zm; hU >iS|,r*c[ 9zԐqpS gi ˫]3sOgG٦Tbfuuz+,rG4/խ3~Q_Y..3p2pCW%Ź󫧲/ܓgB(8Rn=D#r0ٻ]K 03^[[N}SOO|bv;_<R3r$M]Q14Yu;Ru-!ҶIUshRM K, nn=zwцK%(#^1葅iYb)rb,&3\Z056ꞣ`M ^xpmu}4,9@23 4g6bD6 e<#M]TY/y4,hAĦ*\IQ $("SnB OK;nܔm8F6E#$nIDAT9j<[P9Z"ZjWF\ i*T- \z6(m{+8dKn5 ҽ3nNGGvͽGϜzbߞsw,<ԣ/|KwΝy},/n~qҘ٭/6Yݓ*4&2ɲ1XĐ$:Ni2jS +k|3$,rm.o X]ҏ@svfrQx`rU!JmRj17XzS}/}wN-`. p~dZήh nAHalkDlݭӳSJ&*8s0jIMrT# 1jNRTzEYcV֨ e(U"xVo[q"N[jW9%L]!\#k"&.ZudjX\, [1f{v[3P#ۆb0xKn{:xe7~[@6I딽,]f;݂;"fg2V&E2;wd2ɖSJ,B!%tw KqKSlo5/_S{v9ȷ/=gY.D}ﶷE4 mO]K:A8X76rK^Ջ\[O<17`kkΧϮefXټ4Ba7<7? jk7_3w:SK+Q~ꑇ'ϟ+ϡyP XeSXշ1NdMK&^zڙ;v.kyr6{1[v63SUf́$ݣe.AFdKDT NJeĸ*1vU?Xa eD>PȇE hCukm:s׻v. l)x٪"drl$jl0%H %OdAyvƍXk۶_F1m!XfP8sGBP&!˩0fF\D$`숱 IJEQ Vj ([ldF]RJDC(Zv1)T%hv>񩻾GtNtSyphl?{vso^c+[{PM76HUT8qlW-a[ UgVw6c(I( uk1ڶ)3 ܽwG>?W7\qmwqKn{+nfzD]qhcquB~jjhUyiFgStX7"d8(/r&Hmlʼn0F4Q(<sUJXؤ8+Iȭ!p1q*)7\An&]8du"`D^d!, kH e%7IjH(6ZrQ RTekKX'?5\p<SYbgn[4{Z?id(/ۿo׮^`GﹳϴŐ@x>SO;5s']_q2ǩ_#Bzz4=Aܩs7~7͹icUz$sHPby×5ٟ̾5o^,h&pwSU7a!3!QH؉={8}B39k; ?sqρ]~}]v{?O=5+Z>2]s+gsqiGFl 0`9"%5tU 65D4i E&⚢K cȍEL-P,r޵ \LMBҶ mt({Fb$5ZpbkQ0!ww XC(`) sjɕi֌ {KmKqWv0-IS " ֶ&.eV,x)#Q*wZuW{gfM=$%rKT??z[}v|z|t&+^'ylxhAN~SW0ۖkջ,8JDBYvmxٴ!MqBu!23aɦPUeJQdq$ʘ&i$g2P, I`31Pծf*Fl,"[IB(7E.1ciά,Dya a3{PZvp23p`'KU`ffUԽD,m`q*zҌ[+(an羄ij pg.f92{ gو~_?M ЏavߎGs=EGd3K5(fof_ϝx͕k>>=멳g(P(Ԩ ы耻; $`cN rY#VD@$l7Ği_u(~8։{?hݽ^m;92'63#s0"Cffj!ݎӥVUi]{ׇpitA^ƷW= x4]uJ*)q?zϧ>zϧgw8rJy8ii*y G.` nVv۝TS3{xe3h%xLп_7}]?ϜCm.`1RQO4+ydQwgR(kB3O& 0_6{ _S^ŰZ7zsrN:S{beF]誗_o=,TTj)ƒw+Nw[ZTM}[ꍱUSطZjͣ4>=3s'~am-p8 (n̜S+EȚ 7)LƱ"sRgF(8BA\[---e (͊3 f5e#0.5E8ɑ[%_!+Ev'O+r5`lݷ/žO OY:svS=APS3}S{\ Ld{anΎ6MSbPO~˿t6Tbf,3mu}1wT?W5~cc?K҄] %b꘎ nk RHñ* 0H|;ψHov  _p9}H!ъ;HU&M٧Jefv[o~u_v9xP#"n-_?^D|v݋n|坯ݵk3;i=սIX"w3\rS 7-=m6IdnSPB`jU({H:Z F9ԋRARPZJN) !\r:DYThgJ.Trv HHn)g'"8s!pT9CK٘"GRPb!w=_.2s,]XFYn>3vQsqzz߲};NCLu4n am_pvjr݋I<၏}E-3b "&' E7Ay `[ ̌ Ԫ?Cz9%ĺi!tox#;t>wMGҘ&@@)PCoGl;%:yIHas"2ĠΒMw\# +^sW >{-m QBS(L/C}p'oxK* ҒF٫JND?ۿGfvunQg{ xԶP(\ TEUNx= n*H@ PjQ낰\4'FMV"`K$vR`r*rn fCHȞ3̕5EJ("\B DfD69GCȞL r6MrQd6…V !-\Ģ%>gmnPLa 2[6 3Ռ쒶v]/gyN95muJNDnWOCoYLK؞{|~q|W' n8zOllgW}VRdR";L[wrW1sBf"mtD[v㡿wGOm;)&Ք۔[mWWVȑ:$JfrΡ }3w>sߕFm'IM.m;973uWC:qⲦRptrwyd?P@13??uٛ^#e"bR&5ǵAq2P2NjZL& ]5iܦZda0G;zUA![KdNlp'qphٍ#iJB+{r&)-Kpȩ^\YݵkI}=E/^8gB-8|=N dV3Wqx"*c[k+qfWowǂKQK``83s*c,]{~3O; `p,gNĄ6g(;pU1O|׾5\qN—ശf'g/֓OF)CY@41e%PJw.iN;{ur&=6"rϼ JeJ;8k72d|^e4i<ݰ;m'}+~iͬu]zqefD#=vmbH "bl_ v_wU>(W;җz?~g9pu~fUXftqr& ?M񺷾77kׇ" /phpDaAdl.mcm*(piKEY4Y%X0ȍ3G:X6U e%b !RgYU֤JQ Lu{[s,uV5^]_{§?}em v__xnK~Pj>4 vn@AIع죏xgqs ՜Ȳgj/zw/9;=[qIZRF[˛kM92U;$ܬ#: `KWDBgÍ1zm_sk̩S_?b}rs?_~DKf6ĂaDp 0C"CrOPɓ]t;ϙEd āsIN&RɝٻYd"mjL @dRF 53m_-+D6o !% .}B UkO3vN7DdczDeqe>oo^W>~0p|}3e&dcood&e6oc7rF`wA2m_15e!8Jr0Hnig?~ṋ׾/ˉݹk=}ITH=iʩD$̤HcQ8g2cRe( D* "B@4@^`pkͱNNkL\HT&O`@:" T !EpEQy=snr͛EieLk>qEG~:ZoI2`y438 G#3g8xo_7gh3{􇇣C)\K*i<S,($9V) rwPY;؉;%Q*{[O.Np;Y( 7Ν3,0{26b#j )Xr[Ow/=vG>;4{Ԃ4'^}؍/6?@~ǟywvN BKb%j4;7gi95o?>]=wtXLM[Q,׭ŨhO0`TҸ_Gxɷnky|/ܟJIuf]N&[Sӕ)KfcwjDl$!!&Aa&N&z=邈 !ԥVei{CXp"ݐDusu*LtTlD* eF&s)J, $1xӪx4Mk=`uuwHme =O8ve7ΞC4`tt@p@ܲ`w w |Ǭe# p0ΰ#5:UYzA-|y1:~/yێ]qݳB6I$`dDUm'YՍPpKSťSe =Zbi9 r#T27X+dLnEKlas *Wja1΀IPIABQxmj$ETqwe"#U&j#^ng3nLz[_:: =.=  fd,, |:>{Tqh[kM|uΖv0ƣW.6Ϟnd #Շ=1vYdTH4gq@KwA $N.dŻaJl9Tst&&D1H$F7V7VVWW hEи7WjڽoSSL\:77=g{zXI\q`߱˯Mo;N>w3YlG?{~t] z3skKw?BrDL9ض =CW^I,*r)FA˜]s4I^t!d7ﴉ,dP;LIA:i&e %g53͓āBێ:yE~IZmܡDEc¤iA`yeyz,uh<EB lM4=իr~zZ+?y;qUxko,/ H$#""&Kh\[c OԌ%eKVu^qH9J{Uh8ǛΜ}G8~O/կܷ0_Wݹ:mn|۾~o_rn?^v~仾?AGV@,dג" öa99nr?S?==xcc0yJV` v݌ӥBwm."aF@D-w^Y !Q`)3S(Eʲr08|eW /\}c/yEҪI;rq%LE ) Q3bd c!dU*h::1ܬ` JE88QJ5-2X!g)ȜXcs$խ9֖ R S*fa Tuf -(dbD AqP"dž2r7!]40w;$0@Z!f$'͉\{O}\*yfϞ\ƹaRO?X 31z۾! ͡R&O,Pt-c2JPGsg6EDĂ5Ls0xxյkps}kkkTeMed,q,dmmcmn0X?ObR}߳` Km"ͭp(˒HUJ5"eD&SLu8Dj;ݽ̌ v0UR!MYM-0ufĹ5')CLb2FW,YXDAX"ٕ0&Zr$R,F<8*r,% XĈUp+~SѦV ^p~!"ւ{?{DV[ =}}; |:9_FW=5gk,jf.S|cGa2?rdjzjԥbۨzwO;wk^v{/67~ï~7,-/ϾImAHa 3S%an!׷o+?Sozݛx>$dFr(B)5[.T{8EpalfH!e .l9"%"0F4A\_[K+Poz/= ]~?C^>YZ؜]v嶦yw93}61'@7|"`@PG3F1- #c)!ggܻMg݁m ??-Oy&ťO̵t)%F䔰X1DX‰9 (6(9("1tID˜Yx#dnQID  $1QC #&,<%Wk ۆk =I:b$&a.+U0ע9TiD6>wqtCcfS}撋 *1l]RbraTξ9NvF*:lmk~󟿨e߳/?W]˻m}66g}63$¡ Ձ31DҰ-2F8BqDDTT+PÇSϜ^?]xƮԹp84Al6-[Qdf5(Y\M7k65õw"! x S@8Y&Rtg;xǟ^:ttxbS(j?mZܶԅ'aA`\`QܹKr TPfUG}LНPs"șc62bzO>K>N?}dcq>?O_Zp{!BT`3PkZl~v-yܶm<р]@B7H1x'@݀̓^]JV5Cf` 2xd6 K+} m[J/1/ʱ;v6MJ㦽l/<>k>j[U8A*]XtcJSHP ֨L-$ 96ס( &"fCPũbqF ljAPp0!Q Nj0KT&GHHm]qn@auZS#9!qfp䊁}<;ȳq6_f\O" dhXWD4g]d8%XQO>Ym``9ٰ p`r hV;$fՐt깳OZ_z ڬb,Mf 0}:#,ż:#QuND*A`FWɝ|ă3F%r22@ ^f$tm~ÏmkW<SFL"M^zF¢2yJJRJDXk "DrUuI4}F\ȍLݼeMɬ[NjJ 8MӨYXkw@8Fq '|^L}i׮n_iRˠkǮc[}9hfppBzD2Q}so9{꘴KMZt~s>^XZرres=/{WlΦ1ؐϟ^/O}6V4M>$sΒb|~\n_ ?uj=ēܵoy>t/ym}=_N''`Rn=܌ k?g߮Җ 85+"{hlA,d0Q# Wppc%hDZCoc?cLD8,tPtMLgOXܢI)̅DjPuDaxU@<))a:x853!#@iJPl'm*Ay@T ㆽR${f"PX-w^rHÄJ4j`4 y%nyǩ~ EZ޲ xFAlk!lC`"N77m#`(;D舰E1BAñZ 4 9zsyޫ AӶȧH y"PFA; Zʳnч4c.nrF+4L7*}ϻz̩1h@+k'~O/&t3:KB!AZiRm~Y}7\O}g?O:sf5 p^?W7ϼziy!lsI |^0fF*H= pfH aD"K Y?޶pom;bT;eyfĄjR; '$W5$-)' tg`usf$ñ 6B{DyV`kQ +?<0+f ٽOY7;3z7tmw1bvӕzG>}3a\(  Xh4<* 0ptb@'0R@Jz]m?W>C'ox]uң?C͇ LFA0 "snD #67{e{{{5K.C_~+^Vؑ pP-3dA8-QNU[2$@Hr!,D2\/ɐRp!rURr &"@R;3 іb6.2s.k=<ӿm.W[󗟸nHةD24PJA.>1y p *nzE~~WqA&Mm]/!!^ K ֕ΫFbMʥdKp-C53 @ $XꀄVJ)SK|# !X-!CDtCp;aTj@D$aN E 45/-;?\߾{ϩգOO+Yڂ*:'D0uf< 4gs&OF٬ZmvuO=q2\_ܴyW Hg^d&npNZdI"#BI͙J'cHij$ZP$dbC$eᮒQeK%ީJnA-0 D` Ld FF@u YfG~_{ɥW,2O^j{gh}}0:E Lry4׍KFX~&K&|~/y嫿M} ?CxVp/KgKَ!"L]Ͻxumǟ~l\ɐMVSj+Ǿxϡٳ6;4(y-c`ʶk{N^5sstB4̍եl"!sC`sG4#IIU@&*_1̚ AɰWnaÂdFD<+71kW]v93_ ?ߴчx!'w~1E CС&PgMm0{ b  bX??4^>1@`0RK_//8U%H#@n ! z̑8kU b<6Auo8UL`QXsx&M[؅+9X/,ȃ4̙43Q$f I-[BT$ M / Kf&͸i4kٿmcOn{%}?w @HUfwQAI1<[oYt"aB X0T@;Z6'O}+6$A8J4P, :+-zaK&/#!Q0y@`aƈ۔jQjt4BgemKH $u3Q"a(A2wB@"!'ӂoSkO~_^i5:v~uuǶE&)gw_,.#ȽxI̜ܣz)'ՂB6A gGY$:/$"H(J5gj̵ѻ0aԫ%9K/="$З.(5fF "PR{Ο_ȝ_6B&D-E804@D'Vm\rޮ}Y0n& Z"riPmq&&YyW6Μ:zGzw|irp_o'zԊK:D<A\Hi+<TČVD^L@-A1 uFt3JiѰ 5MZqus3;?;/:Xٽ}{3aiv.>gw\LcНŦP +F bJ!;Ơ4'Cݙ*yў]WWmG[S8PzXY:"S3f Bו pSÝ'VA> 3)`/ -$%)̇*s s!Zàj‰sCՔRd+a <2b`XU@vn㉮$rcR>hjG=}ex%6g&0NrugS()!) qudԹ<|?-qzt7m߰8iԱg?uwc]сCGc'F9Q3r*&/zwxit۞{;`XjRb1_[^׾-o=r왍^?-͠k߁˞{}nJH •cܛ/w_K1xGB;7mɹQ3[KBA5@W4Ti's"~ 0D:̨Qkz b4jN;mǎŅOyԉ׿ia}y׏۝;wG}d;쀏w؏?o}U;J:z09#_ASb 0b,#d < #<|[^vnmAhګ>'#yH[fqavU$Gbޛ#$ MbFЂ S涍 IMF<E`yJyV2I/ޤԁB5t'QF5P$Ö:4$p;{(\8K_MfX_S 2RJO=дTd-kp?4#9 !0"t7@3"a^#JYh9oagE#(-JF2 ū \dשR4eAgH 0Epe @Hf'h>fjO4KQCسwBʫ[ (Êc{B5lwsJC#0-+ AWP Pa;JA=([0ax8;c~Ҍ1ts X-sZ_\{Y[Qf5\ (7X-E !ts3@ivO?{$dfDV3:y.jo|_7;]tIΟyr(9_x:~vƗKuc-3؂S&dmx'/>>ۖ5yxcs1끷܆%1 ܢ23DT;QBpI.\ ۚ1ɉݍP pי_z }MO%r5W|<]:ue28Ǔ}?9ea/…01218ԡ0\ }zu^sOZ 9 ( P\`qP $-چqB JASHE A5oS` (n@JI( f- L j40y-vJnIk$*$<*MRBWFL 8'jd cO;#!b&rÛ6i=-  rR Sp"&"L ɉU̱3kkgN<1k8 Jfq3Jm Y-$-)5"^-(,]#2Mв(JIshC0n`oѐ1iҸULA2N3텛6̐fM;l_i]qŕW\y՗wcϴyow}(cox+u]o^ϞG%bܻc9}n4Hwׇ=U\ms80w|ӛpU_tϝyGA#QdDڮ|ݻ$2&6M#X!E Ԛm4jڪo[eC7nx qZ)8E#Z%&Dg"0W# B |@іQvԼuZ\Xqjk?|x~m._W_s|}+ /2?G>q f4"}CnqA@[+AjC]?C?<8BYQ:+mB.-D b  )!;NZ=ӂB5qC``hDE=]AKLԗHb%\ ;p !Uy_XE0R lӎGU" GF /Ѧ f® *}r +Iׯ{~umu)uY,"B YC AT Ωv$w/"M&6 !u{{ G5#dˆpܤF~p~ b^-~;q ^VJ8k_w/]uΞz!??K_ MGkWP8VϝxzxZk7-_/cg9Ht" n>v E7x ` :q3pKͱ8]z͍Y=-sk^P=| 8DѮG!SÚeڪ2*pK# DÐzsb@rf"@l"soߜv۫< ,CBW\~dG?klcR@cdC &3%CwFM[aY)Ӿk3G&sBՀxQ xS-,, CZ܊#b@?&nQELsNmkܫ"3'E"*CF0 M9 Y2 Vj x82 hf]hĵVLDX'MSqmtPJU=0Hp3:iv؇V7~}\+oW?+WOϾ%A~v';wĚtZ,5[ M9p-#AHբfZ*)(+y_c,FcBEd$aDKMϐ8j=4OĜbB"\,TDb]#;BK)yaAHZv^F6V9eG%:$Gpw 2q $gUc1l@́9~mqy۾WO?&+4 ivf= ƤGr9qUV-$P*j:M7҄*1g=XpUf2nغyD h!͚vlj$DL2t2EaN8Cp)lU׾˯EW5O8EM ]Xx?-AL'N>gw=ڬv/kwݾc8}'=<]o=s)e=/ |}J<~׻~3{:u[5=OtxI8i4ãJG/?>'zw|xQ;Ru8w+kj dZ|xcSgR h^k;Gw}ӷƱG d8d-:pp&Ü)Θ F$ce sI@BcIk@tF(Q rpH6CMUD8jEhn™ z@9F)=jJ~fĭgI+sT uvi T{1(vۜ7 i6FYw><9qrm珮Z?CRCM!C]\  cC\iP;D$OĀ6վҼiE]S =X=LkpB50BRc-i˔W LTّ܌,['&tCs6 ʉ]sFi"H\PvEA!&9n]j8T`Īɠ(]\BU`h#JGk͈uqQ:qXرg9rtVHbvS3KDDln8$33keZ@T3UX+tsyey>jaR S 33Y͒5p5ƘTMVC]FM?>{+-fܮ\sϽoXlx6,?\66lGJj 8"hwL&-1'< 1{$K[O)2Zq"t학U@fA R&Mied``pspAlx0A '^yYZ0)h+@< IF1 ml‚!!aوС:k6Mt#Qyo~[6-4m5uY*%ƌI i"Ae3К@9;‚){ I2 05@N#@\v&YW \JפL8#h[X87cΐ!#l@RV J ?[B@ cf]K]a:ZY8X 8Aݸkn~?ǿOu/q_rvܕ'6m2jϝW 8饯÷~(;?s3G9r?|7rO矽Ϟ_;wvoۧ+VJ7-,Yb6:lU f@3 !#hHai}]4ݻ68wnDt`χ~NܷF)z-㛛{AldDq#. A,*zf¡O΂GpC?W^{ؿr֏Ф_\hf~{,WObxhmڿǏ#F u^"FFbʨw7}׿ڹ󒃇Kt!~g{, `W @!B0w6mٷ-@]ŷܵAKAqfcjV8Yjb"# !n@ {qwfxU!p`{-!LmaH]!K`h}ޗ&%33- ܣIlhHU#2gX;GGjЬ%' ŒJQ rfNWJ)@m˛]+F.k+g}$ި;i&3z$-WaF;"lxrnMi LTY MYQāժQmL"1 X8; 0rAę V"v3,Y CbhVRt}OPɳ5aPE0@bMXfI 0$` /IzfG''" 0P@z EH‰)YkQt!H?_[̬gDo{K0J EOydzN+zU"\XIQDU5,ZQ|y@>.z/Dʒ C`Jc.[Тa@o{;pepzgt6W7GxU#UlXi~=گ܎N;w3~:u7YO~ ~+~f<ǣQӎV-= "vuWt-߿oG66hjn1ZX#@r`jDF3C`"!DdBS7F#H}g{oN?Oo:+v}}e{4 4aڽ@gΜۿg{7<(Z#[ʰ>j'`sfV `)5!Ǡ<3wW;Vn|ͯk?j\|'Y;w9~<:>{쳮+)O>{r`zdʞa|9bߡ_3l^?˿pEW| 2xm ˛ #>mٿca,첻7y&!'.n[$3E$(V9ЕERpwtA@ wd  +$BȥwO쵗1VA-(ED5 pYD8!USDFS0 Y-|N !ȽRܴ*)X\0l.i1Mi'*捈U@Dca 7j8G 9 s@F403=5(,ج(X@(PcԗBJѰwSTOIkWg&=[U&񁆣iRNXQLLP݂#(Ih`H`䔺ZdR 0ñ%у%N@C%. P+p!2PIةGhyc@:7;y/m;_],4 O!"F YQna^\ d00PG&5 lڶ"J` 4n!#Z+82q 8J%pWO9),y<#Gejz-{eId&-)sK;S>HI۱mimVS\ź. Ґx=%U2~͓~xg];ؾ'K6-ayJ@L7gMϺ8fn2Ğx89t]w{xק]O=Ī3\v/e/_qn|yknZ~ޗy~HUۅWMA01 ¡ "P"䠉0`=P3p@5f03@"%7"ݬ/-~{Fg;mڝ/XY\ܹ}ש'νNJ7}Sꡝiz0Oy L 00,\Ey:+ O CZ=( ё ܭ"Hܔ:NJ6V r컹G0"",ȝ Kфđ"q&Uw4td-F0MM`:34U5nJiW,F܍v,OFKٶ1OgcO΋{6\>ct ɧlt]櫸Mqρ{ k7u?|OF#c"~"98z!0˹+/={~΅Zw}_ o. +yQK}lFM?ix`eOM H3wT|G9{0_P20 qЄ[H u ^M8b0ZXP{`e 1yw , wuHE YGZJIN gH.$ՋxxFnR 1Z)h 0HsD`JރCԜgB(I@}-00 Eieg]6 dDԄ0bFAOQ+2@aHwEUK\'.+3٘wL4w-JѺ@I pDUSH-vX<$J&'97~"pz/jog^yjn]%!@QcZH$XWu!ORK˔="rDUKJW$}7ChF97]dVu3N)#A\+ qRӘE%@KDUD!,m5pfLvܹ>3h2b brsIF@9RJkk'fxM?2]_۱Љcg/w$ʟSO<~Y>ȹlL{'?gi.+oC{oF:? 2ƦGD t^`ǂsDMDc6wLa)e GvdeE (`0 İf2  LA0H$EP7C64)9F Ԉ!Z8"RtiarEW(D]1l.(ZdqR7s#4->h0II2#Dİ,U4S֪GN4f[g<}v;r+ZMr懏Vm__zJ]۾O=pO+NHݷ >ZXOFm]{?ßMo_q蒳g^}~ɧzy`#@Zmۛ'.}KUq4 Q ~QżUi ±F%C&vq`V共!sk,u8@5dKCX & b0DwG SpѪЁ'p d˘I  *n{Ҷe\;{xm?+gϦbW}W3)!"hV %jѻYp.BHR 9V@EZ!S4@Z%r-B)U*70j8x}EW~3E %m! alA Q hgQ_myzϜ7^qCgpd++oy^zɾڱmǭ#fZg@b@0nVH`@8S@'b 5 xt[['ٹ{WvCviνj1vïhp8VX9Z^oh(Gy}r| l[Dt]vuvRfR{rvhkiyWϣ!(jPQЙБ)qEp҇:p@+6ܗ12jń^\5+`8R3R @\Um:1 d6]"(@P<ܫ4 LPX/N[C굖@L$MSkhHkuA2&1 MU"ݚ-]'Uƹjp5͉j 5\"HU $H J?$ &1&:0YA)v`1ػBmnZBH 7=M#a"ًaxps53Dw*AHj=%A2Bq4'2& !>=w}|W[WxivykEJI͸]h+N=2xT呺%EC`r6>I7-51T|G8*:ay Qw .ȡ`8YͣTœ @B5PDU9J0KIfrF9 !ZxR왰Wu&1H:5wr00 d?ؕY0\_se+}v;QUg٬2@N$NAC1<1 &f ԢA ܎ڧ̽i&EJs4"ՅЅD" &0 RbUDd7 ׎b0AoM3\谾8ɒ2jϝڹry7╷P,.+;-M&۷y1Z8,+ e_Z޾k> y򩃇=|؉^p駞|'nz-4n3;k3߸ۤٹv[=r/GyWnl{W]uݵ\zE.-m/j1+l>tz"&  _}]o}+&E7wmSĖ3W߬>-}Dш˄e#z̬DԸRqX!0ZAǹZD&$k P3I3#OB1 J_X >Ш6YЁ9# `CX1"|w aR:T4tN{hI*1 i_#Fd>PF ]٫Fb42Ќͅk`D󔥚K& p4 Cu4 Pó"+2d^0-!Ij;MӧӝNt>LDBbU` R*A\^zVqUF1ZLEL$4NH>}ߵc<^}֪9_na\a0mX[-"m :e"-Tj$3SC/1bvi 5ׄTu8dBN̑1}27*+$pXR *Mu15mPs*Tǒw GOZ*8KmnIKIhmrome P5ք5 cQP?MTu| #y$  m [ b6`(S{5$ՖZMXź"Q)\+0!ZN_~D0Mn^yZzx'q8\;/\e> *&m5Fb Ʊ̷>O<٭]\Ts+ɑky?Mm2m L MdTKjYzB+:@[W&KeZ\n'&pӳ_C?ƴȑOM7Qe|w|`Aݠ!!0 d.{7zՍ7n=<'/> Q@!ʌJH*mu0 *wQLehPUˑn2uxx<".yini]ĴT´gRTJe#sw q̪h&1ᮥ !UUtiTE YeJDPM*`ӹ-=|o;ܼ~|a258ɬ-3cU G=Ԅl&AJ]"fU&!D4s>6|2J'˽$ "c {_|[ gg'&|Eކ;`}պ+BԈ׃e{[ UTnUv/BP1H]- +0 5Z N`ک}9㥨W 5:d2ӌ -u( CHR`=d>u/Y&+k)2+1n#C(^b2;Za|U =&4KS##ŽM9bm ,Q[6J#EnfGT8L}%&ք BdûqjV M۠z>A7)ʈG']P1i` -TY[Qm`T*`Wwq$ȑ~pw"GPUƶɾ\Tr|2O &Cb;;?CVZ2I,DGDʈ-G,(f",'׿ӟS|;oOƍmX׋4YmLdkWq05iHH+\sF Q*f[DKԨ-mKb戵 ]piTeWfe^U.cr 7UjV#D>˿0Щ UfD>nGSϘP{Vj\.ۏ}=U1eD)% ʞnkEVfn!9UDԡ *̺4W *lTZ2(U {YNj7ɷ]=أ7u&`lk눈oIRh*p1뒼bg ZضUyc˱eFU1g!koDvZhP!)'|kzHrFl۲)W9I5KI5=Oj` ,w*"@w:5*|o"A'ݯ" *P6K`P@J)''ŝu)b0@m}jiAB e"#c:TCT@j߻Tkթ (5w1jV+*j4FR Av2,ʚO3l1E}9 M20(dz@T!6*dZ$05רL<4Jg&1֩$RS/cSQIj2&bīT[jB%W!B]#x[,EDv< rw5g~ݗ5Wq54FmY5WT.Ul=)8X@PҼ4N]|sO}n=gɿ>K'}^\Φ$ik7U؇Xl^BMJzOE%`o2(LFU5,C+ Z62>[Vn"`A/P|]5UÛH@nMXEf!2KG^uw_,yꕛ&PGN੧w+epHWGeB^mqpTAU 꽱0MW!1*AV SNA.䲎~v5G\,놌&*0JG2#׭-HVfB *"t>Ywܧ!mV!JLQ`Zj[K.[^Y%$xycDfT\B`B>.e 4,ePȖcWBDU)ӫ[b.J 3Yܖ{~^pTi؁qR3=^NHf꡶"b ݑW9 ?ځjWb"I%)hZqvf0E#b1#$zY\敚3l)g S0Y24e ސ!2Zf5! R; !ؖ3yemYZ:Xe2fٖIaeR*|ULZbٌ= qH#1Y" ZwmlRUQ)%CА`P~ 65zmIUQa}jPz~TP1&VVqTuӹ&D-c|fs,QѩoYaJk[@LȡEEGUI")MI%d?L<^(J롬$RuOLG/i)&6T 饥$hjN`1}_{O9^uq\.?e̓D}e>=w/m ZeppV<2sT aI~eHJ5T@acGV0 8,|CX 78 V1tj}Ӷˈmo:t[.Fڍku_|Oe]~pС""HT%~8'uV*J!̴y6];\3~ÿ5 T[Қ_;!G+^Skܿg7mۖc,>5T  Eud7A)3ĵbՊ (,i 2UdkMJSJ^87)Mۯ;V 0Jfs1[-.k$ݕW*"̢It)Y^{H41@s e ݙxbRp\,TbF QW`ѝx4Ji>93|AO~U۟jVƂJ.U.UԪf}H&yǺ 1  :XIcyXT:*mЊ,==kU*F%Ry1cHZMC :F]á~-Ff]K Z[?xe͎pȑ`&ITV:V&6&պ7b`1b:$b& kD@ȼq @L_MZH)HLer1 BȦo9J*l#u2Og!R\Xl֔˰֋IG8zY`IJsUCӸ՚#+(T5Q*}ɕBc3")N0jR*T׿oyG׿/^'嶽^u| AEzfi24}HTlM kPR",vA#uohFd,|ȱm ~*MjE jd2\56n*v;hIBZSn4&m/?%"A;wƵ/|;#MoN$M% IHmTXMSUf.*0O?sڳw> ?Dݼy; ьbҝ˟?|wo_7[v{{^ /Ч*Z4qj(Xެ4Y)dI-8ֈuke䒌M2DŽ ܵ7Sv_~%4Er 5\>T Tu/prП__xO},ް m^@BM| :v)SQ E U 'չQ;o(I J\ ك] }>z~'>7߸~ S\mqvbБhmd"h_4m!B5`d͞]zd谱-Sp\W^ye>;i׶eU,.lbEu#3RkU$ :L>MYiPjy6F֕=z7LS>=oz;7 ̗t8;*uv(WV`R_sz49yk]%':?Ļ[xC?s/~\ ?<{_xS}z:a~#~ޮ?7}Uo|[ܧZؖ%_}Yc0]k@jn&H졯}\B@VEQj(YU^ؙ։Rj(ͪ LX؎uuh /~_{ßyŽZ3kAoEjL2>VWO7  -XP!ɡf)fOTk@ZREHqoz.\i!JZ [PGb*UWj 7<"{g{hIQD "dHVCg^ n`lR|nuVQ#VXSE 9EJ'&z;% V], t%LMA[ 'CEd-smF$u/jr3o#{,#"(6(՛xOi~mR9TtD@m(t} TFu'h"L1psAyUkt:UPU YJۺ8әF/@-/Z[,ծ]A4_[>IENDB`fritzing-app-0.9.3b/resources/images/splash/fab_slice3.png000066400000000000000000005165261272363601300235720ustar00rootroot00000000000000PNG  IHDR1LbKGDIDATxLgeu9׎'3BfLQLd[~itmw?o^Cז,[d($ (r[=|?@ុ^k7"P!J!1(VH  Ā'3`fDfb@@% @Ĉ!/ 0#(`F?`Dd ̌P!")``$b A0d"$C0##!2_ 1+LD,Q0(@F$A! VȀ QDB"(AK L~ Y"""0 /S $Q"Bԫvuein>HikWʼn{?;խJY?xe5.])nW3o^(1hٮę[ZC [a[VXlu ӎجiY3ٮ׷¦\!+eb-wn2̀z[Ͷek&R"Rl3餕ͷ;RYRhٶ =`۶mnTBP*L*9=?W^e U-+f4YFo:+@} 02)F*:(0 D@bPAS@ fdT >~ѫ *[uz_xr]oRxڕ_k}~k/Ẇ00`"03tC[wC}}^NW2߿uknyմeֶf2iuZNX pfm޽^e9 d<1pՍ0d`萑I@n{cyyec3]*=g,9P"3Rږz~8G'OS/$ܻ~czul>78>:جVR_"D)T@XEfn!҉1D XP 0C?D_r>.O?XgR7avH0*KVٳMi0aل2;̣Z   K@ b$B~  8"BJa Y3 8|d?!"!"~kY HH@qm$od`}W.C DfB  A OF" 3'B@F$ H(!1NOP)SU!l@J13Ƭ<`V H t=a]1zAgHہJ̄H###J`$@(o&ȑLjF۩nj\1+V 8v)M^/~{oTWNsksctdpdbzmH^WּN{+\;/'wҗͤ}ZsNu;{:W{ge?wڍF#K 3Ϳ_ypyi[n`|h|ϩ+2η676J3if腶f+޺xj@@"ͭjR*c;\^E2GNJH%ak4[_! Dfi|\,+WܸU|PdLADDt0wë􎎶n_j&V\ Rba PJ0Q] ء ;H4@%CX/mOLb71`vv{XH% aVR}nGwOJ Uִ 䲎 ;3""#JKR$r[ 3!!9"@"m ;fB$7*Sr< 0!*}5*`AB? X!o/ BX =03B`Ć~,"TAT @(D%dE$YW5t- Bf"D@hP̄~GCG#C7>J9'A$=A2+` D VJ?I=bTsm2 (|\~DPAb&f@T\ D@A0:͠!DusZv̡~'v,{xt Q',/=hi~X C{^XT*bW~f0V<*\[]z3H$S NK䋻kvC|㛿2<{FP]tm':z/.ͬ`(fV :lV7+K:rhwOtdı_EVa>[l9VyՅz}+8x תOKSlnnrm/^grlԩ}}|*hf m;+HeݪN"Y֚N2U0 S-)KɄέmkk sY( \hywV)Ra2BDb~p r]QbbT* @F@AYaXIX! aD!H&($1؀̄.eX\ X!B Q`Zf)3g8t>8޹~la:F#L9Bh7b&K=Ϟ}|G[O/676)Q% $d@;]r..I&i&\ouBV){ Cl4ReY!;{J0@`ŊH?+A^(N8쓧Gea IABD7H?٥/}lZqt:d O@bF0T u?e[m %׷ xxV$9ҩMʗajGS?ۅʯ~0DUqdm7zCO?}Fv;J d:-.44:ѵ?yn޺CF:;!rouy, dWٳgO6a ӶfP$SJp!K3oT8㚃} 2LOb)EKyN!͍VMPA8241n]tGfV;Y}j-./`dtHB?l7(6+}BL]ktV"m%\^^MD6+kfGJσ0 dB0[խL:mԦl I(sP6 @TJ!2  Y7qs" ]44a nuG#,' T4ʛDlW?HedҴ' M&FCfmṆnqAެAX(koqnC}}mvz:ҧ^xth+ש)0Qխl_HOh儾AX aûvOܺ`f}I-ddz}k˯ϟꙧ^xL1vPI2XtvO %PVg<ńJVXq}R: )_ 'ٛvr9zʕg^x@z~cAPxrdldv~nvR}' DJB4FRg%Y`P?IbeD(,) ֜ siQc@0&7XIc"BE1ڦ!XF @f9bTmAПQOlEHk, pP?8 2 %G$ {{잜D.t6<-W)Y*ǶW}e H΁ yDŊw@F=#!Oz © V-QҀ54qhTfB(u.N*y8Ƀv|Փ'4CW^Z]j6!'O;m_MȱFyojȉCC&=?{N].fO Lt0 f6}\|iUyGϽ?_L4;n*N{}sNV*=O!W>;qju;6ʖKm/x|J8e$s@I?fn(W RtmBx]7?_̡;j7eNg`blim QTvG^@1vVdVR2u[r1S,`iqݯʅLv`P8鄓s @L%M2iK9f!F%%Kf$Vb2 L !*7Pw<$@hEo"2"!#2@M #(FR3{~Vw``Opj_|kL. tucH ]Wq̋_\1d^<pD&_)$29VP(mNķ/]yؘ&\6lC Z-ӔAXo5gF{B:,/w!DF(B۶,C쭩[`2!# $Lk6ttS>qfXRÅ2 Cc%a}"B.@?ͦDL%[޽|9uUy6vhb\^zWǚܽotrO_1SL6NⲕK%L# 8I'-Aȷ.]m:';D;mLN9^]~7kopcvݭt.9qx2?225k?[?T[/>xy_~7^}#rF&=y366{mo?/,nԯ^᷻c cw<^O:R>o rL, o4;iKٮb˥lx0W*ײ{򅑡B:SdSy L#rnp?QȳՕi!5 #`'֪oܽzyt8Rl6r}cïoO z !tsD. B=u-aX,f3Im7Hةt}0 m zo˙?3i;BJR IJ:D@"Bd.\ÚFqFXq/jdEfV ( !ڍj6 //.=~ʫo|6^[W*=Tv\˶d# "B'H$յzGR)AR"loW;zOȈTZ*%88{dpptdV0L& q Rw}W_/?~op5-8"0PXfg|xW5:|)u[7_~sB6I-QaYJ]R`۱[f&E]5!$,nomVTjkn{-JO>'g)qs} $P!XrmSG=#S%-ow7gƝCN3,˲ld:N/ރ}]}Oy~Ms`dxHfgb)jn @ J{a}!k?~*Z[ƀX!lttPQzx^fqD(-R@4`-lp````LbGO $Hb LD saŌ1"Ĕ-#F.#!iR #da)3>{bҌUGW޿|ŧN;X5'$L,tj[72M}1B@}DDzjШG"BHY`(47qQGTxD_\ {Y$Ο>g:hooKK['s@ [m4_|pvanqsm}ngw~x|;[ZO'wny=T*g= +7KTN2C#J{ۛZ^k]Av֦ͭ å5'=?泏O?Bxal4&F6 i= azvAF"Bt:7Xiq8P)޳'Y[_dzv}ݽSS3 #HZR{{7[\2RH*eHe"vs=0/L0M1Flw4{"rwFu1Mh4uiE̤@eۙlrivK^$>mG2!"tl3hgo8(iXVX h+kkB٧~XO Rd> fkhl"$JM]c'7)C_[_{,|W5 )U :FPA-~3PmP*Z) lZ4-N ‡o}Rh>3g-d/4 뫫7O$y~BX٤w۝vwi~ޣfHh-J)"@DMߤl)cy;HEw H>rP7QN# 2|U\4p+(n")!3EODO(Y&0RElTŀ@wx%T"@2tBv^B*yfg<}T@n?|OO]ZyʽR1mn5?٫gGO`}h4$@v?8"ISp ե)jf5"tU$<@C՝5D"\&b1 eHͻW.^H'L@XY>ښ$s$W>ʝvT eng?kGyr(f}p/7._.s_|ۍ`im3g6Jݻ:=pvsG~PX6@F͛K3a7WV-?~O=uK$Ľ{  xDZB)F}߁a"c-On:{Wt' PNt*pKc+{*}{7MݶU""/C4Pzb*4 -ô0t =aފ Q>p+skb:t23*I`AGOȎ$w["=iQ0Җh-r:A&4oƯ~:9뷺p6W(fpɟNȑCK~?{`i;50 SfDz "pʉ}_"DCL2!m;J}hwygV?FS*嬊Te{G{)F*$"b/ʹlѼ~Ƶ۷]P(y4 )@TT 4077:AhT;-v>p,Dݮnn֛Dʝ{0ƛo CrJ__P-3!H8 &jno7f7ww \b^K0l`'D홺tݎ w76ڊ񱡡r4VRTqIhM4 DtE7528|Wu5ѯ" (z2*}&BE,/'$4cĸSS#rTSig~L8at!DAˆ&iu xtbΜٻ2S$][W~>w'c|>Y)ۭN tyR]ɂb==R*tm"F\#ҘB4f/0!hA#uc4Y3f1aYX[MCH{ቁS'rL'36߿rA֮WBh-k~_Y_tmcPoͭwDSF\fJov:;;\_\ܳwl'_ȦLL*G_ꑽN:0Lٗpp@_iN þ{F&'/CR B~Ϟ݆ii~%&#{f%P\%dVZ@rµ i-K^#ڍ)LDZI"( YIidz.օDv}{ׯO a16M$ܳgwypz$%uGc bk} Qtߪnw^Ho70"R;-KHvZRUʥё~jvȠ|>/ðl*똦A,ZXX[skyzf_a4 4 !; 0 KscsmP/TJ\7 ˒V2>qԽx~envprߗ#-ѯޕ#;&c!UAIёR:!D}fPebu[ׯ^zfdb!g Pƍ2IVR)quۡq$lkvW m!"mo[~wSOR(۞W(Ӷͷ/^u&4lfLJoW) B1 ?Tn0<40WJ[/!r$"kPE22+1!:>$=`04NBDjHi).|}#* 3f[?{,J\!if'˹WW[G33v& Bn7ۍ5xfy#Ng3ǎ|׾r@f1 gX&*!U\=2p,@e B"!( "VETT* AJ!J5R*?AJ#Wo^?02hҷ=ŏ~ۿkC{nח3O?_ƩO|J绝edbϞ]ã*eC㮮]AtԀ ~ q1"XOuTgG2#7DU&Ԓ}`2Ə04kյv/|5WmݝYxuGIPJg3)DZLS&?]^_سg0TzJ5(|N&KYSg3}=c `ިݽugRߪ޼hԳ}ŕ[7o=z'WSIW5Ih}7i[7o3Z4R RJ]_ցI)1$ ۵ZM!燍ZCGGﶷַ77v5t73:83K* ;^oS,-n=~0_]\ڨZ.޺s{nޚu nܸ] XollW<򅗾\X^}g/k!~WhXJd$Ka,JhPZ L5PEqȬuڧ(vd5<%ҭ>QXH9̣^4K}}\ ڎe0-v~Gʭ=GTa $P2)WXqȜe]páaS67081ͦR)u/\޷\rŏLկ=N>3|#@a>D0R BLg`hbpfP"j~d۹Rv[W.]qgcΖl RdDԝ)PXI;z~t~oS_hm/-[d OL;~) Zn6iw~aI+B>:Fv1 MGCÇO9y! 7O%7 ӵB2D:;8PdIqK@4P@N*58<82"Զk^H9#b_>jY>r@CcV01n [CE0y"/Tؖ;hddbz\I "wpzƉnnyZں(7k%C 3O M}T(:# b{}~9e'n\QΦ:[,3_:f2]o4Cc2 |JaT0 $+-Զ۵: Ŀ(G=)#W㰏RD;8"1"tL*V#1z}<`"NL*w|F@===ӏkho1_,8H:MzPI/ LӔl{!y G2b[ˆ!,2Փ|䲎tZ\n\SwnݸsS.3YJJ)0R0;ϛػT궻AtT:#eh-7r(Rt[+6]JB̬gJw^}sp_ )[LOh) 2M èQDc+4qCɱ| ~y"O!0eإrX,4\rR)JByx1[^9斑N {*l(O?wgf %Owu7/νWυ O} O}NږvXvN'kgIZVrE)\KQ2\DZmH=[keGwRʖ+c#T4+I#vHwJDʯO'SDH8z? @u P+|vwtf4O"0 0A2ҏ@w񁋣&w,XF0&^xSFm/h֎wɧ2>ϗ2{{vn_Y>22Tmsv=b6-3v=w;JM~ F$R,#C^oH) A( 0G?JnoH,24dۊ%.E+RA  oS12X3k<;8[vm \.rl6L!AIuK󷕰/~^>ܻ_J8Bn? o~+so\<ooݼ}Zd3mL2R( ˃ݻ.gç@ 3gO~_7H*Tzu{R2inLJ)ŊENcyfN0MKETRNu 313+\N3KԶ&Bo-j'L0Q^\+ٙUDP*|7|Б#njZ92:_u{{J~B8&i˲{t&h7cO?@O%iه*ɉnlM!8`_  |oZwED vN"s#XrB$;7oN=zȆU.RGy3{XI۶DVUؖcف 7y޽f<"V|)mu]'H=8?u=q;wyhXV6;~doب5(:BQʴF$ByJ,]P{J4 arG);hM t"f L+ l{}ڥצR|6CD:<%jw^d Lð6M';3ߙIӗ?jp/IYvr=_ַE)C- :s]XV[5vKe!FbO-Yz|GC6{eNT*ӱL]]ng9 ӌ1V̊ރLdjptZljxc$BEo"*TUĆƚegcp b:ҒEP$Dbf8" dox`{!x"E1h7 eQۢbgu" z~zAҵ ;t߇2=6l P10:^wkm\6)_JVV{̌T {ݔH*$Kn7n_ruDBiIVSa HK)D|t:G @a+E{l& *,lp bADa !ljrW~N6MA`9T*J4k cev|sy<^+ x~[سkӓ&әz*hft\6Lv-CU6ڔ&zZ͎AH`j|pp` O@i/-՚TN;nm2l2]fSADà[ ܄c8阎Mt  9&)hfoDDƝǹBv\&ݰ۩oWRT{iMON㛿~g~mZ(i&nUkd)^[kaZ۸{g{?gPMd40)ff=$1DJhk`@*N8a"CC;ܘEblZ^x#?_i#I?.LՍ )GK@o1~^R4jZ`_0omoURٺōKFO6+eb$];1JF@bVRP /Ƚb!0#Ϣ62-ӱWorHnjLFH'Mc !1(-TedBSE2! ܹ3@Pl L&,5H)79g3g9, (X T2,@H}}h; @0IuxNu?>;) R jx`D,!(lm6 !"vfbS[u Pk<" Y t֜hg>LhD%䘆AR*mxLȞ3=sL!iV&uABnn Ǵb&\DZX!%Rb@ixLlmlw덣MO@oe "0N:Up6&,4TwphBvCA2 [*i;JJHDN?>%6Sfam^UL^;hתfk5mfײ)'N *se!ө\&c"dn2 )%훘)Uz'&7W^˟ +_ٽ{VW'OXJ P*-ج@ fȘ$CVJf:I- vML a0PD"@bfa\vZN6<*kٶp aXaVw %)rs_{/~B)H%9;5DH$ ƚP* 2/FϘ*( C0$Nnwt| ~,) Lyw|Tr)8Y?ynoF.@()o&ҫs&B>&al&Sݍ:gy|&L%z@D4$i\ZF$xOzMmtm)ņe`$N±l1=w{4K$()(j}bEAFEM: P!Hb QLD7h9Hw@ ({+ݚDfEQSRe"r{;/81PʐA\޷{TڻtɮKcAIw}䳩\u̬20Z,s~֖U3Sz/zC3ɀ܇<Hn0HCK7o3'{Z]`fB˜i$yOm(~Iǿ$(c>cmFa4u'cD.E4Ŭ+%'wıC%3 vV66g ' Vc;H 35@43jJ>a 6! C&S LMbP)J|x~^۳grpp N/կ~@]7 X*zDa(L~R(oߜy>lv8j4;~;!SoPZ34T:Iivw٪w;BZ-)bVZ ]۲,ROȡr8um>v핗_Xy0U+SXVAL8w\t^Wu:!@32a&ˤӄ$Q AD,Lr/\GWe .j@eBKt:J4Dz=8p'%Nk۷恴Ig"YzL;Kbiλhw$hP#pGF,>i8O7k;ppKÞbN:55x⇞ױ ;M+n$# Ħ[3ٓ暝Nްm3ڦm/o7[JO}:Si6L&Z|핕B2?4ݮeԅ7~l#n&yePGꪎܯL]uFn&KpF k̤`'` H/-sbE> j43=w>r߀i;dS^ݻ'w|o}kcnnvl%" X}@aOOY2 aVcsiqph$W(G%fӶLB*LD{] } 4 Np_~ۿ$l2KrӑP`ݭ!PJa ӬoCs[T*gCC{5-key'ǫ[1tWf;SSKk[= }C#ogsmޣvDR:@BmMGV T(>˾VJg*`#*]Iq,YBvrIuNY4:N]T?Fv2 DPu9NJ0<tO){/ۻǔTeī3-v?DdA Ii*%}pYFzΉ#'X1RRZn`|h8zdAH-ʖtqҥ;L*%R 8/~ noG?s*et8nlwW2V5y`8d FLZ -,:|\'݄0M/L#YÓL&$D"a8bfz< V2cvb+^Dr@n=zuđÇE "w.ãM3ZfL'.e!Hq|-"Y$;:dP> 6MhUOl߫%_%krV"YUt29m6z*=" ۶ Ti57*I_'Ϻ]7:t6fШП=s\eZ|1?Q9sp/KBp\WQaa0B&ت6=𮉉ja;:;\)]屬P+ 0tFQAݸ !uZrDQfEhd^?6{ZUJ|/:N$(Wziͭ:T^۞}m߾sO(R*<̕K\A'%L!VVV˕Dn6e./tN'no ͋眰se=z3 C$L@azl[MT@Ive+0&&&wۅ\&WJ9XJ6]zgZQ醾)%S ac(e,,kٖџ;qn޾y[o}[\kϻ1u:CmuC?k/ʗ{?O\:niyjcخB6?޽nQ͕ccF2qphȲ6V6mB%UyG+2Qtb8F@1:* 1դA pt&j4gg4zK 1'Ǐ>vL jfpm۱-Lۮtk~ostg //̭:1oѴ4 i^xwnMM?/Js][]U4M%MTb)C)i)RDòR׭]z WַiT b[d.}]i`e[luWC0c)%u8dc#""5D`bO_ +ADB C%3pPZؼ||Gt7t$v Ghx%fQ6/4g%(RYEIZw&A/] R>uCG3VR*dJkA(]'Q(zz vv](9س{ 2??<0/Y۶r}m A(FAńk T Mёa)TA.Jn4՛Dj{Ah _d6ɦStʰnBq]V>XXx0;s^*d3=ݖN>RITrHo_Obd,?2VɗL)J &ɤev &l6,d/Jw>|kW/eriwſg7ǿ_\Be ~楯|;>z("6?/_wfZZ?agBi]O?zqpܮu]ABd\Yh>ws"B#ҢO "X "9 Db%Dt6&ͭ{-m@__±ڵf}IrʲL0iA  m-/~<2K-JDFa@ 65{$ ``ΕRed_GB@PH*w~DBCrLE CN3CC 6V;0޸d l%Jou?Ň4 |2m8$&gr s?hr{ VQ{hgSGpq2Лׯ,nտoݳqD"Uds7nz^h[&K6, 0dp~C ip(8,DtKTAR닣FRÇq0hDy ݌dD= ujH7VuSF-4T/8Qˠ$;+p8rs~gdtNdS*c D+ /^r͟lخqv7k[iM~Y#k4b\X8ιg?\G˾ W<>Z(v<{L:B.{`u=QCFtPEnV:RrdY*b22_m[Z_R:57rE#e$PJkeَ0ť\@|"noz~wca2KdتnA#o^_[kA:>9U;pm';N/U͍շ{˟+_Vaa++2BNFM $ 2jJeR'/fgg,(o?W<{|m IDpChf2[TA 80lwµ7kgo͎ؠ3/ЮmWgNg?7l4A ܼ~W \^3?Е.$rSOrNqh0, BA*`nii~Z~qp#: z wȫC1Y޳^㤯1jo"BTf*` Hӎ./ݼycnuIR ;T 01y#hk{vRn>$0hy !Db)A@! B{$肄r͚zRw#!E(#3Ed N'۷lAh7v썳>%HR(c.-,ܸpfTvi W.ɾ~KP)szle= es5lpymwtޞJܮm/ Iƃyq/JH EPJr(d ֵj&,C p(#!0!*!*h1P3_̳TDwpq~yW·_)WWۭ'MN)d2,-,%ťj5$Zvٰ gmssm}qml;JӮeI nwqa6i 3V_ݷBH&\sILR*U[y?Aq@}kc#גtZ 4H?}v9G0BԻ20"#goy!ЛU@'8NxdF,Ikz8 f%rQ{ϡ@aɆq+eJE"yQF0( ~B0*˲?Z6bwAռJ шvPF6 J~(x:B(w\%Jq'0Z =_I-)dńd9i+7-Rd`E0Z[tB].;է_lJ/T@O@R(RDaD5GG6J "PJ"8$zX3 t(D2Wk27~[LO[nCM Xe=@fèmo{~'WqΏ !Wƚvmڶ}S.ųOyﳊ>=Z) @YiCǻt' L86΅.Mkya_o7=d4 !ُF@ V#QE9 9үt^T6!F$T"6>q,W \XV|ı[VVW]ɤSAJ1jTv%imon..ۡmYbewuvo/; f%,cccmm]ok[[۵Z :NUkԷ}O~gd|ÁсB>wt&T޾qƋt:YZZZY"a2T)uf{7._8xHչsG?ؿɳO,.d 6$s6_ti㸮e^7mGʰި Wy'TAHȆmߜl5O9X{}ph?B8p,7o;_ j4?yׯ]5=>}cۆC+[,B:^Gv84:8zƹv$d",ď} VF(7$±tT~o$px6YM , ۽aR:\jD2Vu}-H;}#LVy{"0-A$0Phut2! ]Y^| !47R* J EtJKL24 H eĝGLۭ@F &u/+d%ɠB18wULdgJy 3SS.lvdZ"hfon-/.$:3OwTni6t_|U20;?jgg:% +LV뽟_]l&LR PR!)L;aA{ݫ7T]2> (%zy4LR"m⨇Aűf@7R)'>QlO=L;8#[@J*^e Rh(Q QŠ50h R3$Qn0Ϣܻ3u`|g_lvìkK;,H7G BLr=7?M٢K;|౓RXV4d6{svcm䡃|SV*iue^Ih2JG[F҈ X P wȗz9GGFt) $o00Hp7HoXH(l&9w2H 7gSw]p1t֖7Vۍfv{z=Nm{z_sNRR]/vVVޞ_XX\\}xу3+kjcgxu,Am;vghr~s lO>\๷O翿y'+_}ύ xL_կ={)W>W_W''FF'G%L^x`b|\4궺'ѧgp|O6􃇗/]Y\YKrB"N #㣽nv=ILA(BD]GۿB`m}J;62?<18o |_ X8+VΟt"'sD&H(j,fq)fP#KGjl6"#= bbj\#iEҸETY*f{){l8e4*Zd.P8V ܩ'G2|@0Cz٦~WQol\8xCGCfv}[aq'~L%;ݎ b-QJHI6ŘJ% 9I RIȈثkA'YP @8NcK:R?zzZ A̓iPP(ZKi=E26ƫ\'}{v{{\!*22eַ7+\Td-G7V&v*Ûk.$ʤ3L:K粩L&L:D0 LJZ:VӚY_X\j4׫[u]oquZM7ZR8v}OGF6[~"7-:嫛MÛk{GF홐Ȯ鎌8yl|`A߿{w_gb|dĨFoJ<7Za$S)&tڮzfQ"l&3ffG{8ОͿc_үd]w߽3kWIA'_|yZ[S}`?ιs OݿoOvf߻#βfP=4D=*"*1oBʏџuPQa $E;Xg[jeh|$D*j5n~0=m%J:"[̆eL=zfLLa:n7 0u0"ѪTRJEdajWR E䞈#7mFj㛔wV$HL}sRۉD:X~t+]M|F9 xhsoPl4;Ljh-NJ% .^Yݪ B>áa% wR_f6n8MIwg{K]c}CT2 RH( Mw{@*z0z'D5ެiaN9J6D@BA@^E8iu7+&Ѡ$;I;_b'9<.QPĩ_-*Qq\*DQpT*S+. _MB4C.^;#R |W/60N8zIa$9R!iujKK? kUL>^ HTn UD.-:Qff5c0t#Ą#[Cs%cng >=!ҙnvcà ePaT2,IX$|?,cuuTrc}#NN2Y7e.%\7K9 4,lCTXɤ|ԭSׯݾѕn]tۏϬnT?^XZZFbl\g[|.Sfs}=KK >ytO>Ğ+{wMڎ:"Vk[(ZbOq{%d2{τ cqi vRnP՝b)X }շVR}cO=JdRa/W>/ڿL ڡB٪D*}͚a2JgZ}<@/V@RJxqǯ*)Fs`#,HvqhR~()E(fmW̊D4C(!0BT*H4k;o-.:TXp,+IV7~7gacX`eĊeIaJP7:̱E2Kc mP`eBFm.H_-HF}nf7Zh|o#wll sC{vo-ZDwp`)j[ O #@ADfBdyzR.3AS$FFH&ӽŵ#'%\.J38{Ϯc)J $Baٶ㘛[/^r~B{(3FE];ҰW oku23hn\FI] 5:biv47;704WvZ²4#t.CloԪF;pzz~ ;#7.ݨ6;ﮯmg}sS'WWy{Z-J?bQq v66FDCv,ZDTDRi`o8L"BboOM`aN"e;w֪D>_=2:߮[zvHMD#@%C$#0@-0d⏨+8^{[(^**yg$mDd2T1KuLèjW>xͷ}+_̧^pmo|GōFC)LRHH^|NȠYkDDRYD&e1 OݻrF-Y,e$zJs?};:L)x~ص+eYRRJ0 ݽ{罏N/(B$*ѢI2 S:;L~*H(gDa[YAl!ޟ@dw"ǯh,)]D(cL:u-4ԥ/^֭pD,Vh6ɛfL*@RQ - JSc"H@/58ÿDq8y\Ih1/u3c---۲Ae5ۖm++ cgjvC{DJy{㙇333 +,0d*;j +:v'xg:Z(?pp@4>OS. 9~hdt*ut* ’R.̟{?lR ;͆e[AS2@Yb t\vIɄL[!c줕V¶DIbS=3:HaguighĩSR'?_~f捩D.kp.{vuɃ^7|Onrlԯݼyz4LG94i߁Cfu~'v{~f1d5yp{^"bPaDzR0l[0 WEC{Q!+R}D"n?5$#)^N(19DcHP];CV4ҙ -.ߝZߪ˕}GSzݓݮ&\ײ0TD D!>.j7M`HHd>>9xфhGiɄ*^|tO~7Phת{N<'.FY 4en旪~?24>28T)Q(DjuD*r*LݹrFU)dSʗl#&ds{+_.ݹtz8aX2dot'06>pʭ{V -yc-T؏l; ͫ+#R,F }kic[ W(iYڡG(=-RJ:skcJ0Gl.iLu>Ng =SSHLk^ mV255=4=5>_( C,cb|4](k- Ix߼N2H>}݇b3so{n_ODUݮm!IUgT-j#)^Bwu޸IUdtݭngY_XX"QD6(caDGzlF!Ŏ/Bc2ѵb?L#=qSH&V\\Xϝp։32C6v}hzazz~nzqceQmy.@1H58J'@Dcw~}zznk{T)<+rw _qm;꿃$MN}?2#RkY%tu P 5=%];.vrvI`@ݍ֪ZYUZ_lmlfZTeE|+tv/T1>u!gF+}ů|wA{uJi sXւ0L[h:t <!3pt\\o[鞮>q{F4 ѱse,o/to4~ݬj=f˳ӹL.](.޻;.o;Y/ V DAHhzDqd!'l y(Qw*L7[LR`@# # m9=t(fLFD;w˵Z&pg.+~׿6{QnYٽ0&b%@J E_xRu@R~g2ӽqv53!Tu_`BhTEAH2K y`5 LJʥRB| Ap() T9 GQH2:&*Ȳ(OW!tXR.ڧ"%̶LXj d@T2V5(8D4;۱}BHĸp< RH e({;}>OaZGydbߌ8[}/kiN1 Va34j6K!ږm+ 6U!`HMTI"(!ZW [(߻5qeA$ Q JM! LR~b2ٞdLlk].\IwS=C=F&UwB]tu}qausiZ4j8QpY)D*Dg# nexG<6BGXX)U4l j: Cv{ƛV~|3&ctutv5lyR (oomākl!#3cDs,ؙd, nhhB)꘲ƭ7wCG_,L}n蕗Gt?ԻowVW{; ̭-.J+omW>ɽF3\X\i2]w-޶M,Q}!/:* gȉ8]AʱUD@)aqDCJc%URfxp"8!@3-6-'4]ojwѴc"_|i5xnY:q e Pm*Qq9&2"%2pPWΖ4!T ŹQ˶2i͜9#gv\_/#gN޳QVL"Sϧ$W7M7S;|`ؘYau r$ P%Z8fղB.,Wn^~s'x+}fm[EFmXik><酫V@eDr$RN1Y ["p@*_wC"]Mj$9&#ҙ(/ PiV񤄨o0y?HPp&D@ % T/H.юA2R>كDWMp~P'$nԚׯ^pI3(u=mKwfSOG" | QDg?;3=}NSv*_p}k5!@i7$LNϩ䊗$7z95J堵6vvWW/ ߃KE!ړ;ș<JmDiUOj}lgjv,ESZU-o>tDQ7<:kzKi+ZWIDATGaycمJU[g2i8[9QDmS45=?Ng4!ǁL#MŴp^S#:zA8{O8@t;G ˊ4YY49eήL7dM 0 h-N2CC"RR5 l,/bƶ: Y ]ϯ==wϞ{vMN 8𑙯|gVn?y+Yd˷g7*jT.mlmmc*WC9b#%#A Uw{n~y ΞG8: +ڋ/o{خQ&>LC)B$ oNJ)""M P\Vu 8qnlN,Xg!ߪ՛S]VYN*(4a ]&w_zyڜ޿2C 9(cAbO@O6C l&Y/\>K7ׯߜ_vtRrFic oߞ BT#3QaWۉ"!?X1$@vDHH&V!Q9xF$"Dhmj,9C E=ȅ4*UEV,IZ T@Q0q媿.XJB-m n`g;30|#Ҧ MA %Cq,Vӟy>rp=O=d]1j6f˲\6F+ K+âR]x B?JAj֛NygmCDs* $/ 慶IIiv=$VJA)MvBS %\(\@\o}g}JuZ^ךhqP_΁]I/ Cm?\޻\Rb9`W.=yN9NX,~jnq+gG.RjVw\zf7nެ{AooƶHXY[B.pH9qR٨Vog4t˲,`1@;,Ad(`,)Ց ~޸r\̜| ̶M@QM:JѬ 125JA69Jrҡo;NRޘ[i-9c15J@8 Tjnɶ2ZBpr/5Bp""Es"mx"cc#g9356\ظx{c-|.\ rAUS1HeZBB+*)K~LO<~ljl,r߸v\Wχ}8:s\?e1JJs"I~G cP@ Q*3r[¶LJH,;}o:j?tO[̅썛fXsM?fY*iOOORIFf+ϥMҧ[H&MJujS++]F)%DB5*(ð]>odJף f# թaJewW 5J9pjY|"JoY Y%@]" B)bE \@`'?dT 9c֛zG׾o~# XG>pJj1Q1 !(4zԨnolW+UZPT\p(8SU WWQ0󮉶O~AQ$Ƕɽ-Cɕ9n+=uH5F{bM0&yGj25n9GB3qHRBpu}cmUZ|ru](D|p{w#FeNaŋv9%+ʫz+ DuR:5Au| rJ9 A`KD'{{с|6u>t}թC>Paga̅C>I:A@*@"@y)56 \pLMC$D[[>Je&7g?l NA4-+5tǡy԰Gt^\׿( [5㭭֖516h4 yд::s/_[K++rըAё\B)°S C6:ԙ{t .{w^UHJ!W%QXEs-Fr% c[ "W2 Hr @pD"Jn NW:dM ?kU " շ Jy[*t@f-pApt_i0q%BbQrٜ!(U$!8",D~` QeJES_ӝz]pNT,dq*Amt~z_z:z:bMrɨf2-VyP5iDa??ժQKD^"TU5ϣ|%\B4p@"'T&4PBuHR$Jf(A8@* %2JDP)"rA %PI$ +h)j@ꥋ׾o]z}k}cྕJv8h}s:zZ󨼱z{͵~7{ei qa[&)a(Y3D3Σ84AB`j'A'Ii g^f:fS*Pfk͖[s]/ s8b#y̸DO!ЈFj hjPMJG6ED@`T@$:L9\2D OɋT +JZ=ᡡ3'OMO璉6(Pqȭ_v{GMkɹD$n@KZ+/*W.p,]&2@"DB)6uðff>tp/r|x}W\>uq%!d5#B"p.%rtKf )L0l?~񇻺FVfo{=Iw!'h55'U("m#krC#@Aۗ/.j׋ ˤ[I[ArڍJe{ z~w7*4tu)R T VT"GNE<;{rXnc@&. SjqJ笤oMP7oTL]r(t<8FnI [BK,!31LuGUYmט 1xW'O=S++kQ4-w7VUBvI"(D0t~vqy~`'f4 r,˰(OW (ٽ?_."lm;:Xsi\ZHH3Q8HS6w^jyT9?yA9SͦLLCTbbJrrtx|rm{R)%PD,.]J(Y}(}s8 c*6I* %4Ad.2* N@oHA)"@IݼrelxTڹtB_]]ʪiΡW_~ xժTW7W6;: }.DE~L98) ʕ B#$""QE绩%Di; 2aʕ@OKWkN^.v*ͺE!\Pbڦ8)vRv:e\&ё2l&ߑvdBҙͤ)˱L0uӠͅLSXe X±3PѦ\@HK{aaJ|:S'z(QD9SXS<BjCã=+Z*#%ߛ*5Q D@ A.4SD!6e"ɠcb/ܿwXpӲGG>hn>z套ictt˦]8wۋN>z0.:ӝs Zc*ن@9Ռl.)m{KgQs.mRKhQI1Zm U=`['T Pe>R= rxHP(c *. ٧"[N(HxqHYtKW/ab \ 3-,<=?y3 {uqybx0],olOT#CjU+YftkOVkίTf], \Iv@J-nn޿w馩(@,Hl7-F^Lڷm A*׀R9"h r"ܼo-YzwW\Tޙ'Q@lr Z`T.c  4Wsr-$P"qUJپ8(A!O枉]MԔ+ܕ<#=V@JSS8q<.*%.qBl['}za~4A/jeg3],}ޞΙC 7pa6 qz fkO NxCY-ñL1&8g1 MÈ B/Nz;l&CQh4Ϧ7Wv,QbRMYh:RBPOaDbzHf(q=,fjOuIBUϙF}.o)pQ({HTIxT3}|}dnaXNgR `X1OgscCLި6Z6NlA5GB )Va\VR\|ǹ( lw-jٞCG+z6w0A JAT (BuΙ.%Zg_썿@޽wס':W/n˺g;{z]C K*jieRlZ^o]Z홚r-+T==!⺮k!/}KTulZ^V cnYK;+*8fj^C"=>MMF$)YU5G%+8q*N}7U$3z>y8( I Ƥ|+-Jw2`0JM`Y@!m[{8y#G߻t۝>o׾L'TӀ$HUi/T"gc ue_r)k#c!f<} `;kwf'''nJvzI ў]ƦƷ+_ vim{pdL9Ŋ/^zuWJX: A)\W{䱧{7Vn^k MI2{#xo.QB"Cǖe@sBD%a/60 +iCr\Uٕ^RҼ=LNml4 "D($I \^R3 .j5c;}@9KGMwkueLL;os+!']]Q|/ PxHhc+(&[C]k+wL#♎^ޙNۅ\j2 CKK4T>B# HIP" s9SG\gIH=;J,ӆU q@&eKuy0)aVR<*wr=G}_g?>#)YyQXoMBFAGRň" x2J@< )K.ꑐI,.D#!aYvը߾yʚascV|@~.LrO5 9g ! W(O^_L2G>#55MtnhR٬l*U#4 -ϏkI%8GZ5Y>r@ofA칡19211ɯ~d1ՉhXM5 l TaE~z>=(DIpVT$ڎ%0@od&cHIBDJ2. ڻ- x_O=eReo\kKS= Y@Q yuTpkMM""qƦ''ƫ&ܴ,Psl:}7,tf=BWń!c1(el\ŁuvOKΝO?~~PQH*Ih|OC3O<1B"a\gjֆ>QD:\ʙچwDkAU*Ip*PzT.GU&(n4OtvhxrS9'{^4 /ޣGGS(s\#[ N zZ5\z^QEA acڡttҍ bM"*=ES9%[%Ȃ dPY. &KOrr P/g\YO$jA25 UH?T+ @(nUZ덏^uջ ΝL'-A`6vo޺岩c]ɦ>Q9g\"aLZNw~xZ<9s_+kfmT:qxG&cG MTnFi\˱=wky< #g}gfR6; jJۋ./z]12֑Kv*cN(z#s=w#GLJ (W1prz-[?wݹ;wщ'75U8k43&ՔE @- A~D"֡Ĵ/G'd7 o~ b~z`f"G]rZ)S`DHNL74J[SSV&㇡ͥzGF[-w(DF?)L鈂q|&bB)!t"0ꞻ٪oeswo,RLTKT tj'; B}O>{m+ӝnnW W*>18B" Q7PM\W+MIhǟ(U? js&K^H-U(1DX:rY./ٻݵGF?ttK|Bah?09W2q0֮^vHwdرVVAq>rt7l4bQJHgO;===(V,aB7-$tt FN:1 ^|iikwp|ÃA:VΦcϫklnΝ^Q@G>W.,Ν 7Ts{{W\7޻}ܑ)|G~tth+k7[.8aBH],Ji!RPJ"Ŭ y!8;0=EG7$8)njyPy"|*E  FQɣ_:Gkf~x:mQfFGʍfTҨ4jJ Qzq{Bۀ+u(4M淖FsbE BM#mGAaQؖa./X>tĞ(1c1c~]z-M7=˱C?[V6W旛\C"@0 Jb׶O@apC3O=rfv&AtVkr (?FPZ[IQ; \jGT|'@;j]{ 8OH~H3xMjEh0 ꡐ,dɹn\fttΕSw{B4qJKggNvcS{vEQX tݝ8dR, Q0$-j6bX(tv L>.ln~WssmjL)jBG*QZJZ[WVW7T.L8[)sw DJd$kkڂ@9m:Ԥ,Fr+G7DnѓFpT`ATleqqvvz{2T`o}w{rC8be\pHοw {<ƹ N 4-RZ+W=% 3_?w Sc< fi~x#w4]L #[(pK/_z#qɇO;TaH()mjT>y2㣹tpٻ {O?|d^lTxf@AjVs<=S6:ã|'[n1$zkb|ld0\s8 ;&adDDjQ0Te&$DMw= ߻cGN5Bjlnjר7w=Zs06H*DYCRT.C!CN!Y" BH8Oi6PR)X>P";҇!oT|`~V֘ؽ'r|]_^> MgΜȳ8ݯ_z𡉩]cccU ]~/M]qafz[KkLSM<}r-ȑ49D&bŨr:#GbEhDܦYcsU/WAw~k+O|bG!jgYB5M?y٥͓O,rāߨ7t*{~Yy}ussgp@Cnc5kjn wzO,#d`[&g\5˵j6.ݝiGQp]QPk\@ޠڹeY<5B_޹yv"@fQX5S__|R}wOBj梁Zyty)OdG\D3i`hHy'{͝k~|l} lIg[͗wxL>έY T(қhostT}2>z`fi#cG{^;ٌ3&tašm;MQ-ms4PD2 3 ݻw["*mm-nnl*ucQSV# bq 1t6͎|_OwWgWooOGGNZZkUk-?}!ҹD顃EҦg\} *SPJlraxϏ@OJ-mBFo_oz-1۷{cF֚~\;`QNrB?|şDǞ|gXѕHbRR 8W}>QDf|JFÉqB8,J݈j &z*P]cad:Apll0ӵSZ.WK; 7VC%T8mt"A.ȃQ !QĚMzđ}xlb l6edJT"DQ#C#\vTj4TP^R2$rvE"QѶ 63q$+u,d@hN `2VT#"O '\$$ JS+jbw vl}]sղI߽W^~Əݽ= 0vJb 1;ry(ðlQhcO.]X P@Q.gejf'lC^rA E"d*E &\ȩE'=(pNՠP |,#`Drhjt]#Iȱ#q < 8F0;:dҍz?z|׾SOk/߽]- |Q;5'g?(׏8R+nO_Pz"$hIH %VrN `lrTϩB%" C"m;ʕKi L;W9tp`OW) &r˄<0sQ3XlnnkcNa~l4jێ?#Cݝ;1g]0M3|'w^36驑uw7j6]/,PЉzՍ[y笇"LJ_۪T5Zo}~cӏKё.JØ 4G}`>S۷__3>6?91:##L번 C;H@bsPw__oZ+Uꈠ$@.%t4aKk^ETb(@ve:Y"ψ !D'%TdӬ\՚p!xL$\` @hVkr)#'83ٓ5}o?xgc'OrT*mP]p̦njs Izt]g]1uw?Ϥ-@N9'b WIH$]ɓgD!X[]+7ANJ0'AA9>@%Eu(" AZn0>2ē=wnVtډcF( HHŚ903KW߽{{vOڎ˟O7>3'sAGGlRy z&|D_gAڌ8`P N$&'zVP5r#`RǑPPPĂ$c-mm޾c@OQxbMJ)@Q4EzqJәT\BZ:m[DjBjj+ vMJ&55Mu/,C_{ƍwo]=g{{z,KY7V+M}1(ޞu9Vbov.mq5R}cG4}b|g QAr=]ccC?[~_;qdFO>]X훜̘αC=tcCCt:J(ZfĘnMNe;ҦiMIل30өNu~ȌU 9х`BʙA~(f, p?#w8v/>GsTZi8H"q RΎ裩ξj\*h>r,f'jCDٻw8u Q2I'R2 B@bDlԛS_WTmW)1iͻsrZww孵 "f"rt+\4" Z#}=GퟚBYW?&>+ Jߺ{4tB$TiNI}L6"IV,ˉOa^D)(1y-&\ wNKiv*Օ H- A18fc{sSZ&1Xn|ɾ=?>rةSn4QHZK==)B \QI!(! +ղ;2kշ4CKt\dK݂ DPL-"P"P* A mr=\WF' ! DEqΥ .\v3r=,>裻&0hssĘ`q8hE/XA !L{}kCF#Rvóo_yJh&O~KV&66:l4NJ}+vy^4>5ձ2s\lEt\p@c@@Upұ+2)R\"F8JBJ=k43O9ړυQE ILV !g*#\!p~sMs&8(f А4|Z*y^;^~mRWxkcBmu?}ǎc5oGzZYysn3mjղ߿ <.߸SGGFxQA%D~+pr"-oM8};~zŋ|ܱ>Vt߭V܀YiYԪ5iNX& B n˵sXF\.p[e6juʹl˨VfI~G DJMuO DI$Hp@4:/܅{GNe: X,(dq$2\p2a' }ٮ!l St:!H^krL۲֕;7މ|_LE("pU BJT aPY_;~5?0hYr>#hPøvGۛ w #;uͅMAp!0%+F3x!>yؑѩI 8nd`|v!(#dW \ea/_z%?ihjh\$ʥ\B\P"P$\rTOĘBj 9.%&JB 1wKtfq캾﹁E̱fϿz7lˉPF<(\ϫTK]twoQΙ`ʚlXV˂_x>o\Z$DN ^D2 \( H6JgΏCBm$ykXzL љDpWoes~bfPj^sӶ3ѪLBe&7J4]#?~slF|Wg=]]lz\ܥIdhta[7o^5 pΉ" Z3@ArDJB6\}7F@H, D1W+0D^QO9#GٟK9,8*sPp 2ι|_ZiBJq. Xvc3F4- CdG{nݞ|9fL bqČ4qKe57֬L0z֝rE75GQV'cC$r4 lh "G-O),ٮ:ŬFȭO?caWoo,m'ح78:Qpwna|'ca$8qa,Z vbpwgr8;xG|=~?~78}q\wFB=S[i{ny7vy? 7w\S ˯4Zj:)Dz -AOwZR4Hʩ%I):EaR,oܸ:U^QBQTO[-NĶu+" )pdB$9SH#5_ЏYu=}3=~~Y,7jGHI F*MX lBp6}Sv6 TM%[6AJ e&̴FJ}yRJDQy+,+ӣfBqJ$ɹmtkz+k]޽{7-.bg'ѵFtM'H-|ﭷ>y+Wol tSm CsvWwnXMG0pS }d̙He臘NH['O( $-8_\tKO0!VDurۡ9?ÏܳOJbW6uq:;;,;[敹;#B;,4M3H8n46ՆtӬ뭖ۦ% `B"ilPBn׽kW.vq΁893LR+?%BTO%F?אȪ9'AZG'Nc0 &}`Sn ۿw}mRj$-%6BsEH;}$ JtZK9̽f ''D@ ੴ!%΁#mmj!h5NmP~kGYI`VdݹL\:ب JP1M's+2 Љ!Hxpd8' \@Wis.[%A"B|D.^]^tӏ7T:CTvnޜ;K5Iԭvwv Q.&ZzC 0>"{ 4 xqd\ڻ|h8 ۬ݻdR]ŢcZ12e{wM"QXw=CߏcN^<ѧOX&Ύe=r˅]VR=q A!7Z-;lV>\ʤr΂kW:фm D0',Ɵ/F9]t,K0a mw;,6ҩϞ;ّo}#S1&L$qd]nܘynR5B($ܓ=@ hc96_yɉW+J,XI;!V,7hk΄j2JUeA(f N8whx0S~&A$1BD$Y+KB׉Z_6ʵGyṕÚy㛳 "%ra'$-JE{D~yLP˒viUږ"QP} U+|\7؃ CTf7"d LČF,׫f345cj:%Z:k t.\]== qGGFpnXv$m@) U_V~{+kkM5T&K \N9$JdȪ\cHP*8&*N9sN@I6y1l4Xx+/uwMfaJ¼@hΜ>}ܫq%N35Cvl3+~W?w辽zMQͿOkt|gxॿw?C3疥}-WGzUau]n[8,JdFp z$ &Bo 5I]*VQ@.AMbQO=rhbxp[.L纩ୖQ7W!cw(AMטs"f rnugiyȦ,$|ˈ7D@8ѧn{NHY&Zxe~s۠P+5gqŋVL};M+u3&Щh5eF%hglscKەf*K~=_η _yZs/vgx㧿h}{_;S__-mmi|gg'Aa`M )R]c6Z몡&額M #&6=c#40PHɅۤ6@UsAIL. 67-Khdn~Xu૿}s_"=g^aq[z6<"7sHJ(!EΤ\1]r2"SQ$ \6@H%{>hk"g,V%9#+8SRMMvL6׬ԚF33ogmtR!Ml;/~߽qg1RɼI 2n}p )4# BriIjV"(A&S4CAVAk;[/^Wܓ/GլW* #ڥk~}Ӻjsss1Ѧ. 7|go<ЍbsieM"CMӬlo,- Oiꥭ^o}42ԣ23K_~. 32{hxEM{շSC_s͝L\9wzUhDq$@ hR rBѦIG.Ҙ̨I0J9|xU>\Qnٰ,s=!rB(rFQyscm}ghښ_udPq=eK4]J1XLYՋ7ַkFzA^XO^z.84,;Xhl}ed w03YDc1 )՛nNb.U(ꔁf1I4HE@qފ˦tYX[ "W^M !coĈ,M ea+fC/(!!Hs b`BfIwD!0!ȡSO=rȱÙB1 "9 If򣗨&9H:;oHC&n,H̭HEgi}G? .: xWFfʦͲ4M¥ _\[rαOy@[hCUTF${D+-FBgVR9޶qH(M˱3l.]vtsy'-CMDbQj9!CUKj]Z]8woqݛ-ܸ{ݹu?.^ضKPys+ЇI@T" +H Q11a$[%_D6H2gJq 2h(~_٩5{[y1f-mfS^o43Yxq)Nݻ7);YX;<8Nriyoob?_ȧʏWWWRg;r]ӻMdoƵ3ΎB>HWOvE3Sv冔jWZoɝ#KT Hb@APzZ}2Q)ʅ4N91+KwknPd3D!Ȍ_aIEg}395߫ko6[hԡILm`qЬ#?J9vjqfg__J#^\x(kۛ7g/ܜ-CBxQ|s_wU^_ݓlα,M Ӕ*ktzӽf:r6A[h +.o~yla|gCfGwd?{MVzsO??p졂aRJ{4AD{#οپ{{f}͐( E\8ujwt84rv$<B 0y콩ޮ?7?Ft>Ƃ1a2o޼`'5:WVaK:D3Eu1' !i*(ԴJLnBf $puئn9@s@bhM\_fm'_Jz}s4:6246ƛo晇Owuv씫JP)UN^IvĐ4ynQ3E{2J ^-T/Q/AJ*@.o$]m׭m+!AQA2+Hͥ ՈQCi,+Nm3i;N޾BWn4-/6Rٮmm7vJVνňt4UڤdUSG;; -jPO$zMD*y: z,i׮\!ӹ0w{yuexbQ)Tzמ]La`9)0IQBlǢcOٻsb~s{˱0r(⢑>—&ƆF๗>;Gwn?ti:|6ZTs{VuhZD{O|o^\brzkwvM0ٻg[QӶmM遙K@-J{/ϯ,Wv{hof+^\:t fE`Oӏ9أ W8 uM%ZafQHn.ޜ}_ twd{MC-{ecZw( ƈW~lymX۶fLˊ"Fe]Zى 9e9J.$OɜM (OZa^']8w"Ts:y9<*\0U)EI{*DrE5#ID-ѵDC7L'l5-F |&FVWLj6]ЅsJ(0w sijiRpk_y鵀hBpkm[nުj CbY??oj|O'326}Qm" VV)Wܦ mplP0ϝ?w#Yqz#g̈zTwvxכax!cשּׁԛN6Z.#9 pREax}m=phyo_zC{7Zx7ؽ{}/~Bv~MҩT\HXhos*|ctr#렕՚]ׄr!)5t +)7boWCM\TSq.FQmnn^A͔Oie; k>Ytsa}?K' &{AhUl_[_Y\ۼ 2L(`PyN >أToq!@ . /|+SGL=q29|ϕ.U%$y%w"W5i"x-)PmۚN0ÃN*s58PrkLO__?xlhç1k$' <IqC M&o~2LꈫpL-!&䅠b&6?"Py GB4J( !D#T'#jNТ0SKtJ:dL*،l@"o3AWJd!T8mk#QR34}-G􅁌S]_1ugmӰLbߑ{]zݕ?u@I>I|ٶMXc3.8h/2~J}G4j^q2I1CB` $]S}Sr !n4v9łEᡡt.lJ۳{sO?zCVPkvƚ__X][[m! pE3 l(0z,THi2{.Q P_ BBp(ܰñS2l`Ta+$iL4 46(ʹ142g#CaDFE:T][ Ffi>A:ӑuDx55|Q˶VWkO8NGxEv޿ ?J+5=ij:0.m}BujڱGNEt¹7~?%5XO/᷏;8);hP,'Dah)-ZmFBPж!)Y0&i͗:぀佂@#R)'\LQ92l$Ν[uwGFO<0ZB`g3&tݴummmF, 민cO?v:s;\6= QG3Z;6;+ow"/aAzaw"FL{׾=]̹_|s>4j~ś3}^N[K+./729I9͍ͭ {tlHr2*>6Q@FԴFL<mm_ygܹ&Dršc'xB֡<=G; i?*dSŎÇtg³_|F)L08<J?Q7VܿWw !|/" HX D4 f+"!J;BVffrRIaemZq^MojK7rS#EAGA{ž>pE\7*bP995 ;R)?NL%<9 9y1:{rf\gWxs ڑ?P/8ɒau в-49SomWwJ>zg`23DzպwO>=o_:8-v߻}ï{F.UkofRTpr<%%Zg@@Q䂣LrO~o(&RM&d:p ɲJ ,Gw0TX)1@8 y2tJH WqNmX\<Bw Qb$ eҺۊ~++iQi5)̵m*M0Tk ߜ Hv&K(ƥJ#[EAn5ҏ9262+6V_=x̡}" . [vctjZ.{қou (#\'DC3L{o/>mjFjϴ|s_|=y|ߞK?E?sbcno@'8e!"EaBJ%Y6,cYN~\ک7҅_t'$:ˡyjlgz4jt71bTCLY}i~is͉>I {ziK_w@gG| -Z7'|Npfɽ"Y !Rblt`<2{믎n]=-?{%AiچM;?+Fi+hn,o,aLx繾d`ﳏ>)Nvi|tp2Q1 2Q8BY+UyIoqw3'v&8$VVض+%R:y ")ّ(@O>|*\D1 djx=\f/űmZtVvS۹x"чNT֥SSz{NE"b<奩AN mrkg-RD1HU \HJYO0I$(Q $4‹d(1"(Ƭ2su}pAUPV"A0ĄM8p3D }Cr Ur,In:Zh# 3rIUHQPJi)V])0l&tb1 f{ziᣃ}K}H`b!׬z{ξQ,f1㜷Z-9y|xpnj Sb*'&ƙ@>qxpx(|uw^~W~wZ3ͿrcÂq;ePo674 3MQ!z'(絚'1K ‰2( -(9E*)x (@pa-'MDp`iq|]=}]EZ #딠 G 41Z Νs:;ӎ/ ޾q[YB3݆__޿=]Rig YQL&cZC{տ_t_?(Vwv-dյ1w];B@{`p` vv>3:64'~t~1VBoi,b@Xfũ+v:Bs4r'?\^q?=Ko]YY,dzgTGAV4=~Y7m۩WsWf/}I_9qpjsiu {!Vm:&AEKѸml6l}]X|ݏF󝝩LO.hj:%%Yb9x{Zgjh6ZQsၧΜz䱮7>'?~ڹOg={NҌ'fU'Y Y"D, PO#6:OLNLRi P 5NH9(TҔB&BTeT8hiv]cNmNV;rr`_e9,Y9'Jf]tuhk|zų홚xŚ֫gq\9(UM49O 53^ Hgoې"r^-/ R-'$aLD!Tc@Jx~j+q@R> BBQ\iUY"S .@dT۶ڥO'އDNϘl5$@(~ A51l Q7ʫU*Lb@.?,Q;Rq jkWܠc˟?}KugԐ߿7dWnF18)<4A^,eSq'BX2ήNj}}G|̉3ge[?*׺O=#c=̇o j l#Hҥ@PnH p5"DU6넨a-"R` 4Эۺx]/YL-0u~ik|!gO/|.Vٕ]b#inּ J}5m{y"u D7FM纺{ Vfr㻦'&gfGzTY<~ȁqmqGAJ:'I1{٩/|+үuM^[\EH-1Z!;EΤ&їq"f:U(vZTG>?8kbGǾ'N'_=512|Gv ˶oO>^d3`[;iC r`?g]({_ jöЩrsaDQ3VJkwvoή?WeYrib͎Z֑ UfupVO/b"< 9Uu Y2ZZ{Ws0!^~f[凔}#C `  Tz^[Z\XzZ 7 (4^sӣ=ͭ'|^k;; ?0Sz;۳j0 $*FHuQ=-Vs(?ށC.݆>PTsR BXT* G:M'AP&Q^\XY_,}T+䁃7ZfP!dFWwOD*2oj`pȉJʝ6VW6z(O|׽F/lDDFS(̘BhL0؝B((>PWD%yjVP-:Sg˞U麗DD(#-.uiF>n=@mѩGwHPIMF>P:F"_"WIX=Wvg(C`Z()0 fpquuRww؆8? 6g7GU ;A)X3CJ嫪G^Yhj965.c oxLJQ[ڏ>@\Z]Ynܺ́w ?}}m^o޾Dݔ2D\a/_Af6.mX*߼i9V~63 )yt9N|gcctȉ]=}([sc;#7.]dC}=ݛrjPtNw|@:= z@2`_Wׄd#[(('5v:J3úڦPQ^x$1J'jƢu$Dj$:$ I*TYP5D-$3CYd?S .Bz•onno?O?9 9sქD2k;x2iچz͝z3H LE~k&)m=[力|k}%K*z(-— $EѠ$l0YT}0flsz{}p3-Tbc=]=ݽ)ݛrٺ_5(D@Dߩ5Oo}Y/WUjHӔkXu߼ntXPFfeɔSʕÀ13sSiJHzo;x 7]jn^#g[+/X'ց1bj"Q=D@*DY(:`ԠT;o^{kZ&!sbs^孪autT򽙟ՅOnXZON{HgHwf=l%U؎kxAR|Go־X*\rNdD҉qێŸR%`C$j5_+dcR\XX_]s> loN>8 <{Ƕڱ3(/-̯: VD-}Qa(n)7()j`X_IUDUJQ˶eW׷0eXGNMZ߻=?t 2tQ}7NL>r`svamsk޸>ۛu=i=Sx?lo 9u@$!c3ށѮV7hhʀw MȦ3^[ İm;fa ܄C՛" A~U {~"ԞIb\-YHT@N`u%/z\n2 J@FzRå&w=fըfVgfR94:Ie4^z}n' yoݸI5 #J[7*H1HeeZY>:2ʶWUN5V爴s$P,g&Ijox2ͭ]]AJ @X,F saqacsٳ'?~ (mm\mN)H5٤aݸ}?r2̝{nș3Ckw^ CC}zaČ/L"(zJDX"aD!1Zh(¶Q! Qr ʖĈHPTd%¨DB!B%#Hh$[` "+Dd)#T!F(@(DBU"OTo) (rƫP~ȽE DLR͡drqMDS}*ѳZ`2Yaމ;Ήc'=B/M͌0 ^.>DF^uLҴm҃ KA]zɯoz,hRRjDi2c߿{;s %۱~f~o|Hb3AVY]X_|@%H.`j+8RP USG.95DHBA:'[Rt{!*~H!uB !BZ& uP_[P|~ÝANCP ?FƇr[N=~Q$J^.VJac[q9(\KX߽~kv?Oc[9|AKuGۄith!RQ<}T)TZGDs 0H+݉*Rjic2o1ڤѢBtVw#E^*&;Q`ѱt)U"u=S+ Tςu%A"~/~j0\/VeN?A$T2{wWm}aBv<1693Xnk+ݔ^$?ZX^M5ej5m9|p~sKọs^jlkˊS "ߨh ʱ鉉D,iۖm[>x磮W:`nk3CNK)^ByQ#Ԩ֢$iqj{3!b= R *:FHB(dP\Z$ml߸yX)TEɐKC Jfhk?7W\ލyD|òLq[$^TW~Hb)2ɘeҨըELZsBX9-@BY`i$@a0M r04X"1(M67=zXyW}LW5n9jha391}an16x| ^X[۹Iu |%ƘeRio~cec`1$ AAf@܉/os[Mͷ.ɮ=N^cIݛjS쳜"pamǫ ָ)(`OpOO wѸ4R)00q#N\e4e[,y{pmlA@#D%bԨNO"JaT+Dc;7z:zzC!EXգTvR;O>sưb(Mƈ[[[ PBl5L:6<]\.]ܡ}r E fn1^?9Ւ)ln.mtv ~͵оf6n^8+a޸+ɮ6JdC?eH΃ C)1$鶶L:Clg[1!<NA)҆C)kjRA Ήysى#'7œjuY#B٦rRH0 YY]=WZ Wflokm&''FF&RiX:S.?TkniOZNg#nޯ t} $ eu-z{ K+_}O<D"BuBxWeq#[;ya9(0f BVVW[2{oT  (ZfEPkTEu ,u*R`f2r+=-M}BLo"d,;ZzGUNKwniN%[ZC-p:C`@7j}cL&B~핕ζc>qFoڦ@lxx|p#O>>o}GܻuwLJfD6"|puDT%Mjջ€0` Iitɀ!(Pn]7w]_&\ *QS?Eg:HUĨh82nu e2J@KR:oI$zۤoem..Ԡ]0 Hp@[ٴg A*ZQ륗ion?(5J+1+0V3L$SÙ{ss dͲKDByf[[Nlm75JJe\w詓GϽf\G'/|3Rv,)r711l硔[7?{٪W5-)U޾ܔtR 2 BR/Kctrl4S+ꍆkPW (>N͇S`HL&S +WZu<s q9L`f9PT(iJN-+I1NQ"R$Zq~i֭Ս@p$(y(%%(2hW3GJREPYP2bg/2"IqQ^PYA"*.C|Բj< ӆ$R e(%L>vcBaޝW/߿?+Wd2Kc (Q !%a L6c3#hL)ѫKu=po:=~n<\{Ͼ؞wz퍏fcL0¡PӲ*'pT79y;z;҃k;dgo_WW6L")y4ڲR5tv4wUb#ug?0(v۟}>퇢+<)e{};h洓+RrܕѓG{yfWo4J^(jvacrQuw {G2]쟞 DZB,,.R,R%S^o;=94TKRbttl N#aۅk-ݿD)_l4`dvlJD/KFVSeZ+U~HbCD֍ױR3,G\G>ĕ ,ktjʢI FH[{+ 0(4oߺ=w] 1 Fsy}~˓M=pƭa?~Rwo]JGI7504PR̤,"s\0 po^~Ww)ReD9;ⳮ+j'G(EmJ;I)51t]DHWLM4I530hN%^SJv OTʕ" p*RTOuEׂ(F>5T2u߶j(J~ ;l)͡UIP<leADrg%U8hd 7J Z.^!(N,K*f)Br)ljiK&iN?p̉LԶe@ LsH%c1P 2I-l07Zkx"J "n fB':cwmizLQzi(A٘JU[D%Pφ%KIѷ, 4$R*BJ)49LMkMg`mn,>{wyy\.L%1JmlӌY[m'}m!?~ǟ=q􉡮K|NZ֗XqeX{nLTy&v'::Δ3=j޾ѱ^#iٶeLamjZ_W|Ld&a _*VٟPB)rdZJizdO٩&asNuُ>e8=op0ݜiδAPՊ^,rjq3OP m{n^ŔcvyIg>\rL?c{19 HF-m`ܱ3M|ooX{&L8i[R"P3 |B#HսFTQA+-1PDФB(0Ib1n߮nݳJ$BE` 0V_?4ɦc9 0LCJArhiJ?̙0RLR@W{?}:֚;:=rlvx@Omue3LS). ˴(" A@P bk->X捀(lV]f[0Ps&Ń'N35!\[ۑ)ntE6Urna )ՠF^ ;U eJΦ t a!2dQ" wE°JZ=G+x3hmE5F- Tv)@ToPFCلB$JR`d%(0JG aa'啥y;ĄbA;ڲ͵J7ntwqϳ,*Iz tKUv!=O;s 3KBE"aDjov){ #%bNSBMAjɑ$B}H[ˆJtCŰRP<@ P(C`4T"=# F5 י*cRJTF 0XS w6g ɤ JA )xHa;Տ?qoOk~%38111,L_?z??0۹UZ>x0156o_;qDtx&{jFA]l׾rƃ60 )Q:ɸew?*-$^.mOzz[)?iQDZ~pխR̋ۧ=j؍z2öB.gWaK˟}ΞPJ-6J;%2D6A1F\ք^wa7KQbN"^VK2cxX2peu7H$mB@ʱ4liyet}TtǎB`tMӊ91h2R IHBJDB:E#ASFqxFDZt샩tsUS"1;H:z?\9:o/! J! 6e/@ Q;~7?o\?{c=_4%?>vT_DH 5({~ΫozKK.=X j.5QJAUN"GR*2R¹5c'ʭomܞ3sͭ\ )|;( I3a k+E~7` #v9J+% ^NaD(g+ΑV%xE]HQ} O$ riH1,( - [A%Kծz(_&x|h%4:8dZ~pv"JP:>5{I_*0LƘ!X[HS@$JQaEݯK D CDm?TBT3@{H=*t"TA~՞Xb*OPBJ H$ 8ZChA$5 HUF2})jsb_`R4 Fh.Wu`兵j=H3T1"H.`7߷gz߾gvJcg֯#?o*wt?̳Omߟ.OnwWu;1w¢!DQZښFٳ4b$̶J߾k;4 / /<7~X5Ӣ4€K \ph_[ΖI D"Wj8=v <^ȕ$8WX^[)wWWRRTC 2'+D Z$ҽWnh}kwzY\]G*j._@1FTCT֙7DS-CQkG,͌UgHu8+4#O…0W8jEʽ *N5t.ǮjGrhqNTUKB) 䋲xU4PH~@щ*nUiJ3Gj0;7MkmuyvvvvtbC)$"AH)L?55x"H >?YH8d& Ws[ŅպιRHRw W (FB´*yBNҷW*%FSIhIdC`|x”DD idkwҬ=<@Y`)+a' b:s {)/e˫ 3iKKkn͙d"n=:­w{ Of#còX[w}._{G7_tWϧ=Q* L.\rƃ ArA(S9, UCC{kbTLNͷoM-_P~\LMW}|9ZxdS<`>rwޙky{9j\pgnftjL̃Ns48~ЁmϬonשּׁl,ͬTJm2.}Ҵozܶn-ܿ3[kZֿZ#gz~ @P$ZrR[ӣG#Q F"B)& ,e*x.rPF_\l)U_DQO(CZ4% RPwc2(%J?Nf:oU ~J'kL_ٱ%T,moi*[wn|0pL%4X㏦gXqikRT77|d@Kk-j|_mÿ{d;x;lki}pVEޘIϗ?EjE~s&S͟sݣ#XClU<֨ՋHqlwF1r ; W} :l i >Q/M۵E T*N"nZ{%Bggg2pdi^o n3=d6 |Ǝ/9a9׏%ho)5 Dj-GD 5d&S;FP.Y`90ʵ@HJwP=U2)$[*!Q+1H)Ġ0fZ@6ZתԲl޻ruauNfI۶`suŕ7cv<Θ@ މ#'{W~W_=}C}.7*S_z.}{~}vr{XyB#xu˱4/~)-m=]L6tbrrk|"٥Յ խh0`B"tl@:ts=WAeé1;44"aGyG}ĂL8nE0cmYQs 'T{oX6L\0e?{Lk{3F.wֶa24~lW+zYWD$!4 оd\Bui-1ޕ}K_|Fic1M4P&81gumksea5lF)5?䳛w5?WWn]Gڇk٦o~bi#ζt1\r3Y)Ogҝ] s+;kmYӶ߯F$}A!wRøngRkv!$pi=g@ u B?;4?R=RHɅJ\R*2.ѹ} =P]k5* P6BNFA RHtvv>“OP.U]7*PJT QT2aPf]%0 RʐI)PQ _w]"B͸VJ޼{jnݿoBq޽Ņ% N&St+W{}z+Wo;GڳMLC+onl4([yjjkNi#<,n~S{[SKġC]k{lmݹymzjčw>\΋Fwxȣ's}\v楋 hIgFdž' C,NΦԲ 2뵤x٫箌t6&jTs2S4FbD N?+9&0D1Jb3 +Vg630rniH@km.N?XՎ6wVGO?HE(0,b~y'p]LCYE @*7 SeJ$RB("Ɵ !L=dW.]lvGuum ) eCzCA%[o^LMt{~l~_ /#~k koT@WOr9JL%̸iooKf76Mm{jARެT֖g6o(sЖ]:Pobؿo/6j:A ia r"/~s᧓>0w u/PYHytG秒] jP@@ j#gk˽WZ S $IUF*bKGZ\% э1(DBR_CRѩ'O锩išeDň|4RHuoGfcK/JPJcD,ZẁlmnHGhkx ڨ3 b(RD DN$@Ta uhdw 1zTղƏLd5 ]u|AJʹag:}y aYvOwgoG Tg2bBaB1rh+}S.(*QJh0e@0,daRJPQ (Ph(eBRn+ےJgE.3Bcq97㱦L_ẃG7xΣǿ?w}Ϝ={{x٧;0)PˎywjF,f%ͩ J(%)X/V llg xstc[aI:{=]ϝ53KϏf ē'ΝBDɽ?WԀ׆{9d=?@ ,6ܝjֶ fe ČcM XӏVWOʋ/t\s]4=g@ɤ\2ezC} 5d¯N,K9̲QHQ` PU;"tPzaWoVMNxT犥dl=鸮W oK7"`r%:4<1 iH!9v!8jD)hZl<ةCP`X.L H9`nt7z[e_7颼uVamoX=|xaa(W+c=[ȗB>o|s}!KI_wS_}()fWۅkP)R"JTQ[e  J 2$K%GCʺc8vKt]%$(&Kej_S ۖs^, 7F5K;c&HQ`$SR}9TT')A`Ğ1(!D))"G#*%z%D+"^v#Q;sLF$(]vjh5~vc+4]1ߨK=D )RsX()슝iR:Q]!Q5Dϔ_C1ڜBB*#ST LN R^ xz&ZPWHƥ&RJM0͈DB8F]6( : 2B s.9"8 !9 9\B0B(, !)e>'89V)./lmS{ߜL sBixb|K3#Myi=u)JfIR}$b2gٶ܌ι1J)WPC@ \}WL?pg[RV2꧿CJ>oK owOϾ #NJ ֖ ŕFD& u!Bi󏟞>|s7^_z'x |JQ#RaX t6^'j`3x*q!PJHϗ7,}i1f;yA&̤V,/5<53w(p+jW.Yj5DM qM=hg j峲+Fd3LZcǑ}uR)?vlpG_//?p@"pC 3I&ޞD*7Ի](AՓ:xͭB9/ lks)_aaN\\Ky/e}}ܭ3-@o5^h3Q#(! `?ux&6t76L6 o,-uvM~-s##՚5>/wdL_럐jhS p(S6e&S=!2TנEu rhYHC=ESkW,!Rr*A(hiKͦ'*Q4޲E_H#Z=_oMh5VShPѲ WsM@q۶sۛgf4M2ÐV4;3Vkԑ)RsLP%F!] iI'FI0%TZXu0BZ |;_?}xʉ }+c@2H@EY -J Ώrvca?c鶖Rpl+|և(oN흌' +çx2V Ϙo’ca۶ 5eӆeuoh|W>;s &̄+?XY-jRA>Q Q<󹈛C!^|ӓv,^mn,bT&麍[ܺ}k=Kcc ;BD^K[vwg?x=-Y_K׮=V8(P];0$ ?A!A 90T% TF*Ct`Q H1RgDDu\20+""LH(/0zHD "RzRJ(PE( !YC}Bje!H%hхH   1".p˥ ?đ[[ō  W'un/HR2E#J{jI"/RB7#Z"zQPA)WhoOOֽؖj^vRX_?h_7p!1\[|vN0(DeJg#|AUBB ()WDHAk% P Cr"s.Pr $R򐇒2䡐B*#q#Gm6r"8f&3=c[oٯq7fO#m AeBp*)d$0 |7A)i-H$[f32eT D̊l˅ͭM3|'?}`em5B$7yH0A^.Vkz躆A){*. &b&mdM;3=xA@P0`!xlpxPRt"cX1>Q 1syꍖL^oKpyP\ڬ*mmM٬85.z]=<1۶m(s^Z.cmQ[XT(P. (\r@ϫF6ooяo}ӭWַv SpqՕ>VOϝڈW8gjDRw^pU%@%S@I q0@ W7 D*$ BR"htTEH@3aP%c^n+@@D-AV%}S_GZ\|_=}=c{;r[]͍ g;Zә@_wb|^jP*Huh> jJ %%v *2eUZF f0ƀJ)%&cD (UqcSǏ )x"&F"%0q'8R.@A PJ˱.A,S0zrg80A0'hwVCv=/L=6zdDIR JXdT_{ʳf([JUN{kɷcf!Y'BKEք(K]Q  *1f; /Xr P[{MN˄%ѥVU @͸.Ւ ֯-h(缸SUܽ>h+ Lhu ۟y{wrl'x'y+nioiknT 9j2( iG&gżR#C) c#Q"&@ 1R a=ΣP ڦiٶi0àf777٩TP(;F=$ 6#L3$@LX+ !1? MmcqKlOs+Nt?l{[▼zVϕRX ,JBa5TlJ6gC>y/?i)J+d#Wi4žζloǗo]|oD~zO{!ĉ9`< AjP)(,Ӱ-󪥲ѱx&K ;VGFǦ>LS&# M +}|\vK2fBȵCЯ؞tu(u^(;1S@Z3%m{/"-ۖDR"bBid<J,Kw+\~~lX) S˾w^6Y7zbVjbjˆٹ|uKĞ=L?tyumaqema{s Q0IHH@?1ʄJ H( 7mXS#O#푉b)HN_߈AP=]3@"ZaÈ9U:\MS."u(9!F/Gܷpyc{gjqmʈe)|6*R/MgJt%$ \}eV W  "$T(HFb4#eJH RioHoADJu&"IHB*Vitvy}{baZ N2RʒeWfWΜ?<5ޞm4<_|^*\zpÍJ "ʐB ݦS$ZLTr(""Q, BQ !2CD)P" .B0R_$a(AP C?v}{=vrw{kMj[/6Q`N̡D!GI%A;a[B]/z?|~YB\(+!M Sσ$9Qip0x*Ëϝu ՝R~qcsmy-Wj!XRAXu6zW7J7/ܭ̡&^ӗX݈ŝ_Տ^)\qA/ @_KtDι $AAbIیۋ뛟}zaBXw2fF@x069^pSNC.PHD)P0~=Wk5VݬVg-Ѐ; e(P*#xX-T7Sg=KA,?F͏Z[SRx|dlV._4Rn4D(Zӣ%Wkܺr5cOLȏ^gbx`~p?2-/tuttu_gmDZO^}݇DwBeHUSD#1UhD&jpDI0ʭ$81Kj0F `"12*2J %ġ LR˰LàW5* nYP+ons#گHL~;UzBk@_ 8@v!+ADpgs[9|Wv{L8Q&h\QVw (8BmB2`Zej8Ɂ,;ږ~ӏk )GGǦ&_TJ6 rmˆE 5"Pƒ;!FxnCC5"* R(BuZFRe*.f+)\ NKHG$TȷN}f٪)Q2R(eBZ=&A eJ&)") Ϊ 1X-KWO>IŲ;ȱNa؃}-TV+j*vn}Wifgj(c! S&P)t"jQN5`hPX`*D5Q]D%ZuHV9>״ Y^zg6al˲*C52xћ~7?nmv,Z6zCMT"iI'5Tq )gνJPRB! VV1P U$3g (jHԍè2)TeJQ4 iXeنit Wr;f' r0lKΖą7޽zw~`zo~ѹPGWc;?7+μvo B]li2FQp a)Ų^ "$?Ff3J DDp7ޮt:^o3|HTU3T lvtu6|HҨd5kږrJ!]ĭFʇR|ɹpcD[etssQtuQ$%a@1%T. UY\Z+ub" )ZJ _4S'+.5n8uHmRìVzv5[xXjZ(mX+ )ExdELIR-.~+Ϟxs/TlОJijI5e3nlLblTKk( 1▴d-nl>X^r"PJ^rD͂" btd A=hkݿБlo}s;i|e!0T0H L:Ohzz H CC-4S yhP!N *8Aejh1D g!Q"BwtJweΚ- TIGIBDA (8i Z])QZL%,kn abv\t͇ˮ߰Vo5 Py -f(PTXJkJi)U!Bi) %Qe&g@V7^wwߓ/<6=9. VTȤb3JW*o@[]R.HT8|MMWmrls?J)s׮޸;\=f0-{AJ"$eTٽ=U_HPʨRmf(.K `$DJ)% . 8rW!Cr. )0\ !Qp.DD͐\ rxgO$$Itvf9<1QփٙDg[g:4ֻwz7Sm=go7mzkit+ϱR9& BH@t"A^Tkqla0m* jWH);4r DBވ`OwGV'W\/y:c:M%cV*y潋]wzɶζtsS& . B"1-ja)_Yοk_WL@% f٥BcA|R -D`(šFXY\ٜ9c"Z6H=+7 bq%3g4TP//NwwێyAT 8DbeX߽u'naN{{j 1ݭpJ8?j*Arl#v*-KF֍6Bdm%'ݻo_2\C?_nKۛ7n=P/SBae!D@,@Cy)a=xPB-=66=m@P$IbT A(Ġ`q/ À ZE(:w;Euz(u[ n;HloxZ-I;u sJclr}l!Uj^( ʈeX@ t<:aBU#e Ek{tW*"9O$HʎWs/mHDh>n(*!@ 4JB;qꡃnpÔ x~!1!A@gSZ,JDf0 㨨fţQ@8 GP`&"A~qcL{բ[9T\؊֡֝ Td(嫶a?ƗZtmn~3ݝݽݝ]]-ͅNs ioi D 0n^K=zb3/^/{z; [4[Z{ڛʞ/a2#AZmBuT҇]ɓ&ޝNR4>1{)c!2"PQ@RcP2~~ ,޽XVʹkf,F8Fwf =m~z}kW.,X'?pc_\1ͮE8-I0|E$aYx4!5eZR6 u@ٷgry{l}[_''=#V #R'Bpu踞gH<|p,mmYӶa . 3PyPܾ?/_Hُ=sfx|2nRx21ą5G KyH2a5lJ^_^5_XߢmېR1i$!DkG+;{3HmdF1yD^mjFv2f2bzk>lEʕmZ٦bQW5D5@T5mXV 7oKvONlĵ ̓N5͹je}gb'2zAeU P(] |+Fyy#'NI%3K_lMLuuz~P)]wk3# _%BPd:M)a*vzBPJ͍0sb B- àZ ?¥s+rWgρ3) H(4(C")%:: "&\ }E4+ZE5ڝ GZpT3 %% @ݮ,f@ <"J#, cT+n86>o̙SgV ۊ;2(yU|/pk %`H;Ba0 D@ލj ¬F 5_}VJ%!a@B`抑!Z.h#HR Dqby{ƃG(K$a "&&O?k$#}#{ uu<{~^lg{&}NBǎ?[wf:kۄYLztKgN Pv R-U^("u,4lqJ4eSz BȟPErWN@0QjP0 CPLa*Ae1jP֣1f̴l4yHPJ/ܛ0mjikN؎[`~t3W`~ʤm3>2|a-}{uI3NO^:3YɗْH) `i0Jw6Mvkrpϗ>}'?7yGpa0< B5lXV o~po8/n2ƘɅ\P d2p "g?}ޑ=ccV+RM\PLL4g$ -yY5ݚ-nX(l HU<:}f&C_}cCtӷ?[Բ-ӰRɤi@L'g߽v}T6KYXC6RD(8Cf4"/hrSQme}S'}O/.޸y{&&v< Bklתjn{zZ#3JTLhb4yJuB(<2qcGȉ#՚K;nfR]== $b2|FZ̶fzD8S)ixZyFv(X"HgW{±vB6! DJ,3U5ʘmY J'dڲo뗮t훚jkoVrԀ $8VbWOo 9/p4zt.(J$pw S0Dx%vPA 8[ezj;BJ%W׾ ,94AURf9w<"9 (]LwH?۲ّޝ.BUH$sN"3#3pΩqrDB$]|dzDqpkگVbeu|pH1c`_KʮK@a\2k %qpx߉VGeR\{U-ɃaX\ Fo2TR{W=~AhR>q˫\Kk{K7ɵ92~ŭǧ4&0BHuKWzjX&'S9vl|hH$bk]m- JڙE,H62&gO"A1\<&V4Ey J(% B(ݓaQSB1@0ƘS˦6]#q܃BP.k\Y^X\\5u+Mw~Zץ~S?FOOxJh7՛G{z:ZCwl=f?_Ϯ]831Vr*tCH(9:>6WF=eg#]tuTI$ex">|t TraızL:pN0-+x|ݭ٣cj.KNWJgJO)s빥f ށ[]Xٜ\)@r.͙`L1BKXjW0%N!oѩt67kw &Rv1@ PJReYR=Bjwj&dM^\zX2A#N=6\k6=[ڶS"E5Q萡S/Qm8B0&*_qƇ_חWeٖhht?H1\(q-J;撉w 9mߡC!8aBjjtpq/~0*fe!Xba6O$'vy^c珌 #|?**Vw8%.)zy8"a)0ąHw@鋯'/td#8piRS+׿yR]]}I //gz:YL%Lz:qT=" A3^y_~+s 1 JVQUJrd#1T`5Jg&J0ńS 4 FN, CjfY&,V~NӉl&tfڃS)_pxYg}cphJCfə8{-Dw䙩xʼn%džtU!i!")]@oΏ~Ϯzǡ_zyp̌mᇀA|moowu5#d褻=ѹ}@QeWj^"\X߸$%زS|BgP酾C(fo+?D zbt[, Y !|/,5+EK+[W[%¹F1p ݐ;ag>Je&BP@B؆4܃+)W#}\0 SwC:B3kφ~m >R߾mɖC* g3d&s_ |O(4FOC"v"[,m/ݟ:w4bw>Z\]۩6*F}aysgeY0 ⭮wP ӎI,JX amo?-`uk콯pjhGw7 \6<-+x<>srods/GT3taB tj@,a:^"LA\kV1pdži R"l6JNxb7͆:KF6 ـ.[ |>r!PTm%Jʖ9Ԡ)d =Ͽge> f J֑Jm` FRsrBt̉8ߟL$~VF 9vm1AXVm25BJ7 AK$Ӷ^+Vj5j7U'1 9p@ $P@L*M H~Jc~k'OsFzRuF؅'.SIjCͥlwש37_}/9ڟNt* }hbS϶ t=-/MɽkuNMen-ygZmmyAaٌ+oRWRFS="[' ٞ._'uxs`@剄(PY WJ%&N'0`L0ȥ5M'`1wwWV>h~k@[2K2bL*2ٶ̽w?ӯQcn;⃯qW'.vӻ+k4?k7fڳ( =z@ 2ӽ ߸xA(x,V5jA`bSV `mP]s\עz%n4Lc泩|\`w0|}j[kNw~gvb,fRZ\ޣ_;䕞AJ B!x%ZAW[XX,b1)7!=㣣}ɉźC p0h,}oaeݑ>quW4Bpn7<6Pz|eOgًgCjܾ`kmR.tXfhZ#k' Dr AjU 0e=Z}yuwwmY5jrW+W#TabFuEX%є0`cO9~rhooWW?wsN$Ncx+>y/q\IDATMNN0Q01Buݝ GѴT.PfQ-cD"Xs  !J PHKRR=2z䱡J ܻySgTJŽ2 UGґ0( FsµȤ]ET"5H C簪Պ&Q0ʣ ђ%KAO0 ,Twdlw~LK6~8nq{k瞻rAIiuj&lTeS-IL5g}/<248==mmx:zxs^YX\~Q<`B]bUA(Dqf4x'FG-tGwGqvv0`DAdfeggjөWk1?r{kPkUǽwF_G<5sD:"v թC7jjvZxD5r;#4CfhGkB7-BbL퉔yKGg ScScr#iZL(`O%+p!`(#2AM \.{a6m^ c] CO$1G bێ:V(Ϫ:FcLШN菈QD|CrsY "z>s B%0 U".+b'.>}Q.?[l6_}.\>!} Z7q&44x",$H *A\ c,`akiuu/ULy:XԵT"!1 "T |`2+@PUrZL0d SB0L& B{L1&rd"bT-C,RZr9՚$l$xttdt[GG^z]E=k<~izbdͯ~L2u=IX89u~<bpk;>e2qA* RX]ʛ[5 0P$y zǯ;]WkNrl&RhZuosxr;ښH4LD6]6*ܬ/o.=\+mH0B.Uv~Vk[M=飆7mk~}YFCp\40RozzY/ө&-Ng6γwud[!G}qqzP8(~@"E+b> EN, R㑟}|rQ/:,h-"ks^ʠ'7W7wkoʉKoݼs/k_oݽ+oM%Ra+Zt c燕r N[R[[{w98gaဥ%(R0D=ͺS/ө˗.dJgX;T+Dc#Bn?sU+n\Y|?y> 2c:ƵJ=GOLdI8c󂐉SǏό}_ϮnLny'SN}/;זIBFq5J L׭\"3x,іgT+ XZo9tK q8T ce7RqL4ٲ<c`yi\1VrF;AR&xќ[ݕuJgҹ|^xn<>uʃ[վT̊Zf&?w?̎6!9إoG_&uMbI#^+/?@A}:99|G,x@# _2CNDs$9k$;GI䗔J&H)Zbt"UL\&̤{/X7M T{Coީn'sՏ.5-1%, 9gF1ƞ&E1覆 C ,8AOoo@#dA9rd C8wY_zΟa{P 8U<(#L#4"yO,a^Œ۠WÈ `BO$ʇpTxAy 9{'a(T˅j=?љIc  뵆e%1!f[aںuZ8َe%Y,@X2LZs|a6 aʫΙqRav2UPzVF[ Vj-SJIJ -کt&眛?KL;}T2O8dR,(ç滯ȉD ]Ӝ x4;h>{K<lk~c48y^ #$*&c߯k <ܓǎO~sdXGGcǺ{:Fs}6 HD1,S~WTo_'pnB @ٜKluBq%rW(e@$[B^sB0F@ 1&hB!!tN` 1" +U*"RQj:ol-/-4Ex<x*Lf'ޭ.V[G[:@LIJ-:BB'}]{OE'6ǧgB醡ӲΙa<7NʯߚcGLJ{zںIHX\.5*FNܼ5;gN;;6Zh\k;K† c &-\z6X+]9qi;4h,ܛ[Z]-182Vzβ~'M H"Q V#D IUj,w Ssxg<7wA Z;yrkW~tb0͍ՇK-ZPo"iTT 8,O%eFriskᝇ?fxT,U& 6ZBaLgcnHG\ꚀAo#?BLpeEhSʝL4 J֜\cO\>}x:,}myߏ% |jğ~ٱ ͦl!@@05u ]%rH&уw;2-nhhAJ hT-, gsّn b9!P$I\ݐjA$V6.] p$7HAdqJ'%/ ;n>?kvWow,fu;5|́c&V,or-̈́G7fRq q^PlwxuMHR6ꝫ2ƏOx[ k]RD*C`_/7Gƞ|[^}dwaD'J%F3NH++KۜsCUNwLġ H8&{D3F&lzI叐p"_uDϧP"J4J̨ |%ATbL$l7;%D %i |w0;?hQTHv%Coޙ]ic 1r}'Νi~ແt+38ӖnJ/2TXYyrw՟FQ{^VgS0Cf(LXܾՁl'TlLrisaܾu+m{ٻom?cT*kaQ(m+J ΅\"!8gHd==85r{4 ,ZҞpVVVWGFGWOf:yo Lp.MS%h8K\7'G'Ξkp}w{g5=`D80M׭6Zs[ko=h6xG NC@DVIb`fe U0 Dc/dYU4U-`@B"3 ~xc=Mq0! K'yfn!JBl&ެ WVVW|uO?>}X,5H M۟}0,˶`naݦ4- Bx?>X_c*<;(Bz{:~[xʙskv塩*zaeD^(зff*L 0$@dы7 ū. CsI?fékL䉙q+#-=zQH/>tg{Јs9 9Q:` s}/h!cX#B^{`Yw % Dөt@NW,uu#*(I70,kw]B!\FA%7 ~C(N:H"j^BB2θE6Ndu4q  Js|O_*l}me{v?p \8Gwoܯy8%^/( Ӷ4A/vn|7P4Oŝ+2N$ kiu-ݹv}jfR s1cvvr/|7ƆxVda^1wfrmK1x2[ڐ+ςtb дbm_|{o?91֒KzեJ?" a"ۨ+Ӿba"_B,_u%yG~dߚ"\T:BH ѩS3? %xwcP*v^ݎZZt 0T"ƐaRUUutL,MۊǬ[<oVT`F@-#׮;=4Jt6燌gN*A%@5":=tB@n E 0v."!, L +Mmya5z{ƦF:J&l3fܟό "EH* !GNQL8]XE݉i-)ǀCYLRo`Z3DKf{< „ fRLg7ﵳjkZuͅG""YT}S'L3֨59 a|ŗݭ#onzZYHdGf|^P BܡG%\!JA|A4;ܮݼ61BHq}uתB[w~Ȉ'bG{i aD2}m+æy0n6ptPY[]ɶ$ݺeÿk˦|\0{swݾq dM \+R=aS|`hz  =OΟ;ٞo6rM:aA'BrKB\JȈH< !]e_9Vb+b/kN9~(Ť\,׿?:soci\V74ΘC#."#_3riV*j6@$'ݍBo:&w}nhT٧ +gOffZegO3 Jia4n&jj I#8 ٠B ;$8[beGYإb XV,KHD8b1S7TYuVPhL&of?[ʵ91}Ãq]- oi|⫿z݉c:Ahh9}TmuKtwR{[F>wvX K{Eԉヽ/65TFb2$n2 NpdHRv!B;#"pb $=G@qRD~廕`@HVdcJ`QeƉPcy$ɂ; ek(Xl B5fjJ}eeu\VKZB鸩SWε&;!bB"T#3ǎJ5hC<`qlLM jV%X<JuK5 Y,M_:kq埾t8=35uD=J D},p|V۩6v=Z^ b1Xuι|  W=!&꥽Tlэr?eyydZfܶxfkmY[FPT}C8:1jX0`eL&n;MP+Z/޿ەXtsWVV [{dZ;Hs.7c wja#ӧz#:W0Fc&@ٸ1M,DRȧP')8G>H6 ՅT. y L&]!nܼk##v<(}0wގ=;z?yq!| !Q%-:vD\ӺfftEZj(3gO~ٗ[fý'_+3ٖL6x[W'2yo;ڑS/Ga/ 2Z fx2ךa(槧rFR ȵwʰ/"@IlAq@aѡ"(^mmSO{0_.X%*ȃKFۖ#CÃiPJ0sY{{K+y4vz&|[lUxSΜ:zߙ=6^٫Lz`bܕ?iJw@2N,uW6}w~u=\P,K*W)l,j*(O%.BR ˅HGĻ-XDjLR(; )A`BXP:)c$%D`R TdJ4BNA40M4S,.T*֣|kȖvxꗱ΁gN㓂s VC!C#a{tS'N4XY~ڑ;w?'_X_ͧ;dÀnXxnQۭUwv nQS! e[@\J ̉tqM=6ygS]{;tʚη#D0 M֮ݽwz{v6kAakkp?ܴ-C3Pc/+đcK엶7F$|[6L'.>vRkkn~vjښM&Sa}skvcyiHZOڱ %QVE0R,q,˥K-Fj0-Jdbi{{}m=d\6۳0mn%a} 1aBTjʉ '>x듍SNIZ*J|Ez@G(ċЩsDP`Gx@Q'B  " _.1;=` N?}詣f鋯oKum# IU !*#ǀ&"! HW@D*@)񼰰[HpٙD.qZot{ר5ZB+fes3qr #\8 ;MϰT&5 tMV>ΖLsd?W+JTL ei+0%3?Ĺ3f!# ,bB8Fǀʊ]HJ/CYRS:%=RQPX` gsp< }yy~vnPwh=nk~uX>rdhk~~u{[ӌ~N"cιo3zIͦkZ5sK׮'nei°wwZZ8Ahwg[ ?U{(" ykA [e{J]jX(E$T !.R 7OڨmF {ac@hIY[L 4D T`i )cu] 0KֺMbl-.Mjڹs\{[dff#S\3vރL؜>sAYo ,<:9%_p"V)R ;Jx\yU..ll./lN)AH%r:#pݠYg5܉g||db(k=}!ۛH0²L;oڎ7:!OͶ7_5 cPnxѨ́Š8"JtXE@``i,ɷrJca1 A &">TmceD UO 2 `2#V10wD!bJk򺁉F qd!7F@fJ3bJ5PjnA54t:sOϟz?[;T#G2.o5 Lp4$-u}]xڗQZQ.ZImkY5t2tj*#@jW5#Uv"^`dtf6.;(`HMfoccFʴ[!ĥtG/z7حFzN(kξ`Z5B\?(Jg9 76{: yVd 8{{" @릾W(?][XX[[sK} UT2IbVM EQUN6"qT4+jowwgtuulm--9^_ڭg'b6!럽w'OttuW0YXSٖ+O_~0wh4#.: R1[wں: FFIȸZ1`g鸹eZ݌vLjT1;ĕc 4BOG)A1vtE 5?c Gc!T8 ̅Kk<8R#"-O$NX=G4:8=q䵷[GNOt-Cp[ů3ӭL:C6<\̠tkyeXlk\_ظ~rib0"k.e@<+UjC \Ky!H.}ZRvv&k4|D &39ȍ-x%uAKPQBya WQ} L @1ŘPLB$ B1bƄ! B@)ihLi:~)t|tzH??It:EfM/FX] R!B mYf<}'&U'xL$x:Qd2[BBY*.mm-/xM4t 3Xq:pSqS_ lMxVf[[6Lwtu6`sc[,vTMN  Za̦W874`}?_m={Şa76-414AQ'@f(@o,?fYm1tY?~bĹ3Bj븆%qM 0B7ߞ„kM`o2,WjC.$j9q$=2#' +^DM!!i`ݿ=x'n8-/FikMsL:K5ʸnM_A`wLdN?so B&b)B^o:Scݝ5Jm[NMdBGP~AYR$HtHCV#$ ʽ٥=u%U8"JPgBjXC{2BU/aTEvPꕋT`n5jZ:c*᧾s{ON   f!aٖǿses?ͯ)[^l0 pKkb&chm֛tR#XuQ6,^,[ ;ZAx"1ӝrV p$.@zsTDzc$ؒ\"@om $PhL˵;M#DDpo&!ca0f'--Ki鱸<'4 ^tYC.錙: T.WݺhPqI&8^5eKAMVc $$&H捕ZA?';1>KY~YҞ;+A!s4%.b2L/%@Nԇ !.fH*@ )`"hŔ",0Ɣ!T"1ƚRȱ 0ƘDz mm6V,}eq1963\!D nFwt1=,ms0{g֯7Ւaö^Xoi+\O7(LV/`0ѨybGǞzѣsBݹdd|A24}C̲}ݺuf< ƎZ̶M+V.[lF#ō_{-ώhϵ7J_kSGGDV+bZ(8?<DRBj>x$B8%qb~ۭL.މ3bIg{{YPcLd2Fy<[4(#ҐK aEEB^FiRb[;֗o%|/d_mp^"f7E'G lj##u/5JɣaL[lPPtiR%H&B.b1K0~x0ȽсK 4Z1K 9gRU/._Xv 7V#ðJs@cv5uB8Ѝ@BprjNaSL9*ʋx ˜<;7?:'B %`>O掝7t,5$n>ԙJ%on>:sn:ЫRT&럼f葡X,ͧů_;cm, x{Y?zx < @a;{:۳<(6PhC}8W69@JO"t\(O凵TOP /<]Y^b--\*z#D58,3>11~3G0[N]:\zp0 LꭻW sR{a ND ڀG*}T:B;M'|w8\KoGݚ[k !,` Epz"9! 47HBCq!ً,`Ld&$X@BQ +0J &DD iBǀrm;4 HinB %ËP2۲+ =}?=~[x)|K͍]FF1#YۚI=6ĕ3fJ!XXX4+Cݝ ;|~  &aǘN:f&jh@㯾ï86uLfA ?t:y4Mv6gg{EKT\s@NV ;2\k{?œcGN5 $J}y}s!78rn;J$ 5L.ㇼVyAa![/ 0d\`,ك HFwj*eL'S߽q!АiY-3j[hkɘx\CG-B"{;/4Z3it9GcBSSɌ~>HeRa\~L#@K^T^]R^A,9 'BxM)!틈GuVIq!pjXrxq̅cOJ*ʥR¥c<ylB'ԈӿP+}r-ב LkKJ3If,Ko΍n޸7ЧJN9 W(;[B”.81B <@T`!Gaz|{ó7-1x '{zc-8GL)ιDӦfoܸȔvJR]=/9s_bd6vm2MBasoxdd|^uw+rΜ;eR !Q?حP͠eaLp2a21 M6V7섵;'Pج;RRcx5h4 f(.cU"&G9D2!#u!Q @eIQW"X(^x4:[FCi{獟׷FML(i L[coO>iV@mz~wn I%|+}B\p܀ @Lm9 H픋 O=/Z2! fZ:Z:xbeb"/c ((?'SNI0`Q$‘!1 „D`1cL8FL`eV H!@ (ǀ8`Bv-;Tņ cX+ol?sd^8X݆04 PX(x82zABS#CVG+omrǀ\oT6 '=S=}}RX, Daۥl:ݱxq.c֭ oDw퐅Ftv3+nagCPl&v (E:&K"EXw6)CGJ\(&4!. 㱥D"ܸ)y_}coo/x^c` c449Вq&1 }^%5T6!@pOLS~޽F8i./^ à{c6g_?ޯ^q41ΙHP<(? GLB^@뇰XHVsɈ#Řf?[Ҡÿ捻g/\qL˶ JǑBž[V ?XGG{ȸeq!w=asښZT)%#10 --]-Nh40P"m{Sgf:ڲM#{V:nZQ55ƅSO5~drnDo!|P>{Ei|H T.\pd*Vxp!^p2tN$ʝ[wM30-fv>OcmFjM/?Й \,m}u Ϥ,- ƕ|$i2ݬ%$F\!75;Or \Z J섥lS7Ў[o~ru`lxM4Jwee Qc,K;~đW+!Q}N!Baʄ(`J" Q:S"yEG?Lg*@Y@+,:~̭5zI0& T5UKZmlkRC ]#'u`L0,accB9 }]c0-Y3 M;o^sjf:kA &X *{Çwnwį}A_#U\dD E,QL"_8(B~qf ԑ]R+?wM:_'}r'Ӯz^*U9B.wNXbVWW8önߺg%] RNWoq8 s)eyj6hi{29!NfL98tuuV՞N%5KjaG# f y @D>LyMzӎݧi"sʉuk* kj4*B!  ,@DB•eD*Jc\Հ%*GNM"Bj%~sa s bLwwh>sJ{r AP gƆ{-{s{$*0SnɘCX .8rJ1!J{OݝBB6nG|ikA[/;[*};o6)uF=PV{,d3o+dINMQ*rz[ &A@UN+  A`"9\}0 *CsAVlG_+aK4|b$bd3k[jt @{J8ЬΌnͿ/PM(ikۉLkUm۰,2-ioa#㺕tA&r!ݽ}`نowݏx=DONtm*{svC0R(댝{Oԉ W^DŽ$}Bd6ݾݛm;TX>v'n?Xz^̘h_<L-{qu?ŵO>fbv6DR C8H8tͩG "N ? ceDUiҐQ{Αv^V9~jZHt щjmm}mz5P \ȸ4aQeT 2R5EȍV0a 2b,Ip8 @QV`@0+`8}dzzFpSy.JFs`Dv#0tc\(GB,-&3m{\{pgfj40j>{'!̵5],K4J1D[sZwG6֞f-y!Xjh[D9ge`Yo1Ƒ)7zݰL˾=;!yPN09<~D& Bl4t]+= j! 8Lm7ot9Gu#S2M2q=x_:vԑA;O)Rr?(V2ͥ JHݤ|puz=O&]8v¹0Ӓ7Raј0ӈ IXYMoMc,7Q\T"a #BO$1GK%b~!1! Y}΃X R` %зntETnꀕ[}n ccsGRh,-fw $v"6yxg&VgW[oG'GBDI$S]]mmYJ B\!*Έ:Jfd3(Ys.Pi$P*\X zpxeUɑP&u&ҳDF596}4Ȉ&tyMX6# b@, ..kt'1\=*6~s̅gFK<:=ͷ 4S io/?lOfj{[Àm[riC: unw}bqvumv~ake35j՝gxd*l}mgsyM7FђNt:fA&Le2jYkJ  DToܸid%ݨ;XޕwqKOR K"b2Ms <+"q6MΠZ5 J90 <~wog@wjh/ Yu a]=]9u`{;p0gW4+0,& f!nf"ȧbe7caSjgJ"6#TXLpT Q;Kd*PA!,eIpI/!G4[J'dzj"#7bS}#cKȑC!RUW #='.#_/<;۝lkk.z57?l[9A}a!3S}|w>SVB Y$_Lp! @Fl~~.lnhZY v}ҥK[;:MqՋ,<s?{q[>ab&dRiԫU>r&ʼnyP*lkc^MN|% Z\_^G=m&rYaf[]7K'R$Fs*<~TꦣuGZD+ …ӧ'ƿ;dK[k 8MY1K\ " r7;8ԏ8T^5?9=탹;w\kNp2^m40!w+v_zhi8Dre!MqYb0+ >tuPuS*wDJ[D^ L"c9 L0]󭭥j/&CÃR7MbF+?دF L'Ehc+?~Nk_@SkoCJhgh֛M-0>scg|ba:5-G=+[_3rcD J0z!5<ГKٕrZs9Ż!$qK!` ,"7 Đ Qvs5fy{"D(%bFéya&^zO?NtZb_|urn+Ύ[+GjiB"e8*2)FBJ H" UG!N_j,E :.T-$RLAcDu0p;tza"uS5ڀ J 62^ HM绺zR !^lpݶ\vlfL$+׮}5lh?o/L"1qG0 MڮWS@zé;3+N]|!B ZsSONacssdc\tjζL>[ԫ*F C2fM>_]loGXàO+oϿ8v|t`rhk;C$HQGyS%#Z!!~x8l[TSZ"̫z`詸wVEZ"BX1ysX3Wj8ٓLq$8@) ` 8mk{}G|:oL/>%$-˲XL7tBzb/n<_&*St9PДFC5q~<'0G&Ps@z$J.@L77 TsG #c@; y,9qʳڲŕL,kSL]'Kb1˲ C125B`Jo~W!O?M&lOͧ2T5BiKUj7ujؙd6fr9 ZF,br I"i\mI$,)) ¹|M0X>#+)gb $\2)+.FВ HeiP CB3X2_<:Mcfn=xTU8fB>mIyCF!z^ʧTb1 Z=s2xwaب'LʝZPjNuƃfB48ngxlk>9ݑuIh_~cv']) Mjp]79G!IhP%2ߜN@"\J/ jj 0RߡSXpyEPqa!(աBG$ %[+QXvQ[mTA 3-.LvuTj A,sW8fzk.ɴ.WZ2OO<BV6F5vJfN(`,r]^+0F7,'Mgr!U.ԈNַū1[$%GF֨Tj:gNL92n:^7|GŃ %IX:3ph#Nݑ0`j ꉄ˵&[]zrG,4:T] A }d\RLrTפ)}F D,HZN&4=WV2fsg|HOwi4w@,(q!cCqtf~?Uguk}ͩr ]U]]93gI1pȯl,gْlYdqG-/څ?{_?O?oV׾x^ T[33o@SCг:b %WZ`Bx+/ WE(ch0d̕ZĠF1kI9HT[cRU.`/qcIt؇#' g,Q&3\ʝ͙iPډ:^+$;rPs/uWN=25=922_ ۝j-j%1PՀ!H&tzRїȕO/P^I%I"=ԣ?;>yo>a/H2IRf2 햫/bh%' EEN#¥iPJe^RQH^&uX-(%ҒkU(.]|2FI$IОUjRUanO~ {Vj\{?|#G'[NpHHnpE"fazm!_}u?g~_Ww~ow'>/}SO$$(*Xmt"Q(DzH2AfR"(5#) $Ӻ d{`3b*QˏI怀UguF aQ]P4EW1ZUOV4 HJKCb$D*ح4.l09s̾q'$V֮>+By>w^m(XayM`N"lvWonms:g7l&kX%v"~3RȨN/"L%ivNoW)S_$4ϟHȠ'Z}moXnV+LD(Hq Շ̘F5WP 1 c h{FBcʈnUC$:xcσZaN*8w\ѓzJukgf~6kMDmHHH{?Zwdtdjn7-_{v[fTZ2 չ5 8 $Y TQƙߎ ^oG$ 2$`NRhcR4UC6. 7Ξ9_MYnv0=gg-4Pd)Q5T_@%=?xG \WoT+Wrw?_̻J2-3}C,d*Τ\g}u}i7^^^*t:d#qqOBƁOo.]]?l6![m{H w¶[t!LQ)c2 %>'2iCԓ. Ԟh""П+gUa {g%ӐQm}{sߛ7۝6ZWNΟ82L}Si RHB!meZiچWy ?udOGO֖7}=;o|F{l|ku0q׽o5;/t̠j2+Ÿr +=gVU!‘IJ LBRiٵ9S=b$XR,4+%Y sN)jδAUJz̵SlB h62$$+?4CI+d2-)0X[z/_|:!߭֗nloV0NsyN[cH`KO?z* qgF _g.IΥ=7^y:v1)C|l"tϱDB"gE3.Te2 IbՆVɧN-, :.!Ip íR-J[A! .!%; 굦"UȞv7 ;`bInԑ-Tf}嫎)dש [XpB詓W.7-(RH)$0H@@V5 ӱatbRߨnn޹>C!AB|BώNJc ToB` IDAT{j:SImvIwP1RJ)s@VJAԐF $|:T7|'|]s' 18L-Za ?pCAq[5 I2Mח \^^< //ܑ'G 3_~ouM?"t\)y6ZLdw#WB)dX;ry2Wh5\v$ \%4k;"Aګ?4iw,.칋_ky>O# )0.TZ*fgD\5GԦ^U4I2$ B]5RjTU)C@gj(!0μV{}aN&O u$F֎f6V*[ƁG6UzAلc߾SkzCcC)RWfji5[?] T:-z~/>K߻rٳnAsR %1@JJ"!DHLb<@4\k#Iĉb&1_@ C+ $SmÇ#}AT59rA3x(Z~x\q 3JDFmn[ T1Ig$R2Ɩv~/;}'rC+n\X6*R:𩻎tnl-qDZBӾz2oOݻ](ٻNOe Q$gyayw{gaqXF&'Yo|+K=rq]A(a]n4ͽ 2d5K(1 p8 @ GCODBQ$M7±?Щ)Jj^/ `ݥR*ժnگ@":DFkx S5.Hg %"@b2,I-,.F=w$+w* Qk"gNNۺu7A{rb!j-,^p}aqks[H $r#? @RrlzM& 8GjJnLt4]i HԆHP\pc^gϊqޠ+:Bh&O[ol_6$IPT|)/DmF3_)"ʍj00+#) nЬ( HH ~wNNKi3t6d*,~hךc',6t&p1q;^@?v{9D&&"?$)Q5qHisq#4}r/o}oizizv  tVZA,LTG 2٩qPSB1-Ё*bu1G[D<a%к;SW/69r!=>3z+Dy޿(R1m$#<@N\f19oZf*f1 ^{뛕)G_6a0tݎvdMQu7._9[+W {rvj/7K@I)>RpciS=4H4BBJRp\E(YK9δ墨_P!6QW=anV~)ew-͕$0(zs!69"L;\ l[vP̌L{{\zV|FB&w[͙Gxٙ'^]\J#vahYoZ|up״|.OHD2y_=Ծbw("GFoVs7\/PsmrdaҶZh62{aF'2 e$$LzΡ^cP2m wZ`oGN=CT*JNXbYlީ뵖rV}1j1SDDɢ|jg7c(d;NyՓ槏9}͛7l1_@D2ڝ~G[KϾ`;rN|+K]"Aa7\EZ( ҈ER,N(x?J5=$FEDbd;8~t"5\b"|UT0k,D:27nߺprWcZm0I$>w]2$7ܰ;D8v6lH@y;csedM?e3VK N%<ةaFRs ise 4m'N2@49HQ#)]ՙa vw)}ucD.cX\qQ;NZ(G~[_ZS= aHbݽ=C}&JĔ؋$RpYyd RX8 JBz~f:qRTaN$ Ƹ@&O ƐhƲ“t[tOl``l7L3IF"Qݭ4=?Nڶaf\"&~_;>~@l;4 ,,}/@Fv&~=Gƅks蓏عu37n.zz:~+mTv*  MS**j|h6 õt>x"5UlPo$#(2bGa|=0U @R}j/zbcfb[:IF67lnM: Ѫ5@2 \ [[\ڭBDݚ-O<9J6[co5~ߺ޻=j@D˴X*wcemåJQlDZ.P{< 7[#w}fa[mDzM%$MC=L%~@Hp:DKD%$qBEz#w?}Fw*j[F*Rxxh;Qw>?_kwvkh0M^V[{8BW-UTkЮP]piO׮g?ډ_>t>ŗoz Zoő}}?q3on^I=2E72 rՈi7:tXx\w:BO@')eȂ B2RN{bbѹG?2Ϡa?3v聞|6-1lu\>ն_3 ѡfn9I/=AKuA}EtFӿ9?-s{­7_wss،v AD( ԁ2>Ռ} (aہ@ViʘPp n!NJ6P0$>h@l~1˴$ޭJT-:$v&!"ד~ $0{| 4MC&S^;}c!P"aVڍ+n^V/9IQ0?0c=~r BDnNj\Ɂq) `&D>R)/^֚cn.x/N2ž3YnDQrv}%…7v 7}ݙdzfS"HlF K5$Hվ1u |/YȦ˻7._| W.]YXٮ/Lӕl4զHܧWSҽӀ8PtPV@LޕR䪏PWwb_C=`p^Pa >J 9$*"cqZd:wф]5r"B"fm~{gwۧm4gnM0?6<dnllW w$OSn,޼vxo__"iή.a ՍR|swnѨ{Et[v¼}_m|Gf'}Mln\|bQ_XK$gxbrR($)y-QR80[} T.$DG&.Vmk~~֋?}>k:G==fv-?`d¶,ƙt:{o^r3GKb^s鳗3FPEaqy*Y7כ@ w%r=?K,ÕJyIrzfER*VWi5C|TAҐ0$"$I#3,3IwBU(t*a'( C)%"  T&]mVVòPQp)vs[B@0ȿuzH|cQ(`pnfSJQ*0R";@! vwەPWB$H4M\\Q;|`)sRSGt'UA ðLo?G~SҌ.HEO8<+ۭ v{ua=o~{(( Z0&D h \ qfZB)I !L1.\O^xW߼icS)i-^[>8Q/o_UoK4Tޘ.U$?Œ&iD5  )PHbR@ƈn/Ly?2 bĞV B_l)RĐalnRgY5:WTDZcL6;ʭZc {aP fƆywfxgXk.\*}cɖJ d)jnL.[KﲭsHVkm ]yWY^[yh<jQ{7{>ıOXŅW.-R3оVn` ]Iۿx) T]AQ5-pprO>pwG`;;ٞ/{|dwl:!)#)W|[׮Hf-`?w쭍 WeZڦLR)H8q&{o7HrsSȾ}K+I˫/jժji-^ŽZMT!õA/rPXMGRChX@E } !)fr<<7sёR18@xAҶ +aJ,svQolWQC0nxU]>FR4+nt:$#f2L0R)' #)@L'K];׮}/V78}`,$7#:[/~U!}DڲG e2n}sL# Lac{L'?;rr>i aF*(r }qnZD2(=c=«߼>vp:q8g`3To,,0A/$Br-MpS0]\Jt 8"RW=!/K }fK[+'O~6oSwn Nc}y'>S@ ac}EnWzS*dB1L R"Cw;۪9r?Ihe:H%(m4 gdҎ>}7Č^/?X_]_!j6:n  c;R4`׃L!pQ "H9d J8@J.8 I2$.% _ (BBU6ָ#"zb$ 0ByAٓ:jKdPa×=7`B 7 30l O"Q3ٴaXgl6x~lۃ}!!`gLa߽r|%wvv>ăp[N˭ͭd*f2YDzao]{O}åqtrJ2 o$"u3%< =5.*Bl )1:>==%k]ɠ~3=o1ppOᓾ'z?6#_셔e<'|>o%o,ƻZͲUcQ,G)IXeL>X2ArƈzQH>QnlY DB-eSӝꕚM1O*^E F@bDRFjJʓv*MRWT&*@H(DiHH6\.oِ'urL˶!z- mmE]ΤleX>/^{ƩO}ԡi)?ڍfXv:LLNp@b1 rY/zfj<(D"dIXW^l N5w/.m'>oNߩUBw1i.߹D -G7!Hm@RG)$X1[@R/!i.g1R$)&&FZOA$a7KUFwh7vV%$Ɩ%7Zݍ LA$Zv4?v'+ mDޞ_~~5<ܛJ'mNt:lffb A6vjnT6ƙ3mj;B}#CI'~ׯ:~|4З*4|ҕ[]ܵLŝ"IĐ 5֖Q \1NvS82 b^+)1# m7h_ׇ>c?\,vo6jDGAY핍L9=tvwlKwAg.TK}ݖauzKF{qe ZDqR{x@؉v@\@)  6K<P uNtHP@DOwil쁩G=2{`!%0h;FF oml^z_z؁B.u~T76w8ބsl&3mB2S);G DJ$(I02-/p B7 LIev[kD2NXNR)42VE2Lv2}{>'mVLX]=ȼr5B[)F0 njMeRd*Y_ƿ+7 ʫVju$7 ep"րri" 0Z:s]}rgZ^{#5`bo[K,FG"C뒕OIH*ɨof+kwku/:#r ohBu:-j4$e g<>mb`%,+d2lkF*S ڮu% PDĘrڕWKIV1Cpd "2BOWS|?_J/^y{'+;m5[ 3]2*TrП^8HFB dHʚ( 9>dNmI8\k$/jkc-]"[kwOqòyG𥬮?7yi[ed"afr8~ JE@a01\/4b*C'}n0zj6ҙx/U>ԣ]n;?rupn1iMn,L.yd2e']`ٮV[LWqΫfXLBBD9VOc<FԒ2jI;K4 b/htIReIAIdzFɏ|*9"r;~D3DR7ZV&-Eh8Am8DRis\A[B bXR$HJD3nneQ!0Ӷ8BFƞO{bg}L@Hi0.%a$> B׶\YYX{{sf'el6V+6 b@袊 ږRX d1 5ziqAJ UA>{~ȡX{aP>VL?kVgnzYhau77Ӕ =B$ &H[=v,r+Y+JE}C\s}q]0y&mcG@z^|vx? rM]l1+(JX^[Xh-Ir/0vW.={hfhjWVϟer&$!DIBqpIFĴ;e?'uOO7{zϝpw :23<:=)d6>1kN}upZC%\u|@oѬT\s] 69Jm*>i,5F}AX\-;ߨ{!:J!@  d2S?<{'FzӉ$0 0")O{#Q~PT{޾0}x%O􎏏viٶe\D0f=4_m7w$͐@AD 2NԒb83gVo/z^tNooqmi3_,0 @Q@LnK˻#f&l!M:vDN&3?YXL2FŞɓܝZڨ.0ƛz1EOi0 <[^^=s5?9HHHkpwcrP4Ԍ\?ɚRH%Qk yTֶҠ ՛}_x1$c;ܽ:u+ N۩V0e7?{gj:`+-nL]Ͽv(iٖ" A{ߋv7 '$,#L䳩[8)HƆc)"=+w.lc$7vE6zow:4Fl$W2#(1u.l`NOkJ+T| cKƸ2>rť=2<ēX5鵗)yrTxxYd$GMCTVm1" A h  s;+7 MM]쏟wb)wIDZ3H8)7?ٸrBUbvuh2 D1]iHfk7Zv̤ ;i9v"|詇f87 \I K+AsD'@m=,0UZ(OmFǮ %bR=1&Nb=vS-$$"}!XSf{{Ə;zС]dHA -Q ViZQ٩DpTwwSl&'!DZqN[ ]28o4[e%杕u?#B)!ؘ{#=#" CI L2BƭΞ.Ƨ'V>0?7޸4sl4LTBi+(L G<~^aRatVtOs|1HZݪtH$zF>Olכ~mp _86?ϏD\nכqh:\:V}m}3/ v> 2 cɔ 8ƙr{{+oޮ5#EB$IX g 8SIsE*9(Z[uzukgtmf"0򵕕.W Dz mv:;=ScdY:seuǝ84٩loC lw%$_QLѝǤSl "gfpГ$u(?H׾Ed\OSAM \Yʾl/^j .'#PO\ 㘋X<JvWY[ԩcGOI!-H糷6_EsL$gg#$rF(;s7n^!DbĭlӟD7FSnyjmkd`p`I! 1m3CPJXڱGHiRX(vb4>4xx~n`pPhdtwv+cGtgb޾48 ko t:l03L͎kvN7:2" WO=x"0 CH[zY&/8Ee2)BK!e"DwIN!3H >o3,A$ B$ ڎOrlS,Ʉm93J Bpf:V ݭc‡^ 猙L4,#t nXvE_/~zW>2F Z-aB=1i!ʽOu5U([D\_Z=Sn޾iG4,+K3#fIN @"j# Ir0Lz^.A`XvШl$c ^)>?$}|dF2v╛-73;ۉt2_ȅ^j5kW^OGN[kiA 0&{aĐv{air^7T!" )ԻC65~#кIyWu $ VJR%ƄBS7IH$Qv72/~b~v =knxƫM=?'U0:K&; KWϽG=6Z थLFBFrl3i a˗`  pwg'g2E!q@'J$2QT+W1i55H Oͦ%G$b@R$*T&7;zf߉cFkg_=cC\fև+yFx K\ REKD|ękH҂=GU:!jG [Oy>,c!_5$b{EkeڮO=3;![ؒtJn\Ϋٙ<ýgdl4$iճIml7ϼnOwzԉtW7-?g&RJp.CٕZN"%r581%ąt+Vigj,J@Pr@RWƓ HԾSw=P:! 0ەsכ, oh;zJ66p1lQ55 S~Ͼz}a읍mݚ80J8!E;ncasz+vƠ}k^YU1E[px5xKc*I˞}Mk|0a$eB6762:33uك#b޶L B`@RRrpuZ;?׷ϦI)"Ւer۴87%DBQX٭\z3}w~/l=wk|bUJ}sznc^:JLM6E{>*'B(L3닫BvOQѱ۶-4 6&(@43Q$i'!n=~ULBpwL~{s{96erIZ .;t.wT&[;nqwL (BDG0X_9sf@AHRRNrQЍBA ^U?|s Hl_/ElwgRqo~h06jc& ~LڱeI"eX2J&0g(Rz,o?{?뿶t'DN'>-nYfYXH 7L&eYER-ۖhwݍJ}{ˋ+zoEo})VP b$ЁpJu@LZ_157YBU2d%S0aFm{ýG, ͦcq}} DX&٩7t(Дh&1B!OP\:tI^DN8\ްFM&MLퟝi[{.7JoZiZm|;;;o?_Ώ  MLN9QIWy dt3?sNc,K==}Djwz뷖n//Vˆu+͛:Wt ,, @e+1pDR"n]>{X*'83$öl2He7{33vG i,:XTABa Zy/r:xlzjzГaຮL'2SOnPI~.HhЮ7"0=iQNLf^,3d\eg+d`o4 tNǏDnp)r,WnۮHdn›gDrtbhWo_|o޸ryXYSF"BfH@t~(& IکVTUqI_/Q<8`{q@kx^me|a"aiC!+W3/K/~w禊#CR97NeaIIA4<ͳLsƹɄL'Q2OD '$U)pF%W+5Іv'tb e0N 4m ҙNr;Qxʕŋ7=9Y.3Rpk惏3soِ"/_4ɦj66ֶڵ# $ATr,XҿtG$ @g[<5Ps=SO#$" C U˒ͤzJ]ݥBˤ2IJ(Ez L%0RlKZO?KgL2}}}mZf*}$H>(@fˏ bONOwip`FVmN'nԛ-z 9 S))Hx<H(@TI_1j紈!`Hrb,ڝ'|ɏox~R-z3Od#YJF^|q]Bz N8"""2-SA$IW?͚'τ2]/  e6@H2(tھHJY ,:+!"f.EB>a$ ϧB>8yggG?/:oCQX67e';Ս_|GW,Y9}`ⱓ/,='OͫoXޑh8&%&] hQN<"^ C$ݦDr1%U5YD(U'%dʐ0w;@,qo܋=7 uK:Mw2/3 S[__5CΈd̍ۋ́vX.e{/:V6-9Dj Āܖ;== uԤJ9}[vvWf>/s۫DF;[kn.6#}̌qR1G/# 1LT8c bR4exxEHtR}]c#g9:?7;392_)(R "RYZtܶFu=? wU+8td:+ (fC"0Ne$NE,|޼tŕ[ ˷JuC1# $e<: I{: W{KT" ճ u!. DP.E\;cc󅐀`ȡWo\=Pfm TAƹ$(2oΟNܑ9b)T*mYoqnm8v؜fe$ ]_Dbgl|&Lʆk++kK*2U0 0SjE/\i]#+o&Ia6zbWyתl>vF T.ڞe 90m1Q$ir6[(7vN/~h_Ej~ @Dm ڄR"?B6Aȓwxv',e۲èmnlSٜCnXa%峧_~ȃd2ת\֨X$@"-^Ko}3 Νy{j/Te}0mߋןv-?B 8HIqذAzŴmA6_  I.# TX A#$C Z<5$q;`1??w`|6 -BOlvv~QnOΝ%BQT_Y};;6T.v;DA$aJ~s4O=Pg V]_ܾy۳=,_ݮԖ֚;i Ph1|סxA"rT}.H@ BU>0vYvoߺ~#_dV0a%L LǬ֛ƝB*yTw[ԉx¾~떉{Vv}}'lFVI g;[0 $$")\DBņ 戡,੪ czJ:^#$g<$2l*{{JȉHH)B7d(}f@t:mf$a20mBQ'({ t % ! l:JuQu[An9"WnQXs$cH*p Dt7(2R2΀n݊2wRgiGAD1L4IYI?k/peYB@zI C*3 ٳЃu$JUw*j3B\o,oA/?▓K91eYFZ T#Pŕ*C9]!H*G$SiRm$1Ƥe+>v])X\X4#z&A2j >^obRoPB[Q+ol<# oj^l0MtL^ۿ}/~Vw)"aHDG4ZΥdժlwڱ{sF~$'}3OQHDL)@q>3\Drn֫_Bi q::pߗw;&`sk5v2HvKyn]׿g̶l#C1;n=tv=X07* uQ+'~Ƈ ݼCy>~ږI`E$2bȤP=/!~tɣu),,.ܼsgNn9j$U%WVp)#˭ސlͯ3SZ|+a'r=DʑD(iP5vXK:sٷ޿vdI\6L DZ2{>Փ-ZVo^}sYoS!YLҍ{X j!8+601-PP= "!7<}`G<05o` )BFBnAFً/Wj}lj O\9J8d@ΐ9IB(\"pF9rg~uvKwn,,^yͅ;wwV:D2kFJ[ zzA{TGUP HLM$Ƒ+a2#EMBdA avkV9;Ę8Q*>fū/䍑ى񥎁oZ~#);qG5$Է_357oWj;feՍz`oye^i=XX ;Ik1I-3Lpc/Ԛ$H Q/t1gKh]2UV PqZF5?'9u׾B!z-qPj+ _S0GRτ%dֹ}sWGGNJIG!HjWg{}YP"-zKcQPtٔ*}Uk҅cj'ӣCCVroltȒ2B!SRrCR 3E!) .\Z\ӂ!DpOaa٩LD z3QG6k绦G{Ke"'QrO}ap]f.kD|Simܸ+vk5=@GTY) "G$A/5# MDAR \ 1UZ*M uchG@a$"/PÇff-#  RH"UwHJq~L?H#xcE[Wv7S_5[Mv6*wKB&d!#IDWoM?>ﵛҶ6nݼS߬ )AUR\$,N$ƸWt 4C3T:$[##QZ=~&74vZ4 ?8C3~F!QD"*҅Ln]zRvwi{4\zJm8ʤ$rC@ɴ#jV٩7K!u$02c$rek p?$j)J[H  'ag3b1]* I1H0 fLDaڬ메q3o\~O~d2>k^fhG AnYkfَ"_H#bj{*`$ ;DYb Y|oq/J'\Bx$Hl!I5ڤ!`ʱ#̧ZmWa4L8gQ~{SVk/273P2$IHh 3@f0̴wos_ïdfӲv;|1ϙ|Oǯ8u?Vc6kQ$RJ!d `XX^[\^4ꈈ$-$b # 쿤ֺ@-¨;ȉO}|*])ȱw/^|R&t9t7O#EEDf0.jJekwdxGikMf[/٩{g>" ) M+-d1ˤlT kWtۮ©,W˪4 v,MRh p$@^ٖ"[o}} <_D9geߪ5[ ]',nz\}_O{]oI\ӬY_k++Xn1=}븯/KEkY#W._\Z Α DZ&ůo\e8;rj TV[K)'J\ǐpȐi6H-UTF /fSȧG'bi^?+J]eb iz^WV*I$T("pHH$vjDŽRrN6wfg Ԉ=˗޿~O8d:&O?˿ #QRr` |?H]:7nom c*PHI*~=VyReU%fS#]jٷn IH #L$\qdxpnnѹ'!Da AJP-[hoB?_]?xs`n2 )ڡ*MKhb d Qi׷֮^r;kZiK #qoR eFsjS6dlo ?$b\ikJ]3QKW꡼vPD2,oܸvޞ!27s}G~6@ou}B2 S%)ch(80("AhJ>&'F\Ӳ2X*rN<0tZJIi0V^vcy 4RWr|o ML IC% QIeJǟ|)IȲM"QѰݨn*JÞq0VaFԬImL*7v6/^Cepyoόن2m$IϪcY^?~{{RUvfDw_-/gÒ\) )\qbqn0 z>mK3MHDQd', X)4xVRt>\oy/_v%393~yn`؉\&K% OŊ`bKfʹx}Ku8xrvkmŏi )~7#RMD`ģ>D/3 \IBa51:=TLzFOk}dhl4|N&Y-7~;z[  tB)I'HqӈYQYIownuust#tG?d/B%# |yslm->CF~gn#PJ R;^`U0=G'$e'f*A|J4]k# Z\<:/w7n6j=,a[Ϧ24-Pv+[;[;K+7_{e:P*uE;l?fԛ͝vss{{ueVmH(M$UŕqVl$#)%LTtl2a[bHDRr|$AHp/~B_oǿ7nge1a)"iijfպN 坊HhZQkwznII\'}@IZ,tfj> @*Kt,|ш$eNT$IQ 9 YJT8Fdqq[I;wy񇛭nKf$|0/Jcb:1HBs@L IēV"ia ޱҎ?|.R6M2^m17PU VNAfsIFN'H.IJ@3m{GeFİ]\xl}n]v~Wڝա!0"|I9P`&A{i3T<8F>`1^{er'O.H= qkuyaqxOϖƅ[.lv:"djK WV/f8@HzD0XSN77]~yўH"s7ӓ|@NxJp Xi4zXO}\YioKfc38yω\QvT&c _{?|Ϗ"0ёj]g2͉qLBve82:4}t.ƭWomEԅBj"^ $=0zGô:f[qԲ}Ir;[^pnn|lu\!kwd  Eq7*yޞ^鞁]nӴ,4Q?8a&fcikʭNrbuSpQC1$) !dS:*;<7{̑'zre$( ( tHF*PȀVov\6S#ɡcLE$^ D~L? ΍;Wo/]qf\v6qB$f0`RJ1вa!qHRy{C| Q+u+q2QLD}A|/DlW1@x̙N}FJKK- Ssm"f0!gʺJRQ("Z y鍷~3+?4߬P B!-AΑ/\>g$HHA@[R3%9d: hmuҲasq("ʋ:%j#'7a*KgR)0l>+Z-#G53 FQHt 0nU}W7ڗ0;eAĸZŘP=gv1ɥӥ\qv֙d M;,_ ,J[%%( `\'% @~K`J񠐴bM7Xnw۞-H -D ~B=\|f[5CT>d8eٌqڼ*[;o1;s#z=|e aU.o8 0?_㥝`ccVm٬swwk~wlf܎N.4f9efA)޷{OC1*8C!p=/=z=s͞޾zwHa+o{}' ^>яte(Pa0zVn}7O_:4w۪$`RE 3P9B"$)!z1(IJ?YcZ'nJ᭙=<+ pDJLidx՝V]K ;a68ʍ[_XϏ0ҙemcs^TZ3L̤/wLNir\o\n6ơKJRzj&8J AML1N{"j$tHs=SG M:A5Ne/qDowxnmm'l#WmV39=ρ1jC061na@iܸtEGB \_C )zo'>b6'Q>EB7VVV7l'Ͻ]=]cSR Х1!29IBj%Y C1n 3PAP7v +Wn-^]X^V#&,D3Sj*)s\̄@uK"2Us;9 %T_knBV{=t y Q ŝ2`@={qHضS(+ۧ?rR3&}/Pſyeܐ}G+t"`'Ps$! [tƴ8X&_Oޟ,`:6g\5g&V}VDJ $%1Gv~#5֚pHFg>)Z-WH 7Go^ˑB0Υa34$hffG-5=Ң' P랣 =췿J'NeS$IQA:MV*q䉇N :ĉ4̐Ą IuTD$)~z+̊r-l&U! cD:~/b.VD#1F` ̵+~GI۵j?ote؂$7!^ڼqex̜?w265f 'a%Bi&c,eLh24 IVvp+kjdd i͍:T&A)t#.E}y@m7H ^FHҕ"4fses{}c믽ӿ:6?wSo;v fȥ٤IeDu_kTo߼{.cRJ)EcLJqZ^C&bu2>a c Ճ3?@nm:ڭ^}7WH^?7+8OMLLf$2 @rqD &/A`!sn HNT+W/_Y\|gmms^z #M,3Ϲ_k_Ba(*4[L#NOO3';ÎpiVVkDJjIR(Pڗ'y)P^]!F50 ; @ kϺU1SE L L=^ޢXp>:Ht `zc!~6ЍWuuQY1PyHe׮\l8thpx(R=tu{1b`<>\($ߴƠsr7=#sBZJS}GNc7K{iA"dim[wgA&Q53 FJ@Jo׷Iľ?zNIR$=ڄ̒@ec܃˒D IjZmќ~zN#b$""hj 8!(K]~Voٳgld4{LhIB{8w4&^20:4++{xd֎쮭` :. drcN\xֽ;[N)u-ɐ?5,Cã#b7jsKssk˫FY@Μ~`H !ڳ4Hc(//>r^D?CC+\>tԱSGO8>3s¹'F$st0,.,%ct nTխ/_X^Z]۩],"^XΘ%Kűۼ]0Z$߅d(8B`ol}>B3.?u'2aͭD? O_7> FDLi>a`_%'R wggޟ3*s 7FmySO>,] ,ng  KQl-9/G`N4sha("C[\{pMi "w'$@vuYmn6>/g̥Yo: I3w4pى!NSc ;'iܮąM B7iԺO3b7˵}xR[ #$il110;woks{rq>%C|ܭUvv3&aN̹,"cSN\9yN/?S/|鵢Kh꯫?Ǧ[BQLd93qndbt-/N;Pryk%uH'7=JMLO'x%'yT|K|VVH"9?Ϩh!9"֠ #b̜nJ'HBKHB0rcz"%Ө4{$4]""N/YɃ[['G&Lp>~QNMML+{b*yortj|YK„*w&1I `dL4ív^7{Fҝ>'f LO9vPYpΥ.$Awt@KXu+ڣ{s.,->\^]:(%20H&hFL2Ƅ87eDXcU~T*D:ۇjA bsXhmBƾAECT 2ze;щƁN4$BUb"|Z:Df44uڍR?t e)K8͝F>rS/>?9~^d06sYxD I{ͻ K+^gq$?$Ao-IYd S7BȒYC{[ccZl3&PN4FZV<@?~?<>9s|74c k!ʣVk߳gKsA̬o !sV88rpzo>ooWiF(N goޙ*ڃ'NDʮ0i2Yn%Ru"M3u;#s>玝=%HqHK0"vG]6v,W}(`Glh@ZPUuHD>BÀPA?QmIۋ Dl\ŅqNd)K;7Б#f'In>>Zw(tsOtyae⃇]gyPF7%[K|z̷=Dt8~<>&&Fw񹧦LQD)$M9Ur;1!"tv`q΃=\^\jN93 XPT%N7uzv]M-d(6&`Y(, <ap!JQ=V:*]7jGQeѲ޺~ޭە*BȎi59}3fUv=;K_: ^Nf|"$c S٭vn\PQ~Wox"LFM w;Z3ɜ(!A,O~SrA4#cq>uw왜( Ư>3yӇ%ݪc2DqE<8{'Z̕ϝ;>G߼r Nڑϼ@hfDFO|ܚZZsbo={\9j %nD%fF~rK#ϿϿcӓO/߼uzrL^DRTon,F0rTIdy2<{k[pWfS C;   Ew.rd|ԉJԡ͝ڍ˗/=ľ_kWOOM=vܩc8/,.JEA+"CcI} +~aU5 z #/1؆WLKLƇ}L-z0 ^_ ֚^y{;\yJ3{qMzDd<2ZL)_M zmEB{ٓOB `)FX.M4LӁ?u "c)@pݩ?p~qy}cҨ 4Ym)I>VTՌZ@4"$c׃q:De^:gC 䓶 9J{ (l:P?XU8`%2q#S.qARܾhqiɉ(QLIY|ғO_|g(t/g CZM$ 6dlޛ_vF/U /v 8?y3d{Nm__(cGADQP]PeΤcMMVY.,u>|g[ XY&#cM,kF|y hl7!/sxt|4Iz7^_]p.|Wr\V-qEG~~~Œ,I;J/ޟ}TK~_{ӝn/"j5|Z?~Py_g/=1TE$iwW+"Xp^Vgci%c"b,"0~3I{6X'772tZ6%'?[P^ >=vnE0)=io 4NI >u G,PGT$_9266K2NBOizODu>xB;rkvhy[^qT(+#b]_i/pt9e!ЬMUPGBHm*@oK=׀q.Iq;Yz.ľ~c`bxZ[Xymniim{:;5IB4hLJ'=v]5ţ*LKAp**#5MhwpF9vXcDOUCDh v+kwݞ_;pqiucҪibYdBe)YT1(E|~hG= o- 0@Jei S~ S Y"Qdq n {<6F@Y1ћS %3[G~PX5aO :46qRt\!i8jl7ޱ]|߽v V 폯?ů>I[yvֽ VaS 0Aeg:ye70Gxo=EVvƏ,i:11l ff=U,,IZVO~t|g/r!1Ύ .0{]K}f_A4C)sDz*!(zf}Z>--ޯ\5 b)޹5f>y|y`~u͹{C҉`oݗ /r ӌB P=MK(D"Åŧffi_bBAtIoV_^zp >Z]_7ji˜Bc0 fD; W {,}zҁP#cDŸ0.&#DѿƃArЉ(8Iw(HG@@N  r:gT!aT sW PH j1a` +z Gh۽W#ÑV'eΗ S{J[VV\լXj7 3/_^ƍg׷$.2E(#- rWqtwJ||i4vQ>gIe ACYIb h5o}8嵤@5#)DM)( o&_ s.|^"Pٱ Q85* ݷ QXPߠCU*.=UNdia~v a^j0iv7O:]gz ȐRc,i;?fcjƕŕ͔S0RW7i*O!`v,J+ș/ci $?87O~|fH}P$ՏO[y,륜1X'!tc \[] +[&r @421GH"6Q@9S2u&Ǐ{l?KW{GJe Q׷77W)U;o?W4@ƒ!Q!kMw6{7bmUX"YvY6;hgcc8* X#js773]Izdrނ! W׮ݸA310zj87 9`ad/Dx ?H;KR_E@Ē!vK dl&l.-=3:?r4tzry4;+B:~Ƚ{'~j,/.n6-4 fZjL((@NYSIDIԊʅg-Hſv]  I5 > ďh3 R(׉F$#W Pgݪ-9IDDcj hB " #nlo'dӬn' G#@ z7oUm'"̺;d?e_u{>q}]9mͭK_xIHӉsq^_}a#>a_xŤ&3ϨOQ`.뵛n޹zk`$X@œ aa =jA@V1yI3!cil.?{@9n6) ;c^_=c%lB"vN&\ux"K:w8.ӕbLY7ۯ|iU1h4^\YwljJJ[c-9NxN "1:U&`Q7byҫN1Hy¼pTB(ٗN@.HXk$ɼ؜P4.S%ou >\|nZnr[oeA`_r iKVXlnv b@8a QfآC``Q썥zPDF$Qٷ ϭGJ_ p$@@k$(wf*zTg|iNי D}ЊkK ULD)! k:ns{.=s~٭v7IΥNJ"Z-%ڗ&D@ql ɹJBn-"+C4 6 IKQ p ;JjкiϪNa?~S[)^5;0 1;L<@fba KW8\XJCPTF陋oj\Zf ! m l旖ۙ3Jl&cE MРwF ;Wۮ;g>u/059m4{Ihs84/g=}P(ѱQccq!Z$c1"q %a&PG%y.ʣOίlqƄ}ZXO5k-jn&t/$R,>Lt{=sҹrZ͎"5?޳c{(!^HAЀSDùO={KKGZNYʇv3 Ʀ%K!Eh.gqZW-GFyc@Ѽswnva% o`&51[dQ:aZç&Na2Qji!xy?"B|7+T;zP=48(}).MFT{S\;Օk˫V,""gED'Euj'&CQԝ%{<:ܒaQC=NoABɥoz[[25H5La,0 buOȨqYR4D1 ;KbkJpeukgi lz:}Ei ߐoD#*@|Ƙ4|85U2 #u2(d@F1@ d@ʌ8a姪nFfp,9AXe2|*2qѨR3<]FvUu$+%÷+Fկ_,ʔVMLL(|4KLl+}P8 $(B ^B_D5:[_zW/F hdY O;~ ܲp`dhaA_Sb(sFXS-[K׃D-{<|8 y je"2d wu<~wG"k77>\N:nb|:@ha<By_"Zc\pm a,`OE*B}cdV7b٣X7lAa EO~{Xֻ߼tf8v{e>||ݭj͋Cc;vH T.FBq N]8)JPHTуXeA 1B*J C /_yZ.@CrFctLHJ0DZ'X˹G~?ګ-tXwFYnmlU7v P bDA a8}c;k nS/l| KI ۲b~d]+EoH^ K(/0V"]%oc&qп!\@'[rQzV\^ϡg79# qѨM-AfD|>xCAf30~21X jD6[Ch Z2(Lh !1du7h!$ E,FH&2hYk#!2XDD"Z2ɠ!c-kn޼@!-3"Ty!j=?&o:R@'O( D :`4M V*<G&9;\.ΌZG$ owUԊc hۭjwrjxQIJ G{?\r8En"Ij-Ei+`P+>4AF(V~CQkELƞˢȴ굤MMzud;wU]4@0Эġ/ 37B1aBmuѱR£?g_N rQD0! dNٳGFwOSۂhaf fnstocc392<٦8790lq!_*sÓNI昘)g۝smk䄕2߂=#TMC}J^d9dMG:"~G@msd=e !PC:hxCy_gzk ` ߔP~,֛WzifYdYj<}+5t!ؠϤk6Z:۰9`|X{P/@&$Fd+ ]Bߜ.?}L SI @Ȼ;aM?:v@;B◡,HOI`ď*}nBkMETkSʯNS((,v'YBXJcE &!6ERvB &K6 d Zk5hMfLZa$c$$ 55*̗"/2F> 5Ћ1 4J@NZ!^R)ݞ{ !B"DZ-E  13W#i~ NxjWD:v Ϟ:s.n&@< ^$ D$ dJ|:=^jlz ,텅Տ^y^VQm4;4\1^^^rzEw0@; 2JDhnOOg.흍 <b'EHsk;4~ӟ?%Ոn֮6;Yq٘zșveӜ:}rx0ߪT%Xr8Qd+7n^XZrF7ZM!4B hB$藚70L*7T9( m5TvhV0;IENDB`fritzing-app-0.9.3b/resources/images/splash/fab_slice4.png000066400000000000000000005237311272363601300235670ustar00rootroot00000000000000PNG  IHDR1LbKGDIDATxLWYs?mef===wfwXA0bTHГ^7"$JO"B"ŀ !X,0;nǷw}VIw/{ŪȈs~.dy2-f&YR`$"YhbLlE7mb7ufht"fB1MmDEYڬZxQyU[e!z15u3^Z*r:ձ%&3mϝʢZcb3lQ,W`Ckg_7''7~ǿT@_??lrLD%!#""\p|I11CUafl:U$ph`) cGS3Gh1E "DS3T$B3 !+" 9j)EXU5M1%0OI)l=Sx^/|k_LҶlLO?d:IK}ߘN'Ȋ `b`L,*fL@TL9h$x<[/jC4dH{>Ѹ{>hHm$Su쓈!ynmij+xT <ܾu=ҳg>KK7o{cӶ~ ۘ '}<={;3[rԛOW?;7ϜyeURCz䩦T ;"d5B4$d9@C4 MUԀL 676j{민IϺQq_ p/ΝXgׯߨ#7c4WTARˀYώo]`I=w~ssТF򬇄`f`L* @`*jHDdHDw$9b&60Q%D3 RS3#h Df1VGESK+ftrJ'Sbg% !:#~t!@EhI42$UC̈oDŽfu[#Z[-nݹ~o?ŜFR>6"akj@hd`f&jG3cL|LYqv}8y{JSa6}g8Ţ^[_JJL#2VU[!(@J  ~`6&ƼձEDHwmuS;皶q @B,kc;91xe$ؤϜ#1EdE$N)W޼BE˲@G;~o,| +JQ Eo}Ϯ7} ?o?_ A&40$"Dd&}z5j"{W=0)6  Q{iL  gSA06ŦS1icݤ5"BdB@"SSPQQТKQ zVϟV+[k|/<2\pJJmDo.mW>êmNoh D$D;0SDT5TDEY=w}hx: v.~0Gx<^ G(.jxvpxΝ-:!ϒ۪Mڶ6VUeY BO[Q޹ON"R=WWVWןS[yQ&1v~󢷴ZLj:^Y=ַΜ޼յ(-g<GO'?}_:sTjf6CQ ʲH13ٱD6/2!FD'nomy|oկ|wqv{ϯ^o%2˞>~{Y=~dk{kh:^zYLmVmܾyMW^֩ر#qYI +L ;НCDƓ@ؕxL DHH f2W:wֵϮW77זV+Ib:d(]2/$`fmv0xz|js͚$%mSJtz|hޜ}ΦdAQDT @ MHf6DjƆ@479d:UIg[g`8ZBvfV/l>=z0{xIrF@Q\;;wI?ob|1+xyO\ee/{:U]%U-fES"24Ml))zd& R۴4hi\Z,jBI4Ɩ`Ĥ`(拖1VbD4`m{ʢ @ 4&A"&2"3ˊmY.m &:v R ճS=rxRdEu|&4هY?on?}9 ڻzo #3PU$V4S3 (be >|oƃcl0IR"f~``(olE]b&ň2G8C)GD@5C3@23B20:Uv&D L `S瘐8,/$&f`΃hS"<{Ei]I6NIԂBDm cLX`]/|ouM@lhڄDɩ 1Je!LzMX];uCʼW5IɑK& "t88>?8xl፻8sֺ&FYRbviڶ^u+ D`DD}<ƛ|1E1Os`1$;8/W|c6UUӦzcZ]SU=;yCDdic{/r96qf?#p̶\쳫uW_exvq7"@R,+A~xpXRo0@&3>cئl1*JJuG^$ t !l6}7!mn۟<]s#}/Wƫ^8fU]ZY]:>`t;+)ͻ>9_dQ@C/ {u݈0;NFH`"TM Ѐ@ "31UUD@Ϣ!csd֭ۋF{~}30}[HƔ&zm,^G CLQڶ%GQ4ȌD(E$O^Ւ*)(F+=eѤ&DRM2 dƞKGiDM = ;`YDۼs_D@EAQs::zly>9:̱R_.m>XT;gU(rf^;WJ-R)1//$p٩qT^ĦjlW,<{43:DfH`A3E*;d#u"J1YV7 s5mS&˳,&)iϼ5MۄHjfQ(&hh'`I򘒴cRK2615 (%Č@ iWWW>_չnZM?x_98Oq>?g_!ޠ,Y Bh4;TIHFȠhZ_^qLoԍHCS,(]L[GND|x1yfbjUhh@<#{&Ι&4AB$&PIf(H&j҂iJ!9F"{ [/D C2;D(ؑg 3F,pW@ %dj T9k>jbolkkC")Jł1@DvJꅳ1ѣ;>o~cfUWu( ΣspT΋9Njŋ?ܥ._Sg5cZ]W,ăg>@hh>Jw_'U 1!tt>ԳyҘLUꦚ, !jq΃B1/, /?OvW ^[hύa?B';7>ɂFA?~G_ֻ.Iabj4"DA0=7]^Cn oc 1!.~7UW, F%BIcZ$$W'(*uմB:LcdRQ#BFNX%(A(뺪it&bgu]ݺzg ϟy"nd$51FVN# 35Fr΃YK޾x?2|fy0/7 ǣS2Tϯ!oƱW(j]#I;/`ʼDYV 㴨ywv||QeVB6$qHUPc+|z!S$:.̒k:?BpI2:rR]=N{3DRK'k#u`Ya~={_i 2y⺭92 ,V!X-5pJ `hb!ښE/Kˊ/>WO7m:(ϟ?R/ gnjpt0͗)ED#&2ΈO`4-8sΉQ>9I^/Jv'4b,sn[$mع,"gK])u\"򰼺b:"}2%%6*U,Y$IT16P)>!)BL!)yG )22*bYsz2[;G&U@4pSLEQlm?}zjk+YՈLAI`1WWO I/sV%(''R`?-/{L]xz܅pE#D(щڸ@d)E#Dr[%ggYA`0Ls3e!Yl[6ͼwψqXl1G"/&ө3Uƺ$  qXKM=,%Q).80BB2S"4F{X\i2W~އʹ)E&ސ7d2m찙;_~ݯ` Ժ"61S#%Byhi0 E YESP2CRCPQf{~Q[LLLDKEs>ͧYQ\;"bt";vΫH2%d4G^ OjJf'BcP",ňjbڑ檦`P ѻys@ޘ)qX@#v|pl1R&ϼ~O[/O7-)|Ȁ J."-,9Bwo"i'#"ft>SiS 5ݽ}^ E9g iR4 T 9o^| }֐/fSFKy[4Σ `<&i(7P\oq!(9D:Q (@H͓;p'Gg/g{҅v=b^#w) ڤh<,AVOE!/Р)%afWi\p($o=yL'1{jd ǓI뤩Y4m۶Ml Rm̹f"&@@{R0bL T P$$#1]V+jM4&6jLc'(B:9yfHf@`b{B$ f`̬k1""1## dYI#;Y.4U eQӢnUowL*I+(=>ݽuNuh'O?KI/Ҷ-4ƕ^V3F"BHBڤ"p쥽"妢{dooVzύm~y!p=OM鉤k,+1L1{nCO"LxR*+{ƚC?ͭSFfƢƖ49vUBF> T1|jM۪d:˂ Hf'2UM8 )ubbU9 wj2MQaPpsmۦ6>{x땽^TE^|'?\t坯#:xs# Y'fB6UΎ<'4DC&]N\l6/Gt^U ܹbTUmoPlZqѳzcyK/xRˈ&lޯ~3q8Jh*&xbs"FK,GTk3Kd6D3!4y[3SEcJ)5"5u#Iǻ @ ag!fPh~.1u|f7Ga9@%f B j6u]Ǐ% ?O>(Wp&?q?/WWj>35@U2 0Q@Զ[~xr,cպ;HUAfJ]׮]/huŗ_~_ٟ=ؙ]~4b*[0C,3hD'x060;ǜgYR1!.6:Ny|Q45fD jdbDd&TSUU0("zۘԒ*y1/wgmSTs󙲘HJɒ%ĞWD!H| $)ܻy(z+&VY&`Ͻ|劚Md6ΏGy_g^ypg8W|[xR9Ρz"v6w(۷6q5HĜ41Ψ ELMH`Knޘ~bU3(U c1#bQmEb @CVx^GmlThr|坿 ;h# !zvIj40fV$$QCDNdf+P'%0X/e0BS,og;7zM̓P@ !DD`Vbzԍ̳c0 P;TEMzvob$ |i8?xpѽK~ؒaqpޏG[kEm5|/lm~zpX]]]OETnmY[9>>2#@#5TԔ@d *"JBD`hjVb64)L: %b#B1CCIj^{SO=9>>9 f5 H@&t0QnDOoRG"FBbA ^?D upKŴ#2/D>;_po <Ď)A$+|j,I'GNj1?<.ml9}~fY4 XO96+o}Ƶ[O}z?z?37M&pȦ -s(!uH2d2 )Vh/*ImB^B_'kjyG{KK٠ȹ̿W|tû[Ͽ+9HBVDkeF$1!E|S71;̔ RZcW8&fY`|ִ8\`gAbMPPDw1lebj /cjM GC3jv,>zl護ޜlS^ib18 y=-dv^< a0VY5JۘCDU"64B< V 05=1yv":$D5TCD$啗x{eW](D!X >@?҅ˢ8:хw 1*Bf~$5{%f/UUhpFbͦ Pp54P,(v( OhqBnVHٳ$iQ^ +Ke/"9!G<Ov<ڙhyivx~ݿ.ݼwwyr_zpepڵhzw I vFc1C"EcbMh=:{S F%zH@XP-.t0\y.?hὟ_Ǔŝ{O/ &VňE64 tX@11dNPLE%P7Aۤ0Xz{g>ֵX2&ҫDJ5K'kّIR~mkA<~x0UήY^ݝXbѓEGA&NjԶ{?˲t-* > c"&L iۖclDC! meVWyՔZ&*IshF|R5ul#{omD `m"w.Ο?{G&dZlӏ>ڻ:w""Jâ,z2˂cWU5u\iٴ̚b1_8a/gX-S+kY9`W刳gGx}}# ~ dt8]~훯zvLLFL<{ @LOT&H]ЈC.fwgy4\2BiDZZ4)pmeW|zr᳃.יLj1en2~q}:kN:wʕ_|is{{8"Al[Mq<=ysU38,-]zorko5XO'sgٻZC%"N8DY?[;qE󀦦 ߩn4%T2όu]O&g6@G|UݿzMX=|v;IêDMRJLA9++/^~yH{Y,isR|(`.)/2b]l&yR#5SB0zUU"?wH@NJ;*u&};Irv~em١Im/-/泺j^{bп{dr ^_plQ!PPdGΣIUK ^Qdv> g !K" E^&Sv YukKIPnEٻdMۚ,ijk{}ss]45Ms֝;7]_}'5ܹ3 $*jrf3dC6RQRJ1 0;z9C]t"uϗVC0UB]6'D4$|tx'.I ^& 9 nڈ̤p"zbb!;6N"7GJNu Q5~mehFxsc#DJNonoؐwH\Uu]%RE9w'efòT5t>ƶ +"Q"v6eB1yȭ4jT-04iJ,,qU1Ief#mڂzQame%my=}+/PYz᷿?>}xll[#T "(x( αG6.bd=:<"hИT\:*脦&¨ M;T(N_|_k6]:_xȽ|J&D `2@`f4|gJHf0DьM٨N="'?}nk_Wo}oim mk*t>fgGv@<}45"yW|64)6 Y޸g7׿ X/uQeDdQ-|OV+Ûn;NmllJQ择#Z=ٟ|ۅDN22TP"bO˃gO?/d6Ey.w7 tK,ٹso?+o ]Rg.IL)v1<5]hl>`Y Z5T],b)6i4PDH[KbƢڦԙEػS[i-IJȀ$L QM RH%.rGqz_̦vSO5z<hiJז@e)F80MM"N] dmt6b"fbbRy' Yffu]{|Q#, 4 CHDԉ ϝZLbF.JPxƩ/߸qv/dE`gK/EH,Mmbj*T5kȲ^Y#LE-#f@{d(.-<{l9@cfcMg[3=i\6("e/ˋ r\BpK&RT}!&몮je0I"1R }I97>=ZG;K .\=;R+z<WVNm>;^/}Ȯ~~>"sbrw>}]`Tٱ!!E(i#)BLM!8GO}f{{i[BT! +W^~U&䙑HDϫM"Ϯ_6VWs" Y~V/>Սgϫ"Tdv4(YTDJ+<拦"dv,uIE^Y>S<#u"Q$4Ήbl ϳ.K 󎝴 ( r%231DX ݿpg<!4ѳ'hi><xz1m$=1buƣs/]y7??E0RL)%EdeB{xKKKUUshڸ4ZھO?{Ė;:$^RJq6OC~m< `щs;Ԅ3B@~%ڦL@c L y^z)&M0lr" _`&t|tdY1J5fgyBϧ7WS۴,2"_>"X^s츃T4z#BSפgD2dslrh*I@46M]-$s06Om wg^| >T/`ׯΏ˾IaTiR=JbZjVUb۰QյuH<nݺtpz+?e/~6͞=}vFWzs>(7Nm5M@J! `HFy>`K/Ti鳣 b6+E6#Bˈ?[_}DȢhwhj1|/pyXП" ѳS[[[͠,e>OޙNօHUu]ETnjK $)1SQڶ 9b@F.PAiJ)pV@@"{p7TPم>@ gLZ\pP͛{W WΒ Ypξ! E-fUַNf0I']Ŷ s=DYfk;OmmEI{ub炘 $KQQUnj֍.kU0xU~.:Ř ID"*.ƙ4@fjFڥV:P,Tm-jHa0"SS7Qco[.HJSQA_%I]KӀB[c'.8vHse9#!QRb":&p~Ku5?!e1LbPt}}}2=kt:=,ϝe. A:zbT(֋ԶcTM;Uެ\_y3=OΝ;3KSˏ?!>cR`d48ѣZ|ݻ>|xյ5dԌte.?=7 i}s˳/qsΘo}ccemi IDh(e^\zM^}ض6,mmoWK4~g7onNfIzB;O>䳵gv٠HF I.^n urtyGBF,}dcs#"dާ6s;I K1qgӤ JU/]yij09ё C">2;eDKҒWϦ{?v!W^]m ]( 0O{? rikPM`A ؁v9Dif7n( 5.&Lj]&~9MR3[o~p̛Nia2#eQP]/hsTƺˬdC%9Y*$[Ip(v~<_8PKjɤoRZ4b= !:D\]+8d4m&qE9cjg6VEm&mVUQR&yy- |^ffc@4dEN< PO&M+T^MFqs+++ң;U]/>O*փͳ)%c*gY4',X<̫kc $$`Dv!cm,}`&jcdDYRDB`$TD%ź _2Д$6#G mjSJH$W^Ίcә6Q  HB%?oG;;gn޾|u}6靝G ĦdY3DE:,#@+u-ϟOQEc"HHAo4&?7ͧ"MݶFƎ!#2qEC`DTMV P2ڦY,,hp |sn{{k8*G/JG5 ؓ>ܹ{ͧG]cԮo޸~;w !s<`w|҅^@cfSʋヽ3go_|{wnݽw'Uxcy}y Jww?䧫ƽ޳E=/mKgΜ}. vb88'0sE(>h)FP% S9vMx< 2{ۺ~񹳗VW+h6L0 M HΩv̹h"][F&bSsB78::l* uuIpĖϳ^7T$8 1#*;]("yT ͘H(tDfjqf3&'1AO>s_ncs U5 V7Ϝ΋?|i/BVٵ/>vR5fU].-:|zͳg G.ȐuIǣ\71z%))`8$j1oVUP;ͭj<RI`r2$O{4ebE̴SUU!^Q:Ķ u}CU0 β;wx/).~;)c; c2- @w^RSgϵmi&$~dޠ d D(ӽs/{EŪ!.K)" ;h;&Z#"R$7C7Gp>imnS' 3d%lu";{]q,5KX^ʢtmvlf*^&kFz& #kȘ f"16~-fRG6zt ڍ d |kå.o?xx4 ~\/p}{D6omo+goޫŕ^}G7_+y:&O&8l2\Y卞?wlz@LmRviv? m3_Ͽh=A;EӤ-&HZG֬7OƛKG;~vvw?9:ſWpyy$2 WGG !9QUC9@T= GEȧbxM<}]lR=p:t1QbQ#$45 2G)Ԭu` &D. Ŭ -dDɣmz BFBSU&""9e>luihrL$3G@L*JDH'x&Jh@( O})D3hZD/o#DT(L'NjA|lY`O}"09U]~?O~WbHU=;P$` MYYMeQE1y7\]_Yh%-㭼\^O[FEїvd*"=3 SjNaB2\R1d2#bÐ~^Iyvф*Nw"q\l8kf8\IT9@0[PR?c }YZZ<}ƛʸ],Ң%B:\D bD*QM۪>;`PUug)򢩪eLjBif0r"&ǖx cYpr|4A`ps;ݼʫ$u]ߺy:)ǶOw>BCkR: d*$iE 2FcBR/gPQϮMͫ/|4;^^^>:>|+o~+s/<]qtx~e}뗞pƝWڛG^pξmnn=Od1%A1箍z H $5lL1T*I&H^;Y{=VE ސةEhۄ4}Izen$>zܥ9{7=jo37=TMYyJ[͝Mzy==~R{pt/\bH@B@Q&xp/p_0ex߿J]ln7ϧz2;lcgˣ"d^84!${pƗ}vS/wN$ú @+:88ι2LjU-05IR¦mG`%U#wtvyp IEu1h&c>x4dq.bcpny֓J, gp@M<m3`Pɋn8 . M, 4BcHQq"U$qI=Bd<Pwvj~NQK3I$6y䞊oys3sufBdl>I424hT$鴊y_r~e3Q |ѵ#gdy uZY;<̜غ@-5UxݴR-osUc5KѠC;BEl ֑sf<:j*rd.ہU"GP "Tigf67cY{Ύ:Ӽh}X}#;;{Q)6F|a6h8IkG_ܹO֓d< @.[0g66Mcp`V+_E:|56N_Ph<9 v=3;;$36$êeΓ:ĽdJ$Tm.F3DuUO4+ B+pI[fE=$8B#36M,ú} g?DA$8DDM9"L&*}YUn &D&Wi$IB.)Y(H:*' Ljuwtu9sdp8tolʋ_h~uZu('`qiv}m%(*#%V*Moiλ_|7~^{Wtgx5Iq0x9 fmeŠrvWx{݃^g 'Μ9}챓dzVTYD42BDD fBYs}= $e%SN `RP ڝb0<npngeez9)Ņv}Hzr8kkksKKN$Yqd{o{/<ٻkòr6]X\/,. ww>>8ڽ'IvUqc H$s{ŗ_9vKǟ|c\;^jq~CWߵKN?6=~ww"m{S,/߼~0p%"R~*=*ZSO2j2%ƀh$6٧-~aލn{x0iS>KEl{qIֵU06((\i6&T,*v+Kpgڕ<ʻŅ{ v?~rV7DNQ$Ib1(gܳ2rdN΀($mE5p$E3!BuL$r$IC c_UcCǣIՁPdd/2iSpH[)$*ϡ:qh8Vr&DY3N\q4M&eEh%b=plS[ˬ)Ǔ:I d[̙mn& eY,K;y2XVդXb4&:@9"(8ӨbIs!Faa[k%$ƪJZK&i "X%T`1ZuBDd&Y*G_թOli'OP|Iջp,I\w~\tgoRV0@ցhoώfHS[Gy7_yu~pGƙ&z3.|}G:)eib][[2Er̉兙!)2 }U`=$2@Jcb1!֕/ڇԶ1LH,J6?`HĈC̋T@D6,94Iu`x\ݹ~m<*_oK0|w/EptXCpvęL+8cU9f S-~g~n@E`)E3Y4ͶEB$,ֹ,ae0:&Fu=Ң,P|]sjl)1i^n/+r6Rl$nADDf@H\HbL\D#+0jjfpD1F$ƗUJo$I4PubeNyyP&Y^,o-Ь,+WΞ=i&skU5&ipqX_je-=!D0D6J @Hv N_:ywnݼ|N''W7֏mnir{4d'Yk *µYPH "fuA"! ", 5#Ӹ 3= 7CKVFED%EV$r`fAe*0#T~[ Aw<^_^,(ɌgTuj"-YZkf$!?V]_ j=8bXXX|_O"@!cӃ/~U9ߝ <ؔC p2I8ĺO:woΟ=~؇E_vmugׯ_?ww~v4Oḏ:R_ӧDE ZU"6 ?~R"7dYXmR%=$1ÃD]R8488tvQGgXЀ-⫫/}x<N-)'"3Z&4p`)r|xW6';K+>[`eݞO?ಃ޹{bEbS%*4p]վ kq<>!C10,,XQ'.LJIɲ"9r.U@(ٙP^|sk)QQk,16+g{u?tys[W^=}/߻u~7?7fg9@FAXȚ\۳_=~ަ<(}oi/{SO_[|B;3pbIձD bu3vv}g_zX+Ҵ=DaȜu32%TP3*&H \֣VFK2kMno|UNK?ۛ[;r~gf]\{p*Φp4)0y,I-C^,mNɱ v@TCFL0M)Q0ɲ4MQ&5$bUIH P74q!iYa 2$U!XEU%t2z9ߢ"I`C cHD8(6DPCh !@9վ:G$I>`fnso|yWuV7VYl 0 DBPUmuoI//,.5kM\Nh֖ϾK3D, {}U7-eOdt %* uX@ QEEDT$$* 85q (h%qV8;i](lH}W;wZevz{o7~ٙݭ9  5"A/.wعS@f\ֈ`d"@yڪK "kXrIk RFE4*uW 2O?~w_x?9ʩsgΝ;}׮/9ccbp,bpB Ĺa,2WṠ34C ǓIDQUj_+PsTw'Ol>H(?uw7_P[,/,rlV/6o\˪.}}lM:=8j205\jE8a&Ū^g{;{O8Ym(5")y;KEi1Hjdium"X4qQF(YUs5G@ Y]J9P >đUY8vlaa~RQ$}t9S L&unaZǑC^!"B@UR!N>o|?3o.i vlmgz3sN;-CI zf]g:"K1 e࣯j_b|MD F997:"UF<劵 ܹ{ۻAaf~PDNp4/< ,!ǣ?|gZF#!r i߿w~wo~33"V('5AA̳)#UDATnlۋK+!zDB4HH23ZDӔʡA4bfյ`r}u.cqYݼqέY~SYVݺؐDXT4ܭ7wwpɁ4=H !iq̹^[ &i!hd1dXE<}IeV+GOV?8~5t۹U!X2œvyi>\H\[h3gi87 %F"B2Q9+J\Ckƻ;IUT}_}x`$iﷲĩxJy Da&棨R2>:R*c"RcGR&ЗA¡$yeNi*e'u T2CyUm>e1=sQ+P "EcIx_ ,k_[CęWi x4&$ƸVM$ 1NUᤜ(D 1{"Y{✱Xg?{,?S|o&lno=n^8Ƒ,jBu[ I8)5fRUޑ'8ѷ揝.˲b 6QA203 M`H`EAK>5AIht DAP 0pm8uJh46$IW=|Y~ŗ_@>A| ٕ(u{wg{ۻ%M X.2`UkIDATjCBcGf&죏}^o`w-/.'du"[2ef=?۹w/2bLjŨ(*({ O{,uzGֳnkuu%Mݽ{wv"lvz|pEeJI:s3IMʑ%ө6zDc-$G8GD$%AMKcSe}Ο=wؙ݃4o}YkL'o@uAcd (GbJMHTBgk#Aq֍#k]$n=w+ٙ 緟>߿__z1oj!28$봫 Ο?|[_9ljaQ۱&wvzoJ/Ūl|ˋ'ϜncZEYWH :yosÃxl?} E7 TEgǻ4k%&ڭlPh8X>rW7/SϫD$ tQ0h5hlgxgW.]R'#"?rԋ_|x<󩊊C@…ukW^z啓OݻsSxf`%L6[[>:cSyJQ5EQ0va .Q¹Χ2,ݬk+GV#(4'T<<b8Փ13ZIa=A+Zت&P1ZТu[{X2%9FC 1J,b%XW! K,1 79 sfp'uVUaC AgZǗfڽ'|tؑW-gf<><O};?ןa@& ujd0@Fe;^p9ቓǍ11M M} +u`,p(K }",`E( K,Al^д $NK4DJ.:*C9j_o)rUM6N_]lo݅?y4x9y J\e>q7'¸oo?wdhU*y}%XU{! C)Ī^Z[_XO>iw{_FTVo|tDT!pIYwJt\UuU0i[mun{w_mXNB 2m-4"BW*괜MCXi4ƘYc\YٕEz]>v"ϳXx1T8bs0A|e7g>N&P>ӛ_Y:zbɣK K^ )N&POrluG ut{Ϟ9zbcmg;vb$N'9JT=KIgVSg=xgWh=B׉Jqd<8vU9.XWݽ7oοpVVA腕9@C_X88<'OIęAQNW Y!2MFlfΈN8tpׯ_rmskʉ[NދL]Ԣ%B fY-¦i!4\Bc9DDHo޸z;<_{΃ ϟ>snuKOv?72k&Z'ΜWݽGlۼ_^0L.>䣢[\\ m a\RDc Dn'Gk%wx֞6?a|ֽ<~3s*j~iem}=u>K}I L kj˟*Z/>7oqR񵯮}UUmm>y.\|d7_'K=(6I{3=danvV/߫f#gN?sN޿}cN (Rxϱ"mdm,!uPjѠ%PC Ḟ_;y32k~<1dQBsև14 DRUB Ijg$04*,ijl87gC(%Vy궞zOih2ZZ_:v 4Mڝgϭ:)hG(.7/O,M\t$Cck$4_mSacNmszpr?kw\`㇢* YnJL@oEՂQh̤DUPB Hd@LsBej"Ȋpcov>D4ƒ!ek\a!Kk_]9֎;O{?N?wW_ C`^~e2<nm&4W$;\{KǺ٭xhʉ֥V2[o[YbUa%C* bO 3äI * &y;Opvme_<~p;rGr4=}%͝mLgam~i5 |Ԝvo{fů[q=幍/>{ڑ6eٷ~f<*% F /G[A~x~O2PhL/O̅Ώe9qdAUJxc $iPZ2 ʆ Ddc4Vfq*i戔9h^ Rh,* E@Y#Z@3 )N 8tY4F$Lm:88BZ&I<~$·jo4Y_"C30MAI-P8)DQ# dmΰJhgFt0*Ǭ 6!UٟI\k%DDC5 c-ZS? 13*cDRșihH""Cd}D8q̉YC>62s ΠKg|nas3sIg]j"%#BA CB*_#3 F0DDfl 2;Gy+Q($.Ě15Gu)T7j >,1JT??xlxo> `o/*˲$qIE;ϓv+_\Zt{a֤<k,,.୥jse9}EL\`8 87,/o=rgO-,d.Z8&YfyG~~S'fzH*DGQQNCKM*8,͎=r/߿bh1Sm4 ބӶ 0"6U5D ḨkX8;5**Z4qdpHS#c"!abEf:vÁHSpsmFjS΂E*R Rn'KR$fYeHsܚ^[[ۛ[K+kGMNsnA; Ͻ}*ч"m9qǛO^,ݸG#ycwf *K"X|9yƓͭE`FTgmk8 Kl( _6$v嵥~+ѥGZѸZY?b,?~pY7 SGXOgyG6<7_P<8>ΒNhgٸ,o{r紮H-Nµ\bwp0!;D=m|s5NuR^Ӣ Y50W!'쯜5LD@& $!ԡn@6qMDMY{eyvIAj:D2Ut &| b &*XH"{B:c$ Ƙ}S*9"Zq6)2I4Egt"9DcY(5FE20Ģ}`$^x]t:F(͒]Jc+d`wۃ=~zQǣP-H_ܻvh(,X\4Cdpc4P8k,jd6|#u}!FD|}%v]Ln'y3Pϝ_>ztROnN2\ʭQUqbU[E΍qMʴFvћ7ܺqcaann~AD *u:}v@bl6y鉭1|Ƶifvf4_yKE?qb /<}^}m穨(BhTҏǛ[ǎƥ5ƃ% $ l#&'4"͟"*%E* ݧcF-H \,I,`oOw޻yk{k{ŗ^ɳs 3-׿yrg-3@׾7_?~,Ξ:uaigv}3V}g!s4kI~_ϗ<%D $ 0 amdC$YV B@Dͭyqy.wo\uNwfhD?73:v"rw{/޸у'ڏxW_޼uMRN8Udٝww{=Ϯ][Ϯ;/IARTW$hL@-'ƩX!@pڼ0TDB!Z\:GU窮D#!Y#`oo$k_pphY[\Z]\Z:2.;;<$v[~WMfg=nDX9 |ҙ~`c'IM6p!F5 ZRg柉QB#7ƴjIӺ|lsxd""61/T V(M^T,h<W_{πs&HQ FLQ H`Af&k0 [QjOvpp6뭟8wp03[]v >\}kV;'% )lzU(hHsF&hSrc2Gc$la1Ps4!(DH^/Vq9~$ϿwBhn;.'̋Dҟǿ^.W^W_"Jp<5br7|v/ƚ}Obmuvܺ}5Sf 9JGujl&DZ[]xo~*f#:Fޙӧ?`xdcʃI7{Wovfr(mO_KX"W/o UH!Fiwn ȨJ=!hRI @dHhU) qfjJvN|~츳ȾZBv"9=J0uի2+Κ??"%:ԖLcƤI (24)Q_>PKl 9B4TcU<*[g,YY]>~ϟ~镥Y`cР2={!(r(/Uj}f,$S[&0T"Q$0GcE&UU"q2)kCdL4uIⲢ*iY CQiw9ݷv7y^Vc[E$0! g8Kw?G0nk~/\gDC8)3d;y1YÇΜ=di;/5Zgv=(3 )J"*d1.1࣯}UF8%$FX9Jlvf.I;>;ӟCęN*YUJ0 !U&mO߀P*IƱf׮\ufL\Y iD;M'85VQA j+ˋo^S';[{O!6 bh:-NʣǏ..UCH dRyj,*P%lȀ "&lXTOQв*XVIlXO>[[k6겚v{݅Y/, ~k PD,W:׏l,-/ Cvm7OZ7\~8UO{V"DOf@@Kh./1BHhwU5z̩UD#* !G^ݾS/< K_= o$ZS+D^ o0llY,񣃃轳vaa>O-'POtZ@"#D:^57 U! Q 1[o?y{cLS9$2 ʀQAoͳh&1Z7o;.M kbwww+8q+ZdxQI3g ;DnadӚUs6wc8 (4}NᲬ 3Y-%I04*KHQi"Ez!P^=ztfn6 1($I>T^FXXZXD֒1G?qeITuMcvP1d1V1OA%J0ƣAr6)RXYSllb53779s̙S?v|}eyvq'.JUo4G`%4DJ:ԡjn46ũ&>$W8CcMܢ&h4)jwv(M!׵gW‘.jP}pwћ~?{lҒ_XX#¤e{ܚ3ƾo|/_9[:vL$`tʵ^[E !@;1a@ ` k#kH8͉ϠA"k#(V0On5Ƣ0[c4WOnwz&6@it\4(+뇇7]zgy6MSCDXT4fa2q:KvfSN7|:Gl?{sVOL,9q\eTɺtnq+ 0;C(5hDw<-̿+Kїׯ^_Z\N&p"^*I*Hs@Mt& #"[20Kflg3 ׯ\SO^ݽ۵_ڿkͥ>If y",Z`0ڛFj GPEJi.qd7<7^7;sxk% L/Jd6lZ$&K)I wW/z+pDf)Ǔ8o~{nW'JΣE Uu;ç_߯ѥO^{e yQ0Kd/Y53Vx &;w訮0rmn+OuW}2l20$Yy~fVil6*f&`Y$ C`(6{/2,a^BBl &(1:FLZy`Nowvi4Ҥ= )H LjĖa,0(|H*92(4@l-#,@#*rUMT C'<7ieEbӝC =r`e,QL3CSD|no&μX꺊˭rlb/xJ:qb mn>. P^|kޏGaVh_Y\]ku{E8GxOhī5p Aʓq@Š<=Dm>(| !&Ƹ2K _[^/A#hhS1@(2)y޾uWoٙB46#>FmL \j?)d`{m?O݃{O> T VVkɥ` &@%aODi(r*.P sdƑK41lHHD! P9 EE $CyC+ͤY-Q0Bh]E ʅͻw>~_|%V׏26h}rӧz3r$gKYC`\t~;׮NnnRy>9ܿvp0aT>⋷_}w7CY0TY`ОYZt #T눪Γ,j 1bQ*sk?n/wo~/._^+\O~\ yaUT06IĢ@ Qi:8Em "BRIT궊_] |mimՇ6#UPT8MSYdZWO}yVhR cȨ9Ob9'n"A#mVїY ݺvk˫+Μ Μ'=!5d*AS/k| >;,ܻ-VIgsڽ ]oMpZƚ "{Blp  B 4F@Cڬcj[5q04cB3Ymڝ9JlƂQ@& Yj\H1p8acY!C *4(+*Ezb 1FVQ4W@( Ԛ-ݻ{>9D4-kL䨀1r`e9GCIt:_jwn?wxJ fx(lQ  1R E̬I"l@)$`ghC$M_EtJ扆h= ODW^('߽~oyk-y۝9N&út 4Q$gҔb*bX몺BC9%ZY EUu PYDu ιٙNE(iByY"чD"4}Q&A,c A+420U(7D' dPG) @ٓg%Rc1.46G(ye~֘,Ϛd@dDbD26w52fᆷf(*KPh~M3۩}50m RO8m9{nxxgx4Ud(wNW_c1ڤa!$~ hȲ42'vǏ.!k}[K+,Rm=yǏoݼst.{//6m.5{>5:".:&idKWv5VuC54!a'>^7_'?=r䗗;DD?lu,XP{V)HlZH͊k"zVpȰ*橕P}ɥ{wO}kGV$@`,t@!YTMuy*h[W޽y{̻&*A F $)Um?c3KK73!HVn% ?YY<{ṯHa<QAE .+iB5yTñs-LSf?"UTȁE&S 2J LF'V j02* όֺqdIqko uƤI."Fh$Ys3~&.uՀBdxROPHQUՉ%T1 PfQ4Q i%YEAQ,UffiFqIXяLF^XXP!D$N GfN^x_qSLhh,ae)@E$61M]Cd3QG0(E]}˙ٹWwڅ?+񧾮E;0^+/LE$ C /ѭG}R%^I[ŷ=c!7炀W\?}|}eY|t^:r"wO>8IO?Eΐqd,Vw7rc  )aaӺJrRh*u#W !pL⫯]꫟^yk샇fRPWF@p&Dn@.*H JC4f5E4d#gNM4=zra4g %AAU@CZ4DY[_]ׇAcwIpV֝%1HbT%hRK8jш0B.g?|{{3n?|Kg/zCi3;;{K`\Vd*"@ .7)p`$eڲyJ <ei( %Ch@ 9y@B@ƒD iH1D ƀ1IjVLiUy)!꫉EcD4.6H;X (\Ek$RU1ԅ;uBTUP8Tǣz2Z3xL&I~?[C?h+HE{<u\QS9C=p.X<(2Tb}>0 84'|dݵOݻ/ʃG+G6֗UݝEf +9~2XY@XU!.ypǽ|pIejF;n{PkG(8ìFsUUYUUkaAUcYh b(2᪠4$%M,$D|82dsjIa xQ$$gɤFCDXTSsʑ%C"G V"CVDKTP2 (  5&- *̪ IHA`M0D`XD,H(@vi5nBj^?OSNY)4%"h !<~w:?V.;E'[.7Ǐƣťoko<{ gO=wcrXG^'zb)ͷgߝ[[֟L` 5+W$'w˧ϝ_XGȬI+!P4.g&ycU8LǭV"[vڙǜ3XL$Ʋ@ Zѵ_G5-ZjrA v 3pLQrdQ܂uY)JPhFLy06:Ӻ>w o'Ǔ0 {_Cjg&{,q2*u]+lR"i3 *u9v1u4-JNӴC dMA+ ŐuͰI#+pXZ[>{1€>FEƀ&q@EHC_OveY&{u_F̈YYYrduf$hƿ6m8F16MDVj!5Z_}{->"~9{<0[ѳw,2"&@}Q\a 2xP"d`&kg!4keGfg4*RAf(}!)E㜣@3aYȃBTJ_Z P1Kn} 4:[J<DxeLq/&Z:R*CO&nu>z>vzkaq~L)/?ۭX[;SQ>X +&f%QiMOwڝ6s1 iFo=3*cWo+ҒQE"4(g`Kx}8PJa]\J=Equx_Ƅ&f"N@XQ#f dYFUuk4(bd 0{[d@ޟDۡr T fU΋TARJH<feQYK8Kdxy8ͦgf$~L^2if^Z#g>4/h4;ݮ 0?yt^_65(ڠHe6y@VqӒfQ(z4ם95?z3•ъ{LJ~rii 1%xv_atxœqI Sn~OΜYXZQe->w6B!rTFy$ _ LU:Wp a6HH)'Qsgg|WKZ^X(@cBQo>~b.* .3sQ8:E:Qӥ .{AP!xO'E zDvy+<(M-'6Ŏ\Amzz/\ߚq܍>Э5@%"=65 BUo`Kkqi@<TA e4xÝZ@eb)T :ٿxFB,]`1& lvkevNU*H ",bi+TC "$z (6yUXꬹ@ C'g: TIMILwzgoVH+ByV`2hl6-P 2xTV&VD&l"nZElq`BS$LB_7EtR u~;7{E F盍nw s3.]裏vַ/bPK;(,P? ܏T{0* 6 Mi% E0rp0zQ/'B fwffy5"cb"]7@ΖLJϞwϟ?j6 bi=֍Kb`a sQ:PVQi&P YHyo4S&u52 P)$b/zkBkH;vNJ PUW=lc{RA _ETQUC'8ٳuޕ*5VUC&$ RbO X>EP*.TEMZ{ETkoͭp3 x;vzYݨ3ͲL+ͮmZs @y:P7駟Fҙ|n8ǵvW$<{|>up4ͯ.atpx7k& (P'돾8<@pd jtJޚ>?R0qβwZh8ȋ <CZh0>vIet?< \5 :[Lշz=5^8?t)reNΒgf;ja ddLڵ׻ ]沜 Wg5 `A2&xOq:I(rڳ3sp;xV}lb ;ԻF%qV"g J;q/G#G^% T7peŸg/8|=)˟w;ٟ')AT AntigzSem2YLY 2<@@Q23Ӛr@Q`-{BT}E)h"d8ݝu gAvTUt {z=R҉гGPZbxthW^^\^-W/Z2X B:_xoi.b9"H ){s^ZFHxft'ieJZØP@4ֲaaژXgRȍGi1L logwE~7?g^s "QzRGG8n:Ǘ.\D|&Y&I"Hx8x_;tͤ5"g(DCZ8םC糬ۻ۽0Kg///{o +Z{j̕QB*t)R)R(&ӘݬijIZk4PU8Ϩ'h{R ̜"U8BƃŝՅ^|ne\4EK kY#[B2`K@1"(`pg<&[ 3vCfM Jl4 #hؤ/Gqɷwy9eݭmǢIu\A FW@k& E(P d>O陹RSHJ'Qe|rWۙ5&&Bɂ[pB`F!U\֎yzl0'*{W( _BBqUG/ *B 2" l0x1iҬ4T]YhȁU@”T8%J)0y"$B1]ct`!yqqz\NtRD}IH;[݅SN \4ĉ?AgcyNz 'Z-jDmr|tTjIWz4q** dWy:ɢ0hFã6P*^0/+׻ }/xM>Mu.W2p%Ȭ( f합DJo&aryQMu.2ão~|痗w)< 0R+@QUJ;D eN ,yYYTwq-|8OF *{WY6 ژZaaPW"fA| ΋RD+PI KF/#Lj@$\`<͸tZU+$E$Z+PF H%[0D$m4r΃$"!GI>P7kw|K/^ܹ}0+֧OSt^n=OS7vΜy[xsgkqey4:/* z#C۟nyFYG`;t~ffƘj:OAyL<{Gp8~lG~5v77̯,=}?Ԁ_zin~D0N\Y^z$zWV^^op3Z=$6ϵ6(-ܹ8+/m(ٗ4;$ +'$ޣ0c BcF;r892 M@JI2<_:bx+67>BN. z<dRN)"Pu0Og[(͆c]>[{ި_z93&p0#N1:.0_TFfٕ%zsә^Z\(r?:3"))0Z!;OpŴ crq̬BVLwݻG7Vw^~Z#DyM FdWAE !^ ox4Yl6!wPgnNwx&6QԞiA x _OA3kmW`"TOVPGyH*FޗyQP*X+JLs~ t<ъ"BU"/qX}*Vcx~px^jq2v٩SJx6 ݙ>džL`l#v=4%cf^tt@g }QkΟZ^;|p~o~$ DŕFA*"+{;'"=ٺ35Y_vw;&yD'Ƅ:@ ŕ;"\OJv^DŶ߹{Wފ$@E8kMrP$<%*&b/ ߑ:ߪ3 UDd#kӨtc&*H,BdA U "̕֏PQ'Rѯ~^ob/o/~`92&(WW_͆2+6> '_yŕY>B_LO/-.KgWzN{fffBQl7$@E׌L/ϭkm D(URJARNչpkˢȧf/]lcwD+/0~ڳKQ5?{hׯ t2QA`(dE`j{ܪRz<αhHF*8B,mYTaИ–r$Jb5J)[}?r?TWfJ7gx̃/N:Û7?Z+SJ"Ө5tiDp8嚤Pt[A ncaZ?ia#q?Q9uE1Z|孯]|7w6>>ûspo_ 0#(gQ) ,I)(^/O3 LjKZ@@IbhR Ia(8y^PGa* h.̎=-!P`8R<⩩E&YӐBTAEI*8/o޺Fo\" bg"Ukŝ.a08*Y r3BmN@<͏]Q"TZyy^ s❵ t h<bӍ4iA*`Ah޺@HpfyD*u&_U qQ&(?GxccA/-ݽPl?ߘNu4̩{[{W? S4c? kZH CP'Y^[鏎^k{ǽ.)"3ʢ4W_fU~PUVPqOjyOܽy닏?]\BP^9N{m tYy"C`@7F&Pʟ|00 ޚ(Ns[o!@)bUuA{&F9勠@"wCU"TJWo ,z`x_س/](_!~GOonFnԌQH,;'lP+Ȟsk={ԪXC>bFHiff{Qะkקff;^;=zh}ӛw7ַ|޿@KO 6wsK M^g:ݩn'IEOn[MQdIyԤ҅ED4:iÉh]=xp|~Fsh<33MZ+n3gfK+cryJ+,21Z#El%fDJ%8ks 3(Qg0@J gϟAXת&EZcmNY7vkvnz~~H(Zt&ҁP`wp}V,$>OP #:ﻳv{p0Iө:3Lw[!;Mnᗁs6_y"KC"Kw71AJ)x!}'?{Ff45MH]Bo]^dDdˬ,LC7`gGGq{anez"UfIΣEDB$'!ngGӿ?^q-Fh llV {[ZMbMct7>_;oN%ԨMX)@@m5DU{(}9 K- b5 )&Ż7oFҒkťgo߼szwϞom>|0 r[x+Kg̹Vq^a9(Q3 AWNϞY+_7+5⭏9-YM>gΜK/\2?7ؔRZ!jcF)w:t]~?&NbƄI*f&grgj+s>0ʦa Q4Z 4Ғ`fH1[k24 tR$i+LwZkC1fPk  ( IN КNΛlnՂA$N:v@  P$+"jrP+,0R^XiǵF6U[QIh/8;%M\Eϟm|z!"ғg Z)V/FK_Lh#x$&4 !#=UUr YHP!Rnٟ:{v6@lH^UXF95;àǙ+-8˲I~QY^z-OmwfVVϴ$fC"d<)00ˋpBxRaԫ!HKPE;I$>_|/ϊLHi}3gܨ})v=zz•Wv}э^}ޝ)umQFX in'Y94JOHD5Un_D``D" H%1.EjABc bG&8f8{l AT ӌ`2oIcff|Q3 +ҡ ={`ÍOGy)y&mAS}Xq6qX8 :EP8v;2)ޙzBjg:ͦфرqVdmOOuyݻ}~=nᓧY`(j)"m4cA AX !N"i4/,k *TVre;ZA#+4d'?gEɲAѨQ>nU#]Y:-hQ|`fUY<22U(ʲnznAo(-kFgfNU%cfzGx\NREwމ m$?9L4ʹ1U''R=*B6T%e/֖R%Mu3,w߾V?88`bwfٞ9;JTVq@蝰wYu4Q"_[_x?ŅFG ˋֽG=Z{oQQ/'o֭P6/\}W_և*,s;j{)0!0TgT(&ҁATrў];cF.RVc/ C8AiPEec}c)❻/14vq(us3S3ssKf@h Sԧ|lg/?~fӓt$\RZ{$UxAPD Tb/IU (i?Ǐo}Quhh-[Vg^k0@BlBA> zMIǃ$O,փh`T&b=`hqAwiyoSʂd2ɳ,ko+׶ݻwוZ?/;cgQz gZ] "fYI>Iz5vPP)@EdzP/^ژ`lZExwtԞ )#2"y|D #a7 0 IuEi(atuњ*j;>c/ٛoo,-AkM>[7+d:i]fӧկYDÉN*JErP!(/l6IQZsva%Ja JāɳIwZגf)w7wGQbD& VB*뵤h ,OyoF$!djX<@UDU9Wokx4VJ7յӧ/qbhmK(k҈Ȼ,ũSϬ|WA|xC;مųυQxͳδ:(y_XN"'IhΒЄEQآFYjK -Tf8o !B)hx0P+gE&2x4koDA8 DX 4jQh17yۃ|Gh2DA't\Fx8`VΞ 9Gp2F[HN UH1wH V DWcJ&@g]: 5a s,(Eۻϗ/7~w흃>yoݍ8ػ T .0hx(4iP# Nw<{t̩n=ң^J${^LxksO>,lhfv!gA,ba-L0={QZD^F}GPQ1IFk"Rhc*UVSm Ajƭ[_^ތ[w>daqY$q@IhѰw82eiso [חOݻwogwyqQ tydBbؼztvg jʲQQA HAqq7ODI2-aBgg gz# jF$qn3|r7o)"-- c +lIG%sl"slQ*aeQPj֟l[33͢Z#rhB SVQܿ{ԬNk+k3Ҍ-eYF#m8 FgBш3gϜs1@^|44,RA"A"uGHt"Q0{҆Ui~ *4E*9: (j(dN&c/B &]9MKryFN-%/^V$ ivfgΞz5~wm?]@ 6'&|7ygvn~2!P3?/~W7G߉ &⑨;/C *,,x‚Tk%Dvɝwb8n18t)v8F'ij5Ff4+ ٣Gdc -`jQ$bzJγ'ժOG<s2эf7"x+KϞ&RE [ goG`Wݝ[7oэg5jBetkDƳGR{:@3 BEf.-FGw;Ӯm¥S{[-$X `X'V Qx?LKkk\D Ppҹ:"D,-K=QfʋC]4dڠ"yJ97(E>sN/HdqVV'^" `c% GƘ ْgyQ Beƣh0z-Gd<ɇlg+V%RN\m:h`KWq]"Q,"L(;eLLMd4SOD$fO2 :6ѧ~skt.d`p|]tŗ?yZz&(V\a`Y:$ݹyѽo6Fњ7?ٚ%M-Y wֻ08HI!`zzH,ՀY<#@0{ 8Zj RO2 d2vz;ӉQd۷?D5Nm߻Uvzfo~ ꮭl,hLevi.PicJ1"$./B  X$, 3{ZqXQ!PERʝ9(6"NRyAH8:fSKyQ3y0"qAk^hIl4[z믾~<#2&)g>CUI32ZZY}k~;|h7yfűI";-G>;%+vl9<{xL-P@T=( hLM ǥ҈a߯[sA:[mjO6^JwvjpկxiiOWQQ{d,ك<(f{~>o}Րf a%R{vud(]!`IDATQ"+$)}ޣ[nmldSQ{tJX4TьkIQ=R/ptx|stp4t 2! ygpũQq* ]@rpDxݙhcR+ҦR('7)[~9PF/~鷿{n߻}{^L͸^>xF8}XI.-4M/"BԢ/u3eV0sG"'Is #`c= _V*[iU-J=3iCh<s6=Y^\~R9#  zjNjQ.mKW_8眽փjQ EWO QNc)B2lC"w'Q6J&ԗh`xOoݼvKK^2 AXD,/]Eu"C`w<>|kM flȋTqTYIr{{Ν9ܟ:?PֺgmQ;F!a{ ;kNl*%ϊ"+*J]RANԕ)3+4ˆN7 !*"QĈQڋ8"+{!caȈQX"dFQ,TRc< xP{C= &jTtaƫg/@YtY6=!R.m:eg@ ~wnom̞{bGFb4?{~|p|zLg;7xmQjekiQ( &Go?e9D fT\Zy֋Mlh>o=igCDC!jU DFАF8jTy٧eџR1{w?˗S-qH(udш@{ mZlQֽ/n}];v|s (a̖ꙵE{E'9Qޝn,8=7=wwFQ$Q\Z$9:9릦:USÖ9IڝtdVNwi.+(RGG/h[&=)T#MVt#0Jqy&V21ieiF, fc}[ P=LjrvN_'g$+3{ЊNOvTe,(I-9>>>_z( '[M;X"׀\x@,)M_jT&*&^& Z1U19_re+@RN$0Y?}({|&?X^ZSwܞjY[yy<qgx88E&1ԻK++FUS޹~_ɵ_d;&ѱ1fet_S{Q=~|AnP45S"a/f?dw3Ϝ&$/[#Fӫ04-ŋŅ2(D@D2) )mAZu @Zk` 4&xoy8;* 9 I)jÝdxǓбx&Z! $-s: n)"AŨ A(,yةFs_Xe_nm=v.tfVfYv}p΋B5Up4m'Z@LIM8YX\^^=uozFE!x 䊢ь'vlu+m{~t[PahhyuY֨w$ts&ZH޺A6@4*NB)u%*r^HX m<&V/W (0( &aK[BOӬVkz#V&LZ}EHlbԈ{aѤ/$"y4UOJ"H(&'Ҋ{AM-I~Wo{"2ډyPD &+$:"]a2VK"p$UkWiƕSǽ(WOVXKW._hy}j& ( x~uj$IlHiMFϜ\_;{ч?V V{tߺ}t*B|n~g}6;s˳S?Fwzzvn7F_|~W^mJV XD< Jk+ܾ=[߫bMN3WO/-ugLDZ nEԺF/^z3ɋQOd:+x{{ sN(T/,Ӂ"$K,I(`O||g[XSW{56f69λ&#Ch4Ѻ[oNNbgl2O"Y';( q=~+K箝{?4DJWxfV'LjzӬ<:ǣ"m|U>b))SdhDf;_dzzΦq$@ݬ׈^ x28K'kO-pyzǽEaLs /-)`[T WaWiB y2N&NkA' 8>O}Ҝ_`;ve<VP\E8Q{[K|&TU̦xvggȃ a*"G~~I?]O&B$%w^5 MJB#*B,8OdOhEUPj \ԩ Ζ`)]}ïJT'$ K$(\2qXE:x+׮a ~Ax0kӤ'ORãƥK7kܝqC* 4!._X 4W5:wsGofp2Ѹ||Sן|f2 x{pk}' # YhҲe oדե$i0Z̾:r՟%1"yt{BRCX +DlSб&6({R*LfT܂\ϴ>||svvE)@+<%dARJkm,+Kқot1/ M,{Y~O>O|ɓ'7>Yk4N:D5(Q >yo/J[TMȲM7zV::b@, vBΖxFoIK/Oidy1?ػ{_/\<VY, @/bB3ilꨑh:4$5X*nTi bAZM+P8hPX"e7>{;>ظN5jIG,+D&WF$t"*C  ١"UfQ+{LJf`f)UEdv lܥ\YHQ68e歟d9V2vpZUbI85  +/h ""+U jmͳ"D"AV<{>/iFf:W4]wfͺ}IZ Ml|uڭW$D+{v8KFlieQNO3;zҭ淿z-Ma>|x0o|ͣ?{ =o}0 I5JAm=ƥ/4"*ufë/_[;옺}Q^% P /.F"$2i:0+q?<޹Hݩk3غR@k]yrCϋ4sVpACVVZ0R/X#lVaU#dp s ,/$ ngj<ׯ!^B+ir(Pt<3۝""yk- Xko߽]vWz,~z BShݛ7v67i "&ޖBhTMzׂ H^yc>/x3BJjѩOn(Ҽ֨c[m* QHJi0,@NgUZ%-921(&p+&qrS˫g.-Dܷ5B,K&0DTXY<%ֱs.9A2vn;c[`@+ϲܹҋ$<3j^Jq1 E>mja<>x7ͿAVس΄Fqq9:>Z\^;>|xqy GOl, Ϯ^pTnr3HBWD$WLouM9wʹ/ugp$-9EYB^3)Tu'j3gǽǮ{G#M, "A0 QC=  a݊Z7?(fwn;{^=~ݮtw{aeT:Ilj cINA) VffQ8 kͺaVZ/{;i>4 h(Wr6)տ_|optmŐF`tDsTңϗ@B`AB4ͼs~%fTޯ~ݩ/jL{D"]0{,7NPX :pS b1ƐBv 3jF0D&%Q$-"'B;JYg(}گrꞵV=:87AlM{piq񃇣Rzq42QhH"py~󧏆y 2A"gLނBגĵQkja\k STviԟbÀH8P 4dҨHP0F`OQwI5 n<&N :*`{xleF^d6mY:&TPVJ&@i @:zdvΖlsWj/8.qaGp4GcrT c@kg_~ _2^CPv^xgyw7>/(&b iRKڈ²,Px0y؝#z-  `4;[w?˧/] /x4ɳ-{o!Jl<'@RH- N*-'Ad.p,]5}"@aZ6w$Qdeok>}/i&ӳ+g=GGJ뵤n߾sAJf3v8MWً׊XBh\FiȶC#!ʧY}xBfjI4P-^> СV<e^e5/yh̊j5mzUG,x` xqD@4)djAPN#6vf( Ji:ƃRg> 0ϲ0W_RQA@DBfe7wr UI:WIw+YEDFCE ve =|v[cکq28-A"z,/ Ysŧݾ(Zwgf]<=;Ka|oZx;bBZE]WΨPWX BHk- ooml-tgk:옽Jf$N+3 / @i!@/OѢQH4sON/xY'\[>թwu=q(Äho?xt{gpuuVI8/n~~w8_xŋz]JglVJDyrȮ\)y:5IAAe4*bCZ ~ e'no&Ӌ g@" CVhфGfX4ypwIı-3R ,7vWt"F  5"D}xXdy?>h8z~6@է_~?Ϧ zyܻӦHƣ؍ͭW_zy}QuH#8WT= N A9š5* )r2E1\^ܺ Oյs;]e2{&G-mVH^X<1]L&uPPIpᾤL"$MAYaf^:}b;e 8{\`ogGye[SW]EY:F $Ks"ن"ԁLM,yX*0ogA )4(U@2aU[nw|o+sktTP-T,L_$BmكxTHA=Z=wvy"DA@$eYEѹ cN+ ̯_xj0' {grRO]9r–g`ޑBFB RXp"ŶatGtwf%YȗfE=3BEɧMM//.yWIj>L0===37=im8 D dRDXY<+" X$ 'WP$* ˥mvWsS|~Q?}\ZO&9ԘF` ibT* ιx3.|z- .i6QliyU*}<)JzƇB$)"-`dϞ?|o}o~qoܿ k+K-KS EA!m?wɣa*-q耈=ؘnk/_ykhww;|kI L4i*/192m]؎S t2oq<8 BI:;3&Iw\oBht銢((Kmjyr DX3juSeN Fٹ3tnDp2tcOJ # qɓ,N8KY~xR=߻s20KisSGדfRkFabpjnERy! 9 xVZA$|o0H|- 9/բ()cjƔEjAJI+yB) ..EnxQ- HfۻKg?.!SD,I$$=nj,,ǽ| YlϞp1˲02wl"5&TQF>&fo;>Fğ3YvʵnG(!uRZ ib.CMn: o 8WK8^lAſtD\xQBM xMHHOf p[ s3ίI/Ko: ՜J1-YLQIÃ׾tcsc}GW^~s,>0Ɨ%(*@, G0,YT";h]M~q{}3 3k߿Fk׎=y47;Yhe(CCh)|rnR45P[&bGͭIT{iiW^C+_m9BAJi1c&RD**E+i24i S)P 2-w?Qmiћ75f:_!'QYfC\:?xONS -$x⤪h'&FF:ѥI,rV345{y!" "Vp!jQM X^AMֻ(4m;3s"rgIz?(@1^}S 2 Al&z`1J{&ʳ|\Y8Q?o4jZ Bfb˒[Iާ7~_͞pᥗ^~uqT.Y!wi&!+ksۻ97Ւ[kQ`<JZ(x;Ĩ~22|,ҜuE3V 9'FEu! yqAPO7 +SY^k ۔Fl<͖uGjzeyZkowdГtM HWK4#AWZF3j_UYd2I&`0Ꮙg_ZxOd͝aw=!%GI{罽;Ņn;8?7# 꺮q &*Р)Gl?{7/?Bm]؝X?O&=(6V(R\ٺzM- {8L ֻZ&5)QiVEF{o0osCh23RgfT !3#ZTJBo?]0(B_=87_}궒 jJ$TeU&^\>}d ڵW]lu>|_ u{UeW.nGu9qqlΆG޸~#ҸL1uz~a~LSoFI~ifJ*dlr_\?'bHMI|{{7^&φQ Ix`6EXD:79(86O>7+7Ǐv֮l餶e>~JP9Bȅ vdG~a8N2o~ۯ|ǿO$vZOyV *iDDRFxv{♠ 0xdl"52hb*篽>yf\ܚTutL$2Ve9>{q]09EZD  ,G n|ksɍF_~yw}ck}c HiJ+XX6Ҫ(KA$V$iDNXE  , %uעn sz6'i`2'S ы'޹|Ec$x@h]`A4^ Dx #D,^h 7C3@Ν*7 )oH M#L;@c{ .ueWuUpRNgx2 uRW<UyWe(R̨0 D,"@TD ]8Yjv+I !0"ٹ(F;={}O.ݸuۯl 9u>xNdiu}hRNgɺAa^Y\?:tq9KL}wey?wΝ5 P浟;E|!LҠdO^txyܬ,feIک#kϜuڮ,dI!p@/HQ/,K!.T )F~uw0]t!X((IĜ\b=}rOFk/,N2k ?ՍŕD WZq[W7!8gHhݰDPDT)M$4y9sU`%`]@0ZGI=?LYt]Q4(l*pLг V+%RӢ9nuz^{ueecc?ۯ߿~񡯊vֹo!I-.NΦͅVK﫯$bщ~ iU31&afxG;/^[Ef}?\_X׿yҠ|ͷ  u s=!30B,1}l;wjlJX$ll||oc}͕+Q(A3BH{C9Q`Htg"-HA+EJQ_:+HZ^ۙ-hX,kyMdEJӓ_nOܳQnRx-Ρ>M/ B@v^-Vd4W@D+"CI 6UXt2Uux~>~go"]ևJ)A"NiRֺi*n'MD%G*(e⦣ĻQAe~/>죥[o|;[ݹ˲֩s :ZR1"ր/v!@q m?YhߩՅ+W'N*ڋbEq- Jih/-8B4lg[kʒGcZNS*"} Q Z%A3G`]5170F)ѬF3Յ˛,J`̊8lCmme"$:xN' X km[-\uR"\FG@ D nݳãh}tdz2.+e^wy$ <@ur:M|lUV5KjAV!X/J'GO>_]~뵛6"NtjDBȧ2% BJ(̽-/ČNN,"B,.-X+ U0DQ[\LQ| x *]9VDKsO;O ak~>v[iʥ9Ȩ"\F-A8TJ3Φy`Z5"c4hEuo?yR [G"ǵ (m(^X^|㭍 ui 6]T*Ih4s[J*/}Jo_0F2}h2eA^izJgnHT{sݯ~qEO͵7{xme;%P"l‚0%IH>G/\ܺ4H1+RCEe8ͼTI]lm=Iq 8!eHk * YN:B$AEi!JA&޺|e}k++j$QӇwwcuP=ڃy  6IKGi7hNqiu<w'qu V6́EDpXkwɟL8& D%iiy"tq;"!8W0#!={z[_}}Z'<_pIB*Fm@#xt=)^ HBh,$bsE:_t4EIBs}"v@jN/ʲkW#BJD2006-#F <O|uc}um[' uUluLh+ˁd6 P!8Wں$ "0ScptR泬ۚ4 ·J6X}/?'xwwOQ;v& H]մ SpLQ@T?VW3Gϯ|պP9뫪tggww?3[̫zrO>Qy[GQu@0"Ts LdO$9aQO(5^"4 aU,G"/1*@T"WTJiaL">xg]I+Һ(I}yV㋗/ VFyn43?wtjPEY{d){c?,ו3iӿϞ1ͺy%V P84mo/"ޕuQK T }Za;e$ 4;[EUQ5 r|bg;9RU[UEN>7.ݺΣ6i'i\1=>ݛRsAnks629jA:V7Ft'(\B!pjaF)fN k 8ёtGW}fyqb]44s7%  :J# ݎiֳ<{v#$U 0jMfŒ(4M&nKG3"JggQG x|z6M-u:VkEdbmL,%+jڽ~{|6견j. TtR:;^:ie w~Ňv󵫯~V ]]e!x=6㣃h; KKn suiYGQПP`30 "Rn'M?^<|8{ͷxŭ փ=wU.ZIq`)PGJO'a/,&.>yx4:˗/ ڃG0Mn^wQp2+72AUYoxemY@H}>GQQm U 앓՝5"5a!h^7 $ \.ٱC̑1"bY+I⺪6YkAV:Ɍ1TR3 1@;>>?u,{:@dh;r*({e5eM&/ӓQ=.w)*[DI\Kk+PT_~qvt0*-~~ri 1"xR&Ӣ a$`=!(-C{ @f)"C]O>99}>W* k_%dFǧ٫:V@C XiC$0#paJK@~!p7NwntۭA;5FClAP71fHu]-ha "ą p4)z!iwQ)Ԥkrie$g||xVz+hB^? ˷ZX޼jnr+JMjV䏿zX;'''og+_{+aEYw/^LZ0i­^I? @A Ά\N>gfm>tW+*[)-й u>$/A"]FjOkV8Xb5z9،X3~F6K2sM(|yQ!əs!"H$ W[xV4..eŕ%2rUQLg<-kDtG<3$XgAoBNߝ_(d8 'i:ӮEZwL"D"r{G_~4X\7^}s~aenn|f3kDiWӣӍ q'{i[[[=88kIx\z~q!K5WfUb%&҈YAW}IO:Q4NJwA1GαRZ0Nb&l$/0 تQ=ޞ_+7qQ9;MfΦuKda~^kuDkXk,"yq `]mwHH\YRv{RH4e `JpsOZk\?zA@$y=贒*MIЈVQHi. iO8"oln_lm]a0 Hh"dm A3#Hזba4K(2Id|>z޽??a&D(Vgw?{=|嗟7_ye8>tqY읍?]A_80Ejf `U:R<;,F`AEh˚j#^V R" m?=@οk}w|RgO/ݼFU1 " 'Y;6?_51GKueZ&TUi5a\ f~~"1Kò˄@ǨXE-j6NԸ=%0ie10#ĭ8I$mEQEKdxL?!En"J$Inٔ:MxoVS[`oǽign2:|2ʩQHGZ*G,HH\yO\ۯ]ܺ e^Ue(˺BVW)ݧ^~ >PՋXTl{i|u{=mL7<繲.ؼ^P")sv2+RZ) 81"/w??GuQ./9kVת~{," Z"LS&ܨ!מ>{|ɠI(hDw!HW; u烞-x 3@/XDkdϷ"p,bnw ĩZ\YPɔ֨}egC[[ Lʴ:խ$@ {E++67/ڱFNI/VO&go~㝧o[}GX [a4|B4vY~?zG? WW]y [L[s{{NK,1L|nnߪʪ vx|lŬd7kЦ^P$$M&/vwwww?FD<9E+=ٛNW(լ5ܖ[8w^::{W,jkY6 :NMW,*ՋM/QSmI˲jO&JOH 0ؐRA) CҤc*fʕq7n쳻եKj[Sm:*78:lNGPӭՍW^}W77v{0'yW_GJS[NidZHm 'ŵ Yo*Bf{AAM,INqRD8 Ddq%}DΓӳ ,J$4o0VM )7+wbuUeD9,ܜAM-AEQ1ټ3H8Yi;rcPC`A &KuaRMj"ev;bpKTĩ >$2}>iU|6زs(DycV]tFr\V[iTEbb2(H3 ̵]]?w|囷޼vե\Y[UDi}p|8,]b4>:9|6IȽZo/-//+,㸛oo׻_jB0YQv:xZZkƭw"03k;.^x v޻/WVw=իWzseHi}C! 1BFFJ#:0F՚DrVU3_7¤%|+bC, MѸF _nĔe=I'Y[4qd,mH4{;+[ f1I"TBq]ںս{i&t~qWW4_+HYh 91Q%.YjҸnGik<Wu58HtoM\Z:B!KMD;FI""rS$1QYV H0 F)ݺ|yqi9 $" |}?`-iQy]FtN pEJūvuu:- N/=}oyaM)DǤB6 /@I$NVW|ȉnrV4utQ)h?-ϋA:}YUUBi\H,~.P%Գ'!Xa{g  k9ıw޻ZIygw?~rZ0SD L܉HbRIo?o7v駟9qxη4%hhmq]k4!r WeuT'Ou۪5baT Hms ^س:^},!Fl;*FŌ>}>>l2QǸW5{k../x6>+2?Xt{ + (D*gً֕Wnܼt:탙G+uapwoV~wK`z|Sss6[5CLt!Լ20WT \{ox$ ۭ+[DYIYu_]~7>>9]\^&ӡ$fyg!0TJ=&;!wA)!XR !t2 2JbRIo>imvVn{  8g@$D |Mo2&@e;le~ബ|&5q4H44LL8㓼(\m$"(ц"&jdd~o{só8WW/ڪk{O^s7-(Εy^q8?9==Oqh)Wn.!6i6}۷WWN=T[|{ 30$([]2YX(O~WwU (DX!h$gAggA_S_Ӓ~YiE9&woݛu[Lx ωC@!Kt<ɵWM!07a*&U$g `ǣ`o'$^W\{vO+[WU!P4]˦򎓤LB@<*RP R֓*2Q =:I q݋Y7K7~O/^PkX,,{4?~_ixC9X53$%uQNOG¥*XU3_h "3 p,U@@ǃ6V"x59jw-GH'_?u6*efT0DZ&Óp9(TΓbc`i Z)dFTX)Zl׷.v;Qw-Ce.}YJ Uࢬt.1K!Q0gә&Rl!A`o ql?y:NgJv9㳺VX=~Yp6bIE1*uVo4--D7tTp/W7֒8v*QwX V4:^^䕵6ʨVM㯾;E&(TB8|go~G ꪬLڹz`g.gVUeYOIwӿ~L}٧J4MoO?kWV7&"h21@c`cstvX.7@nn1$qI/>f嫷LGO^]v훎Ax@F"b ,t%]Ru4ƘI֤c>7 kvDeZaDʲV14bu/'gGGᠿTڷZWzo~cP<i⭸&c""QB(ѳǏ.l^"T8x2QͳH3IF AfJV l]20iq02X30kPw펃`9bM 1Ak ƨtzW0oY_Z^\ޘpwZ: @@ LMst(D@!#Mԁ,gGjk+ !JӖBbYY RIl8Z)a4-+D&Фe@<`| bAD18dDi4]iթ\9ɇv[MOyWusS|0.wZk괴"j> )!.^qo]zm~~E6ϕFݵRUಹZ=<`hM:n+YYݸҟK) HQ ␀86/^| I\yI59WgFG|J WV *QDD:x&$֘ 2q, .I6<~CLYVw|(W^Ҷu4Ng^~vu%McZ˶ʋiExg@2k.].IMy$`@ D ,@$[D1/%[;aNUu uֵ!RBm]$M)N5:@;'\;x/]4B-IN?mq]\YkeI1>=m^yWv&BO r:<^(?q;ᣢ&6{ps0F7l ޱuuIko},D>*$i-[+W\^d2РUnmj%":7MulT$M,~A;?b8zOU k 6n;9>9I;?~ۍѳVZECL *U;-nYlݱPseF jS:g[AxYE _dxϽ޷睷73Ɲ\Y@)|@ *dtYȉR*6'㩗ḻcY}mGMw ~j<0 *OWחO󼻼Vӳ!`$LQow@$HPӼ'n2::.76N{@:D0 j&IcQDs(um8w(Q$ŒdIӒ,XF(PE4N$qlBڰ;(diN[u=&QUJSei*  :IYLɡHhʵnz퍵nڭw"/GuYMFI>E󃋗.V#1r{.+Jy$nN%4!(\O0DS`wԕD% F6&&EA4۪8=:Ot8Lƭ[:Ig6~q﫥W_:/ri7B t6A#Jru(K[xŤ*+1d:Y8E|>(mK4f@1A!8N{ #C]EAGY2H=pA8&DEeY%Y{A21ѝ^CEW?e:`GֽNi B_~voevpWO淮\Z^ LyLb $]9{_~/l]9km  }Fk9>=N19==3lUڝ$Yv 2JpbԤYCȘo~y[LgY6_(4#Ž/E_XYZ no]/E D/~EfnnW?nfѠ M I3yUʥ $DafhH`bRĬ{A ??w7.kH4f$լ08 BD< Q&FWU3 lZo!d֒NZq-AED^uH4ܝ%z+hڃJ#7&vRUN(,زYGNb-K0>}vxr0A F D8%gG3kqTuQQD< Ij W^JC܅4M/^zy(#Y_m?y暥`R=HT"~+9=+7.nm 47*F,7f ؤ0k#ks, $xT cw·LUj6Ƙ$ZkMDi>ggf $"" 0_0x/AJ$;דH W瑶oe=Lj2Ϊ2ϒ4mF$J\V1"%Yl9lo=__uʭˋY/tã>FZLL }Jd\`ʹ`Ne&i{|XiwNVm wB#J!{HXk(ʺ>98f(=DPJtcRY_?z#fuKӳtKlǿ[Y[k۞+2x.Wץ!I"kw;YzYq|esJ+kxD4+NRtEUX_eA8Q IA49􅯥pXNJ\,,0@ 9 uq]Gh)]7~_gj~[\XhA2vKཧ;|_vґ"uFX[ۺr_{\o\zێS[WDܔPPEZ3B%>>z^3 k{u%{w嵕I i(+5=TljJ&uQFɧϞ;fd:nٟݽ?vjgVJZq_>iG?d2?`C!lD΁HkZ\\: I`t(PU%)`U]$B.^\'¬I#Ԥ'}?21H΅pxOi:3W*GTo |弘1D,įE H'i! 'YQ:`m|~7kn:-lMg΃w^yUYKS^֕u N:v,7v:~ *` FĚB$@|IIxD VW8:@\F+@8j(,TH)D*ɢ*2^k:>4q%qwW|cH s(8L : +2 .wctNMeAHJǑݸއDiT@ ^ļ!D‰Y&B +wl+ウE~;i^LgϬ#dEʟ<xyە+zIi1 H.,/n,~>: v.lݿAQ[/ٹAH)aOG&Nؼtwo먨4nɫwn:un,ͼ;AJ* u5)rJ;ϷO$ V4MZYNƓw;>w߾r`q4tLx NR2xcekЙ/'.^46({ `lL9NϮz`QD(nM"[Mڕk^gQ{ ]PF(B<QIŋ'+ vNS`'?$X=Di +Psք%E~OJgnЙ_Io?Go./T9F868!4>8JAV$? ԫNt!{ B $!PZ+ V8<;ǐ&@krPV BO;YE dn޼~_+~7^G﫳cZ:6IgQ_>rZ ۢ>[] E|ʪ E,#yZAUYr!JȠ/@i` Lc+9;<{NC@ cI84Kx 3el1MNW'hj,ڛbhzqJkRZX\ :+> tdL[견l6 ޑ61hT82l5NO&GǓHiNU[[F` #zѢ={7_zūsKؔU!ZAljEI{)B!2F1k&UElQ1O&I^ kiu_|WK[ImmUN&gLZ͎_Z-EZl ?${qxrpr?EDȽ4s^EI>:9n}x;(MBa ޹9+&P+fq AǑlؕwvƧGeU>{|uuڦuZhf$/4i@!Ԙ/-$+g_?޽rc(x6EQ" Ρ8T$T8*/ly*E(-e==9NK]IE))P{JQZ%Ini~xtH'[I#>. _?ۻW?o޾U@{w?ڍ7ڝ~Q[݅ۯ7։HSZx ^Q;; ZV5Hs;A`qVVh,*("|nh,Hőֺ./?/֮VΞዃƥvu!<=y$) ~cc}~iQim $ʴ1@O~n\2$N"Q@(4[)$aVFGZϦٌZY]zR 4bƔؐg32I0kW)HSR-v7j7VDC`E)# {yuui# ZϬ=:;vqUjR촨&NWt8|q6VFZMw>4zshhxG^1:4}$fFh 1$5 1&Ã?ytcR $ב(jJt~ w|jߴW/B?|rG, Jչa%<'HB"BHgFOcVimqUU5 -P [$u]ؤ5NOFlj㪌l`q?&쫮D "p󛏎 [WV/]t֥Y7ݼztzAYwx~@ʪ\omu >}އv7Tz}s_|7}osccg _=}{bxXҬKD$غul}tp;?jyr$q/,--,.---, 4bB_h|v<<;=:o Q2 *0#0giGƺZ4S wFNM)޷8_e8<~O T~2LTf@7z?qr6JI]x\WgJ''O?NNF6$덭$˴pww|zF11:mg0w N"R>5SEJr|ΛNV^8|6E ˁmͫ٨36xXHT;/^;;ƮEͭ_{ss;ۻۏ*2QWI7ƑΪ|RT>:;{{,~ysK 8EuMRJ A^뚲9zO^z΋Yp|Iwe94i Q>-ѝ;w~AmQ A9I>=|ݳη}~N8m|lnFUUD+2~cun봕,,^O He),V܊(03ے0 n4F;v `'K4eC#X5j"(3 h("@xK/f@J4;nӍNfE 88f-,Xi e]|*Mh}| l4?k'=(_`xv:NFӢ y{?KҒAHkB} 63fխ le Zŕ|6:is?7˛uM|G̓_U @D4 7_#,pl 5Q |Uٺtxz:PpO~AZؼa(*_R=%(#6„jd# fz$ (y7&.Tg̬XQJ5Xk (]Z}F,I\eiZeb& U J-,ǣtϦh8촻qwdYډui'1 xEZٓg¥֥VW7fb4q{B/e ԝ`j+Puuxi>zowW'WcR^8.-l4_\_‚*prƍťZ"۽չΠyJ0xbo{u'kk <[{; d<98<ٱUiUnƑ6Hs}k6`_>Bj5"ȁ3ֹնRH8p:g3[uXڝvG "W K:I,`]]( -]t=yph_XmiZ<}i_<~pxrr6v+kk.\ U P"!jb4zJR['ϟ[Π[o0gjܠoE,0Ǒ}݃q>fy=ʷ<_\]-/Ծ&|f)! O:P)YYg][7q''ydss3WM+.]fGӝ'Ͽxwm {AhϤ(H0!|qnC QЛ[#@]׮@h A nZMQFgǻO_y?{-Vf_}4HN&o4 =yn2D$R1)yax:|d26Zumi1DPMth~m~aV3" &߯H;(B& y缫mm]{k],ARnssrXiP^09=,^9W̕OOǀpb7pUҵW0!;| 0۹0V@|)s޾et~WrZW9;gb<:*^+hu;ε]9 P7'F,YPS 1\iY}DtF9_GK5~6*RF+M 9)dY&Iu8AT"'EtCFBhZY&V֖T^=7)"G_<=?g?7HШb@y @ʮ\b<b>,/DF:ݥ׊zc͋N?$*y(%A#쾸_M4t_dYvҕ}j׷֚(Z_|]edtdbuv:&!|6fh8;:(_YqVD, 9HXY,$I0U'1i{YwnRAl&|6-Ӳ%Znu3FǑVy^0"Z~w͂G+=OY{euO?=88:-.l^X~mƭՋޠ$Sp_ԼA7 ,bI۩Rq76W7V4m6Zi0ןcгQ@EGjw/nOLjJY5i$Z-$+T8Z][_^^=x<<'IsV?~$_t;qeJ/A@+@j:&~p(4 9Ș L(b Px:3jBIH50V;{dxz%tEt֝[^ټt޽}5D4%%s(Y[~ի&iۢ<۫JU'Ift2&n]tyiel2}x!>2:ґV$aU.xV}]@U,b!F}P[;+z/X%gQlpB1Tέ\~3XK" $ 7A ;O~hKm⥍u]ͼg_~gOzzz6xtqiAi=]H8@9 KG󇷜SU9<!X# |vf]\NE 뗮4a#R !|fΆs(̢Inu# 7zA͌:@z9rϚ||,B˒隲ݝ5¹%;9 T 3s Db율v"IP%%"x) ]gmlP0~4m Օӓi3Kz {~0s1);2YA~_~7o+}͍l6W'11.Ո`1Sjx̓||6-7Vѱv5Vx:}~zpwwF-ݽ'UU .\p?E\pѣ/n_>y7זm *r}ݧOu+v/f4*x,*ۮnf πl5d?1j~xu1줮MF5U1 { !(I'w>9;jU5oQQ-//{$ʳ ع< P̔Dc$M6- A>dID8֖Gu===9:={li۪,~Rg\ְSg@1fPĶM^qҜ<=zU?]pNv=xk&)5fWy ٤|Ko>ImnΕ,/l^Gݺ}}ڇ l^T-Ӄ tEؖ[!K}UrT%2xL=M:n1Yz׾-1 "i2Mm3D1d$0, nyu~~6f()).q:od kk)(auIj[se/ߓ(g0s0|3H^0uQ O;}a.TUzEyꚂN-I#CM/Yf<==9LNY;dumYwê!,h<=Γoܸ[߸~E(\Հ Gzlōgݻ/x_~vg…rF.OvNc`؎͔}qF((NWViY׻K,J x>߫zmlzt:yr8>9m}QVq/WF]Y+"5Mb ΣH@bD]p7 I 1:s$R@s7Uz`i,-/C `"&mcT5D"!cQSbr̃pRU9Nu>K+o|͕5Ҡp< _ d|vr2P,b) DM1!*L * dfAG##2PT03&U0`3$Ƙ$~h+BUh"Dm2qO 6SPbjۙ!b $d) kyfUPM'oM6C5*xCDH*@=eSM]>$\~d:gggyJx׮m^3fz&QնK<1hRUǬF Cό&) I-O%+M T ]lRD&&gTq_TwUGG/=͇O/?r0,m'.zƳ|+p СCX ̀ȹ(iitKmfvrZOR(5wh.5{FUaI%Y*`M6Q FD h/Т*NPCPL]QJs&9bҲhvg˷_MI5eA2u'Kʍ[Uᦻ}+WW7/d:a/1IP!C0F2U,/NjƄDrX3ۘ.~f ll K)IJHĔ<"kzl>LHV mۂC#Wzet7d>Mp~oow6/Λ6`Q~諻mn|o" @WmpoX7M+@ zvvbl6&vtm0&3+XC\2S1$ɕ$ITc;o@cg"`*јؠq(*b_ <_CИ"FДVѹYlmooODž+pxayycUTE `aEr+/"V d b'#QTENT[Im5KeT!K\ FXu1ȣ~@G j̋(Ae+5͜ 1eU%7:6̭Hd+Lb UD̡),dٴӲtQlimpAjMjsdbHHbDy5TCYG}BLcY=מ 7/F};;qb(*RUU0+h1@tċI$cv Ԥ)BH1b49R1j3=>pONAi=iAЗ2۶+*yƅͿ'Kׯ_ZoOyzawE }{[uۢs&&I-O^y/ɨ`>9jS6e3@ c>k+Kocuk}T7x|g 1Ԓpn6:ZƀŸnS$Va6t*Њ~o:7ük9?zt>i%EtĢKGϟ>2'](ֻWx+.UU, G<t~mɑP"6BɞEmsJEצX%^o!r`bfq>XZB牘}+GmĮmg\|C JND}'q7:OƳdvpiT%6 )S g?}pK[6IJ[]Č|ʵ|+z;;;k?y?^Y[^_[X]X_E RGf]S뺨Ro>|k#S}|^—.mmkJ'RU*L;.@S7Q;ux|E9՟ 9r(0=(t]gX2Z鮓-n>ilصmx9pUq4=]wm_z>lp鳏>I]lR&A?쇢PA~U@@Sj @j3mTT ]`$SEEq&*eX_˪tEb6-#Z= Q䜞ԋ1/L*✧¡z>;'~:?ً\--U 310"A-gPLN&^ɉ΃{gk;˫<IcSUDHK>o=9kqf̢pyu.ߺyʅ'5 FMb* T3Q*(GUvRB.VD0#>;>:=:.| @9~1Ĕs0@J"83!A۴D];=8̻o+C$&Ζ,\sRS7ZtىV~5C(xmk{ҕ31$&4槔?xçޗYݢ"םMf&0hk|}e5@;߫Imqu-dДD#fB:pv%-R>E@ s#qNw I_㳳ԥ\;fbG9=;;x,Mn!"x+xǓM'gQ#x{??JKr7F˫2y )( |vGSgŇ8;~-#P?r-WySHDw9rXUpWBc/P*,}ީkx64I0:d%e#Р|uCHHJ)7WGo~h7^\~>h^m+M泮]dBN`pߔ΍^ smb=V8o$jjl:==:~|wg;)uU(b[7auiyЯ ZYT4r  RJ {H0vQ1@` o%>}hc}ҕK?{wf4pu};wWW~/HmjE{bЯ;2 bg?zt{dg|xmyTcLŇB-EDB2#3y0Q3ʼn'#ՂLE( "l:OF$*JM%0(!i,k U(ʢѩĴ45l2C!Tp8$0Ped$bF ;"?B}B154MEU F]מ{O8M7NvOϖVua{kzvm)DD4 ąw`/d<ޕ7/]vK\ھuq⥋+1uݔM,d<8Q$";<6cr/jlLHB0Ǧ"g{db1b*){ P MMPl1f^p萙HÓ囯VLh,:GGg1ģgOlO>Qﶮn]^a=Xx{ 70@k_L**hF|S@"Ǜۛ \)j?&֭Y4@6ח/n-i=V` հuʂ(U׶ kXw/b"倀)0&=I٤Ne,z>UiTSD@H)\zӇLSWw"#e+bNRq;D,>]DBl>.h샪2!Ң돑@Rvܒ"ۄ֋r}ª!!"0;C6ddFd4#$&/CUCB9yO+|(J7ټ=<NeGUUFFUI͔UŴsujpEb@%ΛFDsQ|BW_+~=i._ykG?7G_( 3FQtTL bDU9?_]zc/>o~՟fn\af1ƥ_|ҰG.jؼmA,)7MDݬk.Wzbi+ʢ)J]W TĔSKK+L=ѯ>:;3zzV},ͺ|9E`Qr}w՛G'{ zz̻yvI+-:-t(IkDB ewIL$EDؾ~0ҔT!/f[@c$w@<4^sZJj0d 98y1[آ}2qk!+:B¶iS{eUq_}pr6ڷ#;Z r_ ;rI2fR3@ I#RM/>r_m'Gpˢи2ZcVR N`I-${v΃TR7mk2 & (;R5UX$e\դMܝ!)/j&bb1x0Y=kk1g"1I4_xmE KDT *0;κK| J|vwlͳ~7{:4}vhofHiQ35m ^yt2msxwz7>9<<>8 f2; IT Ak T$.ZJ 1~oʲWwz:+G=e1ƥ~Kwߣ^/%}Rs|[_4=؝ۯ]Y^]}%hS20A{ወoE<<4/C̞s˃G lc`$ID$ikr aH_v>OrlH8~Qmr7MɤO!հ R44CsmUblۺ1nqk+WVWETb"?|nUUTGF, .4o6p0X}y.uG6.\ؤo^0E9o2fWWQM=KiYL WjUu? GSQH D|lX9."av;"&h\]]>L~?D.gW.ݓ?X]Yv;wώ 駻FKb0xͯ{_ F˽aJ;ϬLshW{{{ z>gfQp$*Uuюe=;It@H`")%ρ9.bDl"@b؋)"Y"!Efx| .iP?߯JS@/`*|RMƴH3=LONO}+np6󺭛 \o+(2޿C![7/! i .m"(SnaZC20NT=JvE*`*0N%!BBAPJ2` DQ!bH<9ޢ!%5="v]g ff(sKmu䦒q&KKf?iX?:PD8Or0=y|EfB(QD䬒*('KÓwuv>kJZM ; Pjh&2\)l|7&96ǒDŐr "! g5mA:ORf?.\ K| *RW6gcDVЄry}W^hEfyt@gۭmnnguьo bq$bօv:&Y˪oz~zύ@A>IBG!;1eϫy!`)*019P(`)Gif^Zc]Qc/ WӶ]O''|RLwb@9UH RJhmuu݊%_wò䴍g`>:;,OfcU@wR=ﺦmۦApr0f[[ /z}0Og]Ӯm< &Mxs{{>؀+\p~5Pҁs(`$ !p0SؑIB("D䘘1vS^Y*,u[7Lk:<%B6 @TP&B d5K1KEӮm/x{?_?}bC㢡&5iBb2+MOv'3IУd֐N%f֖vUEEY<ޑ2=z9:=䋻?|c0%I|6ĜcpyQ1`005ujfLDdU5 żk2$A$BIղ8WX&5祂9-#cUipU}<-Amjbҙ1 @S8f0 sX51oo/Ɩ/{˥bὃ{i~/"gM~or96RbBv,m۶,+vyWdFtmjm;M u䘖Ɩ։mS!=@;u?|hweiI;NkT%2._Xffv@uƢP-/ّȐRj&S]73J5R_O՟頿tO810yPS%]ܼt(GGͼA(jyui(Ol_\/+&n>;Qs^0q+qkdqKB0ʆ"VEWsɩ20wLEB!aQHUͦ6ElXۺX+$e7O~k\]fӮO<ǂVWOy}{##30պ_}v"IR@()@2'W_Fݦ&G's(˃je#"Tk`uuc:R=+,fd&heskҕub<òfiRS[RW[TD90bd9-SH]@\T} ۘD _{å!KLgMnnmE{n_ub`p0=|`6ol\o_ /?U7{`8k(Fmh8$PVv]g_fڸ| M҈ɋU0,(!39E^$SՔØ(Or#"HEC1f!mm\v'`s}mY6l jlS@>p P.;<;)ޓyz`𢡊?W7WORm\⬋g?z?dDS <ʵ"?AM4v]3r#XH)%njI1$ԇPU%{VIf/PRB@GLAM #ZP!K9tLmוqrw:9 EݥWTu6.o]Z0S;l:˷|kem5G|2>қO=ohgօ׮}lڔeYDɶ$);MU+7\;wzo=X){')%3 &rީHb (QѢ !f&Ŕ%"FL"IONo&Cfyӌ6ѳxK.^U"JD{fjfyIX bUk˯/??xߕtLk啕NΖWV\&I,|Ϟ~'L_]|s]ђM Đ# `F,7Q3~2kJ9("?OL$9$FD$Hzԓ?zs]\^۹z= 8x_B z=q>.˲/h@Ǝv3ضsAsq@PaC2\Y"k/Mg[Z^i9Ķ21Rؘtt~wwv:CU03bP$$&LjI, P@S@dF*Hk"JqJC'ǓY]V*OڶuBQ+o}ʭ_ܽ 6a^ M@ĈlHĮ߻2wmo||6K]wrtr|x]3ȇ`GoNNN{} "$V[cv]džVɤ-&բ(q]31ʐ"j40T#(''o_~zQ]ӵ"P$rWo3WaQG_ⴙm]."9*XbkB$O2Lq3nms}|] 4E$D`DH)k732(A)h:/Jx$&DqCb0 QI2%][Ϧg3v _ηO<4\ ee$b=K&F$"iض'Q1Wܯzw}uѳ.lzpx~irR xvk+HYİҫ 2*Ģ3ε A# [DF2D G;1#Ŭyf0(nzxg6fgGg!ME8!$ 4j'u`j 2 )LNr9P<( NΎ* .B,dƈҵ'Iz.tNPO 0\z[nkkܵ]~UpiJ.\~<=}X߼'{;O.쀟_y}x< $ ݬs2BIhGW7_JgbVČ\@e ̔t?DjI97Tt?ǧO\?zU0TKS%L#ӽOVVU`r'`Vh0[iL #`Ly`  )cL1E἗X*.?j^Wo10ׯKC^_"e_geaF읩j3()%DsXyStSJX 9TG[xsڶ.|Rlu "wpr{pqsGy}pasteٿM34'k  ׶dp<>ܾqom fhsŧwq,=/?ჽ{̷^~emm]Ds9Pc¶i}~DhemڍۀeL$FW(4(M$FP rFI^%&]4sB&u Xyјbfɬ,/7he|@H^@`0Mqs9,zgO=^^W#P-"vqЯf(H+$0e#Dϧ.o_?x6c) s2+v$J> !( Opnq̙D_!N']*CqLÊ|B`( @4:|l~|pttr?E&b!P`B `88_WVWVFhiׯUssS3U;4S5Z8(6ՌG?zȡihBg}@3H)e.6vDd9#Q|&6dpuJD$s.!I&T/W|xKgV (3šhƥZf*9'\% hcMQbdJ ^\2蓐L 3 ɰTU?&gģ>Bfr7^b$ ѬʳdrOIpɓ+7ndO=1dʲ$n޼]NN''gY]zϏWvm}sg;fi~6)^%s3~ȵ2 ?ـbfUb[F+r:}.ON7ZR {ϴ" gg'?zv5`6iRQɳF #Uu@UАD&qhygz?M]J컶kH~1s&ѹʼ1Qv^\ ."x@^L|mӜDf {UD$*ޑ"lfP.6)=bt r]ѐ"IYKHYz! z~mGGb0ۮF#& kooM^gNv)BD9<8wm3}|Y_:NT ׀QEбO#fCFv>g#HMbb"8db,"ڼ$DJ`R_|Ym03gpHS|GN-G|JQpek)i910˘Ì4E?7YJ(TO0*cMCZŧҠjckbSbmuMqck&鵗o%tqsm2GMge/_Xn/nOOؘ8mSkbm'gͤWγu]۴)~ɥ54%@ywh hL!`2ZD1ǶL ] mY_r7?zp竻k6Q iy;^*~*n1ZCmD"!#lv&]sPMO B ޽>+׿Rܴ͜ Y GvwO<'I~ykKՋL @}RNHWx.=94hg[}ߺQ+*#+)0Uؙr+{xHSg\DDLTĠ#ZjUApTzL`.H9ZQA,Z; ԬkbF!u3!tDHȚ)2ydଔi*闟$B HT;2.IdG\b PD,$JU1Cјz_xǻOߵR (H'D !2`KM1\p֭v☻nb`[R+ L6'V5ÝǏLR !Z f@luMBU'ֶhi KxYLormu1EU.Ey^y|Ûs1E%8sf[DɄpAonV Eճ'GWVßJ3]XYݽԵRJZ҃k?Ab>ku8)aUAI:lr'.Gm|oj鋣@n.E<ϑҹ.{,T_,r(|XBoz!ɑ@rHJ8*H$Y"!eQ^kۦfiPWחE!bu^wY%w+ׯolgc<̛Ycjv<7nnݺ277G'򗗯\ٺ{жݳOW^=MȕW9pkvoo}sz&z˯76.l>~zmlmDӓ+ol]++G &VĘ$/>3R$ \NcE0bf $QF<|˟oo5IQΦZ!&P-jyms|:W+/ݺrƅ_yŭ {S5qEDDXR5D"bsބ%3_V}K˫k &"1s>QBZZZ6ufZQ&$ *d<0|:ad$.DH(HK-ŘTE4&3Ei;IUSY&BBQSSUtjfĜ? L1ڙ;@5%Q-O)$0"C%MdH,Hb#eU,:oɳZ"@Ď|,;}~;N ~t % j&Z#0v6tڴqFܵ[K/|u k%较s+;(:Mڤͬbc+uPuh|-UISC nX5E@TӮ뼧~Wx4 횦k$% ʲ(]6j'vɹ1*?GKIDr\X[ ey9[h|iO#=?4W_=-ϓy7z{imWbu}s8\j3є-Z"H͛ HX`sҕK]#K?pBCvW :22yqi$$D=(\(ͶC;~rd/X0rcѹp/ \LHh=f>90$5Ga%—=E({(/!0omHQ֗S'{;?{ҍz{wa%0\ZxiALU֖m 㽝Vhue6Ę^~pYZZ]!ihZncWworgϟ.-gqްc]zk `G6є" vӳ"_!l:9;=NRJ=bW"evN~r׾goj`s 9"lڶ?oln'YPòWmnm;绶U_ޗUs92P\,wB^9=?ۻ~J.pS@#V`TPB6_d˭MXD3Bcvȅ0NOSR9ᬈrM @34%T%0QAW'v"jA4s=hZpq!Os.{ZB5ݯ>Oi3G#BTE`(Iɀ vހ8MŠ4&mmKHZhjLk8>?~G{g_~WMi[0K12wfTdI#Rv)* X]JKnF<}TYh!fDUbLKMjJLzL w!JR'Et ]'s5$){fL-aҐ ׄFmۭob1?>w=;<:r5}) 1FP1jVֶ/mkV$y_dy,=`ѱchG_'8;'#FM=}Y{""P`G? ^4C&aa"ϰ|7"$"@)PEGV9ś;g-Tn+1"9ȳsLh>e|cv}Qz=L39HĈuy=vm};.VW5@x<.f`>Ƕ3'}~/}@Nŭo;(Rwweż[+BY [A*aW|U;e1L_} <jJW@%P`iRSxtzrtT2l_iS? Қ_o\76I͒K Qe "a?\ūW& UU|uMQ0zIb*3bR0+x QpK7olltd](&H$ RV9T1oDd*>p`$6+(2==OfŠ'&`(Й`晈0vMDfYED C P@A01؂1Cͽ sͷʂfbqq&]Le $h(3`r)|C6uDc.FRDrрR9Gu['Sm:SFbȘL큘;0 mE/^SkT,W#dΗ$S &(aEet%R\:6v∑=<}tT(@͘X$$dG*H'tyTD:s\FŌ5Y; :wo!}Nʥ ]MpnM+xV* 2(r|vd޶֞bV,M+zůwn0\@fciBΞztK(fu]uի2U ,Msv2r{GGϞIoKd?v] :vzR7G}Y\(i+, M@" )쥺@#B=]4oMC#f$N1#NET"2IB(RL2sDҞMb״ҶR^X7.n_*) Rl$I?ͫ!.IF<={t3M Gǧ]+++/Z_\G5{SaBl&F<SF[V۵EY"@mE`&0ޡZq!JfO- WǓYbp$*z楬B$vۈA,F)^#ڻNfΐr 0;3̐3I(B];9>~p61A~*H@Ѕg֚R3pNl6^޾i{m:E# P9&V:0. Q! \(y:9·f2.FbS(Ct@"B .JH11!0M`/Yw@lfޥfIbRtAL .9H$2zT_|@.$V tN}{=pW^)"( 3[ Bձi0D;?AD̦m=ؚH%T!DA,5@sLΫbdD|uˤՈi?#>LہE96؞C/|nG?ٸvÿg_tK_g?z/룓>(q2yvm qy\dU@6Y|Qo>{zzzT2-ZYgYcTkh`]| Ң35w1;9\4UE+#*̌ Lт Aޢa%BqЫ&Df@SCrE,SQ;dÕaQ΢J@I/l_W_.^\3Qm= zn,-ojUSJ$pF#R0U`(RI1YS16źV/0,-*10:囡ĎY4 j&4\=y_ D#j24Si#R"#4"FRTd$sدï0phMDd!):Ђsҽ?}Η!z\ݏ</ݸy , !$BfU|kʹ?on%[ѓR@\2 =!9@D,KIb,Q,̀]( Gu ~ @_ܻqO_]|Eݝ&d`›}`&yieU=S.j^7Z$>۽}D dz>`bt 6KU9>9/B(]QO擳d2>U' pz|rl*P㤩# BVw~7<7 buLMg3[y7s:k߾~ ~?ɰכ TPjO$d\o|7?OqW*5MEi0#Ŧ1ʞIh:v6;mv>ywW˰wtĀ6O]zw\L9he "Q /f/ z~K3.oW/T-@/L| /z02Dskȉ(Hb9 ӴO;KVPX'ϜLygKEy&qG+˫WKUP*pJ!ɐ) Y=ퟜe$!fr+^Rʃ*WWVז6Fe%qA-EȹL])Hތ T:qPiVT`J@2 M2COHT z>v-óm& #=HݽV)]]ǿo<|^6 qVaꐀ,sHKO5M+$Dtn1 ˟ D`#T,8I )nع,\eGUhv(]%@P,HU(#EIf|3T"F/l[$< m`޻XRϑJ߫|QEiu^^nH?B%Mk3AC Nj$Wf $>DX09*uޅ2O?zza)`ʵ6vtwoeue4}Z,kf|:LOgd~ZÓӵ ={wժGs2>{^5B>uށ|&;4 -X;i:"eGA۶ggQOl-,G_{Ýu R660v[>#"5igf3u$J-2ZZfr:wect差l]z}`/omJNR 8g&L~Ka6.lwxdMNen"jnGy/?*B鋢?OW_ruk?o_03"&tC5%dSTgک)s6kk&zzzZ^,Cx1E&V3bg"s@Q U T3b %q]􈋢WVG?ٟw!UeYewޘeʢ(32 ЯeYsjg:F9@uؽ2%%E $IbgZ"R `B'5u"G_B9z4yǿ+4", i@N -MDɱ)E0(*WojA%V@ Av7ڠ eֆ;_|pyz/o GH2'x1Kת Ml''ν/bE…6$>wa0@1EJ>>Gq\nb$"^DLIuu |}u{  Uz69( Gg;_z{}?Y%)t::8m/61n흃>}<n "1k̂' v!}KḤ8bjW@&B?~3?@c2FSK>0"8sΉ$5)S@r_T= UBgI;mhy(;. t'G>^Y#UeO|6^EV^v+:׻{ϞW_mֈH8@{xbz_wƛ}_zSE{7{K~yo~Jkk~>ͷewqș9 s@#9IuL *ڶ<70li=>!`Y>yp瓪`"c &&f%5(;Tx  !4g =Qr%A!0~3"iU{>~pi娪ټAL՛aO8o$Y&Ԩ=lj G>3n8 hA!&0M胕+zFPpjtFDΎ=w \>;85rD_LWzkR2Y]Z6=]_}~ӏΞ?UAx}5@؝|yog.w=̋Hx0J]+g|GQZ=Q9,ΧE!?˗/ jN:WdH EGLD9yi8u` @&($ ڌM}Bp31yn:_ @,[4ߨtfT] m1q!)B?Uf"옙4%*3SG4N:. f4U 6B-5Q\8G`%_f2/n// ~K˟_}&m KowGWS_ϖmYz6̜s&>oݺmlH) |Ѓ(>1" E]UުKov9z-B& Sg9~_RI6h_D"h?{o/_3/x{ :@0GDbιI ԋVJJMJ]2]Pm*1!޾sw}{?-S&(-i\2?Y5Y̥s_7n~Zʮ^uM̙ ٢L{ϿygoWuWs5d.`1C~ SJKYBXed0!"!ml/oW +Y&g:db,s \PXlS9w'E:BVMw!P-fbFPMTE͜sh/Ќ wE9z9n5 ow?ij+:,GJ"Nx,e ?=j'qX(ZuRжټnyM]L]zq2sX7f^(`UZꚩnB"KKq<8A3"psr{6pV  O"cA=E=RFe Q{xr:zky8ii_|~{^ګ~zwl&ٕWvɌVշn}7:֑tDEV,!,y`PF' 9/ 2vI΃&Q%81Ez)1nD*Q!9=~??UH >-ԜmuXkJ r+l!&DLhASv5"-21tppr'J[v?FD 9rKz6aV!K a2=Ԗl"CPOX $.L*t=8sM$ɧoֻ.^J)un~ݟ_8W\Hf[/~k&cEL)fc~}vw™?xޟ76eYbЕUM#')ŪW +}ke}Y,3fm1 K_O{G7)%,s0$\@5/?{wo}7~Ax;{BEE1EHoW/}ѻ阋Rы2cj4vDD,cbѷaP%,\]G.%Q)j2=6FEPD#f@dG| PȒ s.3|& cƅPߋIrt:tX$;ԁrMb(Q{1E 9ˣ9 %AB"!vsߞx^LJqǏ^{_}ٞb!"p2?(t3qh2h^,(i|5u)&H֋b|<>NǓ鸮Pϛi"ɤtfX$-ڶaKk1kٲYRsOuM S#(KQc|Yza 瀹i|\ښG'x ڎgߡ0zp8lksfs(|A^5_}?ov/o}+ܦp{RgSgNS+sRO?|ruUrAסD&'O4ggj6liiʢ4#b2S"彃PA+p4ڂbיhnJ\N&D*ՠwUDkj[j&`š}3(L>8"lFHj#q0BC[bR@ T/,PTrhfzuPxDpL"t 1Q58d.FwW^'S50@s.Ĵa*~˗++n?g!3A4vg6նY, SYY3[[tFY|>LgzL:v(umoW.ň"IIDAT VQ8$9U~ɼ"IԓYԓ}>ݹ3>ȱ7?]svpuGn~Ԉ[yNf X~_G]g%з:ώfMÃ`e%z; EKJ3N,*Q6I0΍:r:!BߖlqL!1p;/Nb %`z2C't&oW2l6ku!16sO !vȱgǎ `)u]ӶmK ;1:PxʆS -tUZjNpirҗ b!`iw4{9ljJ^h[p)>ϓ5DLH4: H8Lڶu>hL 9ԟET!"9&eÕo;1HΎ{ := ÍnߛGޣɬoQPw.NU1kfj31[\7V~G~BR|yFʣ^Qgw+_`8r<>98>wp^Lheݤ}?;=.z˾A2Ss $*Q,*+HJ` SK5%_T(@bUT;1A"x,5L&Z؃ы'$J>OM\ F9I g)Ybp"I`+h6ig{l~srBYsǟ|_?叿}_.__whã'÷wϟ9H! 7 H`hVJ  W۶Lgm%^7bD̀FTS'0$P5ɧS9e̼ `SD[l g|ً1y`PTAb<8:蒮mDW^zl)~hOϾokܶeͦ4XKvKtCs굹s'Gg77Cb2yd$ 8(hJ\G]0Ǭm|XL*:rTVvv/<8sS^1NGDH:v~˿^z y״!>5N͇JիR2A2P5-b[Eeh}й"I՚,u1)V楙HȲ2p}x*0PeκR;Vܥ8KUiA*j7Kr)fP%7CF1R]ۦ^% OKk#'mfDo~E8T({GzS{ϾoLo2{O.=%]ST ܠmVVV~~g?y+"fBM|PRͧ"h}µvRϧ"4l!p[U~9,O _z*x}Rz(B5CM3-|d#b9R2%bDD154r1A=?dspPGǏ0ROOIC29!.!FjaĤ \5Erf& eՃ! TiL)""@Uxﺨ9LeCeA0%Ӳ;ɊE=>~pQ# 8̿x6g_3>x،i5*Y9. d2oߡҟ\[=9(a'ͭEY}O_WÇW^&D *)JRf'Wqigw~{E(vϟyź[Cylqjq5QSG\ އ^1t="A/'cC++{_L  ã[7o!1~w>lܥI͓'+kq6F-.=ydc}cC,!uk C& 8Zo쯮U?JK&,۫##URCBtO3Ts$HFac4\[o4 SьpQ qUv5!xƳ'?_UՋ&3jg3`Wðh|&w{khJ (zm rNM \JYr$u%97=ӥQN%6劔y}N#r_͐fFM3kЄw֘YdPŶ;wfvQQ]r1?UL('JdhU'?y7{?zxr| )qGw`᷿m~i&a* p8zpmE0 ~uem|rtO&'M=_Y{ /x"x20A;ݳϿbDCDCbK!%QXKW&os¹g~[]=s"x7]>~{ΝEm+ϿOy|,G30D1Y/G~ 7^GI At@Eb0hGBpdr<d6gKZ,g\p=^Gs/dihE-E1qEBH\f{{K9;GD]Q&v葙#$.()YL %mN]Զ2uys2g3ߟLf)'GHĮ\) :r33g #2zEoאָ0 ?wZhNϽ ._| _yw*7^aí y}pDE.JL-IRmAjD2ڍW_R43vBAL{fkֹ]doѣGp:˟_*GE E%m?[@NaɸN 2W8WUsHze J!9Dž/$ɇC&Br\w$Hlk(ȕd\PcAp7|[Ofw 1Y"Ѫs+vZPյ+ʪ*,h}Bqx9.Wm_8_4>~RuL4BpئzQ{wnm ^{vmu \1IB^!d:/7/_9|Ubjۦq节3Ka措!1WU?5/:m8".SdDb%Tf;9t]z=J@n[K򻒐BnqU nzrw_. '''EbSwm/ 1礚Ӧ'?%w[ec٪Y@,^_b!p.SO%!#HCB":=>{v]dH if7nlۨQTć ߾~;{F7藡4mۤ, ׆k;:>~rxxseDEQpcc}85m8,7z>1@$b1F |7.+ÕO?7TEX(]!iJ1irCADPFޓǓkׯp3P%@(hϛ{x{3Ϫ7|ѣ77ڶC-̚Ԫ&CnZRxys9ڱˬSvNJdH"\h&b{{Ԡܻ]Ǹ}dp' &,xpΠ`p>hT=S5kc4ȯ DGT1#t#r˨*c3;dv9>PL>$vT9IB!{grܩ"( dGFC$1s̈ #^zsn>_૪Ď 5F߻|R2._/]8;VF񱪜pїbǓEj9 :X Mə(uD! )J"W_|5-Hd2ȴӬS3$9e;KsK$"]3)&Qb5M YkΞ&!=I܅rcgK ]fqp8XԢųgoWh?NWum^pBJ-&9,*T[pɁVw1zWߣ`scˣO@~v2=Ozted|:vTJ)n2tg2kDe3=YO)%-ocgVi\Zܚ;, UWz@4Mr88{}_lmuuRy5bZx?9!.}HӋ8E9b#ћ=xx4> Go~9G,7)`C7QM,&hj/t:9;w]XDyoE *1ce f@t#G8BQfv;W=6>3!g Ďg ``U*b`.:06r$v@ Fb_蚅cN\!#:"a*=C+WWb63;[;tI(/\Bpc "#t9!XQFc 1{g.]M:U rHltI0@MZˌ @ADNm۶z+wU.?<1Y2_++p4`1߫Ƀd5'ǏuK@5Lvb<9{p|8Xn2$"j8U@]./y .5m|tmFU՗y[Ԩi}ccG{>xM*@Ҝ t z(xlJhf!G|rD`1K0 FBEG?z2nöcCv7o9{LիD  r|eR(v/\ZY[{>x4>{6w #?:zI'z•//zm=L˫kf+jJUQ>yt1TEAmݴutIwnn޷m{"6/'"3QMDXV"lvŝj~8}O4QOΟTv}$7Ɲ̢ Zd<@i=i~|P3 yc䌈CGf18O`!Kyh!8"RS sC`"99FDώOU}\.~n 1#$0)*p͕5`Stޗe?󎘀TLAd6ATSuES0#d`64f7DLJ$[.2@'|ҵןԩ3rOf֋a6Eipk"_S꺦bǏL^oT;ʊŨLZFȈBePz›7 rv'y;MR4IZ"|ͲZuSO&mێ6Vlf g[رcɡL @0(2Zjcp1G<|qxkb4[_[1R>iur" fn]S:4 LrsICi37Μ]$5 6$ż#0 MیONBm]]h~~o\s5 z"DK ݗ4AGQ<@A2 7;~'O+ܗ`SS=%=S<$RgH.۞xswLa%Ջ+~I:35!d`4PC54BuW?|7^^gr4\]$ŶkQ͉K]Y5,5=7}睻wLg.ټ}hу_9:[ Ja+)gS"qN0_}h4=[;Gm;nQL1&d[~/./>#?{NETr*R*(@H Z'mthv4JKׯllJs_hYD?H+{٧O}huslE%T-)Dr+{' Fz}LǣՕ'E =ٗ&%;RĹ.}YNv GfDTl&}$L b@i%%Ǵ av TXMUPm=w[.!yfBpi]zhsngTGbk]=8ko>9h{[>s÷@_! }I Uvug59I-_E}dlI^YJwd>9nt p2㍵3k[fǏñG+۟?~ڥ ;Be&j+<8G._3}K۟I224+kPT=RҤmb.ðKQY74z޶a0&.u]1hp{`yfX@;Vb4"er<+$i<[C DS2=in2ZdI*2sI$k*Dbvy%&fd3cB@UtYC}8;{k.{U)ûvEUzUul^<7_]|?{- 7^k/ ^9~}nz1Ra3S9f_C19>ٛY= .\t>l>1UmS3 <M/6"RQ@`A$*j<;bLهhj1)^3Lnпnksp+}laQ۔̇jA $FCbfbc1KJd69̧˓iQ+Cs@ 0ZR1@Y}tԯ-h6O0gOJ"KFe:!.!"yۮU ̊P({&D$x8j A4yU]fY.m\%Hޢ""AGv,eB&vCG53˲ VBxʒ1 H~??_7߭Y.O~{:ʢ*MҬjĮM$bִ]lb:5"fQt][ êX?FT4L вYON3B3f N:DPxh{%/Jdpva}` ;ZL޿t00.λżW'77/?\(i}{d/\ KgO>Y |tnZL0#ţ_-?Q+[sJ=^XHTܤD Rp8$vI6V-޻u@gE Ŭ|| _|S4Hk }pϤimYueQJF}Ny] Ҵ;p[:z giW׾#%I)ve uqd_Rԉ#c&,Bh|Y3PI ,c9,ʄ@L. #>3u 34f~:)>©cY_,{h2stf&*IH9GG'w~&D0Լwb3i拶>o=|/~;|+i:/~/W>._x_{WYϼ~wO4әqcrAL)*NNbM&QouԵXL)2"nTa3D{W_۷^Sdhb`` (/}]7)%5p@皶$L3ͿjRbص]JȢh]^WK6m; >-g 8- D*"ڶ3nD~(:(UUJCG\Qf฿mrr|2r'We鷶,tEK]2}n3f_I9\FK^Q:g\ .JCì1a2L+d2@$d3Z54,Gﻶ% iC$Bb@clȳ%ф}vd>O&5g5E1#cW߹x2T-0&4#'@M L({E`KQE5]Uvl  !HFQ`¼M!@̃iڶbo0Rtjb_UB`(|W$Aώg.d/WE+Ͳhml8Hbp=2[2C(8̨ ;9|0BDfWpBQz.1{J`9fOĞˢe;_p޻P`! 4x.*eFTeئK|5%2c4!EY!1Adƒ[7v.L?#S g?~7Z](i>%T@b׶9I!(]&@Y4eF'dI{Wo:WDé1F(NaژmJ3@c.ƶmHl)3*q0,ݢmllY|rއ|9b(<UXU"~[gWko7<|«7=sdXф"rC&91K/3"P5g㻟~o%#v%_fqrxxԋfљhf72,> V^/}I(]U}sPj,d`ى "{&_20{ 3ZZ@)K>xC {GPEQ zJH%'g,itkg>lUjm۱PfYfW< #vjFLTVdT(C ITTkm|寬 ,vs.c %Påyif,XZM3[RצԵuRRAIUaQRU^|L`>.X*ށt+C$ d:/~wf7o޿{޵LJ]l aPc+C8v~ IE2(zϭ4䚬$$b(?ѦKTzrus^y ͢iErijϻRT7%0MƳz._Vz R!2* AI̛s,z!z:t%4kib6c,Dibǜ̗,ьl"kZF.%OOTN2Trũ /xgw5u),"hF+_j8]Β!qVX0e5L?n|jOjA˒*oMQU/gwOo|L6mvi=ڰWfg~^VlϚ!\*Z!@'{oR!t2FRTrU~!ɒhQ ѻi\{tܿ9=yr2>Tʱ31;"B&9ClQL );taEb8>NM[/ÓtZwEbYؼtG:GMiɼN|PT,f!f-|`Gg(3=cQU@hl1|~{BPcC_R|QTA*#bDQI)ť< B2IunT@]%%#ʑb h€a}kW=t?{_鿾'ѷaʫ_Þě~@y cRJ!aE(pJTEȑcE(X][ ן}JjPKcv\~3/fż`McIDK]1E@5 ")^y4iuBQz+Ԧt(C.ڶ^Y]2N(]{pomeʵ߬DŽD9j:;"XpUWyW]ۢjʉh}_R^dA %(ES)i뵔M_]f&I=ɂPcWahax3J(—2N|ڔ9QZYY]3:f|p:(ٴi^E2.ò~W#XX|N듚榠Oh k;$ g#SRdFui0{ <\Y]K024"gfIR$=‹Gj|vxQ3_ht~R`WTkkY4e $3&`FH$''jw$Qt*) L [ĔfKfХN77~?Ƽwgv+{J8k曇ڙ`c|y~Y }XUE;{H$%`"rpPUN@ gEˬ]DBCI%t$Bl5C0"FbD˃ C02D`, 2pv{U@M,P`@"3R5;Riu!&f"eH*ڡl94q͌ Ƌ3+Wo_t:_3lVw__(v]+R%e.74+8GiEI%ޫ1eh3회VSqkT5fVa%b r-;_t]Iؙo΅aD(.Qa'ܿ`a?{Vi.huT0 ?;>:@ VVV9=C?D @rΕL}ڠcWf:<9ʵA9Ƕw2Q+r "hm 9Įv4ZvnQw1%=;$)&%̌hT,Q r@ʶ@"$N&wvw9>g eou\ N T$Z,_oi\f6],Ul#SUEbW(v"cPOOc({XA p6:_@Jes+Ljhp9LY%6'Q!Y^v9H41.jLZSa81@3_x^b 1,GD5#@ O=DRbł|=wpcgn__7WϞ^x[o-/5 z%$ *3CC{eQM]32 *:Ǫr*PaSd/2"Pr FW>⋟[}4X=QL`>?w,#<2^aI%-v2v];<97X_BcOհ *JXP)w"0Of[2'bI (`l~ **Ԫ 0S(]',?>xZ,|I ̘sqHj")`>v#8xE8#.@ GhVx_8B I\9D& bbUG 41aeHՒU|2,XʢИk[Q"3EFAC4ѥ]X=Z!u^荾'SlɧO`8]  -F,B=hDdQd H(8rEY4 tE=)4RsD ۘH,EFe1$$땦5GcABn,Z/x38\7IZm2i'cTM{{[ۛ:xx1]VOnߤ.1 ZR4`d1sUuZ2w M”@dkb;pe.5eQX(\NI7:fZK2(?!JT4-|AUzvqث \D :dؐQ{fϵVy^2iL9eH{'UVLG~?-¥3?Trumd|٭/.;/b1]ns(]P̦* KiVg:7Id',s)RXӠ?Lf$Y4fZU)LgND`4UP@g \R ÔD,E(9 I3•/}gx{}f͏>x*`pjS"3R2J]+hP%&CLwp H՜֩.oUQH`j։VE>[zg]x睷!:%$E&mEQ3BS͛giZ>犲^)e#LNfc-~ƨl2qъ| /ʥkۮe"@L1%P]KhucmZ W#pdj?><~<\?~s,jQ%1!Ք#.R_EHyYƢ"cd6,*wU0B/eNdf%KL9(3UӴ4*x1`t-YOI 0Zm[#gnD|p+"$f)iTLB91HBÕQ[7«IZ7b-dՋ6onls`NB[ffU^#g6"Ęx7 Q7n>]E2b:ypnlڵGw<-'{>܋.]U\Wh |,W%H;[~jZn!-aD$#\.'d@P4U#7]%I&p [M3mqa^ULڒeA1N>Kׯ/Ϟyk_V>3Ce/<~}Hf'~5owCV~Y⢮V( ZMbQ4G}+ ì6@<9%L0G̪4 K/PUeUUݝLfEδ[Ldҭ^7!]wEŌ]'{ݿwOO&m\ַv.\:ꙋ\<1;bPѮ3љ6;>A,OƇ7|bw]zoݹ~+(߾ܹh9Gdɽgwv?z`!?l Q,.Fd,Q -aڭ-LjzH#rNxMfGYƍH|$}0b L]gs0A4#2c<19)3L;1r2HbLuK*hD$+*uic{ŗ:cۯzlGI/4wNj9wRQU]0Ade !aݶ\HVM]wK:vWWG ̃|N.,n&W#H$ D6~tB.#2r)k?ۿ_7O'7{>tTObmmZDU/DiL@ Dᩧx.@r;#N4Cq?"Uz|6wW^x~}upӏR]OŤI>}G>{x{'Ǔz^cmWpapa@&I@c(d!/۟?xΝ?mn~9w8nmv)lm2AL]fEQa>~r I5O޻<1w.qh 0NB U 2GG'WEx*]Q1!q(Րر f fV-0] ;$Bb7FH\ၑwZ@L#$FVI1iJk@tMS/{$N%&CGDڮypNpPLJӃGء!ڢ'kU"9 ]+_b@تW.\Ħ1nD DKhM&6/7'~>>bmuBQ8|>o1r*[M /Z8rMgjvWQ:vޞ_ 2(|H@U8Wxrлzڍ͍w幫ai̶#f2mYj)2-˵c3!vѥ?ۛgΟ٧õ? ') XQ,2PbK]3O'U{Wo׿Ջ ұrR2gE=DMDU247,rNON>cNrf{+ jl]r{Y'7Djmms3W.=s̙Aph "[]g#LV`#oww}Q޾G'޺ۘ:;>llpmpO}+/eיmI)u-,߉ :믌t&*e)LkըJƈ)@&+(J vl:$d.ĎS|Iɮ9bf,&$uL+rZfRqp>y08bB&3PJW`f DSW^-ݼKMLĦm1uA"8blK h3._A$_qWP_GW_u;s>룦D61e³[_On|4.xM'㓓'{Onݚ;Ik̻ش8iZ8+Wԩ~>V77x{ku_E`u4 ᐼ7dt|bR:IQRJ)vITi50@jZv򕕵Ff,>Vs/\?g''o=yd>LƳ>Oxө;uͳXI= ++aj҄hgV׋n>7?idWoNN^ZڊVQ5\ {%9g`Ϟ"@`>Bt!FY385hhQ#zk%WUef$RbW7 In>iU3{oTuP\3g6OÕݵHrYa^;[4;9TՖU0fbĥsOTB󔳗cKF.ͭ < b7 /iJbe$0hڵ`&K,H""F܉@E#e3.޺Ĩ][Y Wˢݓ'I}¥p=iԥ;&W *m'nֵfB*ɓO8W]-2Ŗp5^5;x9ae\);LDH*ՕlMk++/Ryū?ճgϭ'ψ!EPM)>}Ȳz]$dw槟~ 9w6Ⴧmcحml )|K+kUY,xr<]8{W$WZQyvGnl"2]GD '`VE˪Y!)!eKW:hι$RT V%q*&F0"1yC(t=!0e1Q,9Ct9tL,ITTU MkԡՋm*#1*hl AK@tιkW"(9_ dBB޽p$w,6vRTɸ]Ko}ѻ|ޣGUkYribk=pdpf&d@UU%AjC$ @ЂTLL`1xL{o3[k  t?ly_mm_[l:1Kʹ 3K `(Ȯ$Z}t2ioݐd@gim;oݜOnM4fK:_ 3Ј٭שyF]^G-|kG/B8>1ޫ^q㰙6yU rqul ]]^~WA EYI7nۦ ӪQ!e7f=BER[ DT"8TRhaQr[T̡ 2Pu1HrQ ~9kԌfB )!!<MM֝۷oB Rp9MX\ݟ/omSE+>"͍B*勏>~z׿;nΛ{_" ~ɇ7~_|\vv5 !Ljc]d_wb)jJڽ)+T??\,W8߻o.NgǦiMUmnaC,g˚S4=1`NM  Htw>ݞOzo+wvwoܼwȳ;Ymkș uiJ] S#iM](ѤycOo?ߙ8zN!Э;{ CN(FUǀne#X P*B1 ș! 6 \-Ζ7_yKQ2"!m T:$yg˅\7U֦C [],XmC‘J /K:K4܉\^ց@2Sy2 _C|""d0PG,0Ra!w'/^aE̜@SڙP=}~{ecl$>"Aa#ݾsWWrfº"WO9yd)ongSͽӓ?]Ӄ=To>z}Li9)Ofӂ LG2+RS$("0dp''Ŧo';7oߺ@9%0~"vPגBd6stwR+P rQ!"2;9~ANoD#dw^Oot~{{{{wICŷ[ns\-hC d GLi/NdNA4lLZƻl{wJzUΦ)#q;M[[[[40Ս0%B".K$J(aSt "ff"T$-" hKaBab2d\-( BqsPӄs;47i6QS"kK)@bbvrn(I'*T@1JBLNH !8~J9kX(!7ª(A O>|[oCZpI 'ϟ߸ַ>O߻Z,n߹;_XG?;~:_o΂AMm5uk=JLU'vqH|~eg[_ƛζ&7"bCXAzR U˾i˓G?DTA\˗4a[mqY"+ΚVC1TLJuUewY4ԥ ˣ㳃=b9p7lRuV~Ŝ՛j7~ˀg)Zw 00l* YKᘘ BwڸzbE"Ln݉:=jYa\h*+m 7=zџ%}?}k`1VM1gi* S#[\irA  `I췎,EE,KlR(Rk"!aIYD7Z e,c\ۿַ:Yp&`hBleW ,NE)p2ћ= *# QZA71ungoz~S, x|۩O*9L,0߱&6csaCf9b7/f fD(Aծ iͼA<:~ٱ911;8x?W^ˇwo/˺nHyƫ~\l֟/7~}~|1!!`u;Ie';$|Ύ|I3iL)dB!&Hʷ޺Hy: !7*wdsrFb *$wc@Fb%4{TjRFL$2j,Twg TJJ!{NL{6^Wץ)S(cPU_Gt\ecC$60ݼ F[Cd%U찅|vz):7=``_vTڠ9)Ye @miӨ9f7gp&l h([aS11QH׏|^G"/Onj'+57)[]\jJ93f/R۶ho}O7^ AݡݒšS$UPZ*CS.hXUYL[_8/>WCznj70 "Su"ȈjDU ƋQ65@$L8Bf"Xս5yXϧRc 㨜՝ lǫxl6ϟ= tyڷi[[U l1vW__\]]^^^/M3dҲn)TP^2Hў=;>=t'}xutrav{{vo5f. N܌sNP &TMpy3cն.\ N$$4K$3H7ơiՔ 8f"ъL 3piF+9"%3{\h6!j&ǀlsV&@94(z.O'1C27dd <'4PX5QYS0% eGǃh.);P?{ܥ޻?O].|ckn'=l6sOœSnt6w =WwwQZԼ! `̜ѳFEp󺮷"W˥T㋋OWs6T{w~w2Y7 B!!פx'|}p;irWCBF֔wR"[J)d$M׫YT]"&`O:d3pNnC6VOݾBc4OeGHTb0A$4m6{8f&])qZI-UyIi{7CDl4Gub})#f=; Cѫ*XYAFҘ ڙ p) 5ϳ]q˞bfVt^HI/|]`◒33DBwCBHOd'wSˁ HiԈ9nPT8Ұ{>潻,*P=k$t0]5L s <-XAx@V D NEnL\S@l2O7C?1բws=Ĝ~|4 ,$̔lra Z, ,~_Mo|3!#""&|:Mg[oѻkjM1T"LU͝f4u~VI\_^OOW]g"S]#sesP7u*/㳓#I?mÏxkߪ+N&[;[N! jѕ%:;r5KH!N Sy,^%xHYHXP)R崣CU V#>aLFĄY\A4T䖉&MT)x6A2!v2wj!TyPW s$ X2TlHBY3% 1ĊTYitnju)v'?;o= 7;Ӄnsa_{Ƹ|2M>x@_wS(ZF07pg4C{F)mom 2jtw+xN*U71)j{'YkН5UŕOϟ>?{{ZDwVc)8J DIR5(s ԢFc2jWۤUji=*=~7!)s򃑹.ϴXYVV2i߇PsZ=^(NȹGctSg2RP@ns~l 2րV:&qf,Inf3 ]'u">g[_lM+|j!w!.Yכ߸|^uqHU%a9zp\S8,d pno~_.>7Ϟ~1Uʚ__z`0}eY^fz٭: >e (ܙgə$( Z0Ak`Nnj+y7knM;݊aG<Op_Lb@)AfӕD_Cܩ}=ުղ:HNa)Y*b_=~uCXD0~^?iᡤ<00elfY]DsÊU 4OͰ5!O^<Y+l?vҴ7v=|{{f(񔒻I(@  y޼&f~(g价&8NHfBv-AA(N@@RsVZh^\*FݕCTwEwu/>ث<\0H&I᠒SN[/?dX/W1ܙ!pAj"^.զ@PQy_ftLf{iVWq A&[|oQL:Ju[67~ SBbMJĖ5!*t;#Ȉ*_,c8\ I*hnh@$DUCJ"ŽKC @8vDgrr 3"2ȓsLn^dSEtAΦT.mT)BBPIR@&"lX[< !0c;x65Η]G{TBԂr6˫ٍFWj=U}#CYW`!Ӡfi|3I9W^!`cC<[nrĝP&`^Q3n%6W/NnBL"!Hc]]ёPH3 )}٧O޽SU\+Omws&<}x&hԂPh;2:NKͯ]0E'03>`I֎p/=l9 äٹqI]>Oyجl1\\ DU?VSMmSrzcF,3#Xѐ\"*ӳ~diڰ*RD4,#ˑ4>V y7ݢ#NPMTuĈzӭ׾&B(f=R}~|>63tv߭gHHZmBDNHj׫eiM?͈wrxU:Pt2!"#[4,}X&$0],3_oLhj)挥tѧ?{Z]\l62"rӴi 9e\..NON^]\_l~W DAc*p0Y"+A*6Y Uu^z޼}o=Р޼mU+Sw m" : H-ih8Lj| a",t$l8*/ɉ-UT]j6o:0ќ`J )Q3pb)~` $H;l@aԛdHlkֽ(UTKuf˹Ӫq<;>●^o|-!ˏ ۻϟĥz - RAc?dGC$C HRk ]Yx@jڭ__:0a Mu3ڻubz}ѣ~̈́* =9UTuѣWHҤO6!1D u;O~χfK9MKa4%dqO>9^=~H/֥,6VS~-$cpSai]Rtm/+2dF9!nTK'RIDAT̚UR_-jBO}LBe~alYEʹG55Ou=1/Z5!HLGGۯOx܆mZz)'$@֨\P͚-9x6U][! 杭|kY߸w_}ri_=l2pE,%gH&쪁9š_o6]]-fن!jd'd:9qu{008\ŜJGL _ыI 5ˍWTX bF2)+tPdvrߜvf$h臸^,ٳG˓ˋjE4)`&vUε !$sG͚ 320e@xZU*Fsnb2w̓= Pp3|Y,JH5%3$cڷ٧./.rr[w% hC~𪞆r>ywGD&jf fy]; WXձ:A@MRŒRb4b/{I MHHp 1(TkDdT1fAȅ@dEb)&JFt˄1TT%v7on6q"f%!SUnb>Wu7|}gk_ys:w_{ly\H-o~a3g:1mzAY=Ab#:xR6 CpE\⦇M_\AmˊǧDHH =05KBdOg{wo0Э=}+ 2M:gOn~{~5Ҫ PAQNώ^Eݮ0/i4")x@1lzeF(noao$椺$vo~o[ozcnɤe҂-/'l"3c$C+G:U-z 6vZo.wRUpo1SLRJD%[czbgJP~B$2޳1ob ed1T> E׳%9Y:::0#2:_onش.+a݇ g^ǡ)=r  rܤMl22 t5M!:*YZn =q8zv-2$Q7m/1/}n^un4+ Ĝ: \Q63[Z]?=_-..'VۤA` V UӚ!Mnq`r@ 2A"uuȳfm'uhC21|kX>Q5 q`ۯO'wCm;)??:@T)jZwW_8y6JUgCKNyB]5<[f:;X]B%\jwBfJ)EUPY>n8ܝHeSa&bjF F:g5 g+zb+kBA,yS9' eB-9K9@B!qSR! I䔼=V 0x=:>~q8;83ɸ{%dƂz[O[7<|}z{=1yS'r_ d'R1f4'(j2Q-me's d$`YjFw )kfKEiU3mTT2pvvӇEѱ| TcB@0@&AJVgoݺ羺l'z% ..R̴3m \]f{i4gώN~aNSLF !8 1< .: |لQK `bâ/7.~ߋVH[1 ˮD(8͊qV`׋n+"Zf2 Q*rBF4"WODuRxƥ"'Բ5O?|Ɨ`wkH'CEll 4 ́ƝuvԔ f6z5d ]sTBPԃnR4NfLX,%HSU;?u !°Y|Lnӥ<`N~lbٯ+h5iCS7;MiPe!c)`"VN2YȢD"đ^@9g7Д a>rHEWϖ'ju~qܬ]Q8l Q+Dь+0DԔ'프RBtd2i[0G  ;rPUfoo#4MUP Yi01ڃ]..N֋uܬbL@&MevSUImf-:@fy얓:BΙ Lܭ ,9TIZSb`8DDS Z" 2XET!@E3$XmBF&"\ |0=}s2>7oFmLI-#!Sruw$>:[]~.ÐrgW'W|捇#92d&SoD8o!+PhZwv:`O]}WR2@w"Of#j%@Urtv>xw&a 2! {\ 9gM&O??{_ƭwdR]mDBݤdA4I=98yv_m.V+/>{$ HF^`et KV&+p}]_ġ4v5]v [+SM۩KOϕLr5ݻ:?+ Txr?G֝`63K}P iZ]Jje@:]pwImwwӳvZW^;}hsl͇@$ HWaԫeyyE,H(Ð$2`a1Ez3]Uag"1dJ)E}돞kv1s~~'|}X֛bvi;d{cg>U]0zu@YU E?xomͪjW@s9dh I¤ U3ib=ZI˓/ݦ?8_u}r,Uk='%*4@Cږ64턙DJQDi<|< ݙt{B@aXd>L((5+"XO.!Pm6 լmNzNU?89;ښ3R;n#ᶜnaߙuL8捨 BNݝܽ V$s!TUA\qTʖ2S &s0t'̞Ԑ8g9n%ܞ5c"R@62+Jiڏ|0@w ܾoG?z;:"QD1{ uMs,Řc^xUf,j/WMG|WwT{te )F^aɬ 'LgzY׶o@]VxׄdRIfꀎ%rp_W!4u@J.U=BUS=9}vٗf9F`A!Lj# Г=~lw4G ؃eaUmm6gb{n@;i+"֜^c ^E]Blo}_Ϧ6QqO/WS@$'/"TP8ƲV;/ƄmfyRW)\}՟nM-jw597Hp |@k&80s)~w,0[g޹4YeP:Hp;ua :!JsD+T"qZL,F5bayr1 르Tml_~e?~z~x6޸wnl>45pYU2de$Xc 焈HNP:4dFnX hjĞLΙ:89 4 _Ջjyuz|qq^_]tak7DUöPS4bș1Ǥ;9Ed$P(mwM۶)t""{xdԅ!TZr7`&4Zmms-Nl{[>=X,Ϋ  {*A9(߯>7qZߝ`Hb^$ՀPoHtsgvZTc^uݵ7 !XEsU 8#HU35L|~7VCŇW?tݻhf[iSWQ<c6 %*"eX\<ɴ?M7ꪮh ͩ LEZTs:R/NWً<_.)Y\C0ԈM:jH}KUM 5M+f >LR_|=ib\tH&Ovt9haJ3ds2>%E2"x`f7'󜆫[n'7'?`8jw?| 3Zӝ8`7@U%u $s=ƕ60ϻoL搃1[9e3txgoo{w[- PKmsZ.)d)6wxLGO-VnuӒyO^w20Ju- jE(r*1~G/?ÈBYcjߌ}*ܴN\b엛 n*G2vhjws>BReEZDwP a^>:JK tW.O뫫znݞʞ$岫B0KhP7̈(E.[y%w$)"s5*DsUͦW\99BvctPp!>mhg{_ƣO./ӝfRWx!kZw: IŅ933`!kL ?3@H<ߴfq>~qP5mԤLTDBL"pwM׸8ɳj\,/N6!FS ٮsrKe4ϦFe@mPf:cP 5EPo̙ wK8BD"i>*b@'`I5B()"b@wNXf89EMfuS K銣HIbfbUww6guX U L]X(eR3H]TFTٝE1Oxvg"rPS7uS.,DE܍9e",!T`q-ܭ"1c񺭤 V@7Ĭ=L !mΗWiv7B/(tf ,0.nzJȪS38z~yY6M[fUQs#ޓu(E|"`Bh3D.Kh~Ͽ1淖gnC\7_oyvPROd٭6-C ;[PX{hZ*_GBY4!27Ӊ:Z5R>q%ۋl>mmO+.|qٯ糔s"Ә{*_%*ݝBcla; Sh+2 ^>_ʭ@Nj)5A[OnEMŸN a"ikw<ƾsM)'B9o~wӷ~;gk-: @"%/6MP-=H{*ԛ*v|>V0_~ɣ/rճW˫o߹˳i1u]5TU)`D㰉=njZ}ï~AK00U  tǬy4XdHYr;%PaR!]@-tͭ7 }PGej1x("c1gbb#_Wf3wEY yk>ǛO>:Uv\?X^kOLg{!5%JEDf>y\2! "1!{io߿ma`t~y5ߙͧ[չDDF(\fN~tc,D"#`s0`j`ĮBM5 \@3WLD0{ $s˾[ RL5m֪VQ0or90l`QU7"fcVU!k pUVdu<=;r^!duS))!@tT'8 ЧMNrZD3˭KɆpΦUsLƫ cr f s*!63@q nf@UClZ7 \Jn8 "B1/1(H>J_l]B!H%1$1^א f3iRP,)*Pb @N@`j4H0t5r#J{T01*,d仪J4%1{U)Ea3[N $@ Rx DRp0"rt0ý_{wl &˘=Zh[?۳y[O?+w!!*:{v+36j5 4V W6"vv?~~quiξbZsX߸7'> {|ۿƪO\ $d("2P{6CF'_lfu&U|Xo$TRب39eb ЯrsɤyqC|േ( ,N9o@ro3B(rn$C+~KZkb-kS_}LeaVg;7nqE4)BeˋS^1[\Uu36O?ٟ?5ئrƲ9#*W&373a̸H[ppn&n&6[.Hl ǰ䂻R.q ͤqvWZJH\8`cR9qXX=':@+@ȳfJ)>^^W!̷8A.hqI(̖U2 'bЄ=9e%b3Oiꪒ MR"qyvqꎏxɰaJ dI֜A *qU]b]^ժovC 9@ Դ43\߱HzH@l{o֬ݛn3K]E:riz"W(N"BR$2BbBiV5v دհnN/X$HaJ!`a0aDG@a`Bw!G R5G 9Ĥ5q,ι2+'%`F7\6@H NDE$(Y<A(cbr)`@B3tqIzyѮ`sdݪJ('МԬ% rH341102TBUMX>kީB^bj&Ľx[|?OWdG?7Ӑ"d2P McmqY]ݍTU Pnܼ<==~q~ssX|{MvQ.tIZe{([ݭsP m3TY󠠺BWϟ?{=с Aˡ \ΨT*QBu Cdh_8U] ]SIMzjޙM];'4UvC+D/ueKtP (0c֮(pcw7H.2ևϷ~ #m cIĀbdU5055%bVD400D@@305#&"033$$d3#DUA3dBC 4"&QP@$"U@05b6335D@ȄPL 2Uj9^zKw<;7g&\vME(V "1"! $D@0@E $cUP4CSN;C>B]z3Ved퍟<3O۶qҫ/ K^W73xY5l0f*"DBHuamuS0p?,ĢPU HD$B1 Y."#QiwT&Abd"RJj"B@S%BD"$S54`BD? `ꊈJL`&"fF1F$ XYV1uR3Cc"$,BEՔ90sYL$Z\[IZUe @D̀%pxfp4PjT2HNlO{(DŽ[g>Me1\Q AC46K ݙ_?m4_q5H0QUbݟ sDjH Ko?^ՆVST'tE{zj~jw0*Cmo5B0P40gB"!o  ̌ LT_ f` H9 !#nL_EB4SEDC?Eߡ|5C&|==o~uXc蠮UA/;wСmou$Y 4UE"SE`[[O6oG>rWzeu!!:cΞȾ-eaiYL!3ԀFLh`fL,u1P`b,ZB]fIT QUJ͈ш7ӟ/@&30``DhHDO>OMqCNT03I/?:O\gH A%rtz,)(*cou953iv6T*vYX^|3{.b\X[*ȀfBPUU%"`@ I=[x O?zUs*̏ x9;!G "B٫6#$NOllQ 0P3@D@[R!e]_-.|cgxMAxT%Q@r8Os`l*"dnwz{^~7]oV֪ߊƢON. 뷏?tnw*rJ!"!'AO2!I )٬n{,CozZIbrrݻo*haIRש9 rЈD՛P5D0B$SS3(@EDPkbfjH\trdOBdjq0c dMB3#f`f hf'P:Y%XKZ'O|yEqԛ7\s5(0s>~#Hf TT$W[HfE^yn["+uYY%2$UUVՓPzǕMLVPcgW^{K+܈Fܣʫ{uMT˲B9"PW1`u]_oO^y{溮XD%`u]("#0H]1x*CNGf!X+$Ud!&V LS7b !UD De$"IHLf&V511C0UIuY(JJ̊XյIΞ:t4 ֪T`:7331`©[ -Q"Q Sc^>_}rݥ3Y- !rL ǎQ5߸ck1֩F*CWzz`kyS 4$`dGϳ@=t!aJBFL$` fH(?³^~R.]Ջ[3 $ʐ "! Hq*@F!AǨ|E g`&1KLT LE fLR@f_6QU)r0$$`D$ %$5c"D$"!)1a1@DA2i!IA;hf[,/W*IU@DENH%kuWFkӓٚ  2kvN;㵤VCz۷mb!pMҾG{;\ß~v_rj-.H'Q=.82U'!0rTL)#u݌O׿n@u8H3HbO]D@7)4SC4/v,A7&x !c6D4ZN ȈuYKO>D<7;3;ٟHD$FZ+7/ڝ^x |+_⺛uZY^QIH {㽩W_}{}w=|_wV gRCFsfjHā}!aӮxǍbZ[Z}low=#$۷^g['N[fd ,";^4.aEB[Q WLn$؈!Y6O\2:9SJHHHt!0SdfD D^9~j́PL}O4R{''Nn 'gJA ګ>0;5iwRRs5c( 1\1@F+LJx|Gx/|;VymmPKK':vԱ?9V3bC3 "S!͛Q /7̂AXש.t ݽ֞=[ݮ?>6avmur`DhH`T(9JTEUSU( 1R`  4Yb`JuEHEFh։CDw?RQ.*)%@V25NX"̀9 `FTc輚BDB5DE qfĜDDP6#zFUS L)U *IR Q[]^Z]^K#DQ"CLcZWR$$TU*ZZzu F8vc7S`)* Pp@hRٽkn}G97>3;S(`t7n!ܶm\uS[=؝?xUKJ"0cLz<yiQg3hPt0!byH:̽?V kBl<" |yba" gɣwGf@$0$uM"5Ն )d.TI25w, QNNox?ܱiJ]3mLZ'/?<3~;S g˲FrN5L)IV5+l !r1$)ltE5K) @(QHbѰ,G,!(( T%U5C2SDbfk4 C3疖|mL 8Y P=Zj\̋?rLl8{_}mgm۷b+U'!}O3x%?{CЅeP XD䰕$3)y׺LcΜD9OHΛ1 $A6CPhh oC| o)B0̡pLubdZ~עFL<#9(NBBdLTSt;h5S8b`>t=ߛhg??Doyyu8Bd2Ojj33g/tʨSrF'B7$p!<ߪ`@͜>@(N4 40(gϜ+b늫o|楃fZCۧ7n(Ǔ)'$/z!DT ,rfjTjJȎ6wpPRU~|}N Wdy[o:s™s*#":yA@SZ,%&s>muF*e-( բ(6nz=Ħ-;c@E4#  g~=]s?AwkrtnqgΠY S>}ȁ!P,[("u{i|TJ߲=sUUm0GjRM;wlo#ϽS0>3=ZN;5?7R]YZ1DCQ4 쳿\+i (v)HjB ")% V-_ V3efP$!!*#D4 0U!s"D03@93֙Ŕ24ώ3CBP.?b7Jъ(e]$IM 4T35Ve9jTZ,cmbTD\ILô4 Y c'N.;t843?펩(E(T3P%ٙ /7#7Ύ_x[oְ4ijO_<ԓoVW*E.CҤ 4Bw!d[R5 +5/"{k8\l Aކn;9&ЬyE1A)-$9@PoQG|\"Xd'4cm|okSϩ@P SE3$%F4Q4UEc0k ַO&O-.Trx7PO\d t;?ſز⨮J0c85Uh3~r.OBU]9څ$T< g¹i8`N(ZAv_܅W\V 8ƔѹM:̠ yk9LD_?׈u>>20 )PFE3pPo˘;eD@95,(ǎ?+gNJ&+YVAFl@{v jA3"d&a,N:}~TDc׿_~eđ<07ӝD gO#ZoIqZ[l_|y8\8?[pK`Mou#|voشy8"͛kϜӲ LD D$b'ٷ:\zvDOJ\QHDS I Pe4FTA,{c"Q$5'׾*L gS"SBQTKu("qĬD1/\n!B]`CH)ydbF$Id4a5ZQ`Q@#DV4 !2J0չ)T\hQ1~pft'N2DC uUu{5wtb a g^x3?qy?p-P'ʪVhu!r. @&0XYHH ^"eju$6Ndj?X9 rc( S #S(:9X#&uF\3e^5bS3U Ln]WD FLL*fydb#șɵ!`DIU&"fɹd6N'z$,i?=?z;n{^tveeeeM_Djİ..ɩҫDYWV&ѸN h\?Ol03==15=qemݲy˶͗_~{m_<4 ssZH"+.@i%XӔrfS B4WM `otE~~ȑ}HC|s#Qp҄)ZŻa+xձm|?vV.l_ZgEd$I)iJF̙>$$N6c8}ȶm|7ݹ~xוW=3Og81!UDJo'w7]狟w}2>qvie4#8wmhrro~ك)U\ĵQ\<󷞷w5ŠN9jZ:!2Uӆ٩ r sVcQzeˆM{/yן;7ϦTĹ3IV+JRJ5d!G'LIOUQ ALE%4ASZ'bC .s&lO7ĝzTYrqfa|С6z:q!$Yycf<\:o\n̙AuFX;Rr#GڈXdifQxx%f>Cbʜ:@DY\6SO'rfMy ڲn՜HpȨIWzRSUP LEU*ILBR`]w ?UUBbl813O=o||;ۿU]ZVK ˨ΚhbX&7f:! I$>w~ⲽ[,|{ կ|[ߛ(+!33[wزmWl'Lӭհ]vL d9d T[3_[KO2هND50@^DD)brRk`B*8*jX x;6o`w~}_xWt@kM7 0%z23Mb*3AŘ\W v:z_O|«>zwfmt,S@M(b,|{~[/W<=}zym0b ':!s؝ ~jeGVMsx5x 8LgN1S0L*&ܖ[<}z~r"Ɩ"\7L'{w7ںk~Tn4srm bȩ&u>AdADɾw"0[J*)2q̻%rrTI1@*Q`݋"J̱(4yk"yPh"q@"3e9յ0YTNL$Q`f221BL0N&Z)R2$Q*,Ց8#D2hivN=v͗7ύOjd6HRv:oî]?C>sO=н[f7|SXXZNUNbW}pW^p.:R2A S2yWrbRu'$kV9cUSg+~r4dtpQ詩P2qQ`22!,;ȕv#[\/kS &_ODκasRD\d86 e KU!H̡ 1u̯{ϳ>::yD=>uo;/ܱ_һ{{pvqI5sMAhQW2Ĕ4 "sjvzo}[?;os=^W7?/9|K_[eY;Pc3֟iw{\Օ{L dޥ82eB h]~ LhsO߈fؠObj> BUK\=qW^8}-?{n?Fg:jVUqyU׶g( q`54$Նiu (F `uo>99{W\t%gV{~ۻ[^~gN"AR2]wg~btI JR ڸisz`M:W&۷5ym"?$UETMo9~33Vzzsxɸ}*%ZXZ FmG $EjS!2SסM|b{QOq1 3KuESUB0D$Vchj0 DN5+!!XCyYgd@TBATW] 1; QD$u\MER$,5x`*ìr` c@d!Ru!٠,+.vHw&., qi1I`.t] K+=X0y!Us&N*hf43Lit;:RS}jo.1@'Ӭ3M#ђw\e5(Tudf:0Nt&:ЬiLFQ*J@(nLLXJDTy?3'ݿ+{ozg޺뼚G?^ _?1XX^5OfRUvq8(C3>C~{/;.y'~;r;׾~%--,-.FDV;EEQ$$3CdP$#5CmEfJ _ P`13Q15WPC-y뎈 dF~jkB4dde.C04.;1̞y7weY"ٹV3OҾo}_r=_xOg|dSGSziaa8zhIi)7MEMnp|[wG[zޯO:] ׶l|8xw/#G!Clr!ARAMU|mkoC \%e\ JC'V 2JkU3[`l"(PD0dETOU$;!Qnnsן  '{}Ѥ~ no||Ĺ3;6;QRh@4nobjM۟*T6m1i[tU]jI905 Ղj B4poݑ^|~ASVWo:TUcbƹaΞ[L bdRU!Z:\89;;מc "U@?XNu 7m26>'K7o4Ĥo=Mk!`0ny P@*K$Uv27.9zCo2l=we[ DŤ±@C*xEwWJ\m!n4}hߣc2ھqJ *ɩbj'_m3֙哧ϝY\XZXqkv~'fP'5$4ADfBSS1Dk %h\eR*\]\w:W]qƭWR<a0`rpܦ*AXeέy/=uۧ7 jqTXxlg6lS*B1D̡-@|tKJ`o{mLҺ:&{^s /='֊nwƳKkBj1bDjw5v'."ɼ(H 1ug\E``N8h`<8$jʹk"9bWIh6>kejʢ!D%*[M #bQՌݨƇ&*JPa"= XLCP5fUIHY#1Z+FX]&ZPRz֎yzlbbFFHH `_ZF緝K}#ǔdb||-H0GFN+#le]W8"ȻHYQ$GLT˩A䖸4D(U.M1C0[e1!hk*0碼YFHT@ԐIhU?yz=:35vM}Kut޽Kk|ۖW*\?T'w:cʊvwL8߾mϾx[XT6KwD%Qȷ6 ֽ!5\|ˊ@1ۯ}rqW.T$di'?:3W\{E/X?Əg͗O>gv\iueMH0z%$jU 0$̃}Թp0ԙ%(Mj:ե嵕EѲ]YIIcW^G:rhx̦N $=V;mؽrԆV1H t QE 9rD<}P.!ʣjRS22&&3/ٳ[n0

ov@={={ڱ~XV'wDN/7>|ODr9̏"s#\ܟ/4[jPJ&cb`Zj0f NyݰQu' tA3ٛ$w")Sȵdff!iNu(v_V-'p~E0#tn=xeUbJȠDq{K_曣j'1$fCU@Q3g+"۞ .\\?̗ `IVԡ/|D$xvuæ-6n#4cvn!1S{zp8*GeY1 Wj+oC|w ..HhLM  ''.+?Z(%5YžX2Ӡgsjwy񞹟QBFDBțJb=)_]/ zx[c~_b'~wS%7\?, K#0RTE˯x?H{/ ^  y<ܸ! ֱ!YQW4y|HD s4!TeY׵F؛o9=ٛ">#}wp bb|r;>V#f hsrn4 >6vݻw~"Jy;*yź (ɡ^j/批_< g:T;=n{] eb@TIc^?|쫯>^r$k!ơf A031WU\ @/N$ȒDT)Ed!iEyi1*J%&wp!"2ETKU-Ù**1ĢTZ"f xd" J!ƔD'"NuEHV HD9UY b@` C\R9 FPjF.PDL.2^>M f# ů~/^X^Y^]3ь;c^_þ{ʫ{;o\  #XR=ʹ/ y",։<|r0ooY]~}ƈB@XTN35<<̳~hc7\~;?m[תY FĬd`&hkQ +g* ;%Jn-MYH٩|!k?~Vտ`^af%#/1U15  fHceY RxΝg>sȱwfzeqp+FR\0Ӥ8ZWylƩ~NVc7۷uǦFC)ASRV("#|oz-sHw@J Vez ǟ}74)@X DTI̒G$F(&;̛WqWܢ"DKU d2Q(F€D! $XRH 7"BIB\+$U[vJRkѹ ƬTa6IumyΆb2 1`VE@B0?kPLF 0Tj,`m&D#X[AhGJgWR^;Ͻ|n>Ϟw}Df0I%y$Tq]&P$DNF^^iQ^n GB"ѬuԀp4ST0LC#57 -M >0u;yQ,xk0 LgeOsxjM[_@,$$B|fbDfPm02mݽNOL+_8u#G@˭7n޺mayum895[uJe9lWVC믺lw5X^lMja]V#f&S*^|_o-Ξ[|뭓6BRAv38rO¾үw-;zԭ|Zقaf4CM): -iF:v#(Jq&1 PCذ:;@J!v{OueW=Mo۴\5jX @6؞^N住L{o+$u Hh$6h3A$$j"bu}b ໖iۀ' }벪Rsg=Vþ}5W/Ue PBUC!$nhbʴĭ;/gz%Z*U L n#'zL6G/Μ꽛rwwzr[:ѝ{][j9DUT۝x3'{篞~|[euva!/ypie)k-ЛWkv20ϩbȑ(?XM5#1z2TQCoZ|dĉk/w{~\^\3$6t!,Q;cŕ#en&X-Gp跃UB͖.": nbZE@xO@uMYݍFEtm0 Ezg/䒲Z $c=v:]#%ղZnɝ[~>mݍ+KF)F8~b^{qloƹA]/qT2M5ĔI9Dګ{׽ G^ҋ[wn_Zx D4/Zm8NZ+ϼ˯T;\F &2B&3Ib`(FkL8RVjR& U|S97Jg1,5NEIvܡH pDDH IcQ(X Pu/qwDzTI*LE3ib(G*b!SUcJ3LcDA("H~F&ղiuZ][[^YPF12J)$MIc,a0:ʑDS GhԊqj|r?BVOo~7 ՛nXe&tF7mbNW;dێ@>EURUcj2x`Sx+4  I`h;DDxQ'\gffee 1"$CT2aC\YE=x:@Ud'8wCcӳs ZsB&HL4˲Luldlf∇u6}Cw\+R//8Zb b(&&w!~..EbMI$? 04;S|M3EcOhXp-[6mp,bW&{7_~ۺgJ5Z]@RbR0#c4)34(osPvԅYJ-*दRrp4RբhJj]|FLhu@YEB R'Imw:n|3(u $,;%!/?GחUME1lkZDTVWfh1(I4%aTYJۧr$ID fIT2֒ڹQ9`BT%94V3"UU%ĔjD@AYJUB5Tp:e lij[AtuOo7nCu>;4-M. 0:8W$7ɴYN>"5ˋ@LA-!㖦f8S2$*#s#mL (!Vkj#`JD ȑUMEgiY46l'@Bn8ĺWQkumԙ(ԆɩiP֥+ש}R{DR$RDݼʇ^!ƞ|15aKZ p!߽mƢ,?;ϝJU%0@XrU yY8C1WjJNo/<"FM ~Q]TpE~yϑO>~WݟgFaym0JrUoȉ3q;_B5#D3&mfX[7e Dvqϼ馛X\ j;awBh Ǐ[8ujB""TL0Ku۽b | "/X+QU1HڱcM~(zE3a~˶&g6r!VkG?n?;g.%ƅY1&?ag#7lT,[Nu嵣Z"Xh*vCD&v/УQn{SbIV|-B~NDjN5k/e\58ZM1fܶɫrA6`m“JJ^VS WycN QE<&B _H 6EE!$I HBI3)E< @4IA9LvkiU?7''; LC9nd`ncuOY05kl&0mUK.jf{'D$  ^5 4HD&0؈e9p2u+֦!3Nミ)3+Zy#+ELNLu]Du̴N5 GR՞kvLjyv$y$M*Fj1 ]?rC/?a?15%'3^|뮸pp""u: {ڼ1X0pPC_AtV G|eps8io;oBO<0"Ǣh%_xY>;.;mڨZ]]˻`.W=~}E-7_uQ?3RúE4;Qu#I _P5 3rY3M!sLhp [S!Ė;}zea)Jmiۖ;=* àΐw5?;1νΑ7xѹK.ڻmZ,U MC+4}l}Ow^8r/Ն@ aPIdz4Aю^ݽo<`MqFy JB3w@p0"B@ϕY2 \R(= ~[S=$ DxpTA(RU@*9!~B.+qBL̜ VWUL܌ޙۄ3lhN[E6U!"&ɖ 肧s &"EU5i3h@C E9p]9oD)Q|w(:;j]PZLZ]ZaUZ@$J-"`p8KRJ&K`c-Tݻ3]V2,K `R(56RÏ<}ā\u'? ɬ|eC؄XV'Da/+'ӕ5y= L".s`Gj\m:\}+c.:ե"X`d ) JfhJ ~wbvwݾ?V+۳eeiLB`bQՊxv~{-7&lRcꪮ$RJSLO/~r$ğO|352Zo~OKO|S;yLYv* ypLrx@n,o)pn]UNwÇ`36H6yd (C5 ^|AcW.,\y%7ʴ6\Uc =[nwM.F%) dfSP/L8Tgk?6q|:+S =oÛ5M(kX[]=}ԹSUMOtV;v-,5=1' (XU֖#vvk=m*wWƁ믽q؞vwz .B!A$oFOl((jZUAݍcN^{-}ﭗ_{/,/ub"1B9-}?*6LrwtWK;!Sʆ!j;S;{AT2rJ j9oc9pxK ESUdy%(dž !EUM5bnSD$1@:B"E4YHSc 3E 6&"J"P-M) bк&bd2IȤ,Y55qP)TnFjbRjZ rUUDlLT@E1Tu]UySY%KV $iU:ŤQYՃόMLX^6MW8c5ka]fdBhW8fc͙N+Rk nFFg1FlN.>KOu"B]B<*bL9l{c*"10RJ"&U*HKE ΍\EQRhUAjI*b*I( 6d[j$3T}g."dB`l\<֔J:IUū"bDDIymm]cQ aY89J:hmʡH}ːɽ$IJ.y mumMUMa$Ӫ*+˭V{|o][;;]n|䓓w E ftoּ5Ov,qժ"NuW 5n0 ABd1c GÃȢl!(ffOmˮ@p6ksnEBՀ`!01`Z# GeVx(g4`;e B-P!){{s^s͵sB Ew{5bb^cm{Ä!u͌p7r@SG&A8/)ON<0Lc.Xi"Ms30ܕD<2S?8GaS=c?jn9|dck籯}~Ͻ}{xToگ w׷=K/ף*3QsFu搞G©(=R7٤3$Zcb% sNy} g@ |yO;/B(4}o}[rѧD[$K8݂92,z7>2?Y'DZ!b-EYQTjl/=3^=G#?񱧿מ79N9;l],d;!ԙ:Nl|7}fxˏ.h~q=^.:}hc;Gjm<~x7 _}X,FHt\M~?sY ӽ{ SM(>CqgoG〨T34J=Jɭ8ujumoŤmT.覢"5fyƠaMsUQI0|G@}] }DUj1 TK (anfެ1j!&) #! -EᚴE0@-(FQTMSrJKfB^YiHZDhUHӀ 7gHIX/ECUE}j͋(uAnj|"*Mxo\YdAek>S7^yӛGSQH`wIjƕfe"y32z_!3A@9R e 6HIp!uha"==8gMt |Ջe~_{;n/_{ߏ|o=W\箇 v׫+Wmea!y<ٿ]@B]wO'2)yN*v%;B&;zDr `MW+ߍ5^nOBĤwݷ?+{MJ෣T5& aDdLMEbbYV5wa0dz2y(DR{ Bq,aԪZj)zRQQ$:NF*,( ErOD-< hJ-,dtR*HPH]Y8xHS_(e*GOYs$u2?Q"`y{шĥ"y<Ɠ@H,ڡBQAj N~R^LbE9 5ׂ,KG9e5ɯžD9zh(ԁp+A4Zk-!Ju^tSgxS;}…NWS1Nvɪ5`dgii=1tmڣ~ϏoVð} AaN1nl_ Ԯ];w۹O~3w}؏û7sPVB>a`@ʷFl_$l?sųMpN41Sks'"ApwCqKdƥ50sA6knbᆬaPS*a&2<7 A 36eVطS_GI,.<몃lu)FXrd D K*1-W?WW>t1;zcOyyݛ10ݬ(I '8p[dIͤbx\]T@qeU-="nBAfZ - Tl8CM݇OM7?OWo{~7s׮7$ RP`u3o܃6b'a}f-TD)S^R) (ZX'N~/Xlxj=];qѻΝoηo\[Pg~ZkkU{.*;wGɧZ<Ǯzu wq;C+3a*6gR$W{~h?Gë^wGi~cךK.LABDVbT.ѹ%dsxx fJ {"Oayh.nA΢Za\I*j͘ 8MZQ%->5\ sY̭53fV)dG(Z#\UPZXEѐ$,11kmr@Ԧ@^( 颒P3wfMTJOZT} wo?I-b"@ӲrN&>ȟ>p=۾ {iwFKx?Y߿phfAL!>;ϟ?w}7~;޶2tZDI3ALH3s W(!{bJ22!֘ɦq >~.NUDXZkZjq'&.2 õTAZu JsLALApR,Ӹ&!7Sa7C5;:Cpߝw=llc\OMJ)Zil0#q $<sC t%9FK@o),1yғ3:·b5@];ѻ(L?O7ß}K/?>{'MbW_Ƶ(v$sݙhե`GFA?)&XX4%fy>iT40Yמ{ɽ׮_{c_ݿv)|}g/]t{8ysN;s̉uK/O>==}Hj֌R R&N;>rK{wݕ{THCƥ˫G?N>ؚS!2=p=!~fkM"]ڃ[zb?3:+35DAx110oeLPqjE|N;غ|z?UcraQ@!Zkr)U,[.-8 8LT*X,J-Ţh /Eɭp*0)gF4N$֬.X@YgN4uX4: -vppA#,$\4[eIUwfVFx/he,ܵf2 jqQ^jV㻭W.9svk0',BlQbn{ n&^_U09(]V 0g vBJb=IB4D|NKvyJ-$gFĞwb~Qgo=& s%wpcxNPT gDBOٴs 6Ocz'|oo>|mx'_y go/&X*w乐P^xѹ)H}_d[H IoZ460at/1 SAlj3o;u>+O~ xof|q9H{JkBMxӧMqȑgO>G}{o5el{7o޼^KЫ@܍rRLݲf`pؚa/v/pqID"fb IH"<2?|؉4B6JXɵ@A){c]X8.R2Dtz/D&`8;EH 3*K/{ȑ/=rđ͡8Jjkko|wo=zsa9~rOFI,(JlG4dk+ R8${vЁ of݂x+7tQ?fl(CU񿉲돾G[b;wz7jw=!4f&VRKS(I ETfaB;%\Ȧ3'}!zԓ#|8EKtYK#ڻޟ~>G??zo#[.r :s.ﶋ/],<,X>{8{a\uXG?GM6-6;[tckgKky>)OvN^- =!$dF$35S: $š0g_@ݥAD*DDP JIrz7Ya6QǏ_z,8I8r}J 7턊hR6.jNhQkViqke"RZk*Exkik'Abj&ysaZ lBk9)3DbAZQ^A̬"-܉l0L+ -8y=B 2{Q0'A]\YѢuN"/:)Eka^y饧=vd̅HErp%glcƚmx|S{:sXDryVR.)lacϳ-ʝp?D3C%=@w%P+E,p-L駞WVGa_t=HJ*_p1-ACS(Q]VK9t(k]i+D\Q?zkWOλM Cw%cE4j$DKCLA  >euŃEӌ$Ki<牴}MWJxBبbYKwsZb{+½nU8Vx\?',3`3!~i%YD%q|rjrqԙiE{vٳTzZڭqƳ_ &D=PL-]1YF.CZ}~^z3x.ʗNXmL~t+@'ùn(E0dSߘ& P!TG̑6鄢]P$6:~4 BhaŰ`6Mȫusk jG[ 0IG)V?dET*WhT}KnH Dv3n",QJ gkFa“5\[RO;f#Z &' $ h`~UgLZi\u\bN ҾPsR&k r"-dc./z'cr{S23}"'IZ@6ȋh| ovE]*`n$>~QwӘړ `CZ2 T%!U)"DǷ2"ܧnQU=%0z*#5Γt$b,a>r.]rҕ7uxϜ:~Ͻw]qKJT;;aouα{ަgΞ:sg,i\#\A+T0 E8x숉R*X͉Iͽ/K?ܝnwr^/;溵\JZ3r)yó5Z serr8%0}YU Ah"+k;OY9DOR w%ќXwk3/^NZɝ0Lf x"+hFu8rmhI%g۟ F`ܡq=&2KfmjZ|jmJy6+5&gfaLTaY/DnfLlXHW"F3\Yj)¤4TZI8[GJa.VjQqj ɂ̀3g: Z1 @QbQs7^3qE7V7o>_O-8[>Q .3 UftLS=>Ӝ|3|_A$u~IQ? +iGq2z7BԂ*˽I?Y6&rAnl AS`3R)tPbՍ(JE3-СC;;GXr"8pWw;{͝f^hN^* rU( ޷+]3B8+#̞wgzjQh-ͫW _G?ɟpU/7)ܐҹ-hT& & pwLHchq`;\)!beQaRTtZKTfO>C;ꙫ?sN:~xÆ<7oغvܷa&D-B%GʬN'z3kO ;Nޙ] #]7qם-܄{?'jt+{7w9J~3HN*2qB gx&1EY.XdDwpIl=h=0TJ)H^wN6 "Ϝ\'f0,J-@X2(LfA"MMME(*X di45&ҢDLZ 0dbYneMB 0kAQ6a٫2QաTTk5G02PբRպ y5ޕWϞ?ylg R4-0*YCuQ,flŝ}' NzK1q:` y^buͳI³ fg+ƙBЛY;.LhٰdX2YgNf BpN?437(d&aR jm]GHxkm}k_y2Mk׮\[,9{Z\>l H2ߙ|@`>{>T eِ<(,A&)ǥL,Y?3Ek`>`2f&bG\0*{ˬUSOE8Cva8Zj)uX"Ug_f'.#|t3,-o?w?-7N #llu4n #k ʉw#̣M^ERg_(":t(7Mc=?3GGKW:|͝oG~/eHݙH19DmxlA?jy(|vlp0Т)@pJ&Gi!qY[/_&Q6!w`i f6yx3bUjq-B*J!+IR 8""F>ME(%4Hҙyqr)H`@3fٝTւ)-2G!^.98n\ҙ2aϋ;1ZK`P %6*v Nj{@eS{)Rk(FDQl7wRҍ襧Ǐ:tPm ewP,@Oe񽆦;( P#0ѩ5Tu6˺£me%D>neeF,ZI"%WzmR)6C[aW~>Ub{sK]#Ա4ç:y~=qD.i$s|ĬLZ1wwcs4Qb2K~^tx"0Ĭ),8w\pAu׫iv [iq}R{!C%r bH<ɔL$:v:JnqRS*SbV"n-?o(1l?k_xko\PZLIHx=.Ǵ^usbbi3,Ru4ívh30EE 9y1[cE]$ WP6#JUq=RvE&㪠2/zDPB:dafOTED֜> H_\mqjh>i2C+[i0f,h=L4Vqo{4;5ڥ_8?]R5I}`)D\uȍ9"v7$C!, D_op>)́ ٓubR/ Ǯ!,%nI!b%9pL5#@C+&K`M7=F$469qR 0bf5x4" ,\P:79s4s/3M9yf^$RUkbqrRZYF_̟ٲōD/ys j:(9mO 2KRxt/q9p") Kp8J)Eݸ3OKuZzOyZ_ʵ+]=8}<_ҋLׯztgij/|P>zrq8\ņ{nM T*Q,,%w)8p!% iG7!݋~ۻ}}\9aٜ8%y㜦 a \pCQ}ԋlT'vS=dDP 外Tf"& R8JY#F[;{7n/ 3Cm5k -VVPaeG)4Pnx!U=pRB]ZA֩5^fmc{18NCDxG-Xo;/ @U֪""dBYuw IOkdSӪi^ cTuX"Q"2T#d5QƵx QB4ʙŅEJ!foVedw*^rǟ8|bCٝ,[E}Aң &0Y:r~.Ai㕁tGg("ݜ<Ĉ%wWg YlNIdܝ)JXdev*"|ci<@rf4RUU157J [*6QAx&3[;[u׮^z3N8srY+^6 =q36ԺFMOIU(R̙|Fccђ?Z'wM"u6g:({(wߩC_VzZ~e=|H!y`DIaHRXD!m=x6A9HT-JfݰdL0)JL!l>19|kW\T)B͂CC㊢0*%[RK6g-RUpa)&Z)2,dR"bqiK(,b1Paz# jldZGX"ܲUUZkm5MSt -˴ ߈ Rg hKX|,u!E<CF*y*dp`Vf(LT=uXHY k<v쉺c$ݐ,F2n#trjyŝq5KJr-,X hqa%)C"Ah!h@QXz و*DWK#&G;(+|׀;)NXEE]ɡU{I=3qʲg^M>}BB2g\p8!"C38{AӉypm{{M0sJ6' $s4UH.HKXe_Lg&p|8A쳴gC{xfBS@R9CۧO~W`i #Z(Zi?5 wȣSH * 3S流"E; nZ(h6j$Z ~ JmT0W2yknB*R5FZTE8à㉼Oc+[Ss Xnm ̊HpBfL4WP[#.ʪNhXnE2D sTXh˲(ۇos.v1WdPzh̎}o"А=|сb 鈈 j?'gZ$}.#öȼnHaAfj3?׵ʬ -} FK.\BAau'Fwt@-Hḏ0WFD.(Tc#Pc&L9;6芝mnXaTpqs{[GOVcmfh)S hk cIϜ\Xȉ{ktdӆf u$Q{Xͥ9s|oW.^'&& n]ŬZHU}aZ4iDR*JWј= EL= M* fvLY02;G],K#8h6Ĥ\B 9=R0"_eBX€碜K"r[ KfZ+:+K"9B2FN-аFPIK~jolQZT6Zij?s:qJӑ|I|>4 Ow ϐW W|lVI=yמ2و0bAp2xF"=냉*-zқ 1/ũ;9;c< 0d>~w7of 5/a^]+nѦ{jH3LݑY3arqPK=!z@tmx^Z$$ s t7v4>-bp N=ڊ>Сq f*{0k0_W_sm^'?'w;|Pl_wOa5jZ('V@D4IZG bV5rۥ0aB%ׅ,X+XJzZ޿'βy'^ˈpo jXܱG% T  w3]JBUyv3Aw8niXDiF$98sn+fS[Q-B 3՘&Q^BYQjM%"Q1 Z+ K33!:B0=x:h~|〈 U&ST{npZT֫ʃu6W{Jñco %׭@LCZ;e%*s,x`\aY${/ls@-#QT4)N=%MˢB#pC8g^e˫3.dNx#,x_"Q,OYb u=N8qHkSqJi$)&)pv}vD(dM!!tW-p (AZ L:ĉ o|ϝ?qs~e\=(KvM}pl]5RR<"Z"|EArAR\j2vHnkDZpSYm2y_sIT4:zxǟx7~?>/}Ϸc,es\L\6BFaDDf=3qZ$:<qK{E0%XH"ݡ#{J("VKd8N.nUΟ?-Z^9aC%ܚ,np amhuLDrp2'ce)d(D;rgU|RRz3,ЊON^c݂$3ЃEلPU-[< hJExH%gB3JTܒg"l"2UE2R*I)P sՂW k) 1kcakW;ӋeSu4*4ӱ?N Mgq欴`"P ,0tp\ƪ $N.b3çG@x)| P`?ԣ2'9PÂ9^zL*{4i^,hp't:DC0[ټ==8RT=#GZyG)L. YQ4aA*iTh-02ݭ#{K-qdbo͛f\(W2&dr[PK!1Ȥ8ւMTw&% oZ k1j R:4]i> e\^޻_3>w<u (,fFiE8f gޯ \3OD=9D$Xtӏ斊sMi:׶1WwzYu49qb;G|PqZA@,]S<7 b"*!µ2ܩQ)F] ΤpJT~K_}lkw|˷~֑{-"H%סGR*B`@lBli$ MhBpjy{Kx֚-%."3I.̛<hހL ܈<y|}L<*Dq䪋a1VR$9R&f6 ` m HX"1$"6iVֲZ[GD꺍4 2`DI)"¹zF&EG etئ~0,*("|\X Չk 6L¨YPZeZP59NOi"cqrkJScroako+kVJzo=4Yk46#[6雿?tf&lD)D%~M;Ltpr9zFA !Ax Ba&Ww\2  8&^=5n1GH/jya*bBXd[a<v| @}N$N 6QсtNǬ`daOS\)a58B>όnD LnL<1_{gf V̦loPԟy8dfavϳйDZţI&D0[r>:)ڨRnn^zO_zevivꕫ.[gN9$uɬąXzqZ :hJaGlRaGKF:Z!N TU9Q2TeVp&o}Oׯ?ӯۮ7TCUͭӿ/~s5nf\%XH)<| 0=f w;KGR`KEռϣ,ꆪxMDUM4w&=猈XI܌Ï9shoƵk7eeX, l VU)n 4*g&- ÀZD\bq`&"LYD800WVes%vUPAn*(vâ) mps9Cdy3XlZ i-lnF΅AεR @YHDJTn :ԩhk)u"2FLm"JRCiZK-$:q]+Ux͐yK~rg#YZ #5Q-dq"c 12C]7abޘ )nhG(֢Q oߒ`2f!f]:`J<РDBG_֦ZO-!`\`wC:H f8^ao 靷V7#w9  %;ųb-@70y4dZMC'~o_|{.Rv\UNZ.K$O[@p~ qOq.6?twEH+m=ƳrRl)A#angbAoo8v|oxUQp6f*\!oR<\4EJ13ӭ<β͛MI%"jQ '!w(@*ֻW!Ppxml/q Ԅp3"CDPP}vG0ϥC_* ݱYaiM wXEJUAW^^JP ir-A_~ȉSθ qraQ)yjAzw4M7sX.΃XN"ȓu@>9h!)E 9uf 9^:x9 sww_+JBdD#X  Igoʚ0́{nSy:6HйH BNA2wkU,,E'dn'z؉$ b~WH+`$0GLa!"fyX*$%&"3'eQ@9Qwbc$mN y wvGOpZ8ț:IQ`DR'KR a$k!ĢxdZJɛ>WnRVRYۄr1B'1g+^v[u 9Л,85xjΐQbd9ydW]de'K-J,.*:RBZ6vKX%'2r-u,*퓪C91ibR+eh.(#$+HEPqH-aަ#HKi#2(X0FnP%UiNIW:qKIw('夔C*_*o}JNªKUT EAF[oSZo8Jr&?b`pTH!TK&1kx2M8mhJZc^&s, 4+G;+E EhiJA͐(3  f6>v. <fXɚ`ڤ )L2 ;C06PʢTHYQ8G-:RCM6Exh!؆+A\"jA"WR5̹ ek7+Z,7e/^~/>uhCv6tdu2Fo0JA%urJGi&b%FD13FxXp.4?TT$Ş Y/Z4OHuL⡬`jr'{(_j}LYLZJ3F&9 fԇg/*5MĽ+!SN]=jR`&,O $o`@aǶy^Q&C`O?#ِ)oŜ<#dcd&&4;~`8U/!EZ/𤋮JLDʹ ‘E5:BV"-)d|"\X *ʲvX#7d_xk|;|w|E6wrcs{tO(Lv̹<|bcXn˽w~wڸ,Jo>O>KnW!1> c H1e<1ץ41DEC@Y`JNTU\exq,fъJxdHvbԁ"< UUp "N<5#bpUE  1z}8Z33)Ԉog*$Yh4onVjuo^*y #Ɯp 㫬b60 @>QDD՞,LJUbHIݣ8n/u:T#¦t$E4[##GͭV7KZ\b1WXɐ&7M# Z5kZx(Q6ʻ5WzD!:!p5au t2#֒kNN'p!%íE܂HX\Ńf(vDN^#kᥠ3"€2RjޚC^+k],8JQrg$(v %Z[3F'Bj!>bػje[[ћ7ڳ>g檍O`}ۧ#KH!EQt#9 96Or#IT,..ΛXC$4p8X,?#As*3Bc2A_ĦMʒ mp:%o`ّl{}])'2A'"I />ߥ)֡R<0 襠 b>Nnz1oqXP~:}Xe;S˘S(ҞCL,Pu | ?Cu7o"͵:Ԋ{m$ 1{ kRzh67Ξ;_mXnyykͼY*u۠vgpwGwo#'X}Ao^~޹c{ǮʡC[0R: (:7Z"؉ G{*9td/dnۉү=2_"T(mh̎c g29rБW޸,Ys&3֢rCP4fͬpp|Ch&i`5el!*Mk hu`L0CgUD,9q.4d_җ~7'oџԟ}/?G_}wӯ>?so+33 -f}Jg0$LD@"*PE"<׹'mQ^8fOmxfS%Lڻ R4mlcggyx^?֠R8]D7JB,"m wC=Es PYnn4kYR𱍀=C3ÐAGcãEãց#MfffEԚ3EJ)fdh w7 OFDE%d5J < (ղ$*Si,8LUjĥi] l*RKx2GjQ%lZ2N8LyD &" {LP-,- $9h Cjp cKjͅ5>0r遱*B202xRKmR($.#ʐHp`"-Ev4d="(؃9t t ]H{%|sU * }ܡ2QC /YPKn++ׂ&2Y݉E &  Y3wEpXO7fuɎYsKB 7./οwx??]o֡{k≳;[[zbU& {0uf c2|(of(e99/Qhj>J?!/JG!Ld:uU^>dc⡖͍-zl: 9I8V-ĤZjd-j)uqD3;D+NܦRDMdМ@q!ɦTqj>UOmaUw/À B_, Vk5@ap#85BLTpG ƒ~t\`X! "׊@*=/ُ$Ip=8@JIIoCbSU Yhؕ,dDʃ@! KŃHJ'CSan*CY8t޻:\w?_q߮_}['\~sw52//SO(zUkH0z\[]<܋ox׏ۮ^ZQ=}V~S=|[joi19fr&bb]|B{ʍ"z_qîHp ('QT`RǬi=} ڵqRݦ#j)Zd-[kQk!Z;hp^XSY Id#a fglTfĸ'Eu'4 Y=T"Ohg2wdYi4fMܚcʜɝeU6q`nmTRjxu8,ËKH7֒}It݇H2jRtXlbƍf š3s"#ڝ:xqv֮7gF"IՎc=`6s!QyjazT0W7 XdH;!B׻Lhb@tbr  .=;5Cb#pDs/KY%WLl*ئ{<rpm;? P )%Qj"풉: _9P[* ϣB&"Z EM'2p3e) 4Bn},(')Drq'ȫXz3|*Ȉî=52$(M',Fo ́o&epy9UV'[?c?z^ٽq+yT-ޝ;o>vbў eks0ȸC+y( 5k*ɵ2@ ՠqHXr?unŽdExjk8{ҥ$5hu")ʓYP}RVTJʵZ 33C@8Lq# qQA 7ENQI1!ŦIEE:r̊jEb*77wݣl \kErTUfH("Q03"Y,7G^Z1 ƈٳ.%'%ZXrEI a!mD hgvU BeDl?I㢋iظ~k/]>yJ̊@ {ݞ]=&,bƠZ4/ ܂L l֩3W"Dɱ͡LCRA)FCҼ&QnFi PGS# (ko_/q+6iT(|$fDg*gdC]5 CǬ+1P$#?DPP歙 s8H\4bdJ5&%-j]?;9{d-v^%}uր%!%"قouǁg5s wnѕiQw!g"nnνə8W7#;$$Xu ޾?YrY妳PQV"7x79thqG67M]J`z{W&D0ԪE K-a D@CQf Cϸe( ĢUY|5^v͋Ӵ;mղn!4qm=1^zđ@FxՋ{Ǜ??ѿV$B/f,5lbI ;_Q]Rm)Ly"`~ dA;Ȅ[G!#n87~P伓*Lꗌt=GTdVlv=λ.f/4-ꠢ͝UP A摱S"Ԑ :T8z9<#[\S%,| ' aQk]?؊WUjK5ǼZOS ̈́xkM׊."|`rBЬdNEKa͚N-As(LH 碣OFE e*C1{-MMT pvJW2$d Zb;=KO|ر'O)o E3#ZւBiw \;G0>eV@mDzOMԊ$XƪV"eϼ}D%Z&iS&#1A9D,ﯫJa(C]ln<??~hgΜCxd8}mSO`c Ɓ8ZtZE 6kkH{}psd-+9E4,sJ`S.uM}Tߍi]EbK6n\sxXn֭ҹK??T/oƋ~~g]Qի7Dne[M,c\Wԉ!e #ɍlzp)&x%Zz@3dJ+bQQ+dM'vWv"1T0RZs rw-{ρqfH cԢx ƭHYl~ͭZeYc{k;Ûh$eSk)<%IpxDk$`ibaʁ3EYTYJUUPZZ$"unnTk%&V<\ Քeji\UզE Ϙ"E5bE'k^ӡGѽn-Rr_ps‰`:k133OmB!}Ip33@rQ:>3R06ʎZ8UuW͂TQcR'NfLbm \CZQt wE|&QgAZNӻ@n!Q^T6ʺ 97 u#'")8Xz '>3RILO=/~ek\nnzDqD2uL!t$LU"A pD7)7'|ɯi,_eW.=gΝo9Mq?SϸL<ҟ)ĄHÍ 0J֜YU1Д(č1.\jecYln^+mg0޸~ވCBtxmʪMdƽ?tE6~Obw=]v^teZi[Љ͝({Vd21cJ(Po`&}<„0\2[$W%uk\"J+J6S/r%̉yUFUU`aɦfC1Z$b7P8 BklXP̈YB?WHRCkff9{DkRQ-Zk9C0 ȋqGVU_^ R+,iy.3QK Ȯnk0z'XEh5KIVaAћ^:q6PDwZ#XafzСtUut:NyFtLP:?tЂKIz?at$m݈G]:Ȗr`60`s!u?1"D7\pYs!r펣RR'ͨ_$E|#)D 8'5k'*K(IaeX8t^K#Pn-=:tDuKI7'>Wv.ԲXN͛x<"8[YXϜF`!):KR\)FiOS7w}7o>МAQ-iJJ8cN5AYmELQp){=$ħrZHօHwkC8{g~ơCZM+/Twmu ΰ /'<股hd=f43+-ꞙB)՝K;v8S'.̬]VK~a6Nd>R8?\zY޹54Ү˯O#7\_o=w}J"E13N rw$ 9'ɩmc#"vR4`591z!JL,)J0&݄1mP`2Liq>+^`GzF->2!'W `88d1bm03w%rIruA ľN~-_3SLRD>kTo07M]5@Z'g *v+T|p8~\om<=V#ky`],gS١ r6@XEfo!nЏO(LѩpRZG @?򫯔qc;G+nB0ACFbU= |:TZVIO0~?F I]R7DooFfR6WӑP:LDXn2bF,=B1,pk}Џ<A)O|SOA YS3WΞ y_y;ęo}{\tx~ᇮ7Wg$ims;|v{}L &j\'ੑBvC=x&]j.nh(~IEazn"-A"bU)1%ۿw`͍0 &@A*n213y),1FP.z~͎4Ejhmɜh4zƓLQr0 @:eP΀f5!snbD"ɽRVG(j{qNHSJ 5g0:iUKi nuAOL B,~|B܄H|(kBns6B5P DAn"!bN"; 70gF?L>!č muh(8PDŽB]28h5xi,I0T\ gVUqx@/RWif/`Ao ZaumPdmS#tRqTUZt)+`` HJ #IV Sۜi[u91i~^K]Y|2iխM yWxhs3;vgulUIN(H;nf^Sp0)l14*T%&M{=:ޝ23д$T)|(d+B*a7CÓucgμ3mĀ~cĻ<`#Voyvi bhPrx1ffw{~xz\Gλq~^w]RYY[Kc#x #YCP>4__T%_ =2Ma (SYb-cSuXUF{ȔR^? >LSH$ c071Y n5|4 C~1${=9OUEr,1F0Ђ.CT )'%IL3)YkXE P53UGEYrF q$ol!1z._BUSɄD4H 8@JKbۺMI82#ZӶ¬Ɇ⯰FTB 5k z z%Lr7NH1RKwfeϵA4wL @ Fj28Pf(me>Ty=4uXgWq(CZ@lWӱRJT#Yֈˉ頢|*|b]iMRQG6a!y0ý`FT'RKIS%<o& q~ =C}<]u?GެFX.֔QZu'kCEO%eN39ܯLLp|"PӶkz=Dhf?>=*ޒBwtylnY ԩӯʠOS/F2"2G333;99z iNƓ̎M=w#_~饿k蕷y߫/<뮺ӿZGy 92],c2-t H\P0NQ/?@)!tlS_,tj17¨G?;fBhy0kϩ͵~MrLQ ]VklIm}GT05u&9=i$%fZtyy+FtkÂOAT"246KgΙc %njS@ā""uiGlMD@ ÀА@4K6Y-6IDAT4=c YT:aX=L(RlcIؿcB'ġ mqf76_~)]_9`v>9ԔKMIr$Z{GC`n s jRL͠t=AЃj~:5Tprs Ħg"0=ҫ7V_{};gf1(\g+Y]LATbe#)ʟqpS4[\d7^}ԉc,.gAo4J2 K wСɛoKŵpϯmn>ZF&VT_gr1!3{Ș;NEG;9E➆؋͌^LeK$BCmj4}sQdssLF12ȱ!0$dQQfc0e"TdC2PM 0!cIƝ1GdΨB.v6mb϶di6uIUbIս*bjbbTp[p惯rJ])6.|_F[RdشRJ!Z@!k9LZ.Ռ׎H4 2 ' LoW'/y=;vu@B';U1vǥU gʇdE5bN5R>6B²[UtӴtwSix~UmFm:tw/6z9q]E>2-+HV:spa!_ɰET%djA.@nPXRB2AQ}V2)3IZP DX=^y{v9#G/8MlW<曯ni"bD6uzr;@ܯg]}}zg"Ѝafv$JU:EHyr4ƁW%VEd#hVJP*7T?z(ICu+q̥ z kaA˒=;'i:B $3W>w.bɄ9yJ?+qGDEb+z d ` 9&!H%C$&,j_s`8.R YM9uTxfj.ub0e! A2̻1 b5IƦc&B!ACH` 6Y?Y}BVBhpJpW^08"L5,$mz?{y{v/. UDaz\ds#zCA[o<~!"23.sf-%Qw5f s{ey\C%-`Pt x4}p7:/?!:EZz+j NJwTؖ !JJփPBڽK;@F Ű]ڟ})Rҵ]zC|0g.,.u)Ǚq{zWWzG7"LjRQz@Ӎ`dŤiY7ȏY7xqܿwν{@DA<#"Z.q L-0kS rdq~䙵gS2 %M98vn25!_\EGqoп gv>#w~g>0譮sG!aHn{?g_|_wg ַ r5 bX.NʠbIPɇ$U"VO,XwՀ61g&Z&™PQZqA ͵^i;c# {&I˄ĸv!x51yVHDD&&* < )X)6oT*?kB&i%zca" u+b&!pH֜UŶ0猈DM(s( L&>k6("~揱ܩ23cDbJ-SlCR.v mcSLUC`0R386F߶ ~{/ٽ!d%е+%264癹fPQFDB,mL50R3)QMӧej?F̗JR=LJ=aS$XDhQ^vO^T=^5Q>.B+Ÿߛxl.̲Ju-&4E*E*Mk9OLh*^bZy^ZƨP*h^!W1ƕl~G|ꫮz5Ws۾u`yz;o :R""G_uÊcjXahd{^9#/= ??hCmvpn/q"2m3 {R^bwڱg+Y;uv~nFGE5%f]v[?8TLP| SyЋIbšgJ% jηT4JWGuZLJs&&%T#A 1N%P{ nˤ.<*& _K/ܓ;~ѣr {# LU܍ NO*TRMuc㎻{7޽\/8wL=u]{w.\ޕ'yHE( 6߉JSQ :3M&pzb4 2Hncn/ufΈb bЉd3bΒ6ֶZ5v,&;~pf5~Z޿p}j`AS_~ W|})FJg'£hDCT-~Dop7Œu)ݼʭf(ts_ݠxjCEX PTEKifJ~BҔ[]{ѩg6EPQMq EU%+!siT4p<Мr00\MbTd~& D׶c8#{A$s#K`1Ĕ2!zIh  ")PVT)J CY}b䔋3SИZJ9xݴ hL!% g3pc*`DonB̢}(0wјz ̶sϤC{{l`Ucx5#BV5. oVfD`JPNL!=yT&֘  cW:-gJVu6tEy7i$S+GR NxW]-N6ZI<>rF7al^~'yh|oia6XG>pKL @3ًL~q42@0B?g?yGu`qOT׋`nѧ[o0?7z[(rɧ;!xGn^9ֱWzs6~c*E+GBÜ&s s9晵w)(0wy0{/~>S< jdjϢz˧viϽWN?{̷JjE=%,Y#-ٱ0colfmZG 10sኢ#P`ʭTPgPw?BHP:Sd%|vp21SKz&3+kiH8Di(]'){Z5{ï!8MN" j&5 P4V0t̞'L`f ,d`vQ+qlE䬪C®B"msM$Y'ǘS@Lo]R#+l?b UM D CnMfv X^37sW.*&^wuʆa>+ROS.V:}d+EW ۞feUzP*fT]$`f*Z]wB"1h@p+?TZm*o]1;B!T޳g.`i]Dp sS~/ߐtn~={yaw5ۛI#0Ȋ# U }h^5@E73b v//۽?۵sc= ㍵o\{饿5V|'Rwj%U|4mikk}yf2XΊinєcR0cg7N:fH(9s/'>/~yyGOommMӱ M;>jsc{8%v羠=CHxzKbU(܎W~'o}ng2'9AU,A$OL@jS֞˸A>NZbg[40M=100׬p=(Ihmc^ԹḺ9'U !88T .Z 0$ d(&jEG*Y!H_$0f&8d m:7' Dm:[J{(@P*-Oj-i1`w`؛|LLP,0!h ܥLd961!$2nnClڷ_~}[Z\jm*Ys7rr0P*Drn t(ڞ۸4FeՁ<jmAZR*e3MGĪeD32b@e*HU-L?1wv"P붡5P7 Pufb@1~NJQ@ˉsK?w̞C߹ӯ=O_%s\o5I;=6]KAyx e@~) bZ~ef9xJIw..{QKϞj\)Bmpd,z UzHz|KVR{{<:s2rw6`y׋ow?O [1fn{MAd)w 1eJ4"k&rfۨj]@M 3qb̦"Ymy3$b#"&NM|'0 B^M%@Ȫ16& fE`$!IcdrZzx ;;К9 QJ"%Gh!!+,@9sPд.MƩ6R^'}vy.-@ q8SBRVb)Iw 4E_%jϴz*jMexq:QWTwٲP6ኧp $H!>ry$߱~:Y| qwE{u>FVh >Ȥ/ Vv~\K_!ۺO P yfngw? /9 nݳW& =E0o<̙W^}g.<`o~EeJ9Ŧ6bg'("Ts2[#\[p<:}Ϝoy78_|ﻚC\;̠^qN(@X# P;s߃{[5rbŒִkՕMfzsl Y( :8r.kWG?;__P Cwbv)a}s/,އȀ!!7?l__~:/:7\:莝UY, XI0i' "qvB+@5x +~. -Di?S:撁pt;(LRspXS~~o=ɩ5&FeI9%)%39xlKy1mM-xtR {#`9%g!' !SMNIU ɏjm$Vwj"gxRVUAKj :NT$efHfpNȪSCDD8 TϣCMT!TxW cl<,i_DT}Av7rblM %[8?['VN{}{̬ !zԴ ǖN),؂ Ѩ: DԆV*ܢ.OunPembƊqZr+w:ůzA001@?"YL'ZP|ZFm%Ș jo)q+ <獵Eo.F G "6afǎ~o_W n~.^>rm{^|ٔnf춮c~ѽM]wu汷^7473+6";VTXm N{rr&Q:.-E~wطg?&k޹;[wp$E1E,趈hdgǛ3=&B`A@lsV.\97wC{/{vs2YY[6gW^| PX;fzG'O_{ so? .}ϳ/?۾/qY(6b(9+%ւ.K[{{a@ ."ywUZUߦjk_ڔ]Zm*PP^g0rL1P8!3U\Eǣ1aD8)6.Dx ʀsj5L#Թ ~K6j,焌iRj1P`!("UD2֟?rrdF&,Ȅ-W bb`,G"b"v3A81SF;ALE̤&0umc 7ӍHEڙVF7^zV68037.ĺLHOL7Pje ˅y0xғ.hm* A_q0}mmgo=LՊIlt^ڹ{'}O>̡S{{ͯ>s_GnX޿ou}}c8$4hw{O>#^xxo\OPUJʹ\a(d$.B(X\ "Ev-K"s{ݸȡaN-50 gN;k8<#]IS_@$1D)ܹsvyK3X`VEC6RA5p$ z3)gnŝW77ovşG?ގ3k+dC+rT_ZmZGj8J{z뷾1|솏 F#AUǏ~+//>s?./ܘLFn .mƁbN"ɲw .`Rh#NR2څ)Ġ|)wN vh59ǜ Ħ^l(YNV#NmعkW6Q@0 T*$H4!h5H%B. 9^lMTR .!Pݨ!%+3f1!owk`un–n匥@o{.嬢h]a^`#Sre SjY! xυ*!R.~yru9730ed jF*gLc eB@X(Ґ'IDC_|coٽ{ii ) 'K#:j_ZyE 5Q?3V8*T[XPhr5sk>ԌUS`OxDZ^Bӑo7*SD w.2vR(w+b%qK#mo%uS>h*Vk8 Mrw+WۍL7=tÏɛfwwƶmrm>r%sIZƬ>1&\<@0r!TId{gҼ@ggfN_~7^yy\7;C?7/S/zn?NNw  $"cNd]v?;V ":go~g?ӟ[XO&nxQن)5Cѷ[9 A 87? [oOv99!XS+nm &C?S>re5I)phTpqL2y8$Șb䷁X"h 6hu J1*xѝ)ծ6Eim, E~4iI3h"F%3"9WBp 4ma<f̖ˆ% L@)M|ZO j,E&.#7 al{nDH4^`ajMszAH$@NLu2դD|Z$0-MPch́}YL!d hm%6MzM6`jbYTԹDѬn:A H!%R@qe[/_VEٻk9<;O?qx߲Q~=r߽<أW_zMx3c2DQ)q[58Zu>j4R|*.4b9?pOXY=myDA5 \nt0LT o'(6V .L;#M ^^Y\<1Sώ-BġmfѪ3K9 {*T` HMPQ'e wHy T/YD2BDfU +0et7\27mk`D}M6YzD\`9'M00 b"*sZ"a ҜzMӶ: CdBȹcBb`F ͮ&Fc;q<ƴgߞF*3@3)>Bњq_!jY,~72I:" y:eFg!ǟ~ꞻH]5\{~}R6)YsF3RMRSnzSMh`fh?ywk?vvO>w?쒰)x8^$!:JL9gvʏe pVnxa\՘N U$m]~]sV]X^d]xfW]gdDfQwaշ;CmJ)oZ~c,;UhY̝(LP8h9@;{O?{W^yi;gO~áǟyѼ5|eYg -) Q-)Hq߿ 9p%-s͵]n1 Vv)*!RnŠ{Fm>@rC`&Ok5/>zln~N,+hNa~ h3"Θ0=3>|}㓧{Їz ]֭o\Zr91n܉ƌ!:Gk\RLF؄[w&)'X3QP V(0k j`?f&V.1%l9}툙TA?DFr!D0u6NdMNni@'=w)a H`y< 33bfNUC ^ ZS1ӔS!=el|DD T5u]`"b*(fbITs9zd"B"jB9 30a7O$%c%guui2NFìZY(.hͦ9ddf4Ii؍B vXnZFe^/YXo6&$)qڞrY錮8: j`Ҽ5eׁ7լE׊ibI6m+H#T~w %C@@s2g* Sw?=Z^pr]G QXTCz?3*9aO?w_ӟg|oșSW^~/֯;2bkчpKDJIE(M<:ѧ]wv9J@zyG/`5h[iTqP ҟ]H\[صW_=5W_>cqʩn4\%*z(z=ZKXu#"(ggҋûN~ Jb:BéZ NDɹaQzQ' AA80Rnj8%~t*y44s;|/?^Jg?~swYܱ47T @f<~lMXgfg66SPMq &He^@<ɲMI( S 0긳Չȓ'w.-~.qmVX*7t ZNo7?_{VN>fgb"0UQ&9ML16̜IhZbT=,97MDf &$fQ340PLաI{@ "2PQ&8v۷} ɭ3^\񉁸8̌Gf4_im~xLp/gjf!%AC۳>|71;m-,_\}ŗ^Ǹ_5Jb̜9}#׾缋/\ Db!#NZ < 3oL[xU@* "sI *k*pû|?~䪋9^H~Q#D/Q(_!2C 86w ~{^rvMr:w q3}`jۓ, 8ō[nO]pSk+{ D5x2llDƦO\6rɪDCsO}u_k<;wfm~4(l|r#޵ˁ@ Lޥ['Yy7&tܙ | A'`Z&|? 2֕ґTkHMk!1w/hhYlۛϬMYC`]VŕŁTR rʦ*Y\XeD,9xisQK 6q~sAͼ=ujot=2̓:Rp^Qsgp abDМЀs`^+艰D"b/Mq&1X9l$V#&IMË<(>f%pB0͝=zk4@ '4S\jE~? ?U oO܀Cţ%I5{~F#U[Y+\3)1\<ξh %QU$ yxeZiB|ͣb1'=Z2:z\r/fA@0?807I׉oo@V:{2'?.w~'&@:rt7Tf.[ȫQ( ji0V|y6 0i< fggR `jzQ3V2қ'^xK,ڋ6Gʬ9s"@u!,$*\SPYlGx˭߿nn W]shqě&fiIchڜp sV #ȁrK_ػco3Y20`-!7?;q1 񐐈_2([ԥ E1@T316dPP1\ !KT,+E&53W+#UxB5ڷo<}V$lD<[E&-j6w`0-ش)g!E p 4Η$R7T( dqk3 L,jnE3FМ,p &Vy7p C)' FϜro( fQm#n^SA$ 7 `wO.՘ b$fZCdpSO[ڝ\C\-EN dfZV+h) "uB[ѵCw>:"wĉki:O+y]7ցk-ܙUМG`l5N ̩?՗غMT%Bgc{{v.U4[|<O/®\hΝ?y'?|㮽)oEA-86ssS/s?87ޥҮ V(Wb_ t˼?P݊ڝI L^`bÔ=+0(#Z":|.$'(eccMJxzC%es o끷KiBWV`_` +CHH)2scw-?pxcAqHIH%2QQ%3gfb8(I .4 #0륪z$޻FAhzgx6Оyoş7_~#(טOa`fiVF ׼vVUK(!OOW |Q8#/J|(w۳o_۟?u%![4d}d@!7E Մ۶'2$$ -/1 K(EwA@^`fjiHشJVf413Mr&!ǔD9'S͌ L8bN)3aRD!6f&d"90bL n!61D,G\!U1uc0!Ѱmv `~~W5ԇ}~1Doyxhzt/%jODm- w9x/R4X =T1-sչ8*e!F\;OW4`ST?bhDr 8*܎hաV wZ~~;?^p&1Wߕjٹ~ЅGnn)RoE`Ҙ0 ϧp&]<e&PJ4)jKRCpPaȵ^i1v"YثHU؃<[om$VԹPDZ؝^yꌪۋ;Z [+X"{SU%)N+gއ܋/ޚL'dia~hs7F݃>q|[^}c i%SGGIClcoxߙUOOk@ zyH C fQ|`j4"v]h]FHLMo:H~QZ˳L9z-Fz<7Ŝ#9QHB]O=7]S?wJ ƒCb,I37qnn1ԡn=li5SuLԺH!7VP͇N>gVVB"zm57r")wY͐  .$K%`e51v-,%YghNEȐɲ6Mh9'TcƇLMl,Ezm?kє:ۆ TBrS sXGBHCNy<:7ֱmY, 9w9CD0`4Q "4K"BUS,@emR큐SR0ap8'*&)mMl8p5?37]j7G/0nQ.Pq, Ys l/n+6Yo\.9'$Ku΂#ApGzSC4+ h [P)Ll9Tc^2/w3&3#{gwaS0ST ?~z__/~iiN aM}[?}+\B۶^69˩^~W^9wy/#l @jM?T;䙟'.3k"@ĨjDAA}j] ~j0=ܨEBΝ@AdүD1*GǶNXEP0RŁ)< w{/tcO?ן?wy~v>ePBrgMݾCsOx.ǹNI ٌEщvLE;vW_ӏ?yGoK;VNpf@j duwy!x pu[ ]Mw)*NmǓq%ؾsvw啰ѧ~НwuQ`Q|f3S)0 9@060۾s?o?hg6Fo~;G<>-cz!&Ʀti=s&h4z_~7?nn>yά-T K, C?4C_ȒPʒ,U Oj?Vcv ,al[o.sϻg^c/[[ܽ`U !2gK8|;W{X+[ǯTxPڅwN}ws>zӇٹ~*%uc9 t~$'UܽxB hZ`ZB=cKMwO==wi<{k.W7?Os9ҍ;TȢ;Oc62``e'"`j^Mbh"X}Bh{ Xb X>#'3"13;ǏK7s*Z5kyJ@ 3}{lL^s.֑rGB0똨mb1-˪*hhb@S6FD2gFQBQMTDM Lji݆~(?) H{U>>[|{wssB0G@_ n&LL2ly604q>'9 f2) ]5+sWԣ&&D/0TB D\pX2}PECpBgw7Py\ti PR45a5k!#k^QSG{p LH0{v_W??_~KyλZ=Շ#1$fbZT'3g׼SeW_&[j"àL Ϭ|BZ xAq;ZhۗͩKu+./gȸzЈ)(ͪr٣wߓ{#;;GoMXXnw~G~>zC'6Y=<sgb>RURS1әꀠ k{9^gAbYCWT 3yTJSwyd߾?җ{K 'ΜT_3DG5QfcH!*$M̔0B1uHȹK>fC avWϙ 9C.}-6m9sf~sm00Ƀ1D&b4s0|/̆wN/*M8wu @ϭ 7:I~cE0%tǝ[^ s)DX)ׯX\~egۚӅI6 i۔Q=".*RiW{BůZP H=qv+f>Owѥ,[.Aq0s=?~\wK]ZT,9v·?sviiarV۾o~hqǝ>tc+4/*ٿ:O)d Ѷ6[[㙹GϿ9GxcD4z`жE.<`E~OPWI\ecr0$qaap40(xM̺<[u 4V0/}<u|3`撋/Їяt7Շ.?7ۥVe&N]zLƯdF-@9}+v@)ܦ&u*3[K頀@SbT IS~!03#n褵jq "FSS]ܵ{ai|Fg6&YB 33 x橧^y+ӧ՟]PrNB f\uŷ?+?}Ɂ=?շ gGd}{Nxz﫚dȢIPx>Y塤^k2_zpʇڛn1d}/ϼ]OғO~K|k㭵lvݷobtԘR]/̺NVV6E iofv2RH~c1d*㚒+2ii0e/J1mJTwɴxCmv se8KPď#81t)K 5j SeDٿlϴ+;+w~p=/=+/tgޚ﵋ ˱?S\e#9G"AHҍdqii07D(Lb(`scDFТ xDU+c FӲd" !) "4I"9@UK |1NUY䊂BHE̓GYUD݄$ LD@s$ny߼k#u1^9wiC0`gOnB37/: ""{J-&)9wٽwusfΈܡ{U(0b@E^!Dbιb3kS')圚Edz^5+q`.o)6m MRFbq) ^`P '48ɤxF,1C_ΙBu'BHi -}hb@5Aش^ñ"]BU#@Ir2DhJ@8m3ups v^x78cq Ҡ ZGKE[}w =}%6:oz/N!y^S@]é0ʰH *TUj* *ҺTcXmQ0네B,4IIM/捷ySϿv?j#vڹFդOz=k<|a 8`ERZ~[FOO?e|3wͭpKSSO?vġ;YjՆjt7;7n9ddL}MFjo=Z_kPabMI{:r= p2(qـ*o| enG 4Њ3e1AfDD 6b̌vYuw6.l? S4FV:4k_G`YRƥ~Axco}&RDGd|[nY J,5f6x-Lև3mqiz0ݵ5|* yf06]]!b# exr5f9yA7HĀH"bĪ?LjZX(JLCd#4mY-K ̚$i@4! "rmP(ڱ)IVjDMcYrƟ d4ev[ YUe")%%& b) μW{o:'#"`L0f<d0ccc3`d c0 DR@P Ωrսz?}J_u9{wK.֕ZGI)V]Y U D"HB4M Wp}|a~ϡC6Uj :04iǹ@* 0*p։icI1Ac3D&&s֦K&KPyWD&hm,Sc+ܹ\$#7ġ[7rىBD 1$I{92%(!XɅtc#(TDڷCb}Յ+4_^m+^ ;ZWb[wMB2^ I)YLyߚ)Xwʉ UŅ%E3[Ugejj&B1{e?m̜ĚR 1B1 H-Q1&BT 59 =A!AվM!JL1CA2MH*4ّh(KJYp޻"%1@nJEPg1-1yRcEJ!"s\Z:Agoy&&,˒UY ҭ{+kuJ.ڡp 'C>`fXBּx k2j0Q@mfeo}?rmyYe0# θD!dsX4xؗfTG6I9EQI!RP5%m0ٱi˖ˋ+_/6_z/'>n푟ܶp@5?DE($Xu"&`+F_؊nx3g{ztdhfzjueE|7L@֍ vb&d8<>C;͎,9fG9H4k;f$`Cd,zyYʹ9aAiӼ6(*{퍪BJiv|bP:e.˾}hyimmyzϿ9W>ӽ?)[>ͱ 9Y.EY*#_BcL19r>2;3ǰщ-vlڌU5tfly.sD'5,Z f9ZSXE_G| rۏoGoxqtH03919;o~|lۙmH1F@8($!9-\133Fj@XW$%5ZظScJIbA9ՌY11(1#@FQs(2GS"b $ 1[P$q, TU-[yWRVA"h#DsRbIa1Vݵ9O}ѧtJ?<>[ml:Yl7aL+*6"d@@Ca6c!vn&4{ 4ldA>_ژo#1OriP~r(`RN];udz|F@B(۲}}w<=^zѹ~f7^9/n{N_S^ͷ|#,9e王.hai?8kڡAd׳G4-r!4l$wY`Fe@pL1 CS>;V$`"lD'j>AEA* 啇嗟{n'l89=UIlBn/..9zdexve*OI@ tbm;Ιڸ]Qյ2_ljݼ(6j, 5*`ϪIşQ*Hjۊ 2$jjƦ_˧?w#wO (&N^9ȘbcyΚ4JAB;wmy~]u= G{K_gyx呃tjѲ,KV bT)"`O@ٯ̣Ms~7XU6*,ƿܔK*Mdg}dRLM?Xz$ݵP-v,ݻشsMo}ԩ;7^ñ'袋]@X"35igyVDxpdGѡẎA֛n_՞x_~/z<3_5w]'7m] ҋb 0AUT7IDAT1֒$_GuPDSD8(U`ѾZ`65!)&-abJ*:&Bd&c "", ResgsN v?)9B˅$Df=H1Zd,]4I2z\s\:2+Jwޒ,bL)1SL}1pQ0ň/IR4;vާw.'Tۭ 1_DQk3c3 Q$%:1J} ڱsM%]oj5yg;ȆM6f5c"MЉ v֏l1)`F!6R5"sꔪm5 {A؉& r$Dmۇ@/;ݵ'K%{ͼo0( ݳ-UK ZYYƔ<;bVɢ& uOz_T ?pBE{^~2HԐBB1۞M\ўk/J1ED& *(ɱ :s+s+L` @DCΈ#B uUl1ERs6B (d1sl@toa)$s P(lFrYz1e)@{g-X}RvDdumS&r!`L@:_$"rL1FM1J&I1I.Ӥ}>JQ{rg 5cODR*@}nzs6S=5>58>9@=; &6*j"!/s6xMc `By$MȀyc^;Q@ né5kLͺ>2g"(mfa&k3S|>Ξg>׿c g{ݐRxMoo_%]jbt(Zyf&gPA '$40пwv?3G {zk^=<>&IAz/׾2E҃u*ln:q3ęJI"e :BGU @Ik'*e6KyegYB[ tfߵb fgl"I(as\pɅqHl&*MXDHu{Aͳ?q ^Je"%A`joShU 6$OKِ 6 Jo/ v[F0j&FgԞCk=WRz-`__S_'_ʫ_ן_qej:/b)6L?s{5>s?ٵc vC'^tE|߻_WAQ==Ww{ 4uZ̆ SPW ]Ɉ";,yLIА;PU`2'$ I.ƾbѳ%""@+TDC1&3W9Yf4W.䝊CZ;fP4(<1'UĴHι+(0gQ(VdffP""윱b-%;a]1s6kk`"OHCL4#睽[`t5zL%$RLxϮ[VPRHsnvn]HhxV36 W$U-9`lcLD$I4 fRmfc#&{+$| S%$>4 %sBZ2<ܯ)yGssO?dw>gYbg(ٵynTB$J !V -zM7Oo_z/2=_34wbߕ_oMv/לJWMΫBQMTUR+P%;<$b,lUM1&I!v̶Hwhlݙ}=z6@XP׵;̶Dz2߷bLu\#Q A!/-Ķ,Z1ԱJ(-JhV݈D,6 ."#0P;5e@1}RUr)hI$[mQuĢBc`:d T{ !4Ď-D#[U%%QE)@d"^]EǨu軽jai*FԌ|EBTĄk RLUovF:‰+^:@,ٙ{ϼ;5gbD&a+`Ba7#LΜ39@Y͐ZL+v8`NNoH6wUcPQ^volۺult|qW% Au-O[˵꧎?ݯpw%^uM*BEYzZ^HKUD|Y&+/_+˨088 FH f 2w܁Yl`)5MCXBזW=P#tk`?VU$*1d5\  \+DEb!#HAhq) uzY\gZ%Ō~ϕym+#O7= .PRI"+8x'N xkO* `#&Atާh fXMuGObm1 CiJbc j'#GzaQ˟$8A^;6ꆉٍG s׃~.UZ \:;no|]׿^bg3謹oOD1+ %Z 4+zvƷIIRk-1"H2JC*h/$̻7)%tD!%D2wx!JCIJ*S*BHSd@_%Bu3kJ@蝫DTWsBOᜂXZH* %&jv* |~7NUTL K,DlZ<F&NɼYrZfV 1jk9އ!"ڝ>][]SF`*$ku{եIb @S%⺊j^Cf*zU-82Jec9կزaYTojID& A:&$'ٔDTX$f}TlA\S`f].(&߿/\wwxj|b솹g޹yJU\-X;~Ϲ=U5o_Fv̇s-Ca%H-e+Kk_w]u޳gOֻK_ځWA֕лQsWV='uOk)©=QNY8ʢ Ad]U"✳01G(Fsw g2THJVJf-!Dh!(/(S/BJgDe&*w#vl۸el@˿QbbcK}1Ʌ4:ulLǶkLfCFm8D j˨@l#-Pmm.sm22ڮ2<%up}}~׾S;.__yا?~LW@rSp, CCC}#?Ώ_>ݯkqޮRHDaI9Sgұ']A&IP!g㻒 FU&Z ez=S.]^^}yjGLbP%?.5vNu;?6=N9[ّFShڹ F4AbJH "Joa4 y(( D )%A &&̘CCOݪW[+R]׀Į "U3ܯVP!Tݮ8f|hvP#b*2DXmiDE4TcLv{_R7L6YVՔbU'zq],CsN -B"+kICQ8nC}٩[7W):%"O 0]~m\sq=2  5Rg!j8YM:YC1 ǃ GuW9w صCZ]+b ASmˆKG<+zϽG__6['f?*V>AbBvnwԏ~ԫG]:t{y 35Bu zcvq"zO>0226>2Z+kՂVkݵ3.YB/A{Geˠ΄ȒtLYbvW(%G 9174xw322h 0%R6䓉f_)ֱ^$%xbLT"4U$bǖ(Hb)Vu/4i԰NfIM03 ,W63'0C,Ў# !G.١{oMqc'N18.{ڕ F{ޭ OMnېȅ@Tյ*wj ORK9qlaT^aAZ=.\Q,/B-@-lD9q PC'g3gVP]ШTR]U* Ec4w1%I)ֱ>SȊ(+D8Çh W@KJ4[J ċu繪$l  Rm|;Ic L/Y9̞\/=(KĎ}ᒤ"4]_J{oYLB~⚽\6%1MK;EA'1Ffq XUk/:ZtN=ѾLMHHEǬ+ Ple!E M 4h.%f]dLIς_lgxRƪZݿO? G&WZ@ +/7w玭#diIr#g4Ur 7\׽5 BRR {7_8jGV6J,t:-+k)RL`wsC;nٲ㞧~~ ʩZuKg#Sש{;! HHƷ,h)'~ 57џ*L@(!f"I)9K1O0k%I 6' ̾3|nX$&3ő3Na-9HH( 1ذk!126ܣ]2#4v@\@LX#mHFIE3wծ=@~yX'  R(v*o]}׾w=.tauin~}}ãGN/e~kٰ-QU,Parlbttw_>_?N~rφ )r02ّN4*!#3@klƃ\ŃV,hzj\hx$ YXԤPvgFG;CCsK+ϜO2 ML+g]+X;CSk $1!vbCR@f<+!BɔpDV G(b*<WusܦLsuS>I)H}h/o9ê3I3+%HZ )$v̞[*;KAB!dJHs윩 :@G@\[,;B YIR"TRtCj+KJ`'˛6 %Us8 XhP^%4D !O> q)JWZWۿ!E@Iٍ>O-/ % ź\Q#C^lu!,UW 쎩$ݾm/yj|]-Wݹ3gcJTvZccKꆯ}SW|ogE19^R&ȇ?/]+Ο=k_׵c#8Hbj0g#y%J(x>gg~t_zKGOS6Lm=/A3N !e 9d.4ܦITZJߪS$9DGn18k(4ՠs^/ds]c sΤ]fRөC "bl,hZ2 3 )}fD塩9e4W0!AT7Ή.='H`T"H V d9X#"!w(1 IeԔaYϠ2Jb)|1%缊DE˗$%|pI8[uUE[GƧGFGA$d[f!|T#icM hl˫ P 􏌍=?GTGDIsCHĔj{xl۱kWMJڶm%W\ 4DG̖`-a]'NLOJӳ#7n1mښ([m$K6<T~o]ÎEEU㘶mݴK>ehzBB2CovCVTrpd58PW!g} s4 hc$1iL Z Rkd5ufS7BL_DJLV$j2P17MsE@Vb$RF*ذC k;?F͊fRpry3̘C7(7vۊ1E) ")R3|0# ()ĆqEh(&ɳN&bcu޹;.yei=PWyZC-7O|}ͫqy.&$ώ k֤tm;q) Dqw`{Kg=ɝ i`!U6 "#% 3s+t ^X cQMϜ:{o?o3OcrxlҔ΢Π):!כ{ $Ȏ|QdIa"uzCEQlpy. Yis"PTDR41 "~ܸDh޷Lu_ȜN1Xj2P`mrV ~`)7R֜) ȱrа(ej&i-Gᜑ̛nW MCÏ<ħ>O?ׯw_z~q>Ш+;@GC~[o7uǝۿo/2gN/CRc g8ޑTD"AnhYjl]6{K.<y^\nnvG4`іs*2ggntSS(nR%Te,4ԬxwD+k)Cv)$IɄnov) #VE9zrNBpY= ` )TU!4”DSB\*1#(uJ5Ų6U"$| wvG FM)Q,-~I;2q19`((1YNRrjfX׽fZ K.m7GHĊ TB Bu#2#HQ0rQ ]Z:|xPYlnRaP3̱fP֟4,cmfh ֳ-&{/?ۿ?r|blQ:Jĵž7\XN^H EɨQS&b$#cZp5F fk-nTLTyrIb5фZ6BJe{dbӟg67[噹nKf2EQ ۆhbTq@ PA$ȳ3cKk:u|3'O>yba\.t?y<2ec%5rӑtڝZh/Q `FH9 WNMNjN,-C]ՈF1`eIQb$ LEY1!u-#[_MDf`o[}nZBb0s2q & Dj^=0յa4% 0$L*b`!zD@ۭ1Dٻ—:, T%9weɔDFP@zqS#8$R0JLPi ,T<%7m044bH@2sZfF)3}i }915[ҟxrd4IPTaSW />z7m|Ѻks?'$Zc$0PY:|wv;XiIl vu駫Օ 6 Ǻ:yXCjZ7OXUEYq\(SOGG hw?}E׻({+ks'H'tӫN̟,zhz2AhFhJ ʉɩ[߾_zsg,-CnfU@.F|峟~ǵ|9<6uť g&g۝j1Dr 0siv]w[x]1M  ɩ[ M MLnشasϽ`E9?~ȃ|tkӆ*ĠtF&8~׿t[bl]9ea2*EB'Njwe)dKUHQ{C@15t2bj 1R!!8v$"1ijb-_DI1%fDv 8؈Xe^&e K̨*<5I, @dіE$J)u@ƲJF/f"bD"1Hb4e\.kLTVkE[RS"ǔ9boxV"`]vrn/*^<ŹSCh?VL^PEن *~)rx!3=[bxl5ͷ_|˟^^-*;QVV6[w=W---ϝ$n]}|)tX|Ril9rzյsvUk09['߷șOӄ816nPGQ;{Ԉs\1xz9ypweevznzCÛm[Y=>n~;nñ:bvㆱ$}ZG(sM?}5~} _^nU"09?O~|*2 BgZ"ޫ}ݪVʢ_'MXҬFT#Zt HATU B{"FLjs7T2%akѴe e 1'#Mbu]oMؖ![5)yD%bT$uJN &VQ)'W7k Ɖ(TdTejq~;^{gVVQ :ZWO~RWo{ooetvʪURw.S.WW}hyք}g疿e/Wͯ.Mo9ṓ'ꊢ3 k*-fKԉ驉ٙɩvC"n9[ =mC6.}k_篻=(];f}-y!Yv!0&G{UD`e01:;䝷Eazʲ,T8(QkW£b#v1y&Hľ,RL@,cMP8H1 T@#&t`+h5%C[v{Rr$2c 3ƔL(ol']QĪVQI"13U嘋eq=g`b,T!T8DDYbQ`V`|QEC1b}Y\3{rofv6>bZ?dzkhnphppdl??X_CPR`$J1,/eկ{{^Μ9U0 pQ ~Oi "Ur١?Go۱34"c\4mw/ֺݕ9眤}@|ֹGX,yJSC_seL>ӟ<jzrvvjyu Tl޸q65Ƌl%ǔhWm0c\up^u']hPswa Tu}#g>}3| W\ "8fQV19_#w'N8_|׾gvcg,//g :gY_-O w|~Aks۶mZ[oS{oxj%>e_Z:_tjhLFnvHi:ABJ!1;Дzӣ]܃ rwP?>CO?7]sUo9gC3PGRBFm4U MDH^uU8$ R P>jdq0 Id)%p\u ї|bjxc<2+N)Mz!#3CLub2 #*b1DII,5 yP$wζ 1%kOSP"fLQ<{((8b&Eb,*a(1=\խA;}"U+#zR.w$9Bn{a˶ 9_?COU|oK.-`b1z-w.{][F!GM'f}.|$IRZhy+) &oB2/Ba3͊ ]c֥;fB  uM$!"="z=DUw@TAS2IA|Q@[l$MF»ۮ+sDT=GDrL)c.O)s`TuK|sUĜ@}#* *bJH ,K\J \ 2pZu+Zmus=Qթ o`8nTΊ bzd'/\џ|'U Wm~E.)1iiw{;-;n4m㧖=(H#c ));^Y^˯~իWIM rEb u]סP֔XV>TEٙCD޷|fnZwp`lbS]zDQbO}S#s+kݲUvˏ9n۱A=V{G ?Wԙ̑"MMVZHE)Noi; v LVQ3jRdkJͰ *0AX8 Q0 P 0 f򭒘ʤ!E t)E=%C2:x4yOȘcٲZ ܍9HM-igkD~&:l&CحEب *ϮTP lT>VD8DEbofzf|·smex$!@__SO=~/}o90bȈ`VdP(]N<\qww\E[GQ*ɧɧ?_nזM[7LH&6 1I U@c}zߞ=Koz{>e/-\z4:urv7381^EJ *!nJ&l ;d tvq)֢Q$!UbEQ1C2k2˂6BShַJ]TDlWa[[v.(2"tXR2ᄪ1ZX9Q/3;17rMjUm; A--P;$m855<ҫDKvC"L"LD){晙JT)c,ڎ=%ICU/cH)JJ±#`Nc T?0]=X՛ߴid4hRb@Y@ 40^X@?_lea~d\XCzEYWꗾٹU][ MLu/x V;m}9 kաZU$I$o.bii m.,.+MgfcGVVG{jvԞ'Ԇ3'&~UﮭZ|[ʾ~$ܻv /?zb[DN}W,+o߱[CUbT4VHHz֭wݽwkhrT]Dd"0i*i!R+f΁ ]3G'-7/}?sSڭ/[SS!tϜ:YxGtl %C˺JH@VS %'C"9"sVMUcjZ ,e:Θ Ybh ha)h0Ϲ݈ܰ1QI)ǾE7쵯:2J YHAթXg?C [y̭Ι󩪐;'U,wmpzm?zbsjhlr-"kȫg5'y|̂FD.`K)JD K1u0M"s*KDM{1E&f輷2ƯhJ)lHc(&Za&I&՘L/1bU IYojRs:1BX)s'6)ښE$ƨ{RA*@KIs>H—)I5*mU9IQəSAlI=nU=J;|`‘3C>e7H8g!Ά3`s~xt豣}O<1><2?(CK2'Ͽ=0C%^wy[_~{}晃}2TD))Z@kWV׈ ;{ -̚ =b9h+4qN:EW_ʍ[=vPbӦc`~oᣇc`腻/>lӦo١-|ojmRvr1 @p'7nm3"a3PG AXP4cBU/I!-Oɽ{O͜_y;}o6̜]^WzǷybrj*j+xDH5e{v6Qejd?hfkj DME5 ) 6~;h2A2Tl9„Fw9d`Tm(L]!#;ru.&gˆHnS>y {-c3*W?B3:|fnQ_/SFj$Pqs%ĎK;zf{^oxC7Ƶɷ'&WWMLrHǿ0}w7@,eI5Syـ4*rk ຯ~k?946r; @cViؾmGnMcNE(@1o ,n"E439VT#B]wJR^@󼗐 DZ5sg#R6611E;Am=HCCw>p?7_ QTK@&L!Fp b .T}ëx8Eg@Ď2Χ V{fvvnn3m}sꮬ6ofE pai>46ϔLSr:UTĺ[xvN;Kk>q˖7uMH)FMFJ4!ۿq̋_|MB`e40b__ w|1:*@ClYP1I8ٶλ[&'k\tlBRBFFwef+cvFudC QZ𕫟w鵿 ;.s6#}k_Yŋ_q["0֤!Lu`TA|fxFm , DD- +bV#AXجEjvs@(1t$(*FKFBY" 0;Blr`.(/ ( uϽ=񓟼߿{>svhrg>7;[O/!9$PQmn)6$M6ILP遁]yK_ [/x%$QCuw] 37x[^ =tWxzIHD(k :!QHyj5-o,|k1*8(N1h/=ınyDiL T~QhF3TȤ_XYY 1y5Yx)&˴,&09_+$U:XKKk}J_ hA8M'*|%Y&M3~XQ3$1Zv5JDL(cE o'gvp}I槔")9RLy{gmTD$)W**IӺ5BՀV;Tʜ]FEuS"!"YAUΓs\T%'BW=x⩟{nmw1dk`T[[33C:TRE%+]xevΥ5l%l^ Ei|b|[_ʿ *Tc% 5t*(51&N溄T(3e8~log6.>|c쮬Z\E*B/^+.z,YI4V\x܍ꋐ y|L&FI.֍ʭ[nh;lTH1)53B_| 1qyQpD#Fk6]^\ܲeӆ v};߽_򒗿WN/.N,Μ:sĤ5!Aٞ/cMbq fPdr I Y ٪_|ԉ\Wj `(@ mҸ!!<!1'_WBv@ \k=2xg>O|3ݽmޟg6o)7ok߱^{˞>O^zRŒ`S?sYceiQf +}Q(ѣ<}̱c?W^,׿E;;_?Y<dt2BHYD0HAm֡-onu-u IQ%%493o26l߀ԑLH1%5OfpIT(ilb3пVס,(¦I,Qf6(:VTKL)($a& b"&39Ze0cGD)&}^rWUS"Ey'ˈhtT'S N1h.ш${=P3;")`Y#U19[Qb휷%U]yPAO1=̌1)@]#:bfd`1p)bL16t]Ϝ;s+33+\|Ȧgwc’*J2oCCgp@aW_S+k6cD ?Ng|rGO=:9X#!9/1t7BD\[Z肝_T!Xuf.Erdu74B/o ;:~WG6m233sf{ku]P`!O'g'\YVuP `J?2|yO/ߺiK-1"Ƞ5.fFiL'7N[#wyα:aZ:$AɗSR$$rl UbB (Ʌ؛쌍ǟz_?r;?oayiqq=wr׾Ŵs&Ib ǜ r.:) G8dDL #u=A%&m%7.2~fe ;R ,;? *ɐjQ*vgYjvINѐ'E,cG:vdTJu'?ߕ~wo0=} ˳[7w|!%ʲw@k"dVD *!FL@$&ͷ~{~eW}ƿ?o783 }mELARLz)DŽ(affj;oyTƭT(E5vkfw_q&}Ǥ+:3/<+PQ {%&abhpphhۛ[Yeͭ*))h 2`w!ź$4԰NltB$"_)WG(kP7 "eif98b"!Tu3&PN4lfΩʨV;:X`&Ę<-| \B':R}:XC@!ƺ1Zg*cwvGpH%Qy,JS$1Ģ,5@LXy-]+"RVŅtZZM<鍛fG9:! &D2 /ʞ|qe xeB   BP@??=[ Z1PrΜgxᡱ>%;=TI4Vs^7-,$5%R,@SbLg鹅 ve3phC_~"zڮD W`=ٴbηqzu0U%;7m[vߎ5`P# D!t) gGn}M#SkHPSJFPB1@BBL"@Pb5izM?;793S{{w_nweqO=}?~x$vC k‘C/ͮN5y$|m& .RS,#SPYWг烀塥X;Iy)*tD $j d1!Mˆ-YH!c1=O|Z+eBZVs=euCǎ=}ڷ;㛷_\Z\T:v^<RY>} s>)CHjn=+GZ8MPEDe&\v{"◼/~<4S7%W>/Օ:psѻnQG C86#/ )VS;/'~ضskVH rDiA1ϹB廷w;f@] Zͧ1Ps^nB_^Z^^j쨪L=i^HѺAv,!2jQ"P"ɱB]%."h @$]#)yvw1D; $|v4hw ̼:L@B# *nLw'ܩmӣc{wyo|?^ya֑Zzk^}r a4Ă=Ab\[Y= XjpOF b4ɂB! cIbJ&Ry-}rZJ/~4{<>9ݗkVZs9A yZUG;tgjvʘ")99;{3'ϱT(}>XB04}gY?=evYIteP"-&?xgAu[(ܓS CEU~{\s !ײ6 t /Pg~}U}w QAS39uX_sKˉ ɮJY~׾Շz? PD걓 OCfyrjn6^{[JN1Q!0!=@IRԋM?t15:WQdQPUP3|q@DGA4*0#X΍vwOVA{yw|5cO>3/=H0:\B$$FDNqHI;f"("QTkF"LmKoTG' )@Vja*jhĦגA 1ɞęKIk擏?y};lݽ#g>zs#[6سX؉*JK u1 Qgk2T0nI \04(3%y3'^;'ӧN-.,x&&߸i󴺺juSISV=Ksws Zy?@&N0P@( A(Aq\?(;c;AT Aܷ`?9R ff++@F'nw?'?g߸ѻ|sgmޱu}L"d9N[I'4ƨI33`** e8{pYj$@ذFba-)u`h!cv̜?wOشǿ'y# i4˲l[7Ӿ3Z2Y:T+ lȆHQ0.pZ}xFX)!&J JM3uD D,l?=zB,@(xnx_Dm2WOtw'w|xzE{B¶bUEScO>scQ̘T!R}4MÒzn9f9 J婐DjLDB$!PR΅$%ܪHH=TIUbYsU kH]h(e, ,Qf1zӼGp 8@bRc`r'$0y3cG!:GUY ,13" <ƙ gJ.1;6B$Lt L9&ɘiDzYC3 >Zo[͈@1 08?=x,;hHl۝ haFt^TniU/|\?J[_4f Ĕ[luߖg۶yltz:Q2"c#1$ j 9`Bo1"RUJUGEN|_tCg/{w޲gO,FiQ̘jJ" '\D.iSR$x`Bg X͒q+@2kL H5+41FaO8{s#Ssקqn~wC?7|cqeVA{dB4i M1F:z5ZP7ZDBA!+ 7̭;{ᒈE!+ӭuš^|L-eD;'OfexY;~hύ77'FBXQƊ)ACFeΖm|C{tƱdl@FZ՜UFpIDqzv:˚/BDLd%3SLYsjctrL냀l8=J*ߘ|c`!*:AbrKp6&bSO<9gRʉ@``H]&Ѯ*!y%˲LctN9L\UUE*H_&|D% UbLm ! ;> ,U1 F!IC QB%1;q-Bf±gČwjD)f29}dus D1+uc^?Ջ>c1*9UA8܋ '_ݽotEDAP [i HٿbSgO?3p) e{\|?O*QUI0,EPSO?gZ`jܥ7r='1C61uAcU<ˊ/p۝zjaWg~v_ , 榧O>C8(_w _e]KUv[~U{_8;xⶇqӮy˲ܤ@G<2>\/w#xrSK ͚"e1; K6eрEӿ'B,;ڱvx{޽khRȎz8,t5$c`1*w{v &]j.%h- ʝ(iJO % _w놇@KTL# 9jX#R{bJ<9rȨcԅSM׺Pkv={?b7ڝ EJ}Id(r4,X&MP0ِdϨ'v=@Qt3IՃf-#ʪB#G+&FX*j/~ڋnݴ/g^9 21AM+:]r)ZvٕM]\|;u;U@E$&M8b*[w]6mGT$\&.VfS3yqڠ`KYq;U$>y&TS䰆oJu"$^I" ƐDV4Ĭ*E&|E 1aNG~Vz@]f6κfbyY軇|--x׿ё~?*ۭ.=Hkþϝ\y6͛"r$D@:chw7}~vbK.RY.I.yH.]#L%T(1].dq{_O||'nFjY@nOݩ.* Bru8Z\X\tSg.9[ZPg |cý':OEpEGi|$5C ghxy̔f5M>}nxC:P"#s&qN&1kvH @*~jDS"'F^"$&Kg_SUf::&BUI:*I00` 7X1'.#[8b29M$D&&ﲢ,c)<@8,[TCj]H"XVD ψ܉W9rhrzdbfsj)hXU!x7nz3OˉH =#Dїϝz;vc Fș_?_}fFs&LLLsDz o~[߶֑RYޕ;jG_}utlhH&6C7Q݅Iqt[/<]9Cti חMߴ\g 1RD-/=vMw7L]PE kǾy_`Q߷k8myZt:c:ÿ[;:/ﮚa]{#+2JL̂fQMPd j!(WW&;O}?cqV WVeCÒ*!F ƭ/9ug6lؙj# APAY.-,^8sfharvѠfgs/vK_~f_?gyUv!JZܐ|{i" 1)Po,PGrL^JXU\}CSf4]c|b㯝^:z F[Y<~ɓ|֜R50Q*q0NɁ[OOt'H^ ,q:2ֈcQ3A#||C'|-cssێz-oy/uEqҥJ*$0aX4b&50j5jPm۶e}9;6w&M:Mh)@F!U6XLy׺Vup.(8IDjӚw< Dw\1As;```iHE@Tʐ-jfzɦ)WFH t= [S)]B VT:1䔨(-; ̜ƳyQ L]pan*YKEP0u7Ȳ3 uR. ²Orp$'F;!+)azq϶-;w^qzʙsr&r"J^#ҁ'/]o߻uF!%^ltrs?|u.Bс,򼷲:ħjz~]/4ފfL=ʥvy?;;9iә ^;nf72C&_!0QU,݋۷?a0ƨBīh.s[\8qm\XA,+:2::6=_QG&FೂƑx葇KO?w7޼VFNJWtzoo[(`j>|!!0IDAT_'?sxt5X%V4::Ezv`IO@6|3)ofёҥA>1<;WLmڼZ=CoљP:&sΟkuwJ3ww횷|~Ջ2iL$$1# )wgEm($54t?f)j baTʙKV{1omܼa֑I% 4(EuL`C&?%f*jCcg"c7$i13B콏!˜#y-Ւ)]TWŦUZ,Q{10jf`>ʞyQR~5]T5ƐƎarΥ՝3hZjb1aFh.\Zz駫Ņɉg7ZBBUܰiv.#ǖJR+BE rgn߽}tldwl~wO?9=:,(;#z{o|C*'3t~y7o[81c72>V4+˙w֭kvFÀ.0vKDE<~wy;&l JA\[[CQh;SƺzZ* Lkb;;ٳ~ï6#G{O?ivڝ6Ƞl\u'.<;Mі[iMXZ~G?sv=rˇ_.=m-cP K.9WJ  D'$1r 1j !rPA"ŁeyL"ĘB莿vz7]RdW4~gF<6=S/wN>]W-Oo筗_:/١ӿk=|=2ɧg'+Tr|>*%*H H _6 QcuT4iv$f3S[n޼mm;l߹uM۶MLc꧋Dh%ETŚuP2VmԅQMERJ"*_.o+CMD|-˙ͯꕥksܹ}?S?563~jڈD|k_k_@"*8C! -Qޝ8۸gC'ޭ~u*-qE@w_yrv//7mxLsyH:D%d%2:CؚP3\!!6c!1$(f^b9V#2L{ l4CJ0 9rc0,._h@LAb#82&0> !,#U%f>9`Fa .}QT!ZO Dy/, M$,&:q5LI9NtfFB422:C13DEZ!D"R*j]J. fJ쯬Ơ8Z\[Y~mCсăMvڿW~\o*2`@D!1c#c}/<#ܷuϝ9'/#HM S(WcS?S7nZ*B"`6@yЫ_nd٧UfeF!B5Ĥ)!9}h5|]_:ε-$5[VzU,+` >gtӅʫ#Nmâ% XlSR}ꙧ?0~o{sǦfǯuwm}-m.bjև;sm0223_3̺{~ٯy|//ei:ruedl"DCT$F!VJW;b|O2G5IyX0vl`z5f3Va08yٟje JU_䗖 5JײA^U`,s:!%eE} HdC%JT1Q\FuFcB2Y4_1{|ˎ+kZC0ЙkI7GK/}x?*8O@&[ZhܒVM{#w;-WlĢU!J{3$oW_S??38**SZ$r"!;dBHHՠQd33 gL-s !w!T" %*T{NdB+d*DHfLcL%\!3'M1j*S#$z&ˆHL~@ՠdC^DU bU>sMB"R4$cRN=yRA8$g./ fb@)H`,8tKId>TGQxؗœw"#E#бZT5*{F hJP"TEXcG/{uX0,^i7_kdzбS+Wy^səFD0Б}~+_k̄ =CA2 8_}Y/.Te4閙>E&rĉ϶lޠAҠ1Uk>Az c o}|TU,DA9VBt>ϊyd.缰P5e\<~xͬF'(&eoxo|-ww<>;|yָ7_qE_dea% zͮ[ ~-ͬoN 7E߄ (jh*HuV!v4M0}yt䕽wn>ؿHaa mnbm꯮u-6x2*O0N]22].$eS2ԴZI&%Zj;-xOdդ(A5tPfJB pVAr;Մ )&Sn;)HYgڠ^є`D!zbL:хS'v:H/ٟ=wv5W/׿[UT$Fa86p Մ$w:;_y7f cnw;^~?o_{ZC ŊHŤ(kr3e1f܅˥J^d@/չ ʲE( #1"s5b6*@ff i2&P v`2wcݨevbD(8-j @"`u5e sv+X Vy2O \$ 6#"ڔVAa "KӿDܐ HjrSsg.+2bdwEpHUUbK@pLRBy4MV ErGOou&Ǚd(}ӌ ~۹}{vl}™sh%ငPuWD%#ȳ.<N/M4F*@K7r0bWa \s/ofzj¹skkkcrjrr}'Ϝ:quEqH% &"gfSwy};&97U e {H/~F!|wEvٗЃ…AёyBE27w&&7__\wuQ-zݱWO⴬^qCǩkl?JsyEQh7m4rBV>23_*P^\ZZr0m9[LGQ*,3Yrk4'.A=zǞhBp;m߰']{q>YE@̼O8tԖ@מ?r, U-߼W2v)[2!+ y6u6nث"AJ{#u!ɭ#dHuF J ^7Иbz`4QU.|E4Xb5*h(aNm1n!Ntۨ$`R׵'ӐJ%M+)H,&^2ԈDk)3UB̩&M=>?s#7W/[wn~|0-,.3cH2PPS5TT) BY;zϝڹs7 ʛ#S'Ϟ?ߺ['3ҥEӈ33_)iTT!j GL47TECS ǙSIEHhms0ee!;fbaJ; l05 !ᆚͦ崂&֤+1U%x6DAE5" 97Mrfd&\LfB&UEJA &$S]:EA󜝋AȆ5/n5D)#A 4όXh19Ί\D ]ibLD &*E #Xc9?(K1 U3BM% 4 H#;~YE>сcǏzqٜuEFAk8B vbg/,2R5`AJLEQ0)"3$&&GX~ӟ^wҒ%)~eF1h Uزe9󌝗SŚjJLEzwێ>n6(MLӇ~=wɩIr+sg Ks]ٹ}}V3iUD׽эܣ[:w0>>)iL%& &LQ1FUcqfjzݞ]/p< Kn_G^P xnhܿ}˛m{ۗBPl|rʷ:ejXMŘ?w?!튓 j*K# )5wY ZE$X룥PScd4u%a Ƽ# 28T(%j}q}gDUe0D108uCSa%@҈ش=yāe^zocO'>vƦ/,/ !!X _oӕ ks&Qc,;]}<~KW]WחGB?exqz$[E0D@t}gBT@v1ưtBlzz2k7O_\xhh 9"j5,n)chHT I5a,ޚZR}%UTYR֡,E^VUYV}"c({kiBj)*(@ B1 lDs &vĎA-s>d$/gx LjJYM](MD%^@Tc5I>h X+DĎؙcftiD`;^B6 |{qa/zpY!HVz{U([M_u՞ťxKRs9g@iY ;U #kR^`8 |߇?>б!d<:P5vl{Ͻ?Ю;ƠFD#u26eZh0@3҈h]_z7ug‘"w ch7[Vl0wǺi/hZyĆ Ph4F+{5YVUAC1|o~}?QLoX63ѻȏN='&'rcszB}ogZʙKՙ{>;"^K|d,;LRp{vɱXdR !Cj0i[|[7ھZUbfcQUs!gO\<R\]U:>'tܩǥh;E=X3fg_w}_I?Z9,*Y-F1 үD#^ ir  )IM-QҐ kl QpV_ҩRjZ]دbh"zc8/ummԹӧO?y-xu/ ke_J6l}t_D YDAšZtך0.f3"CP0aFcfʲ>" O<=oJϝ[8un*̦;sVQVȸ N+g2@,*z!a`,KjRJ2F cV@A#*m5œHUU9&B*g.sUQJr&Sj L>+ h T3,!jTSIY#BP3dDyNf*{,٥٩iγgt:`2 e>MK^  ]ERp>bׄ B`9!iYɰ fΥR3Ik\bbPS= h4F%25p'^{uܩٹщĬuϏA쯍4zȋ^8aP!c&/2vҏA5qOg~eϾK +KJ>ϲ ?tM$Ja捛6nBUS!bAV|o'FU%P2γH 7.e;6Ej(Y;` s(^G'Z"D1jT_wׯߴ|҂#p1 b{l#/6̭ھk[wld5\?9kz{OĦ={^$bc[w^aR&HX4O0Lc+ͼ TDF0"fĦeZ0 #!y S†H" ξ7mauܷ~۶nزf":W)ʫ|zҙյե ˋѬ./Μ8vsS}0EUC,)p4A6jKgVR4ح=>V'+{$!qx*7HJ~?sV|k$ZQMjfGM5[Z՚:{/~7GjܦVU(F@'UM5L\$?zHBM{,Xl ݼaMo2.bMt f ?R"@M`%Abev|o~-^C↹=pŇ5Xi0'pGΟ|mi~{}5"(ɟb4's(N1153ꒄ9bB f1Emx \e!d\ "ŽjHEV@Z gdFJN$:NNjfj윩tUcQHRM:IBV5 %s&!\^@q%M%FλFUyhXbL'#X[pdf.ˀzX-N. W޹%7jd泜2*0:P<~P$t !0Ebe90Td2©s^=8>9>9?c *%&#`kxwݛn:rSK\Ig){sL"V#|c'x#dd$ 1?q6j--\h>eyf5)PB:nhU"tDQE>xۭO>uvGFXTe5ZQ AUElgFɲ"]|LD2̸g!ܽP_S&S7"5!X821G}o{+euai2;Lw3Εֱq٤77ox߸mnczk+ +s۷\QVĉ:b}D86Bs,5m%E{9 .\;NN4C蘘D"&5:|b~z|fʼ̘Ԭ냁esg_xKOOcAԕERO*۷oݲRiR1DPc"BrΧw1Hp/J>z1nV-B,$qrBMeA v9~CEs} mF6 &\OO+O珟zχ>~qa푻4]m^!"!y$HɔZhQ?Q(1a$CVݷk覞I D (h ci|L"Z`ɨZS% > 怛Mz/?{klڲ#0+3SV{aiyuin4\ $(&ADqhf8gQ^X$ELy`"FLe2`zhb䩘j(f#T䍂3 u-eɱ#FKHi84Js̙A !% ۟2SP AcDeic"0$Đ>>FșH=T b3\NsiJY*+wyěyD@ώɁ3( r܌C-ɸ@9VBp99df[RO@aPQ ۷nڹ{ǙU7Zj̡4ӛ7~H™ޠh>ug<}YXXDr{]Q$X=J@-Cbfw{gaRǕhZPe.s.332yλFQcf͆dY̋cJ)ON2(AJTT䲶?sxd323YQr+ln4泠FƤ$*<y22>31/|BWY9.Űts5]o !}7XEM~pv-1XSvvL}!#bҚ2c,-A LT 51 ^=uKnx- 1("E^x@[Fy䡻nCOu:>9ŕѱD/hfr<`Ρ\2tfdf[Rg LQ:MCAzMCFix*CyyȪ!K.Y'w{[ħ~o ڝ;F6B.(@b[ףAN+ '4|1E4 9jPY FȪf\?,(1%cWB'0'SWTj ֭۲gϿ7o_:yhe#V&aZs#n^o=q?O @ ~#33s+gR jjPO*k c6b22C E$GgA1D0t޳A^K˗lPg鐲*z"{O!Ee@R$@Lc Sb0`R8< :y24BHqpj?Xh|O? 7dg"U5Uv~4FSMH8-jYUc^nRKہ҄ Q2e ʹDdټpjm bJH! ~~nݾkZxEC,Yv}g~泟ܖ;WW{Q0,?}OܾkG16±<`z>|M7Y02)BR; lAo8uh9, zAE@h{Zv춉&4I@as<~FTe}Xe9 WN:37:a#/+X1H;1V֠b MJ::1ï^Y VV{fc֭vnvG2jb BHL`D *g29v!kS쉜T1 #B0J; O)+˽#zgٯNͮ.2`-(dֹKx`n4߰cio/|_8ru7S[vM3J)VS#'+&*F-CC0p?YWKR/ 5$ GGΎ갚inhx h#cc=񥉦?bqŃc7-/7Ђ;q\-KU4Qz/mȃzRO6e ()@Ҥ$%6D$zٱ#"HuCj†)9;*1СE&ix*rhgoyx(fS lF"12_˔x̉P121Vp8nV3qy펆HT|Um$Ȳ"*b*D,Ad1 (3a!$,b A( CHF)h5ع*e=":Ѡ k>y,#@ȎscX39PDVQk֍`1U:n; ji}đӣcssffo7ٱ<=N@Df00to}|Ibh5cA+ k; v鎰jznbI$?= 1IENDB`fritzing-app-0.9.3b/resources/images/splash/fab_slice6.png000066400000000000000000006015371272363601300235720ustar00rootroot00000000000000PNG  IHDR1LbKGDIDATxL-Gq.WUw;}v89G(grc\_'lc069(!9twÖ?+LW='-y̠))M9|*L󧦚'57_]UWH 0XC V  9Q@=0zv!`98pb~_ƝO.럼ꍿ';zShaIZM0h (``@, FR : ֫ PlZ琅@@ *[%!,VA0378KT*S6mk{3>u {GKeZjP#Qe;]^_/g./GGGB hS`HFA<1xGDϐFTg1 $ċ @JY`IBȉUP w^)$c$ =E\=ndt-- /ImZ7+gfgsZgq3Ӥ\X]4 v[OO<3T[Efˮ}͸%^x@d2-?ҿ}WͶBKmݝ1 @,ʫ+/ӿ/ulVkQ ̌*|3>ĪouuqvظD>ΤqaLc;?3Yȷu{i}vt~x%_;tЮW)O\Yܹ{tKs+wvv͕sq&k߂֐V*gDpbfv^+ XZN8_ȤiBD.eLFKB@ڈ"UmvEd:3?ǏG\_{O'ɗ068<ۯj+s+ J(LB %fbEH 2>m&) AѶDL4ޱ* 9^^X;{RҹK*W(vtJ3S#۶&.JKǏTP&4 ".q)@g"XUh 7a'ibuLD" W(fsA|}?]_[TꍅV~ P/{~퇏ٕE{?u̐ZqyHj%&ӖDZnkMQ!U .jiV)41x/A0֚UlXB>&&ˋėxt]HވjMh϶8sxZlRMu(P&`} X<8fOCQ* e  hg zE(Ŏl[{:awg?@О6 A HRa`Sg@(i4FP@fAԄޡ Oѻ߾#]Qs$x 8酅ztl/)0w&̨o۱wS|67V|yӦHoyA4 DQ 08)Z, \!R/Javz:n-dZr04s *+keaYk4AXI)hk+vp3;&8iV+&_=/|ܰ!@:J)qҀ@>u9$IA{LJS^w5F l&=ج4[rBD=ij|ozSwKRV&0"+lMP =:$'2ZY}?}MN j.~sqޛRw=P\[<<884:*zZRH("(P10($j@,< #!0 "QU)-*)g$&@EbPNaf.~]ratMWa6Ñޑ|_~o|; ,F-;޽?g [le <1z<09PĂ( I3(" iB `$NxDB/^!)0x(D('1Pw64ET}j(AEٌXڃx$! " F Loj4Vؘ!j41ah,TEZÛ6ٛ+\0JZ(, ^:{iZXYrZOZ$qIl2a՘* }C6i,,,.thC/aǶ[8wa.,?ԯڻDJ_(w5o^cQ ';v}W v;rN\cgUmzU  A,!!$.%ʄɋDFM uR|a pFq6j% 26=*}{5o_޾qzG_>1q[ǰ^k%#iL$" "5bTrx XmZlkC#DQA#xt46=.CYGHwGR~`9=7ttoFnT:&VV6o۲)Y_J"FjmE uޛ($y(DXN "%4mqN崱T& sANZzRۺ:rF)6u jt# Zo1EMT2įT+\l`&X biZ}. UHQʏ_߿{űo6L*-K^!Ɩ=jm4Cv,:P<$E@,I )!h yVW| w/(޺|JXl\F* :|ʏƛoپnJ+${ ⴐ+8W(W((EZ(1*2?K/> . yk6}O uɾC#)'LW/]y-ZůAMz$A$&f`Dk " D %6BvW噕3S=fѳGy pw?nN o~9sW\q?Cڗ7M;׽|wжPOۿg{soٸCJ[vئ"J0*A  *Hkk]`/!PA2F{j$dAQhm<@+`d [q*58,J@BZk5 @=]َ6YH"M&$ Q*+%ػc©O|[luI0#bd !@dF"rŒX>UK_Z렭kRO ח녾{fKwgNw?F ([ʫq-󥶕'_|o;+4wA۬@Ҩ?stR~|`Z482Ze `aR&caH,ŞC2-")&Qh P9f4K!F0@)', (i8'ީw7o醮b?oU6RGOG>:}O芛oeM ؉3Z޵Z(aVB鰭tg{n(0pG/-L!ٗl޸P?Šؾ%/ƝO<Ӂm3v/ 8Gl[J:Њ%Z|+hWrՕWz#֠ɉ$$`^k0dP._"djs;v\^^]\XYtm=C/>﷿5LJ>oZ? O>fiDzZ (o{'گ{b+ɘm۶~_[$& 7 w,/-.UZ&@@Dъe1҈38I1 ޡB"`:Ĥ%ed2m")Ԭ^zi6y{S?Nw15y1UNs=͇~}f[knl˶ꍔ$E -L}8_*vչoz붎}3**4 Iδwnm;ze6oG'^:_k== sS2m 珿}5WqrcǶŲ:0qR y`vD,9K)"R1LZlcGń:J3K6V׆oa1%_0m$K瞹󻟹̥}X0{oJ3uuwn!/,:LDF)/EZ+R957YtF|3!)r>qXk\[ q4oLɘ|/}>1?ʃ{ͦ{G[n9jĉCex1a!P!Pı5:b1SpJo@RyA!DBDBw\A*HTd|!Pi}7Do{W^D` m5z|<[MߺQP\TԚq76?җwwriuyf*P)Vb0\_|?}o-YG,^pMmA"Qh*"D$"" k%Q, p{lb +:+LАb2 Z_Xؽe[3䮸D?{?=;I~0^w|뛟9y9&ް}͝ZF(Lڐah<( =CDH LJZ;6@ !3 j/zDJִVi6YOZ!(H:FrH Q$HBH4:a VRaD1!ZVB@b SZ 2*TZ J裼aJ2?lj^7m#]e@ H P@+&!Ɨ @` V<|8S(u3ӳ}ݱ6ģŎKgGe-W`6pH0"*R@`mB(۩@SȋWh"P",4 &!1DbgwU R>lȳO蝷}Y8{- )FNܷ-3 {cLF^P:g&:=1462fdttm WfX/to0UW0&ۺ?y,tl mS߷~˖C}6 uX^X3!a"IkImHmJan5:( s6ՊܸUN݅Ͻ8۸wSw~x ]OXv;~wݲE4iBH$%Y0zH[iyi%>( @vZP!3j (@^'b L X=Џn?wv*.{ū~=ݹ_/_}6[I{t" x40JSгw(H}" nB 2^ G^i% @@-uبT]XB>_4^h^~zD L6`(*4t3/aynHX(0啥Wz[>|gTY'rQX,+9;qc7my]!G|BP$PXH P  k*dQ,=3T\[ըQFRUOBِmEtRc+bG؆՛s^q-Ft0=_?}O?i+ ߴ9/1N 8N%hƩQE$`x (00xSYA 2 ")2mH 0$ Spf`-ժL)P*ģQȞ!^k>f,J50]%Ǣe\8I\,v"$>mt ҺR.hRkw{޽?v[FcM^H "D>e.>hdV*2D[mh&\&4!Wr=ѱ&[[KQp Yx篿j57^w𚙅!f/"~ {zP۬Lw"peq<3;;iոiGJz=I.ר/ךkdP +L82&B`.R϶=\h4R$2J{E Xq@kf!yMh1OmN/>wo{¥c'WadFw.,O7t&Nl(Ͷ8(ڈwHKmkg3^zfe5ٽ,Ik1*u^r=ym=t.eg(r=Fds۶o?z dub6V$$3CW^9N(6fQiz2!+F$c"(,Lڠ xSknBtYYif0e_Z;ý]\j 9JW?#_.mڽcLbv#O,T))4Fg.Zm~v vvαVHIV6 z@B".|t;6_yk^lm~_]wJOދwJ0PJ4ܖsZ>ee)"$OVER/:ZB`vJPDZkTDYX`"Bڊ͕4ۃl[PS>_;2qH()Id1l9b ;s== --nfg/?Ic W6ns@Ϟ7lظk\1uX3 + Df&*˼$$J2Hnptnj6XX:R**vdV إh eu`4_|w<_9i w:s8 ްyö͹1ih%"JETX)ZT0&8ZSR5@G"(@iE"RfJm.-,G4Ʉ bB|32Cm޶}cڪ3\ڨSٰsGE's-{s]JPjϱ_^ FǺJ&fOb)KAMMl4#AdŞpV+sJu% B*މq*l 2{A(ЊYo$AhŽK0{F@z!W\{ƑbbA UT{;ͯNK0uok"[[)Z'Za\^^r~՞ MZdgm.v07+H5l~pW~os嶝^q`#O~~6~\oD@ZiR;E:b`$&Fba .PJl6ZDEZ%ɉ `@QqBc\b;fC6[s[߼gu6[d"T伋 ,cetf.yq/z_?g[Wt>z쿿61}4߽XZE0B;7[J8Y<" /^k Ɔ7-D20 8 ON8TjQ[o'~WWnb-Rxе* TB.eRĜFƼkپb)A24~6Q 2^;O}|d2BOgOw_?̎!2ƍq`MZ/:f5  1B W%ɠTSZ2}CC c*K+.mV\fd{RY,c6fIg M=U"D$id@yx"HR:T)bL}@#6y4℅l|rDhJ!riӖK[qVV_ͷ7l9Ӎ?ԧ>V_Z` @ I1xRI1&p:HH2ʀ6n&VRG6HTeq If+R(3G^xcL6ҕ|ߖ7>gO% AXR|r >[][vF.^<=Q[ssQ&ѵ$,Z!Q6ZY%bHhKd&ׁ"ydr@BLVR2!6 łhC)eK³<|gոPmruLp}D=.ɝw/%(I$DгO]^j.L-VTCf"Ơ R]ȩ^|}ٔw͞o]7Hi\m5u؟-\8;S\sӖ>&^¤YMۣ|O^:F7$Dl̞<;ٲFBb:m X{ŗzy-_LY DBEM+Ih/lB- ( A%a'6YE (aV!j}5F[4N7c[6ʋcچwmܧ<5}{]:^m00 Y ND r="FF҆E0)  @t&@N2b٣3>5qCߝ;zhxplqi6Vuq~]0\ZY(ZsssCs\5lt苃}[Q`Y$QPQ6bO;}qz@wt J3M҆zX SfAq0L6r !A޳gB"@O!3XB.S~?L Vek୿&ڷ/|W.W̗ں$NRKNDM\[@r?uW_/>;~a@Qd̚š~j΂k][2\_{[%|hY6s}mq}~r :7lXߡ`uvⰣ;ǮՉI;rB=<@}qyf~aڤlh@9M8 M(:400ئHi@bt8ˊT7L>W44%7v,޲ mB랯Mji.%h2[?>7:ͯٙ/?"[?O( ^#I-fH<w kq֣0"kARYwꩧdl[ kPcf-.O_ W׾i4jBj.F)bKCmیL`|8 ,jq0#_gȈ =19<"HxɺԄJ{92q<}!brܬ_/q@j^,^5?ֱM-K^}Osv;BJݵV+vF]qvj1*"k:^DJa)_޽?Z+7KsycQ}hI yHxᜎ;Jz'/l޸fM/MN-Mo?Y ./իJ6dXL33w_=_ 2MA"gNOyդ޾ FX\6"=w;'h҄Y11)Цy#Q;$MCzv !*@pyq=iVW;ۮ;xh`:5yjrvvgdx]7F& ?~ྩ>L$&r@2JZJ)hԑ SB":ʰFoewL`QΣV詼2?{ Imr7Zre%iBf z챣~ً"=Lr($`daeøX*W 4{f DBd/Jx֡Lǩ #Eo}޷ܱu8xp),|ⳟԧo_~ƾaMX^=}Hػ/W(=-F>ԯݼo[wQM[YCţeL1o֪N^i%0:Gua(d}2b-f :$ԓ0kf 5H(2Z> bdeBE+ӗ^xǟ[MZZ~؇;|/O]]?R SJ{;qX5xU{W6y&Z77ut˩0q}c,Ο3}}? :mƱZ5]5jӓ˶wN婩]]ܪbC}3_`fs] &.LuF XQx$-¨PsSBMgUHq&d +$a`|#er2 zegyj+qFC]zoʫzG?ԩ;}u@ZmȸLd̚כ3z%ՙgC]mۼolEFIdSxiկ"Z2:N-xCH48I ɪ:iNtHD$)x/_yZƉZ4gr, x@R:OMb%NjcoujfWbZ??q1d|:$]h+IXz?}/ŧ^|>˕-F)`$MR4 HJz:*Wֶ~빉_>Hg&y߻~cr~S80] x:'~{bϖdJ8v>}?8X{F68XkL'i*iB h=xa"QHH9h  B|Z,Cжb1 "*$ 4RDԵf܌k}WرulS{! ׬=G??W~aۤYkxQHNӴ+v¡ǟz6Tj&l+B$a&ƭ(M'3 Z 2{@@$M8!Z6aMg*\Vj٬js}=箟7;/||k/_Z^^ @= {E!P"tF,:~W yƗ Ut3swl1LWoSl~O??)6j˧Tawxm6vV[n}}}gO9s[ ENԖ/4# ]zSαR ym ,Lg {D'SfOSDIЉ>D("T#N=s)+2GE Q&E{T[Jtpڻ;#dJE{)W:{l|fb/Cjp©'+|ܩDGad@&ADV^lJ"bQۤD#[ 2ג 3e^f._jz6lЩdt-mos+e;::BMZ{EXC^,H=<}ziQG9LDΊY$nB8fa6'DkVXSv ZFZ$I[f岅b> ?=hpI</'spn^n+E+>bC=B,``uebo8_qΞͽm3xD-K߻o|n`P73Ut&uJ[둅& 1{Y#TЀAbF'/)x@<:ac(?Pxq@7Qa kis BT+hrV-ϡB\m$ck?GU3G >wmǁ~6cn{w|ڮm/:\]xd6u*NQyF 뫫U咎B[ͅ/\}զ8\+JŮ6@K]u'u/tira+Jzg}ئ|rȱ=pSٲץ޾4lFYgRMPXR Bd$ud H!0KԨxq+քKSzI&>MZ-7ݷiæJZ]fP^y˫J=%M-Y#m!Hzq\(2ĀOmJƀRR# `tlD5[Z(c"hxk=GuXf!'峫[(6iV58-&vMc;w͝z&ko9w!E HZ`e[1DqoؽZ˾j`Ʉ'~ꪫiVVWkKe:Napp-٤EdڊӓgMor-[eZ#{~ƛwm<9=yСRwĹ=3ƾ[_SO*[m߾7EzY@`XݮI' \@|ٹDt[6Rb*  @$ rAq mŴpjQ(d RT>MZk'Q& t_=zVI'޼u׾+_ pמ'ƃK'3.tR*uhPe +KN{𚑎} o->\_Wt̔ "e"ֻ{ÎN{9nxw<;5;7M;vu~ɇؿ`mkue{sʥK)?cSlujr]<|iyѳ'׏;pDڊ YW(" M HE0m<   hmR6̒i/u sǎ?c1CjV$m߸cnz~/=yr~ _"h *9Z>Zb6}0IõXP+%i3$~O2׿ݯس;]YK AiCS+&PNBD2F^EDβkY@Hdf%Ihuu2A$2ƥR 85X bL za:ib&l%DгW\ss\ܙΟ{WߜEM70 7m_ǣ% ryk_BWzѳǂdًBDh¹ݸ\}T ܓ6c/ґ'K1sޑ׿GaWcc0P8 AHSZ<;H[q@ AӤSAf1F;F@]Ts.f\Yen[!ٻjlފ(Xq6=DLљ`1*LpD%Т A"xO)k$Ɨb1q @cp;֡DؑV1lGGLAP)CF\y./̯,Oms5{~?cM~+H^(`3BsN LZt^mO֪בe͆-/'a]>(ut::9[F={Wh4Җk(fdxdQo(z{ G^Hc87;=u۞Jkez-;n9sҡ'6o>}mR@,9AԀ,4gg='"k L=[w\n` V"z񼺲xRnlӖ([ S(bd@Ei K.:polՊRN9@ 4ֺP|ds䒄Q_i ͸ժNMML]J 59R[NI|"KKIG9fǕz|u[{ؼS$Vg=98׾]}۪jڹ}g&  3@3:s/<ʽ='O?{ܦ \g^z5+ +K_k?MM./(mgXjT]XlPnnS/s]\u`?'uy5{[ ;z2!2 2(>Ѝv\uNYT1t &O^Zwo=T&/9 /Ng/Soyk=Ls= |bjF7s'*MF I:7?흹Ykx&oBj嶶SlѨ9}i>u6C {V 90704(h,2aiQBXk2J)[>>3? Ss3\bm]s~3V>ut{{1_(fW4a0;p=~Wۿq ][lQi6ZՊ&b %x(dj8^z߳U5T+Kiܱc'"moS&MOxd%/lk O<X;86>;93 _}/~T akldѡQAH[  jDAC@Qk&"RBifΣm (dI @!$ּv/Xj) nbm7?<[YS :܋#P<|T)1d2Q-JaӏrG[׍:@sJ†޻}jz*I0 @6cQ$WTȣ7kuDbLjJ1(R@$ "$kA$A{{gGs\ { Bh-g3Zk~:59Y}㭷\>ͧV|ɮ 10ޡQ ^yck7:ۻ SO=? B"D2/^wӎ].k?7_Hl8=[loG8C{`Gygd|vҙSg;z&VFA533^}HWV^8q|l6p,@)u*Rĉno sĢV{Kd,iQ@!ZkLHM].ZD@ )Ds(i5bG-: ٸ٪6HzoL ԙ;w \Ͷ;N 3sj~ş]< {ɯۼ?R&ZMwLIk٤o6=U3Ж0o 76U'O)mLgΜ on+OLlȆm=ϜF8sWd)^alZFuC!^>~RLG6 FF lt a`?K6zSցfqj [R8ϚHeŌĨ $a)R)[z-ɸ`7l}pmif)ʐ\H[ܹ&aq൯|+غ'>лqoH8w~c0[LK ͆ugbh<7VKyDԁ0Q+P3ZHLHe&nj/`^ s~MKȩ2 B-T@b(DAms'NRL  @c  L[¾bͬ\8zkGntv/wvrEq|Cں:B\]mcsc/gSG_ؾ~c K O޵wT6еzJ=gшAhuܩ4HdUj=ww_G~?1ﶠsٓ}a0~CX[mIZ7z3}'*Ȗ:KmCsO{ P&4i@ŭ2D90b! ZTч&*`H,  B^k#K(r;zϞm۶IZɿ~﫮-85,~JzHLz٥ l"Mmm[m*ytk5Z0]Zo9~(NUmŶ=՗gWmZ'Fo}2]߻߳oĉ#߹o|; fJyeXhow koWQh$k:LMҴ<+8MÈ4JQ(B׈5Iw̻6m. KL`jKfjNBXo6$NX 2ʄYCAeғG*M*7+R1:ؽ\][W[6o~G>1<flQҡ KoY=d{w./vvVf{3ę#.Y~^r/e6wwf;GV/:\Ǜvm048^p`wyz6;Z1*nhL^_N]7=ybgRn/?ґMweWNgDҤIa.cm52$6pB4 C[ Ha}6Պ!9p!驥*Bxx9~[<;WB#ܴ}0zZR[Y^&6Ԗ'_OMuiZ"6u@a>g!.ͧ&eL= 0hcZ/ xPY6iuyn&qAX ʬnVDS҆0 hH0@xX HL+f{Խi={w-^|@z/2{dЉteF-W$qkÁ6c;| _{З4S&f YH% *]*?+FK f}}s_>p׽bl`/c];n9\?ҋ>޳kOӖoׅ/Կ?.j۴iƑ1l:VBR (MSPAMJif&A&Lg6)hH-/ODJ)1q W{fq}TnyKr\i|Sx*t¹S'v|wǹ_sM֡7ԥq={dXPV8Ni7b۴-Y_nݾmMS33 A6 ADA`3X#iE& \L($I T \-NH+)J}ZYJڄ& q6Wj\m>y_W?K wt7WVgtu[7n3ct̔r&Ɔgg.x)+o{#ۻ:ƒ7(B&3?۷ܲcSR5Q7{ zo8:ox+N?wtK|3+}~}J/Q s$q3刹6M|m^7m֊M(za~!p sNi#ΧqӉm8:8⛕jdrfxifu³3\\]Λ~,|R!!J30-O9<p[>?yۗWmy>pfn_g?ܹ)er=C=}B3ILAֳkKkD@'eHYФ*4уBTBF 8 {Ш $  {К)UfV;ڹ[ ąxG׬gܽry6f|>Qa˺.Nlݴ@'G_:J>ԹښGf?&SD(\dKG PXG͗zy^|YC$ 0a[qhZ$gIxBU+eHiLDqba DD2i4?3}΢!JՌK+rԌ"|3wo|yRwt.NL?snÕ6nϟO׾+?s欳iX clų'.dFC :gR檃~/ח|d-=J%PV<5JN>t] #L ڹGop0|zg_:\>}`϶fWW~d'nn6Rr!!IҔ +۲:J BgB": .u$F鍣cCx^i:旦S#뫕Ȅlnyzԥf"vy\%q 7O{fVc[[oū}2e|wzou sO_'mgܺaf 30%(bb{mw2|k7r R~ Ç}; .Qhg[kncF.̨lqɅS8_BVi`B*\jJj`gss mrgvlD K^ o&PYT@$tm"b*e B* (Il˗/=u8Z>0:ܴ z:^я?\ H$.d;xw놫}徭T Aj2Y(YsXTgBBM6mdE{ ,38r"h*D zm7R.IQ]} #B (L im+ zĵx41={,֣|)-ז*Χa&^r~do{[67䃌xlժWI>=}/>{{}~[m$MX{Ǜ6mm;. a6xTR2_SC놯sum<X҄rT+"5jR~/>8$-۷_9w?;ͻ_&;nxd|d澁 Wh-v1;vQQňB֦: byrr9`Rl.xz0=۬x^Ξ:^r7dz=ѓlu:LSkv ׆u(agmm6v/|ԹO={ܦm;fkqaq 7ML.0I$MѢjЯj(eM< bmݠFy&2!2d(/+B7-o?Mǧ.eOR=Khq[_n(SWxYhnf=e8h;{ʼnm[6'Kan|hd#?;|~EPQNz`L=.n`|TQ6k٭B#rٓg;w;c/_޽kHѪ.T};-Ɨ^m6?wҝ#j5B! TRd$m[2ũ(TL}Z )Ӗho۹mcV@Ξ0zZ:8uJ4:2trLw l޶w@ pqrs_h1PPOgwbjV[.̄;&)+* 3#[w5'ʬuu|~+g^l=Vc]ƹ ξMYɅɥZ]XkOBTqm'_:yzúug4Km^:wR8yR/52ɋ&m̾pyapul](r+="{Ms&6)gFQFӖEҠ8H(̆Ah٫0(*Ǡt{;{M8\6i^y'kqמ0*((>=qʝ{ƢɃɘ($"HXmI[ Ҍ3(yAPL:p0  ADq ;FG~-;2xIܜo9[-r6a``w003`l㌣e[eIVe -%9߾9]Uk={"ж F#f pB`@Ex$BQ$D"!wd42˭Y^YX|x}|jh-[Rie\h4_uh!xMog^[=GzˣmOkK]Be-g!;}®u/5O{fg_x?[MӬe͏{+ v{}O¥U({O{O\DV z@LIB'PRPQXΝCb L R !lD2,h#$Y$P\W5ٶgˮBS)kS>o|=oO}X,' 5x Ex[z+S7tKSAD`r IIb{g{*bd'Ο>}xb-o# I)2y􅳇qo`nvP- >ҋ>,EPCr" Y aT!([FdY @`ӅjRk g.LV#kkS3I' }õ7)}w SIZ)bWƆ/]>Wo WcC\bTpv) xc+ϟ?/ݵo_{;VI:mhhii4H@B@mW O|iݚF<0bGgo{o9sG4|ގRh6꫞+"MŔX\3WB fh c}] ӳ+ή|>;sǁɉrGU),,˝j\8sab~qWGrbVL!.'ĘVԏOOi,/k4anyxuph =^PdmB&@0YCj-P]sʡ^OmZvtúk3MY@E !+d J@$$ %Tt̹ N+c;wܴ7CcsS聎ή[nQo5ZFÏYZ,AEg9G*&X`%v΋z5# H1*4CLF1sҕ+Wd"!4j,mѧ;so?On<3F] EY)$HmϞ;u7J㯽ټc)NN\ٷeùs/֮=puu9GHBP{&'Bh^ˆmp>8AIXʜyFg>6v Gv5:UH1$n+ǮBwY&ɳ̚ةݾͤsK51she/, +^Bj@47q#\DK+!*'zQL R[i0 bCgWWRxtsWiXu90ueoݻF@.>wnn޽eCNp[+׎qN}~cڸvaz2\RK˙TnW._6cv9biZLV372X՜s-hb0  AҐ'r r3Kkh铯ݴґgq麫vK+:1hr$dY$.m֠9 &@Bhb٨zcH%0ƌ4$!YkZ_|']D&0wyzqBvux{O7nM[[1KKFdTd˅L`,4d(z`y͜##( K9!B<X|-'`A3*Qf"Bpmuc(6R3j99ϲER0sg"Y-D&J"Q/57tLwԆ-ۃ)pQ4.FyǎX'\u۾?%3K?ڗԚ(We +  }=A1)VćՅAj4hut ԫ!6IL+Iadh͹ڧ|#^xxݰq˖AA*BUAI,xc@*b@r+ L"piy9m㶱uVVj9W?{7D[0amW62`@PADQT+/_>~w ͺwlb$ΝLGD7RR9xuNpiuzmMd׮}'~3^{BԹ.()P.5beD*!s$6C@@0 s Q$aGQZ,o-$E.ȖX:3qiyucSٷ}]]ebڨ;$U4Bq=Vo|͓gO?yգ&׍o>x 6 tED|mtWT,ĵ׎RX .8guHw}[߹koܴnK/H8. \z^l)2F),YN6i3meَlޚ.VKB,-=23MƘ8^c8ՇnfdMDέ۴ycii~nq&Bl;B`F5w۶ \(&ݕ'N7U]8x?7?hpƮBT.)%d>J6o:<2fW80a_\xY8bk#ԠEV}~wUI-XPt.Ȱ,O3(Bp`횭CsgN9zJ}w]#Y"cO/.ܹ`ei9M4㨐D&w.'!6 4m!B "dY% R\vpǝWVW&480P(B\!NR:H;%DD("kBAQ5SE&&$$@6h`U@6srJq?cvhٗU]w2RrKuKS'7oضo΃)ŅۨsQ'Q}yqy'^zݷC͏?uzpm\azK}!rJHʝ-ǯ|_ٗ&ؚ-[.8q€^!H H#2!8#b )N|`!6SW4QTĊHD*yuC67nٱVWlIW?νHQ"@ȊJEZDavzQMfl5RriB_wO!`e}}B.jptm랓ǎa=ݶ:6~䩟ܽg׮ڶckl[l$-` ^Am GB)33Sh6$ƈA!kqdbk8ƢZ5U"HQg\,.W/?}~_>anYږ/)(XծǞ|kz3-Д+.MtJiei5 ls"6 6.MN^7 &PQTDѡ'~cxTƛ_~Q {&Jb .I"P!0wyjЀ46k620dQՕFgwޝ]q|~2Uծb\>[u6+&}/ ĕ5ht~jEZVo]TXmDEcؾukikPvf~KN<|e\:O~'UϟΉH%rcmZZcr'mj/"2Q1ѥ$CTUpD)aʀd6wgg pPayP*0 *"0o#ޚ2ޓ!0 ّ.ʥ8.H?ܦԭ6[.o~Oz;JOsyľ`ty˧zz)IF7lzF*[_\y꡷BFJV!ȶ>|ףw;zK']]q0(7XJ XAA)xeTu9)2E *D u/_Ą7**h Hhg)7aƭ j+sqJ=zտaKl"(#A[cU+i?=#kwط7e֐TS)U/^:Psq\(wxiUHIP)x hF}^,}=7Rcݫ3s?(|j# ceY. ^\lLʦ6$-2KS&I9<:d!6[({FrV*jbyd١hgm|(3O}O_(wv4[YdsѐJؖ瞏iW'mmgX!|;Ca1sFF*ET&ޣE'+'2}d)b9ZQ Xi1V` +1;X9D3gWZH `{DbDPkg{޾+gNFN"\.LNj+&*^:궽R zsWHJ" BvWW=x3C}=]}==^~}Ge뮅陇yyӭ6Z9<4!<ç.vw޹m1"Arb1lsɐxq$ cfPdޅ82 |*%q*!陉3O,Obr{uPoiYM6lژ9o}17ks3Dc!IF=ӵvMk-B\|ա5 ELLJ m~7H.HDA F A"3^[y{ky\d @HQ tEկ}W3lȚ2/9x@\ךl޺|)U x_]\XZ3m,4B1=q3},Xvts?~'o6-vw7{û̕yNضj]:}>tt tuabYkbR5g"gsSӋrwgDO  (e. ^9zTa3s2 JQ @_p .ʅ6%D50KO=\uh}}֎t : 6>L Pύ`Xr(L H<( !d (" -Z>M gk4m^߸uЕi'nnf'ܼmˣr)a;'(Ȁm **Mo{4zˇO=7?Ƣ|yeg&':B@o| :ؘzڌI|ȭ幂JIDFZC^r Q E4twt|<9[WŸ\,4j3K. PV YkyyĂ%b@`a,o)()hC(@ŵZirk1)7IlV C[vN>?cP~5Wk`-Y#> J qHjlD >6P1Qt>9 ypD{C9E![(w>}w~V*pKl % .'`Y$eEA,J.+E}c, 8LdBmP,D|PC0pNEQU˫M=7ܿoh)s,}?ӯ}xYKMM_ΛMRThOH Qe(WK]O>ŋs,;^~ 7w`,_]I8oGOzT:Gf^uI!參 zϡ3 I9xW^>ʑ߸nӭ.{}=7JѬ }>rd\[!}1Y)d jUuΕEt :e DȓRY3O܉S/./,p\" 6Yr)4Bs\mUl^[՟(.@ Sw.q!`% m۶ IEِ,z^}C+s_\p>O_8R}$Aߺckzj0eBF"Z疙#6ij-WWxx8+J/d)D&R璤Z_.*Vpn~j:P,EMw o}_ox鞻x7߰k/M5m;/MMSZrjz`0lSphts#R墉ZEjU(l •{בchޚIz-ض7]?H_>q4}dsDb>HG5Cq+xb˅.ȡZcUd1r"Q41QĖQP`Ė,% epd"VkA~|͚~0757ԗ=/ZHQW N*@d h,CPY5d]dbc yrY!.XO}A%Qd&ߩyp5i' A#d@AXHA>ƘsP0J <;vR,P0!%B]&oR22Y)$rj=}u/îK P*]2?ݸg%1W]sݱ+/? _޾$޴+b2Y5c6fKG΋ =P9r'?닗Ϟ mH:݈Fٲ cral@"kI58T$'!HA [:f4gjUEHEU!Gc,7ʁ ky;p~ąB `o>vÝo+}ss+!OQ"!TDjQP4fܸ{P7764#?{upSRD{&&L1Fi _{5S?ٓ{v,?BW|Hg^?7{l޸[VnvLN==Wo&l!W4I\TPT\|P m1ς(wΠa8I?sV10c5!)vwr̬,/,rjo_?-h45Mqy3k(#( P@A@ DXыG_صw/D8}}=}]{ _JcڑSg=I/Q\t=ld"{Q,Aaȼ#a `C֚ ~;w@W{KbjA[+54i,OL]?dL\*-N[o.bD&UPtieqvb*ϯ9t]sjRFT_Yd6E؀x<*}et=[ܬLgN?RmzjF啫vyG߿;Gbyة `@CS6l)V)2hQ+&Aw H @ZT Hh@P`D&!w Dž{iϲ Li٨Y+I1 ( TaA֛AyTpim%1DQ J *"aP -[~rxWগ_x0vhX.`HS^=+uBP x%$.8*X [t-2`+R˃?yY113ug1oٱ>x-޹~~~+Owk]<xs}qM"P`;[0~cCsd\B e6HF82^^-y\)YF ڲ!D6 %0T,!{ &ISW:hWW] qbvrY[mN'/]?{tG?y#KNE4W{ @T!%Z\Y`'@"ŋV S'_'?v셭[4W=Ԭ*eKlL߆{v]ysՌLDlCpmߟa>/ @ `rkK3Hu1ե<ϖkKr!O3< ;=6Z6)ʹ\ؖ 񱑅Gqfn 7K`,N[uk"JfJ fTDQb혆$&O82(jQ>Hhʆ h@f ADQQ4z j1!A1.W 6M#F1l*bk)X>%&0).,.F~oz)4.:BQEc9( mc@P_^ ` q2aRJJ"4.Mj]=C>|ӭ[nW} o~j- ݷN}mw9j':s?cz_~~y9娧2vƍ[RѦ !x%bBԠ1Ll]QO V@`cQDk" h<CF% ( xܨ-}moܳwI'W3SSW@uMԷ~eaauv6d)2pPBRAB ##(AQb٨}̥i>yL{Keǟyqm[׮s2,!\}@wzD=kq8h`ЀȪ7D8Hd\LQRBKΧ{{:l =巽ƒug?w ;tm&zm "\x* Ir}3b& jFƚwo_OGWW_v|{î;oެsKrdykM Jd#c0bܥDDXf2 (֠TѲ\[P"Q{INB@LaCTCѠQB\(5ƩD*@+{be+!A$FB}֪ajہ*ٸ3$cS[mىWq&_J˧3@իC( W^1V *X<YZ7kk'^HWg I\+=sy_ 6sOՑ;WAd`oş[tK=ekLJB_>/⤣ipsh@Őf*3";"TDb5VFBFfyFPPP92R(AUO( dbE!hiq"*LP8[ZXWͲ4XZ]i./;|]gZutORZXX+ ADI evD0(0.i}!$墉*E H.hGw7ŷ=i3[Z^Y3Ϟ~~Ξ9t /Vwq{Ry~n BPH =VjowѼoo޺}۶gN\oxxyv>{ӗ ?4*BrΞP4E q#SS*qH h8sv92!Q)DeJ//: 1Bĭ,K}h֦''WZ}-=>{_ްnsD q 6! @DG\ޯ,/n߱'.DYB->|ᅲ)l4vuߴoQaPyJ, T6fVBȈTK}wO4F֬+G_|6.Vm2ukްfإͥY쮯.9@\ ϝ9vƎLDÃkj5BRSn߳n}MBwwGlM\iٵg_}?#e(ris !B,ϭag~s˒ ۭl;p/t[xh_?,=vq~33S&J4ܵel8[_}uRTYC"k٥9qd@ T|"FM=Y`d(H`ȅ@]%G~XJ[޼FmYDsq%*q)8EVe"AZ$D"C@ˤ}X T1F#!$h#e e #i* xW}[v-IJ0yu6m<=z姞߯\صe+SaW&1Ə>ɇjOך ƶXB.U5F=49BGIWd `#4zDR 󁕈;*v[[k% +z]Z?p睇0ʥ 3BqoyzƷ-/.MkZWA u@خ"A AMKf}7k2gʝ.1VBwӍ߽[ׯ{nZ?}`-O=UXq|TNRLn A@PYuN!^{ O}g?$b}aiy•P+uA{9l ++޹ vx3H43($Nb@Drg. 92BpQՀh(" I4j@\*VVk"XqR,[ZY^xVѾmoz]O}Ch;j,!huDJ {!FP מbڱQhms =?qgpĩi*%4<:a˶Ø6EWEʈ,ȒB6-,P#T}#k\v[ZtvJ\\ns_=sKjHcԕXXVgz}kڰ lRl,LN^޶}OXlf'+k7mSWzFF oϩ-ܷs'?#=*;mcB"i 乛-uo{x jc6͝=&*,&O !$Ms75'&ڵw|ޙg.zXexGZϔLPk,3!qAJ [EPA&3]A`%be (vjkt}׬YY@1lM*`H#{ ]= gN,Rlf=4.XC *i=ȒlQ(hPm9OY( qdEqAPo8@`#6D '!PY ]oWg|4uˆO)ƷS|\J2(s"A6r>|N* JhTTQCID*\"B#{W3P08I WZ+|'7-Q׎oY)^~ƕs/pέ{ߑgG=g'O\Gڷ~džC+OH"qyn#2 (N rY< dP"^TCvb+h!Qm6r5bg ՗go}[m#t6"H 0  * J@6Nϟ3;K{x[ZJ`LR`4H(2>Rd0(dJ*lo5%B&)u܊,$z<*qUY `2qȄH6d$sA<`l(FH0`y őx8bEE}dcUCF%*|G{@FƖRWP\^\V/v^{ǿ/ e.F׍$TVKLb@2gD]F$eeMU4BpQ-8b< "[ç^\p֫+3٥nx/̽}ucsE^]fRO?Ѓ[|)Lwڱ=ÊkhS\XH 2 $hL;O+ hbs$7jGB$Y{+` >EB'hxccvIZԏUp恡kO}zgpׄzJ@zmă!΃,V*h"K}1Wos]{oy eLrGW׏ҙ;8?s ween~v奉smo?oyj`Q%8%b`AKItmW-(0(r"$Y qitt=XlĀ7d\P&C1\'Jы(+xA@I<3QDy IjpjM8}ɧ oQClfzijjXH|o}쉯ŗgcH(  A"}JBݽ4ߠR+QH5nll%1U}GTZMS" Q}1I4Kӌ(Z\\\;:v=3g/] iV_ɳZ=EH%֒a+,M۪R1Qb(GD$P)MS"cUIRHL"k6m9S N98`L$خ*A/ll,e*!ȂTA(Y\X_ N3/o[Z./atͷjSςwv!Hf$>0IhЉ0x5vԬs!]7x7߾\fg//Bg;8\-x.]4so_5]Ȑ(I4@\l9"BQP@E !8=}%{{ŋB)D;o#,` <2yPde) B@12rl5r>˫/;,R!6r'&Χ+.* ~M0c?.t}_6}Jdn#"y Ŏ}rucM_ˉ( gx<yT[BIr 0fc8IR<]=}wto >Yw&bdF E<!2l,hBT FzV@ZwǛ޻u%rh)T_ 2 F(_gES)u\8ev>v%d 6۰Z-a1lRQG-`dk4f.4@L>,n[]WO+W88sTl3О[k\0._i JBܙTlܺ90{_{|7]kǡ{5qEz52s kfN~嫟G/,ףmw M D*L50)!(Ƒm6Xzdsq$ʥJS ^z"۵ה279}:+}4zsZLIaDvą b֬[3<\\pch@nlb#HC&)1w92Q;6H* *?.j`o^콒!B AFqw}+瘑I#,uѠfH1 "EDڈccFrޥ h4 ]N!5!WȜ&*QPj6TȀG&c 5)sIE`c& Q%*A . %ITsD 4aozyh֤QLBݕVZ|ۛ|}kˆrdd0sGYHAղ%<hР Q5`/.(% zU=wqjBhg/l耿?}7\s/N\d'վ=lH9If BI}M&*Eq0ʩ Z咡W  dTCDM  \z2>6z[7l d ӧ3<{:=-W4O50 FVPEA,i{ $@ mHh_u[בU0n|G_;z}{غG?zvMvr2R\*vǯ?O/v\6?ӷ\{kot`p ! ƱMQD z3m4h &@ @D@UPDTA5}=q)yG^>uR(("0dqy%8 (GZ:0{/T2 hH[DXO:9=q!`l9)4CQw\6;7W'ޏ~p'~tyB*Ĥy+CRAra^zP]kB+uRIZζxjӯ..]}]ceGϜ>gu(YLZQd˥zHŹjw-mlٰq…ٝW_ '[o)k4ꈉxÌ>so]|zyc{ޛ9Pma%Z$Ih3YCc[O[Il8`g#.6}QlUAT(M 2!ؠ5W.>r7< Xg~  [ #O</ŤXlBpCծjш"X+Q7n~~T?J<@@PȝHYQCsURYcx/1Y FA6($ 9䪨2s1>KGqAԐ%p+zj80H#k$q"g (RRA&#@|E srjBdg!A HLy BpQ/џ"hj՗^x ,垕ŋ'O^j|_Piey`9)ĉRXCc=X%:Pj_O"z%QT@ ʿ giȺ/-nxpHdV1f.Q J\-A 68b,x ,'kb*!LMN?=19ȓs[?uaRP}f}~vjU{>޳s4g[_x@KTEKGGϚ"SK<)"-WR CpL 3|w ޲}ai>[W]wsO>{Qĕ-:+j$VH}L׮YY5VX+؂IbK=%Bv-F&U"EbGecO> y0ʕ{o7mnBkeޠ$1Qod.-pshe BޘH-" L-(ٚG>vuu7d"`2D-Y qĹ GS_(S"YnLL!A "@Uq(j4#V B "b# 1wqcW~7r]g/.\_/_[K5֮Ϟj7>쳜 IV53 (0[f@ d Jl!'CA*3Gb WQVX}P (]EiTpή,^j$}c[w]_ꨈ@J(ڈi`MdHQX6heg(ym5|0D@1`5L,Bhc"|ifswrobiey3rss?x7ܲs{V&)EDddv1-g"P& JH_2 ADqF\uk'^;OO;Wk_8zfsoTEy'صR67yo{[{;ޜDIzѬ7ή~pTtH,Ġ  @@J&jlRHֈ\o6VADc/֛rj%.UI <|LNEfG?__?Ч>񑁲<#w:{|V2fǾ3Ul|޸~d,uF|.X0"6N YKY+6(F;BD(8% 1Ciig|S2V;/Oׯ\ywW[,В/|{Wer&)A,BD AUQYU%*&G M?ݽykY+AZL"C[\*f˫ %l.C0CTuqE$[4YQ F\䐧)OE()@A&}!)Ey #ԀlP"mPA1,CP`$ m&!&EIC [(c 萸#I\ODBϚ5_O3_|b׺M;6 K>m_6W ېwdQȢWƨC&R'FUD(0 2:3_9{auu bG4E%[ CDHTh`"'ހB g3magIW2D""1&H4Hl*,5Xoڶgߥ,omHqrٹ'lmi9:CeFK( IR"A".jP#ۏP03'M q6{lܾ?Q(uy+pNU}[RVN,ɒe[gH/i`f^/` 8rdY9gYjs7U9gp[}zz+/KV!c+  3s'-tHk~'??b~t`zz=؆z}h" @H&=LiB0G8HQL 5vWVWL|t*ZRqΟu֖݇+_gj߬5N{1Y"H~tS1D$4lA~ XZUזr7w={&.R;v޽`~]y]?Xξř˯ڻ٪Z|ya3>kY8۷n8Ɛ #2Զ1D-]gU1xi:1k-֕wݷ}; nv=k|0 㴺Fs }os$\wӎ~Spoc&ן=)[]_~:g_t{A[Q sɾ]QsKdhNDILX,h<"?߫cot@ĉŅKȸN ]:P^ٻ R+訴 \ETŞ gWVNQdK`znn)㇎ֺ=K{.+w^:Z:nm?7/O˽C߶}k^{M_IZRۚ3CparC\t JQpNi~T лb]rf-k_sѹv{3nㆅvصgחVw- ϩY \_{ۻ=|OUkE9T"*jj:;P|x{h֍:Y`l'&Xy-ZhBpKaԌ5F\LUԈ٣W`c"ÔRtPГ,·D:r}lJ<eDfBH^J)1,jEB@faL]kXsj 1aJbDi crס+c \5<1;G,MgnoMox-kFk7~{74n_<'>'brȬ2)2BBHHSUdFBLh`@:7i$B1#FAo03nvU)uED9d l7$l9FR%rl3EFF2I<;Ib. yF@;rlַ鎩 }trM)w]/=r=S䦳?_\ .\$g̩kp>M6=uptl_sc[7ذa| s8_)uJ)!6RfʹP1#Z)!jW,{SD Rsx#=yMwy_p}\/^wkxu˺]T_`~js3Oz׮ڱ:۶l>˿g։q7׹Zްy{eӟ{Dٴ쫯nyJ @)D!uEЁqFǎs󽊉Rʎ(7Idb9zvn.uS !9)Bȱ3'Cmlmq_v-[O>pbtr<5X9߿txS{w}- [.9kGhRxƦ6fw=?ұn]{ou 5 RWQYhCgTz"0;4 V"(9"`*;bAUSQab *r1Dr,].%vK&Oɳ کҁ` \vzo nz'Y۴x斳|YQU3gn~ny-ozJ?} }w-\ـ'~%$-`=|8c 0C99<>|U{[6l_{{r7Kꪚ_X+.'O=y|uutl^_v-ꉥ:B80scw7_u?Cg~ _vuov<JHxa$TBcCl[nޱյb6o/ӛԈ@ES}>Oiwo[z΅qj!w*̘̀I_xVP E"Y[i#`f@t(R"x<4?5 j b57~ -5Ò[f Ǻ??=VoݺՑ`.}8sE_675G`&oڱ␸ QKq #.TTURѢ#&D24j޷N7`d_cH;/tӏnwx%pڰ]>G_z۶NzgWF¦k.;~[mX;q /js W_~htru< 1hI#̩ 1UCBof2 w;tH`{Uv+f$D׬lraZ@2(WUXWoGs/꺫7J6m w0}|Ǐџ8gg_=xɎ[?g=;oV`Mo|W^VM[R)bVHs.\ދ!9`7ǹ+""3RU$ D Wї1wb,R!DjD j-( 9Wlԩ&e.ҙfF1D "+:?h!l Ū ԒSs5J@%- 3)!Y@|o cZ^iNoe>ke]]wUWUkjvVWz5#:-eÍ}?[z I9"sA0䈅=$ T,cRQF ! bك!c7'@T2̀C@QI]yX8p?qA6&'-W4ҢJɤױiK)sQsrLʫGN +VW{6'I'.S [pw[/=msoۻwmQdR%!`B@jt@F8DB5n#/?s< |ARɐSe5+%Q{άܦ/sW\sziJ)fio{Kw\v}7-\,W^sg4!FMF<ܕ{ġc]ׂC"R(0)AdI&J#uʷw[xf7lO]_^Cm)wb4 .72" _җ:\=t*`)*jj{\)46t.x"R|FHF-\m@2Z2Op|PEp%م"RʱUS3g{v/,9dEBpL7gP%\:!P jY-;L20<зxF(k5]}}}g۬->23i;/ېႋ/~շQi(UqBdcrF&˅Mcp9q 뜺\`%eMF#{^)b KS:*nsq}TЮԦ.FތJr,yůKϔ/>xf `N9# 8iBkw8u|nnNU*1#zP4qNR|]ggg/lVV@`}-xǖM ?tm?>3?ŖN+c33T4G2i[g0S5^wty=/~7~SRVHA6pbi_F;޲m?̗X'^x*0L$ IƹuϑsJmj:e&E3m{?L1s"{XPH264 9:t}uwO;u~0U]HS3ܸq@P@ )Kõ94UuEut64ϞQCN%H їTxб{=_׺t`4.ZxyΟز_Kiݦ3O wѼo {՟>?.wσɕ.q}}\'#w=l36/p-]xxumuWHJc SADT Psۅ*aBCӵh!pK8r睵æ]!zi4hBK)h"jBAK{wmg3s^~S^Ai#Go[׼}|cs}3]nj3NU|\V{``WǣHjQ$V<2EmЯ6kS$MnVh W O(D\ R);R AK]W\ `X16*TvIJjSb 's#zrb8h-+ Ă+]I12SAp8 hO?x̠:/Õ^o᧟oͯzk*ПNL+++UoX?[9y$Aݯwz-6 Dd xUPr,HgA VR Zjf @N-Z`B Hf СZ13F4 UujDs*nɾs]}M^K> ~ן=?NKn!3y]Wo?ܶw]CοN=I}b$P`L@@`^c4 d%T6)#Z6ACO>lZ|WHO<?oVUY& :Xq^;?빇|׾[ÇNOcQQ\=GtΫD=U*x :)FxԎ|tj7̨&̎#1qtc (J06B@ [6{s]_~{ս+ya#]|ͭyǏA{_?{ujj^su7T0*]7F-WԬ;um'{}LWIes\ K)+YCCö\[d3P2P>DI񚁈^?Գ< YI #eM^m1U=)%gF&(@ @1T. !z \WU1/s!dڇn<*:ƍgm=c֊w\&:ѣs\@--fw*DLR)cDM&${&Dp 'hۼeێ /رc`C2.w_?ſrny';z%ްܳ_}gxGbPNg6Uq .ؾSaqn+KCm[$VURK]WiA\w #:t5Rvѕ'>K.ܾnѰo{>iz_*^wu\3Y9~ F01LĠ8@ p2 | l\Ȑ 'fz305EdB2S(dƤdR4 mڼyymsOs/kkKXϹѣ'Ne T kן_O./}޿e~㮻Mރh6yP'Go|u뺍k_w׃[03tk ! 'cOO~oz&2KXqpD ЫqZ 2ɈhS|dR@z~]w~G~Y9yW^zq|O>z%Y.܉iԥDZr # .g&פBɰ*FM .$+H@ 9rPFX>X6bT ARwKQN3tc}a""u:GUڸÎșYI믻ru_y0_Wŕc _iazfӭw WO%>ע]%1F2S@wLdm7*N9 uDͅp7%PRj[233S SddB fĞEHPct%(3U1yr0xy&EʡbJ*L JNa*؈ɣ1;5G\D<"t;Jz}.,!!h-( Ʃ4c_+^>xs_¥[z}-[h9a/< jn֒M;6RFɩY+{(jI 0 r](& VL;F"L+X1K*Tyn%ؙ!IQ"B 4/ u7]^SOQܩc>ܮν1]~'#n}䱧_ /y.?~fesgmE;/=#OtqR0t́&;`$#R"<N  @F00P3]X.%җLMjm/ۯ:yrm鄔B8́Db>GS#mٳ6Ioz[7nr1˅No&c{AI%W/}+`jzpy]}ݍ::c<i_waZ~P[Cz^R1Eb.I Ud.҄/\ܶ!&iTӃqc@T$S6-D.b5kJU5 (#"UHU'Ӓ\+TmT#{K &lXڰqDӷ3EBjKhԛfM):bMS92YiW TdKEшcUSvjf\@bkJ1sWL;ˆyQT$])}|ȑe+Ð{aN}ȬbL0:UG#9lDUњPME5ګɴ+-,Ȁ*E|4cK$Td;&5+*#~~zaDt7~ҩ_Sу^Ĩyч6E?xRE\fg}"ʡ+ESN$6%GC4M ("PfDp h:FJƵ!KjN4u #t)Xa#r L" 9ddii٤ n~ffV<|j+/? = R;˴vwaәc#Gڥ۶8vj."0QS&G ܋Ozgpŗ\tɱ#GG! ΐ]ħ 0Uo`DZOIDAT3*Xa~~؉o淗OZphЯULQZ.JTJUKXWUt97ZYѥ\Ĉv!RV^O?vjfq!68rE10b?X^>YS@6n*ja G.I<690)".{ȇJ<3sYj:|WqQ$)2s1{Ǿ݀TU<|CG%g?tiV8ڎ 2;=wd{䒫cOڼa%\xEMSW#v7ݘ`&@Ͻ۵zSU׵& V@YsVsN"8W_9@jЗZ$rFbـ\fvHSf$i8:f=Ǻk9k:8KbӤQ?5{ߋO׭{#RUyeq>gqfVb#heb@`9W.i) 9!R\U ")Ba ̀KPPP׮U&,&u4.T}.K/Dz|iERw 뎞Xiƣxfa~m{G_s\|I' O}wO|o㜛~ߟ齲 ezvm:3`D`Pp2"*#Oɉ#=NL@8Ф{ɣUq24u=SCLx^SFY4H48 SI6C"bzuI`z0XY["7i$䢢֫{Ӹ%*HI."1F+RTc@-` $LQz~?uS٢8BNR@PUݸy-84igov 18'zS+ٴhM:*:-(V@Թ{ :idJœ/;vƹ 9)F @J[*d. h*I ^0 u\ϩafsTRz)mGy;64,FDLIMd$n!yuH %0;gIY"P+|Z2Ju(0;C|( Qɩ͂ʸ05f“֛[_%;>sMI]ozmUozGn[ν]طxyDÆ9'?d؋{RS7)zlz5sHb@dE~DOS (+fV|%)#?CJ`*1`XZҶܭou3 g./rpgoT)U"#B5h9h3dH@ HăL5 CFFȟ^[~ /c:_t5ǎC-'cFM'\)4]㑑0\^T63`QX@1h2hVsJ)wC,EL$*[]ظRY ))p'ȡ\M@rEHKk'SshЯk+Nbԯ#E% BdPu533}{O WҁGN<"l[];tֳ}9ccǝ;kQq."ȁ$e@Cj9fŇ鲫zͷw?gcߺ~$ۻ˻`j?3f]W?VWޗ&b[?68<==[v#<$^WT'dh]I lZT U dCNBUT蚦C^_12R.RW4KήO[o]yɕ# c=^vEW\Ms{}_qkdW?vp߆dM7^sMS4gd&bc8nBI;$+R;]!HApZzT dCvm+%Wޛ*9U~owqގs}᧞7can;t젴dzPH_j'C*Ԏ EmhEr|8‹r~zǮJ~{/#O?A[;ݱyR"bDDijg$L)X`|!% zTSQ<\RGL5W!4kH8qԌk)IA+ At]ہF0VcW֕!A5Uh(l*)Ma]dU`DRR]V"$O]~+)qS(:,rB!eISγsٗ,d6u2<2tI9Fr@t 8`193s9رcUJ.&VT$  0Y1)e-%;&m#Hn;oY\hf*ڲ4IRb[N '10 uUJ YR ԕȊ6M/^xՃs'xd9X%mzp4S?b8巿uX[^s6oٷ{oBmhf`8paӦ}?sϖu!g0"HYZTvAUBVlg@9m;3b=!f4"gS-#wןsJIinn6O<Ԟ_ .Oh~w~o?kz/~G_w5vZZJmDh&M%S()G1P"]fJJ*"<$@@캹܈:$<7EGKlF@ЉP'㙲J9 o+?ϿTRh 1KFh~vn,~lr7'u`+kW^~ǧB0AM+U{GDmXj=J D&73S Y=b~vBj1L-[՗?}68Rri}KUPiFL|QoP&e) aBGlՑw{{ujk9qm[Xh;y__9q5i2: 9 fwbq:UՃ҉bWl*)UõЯ#p8iHr&40LBb\pZPck4JXrbD+JM;b-VR4N׮.MCq*깪+m)]"*i)& Tܣ0.G`jis b55@4GbܨK<+Ԣ*Ԓ4NI:QOAE;-KG{ȥsUpHɠzYJ!c~mAG MXGNqW[G{UDĒWޣK9s*$+mË0Vi |U *I*Didui_ە?c[T+cxSlpy0^sѶ.X/j<[1l {镕W^`-=i6U& CNA}pb"dH" u4f [M"DbZcfp0@&C"hjh{?}f_flVZW\ܿyf??_xInܓϿs\}97uʩ%5p߻( &$f TCk3!0N T~q~e˯OZ[nonlP'ߗ& @>rK.yWڜ+Ǐ; VАt2(h@Z\8~^Ԯnx;cƥ5y޲cG8rE~C'~7~iDՄRD5-Ϯ_{^|3yg}MkXP pYlЛMͤMW~)q~q˦G_wKanq%ans)E0%`)急"#*@Ԝ;Bg\oK3SI#O~a9׼ka9;=<>u=G<^ێ=@b)\Y]9tpy뮻7Q~S XSU21#{tpuTMvb2슫-NU{'\q3nZiyitQɲX1TK*-PQ4JHLj_-:c<Qp**Rߟf]A%`"R^VI3bVq#0w- ;vF82tB,9T42wET:]%RkL5]5(蓴l@K6R2 ͣɥOtM"L~22v9Tz;lERooPY2aP-VwcEHQ `l<.Яx(Q2UzJE{@)hm-qېU)Iֺj'@N]*Y>,O*#W3HQDJ0P@*.:0,xc$ t!h%X{Y21@rhX:7j=RɥvUJš9GƬ93XxTh: oE*pQiHd wkc bλu#s$ev 9qWV5pxA+֎N+Ǐl<{ǝw~W]E`UEMD籊>聗̯77nx?{>(z$AH"9w!L"DLj*@DX4D``F]*`6*2LHXfmA{h{O3oG, κʙ˷x][;rp߻/>+.yx6:0!Ii&P "b hI|5uz;wbێ*mXܽ෾ӽ7-n;?Js@&-1U=E"vyyoh]z=gfx֭7_]Xwȁ@j^x [n%W3~K߸nNO|SWhkJk+OU5#Ǐ>t:6m۾f+p@nmZU窎W^c/?|oy\^g?c5%,ltusч"9w]UUcnF]]ǵ6*`D;i:r=oudD6Z[Ճ~=uú7hpk:oCB2Mž1F>s! Y UUڌc4oEzA "ҥ@ڰzё"^5Z[~EKQPT&0,0!fLv%ht;HEDSI0!tZ 7=ůqv͟\=uם_\|w\p+|;K3|{?D231pcd|rn3QrM@ HER?7ox+_Uz0؅]=ғ~~ ?|k;vly޲6:rbBzb͎;Dͬߴj.ǎ=thXgmxfC) ǖ;vcM=õB[ذe3FkS K'ͮkR_??xysssqq8,-gݰA@,ӽxmH XWإכ󑻦RQjBOg܍ۄ055F#rN sZJ)T$#!v}R*뗶UDR.D>i\v9%`1(8"`#+P}մM0f.4yC$QFȐP${dBN1:@'Pz=T C%{xi{3F3 ɕ\@bBGUuEh0^^я&{Vd`49U.IɻȔѩF#j$Qێc$$gBJlŀI"l,.Fv\RЫJӀk)5~0mkcr}ey{Z1@e@n=$zu]/ǀbd3W-fdlU/-k*^-]N*\$YU0b+e枧\xWALyL;vZuMg2)go焉c7VѦw!0#;jdĔ K25DŽ>( Ds @zRDИ@JFr!qJ w"ȮE-zC{/t2~8@.߀@(0*2CUW?S(KVw+B  (ZdKlYږ-V$[I%9g4 O{ꌾ?ӷtU󹰽.:FCg=vş=n_w˸y< > O}3wv۳nz57>u1=96 _G}|l??~% 3Xqak8V2CACNnń)݀ٽI8 94?Nv=z^+~vO|+^}>yC=믻ը!@&332~F S})M`W `8 @'.;vʱ1y-Ͻ=q{yE/ꫮ}ʙ&\gYm^;sG-ko|˯䳾{k_~ZajLiնm]roo?/4>xѳ+ϟzosR}w|Kɳ' @%λTV;YM#G_rlc됃q˾rOMH]9,;>[ϼk=ѽ=q֦'{䷼o:R{gGN=~EǏ_seV*()(˰BDŌ55^Wu P\)/*wj3j襺I,yuz &^mԌ,1t ; J Y ZmfmU-sϽrFZUB+ظZm" &+`&U,fi:#Kn*x7Ԛfbc 1V03W-lr"\Y3NF]$4%r l 1ƱYױS2 16/nxʵz7|K6Ǿg`'G 6Y8[;{_Ut}^o>p?/yəSvwIЁ`/+-y[/s_{W؜Wΰ1t:w wug?w>p/y+^o[ll!MtM7ܯK]Oy~>uzkG}ƳoK ܜ=$)rbbU8ľ b  ]ݜ+ Kup/}6BbTat5hZ@ ."%瘂8Z :泦8Κ:HmPc8@$Cjb8We!dԻvRND^s5-CjvA-cc>H2 g]ؗ1`8QjȌZ K!Xk3A4k! B@=kͻZc|Cw=]чNSY" #(w~7?{˿7#|GlG?k\s$'s[{>~?~wvꮏ8r!VUJ@ 9"DEe@!#BHAnj,dV] YˑMn}Gs{{?u^u.>m/{+lN?.]~3:񔛞e Tj0I LqbgHlo5 !ϗhЉKo:~94k8ݲu!7% Gywom]u~ _W}kWUǖ{8CFxcxW|3/?YO?bbӛO#N"u=Ago)U: ] X/,<?Z]ve_zQ;V<ͭW~QJEkٕ\~~oi?Q{冧8|rDTUv5c.zFCGBX[pD&ܶk%8H4X4W Mh)z%&k kZZ L0ll1qfdBVۘS3'~T򼋈u9fP]umJD5'DbmB֔ĠGaou[&"y)j #Uƒ #Z1koh|c΀}q69}{x%W>%m̡r^&0u]wvowԱ˯{—o=S?+?xW޷_JVj@o7p~Y~CM/j?ϽS|Cܬh^Jn| dPTCfVIs!%}{e%bYr{X|1𞷼upڦY,T}||-! n qͧ>8~S̅OS<0Ds}#yh=Òǿw˸m_/ rw=◿ڛ^ޛ/<g. :tFE#3q Ny{쒫GTzS &!/]!#plyKYԩ3#GO\tťDjy(Ԯqe=8x|F]y㗤MJRjUKιlcYV"X831Wqfѭ 9jUHʴ trf!*MR fg`Yj&Lh\Jê_a b@&~L!43Kd^*(|m^ u:Z4 dt،M܏kj!9U!Vmb 1),!8;8v)D^njH- RWSh R"@,յzsg7E# Lۦbj0a1.[fŠa4Z T-fЬq|963>!Ls d4rtnslB˥R7.2/ǞB~kb6=DD 0P#}.1he@L<.ıdSkS;i 6KDZ-\-i5vKԽ0@ DA:("80AHf@5/06b{Rî dL$,Vl6Z+ 1 "A٠! j$Y3lƔ"kȽukk[w=p^noWp6m@#:A@Vqe~G^{ÁՉk5=9zׇn]qb/yg?{!֑ozʥǟ}N\r"t?={۾=m_Ͼy|^#'y2O8t0V@D`T4'$gp #Aso~M\vG>j)! :WC|>t sZ WgOϼ7_z ]wQ$\,r\c)sOy'Seoq{)1+_wouБxS.j b'c96l  5(:k(@ LZoDLP+1;3 ^\ k1(J*Єj*ģ뼛YPdҍu6&!ıx&PqESV ,hL$XV jۤ=sYsS5$aTYH1_97zN CGE/\XB0EI] UD"5&1Ď^ǂ|Mfo$llǝTPjb R(P4U ̌S/j A0nXaJ s)SGZ8 9c%dNLԗLQ"G4CBV)8bj Y -gD$ɋE K)q'Zb! O]KVCf\X4e @6<R՜L!FZ 2z f8=4فTu=d!bE bLhX0I bnή$̻nX=9.5k/.!d__zⵣDp&`7cBPZs߃}oo^rȴo~#}O}gcկpp[N\v{>޿}_|ث훛ٓ'#wq:28tCwDu wkZtt:@N8% @rCd9𬛟}_X]PVb6$~w_K?~o goݸn|0oG`?*^ Yrߣp`FCCb">aa5Sr]Y$L2C (Qj`".VV$LXsHP 4Bc_Ɲ~b2-lIԊtf%Uh@y(EصXQ <釞E Cl̪jV# ZY12Z Dn j^5mq,n$1mPCHDsQs?.YN.51בR5D %ln>LDLB1A@p3C7G@) y8_[kZ"ihPݜPjuе|s݄fyg~txa|n#?/G??>Gȇ?n3o|swwH]ʋ|?^!4c޵lꨨc);zvZb*DFr,,`p-*USHH<=>t&C^y >~ +e" tN Hl"; 9<@ڏ ?*#",4vvjpcպ6_NO]]?~];qK_rxi$wBC4D%sҊdn  >ԡ2CCG`S)0Fv'O֬CI>|>nk;r؁#g??:}|QO\|kkjeU!q2 e(%k\N-UbN"!VEqjAFU&l ^(1qPCH\T|6km9xQ&dc͑ @J tz fsMDfk[uq&,i6ŮJl@#WC2 3nC-e*n^jY_=ϤWYqIypGwmjSV&tpY]Q`9mjJ(D٪V#kHi]0I0oZcL 8;HشjƄE597N8[{qfADλ逄 ]rOX*X%ffy5:&FbX%ZFSl$q ٪k-D 0zNU@G#/$H;P]Xi3Jlݔ \b ۫f>s*[LE9b挐K5,M!\9jZ/Vh8a5*Ę8Y99gN)})l5N]|PwjsBX-G% c !h Յş7ǿ맞eߟ=s2ַ9_B\3תwl^rч?OWnxW09}4}e+8`{~>~;|e~V֯gc꿞˟̧võxK6O[wzCwͿ|4Z3bCQC 0Fb@DZQ8SZs5 $P+3#ˣgN=DGf,ǽ WOD\d)6 VOTܝ %+;@ZAJq5ٚ q6[se.Ld0!r5R+ٔ6gm_" V:U1PUbsZX.zoB7c<ݍQ݉WrbVh5dfRj*y"9.+H 0iS qZ1|VkƱQ@@G׈mXU ,+ &&\ͽ/|va ]GZB`vr ݝحus|ZӊjU4&F`"vh1A[UT3!@ ~K}ܴPG˵ ;Rb`BwՉk$CZ2R;sd\\cŮ\+z^ f"$ b$GR*WUl%X!$:q'`FjYJVV=+G@qZA$8X.MZuX-}5Ϲ๏;_u< 1'Ţϴ8KkO%/bK`@̑fѵWc}}z3/_Ps8zEk{? {76^/}g_w;K}ϗ€@Hlnl{oo|>|=s~jV^ 劉܋V}AC@CaPt`MJn*DmwՒYRdְu;_oxѩӧy ᓇi6 `ErI| `(}˂@G?Ľ"{HWoƧ]~e7Uɻ;ۤ  NS4}jK"NDtr 8ԧ#f f'@HDn@OՌ\`ckԹǓș-$U5`U!"i+.J 90(HE2$cI}0k\"b2C6>l/ * Z& 8C`I9 i\FԵy ,[ƉW6""1Sה l,UQ+"FҤ&˪*))ƦY-vdR͐uvV;UEHm0d$lDlV7v$cբnĆQ9%jЋ!\9bet}(Z$@vtt1 f@mDiR.I alۤ"јBwBg2IQT,+ )ZQj6HdbA+j怔10M1U4$.J@fF8֒KEDPjVG7hzFFXRlLFT#Knt1qD65s6FJnXH7gufЭ!r("cdMmWGQLbEURJ@IG5}_!8xM~S/({U_/:pOm?vEZ9s̿=q-[GXVDs ~gϟ?Y&i%b.!ŰD=~ yϻ/okNlrvGo{<[7~7+q}^lJnT|36:6#՜ "XL9Z:jڢYH 5aLhPpҾ|tMTUƝq{8r?/~Wnb-7]ǧ1#LW&98%М M X%gJm k|=7?|ܸﱉWc}:)ߓnZ} )QΛ^ >^'Pd Xjbon<_ox _׶Yژ/詝r"(H@UBD hdU0 \sZetp(X 9;q?jhS͒ZqPHFcܲ*i P;kY\cEӫD69աIf^{bqp^/wz{M}`+?^{+0>tz6%Pi F;z{w=kaGگmGֶ]{^2Qc` I:ffO[=1rUk.6X2#qqk9beRTU%"7B堠԰|G{,Z ~c_H_>}uCp3}LDOq ۗM'r4KLi6Μ9o3Ӟej̜\x"fmN s ݔ&'w>i`w03nHE&T 2k J(m0ow ~Gmo =q]a )XU(bMݫ(a>+wbKnPK@#,\ =9ؤ@j\ fmW T^똛rȞhUpjڅݞlެ/Jfw/#u,LP IƱ'i*給 kv.W,6XK$b8 qpȐqM 0SI:0or.aٹה҆NDi\.Dj%u踗ꌳYˬbtc-|f9q D`qT2y!d!DRm4 m&!yn7aXfH\saB @QZ0f5)J̘Xtp 0V nYc3IPB\ q8pD<i[9kn@u!FBn|&,E$QHBpUr28kڬŊR  Y&kK"=6w mX-ZtDcFE+Dn! uIˬ!vQgi-CuZ-JUD]jlc B/SGk1bͪZ[yM7nZS'?o~pxϧOr;xV] ɪ1y`$W#7Gz+mb\k{}޻o?~eܡ#䥈d͗e״\uO=s 84ʚfLDJ g~{ߝ]#"{u Ts%@LBX+pp_cd([aAUPีyЂ$! YXӻ^/{d|`ӝȧ"38 &qT* 9)+eW\}ӟA{LM`p'6A٧~; Ԧ HnNS(Iaz>q$$E#B rAKn&;WssLݬh~/[~~74[W?oYI7#=^}UAdgzqtB$g46~rd 2W9+!"1USt !`Qsw%T:}'!04^U8D3zj__;.ww7~Wӧ 1hQq+0}8 "O6SkVcs˿3N}/yl:iɔ*^y?я_-NV ;O[DG@wT~goqckި*d+0 EBxdt =R#! qbvk=I@a=tCxKC;+fL8]af`A(#}Gժ*MX,aw|"# O%i#@ y59H щTXxb!P9vK>s a)^ffFOa@,O}~<=X;zg=ZrDvDSJ.)Fs[l,5bn6j^ ܤK+e#DNE$51C.%9"0ZRf(K۴URH0}*]kM1vVN )vb0S$VJC B6KU) 8dRURj1ʹĔ!dUEH(!Tkm 1848.wCbVWwڤȎ!Z%yH!֡W+#1e/)\h90ʼcܪ;U-5-A<ņ c5nݲ c7ls6kV]nS13B>CSӸ K)@UHm엽9B `Ê$"cp&bvIaXe`jlZu³tIVy#y,$=Dt7sզWpPy:T v)8xjlmˁX{+jƦk@-RRu]Tp9f$ QL3qeI" BQDulZ $ZˢMY; ꚺ ;|k}wW|}ţo<Μ>[}#'.oKo| ?} }?-}fhĆ@͍C>2{lj}ۅ6W!Urs̷ؕn-`"Nbb \mH`6'u7q@`E|@HMjh.5:Ը;CjG2W@Bz}M Xϕ ܃γ߽}k_lMGdk5 CvXM4 I5@`Հ+8t_ٗ҄Gz޴끉g@ ldmnC52fHh`$(hZN w'=ȱK>z ph#]Tw~ar#5c#8zrΆ`V44Ѡ3F(CZ|c6֪ bu"UVf7V}Xj@BAd$X ձsֱXWWLkɬܶmqE4<:BvaԜ$tI`%pTC;{i ":8 2(rc-gn9D X ΢ܥ}a(̬#z`a;撋mc/:,;4]%D.z[.m?s]O{ͳ O^XK ?~ى_Mg!&t0pgUM f;^cO\Qvu1X ̼TG&d2W'&i| HO߻uq_|=/Yf8EuC|ZK8LO ;{d>;g*!:#*_";9KHѲiQH䦈U"dX% IMgl>%tJ$t4v'I@@0 Ɂ} *R 8rИ"~$dd+8N0vm2PN'v=A݈a:"sB̝&]LL!8t_^U][[G.n ثuCE6Z9x8R[o_={rjNڧ\i(͛hKYУCjSBlCTDEL1,jJHNSJmmm\)[Mݚ^6ŊiSa3GS  kAWtaav*aX}$Ӑ{شPkPLcF"笨^JE !#!kGZi&Xg4A"( J +Yb,"cR2J캎U h6%# 2!;kUƝeFhcZ RWc{a/ Q뀈vU5ٜu(ARuԪAxSHBlHHjPPGDu+V% 3vbR+UB mC1!Įs0"!y M՗CY:h N!XSy5xU4b%"zǢB R X1ppV]A`=8Z,x5vV n(D0y%ژɹ0.]Ze$b&׾,憨f 7%Ъu5h`Q-*00U׿__/~Wn'?yǪ|ӫ^sg\|~ng{7͗C? K+Wۘ[GFrAyJn:-SδZkCkp_5׬n^7wz뷽⫮{nGO9T8fD?+<"@w7pd4 7?xOg@6٪! $T`PМZ`D$bh"saqDBMML޶ܓ4?pk.l H 61h|:'ݧ !;7ibG0˭n0hLd8y/Цtj6Rq? l4RёLC5! !AEUґC[?r/Ou3/:~㏟wN\8ׯ曇g}˷Kfi~—>&/ݸqtK%XJssBQda,z_FZS GX Pu`-F7 Z-U+&UUcnD|J` Y":X.q.CNO 1ޯHEɴAyjp9BPI$MH Du(^sAM Z= 8!AɅ\y[Ka6rDE6 Tdd62l9@,tf[ 9YCR 9mEע$"Jk&k{M3@C+ 27@X0ֱV$@&$ZZA(}ޟoڎЉ*Qhj.lfl`fdR0AjYK0$_셿x~d_#8;!;3|'KdN d@LZq N'"hO@!ԍB j@ ؟T:(: R/Dj'N>ڶm1@0_#p8\ Ok.a?K58t䊧]{%xӏ>w͛~q`򫯾KkvUB ">i:-уvW+0&,HƘךR[UuڦU@]ViB VR΃Q7DaJC'p:T"F ئ6E\IR5MI⪙ 1Ң9%rUl{ͅ83 b qv4^פN d =ΰ"P䚕H~909вDw'.D5qY!<F˥9ze]%gcHXq m˞Ai;WKZXe?:LAM"h .Iu],ZVR#PAJ_%jf=@`DPƱoZa-mgIvUl:p˵ceu@16cRdu&hȊiﵔRKvT}}躮Iĵ3 VZBPHX\Ȱ|(̅h#4S\+R <0~wTmn D\6 cABN W&t/D"W D cH;yV,W ˾#~ǟ/{䋷{s>};$8r`lz`8">G{Z0׉*0]H "NTV@psNplc[ `07=W=c7雿U]|z/۹SCG5S[ ]TḾTxo< z ^{]?510ZMXL,pZPZ* s"$p` 1a\,SmDJm/s~_=q3<@,&HH`8'9G4raONٸ}G>Mܦ-L?aj!+4çc+ց}W۟)5WCP B7l9k77ׯݝb$& sc^Wxu?}ٓg쐵[W\kjscsw XPL=4͸(L=Û7Rts-q 0V ƢEUЙr)$gRAc%`Ն11gB<:]M~Ebbپ4Mvk{V4Ty3H.P5p '\)#Xk3X@笣KUQP\=HAV˕gfwvAFV zsEL "T/ysq/=|˕;~3ޱwσ;8<|뮾Kݘ)u1sjo<0,M;/V6j(l@ 4<˥yQOA!b)ŃaEJ!\"\K@"LcE)ƄPwv JJm[ifcRv;=W+D>Xqk[t,W6 'ӪqČjE\IWf;}j^2p@@uji% i_,B}]j N4_[CDjz13[JT]$&ZPuj^с)E&Su>:UUx4k1@ :铵lUsͻBE-;|/vK^zEںλ<|mC[fM>P/||غxх!}#@ J@lP': nCZ@̧Ҥ\0JΊ Ξ\:ƍ{>oo'.jٰݝZ Eۮ͵2Z׽|럽C8ve~dµܴVE%&,6d(#:Q jgZ.H)[_sRzffM@V0vE$i")@(hLfv02d%/eH2bJPsH8*y:W2լ$ĸZR[] =+U{\2ֽݝ Ƶt6=D,gcL) A5L<~@AxY3 RQ8eFBcڏ IˌԄhZr1dB$FPq jZD5Q( PyVL{;DsAHWeR$T~ϑRD*RPq82C6j5pp&ZDtt+â_U$IɽKR;?Os` 4vRQ-rX\"FK])YǢðL!c@.@ttRUj)B®uH2rӱ4b5O;nvM{ˌXlom {ޗ'?W}ߓU[iwsvm~J_tQ>~^?ؽ$?~j ]~kvb\ 8#:jMMDalo٣s'~swK~>|okVa%sAZ}OWib ثzx pAb/z`LJU)B(kaTBP B C3 vs n8fF-^+n_O> 'M9I`D nj#e*3)#(Lz:EҐ?mZh9v+/˾ˎ?gq8vkԩk{)Wfgo pJO籘=_o6>~UWRErZRsR 3us"0B0W* Ff󐅦tZӵł #ceeIbjs)]Eg<̙8[\tiWSK-@#kΪU!:O[ػKBu52aeȵ1-ZIDAT@!knYj̢ਜRDUB<.D] P"+_|֍OSwW$c]q{~cDDX@B ljb4j54!s>xӟrhg/Vg?GO ʧ<ã-?wO|ScNո* "c$a Lku5Cn%C)VGplg8Rk5yMbCFFUPlRZh^ #1"C%Bfc.ּZm,99$3t24h@q&y@j9Ww0r35$:KHny9y#+L`՛ȭZedRLkn ًGsK.TKUJX7jH$$>ŁyXKM)u]7ݜ0:hf\UK$E0rǐZBA aUGBgb%$JAՒer7{esaUUtJKEQK'=᏿n;\wrgwuw;+ĩ5wb85o>c҃O\s7<_[7 ف1o>\ַ_:nmv;qw_mp*;$C@ hJ99bg]6>9";Đfa}6Ѽ@eي!;1`^+SDk{Y .ԎN'ucoSVBf_O !pupDFMpGB'wёEH>C&jp0w}+]~ZrT\_e_|}>{YۻI'@d">!䋎!/=wܸu]}R0UǢX-%Q 15R:BI2j`nnZ1&N=زfF¦1J.*mRX!V"9Sjui2hBLa/h@6ܢ RZV̚A\,HQHEǁݐqA˼Y3-YԽ|pѭ3v.)'9}'.K5_xYSr:2j5 cǎ;0U^qhc}n6mlǯđ3;^Ο_Og~7}~gp×W]vvwds5"yJ[I:Us6sQK 'c3չ 8-XrIAԫ(P qY6T7 ydBVjEZæiٱk#C5r4Gbk(:xӵ^ B1jR#\1bDF6@$e#$ffWjU a"E /LPJ=W:{U%{1 NB ,%j*v\\HڲW␈u(~܇26roϷJ-V=E*0fBLaZl:ͻscO`>5sś^//޵'{𑣟+^5557c,I܁ m9#:(OR͋>O `hA9;UF@)t# Ď@<W"* @CD,'™!7jQ-GQmYsΫe={"}x> 6]@AD(EPRHINNmW cǸ{^|JY} xBİIO(?p?@ ~UC;2:$HF) 8T20#20z)xT\cێA6\G9Qm^{TZ_ c"/ @8Lx֖M?yRMYlo޲əNgIk5STD 쐈HU5g-@IRhg= 8-t` I,-j%@C:hB[9D{VUV)@* T%LJ .MSS!IeXϊY%ؚ*L*"@E@#$qp" J(ukB°1D iL +E:}7N潕ŵɹ^_~lbk|#ܲ}T-=tDDfRU%#zW&/۷ifh4Ϫ)򕵵ѿw}{nKg榚ڡmܺuNǵ'+w=;k= O~pxo{NR\K` liA D6 AGu$!EIj#@r!@ss0UeEBԵ.!JPMy;)08  s1 I qE᫁AvCPY"Js`ŐHJxS(BS99(yJdJrL =bRD`H-9i c(qB$A"7%H'!xAQwNв:Jc19c-4\#V"-CB5 %j3J=qNYX l<+KؾwǶY *:珞:yxl .[4-ֺVG,f7nYWgCe IsJN:}7ھ]m4ܰ܊ 쥀̌'rKG&Gڝ(.-غnܺy1\ݶ\{abt5cNk!I'!XnDB{k6'֤^tF2BT"$p!|pJJ< Z=HkCVJ!LM닱qLeY:yz4s%"B`y Gyx6\/y<"Pg'@ֈ`f~:AG?A|ðF` ܦٕ̰}?|˟Ǒ66Hcww_oq!i?G^1:2# ; fUks~/S_TflOطsjv>BG A#j)eDZ)=ִ<'qj*B!0Q4{'Q(6GQ 3W@Ș*IB r5+iB6eHPZ38IU F'<b2+A(+c]Bʸ+r}m07u SvԓTHE>(}~^X)C$ "< s` eݐUsZXIܥsVYkW+_vӁWq ^OL_;=~d㮹T3g(U}1}_ZX8&/]8}A}d4c}OgH*V-?ufe)r'Fcz_}⳦~3fXE_JU&' UU9R`(Ѡ'+˃Aؙ0V !``"B$K̈P#%y)zf(Eg ^D¹ql4c)`뜫MC%klKJ*gl5U( y$dHTIRmUi5{&j(ʢ((dƆIP"=@UTl<ZK!ɲKjQ,"%J4Lʱ IzOB"`T^shQAJ`({*B)0dX)g*V* @I `YD*@BnG2,"+H&7yD6i(x𩵵Ne@+`vgs;lrp15wJ,._8[fgVJP"/ x0Hx|Ϝ?wV|\Q(fC^J/BE~5oܾme} JYBUv-F|wGSe}{qf. sa\+^x[4:[y>w=Њ"Ž<~%: bHа4|xύ?|XD##lw@̎T("B0nHUeH :ؠ@b` #U#PˈыDYy[_Wֺ6/~=O Hx dp4'3 3 |gi70 ?'$GੱHW`yuhr7&:>tء񩩩񱱀8 hhF"ra^lڰѡJZI07717[oZL^2gQ-\PdQIBB$!(rJU 2 AD1&֖A &@IW&E1).US HJZ5TmVEiJ`vB"#k5WTU8mݹ[B`[)2+.—s;]dyQ1AVJ" J7ͼ Jޗ(g؞G>/X&{b-o?45w[sox]:Z Ag%TVFG67? d3J5 ;}yIveOWdG%=eh6m~ܯ.l۶z?䫃˫7>/O Uarźt…{w~qW.g]h]ssMvcoxkXJ-0 zsz|7Ɏ/r#DI9$Zwm=6:6=ыsi4iZWY ޞ]\ewM$[߲qzqHW:lܨ9,$!2zLLBD ,k{i[79( q$ G&dBgv+ŐAcYHC@Hrޠc!dAۗB AI^qRӟ\mm/^^XRq|4 8F@Oo ŸeyhWgDFdH!0 2OSעdvvnM~_j-tb!?AY*ckg<)(/ a9iæo|}3>LUL`% 4ʼp KTG?xM;-k]7ؼwo;Sgy_~C߾jً88951y+wټq i5_XADZglkj~sYF}۶OYqay4%+ )elIZC$AMc&832H媊@J,yW`\B$85UJkRYQ1 pPZyy=Ja\"(A:@ UlY8!V<)&g 1i"CDRh2N"%j)ƫ(I8b`_z$EVL23451E c"zΠTy_Y ( \:*ApX HWeht>@ HqΑ6mdɋl!ʼ޿R0{+ua5WO-S'8ısټxԹjiXLeevQ|߯ˇ/Ǯ߱=teC' XAv#OI p*p@vdyй=ێٟ9v~ϯS'^\uuY,ǟ8Ԏĵ]}"@ӜV !00$dӦ-?SO4)T52cxg:oѱȭ=mU:FqڬQjzgWV,qnӆŧ8`A{vl=Џc O?tu3g|pU,\^L[#3!سg>y?QBIDL)1ͦ0Q98B<+ 8㤬rzO5j<%˼"Ȯ\[jHD"T>wQ81kIHdZ=@ iD&R{x'A= Ƒ47bʒS E%QJѣBbP듿x+/톗}oӮ5QR"gIÀ3 80/ʑo'gϝi$EX`@ Z$H(8'8EDֻ)%dGBv{T B:(ժ/=tz J!P4% 6Cx͈"ӌU?ч24~x LB%tbnK̯;_7 y[n:x|U'&c#ˋknKκ@<ܼy3JW:nzcl(2ZB@]K׳*Ν:u!`V9BJ#T_Z`3ES3,T|GJfg^~rj&;{Zf/^^ZZCtQ90XόU{fnۖmլffǾ}kέv5rZ[_:.xlOysS;n-|qi1*@IT% x@HphwJ.#!Y8 $((*W9FRҳ Q+%TSMU١uރʖ):A։HkCp(dNzQ .E쉐eW6YN-"`m1B脔o9N:g$>}KUHQ`d) `[䕮'q,nh[ H1z,/$; +Ԃ&8s )8U3j! HF;Y&+Jm{u,?V򊑉]{ަRZ'T>s-=q'l_ff[I)+$ yARP-I[w(]Fi҅/K/jk ) V#"_2jgw+wl3//JE:|pow2;?17"Y [@J!3P ToǏP59$IT弄`AjH(ёX0 b HGHLז=biA"Uϼ-S"JCSb`!8p"0 °aDnBs`N=3Rȁ RU}iڟ~}Fk;C^Ynw{SF}dr~Uw삟>6"QgO!0CntV24gL.\<}%Z_CXz] pXE9@MXeZ"5iܖхs\p*Ѥݎ~Qf=j[L$uFSSJGQEr~Ϋص{ə۶;ر׿~}sI}ώPrbvB𞤀N:jQqȟ[͵K6}vy޽Yϩv"Vw^əSG]]\b};Q:n6@!seQ aE"!"]U֩Rһ3 r!Jf UU)ZU""yƷ㚭*j$r"։΃ >(8DZEBKҖq&!"\fT & &nɺAQDJ=NÍ1{4yU}ei`ju|Q}T30 I Y`V"V`P1"&(GYr`ҤnE;I $c/$p XRm6#D<˲,8drݬtG2u f#%w~S]_ֵh{Ͼ}c3swm҄؅FimJ"w$Ukp0+MY-@(J҄$1pLD41 `CUxJq:VXzHk Z'ʕQ .ruнj+n\^X)v>=>=}nr;vmMc=?1>6ڨ"jaian~䃣)$W53DHc7Azj jZ#ccNiUU$&FJJ G ت +n?s@LtiE{뽥KK9lԴ3pc+Š<+ SN'@MLOmٲZҍ?ǎt-fǭ14sg>ɳ[4GfW=juW~i'w\y2K l~Bn&9*m8wѽpZ6l=~#7ok.xD))"Z#i-, CeDzXZ0XTYi{S2 )*EuhRDD2֞5+2HH"޹9FtNH\`Qj v9$0TށBNDDTV7  {@+$ )@$B(P*&)J@ lT}ER -PJWBFAEQSx_)ZTAg& d$ HB=CFq^Qe4m"/(\J\TK6I;/#m*8vB>X4Hm $o)HJtQ 9U^&:Lu)RY*6C` 2NܲҙK@ؚ:ă^DͤPU.QiE;w/p٭v;_TTK fR__Xn7GdBwUie$OQfh7DHjflrvdb4ItFkJ*' ɰzK:hG2̬[gN}oޱgt4^Z^Ӵ(H gϮ{keFM+E{f>8k̠k5py^6" APEUQǎ)I%*D$1xf\5;[ r@Z*&A$[ٜ Y#t~r*w{ﻢsqivv'箻=3Į}W+3u"\Lmo\ ҊeAHPrDC(qW{4i}Qc̗D75O0~㳞un}c۸g-Ѭ1B"0;*Elg Z:I-(-ek%Ҫj\^|c[ŗ~U= Rn}צ-[v=!E!$e10G5m 罓#{ I:Bza ԴUz}mMKEZqLRY3LoW{O_$"d\CLN4#yŅg?Y|{b-o@{}b0+K$I@H+UU67l0 Z_;[#Gl86Wˋ+.("*JI䇑;YlA/ml r^E eEq>AzGR, |@>e "(!033Nhi"Z)ɘ*Uo0J(,J'dH.R*JT=IY*3PqZUeyd58N c* v+AJ"I9 !`Yc ޔ!P,ukyO$QJMePJI#B Se$rE&"B)80 hŨ(Rx(^"yvUHPs_ck, v{SMuʬpD&=qr~i_vNj҉sG/H#򱩹#O>$~~C02bH9Њ#OL#z v_]Dxngsk+(}::1WV* UaI\|wu}o~Nf^]j'k-n}!\xqs_66Z9@( Ew0{C pBH_TRE!HJ{nHS43!"$ Dq`E>YxK #Hcl$_ZYjֵ"=+9E ^  30 P[>O=?_koٲaہS$]pIAO9=_Gq9r5'fΜ:G{uIדFkfw5)(s$#J!4T1H`+J,l|YTJ)@q, T^a*`PyyQ"bʙBiBkI2˝TKӲLuXmTOt~ ^~wݕ Q@c]UP"*˲,2MX5uܾعs|~trf< Dֹ~5h$Xrn6o.87oYl..$Ͼ(Lo0&?{(lU Ynܶ5&&>O^&QfyxO]8WOZ5)MLIľQKF'&}.WGVW^sMٴ݃Qp!f|_h'Wj߮]y<{/'dNg7/ݻ'fVOpljء> REW_{`Y4|ҥV2V$Z;` b!#)"ƚXIZJ G{6.XPqPH\F0iT$TtOkg3 FJZ* CϢzWUY1+*!>bFtU%FAQڨk=0d.϶mRO?bEop/sL S P  E9@!Qgb-IRB "o65-xa` wk-dǼio)\t;24#R`C tgluzɉŋ@DI쀈x(I}V{~x77f ='9?WǕeWY.+r\:9::5-l_nM翚;>:9iRJbf!p@oKRxORLEkF`B2Q=16e |aO=pB%qpa0<;~y}mm-7=Hb8@{ !Θ۶&f#OCO~殩7~݁ fnb%Kc/,uJֱ֚c[5s?[oqT5zz7/"zU:59iӆDEdΡ,WeUEI+<2Z B,cT4] `01(J M%=; ZEW@(Hت: {ַ! 8I8W&)I9/H)*Hz뚫v,,5ۓi$n={ _ǿy!Ml5 B?uf=E$ٹJR4S~f$S3N:VoT2uJG"urQyAhSZD*XKeQ%Iò)A ֪48hԈ3[cEqߗ*2Ud RJv`s(!@bh.$ZWUZ"쪊P3TF: >DqLz#l"0,vЕ#8$ x`)IDܐ͜ (1bJ琓V#e/+|.7d#{%G H T2iLi3reQNoeeieŵNub(WŤY+..nٽr充}~殍z}#,^ݿCoquY⸴(M@%y!$#@`FdDZZ#b [6̷eIC6"l C8A&DfBPqdC>Uٷ>֗_UG{um7lWDʕ˿Ǿx卷Ԡh]zsoRqzX|@d!x$>x"iJUH d>&"Z*R J"\@F@"-S"ByOMWKA @~‘ȮWk_j>(?W_[_Hu|3㣭S.=wzJUN<؏=n4=7ԉ~{h1wמI[9 uDIEQe:M %*pJHD,CB ȻPJ"1j2`JeX8)R!TVj I"8 pHPTB8^16+WY.j֒K/?~Fo=ku/?;vnܲAx3TPʔEդF3>äܶa'zgO>X˪/,0=591֘#];ydsnKژnegYHMYlRy^:)D0N]xarf4߬|?[+zwWW*/wcgnشk[:mWsu{J)g*FE<~-s |Uuzb~ :}VpNE*Ӥ6r#|dH/_F[/۶}e]g<^^H굝۶m۲)N㬗- z$Tڪȭ5a2[:*R+)$ 6y0RĨBp޹Uy&J bDR"D`⠣)yS;g'*^]_H0Nc)6y?旾t^F}=_gdi^Wmaiaf, yVWy?UynZȏ{tsErydA1#㺖6^ bwJV>xBR*r磨,T$7zueܑC tޥs̯BE$CH;EW/-k_|vL^Z8{axxCJn۶wݽs-[MrI-1..-^<_;s3ckyѿۇn<)jnι驠g%:cWiqFQ4( $'AHIJHTF QZvº@,Re*4krǑ(E x8*M8eYK2`gȈ RqlT(wffn~7-&GGҴn*ص=/?> (5UeN0BL(55׹(ٹ]d˽^ #R"WU|?~쐌TZo۳o]pǝw}Ɩ vlۺt¥^/ԓO[+ HkŴ2u4;7SSY$ѣO>vu׻;`i]?z=ܹSG//,$Qctzh^8O/znw}rnr ќ3E/q~KKCmzt51fJH0kU02RֆyhV+G< (i6k=qD`lJ+ld䆅s T,k| d S v;/$uc >,8 ;+@>:Q @2EIHJUUPEUY"q(8eTVJ q9Zf)#D'hRiMg։1=\Kh$Tq!I,-BQ؈D"q`KdpA`+HT( c83,HH*T4FΛ"ƔQVW֋"T`}5{Ç.\T grd)EB7jS6mO7ǛiqUWu/E/9rعh땟~u/zGq(֓$VQ0 Rjު(L11f`2xHHIf% *?le~C"+-?{?rh5N칮]m]w}?m3٬'gO<~g}/-o}>[vav#PA=;+D YHz†i x*0;kEgBJɄi#~7dE#2W+KBA~,8xnUWNmw֫|g>կ3}?Ɨ4u#Qgv)Pd9͑t'Gۣi>59GYPKGKٙ];6 [VJ 8x/ e p֠ ,eF H$=c 0 "%٪Iʅ |@(C*cc,ieD YG h- ʢkRĵ^QX7]IʒΝ>yعN]\pbrb|:VΝKj .9{*bV2j$a(]T֖.9{>'ksZlpҥGM>}/]v>諨Ywyyo߻2语qquh Awbrnƙum[c*ҫKRFVkemE333^_ lk "5cn/Q)[w$lfmU>yȞ™ {Ӵ9>z{0D;lO(BWL뛶z,7l@'NoDEn=*A>6>=vҙѭWtuU+rEػtfX=27ZҔ1PCIM("i71֘/CU>\YEwlFعan"~?gyR gޱU^FZ([fw."l!TUGi&yD{3ȽVJdViT@!ILrKD8P^B+JD:" f3SN(e-xRV I`ۣ8HwYU88X WZR,+F>\#S$q7 80E]MzleX AZM[QWQ^$JcM521.SW^q&ϸuuauׯ:79br"98`R$ػ, ]iI겲BI$I@D9 !"F6>1f#$BTZF?{?ַ"Z@ 5ɞ~ܯ^>zO;ⳅ׿߾mYC_vcb*I"I;WKR|;\}pشaO{Fi;i-w,@,Iʪ8 J(@ $ig)s8:HHR *HIHk@EJDeZIk-W8@$d-sʲ$Is0R+HRlZŮ-yYM[vmy6gIuCg.[:}jjv33?քJ%plsŔkcvZOU{aXp҅=5VY{bݮxTw|ߙڲkM 7eXH@eV=W>vdi˶$;k㓳Tf28_lR;έ\ؽw@Jc>/ff/<]GHImdb5XY|O$.t*6ɝ>s.ףVtMعjuye^Ks36mkWֲAQ*84J+2&$uiW5.yVha /ʉH TG&ur`1!BDZu% ZH$HD$CeU* \`.+D+g,KADA"_#O$IZ_:4k)lU8Z<;u$ Y7Z=X5RgmijAHXcI`f<׉FUgǩ @*UFxF,#ťRikLŁMiV wu8ɳHUkd{ǯz[ytdtbrMUV5j9hFMzѨ%Iy#9sƵ3ԛ>d[?{j=&jgŧ.,/nLuu\Zӭ;?wG##W?;禪N9Ʃ4_Ck4Nwn@ү挆=//g]8¹ lW91KKQl=R_ g{46=}l޽K>/I:օ IH0IC @ 0"Vj\>fme _ڽӿ~qs?² NYؿeoOqڻ۷o^#A<~g߾|+U>U +}򮛙 :yώ0! P['Ip>Aj[%yfL!$&QaO6:uȣ;rn۱/]_[<!l0>x_s KjL}윛X\߻/'ʹ63(_mͨ֞W>Gx3'ƛSAʉv_k/2b:IZ)AUC Jk)`slH f"@ )$ṞTXyh*)r֓@vNJ̈́(xUqR敊"k@FPC 69V;`l!+Y᧮b?xpuWMtWT?i{sKk[fgZ3#oݦ1y ,#WUƗ>ha`4/.^\L[ms+kVO~W/;޽{uOFj-ŠG:{|<~ldbv5bVdffL98}ܞZ!J>}U==ɓIG)t89ѣ'6mu㖱;~FI]ˮ߽_}ۛ[vy냁[TcƦ*ΖC!M<!л*11so/#9w<|9?c|~₱ !Is\}/k^7oܹw}ukϽcES|^_~G~\[?Ȫq7p'j=IBIQp0D8@ /!"@ d ୏ rfD P" g}D-IjmV)]"PzG:7dO?8Į/8'}eq^hVXI@@ʻJ(IJ 6A*B B &x/Y)QeYHs,9V3)CUr ؕV&Jr\CNKKQ zr )(|}wo.%W^q)Ivo$q+i֛u`e*ˊ8L 0#WN8 iisΗYTzȏ0CꅇytW;{vjvvnnog.V47~4VVvkܾGF9nI^7n5jݵʖK#s曯+{Ԅ -$[]Xv/,Yk͛fgnz3GcyVQVssKf\U\tq5W/(13gɱ۞{[v\|i~z9Ω'~:aW.]۠ʩ{|鬢T*B&(^/<ȚK/ 2i=)=oZpбcW]w E$E9S8PV@,*J:cmi6)ARZΆPJqpE#QEh{\r6q7#EQ Zi*ek(CpHC$BXΖ6NHHpl)#R@%R3+0{|t"Q !i)P()#>Xf= IiwD:fF,oK8h,VX1{g|ܨ zUɊX(B^u?[kvp-? G8w]8sw|ۭc3~izS_~/u[ #s҅g-svGx g3w=ٍ̧wo#nRj|uS|ݹa>,r$C{%!N?wc7ܯj2BWf*I}qm`t~{ˇ3_+^::+ۮWػ;waxtU~[-IsTU|gV.IґI k%I>X12J>H0"{H) $tb $T%#VH8Udn|{<5?zgMc>ݷl;([qN;_ilRCXoι\lńJCK:TΔ.iB$BR&X"0X>0D(5 ĶNyI"Օs玲VjJyݦuB@-.WW?)LK=}L*4f36YK{tY7E6qCYf j-*c=`w .سl, U((jrj̮vÉNCw;oxd\+,M::16D¼O0T8.T+R{knn:B/\| ?u7Fi0gJN2(,7DNw[Lb\.h EvՑZe)#@Ynr 9åӧZ)GN"8;uy+^|agqaeaKbՂ1׼ry=3山dj8ry B,c9p$cj!n[g%CAݤ,@@" d@ brG,!"@lͤaȐq`Z@Vtːsp2)$o)MP+=yҽnmSڽ?xӇ?A}D ?:1C7^w}H>/a)SFn`Ύdy\(L>.P%ˏK/Zs~y[#=xb$r@ g/5;m{6^]Ymc8GBiDđ3Yܱs_/Gn狟uZM=// WsO[hםnzBuW\w.ÓOpN1+CV#-'Y(kB0Bɢ%$!$7 c Zd0?V\(BɬkȑSw}?Uo|6.vu羘9>?}8q+^*k1P@?ч?ṹBTUnbm;j)HphEJȭA QDVvBpA'A9#F9 ryΜ-΂# <\Ihf9g@0Kpɀ9dh y` P)ls0:Y8͸f"P"8c$rITח3ZK/˴8Y>o`d`KtNI4kO#[uX=u];O=~!}7<4ڗ6t osRyhsٽy>blڰ!# `ܺZ"AR7[y˻W~afҿqB7qT#oWW[rrq>88|v_ug_}m&Ǟ|ؙ_N}f~e(6ݜ|R1q f׼걶V̜;PgG̥-4vD;k[X_*|qd1^x*6zդJnX.:gFQt92'Khf$_UA@n4ۓ,,tn Ehf. 䦍9zxKLm9$'oI'q(jS,΄dR*`{8N",OqAΉ"hF{i=F:+HuVysi< p䔶HzjHD}MޭaL>~ȱj<+?Ls.9޾8M90d ah13dh{kg=ޠ3M&v^rN˥F~ÆyfbhКlfat>wm=q5p~c}u<^8r|.ظw^||?"褥JN2-Wʅ]}y./[ouGcu~emo}/D݆!Qu||pqYd}uT9GY ]mg:.Fܹ,43?;gUTՆF^)-̭};[X*g,N2řp>G&9HYe^;KUIdR'IVVt:.U1+'JC4[2ZV{#wxU\0D: 9C笊Ȑk9xBγD\ L2IpG dhD8J6)ڗrh@<܊0H; J)u.9'A ϻi6a˨ļtB ɐxT;,,Cӎs)fRI8S8gY&j/j7i_5nH;ICP;p&Q(d)#7YRKj)djVsfLθ0/= n;1&0[imP+qP(BιD,/чz֫0SBet9w~ںK=Sqs?4xo߻"ܬGg=y&9 CA4{BۻLQ~[CO]ر/?~{sq{֭3GwmT_+ M[^~ؘ#=}2u;sdj7~ W&B`WAqD@qm{gv_­MyUkpyf!\iWS}H X,m<]sgn۵%W]`P ;xPoK޲_7 2i!-]Rs.~@}U"P*Pi:;}t7yDk=qgk}dOsQxΉK۽qε,~'x;qxC`nԱS=7fx3_76 7fJ  `ئuSǩTܒ%غr^^LU x$I9x(!1&rH剑@ft< Nk22:;vG/߇s&im׆z6l۾~g`hg?cqԳyˆA[^I8-8$p\q™<*ə0V֒D 0a, c9#p.'\,OR0¦9RvtB)¤94^K"BxF_\m,VR2s>7~{L^H& TJ',VΞitUHGzr輧Y碸kj#Ʋ*QT$33+yk4! @5*fH\*(yxq8cSkyct ֚:c @-@ȣBIr繷dln%NxJ0@k83.e#)ytNwt&e %v޷; He%oۗrw#o-yDBȃJU!m7O9q0σ(;s=xk= ťT+zw-DZ+ZTah֙qV=8mn~{Vօj=鳏?dTg۴r:" !L{!Z_Cg,V[IwB\"ǥBȉ̜>[ydmoyÃc~myG>h6~%WwsġE^(g\\7|ݎ}/'7n(jo@oO,,~wڹ\ ^tM};O ~=?͜lU7}_DT}x^wxS2(am7:" v *ɻ*q(ɖWV,1A19Cr=wy(Z] H?=S,m gV\hk!/2ѩ! X\J&.F; $ЀZVyQ# rĥ sY% P*H!'3K¹d"!'ց|QyY+.y9$^po5+*鍶B.ln,B< H-:7< 9-S &7i2)"2)uH)Jo]QE@8TDi C2Ҵl:pr)>}D9G3?dVWNW\&~_IN/͹_{}g:F@"1#p O;g>N{>%qf"pt7q AqQOhDݒ腐h-/4gxME]xsa_~ѫn~8u[;:1s$ޢН?ۛ;__;O,љ#oێ*Cdyy}O˞/~3M [ܻH<#pG5zI5H=CG yoamzҁC(O[\?;'8筤lrgea wŇF(w~;}#\k9Y(ùC`sHJhrGB\dS4֑Q!s"\Ȍqq8:ϋA%)1ͦTJ!k,IrHrBΐc'!e@9rpg3Xw6Mn]#jӆW0qb,Yo3gf۽+ׇG !ӭ7WbjN7 4[ZZz;}PznO;=\\knصw߹w];xv`t( '>ɻn_==㽣 y901,8˚-V.a7sfePURP:a6VN9Z8_oz }Em4۴}G9G=6W^tg.qVR@ 9>qs>;?s MC[֯K;J9_oKk@0"rI{~e͛R\x-ss bƷsy{׍Rnjj~ ^yb1s&ՖC͹`@9s kc&!H)!0Yn/z5$xIBE޷ZeAF6ӈz.N+t)r,C`'^ze'W]GyDmھg|?K^"ۿՍ[v?%O}ҽ?3ӎ<_ W=081Bϐ|ծ(ٷuA_C te7n8r{>\~rLy #筗qd t9gN[FfFY#csV֊Y*9q$uc\rE1ơNj˽5ȵQrqSp2Ʌ6#uGAą18!1x$ #DKK`P Kq!TbRbܒE.kŒ0E )ګ+;.e)m>KH2I@ȵu%Y3m`BIt"4I;#dKU28gY6sGB!8o=8sQȄ[*x({+pOe?9Խ@A 9A48B(܃dy'O!wK^ncIK vl{寺$*q[G}Wt+OX?,īozuV!tkL$xO9 c:ˀtwc6eyμ'$`2@9 Xt$s?z'w7-#d= $'jK. ИPȲ2dL{dyC N I#'|`J:={('N6mLWko4E)S X(^k+,c8G>7q`@@a潐;b HDp<1 Ltso(Ddi  X]-E!!!cPPp2tN\@x&8jYq63<ygmP{ˈLX!זIh$ڗ^yy}aQEm[_^ᖝn~+=$qvl9*t;vJ-i.-L j=0hq<3? i0Zܛf >Pܸlۦ}}k?L vuYە %pH0:RJ E .xox/bZOZUb۳OMSkC=|TJGgN͕&mPՙY Q8FiډTZ*xO :&(Y4MIp/x-ZKjäϹJ*8`uoE{ΜZPx@D0e:[X ➡ޞ_r]ooњm[ޜ2oJCI$i228ȣw\=|>k^_|J@CFYcHDv'/_v٥/jNJ j˝ 5yCO l^Z6Z`r~'b=s#rιP5$6*rY3#2@8J!@^ wD(qRVkvr84>ѻ~gix2P֍7oVj_f# j\rwș𖐃&=2(@q8'x`*ڿwA&2AJhYag\%DJJ0 M>wRTHr(7 uS}I\[]]/[^9T.;u>9CNY7d;˙im;t╲* #cfgNs!*=幩w^~s=1:4W~fy眓6O<1u JBUyYl?o 'NM ?CƧ  #9B\,KҙerK_ʁG/|Ǟ*U'j&clfw I'Jg/׍OÇ}ڛni֙BXZv$i;͖Ma,{ٳS=C_K.{KtL.6[qmrdly]eǶiW>;{~im~y7o0}Zɞ2xE;tCC' 7OTOe}\B T5,[?*i 8.uWϜ/JT$cD$aQnGKk@ !`+ QQ'YA(8GxF9ki\[dH2)5Em@ kX&<@.pȣ9=R8 1sz%3KXRyQ"h#IRkqUQ,;iR-,JIB9ύ,PJEȁRGYpFtDZ;8Z!EZ̉ʱԫ쁥?~;h榺nt׺.ዮqq(4֗cL}l7ܙ%rbkͰ֕#q>{(_>Cٴ;?ŏf&m7?7GvXnck6e{Nzj={?/};nzR "2dk;hygr=W^1~O=w9˽.| VK mڶQ;궛=BR(*7nس{{.y[#o$&pdǵIpp,ͺIJ󤾴l4[F^oFm׿[^xpAʞ *TMjgޟ؉c[n+Kť D,+KN:<׽D0\w,Yɗwo|gj7;ˬd1T?ٺkZgT0HwXS]o{7K/bY[7Y;vr_)N׹p;J}ilͷȓwN11:19uv_ʦ=8xoxBx@=%;![_?/|UN€ w7->7;OϮГ*"̽A""398kI xB; Q0 RJypΣPb@<:8z(by(XV[iwvzfӆ~͛&δZvrf]?tAy!@%VC(8%{ƒBǙAYV-W@cMɤt5 uLUFiKq)O3:"%x!dЁ`#h5'ʃL$1P9!י$ L8a!ʒ4OSMӤs4 w8Ҽ}{@uM2z'd,KQBƥҙ3ǗBT*Ł^Hi#G٩3;VvZ;3gDTO;ݰI:JivvotXTϞyG7lX ==ƹ0fgzG}jTTڸ}W{}oygλ}^]Uiq@ӧFѠIcuq5{mxjh6|i qd3-u"%hmxltx,y\YXJm຾Qm6[Mݱ?zo7pdl3fΜQ(No^z6ZXZT3o1;| 詭,/z{TJ~s7mt-j2w鎩T{g!\۵7HqI8r6ܢDnG) 1~m-,N{^X{k4IѥUpo8ﯾG98"! *=Q$^~ю4 -*Ϝ>^w}\?baՍE1'pe ;䜸gff<8xekny}7(>ϟ}Gói 2`@W3h&,~|i!k$ gǀ?׻֏8/D3Ӝ'seBv1ƙy5gL*eD,ɵb$703h&)GΈƸeV8mX]ijC =cN/@Eɘ,(N*) feZYxz[)0wڐ%!evd@2k@s P$<\XM^2D"C$#DrL{YmD\jr$oNj| QMXPj4S+\ Bs/eŞJ;Py60(.^gy[έv[N#(nܰ=. А5{Md;]ߵ8gWNeb3axR;4wipMV[Ʈ/Psӧ3'ME[G?^mٶR.\ ķ~[WR.?}ۗ]v,_7W x\Dm2g~'>韞9z\7w?.Uǟ_H.{CG1 y̦ rî婣ǽd C@\J$6(sDN'Yj<Q d=9]^;#=}da~v>}3k;my feixqJT-P scg'6o:̓'Kj,׆.2$*pJ #Ck$Dj/(J:g9QeYnΒJ@f]ၔB̢P w@ G&bk ypV⌼AhC<30:e1:8À{XBZ*B@ gȚ1#2ku7C˅Rlu=0yHؽOŵ_xX|?x}m;Er21<6rm?|TX.3<ssaHGE N b齷{Aa?ݷ-t+W5s\3߷t!j/+D( hh_?^s' <'vCtl@p)pݽkӯg^y]t-x ;g:kZ=nwfcxKVk9[+?7w^k^WjsquO˯zMfgj <_=_c4ƎRatb#>)%S;o ΑR9pqD.4Z^$9w0f $(@#ND;S˲v ƥBW/5/XS# Ra2_3'{cJEkɃFRnS .*P8g]A*o 1$d;9q"5WBL*[b82)D,iU E'V3s{[)8G:$6ϼy0]'ɐ3M:̳.OeHŨwDžSǟڱ{dku)mainY(}k//ϝ]09wӸ~G>jtex}ڑ#G6}gFl^^g ƃwR->Bo4 $ T';1 g``vLQwlI.NT{~"X'"Jc&EO,h_>7Py(G̃`NkE7^m[r~~]/}U}@#!8$uzD 3v9ց$DDQ8qOgMk~Gܰm{wG荫q VgPB1t'MN/bcLikiifn<8)uB,mlX CV7[_- ʅn81\qх=pPΉ8۸vsǧZF) NNK:TĆ$&6әYYmH*=oxLJ~6?[N}=K_tI(ĩG˵ZV;z!3Basw/7It;YgāyN>1JBous _#;$dH==C#JO\PlL'l'm_ڴ{߶-{u7OKe|xtȔ+b[S="BkMHX-$ B( Ƙi~ 0!D癊CktRR{̈́@;ql уLI|n5 0ևRYq$7&w ͼ_>/>1Ti&9p"$`-q'pdn狟/oGFgfV[ C X^\z_tϊ|;_'?8qu}E)%l <:>0pR)[C ;?|?z2lM[7++DĤ \*)2peR"{̵7?z evf]7nF;*=;6o=}z~nvM]cĆ$pω_|g\[կOVsC#'▗<|7?݁UW9^߶g׺bN$xd\!:R0)6* ]81¢fBFe7y }i`!yQ),Y*4VZW^~_J,0&Fw'.KCYDiZ\cfain鼭wȣ!"qD&y7qDa@dR~{:0|dQ5/񋆃?O/nW TV.;Ƽb)i5=1o\xEx7i{AĬ79N&x(8sכmC8r4(VkJLŊ_xe<qŮ ƪ-*Er9~'^>4[M9Q;;}sFhHS֯۾o?c=NJ 0OYN\;ιZm|W[/Z/<7-|g%%DNts&P=={x3Sn:?#9ӕLTNrúS 3ؾ쑌T(6LnK&` 'rk D"Ś:SIi[gr$߫;D3XIgR2\9yf o뷌KMp#3|3wvpy/{ʃZ%8##0#`aυ=zh\ e$=Bw!$M($&)HL&cXYFMUpȑ8 BŹ'#%pCdsa3P:zo8Y8u@jGiիM7 3P6O=^)R@b4Y,FG rY}S:pN.h;fBf\`$-Q<;s:4fnW>j@T0H͍֮ vE,w}][ȣ1Z[On/]p W{g^c31lCjoVۍUo.O[\nppR-b** bؽ{/JFϟ^3O%󃕑.zeq97{o|5WlE7lܴ>ky"yBD{JH.0VGjQu'03@Zjz=v팩 K<ۭNkJwls6U\-Ƀ= hv8,͂bYrDžyo6@6I,0 9(@i+ jk#i #Y`RX z 9'b2\d<9dAWDrP ;3̣C(e`mu2R8O;k-ǙHWWWg'K/X<~w7->55su# _Z>m$F ɼV<$SP e 97@aNv:GDbI3Fn3'PͲuJDi / ^zH0*qD{ j6C7_Ξ:amܼٚe @InB3G⽗_ů|3ӛ:s`!_sE:ay;wzӫ{]Uu$M1i#@2!#xT[}ӧO ZiÈ2R)flhɌRj-Sc!͂P@ၼ ʌa`&$5x'7AH!t`9^,C mb3J#yk%)$kN;\i34Yfrutpk. ]GKt{x{} \B fz{P\?22;*Gr;#YxI|iO {JŨ1 )fj=^}+RiVTjѹUkW{ &; 鹹O8̀ւQ`%Z/v{*eF#k%72:7 /RK|GЛ/ʣoX*2=9prܱCs~wp;*)n&We/:gCw38tS׽MͥمE,fm[Z,ӹcOf}egi`]2#xpB:R]y>.8vuR d:EE7и `^8@D]j-I !T\ȕsa7wƪpc_0s?w>6:~ #ycZuir{U7Pɟ~~/Q}no͡8k2#2` }J |CI!+$sV8$(a Du>a$..σs8 B(m)[:B3+I-$)` sZq^R$]'B$OGP0 F#$%$m]*.Qo=1g>}?ݤڻo|gGٽ?뎟뵡ʗz+ŊJwDq J dCaM/.xkѳeQYքH Z.K T""-G>k( `ji;k7IkSk6mܲe _̲mZuY ת%vuf۲y< !8X/%ML.4__z/SKw_~FμlUuǟz*ޙ:vHk'W;~W^y?}ӭ"VFb硧74<~/~ȉ}:tz~L,с1!2v$"6p]6{$zi! ޳T%)P@v"b@ a3&qV/Wx}ROR+CY-{{&3j+(g`,10# AN_qJ{x1"2 { yA#di9ikܺjjm8/|/jLfn;kS kMj)s$rj5N8f:~݆[6fjrlvM[{}Y=00iju'/O>lC<|#GwCԁN?urh62AfF:rbA> qioCR xd9BM ֗sԮ~skV ,/FhV[*ua40\;vd `PDDݐ!1ZI RhOyTk-ܸug|ǟϾztngF*tl;Mi86=4:0R7Z!d[v64I $- Vw>Mb)ȗDj3!A ΒCkfْk4;9oQ'|bbjzaf &s4xe;[BJ-/l9kbYx!)@&D>r^*BvPN+AYX_nmkwT;ڝۦ[w 񺍢s"eG% eޑ?yjvnyņՓ+:p=?y_4zWZ/泦O2DL$SE=!Its=7އP ,w2sZ̅B:LB F z /E'MD>YN'D [,%3AT{o3cL!ԶqzYh4}}{A0{۩NHm"rt$v%3R.)Yf'ei䣂:n<+Fg+Jhxh n5$z{æO3O̬TH"4-*|qhH+LQ@R,Tz*|>o؛$kMґB6 '֬G?|zC{БO޷#G8~‰O|ء0e0!u`<_|I)rrSjmynCxe/I3υA8n{(˒P t>.@,r朳YyBhR JP afYa'ˈPH82Dž(dR4&aU˔&1A>lfK8E.M4@ E:u.@3*iAZkԃȬIR@!L:AT&WkW]=z;Չk3ɷ|C?Wne[.^wG'_}BN;t4XulhjexbϼT[ZƁ\, $(v^8uB4,w)H"$cLI(](!^b&{H#).Rg'&H=9ty"ujJ+`s=r{Ѷ4gTr>;L) (Xa7./IRcZw/5L04nX8ϕRolwܱ‹n%i4~`_Z5?ww]v [FmZ5.K@q,9m{/$)A g Jb`)H4˼YJjcN+Y!G0ML©[&Whiv>,މW[7WqP]/Yi7|9ZU Rcǔf^aJO~܋/d>'l=J]yUYPb480ֆa NؾRq;e>FJF ATBԞj6S:͇|[?O/eYr'E]Ӌ؜}h?wsWlRI{W'K[&oٴxjz'Θ|D%7کP3gAmj {RŅ'~g nf椞~sWMMh5@ʂ%ܭC=3[R$B TZ(8u5O8 !O'_{{s wj5] e\ISE$lPO ZfudA\,Bʗ.T'8MV/9RS VfB"QQةP0ˌwH$sZg&v[cÜueHP$:m:RIb/P$z,JLs9Q02"pbOl˪\k~=5/}~}N.|Szrn[^UWKkpK'4z<+˒&sCvILRrJirgO6 ؒRR8kY`nuHhD+ɠT,,?y` H)v 2gK B*$X]lW"XL7 z{e9l=<20I nމrG;t]Wo(n47_.o+N:6-)Zq#=V[/N8&2r4s297W>jn>޺}!P(+dO4 ̄l1ԍ#)A)@R8`,[owy)c2f/$)]f Ro2H(hRHQk6 +W]z7/, ϝ87f3Gbahў νL k&(&ck@^L#!"{E]5#0;a/IrtŻO4؈|7QP., IDTR+f2Ibd kH `)AX3lj\H\u XB4:Yw(,N[n.$SssvзjIՉÏ?]/3Y(N:84J,>vRѡzA9zn)O* $ɣ㫄-8KHeA!(K2I'Cxӏ>|׃ŞGc=G}ǎ{bس{ի'rҬAxǾج7TNl s(Nh&o5i-7pڢC1-\sǞr亵GwJNsQ,ιFLn>ډN;Hֳ(;`@qyxz8(ݩ[273GGłv r54@;Ġ 4 @yB-B!R-g=?c}D\8s'jFKiz߼gߊ[ Ǟ|tA OӤl|9RebmkDōfPp%^@AGrog;I؅cHTb'g !QA;8Pdqu!*E|4nך2fc8f>_#ygLHZ8v&B&΁⛯ǞX _y|]__|鍷kC_O_>WM\{\8~ k<zhd Ʃ4TŶ"B@Z.КZ5hiFDB.IbivڝԳL3@TRL5I$<9§^ j!( B:76\qfќ^hs_BoK_΁rT.Ԛe#{Dyfr+?g{Xr/m˖{n~⍷vCcBk mغ{zn5;ȞD ,c/g0j\\.,9əs sn8E!FJa eҀ*RΠk&2yKB< l&H0sR&0Jz$BHYA|$Kg+-W{*1/g_|[l9k.rdƤ.6I0 H3{ۉ'D ]Dȡ# $z屹z9׹Mjf#cu?.U>` A[|(I0/CJ4R6MըaccH$\+ Rdr*|] U!WzK}A8Jqƙ)RNG:.O=;V߸|G>bӽz|uy۶+¹M/{T*9 Ԫ5f@pq~ivqe 6kתB0|gf }07fM>8BZH$ଣ޾}?)Qǻ7˳ . {'}$Q [4MFXplJ&1&_(Iv*xóQw.ͬ v,( 6$Y.VR&ÎfM̌l =Akeq^^N}fgλ_~] [/=}D{}ΧD엽){6ӗ\ݛx_/>O $s9VR^ĖR-_Jeki $7R K9*i`$DN-ESjU/=qB\p9ȌA h"7(,DLYJ֎E/<[w'ٛ7pg>7mI (P@o?㳓x_U|o}>4gkλ8`O y9g8#,Wi)|WL΄DA8j2qkO~3;ɹWZ'/wWZk-:DE%`A{'@!IĀsV*fNJI ~v~Q7W\xGl$IdH L/|[ucv kfRԦ@]68B@H ;0 P"3/{m#BfV㵷b-T駞zW7];nWk/ⲴݲYpng=vk B+ͱcLJ''0ޜ89 #gFA422A!J&ll.ٕ/o5#N,֭HkIkؤt(NtN=hqD)hIV _=H_u{ݿurwXZ^?'{KZ\WgF7yBAOWKO_xkIP7mJ_34-F;Z:/p+bY]NiiwJr\+Z8˕onشflT*:+}w~O?59uFiEaC7u[$fDBFt< Vdu:fxmF!-rdl⌉۞ ;۴/Njd$L;M[E!,$C2 0i'\[N(u=9tB t.ADhIzYjXA!'ja.G AXdo3@{g!l!IHI p\P2#pVr(5A>PQo^G䖋6w]K'O1T.\t_ugmizKO/]aox?O^>gw$?8(S[΅Ӌ$e*W)J[%BA$gΚ2 \9 vr@ W\"Hve Lj!1# % gD,椕A\[QZלs_/-MO:/~N<3]wtD9)l>o}ӯF6V"μ}q_xmw16m7FA dUBĎPН?>=rh]-g98 pXD4D-̡@`vQg'D-"@/X@B]SHoQZ)!qyRKF_}% 3ӧ{VN=xCo}^Gor>#fL|x:NĖ&b `πyL,(JȋbNn={ucs'2`3o# *W͞r"B\Rf6gu1ۼaCy걇s'l`2kC+uk&~}G*CHz֯l*+r>R1B)ԅb4H=W@JPdyLoJ-o߶c5gm߸i4bX-1 ԷNfՐu, "HJSfN=ڭ}'83q3hh4B\WLiӨ[<;.?7BN Biiazy+@Y#3zG $UW;IfB.@kH$1"# P.hP1˹އ|j/\*m.T I%J$'dʦY7:Hh%8,Y,!jMjra"Y/0bt\Ι$sT@zJ%p9dd΅R,I¡"x3 @FkM@ae i+SF3iIh4NNg{kAՅ6^~]u7\^o%V}U]‰kׯ޾ЋO>(+goW\9֭/'绿?>S `a/HT>pr'BhB"LcG"[G>k[HMD[IxRo\A9(.8!x= {%2!, |A@!{{w?ϭzdž5>OWo98 %|NGdyk~ӟ/ _ypG(DXE(>x_lkig(0;8.I^/! Hh+㫦ߟڡ'CR2<2W#@&B* dDDH{ # \jH,@ wXJ!QH0!'DvlɳPsgY:i[nqV+eE6Lvߺ˃Cp;q'RJ|A"v{[ K& &욗=@ @B 39Bs}$D@ؕ []ڹe?9yRZB$)Z+.iJ^+uq]}, v9k=Fev.CV)svnfU7, \=ԓ[wQ|k֬oY1M9~$: 2kt2fͪrK ?鞧o B0S S7\  3 #}$( UP. {{{G+}=%M@H rL^ :'GWر5MRs~Pd«~{2i'iZX^H|◿NgH0ZZ}Bn>n&NzPpmzSssKʊF+\1I[gl߽h=CiF$;Yz|m3n,f+췯-ՙJokV}},Lk>mH,,${QJ!eH()̗sٲnT,4#Q @F`t5,6PDRRe.L5\ (i0 IYf@M@KΓ hZQsε;maI %8i qڊrEآg@Ø5V6s_s˙6Nm\[6nݰfکuccccz0 +c׍6/?o|=xKvM-/֌|ߴ| ykvl8%?>95:cn_]އNv3 ]wn ,k_B3ʅ}&n^YQașMllQNwYF:GAvۛ Щ(WA&-3C 3)% 8]|. 3gMكHRi%!- sޙ\.o}οy7]瑇^6:U{aIDAT*_r0`ٚF@N%ÛoPuj&}s/c{i߷>uݭ$"U()8n@HȂ?{K5S;>ϑO-70_R8j) BI@@ p IJ+z2ћ2.QRRZ /GA[n%Zj7[r1b#KO nM`\6VhN`D Α= L$Dұx{Ǟ$G@fZ-M/>W)|$n0;ByǠό[D3ڤJ HABXp^zom:g cfŅ7^S)3o[fֲ#NfiP~]_#Ǐ<5[TJ>3d-5ޞ\.*+eY?t8IZIBfG":5s:_s/|l7ʼnRAcO|u-Hflo kG&FJ *Rbiԟ+dKaP*{FJ\))Y_[^[LTp>Oc/y͍#Jwo``xowpdykn( y/IgҚF{ѹg|kI@AY;z/vՕ?T]+;ZA@LF͎ I=3'̫l^Mz:s_ם>v(_VÞr*Pwxr{ލ@Y*M FZ Ke]]#=g]9*qY <)Z^CJ"l,zR68\Oo1MM ,Bi$MBG&lLi4)ʃALH>KQ(D@gBڅŕpx5O<:=sx051lr ,|Ch||8kNo?sPߧ8W]{7>UF2&y&YRϗgq@?gwՆC}t+A걠 waQ(n-gfR=*6+2 |cY!$F+̅a' B{oF2ñq#'I'.RVABgFxJckYhMX{s&|1ٗ~g/^Nw VA|Г?}n=zА:rՓڊS5kƖf'GڟY)$paP+ |271U{ ^0.9I]@إ@yfRʅSf`Owpml@J¦34Š4ryH:A>sYcU4eA:ԫ2υ$i;If0 Ifq 9/r:<өvA,xB&g H )kJ\2 @6NRI2tR-뗱 ]#6]S˓[v *B_c߼=k(\]q,Z)cĮ{`uۨnxY]GJX/[z)Ğ'x߿-oPG}v]x.XɇqySS/Zۻ|PTOO(䱻@r#t@|ԎJW\žc&p,UAgXBR$\K ZI636zDN)e3J20,m'rhYw^aʷF. 5T**ϵ-"9vrPp䱽{6o}^Ǡc+B)8vBX]t2)Q-aU|tءg9pvs:>6y 汙v5s˝3'q| UH$E Z ra¨ŨRHN67jVS[ΘZ=b3ZO%vE!ɑGnq=cv^<>:$تށ4܉Z\[egj!_۳ SVkBŗ]U[ZJڴzʿ7s/C@(* N?V:ˏ=pĎvlظ4rґ'f/vNx(؃#~DDl=:ӿjBg+l̜SHRa.fٮ =V"MH^" "dJ3[(h餹(:@T \굖 |*F iI+_,2Cf\T4f4Z:J`;q.[@Y+vV$d Lw ;ΞmKNgMGC#75sO={kIp*=p v_GWx&N֯~x'>sѐzu;֮ O3&ԑI39@3uֵ m[,ŕsٶ̭+\s nUjCUƇF;Xa?|m;īS@ F.ٱ}G_QICx`35iTݸ}j ySwB {~Jɨ44pϜQ̝} P2$H@QB(0|V[^WF X\Nm9sبcY9pY ˉq(w}^Bܩ\c|qa?W?/nzͭ<4k4 @?K^Zi;^Y ڻ/{Rٶ\+_1$un.%6- d^=y>}=?7|D7pU(I2,g] `όB{]".6_}5ͅî{d‚Nj=oǞG]ĩxڶcRZkڝDTh:f I3B3N#1FaP[cIc\TBHP֦imL,̗$(u:: hĝV419J2mQ͕(vύ-粴ŭYuo~QX.)L?C]XTǾw.?HM?ҷG|V{oODGZc/5_O5W>zlT OãKqcLyh؜Ŀ|u؞_i}^J4b*+Wy*D I8u&.KynvF @,T ""H=xyz}rqm˼BO?ݏk77o];6< F=:{O/OMNm\~jxpqv:UdAV v 1{$?$2#F` {C´e)ᑧlrd\26C@bfN!K]\3 {;g\Z[?4my>/k7B@;g02DHaI")Td,\.hg2jU$$nwgꪵk/š}{ +žrnva+"l Ə}nyɹ{ =.QRo;-DXq.(ħ>/ɕ*wO̟*HeYWoX3:=PaPT*@0'%rJH+-Sӎa~u#c}7>j3';R.̞ey 6YתP|}'[m59:,na,]pE>'t>Ԝ;rcZ:Mn޵~TcRT_uRhI1Bɒ5SYZ)fX]:#uon//3J1.,8n^!zϥa; gDBn& ^Y^Uk$#MB&RIn߶>5e>؆2GΕ [$P?$";;b[*M3=L&2A։ET(>>=k%;!!&!уiKt*)ɲ`xƹJ,*E|_Y QQл߰k|?\ij*iɕO?U1p|~#a3ܷuwg?>†Vgǎ=󥯺o߹R .tgO'ISo;DoĿ}{7]zAb 36 )[iRd.5D25e7Z-\bciἃ@ݗ^,=HfOzl\ $ B脔DDV*; Z)+%*KǷ?מl~0qCf%OOͷ;{ZnJk^tGwωUh-~p%6m;[_{ؖ(rq*iU5>>vntl|tl@lpxl 送% qJ(!s D))$k 뀅Hqe'H(. B ʚ {)E^Z}fyeuo~N.--5BPӜ4{k.ܵkZM8;I@GomnHD@GE$[5=gL YJ-X_H^zFFزi9焅(iHD]*g`[ v}7Е}"`B/.:pza(PJJ2"i;EgT0)A,)ͬcz\>$ɴ֞0bg٨yg'V꫗fuyG~̉33&>ΒӮ,Mϝ{͕uW\zruw[/Hrzj6T7?o|?~}]>;p:WZE7_xMz8{ߺ-|@h E@ Q ĒK- d<VmNqt` zJKO :]}79! 6󍰈^iڊR{kb:[+˵j~j7+,yI`~{ǷOBGV=_2(m4; ZH$b2jdY83G_[fNBH"@pޕCO}~Oyd-$dpХ]v<`4`,؝eĞG"aIckA™@K/$DAh|q._pGr@D0 a) *Ju:"fK uGPĤ26&C ݥ $@NbyW&#AA!PǏZm;]gW^T9 Dedu0ԧ>EM%ɜ7J3S(ʨlH)D)2B8v4,NBIVN*Js@lsw_@(5cFeY x};2&완RplRV X@[+Vo|n<1p_|{m*(A~g< rd[.VM˅J9v> FMm\u7`uKKG..}O Mb%M $B)$2#(F$"R $ Dt !I ""#k2޲=1=S.V^W\ujme~j9Q#x7szHGqLM(D'+BD2 @]_$ 3J: <y߰1;jb ,i#9:&@Dp@dEd= F)kﴮlwJ7ցW쬒2ˌ$F @R(M9MQ;NA qؑ"NMjN;fzc=b[%-.)!+5SSoz?\W^r p酹zjeUDanWћNl<"zRGbRWrY9pw~Y<345̭/ںiGOW_f{OP\wK굕 &:YvM):K-J%ApH:0@)RIIAH<./n~Ȑ%2.[\j NӠ T'N=O.P}ūo۹}Gmq !bѪRT|=>0ҩ񑤕"sR~Ț c*0IkGӀ= [H#sL(P8gͧO ҳi`/vqUcAt#9ɣ/,U$"^ԃgwB[QLЕJ[CcceaWyWgrZC=.jCbޤB7}ѩu`Θ(u 8J 2  c{֦d!e礓sH %ww(ХV+i,@S%D"!@ڱ"D@KA)a`C0;=?wpoz֎c^ ٻz|^W2?>O.@*"{.BDn<#H"K;,ɼUw~[Y^O"5PuPVI:,- -eFjl6{t( Rcffh48Fd )-؁ E.Y; S KH+kem8P) !W2P"6iY'eG}ҁDC׋ἕ @{$A{ NzF")ʟ;Y8v|/\n4%vC@IH< LH伧n?CwkBou :sEͼv0ԣE )F͸#4I/<,!uQT% kj6I=8k'lq~Z`N{O852:z/FA{v޹۝JRju8kA)J}LҨ..͟:=3{Sr3^1LFF'VE}RJEJ0>Z9za벼΍ ;q%4K8I44>5BY),ulBE2*%N5Zo^MdsX,pPPauf'_;y~O{9/w?DT*fb.LxmՖսKfNRZyl{Me@BL+o/-<蝳>8_/Y0c:sgfڭZ DV)dDT+q&Vy91SW)2.T4Zew$%7NH% G`Ry)3xD͕%Tj[Ia dIq J g Xh7[ IڌV;vLE+S~w>g`A=87: j#eH#TK^o'}??N͏nڸyj}dV:Re։;Q euC`%<h:,aԮIj!R Εd-AV VR" {IuȎP.3N Z.Mzz l^*tR}tqڇ>rˑ¹gn\~lVOmhe)%HJgE"f4,K X}b3OI; GRpR8PJ(4# 3V8$e&LJJ 6 ԡ.JNҪY-.VO_۪fԳ߹\m$ǭvi3qtҘAGHHBf^0u%1aD({@um?Iu.|DJ.n 22"3 'ІSu,/,npֶ'9w (s-$  !*Mgv}Ds.R9E%.,Fp"Ld׼"*3i)o߼&T{= PrNK+ +=ÕJ+htɽ>{~;[w=p~#3VGbӟ) F{J( 4|_y9ytOP@20ε;q>(fY'iA*Z G:2dA.$EHzH^xF.FX9<֮ݣ=Y$36>CԦ5,G'~_?6 ߈fRYw%W :Z_Gw~!'bm^9y aRLYM"-^v\k^\\J;f?/P3 kp1fzp|늗E_1h3"cTP,:61 }uM DL̞ZҲ d#"I~o|xaz9j6 BzD();턕0Z *83:B!$a'PP$֚@jTIwL)45*.,^i4HI%"Y缛>k9}ZBntr7L[xp\l?oXO䣢·6K-DW|zϺصsC|:kt߽c럾㪏|ѫ7;>V(uؚv;͗5Yz'857O| kk#ewHgIbG!89\Ҏ\!zadF)%*ODYTJYkC;g`"&Hd*Y>9#-l$,:ӂPPOl/wϿ?7.؞vY@IH DGqW8t_>u_+ TJ<\ 8`4l0H`;$gg>*ƺ$MHqh{/0I͌'*2Ϝ9'E^tV:tj(P7׍?}2dv(`պM])3X8o <֒" ΁'!eiO7ؾK/`xh mo󁧟sOON]8sH^+brq @Y11s 剣?~pT1ȅSc$`LK@mQQ#2LYpܮbYfBr$ )Tj;#PAZ QĖ?Hپ(r 4˲OrcǏLUmF mAcg2HTU8ZόE=gz+CWWVMMҔOۭ5~dq@!s@!("ԑP",F IzF]HQ83YŽQ *Ȃ3xl(iBːdX,t@ {"\i,]r_ R= \I^O߃ˋj|"' C#OGKsL +=JnQƹ./Pj 7A'%) I)sAIbG2搬1 % ٙ\ ]jY4˒8*,i<g;.h}QMSy@xjou_ܛ?]>X36U [9_׿^,˿} [P,8 BaZ}??i蹗Z3^[Y"9a|B݈۬|.t* \MRG 5&SdRBΧ0@ǖ,gxOHĎؙ "$ihf qZsH%9*sD+b%JEaӰR~zg򑏾}W zA:Cy=9"g7y/(Es+D[lG= rH <HƘřg7\_ĭ]+(tjz7޺v ;.3Z K7VP %Dh! DL#,`FE舺A^}<67Cz|Pg~?>K>Wrk.[[pus3B"f$Br&Zn$&*/ꢘ.@i a13hQovI/#0'`cW6G B sK Is2Pij Y !q{Ť0qVoyӫۈYBaptxrrtivϱ'կyﯾ}߽OzbgNweCBfL?\2Pkعt铧Oy;tC= e1ГgJB>Ob"H QM2TB&B BgizHZve=kmv};P>EuTG֯}m/%GWKWr\uVNO {J9k<92;׌ieX E׼len6 FSU묬M/XW s};wW_ZR2xD)Bၘ_ t U뫵%'zgoN0Ȏ $ ! D8]";/:f,I@S ^~АAz54yW!o5$۴q"s]'i Z*"B@"HGY;y "3[k:V!B $4!$@S$ZtIVmڒ]\`]Å%٣G̦Z #ᑑkGKw*g|eu/N{+mY<|$\.^?GM3s33w}od<"E9zj_5v?6FHf碂猥V#$IKA jGBs6cHG@̳Ph!Ud։ /e.{{'ݓ4 G{$`vR b,2KeVX쭤luV* B/<9_}S__Y,O0Jg@bS9cm_]É;)řV+=A!- 0>"sIgRd#^$1P' '/ЊP9o Vjڶ^ffQ<}ks?th͖&Ƨuۼ=#w"eBRyj$P$N X30v+$atW>?qƖӋݳ}ޑ|Oo$yvl|}kx;?L?w_Xm1. \n@d P0{"###=`)Mi:"vVj_x6x1A(`֦ .Z2{$ (e6!qѐyk<2Ws k+qϿΟǟܻ3Sv!\oC i D(C )"mRdKɑKo}gh (I"(=o?Óa B$P !IM #)M+Z\mFsfqflQzC:Ƣz5SֹѶAtŋnk+(DwJjD ®ו<9딘\~ŒT_w׿|tT[LjD7Q7YxF Va!,|Y|cm~I*cWKqmkT-ytʊN\,PJZ0BgRT ˪*c̰PUrș"% `6a[[[:-J#PQ" PP**Ǔ>;0~ ΋u\sy/]ZsM0?SN\X-Vm[z' ~ffFʼn*ai+T[y"k؄@"Q#A-'Ơ:S0#sH4"EaCDI|+'Lb"&BN aCG8xq)Yz}yB#36-XhoZ-9ɒP\\\E6BgTDHXARhkm`&&c xБwprBEWӎ;νQ=} 7b-c֖*:M[E;xA1PXpGlL=m qNuQG$РCDDRUIT}r㳯uG-Zr'mz>6G_M^wWb}w_tZN(0ɆCjFD@@v^4fsEΌO#Nz{湭*KFBԤ /HwJک@!D * =  \`BA-c͗7u~^=0݀BCq%dJRDQ%­=лU,[{BlTq:6RVrYƏoٴm m "vT %*JLÐD` D1sV}616v{GKQ镗_76q)uھΝ,;x`Pod{y [zG^e;A&B)X=:ɺc:>>WMWo6qN=]83D<3@tl $Bu@ ΛJ`p]{Kbzٍ>NػWW0t&NNu.x#C}; )jmhW~kgj Vǽ݋,)`b_6g sPD˵ b2:+p^(V&My]aUExPPU,s.DZD O8hsީt(ό@@BB,PN6u0phqĥ -ܲg[8၇ÎӪ~?̍^qؒ ov׾+ݞN,X'%#'⮑;nn{L-ZYW{DRbhתgyቝwD@ D gkeQF>81HldUl>+ Z qYK㹹9#Q}= ]ĜfRhI`p(9xB*;f h@K '8֞a,uZZ@ +kQo+pA)6gDz%+VktT.ye+:ZI> XQ-TQ&`lcn.KNfcȬXE!"h 6>/G~Ϝ\2n_カ/;+x?SO?n|Nʋ6~wUkl `T@A " 1\u/Yx筷{ο|3_Gs6jfbfYqꎴ1G*Bf2 JRQZkE dvCPE͐((g/| ZN!6?Ϟ@hZآ&2IJ&$6N~ァ7kBkllr%VI(c]*ƫG>f&BJu$U*V6:R$,cvA{!lۭ6)uqI~nMG|Fexh`u$6[tvyONϹlMNrE;wro|GE?kvjB AN5t@ PeYAǧvUݏ?~׽chw~nZJ/ThP1;%ڳ}KV4‹PHcfb9q[p f4`CPO=3yF1RB "PUHa>L V;@=UTNA%qϳcnof@dt Gqݹ}5 RO6404pwN]X6#@(hł(H "T yOOeV8󈣎פVC AQ@Q< +Iڇ(qy@4#0"I:ȵ=W?cU{ϝ_z7>KSK:}335~},?Yսtͱtha D0"P&RBM3ҥ^?}e_oz?}~oF{F4+5ULJ @@=84侕(nytCc5zQ%EZyHQᙔ@'J-A"$Œ(de'?a@Q xDCȇPPֆ@ !򜡧EV<f'n=嚏\ϭZZ~t;;\>?YDo݅W>ЙKzm=̳[tY25*U ?7bsܩgG|`ch R`jD"EhT!?di$6,FF]eJMoH ֯? 8hc FϽ&:.(k@ (E tɣT sQX7A#qQ=7 V=0ƛ>wWAmM\r)?0:}DiRMC} {<"  " S2x hx^zsV5g@ :L q^'BAE^7q):;𢡄g]r5^"!4Zs\e->g" "CFQ u(r`DUVf(IvS((`" 6iv5Kob34\cßp>su#废oxu㏺'_ɏg-c=~ sӟG.ۏ?O?cblƐFA!vT0ᾁ[z=<)5u\׿\;:0q`#UTE|uݳuobQ++;WxddbAC2ǒ5)qpRlYG&Q9.Q6(2X6ƑQ@cҡF֘Rb(F[T%cI"[_gz%۷Y4TD x՞.Tq2\2j E]׆HH%"0F" hA%!0J$v}޸s+QDFMMξ>7]斿ycO\'f.5Ͻʊ %bI[mۿ{S>>3"7g+/45zzQG|<@,!a/rh I" PBYjfbBi&{aӞ5en6=tS[^.%RJDD$6QZ_K\^Ax cҿxdqOooV~ _3]Liʓ.aF!i"YEi; Ql\Rd2*6JY=BD(rfTtB ht YuI UsyD8 D $I+r|aj]29:2RRDvO~;^ػMo|hڣt/ ?g);FF~OGg3X)Q\]2g{VnKO:ϧEpiHV8kWLU8מ dK!\t\R׵V* PƐ!f]d)EgH>XbVri$iABH) uɊz/ZHkŬb VY"=X@պJԌUڊag鞻m<}):ۿtޞrRui h<";!c-*a` f"b BJkk3444j\8WzQ8RbFݻ- #Z{쵾\7{qӐY tޠB,HI@ ʱMLlB52SUl tޘl]sY u5:,[^l[[\uغ}3Eo{{>eǿK;yˣsS8ˇf^ O:\./-ƶuśfIuSK.[|M_?¡{o}ak<yao3o%]fMs93'ydu邗".ӱ٨(VZ+`UN(0iUClyHiDp"J4֑&BTblNt5e;71d4TZ[eґ1 %KhIQGFLP}zce*ķ/?_|jgJ\66261(@iEZb`!BQ$@hDE`=D(LBG1-vq銌%#x%xgy!h4{j<8sƙ\[Nܓ&Aѣ&ONMd96w ڬO4=c?q[nznҝ;F֎]p[{}}}޾Rwo\ظ)"yd"]zɰkeh=ʋ`JYT=JGڧRiv\PX@a̴`2 BrHB jiX4PZpy?ed($TuR(C !RR ` S4o3"[JejMM})G\oۛKs;_#Vk /< .:c^/UDu_}Fڱ2QX0V+ITb!k,AdJ6לwUi(Z eP9<\p@BE |€0kI)&2y ZDVTH 0VydbAX>dyn;Sf ѱm#̊\]vozJ@˗UkPu-_8D *0a " .QXɪD/4@d<*bk AqbrP6J"l;'`X\'pFVoKϏMOrXߺ|IjCfFh 20 $ψS)$d8T{ADOTIž?'eĞ= xq4]H׮^1[۳gzcawzfO]sտ]oM;O{m wh?}gM{M4Vo?qǷ_8\M笈/MNA  PPwĉ[>ۮ}/?o4n[pޱs]rI˶mޔh{)wqu\:15GBݒ% ƽ/~]f*= FZ >Tl yfb!Zs3"g+˲,B dC# "@Z73TA破 ":y4Y*UJ'ZWTs{lݿ{.G]JCoOΝm<4]fk~zlbnjj짾hC}v\| |35ɮ]v{q&JmlQ|;=z,?е[_yn_1<:Wݽ+*Y_w==k}-]Z-G{a;7XMj=Qe!yyey;7v~=6'k4?߿ ^~᥁hY|drg!puWOWK@k ?䟷ƭ/%ݛupַkQe-fp3z:r%JYLk@ANQ+,mf njκg_ډuq{v>wY׊[F~qo}7^s͵po}|rᢅףN<_]sNb{T}AĀnoDžKV8o;_Z3Rг@Ƨ,/MdF!- j (ʕJWRo_O¾e]]=*]>S[uĚys/%/>+?vk}ݕ|m#SK=0\Oo|kfxuPfZ- .[UM>#W;jZ Eh %*.E陸Z<)t WegIǒ(a>hJSj\QUk3wOyrbEVD&IR)`Di:KW--y++ !JPBYFq\{__\^6Mk2SW6:KI,Eȑ,,H^Xj47=TGc"ȊyM3Idu%)V|rv'r4:|E2HA)= FAҴ\daMiRNFRAi vOM6w.Ӥm\RPRJ8KSX5!HBjJID2𱱂CgBp:2AEZmnNLLT/},?lJRzyӓhmC-:=[wHcy0uf^@I1x@:"+/l⸤j-_\S>݃=:oc˝,=v{?pͧ? Gv|XoǮZ{|w4{?=OSāo//iZ8=yz=6@̠ vO[up68bxac;{䱻nO}U5c׉^@ڵstjwc}6>+X{sZcC- W/Ǟ}/ËXEi^X5(tN$`cStv~133Cn)QB9 |K{ T(ƀ&&2*6p]=Lv06}%ktҵ٭u WjZ3KV[:)QZ!J9/8oHG )$,dVMG~>T$H@AE,"!4ObL2Pߟ|~w\spEd1 v&(d9rA  "u`姞_Սrpבa~>O^O,S_p@\NnPquSk K\iӴ22Ymk x_8|#[R%C&' fďQdLV8M k@:Q",zv 8( (Ev H$r<}l[|}f1sbrt?I"v) VkJ]n"׊@1"{Eh-B<{Hu֥e:1.E%Q o1yYgǹ;^#77{[+֝YZDvAu@-a0Aa:4#8TJ%5urZujq=o;,_ZV̟݁Ft~}_{NgĮgmnTۧmnyu۝w>лk}P5ofttS+W򷷜~ICs;/8k0I-]W[&*99F)P2hIFgڳʼ#fF󎕃q.%rs"̡ 1 3@^R(2Rbc$RɱIPt?TN<߿x񳍢=?UB]*eH[C`93R"JP aTimH qwٛ/G?ﭔm{ vum϶ϻ lY?|tƫn}~o~M3Uc=3UH'7r;Rlsטmպ[ub"rmZn@RDQn5QS-5DH!Q(-cbDe(IkP0Z) @Ff|))4 eH2]`$B;7P2q2\ J[!01ڞ@ѩ8ZDZj5(A֠Ƚk( k/P{a*ą^8H|jfzo⾾,SRP5#û\ukL53Å)Ǥ`xfx*hV,[) 5Ț3H/]:4[-8ä<#sM=|GZo~g,|Z4k.|'o{;Z~par_?}7ݻAw~ƻMuJ þ᷾K7˼g N߾(CsSݜN]lXuiSSs0}pNhzRWo5'{$ -;/[4zҙޮLi[+ltY]liܜ/ByafdVAgE"6yeubW֊ÀFfXCIV+k"#,p5$S`dQQln}M9B\qiF1x1|_|y?=@_wo9O .(T B`e=2<s穇}[qRZԘu:oU=8\;'J |1DYc񙗞|C8;߾e`Knv ƐRWwWli5&ٻ}g`_wO!)Ҍ"I<\ Lb &C/3534Xz{8?w_v7CfrEZLRFhJϪu'7vnͯ~u>cWIkDGϽp GLg9FH*Ӂ۫.EyZv@ok>]/ZW'޵3(0R=JqIkj%P=C๫V3_ .`Ȅy*k:27==]JF[O R}>(]PVi J# kTV{C!$f.O-WZ#bNIVh( 5E.;1od@TJ#+B0@@@8Rxb-̈$[G Z2Z3$*Fm<=1U_zrf*v?yijppl.==C)~ G[,kg}:b闝^|kLݶZ<5]ڣ]giOn;cW^>wnϯ86$+o-^5{R9Y08>acUm֟sY^ _8 +nU4r)i31>򖝗^"ja;;v2l4WZ\ q?՞jT6[H)CV"p y+i۳^؅Bή8|)"Zs`V\F.g4Xch5$5dSFj -8ry[|ۥ@OI"T]$+;BDDIDd) |)CiSݾkwJvnc3K;|˾+_޹۷5gdڑJTO"/>rhŊ]w39bhx$eCkn9K>ԷSg9A  O Ep Y @/e㎨D%}ku xƜHR\g+zCz#NeU>q9ԪŧvmgRӟٷ{LOX0 (D(T{֟#N_1WRwGNvo㩧η'J3bRZ<.(\'6MX*ʭDE;HF(.'vZJٹ ml\%ĉ@ "[F.3QQR"aoLdHSRAIEMYʥ=ܳ.\}# ppG+yzRs/P:mӓ|~*\pťWmkц7n~$6߽3?'?Jv5&R@^`xnf?Gv;.~{?-Uͻgq9񨝛_˲ON"RoeيeqKDQ43/ccWͅy2 _ G^}CݠV*Ep` (Ucjھʲ>6_ό .' >sx/#8,TpAtD|=C/ݷc`uɢh՚qdP'}Oص/ [6rp}@k+pN}~æ(yuc^=t/8lv͵pO}?cYs?S@k>|r[zp y@W,m'.).b(琦2&siV ;XG-(CE1+ȃGr,DC*-s!xH+PBz5ݥ_K<niɱ~2Ro?#վr\V\ET΁ U`Qm}3_=b?.?#K={o_e  mڳrw/$іg_lvF:VZ Wu6r/6Qw윶CpNH㮞V ( "IE9#7vs‚~nēNP3 r($NE!:Lgs@H "*AafKVX֘AGib` r]@e E+B&hgErX+ ρ)yE B`XaTi0x$,4Xk{nw̻uFژtuض[~KοlxfVK3=#( x@D t:#܂0!cJ<1HT޵'J{.]h˯ΏM9rlw ϼ7zLuǿ߻o]}5@~߻Ʒ9to; αxӻ?v Οu͇O; ~|=031AJ]ՅK?OMesgwL9#+O?lvmI 3 #3e… Vyj$7zg[7>yj5,Z߱xQO𢴕+Cׯ?׺ Q)qy9avwU88 tDDc=5h,Qı D5ETP`6D힂YH@:VH5P#ȪX!0j]JW} )D$vAW_߼-}a'7͖˳f;-P4hDF@ACJ+kl[f[sm[<3 `1곓͹Pxqkh@OL4v7яgdž {f]iςWtwMJjR@6_l>x OGw<䎒UC#k_700R).4 bBT*ubreUuI'rXkXVj#98ZKNZn R{@meɧ{O]ˍٹ]&=݋vޗ7<%KZyyG'Įw ^}=<]{m>*/<\ N\?[j-, @8(q)nISJ0RdZP9d΅bIhCJ{cbRx/r>(PhBp>Z3VȞ;BQZڊ `u\I*宮RguG-]16zp>_P㎟(݋^EmII2?WG: !;u7{sz'6m#8~bj[S-[^ӝ !xmmDJ aT\D 5kȹ(`iM(o{z5`|wW3Η-7+".D2牄dhm± 04*ˡfd>HD!+"(đ{Ja !,$)8I!3h5Юeaᴝ%ֆDk-!\ፍ4 cS s2Bh@{yvD2p!P!B orbj٤V޲m[}垞d|fv(/=}ϋV30CD A3%;` q2D @Tp(&ۦlY八8m  nxхo>♇_v^ܿ' =-_z!޶+ىW^vE#][o۟Ixyn03o>f:wVhdk[r/zV;%wGq-=o~Öصw@@i#Z{py}xۃp?<\eo䣖9qi$e#{oZҶhc\pwއ܅hfsEZs8X(pC8L RkAR&riE1Rᙑ@PJ)Z~QoIDAT7Hu$j\0E\Jo}՗\ymߒ&"#BBF"ޝ۞mg\vݳ\^s$G13ڎnߧ-ڻ镌aٿ<7^7%BWo-)3eFTb.[v%ˎ[4l 括>5fVIt"HVRWٚ۽g4JAk&Fe䫱< X{-ujy_d!R=xp'޿h\iQ\]bSr7{'}>w%g}_|ҥ8`ҽ }ꞍM"U{{\sDB ıʴZ66IٹzW}@yP&bl !0%3c, )F*,"]x_JJU( bŘT+ =jbwWQˋjlIe &yz^ݼ9ђp^74GV?3syUJZǥo'n􍜰|QWn_N \pec8AnVAFSϼtR8ו.Ul={uE_e}{>˽s^jZEVkZigX¡ё–4g9GK,P+ wX#IENDB`fritzing-app-0.9.3b/resources/images/splash/fhp_logo_small.png000066400000000000000000000006551272363601300245540ustar00rootroot00000000000000PNG  IHDR$;g^bKGD̿ pHYs  QIDAT(E=hS߫&I(.u$*I\tsUD7K N.b ."(.AZWiǡ=rof@a{ .yvj(h+uœ>ʣ$ɰ$Kr7{$t=ɯL'Z2FmAB,֫m} >:p?up\t Zqo+Ek&Lk\zAF&|BQЪƝ}/-Z[612n,%O2璼ܭ$w'YˑRF4 fritzing-app-0.9.3b/resources/images/splash/splash_progressbar.png000066400000000000000000000001701272363601300254620ustar00rootroot00000000000000PNG  IHDRQPLTE9&5f:bKGD- pHYs   IDATc` # IENDB`fritzing-app-0.9.3b/resources/images/splash/splash_screen_start-alpha.png000066400000000000000000000077671272363601300267330ustar00rootroot00000000000000PNG  IHDR1ƚAPLTE2"eY뙑rg?0范L>XKuĿb/ KV m>0߰j`xM@yp[P|XN]VSJ |tI:5Kµ]]\I71d5'HHGJ)lA4phǵTIEvvuYMҁylC>0(*:4EQeK_DXw4Ir/C=PG\{.A,4/*'B]qxBZ5NǡШk}ϮPe׼ĺ-&h3".*"O#q#-!~#0 *,1!./ 8IibKGD ٥dIDATxyc- IH ۑ8Mݞmnv$v}|P 7 N_007eÐt4Qh;ΛKKbۢ0D,3qGMGLg>>eD3؈@1I̝4D#q5d*Dk:deS8spS Uٝڴ6A!SMߓ)i=XORUGӫ6^jzxdp>`WR)"bFud-:OhCTK1Ӣ&?^g\/ B.Ӌ脙ECL?h7f\KIb^=Za'*_<Ә954031iYtͫӘӸsZC0t䘩Ły28+ O~2 SxÛxÓ81r+%vM1T 6.Z 1xm9!B7f0Q?2fhHEN{CǺVbtIХXMxf,KqcɘN878( J:r]Y @R%1 lDHЬ,8c&5 {yJjthL ]kQ.-s2B~(U9'%i8zNOM0i}?agzҵ,}7) LƘ)썱XrX1E1ۡ8zl.#!6`V1*bKl# CWIcLSXfLqa`v1%1ۡ[Ɣac;E)%L,]2fRJfFaLmBv[# Cb1aoCFNaLH(֍.Ѻ%obM(J>qtaRw$N6 ([&Jþh T6v"1;Z.=c6RTY?%L~r{eo4nL+K1 Kc/V61-ooO UFF\A=-쎕aKq3Ƨ%e/NsAǦ0$HJ2(hwƨvm!vla東O/%Qn(G_*f"7S7iqanh532a$K#*a̦rI7W53 mLZBϒ'7F'{<}8GɿkYr*$uZsO"gZ!MV/|aOOW˜_z_>MbQnsJ/ZC`v9&U,y2/жe ژ7#j#5WNHxȘilڛUKcJ>XaΕK*kQژ_2\3zgoinC 1M%l~3Fl \*ǐ^[* Ӑ1fHhwY]%;I)#G2:I@]z16E@y C!?4Q5|l_2F806cͽVJRX~VO!MvM8Kch"2FT$JfO/ycl-cUAo>7if[ADRc*io׌ 7iy[yGkcDr2 Z .DЩ3  %h1vݡp05~M@s%h1a oqBZQd8TCi:h"1@uY?ŧYe_o+!~ΉwuL* P捡#-ta ycã`TOycFe ô 1Wk_ ?DEbHyKU چMƽ"/^{r65&d>Ptc.3tdX:٘WQ}* fVz1wietqUCw10%tn`L!f̋mX=1`L(ޅR6}L&jI=0]_ wo7'4cIn7a+[Ƽx`lǼIiKJ_:isn`LsA36 6Tc.yN3g `LsC+c%3L&L6Ė-l 70{%cNat1o2?l fc ;0soă1ƴj. CӦҹyc=j҂S`v`v`v`P7lA)Kӷ Jc0c0׿ߛePn-R`v`v?uߦoc0c0c0c0c0c0c0cf NcHIENDB`fritzing-app-0.9.3b/resources/images/splash/splash_screen_start.png000066400000000000000000000077671272363601300256500ustar00rootroot00000000000000PNG  IHDR1ƚAPLTE2"eY뙑rg?0范L>XKuĿb/ KV m>0߰j`xM@yp[P|XN]VSJ |tI:5Kµ]]\I71d5'HHGJ)lA4phǵTIEvvuYMҁylC>0(*:4EQeK_DXw4Ir/C=PG\{.A,4/*'B]qxBZ5NǡШk}ϮPe׼ĺ-&h3".*"O#q#-!~#0 *,1!./ 8IibKGD ٥dIDATxyc- IH ۑ8Mݞmnv$v}|P 7 N_007eÐt4Qh;ΛKKbۢ0D,3qGMGLg>>eD3؈@1I̝4D#q5d*Dk:deS8spS Uٝڴ6A!SMߓ)i=XORUGӫ6^jzxdp>`WR)"bFud-:OhCTK1Ӣ&?^g\/ B.Ӌ脙ECL?h7f\KIb^=Za'*_<Ә954031iYtͫӘӸsZC0t䘩Ły28+ O~2 SxÛxÓ81r+%vM1T 6.Z 1xm9!B7f0Q?2fhHEN{CǺVbtIХXMxf,KqcɘN878( J:r]Y @R%1 lDHЬ,8c&5 {yJjthL ]kQ.-s2B~(U9'%i8zNOM0i}?agzҵ,}7) LƘ)썱XrX1E1ۡ8zl.#!6`V1*bKl# CWIcLSXfLqa`v1%1ۡ[Ɣac;E)%L,]2fRJfFaLmBv[# Cb1aoCFNaLH(֍.Ѻ%obM(J>qtaRw$N6 ([&Jþh T6v"1;Z.=c6RTY?%L~r{eo4nL+K1 Kc/V61-ooO UFF\A=-쎕aKq3Ƨ%e/NsAǦ0$HJ2(hwƨvm!vla東O/%Qn(G_*f"7S7iqanh532a$K#*a̦rI7W53 mLZBϒ'7F'{<}8GɿkYr*$uZsO"gZ!MV/|aOOW˜_z_>MbQnsJ/ZC`v9&U,y2/жe ژ7#j#5WNHxȘilڛUKcJ>XaΕK*kQژ_2\3zgoinC 1M%l~3Fl \*ǐ^[* Ӑ1fHhwY]%;I)#G2:I@]z16E@y C!?4Q5|l_2F806cͽVJRX~VO!MvM8Kch"2FT$JfO/ycl-cUAo>7if[ADRc*io׌ 7iy[yGkcDr2 Z .DЩ3  %h1vݡp05~M@s%h1a oqBZQd8TCi:h"1@uY?ŧYe_o+!~ΉwuL* P捡#-ta ycã`TOycFe ô 1Wk_ ?DEbHyKU چMƽ"/^{r65&d>Ptc.3tdX:٘WQ}* fVz1wietqUCw10%tn`L!f̋mX=1`L(ޅR6}L&jI=0]_ wo7'4cIn7a+[Ƽx`lǼIiKJ_:isn`LsA36 6Tc.yN3g `LsC+c%3L&L6Ė-l 70{%cNat1o2?l fc ;0soă1ƴj. CӦҹyc=j҂S`v`v`v`P7lA)Kӷ Jc0c0׿ߛePn-R`v`v?uߦoc0c0c0c0c0c0c0cf NcHIENDB`fritzing-app-0.9.3b/resources/images/splitterHandle.png000066400000000000000000000001771272363601300232560ustar00rootroot00000000000000PNG  IHDRVbKGD̿ pHYs  #IDAT-A #QhF͋[icZ@9k IENDB`fritzing-app-0.9.3b/resources/images/waiting_circle_small.png000066400000000000000000000005221272363601300244410ustar00rootroot00000000000000PNG  IHDR ^v^YbKGD̿ pHYs  IDATM=N@FH ^`IFWi(hIn7X 5rf~>P!V t3-SDP.W3bc 4W["/\?o[{9D7%S^S у =,S.W˒ϑ˩I AVTdQXpAN%rRqD/7@}axCVtH|IENDB`fritzing-app-0.9.3b/resources/images/warning_mark_mini.png000066400000000000000000000006441272363601300237660ustar00rootroot00000000000000PNG  IHDR AsPLTEQM$#V~~??~~?<~yOH'tRNS_߿?ߟo/OO?oobKGD4 pHYs  uIDATMBPՑ)Tm4"[{г}?f tM3Nj1BC\%2]ko\^Q. noh0YLgvWeJUx@֪Fxޟog "* u)IENDB`fritzing-app-0.9.3b/resources/images/warning_mark_small.png000066400000000000000000000006641272363601300241440ustar00rootroot00000000000000PNG  IHDR5o#PLTEqxST$-+W.~NOOLʛm"tRNS/_?_?_o//_¶bKGD5޶k pHYs  }IDATUY0DFqDD%ZI'CGWKz}2I24d$wa269ݸf%hB%P.7bX="/T3}hV}j:~ND76IENDB`fritzing-app-0.9.3b/resources/images/watermark_fritzing_outline.svg000066400000000000000000000216641272363601300257630ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/images/welcome_kit.png000066400000000000000000004223761272363601300226070ustar00rootroot00000000000000PNG  IHDRfbKGDIDATxymuZ{{M=h4f@pHJDRRŔbET)ɪ\$NTQR+4QvdQ4D)814o7{ϰ/opV5g>k BŗF<dž:*L@FDW $ݣ+$?G*@@RD3 PE D@EDDP"" G&S5MD0y @DY!D0f& R""op3^J"UKUAUPO% * RH!"B2|W%12 4Q%]|Iŵk> \X #)BD0 cDE CUUn) I' `HY A|(@ JMI# P( @Lt$-A*\EL=] U$@̴PDiCU@@>'l= D[($3 ezR6USZU3 frj^+1UTPEl33}#]R.~qű> E4+8c $a ͦ"LVm2u%mv2O7#2ݰ%Ǭ\ԬR3ّLx,$3[Dh7GVyJ}55cvtWY̗"d5j@8EE}.bqKrDJ)DDmϲ$Lset0#M *05bWb_8nݺ}ҥ˗/ߺuŗ/7]q3Y%eeԙQDAb9U͎ b簔$<0 ^ q:z!c8z lr.`XJOEьO  j̿X+Y.d|ݎZRZZs Ʉ{)FT% HJ5A0H3C. Ibn8]vW/]vjVUY9jYH0` &Ƒ}׍`5sfE5ZR4v]uD"fж^bZULMMS*#","b+0`%J]Q(a.RVL"ƑQL!&" YrzgkBQAPD8MsƩ|Ck"9˹]QPb OIMI.bp|||勗.۷(f9zĬܝ@(zg`h_)ZV6zш`5TD}#Æl<$Udl6r[#Fu; ^=XZ1 #VqkmUنD m1!DCa5̔(0R[n'r项`"3RȢЊg".K7/_~WvmYPXD g><9VU6H Lͺn1+BBE"H vjHak:Zqp9 F2=@X1U)V `)B5@5_.K%NN /tkWZ:َE40h2f8VEMDmXLs 5D$KbmE `r^ֶ9*Sa.@ 3&"(!&PmPzD% -+)dԀUVf@@`fyDTY'&DL tE#bW@$;+qr3UEǨ:n6*5^LMw.d/Xzl<\D̊{LYoC7U ϟܚ@@ը )g"EQ+]#@BGIN_){I:@B а@6 E6%$ 4wjU@D9 S#Cm15@Lt9CHYXSbd+~79EM K)o[oֹPuQ֛f^֧Ϝ%]|r2ExYD.&{l3OYTW(6F:PRE' nƱe;)@ @W{TB-)f$E%dHʵ1GɅdPMsT7%y-]D]E д.i;1 {FBH )!YThtwBEjTNM1ZXk^UF`IL%K1,ﺢ ͛/XW29IFb2D\h@28z5Ѯy" i)(J1uiLg?g-w&#jvsJ%4$pDp"IWlښJxdhHi& g4 /B#8RHuN "EWܐ+JQ%$+$}rv%$o>Jlivl^%@bNI(%{abI$zC&fDr²HBh8ĤΈ|*F'}\H~eXW̬$DTUhjJfmbY)/ج7Qsmɻhfdxpo- 2NX鶏j(Δ7 @rZ VQEP=DT㒙l[Nl"[!'HS";+ MXJIfD5 Mj?ȇIfGA0r-ِ4X`ګhޤ"U Z+l،s_^aR  XM 5G=H-*:zMڢVED*S@m|az9w@v&ҕ58S\Z5+6ړHY"Q"d *$< .hZMȝf2RrR ꮔF%a'] oY%ɝ˄+EH @n#o I $~lſX׉ʉ >{lbsfPf ɱ֚b(jbVJF@M 59?HZ*: 2Sȶ&A!Su֩"t&w!m9IwZkg 4<:7ÍEլ`|)(!႘DZ!S3KH| .]TKJ IX䣨H о/X4u҉94I[dU2x-*R@ RkM [=`$L%I )' &Hd")D1I!i dɦ6./ @cS$ylm ЬmmƠࢲHx8`uKRPtCRn7(SHQm_:R6 HS"4ս($@w]W̼]/XW"0S+DˋzqG)КD+eVAI `cB3{Gd$.-B*ڗȊ$F@"dPHe `T%J Z/Ѽ`L}Hxn4UHU`2)MZ=~TrDzl'V& XdL&zYs!۫# M:Q=dΎG;D'dޯ/yHM"OD.+fe[~&:ӧ7Fy'BaYs V"Knϙ="#EwxZfpѲ8uJ$&b"0"$[А` ʶ!eD>IW5&LgR"ŪA@qL mF$y!WHZn>sJl嶥\pZ= hI͒kp |-R|nf]RŗDz*j"bӖM" TR-"SPXIŢ U'IzXI*ͬ2R?;4|1+/]f:z%2-T4jS#ZnMu\n%:2Lۢ-1JLqh4/&8BSU43@[%U4("ng6̒Z+bO1 =ٕR݃%]Qkx Z:t E\;ICMZYFK-TbY7܍PGj. fgg{O~Ə W{RJ7 /ٿߕS SlFN2 .TMT'ZL~dG Ly?qE "+Tj@TJi$o RF)=<_^#DR#;-kwE.l6왳mu.mey&ؙ3gF:T5!oEf€GvVm:FYy8TZ=Ͻz{5{oٳbg^ig٩^MW˫rj*Ńѫ40m6).$E!cx%-!zdr$!L#R4dsX5',r Fxf`9GkZY' ڧBjw'+-jonIK f!m|Ӧ(tJf0Q-4KsU&GE";hZT`W:}NA'O<8_cy/?Guӏ8zWG?yݹ >Xo6SW^ۛĸ橃հ6r @jYͨzq0 X_Hf-o0Q QM3L(Hg@T 0)h!!m4yhD3N>ոãZؑRFE;pw]RŽ'''Q=Ҥe1iݐ vOPZ ^`MK_iCgnu:iT=|̥ɞKEJ3g}[_oK/m>ޯ;wsX|Og?/ ۾M;}2g?W\yo6"Դ?ϼ'_+{oW7/^,wgf`u3;zXoވ0Tt|TmLmKԴnTO3KZm{Њ5WDVsCanY?cLm_^k3pjCIvpޅ힩YXbffE FY1>4LDdr&bDTS'__|/vt?#_bw>({G?߫Nw>;W=> {]n֋J)ٿy?N? 7|g~Ǐ|O /Ozᙓ_[+G?x݋9Rϴ{Vȳpf3 ϵ׮ *UX_H3\][2LFPm{ioxV ȬXUbn/j^w< K<f :8Ueq6:h@ Fs$ޛ3@̅i\.voy>b9,5F,Cruŋ{}͓W>ޓO\n|7_Գ?vzy/o#?[p/؏<[_|Ӧg>n mϜwzwX_uS<8?cw~';}ɍ_?_w^VG|)6N/n7Ltw8C܊"m]%rf*ި@XLF :e:4&Q:=qf=_+w+]'{t`Q`kC1 Bxryp46V$+ekvvV8`y7e1{u7u\^{N|G7\zw|wg?)y+/UN9>_O|]?#z~񁏾߸+㵓|zl?S<:>yyox<賧~o?<{|?^O]<w2D˖Z0ihи)n so=G?g΍'αV{ ~|;_Ѕg? ~s?ʿٻvO,7o`>f>GJ]O~;o?/?tjazr\?Ё]ܿ\mbwR7x\Aa|M޽&9)䛃4 1FBTҐ໾K)sx O|buD1$/]&h*t5??'>Wfxl3$9Dޛr'^_xs?Ve]8{YL}G#(O_x|߿#sͯ'䓟Zy?U#?׿wٕOǞG04JHCrJeRǜc3m־h E;.89YV3jՉC0LUDAX.?S XBWmnx,jjm"l:6HXHhpDrte{Wr՗z 7_?cRL`A)ejMqj &7uIBm{Ϋn.vEz{pkWlb]IM »B G޳(ņa}h9DtQEgSI慌Ư'ijj{FqRGuē, JP3Ӏ3KH5夡!bA"&/B7n܌ݼ7`X)b&W.cܼήߒ[Q?gYuŤrq;ȷ.+$۲XKj@SL.X/:TK)2%zRiGD[Y1@fywd)D*"O938L]ɄRÃAH1`JZW{:$l~BIu˭=sԤf+J =H4g&@[HgE&xAT+8;UDc&jŦ?&aZ/J{?k THJVM Q RvQJP.8>9!D%nybjFUSY;Ξcնbv|S^N62U*M#IPH]JpUlǑ,_HT&$C:=QJMd#=u+CDY%NΚm AYZ,L$ƍOK͸Z&n[ Q޷ޑjEe#ٟTgo]WǑձ^w4]ڬ~#DK.Lzm«:t(.)Zjk͂ `y4$雧OJ:4:/pl^`'5Y8 B1Ua*#Z`fw$IƬlR+#XI% =4語FEMvϛ J0/s$ ><8Ys{)f,xezܾq$P&a r+{0O1o9Q+f4v*#գ+efש0..n">6%q ܥ&$YuP@LɌTN3}aZ3\6K (V\R@(̆#3SBj>1n-F6Vj8ϮkK/|X1q;. ܬ2of&JқOJz '99sL:j7^xRO.=]XG˶+dUw%H.2Vd"k*Q2_UU WyIY%Á;#`p2Vy iT0ͤUj ĬS"L8%-Sl["fE[&>tXҩjcUjcͰI/H#̀VLXv@J0>M?a3LB3ETWΞ QڮSIY"4I"dlY=ѦCDڲ`~[QXʌ$=%^9"fʐ_($Aj 8V&"M}e|}TxR"\D9bju/W3.XFшV Qc}On{- Ha*,&8& (57&@E D|TAP4 T Li̬FL,hevUR-3#r^#R6 $bB5*HIL"*er5䃶>Y Q1ꮢw礭=oYzD[dK2H- ])]) %#er Sf,j$(d4h۷郳/,o{+/_+ElYgbor^ 2{5jZ6lTI.65v?^*ܛelf^YQdZ"U^/<_D̻9bٜz⩟MQͭۦ8 'FAR'YSMBPl*Pplb9f]RŽͺ) 6MJZj0rۆUSfZGOUpwUM`1'UQT$ƚvQݾvA&yo~?S.F¨Z@=x3r4[lrFk@Gt]WB8:n7_h̻g( se QKdM9$>7,ՔkTpfB!DYEc(Zjw ~gŹvL™j"6TPBdXo{ U<|Iu%]q&YO˜MPizQyV;6$c\&ORʻR(;sa}8ݼv΀~r׼{ _Nןc2֚:@::.ݼt3Xl1flV8|Svxptj͎^ o|fcǒJU'H]/x .YykH( @O<2L$yb9 ,Izu3."Xώhzj|6rorz0nv8]ӑ[Q^#g;A C4Qo::ODlӱ&pSIܮhx%7fs3:-ٮVF$tZə7>d{ G na\WX;胯׏Ǹqz፯[GD:Yo}g_~ϗk{_ab}͵/ҽnJ쫅g6 EQ'~`,[ļ`s8⠢'$̌Al&U|I$%$I EU@OȰ]Wһ`\oJ);]ӱlL=M4ƒI0l4*jJ8٢ZC0-Ejvj !aIP7AL) #*I ,pٙSU ی0JםXRN5_sn3 d9};Ō* 7''>pq8j]ZOobqj\{|ʑ qv/9{ O߾OϾ6L̫(U`@MhݚTS^j~xm X~#j$FӀZfZk㻤{6q<::6:r(mu #l+5Y!J"J <:UZSCUOV}uRG1B:k:" jdzk""*DBE0/gPU{ds?XƱ[,6 pTx^w6d`X ϾÇgO3W_yߴnn|QC_ENET5A(̴ 0D4Nbw #we"&P w/j, jA:vq0 Xj"*;ĸbf<"ҙy,,YB`\El֑HF'HaD2$F&BMHR\󹪄;ܿ9>n?Rw"py!?5[-&⥲<^0>3?79ǷI×ԙ~D"XL#F`Y"ŊGDЃHٜ@XELֈN-0{e)[=@ŪhdiQݍP+Vl]qJ*$ /S2 #(,( BFbrKdT(jn£KLZ7(36ԃroI6$AEwQ5Bfb]{4"bFj)QR͈YĘR"/{{ wQJ Z"<) jWUgd Dٰ2DhV,*J8T&lqz95~?ۓۢp I1NAb˝$se@P$R$00IJrQ1:Bye)=|%*hx3B,34K舤5@իt`Ҍp<|'={64p*Slr (c C}%UD` C~XG."T$PAF:VI@ߨhf'_<7qv賗道=/֭c Лn9^ofReGzv]:cIg5M!Yjf\֛o%f\s0Q/ R8HV$;y٣Z4rG4I;d.8GcZ,׍Vp]DP3Uqj9RJ?7!acr."X]169)&D򥩷 =z79{zSYo=Vzr^y,V/ɉq\f!Ʃ O?,u!Z6vfUٗe[1;=`R6z!oوjbt)dEbjf&Li)ِh*4h!!bxd&KG1K7jiCLܫ5 _=6ԱT͓RJ:c9a"ZKY &Q3+Ek3B`է2,j`tm{`doA(u7rם=-> + aݫG|QEOE*tq Hώ"vt|ɗi=Z$z>zU=w,f>zWk| [ hNU[s`e5 fʥ2Y3RJ+KݘjAON}#b:T6_SM߼dRk5ҥTVw'{7NNkuUI| i47xPcb>_gfUndoEC)Zj6h*tM0U8 sGg}uws㲞>8" 0blz:Pޣ:"p7U \-}.'k;8UΟ^yy~*;]}{qp/XT BT,W+#Rњ Lj&KחIn (hwP'|{Eӣ|5+&']RŽjkc$Nv҅Kf*jjSQo#P#TŌL)>Cf3Sө\Ԓ2 Tilv/SQRazL7DAAg˓[2{tv|X|S Ty_O5~^=[.>纅%CIMO4Ҏ!z#k5[@kFxSDs 6H_d!_- vqI:LEѼw)Oq׼fXϜ>>VX B ғ*VT F^;R[sLg[ xmk""5BDfS၃wܼnqv?4"^L[=^}vG^Ϟ;^0y IW䓲Q_-ReO 4iKC@Df]⒜zQ4MLmh"M73D%'c f@Z]M&)*tFahmgkԀLYJG151U6eAJ2Ee.pga2U*қU @l*ނF{wcD+ &k_KwM2 @[GU0Ys 11Ck][*#PpRiԱ Dkj.ddi$"c}$+DIi}V~*2=^XG}WlS!(=sf= bk"[{$5ij :f +]SDB@#sX굊wAuD.{U8vn>4h< |Rt[r$X'ㄳY[$ r6ixH CU?tR͢:y➌aqPm8YU P!"0FU N}.?A"AIB$h'ۼHMt$Vff݀BTVԧO(T𨥔"ST'ډ!01inY478x/urr}v}?:ȅst`m{T7!$員SIj&TͳJ#=)&~66I`h y ]pI (IqORұ{3NVa5J)VYvs/5cWJ֚0[=:bt/j֧LnbD" JI@] II"P+IӴg1+I$\AlS'ɻduUE-} zDUd!(J > J=7bU2j&z{)Of*"[j2S}`iN\˄F]J|DՂ.BJmbVkHDu$EB騖J8 h6`M%{e*HsbD1Y4HrzhHexؐ X11Ӵ 4/Y^Q̂4D'S-mQm2FzPPc=%UTVE'?$E-YYRhE MV0BԚHp].5CEB=4#69w3wƬݺةt^[gd4,Jכhvz@3" [\dɦU75F"bd.-55TA 0";6BWF5M\i[z$UXa5 9CMD5B%/L@1$Bk%鳥BGTp [F@<'H] HX9IV]) ,}aPuGŽwћ" QaPPN{Ϧ KשS~GLF@,r:It,B褉#rжpҪ$tj5wt( T`LD,ǴQU0R5B=_J)B&%.AԜU*t~&+4ZMLf:8f\bUʗ"+*DJB^X/a0!%}~^j!*hBU%,5UI@n%yr7d znPD"A ԺUL1gT)"ŘYrrMd;R'{e&{*w]ˍ(+.F\Ԧh!xU@+'}qFeM[͙u̙9bOzF0"l$2&57"hh)Ƭ'䇪LVf6 cLREmc2d}vqƵk77{NEP)e{!)BU=(DtDVthd$CD I6֧%`Q-j$+]L*NQuOzxȗ?(EpzCOA7(] 5MaD@ԴkU״S@ 3{eL(ܩ -浪Ji4/SC͘h(~TάNhY6Ń)sk6QTWO`~_]xz2$1b;;͔N5c|JSRcCƊ(`$j9 вd%KڸA<xx<}xG:b WΜO9ap'?uxlnz啛~O-ڗ:,fUup V}!Z6l=zRhAxBɦuiڏ[VEcnKqϻe1ͫHωXF£*0=&SLnyvq/f3~?4EqL 9S$jXoaQ3Ew*<ҽxm*5Hَṵ 8q$^!b(ɮtDŽ>_[@PON֯^9~}_?rzѥK P:1<3n<:qrSK^^Nz!]4-U z%Yd$9e""kK膪Zo }joUu^{vB Iܒ.GSg}yT=lyuNO@:%&"͇MCȄ NA&ϐDi]Iܺ倫׆q֐JfY52޼dѕ׮u5_~ޅ ò.;/fjX^x;@6윗ն6xܙ"R}R ҽݑ'/4ρֆk{KiLagD}~hj+<|vqk7~GڴcjU \VO|}ﵫ3 3ݒ"D'yrS@>s[d$w7kڜII/2rZ5TU<(2}EGٛPT[KTuq߹+{ϼg._wlܓ,6IlN)əj)$PH`4FUbJy?DM= ùC6F` X1!FwIm~?_>\J_,'$G9>:Y>u_kWs3t   UܩcZQ$vZn 4ʯɖSAB`8[(C ]*Py5`! Ĭ4mq1}W4>3MX7b2~1S46Xc0SPY, qgаXKy̙.fMJ}ܸ}MO?qmU,pno#Ľũk>#*:x4͆5W ;6[Ϊ|KJab ?]Z)f"wTw(*i eӹ͔+dO8 娾4]^UE1n RpM@6 l4Ӷ>>::9Yz䙬AfۭzZ H"GpeKai'[9!IQcGC ٦tmgk|<-8k)m_iu5 0N@IB548E_9JSHb˂ʮEEUۜ1;ldoFzS rZڈ6wbZ詻Jh_;LniUc~D?S*Haڋk %?Q XW5Z65N-x+~O~gkz̋dR-SDӃ%3LlddOi )7IHhiRGԈ$4hXW8m+<em4_ "$YVܮ}ႄJg<MӍXUrÙӝ _+'ui)5o.;{x4OJ]Rw"-43u9Yk0|CRHQUv1D[M8WЃdpʈ UuwRlkz|*9󾠿ImJ??g^y`EuVg;Nh&j-9O2ti6V&G P@"Ӥ_6`[r{k'$)BUkɖe2'+fXeL/QJϘxnRC Rݧ?1pq9'"Z]8{{"&0cR8]oJ;hS4lQc|rjzGu1l˴3 kgP6b봔\o`Zme 1֛e֗U20`N#_=̤/fiu}oEg}}_̬+TL_S4U Mi59)8 Zhu(Dh D)HNf5d#Tέ4HZ#e #qU DLM`inp!H@YXەh 7=Il kN76.D➅3%@,Ez/ڿؖcPRQ)ODAD \P"TΝt"[5<%P&;|tcsD܆) jm_4Vu])JPf}ߩJ1麾\R-43 Hp*Ym5LmPs+-Ӄ E`P-QcbGL㼸h,t2`sYmTztZn@ĝf-"Up'sj^#sR因M)DTY4'ߊ/.S4;4>!,{~sE0;PB@>s.i6}Aͣ)i+ӓi7ALIyR 'kL O>Y?~|tҥWlz+N0`9SR1KPqFR'(QsC~Ԝ$N|G`ЋESʧOnHm&-5{MFƶٰCEdWR$1_٩%wµ2'wh=w13a,<&Ll&sh7fe7UdǗnrs˽G^>'z3_ݝ2t,Rߖ͈Vr؜I,y!\ta* h+Sj+Z&ϭ>˯|և[oxfq#0뻮Tl֋jD-A,. cS)]:uFB0zb j,at]_/}uwu[tt]WbfSDc"hje$a2:F"ڇ >Pw/ӷܦf)} ZCG$zSFi\Ь%O>4k>*躮͆=j$}r!c(bde$Ԥ\GhgW>uƯ8?G-.`O*^o!]pjhd ͮ5jH5,Kj%X@^'d5Еȝ*ab0V%>u1AvĿNյqɭY׭ߺy=-ϼ_%\.O[NN7=kufŊvYQXPTU5q+8xuzDkT"kgbֈ82-:$LlywboZVcɲ7 7@: BluS5ŔJFri*4K;jn̛&MdIc=<ݹ;&)iO%'s !wvl.5E/:w[˫׭zuԷ|9UcOxQ#trofWz&tKl:Q{vڑƭZ0 U뺈ysgL.ӈ a f~Adj:@NY{gzMR%x?~tn߼~Ɵ oy~|-řӧN{ɳYAd\:Z,O+e}ߩlf(3"ZCFcsTKY:Be6mKWbfue6ەK% +TwI=܃MTD,¾ZB2{6BBW3sw" sd+0"IKe+%1AE@d%8!vWwOx^;9|a5,;w#eo4| !t1,Iҩ%׋K6 /|b/P=_rޫn?{xPkf^7M~5Μ:YjMo Eȯgu 4CJg^ÃY)}WzU~S+~BQ=99yW_+ð\Y./^z @/?ynn]~\,ut:50ֱ}H|MiTZZ+H5,if"!&@gY_bul^,TN$J)G'faY^c8$i Ƒym DLLuN5$1Y-p1k{QwVb2HJIpDTvo92i%}o|#= v2kQ4atn˯z߭@?`l*_⑻*51w2u:VnDd}@ϜW+'խ'ҕyff~L4(zѴ&얌@2=mSBzݛzZ]7{}iZ}7f^ZJ'OS/8=8+3,fu"kryt|W`6_٩<;/{W[o>vttt+v栘ǨHHs"'""X*t$hfɱj`]߫@Ug."l +j"芴]Xi\$dRln[byn`e&ЙrP5 2A"B;NcJIA~wTuդ4Mv݋a~ '!!uy߆EԮ+:XvD7 0 /@8@8քh3Ă6m-k;Rf\GS;^nޕ糹*e/G~v^yq0zX9wnRz,Gd4^a fiP5h!fku~~l#p\^r?~G7n\zGtcW:5y=q׮ڌCח/swc?h{>| UoE &̀hQݑ5:@*«;\8!V똫Rƈ])I2Ӯ lb'9uŬ5i$fb`DCer=NuFYĭ a8dfI)#` D$A]|_ ΐi5 ͤwdGeGreşY,wmuXk&DU:+}r{p/v7rތW+&SCe}2.Wǵj}gW:nn׺>.f3y7gh\:QeL/HIF 3l ^\ y׿M}OO޺uuʥϩ ǮUNPs<30^~{wo~r2JWfW..sՃ:R5'QYPD/L4ѫ AZ. Sc"ف"cMnsW27uj%jźRżgb6KMp5B!BxB+Ctq>HV|7PJĽ&4ϫרFHL3:S/w@;wEû4_޶)Dr'Qz@t Ӽ#: - ٹb پ-j.5{jRbNjZ5LRJRD`׮\of]7tDy&3UJP+Ɂڿ~ c6џu|0oW/=O n޺uϫ@clNzQ\g~e_u|fٌ_.潈.˫Wc?Qy{=#HqDDjz G?}_֓7.SO>~^Pr^}"Qڦ$ AGNՅiLPN9"yMUu1fUI B ZVT%+pM| F1x\vf]U5bϺu]6%٬dw(P1B>45Subn`Yzj0b^N&{ի.v ?ǻFE^ݾqʍׯXo*έl&7/_uo/]G٩[G."᩽5i[[LvlITmo^Nܬ+䭣eyrj>Eհ9Xk-R mciw QLNG 1իW{8y5իW/}KYa\|ғO=*ED# !m$A0Gw[?}ѯʯ #">ïwŻ^?yPlKQN"XzŌ.U!])WQ+AWEShf*%-D8tx*Ӭ$OM49:T XEcku?Q"*(f̴+Y[QE&`X)i*j`3I-P 0ǫ0bX\gvI 'ϋ_knSض~_}r}%CΜno_O>=q }b~^jeXXz%ߨN/>ɉB9s^H=B"5z@ a5 ի糾+e1 jҩ~?afLqP7Nz^mҴ _tnxBnݸZ }8~W>0u =ʶfs]@HT(!~.KW>w9x|>+ݸum6ʍ7׽‹~q6֫bE貿/A廒R3BLLDeu&RC5!{"P RUHㆋL†""$79奙zVBmlwlre]gͤ/}N7޼KVMuQGBEz"[bJ< aT:}/=.}_hJzsnA9,̀˗^v|h8|j?ZN/WGŷ?ȩ}.}dD\ï~5t++4MPȦh0N핃}ٌfJܸ9wxl@qj -͓|QLbڎU |Y~z3[6׮w_8Y祋xSc],׮]??8@ߜ}s}+>˯^}o)GKZ Ν;淾֭쟾g\{UZ7g^f U0P0-$PJ Q'C`W:*FTɺ D,լi禲P5j*JEg04}-ub5X/<>K~:*V'N+{{7֣'o?܋Qf '/_z~ʐN㺘(n C8롪1 &U1Z!\eHV ".QMЊpG*q t !{DdT+Sh;̲:ᄛ+9V+" C0mǟxی11HB( jnswus8iセ?νՒy?;O?TUߪ:}޽»9c`H7u HXך"I6Jw8ǸFfI8eIDZ-K"I.Uy[7oB$?0ttݵ|iy֡I`<Dp`cei'bq`\mYzlfh-oy?~k;vlGw?ph__F?Ȧ[w}7gu+B"3~u9p&IDXU֦IaC(b&an&jˤ (N )7 c CDft*=cfl6ZCGl&/u{;DyBs+.D"N FggC4'gNMRqC߉K7xpi!H;vP42X_| o]_fZ+mB2U" G$EJPtV%ڨRFpHky3dG$)b>\Adl.cU8XTbj~넑k#²%CBJ) cŜV3Дe ɐ&;-3ΐh4U}Kx̵-&%9rb.@sdAqjFMȖ%,K=z έc5+h ^fJA@G&ZO{fźnC_Z.yG:G24ԝΤ:O_g~>ws]^zc~]w_'8FÍF)yulfq9 5M^SVUlsi$Ih:3cĶ/LƒJXLM$N1s4pAH,d[7 U<Ï2qt'.ImzpfDT\:;xOQ@c0Fva_/uphM(EPH01bDadM@!g"6EH)bb*Ų(q 1Қee1 P<2cm) `q)e|F58!q,K!,d( -EBJ 1}G2M-r`R j wn-J[֏7P+{-.$\-lN2l2L$k +aIKrwaҔXU<dK3Ճ#gu?kAYVN6eCi.ntO7 {v>?{j}/? ar=~zS߼mءǞy훮m璳f_+FՂ Z 15aZi 51GPU5'\tKg?Lu eͱU֤Y"@$ #~R~qRd6!̍mo=9u戵1:$128k˖8 זBiwpsG $Uyr2='}镜hO͜7u'O0HaBHET@z828 $CkjP,n"HkYBɹ&PC `"CZGhUQcenƑ1 rx{7lL+ZRP*QT"9",\`lFŚzBh4pU9G✩H "}p&ʶ^ax|sl$IԨQek3Ye@\K&D*!]'2+]Mɴ_;,g`7߼o-Lfk#Ι[Kg9Zqdܶ$&8#{}w_y_:O}n{߶m֭Ǿ˒o>3\hh21Ö׺tZc-9qV)6{k4՚:XjM>`OciE>d[G;1^.toy4 0`i=<2Ҟm]uLjkƽ٣#vWirӇylTbHP+sAw}njG UHa*?J}/T)Χ"NV~'SɄ+dBuMds]nGKEKl.kꞙ[Y_tٕR`LpɒR"j \b)r2F @HȚTmHaEoukkIfsI7iI)$aAEJR40 ^=Hgs.-JY 2@ Ęh1Tuà SOzұHdۜ@  " W"PYvL~@& x@ -R" |#=+5?3?_vѮ $S뉎즍\dʟIl a~n%7;C Tጕ|˭nݺmZKO=+~o]tǟxfO>RHD|Vc~'NRaH[Bs(6&ٚ>=l&Lk~kCZ0!O#ǒ-T[*4ES1f$# +"0L} 3i0hrfBri)W뚫/y'WʎeiӢi¾~i[D2I2Ͻ~̉wݸeq;˻rɤ=YNWt xݵGo9p4UA+  2QkXbbYǕjʒ ϯ `^.e^^YXXcɩ۷oȨ@iтKR1EBZ1bD u l #&"511UX)Ɨ~ Yuu:RSl'3 2&"1R /h4|Z $ DZ c9c \psZH)>R;U\~t쑙\B:Ng cIiHŐq&%b),$,KKmFqY-.,:(jBd-\5"h p 'do"^i_}|煽\.n5/6(J{C@w;tPJk/}kW\y;t0:&9ݽcoڴ>'x6gsjU9QߢUۦV`-iX-5lEpZ?^IDAT2V;-#Ӕ]fr gZGZ rDX^ZDn|Mc/8d[Ҡ1F(3 UĤ7hYng{q]׾ M%z2ꊺz豄f pLhB!N]7eizџ<~%cJ%"zNnz:|µ+o+F*(Fa1/eE% ENVӈY^o+@HRRY@hR,fM"408*j F)tpoVYXFBOqKǿvמ \vG@oFHJRTW{-:ӻ?ܵs1ݶi\Ͱd|繢V1#d@Uiq]tK4,W$]*iOOx|+dž&יgjONZr'~ľ KrZcyp[ږK|;F P32dTB' զ:SW{v8.Mͭ6*&Rbߩ@sG{DX܇Z6Zqal T\q7ΌO2`5d%3|uAR~/G!`(a] bSxbDa־G6сaC n"RHȚ€(8 Ď(7w"@Aac-[u?u[ڰi˦o?/O`OC޳X,nX?R-@bWwoOG{2!;6ƹd5~Q/WJC˥ຳ/RezWPx /0GN%3F"M>(51 ~1 #02e<.:h6V: L$cR$6\2='euPu7%Ml@`ٻ޴;͟X7n&jzv}Ǝt1Z.nO}^ܼm}??='ᴵw5DsKK}]mժF}oࡱR\ RKY}exF 46[XT U:vnԪv2_*h-稦 G5պY6M9W0&}) d(ornz~ۮ-9!. ^}eO=^nY/fZh4'SDQl}m/Kǧbd`el$wR!@(V=R}~+ sO<8aA(0_*TQ˹rYOMl?]g{Ƶo'AV,Y;8D{{&T!h x Ί噓'K˳Kւv&QrId HF#X쏉LYDF!֍]O-Z*.h7/߼߼Ú|G_-ڗ_n猪ut_7pmK\[g TO͕ÐEJ ^U} rI7P"IqQ@F ! m*ZnԜ[dR:U)V-VsQkj${p{5 c`FX(A=6wޫGPk,_Vz'̾o扇ǧOxY˳3s 뇓 n醭[6./|@ <֗7\&f6!g%cK @?qӠ%[Ԛ3b/VK{Aڦ5bV*GI`1ĝVGYk+:Oԕ[~ǯWy֟73QG_oR1TygZO=JX&DCѐ\[! X `q8#l0Boc RИABOg>sG}}CG-mR,sm&/iw\~ٖs[[x=㓓&Ȑd@EaϮ3ƴ mrZB ˋU16,ۖpےJI\0( `Rr1Zx#Wsm75jc z#wݶ}3'{xo*-|JJh_}/;2_xyhtK5 ّo&~sOom;/.}en<0_0/']=@-1 șK1hBuW1ƄG mm8XĒ,W FS檊 釁j°f/aͱRT1}o⨬u)_6 >EH-= q&ꍆp3]Nb/_Z}o /y٧;~TbRrrfGUfWxnL}cl @<M^eF^DʀӪhM`thjM5X2X͌ɜ`k_l{ KM\kWj?Os5x띏}韞61T|UWؾcu jB&0"B G>' moܼASouYrw;{ظ -hHYBVOE%ղW+JȆahE E6HU-KZ-a a mW [ !ID`0RhržJPVo`s+ޝL ԧ c׼]q=BG兓Ɍ|훟-\pD~w ^n ]m +W"߷_Vn`/1W*"BT( q!-.lJ9B֤ B ׀a2m8 1B -ƥ:4@5m䙹nԟzG#7w-N_ͺݍtx#wpq؏zj꿻ꪾN^]jxuaӺ{ǖ*/+nSS/otwQ`oAWWvԩn7?G/+^y50$"Z^ Him*TK\֪V<'ZNJzZ#f"Xece\ EZ c5̦|s @~4ljIǂj*Cg?X:w5~Vv[l~;?qĆN1APd#WOV  p{o\d+e?m{{' 'n"=0?[{F>_~WNxZ_͟]_RX(MG w15=ў;zh-_u)tBFG`"Ն[l{;XBm4<j5e¶Ƕ--p-I^ffv֯WJ𦷿/p!_Ɲm饹iCȣOEI^{Û/۽/ԞuU/>6잧O3ɾ/ v>p.g|z.م g 4=@-_ t,qdD@lі윟zx֑V %DJ!24B%$h1@ri +k5лu*9:?@?l`n`haҟ?P*_<μ9@;Lsሼ{x9m}2Lzq/%kooGws>+<ȺD:[ʗ xJS{?r$oXw[I`{d=cȖ`H CcE` lӡCԩζLLd )1-*cMd[$6EBؔ31e`ȅ`e,"2C%*xոZMCH?)iRV?eT(ԕ90xx'vWhx0*hb[_?ƾG0'QK*\'H2s3{[7񚛝F6ˆ#e$WvP^:|D6ҩT(Q*pHzlT`iqd<~nَ`sFKR БVj5K2" i'$Ȍ 6j ˥MKw9]|;Kկs }{'|o橳w6}7dwB-7?}{ۖ-x[x7\w}a-^|Yyu r#Å|V+]uYQyd3?$^fө(Mڨ|q8kQ\%Ic|m @ĐDqߦYWZR1c3jEMi 6MId4oaj>Sx3C"==1o?Scuz Yh Ğ"}cx[ 4ny}<8 \)|ui{O=F&+-#:?+D_.oQ`鴌3yt@oO__T\ZZI$3ˋyKE@&GBV!i5Ȅ6Q,.rB˲6 3̖H!3D(БEAh*a/ի?951\(4|O;nkjŹB7ؗHo\}Z>d{'Ƨg 񾧟:5ub4],Wܴ}-mw5>du-k#H |_J~.ύ :dU+d q"Ʊ+׫̉ge~*&v!H\6A i;YL2& fPpR: F b9a%}h(&tZk)ϸ!Dxo+g\y::f jzv=Vb-g2:i( c'Ïޗq7^s>ߵ3iݦKU<{bfr;^{5#}V{//&W^2%%4W [\SLO1rW[=l$WR9p8QX(׫jSl9F~1L,AЌˀl61VQ֠(2RX7V`u 2dHb5]ӫl;U5칆aX,nNDtuo}8痎-܀g@` 6՛׭`@i&J \r]_۵{nN%]Ǣ#?k%7ZAP(E~:O}ۿe: _Q.U0CH8by%/QK *F{&{( s$g\ 5eG0 n2m@Rږ$c A∨D'LT2%L"эdzi>˕2s׽*מ|yut-:r䱧Vqٵ\Ios}+Kjj0{=O?!ǏՅ̭N+\*.s'grʘ0$:?p @m-ah:8wv %ŗ+.M Xš"`n lJUQ~̺@͸efuT1.crԴtgί nMr}9b-; BHk! V؟zza=.;(\ECIg2wMOl8w{?x||3Rz?g\}P-N-s)7ٻ\;xbέ]l*Z"Bm@J˲mDXHd@ d̶mmRZc !)4 GSThf1[KF3 1MӋl5i=ca=䞃џ<}]q G_4 4́TEicuH@DlKDwdz;ȐgGG8F.P lA@G7 XW&,ԖuMXRd;\ZN5ea٣7իBZ 'HA5 LgPMwt B DCQj:YLYҵ-D.5![LG_uZKKg_4D3cί^ qu߁^f`R>&߼Ia>%!~ssoF+ӷ}S'^7@LLuuw4bEi%0MYڿYk>=^ FJ8IC_X\,U_[v|eb츝ЀGB2í Q C!4)ZDXyJ:K}[3$eūO\PZ3*D#PO3v/*8wɨ}"$/3ؖTg棪ٽm+!`fzfwg%^>kZKv!ɗ_:u0w]lmVս(eY(txp>c9#aAH g臑-"@=ljM@ %焺E 8#2DΘ+ Y`Hi"L#N.ԛ۱K4;Bim ;ҭPLj^XܒmU8;0]"ZRDQt1tȑD2`lA1ι%ȤB0R0G2fK ,DF R̋CRҮr~#ORK?S2z^ x=G7zh1d YԳЩQay g l[: _![wlXY^hV))z(,dB7u珷wyj=0&m bm۹BvC݌1MQ&l^#? ^w_vRR8~l:{2-mC~6A {-%O>Fh=ͯTOJIq_o>tx.7m-M[?M]NtumB&$%[`M!#3P[]m\4~<tllqv%)=zPT|q>X282srJp) A+" /qFS̕ñ .灚yE@ 9x6mL1E j5/!˕F{$}.MzPZ>wj$7j^ڌ^@ GQOXwt01= XVfLn߲5!kHA@F#6-P2hHT4Ť !R:vM@H"@"`()&2"͡A(Қ X b+͛!6"f%E "UbJ_OH۔~X-A=>$HSayY_91674a~aQO,Pe$R%a j(-̌ $#q<>yETkᆾ̱N +1 pA  f|䦅y^PK޺[Hzi=gff jڨV"$QȨWr~–FfV7DRZPiU^;G+ ]RI{1jgM{ 4nٰL# ˯$)<>';G6>Ů}jvfst:T;yc?ݹ䷿/9ٟINgFєjs>m[3 iQmOK Ss.}SFZRS=M8NkZ`տW(G:,xG'UH%m4q&-VaqHҩ\[T^@v[2ǨD (b& b@խMg'~ W/=Q{^vi.~W-t.=Sw7Qmwԩ"[@< s@h=fZ.Q';~EJ"PTo:Y 'SI4MUFmH:TZoiXFO`Hr$:a#b d `H(M3q&@M&Ӝ2m)C4)Edz"IG,\[&HKHXB yܐe6m1z߁OL*IrqAxU-Jv)-9ܫne>0רWO@+4tоSܑ秆? zIXkPD&m3ЀJu^7B39J7TOA}vn.Nqe2rvTL\ҶxAj}jy9t?4姽ѡ[vnfUoX!MF@S:t UjF(TPc*%D D/T(J F<%76 2B"C3H:VʶF ˭{ ?$M; 1P@ nZ~@ 2:f"qa2=D. ?uaؖ<ɧFov]E @L 2 Ip Ӫ!DdƁ 9y̼{%-mɍw?6v c2Q&F@˲"'MƘڢzuȁ^mqm]]]\{GW;rm|qn>}J= Mwtp2]W,!yg)4O].^q3$[ 3'S錑Kс>65 3'/]뜹cRcˮv@`NrJh8 !B#)Sk?R3auTXkҒ&ȤRbD 7C%n\|<8sgݔa $`@3@t$3B0J3@MڤUglne~~evO5oƢ;A[ϾN-,͞Oɭ18XNfd:nN[4AŒlwbpSO=h^͛wfҐKcG32Z4N \:}9[} DuXlDA-z~ FzҙF[vqaoY<޲VOXmqt= (T!cT|ײ lxDZ w]fQ*h(4! _DԽ@ #A\IIۖqCƶRh"R iGSu2q cI[d3#cB؏!iuuhs؉{?Z"R.}ZRjHa/b6!ꕰV Y2&R6EZG4'' 2Ji1Kp3l9 N? ZyAIy6sozhoO)kn艾jjVP3֕V 1"e^VI BZ6qQ+cbMl}B(RQ^Wp:#"_BkGs__:>g`d놑/ugeaԑ1!0/ wwmߴnp_Cxͅ}Q [Jaǯ}svHm۱}``ι|cvMWM8d򍗞4#‚n'4:Dw艶l\[ZXV`RFˆY0gR_FFkh?Ա8;J0`K 3תTFzuͯ<;t3Ie GB|A'" $0HG  "gOMOnS4 ,G޷s)^S kQimKkpJeۺL__-"vena[gM.GK8Poh^uH kV\gGE(`rc-UӡԻ.KB'%D{+XiM1k5f8H@8r(pmbYܟ| HAՋCnB*%SEik\S 9',Ҩ)Z.xݳݳ8m ʦ;m6exLP1Qֱsdo6Ga0Ѐڲ>|رR?&(4aXE)sTs+~R8mm@TC+Ԋq@ư+@ڐ煆Hp !&R"":5? ;7T\FS;֓4(R<2-DL`"/M0"hu4;t=ǎIZV(.zm|}WJ6ϗX]NI&"q2/߼[]cû֣B"⒗kϽo˅ʞr/Gv26H{Gt.̟Źlu1GRiT@*!z17aoغ~\Bbݯ/r4OL-.L,/Q !$ѸRidG^QdD"%-1u1ctZO&S:f[£w&3x/!^ڷѓc3>ptts.޸~=GDGƵVN/\pS, f1ژWNw ^P;b "5ϊ_1+&TG&$"mґ(D ƕm02ȤL؏Ӝq!@0 qWꅠM[ͼkA &#p}>FߓF et†'Q0 hiʑ۝y=PZjC9rdD"ڶ`(=Dz80CJ+nȑlQ} "*iz ĕ ƀhdR6Vyhql?RTұe:)Mhm[ #Maiy6/'(@CM3l" N*54RrDRjWF9 0b1C y`[͖iKG S|HXTLg 7\47w- jZZ@B'ɯ}疏}ӇȻn+/u d"Lif~~GOuAgw3Ox.M,/w}ζ$`*FOgDȐsΤ`Nw?}Jc˦}?R, 竓cRsfZi~֏2@˲cJ~9 SS]Z=Йm[PTc~G ńc%;2sװM\[ZZZ ȤU-e^|5WzF5lſc̕ +cSγw:G~~o9e<Q k6Ћ0vjvظq=&$6 ".(4d7WC[eW4[j-=̦ (Һ/xr/}kz|yhsgZiԄJ92 Xư%fhX^:E藵 [wuW\*ܱi4_@1pڀ!+wV3[nњCL##"!8%߿󑏿<5r)KJD0hϑ҆!Ӛ4!1Nd2v"u߱mY+2Q V#dEkDQ#PS6 zCX' g8$#CZGԄSX-" qDϏ im1䌤`Asd cStjm XQ VMRZ7#8BQd}K Fz "}5olhx ׶TL4.̖g׉Po|sY?֋:srNn˕_]\Y ?wOQoz<hevcPȏ\Ź mouIOR_wR@Xu8_2sBE}?D\6`WLgF 02Uul^n? 茊iZ%SZIgZ#NBFQ,pGX=é#O]fzI#3??+?>O%- f\z5 ˶VBi@؄#-̍ nC?8c!@) dQxZ+5F+xPm33NO>z36:;6mɦ;ІQzҩ?p@.bN8ŋ {4cֆq 0c84c9#"c iUXz ,{+.إ*RЛեS/K?C}}m9]&3d(!\-[I!16oǶ5jTAOkXu򪥼ۓ*m]z摽'']ǼMUI)+Jk+0K jg܁x*Vקm?R"`]VMid E2\rXIhiCԽH+--GZ,EЊZwαin"0+j tl+^ v1dcE  1BY!X 4QNɄKY(!BJ+ =N[DE+QG$\#Oc!#B[n;_/7Rd/Ƣ(:N-;b)_??iu tk6w']?׺{;G h?$MLr pyfj܎+ߝ궂Џ0RɊ2$!CX!ٶ[l֨fXtmCm)Uj{^80풷_zցեN8۷lܾe&)UjrXdTP,)͙jr|\0dhor:ۣjRk A#`2 9g7 0D V1m˒@O~Xp0IsiW&_~C[.h#θ1AD)dӸ+UPɃn;]i^M<_c7mEېmV.$Hc7P*P 4"R+2;%;Lo}o|oXJBʽWh;ζM:ZC7WSWtWV/ӭݒ{m_ߢ,Nj@Ʊ#!eb311dj:05JdMwΉߠHS_W{:)V&R eI9\0@ckBpcL]]>^g U]kyQcK"% -FRq.¨@' ]D jͦOin Į"c8 F戠cY'NL_ɣtJ* EHo?|ܐCd jRʎ }?M -Gzl73cagp3Ea/{iśl2?\בJ\ 9K0M땉&viJ\3Ȱősf[c"I SJUTsJ X/*ccs\O'ɱm۶ ]'^ζLO[sDD|UZ=/+R5_~("/VGw'#/~)CntU AA"&Z) 8e.Z,D2t3` !li'TDQGuƙ`d tεO.-o@iC qExf\j*mdb oknւ@k2Y Ĕl[V|t E!rNV 2 0ÑaqN:w6}+]rsq-2N&w~;gmٸn֟*܍_K2&츖4`wj`b ](:XbwM2@ƐAІ(^ -4Z'2 kgj @ǖJgLH112<67!ӜՆ!Jf"Z4 )@S܍iMT68gL ґ `40V˚zj`HՋ$@h3}كxo׍ON>>On޾=p_7;p!GñP-g?[=>v"j@!HްGkG֍ iv.5Ԣ&e[81>QyˮNa!+X&RSdW@sսtg 4Y񈜋jJ$Zw'FyS};y u[O_w ??o:wM?morŶ /,W+Z/=]O THP1ɦ,diV CfHaL>m[ IQޑK:%D=27(D]iiV_hV!l-ţVZUl_]-QTH2%%0nQT߷^=0P0 G4N*gU]goㇷTG[ׇ>cpd[HPՊ$o.>^Ϗ7_wغٙbh* 6}kΫ?]ϖ $9adH'9`>hL Ä'LB2 Ÿ\ol1#|ީ9[uNr"Gj1\m pc}@̘f^HLD#() BVjFEکK.D"|}l&CN8p\֋'6L-ҮJ -C*U =pPuG'PSZqeqbnjjojھ$K,X T:$p8 ν+WzMsu(]Y6kD SOydM+#Uq(?!ANE'm<ʸ\qalt9.͌8_ ɅΎ\_nSLv8"o8z3"m!ӖN)NXϻV`ѤtU84+3$Zk@dD8dU)h0<|x{ogGt Gbi3d\U ihh h0pWURyʂ LT ưMXVv캯j~7 qR`chS 'b5䬪Omo=$Tݽ5Ɗ3#Kqӌ4ڀRͤ29ds3&_hZ^RD[.JR~xtntSs9-a;2ZdBJږJG4-f2`MI ɢѤ3q4ib°؂1K(028gBpےdbxPtvtAڎ˦J΅]%A0um 'MāZpd"R@d I'602bȌ6%i7vu[Yk[i0JkΧ|89Y.֖([\ѣVCW T&NyU1o#s\`qr}{Q MT&FrZܢ?4q=>s#9 H)nɞC^sR镒kwPk]2O>葉>u=9 H8=to/(u\fpgjXSvv6$lm Cxl2ؙNIS=鶵@FBN1'dB0B@ZMs2s( 0RZ)EZ2"G"~65֒[F#p)-Z>6>1霶kJ$3c\յF3` ^M 0 MfjwFP4JJg$$TE O3֬S3Ycdlj2 IH@P@P/@[ZRyйoWk_RW2LodfC3XLX+0|F+@ Ǖw-9`à &  KAկiet%]@w{&E'"V"B4!@%JynqJCim!ѤhVFS5`ʮ=JRkB!܄H:m$Πy[h$*mDXt5Gfh*~1L*RAZhˆ82` 1:aJI=':XBKS\f[mqjkjQ) gJ3qv\arAH3f ;V>N |:3,g[Nډ%'N6j)R&8W%ZGŕ][Y(RڄmKh4ySK^:D{8.|\G RjK#(Ktd\l4C뺒Ʉd2ɿ{{vm;gcw [99zN2&T:"F3i &ȜV+-4plF:V*J!mH`"HXB,?yje A: cg|Uk1Vfֺu@9lV~5\aT,i WhT,R&0ΐb]-f0 8^7Z[01'42&b(ǐ>_C9<1E Z C5ģKg` N#TEFTiDaD2y´˻2X̫0^I#cm9m-)p! f"6v|ݰK.&BdMhMdQ@8hCZiՌ"R!1ke:sJ鮎rٞ!GZ2ژR@2 ̘м4u}sfR|!?2W1 bB[ɚ:{]<#~kg.,{9tQѣB[% <:0;Sӑ9Kjչjq^n׼o׺9mk2Q#T(X @b#VȀƱ1J Rk!'1Qj1{eq%Toٶ 5ǧۗ jȪhu6*Zj+@[e|Ti׶NTE"LQ8 \cC8HYL&0)+Դ !xsUk7^vh=׾lcA<0і ^N6˵Pi4D,;ܕbD3ڄ$tNg]Grxh3LT}#Pr&8JC@AaR&U/ή0!m@kJL& ZUZ 4E±!mRJE:T:" H h@`C֞mjkWZwwt}oO x8&tA%`L*t,{aq N"g81BQJL*kN4TFِh C ~iGǦm G49F ΅,i S$CO2Ȁ"yd%qWXv/f[{ Ԁzaiq˺fɝ1J%\e\FW~r L?O7n|_sx׆vTO¬2&{R>͜ڱ;^QdΥ\-./OĐ @DzLKM%ksHk Q0 MĂQ%yP̹<Ì6`ѶcǏFGHi2FE"](D,yK,UO3KkfZAZllVi+ZDjUFH+kтoBZ#  eR/-]mrEp.Uڸt])38RpU,k+^JF61|"EyaH%]l  Y;P̏T4=vjqvfph`4r\@&Tt# CE6%2p:GFƦ;gkq"ԞbOeybM{jgۚ 8ƸYRZRhcLTEQ4Ɛ<eq}覠|uCv d'kp񰔛L2b9G m"D+cA Tp)H٬;':I@ѳB mHф) cPa2PED]_'rd,2)t-EqD~è\^..`t\-+ebj&#UV`ˁ$ռcS'Ʀw\x W_4\ь7KGO]6bQ/?~/XI9%!{\yrJ!(?cI6XQJ홶^ڿMW ^u@#ƄeY1nE kM plo]NSom; @ӏo5Mĥm[611/870A[ A ֤ݞ=Xۖmk[Z^Vaą0ڰNMxw~m/oLNMJ.T*"PYmIr~vrw.X3cwIQz;ٸy 젨N/M2ɎWJ%&RdLb7j {:T~eqyTe)b=o.GgqQ_+~vMÿ hRu-+'ѳɮT#_ :J)sΟ[,2Qro]C2ZݫDWi^81d%lLC9'bbZ+E2f7dpsƀq;qKʞɱ饙dw;2f.%[L#٨ ;vRJQJӞŢ3>P:ەn[zA,[zudGw ']T*gxdWd1R1e8k3ݿ{~5¡x>4r/߻rh^YJW#aFt:—ʟlD^='xjzv+o^L*ZH _*D-+ɼFztJǎNW*B1=ԮBc%bAb9-Vۇc\ظnD+\ƛ>_~NZHExM= Jk_F1ڐ%HƑuH#0))^(8rTڵ3n<ӏ+f]mpu?N($@ᜳ, *{o™5l{Ě3~8&~4[U%PpŽ:c :RJ)MJGVcY+`doX,-!I2MV]m\J4 #2-_-,Z\+Nn(/ιO˼GJ%= d\Ƹޕ ˶0qq\J{rؙYZ,QB@`0`l66 `l0I+avwr]]U{zvLOtu{n8\$T|~DЌ@ hHCHK[:SIP3GBc[Z60fh݈:dГf oy;{3<{$לWzt ŅB᾽dKvى̵Ss R8g|vܥf6gGu#{[=/?{7 ;+BUTh4Ėvdؒ-STy) ")eas{N 43;]9Tx zZ)aGaШpA}="^x=:G&O*¨\"ϐj!K9OM0:46^qdW-_Eږܵ6vnf!="Mll"MF5a0}=L&^s_}?`"5:?;M 椭hR",7sw$h-хhvC~|Wz>OK]qAo;ԐDfl嬑-V D1(e6 H&g' Fr+!@VjJ5phCZB  DلWX9ϱHVg}Mu|n-׳&IWO4k8VW9k:<+X?ccP4~~٠w5'L;u:iy\~VZz~QARIKJATb-t($ ]{+{M[{|f:ںҞݻ6KAS6!׺32Lk,e&}cBK⡃lȡ2Z>J !-=";@l%c]m(kw\/A=Yّ1 un]_G`-d ²! i1͡UM/}Z7uvo ΧApvNnO4X>`E5Xr Œ$R$\9M\۳ONW`[X -\FpEe KP+lQDOlV؟SS/gnHܶl}& u3Nk`,S|"̠tfH)-Bh\V˝ā] 2{5 0k/DKӳch91&*c`Jryf۶)r>pSO?v; !=o@N)q"H9ؓĞ vju݀C`[@=wGQ_2cI_w8' t(Jї[M$懍08H+UTz3j8қBeG&N^r헟?73Zl7a@6G uȨ ȯ;PQ$lwPm@Lh6V5>en*xm9zT"@ ϕDB㟘'v k)+P9cǞ[^Zعy$~4vKBXW_~,5 5G]:~]_5zt8 $,)5mlOnIЫ]:"um-a_֖ , HmQ{DFXBƱm }+O1H^u]wہL/s;DtJP##Gw7[Q;qnGؾգfx&TQ1Ra@"0&"S Wx59ǘEіuMLFѹ~A{UmM$ DuN, [`vig֑ +l@ .Adrnj9Wti˱N􌌐+fa#P"+$j[.C==ZXɹd^\x2_#i`b&J*q"J0gA^` BH0,,U۽$s7M0}fN/P-0zHJ%\˺aufZE# 2bn!Q ߾uOشsP/%@xvh'=ˌz=.D.\[[kshKgLL~Y/Զ-u̙|jᡝ{ F5"ulA{z™H=1Da`@j$,fԱ߲ik(5=}^.*sg^:\La)ۻYiT9nOZڻ;rD ,&AmI)L7~WHM]Q,k`mU|7;e~(ؿ27&aKȆXB!5 KJKbyK/߲n3;1qFIϴzܩR5zx'!1mr-8 k<D5B4 q#YmrFtTK$d2ٛMkm֭ҍD NJbcY%7KAyK@M:"q"֍uCTTY\~S3Rpfvl:45x9.iW\}󻞘.׮\; :i%sz)ښ:ɞywT"Z2ưKi/;K' gqq?:ht}S'Low`w\p G.5 7\86?19BrlٱBƄF @>_Wi^mWc]g,~epJuv%L_*ӟl4CL*B@&Ih@ql,4t_}k}/f.W{6Ӟ߽"stsjQ|,Ⱥ[ݏ?/~trl'm,Z3NVP DZ9OyZqF3h:tV~띚'"右vc(+/d <6rm(nM9%(j ˙#SW; ֮ƫ |L_glfvn~LnPv|63tRAhBENn=3zSUԬ~UPT}O~?׵۬>K8p_"(C5 3'TOv ]cXd&).MZ33W WIN,q\IgRc'^6kmv+`719&6cpe m(X'[zґq,Rț s"E(:_}\ TX6Se~ik ?ȓ}ciijjЏ sDsgnU}O`ߵ7xpS  VkԑǼ=u=Q܏]e: Ǐkͭ۷G~SEBHDR fiwRJ=Fe~B\^FPjyo\jCFQDA,Kkf&5yСӺÛ~u3oC^~PkC+:8;kH#Vk$J7Hz KfHP s1AɄ&&Ys)F{nxv\ ]ڄ5 '/hԲbh:2߹2P\; D_>=9ԚPޟ[*̬M/K3[$;Fdd5) h_pNF2[2/ M 5c'I $MQJ]%peG poеGjLjI2Mȸɔ%D@1V+VKBklRZV&َ71=vd[z0Q&Ғ㹆+Lr1y1%H&,ƶ3wᙫv_p+hP:Se.C "?* "!\ϕBf<{xg ١cU9JU Nv|@׽ns{N7 'OL[h}jPi;T)j"7EZf:$[vAgS#sŠ#ΓB?R͐#?tl߶vbKHWAg46ZD~-K+/lGǡ]؆b 9+zlX|޲)?vW˺ǻ">jxNU'hm|: hye]8Y._u#)w0NZ ƭ(1%QiM kqm }eKxwZK:w؝n[8Z 12:r؉(ȶC}B<1ӳfw|25rF QhhC|*1hPjǛzAbsr{wf7֧Lt U/Ņc'7\q9(cBmJ㦱hC8{Y& !@ќ+Ɛ}GDDl$PhfӯT+'P28D2I 6dnufk960x+NDP"k G8g}D >ga!yoK.zեWiZ Cteh tyˎsl}5@7][5(H@WKKCwcO'O&Wy211v{#V { TE:fՏ"҄GF׬Z5 l0Rt!}Ԏ6:"Cgy.ojՆ_W}8+݄L8~MPF0;Gᦾ.3pM3?_qdŒ{Ι" _)T Q/tWB烹o2c1kCX/Wxׂ?`h[B+m+Wh7ZiaFF)3*f&m-!-iD]N! (&[3V[k$CAr:>[Ad@h` i;" dBP̓-'͠T%z3񑞱p~8[]9JTx-%w`ʾf9|iY* JwZs3Za.u8 #570=W\,+ ViE p}2cA*ᥒ.vxxܛ؟aӟ]*+Pˆ6^yc"!B۝[\͡lkK[`e,Y&!sb[g~ݚHFӗc{H80==qMo?ӉG񃻾bݣk7{\?S'JaDRӥષyy{~{Kd!:>DZt&)H*[%TY ȡIEKSBijf`:_J5A$6`PhF\c3&0F}q4q|$yL&D>r,s݆em.4 IIDAT!`prurO_1Q_MbïǑ8 @PKsZ~p4]צ3ˮaZP>%SF/Iϙ8:z886V1vc^wޚM~]ض3'Mp٠.K "tz+mNN[w)RQ%i9p|IC+aG&BF#H$m[0 dmNla;olm|.eN; Egҍq a4 k/))8_-.4]%Ijܱ,ZO;"pZ?E&"e:vhρ0$ӽP>2&U3@QX6E٤Wj:{w?>YG~%GV77x}Nԁr97!kp\tޥ##h4q4#"Qd!R[h6m(: DlhͰz8vr^}L,JR_*ݝ y䏟[2Qg]p^Fy߿ms]z\S\sŷON<1\ WM,VWvSGNBI銼p%`-1O1&B;[d9ӞX14fHfq+l)Ʀ/w-* Sv? aЖvbefaaz="]Y%w߾CM3d_r޹sjickʸeՕ[7GwMnN<-/W_zdoFd"A3Vދ0&tw=\m0#)0 ۞~"H)־w8/|%|ɍWqy}#e/-Zdoؑ\wo}l۱{{0/qidBЉkoԛ;i*'62"12ŪӓF`D a1Iܢ0f1BAZVvd$40 1^ AĶצVŽ/AlT$JJ8n"%+LK8g4 |zIiV"GEI;tP\!kuOPf/]m빽 KJ^[G~{f5hh4`ȖR<K@}ii㶍nOvvag-.Gt;ZrTRf!(񪣿uٯ:|x~y ޳{͊_}˃u6Rgzywхw}θWD'Υm/ABF6К>'`Wk qjɀ"ai 1C6ĥgq5Dm哞6 \]5yзo|^7ޥ|麱u׾o ];Q 'f] rur1ћBӷ}MR^6ۼ"ը[f|ut$ [35 f/S+V1%**J{64`"Th =}'@`1Y"e! LFGW$eY-[J&c:"\zlt 1hI!ymckԶ|պuvcRZP"k?ۮxk'/=(?9|b?{ wVˇA'IJ7o/(lFh^Rq =t%K k7 ;e.YJt\KOqBNY%RslRڲ-fDHe:KHaP׾F[XtxU~N؎Otu!ߵP Gvf#m Οgx83@@@R@E h AaoaflZ+ҒeYX%`9DJ3H" pm[~{ d쾟>X,-Il.m ۶v-)eGM 35/Iz۲GWagwSSǧ}|=ͩ`qu-zd+#kv{KozvHc'v Llf(mGDA[tE׃ZЪNh}+qm-PS˳'*nZLdadw@ٌ4# -b! !3juu@20B)A "J᧞ :񾱑\4@@AfӒĮU DAdY8+2L.O?rg՟'Shc#^7i\oTCcknxi/=pЫG^5gfv\%n14 5^,k| A֑6 ePͭ( X$E TYE\*(5t0ш޸pdP0t 921jbm"rݵ !D,l3J!BL21 0;2AZe`vludK%eۖՓN 󫇹FcΕl(u& je$lĨP^,AQ0O+2i˅'(,*督u_+e9^lҦ|.Dh:kfefg`@"aX.-VTpbST# li%2adXյK쩉RƎt#9yrݖm-qlZN7á[܈e 6I't]Y4Hz'պ_. j\/WHHkVBe1:bJEq[B&cm)l@hLM%Іԛˬ^z` LZ g3XߓK6aPgRX.Hl̔?fx o~/=|P'_v*!j@rtLaRTJ7gݡos~f=s>^FZrsܤ*S=Tloh5[6ڴodl[|@4l餟(-rzٗ]ne˰uE4JU#kkK_G =e#?ۿ /a|q(Ndl^C|C&{&^qA~qӊ%1ʀ/-'z4lҖMd&VO|;#AbY"8̐gm/F6 33)tBEEC`^0@-.L 4@Hؽ3M4$#H H6 @0֍fGX*A(Hx^*Ytl;&Үۻna^`ڍ۶<]so޾7Cͣ=MSt̳N>C=y?>V*q RjUx !,Im:(hVvwAX+ Z,磱 *~Z/;J\X)-ՆFz Ⱥ[)UPOf*k$+ݽ{d #4l56# n'3?eg2|7f Uv0~5~JJ^* zDAhìX` kС6a  Xk$+>qa)B!Q@ LĂH$D"@mjiɅL۸a1)YCOf[V K_l60FiQ: uk*XU/о=Ky+lq:xtVI Ql.%`~G?_>㌿ʩ%||{e ?ۯjqqV+O7p==١]~N2Ա3{_֬zn޶iǙkވ@SuamKr٫W~כ,8cQY\*a^nl6ܫmo|g?Z/ JD@ ٤T` ?^КT?䐭]/!e3_t`;o8g snu$ذ Hݳa|bz`h`l?7M%I ds1m%]!dJMH1I4 QcX q#ADBb`fbc1p\`YBI%D*-َc]ּLXedדL[زy5Q>r 5WQoAV/jJR7}?R6`P Ю(FfYץ 0 B&:ZPE`2F1ѨT`ȀF,i ˵׵t2LyKNkIVs4N̝yp_~7ukjIx% ~%r_CF.5_B]yrsC^|D;I bIH$ذ"?-<֝Ivh^vQeDlbqiaa~n䆇׿Kx}O?*ϝWmͼsmiN+5h^{pvlB (\X,5^m?/}7}lc}q.[5/}z[2P o}uuSڏlKɓߟk[ҵy8[$˵iR 5Gܨ}YC*Z*?7Ya\7LN^%\e/`4c9nF-OBkg iF6HvŮ譝-X,oWVH  _qEm"$m9nelabP_TQq^2)7762*lTQX/Xm63yN.<9¡5W )[Jѓ/ԕTgMH$vpl4uN%K,<B,\oceH{4C7\:T(DèDʩ4œ HJc=ҵҪќ-;3v6c6,VhX.*@ld,N7HAFf jQ ߯Z 0 aDMF3 $c,0 æM( zo04A(C,@ $x,5Di ([35@-&`Cȯ5]XD%Pp\OO>{t¶~gwz tu:ˁ A(@L[+ +Aw/'hk.w=@G9˱"i! &"HR(V &hЖG~5=%gZ-瞻oԗՓoei fF" F 븺 3!ƀ߬aT,1’繶+L& /57Q#huhAtpta36l %j>|)}ɥfԠe- l3_nym=ERR2ęD\JKXY ;av0 d[1Z2M$˶ꞙ}/9cȪj4|i-]|j_=|Xǩ"vM2CJiaq!D֑CK`~dxh|d?9nܓO-F:cO\[_ ׏g:qd6#HNsCqåW{$X{Eв@8RxQ昫Eڼ Gs(9]jj)l%\oێ.N͟Cуl~Q>D!Ma DتTmj1IN}z!ĶxBmuE<FD!0 "cXFDNbOX'gy6Zd;dd-2 Y<@`\1%DF IC@u 5$=s^~[֍?x_WP{lXKҶiiX?Dj, n>֚{-&ڴ_';hRF  ;V/B:P`ÀPh1dzjOܸfJIw ;Db[htZHBq(AZ5% dR@ XQ|qȞ@Fi6L6 nͲmba  4p{ alf"Ҍ\8XtT*ṁuDJZJ-f즭glۘ=O !5K?PKb h';{]ϵaww>?| ^|s|e~|Kxh3"E l@8i3|^P~/yÏ~כ^=qePMg{y- #P<[ %$H(01nĶ#-m)A`|PY6^G .x{&F.]v+AWEn‰ @9Ύ_c/~[?pޚ={iloЁCW.dx@P2Y*3YZ;+!KE:Oq"Aշ{_o Tk?a6 Uf4 ByhN&kjDbq,K!%cH-$m QiꏂY T(û,I:!3(b* )9nDZOBHMBBX挑R=oǙGK @!Q9:&-ZGmnŠ-Mɰq'`@@Kť9˵h,psب.CɁ\dw' an#G hmonB=EW:aظzxalˇا9Tacq1A664 `kb혒@ xf@6 N2%b"{ܿ(ukZ=M!hu秋<\.Z={9iX\*4@ieYѽV;n~tu7\se3sK챇Hnlw5c*lO]A A5l+YŽy߾wO ׿o|]Ն E cg۽N[NYt-2dNWKa 5z}.K?q K笹n [@͑Ժ(%-kl0Y"H:^TNAʲL СAafgV $fa1n|K|;?u{޸wq۶YZƺ$E1 ;RJέ͕2ҩRd}=&$֕$cbOe(CKv}д_P7رmc rS^l6knglh6f L$!eI+`c4J҆acLC&P1-$YI)I#d*Hb!RD<}o4M*DgRzv!hôZ"O9l/XDQL6ݨǎ,(KHMxRf$ ٶ,6D[2 I$Öm޼eth(919g.#HE :, ^+m#Z!f| K0jZ$,]uȶ/PL8צc `iWt[Ah 9ɟ^zKl;w~g?Ůg7]Yzb%|᧍x_tuE?=?7_CMz떳oclTU^!1e\n%fU칗/ԪTEoO^_wCzq.{㳙$t@:Yjo3T`XP[W̌|`3ⷷf /~:5oX۷SO3|SɫN7?ޛb$IJi@ cXFJ%rjY-=;UРEFkwGWk|_H]w<,Btft<>pݎ{ h` ׾{~)/o{3Ȥ߻D<]9?~SoxYؾywox}o* Zyyv`OOt΀{#pT;mO}7>{#^p9׷W9^5χk s76[vZo:)57fڝ~}n쩝^{|S5V >৏ycғQl4#_Hi7#2===OLX01D R?W/~/VG8rrnrrܳf C7juBʦ3{)[Mo\w]w]ǎgێ7>wxymԷhkvQ;@k̀H`dugoѽ|A᭗]_e]aY;e_hǻ>yIW(@aEwʫ wx_C>?yzn{wCsmC)X3T&S?hHAZ2RQ24ՒI@ؒt|6hݳu;]XXg!Ujۀ q8;3;5;452IWldD2;:`f(&$#F Bdd@cek" !E ]"iQcbj*XaDRHZ:&zZrl']]7mI7LtF8pߖUV&B 1ɝ2 H,Kc9R-̔jz{2F"!'A.+S`8D_3uxlgZ$ }~ mP~:ũU[WPiJ3X5Emu1& LL0j`<0w2Q-hz/OluOWhe vF#lE+AZf68 ׽o[}DC'/9c_y•ژNFnk@GwɕM|^e%G{u{?^9!kt~{䰣!iH*IF )Oo|ŗ /1X=7|䲹 ?Q?c|iMh&,ZͧvЭood)׺dc%{L_̥G~M݌#k?Z!7AM=7\,%rړ 4DlWԪUτI]=EL$tS,i o;[gtZJe\nÆ5AQ.&g-.>;;AS[5k.:"2HSpK€D!kqE1Qby IxfQ8Ev.cI~ `UׂDOJxv؈E"JS璴%%`m4H) K%禧@}HE iBR+n1~C֊bxnF\\f&dSDfH)$ӟ:fIXݿu"V$)@ͧ뮿_\{Uo]N_ .86UG9//c|zUylqξF%C\`Nv1&SK[owx|1e)/OoSN3K==d~]w' [mK_3 ! 3;1 [ν{\{V dƠ%(4mqG6QX@ػI,ժaƔv"d>_m_PXYNs%b2`$1aK1-J;\r_Ank}G5éd[w+o 1ܚ㟺ڬ-dplVi+8:RɡkC8?oONz6&fF4!`t(d29/sYQ$l$bguEkEDc H}I_|Y=100Hrs5 Taͯ*ss 'gU f\>J%-[R,L,K.8Hf2̚43ʖA¶%ATFi ffOX: R,UkUZ)c0lc.d"xljFVu\-l׵\%-ADdxrz6ۛw2&Nb1I!oKvjZّ6򣭬5❸͢ q#|߽g-טN; o2$pu1Im݁I~xޫ_+_ ^ +O?gv`!+.gk7^[&w :#Zi5;X}jax(?i]qr9X m/bǫ/.7~!žڑo~v^uS-RP: FHE{y2 ,ے ykr/'bIiSM֫ ^qˍP<*:NLFa)e * 1fmպ_T K djx!;U{& ,HMcsk!3J $-~jp IR"eQTR"td2k9ʰцث65DVkԚ* U=]*%59=۳z׾!KbqDVL$0&%pjnox//I}7;H\L:$&ƚKI/"Ԩ5z6f R)MFV619Ԩ,5qK :%L#rtbx\ӵ$ȘpVf"!9 a Ā-S^ƅ; /㠷︎/3/WdqOWܿX7q㿼`ǙhK7k_W<iߺ.츕ĩh|O[+H͝x%֍kE& B1z||tbbjxZth;NOO@Dο.tTYၳ!b]]g o~gG'], \d a`Hq=v JaP{իu;i``⢿~3s/ع~?KX/Qqb1D3ǼH)v잞بJ+h '+ 33|Dܷq]'idF6,mD7gXGґ&jS53+E"rNTj43"ơ X;KxHVBGQ=l6Q7 PI!1cCdkokұ x߾G_뻶]~;ٔ5 ̧͠ЯV rIgoPF?G##kCR:nγŪUvҟRȺ'` QRDir$ڑԬ6ڟyOцqBjj-1qcA 흎V ZA\/i :|9tV;3"_MykTZxzJo%`;e0hEǓcC(:y3 w\BBRI%>CUM_ȕ:=}Ks R 6l$>/g>—𲗾of'[U[n`LM{~ ͹lX-7rOO㲌NĠlmhaut_@jxaTNAS6-Q(EBj6QT-,.yz ߻}S}ݸp†W$g΀"240 ßy/:/R=U0m"b&{w|խ7v:*10~R2|-GM6F)'\&=:8VJfP KO Iܡ\.KDڰ1&-S(L֫mM8-Z/Қ Tbk#æbU7tè:@ZCZt2⡱+Ya߳jo| K3RmilFgoز_wnb3 R^FW{7P@HbʹHV\ٟ,9 r(,6=!xN `h@bQZXo&#ڞ@d-۰;?׍C=Lr _=6c%cت3`"b7K{ios313);b>cc _>ϽFAtԟɟ׿J&[nq8 +ܨmĉ1Ixk=^s 2ڙY53`mᚳngLO۲olٺo|]7֞n@_O"}o?ʏ?K))mKYT:'3/.ntcN!t\CD.ї-B"TcYj4/weN\?sRe +DTR3Z"  Ъ.\c!q8bAkm2a{>xݷok7po+j"YmVY⢜֬p7O$LzvbrfCzg,gC|N1`1_æN 0!he44` @HV6iJhFHGȐ%͆F6&b;v7n?Dxx.GKTHDM_57yO:nlfСiH\K'IJaA$j@<@;p}JR( ` a3'T"՗"m1+rSK8@(,) =56> ȃ=^U0B fjx2 5 im8j#A-]7ulVJ22!ؖF!"$wZLkww۷oi;bj>uQ/maY;cBRt:S(t;wƒf13ئDL++U}L^>W\kn-_KZR sSG;T*gd 9$A6Tܺ-A3#E8$0lFZRt p;硫)3 DCb+^r``<ԾMG AVX EŎ 큏mVWK=fzӣJ?)(L'mlyްuMs,7fY5yqc~dwpӆJyG}w K;mHAL_+.}}}[\Ԫ걡@OVk@ O;yNu?,ˢ˞Y/WLNKef!&j4O>;ۛy_Yoa8nlv0OK=5hv.-54#@ebr1Mk݆I0 TRQT@sd /|iny) N ߾݊h-iWډeIf0xn iS '0>zgFJ~X:95{ĉ#GzjdSɾ|.חrc "R[>g@z,c0Pdc-ff"dƸ'Tʼn遡uI%!MQJaԢojD0F\>5_]B*Kc 7I; CX"$ABmDk$hJZ (X $o ]Ȥ3v*k*O(J.T4_͇(eبX A]׼gsƿ}+-޸f^TY7j`8&2L(,X??&FRec:(ֆ AcYΏ@w8tx̭45;F1ęfucf@d* kvu}&'O<^'PJcЀ__|VoCto(uon_5z~sN$ךsY W'G2zE ?|Ix[o߹n~ߺ hveە4ȋ~kB%!LNCsjqw$H _e4`KR,9>Ȏ9ɦSt6rHemh@ܾc"fd -Q3iB9$ !-I:mXKA bAdl4k^nUGY(ռL?D_~uk1ƀw{[mkSnkyN8fK2h^8ޞw bS{~{tE;>7>?^vў=]~eOXDs+xO]7Oн?sM?/T׿? n@L6m׽-?kF5@GC"ՊKqeٿ ץ7NtBGXd\oIWKQ $9d DdDd3Ιzd[`c^<6iIUx{&!M-/~ю_xw.A().=E6)x б1M%G=s[T30*K ťœ3YJʦL:6(E3 Q )I 5YZld&0h, Y& '*RlG'xp5gqo~>do6iq>ӟxٚVŪ;z0 ?xd˶m+i l'BHZM4}A\<`}η}k_Io|.F=_dx}>VQD?ж[w>ulWwt֥GO}WQ%_}945[en[̝V.鵶i!$Б \T:pM B($16x *2Ț㌰.rkK:}=GNL=W\}Y0jӵ[eLY>BȽ]eelO%zjnhRE\$gQT+Rq~X8|| r]/L b&U3-0R>׶mKJ۲l8v6J&~ՋoرL` +*bӾ vJ(QcK)41FQ BP)˛e*w||[3 FXa%h#E:C#4#kWqNG<FA-kֽݷZA`|?,XвZ6PJ%eJ RˏؘAYRDJB#YCo^rG+.Ri%o]+/t|\k׮etK  >19ojDBa8l]0[i"^3ܗj=uFL+.x;MnXU)Gל?"걁]Wޞl*ߓg2qYԬxʀl;Ks䣝$-#l^DZ f@i%\]HJmزޕjvОcrm|͠v&fLlbLưa4TKzI0m%,UnZҰA9C[ڳog`*ؕrkqL<$!F$|a]X,h%ABD{&dܧݪܮs~ܪ$~s[/}z*}:a\0QX=U%[ 1A)*ϧ#$I*D0!s Xby^cRJ"LNrT@.$!,(W~:,k.:3_]?E&U .`ma :nCx!L(]\)H$]#@\˩O}cϩx{۰_;E?tl2MG㉖>' DmmiT]3E=L&@hQԛpiBkkmi^bFYeh|vtx`vnƕ2кlj3rq-]lrZ/64wl}f: )k&̛sXtU93}õ[(?-L겋O}Xk}JX` c"pWo)թ(>(xp^:FrH8.Q(p[LMdLYS}X pRյ0!X"$R> ܶf'3rôLح aTcs\ ܑ⎦=7ݴeW\|>BJ!Zz`I7A۪j u] w{QQ_5ZW6To!/K!)@0ӻZMZ޿d.S1kLB0o ]ڹXaxNJ(aW)%H.k65:ڻzpo3lv\KinQJuAxnEIBh2%tJW( rhC;,T׮Ig(<7gNf$p65XplK>;q]-6E w5 )T&QE)c-1UY$WL1"B,o0B m`B=\I0S`@=_S96O|o~)w9L&`~$<xC4J$Ss?xcUkl!p3iعo5A/$ I^MБ7)ZS{XyIǪJSSCm!I&#GI mUҰgm)BJCcnv`8lZFǖ.Y17;㏌ PHS = ط uM0"|j0lll•O|WoMޒPx阎}4 11V/f#@ 禓  J@ ". ƚ\v.၄mMP\Kmqj C59ZK LkW-' !邪6~<X,p{ꭩJ " aP k:4SU;ID,ti;+Jb74a*T@a&OFb.AI0 #DeH.D@$w%H%Həʢ!GPwTy1 "L+~sLfO?4r,Ӯm;BY8!0%*J`y\ ʦl|ԦzG 9&>1uBHl۫!95ҷ&tpSo|2U|F6~o4fSY_> s$fQ/Fcl  H}+ߗyYRA뮾i69E.|߭EÉl>u읝+ b(O4RrAvl;BJz"nMЫMOEObG{;Osz֓X B:̙ÀD:/;8}o A]\aEeАhy- :<+ł9{G q(f_/c#S]K}yo>~C%W;][[;x+{G~IQ˯iŊU=<(ۮ-9K# 6>s=x*xO>>511P 4oQ5 s lD Bz OtqOoLldʱ=U:ֲ=Ff١e&h0'XLcL.,K& +P@1Kjt/ &`KX8$v`1"y _?K#j.7/pABGޘjIX5v%,V5)ܕR;+$M` y )Cu8R(H@@ U>5gv'DjO/v{;D܆up 5KNQD(h ` r#V}-(C"@?0a$U 0ӬDQ{3߶$K^;>U@0B@&pPL0#R`2Q(S0Ôb#DQLD0(!SV0% 2J% RFe !D %TZT%Ry;O-;WHX98gx赟}䖏>n:ikmzP,{mpr˖_VMB1@oyx.|m;;cϓ^gDǔ@묈;9 ^8bXqP(`)vvRWByΉyG]qϹFQH}mKo[?/ȮO|˯c%+Vߍ_to")O?s/ڴ˿?2L6_xo~lY[~f;{K}/}?L[uƙ^嵝MID!$ RiER[t2ɃGHrRhI t!̓?z\PhרY*vdSɖ䁹Ʋc#A]W"(H0BEXJ@0) 3(ea@3eTQ/c>aFB1F 1J&RETE!+yd(!޹p]g4FWj@Lkkc&.[~Z,Ѵg2hrj~Tl0-3yttWi̩&S3w799Lv77Q~jwu}({2hg$|3Guy.e/?+V-* O&SVԷd O>KgUCm{&J [>U}@ ]ȧr^aUl%GPb`JfTQJfcDQJ)&D%ÌP,&H"PQ0|T 82#%>< ۱`OɃkZfŶ0pȾ#, =N?=΅Ģ'1* \&n+t1E#J(GNQsoȋH B~1(EB3RB0S_!@ & PU11cJB %PJU`EnaF&1PP* eCEB)a{Mz6)ZRB/_߰ZO͕3ʕ_ }7>ەU=XO%MëB [Rʱ]9j+*Ni|Dp>rgA!$G.JM]s3 5"ݑtMv]/}{??:&dϱ+µ8Y`3-E \\TSU\h~mb~vՆkOY[x+eKaJ'XTkx?ďJ,Wpl!KDUa.ËV~U ["E Q Kk8 8>?GrElrw;V`kUQ4aB`&Q( eai"XU)Ayu~2lݭ`$ë ꫮ΄[*q\gzfXdS\ʱ.iRq\Y1a:caږe0B%OQ0Eej TF Řa)3 bjê q%AH!Tj8k~PZGFA0$!D% ܘ4-3Fvj _R)-rTְ8͔GG%ODap%U5 D3L@@WVAp..BQ@BH ͧ sM3 )LEQtMa (La e &V;kL BtUeaB@UBHm&޵%A\ģ=aL<6)+Do$O~ό{z3OϾ+VNdokmwroELB}jxaߏ sƪ5Vcid*:=e3qߌhs'mIC|*MH- B U)%cȋbLB(aSJ0Oɧ^Yc.$!X[FuԿLzV6ώgcgw 6{~qw nw/H"hk+;S> /=(KPGW/82}o㧮WmܜVb J}`'&M@ T6o"Qqn.)mhбqY H(O"teoۂQ]hDYzվ/W99W7g8J u/?uq֖7:䵦-׬]s9D|QM4UǘbB1Bu%%BbMC^=I7MrYD!wBfS!Rn,یIOezeg}+|؃Ǘ\d»Zp/7y m򲊙BJ c9 5YuTLO&B͙\  ʼƚW]xR!!<5G0ض{ÇiĠ`'Fv͖Hg/QDl\z.Jێ$?P8|zPP#.\Rghư|A%e3ݝVo\seё}T:/ohl Gb*MjZEWu jUA:W-  VFԀ,sb!ob6M勅b,irs.if4a\!c%V&f c)aB w$0H Lu$8ȑ#3;7GH؁Lv{{ⱨj U1c`F(&BQu]S1PB!TPh^ HBgP,[?)?#oWrQ%zCOso?ux%{ւd(.dͯ!PdNn\Im^vV:R-`=R-J~DKL[3@٩KospTQ"Oמix,-B{}ȧW$''FƆ0&O9鴑;^#TignI<#/z7PIDATSx&r)/iϋYM_)=ﺮ\ ښ_z/hzW~! SeP+jl\ Hŭn'*M}v}i Dr;h .BrB!?6!MqS-''+ӺnމGθT;un[k0ʸ #yYH܃zcƑң.$ QZ;䕝=B>DUomFA ;<77XJ 4t 7C3cR9-U*X֬](WkE;;7m1J$꪿;!_XǧǦ&3ʒjvP:czfrW|.`v.911v_ jׇDcg[KFH 0.T*9{\6!al6TJR+dSt)T S#,rLdXf]̯8B@9VvpJaR`)."aT(h=S -cN&'R`<6>$1U(eRD"F%0ʈ -TG (a%Tj^V3(?WP_Vgxf.^G:b⓿Od~:r2Bq'?˟)+8y~s??TSchpN?8?&NVXc=20%h9>ӊ減-꤅8TgKə)˝d:Ǐ !$#cr۳ciRJGxT&eWrT&{JL2-ͤy)PrriJsP(3{Ooty Zvry|ltaxdb*G t-9UJ'|-WNy3iYfVFr1;;z0J6w1Hb( fDF4Eu"rFfƧg[t-YtFvppsyKs{kS +f%8zg /im,fdZ[57? j+rsj<^]FBJb)_,bT.B>2cÅv,׵OT-dx#.8b*Z??pL1HDmPoT+g']gn>-P,,NWo\ںʘ Z[Z,$Bmp E)`X7Btʭ'ſIHQ7_G2P޶dn츴 .RL9^?uNU]GI!0B~O le¯t>4nN5ʁ]Z TN:H) @H)t7_p?wЮ;,]:<1r7_?.ew~vǩG?pӾ]/K߽ 'xg|k^CXEw_]? C-b͍D"0JrLj 2m*YF*`oh`Wd bQQk`)GK\쉩I?e%*K)9Q i8c;EaZF"t'$HcP¿tyj.jr)ppJE #RJ ]^1?=9=ھhE2m +x].ۃqԀՊt~qb$x^y酮׏uO첥KJqjb$OgDjft"$664%=>13111JFz%TInkV bZȰbbeB#`4kh._UxŋOww1Fֶs<9=/޷ȑ+INdRKtALri zVLXYT\RT*BP6ʕ|T* mےBHRJtYCHsG0iTuK2ҪXID0Q_@qdF[_c6U( ΥB@owc(?T: gy5Ls ', 0w\D9}r)}sps?k[x X}5sP#䴥ڻv)K! O=I~߼^Ց^W](ΎH8ܰbJ7 w:6ApEW.#Hjx{0`#a#X>q,I/}c&YP?饊̥2͍e0LfbIOW2J<ު[<1cS,8U%UK6!(I1CDM1 P]``i %JNx2myir*-u)P1$|KO=#?ޑhnޱʅp`SS՝xHHwƎH];8Ϭ^2ݺٹY?GnskBD^;z][׳tQѣdžz{z~d.37;Ժ_|dkks_;ܝ;^ݳ{Ħ k xSBHL)c ͚#Lqm5o<~"jnl8v4"+& X>N;v2 qb~Ju,#T=|aD8eipk:U^iy[.QR;>ݹE"i8j>U_T b61`"$]rܖ;(v*_~?Yv vBX9.T/ݷs.D(U JBDD!b1J%R;o݉Sι_7u]aZ$5/i-sg,D&ǎX{|z6>zl0W4nv,lYókϽ+z@r\.\v.97̢ͧ!$( #H$U]Q5(*yb[FPs|P,2 eg ydg{gg[1@kiYܐh C%(r_<{qoGOBEtvlmn5*N.xZR؆Y)W|`٦aچaY2*خm@"A_R:6vH] UX7-X @K.8PIp'F0fi60QltjVᶠ姊5ƛcEqщcJiKϿM'ǣ΅1J:0u) <{A uYIYD+J I,j~9F[?ҕݶK?]~{+ H"К3ӢrTRJǶ/$J.v(u*㯼pهy~˂%m-!grP,ֹ\xl4 LAʶe }aKqnzp٪Mҡ!+Łx=v9DX#.\Fs ,`oIa*P4_Kr)#cVgCF6I ZRY|tK.L 3ť %H)tvmd3B &ت Vo1 ҥ7}=Tڻ;1H Y+5Vʰ@"VBI BX:ҥ1B@hyo{psKC$q\!  ˊSG$`x~o/l_vζHSOzV-E%` o6S$#[QأOhhj^fiKܗM޽+ѥR,ۂ^L6zk3ɹ险|dqI}DC{ӲD, UPOB)e#'1F(Ř̅/dF3.2t豹EM ʤ=zŭl,ѲdʦP0*[.oI =V*_b^u;?%BJdt[6*|lmٶe;e,cY۔i;e㸮8BBp۱9 L\K3-H69#SqVsk2#rm`9$1 ƂKzb+ҥ--%▆pq0~p(Bc']xkW(s"PH1! @=TW9(@d}3rH$l6007d78o}w?{/>v~Ar-3@3i,όI{Br Q@0 (NBXI80 n7mVݼ!#X SǦwvI}~[?Z})͋zw?~oٚ7_]~C>=}yO;?)ZuM_lljCwyckO:-o~on5?{7 D0 b?xc 7[bT2j(HqOהURa"JIHv$ b:?x|s6ezԇܔBBQt}57sb\脃.$;B݁ Fp}h/oX,5RE˜b SB' 0OBR\uu]WUF)xsk@(dR;_}*9=Ŗ*]=+#D{op@?û-4Hw|me'_[ MLMMMMM$g /v656D~08wc2KH ,]b) !p0ͱビtTnlJ_!k8#%GE--횪cr'zH؅5Vd+gmoGW}:25 !,du"k.ii SjeYi+4$ 9(k#`"m"*8qhr)L薹Cw)Ծ]f9c_|\ѝzɇT6($H7m(WP)ktHd4:աX|{rJw\B%jXKN_8qxJ;`;`SOFj˯ gm5Y9矙(RPHMمx06rMO[믦{h6+V݋TbBU1R]ORgI$AVxU>@%~c"P}UN0<]U:.T@R2Reʜ"Bq곥\2MM`\{Okzzc 8r`p"PGX|ɝkn.R,<|`|jR. E - X((A{HJ k6U  y( \ !2Byɢō񶶙L5}]-`зtI\6+玧c/ .ڭuY~.}JFX4L̠ڗnbCVYpv)*H FM#n7&Utd@#I}PpKN_༗u]k>s/ }0=t\o+5٬ۇ<7#U}^c%8g]Azry@2յˑHm[JY|YJ UfwJ l7::ǧ&-!9ݵ(- LԔht70=N_|'O=jۡ}=7|ONtMW5&%|*S}>- }RB 8ν-dzΦyifZrBAP|I vxh!R*}-╯>y)}1}La J)!B1& cU%cI؎1BYCoBB\~ŧ񑑡Tux4a@۳sk:=s1^5_z]K>y$}wدrҹla:8\T: a˱A_W'FbgfǦ*a/VuJHXәtSc$ܶhp0rWwwQN޸YԿQ<\J ʳMbTL|B#F0Fu\۱M˲mv˶.BϪҕR\ 'U@ 6H(,0+A (ZQ8\"> 3_s$HPX16}89|+bŧIL$R%BTzpZXCϛ[;@jAVURH:iNE1];}l8߭= ?vjDssbY7NDvJocܞY4?6 ?z!hlyvt[xF<_Rz姊Չd tV)e&ׯ6^׿۞v]ksO9cG>䃜-}^tE{- F.P$b<\rcxŗxuN>yw/Qy? XGw(~][ClmK6x9JEMZ|ϢD.ʊ[ =xRbINJ%GOs(ZvVmtOCpI:J;}t`zOՃeD*m,?U^J1ti1US(%ԛ aLik #2OF0vk?@ U=H=3OfRl>ozιS/ w.)/}ïn{f`}+?19xQ]qi°<3rjR0F ׵l! &Ap]A 8-6MeCщԜ#xcc:r 4ٖ{t`!S鉑޵N+ϖU׮\5T,po K.ד. !.eYcX8m˅\HsH!/s&K4UXD'>tէ+Xer0gh4ј+Llh`\(\NUivZ;sBڶL0\<6w47*T\WcK^"@:A-; ! ' ~TrW[}zt@#D !UV-b;L ! M-C.xx WzޱEYߛ<26;?//l#YRH]OXŻ9~֨MZfJ+}Ḏ \zZS<`oyM"7>~s_M~ z_vw~[vltYk٦DksWj?޷?9_o"o> `3M\u/?:.F>)HRs5]S` a_R5SB-ۙNfmW_CQ8G2e[ֲfSw˦*SGD ?E+n#ggϺpHSEcXʘ'UdXM1BrbF 홚mkO^=ڻ>g|ɩ\I FV,]6oe3>uEʯ"RR )00,8ij. ؎`s\?%H)Ćz'|albX,u-3Ñx016|!<{K~}{ML*E(H1 X̧^߱3e1UHnAYP\̦|{/Z?ql.oY6 \ h46-$xaEaZSdž3l։[L-VmYO{CScR)=hxcD01v,d /OحuweʾJ`77)\ @SՁ&cb-US5Uu] |P$)4}>u8prEsnZ+-r.F_m #a_8Adϟ4 1Y/Emj \DjρjG֫.>&S?䶭g^t2)+?`O&.BciE'P(`aT"*ahú\|/>r?u:>0; I>wѹ/ 7'# }뻆G>ϚԲ:M8 zd>_TJayL Bl]Hc67˜jŠ/i/\Vw9T EZw\}b#~ݯ t뺦j> C~=ZLx{˪(d*[.֢G0h\G}{?3bJ,GMh$xgmO5/.\p*};vQ/~mobP.8b]W~ͷow]<*[J$ t29R|0Ynk4'࣓ɥKp,7l:?Lm}vkj'"?{}t)@,UM ,_Zl۱@a2"1ZPAM FpGr;%E똌J-dA:tb֍9Xly$oE ;Ծޱ_X4|[Mӂ_y)AvyuҒPz;xk X0x&`R1}Xϊ#YZ4Y Ï>gy(=ME9tg^LϹ9`Ͽx2 l:ZHrTGe]bƝr^l.:OߗKpk~O1!_qu^{gXO>+OMΟ %?<ڢǦ.JRdZ`hd,/WUE#)UW'ry0NnMq8fq@CSkV,2Z~#/?}MTP(PBԪ(BR=11DJiI &\CQ.8&M"$䮙2XߧP"B)Ƈ{= E~ڦMH_|׸nHL%FÍD"iQ5VH&wh bps ׮{e!LJ醕k־6mւ 1ʼnW'.|ǟ7mDVtt!ggb)k,3F\qs[׽c׮ɑ|y5B<D`&S@Ji(%R@}|^7o*D'YYL",)UTs" p%BTCUXXzRTaEa0D@fc^Ha\!OwO=7vҷ-ʗlF@̤٦2 $Ŝ4tDrZ9C*QKStyp`i7~5O>|VjkO]{.uÆtrd$|Bv5&s~=J\yL4o9u㦛oz;v4$Vӧ>wշRO9eӍW{+ w|wSs᯴@%nV>oJ䔭OipEڹfyT`!HJI9J\)%j$}/#k@5"%H!@SYgK)LǦS 12ٴG i\s͖I)Ŏ^5)G_r`|&hڶÐȧg䢾u/|f4P `?72T%+'k+FW%#5F7GUX^ ;p-P˅[Donv╁n3k-#ij+,ߢ@"s$gQsӑLY1C̥~ܰS5^Tӏ==8g>twtFGw?ͤ"زR.}~s^%>co;vKcCk"zhl6:NSKU1So<6:bV™^xTab(+cJj #݀ߏkoCF\N)ڛcaxgkXg!N[jDSMU4U0Fg0)¨ª}31\G5 086HP!3Xcǚ {_ڿ$S5 R)W45{#]eXOjx,M$@HQcsc/|3O :DHaBNq}djE#T!\X7 㒫틷Ysc|r᾵6^i9N > Q,9rX?{;C`Ű9p4Ә+M.[']ʪʫSjFV*{ћ-JZD\p.lg!0.QU;@0H ].\/\z\'/T/ҩ]V ,'vkCo[.6R0gsX9[v;Lt#Q[-V_ޟWOKMǛ@oO( Oqf0$e:}[U_cm]Ρ!bͽ"J8x0w.}?87دW.w]|njm19v`P}]m"x=@ q^>, 4cEG,hA۬H/RdG>êP*ߌԥ_WMkh4MpccF]ae4կubp8kʨwPM;9./|~r\t0$A9W].lC{Ѹrº&EoUP Όrpx4ܿi-_H&aOt(J3zVzɱL+z$cMx4*g 8ԡ;?x{= O;~ss.Bp~]-J^JմJ1|Wr94r۶䬮b!),CiV.*J3^dw㙧>0ZdUH dXp;3zoP-C?~0.`BT1GL ,mjg ӷ|o Ih>ioDE› /-nG8WUQ{ZH>y,^(R@$A`:TQJXc,F.姏#ʃg[hgoXiuIbm;" ~ƥkfiպS=K $=Gv-[=c7!^s ܵz-Yfiu%]U!!_y>w|k]V|~ۮmc 9ӿ-OZhXNl3UabmWPJtUjiOSxex~ 85t!D)V1FW{VgUO t⊩e;oI L˚T(ή^ee9p.h6539]l\4I %iCC,6M񸪪FeE tK7mڸziD(ݷ}|JRV*ͣjم1\|Q=z+;@U~N0`\~槓7S~ЂA[3N\8j.@XDHt{={TJP4]Jmlb"ċOF7)l, OULD1,Hr6GAc-j'n]:M-mcCԩ *zgUZ" h6w(ҿvc2kOpYg?39:xt?g?oyLi=hὯ_pEF4pY^x?O| =K7{YWW~ֶkz[Xt; Ip0qEuji1) dqP*1_U!ՠv5_)})-GmUx\'A &qąo'/KA og#Jq$綏iS<L^I 6xp b-T4 !#D,knqctUG+/kq*#ϟ⟶mJ;/QA#M)/RdD.g^5|Qr{E6/ 57N \881]`MNGǢ.jkNG,U*Jl9n(/j$ h㑑X,nr=53\s`cs3;Ҷ Lq"+Tz03踁K7|}`z.Z\sc,;:;;-w]@Sէ_y{aTl 3cC33D LK[zi+](fF\ E04yA^Ay,jqz7h& ř_c+dgT#ⵌ>z=^ _}KU^Vjig мԂöA /AتPR4ssӀ0*~кe+I=WKV*vl/E))éٜ߯C!!@$DL54477%bֆH0E4\MxW! Vk_Qn>q]lXܩ>o~8IyW C-^-y, 4 p#Sav6׿R]'9=­Z#hOX7d >3" m`HJ:ԦL8z] Ny4%SBƕDjjF!9 mAdMHB0|tT:]>vXKpxxMw䎭(뺚Oٖt/ x- 5D"QmXP(t]a Q”tv[td͝}H♹ c ؑ@(T1ESϧc ZmAL֧ftz`sS0vG?}B!u- Ux W^|n|!.ܱC^b陹ɖ%]P$ C!Ea~ewSX[z{Ж-]ߗ`מ{quK}QJYBK[VP LlGw0lLSԃv TcB@RTT_c 䘥cGRJJe:ou/ | T GB1"U gC'֡\0` ` ZBUZW>ʻ>tɅz"ԲOgV 8HW." ޷գwME p35ވ T084<84zڦ :҃׋uփWw]*ô::Z?w>+CG,rCHXTESF Nz_oO/FR*Y!RRp^:_p庮oCq z:]. @  ʅk}b&'G$l:5=҇+\׵DŬiMJx bYuu5)1B43c <ݮ7]1G0@}kuyO3C,Z׋Pel-)eni _+.d˖9u ,`=`!.Pݰvh-Hao὜w^:߰>ޕoZ ڼGŴ0"tP~20g?r{lR}ka aPBH,1qKcwl+P1~񖓸L0 R `,Q<y8zyOΧ?=i C 4GGƙi>=}gO=oF߾) nM>s4:2 &([5#h/U48mM P$e0MͤY.QFӦ%=5 .UL#a.@H"$A oATF2-0;{V=j>x?]\(>K *QUwK{v`O23w A_Zq1񀺖<᧖߷oK"˟Ae^nf<d9Kew?n&M‘HT0A-)DDT,V~Φ/w_ʧ^~}Ko׿Û7'\~<_0 RH._dzo~_#=0ʰwSe, |GTQ)yy|ypXc+HJ8N\}Z}P) Z $f TU^@Br/#/̹N t!j@&IMwK펟ݟ~cV DaR#o\*\ ]-˦VtWUA'Q݋<0%457+ ,-fE֭44z]uBI%Ǩy|jӭ^ {c኱/!Iv8p uhD߼ԓRt\HJX≃;.TI HJכϔ9k۫V,}pf:=9tRΎ6=oko|M-Y} O d5]byꊓSsӏ?{,/G=b.==ڹ˴w|0bW,ll6Pg[<&@A8-+Q)8wS\{K)6&5lς&q _=xQ䣏<{‹/U Euml}uv%kWn}6֋1fO]mADTQ vLN}߿6:t>F6u>rW'fggۛZeRLbOkZFIRUELJg7ޏ}?詟[~͍@s\'?ܳ5)NJ ) E%U@r!};;>{hd`Z>@'ZZi A"MeXrYnv|drH8L !$,.#^QZ-BVkxm| />N!ԕ˚zK#ْJ#̫4DfŃr.bT؋aD ֛\$pEM!퀦?|\/` TRzbpD !%_ܬBULY.STF%I@J8.m޸~?85[~_k\l}z|'{Ox4thSڳϏX(PtswwnkDJ9sn$1?n AN(~:xȯ"Kף?9l*KOqnmB0FXιK(@> ³ 0v>7F &pm>LrEW±-T[])=ֻɘHIH"MZEȁD"+_fWv0:X@Nh j~482[/}9Wڽg}}m\JwNLpG!Ue *}zm9#1 !Lĺv N[Ngկpwoֽ, /կ}鬳c؎ 3-|CZ"}Gn|ͯY۬TqC*0FU*T]|]6ɹiК:{ìdE**]q}>9SLch0BH!@&*cO=-&nzggmqK:{:˖t.o܆M3â,Tl}Cnhem- Rh #=.a8BQB @ʶW_~-F&7HFF' ,9loΥ׆%!v}>M5Z 7dje-< %3G~sq!)4BDj@f#B! :\i~w]ݧtUTUU|>MӘPb0&+>#CC/2rNe(P0 t)8i]ǹ]y}ՖM{k.:fDžwiNo8|z2^ܖ`SEiޮŋ`,DBP0X4 CP4hh/⍍ `s6U {6U cBR]WUi/: MֺfmmKǻ+_.)SA 6A ԾuV}^UvzϽyyᅱBD\;K\b]( A ={s~Bν6o000 3-'@Ē?)ssS%v>xo>YV ALw3q{2syG;Җil+J{GJˋAJW/]ܻ}ȱ- QJ}3Ed MM*">:0@B ٯ&3v}=z0Fg_Ϯ[4bv,iRdHNF cwmv-sE6wWo_Of*6x>]u`@O0ґILv'9HX,ēۺmxya{o 3AJa n#ZpYeٖ̘iGBcϬ֑|ZZ\}NuK)=kqm9Wn/owO~7 BEHl{90"ؖL%΀qO `M|fn-YY]ɭHGu3{ sę3yN"?57qζlW{j5ud3&" jΤ_?f_gG_oɷ. 41fq=7˴͛Di*Ԫ3%2@MF)DQ>èG sDB C2,2eGIi00¥m޼qϮ7R`eux'LipMeQ2 x2m10B^.xf:wl y)Sov?%;<@:k570Dz Ɠ1$L8 18B DoV,@@QwO[(iiZjm˦!V۸as&PzDrݶ̾Ύ۶mٵitbbJc!EW]t Nl<O%]qZ-Ԧ;wmͨի/SO)');wuw3l0ɏ|CݛF;۳t̎BDZ`4߬V*hP4GVKU?mKiɺ]c7"0XCFKY}wة}aBQ6L$5m#>!tfiuyqn&Seّ D~-Q\Nn5-{];IO҆ 5x4]=~H4k+==[ͯ|k|nnqLzvna膖p 2B~W6ozW۶ PysSMOBg&5cB * HZs#PkiM qtD[o051@"qܺy_y0 :{;>ti)LID(,Y(i ~u_VhVj6zU ΈG LGpZ3u@'ns0oc~ߴ6&鄳orv陥_7yX+s޷.0M9P"tiK >KKvɶ#CҼP,e2pO֍i=/ZkE]mԁ}}SA{L%^IdҶ`3\]9zz-q:)ָމ . l&O-N5nhܽDyhčQ6.TWWuǡr~Zuy߶=mOf@Emn՝~myiYF*\H) )9RQkBb;0[BwMdH2crl]kdL[+߽mm]m o{\ǀIL:ͤ}ϟ_.-y{Zsv~ʵKٹ|R!]}ݻvN9yP߀؂ HDՉ܊瀩\;ZG`ҪVw34-h,3Ф@@ g K>'F7A"j>]&]'N;rqsb?e1ӤVJ]^z6ouzܹSIُd2azj&^Z\:9Oə7,L+#y c/c"fg v |߱cms4B@j@Tx[Nr眯5kn"|ϙcS/{0 ӎB8JHSBT*U*]/(U+QjgS"GXM$7Da:[Z*.R*9;3o?w?`ZH$qvhTܾ! C!-h3po/f-ȐjV(VZ ֶl{[:f[0rN%ǹs݊빋3._4pxNКTk]{bXRiq|4Ll?[;Azo{[.'-k9IyͨsRDitH 30fgx[\H)dZWsǟz\dPT2 D3B:W. 6*\)8{cwqf'WdL^sw"d/_޲m綝7Ưٹ׿Ks#gPH4?X*_ 9+\X^ڻk:D􁽻_;Ey@\hڱ,7@@1Nie 1 ͅ_"P@4/81v([TC* %++'O\J;;Ĕ_n iTZ4Nsg۳qcCUKkWǸZ]r}hF@ B@^]0 *3n3+KCC0pOp}R$r1ajeHk뷛&-Ш,!94Xk5 &⍦ 8c?OнN޽ي׮-,~ۍ1ƹ4 ˴M4,SRHiCȘS]vxAXVlnLX MRc4U e2* "]?3. ugi!D_jWv-LkVlZ"V uo ycGL'f:DT֡RQҊ3I!j7R0~ƕC[Dμ[Ǎ6oԼBY+/T\[\N9Ni6l6 )=o~LfM%V:d a)m#ǿdnqβc1ƹ&|TȁJoocAi;B8AA<:5I˦{;Nvwu8Zw6ŸXc04q _>왠_WkR! Jwe0W&rn|Gf6-폦 @ XvnZ*UJU2t% D= 6k8R32*W4~DD)E !qɫ^B.Ii'n\~Pʵ#ha鈐RT@yLoOqCuK$%@+Ksb"Pͥmm[ZT!!ev{SʢaJą@& 4;@GK hm3o_I7Ш UҍNU#w^ܙm&{&g mabd]_-//]9k +N ۊ'c4 ðcĄ(m[)-a!2m X֚5Xg ߷n6d l1\^R!Ȼ禗^4ve5GFw2ƴ9`]Y]_ŐZycZ5f2@i#q!<ji|>_*Ǧj}wߙ|S@3]m'ύmppΑlgb*ps]#%Qs"~嗟?xYփN[-ij299y܅W_} _ _}ƍW/_,MxcАgmpJ/ 908a֘۳ BhoO'2[l}Ksp53/לxLJ:$T:9?#ǿL\=| ܊0eT3fo,ܫ#?M-Ō=m#tHt9)֩=uF iD&otai2%"0ȴ& #hC B@B6V8džgk@D"{9b0 TH3i֠vLܰVH7g⫫e]nɁ=xA`Ջ[v}胏]<{1ݞ^^qo}yĉc:pRg:.]m߹xWWGZݲ|+M33ªV @Ύ%d>(Ě@)SFbVkt50j'm![N{:*m޹yO}'+-L[2 ! CeX!M1V6.XL+Bp}n]+7M#l2Sˆ(xlyqޞn Njv̮mԮ"h Z#hmb h[o؜ګ0yK5C_zs~n2AͫB<>6Cojy"b)זf+/^~•]{u&Ƹ)$gE?h @(@Đ)P߃Sk~ڳᠷ[e24J1_XMON.LE, ,†j^s{_uȎ-;R LLFa[G/@Ŏҝn$1^zjއ26T ٷW<{^pN0K u+~k2-3ÓG޺loBx=^cϟ;Q{GRZZ3sv?K\=}Os@sjKF*܀־uµi}D@ E P'hX7F@10kzjx6|hMK"=۶o9t x{:pbN3LSڻNL' 2wMtbN{G[\ \?!ȸ9񎶮Xt6^=K'#q=d*OfTډYҤ5פ\w{#n=¹M[7O}Qx4M׻) Qi<׫m%iiF`p|&&>p؋j?ݵ]jї.n=H9v:*V0QmYcAܢ_/o84V뮔(5!Ǧ>xT͚ٞ!X]]Avѷt=-! qvY*nf$W34iaˆ4|nRi1 $4Q)L]={cJAWjb[^Nz?tp6[Uj+Z ܸR){+5mumUUp1RDjsPt hmc i(3goAiE+=zh}?yѡsO dKAs1vny7 '/t-^}g^zkhG-2r T[ؾc@roy sǏjGǬd2+o%e*Ʀ6)Vwp!crn^|9+ݢ#Yx?Bg>_o+woVf:By X-+5|XOZg ܘI 5lce5^8x3"bĈ5-8#ީHk"@&DVZb͒'eBaXUQf0f5_^5]ORCr]pyɯTe/dY],p.D1oϙ$ @_y)*tr!qwמܵTGD`(`:@v@j6jfY@&Wx 5tu&<(`nvGy_ȦM݃}LLa[&ٺVL3Oib Pr ae١RX~iqryT&2i!c[ |*&Qq(7MC2OFjr]>ڑלPVruxÆrdKoڼwC(0PJR*D#Ѹ-35jӤ[ib!eVzc2a+zus#ўnIKySj4(l#me pu z62,ShQլM:!2)~6N~X6VzI-Jzy{OO@7n|yhXLiôLVsSX㗯3?q}e{]%M/޾I$ݲqjbZ<:2Z\ sxs/{ŕ/r$IDAT=tSϝށ;ZzO~w WxbJ>X_Wbqd.-MKrbǪNg&[K'?|yP}j؅uB֬֕ @n#cf=c j [6vf)7#M#'l!ΐD^l6s,Z(H`{waYn sÀٮPW܊=qTJS`LkB{^*o)0LKBp)'.+\.;wkݩ$Jɀ0Giو`Jɑ38[xMknf rlb:7E8'UfaPxxO#ǟGka;W!C|eH#jv‘VA( ï槦VJ\@"IuLIe]ٌc[M*AK M\jc4hE;6ow둋MOgNٴqS#T\rbjږMۻS+cN7[ꞆH>| 4:~+gRPPzlt wPG"1NWt>++D{%\<~}lM)sͶKSʆ,5i\DrkhѕR 8mз@2 ˰yΝۯ0jE k^Vlٲ;sة~K[^xqdv1sܖuڸcBG-Z{%߳HS ˙j~ynLuSn\qGOVıɖN|z7X<ѷaSٻ[}$[5l<0T4o1{!bD7 Lpkո#߁5#8@j . CP>hM4Xnz2(WwW)cPxc-&j>fRZ\H Y6 t!u\I &# \@7Gd̀LJp$3,!2%MԞJLI7E2 CJNچkD|hdrAC ΣtThXZjkPSTh J U z8yn_\Y޻>7 \Ja P8~nXxpj~pqlg{;~|XY>udydT3۞'Z!^&5uzkLc|k/?O|td_r)+D*uLou5`" udowmxpׁJ ڳkҪ=?ŧ s#O}%+MwŇ,>aYǪ.#"]==N">39M?~ի9}jڹ{wdJec*wzϿ~!JƗMYMT)*@@E~& 1V1q0DS7zN4V0U7)0$5iLZ7tZ+BC@%F@ HdՑQ WӁ(F!@я_'ʄ,.[̮nɉgޡޞ =ýv#/Xw" Z]]:&;s\1{ɸ4MӎŕR o'5BE6-U+T~u9TCzuO+/_LN,0=ݹe$m0ha"4qה_vcUGbB+y/?lHڱ၍}iFV"Z]w'gL&kľ4?ӿj`p?|/_ݾm͛Sȓe2[6oAͱ̵\{!_?nҥ/Omڸevgǯ_[p'z+9{qwܙs+"q/< qIu~^ϗ aPkRiN<mo\z;{G/_tnsB \y"4{95JRRޡp89+o ÃVO_G>Rr% I0_KRn9񽧸a]|?G/L=݊ M'bq#Je7J- lH??34G7 v6hlyXHxx/M3WScW@wUXD Ҍ1dFh2$䜈4\i #BFHg .V6MRsPkUjh2 Z3EDHW#/r`!(EmpD#CH8a4 Ci~ y6i H@{PCn(f0%׳-Z߭RPkm[o0ZE#$@o|󷕐v2{~< &ڋGϜ/uubµ,/zz{6 wwe icz5{"@j۲{ҩ{5?(+B%i3 S}6ĒL2m[6rZnآNQ'Ϙq5M?Qqr2۹}[gGW6VN;_׿N@J)0J^u=.~(&٩7o$Y\w=l+ްڑ҆|;ggӱTaVJkDjU]Y~ldpW~R(G{>KY1`M 2=ƞQٞ̈1 \\ykf]֫~)ԍ7j-!)D$@OȐ3Z zqƐ!gHB 1,@YRA(WZu϶MDkHt- kzk*ÀPZ'JXWE.2ɢ HSN X#T] 5@iQBPN4]#z1gL7 3p,Ð7%-xC>)& 3!C:W]B[||ti<ٵ<3vˇ>{^k//- Su:;{҉8[}4/ii |._)Vz0fIb@Ba斪J|@1(dĀ"+TJjJ nܾw6'{MfM4N%DfH`fܕoֽjկ-Vdw-j&V󹕙9$jOn߻kMa4NGzj,ԆPahvz>W t LϭJRIphߞ=7 H{nͶd;W=#Gw}r\fX{&MldXY&&Αad ȄB$ΎRP, |~u^+_sL- nfRZV ֫j|ͣF;R)A&_ Lr/_Z*{ªg{{z*,rS3 l0eFƺX{M HК;. !0Ah ݇ŗqw׎Ŋ«ol߶y_vl]<β,ϫ% NMNX̚^ϝ\sV)W;1<e4Ƚ!8@$o=Dnaraa>?zٴLV/@hغklΌq8ݶCv<\nƑҥY[L{׿'~곣vW5pf  IEA"CE@Dh\Zۛ!C2 M1H C4\pAS 1@̕4Rjn'SCh@"(A& H)d\3j #)>LSȐaC#GRԑLC890P BiD!PX!D$5u׻ w4 1a B@''Ο>G?w=85P(U޿Ӝ_|__9ٴo^>uxw磷Oƕ +JgQϪk EqaK$RT~( J[]]=ɄzcN;= ҰFcZEHM\٘;6;xj(Qk4Qo>tSOGIB t j9n4>&$PQ1`Q4*'Қ)FL4Rk %(G)--MM?_M#$5Ƞ1* 'V;EJ؈-xāg \.:0"Wid(c\2&0CJd!8x= 8Z-niF7 o@J0u7\m;246ŐN-{o3b4iΘ!x,֖TRX=yfea&'ڦR)_(kb-c0"4FTJV9T]Xp_߀in4oz+Y֚*z2.^Tϥ.@Jk?@Szܰ).-_V M#;vݘ^rށ};/`J!T='<7g6yTiqnh&nLs;>=1~>I;}I[< _ S#Sy/[Rr.\ϫVʤV?zmٺR>wRn`hd OSM dPk@䀡b(h`4F|Dja @∮DXX+ n"'i H"\J5itCanM-b\`6K*4 ΏiY@q5L-+br6ޥn(ET`ttwg-Iگ-E Gց]1LhrYhzqB^R֪U2Ð!MB!1Tuz^-V*j5 BR B paZt*ۖͦ)Jnn>W jk4Ƭ|*砕IENDB`fritzing-app-0.9.3b/resources/obsolete/000077500000000000000000000000001272363601300201305ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/obsolete/error_count_small.png000066400000000000000000000005571272363601300243760ustar00rootroot00000000000000PNG  IHDR ]PLTE__oo珏꟟OO//??'tRNSO/_߿o?Mm7bKGDn pHYs  IDAT] DժR,?؜I2,M ą&{# "MC-otp" ˆHr 2e++|ŀbJ]yCxIOLOap:?1  `IENDB`fritzing-app-0.9.3b/resources/obsolete/error_x_mini.png000066400000000000000000000004321272363601300233310ustar00rootroot00000000000000PNG  IHDR wEPLTE//__??OO񿿈i tRNSϯ_?/-bKGD pHYs  XIDATE[ D" : Rm!Ù Dˑ<,>8HJ@fN:6tѳSZeOF$hDv1fkzIENDB`fritzing-app-0.9.3b/resources/obsolete/error_x_mini.svg000066400000000000000000000020531272363601300233450ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/obsolete/error_x_small.png000066400000000000000000000004271272363601300235110ustar00rootroot00000000000000PNG  IHDR]R0PLTE__ooOO+etRNS/?y;obKGD Qc pHYs  mIDATc``RRRdFb C !`!U3MZyȨ>9g;Q~l9Q5r0c=7۫l'00 ,Zb$lM C/82]IENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/000077500000000000000000000000001272363601300227745ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerActiveBottomLeft.png000066400000000000000000000002111272363601300314160ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  -IDAT]A 0 _N!ɕ(qNI-l= IENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerActiveBottomRight.png000066400000000000000000000002071272363601300316060ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  +IDATc?d.A |4Ō(  IENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerActiveTopLeft.png000066400000000000000000000002041272363601300307160ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  (IDATcdπX L hq.F,u^IENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerActiveTopRight.png000066400000000000000000000002101272363601300310760ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  ,IDATeA _֋-$28OS;征FIENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerDarkBottomLeft.png000066400000000000000000000002141272363601300310670ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  0IDAT] ؞Z`|=X.$eiyG?_0sIENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerDarkBottomRight.png000066400000000000000000000002101272363601300312460ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  ,IDATc ,0eP./M1#bPi IENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerDarkTopLeft.png000066400000000000000000000002101272363601300303610ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  ,IDATcdπX| L hq.FtŌ0!im|tIENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/cornerHandlerDarkTopRight.png000066400000000000000000000002141272363601300305500ustar00rootroot00000000000000PNG  IHDRn[XbKGD̿ pHYs  0IDAT]̱  zLˀ%,Fw.zXZIENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/crosshairHandlerActive.png000066400000000000000000000002621272363601300301310ustar00rootroot00000000000000PNG  IHDR bKGD̿ pHYs  VIDATם0 ߁% x8;dQ`|N'vB55W>3n = 'լghp- >ݵ,oIENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/crosshairHandlerHover.png000066400000000000000000000002611272363601300300000ustar00rootroot00000000000000PNG  IHDR bKGD̿ pHYs  UIDATcfHI+\XAA,  7cF$ PMdFf8K M/?IENDB`fritzing-app-0.9.3b/resources/obsolete/itemselection/crosshairHandlerNormal.png000066400000000000000000000001741272363601300301500ustar00rootroot00000000000000PNG  IHDR JΛbKGD̿ pHYs   IDATc3BXL 8!FBT4 %ʽIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinAddPartPressed_icon.png000066400000000000000000000007751272363601300262270ustar00rootroot00000000000000PNG  IHDRاPLTE]]]|}wxzqrtlmobbdVWYPPRKKM||~EFH??A99;224//1VVXQQSLLM>>@<<>779//1//1MMM~~~zz{opqghjuuv\\^~llmMMOuvxSSToprNNOhikIIJbbdEEF[\^ijkgghdefbbc``a]]^ZZ[YYZ99;>>@99:**+&&&MMMk-:!tRNS____Ͽ_?!bKGDI| pHYs  IDATӍPDV9gTT`T9_$SvYtUWA Vj'FcNf^ܡDF2Ś(:|Yic]غV cj`X**!u&zY!Ce-IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinAddPart_icon.png000066400000000000000000000007631272363601300246760ustar00rootroot00000000000000PNG  IHDRاPLTE]]]֏àx!tRNS____Ͽ_?!bKGDF pHYs  IDATӍPDV9g̀#TI);, (‘(|` /&SڈލG3)9k~&*YEi]bݸj[WwVݰ}:_>*FurΣ O IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinBinoptionsPressed_icon.png000066400000000000000000000005461272363601300270300ustar00rootroot00000000000000PNG  IHDR #2sbKGD̿ pHYs   IDAT(υб.CA$BBb O>lxjlІD ޶!Rپ/|a-| 0,O歉i%m\@>^E0Pb?K:ڭFk [RfKUvzuoTx(L ndr3/p_F6և XP3H(,$:ڧ*HCTO{zg<{.xgI 0<[2n@ C1LzQISQ6G٭a^K+R%m{գfRuaμMQREh2y 82[_wй/~S_8}wk4uuAI, NLE+DZ;sH[;?%2I1IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinCoreEnabled_icon.png000066400000000000000000000004111272363601300255100ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  IDAT(υ=@Flbl^KQx`GT4x H,Uj$#Q8{0=O $R 0fOI#1` [!qÙsJru9dsdW'Էqt8=R_ |x 9<.<{E(cmtD)IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinCorePressed_icon.png000066400000000000000000000003761272363601300255750ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  IDAT(υ;0D#9GB.SA)Yn,YYg#2v f;ņUssshnmt??yh"R:ɘd;Jbh=';AtIF%:=I^~&`,V}C)j}p=RE \|6SA|C-NUi{r1|[NLLF{U(zv$Wr[!S&?}sW%Zǿ.i̟Oo2ݿ"zԬIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinDeleteEnabled_icon.png000066400000000000000000000006101272363601300260230ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  ,IDAT(υ1kQLDvY7Z,,ҤGR A}J@,DA^& }c˪9fO~ N.wEʣϓ%9gُRj=zBt-:CD2IM-QZ3~KΛנiVQ:_q~W5j"KeBEx& gѱzv W5avHPjq"d˪ ϕ*r(b@):PƷO_U}S Niwh韃ǫ$lwyx#IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinDeletePressed_icon.png000066400000000000000000000006031272363601300261000ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  'IDAT(υѱja_&Cj@:yNNA'>AXnkUYm v@JmRdw2bcn{sν['{b/`yQ%+^dL?9(HT[I;2nW4JcDK"9iAl; ج8MPct L4]_3]<bVSʭ-g:w$OhVJl[OAdJTQ)iqbVSʭ-g:w$OhVJl[OAdJTQ)iq,1ϟiAڟy~e?(,6IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinIconViewDisabled_icon.png000066400000000000000000000003211272363601300265200ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  uIDAT(cşe^ȟyAx>ae,EPz3 Y)SX ,^`)f``G2GL$e}a )< } +~'NiIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinIconViewEnabled_icon.png000066400000000000000000000003171272363601300263500ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  sIDAT(c{1};aU , p41&d=Q QX ,pє5`3001o!s L$p[l>,1ϟiAڟy~e?(,6IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinIconViewInactivePressed_icon.png000066400000000000000000000003271272363601300301070ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  {IDAT(c\ßy^ƟiA{aE ,zt  YO3C3V6 8hzp{ØH𣙉dn+Q Bއ^/|=*`7σ? !J6 WIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinIconViewInactive_icon.png000066400000000000000000000003211272363601300265530ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  uIDAT(cşe^ȟyAx>ae,EPz3 Y)SX ,^`)f``G2GL$e}a )< } +~'NiIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinIconViewPressed_icon.png000066400000000000000000000003171272363601300264230ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  sIDAT(c{1};aU , p41&d=Q QX ,pє5`3001o!s L$p[l>,1ϟiAڟy~e?(,6IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewActivePressed_icon.png000066400000000000000000000003721272363601300276030ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  IDAT(cTπ<`.ȨOHS0AY( v¸ P^a1D ba2$1g`- bz#>ǢLR^+pb>PK`1C4y,M"9 ai҇Xmx ei_ ~߂ʥưIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewActive_icon.png000066400000000000000000000002651272363601300262560ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  YIDAT(ctπ0>`-ش12nª&X Xdb peQX!:,eՆ,\P+1-h-FIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewDisabled_icon.png000066400000000000000000000002621272363601300265470ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  VIDAT(c/OF'1000 `I2,0/0\),^Y,Bq2ZjkD)cOBJ0mv+IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewEnabled_icon.png000066400000000000000000000002651272363601300263750ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  YIDAT(ctπ0>`-ش12nª&X Xdb peQX!:,eՆ,\P+1-h-FIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewInactivePressed_icon.png000066400000000000000000000002631272363601300301310ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  WIDAT(c\?xc#B$a ՃEP+kƢ#ł) g)è6<`ec%ccFlhɆ<IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewInactive_icon.png000066400000000000000000000002621272363601300266020ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  VIDAT(c/OF'1000 `I2,0/0\),^Y,Bq2ZjkD)cOBJ0mv+IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinListViewPressed_icon.png000066400000000000000000000002651272363601300264500ustar00rootroot00000000000000PNG  IHDR^͌bKGD̿ pHYs  YIDAT(ctπ0>`-ش12nª&X Xdb peQX!:,eՆ,\P+1-h-FIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinMenuPressed_icon.png000066400000000000000000000005121272363601300256010ustar00rootroot00000000000000PNG  IHDR 9bKGD̿ pHYs  IDAT(υOJqY!-u`I' G VZPi3=}йtl|yO%`V-!E7OL4W [Ր8gQj_Э)Gde EIf i1iMJ/ ?V)[>pta Ӱ֛%}qI9,Cqm)s AKKGq B<IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinMenu_icon.png000066400000000000000000000005301272363601300242530ustar00rootroot00000000000000PNG  IHDR #2sbKGD̿ pHYs  IDAT(υнJ)RQB]D]@K[asC{[tB6x 67IA$-Qߔ$obF<9yTNQTbg5j R9yS/JL'8N2 u7;F WYGzLlRe[N }7bD}L Y"ͳ[3oky.569L2)-T-En,~rl*Sr5gumФ~ӈɾ#Rfv3IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinOpenEnabled_icon.png000066400000000000000000000005221272363601300255240ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  IDAT(ϥ*qo|5cRIɆdc <,l)a2O wq휓ߧ.dP?;IlQMÒggG]W\m-~)XI~b+ s1MnU-#idʲ- T 0CօK-!B>hO&*f:U0ؑo\'*&wgİ+WOۇ$hSWdkUw!dePlWFIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinOpenPressed_icon.png000066400000000000000000000005041272363601300255770ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  IDAT(ϥ1JQ%3F{!Zuh ,Z,܄لZ`#AM23MuD oq 'DO.s Ifj/]VIS #_DvC"bJݸx0/uV1 qg"t,kr);OCJћ, £QvvLT0Ean}G3mmKHAIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinPartoptionsPressed_icon.png000066400000000000000000000010261272363601300272200ustar00rootroot00000000000000PNG  IHDRاPLTE]]]]]]]]]]]]]]]]]]֏]]]àFe&tRNS____/Ͽ_??z2xbKGDLo pHYs  IDATӍG@D6,:sQ b>: ˷(l;^?dpd0FJԦ񄚩t6Oq]h_ztẶ†%`k.Gt0E0DTV.5(x4|LJWݵ+IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinPartoptions_icon.png000066400000000000000000000010371272363601300256740ustar00rootroot00000000000000PNG  IHDRاPLTE|}wxzqrtlmobbdVWYPPRKKM||~EFH??A99;224]]]//1]]]]]]]]]]]]VVXQQSLLM>>@<<>779//1//1]]]MMM~~~zz{opqghjuuv\\^~llmMMOuvxSSToprNNOhikIIJ]]]bbdEEF[\^ijkgghdefbbc``a]]^ZZ[YYZ99;>>@99:**+&&&MMM!K&tRNS____/Ͽ_??z2xbKGDOnfAI pHYs  IDATӍ@ᱻEsUYT/9gbNrjhz- jH q >Ht:!Ƹtau|Q)LnwI*E<_+aIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinSaveDisabled_icon.png000066400000000000000000000003261272363601300257000ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  zIDAT(ύ1 0gX,l/>N-Q!\s;q0njrY~B``E00_X~7Ҏ w7,e]  58"ϬQMhBw$_|hiIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinSaveEnabled_icon.png000066400000000000000000000003441272363601300255230ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  IDAT(ϭA @ E_\A^;V3\`K.FF3Յ@OLKJ$eVYz5%/ZOXmoEzuAB$%n,CWDq9sw%pvIENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinSavePressed_icon.png000066400000000000000000000003471272363601300256010ustar00rootroot00000000000000PNG  IHDR7bKGD̿ pHYs  IDAT(ϭ1@Elb#ŠB3ѓRh`b!J)翼?ٕH~.G?{n(<{KDpdfL+p ٞ.n>HaKjU>2g{B00@ʯ~`IENDB`fritzing-app-0.9.3b/resources/obsolete/partsBinToolButtonArrow.png000066400000000000000000000002321272363601300254620ustar00rootroot00000000000000PNG  IHDRT pbKGD̿ pHYs  >IDATc`@  L   >08,Տ}kπ HIENDB`fritzing-app-0.9.3b/resources/obsolete/partsEditorConnectorAddDisabled_icon.png000066400000000000000000000002701272363601300300610ustar00rootroot00000000000000PNG  IHDRsыbKGD̿ pHYs  \IDATӥM @L`Sz0@v,;~M{ۙj $?Qؑ|7>[7l)`EBZ@"j&IENDB`fritzing-app-0.9.3b/resources/obsolete/partsEditorConnectorAddEnabled_icon.png000066400000000000000000000002701272363601300277040ustar00rootroot00000000000000PNG  IHDRsыbKGD̿ pHYs  \IDATӥM @L`Sz0@v,;~M{ۙj $?Qؑ|7>[7l)`EBZ@"j&IENDB`fritzing-app-0.9.3b/resources/obsolete/partsEditorConnectorAddPressed_icon.png000066400000000000000000000002701272363601300277570ustar00rootroot00000000000000PNG  IHDRsыbKGD̿ pHYs  \IDATӥM @L`Sz0@v,;~M{ۙj $?Qؑ|7>[7l)`EBZ@"j&IENDB`fritzing-app-0.9.3b/resources/obsolete/partsEditorConnectorRemoveDisabled_icon.png000066400000000000000000000002421272363601300306250ustar00rootroot00000000000000PNG  IHDR:bKGD̿ pHYs  FIDATc\ȳ<4Q@?8   C7pAc k s00{qjLIENDB`fritzing-app-0.9.3b/resources/obsolete/partsEditorConnectorRemoveEnabled_icon.png000066400000000000000000000002421272363601300304500ustar00rootroot00000000000000PNG  IHDR:bKGD̿ pHYs  FIDATc\ȳ<4Q@?8   C7pAc k s00{qjLIENDB`fritzing-app-0.9.3b/resources/obsolete/partsEditorConnectorRemovePressed_icon.png000066400000000000000000000002421272363601300305230ustar00rootroot00000000000000PNG  IHDR:bKGD̿ pHYs  FIDATc\ȳ<4Q@?8   C7pAc k s00{qjLIENDB`fritzing-app-0.9.3b/resources/parts/000077500000000000000000000000001272363601300174455ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/core/000077500000000000000000000000001272363601300203755ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/core/30.20PerfboardModuleID.fzp000066400000000000000000011164621272363601300250030ustar00rootroot00000000000000 4 JC basic single-sided perfboard 2011-06-02 Perfboard Prototyping Board Perfboard 30.20 prototyping.perfboard.perfboard A perfboard for easily prototyping circuits perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

perfboard socket

fritzing-app-0.9.3b/resources/parts/core/30.20Stripboard2ModuleID.fzp000066400000000000000000011177131272363601300252720ustar00rootroot00000000000000 4 JC basic single-sided stripboard 2011-06-02 Stripboard Prototyping Board Stripboard 30.20 vertical strips prototyping.stripboard.stripboard A stripboard for easily prototyping circuits stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

fritzing-app-0.9.3b/resources/parts/core/30.20StripboardModuleID.fzp000066400000000000000000011177141272363601300252110ustar00rootroot00000000000000 4 JC basic single-sided stripboard 2011-06-02 Stripboard Prototyping Board Stripboard 30.20 horizontal strips prototyping.stripboard.stripboard A stripboard for easily prototyping circuits stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

stripboard socket

fritzing-app-0.9.3b/resources/parts/core/blocker.fzp000066400000000000000000000021051272363601300225350ustar00rootroot00000000000000 4 Jonathan Cohen Copper Fill Blocker 2010-04-29 pcb copper blocker fritzing core Copper Fill Blocker copper1 discreteParts.pcb.copperblocker Blocks copper fill fritzing-app-0.9.3b/resources/parts/core/blocker0.fzp000066400000000000000000000021051272363601300226150ustar00rootroot00000000000000 4 Jonathan Cohen Copper Fill Blocker 2010-04-29 pcb copper blocker fritzing core Copper Fill Blocker copper0 discreteParts.pcb.copperblocker Blocks copper fill fritzing-app-0.9.3b/resources/parts/core/boardlogoimage.fzp000066400000000000000000000024521272363601300240740ustar00rootroot00000000000000 4 Jonathan Cohen Custom PCB Image 2012-05-30 pcb part diy fritzing core Plain Vanilla PCB 2 Custom Shape discreteParts.pcb.logo For adding images to the board layer of the board mm fritzing-app-0.9.3b/resources/parts/core/boardlogoimage_one_layer.fzp000066400000000000000000000024621272363601300261320ustar00rootroot00000000000000 4 Jonathan Cohen Custom PCB Image 2012-05-30 pcb part diy fritzing core Plain Vanilla PCB 1 Custom Shape discreteParts.pcb.logo For adding images to the board layer of the board mm fritzing-app-0.9.3b/resources/parts/core/breadboardlogoimage.fzp000066400000000000000000000023211272363601300250650ustar00rootroot00000000000000 4 Jonathan Cohen Breadboard Image 2012-10-23 breadboard part diy fritzing core Logo image discreteParts.breadboard.logo For adding images to the breadboard view mm fritzing-app-0.9.3b/resources/parts/core/breadboardlogotext.fzp000066400000000000000000000023161272363601300247730ustar00rootroot00000000000000 4 Jonathan Breadboard Text 2012-10-23 breadboard part diy fritzing core Logo logo text discreteParts.breadboard.logo For adding text to the breadboard view fritzing-app-0.9.3b/resources/parts/core/cmRuler.fzp000066400000000000000000000017061272363601300225330ustar00rootroot00000000000000 4 Mariano Crowe Ruler 2008-08-21 ruler cm Ruler 10cm An adjustable ruler cmil fritzing-app-0.9.3b/resources/parts/core/copper0logoimage.fzp000066400000000000000000000023701272363601300243540ustar00rootroot00000000000000 4 Brendan Howell Copper Image 2008-11-25 pcb part diy fritzing core Logo image copper0 discreteParts.pcb.logo For adding images to the bottom copper layer of the board mm fritzing-app-0.9.3b/resources/parts/core/copper0logotext.fzp000066400000000000000000000022721272363601300242570ustar00rootroot00000000000000 4 Brendan Howell Copper Text 2008-11-25 pcb part diy fritzing core Logo logo text copper0 discreteParts.pcb.logo.copper For adding text to the copper bottom layer of the board fritzing-app-0.9.3b/resources/parts/core/copper1logoimage.fzp000066400000000000000000000023651272363601300243610ustar00rootroot00000000000000 4 Brendan Howell Copper Image 2008-11-25 pcb part diy fritzing core Logo image copper1 discreteParts.pcb.logo For adding images to the top copper layer of the board mm fritzing-app-0.9.3b/resources/parts/core/copper1logotext.fzp000066400000000000000000000022671272363601300242640ustar00rootroot00000000000000 4 Brendan Howell Copper Text 2008-11-25 pcb part diy fritzing core Logo logo text copper1 discreteParts.pcb.logo.copper For adding text to the copper top layer of the board fritzing-app-0.9.3b/resources/parts/core/dcpower.fzp000066400000000000000000000047561272363601300225750ustar00rootroot00000000000000 4 Brendan Howell DC Power 2008-07-28 obsolete dc power symbol fritzing core obsolete power symbol dc battery/supply 5 discreteParts.power.power.dcpower A generic dc power symbol cmil Positive Supply Voltage

Ground

fritzing-app-0.9.3b/resources/parts/core/ellipse_pcb.fzp000066400000000000000000000024401272363601300233770ustar00rootroot00000000000000 4 Jonathan Cohen Ellipse PCB - Resizable 2012-06-03 pcb board diy fritzing core 1 Ellipse Plain Vanilla PCB discreteParts.pcb.ellipse.resizable Ellipse Resizable Circuit Board mm fritzing-app-0.9.3b/resources/parts/core/ellipse_pcb_two_layers.fzp000066400000000000000000000024501272363601300256500ustar00rootroot00000000000000 4 Jonathan Cohen Ellipse PCB - Resizable 2012-06-03 pcb board diy fritzing core 2 Ellipse Plain Vanilla PCB discreteParts.pcb.ellipse.resizable Ellipse Resizable Circuit Board mm fritzing-app-0.9.3b/resources/parts/core/ground.fzp000066400000000000000000000031631272363601300224170ustar00rootroot00000000000000 4 Brendan Howell Ground 2008-07-28 ground symbol fritzing core ground symbol discreteParts.ground.ground.ground A generic ground symbol cmil ground

fritzing-app-0.9.3b/resources/parts/core/groundplane.fzp000066400000000000000000000026131272363601300234360ustar00rootroot00000000000000 4 Fritzing Copper Fill 2009-10-22 copper fill fritzing core copper fill discreteParts.part.part.copperfill A copper fill

fritzing-app-0.9.3b/resources/parts/core/hole.fzp000066400000000000000000000020041272363601300220410ustar00rootroot00000000000000 4 Jonathan Cohen Hole 2010-04-29 pcb hole fritzing core 2.2mm,0.0mm Hole discreteParts.pcb.hole Hole with or without copper fritzing-app-0.9.3b/resources/parts/core/jumper.fzp000066400000000000000000000042401272363601300224200ustar00rootroot00000000000000 4 Brendan Howell Jumper 2008-07-28 jumper fritzing core jumper discreteParts.wire.wire.jumper A jumper cmil jumper end

the other end of the jumper

fritzing-app-0.9.3b/resources/parts/core/logoimage.fzp000066400000000000000000000023431272363601300230630ustar00rootroot00000000000000 4 Brendan Howell Silkscreen Image 2008-11-25 pcb part diy fritzing core Logo image silkscreen discreteParts.pcb.logo For adding images to the silkscreen layer of the board mm fritzing-app-0.9.3b/resources/parts/core/logotext.fzp000066400000000000000000000022411272363601300227620ustar00rootroot00000000000000 4 Brendan Howell Silkscreen Text 2008-11-25 pcb part diy fritzing core Logo logo text silkscreen discreteParts.pcb.logo For adding text to the silkscreen layer of the board fritzing-app-0.9.3b/resources/parts/core/netlabel.fzp000066400000000000000000000032271272363601300227100ustar00rootroot00000000000000 4 Jonathan Cohen Net Label 2012-10-02 net label symbol fritzing core net label A net label for schematic view will be connected to any other net label with the same name. cmil

fritzing-app-0.9.3b/resources/parts/core/netlabel_left.fzp000066400000000000000000000032441272363601300237210ustar00rootroot00000000000000 4 Jonathan Cohen Net Label 2012-10-02 net label symbol fritzing core obsolete net label A net label for schematic view will be connected to any other net label with the same name. cmil

fritzing-app-0.9.3b/resources/parts/core/newbreadboardlogoimage.fzp000066400000000000000000000023401272363601300256000ustar00rootroot00000000000000 4 Jonathan Cohen Breadboard Image 2012-10-23 breadboard part diy fritzing core Logo image discreteParts.breadboard.logo For adding images to the breadboard view mm fritzing-app-0.9.3b/resources/parts/core/newcopper0logoimage.fzp000066400000000000000000000023571272363601300250730ustar00rootroot00000000000000 4 Brendan Howell Copper Image 2008-11-25 pcb part diy fritzing core Logo image copper0 discreteParts.pcb.logo For adding images to the bottom copper layer of the board mm fritzing-app-0.9.3b/resources/parts/core/newcopper1logoimage.fzp000066400000000000000000000023541272363601300250710ustar00rootroot00000000000000 4 Brendan Howell Copper Image 2008-11-25 pcb part diy fritzing core Logo image copper1 discreteParts.pcb.logo For adding images to the top copper layer of the board mm fritzing-app-0.9.3b/resources/parts/core/newlogoimage.fzp000066400000000000000000000023621272363601300235760ustar00rootroot00000000000000 4 Brendan Howell Silkscreen Image 2008-11-25 pcb part diy fritzing core Logo image silkscreen discreteParts.pcb.logo For adding images to the silkscreen layer of the board mm fritzing-app-0.9.3b/resources/parts/core/newschematiclogoimage.fzp000066400000000000000000000023261272363601300254570ustar00rootroot00000000000000 4 Brendan Howell Schematic Image 2008-11-25 schematic part diy fritzing core Logo image discreteParts.schematic.logo For adding images to the schematic view mm fritzing-app-0.9.3b/resources/parts/core/newsilkscreen0logoimage.fzp000066400000000000000000000023771272363601300257470ustar00rootroot00000000000000 4 Brendan Howell Silkscreen Image 2008-11-25 pcb part diy fritzing core Logo image silkscreen0 discreteParts.pcb.logo For adding images to the silkscreen layer of the board mm fritzing-app-0.9.3b/resources/parts/core/note.fzp000066400000000000000000000015631272363601300220700ustar00rootroot00000000000000 4 Brendan Howell Note 2008-07-28 note fritzing core note discreteParts.note.note.note A generic note cmil fritzing-app-0.9.3b/resources/parts/core/pad.fzp000066400000000000000000000027641272363601300216730ustar00rootroot00000000000000 4 Jonathan Cohen Pad 2010-04-29 pcb Pad fritzing core pad copper1 discreteParts.pcb.pad Pad via

fritzing-app-0.9.3b/resources/parts/core/pad0.fzp000066400000000000000000000027761272363601300217560ustar00rootroot00000000000000 4 Jonathan Cohen Pad 2010-04-29 pcb Pad fritzing core pad copper0 discreteParts.pcb.pad Pad via

fritzing-app-0.9.3b/resources/parts/core/power.fzp000066400000000000000000000035261272363601300222600ustar00rootroot00000000000000 4 Brendan Howell Power 2008-07-28 power symbol fritzing core power symbol voltage reference 5 discreteParts.power.power.power A generic power symbol cmil Positive Supply Voltage

fritzing-app-0.9.3b/resources/parts/core/power_label.fzp000066400000000000000000000034561272363601300234210ustar00rootroot00000000000000 4 Brendan Howell Power 2008-07-28 power symbol fritzing core power label voltage reference 5 discreteParts.power.power.power A generic power symbol cmil Positive Supply Voltage

fritzing-app-0.9.3b/resources/parts/core/rectangle_pcb.fzp000066400000000000000000000024711272363601300237120ustar00rootroot00000000000000 4 Brendan Howell Rectangular PCB - Resizable 2008-11-25 pcb board diy fritzing core 1 Rectangle Plain Vanilla PCB discreteParts.pcb.rectangle.resizable Rectangular Resizable Circuit Board mm fritzing-app-0.9.3b/resources/parts/core/rectangle_pcb_two_layers.fzp000066400000000000000000000025001272363601300261530ustar00rootroot00000000000000 4 Brendan Howell Rectangular PCB - Resizable 2008-11-25 pcb board diy fritzing core 2 Rectangle Plain Vanilla PCB discreteParts.pcb.rectangle.resizable Rectangular Resizable Circuit Board mm fritzing-app-0.9.3b/resources/parts/core/roundrect_pcb.fzp000066400000000000000000000024651272363601300237560ustar00rootroot00000000000000 4 Jonathan Cohen Round Rect PCB - Resizable 2012-06-03 pcb board diy fritzing core 1 Round Rect Plain Vanilla PCB discreteParts.pcb.roundrect.resizable Round Rect Resizable Circuit Board mm fritzing-app-0.9.3b/resources/parts/core/roundrect_pcb_two_layers.fzp000066400000000000000000000024741272363601300262260ustar00rootroot00000000000000 4 Jonathan Cohen RoundRect PCB - Resizable 2012-06-03 pcb board diy fritzing core 2 Round Rect Plain Vanilla PCB discreteParts.pcb.roundrect.resizable Round Rect Resizable Circuit Board mm fritzing-app-0.9.3b/resources/parts/core/schematic_frame.fzp000066400000000000000000000024661272363601300242400ustar00rootroot00000000000000 4 Jonathan Cohen Schematic Frame 2011-06-29 schematic frame legend fritzing core Schematic Frame discreteParts.schematic.frame.resizable Document frame for schematic view fritzing-app-0.9.3b/resources/parts/core/schematiclogoimage.fzp000066400000000000000000000023071272363601300247440ustar00rootroot00000000000000 4 Brendan Howell Schematic Image 2008-11-25 schematic part diy fritzing core Logo image discreteParts.schematic.logo For adding images to the schematic view mm fritzing-app-0.9.3b/resources/parts/core/schematiclogotext.fzp000066400000000000000000000022441272363601300246460ustar00rootroot00000000000000 4 Brendan Howell Schematic Text 2008-11-25 schematic part diy fritzing core Logo logo text discreteParts.schematic.logo For adding text to the schematic view fritzing-app-0.9.3b/resources/parts/core/silkscreen0logoimage.fzp000066400000000000000000000023601272363601300252250ustar00rootroot00000000000000 4 Brendan Howell Silkscreen Image 2008-11-25 pcb part diy fritzing core Logo image silkscreen0 discreteParts.pcb.logo For adding images to the silkscreen layer of the board mm fritzing-app-0.9.3b/resources/parts/core/silkscreen0logotext.fzp000066400000000000000000000022561272363601300251330ustar00rootroot00000000000000 4 Brendan Howell Silkscreen Text 2008-11-25 pcb part diy fritzing core Logo logo text silkscreen0 discreteParts.pcb.logo For adding text to the silkscreen layer of the board fritzing-app-0.9.3b/resources/parts/core/via.fzp000066400000000000000000000027411272363601300217010ustar00rootroot00000000000000 4 Jonathan Cohen Via 2010-04-29 pcb via fritzing core 0.4mm,0.3mm Via discreteParts.pcb.via Via via

fritzing-app-0.9.3b/resources/parts/core/wire.fzp000066400000000000000000000055751272363601300221000ustar00rootroot00000000000000 4 Brendan Howell Wire 2008-07-28 wire fritzing core wire discreteParts.wire.wire.wire A generic wire cmil wire end

the other end of the wire

fritzing-app-0.9.3b/resources/parts/svg/000077500000000000000000000000001272363601300202445ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/core/000077500000000000000000000000001272363601300211745ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/core/breadboard/000077500000000000000000000000001272363601300232615ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/core/breadboard/30.20perfboard.svg000066400000000000000000003314021272363601300263340ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/breadboard/breadboardlogo.svg000066400000000000000000000004301272363601300267450ustar00rootroot00000000000000 logo fritzing-app-0.9.3b/resources/parts/svg/core/breadboard/ruler.svg000066400000000000000000000003711272363601300251340ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/breadboard/wire.svg000066400000000000000000000013421272363601300247500ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/000077500000000000000000000000001272363601300221245ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/core/icon/blocker.svg000066400000000000000000000022461272363601300242720ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/breadboard_logo.svg000066400000000000000000000033511272363601300257540ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/breadboard_text.svg000066400000000000000000000150671272363601300260070ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/copper0_logo.svg000066400000000000000000000037571272363601300252510ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/copper0_text.svg000066400000000000000000000157041272363601300252700ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/copper1_logo.svg000066400000000000000000000037561272363601300252510ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/copper1_text.svg000066400000000000000000000157041272363601300252710ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/groundplane.svg000066400000000000000000000013531272363601300251650ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/hole.svg000066400000000000000000000017521272363601300236010ustar00rootroot00000000000000 Hole SVG fritzing-app-0.9.3b/resources/parts/svg/core/icon/iconPerfboard.svg000066400000000000000000000202651272363601300254270ustar00rootroot00000000000000 image/svg+xml fritzing-app-0.9.3b/resources/parts/svg/core/icon/iconStripboard.svg000066400000000000000000000330201272363601300256250ustar00rootroot00000000000000 image/svg+xml fritzing-app-0.9.3b/resources/parts/svg/core/icon/jumpericon.svg000066400000000000000000000034571272363601300250310ustar00rootroot00000000000000 Jumper SVG fritzing-app-0.9.3b/resources/parts/svg/core/icon/note.svg000066400000000000000000000023351272363601300236150ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/pad.svg000066400000000000000000000012151272363601300234100ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/rectangle_pcb.svg000066400000000000000000000016251272363601300254410ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/rulericon.svg000066400000000000000000000065041272363601300246540ustar00rootroot00000000000000 cm in fritzing-app-0.9.3b/resources/parts/svg/core/icon/schematic_frame_icon.svg000066400000000000000000000026261272363601300267750ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/schematic_logo.svg000066400000000000000000000032771272363601300256360ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/schematic_text.svg000066400000000000000000000150571272363601300256610ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/silkscreen_logo.svg000066400000000000000000000041331272363601300260300ustar00rootroot00000000000000 Jumper SVG fritzing-app-0.9.3b/resources/parts/svg/core/icon/silkscreen_text.svg000066400000000000000000000157041272363601300260620ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/icon/via.svg000066400000000000000000000023451272363601300234300ustar00rootroot00000000000000 Hole SVG fritzing-app-0.9.3b/resources/parts/svg/core/icon/wireicon.svg000066400000000000000000000441541272363601300244740ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/000077500000000000000000000000001272363601300217405ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/core/pcb/copper1logo.svg000066400000000000000000000004161272363601300247140ustar00rootroot00000000000000 logo fritzing-app-0.9.3b/resources/parts/svg/core/pcb/ellipse_pcb.svg000066400000000000000000000011741272363601300247450ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/groundplane.svg000066400000000000000000000004501272363601300247760ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/hole.svg000066400000000000000000000005451272363601300234140ustar00rootroot00000000000000 Hole SVG fritzing-app-0.9.3b/resources/parts/svg/core/pcb/jumper.svg000066400000000000000000000014641272363601300237700ustar00rootroot00000000000000 Jumper SVG fritzing-app-0.9.3b/resources/parts/svg/core/pcb/logo.svg000066400000000000000000000004211272363601300234160ustar00rootroot00000000000000 logo fritzing-app-0.9.3b/resources/parts/svg/core/pcb/pad.svg000066400000000000000000000004551272363601300232310ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/pad0.svg000066400000000000000000000004541272363601300233100ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/rectangle_pcb.svg000066400000000000000000000015111272363601300252470ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/roundrect_pcb.svg000066400000000000000000000012721272363601300253140ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/pcb/via.svg000066400000000000000000000005571272363601300232470ustar00rootroot00000000000000 Via SVG fritzing-app-0.9.3b/resources/parts/svg/core/schematic/000077500000000000000000000000001272363601300231345ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/core/schematic/frame.svg000066400000000000000000000003141272363601300247450ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/core/schematic/netlabel.svg000066400000000000000000000013211272363601300254400ustar00rootroot00000000000000 net label fritzing-app-0.9.3b/resources/parts/svg/core/schematic/schematiclogo.svg000066400000000000000000000004271272363601300265010ustar00rootroot00000000000000 logo do not change these svg files with anything but a text editor.txt000066400000000000000000000011251272363601300342000ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svgWith the exeception of icon files, do on change resource svgs with vector graphics programs unless you are very very very very goddamn careful. For example, adobe illustrator does not recalculate stroke-width when loading SVGs when the units are not pixels. Secondarily, illustrator, inkscape, and the like tend to shove a lot of specialized extra crap into SVGs in order to facilitate later re-editing. Since many of the resource SVGs are used as generator templates, errors can proliferate beyond the single SVG that gets incorrectly changed; and the extra crap can muddy the templating process.fritzing-app-0.9.3b/resources/parts/svg/obsolete/000077500000000000000000000000001272363601300220605ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/obsolete/schematic/000077500000000000000000000000001272363601300240205ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/parts/svg/obsolete/schematic/0.3.schem.frame.svg000066400000000000000000000003131272363601300272250ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/parts/svg/obsolete/schematic/0.3.schem.netlabel.svg000066400000000000000000000012641272363601300277270ustar00rootroot00000000000000 net label fritzing-app-0.9.3b/resources/properties.xml000066400000000000000000000144451272363601300212420ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/ratsnestcolors.xml000066400000000000000000000115741272363601300221330ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/styles/000077500000000000000000000000001272363601300176375ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/styles/fritzing.qss000066400000000000000000001272141272363601300222320ustar00rootroot00000000000000/* ########### the partseditor window ############## ################################################ ################################################ ################################################ */ #PEMainWindow { font-size: 11px; color: #fff; font-family:Droid Sans; background-color:#666666; } #PEToolView::title { border:1px solid #b1b1b1; } #PEToolView, #peSVG, LayerPalette, LayerPalette::title, LayerPalette QObject { background: #d9d9d9; } #PEToolView QPushButton { font-size: 11px; /*min-width: 100px;*/ text-align: center; background: #ececec; border: 1px solid #b1b1b1; border-radius: 0px; padding: 2px; color: #323232; margin-top:0px; margin-bottom:0px; } #PEToolView QPushButton:hover { background: #fff; } #PEToolView QPushButton:pressed, #PEToolView QPushButton:on { background: #666; color: #fff; } #PEToolView QTreeWidget { background: #fff; color:#333; show-decoration-selected: 0; } #PEToolView QTreeWidget::item:selected { background:#dadada; color:#333; border:none; show-decoration-selected: 0; } #PEToolView QTreeWidget::item:selected:!active { background:transparent; color:#333; border:none; show-decoration-selected: 0; } #PEToolView QTreeWidget::item:selected:active { background:#dadada; color:#333; border:none; } #PEToolView QTreeWidget::item:hover { background: #a5a5a5; color:#fff; border:none; } /*############ parteditor tabs ################*/ QDialog QTabWidget, #pe_tabs { background:#b1b1b1; } QDialog QTabWidget::pane, #pe_tabs::pane { /*the tab-window*/ padding: 0px 0px 0px 0px; border:none; background:#d9d9d9; margin:0px; font-family: Droid Sans; font-size:14px; font-weight: regular; } QDialog QTabWidget::tab-bar, #pe_tabs::tab-bar /*this is the complete tab-bar*/ { top: 0px; left: 10px; right: 0px; font-family: Droid Sans; font-size:14px; font-weight: regular; background: #b1b1b1; } QDialog QTabWidget QTabBar::tab, #pe_tabs QTabBar::tab { margin: 0px 10px -1px 10px; font-family: Droid Sans; font-size:14px; font-weight: regular; min-height: 35px; background:#fff; min-width:120px; } QDialog QTabWidget QTabBar::tab:selected, #pe_tabs QTabBar::tab:selected /*the active tab*/ { font-family: Droid Sans; font-size:14px; background-color: transparent; font-weight: regular; color: #eee; border:none; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXActiveFocus.png); } QDialog QTabWidget QTabBar::tab:selected:hover, #pe_tabs QTabBar::tab:selected:hover /*the active tab hover*/ { font-family: Droid Sans; font-size:14px; background-color: transparent; font-weight: regular; color: #fff; border:none; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXActiveFocusHover.png); } QDialog QTabWidget QTabBar::tab:!selected, #pe_tabs QTabBar::tab:!selected { /*the inactive tabs*/ background-color: transparent; font-family: Droid Sans; font-size:14px; color: #666; font-weight:regular; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXInactiveFocusHover.png); } QDialog QTabWidget QTabBar::tab:!selected:hover, #pe_tabs QTabBar::tab:!selected:hover /*the inactive tab- hover*/ { background-color: transparent; font-family: Droid Sans; font-size:14px; border:none; color: #fff; font-weight:regular; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXInactiveFocusHover.png); } QDialog QTabWidget QToolButton, #pe_tabs QToolButton { background: rgba(217,217,217,200); border:0px; } QDialog QTabWidget QToolButton::right-arrow, #pe_tabs QToolButton::right-arrow { background-image: url(:/resources/images/icons/TabScrollArrowButtonRightNormal.png); background-repeat: none; background-position: center; } QDialog QTabWidget QToolButton::right-arrow:pressed, #pe_tabs QToolButton::right-arrow:pressed { background-image: url(:/resources/images/icons/TabScrollArrowButtonRightPressed.png); background-repeat: none; background-position: center; } QDialog QTabWidget QToolButton::left-arrow, #pe_tabs QToolButton::left-arrow{ background-image: url(:/resources/images/icons/TabScrollArrowButtonLeftNormal.png); background-repeat: none; background-position: center; } QDialog QTabWidget QToolButton::left-arrow:pressed, #pe_tabs QToolButton::left-arrow:pressed{ background-image: url(:/resources/images/icons/TabScrollArrowButtonLeftPressed.png); background-repeat: none; background-position: center; } /* ########### the main window and general things ############## ################################################ ################################################ ################################################ */ #theMainWindow { /* layout here */ background-color:#b1b1b1; /*mainbackground color - you see it behind the mainwindow-tabs*/ /* styling there */ font-size: 11px; color: #fff; font-family:Droid Sans; selection-background-color: none; selection-border: none; } QStatusBar { background-color: rgb(130,31,9); /* the statusbar at the bottom */ color: #fff; } #statusBarContainer { background-color: rgb(130,31,9);/* the statusbar at the bottom */ color: #fff; } #statusBarContainer QTooltip { border: 1px solid #fff; /* padding: 3px; */ border-radius: 1px; background:#fff; color:#333; opacity: 200; } QScrollBar:vertical { border: 0px; background:rgba(255,255,255,10); width:10px; margin: 0px 0px 0px 0px; } QScrollBar::handle:vertical { border: 2px solid #b2b2b2; border-radius: 2px; background: #b2b2b2; min-height: 5px; } QScrollBar::add-line:vertical { background: none; height: 1px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line:vertical { background:none; height: 1px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar:horizontal { border: 0px; background:rgba(255,255,255,10); height:10px; margin: 0px 0px 0px 0px; } QScrollBar::handle:horizontal { border: 2px solid #b2b2b2; border-radius: 2px; background: #b2b2b2; min-width: 5px; } QScrollBar::add-line:horizontal { background: none; width: 1px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal { background:none; width: 1px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: transparent; } /* complex property values with up and down arrows */ PCBSketchWidget QObject QDoubleSpinBox, QDoubleSpinBox { background: #fff; font-size: 11px; color: #000; border: none; } PCBSketchWidget QObject QDoubleSpinBox::down-button, QDoubleSpinBox::down-button { background: #fff; border: none; image: url(:/resources/images/icons/spinArrowButtonDownNormal.png); margin-bottom:1px; } PCBSketchWidget QObject QDoubleSpinBox::down-button:pressed, QDoubleSpinBox::down-button:pressed { background: #fff; border: none; image: url(:/resources/images/icons/spinArrowButtonDownPressed.png); margin-bottom:1px; } PCBSketchWidget QObject QDoubleSpinBox::up-button, QDoubleSpinBox::up-button { background: #fff; border-bottom: none; image: url(:/resources/images/icons/spinArrowButtonUpNormal.png); } PCBSketchWidget QObject QDoubleSpinBox::up-button:pressed, DoubleSpinBox::up-button:pressed { background: #fff; border-bottom: none; image: url(:/resources/images/icons/spinArrowButtonUpPressed.png); } /* this separates palettes from each other and is movable */ MainWindow::separator { background-color: rgb(101,101,101); } MainWindow::toolbar { background-color: #fff; } MainWindow QLabel { color: rgb(50,50,50); font-family: Droid Sans; } QStatusBar { left: 6px; color: #fff; font-weight: bold; font-size: 11px; font-family: Droid Sans; } QStatusBar::item { font-family: Droid Sans; border: none; color: #fff; } /* ################### Mainmenubar window #################### */ QMenuBar { background-color: #d9d9d9; left:5px; font-family: Droid Sans; } QMenuBar::item { spacing: 10px; /* spacing between menu bar items */ padding: 7px 10px; background: #d9d9d9; color:rgb(51,51,51); background-image:none; font-family: Droid Sans; } QMenuBar::item:selected { background: #aaa; color:#fff; background-image:none; font-family: Droid Sans; } QMenuBar::item:pressed { background: #aaa; color:#fff; background-image:none; font-family: Droid Sans; } /*########### style of the dropdowns ######*/ QMenu { background-color: #d9d9d9; color:#333; margin: 0px 3px 0px 3px; border:none; font-family: Droid Sans; } QMenu::item { background-color: #d9d9d9; color:#333; padding: 2px 15px 2px 15px; font-family: Droid Sans; } QMenu::item:selected { background-color: #a5a5a5; color:#fff; font-family: Droid Sans; } QMenu::separator { background-color: #c1c1c1; height:1px; } QMenu::indicator { width: 15px; height: 15px; } QMenu::item:disabled { color:#b7b7b7; font-family: Droid Sans; } QMenu::item:disabled:selected { color:#b7b7b7; font-family: Droid Sans; background-color: #d9d9d9; } /*############## indicator:non-exclusive is a ###########*/ QCheckBox::indicator:checked, QMenu::indicator:non-exclusive:checked { image: url(:/resources/images/icons/check.png); background-color:transparent; border:none; } QCheckBox::indicator:checked:selected, QMenu::indicator:non-exclusive:checked:selected { image: url(:/resources/images/icons/checkhover.png); background-color:transparent; border:none; } QCheckBox::indicator:unchecked, QMenu::indicator:non-exclusive:unchecked { /*the checkbox for the mainmenu */ image: url(:/resources/images/icons/nocheck.png); background-color:transparent; border:transparent; } QCheckBox::indicator:unchecked:selected, QMenu::indicator:non-exclusive:unchecked:selected { image: url(:/resources/images/icons/check.png); background-color:transparent; border:none; } /*########### MAIN WINDOW TABBAR #################*/ /*the tab-window*/ #mainTabBar { qproperty-iconSize: 36px; font-family: Droid Sans; font-size:14px; font-weight: regular; margin-left:10px; } #sketch_tabs::pane { /*the tab-window*/ padding: 0px 0px 0px 0px; border:none; background:#e6eef2; margin:0px; font-family: Droid Sans; font-size:14px; font-weight: regular; } #sketch_tabs::tab-bar /*this is the complete tab-bar*/ { top: 0px; left: 10px; right: 0px; font-family: Droid Sans; font-size:14px; font-weight: regular; background: #b1b1b1; } #sketch_tabs QTabBar::tab { margin: 0px 10px -1px 0px; min-width: 150px; max-width: 150px; font-family: Droid Sans; font-size:14px; font-weight: regular; } #sketch_tabs QTabBar::tab:selected /*the active tab*/ { font-family: Droid Sans; font-size:14px; background-color: transparent; font-weight: regular; color: #eee; border:none; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXActiveFocus.png); } #sketch_tabs QTabBar::tab:selected:hover /*the active tab hover*/ { font-family: Droid Sans; font-size:14px; background-color: transparent; font-weight: regular; color: #fff; border:none; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXActiveFocusHover.png); } #sketch_tabs QTabBar::tab:!selected{ /*the inactive tabs*/ background-color: transparent; font-family: Droid Sans; font-size:14px; color: #666; font-weight:regular; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXInactiveFocusHover.png); } #sketch_tabs QTabBar::tab:!selected:hover /*the inactive tab- hover*/ { background-color: transparent; font-family: Droid Sans; font-size:14px; border:none; color: #fff; font-weight:regular; margin-bottom:-1px; background-image: url(:/resources/images/icons/segmentedSwitcherXInactiveFocusHover.png); } /* ################### WelcomeScreen styling (WC) #################### */ WelcomeView { background: #E6EEF2; border:0px; margin:0px; padding:0px; } WelcomeView QFrame { border-bottom: 8px solid #d9d9d9; font-family: Droid Sans; } WelcomeView QFrame QLabel { border:0px; font-family: Droid Sans; } /* -------- position of WS"content-blocks" ------------ */ #recentFrame { margin: 10px 0px 13px 15px; padding-bottom:5px; background: #fff; border-bottom: 8px solid #d9d9d9; font: Droid Sans; font-family: Droid Sans; max-width: 350px; min-width: 220px; } #tipFrame { margin: 0px 0px 13px 15px; background: #fff; border-bottom: 8px solid #d9d9d9; font: Droid Sans; font-family: Droid Sans; max-width: 350px; min-width: 220px; } #blogFrame { margin: 10px 15px 13px 15px; background: #fff; border-bottom: 8px solid #d9d9d9; font: Droid Sans; font-family: Droid Sans; max-width: 820px; } #shopFrame { margin: 0px 15px 13px 15px; background: #fff; border-bottom: 8px solid #d9d9d9; font: Droid Sans; font-family: Droid Sans; max-width: 820px; } /* -------------- WS-switchable Header style -------------/* /*#####this is used in blog/projects and shop/fab frame*/ #wsSwitchableFrameHeader { border-bottom: 1px solid #c4c4c4; padding: 8px 0px 8px 0px; margin:0px 5px 0px 5px; font-size:14px; font-weight:bold; min-height:18px; max-height:18px; background:#fff; } #headerTitle1 { font-size:14px; font-weight:bold; color:#c4c4c4; margin-left:0px; margin-right:0px; font-family: Droid Sans; margin-left:1px; margin-right:2px; } #headerTitle2{ color:#323232; font-size:14px; font-weight:bold; font-family: Droid Sans; margin-left:1px; margin-right:2px; } #headerTitleSpace { margin:0px, 3px; } /* ----------- WS-"recentItem"-block ------------- */ #recentTitleFrame { border-bottom: 1px solid #c4c4c4; margin:0px 5px 0px 5px; font-size:14px; font-weight:bold; min-height:34px; max-height:34px; background:#fff; padding:0px; font-family: Droid Sans; } #recentTitle { font-size:14px; font-weight:bold; color:#323232; background:#fff; margin-left:1px; margin-right:2px; font-family: Droid Sans; max-height:16px; } #recentOpenSketch, #recentNewSketch{ margin: 5px 5px 5px 5px; padding:0px; text-align:left; border:none; font-size: 12px; min-height:14px; max-height:22px; background:transparent; } #recentList { border:none; margin: 5px 5px 5px 5px; padding:0px; text-align:left; font-size: 12px; background:#fff; font: regular Droid Sans; } #recentList::item { margin:3px 0px; border:none; padding:0px; text-align:left; font-size: 12px; background:#fff; color:#666; font-weight: regular; font-family: Droid Sans; min-height:14px; max-height:24px; } #recentList::item:selected { margin:0px; border:none; padding:0px; text-align:left; font-size: 12px; background:#fff; color:#666; font-weight: regular; font-family: Droid Sans; min-height:14px; max-height:24px; } #recentSpace { padding:0px; margin:0px 5px; border-bottom: 1px solid #c4c4c4; max-height:1px; background:transparent; } #recentText { font-size: 12px; margin-left:3px; padding:0px; border:none; background:#fff; max-height:20px; min-height:20px; } #recentIcon { margin-left:1px; padding:0px; image-position:center; max-width:18px; max-height:20px; min-width:18px; min-height:20px; border:none; background:transparent; } /* ----------- WS-"blog"-block ------------- */ /******* this is the styling for the blog-frame-title ***********/ #blogTitleFrameLayout { } #blogList { border:none; margin: 5px 5px 5px 5px; padding:0px; text-align:left; font-size: 10px; background:#fff; font-weight: regular; font-family: Droid Sans; color:#656565; qproperty-titleTextColor: #000; qproperty-titleTextFontFamily: Droid Sans; qproperty-titleTextFontSize:14px; qproperty-titleTextExtraLeading:3px; qproperty-introTextColor: #656565; qproperty-introTextFontFamily:Droid Sans; qproperty-introTextFontSize:12px; qproperty-introTextExtraLeading:5px; qproperty-dateTextColor: #656565; qproperty-dateTextFontFamily: Droid Sans; qproperty-dateTextFontSize:10px; } #blogList::item { border:none; text-align:left; font-size: 15px; background:#fff; font-weight: regular; font-family: Droid Sans; border-bottom:1px solid #d4d4d4; } /******* this is the styling for the blog-footer ***********/ #blogLogo { background:#fff; min-height:22px; max-height:22px; } /* ----------- WC-"tip"-block ------------- */ #tipFrame { background: #D5E1E9; border-bottom: 8px solid #A9C6D6; } #tipTitle { color: #77ACC2; border-bottom: none; padding: 0px; margin:8px 5px 8px 5px; font-size:14px; font-family: Droid Sans; font-weight:bold; min-height:18px; max-height:18px; } #tipScrollArea { border:none; padding: 0px 0px 0px 0px; margin-right:5px; } #tipScrollArea QScrollBar { border:none; } #tipScrollArea QScrollBar:vertical{ border: 0px; background:#D5E1E9; width:10px; margin: 0px; } #tipScrollArea QScrollBar::handle:vertical{ border: 2px solid #77ACC2; border-radius: 2px; background: #77ACC2; min-height: 0px; } #tipScrollArea QScrollBar::add-line:vertical{ background: #D5E1E9; height: 10px; subcontrol-position: bottom; subcontrol-origin: margin; } #tipScrollArea QScrollBar::sub-line:vertical{ background:#D5E1E9; height: 10px; subcontrol-position: top; subcontrol-origin: margin; } #tip { background: #D5E1E9; border: none; font-family: "OCRA"; color:#2e94af; font-size:13px; qproperty-wordWrap: true; padding: 0px 10px 10px 10px; } #tipFooterFrame{ margin:5px 10px 5px 10px; border-bottom:none; } /* ----------- WS-"shop+fab"-block ------------- */ #shopContentFrame, #fabContentFrame { border:none; font-weight: regular; font-family: Droid Sans; margin:5px; height:100%; } #shopContentImage, #fabContentImage { background:#fff; min-width:45px; min-height:45px; margin:0px; padding:0px; } #shopContentTextFrame, #fabContentTextFrame{ margin: 5px 5px 5px 15px; border:none; min-width:200px; } #shopContentTextHeadline, #fabContentTextHeadline { font-size:16px; font-weight:bold; max-height:20px; font-family: Droid Sans; background:#fff; } #shopContentTextDescription, #fabContentTextDescription { font-size:14px; font-weight:regular; qproperty-wordWrap: true; font-family: Droid Sans; min-height:65px; background:#fff; } #shopContentTextCaption, #fabContentTextCaption { font-weight: regular; font-family: Droid Sans; font-size:11px; max-height:14px; background:#fff; } #blogFooterFrame, #shopFooterFrame, #fabFooterFrame { border-top:1px solid #c4c4c4; background:transparent; border-bottom:none; margin:0px 5px 8px 5px; font-weight: regular; font-family: Droid Sans; padding-top:10px; padding-right:5px; /* padding:8px 0px 8px 0px; */ min-height:25px; max-height:25px; } #blogLogoText, #shopLogoText, #fabLogoText { margin-right:5px; } /*############### a styled loading/progress bar #################### */ QProgressBar { border: none; border-radius: 0px; background-color: #821F09; text-align: center; color:#fff; font-weight:bold; } QProgressBar::chunk { background-color: #f32926; width: 10px; text-align: center; font-family: Droid Sans; } /* ############ tooltips for the partsbin and toolbar NOT the main window ########*/ QToolTip { border: 1px solid #fff; /* padding: 3px; */ border-radius: 1px; background:#fff; color:#333; opacity: 200; font-family: Droid Sans; } /* ############## RADIOBUTTON STYLING ###########*/ QWidget QRadioButton::indicator, QRadioButton::indicator { width: 13px; height: 13px; background:transparent; } QWidget QRadioButton::indicator::unchecked, QRadioButton::indicator::unchecked { image: url(:/resources/images/icons/nocheck.png); background:transparent; } QWidget QRadioButton::indicator:unchecked:hover, QRadioButton::indicator:unchecked:hover { image: url(:/resources/images/icons/checkhover.png); } QWidget QRadioButton::indicator:unchecked:pressed, QRadioButton::indicator:unchecked:pressed { image: url(:/resources/images/icons/checkactive.png); } QWidget QRadioButton::indicator:checked, QRadioButton::indicator::checked { image: url(:/resources/images/icons/check.png); } QWidget QRadioButton::indicator:checked:hover, QRadioButton::indicator:checked:hover { image: url(:/resources/images/icons/checkhover.png); } QWidget QRadioButton::indicator:checked:pressed, QRadioButton::indicator:checked:pressed { image: url(:/resources/images/icons/checkactive.png); } /* ################### the toolbar #################### */ #routingStateLabel { color: #ea9f84; background:rgb(214,50,34); font-family:Droid Sans; font-size:16px; border: 0px; background-color: transparent; margin-top: 8px; margin-bottom: 5px; } #fileToolBar QToolButton, #editToolBar QToolButton { border: none; color: rgb(214,50,34); background: rgb(214,50,34); width:75px; padding:0px 5px; } #fileToolBar QToolButton:pressed, #editToolBar QToolButton:pressed { background: rgb(242,57,38); color: rgb(214,50,34); /* doesnt work, why ??? */ margin: 0px; width:75px; padding:0px 5px; } #sketchAreaToolbar { background: rgb(214,50,34); /*background of toolbar*/ border: none; padding: 0px; } #exportButton, #viewFromButton, #activeLayerButton, #sketchToolButton { border: none; padding: 0px; height:66px; max-width:110px; min-width:95px; } #sketchAreaToolbar QLabel { font-weight: bold; color: rgb(214,50,34); border: none; padding:5px 5px 0px 5px; } #sketchAreaToolbar QToolButton { /*all the button styleing*/ border: none; color:#fff; margin: 0px; height:66px; padding:5px; font-weight: regular; width:75px; } #sketchAreaToolbar QToolButton:hover { /* hover on toolbar buttons */ background: rgb(242,57,38); border: none; color: #ececec; margin: 0px; height:66px; } #sketchAreaToolbar QToolButton:disabled { /* disabled toolbar buttons */ border: none; color: #ececec; margin: 0px; height:66px; } #sketchAreaToolbar QToolButton::menu-button { /*the little arrow to open the submenu*/ border: none; /* background-image: none; */ margin-top: 28px; margin-right:20px; background:transparent; width:10px; height:10px; float:right; } #sketchAreaToolbar QToolButton::menu-arrow { image: url(:/resources/images/icons/smalToolarrow-normal.png); border: none; padding: 4px; background:transparent; } #sketchAreaToolbar QToolButton::menu-arrow:open { border: none; image: url(:/resources/images/icons/smalToolarrow-pressed.png); padding: 4px; background:transparent; } #sketchAreaToolbar QToolButton::menu-arrow:disabled{ border: none; image: url(:/resources/images/icons/smalToolarrow-inactive.png); padding-top: 0px; padding-right:0px; margin: 2px; background:none; } #sketchAreaToolbar #shareProjectButton{ color:#fff; border-bottom: 5px solid #006737; background:#00a55b; height: 55px; width:80px; margin:5px 10px 5px 5px; padding: 3px 0px 0px 0px; } #sketchAreaToolbar #shareProjectButton:hover { color:#fff; border-bottom: 5px solid #148d50; background:#6dba82; padding: 3px 0px 0px 0px; height: 55px; width:80px; margin:5px 10px 5px 5px; } #sketchAreaToolbar #shareProjectButton:pressed { color:#fff; border-bottom: 5px solid #148d50; background:#6dba82; padding: 3px 0px 0px 0px; height: 55px; width:80px; margin:5px 10px 5px 5px; } #sketchAreaToolbar #orderFabButton{ color:#fff; border-bottom: 5px solid #3a2e2a; background:#5e4c49; padding: 3px 0px 0px 0px; height: 55px; width:80px; margin:5px 10px 5px 5px; } #sketchAreaToolbar #orderFabButton:hover { color:#fff; border-bottom: 5px solid #6d5e5a; background:#917d78; padding: 3px 0px 0px 0px; height: 55px; width:80px; margin:5px 10px 5px 5px; } #sketchAreaToolbar #orderFabButton:pressed { color:#fff; border-bottom: 5px solid #6d5e5a; background:#917d78; padding: 3px 0px 0px 0px; height: 55px; width:80px; margin:5px 10px 5px 5px; } /* ################### zoomslider #################### */ QSlider::groove:horizontal { /*this is the slider - "line" */ background:rgb(214,50,34); height: 2px; border-radius:-5px; margin-bottom:10px; margin-top:0px; } QSlider::handle:horizontal { /*this is the handle: it needs negative margin to be "bigger" than the slider - "line" */ width:10px; height:10px; margin-top: -4px; margin-bottom: -4px; background-clip: content; background-image: url(:/resources/images/icons/zoomSlider.png); } QSlider::handle:horizontal:hover { width:10px; height:10px; margin-top: -4px; margin-bottom: -4px; background-image: url(:/resources/images/icons/zoomSliderHover.png); } #ZoomSliderFrame { max-height: 16px; min-height: 16px; max-width: 202px; min-width: 202px; } #ZoomSliderValue { /*this is the number in percent of left to the zoomslider*/ margin: 0px; padding: 0px; border: none; color: #fff; background-color: transparent; width: 35px; max-width: 35px; min-width: 35px; text-align: right; } #ZoomSliderLabel { color: #CCCCCC; width: 15px; max-width: 15px; min-width: 15px; text-align: left; } #ZoomSliderButton { margin-top: 0px; margin-bottom: 0px; margin-left: 5px; margin-right: 5px; padding: 0px; border: none; width: 16px; max-width: 16px; min-width: 16px; } /* next to the zoomslider in the status bar */ #LocationLabel { font-size: 9px; font-weight: 500; color: #fff; } /* ################### the palettes #################### */ FDockWidget { /* only relevant if palette is floating and only visible with tabs inside the widget such as Parts Library */ background-color: #d9d9d9; /* this defines the title text! */ font-size: 14px; font-weight: 150; color: rgb(101,101,101); font-family: Droid Sans; titlebar-close-icon: url(:/resources/images/icons/dockWidgetEmpty_icon.png); titlebar-normal-icon: url(:/resources/images/icons/dockWidgetEmpty_icon.png); } #topMostDock { margin: 0px; margin-top: 8px; border: 0px; } #bottomMostDock { margin: 0px; margin-bottom: 8px; border: 0px; } FDockWidget::title { font-size: 20px; font-weight: 300; height: 35px; font-family: Droid Sans; /*border-top-left-radius: 5px;*/ /*border-top-right-radius: 5px;*/ background: #d9d9d9; border-top: 4px rgb(51,51,51) solid; padding-left: 6px; padding-top: 2px; padding-bottom: 0px; } FDockWidget::close-button, FDockWidget::float-button { margin: 0px; padding: 0px; border: 0px; subcontrol-position: top right; subcontrol-origin: margin; position: absolute; top: 4px; bottom: 0px; height:15px; width:15px; } FDockWidget::close-button { right: 2px; image: url(:/resources/images/icons/dockWidgetCloseNormal_icon.png); } FDockWidget::close-button:hover { border: none; image: url(:/resources/images/icons/dockWidgetCloseHover_icon.png); } FDockWidget::close-button:pressed { border: none; image: url(:/resources/images/icons/dockWidgetClosePressed_icon.png); } FDockWidget::float-button { right: 17px; image: url(:/resources/images/icons/dockWidgetFloatNormal_icon.png); } FDockWidget::float-button:hover { border: none; image: url(:/resources/images/icons/dockWidgetFloatHover_icon.png); } FDockWidget::float-button:pressed { border: none; image: url(:/resources/images/icons/dockWidgetFloatPressed_icon.png); } AutoCloseMessageBox { background-color: #FFFFE1; border: 1px solid black; padding: 5px; font-size: 12px; } /* ################### info view #################### */ HtmlInfoView { margin-right:0px; padding-right:5px; border:none; background:#d9d9d9; } HtmlInfoView QScrollBar:vertical { border: 0px; background:rgba(255,255,255,10); width:10px; margin: 0px 0px 0px 0px; } #infoViewMainFrame { background-color: #d9d9d9; border: 0px; margin: 0px; font-size:14px; } #instanceTitleEditor { /* background-color is set programmatically */ text-align: left; font-size: 16px; font-weight: 600; border: 0px; margin: 5px 5px 5px 5px; padding: 0px; background-color: #ececec; } #iconFrame { /*background-color in the icon-row*/ background-color: #d9d9d9; max-height: 34px; min-height: 34px; margin: 0px 0px 0px 5px; padding: 0px; } #iconLabel { background-color: #fff; } #infoViewLockCheckbox { font-size: 11px; padding: 0px; margin: 0px; } #infoViewPartFrame { font-size: 11px; padding: 0px; margin: 0px; color:#f0f; } /* part description/subtitle */ #infoViewPartTitle, #infoViewPartVersion { margin: 3px 5px 3px 5px; padding: 0px 0px 1px 0px; font-size: 11px; font-weight: normal; color: #323232; background: #d9d9d9; /*just the part discription titel not the part main-titel*/ } #infoViewPartUrl { margin: 0px 4px 0px 4px; padding: 0px 0px 1px 0px; font-size: 11px; font-weight: normal; color: #d63222;/*rgb(110,110,110);*/ } /* section title - these are the subtitels in the infoview (like: placement, properties, tags etc.) */ #expandableViewLabel { padding: 0px 0px 3px 0px; margin: 4px 0px 0px 4px; border: 0px; font-weight: bold; text-align: left; font-size: 11px; color: #D63222; } /* Properties */ #infoViewPropertyFrame { margin: 2px 4px 2px 0px; } #propNameLabel { font-size: 11px; color: rgb(51,51,51); min-width: 65px; max-width: 65px; margin: 0px 0px 0px 4px; padding: 3px 3px 3px 0px; border-top: 1px solid #b1b1b1; font-weight: normal; } #propValueFrame { margin: 0px; border: 0px; padding: 1px 0px 1px 0px; border-top: 1px solid #b1b1b1; } #propValueLabel { font-size: 11px; padding: 3px 3px 3px 0px; font-weight: normal; } #infoViewSpinBoxLabel { font-size: 11px; color: #000000; font-weight: normal; } #infoViewComboBox { font-size: 11px; color: rgb(0,0,0); border: none; /*padding-left: 5px;*/ /* This causes a bad side-effect where the values get covered by the arrows in the drop-down */ } #infoViewComboBox:disabled { color:#323232; background: #d9d9d9; } #infoViewComboBox:!editable:disabled { background: #d9d9d9; } #infoViewComboBox:editable:disabled { background: #d9d9d9; } #infoViewComboBox:!editable { background:#ececec; } #infoViewComboBox:hover { background: #fff; } #infoViewComboBox:on { } /* drop-down arrow of the combobox */ #infoViewComboBox::down-arrow { image: url(:/resources/images/icons/combobox-arrow.png); } /* drop-down button of the combobox */ #infoViewComboBox::drop-down:button { background: #ececec; } #infoViewComboBox::drop-down:hover { background: #ececec; } #infoViewComboBox::drop-down:on { background:#ececec; } /* drop-down list of the combobox */ #infoViewComboBox QAbstractItemView { background-color: #ececec; margin: 3px 0px; padding: 0px; color:#333; } #infoViewComboBox:editable { background: #fff; } #infoViewComboBox:editable:hover, #infoViewComboBox:editable:on { /* BUG: for some reason this background is shown _behind_ the :editable background */ background: #fff; } #infoViewComboBox:hover { background: #ececec; } #infoViewComboBox::drop-down:editable { border-left: 1px solid #989898; background: #ececec; } #infoViewLineEdit { font-size: 11px; color: rgb(0,0,0); background: #fff; border: none; } #infoViewLineEdit:hover, #infoViewLineEdit:focus { background:#fff; } #infoViewLineEdit:disabled { background: #d9d9d9; color: #3c3c3c; } /* the label between the two spinners in the schematic frame */ #infoViewOfLabel { font-size: 11px; padding: 3px 3px 3px 0px; margin: 0px; width: 30px; min-width: 30px; max-width: 30px; } /* this is the style of buttons - mostly in generic parts like: "edit pin lables"-button */ #infoViewButton { font-size: 11px; /*min-width: 100px;*/ text-align: center; background: #ececec; border: 1px solid #b1b1b1; border-radius: 0px; padding: 2px; color: #323232; margin-top:3px; margin-bottom:3px; } #infoViewButton:hover { background: #fff; } #infoViewButton:pressed, #infoViewButton:on { background: #666; color: #fff; } /* the title if you have a "advanced settings" box - i.e. gereric ics */ #infoViewGroupBox { border: 1px solid gray; font-size: 11px; font-weight: bold; margin: 5px 0px 0px 0px; padding: 15px 2px 5px 2px; } /* the text inside the advanced settings box */ #infoViewGroupBoxLabel { font-size: 11px; } #infoViewCheckBox { font-size: 11px; padding: 0px; margin-right: 2px; } /* Connections */ #connectionsFrame { margin: 0px; } #connectionsLabel { font-size: 11px; color: #575757; min-width: 65px; max-width: 65px; margin: 0px 0px 0px 4px; padding: 3px 3px 3px 0px; border-top: 1px solid #9b9b9b; } #connectionsValue { font-size: 11px; color: black; margin: 0px; border: 0px; padding: 3px 3px 3px 0px; border-top: 1px solid #9b9b9b; } /* Tags */ #tagsValue { font-size: 11px; margin: 0px 4px 0px 4px; padding: 2px 0px 0px 0px; border-top: 1px solid #9b9b9b; } /***************** Parts Bin *****************/ #partsBinHeader { margin:0; padding: 0px; max-height: 28px; height: 28px; } #partsBinHeaderSeparator { border: 2px solid #b1b1b1; } SearchLineEdit, #searchStackWidget { max-height: 20px; height: 20px; } #partsBinLabel { /*the chosen libery-labelname and background*/ background-color: #d9d9d9; font-size: 14px; color: rgb(50,50,50); font-weight: bold; /*text-transform: uppercase;*/ padding-left: 1px; margin: 0; } PartsBinIconView { padding: 3px; /*this is the size of the grey-border around the partsbin-iconview*/ } PartsBinIconView QPushButton{ padding: 3px; selection-background-color: #b1b1b1; background-color:#d9d9d9; border: 0; border-bottom: 1px solid #D0D0D0;/*this is the size of the grey-border around the partsbin-iconview*/ } PartsBinListView { /*the styling if you look the parts-window in list view mode*/ selection-background-color: #b1b1b1; background-color:#d9d9d9; border: 0; border-bottom: 1px solid #D0D0D0; } PartsBinListView::item:selected { background:#b1b1b1; color:#333; border:none; } PartsBinListView::item:selected:!active { background:#dadada; color:#333; border:none; } PartsBinListView::item:selected:active { background:#b1b1b1; color:#333; border:none; } PartsBinListView::item:hover { background: #a5a5a5; color:#fff; border:none; } #partsBinHeader, PartsBinPaletteWidget, PartsBinPaletteWidget SimpleEditableLabelWidget, PartsBinPaletteWidget InfoGraphicsView { background-color: #d9d9d9; /*background of parts library*/ border: none; } #partsBinContainer { background-color: #d9d9d9; /*background of parts library*/ border-top: 1px solid #b1b1b1; } PartsBinPaletteWidget, StackTabWidget, StackTabWidget::pane { /* trying to get rid of the line across the top of the tabs */ border: none; background-color:transparent; } StackTabBar::tab { margin: 0px 0px 0px 0px; padding: 0px; min-width: 32px; max-width: 32px; min-height: 32px; max-height: 32px; border: 0; background: #d9d9d9; /*this is the color for the active tab*/ font-weight: 600; } StackTabBar::tab:selected { /*color for not active tab*/ color: #b1b1b1; } StackTabBar::tab:!selected { background-color: #b1b1b1; /*#9b9b9b;*/ /*color for not active tab*/ color: #ff0;/*#686868;*/ } StackTabBar::tab:!selected:hover { background-color: #ececec; /*the hover status of the library buttons*/ } StackTabBar[current="true"]::tab:selected { border:5px solid #00f; /*background-color: black; color: white;*/ } /*styling for the scrolling arrows for the part-bins*/ StackTabBar QToolButton { background: rgba(217,217,217,200); border:0px; } StackTabBar QToolButton::up-arrow { background-image: url(:/resources/images/icons/TabScrollArrowButtonUpNormal.png); background-repeat: none; background-position: center; } StackTabBar QToolButton::up-arrow:pressed { background-image: url(:/resources/images/icons/TabScrollArrowButtonUpPressed.png); background-repeat: none; background-position: center; } StackTabBar QToolButton::down-arrow{ background-image: url(:/resources/images/icons/TabScrollArrowButtonDownNormal.png); background-repeat: none; background-position: center; } StackTabBar QToolButton::down-arrow:pressed{ background-image: url(:/resources/images/icons/TabScrollArrowButtonDownPressed.png); background-repeat: none; background-position: center; } #binToolButton { background-color: transparent; font-size: 11px; } #partsBinCombinedMenuButton #partsBinCombinedMenuButton:pressed { margin: 0; margin-top: 2px; margin-left: 2px; padding: 0; border: 0; width: 25px; background-color: transparent; background-attachment: fixed; background-position: 100% 100%; background-repeat: no-repeat; } #partsBinCombinedMenuButton { background-image: url(:/resources/images/icons/partsBinCombinedMenu_icon.png); background-repeat: no-repeat; border: none; height:12px; width:12px; } #partsBinCombinedMenuButton:pressed { background-image: url(:/resources/images/icons/partsBinCombinedMenuPressed_icon.png); background-repeat: no-repeat; height:12px; width:12px; } #partsBinCombinedMenuButton:hover { background-image: url(:/resources/images/icons/partsBinCombinedMenuHover_icon.png); background-repeat: no-repeat; height:12px; width:12px; } #partsBinCombinedMenuButton::menu-indicator { image: url(:/resources/images/icons/noArrow_icon.png); } /**************** LayerWindow and UndoWindow ***********************/ LayerPalette, LayerPalette::title, LayerPalette QObject { background: #d9d9d9; } QUndoView { background: #d9d9d9; show-decoration-selected: 1; } QUndoView::item:selected { background:#b1b1b1; color:#333; border:none; } QUndoView::item:selected:!active { background:#dadada; color:#333; border:none; } QUndoView::item:selected:active { background:#b1b1b1; color:#333; border:none; } QUndoView::item:hover { background: #a5a5a5; color:#fff; border:none; } /**************** Fab Quote ****************/ #quoteOrder { font-size: 16px; font-weight: bold; color: rgb(214,50,34); margin: 0px 0px 0px 18px; text-align: left; font-family: Droid Sans; font-weight: bold; } #quoteMessage { font-size: 11px; color: #000000; margin: 0px 0px 0px 10px; text-align: left; font-family: Droid Sans; } #quoteAdditional { font-size: 11px; color: #000000; margin: 0px 0px 0px 10px; text-align: left; font-family: Droid Sans; } #quoteTable { background-color: transparent; border: 0px solid transparent; margin: 0px 0px 0px 10px; font-size: 11px; font-family: Droid Sans; } #quoteTable QHeaderView { background-color: transparent; color: #000000; border: 0px solid transparent; } #quoteTable::item::disabled { margin-right: 10px; color: #000000; } #quoteVisitButton { color: #ffffff; text-align: center; /* text-align: center is not implemented. QSS is shit. */ qproperty-alignment: AlignCenter; font-size: 16px; font-family: Droid Sans; font-weight: bold; } /************************ About Box ****************************/ #aboutText { border: 0px; background-color: transparent; margin-top: 0px; margin-bottom: 0px; } /**************** sketch widgets ****************/ MainWindow { qproperty-fireQuoteDelay: 250; /* milliseconds */ background:#aaa; } SketchWidget { border: 0px; font-weight: bold; background-image:none; } PCBSketchWidget { qproperty-gridColor: rgba(150,200,250,20); qproperty-ratsnestOpacity: 1.0; qproperty-ratsnestWidth: 0.5; } BreadboardSketchWidget { qproperty-gridColor: rgba(0,50,100,20); qproperty-ratsnestOpacity: 1.0; qproperty-ratsnestWidth: 1.0; } SchematicSketchWidget { qproperty-gridColor: rgba(0,50,100,20); qproperty-ratsnestOpacity: 1.0; qproperty-ratsnestWidth: 0.5; } /**************** the breadboard-, schematic-, pcb-view switcher ****************/ WaitPushUndoStack { background-color: white; color: #0f0; } SearchLineEdit { color: #000000; font-size: 11px; font-family: Droid Sans, LucidaSans, Tahoma, Arial, sans-serif; border: 1px solid #8c8c8c; } /*############# aboutbox ################*/ /*############# colorDialog################*/ QDialog { background-color:#d9d9d9; color:#333333; } QDialog QTabWidget { background-color:#d900d9; color:#333333; } PCBSketchWidget QListWidget { background-color:#0ff; } QDialog QFrame, QDialog QFrame QLabel { color:#333333; } PCBSketchWidget QDialogButtonBox, QDialog QDialogButtonBox, QDialogButtonBox { font-size: 11px; /*min-width: 100px;*/ text-align: center; background: #ececec; border: 1px solid #b1b1b1; border-radius: 0px; padding: 10px; color: #323232; margin-top:3px; margin-bottom:3px; } QDialog QPushButton { font-size: 11px; /*min-width: 100px;*/ text-align: center; background: #ececec; border: 1px solid #b1b1b1; border-radius: 0px; padding: 2px; color: #323232; margin-top:3px; margin-bottom:3px; } PCBSketchWidget QDialogButtonBox:hover, QDialog QDialogButtonBox:hover, QDialogButtonBox:hover, QDialog QPushButton:hover { background: #fff; } PCBSketchWidget QDialogButtonBox:on, PCBSketchWidget QDialogButtonBox:pressed, QDialog QDialogButtonBox:pressed, QDialog QDialogButtonBox:on, QDialogButtonBox:pressed, QDialogButtonBox:on, QDialog QPushButton:pressed, QDialog QPushButton:on { background: #666; color: #fff; } #PrefsDialog { background:#f0f; background-color:#0ff; }fritzing-app-0.9.3b/resources/styles/linux-fritzing.qss000066400000000000000000000002501272363601300233550ustar00rootroot00000000000000/* override the combo box color because the linux menu background is dark grey so the standard color is too dim */ #infoViewComboBox { color: rgb(0xd0,0xd0,0xd0); }fritzing-app-0.9.3b/resources/styles/linux-gnome-fritzing.qss000066400000000000000000000000001272363601300244510ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/styles/linux-kde-oxygen-fritzing.qss000066400000000000000000000003021272363601300254230ustar00rootroot00000000000000/* probably obsolete now StackTabWidget::pane { position: absolute; top: 0.43em; } StackTabBar::tab { min-height: 1.5em; padding: 0; } #partsEditor QTabWidget::pane { top: 0.65em; } */fritzing-app-0.9.3b/resources/styles/mac-fritzing.qss000066400000000000000000000000421272363601300227550ustar00rootroot00000000000000/* add Mac-specific styles here */fritzing-app-0.9.3b/resources/styles/newpartseditor.qss000066400000000000000000000070661272363601300234520ustar00rootroot00000000000000/* new parts editor */ #NewNewPartsEditorConnector0Frame { background-color: #cccccc; margin: 0px; padding: 0px; border: 0px; } #NewNewPartsEditorConnector1Frame { background-color: #eeeeee; margin: 0px; padding: 0px; border: 0px; } #NewNewPartsEditorConnectorFrame { margin: 0px; padding: 0px; border: 0px; } #PESvgView { background-color: #000000; color: #000000; } #PEToolView { background-color: #000000; color: #000000; } /*try to change tab-menu without luck ... QTabWidget#peTabs ::item { background-image:none; border: 10px solid #f0f; color:#00f; font-weight:bold; } QTabWidget#peTabs{ border: 10px solid #f0f; color:#00f; font-weight:bold; background-image:none; } QTabWidget#peTabs ::pane { border: 10px solid #f0f; background-image:none; } QWidget#peTabs ::pane { border: 10px solid #f0f; background-image:none; } #peTabs QMenu { background:#f0f; background-image:none; font-size: 10px; font-weight: bold; } QMenu#peTabs { background:#f0f; background-image:none; font-size: 10px; font-weight: bold; } */ /* the Infos in the right window. */ QLabel{ background:transparent; background-image:none; font-size: 11px; } QComboBox, QPushButton, QPushButton:enabled, QPushButton:active{ background-image:none; font-size: 11px; text-align: center; background: #ececec; border: 1px solid #b1b1b1; border-radius: 0px; color: #323232; padding:5px; margin-top:3px; margin-bottom:3px; } QComboBox:hover, QPushButton:hover{ background-image:none; color: #fff; background:#666; font-size:11px; } QComboBox:pressed, QPushButton:pressed{ background-image:none; color: #d9d9d9; background:#f23926; font-size:11px; } QComboBox:disabled, QPushButton:disabled{ background-image:none; color: #656565; background:transparent; font-size:11px; } /* drop-down buton of the combobox */ QComboBox::drop-down:button { background: transparent; border:none; } QComboBox::drop-down:hover { background: #f23926; border:none; } QComboBox::drop-down:on { background: #f23926; border:none; } QComboBox::drop-down:selected { background: #f23926; border:none; } QComboBox::down-arrow { image: url(:/resources/images/icons/combobox-arrow.png); border:none; } /* drop-down list of the combobox */ QComboBox::QAbstractItemView { background-color: rgb(236,236,236); margin: 0px; padding: 0px; } QSplitter::handle { height: 4px; width: 4px; color: #666666; background-color: #666666; } /* spinbox styling for connector position */ QDoubleSpinBox { background: rgb(255,255,255); font-size: 11px; color: #000; border: none; } QDoubleSpinBox::down-button { background: #fff; border: none; image: url(:/resources/images/icons/spinArrowButtonDownNormal.png); margin-bottom:1px; } QDoubleSpinBox::down-button:pressed { background: #fff; border: none; image: url(:/resources/images/icons/spinArrowButtonDownPressed.png); margin-bottom:1px; } QDoubleSpinBox::up-button { background: #fff; border-bottom: none; image: url(:/resources/images/icons/spinArrowButtonUpNormal.png); } QDoubleSpinBox::up-button:pressed { background: #fff; border-bottom: none; image: url(:/resources/images/icons/spinArrowButtonUpPressed.png); } /* QTableView { selection-background-color: #f0f; background-image:none; image:none; } QTreeWidgetItem { selection-background-color: #f0f; background-image:none; image:none; } */fritzing-app-0.9.3b/resources/styles/partseditor.qss000066400000000000000000000105011272363601300227240ustar00rootroot00000000000000/* #partsEditor QLabel { font-family: LucidaGrande, LucidaSans, Tahoma, Arial, sans-serif; font-size: 12px; font-weight: bolder; } PartSpecificationsWidget { padding: 0px; margin: 0px; border: 0px; border-top: 2px solid #52182C; } PartSpecificationsWidget #scroll { padding: 5px; background-color: #7F7F7F; border: 0px; margin: 0px; } PartConnectorsWidget { border-top: 2px solid #52182C; background-color: #7F7F7F; } QSplitter::handle { height: 6px; width: 6px; background-image: url(':/resources/images/splitterHandle.png'); background-repeat: no-repeat; background-position: center; } PartsEditorViewsWidget QSplitter::handle { background-color: #7F7F7F; } PartsEditorView { border-radius: 5px; background-color: #D8D8D8; } SingleConnectorInfoWidget[selected="true"]{ background-color: #D8D8D8; } MismatchingConnectorWidget[selected="true"]{ background-color: #EDEDED; } SingleConnectorInfoWidget[selected="false"], MismatchingConnectorWidget[selected="false"] { background-color: transparent; } SingleConnectorInfoWidget { padding-left: 9px; } SingleConnectorInfoWidget #description { color: #777777; } #header { background-color: #330011; } #center, #footer { background-color: #52182C; } #footer { padding: 5px 0px 5px 0px; margin: 0px; border: 0px; } PartSpecificationsWidget PartEditorViewsWidget QLabel, PartSpecificationsWidget AbstractEditableLabelWidget #title, PartSpecificationsWidget HashPopulateWidget QLabel, ConnectorsInfoWidget #title { color: #52182C; } PartEditorViewsWidget { } PartSpecificationsWidget HashPopulateWidget, PartSpecificationsWidget PartEditorViewsWidget, PartSpecificationsWidget AbstractEditableLabelWidget, ConnectorsInfoWidget { background-color: #B2B2B2; border-top-left-radius: 10px; border-top-right-radius: 10px; } ConnectorsInfoWidget QScrollArea, #connInfoContent { border: 0px; background-color: #B2B2B2; } HashLineEdit { background-color: #D8D8D8; } AbstractEditableLabelWidget QLabel { color: #7F7F7F; } #createdBy { font-weight: normal; font-style: normal; padding-top: 5px; } #iconImage, #partTitle { background-color: #996B7A; } #partTitle { border-radius: 6px; padding: 4px; padding-left: 15px; } #partTitle QLabel { color: #656565; font-size: 18px; } #partsEditor QTabWidget::pane { padding: 0px; margin: 0px; border: 0px; position: absolute; } #partsEditor QTabWidget::tab-bar { left: 5px; top: 5px; } #partsEditor QTabBar::tab { margin-top: 5px; border-top-left-radius: 4px; border-top-right-radius: 4px; padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px; color: #821F09; border-bottom: 0px; min-width: 6.5em; max-width: 6.5em; margin: 0; padding: 0; min-height: 1.5em; } #partsEditor QTabBar::tab:selected { background-color: #D63222; border-bottom: 0px; font-weight: bold; } #partsEditor QTabBar::tab:!selected { border: 2px solid #D63222; border-bottom: 0px; } #partsEditor QTabBar::tab:!selected:hover { border: #F23926; } #scroll_test { background-color: green; } #mismatchConns { background-color: #d8d8d8; margin-bottom: 5px; padding-left: 2px; padding-bottom: 4px; padding-top: 4px; } #mismatchConnMsg { color: red; } #mismatchConnsHeader, #mismatchConnsFooter { font-size: 11px; color: #777777; } #mismatchConnsHeader { font-style: normal; } #mismatchConnsFooter { padding-left: 15px; } #mismatchConnsFooter { margin-top: 4px; margin-left: 3px; } PartsEditorViewsWidget { background-color: #7F7F7F; } #iconBrowseButton { margin: 0px; padding: 0px; border: 0px; } #guidelinesLabel { margin-top: 5px; font-size: 11px; } #partsEditor QComboBox, #partsEditor QPushButton, #partsEditor QPushButton:enabled, #partsEditor QPushButton:active{ background-image:none; color: #fff; background:transparent; font-size:12px; height: 34px; } #partsEditor QComboBox:hover, #partsEditor QPushButton:hover{ background-image:none; color: #d9d9d9; background:#f23926; font-size:12px; border:none; height: 34px; } #partsEditor QComboBox:pressed, #partsEditor QPushButton:pressed{ background-image:none; color: #d9d9d9; background:#f23926; font-size:12px; border:none; height: 34px; } #partsEditor QComboBox:disabled, #partsEditor QPushButton:disabled{ background-image:none; color: #656565; background:transparent; font-size:12px; border:none; height: 34px; } */fritzing-app-0.9.3b/resources/styles/programwindow.qss000066400000000000000000000134541272363601300232750ustar00rootroot00000000000000 /*this is the background behind the tabs */ #programmingWindow #center { background-color: #85807e; } /*the main styling for the tabs in the programm-tab*/ #programmingWindow QTabWidget::tab-bar { /*top: %2px;*/ left: 10px; right: 10px; /*top: 5px;*/ background-color: #ff0; color: #f0f; } /*this is the background around the tab-frames*/ #programmingWindow QTabWidget::pane { /*top: %1px; the value is swapped in at runtime in ProgramWindow::setup() */ padding: 0px; margin-top: 0px; border: 0px; background: none; } /*this is the base-styleing for the tabs*/ #programmingWindow QTabBar::tab { margin-top: 5px; margin-left: 8px; margin-right: 0px; margin-bottom: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; background: #5f5756; border: 0px; min-width: 6.5em; min-height: 1.5em; } /*the active tab*/ #programmingWindow QTabBar::tab:selected { background-color: #1c1a18; border: none; font-weight: book; color: #fff; } /*the inactive tabs*/ #programmingWindow QTabBar::tab:!selected { border: none; color: #999999; font-weight: book; background-color: #5f5756; } /*the inactive and active tab-hover-effect */ #programmingWindow QTabBar::tab:!selected:hover { background-color: #474141; border:none; color: #c1c1c1; font-weight: roman; } /*the "closebuttons" on the code-file-tabs*/ #programmingWindow QTabBar::close-button { image: url(:/resources/images/icons/dockWidgetCloseNormal_icon.png); subcontrol-position: right; padding-top: 5px; } #programmingWindow QTabBar::close-button::hover { image: url(:/resources/images/icons/dockWidgetCloseHover_icon.png); subcontrol-position: right; } #programmingWindow QTabBar::close-button::pressed { image: url(:/resources/images/icons/dockWidgetClosePressed_icon.png); subcontrol-position: right; } /*the actual code area */ #programmingWindow #code { color: #ede0ce; background-color: #1c1a18; padding-top: 8px; padding-left: 8px; border: none; } /* the splitter/drag handle between code and console */ #programmingWindow QSplitterHandle, #programmingWindow #splitter { background: #5f5453; background-color: #5f5453; color: #5f5453; } /* console output window */ #programmingWindow #console { background-color: #8c8686; color: #3c3936; border: 0px; padding-top: 8px; padding-left: 8px; } /* toolbars at the bottom (new, open, save)*/ #programmingWindow #toolbarSelector { width: 120px; min-width: 50px; max-width: 190px; padding: 0 10px; } #programmingWindow #boardSelector { /* width: 120px;*/ min-width: 50px; max-width: 160px; padding: 0 10px; } /*the closed dropdown menues for platform, board, port */ #programmingWindow #toolbarSelector QComboBox { margin-top: 5px; } /*the text below the dropdown menus (platfom, board, port)*/ #programmingWindow #toolbarSelector QLabel { color: #ffffff; font-size: 11px; font-family: Droid Sans; font-weight: normal; qproperty-alignment: AlignCenter; } /*don't know-... maybe console window*/ #programmingWindow #unableToProgramLabel { color: #333; background-color: #FFFFC2; padding-left: 5px; padding-right: 5px; padding-top: 1px; padding-bottom: 1px; } /* the dropdowns */ #programmingWindow QComboBox, #programmingWindow QPushButton, #programmingWindow QPushButton:enabled, #programmingWindow QPushButton:active{ color: #fff; background:#821f09; font-size:12px; border:none; height: 34px; padding-left:8px; padding-right:4px; } #programmingWindow QComboBox:hover, #programmingWindow QPushButton:hover{ color: #d9d9d9; background:#f23926; font-size:12px; border:none; height: 34px; } #programmingWindow QComboBox:pressed, #programmingWindow QPushButton:pressed{ color: #d9d9d9; background:#821f09; font-size:12px; border:none; height: 34px; } #programmingWindow QComboBox:disabled, #programmingWindow QPushButton:disabled{ background:#AC6A5B; color: #d1aca4; font-size:12px; border:none; height: 34px; } /* drop-down button of the combobox */ #programmingWindow QComboBox::drop-down:button { border:none; } #programmingWindow QComboBox::drop-down:hover { border:none; } #programmingWindow QComboBox::drop-down:on { border:none; } #programmingWindow QComboBox::drop-down:selected { border:none; } #programmingWindow QComboBox::down-arrow { image: url(:/resources/images/icons/combobox-arrow.png); border:none; } /* drop-down list of the combobox */ #toolBarComboBox QAbstractItemView { color: #000; background-color: #ececec; } #toolBarComboBox QAbstractItemView::item { color: #000; border-bottom: 5px solid white; margin:3px; } #toolBarComboBox QAbstractItemView::item:selected { border-bottom: 5px solid black; margin:3px; } /* scroll bars */ #programmingWindow QScrollBar:vertical { border: 0px; background:rgba(255,255,255,10); width:10px; margin: 0px 0px 0px 0px; } #programmingWindow QScrollBar::handle:vertical { border: 2px solid #b2b2b2; border-radius: 2px; background: #b2b2b2; min-height: 5px; } #programmingWindow QScrollBar::add-line:vertical { background: none; height: 1px; subcontrol-position: bottom; subcontrol-origin: margin; } #programmingWindow QScrollBar::sub-line:vertical { background:none; height: 1px; subcontrol-position: top; subcontrol-origin: margin; } /* Serial Monitor Console */ #consoleWindow QPlainTextEdit { background: #222; color: #eee; } /* not used styling */ /* #programmingWindow #header { background-color: #f0f; padding: 0px; padding-bottom: 0; } */ /* */ /* #programmingWindow { background-color: #ff0; } */ /* */ fritzing-app-0.9.3b/resources/styles/updatedialog.css000066400000000000000000000006221272363601300230130ustar00rootroot00000000000000table { border-width: 1px 1px 1px 1px; border-spacing: 2px; border-style: outset outset outset outset; border-color: white white white white; border-collapse: separate; background-color: white; } table td { border-width: 1px 1px 1px 1px; padding: 1px 1px 1px 1px; border-style: inset inset inset inset; border-color: white white white white; background-color: rgb(255, 250, 250); } fritzing-app-0.9.3b/resources/styles/win-fritzing.qss000066400000000000000000000000461272363601300230160ustar00rootroot00000000000000/* add Windows-specific styles here */fritzing-app-0.9.3b/resources/system_icons/000077500000000000000000000000001272363601300210335ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/system_icons/fz_icon.svg000066400000000000000000000062361272363601300232120ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/fzb_icon.svg000066400000000000000000000124621272363601300233520ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/fzbz_icon.svg000066400000000000000000000107551272363601300235470ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/fzm_icon.svg000066400000000000000000000076151272363601300233710ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/fzp_icon.svg000066400000000000000000000077071272363601300233760ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/fzpz_icon.svg000066400000000000000000000111141272363601300235530ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/fzz_icon.svg000066400000000000000000000074531272363601300234060ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/system_icons/linux/000077500000000000000000000000001272363601300221725ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/system_icons/linux/fritzing_icon.png000066400000000000000000000024541272363601300255510ustar00rootroot00000000000000PNG  IHDRkXTfPLTE2"XKeYL>范?0u뙑rgĿϏj`KV ypﰗm>0[Pb/ xM@ǵ !@bKGDޕztIDATx[{0@j'nHls^a;IL~iL:%I|^/a~euW8٢럯+PV ʕWUH_ Q?w2鰇tnnPRu ]Auԁ\:x5_<$A/ ˿N F*A WD62 ?& | ?@<'Ac" kiO&U{yTth\6inJV:h_:qi6p<ZJS4u*z]G^(tJDžaľʅ @X&~\| ;e72H,Mr%BNx 7T1oB^P "8GՀHҋ@5 ? K/F=ha#K/w^8 xS?" ~.0 p!ab? Ps= OWY3rk5$I^J-TեB @ @ @ @ @@eVwbӲy<\ܙvpDgg'qsbӝ-Tk(JkXOIvqz!wuK+c$}ߒ3Qh.x_)lbqv{oEYWhJb_8<mZ.R8=?9^l[NNW q>"xu]MO|nUV/ ޯ~Prhjw48'if;[k`ire,\4 ¹At @:i 4@H HAt @:i 4@H >:IENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fz_icon128.png000066400000000000000000000030461272363601300245650ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<lPLTE -뙑K젙eY?0L>uXK范rgxM@b/ ypV Ŀǵzpj`g\2"ZdS$tRNSX,  IDATxRJ P<δ-j[ ڶKO?*J" hp[ /8_:>-lj0 (" C@2zm (@*R@'R:dj ( @"B@7rV X+`uV X#``V X*``!e X"` `1f +``Ayf #``,uEUѤk|>K*O (^fa(̿-xv|[|uC2@܇ V *F:];XxMw'I?jWL \hO/>Jү"@Uy:D$3 c$gF+LĽ$>s膇r{,R{mCh%2C{ $Ci,d!ӝ`{PbB`[eKMHesc)rۂTw Ta4w!|c R} =% `\)rnYJF޽#g0ў[<̑p,|y3?p;8>]y8?Fw_ m9q&`\mS>@4 .ࢷ(ӧ]JH[ Yj'hS<:+0OȘZkkH Af mQU,3``7Ap4@`:@xʏ% IENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fz_icon256.png000066400000000000000000000031261272363601300245660ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<PLTE2"g\ꘐ )eY뙑KC3젙M?$?0XKuL>rg范Ŀb/ σ[Pj`xM@ՔV +ڪ;*ﯩǵypm>0ˑcsX1tRNSN!IDATxgs0 `iJhґtzVT@_|$qG wu|iYP@@8)H@@IAD@@4rq H  @) P@(T2TrH  @! @@8tL0Q@4 TL0A@ x-0R@8MT0B@pm 0P@!0T  0@@):ȝ'x \,"+pQҥǢͦx \`bdl'5of^|ڟ.'gV-ܫiJ~/@Z%Joc`h8h0_v {M6w$A-@g Id!< $ @UWZgKKe\1h۟Bv|O!(c0R< C |* iVs_jgܺM9XD{|zb({vtnodPaΉK3l pdP9i.` %߾s?:īGj'C"kz/^S x ,x {:6^`ݭ`o$ sXכ/ѻYo' g9+>]AW WrgXKM4范u򿺟'賂V ĿH2U:<)ϖ -#ypOFkOxM@B-"|im>0[P N6({t,R=I1S8吁䗏?)@6le X<ﰡ?+6"k%E/-~C.}t<)̘1'5$[?x3!rǵ+?)b/ 뜊/ aC2"~SWntRNS<3IDATxgw6`U<&vFѽtHB٦@PBPp9xI{ٛ$^;!1XoF#2rg)_?\_@V4:0  |U:r!_q<rڥҨ7{t4˄cgc 5_Me[@GUX\S^ Tq ZUL.P0(T?0XK㷇rg范򿺌$uN4xM@ﰡĿ<)[P飘;&-VJǵV j`rb/ #ԐH2"U:7%m>0睔ڵבkOI1ĸ,ؒ,2" B-paf^Ale#r15$1'̘ "" wK4yp\?ȋN6u[6"@6=}DntRNS<3IDATxi4`ǞV8^bㅄtii) m-%g-'SIgrr֕t%[3aXqr6x~IK%fq\|b~<,#m?rh\'Cqk7<u}%pm/< x:Iz̫EHs "&^c"۱Ƿ߶#vHh۶g,@Ъvf,k~5 k~;4 lw؉` ` 0jwl}8,@-#?&R{{ $ '7#u9#7'ȝ9.@ 7_ >&@к2G $~S |Px:-W%OM|DjɨŒh&o$<`>,E/ǂ ¼HQj$* m.Dad+qy j'pӽ^J)gE#P `G/:3G}RoxQbA%$PQH]:[ Zc2@i2!P`qL*$뙑?0KpL>uXK范rgxM@N4Ŀ<)B-ĉ ߏj`?)I1H2pr%U:.VJ o\V ᕍ랔C.,(OF쪜:( @6̘\?6"w H6#3#+^AĽaC2"UGZtRNS֊o8IDATxgw0Av2$M&{teG*acCjF'p|pqX/^~B}VgkDook}rBt81 U\S `S04GؕF)dSR\g~j%$S_"l B2kEc8,T,bQ nG@Ox6;6MX+ ~[+S `7w6d`0΄@h `(-W כ( ;f3OL{9l/FJtģ `r) ^n?Mԇ#JoiC8@wb!,(TT'AmgCIV@h JA#~pyTAe`5 e%o]];@\nGmf<[+2}֐\X<anpۢ,`鍦o< ?q ?!Y0-,mwImSTm]˸+\#EgA .ҭD8*<_FleNO[+^V%҈x+wM,~h[6Zt Iw}'JR@u(F4+T/\9IENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzbz_icon256.png000066400000000000000000000042711272363601300251240ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<JPLTEpeY1"1!wrJ3+뙑9%N4K?0L>XKrgu范M4򿺟'賂V ĿH2U:<) -ypxM@B-kOOF"|i#m>0[PS8<)ĈN6뜊?+5${t吁[?X<b/ %ܶleﰡ}tkr3! ?)6"䗏+ǵ-̘(~xI11'E/R=@6 ?)C.,/ aC2"&ntRNS<3IDATxW{6`mE<6vht޻[˔%e\ʶ &GXobdKp昍>XrG171_!L׫_9yIC;H  H8y,Jj6J><'IO (-G?H<*x@),]R:R9F$+G'/ %V" )]bP<v~Zi9oEM,=f4Ț,[I h*o&'@П$ksR5(f4)gDŽ =⨸OU WU>Fɽh2iUvgU"Rww8ni\p\P0]9AbVWOjϭi|H Hh ppo>!wb.swzS7//Y+?16N 0 x\Jz M 6}.zpX S*>[k Q0DL/tV0t02`Hq1IENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzm_icon128.png000066400000000000000000000033221272363601300247370ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<lPLTE -뙑KeY젙?0L>uXK范rgxM@ypb/ V Ŀzpǵj`g\2"x$tRNSX, IDATxv0 ਲXtJ}w<4L۔HF29-jGqO~{T %@!;!@ 1 0-@A 0)@QsT 0%@a30!@q}th 0%@G\V X+`#uV X# `3 X*`Ce X"`IԄh=u4J @Y`S82>X@U`K?6{~ae8P[sP2P.+@66 xSFi(E][ Q&?lYj: `xP!<@ `v1@T El0+"e 1HSj ;E?`@ E1?X,Ch% ]z dI U%jp&z@QL3słmBHZaS\.,'G: iAWr?2KnJ%ۗkJnh%=U|4?C<,v `3;ś9 Eg݋Yc$0oGsPc-0z?B_ @s[)< =??g柁V{QXFPL % *\tqY.nIJڱR?57/ǝO]2mWNo8WCԕ\PY D,N4BZ+\C壥^v$yq|(k+jVn M#JrrB+i=͘3 R}EqG8c1¾ =)n 0JlZ4pxTo0°';~R%ΎXZɴ^86SNu0hfuR=Oo(b BǛ1ŗ`e++.6E |,Yq_v8+@ ;)۔e5秒ADLo%&Ò(KDV<'~"iWN`!@x^ 7 m=LT ?<~v*> ryw]JQAp9&t !I ߈1Sr|)FRIENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzm_icon256.png000066400000000000000000000033711272363601300247450ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<PLTE2"g\ꘐ )뙑eYKȲC3젙M?$?0XKL>urg范Ŀ[PϏj`b/ xM@Ք;*ﯩڪm>0+ǵypˑV 6\;1tRNSN!IDATxis8`{3 6̽;u[c!(?٭#zo~o˻~z:C"8 J#Bn(8 BbX `[ev(X `S&EX `K.%v(X `C6, T>B8,`D&`0WLF8`0G fL`0UD8L` 0E'B$"Q"_0ZrM`]2_*]0RJ~y^|Fw }3/q}_?^]>XX '2-FM`@Ɉ+<}ؕ* tx 8ɢ!: @Tp֡H@u3[v4h)y1q2&,\` 'ĸllLy c7D]&j, ո/r|-.*} x%QxP]>mNӐ2nѐHM84.lB#/rX.`O>?|8Ňhsԇ>>" 5*?Qp8,`7/І jQ+3ZA}aڣvi =o' 7 ^ApW<Ȥhu8wᜨfMEA/WRCSy8*ű2ϕZ4/d4Mc)NB,D@\+eX(o&Oj'bY* qaiT3+>? @R7H1lT1m{__x(saY' e1w9~G%[5ȗsǚ;@$'I޷rx HmaWjRvڎC:zɀʻ'_2iB@/6@M`pTe!Ŧ+ NBq&0M@fw/iҏ h91!U'mV^fDz83e o Ҙ*@y ̇+&1ge :W,"z8>@7Fhu@檺M8+|Zsik㭩PQxY4uyy~F":E("Iihs 6I0[h #j u`/+n.feIENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzp_icon128.png000066400000000000000000000032051272363601300247420ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<lPLTE -뙑KeY젙?0L>uXK范rgxM@ߚypb/ V Ŀzpǵj`g\2"|Nb$tRNSX, IDATx뒛8!_>I6}w܊ @Qij`n_Gs~:C"8 J#Bn(8 BbX `[ev(X `S&EX `K.%v(X `C6 0``&H&l`UFF8l`Ef`VJ8` F' ҤѦf;ȼ2@[`W/dZ JqU>l /+~m t{3@܇ :^}@Vh[7@tIHZ_<`WChCh68A"@ErZhRhLd]ӜE #a]f ?*@ U+8>D`u2W!D:KG1i%Xr룘`LhI ^"Cj+H ,sbJ^ !ܪ.]*^_]_;E@_QxyvDfo4q8:yp7D>(B>x +0zz?LSQO`s}{_^^^ƣ@r{Qa3Sb:=}_p #R;V;Oϵ#:90ӻ!]~ogWdp~!.Pv*7YUW$u, dP Z+3 \< VJ$uco\>ypeFG2z)yɑ4W9{SGg@4KNۆ KCW1yC} leDtz7WG¬пh5`a)~+M^\6Ӂ}>HPJ2SKQ c?-T R@>]4Z= @pf(y@n_Ւa@:Ҙ&` Pg3ST4VHQT;0F4< ` _&́#fIENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzp_icon256.png000066400000000000000000000032651272363601300247520ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<PLTE2"g\ꘐ )뙑eYKȲC3젙M?$?0XKuL>rg范Ŀ[Pj`b/ xM@ՔV ;*m>0ǵﯩypڪˑ+%31tRNSN!{IDATxi8`{' mv}C&s@&`#! [o3ƺZ#F<77CrM? p\ % #0! 3 -C )S\ %c?!sw\8 p$ E@R X* ` e X" `0f + `@yf # `PweL"7v,pgI/&s*pW¤ע\& `Dy_\(8<_-Gڟ_4WD*7щT}c7Th2! s_ )p^$}uH"0hX4[S]_h*tA+-`"߁!fz<p 0t,O@@@9/J8`jBmRsm2hsxz&tl{PXEc:7X䁓#ys\i 1H0epK~sG> ?,IrԻշ95c[wW '}වBVGtVV.V? VO_aL'#ᷖ^X@w2_X:>@nx"`yb WTLE۱2k$U$w2KW`Ճͱ*60Y_ 6YD phuR}jeJIe h+Z@sθP)EI-P) M ,Dl%J]Bjޱ]B-@V\@=c {3=@y׻@@p`T{6_.(K؟CY>eGc^OT]`3P9: {/ANU).uw,G1fP\* 4‰鯃E0\0GuGvt ac۞.ٸH2=}<>>򌏾D kY6]IENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzpz_icon128.png000066400000000000000000000034271272363601300251420ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<lPLTE -뙑KeY젙?0L>uXK范rgxM@b/ ypV Ŀzpǵj`g\2"$tRNSX, IDATxiw:P.i{[svj,!)H|y"چ܏h{;8'tL pp&Gpp" / p.?  p*OL p&_Kp"o  0`(F^` v_6 xUl `W[ o )!Z^ !@ Kx]f.3@Z$1P ?G ЭG@Q`Gt/7>l O m *Nf2@|  Q? W8 P|Plq)@hz_@Q7i- $IRZ4`*!UdqU1  9{@\>ǃ $I%&Ac)Qq$ /z$d[+ht "-|(RRバF}r$Ci,Z Ȓs`5lbB`_2H^| \)o$P ۂ0ik;ַJeMpx{t=*@9Hϯ.Q}w0"(< ???G 8!S뾶4i)A * R\IegZGh _)pLl\N nܥOBI8h|AKNHJk+}#m|4!P":=b|m1Rt˟;e'*_:jY16 ^R ?競}r}t̯ob,ǿG`ڝ17OFtW4@3ePH.q.3D _o3jn @0$%Dd]n@]e.@>9-+ Os3.УpI_Hr?n8ZGe,0=^r9D"dCF:j@J {k{t>"ҵnq/撻qm0YKkT>/$:@AV>Y(64 6,ht(,=F [ЬDMnH@%wJ:"@bn=j5{1V>rɗ_],=F%w(먒uY,CQZE.$?SP 1 kL^/ tIENDB`fritzing-app-0.9.3b/resources/system_icons/linux/fzpz_icon256.png000066400000000000000000000035351272363601300251440ustar00rootroot00000000000000PNG  IHDRkXTtEXtSoftwareAdobe ImageReadyqe<PLTE2"g\ꘐ 뙑)eYKȲC3젙M?$?0L>XKurg范Ŀσ[Pb/ j`xM@ՔV +ypڪﯩǵm>0;*ˑ 1tRNSN!#IDATxy{8`ؔTv\N{_ dψiͣ7 !i4Dm<"`1m[Emw[(ol| $'/| 7\ p,G,\ p(Wgl\ p$g'\p `w X):V F  `- @8 `%@X `,@x `$$@  )xLU7x2]26])pK*^. t }c4O]K@qWQj Pi?C3=2T .@wa BB]}ҋ:]{$ùY8+}*6 7 \Nx:lnd1ʤ \-D`n폳4@`~M( b\LL:"݄$B;̫D_dKdm˧h;0@ҹ2IfWH 0*n@\'G.X.@/]O^.~/#\$G}}O>OS8a] p1<8- `i.N m [ W v.F/gà.+FA'c7/=><<:,b6Y1rW,KeCH+P ~* JC7FtsT 6NJT.ZÔ+Ku<($udBqӮi22? !-f*ϓri$f$3| BRDyd tTy6d{uz+e&ϒV$qūd*ID?ewMӥYV1ik–JiVN@N&c q-0L+' pj˹Z ۢp6LKÂ:{Jt|Pϓ|~TNY (<%&H Q Y>P̠/=dUmYurg范xM@ϥb/ ՔĿV +ڪﯩˑ[P;*D*tRNS2\IDATxz0 `{ց1 I/8A|XU G[Fub{/޸|q $@l6p,@@x`@TD l& $,* 0'ׄYL vDv"}T"Ϸx 8 K%.`,*8/ϔx?Єl?1̀<4tЬ|ب Wc ~!`9 LpLpc:N0 DƤݛaO0OF.U@]wam),GUUf},`|8We bj6|4rg范Ŀ[PϏj`b/ ՔߥxM@+yp;*ﯩˑV m>0ڪǵL11tRNSN!IDATxis8 `iT4+ݽvbP6%6d&3^`_65]vM?I xR %  8! 0zi @* R@&P:db  @" B@6ppB ($ D@@Zf +`"yf # `2& *`Bi& "`RCamDe.[fƪ )@ks,C<n ?΋7ׯ=o7y^uП yz| U~V 3]~71gdZdZ˗ھ8 ;lӱɢ[!pf,,5%`0hR0,mѪX{NF'$mb`*,'tc>mf@7``5Q0#Pk|7D. }v45t -V$K`0]ܼX,ShES:7V7G2h8ĥEp8Uhi6b G%?}~1np_#d p9k|yn=U }3|zXD,t9X,8>=lOg*8Ȅ;zA\ >%,จvHXRWc pi`,Y]-^8&7&BHt*_<qZg2e91h x8WG?iJm5oA32zV]!,Qvh+P5bĻ=Ae*W2 H:H0`W l8yIENDB`fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fz.xml000066400000000000000000000004451272363601300254350ustar00rootroot00000000000000 Fritzing Sketch fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fzb.xml000066400000000000000000000004531272363601300255760ustar00rootroot00000000000000 Fritzing Parts Bin fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fzbz.xml000066400000000000000000000004631272363601300257710ustar00rootroot00000000000000 Fritzing Parts Bin Bundle fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fzm.xml000066400000000000000000000004461272363601300256130ustar00rootroot00000000000000 Fritzing Module fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fzp.xml000066400000000000000000000004571272363601300256200ustar00rootroot00000000000000 Fritzing Part Definition fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fzpz.xml000066400000000000000000000004561272363601300260110ustar00rootroot00000000000000 Fritzing Part Bundle fritzing-app-0.9.3b/resources/system_icons/linux/x-fritzing-fzz.xml000066400000000000000000000004551272363601300256300ustar00rootroot00000000000000 Fritzing Sketch Bundle fritzing-app-0.9.3b/resources/system_icons/macosx/000077500000000000000000000000001272363601300223255ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/system_icons/macosx/fritzing_icon.icns000066400000000000000000001266631272363601300260650ustar00rootroot00000000000000icnsTOC hics#Hics8is32ds8mkICN#icl8il322l8mkit32Nt8mk@ic08 ic09/ics#Hics8@@@@@@@@@@@@@@@@@@@@@@@@AA@@@@@@@@@@@@@9@@@@@@@@@@@@@@@@@@@@@@A99A@@@@@@@@@@@9@@@@@@@@@@@9@@@@@@@@@@@A9:A@@@@@@@@@@@@9:@@@@@@@@@@@@@@@@@@@@@@@@@:]@@@@@@@@@@@@@@@@@@@@@@@@@@@:@@@@@@@@@@@@@@@A@@@@@@@@is32dՈՆԈՉۈփ߅؃Åĝ2.**+2.[120D׬12)c_(.2,~ާ02-i‰12'`T&2+cW,21302/O]D0215ޭ2316N413.-+-.""N!"6ԥ!"WS"tܠ "] "TF"WI"!# "AR6 "!%ܦ"!"#!&@$!#s8mkICN#icl8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAA@@@@@@@@@@@@@@@@@@@@@@@@@@@9@@@@@@@@@@@@@@@@@@@@@@@@9@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A9999@@@@@@@@@@@@@@@@@@@@@@@A9A@@@@@@@@@@@@@@@@@@@@@@@@@@A9A@@@@@@@@@@@@@@@@@@@@@@@@@9@@@@@@@@@@@@@@@@@@@@@@9@@@@@@@@@@@@@@@@@@@@@@:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@A9A@@@@@@@@@@@@@@@@@@@@@@@@@@A9@@@@@@@@@@@@@@@@@@@@@@@@@@@A9@@@@@@@@@@@@@@@@@@@@@@@@@@@A9A@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@9]@@@@@@@@@@@@@@@@@@@@@@@@@9+މ:@@@@@@@@@@@@@@@@@@@@@@@@:+@@@@@@@@@@@@@@@@@@@@@@@@@@9@@@@@@@@@@@@@@@@@@@@@@@@@@@:@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeil322ՂՕՓד ٓҀՓՓ ՑՒۖՖԗԗԖזՔ qՓ AVՓוՖ՘Ռ֝םϝ2/*)-2,K\/2,g612/E312 *wssm@02)"+02 0+&u'--2 0JLL2120%u&/2)x*2)x*2&s(2.E60217-2 ,hCaG/2 *vQ.2 /N712,d,2-XJ,2.)*/20 ""=O"[&!"7#!" wmhhb1 " t " j" -Ža"g !" C" /=?>!!" j"m"m"h"7' "!'" ]8V9" lD" @'!"{X"K{<"" l8mkit32N֢րՉՋވՂ҅ހރւӅ߆hIEJmЅLFKEPӅjFKEsJKJN߄FKGфڄKJOoEKDySDJKJDX܅ކvOIP{܅ Ԁ֥222222222222222210.,+*+.12.*1AVgrz{pV8*02/,Qܒ<.2+GC/21*n,21,D/21,`,2*g,2+db-20<ݛG/2*-20?E/2*u?.2*ày\<*0214׈r,)*+-020F'2-[712+p*2*)2)*2)~*2)~*2)~*2)~*20,*) y!)*,121*?j҈ͬe:+1204܁01210,2,lV.2+)20͡*212ԡ*2.š*2*}*2.M=02,o],2,MғC,2.*6IZ`XY`XG3*/21/-#{$-/12)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2)~*2'z)2,31-2*\ҊU*2*yp*2*yn*2,\R-216Ӓ212*u+215׆ů012-_7 =T.2* Ņ*2+9 E*210ʄՄ,211̈́  ,210Ʉ؄,2+@N*2*"х*2-[IPхP.213цۇ/2*zm+212ɒ/2-RI.2+ka+2+ja+2+OI+2-1rl/.21+0Qù}N.+12.**,.,)*.22222222222222"""""""""""""""""! !"!3I[gpeI( "Dى-"85"!b"!}5"!S"z["XU" -ڛ8"" 06"kߓ0"{oP,"!$Ԉg "8z"N'!"e"u"x"s"t"t"t" ~n "!/^ψɦY*!" $w !"! "aH""! ɡ"!"С""r"?/ "dQ"?Ί4" &;MTSSKMSK8#"!p!"t"t"t"t"t"t"t"t"t"t"t"t"t"t"t"t"t"t"p"#!"OϊH"oe"oc"OD"!'В"!"yj"!%Ԇª !"S,3G" z"+8"! ƄŅ҄"!!Ʉ"! ńՄ"4B"΅v"O@ GͅC"!#͆؇"pb"!#Œ"D;"`U"_U"A;"!g`"! Cvs@!" !"""""""""""""" t8mk@ic08 PNG  IHDR\rf$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+IDATx ՕO~ޠYvA[%h܍;97&%%%lj`#N͸E#.((*4KC/4~ۜS j^U~Qr~_˽\c !@$Vh · 1!&a:@p @ @B| b; · 1!&a:@p @ @B| b; · 1!&a:@p @ @B| b; · 1!&a:@p @ DCl;Lϖ@&Mċg2͕ "RצK_ɝI')"g"AdD)-Hq k [)JYx6 ܪ1B~9 &! G±pX*j؇b)ZSGR">#EEdɭ;Xs(Tw7Z7RߗSwGߴwdbRe\fHlfdRɁc(6OygArS u-xڟC,yD GATP JrSr 9d_ W(nW9Sk`5xW\Ŵ榫)a=D!vCAȞǩp\^w 9eb/[ 5x eRI-V7M~w~r4OhsT=rY 1~@7H.=ƫ4Q:]p@[b[ wBq/ڛ9YϮ"$+)\qyWP^N~$WHS)75pWPo󲑊U8faa^)ȍ yGrZv|Ej[_P@ M=:HιɶVsdEi˼W(WxDRC'l?ٺﭥtW{)9eܴmWXLPrd|O1 $Z<]-M7YW˨wF=㓿H  j'+ɤQ R;o:l. pњv[S|jE(tb P3EX0lgo :йv, BG:`N@@Br  @7 Hx3y1J/+B_2i姰X-;𪁁GZZ *ăyVYe FE{qO'Cҗ[B0zOXSz`O\PX|>`q$hȠP(?qFN hYO=vcg'9S/(.ΦK֧O|d Ǭ@ZP6'$7o\kI-O | z._s)$5#?O\`.:B"eT9\1K'OppS{𕟯9x+`_o ) SqIT15PA0?գ+>%ٿz|@]oϣDɔ>@pM;-'$RZFeSb j8F^j5_PAyŎ*Rj}bPx lD)~!a]|ǸeRYQIsweHmLɎ"R@&YCۈ\$w_n~W(㡽B&ά @F[O>M){,Y ]T:w#ڸ}̐!ƕ0g 6! `>3m@q%  !hC+a' 9@@m\ C@>}fhJ @3CІ@WO`r6 ڸ}̐!ƕ0g 6! `>3m@q%  !hC+a' 9@@m\ C@>}fhJ @3CІ@WO`r6 ڸ}̐!ƕ0g 6! `>3m@q%  y#JQz3qLzPA/vmI&CxEkj暟PAEŮQ`d{"6b0Mn8,] W}0hskpxjٵ%P3{2QRg  })њ:g4s<: 5v7i 'cNQ~5y*1̤{S&țv!j@ptTtm_ *9*ˍEN?j$nT2}" j儊G|0/Om,HwK4 lO~~jqTʏ=/(؀r?켋STZy/J`Mn j^ |S?BR61dA`m F}<7nJ٥!Ei;hܗI&ܪ@x! O@PYPܽ=n%]ǜwRtۖ0ϳ=$.4?3WE_1}Ҵb) E%QpؑtcϽ'+|9K10ȃexɞߚG|c>M$3xݬ^&/Gs(ALN6@]>o_nfk~@=);T:,u#{w_K.|4BQ[ r =< AV6_C}xg_(_bvoVB P ^CA?όNrDpf+9na_ͫQ{'(݊m}bZoZɁIW˟1cx(j2@Hߊ@Dk.Esy$A%._xeT1eJzWz$+@ A @@BdV VdB@'D"" 8&l0@`; N `E`EA  !p2L++2! a"XXv@ A @@BdV VdB@'D"" 8&l`& DW~hO pKF@FlО^jb l'0 @`hOa XXО@{@&f= =. `M`{@@{] A  ڻ55 @w1 kk6hb l'0 @`hOa XXО@{@&f= =. `M`{@@{] Ac"/̑C IENDB`ic09/ۉPNG  IHDRx$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl++jIDATx}'ᇃeZ"UA`T\5j)JauRᤚU(U^/-JkpB49esjUҊ95$"L,}%1ػg3zvLwf=Ul, @@UgVUb  @E@**l  @` @ 6] @@ @  d  @@@MW2 @B¦+ PPaӕLP@J&@}T( Tt% @>@**l  @` @ 6] @@ @  d  @@@MW2 @B¦+ PPaӕLP@J&@}T( Tt% @>@**l  @` @ 6] @@ @  d  @@@MW2 @B¦+ PPaӕLP@J&@}T( Tt% @>@**l  @` @ 6] @@ @  d  @@@MW2 @B¦+ PPaӕLP@J&@}T(pv5+m֬''{xcS㏍ &=$V n|:yL+3gk_z?|9? _xV`gjEV($/͆;v\qjtG'R:  hL+^o}Ca W-~:;p_j^w@d rw̍@FJ753kז> tGj @TC,No)~s)#sw4kO ߲Vh5`6I]@hEWΧ]uo} _(J ]׿U؈I$XD$]0 ld~]nl$YD"tt$^`WgMc'™0 5v]E vUK{2,@ ]v}W`0҃zؔMhZi|@|tOie˸Ο-B`H`H}&0@zyj'0n; ,={ǔc-]kH+@ͨp*@MWx|53w[@[Q }nע 3K(*,@&p6X[@-l|S 8O 7^3 MTΪ"ڨ!m1)Mo/ @ S(n s+V'hK( u5Hv?lL@9@9TI _@#@`duP:zJ]0fv]#}+ ~tP+{h`~EsRCZ[?x-0|O{O?Yo*T@k<{s Mo,\/ҩ Z3AG700-W.w`aT?0(*N캮=H7c, & Fo5 l嶚WD@`P`P~Q }ʟjӍG?tb nu lq_;#sw`x`AE_Q×)50`XE__o[\`\Y7aZa" q.חu꿝wzzzS,u 8U ;d~}r55h~{ي\ u!?6տ@D bW̩8]@?}# 酙,͝{_j^<'}m!0` ]2Q u 3tOӫA=DV@@ALq3u>~s7{-. ^dz5?Jɟŵ% % (PrHhnxs{;oHK\gp/jF@ B`;lmWYɪlpƶP@igxVzI\R7Nˮ7Nr~@L f_̪Rk^S*OL>B@Y@YTM u[ڧrd7o+ m]}u{`0A@%\x{0M12Μ9oԅ,}ͨ0yZV 0g!@\ުl@>7}̭rr{g,~;>o@J&@ ְ,=s'2`eN~6C^[J O?TSPC+q7vL0PL0ρ TmbX6츦I`eW4Lv`؞vKn{훏o~22(V!BAZΚ &!ƺm3Ç>F:a wX}\?.6{615 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@15 K@%k\ X@S#@\%@1q lq8'YsY3獺'@gj1?AOΧT /7?|9]پOH6δW칵<{sIu45l^{F4 Q(0Q 캶hO?e0Hsdҙ eeQ= W}u][nsdfape::4P-ZجۺO}7?{' xzf \w_6M33J.ur*՗΂\o 0N`}3L^n}C[Άp+3$˭A.J}HgFrtC?;p \KL(;B3+"`W!`f~Rmn7 - -n{ҝM)[$ЗЗB ]˞U0r 4_ рt%=-1m!@ /fճ@:e!?ݢ3h5(.?oƤ*-ČzrmnZh^%g34l@6ZA `ϭchHl!@ fӳ@:o/0:3U % ޯ_G/m̖8pZ"?P@:-~;n&oa!@ fѳ@z>Oxm,V,@d_ {RH`9`9: 0X$]W÷Y{``?J2m :}.ןc;##c,8U^@zg~yl{?4ߟ?^]+_UK7falJJZN)3գ/5|ʟa`ܜ_}承_Fofoj]ߋ?r /G(VWmF?M:uI|ɟ/4?͒ gsw6G?4M~5 pp~m\w&/sSSgYO]/~M QtB00ڳ@_{_PieOdN3L] m&@U@h6\3d9_盳mG>Ԥ#.vBi[tYĻ?h^)Կy&`Îᬗm0it%ggmB@B6{Xm,];9o%]Hg"-D1\ #a5a&ߺ;ZT1_gh=RA 5tXwt#;ܳa0g &-mswO+c~O"x_4vF* q2H7S_{z6O+"0~`=TH`?o[)ǣpX !ˮ3ߍ#,Ll!@__o[Y =(’I|y",~*`b>7b(KL.~v 5 &%PPU;~oMr컪  @r@ PPgUݳH[\~ ŵTA6w~i5^|Iyƶ@~C*\]!eR€Ǟ[?y;WO^_Q.M{@.~WW aj_xl<_bN?^x9Y35+΄V1'~~1zIʟoC= /yϞ'|@N]c:fͫ 0 C8@zWɷ d2>@:D0Xټ/ lTV, "=[H 7[&@`Q@#T'ptr{۸- 6/jx?0/)Y^@f0@:pT ۆF@8 o+`oo㘅~mS^tiqzꟇcm+V* T) inZmjՊ+swUSbigZu|4`/^*. `52ii~'v#sw= t" th&bt#N{_7F!C@ȡjQ SL/.zG#) 5ҁMӵ-]J3PQ G kkY:YR* mccH!3W׭t(Vg ) oۡY!`e-J;)VD"t fS'j&%N@&枀廒长)/c-@[t){nn; 'Q j)"T[HEYNrm ҩgnI3!>ܧ_E EUQ+t #g@ҙ 8U* 0Y35칵<{0a)=`m\ ;ir[.eI]/ pB@8a+S l}CerF`mS5[%!mYX - M  K]u?xG#kG%;4 cyZ ]H! Xג2aɟ_@+X )y`zwЯ|T>v 93@W7.~HOg R'0 `m2R8s]Ѥg ,-f-@  @d.j#@-@  @d-0V @6 " XMJJ @@c5(Y@(j#@-@  @d-0V @6 " XMJJ @@c5(Y@(j#@-@  @d-0V @6 " XMJJ @@c5(Y@(j#@-@  @d-0V @6 " XMJJ @@c5(Y@(j#@-@  @d-0V @6 " XMJJ @@c5(Y@(j#@-@  @d-0V @6 " XMJJ @@c5(Y@(j#@-@  @d-0V @6 " XMJJ @@c5(Y^r}j#@8GeV @% PrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ  @@@UhZ`&@% %wWm @E@h, ] @j PPrwFZ?WWH #IENDB`fritzing-app-0.9.3b/resources/system_icons/macosx/mac_fz_icon.icns000066400000000000000000011132331272363601300254560ustar00rootroot00000000000000icnsic08AVPNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o+zG vq\?A[mF7xಝ`;e|vtL&ӄ1c6-m vɴHmX,3X`ނO_ۿN+8ի^BٲT`kz @;L6kdxuO~K.{.UVkNc姥,J FvbfE;gfK Au~:ÛAf Ĉ( 1-JO>gdžt?KVHQU^u,Nk6@ "x0mӏ=8ZI3 p Fԓb k^?^o;OQ 1#d4H&o}##kyI fv0=mG;Gn_1?0/۝cxuÂ|t#?pDbz렴3Jij sG.lJ] ٲd^, ;_) iN :n_K.z0aH Qv2|c,ӸQ\l\[͂ gGڀà ದCP >O+.UGh,>u&ʨ~m)zP偖pT89C>u:Pkx0HGS%Dpݹn}E7 w/e 8c g+bFd#'OX.ʩ D^3!j>SV_&Lyl랺' b5Nlg'=PuJFBj]fZr#0/\NA_LTRyYh|u*Lo8m}[ ,{3,2^l\wُ/=ǍAA.d 8l5 !ab81rSSZG$z{jL n&+6G,qxb ca\=}.di9;2~\V?P! in sD$wɷ?$\gƪD' aoܮ47SsfNƣlQS À6-q]qa)k8 A鬢e+F/Ruԅq4ήxMT$+vz~\!B~;WˣC=N7[^H>^ c _,]#+ƈBH)o,Afk$HrF ý|(L\/th3Jl;oK=Q}Q:>뇈 6 mEr֬Yxv@Vk@--j>:# ?Zh>[dvr"u<upzZbj喵O# @Ţ^ $D4X5pB`;V>Wewbα  %<1jr<]<͂`Y6Vh>y3S6/첷H85%2_4f8<cy.]B) ơ̗g`ipNbUed|yA:0kXqK>HU T`iJ7~a(ׄ /ey _+"FQLj6^@ k]b}O <`@UZ@@sȹE@O\%E82ͭuz@nP0qu{ɋC(~ nep> ,Yvv%?&_*E>LW݂EY5WLH0j^ʂ6Gke&zYgИ_ UiVG:(zij&0Y>R}:@\/@$&{­/8m e!(+P$k }Wuܞq<7N:ho*(c C&}491?^y҆T-/5vW@=k .'2ȹp\2tK-CۦJҏze*;#~r[eI=2\f^@P<ў^$[ ,ؿgN \[6aX +<َN] =81`B,7!dA=yhyNḎSpScוq= 6sO mmOLa fx?N|ͫ.\4tgp|fUPjFbز גgy$A'w زzoHqbnSO(hwcDFQyM0ܶ sگc `0d9Ci Ë~sy퇿ҋ5%$CH٠31*}^^T 3 `=?&l;-7,o˰-' ~`mᦵ%~H=^#]pE*osNb_RZP`^u/<`J6+a׶ 3Qլ4x>w6a? =9լ_}N vkt>8'åkx?q &B1 ~^qMA!#OI|{V >ÉCNGIΞ !64Q6ʶ/Hhňa,F!zghΦi=z(9Ώ2xޫt*Ga)ɄFazGB*67sk[}&5 UtSTn[B]k\|C燺jU8es 2^ҏi]i~#>Z*@D0eLFt$U!a&@A`t`-W?8jl zH BT -)Es$IL/ `^[9k5 !0@3Fu:%tՙT h5HD#xzgտ]3Uc#C捲Ռ5VHD!%h3:c*ӣ.TNPGSYi-3gfK ,#޳SzzzT އ@2 ǹ> l-APfd ̥@0NU,}$yG-[UcC$F -ԗa/[r{O?KM{+T'c,vR k`Uf3c*ѩ[7]?EW)%Uv ~:[>հ롪+1u~et@Hүæc!~A,[Am5͊!3@Fك\0 yȌWF|ӏNNh  2*U={ @H}yo`<s yV@^ \םJ*+ز38C`D[[FZNP ? K͟=Pqn݂3c&0!S& a88" 1fO M /%4`gZuFb4z:Dswxctھ@0fx'%DA·nj99: UfF;̋=:);rEg,DB,Q덒 oQW#QV1Zuo=:pj;8q@Hx@ohA6_(@ K|Or w@x /*1cv8?$>y ?8qTy ȯ5u_= $ ϧxpz싟q_"ZbX-Z}4tYo-?x!@9cR (сtӧN95.H.Q7}{уWyvq3g%%~ؼӧ#ر%Պ;Y{J48JM⚖\ÎgV]9<q>nL[α ͞4l':(P)C!ィ6JIu|A!Hcv;n'ʦqjb@#~v|krnƳz)9!;+xBBCD@fp=k Dn@l&;xxrL]`{Մ O=址v{Ox.to" ʧ;Nsewi NRIBm9c` e2:gQD2{irtmdԒjGa<%[!eWwR~>⨫ܦ0hAR*` aze~szl^lB BA"bY=38]yl?a֖ytXz`y𰊧jwox=;Ⱥ5j. 5Gp[#kεe8ݤ;tא%I5EPCk<^ '$ ]!/NxFnXM޸L(]AjA,u $UkӻTzFK;t#Ύ}:xA΀NA.#fUd:E6x )8+?4aȐ,ny@kf #|?7??O*޹&@ OX&y۵_+#U+/ylX{6ظy8xs: "<+xsr (*2ܢ~*X7瑢,8jFv޺zݺ~"!9`ݝSLh~1cET9`?} `G+`B^"`Pث`̧U <=|x+N;锩S6ά^n$e7sBhZ~BMs%`4bDfXsc{S!l|ր[֮M|hpZ@0~ )TwTLr<3Z7 Hw(~9GU-kc{w~vj<m3.OΫ5_!8e~"e=u+O5 ,HGf%@?vҠg9ǎlׇ!/ YI^ ќo N@SvQ\NUE(ʯ nc i>M.' u0e)o bZӢD@evkkJFhi\ة ^Fg Y& Byrɓ _Y(|>pNjVk=£b 8멼W`s9(Hqۅϟ7ܾ3jނ"V4ӻ0FjP CZP%?%H(GiFG~b&< 3bbPPPsVc<9Tj׳ݔ1d^A`{v}1l6u` j RY$ަm%Gk;U-v]޸w=˗\a8y yt̋>o!eqRY$ƎuRlׁY TSYz-aUƒP8[@`ٷJL<"Úk(N^PsGk ӧUǨ<xf%ežCIs_Sze?O|[UzV5/ԑ 9yv|ck#-*eJ{TuL{^;<|0n 6"D##/6Wo=UӁFY8#EKyBf;՞~V}8)Ń6r9:YK&MZ֕R Tc+"/H}#KO;J-= 8K8}`zKbwǼ|L W]һw (Ux6~Qtb>hB\ Lp/sPCsԚ ѻs\u_x !j{DzC|lɪU7dzK׀1FыyI,X`[p!,J35PSsV.,U_JOKj$'G<խzwVqf/ q )Ѫe.k504=] :j` 0Ҧ$4'}Yy_tK}y<x#;Rvjs i>k3)mfƇ%UMUvj@݋MOJ>(ӆO_-D6]5A]ȷ2 1NiFN @:t-?N\;jtvV] Ɲy0`\@|vX-q M%4܅K̘]YGGR=HeإYcڧ-A]&50*H!fEΌZLK—;1e|kPWAfL>V6G.WP[mFC@2J;7?3@7*mٶIEמ{GmZW]+r5UY 8FT^M%Ԝ ;#M+~; pkV<ҭZ!_M Mފͷ%di;{r,Ni`0@L :~% ͵=Ձ>^\*I ]:2!QƎ)f843LCcŗ\n~P'}Ȭr  y򚩪o$_vC*3\yȠw-oyCAu 8-HxkGTirl =pO>C[ztb@w*1n&#| _AĹz֜vX&T=;Ԟ|/RF`7]Lg وہ1i÷Fi5mNg! xe:r%i!NðZP]+N' x,[ߠFvYjlR=C'H5k)ܮL5$s#292M+ZDך[ rtL:D 4imiK,$8!XHkK[b, uFZ[ 8f u#h$4ҖX1 HpCD@#- ֖Y@c"i %pQG,H Hh-cX:bFZ@@#-m:ub΢Fbm$4m%&ћ+^Q*.Cte72-OxO}Ej/WVXKdѷoStʎ).- *=Q r/touYꄇתIgm7 Hh%u$_k}Ƶ{2(S H#Gy K_Œ5ځWA;oJI}Y@@ uXv$ܲJŧ>Ff;Tg`# _;3.~ֵoQ_Uj$B7>1uu?9s,6i:_EVXM%j>R_%ũ@9Bݕ cXS.7MMUg(xGȰNT*Z cN&ݱۚ~3tW^ZGUN~3-Ϩeg. jƂE*C qA3ϑ }s @Ft5yQ:!2:~N?m7}I 㴆0J2GX/wݯLԡGAG6 2AҴ҆Nb*FUTi*ugbgg~r~U Ou*upf~SS0׈G5c:ȓʌ% 4EuW6tq&ꉩOjh` Z~bA$8SK!q?P4fNꉫ]q~; MHp@Ԃ<_CO~XMYA<:=|"b)?Q HpcXٴrm;~LJ 1T@LxbPu- rZNG mXq ʌEr, >kaJ9SDQ"~lNێfv.GO b]G7 V2t@Z@}7vzW:K._~)3f3fE YZ/ ;nAQ%ŤH],0iZ/?3/BLe>6<2Q|l=uw\ H+okmQ3ZuT%;KZ_y(pZ͙μo;C/hrPES}- ^:9‹#ˊy?OՉ^0?͌ۇӛ ܠ-HM4 J(@Gڡ?>Fmt5OJ_, Ӆ $B,u.=UNZ@RRX{x/W<:jcbȲ>@Lܤ]R?ć@᷈羶^yIϯP].Q}׼^Ru.ZzŢebNOitk[}*N iŌH_G-Bh,z`22bY@Fu=JSյtR*;YG5uZ|tu>PTܰ0_@^k:L ?RtێX jA&$L~ Z@@Y)01YR,j fX`bX@gK@$Eba e/B|d|T㟣{kMjSy{Tj㲹 I~_Uzn$4%7O;MzJ-=NmyU%ZVj߭^M/C@xJ1N*p]W[hC<>L>E>BRR(=eX(٘D(j2m5ut}߾=ϪP׎3]hlfxZ@@c]qkxV]thOWzfxH~Dx`M?Qr":I Hp/kB i?Qg/ |8}8)c EYTsPVLD wQ&*T?)@pr]b eʅ!||N=ܵwu4u%wk_v', Ii:g/%=tU`'7- 37:;?k~|8)i/jzp}jM0ue׽w n;~`]2U}j[U_Mބג@AWw;l wN5nckv?8]Ho?/;g ܵ7Uk3ZxISsSWcpq#!V֟k{H~Gh],ǖ7yעja b#dvmnA=wwD#8۔(ZZ?lTe'ְ,F?%M tKƎڷokg4ş~{f98o w; .^_QP4lYlqB߁[=6,к@]C5_poI, O7^ ~[/_{ JMNnSfL >')M/8{ jL- p ˏypopC*3(I[2h~w~[B_ h Hhys^ښ]|b7ԛ³z:p2pKМw7Vn|JͨV`Ԛ(o} uy(U/zYJÇ}FZk㽵4}[-EY@jdZ':cWl|*k*gb՟A;Y@0꠭5[,/#CN PY'L$Պ=XRҴ)*55J2pk{LK 8c VJR@pr\(! h' `LclcL͟>wD[ vrAwRI@,-}68m֘8t SSuh+:*bq@ g#T#l­ph6&bsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙYRU6ݳ0 첣E40Q\B%FE?*$(( *6M@6f٘fߚs]]Z۫ߝϛ{sﭮs֭[zeLff!M 򑺵 AdSM7^33<$I:]#sEZc`TlCG34+iH{yreC jb$ k\}~t'vT%Q)K68M%C w*=^̡c(w-uf1Z[KOflmu,4>pY`R8S.EM,k.Qr05,4G@Vc\-sWrt) -k$U[T=2LW Sc,,_e! Nh)+@< 8X3ye*g=f%lR1rtF6)esk Zufr*h:P䅀 Yc @mNUڥ.!ui{̸ki_ (D3\_ ;oi[G,407~7B@5TBM.0;9M7TT-(r!l|6졳3;Q[b*O#S+qmaiw9゛iUo6$*UfN6K8M? >4J@N?,#@/mq~! PRXClxe 1Sҭ5Z*v5췄řqv6k/_ݱet@h*&ip"qh+:_sRlOӶVI\Z[KH>g%Ne$\YVV QUxzB% :>=W8GΡƺ4+}4z8?'}Cjx;vWi16f ?{$84C@dRd+Rsr>GiyxCVOit.7Y*sZ.a57/Ӟdpj M3|ONlP)='|[h+ 7*pa@˨Um߼>T%f*21HPm#(8Ka+S{Q;ҳPt^j4BnׂmظIuh;Σ#t_dTjXM#Wb= ?߰hb 7Rqxƞ_E>3/,rdh{Lf q= 1o|J331lCbMOu?fXx90kXY*Y 2 HܐM.û77ZУS*S+tNѼ>ȋ.YF]Q"}x]*2*OGc?HzCCTIf,W|-ĉ+՗cb(#M35\Ip4ySV۪~9),L*5%:nT&h1W@=RR+TJ*W-'d0'Tߗ m^K~ ~4-d%m4Rnh=C3^NGoEՆFT3 ׵{RH4mNլ2S1fJg~`07ayoT%qdœ Év"/y4(|%3r5EtQ\yL1`?>N/~#4ae$kWM èTh7]^_oRce֥f "(2]|zJZ}df@XEY &ӉpGĺT69̲@tZy4W 5$W~U,5D#\G):*ٌ$ezV9EVF)!L@EQ1_%.@Op#0p+UGVd^m܄,1YV|*}m *ά)~sK:t22s7Z[M3u3 ggٯڝj,hgS H`ul_201d]J:GPϞy (br.GsV~cn6ɩLEi836iM SaI# ߼ݶ"+ii*+'hX%N%N_㜾H}مmj65yl5=q> 9:\@_s)Ե|jr4Z;-:ܩhS#RsSnq^}@Io#Q_i"6$^J:3)2^~9*M"Qvff^E$2 @"v#нh1wO%{o0q!lvJukiZ%鱙Y^Pt--f7ոD09 \ >wEITysMxt&@z0nHP R;c5UKVYD&3]\EFˡWO c;XuZE^&qXۄkxU&Mͥu/zY6" )#~&vjjj2US8nZtiR@RIxt3OsOD Sws6)J+p+U864(eH4>`mUQm#_yj3rS=QWmcҿtd56eC)F D9Y΂ /<_\5C%P]K"M<ƔQBi&VOS,IwjMŹݔ33e롣]oYZ&O4ԑGgX/<\'frR">`/md&a3MelgTi%6xO JQrNҪT7H ㉧mc2KQ`XKe ifTd.UW4L¢J^ ){3TRJn:n&̐Lh <=@9_ ۇ`QzW6+Zyw!iihX}IB;a#TnyҝF3^g16)[^ᛌtցc [8 ğ0Aw% ~4-Bm}Fn*vf:rӟ ̰ʉ6Jum8*qqu32l *2'W78iHe/R+(?|ەi<.g81A?^kLkhMk(JTfEFuu|1tg˔/:]Egdd2/,1{_ǩ4<[6jn*GT9v9i\|g?Xp\-ՔyZ!1hD@]gQҜ7O}+`=wYqjZ_~lF[o?m *Mp&:89ְk^69&ჀpWI" \'bEz%(wQF<+нp}i4pRGiR`5 e"AC>(qf9oGY hM?"bnCr?|M.EKy65-W½CϮdO.ON^Y#dN~43IDATL}JPIw@9HQJb<_E_z;zVSe^3O޾ˌGӒrE l@.QA-]T_ib/oSS~zۺݮPl /ফ42*cI8NkU/n88} R 4 #I7kMY5‹Os:AJSz|yi")u1h!D7}^kʻvRi|m 7éo SXAQLqV s۱/m'cMS>i~r /ISE`ڳ喍%(Sq7J\|i܋ClR?ߋ;)i\rJ:0Io=nyowdyM0/Y)O4yI37@NE<%N5^|ө[vꗦ/N}?}7ۈ;sA,@ sSn?EkRN-eATͨ}`%JLeN|2t'SYgZm>KU-agŃ xZ  jܔS֩%)7W*L 01h@HTq9Wf*jfM_ZXWkƫX+3S߯]_\a$U*Wk*;U"'yx ;˒cS_řm2y~iݜ7ⴞF@&g@MaP6eL%r7e[hV(YͶ+iĻi$_54y T$ ˩LaMY3+08ͶMy3,}ccqf|n+p>qp*+2JTdfgƹVHm{M38x%,S00!h UPR*'͟SqFUwXWM3i )  4j Mkt    UM/cUF%yUF@YX,9 ]*~)Zæ9& { ` OMyRw*|_۱[TL83ޔG80qGAB"`*aU3 k3%N坾8}#L0i  >pSܚF3Jߔ5n90&२40@c3HXM_93W wyT;:4 @ b3tJ5];j*w4S)ky=6j̼ȫL|fk#>N@ "tSkKؙf*H3̧M_Y7} Kz]#yLY 헰y\o"`B  YUz*j*Q-T-k}kkgn_4^7Fo4   Uv5BǦTN$-Niyєk9N9ͯeUFpt @k@"C@6y,8=_*[i6<"cW9lXGS;RjfZ%  !&vSa3]IfX5^WO̲U-o xA@"MMiJU_cneh-C|u*ƛ13 Nܹ @:*SMaMQjZp h?S  T3];U-e|xAs_wJt TzvߓRʥQ>6h pALP˹<;TGӼGlw+j8~o+[hWS*͏Q{LDN+@&CT,R+}=||mV^mu߯hӗ.ukm#"3Lh4J BWFy17k7P!GXw֩,Q;4HD=Zų8HW2Zb<h:$f;x(D.X@}! ㏠wa# %?1\RßYG6ӥ%6ZagҸg{ٗV^q r:+N "ՕWh+QH'yn)}tO? l2k! ,_T˸iHI¤;(~q|?x뭅Y1To/-+Dllō1 ! 5:Ki&*0W)YJG?{2p,# Q.@[`-Qs_jZkgtgY yÅZ܁ @ 20@C:+~qO>~_'/}(38HK>%~^Q΀@AD+종WRfk\Z[S ͆˔=+vo8@ ࠠI1' w -(r}n̻Uon~ouGP $ &ş3ϡd|<z-~z` A$mԕ}]`+Dxf'͂h9PϪU3 :1*SK>{om1k#( K?W886 5*hyxtw}}(dWus G08jhs$ +psq.!~6}y" !0"4hJL +}׵le(Z,t1H@Quhg%z_Ѵ>Čv;=A& `ਮ #Wd&T*Rۋ<.㇈z_ {ڙ!U`4F TVR?SyQEА&lCloElAt qF'XA*i:$Ӻ?8`?.g|MEJgoZQzx:d) F連i֌|0m >1z8KW?`" 4AG}C@'Oz w_f'o|֛iS۩o_B]iZGF6n/n=F7~V8DzEҲ3]2hIF[m0蜿$v™&4\=> ~/2O,?]Bt?;hHS&ėG<ӴpYشtQя&\  wG@xQs v/ݑ msBe&j{,u [ya`*3]yǗ.{46#E)_LOLL@Q]_;ݬ>8?[b_cG'l~f=?Z~@6}>E"!@{TR15T*w/u?HwZ[ RCmB{}1xef-4D@C .K2 oĕ!Qwiu\pA( 9j C,2vs>6?n'AG@ -N/bC?0dzBeJ##"`L# Y\C_PNLwEh#ΠO0< <ձ?8򷌫 F +]<Fj"O7 _+Kz)VMߗ`߱)c&zy&距? (as[i~UǼd})G*ē}/^ܴ%+3 k!._Ow>~I/J<6_ǏIX{Cfh2A@QG`8:FC~R7 0@ `DaІ~4V7v-4rݕ# [8(Q@:(:c81je )KrF4@0|BHMu/l|R-JS%6}ibFHy>@`4K@OqB}x;y杼Ƭ[o~oO!z "B@D (@O\Hď z3@(xr myA(:ѕN"F}BD?x.Fv$7Ci0mSi`LÁh/iΧ"_ ,&<7QnDi m'HQ L'0+Sdib~EpHx_:GGhe=h}BH@!=wS/!AJ;iG܋E!j!7AidAȢ@1^_|D(zݏ z3MlXo}m>hR` )#~K@[{| +?Wq[%_V?={걔۴?v $C 4F߶ֽ/i'?R!WyE0&{͎d@ϿT9ZaA Xx,oԖtD6}NZ|v/ˊ5@8J wth/! x6`w'N6o.zy,P/fFh鏯;F?ZT GKA@o7AC TgXu9}>{";2 S<*cM??ysJb"~b@@  M|)V7yz_-"=p[hz4w' EJ𖾏A~G]1i @I@ާ]?Dϟ8@Pv ZHfx_+>(6kYUI)?Ю'.#R< /J"S;yv| 칊z;:z9I55sPfz\ۆho!*l}7 d클X|_fy䔃\A'bj'ٮMk' SrG.>uO];K}VQ}8d}dH!q-g6[ew<[X\( _1[gw5Ͼؒ%/>ɭ{ֱ9&qrHp& YӮ~SI*PR#A:$ 8   BH  tt@@`AGA@@H  tt@@`AGA@@H  tt@@`AGA@@H  tt@@`J`eh.T*E_K;sqdv__\,/=0HO~#eG!G[d[&RA`Vtђehף >ݭF,%4rLT~16aicQ xNeD= '.Md{ZIoh~dhC\B@i1d@< ioi(XgOe@:f:g,XwmfѳjȭyhsQ$f704N4>x0rDlabP'K@ 1! S/CT,vV.8RR;EY `tsI# ?ŋ_A"uǍx_9/|=gmB@ `D{|:h@n _coӢ=~ѓ9ƞzxJQU5ۼ3̅|MYT':E՞6Eތh'?C"@}>Nηg}g 54hfQ6`Oo9,7ēk< P? Pg/XEcsh ) rINN"9V_L\W'̰ITfXz/*Iӟ䕰ąaC.'*h=9XH98/^Ncw|]Jpkx%އ} 1r]$@@"?vB5>d2k6_-D7fwMC)5 %X@A@(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`]^HYIENDB`ic10PNG  IHDR+ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx mYy>J=I yVB[2C[[|ъP[!B2f`=mZP $E%URJU[{=s}=oޱ7k{78#HD HD HD HD HD HD HD H?W% D HD`dc 2tsD 8D HD0!a:[uO|D H#p5rD#HD Hl#=ɪU4XD Hv^6{{ͰD HD  ) TOeeԺ1>D H\orM:h$ID H-pS/{Nq4Hˁ^6ɛν|q9SΙ$@"$`[a]3cz齦U|?D H M6;ɵnr&6֑r"$@"\Gdʘ-ɫB~@"~7η~$@"${@6R{K;>YE},z: 4Hˉ~6̵̫g~N]"$@"ll6O{,ӹyAaK$aA`y2;LMJ>HD H6@6ZGtDO`yno:wD fu3gԹS7;3Wm>7eW%$@"${@6U{J{Dj.Nj!T9_>л.D H جŎNUyqq8,Yq)|n?i,۝_%'$@"$@ ]w BcLF4gq/rG.}._u9.QȤ6uS'N/ۗo>TMwKN_X -[.N<[pԹgN<}ĥ'x.\<ϜGxG?v(|3@)/C:*MVY:?etcoO5>bS$@"$+ pzt9pdS}mH5~XWXPP;}7Ty/k]Tm'ϟD9ܧw h_]7>q/9k]s[g* N;C:๭K_SO?pǾ>}w>%Rl>SmTe@QZ85(U1i#\1'@"$/5#{_GrG\^"oxyWhp]7oߤFTgMU!^+k:'g]y╧z|"$@"&R<yA,ypS;D}u|We79w;V^\cyc~x SlQmzbQ>cy_o*Nm4|Mֳt5W uGi^A&k[|T +zok+?uדO/˧pq{@lPBQ-%:磭:K5ӌ]gL"$@"bI#.ËF>u߾;;D`SxºsJ?Qn|1:r鞊Gcys|Qyuzn+<~ʫ֥7Bfs)|S>S~&G6U|M$@"{hڎ= pY<2~kO<;?mO ¦@a>ʮ<7ιegr@"$1Dkc\r~pY<2 OvIU"mpG|1[CO{V9΁EMt|fcEX*;IozPʫ|獚5*t^ȯ/=Gx-SWE_fW cv5ܯiev&@"$ ~EbGC} Ql[#_D @C@{p^i|uMܳœ%2F}}0 ƁO@.nuU=ޓW?k'xࡢ>W}]`O _Άϔ3e)'@"$G"sq+! N5e6J~Qc9߳sー %ƩQm5M2ѝ-@"ENdv26^6(6XKC—^OG4|⯹uq#ts)6ȳ JE^qn]c{y ZsXBPh~5yn>8(U>ߺzΗ?σwգ嘺+)}/5u6J.HD 8BwrD@{! / oOH5D?X.f+6 Elަܝ13=0^jp9y搯_q^}ߧXۥ'/>'Or讀g]L-hc]&$@"t#df7lz›$3ᇍ.Yb٢/2k~FF|/[v߲n]u捵La #^՛gL&@#~6Qu>E9_wýC?/p~p7 ^FZIO}ʆOD HD#5ӡ_H.`WS~L5?X ] >J*ZgёHdxcM+/|#uYǗ˨og?ʁ,^>!sl)#LQ&@_ya]鶮/>?Og3ށ|Jxh1/%ki}OD H5~q D^2:QxSMo"HNG?|]7Ɗ4`:jˢWac QѶ-mMs#L]tշ]5XIqG^/|_KcwΦdg}OD H%\!KFG/ߩ6 6Ve&J]#Άx3FP;0%?/n@8=*щ'D HD!F-G"0BE1p>Orw(&|qk:"A ԰rZ9ix5xM?QˁT:=Qjs} 9d0=G9Š/. If`N^yї~'{_Z^(u)BXsggwE~{E5+Iu,_Ի[X̻6Y?^Eu藇>֤zLO_bFJ`s_p{ȿ3..#B9xFS:_mkӤ~\"$@"pH;ɉ2umŀw*>|/=o )k6y&M(!tn?N鼚Jj90dLud~׶jc՘MykdaIw,H0zMrXsrHtҥSW+_woƯ{5{{|Z64fS6qA {̎ 9i"$@"ph_jm@v*Û|oh}@W6Vi&J dF~ja^79ԠQl޽yQ3J:54UzѬmcC㘲.8Ds#f~'Lk@C\n{_U\X=@JE|"ߓ G5HD 8dp-sr7@| ;Ûd6N_xNwH}F.dMRֳTuR,zCm 0^qFwq@<+ fS2)r]?1^^tйo`Y>"HD 8Qˑ]r]nfl"$MrRT ^j&i(}Ce/nY쬅_5*YE̵:4G JNc N/u^آę?7/帶z- XHUM3S Qr"$@"p/x!>N&g7\}"@mRkeo~jnd-똊_y5YvŰ&K[1ePPoF|EIŭtʭ&V(r\uG~_vۛox ?bzFr(tؒ&@"$v`O;Ȣ*^:}|">W+vb#݊~\ޞhXWUʃ EsG㋼*m7?QEպEiq-~Qpcn<~%݂5Wq^e3~?y S1['O=s 駊ˡg_ ;Ga+?Ee`&_sD H@p@Ne,CR>E9dl)P[n;C@?LtM{^{kڱ ~|=rk:h1!Cыr*OUoG1N1a+/}oy_ #ߌRɱ D0>\7k^gw>cU-@W+^9fۑ#'MD H,#Xޅ:(>i"pPQ1Xgts+ؤ7jdiC}N_n9M:1bӧSC9Qޟ W?׉Mc<,Nv N Kwۯ:3ovK9Ug(]K".$3Aܞ|"$@"pYP# e%[jcbdH x(PLk%w:V[MQ96&Ώ!@ca}}&J~($. 8pGj0?׉gM( /KqJn./Ͼn?Wn,k􎢮NCqqt'D HD! C5N.hvs@buPsB$^6,/4y=?c5I!4f@ϗtGApZ67/G#Mb{x5PQ.@*xmb=zOy2ջ<|;{y帲S_ (<-\M}kOD Hˆ@l{Ywp%%_k^˅6umn+)Mu~Sy6&nb;{]Ξnz=^<yJP(q,qJ刺|\1GM٘Kj?5c-SPEyO>y/T;zOqm`1d(| h&_菕ʱ_ 5⢼L=i"$@"iˑluh^5h5yV-zrm75&kɽXqWA)Q'rQeǯÏ{G/_:<Kq1G}VXO,g_E{/QC){z9j" b4x=T ڋn ջ5}/v[֫6CG󚿒t Y5135 ?ġ;][=o♏o|ۊrJ'7(!cHD Ҿ|'dnt*>FP9c~Мw@h\^4P[g3a'85dgJ)E~3ۓݗ᡽t}>z:l=ګS~1dt̬<קgVPȂN2ƞ y; *k?/xsGeq{ˡG i1U[J8zr"$@"gpD H}D{wRmYF-6k7M^6jҼ! r:9RT[7L&YӸZ;3߁=GשayHuW焯5g_|uM΁]:.9exzs,;wZ7k&z9WRŏ~S9W'"kvF#ܧK: PybʀFͬՃ^5 qH pWvם]П $lU:ҋ׈:K"$@"^#D A@(_MtQoT#hr[D1O^8OS9KMn[|3b͡>%k|]4c2\!T_}HaI/nF8m<}[~|χ(jml,HP*uVn8ܞ|"$@"'ߑ{2I&MD XD ^KQ9i6K<F{ [IO ~ ٩=e@x M7n!l9$s < ~F #8U. 0+Ve?)z蔃C>q(7,p(^U23WT]Ÿ.xrk[#-C_E5+iD=hOD H#߽9D H!@3ʧQH13ɳ; h$L&JrͩyvkSf孔n:5&’s}]^Vԥ9 u +JΏk~#ygS YCj59M`(S%B_ܺ;?M3<\Spv/C~qt'D HD`cᅪ%D@"$5V:jAJ><6v '4`/op{1g[Gʱj'\5jBU78b J,vQtw:|A׶@JMEm!09ؐ\G/\zN'Tq5u+^9}7}ؑ?8K_e;gU|^PmuE=Ë2GO}RND Hv@넙 HD`#ӈVyk -eAӺHIƌXdistධnC t1#%^1^ gt؜t㼲Lw8:P.x]dl ٽ?u3@1%6UPS{yh|:WJAz/}lxϝ_&%*<JIU|ID H6^T̓$@"pH &Q'9o*p7k_?~j2 (C3EU8:GY=(&ϪLux'KF!C9ʡfZ팕"z\(Wg{i}Z޹M\7{[\jp }։>~}ZTהCw8tl:I!]=]I9HD 1c20HD` bwOE~ۖAONzkh,ܒUIm]|CTTSkxxC^搏ۉ$;"Cr;7Z5m\Y6W qHp~m5v'?]_8ŭϾw|䍯[obZNޝKFO,$@"~72M'@"Gb6:lۡ*WXPVhw_?y_U|-q)H9F56s{@"$ A@"$B`K 4x1yCXêfq^jW&>xfֳ~bDCUƲUr8օtY ¶6Qp 0jb^_#;'w\5<kpWoQ؋M)s$@"$G 7vaf@]xq%]/V~L~cHZ%ļSsbd˜P7E=Ϙ*>cc^Ξx!0z`cu$z۾cmK YWxPNCw<\we/yEp(](ꅩ\d!'MD HvGw M#US/WA׫ &rKtXyrBes})zzWqU^Ψˋߘ(녙cmNElhx,޸6Ϧ 4LJ6xyStRꊺx=ojs:ݦ\ eCE4k[_ ohd|e@fnYO~C|_~Ѓ7}'@I9+G|D HD`ߥ;N@"h:v^Ϛ KzS{VX9EPO2mlେaHcE,SjoU5ǺsqNR G @ʶJG ŋx !/ni4 ?RTqP ^C_ןUxM6H5}\|"$@"2wʁ$qA9^G2\zX6Q;^]Z7~A,jѻsSBAXu=+kpۘ~PtZXNpTgw-sNwCͷ_r)vjVG}"PˋfyVtg}+̵'?G_7VC\L$ ?d{D HD`9D H+ˮ#niQQH 41j?z?׷5^ˋ ,gkc(J'/: _Q9]ʇs=ywu5<Y6b\7o:J}Q34'uk *W4/<-EMҋ]|"$@"0.tLc"ShS1m_wny|G]u?lC4t1>@v1]?h|w_l.,깑wev:g\FԫcP o$٘}#,At*?VC{k=Y>yvMtj0%a ҊUZ:/E9GЎ|knΥx=zЉJFYOk\5Uk4^+V_(lwnКb. 08S#ymN]tZ7P1O?M+owq -(:5:wu;_D H(G"$>#P^<*הm<::uк{kF_k^tl7X3eU﹘kJuQ4`#+1F?o&3J醳Q7IΏ6fs_g]JGw晧{߸-X+ˁYtsZC1ڐ=.$@"LL"$FR]z0^|eO%eJL:FQ!5l,[ض9e91|%~z\η̳­{QA\T]玼r=w&LSzD?\6f!<_o֢ҝ 7"DT*|ܞ|"$@"0C_>3E2@"$;C`fʛ o]FR.i}&s].@6hsy*E~81)[Ͽ##v<>.+s2?1$3"e nE#_y{vǨZ몃 D:bmj4el{GW⌯d6k`zހh<mʫ5^zDi|ݾSS|uן<=O+Y𕩮_t=:_؂lx&֙?\\s#HD!WA|i* /\غyb v|kDuF|/X^Ϫc=]<8z|1<ﹱB O%8h:c 2> ߩ1 =)1:1jo롕's|U[IcFY7].nRJ?L1s_\bnbو7 `R^ɭ4˞{RvryLӵ*8CrSŦw9Rg =_N9=pڨKw~{,f6ggiGWT3;(C~qt'D Hc~7H$*&cE}LLz^,~c1N˽,2ʇclM?cUjZ%nlNS1kz,q>5+ ]Njm+CkrT]n^u$7jT3_rh<5+ΐ?uzj5|{?6, h]毼L( l fg֬? h>?ZBOO?lQ=:o0-Uz ED H)pLO|.;H.~eNsMxNFYjZhz#סvϵZ"?ۿ I$@"o}4'JD#k9/Ԭ19e(_ кhxt*_>41f(#uuοg@^Q(o=<(e]e΋gk%\6Ŝ.,SʤOYڰ*;~kE|^9*G P@ȑ$@"c3$@"0@mUTj˲F{5HnsWEU?=-qMby'W^o ;.3D#{`9^q+cƂKhyt넛U^SwnɠMnő )0]ghPGP[>s* ʑ@"${~!HD"@3kdmݿzcᢉ?&(kU. Dȡ8^r_ G+G(ד}.T~4䮛{ڠc9x1/kYfP.r_~Q&(>npTcDȺpCԤ;noΜ8&.5};U^l=$$@"pw1Zr.5H!@S{:G/zcWczŬ3h<9z>ZY'zb6.mFki `k=1_QZWO9YSy <{٨.٨[{M>q (P?/ .H*@+2P^:|tϧ埾-?3g_S}Y9E_]'tqt'D Hc:%&@"YzW=9~QߓNs'S YC4QGLYmVcH5Ul2󔞣& /X"fB8I|Ur:JL< ZL%x=őX̻ۿᇟrho=+|1ϦF'p~J-i"$1A`1 $E+X^~ˮ=|Jv5>qʾ/_o0cϔ^xYۊxYhru  +&;ǹ2zlptt,l q^Rv9s\bjc="8|yqxR)/j  w*jmgoox/mh`MI%MD 818ɹD HA@]M:yVR$^TԠ&|$">/ZGsz=Gc50{eIt:?tf@a *Wq1= 'lk\{=p|l5Oy>j)Ju]ֻ q^qŷ:^mh@"$8'=$˚©um YiI)0K |b' 9A_|997oպ$kCxdxrMTsY1>xk/+-^luJ@"$8F';$NOʦDz?GuʧI5@ɍ E/6zd^2Ƭe|O_oݟƟMK~w~֌(1f_w?.@^ Fjo4~mۿ楯(! P@ȑ$@"l%HD#@:-Z,M&Lej_kv\sKFX y_ ~_Tl{]s-Kslm34y:3VsqUx.,st'yY@IDATc:U=t~+%r`@M):}:T%@"%u֔kID XofbHa>FrnvQ:1ԫk^XKVn3yQUYN4zz+v\|rMnl3k9KzQ7x.䯯-";>J&@ P㪾l<[ug~կ+*tZ̳i|:cO$@"pD/!WP[Ǽ]XQ65O1ȦCyΜ} co|rN(!4 wy ȑ$@"p_f.r"W]L qT&❺OqSͮ3!X/z'Ήo1'4΁mq>w[Ak>9 H=\.|Ѧ8|uMv yf\ccW>һ,^Z"A'!Pą *k|a[Ct1u4'Cgr5oNr7G6 >$2/z8 !3d@ɠ4>ԼeAI-Ccw$)T@k||x`[} $$Tˢn;?O7MEuC966Jl)U,/G"$C`w[f.r#W/+Fy,7r''8dQ?;W:xbC쾮 }b]Q2~hG7RhG\cwJ.lzE~l4jKy uP6=mt|cw:oKZ5 MqI"tH_s2S,G)Q(9}M<~ϥ9=Dd5ugUL̠wsk @'&@yrǞ{ϟwwX&0-S򉣧>)'@"Rr,;H'4H^:W۽xuyaQ5bYmDsCm}%ہ[7\8o(搾gc]9cqءlN rGǺyT[!xdy*v̨sL>Q6EoxXU;NǙo"$U9X*;aN2d>)'@"B"G"$>!U5mdU>kpV(Lr>TJuuxŘ˾ۮ<)WU<9+>%aeRKS ɘSx6*?&?^6|ӀM:`=SϾ{Sěqe9hE󰢮eImvoD HC$D H7~%NӂyjL4Cfs1ǹ&G|jҳvlT*Yv<:(>=_bE;kYԷZ7~Stcs)G\yܝUcClU_^ @kϟҐ MP3׼~o-!l"bN >tnO>HD!e@"p8!ޕ6>4y"5noopf -T^r֮sOO/pp*Eĝj@xFX&9!Y'Rs $sl׵~5풟*<_@Qەc[;XS-xW&@̑$@"X Q:$@"0MŸG݇ljG1ǑbFPR΋"T\F,ES7t||<w'\28|Ϣ օ} .#j[4m z|[ϙN;G{/z&E5rxg'@"r,3H&+ez#E_ZvT)kGLt S Xw;b(4gPpZ#9If>h&5k._8x^#6Z*s_'6k.fP| liM.?>޷rMR9UZdxQlqt'D H@nt5;zv4rGNAqO' _; wSMu]51MDrE2vρ)c_:σˮF?M>iA>%눍T>"O|3C)§G7vr6Q lN{sGWYv3X@:%cLW_%A& ,Wlճ jnfU2+q>kC<'x! pOT:Qe/Cz 6m{]_9L~t9hO s<8WeBu}bk1G5`׈+^|קGR\W#HD`}hI)HD`)~qαipx9*r yeL>`HS>ThRs񗌎E}3`'u Su5t82g\1hwyʇȇ&7g.,x^bSWƼ/%PM/.r3fS=|a1GO}RND 8<)YR"~:M +[t_Q]G)|d[7/vG&iCw2+f 95WY ϛKzk1=90f[g.yʩctbGQ1EUX"&N喍 .5_]2 ELp'a}ǷyM;5;:QU|ID 8V%@"p+jH˽o5rgpNa@>w&erc^k¨g9i U ԁ>VwԎ7S{^3%zkNo95}?hn3yXC;w|ϝg?sċJˡ;V ] =, D H@ns%@ 6QKhwǻ>/>oOů[7Žݛ6s֝7*8ϼlT5S6m`֫eϪ?~Yu=&]=Eϩ" ;5Q lE_6Epӽy?}7bTbnϐ_D HC@ne&B`8H>~h^QgLͱĹ6t#>\%6;O [1ǔl)Fݦ|hvG͠o |K6&@q_ҧMk|MD 8P:YL"UV:ji"VgV>q~^< g7ySuѼOg7s@=VͣSO#)~+M,s¯/sZ5sx5ēo<8ϳ,vN͗n yI]UvwB-z>|+>;^tהc6l#HD!d9@"p@+bB.[Ǯ+Jֲiдn%3S ǶbU`´maPxq9T/cS>t7uDdf ke~.Qz ,J5u/zr&ʧ$9D HpN+Gί Q87C<~8ymē;qPs_車W%u9{;@]ۣ^2:xd"=boG5IBjA'z(%s6!7ܑM 6ȧW?W~ɷ%&|"$8&'`,KU)6QՈ~iC1s>lb2SlPw=S{b >grG4E5z4_9#4CWËD?ONZW>mH1ǪvlSO='ǯGѩ56rJ'ӻZ@5jzțvjM{6ʻMu9lS5D5)9/{j$Xsw1s姿p|WyȯxF ?nu3g~>sC썯}C{}9t'?ˀW3:>u]@"$ L紉@"pXׯiz>ؽ5P؝׳-MDqt'D H=F 7L$4~M Y˴`] H~ww;GX3>ʃxJfcY:vS߲؈c˧򫩞'zgQAy0$bNRZ'_f-R)~w{o~wQT zGQo[Oʉ@"${:HD`56Tҡ֋R8I~< W#3M{i7?b=SŊD]ԀzHFfnri>jzmhLm{vB_urAU!E5˜ǂ G63ks|Q ׌/BD(3~/\u~o?7")'@"p C~D 8lZ$C{{9W49]~FaF\ TשsyΣe}sy 1\otek1.+7 s'd\Xxt ྲq+fyM ZG kE6 Uӯ{'&BA@e:'$@"ar`Àf>\s:s<ҡbs:fx')6υw_׋_%%z~Vy{:lءآcE_u{9~}k`j)3|-z&j1UBjދ^16ʋ{+175T'>Nat2)HЕ xoxuU?1ګmk#ġL.R>Q~y|@į:<^1¥k{ 0p|}4YtǾ .|=?w">z&o-E52GO}RND ! 3j.Hʯ{~ <'c'?vρp^:mđ_Qj񸘳's_/zdFe4Gz#${~=q䂒bNo,EynxcU=ZN3rNПڛClPٕ[hױX 6^'>{(<؟2 \bL{6OO~zIEq6^COrA E0_ݒw*p>MM>/_= pK/~}WQ&@ˑ$!G 7 IVҎ2ӌYS\|F]G'ӵH/.m\W0;)Bc֯5?c-"j!ٲ|HM6 Qk{wISPLpjѼBW,dr!Cgs vPj‡O}?z;z1?W#( HD0"X{֜]8 ek]fmcOes~o>[],Vh|ƝZxB>Ht[`#H{k&xIDzdTK}uw+~J.armrVP ~>Ob>͎XljϿ?-#7 9D 8a^0Yhe9_/[7Ϝ)Ka2|ܞ|"$v&D H64jL#k>|hZ=~>ijؓDa.1/Qt_b\ӹzשYs%z+| B=^s}p&Fٛ:sdt{C>u" n'jN1 ţ0Pg{#o{?o-ˡM _lemkh8zr"${"$F^x%dl3.cQt-_5Fe(;יkett/PMc%8޺^sLass8/#W1&s5z^5;1n~}~?y/-& u6BJGO}RND XXtMD`/ڝ9$k`o\X_|1fi(fE e V͒(؝UBdNͫ`N*//}=89}FH\Wmɿ0G}O͛q#fr EBvDgW_}uq{ @Y|ID 9s22H=Cf/4]|y_W:{9椓RJ (<ϑq"l^+}4֊o A`iv)ַ<^ӟ7}}<^>nS>](N=>U[_-{ )K|D H";.D 44˅7ء}VDzk ,]t>9~c޽^|n)fPN=bs{u)'*7iN@zC[gEu9E A|s#˯6Y/X.|['ۗM"C_C_Ͻÿ_"&@o@!S׉@"$;D 7v\%@"i'|lyDy,kr׉]B*21ΏGbv*οΩ=d:C36{Zk%`j147MS>@ՠiWbju.F;4>ìƕ^ly/Wzkİ WAŷ$:_L3[%3AN$@"""P$^#x=46[ W,Fy.ts;ʬsny<;_i~ll x-\&%eqF[1qW!@e!W?e^2 Q^ź]|}`asJ'Пxk7C_4.Y#Rtӹ=D H%U@"$hn u+]O>_?O˥1XT_-8QM MryFqy}y:!KY>UԦy;VU74I:rx~ p_ݿ ⨿Aʛ Dy sآ]<p/FE~|~/VıMb @T|Oʉ@"$@n&@"p\wBi |5ܴ(\nڐE6}/jZ^2BirdMs#nce5_ұ':}2Ly%ʭ#̼}'U%7↞.|zJ2RM^x-[;&,t.^g~}Ko{ӻM8S\EUS)]<2GO}RND 9D H~Qr]w^B2:NaW60C.;!ڮMG# rkoy밹:Y):8/y~5~H[&Q"e׀/Q吏&_])^6ǞsU/ݎY\n0{/.Y/|_ sUz Q4ĉ*nl#m~5oa׬-όYBӦm۬+1EE-2(X(M.Z2PNs̯xmjk9G[|(ka0 ?gzM)ٶ4Zl~n9l$3ǂ݀u}ۿi$ ߼ _~ޢfQ{ѹ[(9|^'gY_~BdY@V+@C1՜|@H 3@?t#ܭwïo]nr6rnԚaI~:"v՜Z;ϬѸ~@_bA1?,Ɗ>%l= "vEfsa9N_p}3g?QĹ dGT8gج@V +p*BX 8&oJ$IDV d4d4"{֋ȉom[;!0_~`ܑ)H Ԣ"E#fsN~@0''}w: [2 kiaTGN@;Os n)rѢE'gYY8= d4;8G~j2جw=v XWvl𵩽іư:߽1"A2Ns3eন)"V/͂Gr]\yaՈшLYt9&7K{ř6$@nKV +8 #e@V ++`thsyQ',Ed5s cŹqo35Ƨ#[EWOʤSyյ0"vEI8slLzn~@Aɳ8Kzx\&—\u׃O{r&@)B@V +p èr d'gPMԞpLg@G׺x[ S6['{jG_u|ZZWuǔ͘o͔Xřk`V! LhUz"9Ftj|yOxQY8@)B@V +p p dYZoi pKmZm6ș7 Z3pO<1Mc޽1"AWV5c9tڭ[YUxms!Ȕo.`. K.[zF|Ulwo'į,&̏2]K#Υ&@V +pb*'TBYU "6-dn| h].r+ ~"Y@Vh*~Ɏ:7k׳)N[44jK"$C]&^fI_w[3^%Gu}Z65ܫZ>8jvIucüzʧ ]㚵ˏn68@d p|篽?wST+6b -yDO% d'p"Ns.2+ Mb3si67ؾ_CDduv3.F7tۘ~7zXٍz_rw0@D\W5OM; ǠՍV:)\ϼE}wGQ trdYW8q< dzcIxs{7?֬W"7nҸ6㓸c~ m4hCu4҅3?dѳoa/\`5pq;79t?b޲6e.|/Xf py?E}m9b](9Y_u/6=/}xͿxɽE9Y~T 7cE)7G/lQ/,Ȼm@66G9cdί3Ǐۉg< c<8bE9sPɝ8.p9(Fr llӘ>l#R`A|Npx盬?cT6ڢ=L6@5N+p`vy-GorУ"UPrw?F(F؜TѮ dcSquN5' @m^TοWڏ" `|V +"*w"LEoZ+sA}E;!sһс=(~s9v.Mq`K<| y{:cX xQ m^]1n-2z|@:}vl` 9Љ£#ׁ/xN=溝_Uu`@&ѬE`(z8u9 dWL3GV*oHY SxGG1$L6|j`clu~C]/tP=*>ꑉ*=»dq-$=zͥ~Eß˱u z:!wc\6q9q4Z0Fhn"Xl\>&[Ĭ[YΌ)zs:??Ƣ~A9 @˙ L祑zdqyY@V~h YziAîdt ד#qnf*})b~zrqU:A(rhOޓQj`8"l܎9 җؤav~9)t[:y<Ma8wS|լc#6~"`b,֥nl 1 OQz [n>z]X{q9ZD 3yw,(fם W/0*躮v5k#@3O~?v񩏾Ms&@q #+ \Y@V +pL*;xwi\N+%zuEˢj~ƚ12Xw.ۑ(#?&_Cټ!z_G5m Uݼvn/brhא1d{qx5dE]vN*(kO7~M;r29.eHGO19 dDzp,O[& ia PIW;4?mUu󫭅yފh19%~O,6SSsf/742^zОL1<֠ 0$.ltf'Ų֑8=Y95 -M6t烶/΋8a? _ Xc=wO|{MӅ' qj0 U2' dUxdY@V`ܹfh%{Oʕfԛ$|>]XI?-WiU6?;?揼\NpSpG;sXmRNQxw+  9UhV +hН^xfp'KL@IDATs' uجU<+n_ཬ:˿p:b,6j= nb03X%r;A҃:ߢՀv_H{>'8kO 5E+U^t< Pe.~C٢ˡ?Gze&,br d{fY@VJ@l*KYZ?)L+f$IX9㌩+xp!ϩ O:*h*j{qx1`WQXaE~Lhl> f H|H2HE]3M֏TZ/z>һ"& 7JrdYXyV +8fস ?zlge44;@ Zվ7ŹFg,]Eřڻ+YwlsÒ CSZF8e~}j{eT1N{N 3Dq=Y]|>/l?=Kת9{:o'>+yGmM/lY@V`VK!+ lY CYC f/Ecr'zFDF7i5VU(-ʰBARcn'gvsѩ+mU,VlcW8p1N*}je=Ƒ{;{ED3AgyQN-||w {{99YWG.K>+ dv/Vfg,,U؞IIf_Cs齙lT~ѓ;fslS<dn l'(|5R_+yM{qӬ#k[yȦg96"qP#tmgfc7#nb@ P"~{w' {G9DQs9(C6qd@V +uY@V +p+ $\;׽zNǑ7^+ZJ2: 󊅟cY [+16 90WL.?:$G7NO.ǨŇcyA*n]=1*_"J>%NYT3'q~]5g]r; GL_m d[WغS eY+ hpړ^6هmz ri윏 MNɱ忙^֯x|1eMlL67xYAHʧ[^x_VryCyzQQo P1|gYkrTV +*A[cd.G23蓶 ӀA^Y{|Y[/J:v¬#J3 "X_R_טbs5#ʵѵGy?w*l[c8-T'goN[|WL?~u7^fte;x|: p̓fY@nlIJYVArFE:\XqG*Ekgg̽4h:kqU1\?a{|4bL٠S6,9y9`S0ڮp=W{#Fi#'Ɗlz %~ Y&Tq@>j_mZm4<7~U/qK rh 62JY@V`*u>2@V + L݁$9+܁@&_^\JcufyuaP]\Xq/˚*N'6c5sxmC "bje1VʊQ^E3Z{^Vז 6#+ lkr`[L d&*ѓ1$%(\2 5 fl.6CtM՝le##edc~֕㗕VQ}L˾@V@n\8 \fwlmcz5 y#q9iZe7>͐r8]&}?x5%u_8R/G*g/ظ,@V +p #?@V + dDTrtȄA>bjs) Qmjfm ,zc1sTp)#hl"Kauf>֍s'' z\˯) O>ѣT(,t)^Fyfwgos'c/מoMf% Qy5g8YҬ@V +p #- d_h`ҹz2t'ֆS 5z4_9v8ZX_Q9q18I"R{Ď%1a{1:ۓEClQ-}9²cyH+SEAb\9p,N>X(m/}-n<2;<.6$ˆ92 z>\/>ƈ{JI\MPNŷ P*f_)Qtȑҋ*+xV|_(OTиGWX#)7EEoKCPlaŐaiA 3Z @y{~ %I{@g_ȏw 创χ lŔh@V +pdڑ% 7nxɍ(ثZ/2py?N{ !lo.l=~s`4G>w\/d?#,(<a.*;G:ƨ۬oI r#z·#c>G? "Ezl E=2Q21裌6Ԝ}$ f6:V~^:yй4V;(:`q!g.x44G.'z#wl\%C'J ɝ\l-ẓ/`DLqgJcЋ8B'e/q?AMĢ!8w~_#o6 vCմsކk4{`k6i{%7Kxxϣ6D+ X^[ȩۯ7a+>,1 *c{\#E92t>w^:|K!y?Pd60kH:ſ#w,tC76xcp=uE\45"FsVs3f/|J=G£ۢs9Ћ'gãxȠQz#ߚyPG8WU*U9\VustPt2<4-QtSgݥY@o5/m2azz;X 1fJk(rq{X˸ɢ0S8t^k/d33朠g@/pW?|Гq@(^3k'[D,+ @r.+!syZn33f7)yȞc$ *(2އuxP5_Qcx|Cp`3G\s܏xN2p\rrr29|c>ne)v9> >z\'0c0)4y poR#`+od⛝VR *-\/9s:j3łL2® x!?⛏6ƝbJLh&}ys'_/6ֵjЫzֶ{Iז`׳%t^gj Vswщ P7 ~5$|+~=o_RП Zs=(Cqd@V + ,+@oOD7>rd(rE0ః E$zא,b pFXLmI.'9#/sFT?$+> l2+b4w<#FTK6/w[2p ?`D%9jO7E㺈v֩!T8h~$ZCsXG kE9:l0 W\-v}T /;LH]/^ƿE e/ū!A pc"!#d=ɠsnҵ?#?&\|r䋗O;GTy%?wE|k9$@n" dw<ޫEɊǒ7aA dc(v/t z»ݘL1ܔ< _(2᥏v@}8?>=a۰^\ M:z6.-;؀p>L˝7\/?&(vGA㕗z!a>pu6lVH\GY M`m-69(ƺq[(èYoB+/èvL~1>BMo[ԏ*cZL{^{8w |Dfk5g,s3 ,Ҿ.ʑ  3/!S?./ޘ^y،av#n=wuob[7#M}ֳb0:WawKCzb~>Ͽ9/+_tSVgc1ֶu0XXP=dOiPc:?|P{|1>f"S64OW+ˑ0IV +p<+Ͼ}f8iri' xlF|~^LXn]s2Kk8\;fm}Wa,׽.AZ(Mi¯",~*6:eSeg!d]Yx=ܲ%a6 ]&6]SOٞع_|,PZDs(ӝK d/eL'Y@V`*лefDd=㤏a[9r8S nm\Y';k:ӷh׿\Srfq81}XEre5}']7ؙ3;ݿ5g]T+`M#Υ&@V + ʗY@Vʩ@ԜCmZ7Omu7( p>+Axmr圸ͺkmg{#c"9TvG&r)rNOVHrZ2[ Iƹ{*0daHd xS76<:IU:z>/򺟽_TlT 1;4 6k= d@nti d6sh~^ kZCa㈞fUz{3M5l:}/Ly{ͱ7>{k_M(9ˮτǬ#YB^wG}3ѓ =>r+?|׽?z`m#ǧ7Du1"Q|:"PckыrN]PL07P7 /y=? pWuxٷr!GV +Xwv dY+@S3p 6?*ްsrkp Xe (P g"&j퍛jbG5r]8n ަ OǩO2j6Sw/}~ocD 5ܗC9|^>@V +P+,GV + oU8GJ;e&9<ڼ|fW";>wQ4Ci{XymrĔ !1Ή~ltޣD(#Ψ<(AjizNqmU6' eaų~3?SГqPEd "&Y @n  diHhZEmb[?q-hSl1.n7{)To*#6M(ͅ}L_gޗ%1pK&n1gy9Tph(tSlza3/g>sz}vK9ΗC&=Zs=(C6qd@VW 7NϥgY*&:Σ?lX 4}dhvJYy-W[Ԍ9쭢hKA푯KY*|̋˙ b9Ďu2[G9\ |oxim}0I}@'|`|?~ rI(Eȑ ^rjYTfc*!nѕyͷu&jymGmQ41|Qӈw䱋A׉eMA~Wo]1ӹqȏ%kls:1WF^W\6?Ҩa@&E/|;r[9r!GV +px ëuF d[Sp/2/ 9 QPvxPlmM䵊M5|Dj5s\<^|rs΃ecػcuvZ,g%qc6u{mp0ac~@ [ŻOo~?V#? dp(e Y@VU_F9+s92loU~{ضGM ͑6ϊQ,c詩!|+'V5Hy:2,n߯]&GEĹy1ɦgԍ5L_ɖ>n^}߼*;&fvZ`եd6[0`f'pBO|.;+ [u~G16b:yl7fa`[':pA*±cL蛙14Ė?r1;{JVe{(s3\ +}5lj llr ˵&9:5Itc7=w駮9{br;}1R ӓ>@VU 7NΥf'ܼ~L;FtלJ66tc6{5%:3/lqP5-C0GH{zEˡГEigX -M|r~c]׫bxqV:9f[q8Q];nuܾ5|m_|_Ə%/r@ɑ [r`JY[/:ꍮ-D7رa SPQ4U9{q˓ d s ]^.++ dQ b'#˹'S.csb~U\SjĆ|~gƱsjs]b' سOըW޹ʵ5ݺoy/ B13NιVc~{\}{+f P Bsxל1˚iҬ@VJ?5ڲY@V` t82CO X?IYϕiuQV9h|05ݾ)@L-)1o4mSwc6u{K-abH9+OPY'נpKW1*oWy{ܓO'=K{߫Pܼz@06n+)qd@V ?Y@V`/Ckx%Cu' `zv-#]23hj^uvhzɢUSG!kf=\_OCx7Xur#]Gq!Ơᦛ'S<]&']|׿ޏM-qHL6z2tIY+,B dYT`CFH#$y<xrsLY6nnj:uf+DsRi~=*^:bjƴvr\G8XzE7۴ojCuZڋx^:m } 1Ox+Ugyzk7%qs4"-9F6m^{2'  s/# dYîiLso,bzsɶa;bC(߆\7AM:;j=]ǰu<9Zrױq}dQS_ϺFGkƹ< w꧆cͷH&gO~#?rX 0wm)v(c!J{@NY[~PdY@V + )[.1^f gAs PqmռX%,ՈFӓcF6|#`fw1z0\نtc~wc#_˯x^gc<f2p+ٸׁz7GʕZ$m̅ ׼G>co#]`zY5-Ts*Y%s}Y1?ivV + Gzws,9躾kPN/7&m{3r9U)}sהE_Xn=r==\{>-6kS<ײ(<-:c>n8 sȉpi߯ O7Іϝݗ>S?|Ous9 "pS 8'sYcX~@3@V +ئ pKNu"\ݮ%^+[,Y3{6q:⸼q/}գ_~qmxLB&^#d/Y?wguV + jhZ{*Z#Ϳ:y.)OkO3lÜ|#E[$J',;ٍn8}W>w>r/L %ѢE%L  ?@V +p(kzwY?k3MLiSKɛS|ؿSIP;qy"A6܌Eh Mrvovs~Xc)n=VhjP奷5P> P?S/y?%[ˡ&@)B@V_~) dYyMG:P\XKvF\q\C΋ .[:,#֗h/2\c5 7sNW 3Y٬g{dŹ鯱ե}3-dϞglu޶qS.ͿS/K.ʑ9Y~Yצ4+ dV|17âˆטt=m876{XY)i:bb'=tl`O<|Js<C8^q9'Q-5X 6k:nˢWcXïIng}i/N+dM;;qRYr9IIV + dF*MKtaա|PJNDo{{3Ŋ^{05h.5W4bƨ|o9n7e?~YxMgўkúӵ%&6G:&pO~Ǜ?T,r\-ӢE\'s}YW1[b dǭ~wn~fME n[h/:8iJ9F`<ڟ|s|C=}yUe#Lժܦx|Lf^蔗U cfL/nе w |Iw駮9{}^9nԔ ,CM3Y$+خ elY@V`+л$at45P;Mp ̓56nn!qVY'˚q"pϺRX_9]guR&x5Jm(ʞ^(o[67Z]VB= pP#+n|!ÝjSoP y"wqp7§jـ &ƘOٰP~ƯNjk+5&~e/h!k^k6M4Qd6 6PhNnڴkUy"o?+B=Uhśvبţj[mf^]un\m:?|,xL/\ՍpYYs܀WR7FLy5Z&W2O?o^sӏ~YďB9榅Yy15iړ>@V*C a~ D^xB2QP#+w~!^2pwɿG/g /xYu˱l"[5s$_3|G藪޹M1~=6Bl6qYW»=l53dCܜƟ>˱gXrE9v ɴ[\fs|$smˡ-sxF,:T(g)a(W rclnLm\<{z÷?[).&+(d1$A\V +p(ȏJ3HV +8oza]n(d'YkC.[KO6{3rbՙ) 44~ G=JD<֡k^댱Qoq*6jGSa@粮芛s\ׯsV3G{p;עMѨ ˺缯Z9q6Ź|qWdFS/ ƀx}AlɃF./Ent|10< ܩS~[XLZ}i)A Qtd2`ڬdO>+8 ,:@zk+@5GnlIJ]As.w~WJ;E3ƣ5(up|Oz5‚q1~,M`W.c~z b̼ mЉcظ)J/)x)ˢ} Ohs;es鼙P`Gz7W31[Ӿ2IO% mP6n86c{W;|}VyQtع⿿~oz~z7p@k4m+/!?pwR:nw|\ srfeG&i:ַiOeS8eДqjt ;*&)[ש7Ŭ8u_eO}U#_k&U>yIGldKrQj`/jY_-VtĿ|i464dh5WsĆ6уÒ˦ W6 7s\b͈`'Oojq' ?tƎEq)dfTq+@5Gn%Wؗ !N10m8%Gnz~a]_pʚwf}ĕ=똯Ur|*CLu3ȝ<h 2AC  @΂ɽ' P3&@CXh^h⑁s8`5ןedMt\҆R~A<pSЗ ƺ>ϟGyQl? O5~Gˑ9WBO+a%Vkm.+Xn:5d> xGkҺ}kT4{+cx3}nRUX)ڱ;tn Fc;8/ܧF>rPlǞ px[ iEz|99;t9)y i+z΅7e[B!4զ`$cQ/ ْe#g* cżĨ>[,(K(xiķo~ P#+p+㾎k #7v_ FRHdrqYOw 9U$.X7m1?OG7U֞5c1ĻkNכt>ܿ:||@/w@l c'P*X7cuvy/O>kVh!z:@KB~Cy"ֳmCy꧙WW$g3DsM~ΟEMgˑ(Eȑ8Xq\Wy~ D^x^YBW>1?uhFo~VZljEpl}\My1&']gQO\>WF?DnMhOnQ9ͩ"+L>&-}/k|RkK8Gxŏ?u6) %GG'C8zyV + w.Wk8'< Ft?Pɝ]O' zz"m˚'myԬ ԃd Kv]K3 uH\ fqp6=YϏd:WPhtQZa9l A2p4׃d9z9MUGA81P|26nhʛd7&osΛ hAp[ ܙN鶭4:#e[oN7Mqo?7kO?`fP GO19 dvY-]:Ic]=k a5q;n|_9YVaKw`$l ÞN66utsoO45:~MڏU:$es{AYS =/U"k=?m ]w,<:eg&D|c:d XX(z|UE)yZNQF0eJxk^ӅK?q߼ k#8S0_G`S_K;sO>mo?3zab[sJ0#$_Y}@۷R[G~ D^xx/^HJr^Rh@V +P {DE]w)kQXCFSЅ-:^~<3+;gN]9sÍ;Ϝk.˗vNxs9nK/=VJl m9s&q*9y2f+57v.> ڹ;O~ X?=ϞmoZQSQ`kyGV +w>LWǬ~ D^x1;nV`+;;|N`S4xHһG8QvUcܹ1P.Ҙ_Uιڹ%/9}u;nšr㮝3rw_,}`Py\~KO<ܗc_ݹC;Ηg&S츾LNڄ)'_!*#_͐]\z)l! v=_qC[;Q}^,Eie^QMF.^(~n,AβpS·6_$@_.egn?|[TO 6;d;{e}W@V`*}p.i cz3qw~ͣWkۚf|X1KO?[wNpCy<֝S׿`^P윿;o}Me#ȟ)J_oKYMᅡ!TTԴ|T(_z晝KO>sR6)xj?\6ڗv~ǿuXz-K@ׯ`[uy\1HAD<*йƢ\ѺUQ<\~!Vy9lFSzY s}qܮ~ f/Mo^go<{wJIpN&e됝&{<}޿**}9@|f=UF^s̓fV ރmh+~ D^xps Y Dyo.lu=)?Z|=C_gRi ;׼澝sU;g~a;!w(7)]|739_]v%h.KcWУn<;ʓO?vWs{b>BS9s\o]0D9Q<Ss}L!IM2jz|J%0=m:&^M=9_7\'f: p͵_{W;ϋrmW6 g7:׹&=IO$[Y$kd˖16F0 &Ni!R MUSՐ86X$Y~ڿ}g=[}Zy[{8V#t4w!D)z>$G`L~!ӎ::y e;gK/zx7煉kы/ \O\}][>n)UeM"$xnЗI72E-B$%bxʪ>2$_~핰zp8ͧgV8%+eD^X#l-LT笕7Z>kcV>m/[ٴ~n!ٮS8"7"Cv Z5sEB>y?e0#w0swN=>+&UJBRf)xئ%'Km8eg#`G=|`g/>zG`~1q)t7ǂ~s!WkSS嗆w!_/k~$)x?=HAro(NA^Fb/xDX|a›g  (&=QR+τ]#*}+KcuLQxlWmaYQilB[$(Evg 7m;/ Bb!w fv`bgoٗDLˆ v a|OtY#AvHyvȁti_B}ZOc jccx@S!#o {{ h:t$l풟dã~,ÓuAtٷXY9}*g‘O}E '//-H|sI^Yb]Э/-k[hJhOI:Ԏ9J\c:)#\g(,쩃q Rx C ?a݊xB1G'/0>>=>$]`7D]B]IyذІuМw,{WwlQdž?g~~\GpF_DQfdV6#Mmnm1 IOi& w7]>Ca[ ]/rql7j'v7 =ް 0+/I( PJ9백%CYG A@v`~q<6KpzC~ 2Nҭʭ-yRR>Uǡɿ$/={~,î cW\#I&/M+0zn%;"u'bj&L%L8:̷ex) K̏:;E No]^|FMJjkyzLp#M,v*g@SNMa:6_8_Lf8{ ('zNjY?)|e0GA"O_se}a?~tq`DnbAw1d 0oRN4I^=nlFSN6^bOX-W^>8R6(I:1!oi[yYQvSRA r|*Vt"]A6ʋ-~N,Yy,wrmc{O=·$׸"byz #۷Ly;m#/)6QI^[O嬸6Vܖ=h>&}{MSZH⊤_1t/qht09;&'ÉoK~'HIM[!YRhQ/6+MZvuBc=|* yCaގB˘̣136K9(LJ1ȣJAI=,/fQXم}Yr|ÞG~&ɂll8[`hS\-QX#0T%33)_p%^ԧW^e=:"^sG Qy?fbx^u! IGBG&V_NXxey5lֺo'(yMb)=eU.A97Rg%Ѻ|r$'rTg? 9CF{#/X&/~i E0!6g<.Fz&gd!<0wD\w^^f?.cY]ocGD9B P-?[;"l#ղoUОYhxƱ;p\8x[j(Ѥ}ʍFB}%kDZ$! DĪ<  Ϟ ɓŨ,]tIh>FX>s:l CnuHWF;ѐmBnyYJ=iGۢaM)-eҾ`8V詣О!2F8"G!D(D_qMR>c]x8 ~!`7%SO\C7O<3"E#f=b;%Qs8YmI7hp*KdO.\ {8_6$dz xq6a8#091N>lhQ><~^ rG#dVh͘ZB[ao7z%ް)eἳ1+#϶c[͎X=mPDA)QGLշ?;m]$P,99<0u5pݡ/<酥E!z8 A!٭8|`ʁ/ɔG кpzA lTuv<( iʫr;~DRGEa;꫾ wyV_ՓѨ#$=ZeKIlEolTu,ZT NL[*^ !bE0O4޹ gW0QЅOL><>:s`Xv0^v͝k+_:67; j hZH[qmi SN/ו#- > 'ۙ"e|:.}L^#ra.ocDCtvm o{;Bտ /r9*l,`!)*ѦiPYJRؗ_ۍ4Vȧi?Z RmBX:q ǏR'/ehbqsYY Xe sw\x굓'O "Wwlѫ)cV#_)zp`^Fnc/.1́k+=a5fxmL~ޙF@M$0²*"sC)\xNbb6ꭎ؍ވ^:[Y'lD]Pǘ 3q& H)#]#@ YjH_;RF;qKH=4bQb, srw]t'3z#iΟ}fgNm~mߞn?ga#BltP >Yru+Q@,no1X0.p,V:o>q n7n"VyZxH#vG؞37ԙGbdV1HSq+? ~G[&3}X}6=#[E\~柎V#PC'&& 7/r"/\csI'5 ;^}%E\'vbUԯQ u]c~E0*<xـ .:t1CXIi0wU;/@ypG>/Rur!Ϻܙ2~aPri"jncM?P ߶53goaHڑ wc|TR>gRز7,^: m=.#n⍅-zNqa3'_˿Cw|앍w MZQ+/B'GpG`! >) ܬ JZS!1 )[EQ8N>}j:ȫto:ہ {Z. iLcw.Np< yCO\mc %G^'Ows]'E܄U>G < |8;8^GpDysZ[ԑ8VF~UV)>/7/#%}2`hĸ!r־)QȱEW_fwMYYQ={9q˄3> &Zf2ۥMpƢTmaO#9;=Ec"r!@!luǘ|,dB%}}W?q]I/FFⸯ#8#wdn_&LAOv6m_і"=v=>?/\ *56Bivt=Fnk'"ikkxghN=A-X=}qD[`/Q@~X |,@ . X 857o{ݏKulܓcX6I*jm2G < |8C/ .:#,4EAIrq?='=Bxۿ{A6"bFY)yͭ܅6>۸9]Lss86P$_K^hCFyn1:m )pFMWQ@킇 6|_:ڵ_C,]Xg<|@@/;# $2?ʙҘzPnԥԯ_+wm?'Q_u-F\F/(/"E,.Ib  <6M:LʓB^URcj]~ݴ9&iN c }mEF(ukoe;CN/#8!d>r7?al3d/vDȶ9Q߉)J>YJ]EVeon GFkR^Gh8ݘ)j!>[/ ŦVEl# bOuu@\X]^w}{E"-i8lK|`[6#8@' < I!:+:}/ųz{'mrG@yۿ ۋ#m6V-b,1%lQ8H>X['i\Z Iq>7}$+,hF <6[Km<+g `m E]<C]8@"@}i uD".GIQ꟎!3!ۡ>Gp<vfGs:RR%6yIPp-Gn`;" IUet7$Р6ل=]QGA۪b}n7@&СX[]'ǀ$ 'oJ0< cC)Hȧ2(1B5 qh^ ޳Gwzvh 4:iض۶;#8 >?NvӘ]1 ž {>ms:*9$( Mqڳu\VHXROdiiKmsuU[^l)և Q:Bl{be\bYEP[0{u^} 8@_{7dx'lp7NMX00sCѽc\mkydLSoe9G}|{㋴He?N;}  2#QB%o}C<\]M}ܣ\(`WC h% ӵkQijPV ϐ"*eA 8CN /#8Fr{;B1llKc@}#aϓ?.I8ucp(]38wmG1F3yri|򶯴]+оEx> mvX`; qA:+GlGӖwFO)_JƆ>('hݎmsC{I2Q@Ut@rKfz>#rڂ" } 3y _,vҔG݋#9pG`+UrkIvjk'9lJ^'^. ߮/ĝ#rG`h 1P$m\/pT}[lJe~qt7dL~YOPh9Nu}@!%O _@ @]e?>|Q,%5Qgq-b+  } q+*1ȱ0rw}D 庁hnVUuʝ:ϥwpG,8cbOjۣ [ڗ4VRvφ/L|{1/qRX;pnL@EB+̮d[. $Md:}u\ ö1džzZ6D gbJ 1 jZ6%'x7E B=1C8&vݑWG>h춰eYIomw܁?08#8@:{CʘYRjo$uf:{0}-aš!ڸ\S|cBg΄c-ltd_*-.$bd0`ũD^k'`#i.V'FZe0jnǬb)cC & w Ft?+˳,ҭW^zd)Ƀ@,#0P@#8V mgl>RL@W%_ nH025-b4.DCtQ}L:YfU?yյCӧCX]>5ob ["\ s=qpd+@֭x{^䟕 Tr9g>l-1k~&A||MڰwP^E->%1 PBmnƃN}ițw#ݼP̴ ASYZ;<nxGp6;o7[Vgƾ׋p ԔPegtr 5\-kϽj$$ !xnЙ!u+Mi=a?;vuю1)_v!~;R'VG^v!ukC^R~~0HӅC{_{b9*fewWۭ*;ŁwpG,8g%ڑb-~4~0y_~JgW8OtY9VX8z4Nދ='=yZ'<(Uus2ڃihƈMcSu3Y'Fb ~v؎Ilp7cK~꬟}3ɷ@?PwRn18t7xp@Ip@RSN:#8DNlŅ]73Ҧ^0u%aϣ_-hkuP VVO,XKPs6iT4yU(̲n)xk%՟hžY }߂s8<=dR>HcXX@}F I 9|u1@*v-1^GgF{/\'n=4 ( lIV#00yGGpB5R 삷vm]}*o SFc2Pcr'ԏY3X{nQv)H?[6[].Ysi+14[Bθ]J@^OIwnMmxnT1ۅƆ<3h{W:f>&I {WO@.<\L_t'~'EO6> (:(d9:u^GpG E錎-A-f@'ɿkHY5p  %wV|jmz>@acXp@)T٧a2qΘdEhن01+iU1}?P(io3xWB @ x#?Ȼl`Z?/" kc#8Naggq]x?:=foW}advJ0kw{"#[`7jYnl.u6/؄vGp ;Yjc]mEnUgn-Lr. U!aCG`$qrh8Ah,.acugw01dL몲Oz=\%O]J-6} e[$Q99BϘ}cHqHpKD^d[!\iQib( Xvk/E_nS*&Zh?DGpG`G!`giv%ɳ1w=eÉdkLgO3_r8կJ@_ fH!#_e}<&vw+ũB!ՎqQa<0C(y-(#NZbLݴ.ŘrP/6nmAr@um3q?|/xliOZPWaf|NwŒM R.0HRDKƓK mL!K æ4TU ;Ũ[cea㧬\ \7>a폕[ӌ## 9E"H~/rh_  ~ѷGo-UOi܋#9:uGp@7vlϮ0y0y 61)/=Hvۯ{G`@_,ʱ#}6ykebV[7ezQ;^N\O3To۩m zU*yU6.ybv[}`p< [v4-9VA, gE/J'lw\ʋ]%UhOujD ޶#8#p0ʮ@{^O~0y@/.7^!GɿB~bgkr{rcښ=zL;Ui>QFk,&V1JKGsA島b&i='+b}X~&tA_x&H"%c=l9 #<]J qDvLX21Ih/'yŰzccrqSk_Ibs*#)c&.656q F,}ۂjWͽr݊b1gB$hg5&TZ*?FqopG`!iq3c0s;Z:;lcPdb"8|3jcq c%͊R51%VLS26M c@N6˘]}>`_ךs*L;`w0j$(E|/'(y1EѨS--͋EU6]زXyN_:k|\opG`X33O\0u=kQ;tK/ Փ'©O~x~1̿zX-[asQ:9Z&/gGYNg8)cTn #^m&ƢTD|} /65/X ~;OXD("ÂReO8pϘ4e8q%aa(XOG`[#,m2gN~//Sv5-opLD o4y)LCU8躟Ի3m{V5ԁG2ԩMa?Hq3VV&ZLi ~Zxb۶E0ZeH(/)%R،8~/s'.QM( Y'GpG`; \]o):$3ETgȾv =51.d/p鯅S~Gj#w0!U9>6v&†|jz} %Z9?pfjیԴOӪq8ыukC]>?# (t]ERqCA\Ue|٦exth}7,|-LS4}BQZbGC, _G>sO c;2 떇qZ/8l6{|GpBNτo+㡱^UAh&=B9?IS u-#aWKG3/_)wH 9WŶmyfbMX!c+d䋡$˄>H Qa@yZՃOcd1Hm8ҟNJ@y>x_>"cRʶE\,ܓRf@|iW}%(6ҮO'X"tr9u)pNGp@6U/FЙ)gnPUͨehJ% b,8a]gchk?/u&[1d%_$eD^d&CPzk%'Zھ[=yrXPg\aˢ85C$9,*λms?r1{'sxM\:4Ϧ(OG`M;# x_]Ņ0q a p }{&;(zvC!N6#AVLL/Ha/gOչ9Y2oc0LwޡNeBֳ>0+I$ҘX+Ij~W[9G\"|[}X3h+ 3ïs=_.h(͆b ݔr^s/D8#`V#C@52ʹb3ل̉1-NK]jEd5kr.l=crq aızHX9\>,ao/57$׫E)Щ0,iK sKY:~ud=§}IlX[˧PֈN `c}a΅Rb. 6f JQx7#luKqOxXKsGDw2[ 孭 [78# \O]r(&cNgd?̕yY/ :IFF2نxuʽz\;~%MC#1?VOӧP?sZ$?3OY$.f1Cn֧/wq.F\bK:viS:6,YQ?=M޺6[12RCMaLR ho&%&HAcIAؿo gis3r ?} #>'-UE:e ac3j5G@P#8Cq2 koImt!f M^sb*jSrYU:[51%mz1z]~n@_ڹ5&[=y\2f\DPrWʪ<ƒ<"88^y),O;(ԀdC5Y(wMb#m[gn(B^ۭ.l>0-oqQ2[ڠ5=Z^ Ej"wG}~/Dk+h+ptBvȷ<]j#uO Lc?P1x=lJ#іT-hOP_-m{X[ksrnyAB0X(Mz/# 78#0 4jxhP+ިN`a}fiB/hܺKa[OVPB`&p 6fR PGq SeW EK>Q*KVD9qSbT*eZ5TG܅S:v,ga6f'&h)r6 )>AmJ'm۪. QzS^XSAchl)~N\  9͎{b1`ԉ#~OT]RU32֣vU5G@P#8C@1 7F'ǃI2t-;/bܧ^Ec-YM.Yc).ky;϶5:HS.~ʻA -_Ja#uu1N#6H9l&΄FN_j n1}>1fGt2|3"aҤ}؈x70: >#1@ Ö,|DG w $Åu(yG:Z6`8#0dma,XYoO}O[?#Kh_:ux<7ӭt K#yF^DkTnm6!ܘYGv:ڬ"6-Ox{0_Ac:?U./S{X!FܠĖ>&dΘV0-Hb"$Ĉfn&z|%}K}Џ<~/? ;).+( 뱶N2"y=#8#T=pif(ŚdnH_?y",+og8Dhgg@yX#ԹqkDy*uBxT)4iܨcU[Wc$ڄ&gbb;[O_wnٰ:ObR |i#Ye𸎀#8P H;Bx|r2cn62=?g5?Cc|*_k2#uE7-# K&UА_!QOLsmNknC_: ۞J2إ:sa MV„ )&4#dgybC@*;N*8 11Gls}XM=r/_fw+`pG`(fx聋=W>Ngq_0h,mK/>Ưo}D>T28yUQ'Ta mue@D6Xx$5}>_Nk_;6t,Z~\r& ,KPY4^H'qQlП4J[??$ڨ ƃoZ\ Ӷ :}yA&&ą>%ۢlB=;ڄFDEas)Jp6 `#8C_=q<]xQcrhF4VWON^ G~76)vxqb:,t=^L6kj;܀`w(Z+t}J"Z e82i;Ol#^! :MŒI0¬M[l0BR㮨F|zԓjһX_k}%31h&Km]Z?Ʀ;+wGK5o"XەְgټSG/@_` #8ÌN8~eal~}F&>?"{"~_엿Ƣ\( dDY0u;m{1)+@mRXmIV>E6HXirx:r[M۶&'Gii~FI|XT5=ZєwvGjlsat|T {,]]z0 ?'"1y%\Y.(yLu/@6c4u)[o2=>,o&;ke+cxn! iEb\ ?Uw`2 ϶@!- .n!chCx=& Vz7@:7IQዎkliv:+s4|`Ӡ#8C\/ /G6:1~lKV8q^k_m35s?Β!ncX['Q_oc^E? $3-#lF=d醶y>XVbo,G@rd<&ۦON/ZY؁8ܬ]>,MYSzW?t`  ظ@s[>W ԋ#iAGp r^Zi-`ak喇 \#1*vA6*gg-8_uvضNaRyYTS%Ou%+u|KgpNB_Y=h;6V/<ڵm|:R +)tyl if y<`RL&궽^Ic:ޅ` (#UXAIۥ>G1n[8|$3/rwXWt[[ # 8# /y؁"Mg BǑ]/-gl郻)MZCGSӭ2eZ|(#\>[vV*Gzlvh1CB2oFj>}eΔGAdE tG*#<6b[|\<~TԨ0c?hUd[bMƍnI`&jᩳ2ڥ@%q_i GpG`!`]H=k.8P%-L/7/F8/'df]L1 X.xBI> eJ7k{m*f-vM [9n?웥M%-`m5ϾQ-u[]^3aFVx-,* ܄Q4ԦA);N70d\XC%@tA  }arxG;guGue{ rL7_}L^xwa}yG/@_` #8É@s62;;/Fpo2?KaF:_ 093cHn6;{1gLRTYu-m}uSKsMP?):mac,,(Cwz vE(xE]J?e܄ -%4cjf,ќԸ, lc {LJ}x`7wӺR%ީ#55Tn8#KadiCek/bXzk0m8n辒v8esQi{H* 8'=:h jRl5c aͭS?`BkkoMe8FyGŹF9C[WA2ԭLcuuwia_/A]5 9_b_:=BQGMOȼ8}Gf:#8kK6R> FM aKͶ3aL9[kf'tW؄XXfMIܷV ,'֐馞k!:˜Li(5W'СC;,eh+d#bxŀVhkWom7^#cpUZMU~U6.w:"3#8Dx]M^7yA`\r(T3ݑV_=,Fej_J$|͂6;&&iҳ9Hi`MXb2s=i}}uq7ɬqsS-eNĘ=3dz fq!6]]DiCؗԾS퀒O} QplX XOYN"pK:8# R议*{YjX=v$OZ gϝf=Ls{&E6#a+lDޢ ow"Y?Rm5X@ɣG:Iaw\"X_1I5|m-mj/0hp#.;yQage]PYxڳ0a=#8P"\;@ٻO&vq.^wG. ǏܙG.ijq.Atc C8]f Z@Hc.҃^Vmԁ)q%O8xo aۤQ% ~CG$.66x,pǁm;**8sǏEd@\h,MyG+|+pG`!Yx`ʃ Td;kȋW'O4ǜ曚M҄dSCXMK-=p֊Vger~(VoZN ҭoU;2s|'Lmӟ Խa-"o6)EFnJ1ا`xg}s> _`ٮ$h_Xn8#ךݭvEX=,]cU&$h{ unqKLNU%? {{kF4}S/ ,W%؃ھ5ٯYK:򌑣3tNMg}#gAU}c P XÜur/@CGp &v:\\h^as8[1ձ ;2cJ|VUS}Y_^~!SI)9kvm" 0qVk&4SRɶxO^?<b?!r.,-U*uD*q#8FbFFȮ6;9>5e X\8g/,c:nHPѤصZ<;"arwJ~h 3ZCkuRJh/ L1e=F? k{{=DI2e`g 1v<ɆQթ?v|'kf1i.yۜ5X;_X7t8#0S8Kv6-dE~&|[a|sՖp4t[ҡm>7Y+&=:g:Gl_+-v&[UBub~p-HŀFXZm:ur/,OĿTxPj| M[] 9/lpG`xx{x#. wb'iWcLrp!Ũݞr7lk܃E'w̥uD#k9ۖZn\b!VR>#_X GpzHwW~dÄS7w:kxU^[y=ޭ|nD\zsPXk~vsNXRjy' =q!Kë*m'6q;'2)mj!(i=J_07wGit&]fz; LO IΚsٿAk{uW,Urk"XҚժy$o4VUW^M, lwnll87BfmS=Fe*

Gs@F9ۃs(]/%V P&mgHڱ_2wpGӬD0zsvpxΜG|h=[9;e=S \61gj v1};1ab0*z.9bMm=baND"GniS}lu+)|:}BqG6"G'%Μ]P{~LX=rX^RD%э4{I(%ڦPIy% 9Y ýTZV֓rRxY5J O}-cYT}Oy;_4&|l,#;Qk#'=tLlwʪ(pa߳_Sٶ>;o ;`n0Mҫ~N5H\8#$p]&;Z;Vi.mU%+hH}jzأ w`M@!5mI u(q3mEeBۍ2^ٷ1l`1m·tkp9I1b@ KpXWZ[So:߁ڟ.b[>jv;uzBzˍGpv$[ls=gٶL(ͺ۩4sFNqr{QsD/O ةM:m=us?AmIV<8@)[kU#c%,֭ؒn|wF_ wI MHa 9u6/lB8#PdV[勭=e&;tV l2z;/CdySYђ1Sy/t ڑ&h:S_Eaςxl,,()>CjU|7vމcw@Ҩ.W9[9 pGp#?NnP<1UM(.0$_#}q!*#B֏bY#E?4-@նn=:'۟(&\0HAL(׺Tl?`gR6_cX5EsaVGxq6/lC8#8CgݜqQ]fU?25l9}w-ӓ^& {q{D[U1EG O(b iuǪvr~%}`"$@c 1Zj;cD\@IDAToGS} *MFRge;5E;|` $.pGp^3QclIn\%6;OL$ZSl`}؇܏Ѝ!J)Cq0ZO?‡ c2S}bNz,{'d 2ځB^dy6Qc 5tegv@;t\8#8=!`'=91g۠-&iv>׹W#9)ѫ^c۵Y}ZIJ".6_?EƧ|+(TU&I9B6OB؛7 96yJŤ8]Fv;#8F9pn&M@tx:Dbsu_r{$'kjySJYk3mTO;Kqrlc_}CrՉ }Roi;_66OU!.#nU}=ss~=_/4!M]!֩#Ln8#8;;h2&hP:X}Jel Ѧc[1Ź=]9l<#EpGpENmczYַЎMt HD4 "dZvhKk[֞qA/2Ɓ,aui_s}aOJNS;{yMɆ< 9HLKZgMdK^~΂ fۀ`#H$PhKX)Ep!D+hK&EA "DI fpv۷WTS[WuWW=w'O=o֩Stю``O@]y5x#^U;qYIf<%5"-i^kZiӌ1y1nMZa:G덢Ҷ4rLcʱůćFuG7GY<C.S@l ă a|Ud¥wq#IY9|D-C?2r |'@Ca^:Wz#iO_Ѓ5{*=<=a&?46lA@D╩.' xN.]$h5Oc./?=@`VHbb5ږnOq]y?McJ˵kb撚H7/G3heNt،BJuΕh;9Lmc{q{mTn;-s)喕j:ۜvO#J.{N|{FD3mbi(VlA@8E~z:h\ҍ7Q,7NVhЪ^eyk>Gw\}jc,e\m%^#'jA7vn0+|em\/cu{.mhCp @&!PM>IIr6yASe(A r]z3jѢf`˚͸2Ŋs{8~e.5t[:5vFˢd:Jtݧ˲8ov;ǜ9J۰Xrl, !@'%/Uוe.ՇuGdph淈s:9itbYj|h(ˮ/w{ұll⛆˶^#[mlaV*-qqTuNx_9|.|\l&]vݿ5YS!6QP @`3lKykkz9Q,wr<N"RYw\=he]1uٕwv9q|G\S«˶ꑅٮ,}$8F*^}᏿{s ʐ D V麮RkWۿl[~J͵6xyx0ӘD>qi||u~%-쳌I{Njkmx3>=#EbXG -/1LR>/|%@}ߛMKbݝlvh1H H: @c蠋mʫԲN%<֥;/G9yӼɤIoͶmqFh%Dݸqkq1Ƭy.5kk2ފVJQb;Mmq7?{FA}L}Ha[~i AlcJ~<zw,6KU:PMW2h@ 4*ej뢹˧n|<<~M9F'&6 Rb}xhaOQsɶm6mg]hx-:M9q(166.zJ!sAs4TPj b8J ]ګ/ŋs_l25@d @8|j׶eiR~qfQa؋ޮ}$ 'gQճzĺ8Xjn{Nmn[A~k݌C06ǔ4ط`uo~_ mmZ d`s (WŷKnb8K$3M\?Lڀ*A,kqck:'1%GdY.;Hh$1G\ F\s.ƌV}q[/VMX/e5ؕ..և-%"mT!@8-ۺv]cq]xA.ݢ:߳WgWUގcQFxinSey7sYmzy#mUh~XlVݭ7/ujÊMm/96` Y(duNf=y9D>oٞ9Ӹ1Y0[XM^m֒tQAe<ڜF]Ќ"FN=6,w;.KVj\[M?-م[ޒc+ 0lZ]gI  ϴ!@" {Ǐs5{*߬WSNNɟ2n襧WPgq;lԗqz=6\v]3v@ק?j=5՘/;l^=FOqGr!wLSW6q@ΐ@SW`>\wi١xZ:fT :K,k{+'_=GƍQZۻ~=^5uԖ.\_ ]xx;O?ogutYP.86| 'лƉW^~F8kz>ؾ[*e^j6iJǢ>buVzMt1ۮ<j|m߿y,V^],v]eWٟ'@]I$eW1[ikiRgB5Ԝ0t%]+kiO7?洚FvRB~6NY;U3mƞ7ܛ+b-ռAr~`ѹ;wnth{77Wr덴ܗ7rֳϥ_f&ם!}]p2^ǺbJ_2b=7a#.Od=UTo^:wDE*>$GQ?OuAek\cL:n*χß5W_N奻[ɿmIxKN+%?> Ւ>ω~t7_=mlimJk>},?`ޜد˟մ?'Uj+j2p?U最lRGd ";+.΃7ݵ y.z{C~@ ΀|;|jx+ _K[uyh 9 &S>QXpq4qlֹm0Ʋpތ+2Trn[_Wa/Z9q[<\;tx,O_s\L!؅umr)!`C@)]ኺ6ִG}7?oK+Wɉ֣i5ϟ ʯU}v~*ĺVK\mhpעy>zY0֡\4kzKR6|w_n{ɴw習lo[_jKH_{>|J _B1'm;?$oI8hl8o`xH嗢ƺ@:8@<ʺK]R/]5d='C.9 %!&C(.tJLk׻+)ץi}95L]N#i/6wҝ/)\j<; o 4d#'z̐1$3x]|ս e*Q:K)(Ү58,Ggs%PVqNv[r`&MI.AÊ!p@5Gt!'tgƛ$D$(ݽ_Y$UC'%p|^7w(tGNWV[Lݗoܓ.|tK[_Q!cףnjńBmEQ: ㇘<ûRy~ԼM[/Ѧce86W'zKǷv#oX @}<8X/<0` )C |u_ѥJLWHޟ͠d}P?]4WCS 8:{qSQ'*ug;. .P{[g~/EgA'_vဵL2륺sb 8qGElGսzu{H^9mKyڦXyئ'@,m;Jy}59r~C#``dTB@~yN`t'ҕO|я7Ҫ觯ğE;\_{8G2]x]z# @ՙ)fe@0VKd/Fve{ K6Qt=2GRi,W&,n^rn{.59DJ>9In{nj>Y'x^oKvߛW;/ã,w`.}N[myNj3%v5e1m:b[>;- mQR,'&Ƞ4aeU꭛ (r$m{rҕ,3=@]h/+p?|ι}gWyfͳ1V;ߓG,F3ʤC(c6|^B)'ygmTZ]({klLVӬj}dz(}ԮSGҿo?'Ϗ[W~]H>&`8$ +Nr=X~OLc _h@;} g|i]IϤ|L/!:2cm@\X?yaqcݾ} r\O_M}~@e+m9mU&_?'t2GJ* 9e!We' tt{_TW>|x.1qW2 fO[9}`Lr%_2Tc'ӏ>(WgL3e(u;%kۧQ&Wz-\w]2t@4# zMKҕ}<]wz4.4CS 2:xG~_|I߻$b0΀Y$gYtWf@I^%;1ʊRzE;K\q;wơWk(Mk^W c`U86Ng@ J@IJLWm+inZY(͕Brb+?ߦyVL(F"?m4ɢz?7.qώU\Ayu~Gy%:GxźmG-cLDYK_3i9(t)~Fʽh1Ͱ藐ߗK8u @{ifKDZɿaѓO犵uh( t~L<G}?fs>7pҡ2ֻu why!0~I=|VԹ=z_*ӽrEz=ʘ1tm1,w >zml !CËa䲱.S!6QP @NNL.䁴v e68O 09\ɷ뽕~_p{Ze,ә6bpN &xFgV+[#ƱoW=c]#WM7rO!tc{<v WG68F'1@XR+ϕWҥd|[;٦m\ѴwuBp(rEַ7!-ta ~.Vb{:q_M/_dzV^%w_5T]cͽ9򳘣EcxR]'OA{ikW.&?DuUlߒ 0 m;HgvUbS1b&Ѧ=(Kj{GUu97|Ǟ۵Yei{۹-;좍];^w}\&ֹ ! v@I`}.면oˉYC`:niu}#]G>{_ǙBp'5~DŽf5uj_ԏVZٶ>bާ-Tƺ݃KU]>2ݏKٕ2(mK㖾/KoX}]Cz>HW҆!@@;J@|s,7?scu%g.fk<֕ &z:ee4^Ujuh< 2zLv1$/fb:8lSڕ~jw޹ @QOi6yiG{p-6S 0*Mo}25?W&"uA|轷Ǿk)mwf3Ɍƌĺ(tSuOE*z%]j3BQ&ؖ~QG}zfWseX1D(խ+NSu(ؖc{i,'!@` @D|5$hm/%`7emI t9m'WYMeMh;d#u',2jeͷfWy|*c]n;۵8lewGvwiUٶC 4@Xk>z$u{O@d@ɖۤt=Ih1u &c&~P9ߠ.5rC`ǕJv,UwYpnz۩{6hV?oUgG3o~F)G%O4چ| rb-ങ |Çj]p@%uq5[,"c-ݰcaU_&>ڷٌʫ^y~=cSwejVYM7Mzo([mH F @5+m])_V-rZ Zyk+Wb?/ɒa+Ti[<]NcY~[}~mPU=Z~|O4~(jchFYxLgAdYYx>tM/(mcEeO#(W/D]\gUd[l_2ۺ#4ʣrk-UPlja ohn&@8$YHUs Nlk5'˥zmtf@Lm5tۘZrմŕ]FbF~u펶doߪsp[zq@`&› VB8v~ 8#1 xP1cQwwGO\ L(۱ ZҫWZEW\6Q^k;Nq]uPdG~Q >b}!` ,2S 0翚^^e5@`tnwׂ Zª3 G2\BDSNIotYz5YO5\=KIdz8֫vYuBH H: o΂~ڿ߃[[gԧ-$SzR|oHReѭw{>K=3};zHh%g3a;j'f6Ԏ1U^>CvQu'嶸 z|)Q{7ng?m`Ç268 @o#} j` 0C{|3aO34$?Ɩ*jK[6{}14It^6m7fϦIzQclgrggg3k-e}҇6 ^)G\qq-̀@@ʷ|+'ADA*u&rL؎omdl{+>\ۢ1Ц[vMgDyf CivVN_6(h3J$f p/à p]]1JvV9}DXfF%gl?`sdA#-%ʞovR7ƥ֚+B8bҏutTv) Uee3Fer;ݏ :blŘ19F`<9-siOۨKdvͯyII+{;r$]hl,29@{?oIP<p2xA`t{ɩ˶He:fw8h~ba.-WcXbܨ'B:ݪ)kAIP]((W|m^{7v!066F 'p7'!z$ݖueR^,>fY7uܗn~wΓ,f>1KqЫY^r:\v[q[,I)}mcmdNmo䥯cJDZKZLgI˶-e#cz:TL |BX>ki ϧ_[dKY>4S'T<bWuڼ1փBWͥ6IjȩԫpݖT[}6*k+~MlGnu&Kc;?iT30>l̆+Q!_x=mL_UW:3d/{{9鿕BS=Nkc4]dzlG|G鄼Ouemc].;m2M9e1e*mw\ qgJbp̮ͩ Bc``,\CXێoz}Թp!uvS) o毦;'$puHʠL@Sί6Eq~mVoYDӚQɶWNujnGF{F}ۥm+vvyFU<0waY,H xPBXBOwl,'+WbK)C`#i/oO7TZ\6╽1uLcubg~ejM&yQc6F^t;1LJ뻒Rf?ۨm_:6깕 Yk?ݟJm9,0E8   ?t3:r;4vy/@`9?ȷܽv_Vn/@(P2ti/wY[ʶc,b)%+|v=Z&}]Ĵ,J}$\Jlc]:Wa'>CugQAw۲w=ZP0+ą p gn~)^|$UHWi:K7_Li5?PM'!R8e}~t۱n̺REmIuDzO6~''إ#m%vJxWgy_Kc2Su:@^n~?t X ݷZ&`uD.Kou3mzYzo۔pR]+k2:h[s;&ےYn_]Xѳ"n(lv|OM iXV @}ʿ_g9څiwY,˛\,3hn[O_M?JnJM[=Y)]dcqZ;δ̻1ζ]Ҟ%ʃbb}b(wiJK^#yexc[2Q 0mă HJg}='Zz7uZv5|۞L9Qmp@Ala⥴sڿnZ˿4D3| $ڸR)uAOm>o=鸣Y.e#YF9bJc6,wlʺmg 6ƌ[6#(4&ZlA @]J}}ʯrZȬ>`Ma+J@O _+͙fl?'wk_i55{~עMYW\ǶڲmS[,ed`fXD;KFmҎv;DGPNk5YzQe6ҹm1~-I i$ E&;Ͽ^'8Ƌ|<]7|#fT`n#W晞u'^JzΫ[]:mL(_ ֻ6ն]YJCkHky+=ד{2~lgɶKM^>@k'햻tBeoSu.]~ٯmM @`JH@Xx9YRo^_i[i'S'|y7?5P]jg1'x%o~/WV66+K=^=P ddټ礲&n;llǺT6\bXŲ(\e+h[l8'ղ,ev+Fa[@IDAT]GGu)!0MlL& ,:<`Ts{Iky|⯥KdZ6cW[΅A@t{F}akg'r/OR^79DY'?m'Yԫu밼ZlĪ䥶ٸnc{D:&]eWdl>$'?F9  +tSsg ]O~o{kk݋Yb@Q)最s?'Ms~c3~Vڳg4,m}ۚ'<(:t;|Fz[fk]}M8KRz鰬.Sq9NI|jk MZ `l% F@W*@trVݜjo |{Z\vOZ{-S|ٻMzHw/t<^h #s=vKs9r|^y%~녴~>ϟN[_|~_vsʸGh2\m{l[+ef&YD9f=kcǵ3 ٩W>6fEy櫗g>淚7ݟ?sjZ>Yc1Wv)sX>ϟq3m}Oʹn}n&'d1$sۥݬ~˦ۯV:v-,poigb]QbX}[%-3 pz'wݴCywZtϤMk;Un IԱ隣[6wެZ9-q+V*&LF?ys2oϋ.ayk9k_}9[icLSb}tCoonuy(*ֻtX%Xf;){ɡvYϬU&֕hC l F#$GkJ77_M tڛߖ6y$\~oZrKsbSoOh.M ꔒ&յ5ݞ8 oSp rk35:wϬh;f WS~{/'wyvrw_j?N{J}KSfjjb*)$Z2}mu[>}%%o7Wc[NJ Ujﲶ%yvgS+7O>6|*?#౴viGz})Sss%\|"{Z{S;i{^цOAcPeaڼK=Rkw,ݗvt[}jhٕh68>ڕ1"73ڑQfXD``֜C8=eRy7ʳ_˟_ʿ~!ʥMuv}*_\V{Ssn>Lf5w?ؽ3AcQGHzo 9aʟOc<,,cV~p믤w"od}f o?ؾn391~-\}2>eM|ǂ|[4~Sڸ4ky.ޝg Amn*a{ە>v6ARڶḳ~nlC ,6k- !r a'~;q0n}tn\{i?pzB$-Vqo~fE4Û>SLB K5('|4Xo-~Zۥt:6E{q~UG[Xf~j&}P lp̒eĆ3%Gw k+|5©fcJe]}A _lߵc @`(%pNlxֻ,pR^G+ƸAde!.mo/{l,!@XJ%9\[a2mcyQmtYJV/}JwZ o1q3b@ 11I,]eWNOxvQWe=oA<#emeo1#o'0G@- @8wN9 )HnHo^V1^_ۭ٦>J 2lcm䭶lK_D]/e! Wc @Aɚ;8I,m2F. k=qmGȻ^Cee嶏0mcL(!%: @8ԠcXʭ\uC:>Ʋmծlx=8u.2me6   ':@ۻCO+Ylw-˸j5aJqMUȹqwVbS[t\}~~|׀@ 3'e(uԒAcY}qT/J}_iwb|"kٺe;ښm\&\/cJn|]]ծ > j @`x Y\~]ek$2zi{moS6y-NͶ6I^ŕ>mzljiJ@863A@@-4qJxe6H_:1ZY&~˶GQb̶m]~k_z"ϗ>6N9=B !0iadzWLmǍ2ڶ$懼Okҗs/yCum~ʾַMXy5Yh+| @5b" Jhf' cǒcQ6e*K[u۱$m)cqa{}Oc88k @MH2N.X&Q/_RZL\8نrt^a+[>_zq]F83N2nL!0.6%= @XBJ\D&&?喩,㏪v&?>!iT>عOږ>Uv,Jeм}57'"PkI+;zwP;  [y.ۃ|A`Eb@ LD qT˶>GźmqXfhc&w2ڨmrܲt >lb^r -hC`&MbA MLIfyLjIqMou8vj[.ڕ/ec8QVƠ i``4@ 0'@1s(UweS,ei?l ǥu:Ko2wXFg2֩u66Mx @'&0J"m<,zۗrU6ʨ'r>2F(wuucSh:fE Y%. @PN bTk:>nKW٧ewYYN9^ke^ZcWi3,1VLʺ%6fI @`If)'&C5_'YQ'_K.K}McFYMn8e8j; I|"?b8K۔6Xqk1,?(!P`$B @`ldFAд%5m><ɶlKcRqMlG? >nhCx=b5mV)Niر&S @#PB3iRS,5 T/-cFjRKW;lvgth+| @(؎u#%eoAsLŗ+w0}r@5lԨ  @kĸ-\>޶j[6W-emw`Z-HXf @` %Q3ChgyLbݶnazQ^3bF XUf @hK$L:KH֫l~^8zCI pRC ̜@Lcgmr8 w[|t>u۹uEhce+un}Y>2o}=6fϘ @Έ9&y#垖ݖmbLQfyf,%Q @)Z*Yto>˺_1VQ& 0ԙ-\`.t nw@ (=6Xm%s.ymm}8n-fR}q{h2̱6odO;N9=B 8dlG[f&r'eb;ڔ1qi9k,bߠJ׭``RrA 'SV&[+UTw1N2(0]+壎 ;'/ @RP򦗓9m0l/m6o˥䱮L#V-F(ůfX㎡˲X?i{J̒Kl@ S%0i2U%nEh#^1u1e,TKM E۹,׫.|X!0)6&% @BI7MXubmlRWڸ]aK/$v[/κ2vi˱Ϛ,m|~n[O Y``t @ 9k!;8mCzNmdz.ƒ]mdQ8jQTq-o(곔q!IpzB , ZVʶ%.<j.mNxD-ee _6퓖W=n\;V]\TJn㔥i`4( @Bp"WNMn;ku.mbĺ1dzEW]:+c9mҶ.ݗ},wi؎FcoPwm|sL/edl*0@ sE9Dܮٖ6n|Rwɑ~Q}3l߱te[6Q[G,KѿKk-βQ2}4sq:x1l}SBpZ @` KJ}l۠/DOuLu$6ݟ}ʶq\ʾuZYs,h+ض}rݖ*]1e v' @=ϫl[>JRN$?emJ쫲<ܗþn[_+cr Qz-eW^zn~Ѷò}!p,' @Iu:bGʶ8NRml:Jǒ>m8+~jZz.b$S]eԷekX#G%Űg8p  @8q;YI2ZD5u.GcvQl{C_ExjmnjZ]28c]z.(㹌qiǑ؏:wj0@ '2. &.]Wel[&K^;b Fc]m{۹ض-GʸJǩޥlJA~J6Ίc좭ei{cf':A~#$;um#Jzǒ; 9NI @ӜW9J5 o|.;~~GTdo})SS寣ݕsۥt{P9v~Plt0-ā @@&u11~Ɗv$?cǘ̺QUƾ,w,K[-s)Y<]ԗ}]Kaҟ6Nwe @e8tjP,%zՎ(W-(srٗR&[Ŷ&9>jvd $, @sDIuLkZb[E]=1bQb /n?aJ׭eu!0UaL @8E$7&GY䶉rץkӗ1vzT|җlm_Ƴe J r @C%’G]ڥ,ƈچT.j1[ ?ۨv5}(!0x༮  @XjJ8|N 7ʼnpԷXA1j~і:w 1>@ $P&ӄķkkcrR6oT]//}b @A mhld_w就L?A`5aC @`I *U%]&DXf @E> Dc74gt88+Ǹ!@Lv=y[y׼qb}C @c'l $68 @ sGݒ19 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @` Oet:) @` pU3+ @ 䤶'rWSK 2=̀~ @E ZF@sF?ڲtQ߉?70!  hf) )%!wtH+M_h3tI+I+WtfwPMYצ R `kPBUޯ[wvꕋiw+]~'Yi o@k?_|!v3rf}hlՉ>j 3$§k@z_Nul{-]Gӕ̀hϧڇkSsZG滿/_]X] !@8lt 3&t-Krg+]OW?i㭏Ǟ̟p|ϼs^DViw{S.ߝn?Ly9?@QU~E+/ @M &N pO^wo?S>Rurº}7'n%<غ cʛ#zum\6~8?L[E{ɿXc,@NA> @`N(Cѧҕg>L~ 2?0'_t[wp0Q ̜1@ȷ7jҵd?`y@ D`sLNOWZjK)~10 *tGA4@zT "p~{ޗn7.~j:%J: ?oҊ_`&;?q?]n~3i֝~l4 @`&MbAt?tJ~ߠ9?Um 3+m= ݴwL6No] KLȟ7r/?o/4x[=p[͛s.W^ͧN~SiOמk~A8_5p7@``qזAXN\mk?tT}ݝ;rrqFPl ;3n ˿+is쵶 LWASH@83E򿲻>?iW66qfVtʕSߙV\m6^~5׻NA<) @`8 5| @`>j~܍=?="zE}ݝz4]O6_|c]kC 'gH@8տ{?WWҵOLGimO6InoH}1upJoz$]? Im:@o p~V}=\6Vwtҽ?ʿ=:tj` p֍!B#G"o1 dʹՍm'uJ@{O>:@Ns *  @}o|`/]{/$ͧ޸ohfkGN7ϥ{otG7|8 @_ p:+->Ϳxg] ${g,S/7?rbkޝn?3 8 @WIcΒn?OqNK~&՜Y.ϔ>u>N~=j]N]Ng@w, 3?@ DD6"~|S)R3duiY?]yw35w~ @``tVXB?{w&Guzeg@B,b;ŀ6xNlߛ;7_0 uGs:TK36/trDjU̞&zƾ hu"MڹKR[v$|t5X# p$IϦ+_d PƿF7KUˮxqΤK8^%`=DNEοD$lh9c` jDk"X ~ 0*`]m͗VV/%KXT"dɨjFm}D`4Q'MW^|)wg@-:eB!3K cms-)F"/\8XO D84i"irP@!(" B4@(/fkinjޓ=E{@1H^C/ P@bIX;Y"uң] 6حjw#;;+/=;Szt`o%=4=eWWvKJ޽lj~A[ۤm"g" .^&KϾ@.[>#6 j.h}zDݲXNokˊɂ I@fFy>Oآ˜GE(v^ͺ[#mIk}ҹ}lz)Y=GdSuz#[;˸ⱈ̙w|!סn-;|Wotxwzk_'A:A;Om6Z+Ir2.Pgk\0V UQ8@s6XciHB{XWuO6_AqIk{HdСmkπHS ,s:<@8,I)OʣvyJv<4ΑV.z圫(=,0rgom RQ,%%m[K+O־ s{ J gu%5ttm({P.x˻eoIs *lE -z Y0ɺN UC5FQ@fI,As@5qOHӹKYj#]S7ڧWӮo3[c_[LJ)`txAiKDē1}RB6-WmoHN*kcNҲ E,Z (-'-Z-sTl! pL@<EA+/N,7<;O.r!y;}Jv߬$dW {L,P[7[TlzW4vyU˥AMHX+r9lb:1qRtaX:@D{  VayʍTKmdjcē%׏}v?V<8M/szԭ~sȳLslnƴ n{,N>h P^? uwʵc9㲫, h1%=ՇMpi2e{v~@&@ l5Ny@j XL6JmXKkiG tһΝ~EץߠNeNhc7__!-|jxANLThщ/QibQdIiXyt:M^d@`*~*; .6}]kMͣy xgriv~]yJAt7:NwkA$]wVٽE'7f;_3q>ZҀSl^ė,P3q^D@|\9d )AJ5IYg8xmRԻ;C֫~yg?E)z]bt:U_4xLIlIڗdƗF>V|Ayl|'B @sV@ |->nR)j#Np{6V]!{^ک  x4&%:]ҝ>}ӟӞsu\Q婻~$a9 }+CTX6 B{B  a_0:eF,mixqGnm,߲Qd3kM'iKƱ5٢ vu 'scmҏk*,y z R+ ^ Z@lϗO6-6[}Gv[wE'K:0!)*'GҥK+â0Ѧ&i:}ArO}㔓 E@Xjr"hlE[ޑ܇!V=t5s. kG zre@IDATr`l[ߴCubΐ) JFvhCh dR8ΗH}y"" $I@NWl ?L4z'"y{>3bFit[`,Ƅr"۟}F?z$qX(PÙc"t @ LT۔@&f٣lna'9. <-@&'@`r^ 0rC+][.H9'>.`Kky9-Ȏg>Ђ tL f Η^u/~n#X%ܺ^]Ii#(F޲ @  x tF.8s Uֻ΁[y;:浫e4Ad{!_uh@mhT=4k Xt#N,#H* HIvî߹m3MP|;F`脆e հ jfl D_.ER`vm#޷KdQs.LE۟ӹ6G{Xk/^'k, I@j"DZ9FVXEЮW'˥ Kdy28[z<ڹMt?hO7ɸj66cuɑ%H@@`&o7@&-0r)eIܥwdo Ao@-@ BUg]ࣥrp^둘vsO i"э];Je@)@ F@ YοM耤Ʋss9es*-\&+'f @%@ PEf@"P>`/VN/:.>09FJ (MSr@&%` `krydRI%m Pn=RѼJcDl! z)( ' kz(oM! !d 0 _M9HR'Eh&VQ\9Xti_xTAmDۄй X@KXO@fQJnN)v5ݲ#>vvUlpN*?-dKˢN ve܁.VTДl" Pl) @F9/ kHrwY$9KYZ@w/ʐӛu 0%k8Or)uċo4l! 0i&  SnbVXe#xKeg:$Am kϧlU/Z~eh+@-w/ 7SMve_dL79 @3 P]> R ڠt'(K8[+ֈKo,=k歒hlv*6xᕯ/HaH4#KO#Yg@)_Aӻ[;̨0h@ot!o_x,a9= ,/9,9t9o&#Q{rЇI^?\#)h@*@ 5O@Y9 B1*{Kh#ںr~$Vgm\ tC.l)m׷i3eɧ: s#u`Oh([Z jil! 0)`@ T/u9esmYy `$7IN<\0>i.hOS Z뮗S^sHUzJw3Q\^j@B*@ O@Ypbz@l 3-xQȗz04Sθ⊕OdH dI쳙~PNK$Z0#(z9uVL$ku 5,@+!~kcb69q}LZt=s0 bIg_qUWKBhlwCYMmh1՞'Gnl! pw8@ T4 s40| ^͛'\Yvk]'ͳ֧AX<qz[87j8GlID_px6)I, Q@k2#U(Dlۥ8<,Eԉˎ,8~\?C̉Ο'uu:&OHL'fLfDr'EoM9+  hTnS)/倓M$h8s  o' ~(7,$sH"ܺg[cY;Nu%䜫;9:Ի)& ԖwC+UmG.1fET^e׍fIřahPr]%*cu`A@ |_Sb@:nb J3SN^":欕]e%'DN.N~m%r{> W`ZI рwlъ~" z)% 3ocwIv@?GCte^g]x;?I6ڠ ihIHPЗ/X_Q XT_Jl~S\EΉ,oQ+;T dصVnݠfBBO@ ]S`@Em`mnM7_o*gʺfb|VOWz6!Yߠ?6ؾMRC3zxiL5ң\.~o~q‰7r@'c6y|u1:q2v~$:% ` I@`2WQcgwm>`dvοҫm'|6+Q'.5L:U [":p@ Qe@ n,7IDhq5ui\zYPHʙ]%V's_&u}_v ұ:Q5m9ZJ/-BF__?MIs$H>gR-w e{%m?IGv @)* kJ\֭Ҩbm֮bˍ_w*Ww+V=*~y{ex@{$Jcۣ8b&t|z> $tv\׾UBnV4`3ہ=( agbd-3-@$@ LMY@X@[=^Nνi;ck,`sl>0.xŲ@';5WHҵ{;][:櫋kbbmvkZG ^:[,K;K,].ǝ|,=|9܋dFim[XB5u^ 26Q<6  ! Ҋ @U*am$e4s3t뺪pX\g/ߥr665+_#yZ6*C="$BHw;@ 0ڜ @ϷO^~B h}N/W0 iu.5PF@x  /l(ԹқݼD;ysҘE=-6BD :b[W$+e"|@!@jsN@W }ggdAvwbGYmURdYCk@Ɗ@:K1O` wuguyHQKR?3:?s7@* kGRfN933j5Su8HT{z{_U33zFjCAX@@pLL PU]^oOJ=wH@Ygb8k$Ԑ6 YoC@ҹ~+_*`A@ G#TC'H޻Nm F9Zd^~Az~C! 8CB*B3E@PJ@hm*q1i}{KOEu@A"xNGe >+Q^,  0y7 .61\E<, ^,6}hFd$?qլjC߂/>'= z\ ؉lS{ ௣_ ƿ-^6f3ΐg?|/`;F^"ٝ[ߑ%ƤU3 u$ > !k  OVI@g/ŴN ̹LA[|NwǷtGƿr?d<  TA9 PO:`\hOSnvO}c}3/^І?o AImH-7D@X:@) Gol?d^yߞ)Yϵč/jZݥ?=8j" &@YD 3"`]3YuwǷIoV4N `dD'-w :ۿn7|M|LR8:4C@D'$ P]N'1y{藥xL.LMi-nV~v X?wznXP&³H`3dw   @'4m8}<\]hsk| `;H4|odI|Uz~zgi?u8 U$N Pjv) 6k`X#UR쎭cʣ7جf q`LZ򃃒Zt۝5Ggzd   @xl?o ^tRKJy8SmG`!k[`F룠 XS_ /{gڈZ 6f(3$ @)= PE֔ڼU{|KDZڥ~ў (jAGoGhǤIf^}CL[?2RC@B@ 5&PDrG;2%\ y v9O pAՊSo/ ~LPLr#Y+jo 0#l  0cf@|#`]sN `Ft۽CO;miB6[ W{' xoO]n۪{Xy@R/\xD.64?@yq4 @i]mް^26KWRgX)O B%[_Ԟ`ý۳S_clѵ3)`?@@`2@@y\rVr)yz~\s4_}4\|R@ 6X\_^|NlŜֆ=?7kJ'O{d  ]oΆ PMKmMhwsy޽OwD:&=wD-N (Ut6͢nidwo'OWt@B;q6"+b@fKlIs@ؐ[~41IvFij)-kmnbW.wKwDQ%!Im{E֮;wzHi_lE3i@&@j@ڌn -[[$_H o3ϕK_4`KA#NWv= n1f3i_ζd$D :Ρ:  T_@ TE|:$i[2vH=w/ <}d`VaF]K@,@@|$` _;bhv`2jX$-]$JoJϺcFs9x]?k<+o vkwg[ws{%q = zTr:IbZCIyqW~@,@@|* H,<ےٹ]z{Hb&4qDnI_X_9.. HзEtwfOEp&>|In.-eXۧ?'ܞN ~ !? rѝyry[+ J$qyRyX^" "!:}~rHMg'RC25I RYm&ⰖW;wHv˒޲A|L4582Ϳ"0r  ;2 ;kƼ;JY{A-?qԟp$N])3Ζ%K%~Y}3"z o? X9|Ȫ[g@SG 8*vN{wI KUa *Q  Or Wk#vӸ^ mRu׭mj%P/}6JK*xz{̠ދvƽ;c%X i~Hr$ߵOÒ޻O J^7dvl۟vb'C :\av>I@fWzs6@ uw;n40iԎÒ۸Q^%,\(q+`-(MhH$ J=p g/Z>A$>g~炥>i6H^g\5 nvGD<ذo   # UpnW&LOzx\"Wk_Ƶ;x@',t9$qD36_ 66J'jԸa)v^g .5{}SӠ@9HP,9 0o@)5U@8/ {NˑȔ^c޺Yg)&xBbs% y脂N`dtԭ&v( 9/;%Tvt'o\@@@T   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !d   @@@B @ L@@@ 5   @)"   @@@T2ED@@@   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !d   @@@B @ L@@@ 5   @)"   @@@T2ED@@@   ! J   @@@@ ""TIoD!FO@j >F@_ ԟw6/ !#!@"@`*j|@jVȢ[n|C=#ԳH@ C@IgyRB nyI"  0]A 5#in?SIzËGB LGt. Ԕox\Oʔ/|֓H@/xH  xS]_')v{ ! 0]  x`-h*Yz$ Lxۙi" '~=K1\!ImxQ{XzqH:L8ws>i8,O ٬ǿ' 3'@`lI@h,A*DYbM͒<~ML_C9d"-G:}C6>D@ C-@;_Neλ-&ك?œH@ 0¤ j$O;3~*|S,Kz)Ѹ7# ^xNzsT  ( \K^+Ѷ9-@5{V}N *=+0 !^@ૐ ]z4^gHHY_\Yz$ l e΁ z7&$IrbcY@E@!UX"zvR8tгH@ 0[Ҝ@ P_x'$IR[_}ӓH@ 0@ ,w ]Ev<|γH@ 0ڜ @ BAy*ef,6ϲ/dhcGB ̶| ZzCWjx;KN;ӓl)$-A@Z%y@B#_ՑV͞7,=ANAs x H›҃JI[ټѳH@j v p~@D`$IZC{  ~  Su,yq=  #  ` o<~=ID@?yA@I ԟw]Io/8y, ԖڪOJ @,6s%Y#!@$@OA^@@`Rz4v椾3AOO1@W!@@ VYя{V_vy ! 7~ $qMo{Lr;_=a| @< HB~gH.Yz$ V L(`/$Mxd>||c@@VF@ -\'_ @!'  !w  P)H›rϴ}%k E@Pj|" ?=vE" A@j<" '>tg?R,=B@^C@7}Bb h Y-}IZ$ @P' !h8bi w_KēH@N]S@B)PDd᭷yV;G2_,=B@ (RS@ Ұ OJ푮'i A # H * >vg%礠A@(@ N@@G?.9vxw=ID@ yF@ WȜg%$  Vayʍ |R'd٧z ! PjV) > xhR_IDATԖMr$-A@rR6@|(_X}FrH>Yz$ @ ՚\ TOHޓ^zqO"@j]@0C@G Fڮ}'9ʧSΓH@0C-SF@| PFe᭷yC_OYz$ @ | D]SO$7mڗ4*ǷJrW $l~I[#? yZ"/@ yD@ 6Yћ=i44=?$.q}Y&׷Jn?d< ԰Cjr) )c7Kml&tŲk?ek<@[7 @$NY!soϕ?%)`Y@ \@Ƣ[nZ1'Q dLV@@07I.}@uo  @8@&C>@`RdA@`&f&TI@ D!*5ZW'6  3!@`&TI@(mtt/Ҙn>'kI ͙@p W}SZ@`V C>_~iA)@ @TYd@{Ywv$@P ٬zR_E$zi6kߋ`Z̢g @ LT۔exZM{e  ,M 2Sʶu>?ߘwAHo*=?~N@"@ E6@ '$3wg$}ˤdן(1@ RQd@nn{/SCJ_t<!tmzd@ }? Pٝd&W^#-W_+ g'y $"LZz3F\C)u0@^'vH߽w%"ʼng?  @$` Gp^G:ς'^}㈏BV@j[!]@@@G   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !d   @@@B @ L@@@ 5   @)"   @@@T2ED@@@   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !I8c9@@/Y!YD   TO@93   &@`֨9    P={Ό   5?|S@@@* vkjFQG  pv19 TUU3:_>SNU @@Tw|R\1[fKv3[rJ  z=;לR    ڄ=G/sn c߻Y#  cv߻os8LaP_Fb  @[:T={%0{A=]WrDەdz  &0וݔ}1G2g~w=&I"  Qܿ'o鳱>dBV3P\ӑ$  uݡ ޴SKc?F@@ m=c9xWnX >]"p  @I T]Tmƾwh='J Њl헃}n}|r @@jIvrc>Rct P 2SW T^cؗl{t\_MjW]{}^Fe~g]-n:G;@@04c+cw:/[Y} ks8wmm[Tn>gƂ/ 1nz2WF_n@ ][n]u嶾uw~7ُ  .Xmoc6+׶=ek=McZ91o pTG% 6nW2_Zqm̬q? tu@dA@@sFmەyv_vw򎿛mDzF`\㲄z2qrqUmxwhkf5m{yǮ;cy  toԉʵmrYxǎgwサ,8*`|:Һ_y 5ˎwW~?v@@iw=lkǏM,LJj`bGz?/}azrTm@@&#`NT~nz ue}_xnhcg"!i~au7)klC߽- ;o-ʗul2޾@@ ߫c}'zcaᄋ5 G pD>, /ʵm/.k۶}{7M[}m~  ̎M[[۾Weÿrv?m[ƮK{Ga 1ƀ_5_,ֶmwݵҰcl=KwߖK{  g|nH/_vsrUYqlH@*(E_5+vﹿ`엖{ݶ-ncm]ҷ[޶mY@@{~xk6cv2M7qgzC"@ $A1헆v{vi}ʽ}ݵm  L T l|nhFt+Өg۶+'/mP] mmk=ھWy\{۶  TG`ˁo[0sg~[E\56Gp`'rO~5z hOuKۊ91 @KLc\ >s8$CLT/^X*N诂֖| +3ƕ[91 @W 9݊3/6A{߬[U<[tI?\urI_4:H ){[^=Wqm @W:䎶>կ-~%# KdUlg,/~k^>5^qmcs_??u  @\'N[K&XN!s @xGUaǶw+Isb? +Y1cxG} @fx,sӎ$_\G|aUua>='{>Z{Cr @x}Uk[i A`%*ȏ9c9\2GK @,<6sQ /}YYS9y @)*1z^([c{gqϾ{k'@ h#j*_[}]\ssx.io @8Ѣ|o޽g2q.'P`&)=?{:_ @ Ppy8 w؟ @; -{GdEٓ/ӭ/r  @>T"P+h+W{f!G ,_yk~*XH>Xs`P @|#oy.Jav{q3]nV @%#SX-zg< @N@[ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ TIENDB`ic11݉PNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIDATX W]lTE>3}66mZ x0&&@H FM/%Q!Ѥ[ JՔЦ*ۦUiS-޽w8gn۝̽sfΜ H&y￳OV>H!42`Ym| ,SӧSųĽiYy.ߵ4^_ T -42BtlULjԱcÇMb4/f2\뒶"ZꌱPmfKWD:: bt1ku9:8|:-#Vȍ==bJ Br|'8\'zL-q{Lopdi B=P)ʱ: OO5ܱ,2TD45-tzpu-+4.?q3fcRҖ›rф-LeM2-~mʮfߠ(:oL4o'Ns߆WBljﹹ}hnR'ʲphpk7O^cP$08PkhB?EyRHYɓ8tU`lX/]7{/<(%$C2mq:qY-fs-~DLT8+ ҰX,&5Ytn L'8)Q)EeǯeӢPs/T%鑽ρ< n-w\K19P/|C L?k. 6*'un'z:ְZ- L} Fvmsj}-7_H30{k^ MN@w;RZ' 5PLLCSyzOL;a[P}%`Z J @`׽!doc1Xm_ TPiw)iF׏-{Yu:TDžHؾ `]r: 翂ě03އmX4H<|Yɦ 0mxGdpJVVR&[2gL{y x@)3X~ڶ@7j+o J1Y(,0fgVd vQ[ܾP=*#t,IENDB`ic12PNG  IHDR@@iq iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o &IDATxZ pTWcw< Jx)EhEAuСiqqZLGî0;v:3N[vB} EұX+K-oJ$GH~9lnMޠ;ss;9?ACLmk՚f>y5[":lSh]嶓E24n)|Mw|qX:X,4{ڔߓhnnQ.5n-{ȱl54k6F֙I3U;mBԿk!$6lfL4J{@e,L2¶:UܾW_^D! D'H3uRee'`Pq|<&"ED^]ŁlWbYu)ɼ(^]K_k{PT(J{eB{B<5Jbw~dYUNhGX4gW.'@DD55ί_p~/lmz ~_o=O'H >(Tyc51x4ߥ4SZ Y'#00詋uB<'0YYvi70XB!177i)A@z"#BV&T~a\-x|T BQ8i$ayr:xm d2dbĦ99캚+L֐QTyT`8< XM&wԮ[gde}KhNK*|wy #ul=I(#-X,:TՊB&Ct*T3nf֒xA җEج94hTKlu tR-75$q]LtEy@3vpgoY+Dyd4򮻩F/kƥvT:{.M^5nyY4`7YjN۶*lmy)j=.J) 5KdygYTGT8 yM bX#2t}Tشohܱu"V46,4f,ZS)B!f E3X""Hdq·OۚM:xہ7`+ ~)X{%7~VνdcRKEB ̂_~j_Ed+ nW<&VELynNԉν{#Ѽ}+~' F-cye`>!:K}}nP e:_+˺ά{!1H<ⴗw 6d8F^ᡂ8]CEA DXHހ^pCP`(1mIENDB`ic13AVPNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o+zG vq\?A[mF7xಝ`;e|vtL&ӄ1c6-m vɴHmX,3X`ނO_ۿN+8ի^BٲT`kz @;L6kdxuO~K.{.UVkNc姥,J FvbfE;gfK Au~:ÛAf Ĉ( 1-JO>gdžt?KVHQU^u,Nk6@ "x0mӏ=8ZI3 p Fԓb k^?^o;OQ 1#d4H&o}##kyI fv0=mG;Gn_1?0/۝cxuÂ|t#?pDbz렴3Jij sG.lJ] ٲd^, ;_) iN :n_K.z0aH Qv2|c,ӸQ\l\[͂ gGڀà ದCP >O+.UGh,>u&ʨ~m)zP偖pT89C>u:Pkx0HGS%Dpݹn}E7 w/e 8c g+bFd#'OX.ʩ D^3!j>SV_&Lyl랺' b5Nlg'=PuJFBj]fZr#0/\NA_LTRyYh|u*Lo8m}[ ,{3,2^l\wُ/=ǍAA.d 8l5 !ab81rSSZG$z{jL n&+6G,qxb ca\=}.di9;2~\V?P! in sD$wɷ?$\gƪD' aoܮ47SsfNƣlQS À6-q]qa)k8 A鬢e+F/Ruԅq4ήxMT$+vz~\!B~;WˣC=N7[^H>^ c _,]#+ƈBH)o,Afk$HrF ý|(L\/th3Jl;oK=Q}Q:>뇈 6 mEr֬Yxv@Vk@--j>:# ?Zh>[dvr"u<upzZbj喵O# @Ţ^ $D4X5pB`;V>Wewbα  %<1jr<]<͂`Y6Vh>y3S6/첷H85%2_4f8<cy.]B) ơ̗g`ipNbUed|yA:0kXqK>HU T`iJ7~a(ׄ /ey _+"FQLj6^@ k]b}O <`@UZ@@sȹE@O\%E82ͭuz@nP0qu{ɋC(~ nep> ,Yvv%?&_*E>LW݂EY5WLH0j^ʂ6Gke&zYgИ_ UiVG:(zij&0Y>R}:@\/@$&{­/8m e!(+P$k }Wuܞq<7N:ho*(c C&}491?^y҆T-/5vW@=k .'2ȹp\2tK-CۦJҏze*;#~r[eI=2\f^@P<ў^$[ ,ؿgN \[6aX +<َN] =81`B,7!dA=yhyNḎSpScוq= 6sO mmOLa fx?N|ͫ.\4tgp|fUPjFbز גgy$A'w زzoHqbnSO(hwcDFQyM0ܶ sگc `0d9Ci Ë~sy퇿ҋ5%$CH٠31*}^^T 3 `=?&l;-7,o˰-' ~`mᦵ%~H=^#]pE*osNb_RZP`^u/<`J6+a׶ 3Qլ4x>w6a? =9լ_}N vkt>8'åkx?q &B1 ~^qMA!#OI|{V >ÉCNGIΞ !64Q6ʶ/Hhňa,F!zghΦi=z(9Ώ2xޫt*Ga)ɄFazGB*67sk[}&5 UtSTn[B]k\|C燺jU8es 2^ҏi]i~#>Z*@D0eLFt$U!a&@A`t`-W?8jl zH BT -)Es$IL/ `^[9k5 !0@3Fu:%tՙT h5HD#xzgտ]3Uc#C捲Ռ5VHD!%h3:c*ӣ.TNPGSYi-3gfK ,#޳SzzzT އ@2 ǹ> l-APfd ̥@0NU,}$yG-[UcC$F -ԗa/[r{O?KM{+T'c,vR k`Uf3c*ѩ[7]?EW)%Uv ~:[>հ롪+1u~et@Hүæc!~A,[Am5͊!3@Fك\0 yȌWF|ӏNNh  2*U={ @H}yo`<s yV@^ \םJ*+ز38C`D[[FZNP ? K͟=Pqn݂3c&0!S& a88" 1fO M /%4`gZuFb4z:Dswxctھ@0fx'%DA·nj99: UfF;̋=:);rEg,DB,Q덒 oQW#QV1Zuo=:pj;8q@Hx@ohA6_(@ K|Or w@x /*1cv8?$>y ?8qTy ȯ5u_= $ ϧxpz싟q_"ZbX-Z}4tYo-?x!@9cR (сtӧN95.H.Q7}{уWyvq3g%%~ؼӧ#ر%Պ;Y{J48JM⚖\ÎgV]9<q>nL[α ͞4l':(P)C!ィ6JIu|A!Hcv;n'ʦqjb@#~v|krnƳz)9!;+xBBCD@fp=k Dn@l&;xxrL]`{Մ O=址v{Ox.to" ʧ;Nsewi NRIBm9c` e2:gQD2{irtmdԒjGa<%[!eWwR~>⨫ܦ0hAR*` aze~szl^lB BA"bY=38]yl?a֖ytXz`y𰊧jwox=;Ⱥ5j. 5Gp[#kεe8ݤ;tא%I5EPCk<^ '$ ]!/NxFnXM޸L(]AjA,u $UkӻTzFK;t#Ύ}:xA΀NA.#fUd:E6x )8+?4aȐ,ny@kf #|?7??O*޹&@ OX&y۵_+#U+/ylX{6ظy8xs: "<+xsr (*2ܢ~*X7瑢,8jFv޺zݺ~"!9`ݝSLh~1cET9`?} `G+`B^"`Pث`̧U <=|x+N;锩S6ά^n$e7sBhZ~BMs%`4bDfXsc{S!l|ր[֮M|hpZ@0~ )TwTLr<3Z7 Hw(~9GU-kc{w~vj<m3.OΫ5_!8e~"e=u+O5 ,HGf%@?vҠg9ǎlׇ!/ YI^ ќo N@SvQ\NUE(ʯ nc i>M.' u0e)o bZӢD@evkkJFhi\ة ^Fg Y& Byrɓ _Y(|>pNjVk=£b 8멼W`s9(Hqۅϟ7ܾ3jނ"V4ӻ0FjP CZP%?%H(GiFG~b&< 3bbPPPsVc<9Tj׳ݔ1d^A`{v}1l6u` j RY$ަm%Gk;U-v]޸w=˗\a8y yt̋>o!eqRY$ƎuRlׁY TSYz-aUƒP8[@`ٷJL<"Úk(N^PsGk ӧUǨ<xf%ežCIs_Sze?O|[UzV5/ԑ 9yv|ck#-*eJ{TuL{^;<|0n 6"D##/6Wo=UӁFY8#EKyBf;՞~V}8)Ń6r9:YK&MZ֕R Tc+"/H}#KO;J-= 8K8}`zKbwǼ|L W]һw (Ux6~Qtb>hB\ Lp/sPCsԚ ѻs\u_x !j{DzC|lɪU7dzK׀1FыyI,X`[p!,J35PSsV.,U_JOKj$'G<խzwVqf/ q )Ѫe.k504=] :j` 0Ҧ$4'}Yy_tK}y<x#;Rvjs i>k3)mfƇ%UMUvj@݋MOJ>(ӆO_-D6]5A]ȷ2 1NiFN @:t-?N\;jtvV] Ɲy0`\@|vX-q M%4܅K̘]YGGR=HeإYcڧ-A]&50*H!fEΌZLK—;1e|kPWAfL>V6G.WP[mFC@2J;7?3@7*mٶIEמ{GmZW]+r5UY 8FT^M%Ԝ ;#M+~; pkV<ҭZ!_M Mފͷ%di;{r,Ni`0@L :~% ͵=Ձ>^\*I ]:2!QƎ)f843LCcŗ\n~P'}Ȭr  y򚩪o$_vC*3\yȠw-oyCAu 8-HxkGTirl =pO>C[ztb@w*1n&#| _AĹz֜vX&T=;Ԟ|/RF`7]Lg وہ1i÷Fi5mNg! xe:r%i!NðZP]+N' x,[ߠFvYjlR=C'H5k)ܮL5$s#292M+ZDך[ rtL:D 4imiK,$8!XHkK[b, uFZ[ 8f u#h$4ҖX1 HpCD@#- ֖Y@c"i %pQG,H Hh-cX:bFZ@@#-m:ub΢Fbm$4m%&ћ+^Q*.Cte72-OxO}Ej/WVXKdѷoStʎ).- *=Q r/touYꄇתIgm7 Hh%u$_k}Ƶ{2(S H#Gy K_Œ5ځWA;oJI}Y@@ uXv$ܲJŧ>Ff;Tg`# _;3.~ֵoQ_Uj$B7>1uu?9s,6i:_EVXM%j>R_%ũ@9Bݕ cXS.7MMUg(xGȰNT*Z cN&ݱۚ~3tW^ZGUN~3-Ϩeg. jƂE*C qA3ϑ }s @Ft5yQ:!2:~N?m7}I 㴆0J2GX/wݯLԡGAG6 2AҴ҆Nb*FUTi*ugbgg~r~U Ou*upf~SS0׈G5c:ȓʌ% 4EuW6tq&ꉩOjh` Z~bA$8SK!q?P4fNꉫ]q~; MHp@Ԃ<_CO~XMYA<:=|"b)?Q HpcXٴrm;~LJ 1T@LxbPu- rZNG mXq ʌEr, >kaJ9SDQ"~lNێfv.GO b]G7 V2t@Z@}7vzW:K._~)3f3fE YZ/ ;nAQ%ŤH],0iZ/?3/BLe>6<2Q|l=uw\ H+okmQ3ZuT%;KZ_y(pZ͙μo;C/hrPES}- ^:9‹#ˊy?OՉ^0?͌ۇӛ ܠ-HM4 J(@Gڡ?>Fmt5OJ_, Ӆ $B,u.=UNZ@RRX{x/W<:jcbȲ>@Lܤ]R?ć@᷈羶^yIϯP].Q}׼^Ru.ZzŢebNOitk[}*N iŌH_G-Bh,z`22bY@Fu=JSյtR*;YG5uZ|tu>PTܰ0_@^k:L ?RtێX jA&$L~ Z@@Y)01YR,j fX`bX@gK@$Eba e/B|d|T㟣{kMjSy{Tj㲹 I~_Uzn$4%7O;MzJ-=NmyU%ZVj߭^M/C@xJ1N*p]W[hC<>L>E>BRR(=eX(٘D(j2m5ut}߾=ϪP׎3]hlfxZ@@c]qkxV]thOWzfxH~Dx`M?Qr":I Hp/kB i?Qg/ |8}8)c EYTsPVLD wQ&*T?)@pr]b eʅ!||N=ܵwu4u%wk_v', Ii:g/%=tU`'7- 37:;?k~|8)i/jzp}jM0ue׽w n;~`]2U}j[U_Mބג@AWw;l wN5nckv?8]Ho?/;g ܵ7Uk3ZxISsSWcpq#!V֟k{H~Gh],ǖ7yעja b#dvmnA=wwD#8۔(ZZ?lTe'ְ,F?%M tKƎڷokg4ş~{f98o w; .^_QP4lYlqB߁[=6,к@]C5_poI, O7^ ~[/_{ JMNnSfL >')M/8{ jL- p ˏypopC*3(I[2h~w~[B_ h Hhys^ښ]|b7ԛ³z:p2pKМw7Vn|JͨV`Ԛ(o} uy(U/zYJÇ}FZk㽵4}[-EY@jdZ':cWl|*k*gb՟A;Y@0꠭5[,/#CN PY'L$Պ=XRҴ)*55J2pk{LK 8c VJR@pr\(! h' `LclcL͟>wD[ vrAwRI@,-}68m֘8t SSuh+:*bq@ g#T#l­ph6&bsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙYRU6ݳ0 첣E40Q\B%FE?*$(( *6M@6f٘fߚs]]Z۫ߝϛ{sﭮs֭[zeLff!M 򑺵 AdSM7^33<$I:]#sEZc`TlCG34+iH{yreC jb$ k\}~t'vT%Q)K68M%C w*=^̡c(w-uf1Z[KOflmu,4>pY`R8S.EM,k.Qr05,4G@Vc\-sWrt) -k$U[T=2LW Sc,,_e! Nh)+@< 8X3ye*g=f%lR1rtF6)esk Zufr*h:P䅀 Yc @mNUڥ.!ui{̸ki_ (D3\_ ;oi[G,407~7B@5TBM.0;9M7TT-(r!l|6졳3;Q[b*O#S+qmaiw9゛iUo6$*UfN6K8M? >4J@N?,#@/mq~! PRXClxe 1Sҭ5Z*v5췄řqv6k/_ݱet@h*&ip"qh+:_sRlOӶVI\Z[KH>g%Ne$\YVV QUxzB% :>=W8GΡƺ4+}4z8?'}Cjx;vWi16f ?{$84C@dRd+Rsr>GiyxCVOit.7Y*sZ.a57/Ӟdpj M3|ONlP)='|[h+ 7*pa@˨Um߼>T%f*21HPm#(8Ka+S{Q;ҳPt^j4BnׂmظIuh;Σ#t_dTjXM#Wb= ?߰hb 7Rqxƞ_E>3/,rdh{Lf q= 1o|J331lCbMOu?fXx90kXY*Y 2 HܐM.û77ZУS*S+tNѼ>ȋ.YF]Q"}x]*2*OGc?HzCCTIf,W|-ĉ+՗cb(#M35\Ip4ySV۪~9),L*5%:nT&h1W@=RR+TJ*W-'d0'Tߗ m^K~ ~4-d%m4Rnh=C3^NGoEՆFT3 ׵{RH4mNլ2S1fJg~`07ayoT%qdœ Év"/y4(|%3r5EtQ\yL1`?>N/~#4ae$kWM èTh7]^_oRce֥f "(2]|zJZ}df@XEY &ӉpGĺT69̲@tZy4W 5$W~U,5D#\G):*ٌ$ezV9EVF)!L@EQ1_%.@Op#0p+UGVd^m܄,1YV|*}m *ά)~sK:t22s7Z[M3u3 ggٯڝj,hgS H`ul_201d]J:GPϞy (br.GsV~cn6ɩLEi836iM SaI# ߼ݶ"+ii*+'hX%N%N_㜾H}مmj65yl5=q> 9:\@_s)Ե|jr4Z;-:ܩhS#RsSnq^}@Io#Q_i"6$^J:3)2^~9*M"Qvff^E$2 @"v#нh1wO%{o0q!lvJukiZ%鱙Y^Pt--f7ոD09 \ >wEITysMxt&@z0nHP R;c5UKVYD&3]\EFˡWO c;XuZE^&qXۄkxU&Mͥu/zY6" )#~&vjjj2US8nZtiR@RIxt3OsOD Sws6)J+p+U864(eH4>`mUQm#_yj3rS=QWmcҿtd56eC)F D9Y΂ /<_\5C%P]K"M<ƔQBi&VOS,IwjMŹݔ33e롣]oYZ&O4ԑGgX/<\'frR">`/md&a3MelgTi%6xO JQrNҪT7H ㉧mc2KQ`XKe ifTd.UW4L¢J^ ){3TRJn:n&̐Lh <=@9_ ۇ`QzW6+Zyw!iihX}IB;a#TnyҝF3^g16)[^ᛌtցc [8 ğ0Aw% ~4-Bm}Fn*vf:rӟ ̰ʉ6Jum8*qqu32l *2'W78iHe/R+(?|ەi<.g81A?^kLkhMk(JTfEFuu|1tg˔/:]Egdd2/,1{_ǩ4<[6jn*GT9v9i\|g?Xp\-ՔyZ!1hD@]gQҜ7O}+`=wYqjZ_~lF[o?m *Mp&:89ְk^69&ჀpWI" \'bEz%(wQF<+нp}i4pRGiR`5 e"AC>(qf9oGY hM?"bnCr?|M.EKy65-W½CϮdO.ON^Y#dN~43IDATL}JPIw@9HQJb<_E_z;zVSe^3O޾ˌGӒrE l@.QA-]T_ib/oSS~zۺݮPl /ফ42*cI8NkU/n88} R 4 #I7kMY5‹Os:AJSz|yi")u1h!D7}^kʻvRi|m 7éo SXAQLqV s۱/m'cMS>i~r /ISE`ڳ喍%(Sq7J\|i܋ClR?ߋ;)i\rJ:0Io=nyowdyM0/Y)O4yI37@NE<%N5^|ө[vꗦ/N}?}7ۈ;sA,@ sSn?EkRN-eATͨ}`%JLeN|2t'SYgZm>KU-agŃ xZ  jܔS֩%)7W*L 01h@HTq9Wf*jfM_ZXWkƫX+3S߯]_\a$U*Wk*;U"'yx ;˒cS_řm2y~iݜ7ⴞF@&g@MaP6eL%r7e[hV(YͶ+iĻi$_54y T$ ˩LaMY3+08ͶMy3,}ccqf|n+p>qp*+2JTdfgƹVHm{M38x%,S00!h UPR*'͟SqFUwXWM3i )  4j Mkt    UM/cUF%yUF@YX,9 ]*~)Zæ9& { ` OMyRw*|_۱[TL83ޔG80qGAB"`*aU3 k3%N坾8}#L0i  >pSܚF3Jߔ5n90&२40@c3HXM_93W wyT;:4 @ b3tJ5];j*w4S)ky=6j̼ȫL|fk#>N@ "tSkKؙf*H3̧M_Y7} Kz]#yLY 헰y\o"`B  YUz*j*Q-T-k}kkgn_4^7Fo4   Uv5BǦTN$-Niyєk9N9ͯeUFpt @k@"C@6y,8=_*[i6<"cW9lXGS;RjfZ%  !&vSa3]IfX5^WO̲U-o xA@"MMiJU_cneh-C|u*ƛ13 Nܹ @:*SMaMQjZp h?S  T3];U-e|xAs_wJt TzvߓRʥQ>6h pALP˹<;TGӼGlw+j8~o+[hWS*͏Q{LDN+@&CT,R+}=||mV^mu߯hӗ.ukm#"3Lh4J BWFy17k7P!GXw֩,Q;4HD=Zų8HW2Zb<h:$f;x(D.X@}! ㏠wa# %?1\RßYG6ӥ%6ZagҸg{ٗV^q r:+N "ՕWh+QH'yn)}tO? l2k! ,_T˸iHI¤;(~q|?x뭅Y1To/-+Dllō1 ! 5:Ki&*0W)YJG?{2p,# Q.@[`-Qs_jZkgtgY yÅZ܁ @ 20@C:+~qO>~_'/}(38HK>%~^Q΀@AD+종WRfk\Z[S ͆˔=+vo8@ ࠠI1' w -(r}n̻Uon~ouGP $ &ş3ϡd|<z-~z` A$mԕ}]`+Dxf'͂h9PϪU3 :1*SK>{om1k#( K?W886 5*hyxtw}}(dWus G08jhs$ +psq.!~6}y" !0"4hJL +}׵le(Z,t1H@Quhg%z_Ѵ>Čv;=A& `ਮ #Wd&T*Rۋ<.㇈z_ {ڙ!U`4F TVR?SyQEА&lCloElAt qF'XA*i:$Ӻ?8`?.g|MEJgoZQzx:d) F連i֌|0m >1z8KW?`" 4AG}C@'Oz w_f'o|֛iS۩o_B]iZGF6n/n=F7~V8DzEҲ3]2hIF[m0蜿$v™&4\=> ~/2O,?]Bt?;hHS&ėG<ӴpYشtQя&\  wG@xQs v/ݑ msBe&j{,u [ya`*3]yǗ.{46#E)_LOLL@Q]_;ݬ>8?[b_cG'l~f=?Z~@6}>E"!@{TR15T*w/u?HwZ[ RCmB{}1xef-4D@C .K2 oĕ!Qwiu\pA( 9j C,2vs>6?n'AG@ -N/bC?0dzBeJ##"`L# Y\C_PNLwEh#ΠO0< <ձ?8򷌫 F +]<Fj"O7 _+Kz)VMߗ`߱)c&zy&距? (as[i~UǼd})G*ē}/^ܴ%+3 k!._Ow>~I/J<6_ǏIX{Cfh2A@QG`8:FC~R7 0@ `DaІ~4V7v-4rݕ# [8(Q@:(:c81je )KrF4@0|BHMu/l|R-JS%6}ibFHy>@`4K@OqB}x;y杼Ƭ[o~oO!z "B@D (@O\Hď z3@(xr myA(:ѕN"F}BD?x.Fv$7Ci0mSi`LÁh/iΧ"_ ,&<7QnDi m'HQ L'0+Sdib~EpHx_:GGhe=h}BH@!=wS/!AJ;iG܋E!j!7AidAȢ@1^_|D(zݏ z3MlXo}m>hR` )#~K@[{| +?Wq[%_V?={걔۴?v $C 4F߶ֽ/i'?R!WyE0&{͎d@ϿT9ZaA Xx,oԖtD6}NZ|v/ˊ5@8J wth/! x6`w'N6o.zy,P/fFh鏯;F?ZT GKA@o7AC TgXu9}>{";2 S<*cM??ysJb"~b@@  M|)V7yz_-"=p[hz4w' EJ𖾏A~G]1i @I@ާ]?Dϟ8@Pv ZHfx_+>(6kYUI)?Ю'.#R< /J"S;yv| 칊z;:z9I55sPfz\ۆho!*l}7 d클X|_fy䔃\A'bj'ٮMk' SrG.>uO];K}VQ}8d}dH!q-g6[ew<[X\( _1[gw5Ͼؒ%/>ɭ{ֱ9&qrHp& YӮ~SI*PR#A:$ 8   BH  tt@@`AGA@@H  tt@@`AGA@@H  tt@@`AGA@@H  tt@@`J`eh.T*E_K;sqdv__\,/=0HO~#eG!G[d[&RA`Vtђehף >ݭF,%4rLT~16aicQ xNeD= '.Md{ZIoh~dhC\B@i1d@< ioi(XgOe@:f:g,XwmfѳjȭyhsQ$f704N4>x0rDlabP'K@ 1! S/CT,vV.8RR;EY `tsI# ?ŋ_A"uǍx_9/|=gmB@ `D{|:h@n _coӢ=~ѓ9ƞzxJQU5ۼ3̅|MYT':E՞6Eތh'?C"@}>Nηg}g 54hfQ6`Oo9,7ēk< P? Pg/XEcsh ) rINN"9V_L\W'̰ITfXz/*Iӟ䕰ąaC.'*h=9XH98/^Ncw|]Jpkx%އ} 1r]$@@"?vB5>d2k6_-D7fwMC)5 %X@A@(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`]^HYIENDB`il32̂î΂ƿ@̌Ӂ ̋ւ ƺdžՊփ ǕՉւ ǝ ɞ ɿŽ Å ͅՀӀՀԳֲֶ׆ZՆ؄؄vfihfgjmg043.-5 i100/.0*Ui210/.Qi2110/-1.ai2110.12.bi210/221/ai21//-+**ei2 ^--,)Xi24P0//.,`i2pll0102Mi2;75210i2@R_R21bi2A2112ji22ii22ii252ii252ii252ii24s82ii21y2ii2Q2ii232ii23G12ii2矘2΄2il2@222ll22X?2ll2522u|2ll26E>2lq2qm_cbc!%$) a! "Ua"!  Qe"! " Xe"!! !" \e"!! " \e"! ]e" QRe"$B Xe"eaa ! $Be"+'%""!e"1ESD"!\e"2"fe""ee""ee"&"ee"&"ee"&"ee"$i)"ee"!l"ee"D"ee"#"ee"#8!"ee"嗐"˄"ed"1"""dd""K0"dd"%""jr"dd"&6/"de"eis32ʾ ɽ ׁɻׁĵׁ׀Ӂ׀ց׀فׁ׀ځׁ׀ׁ׀ׁ؀ׁ׀ׁ؀݁؏jUS__6323A^52103@`521036 `5223/20 `5223T4113n `522V224` `522C225^`522=C25``522CK25``522r25``522z25` _52=N>225_ _526a8{2325_X298U52X`HGHVT'$#$5T%"!$3T%"! $) T%""# ## T%""#G$"!$e T%""I""$S T%""}4""%TT%"".4"%TT%""5<"%TT%""d"%TT%""p~"%T T%".@/""%T T%"&U(p"#"%TL")(H%"Lit32bɁƁ ÿʊɁɄȁŁ f͊ʁʁʄȀΕ˅ʁƀѕˀ̃Ȁ ¿ҙ́ӔЂσԔσ̀ ԖтЄµхф҅ЀρĶց ҂ӂҁπ¿ķ֖ӄрπķ+֜Ӂҁπ¿ķU֗ԆӀĸI֘ԀԂӀЀķU֝Ԅ¿ĸfՆՈՇԀÿĹ;֙ՇԁĹW֙ՇԁÿĹk֚ՇԀĿĺ{fփՅźqփՄŻIփՄĻ+փՄ ƀ żփՄ ȁ żփՄ ʂ ŽփՃ ̈żփՃ½ĻփՃփՁؿօ@ք3ւ3րŀ ǀ ˁȀ Uܓ̀+ς΀ˀ¿փЁ΀ˀG ՆՇԀрЃ ·ڄՆԂҁσĥ߄ՆԃҀρ ȦNՇԃӀҀЂՆԄӀҀςɤՇԃӀҁςS3քՈԃҁЀρIց׃րՈԃ҂рν<ހՈԄ҂рƣՈԄ҅ʣՈԃӀ̣ՈԄϣՈԊӣՉԆ֣Ռ֣ֈՎ֣݂։Ռ֣Շ֋Ո֣֋Մ֣֋Ղ֣֏֣֌֣ՀցՀ֣֣֣֣֣֣֣֣֣֣צ֣إ֣٤֣֣֣٢֣ץ֣SZZS֣ QcaacW֣f_d֣\`_֣ `Y]][\֣pcbl֣⮪֣ע֣֣֣ դ֣֣ק֣֣֣֣֣֣֣֣ނׂށ֣֣׍۞֣݈֣֣ۇ֣֣ܡ֣֣ע֣֣ف؞֣ ֣֣߀ޝ֣֣֣֣֣֢##mU/. --,5BQ^]m/. --./..,+=X30/.-,+)M@0/.-,,*,'O10/0/.--,,+)(Q10/.--,,+)(N10/.--,,+)'R21010/.--,+))N210010/.-,+*(.I210/.--,+**0D21010/.--,+)/0@210/. --,*./1;+210/. --,+-//16+210/ ..--,-0//23I21210/ ..--,-10//1/9210/. --,100//1+ M21210/. --,1100//0+'M210/. -,21100//0+3M210/.--210//+AM210/.-2210/.,LM210/.-2100/.,M9210/..-2100/.,Q$210/.2100/.,U+210//.2100/.,U210/./2100/.,T210/2100/.,Q210/0210//.,O210/-,+*)('&'(*,00.,J210/..-,++* )((%#$(--F2?}ٹu, ++**)((&#$)C2;--,+**)('%%<2Y-, ++**)(&332d..-,++*)*.2\.-,,++*+12>/.-,++,A20/.--,+*Q@23N60/.--,*2ؘ810/.--/L2O>;9851010/..-+ 2021210/.0S23I23210/.24`Ӌ210/\;25k210//2P25o210//25o21052l210]$248:;;>t:;:621003\-2=߰^42103R2=212D22172r521026212m52122/21121122732231רU224221003l10232247p34543225o225o225o225o225o225o225o225o225o225o225o225o223j22{7227r@225A22622h22?3224g}t22\22  22!F22T22 22/)j224ZՊ522ނ6225V222242/62262f܇2422 522pڸ62622222229 2422q2622: 25262622:^`_`[725]_\s2422:w2>2622:{4762vO22 :ĥV2F2522:у24255622 :N2Uw2422:s2;2622:z2652oT322:w232C2522:w2328622:w232Ru22:w23298D@>522:z232 i־24228i282622Y524`[232224624642222mU  '8GTVI  !!1Q3! E@! G! ! I! ! G"! I"! ! D"!  ?"! !9"!  "4"!"!"!  "1+"!   "++"!  ! #%$"!!"!  !! "!9"! ! " M"! "! "'M"! "! !3M"! ""! ;M"! "! GM"! "! I9"! "! M$"! "! M"! "! N"! "! K"! "! J"! "!! E"! !!B"!  ;"/rޅֳk9",0"K("X#"O '". 8" J+"$?'!! ! "ӏ("! !B"A/,)(%! + " ~"#! "J"#:"#"! "$Rώ"! W'"%_"! #F"%c"! "%c"!/"a"!U$"$(+,,/i+,*'"!%S-".ܩQ%"!$F"."!"6"!"!)"f%"!"&"!""!""a%"!"" ""'w#""# ~ހӠG""$""!! #a! "$""$%$(e#$%$#""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""#^""p(""(f1""%2""&""[""0$""$Zsh""M"" "" 6""D""""^""$Lҁz%""ۂ&""%H""""%" '""&"Z}"$"" &""dֱv&"&""""""") {"$""f"&""* "%"&"'""*QTSTO(" %PRSSROzi"$""*l"/"&""*p$(''&"kB"" *H"7"%""*΃"$"%&&"" *A"Hl"$""*h","&""*p"'%"dG#""*m"#"5"%""*m"#"('""*m"#"Dj""*m"#")(51/%""*p"#" ]ҹ"$""(]"("'""Lv&"$T~}|}yN"#"""$&"$&$""""l8mkj/>? ???'?2???K? ???'?<??????????????????????@@@@@@@@DDname icons8mkrt8mk@ӰGw D ~                 #  +  6  B  O ] l }           k    $    d     +  f                                                                                                                          &'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('&  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   fritzing-app-0.9.3b/resources/system_icons/macosx/mac_fzb_icon.icns000066400000000000000000011330331272363601300256200ustar00rootroot00000000000000icnsic08Dsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o?IDATx} $Eu,{qr ( $J$hI4k< b5F .Bh@\@eYb]>9Wog{Ξ^}_OUzW^WUWwGTCCČlا<3oȦMԁ;YcO;#@iѝgm8#J:113Ϸ2/A2*NututwJwvmOul'6D7Ͽ6x~뇈EK.IJA{vaXd{ݸ㲞96)etvܸxU'x1k6-msNяձd\6߼H$Dәt*Jʋ?0gޣ'N_ܱ;X1Tv,%$Tl|0:8f:$WlkIQe6WM)]!e7`@7qdL*Gޙ]3/~k.g."8`x( (! 9qbfye6WM)]%bBˆ9'{L[q@=(cCoIL}.qȃ0q֓^?>vczhD6k&w("4F CUL)o+yiC&$6J``GXϔ;x NXw eLqȤƭK]4t*FZM^u*?-8MvvIQlh|ah!諹#/ g@|F pD/ D1,;u_~S~/]Ip 4Zd@'…:F>нJ' LiammL -1c6xekf'Aku/ipt M@hl.v+_ 2o :riɇ{J2@90>ƨLx:0(@ kOPd:ҿu뗟q"Hۻ8$DhP)Xn(W8e4vq9C4'uU(`RfBox B֠;)MȻGbvn3N|`$ NZFeU"LpT@o̮<5bsfJ#o)K8~h'88JD&ݻk/N[͜9d$e(pHRzC AGL|ͅ ;uBFcfhG=ړL C=gݻ۶m.'`;:!n2yEørN3+a4~]ht~ldx9̓8 (g Cv|[8P)u t{dlzf۶OQJJhBbA\mypci6?wyشiBʎs晣gl`; uwP|K Ǻ;fw> Jnb:T.s+z0ub/T7]uR O2N);H&vчȋ/]hQHiv!Sch͆\V ^|0Y~~1#L$H@&,`!iep DzH+Qm~j)WpB7$F;nꃡ Apa~^sỲ{9(4pw3K}nMvͶ"PUQ @Vs=3q7ʵ1Ri4dG.\!0Tek@WaL&?՛z!L.pg;Ν?ӧ$AI @X@-149^P?Ȳ FNȼcY 'Q^^8`;ʖj@@@U/lʥalv\XoNA.?'eH,psB9HH;ga 08z[.0)4&'Ui@@U F 龜w#yshP70"v&|n9ڜ|w#[P&F69e%Tqjl4`0x-e~6BFVCv6N;l H>b}׬3" j@@ ]KNm.X%Y2+]\w1'8 `40{ݪg@A^]5j ;9Fy41//c9jL57{P@ C&}4>쨵[X^6jZ8J5W:׀]e5-zy*8M][M:us;, vcC~eq=23-(.pݲO/խff9)q_7^ȍ tf2ΆY v{qbIЀWx5d$! 9.2Ai]Ṽc/\O|śhrZa |bg6m)'ЕI%~~eAVof#;)2m|VJq6r0{2^΍oـZK2'd `qs5.  ;c:@w4Y~-/2y٨ѬߖsNlcD}\p3M1Rz3-b\`v e4;ԭsmؼ}P3XNV8KJ: _7ZnI?w<Cv;p'c(bN3'`9g0|wlwWj^Ur:JɻĥrP)\w g~٘f<;o ]bƳӠMz_f^{: BeBv*C&Ѐ Q Mo>5s:? k:Bl챃W {AhxP\7 /el2΁ܴj@y٨d_y!LO:wFq+ D^iϬ7ۆjy플6S͓~X%u]̟'YH3XDEjظs׻>ßk]o1Y }LHݴB4pvx8eK?c%R\ci\2QsW+ove0ɽR, TK4h:а K_^uMo>`I?Nq q#ʠ| j9f|W10b<؍ /`6 t >Sg!xaLAsrS# Ћ^s m6_N׏ F28`>7B j}яpj$ CpPPrv9,e %pu^ 2Nӏw;*d'0gw ~! 2q::swrY#QΣA?2>(L#C1a|܈N`'NGo\wϠNFhCȁ9n1)`i1z,M$ v2 o 2 1 yb,IJ[`(N# @ Rac~IQ 2>H(Qh9t`D #{P}8i[JBP:  s 38Ʀq|(c lƏ 8v `RF;'ci#r HN@r`⤗|{q.tvw4!ps,(ByŁ1 (qr4P`L^Qb2WqMB>'pRTc ?@tVG\ͣf jF#ѱW>r1gPѫFT%+W3Nh=8H(CKL22ɔCQPYsTSiMe۱U{qz_6oUi)@X>_v(NJ(ځs}= 2KUf#LӅ*26SjllG>(>A@ %z 7}K/SG:]2|f:N,VKFIT4ѡ6W~zZo#L v:[~j51e~gh:@́#LDCD"Yȉ"԰5+ևfwv^4i_:c)iA;JxMw^| Yvyig``9z4#@;0s w.{C=Y3 %qEЂ͈W '}ꐛ"}T4F 6z/à OW[_%K˷5]`2rO4L-GfiFpX4FcH z̼==֞Nn4Vj6?3S ~tOCP?ԢG1u">:cBCDP:ay?`Iwn:zq:T&y,S6؞}y{-`VQPsӜ{Pΰ0ĦLWshCxd#a6z[|f8l/<%h66!`&!6s 8GKz@GWuEwޯ;\C| '$v#e#<6rE'Kvd5,qBzոD"D4~Ӂ]OMȌu `K58llx*1 UVϑR;mGG6r    СaN9O"  `GU.h's(;bSgI}It`"pW~=44yPz4@@xpM "=HMmy(ٹ 8[.h&سQm6[+7gW`JN矍)C }yF>5,H.Q7}{+V:i:֨JN:m))z:ƯEdxвd+<>Jo 1hAgTmY3΀F8! 3O'O4wUYLJ:!Kh P-ϿHM8*ҁ U녱ݻ87`gB6I6!;gLM0=E dF@yL&>!~$<8v%8h&BRս;١fr8L4z{YyԬ/Sa@DeQMg0 =%G:3Ds ZSL3<7]zOSA¶Ξ 49Zr2*cGp$H%T\&OV.%pڊ_$k1F%qd liP%sMr֋20XN*p X,ٽ{Zn* NӮ Դ>ocseGe^(Aep3Wb!r1  <1@|t\+O e"]5#$:.!P3͵dFl,-= FbR pi=@ r>}vƖytXZ„7##*Y[#k=߫FWTI57ܞ a/+kִ(mR\a:bWI5pPks4N ' L Y/JpF6/g[n2 . 'cJ+W{T-Mc-F-a崼 c@wA!`]Ыt4080]az C?.g~vLl4@j(0|lX:?tv7]?G7?zv#`:1$RB;hnᄏ7k̳W \U 災/L+1;8Fq9ܝ |mo K E^@(BPϚK&p|'?\ wv  Ru';R4,"5g ecp1A`{uW^}PfȤ ]`C'Pt<1u*ETy旉AZ1im4C ";N[g.63]/*Cwٗ{ѓ&MmmIտqB%GrXR`lXsŞ)LoD"YrO^r$ICE tf͐+|lnYj .J6Q/2){v^ڵ `7&)jwA8>K`PQ|J2/\mߤPFn> &)*T*8ʼ1 ?A@ހxOׇULBqoIZ {xP'PJ^p=w2 rλ312.|OufѴ8>Vje^Y2/@.œ>{4ucK}`tt֯dz<(e ֪;;ǕJ_-#cz2qgd]{v.$̲A@uVVzRʍy\.GRvсǴ=$g$̲6A@mXG.ZzX3b?Y<Κ|upO~lVMl,œp|^5qys"v;;;ޘWu A`~||սh=,wJ?iv^,(y_ʌ+xKwt'7oTc6G֮U={Ɔ" Ct[-.IDEpGQ1>m||~l 6,E#/R7 ҷi+1UӅf{7!WYB|~[PrV_Q{U 3E=,(!3.dȖ#Z8atJ@jA@}{Yjh[/,c+A7s6EĜO~Qا+.Pc۷8 X*>@gCtѦ,0-=8ى 5dZS zw̙AM 2 V;#d _z?OfOOP{ 8\L8Dgrtޚ= `hA)8rf鱤:[?Qz_V^Q#)2Ljү Gqح .y2m1'Wz:lKWM`e)rBre^)1ÃSV p jt{RS#wvmN\0ܨ O :<4(3Y18xe3^!S$1@Bep'&L3Ԟee(AnTf=/9 Wc-* Mɴ(~nǷʄ^ #L>h+[H+Ff|oAN< [*ݴ^kϳ֯٩7ꪪ xIXT'^MT3zI&_#cZ|mTHA@ӛwsm2ccjeg^Sng<;64&]ih'wlW{VLv{p pmT-5idOFY6|ț@43Lv顱.pgV<|cF=,<ׯvݎt^0_3E;%qyh~f PۿY|ռgA>4~8lE X~|j0Nr`O>Cx|<@SOS#*>{f2`\EJ3f^o#JݫvlQ;s}'5hQPOޥdF?u=HJnXT1:iY,G=,?YE&{UF #j]u-=(sz+ݺgIpR{}t'5i968z)4ZW'VN@n+OܳW)EB mslV5F= )RU8k(q]\`7:N^pT:S$vWW`N$ 5P2 b[7w ۝/jm8n cp !mIک5\FXo,Vj@+Tdlu:VW-#e͔% h`SӡS%L,eHu4`ӭsF͑T@s.Xly(^*ڒ8l}R N#c|k'Crmxte e̼["`-[^x^lN#^Xdg23̝FM0qal)p;Bpm8=)@[H䫫Ε 6rNs8hYk50d AD@#5 ږD!Ӏ85#h4RRh dqDԀ8Fj[ LB "hHmK]i@@D 4RmK42 Y8Fj@@#-uBq!kG4H h.@4  d "qԶ%A',jĦsh@@c\Zb1?>aJ%^ў5h.vL ޓ>W-㈥*Rߪ2ɔRsL@ CϤ!߸U-*7.U^;I*5eTqآdD\-͓82P~ *H"٥ڭj{?ЊZ}h@% 3 iܯ" Ood FjpU{VxCr'0wrhRbB S oKE'|A#GTq'`#_ۋ|2~Uտy_to*BZj4YMќM0Qo"dG+hNd,2ItZgTF_ykwb*{iPw%B2 YCySU+irQqJŁ ~KPCؼdxtnIhwvc4\_K|SYWKM7_D.(bs⩲1BQF!(AmDWɕ/E8K{wTQ{stXًUxji*^~?|@G>n*1FG4RC12J֕<U.W?#Q=pu7BV+>=;իޭUOjAˮYCdhՀ8諛_>ߴ4qT5i@#C!נWJ{I).RV7 ~;l=KTϔ*$ق;v¸PW^;mAF Uj@R;p AVnVYAsԉUze_!Ws'C gWĹwoPG~V}Eۇ֯ؠHM׀Le+K/M Fc~9z) qz4 xSGoñKhVОXBJ('4EdhC~2<_?\5Ujh׼aRs]_57]`v#5 ڮ.xU:W.Zm6}@Udx-|I2az}.-Q6"$7mP}[#:q Tv:@ p|:3їש5U+O4  DDV yXp緫5o9m4WU>eZ}Qj|t^OS{vMzO>UԀ8P6OG{6jßYK認v db2>@ =}K>o:#О"֞g5j/Q].TW_zNX:/Ws}bQ2v8Bڻ]#tňpߐj ȁ^G@ ]Ҫc,ճ kb{vvhoJE5q^|jEQT\@X $#R֊eSgǶoWߡhێрd S-  ,g)Ԁ8OP40>4 `|hS<"@Ѐ8rO TECG;Y<5   ĜyjUfKaO>W0߾@W] /!%F18 <̓`ꞡ}{rЗz;)TM5 l]fQah@@}ZCjB`zxH ғ 4O Efލ_/͆}هMA>Pzr G ' n/o/Kj7h( 9hO32QBc5?܉[~:6Cun?hyӟ!y_©Ŧy4ڽ8oGiB7Sx~z P%[|=ο̯y~u=_,ڊo=5?Z.ʐd PZj?HX÷KS;g* 6P=rK/̳3tc-_[$Z@4`A F ;l;<1G6S];`a$o-< X&PW{ξsZwyC`x3c,?VZf@-q;߻gIvazuHBsB!Dj!ނSfZ  H[N@ܩ ڷ72hFOiO;y46=>mzN&zp'ryIT2:CƌxJқW= Sfߪbr"jP ^Cu 9'B6Q2ɗ]f~Lئy0NjPq;;@ o$b=U^4`.N-C<7YIZPkZ6Vſz_x~wq =G5(?G@Ū+Ohԑig~VʉU?.p%_c Bmvr~`0?#4nm9Sto$wT/8U"C?R5NuUuzD:+_PRh훨&^p\}y]^p1kBMc[2A>z^/+Ш|*U@*ґOJACTG6":quPjK @Bj𯃺2@Իu.Y7 / /5 h=5 =UJ4K|ID08Fy{*YJ4PDMapEt#`рhhnDE`!(X4PBٯcgvJI Чѷ N@G[;NһyJ]_Vd'$ذlVqebDNTW%; 87,2(D#G"GpM a fkС ʼn %H/}N12>N08(AA#aDa820Oq\~m<&W~hCBi3POc(ˡ@ j6raCn09(cNMST,814-iB*eBKY6\iX_virŭ`H^2JLǽ.  Dрh@4  Dрh@4  Dрh@4  4] 3SIENDB`ic09lPNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRTUUW_=s_0)r x(+. *E\tW\AA@PQ@`8髪^f쬬+2+17󽌌+2e+1(i[eE7^}f$t92[5Fæ:>ڞq^L#@I;6o8 _1:3qazySDTST6 },`ޱV֪P{9Aܵ։5h;nH~i*u,4>pY`X9WV9M"X6|0b5,F@c_[+KWrmtƧj H%\qlg4Xff%-ek]c!85죉/$Tc-햩i*KQ@u2)eKk{1AM֙}ihjeоL@~/tSffX؟ 8WV6ҖٞcMՠRj4Õwߕdڑ;멥>-# ƍP b!&T{*&`^MU~e**(r9ep2~6졳;Q>XjU:/FӴr<"n#1GE2/N죘 '+o ,NyWUJ[+oWzՠ $Tx,C*֤]}7-aqf3^fˀrP0}m.-tF`ל/;MU$2K~+c| I7i tVźUC<>XjrM_1GΡ꺫L~3K4+}4v8?'}C%o%v Wi56E2?{$8B@dR,+|mB6C#=[kƇ2!+'gם,~8-QL57/p~=Ifr\᝾-ARz,N| [UTP)7pai@(׼C9pFGXRLUtfѺHuFrTȌPQ^PE>SQ,t^! elvMظI$h;GGHɎQTjXM#Wb= ?ݰhxF6o\o > F}f^8^'ˑ{c*BҊMO,XSr՚ ~fXx-횲5/4Vo6e1f؅an g]Gq-qıTf)*ހK6:'ZmYM[@-{Q"}r]+2* A?BwL+\O准vCKR8~WݲR{\ei汆+oΒ&abUY/R#} 2} FāI@y;=srWS)-o)EB%[rղ~RXAfE sJt}ૼko{QSQl?h7L{gߊKjT3 {hHۼkYL>R>xݙ_V6×$<3_upH w>xu-^va56x:Qvp N|u>Z_Zw)]H<U Y$?HW^GSDPF b)54E{Ԛ](yfXf7YWiͧ䕰6QxJ(PNn$_%.@Op#u!UGVd^m2#.Xrgc,5{.-ԶP *¢ ͦ i,]mn-lY,#,d&@ޞ֙a hwnLqEjr( FV@')Zxt䮸4,H8;u/SooiTZh nڡ:OOI+cbOWbmf㉥KxiaW}3^:8ENt&ՌYm0b7pI|5- ¯_ea&[Rא@2;~14bR !M!wzj_e׋[IhMv%e ai0c65UR},0+WMmK%Y226pT$V0UZWLƀęɤ=6)%U$,N|vۊ,i^8G/q/q%ّS_fXcݐåվiA- }/"- 9wloDtjQ*QpnJ-Ϋ/( r1+#3Mrk4ײΌs<^~!OO$g"Qvff^U2 @,v#:g.x(O%{o0Vq $P6{?BTraIHA<4;F\94\JhZB4K+1*$S*`lfx,3BJh@ 6xA%s1_7mqk8*lОz/[4$^Jt1pt4c$ݑt^@Re. 6QET"hqfis,vpF@uPߤ"qcc9l>ʱZZ=3LsiUə7RaDj Z;ӡ~K A-sU܊iE4. ʼn۱/hJ»ɭ%J)Mךc KK*RFS#Ŭ*&ť\@1 Ԕnz;ܤF:Un67OؙfFCRl搰Y3kWy+?f29pc`@(K9wtbo I1~[u@T*YIAU}cg.mM[Z&=68KtpVVDj\,}vtڍ|d.[^" $W<: = WSR(xK㝱HI k^+$OgZ2b-.Zf\1{|={A# E*u*49u6@d^qJH5c-ih)=dd@xk*TK&qi<ڮE6 E%S958`6pBTj/J7h5Rl+Zє[a~G3@[nJ4j(hQj.ճRR+m|G卷w4kؽʘ mhſ*j>떴;7*"Z#QIՇ͆˖w5yZ8S%y2 Vt+QbZ5LQKe׬nҸDèF1 _UZ TdJS|yJX9Y΂/_k]5!"BKGr4㖌 [4D^UMڸ UY̫e N7& O[ &zzЪ4QM\F3!aqy{u(j:1N#d(mA]R9ԗ3L^g RRm6} K3l֡柔6c+E)9JRɠM@]O@?d_#/PwZ386* hT]T#鱑805*,\~EgM;8ˍG(3ӭpɄ 9c nO$PHhdgxB#J>F~p%cuWq#:$2F՗0Y Q2w+42>I5~Hg8 J 7Brf5~ hh*WU_ [mنE6 *Eɩ_~Uķ:hI͎W94  DK mǬkN>3l_tE{S±hSI#,^ipQ\U鱄5N44Ęح7|'r%;ȁge=Og2q"Qfˬ\l% U?=_4")E p*5lٙچi>3ue&b)$[| $3ďE02b͎kE} XL_$8 k>DFI|^8*UL-Cg:=6hi4h~r|uGD:M/eO)|5lޮq=+aPCZЎPU&Uo5Ú?h_aRfTd5D*JTf%5W֡0{g @1zX ~L\h)%PH5OlSEyfOC_KXN}zgkR,mT9oʱ3.1>ƅwC5 IrRՇ`:^'bhocѴ׽ ;ˬ8\l_~lFnwF(qf9oGY$C#7;om7HF-Cr?|M.UKyIW2I7 }Њ']x'', 9IDATL2u'?E>G ;a`l(%K1[ "w.YDmAm{O3}xKD{eƣqI|9JtvPj qN@.E#=[X*/?i~| /IS+G`ܳ喍% Qn7Jl/WtJ4xf:~g-k}0ڏDk^l`=2RmT[[$?A;[mؼ^Q*i'a)k:.3>t2f?y"nGT`ņ,o֔S5  NRg'YJS YTo摰3]I|XSV?a@Y1ZE Xv["X~Mɇ6[^gY3#/ʬrini>MskkMv'w.1UtnM4SNk R2omՏ DM MB@Uىo43݉Ai^gZj%^*rK'p  )A|"Tvڌrsu*FIwK~U&^01@ATq9Wf*Ryx Ƈ/%ƫX+3ͧ_~nt0=h@ T_JHUv%15K5N})g,ae aX/4 9Ii;ՔA^  0 n TRTXOfIՐ@x4t񕃔+崜;57|  Da94Wt3> aSvi KXX'a1\e@e @\ 8d2JTdfgƹ6HRL2^u9yu֋1cԅ*(Lϩ5qNJ;EXf3Ui ()  P3R MK^װ闪fP    UM/bG%e5Á@Vr( $`*tUj _ǓcL31|t@ xn[Sk܎5POigƛ'sk0*|Ō7*i~yx)&4)`Uninbh~IS#Vo5nF90&२40@c3HXM_ԗ|f9+%˫Z3:L_ÚGpt#Ci U*Xqz,(ZUt3lƙe$Y^uk_i^hg&K3%  "MT)Y3a3]IfX5^˚W|fݚ߭ x!7U_cnuhC|u_}7}-_i>  bA@ 2UTִR0W3^4?gA@ DњTvkJt_.ެ+Y“'` QD(R [3]u2 hR/U#:^   Ti8/ȫ-Wm~/V l  0 P䓀@Q@F   606"@@  h@@F@F@`M@ۈ@ a  d4  a# l#y@@@ 0&@@@ l`mD @@ 2<   @F   606"@@  h@@F@F@`M@ۈ@ a  d4  a# l#y@@@ 0&@@@ l`mD @@ 7B(?D"cރ@ٕI)[ P!Dk+e7#*+,B><[! >ce* ?#<@"Im{.Dxl@"UP.d:(*B&K3/}of!J[P DK M}hOoe'Q"ɏD={(q'w?4@*EQ:8 ||2ڛZ㽴雗5&UZё"LeֻCS8h:fi} ";i:W}GQ@~?(ʯ q&zl(j4Dgxdk}(W5s E08j9x/!~6\ hF ! Qʟvƻ,X(ʇZ-:H@&!w{3Fp3ZR 0GsMH}Pÿ'3|<ݞfٗp~!oy{@ && AZŕ{|T@5%(|hzA!"*Kk區_m}o4d(#М_&ERzutAwitq'q4s"kF@T UmAhxK 0R ! ʓܧ,;/OB]Y_o6?:%~Қ{o&ڵ~#Y9-9t>|kM쳯 2BSO}-m#yГh|e0A`p!KC\a92Yji(hhC}\x(xB߮h?&!IyJ>'Ekk+P_7 p9u6 4n @E@ȣhzL7ڏOy_s.6e2WoMw:w Oֻ  aʮ~vB"@# 8hyaFx-wtO=Rŀ4o7%4{ў<=o?m 6ë_ T  P &i`?Ům h|_N<7tK݌Qؑy-ǭy<0tb8K/ 8϶.<*" (2x @V@OheR>7Z&n5M:p7?^n_;A `D{ }# "B\N_j-n~[h׶"gZHaa!02_.pgvw'3<}*"A`<ܠeœzH~r,ҵ|EȽ1hBKxW+{XSf6;Bڸrߨ,O̅ i}h#}5uvuY_ V۸FzwgzU@6@Z"b,?4eg+o\ONY1~1.ƘkhoPA儯'lv18&SN#N9g<ͯʕ7j`x=^D(9|e0pxr;,An@Vi?Q4(ƀ|6X$9vQ. %^~~kc#)% @`ApHb|w'kwFKi+yQv(B~whQ>@@  8y۱zݞ~u)?&g@`-6r}wB5/򝮏_ҋ |6KVk1I#DD 2h~l,x8@_32`DfÁ 0d>wMCZgL<[oGp a  (ﯿQ 5EjDgBߵ_ڈZQB/XQi X#zhUߢ,~+T : [qp7ͽ2J[f?IŐoS 8 74h/! x6ӪSC#7Z e<(Nx3=wK}=ʟg-MtCR% >4), ϣOYK^CNT{C\Q[ ?Ov[ܯ(SDZ4AO J_hpԾ4#C%֭G}wa$Gɶ6)[(Xc1@ F3 PO?8H}ur)`[@$3NzίZ_Jk5 D,$ CCĊhgm=`[ /E PQxOЮ%v}~1s)峼PB2g# 1cv5H\??rskbvʬ^E;o5e_zW=og:E&k B ' RO4v- [O}Ds(5-[kNQAV⒭mCCOɭ{ֱ9&qrp& YsX/Ƃ/.:@.@ـ   `<AGA@@!1tt@@`AGA@@!1tt@@`AGA@@!1tt@@`AGA@@!]:D=O>-t=)rY26TuNYv %ӭu~IW=CVE89 P~p˫)u-{ _GF&!% <7}B1ؤ+,q Q_VLz$%jͲ_1xzo>EI6Ԙ1Sz)q 'cs:L W b-6"DOOzӾ |WD:m&Q"ur^A>yi=wK.: H@sWcGQ5ԃ"Q\2>m":Cm; J[ա71})tѬw!@؀A;<Pl.;BoQG:ܝY0-Nc=K$`qfbɶȏ CC%l4. Ӿw<81Ssp ;`8aէ.ku }~vUuRӦўQ"]uC@& e1\fz?JkzqwOє_ImYAxMo an$K o8"l[z^W)5g}V\մ'FLՂZ PGuX\GiPԉ@Cj9x}zZjϟjgyy͔景މԬy+iH'BT|K^]}Q# N/!0I'hB[U >zѫLэY^4ށ jB$FG "/fd%C(%1Df6br~r)Wj4]6p{Am[YSlnyg& +"6‹xZ)'7(9:Vxy y(88@`eE{Ϟ٭or=[=5Q9x.ڟhmBk>.Z}E< h2wV@ҁX 0)Mmz%4{b\}9>fcf#B6z}ymr{>T& 0MX)_c_Y k|7}| 3e|pꩧ7= t9 hqELWoKʾVg%Bwׯ)׿%heYnI! P%V\rk|? ~yM&;wFx-blX# rCA+4~/5 Jww:VLDmHqT JO,- vrẇD&k)WֿN9)LH !pP J`wx+$/'lE^Ym|_3M~ "D3, % TuQ1Jfф \h/ER֗7&J?RIPރlM! ?6 cJOK^'{X)4BO}0bC@%0_+?OQJ klp9԰ @F@X9̞C3|g y+`Կ'_BɧHeO'%$L0 dJEڟǴ~ȦU0uaiZ'_';fB?D\¿)ǽ:?%q,J]\<CRyȚaK]ǝXXe (Y@q`4=Z j230Sjo'%ye[|xv8SB#L"7^9"J]4ϵ2 pA\ID`ѷJ9(ms|ǜ Xzjc"$^i}kvEJ/dA0(} @zmhjU%oni/'x3´.USf#jiw@` @ ƚO9/u|Dv-\X }yjC^ J(!4lHsdq_Xl9<̳Zw8 ϝ_]#Ց"&9%}nd#!k5Bʢ?qH$5Ir}w`>5$+YyF.l8*NUH@.yׇhw|sEY<3}3s'E Fh@@@D,Cd RR{Sr@# uZF1>DN RqBB,\ݮa1? װzBiR8ZkE>3y*L/'$oO,F0"yF10q(SGz҈ብ  huyv^-kۇPZ#~4| @)ry w˯niEdpPbX|c7wڴͽxJ1.;_ *i]sԨ5G@ө6orIY𯟯]|(K48 kVDsSPq,T]aU/ezAiz @@Ua/F(rU IM@ ׉O@N Uz $q<ek9'XӬ  I@⫓kO Uziry9h9930`A=qL_MSkfJ5>@ik^k՗^EOMCA ` rҩH՗S'I:3qA@&0V宾_'e%l:>3>va ZO9tƋOKCج[@@k7rc>W@4/DžXX;_NUzbɱ)ɯh]  P5Uæ7|ڬ p?$Q/] J^X~O8  PW0Ǣ%Me1 >E4rR qG9* _}<5Xjk &/EE5~0KXi~W_kҼz @@>TKmx=6} AYM~e䐰X  k>8 ÁLSK'~)'pLTZ2F@/)v5̧'XB@uUz\ʗiߔejSvn2x49cy)sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx mYy>J=I yVB[2C[[|ъP[!B2f`=mZP $E%URJU[{=s}=oޱ7k{78#HD HD HD HD HD HD HD H?W% D HD`dc 2tsD 8D HD0!a:[uO|D H#p5rD#HD Hl#=ɪU4XD Hv^6{{ͰD HD  ) TOeeԺ1>D H\orM:h$ID H-pS/{Nq4Hˁ^6ɛν|q9SΙ$@"$`[a]3cz齦U|?D H M6;ɵnr&6֑r"$@"\Gdʘ-ɫB~@"~7η~$@"${@6R{K;>YE},z: 4Hˉ~6̵̫g~N]"$@"ll6O{,ӹyAaK$aA`y2;LMJ>HD H6@6ZGtDO`yno:wD fu3gԹS7;3Wm>7eW%$@"${@6U{J{Dj.Nj!T9_>л.D H جŎNUyqq8,Yq)|n?i,۝_%'$@"$@ ]w BcLF4gq/rG.}._u9.QȤ6uS'N/ۗo>TMwKN_X -[.N<[pԹgN<}ĥ'x.\<ϜGxG?v(|3@)/C:*MVY:?etcoO5>bS$@"$+ pzt9pdS}mH5~XWXPP;}7Ty/k]Tm'ϟD9ܧw h_]7>q/9k]s[g* N;C:๭K_SO?pǾ>}w>%Rl>SmTe@QZ85(U1i#\1'@"$/5#{_GrG\^"oxyWhp]7oߤFTgMU!^+k:'g]y╧z|"$@"&R<yA,ypS;D}u|We79w;V^\cyc~x SlQmzbQ>cy_o*Nm4|Mֳt5W uGi^A&k[|T +zok+?uדO/˧pq{@lPBQ-%:磭:K5ӌ]gL"$@"bI#.ËF>u߾;;D`SxºsJ?Qn|1:r鞊Gcys|Qyuzn+<~ʫ֥7Bfs)|S>S~&G6U|M$@"{hڎ= pY<2~kO<;?mO ¦@a>ʮ<7ιegr@"$1Dkc\r~pY<2 OvIU"mpG|1[CO{V9΁EMt|fcEX*;IozPʫ|獚5*t^ȯ/=Gx-SWE_fW cv5ܯiev&@"$ ~EbGC} Ql[#_D @C@{p^i|uMܳœ%2F}}0 ƁO@.nuU=ޓW?k'xࡢ>W}]`O _Άϔ3e)'@"$G"sq+! N5e6J~Qc9߳sー %ƩQm5M2ѝ-@"ENdv26^6(6XKC—^OG4|⯹uq#ts)6ȳ JE^qn]c{y ZsXBPh~5yn>8(U>ߺzΗ?σwգ嘺+)}/5u6J.HD 8BwrD@{! / oOH5D?X.f+6 Elަܝ13=0^jp9y搯_q^}ߧXۥ'/>'Or讀g]L-hc]&$@"t#df7lz›$3ᇍ.Yb٢/2k~FF|/[v߲n]u捵La #^՛gL&@#~6Qu>E9_wýC?/p~p7 ^FZIO}ʆOD HD#5ӡ_H.`WS~L5?X ] >J*ZgёHdxcM+/|#uYǗ˨og?ʁ,^>!sl)#LQ&@_ya]鶮/>?Og3ށ|Jxh1/%ki}OD H5~q D^2:QxSMo"HNG?|]7Ɗ4`:jˢWac QѶ-mMs#L]tշ]5XIqG^/|_KcwΦdg}OD H%\!KFG/ߩ6 6Ve&J]#Άx3FP;0%?/n@8=*щ'D HD!F-G"0BE1p>Orw(&|qk:"A ԰rZ9ix5xM?QˁT:=Qjs} 9d0=G9Š/. If`N^yї~'{_Z^(u)BXsggwE~{E5+Iu,_Ի[X̻6Y?^Eu藇>֤zLO_bFJ`s_p{ȿ3..#B9xFS:_mkӤ~\"$@"pH;ɉ2umŀw*>|/=o )k6y&M(!tn?N鼚Jj90dLud~׶jc՘MykdaIw,H0zMrXsrHtҥSW+_woƯ{5{{|Z64fS6qA {̎ 9i"$@"ph_jm@v*Û|oh}@W6Vi&J dF~ja^79ԠQl޽yQ3J:54UzѬmcC㘲.8Ds#f~'Lk@C\n{_U\X=@JE|"ߓ G5HD 8dp-sr7@| ;Ûd6N_xNwH}F.dMRֳTuR,zCm 0^qFwq@<+ fS2)r]?1^^tйo`Y>"HD 8Qˑ]r]nfl"$MrRT ^j&i(}Ce/nY쬅_5*YE̵:4G JNc N/u^آę?7/帶z- XHUM3S Qr"$@"p/x!>N&g7\}"@mRkeo~jnd-똊_y5YvŰ&K[1ePPoF|EIŭtʭ&V(r\uG~_vۛox ?bzFr(tؒ&@"$v`O;Ȣ*^:}|">W+vb#݊~\ޞhXWUʃ EsG㋼*m7?QEպEiq-~Qpcn<~%݂5Wq^e3~?y S1['O=s 駊ˡg_ ;Ga+?Ee`&_sD H@p@Ne,CR>E9dl)P[n;C@?LtM{^{kڱ ~|=rk:h1!Cыr*OUoG1N1a+/}oy_ #ߌRɱ D0>\7k^gw>cU-@W+^9fۑ#'MD H,#Xޅ:(>i"pPQ1Xgts+ؤ7jdiC}N_n9M:1bӧSC9Qޟ W?׉Mc<,Nv N Kwۯ:3ovK9Ug(]K".$3Aܞ|"$@"pYP# e%[jcbdH x(PLk%w:V[MQ96&Ώ!@ca}}&J~($. 8pGj0?׉gM( /KqJn./Ͼn?Wn,k􎢮NCqqt'D HD! C5N.hvs@buPsB$^6,/4y=?c5I!4f@ϗtGApZ67/G#Mb{x5PQ.@*xmb=zOy2ջ<|;{y帲S_ (<-\M}kOD Hˆ@l{Ywp%%_k^˅6umn+)Mu~Sy6&nb;{]Ξnz=^<yJP(q,qJ刺|\1GM٘Kj?5c-SPEyO>y/T;zOqm`1d(| h&_菕ʱ_ 5⢼L=i"$@"iˑluh^5h5yV-zrm75&kɽXqWA)Q'rQeǯÏ{G/_:<Kq1G}VXO,g_E{/QC){z9j" b4x=T ڋn ջ5}/v[֫6CG󚿒t Y5135 ?ġ;][=o♏o|ۊrJ'7(!cHD Ҿ|'dnt*>FP9c~Мw@h\^4P[g3a'85dgJ)E~3ۓݗ᡽t}>z:l=ګS~1dt̬<קgVPȂN2ƞ y; *k?/xsGeq{ˡG i1U[J8zr"$@"gpD H}D{wRmYF-6k7M^6jҼ! r:9RT[7L&YӸZ;3߁=GשayHuW焯5g_|uM΁]:.9exzs,;wZ7k&z9WRŏ~S9W'"kvF#ܧK: PybʀFͬՃ^5 qH pWvם]П $lU:ҋ׈:K"$@"^#D A@(_MtQoT#hr[D1O^8OS9KMn[|3b͡>%k|]4c2\!T_}HaI/nF8m<}[~|χ(jml,HP*uVn8ܞ|"$@"'ߑ{2I&MD XD ^KQ9i6K<F{ [IO ~ ٩=e@x M7n!l9$s < ~F #8U. 0+Ve?)z蔃C>q(7,p(^U23WT]Ÿ.xrk[#-C_E5+iD=hOD H#߽9D H!@3ʧQH13ɳ; h$L&JrͩyvkSf孔n:5&’s}]^Vԥ9 u +JΏk~#ygS YCj59M`(S%B_ܺ;?M3<\Spv/C~qt'D HD`cᅪ%D@"$5V:jAJ><6v '4`/op{1g[Gʱj'\5jBU78b J,vQtw:|A׶@JMEm!09ؐ\G/\zN'Tq5u+^9}7}ؑ?8K_e;gU|^PmuE=Ë2GO}RND Hv@넙 HD`#ӈVyk -eAӺHIƌXdistධnC t1#%^1^ gt؜t㼲Lw8:P.x]dl ٽ?u3@1%6UPS{yh|:WJAz/}lxϝ_&%*<JIU|ID H6^T̓$@"pH &Q'9o*p7k_?~j2 (C3EU8:GY=(&ϪLux'KF!C9ʡfZ팕"z\(Wg{i}Z޹M\7{[\jp }։>~}ZTהCw8tl:I!]=]I9HD 1c20HD` bwOE~ۖAONzkh,ܒUIm]|CTTSkxxC^搏ۉ$;"Cr;7Z5m\Y6W qHp~m5v'?]_8ŭϾw|䍯[obZNޝKFO,$@"~72M'@"Gb6:lۡ*WXPVhw_?y_U|-q)H9F56s{@"$ A@"$B`K 4x1yCXêfq^jW&>xfֳ~bDCUƲUr8օtY ¶6Qp 0jb^_#;'w\5<kpWoQ؋M)s$@"$G 7vaf@]xq%]/V~L~cHZ%ļSsbd˜P7E=Ϙ*>cc^Ξx!0z`cu$z۾cmK YWxPNCw<\we/yEp(](ꅩ\d!'MD HvGw M#US/WA׫ &rKtXyrBes})zzWqU^Ψˋߘ(녙cmNElhx,޸6Ϧ 4LJ6xyStRꊺx=ojs:ݦ\ eCE4k[_ ohd|e@fnYO~C|_~Ѓ7}'@I9+G|D HD`ߥ;N@"h:v^Ϛ KzS{VX9EPO2mlେaHcE,SjoU5ǺsqNR G @ʶJG ŋx !/ni4 ?RTqP ^C_ןUxM6H5}\|"$@"2wʁ$qA9^G2\zX6Q;^]Z7~A,jѻsSBAXu=+kpۘ~PtZXNpTgw-sNwCͷ_r)vjVG}"PˋfyVtg}+̵'?G_7VC\L$ ?d{D HD`9D H+ˮ#niQQH 41j?z?׷5^ˋ ,gkc(J'/: _Q9]ʇs=ywu5<Y6b\7o:J}Q34'uk *W4/<-EMҋ]|"$@"0.tLc"ShS1m_wny|G]u?lC4t1>@v1]?h|w_l.,깑wev:g\FԫcP o$٘}#,At*?VC{k=Y>yvMtj0%a ҊUZ:/E9GЎ|knΥx=zЉJFYOk\5Uk4^+V_(lwnКb. 08S#ymN]tZ7P1O?M+owq -(:5:wu;_D H(G"$>#P^<*הm<::uк{kF_k^tl7X3eU﹘kJuQ4`#+1F?o&3J醳Q7IΏ6fs_g]JGw晧{߸-X+ˁYtsZC1ڐ=.$@"LL"$FR]z0^|eO%eJL:FQ!5l,[ض9e91|%~z\η̳­{QA\T]玼r=w&LSzD?\6f!<_o֢ҝ 7"DT*|ܞ|"$@"0C_>3E2@"$;C`fʛ o]FR.i}&s].@6hsy*E~81)[Ͽ##v<>.+s2?1$3"e nE#_y{vǨZ몃 D:bmj4el{GW⌯d6k`zހh<mʫ5^zDi|ݾSS|uן<=O+Y𕩮_t=:_؂lx&֙?\\s#HD!WA|i* /\غyb v|kDuF|/X^Ϫc=]<8z|1<ﹱB O%8h:c 2> ߩ1 =)1:1jo롕's|U[IcFY7].nRJ?L1s_\bnbو7 `R^ɭ4˞{RvryLӵ*8CrSŦw9Rg =_N9=pڨKw~{,f6ggiGWT3;(C~qt'D Hc~7H$*&cE}LLz^,~c1N˽,2ʇclM?cUjZ%nlNS1kz,q>5+ ]Njm+CkrT]n^u$7jT3_rh<5+ΐ?uzj5|{?6, h]毼L( l fg֬? h>?ZBOO?lQ=:o0-Uz ED H)pLO|.;H.~eNsMxNFYjZhz#סvϵZ"?ۿ I$@"o}4'JD#k9/Ԭ19e(_ кhxt*_>41f(#uuοg@^Q(o=<(e]e΋gk%\6Ŝ.,SʤOYڰ*;~kE|^9*G P@ȑ$@"c3$@"0@mUTj˲F{5HnsWEU?=-qMby'W^o ;.3D#{`9^q+cƂKhyt넛U^SwnɠMnő )0]ghPGP[>s* ʑ@"${~!HD"@3kdmݿzcᢉ?&(kU. Dȡ8^r_ G+G(ד}.T~4䮛{ڠc9x1/kYfP.r_~Q&(>npTcDȺpCԤ;noΜ8&.5};U^l=$$@"pw1Zr.5H!@S{:G/zcWczŬ3h<9z>ZY'zb6.mFki `k=1_QZWO9YSy <{٨.٨[{M>q (P?/ .H*@+2P^:|tϧ埾-?3g_S}Y9E_]'tqt'D Hc:%&@"YzW=9~QߓNs'S YC4QGLYmVcH5Ul2󔞣& /X"fB8I|Ur:JL< ZL%x=őX̻ۿᇟrho=+|1ϦF'p~J-i"$1A`1 $E+X^~ˮ=|Jv5>qʾ/_o0cϔ^xYۊxYhru  +&;ǹ2zlptt,l q^Rv9s\bjc="8|yqxR)/j  w*jmgoox/mh`MI%MD 818ɹD HA@]M:yVR$^TԠ&|$">/ZGsz=Gc50{eIt:?tf@a *Wq1= 'lk\{=p|l5Oy>j)Ju]ֻ q^qŷ:^mh@"$8'=$˚©um YiI)0K |b' 9A_|997oպ$kCxdxrMTsY1>xk/+-^luJ@"$8F';$NOʦDz?GuʧI5@ɍ E/6zd^2Ƭe|O_oݟƟMK~w~֌(1f_w?.@^ Fjo4~mۿ楯(! P@ȑ$@"l%HD#@:-Z,M&Lej_kv\sKFX y_ ~_Tl{]s-Kslm34y:3VsqUx.,st'yY@IDATc:U=t~+%r`@M):}:T%@"%u֔kID XofbHa>FrnvQ:1ԫk^XKVn3yQUYN4zz+v\|rMnl3k9KzQ7x.䯯-";>J&@ P㪾l<[ug~կ+*tZ̳i|:cO$@"pD/!WP[Ǽ]XQ65O1ȦCyΜ} co|rN(!4 wy ȑ$@"p_f.r"W]L qT&❺OqSͮ3!X/z'Ήo1'4΁mq>w[Ak>9 H=\.|Ѧ8|uMv yf\ccW>һ,^Z"A'!Pą *k|a[Ct1u4'Cgr5oNr7G6 >$2/z8 !3d@ɠ4>ԼeAI-Ccw$)T@k||x`[} $$Tˢn;?O7MEuC966Jl)U,/G"$C`w[f.r#W/+Fy,7r''8dQ?;W:xbC쾮 }b]Q2~hG7RhG\cwJ.lzE~l4jKy uP6=mt|cw:oKZ5 MqI"tH_s2S,G)Q(9}M<~ϥ9=Dd5ugUL̠wsk @'&@yrǞ{ϟwwX&0-S򉣧>)'@"Rr,;H'4H^:W۽xuyaQ5bYmDsCm}%ہ[7\8o(搾gc]9cqءlN rGǺyT[!xdy*v̨sL>Q6EoxXU;NǙo"$U9X*;aN2d>)'@"B"G"$>!U5mdU>kpV(Lr>TJuuxŘ˾ۮ<)WU<9+>%aeRKS ɘSx6*?&?^6|ӀM:`=SϾ{Sěqe9hE󰢮eImvoD HC$D H7~%NӂyjL4Cfs1ǹ&G|jҳvlT*Yv<:(>=_bE;kYԷZ7~Stcs)G\yܝUcClU_^ @kϟҐ MP3׼~o-!l"bN >tnO>HD!e@"p8!ޕ6>4y"5noopf -T^r֮sOO/pp*Eĝj@xFX&9!Y'Rs $sl׵~5풟*<_@Qەc[;XS-xW&@̑$@"X Q:$@"0MŸG݇ljG1ǑbFPR΋"T\F,ES7t||<w'\28|Ϣ օ} .#j[4m z|[ϙN;G{/z&E5rxg'@"r,3H&+ez#E_ZvT)kGLt S Xw;b(4gPpZ#9If>h&5k._8x^#6Z*s_'6k.fP| liM.?>޷rMR9UZdxQlqt'D H@nt5;zv4rGNAqO' _; wSMu]51MDrE2vρ)c_:σˮF?M>iA>%눍T>"O|3C)§G7vr6Q lN{sGWYv3X@:%cLW_%A& ,Wlճ jnfU2+q>kC<'x! pOT:Qe/Cz 6m{]_9L~t9hO s<8WeBu}bk1G5`׈+^|קGR\W#HD`}hI)HD`)~qαipx9*r yeL>`HS>ThRs񗌎E}3`'u Su5t82g\1hwyʇȇ&7g.,x^bSWƼ/%PM/.r3fS=|a1GO}RND 8<)YR"~:M +[t_Q]G)|d[7/vG&iCw2+f 95WY ϛKzk1=90f[g.yʩctbGQ1EUX"&N喍 .5_]2 ELp'a}ǷyM;5;:QU|ID 8V%@"p+jH˽o5rgpNa@>w&erc^k¨g9i U ԁ>VwԎ7S{^3%zkNo95}?hn3yXC;w|ϝg?sċJˡ;V ] =, D H@ns%@ 6QKhwǻ>/>oOů[7Žݛ6s֝7*8ϼlT5S6m`֫eϪ?~Yu=&]=Eϩ" ;5Q lE_6Epӽy?}7bTbnϐ_D HC@ne&B`8H>~h^QgLͱĹ6t#>\%6;O [1ǔl)Fݦ|hvG͠o |K6&@q_ҧMk|MD 8P:YL"UV:ji"VgV>q~^< g7ySuѼOg7s@=VͣSO#)~+M,s¯/sZ5sx5ēo<8ϳ,vN͗n yI]UvwB-z>|+>;^tהc6l#HD!d9@"p@+bB.[Ǯ+Jֲiдn%3S ǶbU`´maPxq9T/cS>t7uDdf ke~.Qz ,J5u/zr&ʧ$9D HpN+Gί Q87C<~8ymē;qPs_車W%u9{;@]ۣ^2:xd"=boG5IBjA'z(%s6!7ܑM 6ȧW?W~ɷ%&|"$8&'`,KU)6QՈ~iC1s>lb2SlPw=S{b >grG4E5z4_9#4CWËD?ONZW>mH1ǪvlSO='ǯGѩ56rJ'ӻZ@5jzțvjM{6ʻMu9lS5D5)9/{j$Xsw1s姿p|WyȯxF ?nu3g~>sC썯}C{}9t'?ˀW3:>u]@"$ L紉@"pXׯiz>ؽ5P؝׳-MDqt'D H=F 7L$4~M Y˴`] H~ww;GX3>ʃxJfcY:vS߲؈c˧򫩞'zgQAy0$bNRZ'_f-R)~w{o~wQT zGQo[Oʉ@"${:HD`56Tҡ֋R8I~< W#3M{i7?b=SŊD]ԀzHFfnri>jzmhLm{vB_urAU!E5˜ǂ G63ks|Q ׌/BD(3~/\u~o?7")'@"p C~D 8lZ$C{{9W49]~FaF\ TשsyΣe}sy 1\otek1.+7 s'd\Xxt ྲq+fyM ZG kE6 Uӯ{'&BA@e:'$@"ar`Àf>\s:s<ҡbs:fx')6υw_׋_%%z~Vy{:lءآcE_u{9~}k`j)3|-z&j1UBjދ^16ʋ{+175T'>Nat2)HЕ xoxuU?1ګmk#ġL.R>Q~y|@į:<^1¥k{ 0p|}4YtǾ .|=?w">z&o-E52GO}RND ! 3j.Hʯ{~ <'c'?vρp^:mđ_Qj񸘳's_/zdFe4Gz#${~=q䂒bNo,EynxcU=ZN3rNПڛClPٕ[hױX 6^'>{(<؟2 \bL{6OO~zIEq6^COrA E0_ݒw*p>MM>/_= pK/~}WQ&@ˑ$!G 7 IVҎ2ӌYS\|F]G'ӵH/.m\W0;)Bc֯5?c-"j!ٲ|HM6 Qk{wISPLpjѼBW,dr!Cgs vPj‡O}?z;z1?W#( HD0"X{֜]8 ek]fmcOes~o>[],Vh|ƝZxB>Ht[`#H{k&xIDzdTK}uw+~J.armrVP ~>Ob>͎XljϿ?-#7 9D 8a^0Yhe9_/[7Ϝ)Ka2|ܞ|"$v&D H64jL#k>|hZ=~>ijؓDa.1/Qt_b\ӹzשYs%z+| B=^s}p&Fٛ:sdt{C>u" n'jN1 ţ0Pg{#o{?o-ˡM _lemkh8zr"${"$F^x%dl3.cQt-_5Fe(;יkett/PMc%8޺^sLass8/#W1&s5z^5;1n~}~?y/-& u6BJGO}RND XXtMD`/ڝ9$k`o\X_|1fi(fE e V͒(؝UBdNͫ`N*//}=89}FH\Wmɿ0G}O͛q#fr EBvDgW_}uq{ @Y|ID 9s22H=Cf/4]|y_W:{9椓RJ (<ϑq"l^+}4֊o A`iv)ַ<^ӟ7}}<^>nS>](N=>U[_-{ )K|D H";.D 44˅7ء}VDzk ,]t>9~c޽^|n)fPN=bs{u)'*7iN@zC[gEu9E A|s#˯6Y/X.|['ۗM"C_C_Ͻÿ_"&@o@!S׉@"$;D 7v\%@"i'|lyDy,kr׉]B*21ΏGbv*οΩ=d:C36{Zk%`j147MS>@ՠiWbju.F;4>ìƕ^ly/Wzkİ WAŷ$:_L3[%3AN$@"""P$^#x=46[ W,Fy.ts;ʬsny<;_i~ll x-\&%eqF[1qW!@e!W?e^2 Q^ź]|}`asJ'Пxk7C_4.Y#Rtӹ=D H%U@"$hn u+]O>_?O˥1XT_-8QM MryFqy}y:!KY>UԦy;VU74I:rx~ p_ݿ ⨿Aʛ Dy sآ]<p/FE~|~/VıMb @T|Oʉ@"$@n&@"p\wBi |5ܴ(\nڐE6}/jZ^2BirdMs#nce5_ұ':}2Ly%ʭ#̼}'U%7↞.|zJ2RM^x-[;&,t.^g~}Ko{ӻM8S\EUS)]<2GO}RND 9D H~Qr]w^B2:NaW60C.;!ڮMG# rkoy밹:Y):8/y~5~H[&Q"e׀/Q吏&_])^6ǞsU/ݎY\n0{/.Y/|_ sUz Q4ĉ*nl#m~5oa׬-όYBӦm۬+1EE-2(X(M.Z2PNs̯xmjk9G[|(ka0 ?gzM)ٶ4Zl~n9l$3ǂ݀u}ۿi$ ߼ _~ޢfQ{ѹ[(9|^'gY_~BdY@V+@C1՜|@H 3@?t#ܭwïo]nr6rnԚaI~:"v՜Z;ϬѸ~@_bA1?,Ɗ>%l= "vEfsa9N_p}3g?QĹ dGT8gج@V +p*BX 8&oJ$IDV d4d4"{֋ȉom[;!0_~`ܑ)H Ԣ"E#fsN~@0''}w: [2 kiaTGN@;Os n)rѢE'gYY8= d4;8G~j2جw=v XWvl𵩽іư:߽1"A2Ns3eন)"V/͂Gr]\yaՈшLYt9&7K{ř6$@nKV +8 #e@V ++`thsyQ',Ed5s cŹqo35Ƨ#[EWOʤSyյ0"vEI8slLzn~@Aɳ8Kzx\&—\u׃O{r&@)B@V +p èr d'gPMԞpLg@G׺x[ S6['{jG_u|ZZWuǔ͘o͔Xřk`V! LhUz"9Ftj|yOxQY8@)B@V +p p dYZoi pKmZm6ș7 Z3pO<1Mc޽1"AWV5c9tڭ[YUxms!Ȕo.`. K.[zF|Ulwo'į,&̏2]K#Υ&@V +pb*'TBYU "6-dn| h].r+ ~"Y@Vh*~Ɏ:7k׳)N[44jK"$C]&^fI_w[3^%Gu}Z65ܫZ>8jvIucüzʧ ]㚵ˏn68@d p|篽?wST+6b -yDO% d'p"Ns.2+ Mb3si67ؾ_CDduv3.F7tۘ~7zXٍz_rw0@D\W5OM; ǠՍV:)\ϼE}wGQ trdYW8q< dzcIxs{7?֬W"7nҸ6㓸c~ m4hCu4҅3?dѳoa/\`5pq;79t?b޲6e.|/Xf py?E}m9b](9Y_u/6=/}xͿxɽE9Y~T 7cE)7G/lQ/,Ȼm@66G9cdί3Ǐۉg< c<8bE9sPɝ8.p9(Fr llӘ>l#R`A|Npx盬?cT6ڢ=L6@5N+p`vy-GorУ"UPrw?F(F؜TѮ dcSquN5' @m^TοWڏ" `|V +"*w"LEoZ+sA}E;!sһс=(~s9v.Mq`K<| y{:cX xQ m^]1n-2z|@:}vl` 9Љ£#ׁ/xN=溝_Uu`@&ѬE`(z8u9 dWL3GV*oHY SxGG1$L6|j`clu~C]/tP=*>ꑉ*=»dq-$=zͥ~Eß˱u z:!wc\6q9q4Z0Fhn"Xl\>&[Ĭ[YΌ)zs:??Ƣ~A9 @˙ L祑zdqyY@V~h YziAîdt ד#qnf*})b~zrqU:A(rhOޓQj`8"l܎9 җؤav~9)t[:y<Ma8wS|լc#6~"`b,֥nl 1 OQz [n>z]X{q9ZD 3yw,(fם W/0*躮v5k#@3O~?v񩏾Ms&@q #+ \Y@V +pL*;xwi\N+%zuEˢj~ƚ12Xw.ۑ(#?&_Cټ!z_G5m Uݼvn/brhא1d{qx5dE]vN*(kO7~M;r29.eHGO19 dDzp,O[& ia PIW;4?mUu󫭅yފh19%~O,6SSsf/742^zОL1<֠ 0$.ltf'Ų֑8=Y95 -M6t烶/΋8a? _ Xc=wO|{MӅ' qj0 U2' dUxdY@V`ܹfh%{Oʕfԛ$|>]XI?-WiU6?;?揼\NpSpG;sXmRNQxw+  9UhV +hН^xfp'KL@IDATs' uجU<+n_ཬ:˿p:b,6j= nb03X%r;A҃:ߢՀv_H{>'8kO 5E+U^t< Pe.~C٢ˡ?Gze&,br d{fY@VJ@l*KYZ?)L+f$IX9㌩+xp!ϩ O:*h*j{qx1`WQXaE~Lhl> f H|H2HE]3M֏TZ/z>һ"& 7JrdYXyV +8fস ?zlge44;@ Zվ7ŹFg,]Eřڻ+YwlsÒ CSZF8e~}j{eT1N{N 3Dq=Y]|>/l?=Kת9{:o'>+yGmM/lY@V`VK!+ lY CYC f/Ecr'zFDF7i5VU(-ʰBARcn'gvsѩ+mU,VlcW8p1N*}je=Ƒ{;{ED3AgyQN-||w {{99YWG.K>+ dv/Vfg,,U؞IIf_Cs齙lT~ѓ;fslS<dn l'(|5R_+yM{qӬ#k[yȦg96"qP#tmgfc7#nb@ P"~{w' {G9DQs9(C6qd@V +uY@V +p+ $\;׽zNǑ7^+ZJ2: 󊅟cY [+16 90WL.?:$G7NO.ǨŇcyA*n]=1*_"J>%NYT3'q~]5g]r; GL_m d[WغS eY+ hpړ^6هmz ri윏 MNɱ忙^֯x|1eMlL67xYAHʧ[^x_VryCyzQQo P1|gYkrTV +*A[cd.G23蓶 ӀA^Y{|Y[/J:v¬#J3 "X_R_טbs5#ʵѵGy?w*l[c8-T'goN[|WL?~u7^fte;x|: p̓fY@nlIJYVArFE:\XqG*Ekgg̽4h:kqU1\?a{|4bL٠S6,9y9`S0ڮp=W{#Fi#'Ɗlz %~ Y&Tq@>j_mZm4<7~U/qK rh 62JY@V`*u>2@V + L݁$9+܁@&_^\JcufyuaP]\Xq/˚*N'6c5sxmC "bje1VʊQ^E3Z{^Vז 6#+ lkr`[L d&*ѓ1$%(\2 5 fl.6CtM՝le##edc~֕㗕VQ}L˾@V@n\8 \fwlmcz5 y#q9iZe7>͐r8]&}?x5%u_8R/G*g/ظ,@V +p #?@V + dDTrtȄA>bjs) Qmjfm ,zc1sTp)#hl"Kauf>֍s'' z\˯) O>ѣT(,t)^Fyfwgos'c/מoMf% Qy5g8YҬ@V +p #- d_h`ҹz2t'ֆS 5z4_9v8ZX_Q9q18I"R{Ď%1a{1:ۓEClQ-}9²cyH+SEAb\9p,N>X(m/}-n<2;<.6$ˆ92 z>\/>ƈ{JI\MPNŷ P*f_)Qtȑҋ*+xV|_(OTиGWX#)7EEoKCPlaŐaiA 3Z @y{~ %I{@g_ȏw 创χ lŔh@V +pdڑ% 7nxɍ(ثZ/2py?N{ !lo.l=~s`4G>w\/d?#,(<a.*;G:ƨ۬oI r#z·#c>G? "Ezl E=2Q21裌6Ԝ}$ f6:V~^:yй4V;(:`q!g.x44G.'z#wl\%C'J ɝ\l-ẓ/`DLqgJcЋ8B'e/q?AMĢ!8w~_#o6 vCմsކk4{`k6i{%7Kxxϣ6D+ X^[ȩۯ7a+>,1 *c{\#E92t>w^:|K!y?Pd60kH:ſ#w,tC76xcp=uE\45"FsVs3f/|J=G£ۢs9Ћ'gãxȠQz#ߚyPG8WU*U9\VustPt2<4-QtSgݥY@o5/m2azz;X 1fJk(rq{X˸ɢ0S8t^k/d33朠g@/pW?|Гq@(^3k'[D,+ @r.+!syZn33f7)yȞc$ *(2އuxP5_Qcx|Cp`3G\s܏xN2p\rrr29|c>ne)v9> >z\'0c0)4y poR#`+od⛝VR *-\/9s:j3łL2® x!?⛏6ƝbJLh&}ys'_/6ֵjЫzֶ{Iז`׳%t^gj Vswщ P7 ~5$|+~=o_RП Zs=(Cqd@V + ,+@oOD7>rd(rE0ః E$zא,b pFXLmI.'9#/sFT?$+> l2+b4w<#FTK6/w[2p ?`D%9jO7E㺈v֩!T8h~$ZCsXG kE9:l0 W\-v}T /;LH]/^ƿE e/ū!A pc"!#d=ɠsnҵ?#?&\|r䋗O;GTy%?wE|k9$@n" dw<ޫEɊǒ7aA dc(v/t z»ݘL1ܔ< _(2᥏v@}8?>=a۰^\ M:z6.-;؀p>L˝7\/?&(vGA㕗z!a>pu6lVH\GY M`m-69(ƺq[(èYoB+/èvL~1>BMo[ԏ*cZL{^{8w |Dfk5g,s3 ,Ҿ.ʑ  3/!S?./ޘ^y،av#n=wuob[7#M}ֳb0:WawKCzb~>Ͽ9/+_tSVgc1ֶu0XXP=dOiPc:?|P{|1>f"S64OW+ˑ0IV +p<+Ͼ}f8iri' xlF|~^LXn]s2Kk8\;fm}Wa,׽.AZ(Mi¯",~*6:eSeg!d]Yx=ܲ%a6 ]&6]SOٞع_|,PZDs(ӝK d/eL'Y@V`*лefDd=㤏a[9r8S nm\Y';k:ӷh׿\Srfq81}XEre5}']7ؙ3;ݿ5g]T+`M#Υ&@V + ʗY@Vʩ@ԜCmZ7Omu7( p>+Axmr圸ͺkmg{#c"9TvG&r)rNOVHrZ2[ Iƹ{*0daHd xS76<:IU:z>/򺟽_TlT 1;4 6k= d@nti d6sh~^ kZCa㈞fUz{3M5l:}/Ly{ͱ7>{k_M(9ˮτǬ#YB^wG}3ѓ =>r+?|׽?z`m#ǧ7Du1"Q|:"PckыrN]PL07P7 /y=? pWuxٷr!GV +Xwv dY+@S3p 6?*ްsrkp Xe (P g"&j퍛jbG5r]8n ަ OǩO2j6Sw/}~ocD 5ܗC9|^>@V +P+,GV + oU8GJ;e&9<ڼ|fW";>wQ4Ci{XymrĔ !1Ή~ltޣD(#Ψ<(AjizNqmU6' eaų~3?SГqPEd "&Y @n  diHhZEmb[?q-hSl1.n7{)To*#6M(ͅ}L_gޗ%1pK&n1gy9Tph(tSlza3/g>sz}vK9ΗC&=Zs=(C6qd@VW 7NϥgY*&:Σ?lX 4}dhvJYy-W[Ԍ9쭢hKA푯KY*|̋˙ b9Ďu2[G9\ |oxim}0I}@'|`|?~ rI(Eȑ ^rjYTfc*!nѕyͷu&jymGmQ41|Qӈw䱋A׉eMA~Wo]1ӹqȏ%kls:1WF^W\6?Ҩa@&E/|;r[9r!GV +px ëuF d[Sp/2/ 9 QPvxPlmM䵊M5|Dj5s\<^|rs΃ecػcuvZ,g%qc6u{mp0ac~@ [ŻOo~?V#? dp(e Y@VU_F9+s92loU~{ضGM ͑6ϊQ,c詩!|+'V5Hy:2,n߯]&GEĹy1ɦgԍ5L_ɖ>n^}߼*;&fvZ`եd6[0`f'pBO|.;+ [u~G16b:yl7fa`[':pA*±cL蛙14Ė?r1;{JVe{(s3\ +}5lj llr ˵&9:5Itc7=w駮9{br;}1R ӓ>@VU 7NΥf'ܼ~L;FtלJ66tc6{5%:3/lqP5-C0GH{zEˡГEigX -M|r~c]׫bxqV:9f[q8Q];nuܾ5|m_|_Ə%/r@ɑ [r`JY[/:ꍮ-D7رa SPQ4U9{q˓ d s ]^.++ dQ b'#˹'S.csb~U\SjĆ|~gƱsjs]b' سOըW޹ʵ5ݺoy/ B13NιVc~{\}{+f P Bsxל1˚iҬ@VJ?5ڲY@V` t82CO X?IYϕiuQV9h|05ݾ)@L-)1o4mSwc6u{K-abH9+OPY'נpKW1*oWy{ܓO'=K{߫Pܼz@06n+)qd@V ?Y@V`/Ckx%Cu' `zv-#]23hj^uvhzɢUSG!kf=\_OCx7Xur#]Gq!Ơᦛ'S<]&']|׿ޏM-qHL6z2tIY+,B dYT`CFH#$y<xrsLY6nnj:uf+DsRi~=*^:bjƴvr\G8XzE7۴ojCuZڋx^:m } 1Ox+Ugyzk7%qs4"-9F6m^{2'  s/# dYîiLso,bzsɶa;bC(߆\7AM:;j=]ǰu<9Zrױq}dQS_ϺFGkƹ< w꧆cͷH&gO~#?rX 0wm)v(c!J{@NY[~PdY@V + )[.1^f gAs PqmռX%,ՈFӓcF6|#`fw1z0\نtc~wc#_˯x^gc<f2p+ٸׁz7GʕZ$m̅ ׼G>co#]`zY5-Ts*Y%s}Y1?ivV + Gzws,9躾kPN/7&m{3r9U)}sהE_Xn=r==\{>-6kS<ײ(<-:c>n8 sȉpi߯ O7Іϝݗ>S?|Ous9 "pS 8'sYcX~@3@V +ئ pKNu"\ݮ%^+[,Y3{6q:⸼q/}գ_~qmxLB&^#d/Y?wguV + jhZ{*Z#Ϳ:y.)OkO3lÜ|#E[$J',;ٍn8}W>w>r/L %ѢE%L  ?@V +p(kzwY?k3MLiSKɛS|ؿSIP;qy"A6܌Eh Mrvovs~Xc)n=VhjP奷5P> P?S/y?%[ˡ&@)B@V_~) dYyMG:P\XKvF\q\C΋ .[:,#֗h/2\c5 7sNW 3Y٬g{dŹ鯱ե}3-dϞglu޶qS.ͿS/K.ʑ9Y~Yצ4+ dV|17âˆטt=m876{XY)i:bb'=tl`O<|Js<C8^q9'Q-5X 6k:nˢWcXïIng}i/N+dM;;qRYr9IIV + dF*MKtaա|PJNDo{{3Ŋ^{05h.5W4bƨ|o9n7e?~YxMgўkúӵ%&6G:&pO~Ǜ?T,r\-ӢE\'s}YW1[b dǭ~wn~fME n[h/:8iJ9F`<ڟ|s|C=}yUe#Lժܦx|Lf^蔗U cfL/nе w |Iw駮9{}^9nԔ ,CM3Y$+خ elY@V`+л$at45P;Mp ̓56nn!qVY'˚q"pϺRX_9]guR&x5Jm(ʞ^(o[67Z]VB= pP#+n|!ÝjSoP y"wqp7§jـ &ƘOٰP~ƯNjk+5&~e/h!k^k6M4Qd6 6PhNnڴkUy"o?+B=Uhśvبţj[mf^]un\m:?|,xL/\ՍpYYs܀WR7FLy5Z&W2O?o^sӏ~YďB9榅Yy15iړ>@V*C a~ D^xB2QP#+w~!^2pwɿG/g /xYu˱l"[5s$_3|G藪޹M1~=6Bl6qYW»=l53dCܜƟ>˱gXrE9v ɴ[\fs|$smˡ-sxF,:T(g)a(W rclnLm\<{z÷?[).&+(d1$A\V +p(ȏJ3HV +8oza]n(d'YkC.[KO6{3rbՙ) 44~ G=JD<֡k^댱Qoq*6jGSa@粮芛s\ׯsV3G{p;עMѨ ˺缯Z9q6Ź|qWdFS/ ƀx}AlɃF./Ent|10< ܩS~[XLZ}i)A Qtd2`ڬdO>+8 ,:@zk+@5GnlIJ]As.w~WJ;E3ƣ5(up|Oz5‚q1~,M`W.c~z b̼ mЉcظ)J/)x)ˢ} Ohs;es鼙P`Gz7W31[Ӿ2IO% mP6n86c{W;|}VyQtع⿿~oz~z7p@k4m+/!?pwR:nw|\ srfeG&i:ַiOeS8eДqjt ;*&)[ש7Ŭ8u_eO}U#_k&U>yIGldKrQj`/jY_-VtĿ|i464dh5WsĆ6уÒ˦ W6 7s\b͈`'Oojq' ?tƎEq)dfTq+@5Gn%Wؗ !N10m8%Gnz~a]_pʚwf}ĕ=똯Ur|*CLu3ȝ<h 2AC  @΂ɽ' P3&@CXh^h⑁s8`5ןedMt\҆R~A<pSЗ ƺ>ϟGyQl? O5~Gˑ9WBO+a%Vkm.+Xn:5d> xGkҺ}kT4{+cx3}nRUX)ڱ;tn Fc;8/ܧF>rPlǞ px[ iEz|99;t9)y i+z΅7e[B!4զ`$cQ/ ْe#g* cżĨ>[,(K(xiķo~ P#+p+㾎k #7v_ FRHdrqYOw 9U$.X7m1?OG7U֞5c1ĻkNכt>ܿ:||@/w@l c'P*X7cuvy/O>kVh!z:@KB~Cy"ֳmCy꧙WW$g3DsM~ΟEMgˑ(Eȑ8Xq\Wy~ D^x^YBW>1?uhFo~VZljEpl}\My1&']gQO\>WF?DnMhOnQ9ͩ"+L>&-}/k|RkK8Gxŏ?u6) %GG'C8zyV + w.Wk8'< Ft?Pɝ]O' zz"m˚'myԬ ԃd Kv]K3 uH\ fqp6=YϏd:WPhtQZa9l A2p4׃d9z9MUGA81P|26nhʛd7&osΛ hAp[ ܙN鶭4:#e[oN7Mqo?7kO?`fP GO19 dvY-]:Ic]=k a5q;n|_9YVaKw`$l ÞN66utsoO45:~MڏU:$es{AYS =/U"k=?m ]w,<:eg&D|c:d XX(z|UE)yZNQF0eJxk^ӅK?q߼ k#8S0_G`S_K;sO>mo?3zab[sJ0#$_Y}@۷R[G~ D^xx/^HJr^Rh@V +P {DE]w)kQXCFSЅ-:^~<3+;gN]9sÍ;Ϝk.˗vNxs9nK/=VJl m9s&q*9y2f+57v.> ڹ;O~ X?=ϞmoZQSQ`kyGV +w>LWǬ~ D^x1;nV`+;;|N`S4xHһG8QvUcܹ1P.Ҙ_Uιڹ%/9}u;nšr㮝3rw_,}`Py\~KO<ܗc_ݹC;Ηg&S츾LNڄ)'_!*#_͐]\z)l! v=_qC[;Q}^,Eie^QMF.^(~n,AβpS·6_$@_.egn?|[TO 6;d;{e}W@V`*}p.i cz3qw~ͣWkۚf|X1KO?[wNpCy<֝S׿`^P윿;o}Me#ȟ)J_oKYMᅡ!TTԴ|T(_z晝KO>sR6)xj?\6ڗv~ǿuXz-K@ׯ`[uy\1HAD<*йƢ\ѺUQ<\~!Vy9lFSzY s}qܮ~ f/Mo^go<{wJIpN&e됝&{<}޿**}9@|f=UF^s̓fV ރmh+~ D^xps Y Dyo.lu=)?Z|=C_gRi ;׼澝sU;g~a;!w(7)]|739_]v%h.KcWУn<;ʓO?vWs{b>BS9s\o]0D9Q<Ss}L!IM2jz|J%0=m:&^M=9_7\'f: p͵_{W;ϋrmW6 g7:׹&=IO$[Y$kd˖16F0 &Ni!R MUSՐ86X$Y~ڿ}g=[}Zy[{8V#t4w!D)z>$G`L~!ӎ::y e;gK/zx7煉kы/ \O\}][>n)UeM"$xnЗI72E-B$%bxʪ>2$_~핰zp8ͧgV8%+eD^X#l-LT笕7Z>kcV>m/[ٴ~n!ٮS8"7"Cv Z5sEB>y?e0#w0swN=>+&UJBRf)xئ%'Km8eg#`G=|`g/>zG`~1q)t7ǂ~s!WkSS嗆w!_/k~$)x?=HAro(NA^Fb/xDX|a›g  (&=QR+τ]#*}+KcuLQxlWmaYQilB[$(Evg 7m;/ Bb!w fv`bgoٗDLˆ v a|OtY#AvHyvȁti_B}ZOc jccx@S!#o {{ h:t$l풟dã~,ÓuAtٷXY9}*g‘O}E '//-H|sI^Yb]Э/-k[hJhOI:Ԏ9J\c:)#\g(,쩃q Rx C ?a݊xB1G'/0>>=>$]`7D]B]IyذІuМw,{WwlQdž?g~~\GpF_DQfdV6#Mmnm1 IOi& w7]>Ca[ ]/rql7j'v7 =ް 0+/I( PJ9백%CYG A@v`~q<6KpzC~ 2Nҭʭ-yRR>Uǡɿ$/={~,î cW\#I&/M+0zn%;"u'bj&L%L8:̷ex) K̏:;E No]^|FMJjkyzLp#M,v*g@SNMa:6_8_Lf8{ ('zNjY?)|e0GA"O_se}a?~tq`DnbAw1d 0oRN4I^=nlFSN6^bOX-W^>8R6(I:1!oi[yYQvSRA r|*Vt"]A6ʋ-~N,Yy,wrmc{O=·$׸"byz #۷Ly;m#/)6QI^[O嬸6Vܖ=h>&}{MSZH⊤_1t/qht09;&'ÉoK~'HIM[!YRhQ/6+MZvuBc=|* yCaގB˘̣136K9(LJ1ȣJAI=,/fQXم}Yr|ÞG~&ɂll8[`hS\-QX#0T%33)_p%^ԧW^e=:"^sG Qy?fbx^u! IGBG&V_NXxey5lֺo'(yMb)=eU.A97Rg%Ѻ|r$'rTg? 9CF{#/X&/~i E0!6g<.Fz&gd!<0wD\w^^f?.cY]ocGD9B P-?[;"l#ղoUОYhxƱ;p\8x[j(Ѥ}ʍFB}%kDZ$! DĪ<  Ϟ ɓŨ,]tIh>FX>s:l CnuHWF;ѐmBnyYJ=iGۢaM)-eҾ`8V詣О!2F8"G!D(D_qMR>c]x8 ~!`7%SO\C7O<3"E#f=b;%Qs8YmI7hp*KdO.\ {8_6$dz xq6a8#091N>lhQ><~^ rG#dVh͘ZB[ao7z%ް)eἳ1+#϶c[͎X=mPDA)QGLշ?;m]$P,99<0u5pݡ/<酥E!z8 A!٭8|`ʁ/ɔG кpzA lTuv<( iʫr;~DRGEa;꫾ wyV_ՓѨ#$=ZeKIlEolTu,ZT NL[*^ !bE0O4޹ gW0QЅOL><>:s`Xv0^v͝k+_:67; j hZH[qmi SN/ו#- > 'ۙ"e|:.}L^#ra.ocDCtvm o{;Bտ /r9*l,`!)*ѦiPYJRؗ_ۍ4Vȧi?Z RmBX:q ǏR'/ehbqsYY Xe sw\x굓'O "Wwlѫ)cV#_)zp`^Fnc/.1́k+=a5fxmL~ޙF@M$0²*"sC)\xNbb6ꭎ؍ވ^:[Y'lD]Pǘ 3q& H)#]#@ YjH_;RF;qKH=4bQb, srw]t'3z#iΟ}fgNm~mߞn?ga#BltP >Yru+Q@,no1X0.p,V:o>q n7n"VyZxH#vG؞37ԙGbdV1HSq+? ~G[&3}X}6=#[E\~柎V#PC'&& 7/r"/\csI'5 ;^}%E\'vbUԯQ u]c~E0*<xـ .:t1CXIi0wU;/@ypG>/Rur!Ϻܙ2~aPri"jncM?P ߶53goaHڑ wc|TR>gRز7,^: m=.#n⍅-zNqa3'_˿Cw|앍w MZQ+/B'GpG`! >) ܬ JZS!1 )[EQ8N>}j:ȫto:ہ {Z. iLcw.Np< yCO\mc %G^'Ows]'E܄U>G < |8;8^GpDysZ[ԑ8VF~UV)>/7/#%}2`hĸ!r־)QȱEW_fwMYYQ={9q˄3> &Zf2ۥMpƢTmaO#9;=Ec"r!@!luǘ|,dB%}}W?q]I/FFⸯ#8#wdn_&LAOv6m_і"=v=>?/\ *56Bivt=Fnk'"ikkxghN=A-X=}qD[`/Q@~X |,@ . X 857o{ݏKulܓcX6I*jm2G < |8C/ .:#,4EAIrq?='=Bxۿ{A6"bFY)yͭ܅6>۸9]Lss86P$_K^hCFyn1:m )pFMWQ@킇 6|_:ڵ_C,]Xg<|@@/;# $2?ʙҘzPnԥԯ_+wm?'Q_u-F\F/(/"E,.Ib  <6M:LʓB^URcj]~ݴ9&iN c }mEF(ukoe;CN/#8!d>r7?al3d/vDȶ9Q߉)J>YJ]EVeon GFkR^Gh8ݘ)j!>[/ ŦVEl# bOuu@\X]^w}{E"-i8lK|`[6#8@' < I!:+:}/ųz{'mrG@yۿ ۋ#m6V-b,1%lQ8H>X['i\Z Iq>7}$+,hF <6[Km<+g `m E]<C]8@"@}i uD".GIQ꟎!3!ۡ>Gp<vfGs:RR%6yIPp-Gn`;" IUet7$Р6ل=]QGA۪b}n7@&СX[]'ǀ$ 'oJ0< cC)Hȧ2(1B5 qh^ ޳Gwzvh 4:iض۶;#8 >?NvӘ]1 ž {>ms:*9$( Mqڳu\VHXROdiiKmsuU[^l)և Q:Bl{be\bYEP[0{u^} 8@_{7dx'lp7NMX00sCѽc\mkydLSoe9G}|{㋴He?N;}  2#QB%o}C<\]M}ܣ\(`WC h% ӵkQijPV ϐ"*eA 8CN /#8Fr{;B1llKc@}#aϓ?.I8ucp(]38wmG1F3yri|򶯴]+оEx> mvX`; qA:+GlGӖwFO)_JƆ>('hݎmsC{I2Q@Ut@rKfz>#rڂ" } 3y _,vҔG݋#9pG`+UrkIvjk'9lJ^'^. ߮/ĝ#rG`h 1P$m\/pT}[lJe~qt7dL~YOPh9Nu}@!%O _@ @]e?>|Q,%5Qgq-b+  } q+*1ȱ0rw}D 庁hnVUuʝ:ϥwpG,8cbOjۣ [ڗ4VRvφ/L|{1/qRX;pnL@EB+̮d[. $Md:}u\ ö1džzZ6D gbJ 1 jZ6%'x7E B=1C8&vݑWG>h춰eYIomw܁?08#8@:{CʘYRjo$uf:{0}-aš!ڸ\S|cBg΄c-ltd_*-.$bd0`ũD^k'`#i.V'FZe0jnǬb)cC & w Ft?+˳,ҭW^zd)Ƀ@,#0P@#8V mgl>RL@W%_ nH025-b4.DCtQ}L:YfU?yյCӧCX]>5ob ["\ s=qpd+@֭x{^䟕 Tr9g>l-1k~&A||MڰwP^E->%1 PBmnƃN}ițw#ݼP̴ ASYZ;<nxGp6;o7[Vgƾ׋p ԔPegtr 5\-kϽj$$ !xnЙ!u+Mi=a?;vuю1)_v!~;R'VG^v!ukC^R~~0HӅC{_{b9*fewWۭ*;ŁwpG,8g%ڑb-~4~0y_~JgW8OtY9VX8z4Nދ='=yZ'<(Uus2ڃihƈMcSu3Y'Fb ~v؎Ilp7cK~꬟}3ɷ@?PwRn18t7xp@Ip@RSN:#8DNlŅ]73Ҧ^0u%aϣ_-hkuP VVO,XKPs6iT4yU(̲n)xk%՟hžY }߂s8<=dR>HcXX@}F I 9|u1@*v-1^GgF{/\'n=4 ( lIV#00yGGpB5R 삷vm]}*o SFc2Pcr'ԏY3X{nQv)H?[6[].Ysi+14[Bθ]J@^OIwnMmxnT1ۅƆ<3h{W:f>&I {WO@.<\L_t'~'EO6> (:(d9:u^GpG E錎-A-f@'ɿkHY5p  %wV|jmz>@acXp@)T٧a2qΘdEhن01+iU1}?P(io3xWB @ x#?Ȼl`Z?/" kc#8Naggq]x?:=foW}advJ0kw{"#[`7jYnl.u6/؄vGp ;Yjc]mEnUgn-Lr. U!aCG`$qrh8Ah,.acugw01dL몲Oz=\%O]J-6} e[$Q99BϘ}cHqHpKD^d[!\iQib( Xvk/E_nS*&Zh?DGpG`G!`giv%ɳ1w=eÉdkLgO3_r8կJ@_ fH!#_e}<&vw+ũB!ՎqQa<0C(y-(#NZbLݴ.ŘrP/6nmAr@um3q?|/xliOZPWaf|NwŒM R.0HRDKƓK mL!K æ4TU ;Ũ[cea㧬\ \7>a폕[ӌ## 9E"H~/rh_  ~ѷGo-UOi܋#9:uGp@7vlϮ0y0y 61)/=Hvۯ{G`@_,ʱ#}6ykebV[7ezQ;^N\O3To۩m zU*yU6.ybv[}`p< [v4-9VA, gE/J'lw\ʋ]%UhOujD ޶#8#p0ʮ@{^O~0y@/.7^!GɿB~bgkr{rcښ=zL;Ui>QFk,&V1JKGsA島b&i='+b}X~&tA_x&H"%c=l9 #<]J qDvLX21Ih/'yŰzccrqSk_Ibs*#)c&.656q F,}ۂjWͽr݊b1gB$hg5&TZ*?FqopG`!iq3c0s;Z:;lcPdb"8|3jcq c%͊R51%VLS26M c@N6˘]}>`_ךs*L;`w0j$(E|/'(y1EѨS--͋EU6]زXyN_:k|\opG`X33O\0u=kQ;tK/ Փ'©O~x~1̿zX-[asQ:9Z&/gGYNg8)cTn #^m&ƢTD|} /65/X ~;OXD("ÂReO8pϘ4e8q%aa(XOG`[#,m2gN~//Sv5-opLD o4y)LCU8躟Ի3m{V5ԁG2ԩMa?Hq3VV&ZLi ~Zxb۶E0ZeH(/)%R،8~/s'.QM( Y'GpG`; \]o):$3ETgȾv =51.d/p鯅S~Gj#w0!U9>6v&†|jz} %Z9?pfjیԴOӪq8ыukC]>?# (t]ERqCA\Ue|٦exth}7,|-LS4}BQZbGC, _G>sO c;2 떇qZ/8l6{|GpBNτo+㡱^UAh&=B9?IS u-#aWKG3/_)wH 9WŶmyfbMX!c+d䋡$˄>H Qa@yZՃOcd1Hm8ҟNJ@y>x_>"cRʶE\,ܓRf@|iW}%(6ҮO'X"tr9u)pNGp@6U/FЙ)gnPUͨehJ% b,8a]gchk?/u&[1d%_$eD^d&CPzk%'Zھ[=yrXPg\aˢ85C$9,*λms?r1{'sxM\:4Ϧ(OG`M;# x_]Ņ0q a p }{&;(zvC!N6#AVLL/Ha/gOչ9Y2oc0LwޡNeBֳ>0+I$ҘX+Ij~W[9G\"|[}X3h+ 3ïs=_.h(͆b ݔr^s/D8#`V#C@52ʹb3ل̉1-NK]jEd5kr.l=crq aızHX9\>,ao/57$׫E)Щ0,iK sKY:~ud=§}IlX[˧PֈN `c}a΅Rb. 6f JQx7#luKqOxXKsGDw2[ 孭 [78# \O]r(&cNgd?̕yY/ :IFF2نxuʽz\;~%MC#1?VOӧP?sZ$?3OY$.f1Cn֧/wq.F\bK:viS:6,YQ?=M޺6[12RCMaLR ho&%&HAcIAؿo gis3r ?} #>'-UE:e ac3j5G@P#8Cq2 koImt!f M^sb*jSrYU:[51%mz1z]~n@_ڹ5&[=y\2f\DPrWʪ<ƒ<"88^y),O;(ԀdC5Y(wMb#m[gn(B^ۭ.l>0-oqQ2[ڠ5=Z^ Ej"wG}~/Dk+h+ptBvȷ<]j#uO Lc?P1x=lJ#іT-hOP_-m{X[ksrnyAB0X(Mz/# 78#0 4jxhP+ިN`a}fiB/hܺKa[OVPB`&p 6fR PGq SeW EK>Q*KVD9qSbT*eZ5TG܅S:v,ga6f'&h)r6 )>AmJ'm۪. QzS^XSAchl)~N\  9͎{b1`ԉ#~OT]RU32֣vU5G@P#8C@1 7F'ǃI2t-;/bܧ^Ec-YM.Yc).ky;϶5:HS.~ʻA -_Ja#uu1N#6H9l&΄FN_j n1}>1fGt2|3"aҤ}؈x70: >#1@ Ö,|DG w $Åu(yG:Z6`8#0dma,XYoO}O[?#Kh_:ux<7ӭt K#yF^DkTnm6!ܘYGv:ڬ"6-Ox{0_Ac:?U./S{X!FܠĖ>&dΘV0-Hb"$Ĉfn&z|%}K}Џ<~/? ;).+( 뱶N2"y=#8#T=pif(ŚdnH_?y",+og8Dhgg@yX#ԹqkDy*uBxT)4iܨcU[Wc$ڄ&gbb;[O_wnٰ:ObR |i#Ye𸎀#8P H;Bx|r2cn62=?g5?Cc|*_k2#uE7-# K&UА_!QOLsmNknC_: ۞J2إ:sa MV„ )&4#dgybC@*;N*8 11Gls}XM=r/_fw+`pG`(fx聋=W>Ngq_0h,mK/>Ưo}D>T28yUQ'Ta mue@D6Xx$5}>_Nk_;6t,Z~\r& ,KPY4^H'qQlП4J[??$ڨ ƃoZ\ Ӷ :}yA&&ą>%ۢlB=;ڄFDEas)Jp6 `#8C_=q<]xQcrhF4VWON^ G~76)vxqb:,t=^L6kj;܀`w(Z+t}J"Z e82i;Ol#^! :MŒI0¬M[l0BR㮨F|zԓjһX_k}%31h&Km]Z?Ʀ;+wGK5o"XەְgټSG/@_` #8ÌN8~eal~}F&>?"{"~_엿Ƣ\( dDY0u;m{1)+@mRXmIV>E6HXirx:r[M۶&'Gii~FI|XT5=ZєwvGjlsat|T {,]]z0 ?'"1y%\Y.(yLu/@6c4u)[o2=>,o&;ke+cxn! iEb\ ?Uw`2 ϶@!- .n!chCx=& Vz7@:7IQዎkliv:+s4|`Ӡ#8C\/ /G6:1~lKV8q^k_m35s?Β!ncX['Q_oc^E? $3-#lF=d醶y>XVbo,G@rd<&ۦON/ZY؁8ܬ]>,MYSzW?t`  ظ@s[>W ԋ#iAGp r^Zi-`ak喇 \#1*vA6*gg-8_uvضNaRyYTS%Ou%+u|KgpNB_Y=h;6V/<ڵm|:R +)tyl if y<`RL&궽^Ic:ޅ` (#UXAIۥ>G1n[8|$3/rwXWt[[ # 8# /y؁"Mg BǑ]/-gl郻)MZCGSӭ2eZ|(#\>[vV*Gzlvh1CB2oFj>}eΔGAdE tG*#<6b[|\<~TԨ0c?hUd[bMƍnI`&jᩳ2ڥ@%q_i GpG`!`]H=k.8P%-L/7/F8/'df]L1 X.xBI> eJ7k{m*f-vM [9n?웥M%-`m5ϾQ-u[]^3aFVx-,* ܄Q4ԦA);N70d\XC%@tA  }arxG;guGue{ rL7_}L^xwa}yG/@_` #8É@s62;;/Fpo2?KaF:_ 093cHn6;{1gLRTYu-m}uSKsMP?):mac,,(Cwz vE(xE]J?e܄ -%4cjf,ќԸ, lc {LJ}x`7wӺR%ީ#55Tn8#KadiCek/bXzk0m8n辒v8esQi{H* 8'=:h jRl5c aͭS?`BkkoMe8FyGŹF9C[WA2ԭLcuuwia_/A]5 9_b_:=BQGMOȼ8}Gf:#8kK6R> FM aKͶ3aL9[kf'tW؄XXfMIܷV ,'֐馞k!:˜Li(5W'СC;,eh+d#bxŀVhkWom7^#cpUZMU~U6.w:"3#8Dx]M^7yA`\r(T3ݑV_=,Fej_J$|͂6;&&iҳ9Hi`MXb2s=i}}uq7ɬqsS-eNĘ=3dz fq!6]]DiCؗԾS퀒O} QplX XOYN"pK:8# R议*{YjX=v$OZ gϝf=Ls{&E6#a+lDޢ ow"Y?Rm5X@ɣG:Iaw\"X_1I5|m-mj/0hp#.;yQage]PYxڳ0a=#8P"\;@ٻO&vq.^wG. ǏܙG.ijq.Atc C8]f Z@Hc.҃^Vmԁ)q%O8xo aۤQ% ~CG$.66x,pǁm;**8sǏEd@\h,MyG+|+pG`!Yx`ʃ Td;kȋW'O4ǜ曚M҄dSCXMK-=p֊Vger~(VoZN ҭoU;2s|'Lmӟ Խa-"o6)EFnJ1ا`xg}s> _`ٮ$h_Xn8#ךݭvEX=,]cU&$h{ unqKLNU%? {{kF4}S/ ,W%؃ھ5ٯYK:򌑣3tNMg}#gAU}c P XÜur/@CGp &v:\\h^as8[1ձ ;2cJ|VUS}Y_^~!SI)9kvm" 0qVk&4SRɶxO^?<b?!r.,-U*uD*q#8FbFFȮ6;9>5e X\8g/,c:nHPѤصZ<;"arwJ~h 3ZCkuRJh/ L1e=F? k{{=DI2e`g 1v<ɆQթ?v|'kf1i.yۜ5X;_X7t8#0S8Kv6-dE~&|[a|sՖp4t[ҡm>7Y+&=:g:Gl_+-v&[UBub~p-HŀFXZm:ur/,OĿTxPj| M[] 9/lpG`xx{x#. wb'iWcLrp!Ũݞr7lk܃E'w̥uD#k9ۖZn\b!VR>#_X GpzHwW~dÄS7w:kxU^[y=ޭ|nD\zsPXk~vsNXRjy' =q!Kë*m'6q;'2)mj!(i=J_07wGit&]fz; LO IΚsٿAk{uW,Urk"XҚժy$o4VUW^M, lwnll87BfmS=Fe*

Gs@F9ۃs(]/%V P&mgHڱ_2wpGӬD0zsvpxΜG|h=[9;e=S \61gj v1};1ab0*z.9bMm=baND"GniS}lu+)|:}BqG6"G'%Μ]P{~LX=rX^RD%э4{I(%ڦPIy% 9Y ýTZV֓rRxY5J O}-cYT}Oy;_4&|l,#;Qk#'=tLlwʪ(pa߳_Sٶ>;o ;`n0Mҫ~N5H\8#$p]&;Z;Vi.mU%+hH}jzأ w`M@!5mI u(q3mEeBۍ2^ٷ1l`1m·tkp9I1b@ KpXWZ[So:߁ڟ.b[>jv;uzBzˍGpv$[ls=gٶL(ͺ۩4sFNqr{QsD/O ةM:m=us?AmIV<8@)[kU#c%,֭ؒn|wF_ wI MHa 9u6/lB8#PdV[勭=e&;tV l2z;/CdySYђ1Sy/t ڑ&h:S_Eaςxl,,()>CjU|7vމcw@Ҩ.W9[9 pGp#?NnP<1UM(.0$_#}q!*#B֏bY#E?4-@նn=:'۟(&\0HAL(׺Tl?`gR6_cX5EsaVGxq6/lC8#8CgݜqQ]fU?25l9}w-ӓ^& {q{D[U1EG O(b iuǪvr~%}`"$@c 1Zj;cD\@IDAToGS} *MFRge;5E;|` $.pGp^3QclIn\%6;OL$ZSl`}؇܏Ѝ!J)Cq0ZO?‡ c2S}bNz,{'d 2ځB^dy6Qc 5tegv@;t\8#8=!`'=91g۠-&iv>׹W#9)ѫ^c۵Y}ZIJ".6_?EƧ|+(TU&I9B6OB؛7 96yJŤ8]Fv;#8F9pn&M@tx:Dbsu_r{$'kjySJYk3mTO;Kqrlc_}CrՉ }Roi;_66OU!.#nU}=ss~=_/4!M]!֩#Ln8#8;;h2&hP:X}Jel Ѧc[1Ź=]9l<#EpGpENmczYַЎMt HD4 "dZvhKk[֞qA/2Ɓ,aui_s}aOJNS;{yMɆ< 9HLKZgMdK^~΂ fۀ`#H$PhKX)Ep!D+hK&EA "DI fpv۷WTS[WuWW=w'O=o֩Stю``O@]y5x#^U;qYIf<%5"-i^kZiӌ1y1nMZa:G덢Ҷ4rLcʱůćFuG7GY<C.S@l ă a|Ud¥wq#IY9|D-C?2r |'@Ca^:Wz#iO_Ѓ5{*=<=a&?46lA@D╩.' xN.]$h5Oc./?=@`VHbb5ږnOq]y?McJ˵kb撚H7/G3heNt،BJuΕh;9Lmc{q{mTn;-s)喕j:ۜvO#J.{N|{FD3mbi(VlA@8E~z:h\ҍ7Q,7NVhЪ^eyk>Gw\}jc,e\m%^#'jA7vn0+|em\/cu{.mhCp @&!PM>IIr6yASe(A r]z3jѢf`˚͸2Ŋs{8~e.5t[:5vFˢd:Jtݧ˲8ov;ǜ9J۰Xrl, !@'%/Uוe.ՇuGdph淈s:9itbYj|h(ˮ/w{ұll⛆˶^#[mlaV*-qqTuNx_9|.|\l&]vݿ5YS!6QP @`3lKykkz9Q,wr<N"RYw\=he]1uٕwv9q|G\S«˶ꑅٮ,}$8F*^}᏿{s ʐ D V麮RkWۿl[~J͵6xyx0ӘD>qi||u~%-쳌I{Njkmx3>=#EbXG -/1LR>/|%@}ߛMKbݝlvh1H H: @c蠋mʫԲN%<֥;/G9yӼɤIoͶmqFh%Dݸqkq1Ƭy.5kk2ފVJQb;Mmq7?{FA}L}Ha[~i AlcJ~<zw,6KU:PMW2h@ 4*ej뢹˧n|<<~M9F'&6 Rb}xhaOQsɶm6mg]hx-:M9q(166.zJ!sAs4TPj b8J ]ګ/ŋs_l25@d @8|j׶eiR~qfQa؋ޮ}$ 'gQճzĺ8Xjn{Nmn[A~k݌C06ǔ4ط`uo~_ mmZ d`s (WŷKnb8K$3M\?Lڀ*A,kqck:'1%GdY.;Hh$1G\ F\s.ƌV}q[/VMX/e5ؕ..և-%"mT!@8-ۺv]cq]xA.ݢ:߳WgWUގcQFxinSey7sYmzy#mUh~XlVݭ7/ujÊMm/96` Y(duNf=y9D>oٞ9Ӹ1Y0[XM^m֒tQAe<ڜF]Ќ"FN=6,w;.KVj\[M?-م[ޒc+ 0lZ]gI  ϴ!@" {Ǐs5{*߬WSNNɟ2n襧WPgq;lԗqz=6\v]3v@ק?j=5՘/;l^=FOqGr!wLSW6q@ΐ@SW`>\wi١xZ:fT :K,k{+'_=GƍQZۻ~=^5uԖ.\_ ]xx;O?ogutYP.86| 'лƉW^~F8kz>ؾ[*e^j6iJǢ>buVzMt1ۮ<j|m߿y,V^],v]eWٟ'@]I$eW1[ikiRgB5Ԝ0t%]+kiO7?洚FvRB~6NY;U3mƞ7ܛ+b-ռAr~`ѹ;wnth{77Wr덴ܗ7rֳϥ_f&ם!}]p2^ǺbJ_2b=7a#.Od=UTo^:wDE*>$GQ?OuAek\cL:n*χß5W_N奻[ɿmIxKN+%?> Ւ>ω~t7_=mlimJk>},?`ޜد˟մ?'Uj+j2p?U最lRGd ";+.΃7ݵ y.z{C~@ ΀|;|jx+ _K[uyh 9 &S>QXpq4qlֹm0Ʋpތ+2Trn[_Wa/Z9q[<\;tx,O_s\L!؅umr)!`C@)]ኺ6ִG}7?oK+Wɉ֣i5ϟ ʯU}v~*ĺVK\mhpעy>zY0֡\4kzKR6|w_n{ɴw習lo[_jKH_{>|J _B1'm;?$oI8hl8o`xH嗢ƺ@:8@<ʺK]R/]5d='C.9 %!&C(.tJLk׻+)ץi}95L]N#i/6wҝ/)\j<; o 4d#'z̐1$3x]|ս e*Q:K)(Ү58,Ggs%PVqNv[r`&MI.AÊ!p@5Gt!'tgƛ$D$(ݽ_Y$UC'%p|^7w(tGNWV[Lݗoܓ.|tK[_Q!cףnjńBmEQ: ㇘<ûRy~ԼM[/Ѧce86W'zKǷv#oX @}<8X/<0` )C |u_ѥJLWHޟ͠d}P?]4WCS 8:{qSQ'*ug;. .P{[g~/EgA'_vဵL2륺sb 8qGElGսzu{H^9mKyڦXyئ'@,m;Jy}59r~C#``dTB@~yN`t'ҕO|я7Ҫ觯ğE;\_{8G2]x]z# @ՙ)fe@0VKd/Fve{ K6Qt=2GRi,W&,n^rn{.59DJ>9In{nj>Y'x^oKvߛW;/ã,w`.}N[myNj3%v5e1m:b[>;- mQR,'&Ƞ4aeU꭛ (r$m{rҕ,3=@]h/+p?|ι}gWyfͳ1V;ߓG,F3ʤC(c6|^B)'ygmTZ]({klLVӬj}dz(}ԮSGҿo?'Ϗ[W~]H>&`8$ +Nr=X~OLc _h@;} g|i]IϤ|L/!:2cm@\X?yaqcݾ} r\O_M}~@e+m9mU&_?'t2GJ* 9e!We' tt{_TW>|x.1qW2 fO[9}`Lr%_2Tc'ӏ>(WgL3e(u;%kۧQ&Wz-\w]2t@4# zMKҕ}<]wz4.4CS 2:xG~_|I߻$b0΀Y$gYtWf@I^%;1ʊRzE;K\q;wơWk(Mk^W c`U86Ng@ J@IJLWm+inZY(͕Brb+?ߦyVL(F"?m4ɢz?7.qώU\Ayu~Gy%:GxźmG-cLDYK_3i9(t)~Fʽh1Ͱ藐ߗK8u @{ifKDZɿaѓO犵uh( t~L<G}?fs>7pҡ2ֻu why!0~I=|VԹ=z_*ӽrEz=ʘ1tm1,w >zml !CËa䲱.S!6QP @NNL.䁴v e68O 09\ɷ뽕~_p{Ze,ә6bpN &xFgV+[#ƱoW=c]#WM7rO!tc{<v WG68F'1@XR+ϕWҥd|[;٦m\ѴwuBp(rEַ7!-ta ~.Vb{:q_M/_dzV^%w_5T]cͽ9򳘣EcxR]'OA{ikW.&?DuUlߒ 0 m;HgvUbS1b&Ѧ=(Kj{GUu97|Ǟ۵Yei{۹-;좍];^w}\&ֹ ! v@I`}.면oˉYC`:niu}#]G>{_ǙBp'5~DŽf5uj_ԏVZٶ>bާ-Tƺ݃KU]>2ݏKٕ2(mK㖾/KoX}]Cz>HW҆!@@;J@|s,7?scu%g.fk<֕ &z:ee4^Ujuh< 2zLv1$/fb:8lSڕ~jw޹ @QOi6yiG{p-6S 0*Mo}25?W&"uA|轷Ǿk)mwf3Ɍƌĺ(tSuOE*z%]j3BQ&ؖ~QG}zfWseX1D(խ+NSu(ؖc{i,'!@` @D|5$hm/%`7emI t9m'WYMeMh;d#u',2jeͷfWy|*c]n;۵8lewGvwiUٶC 4@Xk>z$u{O@d@ɖۤt=Ih1u &c&~P9ߠ.5rC`ǕJv,UwYpnz۩{6hV?oUgG3o~F)G%O4چ| rb-ങ |Çj]p@%uq5[,"c-ݰcaU_&>ڷٌʫ^y~=cSwejVYM7Mzo([mH F @5+m])_V-rZ Zyk+Wb?/ɒa+Ti[<]NcY~[}~mPU=Z~|O4~(jchFYxLgAdYYx>tM/(mcEeO#(W/D]\gUd[l_2ۺ#4ʣrk-UPlja ohn&@8$YHUs Nlk5'˥zmtf@Lm5tۘZrմŕ]FbF~u펶doߪsp[zq@`&› VB8v~ 8#1 xP1cQwwGO\ L(۱ ZҫWZEW\6Q^k;Nq]uPdG~Q >b}!` ,2S 0翚^^e5@`tnwׂ Zª3 G2\BDSNIotYz5YO5\=KIdz8֫vYuBH H: o΂~ڿ߃[[gԧ-$SzR|oHReѭw{>K=3};zHh%g3a;j'f6Ԏ1U^>CvQu'嶸 z|)Q{7ng?m`Ç268 @o#} j` 0C{|3aO34$?Ɩ*jK[6{}14It^6m7fϦIzQclgrggg3k-e}҇6 ^)G\qq-̀@@ʷ|+'ADA*u&rL؎omdl{+>\ۢ1Ц[vMgDyf CivVN_6(h3J$f p/à p]]1JvV9}DXfF%gl?`sdA#-%ʞovR7ƥ֚+B8bҏutTv) Uee3Fer;ݏ :blŘ19F`<9-siOۨKdvͯyII+{;r$]hl,29@{?oIP<p2xA`t{ɩ˶He:fw8h~ba.-WcXbܨ'B:ݪ)kAIP]((W|m^{7v!066F 'p7'!z$ݖueR^,>fY7uܗn~wΓ,f>1KqЫY^r:\v[q[,I)}mcmdNmo䥯cJDZKZLgI˶-e#cz:TL |BX>ki ϧ_[dKY>4S'T<bWuڼ1փBWͥ6IjȩԫpݖT[}6*k+~MlGnu&Kc;?iT30>l̆+Q!_x=mL_UW:3d/{{9鿕BS=Nkc4]dzlG|G鄼Ouemc].;m2M9e1e*mw\ qgJbp̮ͩ Bc``,\CXێoz}Թp!uvS) o毦;'$puHʠL@Sί6Eq~mVoYDӚQɶWNujnGF{F}ۥm+vvyFU<0waY,H xPBXBOwl,'+WbK)C`#i/oO7TZ\6╽1uLcubg~ejM&yQc6F^t;1LJ뻒Rf?ۨm_:6깕 Yk?ݟJm9,0E8   ?t3:r;4vy/@`9?ȷܽv_Vn/@(P2ti/wY[ʶc,b)%+|v=Z&}]Ĵ,J}$\Jlc]:Wa'>CugQAw۲w=ZP0+ą p gn~)^|$UHWi:K7_Li5?PM'!R8e}~t۱n̺REmIuDzO6~''إ#m%vJxWgy_Kc2Su:@^n~?t X ݷZ&`uD.Kou3mzYzo۔pR]+k2:h[s;&ےYn_]Xѳ"n(lv|OM iXV @}ʿ_g9څiwY,˛\,3hn[O_M?JnJM[=Y)]dcqZ;δ̻1ζ]Ҟ%ʃbb}b(wiJK^#yexc[2Q 0mă HJg}='Zz7uZv5|۞L9Qmp@Ala⥴sڿnZ˿4D3| $ڸR)uAOm>o=鸣Y.e#YF9bJc6,wlʺmg 6ƌ[6#(4&ZlA @]J}}ʯrZȬ>`Ma+J@O _+͙fl?'wk_i55{~עMYW\ǶڲmS[,ed`fXD;KFmҎv;DGPNk5YzQe6ҹm1~-I i$ E&;Ͽ^'8Ƌ|<]7|#fT`n#W晞u'^JzΫ[]:mL(_ ֻ6ն]YJCkHky+=ד{2~lgɶKM^>@k'햻tBeoSu.]~ٯmM @`JH@Xx9YRo^_i[i'S'|y7?5P]jg1'x%o~/WV66+K=^=P ddټ礲&n;llǺT6\bXŲ(\e+h[l8'ղ,ev+Fa[@IDAT]GGu)!0MlL& ,:<`Ts{Iky|⯥KdZ6cW[΅A@t{F}akg'r/OR^79DY'?m'Yԫu밼ZlĪ䥶ٸnc{D:&]eWdl>$'?F9  +tSsg ]O~o{kk݋Yb@Q)最s?'Ms~c3~Vڳg4,m}ۚ'<(:t;|Fz[fk]}M8KRz鰬.Sq9NI|jk MZ `l% F@W*@trVݜjo |{Z\vOZ{-S|ٻMzHw/t<^h #s=vKs9r|^y%~녴~>ϟN[_|~_vsʸGh2\m{l[+ef&YD9f=kcǵ3 ٩W>6fEy櫗g>淚7ݟ?sjZ>Yc1Wv)sX>ϟq3m}Oʹn}n&'d1$sۥݬ~˦ۯV:v-,poigb]QbX}[%-3 pz'wݴCywZtϤMk;Un IԱ隣[6wެZ9-q+V*&LF?ys2oϋ.ayk9k_}9[icLSb}tCoonuy(*ֻtX%Xf;){ɡvYϬU&֕hC l F#$GkJ77_M tڛߖ6y$\~oZrKsbSoOh.M ꔒ&յ5ݞ8 oSp rk35:wϬh;f WS~{/'wyvrw_j?N{J}KSfjjb*)$Z2}mu[>}%%o7Wc[NJ Ujﲶ%yvgS+7O>6|*?#౴viGz})Sss%\|"{Z{S;i{^цOAcPeaڼK=Rkw,ݗvt[}jhٕh68>ڕ1"73ڑQfXD``֜C8=eRy7ʳ_˟_ʿ~!ʥMuv}*_\V{Ssn>Lf5w?ؽ3AcQGHzo 9aʟOc<,,cV~p믤w"od}f o?ؾn391~-\}2>eM|ǂ|[4~Sڸ4ky.ޝg Amn*a{ە>v6ARڶḳ~nlC ,6k- !r a'~;q0n}tn\{i?pzB$-Vqo~fE4Û>SLB K5('|4Xo-~Zۥt:6E{q~UG[Xf~j&}P lp̒eĆ3%Gw k+|5©fcJe]}A _lߵc @`(%pNlxֻ,pR^G+ƸAde!.mo/{l,!@XJ%9\[a2mcyQmtYJV/}JwZ o1q3b@ 11I,]eWNOxvQWe=oA<#emeo1#o'0G@- @8wN9 )HnHo^V1^_ۭ٦>J 2lcm䭶lK_D]/e! Wc @Aɚ;8I,m2F. k=qmGȻ^Cee嶏0mcL(!%: @8ԠcXʭ\uC:>Ʋmծlx=8u.2me6   ':@ۻCO+Ylw-˸j5aJqMUȹqwVbS[t\}~~|׀@ 3'e(uԒAcY}qT/J}_iwb|"kٺe;ښm\&\/cJn|]]ծ > j @`x Y\~]ek$2zi{moS6y-NͶ6I^ŕ>mzljiJ@863A@@-4qJxe6H_:1ZY&~˶GQb̶m]~k_z"ϗ>6N9=B !0iadzWLmǍ2ڶ$懼Okҗs/yCum~ʾַMXy5Yh+| @5b" Jhf' cǒcQ6e*K[u۱$m)cqa{}Oc88k @MH2N.X&Q/_RZL\8نrt^a+[>_zq]F83N2nL!0.6%= @XBJ\D&&?喩,㏪v&?>!iT>عOږ>Uv,Jeм}57'"PkI+;zwP;  [y.ۃ|A`Eb@ LD qT˶>GźmqXfhc&w2ڨmrܲt >lb^r -hC`&MbA MLIfyLjIqMou8vj[.ڕ/ec8QVƠ i``4@ 0'@1s(UweS,ei?l ǥu:Ko2wXFg2֩u66Mx @'&0J"m<,zۗrU6ʨ'r>2F(wuucSh:fE Y%. @PN bTk:>nKW٧ewYYN9^ke^ZcWi3,1VLʺ%6fI @`If)'&C5_'YQ'_K.K}McFYMn8e8j; I|"?b8K۔6Xqk1,?(!P`$B @`ldFAд%5m><ɶlKcRqMlG? >nhCx=b5mV)Niر&S @#PB3iRS,5 T/-cFjRKW;lvgth+| @(؎u#%eoAsLŗ+w0}r@5lԨ  @kĸ-\>޶j[6W-emw`Z-HXf @` %Q3ChgyLbݶnazQ^3bF XUf @hK$L:KH֫l~^8zCI pRC ̜@Lcgmr8 w[|t>u۹uEhce+un}Y>2o}=6fϘ @Έ9&y#垖ݖmbLQfyf,%Q @)Z*Yto>˺_1VQ& 0ԙ-\`.t nw@ (=6Xm%s.ymm}8n-fR}q{h2̱6odO;N9=B 8dlG[f&r'eb;ڔ1qi9k,bߠJ׭``RrA 'SV&[+UTw1N2(0]+壎 ;'/ @RP򦗓9m0l/m6o˥䱮L#V-F(ůfX㎡˲X?i{J̒Kl@ S%0i2U%nEh#^1u1e,TKM E۹,׫.|X!0)6&% @BI7MXubmlRWڸ]aK/$v[/κ2vi˱Ϛ,m|~n[O Y``t @ 9k!;8mCzNmdz.ƒ]mdQ8jQTq-o(곔q!IpzB , ZVʶ%.<j.mNxD-ee _6퓖W=n\;V]\TJn㔥i`4( @Bp"WNMn;ku.mbĺ1dzEW]:+c9mҶ.ݗ},wi؎FcoPwm|sL/edl*0@ sE9Dܮٖ6n|Rwɑ~Q}3l߱te[6Q[G,KѿKk-βQ2}4sq:x1l}SBpZ @` KJ}l۠/DOuLu$6ݟ}ʶq\ʾuZYs,h+ض}rݖ*]1e v' @=ϫl[>JRN$?emJ쫲<ܗþn[_+cr Qz-eW^zn~Ѷò}!p,' @Iu:bGʶ8NRml:Jǒ>m8+~jZz.b$S]eԷekX#G%Űg8p  @8q;YI2ZD5u.GcvQl{C_ExjmnjZ]28c]z.(㹌qiǑ؏:wj0@ '2. &.]Wel[&K^;b Fc]m{۹ض-GʸJǩޥlJA~J6Ίc좭ei{cf':A~#$;um#Jzǒ; 9NI @ӜW9J5 o|.;~~GTdo})SS寣ݕsۥt{P9v~Plt0-ā @@&u11~Ɗv$?cǘ̺QUƾ,w,K[-s)Y<]ԗ}]Kaҟ6Nwe @e8tjP,%zՎ(W-(srٗR&[Ŷ&9>jvd $, @sDIuLkZb[E]=1bQb /n?aJ׭eu!0UaL @8E$7&GY䶉rץkӗ1vzT|җlm_Ƴe J r @C%’G]ڥ,ƈچT.j1[ ?ۨv5}(!0x༮  @XjJ8|N 7ʼnpԷXA1j~і:w 1>@ $P&ӄķkkcrR6oT]//}b @A mhld_w就L?A`5aC @`I *U%]&DXf @E> Dc74gt88+Ǹ!@Lv=y[y׼qb}C @c'l $68 @ sGݒ19 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @` Oet:) @` pU3+ @ 䤶'rWSK 2=̀~ @E ZF@sF?ڲtQ߉?70!  hf) )%!wtH+M_h3tI+I+WtfwPMYצ R `kPBUޯ[wvꕋiw+]~'Yi o@k?_|!v3rf}hlՉ>j 3$§k@z_Nul{-]Gӕ̀hϧڇkSsZG滿/_]X] !@8lt 3&t-Krg+]OW?i㭏Ǟ̟p|ϼs^DViw{S.ߝn?Ly9?@QU~E+/ @M &N pO^wo?S>Rurº}7'n%<غ cʛ#zum\6~8?L[E{ɿXc,@NA> @`N(Cѧҕg>L~ 2?0'_t[wp0Q ̜1@ȷ7jҵd?`y@ D`sLNOWZjK)~10 *tGA4@zT "p~{ޗn7.~j:%J: ?oҊ_`&;?q?]n~3i֝~l4 @`&MbAt?tJ~ߠ9?Um 3+m= ݴwL6No] KLȟ7r/?o/4x[=p[͛s.W^ͧN~SiOמk~A8_5p7@``qזAXN\mk?tT}ݝ;rrqFPl ;3n ˿+is쵶 LWASH@83E򿲻>?iW66qfVtʕSߙV\m6^~5׻NA<) @`8 5| @`>j~܍=?="zE}ݝz4]O6_|c]kC 'gH@8տ{?WWҵOLGimO6InoH}1upJoz$]? Im:@o p~V}=\6Vwtҽ?ʿ=:tj` p֍!B#G"o1 dʹՍm'uJ@{O>:@Ns *  @}o|`/]{/$ͧ޸ohfkGN7ϥ{otG7|8 @_ p:+->Ϳxg] ${g,S/7?rbkޝn?3 8 @WIcΒn?OqNK~&՜Y.ϔ>u>N~=j]N]Ng@w, 3?@ DD6"~|S)R3duiY?]yw35w~ @``tVXB?{Gy&~٢]\ncƦNBғ%$wOr ՄK ]B !!Z`1bY]w;+Ҏvgv~s'i;~g>ϋ0񭗾S/i31Rfrk=ξ9"۶]bj¹|,h@ 0@:|V* 0/JV~ԝ~fYI!xU -9Tiyχ$`h;<TCWɅtb38P'@Q3l:߻ᴷHd3 g:Uw~AjtDH~?&@I 0I `@axy4R{)⫭qe< `FhaGJ'Ig4'䁛&"!9;)@~l2)&iRkuo?K4+d1 />F~yz/hl"S?@*._* O|ҿl2%g-gCÒip~Z>j0 Ϛ9 ԳT ?#&@`#&E!$`:r:IݑJ'IZf?봏l2!zi$aPJ6@اA퓅7@*)0_}7\^ -Z"+_nJ֎sWT|!ڣ3*5(A#:4@! 9v2: 0? }YC} I%%4{4B́ ~없@.Ov [C傋fz.-WFU 4(d֝}5Q٬U##_`/' L>׌n֖I%8x#bFhɯgp ni(2Ej8J/T &;.%D(zs6@ YMS%r ^vjkE'M@gb1gigJ&٘ 0B @  D@СM^h>әq47gJEQ ":Q/@  >FP3\l@mHr?[ `7_0 cN8i#' %@`/~A(@Ol}.:RB*Ӭn>yaW:](1[/zʹ1m$w@ĭ PYIyf=$io6$`eÇi吨6E{z@p| EmU cto2Xj9IEOA;J9""f2I9x3{ʅF  N@&pgi 7i$pk}wϯ^n̯`VJ}<oNe#IS5thH"9j+@O 0ӗ#IgKh XkWv;:Ko^bا##OW83?MG">bgi @'@{ל#̲mSϖ`,k 7HOVj:L:vO)ǫTR%q+sĴӴ99j ͞)$l  X@@@b?:r?ª5b*׉O 7?64(XLR:""LJ*d<*}Nn^J5Zk. CޭZfҚS--ҨK{d܊|g~" 5^pv CxR Mk=#o'gu0Ϊ4$!0 >Q:|/'=vIY;kj3u2}BYx2 2c‘&`uAU027QPĢHڼ7ߌ{4 @ 8!L.[`\=tk?_P;렙a&fvܲQֿz^Y2ԩlZ}̗ |2-h|ZzzlO:^j xG-roKKIXSaJp, _ [DBbmwo@*@D `>Xmv_HG궟ZMv!/?<[䵧=ڡב)},0_֦?MĔN1[Hɧ 5Yp+/>#Yx[wO9beu$.z: RVL3$%͛r' @'@`|^ `Ϧ3RT' 4\[E=R+f=״S~)W(_Y)=;ŧ!u橿W3B{}֣}]_u{+XX#(tZ@"%6tmzM^{9gI# *k̴*2$3t"8[/ @_Uth  @ LL;ڐ{tvP]x?2?c7|^읿dN4o:DތE*`^hlp@W ':ˆg뤻}kRg+m:,Mbre|ѦUS;G[+@ P;!&`&g5]mDKOdml|;m!MX]v0Mf i3lzV]?|e FKj`d4h0 soI,YICk҅d5OG! U^@Cfy\Ruiz&'a^zA^|.Yq Vg<!R,e4'2i,V >Ws*uAޓ^I>AĔzDGJtY}! 5^NhlʽQ C m_<@&6ku)?]vv Di@g<%#q©VJTh.:\l"KL%x촦X# S}@_c@*'CM uq+\5];˓xodcHF'vz脤.!h(Ջp=rۿ]+;^[cZ% 8Ip!e0R&  C@(tȴQzLkT>iڌj05>I}zο%L'oVd`xߝ@IDATL^z~c-0\i2Q]uAGagϖМyMsO+@W@^[Z8Vdw\lӹ]ysұ~.}w 7g:,5L=$ȍryE/t $:!0ej] ]쒤@ XC {h&|C$I5D^Х^z b0qBӴ%\LJdˋ/Ȗ0vo5a4` 7!xI6mE*(5t3MFv=e&.~674~[VwXZ"Mԙ |n &Շiui*k_;^btIeť @ {#LD PƓN̙V *ʯйl 'Ӻo|]|Vzwꒆ&ra&a.GMXd+/˪GՈB]-kIk5F#@* P!Ќ~yf$TY:n<!V.ޞ\J6o XyNШFaWԀn< 2 8bP@> Oy:acwnh7||vyIMhVwnnKkVnmyد Dط}. #ðC0l[d&K3dyc>m{^~3mwHݶHuTA# q@ G [`e-)cl2;+͞or䞡>uOM DBz;e!noB@`b&Q ;Ogsc} J@˛GҴ41K7vm$cGgl  Nnp;{vK|x,[޶])J$cZQD9F_ @\%@U"8E cuЀ>ժZ(pG awm@ Rz@q gRik@tpZY.7`\E9zg@s: kD9F[+@<#\j `pVUioU! 4@`2ry;5yqQ& e PvrN P-&`FT\`^  j|T@Z򁚐XPMh%/|V:>)Oml=˗qm@]sZ P@!9qʴ?+x'kau)Y%:R-_aɃwx n +E=@W ^~Ny=A]*uF̨9ϖ\* T~BYìKnfy:n)S%K|X[>_ujS"Am̅mr4*1r1x 806@&[ ?@Ɔ%dz~9Rג5Qo$(yf/:JZgm[;&ajhn6@*Pū e&ڵM2}=gulϙG-G}k$MFW/ Cpe:]s1Fy T_0UD PqB'_gt@rJn ya>P9ӤA,W7ʴ!k":E4yC7׶OlTGV7|  0n2@d3i녍O;_" iYG|V8 ѩ uSD)Y3}ȩDJ=2J-@p7^5T @.PSI$T[+1:?-^*K}k.XVg{%3Q}@  S ӧM[N_3[]#C[g̒: `kd55ahȜ#v.oVn0;kd#=8(}† Wxn@ß%kW.vrWnrF[^!9AhC.0l˚ǯmKdaGX2:rõА0#G[O-  0..vF.kY6cҎ2oڈp%rȒc%̳ҜV05Djeم᧜9r]zyr/T^2] l xTG/fngfn;3̓t3d>Xcι@zvm=ܸM4/I=1\>jk=RqMƘq|yǮ\8Z%ݧ6+O𐤺:$$:0 `C@{5 @e _3{pH^|QdYuǕ=g}T.s$+ N!~?9r?*gc2}\~ˇɏLV a1; `73D#`̛9%}53;~|ڹLkL/~>m:].P$L&_:>cT>k k;|yŧZZfZ'2u+Xaߩ^R.av7Jr:]Am69#xN.9 F*+ ش^SI|2g>7m3!\g%?$.lp` [´NF5Oҋs,ay>%mHi9 hWn8Xj{z-kV鈓^:+)F(])R x򝯬Nn"i]`d/iL?COMr[ : t.&_@sK|$[,}ZFf,X8oFTf[:"u oߕjʴ@`#&06a_$٣iYSi^i'ѦmcLtNA^}b'MtZu0FyĔc !K4ͱK|r%N;[MO'ⷂ.}Q4a.+_-euQ ?@p] xB9$Zi8itZͼ*Zk.v eg˂ɔ &?ҾU+_PϰUD:<1o eh3:=/3_? Ci JsyyRfAj^D=[!@@C!u}ԑamҘ,f>F0 `ie4a:eF{Hh;H9.x>l U`ov0"j$پc诼B@Kti+ fXvrN]%cOz* ιyl:]0gjgil rtn ];6}bݵSLMfӯ?u@KS7㎖ʬÎˎC=IϞ7rx:Cna M!M[FUDl xTG/%C/,:fI6ђ*Pגv!/@~SJD8@thy@.LMIeփMt߸MM_,KxQ]^-KѨ<(Hk`t|sI8e"uR~A9`637u31Jj%O}bt x_ ޟO@(]tԴf2’ض]j}a+(zRx~d$ 4_foiڹ_Cc4;L`A&ԛ4p͔o%VT|}U=e( mkV!_{Yb?f%ZheE@&"@`"j P[[g&yۡ#Z]k1OK;3>_! `~)Ш|uѷ2+5'qTQۮ:?3 &W-LtȚ`Og x@`tlK@&m:i5/Jr\Ik3sLB$}f&`ǘch+0h9xCDVs;%~21Wy ׎ M@٨9 2 .^įsߴww 2P~3W=7j H>Z6N0LX[>-@$@KW"o_/^]VJF˦t>͌ 03_==1#tyE3!J$6-h*O@*@Wv#;?e^Z.am:tr] 3R+#$t9'^j$9Q;@r (6BxckWOKFkj6wFw0A"gmXaM%a^T p 8BL0s4@|Z}4n]:sNNɁD fzdDҩ\5Vo @%P uΉ &w&{Bzkd2M^0v3C2ܳӒM$Z!= Q@*#eqAo2"s+sM|V㟶Fd?ǻ%,G@*$@B@`f(/!~y]1Z+nFo)!03<2~(`C@o ɏ]!CڭsHXsfT~etKl*xyI fwZ  @@cwغILǏ&!rF$wwxڄ# Pb@:KlfNIlxUϯýͰ1AV9BXCbzzQ2;P @ p~ IxodxrՄ, kMhsN@ 3 '':?=4$.A@Ç ЄbX|O2po%LH&23bW'tIcώfgzHE 'Gp[fC@~D׬m^rn%4|:-|g.@4dFd')!kTA!4@$F &0&_z(*>#їKB;V873fI;A<7l*oU  8X/UCP.%ukO߷: U@o:&?YZmL$ \@:%o|][?+D"С=JVs.`:P_:In O<,O??Yi S  @u :+BJoQ셧dVj_} V @U]s1֓}Krv^!}>"Ѷ l  P#bb'@ -G~{F]2;%LZL'cI3D1dgG: @'@`|^ P k.v~qfntPO+qu&YfР$^}Yz) f MJ(@*` @^TTM^w^[@krE5k,ص][2.WQl  0~7@J !&1Z5D_ cO_S. O\%/Dϝe7A蚗?gvd8@ בo@ hlo<ߵspg5);|s1%ܶI nՅ%rM%  8P5@ V0#}VKpt /;^qMh _8A 7C)IHh'οU'T:  Vnr@ O;&4`PZt$@hX.%g˙Ϙkh~tv|^xҌfJ#?r@&"`FԱ! N8'&+[{$+\ Z~Q;Yw%mG h! @P6jN 0)fx"^_w':|4w4\\8`TB-Ol/Jm?gXnkTU @l ` # TVdѤfyZcu0 O]}~@\G +[UlL?FnL֟3KJh1~~@؈IQ :f:>7,_oPs/3:Oᰤz%.=qg.p0:? \$N P6@kif:=+In$W)V:g(b`1$91OW,ojDǿׅ!xGw5-E#`ӑzylZjO8Mj;JI&F$ μ h?P It*N~y a=ϘTE@}i= Po6 M:iilOGH@V;f :FXb~3-ь0_~Jh'[%ѾKDWp3*zϨ {! 0F ^"T@>^r_dL9AS[s3g` 25'ߜ?XKzDzRR==#Uh 0#@@`L-#8F e@4O%cؚolL2WH hŧ͜Br&]1zaVRCV> ֓ C@@`| n0L3 5קuk%y4yH0$ˎ>3U!e \dWc3oVb0>I&=(=?o Jc̦? e PdN Park|URј> ^)^$ \$ϐL,*~0Y-tX+G^;tJLŔHЎ矑WשkLI#2@@@*)`R$4OSih=IO.5Helx,7`@mBߤY#ׯ-22]tpgV؂x  P.i΃ 3%ltA]ԟqd3Y qa 扵vts<Ε_h>7$(F%u^\߮?WԪv$X4)! @T#TV@cltF]2px4_|D+uf  F>Pf7+i_k5Il$^Vк׬ P&dC@ 5 ?7[`unuJ۷J߽wjWeUR.:UG.dS}7Cr2\/̏vؚ%)C_+/Ib&Odq @@\~> @^褚צ3;wZ_>$DKдVGߧKͪzIh60<`VA5(. vfb:?.ݻ%v ? ?Dt+ŒJ; TX@/Gplc6vl?//,xIZxԟ6 Ι[^: 7grC07 ][ ?չK⯭瞐gT߀&IKvxH;&h F'>' TX@/Gpv}oĶ-׻[B KtRx#R3P Θ_ħOMr<\74q|CPν㛎OUćZtIodIuwKTK/I|wNV N +B}@!l!k$y_Z)S$|Z|}%_mD)]N;U`0/[6| uǖV8I \[M&lT'm[%Uo\'<){FJ:?&0/@@qwI L ͜xg$^ߪʷH!v< T‹I͜5Z= %f@n3Oͨ?0rk>#?rjv;M3~WVIrv{Oo$Jzx8([J  Lμ. @#n4u[{q,56KpJ L& PZ NiD$(SVVӛeY|-/t M@v%K]ڹJ|WdtKZ7$nyڟbF YPC@(zs6@ Dw& _JDW~ 66H 'J As! vMP CnDex#0YSFә z\Fdu)#ӭ7kC;%M. ZՐBuO* PQa~*헾לڹ6Rݻ%7T u ̘%\N:MB @SCouRxLdbQFܥ_;wHr6Y%Js^@I/I@H@Mbj[$ \.V۷^x9Je#S>98#@h@V|5s1[7YAL7 ` {5E<-PIrO+5 uC97(>:ZT@ w9gdM?}X|cCp]37؍uK]@U~&:O^)M\C[di8 #}Bھ1S\@v  Dp@Σoy厭y2:퓟slveGLSR $ d`G_颷_M%6ul*bpFZwv]+Su:ۘ!v S@"Pwb;q;4'V:!0)2纯,' 0^g4V? SGpjQ'"pY;$|䒲 O@!Fg9!wը@h\YۤV @`?  9~G֋J!PN@VWArs.@E  2ɤz{U|mRY/*@;->5C؏6 sW>/tڑ^#J%:vIbf7V:L`οuZ @TP@q ?p8(O<*򝰊ǪhZPW{EB!o4V" C@,߲I8Lt{1³=m5K x̸{/  P~sΈ 0A3՟I&XBy*ɪ,~¶yI-TG-2mG*.@◀ #0zl%yC1Wt`5!u$@&h]xBƪS~"Y0 0I-@@zhP]BS}Jg'XZK%?.="-ԝt9TM_ǼrȒeIqyK> Nqu Qy׽ֻaj}ӷ{,_־S*gFG$MπB O1'H 4i[e]Л4 F+@ޜ @0Cg^U}uWMt`Z6:,){GWHFj+g'-,vnf$LY,e!!@"@,n~?T.R+˥{7뗞iMH]q?[J 9@`&a xY(mwt`[yd@6l=j)uˎ m+@ P{! W^%)cޙ˴E'^GN@r]rZܶs5_ʢ @^  -<\ZuMws:*r&I +sl3-ghnZÖ6S Pi@ 34]KƷm=]!L ^ Է֎o&csIw]TNdC(Ys&@- I<5O<*>~Mh?匒ˠ@oŞ xZ`G?#fض[&߬I>tg >P;TH @bO@<+h)>yڿ?gsC\`S̜]r9 P @O /I RgASL6w >㱒>|QeP @qsb/@<+P{܉|;mk~Maʣk83%<|%A  (Ή@@瓙\g[ۇ_zAö(J@0FH @b7@(J䨣miz&'-($uS NQr P @ /ֶvV[c[yTy*jxDG.4D@`ӯJS{>OIg^{ @@ p C piߖL_mQ3ҽ{JHI$* @8 jkg\U;Um)B%mXoҡ6 8P#@**x΅p[lCh?MVP:qh 0 T@ P춫b[G/| xq4RLDTB@*g*Gy SvY[VOغ52cw"q:9h[I&?Il/P3cfɍL/23%KR pp7b@1@1u9`E oKd_?0>,Qom;qϭ7Krʣ g ԝxjLuv/Y@P{! P3e}. W^%)-]?O[ʢwԟrFMuu\ '@8'BA`ƗKl("z^Q; < r@VG$՝pJɕLl\r P @f okm("J0$f\U 8Kw`@YYS}JmplRx@" n ԝ7ۓ9ܞys.ooG4^i:}Ď- ;)(F@1J ``ǰa*łB!i+H|*ʣ 4]pD8䊚_- @(N@qN `@p K L؟KxμS|igKyB$, A8 ,Z|q2S/+=ًF yfϵI>hK9 P @FtQxf\% D"9d6.=lA kdο}WgK2 >-eQ @qsb/@XFqU{I|;qāwIV@hbYxtg$c[y pp{9y@K ϡQf'3ζ?r ?mQ3| 2Oʴkjld( @n 6 /~6z[T{?$#3;o܇+`s,=VZaiM+2yV k@Li@rd/kl/\mۉ#In[yTקF YYLjˤuVLj ov]@Iu_zJ ]y[h?K!|Svy^M2ttC: @o@ zk_\+awV@%;~b%LG! (ݐ@ I%#ukdGK6:u̳s?OmMv)jL?ݽjG@ [I@*wk*i ͘ls/ϰ4fvANOs/p/5\, KMC@] kNb^< ~MMj4E.@! P@eWH%~Cݯp>D@)n!Q@9 %klmэ< @ȼH$&F&@a  0+`딉c[7ɞ_5S!IDATM?Tȡ `A)@r [$-Զv|_X6M 2-|4]0@*,@# P@Vq??(cX|tw^ `/,XhWq 0@@"xpT'Lߖ( pο}Wjl  PwΊ @̸K/'_ž[W۷V!FQGK˻Ʒ@L2  S?Y ͞kKɮeKYRƏ[^.ng2F4>,斑ЂC3$r xƽ2UwH< 0&  `@`,+m+;_ltض(ȹf~vxHR}^%=Q>_Z}vn5gԏt{vKY E0$vA@i3"[5bw-eQH2S)#ɤR6lG>-SX@+@ޜ @"ǟ,rLLFoG!--UWHv_ttO\&7(&i:RX@   P)A}Ƶv%^< N5IlzV!'@8'B@]!EmKW뛶E!/)1 #O:MsQe )@H(vC@>6= WV|K2`CXt.2r}t:> `,) @Ihӂ-#~E!x~z9[]w鶔C!  (Ή@@EV}lW$LRO,)(Kex@hES# ig\UT$j\[*D!Vz[HK.@ P{! P1.*FCucCTG/B@R P"@ B2_$n[y]5 A8 Ih\[߾U?E!@U%C,, @(N@qN @3g صu|_D $楒B(ِ@b+~ Y/I 嬃OIdϐm[*D!U!غv4  @yǙ  0.'Ko19LJ3',`Ke@@ {# 0Y_f\{mHrFʣ  vM͒%KJB@h/3㞟PRvl7>GVzVHfk( @`o' L@eWH䈣l)??ɖ(ԝtxv› V E@`f8?ֿYFml^/R'X޺[7\ '@8'B@6[l)oP <|-eQhy }& 7D@+:||<dR)A싀LJ.)KVF_&P2$ E (@(U ̼:es-ظޖ(jfzx$g `M)@} 4ې4閮&)P`L_rg A(N@qN @iᰴ}˥1!17ʼ5[Q e )@H(vC@i:_:4kN)E]Zzmw^ P_CKx[N߱Mb^, A(N@qN 3g*~˚lG SWm;w϶(@n), ڮjkmv^aKYSY~u\g su ;#.@tCJ@@`N . =4(¾[ NmG4.6WZ~%;m6dLaV ɐv㵒ھu^TW: $*K %m eO~+5 Nhq@C2`CʸsV@*HZ.[8M N߼I>-f'7O@pȅ !f̼溊αڅK˻_ M *߶Ev^<߁8US Bp@.NseݝV *Q e}^% @eT֟#TZTi>"{aW믺"UIS@*/ UURM|lHGlFP@^Tڲ޷aΆwMƫ- ?O@Vmh 0p_ӃkA`r}{J] nlg%n{ P8(Z S1ݳg>U'I&%ջ.tZC"KjM5@o 0[כ"@b)[ɢ] M`xri1MA"xt(t¡+xFm*{z^^`@mLp Z ֠&j8q } VM_U_W7?;nI&QW"@+I;@,l_fC[6TRR}oIJ-m0eOIohۻ{Y CDC2,@ܰ(THr8op$'8abÆA@ "h㒗]ٺ3wgwgzTuuӍFWRW+ w%< |oo6n^?klmno[ <~?ǿ}! 6_^N<sg=2Oy,{{x֛|6/;gG߲}_l+?r)6 @w%,r]yND܈3E3xh. @ c^/O3WL @-`dX ĥ @, )'G-z<+p(Q舘"@JTkLy$豵|o/O @ak=K W8]/ē]* @ G IkjnGy2 [n`[8.  @L!0~[) o{_B}c] @ooXL) OLȋЁ{}pv @.пcӿ+6n-n~T޸;}/O'wpNA @! 7tk\ɏڻƜڨP lO `Wykb.o/l?/m?U'^R|O+oK?ک'@ Pיtdm XKAK&c5xKjWz~ڏF @}ҝ|cO+;9 |J , XXd:X/L "/zi^nD?*g i3ni3ƕ  @DQ׶K{&UKmX?1TNR  gOMڕ~?__jx?61L @w=\Ǵ+Rk X׃m\/dU^z9Xb4~>y @oյ%_阯o+{R>Pu}WN۱]҉,Lto8zadRˤD?OZ}T>rm\imU7nK2 @ XY XJDž>=r6;,Q@`&ى⪉}W*W]mq{˒ @FiRZ6O]k?SeD ``Q:/ԧV,?i6.9+^XI @v훳r3i̹鮺N~" =C҄|򂩗V~|VmJ^|mF @󧼔V,8OyL}b3ikK'0ɍ>0륑Iw^ U|ue'fO)'M_V^'O @7pt''M|m9eO+V`/^"d eWp2/+GKirkKeO @#п R^J+g^~]A@t2 h^'/X=|r=&O @7o?g=U8ukmp4'ֽNd_WZ[\)W1_'@ p_;8אXOZ˵--\|3Vd-?}yܔ7t\2 /r$ڥ{eO @%0~ \iU'vh:SeO{,^"kES/\j+_ۘEGaE @*пҊe>ӱtil̊@4D/jmڥb'Ml.1) @SXεҞz<זc.KOA8D^,<}.Ǧ\*V{%-}< @٥-&J{>uigK)K <%`2㳱VN|)]U&m @8UxbIm_+'.Xjcx:J< @8u]ԍibkĥ  |^N>iu.6e) @sXxcZGڤ܏1y_LPXXRm  @ܗDe)c'I*n+?7CՍ}C/i#@ pLs+]'x~ Mu]u&[= @c 2_kk\[{\{&W&=77ϐvR @$p|_֟k9 g| }Я~<^ @) ȏ׼?e&\  x @(pvhoY얁'uOi @TຓluS :~I>< @y >}w/ ,-YsvdP @sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIDATX WohU_.iDҢgS1 bcB[? ~R(RTK@(r~ZO+ $6()Mx 4wƻsq9nӼ{3oͼcx$"`\4@de1&_܋]ScOsڕ(i&h뚦r⾊X2tqɣG5Rd_T%K3Zv=c>&AZ$]g) ˌd낇:ɘr 5tի(zDowwխΈ5Xػ < 8\ȒwjNtl˭Hy +BD(P$cd5CCDSF1DB RPxC?5bf. mM9fH 8_ X20XLa󵁑槚)rmlEda'<{}gI6 7_9:Jg0gӃW?IGODCX@ //w# *@EnPU߇5Bg?sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o IDATxZklT>w_l!ԞZ8w*7j G3I42jj?}oPS Կi$$W/͋IӼi` hL^|˜P 9@J8%UAO΋*+@ +!!/c3i{sTB; RYՀRV!-?r+>Q٬;w|IxEvNhY4'8C<*OA˽P9lڤ}n!yi7 ( bO'H >(OqeS>icRr@c<*(ɔJH  9s뮍,d** GHJ ʬ_'RQ@-gD|M7MSYRR2UL,Ri\k{QAjXr\NVx6 "^kqfVt;FQPYsSa)t97zw$debUG//-AZ nRR(-=uwnҮx8JIi 5Z!_gԴnҒ&B[GG#ȓPDkoAQ-@1خgxe՝8rh…׿~}[eW`جIש`޳b2˚ e;/E}OL,^zy[L(:z{}llw`sm(4lxz~[Bf8PyUKOpP 4a! kdOS **s W-@V q 4}!Bđ`Nf鰩i~x铏؝lJcóKr^o ŹLLj&s!vj/x8C u/ӾcM|'*Qc\P;${jT: 8֨}XGL0q"Cr.L*5m(v|;$w޷@ 8kvR5L?c +!d+W,J&g+|.b`bƷ{f[;\B3 90Y[s![ˢ(wcLFcMT+周"yCs܏})x[lkq ;bDg@S,:٪O q\B\wE;1.b+4N|qiT^$_?u%@y_"lpv˘(}4M8r@C.ysPQʘֈ+pX<=ze m-{Wƨ򗥢^w&Wȼ6B(浯ҚS?H@pP2?e1:8IR ܙFKagu+ r|d2Ȣ~mbLjsxȃ ,(p^6@'{n:X@fbj:&d3xN`Nh=uBVthW;Z5K"[Х82eK+C×^\Jjq#dX;A`4KW׮<7h:7w&տ_J+8 YTd--]JHC=ռJ%kpWh@/=@7 ‹+Z4|d/;{~нGU RWKg⋑}inr MwDZ+.yB,i\լyn~E|R')P/|"5a ]<1?QÎtK?[JV嗖uypzhdN`22Dok};I U>o9HVRz؋0(X7vQOuy}Y;o -tތR۩Oa-Bxwmb9q:5AaD9$[dHEe=i+XTE)a.yPsN~{T9}"8!/X(y<Ŕ3q 1yy$]!04|Qt%lxCrJ>2f9wt󛲬 4(+`6lD6N ^悟|DF{<Pûm+pAzK`2sq| ^jǎ8cI^W2cLJ[t($~UuFvXVeOFjǷ$>FณwvLjgTxݥWT+]sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o?IDATx} $Eu,{qr ( $J$hI4k< b5F .Bh@\@eYb]>9Wog{Ξ^}_OUzW^WUWwGTCCČlا<3oȦMԁ;YcO;#@iѝgm8#J:113Ϸ2/A2*NututwJwvmOul'6D7Ͽ6x~뇈EK.IJA{vaXd{ݸ㲞96)etvܸxU'x1k6-msNяձd\6߼H$Dәt*Jʋ?0gޣ'N_ܱ;X1Tv,%$Tl|0:8f:$WlkIQe6WM)]!e7`@7qdL*Gޙ]3/~k.g."8`x( (! 9qbfye6WM)]%bBˆ9'{L[q@=(cCoIL}.qȃ0q֓^?>vczhD6k&w("4F CUL)o+yiC&$6J``GXϔ;x NXw eLqȤƭK]4t*FZM^u*?-8MvvIQlh|ah!諹#/ g@|F pD/ D1,;u_~S~/]Ip 4Zd@'…:F>нJ' LiammL -1c6xekf'Aku/ipt M@hl.v+_ 2o :riɇ{J2@90>ƨLx:0(@ kOPd:ҿu뗟q"Hۻ8$DhP)Xn(W8e4vq9C4'uU(`RfBox B֠;)MȻGbvn3N|`$ NZFeU"LpT@o̮<5bsfJ#o)K8~h'88JD&ݻk/N[͜9d$e(pHRzC AGL|ͅ ;uBFcfhG=ړL C=gݻ۶m.'`;:!n2yEørN3+a4~]ht~ldx9̓8 (g Cv|[8P)u t{dlzf۶OQJJhBbA\mypci6?wyشiBʎs晣gl`; uwP|K Ǻ;fw> Jnb:T.s+z0ub/T7]uR O2N);H&vчȋ/]hQHiv!Sch͆\V ^|0Y~~1#L$H@&,`!iep DzH+Qm~j)WpB7$F;nꃡ Apa~^sỲ{9(4pw3K}nMvͶ"PUQ @Vs=3q7ʵ1Ri4dG.\!0Tek@WaL&?՛z!L.pg;Ν?ӧ$AI @X@-149^P?Ȳ FNȼcY 'Q^^8`;ʖj@@@U/lʥalv\XoNA.?'eH,psB9HH;ga 08z[.0)4&'Ui@@U F 龜w#yshP70"v&|n9ڜ|w#[P&F69e%Tqjl4`0x-e~6BFVCv6N;l H>b}׬3" j@@ ]KNm.X%Y2+]\w1'8 `40{ݪg@A^]5j ;9Fy41//c9jL57{P@ C&}4>쨵[X^6jZ8J5W:׀]e5-zy*8M][M:us;, vcC~eq=23-(.pݲO/խff9)q_7^ȍ tf2ΆY v{qbIЀWx5d$! 9.2Ai]Ṽc/\O|śhrZa |bg6m)'ЕI%~~eAVof#;)2m|VJq6r0{2^΍oـZK2'd `qs5.  ;c:@w4Y~-/2y٨ѬߖsNlcD}\p3M1Rz3-b\`v e4;ԭsmؼ}P3XNV8KJ: _7ZnI?w<Cv;p'c(bN3'`9g0|wlwWj^Ur:JɻĥrP)\w g~٘f<;o ]bƳӠMz_f^{: BeBv*C&Ѐ Q Mo>5s:? k:Bl챃W {AhxP\7 /el2΁ܴj@y٨d_y!LO:wFq+ D^iϬ7ۆjy플6S͓~X%u]̟'YH3XDEjظs׻>ßk]o1Y }LHݴB4pvx8eK?c%R\ci\2QsW+ove0ɽR, TK4h:а K_^uMo>`I?Nq q#ʠ| j9f|W10b<؍ /`6 t >Sg!xaLAsrS# Ћ^s m6_N׏ F28`>7B j}яpj$ CpPPrv9,e %pu^ 2Nӏw;*d'0gw ~! 2q::swrY#QΣA?2>(L#C1a|܈N`'NGo\wϠNFhCȁ9n1)`i1z,M$ v2 o 2 1 yb,IJ[`(N# @ Rac~IQ 2>H(Qh9t`D #{P}8i[JBP:  s 38Ʀq|(c lƏ 8v `RF;'ci#r HN@r`⤗|{q.tvw4!ps,(ByŁ1 (qr4P`L^Qb2WqMB>'pRTc ?@tVG\ͣf jF#ѱW>r1gPѫFT%+W3Nh=8H(CKL22ɔCQPYsTSiMe۱U{qz_6oUi)@X>_v(NJ(ځs}= 2KUf#LӅ*26SjllG>(>A@ %z 7}K/SG:]2|f:N,VKFIT4ѡ6W~zZo#L v:[~j51e~gh:@́#LDCD"Yȉ"԰5+ևfwv^4i_:c)iA;JxMw^| Yvyig``9z4#@;0s w.{C=Y3 %qEЂ͈W '}ꐛ"}T4F 6z/à OW[_%K˷5]`2rO4L-GfiFpX4FcH z̼==֞Nn4Vj6?3S ~tOCP?ԢG1u">:cBCDP:ay?`Iwn:zq:T&y,S6؞}y{-`VQPsӜ{Pΰ0ĦLWshCxd#a6z[|f8l/<%h66!`&!6s 8GKz@GWuEwޯ;\C| '$v#e#<6rE'Kvd5,qBzոD"D4~Ӂ]OMȌu `K58llx*1 UVϑR;mGG6r    СaN9O"  `GU.h's(;bSgI}It`"pW~=44yPz4@@xpM "=HMmy(ٹ 8[.h&سQm6[+7gW`JN矍)C }yF>5,H.Q7}{+V:i:֨JN:m))z:ƯEdxвd+<>Jo 1hAgTmY3΀F8! 3O'O4wUYLJ:!Kh P-ϿHM8*ҁ U녱ݻ87`gB6I6!;gLM0=E dF@yL&>!~$<8v%8h&BRս;١fr8L4z{YyԬ/Sa@DeQMg0 =%G:3Ds ZSL3<7]zOSA¶Ξ 49Zr2*cGp$H%T\&OV.%pڊ_$k1F%qd liP%sMr֋20XN*p X,ٽ{Zn* NӮ Դ>ocseGe^(Aep3Wb!r1  <1@|t\+O e"]5#$:.!P3͵dFl,-= FbR pi=@ r>}vƖytXZ„7##*Y[#k=߫FWTI57ܞ a/+kִ(mR\a:bWI5pPks4N ' L Y/JpF6/g[n2 . 'cJ+W{T-Mc-F-a崼 c@wA!`]Ыt4080]az C?.g~vLl4@j(0|lX:?tv7]?G7?zv#`:1$RB;hnᄏ7k̳W \U 災/L+1;8Fq9ܝ |mo K E^@(BPϚK&p|'?\ wv  Ru';R4,"5g ecp1A`{uW^}PfȤ ]`C'Pt<1u*ETy旉AZ1im4C ";N[g.63]/*Cwٗ{ѓ&MmmIտqB%GrXR`lXsŞ)LoD"YrO^r$ICE tf͐+|lnYj .J6Q/2){v^ڵ `7&)jwA8>K`PQ|J2/\mߤPFn> &)*T*8ʼ1 ?A@ހxOׇULBqoIZ {xP'PJ^p=w2 rλ312.|OufѴ8>Vje^Y2/@.œ>{4ucK}`tt֯dz<(e ֪;;ǕJ_-#cz2qgd]{v.$̲A@uVVzRʍy\.GRvсǴ=$g$̲6A@mXG.ZzX3b?Y<Κ|upO~lVMl,œp|^5qys"v;;;ޘWu A`~||սh=,wJ?iv^,(y_ʌ+xKwt'7oTc6G֮U={Ɔ" Ct[-.IDEpGQ1>m||~l 6,E#/R7 ҷi+1UӅf{7!WYB|~[PrV_Q{U 3E=,(!3.dȖ#Z8atJ@jA@}{Yjh[/,c+A7s6EĜO~Qا+.Pc۷8 X*>@gCtѦ,0-=8ى 5dZS zw̙AM 2 V;#d _z?OfOOP{ 8\L8Dgrtޚ= `hA)8rf鱤:[?Qz_V^Q#)2Ljү Gqح .y2m1'Wz:lKWM`e)rBre^)1ÃSV p jt{RS#wvmN\0ܨ O :<4(3Y18xe3^!S$1@Bep'&L3Ԟee(AnTf=/9 Wc-* Mɴ(~nǷʄ^ #L>h+[H+Ff|oAN< [*ݴ^kϳ֯٩7ꪪ xIXT'^MT3zI&_#cZ|mTHA@ӛwsm2ccjeg^Sng<;64&]ih'wlW{VLv{p pmT-5idOFY6|ț@43Lv顱.pgV<|cF=,<ׯvݎt^0_3E;%qyh~f PۿY|ռgA>4~8lE X~|j0Nr`O>Cx|<@SOS#*>{f2`\EJ3f^o#JݫvlQ;s}'5hQPOޥdF?u=HJnXT1:iY,G=,?YE&{UF #j]u-=(sz+ݺgIpR{}t'5i968z)4ZW'VN@n+OܳW)EB mslV5F= )RU8k(q]\`7:N^pT:S$vWW`N$ 5P2 b[7w ۝/jm8n cp !mIک5\FXo,Vj@+Tdlu:VW-#e͔% h`SӡS%L,eHu4`ӭsF͑T@s.Xly(^*ڒ8l}R N#c|k'Crmxte e̼["`-[^x^lN#^Xdg23̝FM0qal)p;Bpm8=)@[H䫫Ε 6rNs8hYk50d AD@#5 ږD!Ӏ85#h4RRh dqDԀ8Fj[ LB "hHmK]i@@D 4RmK42 Y8Fj@@#-uBq!kG4H h.@4  d "qԶ%A',jĦsh@@c\Zb1?>aJ%^ў5h.vL ޓ>W-㈥*Rߪ2ɔRsL@ CϤ!߸U-*7.U^;I*5eTqآdD\-͓82P~ *H"٥ڭj{?ЊZ}h@% 3 iܯ" Ood FjpU{VxCr'0wrhRbB S oKE'|A#GTq'`#_ۋ|2~Uտy_to*BZj4YMќM0Qo"dG+hNd,2ItZgTF_ykwb*{iPw%B2 YCySU+irQqJŁ ~KPCؼdxtnIhwvc4\_K|SYWKM7_D.(bs⩲1BQF!(AmDWɕ/E8K{wTQ{stXًUxji*^~?|@G>n*1FG4RC12J֕<U.W?#Q=pu7BV+>=;իޭUOjAˮYCdhՀ8諛_>ߴ4qT5i@#C!נWJ{I).RV7 ~;l=KTϔ*$ق;v¸PW^;mAF Uj@R;p AVnVYAsԉUze_!Ws'C gWĹwoPG~V}Eۇ֯ؠHM׀Le+K/M Fc~9z) qz4 xSGoñKhVОXBJ('4EdhC~2<_?\5Ujh׼aRs]_57]`v#5 ڮ.xU:W.Zm6}@Udx-|I2az}.-Q6"$7mP}[#:q Tv:@ p|:3їש5U+O4  DDV yXp緫5o9m4WU>eZ}Qj|t^OS{vMzO>UԀ8P6OG{6jßYK認v db2>@ =}K>o:#О"֞g5j/Q].TW_zNX:/Ws}bQ2v8Bڻ]#tňpߐj ȁ^G@ ]Ҫc,ճ kb{vvhoJE5q^|jEQT\@X $#R֊eSgǶoWߡhێрd S-  ,g)Ԁ8OP40>4 `|hS<"@Ѐ8rO TECG;Y<5   ĜyjUfKaO>W0߾@W] /!%F18 <̓`ꞡ}{rЗz;)TM5 l]fQah@@}ZCjB`zxH ғ 4O Efލ_/͆}هMA>Pzr G ' n/o/Kj7h( 9hO32QBc5?܉[~:6Cun?hyӟ!y_©Ŧy4ڽ8oGiB7Sx~z P%[|=ο̯y~u=_,ڊo=5?Z.ʐd PZj?HX÷KS;g* 6P=rK/̳3tc-_[$Z@4`A F ;l;<1G6S];`a$o-< X&PW{ξsZwyC`x3c,?VZf@-q;߻gIvazuHBsB!Dj!ނSfZ  H[N@ܩ ڷ72hFOiO;y46=>mzN&zp'ryIT2:CƌxJқW= Sfߪbr"jP ^Cu 9'B6Q2ɗ]f~Lئy0NjPq;;@ o$b=U^4`.N-C<7YIZPkZ6Vſz_x~wq =G5(?G@Ū+Ohԑig~VʉU?.p%_c Bmvr~`0?#4nm9Sto$wT/8U"C?R5NuUuzD:+_PRh훨&^p\}y]^p1kBMc[2A>z^/+Ш|*U@*ґOJACTG6":quPjK @Bj𯃺2@Իu.Y7 / /5 h=5 =UJ4K|ID08Fy{*YJ4PDMapEt#`рhhnDE`!(X4PBٯcgvJI Чѷ N@G[;NһyJ]_Vd'$ذlVqebDNTW%; 87,2(D#G"GpM a fkС ʼn %H/}N12>N08(AA#aDa820Oq\~m<&W~hCBi3POc(ˡ@ j6raCn09(cNMST,814-iB*eBKY6\iX_virŭ`H^2JLǽ.  Dрh@4  Dрh@4  Dрh@4  4] 3SIENDB`ic14lPNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRTUUW_=s_0)r x(+. *E\tW\AA@PQ@`8髪^f쬬+2+17󽌌+2e+1(i[eE7^}f$t92[5Fæ:>ڞq^L#@I;6o8 _1:3qazySDTST6 },`ޱV֪P{9Aܵ։5h;nH~i*u,4>pY`X9WV9M"X6|0b5,F@c_[+KWrmtƧj H%\qlg4Xff%-ek]c!85죉/$Tc-햩i*KQ@u2)eKk{1AM֙}ihjeоL@~/tSffX؟ 8WV6ҖٞcMՠRj4Õwߕdڑ;멥>-# ƍP b!&T{*&`^MU~e**(r9ep2~6졳;Q>XjU:/FӴr<"n#1GE2/N죘 '+o ,NyWUJ[+oWzՠ $Tx,C*֤]}7-aqf3^fˀrP0}m.-tF`ל/;MU$2K~+c| I7i tVźUC<>XjrM_1GΡ꺫L~3K4+}4v8?'}C%o%v Wi56E2?{$8B@dR,+|mB6C#=[kƇ2!+'gם,~8-QL57/p~=Ifr\᝾-ARz,N| [UTP)7pai@(׼C9pFGXRLUtfѺHuFrTȌPQ^PE>SQ,t^! elvMظI$h;GGHɎQTjXM#Wb= ?ݰhxF6o\o > F}f^8^'ˑ{c*BҊMO,XSr՚ ~fXx-횲5/4Vo6e1f؅an g]Gq-qıTf)*ހK6:'ZmYM[@-{Q"}r]+2* A?BwL+\O准vCKR8~WݲR{\ei汆+oΒ&abUY/R#} 2} FāI@y;=srWS)-o)EB%[rղ~RXAfE sJt}ૼko{QSQl?h7L{gߊKjT3 {hHۼkYL>R>xݙ_V6×$<3_upH w>xu-^va56x:Qvp N|u>Z_Zw)]H<U Y$?HW^GSDPF b)54E{Ԛ](yfXf7YWiͧ䕰6QxJ(PNn$_%.@Op#u!UGVd^m2#.Xrgc,5{.-ԶP *¢ ͦ i,]mn-lY,#,d&@ޞ֙a hwnLqEjr( FV@')Zxt䮸4,H8;u/SooiTZh nڡ:OOI+cbOWbmf㉥KxiaW}3^:8ENt&ՌYm0b7pI|5- ¯_ea&[Rא@2;~14bR !M!wzj_e׋[IhMv%e ai0c65UR},0+WMmK%Y226pT$V0UZWLƀęɤ=6)%U$,N|vۊ,i^8G/q/q%ّS_fXcݐåվiA- }/"- 9wloDtjQ*QpnJ-Ϋ/( r1+#3Mrk4ײΌs<^~!OO$g"Qvff^U2 @,v#:g.x(O%{o0Vq $P6{?BTraIHA<4;F\94\JhZB4K+1*$S*`lfx,3BJh@ 6xA%s1_7mqk8*lОz/[4$^Jt1pt4c$ݑt^@Re. 6QET"hqfis,vpF@uPߤ"qcc9l>ʱZZ=3LsiUə7RaDj Z;ӡ~K A-sU܊iE4. ʼn۱/hJ»ɭ%J)Mךc KK*RFS#Ŭ*&ť\@1 Ԕnz;ܤF:Un67OؙfFCRl搰Y3kWy+?f29pc`@(K9wtbo I1~[u@T*YIAU}cg.mM[Z&=68KtpVVDj\,}vtڍ|d.[^" $W<: = WSR(xK㝱HI k^+$OgZ2b-.Zf\1{|={A# E*u*49u6@d^qJH5c-ih)=dd@xk*TK&qi<ڮE6 E%S958`6pBTj/J7h5Rl+Zє[a~G3@[nJ4j(hQj.ճRR+m|G卷w4kؽʘ mhſ*j>떴;7*"Z#QIՇ͆˖w5yZ8S%y2 Vt+QbZ5LQKe׬nҸDèF1 _UZ TdJS|yJX9Y΂/_k]5!"BKGr4㖌 [4D^UMڸ UY̫e N7& O[ &zzЪ4QM\F3!aqy{u(j:1N#d(mA]R9ԗ3L^g RRm6} K3l֡柔6c+E)9JRɠM@]O@?d_#/PwZ386* hT]T#鱑805*,\~EgM;8ˍG(3ӭpɄ 9c nO$PHhdgxB#J>F~p%cuWq#:$2F՗0Y Q2w+42>I5~Hg8 J 7Brf5~ hh*WU_ [mنE6 *Eɩ_~Uķ:hI͎W94  DK mǬkN>3l_tE{S±hSI#,^ipQ\U鱄5N44Ęح7|'r%;ȁge=Og2q"Qfˬ\l% U?=_4")E p*5lٙچi>3ue&b)$[| $3ďE02b͎kE} XL_$8 k>DFI|^8*UL-Cg:=6hi4h~r|uGD:M/eO)|5lޮq=+aPCZЎPU&Uo5Ú?h_aRfTd5D*JTf%5W֡0{g @1zX ~L\h)%PH5OlSEyfOC_KXN}zgkR,mT9oʱ3.1>ƅwC5 IrRՇ`:^'bhocѴ׽ ;ˬ8\l_~lFnwF(qf9oGY$C#7;om7HF-Cr?|M.UKyIW2I7 }Њ']x'', 9IDATL2u'?E>G ;a`l(%K1[ "w.YDmAm{O3}xKD{eƣqI|9JtvPj qN@.E#=[X*/?i~| /IS+G`ܳ喍% Qn7Jl/WtJ4xf:~g-k}0ڏDk^l`=2RmT[[$?A;[mؼ^Q*i'a)k:.3>t2f?y"nGT`ņ,o֔S5  NRg'YJS YTo摰3]I|XSV?a@Y1ZE Xv["X~Mɇ6[^gY3#/ʬrini>MskkMv'w.1UtnM4SNk R2omՏ DM MB@Uىo43݉Ai^gZj%^*rK'p  )A|"Tvڌrsu*FIwK~U&^01@ATq9Wf*Ryx Ƈ/%ƫX+3ͧ_~nt0=h@ T_JHUv%15K5N})g,ae aX/4 9Ii;ՔA^  0 n TRTXOfIՐ@x4t񕃔+崜;57|  Da94Wt3> aSvi KXX'a1\e@e @\ 8d2JTdfgƹ6HRL2^u9yu֋1cԅ*(Lϩ5qNJ;EXf3Ui ()  P3R MK^װ闪fP    UM/bG%e5Á@Vr( $`*tUj _ǓcL31|t@ xn[Sk܎5POigƛ'sk0*|Ō7*i~yx)&4)`Uninbh~IS#Vo5nF90&२40@c3HXM_ԗ|f9+%˫Z3:L_ÚGpt#Ci U*Xqz,(ZUt3lƙe$Y^uk_i^hg&K3%  "MT)Y3a3]IfX5^˚W|fݚ߭ x!7U_cnuhC|u_}7}-_i>  bA@ 2UTִR0W3^4?gA@ DњTvkJt_.ެ+Y“'` QD(R [3]u2 hR/U#:^   Ti8/ȫ-Wm~/V l  0 P䓀@Q@F   606"@@  h@@F@F@`M@ۈ@ a  d4  a# l#y@@@ 0&@@@ l`mD @@ 2<   @F   606"@@  h@@F@F@`M@ۈ@ a  d4  a# l#y@@@ 0&@@@ l`mD @@ 7B(?D"cރ@ٕI)[ P!Dk+e7#*+,B><[! >ce* ?#<@"Im{.Dxl@"UP.d:(*B&K3/}of!J[P DK M}hOoe'Q"ɏD={(q'w?4@*EQ:8 ||2ڛZ㽴雗5&UZё"LeֻCS8h:fi} ";i:W}GQ@~?(ʯ q&zl(j4Dgxdk}(W5s E08j9x/!~6\ hF ! Qʟvƻ,X(ʇZ-:H@&!w{3Fp3ZR 0GsMH}Pÿ'3|<ݞfٗp~!oy{@ && AZŕ{|T@5%(|hzA!"*Kk區_m}o4d(#М_&ERzutAwitq'q4s"kF@T UmAhxK 0R ! ʓܧ,;/OB]Y_o6?:%~Қ{o&ڵ~#Y9-9t>|kM쳯 2BSO}-m#yГh|e0A`p!KC\a92Yji(hhC}\x(xB߮h?&!IyJ>'Ekk+P_7 p9u6 4n @E@ȣhzL7ڏOy_s.6e2WoMw:w Oֻ  aʮ~vB"@# 8hyaFx-wtO=Rŀ4o7%4{ў<=o?m 6ë_ T  P &i`?Ům h|_N<7tK݌Qؑy-ǭy<0tb8K/ 8϶.<*" (2x @V@OheR>7Z&n5M:p7?^n_;A `D{ }# "B\N_j-n~[h׶"gZHaa!02_.pgvw'3<}*"A`<ܠeœzH~r,ҵ|EȽ1hBKxW+{XSf6;Bڸrߨ,O̅ i}h#}5uvuY_ V۸FzwgzU@6@Z"b,?4eg+o\ONY1~1.ƘkhoPA儯'lv18&SN#N9g<ͯʕ7j`x=^D(9|e0pxr;,An@Vi?Q4(ƀ|6X$9vQ. %^~~kc#)% @`ApHb|w'kwFKi+yQv(B~whQ>@@  8y۱zݞ~u)?&g@`-6r}wB5/򝮏_ҋ |6KVk1I#DD 2h~l,x8@_32`DfÁ 0d>wMCZgL<[oGp a  (ﯿQ 5EjDgBߵ_ڈZQB/XQi X#zhUߢ,~+T : [qp7ͽ2J[f?IŐoS 8 74h/! x6ӪSC#7Z e<(Nx3=wK}=ʟg-MtCR% >4), ϣOYK^CNT{C\Q[ ?Ov[ܯ(SDZ4AO J_hpԾ4#C%֭G}wa$Gɶ6)[(Xc1@ F3 PO?8H}ur)`[@$3NzίZ_Jk5 D,$ CCĊhgm=`[ /E PQxOЮ%v}~1s)峼PB2g# 1cv5H\??rskbvʬ^E;o5e_zW=og:E&k B ' RO4v- [O}Ds(5-[kNQAV⒭mCCOɭ{ֱ9&qrp& YsX/Ƃ/.:@.@ـ   `<AGA@@!1tt@@`AGA@@!1tt@@`AGA@@!1tt@@`AGA@@!]:D=O>-t=)rY26TuNYv %ӭu~IW=CVE89 P~p˫)u-{ _GF&!% <7}B1ؤ+,q Q_VLz$%jͲ_1xzo>EI6Ԙ1Sz)q 'cs:L W b-6"DOOzӾ |WD:m&Q"ur^A>yi=wK.: H@sWcGQ5ԃ"Q\2>m":Cm; J[ա71})tѬw!@؀A;<Pl.;BoQG:ܝY0-Nc=K$`qfbɶȏ CC%l4. Ӿw<81Ssp ;`8aէ.ku }~vUuRӦўQ"]uC@& e1\fz?JkzqwOє_ImYAxMo an$K o8"l[z^W)5g}V\մ'FLՂZ PGuX\GiPԉ@Cj9x}zZjϟjgyy͔景މԬy+iH'BT|K^]}Q# N/!0I'hB[U >zѫLэY^4ށ jB$FG "/fd%C(%1Df6br~r)Wj4]6p{Am[YSlnyg& +"6‹xZ)'7(9:Vxy y(88@`eE{Ϟ٭or=[=5Q9x.ڟhmBk>.Z}E< h2wV@ҁX 0)Mmz%4{b\}9>fcf#B6z}ymr{>T& 0MX)_c_Y k|7}| 3e|pꩧ7= t9 hqELWoKʾVg%Bwׯ)׿%heYnI! P%V\rk|? ~yM&;wFx-blX# rCA+4~/5 Jww:VLDmHqT JO,- vrẇD&k)WֿN9)LH !pP J`wx+$/'lE^Ym|_3M~ "D3, % TuQ1Jfф \h/ER֗7&J?RIPރlM! ?6 cJOK^'{X)4BO}0bC@%0_+?OQJ klp9԰ @F@X9̞C3|g y+`Կ'_BɧHeO'%$L0 dJEڟǴ~ȦU0uaiZ'_';fB?D\¿)ǽ:?%q,J]\<CRyȚaK]ǝXXe (Y@q`4=Z j230Sjo'%ye[|xv8SB#L"7^9"J]4ϵ2 pA\ID`ѷJ9(ms|ǜ Xzjc"$^i}kvEJ/dA0(} @zmhjU%oni/'x3´.USf#jiw@` @ ƚO9/u|Dv-\X }yjC^ J(!4lHsdq_Xl9<̳Zw8 ϝ_]#Ց"&9%}nd#!k5Bʢ?qH$5Ir}w`>5$+YyF.l8*NUH@.yׇhw|sEY<3}3s'E Fh@@@D,Cd RR{Sr@# uZF1>DN RqBB,\ݮa1? װzBiR8ZkE>3y*L/'$oO,F0"yF10q(SGz҈ብ  huyv^-kۇPZ#~4| @)ry w˯niEdpPbX|c7wڴͽxJ1.;_ *i]sԨ5G@ө6orIY𯟯]|(K48 kVDsSPq,T]aU/ezAiz @@Ua/F(rU IM@ ׉O@N Uz $q<ek9'XӬ  I@⫓kO Uziry9h9930`A=qL_MSkfJ5>@ik^k՗^EOMCA ` rҩH՗S'I:3qA@&0V宾_'e%l:>3>va ZO9tƋOKCج[@@k7rc>W@4/DžXX;_NUzbɱ)ɯh]  P5Uæ7|ڬ p?$Q/] J^X~O8  PW0Ǣ%Me1 >E4rR qG9* _}<5Xjk &/EE5~0KXi~W_kҼz @@>TKmx=6} AYM~e䐰X  k>8 ÁLSK'~)'pLTZ2F@/)v5̧'XB@uUz\ʗiߔejSvn2x49cy)25_ _55b5{VA25_X2296V@N2X`HGHVT'$#$5T%"!$3T%"! $) T%""# ## T%""#G$"!$e T%""I""$S T%""}4""%TT%"".4"%TT%""5<"%TT%""d"%TT%""p~"%T T%)Bc/"%T T%%V%pI2"%TL"")&I1A"Lit32"YɁƁ ÿʊɁɄȁŁ f͊ʁʁʄȀΕ˅ʁƀѕˀ̃Ȁ ¿ҙ́ӔЂσԔσ̀ ԖтЄµхф҅ЀρĶց ҂ӂҁπ¿ķ֖ӄрπķ+֜Ӂҁπ¿ķU֗ԆӀĸI֘ԀԂӀЀķU֝Ԅ¿ĸfՆՈՇԀÿĹ;֙ՇԁĹW֙ՇԁÿĹk֚ՇԀĿĺ{fփՅźqփՄŻIփՄĻ+փՄ ƀ żփՄ ȁ żփՄ ʂ ŽփՃ ̈żփՃ½ĻփՃփՁؿօ@ք3ւ3րŀ ǀ ˁȀ Uܓ̀+ς΀ˀ¿փЁ΀ˀG ՆՇԀрЃ ·ڄՆԂҁσĥ߄ՆԃҀρ ȦNՇԃӀҀЂՆԄӀҀςɤՇԃӀҁςS3քՈԃҁЀρIց׃րՈԃ҂рν<ހՈԄ҂рƣՈԄ҅ʣՈԃӀ̣ՈԄϣՈԊӣՉԆ֣Ռ֣ֈՎ֣݂։Ռ֣Շ֋Ո֣֋Մ֣֋Ղ֣֏֣֌֣ՀցՀ֣֣֣֣֣֣֣֣֣֣צ֣إ֣٤֣֣֣٢֣ץ֣SZZS֣ QcaacW֣f_d֣\`_֣ `Y]][\֣pcbl֣⮪֣ע֣֣֣ դ֣֣ק֣֣֣֣֣֣֣׀߀֣ރڂہڕ֣ہ֣݁׍֣֣݁݁߀֣֣܄֣׊ ڕ֣֣݁ށ֣܂֣ف؁ ֣ـ ֣ږ֣܉؀ߘ֣֣֣֣֣֢##mU/. --,5BQ^]m/. --./..,+=X30/.-,+)M@0/.-,,*,'O10/0/.--,,+)(Q10/.--,,+)(N10/.--,,+)'R21010/.--,+))N210010/.-,+*(.I210/.--,+**0D21010/.--,+)/0@210/. --,*./1;+210/. --,+-//16+210/ ..--,-0//23I21210/ ..--,-10//1/9210/. --,100//1+ M21210/. --,1100//0+'M210/. -,21100//0+3M210/.--210//+AM210/.-2210/.,LM210/.-2100/.,M9210/..-2100/.,Q$210/.2100/.,U+210//.2100/.,U210/./2100/.,T210/2100/.,Q210/0210//.,O210/-,+*)('&'(*,00.,J210/..-,++* )((%#$(--F2?}ٹu, ++**)((&#$)C2;--,+**)('%%<2Y-, ++**)(&332d..-,++*)*.2\.-,,++*+12>/.-,++,A20/.--,+*Q@23N60/.--,*2ؘ810/.--/L2O>;9851010/..-+ 2021210/.0S23I23210/.24`Ӌ210/\;25k210//2P25o210//25o21052l210]$248:;;>t:;:621003\-2=߰^42103R2=212D22172r521026212m52122/21121122732231רU224221003l10232247p34543225o225o225o225o225o225o225o225o225o225o225o225o223j22{7227r@225A22622h22?3224g}t22\22  22!F22T22 22/)j224ZՊ522ނ6225V222242/62262f܇2422 522pڸ6262222222x2422@j242Bi232238422Kc232E25223[a_H2L`_\i2522D|_`_kF223Ő2xM2 DW2322?2422328632E242 DX2422<2522 3夨2326452DU2;25223252Ut232 Da<>=;kk2422 3⛟w232:262E26223Í2qQ32 E2422327542B252 DhjhuH223242662DU2:25223242Qw242 DZ3652>2522324282DS2625223242kdBA@<2 Dc?A@>fl24223242A󶿀D2 Eɽ2522u232S f232DJ22=R2Aw2322:_226322632652642222mU  '8GTVI  !!1Q3! E@! G! ! I! ! G"! I"! ! D"!  ?"! !9"!  "4"!"!"!  "1+"!   "++"!  ! #%$"!!"!  !! "!9"! ! " M"! "! "'M"! "! !3M"! ""! ;M"! "! GM"! "! I9"! "! M$"! "! M"! "! N"! "! K"! "! J"! "!! E"! !!B"!  ;"/rޅֳk9",0"K("X#"O '". 8" J+"$?'!! ! "ӏ("! !B"A/,)(%! + " ~"#! "J"#:"#"! "$Rώ"! W'"%_"! #F"%c"! "%c"!/"a"!U$"$(+,,/i+,*'"!%S-".ܩQ%"!$F"."!"6"!"!)"f%"!"&"!""!""a%"!"" ""'w#""# ~ހӠG""$""!! #a! "$""$%$(e#$%$#""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""#^""p(""(f1""%2""&""[""0$""$Zsh""M"" "" 6""D""""^""$Lҁz%""ۂ&""%H""""%" '""&"Z}"$"" &""dֱv&"&"""""""n"$""1 ^"$"4޵]"#""#y($""=W"#"6"%""#zNUSR:">TRSSO]"%""5rRTR_8""#z"n?" 5J"#""0w"$""#z"('#"6"$" 5K"$""-"%"" #y㝡v"#"&$%"5H","%""#y"%"Hi"#" 5U-/.,_`"$""#yl"#"+"&" 6"&""#z"fC#"6"$""#z"(%$"4"%" 5{\^\j:""#z"$"'&"5H"*~"%""#z"$"Cm"$" 5M#&%"/"%""#z"$"("5F"&"%""#z"$"_X321-" 5V021/Za"$""#}"$"2򱺀5" 6Ÿ"%""j"#"F Z"#"5<"".xD"2~~|}l"#""*y~}||}vR""&#""&#"&%"&$""""l8mkj/>? ???'?2???K? ???'?<??????????????????????@@@@@@@@DDname icons8mkrt8mk@ӰGw D ~                 #  +  6  B  O ] l }           k    $    d     +  f                                                                                                                          &'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('&  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   fritzing-app-0.9.3b/resources/system_icons/macosx/mac_fzm_icon.icns000066400000000000000000011264641272363601300256450ustar00rootroot00000000000000icns4ic08CdPNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o? IDATx] $Eyy{o`/Xf!ܗ$($Hb<ML$"j j"Ĉ + a]b]>}|f{zf{=U}W}]]]S13>ӍW+F$@ES– ilIHV?AZ?ZTQ3$}`E70NvIYͲff6u؆.cZ[XQ2&6xuW'?na׻iO}vib7oVwL?v-=Ž1YFoX8ȓˌmo!d,e XLQ*˨Xz#7ٽ7յ;1սi}ӾLls[w#<:bgz*ƍAl-Dsj F0[)؆kX3$KŴL,FX,=FGHѱik^ #"veu}sٲ ǀ=;0PQp,?AG;}dG3xಞ96etvxW'1s6-mri2.b<Kct&='q5gSw-O_vIVq;J x"6FS}O7oo ˹lvMzqu:`^9mq0M&Y6hf4݆}3f.=uK."86`x,-NQB@Ns2vl֋S,Nbs0O<(zPx hթʙ&\ N=v{wȵ4ܡ :1 g{0ZhR҆LHl0`:ر)w8@9ҙ6ĝCy/YoqgR1j\i406ۃJ[Xlh|alɞ!賹Í6x<_׌ᅁP8#Co]o}%Kqi<@5:@ "x0cߗ*D)jF؋b(^?_mڹ/Q Q*%$O RmqU@fRVB5ËJNHti?j ]ncn6`۝cpuUC|g~?q NqG&ޙgڿsq.&le0P[NȜXOݩ. .s]);5 =9 0NX'](u߼M@0NZL"(8FHM@LDyXٺzN2N`0S׾^vWX0;KЧ ceԠ~ww9c~4 F!nr2(K4$ڢ, KX 'ǗU3A%DFA('lƨLx:0(@ ko}tlҶ-켥O)fxQACBD4dҌ1 r0#[FxLcA:3d@<9 2aCK z< 8n < yHg׎-;? "rdrٕf\lLp40n 46~ȋ}i՞I۷{X͘1yLʈP>H' !`5p}ˏx0O  B4g؝QPw߁C_z̆۷Ǘtd= q a\v90o 4:O?q2Oei< (g Cu|[8P)uwIxs۷w3mFh  )v qa ^<덙yp7o󦙇M&l ?^9z6h i^' z~Rxp鑮;8qGe<#sy_?/w|0ƠKs]^qc(EKDDyuo6mЏ:()f# ?#8x~Kp, pm98#6 җW}N;er !5  *A a0U6W; .PoNA.?'eH,pΞ`pesv;ga 08vêڛ/@cߊ4 "#ֆ _Ns Nt=I9t|l| i Y`s;c54ܶm @f;f ̷֯/3T 'ـMNY j@@!=M*. ;?>~eJ;}C69kg㔱D nG (uT$\ (Ws+rmSVNJfW2= ] Nxp \]?zӹ? 3@F^BH D{cJsc~ev{NkcOVS-z6^(89DYh,_Sb^@+ Q;[̆@j.v"m0/(̎ 8Lϸ{q<PbC =/sP//v5aj3S¼rL!b~eװ, NI7^N-  ->IH;mhZ`MAO٬p 'B[К%z5-%|lbvzg4>FXŀOj2>G[V%7!w0J4`A&ٳ먻8~%g:()!TyϾi,|2Ϙu ځhtn =(a C&}Cu[X65z-W@=.˂2 Țp\h=iHicpccq9Ax6?D%~``ރ5<9i1ZZNK0Ly\17 |ܦ-:3N3سTdy &h4wAb0.ƷaU 8ǖbt^|;lj_,mcs^eүim ۡg2eӔsXL>k-;eR XhT27KPyQ1e2"4q>]kC8t}3{P1\o g~٘f<;o ]BƳӠ  ]_e^{v4=$ B%B~*A&ŵЀ QMs9u_M5!챃 W >~l^WTg*MKj+ؤC@+s`^:/R'4vnч]TRU@Zgs9-$wivP(rN !o;6N9zbHc*@X.ޕۣ1B7{R, XK4h:аWz.Xsu|LqnȰ2,>78Cm4njϱu89)# A/NB|9pU|کJ$E^^c(%cXܥ@fW/9r~M6J_a  LN/0 q؆U֛λPeA/M diYa^ KU.""oRZ`0oZu/4yBv6u-_l@a,yv:! a8bx< "pI>2?=q @9 մM  zw>&ín:FV8, 1 F~NaMA!!O|;Ei|+ ia!@Ba]AJc0l\'ɠu|?+#Ìda*M=qXҁ&A o1" a`0`, B6ญM:!Q7xq#G/?gx xG9|ة 2C9m 5+Tlऩ+~dTֈ|qMԵD ;ԕS3€7ǀ̍_4 hӬtÀcjx 28< L3(?Nk06MNa` )HU˟{3ƌd40ؽXl ٖY(rC"06zG]˜5 њf2 >NhSs蠫OHñH_gZT0ЁdkjDd 1iÄ0vZIT&5bݪTǢT!TrtZ}Kiڒ KHܦ^^{W-Tsw ևe@I E;Zo-A_PҪf3L˅*62qģGGi!dqMԗa,}q'&^z:q"7Qowʐ` L_2LzT;Ȧ jOPkTfv:[~԰! 1e~frGlٟ6J/ ?z5Yȉ["T7ևBf~4i)oZMG#E *}57^^["{'@]ig``9z43@;0+9 W:cBD_P:.y,Mv1iSeڶ~wyb{M4aVVQw5ݥ&\>k{McxAxWȳ^AD,׶>H&X3qqMe>{gjKBk d2m=X?N!c`f 64lhf`G̤'yo:O?i; G@+d<3?ɜ4a۰v]?6m;m 4CYBn-f9#C8%5$W}ttɓlӪ%:IёJ$)_8KM&\qMS.zwWHb\ Ƒ5l!;@ v @!wɐ^q2kH$F?<}܃k駢n@gʌҴ>~˸y&dlk˶\u N(Ë`2`(z@ tb\Y_yMwWUV⬫M1a4Ə)Ä'AcUXq) y84è^aGo_?쪿æMM8~ 7CC*ޭkσ5+Un 22 ̙5kZڊ}zʏdR霂i:br8v(ƌu9S'8x#O/ȴM_d@ . 'cJF+VT,͸%ѻUZڡq~CjhyÝfe#u>6C[i:`1ap!3a3  ~\:8ٺi6P`xggX"?⅏5,נY\%;ܠ>;S}6Jѹ96φ1vŵkiϨs `׼{LfH)4@&Mgj4g&k̳g U󃞬~heJ.+178Fq9ܝ[/;|V8HtCvlw)[>Xݵ#@e<*V<na9\)`0P6^mbߔc7P.2XuӦYrF$HGDE f+|l^Yj .K6Y/2;2duxoL@CJ1Js;-,Ua|j"ү 0v6>@7,/:m[N$dV#Ey8TМ1pREQ㮉ߍ/g''4} t^fJ6>h|DNJV=:b 0멼as˟q(DžϞ5ISb|xY4,/avj`gւLȹ!) !OS7  xgؠA@]\`\+[xF`L6S @!4bmad29,+L6Um'ո3QsUx'`3 =CmjW2t~0-Mou*wojaqeEvdp~3'h@#4ROj;Z%Ϥ{tkQO+TGǼ /!A?{-(c[ԾeFQ (kfAd+ e,Lw=AtqjV'gug ]A[dCwgU3?EuG?]yٱ=ϡp\_VWjDp۟ #1GNԶm$\)#rmjN1k:U6lwHx47[Hnȏϒ-Ay2xҪC00r1 l{7{TSU\qUUg#)u~4-zRR;.5 ֊0[C@UZ5s[peN@!}}]P0ջ+=tWx7'B摴NclTݓ͎zO%,Ms bN +kV(:89/|PC<&Unm1#*Ivl,E|H/:@`d?(DHG(܂kŀ]ǟi0ZN-vcx? ,7+˓' jجVX@]]Ĕ%ϐ_>{n(!28E9`fƎj_xee0A^Tf^5O9uWc5*̘Fc ux!BdЇ2C=,C0ZgX<8S(ͣq;+r9J+517oOѵfu kִ&WYU9?sh@eUZ@UY>gonY x ǘVtų;.]-E/ ]` a<ݾj|q-u#xcsΤ[0wNS7G@c=s߷g[k[{RN 0mz F06lț#O<42H{~Kcŗ=TXvzk}T^G:?ï$g6xqgUzUƷ}_m~{Aq 8.i'Q 3 R7#+O>]/~WVm3gD/=LFg̫=!Tk^/IEbX:FS#;9DJr6f*Ԓw11~z= H%7J,NO4,x|:DzM}*C3!5 :@oV9,ޮmYjlR99}{Uj~J:Ě}i: ~p7e#WMjjQ}Wkc9{S G0m*sd65&= )PU8k(ȝ\`w:Q+z8fq*U){+V_0f'D(ӈN7_^ TS57D@s_K1\3vv}HVR8Vokm›YQ]hf5Ԁm|NP `$ƶvܭևZHsxve f̼"`5{^xE^lN#]Xdg23̛FM(qQ!7Bpm8F=KWS +αmq)L%V]^^aX8zj@@=-u"qG4PO .@4  b"qԶ%DCD@=5 ږDӀ8u#SRh bqDԀ8zj[ DL"!"hPOmK]i@@:D SmK41 XT"N,^,Ħm}4 >zm-C;WVh+ݭT\Fm;-ܥI|{¹ſY:Zb.9y߿W*Uq. (TQ׀8P!`虴:Y_JNgNQG=JԂJ4 {?ަiu( `&Kv_]MQK"s Haa='XSn5o?)50р8>\p'GUSR pwu)4)o2 h wܣzA=ՇոNz*uwո1~ V󏠳6TjHh hn}bD5GXx5ߠEȎfМXBM%bG鴚Ϩ>VV_9ť@ uC.4Ӏ8uH8dpm&XъȰJ 8CN@ ?PCظ ڒ~STވ^+,>A?< }]/v:皷)s49xEO>]vF;4 ,rAxJՑ=OPPJҬ=Nk)9QE wW"\-]O + ob[7 ϪߡԿž8E I7nT:¥82z8joGuRG5 ʮVUijppjTK|! PgcU/?K Ԁ8HvK@Gl?c5Σ6zj( 1H-悥B|DBkjlkKjԆ]:_&]s>i꘷PuycQ2v8F!zw?T*N/ P$@\;f!ɗH eUCT׫΅TCU#>cڿ.EH j@|"@Ѐ8JрEqc_ UEcCF?K+EƆ~V|5 :o}.mV–=j %F/䩁K"M8:T5 IOy= mq~ zA U) 4 "m=Wurwߩ+#"Co!(xrp&MV'b^Hb_>֜{CX{Oھ\Nk\T]C2usY7Zۀժ;\nA)g7 'LTV2ӧX* r7>u }kޓyuj;d&Gp64mYWӰ`f tnYxݺdԀ8[s=Wq_]J2{nJٕoZĭuJP\?U-ͤOU€b]jܩٺYly44}Xz=H\ҵӀn]1cQ+ֹ2;Uk7CJY:Z| nm/C>Y1ڸN?h&i@fu蚦=it.9A%zzd8w&Lo@ q Q{Jf\kZʎ>S$/Q}q}ɚq?ꖒx"L߹H1x.X3ǟ2Ӽ?kgի4 kT1۾@ Oo*౜)sjתJ× ~?E<ֶ>^T~շaխ"0w>O)=Zp4j@@uo*9z!tpix~ڠˍЏ6O1S81չ.EEARcF23]m{᥮m6ScZd#/n)]= Pɍ `t49_ڗxf*@5 ]PbN{ʆ6WK)c,#`t]C$=64 ` srQ*1~nW^2cS8}> ;o$1xOὮ~h[- T3ѣA)ڙxplRΈhΟ='tdDzh'}G׺0;1(}d5m&4 k5O܋^'n(\ף-{04԰7}6 ]p7rLdyfkRȅ=_v}-Ҁ8_,6hjnG( 9,t෿ ,Ƣρ9g1a6/jqs\; ##)n_t_~y}ށw(lU0mka9Ϥ+VÛ6?^ տfmgASJQ~zjx?d3}QAU$bTNP`o_ /ڗaFՀ&@ԠX5<eQ-f5[&Ii@T"р8R@D(8FQT4@h؏  4HQr7 RT+1mZIXfQr,;P*@$uTJ& ;$A45 } 1=c;?Dňly"3"A48{&.y6GaЬ4xM:p8q e>c, X8< Ԙe"4(psg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRTyUUW=s -#"ʲx,+ Ṣ.7 r*0 3WUWվY+:;+ʬ̯:"|ENzeBfb%uf#m AYk\Ow:GxhؔCG34߫i X?:pzsʦ/;Zj/1:FmM/[e;Mc WusuSx} +KSW䌙upH w u5^va5}:9yS5CʯB$Qk ~T白7åX 믔YWiͧ䕰6QxJ(PNn$_%.@Op#{UGVd^m2#.Xrgc,5o-UԱ|mT)^WXeѦfSPK<dd}g,g2 oN/_;WpRbR`h*sd:EKo_}FV Tgn9_ Ju `-j M;T[G)~urLOL1ÒyE%Q_JjۥZ |n۶4{q3W 8[㎵WOHW>/RE )oM"X8ƹi;wPA7H\)*NaQ%e֧q`":Ll6_ Ra$~[OEX j[ЮR/56VLW-q5VxvY (N؎Tמ+ :$(l4]k2%,-rlK=Ngfpd"pE@ vR=}v)v"a3I_k'LkC <%loB{%Tʱ3͌cɥZ!a69g*aV~hd?V&J`X sω9wtbo I1~ۈu@T*YIAU}cg.mM[Z&=68KwqVVDj\,}vtڍ|d.[^" $W<: =(kR{)iԥXMZ5ĕs'3mLw1CB}3= =z `}C"`j:{ K:Am i8%$WRK1ZJ3+^  @\FL+`z#8͂kjTiN<Xi/4\DիK%2v9-=QmT.Ť*mF4Vo$`Dr r{vQ- @~t4ּM( ZG! axr\J\{y`v20b:TۧF"&Dso٭MWzuKT^ਤCVf˻l芼b-A-@K+:U5b|(`1`=ZjҸDèF1 _MZ TdjJS|yJX9Y΂cϬɵO,%P]9{AKƄQ"&mVO%@U;jMŹn3SVCG/޲*McӟLk@\GB`lf>_Op'2^Kr4_^ .&a3M)gTi6x6Nc+E)9ARɠM@]L@=p\#/onfpLmTN&ѐT]Fc#q`kVXTɋΰwq+G(RdIVl”1ӁG($R4_~S6F|v|JKBQ8ftHd0 /a6MeV-i48˙e}&3lk$|6pl3@n~~3(j,(d3[WTիڲ  lUnS-Dѫ8鏙O7uhI͎W94  D[m5'`F:"P⿝㏩S±hSI#,^ipQ^U鱄5N44DIї0vGM7Ɖ\Irx|&'꫌M[?se6K~Nk<QʤJV͢fX>Lj h5Uiܬ\Ѳf*:Ufolh1F!ϖ)]G{+>»f8 M~ (3il~3zix&2jIf;i\|g?Xpd$K9e^.R}Hfjq" F_~{*Өok빳 U0ݿ:+ekf6r-4p/cTo 4ic k}IuyG*@9ȕ\~r,Ƒ^_aQ+71\reijҬzWN4%y4F)>},8QàVV\.TD1+gf }da34e3qÏqQ#S_ӥfՒn`ժfx DpeI=^+ yLOлd8IDATQs*N(q?ޒk)Ux;)/G.J͙_1iȥgxvG:d ˏPv.<(el?GŸ* J W}nV<'R~t] 7éo SXAQ|I8 k s۱ʼn,)ٟrrCҬ?VwuG; mSJ}r,?QÉx -aWeV947495~4 ` AAȜRĩQƋo:kƅ!/SJtũowSF ` bAbL@r4)"ͯƫ/TRƚ7HL5 eF[#q,Q@f*sUv?Lwb|יcg׾vI<\48AJ86ܜyQҝy(_yW~ phU\U \^3^æamyaɧ+#9ְLW/]/ f%U*R*@'ex ;cS_Gř2KXHw1uy,n;4eJﻒ}4|oh. m#B3Lj%L#?DAt7~u1֝u"6y?3<:ـ9gI$ʿ-m- %=:YqΥ8^qP;.nҖ>F ?5 p !$:yvʿhSOgaYQ$R}?kK=o+B7 -O@1: ~?z>RBɎvy(MmsӢ+Y/]m7Ղ $Gͤ[(~q|߷Zru¿Ĉ>%{(I /<o}{qcD{A 60FGAR(gbZogfW YJ?{2p,#Q/@C`!Q s3N<}jY"ٚ]b-}ۭ@  P@"_\A_nʋq@WhşC%WtZ Lt%DXϣ%"̵̅H:Dv077|A `pP R p:E ?z)6ѱύx_h,ԗYP+|T @ `pP R :M}Ŋ߫zGG nX6d 6ڊҼVNڛ7:V1 M'CZ@q{'"-~[swD$::i`gBjdž FC-M@?s?lopy=–~tH@G 27o/}φ7QsBD@D'g.xm~9ᠶKhߋN88&DhE~j>47z_Ѵ>Č9\6ăL@\ MLD>1:{$-O,HE@ h0&Z(Ory w_dትt5?rm{|7uK=IMgZ#tЕE^L:ZsGTpTw8h_ &=WFdc_ S?/Kx=4{!a&)NX_ˌ#O]IlNzǟ̺ GAB!O/8^ k@G@أ`*ݷ-Xb)d#וo:~ Ƹi{,Dgڲҍ.="A5<(jS/@hz4u\:/T@6g#CCt+_@t3Xf~08uo.<(fFEՔN$GGik0jP@`4"!֢2sT0WB} /kht7= ݕ6'8gҼ?#3'ǿnVOP7Č 8 <|^2/W_nW4(ڕzҝ ~+!bwp0ei 8 1hIȒ=xw(]O3eG:dvṇQCE1@alœp M#ip U|톬sɇ=`n6?AG@ DŽLyٌxqJ##!`L# Y\C_PNT{Qh#ΠO0< <ձ7򷘛 FY-}y (GE<Do/7 &vҩVKߗ`߱(G Lg? Q/}$c^bv&_G xu#D}/ۺ%+3 k!2#Yjk(hAsiaG>.<^qapO4R@"nH@?O=$%;yw㢽Nxӛit0gdF^pY=c&6 4>+4 fG& ,em g_ydx3 Eю 4cA]ltSꓯc}j4DG@pR+;[I\;ŀ4 >t)[G <6af쩧}kT&2#x??2wO`Q/p{'::U`0#@fweo8V^$9#_:`qϦ̀ijOp:m5[NC:^'ݿ{Πr6?HLr-7ׅEk=o |w| 1^|3g|rןh Y:7qR>+' 'wtv >@hx4  yAʳ -'wyn=W.gwWRɔά]m+2_M8/&ˤcoZ !ȃl4 =\D H qz/Pm4Tѧ+C'4cR,(7\ˌhd' 4sR:7cVS$vk?)?`&W1  -+tՓDcK,5G\p 0Acbk)DwG5mfF[<@+ R 9d -oAǽ{{/ m\;׿gU@6@y`b,՟X̃7'N]'̬_ 1&Zvlf럨rדV]^}o|K^JGT H4*WHxR;=۵e u4 wp (NWx'dw@1F 4/ӟtYH(0 [I=. h"D@ @޿p a  Ow_h3Q&o4x]ŷ# [80Q -A@]q0F[_v3!!z__ڈzQB/XQi X;iWPBTx?Ю|6lo8w3K@P\ 7/t?˻3m6fMr==?)H !BQm;[6PbflQP:%! L"`@c mH6GpZ|fg2۷5$jh8 G A`2Ѷ_NVY}S֡04H[/x+-X1! &n6ɿ׿-?r;w!BC{i&jhj!Єo"@ EbyenW||D(zݏ *ۼ G}T!\` .#~6|<V'WQ[%_V?[2[7.=:F@mGݵ6h?x`D ϛ7~~h|P ( #`y(폏i'/~vY$bwGq?V"VM%: .zi跿m[^h s~6zcϳE&:!)톊Z@Qyrs/gWz <*#E{/ysLyj~h"@@ ">+աFh" h*GFh鷽z=y빔^.&^'`=x JeB~GQoe4Z[lnv!ŖK|Q;F@8C\~$ 88;n+ٳD8`JZt+=ݯ[Q-y~~3e7ohD{;}ǿQalԳ/ gKHX-E}{P(bUm$X KRUԛNGnBd}3_N!e*b zSc Z9_9oy7=`M#QǾ+*\&F:@J*kO6 lEцu@0[7УG8<=Qa|UwXFR42΅l]yrykXj:Ъitj+('eH $1Z(.o|YMC(i)j-^vu6jf 8[rgj. 506b7:c?;i;6Wt4'{'\xfO~iͲQE@ R`Dj l(? cWQ%Ri3gO&0*| (XihYٲpoG nbtpeq'h;g؏_~%7?p 021g7o8"l~͕3*]b-+41V|g&R=h A@`P.d[+j6:k=ˋ}ˍ/DyE7>+FKzr̳ m@(` !|!h D#*7Ը(ۭ}Z˺裗>~-(cpmsMBƜ<a'`ms*O\XPZY' r1ʖ|ظፕY'0 9QH@G2@\r=%Esig މC{6!=o}Kq\ьOd{G)adCόa' c/!͐[Y *xV2MӚZ(FKE^Ċ}׾We)_mߡbndh ?Ns^fO' you_`翤v SoxU@؟w#_T4n-jlZ`x7 0}ܲeeU,nkh)rN Ivu#6n΍i]:ă@  @ kL?uJِ& AX/ d f͟πMp6c"Q5 ~`:uoɽyK}խN6^2Ogmg8/ jeA `| F^ XKCa"жO_l`x= p)fEYSEdă@KRÉ΀@]+EA?_C߿koy&U~Vv% ³MFā@hI>&~(|4KīY/x~OVvl;9E4~QθcA!g, I Xjw[v7%1WRY dYi 2ڇ?T} ܾ` 1@ @q,{(|Y{K_:=3Q"S'B_8n÷ϋ5\]QK.:?ci B D@9AϘx7reC ]qk<>{ ]ʖg62Ϭ=E dHe֟b""[ V]7! #3<[;Vr}?圵{n;d6@ `qhgNt Řswjs cE-2+Q5 K{Tv^NmEյiч^|bH@ ]> |W?D5 L}/8+ycV淛w,v|h7HF@FQ(󕯥6s6H;O,нwѲ+N3_ik,AhEwJ|ɔf[WGM _|ѧJ\Bxch;]R#}ʮ֝Rn_8~I@8R5/p+`;I$\p?.ڟg' y+`73AO?T&w˿|q'/\rDf"?T S;=3, ? <{D!:~޽kW"˭ߵy 8@ >6?slo92 @38@ }س;=s"@``+&?}UUo~>@:xP'h)4{-<+8x7ޓA`F!p=vUE7_zU `q }IO1wl8@ Y9R}}=`ڟߓA `a&]pgeyrg>$ć\a|@ ,+ 6h4%j` ;mlW xkWaߓA ~01G[T^?24fՀ@x_/lok$ ^GO/+?#4t[ <*_ >s~5_i }u0P9Df;v<w߳lvSʏ D  x 2W\cst;uu crϊ?Ӗ/D[ @C@+mZ~+'RUe+׈LDy7-yЈP ` o߻yg "i>-m8pt `}/y)ydYкR\  01N2: 틖:l$d g0~f Z 0:JwWxHw7]t@%21c ǤPq"8&rG(_T+y&N|g!6箲B=G @0?l9Qvۖ'_}2 (!?N^]W Fֆ7Wqܙ7L1DX?vᴇNq1k@OaM"rH~r,{$rK9j3| f Z.-bBiznoxX)YNj2]$=Á8:q[P>vۋ(M$44 fG   D0M@hy   M$44 fG   D0M@hy   M$44 fG   D0M@:.4 @>4   $`*r~2xj Ā@kuk{X\=\er 9@@:~3e }ۘp")8Y!@D@n0 Y`LSkX|=4nr)y"MDO7㧖B @ lb@@FڼtX"Y8(T1,1[0sf|SKJ~Z5`tM@@&t͛g_kt/_'?'%,NZ?n **pS#@4_⥌R8h9[x0*lHE.*x5ė4װiq:Q^p ȉ [O9$N|x-$^k?  ):ʱPeǚ&ǚc8 4a iOT#@'fV}5Y44p0J@NDr'':=$^Nՙa  0Ͱ*w_|U?)+aә \NUzrȱ''sp"^x_Zf  P^;'kYy98)9c墾h83Z^c몄/m9i%(Ԗ4;e ir"xb8Ć*o+fjZW}('Syc@@>rZ3ybCQU>U 9@@"j?rB>d2\D@rѦUPJus).P >*BY|VjIENDB`ic10#"PNG  IHDR+ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx mYy>J=I yVB[2C[[|ъP[!B2f`=mZP $E%URJU[{=s}=oޱ7k{78#HD HD HD HD HD HD HD H?W% D HD`dc 2tsD 8D HD0!a:[uO|D H#p5rD#HD Hl#=ɪU4XD Hv^6{{ͰD HD  ) TOeeԺ1>D H\orM:h$ID H-pS/{Nq4Hˁ^6ɛν|q9SΙ$@"$`[a]3cz齦U|?D H M6;ɵnr&6֑r"$@"\Gdʘ-ɫB~@"~7η~$@"${@6R{K;>YE},z: 4Hˉ~6̵̫g~N]"$@"ll6O{,ӹyAaK$aA`y2;LMJ>HD H6@6ZGtDO`yno:wD fu3gԹS7;3Wm>7eW%$@"${@6U{J{Dj.Nj!T9_>л.D H جŎNUyqq8,Yq)|n?i,۝_%'$@"$@ ]w BcLF4gq/rG.}._u9.QȤ6uS'N/ۗo>TMwKN_X -[.N<[pԹgN<}ĥ'x.\<ϜGxG?v(|3@)/C:*MVY:?etcoO5>bS$@"$+ pzt9pdS}mH5~XWXPP;}7Ty/k]Tm'ϟD9ܧw h_]7>q/9k]s[g* N;C:๭K_SO?pǾ>}w>%Rl>SmTe@QZ85(U1i#\1'@"$/5#{_GrG\^"oxyWhp]7oߤFTgMU!^+k:'g]y╧z|"$@"&R<yA,ypS;D}u|We79w;V^\cyc~x SlQmzbQ>cy_o*Nm4|Mֳt5W uGi^A&k[|T +zok+?uדO/˧pq{@lPBQ-%:磭:K5ӌ]gL"$@"bI#.ËF>u߾;;D`SxºsJ?Qn|1:r鞊Gcys|Qyuzn+<~ʫ֥7Bfs)|S>S~&G6U|M$@"{hڎ= pY<2~kO<;?mO ¦@a>ʮ<7ιegr@"$1Dkc\r~pY<2 OvIU"mpG|1[CO{V9΁EMt|fcEX*;IozPʫ|獚5*t^ȯ/=Gx-SWE_fW cv5ܯiev&@"$ ~EbGC} Ql[#_D @C@{p^i|uMܳœ%2F}}0 ƁO@.nuU=ޓW?k'xࡢ>W}]`O _Άϔ3e)'@"$G"sq+! N5e6J~Qc9߳sー %ƩQm5M2ѝ-@"ENdv26^6(6XKC—^OG4|⯹uq#ts)6ȳ JE^qn]c{y ZsXBPh~5yn>8(U>ߺzΗ?σwգ嘺+)}/5u6J.HD 8BwrD@{! / oOH5D?X.f+6 Elަܝ13=0^jp9y搯_q^}ߧXۥ'/>'Or讀g]L-hc]&$@"t#df7lz›$3ᇍ.Yb٢/2k~FF|/[v߲n]u捵La #^՛gL&@#~6Qu>E9_wýC?/p~p7 ^FZIO}ʆOD HD#5ӡ_H.`WS~L5?X ] >J*ZgёHdxcM+/|#uYǗ˨og?ʁ,^>!sl)#LQ&@_ya]鶮/>?Og3ށ|Jxh1/%ki}OD H5~q D^2:QxSMo"HNG?|]7Ɗ4`:jˢWac QѶ-mMs#L]tշ]5XIqG^/|_KcwΦdg}OD H%\!KFG/ߩ6 6Ve&J]#Άx3FP;0%?/n@8=*щ'D HD!F-G"0BE1p>Orw(&|qk:"A ԰rZ9ix5xM?QˁT:=Qjs} 9d0=G9Š/. If`N^yї~'{_Z^(u)BXsggwE~{E5+Iu,_Ի[X̻6Y?^Eu藇>֤zLO_bFJ`s_p{ȿ3..#B9xFS:_mkӤ~\"$@"pH;ɉ2umŀw*>|/=o )k6y&M(!tn?N鼚Jj90dLud~׶jc՘MykdaIw,H0zMrXsrHtҥSW+_woƯ{5{{|Z64fS6qA {̎ 9i"$@"ph_jm@v*Û|oh}@W6Vi&J dF~ja^79ԠQl޽yQ3J:54UzѬmcC㘲.8Ds#f~'Lk@C\n{_U\X=@JE|"ߓ G5HD 8dp-sr7@| ;Ûd6N_xNwH}F.dMRֳTuR,zCm 0^qFwq@<+ fS2)r]?1^^tйo`Y>"HD 8Qˑ]r]nfl"$MrRT ^j&i(}Ce/nY쬅_5*YE̵:4G JNc N/u^آę?7/帶z- XHUM3S Qr"$@"p/x!>N&g7\}"@mRkeo~jnd-똊_y5YvŰ&K[1ePPoF|EIŭtʭ&V(r\uG~_vۛox ?bzFr(tؒ&@"$v`O;Ȣ*^:}|">W+vb#݊~\ޞhXWUʃ EsG㋼*m7?QEպEiq-~Qpcn<~%݂5Wq^e3~?y S1['O=s 駊ˡg_ ;Ga+?Ee`&_sD H@p@Ne,CR>E9dl)P[n;C@?LtM{^{kڱ ~|=rk:h1!Cыr*OUoG1N1a+/}oy_ #ߌRɱ D0>\7k^gw>cU-@W+^9fۑ#'MD H,#Xޅ:(>i"pPQ1Xgts+ؤ7jdiC}N_n9M:1bӧSC9Qޟ W?׉Mc<,Nv N Kwۯ:3ovK9Ug(]K".$3Aܞ|"$@"pYP# e%[jcbdH x(PLk%w:V[MQ96&Ώ!@ca}}&J~($. 8pGj0?׉gM( /KqJn./Ͼn?Wn,k􎢮NCqqt'D HD! C5N.hvs@buPsB$^6,/4y=?c5I!4f@ϗtGApZ67/G#Mb{x5PQ.@*xmb=zOy2ջ<|;{y帲S_ (<-\M}kOD Hˆ@l{Ywp%%_k^˅6umn+)Mu~Sy6&nb;{]Ξnz=^<yJP(q,qJ刺|\1GM٘Kj?5c-SPEyO>y/T;zOqm`1d(| h&_菕ʱ_ 5⢼L=i"$@"iˑluh^5h5yV-zrm75&kɽXqWA)Q'rQeǯÏ{G/_:<Kq1G}VXO,g_E{/QC){z9j" b4x=T ڋn ջ5}/v[֫6CG󚿒t Y5135 ?ġ;][=o♏o|ۊrJ'7(!cHD Ҿ|'dnt*>FP9c~Мw@h\^4P[g3a'85dgJ)E~3ۓݗ᡽t}>z:l=ګS~1dt̬<קgVPȂN2ƞ y; *k?/xsGeq{ˡG i1U[J8zr"$@"gpD H}D{wRmYF-6k7M^6jҼ! r:9RT[7L&YӸZ;3߁=GשayHuW焯5g_|uM΁]:.9exzs,;wZ7k&z9WRŏ~S9W'"kvF#ܧK: PybʀFͬՃ^5 qH pWvם]П $lU:ҋ׈:K"$@"^#D A@(_MtQoT#hr[D1O^8OS9KMn[|3b͡>%k|]4c2\!T_}HaI/nF8m<}[~|χ(jml,HP*uVn8ܞ|"$@"'ߑ{2I&MD XD ^KQ9i6K<F{ [IO ~ ٩=e@x M7n!l9$s < ~F #8U. 0+Ve?)z蔃C>q(7,p(^U23WT]Ÿ.xrk[#-C_E5+iD=hOD H#߽9D H!@3ʧQH13ɳ; h$L&JrͩyvkSf孔n:5&’s}]^Vԥ9 u +JΏk~#ygS YCj59M`(S%B_ܺ;?M3<\Spv/C~qt'D HD`cᅪ%D@"$5V:jAJ><6v '4`/op{1g[Gʱj'\5jBU78b J,vQtw:|A׶@JMEm!09ؐ\G/\zN'Tq5u+^9}7}ؑ?8K_e;gU|^PmuE=Ë2GO}RND Hv@넙 HD`#ӈVyk -eAӺHIƌXdistධnC t1#%^1^ gt؜t㼲Lw8:P.x]dl ٽ?u3@1%6UPS{yh|:WJAz/}lxϝ_&%*<JIU|ID H6^T̓$@"pH &Q'9o*p7k_?~j2 (C3EU8:GY=(&ϪLux'KF!C9ʡfZ팕"z\(Wg{i}Z޹M\7{[\jp }։>~}ZTהCw8tl:I!]=]I9HD 1c20HD` bwOE~ۖAONzkh,ܒUIm]|CTTSkxxC^搏ۉ$;"Cr;7Z5m\Y6W qHp~m5v'?]_8ŭϾw|䍯[obZNޝKFO,$@"~72M'@"Gb6:lۡ*WXPVhw_?y_U|-q)H9F56s{@"$ A@"$B`K 4x1yCXêfq^jW&>xfֳ~bDCUƲUr8օtY ¶6Qp 0jb^_#;'w\5<kpWoQ؋M)s$@"$G 7vaf@]xq%]/V~L~cHZ%ļSsbd˜P7E=Ϙ*>cc^Ξx!0z`cu$z۾cmK YWxPNCw<\we/yEp(](ꅩ\d!'MD HvGw M#US/WA׫ &rKtXyrBes})zzWqU^Ψˋߘ(녙cmNElhx,޸6Ϧ 4LJ6xyStRꊺx=ojs:ݦ\ eCE4k[_ ohd|e@fnYO~C|_~Ѓ7}'@I9+G|D HD`ߥ;N@"h:v^Ϛ KzS{VX9EPO2mlେaHcE,SjoU5ǺsqNR G @ʶJG ŋx !/ni4 ?RTqP ^C_ןUxM6H5}\|"$@"2wʁ$qA9^G2\zX6Q;^]Z7~A,jѻsSBAXu=+kpۘ~PtZXNpTgw-sNwCͷ_r)vjVG}"PˋfyVtg}+̵'?G_7VC\L$ ?d{D HD`9D H+ˮ#niQQH 41j?z?׷5^ˋ ,gkc(J'/: _Q9]ʇs=ywu5<Y6b\7o:J}Q34'uk *W4/<-EMҋ]|"$@"0.tLc"ShS1m_wny|G]u?lC4t1>@v1]?h|w_l.,깑wev:g\FԫcP o$٘}#,At*?VC{k=Y>yvMtj0%a ҊUZ:/E9GЎ|knΥx=zЉJFYOk\5Uk4^+V_(lwnКb. 08S#ymN]tZ7P1O?M+owq -(:5:wu;_D H(G"$>#P^<*הm<::uк{kF_k^tl7X3eU﹘kJuQ4`#+1F?o&3J醳Q7IΏ6fs_g]JGw晧{߸-X+ˁYtsZC1ڐ=.$@"LL"$FR]z0^|eO%eJL:FQ!5l,[ض9e91|%~z\η̳­{QA\T]玼r=w&LSzD?\6f!<_o֢ҝ 7"DT*|ܞ|"$@"0C_>3E2@"$;C`fʛ o]FR.i}&s].@6hsy*E~81)[Ͽ##v<>.+s2?1$3"e nE#_y{vǨZ몃 D:bmj4el{GW⌯d6k`zހh<mʫ5^zDi|ݾSS|uן<=O+Y𕩮_t=:_؂lx&֙?\\s#HD!WA|i* /\غyb v|kDuF|/X^Ϫc=]<8z|1<ﹱB O%8h:c 2> ߩ1 =)1:1jo롕's|U[IcFY7].nRJ?L1s_\bnbو7 `R^ɭ4˞{RvryLӵ*8CrSŦw9Rg =_N9=pڨKw~{,f6ggiGWT3;(C~qt'D Hc~7H$*&cE}LLz^,~c1N˽,2ʇclM?cUjZ%nlNS1kz,q>5+ ]Njm+CkrT]n^u$7jT3_rh<5+ΐ?uzj5|{?6, h]毼L( l fg֬? h>?ZBOO?lQ=:o0-Uz ED H)pLO|.;H.~eNsMxNFYjZhz#סvϵZ"?ۿ I$@"o}4'JD#k9/Ԭ19e(_ кhxt*_>41f(#uuοg@^Q(o=<(e]e΋gk%\6Ŝ.,SʤOYڰ*;~kE|^9*G P@ȑ$@"c3$@"0@mUTj˲F{5HnsWEU?=-qMby'W^o ;.3D#{`9^q+cƂKhyt넛U^SwnɠMnő )0]ghPGP[>s* ʑ@"${~!HD"@3kdmݿzcᢉ?&(kU. Dȡ8^r_ G+G(ד}.T~4䮛{ڠc9x1/kYfP.r_~Q&(>npTcDȺpCԤ;noΜ8&.5};U^l=$$@"pw1Zr.5H!@S{:G/zcWczŬ3h<9z>ZY'zb6.mFki `k=1_QZWO9YSy <{٨.٨[{M>q (P?/ .H*@+2P^:|tϧ埾-?3g_S}Y9E_]'tqt'D Hc:%&@"YzW=9~QߓNs'S YC4QGLYmVcH5Ul2󔞣& /X"fB8I|Ur:JL< ZL%x=őX̻ۿᇟrho=+|1ϦF'p~J-i"$1A`1 $E+X^~ˮ=|Jv5>qʾ/_o0cϔ^xYۊxYhru  +&;ǹ2zlptt,l q^Rv9s\bjc="8|yqxR)/j  w*jmgoox/mh`MI%MD 818ɹD HA@]M:yVR$^TԠ&|$">/ZGsz=Gc50{eIt:?tf@a *Wq1= 'lk\{=p|l5Oy>j)Ju]ֻ q^qŷ:^mh@"$8'=$˚©um YiI)0K |b' 9A_|997oպ$kCxdxrMTsY1>xk/+-^luJ@"$8F';$NOʦDz?GuʧI5@ɍ E/6zd^2Ƭe|O_oݟƟMK~w~֌(1f_w?.@^ Fjo4~mۿ楯(! P@ȑ$@"l%HD#@:-Z,M&Lej_kv\sKFX y_ ~_Tl{]s-Kslm34y:3VsqUx.,st'yY@IDATc:U=t~+%r`@M):}:T%@"%u֔kID XofbHa>FrnvQ:1ԫk^XKVn3yQUYN4zz+v\|rMnl3k9KzQ7x.䯯-";>J&@ P㪾l<[ug~կ+*tZ̳i|:cO$@"pD/!WP[Ǽ]XQ65O1ȦCyΜ} co|rN(!4 wy ȑ$@"p_f.r"W]L qT&❺OqSͮ3!X/z'Ήo1'4΁mq>w[Ak>9 H=\.|Ѧ8|uMv yf\ccW>һ,^Z"A'!Pą *k|a[Ct1u4'Cgr5oNr7G6 >$2/z8 !3d@ɠ4>ԼeAI-Ccw$)T@k||x`[} $$Tˢn;?O7MEuC966Jl)U,/G"$C`w[f.r#W/+Fy,7r''8dQ?;W:xbC쾮 }b]Q2~hG7RhG\cwJ.lzE~l4jKy uP6=mt|cw:oKZ5 MqI"tH_s2S,G)Q(9}M<~ϥ9=Dd5ugUL̠wsk @'&@yrǞ{ϟwwX&0-S򉣧>)'@"Rr,;H'4H^:W۽xuyaQ5bYmDsCm}%ہ[7\8o(搾gc]9cqءlN rGǺyT[!xdy*v̨sL>Q6EoxXU;NǙo"$U9X*;aN2d>)'@"B"G"$>!U5mdU>kpV(Lr>TJuuxŘ˾ۮ<)WU<9+>%aeRKS ɘSx6*?&?^6|ӀM:`=SϾ{Sěqe9hE󰢮eImvoD HC$D H7~%NӂyjL4Cfs1ǹ&G|jҳvlT*Yv<:(>=_bE;kYԷZ7~Stcs)G\yܝUcClU_^ @kϟҐ MP3׼~o-!l"bN >tnO>HD!e@"p8!ޕ6>4y"5noopf -T^r֮sOO/pp*Eĝj@xFX&9!Y'Rs $sl׵~5풟*<_@Qەc[;XS-xW&@̑$@"X Q:$@"0MŸG݇ljG1ǑbFPR΋"T\F,ES7t||<w'\28|Ϣ օ} .#j[4m z|[ϙN;G{/z&E5rxg'@"r,3H&+ez#E_ZvT)kGLt S Xw;b(4gPpZ#9If>h&5k._8x^#6Z*s_'6k.fP| liM.?>޷rMR9UZdxQlqt'D H@nt5;zv4rGNAqO' _; wSMu]51MDrE2vρ)c_:σˮF?M>iA>%눍T>"O|3C)§G7vr6Q lN{sGWYv3X@:%cLW_%A& ,Wlճ jnfU2+q>kC<'x! pOT:Qe/Cz 6m{]_9L~t9hO s<8WeBu}bk1G5`׈+^|קGR\W#HD`}hI)HD`)~qαipx9*r yeL>`HS>ThRs񗌎E}3`'u Su5t82g\1hwyʇȇ&7g.,x^bSWƼ/%PM/.r3fS=|a1GO}RND 8<)YR"~:M +[t_Q]G)|d[7/vG&iCw2+f 95WY ϛKzk1=90f[g.yʩctbGQ1EUX"&N喍 .5_]2 ELp'a}ǷyM;5;:QU|ID 8V%@"p+jH˽o5rgpNa@>w&erc^k¨g9i U ԁ>VwԎ7S{^3%zkNo95}?hn3yXC;w|ϝg?sċJˡ;V ] =, D H@ns%@ 6QKhwǻ>/>oOů[7Žݛ6s֝7*8ϼlT5S6m`֫eϪ?~Yu=&]=Eϩ" ;5Q lE_6Epӽy?}7bTbnϐ_D HC@ne&B`8H>~h^QgLͱĹ6t#>\%6;O [1ǔl)Fݦ|hvG͠o |K6&@q_ҧMk|MD 8P:YL"UV:ji"VgV>q~^< g7ySuѼOg7s@=VͣSO#)~+M,s¯/sZ5sx5ēo<8ϳ,vN͗n yI]UvwB-z>|+>;^tהc6l#HD!d9@"p@+bB.[Ǯ+Jֲiдn%3S ǶbU`´maPxq9T/cS>t7uDdf ke~.Qz ,J5u/zr&ʧ$9D HpN+Gί Q87C<~8ymē;qPs_車W%u9{;@]ۣ^2:xd"=boG5IBjA'z(%s6!7ܑM 6ȧW?W~ɷ%&|"$8&'`,KU)6QՈ~iC1s>lb2SlPw=S{b >grG4E5z4_9#4CWËD?ONZW>mH1ǪvlSO='ǯGѩ56rJ'ӻZ@5jzțvjM{6ʻMu9lS5D5)9/{j$Xsw1s姿p|WyȯxF ?nu3g~>sC썯}C{}9t'?ˀW3:>u]@"$ L紉@"pXׯiz>ؽ5P؝׳-MDqt'D H=F 7L$4~M Y˴`] H~ww;GX3>ʃxJfcY:vS߲؈c˧򫩞'zgQAy0$bNRZ'_f-R)~w{o~wQT zGQo[Oʉ@"${:HD`56Tҡ֋R8I~< W#3M{i7?b=SŊD]ԀzHFfnri>jzmhLm{vB_urAU!E5˜ǂ G63ks|Q ׌/BD(3~/\u~o?7")'@"p C~D 8lZ$C{{9W49]~FaF\ TשsyΣe}sy 1\otek1.+7 s'd\Xxt ྲq+fyM ZG kE6 Uӯ{'&BA@e:'$@"ar`Àf>\s:s<ҡbs:fx')6υw_׋_%%z~Vy{:lءآcE_u{9~}k`j)3|-z&j1UBjދ^16ʋ{+175T'>Nat2)HЕ xoxuU?1ګmk#ġL.R>Q~y|@į:<^1¥k{ 0p|}4YtǾ .|=?w">z&o-E52GO}RND ! 3j.Hʯ{~ <'c'?vρp^:mđ_Qj񸘳's_/zdFe4Gz#${~=q䂒bNo,EynxcU=ZN3rNПڛClPٕ[hױX 6^'>{(<؟2 \bL{6OO~zIEq6^COrA E0_ݒw*p>MM>/_= pK/~}WQ&@ˑ$!G 7 IVҎ2ӌYS\|F]G'ӵH/.m\W0;)Bc֯5?c-"j!ٲ|HM6 Qk{wISPLpjѼBW,dr!Cgs vPj‡O}?z;z1?W#( HD0"X{֜]8 ek]fmcOes~o>[],Vh|ƝZxB>Ht[`#H{k&xIDzdTK}uw+~J.armrVP ~>Ob>͎XljϿ?-#7 9D 8a^0Yhe9_/[7Ϝ)Ka2|ܞ|"$v&D H64jL#k>|hZ=~>ijؓDa.1/Qt_b\ӹzשYs%z+| B=^s}p&Fٛ:sdt{C>u" n'jN1 ţ0Pg{#o{?o-ˡM _lemkh8zr"${"$F^x%dl3.cQt-_5Fe(;יkett/PMc%8޺^sLass8/#W1&s5z^5;1n~}~?y/-& u6BJGO}RND XXtMD`/ڝ9$k`o\X_|1fi(fE e V͒(؝UBdNͫ`N*//}=89}FH\Wmɿ0G}O͛q#fr EBvDgW_}uq{ @Y|ID 9s22H=Cf/4]|y_W:{9椓RJ (<ϑq"l^+}4֊o A`iv)ַ<^ӟ7}}<^>nS>](N=>U[_-{ )K|D H";.D 44˅7ء}VDzk ,]t>9~c޽^|n)fPN=bs{u)'*7iN@zC[gEu9E A|s#˯6Y/X.|['ۗM"C_C_Ͻÿ_"&@o@!S׉@"$;D 7v\%@"i'|lyDy,kr׉]B*21ΏGbv*οΩ=d:C36{Zk%`j147MS>@ՠiWbju.F;4>ìƕ^ly/Wzkİ WAŷ$:_L3[%3AN$@"""P$^#x=46[ W,Fy.ts;ʬsny<;_i~ll x-\&%eqF[1qW!@e!W?e^2 Q^ź]|}`asJ'Пxk7C_4.Y#Rtӹ=D H%U@"$hn u+]O>_?O˥1XT_-8QM MryFqy}y:!KY>UԦy;VU74I:rx~ p_ݿ ⨿Aʛ Dy sآ]<p/FE~|~/VıMb @T|Oʉ@"$@n&@"p\wBi |5ܴ(\nڐE6}/jZ^2BirdMs#nce5_ұ':}2Ly%ʭ#̼}'U%7↞.|zJ2RM^x-[;&,t.^g~}Ko{ӻM8S\EUS)]<2GO}RND 9D H~Qr]w^B2:NaW60C.;!ڮMG# rkoy밹:Y):8/y~5~H[&Q"e׀/Q吏&_])^6ǞsU/ݎY\n0{/.Y/|_ sUz Q4ĉ*nl#m~5oa׬-όYBӦm۬+1EE-2(X(M.Z2PNs̯xmjk9G[|(ka0 ?gzM)ٶ4Zl~n9l$3ǂ݀u}ۿi$ ߼ _~ޢfQ{ѹ[(9|^'gY_~BdY@V+@C1՜|@H 3@?t#ܭwïo]nr6rnԚaI~:"v՜Z;ϬѸ~@_bA1?,Ɗ>%l= "vEfsa9N_p}3g?QĹ dGT8gج@V +p*BX 8&oJ$IDV d4d4"{֋ȉom[;!0_~`ܑ)H Ԣ"E#fsN~@0''}w: [2 kiaTGN@;Os n)rѢE'gYY8= d4;8G~j2جw=v XWvl𵩽іư:߽1"A2Ns3eন)"V/͂Gr]\yaՈшLYt9&7K{ř6$@nKV +8 #e@V ++`thsyQ',Ed5s cŹqo35Ƨ#[EWOʤSyյ0"vEI8slLzn~@Aɳ8Kzx\&—\u׃O{r&@)B@V +p èr d'gPMԞpLg@G׺x[ S6['{jG_u|ZZWuǔ͘o͔Xřk`V! LhUz"9Ftj|yOxQY8@)B@V +p p dYZoi pKmZm6ș7 Z3pO<1Mc޽1"AWV5c9tڭ[YUxms!Ȕo.`. K.[zF|Ulwo'į,&̏2]K#Υ&@V +pb*'TBYU "6-dn| h].r+ ~"Y@Vh*~Ɏ:7k׳)N[44jK"$C]&^fI_w[3^%Gu}Z65ܫZ>8jvIucüzʧ ]㚵ˏn68@d p|篽?wST+6b -yDO% d'p"Ns.2+ Mb3si67ؾ_CDduv3.F7tۘ~7zXٍz_rw0@D\W5OM; ǠՍV:)\ϼE}wGQ trdYW8q< dzcIxs{7?֬W"7nҸ6㓸c~ m4hCu4҅3?dѳoa/\`5pq;79t?b޲6e.|/Xf py?E}m9b](9Y_u/6=/}xͿxɽE9Y~T 7cE)7G/lQ/,Ȼm@66G9cdί3Ǐۉg< c<8bE9sPɝ8.p9(Fr llӘ>l#R`A|Npx盬?cT6ڢ=L6@5N+p`vy-GorУ"UPrw?F(F؜TѮ dcSquN5' @m^TοWڏ" `|V +"*w"LEoZ+sA}E;!sһс=(~s9v.Mq`K<| y{:cX xQ m^]1n-2z|@:}vl` 9Љ£#ׁ/xN=溝_Uu`@&ѬE`(z8u9 dWL3GV*oHY SxGG1$L6|j`clu~C]/tP=*>ꑉ*=»dq-$=zͥ~Eß˱u z:!wc\6q9q4Z0Fhn"Xl\>&[Ĭ[YΌ)zs:??Ƣ~A9 @˙ L祑zdqyY@V~h YziAîdt ד#qnf*})b~zrqU:A(rhOޓQj`8"l܎9 җؤav~9)t[:y<Ma8wS|լc#6~"`b,֥nl 1 OQz [n>z]X{q9ZD 3yw,(fם W/0*躮v5k#@3O~?v񩏾Ms&@q #+ \Y@V +pL*;xwi\N+%zuEˢj~ƚ12Xw.ۑ(#?&_Cټ!z_G5m Uݼvn/brhא1d{qx5dE]vN*(kO7~M;r29.eHGO19 dDzp,O[& ia PIW;4?mUu󫭅yފh19%~O,6SSsf/742^zОL1<֠ 0$.ltf'Ų֑8=Y95 -M6t烶/΋8a? _ Xc=wO|{MӅ' qj0 U2' dUxdY@V`ܹfh%{Oʕfԛ$|>]XI?-WiU6?;?揼\NpSpG;sXmRNQxw+  9UhV +hН^xfp'KL@IDATs' uجU<+n_ཬ:˿p:b,6j= nb03X%r;A҃:ߢՀv_H{>'8kO 5E+U^t< Pe.~C٢ˡ?Gze&,br d{fY@VJ@l*KYZ?)L+f$IX9㌩+xp!ϩ O:*h*j{qx1`WQXaE~Lhl> f H|H2HE]3M֏TZ/z>һ"& 7JrdYXyV +8fস ?zlge44;@ Zվ7ŹFg,]Eřڻ+YwlsÒ CSZF8e~}j{eT1N{N 3Dq=Y]|>/l?=Kת9{:o'>+yGmM/lY@V`VK!+ lY CYC f/Ecr'zFDF7i5VU(-ʰBARcn'gvsѩ+mU,VlcW8p1N*}je=Ƒ{;{ED3AgyQN-||w {{99YWG.K>+ dv/Vfg,,U؞IIf_Cs齙lT~ѓ;fslS<dn l'(|5R_+yM{qӬ#k[yȦg96"qP#tmgfc7#nb@ P"~{w' {G9DQs9(C6qd@V +uY@V +p+ $\;׽zNǑ7^+ZJ2: 󊅟cY [+16 90WL.?:$G7NO.ǨŇcyA*n]=1*_"J>%NYT3'q~]5g]r; GL_m d[WغS eY+ hpړ^6هmz ri윏 MNɱ忙^֯x|1eMlL67xYAHʧ[^x_VryCyzQQo P1|gYkrTV +*A[cd.G23蓶 ӀA^Y{|Y[/J:v¬#J3 "X_R_טbs5#ʵѵGy?w*l[c8-T'goN[|WL?~u7^fte;x|: p̓fY@nlIJYVArFE:\XqG*Ekgg̽4h:kqU1\?a{|4bL٠S6,9y9`S0ڮp=W{#Fi#'Ɗlz %~ Y&Tq@>j_mZm4<7~U/qK rh 62JY@V`*u>2@V + L݁$9+܁@&_^\JcufyuaP]\Xq/˚*N'6c5sxmC "bje1VʊQ^E3Z{^Vז 6#+ lkr`[L d&*ѓ1$%(\2 5 fl.6CtM՝le##edc~֕㗕VQ}L˾@V@n\8 \fwlmcz5 y#q9iZe7>͐r8]&}?x5%u_8R/G*g/ظ,@V +p #?@V + dDTrtȄA>bjs) Qmjfm ,zc1sTp)#hl"Kauf>֍s'' z\˯) O>ѣT(,t)^Fyfwgos'c/מoMf% Qy5g8YҬ@V +p #- d_h`ҹz2t'ֆS 5z4_9v8ZX_Q9q18I"R{Ď%1a{1:ۓEClQ-}9²cyH+SEAb\9p,N>X(m/}-n<2;<.6$ˆ92 z>\/>ƈ{JI\MPNŷ P*f_)Qtȑҋ*+xV|_(OTиGWX#)7EEoKCPlaŐaiA 3Z @y{~ %I{@g_ȏw 创χ lŔh@V +pdڑ% 7nxɍ(ثZ/2py?N{ !lo.l=~s`4G>w\/d?#,(<a.*;G:ƨ۬oI r#z·#c>G? "Ezl E=2Q21裌6Ԝ}$ f6:V~^:yй4V;(:`q!g.x44G.'z#wl\%C'J ɝ\l-ẓ/`DLqgJcЋ8B'e/q?AMĢ!8w~_#o6 vCմsކk4{`k6i{%7Kxxϣ6D+ X^[ȩۯ7a+>,1 *c{\#E92t>w^:|K!y?Pd60kH:ſ#w,tC76xcp=uE\45"FsVs3f/|J=G£ۢs9Ћ'gãxȠQz#ߚyPG8WU*U9\VustPt2<4-QtSgݥY@o5/m2azz;X 1fJk(rq{X˸ɢ0S8t^k/d33朠g@/pW?|Гq@(^3k'[D,+ @r.+!syZn33f7)yȞc$ *(2އuxP5_Qcx|Cp`3G\s܏xN2p\rrr29|c>ne)v9> >z\'0c0)4y poR#`+od⛝VR *-\/9s:j3łL2® x!?⛏6ƝbJLh&}ys'_/6ֵjЫzֶ{Iז`׳%t^gj Vswщ P7 ~5$|+~=o_RП Zs=(Cqd@V + ,+@oOD7>rd(rE0ః E$zא,b pFXLmI.'9#/sFT?$+> l2+b4w<#FTK6/w[2p ?`D%9jO7E㺈v֩!T8h~$ZCsXG kE9:l0 W\-v}T /;LH]/^ƿE e/ū!A pc"!#d=ɠsnҵ?#?&\|r䋗O;GTy%?wE|k9$@n" dw<ޫEɊǒ7aA dc(v/t z»ݘL1ܔ< _(2᥏v@}8?>=a۰^\ M:z6.-;؀p>L˝7\/?&(vGA㕗z!a>pu6lVH\GY M`m-69(ƺq[(èYoB+/èvL~1>BMo[ԏ*cZL{^{8w |Dfk5g,s3 ,Ҿ.ʑ  3/!S?./ޘ^y،av#n=wuob[7#M}ֳb0:WawKCzb~>Ͽ9/+_tSVgc1ֶu0XXP=dOiPc:?|P{|1>f"S64OW+ˑ0IV +p<+Ͼ}f8iri' xlF|~^LXn]s2Kk8\;fm}Wa,׽.AZ(Mi¯",~*6:eSeg!d]Yx=ܲ%a6 ]&6]SOٞع_|,PZDs(ӝK d/eL'Y@V`*лefDd=㤏a[9r8S nm\Y';k:ӷh׿\Srfq81}XEre5}']7ؙ3;ݿ5g]T+`M#Υ&@V + ʗY@Vʩ@ԜCmZ7Omu7( p>+Axmr圸ͺkmg{#c"9TvG&r)rNOVHrZ2[ Iƹ{*0daHd xS76<:IU:z>/򺟽_TlT 1;4 6k= d@nti d6sh~^ kZCa㈞fUz{3M5l:}/Ly{ͱ7>{k_M(9ˮτǬ#YB^wG}3ѓ =>r+?|׽?z`m#ǧ7Du1"Q|:"PckыrN]PL07P7 /y=? pWuxٷr!GV +Xwv dY+@S3p 6?*ްsrkp Xe (P g"&j퍛jbG5r]8n ަ OǩO2j6Sw/}~ocD 5ܗC9|^>@V +P+,GV + oU8GJ;e&9<ڼ|fW";>wQ4Ci{XymrĔ !1Ή~ltޣD(#Ψ<(AjizNqmU6' eaų~3?SГqPEd "&Y @n  diHhZEmb[?q-hSl1.n7{)To*#6M(ͅ}L_gޗ%1pK&n1gy9Tph(tSlza3/g>sz}vK9ΗC&=Zs=(C6qd@VW 7NϥgY*&:Σ?lX 4}dhvJYy-W[Ԍ9쭢hKA푯KY*|̋˙ b9Ďu2[G9\ |oxim}0I}@'|`|?~ rI(Eȑ ^rjYTfc*!nѕyͷu&jymGmQ41|Qӈw䱋A׉eMA~Wo]1ӹqȏ%kls:1WF^W\6?Ҩa@&E/|;r[9r!GV +px ëuF d[Sp/2/ 9 QPvxPlmM䵊M5|Dj5s\<^|rs΃ecػcuvZ,g%qc6u{mp0ac~@ [ŻOo~?V#? dp(e Y@VU_F9+s92loU~{ضGM ͑6ϊQ,c詩!|+'V5Hy:2,n߯]&GEĹy1ɦgԍ5L_ɖ>n^}߼*;&fvZ`եd6[0`f'pBO|.;+ [u~G16b:yl7fa`[':pA*±cL蛙14Ė?r1;{JVe{(s3\ +}5lj llr ˵&9:5Itc7=w駮9{br;}1R ӓ>@VU 7NΥf'ܼ~L;FtלJ66tc6{5%:3/lqP5-C0GH{zEˡГEigX -M|r~c]׫bxqV:9f[q8Q];nuܾ5|m_|_Ə%/r@ɑ [r`JY[/:ꍮ-D7رa SPQ4U9{q˓ d s ]^.++ dQ b'#˹'S.csb~U\SjĆ|~gƱsjs]b' سOըW޹ʵ5ݺoy/ B13NιVc~{\}{+f P Bsxל1˚iҬ@VJ?5ڲY@V` t82CO X?IYϕiuQV9h|05ݾ)@L-)1o4mSwc6u{K-abH9+OPY'נpKW1*oWy{ܓO'=K{߫Pܼz@06n+)qd@V ?Y@V`/Ckx%Cu' `zv-#]23hj^uvhzɢUSG!kf=\_OCx7Xur#]Gq!Ơᦛ'S<]&']|׿ޏM-qHL6z2tIY+,B dYT`CFH#$y<xrsLY6nnj:uf+DsRi~=*^:bjƴvr\G8XzE7۴ojCuZڋx^:m } 1Ox+Ugyzk7%qs4"-9F6m^{2'  s/# dYîiLso,bzsɶa;bC(߆\7AM:;j=]ǰu<9Zrױq}dQS_ϺFGkƹ< w꧆cͷH&gO~#?rX 0wm)v(c!J{@NY[~PdY@V + )[.1^f gAs PqmռX%,ՈFӓcF6|#`fw1z0\نtc~wc#_˯x^gc<f2p+ٸׁz7GʕZ$m̅ ׼G>co#]`zY5-Ts*Y%s}Y1?ivV + Gzws,9躾kPN/7&m{3r9U)}sהE_Xn=r==\{>-6kS<ײ(<-:c>n8 sȉpi߯ O7Іϝݗ>S?|Ous9 "pS 8'sYcX~@3@V +ئ pKNu"\ݮ%^+[,Y3{6q:⸼q/}գ_~qmxLB&^#d/Y?wguV + jhZ{*Z#Ϳ:y.)OkO3lÜ|#E[$J',;ٍn8}W>w>r/L %ѢE%L  ?@V +p(kzwY?k3MLiSKɛS|ؿSIP;qy"A6܌Eh Mrvovs~Xc)n=VhjP奷5P> P?S/y?%[ˡ&@)B@V_~) dYyMG:P\XKvF\q\C΋ .[:,#֗h/2\c5 7sNW 3Y٬g{dŹ鯱ե}3-dϞglu޶qS.ͿS/K.ʑ9Y~Yצ4+ dV|17âˆטt=m876{XY)i:bb'=tl`O<|Js<C8^q9'Q-5X 6k:nˢWcXïIng}i/N+dM;;qRYr9IIV + dF*MKtaա|PJNDo{{3Ŋ^{05h.5W4bƨ|o9n7e?~YxMgўkúӵ%&6G:&pO~Ǜ?T,r\-ӢE\'s}YW1[b dǭ~wn~fME n[h/:8iJ9F`<ڟ|s|C=}yUe#Lժܦx|Lf^蔗U cfL/nе w |Iw駮9{}^9nԔ ,CM3Y$+خ elY@V`+л$at45P;Mp ̓56nn!qVY'˚q"pϺRX_9]guR&x5Jm(ʞ^(o[67Z]VB= pP#+n|!ÝjSoP y"wqp7§jـ &ƘOٰP~ƯNjk+5&~e/h!k^k6M4Qd6 6PhNnڴkUy"o?+B=Uhśvبţj[mf^]un\m:?|,xL/\ՍpYYs܀WR7FLy5Z&W2O?o^sӏ~YďB9榅Yy15iړ>@V*C a~ D^xB2QP#+w~!^2pwɿG/g /xYu˱l"[5s$_3|G藪޹M1~=6Bl6qYW»=l53dCܜƟ>˱gXrE9v ɴ[\fs|$smˡ-sxF,:T(g)a(W rclnLm\<{z÷?[).&+(d1$A\V +p(ȏJ3HV +8oza]n(d'YkC.[KO6{3rbՙ) 44~ G=JD<֡k^댱Qoq*6jGSa@粮芛s\ׯsV3G{p;עMѨ ˺缯Z9q6Ź|qWdFS/ ƀx}AlɃF./Ent|10< ܩS~[XLZ}i)A Qtd2`ڬdO>+8 ,:@zk+@5GnlIJ]As.w~WJ;E3ƣ5(up|Oz5‚q1~,M`W.c~z b̼ mЉcظ)J/)x)ˢ} Ohs;es鼙P`Gz7W31[Ӿ2IO% mP6n86c{W;|}VyQtع⿿~oz~z7p@k4m+/!?pwR:nw|\ srfeG&i:ַiOeS8eДqjt ;*&)[ש7Ŭ8u_eO}U#_k&U>yIGldKrQj`/jY_-VtĿ|i464dh5WsĆ6уÒ˦ W6 7s\b͈`'Oojq' ?tƎEq)dfTq+@5Gn%Wؗ !N10m8%Gnz~a]_pʚwf}ĕ=똯Ur|*CLu3ȝ<h 2AC  @΂ɽ' P3&@CXh^h⑁s8`5ןedMt\҆R~A<pSЗ ƺ>ϟGyQl? O5~Gˑ9WBO+a%Vkm.+Xn:5d> xGkҺ}kT4{+cx3}nRUX)ڱ;tn Fc;8/ܧF>rPlǞ px[ iEz|99;t9)y i+z΅7e[B!4զ`$cQ/ ْe#g* cżĨ>[,(K(xiķo~ P#+p+㾎k #7v_ FRHdrqYOw 9U$.X7m1?OG7U֞5c1ĻkNכt>ܿ:||@/w@l c'P*X7cuvy/O>kVh!z:@KB~Cy"ֳmCy꧙WW$g3DsM~ΟEMgˑ(Eȑ8Xq\Wy~ D^x^YBW>1?uhFo~VZljEpl}\My1&']gQO\>WF?DnMhOnQ9ͩ"+L>&-}/k|RkK8Gxŏ?u6) %GG'C8zyV + w.Wk8'< Ft?Pɝ]O' zz"m˚'myԬ ԃd Kv]K3 uH\ fqp6=YϏd:WPhtQZa9l A2p4׃d9z9MUGA81P|26nhʛd7&osΛ hAp[ ܙN鶭4:#e[oN7Mqo?7kO?`fP GO19 dvY-]:Ic]=k a5q;n|_9YVaKw`$l ÞN66utsoO45:~MڏU:$es{AYS =/U"k=?m ]w,<:eg&D|c:d XX(z|UE)yZNQF0eJxk^ӅK?q߼ k#8S0_G`S_K;sO>mo?3zab[sJ0#$_Y}@۷R[G~ D^xx/^HJr^Rh@V +P {DE]w)kQXCFSЅ-:^~<3+;gN]9sÍ;Ϝk.˗vNxs9nK/=VJl m9s&q*9y2f+57v.> ڹ;O~ X?=ϞmoZQSQ`kyGV +w>LWǬ~ D^x1;nV`+;;|N`S4xHһG8QvUcܹ1P.Ҙ_Uιڹ%/9}u;nšr㮝3rw_,}`Py\~KO<ܗc_ݹC;Ηg&S츾LNڄ)'_!*#_͐]\z)l! v=_qC[;Q}^,Eie^QMF.^(~n,AβpS·6_$@_.egn?|[TO 6;d;{e}W@V`*}p.i cz3qw~ͣWkۚf|X1KO?[wNpCy<֝S׿`^P윿;o}Me#ȟ)J_oKYMᅡ!TTԴ|T(_z晝KO>sR6)xj?\6ڗv~ǿuXz-K@ׯ`[uy\1HAD<*йƢ\ѺUQ<\~!Vy9lFSzY s}qܮ~ f/Mo^go<{wJIpN&e됝&{<}޿**}9@|f=UF^s̓fV ރmh+~ D^xps Y Dyo.lu=)?Z|=C_gRi ;׼澝sU;g~a;!w(7)]|739_]v%h.KcWУn<;ʓO?vWs{b>BS9s\o]0D9Q<Ss}L!IM2jz|J%0=m:&^M=9_7\'f: p͵_{W;ϋrmW6 g7:׹&=IO$[Y$kd˖16F0 &Ni!R MUSՐ86X$Y~ڿ}g=[}Zy[{8V#t4w!D)z>$G`L~!ӎ::y e;gK/zx7煉kы/ \O\}][>n)UeM"$xnЗI72E-B$%bxʪ>2$_~핰zp8ͧgV8%+eD^X#l-LT笕7Z>kcV>m/[ٴ~n!ٮS8"7"Cv Z5sEB>y?e0#w0swN=>+&UJBRf)xئ%'Km8eg#`G=|`g/>zG`~1q)t7ǂ~s!WkSS嗆w!_/k~$)x?=HAro(NA^Fb/xDX|a›g  (&=QR+τ]#*}+KcuLQxlWmaYQilB[$(Evg 7m;/ Bb!w fv`bgoٗDLˆ v a|OtY#AvHyvȁti_B}ZOc jccx@S!#o {{ h:t$l풟dã~,ÓuAtٷXY9}*g‘O}E '//-H|sI^Yb]Э/-k[hJhOI:Ԏ9J\c:)#\g(,쩃q Rx C ?a݊xB1G'/0>>=>$]`7D]B]IyذІuМw,{WwlQdž?g~~\GpF_DQfdV6#Mmnm1 IOi& w7]>Ca[ ]/rql7j'v7 =ް 0+/I( PJ9백%CYG A@v`~q<6KpzC~ 2Nҭʭ-yRR>Uǡɿ$/={~,î cW\#I&/M+0zn%;"u'bj&L%L8:̷ex) K̏:;E No]^|FMJjkyzLp#M,v*g@SNMa:6_8_Lf8{ ('zNjY?)|e0GA"O_se}a?~tq`DnbAw1d 0oRN4I^=nlFSN6^bOX-W^>8R6(I:1!oi[yYQvSRA r|*Vt"]A6ʋ-~N,Yy,wrmc{O=·$׸"byz #۷Ly;m#/)6QI^[O嬸6Vܖ=h>&}{MSZH⊤_1t/qht09;&'ÉoK~'HIM[!YRhQ/6+MZvuBc=|* yCaގB˘̣136K9(LJ1ȣJAI=,/fQXم}Yr|ÞG~&ɂll8[`hS\-QX#0T%33)_p%^ԧW^e=:"^sG Qy?fbx^u! IGBG&V_NXxey5lֺo'(yMb)=eU.A97Rg%Ѻ|r$'rTg? 9CF{#/X&/~i E0!6g<.Fz&gd!<0wD\w^^f?.cY]ocGD9B P-?[;"l#ղoUОYhxƱ;p\8x[j(Ѥ}ʍFB}%kDZ$! DĪ<  Ϟ ɓŨ,]tIh>FX>s:l CnuHWF;ѐmBnyYJ=iGۢaM)-eҾ`8V詣О!2F8"G!D(D_qMR>c]x8 ~!`7%SO\C7O<3"E#f=b;%Qs8YmI7hp*KdO.\ {8_6$dz xq6a8#091N>lhQ><~^ rG#dVh͘ZB[ao7z%ް)eἳ1+#϶c[͎X=mPDA)QGLշ?;m]$P,99<0u5pݡ/<酥E!z8 A!٭8|`ʁ/ɔG кpzA lTuv<( iʫr;~DRGEa;꫾ wyV_ՓѨ#$=ZeKIlEolTu,ZT NL[*^ !bE0O4޹ gW0QЅOL><>:s`Xv0^v͝k+_:67; j hZH[qmi SN/ו#- > 'ۙ"e|:.}L^#ra.ocDCtvm o{;Bտ /r9*l,`!)*ѦiPYJRؗ_ۍ4Vȧi?Z RmBX:q ǏR'/ehbqsYY Xe sw\x굓'O "Wwlѫ)cV#_)zp`^Fnc/.1́k+=a5fxmL~ޙF@M$0²*"sC)\xNbb6ꭎ؍ވ^:[Y'lD]Pǘ 3q& H)#]#@ YjH_;RF;qKH=4bQb, srw]t'3z#iΟ}fgNm~mߞn?ga#BltP >Yru+Q@,no1X0.p,V:o>q n7n"VyZxH#vG؞37ԙGbdV1HSq+? ~G[&3}X}6=#[E\~柎V#PC'&& 7/r"/\csI'5 ;^}%E\'vbUԯQ u]c~E0*<xـ .:t1CXIi0wU;/@ypG>/Rur!Ϻܙ2~aPri"jncM?P ߶53goaHڑ wc|TR>gRز7,^: m=.#n⍅-zNqa3'_˿Cw|앍w MZQ+/B'GpG`! >) ܬ JZS!1 )[EQ8N>}j:ȫto:ہ {Z. iLcw.Np< yCO\mc %G^'Ows]'E܄U>G < |8;8^GpDysZ[ԑ8VF~UV)>/7/#%}2`hĸ!r־)QȱEW_fwMYYQ={9q˄3> &Zf2ۥMpƢTmaO#9;=Ec"r!@!luǘ|,dB%}}W?q]I/FFⸯ#8#wdn_&LAOv6m_і"=v=>?/\ *56Bivt=Fnk'"ikkxghN=A-X=}qD[`/Q@~X |,@ . X 857o{ݏKulܓcX6I*jm2G < |8C/ .:#,4EAIrq?='=Bxۿ{A6"bFY)yͭ܅6>۸9]Lss86P$_K^hCFyn1:m )pFMWQ@킇 6|_:ڵ_C,]Xg<|@@/;# $2?ʙҘzPnԥԯ_+wm?'Q_u-F\F/(/"E,.Ib  <6M:LʓB^URcj]~ݴ9&iN c }mEF(ukoe;CN/#8!d>r7?al3d/vDȶ9Q߉)J>YJ]EVeon GFkR^Gh8ݘ)j!>[/ ŦVEl# bOuu@\X]^w}{E"-i8lK|`[6#8@' < I!:+:}/ųz{'mrG@yۿ ۋ#m6V-b,1%lQ8H>X['i\Z Iq>7}$+,hF <6[Km<+g `m E]<C]8@"@}i uD".GIQ꟎!3!ۡ>Gp<vfGs:RR%6yIPp-Gn`;" IUet7$Р6ل=]QGA۪b}n7@&СX[]'ǀ$ 'oJ0< cC)Hȧ2(1B5 qh^ ޳Gwzvh 4:iض۶;#8 >?NvӘ]1 ž {>ms:*9$( Mqڳu\VHXROdiiKmsuU[^l)և Q:Bl{be\bYEP[0{u^} 8@_{7dx'lp7NMX00sCѽc\mkydLSoe9G}|{㋴He?N;}  2#QB%o}C<\]M}ܣ\(`WC h% ӵkQijPV ϐ"*eA 8CN /#8Fr{;B1llKc@}#aϓ?.I8ucp(]38wmG1F3yri|򶯴]+оEx> mvX`; qA:+GlGӖwFO)_JƆ>('hݎmsC{I2Q@Ut@rKfz>#rڂ" } 3y _,vҔG݋#9pG`+UrkIvjk'9lJ^'^. ߮/ĝ#rG`h 1P$m\/pT}[lJe~qt7dL~YOPh9Nu}@!%O _@ @]e?>|Q,%5Qgq-b+  } q+*1ȱ0rw}D 庁hnVUuʝ:ϥwpG,8cbOjۣ [ڗ4VRvφ/L|{1/qRX;pnL@EB+̮d[. $Md:}u\ ö1džzZ6D gbJ 1 jZ6%'x7E B=1C8&vݑWG>h춰eYIomw܁?08#8@:{CʘYRjo$uf:{0}-aš!ڸ\S|cBg΄c-ltd_*-.$bd0`ũD^k'`#i.V'FZe0jnǬb)cC & w Ft?+˳,ҭW^zd)Ƀ@,#0P@#8V mgl>RL@W%_ nH025-b4.DCtQ}L:YfU?yյCӧCX]>5ob ["\ s=qpd+@֭x{^䟕 Tr9g>l-1k~&A||MڰwP^E->%1 PBmnƃN}ițw#ݼP̴ ASYZ;<nxGp6;o7[Vgƾ׋p ԔPegtr 5\-kϽj$$ !xnЙ!u+Mi=a?;vuю1)_v!~;R'VG^v!ukC^R~~0HӅC{_{b9*fewWۭ*;ŁwpG,8g%ڑb-~4~0y_~JgW8OtY9VX8z4Nދ='=yZ'<(Uus2ڃihƈMcSu3Y'Fb ~v؎Ilp7cK~꬟}3ɷ@?PwRn18t7xp@Ip@RSN:#8DNlŅ]73Ҧ^0u%aϣ_-hkuP VVO,XKPs6iT4yU(̲n)xk%՟hžY }߂s8<=dR>HcXX@}F I 9|u1@*v-1^GgF{/\'n=4 ( lIV#00yGGpB5R 삷vm]}*o SFc2Pcr'ԏY3X{nQv)H?[6[].Ysi+14[Bθ]J@^OIwnMmxnT1ۅƆ<3h{W:f>&I {WO@.<\L_t'~'EO6> (:(d9:u^GpG E錎-A-f@'ɿkHY5p  %wV|jmz>@acXp@)T٧a2qΘdEhن01+iU1}?P(io3xWB @ x#?Ȼl`Z?/" kc#8Naggq]x?:=foW}advJ0kw{"#[`7jYnl.u6/؄vGp ;Yjc]mEnUgn-Lr. U!aCG`$qrh8Ah,.acugw01dL몲Oz=\%O]J-6} e[$Q99BϘ}cHqHpKD^d[!\iQib( Xvk/E_nS*&Zh?DGpG`G!`giv%ɳ1w=eÉdkLgO3_r8կJ@_ fH!#_e}<&vw+ũB!ՎqQa<0C(y-(#NZbLݴ.ŘrP/6nmAr@um3q?|/xliOZPWaf|NwŒM R.0HRDKƓK mL!K æ4TU ;Ũ[cea㧬\ \7>a폕[ӌ## 9E"H~/rh_  ~ѷGo-UOi܋#9:uGp@7vlϮ0y0y 61)/=Hvۯ{G`@_,ʱ#}6ykebV[7ezQ;^N\O3To۩m zU*yU6.ybv[}`p< [v4-9VA, gE/J'lw\ʋ]%UhOujD ޶#8#p0ʮ@{^O~0y@/.7^!GɿB~bgkr{rcښ=zL;Ui>QFk,&V1JKGsA島b&i='+b}X~&tA_x&H"%c=l9 #<]J qDvLX21Ih/'yŰzccrqSk_Ibs*#)c&.656q F,}ۂjWͽr݊b1gB$hg5&TZ*?FqopG`!iq3c0s;Z:;lcPdb"8|3jcq c%͊R51%VLS26M c@N6˘]}>`_ךs*L;`w0j$(E|/'(y1EѨS--͋EU6]زXyN_:k|\opG`X33O\0u=kQ;tK/ Փ'©O~x~1̿zX-[asQ:9Z&/gGYNg8)cTn #^m&ƢTD|} /65/X ~;OXD("ÂReO8pϘ4e8q%aa(XOG`[#,m2gN~//Sv5-opLD o4y)LCU8躟Ի3m{V5ԁG2ԩMa?Hq3VV&ZLi ~Zxb۶E0ZeH(/)%R،8~/s'.QM( Y'GpG`; \]o):$3ETgȾv =51.d/p鯅S~Gj#w0!U9>6v&†|jz} %Z9?pfjیԴOӪq8ыukC]>?# (t]ERqCA\Ue|٦exth}7,|-LS4}BQZbGC, _G>sO c;2 떇qZ/8l6{|GpBNτo+㡱^UAh&=B9?IS u-#aWKG3/_)wH 9WŶmyfbMX!c+d䋡$˄>H Qa@yZՃOcd1Hm8ҟNJ@y>x_>"cRʶE\,ܓRf@|iW}%(6ҮO'X"tr9u)pNGp@6U/FЙ)gnPUͨehJ% b,8a]gchk?/u&[1d%_$eD^d&CPzk%'Zھ[=yrXPg\aˢ85C$9,*λms?r1{'sxM\:4Ϧ(OG`M;# x_]Ņ0q a p }{&;(zvC!N6#AVLL/Ha/gOչ9Y2oc0LwޡNeBֳ>0+I$ҘX+Ij~W[9G\"|[}X3h+ 3ïs=_.h(͆b ݔr^s/D8#`V#C@52ʹb3ل̉1-NK]jEd5kr.l=crq aızHX9\>,ao/57$׫E)Щ0,iK sKY:~ud=§}IlX[˧PֈN `c}a΅Rb. 6f JQx7#luKqOxXKsGDw2[ 孭 [78# \O]r(&cNgd?̕yY/ :IFF2نxuʽz\;~%MC#1?VOӧP?sZ$?3OY$.f1Cn֧/wq.F\bK:viS:6,YQ?=M޺6[12RCMaLR ho&%&HAcIAؿo gis3r ?} #>'-UE:e ac3j5G@P#8Cq2 koImt!f M^sb*jSrYU:[51%mz1z]~n@_ڹ5&[=y\2f\DPrWʪ<ƒ<"88^y),O;(ԀdC5Y(wMb#m[gn(B^ۭ.l>0-oqQ2[ڠ5=Z^ Ej"wG}~/Dk+h+ptBvȷ<]j#uO Lc?P1x=lJ#іT-hOP_-m{X[ksrnyAB0X(Mz/# 78#0 4jxhP+ިN`a}fiB/hܺKa[OVPB`&p 6fR PGq SeW EK>Q*KVD9qSbT*eZ5TG܅S:v,ga6f'&h)r6 )>AmJ'm۪. QzS^XSAchl)~N\  9͎{b1`ԉ#~OT]RU32֣vU5G@P#8C@1 7F'ǃI2t-;/bܧ^Ec-YM.Yc).ky;϶5:HS.~ʻA -_Ja#uu1N#6H9l&΄FN_j n1}>1fGt2|3"aҤ}؈x70: >#1@ Ö,|DG w $Åu(yG:Z6`8#0dma,XYoO}O[?#Kh_:ux<7ӭt K#yF^DkTnm6!ܘYGv:ڬ"6-Ox{0_Ac:?U./S{X!FܠĖ>&dΘV0-Hb"$Ĉfn&z|%}K}Џ<~/? ;).+( 뱶N2"y=#8#T=pif(ŚdnH_?y",+og8Dhgg@yX#ԹqkDy*uBxT)4iܨcU[Wc$ڄ&gbb;[O_wnٰ:ObR |i#Ye𸎀#8P H;Bx|r2cn62=?g5?Cc|*_k2#uE7-# K&UА_!QOLsmNknC_: ۞J2إ:sa MV„ )&4#dgybC@*;N*8 11Gls}XM=r/_fw+`pG`(fx聋=W>Ngq_0h,mK/>Ưo}D>T28yUQ'Ta mue@D6Xx$5}>_Nk_;6t,Z~\r& ,KPY4^H'qQlП4J[??$ڨ ƃoZ\ Ӷ :}yA&&ą>%ۢlB=;ڄFDEas)Jp6 `#8C_=q<]xQcrhF4VWON^ G~76)vxqb:,t=^L6kj;܀`w(Z+t}J"Z e82i;Ol#^! :MŒI0¬M[l0BR㮨F|zԓjһX_k}%31h&Km]Z?Ʀ;+wGK5o"XەְgټSG/@_` #8ÌN8~eal~}F&>?"{"~_엿Ƣ\( dDY0u;m{1)+@mRXmIV>E6HXirx:r[M۶&'Gii~FI|XT5=ZєwvGjlsat|T {,]]z0 ?'"1y%\Y.(yLu/@6c4u)[o2=>,o&;ke+cxn! iEb\ ?Uw`2 ϶@!- .n!chCx=& Vz7@:7IQዎkliv:+s4|`Ӡ#8C\/ /G6:1~lKV8q^k_m35s?Β!ncX['Q_oc^E? $3-#lF=d醶y>XVbo,G@rd<&ۦON/ZY؁8ܬ]>,MYSzW?t`  ظ@s[>W ԋ#iAGp r^Zi-`ak喇 \#1*vA6*gg-8_uvضNaRyYTS%Ou%+u|KgpNB_Y=h;6V/<ڵm|:R +)tyl if y<`RL&궽^Ic:ޅ` (#UXAIۥ>G1n[8|$3/rwXWt[[ # 8# /y؁"Mg BǑ]/-gl郻)MZCGSӭ2eZ|(#\>[vV*Gzlvh1CB2oFj>}eΔGAdE tG*#<6b[|\<~TԨ0c?hUd[bMƍnI`&jᩳ2ڥ@%q_i GpG`!`]H=k.8P%-L/7/F8/'df]L1 X.xBI> eJ7k{m*f-vM [9n?웥M%-`m5ϾQ-u[]^3aFVx-,* ܄Q4ԦA);N70d\XC%@tA  }arxG;guGue{ rL7_}L^xwa}yG/@_` #8É@s62;;/Fpo2?KaF:_ 093cHn6;{1gLRTYu-m}uSKsMP?):mac,,(Cwz vE(xE]J?e܄ -%4cjf,ќԸ, lc {LJ}x`7wӺR%ީ#55Tn8#KadiCek/bXzk0m8n辒v8esQi{H* 8'=:h jRl5c aͭS?`BkkoMe8FyGŹF9C[WA2ԭLcuuwia_/A]5 9_b_:=BQGMOȼ8}Gf:#8kK6R> FM aKͶ3aL9[kf'tW؄XXfMIܷV ,'֐馞k!:˜Li(5W'СC;,eh+d#bxŀVhkWom7^#cpUZMU~U6.w:"3#8Dx]M^7yA`\r(T3ݑV_=,Fej_J$|͂6;&&iҳ9Hi`MXb2s=i}}uq7ɬqsS-eNĘ=3dz fq!6]]DiCؗԾS퀒O} QplX XOYN"pK:8# R议*{YjX=v$OZ gϝf=Ls{&E6#a+lDޢ ow"Y?Rm5X@ɣG:Iaw\"X_1I5|m-mj/0hp#.;yQage]PYxڳ0a=#8P"\;@ٻO&vq.^wG. ǏܙG.ijq.Atc C8]f Z@Hc.҃^Vmԁ)q%O8xo aۤQ% ~CG$.66x,pǁm;**8sǏEd@\h,MyG+|+pG`!Yx`ʃ Td;kȋW'O4ǜ曚M҄dSCXMK-=p֊Vger~(VoZN ҭoU;2s|'Lmӟ Խa-"o6)EFnJ1ا`xg}s> _`ٮ$h_Xn8#ךݭvEX=,]cU&$h{ unqKLNU%? {{kF4}S/ ,W%؃ھ5ٯYK:򌑣3tNMg}#gAU}c P XÜur/@CGp &v:\\h^as8[1ձ ;2cJ|VUS}Y_^~!SI)9kvm" 0qVk&4SRɶxO^?<b?!r.,-U*uD*q#8FbFFȮ6;9>5e X\8g/,c:nHPѤصZ<;"arwJ~h 3ZCkuRJh/ L1e=F? k{{=DI2e`g 1v<ɆQթ?v|'kf1i.yۜ5X;_X7t8#0S8Kv6-dE~&|[a|sՖp4t[ҡm>7Y+&=:g:Gl_+-v&[UBub~p-HŀFXZm:ur/,OĿTxPj| M[] 9/lpG`xx{x#. wb'iWcLrp!Ũݞr7lk܃E'w̥uD#k9ۖZn\b!VR>#_X GpzHwW~dÄS7w:kxU^[y=ޭ|nD\zsPXk~vsNXRjy' =q!Kë*m'6q;'2)mj!(i=J_07wGit&]fz; LO IΚsٿAk{uW,Urk"XҚժy$o4VUW^M, lwnll87BfmS=Fe*

Gs@F9ۃs(]/%V P&mgHڱ_2wpGӬD0zsvpxΜG|h=[9;e=S \61gj v1};1ab0*z.9bMm=baND"GniS}lu+)|:}BqG6"G'%Μ]P{~LX=rX^RD%э4{I(%ڦPIy% 9Y ýTZV֓rRxY5J O}-cYT}Oy;_4&|l,#;Qk#'=tLlwʪ(pa߳_Sٶ>;o ;`n0Mҫ~N5H\8#$p]&;Z;Vi.mU%+hH}jzأ w`M@!5mI u(q3mEeBۍ2^ٷ1l`1m·tkp9I1b@ KpXWZ[So:߁ڟ.b[>jv;uzBzˍGpv$[ls=gٶL(ͺ۩4sFNqr{QsD/O ةM:m=us?AmIV<8@)[kU#c%,֭ؒn|wF_ wI MHa 9u6/lB8#PdV[勭=e&;tV l2z;/CdySYђ1Sy/t ڑ&h:S_Eaςxl,,()>CjU|7vމcw@Ҩ.W9[9 pGp#?NnP<1UM(.0$_#}q!*#B֏bY#E?4-@նn=:'۟(&\0HAL(׺Tl?`gR6_cX5EsaVGxq6/lC8#8CgݜqQ]fU?25l9}w-ӓ^& {q{D[U1EG O(b iuǪvr~%}`"$@c 1Zj;cD\@IDAToGS} *MFRge;5E;|` $.pGp^3QclIn\%6;OL$ZSl`}؇܏Ѝ!J)Cq0ZO?‡ c2S}bNz,{'d 2ځB^dy6Qc 5tegv@;t\8#8=!`'=91g۠-&iv>׹W#9)ѫ^c۵Y}ZIJ".6_?EƧ|+(TU&I9B6OB؛7 96yJŤ8]Fv;#8F9pn&M@tx:Dbsu_r{$'kjySJYk3mTO;Kqrlc_}CrՉ }Roi;_66OU!.#nU}=ss~=_/4!M]!֩#Ln8#8;;h2&hP:X}Jel Ѧc[1Ź=]9l<#EpGpENmczYַЎMt HD4 "dZvhKk[֞qA/2Ɓ,aui_s}aOJNS;{yMɆ< 9HLKZgMdK^~΂ fۀ`#H$PhKX)Ep!D+hK&EA "DI fpv۷WTS[WuWW=w'O=o֩Stю``O@]y5x#^U;qYIf<%5"-i^kZiӌ1y1nMZa:G덢Ҷ4rLcʱůćFuG7GY<C.S@l ă a|Ud¥wq#IY9|D-C?2r |'@Ca^:Wz#iO_Ѓ5{*=<=a&?46lA@D╩.' xN.]$h5Oc./?=@`VHbb5ږnOq]y?McJ˵kb撚H7/G3heNt،BJuΕh;9Lmc{q{mTn;-s)喕j:ۜvO#J.{N|{FD3mbi(VlA@8E~z:h\ҍ7Q,7NVhЪ^eyk>Gw\}jc,e\m%^#'jA7vn0+|em\/cu{.mhCp @&!PM>IIr6yASe(A r]z3jѢf`˚͸2Ŋs{8~e.5t[:5vFˢd:Jtݧ˲8ov;ǜ9J۰Xrl, !@'%/Uוe.ՇuGdph淈s:9itbYj|h(ˮ/w{ұll⛆˶^#[mlaV*-qqTuNx_9|.|\l&]vݿ5YS!6QP @`3lKykkz9Q,wr<N"RYw\=he]1uٕwv9q|G\S«˶ꑅٮ,}$8F*^}᏿{s ʐ D V麮RkWۿl[~J͵6xyx0ӘD>qi||u~%-쳌I{Njkmx3>=#EbXG -/1LR>/|%@}ߛMKbݝlvh1H H: @c蠋mʫԲN%<֥;/G9yӼɤIoͶmqFh%Dݸqkq1Ƭy.5kk2ފVJQb;Mmq7?{FA}L}Ha[~i AlcJ~<zw,6KU:PMW2h@ 4*ej뢹˧n|<<~M9F'&6 Rb}xhaOQsɶm6mg]hx-:M9q(166.zJ!sAs4TPj b8J ]ګ/ŋs_l25@d @8|j׶eiR~qfQa؋ޮ}$ 'gQճzĺ8Xjn{Nmn[A~k݌C06ǔ4ط`uo~_ mmZ d`s (WŷKnb8K$3M\?Lڀ*A,kqck:'1%GdY.;Hh$1G\ F\s.ƌV}q[/VMX/e5ؕ..և-%"mT!@8-ۺv]cq]xA.ݢ:߳WgWUގcQFxinSey7sYmzy#mUh~XlVݭ7/ujÊMm/96` Y(duNf=y9D>oٞ9Ӹ1Y0[XM^m֒tQAe<ڜF]Ќ"FN=6,w;.KVj\[M?-م[ޒc+ 0lZ]gI  ϴ!@" {Ǐs5{*߬WSNNɟ2n襧WPgq;lԗqz=6\v]3v@ק?j=5՘/;l^=FOqGr!wLSW6q@ΐ@SW`>\wi١xZ:fT :K,k{+'_=GƍQZۻ~=^5uԖ.\_ ]xx;O?ogutYP.86| 'лƉW^~F8kz>ؾ[*e^j6iJǢ>buVzMt1ۮ<j|m߿y,V^],v]eWٟ'@]I$eW1[ikiRgB5Ԝ0t%]+kiO7?洚FvRB~6NY;U3mƞ7ܛ+b-ռAr~`ѹ;wnth{77Wr덴ܗ7rֳϥ_f&ם!}]p2^ǺbJ_2b=7a#.Od=UTo^:wDE*>$GQ?OuAek\cL:n*χß5W_N奻[ɿmIxKN+%?> Ւ>ω~t7_=mlimJk>},?`ޜد˟մ?'Uj+j2p?U最lRGd ";+.΃7ݵ y.z{C~@ ΀|;|jx+ _K[uyh 9 &S>QXpq4qlֹm0Ʋpތ+2Trn[_Wa/Z9q[<\;tx,O_s\L!؅umr)!`C@)]ኺ6ִG}7?oK+Wɉ֣i5ϟ ʯU}v~*ĺVK\mhpעy>zY0֡\4kzKR6|w_n{ɴw習lo[_jKH_{>|J _B1'm;?$oI8hl8o`xH嗢ƺ@:8@<ʺK]R/]5d='C.9 %!&C(.tJLk׻+)ץi}95L]N#i/6wҝ/)\j<; o 4d#'z̐1$3x]|ս e*Q:K)(Ү58,Ggs%PVqNv[r`&MI.AÊ!p@5Gt!'tgƛ$D$(ݽ_Y$UC'%p|^7w(tGNWV[Lݗoܓ.|tK[_Q!cףnjńBmEQ: ㇘<ûRy~ԼM[/Ѧce86W'zKǷv#oX @}<8X/<0` )C |u_ѥJLWHޟ͠d}P?]4WCS 8:{qSQ'*ug;. .P{[g~/EgA'_vဵL2륺sb 8qGElGսzu{H^9mKyڦXyئ'@,m;Jy}59r~C#``dTB@~yN`t'ҕO|я7Ҫ觯ğE;\_{8G2]x]z# @ՙ)fe@0VKd/Fve{ K6Qt=2GRi,W&,n^rn{.59DJ>9In{nj>Y'x^oKvߛW;/ã,w`.}N[myNj3%v5e1m:b[>;- mQR,'&Ƞ4aeU꭛ (r$m{rҕ,3=@]h/+p?|ι}gWyfͳ1V;ߓG,F3ʤC(c6|^B)'ygmTZ]({klLVӬj}dz(}ԮSGҿo?'Ϗ[W~]H>&`8$ +Nr=X~OLc _h@;} g|i]IϤ|L/!:2cm@\X?yaqcݾ} r\O_M}~@e+m9mU&_?'t2GJ* 9e!We' tt{_TW>|x.1qW2 fO[9}`Lr%_2Tc'ӏ>(WgL3e(u;%kۧQ&Wz-\w]2t@4# zMKҕ}<]wz4.4CS 2:xG~_|I߻$b0΀Y$gYtWf@I^%;1ʊRzE;K\q;wơWk(Mk^W c`U86Ng@ J@IJLWm+inZY(͕Brb+?ߦyVL(F"?m4ɢz?7.qώU\Ayu~Gy%:GxźmG-cLDYK_3i9(t)~Fʽh1Ͱ藐ߗK8u @{ifKDZɿaѓO犵uh( t~L<G}?fs>7pҡ2ֻu why!0~I=|VԹ=z_*ӽrEz=ʘ1tm1,w >zml !CËa䲱.S!6QP @NNL.䁴v e68O 09\ɷ뽕~_p{Ze,ә6bpN &xFgV+[#ƱoW=c]#WM7rO!tc{<v WG68F'1@XR+ϕWҥd|[;٦m\ѴwuBp(rEַ7!-ta ~.Vb{:q_M/_dzV^%w_5T]cͽ9򳘣EcxR]'OA{ikW.&?DuUlߒ 0 m;HgvUbS1b&Ѧ=(Kj{GUu97|Ǟ۵Yei{۹-;좍];^w}\&ֹ ! v@I`}.면oˉYC`:niu}#]G>{_ǙBp'5~DŽf5uj_ԏVZٶ>bާ-Tƺ݃KU]>2ݏKٕ2(mK㖾/KoX}]Cz>HW҆!@@;J@|s,7?scu%g.fk<֕ &z:ee4^Ujuh< 2zLv1$/fb:8lSڕ~jw޹ @QOi6yiG{p-6S 0*Mo}25?W&"uA|轷Ǿk)mwf3Ɍƌĺ(tSuOE*z%]j3BQ&ؖ~QG}zfWseX1D(խ+NSu(ؖc{i,'!@` @D|5$hm/%`7emI t9m'WYMeMh;d#u',2jeͷfWy|*c]n;۵8lewGvwiUٶC 4@Xk>z$u{O@d@ɖۤt=Ih1u &c&~P9ߠ.5rC`ǕJv,UwYpnz۩{6hV?oUgG3o~F)G%O4چ| rb-ങ |Çj]p@%uq5[,"c-ݰcaU_&>ڷٌʫ^y~=cSwejVYM7Mzo([mH F @5+m])_V-rZ Zyk+Wb?/ɒa+Ti[<]NcY~[}~mPU=Z~|O4~(jchFYxLgAdYYx>tM/(mcEeO#(W/D]\gUd[l_2ۺ#4ʣrk-UPlja ohn&@8$YHUs Nlk5'˥zmtf@Lm5tۘZrմŕ]FbF~u펶doߪsp[zq@`&› VB8v~ 8#1 xP1cQwwGO\ L(۱ ZҫWZEW\6Q^k;Nq]uPdG~Q >b}!` ,2S 0翚^^e5@`tnwׂ Zª3 G2\BDSNIotYz5YO5\=KIdz8֫vYuBH H: o΂~ڿ߃[[gԧ-$SzR|oHReѭw{>K=3};zHh%g3a;j'f6Ԏ1U^>CvQu'嶸 z|)Q{7ng?m`Ç268 @o#} j` 0C{|3aO34$?Ɩ*jK[6{}14It^6m7fϦIzQclgrggg3k-e}҇6 ^)G\qq-̀@@ʷ|+'ADA*u&rL؎omdl{+>\ۢ1Ц[vMgDyf CivVN_6(h3J$f p/à p]]1JvV9}DXfF%gl?`sdA#-%ʞovR7ƥ֚+B8bҏutTv) Uee3Fer;ݏ :blŘ19F`<9-siOۨKdvͯyII+{;r$]hl,29@{?oIP<p2xA`t{ɩ˶He:fw8h~ba.-WcXbܨ'B:ݪ)kAIP]((W|m^{7v!066F 'p7'!z$ݖueR^,>fY7uܗn~wΓ,f>1KqЫY^r:\v[q[,I)}mcmdNmo䥯cJDZKZLgI˶-e#cz:TL |BX>ki ϧ_[dKY>4S'T<bWuڼ1փBWͥ6IjȩԫpݖT[}6*k+~MlGnu&Kc;?iT30>l̆+Q!_x=mL_UW:3d/{{9鿕BS=Nkc4]dzlG|G鄼Ouemc].;m2M9e1e*mw\ qgJbp̮ͩ Bc``,\CXێoz}Թp!uvS) o毦;'$puHʠL@Sί6Eq~mVoYDӚQɶWNujnGF{F}ۥm+vvyFU<0waY,H xPBXBOwl,'+WbK)C`#i/oO7TZ\6╽1uLcubg~ejM&yQc6F^t;1LJ뻒Rf?ۨm_:6깕 Yk?ݟJm9,0E8   ?t3:r;4vy/@`9?ȷܽv_Vn/@(P2ti/wY[ʶc,b)%+|v=Z&}]Ĵ,J}$\Jlc]:Wa'>CugQAw۲w=ZP0+ą p gn~)^|$UHWi:K7_Li5?PM'!R8e}~t۱n̺REmIuDzO6~''إ#m%vJxWgy_Kc2Su:@^n~?t X ݷZ&`uD.Kou3mzYzo۔pR]+k2:h[s;&ےYn_]Xѳ"n(lv|OM iXV @}ʿ_g9څiwY,˛\,3hn[O_M?JnJM[=Y)]dcqZ;δ̻1ζ]Ҟ%ʃbb}b(wiJK^#yexc[2Q 0mă HJg}='Zz7uZv5|۞L9Qmp@Ala⥴sڿnZ˿4D3| $ڸR)uAOm>o=鸣Y.e#YF9bJc6,wlʺmg 6ƌ[6#(4&ZlA @]J}}ʯrZȬ>`Ma+J@O _+͙fl?'wk_i55{~עMYW\ǶڲmS[,ed`fXD;KFmҎv;DGPNk5YzQe6ҹm1~-I i$ E&;Ͽ^'8Ƌ|<]7|#fT`n#W晞u'^JzΫ[]:mL(_ ֻ6ն]YJCkHky+=ד{2~lgɶKM^>@k'햻tBeoSu.]~ٯmM @`JH@Xx9YRo^_i[i'S'|y7?5P]jg1'x%o~/WV66+K=^=P ddټ礲&n;llǺT6\bXŲ(\e+h[l8'ղ,ev+Fa[@IDAT]GGu)!0MlL& ,:<`Ts{Iky|⯥KdZ6cW[΅A@t{F}akg'r/OR^79DY'?m'Yԫu밼ZlĪ䥶ٸnc{D:&]eWdl>$'?F9  +tSsg ]O~o{kk݋Yb@Q)最s?'Ms~c3~Vڳg4,m}ۚ'<(:t;|Fz[fk]}M8KRz鰬.Sq9NI|jk MZ `l% F@W*@trVݜjo |{Z\vOZ{-S|ٻMzHw/t<^h #s=vKs9r|^y%~녴~>ϟN[_|~_vsʸGh2\m{l[+ef&YD9f=kcǵ3 ٩W>6fEy櫗g>淚7ݟ?sjZ>Yc1Wv)sX>ϟq3m}Oʹn}n&'d1$sۥݬ~˦ۯV:v-,poigb]QbX}[%-3 pz'wݴCywZtϤMk;Un IԱ隣[6wެZ9-q+V*&LF?ys2oϋ.ayk9k_}9[icLSb}tCoonuy(*ֻtX%Xf;){ɡvYϬU&֕hC l F#$GkJ77_M tڛߖ6y$\~oZrKsbSoOh.M ꔒ&յ5ݞ8 oSp rk35:wϬh;f WS~{/'wyvrw_j?N{J}KSfjjb*)$Z2}mu[>}%%o7Wc[NJ Ujﲶ%yvgS+7O>6|*?#౴viGz})Sss%\|"{Z{S;i{^цOAcPeaڼK=Rkw,ݗvt[}jhٕh68>ڕ1"73ڑQfXD``֜C8=eRy7ʳ_˟_ʿ~!ʥMuv}*_\V{Ssn>Lf5w?ؽ3AcQGHzo 9aʟOc<,,cV~p믤w"od}f o?ؾn391~-\}2>eM|ǂ|[4~Sڸ4ky.ޝg Amn*a{ە>v6ARڶḳ~nlC ,6k- !r a'~;q0n}tn\{i?pzB$-Vqo~fE4Û>SLB K5('|4Xo-~Zۥt:6E{q~UG[Xf~j&}P lp̒eĆ3%Gw k+|5©fcJe]}A _lߵc @`(%pNlxֻ,pR^G+ƸAde!.mo/{l,!@XJ%9\[a2mcyQmtYJV/}JwZ o1q3b@ 11I,]eWNOxvQWe=oA<#emeo1#o'0G@- @8wN9 )HnHo^V1^_ۭ٦>J 2lcm䭶lK_D]/e! Wc @Aɚ;8I,m2F. k=qmGȻ^Cee嶏0mcL(!%: @8ԠcXʭ\uC:>Ʋmծlx=8u.2me6   ':@ۻCO+Ylw-˸j5aJqMUȹqwVbS[t\}~~|׀@ 3'e(uԒAcY}qT/J}_iwb|"kٺe;ښm\&\/cJn|]]ծ > j @`x Y\~]ek$2zi{moS6y-NͶ6I^ŕ>mzljiJ@863A@@-4qJxe6H_:1ZY&~˶GQb̶m]~k_z"ϗ>6N9=B !0iadzWLmǍ2ڶ$懼Okҗs/yCum~ʾַMXy5Yh+| @5b" Jhf' cǒcQ6e*K[u۱$m)cqa{}Oc88k @MH2N.X&Q/_RZL\8نrt^a+[>_zq]F83N2nL!0.6%= @XBJ\D&&?喩,㏪v&?>!iT>عOږ>Uv,Jeм}57'"PkI+;zwP;  [y.ۃ|A`Eb@ LD qT˶>GźmqXfhc&w2ڨmrܲt >lb^r -hC`&MbA MLIfyLjIqMou8vj[.ڕ/ec8QVƠ i``4@ 0'@1s(UweS,ei?l ǥu:Ko2wXFg2֩u66Mx @'&0J"m<,zۗrU6ʨ'r>2F(wuucSh:fE Y%. @PN bTk:>nKW٧ewYYN9^ke^ZcWi3,1VLʺ%6fI @`If)'&C5_'YQ'_K.K}McFYMn8e8j; I|"?b8K۔6Xqk1,?(!P`$B @`ldFAд%5m><ɶlKcRqMlG? >nhCx=b5mV)Niر&S @#PB3iRS,5 T/-cFjRKW;lvgth+| @(؎u#%eoAsLŗ+w0}r@5lԨ  @kĸ-\>޶j[6W-emw`Z-HXf @` %Q3ChgyLbݶnazQ^3bF XUf @hK$L:KH֫l~^8zCI pRC ̜@Lcgmr8 w[|t>u۹uEhce+un}Y>2o}=6fϘ @Έ9&y#垖ݖmbLQfyf,%Q @)Z*Yto>˺_1VQ& 0ԙ-\`.t nw@ (=6Xm%s.ymm}8n-fR}q{h2̱6odO;N9=B 8dlG[f&r'eb;ڔ1qi9k,bߠJ׭``RrA 'SV&[+UTw1N2(0]+壎 ;'/ @RP򦗓9m0l/m6o˥䱮L#V-F(ůfX㎡˲X?i{J̒Kl@ S%0i2U%nEh#^1u1e,TKM E۹,׫.|X!0)6&% @BI7MXubmlRWڸ]aK/$v[/κ2vi˱Ϛ,m|~n[O Y``t @ 9k!;8mCzNmdz.ƒ]mdQ8jQTq-o(곔q!IpzB , ZVʶ%.<j.mNxD-ee _6퓖W=n\;V]\TJn㔥i`4( @Bp"WNMn;ku.mbĺ1dzEW]:+c9mҶ.ݗ},wi؎FcoPwm|sL/edl*0@ sE9Dܮٖ6n|Rwɑ~Q}3l߱te[6Q[G,KѿKk-βQ2}4sq:x1l}SBpZ @` KJ}l۠/DOuLu$6ݟ}ʶq\ʾuZYs,h+ض}rݖ*]1e v' @=ϫl[>JRN$?emJ쫲<ܗþn[_+cr Qz-eW^zn~Ѷò}!p,' @Iu:bGʶ8NRml:Jǒ>m8+~jZz.b$S]eԷekX#G%Űg8p  @8q;YI2ZD5u.GcvQl{C_ExjmnjZ]28c]z.(㹌qiǑ؏:wj0@ '2. &.]Wel[&K^;b Fc]m{۹ض-GʸJǩޥlJA~J6Ίc좭ei{cf':A~#$;um#Jzǒ; 9NI @ӜW9J5 o|.;~~GTdo})SS寣ݕsۥt{P9v~Plt0-ā @@&u11~Ɗv$?cǘ̺QUƾ,w,K[-s)Y<]ԗ}]Kaҟ6Nwe @e8tjP,%zՎ(W-(srٗR&[Ŷ&9>jvd $, @sDIuLkZb[E]=1bQb /n?aJ׭eu!0UaL @8E$7&GY䶉rץkӗ1vzT|җlm_Ƴe J r @C%’G]ڥ,ƈچT.j1[ ?ۨv5}(!0x༮  @XjJ8|N 7ʼnpԷXA1j~і:w 1>@ $P&ӄķkkcrR6oT]//}b @A mhld_w就L?A`5aC @`I *U%]&DXf @E> Dc74gt88+Ǹ!@Lv=y[y׼qb}C @c'l $68 @ sGݒ19 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @` Oet:) @` pU3+ @ 䤶'rWSK 2=̀~ @E ZF@sF?ڲtQ߉?70!  hf) )%!wtH+M_h3tI+I+WtfwPMYצ R `kPBUޯ[wvꕋiw+]~'Yi o@k?_|!v3rf}hlՉ>j 3$§k@z_Nul{-]Gӕ̀hϧڇkSsZG滿/_]X] !@8lt 3&t-Krg+]OW?i㭏Ǟ̟p|ϼs^DViw{S.ߝn?Ly9?@QU~E+/ @M &N pO^wo?S>Rurº}7'n%<غ cʛ#zum\6~8?L[E{ɿXc,@NA> @`N(Cѧҕg>L~ 2?0'_t[wp0Q ̜1@ȷ7jҵd?`y@ D`sLNOWZjK)~10 *tGA4@zT "p~{ޗn7.~j:%J: ?oҊ_`&;?q?]n~3i֝~l4 @`&MbAt?tJ~ߠ9?Um 3+m= ݴwL6No] KLȟ7r/?o/4x[=p[͛s.W^ͧN~SiOמk~A8_5p7@``qזAXN\mk?tT}ݝ;rrqFPl ;3n ˿+is쵶 LWASH@83E򿲻>?iW66qfVtʕSߙV\m6^~5׻NA<) @`8 5| @`>j~܍=?="zE}ݝz4]O6_|c]kC 'gH@8տ{?WWҵOLGimO6InoH}1upJoz$]? Im:@o p~V}=\6Vwtҽ?ʿ=:tj` p֍!B#G"o1 dʹՍm'uJ@{O>:@Ns *  @}o|`/]{/$ͧ޸ohfkGN7ϥ{otG7|8 @_ p:+->Ϳxg] ${g,S/7?rbkޝn?3 8 @WIcΒn?OqNK~&՜Y.ϔ>u>N~=j]N]Ng@w, 3?@ DD6"~|S)R3duiY?]yw35w~ @``tVXB?{ U~vNݩ*.eFn7&$ĐH $$HB1ōbpUlM%+~}IVۙ۝y%߼a߶G|k^Oϐ6D /WLumG4.شM,]ێ`ǒ?@8Sg_d|(X_DUK%JKu We Hk3Εv/\0Z2F1x"+!X ^lʄ 0*`C-|.egHtghx(u_0ڶa[A ǪI/!LVN S$@``@HLixHK^*j6 PZbǟ$ug- \O(7UDpCqsn( ytZ" tkyYb^v'_z4=0ӒM齠 SI! PvF ( Aiir\2Oz"oVџUJH: @FTbS'@"&Y!$`9 ˝m. 'P@NȧS淽Or2z@@ , PN':UoؒS$;,WeOHH˥K%֢A! /kpq@ ~QB@ pyt@|Rw? stExy IKl1WiL8-! R!i@`n),Z$3xTovhgA![+% EZrv`-@T@:1| ]ILVjN[.5g/:ԗ+C@B0̘)',M|D"l4%D^@%`|MⳚ%gJ3%T]-ѿXPr|߂:?H8?ϻHjuȑT ^  P P' @yn|͛t]?Ost6^M{Æלsď;A$5A$^m5ʅ PFe *`õ-1rZpw>6;( 3CBшԜ~ԟ§L>A@`?q  :i6MK$8Κ,KL{ḠºhrAJDl2I՜p%>hK+@)@ N@&pY7Kl Ͻ84Y%vJ}=֞744CwБTJgs5thXDrԂW ` @#e(JH옅:?%rx/svy:֥ץ\wa}=Z;EJgU_S#O#UWQ‘wy @ͩ1 bm9up 9܀d岅CH'?^fҺ]"Ϟ; )zէ$VOs4*9SdpI-y ! >c@ Y̛'N':EqPub=bbI'tZ2ÒajtD!PzwQ (<+> 1CtKpaGz%g~" 4AkqxA@;j!ji8}fz32 d瞖[C:}to"$X/s,c$Ac&3¥ˤiF);gt]; lu۽(=NKI{D,`@T(@lqY;^BU1خ[V;Q=ªe3kgvhv$uD@ {^޽[uMImc4q~64H˂ŲeigJ##,`S :ڠ"GYX#'İDUFʼ$@'@p:| 05~͟ȜcBQ;O+f-:co$xL.2خlj}˸̘u|#թn/;+{osUh@S_q|eeּc% 䕦SaJm,Զ"1EqR[Ŋ@# 8" .0mm*<3i>*ICɣ?ףz+޷`}9IBc,K).ֿͫyV?&۞xTjg/_f9+dv-u$EF[,W+ ԽIwHbB OVFM  0>/FpC@9j…B?2_{S$[  N$1ttl^/W|e/Eks *DlE%,dC'R TUA-FU@I4As@usIKΐh}N8#*o G'tl5+'ӡo+[g_}TJ!`4YJDOu.+ɶIמ]ҧ .ri][^u5BR}i}ѪUR=Gk+@JQ1q k6= UH|ٺX 8[;Nk)sF6d?T?<4$3K7Ȏu>]VJaMEBֆ[K6;ˮ` }KN[R:z’Ub°u>!! TAmyKXբ%׏<}.}Vp]{faX''~{gɍNg<!R*Sl[<]|: m%~cn+aYzeN:T)/_ o1%-X$޴pU} A  @W)R[=Xxҗ:۷?_y'ߑ=9^[ә-J>o~3IhFN<\,Th 4ȧRd\j!]tg0GH9@|:Vj* \W?Zp+֦cywTjdbJ;6:![:SnusNi\SqҽȬV_,;,F xq( PQV*T'5)Ok|oڹQ o5.]"t:eOZp4"U񸤇D"ywcs#S~+_<2k '/-H@#@<\~Le@%5|+'!w<ʮk_qɦΐtgbZߞgt$@L]/y[%1M]3=Y:ͤj<4MC @ 7`[:#e74Iunq?[|ǝ:{7{Um҅oڬv߯sl]l:/}'t[FKc48oۘ{*R[/Y-N; @-J~ !k-U,bο%[o?mO'ŝia\֦}@H.^h-lTEauuRL]őSO! Aii[`Lg+ܨ#l(Rڌƿ}^g(}z8!u!ɿ;vKX'?l;2hbp'u@IDATs-clg&[nXw]Qy$6Ѫ'G +@rۖ!HV\lGAٲ1ٻan}wK73I;S|O=$(ryG/t E&uCdl UQ(* n pK|@+ .+؜NO:C){+|\l#kfח*VxmLrO<.[\5د_K1bM @ H(@d+ۊjO{]P.Y_uOKMv4ufC6@}i aV:Sv@azM7贓HӌQF++@'@`|^ 0bG+C>[s8'nCT_\`N˾g m ٥[ZPCW,aQ3}wKbߩD)iiK^UH% _y @ z tE_w‡@C<@HµudOg}8 {-y+׎:O;Ƀ ]۝ҙam׺~ Ɵ2@» n Î9ð }oD{vȮ矕n5WOɵuX4:=:2eȔUUmmHg@ 6uE-`۱569F^ؘ>[e.I$|8ZI'6[}~`:!\[#ɇHy ߌ3@-0rc1uEJw.KNWϷ5 K. TJzъ9nx L̍@&*0whe=φ ckn둈BaU֍6K @)@F@ Y,-׿SCCNgy*NL*-{7O,1W  +j.  ЭPJ;4M";;dsݷkԀ WopO@udr:n#ѲVJcFB@ 0LSSQ@Wt: V̯4]O* @" Tvy;3ySq' .@`ɹ  @X T2i`^  kn>  0^򑪘-\,M[Dt/}6erz!SV;-R8z o8Z^! <ksj 0R Y󏕆GRl2{\-r֭Γ# <y  ~i)ʉZ`ths VynSiO5`"hj' _9|  hT@Ϗ#a֎_ gΒx<.!}boBת=fDZ9HS뜑4 1@@`0 S-P[COW~/=tRWƜR;0 oQ' քC&:,sʯh[Ѣ @ So@iȧӒٽ]rݣWPx9'*']r$5VG_P9ǝ:iѩ)c% +0/"C(@Os: =R$?vBZ'ǝyTKV!JY2FGę搵74~zuI2Y7Zz^! 0n&@ 0lO K>W^NcȂOqHAc?U9N.9P;SE,R e5KiȘlv (3 S`>PfRS[+ &j|\[uVL]:;mv>X^z$7QHy LS@)P[NfXWw4G7͕: `*gd3Eg>',gR][4v(l;7N}P5>!! TAmyL@aI]X@;6ܗI;Hњ7^% N?SB:]>b,4p)2 9i 6r6``U|@qqq0 Dn:mٜO;b6d9寖ED:<;m7+bPX-J94OPR5*i-^@B@ TvRGL/蓬n&n\0FwXzdE$I %Й Ȉ2g+·KN>[$\0_^=,^Y>̎͒uFҘ{r=^  P*q UlN}Jiů!ihik4 b$NW_r%$k4dt[렴a_dHWGth%x H@/03JC:ļi,9/f]4zVo"Ѩ X+UW_'Ms߰c&i'!lM[$'Z' @ :uF,ׅ;J~xX6<ۯ ǮorFП/=]Jx)91Dta`J,ǜv?^\i@c/R 8Y#N{:vDQU@8(>t]@1wd{IHllks9[ ېhULN ޽Mi}v}ul@yq(Wsª$u1ƴH w]-.{hJH: C+Ftu AB@ x^Sc@<'=NYB:qy Unw)o/HԝQц{`H3_Y7_};咷KZPX0017ަS|Vx ChgjxFۜޱ=?_Hzڹjl^_~+v"5 1rs6HyX/6?ї[߯rU~in\֧N½KO{N@k9ou5V@'@ pMN@ 䵃i)\_/$:kTSW, ͞q'Ѷi \@U ҹu$R g}=]jLddV^b9K>i=vsFF;X}Mqp/-}JG  M)4 0yCr@@[%; mOCŕm*eos_/|,nu$0 YYl1M`-Tu-\ U%g=;:YE=mJ @$@ @MU@X/W2'Û7KN45Kz֋-v~=SIJߗR]C\w].ϭxPV~<_kՕV鷭8ltlF{Hh=$93x;~kkKE`oVV*%u޳s诼B@ Ht N%ɵOIKrz Xܞ<[<tq9o3OZtS^zo(;v}bݱ]l-v`AIgNY/oOg-Oe'ʂΐE/9[Z;rz6C_naUXRpu{ePKlx^*B$@*@ O@ 鄥lRsYT£벖nJ.n"Q]Y[W/.~յgz{~osS 6G߂TX_ qKT`Ms?epŲӝHq1B[ Pn||i&FK*pC%Bp_ p$1KdoohcvtbHs[g4KG4UK@ݺI:$5<Jkd50`:e& 3gK/kd#*>%3Jk%OC'5W-@@/x>E@`QNxKjAuuNmGΓjnɼ-h_f4,/! }Q07o Yގ,΅;r( k^BLF+wtj謊X9u& LDD8@`rwRg.᧞pmNZtt}<y]Sh}.:J7c ē"*jStɧVInxЙOg @`tl`*K@&PmOHzBIwk nsXq#;F8@ˑz.y] ۾G7l]& $ 6j. "ZDRVtgu^{au7҂v9g( u~/(Rtus}2Ƚҵl '~4B@ H@Hx"a Kupx9N6*!6_=?M{H_)M_ @P7 CS[7JUyVv1lf|PzDJI<֣t )@`: oX |Dr_{ZWsgxg? i?gu-TG/   'l*Եݬg;esҙD;yBHwK]1-Z$)sLpk< CP /(l}gd;At/⍢ Hc+G$J^_C{@( pMdR[۶򴉗tΨv~w$~|trQ6@2 (*thhOڮ X2 %\S+e{tG!:="TkTfR+@!%X뫥j 5Pa6w*[Uւ _8@6$w%HI7 ډ E+4ktv-0o%N1c xu[@gd|[ kq>+LpltȗHzf;$T&vM=ߒ@<8 @l$@@Z$~Oꂀ\iCg'^6?@ÃN)^qJ@F(,F ׺`TfHiscJY/o h?gmh?uv]ܭ+۔Kő_ u$@)G{oj?}^x*Y/kZ?#8*& &iB S"`CSUuǷH^%|}a8]/ ;8))[@;t}ܰVn <$v:: Aˆ$KG{%=`:?24Ddܺ u0m?Mxlz%m4]g:<,j|*O `Zg ĤĚǤ۞^5ڿ C55E#`H>|V^NPV=.gR{Ega)PaeN]?9 } =|=dG`F8x 2Z2F m@w$sTԙnh\:]WQ4I_9 ٿ][6~Ht2iŦ8O'p9NA@ G#Q:6߆uk%e4^tH4&է|fU됲S:jlCö9JfvnIw@"?E @A4 s @2 ;[NH}k++ $,``RUf_8ANu |T_ ]o~:" +@`z @9)%[ОPg2sduNzz;_'&!]>L (^2zd $s > zT7b:8@ٴ?6#^# t .i `I> =]қKI>'K(utnO[x=Iv!|FI5PĖdh*}3Hi_lE3h矄 e P6z. P^ƎnMtMoUS^"]t`󩔎JN#Pv= (o5h_k5ImE~ I{Z׭wVǪ-Cu1@ _@ۀ E::%Kۤte虧nno@ 0I봗:::=kw%yźP$JlvցaU@ϵ-9߭@PP^ zC:?)NI}J[V>$f*0 e P f?&c;j !"_-c؉$4@sqvȧu  PVa! GЪU5smd:%~;1K"s%CT@VYB^ K[:9j['KĦ$H9;z CuOB@ PQIe@.D~} 4T%7oKN$HL iG$OC`tv4Y?* 뷅t>=r}Η=tvH:] ltC  [ۏ#xM`L:G)/'{41#挑s ! t" @@@*]@0C@@@@p    hd   @@@ @ L@@@ =   @"   @@@42UD@@@    F   @@@@ T@@@   @@*"   @{@@@@#SE@@@p    hd   @@@ @ L@@@ =   @"   @@@42UD@@@    F   @@@@ T@@@   @@*"   @{@@@@#SE@@@p    hd   @@@  @" EԜr'Bd$ӾGZ-MV=*!?2FJUj>ObH GCԆu\LCO-rrUםwǍx [!64}UD6\Jv_wlH$*-WDf/%̔-tWZC]E8Ǽ (" Hewny#t{?_ 9U :Yb/YZ/El!іxS% 6B"9!v ou7ճ鴅Kϖy_{4kLy( L܎3@$xkt#_%RW9|{s4}#%&n.L&@j#6ދX,CAlDI9SG>.EǕ\@Eo  @ $<ӓz\92|n#fe:E SJ S.nhkL2?H9ǻMW^kSI},CbS L S#'ko9p: Q 0]Fm.u@I4As@rdzŜ2.FVDuT= ?+& L܎3@$0f2]ZӣH-m[J> S??6WSLI@@ tSy@}˳x> z|~*X?'LSZֿN?eװnq0e!c@ (+?G@rf-=mڎJw1m׫ >B~{gVoBīegnD'tNy{ t#X#AyۯKlUפt\Ns 4/.[d>&N7\8@B@zzly[$eEl}+ԑ\Z{n^xkLyfRl{nYl @f YeQ;@iH} ?Z~} xXe>Itˌ?=~njPm$~̂ I  v U T ު=$<^:U[dqQwˤoc)3 $LS@@Hy+IWJ^A̤eWJtUϘ)s?>@*G@%5A@`ZbO淾ӵkuW%۱׵Q3U sR5s@ PymJ@2@Zgӭ ۷Ⱦ.ZWnKf5s^Ob23_t+u|޺٭@`L#6B@ϒW޵%?.wҵ*)~*0[)7*mlP6$!Q[2# 0M3mRs\-stC;M}'${C~W>/Z. 09l@*V T կ?g]˯3v^ x- )! @T^R#@-P,iz}ޡNh?R)ݟ8὿]  f L@>9޵ \: Wg+/d;"͝&-6ӭH +@rۖ!@ #*Z/z@^ji]Tl*eAZo]q+-]ˏ ʮ^}0znUgp: | @T͜-5wT@m* -W_#普TƿZ`x#';$w3jcq8 (2 W Nk+ܱN淾ӵzMp-?2:@W [}8>}'hqxq( g~n=ʎL K뇯Y+D>);yRzo+UVİt~+G!X =!>m@J Pi-J}@ Ԟ{fx_^)/S/PZ=Qvppҥ$@N[SS@6 ̗z,-D,&k|)##@SJ S>jY ?֕d Lyt!@`L5B( DP92}tr-?2:z_&r.12p> >k PbQ&k>.Wtؽ\)T2}M͂@|$@GEQ@JQ^~4]z._>-0t {vI{RNn\?9@_^@.}=V|($s޵b-He2 ω  6 V=*=5@ ?oNqdB~鳮E& g08a=ND)@F@ b7]QZ?tkoJf# j'~@&:剜9 > ƣ vt$>bW_#普#{t~E&io:ǕY)g֌F!5@tѷexJp?~A#οEM@ l?gZ._)\Jm=:ο+SN̜%Us$LWdI i*  8b1iqb#yS uIRH ` F@Bfw+tvVwŒL@(rsm@@ Cgjrn/Jϵ@#@<\@)hq%瞑_ĕ@+@\@W-MW޵< @.@-HRhIDAT@( C!sysI?݉&g" o(( "oJj,u"#y+y  x_ۈ" C׺/IN$@`3D~T3]u}\ɋL@@N@ Ȍk {oAB @#@ 8mMM@|*r}D\Aon's%/2A@O[QR@ 4J?|Wj_+y  Kڋ"M kl##@%EJ͓;ɾ+y  Oڌ#D :gXψRneG>  3>k0 k>.W*<dw%/2A@ gQj@ 9i⵮2N˞QBF&  W~m9ʍ PpXڮ޵oKz&##@F`7]%5KRtgt~E&  on?J PaFiG]U$##@%G@VGg4RWK?w%/2A@ R@ DfO]͞nЅGF  on?J P!MoOJ8qFݿ$>J^d !@2ڑZ \寖sw>i]ɋL@@rTN[R@ OV亻\ˏ@@2TF;R @ z$6w+5Hl ~=W"@,՞@g9d{v{oY##@Ӗ@|\"ծ  @ 6F >9tk])m6YW"@Lٮ @pXڮ޵R7${k TkSj 7]%5'JIS]ɋL@@rTnR3@ C׺V_gdҵ@*S@e+B@?*Ѧp@7E&  P*} b',o~+e2lJnd .@[!xF %%R[/M\ɋL@@T~SC@4Rwy&%_+y  C@0ڙZ"[ ֏~µRt|w-?2B@ PmL @< 0=)])ڧ痷  @p) @sL t @8zGoő LHOHzBxR?ӫ|@@# 8"  0q3_*MWf93;8 1@8zGoő K Ku׏lW3@@C~  0)7_%5'<<ƞ?6-߿N鎽xj2z|_+* 7x]( Mk]E8q5?/d,Q@L5SIf6/2  PQL2 ^hеmug#}T-\2SP@"@/-E9@|#;axU) m)uWYS@` V{S[@)ks>vTp)/'Ȍ7e@&'@`r~ ~xԝ}~_Y  pPeM@&&0؉"Z]R$! ;#  bH$[ pǑ\@# 6;|ɍ<&[G`"InFcK@ 8 " &gvy~zI+70?&]2 M:`eXL @) 0d=ݥ7s9 ixϺP.m.x @թ3E ܳnr6lR/5$zЮ\!#z{fRmܲI."T!844D )]6ft-:5o @ԝl@h۷^/_. ]!5- 7H>>L^W="TrhqN:2jm2a Ipx곉T +RJ/K_QmŸmɄؖ dp#ң+dD zJ;h* c_~٨{~W ?jj[{qO  )OlT۫_ P+wELK?_R?"`8   T o`   >@@@@ T@@@   @@*"   @{@@@@#SE@@@p    hd   @@@ @ L@@@ =   @"   @@@42UD@@@    F   @@@@ T@@@   @@*" vm7r܊($Oik۽gR]eZCRTD` @~ @ @>&D @  @8. @707 @L @ @a|x[ @ @|g?9/ @|w폒=ؼ>Iu @O/wƧ 8><νx3ϯ΍ @g ʻY8"܈'>AҜ: @/X߭?wϜ1 @YW  @Xߡn{mf:7>и1ME p(҇ݟw>|y3W  @N`z;Ss͋Cc8kqR @ }n\3\:|8 5~LI @|>73yk_  @(wwuuگM'~"9\k/|?۩kU#@ *6;q{{?Oz>/=1y: @$w_Z_ߵ]sǞ85`np\SO/?9ǜR_*8 @"pcg\距Ӗ- hkqRONIc_{=@i kbĽ-~5f.{Y?5_Jsmk/O p,^Sn["}-OαӐ7+. pȀ!Kk} {y塕qayiJ @梻ĵ>w˸'77ſg{ޱ"M6Yä@l_ydzqט}1k^ @{W7c[[k.onoKTyd݇й8x#@2okN @G w-}N58.׸jwDM>mEZip1gI?ߺyXVL c92&qk;ߜG|~K @cۣv.gl~=Vs3$nKbkA=kSf>`;>1cvj;۩dB @㷽.^c77Hpaٿs7۩/ @9br[[Y_;WOBE?[g\`]gZ1Yz3N @;1.{3f'7K9u @_%0߃{%1)[^{~y٫}vHyuyt>4vc3f\k~ROז @K`}n;qs]}N鼯- \[lk=Tk;YuK)k˟_Қ @>T`}oe׸v[%s)~7 <4B{[Lq%|c-9 @lڗ8onO>~n7/x1{.Wi{7s~[ '?}[<&?Ǩ @*xe+?s748G{ ɥL?۳Co' @xK .>{{ k똵cnz.:F$@ ([ VߚY:ͿVs~ @B1.;:s}sxkw&(.{4~ @f!7f/s ,MKyo/<' @x$kƽLX`=MxSԿ^  @EY=ν8ҙnV @= =VCoxv%@x8%N @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @>V3PWPIENDB`ic11 PNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIDATX WkLU>wbĄ6JkPMEH[h!+m&V1>(& V JHAXR%)Psgw;3lvܙ{^|{ r݊eH2,G,> |p&+sQ{/ -Nyxg|򗟚~犒8:GM]YW=]9xPM%P ",UOyr"hݣ8dJy{ uwCY e1,S0#XCW_] { 9͕9Prc2&8sFǹ%c;MŁp/Mh R@m#ӃӓUt :"F-tz\HD/%^?20 XYfHa A7ʀ#t$5t8?TUWز(΢k#[O彈vg.-u3zzayw [DTn=[>D4Ym R.O*|><*ӹb JT4+|¾-4NAm7T`.no@_%NBZF_&#p?0فaX~z<:'wI:f4C-cڥL[6\sX87=;{gIh^ӬXoտI@#6Z;2=ҝ측CާɶU448zA2 '*cP{zhVEԄi#sL-!\E[غ*Hևpr4"A(Lٱ&tvWB'P30mwcppw"?AɣP}_P +s@8αtzqMFKWUpyn9X*Hpڛmyzl6p; JKfڄ`>?h'c5: S[j>>IENDB`ic12+PNG  IHDR@@iq iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o IDATxZyT̛]ZH-&(A "B0"X LRb̪PE%1IJI%f)%rD}\, .̼z0`fS=}}}LԦj֭`EOiՒ%.ԿVnZqӊ$#_%׿vRPw=o5a[39Sgwj)GdZyH0 $]hjijV1]M3}dΓ^4/K'Mi{/32M7 4|t/\fl iO2m̃[Vw^ iZ>(=v/Rqo2s`T0L;RXՁV!H5^?~2I]_Z&c;hE,G>'@E@^=c6+=s +uĚEyM~N1VYu(GGyřRZ A!GW[ l5O aMTO(x"?8D 5q)A@ i>#+n4zҸz$ʬ!&bpN㢮©iyq6Yx-'@F%`n̪9sجq_@e8-B@#cm#*֮~PY֒-wK.=.ْ+-# 9_jO>2PU9DZy.w.x\ %郆A-(]G<|#h‘3ˇZAXkrւ&PSި)*H (b͵B;QQ@&9ةyџitpϞ OGyOO&psA Xݭ\% diz#H4RАS[#i lehDhAi\Hxpo 9fg}c2 7)*̈́ɜH,s 8RM6xdK,y-FB2! D9^Dh}=@`=`Ԭ^DrsV Po  oh[ Wl'HRB dΧPk?6vPdk[eaQX(fx{$4./u8v%$@LiO4 dR,jOq\րL\\wM;%Ka]*oWVH[s(iW LX,Me8$+11 \ [h؛p%&e1yK$S1ӈ+Z<=za m{W[i礠^w7&Wȿ1ZȻ/@+LOޔ$We~b*88I c%Pcy*Ɵ=v5J vP`h? 7D?_AVԇRMCׄ(քБRFKB6`17k@3kĞo|oߵ§O %.nL~) 7ԋ!ÈBq gut7u:I΢w(zw ula[FF"i0}]7wF}jT^()p]IecʊᣨȴA{6v~"|Zt:Sv  e n%O߮Ԗ9Xb(?b5UITa,`xO4|yV<ʬ{u0'T]NI&amYBmwTO,0@oQj8$k-P? e>Pؔ1iСqesԼ2/S$PYꮯX+̼sN&׹wRd~f+sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o? IDATx] $Eyy{o`/Xf!ܗ$($Hb<ML$"j j"Ĉ + a]b]>}|f{zf{=U}W}]]]S13>ӍW+F$@ES– ilIHV?AZ?ZTQ3$}`E70NvIYͲff6u؆.cZ[XQ2&6xuW'?na׻iO}vib7oVwL?v-=Ž1YFoX8ȓˌmo!d,e XLQ*˨Xz#7ٽ7յ;1սi}ӾLls[w#<:bgz*ƍAl-Dsj F0[)؆kX3$KŴL,FX,=FGHѱik^ #"veu}sٲ ǀ=;0PQp,?AG;}dG3xಞ96etvxW'1s6-mri2.b<Kct&='q5gSw-O_vIVq;J x"6FS}O7oo ˹lvMzqu:`^9mq0M&Y6hf4݆}3f.=uK."86`x,-NQB@Ns2vl֋S,Nbs0O<(zPx hթʙ&\ N=v{wȵ4ܡ :1 g{0ZhR҆LHl0`:ر)w8@9ҙ6ĝCy/YoqgR1j\i406ۃJ[Xlh|alɞ!賹Í6x<_׌ᅁP8#Co]o}%Kqi<@5:@ "x0cߗ*D)jF؋b(^?_mڹ/Q Q*%$O RmqU@fRVB5ËJNHti?j ]ncn6`۝cpuUC|g~?q NqG&ޙgڿsq.&le0P[NȜXOݩ. .s]);5 =9 0NX'](u߼M@0NZL"(8FHM@LDyXٺzN2N`0S׾^vWX0;KЧ ceԠ~ww9c~4 F!nr2(K4$ڢ, KX 'ǗU3A%DFA('lƨLx:0(@ ko}tlҶ-켥O)fxQACBD4dҌ1 r0#[FxLcA:3d@<9 2aCK z< 8n < yHg׎-;? "rdrٕf\lLp40n 46~ȋ}i՞I۷{X͘1yLʈP>H' !`5p}ˏx0O  B4g؝QPw߁C_z̆۷Ǘtd= q a\v90o 4:O?q2Oei< (g Cu|[8P)uwIxs۷w3mFh  )v qa ^<덙yp7o󦙇M&l ?^9z6h i^' z~Rxp鑮;8qGe<#sy_?/w|0ƠKs]^qc(EKDDyuo6mЏ:()f# ?#8x~Kp, pm98#6 җW}N;er !5  *A a0U6W; .PoNA.?'eH,pΞ`pesv;ga 08vêڛ/@cߊ4 "#ֆ _Ns Nt=I9t|l| i Y`s;c54ܶm @f;f ̷֯/3T 'ـMNY j@@!=M*. ;?>~eJ;}C69kg㔱D nG (uT$\ (Ws+rmSVNJfW2= ] Nxp \]?zӹ? 3@F^BH D{cJsc~ev{NkcOVS-z6^(89DYh,_Sb^@+ Q;[̆@j.v"m0/(̎ 8Lϸ{q<PbC =/sP//v5aj3S¼rL!b~eװ, NI7^N-  ->IH;mhZ`MAO٬p 'B[К%z5-%|lbvzg4>FXŀOj2>G[V%7!w0J4`A&ٳ먻8~%g:()!TyϾi,|2Ϙu ځhtn =(a C&}Cu[X65z-W@=.˂2 Țp\h=iHicpccq9Ax6?D%~``ރ5<9i1ZZNK0Ly\17 |ܦ-:3N3سTdy &h4wAb0.ƷaU 8ǖbt^|;lj_,mcs^eүim ۡg2eӔsXL>k-;eR XhT27KPyQ1e2"4q>]kC8t}3{P1\o g~٘f<;o ]BƳӠ  ]_e^{v4=$ B%B~*A&ŵЀ QMs9u_M5!챃 W >~l^WTg*MKj+ؤC@+s`^:/R'4vnч]TRU@Zgs9-$wivP(rN !o;6N9zbHc*@X.ޕۣ1B7{R, XK4h:аWz.Xsu|LqnȰ2,>78Cm4njϱu89)# A/NB|9pU|کJ$E^^c(%cXܥ@fW/9r~M6J_a  LN/0 q؆U֛λPeA/M diYa^ KU.""oRZ`0oZu/4yBv6u-_l@a,yv:! a8bx< "pI>2?=q @9 մM  zw>&ín:FV8, 1 F~NaMA!!O|;Ei|+ ia!@Ba]AJc0l\'ɠu|?+#Ìda*M=qXҁ&A o1" a`0`, B6ญM:!Q7xq#G/?gx xG9|ة 2C9m 5+Tlऩ+~dTֈ|qMԵD ;ԕS3€7ǀ̍_4 hӬtÀcjx 28< L3(?Nk06MNa` )HU˟{3ƌd40ؽXl ٖY(rC"06zG]˜5 њf2 >NhSs蠫OHñH_gZT0ЁdkjDd 1iÄ0vZIT&5bݪTǢT!TrtZ}Kiڒ KHܦ^^{W-Tsw ևe@I E;Zo-A_PҪf3L˅*62qģGGi!dqMԗa,}q'&^z:q"7Qowʐ` L_2LzT;Ȧ jOPkTfv:[~԰! 1e~frGlٟ6J/ ?z5Yȉ["T7ևBf~4i)oZMG#E *}57^^["{'@]ig``9z43@;0+9 W:cBD_P:.y,Mv1iSeڶ~wyb{M4aVVQw5ݥ&\>k{McxAxWȳ^AD,׶>H&X3qqMe>{gjKBk d2m=X?N!c`f 64lhf`G̤'yo:O?i; G@+d<3?ɜ4a۰v]?6m;m 4CYBn-f9#C8%5$W}ttɓlӪ%:IёJ$)_8KM&\qMS.zwWHb\ Ƒ5l!;@ v @!wɐ^q2kH$F?<}܃k駢n@gʌҴ>~˸y&dlk˶\u N(Ë`2`(z@ tb\Y_yMwWUV⬫M1a4Ə)Ä'AcUXq) y84è^aGo_?쪿æMM8~ 7CC*ޭkσ5+Un 22 ̙5kZڊ}zʏdR霂i:br8v(ƌu9S'8x#O/ȴM_d@ . 'cJF+VT,͸%ѻUZڡq~CjhyÝfe#u>6C[i:`1ap!3a3  ~\:8ٺi6P`xggX"?⅏5,נY\%;ܠ>;S}6Jѹ96φ1vŵkiϨs `׼{LfH)4@&Mgj4g&k̳g U󃞬~heJ.+178Fq9ܝ[/;|V8HtCvlw)[>Xݵ#@e<*V<na9\)`0P6^mbߔc7P.2XuӦYrF$HGDE f+|l^Yj .K6Y/2;2duxoL@CJ1Js;-,Ua|j"ү 0v6>@7,/:m[N$dV#Ey8TМ1pREQ㮉ߍ/g''4} t^fJ6>h|DNJV=:b 0멼as˟q(DžϞ5ISb|xY4,/avj`gւLȹ!) !OS7  xgؠA@]\`\+[xF`L6S @!4bmad29,+L6Um'ո3QsUx'`3 =CmjW2t~0-Mou*wojaqeEvdp~3'h@#4ROj;Z%Ϥ{tkQO+TGǼ /!A?{-(c[ԾeFQ (kfAd+ e,Lw=AtqjV'gug ]A[dCwgU3?EuG?]yٱ=ϡp\_VWjDp۟ #1GNԶm$\)#rmjN1k:U6lwHx47[Hnȏϒ-Ay2xҪC00r1 l{7{TSU\qUUg#)u~4-zRR;.5 ֊0[C@UZ5s[peN@!}}]P0ջ+=tWx7'B摴NclTݓ͎zO%,Ms bN +kV(:89/|PC<&Unm1#*Ivl,E|H/:@`d?(DHG(܂kŀ]ǟi0ZN-vcx? ,7+˓' jجVX@]]Ĕ%ϐ_>{n(!28E9`fƎj_xee0A^Tf^5O9uWc5*̘Fc ux!BdЇ2C=,C0ZgX<8S(ͣq;+r9J+517oOѵfu kִ&WYU9?sh@eUZ@UY>gonY x ǘVtų;.]-E/ ]` a<ݾj|q-u#xcsΤ[0wNS7G@c=s߷g[k[{RN 0mz F06lț#O<42H{~Kcŗ=TXvzk}T^G:?ï$g6xqgUzUƷ}_m~{Aq 8.i'Q 3 R7#+O>]/~WVm3gD/=LFg̫=!Tk^/IEbX:FS#;9DJr6f*Ԓw11~z= H%7J,NO4,x|:DzM}*C3!5 :@oV9,ޮmYjlR99}{Uj~J:Ě}i: ~p7e#WMjjQ}Wkc9{S G0m*sd65&= )PU8k(ȝ\`w:Q+z8fq*U){+V_0f'D(ӈN7_^ TS57D@s_K1\3vv}HVR8Vokm›YQ]hf5Ԁm|NP `$ƶvܭևZHsxve f̼"`5{^xE^lN#]Xdg23̛FM(qQ!7Bpm8F=KWS +αmq)L%V]^^aX8zj@@=-u"qG4PO .@4  b"qԶ%DCD@=5 ږDӀ8u#SRh bqDԀ8zj[ DL"!"hPOmK]i@@:D SmK41 XT"N,^,Ħm}4 >zm-C;WVh+ݭT\Fm;-ܥI|{¹ſY:Zb.9y߿W*Uq. (TQ׀8P!`虴:Y_JNgNQG=JԂJ4 {?ަiu( `&Kv_]MQK"s Haa='XSn5o?)50р8>\p'GUSR pwu)4)o2 h wܣzA=ՇոNz*uwո1~ V󏠳6TjHh hn}bD5GXx5ߠEȎfМXBM%bG鴚Ϩ>VV_9ť@ uC.4Ӏ8uH8dpm&XъȰJ 8CN@ ?PCظ ڒ~STވ^+,>A?< }]/v:皷)s49xEO>]vF;4 ,rAxJՑ=OPPJҬ=Nk)9QE wW"\-]O + ob[7 ϪߡԿž8E I7nT:¥82z8joGuRG5 ʮVUijppjTK|! PgcU/?K Ԁ8HvK@Gl?c5Σ6zj( 1H-悥B|DBkjlkKjԆ]:_&]s>i꘷PuycQ2v8F!zw?T*N/ P$@\;f!ɗH eUCT׫΅TCU#>cڿ.EH j@|"@Ѐ8JрEqc_ UEcCF?K+EƆ~V|5 :o}.mV–=j %F/䩁K"M8:T5 IOy= mq~ zA U) 4 "m=Wurwߩ+#"Co!(xrp&MV'b^Hb_>֜{CX{Oھ\Nk\T]C2usY7Zۀժ;\nA)g7 'LTV2ӧX* r7>u }kޓyuj;d&Gp64mYWӰ`f tnYxݺdԀ8[s=Wq_]J2{nJٕoZĭuJP\?U-ͤOU€b]jܩٺYly44}Xz=H\ҵӀn]1cQ+ֹ2;Uk7CJY:Z| nm/C>Y1ڸN?h&i@fu蚦=it.9A%zzd8w&Lo@ q Q{Jf\kZʎ>S$/Q}q}ɚq?ꖒx"L߹H1x.X3ǟ2Ӽ?kgի4 kT1۾@ Oo*౜)sjתJ× ~?E<ֶ>^T~շaխ"0w>O)=Zp4j@@uo*9z!tpix~ڠˍЏ6O1S81չ.EEARcF23]m{᥮m6ScZd#/n)]= Pɍ `t49_ڗxf*@5 ]PbN{ʆ6WK)c,#`t]C$=64 ` srQ*1~nW^2cS8}> ;o$1xOὮ~h[- T3ѣA)ڙxplRΈhΟ='tdDzh'}G׺0;1(}d5m&4 k5O܋^'n(\ף-{04԰7}6 ]p7rLdyfkRȅ=_v}-Ҁ8_,6hjnG( 9,t෿ ,Ƣρ9g1a6/jqs\; ##)n_t_~y}ށw(lU0mka9Ϥ+VÛ6?^ տfmgASJQ~zjx?d3}QAU$bTNP`o_ /ڗaFՀ&@ԠX5<eQ-f5[&Ii@T"р8R@D(8FQT4@h؏  4HQr7 RT+1mZIXfQr,;P*@$uTJ& ;$A45 } 1=c;?Dňly"3"A48{&.y6GaЬ4xM:p8q e>c, X8< Ԙe"4(psg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRTyUUW=s -#"ʲx,+ Ṣ.7 r*0 3WUWվY+:;+ʬ̯:"|ENzeBfb%uf#m AYk\Ow:GxhؔCG34߫i X?:pzsʦ/;Zj/1:FmM/[e;Mc WusuSx} +KSW䌙upH w u5^va5}:9yS5CʯB$Qk ~T白7åX 믔YWiͧ䕰6QxJ(PNn$_%.@Op#{UGVd^m2#.Xrgc,5o-UԱ|mT)^WXeѦfSPK<dd}g,g2 oN/_;WpRbR`h*sd:EKo_}FV Tgn9_ Ju `-j M;T[G)~urLOL1ÒyE%Q_JjۥZ |n۶4{q3W 8[㎵WOHW>/RE )oM"X8ƹi;wPA7H\)*NaQ%e֧q`":Ll6_ Ra$~[OEX j[ЮR/56VLW-q5VxvY (N؎Tמ+ :$(l4]k2%,-rlK=Ngfpd"pE@ vR=}v)v"a3I_k'LkC <%loB{%Tʱ3͌cɥZ!a69g*aV~hd?V&J`X sω9wtbo I1~ۈu@T*YIAU}cg.mM[Z&=68KwqVVDj\,}vtڍ|d.[^" $W<: =(kR{)iԥXMZ5ĕs'3mLw1CB}3= =z `}C"`j:{ K:Am i8%$WRK1ZJ3+^  @\FL+`z#8͂kjTiN<Xi/4\DիK%2v9-=QmT.Ť*mF4Vo$`Dr r{vQ- @~t4ּM( ZG! axr\J\{y`v20b:TۧF"&Dso٭MWzuKT^ਤCVf˻l芼b-A-@K+:U5b|(`1`=ZjҸDèF1 _MZ TdjJS|yJX9Y΂cϬɵO,%P]9{AKƄQ"&mVO%@U;jMŹn3SVCG/޲*McӟLk@\GB`lf>_Op'2^Kr4_^ .&a3M)gTi6x6Nc+E)9ARɠM@]L@=p\#/onfpLmTN&ѐT]Fc#q`kVXTɋΰwq+G(RdIVl”1ӁG($R4_~S6F|v|JKBQ8ftHd0 /a6MeV-i48˙e}&3lk$|6pl3@n~~3(j,(d3[WTիڲ  lUnS-Dѫ8鏙O7uhI͎W94  D[m5'`F:"P⿝㏩S±hSI#,^ipQ^U鱄5N44DIї0vGM7Ɖ\Irx|&'꫌M[?se6K~Nk<QʤJV͢fX>Lj h5Uiܬ\Ѳf*:Ufolh1F!ϖ)]G{+>»f8 M~ (3il~3zix&2jIf;i\|g?Xpd$K9e^.R}Hfjq" F_~{*Өok빳 U0ݿ:+ekf6r-4p/cTo 4ic k}IuyG*@9ȕ\~r,Ƒ^_aQ+71\reijҬzWN4%y4F)>},8QàVV\.TD1+gf }da34e3qÏqQ#S_ӥfՒn`ժfx DpeI=^+ yLOлd8IDATQs*N(q?ޒk)Ux;)/G.J͙_1iȥgxvG:d ˏPv.<(el?GŸ* J W}nV<'R~t] 7éo SXAQ|I8 k s۱ʼn,)ٟrrCҬ?VwuG; mSJ}r,?QÉx -aWeV947495~4 ` AAȜRĩQƋo:kƅ!/SJtũowSF ` bAbL@r4)"ͯƫ/TRƚ7HL5 eF[#q,Q@f*sUv?Lwb|יcg׾vI<\48AJ86ܜyQҝy(_yW~ phU\U \^3^æamyaɧ+#9ְLW/]/ f%U*R*@'ex ;cS_Gř2KXHw1uy,n;4eJﻒ}4|oh. m#B3Lj%L#?DAt7~u1֝u"6y?3<:ـ9gI$ʿ-m- %=:YqΥ8^qP;.nҖ>F ?5 p !$:yvʿhSOgaYQ$R}?kK=o+B7 -O@1: ~?z>RBɎvy(MmsӢ+Y/]m7Ղ $Gͤ[(~q|߷Zru¿Ĉ>%{(I /<o}{qcD{A 60FGAR(gbZogfW YJ?{2p,#Q/@C`!Q s3N<}jY"ٚ]b-}ۭ@  P@"_\A_nʋq@WhşC%WtZ Lt%DXϣ%"̵̅H:Dv077|A `pP R p:E ?z)6ѱύx_h,ԗYP+|T @ `pP R :M}Ŋ߫zGG nX6d 6ڊҼVNڛ7:V1 M'CZ@q{'"-~[swD$::i`gBjdž FC-M@?s?lopy=–~tH@G 27o/}φ7QsBD@D'g.xm~9ᠶKhߋN88&DhE~j>47z_Ѵ>Č9\6ăL@\ MLD>1:{$-O,HE@ h0&Z(Ory w_dትt5?rm{|7uK=IMgZ#tЕE^L:ZsGTpTw8h_ &=WFdc_ S?/Kx=4{!a&)NX_ˌ#O]IlNzǟ̺ GAB!O/8^ k@G@أ`*ݷ-Xb)d#וo:~ Ƹi{,Dgڲҍ.="A5<(jS/@hz4u\:/T@6g#CCt+_@t3Xf~08uo.<(fFEՔN$GGik0jP@`4"!֢2sT0WB} /kht7= ݕ6'8gҼ?#3'ǿnVOP7Č 8 <|^2/W_nW4(ڕzҝ ~+!bwp0ei 8 1hIȒ=xw(]O3eG:dvṇQCE1@alœp M#ip U|톬sɇ=`n6?AG@ DŽLyٌxqJ##!`L# Y\C_PNT{Qh#ΠO0< <ձ7򷘛 FY-}y (GE<Do/7 &vҩVKߗ`߱(G Lg? Q/}$c^bv&_G xu#D}/ۺ%+3 k!2#Yjk(hAsiaG>.<^qapO4R@"nH@?O=$%;yw㢽Nxӛit0gdF^pY=c&6 4>+4 fG& ,em g_ydx3 Eю 4cA]ltSꓯc}j4DG@pR+;[I\;ŀ4 >t)[G <6af쩧}kT&2#x??2wO`Q/p{'::U`0#@fweo8V^$9#_:`qϦ̀ijOp:m5[NC:^'ݿ{Πr6?HLr-7ׅEk=o |w| 1^|3g|rןh Y:7qR>+' 'wtv >@hx4  yAʳ -'wyn=W.gwWRɔά]m+2_M8/&ˤcoZ !ȃl4 =\D H qz/Pm4Tѧ+C'4cR,(7\ˌhd' 4sR:7cVS$vk?)?`&W1  -+tՓDcK,5G\p 0Acbk)DwG5mfF[<@+ R 9d -oAǽ{{/ m\;׿gU@6@y`b,՟X̃7'N]'̬_ 1&Zvlf럨rדV]^}o|K^JGT H4*WHxR;=۵e u4 wp (NWx'dw@1F 4/ӟtYH(0 [I=. h"D@ @޿p a  Ow_h3Q&o4x]ŷ# [80Q -A@]q0F[_v3!!z__ڈzQB/XQi X;iWPBTx?Ю|6lo8w3K@P\ 7/t?˻3m6fMr==?)H !BQm;[6PbflQP:%! L"`@c mH6GpZ|fg2۷5$jh8 G A`2Ѷ_NVY}S֡04H[/x+-X1! &n6ɿ׿-?r;w!BC{i&jhj!Єo"@ EbyenW||D(zݏ *ۼ G}T!\` .#~6|<V'WQ[%_V?[2[7.=:F@mGݵ6h?x`D ϛ7~~h|P ( #`y(폏i'/~vY$bwGq?V"VM%: .zi跿m[^h s~6zcϳE&:!)톊Z@Qyrs/gWz <*#E{/ysLyj~h"@@ ">+աFh" h*GFh鷽z=y빔^.&^'`=x JeB~GQoe4Z[lnv!ŖK|Q;F@8C\~$ 88;n+ٳD8`JZt+=ݯ[Q-y~~3e7ohD{;}ǿQalԳ/ gKHX-E}{P(bUm$X KRUԛNGnBd}3_N!e*b zSc Z9_9oy7=`M#QǾ+*\&F:@J*kO6 lEцu@0[7УG8<=Qa|UwXFR42΅l]yrykXj:Ъitj+('eH $1Z(.o|YMC(i)j-^vu6jf 8[rgj. 506b7:c?;i;6Wt4'{'\xfO~iͲQE@ R`Dj l(? cWQ%Ri3gO&0*| (XihYٲpoG nbtpeq'h;g؏_~%7?p 021g7o8"l~͕3*]b-+41V|g&R=h A@`P.d[+j6:k=ˋ}ˍ/DyE7>+FKzr̳ m@(` !|!h D#*7Ը(ۭ}Z˺裗>~-(cpmsMBƜ<a'`ms*O\XPZY' r1ʖ|ظፕY'0 9QH@G2@\r=%Esig މC{6!=o}Kq\ьOd{G)adCόa' c/!͐[Y *xV2MӚZ(FKE^Ċ}׾We)_mߡbndh ?Ns^fO' you_`翤v SoxU@؟w#_T4n-jlZ`x7 0}ܲeeU,nkh)rN Ivu#6n΍i]:ă@  @ kL?uJِ& AX/ d f͟πMp6c"Q5 ~`:uoɽyK}խN6^2Ogmg8/ jeA `| F^ XKCa"жO_l`x= p)fEYSEdă@KRÉ΀@]+EA?_C߿koy&U~Vv% ³MFā@hI>&~(|4KīY/x~OVvl;9E4~QθcA!g, I Xjw[v7%1WRY dYi 2ڇ?T} ܾ` 1@ @q,{(|Y{K_:=3Q"S'B_8n÷ϋ5\]QK.:?ci B D@9AϘx7reC ]qk<>{ ]ʖg62Ϭ=E dHe֟b""[ V]7! #3<[;Vr}?圵{n;d6@ `qhgNt Řswjs cE-2+Q5 K{Tv^NmEյiч^|bH@ ]> |W?D5 L}/8+ycV淛w,v|h7HF@FQ(󕯥6s6H;O,нwѲ+N3_ik,AhEwJ|ɔf[WGM _|ѧJ\Bxch;]R#}ʮ֝Rn_8~I@8R5/p+`;I$\p?.ڟg' y+`73AO?T&w˿|q'/\rDf"?T S;=3, ? <{D!:~޽kW"˭ߵy 8@ >6?slo92 @38@ }س;=s"@``+&?}UUo~>@:xP'h)4{-<+8x7ޓA`F!p=vUE7_zU `q }IO1wl8@ Y9R}}=`ڟߓA `a&]pgeyrg>$ć\a|@ ,+ 6h4%j` ;mlW xkWaߓA ~01G[T^?24fՀ@x_/lok$ ^GO/+?#4t[ <*_ >s~5_i }u0P9Df;v<w߳lvSʏ D  x 2W\cst;uu crϊ?Ӗ/D[ @C@+mZ~+'RUe+׈LDy7-yЈP ` o߻yg "i>-m8pt `}/y)ydYкR\  01N2: 틖:l$d g0~f Z 0:JwWxHw7]t@%21c ǤPq"8&rG(_T+y&N|g!6箲B=G @0?l9Qvۖ'_}2 (!?N^]W Fֆ7Wqܙ7L1DX?vᴇNq1k@OaM"rH~r,{$rK9j3| f Z.-bBiznoxX)YNj2]$=Á8:q[P>vۋ(M$44 fG   D0M@hy   M$44 fG   D0M@hy   M$44 fG   D0M@:.4 @>4   $`*r~2xj Ā@kuk{X\=\er 9@@:~3e }ۘp")8Y!@D@n0 Y`LSkX|=4nr)y"MDO7㧖B @ lb@@FڼtX"Y8(T1,1[0sf|SKJ~Z5`tM@@&t͛g_kt/_'?'%,NZ?n **pS#@4_⥌R8h9[x0*lHE.*x5ė4װiq:Q^p ȉ [O9$N|x-$^k?  ):ʱPeǚ&ǚc8 4a iOT#@'fV}5Y44p0J@NDr'':=$^Nՙa  0Ͱ*w_|U?)+aә \NUzrȱ''sp"^x_Zf  P^;'kYy98)9c墾h83Z^c몄/m9i%(Ԗ4;e ir"xb8Ć*o+fjZW}('Syc@@>rZ3ybCQU>U 9@@"j?rB>d2\D@rѦUPJus).P >*BY|VjIENDB`il32̂î΂ƿ@̌Ӂ ̋ւ ƺdžՊփ ǕՉւ ǝ ɞ ɿŽ Å ͅՀӀՀԳֲֶ׆ZՆ  نvfihfgjmg043.-5 i100/.0*Ui210/.Qi2110/-1.ai2110.12.bi210/221/ai21//-+**ei2 ^--,)Xi24P0//.,`i2pll0102Mi2;75210i2@R_R21bi2A2112ji22ii22ii252ii252ii252ii24s82ii21y2ii2Q2ii232ii23G12ii2 ˜22k2il2 ΖN3262ق2ll2ǀ2S@222ll22o~242ll2:FA2lq2qm_cbc!%$) a! "Ua"!  Qe"! " Xe"!! !" \e"!! " \e"! ]e" QRe"$B Xe"eaa ! $Be"+'%""!e"1ESD"!\e"2"fe""ee""ee"&"ee"&"ee"&"ee"$i)"ee"!l"ee"D"ee"#"ee"#8!"ee" ǔ""_"ed" ˎA#"~'"ւ"dd"À"F1"""dd""ct"$"dd"*72"de"eis32ʾ ɽ ׁɻׁĵׁ׀Ӂ׀ց׀فׁ׀ځׁ׀ׁ׀ׁ ׁ ׁ݀؏jUS__6323A^52103@`521036 `5223/20 `5223T4113n `522V224` `522C225^`522=C25``522CK25``522r25``522z25` _59OpI26_ _55b5}IaV26_ X2296V:8722X`HGHVT'$#$5T%"!$3T%"! $) T%""# ## T%""#G$"!$e T%""I""$S T%""}4""%TT%"".4"%TT%""5<"%TT%""d"%TT%""p~"%T T%)Be;"&T T%%V%s;UI"&T L"")&I+((""Lit32"ɁƁ ÿʊɁɄȁŁ f͊ʁʁʄȀΕ˅ʁƀѕˀ̃Ȁ ¿ҙ́ӔЂσԔσ̀ ԖтЄµхф҅ЀρĶց ҂ӂҁπ¿ķ֖ӄрπķ+֜Ӂҁπ¿ķU֗ԆӀĸI֘ԀԂӀЀķU֝Ԅ¿ĸfՆՈՇԀÿĹ;֙ՇԁĹW֙ՇԁÿĹk֚ՇԀĿĺ{fփՅźqփՄŻIփՄĻ+փՄ ƀ żփՄ ȁ żփՄ ʂ ŽփՃ ̈żփՃ½ĻփՃփՁؿօ@ք3ւ3րŀ ǀ ˁȀ Uܓ̀+ς΀ˀ¿փЁ΀ˀG ՆՇԀрЃ ·ڄՆԂҁσĥ߄ՆԃҀρ ȦNՇԃӀҀЂՆԄӀҀςɤՇԃӀҁςS3քՈԃҁЀρIց׃րՈԃ҂рν<ހՈԄ҂рƣՈԄ҅ʣՈԃӀ̣ՈԄϣՈԊӣՉԆ֣Ռ֣ֈՎ֣݂։Ռ֣Շ֋Ո֣֋Մ֣֋Ղ֣֏֣֌֣ՀցՀ֣֣֣֣֣֣֣֣֣֣צ֣إ֣٤֣֣֣٢֣ץ֣SZZS֣ QcaacW֣f_d֣\`_֣ `Y]][\֣pcbl֣⮪֣ע֣֣֣ դ֣֣ק֣֣֣֣֣֣؀֣׀߀ ֣ރڂہ ֣ہ ֣׍ ֣ ֣ ֣ ֣܄֣݁׊֣֣֣֣݁݁݁݁ف؁֣݁ـ֣݁ہ֣܉؀ٕ֣֣֣֣֣֢##mU/. --,5BQ^]m/. --./..,+=X30/.-,+)M@0/.-,,*,'O10/0/.--,,+)(Q10/.--,,+)(N10/.--,,+)'R21010/.--,+))N210010/.-,+*(.I210/.--,+**0D21010/.--,+)/0@210/. --,*./1;+210/. --,+-//16+210/ ..--,-0//23I21210/ ..--,-10//1/9210/. --,100//1+ M21210/. --,1100//0+'M210/. -,21100//0+3M210/.--210//+AM210/.-2210/.,LM210/.-2100/.,M9210/..-2100/.,Q$210/.2100/.,U+210//.2100/.,U210/./2100/.,T210/2100/.,Q210/0210//.,O210/-,+*)('&'(*,00.,J210/..-,++* )((%#$(--F2?}ٹu, ++**)((&#$)C2;--,+**)('%%<2Y-, ++**)(&332d..-,++*)*.2\.-,,++*+12>/.-,++,A20/.--,+*Q@23N60/.--,*2ؘ810/.--/L2O>;9851010/..-+ 2021210/.0S23I23210/.24`Ӌ210/\;25k210//2P25o210//25o21052l210]$248:;;>t:;:621003\-2=߰^42103R2=212D22172r521026212m52122/21121122732231רU224221003l10232247p34543225o225o225o225o225o225o225o225o225o225o225o225o223j22{7227r@225A22622h22?3224g}t22\22  22!F22T22 22/)j224ZՊ522ނ6225V222242/62262f܇2422 522pڸ6262222222x2422@j242B=324ސ25223#8422Kc232E22Y25223[a_H2L`_\i2522Ez>25223Ő2xM2 D2522328632E242 Dc2522 3夨2326452 DTT225223252Ut232 DS>j82522 3⛟w232:262 DV2f4;25223Í2qQ32DW2:2522327542B252 DW2552:25223242662 DW2322:25223242Qw242 DW2322:2522324282 DW2322:25223242kdBA@<2 DW2322:25223242A󶿀D2 EY2322:2622u232S f232BL282522=R2A w23225gw232LB222632652452362222mU  '8GTVI  !!1Q3! E@! G! ! I! ! G"! I"! ! D"!  ?"! !9"!  "4"!"!"!  "1+"!   "++"!  ! #%$"!!"!  !! "!9"! ! " M"! "! "'M"! "! !3M"! ""! ;M"! "! GM"! "! I9"! "! M$"! "! M"! "! N"! "! K"! "! J"! "!! E"! !!B"!  ;"/rޅֳk9",0"K("X#"O '". 8" J+"$?'!! ! "ӏ("! !B"A/,)(%! + " ~"#! "J"#:"#"! "$Rώ"! W'"%_"! #F"%c"! "%c"!/"a"!U$"$(+,,/i+,*'"!%S-".ܩQ%"!$F"."!"6"!"!)"f%"!"&"!""!""a%"!"" ""'w#""# ~ހӠG""$""!! #a! "$""$%$(e#$%$#""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""#^""p(""(f1""%2""&""[""0$""$Zsh""M"" "" 6""D""""^""$Lҁz%""ۂ&""%H""""%" '""&"Z}"$"" &""dֱv&"&"""""""n"$""1^"$"4.#"$v܈"%""#y#($""=W"#"6""L"%""#zNUSR:">TRSSO]"%""6o/"%""#z"n?" 5"%""#z"('#"6"$" 5Wy"%"" #y㝡v"#"&$%" 5GG""%""#y"%"Hi"#" 5F/^("%""#yl"#"+"&" 5I"Z$,"%""#z"fC#"5J"*"%""#z"(%$"4"%" 5J"%%"+"%""#z"$"'&" 5J"#""+"%""#z"$"Cm"$" 5J"#""+"%""#z"$"(" 5J"#""+"%""#z"$"_X321-" 5J"#""+"%""#}"$"2򱺀5" 6L"#""+"&""j"#"F Z"#"3>"("%"".xD"2~~| }l"#""%[l"#">x3"""&#"&%"$%"#&""""l8mkj/>? ???'?2???K? ???'?<??????????????????????@@@@@@@@DDname icons8mkrt8mk@ӰGw D ~                 #  +  6  B  O ] l }           k    $    d     +  f                                                                                                                          &'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('&  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   fritzing-app-0.9.3b/resources/system_icons/macosx/mac_fzp_icon.icns000066400000000000000000010620441272363601300256410ustar00rootroot00000000000000icnsd$ic08C PNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o>IDATx} %Euw+0l2 " @C#q&jӸ~Q?5&@р(affaay]sou߾vߵ}[U9uT11 ?x jD @%l6NĊd5~O K55CbyVtti,iffXm8p25%cbWW_}u?HMK|sSf̈m޼Yر#wڵ0#;ĜfyK`9S0??Fl-c1G TW\'=0ۿ'׷+ݞt0ڴ7[֝+Cς?qc>([^ L [z>t =Jmv*5RxNtPLb*Mc1Poޑ3:0ao{u/MW\e2`4$@ D 0):0(k&w("4NBYLVu)o+Z!% cyv,tge<'@;\>wdx5}% .4l*FZ: @T&b{Pi`|TjO?l6-3}6uo91!0p*Ǵwtx™[ xɒ4E_ZD%ti'L/~#fjh&Q >tDs. |9k*KYO2H&{!Ysamd6)e%D]0D.6g9v GZYp94ȗ lq,|Y@L){To";tY`vP%`--GaFzNt9oGPnhuJe'`a@1?vX:,@֯-S„vb"Q D0Fleb"*q,$px6p=,xp `} JŸ͆XB4>d+;S̡.a0rEwS4xiG@q^ M`Np\wϚ7J :_V pEF fAܨu4W>6O1_c|/!f:j: )ob&4̚篹N v̆@%&II`9̗cWrn>ammL -1c6xekf'Akk.%git m@d !v/_ 2'h :riq}^k`zG gl`yc\bbg<bOBQ5ʧ`o&m-`cDw3*HqH1 si8H0>p 'uUv0lpqh)rsAox B֠;)GȻGFwnͲsN}`& NYEe"Lp@o̮:5csfJ#o)wch cx˹./8S 8RIs9}N7Ow/?z-%4 7$*o 0o؏<+9H=/10`.?dM4ok xPmĥ Hp :nޖ'wz)8KNluc>J4>'[ª\!@wDs&lؼk, `,\%~ Dx8m˽P Kq0mR(^xl8, Ot"ԸLf"Hh"gV1'JeVϵ2TKO?fA\60.s2pb ]mAlj{MOLO沥KyM6[Jh4Km xqY^0ppZG)@(Ãgt 'p͞/\J |w@@׃5"2V}a4.hMR(+vڔ|~\!D~;WC9.7[^?^'e W^,Z+C"&' ;1l#=+2l`Cy%nCayiFs18R]?DD47ml^wDžyƌxv@o+uPRB#5U6F8'Gp"VrYaA pl#6 W}gNNEGRa۰i*s]`A_S2Ao8Lg0pB9HH0H aտ~ ~Ѐ@cߺ4 .#ֆ _Ns Nt=I9t|l| i Y`s;c54ܶ"m @f;f G_P&f69e%ԫqjl4ի`0\j+2?+de!#"SN08|ݪ eX_KkPZ5 VEµGNmX-Y2+YX \w9'8 `60kw@A^]5Bj ]\˘8.6..otZ~ l[B#BͺEC4 `9Zv18i/K_{l$!XaZo, 9r^˱3N /f:܋bmg\v91%̫Xڍ"'Zv +° 0y?./ j⃝؆E >qDT1)!\ ~,DN `\WR:ʗ Қ@lҎOӞ 4yϠQcݿVag1`cگ~eQ֦U c(5́3r&>^0B K߽;:q%f:()!TyϾi,|2Ϙu ځhtn [8(a Æ!FN>L`a?[zܺT,/5zW@k=(p\J Mx`3ȏM= '9 I8q%q Ns|j0.g3</ 2x {'w> <7FWi :/ٛ9f۴@o>5={Yc ڍD8mXvU2m>`(ñe1γ۵WJe6Ok6\*98v f}xlٱs޹yeKeF~;kVaa"ƈP/~/2HWδX3 iJ9, 쭳oܲ}P3XMV8KJxp _7Zѽ06! jy vAPĜf NЕsx`"ĪCCf^1qUUfL+!*+5J26b 1%8xvޖp gA {elnX3V 7C^RG-4Enj!t}7t>c;g  ^?}=V nZ^bU]&} 2je^y͝yi:y٨bǖ_{!LO:oF1+ B^Ȭu4ۆjy딌6S'~3K'`ƻ.?獴YH3XEe/|Pl nv}?Z4*rxϑ{im2.i6!m'p~xi'@,"819,Ҙeg$Jс=w)YNY)إv4ah N%/_'8 "aGeX|nqica  >'(up88i B|9pU}کJ3H` (&PMưE~K"# Џ^s m6_N7 f28,V}ۯ+* ԀIV5TrN۱1X,"@K M.4e\;u=TN`57}sn#1CW-`]d᮵tX1kuݹNfyg2;Ay0}R,<8|p}0gp;@Nv=B5c?{~b:FB\VM?+O^b -L i'0q Ð'&z440b x!0 t 16dx\S`_aF2JPE{gx3TMtHbD`8XmquCLofF_6~ANsS9Hp !LL&drSS 8W X+}hũTֈ|qԵD ;ԕS3€7ǀ̍_4 hӬtÀcjx 28< L3)0a:9CRIONa` ) L5˟~ g0N3N@`v`aCX '8g[R"PHSuu.6/s$DkcpBa'Ke:&tړTs頫5HH#@ok_y$gPѫFT%+xW;Z'k!NC&TŇh &QLVUᇫ#QCg􌙴P3LUVݱM[JЯի[ ʀ,v ǵ>Z 2 "G0.d\gUc`C8A :/1Ü1X N:MM*wq#UOLGq)C%0ٺ~󹬊z ꕟަ~[䈒*Ζ/5rqr Gb$CyfS?yfa#W e8+B{f} 0i`JG# k}0rZDki r^U{ԫsHp+Ll3CZP;zƠwfGqg_<38ڵC qXGWq؍}p}G{*6a'hF{4pj`din[q&,%iC pـ Dff"qg0w}vh7!iGՂߣS)x$jѳ:GwHtO\1AOa(Q  ay?`I>:!u|rh6ænܱ=;2118:>.ęk?CM .!1u2g6\AQf8Ƹm7gƌc!S!a9`S!H C8NŁ8::1:LupFb4y=Xs֏pctOh Il#i3<+6r Svd5qBzոbT<{݁]OϏw) `K38¨ 4}Tj<UNHvǶc Clj0/"  dSԔ/\=Nmz9k#JL1Ə.OHk i>>k{McxAgYKX"ݫ[J 82}G <[xhý1p:86gr bn! 0Liߟtp]>880z)`G@eXIGMvX >(9A@uktKkޗNA t;pc31dFĠq&Rh<B=;&̼K>EFT2skV ëtB1Z&_|kU' wi6`gB6I 6!;LM0}@z]|>9>$<8u#8Bգ;Qt8J4{{t4.``8 1Q;f`LCFopY!av|.5e׎o~{ʂq<)&/Z@ k/F`mǾ af\cp(1K(%'2vxG³&1pOًN}WL$GG*v ,5ytu臆i++_"`se7ׯgX`}ՆO=址vm=gk(to>Ge^٨@]ep3Wb!Ẋ*1  <1@|t\KO e"[5E#$t oh0%Gsa<%[c K TفP{K#d6,р <?B!vg7g|.Wy`ƥ@.$r+3zSM:aq. _{ xq[{WY2tknx=?`Y (788Mi6H LL; WE' ״C1f .8%3-d8y H ~ջ?N ]Nb-ǔV[qKNgCq{VÝf5#u>6O"1tbC`g8tf<, u2 p ul!9 /rDo⹏u,צYh^{ܨ<;S}6zѹ9φ1Vvŵkiϫلk0k~{LfH)4@̒S3u#ᄏ`̳g UK~hJ.178Fq9[S/;|V8HtCљ`*ѰLS2ר-q@iĨ]ktt7#? [fB!$re`"PH Դi4S+[^$lh夵 8vW]k'@ ";lhvs|;@ qϺ㎟4inkLv )ҋiH;^QFs#F9kUWۚ`Ã:[Y6eV{m: gnXtĻ73{HqI2ی:AOWrU YѲH"o#\/*&Ŏʦ4\_"KLuknO|ac"D74Fx`dwi) f+Off0uK\g,"{Rc&&r%17qD pFV@4!R$9n>X2l \ϰ"V0(6boG1[X,w 3׆g "QF;  WVcC÷ yṼ=;/[n0c?mhڣ])Fi`n*S TԝsSF4h &U2ZmI71YUHQ[$6u5U-#h F5WQlTkb+wc 03̓?:u΄ XQ$l>7zu'` Sy'0.缗?AQۅϝ=)ӧc|xY-/mvF` gֲLȹ!) !OS7FGxv< 3JlP xs\{-?#0g)CJx\^o}vy%|>޻g @RU5>hN#ZU/}z ]ܘw<*T~0.il'ixL۳pJb@RU7&hȥTKs=)yzͧa!Zcq(l#]NC} &ޘ_u- AZp~|rTշhwy*50>/L0^(H_ʏ+xStglR7Q׭S={ Ɔ7" C[-HDEc矵GO}s{E/`g_1gv\6[*+^A@ۻws]j5r3/@3@ t&݂s?ګB՞>]}ۿ)C d cם``* c9J$)0M;cqԾWn_zhr*ك+|ɵ Kf ]#NQ]MRG3ozϪpoA틟P/ea+,bRHGA7pB{~:FW`7c3y^pYjX%gQ e*W{~eBys^ocݫFwlU;s}'5lhVh&J2czl %K,NO4,rwKN&Uf jU.9(szK{ٶgIp{̾}t'5i5Btn`9G:ͯ|v1Wkc9{fU Gfz}eyi\0~x.A.;hup=8ՕAk- R hĥjb͛R*K:"g nZ;>|7)]@7fk6ryᝬtNE&j6>^'hbueYr0LPNX 8 Zi f;;9Y% IvlssZIGRk5 +ىnۍbg-p1c[;֋pCr]Y<;272fޝpd=/"6Nj.,@MܦFy8( x֛gl8l6q%@{HkΕx 6rNs8Yz 0b DCD@+5 ږDӀ8u#hRRh bqDԀ8Vj[ DL"!"hJmK]i@@:D RZmK41 X8Vj@@+-u"qG4J h.@4  b"ZqԶ%DC'o,Ħmk4 5znn-?\YxRqhp^F?& I_ spɁj7nULV)ǹ&PE]C䃡soުfk*1a<[ut:UjK-$M:GcZ'TId0?TD,ݫ}V5=D-4  "B?Ob-OԬd 4FpY{VdSj t;wE54)0 ˏݥzA+ոNV*uv51~ƍQGٿ}_to)B:l4Yx5{,6~o"dO'hNd*I\NgT^ywR`ܙB} M4  bR"\rd5յ#QaɩhrgYjV`tw1D[7]yz?0Mןy{R;_|Iyޡ͝r!1 ?LY o|(g6ʓxʍ=;Wo_jxpDi5 Rj֑Ga5qt~߯_cX?0#C:QN6<W#櫟q_dpfm{QOj. †W]'ViqC<3Tnd)W}ڻy= 3>!˫ګA(2% xO,::XJoWiV0t\}sC,RUHi5  suy?!x>&N |K>C', ֓ S%8Հ8v 1V­>yv_?ׁ>摻]e2X O J\ z߅,w C89uA!@:=z>qKbJ9^wwɳg jrf~nP{-91ӂX/J*qi`hjZˮPSgxdžGs/#o>CѴ k@@u\{ t=?ߩ8n 0c߯RdTctyq{J%n7B7wMpq48Yݿ/yPgA<ן6$t.ISjU>/z]N ĚA@: ӀuM5a?jz6ԗ]V>?߾Em|z= <2[zE^QSYƎ[1̾j@@s[?w255B9>ձgWBfq& k6(Ea3t5 m*ЙvjdPwl񄿟Ţ0m7.DJB'k@@'^Gh҃3r<[ +h@@D =3ߪ| 74oC>fڧ5o}9#oDZJmY Kok`C= S:{2:$ڸQ ([x277I-Ԁ8*QUN3h‘׫W(O4  BFV=x9Oc՟߮־r6J^ h}xV鼺 v fR?n3}+ "q얀Ba1lF5vY Ġ}${nZsRuGt!>";5E;խ:sj[PG,TSAT_bQ2v8Faڻ]cthČp߈j ȁQG@ ]rg֡EGԜT#>}ڷEv$|5 kjh`lh@gihW|"@Ѐ8JрEqc_ UEcCF?K+E@ji p[ު&O/mmyP; ?O*1uz H<{FN>]|χ!⧩4N%Rs橾O >&`gWɣ<=CO6AͧqINO>wK~OZ_K삜?7 ~F=-TSua0W<w;Aa]*u90 oʟ\L8d3pq6m.7C&a%WJr$qeUNxw-_VQK@ᕚ=WM?cy! tTyV~hqᣣ&S/UЃhr 8!4mBTdPɠ^l,Ӽ^YxMolm14v }Ez‚wxJAu+ɤs5 /.X+2={ O ?Վ:noO|UwkE&ya,޸(IDY}.8}ù.dOZا 펫=qQ!jtK.dOآ>bZ{̊uk񝯩_90~k/ժΎ@kƻtz"n^T F?"9kNٲF,CT3`0x=MV{QOW~67R h_'`D{~Gӷ69Ȅ܁_z%m7G- '9Ȭ'L߶\ykoo}'}1{Gm-1Jg\uٴQ PLٱOFǷfУr[c:Ԣ4ۗakoFXZ": Њx!S;nw,.Y() ttK Ee5 j@4¸i@~qih JD@Ǵ_CAPNdBI,SQql3 ;$IXo{2VSmٽ{H\OPl;(N"I h a#C58pl@և UM:䍊$@,>#`@[CàYi0t(AqH "|X@qx=! `E@iPCy@51 G9&׆(8n4mH4 xDj9 0Nm#G1ˑo[X;]6IŢs3Nb۸&b[&ex%+hʍUn&(W 'cʔxk@рh@4  Dрh@4  Dрh@4  D@5 6RIENDB`ic09%PNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRvf`Y.ABT<ň[h"Q&j bf]vvwvvvfS5gU{{}ߚzz뭷*pB#ukɦVL7^33\YTOmoQ4gҞ4o#\H 3h@ ]=UI_J:,9 Nexk}J3s4]KY{/-[d4>5\oa?(|@}54ڬzK D 7: .b7WK\\.QnwI՟;-UUwuu Jm;sE6>`㙝-1\J\[eh]fa[*wɇbSM2/N죄 '+ 헶8hW(u^R6{<]2jiC-Io[8;˗{Gw/fP0}Il.H,tF`ל&/;S崭e$mV/naS WuVUCTL(皁\@+su9rXwWߔp5~}OZY9doy[Y^_a cccJ8-Ff,gfȕRr~<YlWhrfZCސS.MGJ*KXeͰK,'68B _+7B.FJʼna+bz5V -C| r9fXp22jU/{:O,UɦYLL:f3h:# RX*&i#k{ ̝GskL:r5! kBvM;ظIuhF}ǝBěGz<~'i=MnL!{fm̼T(^+L{cLyMuc!igflcنZŚ $~f[Ͱ`E}*36e!G& ]wIo_i)ƾOR>OLDXk2Η9F / 7duFwv< ʨ41F=t?v# ?p?8>ηXZR[)e?W|E-/7LQFfk:4i&UR&30U<4V(ӗ0[pn`QAdY/:_8,K[JPn*\dkYPܟTR}~_**/yz{-싨i!{i׭7؃N{gߊ̫ 35fHkXhHۼYecZi}͔$y &`nl3|JjCɄ9ݍ#"/B|k)|%3$_kxޚvj}k̇|\5O~): _'}M[ߠtW}I S a*nn=Q$EjHhRZ~S@dTȬ =9Ja[nNSH%`A'b&kJkYN|~N~.-+}>E;O<&@ Ƌ?D6YFfx2.rKSyRp̺ۢL━Vdbk߯g_~2E$?VQ|lVÇt@2x?3(y('@˳,i~<?V~:u gj +*I.ۣxRg OlJX2r"+am#c&QܨSiS/x 8pZk#+y2H6nˆ \,\L+>w6l^WXg֎)~sK:t22s7Z[M3u3 ggٯڝn`Eri`$h:st6C˯_}k[Gi@)ǯ[Υe' `-z M;[FTv,L1"#]uY@Z^RnLQOE{3skfJ@}MOc+%l>ʱQZ>3Le 9,kF)y.4O݇*r:Qv{ kH7X[6_5X|@P( [R^{$iQMhdKXjȱ.y83]i] TL#˘MfR_;aj_+jS,aWk͡Tifl4djeSBfir,,U^-yXsap`L@(L8wtbo I1~ێu@T*Y)IAU}cY5hZy&'8]rˢb5.> D;:F@>2q-]djU^\}S+ L)R{)ixgj)S*+q՜Ȥy+/hv9"7`$wsӾ8SQ+_X5kZ}SBOפ.?E9ІUTR`$e.Pm\VMJsjqMYN53M ^^* /nibd n&Ein*r# ÆFA{Ţ ͻ*4uD`6+{]g%WZeGwT5^dBca$t ۷͌DL4s[:^2}-iGTh=G! 6+ߍ&`CW5Kazdc) ÊN5C`b8_?JX )k; TOaT#EUۘ!]-p2|c)MPfBE>Q`~ 0ijO产E@Dh xd&}jc(i&VOӆ,IwjMŹݔ33e롣]oYZ&W I Hȣ3LlDg 39BƋRFC~n6K יAZ]M_Ò eh'eMp5:fRS!UV# |tCۿvRY8NtH`%V_z؈"7[tgWt̰Mʖ |S:pl3@a|3k,(s[WmTիڲ  K$,mvK\*n'^IL9MW쯖1MFSMjvPP/S .Xלbn¾@v Ѝ?RH)v΢L&mjʄzQ%. ^KX$NMNIL+-{I" Wܻ1 yr1KHUƦĿ-$?VPUy/qcU7!m4ۧ}p*5lٙZƩ)+c]C;q(0 @d#o]KɌp(MNZc+hWjn/-8 rFiqg[%.T2tcSFuMAU~ PAT6Kϔ^J1?_fvBV'&hGE#mGC;B6x3V}Շi  0Vi Ui,Zh^Sΰ} @=l?[L{mha%Pd5OlSEEfO43oa(+G ɕD3yˆ>R9ܼ~mDltqhP#WSEhI7}BrFf "P\{੕I=^+ yLhdι6IDATOQs*N(I?xoI[5훾x[ėTe/jހ(4_Fqd7Mm=Fc߱r=Bh{(}]WeCU_MWneUƌp؝l^ܜqqUO4HA7 #)7kMY5F9y )=k ~4r}:EheCo;<'8;)#nS߆7j(%A$2dyMN__mS|MskiXGf9nh5@ y,EKkSY3. a~/M_XnawpXPE$MHxZXeZ{My3dQWhK LeN|2t'SYgZm>KU-agE hZ  jܔS֩%)E7W*L01h@rVJTdx Ƈů>W_ɱWf*_|ratzP?@( RQZ#U٩9ckYk?*lcK$$468@@n TR)c*,S娾)B:l2H[iAUsOӝߛ o>HOJTHVߔ83݌Bl[4N}7G989gI'fr WP'H+!SAiةLEfiyfk!Զ43WY|^rqhU◢5l*|3[l'f<:@ܔ*uxŹk%PO43MyI3w@CL% _bƛamĩWy$^I3& A@'n X[[3T^pU雲fح(Źo~ |`^L3S D9835%,N}3Y.qhޠ|gA#Nh/ 'T)=tUFTN}eHX̫q"S3#2Ue 8A @%K#Le/ag Ͱ37}-KdU5,yu1e%l_qCn i@h 2W驼vHDSJ[>xg嫯kok~x/H@GS߭<5]|=6}ͧr&nq.N˳eXqi~-52ZLgofZ  AcwhUي/NͰg3ʙe/T>)7P0*APl6I5t 验5M0Ú׌׸zgn5 ~c`4  jnJSTZs+ChSy55)_awx  {L75F媕ckf'LQ"D(ZSٚ^tTAs1RiwJeʗ ,B@!0ah3B-4דּF{7mQDfhL'J4?61ECH:@Ac2B>~S|_m;8nSvմߠme4~m@h F *h!&w2ݿoB+;8:e)KK."n}{qcD{f@b` 1CvYQ9K [h_ud! +E0xeXF_\ @[0p' fz*-jY*?9k b#X wv;ĂfB3hH{a裗ٯy<EAZ/Ь%X+:1" F+종/Sfk\Z[S ͆şgE `!!4)n8%p)eW6ѱύxmX~WYPg>9!!4)|?A?,@'w6 $AF] зj?Z?=I{.w{gժ84 6SK*";76k#( K?W886 5*5Qn{9wPϾy>~2pc=D (J 7nxp]?FA$" "0B4hJD +}׵le(Z=X脃c cB3s) fy 0Gu1$w;>_b@En/K`Aj׾@ && AQT@%E^}A>4d [[sP%]:~y#`XAT e.|$(e˳V&weMikL6}wӪR>Τx2H󗭰fKi`Q )tM{cF* A4qc݄ˣ\Tϴ899X~][ uyNg2~?B ^'m9{z]@^JAW}yҳO-Y# [.Jh pA\Y!%޶+ eCGdQ e>6?n'AG@ -N/bC?0dzF06G`]CE `T,.jy١/('FGzTgP'xXY_[' +]<Fj"O7 J_+K8VMߗ`߱(#&-Lo8 ~WQc^j[i~UǼdBA#_x}C I/lĊO}K_F]th΢Yά]m+2_$/&eoZ vh!{!N&$64:,&'&i􏿷 zo[l8ҏ=9:WfwoTOrc%d qg3$vk?Ӵ۩8`&f @ ZV<'1'2*/]˗XGkY@`@eR֔:יO~qm^=hdh,O(i~G9%?0`}a0XoNOU<0̯m$pOk,KSvy0Ƶɱ넙b ؼp?v_NzvkCޫm:et/癀Or卄zY/"W ,/_ qcM۹-׎ An@YiW; h6PlHs8 '79%]J8pg6FROT" B@?wZvEo+{Yi'"7|Mn (C' ϲ ;wOngpw[9؏u*룉JO@Qc<&ʯ{t}^y=8mtbA APF'@atv%=S7쁳 5ákdLl80Q@Owg3Q&o4r/oGp a  6Ă(ﯿb^*LǢ_v3!Qz߾j͢D> _$vw',~K+T yذ.xޡ ,͒C>p%SܼmG{~kNy'o1kO['r=E !! $fĈ? R~z*R -'֮-_iq?F'7'04KL抴}o|F~(M}e@Dc@ 8ذ6Tbn3oxmm/H v0ڎ@%/^F%Vʼn1Jhu(Ж.`=h}BH@${OhŽlh;\QCqtmc4|XS U@#:MFYY(FwP~FZq׬GؠyMM GO $e#Y4Jy28jwjG:r[6.=P;4F@c owK8+"DfGȍߡg4g@k'r@ Xި-wEO]wKDl7XC'ɳ;mݴ{QO`?N0D_C@ l?XAE/4hry='=YAh)6>Ĕ@Yy %Z iE'ӂN'z <*c~6EEGR$41RTG-#|(-zco u+bߝ(~~_,P}v}Fx {Yc1@ F5 PO?6F{zyiiVPגeD2#ZA)Y@BȯH4>N-ߺ>n6grM@%_(# XOi{h)TfB)U,F@jJIDk|~0Ju{qu(Z{ [Dʔ6i3" q0">h~L l(t&M4r iI'Z) ꚿК9(l=.CC4iO}wSnMn}7 d클H|_fy䔃\A'bj'ٮM' SrG.>uO];K}VQC8doɖBn:ΒmQa<[X\( _1[8쁧-5Ͼؒ%/>ɭ{ֱ9&qrHp& Y~I3tf(Iw)ב 1# wp    # a@@H@]8@@@ `$pes@@H@]8@@@ `$pes@@H@]8@@@ `$pes@@H@]8@@@ gt9R)9n+9SC)nk&xQ󫟷ZQƹ86Fa*'Rri_*[J@3 JP߂J2+b%.%JsWC}F+:g)B@ [ojСqN3ꪼ4mir=8<#`_A@(ڰ&zM"|y୿䤥R٬1L ,Df)2΃Ax9 c i#+`` @͋&Q\2>q7T(;!jl z:=Gx-P+ %fg;Zkr@`4 9@N%*%Jua1Y,c'dvx&}8*xFuTX5 ՞s)Rd|Ο񱝭;bnZ@zTWP6ouQΛGY84;g-<<?X}7xBMjm"G'4^( 'B)_a~ַO?I0G7 0@"/~ÓfR@e7 mf4Pa6O9$D hZ J~Dno ;M}kBY{mk-/x @ @Gmj|%N' |tOc8L=`#|f $,Wo7_ /[y5[ZI?>?DP@`Uďeȳ󅋣 ֎ҟjB<D ]x31g1Bmk#M]َF@bN@@~ 7ᮐ>^.k6|0{qG>^!1Śs~ݻ8ۯ*+0OگbKLb$%*ُHB;&P뮭9*y.^eո;l_Zy7E%ܜqH q4@JX# |c-m*iQ}M4ͫYykBO&κ/̫OP*M91KVD@G (~T'' Jy;Q|쓗{bѻKD"X=7I=E8!IO$ ڮ#^|%k+zڟ#+3HϢUq#u/YI[oIH"D@ M 2O}"o KUhϺC'k$2$Gc&4Z$r߯^9m-?I[^ʹՊ-4˕$}sh@BL@Ml/<?ʳ8QS'y'Db B# b@䵿+2=s$/Œ_H%yd(p 8J=@[A}/b몪%Lw[u)2[/ *8Dm^hzNLe{RL|oΦܳOտJ,804!sW~폧7b>z 41W$H8s ~vU4Wzg]s{4Cyc k_> @G7o_+x͉R#6߿-FG :h!J`+zkfC ~Zҏ}% Y0:@}xA/,o#d!uav@48I'wYнtD8 h2tW]bmH@&w XXwus@'E3CNgUŽH}0۳0ٿB 0 z4>Nw';.-1(NLX<&-u>dZ`" i>Bu_A{(W5ΆJiLj8Y2.uH-_Aa Yn5~5Ǭ~L@^As0P{چqnrOݑE! UOeP\`ـVƹ D@}` R   +0b5 G@}   " VÉ΀@}` R   +0b5 G@}   " VÉ΀@}` R   +0b5 G@}   "D"Vg4:  ό`>Q   #`V 3s @@ ,]]!߼M@N<9A@:I@w-3C ';+Ă@ȵ6-0*T_O(̅#00zV_gގfDO7gB @ liyN14c`P8_3s!@@zw@u'/C@@k^k%"=AYda'I9sY,*'TӲ)k   PA@ө6o ,<'7F-KTNKX\+")rPM.^Ӱ*|ǚ]97 IENDB`ic10!#PNG  IHDR+ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx mYy>J=I yVB[2C[[|ъP[!B2f`=mZP $E%URJU[{=s}=oޱ7k{78#HD HD HD HD HD HD HD H?W% D HD`dc 2tsD 8D HD0!a:[uO|D H#p5rD#HD Hl#=ɪU4XD Hv^6{{ͰD HD  ) TOeeԺ1>D H\orM:h$ID H-pS/{Nq4Hˁ^6ɛν|q9SΙ$@"$`[a]3cz齦U|?D H M6;ɵnr&6֑r"$@"\Gdʘ-ɫB~@"~7η~$@"${@6R{K;>YE},z: 4Hˉ~6̵̫g~N]"$@"ll6O{,ӹyAaK$aA`y2;LMJ>HD H6@6ZGtDO`yno:wD fu3gԹS7;3Wm>7eW%$@"${@6U{J{Dj.Nj!T9_>л.D H جŎNUyqq8,Yq)|n?i,۝_%'$@"$@ ]w BcLF4gq/rG.}._u9.QȤ6uS'N/ۗo>TMwKN_X -[.N<[pԹgN<}ĥ'x.\<ϜGxG?v(|3@)/C:*MVY:?etcoO5>bS$@"$+ pzt9pdS}mH5~XWXPP;}7Ty/k]Tm'ϟD9ܧw h_]7>q/9k]s[g* N;C:๭K_SO?pǾ>}w>%Rl>SmTe@QZ85(U1i#\1'@"$/5#{_GrG\^"oxyWhp]7oߤFTgMU!^+k:'g]y╧z|"$@"&R<yA,ypS;D}u|We79w;V^\cyc~x SlQmzbQ>cy_o*Nm4|Mֳt5W uGi^A&k[|T +zok+?uדO/˧pq{@lPBQ-%:磭:K5ӌ]gL"$@"bI#.ËF>u߾;;D`SxºsJ?Qn|1:r鞊Gcys|Qyuzn+<~ʫ֥7Bfs)|S>S~&G6U|M$@"{hڎ= pY<2~kO<;?mO ¦@a>ʮ<7ιegr@"$1Dkc\r~pY<2 OvIU"mpG|1[CO{V9΁EMt|fcEX*;IozPʫ|獚5*t^ȯ/=Gx-SWE_fW cv5ܯiev&@"$ ~EbGC} Ql[#_D @C@{p^i|uMܳœ%2F}}0 ƁO@.nuU=ޓW?k'xࡢ>W}]`O _Άϔ3e)'@"$G"sq+! N5e6J~Qc9߳sー %ƩQm5M2ѝ-@"ENdv26^6(6XKC—^OG4|⯹uq#ts)6ȳ JE^qn]c{y ZsXBPh~5yn>8(U>ߺzΗ?σwգ嘺+)}/5u6J.HD 8BwrD@{! / oOH5D?X.f+6 Elަܝ13=0^jp9y搯_q^}ߧXۥ'/>'Or讀g]L-hc]&$@"t#df7lz›$3ᇍ.Yb٢/2k~FF|/[v߲n]u捵La #^՛gL&@#~6Qu>E9_wýC?/p~p7 ^FZIO}ʆOD HD#5ӡ_H.`WS~L5?X ] >J*ZgёHdxcM+/|#uYǗ˨og?ʁ,^>!sl)#LQ&@_ya]鶮/>?Og3ށ|Jxh1/%ki}OD H5~q D^2:QxSMo"HNG?|]7Ɗ4`:jˢWac QѶ-mMs#L]tշ]5XIqG^/|_KcwΦdg}OD H%\!KFG/ߩ6 6Ve&J]#Άx3FP;0%?/n@8=*щ'D HD!F-G"0BE1p>Orw(&|qk:"A ԰rZ9ix5xM?QˁT:=Qjs} 9d0=G9Š/. If`N^yї~'{_Z^(u)BXsggwE~{E5+Iu,_Ի[X̻6Y?^Eu藇>֤zLO_bFJ`s_p{ȿ3..#B9xFS:_mkӤ~\"$@"pH;ɉ2umŀw*>|/=o )k6y&M(!tn?N鼚Jj90dLud~׶jc՘MykdaIw,H0zMrXsrHtҥSW+_woƯ{5{{|Z64fS6qA {̎ 9i"$@"ph_jm@v*Û|oh}@W6Vi&J dF~ja^79ԠQl޽yQ3J:54UzѬmcC㘲.8Ds#f~'Lk@C\n{_U\X=@JE|"ߓ G5HD 8dp-sr7@| ;Ûd6N_xNwH}F.dMRֳTuR,zCm 0^qFwq@<+ fS2)r]?1^^tйo`Y>"HD 8Qˑ]r]nfl"$MrRT ^j&i(}Ce/nY쬅_5*YE̵:4G JNc N/u^آę?7/帶z- XHUM3S Qr"$@"p/x!>N&g7\}"@mRkeo~jnd-똊_y5YvŰ&K[1ePPoF|EIŭtʭ&V(r\uG~_vۛox ?bzFr(tؒ&@"$v`O;Ȣ*^:}|">W+vb#݊~\ޞhXWUʃ EsG㋼*m7?QEպEiq-~Qpcn<~%݂5Wq^e3~?y S1['O=s 駊ˡg_ ;Ga+?Ee`&_sD H@p@Ne,CR>E9dl)P[n;C@?LtM{^{kڱ ~|=rk:h1!Cыr*OUoG1N1a+/}oy_ #ߌRɱ D0>\7k^gw>cU-@W+^9fۑ#'MD H,#Xޅ:(>i"pPQ1Xgts+ؤ7jdiC}N_n9M:1bӧSC9Qޟ W?׉Mc<,Nv N Kwۯ:3ovK9Ug(]K".$3Aܞ|"$@"pYP# e%[jcbdH x(PLk%w:V[MQ96&Ώ!@ca}}&J~($. 8pGj0?׉gM( /KqJn./Ͼn?Wn,k􎢮NCqqt'D HD! C5N.hvs@buPsB$^6,/4y=?c5I!4f@ϗtGApZ67/G#Mb{x5PQ.@*xmb=zOy2ջ<|;{y帲S_ (<-\M}kOD Hˆ@l{Ywp%%_k^˅6umn+)Mu~Sy6&nb;{]Ξnz=^<yJP(q,qJ刺|\1GM٘Kj?5c-SPEyO>y/T;zOqm`1d(| h&_菕ʱ_ 5⢼L=i"$@"iˑluh^5h5yV-zrm75&kɽXqWA)Q'rQeǯÏ{G/_:<Kq1G}VXO,g_E{/QC){z9j" b4x=T ڋn ջ5}/v[֫6CG󚿒t Y5135 ?ġ;][=o♏o|ۊrJ'7(!cHD Ҿ|'dnt*>FP9c~Мw@h\^4P[g3a'85dgJ)E~3ۓݗ᡽t}>z:l=ګS~1dt̬<קgVPȂN2ƞ y; *k?/xsGeq{ˡG i1U[J8zr"$@"gpD H}D{wRmYF-6k7M^6jҼ! r:9RT[7L&YӸZ;3߁=GשayHuW焯5g_|uM΁]:.9exzs,;wZ7k&z9WRŏ~S9W'"kvF#ܧK: PybʀFͬՃ^5 qH pWvם]П $lU:ҋ׈:K"$@"^#D A@(_MtQoT#hr[D1O^8OS9KMn[|3b͡>%k|]4c2\!T_}HaI/nF8m<}[~|χ(jml,HP*uVn8ܞ|"$@"'ߑ{2I&MD XD ^KQ9i6K<F{ [IO ~ ٩=e@x M7n!l9$s < ~F #8U. 0+Ve?)z蔃C>q(7,p(^U23WT]Ÿ.xrk[#-C_E5+iD=hOD H#߽9D H!@3ʧQH13ɳ; h$L&JrͩyvkSf孔n:5&’s}]^Vԥ9 u +JΏk~#ygS YCj59M`(S%B_ܺ;?M3<\Spv/C~qt'D HD`cᅪ%D@"$5V:jAJ><6v '4`/op{1g[Gʱj'\5jBU78b J,vQtw:|A׶@JMEm!09ؐ\G/\zN'Tq5u+^9}7}ؑ?8K_e;gU|^PmuE=Ë2GO}RND Hv@넙 HD`#ӈVyk -eAӺHIƌXdistධnC t1#%^1^ gt؜t㼲Lw8:P.x]dl ٽ?u3@1%6UPS{yh|:WJAz/}lxϝ_&%*<JIU|ID H6^T̓$@"pH &Q'9o*p7k_?~j2 (C3EU8:GY=(&ϪLux'KF!C9ʡfZ팕"z\(Wg{i}Z޹M\7{[\jp }։>~}ZTהCw8tl:I!]=]I9HD 1c20HD` bwOE~ۖAONzkh,ܒUIm]|CTTSkxxC^搏ۉ$;"Cr;7Z5m\Y6W qHp~m5v'?]_8ŭϾw|䍯[obZNޝKFO,$@"~72M'@"Gb6:lۡ*WXPVhw_?y_U|-q)H9F56s{@"$ A@"$B`K 4x1yCXêfq^jW&>xfֳ~bDCUƲUr8օtY ¶6Qp 0jb^_#;'w\5<kpWoQ؋M)s$@"$G 7vaf@]xq%]/V~L~cHZ%ļSsbd˜P7E=Ϙ*>cc^Ξx!0z`cu$z۾cmK YWxPNCw<\we/yEp(](ꅩ\d!'MD HvGw M#US/WA׫ &rKtXyrBes})zzWqU^Ψˋߘ(녙cmNElhx,޸6Ϧ 4LJ6xyStRꊺx=ojs:ݦ\ eCE4k[_ ohd|e@fnYO~C|_~Ѓ7}'@I9+G|D HD`ߥ;N@"h:v^Ϛ KzS{VX9EPO2mlେaHcE,SjoU5ǺsqNR G @ʶJG ŋx !/ni4 ?RTqP ^C_ןUxM6H5}\|"$@"2wʁ$qA9^G2\zX6Q;^]Z7~A,jѻsSBAXu=+kpۘ~PtZXNpTgw-sNwCͷ_r)vjVG}"PˋfyVtg}+̵'?G_7VC\L$ ?d{D HD`9D H+ˮ#niQQH 41j?z?׷5^ˋ ,gkc(J'/: _Q9]ʇs=ywu5<Y6b\7o:J}Q34'uk *W4/<-EMҋ]|"$@"0.tLc"ShS1m_wny|G]u?lC4t1>@v1]?h|w_l.,깑wev:g\FԫcP o$٘}#,At*?VC{k=Y>yvMtj0%a ҊUZ:/E9GЎ|knΥx=zЉJFYOk\5Uk4^+V_(lwnКb. 08S#ymN]tZ7P1O?M+owq -(:5:wu;_D H(G"$>#P^<*הm<::uк{kF_k^tl7X3eU﹘kJuQ4`#+1F?o&3J醳Q7IΏ6fs_g]JGw晧{߸-X+ˁYtsZC1ڐ=.$@"LL"$FR]z0^|eO%eJL:FQ!5l,[ض9e91|%~z\η̳­{QA\T]玼r=w&LSzD?\6f!<_o֢ҝ 7"DT*|ܞ|"$@"0C_>3E2@"$;C`fʛ o]FR.i}&s].@6hsy*E~81)[Ͽ##v<>.+s2?1$3"e nE#_y{vǨZ몃 D:bmj4el{GW⌯d6k`zހh<mʫ5^zDi|ݾSS|uן<=O+Y𕩮_t=:_؂lx&֙?\\s#HD!WA|i* /\غyb v|kDuF|/X^Ϫc=]<8z|1<ﹱB O%8h:c 2> ߩ1 =)1:1jo롕's|U[IcFY7].nRJ?L1s_\bnbو7 `R^ɭ4˞{RvryLӵ*8CrSŦw9Rg =_N9=pڨKw~{,f6ggiGWT3;(C~qt'D Hc~7H$*&cE}LLz^,~c1N˽,2ʇclM?cUjZ%nlNS1kz,q>5+ ]Njm+CkrT]n^u$7jT3_rh<5+ΐ?uzj5|{?6, h]毼L( l fg֬? h>?ZBOO?lQ=:o0-Uz ED H)pLO|.;H.~eNsMxNFYjZhz#סvϵZ"?ۿ I$@"o}4'JD#k9/Ԭ19e(_ кhxt*_>41f(#uuοg@^Q(o=<(e]e΋gk%\6Ŝ.,SʤOYڰ*;~kE|^9*G P@ȑ$@"c3$@"0@mUTj˲F{5HnsWEU?=-qMby'W^o ;.3D#{`9^q+cƂKhyt넛U^SwnɠMnő )0]ghPGP[>s* ʑ@"${~!HD"@3kdmݿzcᢉ?&(kU. Dȡ8^r_ G+G(ד}.T~4䮛{ڠc9x1/kYfP.r_~Q&(>npTcDȺpCԤ;noΜ8&.5};U^l=$$@"pw1Zr.5H!@S{:G/zcWczŬ3h<9z>ZY'zb6.mFki `k=1_QZWO9YSy <{٨.٨[{M>q (P?/ .H*@+2P^:|tϧ埾-?3g_S}Y9E_]'tqt'D Hc:%&@"YzW=9~QߓNs'S YC4QGLYmVcH5Ul2󔞣& /X"fB8I|Ur:JL< ZL%x=őX̻ۿᇟrho=+|1ϦF'p~J-i"$1A`1 $E+X^~ˮ=|Jv5>qʾ/_o0cϔ^xYۊxYhru  +&;ǹ2zlptt,l q^Rv9s\bjc="8|yqxR)/j  w*jmgoox/mh`MI%MD 818ɹD HA@]M:yVR$^TԠ&|$">/ZGsz=Gc50{eIt:?tf@a *Wq1= 'lk\{=p|l5Oy>j)Ju]ֻ q^qŷ:^mh@"$8'=$˚©um YiI)0K |b' 9A_|997oպ$kCxdxrMTsY1>xk/+-^luJ@"$8F';$NOʦDz?GuʧI5@ɍ E/6zd^2Ƭe|O_oݟƟMK~w~֌(1f_w?.@^ Fjo4~mۿ楯(! P@ȑ$@"l%HD#@:-Z,M&Lej_kv\sKFX y_ ~_Tl{]s-Kslm34y:3VsqUx.,st'yY@IDATc:U=t~+%r`@M):}:T%@"%u֔kID XofbHa>FrnvQ:1ԫk^XKVn3yQUYN4zz+v\|rMnl3k9KzQ7x.䯯-";>J&@ P㪾l<[ug~կ+*tZ̳i|:cO$@"pD/!WP[Ǽ]XQ65O1ȦCyΜ} co|rN(!4 wy ȑ$@"p_f.r"W]L qT&❺OqSͮ3!X/z'Ήo1'4΁mq>w[Ak>9 H=\.|Ѧ8|uMv yf\ccW>һ,^Z"A'!Pą *k|a[Ct1u4'Cgr5oNr7G6 >$2/z8 !3d@ɠ4>ԼeAI-Ccw$)T@k||x`[} $$Tˢn;?O7MEuC966Jl)U,/G"$C`w[f.r#W/+Fy,7r''8dQ?;W:xbC쾮 }b]Q2~hG7RhG\cwJ.lzE~l4jKy uP6=mt|cw:oKZ5 MqI"tH_s2S,G)Q(9}M<~ϥ9=Dd5ugUL̠wsk @'&@yrǞ{ϟwwX&0-S򉣧>)'@"Rr,;H'4H^:W۽xuyaQ5bYmDsCm}%ہ[7\8o(搾gc]9cqءlN rGǺyT[!xdy*v̨sL>Q6EoxXU;NǙo"$U9X*;aN2d>)'@"B"G"$>!U5mdU>kpV(Lr>TJuuxŘ˾ۮ<)WU<9+>%aeRKS ɘSx6*?&?^6|ӀM:`=SϾ{Sěqe9hE󰢮eImvoD HC$D H7~%NӂyjL4Cfs1ǹ&G|jҳvlT*Yv<:(>=_bE;kYԷZ7~Stcs)G\yܝUcClU_^ @kϟҐ MP3׼~o-!l"bN >tnO>HD!e@"p8!ޕ6>4y"5noopf -T^r֮sOO/pp*Eĝj@xFX&9!Y'Rs $sl׵~5풟*<_@Qەc[;XS-xW&@̑$@"X Q:$@"0MŸG݇ljG1ǑbFPR΋"T\F,ES7t||<w'\28|Ϣ օ} .#j[4m z|[ϙN;G{/z&E5rxg'@"r,3H&+ez#E_ZvT)kGLt S Xw;b(4gPpZ#9If>h&5k._8x^#6Z*s_'6k.fP| liM.?>޷rMR9UZdxQlqt'D H@nt5;zv4rGNAqO' _; wSMu]51MDrE2vρ)c_:σˮF?M>iA>%눍T>"O|3C)§G7vr6Q lN{sGWYv3X@:%cLW_%A& ,Wlճ jnfU2+q>kC<'x! pOT:Qe/Cz 6m{]_9L~t9hO s<8WeBu}bk1G5`׈+^|קGR\W#HD`}hI)HD`)~qαipx9*r yeL>`HS>ThRs񗌎E}3`'u Su5t82g\1hwyʇȇ&7g.,x^bSWƼ/%PM/.r3fS=|a1GO}RND 8<)YR"~:M +[t_Q]G)|d[7/vG&iCw2+f 95WY ϛKzk1=90f[g.yʩctbGQ1EUX"&N喍 .5_]2 ELp'a}ǷyM;5;:QU|ID 8V%@"p+jH˽o5rgpNa@>w&erc^k¨g9i U ԁ>VwԎ7S{^3%zkNo95}?hn3yXC;w|ϝg?sċJˡ;V ] =, D H@ns%@ 6QKhwǻ>/>oOů[7Žݛ6s֝7*8ϼlT5S6m`֫eϪ?~Yu=&]=Eϩ" ;5Q lE_6Epӽy?}7bTbnϐ_D HC@ne&B`8H>~h^QgLͱĹ6t#>\%6;O [1ǔl)Fݦ|hvG͠o |K6&@q_ҧMk|MD 8P:YL"UV:ji"VgV>q~^< g7ySuѼOg7s@=VͣSO#)~+M,s¯/sZ5sx5ēo<8ϳ,vN͗n yI]UvwB-z>|+>;^tהc6l#HD!d9@"p@+bB.[Ǯ+Jֲiдn%3S ǶbU`´maPxq9T/cS>t7uDdf ke~.Qz ,J5u/zr&ʧ$9D HpN+Gί Q87C<~8ymē;qPs_車W%u9{;@]ۣ^2:xd"=boG5IBjA'z(%s6!7ܑM 6ȧW?W~ɷ%&|"$8&'`,KU)6QՈ~iC1s>lb2SlPw=S{b >grG4E5z4_9#4CWËD?ONZW>mH1ǪvlSO='ǯGѩ56rJ'ӻZ@5jzțvjM{6ʻMu9lS5D5)9/{j$Xsw1s姿p|WyȯxF ?nu3g~>sC썯}C{}9t'?ˀW3:>u]@"$ L紉@"pXׯiz>ؽ5P؝׳-MDqt'D H=F 7L$4~M Y˴`] H~ww;GX3>ʃxJfcY:vS߲؈c˧򫩞'zgQAy0$bNRZ'_f-R)~w{o~wQT zGQo[Oʉ@"${:HD`56Tҡ֋R8I~< W#3M{i7?b=SŊD]ԀzHFfnri>jzmhLm{vB_urAU!E5˜ǂ G63ks|Q ׌/BD(3~/\u~o?7")'@"p C~D 8lZ$C{{9W49]~FaF\ TשsyΣe}sy 1\otek1.+7 s'd\Xxt ྲq+fyM ZG kE6 Uӯ{'&BA@e:'$@"ar`Àf>\s:s<ҡbs:fx')6υw_׋_%%z~Vy{:lءآcE_u{9~}k`j)3|-z&j1UBjދ^16ʋ{+175T'>Nat2)HЕ xoxuU?1ګmk#ġL.R>Q~y|@į:<^1¥k{ 0p|}4YtǾ .|=?w">z&o-E52GO}RND ! 3j.Hʯ{~ <'c'?vρp^:mđ_Qj񸘳's_/zdFe4Gz#${~=q䂒bNo,EynxcU=ZN3rNПڛClPٕ[hױX 6^'>{(<؟2 \bL{6OO~zIEq6^COrA E0_ݒw*p>MM>/_= pK/~}WQ&@ˑ$!G 7 IVҎ2ӌYS\|F]G'ӵH/.m\W0;)Bc֯5?c-"j!ٲ|HM6 Qk{wISPLpjѼBW,dr!Cgs vPj‡O}?z;z1?W#( HD0"X{֜]8 ek]fmcOes~o>[],Vh|ƝZxB>Ht[`#H{k&xIDzdTK}uw+~J.armrVP ~>Ob>͎XljϿ?-#7 9D 8a^0Yhe9_/[7Ϝ)Ka2|ܞ|"$v&D H64jL#k>|hZ=~>ijؓDa.1/Qt_b\ӹzשYs%z+| B=^s}p&Fٛ:sdt{C>u" n'jN1 ţ0Pg{#o{?o-ˡM _lemkh8zr"${"$F^x%dl3.cQt-_5Fe(;יkett/PMc%8޺^sLass8/#W1&s5z^5;1n~}~?y/-& u6BJGO}RND XXtMD`/ڝ9$k`o\X_|1fi(fE e V͒(؝UBdNͫ`N*//}=89}FH\Wmɿ0G}O͛q#fr EBvDgW_}uq{ @Y|ID 9s22H=Cf/4]|y_W:{9椓RJ (<ϑq"l^+}4֊o A`iv)ַ<^ӟ7}}<^>nS>](N=>U[_-{ )K|D H";.D 44˅7ء}VDzk ,]t>9~c޽^|n)fPN=bs{u)'*7iN@zC[gEu9E A|s#˯6Y/X.|['ۗM"C_C_Ͻÿ_"&@o@!S׉@"$;D 7v\%@"i'|lyDy,kr׉]B*21ΏGbv*οΩ=d:C36{Zk%`j147MS>@ՠiWbju.F;4>ìƕ^ly/Wzkİ WAŷ$:_L3[%3AN$@"""P$^#x=46[ W,Fy.ts;ʬsny<;_i~ll x-\&%eqF[1qW!@e!W?e^2 Q^ź]|}`asJ'Пxk7C_4.Y#Rtӹ=D H%U@"$hn u+]O>_?O˥1XT_-8QM MryFqy}y:!KY>UԦy;VU74I:rx~ p_ݿ ⨿Aʛ Dy sآ]<p/FE~|~/VıMb @T|Oʉ@"$@n&@"p\wBi |5ܴ(\nڐE6}/jZ^2BirdMs#nce5_ұ':}2Ly%ʭ#̼}'U%7↞.|zJ2RM^x-[;&,t.^g~}Ko{ӻM8S\EUS)]<2GO}RND 9D H~Qr]w^B2:NaW60C.;!ڮMG# rkoy밹:Y):8/y~5~H[&Q"e׀/Q吏&_])^6ǞsU/ݎY\n0{/.Y/|_ sUz Q4ĉ*nl#m~5oa׬-όYBӦm۬+1EE-2(X(M.Z2PNs̯xmjk9G[|(ka0 ?gzM)ٶ4Zl~n9l$3ǂ݀u}ۿi$ ߼ _~ޢfQ{ѹ[(9|^'gY_~BdY@V+@C1՜|@H 3@?t#ܭwïo]nr6rnԚaI~:"v՜Z;ϬѸ~@_bA1?,Ɗ>%l= "vEfsa9N_p}3g?QĹ dGT8gج@V +p*BX 8&oJ$IDV d4d4"{֋ȉom[;!0_~`ܑ)H Ԣ"E#fsN~@0''}w: [2 kiaTGN@;Os n)rѢE'gYY8= d4;8G~j2جw=v XWvl𵩽іư:߽1"A2Ns3eন)"V/͂Gr]\yaՈшLYt9&7K{ř6$@nKV +8 #e@V ++`thsyQ',Ed5s cŹqo35Ƨ#[EWOʤSyյ0"vEI8slLzn~@Aɳ8Kzx\&—\u׃O{r&@)B@V +p èr d'gPMԞpLg@G׺x[ S6['{jG_u|ZZWuǔ͘o͔Xřk`V! LhUz"9Ftj|yOxQY8@)B@V +p p dYZoi pKmZm6ș7 Z3pO<1Mc޽1"AWV5c9tڭ[YUxms!Ȕo.`. K.[zF|Ulwo'į,&̏2]K#Υ&@V +pb*'TBYU "6-dn| h].r+ ~"Y@Vh*~Ɏ:7k׳)N[44jK"$C]&^fI_w[3^%Gu}Z65ܫZ>8jvIucüzʧ ]㚵ˏn68@d p|篽?wST+6b -yDO% d'p"Ns.2+ Mb3si67ؾ_CDduv3.F7tۘ~7zXٍz_rw0@D\W5OM; ǠՍV:)\ϼE}wGQ trdYW8q< dzcIxs{7?֬W"7nҸ6㓸c~ m4hCu4҅3?dѳoa/\`5pq;79t?b޲6e.|/Xf py?E}m9b](9Y_u/6=/}xͿxɽE9Y~T 7cE)7G/lQ/,Ȼm@66G9cdί3Ǐۉg< c<8bE9sPɝ8.p9(Fr llӘ>l#R`A|Npx盬?cT6ڢ=L6@5N+p`vy-GorУ"UPrw?F(F؜TѮ dcSquN5' @m^TοWڏ" `|V +"*w"LEoZ+sA}E;!sһс=(~s9v.Mq`K<| y{:cX xQ m^]1n-2z|@:}vl` 9Љ£#ׁ/xN=溝_Uu`@&ѬE`(z8u9 dWL3GV*oHY SxGG1$L6|j`clu~C]/tP=*>ꑉ*=»dq-$=zͥ~Eß˱u z:!wc\6q9q4Z0Fhn"Xl\>&[Ĭ[YΌ)zs:??Ƣ~A9 @˙ L祑zdqyY@V~h YziAîdt ד#qnf*})b~zrqU:A(rhOޓQj`8"l܎9 җؤav~9)t[:y<Ma8wS|լc#6~"`b,֥nl 1 OQz [n>z]X{q9ZD 3yw,(fם W/0*躮v5k#@3O~?v񩏾Ms&@q #+ \Y@V +pL*;xwi\N+%zuEˢj~ƚ12Xw.ۑ(#?&_Cټ!z_G5m Uݼvn/brhא1d{qx5dE]vN*(kO7~M;r29.eHGO19 dDzp,O[& ia PIW;4?mUu󫭅yފh19%~O,6SSsf/742^zОL1<֠ 0$.ltf'Ų֑8=Y95 -M6t烶/΋8a? _ Xc=wO|{MӅ' qj0 U2' dUxdY@V`ܹfh%{Oʕfԛ$|>]XI?-WiU6?;?揼\NpSpG;sXmRNQxw+  9UhV +hН^xfp'KL@IDATs' uجU<+n_ཬ:˿p:b,6j= nb03X%r;A҃:ߢՀv_H{>'8kO 5E+U^t< Pe.~C٢ˡ?Gze&,br d{fY@VJ@l*KYZ?)L+f$IX9㌩+xp!ϩ O:*h*j{qx1`WQXaE~Lhl> f H|H2HE]3M֏TZ/z>һ"& 7JrdYXyV +8fস ?zlge44;@ Zվ7ŹFg,]Eřڻ+YwlsÒ CSZF8e~}j{eT1N{N 3Dq=Y]|>/l?=Kת9{:o'>+yGmM/lY@V`VK!+ lY CYC f/Ecr'zFDF7i5VU(-ʰBARcn'gvsѩ+mU,VlcW8p1N*}je=Ƒ{;{ED3AgyQN-||w {{99YWG.K>+ dv/Vfg,,U؞IIf_Cs齙lT~ѓ;fslS<dn l'(|5R_+yM{qӬ#k[yȦg96"qP#tmgfc7#nb@ P"~{w' {G9DQs9(C6qd@V +uY@V +p+ $\;׽zNǑ7^+ZJ2: 󊅟cY [+16 90WL.?:$G7NO.ǨŇcyA*n]=1*_"J>%NYT3'q~]5g]r; GL_m d[WغS eY+ hpړ^6هmz ri윏 MNɱ忙^֯x|1eMlL67xYAHʧ[^x_VryCyzQQo P1|gYkrTV +*A[cd.G23蓶 ӀA^Y{|Y[/J:v¬#J3 "X_R_טbs5#ʵѵGy?w*l[c8-T'goN[|WL?~u7^fte;x|: p̓fY@nlIJYVArFE:\XqG*Ekgg̽4h:kqU1\?a{|4bL٠S6,9y9`S0ڮp=W{#Fi#'Ɗlz %~ Y&Tq@>j_mZm4<7~U/qK rh 62JY@V`*u>2@V + L݁$9+܁@&_^\JcufyuaP]\Xq/˚*N'6c5sxmC "bje1VʊQ^E3Z{^Vז 6#+ lkr`[L d&*ѓ1$%(\2 5 fl.6CtM՝le##edc~֕㗕VQ}L˾@V@n\8 \fwlmcz5 y#q9iZe7>͐r8]&}?x5%u_8R/G*g/ظ,@V +p #?@V + dDTrtȄA>bjs) Qmjfm ,zc1sTp)#hl"Kauf>֍s'' z\˯) O>ѣT(,t)^Fyfwgos'c/מoMf% Qy5g8YҬ@V +p #- d_h`ҹz2t'ֆS 5z4_9v8ZX_Q9q18I"R{Ď%1a{1:ۓEClQ-}9²cyH+SEAb\9p,N>X(m/}-n<2;<.6$ˆ92 z>\/>ƈ{JI\MPNŷ P*f_)Qtȑҋ*+xV|_(OTиGWX#)7EEoKCPlaŐaiA 3Z @y{~ %I{@g_ȏw 创χ lŔh@V +pdڑ% 7nxɍ(ثZ/2py?N{ !lo.l=~s`4G>w\/d?#,(<a.*;G:ƨ۬oI r#z·#c>G? "Ezl E=2Q21裌6Ԝ}$ f6:V~^:yй4V;(:`q!g.x44G.'z#wl\%C'J ɝ\l-ẓ/`DLqgJcЋ8B'e/q?AMĢ!8w~_#o6 vCմsކk4{`k6i{%7Kxxϣ6D+ X^[ȩۯ7a+>,1 *c{\#E92t>w^:|K!y?Pd60kH:ſ#w,tC76xcp=uE\45"FsVs3f/|J=G£ۢs9Ћ'gãxȠQz#ߚyPG8WU*U9\VustPt2<4-QtSgݥY@o5/m2azz;X 1fJk(rq{X˸ɢ0S8t^k/d33朠g@/pW?|Гq@(^3k'[D,+ @r.+!syZn33f7)yȞc$ *(2އuxP5_Qcx|Cp`3G\s܏xN2p\rrr29|c>ne)v9> >z\'0c0)4y poR#`+od⛝VR *-\/9s:j3łL2® x!?⛏6ƝbJLh&}ys'_/6ֵjЫzֶ{Iז`׳%t^gj Vswщ P7 ~5$|+~=o_RП Zs=(Cqd@V + ,+@oOD7>rd(rE0ః E$zא,b pFXLmI.'9#/sFT?$+> l2+b4w<#FTK6/w[2p ?`D%9jO7E㺈v֩!T8h~$ZCsXG kE9:l0 W\-v}T /;LH]/^ƿE e/ū!A pc"!#d=ɠsnҵ?#?&\|r䋗O;GTy%?wE|k9$@n" dw<ޫEɊǒ7aA dc(v/t z»ݘL1ܔ< _(2᥏v@}8?>=a۰^\ M:z6.-;؀p>L˝7\/?&(vGA㕗z!a>pu6lVH\GY M`m-69(ƺq[(èYoB+/èvL~1>BMo[ԏ*cZL{^{8w |Dfk5g,s3 ,Ҿ.ʑ  3/!S?./ޘ^y،av#n=wuob[7#M}ֳb0:WawKCzb~>Ͽ9/+_tSVgc1ֶu0XXP=dOiPc:?|P{|1>f"S64OW+ˑ0IV +p<+Ͼ}f8iri' xlF|~^LXn]s2Kk8\;fm}Wa,׽.AZ(Mi¯",~*6:eSeg!d]Yx=ܲ%a6 ]&6]SOٞع_|,PZDs(ӝK d/eL'Y@V`*лefDd=㤏a[9r8S nm\Y';k:ӷh׿\Srfq81}XEre5}']7ؙ3;ݿ5g]T+`M#Υ&@V + ʗY@Vʩ@ԜCmZ7Omu7( p>+Axmr圸ͺkmg{#c"9TvG&r)rNOVHrZ2[ Iƹ{*0daHd xS76<:IU:z>/򺟽_TlT 1;4 6k= d@nti d6sh~^ kZCa㈞fUz{3M5l:}/Ly{ͱ7>{k_M(9ˮτǬ#YB^wG}3ѓ =>r+?|׽?z`m#ǧ7Du1"Q|:"PckыrN]PL07P7 /y=? pWuxٷr!GV +Xwv dY+@S3p 6?*ްsrkp Xe (P g"&j퍛jbG5r]8n ަ OǩO2j6Sw/}~ocD 5ܗC9|^>@V +P+,GV + oU8GJ;e&9<ڼ|fW";>wQ4Ci{XymrĔ !1Ή~ltޣD(#Ψ<(AjizNqmU6' eaų~3?SГqPEd "&Y @n  diHhZEmb[?q-hSl1.n7{)To*#6M(ͅ}L_gޗ%1pK&n1gy9Tph(tSlza3/g>sz}vK9ΗC&=Zs=(C6qd@VW 7NϥgY*&:Σ?lX 4}dhvJYy-W[Ԍ9쭢hKA푯KY*|̋˙ b9Ďu2[G9\ |oxim}0I}@'|`|?~ rI(Eȑ ^rjYTfc*!nѕyͷu&jymGmQ41|Qӈw䱋A׉eMA~Wo]1ӹqȏ%kls:1WF^W\6?Ҩa@&E/|;r[9r!GV +px ëuF d[Sp/2/ 9 QPvxPlmM䵊M5|Dj5s\<^|rs΃ecػcuvZ,g%qc6u{mp0ac~@ [ŻOo~?V#? dp(e Y@VU_F9+s92loU~{ضGM ͑6ϊQ,c詩!|+'V5Hy:2,n߯]&GEĹy1ɦgԍ5L_ɖ>n^}߼*;&fvZ`եd6[0`f'pBO|.;+ [u~G16b:yl7fa`[':pA*±cL蛙14Ė?r1;{JVe{(s3\ +}5lj llr ˵&9:5Itc7=w駮9{br;}1R ӓ>@VU 7NΥf'ܼ~L;FtלJ66tc6{5%:3/lqP5-C0GH{zEˡГEigX -M|r~c]׫bxqV:9f[q8Q];nuܾ5|m_|_Ə%/r@ɑ [r`JY[/:ꍮ-D7رa SPQ4U9{q˓ d s ]^.++ dQ b'#˹'S.csb~U\SjĆ|~gƱsjs]b' سOըW޹ʵ5ݺoy/ B13NιVc~{\}{+f P Bsxל1˚iҬ@VJ?5ڲY@V` t82CO X?IYϕiuQV9h|05ݾ)@L-)1o4mSwc6u{K-abH9+OPY'נpKW1*oWy{ܓO'=K{߫Pܼz@06n+)qd@V ?Y@V`/Ckx%Cu' `zv-#]23hj^uvhzɢUSG!kf=\_OCx7Xur#]Gq!Ơᦛ'S<]&']|׿ޏM-qHL6z2tIY+,B dYT`CFH#$y<xrsLY6nnj:uf+DsRi~=*^:bjƴvr\G8XzE7۴ojCuZڋx^:m } 1Ox+Ugyzk7%qs4"-9F6m^{2'  s/# dYîiLso,bzsɶa;bC(߆\7AM:;j=]ǰu<9Zrױq}dQS_ϺFGkƹ< w꧆cͷH&gO~#?rX 0wm)v(c!J{@NY[~PdY@V + )[.1^f gAs PqmռX%,ՈFӓcF6|#`fw1z0\نtc~wc#_˯x^gc<f2p+ٸׁz7GʕZ$m̅ ׼G>co#]`zY5-Ts*Y%s}Y1?ivV + Gzws,9躾kPN/7&m{3r9U)}sהE_Xn=r==\{>-6kS<ײ(<-:c>n8 sȉpi߯ O7Іϝݗ>S?|Ous9 "pS 8'sYcX~@3@V +ئ pKNu"\ݮ%^+[,Y3{6q:⸼q/}գ_~qmxLB&^#d/Y?wguV + jhZ{*Z#Ϳ:y.)OkO3lÜ|#E[$J',;ٍn8}W>w>r/L %ѢE%L  ?@V +p(kzwY?k3MLiSKɛS|ؿSIP;qy"A6܌Eh Mrvovs~Xc)n=VhjP奷5P> P?S/y?%[ˡ&@)B@V_~) dYyMG:P\XKvF\q\C΋ .[:,#֗h/2\c5 7sNW 3Y٬g{dŹ鯱ե}3-dϞglu޶qS.ͿS/K.ʑ9Y~Yצ4+ dV|17âˆטt=m876{XY)i:bb'=tl`O<|Js<C8^q9'Q-5X 6k:nˢWcXïIng}i/N+dM;;qRYr9IIV + dF*MKtaա|PJNDo{{3Ŋ^{05h.5W4bƨ|o9n7e?~YxMgўkúӵ%&6G:&pO~Ǜ?T,r\-ӢE\'s}YW1[b dǭ~wn~fME n[h/:8iJ9F`<ڟ|s|C=}yUe#Lժܦx|Lf^蔗U cfL/nе w |Iw駮9{}^9nԔ ,CM3Y$+خ elY@V`+л$at45P;Mp ̓56nn!qVY'˚q"pϺRX_9]guR&x5Jm(ʞ^(o[67Z]VB= pP#+n|!ÝjSoP y"wqp7§jـ &ƘOٰP~ƯNjk+5&~e/h!k^k6M4Qd6 6PhNnڴkUy"o?+B=Uhśvبţj[mf^]un\m:?|,xL/\ՍpYYs܀WR7FLy5Z&W2O?o^sӏ~YďB9榅Yy15iړ>@V*C a~ D^xB2QP#+w~!^2pwɿG/g /xYu˱l"[5s$_3|G藪޹M1~=6Bl6qYW»=l53dCܜƟ>˱gXrE9v ɴ[\fs|$smˡ-sxF,:T(g)a(W rclnLm\<{z÷?[).&+(d1$A\V +p(ȏJ3HV +8oza]n(d'YkC.[KO6{3rbՙ) 44~ G=JD<֡k^댱Qoq*6jGSa@粮芛s\ׯsV3G{p;עMѨ ˺缯Z9q6Ź|qWdFS/ ƀx}AlɃF./Ent|10< ܩS~[XLZ}i)A Qtd2`ڬdO>+8 ,:@zk+@5GnlIJ]As.w~WJ;E3ƣ5(up|Oz5‚q1~,M`W.c~z b̼ mЉcظ)J/)x)ˢ} Ohs;es鼙P`Gz7W31[Ӿ2IO% mP6n86c{W;|}VyQtع⿿~oz~z7p@k4m+/!?pwR:nw|\ srfeG&i:ַiOeS8eДqjt ;*&)[ש7Ŭ8u_eO}U#_k&U>yIGldKrQj`/jY_-VtĿ|i464dh5WsĆ6уÒ˦ W6 7s\b͈`'Oojq' ?tƎEq)dfTq+@5Gn%Wؗ !N10m8%Gnz~a]_pʚwf}ĕ=똯Ur|*CLu3ȝ<h 2AC  @΂ɽ' P3&@CXh^h⑁s8`5ןedMt\҆R~A<pSЗ ƺ>ϟGyQl? O5~Gˑ9WBO+a%Vkm.+Xn:5d> xGkҺ}kT4{+cx3}nRUX)ڱ;tn Fc;8/ܧF>rPlǞ px[ iEz|99;t9)y i+z΅7e[B!4զ`$cQ/ ْe#g* cżĨ>[,(K(xiķo~ P#+p+㾎k #7v_ FRHdrqYOw 9U$.X7m1?OG7U֞5c1ĻkNכt>ܿ:||@/w@l c'P*X7cuvy/O>kVh!z:@KB~Cy"ֳmCy꧙WW$g3DsM~ΟEMgˑ(Eȑ8Xq\Wy~ D^x^YBW>1?uhFo~VZljEpl}\My1&']gQO\>WF?DnMhOnQ9ͩ"+L>&-}/k|RkK8Gxŏ?u6) %GG'C8zyV + w.Wk8'< Ft?Pɝ]O' zz"m˚'myԬ ԃd Kv]K3 uH\ fqp6=YϏd:WPhtQZa9l A2p4׃d9z9MUGA81P|26nhʛd7&osΛ hAp[ ܙN鶭4:#e[oN7Mqo?7kO?`fP GO19 dvY-]:Ic]=k a5q;n|_9YVaKw`$l ÞN66utsoO45:~MڏU:$es{AYS =/U"k=?m ]w,<:eg&D|c:d XX(z|UE)yZNQF0eJxk^ӅK?q߼ k#8S0_G`S_K;sO>mo?3zab[sJ0#$_Y}@۷R[G~ D^xx/^HJr^Rh@V +P {DE]w)kQXCFSЅ-:^~<3+;gN]9sÍ;Ϝk.˗vNxs9nK/=VJl m9s&q*9y2f+57v.> ڹ;O~ X?=ϞmoZQSQ`kyGV +w>LWǬ~ D^x1;nV`+;;|N`S4xHһG8QvUcܹ1P.Ҙ_Uιڹ%/9}u;nšr㮝3rw_,}`Py\~KO<ܗc_ݹC;Ηg&S츾LNڄ)'_!*#_͐]\z)l! v=_qC[;Q}^,Eie^QMF.^(~n,AβpS·6_$@_.egn?|[TO 6;d;{e}W@V`*}p.i cz3qw~ͣWkۚf|X1KO?[wNpCy<֝S׿`^P윿;o}Me#ȟ)J_oKYMᅡ!TTԴ|T(_z晝KO>sR6)xj?\6ڗv~ǿuXz-K@ׯ`[uy\1HAD<*йƢ\ѺUQ<\~!Vy9lFSzY s}qܮ~ f/Mo^go<{wJIpN&e됝&{<}޿**}9@|f=UF^s̓fV ރmh+~ D^xps Y Dyo.lu=)?Z|=C_gRi ;׼澝sU;g~a;!w(7)]|739_]v%h.KcWУn<;ʓO?vWs{b>BS9s\o]0D9Q<Ss}L!IM2jz|J%0=m:&^M=9_7\'f: p͵_{W;ϋrmW6 g7:׹&=IO$[Y$kd˖16F0 &Ni!R MUSՐ86X$Y~ڿ}g=[}Zy[{8V#t4w!D)z>$G`L~!ӎ::y e;gK/zx7煉kы/ \O\}][>n)UeM"$xnЗI72E-B$%bxʪ>2$_~핰zp8ͧgV8%+eD^X#l-LT笕7Z>kcV>m/[ٴ~n!ٮS8"7"Cv Z5sEB>y?e0#w0swN=>+&UJBRf)xئ%'Km8eg#`G=|`g/>zG`~1q)t7ǂ~s!WkSS嗆w!_/k~$)x?=HAro(NA^Fb/xDX|a›g  (&=QR+τ]#*}+KcuLQxlWmaYQilB[$(Evg 7m;/ Bb!w fv`bgoٗDLˆ v a|OtY#AvHyvȁti_B}ZOc jccx@S!#o {{ h:t$l풟dã~,ÓuAtٷXY9}*g‘O}E '//-H|sI^Yb]Э/-k[hJhOI:Ԏ9J\c:)#\g(,쩃q Rx C ?a݊xB1G'/0>>=>$]`7D]B]IyذІuМw,{WwlQdž?g~~\GpF_DQfdV6#Mmnm1 IOi& w7]>Ca[ ]/rql7j'v7 =ް 0+/I( PJ9백%CYG A@v`~q<6KpzC~ 2Nҭʭ-yRR>Uǡɿ$/={~,î cW\#I&/M+0zn%;"u'bj&L%L8:̷ex) K̏:;E No]^|FMJjkyzLp#M,v*g@SNMa:6_8_Lf8{ ('zNjY?)|e0GA"O_se}a?~tq`DnbAw1d 0oRN4I^=nlFSN6^bOX-W^>8R6(I:1!oi[yYQvSRA r|*Vt"]A6ʋ-~N,Yy,wrmc{O=·$׸"byz #۷Ly;m#/)6QI^[O嬸6Vܖ=h>&}{MSZH⊤_1t/qht09;&'ÉoK~'HIM[!YRhQ/6+MZvuBc=|* yCaގB˘̣136K9(LJ1ȣJAI=,/fQXم}Yr|ÞG~&ɂll8[`hS\-QX#0T%33)_p%^ԧW^e=:"^sG Qy?fbx^u! IGBG&V_NXxey5lֺo'(yMb)=eU.A97Rg%Ѻ|r$'rTg? 9CF{#/X&/~i E0!6g<.Fz&gd!<0wD\w^^f?.cY]ocGD9B P-?[;"l#ղoUОYhxƱ;p\8x[j(Ѥ}ʍFB}%kDZ$! DĪ<  Ϟ ɓŨ,]tIh>FX>s:l CnuHWF;ѐmBnyYJ=iGۢaM)-eҾ`8V詣О!2F8"G!D(D_qMR>c]x8 ~!`7%SO\C7O<3"E#f=b;%Qs8YmI7hp*KdO.\ {8_6$dz xq6a8#091N>lhQ><~^ rG#dVh͘ZB[ao7z%ް)eἳ1+#϶c[͎X=mPDA)QGLշ?;m]$P,99<0u5pݡ/<酥E!z8 A!٭8|`ʁ/ɔG кpzA lTuv<( iʫr;~DRGEa;꫾ wyV_ՓѨ#$=ZeKIlEolTu,ZT NL[*^ !bE0O4޹ gW0QЅOL><>:s`Xv0^v͝k+_:67; j hZH[qmi SN/ו#- > 'ۙ"e|:.}L^#ra.ocDCtvm o{;Bտ /r9*l,`!)*ѦiPYJRؗ_ۍ4Vȧi?Z RmBX:q ǏR'/ehbqsYY Xe sw\x굓'O "Wwlѫ)cV#_)zp`^Fnc/.1́k+=a5fxmL~ޙF@M$0²*"sC)\xNbb6ꭎ؍ވ^:[Y'lD]Pǘ 3q& H)#]#@ YjH_;RF;qKH=4bQb, srw]t'3z#iΟ}fgNm~mߞn?ga#BltP >Yru+Q@,no1X0.p,V:o>q n7n"VyZxH#vG؞37ԙGbdV1HSq+? ~G[&3}X}6=#[E\~柎V#PC'&& 7/r"/\csI'5 ;^}%E\'vbUԯQ u]c~E0*<xـ .:t1CXIi0wU;/@ypG>/Rur!Ϻܙ2~aPri"jncM?P ߶53goaHڑ wc|TR>gRز7,^: m=.#n⍅-zNqa3'_˿Cw|앍w MZQ+/B'GpG`! >) ܬ JZS!1 )[EQ8N>}j:ȫto:ہ {Z. iLcw.Np< yCO\mc %G^'Ows]'E܄U>G < |8;8^GpDysZ[ԑ8VF~UV)>/7/#%}2`hĸ!r־)QȱEW_fwMYYQ={9q˄3> &Zf2ۥMpƢTmaO#9;=Ec"r!@!luǘ|,dB%}}W?q]I/FFⸯ#8#wdn_&LAOv6m_і"=v=>?/\ *56Bivt=Fnk'"ikkxghN=A-X=}qD[`/Q@~X |,@ . X 857o{ݏKulܓcX6I*jm2G < |8C/ .:#,4EAIrq?='=Bxۿ{A6"bFY)yͭ܅6>۸9]Lss86P$_K^hCFyn1:m )pFMWQ@킇 6|_:ڵ_C,]Xg<|@@/;# $2?ʙҘzPnԥԯ_+wm?'Q_u-F\F/(/"E,.Ib  <6M:LʓB^URcj]~ݴ9&iN c }mEF(ukoe;CN/#8!d>r7?al3d/vDȶ9Q߉)J>YJ]EVeon GFkR^Gh8ݘ)j!>[/ ŦVEl# bOuu@\X]^w}{E"-i8lK|`[6#8@' < I!:+:}/ųz{'mrG@yۿ ۋ#m6V-b,1%lQ8H>X['i\Z Iq>7}$+,hF <6[Km<+g `m E]<C]8@"@}i uD".GIQ꟎!3!ۡ>Gp<vfGs:RR%6yIPp-Gn`;" IUet7$Р6ل=]QGA۪b}n7@&СX[]'ǀ$ 'oJ0< cC)Hȧ2(1B5 qh^ ޳Gwzvh 4:iض۶;#8 >?NvӘ]1 ž {>ms:*9$( Mqڳu\VHXROdiiKmsuU[^l)և Q:Bl{be\bYEP[0{u^} 8@_{7dx'lp7NMX00sCѽc\mkydLSoe9G}|{㋴He?N;}  2#QB%o}C<\]M}ܣ\(`WC h% ӵkQijPV ϐ"*eA 8CN /#8Fr{;B1llKc@}#aϓ?.I8ucp(]38wmG1F3yri|򶯴]+оEx> mvX`; qA:+GlGӖwFO)_JƆ>('hݎmsC{I2Q@Ut@rKfz>#rڂ" } 3y _,vҔG݋#9pG`+UrkIvjk'9lJ^'^. ߮/ĝ#rG`h 1P$m\/pT}[lJe~qt7dL~YOPh9Nu}@!%O _@ @]e?>|Q,%5Qgq-b+  } q+*1ȱ0rw}D 庁hnVUuʝ:ϥwpG,8cbOjۣ [ڗ4VRvφ/L|{1/qRX;pnL@EB+̮d[. $Md:}u\ ö1džzZ6D gbJ 1 jZ6%'x7E B=1C8&vݑWG>h춰eYIomw܁?08#8@:{CʘYRjo$uf:{0}-aš!ڸ\S|cBg΄c-ltd_*-.$bd0`ũD^k'`#i.V'FZe0jnǬb)cC & w Ft?+˳,ҭW^zd)Ƀ@,#0P@#8V mgl>RL@W%_ nH025-b4.DCtQ}L:YfU?yյCӧCX]>5ob ["\ s=qpd+@֭x{^䟕 Tr9g>l-1k~&A||MڰwP^E->%1 PBmnƃN}ițw#ݼP̴ ASYZ;<nxGp6;o7[Vgƾ׋p ԔPegtr 5\-kϽj$$ !xnЙ!u+Mi=a?;vuю1)_v!~;R'VG^v!ukC^R~~0HӅC{_{b9*fewWۭ*;ŁwpG,8g%ڑb-~4~0y_~JgW8OtY9VX8z4Nދ='=yZ'<(Uus2ڃihƈMcSu3Y'Fb ~v؎Ilp7cK~꬟}3ɷ@?PwRn18t7xp@Ip@RSN:#8DNlŅ]73Ҧ^0u%aϣ_-hkuP VVO,XKPs6iT4yU(̲n)xk%՟hžY }߂s8<=dR>HcXX@}F I 9|u1@*v-1^GgF{/\'n=4 ( lIV#00yGGpB5R 삷vm]}*o SFc2Pcr'ԏY3X{nQv)H?[6[].Ysi+14[Bθ]J@^OIwnMmxnT1ۅƆ<3h{W:f>&I {WO@.<\L_t'~'EO6> (:(d9:u^GpG E錎-A-f@'ɿkHY5p  %wV|jmz>@acXp@)T٧a2qΘdEhن01+iU1}?P(io3xWB @ x#?Ȼl`Z?/" kc#8Naggq]x?:=foW}advJ0kw{"#[`7jYnl.u6/؄vGp ;Yjc]mEnUgn-Lr. U!aCG`$qrh8Ah,.acugw01dL몲Oz=\%O]J-6} e[$Q99BϘ}cHqHpKD^d[!\iQib( Xvk/E_nS*&Zh?DGpG`G!`giv%ɳ1w=eÉdkLgO3_r8կJ@_ fH!#_e}<&vw+ũB!ՎqQa<0C(y-(#NZbLݴ.ŘrP/6nmAr@um3q?|/xliOZPWaf|NwŒM R.0HRDKƓK mL!K æ4TU ;Ũ[cea㧬\ \7>a폕[ӌ## 9E"H~/rh_  ~ѷGo-UOi܋#9:uGp@7vlϮ0y0y 61)/=Hvۯ{G`@_,ʱ#}6ykebV[7ezQ;^N\O3To۩m zU*yU6.ybv[}`p< [v4-9VA, gE/J'lw\ʋ]%UhOujD ޶#8#p0ʮ@{^O~0y@/.7^!GɿB~bgkr{rcښ=zL;Ui>QFk,&V1JKGsA島b&i='+b}X~&tA_x&H"%c=l9 #<]J qDvLX21Ih/'yŰzccrqSk_Ibs*#)c&.656q F,}ۂjWͽr݊b1gB$hg5&TZ*?FqopG`!iq3c0s;Z:;lcPdb"8|3jcq c%͊R51%VLS26M c@N6˘]}>`_ךs*L;`w0j$(E|/'(y1EѨS--͋EU6]زXyN_:k|\opG`X33O\0u=kQ;tK/ Փ'©O~x~1̿zX-[asQ:9Z&/gGYNg8)cTn #^m&ƢTD|} /65/X ~;OXD("ÂReO8pϘ4e8q%aa(XOG`[#,m2gN~//Sv5-opLD o4y)LCU8躟Ի3m{V5ԁG2ԩMa?Hq3VV&ZLi ~Zxb۶E0ZeH(/)%R،8~/s'.QM( Y'GpG`; \]o):$3ETgȾv =51.d/p鯅S~Gj#w0!U9>6v&†|jz} %Z9?pfjیԴOӪq8ыukC]>?# (t]ERqCA\Ue|٦exth}7,|-LS4}BQZbGC, _G>sO c;2 떇qZ/8l6{|GpBNτo+㡱^UAh&=B9?IS u-#aWKG3/_)wH 9WŶmyfbMX!c+d䋡$˄>H Qa@yZՃOcd1Hm8ҟNJ@y>x_>"cRʶE\,ܓRf@|iW}%(6ҮO'X"tr9u)pNGp@6U/FЙ)gnPUͨehJ% b,8a]gchk?/u&[1d%_$eD^d&CPzk%'Zھ[=yrXPg\aˢ85C$9,*λms?r1{'sxM\:4Ϧ(OG`M;# x_]Ņ0q a p }{&;(zvC!N6#AVLL/Ha/gOչ9Y2oc0LwޡNeBֳ>0+I$ҘX+Ij~W[9G\"|[}X3h+ 3ïs=_.h(͆b ݔr^s/D8#`V#C@52ʹb3ل̉1-NK]jEd5kr.l=crq aızHX9\>,ao/57$׫E)Щ0,iK sKY:~ud=§}IlX[˧PֈN `c}a΅Rb. 6f JQx7#luKqOxXKsGDw2[ 孭 [78# \O]r(&cNgd?̕yY/ :IFF2نxuʽz\;~%MC#1?VOӧP?sZ$?3OY$.f1Cn֧/wq.F\bK:viS:6,YQ?=M޺6[12RCMaLR ho&%&HAcIAؿo gis3r ?} #>'-UE:e ac3j5G@P#8Cq2 koImt!f M^sb*jSrYU:[51%mz1z]~n@_ڹ5&[=y\2f\DPrWʪ<ƒ<"88^y),O;(ԀdC5Y(wMb#m[gn(B^ۭ.l>0-oqQ2[ڠ5=Z^ Ej"wG}~/Dk+h+ptBvȷ<]j#uO Lc?P1x=lJ#іT-hOP_-m{X[ksrnyAB0X(Mz/# 78#0 4jxhP+ިN`a}fiB/hܺKa[OVPB`&p 6fR PGq SeW EK>Q*KVD9qSbT*eZ5TG܅S:v,ga6f'&h)r6 )>AmJ'm۪. QzS^XSAchl)~N\  9͎{b1`ԉ#~OT]RU32֣vU5G@P#8C@1 7F'ǃI2t-;/bܧ^Ec-YM.Yc).ky;϶5:HS.~ʻA -_Ja#uu1N#6H9l&΄FN_j n1}>1fGt2|3"aҤ}؈x70: >#1@ Ö,|DG w $Åu(yG:Z6`8#0dma,XYoO}O[?#Kh_:ux<7ӭt K#yF^DkTnm6!ܘYGv:ڬ"6-Ox{0_Ac:?U./S{X!FܠĖ>&dΘV0-Hb"$Ĉfn&z|%}K}Џ<~/? ;).+( 뱶N2"y=#8#T=pif(ŚdnH_?y",+og8Dhgg@yX#ԹqkDy*uBxT)4iܨcU[Wc$ڄ&gbb;[O_wnٰ:ObR |i#Ye𸎀#8P H;Bx|r2cn62=?g5?Cc|*_k2#uE7-# K&UА_!QOLsmNknC_: ۞J2إ:sa MV„ )&4#dgybC@*;N*8 11Gls}XM=r/_fw+`pG`(fx聋=W>Ngq_0h,mK/>Ưo}D>T28yUQ'Ta mue@D6Xx$5}>_Nk_;6t,Z~\r& ,KPY4^H'qQlП4J[??$ڨ ƃoZ\ Ӷ :}yA&&ą>%ۢlB=;ڄFDEas)Jp6 `#8C_=q<]xQcrhF4VWON^ G~76)vxqb:,t=^L6kj;܀`w(Z+t}J"Z e82i;Ol#^! :MŒI0¬M[l0BR㮨F|zԓjһX_k}%31h&Km]Z?Ʀ;+wGK5o"XەְgټSG/@_` #8ÌN8~eal~}F&>?"{"~_엿Ƣ\( dDY0u;m{1)+@mRXmIV>E6HXirx:r[M۶&'Gii~FI|XT5=ZєwvGjlsat|T {,]]z0 ?'"1y%\Y.(yLu/@6c4u)[o2=>,o&;ke+cxn! iEb\ ?Uw`2 ϶@!- .n!chCx=& Vz7@:7IQዎkliv:+s4|`Ӡ#8C\/ /G6:1~lKV8q^k_m35s?Β!ncX['Q_oc^E? $3-#lF=d醶y>XVbo,G@rd<&ۦON/ZY؁8ܬ]>,MYSzW?t`  ظ@s[>W ԋ#iAGp r^Zi-`ak喇 \#1*vA6*gg-8_uvضNaRyYTS%Ou%+u|KgpNB_Y=h;6V/<ڵm|:R +)tyl if y<`RL&궽^Ic:ޅ` (#UXAIۥ>G1n[8|$3/rwXWt[[ # 8# /y؁"Mg BǑ]/-gl郻)MZCGSӭ2eZ|(#\>[vV*Gzlvh1CB2oFj>}eΔGAdE tG*#<6b[|\<~TԨ0c?hUd[bMƍnI`&jᩳ2ڥ@%q_i GpG`!`]H=k.8P%-L/7/F8/'df]L1 X.xBI> eJ7k{m*f-vM [9n?웥M%-`m5ϾQ-u[]^3aFVx-,* ܄Q4ԦA);N70d\XC%@tA  }arxG;guGue{ rL7_}L^xwa}yG/@_` #8É@s62;;/Fpo2?KaF:_ 093cHn6;{1gLRTYu-m}uSKsMP?):mac,,(Cwz vE(xE]J?e܄ -%4cjf,ќԸ, lc {LJ}x`7wӺR%ީ#55Tn8#KadiCek/bXzk0m8n辒v8esQi{H* 8'=:h jRl5c aͭS?`BkkoMe8FyGŹF9C[WA2ԭLcuuwia_/A]5 9_b_:=BQGMOȼ8}Gf:#8kK6R> FM aKͶ3aL9[kf'tW؄XXfMIܷV ,'֐馞k!:˜Li(5W'СC;,eh+d#bxŀVhkWom7^#cpUZMU~U6.w:"3#8Dx]M^7yA`\r(T3ݑV_=,Fej_J$|͂6;&&iҳ9Hi`MXb2s=i}}uq7ɬqsS-eNĘ=3dz fq!6]]DiCؗԾS퀒O} QplX XOYN"pK:8# R议*{YjX=v$OZ gϝf=Ls{&E6#a+lDޢ ow"Y?Rm5X@ɣG:Iaw\"X_1I5|m-mj/0hp#.;yQage]PYxڳ0a=#8P"\;@ٻO&vq.^wG. ǏܙG.ijq.Atc C8]f Z@Hc.҃^Vmԁ)q%O8xo aۤQ% ~CG$.66x,pǁm;**8sǏEd@\h,MyG+|+pG`!Yx`ʃ Td;kȋW'O4ǜ曚M҄dSCXMK-=p֊Vger~(VoZN ҭoU;2s|'Lmӟ Խa-"o6)EFnJ1ا`xg}s> _`ٮ$h_Xn8#ךݭvEX=,]cU&$h{ unqKLNU%? {{kF4}S/ ,W%؃ھ5ٯYK:򌑣3tNMg}#gAU}c P XÜur/@CGp &v:\\h^as8[1ձ ;2cJ|VUS}Y_^~!SI)9kvm" 0qVk&4SRɶxO^?<b?!r.,-U*uD*q#8FbFFȮ6;9>5e X\8g/,c:nHPѤصZ<;"arwJ~h 3ZCkuRJh/ L1e=F? k{{=DI2e`g 1v<ɆQթ?v|'kf1i.yۜ5X;_X7t8#0S8Kv6-dE~&|[a|sՖp4t[ҡm>7Y+&=:g:Gl_+-v&[UBub~p-HŀFXZm:ur/,OĿTxPj| M[] 9/lpG`xx{x#. wb'iWcLrp!Ũݞr7lk܃E'w̥uD#k9ۖZn\b!VR>#_X GpzHwW~dÄS7w:kxU^[y=ޭ|nD\zsPXk~vsNXRjy' =q!Kë*m'6q;'2)mj!(i=J_07wGit&]fz; LO IΚsٿAk{uW,Urk"XҚժy$o4VUW^M, lwnll87BfmS=Fe*

Gs@F9ۃs(]/%V P&mgHڱ_2wpGӬD0zsvpxΜG|h=[9;e=S \61gj v1};1ab0*z.9bMm=baND"GniS}lu+)|:}BqG6"G'%Μ]P{~LX=rX^RD%э4{I(%ڦPIy% 9Y ýTZV֓rRxY5J O}-cYT}Oy;_4&|l,#;Qk#'=tLlwʪ(pa߳_Sٶ>;o ;`n0Mҫ~N5H\8#$p]&;Z;Vi.mU%+hH}jzأ w`M@!5mI u(q3mEeBۍ2^ٷ1l`1m·tkp9I1b@ KpXWZ[So:߁ڟ.b[>jv;uzBzˍGpv$[ls=gٶL(ͺ۩4sFNqr{QsD/O ةM:m=us?AmIV<8@)[kU#c%,֭ؒn|wF_ wI MHa 9u6/lB8#PdV[勭=e&;tV l2z;/CdySYђ1Sy/t ڑ&h:S_Eaςxl,,()>CjU|7vމcw@Ҩ.W9[9 pGp#?NnP<1UM(.0$_#}q!*#B֏bY#E?4-@նn=:'۟(&\0HAL(׺Tl?`gR6_cX5EsaVGxq6/lC8#8CgݜqQ]fU?25l9}w-ӓ^& {q{D[U1EG O(b iuǪvr~%}`"$@c 1Zj;cD\@IDAToGS} *MFRge;5E;|` $.pGp^3QclIn\%6;OL$ZSl`}؇܏Ѝ!J)Cq0ZO?‡ c2S}bNz,{'d 2ځB^dy6Qc 5tegv@;t\8#8=!`'=91g۠-&iv>׹W#9)ѫ^c۵Y}ZIJ".6_?EƧ|+(TU&I9B6OB؛7 96yJŤ8]Fv;#8F9pn&M@tx:Dbsu_r{$'kjySJYk3mTO;Kqrlc_}CrՉ }Roi;_66OU!.#nU}=ss~=_/4!M]!֩#Ln8#8;;h2&hP:X}Jel Ѧc[1Ź=]9l<#EpGpENmczYַЎMt HD4 "dZvhKk[֞qA/2Ɓ,aui_s}aOJNS;{yMɆ< 9HLKZgMdK^~΂ fۀ`#H$PhKX)Ep!D+hK&EA "DI fpv۷WTS[WuWW=w'O=o֩Stю``O@]y5x#^U;qYIf<%5"-i^kZiӌ1y1nMZa:G덢Ҷ4rLcʱůćFuG7GY<C.S@l ă a|Ud¥wq#IY9|D-C?2r |'@Ca^:Wz#iO_Ѓ5{*=<=a&?46lA@D╩.' xN.]$h5Oc./?=@`VHbb5ږnOq]y?McJ˵kb撚H7/G3heNt،BJuΕh;9Lmc{q{mTn;-s)喕j:ۜvO#J.{N|{FD3mbi(VlA@8E~z:h\ҍ7Q,7NVhЪ^eyk>Gw\}jc,e\m%^#'jA7vn0+|em\/cu{.mhCp @&!PM>IIr6yASe(A r]z3jѢf`˚͸2Ŋs{8~e.5t[:5vFˢd:Jtݧ˲8ov;ǜ9J۰Xrl, !@'%/Uוe.ՇuGdph淈s:9itbYj|h(ˮ/w{ұll⛆˶^#[mlaV*-qqTuNx_9|.|\l&]vݿ5YS!6QP @`3lKykkz9Q,wr<N"RYw\=he]1uٕwv9q|G\S«˶ꑅٮ,}$8F*^}᏿{s ʐ D V麮RkWۿl[~J͵6xyx0ӘD>qi||u~%-쳌I{Njkmx3>=#EbXG -/1LR>/|%@}ߛMKbݝlvh1H H: @c蠋mʫԲN%<֥;/G9yӼɤIoͶmqFh%Dݸqkq1Ƭy.5kk2ފVJQb;Mmq7?{FA}L}Ha[~i AlcJ~<zw,6KU:PMW2h@ 4*ej뢹˧n|<<~M9F'&6 Rb}xhaOQsɶm6mg]hx-:M9q(166.zJ!sAs4TPj b8J ]ګ/ŋs_l25@d @8|j׶eiR~qfQa؋ޮ}$ 'gQճzĺ8Xjn{Nmn[A~k݌C06ǔ4ط`uo~_ mmZ d`s (WŷKnb8K$3M\?Lڀ*A,kqck:'1%GdY.;Hh$1G\ F\s.ƌV}q[/VMX/e5ؕ..և-%"mT!@8-ۺv]cq]xA.ݢ:߳WgWUގcQFxinSey7sYmzy#mUh~XlVݭ7/ujÊMm/96` Y(duNf=y9D>oٞ9Ӹ1Y0[XM^m֒tQAe<ڜF]Ќ"FN=6,w;.KVj\[M?-م[ޒc+ 0lZ]gI  ϴ!@" {Ǐs5{*߬WSNNɟ2n襧WPgq;lԗqz=6\v]3v@ק?j=5՘/;l^=FOqGr!wLSW6q@ΐ@SW`>\wi١xZ:fT :K,k{+'_=GƍQZۻ~=^5uԖ.\_ ]xx;O?ogutYP.86| 'лƉW^~F8kz>ؾ[*e^j6iJǢ>buVzMt1ۮ<j|m߿y,V^],v]eWٟ'@]I$eW1[ikiRgB5Ԝ0t%]+kiO7?洚FvRB~6NY;U3mƞ7ܛ+b-ռAr~`ѹ;wnth{77Wr덴ܗ7rֳϥ_f&ם!}]p2^ǺbJ_2b=7a#.Od=UTo^:wDE*>$GQ?OuAek\cL:n*χß5W_N奻[ɿmIxKN+%?> Ւ>ω~t7_=mlimJk>},?`ޜد˟մ?'Uj+j2p?U最lRGd ";+.΃7ݵ y.z{C~@ ΀|;|jx+ _K[uyh 9 &S>QXpq4qlֹm0Ʋpތ+2Trn[_Wa/Z9q[<\;tx,O_s\L!؅umr)!`C@)]ኺ6ִG}7?oK+Wɉ֣i5ϟ ʯU}v~*ĺVK\mhpעy>zY0֡\4kzKR6|w_n{ɴw習lo[_jKH_{>|J _B1'm;?$oI8hl8o`xH嗢ƺ@:8@<ʺK]R/]5d='C.9 %!&C(.tJLk׻+)ץi}95L]N#i/6wҝ/)\j<; o 4d#'z̐1$3x]|ս e*Q:K)(Ү58,Ggs%PVqNv[r`&MI.AÊ!p@5Gt!'tgƛ$D$(ݽ_Y$UC'%p|^7w(tGNWV[Lݗoܓ.|tK[_Q!cףnjńBmEQ: ㇘<ûRy~ԼM[/Ѧce86W'zKǷv#oX @}<8X/<0` )C |u_ѥJLWHޟ͠d}P?]4WCS 8:{qSQ'*ug;. .P{[g~/EgA'_vဵL2륺sb 8qGElGսzu{H^9mKyڦXyئ'@,m;Jy}59r~C#``dTB@~yN`t'ҕO|я7Ҫ觯ğE;\_{8G2]x]z# @ՙ)fe@0VKd/Fve{ K6Qt=2GRi,W&,n^rn{.59DJ>9In{nj>Y'x^oKvߛW;/ã,w`.}N[myNj3%v5e1m:b[>;- mQR,'&Ƞ4aeU꭛ (r$m{rҕ,3=@]h/+p?|ι}gWyfͳ1V;ߓG,F3ʤC(c6|^B)'ygmTZ]({klLVӬj}dz(}ԮSGҿo?'Ϗ[W~]H>&`8$ +Nr=X~OLc _h@;} g|i]IϤ|L/!:2cm@\X?yaqcݾ} r\O_M}~@e+m9mU&_?'t2GJ* 9e!We' tt{_TW>|x.1qW2 fO[9}`Lr%_2Tc'ӏ>(WgL3e(u;%kۧQ&Wz-\w]2t@4# zMKҕ}<]wz4.4CS 2:xG~_|I߻$b0΀Y$gYtWf@I^%;1ʊRzE;K\q;wơWk(Mk^W c`U86Ng@ J@IJLWm+inZY(͕Brb+?ߦyVL(F"?m4ɢz?7.qώU\Ayu~Gy%:GxźmG-cLDYK_3i9(t)~Fʽh1Ͱ藐ߗK8u @{ifKDZɿaѓO犵uh( t~L<G}?fs>7pҡ2ֻu why!0~I=|VԹ=z_*ӽrEz=ʘ1tm1,w >zml !CËa䲱.S!6QP @NNL.䁴v e68O 09\ɷ뽕~_p{Ze,ә6bpN &xFgV+[#ƱoW=c]#WM7rO!tc{<v WG68F'1@XR+ϕWҥd|[;٦m\ѴwuBp(rEַ7!-ta ~.Vb{:q_M/_dzV^%w_5T]cͽ9򳘣EcxR]'OA{ikW.&?DuUlߒ 0 m;HgvUbS1b&Ѧ=(Kj{GUu97|Ǟ۵Yei{۹-;좍];^w}\&ֹ ! v@I`}.면oˉYC`:niu}#]G>{_ǙBp'5~DŽf5uj_ԏVZٶ>bާ-Tƺ݃KU]>2ݏKٕ2(mK㖾/KoX}]Cz>HW҆!@@;J@|s,7?scu%g.fk<֕ &z:ee4^Ujuh< 2zLv1$/fb:8lSڕ~jw޹ @QOi6yiG{p-6S 0*Mo}25?W&"uA|轷Ǿk)mwf3Ɍƌĺ(tSuOE*z%]j3BQ&ؖ~QG}zfWseX1D(խ+NSu(ؖc{i,'!@` @D|5$hm/%`7emI t9m'WYMeMh;d#u',2jeͷfWy|*c]n;۵8lewGvwiUٶC 4@Xk>z$u{O@d@ɖۤt=Ih1u &c&~P9ߠ.5rC`ǕJv,UwYpnz۩{6hV?oUgG3o~F)G%O4چ| rb-ങ |Çj]p@%uq5[,"c-ݰcaU_&>ڷٌʫ^y~=cSwejVYM7Mzo([mH F @5+m])_V-rZ Zyk+Wb?/ɒa+Ti[<]NcY~[}~mPU=Z~|O4~(jchFYxLgAdYYx>tM/(mcEeO#(W/D]\gUd[l_2ۺ#4ʣrk-UPlja ohn&@8$YHUs Nlk5'˥zmtf@Lm5tۘZrմŕ]FbF~u펶doߪsp[zq@`&› VB8v~ 8#1 xP1cQwwGO\ L(۱ ZҫWZEW\6Q^k;Nq]uPdG~Q >b}!` ,2S 0翚^^e5@`tnwׂ Zª3 G2\BDSNIotYz5YO5\=KIdz8֫vYuBH H: o΂~ڿ߃[[gԧ-$SzR|oHReѭw{>K=3};zHh%g3a;j'f6Ԏ1U^>CvQu'嶸 z|)Q{7ng?m`Ç268 @o#} j` 0C{|3aO34$?Ɩ*jK[6{}14It^6m7fϦIzQclgrggg3k-e}҇6 ^)G\qq-̀@@ʷ|+'ADA*u&rL؎omdl{+>\ۢ1Ц[vMgDyf CivVN_6(h3J$f p/à p]]1JvV9}DXfF%gl?`sdA#-%ʞovR7ƥ֚+B8bҏutTv) Uee3Fer;ݏ :blŘ19F`<9-siOۨKdvͯyII+{;r$]hl,29@{?oIP<p2xA`t{ɩ˶He:fw8h~ba.-WcXbܨ'B:ݪ)kAIP]((W|m^{7v!066F 'p7'!z$ݖueR^,>fY7uܗn~wΓ,f>1KqЫY^r:\v[q[,I)}mcmdNmo䥯cJDZKZLgI˶-e#cz:TL |BX>ki ϧ_[dKY>4S'T<bWuڼ1փBWͥ6IjȩԫpݖT[}6*k+~MlGnu&Kc;?iT30>l̆+Q!_x=mL_UW:3d/{{9鿕BS=Nkc4]dzlG|G鄼Ouemc].;m2M9e1e*mw\ qgJbp̮ͩ Bc``,\CXێoz}Թp!uvS) o毦;'$puHʠL@Sί6Eq~mVoYDӚQɶWNujnGF{F}ۥm+vvyFU<0waY,H xPBXBOwl,'+WbK)C`#i/oO7TZ\6╽1uLcubg~ejM&yQc6F^t;1LJ뻒Rf?ۨm_:6깕 Yk?ݟJm9,0E8   ?t3:r;4vy/@`9?ȷܽv_Vn/@(P2ti/wY[ʶc,b)%+|v=Z&}]Ĵ,J}$\Jlc]:Wa'>CugQAw۲w=ZP0+ą p gn~)^|$UHWi:K7_Li5?PM'!R8e}~t۱n̺REmIuDzO6~''إ#m%vJxWgy_Kc2Su:@^n~?t X ݷZ&`uD.Kou3mzYzo۔pR]+k2:h[s;&ےYn_]Xѳ"n(lv|OM iXV @}ʿ_g9څiwY,˛\,3hn[O_M?JnJM[=Y)]dcqZ;δ̻1ζ]Ҟ%ʃbb}b(wiJK^#yexc[2Q 0mă HJg}='Zz7uZv5|۞L9Qmp@Ala⥴sڿnZ˿4D3| $ڸR)uAOm>o=鸣Y.e#YF9bJc6,wlʺmg 6ƌ[6#(4&ZlA @]J}}ʯrZȬ>`Ma+J@O _+͙fl?'wk_i55{~עMYW\ǶڲmS[,ed`fXD;KFmҎv;DGPNk5YzQe6ҹm1~-I i$ E&;Ͽ^'8Ƌ|<]7|#fT`n#W晞u'^JzΫ[]:mL(_ ֻ6ն]YJCkHky+=ד{2~lgɶKM^>@k'햻tBeoSu.]~ٯmM @`JH@Xx9YRo^_i[i'S'|y7?5P]jg1'x%o~/WV66+K=^=P ddټ礲&n;llǺT6\bXŲ(\e+h[l8'ղ,ev+Fa[@IDAT]GGu)!0MlL& ,:<`Ts{Iky|⯥KdZ6cW[΅A@t{F}akg'r/OR^79DY'?m'Yԫu밼ZlĪ䥶ٸnc{D:&]eWdl>$'?F9  +tSsg ]O~o{kk݋Yb@Q)最s?'Ms~c3~Vڳg4,m}ۚ'<(:t;|Fz[fk]}M8KRz鰬.Sq9NI|jk MZ `l% F@W*@trVݜjo |{Z\vOZ{-S|ٻMzHw/t<^h #s=vKs9r|^y%~녴~>ϟN[_|~_vsʸGh2\m{l[+ef&YD9f=kcǵ3 ٩W>6fEy櫗g>淚7ݟ?sjZ>Yc1Wv)sX>ϟq3m}Oʹn}n&'d1$sۥݬ~˦ۯV:v-,poigb]QbX}[%-3 pz'wݴCywZtϤMk;Un IԱ隣[6wެZ9-q+V*&LF?ys2oϋ.ayk9k_}9[icLSb}tCoonuy(*ֻtX%Xf;){ɡvYϬU&֕hC l F#$GkJ77_M tڛߖ6y$\~oZrKsbSoOh.M ꔒ&յ5ݞ8 oSp rk35:wϬh;f WS~{/'wyvrw_j?N{J}KSfjjb*)$Z2}mu[>}%%o7Wc[NJ Ujﲶ%yvgS+7O>6|*?#౴viGz})Sss%\|"{Z{S;i{^цOAcPeaڼK=Rkw,ݗvt[}jhٕh68>ڕ1"73ڑQfXD``֜C8=eRy7ʳ_˟_ʿ~!ʥMuv}*_\V{Ssn>Lf5w?ؽ3AcQGHzo 9aʟOc<,,cV~p믤w"od}f o?ؾn391~-\}2>eM|ǂ|[4~Sڸ4ky.ޝg Amn*a{ە>v6ARڶḳ~nlC ,6k- !r a'~;q0n}tn\{i?pzB$-Vqo~fE4Û>SLB K5('|4Xo-~Zۥt:6E{q~UG[Xf~j&}P lp̒eĆ3%Gw k+|5©fcJe]}A _lߵc @`(%pNlxֻ,pR^G+ƸAde!.mo/{l,!@XJ%9\[a2mcyQmtYJV/}JwZ o1q3b@ 11I,]eWNOxvQWe=oA<#emeo1#o'0G@- @8wN9 )HnHo^V1^_ۭ٦>J 2lcm䭶lK_D]/e! Wc @Aɚ;8I,m2F. k=qmGȻ^Cee嶏0mcL(!%: @8ԠcXʭ\uC:>Ʋmծlx=8u.2me6   ':@ۻCO+Ylw-˸j5aJqMUȹqwVbS[t\}~~|׀@ 3'e(uԒAcY}qT/J}_iwb|"kٺe;ښm\&\/cJn|]]ծ > j @`x Y\~]ek$2zi{moS6y-NͶ6I^ŕ>mzljiJ@863A@@-4qJxe6H_:1ZY&~˶GQb̶m]~k_z"ϗ>6N9=B !0iadzWLmǍ2ڶ$懼Okҗs/yCum~ʾַMXy5Yh+| @5b" Jhf' cǒcQ6e*K[u۱$m)cqa{}Oc88k @MH2N.X&Q/_RZL\8نrt^a+[>_zq]F83N2nL!0.6%= @XBJ\D&&?喩,㏪v&?>!iT>عOږ>Uv,Jeм}57'"PkI+;zwP;  [y.ۃ|A`Eb@ LD qT˶>GźmqXfhc&w2ڨmrܲt >lb^r -hC`&MbA MLIfyLjIqMou8vj[.ڕ/ec8QVƠ i``4@ 0'@1s(UweS,ei?l ǥu:Ko2wXFg2֩u66Mx @'&0J"m<,zۗrU6ʨ'r>2F(wuucSh:fE Y%. @PN bTk:>nKW٧ewYYN9^ke^ZcWi3,1VLʺ%6fI @`If)'&C5_'YQ'_K.K}McFYMn8e8j; I|"?b8K۔6Xqk1,?(!P`$B @`ldFAд%5m><ɶlKcRqMlG? >nhCx=b5mV)Niر&S @#PB3iRS,5 T/-cFjRKW;lvgth+| @(؎u#%eoAsLŗ+w0}r@5lԨ  @kĸ-\>޶j[6W-emw`Z-HXf @` %Q3ChgyLbݶnazQ^3bF XUf @hK$L:KH֫l~^8zCI pRC ̜@Lcgmr8 w[|t>u۹uEhce+un}Y>2o}=6fϘ @Έ9&y#垖ݖmbLQfyf,%Q @)Z*Yto>˺_1VQ& 0ԙ-\`.t nw@ (=6Xm%s.ymm}8n-fR}q{h2̱6odO;N9=B 8dlG[f&r'eb;ڔ1qi9k,bߠJ׭``RrA 'SV&[+UTw1N2(0]+壎 ;'/ @RP򦗓9m0l/m6o˥䱮L#V-F(ůfX㎡˲X?i{J̒Kl@ S%0i2U%nEh#^1u1e,TKM E۹,׫.|X!0)6&% @BI7MXubmlRWڸ]aK/$v[/κ2vi˱Ϛ,m|~n[O Y``t @ 9k!;8mCzNmdz.ƒ]mdQ8jQTq-o(곔q!IpzB , ZVʶ%.<j.mNxD-ee _6퓖W=n\;V]\TJn㔥i`4( @Bp"WNMn;ku.mbĺ1dzEW]:+c9mҶ.ݗ},wi؎FcoPwm|sL/edl*0@ sE9Dܮٖ6n|Rwɑ~Q}3l߱te[6Q[G,KѿKk-βQ2}4sq:x1l}SBpZ @` KJ}l۠/DOuLu$6ݟ}ʶq\ʾuZYs,h+ض}rݖ*]1e v' @=ϫl[>JRN$?emJ쫲<ܗþn[_+cr Qz-eW^zn~Ѷò}!p,' @Iu:bGʶ8NRml:Jǒ>m8+~jZz.b$S]eԷekX#G%Űg8p  @8q;YI2ZD5u.GcvQl{C_ExjmnjZ]28c]z.(㹌qiǑ؏:wj0@ '2. &.]Wel[&K^;b Fc]m{۹ض-GʸJǩޥlJA~J6Ίc좭ei{cf':A~#$;um#Jzǒ; 9NI @ӜW9J5 o|.;~~GTdo})SS寣ݕsۥt{P9v~Plt0-ā @@&u11~Ɗv$?cǘ̺QUƾ,w,K[-s)Y<]ԗ}]Kaҟ6Nwe @e8tjP,%zՎ(W-(srٗR&[Ŷ&9>jvd $, @sDIuLkZb[E]=1bQb /n?aJ׭eu!0UaL @8E$7&GY䶉rץkӗ1vzT|җlm_Ƴe J r @C%’G]ڥ,ƈچT.j1[ ?ۨv5}(!0x༮  @XjJ8|N 7ʼnpԷXA1j~і:w 1>@ $P&ӄķkkcrR6oT]//}b @A mhld_w就L?A`5aC @`I *U%]&DXf @E> Dc74gt88+Ǹ!@Lv=y[y׼qb}C @c'l $68 @ sGݒ19 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @` Oet:) @` pU3+ @ 䤶'rWSK 2=̀~ @E ZF@sF?ڲtQ߉?70!  hf) )%!wtH+M_h3tI+I+WtfwPMYצ R `kPBUޯ[wvꕋiw+]~'Yi o@k?_|!v3rf}hlՉ>j 3$§k@z_Nul{-]Gӕ̀hϧڇkSsZG滿/_]X] !@8lt 3&t-Krg+]OW?i㭏Ǟ̟p|ϼs^DViw{S.ߝn?Ly9?@QU~E+/ @M &N pO^wo?S>Rurº}7'n%<غ cʛ#zum\6~8?L[E{ɿXc,@NA> @`N(Cѧҕg>L~ 2?0'_t[wp0Q ̜1@ȷ7jҵd?`y@ D`sLNOWZjK)~10 *tGA4@zT "p~{ޗn7.~j:%J: ?oҊ_`&;?q?]n~3i֝~l4 @`&MbAt?tJ~ߠ9?Um 3+m= ݴwL6No] KLȟ7r/?o/4x[=p[͛s.W^ͧN~SiOמk~A8_5p7@``qזAXN\mk?tT}ݝ;rrqFPl ;3n ˿+is쵶 LWASH@83E򿲻>?iW66qfVtʕSߙV\m6^~5׻NA<) @`8 5| @`>j~܍=?="zE}ݝz4]O6_|c]kC 'gH@8տ{?WWҵOLGimO6InoH}1upJoz$]? Im:@o p~V}=\6Vwtҽ?ʿ=:tj` p֍!B#G"o1 dʹՍm'uJ@{O>:@Ns *  @}o|`/]{/$ͧ޸ohfkGN7ϥ{otG7|8 @_ p:+->Ϳxg] ${g,S/7?rbkޝn?3 8 @WIcΒn?OqNK~&՜Y.ϔ>u>N~=j]N]Ng@w, 3?@ DD6"~|S)R3duiY?]yw35w~ @``tVXB?{ Uz3;WҮf[eٖewj xICq $Tcc ɖ-Ye^hNy߹3;%m3_ٙ;;#K|*%`=[_Ff̴jĴJu^$z4} rU"۳Wbwkµ|,h@ D:V"+U#@N (Zi* ϶#S.3@[d%rEwIxv1# ]%fpbP'@Q3޻$< ΝgM 3@猪~-UpūtFH~?i  "XEG0?/Jj P^j 0|͛~7K>{jkE'M@gb1gŗK&9X 0B@  D@~ЩM{_,z7`}ԫe0#\.E$D$tjQ/@  >FP3]@mH/Fj-kE@^ _ $rrirCWY6@#/ e( /ŧKh" Na%t 甽r\~of9xoe#9# `/%>  04]oe+7HJ3k98l>t+D͌i<#|zNk ^F/:mF#3/c: 01=&$5J㕯9U.0 @Ov;F"[M%\NS={~Z{az3K)\s\9c\@gd ]-yi0:DrԂG ` #OI%WJ:?!xp*vy3 ͐<.TŬu׉#u OWxg~G">mgi<@'@{}N@f۶EWJ}zd$m5L&k' FUlt*y掸H9>5514m%8kDX"CsfIbɪ =@fO72gN_ObMXu=`! J2ΈH'J%JFu׉ak@MVj$5x?jʻ5@ˬ@ZZ#t)@EuKL4 oO@&@k=N{@'@0=\_+,Ќ]vB{&Q3j{a{l]/S;c)ўnjYJsI ʴ'Ig-3J}sK8kv5lش1k>[:E>yD x\?4@Q 4oN_M(_H[+suSgݿG:;Cz$>4$!0 >Y:}/'=HYkjYϺFiP;{̜pdF ~mP,LZwH256ύ7+eɁ H@FO#0$]~/bnx 4|}suzAص]}dC5z#['/ eF1. XchwڼۚTKKΕK9'KXSaJpl ϛ/ݻDBb7 )@`L"N@]h l)5:fs@|/uJ'g~~l~>ܫz7e$JL`=e|m^_E ޷{g$" W\&+69ke͑ՙ&0r fHY2A /dGv5.x:ZJ+@yq6 : >HMSrnHdʿ7HvyAFzO'C]35_f [*4>:V -fP貀D4DtKlaܱY6?,d֤5fE2$3u"8G?_:A *|UQ@2 (4A n!?< 64Z5&dLo_{_D:U_34y3C@o룒 izxA tï3ɸl[Qv(K&\z5>o57e EWW'g-gmZ5sU;&f[h /~ck?r%W[UHBNS|)5LD3O]ϙ>ρ xi/ Pi3*çR=ؔ{ {v? d7dZ{@*'SM uq +\5ʓ?pOeАfݍNc-# IBLPyUm~ɪ;@5+|4. 4b!a> 5ץL@,@C@( ȴQzuY[[j0<~]zv`> HM8,a͚$){7< 1La::W#H@*#@2\@?۲̝'Ydf|̝uMg'u'O{=:׀G]X҉5%Z7wS:i&Ke婟@gjD@ 0S~6uI95 0;p  Ebf@f ogϻxo,~M=F5ѿMցSGt?Pg!y_KBe4G 7}+L$8'75" `E)@RsM ]<7aڵCez_4^ZPZw5%vl}ď[:5y9\a0z_\נG~&bo xEWzv"(ltޑ Ѹw V߆''1oYw<7 34X1t"@IDATs=m0sHhɣM)L>#@W@--C+Dg|-ANٹY9eny St*i-Yk'l|5H0I&uC`  T@%I9 'bLF+5HvXk=[Cl`;iK.$Ȯ]/ `woʑ5rL YN8@$@KM[@ dM"<=L_{Hٽ uMRm\'&hʆl>7@'|iܿuxi5߯g-/1M$2hx L̋@&#ht*v5 XEu:sZ۷5+wnih: wf;j"{^^/k{XbV# wHk[X[OZ4" T*`8J@3u vFי]e#_gsgp!ٶf R9^e߆M jNuO ؘN*#8H: ^wпYN.߱s k:Pz[[ yO4޿XZK5L_mflx2@³ v LYӰ]8Yoz^n5wu|mPY;5@_ mX#2ee>{ ϔ@gl xi+ Pi[S `*.%4_*@$+zqywz ;mw]n'G~F@&.@`f@`-kK97/jIW8d4{ ^xս?5] =tPG?w#@&'@`rn @`cɖP)\>x@{%̀u#iaҭ;w S;Z#p2_općusm+|d^RھA:^1Q* Pn_~/@MHf_(3DZ N_ϧmJv"3DjF/mG#@'@{}N@J(>di6c$ J) 50I`9ii3<@@pKOQO@SG_SkOPuCj+3!A9R8pu;WG#8@:* /P!u`L7w4mas}竎V]tJ$mp4.W/s#@&( @@`3t˼llXɢ}SP~F+ [f͕$`')9 i5gUnD& 8@@/^m?F(@6ԁ= e}:,9+%\_#hJ_m0&\+ϗ:%=WKS! U+0/m" C@aw3: 9R%7|zp[$_, fz|Q )Y^.9z}FuILP5>@@(g~I8h3ܕ}_-y,ÚPv.;k5p{irp|2fh54! 0!d@ ϘwZ۲YK̔yFd_+ +I]f՝ܶ T+g_wz#]տPyʤ:HO7p  Qx]0 g%[I{E&]μU $J2o2wOθʑ??a <ϜP?gxLR{wHVEɑx @ Υi SfXut@$+g^ƶv1;X9<Y`kŗ\)K/ZB!;6\vڛ4HĬɐT_<?w WÙ L\hwuyeJUѧi`Pb,yrÍJslo8c}̲~Lm8@/:mF*,Dl}$JLvk"cq~/t %۝͟'55&II@3Ƈc9E ,g^t5s! N8g$ j8'! p|(>~[x@bAIN37ӳ`37Y۝̙\N2 9W_+=va rtlߣwc$ ԑwCWmG*"3O'WFYzuUs4 I$ݥ3 @' 9-F*#P؃C2 '{̪O^Lj-+q mtzL8Es墷W|mI~˧}LnЎ0Gk' 7~ c̚y&;o$f"ufyǯ ˴$;77EܨDCdKg\Ju_̴XLL~S\'YօLz Vw鲗 dZF%cn͠6@<'@s]N@ duiC$vlRsR}RfjvGC𛁶Y#Ձ_];%6&X4:]ɨi]zqr[Dzׇ;R:@BGە%ڞE:$/H}J@J` @醔 0+ӱ{wIZw9di{g7K~ׇSN3ws_gBS$ hn{gU^g)3/70-hi۽G Ղ@2@3&*  mԩ%{:`l:B-lWE*n363>[t eȺG~-X#{ %v|)}:D[b1VB됌%tXu,Bqo_)3ڬɤ`g_F>c+_-euQ ?@puUAA-;K-٧&>OB 0K!߲A:êuw#i09|[hL_ >H:fQpi> TBP m}g'dI.K^yfhP])}O?-D|F@8Z" PN#K#K|X]>qUښ5oغG3{!TH@, 1Tn_RC2s㒵v)82KB4`2gb1+ydbQ  G 8҃@*)P4ȏoX-C5[kfV~ewKlZx9I fwZ  N  y2f$vФn43ofdtvHgɮC6 Gx8@~KlNͽضIӽʹ Ay+!`@X-2cdwDP @ pzQ?@A~$Oex*Մ, kMhsN@?f@O}Pu{@_ҐO  ' pB^D&4wú-3&dB2뙊^8A:;$+VW Gp[@@9~D_Z+5m^rj%4|,|n@4dF[7Jt2̓֒_M5+>4@$fǛ@L(_z(*+苫$?+ ۛ):i*BN0w?JjdKvxȪ  8X;!@~]wSmmjM֠ $3YW^2y!snɁ c ۈ3@*)S}\}:{d\@+YQmB}~G$k ? 4{"=rfpT"MeB S"`'YqwJӫnޘK|C JP􎾕?A|;2̓;e4/r  -la@ 5Ь!OKc=$r%⯫/lpe Azz:ZL?bh? lĤ(@ X@x `"Yt- ` D3|+g{~,DtN7;ʰ( @"E ֝I\  L̋@(`fjw7Hb6iJ`Hj>z V̀&ffʿ`}ڿGXz ƘCZIs@ @ \@uV1}^F^^"\*XT&`fZV Z)+ܧ~&C=#Û6kL%  P^j 0wa;ԩDIO+5Jŧ3Xn9[ ߤY#l]2dx3ݲUtrgVz~qAO@j`HR`59M§.3IY$4<(pj#".w# 4p=8yh=U`n_\Z&1'sm5Qm_$M߾QyR]GJ_X9  8* 83=*YD [^|.'',ӖJ̳f< >= -f@0wͬ qubF~G=~~ G`VJGYYj7|dAm*XЛ@>HMhG̀ 9@@TUw@ <_[3Lt@"ut;iv= $:M|: w& h#ܴ~O;&w@f2wS]%G3}qe`֎fvC [#8Mhm9ф/8thnIx@,pF?P @1ꗭ_4ƙ9ۿ&3>qj0ΐe.EM(@T  qk Pa`K;%;?x4]}m<kЫdYwq@ oSǴc GVѷ9"tŒ~"TZL΋E?D. G 8 _@/t[+@}cG(@H_..H  0.&NBp@ݲ ĬwQwNuB"39/۩E* =UK"G_S#ipݑRTJ-ñAJp]@*5@J76\T p@󵯓9]>r)o @yכ!$賩${I[ !P%-? @o@&To# R]B ^izݛPxR'F#^iG6bxӎ+8t@wfK S%@`d)@`z`.'P~"U&O_iC;Z"vH/vGO -z~?t[K='(Pl4^s  (U#MsGEҩ]s2e[y dty?+䋯C_ʮŴ(@B@^)0n%s+_t3&?[>!]: 8vίN~(_QjX@xB貒 NtF@<AIv2)#-J*)_}K݊%`YU&3OTYh \ܾf +$q+n:Osϗm/Kv߶\</_mk#hjy $Y_ \ikkEjB"Ʉ!U#@j  P^'nmn ?&:@ 3 ')[kq{sL4K_IJYgKl,@@%y T@˛X"g.)o.Za[SPG:vHƦu]j\;# P _Cmt]`4瞒ίa˅jO;Ör(@`lcq %vMlvԳ5̻*&oJ̝Wr 0>s,@ *xm]5Iw< *O/{reP ω@@@ Ϯ {vỾK|u2?H @@4m ]fơ/H*i[yT^%_X[\ )@`L"N@@K 6'Ap@>[vֺ#. ΢ TR`{mZm3w}+(]/ f_8\r 08@ gɌ6?$u?XɍP2"  0$NA@3S:26|@+@ET@ .;_oxm$< @6YzG_MMe@HGL@&#d-M|K'HV! @J7@\-d-ֆg< B@{מP   wH#I6$CO>.}W$wv=\7S"ge@&⳥4 A@;ةIY  )M$P Ik^+mrs/=d(._C*Nz~=If[y  >KJB8@ʹr.-oӕh& N.Wm)B@@~R" qf~>}q^r &xm%;4h[y  ד@N o=T.Ib-ОՀ__i @pv CSRsQN.Զ3 *mk0! B.4Y[~B~֎{*V! WJE8@m ^奩?ܓmLjp@@p5DN i}Ll~dwV! :[JF8@8T ̼SDlLlVm)B\,^L2(@`Lcq CvIy]B8)DҩYU+૩)mx2(@`lcq 76HQ]Y6SuNO\NnfcJF@`Ɓ)  zF2}=HIhy˻$rq;Ii[{+4iD#Z4su@sd/7vﵭ< rYhj*,(@q '!!pJ|:; x[M?5q`t}+=W5ۜ'J.@} g  P@W~2Ļ',:e=~qEHv<=6'ْˡ@m zTo7$O<&}B ![^A{ 8 4\zUIU P{EPr@G:^y'xup;y2% ǀfӵ+J&@`7'" "PHfwU$mmQR6ķn* @9q HhI9sX:Ö(:"/O-]mK9 0K6 @\%h%?xmu/IVq khSᏋ?($Ht%C  0>'B@53o"[}y'Ed}kk$Hx"ӻ _-PضD׬Laʣ @ 8" *e+7Vg+_-l-*hq]F5︯ 7D@*"`Ϻ6ۮ{%ʣ#Bd-[?t*Mm@ H@(9}Iג~_XR =) `A)@Jtv>iۥ5IwwV!P,)#A@ S-~Mlfew~ۖ(c t/`:K< 0@(@vC_"mQї|)#I@  S!`ϼmٓoГ.`rK-dl/@ 0g  Xk .~dBF!R`$oy < Pea" L@($3o; >|Kr.ʣ :_e𱇊1 @! `DBsNd!, Ahn]4#Y Xq9@A 0sL6+_ltض(@nwϋ?@*&@b\@ ̼SD&_@;VeVd2;tfɿ*\UiIDAT@[H! O r|l3 lѤ@KM}i>[~" N  W [oc{Hbc Gt|  8?  'h}%3S}_震q2 =tW$eq@ ?WG@`&i>;hdR)m ]tWd= e PFl. @)[ZK)bQK>{Dt<&x\b&=ؿW7HtjƢ@\IT@) 樜W\@s~ϵ# 2w- `K~+-eQ G{"xTUKt4*-eQ K"xM [ZtAʣ @p5E|TBsN]eKY  > Κ#&`qO$8A@ pYQ]@) D"4xgKY  ~ U.9i2LGR  [7 @ d~ym|GV! ;ߨ5 @ Y|--LvuJ7fKY   U&kl}ҶVu< B@ poQs@*hՖ [-}ܖ(@/@}H @D tbiy{lk?GA  n?j P%f>?-@'߰Ζ(@C@u#@p@ӫ_+ \bK+R/R  @% nc[_%m[y !@:V X`?"smiAl9R  @u 5 .Κ#?xm>t?/< B@ P=}IK@\(~ӧ$P[kK ?-eQ TSZ  Rt<&l)B@@:Tg*@ d~ymw!l+@@T_"@9 [jЁw, A@ P}K@*ojl[}s"mQ TWZ `}R-pP <[ʢ@nݿ@aSKĖZeR)4 A@j P=L@#՚̺6ԩ$}-eQ TcZ WVW\lKm=yE!  gZ PipX?7բk_mQ TcZ fϵ& wR  wxi) @4 u˚  ߊ3@@M#I7w֭>i~G@ 0&'  0y Mw_,z  ߊ3@@6oNt ! ^@@ZnvFIeo}KSѝ $v29U4@g pfP+@ c7  `kyN(*A20*P= @U  8E7K)qM=ϻ@~gR3kLE@pDp@wNhu;%[3fM}@%@[Mk@X Ϻ6SLwD O7@J PF@׼VW\|s2907.@8c$ 'y+4J@oU{)@KWB&M|  `{)S 5K=(:~'hrN yfɕ@P| @2 dKRzp2(7 Y][<GP)@ plP16toOMJ. @-dRR-" P,@X  ^~{wK;%fw Y%tڑϳ#J!!@` ^F|򱒊zϛp8yvlP1@8/!v $n瞚Tf*ޤ˛p@|O[uyvlQ1@c 8& O"S#p೟T_ /Ir 7 Fgd_(N9?ώ *  @`RM_Ea9O/=qI]`xoĎo ET@(Q+  $~4\q4^}D^&mohl".iߺYV=-}T2m(#iMl8$u{ehS⓬kZϟgtEP ( ~5b3?W?s5sEO@@_ү~PW?s&bZiOYyp?.(~x1  Ox.~PR߮6QEp4Cn=LMұ=EfS\6g[ "pz2& @}n\3Z:8Lo2kә @)g} ^wq k] '@ @P [똵_+/^ǭsVU @< ||Nmmw^w<_T /}tsH4_) @|''6ſ^ޱ"M/6YN]ͤ@l'G17lY7O/:_=s̺uZ&@ Yp&t-΅<| o}Z&@ y]>|y.N5ڝ?F./sc"{irs9Z}u|r @Yuo}<'΅۳+gH܎k]<'xatީ$7f3/ͧvbVmM @y^]YkeV\_ l͑zۉ6Wʣ@n0[gLWɻ-e뜉vl=/ @_#gڹS\|I޾\?+2 @n{XVL cN92&qk_|3lk/ @o2]q=Wk3^'D_597ܴuOĹ_/vlo#@ Y}ފu_m1[sviw\O"Iw4 $m4fo;oۍ+1[s[۳ON @3p5oy<[;0O7.sCN!l;GN-  @||'h{+w߾ٞ:<ߵ<}6O^!#N߽m?OuyZk>cs̚ @ϼϸi}]ϾLM^tAy̅lř7.O=1[]\mg̚63kr @%0ZK1ٶ^uku疿8:awn"{FqCS[ϸOm4`KY @ gDVL{hƵo'nmFw/~1ڳ|X2v3muӗZ덭'v} @E`}3Ok^zsuu~Ʋ.[q}=K->cuY @,59 @GZvOul:~ݺxb7\kޘ'6_볽[l @]b}k5Woɳv.C`fyclƭ~m[$@ |kztLY}qbZmm[yK} @,x>V}֚7Ǵ8^^|@|p G8 @# ܺ(?G2Y@/~?G;~=^ @# -|4O[n : @[}E,>d{w8]. @7wo*`VAo |3ϽeF >sߚO)`ɢ~g j: @o)`-ǹ( .ImV @= =VAo x%@x8J|  @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @Xzђn -IENDB`ic11 +PNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIDATX WohU_4LMmIJ 46C@  hQQ,@VZ)+ M0JJbmKRhwۻvosܞ}}3oͼ pXUb/rR˛C>8]ŨyO/Q;Mj[>-? rEIW?qi*,[=Mut:ђ}R ",UO{"hݣLgju{ tu5HXf $[<,IƔS^E&.z{t=Ō9P<%dMp8O uG=Ɩ"K ulF&P7p)HƎ٩/!kCCDS˾Řv YO*d76sfRRphkpͱa݀E- )E5j~;+lEg- O=K1̓ts65tnd|ez"DYIop#8 MX/wE62_ :.šہk$q:-֎  +(x/_?t{*- 2z`¯4h#j`kuBn'%NB/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o IDATx[klT>w_ iI!h*(]B%J *4$EQ#6 "GdT i(~ļ1`ۻY_[{!ʱy933g殽t4Mmko7n(zN+/| ogT̶rӊV gk^\Rl)X{z7~͚GSP[[K=O۵hT"Ko2-< fid4666 +Fуͽ͍3&}ϓB͋IӼi` LeoCyJlc3_K%S:S@t4݃QS§~3o<@Z6wA!iZ6=R䃊kP 9@J8%UAo΋++@ +!!/ClxisTB; RYՁRV!H-^?A[zJ";'X,G!'@G@埭^5cN)rur3 7$-ʓvBpX0H+{ J2% CN6 kO aWFWMy$x8D)5I)A@xtz#"|F̗|cHդquH`0mr;jXf!Ӹk 5-T9/&+yErr u=YY^nEm5qZVAgXlXQ0ғ-vtK/!=.Œ++# 9_ZO:- T粿OW.w#T|6}0)]Ah@Q$}fػWە[ 26!Ús|*E"\kq:7aN!7=Ȅn,7+8P~oxeW`جIשx3T"21ʚz2卟H#9 #o:&BV4J|2LuulM%O'dFIg^U C rGz<%~ݜtx ñ;_U `|S¥dF"|%G!`5gp"qe 9C:jj1>G~lJcûGr^o yL,j\Z3:q b'Yby`x,M|'*1\P~\*`k>+&v8`K gg3Ɵ{=kLZP,B5h;Y#F9 +!d+W,JEfˡ|GC=Ċo<Ͷ ;\B3 90HFT98Y)G<-~g<4&`Hp`N$ ++AP<쀄J- ǎɡ)k@%xCCKSg*9 f L Z>M$ E  R)KekuuHv43߾N~%W͝8 "tnт8o|*ŕD(ӈ@@%%F2^W{z2ReȂ1`uFsz0;"F}XQ4>yFR2xo)zc&כD pj,r6@ Db6 7).̈́˜ț,q -6xd "emMN\2Ba D9D}Etf7%BzQ 4'>5n|_ "/:V)k{qŋRن4r67r3F M}8vg>B|=f~&v۲LKѫ0s/,3˞Oε$Eณ:`mN{T|ƶ+ĒSexNwJE ˷SzMZdM·{lhmIENDB`ic14%PNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRvf`Y.ABT<ň[h"Q&j bf]vvwvvvfS5gU{{}ߚzz뭷*pB#ukɦVL7^33\YTOmoQ4gҞ4o#\H 3h@ ]=UI_J:,9 Nexk}J3s4]KY{/-[d4>5\oa?(|@}54ڬzK D 7: .b7WK\\.QnwI՟;-UUwuu Jm;sE6>`㙝-1\J\[eh]fa[*wɇbSM2/N죄 '+ 헶8hW(u^R6{<]2jiC-Io[8;˗{Gw/fP0}Il.H,tF`ל&/;S崭e$mV/naS WuVUCTL(皁\@+su9rXwWߔp5~}OZY9doy[Y^_a cccJ8-Ff,gfȕRr~<YlWhrfZCސS.MGJ*KXeͰK,'68B _+7B.FJʼna+bz5V -C| r9fXp22jU/{:O,UɦYLL:f3h:# RX*&i#k{ ̝GskL:r5! kBvM;ظIuhF}ǝBěGz<~'i=MnL!{fm̼T(^+L{cLyMuc!igflcنZŚ $~f[Ͱ`E}*36e!G& ]wIo_i)ƾOR>OLDXk2Η9F / 7duFwv< ʨ41F=t?v# ?p?8>ηXZR[)e?W|E-/7LQFfk:4i&UR&30U<4V(ӗ0[pn`QAdY/:_8,K[JPn*\dkYPܟTR}~_**/yz{-싨i!{i׭7؃N{gߊ̫ 35fHkXhHۼYecZi}͔$y &`nl3|JjCɄ9ݍ#"/B|k)|%3$_kxޚvj}k̇|\5O~): _'}M[ߠtW}I S a*nn=Q$EjHhRZ~S@dTȬ =9Ja[nNSH%`A'b&kJkYN|~N~.-+}>E;O<&@ Ƌ?D6YFfx2.rKSyRp̺ۢL━Vdbk߯g_~2E$?VQ|lVÇt@2x?3(y('@˳,i~<?V~:u gj +*I.ۣxRg OlJX2r"+am#c&QܨSiS/x 8pZk#+y2H6nˆ \,\L+>w6l^WXg֎)~sK:t22s7Z[M3u3 ggٯڝn`Eri`$h:st6C˯_}k[Gi@)ǯ[Υe' `-z M;[FTv,L1"#]uY@Z^RnLQOE{3skfJ@}MOc+%l>ʱQZ>3Le 9,kF)y.4O݇*r:Qv{ kH7X[6_5X|@P( [R^{$iQMhdKXjȱ.y83]i] TL#˘MfR_;aj_+jS,aWk͡Tifl4djeSBfir,,U^-yXsap`L@(L8wtbo I1~ێu@T*Y)IAU}cY5hZy&'8]rˢb5.> D;:F@>2q-]djU^\}S+ L)R{)ixgj)S*+q՜Ȥy+/hv9"7`$wsӾ8SQ+_X5kZ}SBOפ.?E9ІUTR`$e.Pm\VMJsjqMYN53M ^^* /nibd n&Ein*r# ÆFA{Ţ ͻ*4uD`6+{]g%WZeGwT5^dBca$t ۷͌DL4s[:^2}-iGTh=G! 6+ߍ&`CW5Kazdc) ÊN5C`b8_?JX )k; TOaT#EUۘ!]-p2|c)MPfBE>Q`~ 0ijO产E@Dh xd&}jc(i&VOӆ,IwjMŹݔ33e롣]oYZ&W I Hȣ3LlDg 39BƋRFC~n6K יAZ]M_Ò eh'eMp5:fRS!UV# |tCۿvRY8NtH`%V_z؈"7[tgWt̰Mʖ |S:pl3@a|3k,(s[WmTիڲ  K$,mvK\*n'^IL9MW쯖1MFSMjvPP/S .Xלbn¾@v Ѝ?RH)v΢L&mjʄzQ%. ^KX$NMNIL+-{I" Wܻ1 yr1KHUƦĿ-$?VPUy/qcU7!m4ۧ}p*5lٙZƩ)+c]C;q(0 @d#o]KɌp(MNZc+hWjn/-8 rFiqg[%.T2tcSFuMAU~ PAT6Kϔ^J1?_fvBV'&hGE#mGC;B6x3V}Շi  0Vi Ui,Zh^Sΰ} @=l?[L{mha%Pd5OlSEEfO43oa(+G ɕD3yˆ>R9ܼ~mDltqhP#WSEhI7}BrFf "P\{੕I=^+ yLhdι6IDATOQs*N(I?xoI[5훾x[ėTe/jހ(4_Fqd7Mm=Fc߱r=Bh{(}]WeCU_MWneUƌp؝l^ܜqqUO4HA7 #)7kMY5F9y )=k ~4r}:EheCo;<'8;)#nS߆7j(%A$2dyMN__mS|MskiXGf9nh5@ y,EKkSY3. a~/M_XnawpXPE$MHxZXeZ{My3dQWhK LeN|2t'SYgZm>KU-agE hZ  jܔS֩%)E7W*L01h@rVJTdx Ƈů>W_ɱWf*_|ratzP?@( RQZ#U٩9ckYk?*lcK$$468@@n TR)c*,S娾)B:l2H[iAUsOӝߛ o>HOJTHVߔ83݌Bl[4N}7G989gI'fr WP'H+!SAiةLEfiyfk!Զ43WY|^rqhU◢5l*|3[l'f<:@ܔ*uxŹk%PO43MyI3w@CL% _bƛamĩWy$^I3& A@'n X[[3T^pU雲fح(Źo~ |`^L3S D9835%,N}3Y.qhޠ|gA#Nh/ 'T)=tUFTN}eHX̫q"S3#2Ue 8A @%K#Le/ag Ͱ37}-KdU5,yu1e%l_qCn i@h 2W驼vHDSJ[>xg嫯kok~x/H@GS߭<5]|=6}ͧr&nq.N˳eXqi~-52ZLgofZ  AcwhUي/NͰg3ʙe/T>)7P0*APl6I5t 验5M0Ú׌׸zgn5 ~c`4  jnJSTZs+ChSy55)_awx  {L75F媕ckf'LQ"D(ZSٚ^tTAs1RiwJeʗ ,B@!0ah3B-4דּF{7mQDfhL'J4?61ECH:@Ac2B>~S|_m;8nSvմߠme4~m@h F *h!&w2ݿoB+;8:e)KK."n}{qcD{f@b` 1CvYQ9K [h_ud! +E0xeXF_\ @[0p' fz*-jY*?9k b#X wv;ĂfB3hH{a裗ٯy<EAZ/Ь%X+:1" F+종/Sfk\Z[S ͆şgE `!!4)n8%p)eW6ѱύxmX~WYPg>9!!4)|?A?,@'w6 $AF] зj?Z?=I{.w{gժ84 6SK*";76k#( K?W886 5*5Qn{9wPϾy>~2pc=D (J 7nxp]?FA$" "0B4hJD +}׵le(Z=X脃c cB3s) fy 0Gu1$w;>_b@En/K`Aj׾@ && AQT@%E^}A>4d [[sP%]:~y#`XAT e.|$(e˳V&weMikL6}wӪR>Τx2H󗭰fKi`Q )tM{cF* A4qc݄ˣ\Tϴ899X~][ uyNg2~?B ^'m9{z]@^JAW}yҳO-Y# [.Jh pA\Y!%޶+ eCGdQ e>6?n'AG@ -N/bC?0dzF06G`]CE `T,.jy١/('FGzTgP'xXY_[' +]<Fj"O7 J_+K8VMߗ`߱(#&-Lo8 ~WQc^j[i~UǼdBA#_x}C I/lĊO}K_F]th΢Yά]m+2_$/&eoZ vh!{!N&$64:,&'&i􏿷 zo[l8ҏ=9:WfwoTOrc%d qg3$vk?Ӵ۩8`&f @ ZV<'1'2*/]˗XGkY@`@eR֔:יO~qm^=hdh,O(i~G9%?0`}a0XoNOU<0̯m$pOk,KSvy0Ƶɱ넙b ؼp?v_NzvkCޫm:et/癀Or卄zY/"W ,/_ qcM۹-׎ An@YiW; h6PlHs8 '79%]J8pg6FROT" B@?wZvEo+{Yi'"7|Mn (C' ϲ ;wOngpw[9؏u*룉JO@Qc<&ʯ{t}^y=8mtbA APF'@atv%=S7쁳 5ákdLl80Q@Owg3Q&o4r/oGp a  6Ă(ﯿb^*LǢ_v3!Qz߾j͢D> _$vw',~K+T yذ.xޡ ,͒C>p%SܼmG{~kNy'o1kO['r=E !! $fĈ? R~z*R -'֮-_iq?F'7'04KL抴}o|F~(M}e@Dc@ 8ذ6Tbn3oxmm/H v0ڎ@%/^F%Vʼn1Jhu(Ж.`=h}BH@${OhŽlh;\QCqtmc4|XS U@#:MFYY(FwP~FZq׬GؠyMM GO $e#Y4Jy28jwjG:r[6.=P;4F@c owK8+"DfGȍߡg4g@k'r@ Xި-wEO]wKDl7XC'ɳ;mݴ{QO`?N0D_C@ l?XAE/4hry='=YAh)6>Ĕ@Yy %Z iE'ӂN'z <*c~6EEGR$41RTG-#|(-zco u+bߝ(~~_,P}v}Fx {Yc1@ F5 PO?6F{zyiiVPגeD2#ZA)Y@BȯH4>N-ߺ>n6grM@%_(# XOi{h)TfB)U,F@jJIDk|~0Ju{qu(Z{ [Dʔ6i3" q0">h~L l(t&M4r iI'Z) ꚿК9(l=.CC4iO}wSnMn}7 d클H|_fy䔃\A'bj'ٮM' SrG.>uO];K}VQC8doɖBn:ΒmQa<[X\( _1[8쁧-5Ͼؒ%/>ɭ{ֱ9&qrHp& Y~I3tf(Iw)ב 1# wp    # a@@H@]8@@@ `$pes@@H@]8@@@ `$pes@@H@]8@@@ `$pes@@H@]8@@@ gt9R)9n+9SC)nk&xQ󫟷ZQƹ86Fa*'Rri_*[J@3 JP߂J2+b%.%JsWC}F+:g)B@ [ojСqN3ꪼ4mir=8<#`_A@(ڰ&zM"|y୿䤥R٬1L ,Df)2΃Ax9 c i#+`` @͋&Q\2>q7T(;!jl z:=Gx-P+ %fg;Zkr@`4 9@N%*%Jua1Y,c'dvx&}8*xFuTX5 ՞s)Rd|Ο񱝭;bnZ@zTWP6ouQΛGY84;g-<<?X}7xBMjm"G'4^( 'B)_a~ַO?I0G7 0@"/~ÓfR@e7 mf4Pa6O9$D hZ J~Dno ;M}kBY{mk-/x @ @Gmj|%N' |tOc8L=`#|f $,Wo7_ /[y5[ZI?>?DP@`Uďeȳ󅋣 ֎ҟjB<D ]x31g1Bmk#M]َF@bN@@~ 7ᮐ>^.k6|0{qG>^!1Śs~ݻ8ۯ*+0OگbKLb$%*ُHB;&P뮭9*y.^eո;l_Zy7E%ܜqH q4@JX# |c-m*iQ}M4ͫYykBO&κ/̫OP*M91KVD@G (~T'' Jy;Q|쓗{bѻKD"X=7I=E8!IO$ ڮ#^|%k+zڟ#+3HϢUq#u/YI[oIH"D@ M 2O}"o KUhϺC'k$2$Gc&4Z$r߯^9m-?I[^ʹՊ-4˕$}sh@BL@Ml/<?ʳ8QS'y'Db B# b@䵿+2=s$/Œ_H%yd(p 8J=@[A}/b몪%Lw[u)2[/ *8Dm^hzNLe{RL|oΦܳOտJ,804!sW~폧7b>z 41W$H8s ~vU4Wzg]s{4Cyc k_> @G7o_+x͉R#6߿-FG :h!J`+zkfC ~Zҏ}% Y0:@}xA/,o#d!uav@48I'wYнtD8 h2tW]bmH@&w XXwus@'E3CNgUŽH}0۳0ٿB 0 z4>Nw';.-1(NLX<&-u>dZ`" i>Bu_A{(W5ΆJiLj8Y2.uH-_Aa Yn5~5Ǭ~L@^As0P{چqnrOݑE! UOeP\`ـVƹ D@}` R   +0b5 G@}   " VÉ΀@}` R   +0b5 G@}   " VÉ΀@}` R   +0b5 G@}   "D"Vg4:  ό`>Q   #`V 3s @@ ,]]!߼M@N<9A@:I@w-3C ';+Ă@ȵ6-0*T_O(̅#00zV_gގfDO7gB @ liyN14c`P8_3s!@@zw@u'/C@@k^k%"=AYda'I9sY,*'TӲ)k   PA@ө6o ,<'7F-KTNKX\+")rPM.^Ӱ*|ǚ]97 IENDB`il32À̂î΂ƿ@̌Ӂ ̋ւ ƺdžՊփ ǕՉւ ǝ ɞ ɿŽ Å ͅՀӀՀԳֲֶ׆ZՆ  ނنvfihfgjmg043.-5 i100/.0*Ui210/.Qi2110/-1.ai2110.12.bi210/221/ai21//-+**ei2 ^--,)Xi24P0//.,`i2pll0102Mi2;75210i2@R_R21bi2A2112ji22ii22ii252ii252ii252ii24s82ii21y2ii2Q2ii232ii23G12ii2 ˜22ؙ2il2 ΖN32623ׂ2ll2ǀ2S@2[2ll22o~22ll2:FA22532lq2qm_cbc!%$) a! "Ua"!  Qe"! " Xe"!! !" \e"!! " \e"! ]e" QRe"$B Xe"eaa ! $Be"+'%""!e"1ESD"!\e"2"fe""ee""ee"&"ee"&"ee"&"ee"$i)"ee"!l"ee"D"ee"#"ee"#8!"ee" ǔ""Ց"ed" ˎA#"~'"#Ԃ"dd"À"F1"ޑN"dd""ct""dd"*72""%#"de"eis32ʾ ɽ ׁɻׁĵׁ׀Ӂ׀ց׀فׁ׀ځׁ׀ׁ׀ׁ ׁׁׁ؏jUS__6323A^52103@`521036 `5223/20 `5223T4113n `522V224` `522C225^`522=C25``522CK25``522r25``522z25` _59OjI25_ _55b5{X245_X2296V:52X`HGHVT'$#$5T%"!$3T%"! $) T%""# ## T%""#G$"!$e T%""I""$S T%""}4""%TT%"".4"%TT%""5<"%TT%""d"%TT%""p~"%T T%)B^;"%T T%%V%pK"$%TL"")&I+%"Lit32!ɁƁ ÿʊɁɄȁŁ f͊ʁʁʄȀΕ˅ʁƀѕˀ̃Ȁ ¿ҙ́ӔЂσԔσ̀ ԖтЄµхф҅ЀρĶց ҂ӂҁπ¿ķ֖ӄрπķ+֜Ӂҁπ¿ķU֗ԆӀĸI֘ԀԂӀЀķU֝Ԅ¿ĸfՆՈՇԀÿĹ;֙ՇԁĹW֙ՇԁÿĹk֚ՇԀĿĺ{fփՅźqփՄŻIփՄĻ+փՄ ƀ żփՄ ȁ żփՄ ʂ ŽփՃ ̈żփՃ½ĻփՃփՁؿօ@ք3ւ3րŀ ǀ ˁȀ Uܓ̀+ς΀ˀ¿փЁ΀ˀG ՆՇԀрЃ ·ڄՆԂҁσĥ߄ՆԃҀρ ȦNՇԃӀҀЂՆԄӀҀςɤՇԃӀҁςS3քՈԃҁЀρIց׃րՈԃ҂рν<ހՈԄ҂рƣՈԄ҅ʣՈԃӀ̣ՈԄϣՈԊӣՉԆ֣Ռ֣ֈՎ֣݂։Ռ֣Շ֋Ո֣֋Մ֣֋Ղ֣֏֣֌֣ՀցՀ֣֣֣֣֣֣֣֣֣֣צ֣إ֣٤֣֣֣٢֣ץ֣SZZS֣ QcaacW֣f_d֣\`_֣ `Y]][\֣pcbl֣⮪֣ע֣֣֣ դ֣֣ק֣֣֣֣֣֣ږ֣׀߀֣ؕރڂہ֣ہ֣݁׍֣֣݁݁܂֣߀֣܄ݕ֣׊֣֣ؗݝ֣ݝ֣ف؁ݝ֣ـݝ֣۝֣܉؀֣֣֣֣֣֢##mU/. --,5BQ^]m/. --./..,+=X30/.-,+)M@0/.-,,*,'O10/0/.--,,+)(Q10/.--,,+)(N10/.--,,+)'R21010/.--,+))N210010/.-,+*(.I210/.--,+**0D21010/.--,+)/0@210/. --,*./1;+210/. --,+-//16+210/ ..--,-0//23I21210/ ..--,-10//1/9210/. --,100//1+ M21210/. --,1100//0+'M210/. -,21100//0+3M210/.--210//+AM210/.-2210/.,LM210/.-2100/.,M9210/..-2100/.,Q$210/.2100/.,U+210//.2100/.,U210/./2100/.,T210/2100/.,Q210/0210//.,O210/-,+*)('&'(*,00.,J210/..-,++* )((%#$(--F2?}ٹu, ++**)((&#$)C2;--,+**)('%%<2Y-, ++**)(&332d..-,++*)*.2\.-,,++*+12>/.-,++,A20/.--,+*Q@23N60/.--,*2ؘ810/.--/L2O>;9851010/..-+ 2021210/.0S23I23210/.24`Ӌ210/\;25k210//2P25o210//25o21052l210]$248:;;>t:;:621003\-2=߰^42103R2=212D22172r521026212m52122/21121122732231רU224221003l10232247p34543225o225o225o225o225o225o225o225o225o225o225o225o223j22{7227r@225A22622h22?3224g}t22\22  22!F22T22 22/)j224ZՊ522ނ6225V222242/62262f܇2422 522pڸ6262222222x2422@j242BI2238422Kc232E=3223[a_H2L`_\i2522D|_`_\v25223Ő2xM2 DW2322:2522328632E242 DX2422;2522 3夨2326452 DW2322:25223252Ut232DT262522 3⛟w232:262 Dc>@?:L25223Í2qQ32EV2322327542B252E25223242662 Dfhgg_>25223242Qw242DU23522324282DY36443223242kdBA@<2DW23223242A󶿀D2EY2322u232S f232BL22=R2A w23225gw23222632652452222mU  '8GTVI  !!1Q3! E@! G! ! I! ! G"! I"! ! D"!  ?"! !9"!  "4"!"!"!  "1+"!   "++"!  ! #%$"!!"!  !! "!9"! ! " M"! "! "'M"! "! !3M"! ""! ;M"! "! GM"! "! I9"! "! M$"! "! M"! "! N"! "! K"! "! J"! "!! E"! !!B"!  ;"/rޅֳk9",0"K("X#"O '". 8" J+"$?'!! ! "ӏ("! !B"A/,)(%! + " ~"#! "J"#:"#"! "$Rώ"! W'"%_"! #F"%c"! "%c"!/"a"!U$"$(+,,/i+,*'"!%S-".ܩQ%"!$F"."!"6"!"!)"f%"!"&"!""!""a%"!"" ""'w#""# ~ހӠG""$""!! #a! "$""$%$(e#$%$#""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""#^""p(""(f1""%2""&""[""0$""$Zsh""M"" "" 6""D""""^""$Lҁz%""ۂ&""%H""""%" '""&"Z}"$"" &""dֱv&"&"""""""n"$""1 ^"$"4ߪ;""#y($""=W"#"6.#""#zNUSR:">TRSSO]"%""5rRTSOk"%""#z"n?" 5J"#""*"%""#z"('#"6"$" 5K"$"","%"" #y㝡v"#"&$%" 5J"#""+"%""#y"%"Hi"#"5G"'"%""#yl"#"+"&" 5V/10+>"%""#z"fC#"6I"#""#z"(%$"4"%"6v"%""#z"$"'&" 5yZ\[\S/"%""#z"$"Cm"$"5H"#%""#z"$"("5L#&$$#""#z"$"_X321-"5J"#""#}"$"2򱺀5"6L"#""j"#"F Z"#"3>"".xD"2~~| }l"#""%[l"#"""&#"&%"$%""""l8mkj/>? ???'?2???K? ???'?<??????????????????????@@@@@@@@DDname icons8mkrt8mk@ӰGw D ~                 #  +  6  B  O ] l }           k    $    d     +  f                                                                                                                          &'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('&  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   fritzing-app-0.9.3b/resources/system_icons/macosx/mac_fzpz_icon.icns000066400000000000000000011265321272363601300260360ustar00rootroot00000000000000icnsZic08EPNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx} #Ev% ; "C~QχOqy;0upavfr$$՝NҝtNnս:uΩSꜮpƩ+FL>/(\.nXǩXM7nnp7zkR ͐X8M%e5Kwz0%b\iM1%Kz뭃MZ7-Ί0!eqh׮}kӖ setn&@X%H>qܬpyR@FQ`41HQYFDҩDOc_}O:Ѷ3ޱ%;Y+ρ',i{'3lܘ<fSlI4-=a-t?Nnz5XxNt(PDD"d3"j!EG"94bC"rՅ߰l ǀ==07X8=dG5xಞ9A7etzxV'㸕1s6-mri2.b<I#t&8L|{m~J O:mيIzn'gBH5Woxof%1 La_F4ni'/gW씝N\Y\ie2t ʺ'MYc~? :ozYˠN1*g~J0z؉~`(! 9vqbfye:[I)Y%bBCˆ:'{L]q@=(cEjIL}.-a;s͚|4#F5[14N CYDLV)+T!% Xcyv,t-e<'@;tqYks<VcNBOCi ,c>$~H>MWħ6jN͖<:xvW΃^81bZЖhۺ?xik,HP%E܄:2`ăeϜ0{܋fbP~/̭}I|IGiR0G6 RT"d(K 9=xy L' a /,AwF8!ѹI1|Ųـj=w^ÑeZ=\ e:x F #Ƶh6GL:fڻɳgLB7te0PNȬXNݩ6̻r ,]);U=9 0NX']q7|~@0NRL&Tap4?fDu2uY: : eF -%`Y'ǯ}s?X0;6 OC˨A;p9s(Bi*C ePh0HES!XݺfUܛ@K&FA('lw8˹.'\9U 8IziyN-w̗]zyj3t3e\^n C Nzm`0}va2L'g7aɵ I<(6R@ G%,8fmhݻ.9v"3N@k<6k]ﰩ'wÅ1z rρ*TZ Oаs݁DK,i#6lٳg0Z5# u5Z0Z#_X2U >2O pr@lneOaK[yC6YKgc"f[_x(uTdB0\ͅȵCNiX9Y2-] \w!'8 `60e+@A^^5&@g9Fy41/7^0B u}S `4s}sWXye\25.i%\7{PBA C*}w,`eFdMKA^.Gitr3lzϠ J6(pղO.Uflo=8c/Fmsx;D&O x`3ȏ Z}@zOrpb0.4e`\<8f^ApeΏ$Q`1O|gy.S Su^07s?/(i9L*~+{ j=<<9 O1qb*e|JP˨cx1:'gk/t,2Xի׬s(/p,#ڣȲo[{˘ Ff6֬ EЍ^>csNeҭim ۢg2eӔrsXLn:~֝=)^.A\5Sf-D)Ĕ]<ӌ (BN[?21TíJzX7Z aiL\L.up旍)i󺬀e/4t<= N}|0J/}3y Z"dp:ʡSs1ss!=9= /NxEg={9ռg*(gjWIW igS^u@AtNZgyN ^6*.ܵGϜE/SYtN/wd;uCc5PtJJF)cI?,Lljپq96 wQK5TxpW>şk^o!Y }LH𝴺B4p^wx8mˋ|)_N,"819,Ұ D]H,Vۡ2B;{R,  XK4h:а  6?`I?Nq aiwcD~ (j9f|W1PB<؉ /6 W]7*ARq ci d㗻1ʅ>YΏ FuK<4p8`/pܰ7yϠN diYaNKU^/+"" `δB_ƕiN'JnXs/8fb;nִBܵ+f; 9`,,Y1 COe䑡a|܌V`'NG(&l^;cؿgP'[c!5ntБ\y✽i1,MH% 2 o 2 1 yd,*CƷPF/&[4>9} :Q 2>HR U=q.{3P5adJ6o0A#Ƃ \$ez\|7'0r#Zyʀ|$ŇJ!1tC!$(^9'0 "CcƯ|+N*Z%MhU&]`l١D9 gn2\`@˝fUēm 1@tZ:&[D9~a&@N`gM,(jl8 ҐMX:rb -NK0CC"3>sh=(Z 2J4B`4].d_K%OC[Sd/1ì1Yz/'"Fb~1u"'n(!lEdtJD"yqbOo G"2)]tRC/6+p/Frܟn*A"ܟ$ 3!GWP9qPuS{l}a ܍&M2-o/B}0rZDi -҇\u8:ZNgtAr0 0#P0Z%h#g r-pw`j{" ?i&gkAb94`7bڏ8JDd(xM0h:#3D&-:TXK p 0-38,FH{7{9@ݼ[*ԏV"6f9D{/[ x8{|@bp\ 8-XUщۇ&2-[u3nܵ35ez{ h.ęk9Mz .!6v3m4g]ANf8ʸuWgƔc!S!a9`S!f2bXNŁ8:1&40|>:EhPϻ~vvG,<_6G0fx%i䄋AΧnK>Y< @݅q5#Dh$C{ sIs-`K1qoQiҙ?BeDV!t6C/R:u@Vp   !aV9_" E0 '@!sLj1Ǟ;ynBk"6nb?$:b$?2T;/%utcﯜ gK<8. "Hu=K$j3qv}43t[ow2 U #X1٘p 0t0ݧ}v`'f|e F_ jo\:ԽbʗG1~|b*d\%ŨCp][Z}t?l@ٸ A[:+CfkD Z)g,U{F3,Nȸc<ԻSdꈷ ];kջwC,&%-jMh  _Xt-zDx!wmv(TieȫTJ3},6dZO8ABCN'j"]HrtGbWCNa¤Kd%BӴOSa@DYN0=%#g.$2=NIs4wL1ϔOGmXxe MA¶-ζJqqm,!@|! Ԛ_DO;N@-N1Z“$kgѣŨ/mE?4z\1a- JSbKPB,K}jbk0L/$qi` g!'ܯ7Odvv:˳ҩ%rx1 P E!P:I:{NE?uƱ3#:$xi XiV#}κCi8c|$]F,-{ וTi~hH#ˬGD4ܭ֞kV!57[ݞ[Pg֬iI+J)?NQڤ$ :tVI%pP⫡˺s4V ' L Y/JpF6/ȴL\=d'. 'cJ+Xѳ-FmI}2Zފ1`S̐s.fUd2FX b` g,p0=!3?NNn D%(d@:͋Qfٸ:5E$Ǘ>^qOِ*eFgԭ>|tXڞWՍ#rp7?#f\a\c2;"@HAtˬey9[*>YgA1pVrn=(^TXrĜ>ΠpBwfeswbo}GO? YM(Fgi4J+RLǷ~yB`8 O1+Vq#(F"2N\| X6 vYq݌7|dŚ5kL =t|jǍ(֞XOnD ?4 I,ND :sz9 PwVNc5jnkLjv ) Ϯ(9ΚnsDB2p@kM)^L[8,#HECϛ}NYCUwT&LˤjVoyT͊nnJ"9,7A7|n W.V)vT:=9btl/]mA W8PtC0BDKm[KSQ' 0pm/qa hDZ2H10UO=9uQG3rH ppG8pC R&MRwzr?@׭3,+ +5W p jE&Ioyń kóEߐBaYF= SVcC÷ yṼ}/_{̱])Fߠn*S"TdԞSFg$h*2Z} 71YЪ(/2<55U9#4g J5TQlTkb+s÷c 03Ƀ{+O6bVTak3 %NJV~@1T09pĸDv᳦N8cX@1NuP7ՇbJCh&m۷{U@v )*ȂӦȣFUJ_^>C*W3OrKu pg:"wDIIH*"`q豊\ QG:03b/Y<Κ| pW~lZN5 .[M\n[ҶH$1,ݪ)̼WǷ+g-g8s xfb\O[x]ȳHEL'"3Hw OY<9?uHn,zU['cD([]=] lGQ1^s_=ľX08zj@01UElWM'<e_q:ZggMx 2=mAC;]7|Wt/['3E5,ȯ!3{.dȖ#ux[I!PK5V`@u{bd1p0zx zK9lw|ab)&+bݒEr<qA~> X]gn:ԏLLuZȂ;[3Z ]65@tn:E^qzGs,ܥXt U%`~b4%C\kFSM򙥓Cbƍr_z_Q-)Dsky> ҪD9,a82Dt'rOO-nuP7+q"uus1y$X#b<8T宅7p.Aine-&pj8yE9/|P C4&ƼWx ;6]"WK>$Wb| w^`HG܂kƀǟi0jN@-ve DF~ tV il@M]ظ%ϐ_v/!28Eٻ`fʎ_8eehr? '*3yܜ1 C3&hLCp:·g<5T"Dа3IdCDnbd(G?J(m N oN-.l=aM`M+mrUUp 0T&^=}s{E/`g GVtų=.].E/P`@ݻ@ }|SL2)gxOl_#yq\M:X]3o̞1>k35 -7XVZ'k(6ZY6Ց'IiPO;q7D?衱pgV,;mF5,<׫vtn0_=E58:xebw&寚׿d}b7 6}]a+ A)Nà8!ȁ= Rɕ'K xG,:]D˔i"66Yj|ϯ UZ=KRQxm$nܵMkO)u QPMd?K0D\/jet2a~Bkߖ=eh1Ũ24cӿ=ѶzGB/}]bp6&IuCS: M 7lpt),$ zbu' ֆ q-!6Z ՙܱ] YB T?Jr!蝎fT:YJU| ى F(ӈѭEO3_^ TS568 c4gjͤt7-N#a odu ujdtu*VW.#eEK'tNAONy@@SI6tsqZTIMk 1O)Д:Z4b>v7k" 1<Șy71Az qBP=sa0g:7c:,G _3ncs 2UU|FJys99fFh2:W2C8FԀqԶh d0 db1M]F!Ӏq!#@-5`@-m2YqjjmS@4`@:Ĉc4PK PKmBB!FZj8Zje42 1 RRۦ.i8uh06u LC*' u-606zn-EozWCpp7~(O ޣ^s+DQ DR%2wY?IdRBXΥ< U5`@{|0LZ̼&1?__SQO]'/Ԡ&l0{?oh? C%IDm$&~࣍%# AIB!ÌxFDGLǽcbg*5aрq6\p'0GD%蔿TF].1B3 2I1":2`}a?%:O8, RUʍivsE߹A0)4`@#t#cF 7oEFþs=-B6挌%4PTB8Sli Sp3op/.:6 CnhBB!y-FV.r@HЬ=JkCs_)_1rxR5TxG?~aґctRS10K XUHT$fW>#>Hqߥdpfj{F3G$o}*-dCl!wzH%34EKD_N38ϸL:#:"ڷ_,{z -@G(1Fрq!yG{[_\)-"Lx':k{ga4W”Xs Zbatݮ"*vk񭞟xm1 &h/bj8v 2a"[};v_?ׁ>ѥCCXO и0 }粜i:3D{"|aç'v|L1Kv6rn1z7Cv{3O=-8ӂX/Kw-67N7zAP5h+SV5 YM B1vz|lx7->_|3Cl]Uka\} P}_]1m~f UАhi-.]^c7~_l|v= c&M'_fq޻?,:ee*CT] P]VΝ y <7uKEGqvEۇ lPyBgdu׀{δ/[6s|/oz)Y4B,/ .=8-dzō=#c PD9)"C룇uz{Hdxn?vb*~̌}׼ab{~-wW_-0ڮ.x/ξ]|?.Wؓ!7 <}IKz'e-ЖboEuj8*;KBpppz QgУ)R}}y" E}lO; n}5`@}_Q۶5#>gOU`[0o+Ŷ T0ex qG`>*6:ڝGgmk#bF漅B|ĄԀlkSņP͞+\z8ih5OeŢebFOq_=B21PoH5׎Yãe@ ]ҢudѱUm">m&"9BбO l,W>#z^seWli _23bֈeSgē;wnmW 5WЀqãM+\5`Z h`xh8ϦF0U-h40<4`gJW 40`xiрF W3;K.>g+}K5:O{Kz'[7_s+JYt_|{+ Q?9ڱ5Flx Jtt'2] ]zN[V>1?WRѶW/ⓦxc3 Ҹ-za,p,vm~"fM8/J('| (}O:=:1.1} 0qxOKͻa<5K?a8ҳO&F,-'^85GDl(&- vܻ?yUg+J22y Yt{uZN^鬻|ՂOOlN 6;02'9Bb}e"[,lgEKlƟ'ᵡ0~/my1',uz9 hhnalpZ0G)vn0gɑE+Fq mqٺB'Co KBcmfƽYI׀qc+*k#ע^zd6_q?*CSîmv|jfc}h`1 p3L+8k߄Fyly ;cag YUq]ElX~YCQEOCr1 06+rkp~p &ҀqǪz;j}˳[oY Q~umdsHt?1,` 5J{# lpf1~w,P t 15'F󛟉}YI]m)7K_Չ~̟ؐ9C3oOv]_ܪPnC~ 򶟎h޽to{?,b#Ff;p˛z(m@]I5ߵU{>-2쮫Djm?@{A&@j{y7]֪v˝479Ǻ7cUB_g5hxQǪU~5mKexiI %6"h j8`8Y?g~7s?Z6s6߆pXb%!tۯsE y+];-ir&khG|nvboEiߴ S,%fN~PZA+&W-TF(3D/u/-{[K/Vٟuw L"۾w-q&h9^VZpBq::Zyo#j'wܢn9īFm/:zA|i5o9Gt{Лݵv;_T~nİ| $5"iΠB96o:sQPAf Q.Z=GzYڬ 755` 6f0`uiр087ijc? =`*7нc<Ǵk XPNhBhB r\#k^5F51uC pYBB8" 6vlֽ{{(EGǻuI$ذq0z6,8pt@ڇ UV: $ Fy#gl(.a2IDAT! [aЬ4xM:p8H &^< 'c'P}BҠ 0®6peaG9*W08n4̙04g|rcx(3~gr(jT7raCn09(cNuSh`tcg8njSX7 X i7e8r5 U&(W&c4׀~                         ]X0;IENDB`ic09PNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VyYRUu90(,Y *ŴvA#e]?aMG]0$ISLOOOT}^zUn<{뜺u^YZIDH*Cm)i0{f$)SmoQ rh{fʙFȓl|$+ݿ4cV#aufXH SZjϩRRdp*k >0X*=^,cP+wuf1ڎ$'_Zd4>57 +JEyH>ɾZD|os QÍ΂@}**Ko=vRy%׶*n}(ijڻO{]'>g;06.uo-[o֯ev_xœybS#>yj<2,ON%-d5T C| r9fXp2:5/{G<{nOXRLVUt$fѦHuF TS1GQǁS3ҳfSt^! cBvM[ظIuhϣBITjXM#Wb= ?ۼh|۶PahFﻇFZU>3/ ]ӓLQyLf q= 1~7{>3r3 61ƔZ&)')^Y?H72a3O07pD#)}+-s T()߀K6R:'Zk.YF(>8ƮgD[~BCk vCKR8~Wݲ=V.f24X۬-)CyUVKL~axpiQ/ap80 4?OgϣN罀_x-Wt'TZO 5Ȭ(aO*A/|3xV6×$:ə0_upHwKy! ._ILk O[SnWvh=9!a'WzaޓOS^Dc<@{oĪ0f(;_xfA%)UCl4ͬ_j6ofEiwQ\yL1`>D>A߼25CeU0jr֧_k7][g.KM2?3Sڐa q=v`9-ǾdH~23lD8 eoԺS*PNTgYx :y8:;935CʯJ$Qk ~T白7S5TCf^5WF)!L@QlOsk*;?@QGв/_WY ϓAzqˌp ŴsWRl#z_UϢ-ͦ )e,]kn-UlY,#,d&@ޞݛ]c hwnJiEjr'( A -=%'̞13,93KWZf-ft$E{Q+!4MV߫ąa$njZzuK^C*BB LܳlQ !M!wzj_c[IhMv%ke a㴌~;gqwWVcUT<01/|%{sռE Sibu 2 "4uTj H_LcTXRH7oZhz4,qN_H=مmj1yl5=q> 9:\@_Sw+C|y=@h9oڛAJwJB}iL/a3M⵬35_JsRݼHYYWLaGt.ZL]>7q+(omόSBn6garfZōU~.$Cƪ~th-HQ%vzb'jгAq%v\Ix!D%eZy,aiI%c3]q&?1U$+4&#L#˘Mf@;aj@jR,a P}*43N6%je3XY[ysbB:gV6n )%@v3N ݷ4w کBvp6~P:UzVjpRPU_*+rؙl[kVI2R|kUnEY%҇aGGfbN +LMKzţ3҃p-Uj/R84<=Z԰數JNycbGˡVWΞ\@r=w>!x0jE%My P6لkxͫTK1ZJ}2 V5g* @RFN+`z#<kjTiN<Yi/4\dիK%2v9-=YmL!Ť*mf4Vo,`r t {wQ- @ql,ּM(ZG! axz+RJ^{y`v20:LgF"&Ds/ckKU-{ݒT`G㦢W_Fa8*Upn4"Z ga-haE!0\%,_K˔Tn-C7ՓhHq6! IWKN6}4ŷ)LZO3%, 05EQ32 "J `1_dL%jm4eʒtg^-5_p)~87|fxf^?t-AD=6ḯa$u7nO׉p!ſT@#|?h ʡa:4orVjKkXʘa7̰wz3^)JIZJMo*G03 WL30:LT~K0OdcDɧh5_d,HMGd=O.NL$@*cS&6 ğ0A! ~4-|~wT;+H.q35|ʉ"qRf)ՙ[tzltfh|uFT6K[.aP++.G  ȕD3yˆ>R oLnvc"6l9xjȕktѬZ4\hkn ȅOdO.ON^Y#dN~L}f7IDATJPIw@9HQJb<_E]} u~&Χg[Җx@/{.3NKQ2o@L4_Fqx/KU䖰3NA@< RD7Ṭ7gͫL#-`Dc @r6JT6} kc_I>W_ɱWfOtAzaz>@$ RQTJ>)ckYkR>.ΔY@_ivs^vj)68@@n TRTXOfjIQSve H[iAUrZNӝߛ o>HOJTHV̫qff)[4N}orqr,ΌYNvr+LҰSIL8׆#d,UW^g8")@ST9o ^R3_/{l1^eV l ux ~A i !Pe T"y$^ZVH<Kk%r  SW/UkTh*k=|Z^0} kY3^7F3Ci U*Xqz,(ZUt3lƙe$Y^uk_i^hg%F <D*`S$UnJokLWei҆tײfϬ[4P/HpS"[*Sṷ-uN3 Nܹ @ڞ*SMaM|1N3|@bA@lMe/agvRI7!83D  K+)lQtUԕT*;뫖R= x!7$S)K`"\d@Ze0*A @BQ!@F   506"@@B  h@@F@F@`M@ڈ@  Q  !d4  Q# j#y@@@ 0B&@@@ j`DmD @@D 2<   !@F   506"@@B  h@@F@F@`M@ڈ@  Q  !d4  Q# j#y@@@ 0B&@@@ j`DmD @@D I6R(?T*cރ@ؕM)[ @r7>KT,-@rHXQ&w٥bJc4/JSQ*ST`6 gd 2@ ji"GsO?[h9T Q?HutЬ=)"sS=H:ZF !*Ϡ$sgu6(ex$ :$f;xKrgř]BxY|AF@J~bzi?I}}m%۱~,k@C3+Hw윕!˯Ɋv_o,A}+,2j4f;Y87ϧz֟J矜@w1\K{nbA"C3 VX>0ZWy<-g{C[+:mD@ &!^A s.0Q ͆K7A `DpP R p6CKRʮ\emcmn_,WYP;|| A0"8()}. {Xq@{UCxh߅k &aF[ г@ZxODgv,{uZ53b@ZN@ˇ TO_I{[[Mj< HuuR`gBjdž FCmM@?³opy=~tH@G 2G7o/}φѢ3oQs"D@Ĕ'g.xV&~9᠎%i.@'@ h!|4&n?3g9M^Wi41y缃tVʆx 8kC|?Q3kd&X,P .:$8猳[(0 0i (lQ?SД !Mنފت/钯%ۗ7NydВQ@ 0@{~dZq??+_u;{Q~ u6{6= UG/y7?d>>/[aȗ Ӗ jc(tcF* a6q>Dy{+βnҟ(4+ =B~=z%~љfixVڻi _ ]?ZuR:5 QA:e G^b~m F@='#Кa B{ԑlkx*?o)p1]~Hc{%|ݟL6e}ob :JewvE'I?⧬py4+Kŀ&@u`=Z;֮}B;y]a7|k?2~?Lqn^'|Ł00♃e{r>A"A5>XS/@hz4wB-P6g##WLkof-ɲuz??L%a"O]}z_r,&ƬE~7'wű175R ʂ4 &@D $ZT;JP/qśϠ=N^^?= ۓOlϜI|oN4ϜZ eC 006nTvo\x44o|tmrEQx֮ԓNL}2"v!& 3 7 8#FmIYWg5A{@%={RlYl葹$][~:j?(Rx@e` =;7{NݐU\̦~A4XO"# v2K#.93wA@-Z?`< `<}={*]w7w~n3z^@w]}5 \}J;yIa 8kx'> g=zZ~M=5 tOoA F P@vŁ` mvon;4K[bp۬-y\_ &^D6 2 ?+bDC۷ralHE]Y,2\" @ |0gA7ёaڷg󇣌ݝ捇uOwn&ن@;A `{ }+ "+BО@j-r'twgf|rן 9z9H _#,u- -"El;(CSQla8C/r[GtM?}Ngd>9w47Ky^L8>ŏ˿em7v4LlCxBK K0b9l:*y˟lmx[x*{^Ѭ+d@ڿ ci*~%d]ҷ:ϐLӾoOc4H66hY񤫞 |t-_b;vz>I]KA?R!֔:Ͽ[KOOW(LHqdv|XQ/J@+*M"k7=~=sv^%Jⷄ*OJׇ7B;%^r(xhw6q=?)HFQmr!l"?(%٢xu*^FWډ vM%RE޷Rqxob>|#o?\`>P:%! L#`@s oHw>#8j`qbSMl\ @`4)*Fy}2*,گN#◵az;Yox(O htxO%mkm(~ۡ8]Q.,kᩆA-&h-!/Y(FQn&ZkGؠyM G'f2gk wˣcejaxoU~kYc֞dhj#6^ ov o;鏨("DfGOGO 7RfA \x.otx~b6Z|v/ˊ5),JRI'ӂsΧ5S/2WVgCe;hg7О_7oW)&O-Z@  A'b:ht_ϦE|8?Z "܉g|]]?3T 4}QZz7RGмCe+c2"uփw~TZa ^f!W~$QVVo_M~۳]FH%x ,_E,/=HUu 4p}= *x3*eF@jRIDk~~0J4cSGhYNI*@ B6%ә4m@c7/nEE׬/CcBkytgv_HcE;)Y,"$<)O8N]hݱ1O@\|ꞺTgzVՇq0 |9[G [s oC ۷ȳŅEZ?| >q@YcKF$~Yt%a QÍƚ(g+wD?JSAGhtzp fn@@@ a`$l]0p@ Hࠣ   $ :   0p@ Hࠣ   $ :   0p@ Hࠣ   $ :   0p@ Hࠣ   $@G.7.T*E_kmw[dU!?@^J̟ݏ-W~G=ΥB 8r[7D|k@%4 `BU5rX Əz=Õ z׃W!tP*OO{fӥZiLRRJ;hqOd s-R1LOV[WӞ~;4C iy}′ 9WXJlEZ?cK @mC15WANe>E;Fgђ]F[?j1Q1WX_b~g;h"8gΣ%-'sΠsp@CYщV교2k) Ns'-OM?|)jp l@Ȭap`c#C@dA@Л=;ѵc FU x.ziET_osP..`e gdF## [)kYڻٛRO@= S-(Fɾ{~K=ܼ>7*TKU#kjyG @ rH~r@xG+(r ?muοYl|${V\-?dk%W̦맾Ny羝~W-lGߥW|3IgO1H}d1ST;Tf=ͨ:P"Mʟ_}yΉ4_w\{U.8оnM۾Š˥3ԿUً:bN@'+&3VvW~2߲ZȀ=tįg%u{Rs֛(3{F7b@ykNVɀqny[ `|W"J@\Grǖ/߿,`d"!30g9R;tΊ1TYA}LIs1@zߘA)UӬ^kk7ݬvSq(+C^Nx?0n^Zsx R`ЄQ?(>~lv7$_Fg$sH#f%mZ7Z;ʃXIzkqN^O?P7%v~mW1 9"C@d5^~_<}E:'?ΫЬ*8C< 5J2Ek[{޷Fg*;&osҜ*ƹY$QO`Am$+oa;o!U\v*®hLeGxDw7::Rb37< AV?~jvQhbl *)ܶ-/ms3" hA0 PO;Y$Am?n>yEֆOyB{_?76Wqn:;TR0Z5^oܹʶnN;ytKE<_7K> y.dBƹPa h@$ws>R]_Ь;Na#:0~(!J /' G#6^B#m:t8yXu‚@,wyjɢM|WE9< `AF?[;gxJL{Rp"9ƒ _R~`~<5\Y+9q_sJqA>0/<QAn߿''b#<80j EU}A^5!-0QV<@E߂wWr WxsG@35BVH]1Y䭀7^OŽCaDfj% B`Xw.IQElw!(xJhS|EXWRD3`#:00 pm4tW縎 @6U<~VlI)‰Dk Fl `.ko;hG*ν8y p^_t!Vro#Jٚ_U0Mʼn"D+BBB~,zXoqWyoJ|&i}RI' cM+Y_H_?W]VSsN8=ra֍`zx9^ 5PaG|׏0~*K*_¯ƹj(30b6`xA؊~~.Dp 87EB@\F rF@7{ˌ\c0΍C1מ;^7~=bcB P5B֙FpQ_=sql'[v=Q%чhj0MٔʂJbC@l*4^9˗7qoI~B{k|rx6o"qT/!L`A]3>F?Hfrq*_ڲ`}֟Ya=ziBEs@9r@AJ!{ X/kR]4Ùd+ 6'qn[ā 匟<̗OK 3y"8f (!%6#?`<   f`ـ;   ?@@ڌ 6Pt@@@f0PBh30l@CJ  mF= DT#k>P4A:!3e Y1  Q!PZ]CCyW88ysBh%NJY"6f܇iA1N(wV(k5ffsHO5<b@@ZMN;k{emy0*+u=y/-1  P\zm^k[Z"`,E_~-=w>>+e|A%?K}t0&Q  v:UX3/L6ê;UKMgg'. r9!TA!@xQrI7|huK@uz_Or,gUjHരh` ^O,9v %]t: @@ SjTҗx=O82$E<UԿOi@@ RJXI49s‡rדFN*5$Nhsg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx mYy>J=I yVB[2C[[|ъP[!B2f`=mZP $E%URJU[{=s}=oޱ7k{78#HD HD HD HD HD HD HD H?W% D HD`dc 2tsD 8D HD0!a:[uO|D H#p5rD#HD Hl#=ɪU4XD Hv^6{{ͰD HD  ) TOeeԺ1>D H\orM:h$ID H-pS/{Nq4Hˁ^6ɛν|q9SΙ$@"$`[a]3cz齦U|?D H M6;ɵnr&6֑r"$@"\Gdʘ-ɫB~@"~7η~$@"${@6R{K;>YE},z: 4Hˉ~6̵̫g~N]"$@"ll6O{,ӹyAaK$aA`y2;LMJ>HD H6@6ZGtDO`yno:wD fu3gԹS7;3Wm>7eW%$@"${@6U{J{Dj.Nj!T9_>л.D H جŎNUyqq8,Yq)|n?i,۝_%'$@"$@ ]w BcLF4gq/rG.}._u9.QȤ6uS'N/ۗo>TMwKN_X -[.N<[pԹgN<}ĥ'x.\<ϜGxG?v(|3@)/C:*MVY:?etcoO5>bS$@"$+ pzt9pdS}mH5~XWXPP;}7Ty/k]Tm'ϟD9ܧw h_]7>q/9k]s[g* N;C:๭K_SO?pǾ>}w>%Rl>SmTe@QZ85(U1i#\1'@"$/5#{_GrG\^"oxyWhp]7oߤFTgMU!^+k:'g]y╧z|"$@"&R<yA,ypS;D}u|We79w;V^\cyc~x SlQmzbQ>cy_o*Nm4|Mֳt5W uGi^A&k[|T +zok+?uדO/˧pq{@lPBQ-%:磭:K5ӌ]gL"$@"bI#.ËF>u߾;;D`SxºsJ?Qn|1:r鞊Gcys|Qyuzn+<~ʫ֥7Bfs)|S>S~&G6U|M$@"{hڎ= pY<2~kO<;?mO ¦@a>ʮ<7ιegr@"$1Dkc\r~pY<2 OvIU"mpG|1[CO{V9΁EMt|fcEX*;IozPʫ|獚5*t^ȯ/=Gx-SWE_fW cv5ܯiev&@"$ ~EbGC} Ql[#_D @C@{p^i|uMܳœ%2F}}0 ƁO@.nuU=ޓW?k'xࡢ>W}]`O _Άϔ3e)'@"$G"sq+! N5e6J~Qc9߳sー %ƩQm5M2ѝ-@"ENdv26^6(6XKC—^OG4|⯹uq#ts)6ȳ JE^qn]c{y ZsXBPh~5yn>8(U>ߺzΗ?σwգ嘺+)}/5u6J.HD 8BwrD@{! / oOH5D?X.f+6 Elަܝ13=0^jp9y搯_q^}ߧXۥ'/>'Or讀g]L-hc]&$@"t#df7lz›$3ᇍ.Yb٢/2k~FF|/[v߲n]u捵La #^՛gL&@#~6Qu>E9_wýC?/p~p7 ^FZIO}ʆOD HD#5ӡ_H.`WS~L5?X ] >J*ZgёHdxcM+/|#uYǗ˨og?ʁ,^>!sl)#LQ&@_ya]鶮/>?Og3ށ|Jxh1/%ki}OD H5~q D^2:QxSMo"HNG?|]7Ɗ4`:jˢWac QѶ-mMs#L]tշ]5XIqG^/|_KcwΦdg}OD H%\!KFG/ߩ6 6Ve&J]#Άx3FP;0%?/n@8=*щ'D HD!F-G"0BE1p>Orw(&|qk:"A ԰rZ9ix5xM?QˁT:=Qjs} 9d0=G9Š/. If`N^yї~'{_Z^(u)BXsggwE~{E5+Iu,_Ի[X̻6Y?^Eu藇>֤zLO_bFJ`s_p{ȿ3..#B9xFS:_mkӤ~\"$@"pH;ɉ2umŀw*>|/=o )k6y&M(!tn?N鼚Jj90dLud~׶jc՘MykdaIw,H0zMrXsrHtҥSW+_woƯ{5{{|Z64fS6qA {̎ 9i"$@"ph_jm@v*Û|oh}@W6Vi&J dF~ja^79ԠQl޽yQ3J:54UzѬmcC㘲.8Ds#f~'Lk@C\n{_U\X=@JE|"ߓ G5HD 8dp-sr7@| ;Ûd6N_xNwH}F.dMRֳTuR,zCm 0^qFwq@<+ fS2)r]?1^^tйo`Y>"HD 8Qˑ]r]nfl"$MrRT ^j&i(}Ce/nY쬅_5*YE̵:4G JNc N/u^آę?7/帶z- XHUM3S Qr"$@"p/x!>N&g7\}"@mRkeo~jnd-똊_y5YvŰ&K[1ePPoF|EIŭtʭ&V(r\uG~_vۛox ?bzFr(tؒ&@"$v`O;Ȣ*^:}|">W+vb#݊~\ޞhXWUʃ EsG㋼*m7?QEպEiq-~Qpcn<~%݂5Wq^e3~?y S1['O=s 駊ˡg_ ;Ga+?Ee`&_sD H@p@Ne,CR>E9dl)P[n;C@?LtM{^{kڱ ~|=rk:h1!Cыr*OUoG1N1a+/}oy_ #ߌRɱ D0>\7k^gw>cU-@W+^9fۑ#'MD H,#Xޅ:(>i"pPQ1Xgts+ؤ7jdiC}N_n9M:1bӧSC9Qޟ W?׉Mc<,Nv N Kwۯ:3ovK9Ug(]K".$3Aܞ|"$@"pYP# e%[jcbdH x(PLk%w:V[MQ96&Ώ!@ca}}&J~($. 8pGj0?׉gM( /KqJn./Ͼn?Wn,k􎢮NCqqt'D HD! C5N.hvs@buPsB$^6,/4y=?c5I!4f@ϗtGApZ67/G#Mb{x5PQ.@*xmb=zOy2ջ<|;{y帲S_ (<-\M}kOD Hˆ@l{Ywp%%_k^˅6umn+)Mu~Sy6&nb;{]Ξnz=^<yJP(q,qJ刺|\1GM٘Kj?5c-SPEyO>y/T;zOqm`1d(| h&_菕ʱ_ 5⢼L=i"$@"iˑluh^5h5yV-zrm75&kɽXqWA)Q'rQeǯÏ{G/_:<Kq1G}VXO,g_E{/QC){z9j" b4x=T ڋn ջ5}/v[֫6CG󚿒t Y5135 ?ġ;][=o♏o|ۊrJ'7(!cHD Ҿ|'dnt*>FP9c~Мw@h\^4P[g3a'85dgJ)E~3ۓݗ᡽t}>z:l=ګS~1dt̬<קgVPȂN2ƞ y; *k?/xsGeq{ˡG i1U[J8zr"$@"gpD H}D{wRmYF-6k7M^6jҼ! r:9RT[7L&YӸZ;3߁=GשayHuW焯5g_|uM΁]:.9exzs,;wZ7k&z9WRŏ~S9W'"kvF#ܧK: PybʀFͬՃ^5 qH pWvם]П $lU:ҋ׈:K"$@"^#D A@(_MtQoT#hr[D1O^8OS9KMn[|3b͡>%k|]4c2\!T_}HaI/nF8m<}[~|χ(jml,HP*uVn8ܞ|"$@"'ߑ{2I&MD XD ^KQ9i6K<F{ [IO ~ ٩=e@x M7n!l9$s < ~F #8U. 0+Ve?)z蔃C>q(7,p(^U23WT]Ÿ.xrk[#-C_E5+iD=hOD H#߽9D H!@3ʧQH13ɳ; h$L&JrͩyvkSf孔n:5&’s}]^Vԥ9 u +JΏk~#ygS YCj59M`(S%B_ܺ;?M3<\Spv/C~qt'D HD`cᅪ%D@"$5V:jAJ><6v '4`/op{1g[Gʱj'\5jBU78b J,vQtw:|A׶@JMEm!09ؐ\G/\zN'Tq5u+^9}7}ؑ?8K_e;gU|^PmuE=Ë2GO}RND Hv@넙 HD`#ӈVyk -eAӺHIƌXdistධnC t1#%^1^ gt؜t㼲Lw8:P.x]dl ٽ?u3@1%6UPS{yh|:WJAz/}lxϝ_&%*<JIU|ID H6^T̓$@"pH &Q'9o*p7k_?~j2 (C3EU8:GY=(&ϪLux'KF!C9ʡfZ팕"z\(Wg{i}Z޹M\7{[\jp }։>~}ZTהCw8tl:I!]=]I9HD 1c20HD` bwOE~ۖAONzkh,ܒUIm]|CTTSkxxC^搏ۉ$;"Cr;7Z5m\Y6W qHp~m5v'?]_8ŭϾw|䍯[obZNޝKFO,$@"~72M'@"Gb6:lۡ*WXPVhw_?y_U|-q)H9F56s{@"$ A@"$B`K 4x1yCXêfq^jW&>xfֳ~bDCUƲUr8օtY ¶6Qp 0jb^_#;'w\5<kpWoQ؋M)s$@"$G 7vaf@]xq%]/V~L~cHZ%ļSsbd˜P7E=Ϙ*>cc^Ξx!0z`cu$z۾cmK YWxPNCw<\we/yEp(](ꅩ\d!'MD HvGw M#US/WA׫ &rKtXyrBes})zzWqU^Ψˋߘ(녙cmNElhx,޸6Ϧ 4LJ6xyStRꊺx=ojs:ݦ\ eCE4k[_ ohd|e@fnYO~C|_~Ѓ7}'@I9+G|D HD`ߥ;N@"h:v^Ϛ KzS{VX9EPO2mlେaHcE,SjoU5ǺsqNR G @ʶJG ŋx !/ni4 ?RTqP ^C_ןUxM6H5}\|"$@"2wʁ$qA9^G2\zX6Q;^]Z7~A,jѻsSBAXu=+kpۘ~PtZXNpTgw-sNwCͷ_r)vjVG}"PˋfyVtg}+̵'?G_7VC\L$ ?d{D HD`9D H+ˮ#niQQH 41j?z?׷5^ˋ ,gkc(J'/: _Q9]ʇs=ywu5<Y6b\7o:J}Q34'uk *W4/<-EMҋ]|"$@"0.tLc"ShS1m_wny|G]u?lC4t1>@v1]?h|w_l.,깑wev:g\FԫcP o$٘}#,At*?VC{k=Y>yvMtj0%a ҊUZ:/E9GЎ|knΥx=zЉJFYOk\5Uk4^+V_(lwnКb. 08S#ymN]tZ7P1O?M+owq -(:5:wu;_D H(G"$>#P^<*הm<::uк{kF_k^tl7X3eU﹘kJuQ4`#+1F?o&3J醳Q7IΏ6fs_g]JGw晧{߸-X+ˁYtsZC1ڐ=.$@"LL"$FR]z0^|eO%eJL:FQ!5l,[ض9e91|%~z\η̳­{QA\T]玼r=w&LSzD?\6f!<_o֢ҝ 7"DT*|ܞ|"$@"0C_>3E2@"$;C`fʛ o]FR.i}&s].@6hsy*E~81)[Ͽ##v<>.+s2?1$3"e nE#_y{vǨZ몃 D:bmj4el{GW⌯d6k`zހh<mʫ5^zDi|ݾSS|uן<=O+Y𕩮_t=:_؂lx&֙?\\s#HD!WA|i* /\غyb v|kDuF|/X^Ϫc=]<8z|1<ﹱB O%8h:c 2> ߩ1 =)1:1jo롕's|U[IcFY7].nRJ?L1s_\bnbو7 `R^ɭ4˞{RvryLӵ*8CrSŦw9Rg =_N9=pڨKw~{,f6ggiGWT3;(C~qt'D Hc~7H$*&cE}LLz^,~c1N˽,2ʇclM?cUjZ%nlNS1kz,q>5+ ]Njm+CkrT]n^u$7jT3_rh<5+ΐ?uzj5|{?6, h]毼L( l fg֬? h>?ZBOO?lQ=:o0-Uz ED H)pLO|.;H.~eNsMxNFYjZhz#סvϵZ"?ۿ I$@"o}4'JD#k9/Ԭ19e(_ кhxt*_>41f(#uuοg@^Q(o=<(e]e΋gk%\6Ŝ.,SʤOYڰ*;~kE|^9*G P@ȑ$@"c3$@"0@mUTj˲F{5HnsWEU?=-qMby'W^o ;.3D#{`9^q+cƂKhyt넛U^SwnɠMnő )0]ghPGP[>s* ʑ@"${~!HD"@3kdmݿzcᢉ?&(kU. Dȡ8^r_ G+G(ד}.T~4䮛{ڠc9x1/kYfP.r_~Q&(>npTcDȺpCԤ;noΜ8&.5};U^l=$$@"pw1Zr.5H!@S{:G/zcWczŬ3h<9z>ZY'zb6.mFki `k=1_QZWO9YSy <{٨.٨[{M>q (P?/ .H*@+2P^:|tϧ埾-?3g_S}Y9E_]'tqt'D Hc:%&@"YzW=9~QߓNs'S YC4QGLYmVcH5Ul2󔞣& /X"fB8I|Ur:JL< ZL%x=őX̻ۿᇟrho=+|1ϦF'p~J-i"$1A`1 $E+X^~ˮ=|Jv5>qʾ/_o0cϔ^xYۊxYhru  +&;ǹ2zlptt,l q^Rv9s\bjc="8|yqxR)/j  w*jmgoox/mh`MI%MD 818ɹD HA@]M:yVR$^TԠ&|$">/ZGsz=Gc50{eIt:?tf@a *Wq1= 'lk\{=p|l5Oy>j)Ju]ֻ q^qŷ:^mh@"$8'=$˚©um YiI)0K |b' 9A_|997oպ$kCxdxrMTsY1>xk/+-^luJ@"$8F';$NOʦDz?GuʧI5@ɍ E/6zd^2Ƭe|O_oݟƟMK~w~֌(1f_w?.@^ Fjo4~mۿ楯(! P@ȑ$@"l%HD#@:-Z,M&Lej_kv\sKFX y_ ~_Tl{]s-Kslm34y:3VsqUx.,st'yY@IDATc:U=t~+%r`@M):}:T%@"%u֔kID XofbHa>FrnvQ:1ԫk^XKVn3yQUYN4zz+v\|rMnl3k9KzQ7x.䯯-";>J&@ P㪾l<[ug~կ+*tZ̳i|:cO$@"pD/!WP[Ǽ]XQ65O1ȦCyΜ} co|rN(!4 wy ȑ$@"p_f.r"W]L qT&❺OqSͮ3!X/z'Ήo1'4΁mq>w[Ak>9 H=\.|Ѧ8|uMv yf\ccW>һ,^Z"A'!Pą *k|a[Ct1u4'Cgr5oNr7G6 >$2/z8 !3d@ɠ4>ԼeAI-Ccw$)T@k||x`[} $$Tˢn;?O7MEuC966Jl)U,/G"$C`w[f.r#W/+Fy,7r''8dQ?;W:xbC쾮 }b]Q2~hG7RhG\cwJ.lzE~l4jKy uP6=mt|cw:oKZ5 MqI"tH_s2S,G)Q(9}M<~ϥ9=Dd5ugUL̠wsk @'&@yrǞ{ϟwwX&0-S򉣧>)'@"Rr,;H'4H^:W۽xuyaQ5bYmDsCm}%ہ[7\8o(搾gc]9cqءlN rGǺyT[!xdy*v̨sL>Q6EoxXU;NǙo"$U9X*;aN2d>)'@"B"G"$>!U5mdU>kpV(Lr>TJuuxŘ˾ۮ<)WU<9+>%aeRKS ɘSx6*?&?^6|ӀM:`=SϾ{Sěqe9hE󰢮eImvoD HC$D H7~%NӂyjL4Cfs1ǹ&G|jҳvlT*Yv<:(>=_bE;kYԷZ7~Stcs)G\yܝUcClU_^ @kϟҐ MP3׼~o-!l"bN >tnO>HD!e@"p8!ޕ6>4y"5noopf -T^r֮sOO/pp*Eĝj@xFX&9!Y'Rs $sl׵~5풟*<_@Qەc[;XS-xW&@̑$@"X Q:$@"0MŸG݇ljG1ǑbFPR΋"T\F,ES7t||<w'\28|Ϣ օ} .#j[4m z|[ϙN;G{/z&E5rxg'@"r,3H&+ez#E_ZvT)kGLt S Xw;b(4gPpZ#9If>h&5k._8x^#6Z*s_'6k.fP| liM.?>޷rMR9UZdxQlqt'D H@nt5;zv4rGNAqO' _; wSMu]51MDrE2vρ)c_:σˮF?M>iA>%눍T>"O|3C)§G7vr6Q lN{sGWYv3X@:%cLW_%A& ,Wlճ jnfU2+q>kC<'x! pOT:Qe/Cz 6m{]_9L~t9hO s<8WeBu}bk1G5`׈+^|קGR\W#HD`}hI)HD`)~qαipx9*r yeL>`HS>ThRs񗌎E}3`'u Su5t82g\1hwyʇȇ&7g.,x^bSWƼ/%PM/.r3fS=|a1GO}RND 8<)YR"~:M +[t_Q]G)|d[7/vG&iCw2+f 95WY ϛKzk1=90f[g.yʩctbGQ1EUX"&N喍 .5_]2 ELp'a}ǷyM;5;:QU|ID 8V%@"p+jH˽o5rgpNa@>w&erc^k¨g9i U ԁ>VwԎ7S{^3%zkNo95}?hn3yXC;w|ϝg?sċJˡ;V ] =, D H@ns%@ 6QKhwǻ>/>oOů[7Žݛ6s֝7*8ϼlT5S6m`֫eϪ?~Yu=&]=Eϩ" ;5Q lE_6Epӽy?}7bTbnϐ_D HC@ne&B`8H>~h^QgLͱĹ6t#>\%6;O [1ǔl)Fݦ|hvG͠o |K6&@q_ҧMk|MD 8P:YL"UV:ji"VgV>q~^< g7ySuѼOg7s@=VͣSO#)~+M,s¯/sZ5sx5ēo<8ϳ,vN͗n yI]UvwB-z>|+>;^tהc6l#HD!d9@"p@+bB.[Ǯ+Jֲiдn%3S ǶbU`´maPxq9T/cS>t7uDdf ke~.Qz ,J5u/zr&ʧ$9D HpN+Gί Q87C<~8ymē;qPs_車W%u9{;@]ۣ^2:xd"=boG5IBjA'z(%s6!7ܑM 6ȧW?W~ɷ%&|"$8&'`,KU)6QՈ~iC1s>lb2SlPw=S{b >grG4E5z4_9#4CWËD?ONZW>mH1ǪvlSO='ǯGѩ56rJ'ӻZ@5jzțvjM{6ʻMu9lS5D5)9/{j$Xsw1s姿p|WyȯxF ?nu3g~>sC썯}C{}9t'?ˀW3:>u]@"$ L紉@"pXׯiz>ؽ5P؝׳-MDqt'D H=F 7L$4~M Y˴`] H~ww;GX3>ʃxJfcY:vS߲؈c˧򫩞'zgQAy0$bNRZ'_f-R)~w{o~wQT zGQo[Oʉ@"${:HD`56Tҡ֋R8I~< W#3M{i7?b=SŊD]ԀzHFfnri>jzmhLm{vB_urAU!E5˜ǂ G63ks|Q ׌/BD(3~/\u~o?7")'@"p C~D 8lZ$C{{9W49]~FaF\ TשsyΣe}sy 1\otek1.+7 s'd\Xxt ྲq+fyM ZG kE6 Uӯ{'&BA@e:'$@"ar`Àf>\s:s<ҡbs:fx')6υw_׋_%%z~Vy{:lءآcE_u{9~}k`j)3|-z&j1UBjދ^16ʋ{+175T'>Nat2)HЕ xoxuU?1ګmk#ġL.R>Q~y|@į:<^1¥k{ 0p|}4YtǾ .|=?w">z&o-E52GO}RND ! 3j.Hʯ{~ <'c'?vρp^:mđ_Qj񸘳's_/zdFe4Gz#${~=q䂒bNo,EynxcU=ZN3rNПڛClPٕ[hױX 6^'>{(<؟2 \bL{6OO~zIEq6^COrA E0_ݒw*p>MM>/_= pK/~}WQ&@ˑ$!G 7 IVҎ2ӌYS\|F]G'ӵH/.m\W0;)Bc֯5?c-"j!ٲ|HM6 Qk{wISPLpjѼBW,dr!Cgs vPj‡O}?z;z1?W#( HD0"X{֜]8 ek]fmcOes~o>[],Vh|ƝZxB>Ht[`#H{k&xIDzdTK}uw+~J.armrVP ~>Ob>͎XljϿ?-#7 9D 8a^0Yhe9_/[7Ϝ)Ka2|ܞ|"$v&D H64jL#k>|hZ=~>ijؓDa.1/Qt_b\ӹzשYs%z+| B=^s}p&Fٛ:sdt{C>u" n'jN1 ţ0Pg{#o{?o-ˡM _lemkh8zr"${"$F^x%dl3.cQt-_5Fe(;יkett/PMc%8޺^sLass8/#W1&s5z^5;1n~}~?y/-& u6BJGO}RND XXtMD`/ڝ9$k`o\X_|1fi(fE e V͒(؝UBdNͫ`N*//}=89}FH\Wmɿ0G}O͛q#fr EBvDgW_}uq{ @Y|ID 9s22H=Cf/4]|y_W:{9椓RJ (<ϑq"l^+}4֊o A`iv)ַ<^ӟ7}}<^>nS>](N=>U[_-{ )K|D H";.D 44˅7ء}VDzk ,]t>9~c޽^|n)fPN=bs{u)'*7iN@zC[gEu9E A|s#˯6Y/X.|['ۗM"C_C_Ͻÿ_"&@o@!S׉@"$;D 7v\%@"i'|lyDy,kr׉]B*21ΏGbv*οΩ=d:C36{Zk%`j147MS>@ՠiWbju.F;4>ìƕ^ly/Wzkİ WAŷ$:_L3[%3AN$@"""P$^#x=46[ W,Fy.ts;ʬsny<;_i~ll x-\&%eqF[1qW!@e!W?e^2 Q^ź]|}`asJ'Пxk7C_4.Y#Rtӹ=D H%U@"$hn u+]O>_?O˥1XT_-8QM MryFqy}y:!KY>UԦy;VU74I:rx~ p_ݿ ⨿Aʛ Dy sآ]<p/FE~|~/VıMb @T|Oʉ@"$@n&@"p\wBi |5ܴ(\nڐE6}/jZ^2BirdMs#nce5_ұ':}2Ly%ʭ#̼}'U%7↞.|zJ2RM^x-[;&,t.^g~}Ko{ӻM8S\EUS)]<2GO}RND 9D H~Qr]w^B2:NaW60C.;!ڮMG# rkoy밹:Y):8/y~5~H[&Q"e׀/Q吏&_])^6ǞsU/ݎY\n0{/.Y/|_ sUz Q4ĉ*nl#m~5oa׬-όYBӦm۬+1EE-2(X(M.Z2PNs̯xmjk9G[|(ka0 ?gzM)ٶ4Zl~n9l$3ǂ݀u}ۿi$ ߼ _~ޢfQ{ѹ[(9|^'gY_~BdY@V+@C1՜|@H 3@?t#ܭwïo]nr6rnԚaI~:"v՜Z;ϬѸ~@_bA1?,Ɗ>%l= "vEfsa9N_p}3g?QĹ dGT8gج@V +p*BX 8&oJ$IDV d4d4"{֋ȉom[;!0_~`ܑ)H Ԣ"E#fsN~@0''}w: [2 kiaTGN@;Os n)rѢE'gYY8= d4;8G~j2جw=v XWvl𵩽іư:߽1"A2Ns3eন)"V/͂Gr]\yaՈшLYt9&7K{ř6$@nKV +8 #e@V ++`thsyQ',Ed5s cŹqo35Ƨ#[EWOʤSyյ0"vEI8slLzn~@Aɳ8Kzx\&—\u׃O{r&@)B@V +p èr d'gPMԞpLg@G׺x[ S6['{jG_u|ZZWuǔ͘o͔Xřk`V! LhUz"9Ftj|yOxQY8@)B@V +p p dYZoi pKmZm6ș7 Z3pO<1Mc޽1"AWV5c9tڭ[YUxms!Ȕo.`. K.[zF|Ulwo'į,&̏2]K#Υ&@V +pb*'TBYU "6-dn| h].r+ ~"Y@Vh*~Ɏ:7k׳)N[44jK"$C]&^fI_w[3^%Gu}Z65ܫZ>8jvIucüzʧ ]㚵ˏn68@d p|篽?wST+6b -yDO% d'p"Ns.2+ Mb3si67ؾ_CDduv3.F7tۘ~7zXٍz_rw0@D\W5OM; ǠՍV:)\ϼE}wGQ trdYW8q< dzcIxs{7?֬W"7nҸ6㓸c~ m4hCu4҅3?dѳoa/\`5pq;79t?b޲6e.|/Xf py?E}m9b](9Y_u/6=/}xͿxɽE9Y~T 7cE)7G/lQ/,Ȼm@66G9cdί3Ǐۉg< c<8bE9sPɝ8.p9(Fr llӘ>l#R`A|Npx盬?cT6ڢ=L6@5N+p`vy-GorУ"UPrw?F(F؜TѮ dcSquN5' @m^TοWڏ" `|V +"*w"LEoZ+sA}E;!sһс=(~s9v.Mq`K<| y{:cX xQ m^]1n-2z|@:}vl` 9Љ£#ׁ/xN=溝_Uu`@&ѬE`(z8u9 dWL3GV*oHY SxGG1$L6|j`clu~C]/tP=*>ꑉ*=»dq-$=zͥ~Eß˱u z:!wc\6q9q4Z0Fhn"Xl\>&[Ĭ[YΌ)zs:??Ƣ~A9 @˙ L祑zdqyY@V~h YziAîdt ד#qnf*})b~zrqU:A(rhOޓQj`8"l܎9 җؤav~9)t[:y<Ma8wS|լc#6~"`b,֥nl 1 OQz [n>z]X{q9ZD 3yw,(fם W/0*躮v5k#@3O~?v񩏾Ms&@q #+ \Y@V +pL*;xwi\N+%zuEˢj~ƚ12Xw.ۑ(#?&_Cټ!z_G5m Uݼvn/brhא1d{qx5dE]vN*(kO7~M;r29.eHGO19 dDzp,O[& ia PIW;4?mUu󫭅yފh19%~O,6SSsf/742^zОL1<֠ 0$.ltf'Ų֑8=Y95 -M6t烶/΋8a? _ Xc=wO|{MӅ' qj0 U2' dUxdY@V`ܹfh%{Oʕfԛ$|>]XI?-WiU6?;?揼\NpSpG;sXmRNQxw+  9UhV +hН^xfp'KL@IDATs' uجU<+n_ཬ:˿p:b,6j= nb03X%r;A҃:ߢՀv_H{>'8kO 5E+U^t< Pe.~C٢ˡ?Gze&,br d{fY@VJ@l*KYZ?)L+f$IX9㌩+xp!ϩ O:*h*j{qx1`WQXaE~Lhl> f H|H2HE]3M֏TZ/z>һ"& 7JrdYXyV +8fস ?zlge44;@ Zվ7ŹFg,]Eřڻ+YwlsÒ CSZF8e~}j{eT1N{N 3Dq=Y]|>/l?=Kת9{:o'>+yGmM/lY@V`VK!+ lY CYC f/Ecr'zFDF7i5VU(-ʰBARcn'gvsѩ+mU,VlcW8p1N*}je=Ƒ{;{ED3AgyQN-||w {{99YWG.K>+ dv/Vfg,,U؞IIf_Cs齙lT~ѓ;fslS<dn l'(|5R_+yM{qӬ#k[yȦg96"qP#tmgfc7#nb@ P"~{w' {G9DQs9(C6qd@V +uY@V +p+ $\;׽zNǑ7^+ZJ2: 󊅟cY [+16 90WL.?:$G7NO.ǨŇcyA*n]=1*_"J>%NYT3'q~]5g]r; GL_m d[WغS eY+ hpړ^6هmz ri윏 MNɱ忙^֯x|1eMlL67xYAHʧ[^x_VryCyzQQo P1|gYkrTV +*A[cd.G23蓶 ӀA^Y{|Y[/J:v¬#J3 "X_R_טbs5#ʵѵGy?w*l[c8-T'goN[|WL?~u7^fte;x|: p̓fY@nlIJYVArFE:\XqG*Ekgg̽4h:kqU1\?a{|4bL٠S6,9y9`S0ڮp=W{#Fi#'Ɗlz %~ Y&Tq@>j_mZm4<7~U/qK rh 62JY@V`*u>2@V + L݁$9+܁@&_^\JcufyuaP]\Xq/˚*N'6c5sxmC "bje1VʊQ^E3Z{^Vז 6#+ lkr`[L d&*ѓ1$%(\2 5 fl.6CtM՝le##edc~֕㗕VQ}L˾@V@n\8 \fwlmcz5 y#q9iZe7>͐r8]&}?x5%u_8R/G*g/ظ,@V +p #?@V + dDTrtȄA>bjs) Qmjfm ,zc1sTp)#hl"Kauf>֍s'' z\˯) O>ѣT(,t)^Fyfwgos'c/מoMf% Qy5g8YҬ@V +p #- d_h`ҹz2t'ֆS 5z4_9v8ZX_Q9q18I"R{Ď%1a{1:ۓEClQ-}9²cyH+SEAb\9p,N>X(m/}-n<2;<.6$ˆ92 z>\/>ƈ{JI\MPNŷ P*f_)Qtȑҋ*+xV|_(OTиGWX#)7EEoKCPlaŐaiA 3Z @y{~ %I{@g_ȏw 创χ lŔh@V +pdڑ% 7nxɍ(ثZ/2py?N{ !lo.l=~s`4G>w\/d?#,(<a.*;G:ƨ۬oI r#z·#c>G? "Ezl E=2Q21裌6Ԝ}$ f6:V~^:yй4V;(:`q!g.x44G.'z#wl\%C'J ɝ\l-ẓ/`DLqgJcЋ8B'e/q?AMĢ!8w~_#o6 vCմsކk4{`k6i{%7Kxxϣ6D+ X^[ȩۯ7a+>,1 *c{\#E92t>w^:|K!y?Pd60kH:ſ#w,tC76xcp=uE\45"FsVs3f/|J=G£ۢs9Ћ'gãxȠQz#ߚyPG8WU*U9\VustPt2<4-QtSgݥY@o5/m2azz;X 1fJk(rq{X˸ɢ0S8t^k/d33朠g@/pW?|Гq@(^3k'[D,+ @r.+!syZn33f7)yȞc$ *(2އuxP5_Qcx|Cp`3G\s܏xN2p\rrr29|c>ne)v9> >z\'0c0)4y poR#`+od⛝VR *-\/9s:j3łL2® x!?⛏6ƝbJLh&}ys'_/6ֵjЫzֶ{Iז`׳%t^gj Vswщ P7 ~5$|+~=o_RП Zs=(Cqd@V + ,+@oOD7>rd(rE0ః E$zא,b pFXLmI.'9#/sFT?$+> l2+b4w<#FTK6/w[2p ?`D%9jO7E㺈v֩!T8h~$ZCsXG kE9:l0 W\-v}T /;LH]/^ƿE e/ū!A pc"!#d=ɠsnҵ?#?&\|r䋗O;GTy%?wE|k9$@n" dw<ޫEɊǒ7aA dc(v/t z»ݘL1ܔ< _(2᥏v@}8?>=a۰^\ M:z6.-;؀p>L˝7\/?&(vGA㕗z!a>pu6lVH\GY M`m-69(ƺq[(èYoB+/èvL~1>BMo[ԏ*cZL{^{8w |Dfk5g,s3 ,Ҿ.ʑ  3/!S?./ޘ^y،av#n=wuob[7#M}ֳb0:WawKCzb~>Ͽ9/+_tSVgc1ֶu0XXP=dOiPc:?|P{|1>f"S64OW+ˑ0IV +p<+Ͼ}f8iri' xlF|~^LXn]s2Kk8\;fm}Wa,׽.AZ(Mi¯",~*6:eSeg!d]Yx=ܲ%a6 ]&6]SOٞع_|,PZDs(ӝK d/eL'Y@V`*лefDd=㤏a[9r8S nm\Y';k:ӷh׿\Srfq81}XEre5}']7ؙ3;ݿ5g]T+`M#Υ&@V + ʗY@Vʩ@ԜCmZ7Omu7( p>+Axmr圸ͺkmg{#c"9TvG&r)rNOVHrZ2[ Iƹ{*0daHd xS76<:IU:z>/򺟽_TlT 1;4 6k= d@nti d6sh~^ kZCa㈞fUz{3M5l:}/Ly{ͱ7>{k_M(9ˮτǬ#YB^wG}3ѓ =>r+?|׽?z`m#ǧ7Du1"Q|:"PckыrN]PL07P7 /y=? pWuxٷr!GV +Xwv dY+@S3p 6?*ްsrkp Xe (P g"&j퍛jbG5r]8n ަ OǩO2j6Sw/}~ocD 5ܗC9|^>@V +P+,GV + oU8GJ;e&9<ڼ|fW";>wQ4Ci{XymrĔ !1Ή~ltޣD(#Ψ<(AjizNqmU6' eaų~3?SГqPEd "&Y @n  diHhZEmb[?q-hSl1.n7{)To*#6M(ͅ}L_gޗ%1pK&n1gy9Tph(tSlza3/g>sz}vK9ΗC&=Zs=(C6qd@VW 7NϥgY*&:Σ?lX 4}dhvJYy-W[Ԍ9쭢hKA푯KY*|̋˙ b9Ďu2[G9\ |oxim}0I}@'|`|?~ rI(Eȑ ^rjYTfc*!nѕyͷu&jymGmQ41|Qӈw䱋A׉eMA~Wo]1ӹqȏ%kls:1WF^W\6?Ҩa@&E/|;r[9r!GV +px ëuF d[Sp/2/ 9 QPvxPlmM䵊M5|Dj5s\<^|rs΃ecػcuvZ,g%qc6u{mp0ac~@ [ŻOo~?V#? dp(e Y@VU_F9+s92loU~{ضGM ͑6ϊQ,c詩!|+'V5Hy:2,n߯]&GEĹy1ɦgԍ5L_ɖ>n^}߼*;&fvZ`եd6[0`f'pBO|.;+ [u~G16b:yl7fa`[':pA*±cL蛙14Ė?r1;{JVe{(s3\ +}5lj llr ˵&9:5Itc7=w駮9{br;}1R ӓ>@VU 7NΥf'ܼ~L;FtלJ66tc6{5%:3/lqP5-C0GH{zEˡГEigX -M|r~c]׫bxqV:9f[q8Q];nuܾ5|m_|_Ə%/r@ɑ [r`JY[/:ꍮ-D7رa SPQ4U9{q˓ d s ]^.++ dQ b'#˹'S.csb~U\SjĆ|~gƱsjs]b' سOըW޹ʵ5ݺoy/ B13NιVc~{\}{+f P Bsxל1˚iҬ@VJ?5ڲY@V` t82CO X?IYϕiuQV9h|05ݾ)@L-)1o4mSwc6u{K-abH9+OPY'נpKW1*oWy{ܓO'=K{߫Pܼz@06n+)qd@V ?Y@V`/Ckx%Cu' `zv-#]23hj^uvhzɢUSG!kf=\_OCx7Xur#]Gq!Ơᦛ'S<]&']|׿ޏM-qHL6z2tIY+,B dYT`CFH#$y<xrsLY6nnj:uf+DsRi~=*^:bjƴvr\G8XzE7۴ojCuZڋx^:m } 1Ox+Ugyzk7%qs4"-9F6m^{2'  s/# dYîiLso,bzsɶa;bC(߆\7AM:;j=]ǰu<9Zrױq}dQS_ϺFGkƹ< w꧆cͷH&gO~#?rX 0wm)v(c!J{@NY[~PdY@V + )[.1^f gAs PqmռX%,ՈFӓcF6|#`fw1z0\نtc~wc#_˯x^gc<f2p+ٸׁz7GʕZ$m̅ ׼G>co#]`zY5-Ts*Y%s}Y1?ivV + Gzws,9躾kPN/7&m{3r9U)}sהE_Xn=r==\{>-6kS<ײ(<-:c>n8 sȉpi߯ O7Іϝݗ>S?|Ous9 "pS 8'sYcX~@3@V +ئ pKNu"\ݮ%^+[,Y3{6q:⸼q/}գ_~qmxLB&^#d/Y?wguV + jhZ{*Z#Ϳ:y.)OkO3lÜ|#E[$J',;ٍn8}W>w>r/L %ѢE%L  ?@V +p(kzwY?k3MLiSKɛS|ؿSIP;qy"A6܌Eh Mrvovs~Xc)n=VhjP奷5P> P?S/y?%[ˡ&@)B@V_~) dYyMG:P\XKvF\q\C΋ .[:,#֗h/2\c5 7sNW 3Y٬g{dŹ鯱ե}3-dϞglu޶qS.ͿS/K.ʑ9Y~Yצ4+ dV|17âˆטt=m876{XY)i:bb'=tl`O<|Js<C8^q9'Q-5X 6k:nˢWcXïIng}i/N+dM;;qRYr9IIV + dF*MKtaա|PJNDo{{3Ŋ^{05h.5W4bƨ|o9n7e?~YxMgўkúӵ%&6G:&pO~Ǜ?T,r\-ӢE\'s}YW1[b dǭ~wn~fME n[h/:8iJ9F`<ڟ|s|C=}yUe#Lժܦx|Lf^蔗U cfL/nе w |Iw駮9{}^9nԔ ,CM3Y$+خ elY@V`+л$at45P;Mp ̓56nn!qVY'˚q"pϺRX_9]guR&x5Jm(ʞ^(o[67Z]VB= pP#+n|!ÝjSoP y"wqp7§jـ &ƘOٰP~ƯNjk+5&~e/h!k^k6M4Qd6 6PhNnڴkUy"o?+B=Uhśvبţj[mf^]un\m:?|,xL/\ՍpYYs܀WR7FLy5Z&W2O?o^sӏ~YďB9榅Yy15iړ>@V*C a~ D^xB2QP#+w~!^2pwɿG/g /xYu˱l"[5s$_3|G藪޹M1~=6Bl6qYW»=l53dCܜƟ>˱gXrE9v ɴ[\fs|$smˡ-sxF,:T(g)a(W rclnLm\<{z÷?[).&+(d1$A\V +p(ȏJ3HV +8oza]n(d'YkC.[KO6{3rbՙ) 44~ G=JD<֡k^댱Qoq*6jGSa@粮芛s\ׯsV3G{p;עMѨ ˺缯Z9q6Ź|qWdFS/ ƀx}AlɃF./Ent|10< ܩS~[XLZ}i)A Qtd2`ڬdO>+8 ,:@zk+@5GnlIJ]As.w~WJ;E3ƣ5(up|Oz5‚q1~,M`W.c~z b̼ mЉcظ)J/)x)ˢ} Ohs;es鼙P`Gz7W31[Ӿ2IO% mP6n86c{W;|}VyQtع⿿~oz~z7p@k4m+/!?pwR:nw|\ srfeG&i:ַiOeS8eДqjt ;*&)[ש7Ŭ8u_eO}U#_k&U>yIGldKrQj`/jY_-VtĿ|i464dh5WsĆ6уÒ˦ W6 7s\b͈`'Oojq' ?tƎEq)dfTq+@5Gn%Wؗ !N10m8%Gnz~a]_pʚwf}ĕ=똯Ur|*CLu3ȝ<h 2AC  @΂ɽ' P3&@CXh^h⑁s8`5ןedMt\҆R~A<pSЗ ƺ>ϟGyQl? O5~Gˑ9WBO+a%Vkm.+Xn:5d> xGkҺ}kT4{+cx3}nRUX)ڱ;tn Fc;8/ܧF>rPlǞ px[ iEz|99;t9)y i+z΅7e[B!4զ`$cQ/ ْe#g* cżĨ>[,(K(xiķo~ P#+p+㾎k #7v_ FRHdrqYOw 9U$.X7m1?OG7U֞5c1ĻkNכt>ܿ:||@/w@l c'P*X7cuvy/O>kVh!z:@KB~Cy"ֳmCy꧙WW$g3DsM~ΟEMgˑ(Eȑ8Xq\Wy~ D^x^YBW>1?uhFo~VZljEpl}\My1&']gQO\>WF?DnMhOnQ9ͩ"+L>&-}/k|RkK8Gxŏ?u6) %GG'C8zyV + w.Wk8'< Ft?Pɝ]O' zz"m˚'myԬ ԃd Kv]K3 uH\ fqp6=YϏd:WPhtQZa9l A2p4׃d9z9MUGA81P|26nhʛd7&osΛ hAp[ ܙN鶭4:#e[oN7Mqo?7kO?`fP GO19 dvY-]:Ic]=k a5q;n|_9YVaKw`$l ÞN66utsoO45:~MڏU:$es{AYS =/U"k=?m ]w,<:eg&D|c:d XX(z|UE)yZNQF0eJxk^ӅK?q߼ k#8S0_G`S_K;sO>mo?3zab[sJ0#$_Y}@۷R[G~ D^xx/^HJr^Rh@V +P {DE]w)kQXCFSЅ-:^~<3+;gN]9sÍ;Ϝk.˗vNxs9nK/=VJl m9s&q*9y2f+57v.> ڹ;O~ X?=ϞmoZQSQ`kyGV +w>LWǬ~ D^x1;nV`+;;|N`S4xHһG8QvUcܹ1P.Ҙ_Uιڹ%/9}u;nšr㮝3rw_,}`Py\~KO<ܗc_ݹC;Ηg&S츾LNڄ)'_!*#_͐]\z)l! v=_qC[;Q}^,Eie^QMF.^(~n,AβpS·6_$@_.egn?|[TO 6;d;{e}W@V`*}p.i cz3qw~ͣWkۚf|X1KO?[wNpCy<֝S׿`^P윿;o}Me#ȟ)J_oKYMᅡ!TTԴ|T(_z晝KO>sR6)xj?\6ڗv~ǿuXz-K@ׯ`[uy\1HAD<*йƢ\ѺUQ<\~!Vy9lFSzY s}qܮ~ f/Mo^go<{wJIpN&e됝&{<}޿**}9@|f=UF^s̓fV ރmh+~ D^xps Y Dyo.lu=)?Z|=C_gRi ;׼澝sU;g~a;!w(7)]|739_]v%h.KcWУn<;ʓO?vWs{b>BS9s\o]0D9Q<Ss}L!IM2jz|J%0=m:&^M=9_7\'f: p͵_{W;ϋrmW6 g7:׹&=IO$[Y$kd˖16F0 &Ni!R MUSՐ86X$Y~ڿ}g=[}Zy[{8V#t4w!D)z>$G`L~!ӎ::y e;gK/zx7煉kы/ \O\}][>n)UeM"$xnЗI72E-B$%bxʪ>2$_~핰zp8ͧgV8%+eD^X#l-LT笕7Z>kcV>m/[ٴ~n!ٮS8"7"Cv Z5sEB>y?e0#w0swN=>+&UJBRf)xئ%'Km8eg#`G=|`g/>zG`~1q)t7ǂ~s!WkSS嗆w!_/k~$)x?=HAro(NA^Fb/xDX|a›g  (&=QR+τ]#*}+KcuLQxlWmaYQilB[$(Evg 7m;/ Bb!w fv`bgoٗDLˆ v a|OtY#AvHyvȁti_B}ZOc jccx@S!#o {{ h:t$l풟dã~,ÓuAtٷXY9}*g‘O}E '//-H|sI^Yb]Э/-k[hJhOI:Ԏ9J\c:)#\g(,쩃q Rx C ?a݊xB1G'/0>>=>$]`7D]B]IyذІuМw,{WwlQdž?g~~\GpF_DQfdV6#Mmnm1 IOi& w7]>Ca[ ]/rql7j'v7 =ް 0+/I( PJ9백%CYG A@v`~q<6KpzC~ 2Nҭʭ-yRR>Uǡɿ$/={~,î cW\#I&/M+0zn%;"u'bj&L%L8:̷ex) K̏:;E No]^|FMJjkyzLp#M,v*g@SNMa:6_8_Lf8{ ('zNjY?)|e0GA"O_se}a?~tq`DnbAw1d 0oRN4I^=nlFSN6^bOX-W^>8R6(I:1!oi[yYQvSRA r|*Vt"]A6ʋ-~N,Yy,wrmc{O=·$׸"byz #۷Ly;m#/)6QI^[O嬸6Vܖ=h>&}{MSZH⊤_1t/qht09;&'ÉoK~'HIM[!YRhQ/6+MZvuBc=|* yCaގB˘̣136K9(LJ1ȣJAI=,/fQXم}Yr|ÞG~&ɂll8[`hS\-QX#0T%33)_p%^ԧW^e=:"^sG Qy?fbx^u! IGBG&V_NXxey5lֺo'(yMb)=eU.A97Rg%Ѻ|r$'rTg? 9CF{#/X&/~i E0!6g<.Fz&gd!<0wD\w^^f?.cY]ocGD9B P-?[;"l#ղoUОYhxƱ;p\8x[j(Ѥ}ʍFB}%kDZ$! DĪ<  Ϟ ɓŨ,]tIh>FX>s:l CnuHWF;ѐmBnyYJ=iGۢaM)-eҾ`8V詣О!2F8"G!D(D_qMR>c]x8 ~!`7%SO\C7O<3"E#f=b;%Qs8YmI7hp*KdO.\ {8_6$dz xq6a8#091N>lhQ><~^ rG#dVh͘ZB[ao7z%ް)eἳ1+#϶c[͎X=mPDA)QGLշ?;m]$P,99<0u5pݡ/<酥E!z8 A!٭8|`ʁ/ɔG кpzA lTuv<( iʫr;~DRGEa;꫾ wyV_ՓѨ#$=ZeKIlEolTu,ZT NL[*^ !bE0O4޹ gW0QЅOL><>:s`Xv0^v͝k+_:67; j hZH[qmi SN/ו#- > 'ۙ"e|:.}L^#ra.ocDCtvm o{;Bտ /r9*l,`!)*ѦiPYJRؗ_ۍ4Vȧi?Z RmBX:q ǏR'/ehbqsYY Xe sw\x굓'O "Wwlѫ)cV#_)zp`^Fnc/.1́k+=a5fxmL~ޙF@M$0²*"sC)\xNbb6ꭎ؍ވ^:[Y'lD]Pǘ 3q& H)#]#@ YjH_;RF;qKH=4bQb, srw]t'3z#iΟ}fgNm~mߞn?ga#BltP >Yru+Q@,no1X0.p,V:o>q n7n"VyZxH#vG؞37ԙGbdV1HSq+? ~G[&3}X}6=#[E\~柎V#PC'&& 7/r"/\csI'5 ;^}%E\'vbUԯQ u]c~E0*<xـ .:t1CXIi0wU;/@ypG>/Rur!Ϻܙ2~aPri"jncM?P ߶53goaHڑ wc|TR>gRز7,^: m=.#n⍅-zNqa3'_˿Cw|앍w MZQ+/B'GpG`! >) ܬ JZS!1 )[EQ8N>}j:ȫto:ہ {Z. iLcw.Np< yCO\mc %G^'Ows]'E܄U>G < |8;8^GpDysZ[ԑ8VF~UV)>/7/#%}2`hĸ!r־)QȱEW_fwMYYQ={9q˄3> &Zf2ۥMpƢTmaO#9;=Ec"r!@!luǘ|,dB%}}W?q]I/FFⸯ#8#wdn_&LAOv6m_і"=v=>?/\ *56Bivt=Fnk'"ikkxghN=A-X=}qD[`/Q@~X |,@ . X 857o{ݏKulܓcX6I*jm2G < |8C/ .:#,4EAIrq?='=Bxۿ{A6"bFY)yͭ܅6>۸9]Lss86P$_K^hCFyn1:m )pFMWQ@킇 6|_:ڵ_C,]Xg<|@@/;# $2?ʙҘzPnԥԯ_+wm?'Q_u-F\F/(/"E,.Ib  <6M:LʓB^URcj]~ݴ9&iN c }mEF(ukoe;CN/#8!d>r7?al3d/vDȶ9Q߉)J>YJ]EVeon GFkR^Gh8ݘ)j!>[/ ŦVEl# bOuu@\X]^w}{E"-i8lK|`[6#8@' < I!:+:}/ųz{'mrG@yۿ ۋ#m6V-b,1%lQ8H>X['i\Z Iq>7}$+,hF <6[Km<+g `m E]<C]8@"@}i uD".GIQ꟎!3!ۡ>Gp<vfGs:RR%6yIPp-Gn`;" IUet7$Р6ل=]QGA۪b}n7@&СX[]'ǀ$ 'oJ0< cC)Hȧ2(1B5 qh^ ޳Gwzvh 4:iض۶;#8 >?NvӘ]1 ž {>ms:*9$( Mqڳu\VHXROdiiKmsuU[^l)և Q:Bl{be\bYEP[0{u^} 8@_{7dx'lp7NMX00sCѽc\mkydLSoe9G}|{㋴He?N;}  2#QB%o}C<\]M}ܣ\(`WC h% ӵkQijPV ϐ"*eA 8CN /#8Fr{;B1llKc@}#aϓ?.I8ucp(]38wmG1F3yri|򶯴]+оEx> mvX`; qA:+GlGӖwFO)_JƆ>('hݎmsC{I2Q@Ut@rKfz>#rڂ" } 3y _,vҔG݋#9pG`+UrkIvjk'9lJ^'^. ߮/ĝ#rG`h 1P$m\/pT}[lJe~qt7dL~YOPh9Nu}@!%O _@ @]e?>|Q,%5Qgq-b+  } q+*1ȱ0rw}D 庁hnVUuʝ:ϥwpG,8cbOjۣ [ڗ4VRvφ/L|{1/qRX;pnL@EB+̮d[. $Md:}u\ ö1džzZ6D gbJ 1 jZ6%'x7E B=1C8&vݑWG>h춰eYIomw܁?08#8@:{CʘYRjo$uf:{0}-aš!ڸ\S|cBg΄c-ltd_*-.$bd0`ũD^k'`#i.V'FZe0jnǬb)cC & w Ft?+˳,ҭW^zd)Ƀ@,#0P@#8V mgl>RL@W%_ nH025-b4.DCtQ}L:YfU?yյCӧCX]>5ob ["\ s=qpd+@֭x{^䟕 Tr9g>l-1k~&A||MڰwP^E->%1 PBmnƃN}ițw#ݼP̴ ASYZ;<nxGp6;o7[Vgƾ׋p ԔPegtr 5\-kϽj$$ !xnЙ!u+Mi=a?;vuю1)_v!~;R'VG^v!ukC^R~~0HӅC{_{b9*fewWۭ*;ŁwpG,8g%ڑb-~4~0y_~JgW8OtY9VX8z4Nދ='=yZ'<(Uus2ڃihƈMcSu3Y'Fb ~v؎Ilp7cK~꬟}3ɷ@?PwRn18t7xp@Ip@RSN:#8DNlŅ]73Ҧ^0u%aϣ_-hkuP VVO,XKPs6iT4yU(̲n)xk%՟hžY }߂s8<=dR>HcXX@}F I 9|u1@*v-1^GgF{/\'n=4 ( lIV#00yGGpB5R 삷vm]}*o SFc2Pcr'ԏY3X{nQv)H?[6[].Ysi+14[Bθ]J@^OIwnMmxnT1ۅƆ<3h{W:f>&I {WO@.<\L_t'~'EO6> (:(d9:u^GpG E錎-A-f@'ɿkHY5p  %wV|jmz>@acXp@)T٧a2qΘdEhن01+iU1}?P(io3xWB @ x#?Ȼl`Z?/" kc#8Naggq]x?:=foW}advJ0kw{"#[`7jYnl.u6/؄vGp ;Yjc]mEnUgn-Lr. U!aCG`$qrh8Ah,.acugw01dL몲Oz=\%O]J-6} e[$Q99BϘ}cHqHpKD^d[!\iQib( Xvk/E_nS*&Zh?DGpG`G!`giv%ɳ1w=eÉdkLgO3_r8կJ@_ fH!#_e}<&vw+ũB!ՎqQa<0C(y-(#NZbLݴ.ŘrP/6nmAr@um3q?|/xliOZPWaf|NwŒM R.0HRDKƓK mL!K æ4TU ;Ũ[cea㧬\ \7>a폕[ӌ## 9E"H~/rh_  ~ѷGo-UOi܋#9:uGp@7vlϮ0y0y 61)/=Hvۯ{G`@_,ʱ#}6ykebV[7ezQ;^N\O3To۩m zU*yU6.ybv[}`p< [v4-9VA, gE/J'lw\ʋ]%UhOujD ޶#8#p0ʮ@{^O~0y@/.7^!GɿB~bgkr{rcښ=zL;Ui>QFk,&V1JKGsA島b&i='+b}X~&tA_x&H"%c=l9 #<]J qDvLX21Ih/'yŰzccrqSk_Ibs*#)c&.656q F,}ۂjWͽr݊b1gB$hg5&TZ*?FqopG`!iq3c0s;Z:;lcPdb"8|3jcq c%͊R51%VLS26M c@N6˘]}>`_ךs*L;`w0j$(E|/'(y1EѨS--͋EU6]زXyN_:k|\opG`X33O\0u=kQ;tK/ Փ'©O~x~1̿zX-[asQ:9Z&/gGYNg8)cTn #^m&ƢTD|} /65/X ~;OXD("ÂReO8pϘ4e8q%aa(XOG`[#,m2gN~//Sv5-opLD o4y)LCU8躟Ի3m{V5ԁG2ԩMa?Hq3VV&ZLi ~Zxb۶E0ZeH(/)%R،8~/s'.QM( Y'GpG`; \]o):$3ETgȾv =51.d/p鯅S~Gj#w0!U9>6v&†|jz} %Z9?pfjیԴOӪq8ыukC]>?# (t]ERqCA\Ue|٦exth}7,|-LS4}BQZbGC, _G>sO c;2 떇qZ/8l6{|GpBNτo+㡱^UAh&=B9?IS u-#aWKG3/_)wH 9WŶmyfbMX!c+d䋡$˄>H Qa@yZՃOcd1Hm8ҟNJ@y>x_>"cRʶE\,ܓRf@|iW}%(6ҮO'X"tr9u)pNGp@6U/FЙ)gnPUͨehJ% b,8a]gchk?/u&[1d%_$eD^d&CPzk%'Zھ[=yrXPg\aˢ85C$9,*λms?r1{'sxM\:4Ϧ(OG`M;# x_]Ņ0q a p }{&;(zvC!N6#AVLL/Ha/gOչ9Y2oc0LwޡNeBֳ>0+I$ҘX+Ij~W[9G\"|[}X3h+ 3ïs=_.h(͆b ݔr^s/D8#`V#C@52ʹb3ل̉1-NK]jEd5kr.l=crq aızHX9\>,ao/57$׫E)Щ0,iK sKY:~ud=§}IlX[˧PֈN `c}a΅Rb. 6f JQx7#luKqOxXKsGDw2[ 孭 [78# \O]r(&cNgd?̕yY/ :IFF2نxuʽz\;~%MC#1?VOӧP?sZ$?3OY$.f1Cn֧/wq.F\bK:viS:6,YQ?=M޺6[12RCMaLR ho&%&HAcIAؿo gis3r ?} #>'-UE:e ac3j5G@P#8Cq2 koImt!f M^sb*jSrYU:[51%mz1z]~n@_ڹ5&[=y\2f\DPrWʪ<ƒ<"88^y),O;(ԀdC5Y(wMb#m[gn(B^ۭ.l>0-oqQ2[ڠ5=Z^ Ej"wG}~/Dk+h+ptBvȷ<]j#uO Lc?P1x=lJ#іT-hOP_-m{X[ksrnyAB0X(Mz/# 78#0 4jxhP+ިN`a}fiB/hܺKa[OVPB`&p 6fR PGq SeW EK>Q*KVD9qSbT*eZ5TG܅S:v,ga6f'&h)r6 )>AmJ'm۪. QzS^XSAchl)~N\  9͎{b1`ԉ#~OT]RU32֣vU5G@P#8C@1 7F'ǃI2t-;/bܧ^Ec-YM.Yc).ky;϶5:HS.~ʻA -_Ja#uu1N#6H9l&΄FN_j n1}>1fGt2|3"aҤ}؈x70: >#1@ Ö,|DG w $Åu(yG:Z6`8#0dma,XYoO}O[?#Kh_:ux<7ӭt K#yF^DkTnm6!ܘYGv:ڬ"6-Ox{0_Ac:?U./S{X!FܠĖ>&dΘV0-Hb"$Ĉfn&z|%}K}Џ<~/? ;).+( 뱶N2"y=#8#T=pif(ŚdnH_?y",+og8Dhgg@yX#ԹqkDy*uBxT)4iܨcU[Wc$ڄ&gbb;[O_wnٰ:ObR |i#Ye𸎀#8P H;Bx|r2cn62=?g5?Cc|*_k2#uE7-# K&UА_!QOLsmNknC_: ۞J2إ:sa MV„ )&4#dgybC@*;N*8 11Gls}XM=r/_fw+`pG`(fx聋=W>Ngq_0h,mK/>Ưo}D>T28yUQ'Ta mue@D6Xx$5}>_Nk_;6t,Z~\r& ,KPY4^H'qQlП4J[??$ڨ ƃoZ\ Ӷ :}yA&&ą>%ۢlB=;ڄFDEas)Jp6 `#8C_=q<]xQcrhF4VWON^ G~76)vxqb:,t=^L6kj;܀`w(Z+t}J"Z e82i;Ol#^! :MŒI0¬M[l0BR㮨F|zԓjһX_k}%31h&Km]Z?Ʀ;+wGK5o"XەְgټSG/@_` #8ÌN8~eal~}F&>?"{"~_엿Ƣ\( dDY0u;m{1)+@mRXmIV>E6HXirx:r[M۶&'Gii~FI|XT5=ZєwvGjlsat|T {,]]z0 ?'"1y%\Y.(yLu/@6c4u)[o2=>,o&;ke+cxn! iEb\ ?Uw`2 ϶@!- .n!chCx=& Vz7@:7IQዎkliv:+s4|`Ӡ#8C\/ /G6:1~lKV8q^k_m35s?Β!ncX['Q_oc^E? $3-#lF=d醶y>XVbo,G@rd<&ۦON/ZY؁8ܬ]>,MYSzW?t`  ظ@s[>W ԋ#iAGp r^Zi-`ak喇 \#1*vA6*gg-8_uvضNaRyYTS%Ou%+u|KgpNB_Y=h;6V/<ڵm|:R +)tyl if y<`RL&궽^Ic:ޅ` (#UXAIۥ>G1n[8|$3/rwXWt[[ # 8# /y؁"Mg BǑ]/-gl郻)MZCGSӭ2eZ|(#\>[vV*Gzlvh1CB2oFj>}eΔGAdE tG*#<6b[|\<~TԨ0c?hUd[bMƍnI`&jᩳ2ڥ@%q_i GpG`!`]H=k.8P%-L/7/F8/'df]L1 X.xBI> eJ7k{m*f-vM [9n?웥M%-`m5ϾQ-u[]^3aFVx-,* ܄Q4ԦA);N70d\XC%@tA  }arxG;guGue{ rL7_}L^xwa}yG/@_` #8É@s62;;/Fpo2?KaF:_ 093cHn6;{1gLRTYu-m}uSKsMP?):mac,,(Cwz vE(xE]J?e܄ -%4cjf,ќԸ, lc {LJ}x`7wӺR%ީ#55Tn8#KadiCek/bXzk0m8n辒v8esQi{H* 8'=:h jRl5c aͭS?`BkkoMe8FyGŹF9C[WA2ԭLcuuwia_/A]5 9_b_:=BQGMOȼ8}Gf:#8kK6R> FM aKͶ3aL9[kf'tW؄XXfMIܷV ,'֐馞k!:˜Li(5W'СC;,eh+d#bxŀVhkWom7^#cpUZMU~U6.w:"3#8Dx]M^7yA`\r(T3ݑV_=,Fej_J$|͂6;&&iҳ9Hi`MXb2s=i}}uq7ɬqsS-eNĘ=3dz fq!6]]DiCؗԾS퀒O} QplX XOYN"pK:8# R议*{YjX=v$OZ gϝf=Ls{&E6#a+lDޢ ow"Y?Rm5X@ɣG:Iaw\"X_1I5|m-mj/0hp#.;yQage]PYxڳ0a=#8P"\;@ٻO&vq.^wG. ǏܙG.ijq.Atc C8]f Z@Hc.҃^Vmԁ)q%O8xo aۤQ% ~CG$.66x,pǁm;**8sǏEd@\h,MyG+|+pG`!Yx`ʃ Td;kȋW'O4ǜ曚M҄dSCXMK-=p֊Vger~(VoZN ҭoU;2s|'Lmӟ Խa-"o6)EFnJ1ا`xg}s> _`ٮ$h_Xn8#ךݭvEX=,]cU&$h{ unqKLNU%? {{kF4}S/ ,W%؃ھ5ٯYK:򌑣3tNMg}#gAU}c P XÜur/@CGp &v:\\h^as8[1ձ ;2cJ|VUS}Y_^~!SI)9kvm" 0qVk&4SRɶxO^?<b?!r.,-U*uD*q#8FbFFȮ6;9>5e X\8g/,c:nHPѤصZ<;"arwJ~h 3ZCkuRJh/ L1e=F? k{{=DI2e`g 1v<ɆQթ?v|'kf1i.yۜ5X;_X7t8#0S8Kv6-dE~&|[a|sՖp4t[ҡm>7Y+&=:g:Gl_+-v&[UBub~p-HŀFXZm:ur/,OĿTxPj| M[] 9/lpG`xx{x#. wb'iWcLrp!Ũݞr7lk܃E'w̥uD#k9ۖZn\b!VR>#_X GpzHwW~dÄS7w:kxU^[y=ޭ|nD\zsPXk~vsNXRjy' =q!Kë*m'6q;'2)mj!(i=J_07wGit&]fz; LO IΚsٿAk{uW,Urk"XҚժy$o4VUW^M, lwnll87BfmS=Fe*

Gs@F9ۃs(]/%V P&mgHڱ_2wpGӬD0zsvpxΜG|h=[9;e=S \61gj v1};1ab0*z.9bMm=baND"GniS}lu+)|:}BqG6"G'%Μ]P{~LX=rX^RD%э4{I(%ڦPIy% 9Y ýTZV֓rRxY5J O}-cYT}Oy;_4&|l,#;Qk#'=tLlwʪ(pa߳_Sٶ>;o ;`n0Mҫ~N5H\8#$p]&;Z;Vi.mU%+hH}jzأ w`M@!5mI u(q3mEeBۍ2^ٷ1l`1m·tkp9I1b@ KpXWZ[So:߁ڟ.b[>jv;uzBzˍGpv$[ls=gٶL(ͺ۩4sFNqr{QsD/O ةM:m=us?AmIV<8@)[kU#c%,֭ؒn|wF_ wI MHa 9u6/lB8#PdV[勭=e&;tV l2z;/CdySYђ1Sy/t ڑ&h:S_Eaςxl,,()>CjU|7vމcw@Ҩ.W9[9 pGp#?NnP<1UM(.0$_#}q!*#B֏bY#E?4-@նn=:'۟(&\0HAL(׺Tl?`gR6_cX5EsaVGxq6/lC8#8CgݜqQ]fU?25l9}w-ӓ^& {q{D[U1EG O(b iuǪvr~%}`"$@c 1Zj;cD\@IDAToGS} *MFRge;5E;|` $.pGp^3QclIn\%6;OL$ZSl`}؇܏Ѝ!J)Cq0ZO?‡ c2S}bNz,{'d 2ځB^dy6Qc 5tegv@;t\8#8=!`'=91g۠-&iv>׹W#9)ѫ^c۵Y}ZIJ".6_?EƧ|+(TU&I9B6OB؛7 96yJŤ8]Fv;#8F9pn&M@tx:Dbsu_r{$'kjySJYk3mTO;Kqrlc_}CrՉ }Roi;_66OU!.#nU}=ss~=_/4!M]!֩#Ln8#8;;h2&hP:X}Jel Ѧc[1Ź=]9l<#EpGpENmczYַЎMt HD4 "dZvhKk[֞qA/2Ɓ,aui_s}aOJNS;{yMɆ< 9HLKZgMdK^~΂ fۀ`#H$PhKX)Ep!D+hK&EA "DI fpv۷WTS[WuWW=w'O=o֩Stю``O@]y5x#^U;qYIf<%5"-i^kZiӌ1y1nMZa:G덢Ҷ4rLcʱůćFuG7GY<C.S@l ă a|Ud¥wq#IY9|D-C?2r |'@Ca^:Wz#iO_Ѓ5{*=<=a&?46lA@D╩.' xN.]$h5Oc./?=@`VHbb5ږnOq]y?McJ˵kb撚H7/G3heNt،BJuΕh;9Lmc{q{mTn;-s)喕j:ۜvO#J.{N|{FD3mbi(VlA@8E~z:h\ҍ7Q,7NVhЪ^eyk>Gw\}jc,e\m%^#'jA7vn0+|em\/cu{.mhCp @&!PM>IIr6yASe(A r]z3jѢf`˚͸2Ŋs{8~e.5t[:5vFˢd:Jtݧ˲8ov;ǜ9J۰Xrl, !@'%/Uוe.ՇuGdph淈s:9itbYj|h(ˮ/w{ұll⛆˶^#[mlaV*-qqTuNx_9|.|\l&]vݿ5YS!6QP @`3lKykkz9Q,wr<N"RYw\=he]1uٕwv9q|G\S«˶ꑅٮ,}$8F*^}᏿{s ʐ D V麮RkWۿl[~J͵6xyx0ӘD>qi||u~%-쳌I{Njkmx3>=#EbXG -/1LR>/|%@}ߛMKbݝlvh1H H: @c蠋mʫԲN%<֥;/G9yӼɤIoͶmqFh%Dݸqkq1Ƭy.5kk2ފVJQb;Mmq7?{FA}L}Ha[~i AlcJ~<zw,6KU:PMW2h@ 4*ej뢹˧n|<<~M9F'&6 Rb}xhaOQsɶm6mg]hx-:M9q(166.zJ!sAs4TPj b8J ]ګ/ŋs_l25@d @8|j׶eiR~qfQa؋ޮ}$ 'gQճzĺ8Xjn{Nmn[A~k݌C06ǔ4ط`uo~_ mmZ d`s (WŷKnb8K$3M\?Lڀ*A,kqck:'1%GdY.;Hh$1G\ F\s.ƌV}q[/VMX/e5ؕ..և-%"mT!@8-ۺv]cq]xA.ݢ:߳WgWUގcQFxinSey7sYmzy#mUh~XlVݭ7/ujÊMm/96` Y(duNf=y9D>oٞ9Ӹ1Y0[XM^m֒tQAe<ڜF]Ќ"FN=6,w;.KVj\[M?-م[ޒc+ 0lZ]gI  ϴ!@" {Ǐs5{*߬WSNNɟ2n襧WPgq;lԗqz=6\v]3v@ק?j=5՘/;l^=FOqGr!wLSW6q@ΐ@SW`>\wi١xZ:fT :K,k{+'_=GƍQZۻ~=^5uԖ.\_ ]xx;O?ogutYP.86| 'лƉW^~F8kz>ؾ[*e^j6iJǢ>buVzMt1ۮ<j|m߿y,V^],v]eWٟ'@]I$eW1[ikiRgB5Ԝ0t%]+kiO7?洚FvRB~6NY;U3mƞ7ܛ+b-ռAr~`ѹ;wnth{77Wr덴ܗ7rֳϥ_f&ם!}]p2^ǺbJ_2b=7a#.Od=UTo^:wDE*>$GQ?OuAek\cL:n*χß5W_N奻[ɿmIxKN+%?> Ւ>ω~t7_=mlimJk>},?`ޜد˟մ?'Uj+j2p?U最lRGd ";+.΃7ݵ y.z{C~@ ΀|;|jx+ _K[uyh 9 &S>QXpq4qlֹm0Ʋpތ+2Trn[_Wa/Z9q[<\;tx,O_s\L!؅umr)!`C@)]ኺ6ִG}7?oK+Wɉ֣i5ϟ ʯU}v~*ĺVK\mhpעy>zY0֡\4kzKR6|w_n{ɴw習lo[_jKH_{>|J _B1'm;?$oI8hl8o`xH嗢ƺ@:8@<ʺK]R/]5d='C.9 %!&C(.tJLk׻+)ץi}95L]N#i/6wҝ/)\j<; o 4d#'z̐1$3x]|ս e*Q:K)(Ү58,Ggs%PVqNv[r`&MI.AÊ!p@5Gt!'tgƛ$D$(ݽ_Y$UC'%p|^7w(tGNWV[Lݗoܓ.|tK[_Q!cףnjńBmEQ: ㇘<ûRy~ԼM[/Ѧce86W'zKǷv#oX @}<8X/<0` )C |u_ѥJLWHޟ͠d}P?]4WCS 8:{qSQ'*ug;. .P{[g~/EgA'_vဵL2륺sb 8qGElGսzu{H^9mKyڦXyئ'@,m;Jy}59r~C#``dTB@~yN`t'ҕO|я7Ҫ觯ğE;\_{8G2]x]z# @ՙ)fe@0VKd/Fve{ K6Qt=2GRi,W&,n^rn{.59DJ>9In{nj>Y'x^oKvߛW;/ã,w`.}N[myNj3%v5e1m:b[>;- mQR,'&Ƞ4aeU꭛ (r$m{rҕ,3=@]h/+p?|ι}gWyfͳ1V;ߓG,F3ʤC(c6|^B)'ygmTZ]({klLVӬj}dz(}ԮSGҿo?'Ϗ[W~]H>&`8$ +Nr=X~OLc _h@;} g|i]IϤ|L/!:2cm@\X?yaqcݾ} r\O_M}~@e+m9mU&_?'t2GJ* 9e!We' tt{_TW>|x.1qW2 fO[9}`Lr%_2Tc'ӏ>(WgL3e(u;%kۧQ&Wz-\w]2t@4# zMKҕ}<]wz4.4CS 2:xG~_|I߻$b0΀Y$gYtWf@I^%;1ʊRzE;K\q;wơWk(Mk^W c`U86Ng@ J@IJLWm+inZY(͕Brb+?ߦyVL(F"?m4ɢz?7.qώU\Ayu~Gy%:GxźmG-cLDYK_3i9(t)~Fʽh1Ͱ藐ߗK8u @{ifKDZɿaѓO犵uh( t~L<G}?fs>7pҡ2ֻu why!0~I=|VԹ=z_*ӽrEz=ʘ1tm1,w >zml !CËa䲱.S!6QP @NNL.䁴v e68O 09\ɷ뽕~_p{Ze,ә6bpN &xFgV+[#ƱoW=c]#WM7rO!tc{<v WG68F'1@XR+ϕWҥd|[;٦m\ѴwuBp(rEַ7!-ta ~.Vb{:q_M/_dzV^%w_5T]cͽ9򳘣EcxR]'OA{ikW.&?DuUlߒ 0 m;HgvUbS1b&Ѧ=(Kj{GUu97|Ǟ۵Yei{۹-;좍];^w}\&ֹ ! v@I`}.면oˉYC`:niu}#]G>{_ǙBp'5~DŽf5uj_ԏVZٶ>bާ-Tƺ݃KU]>2ݏKٕ2(mK㖾/KoX}]Cz>HW҆!@@;J@|s,7?scu%g.fk<֕ &z:ee4^Ujuh< 2zLv1$/fb:8lSڕ~jw޹ @QOi6yiG{p-6S 0*Mo}25?W&"uA|轷Ǿk)mwf3Ɍƌĺ(tSuOE*z%]j3BQ&ؖ~QG}zfWseX1D(խ+NSu(ؖc{i,'!@` @D|5$hm/%`7emI t9m'WYMeMh;d#u',2jeͷfWy|*c]n;۵8lewGvwiUٶC 4@Xk>z$u{O@d@ɖۤt=Ih1u &c&~P9ߠ.5rC`ǕJv,UwYpnz۩{6hV?oUgG3o~F)G%O4چ| rb-ങ |Çj]p@%uq5[,"c-ݰcaU_&>ڷٌʫ^y~=cSwejVYM7Mzo([mH F @5+m])_V-rZ Zyk+Wb?/ɒa+Ti[<]NcY~[}~mPU=Z~|O4~(jchFYxLgAdYYx>tM/(mcEeO#(W/D]\gUd[l_2ۺ#4ʣrk-UPlja ohn&@8$YHUs Nlk5'˥zmtf@Lm5tۘZrմŕ]FbF~u펶doߪsp[zq@`&› VB8v~ 8#1 xP1cQwwGO\ L(۱ ZҫWZEW\6Q^k;Nq]uPdG~Q >b}!` ,2S 0翚^^e5@`tnwׂ Zª3 G2\BDSNIotYz5YO5\=KIdz8֫vYuBH H: o΂~ڿ߃[[gԧ-$SzR|oHReѭw{>K=3};zHh%g3a;j'f6Ԏ1U^>CvQu'嶸 z|)Q{7ng?m`Ç268 @o#} j` 0C{|3aO34$?Ɩ*jK[6{}14It^6m7fϦIzQclgrggg3k-e}҇6 ^)G\qq-̀@@ʷ|+'ADA*u&rL؎omdl{+>\ۢ1Ц[vMgDyf CivVN_6(h3J$f p/à p]]1JvV9}DXfF%gl?`sdA#-%ʞovR7ƥ֚+B8bҏutTv) Uee3Fer;ݏ :blŘ19F`<9-siOۨKdvͯyII+{;r$]hl,29@{?oIP<p2xA`t{ɩ˶He:fw8h~ba.-WcXbܨ'B:ݪ)kAIP]((W|m^{7v!066F 'p7'!z$ݖueR^,>fY7uܗn~wΓ,f>1KqЫY^r:\v[q[,I)}mcmdNmo䥯cJDZKZLgI˶-e#cz:TL |BX>ki ϧ_[dKY>4S'T<bWuڼ1փBWͥ6IjȩԫpݖT[}6*k+~MlGnu&Kc;?iT30>l̆+Q!_x=mL_UW:3d/{{9鿕BS=Nkc4]dzlG|G鄼Ouemc].;m2M9e1e*mw\ qgJbp̮ͩ Bc``,\CXێoz}Թp!uvS) o毦;'$puHʠL@Sί6Eq~mVoYDӚQɶWNujnGF{F}ۥm+vvyFU<0waY,H xPBXBOwl,'+WbK)C`#i/oO7TZ\6╽1uLcubg~ejM&yQc6F^t;1LJ뻒Rf?ۨm_:6깕 Yk?ݟJm9,0E8   ?t3:r;4vy/@`9?ȷܽv_Vn/@(P2ti/wY[ʶc,b)%+|v=Z&}]Ĵ,J}$\Jlc]:Wa'>CugQAw۲w=ZP0+ą p gn~)^|$UHWi:K7_Li5?PM'!R8e}~t۱n̺REmIuDzO6~''إ#m%vJxWgy_Kc2Su:@^n~?t X ݷZ&`uD.Kou3mzYzo۔pR]+k2:h[s;&ےYn_]Xѳ"n(lv|OM iXV @}ʿ_g9څiwY,˛\,3hn[O_M?JnJM[=Y)]dcqZ;δ̻1ζ]Ҟ%ʃbb}b(wiJK^#yexc[2Q 0mă HJg}='Zz7uZv5|۞L9Qmp@Ala⥴sڿnZ˿4D3| $ڸR)uAOm>o=鸣Y.e#YF9bJc6,wlʺmg 6ƌ[6#(4&ZlA @]J}}ʯrZȬ>`Ma+J@O _+͙fl?'wk_i55{~עMYW\ǶڲmS[,ed`fXD;KFmҎv;DGPNk5YzQe6ҹm1~-I i$ E&;Ͽ^'8Ƌ|<]7|#fT`n#W晞u'^JzΫ[]:mL(_ ֻ6ն]YJCkHky+=ד{2~lgɶKM^>@k'햻tBeoSu.]~ٯmM @`JH@Xx9YRo^_i[i'S'|y7?5P]jg1'x%o~/WV66+K=^=P ddټ礲&n;llǺT6\bXŲ(\e+h[l8'ղ,ev+Fa[@IDAT]GGu)!0MlL& ,:<`Ts{Iky|⯥KdZ6cW[΅A@t{F}akg'r/OR^79DY'?m'Yԫu밼ZlĪ䥶ٸnc{D:&]eWdl>$'?F9  +tSsg ]O~o{kk݋Yb@Q)最s?'Ms~c3~Vڳg4,m}ۚ'<(:t;|Fz[fk]}M8KRz鰬.Sq9NI|jk MZ `l% F@W*@trVݜjo |{Z\vOZ{-S|ٻMzHw/t<^h #s=vKs9r|^y%~녴~>ϟN[_|~_vsʸGh2\m{l[+ef&YD9f=kcǵ3 ٩W>6fEy櫗g>淚7ݟ?sjZ>Yc1Wv)sX>ϟq3m}Oʹn}n&'d1$sۥݬ~˦ۯV:v-,poigb]QbX}[%-3 pz'wݴCywZtϤMk;Un IԱ隣[6wެZ9-q+V*&LF?ys2oϋ.ayk9k_}9[icLSb}tCoonuy(*ֻtX%Xf;){ɡvYϬU&֕hC l F#$GkJ77_M tڛߖ6y$\~oZrKsbSoOh.M ꔒ&յ5ݞ8 oSp rk35:wϬh;f WS~{/'wyvrw_j?N{J}KSfjjb*)$Z2}mu[>}%%o7Wc[NJ Ujﲶ%yvgS+7O>6|*?#౴viGz})Sss%\|"{Z{S;i{^цOAcPeaڼK=Rkw,ݗvt[}jhٕh68>ڕ1"73ڑQfXD``֜C8=eRy7ʳ_˟_ʿ~!ʥMuv}*_\V{Ssn>Lf5w?ؽ3AcQGHzo 9aʟOc<,,cV~p믤w"od}f o?ؾn391~-\}2>eM|ǂ|[4~Sڸ4ky.ޝg Amn*a{ە>v6ARڶḳ~nlC ,6k- !r a'~;q0n}tn\{i?pzB$-Vqo~fE4Û>SLB K5('|4Xo-~Zۥt:6E{q~UG[Xf~j&}P lp̒eĆ3%Gw k+|5©fcJe]}A _lߵc @`(%pNlxֻ,pR^G+ƸAde!.mo/{l,!@XJ%9\[a2mcyQmtYJV/}JwZ o1q3b@ 11I,]eWNOxvQWe=oA<#emeo1#o'0G@- @8wN9 )HnHo^V1^_ۭ٦>J 2lcm䭶lK_D]/e! Wc @Aɚ;8I,m2F. k=qmGȻ^Cee嶏0mcL(!%: @8ԠcXʭ\uC:>Ʋmծlx=8u.2me6   ':@ۻCO+Ylw-˸j5aJqMUȹqwVbS[t\}~~|׀@ 3'e(uԒAcY}qT/J}_iwb|"kٺe;ښm\&\/cJn|]]ծ > j @`x Y\~]ek$2zi{moS6y-NͶ6I^ŕ>mzljiJ@863A@@-4qJxe6H_:1ZY&~˶GQb̶m]~k_z"ϗ>6N9=B !0iadzWLmǍ2ڶ$懼Okҗs/yCum~ʾַMXy5Yh+| @5b" Jhf' cǒcQ6e*K[u۱$m)cqa{}Oc88k @MH2N.X&Q/_RZL\8نrt^a+[>_zq]F83N2nL!0.6%= @XBJ\D&&?喩,㏪v&?>!iT>عOږ>Uv,Jeм}57'"PkI+;zwP;  [y.ۃ|A`Eb@ LD qT˶>GźmqXfhc&w2ڨmrܲt >lb^r -hC`&MbA MLIfyLjIqMou8vj[.ڕ/ec8QVƠ i``4@ 0'@1s(UweS,ei?l ǥu:Ko2wXFg2֩u66Mx @'&0J"m<,zۗrU6ʨ'r>2F(wuucSh:fE Y%. @PN bTk:>nKW٧ewYYN9^ke^ZcWi3,1VLʺ%6fI @`If)'&C5_'YQ'_K.K}McFYMn8e8j; I|"?b8K۔6Xqk1,?(!P`$B @`ldFAд%5m><ɶlKcRqMlG? >nhCx=b5mV)Niر&S @#PB3iRS,5 T/-cFjRKW;lvgth+| @(؎u#%eoAsLŗ+w0}r@5lԨ  @kĸ-\>޶j[6W-emw`Z-HXf @` %Q3ChgyLbݶnazQ^3bF XUf @hK$L:KH֫l~^8zCI pRC ̜@Lcgmr8 w[|t>u۹uEhce+un}Y>2o}=6fϘ @Έ9&y#垖ݖmbLQfyf,%Q @)Z*Yto>˺_1VQ& 0ԙ-\`.t nw@ (=6Xm%s.ymm}8n-fR}q{h2̱6odO;N9=B 8dlG[f&r'eb;ڔ1qi9k,bߠJ׭``RrA 'SV&[+UTw1N2(0]+壎 ;'/ @RP򦗓9m0l/m6o˥䱮L#V-F(ůfX㎡˲X?i{J̒Kl@ S%0i2U%nEh#^1u1e,TKM E۹,׫.|X!0)6&% @BI7MXubmlRWڸ]aK/$v[/κ2vi˱Ϛ,m|~n[O Y``t @ 9k!;8mCzNmdz.ƒ]mdQ8jQTq-o(곔q!IpzB , ZVʶ%.<j.mNxD-ee _6퓖W=n\;V]\TJn㔥i`4( @Bp"WNMn;ku.mbĺ1dzEW]:+c9mҶ.ݗ},wi؎FcoPwm|sL/edl*0@ sE9Dܮٖ6n|Rwɑ~Q}3l߱te[6Q[G,KѿKk-βQ2}4sq:x1l}SBpZ @` KJ}l۠/DOuLu$6ݟ}ʶq\ʾuZYs,h+ض}rݖ*]1e v' @=ϫl[>JRN$?emJ쫲<ܗþn[_+cr Qz-eW^zn~Ѷò}!p,' @Iu:bGʶ8NRml:Jǒ>m8+~jZz.b$S]eԷekX#G%Űg8p  @8q;YI2ZD5u.GcvQl{C_ExjmnjZ]28c]z.(㹌qiǑ؏:wj0@ '2. &.]Wel[&K^;b Fc]m{۹ض-GʸJǩޥlJA~J6Ίc좭ei{cf':A~#$;um#Jzǒ; 9NI @ӜW9J5 o|.;~~GTdo})SS寣ݕsۥt{P9v~Plt0-ā @@&u11~Ɗv$?cǘ̺QUƾ,w,K[-s)Y<]ԗ}]Kaҟ6Nwe @e8tjP,%zՎ(W-(srٗR&[Ŷ&9>jvd $, @sDIuLkZb[E]=1bQb /n?aJ׭eu!0UaL @8E$7&GY䶉rץkӗ1vzT|җlm_Ƴe J r @C%’G]ڥ,ƈچT.j1[ ?ۨv5}(!0x༮  @XjJ8|N 7ʼnpԷXA1j~і:w 1>@ $P&ӄķkkcrR6oT]//}b @A mhld_w就L?A`5aC @`I *U%]&DXf @E> Dc74gt88+Ǹ!@Lv=y[y׼qb}C @c'l $68 @ sGݒ19 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @` Oet:) @` pU3+ @ 䤶'rWSK 2=̀~ @E ZF@sF?ڲtQ߉?70!  hf) )%!wtH+M_h3tI+I+WtfwPMYצ R `kPBUޯ[wvꕋiw+]~'Yi o@k?_|!v3rf}hlՉ>j 3$§k@z_Nul{-]Gӕ̀hϧڇkSsZG滿/_]X] !@8lt 3&t-Krg+]OW?i㭏Ǟ̟p|ϼs^DViw{S.ߝn?Ly9?@QU~E+/ @M &N pO^wo?S>Rurº}7'n%<غ cʛ#zum\6~8?L[E{ɿXc,@NA> @`N(Cѧҕg>L~ 2?0'_t[wp0Q ̜1@ȷ7jҵd?`y@ D`sLNOWZjK)~10 *tGA4@zT "p~{ޗn7.~j:%J: ?oҊ_`&;?q?]n~3i֝~l4 @`&MbAt?tJ~ߠ9?Um 3+m= ݴwL6No] KLȟ7r/?o/4x[=p[͛s.W^ͧN~SiOמk~A8_5p7@``qזAXN\mk?tT}ݝ;rrqFPl ;3n ˿+is쵶 LWASH@83E򿲻>?iW66qfVtʕSߙV\m6^~5׻NA<) @`8 5| @`>j~܍=?="zE}ݝz4]O6_|c]kC 'gH@8տ{?WWҵOLGimO6InoH}1upJoz$]? Im:@o p~V}=\6Vwtҽ?ʿ=:tj` p֍!B#G"o1 dʹՍm'uJ@{O>:@Ns *  @}o|`/]{/$ͧ޸ohfkGN7ϥ{otG7|8 @_ p:+->Ϳxg] ${g,S/7?rbkޝn?3 8 @WIcΒn?OqNK~&՜Y.ϔ>u>N~=j]N]Ng@w, 3?@ DD6"~|S)R3duiY?]yw35w~ @``tVXB?{w&Guzeg@B,b;ŀ6xNlߛ;7_0 uGs:TK36/trDjU̞&zƾ hu"MڹKR[v$|t5X# p$IϦ+_d PƿF7KUˮxqΤK8^%`=DNEοD$lh9c` jDk"X ~ 0*`]m͗VV/%KXT"dɨjFm}D`4Q'MW^|)wg@-:eB!3K cms-)F"/\8XO D84i"irP@!(" B4@(/fkinjޓ=E{@1H^C/ P@bIX;Y"uң] 6حjw#;;+/=;Szt`o%=4=eWWvKJ޽lj~A[ۤm"g" .^&KϾ@.[>#6 j.h}zDݲXNokˊɂ I@fFy>Oآ˜GE(v^ͺ[#mIk}ҹ}lz)Y=GdSuz#[;˸ⱈ̙w|!סn-;|Wotxwzk_'A:A;Om6Z+Ir2.Pgk\0V UQ8@s6XciHB{XWuO6_AqIk{HdСmkπHS ,s:<@8,I)OʣvyJv<4ΑV.z圫(=,0rgom RQ,%%m[K+O־ s{ J gu%5ttm({P.x˻eoIs *lE -z Y0ɺN UC5FQ@fI,As@5qOHӹKYj#]S7ڧWӮo3[c_[LJ)`txAiKDē1}RB6-WmoHN*kcNҲ E,Z (-'-Z-sTl! pL@<EA+/N,7<;O.r!y;}Jv߬$dW {L,P[7[TlzW4vyU˥AMHX+r9lb:1qRtaX:@D{  VayʍTKmdjcē%׏}v?V<8M/szԭ~sȳLslnƴ n{,N>h P^? uwʵc9㲫, h1%=ՇMpi2e{v~@&@ l5Ny@j XL6JmXKkiG tһΝ~EץߠNeNhc7__!-|jxANLThщ/QibQdIiXyt:M^d@`*~*; .6}]kMͣy xgriv~]yJAt7:NwkA$]wVٽE'7f;_3q>ZҀSl^ė,P3q^D@|\9d )AJ5IYg8xmRԻ;C֫~yg?E)z]bt:U_4xLIlIڗdƗF>V|Ayl|'B @sV@ |->nR)j#Np{6V]!{^ک  x4&%:]ҝ>}ӟӞsu\Q婻~$a9 }+CTX6 B{B  a_0:eF,mixqGnm,߲Qd3kM'iKƱ5٢ vu 'scmҏk*,y z R+ ^ Z@lϗO6-6[}Gv[wE'K:0!)*'GҥK+â0Ѧ&i:}ArO}㔓 E@Xjr"hlE[ޑ܇!V=t5s. kG zre@IDATr`l[ߴCubΐ) JFvhCh dR8ΗH}y"" $I@NWl ?L4z'"y{>3bFit[`,Ƅr"۟}F?z$qX(PÙc"t @ LT۔@&f٣lna'9. <-@&'@`r^ 0rC+][.H9'>.`Kky9-Ȏg>Ђ tL f Η^u/~n#X%ܺ^]Ii#(F޲ @  x tF.8s Uֻ΁[y;:浫e4Ad{!_uh@mhT=4k Xt#N,#H* HIvî߹m3MP|;F`脆e հ jfl D_.ER`vm#޷KdQs.LE۟ӹ6G{Xk/^'k, I@j"DZ9FVXEЮW'˥ Kdy28[z<ڹMt?hO7ɸj66cuɑ%H@@`&o7@&-0r)eIܥwdo Ao@-@ BUg]ࣥrp^둘vsO i"э];Je@)@ F@ YοM耤Ʋss9es*-\&+'f @%@ PEf@"P>`/VN/:.>09FJ (MSr@&%` `krydRI%m Pn=RѼJcDl! z)( ' kz(oM! !d 0 _M9HR'Eh&VQ\9Xti_xTAmDۄй X@KXO@fQJnN)v5ݲ#>vvUlpN*?-dKˢN ve܁.VTДl" Pl) @F9/ kHrwY$9KYZ@w/ʐӛu 0%k8Or)uċo4l! 0i&  SnbVXe#xKeg:$Am kϧlU/Z~eh+@-w/ 7SMve_dL79 @3 P]> R ڠt'(K8[+ֈKo,=k歒hlv*6xᕯ/HaH4#KO#Yg@)_Aӻ[;̨0h@ot!o_x,a9= ,/9,9t9o&#Q{rЇI^?\#)h@*@ 5O@Y9 B1*{Kh#ںr~$Vgm\ tC.l)m׷i3eɧ: s#u`Oh([Z jil! 0)`@ T/u9esmYy `$7IN<\0>i.hOS Z뮗S^sHUzJw3Q\^j@B*@ O@Ypbz@l 3-xQȗz04Sθ⊕OdH dI쳙~PNK$Z0#(z9uVL$ku 5,@+!~kcb69q}LZt=s0 bIg_qUWKBhlwCYMmh1՞'Gnl! pw8@ T4 s40| ^͛'\Yvk]'ͳ֧AX<qz[87j8GlID_px6)I, Q@k2#U(Dlۥ8<,Eԉˎ,8~\?C̉Ο'uu:&OHL'fLfDr'EoM9+  hTnS)/倓M$h8s  o' ~(7,$sH"ܺg[cY;Nu%䜫;9:Ի)& ԖwC+UmG.1fET^e׍fIřahPr]%*cu`A@ |_Sb@:nb J3SN^":欕]e%'DN.N~m%r{> W`ZI рwlъ~" z)% 3ocwIv@?GCte^g]x;?I6ڠ ihIHPЗ/X_Q XT_Jl~S\EΉ,oQ+;T dصVnݠfBBO@ ]S`@Em`mnM7_o*gʺfb|VOWz6!Yߠ?6ؾMRC3zxiL5ң\.~o~q‰7r@'c6y|u1:q2v~$:% ` I@`2WQcgwm>`dvοҫm'|6+Q'.5L:U [":p@ Qe@ n,7IDhq5ui\zYPHʙ]%V's_&u}_v ұ:Q5m9ZJ/-BF__?MIs$H>gR-w e{%m?IGv @)* kJ\֭Ҩbm֮bˍ_w*Ww+V=*~y{ex@{$Jcۣ8b&t|z> $tv\׾UBnV4`3ہ=( agbd-3-@$@ LMY@X@[=^Nνi;ck,`sl>0.xŲ@';5WHҵ{;][:櫋kbbmvkZG ^:[,K;K,].ǝ|,=|9܋dFim[XB5u^ 26Q<6  ! Ҋ @U*am$e4s3t뺪pX\g/ߥr665+_#yZ6*C="$BHw;@ 0ڜ @ϷO^~B h}N/W0 iu.5PF@x  /l(ԹқݼD;ysҘE=-6BD :b[W$+e"|@!@jsN@W }ggdAvwbGYmURdYCk@Ɗ@:K1O` wuguyHQKR?3:?s7@* kGRfN933j5Su8HT{z{_U33zFjCAX@@pLL PU]^oOJ=wH@Ygb8k$Ԑ6 YoC@ҹ~+_*`A@ G#TC'H޻Nm F9Zd^~Az~C! 8CB*B3E@PJ@hm*q1i}{KOEu@A"xNGe >+Q^,  0y7 .61\E<, ^,6}hFd$?qլjC߂/>'= z\ ؉lS{ ௣_ ƿ-^6f3ΐg?|/`;F^"ٝ[ߑ%ƤU3 u$ > !k  OVI@g/ŴN ̹LA[|NwǷtGƿr?d<  TA9 PO:`\hOSnvO}c}3/^І?o AImH-7D@X:@) Gol?d^yߞ)Yϵč/jZݥ?=8j" &@YD 3"`]3YuwǷIoV4N `dD'-w :ۿn7|M|LR8:4C@D'$ P]N'1y{藥xL.LMi-nV~v X?wznXP&³H`3dw   @'4m8}<\]hsk| `;H4|odI|Uz~zgi?u8 U$N Pjv) 6k`X#UR쎭cʣ7جf q`LZ򃃒Zt۝5Ggzd   @xl?o ^tRKJy8SmG`!k[`F룠 XS_ /{gڈZ 6f(3$ @)= PE֔ڼU{|KDZڥ~ў (jAGoGhǤIf^}CL[?2RC@B@ 5&PDrG;2%\ y v9O pAՊSo/ ~LPLr#Y+jo 0#l  0cf@|#`]sN `Ft۽CO;miB6[ W{' xoO]n۪{Xy@R/\xD.64?@yq4 @i]mް^26KWRgX)O B%[_Ԟ`ý۳S_clѵ3)`?@@`2@@y\rVr)yz~\s4_}4\|R@ 6X\_^|NlŜֆ=?7kJ'O{d  ]oΆ PMKmMhwsy޽OwD:&=wD-N (Ut6͢nidwo'OWt@B;q6"+b@fKlIs@ؐ[~41IvFij)-kmnbW.wKwDQ%!Im{E֮;wzHi_lE3i@&@j@ڌn -[[$_H o3ϕK_4`KA#NWv= n1f3i_ζd$D :Ρ:  T_@ TE|:$i[2vH=w/ <}d`VaF]K@,@@|$` _;bhv`2jX$-]$JoJϺcFs9x]?k<+o vkwg[ws{%q = zTr:IbZCIyqW~@,@@|* H,<ےٹ]z{Hb&4qDnI_X_9.. HзEtwfOEp&>|In.-eXۧ?'ܞN ~ !? rѝyry[+ J$qyRyX^" "!:}~rHMg'RC25I RYm&ⰖW;wHv˒޲A|L4582Ϳ"0r  ;2 ;kƼ;JY{A-?qԟp$N])3Ζ%K%~Y}3"z o? X9|Ȫ[g@SG 8*vN{wI KUa *Q  Or Wk#vӸ^ mRu׭mj%P/}6JK*xz{̠ދvƽ;c%X i~Hr$ߵOÒ޻O J^7dvl۟vb'C :\av>I@fWzs6@ uw;n40iԎÒ۸Q^%,\(q+`-(MhH$ J=p g/Z>A$>g~炥>i6H^g\5 nvGD<ذo   # UpnW&LOzx\"Wk_Ƶ;x@',t9$qD36_ 66J'jԸa)v^g .5{}SӠ@9HP,9 0o@)5U@8/ {NˑȔ^c޺Yg)&xBbs% y脂N`dtԭ&v( 9/;%Tvt'o\@@@T   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !d   @@@B @ L@@@ 5   @)"   @@@T2ED@@@   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !d   @@@B @ L@@@ 5   @)"   @@@T2ED@@@   ! J   @@@@ ""TIoD!FO@j >F@_ ԟw6/ !#!@"@`*j|@jVȢ[n|C=#ԳH@ C@IgyRB nyI"  0]A 5#in?SIzËGB LGt. Ԕox\Oʔ/|֓H@/xH  xS]_')v{ ! 0]  x`-h*Yz$ Lxۙi" '~=K1\!ImxQ{XzqH:L8ws>i8,O ٬ǿ' 3'@`lI@h,A*DYbM͒<~ML_C9d"-G:}C6>D@ C-@;_Neλ-&ك?œH@ 0¤ j$O;3~*|S,Kz)Ѹ7# ^xNzsT  ( \K^+Ѷ9-@5{V}N *=+0 !^@ૐ ]z4^gHHY_\Yz$ l e΁ z7&$IrbcY@E@!UX"zvR8tгH@ 0[Ҝ@ P_x'$IR[_}ӓH@ 0@ ,w ]Ev<|γH@ 0ڜ @ BAy*ef,6ϲ/dhcGB ̶| ZzCWjx;KN;ӓl)$-A@Z%y@B#_ՑV͞7,=ANAs x H›҃JI[ټѳH@j v p~@D`$IZC{  ~  Su,yq=  #  ` o<~=ID@?yA@I ԟw]Io/8y, ԖڪOJ @,6s%Y#!@$@OA^@@`Rz4v椾3AOO1@W!@@ VYя{V_vy ! 7~ $qMo{Lr;_=a| @< HB~gH.Yz$ V L(`/$Mxd>||c@@VF@ -\'_ @!'  !w  P)H›rϴ}%k E@Pj|" ?=vE" A@j<" '>tg?R,=B@^C@7}Bb h Y-}IZ$ @P' !h8bi w_KēH@N]S@B)PDd᭷yV;G2_,=B@ (RS@ Ұ OJ푮'i A # H * >vg%礠A@(@ N@@G?.9vxw=ID@ yF@ WȜg%$  Vayʍ |R'd٧z ! PjV) > xhR_IDATԖMr$-A@rR6@|(_X}FrH>Yz$ @ ՚\ TOHޓ^zqO"@j]@0C@G Fڮ}'9ʧSΓH@0C-SF@| PFe᭷yC_OYz$ @ | D]SO$7mڗ4*ǷJrW $l~I[#? yZ"/@ yD@ 6Yћ=i44=?$.q}Y&׷Jn?d< ԰Cjr) )c7Kml&tŲk?ek<@[7 @$NY!soϕ?%)`Y@ \@Ƣ[nZ1'Q dLV@@07I.}@uo  @8@&C>@`RdA@`&f&TI@ D!*5ZW'6  3!@`&TI@(mtt/Ҙn>'kI ͙@p W}SZ@`V C>_~iA)@ @TYd@{Ywv$@P ٬zR_E$zi6kߋ`Z̢g @ LT۔exZM{e  ,M 2Sʶu>?ߘwAHo*=?~N@"@ E6@ '$3wg$}ˤdן(1@ RQd@nn{/SCJ_t<!tmzd@ }? Pٝd&W^#-W_+ g'y $"LZz3F\C)u0@^'vH߽w%"ʼng?  @$` Gp^G:ς'^}㈏BV@j[!]@@@G   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !d   @@@B @ L@@@ 5   @)"   @@@T2ED@@@   ! J   @@@@ BP@@@\   @@*""   @k@@@A%SD@@@p    !I8c9@@/Y!YD   TO@93   &@`֨9    P={Ό   5?|S@@@* vkjFQG  pv19 TUU3:_>SNU @@Tw|R\1[fKv3[rJ  z=;לR    ڄ=G/sn c߻Y#  cv߻os8LaP_Fb  @[:T={%0{A=]WrDەdz  &0וݔ}1G2g~w=&I"  Qܿ'o鳱>dBV3P\ӑ$  uݡ ޴SKc?F@@ m=c9xWnX >]"p  @I T]Tmƾwh='J Њl헃}n}|r @@jIvrc>Rct P 2SW T^cؗl{t\_MjW]{}^Fe~g]-n:G;@@04c+cw:/[Y} ks8wmm[Tn>gƂ/ 1nz2WF_n@ ][n]u嶾uw~7ُ  .Xmoc6+׶=ek=McZ91o pTG% 6nW2_Zqm̬q? tu@dA@@sFmەyv_vw򎿛mDzF`\㲄z2qrqUmxwhkf5m{yǮ;cy  toԉʵmrYxǎgwサ,8*`|:Һ_y 5ˎwW~?v@@iw=lkǏM,LJj`bGz?/}azrTm@@&#`NT~nz ue}_xnhcg"!i~au7)klC߽- ;o-ʗul2޾@@ ߫c}'zcaᄋ5 G pD>, /ʵm/.k۶}{7M[}m~  ̎M[[۾Weÿrv?m[ƮK{Ga 1ƀ_5_,ֶmwݵҰcl=KwߖK{  g|nH/_vsrUYqlH@*(E_5+vﹿ`엖{ݶ-ncm]ҷ[޶mY@@{~xk6cv2M7qgzC"@ $A1헆v{vi}ʽ}ݵm  L T l|nhFt+Өg۶+'/mP] mmk=ھWy\{۶  TG`ˁo[0sg~[E\56Gp`'rO~5z hOuKۊ91 @KLc\ >s8$CLT/^X*N诂֖| +3ƕ[91 @W 9݊3/6A{߬[U<[tI?\urI_4:H ){[^=Wqm @W:䎶>կ-~%# KdUlg,/~k^>5^qmcs_??u  @\'N[K&XN!s @xGUaǶw+Isb? +Y1cxG} @fx,sӎ$_\G|aUua>='{>Z{Cr @x}Uk[i A`%*ȏ9c9\2GK @,<6sQ /}YYS9y @)*1z^([c{gqϾ{k'@ h#j*_[}]\ssx.io @8Ѣ|o޽g2q.'P`&)=?{:_ @ Ppy8 w؟ @; -{GdEٓ/ӭ/r  @>T"P+h+W{f!G ,_yk~*XH>Xs`P @|#oy.Jav{q3]nV @%#SX-zg< @N@[ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ TIENDB`ic11 fPNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o IDATX WkL\E>s郒%%kE&6mD[Mh#*F%jcm$1MCE!ZS?m4 )B-BVCв,{{7w^lsf.u\0igk2O7}GNӎZ.꒾_sE w?qi,  ,>xPNKKIU2T=uj`ף1akK|3]]վg:> ˌdb ="y1k[QKsg;;s/7)K@{pmyRN{8]d1\x!3)KR i \)Q5;;1Z3pDz"YoA鑐pԲ-Kbg YIiA,!  .A+ =0EX@aMcՔ]6"-:lk?\E#,sЫρpϹֹ=hN I.^%'N{ @zE۲> t2b*tSX 8\Ӏ/`ґ;s\d*VKQTbIuwl,!y/]D{GDhITK븨X%jrLJ(~L <1,#-}Tӹ)0D _޿.(Ӣmdo.]>{[BE |T D-{?>O IIƬeuQ17_@O9̞F;6Ŀi Ә H\応ړ6H1re 95"C~>`rRle 1^Cw1ؓBcn*B0t6+?a }Mw>5{/#m*}XDžc =ۀŕUѽ;VΞ}|“"\\{npkLk$ 09Z1E#.̜<Kc#pZ!g!$͖OUR># M g@H3 ,F4}A0sI*K±E'Lj5ߚ;ީ.*rcWqЩGR1lUIENDB`ic12‰PNG  IHDR@@iq iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o hIDATxZ Tߛ73;̲˵, ^xEB $UTDQ qPRSA% Q4(WʈWJʢJ4]DV#I$rɡȹ˺ {;;{׳oٙJOwwG'YM4իӪC#=۰nQnqˎ$#_%Zs׬i W#{[o_tiLޣ9!7rdMN5 M#xScYq<~_iooW/\|,4z}‡D(42}u>a7\XiWqyId8D~@o8筊:WF̴[/U(a4X h'̘7¹Ad̲{HssاHJ;L˹@v2\Cy &p}lQzڋk^pU: #£l^qvB*``#fxM`SoFQӗgLsd.* E\%ĥevD/%D*F̈́uTͤ uHZ}Ʒy¹: *ŕd9HSn!G:*/g׎:a. *Ki*0< ~.sNeeNKV'r .qɖ%8_iydvRvpxDŽߪ VU6e]:O#Tt6!}iRKsFr8h+ [Ђtl p94=3ٸHQiLtLk,`-SbwaXFza!ܲ:7~(%B+yL [f"/0g`/@8;X 0 }N \|HDFݝjmQlo9<K|ZQ oD3xR VJg\ T"G)Ap3C'5 3'2[xNwi;%< lSG1l:[ p˘$<*{5\IDO߾LbTR#<5 <#=Mz~BnW=`ܹ4lRP'O?COO^ࢤWeb*pp'pƍ\Cw}q#T?S:{*9 3(sطfj Ӟr:4-~v!vn>BO4sV!xpmY]CSnC%&^X"n2]" >︟س-R~\-ѓ)@E:"d h;.Al.5UqH*!8sV.OeUcYst6Ϫoy,h1DV,Jg]EzP×]N~*P~1ڳv8ƟGxNOR3S`3V) Jz leсa0JEՖ 2MtߞTd(hS7(} u9)++[~3̨ +V(bSyjmΤ+ϗu~Z,A_*evn?IFa+`:0:>XByRos:>5)ݥ>?#ų)DkQ݃w Q"6Qj}(R5gd_:ͺ>2IF߮diMɧ4bIl)X%{RK<}]0{tAvolT]q'8Мg5%p!/$Ҷwj&0MצּLr^2P"} ~IVK_@;Cl c8ZO\f8z:lsPن_=cy_srKg&}nN;`VO8ye\ 1HlqM0Ǐl ;UV*>x =AH,6}NYⓉ׾iT8 KIENDB`ic13EPNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx} #Ev% ; "C~QχOqy;0upavfr$$՝NҝtNnս:uΩSꜮpƩ+FL>/(\.nXǩXM7nnp7zkR ͐X8M%e5Kwz0%b\iM1%Kz뭃MZ7-Ί0!eqh׮}kӖ setn&@X%H>qܬpyR@FQ`41HQYFDҩDOc_}O:Ѷ3ޱ%;Y+ρ',i{'3lܘ<fSlI4-=a-t?Nnz5XxNt(PDD"d3"j!EG"94bC"rՅ߰l ǀ==07X8=dG5xಞ9A7etzxV'㸕1s6-mri2.b<I#t&8L|{m~J O:mيIzn'gBH5Woxof%1 La_F4ni'/gW씝N\Y\ie2t ʺ'MYc~? :ozYˠN1*g~J0z؉~`(! 9vqbfye:[I)Y%bBCˆ:'{L]q@=(cEjIL}.-a;s͚|4#F5[14N CYDLV)+T!% Xcyv,t-e<'@;tqYks<VcNBOCi ,c>$~H>MWħ6jN͖<:xvW΃^81bZЖhۺ?xik,HP%E܄:2`ăeϜ0{܋fbP~/̭}I|IGiR0G6 RT"d(K 9=xy L' a /,AwF8!ѹI1|Ųـj=w^ÑeZ=\ e:x F #Ƶh6GL:fڻɳgLB7te0PNȬXNݩ6̻r ,]);U=9 0NX']q7|~@0NRL&Tap4?fDu2uY: : eF -%`Y'ǯ}s?X0;6 OC˨A;p9s(Bi*C ePh0HES!XݺfUܛ@K&FA('lw8˹.'\9U 8IziyN-w̗]zyj3t3e\^n C Nzm`0}va2L'g7aɵ I<(6R@ G%,8fmhݻ.9v"3N@k<6k]ﰩ'wÅ1z rρ*TZ Oаs݁DK,i#6lٳg0Z5# u5Z0Z#_X2U >2O pr@lneOaK[yC6YKgc"f[_x(uTdB0\ͅȵCNiX9Y2-] \w!'8 `60e+@A^^5&@g9Fy41/7^0B u}S `4s}sWXye\25.i%\7{PBA C*}w,`eFdMKA^.Gitr3lzϠ J6(pղO.Uflo=8c/Fmsx;D&O x`3ȏ Z}@zOrpb0.4e`\<8f^ApeΏ$Q`1O|gy.S Su^07s?/(i9L*~+{ j=<<9 O1qb*e|JP˨cx1:'gk/t,2Xի׬s(/p,#ڣȲo[{˘ Ff6֬ EЍ^>csNeҭim ۢg2eӔrsXLn:~֝=)^.A\5Sf-D)Ĕ]<ӌ (BN[?21TíJzX7Z aiL\L.up旍)i󺬀e/4t<= N}|0J/}3y Z"dp:ʡSs1ss!=9= /NxEg={9ռg*(gjWIW igS^u@AtNZgyN ^6*.ܵGϜE/SYtN/wd;uCc5PtJJF)cI?,Lljپq96 wQK5TxpW>şk^o!Y }LH𝴺B4p^wx8mˋ|)_N,"819,Ұ D]H,Vۡ2B;{R,  XK4h:а  6?`I?Nq aiwcD~ (j9f|W1PB<؉ /6 W]7*ARq ci d㗻1ʅ>YΏ FuK<4p8`/pܰ7yϠN diYaNKU^/+"" `δB_ƕiN'JnXs/8fb;nִBܵ+f; 9`,,Y1 COe䑡a|܌V`'NG(&l^;cؿgP'[c!5ntБ\y✽i1,MH% 2 o 2 1 yd,*CƷPF/&[4>9} :Q 2>HR U=q.{3P5adJ6o0A#Ƃ \$ez\|7'0r#Zyʀ|$ŇJ!1tC!$(^9'0 "CcƯ|+N*Z%MhU&]`l١D9 gn2\`@˝fUēm 1@tZ:&[D9~a&@N`gM,(jl8 ҐMX:rb -NK0CC"3>sh=(Z 2J4B`4].d_K%OC[Sd/1ì1Yz/'"Fb~1u"'n(!lEdtJD"yqbOo G"2)]tRC/6+p/Frܟn*A"ܟ$ 3!GWP9qPuS{l}a ܍&M2-o/B}0rZDi -҇\u8:ZNgtAr0 0#P0Z%h#g r-pw`j{" ?i&gkAb94`7bڏ8JDd(xM0h:#3D&-:TXK p 0-38,FH{7{9@ݼ[*ԏV"6f9D{/[ x8{|@bp\ 8-XUщۇ&2-[u3nܵ35ez{ h.ęk9Mz .!6v3m4g]ANf8ʸuWgƔc!S!a9`S!f2bXNŁ8:1&40|>:EhPϻ~vvG,<_6G0fx%i䄋AΧnK>Y< @݅q5#Dh$C{ sIs-`K1qoQiҙ?BeDV!t6C/R:u@Vp   !aV9_" E0 '@!sLj1Ǟ;ynBk"6nb?$:b$?2T;/%utcﯜ gK<8. "Hu=K$j3qv}43t[ow2 U #X1٘p 0t0ݧ}v`'f|e F_ jo\:ԽbʗG1~|b*d\%ŨCp][Z}t?l@ٸ A[:+CfkD Z)g,U{F3,Nȸc<ԻSdꈷ ];kջwC,&%-jMh  _Xt-zDx!wmv(TieȫTJ3},6dZO8ABCN'j"]HrtGbWCNa¤Kd%BӴOSa@DYN0=%#g.$2=NIs4wL1ϔOGmXxe MA¶-ζJqqm,!@|! Ԛ_DO;N@-N1Z“$kgѣŨ/mE?4z\1a- JSbKPB,K}jbk0L/$qi` g!'ܯ7Odvv:˳ҩ%rx1 P E!P:I:{NE?uƱ3#:$xi XiV#}κCi8c|$]F,-{ וTi~hH#ˬGD4ܭ֞kV!57[ݞ[Pg֬iI+J)?NQڤ$ :tVI%pP⫡˺s4V ' L Y/JpF6/ȴL\=d'. 'cJ+Xѳ-FmI}2Zފ1`S̐s.fUd2FX b` g,p0=!3?NNn D%(d@:͋Qfٸ:5E$Ǘ>^qOِ*eFgԭ>|tXڞWՍ#rp7?#f\a\c2;"@HAtˬey9[*>YgA1pVrn=(^TXrĜ>ΠpBwfeswbo}GO? YM(Fgi4J+RLǷ~yB`8 O1+Vq#(F"2N\| X6 vYq݌7|dŚ5kL =t|jǍ(֞XOnD ?4 I,ND :sz9 PwVNc5jnkLjv ) Ϯ(9ΚnsDB2p@kM)^L[8,#HECϛ}NYCUwT&LˤjVoyT͊nnJ"9,7A7|n W.V)vT:=9btl/]mA W8PtC0BDKm[KSQ' 0pm/qa hDZ2H10UO=9uQG3rH ppG8pC R&MRwzr?@׭3,+ +5W p jE&Ioyń kóEߐBaYF= SVcC÷ yṼ}/_{̱])Fߠn*S"TdԞSFg$h*2Z} 71YЪ(/2<55U9#4g J5TQlTkb+s÷c 03Ƀ{+O6bVTak3 %NJV~@1T09pĸDv᳦N8cX@1NuP7ՇbJCh&m۷{U@v )*ȂӦȣFUJ_^>C*W3OrKu pg:"wDIIH*"`q豊\ QG:03b/Y<Κ| pW~lZN5 .[M\n[ҶH$1,ݪ)̼WǷ+g-g8s xfb\O[x]ȳHEL'"3Hw OY<9?uHn,zU['cD([]=] lGQ1^s_=ľX08zj@01UElWM'<e_q:ZggMx 2=mAC;]7|Wt/['3E5,ȯ!3{.dȖ#ux[I!PK5V`@u{bd1p0zx zK9lw|ab)&+bݒEr<qA~> X]gn:ԏLLuZȂ;[3Z ]65@tn:E^qzGs,ܥXt U%`~b4%C\kFSM򙥓Cbƍr_z_Q-)Dsky> ҪD9,a82Dt'rOO-nuP7+q"uus1y$X#b<8T宅7p.Aine-&pj8yE9/|P C4&ƼWx ;6]"WK>$Wb| w^`HG܂kƀǟi0jN@-ve DF~ tV il@M]ظ%ϐ_v/!28Eٻ`fʎ_8eehr? '*3yܜ1 C3&hLCp:·g<5T"Dа3IdCDnbd(G?J(m N oN-.l=aM`M+mrUUp 0T&^=}s{E/`g GVtų=.].E/P`@ݻ@ }|SL2)gxOl_#yq\M:X]3o̞1>k35 -7XVZ'k(6ZY6Ց'IiPO;q7D?衱pgV,;mF5,<׫vtn0_=E58:xebw&寚׿d}b7 6}]a+ A)Nà8!ȁ= Rɕ'K xG,:]D˔i"66Yj|ϯ UZ=KRQxm$nܵMkO)u QPMd?K0D\/jet2a~Bkߖ=eh1Ũ24cӿ=ѶzGB/}]bp6&IuCS: M 7lpt),$ zbu' ֆ q-!6Z ՙܱ] YB T?Jr!蝎fT:YJU| ى F(ӈѭEO3_^ TS568 c4gjͤt7-N#a odu ujdtu*VW.#eEK'tNAONy@@SI6tsqZTIMk 1O)Д:Z4b>v7k" 1<Șy71Az qBP=sa0g:7c:,G _3ncs 2UU|FJys99fFh2:W2C8FԀqԶh d0 db1M]F!Ӏq!#@-5`@-m2YqjjmS@4`@:Ĉc4PK PKmBB!FZj8Zje42 1 RRۦ.i8uh06u LC*' u-606zn-EozWCpp7~(O ޣ^s+DQ DR%2wY?IdRBXΥ< U5`@{|0LZ̼&1?__SQO]'/Ԡ&l0{?oh? C%IDm$&~࣍%# AIB!ÌxFDGLǽcbg*5aрq6\p'0GD%蔿TF].1B3 2I1":2`}a?%:O8, RUʍivsE߹A0)4`@#t#cF 7oEFþs=-B6挌%4PTB8Sli Sp3op/.:6 CnhBB!y-FV.r@HЬ=JkCs_)_1rxR5TxG?~aґctRS10K XUHT$fW>#>Hqߥdpfj{F3G$o}*-dCl!wzH%34EKD_N38ϸL:#:"ڷ_,{z -@G(1Fрq!yG{[_\)-"Lx':k{ga4W”Xs Zbatݮ"*vk񭞟xm1 &h/bj8v 2a"[};v_?ׁ>ѥCCXO и0 }粜i:3D{"|aç'v|L1Kv6rn1z7Cv{3O=-8ӂX/Kw-67N7zAP5h+SV5 YM B1vz|lx7->_|3Cl]Uka\} P}_]1m~f UАhi-.]^c7~_l|v= c&M'_fq޻?,:ee*CT] P]VΝ y <7uKEGqvEۇ lPyBgdu׀{δ/[6s|/oz)Y4B,/ .=8-dzō=#c PD9)"C룇uz{Hdxn?vb*~̌}׼ab{~-wW_-0ڮ.x/ξ]|?.Wؓ!7 <}IKz'e-ЖboEuj8*;KBpppz QgУ)R}}y" E}lO; n}5`@}_Q۶5#>gOU`[0o+Ŷ T0ex qG`>*6:ڝGgmk#bF漅B|ĄԀlkSņP͞+\z8ih5OeŢebFOq_=B21PoH5׎Yãe@ ]ҢudѱUm">m&"9BбO l,W>#z^seWli _23bֈeSgē;wnmW 5WЀqãM+\5`Z h`xh8ϦF0U-h40<4`gJW 40`xiрF W3;K.>g+}K5:O{Kz'[7_s+JYt_|{+ Q?9ڱ5Flx Jtt'2] ]zN[V>1?WRѶW/ⓦxc3 Ҹ-za,p,vm~"fM8/J('| (}O:=:1.1} 0qxOKͻa<5K?a8ҳO&F,-'^85GDl(&- vܻ?yUg+J22y Yt{uZN^鬻|ՂOOlN 6;02'9Bb}e"[,lgEKlƟ'ᵡ0~/my1',uz9 hhnalpZ0G)vn0gɑE+Fq mqٺB'Co KBcmfƽYI׀qc+*k#ע^zd6_q?*CSîmv|jfc}h`1 p3L+8k߄Fyly ;cag YUq]ElX~YCQEOCr1 06+rkp~p &ҀqǪz;j}˳[oY Q~umdsHt?1,` 5J{# lpf1~w,P t 15'F󛟉}YI]m)7K_Չ~̟ؐ9C3oOv]_ܪPnC~ 򶟎h޽to{?,b#Ff;p˛z(m@]I5ߵU{>-2쮫Djm?@{A&@j{y7]֪v˝479Ǻ7cUB_g5hxQǪU~5mKexiI %6"h j8`8Y?g~7s?Z6s6߆pXb%!tۯsE y+];-ir&khG|nvboEiߴ S,%fN~PZA+&W-TF(3D/u/-{[K/Vٟuw L"۾w-q&h9^VZpBq::Zyo#j'wܢn9īFm/:zA|i5o9Gt{Лݵv;_T~nİ| $5"iΠB96o:sQPAf Q.Z=GzYڬ 755` 6f0`uiр087ijc? =`*7нc<Ǵk XPNhBhB r\#k^5F51uC pYBB8" 6vlֽ{{(EGǻuI$ذq0z6,8pt@ڇ UV: $ Fy#gl(.a2IDAT! [aЬ4xM:p8H &^< 'c'P}BҠ 0®6peaG9*W08n4̙04g|rcx(3~gr(jT7raCn09(cNuSh`tcg8njSX7 X i7e8r5 U&(W&c4׀~                         ]X0;IENDB`ic14PNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VyYRUu90(,Y *ŴvA#e]?aMG]0$ISLOOOT}^zUn<{뜺u^YZIDH*Cm)i0{f$)SmoQ rh{fʙFȓl|$+ݿ4cV#aufXH SZjϩRRdp*k >0X*=^,cP+wuf1ڎ$'_Zd4>57 +JEyH>ɾZD|os QÍ΂@}**Ko=vRy%׶*n}(ijڻO{]'>g;06.uo-[o֯ev_xœybS#>yj<2,ON%-d5T C| r9fXp2:5/{G<{nOXRLVUt$fѦHuF TS1GQǁS3ҳfSt^! cBvM[ظIuhϣBITjXM#Wb= ?ۼh|۶PahFﻇFZU>3/ ]ӓLQyLf q= 1~7{>3r3 61ƔZ&)')^Y?H72a3O07pD#)}+-s T()߀K6R:'Zk.YF(>8ƮgD[~BCk vCKR8~Wݲ=V.f24X۬-)CyUVKL~axpiQ/ap80 4?OgϣN罀_x-Wt'TZO 5Ȭ(aO*A/|3xV6×$:ə0_upHwKy! ._ILk O[SnWvh=9!a'WzaޓOS^Dc<@{oĪ0f(;_xfA%)UCl4ͬ_j6ofEiwQ\yL1`>D>A߼25CeU0jr֧_k7][g.KM2?3Sڐa q=v`9-ǾdH~23lD8 eoԺS*PNTgYx :y8:;935CʯJ$Qk ~T白7S5TCf^5WF)!L@QlOsk*;?@QGв/_WY ϓAzqˌp ŴsWRl#z_UϢ-ͦ )e,]kn-UlY,#,d&@ޞݛ]c hwnJiEjr'( A -=%'̞13,93KWZf-ft$E{Q+!4MV߫ąa$njZzuK^C*BB LܳlQ !M!wzj_c[IhMv%ke a㴌~;gqwWVcUT<01/|%{sռE Sibu 2 "4uTj H_LcTXRH7oZhz4,qN_H=مmj1yl5=q> 9:\@_Sw+C|y=@h9oڛAJwJB}iL/a3M⵬35_JsRݼHYYWLaGt.ZL]>7q+(omόSBn6garfZōU~.$Cƪ~th-HQ%vzb'jгAq%v\Ix!D%eZy,aiI%c3]q&?1U$+4&#L#˘Mf@;aj@jR,a P}*43N6%je3XY[ysbB:gV6n )%@v3N ݷ4w کBvp6~P:UzVjpRPU_*+rؙl[kVI2R|kUnEY%҇aGGfbN +LMKzţ3҃p-Uj/R84<=Z԰數JNycbGˡVWΞ\@r=w>!x0jE%My P6لkxͫTK1ZJ}2 V5g* @RFN+`z#<kjTiN<Yi/4\dիK%2v9-=YmL!Ť*mf4Vo,`r t {wQ- @ql,ּM(ZG! axz+RJ^{y`v20:LgF"&Ds/ckKU-{ݒT`G㦢W_Fa8*Upn4"Z ga-haE!0\%,_K˔Tn-C7ՓhHq6! IWKN6}4ŷ)LZO3%, 05EQ32 "J `1_dL%jm4eʒtg^-5_p)~87|fxf^?t-AD=6ḯa$u7nO׉p!ſT@#|?h ʡa:4orVjKkXʘa7̰wz3^)JIZJMo*G03 WL30:LT~K0OdcDɧh5_d,HMGd=O.NL$@*cS&6 ğ0A! ~4-|~wT;+H.q35|ʉ"qRf)ՙ[tzltfh|uFT6K[.aP++.G  ȕD3yˆ>R oLnvc"6l9xjȕktѬZ4\hkn ȅOdO.ON^Y#dN~L}f7IDATJPIw@9HQJb<_E]} u~&Χg[Җx@/{.3NKQ2o@L4_Fqx/KU䖰3NA@< RD7Ṭ7gͫL#-`Dc @r6JT6} kc_I>W_ɱWfOtAzaz>@$ RQTJ>)ckYkR>.ΔY@_ivs^vj)68@@n TRTXOfjIQSve H[iAUrZNӝߛ o>HOJTHV̫qff)[4N}orqr,ΌYNvr+LҰSIL8׆#d,UW^g8")@ST9o ^R3_/{l1^eV l ux ~A i !Pe T"y$^ZVH<Kk%r  SW/UkTh*k=|Z^0} kY3^7F3Ci U*Xqz,(ZUt3lƙe$Y^uk_i^hg%F <D*`S$UnJokLWei҆tײfϬ[4P/HpS"[*Sṷ-uN3 Nܹ @ڞ*SMaM|1N3|@bA@lMe/agvRI7!83D  K+)lQtUԕT*;뫖R= x!7$S)K`"\d@Ze0*A @BQ!@F   506"@@B  h@@F@F@`M@ڈ@  Q  !d4  Q# j#y@@@ 0B&@@@ j`DmD @@D 2<   !@F   506"@@B  h@@F@F@`M@ڈ@  Q  !d4  Q# j#y@@@ 0B&@@@ j`DmD @@D I6R(?T*cރ@ؕM)[ @r7>KT,-@rHXQ&w٥bJc4/JSQ*ST`6 gd 2@ ji"GsO?[h9T Q?HutЬ=)"sS=H:ZF !*Ϡ$sgu6(ex$ :$f;xKrgř]BxY|AF@J~bzi?I}}m%۱~,k@C3+Hw윕!˯Ɋv_o,A}+,2j4f;Y87ϧz֟J矜@w1\K{nbA"C3 VX>0ZWy<-g{C[+:mD@ &!^A s.0Q ͆K7A `DpP R p6CKRʮ\emcmn_,WYP;|| A0"8()}. {Xq@{UCxh߅k &aF[ г@ZxODgv,{uZ53b@ZN@ˇ TO_I{[[Mj< HuuR`gBjdž FCmM@?³opy=~tH@G 2G7o/}φѢ3oQs"D@Ĕ'g.xV&~9᠎%i.@'@ h!|4&n?3g9M^Wi41y缃tVʆx 8kC|?Q3kd&X,P .:$8猳[(0 0i (lQ?SД !Mنފت/钯%ۗ7NydВQ@ 0@{~dZq??+_u;{Q~ u6{6= UG/y7?d>>/[aȗ Ӗ jc(tcF* a6q>Dy{+βnҟ(4+ =B~=z%~љfixVڻi _ ]?ZuR:5 QA:e G^b~m F@='#Кa B{ԑlkx*?o)p1]~Hc{%|ݟL6e}ob :JewvE'I?⧬py4+Kŀ&@u`=Z;֮}B;y]a7|k?2~?Lqn^'|Ł00♃e{r>A"A5>XS/@hz4wB-P6g##WLkof-ɲuz??L%a"O]}z_r,&ƬE~7'wű175R ʂ4 &@D $ZT;JP/qśϠ=N^^?= ۓOlϜI|oN4ϜZ eC 006nTvo\x44o|tmrEQx֮ԓNL}2"v!& 3 7 8#FmIYWg5A{@%={RlYl葹$][~:j?(Rx@e` =;7{NݐU\̦~A4XO"# v2K#.93wA@-Z?`< `<}={*]w7w~n3z^@w]}5 \}J;yIa 8kx'> g=zZ~M=5 tOoA F P@vŁ` mvon;4K[bp۬-y\_ &^D6 2 ?+bDC۷ralHE]Y,2\" @ |0gA7ёaڷg󇣌ݝ捇uOwn&ن@;A `{ }+ "+BО@j-r'twgf|rן 9z9H _#,u- -"El;(CSQla8C/r[GtM?}Ngd>9w47Ky^L8>ŏ˿em7v4LlCxBK K0b9l:*y˟lmx[x*{^Ѭ+d@ڿ ci*~%d]ҷ:ϐLӾoOc4H66hY񤫞 |t-_b;vz>I]KA?R!֔:Ͽ[KOOW(LHqdv|XQ/J@+*M"k7=~=sv^%Jⷄ*OJׇ7B;%^r(xhw6q=?)HFQmr!l"?(%٢xu*^FWډ vM%RE޷Rqxob>|#o?\`>P:%! L#`@s oHw>#8j`qbSMl\ @`4)*Fy}2*,گN#◵az;Yox(O htxO%mkm(~ۡ8]Q.,kᩆA-&h-!/Y(FQn&ZkGؠyM G'f2gk wˣcejaxoU~kYc֞dhj#6^ ov o;鏨("DfGOGO 7RfA \x.otx~b6Z|v/ˊ5),JRI'ӂsΧ5S/2WVgCe;hg7О_7oW)&O-Z@  A'b:ht_ϦE|8?Z "܉g|]]?3T 4}QZz7RGмCe+c2"uփw~TZa ^f!W~$QVVo_M~۳]FH%x ,_E,/=HUu 4p}= *x3*eF@jRIDk~~0J4cSGhYNI*@ B6%ә4m@c7/nEE׬/CcBkytgv_HcE;)Y,"$<)O8N]hݱ1O@\|ꞺTgzVՇq0 |9[G [s oC ۷ȳŅEZ?| >q@YcKF$~Yt%a QÍƚ(g+wD?JSAGhtzp fn@@@ a`$l]0p@ Hࠣ   $ :   0p@ Hࠣ   $ :   0p@ Hࠣ   $ :   0p@ Hࠣ   $@G.7.T*E_kmw[dU!?@^J̟ݏ-W~G=ΥB 8r[7D|k@%4 `BU5rX Əz=Õ z׃W!tP*OO{fӥZiLRRJ;hqOd s-R1LOV[WӞ~;4C iy}′ 9WXJlEZ?cK @mC15WANe>E;Fgђ]F[?j1Q1WX_b~g;h"8gΣ%-'sΠsp@CYщV교2k) Ns'-OM?|)jp l@Ȭap`c#C@dA@Л=;ѵc FU x.ziET_osP..`e gdF## [)kYڻٛRO@= S-(Fɾ{~K=ܼ>7*TKU#kjyG @ rH~r@xG+(r ?muοYl|${V\-?dk%W̦맾Ny羝~W-lGߥW|3IgO1H}d1ST;Tf=ͨ:P"Mʟ_}yΉ4_w\{U.8оnM۾Š˥3ԿUً:bN@'+&3VvW~2߲ZȀ=tįg%u{Rs֛(3{F7b@ykNVɀqny[ `|W"J@\Grǖ/߿,`d"!30g9R;tΊ1TYA}LIs1@zߘA)UӬ^kk7ݬvSq(+C^Nx?0n^Zsx R`ЄQ?(>~lv7$_Fg$sH#f%mZ7Z;ʃXIzkqN^O?P7%v~mW1 9"C@d5^~_<}E:'?ΫЬ*8C< 5J2Ek[{޷Fg*;&osҜ*ƹY$QO`Am$+oa;o!U\v*®hLeGxDw7::Rb37< AV?~jvQhbl *)ܶ-/ms3" hA0 PO;Y$Am?n>yEֆOyB{_?76Wqn:;TR0Z5^oܹʶnN;ytKE<_7K> y.dBƹPa h@$ws>R]_Ь;Na#:0~(!J /' G#6^B#m:t8yXu‚@,wyjɢM|WE9< `AF?[;gxJL{Rp"9ƒ _R~`~<5\Y+9q_sJqA>0/<QAn߿''b#<80j EU}A^5!-0QV<@E߂wWr WxsG@35BVH]1Y䭀7^OŽCaDfj% B`Xw.IQElw!(xJhS|EXWRD3`#:00 pm4tW縎 @6U<~VlI)‰Dk Fl `.ko;hG*ν8y p^_t!Vro#Jٚ_U0Mʼn"D+BBB~,zXoqWyoJ|&i}RI' cM+Y_H_?W]VSsN8=ra֍`zx9^ 5PaG|׏0~*K*_¯ƹj(30b6`xA؊~~.Dp 87EB@\F rF@7{ˌ\c0΍C1מ;^7~=bcB P5B֙FpQ_=sql'[v=Q%чhj0MٔʂJbC@l*4^9˗7qoI~B{k|rx6o"qT/!L`A]3>F?Hfrq*_ڲ`}֟Ya=ziBEs@9r@AJ!{ X/kR]4Ùd+ 6'qn[ā 匟<̗OK 3y"8f (!%6#?`<   f`ـ;   ?@@ڌ 6Pt@@@f0PBh30l@CJ  mF= DT#k>P4A:!3e Y1  Q!PZ]CCyW88ysBh%NJY"6f܇iA1N(wV(k5ffsHO5<b@@ZMN;k{emy0*+u=y/-1  P\zm^k[Z"`,E_~-=w>>+e|A%?K}t0&Q  v:UX3/L6ê;UKMgg'. r9!TA!@xQrI7|huK@uz_Or,gUjHരh` ^O,9v %]t: @@ SjTҗx=O82$E<UԿOi@@ RJXI49s‡rדFN*5$Nh/.-,++,A20/.--,+*Q@23N60/.--,*2ؘ810/.--/L2O>;9851010/..-+ 2021210/.0S23I23210/.24`Ӌ210/\;25k210//2P25o210//25o21052l210]$248:;;>t:;:621003\-2=߰^42103R2=212D22172r521026212m52122/21121122732231רU224221003l10232247p34543225o225o225o225o225o225o225o225o225o225o225o225o223j22{7227r@225A22622h22?3224g}t22\22  22!F22T22 22/)j224ZՊ522ނ6225V222242/62262f܇2422 522pڸ62622222227a262527s232UN23228 2328 2528 {2422jE227s`_Y2;`_^`J28Yb_^f26224T`_Zt26227I2 C24228252^262665227N76262528262^262Zy24227A2Vq2328252]262>26227{2:25282\262rV23227;2tN42 88B?>?262G26227C2B242 8252396227M65235628242T|24227J2Rt232 8aiggeO252;26227J282628262kZ23227J2nM2826432D227J2@7B@>27252JC@:228K24 r2328252S渿53227B2: 2526242wI23223ju25sJ2ID2Nl232232255256323623652222mU  '8GTVI  !!1Q3! E@! G! ! I! ! G"! I"! ! D"!  ?"! !9"!  "4"!"!"!  "1+"!   "++"!  ! #%$"!!"!  !! "!9"! ! " M"! "! "'M"! "! !3M"! ""! ;M"! "! GM"! "! I9"! "! M$"! "! M"! "! N"! "! K"! "! J"! "!! E"! !!B"!  ;"/rޅֳk9",0"K("X#"O '". 8" J+"$?'!! ! "ӏ("! !B"A/,)(%! + " ~"#! "J"#:"#"! "$Rώ"! W'"%_"! #F"%c"! "%c"!/"a"!U$"$(+,,/i+,*'"!%S-".ܩQ%"!$F"."!"6"!"!)"f%"!"&"!""!""a%"!"" ""'w#""# ~ހӠG""$""!! #a! "$""$%$(e#$%$#""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""#^""p(""(f1""%2""&""[""0$""$Zsh""M"" "" 6""D""""^""$Lҁz%""ۂ&""%H""""%" '""&"Z}"$"" &""dֱv&"&"""""""(U"& "%"(i"#"HA"#""( y"#"( "%"( p"$""_6""(hTSL" ,TRSSQT<"(LVSQZ"'""$GTRSSMi"&""(;" 5"$""("%"R"'"''%""(A('&"'"%"("&"Q"'"Mo"$""(2"If"#"("%"P"'"/"'""(p"+"%"("O"'"gI"#""(,"i@$" ((30/0w"'"9"&""(4"3"$" ("%"#)&""(?&%"#&&"("$"Gr"$""(<"Di"#" (U][\YB"%","'""(<"("&"("&"`M"#""(<"c?"("'$#"5""(<"1(41/"("%"<51*""(="$» g"#"("%"F岺%#""(3"* "%"&"$"l;"#""#^j"%h~}|}w<";z5"A}~|}}a"#""#""%%"%&#"#&"#&%""""l8mkj/>? ???'?2???K? ???'?<??????????????????????@@@@@@@@DDname icons8mkrt8mk@ӰGw D ~                 #  +  6  B  O ] l }           k    $    d     +  f                                                                                                                          &'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('&  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   fritzing-app-0.9.3b/resources/system_icons/macosx/mac_fzz_icon.icns000066400000000000000000011225151272363601300256530ustar00rootroot00000000000000icnsMic08D PNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o?IDATx} \EwuoɾB&d ( #"80:߸t\gAQ?p@  0!l$!y[󯺧}}ߩnWթsN{ιu֭P s>ӍW+F$@GS– ilIXV?AZ?ZTq3$}pgE30N6IEe;;l ]ƴdLlK/m/~1z>s]S&֯_lْJ{bN<%n I)'m={l-}1K ,L`G׎\glcpg׺}mu;snܺ[<HH|K;~CkSbe+b@ͩ6FKW}@n`}^NTxNtPB%JsjxH::wL̞{o~H\{^xpcfM88Ɵ#MG/Ft7g#F@F2 Jl:;T^\guou…ݷ>,eP'cߦ vb%2@i`\.wyvMzqJ%{`P0<41bӖeP/:uR9C߄KlAeG~5s@4"F5;T@#!*bw&ܫ^!% cyv,tge<'@;l q@9+օv}2#`UOSi .}ݩ4~p|:7[)~بm:3ZGjp 8r5b# BxaT.aA`7-p/pa"n/-"GhFȀN ]t?s;*i&q N#s)|cx遯67(s0 m2L  =mzy &k`;#\"t2|v s8v GZYp94ȷ hlq,|י@BistG*d'O b(ՖCGm#?f#sb=tt9oGPnhuJe'`a@1?v<,&@Wzg(D,!n'ih(.Vf.&@ó uaEƃS >/|u*-&m6̎i,h58O;z\J1?r #Pp7Gv%tmT-xXv_p BeLP 7Pl*b:Q#*^A)x'{ AEs&HGPTMG88@ށa1%cB1>_!q` PUǤ$veb1KQ_9>ammL -1c6xekf'Ak+9'?Hi, xEk KSS⎋r;/C4p9洁eR50#3L0P1j.113' `"P(|Ts(i_->cDw3*HqH0 S1P>si8H0>p 'uUv0lpqh)rsCoxBѠ+)IScn~S H@)!! ʪD& $ޘ]yj+͔ GN#o;SK8~h'<9;U\{綹%>}:yHʈQ6H' !`7pȏx0O Vz넌Ƃ?y"vjа^0ջ{^K.'`;:!n2yGørN3/a4A]ht~ldx9̓8X (g CuX8P) l޸`~ԩxw@ヒ:21pia~4\f{7f޼ϛf64!G T9,гASL M vL<)CK d;?rl< Jn`:]y\ ~|`yb?T/[uyR&O7շ:Gc/8;MFFm ɝCk6̛bp.4!JR/=`" >ʏ0f#͛\q9V%8Fdׇ֭.=N"'`IwC =dG*V GаsӁL[j3ǮYm /X#d@qgW{&&C6F*2/ơH :Jz.B(㳱G/<˥2d3/d:N@j!k g_7l*4[ jLeNF;NSGX  :q{2vhSo8o"@͖jqlz} cVyJ #pg=-H \c ƴ-A~: N A N[A F{|)e~llp? ,ZS Nr[5BmaF лl'e ^ϻx_&?aT72 SaDtWl݃K܁:k6g?8A c?/JQƗw`~gΙyԩS^V려(5W9Ɔ:8'#8x~p,?!pm8yp{\j/'nN%EGRaՠ۰ i*s]`A_S2Ao& 8LgйpB9HH31H f}m~Ѐ{ oUPkq/9'oHsޤԋ>6 y~4,v鹈1O㚌cy>m @f~:F ^\xnNՀ8j5XCz6U]Lw`5~|p ӀiÕv2"SN 08`(wT$R Ts+qoSVIJg2= ] Nxp X?yyU#/!bxϋ[cKsc~ev{Q1'_KuZ/?"ԬGh,Sb@K~"vw $+1,]]E> 3a^8QK8qqq9y)L2{A_^,_Ć]|L *3=:v#] 0,OCo;&_{/8 '`'!bCӢ;ET!)!\ ~,DNy `\WRy:ʏ0Ҝ@bW"4gШ_qUIWWq2(hӪ1?b};@ X#eSco=ΓZFhC:XX%%Հ8j<շp}5'K73f]mv >BJ(ð`Ȥٳk_.:|F*F^+P";|,(i3_io2غn*1 `b[cYG9 HVr8z5ˢ>=IV9)q_7~( drtfoG>g_'fl1  Zc@vKrpR0.4e`\<8fx^Ae͏4Q`1On|=yo- Cu07s ?/(iK9p7},}Yc D8mX*e|Pchcx):/gk/l,1X-klr(/p,#:fm|q-Ff6vZ͇N^`#C|8#_9"Z.~oPOS `b06|kϔ6})*`)QB.A\=WfD-Đӌ bN _L-7JW%W=cnQJ\J.p族)iƳ󶬀e/ֱl<; N}|0N/3~gJ-S2)Jh |ԜC6oh|vN'^I'@ ^ջ9eAhxP\7u/cl2Ɂz鼴j@l6.ز᷇0tzD/[[tQW3+Ͷl^(:%#Ŕ#cI?,H{ glZ{sf5-}G6Q[5TZllܹk]ϵʮ,މ>s$~^ZAu~8o;&NXҏ:o* s"XF1jjzbzWnl^\oKɲv0R.Ӡ @N _^~ o=C`INq QĴ#ʰ| j9f|q H1y|NP pxrҬ0v˿S3H`u8&PNư~[# Џs m6_N7 F`5?v[ϸPeA/O dyYa^ KU.$"oR^`0oZu/4yhBv=kV\sOXcZZ6 MkwbVӀÈC4ô3y绂'`ظϐAgIE1?^Fԇ T0@+z#TM4IۢGV`8XmquCLofF_6~ANsSGp c!kK &Sp W=,RpZ#-4Q .pX|pvPWbLX B`+7~^0X. ]iz.\hG0ylV;)T9>a$@N`w/[h$m56j8%m †4dNpE(ϣ80F3Nf$?8,jyeؼU\y aa*1 t65[P{ i zٓ}m566tj>(Zۿqv2Bh4aBY|Z`TnhX%TUzTf4}KԔڲ H n٤v\6, /;E['%X@{}}@e5Uci40]أw_􊹴tx:JNDm >85TaR9\>0& d%#eU2ݮ׭Q6{&GԦrð]Q]8N(/GT]A&=+ٟ^"x̴EAT!֬X2:td4كdB&_|r廍FNhu dUvR tu β#v #``D``HJxG\!@OgfO.zhW8Z"hfD t|:* zatE'MmM;t= #`Z3mFp4L&SCRkns⑳vxk::y[Oχ>8EcaP}QE"3}ru/?VD1t8n\fYl&A ;CP _-gg`8&lB\* jn;mXbR3{ < 20mY-h>3f4 h Ajo.6շ<ӿgXNFBiN T+[:;Pf1>]{> -!r cd}y\FN|N34X]H[A"L'ٷg3a 3hv@= -~HgV uo={m|dpl8:1tKphopN s7h997-&qq8w M?Uj1~4Ir80XcHv_kC<(_= @Ϸxq$R[^H*gXM4] S޼~I xݛ+7gW`JN矏)C }Zo\|s'X NC;.]L8Ql yWR:9z5+ߤezWx8z1}͕2[#bЂ8c4fgpBfO+6tÜ)+^ܺjWe1)T.4@}tDpyj̉oVv\/ nߦƹ;1`MG&i5>gj[Bs rmcN>aBC^'j"hf .$]ݻv5_Jq¤λHN: ;6|LT扁%tг\!>zT@HmvA&MOܶ7{ܹMqZ)&q Є熹WYްq 0Hضcaf\cp(1J(%<'2vR$kW hI?x/F%q liP%s(2e\Y?`tTO RGu?rTtڭ?WAܚ6Y ǕT],lNLF l;6ک%rx2 P E!P:˦}#oa XmR߃{ιC8!2p^ǀwJ_U[^F <-{ UTi~iH˜<;pL"5D3j*GIh! d- aHίk rxK@ۧ{w}T.OK.:ٔF.`8Lyr4\\0ȢDN jmgYOYi^o0glJf{׿vy>5bGs[> ʚ7-mEٽzȏd)it.0rUdqpMkap9;c`hZS ?BVK rmV/ @,嘒 UKӽHz?ZQ;[L[i2tw T e@mEc px0X2q9dBRs@A d_?,փ>@?d*鈄+Fy⏊lH2gsklW c>6@N݋kǏs98mSeЯy;1$RB+hMQήR}gW \UG=Xw/*V*p9bN{oq8X's't3Kv1 yE^@,!!w7ziMO~dP9`Pg e|8adQ[@,〆ӈQ,8iFn֫k>tŊ=9C&I;Dɓi,f7L j5ЊIkq.NEv : z9 0vfݻwϸÏ?~<kLRdy=,(zjU7; 5u\6mh봃8drwzw7F( b 6*`ާЖ\B}fls3yțgafpybbGesc/ͦ&}7&1oqUĢ"? l."1"!l` nwi H"dOq XN6vqzc9ǍǷȊG;D"Ej~Ú*P0ҡu +`Jjc-l~p3uK$7L6< 0I6!W'H2 .g']~lȣ,d\'w&zޒUoL@CJK8-,Uq|j"ү 0v6>@7,0ǻbjCd.رu!U@v .ȢiKqZIUK_^B+7]0O Km pG6"=;@I϶H"h8hXC.ZΪ8HhCOˏ*Cšx2 G`W9d2Ǝ [c{c~&U*ITTz*7|68/^<(jS]W ,ڰN _zYsF8 Y*>@nadb:hB< Җܿ-Z؅.;=3ԁw=i 3Mpon3"!?>K6TdD}UGJ4iJdfܱ;֌HqDʙeLa|[yEHf:;(Z+lEY5W%ju9o !q'Ƨ'}}w CmL@Tx ' (ݬKOCh٨щ͎zO%,HĜWG[5Qyqv+60= s^L dJM|}ƫ&] ZFB e~)1NËL@ZdXp#V xyqO TP ص6VF#}} lV k,.^YjW/IϜJH C@ؤcڷ-^Y'98j`=nKNEQLS:q:1y|kLH`иĤerPKb"wP1r}HcA u y7n~gxb'Py&5z]kfEdV䪫2''`9 NHDʙ{)&֊^ݿ?1Gv\6[*KA@Û; Ac<}~Q|q-u#xcsÂƤ[0wNS7G@c}hfod(i"`L#U''ذ9@#$`F}Oݯ%XhU.Qok~T^G:?ïNIKϬJϫl? }{զ/Bw㢈! F?8Fn $3J]t .{΀~y')j1Sb2`\EJܫzY*&95sܲQmO)5k NVKɌ~S{/VPrĢHd?-QX^Trш_GUzN; wM{TNgx5k% bMZ>j: ~p7e#%`b5\ڨzC;v*Hbj|mxej8o(=ntFũVu̧H쭮Z}H @F|+)]@+fkm›Yqhf5Ԁm|Fvg/ɵl1<ʘy7ÑI([^x^lN#Xdg23̛FM8qql!7Bpm8=[WS +αmq)L%FzW3b "hPOmK]i@@D SmK43 Y8zj@@=-ubq1kG4PO .@4  f "qԶ%ĬAD@=5 ږD1Ӏ85#SRh fqDԀ8zj[ Lb ՈHFEDl9Gϵ%ƌ+^R*)]hʤp>=3Ղ/U.Tj Ȝݭ|VVq. (Tq׀8P1`蹬:[UϗRcſʫUWǩCXȂJ4 [?nS |JuM Ï$5i○$2Ѐ8J f3*9aJa>f'c& FႫ<9<mHN`eRФ4 ,70vJ}r _z*uշdZkޯs6VjHh hf}j5{6Yb857$d{3hNd,2IlVTN_y{W~q+}iPg%B3 Y m5T5%1<8u#Rq șO X!ld|toIhwV S4\ÿQOs+NP].5y%G 9Q@ױ'\@ƈ!~cǓF!@84] -{mS:Qzzj-R{TFIC"}J8@U&MQmoԮ~- @/=Փ }|/t сFb(FUR5%O$UwQ|# [%7PGR;7UX0S={  .B?b/ f  y' \6CC2sjZ/@\g|B.S] u^+C PfaEJ,jH( 1e\ bY:3]I N 0NWKW!1ր87D̚M} b_7=Ap<=>!F OD,ĴaXh.\scjhsSA ͫqqo0t1)!I`V d? _% 1$ǘ?IGZOٔ"jBT.QNWT.c0: O꽴hK1;B# ͫq1oW7_׊9=޴X4nd5~P#G=1נWJ{I).RV3 x&;,= Tפi*ၽY߿ýw lٹ k@@u\y t?TIz&`ZRme^ۥۋ>q#xyޛ0l/ W{ ϩݻ=Q@K}5aƁ3IkiNOhOd2; u7`%B+hӀ X?]WTF{T9P;aAPS}j٣W&٥*8G{Y6((cMfPEQm5 ;|C9м#CN|:FЇ\|xp z8J!Q،$p -@Ût{E5~]6dW(~ _  GW?s=zUzpFZwA{"c (uzHσbG18݇yaR/۾jpgjzj@@=]e]x:WNFm.}@Ud|ħ-IҷvzW.MQ# _^}I=:quTvTU W{ Q˫ OJ|# QcecP/ۀQgEN !R"]fFD*Z4xNu@΄>õ緖6~z_ݨAwC?#e%ztmSvrs. {.dӀ8tY?guϝUܒ# ~I +traO\ /{b0.4 XSK ? LzH< $X"Vh)vxwM_)?VI& FmvYjWA0½O=WX*AnP[yC)e?O03<DqE`_Df3N3Yw;8,BY:ϠH2S`s7}txwd'62WI٥RXF5Tn3y54ԟv])v2P{ 5hr+K$::Uppz5,L[Z؟)YuYW% 5 zɴ {Uyo7ְ遹zQ_9 05+@IT2zc>=AꎅGԘyL<L̿>wn=ރCXs5}z\B4 n3+# Hu۵V#_\@ бdm|X喛Tn Y%PD!ZTS0Cb!k.wjzfxO˜T'Lk@& unQ@پ}j/WBy} yMS*ٞɳ%y&H*R[eDw}jKgW{#~.%cwc+إq.u>?G)Tf5!|.rͻWXꧯKRdௗҍsGm.ܳPmGdMFUּ3#-Z3! =zzanYI6Bԙt]m4⬯}Gog㥷7Pr @׳=:bsBM!cبv5ڴ1D4 #n5gRƻ*sK\z6t*b@1ʹ|Oq6[nH|#qc*G Ѐ5b5z\՗gLC| <Û]04V2hk[; î:hgϹ`gީ'Xq㚿%S'Lk3; Oxc=di~H5O/ W{D&Mj$qa#ّ&8_ 6ae]+CXޯ'nH\?N𵜕8b @ чl]* =F@QE'~gm6>jb=/t5y:pՍ*y)}\O- -ha5  Bp0mWQwJ"_V[") 2Q6'90_Zƹ(n;N;wf[ Gw繢kVyɰb琞};=Y%,&T =z2>M3EHĨ pWo醑?ʺ]W O щQ'벑u ҀR ZSZ]D4  I4КLh@~hlx9g@,4'`{C; E*5`i;]%L(X4PbٯceVJI ЧѷJ@GK33'Mް}{/vXqD `F>L=VwebDNTǖ2tI #G"Gp qX fkС ʼn %H/}N12>NƄ88(AA#aDq820oq\~m<&W~hCBi3XOc(ˡ@ j6raCn89(cN ST,814,aBx*eBOY6\iX_var`H~2JLG.  Dрh@4  Dрh@4  Dрh@4  4\hYIENDB`ic09PNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRvf`Y[Q1F [#+ H<#jD!A(r)޻tOwwj~~z߷yoM=O[zeBfn%-f#m q٢sћ[N#sE:c`4lʡi׈T4D,x`^H 3gGAq~WOU|N~"W4_c%yXC:rZI"}2uLαN#XÍfj\]_,W4W#@b5ڜU`X5,F@VcZ+KWrmvΧi H%\mg&Zff%-e[]vyg.?L qjGOY_  HZ8-Sx1TN3dRT/ܳkי} khfeоL@;/SffX @}^UC2Sy̸[ URn]piG0yҽ;vB@54<0{&`^MU~c*(ryekp2l~6졷;Q?lfΖU FӴr<"n7"*W1gD2/N#` @ ;e"WfG~UҖ*ە*edz5(~HњTf%,ΌscʬqA|JΣ3 F~P5֦ k +:_sZd2ldU$2~+| I7༰i tNպUC<7ZjrM[3Gޡ溫jL~3k4}4v8?/}C$?z [:11!j4ldIph\X-,W0dz~bGkEܟ[woUL5]š k<$W[h˕^~rFk^H85DרTD@:wFi /^ҿxqKRbZ_Jk1`PuFJT)L)'QGQu[ Q2^! grvM;ظdh^MCفTjXM#Wb= ?SܾhjwMNxdV̼R*qvM#3E1%#(I445ՏU_0Sz՚ ~fXx9޲8EٔY y n g]G2[8q3R,R&W+metNٲ_zcD;VeTdcc&%-T$N2chIR'~ү6[N_o~4::*l/MI\}(2_e8n$PUoBʯ]OL2w">5zfC>NšOz-84y/==&q:L-`yʮ!Az<Q!M6G^/hs73Hd6@%v0} ϯ4GC9߁X#_eө1\j u/PgJO=cCR'W~y XҶowP[x2)7M7اТ̶L━6drg_>xD5ȾdH~23lt@2|Kvs (y(/X,Y~<<Zpk:ɕ_KIڣxB3{ogk6c%͇:Jk>% SBGJr&̟8U/q;֟G`h'櫎 eF\XnJZ+ Рz_UbE;MOA[F{/3$YZUٮYFXL=h0j}ܬsT(FW _y}+n"K25f)$t|-y_kE4iES]7jvVZ&hfi9iEkXqZmB.YO1}ͪqToEV 3:.xwF^i% SP?s FEjSiZuŝTj HߜLclXRH7oZhz4,q^_H٥mj1yl5:uC"PW,Y{{|O,Y{8U{3 W#RIS~qA}@I#Q_i[%lI4uf7 +Lyq&304V;:GwJ;DJ`Ư,:Hm ѬI4%)m*3_sIp-Ţfk yӴ.Wl͕e25N3kOK,R7t 8[3pWHW;/RE +oO 6_E4"hqfi,ndF@sPߤ"qع9\>ʱYZ=3KEUɛ7Uaj lzcӡQ%*d~4N|"dgDKXFJs%\kM汄%@t)ǹjVNRJ֘XG `2YG 1㙛܊Ցv(6R,axk-Tifl4K.f ɱ8V w @o Z '7K, ,'8 OQ;UHҎ0Sug/U"w̶6mAk՚,/3==Z_-nVqV0vtڏ|d.[ވ" $ԏW<: = 7SR(xKs㽱HI k^$Ogz j-.Zf\5 {=p>!0j%%My P9لkxͫlK1ZJ +^ lF?h X^䈏jZtZ2UWxcnsz : -4ARIt[NKTB W*r1J%Me T@(E1(ONZpSiTAW#4$luՊAisk?/oL_UZ ҁG(=?1 ӏ쨼e[Ҏ iThO%M6[ޏ&BWuk :L<0rXѩVLm G[2U-UpGŐM$F5RZiHR'ל>V⻔ ețT[ fVy}&;(j^D$@w,Oh꾻3F鈼qW=$ݛW$~/Ov3:JM|Uifa<=T|a:1N#dhT]A]R9ԗ3LAgSZm6} K3l֡柔6 עc+E)9CRɠMY|GS̭ G`R)Wh8386 bhT]T#鱑=80u*,\~EoO{xG(ٔ3ӝp̈́y9-` T29wizx#J>G{>#cuWq':$2F՗$Q2+52>vI5~Ho8v J4M}FG\,j, WTի'r  lUnKS-Dѫ8鏙O7u̒TTshS 9לraʽ&@ċȷIR2E 5IAe O$zҨ/N%qV惡$f~h} Tȕ \ yrP@*cSΏ [e* U<ŗ87M/"_dmSu8aՎHL64Nye(~/.6aS;H64ϓ?K36GTQbz"fX)'2LEVg*o:鱙GuMAkldy?W^7Svp_2+s*A$x\\Eqjv4vxd*TIƛEͰW}Հ!Y њ 6YAxɣe u0cB-S.G#_4y( hϚyF@f 'ZN62Iw著]@c?gkr,]T9oʱ7-17%C5 'IzRՇ`9^m1J4_ӡ׾<=4|p繳 U:+ekf6x4˟paT i<'}8QàYV\.QD1gf }da 4c;:s7rqhR#REjI7#Bv*)nT zh={uyWrBz5qIDAT$SwSds}N@r`QJbv<_Eܰ;6C%KqG^30HL@srs*l@.Q;@#YCtZ!<5AŽTܱ&~u'wj(*\tU(3y8 'ݩzqƙIW#5iH04j&̠@ 8U/o "G9?-);0HCl 奙Ђ3H+/VA_p3*f h tG`֗摰8"8Gʥǚ}0|{ $#0srF&&4ɛe֫en:_em+zȳ>B[kG5{(vnw^OIA6-CPޖg6oWGrFJIXʚάˌ;,2zƩff)?^au%TȲY&mAI>׈x!7˘Q<K8O3`jV8)}7+M@+~?b `*BO'T|8/a5w"*'4O's 5@{,UKkӼf\~2/M_Rnʈ?sA,T)7Iӟ"kRO=eykTK^3SfDM ]B@Uىo47݋Ai^oZj%^*rK'p  )A""Uvڌr*FI>H~U&A 01@CTqyWf*Zyx 'ů%ƫX+3ͧ~Tt0:=h@ T_KHUv%15K5N})g,ae aX/4 9Ioi;͔A^  ?e*Bc*,ɧQ}3obu)2A$/M4]| j9-cL@0@ QX^e*$ o83݌OCؔݯo7G989gI,'fr '_^e%La2q '0ReoT4LP]rAyxffY  P%ry35+Txo4k_c 6 @Z&PKA)xɫ6Zu, @b$\E =_Hx8hJ@@MBW_ְ%yܦ(n1X<:@*uxk7$Ό7#l'95@JXbƛaS4<^_HfL@D*z4?14 7az}z\`DMRTfi J^ ʱƙe$/aqK>H-{ nM@ T\:ǚ54ͧ7uHXYV$O#,H~S\Zhr@Tz}Tƚ. {iy׺$7} KF]3e̼6嗰yh7K, @@ T74vHDSJ_9xo֯kokyx/Pe<5]|=6}-4M$]cǛOkaͣuxk:`=~(  ! \K7NEV|qn81k>nͣ˱87P0.APl Oɚt 验5M0ÚZ֌׸F3~5 ~s`4 A@ TzChkk'}@RI@u--JٛVZ\to}ת/+xt/.*f5?H&6A x1d4  I# i#y@@@ 0b&@@@ i`$mD @@ $ 2<   1@F   406"@@b  h@@F@F@ `M@Hڈ@  HI  1d4  I# i#y@@@ 0b&@@@ i`$mD @@ $ 2<   1@F   406"@@b  h@@F@F@ `M@Hڈ@ T*DلYf2v z]J aW.n@2TܾRnD*}oC=c&eWLN?yVr L?2I> 4E@SB&PUB\p=7ӒH|o P?9hP&ˏD=h{(#J4ph?HwxNcGl"i/-]#3L%L#?Atz%Wiӝ;Lm]g$xy"TK_"Q=yhIH@tfzt#D/[F+^w1 ?|ǟ@F@F~bdi>H N2Kclc^ HXAҴg>jzօ8y?iwu8#LO :s_*N7 ]O@1:~?N;QGOپ^y"lN=V+ϼ1nA:I:Imw?Qsh8 f^sS);4L~Zu'( 컋S+  5CvYQ.:iS_$.:睐%63w~0xc8F@T ^P`  ¹Oy UγL;Y @ 10 ]Ec}ů、U8-88^{qEg@DWD_~rZOSn2g\LgC~a}  bH  rr7Ѫ\FMtsSޭFŗzk?Yo3BA>H  ~_ kTWDwotȣL_?|L/*hLHPb# 6h rK/s=}x.𣐡Ï΁@ HADWU<~_vÃs0ZA0pN@H`@Wd"gz(YMX腃c F/| j ftE!@ f0b源/<4=Of"yT,/>~;C򫁋_JAL0L@+{;)fk%(|hyA!v"v*K區s|otd(!Н_&E@㏣Zy?W^߳} &@[7 '=֓,Zf3# _*:C|eoǟBhj# 0R ! ʓ< w_e}~q40/!c,>"Oc;vҁm;h]еWІWy=Қ<* §?v_Ϯ#{Г|e0A`!K"Klp|_HyUTSl:@<>y&!KQ:>Fs_i.+ЌQ8HO|׿Y 3MÉ3l+tQ!t5M\D쁟kJ5b_>L*f@O[z4uBz?p7?^}g" *t$ J#]]tė왮3 K>Ȗ"=墿~:"VyONZ|!hTpTf#@[N\ }eGygsjYNd›i^L8U{o&S_rCv nh#z!N%H%6S4_@m <}u_¥d@:tJY*gM -ZƋKs$Evo?t[KKݻ@B' t>˩ <5"87Kx_:Th%o`=0,htxO?.;w/](~ۡ<~v]n,੆A&h3!/tY(FmRq6Z;9A˟OS۷:_s*䷅fli{7|M=xuJ N*]{ Φ_gO2/7і~T.T_L^y#[_G^4}p@{'J@ ^hvջ2i[ieLyV Y y_]F:ӟZő'0M0M<ـ=hOӻv8 e<Nx3#4v?9?ZT 􀇤 J *"|RUY)Uh^LCO|-54xHީj*~G2]G=ܯ*SJZtAO JuMcoz;p-~<`E+%^]!V4| D@L % OLСG^2::ouԳj :;V|}P*m0e/>LNRqd'+hߗC.#<h /j"S[:f~$p;L>d? ~$PF2g# 3cu5H\??rugbqf*< ;T|~xnSdRf " r0R>K l(l.K[m4vu[C igZ(hYܙ9pF\JFh򷿢E? {Fhz_sY kD(F"2# *?9HV;vmMnp ?;r{2yذz70+x'o)vl"9ކx7v櫓_g < Eዲ7f ~ UY-Y?ag UÍΦ(g+JHx&l%ݧ^O1tۅ !XH.    `!:   0p`X829  `ᠣ   XH.    `!:   0p`X829  豰r d2ciדJD-n$8TWsOcl7ۭL?}t?h6y@d`DGF(eT*5l6LeD)Jz使 f);:TE+b MׇgggtDO@s+|se;lE&u@09ѽ^ezM?4ezzy$1pcY9$ c5  Nbt?+"Vm;;x;ʿP}Nø@Ʈ!kI{;gmʰwƶIU69UaN@G'@ʇە)T&'ۭ%Cq69p#_jc!D@(p̏q[8uEXK{=69±A %K &Y( ;ѡ$Q¾uKK'N> VYh">xSBae]ATa#<̈́oK~2lsX "DA=  ?fE 9O#O4rg|R"q??r㷨4'0_6UV( @/}2<㭂&FTD{fc# Pf"BO/Pul~1?Hϩ0CYf|׽C lvT:8F}=0_jmsjMsawI`;o3¿0 l1D-QԞdH@6Bo-}+[~M=po`T$T fA xW} H|Ymʒ4с @D`Q-Oɴ'/g9O|x>.m!F?A$;  JsW%/>o q RIx%ek*p>siP6~b` 1CA@Nx;}'O=O1dcg{0` X}'_ס2 @rK+{Ö0Ug'̗Dq'mz7Ɵ[ߖFHY~Wec?,/_rlsF GL";C@(Io|Q EտXйSQP-/rݱ>r2%|,wDQ~/ӟZ;跿-=69=Ic$ Fh{8DA._ PηXi~/:ms$ Q-0`HO?Ի~C`*nρDO@}F"}M4~ǏշZZ, #A!o-&Yٯ.[,q;AC@3Ia,{k]- ̗DI&$d& 4/ a#ʒ4% &jǟDCg].鎅6"4N@㬐K@^̿/9g=i8t")`$e$ J_[—Rnx81 6_g}NA$QL ~KK?&>fp hZEz  @>¿mxCв]3xHb` 1CA@I{/,(o<+c>.AuhJ@g T*N^ Ksmc;{.&.\t "g=9.~o}MwgcG Ci ?_}?ajcf@ G A x}3_o-ozEwocSq2B4 @G[I@x[^)hߦOg^!;}%%ǕAۂ u?~&^e4x79 j %~E;.ezr69A% @$@:@wx4-<]|ad?XZ»Q Mgc :hJ@x&?S YO]oImls(P '>#Ep=OIu _κԁ\]I@W+:u:; ]ۗ>O 5@`4 Cw+/N%_D (t"6x"``"B4E E#5ӑy`쇷tǶ b A @$Tf_8^o{ݏGTd SS'xAI-%F$ "リq|%_Ƒݿ[L5+͚]p){mdfh NBF\0P2#"?9H[_fөc$Wx6@.cU+qXx.Ar,?kX;]q(.\t @ ,0"z@@@ E`h *E@X$Q  D$   aD=   "0R4X@@" ,@@RD@ @X`E@pB|  \" 0N%  )#`ղ K @@ )j]k]ۓ"wr7'O}N $i;)K y-H9  $r'ID`0J^ qsK@@ k^p x0 z?b@@:Mf,汆;-cb NI#B @ i{mﴬo@m'  5Z5/Yf,rհ$oEFGj\}'TӺk  0(oSEmތ88_}z,q|R^$:6秠l,T]aU/ezAiz @@ Ua/F(rU IM@ Dy% '*l=I8QzirⵜxqriN$LɵW*{=49ּt`08֦5] I3Zz @@65SXɢ'wŇ0 DN9I$KX~rӓKс$^8   rW_W/i6Yo];rB֓C%,?943ēn9688)8͟\{XϪ 89a=j` ^O,9 %]t: @@Z SjTҗx=O8*$E=UԿOi@@Z RJXI49‡rדFN*5$Nhc-k>  0=ZcgEUcW& EʬrK  n*V˥VC)ΧX@AD@>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`=uaO@IENDB`ic10 PNG  IHDR+ iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx mYy>J=I yVB[2C[[|ъP[!B2f`=mZP $E%URJU[{=s}=oޱ7k{78#HD HD HD HD HD HD HD H?W% D HD`dc 2tsD 8D HD0!a:[uO|D H#p5rD#HD Hl#=ɪU4XD Hv^6{{ͰD HD  ) TOeeԺ1>D H\orM:h$ID H-pS/{Nq4Hˁ^6ɛν|q9SΙ$@"$`[a]3cz齦U|?D H M6;ɵnr&6֑r"$@"\Gdʘ-ɫB~@"~7η~$@"${@6R{K;>YE},z: 4Hˉ~6̵̫g~N]"$@"ll6O{,ӹyAaK$aA`y2;LMJ>HD H6@6ZGtDO`yno:wD fu3gԹS7;3Wm>7eW%$@"${@6U{J{Dj.Nj!T9_>л.D H جŎNUyqq8,Yq)|n?i,۝_%'$@"$@ ]w BcLF4gq/rG.}._u9.QȤ6uS'N/ۗo>TMwKN_X -[.N<[pԹgN<}ĥ'x.\<ϜGxG?v(|3@)/C:*MVY:?etcoO5>bS$@"$+ pzt9pdS}mH5~XWXPP;}7Ty/k]Tm'ϟD9ܧw h_]7>q/9k]s[g* N;C:๭K_SO?pǾ>}w>%Rl>SmTe@QZ85(U1i#\1'@"$/5#{_GrG\^"oxyWhp]7oߤFTgMU!^+k:'g]y╧z|"$@"&R<yA,ypS;D}u|We79w;V^\cyc~x SlQmzbQ>cy_o*Nm4|Mֳt5W uGi^A&k[|T +zok+?uדO/˧pq{@lPBQ-%:磭:K5ӌ]gL"$@"bI#.ËF>u߾;;D`SxºsJ?Qn|1:r鞊Gcys|Qyuzn+<~ʫ֥7Bfs)|S>S~&G6U|M$@"{hڎ= pY<2~kO<;?mO ¦@a>ʮ<7ιegr@"$1Dkc\r~pY<2 OvIU"mpG|1[CO{V9΁EMt|fcEX*;IozPʫ|獚5*t^ȯ/=Gx-SWE_fW cv5ܯiev&@"$ ~EbGC} Ql[#_D @C@{p^i|uMܳœ%2F}}0 ƁO@.nuU=ޓW?k'xࡢ>W}]`O _Άϔ3e)'@"$G"sq+! N5e6J~Qc9߳sー %ƩQm5M2ѝ-@"ENdv26^6(6XKC—^OG4|⯹uq#ts)6ȳ JE^qn]c{y ZsXBPh~5yn>8(U>ߺzΗ?σwգ嘺+)}/5u6J.HD 8BwrD@{! / oOH5D?X.f+6 Elަܝ13=0^jp9y搯_q^}ߧXۥ'/>'Or讀g]L-hc]&$@"t#df7lz›$3ᇍ.Yb٢/2k~FF|/[v߲n]u捵La #^՛gL&@#~6Qu>E9_wýC?/p~p7 ^FZIO}ʆOD HD#5ӡ_H.`WS~L5?X ] >J*ZgёHdxcM+/|#uYǗ˨og?ʁ,^>!sl)#LQ&@_ya]鶮/>?Og3ށ|Jxh1/%ki}OD H5~q D^2:QxSMo"HNG?|]7Ɗ4`:jˢWac QѶ-mMs#L]tշ]5XIqG^/|_KcwΦdg}OD H%\!KFG/ߩ6 6Ve&J]#Άx3FP;0%?/n@8=*щ'D HD!F-G"0BE1p>Orw(&|qk:"A ԰rZ9ix5xM?QˁT:=Qjs} 9d0=G9Š/. If`N^yї~'{_Z^(u)BXsggwE~{E5+Iu,_Ի[X̻6Y?^Eu藇>֤zLO_bFJ`s_p{ȿ3..#B9xFS:_mkӤ~\"$@"pH;ɉ2umŀw*>|/=o )k6y&M(!tn?N鼚Jj90dLud~׶jc՘MykdaIw,H0zMrXsrHtҥSW+_woƯ{5{{|Z64fS6qA {̎ 9i"$@"ph_jm@v*Û|oh}@W6Vi&J dF~ja^79ԠQl޽yQ3J:54UzѬmcC㘲.8Ds#f~'Lk@C\n{_U\X=@JE|"ߓ G5HD 8dp-sr7@| ;Ûd6N_xNwH}F.dMRֳTuR,zCm 0^qFwq@<+ fS2)r]?1^^tйo`Y>"HD 8Qˑ]r]nfl"$MrRT ^j&i(}Ce/nY쬅_5*YE̵:4G JNc N/u^آę?7/帶z- XHUM3S Qr"$@"p/x!>N&g7\}"@mRkeo~jnd-똊_y5YvŰ&K[1ePPoF|EIŭtʭ&V(r\uG~_vۛox ?bzFr(tؒ&@"$v`O;Ȣ*^:}|">W+vb#݊~\ޞhXWUʃ EsG㋼*m7?QEպEiq-~Qpcn<~%݂5Wq^e3~?y S1['O=s 駊ˡg_ ;Ga+?Ee`&_sD H@p@Ne,CR>E9dl)P[n;C@?LtM{^{kڱ ~|=rk:h1!Cыr*OUoG1N1a+/}oy_ #ߌRɱ D0>\7k^gw>cU-@W+^9fۑ#'MD H,#Xޅ:(>i"pPQ1Xgts+ؤ7jdiC}N_n9M:1bӧSC9Qޟ W?׉Mc<,Nv N Kwۯ:3ovK9Ug(]K".$3Aܞ|"$@"pYP# e%[jcbdH x(PLk%w:V[MQ96&Ώ!@ca}}&J~($. 8pGj0?׉gM( /KqJn./Ͼn?Wn,k􎢮NCqqt'D HD! C5N.hvs@buPsB$^6,/4y=?c5I!4f@ϗtGApZ67/G#Mb{x5PQ.@*xmb=zOy2ջ<|;{y帲S_ (<-\M}kOD Hˆ@l{Ywp%%_k^˅6umn+)Mu~Sy6&nb;{]Ξnz=^<yJP(q,qJ刺|\1GM٘Kj?5c-SPEyO>y/T;zOqm`1d(| h&_菕ʱ_ 5⢼L=i"$@"iˑluh^5h5yV-zrm75&kɽXqWA)Q'rQeǯÏ{G/_:<Kq1G}VXO,g_E{/QC){z9j" b4x=T ڋn ջ5}/v[֫6CG󚿒t Y5135 ?ġ;][=o♏o|ۊrJ'7(!cHD Ҿ|'dnt*>FP9c~Мw@h\^4P[g3a'85dgJ)E~3ۓݗ᡽t}>z:l=ګS~1dt̬<קgVPȂN2ƞ y; *k?/xsGeq{ˡG i1U[J8zr"$@"gpD H}D{wRmYF-6k7M^6jҼ! r:9RT[7L&YӸZ;3߁=GשayHuW焯5g_|uM΁]:.9exzs,;wZ7k&z9WRŏ~S9W'"kvF#ܧK: PybʀFͬՃ^5 qH pWvם]П $lU:ҋ׈:K"$@"^#D A@(_MtQoT#hr[D1O^8OS9KMn[|3b͡>%k|]4c2\!T_}HaI/nF8m<}[~|χ(jml,HP*uVn8ܞ|"$@"'ߑ{2I&MD XD ^KQ9i6K<F{ [IO ~ ٩=e@x M7n!l9$s < ~F #8U. 0+Ve?)z蔃C>q(7,p(^U23WT]Ÿ.xrk[#-C_E5+iD=hOD H#߽9D H!@3ʧQH13ɳ; h$L&JrͩyvkSf孔n:5&’s}]^Vԥ9 u +JΏk~#ygS YCj59M`(S%B_ܺ;?M3<\Spv/C~qt'D HD`cᅪ%D@"$5V:jAJ><6v '4`/op{1g[Gʱj'\5jBU78b J,vQtw:|A׶@JMEm!09ؐ\G/\zN'Tq5u+^9}7}ؑ?8K_e;gU|^PmuE=Ë2GO}RND Hv@넙 HD`#ӈVyk -eAӺHIƌXdistධnC t1#%^1^ gt؜t㼲Lw8:P.x]dl ٽ?u3@1%6UPS{yh|:WJAz/}lxϝ_&%*<JIU|ID H6^T̓$@"pH &Q'9o*p7k_?~j2 (C3EU8:GY=(&ϪLux'KF!C9ʡfZ팕"z\(Wg{i}Z޹M\7{[\jp }։>~}ZTהCw8tl:I!]=]I9HD 1c20HD` bwOE~ۖAONzkh,ܒUIm]|CTTSkxxC^搏ۉ$;"Cr;7Z5m\Y6W qHp~m5v'?]_8ŭϾw|䍯[obZNޝKFO,$@"~72M'@"Gb6:lۡ*WXPVhw_?y_U|-q)H9F56s{@"$ A@"$B`K 4x1yCXêfq^jW&>xfֳ~bDCUƲUr8օtY ¶6Qp 0jb^_#;'w\5<kpWoQ؋M)s$@"$G 7vaf@]xq%]/V~L~cHZ%ļSsbd˜P7E=Ϙ*>cc^Ξx!0z`cu$z۾cmK YWxPNCw<\we/yEp(](ꅩ\d!'MD HvGw M#US/WA׫ &rKtXyrBes})zzWqU^Ψˋߘ(녙cmNElhx,޸6Ϧ 4LJ6xyStRꊺx=ojs:ݦ\ eCE4k[_ ohd|e@fnYO~C|_~Ѓ7}'@I9+G|D HD`ߥ;N@"h:v^Ϛ KzS{VX9EPO2mlେaHcE,SjoU5ǺsqNR G @ʶJG ŋx !/ni4 ?RTqP ^C_ןUxM6H5}\|"$@"2wʁ$qA9^G2\zX6Q;^]Z7~A,jѻsSBAXu=+kpۘ~PtZXNpTgw-sNwCͷ_r)vjVG}"PˋfyVtg}+̵'?G_7VC\L$ ?d{D HD`9D H+ˮ#niQQH 41j?z?׷5^ˋ ,gkc(J'/: _Q9]ʇs=ywu5<Y6b\7o:J}Q34'uk *W4/<-EMҋ]|"$@"0.tLc"ShS1m_wny|G]u?lC4t1>@v1]?h|w_l.,깑wev:g\FԫcP o$٘}#,At*?VC{k=Y>yvMtj0%a ҊUZ:/E9GЎ|knΥx=zЉJFYOk\5Uk4^+V_(lwnКb. 08S#ymN]tZ7P1O?M+owq -(:5:wu;_D H(G"$>#P^<*הm<::uк{kF_k^tl7X3eU﹘kJuQ4`#+1F?o&3J醳Q7IΏ6fs_g]JGw晧{߸-X+ˁYtsZC1ڐ=.$@"LL"$FR]z0^|eO%eJL:FQ!5l,[ض9e91|%~z\η̳­{QA\T]玼r=w&LSzD?\6f!<_o֢ҝ 7"DT*|ܞ|"$@"0C_>3E2@"$;C`fʛ o]FR.i}&s].@6hsy*E~81)[Ͽ##v<>.+s2?1$3"e nE#_y{vǨZ몃 D:bmj4el{GW⌯d6k`zހh<mʫ5^zDi|ݾSS|uן<=O+Y𕩮_t=:_؂lx&֙?\\s#HD!WA|i* /\غyb v|kDuF|/X^Ϫc=]<8z|1<ﹱB O%8h:c 2> ߩ1 =)1:1jo롕's|U[IcFY7].nRJ?L1s_\bnbو7 `R^ɭ4˞{RvryLӵ*8CrSŦw9Rg =_N9=pڨKw~{,f6ggiGWT3;(C~qt'D Hc~7H$*&cE}LLz^,~c1N˽,2ʇclM?cUjZ%nlNS1kz,q>5+ ]Njm+CkrT]n^u$7jT3_rh<5+ΐ?uzj5|{?6, h]毼L( l fg֬? h>?ZBOO?lQ=:o0-Uz ED H)pLO|.;H.~eNsMxNFYjZhz#סvϵZ"?ۿ I$@"o}4'JD#k9/Ԭ19e(_ кhxt*_>41f(#uuοg@^Q(o=<(e]e΋gk%\6Ŝ.,SʤOYڰ*;~kE|^9*G P@ȑ$@"c3$@"0@mUTj˲F{5HnsWEU?=-qMby'W^o ;.3D#{`9^q+cƂKhyt넛U^SwnɠMnő )0]ghPGP[>s* ʑ@"${~!HD"@3kdmݿzcᢉ?&(kU. Dȡ8^r_ G+G(ד}.T~4䮛{ڠc9x1/kYfP.r_~Q&(>npTcDȺpCԤ;noΜ8&.5};U^l=$$@"pw1Zr.5H!@S{:G/zcWczŬ3h<9z>ZY'zb6.mFki `k=1_QZWO9YSy <{٨.٨[{M>q (P?/ .H*@+2P^:|tϧ埾-?3g_S}Y9E_]'tqt'D Hc:%&@"YzW=9~QߓNs'S YC4QGLYmVcH5Ul2󔞣& /X"fB8I|Ur:JL< ZL%x=őX̻ۿᇟrho=+|1ϦF'p~J-i"$1A`1 $E+X^~ˮ=|Jv5>qʾ/_o0cϔ^xYۊxYhru  +&;ǹ2zlptt,l q^Rv9s\bjc="8|yqxR)/j  w*jmgoox/mh`MI%MD 818ɹD HA@]M:yVR$^TԠ&|$">/ZGsz=Gc50{eIt:?tf@a *Wq1= 'lk\{=p|l5Oy>j)Ju]ֻ q^qŷ:^mh@"$8'=$˚©um YiI)0K |b' 9A_|997oպ$kCxdxrMTsY1>xk/+-^luJ@"$8F';$NOʦDz?GuʧI5@ɍ E/6zd^2Ƭe|O_oݟƟMK~w~֌(1f_w?.@^ Fjo4~mۿ楯(! P@ȑ$@"l%HD#@:-Z,M&Lej_kv\sKFX y_ ~_Tl{]s-Kslm34y:3VsqUx.,st'yY@IDATc:U=t~+%r`@M):}:T%@"%u֔kID XofbHa>FrnvQ:1ԫk^XKVn3yQUYN4zz+v\|rMnl3k9KzQ7x.䯯-";>J&@ P㪾l<[ug~կ+*tZ̳i|:cO$@"pD/!WP[Ǽ]XQ65O1ȦCyΜ} co|rN(!4 wy ȑ$@"p_f.r"W]L qT&❺OqSͮ3!X/z'Ήo1'4΁mq>w[Ak>9 H=\.|Ѧ8|uMv yf\ccW>һ,^Z"A'!Pą *k|a[Ct1u4'Cgr5oNr7G6 >$2/z8 !3d@ɠ4>ԼeAI-Ccw$)T@k||x`[} $$Tˢn;?O7MEuC966Jl)U,/G"$C`w[f.r#W/+Fy,7r''8dQ?;W:xbC쾮 }b]Q2~hG7RhG\cwJ.lzE~l4jKy uP6=mt|cw:oKZ5 MqI"tH_s2S,G)Q(9}M<~ϥ9=Dd5ugUL̠wsk @'&@yrǞ{ϟwwX&0-S򉣧>)'@"Rr,;H'4H^:W۽xuyaQ5bYmDsCm}%ہ[7\8o(搾gc]9cqءlN rGǺyT[!xdy*v̨sL>Q6EoxXU;NǙo"$U9X*;aN2d>)'@"B"G"$>!U5mdU>kpV(Lr>TJuuxŘ˾ۮ<)WU<9+>%aeRKS ɘSx6*?&?^6|ӀM:`=SϾ{Sěqe9hE󰢮eImvoD HC$D H7~%NӂyjL4Cfs1ǹ&G|jҳvlT*Yv<:(>=_bE;kYԷZ7~Stcs)G\yܝUcClU_^ @kϟҐ MP3׼~o-!l"bN >tnO>HD!e@"p8!ޕ6>4y"5noopf -T^r֮sOO/pp*Eĝj@xFX&9!Y'Rs $sl׵~5풟*<_@Qەc[;XS-xW&@̑$@"X Q:$@"0MŸG݇ljG1ǑbFPR΋"T\F,ES7t||<w'\28|Ϣ օ} .#j[4m z|[ϙN;G{/z&E5rxg'@"r,3H&+ez#E_ZvT)kGLt S Xw;b(4gPpZ#9If>h&5k._8x^#6Z*s_'6k.fP| liM.?>޷rMR9UZdxQlqt'D H@nt5;zv4rGNAqO' _; wSMu]51MDrE2vρ)c_:σˮF?M>iA>%눍T>"O|3C)§G7vr6Q lN{sGWYv3X@:%cLW_%A& ,Wlճ jnfU2+q>kC<'x! pOT:Qe/Cz 6m{]_9L~t9hO s<8WeBu}bk1G5`׈+^|קGR\W#HD`}hI)HD`)~qαipx9*r yeL>`HS>ThRs񗌎E}3`'u Su5t82g\1hwyʇȇ&7g.,x^bSWƼ/%PM/.r3fS=|a1GO}RND 8<)YR"~:M +[t_Q]G)|d[7/vG&iCw2+f 95WY ϛKzk1=90f[g.yʩctbGQ1EUX"&N喍 .5_]2 ELp'a}ǷyM;5;:QU|ID 8V%@"p+jH˽o5rgpNa@>w&erc^k¨g9i U ԁ>VwԎ7S{^3%zkNo95}?hn3yXC;w|ϝg?sċJˡ;V ] =, D H@ns%@ 6QKhwǻ>/>oOů[7Žݛ6s֝7*8ϼlT5S6m`֫eϪ?~Yu=&]=Eϩ" ;5Q lE_6Epӽy?}7bTbnϐ_D HC@ne&B`8H>~h^QgLͱĹ6t#>\%6;O [1ǔl)Fݦ|hvG͠o |K6&@q_ҧMk|MD 8P:YL"UV:ji"VgV>q~^< g7ySuѼOg7s@=VͣSO#)~+M,s¯/sZ5sx5ēo<8ϳ,vN͗n yI]UvwB-z>|+>;^tהc6l#HD!d9@"p@+bB.[Ǯ+Jֲiдn%3S ǶbU`´maPxq9T/cS>t7uDdf ke~.Qz ,J5u/zr&ʧ$9D HpN+Gί Q87C<~8ymē;qPs_車W%u9{;@]ۣ^2:xd"=boG5IBjA'z(%s6!7ܑM 6ȧW?W~ɷ%&|"$8&'`,KU)6QՈ~iC1s>lb2SlPw=S{b >grG4E5z4_9#4CWËD?ONZW>mH1ǪvlSO='ǯGѩ56rJ'ӻZ@5jzțvjM{6ʻMu9lS5D5)9/{j$Xsw1s姿p|WyȯxF ?nu3g~>sC썯}C{}9t'?ˀW3:>u]@"$ L紉@"pXׯiz>ؽ5P؝׳-MDqt'D H=F 7L$4~M Y˴`] H~ww;GX3>ʃxJfcY:vS߲؈c˧򫩞'zgQAy0$bNRZ'_f-R)~w{o~wQT zGQo[Oʉ@"${:HD`56Tҡ֋R8I~< W#3M{i7?b=SŊD]ԀzHFfnri>jzmhLm{vB_urAU!E5˜ǂ G63ks|Q ׌/BD(3~/\u~o?7")'@"p C~D 8lZ$C{{9W49]~FaF\ TשsyΣe}sy 1\otek1.+7 s'd\Xxt ྲq+fyM ZG kE6 Uӯ{'&BA@e:'$@"ar`Àf>\s:s<ҡbs:fx')6υw_׋_%%z~Vy{:lءآcE_u{9~}k`j)3|-z&j1UBjދ^16ʋ{+175T'>Nat2)HЕ xoxuU?1ګmk#ġL.R>Q~y|@į:<^1¥k{ 0p|}4YtǾ .|=?w">z&o-E52GO}RND ! 3j.Hʯ{~ <'c'?vρp^:mđ_Qj񸘳's_/zdFe4Gz#${~=q䂒bNo,EynxcU=ZN3rNПڛClPٕ[hױX 6^'>{(<؟2 \bL{6OO~zIEq6^COrA E0_ݒw*p>MM>/_= pK/~}WQ&@ˑ$!G 7 IVҎ2ӌYS\|F]G'ӵH/.m\W0;)Bc֯5?c-"j!ٲ|HM6 Qk{wISPLpjѼBW,dr!Cgs vPj‡O}?z;z1?W#( HD0"X{֜]8 ek]fmcOes~o>[],Vh|ƝZxB>Ht[`#H{k&xIDzdTK}uw+~J.armrVP ~>Ob>͎XljϿ?-#7 9D 8a^0Yhe9_/[7Ϝ)Ka2|ܞ|"$v&D H64jL#k>|hZ=~>ijؓDa.1/Qt_b\ӹzשYs%z+| B=^s}p&Fٛ:sdt{C>u" n'jN1 ţ0Pg{#o{?o-ˡM _lemkh8zr"${"$F^x%dl3.cQt-_5Fe(;יkett/PMc%8޺^sLass8/#W1&s5z^5;1n~}~?y/-& u6BJGO}RND XXtMD`/ڝ9$k`o\X_|1fi(fE e V͒(؝UBdNͫ`N*//}=89}FH\Wmɿ0G}O͛q#fr EBvDgW_}uq{ @Y|ID 9s22H=Cf/4]|y_W:{9椓RJ (<ϑq"l^+}4֊o A`iv)ַ<^ӟ7}}<^>nS>](N=>U[_-{ )K|D H";.D 44˅7ء}VDzk ,]t>9~c޽^|n)fPN=bs{u)'*7iN@zC[gEu9E A|s#˯6Y/X.|['ۗM"C_C_Ͻÿ_"&@o@!S׉@"$;D 7v\%@"i'|lyDy,kr׉]B*21ΏGbv*οΩ=d:C36{Zk%`j147MS>@ՠiWbju.F;4>ìƕ^ly/Wzkİ WAŷ$:_L3[%3AN$@"""P$^#x=46[ W,Fy.ts;ʬsny<;_i~ll x-\&%eqF[1qW!@e!W?e^2 Q^ź]|}`asJ'Пxk7C_4.Y#Rtӹ=D H%U@"$hn u+]O>_?O˥1XT_-8QM MryFqy}y:!KY>UԦy;VU74I:rx~ p_ݿ ⨿Aʛ Dy sآ]<p/FE~|~/VıMb @T|Oʉ@"$@n&@"p\wBi |5ܴ(\nڐE6}/jZ^2BirdMs#nce5_ұ':}2Ly%ʭ#̼}'U%7↞.|zJ2RM^x-[;&,t.^g~}Ko{ӻM8S\EUS)]<2GO}RND 9D H~Qr]w^B2:NaW60C.;!ڮMG# rkoy밹:Y):8/y~5~H[&Q"e׀/Q吏&_])^6ǞsU/ݎY\n0{/.Y/|_ sUz Q4ĉ*nl#m~5oa׬-όYBӦm۬+1EE-2(X(M.Z2PNs̯xmjk9G[|(ka0 ?gzM)ٶ4Zl~n9l$3ǂ݀u}ۿi$ ߼ _~ޢfQ{ѹ[(9|^'gY_~BdY@V+@C1՜|@H 3@?t#ܭwïo]nr6rnԚaI~:"v՜Z;ϬѸ~@_bA1?,Ɗ>%l= "vEfsa9N_p}3g?QĹ dGT8gج@V +p*BX 8&oJ$IDV d4d4"{֋ȉom[;!0_~`ܑ)H Ԣ"E#fsN~@0''}w: [2 kiaTGN@;Os n)rѢE'gYY8= d4;8G~j2جw=v XWvl𵩽іư:߽1"A2Ns3eন)"V/͂Gr]\yaՈшLYt9&7K{ř6$@nKV +8 #e@V ++`thsyQ',Ed5s cŹqo35Ƨ#[EWOʤSyյ0"vEI8slLzn~@Aɳ8Kzx\&—\u׃O{r&@)B@V +p èr d'gPMԞpLg@G׺x[ S6['{jG_u|ZZWuǔ͘o͔Xřk`V! LhUz"9Ftj|yOxQY8@)B@V +p p dYZoi pKmZm6ș7 Z3pO<1Mc޽1"AWV5c9tڭ[YUxms!Ȕo.`. K.[zF|Ulwo'į,&̏2]K#Υ&@V +pb*'TBYU "6-dn| h].r+ ~"Y@Vh*~Ɏ:7k׳)N[44jK"$C]&^fI_w[3^%Gu}Z65ܫZ>8jvIucüzʧ ]㚵ˏn68@d p|篽?wST+6b -yDO% d'p"Ns.2+ Mb3si67ؾ_CDduv3.F7tۘ~7zXٍz_rw0@D\W5OM; ǠՍV:)\ϼE}wGQ trdYW8q< dzcIxs{7?֬W"7nҸ6㓸c~ m4hCu4҅3?dѳoa/\`5pq;79t?b޲6e.|/Xf py?E}m9b](9Y_u/6=/}xͿxɽE9Y~T 7cE)7G/lQ/,Ȼm@66G9cdί3Ǐۉg< c<8bE9sPɝ8.p9(Fr llӘ>l#R`A|Npx盬?cT6ڢ=L6@5N+p`vy-GorУ"UPrw?F(F؜TѮ dcSquN5' @m^TοWڏ" `|V +"*w"LEoZ+sA}E;!sһс=(~s9v.Mq`K<| y{:cX xQ m^]1n-2z|@:}vl` 9Љ£#ׁ/xN=溝_Uu`@&ѬE`(z8u9 dWL3GV*oHY SxGG1$L6|j`clu~C]/tP=*>ꑉ*=»dq-$=zͥ~Eß˱u z:!wc\6q9q4Z0Fhn"Xl\>&[Ĭ[YΌ)zs:??Ƣ~A9 @˙ L祑zdqyY@V~h YziAîdt ד#qnf*})b~zrqU:A(rhOޓQj`8"l܎9 җؤav~9)t[:y<Ma8wS|լc#6~"`b,֥nl 1 OQz [n>z]X{q9ZD 3yw,(fם W/0*躮v5k#@3O~?v񩏾Ms&@q #+ \Y@V +pL*;xwi\N+%zuEˢj~ƚ12Xw.ۑ(#?&_Cټ!z_G5m Uݼvn/brhא1d{qx5dE]vN*(kO7~M;r29.eHGO19 dDzp,O[& ia PIW;4?mUu󫭅yފh19%~O,6SSsf/742^zОL1<֠ 0$.ltf'Ų֑8=Y95 -M6t烶/΋8a? _ Xc=wO|{MӅ' qj0 U2' dUxdY@V`ܹfh%{Oʕfԛ$|>]XI?-WiU6?;?揼\NpSpG;sXmRNQxw+  9UhV +hН^xfp'KL@IDATs' uجU<+n_ཬ:˿p:b,6j= nb03X%r;A҃:ߢՀv_H{>'8kO 5E+U^t< Pe.~C٢ˡ?Gze&,br d{fY@VJ@l*KYZ?)L+f$IX9㌩+xp!ϩ O:*h*j{qx1`WQXaE~Lhl> f H|H2HE]3M֏TZ/z>һ"& 7JrdYXyV +8fস ?zlge44;@ Zվ7ŹFg,]Eřڻ+YwlsÒ CSZF8e~}j{eT1N{N 3Dq=Y]|>/l?=Kת9{:o'>+yGmM/lY@V`VK!+ lY CYC f/Ecr'zFDF7i5VU(-ʰBARcn'gvsѩ+mU,VlcW8p1N*}je=Ƒ{;{ED3AgyQN-||w {{99YWG.K>+ dv/Vfg,,U؞IIf_Cs齙lT~ѓ;fslS<dn l'(|5R_+yM{qӬ#k[yȦg96"qP#tmgfc7#nb@ P"~{w' {G9DQs9(C6qd@V +uY@V +p+ $\;׽zNǑ7^+ZJ2: 󊅟cY [+16 90WL.?:$G7NO.ǨŇcyA*n]=1*_"J>%NYT3'q~]5g]r; GL_m d[WغS eY+ hpړ^6هmz ri윏 MNɱ忙^֯x|1eMlL67xYAHʧ[^x_VryCyzQQo P1|gYkrTV +*A[cd.G23蓶 ӀA^Y{|Y[/J:v¬#J3 "X_R_טbs5#ʵѵGy?w*l[c8-T'goN[|WL?~u7^fte;x|: p̓fY@nlIJYVArFE:\XqG*Ekgg̽4h:kqU1\?a{|4bL٠S6,9y9`S0ڮp=W{#Fi#'Ɗlz %~ Y&Tq@>j_mZm4<7~U/qK rh 62JY@V`*u>2@V + L݁$9+܁@&_^\JcufyuaP]\Xq/˚*N'6c5sxmC "bje1VʊQ^E3Z{^Vז 6#+ lkr`[L d&*ѓ1$%(\2 5 fl.6CtM՝le##edc~֕㗕VQ}L˾@V@n\8 \fwlmcz5 y#q9iZe7>͐r8]&}?x5%u_8R/G*g/ظ,@V +p #?@V + dDTrtȄA>bjs) Qmjfm ,zc1sTp)#hl"Kauf>֍s'' z\˯) O>ѣT(,t)^Fyfwgos'c/מoMf% Qy5g8YҬ@V +p #- d_h`ҹz2t'ֆS 5z4_9v8ZX_Q9q18I"R{Ď%1a{1:ۓEClQ-}9²cyH+SEAb\9p,N>X(m/}-n<2;<.6$ˆ92 z>\/>ƈ{JI\MPNŷ P*f_)Qtȑҋ*+xV|_(OTиGWX#)7EEoKCPlaŐaiA 3Z @y{~ %I{@g_ȏw 创χ lŔh@V +pdڑ% 7nxɍ(ثZ/2py?N{ !lo.l=~s`4G>w\/d?#,(<a.*;G:ƨ۬oI r#z·#c>G? "Ezl E=2Q21裌6Ԝ}$ f6:V~^:yй4V;(:`q!g.x44G.'z#wl\%C'J ɝ\l-ẓ/`DLqgJcЋ8B'e/q?AMĢ!8w~_#o6 vCմsކk4{`k6i{%7Kxxϣ6D+ X^[ȩۯ7a+>,1 *c{\#E92t>w^:|K!y?Pd60kH:ſ#w,tC76xcp=uE\45"FsVs3f/|J=G£ۢs9Ћ'gãxȠQz#ߚyPG8WU*U9\VustPt2<4-QtSgݥY@o5/m2azz;X 1fJk(rq{X˸ɢ0S8t^k/d33朠g@/pW?|Гq@(^3k'[D,+ @r.+!syZn33f7)yȞc$ *(2އuxP5_Qcx|Cp`3G\s܏xN2p\rrr29|c>ne)v9> >z\'0c0)4y poR#`+od⛝VR *-\/9s:j3łL2® x!?⛏6ƝbJLh&}ys'_/6ֵjЫzֶ{Iז`׳%t^gj Vswщ P7 ~5$|+~=o_RП Zs=(Cqd@V + ,+@oOD7>rd(rE0ః E$zא,b pFXLmI.'9#/sFT?$+> l2+b4w<#FTK6/w[2p ?`D%9jO7E㺈v֩!T8h~$ZCsXG kE9:l0 W\-v}T /;LH]/^ƿE e/ū!A pc"!#d=ɠsnҵ?#?&\|r䋗O;GTy%?wE|k9$@n" dw<ޫEɊǒ7aA dc(v/t z»ݘL1ܔ< _(2᥏v@}8?>=a۰^\ M:z6.-;؀p>L˝7\/?&(vGA㕗z!a>pu6lVH\GY M`m-69(ƺq[(èYoB+/èvL~1>BMo[ԏ*cZL{^{8w |Dfk5g,s3 ,Ҿ.ʑ  3/!S?./ޘ^y،av#n=wuob[7#M}ֳb0:WawKCzb~>Ͽ9/+_tSVgc1ֶu0XXP=dOiPc:?|P{|1>f"S64OW+ˑ0IV +p<+Ͼ}f8iri' xlF|~^LXn]s2Kk8\;fm}Wa,׽.AZ(Mi¯",~*6:eSeg!d]Yx=ܲ%a6 ]&6]SOٞع_|,PZDs(ӝK d/eL'Y@V`*лefDd=㤏a[9r8S nm\Y';k:ӷh׿\Srfq81}XEre5}']7ؙ3;ݿ5g]T+`M#Υ&@V + ʗY@Vʩ@ԜCmZ7Omu7( p>+Axmr圸ͺkmg{#c"9TvG&r)rNOVHrZ2[ Iƹ{*0daHd xS76<:IU:z>/򺟽_TlT 1;4 6k= d@nti d6sh~^ kZCa㈞fUz{3M5l:}/Ly{ͱ7>{k_M(9ˮτǬ#YB^wG}3ѓ =>r+?|׽?z`m#ǧ7Du1"Q|:"PckыrN]PL07P7 /y=? pWuxٷr!GV +Xwv dY+@S3p 6?*ްsrkp Xe (P g"&j퍛jbG5r]8n ަ OǩO2j6Sw/}~ocD 5ܗC9|^>@V +P+,GV + oU8GJ;e&9<ڼ|fW";>wQ4Ci{XymrĔ !1Ή~ltޣD(#Ψ<(AjizNqmU6' eaų~3?SГqPEd "&Y @n  diHhZEmb[?q-hSl1.n7{)To*#6M(ͅ}L_gޗ%1pK&n1gy9Tph(tSlza3/g>sz}vK9ΗC&=Zs=(C6qd@VW 7NϥgY*&:Σ?lX 4}dhvJYy-W[Ԍ9쭢hKA푯KY*|̋˙ b9Ďu2[G9\ |oxim}0I}@'|`|?~ rI(Eȑ ^rjYTfc*!nѕyͷu&jymGmQ41|Qӈw䱋A׉eMA~Wo]1ӹqȏ%kls:1WF^W\6?Ҩa@&E/|;r[9r!GV +px ëuF d[Sp/2/ 9 QPvxPlmM䵊M5|Dj5s\<^|rs΃ecػcuvZ,g%qc6u{mp0ac~@ [ŻOo~?V#? dp(e Y@VU_F9+s92loU~{ضGM ͑6ϊQ,c詩!|+'V5Hy:2,n߯]&GEĹy1ɦgԍ5L_ɖ>n^}߼*;&fvZ`եd6[0`f'pBO|.;+ [u~G16b:yl7fa`[':pA*±cL蛙14Ė?r1;{JVe{(s3\ +}5lj llr ˵&9:5Itc7=w駮9{br;}1R ӓ>@VU 7NΥf'ܼ~L;FtלJ66tc6{5%:3/lqP5-C0GH{zEˡГEigX -M|r~c]׫bxqV:9f[q8Q];nuܾ5|m_|_Ə%/r@ɑ [r`JY[/:ꍮ-D7رa SPQ4U9{q˓ d s ]^.++ dQ b'#˹'S.csb~U\SjĆ|~gƱsjs]b' سOըW޹ʵ5ݺoy/ B13NιVc~{\}{+f P Bsxל1˚iҬ@VJ?5ڲY@V` t82CO X?IYϕiuQV9h|05ݾ)@L-)1o4mSwc6u{K-abH9+OPY'נpKW1*oWy{ܓO'=K{߫Pܼz@06n+)qd@V ?Y@V`/Ckx%Cu' `zv-#]23hj^uvhzɢUSG!kf=\_OCx7Xur#]Gq!Ơᦛ'S<]&']|׿ޏM-qHL6z2tIY+,B dYT`CFH#$y<xrsLY6nnj:uf+DsRi~=*^:bjƴvr\G8XzE7۴ojCuZڋx^:m } 1Ox+Ugyzk7%qs4"-9F6m^{2'  s/# dYîiLso,bzsɶa;bC(߆\7AM:;j=]ǰu<9Zrױq}dQS_ϺFGkƹ< w꧆cͷH&gO~#?rX 0wm)v(c!J{@NY[~PdY@V + )[.1^f gAs PqmռX%,ՈFӓcF6|#`fw1z0\نtc~wc#_˯x^gc<f2p+ٸׁz7GʕZ$m̅ ׼G>co#]`zY5-Ts*Y%s}Y1?ivV + Gzws,9躾kPN/7&m{3r9U)}sהE_Xn=r==\{>-6kS<ײ(<-:c>n8 sȉpi߯ O7Іϝݗ>S?|Ous9 "pS 8'sYcX~@3@V +ئ pKNu"\ݮ%^+[,Y3{6q:⸼q/}գ_~qmxLB&^#d/Y?wguV + jhZ{*Z#Ϳ:y.)OkO3lÜ|#E[$J',;ٍn8}W>w>r/L %ѢE%L  ?@V +p(kzwY?k3MLiSKɛS|ؿSIP;qy"A6܌Eh Mrvovs~Xc)n=VhjP奷5P> P?S/y?%[ˡ&@)B@V_~) dYyMG:P\XKvF\q\C΋ .[:,#֗h/2\c5 7sNW 3Y٬g{dŹ鯱ե}3-dϞglu޶qS.ͿS/K.ʑ9Y~Yצ4+ dV|17âˆטt=m876{XY)i:bb'=tl`O<|Js<C8^q9'Q-5X 6k:nˢWcXïIng}i/N+dM;;qRYr9IIV + dF*MKtaա|PJNDo{{3Ŋ^{05h.5W4bƨ|o9n7e?~YxMgўkúӵ%&6G:&pO~Ǜ?T,r\-ӢE\'s}YW1[b dǭ~wn~fME n[h/:8iJ9F`<ڟ|s|C=}yUe#Lժܦx|Lf^蔗U cfL/nе w |Iw駮9{}^9nԔ ,CM3Y$+خ elY@V`+л$at45P;Mp ̓56nn!qVY'˚q"pϺRX_9]guR&x5Jm(ʞ^(o[67Z]VB= pP#+n|!ÝjSoP y"wqp7§jـ &ƘOٰP~ƯNjk+5&~e/h!k^k6M4Qd6 6PhNnڴkUy"o?+B=Uhśvبţj[mf^]un\m:?|,xL/\ՍpYYs܀WR7FLy5Z&W2O?o^sӏ~YďB9榅Yy15iړ>@V*C a~ D^xB2QP#+w~!^2pwɿG/g /xYu˱l"[5s$_3|G藪޹M1~=6Bl6qYW»=l53dCܜƟ>˱gXrE9v ɴ[\fs|$smˡ-sxF,:T(g)a(W rclnLm\<{z÷?[).&+(d1$A\V +p(ȏJ3HV +8oza]n(d'YkC.[KO6{3rbՙ) 44~ G=JD<֡k^댱Qoq*6jGSa@粮芛s\ׯsV3G{p;עMѨ ˺缯Z9q6Ź|qWdFS/ ƀx}AlɃF./Ent|10< ܩS~[XLZ}i)A Qtd2`ڬdO>+8 ,:@zk+@5GnlIJ]As.w~WJ;E3ƣ5(up|Oz5‚q1~,M`W.c~z b̼ mЉcظ)J/)x)ˢ} Ohs;es鼙P`Gz7W31[Ӿ2IO% mP6n86c{W;|}VyQtع⿿~oz~z7p@k4m+/!?pwR:nw|\ srfeG&i:ַiOeS8eДqjt ;*&)[ש7Ŭ8u_eO}U#_k&U>yIGldKrQj`/jY_-VtĿ|i464dh5WsĆ6уÒ˦ W6 7s\b͈`'Oojq' ?tƎEq)dfTq+@5Gn%Wؗ !N10m8%Gnz~a]_pʚwf}ĕ=똯Ur|*CLu3ȝ<h 2AC  @΂ɽ' P3&@CXh^h⑁s8`5ןedMt\҆R~A<pSЗ ƺ>ϟGyQl? O5~Gˑ9WBO+a%Vkm.+Xn:5d> xGkҺ}kT4{+cx3}nRUX)ڱ;tn Fc;8/ܧF>rPlǞ px[ iEz|99;t9)y i+z΅7e[B!4զ`$cQ/ ْe#g* cżĨ>[,(K(xiķo~ P#+p+㾎k #7v_ FRHdrqYOw 9U$.X7m1?OG7U֞5c1ĻkNכt>ܿ:||@/w@l c'P*X7cuvy/O>kVh!z:@KB~Cy"ֳmCy꧙WW$g3DsM~ΟEMgˑ(Eȑ8Xq\Wy~ D^x^YBW>1?uhFo~VZljEpl}\My1&']gQO\>WF?DnMhOnQ9ͩ"+L>&-}/k|RkK8Gxŏ?u6) %GG'C8zyV + w.Wk8'< Ft?Pɝ]O' zz"m˚'myԬ ԃd Kv]K3 uH\ fqp6=YϏd:WPhtQZa9l A2p4׃d9z9MUGA81P|26nhʛd7&osΛ hAp[ ܙN鶭4:#e[oN7Mqo?7kO?`fP GO19 dvY-]:Ic]=k a5q;n|_9YVaKw`$l ÞN66utsoO45:~MڏU:$es{AYS =/U"k=?m ]w,<:eg&D|c:d XX(z|UE)yZNQF0eJxk^ӅK?q߼ k#8S0_G`S_K;sO>mo?3zab[sJ0#$_Y}@۷R[G~ D^xx/^HJr^Rh@V +P {DE]w)kQXCFSЅ-:^~<3+;gN]9sÍ;Ϝk.˗vNxs9nK/=VJl m9s&q*9y2f+57v.> ڹ;O~ X?=ϞmoZQSQ`kyGV +w>LWǬ~ D^x1;nV`+;;|N`S4xHһG8QvUcܹ1P.Ҙ_Uιڹ%/9}u;nšr㮝3rw_,}`Py\~KO<ܗc_ݹC;Ηg&S츾LNڄ)'_!*#_͐]\z)l! v=_qC[;Q}^,Eie^QMF.^(~n,AβpS·6_$@_.egn?|[TO 6;d;{e}W@V`*}p.i cz3qw~ͣWkۚf|X1KO?[wNpCy<֝S׿`^P윿;o}Me#ȟ)J_oKYMᅡ!TTԴ|T(_z晝KO>sR6)xj?\6ڗv~ǿuXz-K@ׯ`[uy\1HAD<*йƢ\ѺUQ<\~!Vy9lFSzY s}qܮ~ f/Mo^go<{wJIpN&e됝&{<}޿**}9@|f=UF^s̓fV ރmh+~ D^xps Y Dyo.lu=)?Z|=C_gRi ;׼澝sU;g~a;!w(7)]|739_]v%h.KcWУn<;ʓO?vWs{b>BS9s\o]0D9Q<Ss}L!IM2jz|J%0=m:&^M=9_7\'f: p͵_{W;ϋrmW6 g7:׹&=IO$[Y$kd˖16F0 &Ni!R MUSՐ86X$Y~ڿ}g=[}Zy[{8V#t4w!D)z>$G`L~!ӎ::y e;gK/zx7煉kы/ \O\}][>n)UeM"$xnЗI72E-B$%bxʪ>2$_~핰zp8ͧgV8%+eD^X#l-LT笕7Z>kcV>m/[ٴ~n!ٮS8"7"Cv Z5sEB>y?e0#w0swN=>+&UJBRf)xئ%'Km8eg#`G=|`g/>zG`~1q)t7ǂ~s!WkSS嗆w!_/k~$)x?=HAro(NA^Fb/xDX|a›g  (&=QR+τ]#*}+KcuLQxlWmaYQilB[$(Evg 7m;/ Bb!w fv`bgoٗDLˆ v a|OtY#AvHyvȁti_B}ZOc jccx@S!#o {{ h:t$l풟dã~,ÓuAtٷXY9}*g‘O}E '//-H|sI^Yb]Э/-k[hJhOI:Ԏ9J\c:)#\g(,쩃q Rx C ?a݊xB1G'/0>>=>$]`7D]B]IyذІuМw,{WwlQdž?g~~\GpF_DQfdV6#Mmnm1 IOi& w7]>Ca[ ]/rql7j'v7 =ް 0+/I( PJ9백%CYG A@v`~q<6KpzC~ 2Nҭʭ-yRR>Uǡɿ$/={~,î cW\#I&/M+0zn%;"u'bj&L%L8:̷ex) K̏:;E No]^|FMJjkyzLp#M,v*g@SNMa:6_8_Lf8{ ('zNjY?)|e0GA"O_se}a?~tq`DnbAw1d 0oRN4I^=nlFSN6^bOX-W^>8R6(I:1!oi[yYQvSRA r|*Vt"]A6ʋ-~N,Yy,wrmc{O=·$׸"byz #۷Ly;m#/)6QI^[O嬸6Vܖ=h>&}{MSZH⊤_1t/qht09;&'ÉoK~'HIM[!YRhQ/6+MZvuBc=|* yCaގB˘̣136K9(LJ1ȣJAI=,/fQXم}Yr|ÞG~&ɂll8[`hS\-QX#0T%33)_p%^ԧW^e=:"^sG Qy?fbx^u! IGBG&V_NXxey5lֺo'(yMb)=eU.A97Rg%Ѻ|r$'rTg? 9CF{#/X&/~i E0!6g<.Fz&gd!<0wD\w^^f?.cY]ocGD9B P-?[;"l#ղoUОYhxƱ;p\8x[j(Ѥ}ʍFB}%kDZ$! DĪ<  Ϟ ɓŨ,]tIh>FX>s:l CnuHWF;ѐmBnyYJ=iGۢaM)-eҾ`8V詣О!2F8"G!D(D_qMR>c]x8 ~!`7%SO\C7O<3"E#f=b;%Qs8YmI7hp*KdO.\ {8_6$dz xq6a8#091N>lhQ><~^ rG#dVh͘ZB[ao7z%ް)eἳ1+#϶c[͎X=mPDA)QGLշ?;m]$P,99<0u5pݡ/<酥E!z8 A!٭8|`ʁ/ɔG кpzA lTuv<( iʫr;~DRGEa;꫾ wyV_ՓѨ#$=ZeKIlEolTu,ZT NL[*^ !bE0O4޹ gW0QЅOL><>:s`Xv0^v͝k+_:67; j hZH[qmi SN/ו#- > 'ۙ"e|:.}L^#ra.ocDCtvm o{;Bտ /r9*l,`!)*ѦiPYJRؗ_ۍ4Vȧi?Z RmBX:q ǏR'/ehbqsYY Xe sw\x굓'O "Wwlѫ)cV#_)zp`^Fnc/.1́k+=a5fxmL~ޙF@M$0²*"sC)\xNbb6ꭎ؍ވ^:[Y'lD]Pǘ 3q& H)#]#@ YjH_;RF;qKH=4bQb, srw]t'3z#iΟ}fgNm~mߞn?ga#BltP >Yru+Q@,no1X0.p,V:o>q n7n"VyZxH#vG؞37ԙGbdV1HSq+? ~G[&3}X}6=#[E\~柎V#PC'&& 7/r"/\csI'5 ;^}%E\'vbUԯQ u]c~E0*<xـ .:t1CXIi0wU;/@ypG>/Rur!Ϻܙ2~aPri"jncM?P ߶53goaHڑ wc|TR>gRز7,^: m=.#n⍅-zNqa3'_˿Cw|앍w MZQ+/B'GpG`! >) ܬ JZS!1 )[EQ8N>}j:ȫto:ہ {Z. iLcw.Np< yCO\mc %G^'Ows]'E܄U>G < |8;8^GpDysZ[ԑ8VF~UV)>/7/#%}2`hĸ!r־)QȱEW_fwMYYQ={9q˄3> &Zf2ۥMpƢTmaO#9;=Ec"r!@!luǘ|,dB%}}W?q]I/FFⸯ#8#wdn_&LAOv6m_і"=v=>?/\ *56Bivt=Fnk'"ikkxghN=A-X=}qD[`/Q@~X |,@ . X 857o{ݏKulܓcX6I*jm2G < |8C/ .:#,4EAIrq?='=Bxۿ{A6"bFY)yͭ܅6>۸9]Lss86P$_K^hCFyn1:m )pFMWQ@킇 6|_:ڵ_C,]Xg<|@@/;# $2?ʙҘzPnԥԯ_+wm?'Q_u-F\F/(/"E,.Ib  <6M:LʓB^URcj]~ݴ9&iN c }mEF(ukoe;CN/#8!d>r7?al3d/vDȶ9Q߉)J>YJ]EVeon GFkR^Gh8ݘ)j!>[/ ŦVEl# bOuu@\X]^w}{E"-i8lK|`[6#8@' < I!:+:}/ųz{'mrG@yۿ ۋ#m6V-b,1%lQ8H>X['i\Z Iq>7}$+,hF <6[Km<+g `m E]<C]8@"@}i uD".GIQ꟎!3!ۡ>Gp<vfGs:RR%6yIPp-Gn`;" IUet7$Р6ل=]QGA۪b}n7@&СX[]'ǀ$ 'oJ0< cC)Hȧ2(1B5 qh^ ޳Gwzvh 4:iض۶;#8 >?NvӘ]1 ž {>ms:*9$( Mqڳu\VHXROdiiKmsuU[^l)և Q:Bl{be\bYEP[0{u^} 8@_{7dx'lp7NMX00sCѽc\mkydLSoe9G}|{㋴He?N;}  2#QB%o}C<\]M}ܣ\(`WC h% ӵkQijPV ϐ"*eA 8CN /#8Fr{;B1llKc@}#aϓ?.I8ucp(]38wmG1F3yri|򶯴]+оEx> mvX`; qA:+GlGӖwFO)_JƆ>('hݎmsC{I2Q@Ut@rKfz>#rڂ" } 3y _,vҔG݋#9pG`+UrkIvjk'9lJ^'^. ߮/ĝ#rG`h 1P$m\/pT}[lJe~qt7dL~YOPh9Nu}@!%O _@ @]e?>|Q,%5Qgq-b+  } q+*1ȱ0rw}D 庁hnVUuʝ:ϥwpG,8cbOjۣ [ڗ4VRvφ/L|{1/qRX;pnL@EB+̮d[. $Md:}u\ ö1džzZ6D gbJ 1 jZ6%'x7E B=1C8&vݑWG>h춰eYIomw܁?08#8@:{CʘYRjo$uf:{0}-aš!ڸ\S|cBg΄c-ltd_*-.$bd0`ũD^k'`#i.V'FZe0jnǬb)cC & w Ft?+˳,ҭW^zd)Ƀ@,#0P@#8V mgl>RL@W%_ nH025-b4.DCtQ}L:YfU?yյCӧCX]>5ob ["\ s=qpd+@֭x{^䟕 Tr9g>l-1k~&A||MڰwP^E->%1 PBmnƃN}ițw#ݼP̴ ASYZ;<nxGp6;o7[Vgƾ׋p ԔPegtr 5\-kϽj$$ !xnЙ!u+Mi=a?;vuю1)_v!~;R'VG^v!ukC^R~~0HӅC{_{b9*fewWۭ*;ŁwpG,8g%ڑb-~4~0y_~JgW8OtY9VX8z4Nދ='=yZ'<(Uus2ڃihƈMcSu3Y'Fb ~v؎Ilp7cK~꬟}3ɷ@?PwRn18t7xp@Ip@RSN:#8DNlŅ]73Ҧ^0u%aϣ_-hkuP VVO,XKPs6iT4yU(̲n)xk%՟hžY }߂s8<=dR>HcXX@}F I 9|u1@*v-1^GgF{/\'n=4 ( lIV#00yGGpB5R 삷vm]}*o SFc2Pcr'ԏY3X{nQv)H?[6[].Ysi+14[Bθ]J@^OIwnMmxnT1ۅƆ<3h{W:f>&I {WO@.<\L_t'~'EO6> (:(d9:u^GpG E錎-A-f@'ɿkHY5p  %wV|jmz>@acXp@)T٧a2qΘdEhن01+iU1}?P(io3xWB @ x#?Ȼl`Z?/" kc#8Naggq]x?:=foW}advJ0kw{"#[`7jYnl.u6/؄vGp ;Yjc]mEnUgn-Lr. U!aCG`$qrh8Ah,.acugw01dL몲Oz=\%O]J-6} e[$Q99BϘ}cHqHpKD^d[!\iQib( Xvk/E_nS*&Zh?DGpG`G!`giv%ɳ1w=eÉdkLgO3_r8կJ@_ fH!#_e}<&vw+ũB!ՎqQa<0C(y-(#NZbLݴ.ŘrP/6nmAr@um3q?|/xliOZPWaf|NwŒM R.0HRDKƓK mL!K æ4TU ;Ũ[cea㧬\ \7>a폕[ӌ## 9E"H~/rh_  ~ѷGo-UOi܋#9:uGp@7vlϮ0y0y 61)/=Hvۯ{G`@_,ʱ#}6ykebV[7ezQ;^N\O3To۩m zU*yU6.ybv[}`p< [v4-9VA, gE/J'lw\ʋ]%UhOujD ޶#8#p0ʮ@{^O~0y@/.7^!GɿB~bgkr{rcښ=zL;Ui>QFk,&V1JKGsA島b&i='+b}X~&tA_x&H"%c=l9 #<]J qDvLX21Ih/'yŰzccrqSk_Ibs*#)c&.656q F,}ۂjWͽr݊b1gB$hg5&TZ*?FqopG`!iq3c0s;Z:;lcPdb"8|3jcq c%͊R51%VLS26M c@N6˘]}>`_ךs*L;`w0j$(E|/'(y1EѨS--͋EU6]زXyN_:k|\opG`X33O\0u=kQ;tK/ Փ'©O~x~1̿zX-[asQ:9Z&/gGYNg8)cTn #^m&ƢTD|} /65/X ~;OXD("ÂReO8pϘ4e8q%aa(XOG`[#,m2gN~//Sv5-opLD o4y)LCU8躟Ի3m{V5ԁG2ԩMa?Hq3VV&ZLi ~Zxb۶E0ZeH(/)%R،8~/s'.QM( Y'GpG`; \]o):$3ETgȾv =51.d/p鯅S~Gj#w0!U9>6v&†|jz} %Z9?pfjیԴOӪq8ыukC]>?# (t]ERqCA\Ue|٦exth}7,|-LS4}BQZbGC, _G>sO c;2 떇qZ/8l6{|GpBNτo+㡱^UAh&=B9?IS u-#aWKG3/_)wH 9WŶmyfbMX!c+d䋡$˄>H Qa@yZՃOcd1Hm8ҟNJ@y>x_>"cRʶE\,ܓRf@|iW}%(6ҮO'X"tr9u)pNGp@6U/FЙ)gnPUͨehJ% b,8a]gchk?/u&[1d%_$eD^d&CPzk%'Zھ[=yrXPg\aˢ85C$9,*λms?r1{'sxM\:4Ϧ(OG`M;# x_]Ņ0q a p }{&;(zvC!N6#AVLL/Ha/gOչ9Y2oc0LwޡNeBֳ>0+I$ҘX+Ij~W[9G\"|[}X3h+ 3ïs=_.h(͆b ݔr^s/D8#`V#C@52ʹb3ل̉1-NK]jEd5kr.l=crq aızHX9\>,ao/57$׫E)Щ0,iK sKY:~ud=§}IlX[˧PֈN `c}a΅Rb. 6f JQx7#luKqOxXKsGDw2[ 孭 [78# \O]r(&cNgd?̕yY/ :IFF2نxuʽz\;~%MC#1?VOӧP?sZ$?3OY$.f1Cn֧/wq.F\bK:viS:6,YQ?=M޺6[12RCMaLR ho&%&HAcIAؿo gis3r ?} #>'-UE:e ac3j5G@P#8Cq2 koImt!f M^sb*jSrYU:[51%mz1z]~n@_ڹ5&[=y\2f\DPrWʪ<ƒ<"88^y),O;(ԀdC5Y(wMb#m[gn(B^ۭ.l>0-oqQ2[ڠ5=Z^ Ej"wG}~/Dk+h+ptBvȷ<]j#uO Lc?P1x=lJ#іT-hOP_-m{X[ksrnyAB0X(Mz/# 78#0 4jxhP+ިN`a}fiB/hܺKa[OVPB`&p 6fR PGq SeW EK>Q*KVD9qSbT*eZ5TG܅S:v,ga6f'&h)r6 )>AmJ'm۪. QzS^XSAchl)~N\  9͎{b1`ԉ#~OT]RU32֣vU5G@P#8C@1 7F'ǃI2t-;/bܧ^Ec-YM.Yc).ky;϶5:HS.~ʻA -_Ja#uu1N#6H9l&΄FN_j n1}>1fGt2|3"aҤ}؈x70: >#1@ Ö,|DG w $Åu(yG:Z6`8#0dma,XYoO}O[?#Kh_:ux<7ӭt K#yF^DkTnm6!ܘYGv:ڬ"6-Ox{0_Ac:?U./S{X!FܠĖ>&dΘV0-Hb"$Ĉfn&z|%}K}Џ<~/? ;).+( 뱶N2"y=#8#T=pif(ŚdnH_?y",+og8Dhgg@yX#ԹqkDy*uBxT)4iܨcU[Wc$ڄ&gbb;[O_wnٰ:ObR |i#Ye𸎀#8P H;Bx|r2cn62=?g5?Cc|*_k2#uE7-# K&UА_!QOLsmNknC_: ۞J2إ:sa MV„ )&4#dgybC@*;N*8 11Gls}XM=r/_fw+`pG`(fx聋=W>Ngq_0h,mK/>Ưo}D>T28yUQ'Ta mue@D6Xx$5}>_Nk_;6t,Z~\r& ,KPY4^H'qQlП4J[??$ڨ ƃoZ\ Ӷ :}yA&&ą>%ۢlB=;ڄFDEas)Jp6 `#8C_=q<]xQcrhF4VWON^ G~76)vxqb:,t=^L6kj;܀`w(Z+t}J"Z e82i;Ol#^! :MŒI0¬M[l0BR㮨F|zԓjһX_k}%31h&Km]Z?Ʀ;+wGK5o"XەְgټSG/@_` #8ÌN8~eal~}F&>?"{"~_엿Ƣ\( dDY0u;m{1)+@mRXmIV>E6HXirx:r[M۶&'Gii~FI|XT5=ZєwvGjlsat|T {,]]z0 ?'"1y%\Y.(yLu/@6c4u)[o2=>,o&;ke+cxn! iEb\ ?Uw`2 ϶@!- .n!chCx=& Vz7@:7IQዎkliv:+s4|`Ӡ#8C\/ /G6:1~lKV8q^k_m35s?Β!ncX['Q_oc^E? $3-#lF=d醶y>XVbo,G@rd<&ۦON/ZY؁8ܬ]>,MYSzW?t`  ظ@s[>W ԋ#iAGp r^Zi-`ak喇 \#1*vA6*gg-8_uvضNaRyYTS%Ou%+u|KgpNB_Y=h;6V/<ڵm|:R +)tyl if y<`RL&궽^Ic:ޅ` (#UXAIۥ>G1n[8|$3/rwXWt[[ # 8# /y؁"Mg BǑ]/-gl郻)MZCGSӭ2eZ|(#\>[vV*Gzlvh1CB2oFj>}eΔGAdE tG*#<6b[|\<~TԨ0c?hUd[bMƍnI`&jᩳ2ڥ@%q_i GpG`!`]H=k.8P%-L/7/F8/'df]L1 X.xBI> eJ7k{m*f-vM [9n?웥M%-`m5ϾQ-u[]^3aFVx-,* ܄Q4ԦA);N70d\XC%@tA  }arxG;guGue{ rL7_}L^xwa}yG/@_` #8É@s62;;/Fpo2?KaF:_ 093cHn6;{1gLRTYu-m}uSKsMP?):mac,,(Cwz vE(xE]J?e܄ -%4cjf,ќԸ, lc {LJ}x`7wӺR%ީ#55Tn8#KadiCek/bXzk0m8n辒v8esQi{H* 8'=:h jRl5c aͭS?`BkkoMe8FyGŹF9C[WA2ԭLcuuwia_/A]5 9_b_:=BQGMOȼ8}Gf:#8kK6R> FM aKͶ3aL9[kf'tW؄XXfMIܷV ,'֐馞k!:˜Li(5W'СC;,eh+d#bxŀVhkWom7^#cpUZMU~U6.w:"3#8Dx]M^7yA`\r(T3ݑV_=,Fej_J$|͂6;&&iҳ9Hi`MXb2s=i}}uq7ɬqsS-eNĘ=3dz fq!6]]DiCؗԾS퀒O} QplX XOYN"pK:8# R议*{YjX=v$OZ gϝf=Ls{&E6#a+lDޢ ow"Y?Rm5X@ɣG:Iaw\"X_1I5|m-mj/0hp#.;yQage]PYxڳ0a=#8P"\;@ٻO&vq.^wG. ǏܙG.ijq.Atc C8]f Z@Hc.҃^Vmԁ)q%O8xo aۤQ% ~CG$.66x,pǁm;**8sǏEd@\h,MyG+|+pG`!Yx`ʃ Td;kȋW'O4ǜ曚M҄dSCXMK-=p֊Vger~(VoZN ҭoU;2s|'Lmӟ Խa-"o6)EFnJ1ا`xg}s> _`ٮ$h_Xn8#ךݭvEX=,]cU&$h{ unqKLNU%? {{kF4}S/ ,W%؃ھ5ٯYK:򌑣3tNMg}#gAU}c P XÜur/@CGp &v:\\h^as8[1ձ ;2cJ|VUS}Y_^~!SI)9kvm" 0qVk&4SRɶxO^?<b?!r.,-U*uD*q#8FbFFȮ6;9>5e X\8g/,c:nHPѤصZ<;"arwJ~h 3ZCkuRJh/ L1e=F? k{{=DI2e`g 1v<ɆQթ?v|'kf1i.yۜ5X;_X7t8#0S8Kv6-dE~&|[a|sՖp4t[ҡm>7Y+&=:g:Gl_+-v&[UBub~p-HŀFXZm:ur/,OĿTxPj| M[] 9/lpG`xx{x#. wb'iWcLrp!Ũݞr7lk܃E'w̥uD#k9ۖZn\b!VR>#_X GpzHwW~dÄS7w:kxU^[y=ޭ|nD\zsPXk~vsNXRjy' =q!Kë*m'6q;'2)mj!(i=J_07wGit&]fz; LO IΚsٿAk{uW,Urk"XҚժy$o4VUW^M, lwnll87BfmS=Fe*

Gs@F9ۃs(]/%V P&mgHڱ_2wpGӬD0zsvpxΜG|h=[9;e=S \61gj v1};1ab0*z.9bMm=baND"GniS}lu+)|:}BqG6"G'%Μ]P{~LX=rX^RD%э4{I(%ڦPIy% 9Y ýTZV֓rRxY5J O}-cYT}Oy;_4&|l,#;Qk#'=tLlwʪ(pa߳_Sٶ>;o ;`n0Mҫ~N5H\8#$p]&;Z;Vi.mU%+hH}jzأ w`M@!5mI u(q3mEeBۍ2^ٷ1l`1m·tkp9I1b@ KpXWZ[So:߁ڟ.b[>jv;uzBzˍGpv$[ls=gٶL(ͺ۩4sFNqr{QsD/O ةM:m=us?AmIV<8@)[kU#c%,֭ؒn|wF_ wI MHa 9u6/lB8#PdV[勭=e&;tV l2z;/CdySYђ1Sy/t ڑ&h:S_Eaςxl,,()>CjU|7vމcw@Ҩ.W9[9 pGp#?NnP<1UM(.0$_#}q!*#B֏bY#E?4-@նn=:'۟(&\0HAL(׺Tl?`gR6_cX5EsaVGxq6/lC8#8CgݜqQ]fU?25l9}w-ӓ^& {q{D[U1EG O(b iuǪvr~%}`"$@c 1Zj;cD\@IDAToGS} *MFRge;5E;|` $.pGp^3QclIn\%6;OL$ZSl`}؇܏Ѝ!J)Cq0ZO?‡ c2S}bNz,{'d 2ځB^dy6Qc 5tegv@;t\8#8=!`'=91g۠-&iv>׹W#9)ѫ^c۵Y}ZIJ".6_?EƧ|+(TU&I9B6OB؛7 96yJŤ8]Fv;#8F9pn&M@tx:Dbsu_r{$'kjySJYk3mTO;Kqrlc_}CrՉ }Roi;_66OU!.#nU}=ss~=_/4!M]!֩#Ln8#8;;h2&hP:X}Jel Ѧc[1Ź=]9l<#EpGpENmczYַЎMt HD4 "dZvhKk[֞qA/2Ɓ,aui_s}aOJNS;{yMɆ< 9HLKZgMdK^~΂ fۀ`#H$PhKX)Ep!D+hK&EA "DI fpv۷WTS[WuWW=w'O=o֩Stю``O@]y5x#^U;qYIf<%5"-i^kZiӌ1y1nMZa:G덢Ҷ4rLcʱůćFuG7GY<C.S@l ă a|Ud¥wq#IY9|D-C?2r |'@Ca^:Wz#iO_Ѓ5{*=<=a&?46lA@D╩.' xN.]$h5Oc./?=@`VHbb5ږnOq]y?McJ˵kb撚H7/G3heNt،BJuΕh;9Lmc{q{mTn;-s)喕j:ۜvO#J.{N|{FD3mbi(VlA@8E~z:h\ҍ7Q,7NVhЪ^eyk>Gw\}jc,e\m%^#'jA7vn0+|em\/cu{.mhCp @&!PM>IIr6yASe(A r]z3jѢf`˚͸2Ŋs{8~e.5t[:5vFˢd:Jtݧ˲8ov;ǜ9J۰Xrl, !@'%/Uוe.ՇuGdph淈s:9itbYj|h(ˮ/w{ұll⛆˶^#[mlaV*-qqTuNx_9|.|\l&]vݿ5YS!6QP @`3lKykkz9Q,wr<N"RYw\=he]1uٕwv9q|G\S«˶ꑅٮ,}$8F*^}᏿{s ʐ D V麮RkWۿl[~J͵6xyx0ӘD>qi||u~%-쳌I{Njkmx3>=#EbXG -/1LR>/|%@}ߛMKbݝlvh1H H: @c蠋mʫԲN%<֥;/G9yӼɤIoͶmqFh%Dݸqkq1Ƭy.5kk2ފVJQb;Mmq7?{FA}L}Ha[~i AlcJ~<zw,6KU:PMW2h@ 4*ej뢹˧n|<<~M9F'&6 Rb}xhaOQsɶm6mg]hx-:M9q(166.zJ!sAs4TPj b8J ]ګ/ŋs_l25@d @8|j׶eiR~qfQa؋ޮ}$ 'gQճzĺ8Xjn{Nmn[A~k݌C06ǔ4ط`uo~_ mmZ d`s (WŷKnb8K$3M\?Lڀ*A,kqck:'1%GdY.;Hh$1G\ F\s.ƌV}q[/VMX/e5ؕ..և-%"mT!@8-ۺv]cq]xA.ݢ:߳WgWUގcQFxinSey7sYmzy#mUh~XlVݭ7/ujÊMm/96` Y(duNf=y9D>oٞ9Ӹ1Y0[XM^m֒tQAe<ڜF]Ќ"FN=6,w;.KVj\[M?-م[ޒc+ 0lZ]gI  ϴ!@" {Ǐs5{*߬WSNNɟ2n襧WPgq;lԗqz=6\v]3v@ק?j=5՘/;l^=FOqGr!wLSW6q@ΐ@SW`>\wi١xZ:fT :K,k{+'_=GƍQZۻ~=^5uԖ.\_ ]xx;O?ogutYP.86| 'лƉW^~F8kz>ؾ[*e^j6iJǢ>buVzMt1ۮ<j|m߿y,V^],v]eWٟ'@]I$eW1[ikiRgB5Ԝ0t%]+kiO7?洚FvRB~6NY;U3mƞ7ܛ+b-ռAr~`ѹ;wnth{77Wr덴ܗ7rֳϥ_f&ם!}]p2^ǺbJ_2b=7a#.Od=UTo^:wDE*>$GQ?OuAek\cL:n*χß5W_N奻[ɿmIxKN+%?> Ւ>ω~t7_=mlimJk>},?`ޜد˟մ?'Uj+j2p?U最lRGd ";+.΃7ݵ y.z{C~@ ΀|;|jx+ _K[uyh 9 &S>QXpq4qlֹm0Ʋpތ+2Trn[_Wa/Z9q[<\;tx,O_s\L!؅umr)!`C@)]ኺ6ִG}7?oK+Wɉ֣i5ϟ ʯU}v~*ĺVK\mhpעy>zY0֡\4kzKR6|w_n{ɴw習lo[_jKH_{>|J _B1'm;?$oI8hl8o`xH嗢ƺ@:8@<ʺK]R/]5d='C.9 %!&C(.tJLk׻+)ץi}95L]N#i/6wҝ/)\j<; o 4d#'z̐1$3x]|ս e*Q:K)(Ү58,Ggs%PVqNv[r`&MI.AÊ!p@5Gt!'tgƛ$D$(ݽ_Y$UC'%p|^7w(tGNWV[Lݗoܓ.|tK[_Q!cףnjńBmEQ: ㇘<ûRy~ԼM[/Ѧce86W'zKǷv#oX @}<8X/<0` )C |u_ѥJLWHޟ͠d}P?]4WCS 8:{qSQ'*ug;. .P{[g~/EgA'_vဵL2륺sb 8qGElGսzu{H^9mKyڦXyئ'@,m;Jy}59r~C#``dTB@~yN`t'ҕO|я7Ҫ觯ğE;\_{8G2]x]z# @ՙ)fe@0VKd/Fve{ K6Qt=2GRi,W&,n^rn{.59DJ>9In{nj>Y'x^oKvߛW;/ã,w`.}N[myNj3%v5e1m:b[>;- mQR,'&Ƞ4aeU꭛ (r$m{rҕ,3=@]h/+p?|ι}gWyfͳ1V;ߓG,F3ʤC(c6|^B)'ygmTZ]({klLVӬj}dz(}ԮSGҿo?'Ϗ[W~]H>&`8$ +Nr=X~OLc _h@;} g|i]IϤ|L/!:2cm@\X?yaqcݾ} r\O_M}~@e+m9mU&_?'t2GJ* 9e!We' tt{_TW>|x.1qW2 fO[9}`Lr%_2Tc'ӏ>(WgL3e(u;%kۧQ&Wz-\w]2t@4# zMKҕ}<]wz4.4CS 2:xG~_|I߻$b0΀Y$gYtWf@I^%;1ʊRzE;K\q;wơWk(Mk^W c`U86Ng@ J@IJLWm+inZY(͕Brb+?ߦyVL(F"?m4ɢz?7.qώU\Ayu~Gy%:GxźmG-cLDYK_3i9(t)~Fʽh1Ͱ藐ߗK8u @{ifKDZɿaѓO犵uh( t~L<G}?fs>7pҡ2ֻu why!0~I=|VԹ=z_*ӽrEz=ʘ1tm1,w >zml !CËa䲱.S!6QP @NNL.䁴v e68O 09\ɷ뽕~_p{Ze,ә6bpN &xFgV+[#ƱoW=c]#WM7rO!tc{<v WG68F'1@XR+ϕWҥd|[;٦m\ѴwuBp(rEַ7!-ta ~.Vb{:q_M/_dzV^%w_5T]cͽ9򳘣EcxR]'OA{ikW.&?DuUlߒ 0 m;HgvUbS1b&Ѧ=(Kj{GUu97|Ǟ۵Yei{۹-;좍];^w}\&ֹ ! v@I`}.면oˉYC`:niu}#]G>{_ǙBp'5~DŽf5uj_ԏVZٶ>bާ-Tƺ݃KU]>2ݏKٕ2(mK㖾/KoX}]Cz>HW҆!@@;J@|s,7?scu%g.fk<֕ &z:ee4^Ujuh< 2zLv1$/fb:8lSڕ~jw޹ @QOi6yiG{p-6S 0*Mo}25?W&"uA|轷Ǿk)mwf3Ɍƌĺ(tSuOE*z%]j3BQ&ؖ~QG}zfWseX1D(խ+NSu(ؖc{i,'!@` @D|5$hm/%`7emI t9m'WYMeMh;d#u',2jeͷfWy|*c]n;۵8lewGvwiUٶC 4@Xk>z$u{O@d@ɖۤt=Ih1u &c&~P9ߠ.5rC`ǕJv,UwYpnz۩{6hV?oUgG3o~F)G%O4چ| rb-ങ |Çj]p@%uq5[,"c-ݰcaU_&>ڷٌʫ^y~=cSwejVYM7Mzo([mH F @5+m])_V-rZ Zyk+Wb?/ɒa+Ti[<]NcY~[}~mPU=Z~|O4~(jchFYxLgAdYYx>tM/(mcEeO#(W/D]\gUd[l_2ۺ#4ʣrk-UPlja ohn&@8$YHUs Nlk5'˥zmtf@Lm5tۘZrմŕ]FbF~u펶doߪsp[zq@`&› VB8v~ 8#1 xP1cQwwGO\ L(۱ ZҫWZEW\6Q^k;Nq]uPdG~Q >b}!` ,2S 0翚^^e5@`tnwׂ Zª3 G2\BDSNIotYz5YO5\=KIdz8֫vYuBH H: o΂~ڿ߃[[gԧ-$SzR|oHReѭw{>K=3};zHh%g3a;j'f6Ԏ1U^>CvQu'嶸 z|)Q{7ng?m`Ç268 @o#} j` 0C{|3aO34$?Ɩ*jK[6{}14It^6m7fϦIzQclgrggg3k-e}҇6 ^)G\qq-̀@@ʷ|+'ADA*u&rL؎omdl{+>\ۢ1Ц[vMgDyf CivVN_6(h3J$f p/à p]]1JvV9}DXfF%gl?`sdA#-%ʞovR7ƥ֚+B8bҏutTv) Uee3Fer;ݏ :blŘ19F`<9-siOۨKdvͯyII+{;r$]hl,29@{?oIP<p2xA`t{ɩ˶He:fw8h~ba.-WcXbܨ'B:ݪ)kAIP]((W|m^{7v!066F 'p7'!z$ݖueR^,>fY7uܗn~wΓ,f>1KqЫY^r:\v[q[,I)}mcmdNmo䥯cJDZKZLgI˶-e#cz:TL |BX>ki ϧ_[dKY>4S'T<bWuڼ1փBWͥ6IjȩԫpݖT[}6*k+~MlGnu&Kc;?iT30>l̆+Q!_x=mL_UW:3d/{{9鿕BS=Nkc4]dzlG|G鄼Ouemc].;m2M9e1e*mw\ qgJbp̮ͩ Bc``,\CXێoz}Թp!uvS) o毦;'$puHʠL@Sί6Eq~mVoYDӚQɶWNujnGF{F}ۥm+vvyFU<0waY,H xPBXBOwl,'+WbK)C`#i/oO7TZ\6╽1uLcubg~ejM&yQc6F^t;1LJ뻒Rf?ۨm_:6깕 Yk?ݟJm9,0E8   ?t3:r;4vy/@`9?ȷܽv_Vn/@(P2ti/wY[ʶc,b)%+|v=Z&}]Ĵ,J}$\Jlc]:Wa'>CugQAw۲w=ZP0+ą p gn~)^|$UHWi:K7_Li5?PM'!R8e}~t۱n̺REmIuDzO6~''إ#m%vJxWgy_Kc2Su:@^n~?t X ݷZ&`uD.Kou3mzYzo۔pR]+k2:h[s;&ےYn_]Xѳ"n(lv|OM iXV @}ʿ_g9څiwY,˛\,3hn[O_M?JnJM[=Y)]dcqZ;δ̻1ζ]Ҟ%ʃbb}b(wiJK^#yexc[2Q 0mă HJg}='Zz7uZv5|۞L9Qmp@Ala⥴sڿnZ˿4D3| $ڸR)uAOm>o=鸣Y.e#YF9bJc6,wlʺmg 6ƌ[6#(4&ZlA @]J}}ʯrZȬ>`Ma+J@O _+͙fl?'wk_i55{~עMYW\ǶڲmS[,ed`fXD;KFmҎv;DGPNk5YzQe6ҹm1~-I i$ E&;Ͽ^'8Ƌ|<]7|#fT`n#W晞u'^JzΫ[]:mL(_ ֻ6ն]YJCkHky+=ד{2~lgɶKM^>@k'햻tBeoSu.]~ٯmM @`JH@Xx9YRo^_i[i'S'|y7?5P]jg1'x%o~/WV66+K=^=P ddټ礲&n;llǺT6\bXŲ(\e+h[l8'ղ,ev+Fa[@IDAT]GGu)!0MlL& ,:<`Ts{Iky|⯥KdZ6cW[΅A@t{F}akg'r/OR^79DY'?m'Yԫu밼ZlĪ䥶ٸnc{D:&]eWdl>$'?F9  +tSsg ]O~o{kk݋Yb@Q)最s?'Ms~c3~Vڳg4,m}ۚ'<(:t;|Fz[fk]}M8KRz鰬.Sq9NI|jk MZ `l% F@W*@trVݜjo |{Z\vOZ{-S|ٻMzHw/t<^h #s=vKs9r|^y%~녴~>ϟN[_|~_vsʸGh2\m{l[+ef&YD9f=kcǵ3 ٩W>6fEy櫗g>淚7ݟ?sjZ>Yc1Wv)sX>ϟq3m}Oʹn}n&'d1$sۥݬ~˦ۯV:v-,poigb]QbX}[%-3 pz'wݴCywZtϤMk;Un IԱ隣[6wެZ9-q+V*&LF?ys2oϋ.ayk9k_}9[icLSb}tCoonuy(*ֻtX%Xf;){ɡvYϬU&֕hC l F#$GkJ77_M tڛߖ6y$\~oZrKsbSoOh.M ꔒ&յ5ݞ8 oSp rk35:wϬh;f WS~{/'wyvrw_j?N{J}KSfjjb*)$Z2}mu[>}%%o7Wc[NJ Ujﲶ%yvgS+7O>6|*?#౴viGz})Sss%\|"{Z{S;i{^цOAcPeaڼK=Rkw,ݗvt[}jhٕh68>ڕ1"73ڑQfXD``֜C8=eRy7ʳ_˟_ʿ~!ʥMuv}*_\V{Ssn>Lf5w?ؽ3AcQGHzo 9aʟOc<,,cV~p믤w"od}f o?ؾn391~-\}2>eM|ǂ|[4~Sڸ4ky.ޝg Amn*a{ە>v6ARڶḳ~nlC ,6k- !r a'~;q0n}tn\{i?pzB$-Vqo~fE4Û>SLB K5('|4Xo-~Zۥt:6E{q~UG[Xf~j&}P lp̒eĆ3%Gw k+|5©fcJe]}A _lߵc @`(%pNlxֻ,pR^G+ƸAde!.mo/{l,!@XJ%9\[a2mcyQmtYJV/}JwZ o1q3b@ 11I,]eWNOxvQWe=oA<#emeo1#o'0G@- @8wN9 )HnHo^V1^_ۭ٦>J 2lcm䭶lK_D]/e! Wc @Aɚ;8I,m2F. k=qmGȻ^Cee嶏0mcL(!%: @8ԠcXʭ\uC:>Ʋmծlx=8u.2me6   ':@ۻCO+Ylw-˸j5aJqMUȹqwVbS[t\}~~|׀@ 3'e(uԒAcY}qT/J}_iwb|"kٺe;ښm\&\/cJn|]]ծ > j @`x Y\~]ek$2zi{moS6y-NͶ6I^ŕ>mzljiJ@863A@@-4qJxe6H_:1ZY&~˶GQb̶m]~k_z"ϗ>6N9=B !0iadzWLmǍ2ڶ$懼Okҗs/yCum~ʾַMXy5Yh+| @5b" Jhf' cǒcQ6e*K[u۱$m)cqa{}Oc88k @MH2N.X&Q/_RZL\8نrt^a+[>_zq]F83N2nL!0.6%= @XBJ\D&&?喩,㏪v&?>!iT>عOږ>Uv,Jeм}57'"PkI+;zwP;  [y.ۃ|A`Eb@ LD qT˶>GźmqXfhc&w2ڨmrܲt >lb^r -hC`&MbA MLIfyLjIqMou8vj[.ڕ/ec8QVƠ i``4@ 0'@1s(UweS,ei?l ǥu:Ko2wXFg2֩u66Mx @'&0J"m<,zۗrU6ʨ'r>2F(wuucSh:fE Y%. @PN bTk:>nKW٧ewYYN9^ke^ZcWi3,1VLʺ%6fI @`If)'&C5_'YQ'_K.K}McFYMn8e8j; I|"?b8K۔6Xqk1,?(!P`$B @`ldFAд%5m><ɶlKcRqMlG? >nhCx=b5mV)Niر&S @#PB3iRS,5 T/-cFjRKW;lvgth+| @(؎u#%eoAsLŗ+w0}r@5lԨ  @kĸ-\>޶j[6W-emw`Z-HXf @` %Q3ChgyLbݶnazQ^3bF XUf @hK$L:KH֫l~^8zCI pRC ̜@Lcgmr8 w[|t>u۹uEhce+un}Y>2o}=6fϘ @Έ9&y#垖ݖmbLQfyf,%Q @)Z*Yto>˺_1VQ& 0ԙ-\`.t nw@ (=6Xm%s.ymm}8n-fR}q{h2̱6odO;N9=B 8dlG[f&r'eb;ڔ1qi9k,bߠJ׭``RrA 'SV&[+UTw1N2(0]+壎 ;'/ @RP򦗓9m0l/m6o˥䱮L#V-F(ůfX㎡˲X?i{J̒Kl@ S%0i2U%nEh#^1u1e,TKM E۹,׫.|X!0)6&% @BI7MXubmlRWڸ]aK/$v[/κ2vi˱Ϛ,m|~n[O Y``t @ 9k!;8mCzNmdz.ƒ]mdQ8jQTq-o(곔q!IpzB , ZVʶ%.<j.mNxD-ee _6퓖W=n\;V]\TJn㔥i`4( @Bp"WNMn;ku.mbĺ1dzEW]:+c9mҶ.ݗ},wi؎FcoPwm|sL/edl*0@ sE9Dܮٖ6n|Rwɑ~Q}3l߱te[6Q[G,KѿKk-βQ2}4sq:x1l}SBpZ @` KJ}l۠/DOuLu$6ݟ}ʶq\ʾuZYs,h+ض}rݖ*]1e v' @=ϫl[>JRN$?emJ쫲<ܗþn[_+cr Qz-eW^zn~Ѷò}!p,' @Iu:bGʶ8NRml:Jǒ>m8+~jZz.b$S]eԷekX#G%Űg8p  @8q;YI2ZD5u.GcvQl{C_ExjmnjZ]28c]z.(㹌qiǑ؏:wj0@ '2. &.]Wel[&K^;b Fc]m{۹ض-GʸJǩޥlJA~J6Ίc좭ei{cf':A~#$;um#Jzǒ; 9NI @ӜW9J5 o|.;~~GTdo})SS寣ݕsۥt{P9v~Plt0-ā @@&u11~Ɗv$?cǘ̺QUƾ,w,K[-s)Y<]ԗ}]Kaҟ6Nwe @e8tjP,%zՎ(W-(srٗR&[Ŷ&9>jvd $, @sDIuLkZb[E]=1bQb /n?aJ׭eu!0UaL @8E$7&GY䶉rץkӗ1vzT|җlm_Ƴe J r @C%’G]ڥ,ƈچT.j1[ ?ۨv5}(!0x༮  @XjJ8|N 7ʼnpԷXA1j~і:w 1>@ $P&ӄķkkcrR6oT]//}b @A mhld_w就L?A`5aC @`I *U%]&DXf @E> Dc74gt88+Ǹ!@Lv=y[y׼qb}C @c'l $68 @ sGݒ19 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @ @&  @ 0E` @ Y``ք@ @l"0@ @ 0kć @ 6` @ @`5aC @s  @ 0kl̚0!@ @s@ 9X@ @56fM @ 9 ,C @ ̚&L|@ @``!@ @fM Y&> @ @`0 @` Oet:) @` pU3+ @ 䤶'rWSK 2=̀~ @E ZF@sF?ڲtQ߉?70!  hf) )%!wtH+M_h3tI+I+WtfwPMYצ R `kPBUޯ[wvꕋiw+]~'Yi o@k?_|!v3rf}hlՉ>j 3$§k@z_Nul{-]Gӕ̀hϧڇkSsZG滿/_]X] !@8lt 3&t-Krg+]OW?i㭏Ǟ̟p|ϼs^DViw{S.ߝn?Ly9?@QU~E+/ @M &N pO^wo?S>Rurº}7'n%<غ cʛ#zum\6~8?L[E{ɿXc,@NA> @`N(Cѧҕg>L~ 2?0'_t[wp0Q ̜1@ȷ7jҵd?`y@ D`sLNOWZjK)~10 *tGA4@zT "p~{ޗn7.~j:%J: ?oҊ_`&;?q?]n~3i֝~l4 @`&MbAt?tJ~ߠ9?Um 3+m= ݴwL6No] KLȟ7r/?o/4x[=p[͛s.W^ͧN~SiOמk~A8_5p7@``qזAXN\mk?tT}ݝ;rrqFPl ;3n ˿+is쵶 LWASH@83E򿲻>?iW66qfVtʕSߙV\m6^~5׻NA<) @`8 5| @`>j~܍=?="zE}ݝz4]O6_|c]kC 'gH@8տ{?WWҵOLGimO6InoH}1upJoz$]? Im:@o p~V}=\6Vwtҽ?ʿ=:tj` p֍!B#G"o1 dʹՍm'uJ@{O>:@Ns *  @}o|`/]{/$ͧ޸ohfkGN7ϥ{otG7|8 @_ p:+->Ϳxg] ${g,S/7?rbkޝn?3 8 @WIcΒn?OqNK~&՜Y.ϔ>u>N~=j]N]Ng@w, 3?@ DD6"~|S)R3duiY?]yw35w~ @``tVXB?{w'YUY{aYT׸Ƙho4oQy$1fu׸" "( 00޳[ugU]ֹ|s9j%`_F=$-jİZfnk =Zgߦ4_ Vݵ[FE c,kp p,ޛV#@I:%$WBZV]'x!x՗yIvi2ߐƕ+ۙ\8@NNfbT M{]ri>BI._ % sKFScT^Ji:"ٌ$ZZe;'lA~yO C9~! 5) /"R` n-:cJe8vM, WHjbM i҈MC@/@ 1 @ڑ+UJUI97?+³!PԫrYI-;Q:^uB—@ 8* o T]@['˼n]zɷBrGZ!bWФ-rׅ"mZ~x@ u}x@3ryi>i 7!]oCLFy 5"l/%D zs4@ X.ƅ$$Xhr|ݒ:ܿ0:ݷ]~bcS(x /@@~Brw: ]gstjݦwÆ7_z4rHfDH:5 PC5 *`õmM)i}ҰW6!dBϿHڮR⍩һL{ H' U;iz6JgJS$p[z8`[r2iMi#1mO@\K @mmk hms6%`eO=[Ca=ǂ=@( ocN@rVo:li:]Yt``"Dl2I~q*;#@")@ a @PJ4vJji#=vxw_J])v\%U^@G3- /d9u`q !DZ)?Gj,Phi+ԉ+uF$]Ƶs_[U:0žV{x'"so Ւ_',RРdGG%#"٬2iɦGM@Fo@Cc4IR/A|woDYGTyҮ)P^ow@ Zi/ C%t{О9xUm7޼Qv<޽C{.#=GMqtv(M,X~xy@|<\i.x϶~En(NƳH{D,`! q|@& ԊSvRKt7c>;ly[Ggw>(^/{wIn;-C}=>Q:|/'=Ickv )z-x*Yu⼋dUc#,`S :ڠ.GYX WhXyFG$ y{m쿍5 @c 8!̍wy?̟n'JEWx%S  ÷GBۖKG{͏Ju Ф'u`݌+ɼEI:y0eGɁM;A/&9~W/^ $@?O}vZ+%'H㊕ٹC$(V߭røC@$K *`ɮ%ҠCc)`C՝,zmv蓒!O?p<ɦ#ڡב9}KJdEJyـkPEl,}nGdKs.dӲeFF9뢂^yt8[YMbkZZ$MvG LI @Mh/BtMx%Xiz:[;NԸ +GUn'd-x.آ]vf M2c\>&#$o?7k`4i s\n-,٠ NZ.HQ6@*@  @NvN>UzgZqlz!3a[xLdݝʎ'xDGBC=of𶽲ߒn(g_qW>=u-ǰxBg)#[wj3} @ Zi/ PkNҞh(P?~'{v-~2H676ܴ/jIJ_}Vdc~e^[в!:Qd|M7F:y> L/LZg@j'Cm6i+[sܳ[7ۿ+t5lzt~^KVnC77˞MxPz& $d]e_찜R&  P5@6**眧[QVڹQ #?KnΧ7Au'(aMH_|ʗPfXKuW F@m9* =~J/ְ|YHkWfgu=[?Cvy_Ҏ&venڶˆ^uAGa4.[&'7;9"@_V ,1[?ss('1zwet޹οzsYoBZ;_[Qc/聎ȤqH,X$B!jUE@ W pLo(okc:!= ׺g~O⧲.`yz4c㉵c .Mya 5.ةNx!@$@ JѦ @ nokiO9 jX?sXCٳ)iЎl(!mɘ{(۞ysx(M/& v7xx HLϋ@f"Pht(v%Hҥ^"+* V_^`Nsݫ4(YB(٧{~#\-߽ғQJ= u.@L@@ q]y6;gClgƏt-poOiI6lB%){6<'NЬk5acٍ;Bx   UA!xK^@ !]yv{VouZ+懵.> 7ύ7Ubֶ~MؐoZ 0 ]x@07 ;>>;FԳ}JYrkz?ՠk(@_ (]2+2ge߽d$Hcg`C@ J$mڊZ.MJ{a}#w>]/.H"eU9<36(・KcQ 6 .>97Vw  0}7 hy i6"ַ *JAWϷ5Jw{5H>e!ao{7! 033sS 3;ϴ~ΆKc}2+ .o ?ÚK7ܹM*G{l  NF+dC:尷Vi;tʼ2Y9uu߱,c! @H*\T@ (?ZK|V9sPzv @ ni9  7`dpл\i ζ΁W7БKcEI˭Ի0~k#@-@`d|@`6 [GGXV ^t YqjdtH;::[6_RЩ - DE'rjQӾ{-# FauFB*`ð =04PjAH;b\V[ZdIIr!׈XDˊsΕs}ZڼXŽFxXQ{0Y-|ɧ@A ӳ#vx̸0͗i@O !Y.oF6xBX^"1 Y~Yr_/M-^cqrԇi~jf6R'l DU@T#O@* ̾}umCCi?(39O^wɊ/#СNOC:5pjY~tr 08&[#@%@`Z\ 0SeټeH;b6dMrѫ^+'~duyQ4X}*Mrk^zXCRkV&׽O}: @ hi6 Pu#.K^/&p_ݒ|iC^+_)g\y$E2p$t&%2F귕xuc_xy ay9oBӣ۽MV{ Ա :.MC*@aoWwlq<;W˵/"^i HHW˹/FRFCN`k ͽ2f!>yrx!ލ7G L]`=@@Egף#/-0uy…ruTyv8 H&5 ^W^/7xtvmx_vӛ]ٺ] md Q Ũf@E]-gGFhóú`ٱr}'s诐9Air1IŒ題hXN< .g_v7r ARN8Yl 'vDÔN ] |K- dr̪=_G$mxu6Ky9[ ΤېdCJο:ٷSh{.=>؂pUbqzҺcVdi+Qν5U}aiC:0Æf! =FD/؃C2zMQ.1]ʹ9jxwxrII zoh_|rYDCiQx<+Pvh lx?@#gZFۜݒݽkn~s"zf=? ?-k6ESR( 6x{???*_iw_ށnq=%+%]+hdmK3hm#Pr' @HD.4@Q;$m$.[˕UN[Gۦ5465kgN.o{8´NKuҋWKxttvOMq._)j,FyRGK?eL4 0{CJ@@U;$Wdj=Ww r_%1Kۉ atV[/kX%=l -fy,YjFf{:bt.޽W eځ 0]FLW@f'PزAr!ų.ͦ=Tz;H?mongc}.9O^poɸZӵ 2¯  H@AU@H>Aڮ}ĴlV_']P/^g\hTjYyĕҐOٿ9oa {;AX޶ 2ڂy/df#̈́_rc+{ҥ*[@?TOgi,"~V^"c/@ Pi* JRFȶmҢ%f]Ŗ;,+ҠCyoW^+Ϯ_;OA ШMW[.go41`n?Ԯ s6@_DBיgoy\td٩gHcK_M6Jc}mBzDי${? Q hV@ (6,;o7<)/{s,`s;l^0.xeX[+{9g3vW>,)`{5P~;ۿpEYbp \"4|Nk4 MMMR!]otsU)c !! Q o!@ުVZ LHD N:V/d2޼ԙz]:ggw`~ڻ\Yҥ[RF knzXo8_QEmzp .0Z) ySɱy@ @ci" @JC~qUl%(ۜd*U^1D9}FxɅDHu|~ټY21Py") U P5j "ZѴ }TJ\絗Vyq+xCm8-=/(%R4IK:~~ѵ*O1^B@ J$mڊP gcGdpz)59ms#٨Kذk;CFmCZ*٢r= Q n@ C3;k8O- Q v5lf^!S~&DRG@)@ @׷ >z&]͝Q/XH;E7=kIx0TZN2;nu"-`+ۈ}o͈6 LET@:;(۷KOn̖:ۆ}W$ [)K#e{Y? A Q?@ N~wexZ5< owMhsI@6'Ku}Oa?% S1yx@f e =)f3RHz+3f運:$Cr}2pdpRpp@ rl P@n[<g]Ҹ%Zu i-zHA$FY <7%$ )MD#fLJ@LbЈ yXFX+yBNsV ~1Յۿ%!jc߇ T:  `UCPܡr_-{Cc)ʝ~523.diD! H߈=@j)CcKݷ5RdJ >eE9s[ě[$c  <HCve8@@` +BJ]nc@[4,_!v+]!*^c3:?2:oMaC@) 0`JL PS]޲zoJPYoa8$Ցv YoS@?k?K@: @'@`z^ P ou:@zAV~W67\:Μ%M͛R P( u(: *MBV?ezOHQ2^.l(% 1k.ٷ[gdA_Ci-`C@ @ u4кǼ!6i~ɥԋhDƒW0_\;y К0c|@ u @oumqG(}öW{Ͽf7e{c7moHnXK[ >T@ $ pt6``IB KyJ1 zk?sG/w `Cmh>'k^߫^yGJ@ Fz#%v.y: t=[C^J[` hǿ`1{߿[~ ާ+۔#?JO 3ul S@;vV{-fk@$(^/=Emt u H@Hsti 5`Y'5Rmsoj7٪$ `LZCC23֎o5_ոp0@Ni) [O[GrgtteRlFqod%H:xhme'dG66(;_sTE@ $ZԧumDu%lӑ_~ҴdHic4'uDma2~[ٿODa6*;Ϩ 14@*HT`@ʉ^No~C>& $p79JЀ:#Ysgb[o\OXՊ:@3sFK !z9/e.:e)MgM wJ!-MЎ]Qm6BoMܿ.do'CGF !&]ljw#  0=bo@0 X'ӆPs=wSOK'$sTJPd @@ $B@ 0KT{lyٻ׻sI_u.xII-Zucz]U@?k svR0:SӒ?tHF7<)-Ï> #:z+0㮥 5 Ppx@ XVbڱ\ >VJ%Hjz%ҵu\N:z C2[2{uXgs$i ?r _r}HbZCIyW= X@@*X"]ۖٵCzIjf򼴜}ě[a)\Lo'x,n1Mz?!)PXѷ.ͮ-|ݒ=Grˈ^/_oxB҇z$,w+?#)V@&@ h> r_:urCuX/#KHigJ $5<(&55ؽN5Shpݼ<=x/9XWuo~V[ɿ8쮝n#cxM H,@ !N:V9Ò{\bO>U4BN:IR+g' WHz ogm`w3C%;_%+6ɾ  Q{@ Tt>-?yہSȔ\1>1vc[  ~Ȇ   .@#L@@@P| @@@ &"   @     D@@@H@@@@ $"d      D@@L@@@ w@@@@i"   $    2MD@@@@@@" @ A   ;   @HD 4@@@|@@@ &"   @     D@@@H@@@@ $"d      D@@L@@@ w@@@@i"   $    2MD@@@@@@" @ A   ;   @HD 4@@@|@@@ &"   @   HF4_4>/u C%r/#OnK, PĒdCZ.\RND<'wQnKfƣ7"8Ϯ|>@`d{ح3d[)Cʾ,;&;/%?O$XAD8;a@0:@`\"'{ҰY4/ɢsĹf@`$fǦ$ѺSj;!@mˉ~F}{ok Gq5B j W$͡@f/Ф/hJ鴅E9*b"@  0b_@@t\@cJ_u$Z&{H8Q0i  ! 6MEEK_qk V:9 ΎA)@**M  a74Hr@V?+Rn[OJC 8s@N%).kտqB@HcTi @_ [d[֓@#@:@@!\o;t]aXP1@c8o!S`xY^aT>Ȫgwa rݵ@`F 4z4+NٿIu]Xgw$@ $j 0EmLqV_Ύ8mqvM+) Ă Gsٳoe?m%B|N'$6(,q]+4 ,A8Se.[Gp^ vrHG T,$57$s-q]ٵO#zr=XZY|͒F-1ɽ֤%.:>gC8?a1EРdSdh̓˧8Og eޯ\r4|;$Lxͯb.HE kJF`f "(,<^{{]f+,-o?SsK.U);ӡ[T;OX<!` @8F@@17}Y=F gQƑR@ B !_uVĊLu b@@ aKj21gQƑR@J$ !X|$9GIYV84@$j @RNxZI-x$hP@ 7 @$l]ե ӻˑw$HhQW@`$nŞ D^;s8:+g7M@"B}@@*%]sg^[sBu@w$YR u-[䤍lFIiR8Ԥ,@ X$j @ ]Ke?nGE` `Ń H<@P`}T-NZ=-?[sbB@$\jR u(ty>)őagQƑR@ rt 5(b;Iߝ?tV n) G! PCyo~4u 'XωB[OJC*@ ^ X .]joIz3ʣ 7ٍ# a (QG@j Ir'GI>, q+@zR dAuC@FUw~< r#@8R  aD@JE=Β>&dG7?'=(ĝqvgII @XH%R@* _iΎu?]-X9X6 @5HTCc  TJ>gjw2gQ#b@p W- s*=+OrrtNʢ٭'!a HQO@X ѵTΎrʣ 7ٍ# a ƨQg@@`}TNJN%Nʢ٭'!a hQW@H邋7:+3-8+g7U@X#G@p$PdMwTw;+g7Y~,@@ [Ev`ozgd'tk7q"PfeĜF!.KMB)@ q Qx"'$&'WV jkk?cg-W$}(ȍqvH) @R@  I{oѪ?$ 4:{rR n=)  Fz#!XOH㙫CXre;q|GAnGJAA@=D6 !7]M[IbMxl᧟l ΁ Aj.@! @Z.{;Elm5Y_tDC(TI@9  @IF\R8j)JIGY znyYyH8;@~HO,i  %k=Vt{~Wil 5Bj-@ @LY]Ke>p䰳(ȍqvH) @ @ Բ>CDsƍn{^zU'eQ[֓@z P/ @rSզ .AD9gQƑR@z PQM @ ?HkXU+b;k\/~"kpV n)WYڅT`xR hZ|j' ̧GgIYV84@H[Di pV~b8;vV n)g]چL`-VN׍NٷG} Nʢ٭'!(F&@` ze'?&?#X@mR![Zٿ)l A*@ ^ @ z{$^RqYV{M!tXbZ - C y(@`2kک~zx  _ie/z}&/yٯS8Don?C֗@z` z&mA@HdG6|M2[6ͺ p,@R P$;@  $D'/|IYV84@H{i @]Ke{otGAnGJA$@ JѦ XJI[G}Zzm'eQ[֓@(Bi# @d.D:z my  E  XQS@)Pd?>y蓏O#[\dԩ : ,BK\!ǿsR n=)  (E"ԭ@Cw?>'GAnGJA*@  u%u$96wn#(ĭqvIi @HD-@R!ng:':+g7DY@O@B/`//$H8iuR ,)  (G#^Hۥ/sҎB6#'QKR@ ni9 @R)Y?w֊#_/< r$@AR   T`oك(ĭqvIi @HD9@ %'>I) ;+g7 P 7@ ,G%CJ]9)B gD]@Կ@ t^*yz _JIiR8Ԥ,@ @ c1Yrǝո̦gGAnGJAx z @@ 뻤1+Nʢ٭'!HM@@ $Y8F ` qV<  PO$)@>aIΛ懶#7J, q+@zR q ! Xԩgȼق%@jԛ z(A;hMx"m}?<(ĝqvgII  ܅W@@+KpRȈ'E!n[OJCx   HdɇY}J{(ȑqvI1  p,C@ %D'H)GNʢ٭'!L.@`r^E@ɥ:"(<WIDATqvI1  p\"v@@6]$|_/, q+@zR H݆w@@JuwrB6+JIiR8Ԥ,@ 8# Peb<.Kn/IvVgQƑR@.@`V @U]|N=tP眔E!n[OJC8  Uw|) :+g7 0=bo@TK;ycuwE!n[OJC 9 s.: rv|B+:+g7 0}7 8n? 'eے𔓲(ĝqvgII  0}3> sWV.I~ܧE!n[OJC y7 CR9< r$@AR  LHT! H`oNXѭȷ, q+@zR HLߌO  L t,|;p_I,wV n)@`v$fǧ@@ׇ>*YwOqP0  a>MG@E/^qR n=) @`$fn'@@1%7|Ɵ#_w3eX88@$^@#0rrv} Nʢ٭'!N4 wt~dڟ;|E飽5 5 G pT@@`nÒA]o8)B g 0{7@,: ߜڱˉwٿc{5 5a  p F@@Q Zz%H8);_Nʢwٝ%%! ֓@8@ǫ^+\~Fgq4A i@f (]|pܧ880Z;\;{ p\%b@f/w~_R',}AZȆqR n=) @  KM m9[sA]@& 0 ! PC*&'%~>IYV84@$ܛR" cT:{< J?ls$@b@ pIa  qYr_壃ϒ?|hqٵ(!̕@r \@.0mr7KYyA(W2< OP=* Tlu Ǫ8_@cD @B(Ӛ ԛ%J#= yv B[|\"oyjd/@ L~! @#š׶ʭ^*=iXщ&8O>O@ m8 @PR!V/J_ '~ R 92Ի@!\/y\ Ys$J {f?`VTQ`xZU<"q:D ji7T]`{gu̡0aΰU@8 3 0C8>LW s2 >?_gaH&?vN!@Ů )O|Tr}S}ldwl{Y?o깙oqW sH@n;oMN !?2,/9 ?l$7C8@9Ha 0@vv7JUJ5IyHrbK1L?I>$}}W \mE^.l> #On5JL[M@@{[O|>5u%@Ic@@@\.ztɲk׹G@@& ?+?5kxO !&E! DJ#5@z$_َ׎r#  u~Ik:@ 8x2AX@?H"  quXM, AsKf"@@Ŀ+t/@i=8Pe['g ;@@+`CONgxFnyٽݬ۠ކ+8'ǞT>.H X' / 3?Ѱ2z~`⽕w{Xv_XzhShu@@h ߷S*~'OY:v1^wyAggC$KPO|\~ifcefgdk7 - @@nO|\ٙN_l߉&>ݳ! 0Scnб+;ǫLXYlO|  ̵;qb{Od i WlX=qO˩5fǰ^u1vT  LGV=V'>?|~x}'}HD(سl;~Qs}loeؾ~{neVm&n6q#  0[{o׎Ƒ(>ε&=(#{,Ur7 HXZģW 9+<fn}ypecXZL\_'O񷞿 @|]LmsN%O[oWd?, oyxm`xZ7Ɯ(kdNWsoU  @|@,<ޫ;njm{csB~Л}ry@6Գ]2VybZM;2_#@ U󷿋n_c6:y.5;.^Dy?6覻ӴU'uoZi϶瘜 @g 97O<{M~Ʈ;טi=߭/?(=D^>8ybw1y<㷞 @F`~^wlg9w/ϘFM>C>vϱ`g֚Ϙ|^Ǭ  @W;7o̒׸%tn" @(p1kޫգsڟ͚ G<>  @|n#kgyzLc8l]ĭovyԻZD @ElQ{j c#ц:g眻ZX @^E`1ϼk$>_Xo;oghluG3 @G mαwzy ;g=[8 @xd#~4稞k<;}Y\7 nsxol  @yώkyx%}~[x} @(p_l>7.g =:q< @(y5M'_l~;bn @ w?m=+`w_s, @Wn]s[- }0 @~ |[_l^罸ҕ]v @G6G2"`#)}Bg < @M˽%. @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @>W IENDB`ic11 $PNG  IHDR szz iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`oIDATX WohU_4IlGcS+1BbC[? RZŢ- ڈF)C)bRc/y}μ-{{mwݙ̛w rDW܊eH3,gZ> |p&+1_xݏ/Gp-vlxW|Z.p+Jک8 꺦Kx`MX,>zs]1^rNT_nWTc&AT$ϞvvN<$,31,IƔS~EG/-T\͝syK86<'`>Y2v\닎\@.>;mF'P7p)H֡Ɂ:^B8 CCS˾MGBѬ'HeG> 3t)-piZCYp4`tB7`VtZc1M燾Je-vE9dPX૽{Y2 Wt`Φ}؅2:"$?>; ZR0(G[ "C5 vc$LιyKaK5 R #sóAbe`E}cj ln ĥ,:GR:.%l 254N|K܄rǤ۰X eo:D =o3 C)2'2lN(ki·@_%vB_,]UACøX~x@AO_\Y=MWX-u˄g/? m[@XsXkm}ƶ7[r4iSKLؗ#G/GP˾M\=>640w{V<oTN>Y`ә :=SBGq _i[94Zi3T{?=@wmM[!uuwo_qڻ';OAݪb V/0p ,Ƞ[3,ye5ÂiX8pycݜ}O 鹓R]Sи<.v!s徺z65$$dMBb-a8G! mѻ_}DBH'=oi9rYBN%-1y*+Rn5LCyun>TlwX /X?h7djڼJQLU0IENDB`ic12HPNG  IHDR@@iq iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o IDATx[ lno2j@(R!J%"H*$ATEE EmSTUMHiJL&<R#yc^6}n?s;ξ-D?3?3̮t4Mmi Qd/^Jͫʽd̶rӊV 0~ByªZvzdo3օlͤ=jIު~Yzi!M@x4tM#vPv@~865MG[ >OFvռ,4 V‹Siix x<{r<6d2<)DI=5%|'Fe#` PqD:e#K6N_bWRV)1 8~r^TqX^y`>0oG#N #Bxiʪ Fl ~0wU̜39Kl,sE[r99APy$p$Zޙ)&"Y'k()bAP@=y &p}lQc*;icRr@1V]wdJJk%Amm1.8x?pމRk$R2+"D*jE/z#k4R&eZ9: RBm[$)Oݷ*0KJح4q_w NS ~%deb]Go/-AZwGo\V(/+qb]+GB)8m@aеp"tz$BpW#5Y;A зEPs)cPc暪~E{wQM@FUz#?Ѩ[,k[F\c&]sQּ7g8-S4xIW5U,-8^pzyL(>fkC0Ζou`sm (4x#CH3X %"H\dvN{|٣GD6r! F/gBq}&Z\Z3<~ b'Yby`x i&qrMQR.(ɃRzD*gk>+&v8`K yg&͜6|?|^;;$wޣq֠DHkXx+0Bp²k\e_(/m us+mD6[x&oD ( YrA`08́ER7zȡ9J:m3V6sG@@trL&)pG+K,YO7Msgqe%.nL{y ]o #k%Lݟ=ҥ+ n*:j={N͙AS"*Aj=s.%U/GFS_W)r.JO,68YG^\/VS޷#eG\h-"utDZ |A]fͦ+֊Ouxo`E&a| 1ilL^; lij9]w!ᆨϣPW|"=fΞ`_ߤ6'yoҵMvvK3Ўe3bˉwFcB1Y:8RP ot2[[ͣ:54h) ٸoA|<.;w<=^@/5Ӡ<7z gƎvc⁁CˌQc7o~̧PC4c:qH=)Oj`jؘ}Rג'oei<$~ i/;a2 ?&Ylϗ'?|jcDFׅl~,3VD B|~l~-YRsC?;42KݡWT*I_q uשfLvJNWqIENDB`ic13D PNG  IHDR\rf iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o?IDATx} \EwuoɾB&d ( #"80:߸t\gAQ?p@  0!l$!y[󯺧}}ߩnWթsN{ιu֭P s>ӍW+F$@GS– ilIXV?AZ?ZTq3$}pgE30N6IEe;;l ]ƴdLlK/m/~1z>s]S&֯_lْJ{bN<%n I)'m={l-}1K ,L`G׎\glcpg׺}mu;snܺ[<HH|K;~CkSbe+b@ͩ6FKW}@n`}^NTxNtPB%JsjxH::wL̞{o~H\{^xpcfM88Ɵ#MG/Ft7g#F@F2 Jl:;T^\guou…ݷ>,eP'cߦ vb%2@i`\.wyvMzqJ%{`P0<41bӖeP/:uR9C߄KlAeG~5s@4"F5;T@#!*bw&ܫ^!% cyv,tge<'@;l q@9+օv}2#`UOSi .}ݩ4~p|:7[)~بm:3ZGjp 8r5b# BxaT.aA`7-p/pa"n/-"GhFȀN ]t?s;*i&q N#s)|cx遯67(s0 m2L  =mzy &k`;#\"t2|v s8v GZYp94ȷ hlq,|י@BistG*d'O b(ՖCGm#?f#sb=tt9oGPnhuJe'`a@1?v<,&@Wzg(D,!n'ih(.Vf.&@ó uaEƃS >/|u*-&m6̎i,h58O;z\J1?r #Pp7Gv%tmT-xXv_p BeLP 7Pl*b:Q#*^A)x'{ AEs&HGPTMG88@ށa1%cB1>_!q` PUǤ$veb1KQ_9>ammL -1c6xekf'Ak+9'?Hi, xEk KSS⎋r;/C4p9洁eR50#3L0P1j.113' `"P(|Ts(i_->cDw3*HqH0 S1P>si8H0>p 'uUv0lpqh)rsCoxBѠ+)IScn~S H@)!! ʪD& $ޘ]yj+͔ GN#o;SK8~h'<9;U\{綹%>}:yHʈQ6H' !`7pȏx0O Vz넌Ƃ?y"vjа^0ջ{^K.'`;:!n2yGørN3/a4A]ht~ldx9̓8X (g CuX8P) l޸`~ԩxw@ヒ:21pia~4\f{7f޼ϛf64!G T9,гASL M vL<)CK d;?rl< Jn`:]y\ ~|`yb?T/[uyR&O7շ:Gc/8;MFFm ɝCk6̛bp.4!JR/=`" >ʏ0f#͛\q9V%8Fdׇ֭.=N"'`IwC =dG*V GаsӁL[j3ǮYm /X#d@qgW{&&C6F*2/ơH :Jz.B(㳱G/<˥2d3/d:N@j!k g_7l*4[ jLeNF;NSGX  :q{2vhSo8o"@͖jqlz} cVyJ #pg=-H \c ƴ-A~: N A N[A F{|)e~llp? ,ZS Nr[5BmaF лl'e ^ϻx_&?aT72 SaDtWl݃K܁:k6g?8A c?/JQƗw`~gΙyԩS^V려(5W9Ɔ:8'#8x~p,?!pm8yp{\j/'nN%EGRaՠ۰ i*s]`A_S2Ao& 8LgйpB9HH31H f}m~Ѐ{ oUPkq/9'oHsޤԋ>6 y~4,v鹈1O㚌cy>m @f~:F ^\xnNՀ8j5XCz6U]Lw`5~|p ӀiÕv2"SN 08`(wT$R Ts+qoSVIJg2= ] Nxp X?yyU#/!bxϋ[cKsc~ev{Q1'_KuZ/?"ԬGh,Sb@K~"vw $+1,]]E> 3a^8QK8qqq9y)L2{A_^,_Ć]|L *3=:v#] 0,OCo;&_{/8 '`'!bCӢ;ET!)!\ ~,DNy `\WRy:ʏ0Ҝ@bW"4gШ_qUIWWq2(hӪ1?b};@ X#eSco=ΓZFhC:XX%%Հ8j<շp}5'K73f]mv >BJ(ð`Ȥٳk_.:|F*F^+P";|,(i3_io2غn*1 `b[cYG9 HVr8z5ˢ>=IV9)q_7~( drtfoG>g_'fl1  Zc@vKrpR0.4e`\<8fx^Ae͏4Q`1On|=yo- Cu07s ?/(iK9p7},}Yc D8mX*e|Pchcx):/gk/l,1X-klr(/p,#:fm|q-Ff6vZ͇N^`#C|8#_9"Z.~oPOS `b06|kϔ6})*`)QB.A\=WfD-Đӌ bN _L-7JW%W=cnQJ\J.p族)iƳ󶬀e/ֱl<; N}|0N/3~gJ-S2)Jh |ԜC6oh|vN'^I'@ ^ջ9eAhxP\7u/cl2Ɂz鼴j@l6.ز᷇0tzD/[[tQW3+Ͷl^(:%#Ŕ#cI?,H{ glZ{sf5-}G6Q[5TZllܹk]ϵʮ,މ>s$~^ZAu~8o;&NXҏ:o* s"XF1jjzbzWnl^\oKɲv0R.Ӡ @N _^~ o=C`INq QĴ#ʰ| j9f|q H1y|NP pxrҬ0v˿S3H`u8&PNư~[# Џs m6_N7 F`5?v[ϸPeA/O dyYa^ KU.$"oR^`0oZu/4yhBv=kV\sOXcZZ6 MkwbVӀÈC4ô3y绂'`ظϐAgIE1?^Fԇ T0@+z#TM4IۢGV`8XmquCLofF_6~ANsSGp c!kK &Sp W=,RpZ#-4Q .pX|pvPWbLX B`+7~^0X. ]iz.\hG0ylV;)T9>a$@N`w/[h$m56j8%m †4dNpE(ϣ80F3Nf$?8,jyeؼU\y aa*1 t65[P{ i zٓ}m566tj>(Zۿqv2Bh4aBY|Z`TnhX%TUzTf4}KԔڲ H n٤v\6, /;E['%X@{}}@e5Uci40]أw_􊹴tx:JNDm >85TaR9\>0& d%#eU2ݮ׭Q6{&GԦrð]Q]8N(/GT]A&=+ٟ^"x̴EAT!֬X2:td4كdB&_|r廍FNhu dUvR tu β#v #``D``HJxG\!@OgfO.zhW8Z"hfD t|:* zatE'MmM;t= #`Z3mFp4L&SCRkns⑳vxk::y[Oχ>8EcaP}QE"3}ru/?VD1t8n\fYl&A ;CP _-gg`8&lB\* jn;mXbR3{ < 20mY-h>3f4 h Ajo.6շ<ӿgXNFBiN T+[:;Pf1>]{> -!r cd}y\FN|N34X]H[A"L'ٷg3a 3hv@= -~HgV uo={m|dpl8:1tKphopN s7h997-&qq8w M?Uj1~4Ir80XcHv_kC<(_= @Ϸxq$R[^H*gXM4] S޼~I xݛ+7gW`JN矏)C }Zo\|s'X NC;.]L8Ql yWR:9z5+ߤezWx8z1}͕2[#bЂ8c4fgpBfO+6tÜ)+^ܺjWe1)T.4@}tDpyj̉oVv\/ nߦƹ;1`MG&i5>gj[Bs rmcN>aBC^'j"hf .$]ݻv5_Jq¤λHN: ;6|LT扁%tг\!>zT@HmvA&MOܶ7{ܹMqZ)&q Є熹WYްq 0Hضcaf\cp(1J(%<'2vR$kW hI?x/F%q liP%s(2e\Y?`tTO RGu?rTtڭ?WAܚ6Y ǕT],lNLF l;6ک%rx2 P E!P:˦}#oa XmR߃{ιC8!2p^ǀwJ_U[^F <-{ UTi~iH˜<;pL"5D3j*GIh! d- aHίk rxK@ۧ{w}T.OK.:ٔF.`8Lyr4\\0ȢDN jmgYOYi^o0glJf{׿vy>5bGs[> ʚ7-mEٽzȏd)it.0rUdqpMkap9;c`hZS ?BVK rmV/ @,嘒 UKӽHz?ZQ;[L[i2tw T e@mEc px0X2q9dBRs@A d_?,փ>@?d*鈄+Fy⏊lH2gsklW c>6@N݋kǏs98mSeЯy;1$RB+hMQήR}gW \UG=Xw/*V*p9bN{oq8X's't3Kv1 yE^@,!!w7ziMO~dP9`Pg e|8adQ[@,〆ӈQ,8iFn֫k>tŊ=9C&I;Dɓi,f7L j5ЊIkq.NEv : z9 0vfݻwϸÏ?~<kLRdy=,(zjU7; 5u\6mh봃8drwzw7F( b 6*`ާЖ\B}fls3yțgafpybbGesc/ͦ&}7&1oqUĢ"? l."1"!l` nwi H"dOq XN6vqzc9ǍǷȊG;D"Ej~Ú*P0ҡu +`Jjc-l~p3uK$7L6< 0I6!W'H2 .g']~lȣ,d\'w&zޒUoL@CJK8-,Uq|j"ү 0v6>@7,0ǻbjCd.رu!U@v .ȢiKqZIUK_^B+7]0O Km pG6"=;@I϶H"h8hXC.ZΪ8HhCOˏ*Cšx2 G`W9d2Ǝ [c{c~&U*ITTz*7|68/^<(jS]W ,ڰN _zYsF8 Y*>@nadb:hB< Җܿ-Z؅.;=3ԁw=i 3Mpon3"!?>K6TdD}UGJ4iJdfܱ;֌HqDʙeLa|[yEHf:;(Z+lEY5W%ju9o !q'Ƨ'}}w CmL@Tx ' (ݬKOCh٨щ͎zO%,HĜWG[5Qyqv+60= s^L dJM|}ƫ&] ZFB e~)1NËL@ZdXp#V xyqO TP ص6VF#}} lV k,.^YjW/IϜJH C@ؤcڷ-^Y'98j`=nKNEQLS:q:1y|kLH`иĤerPKb"wP1r}HcA u y7n~gxb'Py&5z]kfEdV䪫2''`9 NHDʙ{)&֊^ݿ?1Gv\6[*KA@Û; Ac<}~Q|q-u#xcsÂƤ[0wNS7G@c}hfod(i"`L#U''ذ9@#$`F}Oݯ%XhU.Qok~T^G:?ïNIKϬJϫl? }{զ/Bw㢈! F?8Fn $3J]t .{΀~y')j1Sb2`\EJܫzY*&95sܲQmO)5k NVKɌ~S{/VPrĢHd?-QX^Trш_GUzN; wM{TNgx5k% bMZ>j: ~p7e#%`b5\ڨzC;v*Hbj|mxej8o(=ntFũVu̧H쭮Z}H @F|+)]@+fkm›Yqhf5Ԁm|Fvg/ɵl1<ʘy7ÑI([^x^lN#Xdg23̛FM8qql!7Bpm8=[WS +αmq)L%FzW3b "hPOmK]i@@D SmK43 Y8zj@@=-ubq1kG4PO .@4  f "qԶ%ĬAD@=5 ږD1Ӏ85#SRh fqDԀ8zj[ Lb ՈHFEDl9Gϵ%ƌ+^R*)]hʤp>=3Ղ/U.Tj Ȝݭ|VVq. (Tq׀8P1`蹬:[UϗRcſʫUWǩCXȂJ4 [?nS |JuM Ï$5i○$2Ѐ8J f3*9aJa>f'c& FႫ<9<mHN`eRФ4 ,70vJ}r _z*uշdZkޯs6VjHh hf}j5{6Yb857$d{3hNd,2IlVTN_y{W~q+}iPg%B3 Y m5T5%1<8u#Rq șO X!ld|toIhwV S4\ÿQOs+NP].5y%G 9Q@ױ'\@ƈ!~cǓF!@84] -{mS:Qzzj-R{TFIC"}J8@U&MQmoԮ~- @/=Փ }|/t сFb(FUR5%O$UwQ|# [%7PGR;7UX0S={  .B?b/ f  y' \6CC2sjZ/@\g|B.S] u^+C PfaEJ,jH( 1e\ bY:3]I N 0NWKW!1ր87D̚M} b_7=Ap<=>!F OD,ĴaXh.\scjhsSA ͫqqo0t1)!I`V d? _% 1$ǘ?IGZOٔ"jBT.QNWT.c0: O꽴hK1;B# ͫq1oW7_׊9=޴X4nd5~P#G=1נWJ{I).RV3 x&;,= Tפi*ၽY߿ýw lٹ k@@u\y t?TIz&`ZRme^ۥۋ>q#xyޛ0l/ W{ ϩݻ=Q@K}5aƁ3IkiNOhOd2; u7`%B+hӀ X?]WTF{T9P;aAPS}j٣W&٥*8G{Y6((cMfPEQm5 ;|C9м#CN|:FЇ\|xp z8J!Q،$p -@Ût{E5~]6dW(~ _  GW?s=zUzpFZwA{"c (uzHσbG18݇yaR/۾jpgjzj@@=]e]x:WNFm.}@Ud|ħ-IҷvzW.MQ# _^}I=:quTvTU W{ Q˫ OJ|# QcecP/ۀQgEN !R"]fFD*Z4xNu@΄>õ緖6~z_ݨAwC?#e%ztmSvrs. {.dӀ8tY?guϝUܒ# ~I +traO\ /{b0.4 XSK ? LzH< $X"Vh)vxwM_)?VI& FmvYjWA0½O=WX*AnP[yC)e?O03<DqE`_Df3N3Yw;8,BY:ϠH2S`s7}txwd'62WI٥RXF5Tn3y54ԟv])v2P{ 5hr+K$::Uppz5,L[Z؟)YuYW% 5 zɴ {Uyo7ְ遹zQ_9 05+@IT2zc>=AꎅGԘyL<L̿>wn=ރCXs5}z\B4 n3+# Hu۵V#_\@ бdm|X喛Tn Y%PD!ZTS0Cb!k.wjzfxO˜T'Lk@& unQ@پ}j/WBy} yMS*ٞɳ%y&H*R[eDw}jKgW{#~.%cwc+إq.u>?G)Tf5!|.rͻWXꧯKRdௗҍsGm.ܳPmGdMFUּ3#-Z3! =zzanYI6Bԙt]m4⬯}Gog㥷7Pr @׳=:bsBM!cبv5ڴ1D4 #n5gRƻ*sK\z6t*b@1ʹ|Oq6[nH|#qc*G Ѐ5b5z\՗gLC| <Û]04V2hk[; î:hgϹ`gީ'Xq㚿%S'Lk3; Oxc=di~H5O/ W{D&Mj$qa#ّ&8_ 6ae]+CXޯ'nH\?N𵜕8b @ чl]* =F@QE'~gm6>jb=/t5y:pՍ*y)}\O- -ha5  Bp0mWQwJ"_V[") 2Q6'90_Zƹ(n;N;wf[ Gw繢kVyɰb琞};=Y%,&T =z2>M3EHĨ pWo醑?ʺ]W O щQ'벑u ҀR ZSZ]D4  I4КLh@~hlx9g@,4'`{C; E*5`i;]%L(X4PbٯceVJI ЧѷJ@GK33'Mް}{/vXqD `F>L=VwebDNTǖ2tI #G"Gp qX fkС ʼn %H/}N12>NƄ88(AA#aDq820oq\~m<&W~hCBi3XOc(ˡ@ j6raCn89(cN ST,814,aBx*eBOY6\iX_var`H~2JLG.  Dрh@4  Dрh@4  Dрh@4  4\hYIENDB`ic14PNG  IHDRx iCCPICC Profile8U]hU>sg#$Sl4t? % V46nI6"dΘ83OEP|1Ŀ (>/ % (>P苦;3ie|{g蹪X-2s=+WQ+]L6O w[C{_F qb Uvz?Zb1@/zcs>~if,ӈUSjF 1_Mjbuݠpamhmçϙ>a\+5%QKFkm}ۖ?ޚD\!~6,-7SثŜvķ5Z;[rmS5{yDyH}r9|-ăFAJjI.[/]mK 7KRDrYQO-Q||6 (0 MXd(@h2_f<:”_δ*d>e\c?~,7?& ك^2Iq2"y@g|UP`o@IDATx UY&ɖeK.yvH8,v$f:-iai!ICӁ@0@ @$IU%/rX^}{}~ֿ{߾+;z=n{(rpp3^ X.]l ,frҚ{4,#ǫ9Fٷe@NJÃ6pM#G`q#@BOߋ&y!zǪvvK㓎RO0G`PFMI=|i9989QuA}O"z~@BN1t3/&n5O"x<_–9@[b2є {SQq0G G` E@ry]UL1H+^ҩߜT3in,0F']z Je橩#G>?iSo߾}IuIL/.\:>5tQY./5>{-˞Ի|")䜵Y6 ^?:GnF@وΕ^UScYb?6f?Mp["9;uζǧ:u{?ȑ}2~=fhlv_tx5<=srVA⊱ Y.(N.'pIϥUzyNm msUŀr+ ff›3 >uۧ?7>z⩿{Go̵V\kWOsBV!wrrVGެoV 菉ry/s4$9Ȥ~(y—lvu)KK/:xc Ł^MS5X;qlg>?0S_|c|r!@MPf4Xx~@ E 寐UfVIA!@p2uHt%׶)+yx?WDC6{7q+&n#p[Q3Ͽo=P_̝w>*VExj8kVnq&$}O3V<͢Z^4)6iƒS_2_8`?Hyᕝԏ{YiKT^ 1kK/‘D13c;]|1W pe{?xt;vL<3g$4M'(3rrV0ZVЅUa8(+uyR7GWى*ah^o.=ϰ~с>DO' 4HUXI0y]~{}?{}ϞU3(v0G G`#uiͬzA~%E W+qK3L[8i\-{6_=N kZP+14hl̏}~,up@\*w##  ֌ 9is[EI!.xɤdb C|@nkjuv:RPS81f͞`g`ѫ?~}Ͽ-W^&妽{`F<ƞ[@rD@krZ+6XIXke2O'ȶ84_ ^z1?>ik{䒛n oRW1.sX`vT/Pe#<6';= tn =$S{HNU2.萎(ny%w᥅R2xDˮgĉ ?ne#g=klv#DJH&1,^i][ ' W,larӍW=|;!s[9a']8easPo~d0|zo|lb'!Cثka@"D$[`~b(+]ù(ONX.vKj4h_N5=[QJȭ|~nA^b?[3]L{Ol {)&nqln99 |͎lQ HYAb3DBג=\ιp e XI-y .Sҕ)nĭ^|ę+8׾@H w+##0?z#~kC3Eă] 6~OeN7lSȍo+fO;]<+C^;~;Li;`Q7M~ #}ts:@RYb) }7ب|褍v~A-sx)|mf'90T?|s`I _0d}v?em? 1AH鎀*iqn99s@.̺"FHũ> /(|(P} T>ߴ0z=$f,-,@(tB4R @plmݿ^6lmB*^ka@ȡʌ-\ %{(^AU|m>J^p}6>OI>- ]sC^:C$/ Z;ҋ> ~Ўt *E[$] Yn!:JP@[ŀ !}6JlɎ0~у~֔(F/u5):4t͉͂tiZ`_W_q;`y= R i2G5G G`h-G G`P>犙c_G m[[/P&:z[sCgMU$=*A?c}_߳!uE1l9|_ sr:"rF(״wBm6\CY9Xl>oK\k'Ց m.v_#E@(J/~_묵 $O}EKMm͕>rd=.& B/Uā]gfs~?d]E:Tja@@GjKFЊop.v0Iuc*_6?|cߧqXWCJ;n ]:/TLb(WHJ>tۃEW?'6-;У7|~m&(1Rk[e##PF`zTG@>uU$G+*~J`IvC\] Z/ |?$t*©"irg'6=Ə;UІZ50G G @.҈ \Zp4 lOi,؅ "7 OQꇏ+6hJ9Õ=-EKD0`ąTh4=<"mP<@KAU"ܪ0W|:J1aGixЫl"o7xx=c1 .G[w-m2N&<4tDz̊g2bpXhi?"=^ {]FyŇasDWmS44 (I/d>]At iTWMz!=Coj%GvT)-^P$E(s씼a'pr+=0e$ыѣGپ]T砥0bk@@Y!sg"Ui,,fCp%)Ǿ#sPB\. _I_Ix1 >u^Gc_$l|Ǘ S t ꋁR!M"wN^:}?ٟwϿ 50lzjRBc-G G@ߥ+B/D~dS5\jvl8'Jjat:c뺟^'ØLWD{IN1݈ O:EGC}«>|<:gc dofj?~g>E/޽{Ben99.pՓ+Ot`8D5jrSWm!M~WP:#d%,*b[(% 4xIJ?yzuQ!֭TGe`r09 9k2JxwN4']O~|/(z}i}^H O:|}Y6L Jag @qv {߽;yί"@0G`[c%ј}?C%M%@3ah"v5gIM am. -n@<]h¥<p~!z՟kt}%\e)` I~Ez[QeCDz0X2%_.q'@4~1]~4BPySibƛrC9#׃#"@]V :g<-[g 7Y"r*$L͏@ /WKO)"xOnrMNw#/9%(g:sh uЕ,}QC`HuVFWP]$m%bk^>ɆXl"xBE av3> w~ͮP/ b aYh#5l`'Y+V6Ņmy=i꧰MF8ӼbMPk,{(}6#_L *^p^ȥJ +(N_?cI&+8x}KGE5 ["F4>}s3 .O<[_t/޵m  H&s`F-Ai TNtAKeR z.}/~AOS KK'NaR6->dS;ҼuN/L3OOa, xẖp!"^4QKmGҝ r6LraNOJZ5^Y'So'a}dyyH$ڴy]KV[6o#/mi!ӽ0 s= 0W>/x[/ "U 䶱# } O0x'}m%AI9?/0|2YAܼz9с$6:<>-dgl< (?aCE:n:x3w]kaӁzޜ CN sew.'ԓb+{}Dğʴ񎊓./z(:R?&I2}om!~^Bs)/[.:_6\qЦ *"& ]MKViȢK|;n.Xn9er0 硯8{6Qqߦ_6]]rԎ%#|N.\zԤ!Wɍ-l=ؠy%9 AH!h tqj4Ġk޺}U.><+ׇ7bз1<~'`w/q6?%MGB֭L}ƹjry2?޺&+ o?bJF D}ǍjT_^d ; }xP|o|b]L!\l&gߕXiks IĤGpQVm#{JwS΅hm/cm©n633{k'ƒ|o}ۺ~7X8$n#" uwJV46\s6Kҧ8DW/wڹc.NxRjN,z) ".x]g~E.vxտVH8u\tA.OKpDtDM=/;w#A<)M2o{k\ҏ ^M#4OKǞxl!iO?&[ؐny)Ώ!*?MS<QgCO'yAx®DVhxҞyR) /m{ XHwN4ƹ`Ar I8Іchx=\W<.^xh4xDB)`SMXZ'>A ^^|h)7&h "'>ByQ269d3 ci|(Պ_v&Mz>| = UV[g##5=Fs[Ӻ M&vv­G^^hJֻx2!S>:sʖdS:'eLr?LQ>i_U[/}?-;v`'@Eky^oqn9k2X-;݌5YJFk^B+ǍɵmKO O:3$H4ikAU .@ƛ,U>Єg슀MO?uO=zt۹%@{ZܩOknJO;u0V6pTA~0ˁx@ [ 'q@0=Oыw$Dɘ&G5G` E@CkwE+=* k]<"bx z A4 b3ji GݑV8#h.`l#G%*&6( <%iE %SAXK01>g_|?~ʯ2~=Z̓XPЬ16CF] ́&m.\KE@ʟt)O 0 48zVRrE/zٵ˂0T -G`E dd{1te7 7 z~源z]mK`'ՐAL5WUX IvhJdy_v{|۶m Bnk;X/{ȗY@;?l,T&ʅAZڏtJwGI̥Aٴ/_iaL:B,iQ[!6>tg̲+#=Q,G P7&C4Rp [׫ʩLbE@ [xy*4Z#hŚk#~9".g\mP%dPr1J>& I="Y|"T5}:[R? K[di/Sc'txODTOq`#h[iU%^K M34ea>_u6|g܈]E7W7猷ܐXzD[ :mEEh7rV4Xgk1%ON$qlY\-8zٖa-tx}PNEN@H6[_<{=;^&"]k'X;*{J"b[ u+$;VK%Q=V/*d?MenmWr^}hǞַOzd3=d~j'_{0v~J8.5\S]-PBQ}$єnh.Ґ_c+5fvoTqJWA@xK"'e*(8(UpC{^4UBu* P[F llD'QyHid xtDk.n&"/3 }\"CԼp19%zG-`?``,/ C;"u^ e[F llDDAPp*]~sѻ^~>~8ifb>T$~A_ :_5Bh`?vQ.oAҧExQs|o۰m' xԬa9\#/8 '3m4p4d'`ֶ)Nz$+o^N}x(d= '~yS?_J{&x{\<;!UBD^<,~w5=HVFVql O=qFluH׭gȶ2hj6N1V%,[3}7 @vP&Sg#,jD@2im¥|m)O~H.O^Nſ'-K"{My"ە EsWAXO?y;=z޺@EZ`N#8X  I`UV[aFxYJ~>Ɂ;=(2*βqJyeLN:ynݹgv >@E.,|{Xer%yV_H`Z%3QfRJ:n]H|g.J)^|e^nRE' z3>ر{em_IZ #6,QrDjW>~eJ:ZqWvA5X%AHVc aFO-o[qK傁Wi%"6-V^ojߛCv[L'$Ё9[D K֬tE'zjYWm o-{2]Z3h57?PsГ54iw];<Nc?O>S{n\V<XSXX}nmW45( 4<^Il󃧍/ꀗVZC0ZD|G 0|?w>?~m\ Ĝ~n9\,j8-Z]}['f>+}:K)Zk!D lr6dk6G(!wE@U-詒4e3-4dI1 7"涂 ?^0-+pJ95I)yڰ"I>tȎ]pQx_ =kI/+]'b/h"1E})E{N|h-ܴk\vdFd#`Ak5mWuuU"Rӱ]yi_B~| }O UA cl 42m"ȷC٫14Uƾ2EYpmG?}7\5" FuR)h G?E  _^ g/ICH*c%4a~Qc^[< *3HWJSGQ1k BW_27Ʋ91r*STbP|S\mK?":%־%3Q_tAieF{>~ܦyz B:iD<_៫pJHp~=nz9:/ek) H`HFlQ^@O֜?pv<5a(H+S%iUgz{$》{]ۊBEhfii?K\# FB"SU)1G|}!.m^c 'Ry$ ˊP|4'IM-PM^INI X?=?koVn~ Dk= ɫEQ /<^|^|PO Ez '^[}hꋟǧcL%y|/OKKcU_2KXF|pUg(8=:0OHL_rFրQ3E>胒)m|a]/nx}Ke==!?V?}~c$H"4կQdך_;7].j!&׆w_W]=DO25z725^S|AL _4Zx6m4T[WؤԣQx= 0ئ|KaK'F2axS\dHl=3Lp_6@kad$ɀx%H)_ɕ(o{$Q>2NF "‰X]oE!B׹,kXJ&\-䐍kv Z*Htl)>6B5Wj#ϺiˁEi WN{㯽lG3ZiǍrF@oL#"ЕԗkGKcmGݳm󫛻[tHi!M*!(i HC8.;-`͈%藃xvKt'WA$jyh#!5mrG c9I*䓖)o:\aؖ7!-A>D3!XO>Av2J޿6mJVfpac5HVFx\8'q́lѷ?veuD h+*u+rF *rC|aضzйKp:&5v-mh笱^PkKy'[{S|_R)~"d>toƿ6"H?&uX74p4" F8`DmuTjv%G:BE[̣&|jni&֦/? o? ^]m_ך[=A7堁+K}vBMl~߹0;v"`P!( G@.XVKA tbſ%OGK7c5|<|-]fK2]x5 藃T:j 퀩~rSO\;7]{ѣG̤EwɻQzA@@.;aGmў*ToY-iA}Klfbl72g_@aBbmw%`uᥪ9ך[=ArJUq3O:/ݹgv}B barfE B-JVc1VTo:D!8ت"_qkO0zEK T^SJ‹iVOF4pR^"?Lf6L{_u׾c.zeXN@ F@.FSZ TbGtpiK/p;&4hnp b`K);Lc}rx-eP(;03eF( &WuϡC^{7PhU@RXZ`G ߈VMh%iPLp=JdGyuQ6{)Ώ~Ԟ'L_R1i OցƘc^HI=Ԟۯ.eL3==VsD'}^Z #v_\U8{>P"3b3!Ζ~="XK.jmQ:w '~`s5z J"д0111>qn暳Y*uX:6hx##PF@_!IT48ZאPuKeKfHH1)[%G=OG=mJuq}.ke4 zk9h)eOӲ01>6)nE—A,/Et^H,dU!$FD6G3%WO[H/M{5zAci,:PEpppK{,V'k2uMve':@. ӛXU?m%FRԉ\2h+ 5/cvmR#oӆ%p^Ws5zXrC3͈cH 0]&kLGzK@j4J \lӜ'9,iKWĔ>L|1 Dԇ&` 2&Q%6/*YuL)/hyݚC JFc!J[B#N3fࣁN@X?={/ݽm[APAL_rq+aNRҪaʷc]lfl$9M@dTMKs# Jޏ"@4ֿp؋⭏" o?|_XT 4e}G -0~uDP}B[Vq즶[ Y3`U]&=!t/r\`A~o*;x'(7^K*'"޲?~[XTO_nb綎# u|r?J>-%Jbo/5MCIu= QwT>b.}]x,5S=Ay]Es4da/!8;`G{_`MvPj/\0rH"axVIRH3mfYr%REKu<p,v؅Ǿ}9~}ЅpLҽpі,mEk=?[f,v:PO_nmF[@@Bb$jQ .ґ[m6imlSKݵQY: RƧn|<|%#9\=^T>mݯzL ?8oм2\:8y :.ض/IK֢6t'y%Ō# <̨>5+٩'Xc)KkIz!k[Gq`#=-ʛXVRuCc:@. Y5j^A|6EYڛǕoɰ"yp^$iI6c1H d3/GXdL rPUaIqkُ06 =b`KYl\;(4/ؿ~;ߟBaGf-%C.ZQ qʡE8/t,K?̍CT 'ťm>8K.Ë nFSrhP8XHj6:몜 /k 3A 1~(YSݍb`Dpܢ޺e,V\9 ?sz|Wy l~t߱cgLE \/J|{gG \ U`כ&/El`R6--Hjc "VG}At/Cz 𒵬q6kiR,H4է ^pzw1 `: q\(vqԄsQۣXg"~+>Ͻ}Srú4F#5l(n!ҍOD~9VEoC>,vrk][1pѦҩpۀ/֐DO,{&` /|4VuEuCC(9ΟՐ7sT1gvl&}!-B[YLJx+lǙ~#=;z2#9%L >!_[P+}Q1xb_92o=dN ?c"Wm 9vb+.4% j |+fzo{~ޥ\(h("7Dp/&/u#4[3m";f6ٶYIرTIO(m>2_!ztіOyؐ%uK=f</_H4?I ݛU D=m;VX Л'Nsvq:[:UCpkpy]$C.=W!+8όӠfk)xWj\=+k؛S8N&&Ҕ ༟{>ߏ+2*OMt{ 3xJ"zw >j@k?‡wljMS;4`M}7x *tȽEtX8몎֯U;G \,q׳zV>HjkXқxIV1;ﶒGpcft۳*ƶWgfS^fDOl QR :QÏ'zBR7A6^:kɫ+νbjϳbE) S< S]~H$M;BcÒTqST| v=l>}AՂA_j`T_+lD6K b@ȴaNҥ[hg_|1.g( />|UQU>O#5kǪql3&s_XG_}X/dџ_+8;lb ͛-7cQ(,1|_Cjo+(<^ZP"qes)H9HCK0*y'adwd|^=/P+pϳmG`=8H!"]_|OEZiP@mFB%b8`ybnͤ~O~13nC%bU*vop+1_ 1VIz,>?krp|A^ݔTx3n fN.f_|k̞8]L!u7*!n h3 %P)ycr BRW-T2_R!   w(B_. m#c7b8`qzM&kP6+1֛.6_~[1uvۮn"`ONj.z<3`"%Mb-xLkIK60T\7ɸ !L[4x0? Y){>-Gzˇ[ Zkyog k\bkdGMvЃ-/[]WWlMr)_Ř%|S~ϫ +yI_pIYP:#>W/F6-!lx>68e Wϛ/M:S\x757arl 0*@\.ern m2^~}[gU_+v|㿷ڞol1mX&,n|ݷ^r}}vDAӶB[!$a2>$}U߄T O`BA<*Z}  E{b}wm/d+CjCS@V8rr)}E2ݮƮV쾼x|8릛kNV&|`uށh58ak#?~Hȥ|A%-<ŀ k ~ڀ1 +o$In2> 8y`-ʛ>lj *0-m\##DHW;?^"Qm7_ߎ7Tl:{se6ܲ g˿A }:˱pT4AnA_c>W1x#W;@ Ȍ=x~5-vúig##`_Hv뿰;?g}{߂lHb/jJ4 S T} hg/J)m81A( ' o`0㮀pJuk 0n=N@-066qs} ?&uǎd\zXj.qx##Ж?-G^-Sc<ۊ~?(,iXdgT6ѩPɚsW,j.tob@&WLÖZkB@:!D_!dH{,⭀ =jǟ8z/_;=͊vû~}n99KzތOܸo]-Q_kY -7$ALVSv jW<pWA? ;:]UI@x~V5@]v@EU!<<1[ad ԉV70U8>ܖ8}ĦZ]% zlەE7~vfmq#[)SϹ!(*ҧ QL$TL9h/=JC涖ڒ|}Nz*\U`ಞ cS'y{)v* j/\K%R+}=-8{o/v~-ޚo`@$ZP(I-@:,Յ!}+ht:bb@ |ڤ[GkU! ~UbHVB]TEEAQ]N:]7<Ve>Q3]## 5iE,3E7;~?hE@W$b> ':0mU7f% g!HE{oH/CBCE1釂0U@ϖ?9~**sj Ss[prX K~'w<{!+􋝷^_lK}]<P r I_đ-caMS_Wi6hYDjk&᠑9Ч=On:~{eAEa5\}`qԠ"q =UyEo/9ڶ' 6hs:5(#⏮$>Hh*K't'H!0U| W 0&d xu[⠉y_}O*TiK: P|WC͒?J罿oOV\ŖVrͫ  =4S_Rxf"{:qk|© ~3uo7|Ə&v$GPX]|ɛu%<i%[:_`|n99+n>/xr?Zob9l!;v4y14 RАt,^}%]T^`W@tGdn ]$$d鱇~ٛ^pӮ]|c ":ܖ Xf9"ve"•wW8Wۥ--Kx9/d)*[ /:p(hvHU+ƹrrVI },<-YL]~U1/i^%o7Nb#UΟx h_IZA[@@#ڮ|)e <".^&_ >׸cw =7^{7 MT bG Ѭ/G`J_HV8KS;w|Kx0'yy!bve?q48jf.XJLpcJ/e# GݖlCkAz>v(X{O}_Գ 5^@ p ȹ`Yɝ0f- MVXܖ;= Oޙ e> I9 % .6?R*/I?q QÐ@읷.#KRe;7@ƆEһڏ|3wٶؕ.)aۑ&Rѥ>0=D'O8p=EmGYWbaN6Ͼt' Ž)3&/}/h#/PMXU>? o1Qg,So/ˊG[6JKSms5$RA/+d= NPtPpAƐi_Jsv ـX{Kbh,5T) |"@psXD3i%_eM[V/+HGۯO Zt+GqaZc]DI"TzSId{<{Y0 U1#99yȠ""Llz?"(js[`n,s9@2_cVH]cK[5\svxe勀-[<峹,]{<|8X^>|ҏr۟ӕp-G7>eGϼƍd M}ͯ#G #*3,c?[mDMYF \EWI|g<繚ma4DT#e2Q߾·b#řCqfO|8hފ1333^T, 4Tp. ^ar`(J>AD-ަ@'|a/FI~3 Bކ)'O>q/O:5mRm^–r8rYjO=se)9X>d@89)y: HNuk #jwD?u8u}Җ@|BPcwVTyV%7XCbt2t^zW\JĴT}?x|3wQ&7YhO:>6VA3!!H%G`#`=Oun}i3HҜ>SL9\#śվl|z! Y]ńM/~`DŽн}h妗eħ$ OںdRAczb 9MOk;yZW?Cz ?]N:¹t?WS]= BNiQ# Q#r3}~ޭI?ၹ3šx<@%oߠ ̈́\ ʷt5x}h@%:L}W @;W4Fu@Ɇ 8y\5s1nd׽b&/Bg/zG-U0{ 9Q"@PsXM(W^ʰZi\ >ZcrgL[6&en}X Q4O^Hَ>ӈA,2^ှU&8/Z%*պotx|[Wl-/J;g=Eꗿ~υˁ`%oyjƍ\̒#(WϿ!]Eg?&{X_\@:d <0JlȊS]_|P~yZ+R/a\K*\OP:֑xm6Qz>V]Vb=h\̖#l-MLePg>@%2VE˄Tl7sIOm4Oq? 3&y`^5r2^tG-.؊HSaD`;J}+}9p20uXrz_,czs7}#?H`'2B蔈@n')Q줧+_tTm%`o _*zR>?Ox[k7VLOW tc3숀W9sr7q d,_D:D{vO:w+OAfZVenGoAk^tU*.%OFθ-1U._ȗF5EzNBK㘰C}3<; lcO}[@j@tm{Mt])?Fv~w/>\]95 SV5|#[}nm4wtnmY΀3B@zC27SWFI.dΟW_ϹJHHFu`Pe1oXd>f_ÿYE@`f'eҹ3u6/KA|56ុSwzV4D**6@]W>xݱǾ3>h_LF ּɯ" Y!Ɉ33;m [=kةOCѷ ~Q<+Q䗒J_xgBK9Zkrv~bq 0WGˁGвHEOmfk8n\2{7>svfŰ0s9&xLO-᱾_s̽Oe$s 24M/( >ӳp{鱱x߸}fnHpD -Aɨ-k+&οUi+rϾo# ]$4a皧1l#P*U„gKHUTqVs vb!P[w>l[QÀkùUt 92cx*n{́_@ooiJHӡ1pXKuwOp.dHujX%G&+.TvLnl̙-_|֭H bGJ # dlDVGu92<|d;ZSO8M:=,w%x]SEw cSrmA=ű%S*xn|B`zl|K.U8`'b\1`=G`#0yK~Eo o;g 7'4 ]J(8Y"ئS46A8ncД858x> a;Ҡ!_ً?c %O8e:$.Z4p 5-sr-z|즆̉1[}Og.S>fWr}™/C9SVkinTHXZFI^PȠv~1e}䶗⺭[j@{npUrHd#J"f~lt%_RJŀ-ySy)W3*[SF^8^/&j*|y6Lcx@1s11P-G`Y"`Y 㪹,F;}`XVQhZBWJAhVF > sŀ#!\ӭ_ (K\6QGu4K8|ǽK`}6s+#@Fo\ X*{ ;b@l>e<}?n¯ٳ k.> l55ףAiAP-Ǎy:Ciqner 99C"oW .P&1E .##+{S{H5iH ~SL&{(2mv C\b3}Sӛ~WďA>SNbHcp)X>999#Ԫ:jd56oNsi׮@jh~陽K eBy+>rje^Lo+̰#h90999մA\ art%!鞅R|q ҹ9&<Ĝؒ=A{8אydX=?8E)XDW> Oj#.5|e矟>YsҒ!@~ZEUx20JuQes;:ZGm>y|~~6|1~;6o ϤWh0 mp \0]MQRp;ϲEG2ڨ[lyo'yA>=&s`k*Xc?K?=U&8#wf@~ VޙIRvf`Y[Q1F [#+ H<#jD!A(r)޻tOwwj~~z߷yoM=O[zeBfn%-f#m q٢sћ[N#sE:c`4lʡi׈T4D,x`^H 3gGAq~WOU|N~"W4_c%yXC:rZI"}2uLαN#XÍfj\]_,W4W#@b5ڜU`X5,F@VcZ+KWrmvΧi H%\mg&Zff%-e[]vyg.?L qjGOY_  HZ8-Sx1TN3dRT/ܳkי} khfeоL@;/SffX @}^UC2Sy̸[ URn]piG0yҽ;vB@54<0{&`^MU~c*(ryekp2l~6졷;Q?lfΖU FӴr<"n7"*W1gD2/N#` @ ;e"WfG~UҖ*ە*edz5(~HњTf%,ΌscʬqA|JΣ3 F~P5֦ k +:_sZd2ldU$2~+| I7༰i tNպUC<7ZjrM[3Gޡ溫jL~3k4}4v8?/}C$?z [:11!j4ldIph\X-,W0dz~bGkEܟ[woUL5]š k<$W[h˕^~rFk^H85DרTD@:wFi /^ҿxqKRbZ_Jk1`PuFJT)L)'QGQu[ Q2^! grvM;ظdh^MCفTjXM#Wb= ?SܾhjwMNxdV̼R*qvM#3E1%#(I445ՏU_0Sz՚ ~fXx9޲8EٔY y n g]G2[8q3R,R&W+metNٲ_zcD;VeTdcc&%-T$N2chIR'~ү6[N_o~4::*l/MI\}(2_e8n$PUoBʯ]OL2w">5zfC>NšOz-84y/==&q:L-`yʮ!Az<Q!M6G^/hs73Hd6@%v0} ϯ4GC9߁X#_eө1\j u/PgJO=cCR'W~y XҶowP[x2)7M7اТ̶L━6drg_>xD5ȾdH~23lt@2|Kvs (y(/X,Y~<<Zpk:ɕ_KIڣxB3{ogk6c%͇:Jk>% SBGJr&̟8U/q;֟G`h'櫎 eF\XnJZ+ Рz_UbE;MOA[F{/3$YZUٮYFXL=h0j}ܬsT(FW _y}+n"K25f)$t|-y_kE4iES]7jvVZ&hfi9iEkXqZmB.YO1}ͪqToEV 3:.xwF^i% SP?s FEjSiZuŝTj HߜLclXRH7oZhz4,q^_H٥mj1yl5:uC"PW,Y{{|O,Y{8U{3 W#RIS~qA}@I#Q_i[%lI4uf7 +Lyq&304V;:GwJ;DJ`Ư,:Hm ѬI4%)m*3_sIp-Ţfk yӴ.Wl͕e25N3kOK,R7t 8[3pWHW;/RE +oO 6_E4"hqfi,ndF@sPߤ"qع9\>ʱYZ=3KEUɛ7Uaj lzcӡQ%*d~4N|"dgDKXFJs%\kM汄%@t)ǹjVNRJ֘XG `2YG 1㙛܊Ցv(6R,axk-Tifl4K.f ɱ8V w @o Z '7K, ,'8 OQ;UHҎ0Sug/U"w̶6mAk՚,/3==Z_-nVqV0vtڏ|d.[ވ" $ԏW<: = 7SR(xKs㽱HI k^$Ogz j-.Zf\5 {=p>!0j%%My P9لkxͫlK1ZJ +^ lF?h X^䈏jZtZ2UWxcnsz : -4ARIt[NKTB W*r1J%Me T@(E1(ONZpSiTAW#4$luՊAisk?/oL_UZ ҁG(=?1 ӏ쨼e[Ҏ iThO%M6[ޏ&BWuk :L<0rXѩVLm G[2U-UpGŐM$F5RZiHR'ל>V⻔ ețT[ fVy}&;(j^D$@w,Oh꾻3F鈼qW=$ݛW$~/Ov3:JM|Uifa<=T|a:1N#dhT]A]R9ԗ3LAgSZm6} K3l֡柔6 עc+E)9CRɠMY|GS̭ G`R)Wh8386 bhT]T#鱑=80u*,\~EoO{xG(ٔ3ӝp̈́y9-` T29wizx#J>G{>#cuWq':$2F՗$Q2+52>vI5~Ho8v J4M}FG\,j, WTի'r  lUnKS-Dѫ8鏙O7u̒TTshS 9לraʽ&@ċȷIR2E 5IAe O$zҨ/N%qV惡$f~h} Tȕ \ yrP@*cSΏ [e* U<ŗ87M/"_dmSu8aՎHL64Nye(~/.6aS;H64ϓ?K36GTQbz"fX)'2LEVg*o:鱙GuMAkldy?W^7Svp_2+s*A$x\\Eqjv4vxd*TIƛEͰW}Հ!Y њ 6YAxɣe u0cB-S.G#_4y( hϚyF@f 'ZN62Iw著]@c?gkr,]T9oʱ7-17%C5 'IzRՇ`9^m1J4_ӡ׾<=4|p繳 U:+ekf6x4˟paT i<'}8QàYV\.QD1gf }da 4c;:s7rqhR#REjI7#Bv*)nT zh={uyWrBz5qIDAT$SwSds}N@r`QJbv<_Eܰ;6C%KqG^30HL@srs*l@.Q;@#YCtZ!<5AŽTܱ&~u'wj(*\tU(3y8 'ݩzqƙIW#5iH04j&̠@ 8U/o "G9?-);0HCl 奙Ђ3H+/VA_p3*f h tG`֗摰8"8Gʥǚ}0|{ $#0srF&&4ɛe֫en:_em+zȳ>B[kG5{(vnw^OIA6-CPޖg6oWGrFJIXʚάˌ;,2zƩff)?^au%TȲY&mAI>׈x!7˘Q<K8O3`jV8)}7+M@+~?b `*BO'T|8/a5w"*'4O's 5@{,UKkӼf\~2/M_Rnʈ?sA,T)7Iӟ"kRO=eykTK^3SfDM ]B@Uىo47݋Ai^oZj%^*rK'p  )A""Uvڌr*FI>H~U&A 01@CTqyWf*Zyx 'ů%ƫX+3ͧ~Tt0:=h@ T_KHUv%15K5N})g,ae aX/4 9Ioi;͔A^  ?e*Bc*,ɧQ}3obu)2A$/M4]| j9-cL@0@ QX^e*$ o83݌OCؔݯo7G989gI,'fr '_^e%La2q '0ReoT4LP]rAyxffY  P%ry35+Txo4k_c 6 @Z&PKA)xɫ6Zu, @b$\E =_Hx8hJ@@MBW_ְ%yܦ(n1X<:@*uxk7$Ό7#l'95@JXbƛaS4<^_HfL@D*z4?14 7az}z\`DMRTfi J^ ʱƙe$/aqK>H-{ nM@ T\:ǚ54ͧ7uHXYV$O#,H~S\Zhr@Tz}Tƚ. {iy׺$7} KF]3e̼6嗰yh7K, @@ T74vHDSJ_9xo֯kokyx/Pe<5]|=6}-4M$]cǛOkaͣuxk:`=~(  ! \K7NEV|qn81k>nͣ˱87P0.APl Oɚt 验5M0ÚZ֌׸F3~5 ~s`4 A@ TzChkk'}@RI@u--JٛVZ\to}ת/+xt/.*f5?H&6A x1d4  I# i#y@@@ 0b&@@@ i`$mD @@ $ 2<   1@F   406"@@b  h@@F@F@ `M@Hڈ@  HI  1d4  I# i#y@@@ 0b&@@@ i`$mD @@ $ 2<   1@F   406"@@b  h@@F@F@ `M@Hڈ@ T*DلYf2v z]J aW.n@2TܾRnD*}oC=c&eWLN?yVr L?2I> 4E@SB&PUB\p=7ӒH|o P?9hP&ˏD=h{(#J4ph?HwxNcGl"i/-]#3L%L#?Atz%Wiӝ;Lm]g$xy"TK_"Q=yhIH@tfzt#D/[F+^w1 ?|ǟ@F@F~bdi>H N2Kclc^ HXAҴg>jzօ8y?iwu8#LO :s_*N7 ]O@1:~?N;QGOپ^y"lN=V+ϼ1nA:I:Imw?Qsh8 f^sS);4L~Zu'( 컋S+  5CvYQ.:iS_$.:睐%63w~0xc8F@T ^P`  ¹Oy UγL;Y @ 10 ]Ec}ů、U8-88^{qEg@DWD_~rZOSn2g\LgC~a}  bH  rr7Ѫ\FMtsSޭFŗzk?Yo3BA>H  ~_ kTWDwotȣL_?|L/*hLHPb# 6h rK/s=}x.𣐡Ï΁@ HADWU<~_vÃs0ZA0pN@H`@Wd"gz(YMX腃c F/| j ftE!@ f0b源/<4=Of"yT,/>~;C򫁋_JAL0L@+{;)fk%(|hyA!v"v*K區s|otd(!Н_&E@㏣Zy?W^߳} &@[7 '=֓,Zf3# _*:C|eoǟBhj# 0R ! ʓ< w_e}~q40/!c,>"Oc;vҁm;h]еWІWy=Қ<* §?v_Ϯ#{Г|e0A`!K"Klp|_HyUTSl:@<>y&!KQ:>Fs_i.+ЌQ8HO|׿Y 3MÉ3l+tQ!t5M\D쁟kJ5b_>L*f@O[z4uBz?p7?^}g" *t$ J#]]tė왮3 K>Ȗ"=墿~:"VyONZ|!hTpTf#@[N\ }eGygsjYNd›i^L8U{o&S_rCv nh#z!N%H%6S4_@m <}u_¥d@:tJY*gM -ZƋKs$Evo?t[KKݻ@B' t>˩ <5"87Kx_:Th%o`=0,htxO?.;w/](~ۡ<~v]n,੆A&h3!/tY(FmRq6Z;9A˟OS۷:_s*䷅fli{7|M=xuJ N*]{ Φ_gO2/7і~T.T_L^y#[_G^4}p@{'J@ ^hvջ2i[ieLyV Y y_]F:ӟZő'0M0M<ـ=hOӻv8 e<Nx3#4v?9?ZT 􀇤 J *"|RUY)Uh^LCO|-54xHީj*~G2]G=ܯ*SJZtAO JuMcoz;p-~<`E+%^]!V4| D@L % OLСG^2::ouԳj :;V|}P*m0e/>LNRqd'+hߗC.#<h /j"S[:f~$p;L>d? ~$PF2g# 3cu5H\??rugbqf*< ;T|~xnSdRf " r0R>K l(l.K[m4vu[C igZ(hYܙ9pF\JFh򷿢E? {Fhz_sY kD(F"2# *?9HV;vmMnp ?;r{2yذz70+x'o)vl"9ކx7v櫓_g < Eዲ7f ~ UY-Y?ag UÍΦ(g+JHx&l%ݧ^O1tۅ !XH.    `!:   0p`X829  `ᠣ   XH.    `!:   0p`X829  豰r d2ciדJD-n$8TWsOcl7ۭL?}t?h6y@d`DGF(eT*5l6LeD)Jz使 f);:TE+b MׇgggtDO@s+|se;lE&u@09ѽ^ezM?4ezzy$1pcY9$ c5  Nbt?+"Vm;;x;ʿP}Nø@Ʈ!kI{;gmʰwƶIU69UaN@G'@ʇە)T&'ۭ%Cq69p#_jc!D@(p̏q[8uEXK{=69±A %K &Y( ;ѡ$Q¾uKK'N> VYh">xSBae]ATa#<̈́oK~2lsX "DA=  ?fE 9O#O4rg|R"q??r㷨4'0_6UV( @/}2<㭂&FTD{fc# Pf"BO/Pul~1?Hϩ0CYf|׽C lvT:8F}=0_jmsjMsawI`;o3¿0 l1D-QԞdH@6Bo-}+[~M=po`T$T fA xW} H|Ymʒ4с @D`Q-Oɴ'/g9O|x>.m!F?A$;  JsW%/>o q RIx%ek*p>siP6~b` 1CA@Nx;}'O=O1dcg{0` X}'_ס2 @rK+{Ö0Ug'̗Dq'mz7Ɵ[ߖFHY~Wec?,/_rlsF GL";C@(Io|Q EտXйSQP-/rݱ>r2%|,wDQ~/ӟZ;跿-=69=Ic$ Fh{8DA._ PηXi~/:ms$ Q-0`HO?Ի~C`*nρDO@}F"}M4~ǏշZZ, #A!o-&Yٯ.[,q;AC@3Ia,{k]- ̗DI&$d& 4/ a#ʒ4% &jǟDCg].鎅6"4N@㬐K@^̿/9g=i8t")`$e$ J_[—Rnx81 6_g}NA$QL ~KK?&>fp hZEz  @>¿mxCв]3xHb` 1CA@I{/,(o<+c>.AuhJ@g T*N^ Ksmc;{.&.\t "g=9.~o}MwgcG Ci ?_}?ajcf@ G A x}3_o-ozEwocSq2B4 @G[I@x[^)hߦOg^!;}%%ǕAۂ u?~&^e4x79 j %~E;.ezr69A% @$@:@wx4-<]|ad?XZ»Q Mgc :hJ@x&?S YO]oImls(P '>#Ep=OIu _κԁ\]I@W+:u:; ]ۗ>O 5@`4 Cw+/N%_D (t"6x"``"B4E E#5ӑy`쇷tǶ b A @$Tf_8^o{ݏGTd SS'xAI-%F$ "リq|%_Ƒݿ[L5+͚]p){mdfh NBF\0P2#"?9H[_fөc$Wx6@.cU+qXx.Ar,?kX;]q(.\t @ ,0"z@@@ E`h *E@X$Q  D$   aD=   "0R4X@@" ,@@RD@ @X`E@pB|  \" 0N%  )#`ղ K @@ )j]k]ۓ"wr7'O}N $i;)K y-H9  $r'ID`0J^ qsK@@ k^p x0 z?b@@:Mf,汆;-cb NI#B @ i{mﴬo@m'  5Z5/Yf,rհ$oEFGj\}'TӺk  0(oSEmތ88_}z,q|R^$:6秠l,T]aU/ezAiz @@ Ua/F(rU IM@ Dy% '*l=I8QzirⵜxqriN$LɵW*{=49ּt`08֦5] I3Zz @@65SXɢ'wŇ0 DN9I$KX~rӓKс$^8   rW_W/i6Yo];rB֓C%,?943ēn9688)8͟\{XϪ 89a=j` ^O,9 %]t: @@Z SjTҗx=O8*$E=UԿOi@@Z RJXI49‡rדFN*5$Nhc-k>  0=ZcgEUcW& EʬrK  n*V˥VC)ΧX@AD@>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`=uaO@IENDB`il32ʀ̂î΂ƿ@̌Ӂ ̋ւ ƺdžՊփ ǕՉւ ǝ ɞ ɿŽ Å ͅՀӀՀԳֲֶ׆ZՆ ؂؂vfihfgjmg043.-5 i100/.0*Ui210/.Qi2110/-1.ai2110.12.bi210/221/ai21//-+**ei2 ^--,)Xi24P0//.,`i2pll0102Mi2;75210i2@R_R21bi2A2112ji22ii22ii252ii252ii252ii24s82ii21y2ii2Q2ii232ii23G12ii2 ˜222il2 ΖN326222ll2ǀ2S@2_<2ll22o~2yr2ll2:FA23D=2lq2qm_cbc!%$) a! "Ua"!  Qe"! " Xe"!! !" \e"!! " \e"! ]e" QRe"$B Xe"eaa ! $Be"+'%""!e"1ESD"!\e"2"fe""ee""ee"&"ee"&"ee"&"ee"$i)"ee"!l"ee"D"ee"#"ee"#8!"ee" ǔ"""ed" ˎA#"~'"""dd"À"F1"Rz-"dd""ct"og"dd"*72"#5."de"eis32ʾ ɽ ׁɻׁĵׁ׀Ӂ׀ց׀فׁ׀ځׁ׀ׁ׀ׁ ׁׁ܀؏jUS__6323A^52103@`521036 `5223/20 `5223T4113n `522V224` `522C225^`522=C25``522CK25``522r25``522z25` _59O^725_ _55b6y;y235_ X2296U@T322X`HGHVT'$#$5T%"!$3T%"! $) T%""# ## T%""#G$"!$e T%""I""$S T%""}4""%TT%"".4"%TT%""5<"%TT%""d"%TT%""p~"%T T%)BQ("%T T%%V&o,o"#%T L"")&H1G#""Lit32!vɁƁ ÿʊɁɄȁŁ f͊ʁʁʄȀΕ˅ʁƀѕˀ̃Ȁ ¿ҙ́ӔЂσԔσ̀ ԖтЄµхф҅ЀρĶց ҂ӂҁπ¿ķ֖ӄрπķ+֜Ӂҁπ¿ķU֗ԆӀĸI֘ԀԂӀЀķU֝Ԅ¿ĸfՆՈՇԀÿĹ;֙ՇԁĹW֙ՇԁÿĹk֚ՇԀĿĺ{fփՅźqփՄŻIփՄĻ+փՄ ƀ żփՄ ȁ żփՄ ʂ ŽփՃ ̈żփՃ½ĻփՃփՁؿօ@ք3ւ3րŀ ǀ ˁȀ Uܓ̀+ς΀ˀ¿փЁ΀ˀG ՆՇԀрЃ ·ڄՆԂҁσĥ߄ՆԃҀρ ȦNՇԃӀҀЂՆԄӀҀςɤՇԃӀҁςS3քՈԃҁЀρIց׃րՈԃ҂рν<ހՈԄ҂рƣՈԄ҅ʣՈԃӀ̣ՈԄϣՈԊӣՉԆ֣Ռ֣ֈՎ֣݂։Ռ֣Շ֋Ո֣֋Մ֣֋Ղ֣֏֣֌֣ՀցՀ֣֣֣֣֣֣֣֣֣֣צ֣إ֣٤֣֣֣٢֣ץ֣SZZS֣ QcaacW֣f_d֣\`_֣ `Y]][\֣pcbl֣⮪֣ע֣֣֣ դ֣֣ק֣֣֣֣֣֣֣׀߀֣ރڂہف֣ۈ֣׍֣ܗ֣֣֣܈֣׊֣ݚ֣֣֣ف؂ف֣ؖـޕ֣֣܉؀֣֣֣֣֣֢ؕ##mU/. --,5BQ^]m/. --./..,+=X30/.-,+)M@0/.-,,*,'O10/0/.--,,+)(Q10/.--,,+)(N10/.--,,+)'R21010/.--,+))N210010/.-,+*(.I210/.--,+**0D21010/.--,+)/0@210/. --,*./1;+210/. --,+-//16+210/ ..--,-0//23I21210/ ..--,-10//1/9210/. --,100//1+ M21210/. --,1100//0+'M210/. -,21100//0+3M210/.--210//+AM210/.-2210/.,LM210/.-2100/.,M9210/..-2100/.,Q$210/.2100/.,U+210//.2100/.,U210/./2100/.,T210/2100/.,Q210/0210//.,O210/-,+*)('&'(*,00.,J210/..-,++* )((%#$(--F2?}ٹu, ++**)((&#$)C2;--,+**)('%%<2Y-, ++**)(&332d..-,++*)*.2\.-,,++*+12>/.-,++,A20/.--,+*Q@23N60/.--,*2ؘ810/.--/L2O>;9851010/..-+ 2021210/.0S23I23210/.24`Ӌ210/\;25k210//2P25o210//25o21052l210]$248:;;>t:;:621003\-2=߰^42103R2=212D22172r521026212m52122/21121122732231רU224221003l10232247p34543225o225o225o225o225o225o225o225o225o225o225o225o223j22{7227r@225A22622h22?3224g}t22\22  22!F22T22 22/)j224ZՊ522ނ6225V222242/62262f܇2422 522pڸ6262222222x2422@ j232<252238422Kc232B25223[a_H2L`_\i252Bb_\e35223Ő2xM2Yw2422328632E242>2622 3夨2326452sR23223252Ut232F2622 3⛟w232:262466223Í2qQ32Tz2422327542B252;26223242662mV23223242Qw242D26223242825223242kdBA@<2R:B@=223242A󶿀D2;]2322u232S f232D2522=R2A w2322:{>22263265262222mU  '8GTVI  !!1Q3! E@! G! ! I! ! G"! I"! ! D"!  ?"! !9"!  "4"!"!"!  "1+"!   "++"!  ! #%$"!!"!  !! "!9"! ! " M"! "! "'M"! "! !3M"! ""! ;M"! "! GM"! "! I9"! "! M$"! "! M"! "! N"! "! K"! "! J"! "!! E"! !!B"!  ;"/rޅֳk9",0"K("X#"O '". 8" J+"$?'!! ! "ӏ("! !B"A/,)(%! + " ~"#! "J"#:"#"! "$Rώ"! W'"%_"! #F"%c"! "%c"!/"a"!U$"$(+,,/i+,*'"!%S-".ܩQ%"!$F"."!"6"!"!)"f%"!"&"!""!""a%"!"" ""'w#""# ~ހӠG""$""!! #a! "$""$%$(e#$%$#""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""%c""#^""p(""(f1""%2""&""[""0$""$Zsh""M"" "" 6""D""""^""$Lҁz%""ۂ&""%H""""%" '""&"Z}"$"" &""dֱv&"&"""""""n"$""1 ^"#"-߈"%""#y($""=W"#"3"%""#zNUSR:" >TRSSO]"%" 3VRSSOY#%""#z"n?"Ll"$""#z"('#"6"$"/"&"" #y㝡v"#"&$%"iE"#""#y"%"Hi"#"8"&""#yl"#"+"&"$'&""#z"fC#"Gp"$""#z"(%$"4"%","'""#z"$"'&"bI"#""#z"$"Cm"$"5"'""#z"$"("%""#z"$"_X321-"D*41.""#}"$"2򱺀5",P"#""j"#"F Z"#"5"%"".xD"2~~| }l"#""*v~|~q/"""&#"&%"&""""l8mkj/>? ???'?2???K? ???'?<??????????????????????@@@@@@@@DDname icons8mkrt8mk@ӰGw D ~                 #  +  6  B  O ] l }           k    $    d     +  f                                                                                                                          &'(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((('&  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   fritzing-app-0.9.3b/resources/system_icons/macosx/mac_icon_template.xcf000066400000000000000000014322571272363601300265100ustar00rootroot00000000000000gimp xcf fileBB icc-profile H HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) gamma1G~\icon_512x512.png     #^$00<KpMyOZs\a3m]mmu{EXhx_w‡—Ю5Ueu p::7541//,,,*'' & $ $# !! y!"#$"&$&( (') * ( + , , ,. + ./-//c:{s:{j75{4{ZJ1c>nA/ci)T/mJ{,c1L O+Se|+^*8y'V\j'VZ&o<$o_V $oX[ #{`.Z !qmc !qvU |iq/-o|owzohLV_XVTYR8(aZcc!T]Y!3^"I7j"cu#dm&f|% {=f%mM{&' \ K( Rw( ( ~>* * *ozZ+ o+ +_-tX,--O:lg:lZ7w5l{8lGy51O#\,~ܿ/OVA/[q5l,O5v9+y=Rl+yI*twh'>GW'>Dz&]"$]J@ $]BF #lME !`\O{ !af@ mX|yay}qwz|yzy}pwz]tmy]gjp]V:y>JBs>>D= ME|yOvOt!>IC!Ix"1W"Ouc#Q\&Sm% l"S%Z6j&' J6( xv?h( ( yp'* y* *]viE+ u{]u+ z+Jo-ycCu,y--: 97 f4 5w2 3e00v./ - k|,A+[)Z'@ ' 3 & ' $$# !!    $1 <  X!U"=#j#%%%+&( y( \( )  ) /* + y+ [+ ,  ,+-.. k. " ,)189z c]f 7Q`_^[Y{o[j(B]v}o[jQ]xSkFd믨!w3j|w Z Mױ#et{-z16;: OHRo ? Yy   ٘  xdV fV daV]QȖIr[ثdgVqVݷϾ`䰃{ | ҧ ~"T j o5 Œ yyHyƮOciŊӧҦТѣѣѣѣѣѣѤҥ͛t[i+ Ϧ nP L ~ ߉3\ ܽV ^twVd g ݖ)VVugyyN> Q> zPK>sF9yyË1_Fyyǹ~tաtMU>`x>خʸJrlym Οr o@ Yy ^ { ۾gh1j8OW}Ν͜˘̙̙̙̙̙̙͛̚ǐxeFX ˞ ]<q 1 m {F ٶ{> Hbe>myNRڇ >> k ?$  g  ̜r1Eb A    /  y c 0zr_9w9  9  2   jpnmO01111202223414444463355555587744777777777777::::::::::::::::::::x/Su.o}..1c0sV00o002e?1rJf1s11{1{14t5yR3k3333VV3VV357HH4nn&Z4{{To4wwOk4m44444444444768::::::::::::::::::f/t=d.]m..1Ox0bAs/y0]002Q'1a4S1d|11l1l14d5j>3u[3q333>>3>>357--4[[ E4llA_4hh(ӧoimlll̗eU>]]C0]]1+]]؝z> ## ܫ6$ d]bF'?+' 憐{>& zCdyy' Fyy' * il) J,WVl) &DD* , , OO* 憎, f,yKh,q4\, V, ƙ- ],],--Ƚ-ׯ-˘-˗-̛-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-i&40ԃmK3.--,/2| R"y6i !R"n$p &2&K (' ( } ) v) N)  * &* '* w+ w, w, , , {, , u,P, -%-"-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-u71/,޼77/ >27պ-¤rR,ǺX&-îh=7˫z_R7ӫz_R,ͼjO&VV >17Ѳ/ּa<-z海D ,ʽyW$7ƢjJ;7УjJ;,ȵsW5>> ;7365s3%/+3%2c775 z  ͩKVˎ|Vۿɼ_V{Xث~btنM yOo ~n{xu{ iVcccȡ:>ŀl>طĵK>lDpԡpnQqcx6i8] o^l{hgl W@OOOω7 m ' z9  Y51   !Y''%..-3323657869:::::<99<<<<<<<<<<<<<<<<<99;YV>]vV4_5V4_1Ps VIUxmk| VI{-o c)uű c{]c7rboSV3"{ZS){]2\)oec|1czEh3ce3lP4E5YE5uaq668:7888Gf>Ir5>Ir1yqz;d >.?hVWm >.k\ O d OkoIwOaP]=@t"lE>{(lIG)]RP$a+>O:V/>G8h/+%m1Ozi-U3ON3Y;4-5C-5dO`66o8!{7r8889;\U4&Zi`abb . W 'hcw   ' o& * R* @--0 H0B3$4 ^4>5667"7 [7L899n999::::::::::::::::::::*9::::::::::::=:: <::::::::::::=:: <::::::::::::=:: <!5!5!5!5!5!5!5!5!5!5!5!5!5 %55 5bfPv;=7 9 - - - - - - - - - - - - - ( . 0 )99 @ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ҥ%Т.̘1&6* o ̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-͜%˘u.Ǎ1&6* n !-!-!-!-!-!-!-!-!-!-!-!-!-% .. 0vPfb.b =>. @;4.*''$ " " ! """"""""@ib]cc;~t`Ucc4Ϡt`Ucc.dH3d%V*󼵪oj}M#obJc'溑cGjPc'wdV$pT?=I"ڣY#pZ"ɝy! t; l֨Qoo̳oo̘lD;N<T%Ȍdwe﷢ʀ\ɚ{˦pa|ȳ""""" " DZz}Pm@vWNHOO;ncL?OO4˖cL?OO.R2O>*󶭡|^Wwo7[M5O'䳆P/W:O'ԹhwO>$뻏\:(!/yy"יD]Dyy"Ñh! ߣb ZѝC]]Ǭ}x]]ǎZ,#8$>~OgSxttrJÏ{lƾƜ_~Onzì""""" " kl:Z@><  1ba_oR--  2Ͻީ *wS^&(- *T % J$/#" !   *` * y{}::8:9967576243.0/())   2; 88m8>866Rs65}4Y<}33w1{0Im3-*V`}-Va{%, ^sZ%XjfƓ VbKw V2_ZUf V2^Blÿo[jDfegzcZh2cZh;{|{{~ 88]8'~766{?d65n4F%n33hv1ly0/Y0-*>sLm->xMj%q yIa{Dy%yAVT >M5hͻ >IA?R{ >H*Z]FW/TTiODU}2ODU};lmlln k999K87 76~6 6M45(3O1!|2 ,/8,/5l( H$ ~) F3` Ģ|NJDFFL>5 G6>  88:;9::::::::::::::::::::::::::::::::::::?7966:79::::::::::::::::::::::::::::::::::::@7:66:79::::::::::::::::::::::::::::::::::::@7655 (5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5;* ' . 0 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .K.ý&㻠&{fe`ѣ.Ϡ|0ҥ&}{yѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ- -J.ܽ'ഖ&mUTN̙.ʖn0͛&nli̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-M-I<*.0( ,!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-@-d  ! $$%((.31 :w\ncS0bb NNt>accqAccӞecKcNaIibdHoov@V! ŀ[<w$Ƨe*j${Hu[Dcc%ȼiO0ҽ]Lcc(طd;]Lcc(~.νlaoo3ҽlaoo1`7V :eGx]O=MM7ob%MOO`(OOϖpRO2Oޫ5M3VNܢO1#'>⪍=(]] ݖr{.$]]ɻf'o>! qE"f$RX$j0dF,OO%öqV8ͶrH4OO(հQ"rH4OO1my.ɷsYM]]3ϷsYM]]1J> :J, ,| }f {7S i7 D!z# h"% '4;U:'G (-cCDIFDP;,I 286 =::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-=:R685220//-++*(( ' (w ( %$$"$$!!o=o:Vh6Td8[q5V 2o4_x2oB:y0nw/K~/Kw-V*V+V*V0(V&#G(V.@ 'V7(A ("8 %H $& $"o~$j$y!F?Jo8xo!uzC_gC\V٣bvV]fTV tf(8]=]:>T6yq2]Ke2]) h0\gt/{4k/p4f->zx*>+>*>(>,(>% '>-& ( %- $ $"]or$X$sj!.'3]|uh]!em+KT,G>v~ҘNwg>FR=~>`Q y> 9;9 >5,`53 a2 F2 r0z/]/q-;,B+B*C )D )B ) A '1 ' t%x$[$o#}" " !0 " Su uK9y:::::::::::::::::::::9::889;z}:::::::::::::::::::::{8B7R76Rה6Pu978>;>:::::::::::::::::::::l8)7;7q6=ӈ69d978>;>!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5!5(5 5 5768+7u79;<~ - - - - - - - - - - - - - - - - - - - - - , * * ' $ ڭ$ #   #!""%$&(% ')* ( + , ,ϝ-ٷ+ .//ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ѣ-ҤV,Ϣ*У*ެAhN{{'џ|V% ȡoVV$ TT# pV ԗ#~jV }@L>cc䀘Moؤ]Qo|<oiuRwyHt͜w۟wܧs؟|o woܧuf_ccٝfPV!{G_V Ȃ=<'"m7"o:#V$ڵ&WF%wjzi%[ &2|'̼)bNh (ƐfVV (VV )z| *V-MKV+~~y_ +۶~ .zM-k̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-̙-͚>,ʘ*˙*ۣ'W9ll'̒sm>% –_y>% A=y# vs]> ЋnW> ݷm'4$OO6]՛G:]k#]Vc<hh1cƐh{xהqwg؞d{xӔrl]gu]؝fSKOOԒS:>j0J> s$" "Zyy"[yy#vo>$֮A-%fXiU%Cyy &k'ǵ*N7U (R>> (>> )lk *>-31>+nrgI +׮m .g3,Zyy!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-$- - -P* d,) ( & Z $ B$ɱ:!#  w Â]q y]p x]p w \!p"{#1#v$O %%&N '4(t(} )  )  *= + +? + ,. ,u-e !!!!"  """"#$!!!$$$$$$$$$$$$$$$$!!!$#""""  #!!!KocdJok\lIoxcJV{zUnVP wN w   { { "|!|On!wSl!Yu!}!!!!!!!!!!!!!!!h!Yu!vNj!e9W!^7!" c   p xQ p {{xTm^ LQcvca!]NO2]ZuwGZ2n]iO3>kj@]>7o e6|g   l l "m!m;^!h?\!sGf!p!!!!!!!!!!!!!!}!}Uq!rEd!f:X!Q B!I!" O   z` i>~` llh?[I5:OgOKssyu# Qurwz&#+s_ !!~!!! m!!#%" "!"!"!"!"!"!"!"!"!"!"!"!"!"!"!"!" "#"#" W! q!{!!!|!!e 7 ' & xx0=" 2, $"ripȫ}rkd\SNR_feo~ԯ\QTY[]^`cdc`^^\XWLT̔nda]\\]^^_^][\aaj١eTU\abaa``_`ab\UTbvՒ[V^cba__`bcaSXtX_]^__]\alܪyUUba`__b`[Wg cV`d__adZTkV_"_^YoZ]b`_ _`cT_ {U]c_$_b_e X__`_$_a\Z dUc`_%_`^_] m_``_'_`cR VZb_)_`\ |X\a_*_c aa^`_*_bqQb_,_agSb_-_אe[`_-_v_^`_-_][a_._RYc_._UZb_._ߓX[b_._ӋX[a_._ƂZ\a_._ŁZ\a_._āZ\a_._ÀZ\a_._ڏX[b_._VZb_._VZb_._OXc_._YZb_._̀a\`_-_iYa_-_iQb`_,_ oMc`_+_` `b_,_c ~V[b_*_b QZc_)_`\ w^]`_'_`dQ kUb_&_`^]\ ^b_&_c\U тR\d_"_`b\Yq pZ\`_ _`_^Xo oUd`__b_[mvSZca__ad\Wms_[\_`__`]ZZr҄\\`c`__`ac[SvէzSO]bca`_ _`abc^VUjgdYX\^_``__`_^\ZZ]j{羇[R]_abbaab`[YYsӱiTLPUVWWTQNQd~Ƞwrnkiijijosv$Ӽ&& !1* $"լ|iS6%3Yok鵉oZF7,"%$2GUbÑO  Lj1(\|L# ;mf U7 .uЊ@ &d w!  ^+ S 0y C"% O$M ! % 0( t ) E * c*3+#+f!-?-.}.o.e .Z .N.M.L.K._ .q .u .| ..N-ـ& -&+ .+`* I * s) >& +% [$X O!5 4 1 3 /> /85T<ƄB  ,lF#   (DܥX<ŽoT+   #Hf}P93/*''('(/48Is$£&& !0* #"ҥq\A!Gc_|}`J4# 6ERs߽=;zZNn<  (`VF#h-R j  K>  l 0# :$= y % ( g) 4*U*+ +Y -.--q.a.W.J.>.<.<.;.P.d.h.o..>-v-+ +Q* 7* f) ,' % L$G <""  z(}!!{D)y0 ^4  2{ٝH'aD 7X}q?& $3c$Ὓ}|}}y&% //1.1122224٥1220/20e,^y.\Z.[W-]\s-_bX-`cMm-_bSj,_aXh._aYe._`\a._``]i._bZX._cYR._cYR._cYQ._cYQ._a[\._`_]k._`]`z._aZd._aVh,_bSm+_cOr-_aY,]w-ZR-Z.c.h,./002.13 220211..-¯-.h2V.{.ݾ130gVV0uVV001QQ1e:vv/q/ө-˵{,Ժ/X.7w ,e~D,y, b+9+P*+)'+ &, l.N.,.......0.E, f. %+,)5+W*>+ʵc+,W,t'+So`-v3`/X--˱c/֬c.zc1Y/jj3]]00oo0too0(}1ͮ.ݽ.ta~|.K\\--.Uq|>.zl}r.ڷ1u0uS>>0f>>0ﱛ0181P cc/y`/Ϡ-Ǯl,ϲ/I.!h ,Tp3,j{,}S+'+@*)+x, `.?......~...4, Z.+)+C*,*ĮT+~,暴G,c+n=\M-eL/C--ƩO/ѤO.jQuu1DWW2HH00]]0c~]]0 m1ǣ.ڸ.dOoly.3GG.~ .z.|/!/0H0;1|111>12#2!2'223>444|44444444444444444444444444|43e272 2'2 2&111k 110+/ //+.!!!!"%%$ %% ' ( (*++-../103556:9<y}]uVvpP{:j:j+WyZH uvgfIo!^O!QVnx!{!{yN"Z%R`Bb%YX $ciB %{|~ %{ ' ((*4++q-h.nqJ-ni@ĸ/{R0c/KJ{0cNl3~c5{Qe5{;R6Vz:{Zo9{F`<inLe>h`<yly$WxVAiEyy1dfSR0]w!K;t!:{>]g!l!lh7o"F%=M9P%DCv $OX-z %lmqo %lwq ' ((*+q+a-rW.\`2-]W(/l=0O45l0O7W3pzQ5l;T5l >6>k:lG_9l0N<! \ y0 ^ y.b!!d!>"Z$x$$-%&'  ( ( *  + + - ..0 11>2\3_5)i6 E8 8 :=b<:523-!'  ޴ ?:793p/-g'vQzNlz&UyyѨ{dyI{̶оĉcR_zTu˳[:cR_]ķh>cXhIUc­ia? ?:753`/r-Vz'g~=k8[k Agh̞lRjr3lƭ˶|O. n~$  +  1yrru ee  $" !'@j'#! "$  k|}X /n/-.. .,* , + ( * ) )( % $&%$#"s!؏ q ! # #&'(*--1759-KK--^cc.rcc+Ա +g,xt) *Nj (՜T (sv 'K4UC{ '>.{ 'Hi%۸cc %a-qxcc #ć65"ag"ܵ_!ѕwscc!οފ+ݠ[$ܙa)@VVZTVVՀW X^VVݗMXoݞXQoo뻄9OUooҼ:cۡwVVv'VVSyV ܹTn ߕ~h ĩj^N{{!Ԯu nV#ٓnv~oV#滨s8O{&|Ko5VV'㽙{z'aV(վ_r[Dcc*ᬃez]Hcc-nh]Fcc-cEVV1~7ƍ~5x9-33-죏y-JxOO.`OO+Ϩ +pVy,iey)句 *v:Y (ђA (q_pf '3>)l '%l '7V%װzOO %M`hOO #w"~LS"ٮJ!̌gbOO!ɷyzryݎ ٔ}F׍N'>@D>>>pBBJ>>ي6C]ّB;]]t8?]]͵Oחi>rg>>ظu@h> ز<\ ܉pV ԽYJ7ll!Ϧd[>#ֆ\fn]>#䲜b8l&m4]>>'ඍhl L>(еKaF+OO*ߣtQjH/OO-\TI.OO-Ow)>>1my7my5tfyy9.. -k,8,  +! + + *l )9 )  (  ( '& &%\#Y#@"X !e  *40& 벍' 1 4d*\A$ˣe !e # # *$4 '@'~m) +i%,u~[ -e0?($ 2k 5 :9<    ~~~~E|">FZ      pgimp-text-layerT(markup "FZ") (font "OCR A Std") (font-size 170.000000) (font-size-unit pixels) (antialias yes) (language "en") (base-direction ltr) (color (color-rgb 1.000000 1.000000 1.000000)) (justify center) (box-mode fixed) (box-width 722.000000) (box-height 171.000000) (box-unit pixels) (hinting yes) GNGZGfGr6")L----... .0.@8H9BD2DBDRDbDrDDDDEFAFaGGG.G>A;;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;8,,*A;;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;8,,*A;;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;8,,*A;;;;;;;;;;;;;;;;;4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,'ެ;;;;;;;;;;;;;;;84,4,4*? +(&%$$#####$$%&( <;:99888888899:;? +(&%$$#####$$%&( <;:99888888899:;? +(&%$$#####$$%&( <;:99888888899:;? ՜B+%()&%|g$$]##*###$]h$%~*&'( ؟Fc <R;i:2998S8m88n8T88969n:X;i ?:~=:76655433 2 2 1 1 0 / / . .-,,++))(((()(((()()(()()(((() ?:~=:76655433 2 2 1 1 0 / / . .-,,++))(((()(((()()(()()(((() ?:~=:76655433 2 2 1 1 0 / / . .-,,++))(((()(((()()(()()(((() ?9~=!97<66f554+33Q 2 2| 1 1 0= / /g . .-+,,R+ +})~) (=Q((g*()(,a((R7( )}(r)(>F()h"() (-V((S-( )~ ?------------. . / 0 0 1 1 2 334455677889::;;<=>?------------. . / 0 0 1 1 2 334455677889::;;<=>?------------. . / 0 0 1 1 2 334455677889::;;<=>?0-0-0-0-0-0-0-0-0-0-0- -X. . // 0 0 h1 1 =2 33y44L55&67 7]8839::m;;A<=>,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,$,,x + =<<;::;;;;;; =<<;::;;;;;; =<<;::;;;;;; =k<<;.::V;;;;;;)(()()(()()()(()()(()()()(()()(()()())()()(()()()))*,)(()()(()()()(()()(()()()(()()(()()())()()(()()()))*,)(()()(()()()(()()(()()()(()()(()()())()()(()()()))*,f)(>;()i(w)(-K()S%( )~ ([)(?1()i(k)(-@()T( |) (O)(?)()j(`)(.6()U( p)(D))@!()j (T)(.,()U( e)(:))@)u** 1 0 / ...-----. 1 0 / ...-----. 1 0 / ...-----. ҙ?1 $0 (/ .f..--+---.                 . . 02  . . 02  . . 02 / . ).! u0&i2Nt ;;;;; ;;;;; ;;;;; ;;;;; @ @ @ @ . . / 0 . . / 0 . . / 0 i. . +/ '0؟F             iU*Z- Layer     H,HP\\\h\t\L\LlL|LLLLLLLLM MM,M<MLM\MlM|MMMMMMMMN NN,N<NLN\NlN|NNNNNNNNO OO,O<OLO\OlO|OOOOOOOOP PP,P<PLP\PlP|PPPPPPPPQ QQ,Q<QLQ\QlQ|QQQQQQQQR RR,R<RLR\RlR|RRRRRRRRS SS,S<SLS\SlS|SSSSSSSST TT,T<TLT\TlT|TTTTTTTTU UU,U<ULU\UlU|UUUUUUUUV VV,V<VLV\VlV|VVVVVVVVW WW,W<WLW\WlW|WWWWWWWWX XX,X<XLX\XlX|XXXXXXXXY YY,Y<YLY\YlY|YYYYYYYYZ ZZ,Z<ZLZ\ZlZ|ZZZZZZZZ[ [[,[<[L[\[l[|[[[[[[[[\ \\,\<\L2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"@@icon_512x512@2x.png     ]P]t4444aaajVlox65g 0u8bhAhQhahqhhhhw}9=д`'7GWgw$%%%`5>Tn*dn~>N^nv(TQrXXXXY bbbbblya߄ߔߤߴDTdt ,5EEFFF.OOOOOOPTat)~fvņŖŦŶFVfvφϖϦ϶fvنٖ٦&6FVfvFVfv&6FVfv&6FVf     & 6 F V f v &6F +;K[k(()))/-8--.(.x.//h/00X4_4o''''$'''''' ''''''''''''''''''''''''''''''''''''$'''''' ''''''''''''''''''''''''''''''''''''$'''''' ''''''''''''''''''''''''''''''''"&    %        +,//023 & &&& !& "& #& %& && '& (& +& +& +& .& /& /& 0&  2& !3& "3& #4& #5& $7& $7& %7& %7& &7& &8& &8& &8& '8& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& +   +   +  ????????3          ????????3 |, "+(+- z $ %! # #5<;%    = |, "+(+- z $ %! # #5<;%    = |, "+(+- z $ %! # #5<;%    = ????????3       "       ' # ,                     "       ' # ,                     "       ' # ,               :????????3 ľc7öm(ʿm ųc 5                                ľc7öm(ʿm ųc 5                                ľc7öm(ʿm ųc 5                                +"-5 *            310/..-++**('%%    wi[K<2/-+)&$  Ÿ|fR:1-*&# *ɶlR6.)% 1ɫoM1+%8Эb?= @ʸN9į{ 4ȳ0ĤU-͸)Ӿ+&#Ϻ!ʯWϾƢ+ȪCɰZȷfųZƪFß Ŀ ŶW ß ķi›òMļ4d¼½Ŀ@ʸN9į{ 4ȳ0ĤU-͸)Ӿ+&#Ϻ!ʯWϾƢ+ȪCɰZȷfųZƪFß Ŀ ŶW ß ķi›òMļ4d¼½Ŀ@ʸN9į{ 4ȳ0ĤU-͸)Ӿ+&#Ϻ!ʯWϾƢ+ȪCɰZȷfųZƪFß Ŀ ŶW ß ķi›òMļ4d¼½Ŀ=8 3 / +  '  $  !     .)" rE.&  仏]4)   Ǘ]2&  ﹄H+$ ܦl7& y;( w9% h0# L) o0  !;# #E% %O) 'U( )O% +E# -7 .r+ 0O$ 21 3Z% 40 6I  7y) 9; :R ;p&< @+<X;Ŀr:ÿ9ĿŘ8ſǣ7ſɫ6ſ̰5ſѶ4ÿջ320/ſ$.ÿ+- @+<X;Ŀr:ÿ9ĿŘ8ſǣ7ſɫ6ſ̰5ſѶ4ÿջ320/ſ$.ÿ+- @+<X;Ŀr:ÿ9ĿŘ8ſǣ7ſɫ6ſ̰5ſѶ4ÿջ320/ſ$.ÿ+-=<;98654210/-, + * ) (* &; %K $\  #k" "w% !&  ' ) *  +  0  2  7  9 < '  '''' '''' ''''''''''''' '''''''''''''''''''''''''%'''''''  '''' '''' ''''''''''''' '''''''''''''''''''''''''%'''''''  '''' '''' ''''''''''''' '''''''''''''''''''''''''%'''''' '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& '9& ':& ':& ':& ':& ':&?    R`?    R`?    R`?    D $W  ?    D $W  ?    D $W     !     (        !  # $ ! !(*    ###! #   !     (        !  # $ ! !(*    ###! #   !     (        !  # $ ! !(*    ###! #  $"#*                ## '%(* %  *$' ( % * )          $"#*                ## '%(* %  *$' ( % * )          $"#*                ## '%(* %  *$' ( % * )                                                                                                                                                                                                                                                                    <,ƿF+ĿT*Y)g(þo'w&ſ؁%ÿۈ$ڑ#žږ"ڞ!ƿܨ ݬſݱ޷ſݽſ ,5=HɿUbkp}ׄڌ ڕ ؛ ڢ ۦ ۯ۳۸۽.:E<,ƿF+ĿT*Y)g(þo'w&ſ؁%ÿۈ$ڑ#žږ"ڞ!ƿܨ ݬſݱ޷ſݽſ ,5=HɿUbkp}ׄڌ ڕ ؛ ڢ ۦ ۯ۳۸۽.:E<,ƿF+ĿT*Y)g(þo'w&ſ؁%ÿۈ$ڑ#žږ"ڞ!ƿܨ ݬſݱ޷ſݽſ ,5=HɿUbkp}ׄڌ ڕ ؛ ڢ ۦ ۯ۳۸۽.:E@ E I M R Z  ^  e# k#  t%  z&  &  '  ) * +  0 !2 "4 #8 $: %? &C 'G (K )Q *V  *[! ,b" ,h# -q% .v& /}& 0& 1) 2* 4+ 5- 60 72 88 9::=;B6=:#(&#, )+/C'   ,-0-.-517% 303$ 4<174=    >6=:#(&#, )+/C'   ,-0-.-517% 303$ 4<174=    >6=:  !$$ +%=<7|=6/:/7:5"      $ -$!/ "*-  !$$ +%=<7|=6/:/7:5"      $ -$!/ "*-  !$$ +%=<7|=6/:/7:5"      $ -$!/ "*-                                              ,,1*/,1.03-                                              ,,1*/,1.03-                                              ,,1*/,1.03-                                                                                                                                                                                                F)M(\'c&m%|$ր#։"֏!ՙ נ֦خزظػ (4=JW_ju{ԇ ֍ Ք ֜ ֣ תֳֹֿ֯ -:H  F)M(\'c&m%|$ր#։"֏!ՙ נ֦خزظػ (4=JW_ju{ԇ ֍ Ք ֜ ֣ תֳֹֿ֯ -:H  F)M(\'c&m%|$ր#։"֏!ՙ נ֦خزظػ (4=JW_ju{ԇ ֍ Ք ֜ ֣ תֳֹֿ֯ -:H  E I N U [! a!  h#  n%  v&  }&  & ) *  , !0 "2 #3 $8 %: %@ 'C (H )L *S *Y! ,^! ,f# -l# .t% /z& 0& 1) 2* 4, 5- 60 73 88 9;:>;B B G K R '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':&9C 9C 9C ($'#," ' ! )$ )& & ( %&# !        &*&'$($'#," ' ! )$ )& & ( %&# !        &*&'$($'#," ' ! )$ )& & ( %&# !        &*&'$226210675 +04,.47246-2 / . - (-* .0) ,) % &$$!)* $ &#!!  %$&%226210675 +04,.47246-2 / . - (-* .0) ,) % &$$!)* $ &#!!  %$&%226210675 +04,.47246-2 / . - (-* .0) ,) % &$$!)* $ &#!!  %$&%                                                                                                                                                                                                                                                          滼罻                              滼罻                              滼罻  e&r%w$ρ#ъ"А!ј ѡӧӪӰӶӸ ӽ-:GQZerẃψ ϐ ϖ П У Ъѯеи ѻп)6CQӸ־ﳱe&r%w$ρ#ъ"А!ј ѡӧӪӰӶӸ ӽ-:GQZerẃψ ϐ ϖ П У Ъѯеи ѻп)6CQӸ־ﳱe&r%w$ρ#ъ"А!ј ѡӧӪӰӶӸ ӽ-:GQZerẃψ ϐ ϖ П У Ъѯеи ѻп)6CQӸ־ﳱX! ^! e#  k#  t%  z&  '  ) + ,  - !1 "3 #8 $; %> &B 'H (L )R *X! +^! ,e$ ,k$ -t% .z' /' 0) 1+ 3, 4- 51 63 79 8; 9>:B;H B H L R Y! ^! e$ k$  '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ':& ';& ';& ';& ';& ';& ';& ';& ';& ';&$(*)-1.** 30" //351+5799::z48:7C;:@$(*)-1.** 30" //351+5799::z48:7C;:@$(*)-1.** 30" //351+5799::z48:7C;:@%(+%%  (&% ) ..-,.*.,& "(*&,)*.(+)*-( %%++"(,(!$%" ( "  %(+%%  (&% ) ..-,.*.,& "(*&,)*.(+)*-( %%++"(,(!$%" ( "  %(+%%  (&% ) ..-,.*.,& "(*&,)*.(+)*-( %%++"(,(!$%" ( "            ! " #  $  $ % & (()* + +* , . , . ,/ 0 / /,.301,-12. * - ) # * )'(*"'&$%          ! " #  $  $ % & (()* + +* , . , . ,/ 0 / /,.301,-12. * - ) # * )'(*"'&$%          ! " #  $  $ % & (()* + +* , . , . ,/ 0 / /,.301,-12. * - ) # * )'(*"'&$%                                            '                                            '                                            '                                                                                                                                                                                                                                                                                                                                                                                                                              ˆ#̌"̔!ÿ͜ ͧͭ͢βε θÿ󼸷λν)¿̾=CÿQWdmrxÿ{{ ~ v p h TB¿& ¹j¸7ů¡Xȼ   ˆ#̌"̔!ÿ͜ ͧͭ͢βε θÿ󼸷λν)¿̾=CÿQWdmrxÿ{{ ~ v p h TB¿& ¹j¸7ů¡Xȼ   ˆ#̌"̔!ÿ͜ ͧͭ͢βε θÿ󼸷λν)¿̾=CÿQWdmrxÿ{{ ~ v p h TB¿& ¹j¸7ů¡Xȼ   t%  z'  '  )  + , -  1 !3 "9 #; $> %C %H 'L (R )Y! *^! *e! ,h$ ,j$ -l$ .m# /h! 0`! 1[ 2O 3I 4= 54 6* 7' 7w$ 8^ 9E :2;*;}$~@z=L. /79307*     ")* + / - 02//:878>~@z=L. /79307*     ")* + / - 02//:878>~@z=L. /79307* *%%""(,.-'+ '               $( #   &(* *%%""(,.-'+ '               $( #   &(* *%%""(,.-'+ '               $( #   &(*    n      h      (r      5m             n      h      (r      5m             n      h      (r      5m         "- ". "2  ":" #V$ #x( #+ #. #0  #3! #9" $Q% $n' $* $- $.  $1! $3! $8" %@# %U% %i' %) %, %-  %/  %1" %3" %4" %7# %9$ &C$ &T& &b( &n) &{*  &,  &.  %.! %/! %0! %2" %2# %3# %3# %3# %6# %6$ %6$ %8$ %8$ %8% %9% %:% &:% &:& &:& ':& ':& ':& ':' ';' ';' ';' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& ';& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& ")$)% ./ *0 4353q 997=  ")$)% ./ *0 4353q 997=  ")$)% ./ *0 4353q 997= .88/35 +4-,+0**& '        "&#"" #  /059/Ex<=.88/35 +4-,+0**& '        "&#"" #  /059/Ex<=.88/35 +4-,+0**& '        "&#"" #  /059/Ex<=,''! ( ! # %,1!G7:B=-I 57 +=1;41 !%       ,''! ( ! # %,1!G7:B=-I 57 +=1;41 !%       ,''! ( ! # %,1!G7:B=-I 57 +=1;41 !%       '1( 0C=:B@5>533-%      ' ,/* .# (&  $#  '1( 0C=:B@5>533-%      ' ,/* .# (&  $#  '1( 0C=:B@5>533-%      ' ,/* .# (&  $#     #''''''''''&&''''&&&'&     && &&&&$&#      #''''''''''&&''''&&&'&     && &&&&$&#      #''''''''''&&''''&&&'&     && &&&&$&#   ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' ';' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<&{{{(&%() +D,z ? I(&%() +D,z ? I(&%() +D,z ? I     %0 1 !#N-s> L     %0 1 !#N-s> L     %0 1 !#N-s> L  "  ' '''''''''''''''''''''''''''''''''''''''''''''''''  "  ' '''''''''''''''''''''''''''''''''''''''''''''''''  "  ' ''''''''''''''''''''''''''''''''''''''''''''''''''<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '<& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '<' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '=& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '=' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '>& '=& &=& &=& &=& &<& %<& %<& $<& $<& #:& "8& !4&'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '>' '=' '=& '=& '=& '<& '<% '<% '<$ '<$ ':# '8" &4!  )5:>@BEGHJKLMOPQQRSSTTU UV #&*,.024679:<=>>?@@AA BC !#%(*+-/1235677899::;  "$&(*+-/012345667788 9  "$&(*+-./012334556 !"$&()*+-.//011223 4 !"$%'()*+,-..//01  "#%&'()**++,,--.  !"#$%&'(())*+  !"#$$%%&&''(  !""##$%  !!"                        %   &"#?V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ???????V?C?;?9?6?4?1?.?+?(?%?"???????? ? ? ??????V UTSRQQPOMLKJHGEB@>:5) CC BA@?>>=<:976420.,*&# ;;:987765321/-+*(%#! 9 9876543210/-+*(&$"  665433210/.-+*(&$"  4 43210//.-+*)(&$"! 10/.-,+*)('%$"! .-,+*)('&%#"  +*)('&%$#"!  ('&%$#"!  %$#"!  "!                             %    &"@@fritzing-app-0.9.3b/resources/system_icons/win/000077500000000000000000000000001272363601300216305ustar00rootroot00000000000000fritzing-app-0.9.3b/resources/system_icons/win/fritzing_icon.ico000066400000000000000000000442441272363601300252000ustar00rootroot00000000000000f@@(00$% 3t<hXfF6: d g9liVЛ'*[+ei%>!W8`6RM_7屇[[8y9/E5V)YNΠ}o~O;8Xsr{z7oR@3ky=PCsN`<ҿͫSvVN`>9SV˔ ˕t0@@)h_l}Țq믂wvG;;n9κ T+"KU@ZnNQU9.7xF]}g|{wJ'0knzߵP_ ˔3<~-/`W^57H%?ѧU,H+SFQ٪ ?f/.U'Vb3(+WҧH_}>l7Sx5 VKg0 ̹!=i,6>^q9]1Ѵ4Ub`ە9RAҋ_#GY9C Śåba|YhV*@ 752] t 1 c a\ ŷ}Eڼ ooU4f ͞j~i=!մ~-s i3 f7 Q}A4}k 53-rA[djM?f[OE#O,(g4@Yǚ#g=@Q^!k̡e/*NkvPwyWᐭw91%l_{w~q][+ȝSwg`PO3 %Upb2         @$áw&1.)έʁQƐ'LVr-@ ƙUtcG_L^ E9=V<+/~YX@;2 Ӏ%)wMf ѲR+[A٧cTD +TI}sUkB͚USW'#Ǩ?,H%?X&G3T0k> F`k2[xCr #?/_EN}8*yG,@z}y)߮sݗY@)}Y$eptg)΁4Lq6F3oFQ<ӹ:eXm*,a&Os7wXP|ƽwvjo5?ՙ>~n_}]1}q1aljnbQy X^{a:s]?^>tKRծZ2&= :{rO%ߢ Wڿ҃7LP1?mޤGnfXrV\b[m^-sZ^=oV.4CcYTXeav~/俐^;~X z^TQhΟ֮{;~1G bc/6>x}ܥ=:]}Āc*ea ba3v0lvne@~ K 6jO?gOpL Q82l[QK})2l$B,,@L--qoQ?>~%El}۸[ Oa^~eP"X[6 pCw2V KCl~"3t`r=1vvƽõpYp<=rŽU+>c?kȶm=y/}R=[p(Ck;Jp1aۺnꝽ}u_lvn@1wdASl 5h6Y gGڹhj\MФަbc?Q}Dշsw>A 56stC[1b*F8yKtERzVw,$@Wo+bԽĄa2)I0 jް$G: )uOH$*Sꣴo.!*H} ֞Йa~)pzb)oԢyY0/5կx")-hשEw#ې7SǓJ Uwɿ DNWE=/uut;3a f}fB ٸ*ǩ׼ja̟Gղeׯ~^X33eF D>v"~C%HE.2Rar H֜\espHaO?c R* @@@@@@`[gLWK@@@@@@@tGW?IENDB`(@"26E/>#3@NHUJW'7O[O\s}(8:Hgr)8LKFR|Xcpy Rlw,:jPCQq|%5;I~1@]ikvzN[- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww!t uvopq rsl. mnjk (e fgh i_ `aVbc d[ \VVV]R ^X YVVVVZ I TUVVW (N OPQR ?@ A???B;< =>: 8 96 74 5*+,-./ 0...123( )( )& '$ %! "#        (0`"2.=[gkv2A-==KR_%5HU)8AO*9'7&6#39G*:r}TaJWUa?M$4IVu6D%4@N]h6Eep;J/>}Ze0?K)]OU`:Gt5CKXZf8GJWP]>L*  {|}[,~&x yz"s tuv wl mnop qrh ieejk c deefg ; ]^_` abZ[ \GTUV WXY$Q RS&, -* -* -, -* -* -NO, PDDEFGHI JKK-LM>?@ ABC;< =9: ./0123 455678, -* +'( ) ! "#$$$%&     ( @"25Cw$40@S`#3$3[fN[IW6D*9r|+:mwVbWc'6;JR^KXES0?Ta " ! " > "6789 :;<=./01 2345+, -#$%& '()* ! "    (0"27Es}#3(8$49Gxr|grepeqN[,<Yeit5D,;IVZf]h)8%5R__jJV(7[gP\0?9GwCP&6$ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTS OPQRK8LMNEFGHIJD>?@ABCD:;<=789456456./0123)* +,-$%&%'( !"# ( "23A?M[f}3B#32Aep fritzing-app-0.9.3b/resources/templates/bom.html000066400000000000000000000021411272363601300217530ustar00rootroot00000000000000 %1

Bill of Materials: %2

%3

%4

Assembly List

%5
Label Part Type Properties

Shopping List

%6
Amount Part Type Properties


Exported with Fritzing %7- http://fritzing.org

fritzing-app-0.9.3b/resources/templates/bom_row.html000066400000000000000000000001101272363601300226340ustar00rootroot00000000000000 %1 %2 %3 fritzing-app-0.9.3b/resources/templates/generic_dip_fzpTemplate.txt000066400000000000000000000027031272363601300257000ustar00rootroot00000000000000 4 Fritzing Part-o-matic DIP - %1 pins 2009-10-23 DIP fritzing core Generic IC DIP (Dual Inline) [THT] %1 .percent.1 IC true part.dip.%1.pins A DIP stand-in part for whatever IC you want. With %1 pins on a .percent.1 dual inline package (DIP) footprint. %2 fritzing-app-0.9.3b/resources/templates/generic_female_10thin_double_pin_header_schem_template.txt000066400000000000000000000027131272363601300337500ustar00rootroot00000000000000 %3 %4 fritzing-app-0.9.3b/resources/templates/generic_female_10thin_pin_header_schem_template.txt000066400000000000000000000013441272363601300324150ustar00rootroot00000000000000 %2 fritzing-app-0.9.3b/resources/templates/generic_female_pin_header_bread_2nd_template.txt000066400000000000000000000013351272363601300317530ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_female_pin_header_bread_template.txt000066400000000000000000000013121272363601300312030ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_female_pin_header_fzpTemplate.txt000066400000000000000000000033661272363601300305410ustar00rootroot00000000000000 4 Fritzing Part-o-matic Generic .percent.5 header - %1 pins 2009-10-23 Pin Header %1 .percent.3 .percent.1 .percent.8 .percent.9 .percent.10 THT Generic .percent.5 header %2 fritzing-app-0.9.3b/resources/templates/generic_female_pin_header_schem_template.txt000066400000000000000000000011001272363601300312200ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_male_10thin_double_pin_header_schem_template.txt000066400000000000000000000027151272363601300334370ustar00rootroot00000000000000 %3 %4 fritzing-app-0.9.3b/resources/templates/generic_male_10thin_pin_header_schem_template.txt000066400000000000000000000013431272363601300321010ustar00rootroot00000000000000 %2 fritzing-app-0.9.3b/resources/templates/generic_male_pin_header_bread_2nd_template.txt000066400000000000000000000017661272363601300314500ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_male_pin_header_bread_template.txt000066400000000000000000000017251272363601300307000ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_male_pin_header_schem_template.txt000066400000000000000000000011001272363601300307050ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_rounded_female_pin_header_bread_2nd_template.txt000066400000000000000000000027521272363601300334770ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_rounded_female_pin_header_bread_template.txt000066400000000000000000000027071272363601300327340ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/generic_sip_bread_template.txt000066400000000000000000000034471272363601300264020ustar00rootroot00000000000000 IC %3 fritzing-app-0.9.3b/resources/templates/generic_sip_connectorFzpTemplate.txt000066400000000000000000000007731272363601300275770ustar00rootroot00000000000000 pin %2

fritzing-app-0.9.3b/resources/templates/generic_sip_fzpTemplate.txt000066400000000000000000000026661272363601300257270ustar00rootroot00000000000000 4 Fritzing Part-o-matic SIP - %1 pins 2009-10-20 SIP fritzing core Generic IC SIP (Single Inline) [THT] %1 300mil IC true part.sip.%1.pins A SIP stand-in part for whatever you want. With %1 pins on a 300 mil single inline package (SIP) footprint. %2 fritzing-app-0.9.3b/resources/templates/jumper_copper0LayerTemplate.txt000066400000000000000000000006441272363601300265020ustar00rootroot00000000000000 Jumper SVG fritzing-app-0.9.3b/resources/templates/jumper_jumperwiresLayerTemplate.txt000066400000000000000000000004421272363601300275020ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/jumper_pcb_svg_2nd_template.txt000066400000000000000000000012401272363601300265130ustar00rootroot00000000000000 Fritzing footprint SVG %10 fritzing-app-0.9.3b/resources/templates/jumper_pcb_svg_template.txt000066400000000000000000000012371272363601300257560ustar00rootroot00000000000000 Fritzing footprint SVG %10 fritzing-app-0.9.3b/resources/templates/mystery_part_dipFzpTemplate.txt000066400000000000000000000030171272363601300266260ustar00rootroot00000000000000 4 Fritzing Part-o-matic Mystery Part - %1 pins 2009-11-17 mystery part fritzing core Mystery Part %1 Double Row .percent.1 ? THT true part.mystery.magical.%1.pins A dummy, proxy, ersatz, stand-in part for whatever you want. With %1 pins on a .percent.1 inline pin header footprint. %2 fritzing-app-0.9.3b/resources/templates/mystery_part_sipFzpTemplate.txt000066400000000000000000000030021272363601300266370ustar00rootroot00000000000000 4 Fritzing Part-o-matic Mystery Part - %1 pins 2009-11-17 mystery part fritzing core Mystery Part %1 Single Row 100mil ? THT true part.mystery.magical.%1.pins A dummy, proxy, ersatz, stand-in part for whatever you want. With %1 pins on a 100 mil inline pin header footprint. %2 fritzing-app-0.9.3b/resources/templates/perfboard_boardLayerTemplate.txt000066400000000000000000000005421272363601300266600ustar00rootroot00000000000000 %8 fritzing-app-0.9.3b/resources/templates/perfboard_connectorFzpTemplate.txt000066400000000000000000000007551272363601300272540ustar00rootroot00000000000000 perfboard socket

R ɓnvo$,8N2R%+' WS) e@QRx*jx,tQ5bPn|%-INm҂  :DO5-(!AQ5QĤ^VO!AɒVsIEdkKIu@e#,4tb&Н͙uNa%#҇,jdtҡr.h:#Nq5z/.}s~iQi qB@^UH v&1$  ~0~I T3Cת4,d"J|ZʘQ(܏.o퐠$ 20cqT5 4B'  -1^O&xFz,c|]e9!jwz[] Ѿ= 'H9rdGMK uҦ4f `tEXBb6҂ܜJYTNק^YhHFFJã3 RD& q@s3䝌@Q t(jBIozhbj# ZDhWZt܉tr<:0 P^^i2E2>) =#LT0iK+%_adno}q*bሯ4J5Gg$`$|'g1n>[sc>KQT,җ`fZntAI<d$f8~`%=8B5#.$uFjˬ(+(ѕ.ElbvŽ 7D|c9w9GtS/b9ҹjN[\'G6;ZLȍ[@1YN1JR!Lס*c$& pET7V?[ ZL @'@>ɿu!ߪ0Ylf-J%3bJzJ.ς'F: u9K蝂ƙq ۥh8H >J@7#- <ҺVAl s]9GA.(:N%$Կ0Agܶpd,m(>RBsxD^B|2k&EJO/Op-#C)Ŗ16̓>iZ{x:2Ԩ 퍼ѭX;tN/sՂ-$%0YXIU.2 kO.˓BTwL΀wJS6 9V¼qN= 9EIBdZnm%Ϙ}E-_ͿZO,bѢS#=)'{fA{fD%~sBѪn [Շ`ڡI)&Cf]b=_jNrd%4D~&Aݢ,(*|,1cTs.ѱN xPcF3GT+%>䁞Vzډ@ĩ$$F^-yK~:%$Ԗf#1?gIz 8 baKz!:_Adtxdp¸[~vN\gCf0ɤE9i| eCꪳ>0aK!:O4Ki:Q4>/gE :*O5G8^  nlFŅj#Bgi_1e[9}̵{gGwKiCNJ +mZ Z*$ĭY6s$i5US.p.) CnvbJɻŰ0{r)W.qݹY).f8S.Q;bJI%^hP 7ldal 3oEus;\? P2QK5XXTPS!(v<98sY>Y,`"GOsǬ܃#8C LZ6yC#vHAOBIް]Т?GxۇxYaK:ζMp $K BiQMZx{Mͳ*=O dߙRF*[xv7+ )nyUpJ:gͿ;-=]ZKfNd.$U8#|+8c2Ey =:(lϛICж$e-5;iG%dOVғJWt9)q PI2"AẨ3^ZD:Qm#8 9&_])S0[vAd`8DL>1uqhC&MU]b0/ ~>`odj/_XpSؤ]E >O( .4i(F=.wޤb~3bTX 7z G(c3I񪐐?P 'a'nDk'莇ZG*D{ulZE8sZJ %P B(:PR5x[2&i+BaCvdB#,x^#aqM奭x?Nd(۽Wx4F\;*gKf3hP 3i1bicPYw8n/섶2CCI38 Ѥ]#- 2cū%|JEב/(MD^.0OČpRvH "h>#]'a(j1mHdɡi"\s1/g~\@6=iH|DlPoekhw˛]CB*! :\?.+bҴcZHDYuX㙠vB*J1}li'I F /5iMo6!ޣ` d]]) v"& :p5Ec78DZy-ZӨ MmX@aX脩IWO__Rq m,, )!R;IM> b8 }4hJ%-%I:Z*Z 1rCQA"tָk-u.fg6Pg"R㜶+.uńyUeXUSVK;6EHɫ&qk3=Ot_$AN ZIuck&fYVvT,A)Z/tz 2&yVT {B2&P(l_xW-Ul9q`eqY9ݠ(uh8)URgO l޶{8o|?p0jrXxSD dž0m /;ybeǹɩo9fZ&rJ lMH CF^Mew(oT@]"bԻNFͷ)ɛ25h4+vЊ+ z&04B5&aKY,~>[,Dȍu/p8blQРHA^y8O\8$*f}Fz@o8T`h$Z8".}Rn4);W'JIU(S/P~3ĸO7Qc.s!>ZÄ;DuG0åX gXW m֠K &-~V"]p QKo-D {Xy5 %2L fxNN*;|#3IHQO.z Xj1҄vEC _K.DLhn=9)rԧ'O+E4Cy]b4 2\ԤXS$t*mBHy)*CK)%S̓l )AS-²Ig'#2]stFXrQ ?f~zd)g_r(C\ )/곘JDyA8oى̜lxARִXb(}xj3&9xꮾ^ "j,=$d)XuJ| ͠*zy#Ԣ&ZZcj`*骼UaQ#9dBUk#}(3>ϳyWa=w=ɜD%td!Єks*IbW~f0|D渜avBUŃht6M-=F bpTx*,}8u Fc׼^֎X,{H|6A Ù oVnWl0LZM7cKFU,|?>&mԂ^̌JRR*e>ĖwQ[ʰHn(.;-n#{kI谒v v~yƐ0BeD(|uug3ULts}eEK؋VO\37LƧU% (-۱X4tGD,gttXppk_km4q45 y0*CzrJNŵdpfNg3s @:] Xd= 0EB!u'J@y4%UoB"c4Cu]b27ݩ I(((1BI>1۠ d ؿQBwS:%j55ص Ŗ+ɐ[C^X+hB<4Mפ 5fzHEf+ic7YȗH@+`&gݰ 4xah]'b%(_˃x+kVeHW[n(pEĭR2=qqjza(:xŗHp9zrrv I(2N]?iށUYq]Ȼ:().TOE]!ET!vsQa싱O"бWm\m/ӾRe^ "Z $ rM65 P}@mM'JhwP_";#{fTQvUbM&ϯϵXQkwY2}(JϜ roͤ|7\r>Vms|"@)wBWMՆJyTu1TxX/4:P[$50/ǡŎ{Ȃv-,A=HGt~W9Z^iB݈dc_knaHw B7Gu$yn5UL& wRhszb5 ZxǕ?2py}#D5ځz2C>xL\.kB@OPsBHtW"h2}x^mĸhI1:(aT1S~ cgBom}_haړ]9{ aFd b}[@ 4uUeO|;o"w0O$_:3C̆JAn @^IqV!U LM.D_):NR@U-X>E -"D"a .Ω+S'u`V51^A^\섂s:gVH<>ۼڗ@읇l4 ǺWׇCa2&@F14qdI4X}XܶǧcU =f6?g"-Z6@~Q:_Wcc_jK1.Kxm:*j}fi< zQ ;ٍ!l{!Bfa/1ہ0z݈n!Oki 2 87JPGpPbڝCĴ$d2[J)WηK"q ~8s7KğDUm MzZL$D(sO)V3@&N͸gkdt6χ3mk?Tgz'.kQP(V CrX0<(n;KѠ0ĜujGtTו]`(+:x8S4k+RGtֶEt9>v8,"B&Q"4%V&!LGbJJqe:߯k&˒nT&dtf0(<,^H Z[(r[M=AMI5ӷ6W݁zҧ9|aH)[4@U^W87'' [ホGr,2&dy5<!GvI0 BMLKVXG INȩWQBE팸~HpU%l g4Ů ҅g)DWLQ-IU/?6-،XSH;ڵf,+ԴTz9?*3$!5z^1<,@\}*7mg7g=Ap_H6tSTr A7B$x̃ChBgapb(1Gg zՖeWxoI#aܦXߖ;cu ')[ʹ4F-soi#URKǗ+< TLmo?h}}([.Xл9L>0=4-x휖|Dh}k8ڦn r.h!TFH$M*j֌8r2'N4ZAMR8hw„ U IẠ^FGQ5&C:=~}b*pH}%='`ч9{7fYQ`7pؔ=~B2Q?r"u# 2o^pXG8ͭ?v[ JEϽR/!fmF_o%nC:{głm~"^ce"W8\tD̀\*>[ 8S^dr1F2U8ULRR@}MJr"3 e)=h X9,FH]~'KeAWaֆ16&}q'3=fmYuͬ<ˆ.BP3Gх* ObϬb|\ a Ʀb^wUmѺt:+hn׊;HX{bU(r~rdxfB!o}l$1uhw蔤.%._lԘmZUI?II}ֆJ\ܴzB̄ dz 0ȏvgA 0 \n#f=DrKT@sैUhWzYW]Xx*IQ&SB[T=,vrW{O(l-JUcRF{0 Z| <:Faa`O88}W3h&7#Zu|&]󢽌Ǘ{*#%CؾMش;~$Hy* X0F*GlVwC#1٪qggCv3/҄@Z4]pw8NLTjʑbİSQپCiZ($&YG*5-|\w*S3c <-J4ݕx7,V$*e9=FatesDŽL"cB;U[|% Ux7Օ-|uM[Oix1#tRFa8.HdsJ[`a NayHF TwhjyY9.#ج=FlUگbYELB*xI"XNPwc!cX`/`{#nBM ؑ2Y6]>1XbDhˣkL|[ s`:r.@ 6}=,Wmtum1B'Uh/UҲz.'@^@ﻳqR^m&fЊ#2d26P̉jDʴ33Nү u >W'vWW[9zܯ]`5[h~E7rȯ fz\[qehs$H0M4؅4|Q#y]1)ޓPN/?ⳇc&I'l֨`_ Z5xx5(+ATfTL^Q@CiEsKYX‡@ ܂Skr{x A˹A2GoR^*#em^SQ(w =*6ҽMms]N+"+ ۗ+BSCo^iGY,NvlxBfeit98jbJ6V)1@w'0Xu6cfx{PCjůs޾,MX(랏mG>Qq啤W$`i6pa[;0[D\_n~@r9/- 4cCa5oҮӣ*GV8}kg;Yҵs["4=l3ȣtAKf7Mqj9߀;"'px 7QJ5<1u+5P@XQ2qģ8f(5jkt5Y;c?̔vlμ2=sύ)dr~qb-)BeIQ V'{t }*ChY|SԺSG 'uh028%TN»33H6+FS>˙%,Ghy n0Els;FW8Kx F)6 : XQI وAO?4,ZɡciMHMΛRHCw׶`E۾wUxpk /;Rj›)Ij@qҋIb{'!6M zfU?(h RS{Ϲ*:Lw ;Vw6[q_M[ oU''Ӆu8p!fX؄(536y^d["z{b@2|m/kD2gӫuZjn5e`BGeHGnoC6B 27풢`'5>6z25pG(wHQPN/~[BIsP>-`ңnWByǰ/K$դϕ8O|]BPV &qi# ę~#sĿ#hk~n=hS J tX2 ըG<5vҗAU2J3xXUngɒP,ݽ?_ ;aΞh1.Ǖ8qeLZo5Sk 1DE4z_S7hI߾_y(bH u-p_<Q?Ɔ%x~RM8_~ϋT/B=ƫX즣\P/AڄAGChp.Lك7B+T5㩑RC,>Tf1 RPRZFFM0^r4;@J~^5 =`[1xgya]{9fe+*A%A({m1Cb' c7Zy0_AhQx 9dGT9Fu4:n^7O!pepa K;̾=Do  WtJ7/`5kP٨TR ҽJ!i}(P06&@IɓN4i{37}}%MFW ) fkpMTȳSn4hz h~h Yy/o恬MEzu9~Nb5X{hQn^7ou6eOv ؆)oߦY/ b|m6pF_@ls(ɆGRmh.]T|by-p_n?? :=Q2M#<85<75!Q _ -=O{"Ֆ~1U8^B!xM 2sWm K! 8w#ȦkkẢUciCS3f FTO<}L{U1 5<0V,Lm~w*ȶVCˊ yRx\Y=]Suʃ0M/{,C_ Tʀe^l0T2 bLM<&r4fha]obܘjУq3MY0套<&60Dr."0cO#jCDD \->2Q\{GeZN^^glY'/ȑ3)u?$)KcVHA<D5y熾lQAұϻu5FeB{Zq6]'I\ylu}Vc ۭ!d3c >)90K-4X&a5IB&Rk3 c$qDVvy@hm`_@\G  ׅ} ^O;h(q]V;uDW^ǂhn A{AT2XMFa,y%ϭeU23]dIt3bSX}YF?'MHO3RhW}Kf*tD-dŗ/7.jjQb|DGV'*کRU P*l>yY$Y{)-X M.ahMS cV@뚂E/ / J|= RہͰܾ%lQfԚz*AL퉩("L + O8/(́bOQ@vǪ~e("@F@mN:HG Ti`NУW )EG)7ԀIm|Ɉ.J 7!CA݁tr)6;fOL3Z `ڍ*n 4^t{?UczBTWɄ(m߹!S>la@b'6V34Aqvd9}0 -u W:xt7i3$lԷ%>:B= ^_"s6_Ȑ3>)ixO _͠=`kk =E%NCBw:84l!mtiqLOZp-MUY/kXӅ>;fC.bc@ enV?聕RqnzF3Y*u&:zF5c;uGn"ә+U#^ypjl6W"Gy`\Piз3/?.j. "i0 Dze 4^+DrK$Nl DQ "}JFpS9T +KL䈐|~RՎ=D-9#nAI9W·oxc~ I0 x4R`yo̻ݩCVDCh7:[ɽ_BվnfaK]+"!NZ uKOj%lٸW- cV.4Uf[(ҚuGEz L`^n"be5+yED  Dz X\1fD!J ių]V WLĘeAԐRbZtu>෾(5 [JUSG{GG+R.w\EZ;A-ᑒXc0)towJfY=N RǮ+lQRz ]f2,;5;SqUuM^{5H p_ Lػw}-b8=>]UHg]xm*Jeb.9FfC[40e-՛]uM]gzb6JK@7G_ 8u&G0Uf7%UFҥHUtI/8$7r\㡁qu,[曩cR~ZC||But&JO/'e'DP5҅NPC3'nڠE؂LnX`sՖ?e} |?v*S҈20ӺTюUyP>pk>7BI 3`S I J9O-Ś88(n$_NE}ța\s1>ЇµAGa,?8odk\%Y+Wi2k*2 /iUQ?wl{Q.ap1p je@.6_SX|t~T|^@ Sݏi.r{$X-xـv\sI4IനcBRtF$ =%@쿅b-=uId4g[:2;յBkK n]Na-\ȀfЁf1yQ;.xȸב&;MȾ(VW 5ZR(yۡ8c38=su- K!as4=RgrlM,uz|6` NMLCGU./%t+^Zk dx.q84R{OCn("+Ayp((Pt J>AqP;+S=G0ጦu$AUI7k6l(/hB ˟;T [t4uM-%3A4I (ЁLm1 .pv;eV}"R\"hۼP1\) qk03bD{d+p9NаKG 5BGE<_"/'& ЃRa2lvbxCa:V"R5Vlz]҃+_.(dЂ+eϨ [eRyWSc]'Ti2vK}dEtܶe)f`ȞshVz8pYiq*0{lxSkx"mli d}{D!1)-3doWmnEZU;)bT;"'& zYJCP}nx۝")O/~ L.zc pG9{%I#^嘧gzAVL\i)Wԋ3*؈3*6ta yogi{ X$/JIuŭfItLXqZHXɄjc${[x4b"A5D8aܪZ_e+ dbE-a*e_=^+_yH-2Jq"f 3F}*FMÖg\|GjDNm%/zM%ˆg"%IAvk΋Bbk8ך ֮NSchnCsaU;uձZ3 3m(A !^5k&{@(pߠj?Iď ֚/j=*ZlW| q٢F݋;T[,^ӧ/cM_Xu#B_EHR#jvʥx Ps8>V4@`r7-r6z!d csL%? 2 ]) Ȫg?))AqP,tV}GRrc8kJ*`Pmrbu3puFY1 J,YT`s[{ac'c75LUL/.^3aD mk^Ci)&ш,R2n3CaGU,JG-ThWfl'ˠbïL\EǶGda/,.%$*3ꉋ"4I2r_IOȩ) , Y:,0V!Z}/I⠇,JD54 U͉DD +424T!7{/.2zЌ#X NlnAk  ,>Sl'|w6|-#{9c{#iX{LI`VB9ŘÌYS 4{da٠e5IᓊM;g0>hҷELǥ}D'A  TPbcO$VE9ARaZt*x[{Iq "Ph/n}߾lte_zW>hvSnBzDdx@+`.K5"AhEHIbwGAs`Kmf,kE*km^+^yq)t2-eFz 3zx)R@ɐqjù{k8vϽ0$~n@FtBTuXi6g?r) gc]bb%umNe@BE%ءba8]_R-Vx`$ȼ^~m&|Bѝuo[]a*cӑmRE=l&.Cͺd zC=Qwd/yAtr߁q~u$į#ŘHut"a NZ8Ȱ7Cq=CEi"en69ErE%f iYi K+ 6U^HFEJye@JrD_Hlq = "Q@=e֊Ch,;!ׁL7i}3` xbܩ2yď`_RQk7xDDR֢M+hMl8w7f75ڈ-!.y'e{ I~= vwtIA R%Qb*3:;ѹRc2Lb$aHब+QZ8R&&nkh"v EeTqӋl4&*#<؇`&s݇]p06a}myjB ux8ӦAa@B4HQeVplзX;E:6/CҖYODY&Ozq;JʘӋJ҉hKMDyA *`y# 5 :?1 )uB&>~C/m0)fT7w#7u[a[ѽY`E2ۙ-M WzpOhaC%HuϵVpʜsc,6Z=aE`!NUP<8Lƭܣ{<}_Uw:쉨NT lm:!DMu{ؖ{hnޯ=Z|:c9d1=)2 t1p=4mV1z4T & FBNQfmblU[Ë,MԻ^SHےN%R2*4Pe6ۗ$tcwvaP4pcpd 3qi. ns0Am<Tݠ0Fv65yGm]Mr2dy 'Z{}U=!Ė)m}Yw=,"F2loЬ]w -,۲&D]\<}mS:rdi_ _#I %9f2j~t PUykes9YU|^@R7n (kI1{4Oc(D:Ob~J"ZعLk2S z0 dQBG\SDHYڵ#-vWEB)fVVKՆE}$%Fl˞hB|[0='#oagR,{ .^6A(ˬ{ *sߵ3Afxp*'"tK F- T@bpDtHGE8o~hOwwC\ S;7 4l'.E~S*a$Ol$ݣo J ~(H*2m_gft>Ab_*DP5jDH6ݸ}2m;m|&MZZSo>G߳ȣu:(w Ӓx/ib7JKQf#4:p,V &ɴ8Qw ]S. [x[AnҊa!F"M`h`2Y.9BP4㏰]C"eb|EklJqԩtU%ʥ&kw|-LOQye~~()CY{E8&Ǟuϯ^Lq5&2@Fe@O8#ݰţ2]piIl=Qhо~buY|/Q#fk0u)G7y@HYT܏`|ԚE1ÌR'wx@P)(;29I"ۥk Aoxf"G낣 Ҫ< D1zE5Gi׹ahfV3Q]QҔH5:R+m@]8G\%RB _>)H.9;ͅdNm|;M6TZxWɝӖ۴0r/7[^Bâ+="mI2,i9f*/ Pm7mE|:1} EA|>.45P,VEFrW ~S/5:BO ]=7`Oߴ@VPN3/qvN&z=40V *hGQ*Q (7J^DY$xAb6hھuq ˠӎ"ռJՆYPqU~++&v.ՃR G[U r N;S#(VƩZ9 P魳[0~-P<r%x?-*)Z뺚Z\^MzL_j%8W q0}<H -ZȺ\Bm]>C%FJ,Z(\`ٛ*`IK$ )j%w$ikY^Gop/֘t'NՒTS8)R-^Am+ܯ\m0-G@O6MݱY\/p Ǎ48&*]=3⍼zkzHGJ5s_6~w Qwi`pʿXw|~RxqzqJuKr`T j¬c=l$+`S9z-F DkSp1a}T SH|'^W&ĂG[=7@ۤx3Hw1ۋb]x`jCEOS-S pQ@oTfKHo cl<qfkhg3t1;yՈ |%]Ԧ4=Sd ,\9DF{#8-Mԡ86< oQDVNy8Z 6>kO6O@@ZzqctS[t`=/HtQBÛmp{y=';z/HG/ļ)yKV a)p4qDvP)M[RM(8]p}kΛp(%`/2ywjGR@[^u;?]V"l~lBnK_#`DvĄVΩRmG d. YDبo|i)8v[+m{K6j\#{!W5+i De dW\A֨`pV͎ꢐ(؋8:p#k!Jwf$+.8>ᩋŠcVA&ĩM1ૣ!)Tpc 0mRВYpaGl-#}.9>DD)Ѕ2Wp H3aP!ݞX8hB  &'{h?,:\Z#ן׌__{rKM'02-9.;gItIKi[rLڹu@\fd%v %ȍE ]l@Jsë=C~vd27)Rm%4qx#zi2}aXeVPdDW.MvhL]'HxY&\mΨ7e7–[ WCZ`kvrݎnĀگ@ *Qߝ*@l*9ALӛKY}Ӳ"c-_+J'ycT2x&wᙋGYL؟6]Vv\}!%O=Ssܐ0cI,Wn24j![ɘ5~ʫ+.<@u֩3 W :iirKCp(&@X(DvEO6UxWq2cʺ 0`HTW =erBM7r6Ȧu!}9wi :iCάF?)QƅۀR?lFE+CF+R湗 y)?HyVyQ YîQb\0o6ԣ#9J3#M. hD|i#%0ט/i0xj nj^QW:w$F, x 27bt*!11>2PFBh8&;ʑbzY]2qŞYapH4|C'[z4Y2hOo0|߻PRjɊ\^Gv>~"yc*V14TAnR'n8&Wr#2vtҷ'@LpI=SZaFֱXv-XrV?rn4a o Ojsè-0'=g,h1X彬Snj[>93޼,pBz!Wv!y ŋ/Bq}Ņ/yR.1PgSnL ɅJaDQ u\R_N-'plhW؏];wG:n0sů/K@zr@;aL;N4UY쌺b~#܂~48Lpf&Ҿ5ߙt * ?H`չ}7Ms&Ymi{X4A866^ДDڎ #@aHVbe/u<"JV8nChtܸXG_M~r(|g]ee6_ZGFE"Nv\iJ~{B ELhhm̒ az$_F^+.+,\کpquv C"^l-njcQǥ0B j{4?哴hʕ[rF,--{5My_Ԧŋ֯/wu\^ VCԎ] G"tכA"܏LUyYgU\mIdcHa~c78sd,Dfc0*N쁱c'R7Ǝo,cɵ!vmu vކNLSNZIZ獯pqCum}=rus"/~_b _"Ņ-OV?7jC[MX1Ч@ǂhW(kܞQC7r&Iԗ*弴Q1*oq7u%G2 _?q(ad| Τ+e-<.XC1^?V%O%#9 @H{}-^9(fky-TbBUQN_{_0la*G6:mCyӆ@qa N?(8Kł/p< \xƴ?.N _਴hN#3sܼ^~_uYaw/j4ǵ?=t5  jӿy=53'Q3\m%7FHAO m7tc@ƨ)hX`RC Q+}4DͪQ;;:pG;!dZw?÷qD.OD:5ѿSow~+ тlDUy|y"wul_jtFw2D57F hZ9ƨ;z(ƒncBnw!ꜾǨU@QllwwӍP?߹/qYz(&X Μ@ušvf.9o'Ӽ1tA$N 8M apL RqcNrdoi ~f~ 8%w\d;74'7o6~pTpp;8D65A{k8&+J{ 44@H:'C(7<hF˦gs%goeh $_j3?p\ZGܷM 0垵ciJ,IjK)ܣZH) lMN[N!l8uLUt ӑ\@i쮐+ fnsjnۼ~Q R6ұUBj|-_ygl,2#r }&!ӑso$lџCA6Hkb =gHfΫ݌5 GU:|Ԝb 2̷/yO2a"7L:L[ &ҹL%#/DWk#l&xMdDAG"4SotsoE,[Ӧ"-Ah͹ۊN)O* "cR ש\qfί_fUTzS%gy|Q|tRY熌=řVw*lFbɻ!C~Qw`)QD%}e9ܛiR2"!` 0+Aj7E瀆͛e>V5 鼃, q sOIRy.dlpCWҩxAwc[aXaTаrb.> 'Rz"im?ski~ J LiկT$x3lH]FM ƆRC6{- 31 ȨA*u¿Մ['1k#I4[T&cAމьƀAãhWe`Y]̟(+}Iϼ5 Cү oVAy' v_  K;AgàXDXh[}J/`d%\VR<ڱflcȞ]y!'j3etE"m(ſԷu`k5|,PhRU=\J< (J=O$ذ,Si@ .zzIeVcL"laCU]+cݖI,cvSm ï.kX+Vi:bIdUr$HVVI50_v|-I`fT[ }_^sZqk\v D\#duA?QԱۂoFnX7|C0_u.kGO.|iYw* dnf<.$5-a#ȡ(rexR 6Bhge2mseyP25b.aXHB}ŵj₅x Vߑ/Rޏ/Y"]i9Q* kAy>λ޺m4-5Y۠F u^'Mn"Q]4P鎃Zi*1%*c(;C|AyTi:-g}&CXsw폢@0/Y%`Hda* Q81<̩x(qWEҝp.h*iqVzC'ߝ7'gytYmtb 6Av7j:*ygJYi rh`QAiKr0,A"T{]jb0(gT t -u+Z]!Ydxb z'Jp!}>BV;нmy!d"P)ʒ2VC9[ $SFDPcJIg㐝_4jzkwhh"& ,UoA/f(<#3"d:{}̇y/Hh+Y$ܲW}ZfWV#cKBXE܃!IźFm/N1%iM@,?SAq$d)[u臏eYf|0m91-)5&Z^Y`=H[ t 08m V.=;Mh:Ml*nbh^vr{c{Ȯ0yLPŜ,p?T(vCG5W4H4o9J47094tvqRJRD0@E_ 3݉c@/Y)[\s!}rK2.ygeC1t.m}E)3jj[Cʩ"=BCV )_2W@w&jH Hǰ9}(DpV 9 wV0TCRY5s^I#UCziλ!e  1CB" C5`9>5!"h%$fXlTp[WL"1zH2!F1}R 4ԫR=:2Cr[=j9Woawb@v z'FULE%@+;n,x0 XiU;)I)QX_H<Z6 m5)k[k rQ [|rm-V֯BͿ@@ Z5$d+w,&:UHMƧj])59C`sf@1JEĨp}p8@G*!eɷ-AA13P*n$oT!! G.:L[) ,d>x-:kMK^)]v Jro.O\TԊa(PSJG tQ!2dJI!9F\: T=+"F-GkWĂem\G_Šˢ(gʛr&U@18vHbxJc0҇WszN2Kz F4Y +%j.ʾs#% GXT~ +:qn&)U"C55â(kind_!P@BEʇcI`;Y圈\ \Ċf/8K ?홊V VO{23@5'sߋ-Z:5K*d.1 +Ymh\E<$ZtB"[k1nm눸|Pu[ʈf`2VOP؆*"KU7LeeZVG\VnzZ} *),hJ"@KĿ `db5KL\mp* *#k$JHmQJpHx.>c TkXNa9&7j ngݚ DY*U`16 TU47Z9%exp}g6*x̵X~:MQ5 e)&gؙ\s7x 'q.Eu@ u :Ȩo^Zф$K 5Bl &TSl),ki2΅#BQLX!Rp/+!UeDv(-hq̫ϗ˞%!*YF)v: -lcGZM6*/87 $(ڞGBG1Yk \BgL>2\dą-mi!PbEBCe&xδP3IUaSm1x؛#ZʢaPrMA\X KVe0O]tTSb훺 V3 aH߰c::"((Y43|χ!z(>f&7z{Z lF+^HFI Nu}[c1jwwhv6g9ore~qk{J4#P^~ 8Jⶵ7(XZ|@OL]tT` ȷS C_|/kRj΀߆oP-ހ3(p 1T#W84a>K_GiR 8C'GNaM*ܧH\7kZQQT@Io`i1ցT`q`L* t (z$RlOY!6dLH[~yE*GeM*ДÊTx$DhG{XhW@qxPc>NZ4Vt8HF+@hTHma4@jp`b^Ȧs*0`xChw f^0V*0"*W(VtK^Enip@ !yQ#|{(pI*CN@2m坮c]Z{֓fTIDdKϖ4b^iI!~(@Q6P+Yay (TuR@5ݱ&@Y]Hg+y 8RdR*ׯ !KjOYp͡ Pa2|އR`I) @hKkJ% o g]q ySҾN6No)LUI&bemik16:(~TyW2Y#X|Dr}6Zր_Dt"~$@)$"ՅP @ڡzͨ>Jܕ(R(\̇p2x$N^G'v*\hn-q60fL#6=\vޱG3Lg߇}ӎ#st8ܯv\sI~ma W@PfVkn\a!x555K+φ6 v칊q+>jGˏ3>}Ԧ'~cمkKmfGrL#Ϸ!6.y˧GH} lcjm'`A)^Ǹe3y3n.NA(C7IASS5f Dq)9#ۘ f">pf B]hףC/_vagVݜBGi}tb"ڤm҂^"Ke "0"}Hw72ui${ec/TL%"Ǒ HEF-[L4VNO&k4<``rs@M\NQQ"\ھQXE v!Jl8ʪص6ΐq-8%jE,y NfftZE>^[@uH==8~. jYgۣ9MM{0@q3ּĨ#<>G〒qzXiS;z;FvUx qVij .kE'h6޶.F͌&^@Xvڌ 77<@ 6WK'x ΀ËZ$d h65$;iۥ©AcD{z vd:Qڦ5WoE2i} Y)Ze{R  ljLlLf_;JgC0 D%,deH=%\k+ L? ߊ|d\a<+$Y?jMYÕɎ5ht:xDȪJfpH*'M :aվI/ k5']~+mycYxkL] Vj>(Imllk{:[_,|c5T.AN5$m M h觀5ER o@hX^fb F0hIcsg~{Itvh^ B/Ӏ %4 >j =p(,ZKB$Ot=I;OȈ`vCl37#HT9*fl\ NUh;CzBWWRQP֎lT ϋiЄ UdF+~$6,F I|. *!#C~I8Ĝ펴+|~E댜7n*!Ч(K|vzs0J2,DFfTkP/Km 1N5c;TrO 5%.[&.\ƻ5cHIL&f菐'&bZ sX {尾0*ZSw`퓀h:`($W3=w>D*R)٦`;hT \9k}//XfI#cSit/B+N8n &*3Ce3<xOKhvfgMs0B.0֓l V+H!f3V-jg3MorV0ZuhL=Uvj~B x= .ДX&Z)(2~DbH0 =E1d$6Yػd>jVZͪqV}EZ 0zQ!| [ ?! E[%rڂ2.QʞwrGMtFcyf{A$/V@NeBC &T8bFquURAl@Ksb¬XJUV'csBnl\*@Т#]Q]]ee!Ab(/ Ob2ܾ|?9gy; U A,`[l->N}#z 7Tw/=@V٤˳b!]nLWžypoxrԲ P^fg:ZƠD*BAN1 Ng 'TLGM3dHS*kᅎk"ƻչb;$iF !D%U]&cs 9YOb`rWe1HT`h.00fXaMrBShjD SRn NFU-SlEGfqIiDD#Mc%XaRweb_ٟEnx& >uI$mΪm*%p.\M#~|J$L͚%L+j5usX%CZ"4@g7+ec*L'J,$%cP_*}yGˀdR T{4ha1(KԘ'ޠ]=BSuQ0@m{k3 Lnde} >v``pAH2z&@6Bp U͂j@P.TTTlb ɻ)!ƑB;dU4\KLzZ;3dW*@ aNFp H”&P(4( -!}$Z*c\zBrVP8qLVTlR8Uj2Hr z5SGAvhS!&|ҐٳNȚC%Gwa$,=zoUZ"peU!@fKbv,Ne<¦Za`\8̎4*]M4\"IADkkJ4KW ~'xh3ۃMXAyF<"/M(@AcXŽ5f☝G8,,| 44bu!`4V8b E^J +G%pPbQXqJt{$Ahה_756GdsSim8e^:D+Σ: `N_ $l>"Y5xˀ3L&3D;gT&vB#ך7*mCQ#,.@:¢fu#]Gf;`d\&f*77iXpM @]"Ex"QU\48k@u r#jNG lPefؓ`Y0žIE! Pd>BX<b$\Li`x]rȊ%F$39C;RPr3 cٯt2zK{/\6$EYC(dğL>R֪-xS{c q۟^󣬜|/ 8\qZHmE`.rkM]l39CJ$jQC9XYp!ӋS3r+\EH!h&rHu3XiZ5GrL@c{~{1cHWV nXxFj fBMlc.hMϾPx~;Q^ W 8(!3D0F'X|iF:V >Jiʬ~E&!0l $]2,Z8Ӑm_xFGt%X~aJ n3/e&QXL3%A6=g}*4x3f[=UH6VЧIR U9Uk % c[U-{c>L3]e:1IQv'@L%X<h"lCpvRKFx'DžR^BCxsi6+!-? 9tauc-Q聶E^#N$X4B&saQ:(zhfpU3%f7Ha| >f@~W+1.x*@l!ܺV$z- 3:4gq$@nĂ;D2))܆mLjUD%x WgIpvI$CRbQ7PX"*@v}C6ཷ3hqsQAJ,s>ԥpG^>b 2 fLMo=?32wS', ](6+TFESN.RHT/ NG ==x/Bʽ"JOsB^YE TOU k%[2$/lU)L^c{~~XB`z9n>K P͖&Ӛ(cK2`mhJ⨴ 4'Qt}VnRĶ_\ %tHH* s"Ƥ3.2O*[(KXq@a R# ~1_ɆxZړ &1byYbQY=m tmb bA /H\"n!_uz 28E 6v>HjWJ/^YYA/FAiQ|ˮV(*_Lk ]z8*&d(= U$0+aK ZH%ab'&X+|:< h'CaAuaFgl\6!㘪ίiGǡK+rfg)?w C<^ȗ?[@s1r} 污?e;Fi4G* @~Mn> 7yإ#l{>,չ|82 cΣ%q!9F͐l,j Cȑβ'A{y:TVYK׎I^olj48O^ᑞ"Op1mPָ0ڎp\1F}3yfk`ڪڴ{tzNgKg OS~g@aKg`<<f}Z? s^?˾):qZ`na z`[gu4R9Gv|'tHK:C[wfwrD3K xn挭Tt0[k>ʸ{^&tzbdHҎfxM2sF.ǀm<Ҡ0Q3˽Rjs-+myt :[<)q:};Fo4 y jD6D0\"f@" LD *2[͠NI&8F W/X(u~ɖM5vpXi׶yєu62Dn;Ky0Xy$C >fpB8!Ta=PhhEӰ7$JXL;(-ϫNJL0|E&0g]?`hP]{{hNNO1\f_NN-6h?]|88<;>j3B<=xwg_:?:ɲ }t?0}!{x-;k4zW| :GoOߞ}evg-_O?!C[eg$6r~Kxcqf؞ڪE5MVnH?P1-PѤO=LifI[Q׹H$WF6:]UHTtWj- y¹U!f::=/c黦/[_u_~o3H~׳_,^ի7?~YB^b2wgoxto~ _l?mŻ_ll/glllΛN.f~ߟ̖7z_/;\u?-7zM)eVt%Dy6&: \|Fб/ۿ>˛WR* (/K7$ηLJoOo?rdj|_T9r+@QNs=# ?OHJUBujR*"Nՠ K3KT5xQׅ삝v}~*lNǢ=DIlJ犞Uȑ U @; -?Ӡmh1xrzqt ,s/t1sE$7˜V&;̹pVnKNsL&>'ț.UtY63{)L*>׉9kC*wo͆F̺C_߀Ur-3f{de{'Xly5H2LnYCMlg XI#E eZ/RaR5y>:cOyژ6hf7I ʼ|c$mʇ<9e2L:c2}X: bg>7nIE#7+6p N—Nmd`QxϚoHi{xk4TG<ęu'Rmq*+BἊG#"= MAьQ': cuObB`N'.s^Axϑ|-,AM9:BLFDŔ<&I  Xs}JT-xޱc"+AR&, N5z"tOJm t-!X(.z>sfSvKnG,ZDtl@.Dz=g>fPVMKĖX>CcL dQPb=,b?&H @+\L}bف0afx1~{^rW%'߃EFhdX%DDȝVXx}B"ܐ,~$iHѾ=,ixJA8H%XӲZF5Vk Wi]k]G.Ѫ?{#s\ N7jHL"ѳSťڒ'I{w7Mӻ@7\M+qĚnô ךvuiFf\2zs<漏/ڱXTרfkRk5w!eaS: J_!kTLŕmՉhD;fdAl֯#e\NV`"0r.CVcYo5*_!mH!-H-4hڢ=UЏTJ|*[TA?Rn )VM0wf&Qc[RQd6_A^/R-T9`I*);|Yc'eBrm37yZnWϓ^{\`m$Xvv VG,fs] (lǶ) Yƶ~(ɂL[f+l:4Yk?f\N(uͩM)<Л] dz)'i]mI]0 ŭ9jO0#YuF&=^}CE&f~-ҽbsjHg< 4*ă,Y";MtlK@?rJDA-Nq՜"&;t˺ف'5CM|-"k8ؐZ'.yЏ*!}-Gu-۹W^Z!Wø){$ )W OK}!8OvobI'q[ؿ>j,O=,ZDrqVaA$SqoI*؉L]GpZR n͓"Z@rJLWEӜnjE\!F&ꙨnwR3Q/ΐX] t)N@5.#gW:qu(0h%J˼&2M$Z.={^zXt֠(㖐`~uzjw OW['. Sx KiQ T'zݔ,#dOQRZu{W,zRwMqm-'kZf`K,+kmf` @Q h 4d75t}$DC#$uE0Q2SvcxoġS&Q &^7Ҷp< D _^ngjJ荛=xVs w[{~v=ї $<-9'kխu"q&E5+-$ )E&Ң9m>"#|LZ(*C^CғJ_T|6zi,pZe/5!ha(\[ZeQ*\2MźEXJɫͷ_+⎦1 Sյ c_~gYf r.dЦس3T]g{ze:[46.*E"q>Y\'ע$"qv]'H;4%\>)ȝRF:$T'YqGH[CeTT:>ô r(כC%6BӬ MTKoC -9μmKrn"a um 1 _ ! Eg6؇vkcڄ8 NS0PYA~,s.93wj9h(ZN`cqyf%bmJj&)g F%^~<*"1^ARU9B?A*by+۱*+.^%V.+^|Ht1M?0KM|;a2XC\+J'WWl]I7K:˻8WnCnML缤 ~ފD]s"GK6ZѪ#{[\,G6jnoƢ8AW0xT2/L*¸K;TJ=Z;m|ͥxO"`ƒ3MgIHxwRY]<ΜnĠ`PG  o)m CE^\ ?Je;6 2aNM4#;k7p i&7%#Y䖂`@Q:|ħ‚b ٖ09kVj{GoդrʍX?p^/~t40ZnYVlA#=OR[xm;sIJ)$Md[owCQMw׍?׫-zItXRHXi:/-c2#]^|JXƔQLj A^@z6(3Ih "tPAZO$MY `5}ZX( N5'w+IUj7ȩ祰2, 7K]%^dHۍL!)dߒK;%+)Q2s5nrNbB^K^EmAP2CkO8t4/gNQٸCqŰ4|sj%ڜ̕Ű5ԻV%,HʴK2ôWq0mf T ǩ5 yPT7㕴qp=Vo:c X^ u+Wf{bƴtZ ]Rh&U]rI[)ۄ{fxmkV\RW|m/mqgmU:6 [A$m^սFS]䊍YB3Σ|PfӍpLm 1@-8r9E_9#rմ0z7_lRGfF%̳@Vpl N|I|ӛ Dwd\btZ(ӛ^Յ =88Sۥ[SꧩЙ9Edi7 ԓm6U-eu%R+`?O[*Vc#%6hQ&ڭ=77){x )I҄W|R"Eo9ou{bj_pͫe3#vdQ+` \`;H#~^3Tɡ->9]GkF*"֌ #@h#[9pH{Q'9IMI~=?aj?&UvˤѤդ^E t*q\Jg,jd[pAq;f*#܈z*aeb(Fm&1RzrEWIDfMԵ$3'clsUҬlT#O0u(xs9Nn>(g38lUh5*j >+U&i.TތAD0OoJndz9YAEdU)=D}Cw^`SBh7EsUf̯$F=-%^h5"ڵoٓ٥&K-Pu`4\_*?vR8r,yVXf /La$Dqc~(@2P c>bQXQe돗b4ڋ^؆%H%A-xՒUǛj-foJV9 kA&҃2kOτP SPṴ0*ţ*̱W@kDyx~uW. w(ΙͨrvKmOUB R;1>pu'tJ;)+O5|_V$i BQ,ݥ6_}fR?.Ӱ7լkuU֬kPty.&dfmWT`- iYjG*"KיϾ,+\r8_,i%Se4Q?KL8H.÷Ї Eԛ# lk_|\+zßzxMU70w WxOe}%=Y_M;l9r4b3oL(i_ )C#Y}BW yW3k)B?ήc5-|?oeӪke4t5ŵcE;۟E_] gogɮ,NNOېʻwo.ʫ; \mߚ>]wuО^Swu ,4ˣiė{͸J$CڽЩ.O5MMvm4ӋǻɏLL~8uk/o4IzsxI|,*5kɭĆH#CãYQeW$=7+=@xTHlȎY?z=/ko ;[e3`5p̕=597ػw|i/oƫq'h_4juf?E˚";s7BPd@;p KVl,.I7;{`_eN^GOf&aϱ7cW>"C"en"f?˚$ 0WZ}|W FaE. )kG!MڳVd"6\6'hʦ=L3fv8Ĉ̀ 0*&CX۔U(SڎƣٕBL)q}cofM ylj?mϿ[@2ict_ 4Ťzm#gR)od h?eIqvo|LS)ۡG?>R!'/&T%K8ƸM8G& p coq+qO˚-‘1kB aceoB$4xU$cv c#E;fdLS9ۡMgvރlte 'RO2Rs[T45Mi]IW L(1&L J&7&Cɏ/<Qv시|X4%].s7}tSFl/spGyjvɔ4ہ(yzq՘^coqJʇ{KOOޞli߾[xҋOώv[K])TVOK%|+/epSy8;9=x?; wo?],stA,·+˹iC'ߝ}}p˾Уm=o~CVpwˮ/~lNHkv_KeNx:%\~LJEpztrq_}{ó;}uu4>vGR?nG~Ļ$Lɺw'\;p/ݵܰ>nu0ஹٽ ޵6Rͧ>  n^'K}f%s~`ڸK6KVLgw/rL^ѝ:C˹nO}KZyWm]5;rv˾O).6Y\c 7) vgy:ߓn`7E;< /釗:!mq?J d(6eb8=><W!3bw_5YW]Du?mvsمڒ4Qrx3g62㶾t_Xيb>U4t9`qݠ}QEapP25t^1IgEwd3"%碙O[1Bt~7<ǣo \g,z̓k{](#/ 9AYD7]|0jnnwG?Wχ對ƅvۨóp'?p+7Ulx5o)>vIlqہ,V~VۿKM0v>{TGC˲ĸ_:+;A!,'>Es䪷&}xJ񶪕ɝ?Un90 8G2_t?i!_'|'稗{V~]/A>=6_,8^7vh>j]37tvן}&j{rK}N5Om򤅫mKx=a']w˓vI;=^{!vÓ6Gͨ"yTfTQ# d5)~*ثvjee9V|^Lej] S"L H&|э=LLLi;_*K3"hbe/bo%_R FQ}h;3]ELJg;@I*Fyu1]G'sW]v^r֡,SRzчP e&s?_Xjz -0){#;pmKh>ۛ۟׹ vݞB] b]cO݃=EaX= Ke½#μr1qf>s/ǀCΏ=>4%>cu/#wCwRː4EZ_ۙ?_W}wzrǣ쓽;:>/m7.+FtvoÑ|tí%WU^VrWO.RGόnl.9ݟ z 3uok5[nn<RdZOVLpJ/RȄفyY.鋘dɗV5=JD|twX-Re}Hi?1L qvNUs|7%|YaIaۙRC&wmRdr6GĒdrV${>k3 Rl}u- .)wuyB><7VO;Km,".c^e<5'Cr/bo&k}>u2o;%9'H*n\ydE8&J@#Ӷe4O=nT"mgV{sKbgrn'H{.Ǘ= ߜ:2ƳEZ+n&uoWz~m $=΅ S&mp3`V$_^&l`yN?{dv~ Vv&?&iBp|.Xꂕ\ߦ?xYMcrv%A&lH=1 fٹ6_r8\8+Sٞ:gѲaS=s'i.V=/kMW8S)ʎ } o(eL> 2q}ٕ8uM 5=sTPw$NO]S^ݕ_%xSK5Uxb;p55>k^K N%ȗڈ9^bre_Č&K&mc!>e+LܛiCܭkVDIF!gesѶ,e8evKׅ g?VӝQ Dv6G9hȏ $|_'m=Rh;_5|SMP^^"pP.כviE6GMFZ@ܵTSIKfRҎ+'SUy:S/J\P}SEahGٷ{T+ϴ¼*=jG<]0wSML{A6]o&Ŵ&n'#lJosM\d˶K/?<9<;:X%< *TÓߥN>u:=pó,TPM;9@T]^!e4I͞Wl]  X]}>jR ^4nE~.S(dncw}'mg s?~27ܢg]&㿀Ю˼ ޵6Z4}x`TK]npվo,q ⒵ҲIݐv˚Z/{K|nw^~ݢgoEJYhd*}s/SW.cpmj`nmZzW2ўK1[tN"c0E?c ;H- W !ʾ) Yv3:4 2818.CNi ,nOţ2_"IO3gU{]Z;ٛ?_uy%)."KtV껡#M!D˷O8\rYX.#p,q+K_7!R(>W}ˉپI|*v E^o"f&I2o~$aH*qirҫ$wN7t/ix'mv5"WH-oEraOVLw*A^gLȔADɔEed5f!rz~dڷe&U]NUr-xj ǶRdv*AXeہ\@L.Frly]l]YڎVd1 PN29jO=` xe4O=@h="VVɴ;5GJCN-$;_4q=䘐;qcjRyc|f,I,.V*—\qkkGDjuNF[ /Zߛ,qպ%nk6YO^ʐ)3&ўH)9sYܶȳM05^8NvY%5U٥$5%Mi4>&Hyō\o\$`xovFMzhn<ޛݺ2p1% n۠ɪrkY-* Q ƕmٰŗ3e\Kyv83HHyǮaw \ĘGz@*~sFN)nZ!PIh=G)Ӛj[@6 *E, 5ܨUA@yǮUY3a fW볪^gprgov_i}||9==Yeܗo^vCpߊZGO[R+O/ߊ˾p׷ւ@L4,EV{~ <˯= |-|53$GNqL癘#ź%PYnLOaa)˟s/oO̿f ]e?̑c`} mG8BPU%cCjͫ}9A5[T _$Izqg>oeBğB`Uudy&>ox޺(Q+=oQ=9+h14S#gy19S6nm1ioTz7Apk63ٝQ$Q 59rAKc^GBXwگKd9(S`| AwUg 3u7 $Ozp qZcjWShc}O{-Nd!+ @ċ[Fb2^S3LͰ̋fbfo*F\p bLKrɶsfǼQ}Oi} Dc^LdzstBt%,\JHa] Qu@ Rֆ=][ *G ,3! ɕvG ړN%F I`6b8Xgwpmjr΄:>>I-o!ύ.^"\AUQ|fF"Ȳ&=qHE EonRH, mUs&"5v,Pʱ[D+zdHnydSRUlFI݊ةew#f"|bg~޺l#JLscyZd9m6&q4j#z_:-G§95`r^Zv'XT %A,Pjn uAI of[zc1,*9&u9]'[> h< X'\e˪@gSMj5G\6,t3K)Uc bT!0bY{8}q [nl" s6i+l7w]Ö=w+B/V-Z6GX!tp|p]`.aIi/!_jd9hq\5ǽ9)e*}NV4P@: Jd%G3Flr̲m)*A#FLlh<wMN*iKǹQ5v<^:Zk>8aA>р#Y #$go| 7l3@9ހM0B[ %d`2ɜt5QBFti #hhsp@#WH\kz ɋ@:z&hBhloy,P.Ԣ35A:gks1q`Ir;],<7$khn; x'1J4zф#zs4-@${p#kB7(_~ ɵPYC ߫+DBNYz"4>c[ܓKr=qݓ.#nM4Ʌ<#.3@}W:5}+I%JK A ~=q@vfH [?אYTaL;sR8m x!Y9II&9e:`!r%kHq (,Bbc2YGۡi`'T;A. 4-E! v>ye6$W(Xb4GZ`CPb%VQbdE'l0RdQ0R!H, K@' H3ح4 1*2k8suG#-0g,8ZEnQ;*H~>u5dQdݭ[sb>W)6jTbDYQ=>J3ҒC ."l}%6hjlf$9Ȱ((s[7hK)  | c:lԦ)İ鱢{T@#-i0 bб9!v+~PK8DO KuFortuneteller.inoMo@9B*R[S5mQk3x-Chlgv -%iU3̬'@f,7,ʋ%sX#2G6>@n,J˂jj[Rd>%9w @۶216n_OifHWU:<0S_ŅUejHZeJ Cj$+-v"רbV 7BTaɂ%!tN(-ޞ#h? gI1(h1\.ox\)nJWZo@|u$5콣9x(NZE*74TZ&A(O41vshvU-F5+FYڡ5]Y L)'V/%ŧA2j&#:;n+';kZ݀WuMf=cY.Y>x4ɑP8ϞV=>zU- IzOgOdTҟBkrNU2j9vG+xPK8D2" f) Fortuneteller.fzPK8DO Ku9fFortuneteller.inoPK}~hfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/H-Bridge.fzz000066400000000000000000000676471272363601300322560ustar00rootroot00000000000000PKl5D/o} H-Bridge.fzksȕ6}&bo OawnӞqҞ/*UhUVRUo9 7'LI"qyo?o/nǯ_&MEm|տ_<\?}?o\޽{+H#9W^o~}LJͣ<޿}u||F?+}⧻~ek1ׯ\'՗O/}ܵMoyxտ.޹!ee>xq{WO~_s/LJ?9>bO7n]ۿ|?O3?7_o7*}w[new7?~>=X;fzns_]WW^ :Tvk26o9 [ӵߟƹWn~|~tڽx﷯ov;n7W7۫o_Yr˧W cWWVuXm ie__sX^W na 'ԉQ٫:!ʄ1_]⤗hƹ,sh?3*s݆O[_\xvm«̡Яw[v u*;`Í)=Ayq{㐲a.Wpl}_x 0:kk}:3λ7wWS*nu?bHy˛'o.Y(_!{*!o2[kEB7/BJq]nBCQKӮZCԿXԞ$NEe $odtrI- qCVMW%A*,Y!dYG\lqAa2! HLР-Òml,.ӐQ$"!vȦ#t"eX$ 6h6}vFǞ"a9"Q$,{B$B ״~+y"OIrs1&됏#p m*>iѹ$)[1%q͵*-!s:0d\r E!1 q%a$UE"1vg)였ʎ r.~0zĴ 1pΤCr.:&ΚlH~QK!yAIa3I2683I1$ _H^g\1pB:hS~&I=41USQ%~ L'$KC>iIa gDHD$0A

|^ȎsfƐguKcZ6\21p\uȇ(BEgLH %g_jSDV|uŽWWWw?lKju|__o|_qftMq{z{!QPo~4gUB+|^+6B~ydƜ4j)|"b헯_IHl_맋7Nown W>Pݽpxt}s&;Wom;) r),pW:1ܤ"Cn>qLi*=hUܾ eDך?ԻwO M=KZ"Ҍj i5X+92` #jk+y#mc`^+q<+=x+5c;x<ဵ%x\$APƏ|@y) GV!P)oR:Mʓ JԘ1 F7Er34Tq(1¡'TeH&x0qARw8ry62 l~Fp&nD8BS"\{B4sc8w*5QW)f"ŬMeVe*/,֦cBϩJEzYڏ^vLbb=z߽}d.}|Wn~|~twϿ}|w{ϯo__>/^|zI)t;+C2y, =4ZH ad¤LE2u(;eo *k`Jj\d%+Ie?qk (m!aR_n۩W?m\p8&@JM9RVJ,%MvK/.; g;Z֮ j~9 &[2N|2=r6|)RM*+تdJ.8XؔcM _Q2TaԊE8OQhh)SeCƾ޲rS-(H v`)HW45 f j+HRH~Ǿn'>txeِxb` e"SFu_|u<.=ݽw??~޾«f no_JkM3%Yk"Df}P])%o>9Q?nUVZu=AAJO)t!R?Z4ą< )L 1&t(-aR&:upI?o Q"0D,kQ%=84wLIA~ wА؎SZP'y,N@pxR>`B>q$@6J-OOwȟ]M\'Uf:Dԟ)3,3P_C'v=EPDUk2L 6Y%Vj߀v#Jˤ)um]v8L}_},zhgIJiHPJE$t+hqTv tYfd^A&>Ueѳ#0!7$"fw<9,j7'w.o#4(N2vImΊs6!LX=wq叛U: fippE&Lؤ@G< .|O/meGPR7"f &aYȓ0n9J_怡mhFfo0ekոt4|4st}H3;G-Sy6z>f~̈ c2/7dަ 4x߆ )f{>*jb w щC_vuP F&"屎_d&ta4A<`P(XIȪ(k3VrF< >NqѐTdv=W@MAџe˲LaBzUB!d$iZ?R{kWcJ"C;ȒT2eۚ%̦Fl7gȺs y?HkeQkq]\A0GWy cbyIΙM =M,s"G5aXcf~3š@{)Y"@HsXh[772=S)nMY~D?2w4DB&a'9Cx:*lbD0 3CVj$"Rۭ3Uf[{VY#6$S}@-FzVkROU$OUj+&I:KGbWИD] ^|1r-ΚM{ygC3%M-1`2=KAٱ2Ӝ7o 42á(r 6fpCOR"Jg|6l3PX4˶&B6xoHRik}Q6:l>ގ$kqUGҗn#C!w<}dý_xm^A"}wj[5oUb@{t8;#Id-SyбzGءXvvGH;;&dA髥/H;6zAA۵Xn߾(SMA60̯D/G9`a CI*'F<7FޘSI!YϷUgsnBӓ|js5=+~ &@OH>=ZIO5Oag*jsBuFkTʑϝҤ v@|D; @{W|҅$ EC\J="2T"u|D9di:UC4vlp)yܱ߻|floo^xpR?aǗjY߁ѥcHt^76IZOx"T ,ZN3R+=L]^H=ޡ/m {0v+TDZe{lkP]]ٜW΃z5 ;&% ˓r_YcrCmX d\懬/:K-eYFYڷ%Mg1Sk`-i3(Ҿ}^'IL:'ÑoRKxRy&HDIKi;\fUCXoßBoßBo|q]yQ,͗)8*Wg-ܛbkOGOV|/77 Y &Q"˽4,12D *vo*2`J`d'x Ow Mf iʾ tAa_7Ft _*xrб~'ءiW1dsT#A MloI[37cҬ!F:u>Q0e ˪uI2,1ONZY[A"UD,R/鳈L'‚*V;;2#jz :‘t ԈXꡧu ^(~$5BC$dnH!H"!b LbC#,2vC O2\)KltHcFtLe-E,֦_ =Qш̫kh?hDXdc]c9ud]i|;NTDoky:ՠĸƗNJkD۝=JX$]G.+{M1|m4WlXkWZʲkB5ȻNr}ōp$= Ɠ dLiz)WЉ.ExA%W=yone(0.Y6L2m׀0lBs{umk]Ĝ]ʦi&(.F{ZD%7fZN3c3Gd_HQuTNBs>r  +s{D659c2kF٘ KXdV˗3ԪJqP<AXЖYgMneVYG#⚑DlGFSqeVQF{&j*Y U|74- \Fb6Na GCX;(q'!u 6bF:B1`DcAb.&gCͫE%66Zolsĵfy/yn\;{f ث0 Hs aS5P킋O\qq&( G!9;RՏHºNU$!I{xX-!Q( [A*ٳY"4lLfZYz{&keg}fDpXM|ўٓzM2_&wP#DN @VV0V4'Wz~g!g /aZ. @#-Gr.e]Q*Owt2M丝Cg}f3d c73NiJDstjM$CCq|V/g~d oҡ'*u Vt# KeQJ.h4c#rV+i#K<,e[u*Z̠<3`G"iPwqZAMJb;/-@T(GZX&zX^ >3uDY-[RrE[5'Jlld[Y5n]Y `\#e[b-]YPR$*xDgƖHWl(aX1aD5t4"$i7`r-Lhq&ZJ }ze HW\QkNvMfݚ&bG70CwN L]]Z痖f2@/‘`_X5UĺCG11ukL%\ FUM޽ֿ]AS -F+MxuJ۲<˸X{yqsd\ܡ@MibMwIXCy8hy֟"j:E5‘`E1V](zYVH҄}ӢK2-0"Q_[{ބKQAruNڟYP:Ώr5!C%Æd9+1 #j:qp$=q G)zWԋ(n$ԋv,IN$Jja5Xy.GWЩ5l@ C1."`xz!KVkt]ȿN_t#Ix jQ5HNbO6B1/"б zaEBXUZ$V F X=6U7B'LU(WfJ+2꼋*b$i]4aX;p'uu ̺FPև& K2c - ƥD*:64Sqp^.ўJ)dN+ku}]7(.vC6S$M ]ڄCVN:+[Ԯ/-l{RBD&kguz*T\Dq"piƊ%i\FY54%&5+|dR]C-4[Ç$`ik4:" Hzp X=dp'*zu Zo8U8ts{$Bba&v;?&Df٫z ͹[?,bnb GLjz< \&kzX~[Y%Z)\LѰ#Zm_gbI]!FN25`NKRXL8z48ݒ;*j#/"::JȳN{Qŋ}ߋf,ȵ4[k \K0di Y„+N*a$SbG5t/6ݴF缮xD:(㈤MTZC"+UG0dz8UHƺΐEX`“jƲfV9^5= Hz0-kӲ*DZ5tz"Q IGP3*($5[ᲤpYceq SaGN5e@_,[!8b2SHEyh/4;G8B@ll֡'ju t XF9d`fFˊD>T=vU9Wj֯d")r0 +pZ//&@rs@,Af>04::S*#a4zszIX*=%X0#ѩY0S 6ႋ"ts&,+go^)Yжm323we[\왙 w vma} 42QúBxZb"XMED]$r:&j"Yla~Fщwhܽ4FpdO5(WfhG5tlZQ3K+D4-&G"M;Y_;ڦl23:Y?RS=gDa$IL AVgf=Q ܬktQIڶS娙 1jԌkHVUtvdEd-#ĚHvrNvAlOx..Xܓ|+T+ ox&E5:)үCL8be5EH.@FHw;R%ٖ^mI lטi=ֺ$F ,P\2ȂrܱSW]/hWwUOThcS{;8u6\1[a;@G8t($ibJDJ5td4#(iHIr3E)PZZQ=EFg`H>"OL >mE{ZKxlE%H:2$HzpYx艊Gd^]C'2/p /ph)/cD,^k]4`547 FH22eӎ*"#yh=#k3V('>YǢ_Z'FB‘0*F>Vbӵ=Q kVcD!$$ig`dIzg=Q 3qRurhADZv0[Њ+RҔ#%ztb_Z?N:W $ccmhCO9" ڋ~r,ɿ.oSKD)ĦYޥUzK}c1Xk,wŐ} [/i++Jae:A_ZAFH‘pNFDNul!u H>B1,H[Y LVͤi33Ԭ>uiIP3"OIͤ։)>ʥr"7k:9x&#܌8A Ր̺HB2* = 64ZAX1d^S=Q k<3aY-5ڐ3BYM<ERW!  Y.]qHfqV xxZRfte&4$4gYЩ3Hyڴl' u ,#!I{z,ɼvQNFeP텄J?UTdVbeU:NfeVЌIYDG` l|*yz)C!``1QG-.b^}q4dG`]C's0s02$[/їLbuHNV`,{bQ|QV=< "W`Z]aMU+0$b˦E"/}3,58LqnR-o>-?>}{WW?SL] ؕ_,\xS+>fVJm}yK_߹O|O7@7/,>_+_~{V{w[޾S~t;g">~}"T~} +?ß]ON>[W:7M Mwm5ڂrdN/psOfS_3~?nڃWWP P&ąVfJ؆a=˄2kl+m/ǧ_'Wۏ7o>Wo7W`o뛿ϟwWo۷_?^$xkpo?ݿ`b\h*jm,гO?4p ~drtvh~mW-+ կwtP~ys}LJwܺhz=5G8F;ZR6aF^=~e %8%K̽vOX-utщ71r?~㿣+m3:0 u/%3m{鶢7lKq ܂pfi% \?v_~q8_1~,wt70?UnX s ,U7r{iw2-пaˈ*J;4t_٭I907*~P(v2ҳN^~~Ew7TN2e< wOmRo&7]DX Sk~OKaݸR eeEr[aݥt*`1 O5ssIm| Ǫ7>ƯkutoaO//h/˛ *A{_tepY[v#2(UWŜD+e@1e)j/4.`/`YZ~ͳ[𺾄~ K'ٗh`-ʿ &K3m#c0 WOv7i_69Z;)d~W\K4eEޓގ!rsGY/fE-D%K,ԿK}_'+hlaC4MgYVڷ˯na>7yǺiC *?x Q!n  km8ŽpGr_@ՕAHExcFH2@߃~pr%<7ipszS8Rʪl3ZUA%.;qOt\sITjWAKd-!:I^H %@oeqLyqxc"+)s⠟_ |A#6`\ 'mf^n/ggqQv9gd@!m>z4qPhtI{rm8 QPp@r@Q: x,asZ B߅e&D&Nؒ~VK/|=89 m }z)`mR3fs2>Lk3|<8b x\fpyoM&9xOMJ]vY3e%3p^rǼH7_=ʶ at-`ƋW 젲fi45kWq5ރr~S<-?&Xxp)l+nG2 Q}XP6_`GqX)œ=6.lפBqeTl6^R/!0C p ,Oݳ  ZLP_5w-p:skxBCR$`70 ɴxmuN#N@ (/.gr^ Qx㩠[y t!nA&{1s\PK󀟁keD:wOo@|bpI\{\DHu)@8LM~mS/+\ l 0k%G . j$UWq L pQ3@T~v3=Jt0=u%XՁvݩhi ǟ咽wy* lV/tT ͥ[ë{x 0&6-~͒05|i]<* 9Et1\  8(Wj-V3c4;uVnɦA' vYMoNKHL BI S};yġ7q! T#nP_!+4hr4TMqoFϛ놣ֆV|!pz0l_8T(&W̘IpH x 2K hW@):X hUo2{×.`M-H΀ #2a8lg\-仕c}eMC#B|ACv|:U;C;Cte*n[.;S 1l5D+,U[o`Ha&Px/'jLBl429j)N,#/r\2] fGT †?S d5ULx _zPaԈ)p,1 C$D(‚Y"2|X3ڰ&}ΰ8yf$,wS2zL Es^,͉%bflW& O%hܙ)#}/(>^-nz̼]C #6zvN@MӢ@ :A@i@^L,֜/ MNUhxׅȀr</wN5 *\>*vQ5_rgSA@bq>;vR0 E e 0S _x A0y:*`[j;"Q ;ND>^Fqe3DCrsU[Q}KU%ɲL!"^Bv@w{ eCE}yuW *uGXXKg)fvEM(2G&n3.8Q)׎qm8JeiM6 O7@Ɂ*jxƧ<ZJ;a W0U:DHvL\Cѽ-^F'ITR|]hZJ+,d<\r@D?F_.n=t'C=,:\YP950x|3 |' ̡Eu\D$ІI-BGue[(|3Ň eA/`EwC 7Snc);MOzDM;A p.C{AoDyN<޽x}JE29΢B$w o(F5?Yo>ՐtwxG T1j(AǠ{k[ coYW4SƝzlY< 8|JD!ԩTPf=w^__s{ڕg}{Rv׶ Dב{|uT㇋K3&!7$Ro Ag*D>HY|jwKg29^B&۬ Ƥw^]fp#ú[ _VRw eݲZlyeU1,gݾ^!*Vb e(=e%V>uۘʱEޚʚ&~^kC.+i eRZV^ vYXi~f+?7 P$c&E3d'wŏ[;W^w?'C)9c [ʬI]R>n1sDҌzhAHy1@H ۑzjO- SFLK(ztbW- ]Bgjl{ :B8G]|&\ߤb-=wE_Sjb:۵4EZZ#.YKwa}ki0D{=揭ťc+z؊ZKT/*ـZHDQ(z(@ܡ4E-Q"q%f keix޵4wDZ/Z:Ճ bɌN%dF/'|]㯯OoOy/$#䆶Fy!'wO>Ă-\՟1g XKo eˤSϏOo`y۷W>?D_mO7wBZ 4 z$%n:o >{9 #I]ꁏ[qcKYY>P$ϭD# I X 6FlS 77\qw>1HzFP .r,ɾ_Ʋ/+PCf) SfEUٗ=\ &$#2JҦƂr$rTfff@5‘rʠsd@m)K>8ق7u =&(IGTǂL D. &VJajl^AFPu FC LGd% 4+N$`C';hG8L¨aa=tizG`]CgpD}LY{+M$ fYqUvYmJTlU*RΡө$bkcPb$\XЩT(p#0"?lKSw/2g@.S=!SjagȐ9!BɿϟerZ" laeP- 6{Xmx LktV:`^mŶj@Dm H"""HDJ"`Zq"v#K=no"Mh IG:*$ ӺsZτTjUk!`ueb"; %k [ȡEl]^޳=3:R ) @L1zG&#ڋy1uYXeeY!ҝ 2byir S[C.ʆ$>p4X33zUe;Y)9l|6i,5t8 4Bt7J 2 +"u^wl5SNT&t5A`K+}/Tvk./i-.vk2dŸ#:hUG~\j HLT`d-Q92!]zL)_)*TnQA%y½ R2 6"ÆM2[*G7[ް |E-# /EYdu5"B%5 5wl_Cg_+兖g}fH"%lOMaG6e}$_C'/#0h-#򭮡3-` A$ rt3"-ZˣqĹIkiWKӌIG.Cv`^b>WЩ̋(t#̋ l"BA[߄~lxbH}"K10#ҵz{=Ys?>96Ρ31]Fvk8DgIFѡ̑u Lh"H8>fVE'o/髆͝WnDc D&h$l!R%I]1&ju$'9*-ƧF9t:Jk.G%z@VMip$=SWhKWOXRVMbYQd IGը`ƒ.[ҥ˪ rAVn$\,ѥ#2i$=ɿ\+ ΑMBd}FaGʃiXaò`yn Y~̠)1*ȱ  WOtN` ĽF":smmYTz6ȦiK`u UHU/Rv([v#:&d$JzGJ W`TC(ۘ O6E9-ztpuMf@F"mWO 4+M]C#:Z22SjD7BMSv-65A̺DpcIpT!y)WҸށ!|Tt*T/*N41$ix)[) 4y$zC:؈bG8 86x 6Lt.W>6s02ȱ[\YE +|]՟lVOs fƐ}h^\<ȦԫcdE6‘`E2N@j!T#fm獤k &^$#HXtSǬATV5?m3uD R{huq$+v> \L*0cDpb}?W (jDk̴f*Vuh/lPp'|f$PC*$R ѪkUZ1ijFG9tKWZqMkH""" Hz8"'wiucFU:ݕE/B1,f,ɲ.#1,K3UNEji]#]9ҬΡ3KXYsѬNj4HȦ#:9n&`#D XNh3~$:@[5RX̓$ H-ǂDjtLNW]CgW4#$i; )ƒJ/̬TO>\IV4&+quDnU:)IuX E^5*0VE6U%"E8B<HJ'XddʬsDIژc6tJU \m x,I1K9,Kp(i͞G6W:Sֶ,"Z\Щ2H.ʎFf_H:Nl%W&bѶH:N'WfkH>]fV |N#\ VЩ(Z#,*HqrzbrVCs+h^QA% \`e%X + HPPd&t޴2jl`2-2N32;$iخ#Ŷ<&|,̲3L$7/MT,:$IrA@ ICiVd$Mc U#k:Őp$=B|24#ұ3 D=B%9@DŽ mLTnFR6bdeC:4#Z($igei)YdHnfwkܬq4_Y$ GCYM=QcTY I)Yم sAq{}x#C;̚ -OuD;{6Qu摠Qp$=yFD&hqCOD"?:Qŏ} ,.{% ~vhR%;K)?.X•J}AFG~9t~FbH>:{~ք(,VB + "1k:Dp$=QA{ΎRAD$33 fT0dIbf&f1:K2GTvH +aeLJbUn^-s3"If&O7;P>SQ GX͠zH$hC4!hD`dA AtF|kXr59({^*覰uU˳~̣JY,֗D-޳w33B:?jHgCgg4$$igPdIzg=ۅsVnPGsYEz6HIdLAIZחV&it$IwAIdH:N/HAB1>4"($IK&i) ƳD\bgTTi0`2o(gU\,R(ZWMq1R3Zz ͦ9_Lφ&B~<4]G-.l&↞{DF1tzF322#6K22K˨%,3Yn,1LPTdCS2AmH>I"ulץdE7VswdPDn9YЩ(v# n] d'ju NʈHH>Q%I\e$HZJ6bC(בuJ;v FXU^F9tC!I{(M4qe5EGdb_Zp$= QG!D^1t*$,$[Y M%q>"-:Ő=#椽R_Zl2`qXoKk2G}DR1t*)# !I{,IʖFQ e-UTɻ qGJ9tzZQEy/q"u)D7YGiH'dt#I&dTPceB66D#ұcGH>QZdT˻3:p6b$BIaH>"dQ1O `)՝bTA#IU80ԍȾ:NvQ}D"X}-~Lr 09c Z'J *nB=4sEBt%# RMRև)PK1 NHJ‘t #kMFDm$C"HH>GFEdi #K&^ܻ>]yd}9Dv05{06`^CҜWBW6D ,:̺Ì,r#AtPcaCO="%:=Q Iڃ)Y[II)0QEE0Ie+.ngbR6:)Ρk.buJw=AdAq"=O@-A=hC'v# )H( cLm:l*IjCa$ !I Ŵ C 9ٿPKl5D/o} H-Bridge.fzPK9Xofritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/MagicBoard.fzz000066400000000000000000000434041272363601300326460ustar00rootroot00000000000000PKR8DE4Fϼ MagicBoard.fz}ےF?pslG;f$2jZnI6mju̾Ȳ6%qa?`i#7@u6Hw~e4}|xuA3r1=Lo>ۏ/_?^?>~{_A2w}^^nÇ w<|*]~uqnt8yb||7{u{eWz_fWSH/Foo^]bt3pBf\qJWy.k=v=N뾼ǻǧW]><^ }o?t]~x?;1&xrEӛwfU[&rG۟u6ɽ/wGww3wg~N?0U𺼹렗M wUT5eȿ,{13iuM̵ה;"g*sO}p=up͏Wï ||p_\=\ê򻫻?ʘES.uqZ8oQaYSկ(.nax?{~uId#' bψr8yM;mY/SYןu}^<k7~8{>c!A|}jNȵô(-zbsƨv7Dh#³1ig#ƨ'QܧFp(al>xl\F ccaȺPl>@l\@rQu{^ 1؎u{c6W~->jml?5p\6Ny[Efifc3PN8:vMgGhcSƩWmv㴵`2M^ֹJ=K=(J=ӥRϓ \z-ڌ4Ei?{=h/D\Ϗo-EvXհdIcLհdqE %A {IE݋a#7F9a72>a6F>adٮ>w6 ۶E^OغN'l $'lF'lƩ'lS )s5|?=MgˏWOsL.^-{qmLvMvi.Y 2NafZaIu?]}{O/Gwd2]8 ]?^Mo]9Y_>ӟwng)5HQ3 sMbd}((3.2SҬ~hrmᄎ, Ctq?@rX@:,& -w?>~bzs XQbuFv@CV ƜOFM8D)Hh’>!&:X$錉$&C>1陏 Buh1SFIFIf5gV>lET4159`_(lȎ (6"3Y&@P#Hgt!z&#pO͏=yWBP1BETfB"L1o,Tս9FcjORkAhh^O"ShXQRz G6tdXQ3j{DD KÅ~! _"Jԁ2j،5ݮ\h0v"?bXJv)n3Fd$dUOz>[H3>Xr–zSN7% ~En'uXT'qAX(AN}o۵pKϴnB̸ ZIRս9B-S]_Tr4) D&T_=BEg#QS'$rEg'ѕ(WJ\DWEt#eeݣo*{:K5l`nhɅtT̫`@L_l<ۇQG͞bڍZWvu{ے'Tf{vEM1hSŀGz;0]+ih3>7:?>=:3սfOO_z a(!?o)h# !>|-zAP=Cb9F/XS.wibQ&6KELXƈoC~ o4(fo_>OfwPW2 yd`>v8de\; 5N׋qM|-Qseѷ2Y\a2c"Lv=iѷR:}(">E 03~ a19c\˥}c S$4E`$3t-ht:6~oUƂ{b2zE5VxD 9O#=r?v'o{SfuUAuk*0izzZҐ!X؆ich.Bpv@jK'EbRVF]ץXW}BKXuXL(ekvNDE,-aM'uYm%-~ A%bH?:u3K{@W1 grܳ-33ne(KIF[щ$-Ϧ&m?7&}Pn[+G[ K4A ?}24t Gb @%BH{$|~xz=M>>>h4aojheNWXHGgۧzT(cd߽R˄ [&2e_aH ۫h)D4Um/w3Yq->ԩ)iONJPLĆN$tݮ,2VEDmtfifĚ9~+*Fw_ү󩖓rPeEJQZ*]Y~I؀ep݂G֚v^ i-MWsMaarGQ,MΘB`yYt>rbJmO+NxFQLM1:(&u趌%ɁŒ-3)` YKYUuV+;+D ~zI P*uh+hpw`2HY$q}`$Ie2W2*$2C`hgHZ[+oU(R} `8@ u<,@K]6c&줒]u.#>J$awSXN` wM5~vgLܻt8yz@, l'ǟ@Zy\a*y/8T (iz2,iÐ%eMb) =S4\22,/zoTf`eiCZ8~څŒ=`uc:V8,QD˚h G3#`u &qVN<ɤ=0Z)C~8Zaq\>!&ؽ'9̆H6\C%/[2&uU hW֟MZ0ZPrv٩xwy;ψh4h3פ7o} [ρhZGD#)x#=Q#'A]C3AMB"^#y"T f+(ZϰFjⷙ Dݺ!$ڶ,iܹqi[$3_@)$Yҹ]̱e0[;Oд-l&ȀJ -E^_ 65 ##i@Rcm#0ieg<K$,%JvʚKOuzӎJҎg6˷ PIIu@rRH#:c~$fpY eur͊ə~H(39XDe(K2rǕ*1vFmbhͽi)@6IAqӧEILPg8:xJc6s a#C˄mH+*%錰g$$!$ֶڎRQR fP )&3j3|!(cTQ8D'GߌwA`,NBF>]3c"ͱV%B QM1l$Z4zo:*YSyylqK@9'BxSu'ɵbo&E^#V}rԱIoGs#ylϣ6\sfFD""Nq6X]byG;7+vd[4 }J!ee<yC"UFy+wN[E8nS|seoZ:պTY7jπ/ tOuN&'X`؆8OtVy(e^qKژ#89%o=(u!)!8ܲ#I!auAbXDgfLh/!Ju&gMz1KgDRЭޛ$ee<q=7Z 5V*RBTdUorm)qVm[0dWeJ.p @1KRU!}TΨTg$$VMh8۲htϜL2M09΀扵54aG/֦vR"qKmչaHm]yӶD:DiOq[陸%ddV@j8$T[c Smg&QzHţ; F{B:ޅ!֫=[@[7I)Y: kyknf>E.7y jA*C*MݐTKڶhɽCYb+zsWuct/L"2.CRR9[/ؐ d^ K1^(mH;}ZvY;-܀Z{2r%[Sùe=s_He΢yb$~RS,MŎԃ goH丂 1Eɞ413O{5OeM}DIˢ9=39LF4pRYeJ\ߛ#HSf=1んCk[0IK 9_VHbiGbi޴ j{/+I+ܹ{<{4O=\^]s/F/Ww܇?_7K^Mq%ՙ%Fq'Zٍn?<׳R%l󛫏{ˇ/wW>=>///?}ӇKLKw>I$J>Gصgb8g/#2:8y>}O372k†oW@_K߇'Nnܜ_^_E—N_ݬ1 }t6{zɯ??=<YJJ(K߳.?iUjD4 Y$8P)%s,]?tWw)_VΈ*`su1iBUvah0F6| *-p\C f\TƉeZcj4#_fۇ~b]Mq7~#No=bryw#?/!ۧ7;ҟ.zcX_~n6U14_>>>=rQ_GrN$?]?FIVJ/Џ$# 8XL8Pk@7%=̿tW>ƿ%u spp,SA9fwWӟ?<=~zr?jh} ;4INs_4|_z0Q|̴{t؎tL&äL뎭;vo όdw2ROdԽ;'/F!ܑ4k'hwȉr_sOQLHH?k8)kb Qp5gw9x|3q8a^z|?O@wnj)wBj1%:e?5Ͱqc'(|N'A))o@YJ?I Ǿ q/w OvED7 odfr*޽UxJd2f OcZI6fƝgf uh\ӨFq瓚o@p /tAj/F ~wgbxV8_P"}sԽ׸&4>u7{&r'O7t(( TAc;=BqD.k"K_ BƽJ'rϢEyq\uܯAƙ>BC˘0*3G,8ֵN4"ڏh-4 `2CL7L+~0@4Bw*a|* "B+/-hy(Y8u #УIRr4m@O^i@ڸ?Rxp79EƏb~B_O3[Һ{7]'~27#ŷ6Sx(hlqlw4omR ϢBtR4@B|Ly A|LaLeoZNˇf\.< Bev#ztEO!a 5cɺ(L,(0((0-\xQU{PeeFpo:.ۡ7}|p2p+L/IL;ӈ@`~w u<)埞Lgr$ONS,d,*k. >q5ymj ^?x!.Z!RW / ȵ?6KU1y?Ee  =Dc4ʸ c.7\gb @N@AXVX9kD%EG`If#S"Ehkq;. vi:̂.R @b%9߀}p# n7k m*দbpvcjvJ6vb㵳}i>q4iWcvMXmS_5|g4^x€a1[ pv^\ O| Rt3E' Өn^`)@"60NXVwcFP&j֣8v]w)^XeB2T-Yhdi` '݆@Wb& " Ԋ@5L2T@+s [5p~9 E9 Ah*0$5z]|ؽ8BF*^2`#ط Kyج*CÓ9f9yoxL`>c?k: UA0;fS7j1oG)MZq" '8/4 v0Yf!3D87d,'5NN8hgAGc a'R!|!{ xDA14屬&<| p  f?I#~ (3)u6@N 3s1AH[`v ux1 }C\FX@ OJߩl Q8 ઼߀=ş+b hR1wQC; 62"po^Xĸ7DHm Is@[j&?A;|x h:a` JAT j$MWQls ( p(T2N=ldHdj^Gql% >Q3B+?8boo:XE}`BG =^[Փx6L$MnP+J۠[4LTA5S0zyBcF:1vsLU% AR֒|o/ g^O~"f8j,Yr `0T}--HL+ŸI?7(DE‘6FT$߀:5 _7 "*8ojBrEji'z f9`nϠDŽ(IN(.øyRJhScw[*.h{{Ǘm 0BЧr$Cc`>)b9!xDCF@/\iPp#Ψ-昷_ojv"rꇦ-:Zh)[չ#7G8_A W:!4ZcKUYF*?#iq3C᳀VBc81 xh̓.jC ?X]%;ɉB#9#p@VĄW ;EQ"D,v<+03\#*kՄ4âhIn$TE:`|g.YE0(LQyIC p „f9<L08kewY+U9Oݸ%{˸sy= {`eHM0J {%E ;! I"tpr Zh8g\PZf`H8C0C<糱 C`,iS: 8t:Շ r8<Xgr_+ FޚM0<6պrEx. 4Gm^zʀFwI1` @lCL<R3aL}b Ob(9ف&YtA@w !B& kJ#D@`Q0 8.lG8oNU=v Q5Է4}*] H,1㱓‹1P:u׼E{H4Tga6RF?F@CFL)] o4NDI)V y#xʸ#'%$Ch/E#܏ w71tc."]H"RJ ۅۊMUkFdf(\\.o$p\!bh!Sd21|hQE`GǎN06>]O{0t0^6Ʒ* 4hUAsdh-C?`  \ (%(G/?5 9 C+װ"v@*xRq(1/:MsG$0rw ݰRý(!@4.\kq1Bq0(z8X08EtG>Ff`A}|,a@4Ag8kNr'"Nn^[hCbG|#O R )]h5/C4'h*$!8ƒ-eu W<àLf wW;@`b:61@`nUk?Za\I10"8LSt#Nűk$ @4hO2C'e< TCws ASD|nx]hl nB# bx"40*2h OeA0*AcD"A8lyocp-^oG0QЂpP8 A GfxeE0X.= 'A@=^ " ;P3Pw'  pd=h""1>,~ m͕I(ļV XTZNB3 G~v].=JxIcnPgcyLM*8aG@Ie1Ie1f0C+.$8nLYB$ 0̼Ln&ąd9a g2ʜl%QC&nq@p`rlk\8'g9 F} 0yxBۘZ4 1A2d'@W4EAǹ BK {(C`vp`K&EҚCj0Z8<]E " >^O-XW_'cObڗa7Ac` uLwrb*l<MScG 9RnZܜp@f@9J@ASTf>(0p5ڤ^;MdWpi2{RQBд{ 7UD>,xB|zO 7:[i?N WbRPԮ``V,j 5d- WqL6+P(S5x`kVll=E>xtwTU:J+/AƖkr<ѺP RBe""TPy!Tq#::1pLmdF3ހN&,2 `ވh-)Se~#cG$ 4uC|,KF80H kp vs08B7,fEхWOMuB֤Qk@? O葭ÚGS<`{bXi9Qsp1딾 d41w` )*V 8& ~Sv0s[4i~0; ~8v¤cGN2oX8|^[_#X/?w8hTxωJQp*mIШIdiϨЙUYl%/hjV-[8%bD J#Kąw)r#"s 3c9`ԩ4TxTLǩc< tɔ7 '8J\` 9F,cjK 'q AV[k-Ma'"5B7z$tۄ܅Ԭ8\s%/G]r]hk!ׄL!J}n +4!ps Un8G!4 }{#X/ >3Ǯ,tGë0:KȂy)a%3 .BO7P 8 .C*[7U2!l/)vi~o.~~xn}pO7AC7_OW׳[-kqfl8뿫$w)m(Fӷ;םk.U95Bk7 1Uv.}X1<(bxBzvS1<2rBB5ԼĮW+ Osa_iO݆j4W\wjۢAV*NCJtf- MGDU_\U|qCvwomw78"V&3[uL[ ֽn1+SǮL\m1&E{Xf{{5XSC'cWuNNSi@=tIFt@֏._wTh%冝AkR)ةD $$oÝ~VlNaw|R5lN},( hq;JT"N֔t mr莐굔M)gFlD븑 />J;Z jBp56A`&;[~{W2f WaH5a~I UT L#G H!k®CIb~Ù=3?L1?/(!tD2^/;T2)ƂqB*D[G>D'7ɶD䘛AOgK-bV4r#Y9k_FkH[u9YP#zӊuFxWX11(8 /#L "`~UQaqiv ?RC I*(v`HBA_>m>=hmO!$R6CkDCX8Y(eG6&s( *F1YHq_9[bMŵpZh8")N6l\7?./89d=C4&Kv ONd(VUr|I ]T+%>pbGе[`*upSg7jȕjȕW7fD"&(XLyf:H@L΃5]N3vy)L%n76Ό+Mb(q1$,iG-[fGegZ&vA>MB)S4Ѵh G(I-.ڶEs)>^m[(͜_Q"' EH'1ÅM!MDtHjS۠`H+*Fg`IMC'BMia'. m3\IluTI5AwoE ;RtFy-J:|NРKJ[]ٛ\bK,{U)9'IH$-7l)4C& [Z%7aC1$LF2*W&:&cƉ.()WM[]= q=7޲E7t2o% Mql 0S%@x(]밒}ZΛ7.'&@429r;` ¤@_so7_\hŘ6ܮ罋-M{'+~=`֝D:ǐ[GnѣScij)'$y؈"gVxfy̅{;w?Izoǒv$iƔ=4[*V7cw>w1`[D^-ӎzs{LX8[Ox[뀒x[`rWrqB?hΧ41Ω/H1#DHk)X[%?Yna|΄jue J%W%mZJ)>KMUVhi 4,{J^@_xbؕ=;csZg.FSeG:Jٞw#܀\).m7bV($ɶOlLyjς&O j$.f$\7cNX|O~H|/"w jς%~yNhϛ>0VZ2'}F`qy _@Jmͩ&FCTXCĎet; U!)7ޛSƍ#^XXZlmF2J.4HN)?4ʁ ÒxcBm!l;$ :$ %5 - ER]a$ѸzoڇD78yB1S^\fkST Ĉmܞ=>"g黀i4Daӫ.A%Ѹ!%Ѹ.iM %p-RT.b8Itޛ!%ѹhQrZaa љF ]bhf#V`pJXA(cJLuJZhi6,d K' zeal/!%] +?fs:Z$t~V vh*J{]c9viBGB3kГpn 89TW"mfc~`n]/Hpp¥3sێp D$zEGY_7H {>]-jܭ犞!3MʌQ֛Z&vDdJV-=fs_ZS] wϏ.>m0*7gaqdTXP]Nzm s6|>c+ܺsokpq n`t2^Ui&'7FΌ2 g2c6 qg%o>w}J'}n;ጣ5H¢6cDX ]ȴ ,y}`9-ԯ$?@I`VÐ(ת]zˣKw}zp45WiQ,qqcMa:UM>}2;8<촸zsbY%"u>}朲װ+Ef7vȹSgC\!*A7{'IU9J[gVndBӆQb]KC$~a^x"ulę;DaiWΞ(Ŷ')%$Z .iq;W=TĿ-㾡F*{cGŐZɱ&۾DG)mF *q:Qz'S[>9>YN+x_Ҋe0rҶ+'\c(C1Ul11ټrXI&V^yQRA.)rYL'$$YMR>D0dެq&Qf0IÐ&ILXG!ܻW5F*m,c2\ٍUJTf"qVa_D2[bT&STǬ}P9P鱝IJmJ7HhLze 0k,{Uz(F4JmQj;RYZ oZ|urPKR8DE4Fϼ MagicBoard.fzPK;Ffritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/Matrix.fzz000066400000000000000000001565511272363601300321320ustar00rootroot00000000000000PKm5Dzʗ Matrix.fzr8v.>owxtWЅzSPVP]q?? Wo>7.>޽+UPG{駧O7_oH_]]W?#O}u||o/ txa?\7.~{W0vwUW_n}q}~s?>7_wW։}zu[x3|z|_E'@GL绛qo߼?=/&޽`?=?)>x}s_ &jxw-LwY-?>=1b"w?͛c~WWGV몰}_> NSvޅV7Z%mRޑwoUFW,ɮۧ??|z/^nﯶ p7W7W]߿{ӳo_&_'?vvO/݂իë7}x* -W\7+ZyR3Ak,j}&y]kɶx&I] :e s9kd.j}&\}GZ@:$2P ouNZ d;Pt@u_VHjh)[q윲s9ik:sE1gnSޏh5p&[l,YE/:e"j<}&)\%z&I\)qU;'r#ojI*W/jcQ3I~H63IJB)H $I]&ugoĮl$3IJB)v`IzW/`M6Q `y%s;SY{Jɕ ɕ*EQ?r\?'hb]$eh7`'2-N:厠quUbI+wbga$`1T1rˤ;ùN8%_JZ;U1pkiʝqI@HfēV6#VwÕ$m&e6ivq#$ {h$ @R0vI(Pq3iґ_f#%-LtĀH$>F#kFA[FsFkNrШ'mo37zSMmįgmʎƣy6YLR{:'4ΛЈ_$x3&^I"c^4y0:g0Λ_$} ɋsCzSCLwTrNߙ,!5>8g_/$QG9MqF8wHF/'8ىg|}'f ř3S#t;g)ΛnIbhgnIb/nlyS3Wm$nͳXLA$gsY)-v\7s1>^$g/,w_\Dg֝G5IG@ߙd1#6><8gɘ_b#qI6cs>J&_bcj\ʧw?Wxaۿz}zӕ?5g;lEbS}sVZO.OS2ٿPi_mto>s KR:맋7Vo_=ۛjow5|&Gnx S ~&*$|jLZ,_]N(#b8舶$4$T@"g®m'cwv6՟^ i$0(I ã =kBHiA<.J߂ w(ƌE݆a҈◆bHݱ>mNx(Ȍ  5<&X^teY⠗L1zigGC&on\^_ NURl/ =>4 O@;_.[mEuv٭]6۰#Yead2b 6x1帥A-Iy-K㖔j{ޙOKKZDBٲKn23U}Ù]z|yhwh2]-;IZً~;S"ST2ud9hiYgF+l$ 6x<ӵ[gZhR}!uhrS0kDsm$YQV$KE^3@wDSh%qy怡7Lvҙg.4 ɫD X9fc*> <2ǜ0䴼/C2vhp~ւ*$KaRh/UF2-jŷIEdl)/ 3/ =`4>aоrwI\Ԣ Gã{dvep!%ozw-QeMyˤ*ߘ8_*ݽow{_fX꒼}'5Vj- 7ǻ7Wo?>ݼ~gk<^ 5w%S}=`m$t.>;ËO?}|sO<@{<+ۢyIGkfњHQ,AԱjIW]E 9e #ͩeVx1uX*l-pXٌ7$ ^MU%Jd|7DpEXR) K%ohqt`}֝*]vXJo83P1]l5Uj+AJ* 8Lc &cZU2%6$满SffIJJs6u翡3X W&V4b]rvwݭ1K OR[h伴nhuPh.%KV?k-Q5xVd-ѶhuI7| ~~5Hg׋ʽ1(5zjS}KͅcNLTOTa'<%:67 81Vy@:)KKq=wf.aס-IK=:4e —V*aW'*}ccˀohYNR޵. >]Ŀ=mrTm9jPY4Wx+,v6Ϝ+xxD#V[y&g!ApK0S+㭅͘QwOPVP)R8|Z+(ъAMna:h&Sb#q +;tZfYKRH6 ׼yPuw&tlJLաϸ{h? ٵVs@z!uoGg%v x>{#3H'~E:nR,;rt_䝽ӽj}ozI ؄45g:LOǐ;Ūz[sF gNa \`LW+:Ӆ*x3=>[g:#^ ;_ttp0 *vtz eDOlXٗmY`_:-g-_:'lߗmK/ts}.6;ӳ3L}:ӑOtn"#X%&;p;QKZsgozHfj>w:ۇNOޝN͑ޚ+li×4+u>?]B1Yֵ!kcIk򟳺kt ;<.vUPǕ(\Yqqa(ٕF /}}UChL}gzhgzIts3tLt${Oou;% hi7QW]4ނtF;}w^ߝntB vV7={S!M3ӟ`LOߙ=7}~G䦗+5=in^MW/27XLN_G}.:9} UN8eYыno:m]ROOWٟ8vgr:N_'ϼelyKzyM7{өԬ=9}|O]ۗ?r˕tPvP?qΥ<";ݬ ;G邆tž?=C8ST.IY !ELAEOl0;ݮϾcqVkbvϟt󲙟ٟeO~:ԑ4 u~[^o~:MoI9<ꔜ Uo\Dezv]_)f0tf٥]C[V)l᧑2v1zEB+]ohQ =i>t2zbǥ>.utNO˗:g-HKS٥~]8^ϥ~&Iz[̽ԱlzKq ԭ٠ .u]0gdz;=_0LQOvaNRf(ٖ2uݴUWk!$u ؞K`13VDR.K,Um m;1NarVsFח5K]/~xv#{z]s,u3Mœ׫X6%]Py\.uN%UHfz'S}j CKw٧ SgD+ԭ1[ǧ~^^:w(T9]xQrMDm}Q0ƥkez@pIo=IX6<ܒ6 `nEp_d7N6& &#e hf[^o8MoAylz<?=>]M ?ۡm.j/Ty=nb~䓩av_/gt%-ۿD(~މ k+=]|&lo] Ɯ9͟Һ\oMګIZ `ϛ燏[,Gy=k8(-$1@j;2o `9 \0!tB\9'=ؐ9GžaTf Ī#d ƹ j,EBKib AppCIAgAˊiVs2ӗe%,脋OK`Z5T愎ӳt_S-VHǷ(`K0Zʔ4 ')Xre)WggRSu4"[Ru̷ָ-nurيyg?uxRHGEGbq-&9)tiJQ+6t0RFEfODZ|6K+$a-N:[J!+GAXsa_X#-8/%I̱`>9"sk Yd5cIv%.LbCWp"FJdyu٬$YADTPiϙ*LwgY, .>3Y-HN5:uT|9ST愎ӳtϊj h _ u-Qɔ+6700όr hc9e\}|౔q[P ɾeB}L$B3:ɛ.c+5$oE|&~B ^o>S" ZH[82vRɴyrZl픆9NY/&.JQʰ&2ʂqQBQ۷xhj[9`̜32\1$Q' r2 =WIC؉ȂuIZs sZx9- 9߽;{ /eQ*9g9^φsR>[9`̜32\1$ QrѩsF+$"l>!,=c0rRYEBi)LA2l*4!^ PV `qú*uw혵TG;BOl'C2v[]'dE8{cӀ%Y҅PN?Gd%az)k3CNg]U$I ! NMeħADX4uIgBsewgjٴ\D4`ӿU.7J.S3uΧ4N; L2K: SDM̊@F 8Y@9Q# Ź(+6Oe< XNeD I 4X 0cg1^HƗwg":1$-2EDML,2tC $ Yd&6ʺߦd5&6oXl$.0\s1̢\Kgg\RUK|J%W-eE-ff"N,}Z\Bp6)U rP9w]>}2Hq}̴m:."^rΔ~@ *0-Po0 ^Բ/g `3@$_vL% `CNe/#y 60o}lJuH6-ׂ$*PARI "sfdf{$BZ{ -NpԇtstnD0fT^"n޿tpȉ-pƆCG&ɼDXpe{r$'f_<[T5iPFtsk67TтFRO5 )}{T#wsJe 3r(/NO$E݂#tΏr*sE݂/t.LPmPQ:_leZt~ چ]l,d,>CDtܗ| @P4N'vS,vf8#%AbY+[8)[SDҾYO9bZeJ 6+BOٟ+Y"i퓙r^a F'Ly9N/':<60JD2wH$I yfUo L!UCӇ=_б~؉!r/:=03>0,9;lA)I3f\Y4dYVf[3k]X炱hQSrx 3+V=hy瀡32)YYD5q1J8鈧V2<]`39 IX҈c!=tdy(iGF NM!dfsǁ4q{k y~xeFM??>X~ۯ_W_ti^xm eRI;L ڊǞ }PkpL=mǎ-fjh.2'[n;v) ywvY  :!)(g#džN^Oδ=*N94y9a5+y *vGiDmh*/:c?$'IsG%HC;j}6{DNG2 cHJ8 (S;qx%- בт*a,4w*6޳Zlhˆi>Jӂw,! ʲ(ţBkQѴf6JN_X h0du6 LbCCc}/!! ӳ7j69㴐5^9 }53+I753qPi}Ԭɺʜl^IG҃y:@D $Љ13 eXdIR&e)c\B"q8. ʤ,:tzZ0Lf!enZ5)s'Uv5݀T\iH$IOkD#ks)fdn:=)x$cb"f'ZCHd展g!hVwe6EU7;{fD((Zgы֦hh$IhXpdd{DNG2S bHJ>a%ڢ'eXfp05ZJe mԉ%uWTmUZthİ\z\DPz0tA AʆL)DujdڳDNS{6MbC#E&b#Y-z'`hRAmMe:MOIGpȹΟuq`p]6ӱUDґ`~@ 9,6tz)R IIGP4,8 EctaPP4'nydqJI8k1F 8Xy<Y98$IqD#3BD#$sqGJ>& x,ɖ(8m !$Re(ʼ,:t:/c8O MIG2ҙ.y) b"9P᪷,zh}^Kґ`^G6DDC$s )x$#Y m޺pMqʈ. ѼFN9ZthǬM9Cς?,9㲠Dwj8'ZNk^䊳шBPTgȉvHh "Y7 ,‡Q+-A3yT2wģW@wImOujM35;̪f'.$#D* ]w}*yA E}HUEmɴl^ IG҃X?q"'Z!ņN5C )IvEdg 58ځF?j$PH.'P!E1wkgBiيrZ"fKdhYcf91zr|!IXȧHCN4>2# >t#qAϠ.)2ȡIyXF'e>:=T&qbJ>&TǓw9 '0how&cCɐG:&CɦY rIYl0RIIDŽɐȒlK\ꀔNZT|F#[kAdC R KD!u媲EɐBHB>5GFrXlRH\Чfa (9F؁J `VaɬڅQWbV:7`"~Y J$d4d:>tjFPɐl"'!ņN;R12$ 1dab&p3D Nn;9S8RXeN6'HNZp(qD*XѱPX1X;D#)18X-]>&Qp0t>8 ˊXtZK>3cs;IVHN Zh{0YdH$Iװc՛O5FDc$2𑒴kŇ1AȒ M- VC LҊlLPR43;%iQRWfh($;Hivç O*aE3HWaA)tkDNF2G ѐbHJ> ,Ӗ.3+Q4#:QcZ13O!Rq}g\T=FbK hGY}@l&kC'pI:IoȉH&j3pGJҮ! I)=, (ՠn ͨLϢC'wg xJF0nvRUơbZ3f AlUhgxfC'6 %Hzh 8 LbC'7NJ)I{x ,HJ,98Ε%-OJP4( kZX3Ge0m>еt#S~A.Ig-b -5@iԓX#L0_O^3d *k*-RU !7,va3hkX5iȉvHeہ E]Îާyoe8鷥Y0ΡrSsB@Nr6ڿYth,C,sJFM\߼,2Vxr%,fы-ʊiXCQ6hdb9`̪)X dIbV.L̐4oEJEE(L"-,g19= )InJ3)Ҍ>(ӳS+͐BI:LѰv5"'Z#ņN4C !)IFizYfga HQt9 v5}0Qiѡ]r G  xTaՑE;Z"iH%$:b Yl"'!ņGE&jYQprJa_ Ia*IHKo$_[ugb:L?$mlXTi2-k: +#rY-9ɔ,6trVHI5xf)CA?DOvdK;s@ >)I[ +1eT}o`D/Zat$]1@D$S?bFJҮ btGAr;h?UYf;ZYd֬02+J 3ً@6m1ӦEnheNJґ 3,0(kDNF2E \aBRfX`dIɑ=g8`ڮqhjEvLI';%Q0nΟ5@iXZD3T-p&Fg>JrYT֫0C!4CD;$~fɬYaB$grariO RJlI B2/(W̨+IPE HT"Rc4*E hoxhC6"Ŕt$]C85"'Z#ņNpD)I/"rEf|an1CIW8pR %]9Yt P>nl|liǏZ_|G/Zy8BVhed:) …8$\ja…Ltr '~#~ce6-D#s"_8a#%i\ r0lEJQuk-4B΋_{Xtt2se2v۩da'(rXYR IG}a4l8lȉHgRHIa%b =(H-;ss9YK )Iĵ6Ԝ !/oL,"h9p#ɉX@cm6ܐ͍̼bC'%'"}Dr"Xy-G*!KVFdb_K3!"3O %it/:1cMJZW:D-#F7\}NIG  YMBD+$a8#%i̐ Ȃ̬$ 33oVת~㥢uz6>ӳv$V7,*.p&,r#E3(c4يC1CNA29 NΐGJm AeK ypi$yc=2QJ>"R֦5a9H YS paJN8?pFJQ^V^\=L> G7}:/P20402iȉ&Hb3yč= .J BpHBdz]Gg>:13;%i+U9۳Xf׭iHM #D.3Pt8葎;/rMFrёXld6R!1,б$[K``5 ^4+Xui2\gBW`.1.[EExRJS][f:9œӁt%S<֝aS Q'b 1%OJ"֌3Є6Z0f9uh?0"ƍtL,z `8#I!&v!'ņ É)IHY 1fsP"g2:C5NUJ>&Of,W>T,z /^#a0$X! q!'}ņP;R10$  7Jڭ:"` l (pűB vEviXlC6%iAC>N^0-#2 mDO7>zb=R$IGځEV&hs#r9Zl8RIIG05,@$S 355RP:t"2uv',2 +7Q ffݹ3Ξf{RRahَ#q]dcf:t#a!w1$ȱ:grȉ&Gf`gf$.N7$_ja0FFrw$|ƝWwIBfrDVi}/!9c,LBBnnUإZRZfRA4Ѽ5:6t}N,IG9YMDDK$Bc8#%iΐ Ȓ,J f oUɈ ǻ]qѢCgh+>3pmLZT=RiׄP@vݶ:S i;ڔ2EYRx Ԍ8ѐJ:f-gȉHh)RHI,SKraf0P4(/iȖt望REFw,:tZnIђ>1LDŽ)8RP"Pي%5+̢-fH#a!"acM9+6tRRH\Чf$_zY% U@Ntr"NHLIǐ0$i0@h'jjyX181$:`iFHd3pGJ>!yZxYV[Ke25kNf=jyhcJ>E*fQz\tiײnAe]%X8Q#Iwh^8q#9\,6taeH#%iŰ`Ȓ\l20rĢ6ewL&^"ΦKĔ k`3L66:,s֡C^$,S]CCN442 7I)iW0hF:1%^>/ɬpɘtzw(FdLD 팆D#)) YIA1/&X]tDN4A2/ >Rv? ! 2~Y L?Е9z XN IG҃YX4#DN4B27 }7"Kr 4n1 fڀN LʢC'&+5OLQhŖdE-.[JYxB:OM{ёVhud:-Y'h$.SS003؆BjXڷ1e&:ƙ5`c8nvllhΜ}ԖH$I&fX`d9 -6tzOiX`d)FKF9?T Ͽ^|x+7ŗywo?TTYGʴW6/p{su=+(U07yӇϷՅxOO__~zxϟu??#\a{L SV}L4LsT r?Ӆaߟnoo?5A]lp_&>ݵ%>t߽:nǻooއ78dgb_<=?>v'T~^Ij{&/Oo*ӳέҍÅa);_ދ#Motw=^+uh ,J]Cf24cnv%Nw'TX@{F0- S=YZJT_v \|}|{hA~wqݧ_w߿ã?~o.~/շ?|{?w;D??^pwte^"V>6?~}[/_KӽuAUa|?E~/@rǟ^qqh"J)xV- (hv#̄e-/ڧ;+g_9ݲ~Ś777ӧ(-wMu$9 cor{BDKVG@o6М^;j\Bc_>M*>lx%FgGKFWRd~@i_rۜ!j.EoXK/ a5eŔWS>s?l,N7TP;s-̓k*nsצ7r;F н_.waD"0?})w?RjweA} txmodV^n~(?DD; ^^Gykxx}C/%sH}]2mǙKa n:tQ]l5_}d^O@?L tb*<˽7`Tx?NnG탱7W\_M7XWV4n6+ȫ 7nNI}D.DnIn>Jp˽XoVv"75eOޜY탯ܻdr咚B3 -*[Ľf͏a?ڻSuGx4"[ѥ \W ~Iݗ 7H_zP?R ]n?үOs._ܡ=G9UG}G_V3p/.=b2`Bsc)= ht?/4mt*JfI<~o_1~J7LiIc'R`-Sٯu᩹桞2T,ᙫ?ڦ * Mu#N??h@h )!nk!ƿ;_S$@te?8 @.@2i]"CW˲s{e@1t2QBtnl5ؿnک0|6TYQnzoN>AQ2,p6a^ݽnn{s `nnZ/i/S "(*7+K/ =yO~zkh7r韃N|nvB `ȲQy^kᤪwxڏ_WjK$n{ӛVg_a<౬㱲֐ݔQQ&]vn{vɚ)"Ek>[wM(zkWKx]oH }F5<7?FWp㵩=vu'M=ӎwi1hʹ v7)L;Cϙv{Su-wm!i{bb%N0aa1oq+pR]C sGWo(q.̉znNjS W qmZ+?Lx[&_U\gmecԶE#kY4pnVPֶj'v+P[j dm+`6?gU(vv,B][`K}p翎yF*ȽFgƛm;0OսSeo__fǁW/a1E΍9o@3 }ݪ!v35SPg^R˼-@7_>սe=$t!-M/voy +`;UR{u7dn'K؜`ׅ% _蕦1sLKٞc~= ؃_Wx1lM3y;+k\7*l_E (3SR;exLoU w-~w t6 %H󫁀okew,T1rɯ"x"8v# v{j;o=ޫc`h9G( xk==e:G[P"  cTrO=qܲTaCqk'_(|t藁ڋg5R +x(sL64KDyQym۾v~lbuB =$ڧZnדFUM j׀[O̓G0)MP3W4~(¯t?c@ۉ3N0$xVŸ_XrBR%~֪Qͼ,ٿF`adKf e7dT}-^1 I^_{@\֡Em(oQBv &Gn /DPp7t+ Vl ^[9x =''`w9^)< f>3&V$ e.=RJѯ%[\zZY$େ__N@Z)>} ̛^bY!8DFn,uNGzG&KUyW_~gc`3-NVc f%aM"EeߵVoalpiGF<*&^:7`0J-ƻL> +o 0@@Om_Նâ`HzwSs]z/FE~w__XRyRb  a`n 'citYGs=u5/ُ[3ȖE׽geqިP'-~pԾ)C ]l@&KMyyN <~2RC0xyƳK9_@OK)GFOb"ൽÏR N]% ~WO7,lO=^(+H߽ OUhQrƞ 2 C!h,eg  .[%_r@]+ȋ +W*v%UIS5i?@қ@~_9 ݰ·VR"Dks\uP` ~dJX,EѼ  3< n_ 0mAC0i;A`~Y`]i'> 9"`&PS?1<0O" I:w 7pA 1 ~+9.@}̀կw l.!F+98 @r h7mhSYl}/$/M!AE{ aWR % *Wm!:wc$0_Ȁ_|W%Y~ a%ln xm2@#l~L<2a/|~ 06`  ;P3@~xAx`@# 6. #ÂR F\lT/.$A`W~0XۛyGNQS ^~_פ?cvQV10 `쏀>&* k"c6kb D{3L?sJRV۷.]E5eH½S_۷smvlW| =V hح[rz껕=9 qx.{.a#@ꉄL# yxO#mC6KȰ2"_{ҁxf+ zDH| Ba_|d!0[;/]4C 3U4c^r>AۍeجkϦ!Q`ۗ7/F:}_)ةKVr*5,9+ , A=hR}S祡xGqݣpwXm>MM)*AӝKȦQ'_<`!oM8? "z9'~EvMpu)6;UU WؚE 0pu{tikJ+nUT|oem֬~t?lL/o'j/>zg3va/;k'/T!Tb;2Zj~Y*lPyM.8X pxd3nʫ=u2qmZhtba1GO|)`GkLdenn KD8 Ɠ*jFR vn0 R]PDxחd<(Ϛ.jяza7ٹa{=5r瑅6,b_jv}ۨ=8lאuJFbɂik7<&K7ddN:`e^ݨ9y 4l? %$6[ ^[r#s~&;FUL)xX TQf E` ^3o)/ VdvèWǰoV@XU_HVEX `]C^>|^ۅ7A~EQ_5" ^6עEY[;ZZiqRZؖe}ԷPqsY_l%ky7{E셰 l#xЗa_ }ھ4_3 R V0M8TAbN+]_#;籖dž<~E}}L_S-1j8>7J6&hf:=?c[/Y Wpu(8P~Ww۲5f&b~ D.1p睆:E{9˚VL0aM_ԪL REOa:@UO$þ㧤w_AN~ v޹W2DXY$>K(!'!7PmpN`A b@? ] m„a ,jVA.0"E_#,zۚ>JYbW4!psUqBhԯm*  \zi0kV↵. .]0 0O;4SB%3 .o'@l8p\<)!:^ L;x2=ڮ_^Oﯶ/^nm?^4ws]z;ojo֘Oבgd>]JE[AG6ko?\Y6qb;7וVnnp=smD^ɋRm?j;h" V , T"@/}wضgU}Vݧ@Ϣʻwk z~'HrHJ w/>Rh2"#7k pz(sv|nz;UzeV7\QʄjmEMV,Ϻ[P+< Z 멕lx*juJl0/Yo,Պ%VG\U!d[ɔVǠP"Wh0o=VmNEp =>!xCabV7[RU h7, `ip:0+ǙEK}ճ V+TKβZUQV}'y5;8(X.M#I*"e>ۯq4;ڝFxuͽ'+g/P޽{}7`AJtuѦd; ,cvo>[I bOpP\>S"@9jp;z;jgLd$⌤8#̤&H'QiGHây' NkJRȭdR$P;Hx=g$aUGUWo`O =Ļ Ysn^ߤ['ʄj?nWFoG1~C>DTi(uSK]T m4w}k/,aoMDoq=ӸntiJ^w:?]|W._%LKDK׬K׬*/֚ӥ Uui朿^]Z.^]ڿakDt\$U򢊗KʾPgj"t{^Wŧ,) gK Tk*V*-Pfڋsz.-P[_[PYgK((.(A,˝-ᭆ%\m_oGJǛۧpފ?g;(E5QCq'# Vn/p/WB(8#9P>w4^tWFśk+շ;nB=[{wOdw7wϿFc[F磠AzuB?y᭲k" ؍rGO w(SGt:)M![!,CB&V;c!$xicT Jȴc`SJ>&y j,e9c37mT>n1EemW^LNVb#wxX&wT6ґvTct5t"q\n7͍c۵ !'4$#*ƒN-~]~} 7<ߛHHj̮;;3,5\Æ(ͬn>VRJ 6Vw$`Zd>ט,1V$,N'V'X@QvE_,5mg՜":;iXfiAbph{gc"XLu5OÔ> E*65 IuWHf:֘ʚ5(A]Í}k(\1ʝȚY79Rv?ń 22W(#H.o{eH._a>+ ENgeZuz hb933 ̐"I:l3 ''{wؑZ^/,Iϖ;8a4~%\#\,79y-*uIc#cLÎL"Cd+ℍ|ju`jnjUENX  LL/L4wlJmX# &6ڥXt 2ؘacHߢ$fZ=Ӳɱ1 s3$#@vȉeE! ")IHdiEYZ=T҂ @͊N-:t:GprqhK;'1.&fZjiSyR IX҈EAG{ޕ< RH\ڈdIntJJ,WoJj ZJWRmjl*SЮsD$0:~'ӱBfQĎ30L"C'ŰH:B֏) -RHYd8ENaŌ}D"XӲ$f f$Ł1UBjB[kTR H98SpN(ڜl2x>F VIGCXQZ}fQ@68RpR<$e qs9jp(h0 $,uS8_. nvdX򕏃> vp>߳"^}B5N)PZ5PbrkS{r|Ylh?@aR<,$[>gH( &v{L>LC'XBufxHzۮ‡fKe:1І:ґp6ga-ѷÆ`Fg :9҆9R14 z,In9b=S5 6>7S(μ=BnvltivT_NHGC8j>zjr>0S(NHI)XrQ0H(W#כ`f7u5̷Cg[l|k%i[G㆙fAe:lČt$=l!Y R$"3-h![HcI&[ j)pY*QH["oCe:{1ȘZLJME3͓YdTFIґt GN_Ӈ2: ˧D#fHa#%i׀C)iA˸l!ջ!ׯ'a;ZqCE12rCγSY/6672:t@FB>5EG4XA ,k˸&τ{Y |EVo!Z3ъ1Fct8|&^SqFB>5EǻZ aDL"Ceℌ}j{Je]]m$< ]/Y`ޅC IWd ɬ15]8#I^H@!4(ׄbL‰)Iޅ3]t:C )I/"Љ!.x'bXc?ګbX}2aņNma}D!Xe-8X3ak2k U'TG3nX\Q/gt% вǵgZ:5)#Cpv|94݂ƐņNV';: #惊(踼6 *wJٿ );( E:Xq#aeYS=eO%HUDG?O }:|YbD<>ڂG5B'ASm汁Q*_SvZ?U cRNbDU&<>묪Wm|k/0oְhB>s?8W,g=˜,)1!FKV!tS}^Xc) u`V1&{\Ѳv!cRǑY Z&.vAVjgDV^BOaa⢧lq,dt,_ٱaonq5wxWbZ9lx[/]m>2r%H$ bZpK{0-ڈ )خi>pǑ_Яip-}^̨]A]tOw߼;Z)`e]ٿz0SA]4${Rka(' H^8#(ߙa7*l0Mkb) 8 J1a 1Έ-X/lt^dXɊk`ɹM?_{Jt4#ە~ 0( "PM-Q^eǶ;5Eiz"5 ryk1 ,=k=]MZC[=u| V WZIOu-̀P!VV@O7 :WOpV ؟m1C0P\*;q,tB!b* _#{ !vDqfB(ٺ0O7[ 3.8hfP.==aVpիzua⢧l.dT+[Naa7@vGU %k0yS|-i|X DBG?N׿͂1 _'a 5 =e;Nj_ [ s o'0*fou=,;X1t *(vO^ClQtD"VBk_+ҮC5!cyk0>H5ļNZmyCf_*R"4%JaL'Z#ƑR[J'R2Io^ /oųBOa"l'xZ[uI {sVn0-//<gbF3CԈ sֻB!X8W;4r=]xS+(=\g*R`bD_WW֊NawIN04Iɽ=ص'L`O[ͦ>fL3h'[m _¨,For#R 0S{ 4jzVn,ϵM=0ٳt2-ƞugtmY1ykF,P^Š sxBIk/o*)j t%GO#\ '\Z¥1˫EӾ]mx, W1,\*|| I¥zaQ1#ҚpB…ʋ~2U`XѾ ,ן`͚5 =e{f0fU fYJAdZn;ijg {–njY;i`*R+['ӛ+[(h.ZrqXǢ׳@ySwԳPQS|eѲ ؀༅[LDI,ZPhiQ=e{hihFUY TF?*Z0h/ZKZLƴl8B8>zgKtEGO\0\0˚}R>C%`*ҽpa]lX#М4"'{W!]'%0++WR?[,.سtEEO5KWV,XN:4U1VsaO֬V'":RBJ ~2YJXScw^ɚ"wPV⢧lk֪\U ZWzN~yZVPZ"l︖e6;j>f-,@BE?ެY(hY`1 (Be⢧l,dԴC,m2\4e=dq Pf5lqfb,@BE?ޮX hX{Xaϼ5@qS(2j*V^a(r,YPz!+`)%Kε<>;J>fL- dze]gb͚+Y)5 5KWnw zOG'Ta>"ON[旷jCw c*+[uOXqy#zQ82_1Tek  oQA 74y?kJ6W@SD6Vyx6bvqȹ\҇V OkROkGɫ5]{i`*n}~`qׄ]ފME{RcWdAG^Z|ԔN$$T&ux]Jvjb>9i}u hD5V!ZF?\(-,'KV)^FOaZ(ШhZiY bi1K2ẁ5F?Si5:GfZ;i7@ٴ dF?nZ h3c-K 6y Ǵ@QӴj_r%}U"Z'7h-BOF_WP`\-ZefDR7S֬B(Y0qO"AUf= [<EOsr5-0cA,Dw`Z:4["6z)7]̈|YPrK L^Š [Ya`l,fԬ-õeӃBG7aݻ`A(hd1jx``zzv%Ԙ1zV R,^ǟ~?~Yn +0=3SȔHU~|`.tbs d2k ('+\4sV#liPlo4 T4*[Zİ bEq酌\J}5ouCf0zKCپҨL,Z--J4P͖F%%F tK%HOai(iiaiJ 3w^[-cYg"/|_C4Ignヾm!y3a 湘V|S{M%IǙHsC;a%bi+vB(@yRQϜg C2V#cgCOгFSҧ~I=S l\ mAaag|;8igU|(0AqP?۹ąY!;GFHM;U1 vv 3 -ǥRmL|\+yIY ROqQ6  PB*U4Ox~ %.KC(i-i%G !);N9`Un4M_!q?28J JlhPzLb)[ }cpVI1qCJoj_9eH?ff@@0 K\Be3PvJďJk ETXU}v\ICDڙܬ)ۛkg! ,jaLiZ;eI?X;H{CG,q=¢V@S7p0RSj,M_=-a32&vx&,Z1Z; ;@$3wgh(/鵳fPZ "dzs  jqb+vB(xˎxN+;p̑7)4p{p"ؤ~4145G5}off mexsj9;>Ĉ,J3Pti` L>۵ UY!ng<(fa,fBmb{{ldc qT%7Wc3+Y-ms*i7zTFHrwHfDɴ3nR|Sk)EH?_Y3Z[} bKֲB((>zʶːRle/ ^f&в۬eP ȋer:nfiD' ON%.HX dICGON*KZ4mEQ=15sK/d3#ULO5F4PI ƒVeI->ĒJ(Qtөa B(]@S~)iQ"H);f46;Z1BdYd9#e3nX|4Lo.F a?+PDHO^Bʎ-m5oeٶ8#I,i i zI WWJ) gIˇ+h('ӛ% #%NK\BOal4Դ4_KS;;]n/`~~B %ǖ0M-0,Ml_]1ǔw BS(UlN٬gP2Lo3~4H]X2J3PzBzzfgBϔΪao5*ES?;<z{|Y~&A\;,d3<*;bMˇ5 %dz`4r#i< !=e{`UO }MD4ӌ!77ߤ89CjS%}I$&x=>K(S5gU^@AO inhtlXBJW4P|w!5h1,meƗAI(~~$!g&CO+=c9۝m`*Uer3V|nɑ3 >jX2լzaңlʳPƎ(.h;Ҋl&pSlVb jy3l;32MHpx_?k e$>'ԪaBL'yh`j4}FƆ%.BX '&>zvoTiCY$HQ3Ιܾ{zـĞh Q!2 cPL|;ppq8,qBV (@z{f()eSǀ e7:UhCvvLҧ~]ӍQayce6HADc?ò$eG?a(h=^Pz2lP/nTT鈡^%C^OǷ-( iW>XĊ\,b 1N#=&;< ,JBLo30 `470cPz) =e{Q՝/DHG):_5JLC.JaU>u1XlE{XDږ7Y>,g8h}B K[BO2Lvtm`~4TFlNm-n6b(&{d8H[#CdT1_`ˇRU dzIEn4128,q>V(@zc((ecݵ R" #;-R0+;|J20NElm?(aC2:}kZ{To.@7'C,q~V%*Azc(g.lRFco9)gPz=e{F1G)^di<9Ɩ~2P0fi_F,q5–V7%HOai(ii K^0 {ʚҊŴvxߛ-JF*SF2--N?9eCU4PU443:!gPz =e{O "5,V3ۇi#[p1/ACghRҧ~]?3ƳtgD9Jҁ^],iK qPL-v %&C6;Gs;#B,qrV=A0SDjʙ,gC ^\Rh`ŽuJFu2ӫh%C[Olx{W4:\&_;i755\( F%.JX ) A^Ѿ U-`ZfDjex)'5}oi;;Z.^+hMqh\X̮3TQ#mb6B(PSwrDHE;E ;, t: +G4=hOܗlh!WWѸ~M $dzP㞵%EX ){ "5U4%AM0.R,ƛۅ*Z͔_X~Wg Z{AÅH?\FC]K\BB@ SwP(R*ϬV?fq`לOp5b(0E=~frLy7Ѡ, %dz`ҾZXj-Jl4Դʓ4#YL~~ W7Ҋ'XȞ<Ȱȋ--ÔR Shta2Lo3~32!g =e{P#гE~VXgߣg%CO3#=e{TLItM>Ė%[&JvKHkK#F,q5–V=0 SXEjZZaXڍknv*^s1ѴMִiivTҧ~QM܃VP<'@YOyG{aQ+E !=e{aU\c5iG<,Q O,°Ϩ͌ yylC0 A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$XMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKm5Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgn8@\C@DԅT`HMSl؅oc12dN]|+#<IM(Oo?2:͗KN%Y|q姛K.e^\$kz2ʮȿ/*/J-{_փ&5ܽYB_.כ9,cgQkntӼ{1 m#HKZu^Mgz F; ӣXy]/cޜ}!NCхN狲 6a72rׯ<]oEf2];$M`ssAF^i(sH%6E{X~x:OӲH߾?'R|j y'*W>?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tqzR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKm5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKm5Dzʗ Matrix.fzPKm5D_ Matrix.inoPKl5Dt#SȚpart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPKm5D,搮#]svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKm5Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKm5D'0 M=svg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKm5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPK[fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/NightLight.fzz000066400000000000000000000442521272363601300327210ustar00rootroot00000000000000PKY8D]E_ NightLight.fz}r#7w K_|31n/gz.a{zf9(8D-V_h+3-*VŋD iITH|/H|p:M 廞xعo;Hf2}1ѫy 텻׳iMaқn4__txuoq<}*3F*5 Gw/d}u7Ңi~y)Cn( Egl@}sQ~pkg}߿]4~N7_^mvC~yI ˛ϟ/k 5F^ٿ[Oj2VaNOJ/:{ "y羼\N'Ltt<=\tfwO\ǣۇ_&٩xs? X?}FNo;_8>N;t7O\=Qx.}˴? ?9-n4a8 G|Tt!? o{c\uM̵הrsEPYe.|LJGH]3~S(`2^cyAK㛢Z* \|Zxpr?O?u>{Ȕ^Ie|r!DV4ÁӺVWKA=?-MՆXUy!V2UJuCB '-n$nMҲ>#9m _yn~;(b !sO"yaq)y`*y[`q)y_m(V+VuU\x֖}UR$cBڿD4E ֎o*rr%4cUhY1Z99aJ?NzM%\RyoNŢ⻓ >qo"py:"u\"M:Nuicr$$z קu-ih}Oah'-rQϙUvFTSE~Z,Fb3)F@#QJ8^)ך3&&fw[" È1y#S ƨ?M#iad>a}A ;^a6po &sN(8RO[ #yG S> _ +a;mf%T;X'w0:?a*~!TP7l$CqU|K1Jw%t{*_|[71y뗗l~:. ~p7oH꟭6奘hqiEԞ |̽4#@|Atݸ;T_s?2.*ᮃT\t&h n;q_lRpӈRGߖLj?U$z!H \PTQrc۾\e]ʿZj~n^-u*gQNIL[S뼲.fش Ɉd^5T0R!_ 3}B֡qVԠ.\dX%R8I4x)t3Hp['z/衩N5 vpfWVnf7}4pO<aV3>^dVxN=?2 >qZP_y, b(MIA6o{<'sPBbSѢ|u]Ͱ༾`CMST𕱘s n7:65ף٣: ]L;4bRӄ(Iwi RfZY!ʷѱ=)Iʝգ0>ȴ rPedG`eԥ$S֖y7x~dXU/gr_X\ lƬMN1צ%"V./;s~ayf8a_gɒ8Oۏb1f؋KIʨgOJg0b1ڥG _lj_,h[1I:Ɩ$1U{bn+#Tu;Y9*jW_k&38w ,1spa|Ҏ,1sLGV1aa\ Y,fLJ =JU1's; 9OƯǤ[LQ:ŪØ=fRL0al2iZvk\&+sUՌ8'0qia^ l{GaҐ=n] )[d'9Xě>&EzVD2$gH$RgjsB!qSr5G' ;sQa&87{>+&; Sxa%%K|8XD&M@Iʛ=jASE45iX 䰒RS`Sf hwe=*Dƅ-&f7QmXM+&}.t1.S l6כAfOlnyԝaw@<Զ"&9 $;Jꖹ)Ft؂$dL~L mj٥C͵ Ҳ0z݉n*W.[ L恓b.ݳPyU`'#;+i9>&e]ύnSEHj’hjAV5;YUeBDg\ z}Ҁ2GR)ɘ$a]fwJ3K KGRam͞K;q2rn4:!*[6C;fkDLH"7=3MhLOKE0I4i t-~ GKOM }1 m8%rgܨZ*}rʌ5Uooc@y$"_ ++̊ۊ=|i04;W|r2 @F 2qS;3xfl&U$$St<|:?F):_EaZqmYՌG'A,z|.4nWI!;zXZpbź6GmɘDD^ž\]kp̋G7S-LpDB6PxǑ:`:#T1cAI\$Fiъٕ|$n CRr%&gLLnO )HQaɵ )wjDxicrEIL \C򺙜09&}bןq}TnsD^ 5"U8DPζCo9,!ɴQk-iNUr-ÞZ_Wn7,+JK)ʹ+UukP^:J1SM7-8Bhs9*CG"m GZDY9#s_IyZ]>QKŬR2!ٱ϶$bu"LY7.;tLڮ_%o[0Ҕ6<XPPpsxY `d{=>w:N@' qP,r$&vu?Z ϖv.}A}$Ow~E )Tu[A7?*3[roa7/ 7Qc2#PNTefK⠛ʕˮ 1RXB=z+叆b4xؚ@ IUnI@oy]1aeF 9NF޻bGOȶgpsa3]Q4,Sa'|xv==` m:1e HeqO*'سr:R]) "AKVgVr%rP4v==]BgJet,zRAeˤXLH0DcWצ#FCdm&N- q%~Ou;A%%mO^7W9W! $R/GZC $V8HӶ؂AF4)k8${eZxR[T#7֟[vcΘ#o]I<x\CZ |vCAir FIrH2TDVH|vdn 娕(;flt9(5MCdQ)6,NP,{dn)h,DMZ-,n̴*SN!v^>6ж=5mg<703a^~U-翱h`\q1f+RlA-KKkqgDs"[~M2Q7|yp:}G5WH/'GUv|o١N8VG&aHf7d: g0cHk4kllQW 2Fٺb2(Qz-_g W2GNb*2Fٺb2\f^xA~X{uYBr.7RY2@0L;~ _0 >{=7drNϜ2}Oj< 0B3MYHM2*Nxif6Ú)??$a賫lGG[~6t𲕗ioQ≭͎VgrwqHNf!f%ݡ-v|NHbU+a3(QpYb9֙ي`9>i9ڎ*UѾRK,8|-[ kbkS.y#֦16h36M1bmQ)F˱mz9_݂͛S!2 Xrf|_MkOW\uJL~%g8fExr:N쓐,9/sd`\ftӒ4DYBDVE7aL†)ɬ;el2Q21j>{8YoUs;:$$HfBdTm̭ƾ_ =}TeP[5LJWGY0߯I޲ϞZj9t|п~}q8qq<~ '^:<7m)fWmґEv.+/#a.+-*i)X4C@4ef6Xc[Unv'7 DFw:MkM%5QɳZnsQ 9Jxxƿ=$񴆐%k[$Ѵ& $Ѵa$VE?We +X;ȭה(Z}sA{V΅vN,/k=!lD{Py%x3$b$pD|\xB߱02 ymRRf΅a5J!NU2&= Ҳ2#E6CLX|b2k9^wLc$Y8bf9[k9?1K&k L U$R /+ZLJAĦ8Ycȑƿ #ʚW* E"^ `FU[s|HQBA)2 m2øA13,Sx(q@R%)J\x*-^ &Y8;9c.I"#d%^!|$^$t jI0IښJ*Xj-"HD> YG#U[OXnkiI44 xKD!xL7nv$Vz "H"dU,_T-sIQPE˻-P&1FV biK;ZՉbdaZz7ՙ%Qiv$RlnRf<)k=)kA)["eL1?H^an&?šHg|jibg9Xn`gB'vf9֘|G2ʐ%~!z$~pn~f3BQkFQkJQQ6Q oWkl,J< ׺[ vI[>ee4-"mDB1JHϚĎך6o}$fzfARz0S)(8Ycvn۞>;N|fdF#|CGbDŚWKhFba Gba< [CTָ3+ TnQ)Mby Il~Yj k6+k=+XY%F{V[fe Q ,?1YDϢDj[sݵ 0_:7z@!6!5zY{$q$qjk'i٪MLtOkm`gBbCȺ !Eۧj6 5#@$jk$-5y4䶵G!ͬ?f0aYQB&RjY5*#":é |>b>}f3ʴX8K2b%5R!et9ӏȑ[*q)I{ !xlLI0IښJ"txB!.DA/[kkU0gZYboqadY.m~4{u[qzpzTY4S9CDoQIxHH^7sS#5 )/(*=]kTkdw_MҤ#B(xDݯIv dm%""M-JXIѷ&!a}ӭ(5$)Vm$%*YиDhǫߔθ%LKEZBpufKi"pGwyyq|#q9kdzYZVc̘XTRW$zo%$10$đx[[Ǯ&u˚=֦|UIRiۺGl{6A3CHbfÎIxmӨ(Yh@RC3XXZk s*:Z jRdZ~>KH|X5 `]m 9Τl$bKlE%&p$Ē\[\1QcbyEKk+_ۚHڞM=ProLp"ux[bmʾ R.&b˷g4g$x[5s[9Sk6QIaDK3]"vё ']Qn:w9׳l,UEfP\iƿʃQnQ-Hl=$1sLQnّ-Pbl9sk9? .qsicf&*kg[ӚC,.L=_  7Hxƿ &Wxm)5 )VmM(ozy?z7 udNd͗ N˛hyp[sx}\/+u ӉCC~]t#[g'4juƨUu~vr;eqS-Ͷ͋c{N_We]q'ՙ%Fq!3ln8/ T -yB,9?u,:C u} _f4_a޻RJh+4?QZX)+)Gv'2.؉HhutNYr B Q&\s)oyZ3/"qwp}'3+kP{׫nݿ.˘1 >7nVuqlW،;u+kwo?hJkL ܭ~u ŰT߸P"}uu\q*VG勇NK:5;-BnUD.+""n2uhtJuw+-q"?+7,\ے-;+}-5|9w+-T4qJbK6ara0-a(<,rDAA$EG`Ib+_S"Er1sSpPP}@44KDfA) nx Ow5܀6pSQ1gg8C1W6z;cigkL;Z2>َs8z޴4eD]VM!?{`01*,@A0,v+# ‹KY$ E@W0S m`0F$b3-4kX/{7`A2Q eŵ&Oމe~pOJX4lȒE# pҭ ti*(f @ TT KS+M40@[UP,F!]3MiQ$UFo<1.4h‰% 6"}[0;'jUL"̩'P00ȹ}m 5  V l4̖LިRǼH7'z2 0DZOp^,:,h `Z]@)zC@wNg _%D82_JIκ8$4cjl+nD1B{0T_`DQP M@y,[`N }f˂Qx3҈_ pC1 p , dt=&hwSCi ToPX"o( MC a)I0OEp`uMgwA(x/l!n@& 19*P~hGA(#9 ] ĢP%%C%Bhx0fL=R0aX@%@QR P#)h7b50UE9NhKD@pv`{oA4.N(f;k+9Xv!-zbc.)!#Uԇ V/t4 q ҃! ^@0cr A+nIhG0)SL Pc` E*a` AR,%71Xm0 D}1VngjnC%hzn_$ }\q2yHT$y`CiD AYC[ɩQY0Q E V|h!pv0 _DR f>& ((.øyRJhScw[*.hg{Ǘm 0BЧr$Ce`>)b9!xDCF*@4(8 g\s;ԯm9VC#:Zh)]]iV#zA!kBGt,hN#ԑ4pu8}]NU1bhLA?$RyE 3bkV8~{9QU&q$gȪ8/ӏ[4%b LK {aΓc0?5,# ?YH>0,fFba n^]z+q "E]c8K':K= f&4CmQ[_]4%t Nx%/;ƝHY .z\cq`-l(䓎]{+RwSX'< &dRo dɝ4h fnAmG4(4C iؗg]#(jY\{Ӧtf AN]rtګANqx9<.:V-Y91!&Pwm- ޯCt4 0=@ kF% y2aXS O@T!0SSXtv4\ະ Wc7gw Q8Uh1FP_S7 t1 ~_ N O<@hB-\e R_Hap<+˧v5j|:uc}eX1䍴bna-DG[Hp/T1ſxp?2'ܣ 7Ш'g骈tID,"UH-D]BBvW-kwWSG%3CѰ0Rty#nvʠ% FC "A F*=E|q; cE/t|?&0@wJ\V4{SV{U;i.؃p:v(͑e /'@Qb@$364\r%/A9AѮh?HZC:TCшYhh?wAL # A [|x6w"E<k 8.5@(Eo AQ LHl!?zV -åahV*yV9t=,;`0^sNVt M; HWA3Is ]s#ㅫ19GUY<]h EO݂KAӛڷehX: P 0/$E/Mqc0Q Qǀ T.p[! xEiKѧx-n$Sp_JuwQ=~Q^ l(]: 蛆2`uFAgmxavroA̺ѷ& +HB;@/~DP 88 t$zX]bc#<8 k.P%pvÖ)-`ˠNȍ@YV$O-Mж@JHD k t M0s^l(rQ/C{$v_j #: s.1GxQ`,uwp3 ʄ)x1ap!PxtA;1,.朏 &jYjƕ /[Qn0E#0r->[X\z=MD4NʡX(3|RAU¸H94774q(-[3^WDqa6vH7Je]h Oʡi OQL-AcD-E4̂Qv,v4Gd~kg,S hDaB A\ᅞ; b !H@!c80-2t" ;P3Pw'  pd=h""1>,l~ m͕^(<D% <B3 +G@;Q&/xpu3M?kvBJ@Q"9,ef`R p:_ b4Ey,܊m`q<Mi,X\ΪcQc5IaZRX)OKaM;҃(keX[7p ZqB0~з+<}0p`58qۂK p4 ̫lY0Oc0%d&_(^# em,2,^ܾz[ƫ|qd~p|xwyww.Mj7Oa!|oKLGd 83aw?TSw)]?}s%qێB9} k=;3 cH 5^ˌrgS*f2SL}O$?3/X"?C{OW>?em˼M-c"|ZJψX{ɶV=6e9VK%ǬWj9Qi2x=]CH^ux:= ݒFAJƎZ5 cR-9EZ+ UV֪lZ^:̯;A9AӸc0W}'M@ޑkN nғX6*PJV$|a?v ;iDB}2{JrG[]*(Yqv䔤J(O,8/; ,bϷL.$\eF=psJv`@n\j@1=]K"0YPX5܍ݜ=Vx\(L-*do_bl_qmY.~6kMHiGӚ6YMGgϪ9>I!ie4"0S>_>ݞsZeN-6$ *cj% N6ZŒDێ$n=:MڶE^mxM?GqK!7AZ"[$ѷjk&)A.ckUOɃB@g>7*fڧYnݳ>=8䒈Hh#^;NfwJ|pYr/sQ 8I` 5\Œ&y↖䡪jPQmBJrTPI09%*JbVUX*YomAl8Jpi sG◵9R^%nZVQ%q`%qjk+rZ^ $byt4I כDXq\Ni/nUSv9% c {4; 8H+ lOԺN UnJ'[g24g2$b$bYm%-,')d$B$˗B/MlL[ }AE!0$bs\Xkͱ܁5IA9$J$JYm!%*D'%)P$@/·sT&y,=]&PM۰Н3UHeFD&k;-CIӆx(q`2I[ED(D(9>zB$Nyt4I!&$qJ1) 9^_x~urPKh8Dt[U1 NightLight.ino]o0璴(![*]>/iӔJl䘱-1I)Ab~΋,Id$b<JQ$)~RE&D $a|%Je6@uQÜHA_$S5飤D ߘ,q22s1q|髤w1@8D4̈CV`14 Ӟc,`l*rwECD5w_wˑ,L. Il)Bɴ(zf1F cI4bT rmQ Ej,rM%\Cl p@tit+$=u_7ŀ)3lJg)x韨aekdƔf[$LΚI;j]8C3E]FQے*3mJ1=Wd^p Yޯ"k a+f[3?גsě7S"16Sz`z$64l8EE]zѮؙʹgsa{#+N5nO(xۧ1<PKY8D]E_ NightLight.fzPKh8Dt[U1 ENightLight.inoPKwHfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/PingPong.fzz000066400000000000000000001651471272363601300324100ustar00rootroot00000000000000PKV8Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzp[o6ǟOayEQeQ\Izҭh{2(V ;ĖZq(Њ<8&1Š-ҳ'At}v=y|%Yx3\ff&u [7SCCuxEiV8w":sfSgy"VML[A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$ݯ0 㻋~%t\}|SW-~𓫞z~|E~gZ=?}#ҿۓ_~Uۍf?q^lq7Y5Pp}~CZR`-}OJ;m߅Wx7 X]?yzY]Hn}njEK 0ؗW叶m?s͟{O|\==^>_(s+|0h}'|ݥZZcxh>z=Ěٽb@GǻS=ڼ}moh{0c3,eaWl}i}דsi8gqH`}R* .8p2@3d86û rf3; |jc\M{G\B{51.619~cN0=mG{ ScS Sv |Sbkm`{S@np7?+p}p;5OK1cKPwM u 9C#O{w'J2s‰_mw9.f6=5cliu!1@:W~)NW|%:c iO<2Ncl{N{Gtfc|0+6 bT%5C-P!j 0 k XkyB + *4=~@@ӊ @ig)0'Dpϻ=Cpi{OY`== @.&lxϻ=:І齧Ξ6T..lxO<lO{u i{Oh=mp'\DpϻX=?pi{O`=mnנ3 =]xԞ@ޛ(=%v y3/k2JC{.pxϼ= {v{{qpvw3pm_ V}>Ǝuig )'Lϻ !>CCi|O)p=b0>r . nϻ(!>)06..nO?i|Op=m~)ې3&>]6x՞Kڱx.=@v y3/"m220C{ .%rϼ!>r@'{ %Zu'뾼{|}~Y5X,|O?|~u;@ZR`T[6 ǯ}_^W/Wpxur?eu?l y{|vvǯ/m].,0) &֊,0ݤ6'I`m[3w/P)LL| o rˁr9AgA> <$A&>le7'u4Frr=AgK:A>u1s |=E oIN|)oO|R wSߧ0|v]ޭEnAk`o6c@;3dxlC1:tpQC1?7*P{v cxXdvdꄫA+yKX ܿgU,Ipc|y03%yJΖ7/ soB7٭6Aev+{{ߓ7 'Μ#8,qBroϏÅΠ#x3SE GQdjhko{b_\^>ܬ^_^qy'?*RQb g8V?wTMCZs2LIe_9OC7ݛo_.Zk_zuWMpk{!tL:F<%՚tZz*3jZr{Mi-I#$P2>$gÎc7l͈*՘k8 ]5~L&HX-Qc2hyr0eNntԙuQL僀r"vlQkU|DXG{[sC!0<7pv}Sʃ'H͞bu$f(c:~0?PF[{aϕQ[FmEnŁ2jFFS_ g\#uT] Qi:5=Lc`{:hWGy9W8, rJpqss=0YA1ELi޹bʨl4S4Sef*a~6]I$7Jeҳ`ܘsgW{5LZ]5Wc`F[ӭtŴLUnx7,*0%G)Ej̤tH+.5*]G>:ŕRf4%T ZQe/qG{[sS)0< k>Z̤\r P*6 ,FyZr";UVQ.**2r[$1{vUp@KI!7Q rQzNV.]yu-PF{[sf;9$:|>! e+JUҝ}zR(RɮfMHdB2?!hk+5n+U2)k6:J *TrT;瀔RV*(aZXT֜YO`&Itxsl[gپAELƧ"XL$='C ;=3ÏGBd.=2?!;փ8ku˸n{JWy]^ӣo~_~ {{>+?X5FX)J^߲!o/w>W/w7?8?o7zဏ5'x|xo㧇w?]?^~ϝPwσjsy?- aLu.W~{"+V3~aҹXAsBD)c5HQO⻡ӛoxaɑ$ɴz{Kݶ^f3(iCs98f gqw*6ZX>=,dA}+pO0e^{U ":@FRы!*)VжL&@D{|Hߺ|sMV,MfZJ sKf ێq .+' T\V̍\Kĕe\K1|Z yEP 52>p .!BPv{)Y4b+P%v޴c<1Mf JXHYve]UR/VD 4Ep ݚIUxӹtAHcX5 Y˱D'), Y oD-o, YPF \J,zڝoyipW<R+{ͳscZl֌OT`<_TfeH%yLhK,vs9fIf&xA.&+N\%E7U(URC,q m4D0]2!HP]8[GU,sVh Nܢ_҇@8֎R\wёҖ&^rEHm?)vRʌagoK7x[ϵA-!H'mO|+q=w~}˓W+]t};ݿ>Y„vjoWw|[^s"SBybyMiW>,akY!O w'2"16W Ww~X)׈ |_^Gǫۗ_?F}=ʷ8d=ݯ^B+׿Q_tq˞/^Óu'P}Јa2sEsFM[xK<><\[ b]\pI!0pw)'}M6}۬ȩ.m8F mr*J)DZU[0؞>\|SL.p}WƟ^ ?' ~ w(˿޾<8)ϴV}SMh~v؇gK狿O8 4t:0M?G^x=Oy/l* Ne7&)ahGosJMfտKM{L튪qOl"GḋOW/ǽ8 ~VB{P1td7ܲim1<>L[Oŭ3f0 rI0u]u O:ǁLd=(pa&2ω oSmƙ'< /m!ΫsI!N Hnz|$GN4_P~f4߬|g1?=> ({0; o&c8ܶ+(#e`ј,Ndb魣Od=` oX%72 ]Ϻs ܝ~vrD}Œi|D.$?1a| Pdb *?Ӈ؋4[Fu'ӣut]u׉Ko.DOΨ/l=1)0یr;yHq+\ף/Gd pur^ok;`1Ds(bR-,^?Ȗb8H-l=dwI[^<_uk8yfrF7|M4Wnc4>ƿג;KQ2_><}ɁM 痗a8}#gQb)GP(̹|M>F'88*~ `~Lv Gb[Fx>v41XurSz%M F#}MD|!R'˜:/0ǀVQ sĶX6ۍo0,$/rfQQ-mWf/Ot=8Y`<\3}$)%;F]r7!NI}g3 C|%*s2x^E#βؙ+Ogʨ0GS!v O;'@Lf'Ι7Cvc}>G61sOgb7!7b3?;@%&o(Y!>R̟:lݐXgbHkK3oraIH/Mw!}<3Ce>nD}hNh%1?@11\ӓK̟!;Ac|h3@L8Uf̀~4o#w)>3c3c~*< -6"]Tҭފi眈W%xa;>ixb,vY쑆(=wѡ! };a wBO^nVWZƳƲ˰p癿ez^WUj >X9dg8~18kCHzxKf?|S}.n»ۅI1V)a7zoKbѡRm.?==TC(sQ1׆"^LV7NnTRG$`dG/]n|ٖEօ5U BI"a%3.)a[i@#^&!bF`cGjcmL[J0JIr!<KFk**(dYQMm^zQBRlt%"Ry ]4$a2IΌD7tA7[͌|CQnƫ喊0ĸU(foC1f޶4dA+!heX+c2:ֲp3RFXB|RHϖ1.m!eF3/4!Gfޖ(c6te$ AKXeFqJh 22F4U } 6=}1k>=ʉ :7E4GhyvFyCV:cbi0i5 Ւj~tD*9dd~8mZ"5C[%zzI&&+HSSw.'$ղ!$Ug>TR'5$-\O=ު܄eh&"DALZ3id>Ri_0,QyHMuQ)üӘ .L.QLaStI&6'GKSt4z/͝᫻^jhҺ&%WD^#Tq"U8(he={A1u]>oWoC1=3K3 jBU+c-=/sIB : I]&AIG g(7u>NU&ECvN^RyC.892q[fNy S&{*H+xJ*,Hrύ:*mCW(BHg*[Jx+a3,l3 m0ޜW]:I6ځ1!ȉ#*c9ɂ6:WPR!eFYKʌo{n8J[(m)VKni*#w>&Vhm1 QJEo:l^D1LUI+"?+ӔH3C<"50Io3 ?3+u!At8*;b&Da*:d:^21a'h` LIy`g .$T QU R5Rtz/!ZU{#Tëo٪f!¢fB5M<_OyJM< 0fQ errI2?%kGU,> l rT+WT榸rqryrT"RD #Q{[sC!0_(R0Z2M.lL.-nCEk$Ap$UJ*d:~(KPR7:Bj歨>|S2Y( ZJ,lY¶֌hum/Xmp)*X6O[ִO"H Zmǵg&ZAе'rs@X,8)^ProWpW|͟i} ؚHGz"UB(Ih2#3WT9RZZ5{eyvL }-!O> dP|i3nWr5ʽN=) Сh0to :5p-`@euNL!T@85$Kh$T#:ޞ6Mr) 5VT)sYM(%z"=5Ǵgcu=?YrcN$q*D;\rG Fl(ךQJ$;9.5=9q@A \UbhuyB b)(c%_CНձF(#y힋L}]/0!VQdY2IT<~f՞c4l,XP7a_ [5#emeaIDn4 [HJ.ɑ),:U !=ITz7_M% q{{[{m[3>H05([UEinޚeIkU}-ښ!r\kmܘ'lْdl.[*W6٦x9>ET]מTvkpW66 ʬk>E}y*ޞWWct^ZV沫`$dZ AKKɏtա ~:-dOJ5֑Mvk'Ykg&ĊtHK2rqNJ(HТE[3VAeK0bcScщ6!*)V.YwG)!(e֌slwdL62u9g;r$H*<@/-\)Դ s_ӵ5)nWҒd\JE" F@ȴLbDڭI\Qk e6ۚcEs xMiH%fhd"z\MKɍE 2Xcy9ZخkOZX5WжL abg6 p;w[El] (s߮@Z;4l<@2ǡHYL$gc,%BgչUe쬕]+٩1-[ vkgYk3J=p+^SFenYB?-Z3Dƭt5e z8Ƥ(Yjj#/2eXsLƺ#`~[!ոv5q̻ѶV:knfFFi5hxdI G 8U(YrEMT;Y{[zh\siAek&HP ʖy#ZxSIisYhlcё;4 U4k&3mt%EbL6|ݣt6]6JnмH i[$l@T2ri#*/i_jA.O2t*ƿ钑ϳZ޶;a(zn~-yÀ+dDD1ẘTf~xΦ}yXo~.r|}ΩQYrUBZ&FF+HmS?=.tt$fG*)U"?RA+l\dtlOmdf-1 GBj*D*; fҿXi?5ItRךKVEFft\N甜U8"TWD TSdʮVO%RKHTdPzi駆:5z)Lq5r+κ ƦEϬd\H熔ӝxLNJsA-V\#FmYėcğJ3OX ,揊h*8X`#ZYU .47VCC'D.LN"LI6C,sYSc*UBE{2zs$˨0uR ,O_UC)mY(PQlT'#eJ8Z_&(F0FTaKj +*5"KzZ='.I9ȈtRɔ{8CfvR[QRfEK--,vukݦhyPil[[0^ZPR- ;< tnK#TbyI9cz)d2=a$3S&ȓIuMN e6DCNwC rL+LUȷAhu㵬ãlyg3IL?VԒkunaGڣ0:PM&͞N%Te )dZ>]3Av򼥏v)ޢo)%qMrʼj-#M! \ >9*Ԩ0OjqiJe4CnaTS{AC+." Ugj `aޥ$Z7v"zJ{)* 'X3xO-%T*M;O$Fz{Pw]׼a4Xk{@1TZ:V1XNrJƦWRAC uIf91$hKI&Cea.T *D,iey5ϓO{>dGQ=Йt<@ANf *XA#1~|GNmb: Rɢ.3/qq*&x^zWmqtkqryB𗃙pa$/3hjE]1g 4 Qw6FV[i{~h~XH) ndپVmJU#}n¨buɪNk- xm[$2D. f(r.?Y 2pҬ5lst=+}2J)Õxd%2vfesk{E#ͅ.BW+Q]#c %G G$Ȳ}Whmi}|5?8ڠ.[3>CҚs4hVKr~jZI4NlPZ%SYb  J?6sCݳ׵sŽk4^z{{z˧ DS"gSKʔz{Tr68 :I:, X6nOP]s rem83$^Q} ^y4NF>%SB \J,ZeȀSt(Akf|uf˽$We<9@jkP3ź +5=;| P'Fp>4s,a.)]‘:ub%(v$IVR79/uyRRY'KCb]IG1IBS9u5#\B,lZ'ѾtHY4?AK4vkƇ .u'3[PrϖbJpIikGaispT݀ ةhXdt,[D [`$AgΦ:[]@r₿)+(S)] ("^m]o+'n)ilf'(p X+o֌n `q:Uyi':I-E)PfOk{[j:/Xۙ 2)˖c!2e.KPڭ'(sݮ@Lf5(9.ۗm K# qNDKDv}M:y4f&@Z.-o3Tݚ0&@t6UYL8X "dV.3㤤x\_kF;Ѡqlc/AJ0tIX3Y'+(u PRnMm"D\fSӹ۷:,\5XvB'֌ %S8#Ɗz&Tۀ6KI",;晎%@9I̩ 5M3Z|.%e(!.6VH OkHhmQk<[J,\6%@ .[3>N亃qcM.))NDhiή(7!5۽ ˝ XIU{b)F_-%Lmo*ä-CPڭ#m8f6 }yP+!6V|+36&(sy*c} +p)lc(r X.n &Xd2NR@-!H߶mp&!J572MHf CnR% xF8a2?R4CTD,Ye¥*\Bµ.(\CݛN%L3 ¡pb֌T|(d̢kQ5i]_PDxk8myPAeKēe+[T@SK4-Sf[r?btBpb K: F܀A"^i<-mKGiX/US/(9,8sT"v [faps587>8N2e!l>c6^`P8i\ 8\7~8Otn0Yg%G@AG2e"(k)Km 55H]2'R:9Z鿉/cԵ,I\kВ*OK5sn]kGEŊj[@[J,{M曤%` [3>OP溃q|Za5(n@Aa6e>)T--Sbo9#TIqW\O2\4=; w>Ժ1@N(SCF S;Дq$:ίN,Y7KAKԹvk$yF+`ՙUNICX]7DQefgsѸWVǏBAu\Re+t.d.Oڭ)H3!q6!qLNPk/Xs\I}݋p:c"N'pѾHY͙\Ը Akf|ug3k\}塼p:ez b{堿u ίp+dƲ-A`eBuUv_|)#*۞\WF;R@qlɅsuU .dɲ͍g%@ .[3>NP]s1{ɭJ3?@MA:T@̝l"$N. Sđ|r PY͉0Akf|uf˼$WՁR0#'8.֣`0}\8UtCq,Vy ]R,Xe]R;$].FڭIU!cf2BB~8.z> j,)3qYoXI>RqZy 1X0x̓Q)PQ kTQMp47V hk; Z",LZ~%dZDDfYbߒ q. :!.EUDIzF em q&I_kkt#J]E:[Yݭ]ELֵ4Yv1kn▀${ݚiHp2©))!W. q!OrkXp̭0@hC 9OYZƊ)@:[",\Z|2[Bm0TfQƥ"UmOHk UѪzLj0FBXi{v}*eNF)lq8X{TʢhUD˥77f4;'myX%HYj79AK@\nkf|`Z 5,p,˻TaL[lxrU& Ewق6R9]kF0 9Ź%qMѡp "Gs%mj XPRBeoU9 S$Akf|R\w0`r&z$*O*$pɄi8` &#E8p@P¼ 7i,G{LLe2#AkK@̝LH,l{%rU%[o/Tb41E~z@[J,[D \[555h˫qI{8"˄E-.ښ''%DOskPJ58\AKɔe7GAKtvk \{0AN X<#l(QoHiT1z\ofcOisNK8|,а:qi%N*paK=h0 ('S}4> $s8qj$(q$ 3DXf8窴sXlyoX6bͳ3X++Q}΁EIF)eCUDU4|$vfgfi{ȑÉᘚ5AMK$̶eivK--qHJvTp*[TXӚ!bYٶ^q6g(YfwYG\@2[R,vd% .[&̶87'#!.Dp>)b%[v~e֌`Ͷz("ñzs.t%BDzlIzZ:[B:[[ &̬iexKaL\ӐIFgTCS'e9ך8٘+Nk{]lOD S%,J%.J2NNWڭ(q[]8YA.e9*GTnBw2XlP>N!2PyR<٭dJD2Ԫ[ߵuKDEk2A_K\qKTj::PL[OĴ`\pf,P5=m,Ckn.<dTYñ|4,vk \{0aI:g-e関~՜8Q\uȫ(6.Icv/D<+pn!}{ZUߠ \J,[x9 \˵[3>N0lf+cB[ЦhBw2ǢI}@8.8sR(EUL6LLDeH?AUKLLn`KsY-LLڤR0P [^(j]Q+wж&w,FK@& .ɲMf%.[3>HPZ])B$ͫm5RRgXr&V&;Y48` %ࢭ) 3;`j+DjH4nCG}מ<-%G}J@U;%.MPڭ((q[7n72K_d&M:ąN wlHM:֌ǹmn@PB[ogxfC1 -A012!EPζL"gP@J!q]Uߍ9"C.Lڊk}מFԵYš7+AsK4vk m %ʬo:R眪[RUK32Pmu'o\J $2uTun)鐉dkc"l At-iڌN\0u"pPlY !Z3 Hj(j伞mQ"CW\fKE [l)#j k֌ϑ_wھk?]Gmw?>ݯ.^~]*eC+Md}!D ,QYli5aU=Kf8ѺՔ)_]57]ϽeM E8ʽ6|#5"^BZbh7P"98 s#I嬼AD]r˪HFj311XZ %&Ujኪ"║,q)U0x16 %HˎK|sO%d [k=rϷcmFt|-Sh6"^*`NQ5c2fƦJŋ؜ )#,Y|,vk \kձKG(gn/&%Ɔ|MԷndfof)[G[Yɑyh<\y"V}$n)!lw$n Pn8Au&nfWƉu.ryHcIu?)`~~ΈU}c;{@PNj+i&\%Bd4OaH%4.n`zXl>fDtf=+e$1~p9&d@q]9>%v_z{{z˧P3P ntCLsg\ V~*B]Vj]Jp" ,PJ['ힽ4p3V㨩rj^<5^op5 Wp5.%S'QPeݚy2d.1ݼ⤵ĉj%9q,Op5hάqHmΔWE--eJ( 7v/xnjs*<ʥ$ʲUNdc\ȵ[3>NP;79W -`he]J<'e(O"mX0E.^#뉷⿑qT9\AKɔe\9 \˵[3>O{7p9ltXn)KT@J$%>kX4hs19:<ΥʲuNe\ϵ[3>P纻79<(&(|?)#ZP)-GJi}w@!e(6'_v~PYAKvk \7w3}\Zё( fX£#ۚ蘻A:cݍ#3my0AoK e{ILtAWkf|u j6E ʡW(q5cQꚈPA[uMRdٶ)L!Z5SumK׆G2ZT9MaY[{ 0f$4RjHۚHiB&`(yM\ -72CV%I%M (5h0ʶ #"S=> ~$5q:▀#(n֌<nlt4UB'\84I'~*f9֥'P̠Ac2~ힽ=> m/+).l*" w)rhmI֖"9z|(Aqkf|[ &loU`DgJPZK $38 (p (n$f f2[tEJ@'q6z楠DZe,H c #5ãZ3Rʂ@JUp 2T$4`ʔXvJo6Ɩ%hl֌\rvf3kFv2ehm."mXhm`" {>no}מԷYl(A{kf|uf{s,WԚ (%%_etIʐ8֣Hk@v/rqDPR׫DqXT0Aukf|ulufV7!r(-TH{!٩v[bh3s @15[=3ÒꣀG*ESMKP`ݚQ{A܍evj q}QeEƓ$9Lui)V*kHQ.Te@KHF<&J8m2(m}מY[](ˬ-LPڭ(5jFH#U1,'46S\uk8,C0Nbɰ]{DRdƳE`}ݚQU|Md*|=iڽxsY٢DJ'#emHs=hk׭ )ъޔP MR4'΄J'(oY#˖7qb &p֌Tn p2|[OC*N:(X̝lM=DXuIµUKG (IRRd %@ [5ݭ;"Q)a; 쳘F(Ϟl-J'oYPƶ<DzMZ[3>IPֺqYәe-O.5Q'4km KthkJI]  $# ίP#V7"hp `n@A2!d62ɻvJ=h5$¹sj+tkXsׄo](o9!˖7mJ%n֌Tn n6 %'8qrQ”+>BeX@{fr̘,:Q`h"11 !hn)ls9g$(o hnDAyÜ2[\ܨ}a.J7 ĥ7p̯v^H>TtQƛ`Qg*<ʥbv9&hq @n0c9W Epl_+<FU(p(lvNsOooO_{{tftc0c1C霽st/.S=l> ҹAkf|mlnClseιtD{\5Fuҹ*rBHcךqoY\U4qP=+p)n%q֌̭ܒANf<̕1VK9RG@<۬dZ8xtݢE؜kU3mNŗ1kT]{XPRBe:3R}ݚ>99窄t MJJV.qm 'I_kqyG:aL߮9e iJ,ͲstAkf|,)=~&: ׿xI_/~Z==~Fo.V7O?~Oo7w^}o?ן>^/_\?7WWۋw͏oo_\]gA^>^}r땿*\_t1ȇiw˵#_\??`PBxvO/xAh_\ ן?\Fx|秧6SJC3˿(~Ŕ(4w_R/O.ΕxOsG^a~ zaՅ?^1?`€xϫUһkhC뛿}|yX_Eݿ(gˬ^~a?no7/WΥD~R(wu'U \Շ_ kaύKJ`O/Y(Hm2 ~|RnY2F?) /.0jc%# nhkm;$Gо$AkAώZ@I3[5YYJ5~Ǎ  3]Jd03s:qRRo>K9Ru6ḐF8O NJ$U|X1@GonĦK;mI&?×' .6mԝi$(N˜}iC*U>)$&t,,'!0E$']4.`*\<*|7r|#7bv)L9ƿt=6*ĝNmo )]g pc2 g1W&x3>}ϥxV9잛o'ND:oC-٦L\$ӑP 1#BM}V Dn"W&ݨ4I)y`$I._My<߇'onXHfpg 5qx#Իtw;h$|ho*@e`b/j:?\(i8"q4 `&#gzx$#{0Qh3ek@Le3L \ U m{a$)46B+x%%W`W@ ǿ6lL;p" n K`J@ϒ~g־Hx3g/_FGNL`_>+d!(D!Eq0YN"?>4P3=1&Њسb]Yo(Xq|4Ƕ.ECÉyHǴ8iI"PpC:˛[=#XNv#NZndՁY?$Btp OI^tV4) !k}ŏ@w lo8*q9$yA(;Vf$`0 EI(P:lKWy;;6WDPr69'y/4g)Y1c¸Wu;i t6HA07}{\HNiJ^b#e0kR23HPZ̦Iކ9th%dn;gmG<@΀Btjz,%34^ hyPSAKKJ(r[>laY G`D iVUSHmnh%2LKbhaֶPj`=6Oxq1X]g(O)*aXC!՚.ZJ.EхB3WvĊ6ϒgP. Ie H[hiw{KbDB$IN@_!my Л2<, tl 9]`Tp̷ N2j[jf1Bp` 3vS| M+I@C fBހRZ `LA[Rn;DQq3rJk f9˘Fsc >BXIH 0XNPp̙B o4'~h۳Pbl<0mID[f7PY5_ GDti,ICvϦ+> Dyrb0_3`v'9hd4H)jx,EdH6D6H)Yf)0֜~lV Ò  cIn{"_EhaK^"p+K: 1$,ЌX!bKGot%Ĕ"Se)Dz6Ar2,P86= G)r^2Y}t.r>=]ƒ:$scgG3aXs,n?3a.$ M&l0K8 aM@6O0m|X PgWXi s`4P`xI5d)53u.&ʁb =:{y\r 9#1vbs BfU]`蟎%Lk)q*kL9A/S-QJ:MQtU~N1N&BR ?CrLPw')<\R'#E|d :&|87r,3bᑃ6.tqE~)vbeɛ_M˷#d"A>l0T A<ҏ D[,oA=`E{"}VDQ'8]t'TkgCu%* E`V\RIKЮ#A $(Yɰ|Lyf="'>'bm9]}Y&^|,` bؠ0URcΪwra_q;EsLFxB?dA37 \%WP?g#?(ܠ!$*24`An:$B DŽ{dDs7rm$=""] D6+ChJ#6R4Q0bW / 4,@S,6V90']/NP \w" +VlJC@s@"+̤lರb:p"j cpIUԜ{ ׻h1_ Z~gΥ0Lk&e|ۇpq\.7Qᆶmd/$eKK.P >4*B< 6P?\ᶬ;dž"B`9L/bY\%KB8[iMw3~L8qN: xq Rw Yy~v1Y_&*+gK Ёtܘ&Q5T8 $IĵWD2w-(/k#h^OwY2Òk.ncn#==1M beOA4alAYzM΍wb&Cz9PreΏO e4N]K\9e pp7ݛ6X)Sd1) 8h$wЅq˱ŤA%Gm~Q FaW~/-hQ4LNˍKl}r<-_0S.g.XSM3UPa.46A9%r=y M\ ڐ(`x"oJ}{閖oոiX̠{ (9fD`/&l؋𘗶`I%+Q<M88]͐ۇ.Po4~,Ez11c\Qcհbŏm{n6Kl;/%a"C??HI|{/FX_m'űi& u8k̄l!u.!.6FD}~'b6.[kFUҜL80yUD gU( %CϻA랊mm^+;P7_Tƺ΍X3۞RCE" Jp[pO2S-Fs wf糧KW*\ 5s3 N Zb^ s~)cF TjQbi:ҨNt(TwDSP ;"Tn<_rdcRD3W0BAec*?\7YmC+zYnݭo1#; gKGhn)2C* 8/uFi\fR;Q|G@Rq29 #rCm!4R.+`USx7gしX5isNfatiؓ9dda,#p؛Ro]t]sթ|=̀24 ~ #LVnEd]'?T,D/MsꝎ eq?\]\Z"JnDw;Wq`c"=xV.G x_cᚂ֜4cҸ%tDkޱB beYrVyr)LC~[{7>Vftu?]nE^,%R4q|˵̅luK%!Waw.o$q٢b/}[ =[=[rgC[4-$@ܒm7=-b/%=:T4z#<Ʋ}ar)v\D8ؖxd\S./%ר㩧?>|i4o|C 9.bvQ8j据E͓t<쥠ꡳꈂ[?: IJ Y&K䋇]v)j%1k_kbJK.iR;܈qʣp<] k8bV{/o~fǿ߽nǶD۱4Ϲ7x7.sr~ :)㜻=[aN4#cw2-Ruu%t;'hzҰ+9p1.he#O.cIS| e|3~Rcx1_ʗ7yJS|n??sV%ʭ .~m_]1߂.ZtQ|#ڼj/ysx߾?/I>Rǟ~?h}wϷOjf_>v PFI_Y]]p,y\SCxhw$!a^Ƒv>ӏG??|>7 ~־c~/FGw%yLLFb r2ke'n7w.[twVVe'+S]Vܣ[J~~Waw4~MWFa=ld v`3;G&ZIgYAͼ4ˎU+an+L|LZ7ĦWZ'vA6͒)Akd(9+\DXtƌ@,s6HΪYcVN*N"N#N1.s~3j]w\pHSgn8Z3"q/,Ig(ѭN'es gUܓM9c7 c9e6qt֧Z/,#g'(ι}pñ0^ͪZcn 3;g>Oj]jFC8+aY73%ukL &t9\Ei4}łW"/]H;wAi;'\j> L }QUd0u'GQk eExQXJz4kle (_E*^we)Ә?vS; gN|v h AuF@Kqʂ2Z* 9ҚH7̑ iVm%қO?| oM~I'ٍՊK|qȇg~ĘǾ_R/oօLW lͫ[d^I:%UkvI-A[WpWO9--[zJ`W)+pu:}by `ҜՆ`P &Vk]'WWO髕Aymޫ.o(V׫W8Q[J_]q6GkA'`_۴Iu0`R.tڲ&%>JPaխ}LnpWpBըӉVnS8:+V';2ʫӧaOmpۊidNMf]s>.ӴjfDyB9v4l϶bi:{:{DfZ0u5tL43/ [>b}m}v_G7\hU듛rzO\'76S+ԧ*Y<'YFFyfwgp'WO DGYOL7mYOQ[2W'a9w,p_ f} ֮O'ΈH/+LW6>~p˺WO#YӉh,s+V'{ՏqwrxmDt&h?lqýVN i':SL3S?Nnb}z nl7{cu:e3}>7_on6&wu?΁?7ۇO훿>};_^+}ÿNyŰۑftǤESwX;gVT1y_%MJ߅؜RK0I>$$:L>}:[Ry-ͧO)᧟ҝ?No$dzfdWMγ{|.ݶ$>R4UgbS %fHK";)zQ>'Qtxzm6>Q-Wrz)dвQKQ2o*v.X;s* Ç8V=M\ j,(ZOH%x&u!V_H)vQ9iBqTÜA+(N.ZXI剓SKMܺin11QBrZۊᣫ @K' rC:{fl4KUKNN'w(eq vanaaTmrL59uMm1&C4Q`[ O@:UܪP*;霕9Y2]~1"~(NN]NÙ˭*^9r'WnEq91Ef-023TN ;BS浔*RUi , Z'saaG5Oj4eJx%sRUIuF3Mvߦuߤ ?*1=V'u m!.DmxG{Qp4cGl,.Te_ *-[@6JFGs~sxRihRg"+-)㞆;@(]TRm^HY9ۊArhfZ=l||M!-_;~U߭5:1'E__ӕ搯ѤfEWc}̐RعHk$|2m#-HJ|t镛){/&Vʶ6|֕F3/\ֺdynC& Qn proτ ɂK-ac;M /{MgđJՆq)ṅZ]nJ䰱r'Vec(eCeldlv1? ]~JO2+l}yNl.8[d* C*;Aj mNx Mn6Rٞ2p307]>ڏSn6,Y\2m:4~lKWIǑJ&GJ.*mh kM3Vi}hJV.G [qǪ('Ъhy*)#y ..]c@~ccJq=#kL+ KǾ*ض:>* _=TXR'X1|T6R9۠8ۢ;-o+\8懕]Hc/wINrѣr9UQCbOm-j\l8!vƞUfQ]ڛrTVpTNfQKl8aW-uqm^"3Ht"@/xܧ{`sW<:i<Ko,3.B4Z\i٩>E;h%Di(Ua-'fZ^mfQ `ʒRJM6N &swڇ+JkN_3~ugӋrƶJ:(عb^'b}&X%TsFW!Qe}l|D*);*NЮmw*vՂ*_W:s+~Y@lqr8̄$uvugj@*DHs5[JXu9 +*4Skeg\UÂnC' b9J*gZfzXyoHBxR9hR+W ŭ^$(KYY^HW6vg-ftfek[kVHG.ǝ޷;K;ʄ֌'8'TT69~a\b]#Aӗ+j,A+/x4C85l+vj#Jϳm9}ej j+kjc{ﮎ5]6)Svghߎ;쀅qDؙ @V5n'.\Y*W**O9"qj1D&'V θ{ؕ-XR*H^MHA㑦,Y9PW,up4#K%AJ%J]#:'TZىd۝XZ#N9+F*sңѼërYXr\r8xrN@rr8r~МuJZy҈UJZ{ZUntr8]˕F3Upi:NǔCj%kԥTF9RAJ唓JsIW wͲ#6 ;qk5I'Iʫʗ_/ %eaphjc* JeqIeqsBIm|ݻ[xSV}S< }gZےvjkߛR,؛YS*sTjכL/ÕJG_L0JpoߐåPK]8D,wQ! PingPong.inoYn6S&k`M/hHGQ OG]lDXtMI-i2A ~LpL`?be) `he.Sx,p<|w@lĔ7/9q+/|>2g0>9 Oupf4A1{Ì& }pfD Fzl|t>=ۅ#u}|bmHQB#qf {h?݁:6OmB-!aA'P7rrv1GMc:[tt$ :dĝ1>+[tHsHzCAf!p$D^,[$O8`;Ǯ}ۨmh馝}ĺsڗ෌}b튊}^'&"΄.E@ZykMX5pr[{ZV1$4)t U"$ ;d"ITXd $I:]aaDqFRsB#6q0"xqF$!qprsR9?@P^5)Jx$kl)P5իGos6YaI0.}5>:|f8 \*]C"N2& cA 2IR fY(%"w@b%cjA_{{-嵨9R9A,LS^>tq6_<\ːyCɥTr)\vStӔ %,$B,ȘEd,$)t26rJ7\AbYrnEa\"{R }bh~[vcAF_QZE 9IT2{D"6rVޟ}nljpZ[+RZnKҸJ R ~ F$!*J%L,,&hՋ1I}kElSph´:^`39pDtQƵAh\z)1E޼V6 ݕc owJ #3aW^) a9OÝb2/slw/{-[:nw;0e4sNgEӶv)ˢ%ĔcYs'!{Oe.͠WQUU ]}bj}| &4hڃ(hqf yYdT9Aour !kg$9ȓkqls7 xWj?M{o'x&5hpz8Fp/otCu !9^V*+ C=%w6>- in+Y+b-)vgY^˖ͥ!w=@u[J)Ч.eİQ|@\NrRه7S>#%z  Tt鬕,Fɒz;` JwɊX*%ܔo a^g,A;cj]5ol8U~y8PXsLbXv]\(D֎mbUqݥWZbUO~:|ԺyUͫ?ڻy)+b tڠ9L*Y JfKIY@f0*c5Q+'p|d_*7OLjma)u"{gHn ꒓Ohry]r%R[рFUPKV8D,搮#]svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgoO8)|$Nb;eeYH+ݩw҄6R7~)&Ʊgf2R+>XMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKV8Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgn8@\C@DԅT`HMSl؅oc12dN]|+#<IM(Oo?2:͗KN%Y|q姛K.e^\$kz2ʮȿ/*/J-{_փ&5ܽYB_.כ9,cgQkntӼ{1 m#HKZu^Mgz F; ӣXy]/cޜ}!NCхN狲 6a72rׯ<]oEf2];$M`ssAF^i(sH%6E{X~x:OӲH߾?'R|j y'*W>?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tqzR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKV8DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKV8Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPK/8D% %ߟQ PingPong.fzPK]8D,wQ! PingPong.inoPKV8D,搮#]svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKV8Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKV8D'0 M7svg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKV8DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPKUPotentiometer.fzz000066400000000000000000000452111272363601300334330ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/FritzingPKh8D`G#Potentiometer.fz}r#G?`3 ZVI-ɦzM} XD$$~~FG&2ą=SJ{D|_{G FEo8܌y4_x|ndzOwXb eK®oEo< ?ʞzr|3|y4o>'g1ލG7xv?KouoHwyg/z˿O.9y]=^/?E^~t)`ʌ5_ѕtke9uq6dXǣ_jo!|{z~]݀T//zWß?O><޼O[mi^>?^w_}~ ~Lwx ezyӽoO>=M>x|M|Cѻw޻W?P_הßGio;d3W;5e(,G[ڑVP{c c^S5NۋpǛѯTn>]/+mKO7ח۟Hn?=UP߾*Ob_\W -}joaο-](zl*Jy "Zo^PJUM\;tJT=%1ހ;(W/[m{ub_.aeyݾ$t^GDk[Ӻ-o8zkY#y/?ïȿ-uYs=/?0ô6wr^'M_)omx#cm}nC61>tm}%~$%~!Qx65afoHѦ%&'MP'M KN;1v9ߐMKO;'[7$d[llENƮ>u5%2~O<[DŽ|O&⧝MSO;)vZ6M?rJ!1vj6M?l2?l2?n(ߐM Oy6Y9[{AgX'agQY{LXYIS{ʇ8Dy$ gxx?_Lu8_gWޟ9<, =KKwћ<] dz`=+/oU`]>GF+5|Fg5Ll1 K3e \k_Õk\2NY;Z=_0卻;]_ XpL.?XHQI{Na=Z-6`\_7$g4&?`Ԇ3^BP^ѕhy(F|b@-5gI9^Z^k: vVkб[Q{kFQJ_V>.ȌwWeuNc.w}Կuu֞@L 5 Hڮ0$Ҍ"G#q8@´샖iiL0x)⣤iP|NK/-ܸRN+ b="/ X̘%';z ixUQ23]LqeGpSHGׄrbV$ (}bt-1uAHDGH k^UǼca6"@HEÂ:<}4H["MH:2]Q6Wd6\KRJ'.{7[xT"L&8B3V@ ?lE98[D2U<8l̴M:ܡ\Ze5' {m1O*"ә5z!Uڢ p7Dv\u*et!qs !D($.33=Z'GG$nTNZ6QR"d&zijOn@NZDM/,dVwAX9eN:-OSxG9",WI3JN!a$,DT2q;8d&mN/s6'IRMFsmuo&9VH= xiNyUmY#5UlY$b[7\ZV;6R8#)~X#D:A,8mfn \YTc݁Lq\w*z*aw$1 FXFOߴps;m c"j8 }B$|2-9\%f`8von3\S'd| \DZB><}  FfׁkS]z]LJEf޶͹CZ#w9͹ÏZĐ֖E3[c AD6>ޕ^fƙLڒ"&{sSM^V|/Cj{%h kC4](-dɵmy3LZ\FY$6Ebx 8)#4UJfJg:P@.>y:PjN(! (㈣ Xb5P/ܰ9St KtmB˹·M||ޛ,\M}Q 1NV_`js8;L 3ZܬLj.IP4Yp.i" *#֮B=\(W@)4+ĵ )gKuP2kI2hi\>h\Ǜpu 78"knT UrpԢT`by6޵rj-x[mxr<4%ϋk Lλ$Ӷ6#ϋkCr%r`wv{geqŊs0X B*?dR%(t3eV$\"WPJ"f!q%gĔ&p]:)ɵ&9W%hsJtqM\6n‰Ѡ.jhcexœKm;4 Q;do7əl$w=23k8rEUs>uJ옏:( 1ifa O47v7Q%ThNHyڿ $lyXk.G_-E_-bF_% ;N^[o-qX[-#4}3=Rw&e +9O0yĬHJxqZSi8QׇLD@%7 (g:O2kJric7L4uHԃdtVeԄ⮖* i^_9&/H˲atn}+,/ҽo–mk LzzZWIom`H#yVˬ X, y5"M%53ն7$rr~qʉ^]/JW ~-A\I61)\w.Jfq-@IN{sx8LnYwgrc&ǗaRX]Xc.)Qe@E'xNpN9r,{,Q gUڙĭ X2kTΛuddV$ӷeeܝh7,qu&M fwH:(+bU\λG7_I52M`$MP9oם\ P\7L喕qg*iT.F% 0sLZb{[BHN9zbVg:O~&|6qfd^Kibㄖ@s뜌KZ2kVΛud d W$SS81{ZFrb͋?lj&q{sL9'XE61\de5 %ӷ!JomyӷZ9y'-fMwdeے$bdM(zyfoY*WP0&ɰ‰Nb]it>s$s6A伹%̭$̭ޛãIfnˢs 5 #ֲ"eOrUY".tP+w ٛeL4d`-&p'4mn0[r˷m$S$Szo&y%Z{`mc֦%nɔ 7/;+>ہv]/_3$6OtS=ªyV$B xJ5BFhSzk"߾9\ H9\7G~9FB\D LJE(')<+kd0סg*P\@ITk\(o#Q2kMΛĩLZL962kbs2Do͉2pPBRdR&tkvÖLĕ&t;?%$zo'-fwKʭQjo"(m26g 1^ u sA3[&s/–Lns$.IL$M<9og;rN2kH2`ܲhD ؉EJB2PSr2M(NU},.wMŽ ^h ,vnn9\hruddW$ӸgB:hq[^qqur)rLʰl(V}4uVh4N %R EQ0cD /jIʭ U2kQΚud&d&W$3h |/+Q y11Ұ&|q Ƿ[ף(5䈛uHC]~q7j[ms %V$>K2c8 iN$!"4 {+9e}8bIlk}dVM XrN]?My4!k{ߧ?'}5V9ģˋ_z(WOҟ^zA%Bd^rF1 ?`9vDib DȌL5 }6g:.ԩ)G_]ػEd^mSʄɤNYVѠSlϫdf,q"-$GF(^l\D[h _noٗRcR ϳѯ7jQNguc鋛G~W?e9Z$)z+dJŦ7nc4( nx%8A!$T4k*R6o40om|fHz_v{`,dDRan8Asygv} i9@|ڔuxmZ֦$֦4zmygWe]ȁƏLƣĕ1q~z?~R&'Nˤ_뛯^\Ѹ>KŪ /qi4ԦQ}Vy+ C}:qh њc9׼ԉЮ{}f:|t8d2T7hÃѧheOeFt,#Nsk`%}q9EYׄ+4ط:-!nlmqa +]mp"lތLU $}=<6u2륷,Ō# :GF,Gk]3vFs*}D>^ȧ|mOgZkTSkي'?9%'Z bnVB '*k\ 0Kz_fv 3URv&[uC29;IPQ qv)MlK2J;gfS`BgqETq"1ZBmq'cC{SchjMexܸ~ Ieb>3O[$;Ct[(Hk\EZI2kkH.<>%jgnA4{`n]/Z\Icdk%".s2b 2mK "NT}rXTjyAT-VƩsak=E V(C>sHlmb>.Z9Mgx?GwhՇMk7 %8wdnsomB7x2%JyOT㏿?<)$S[:︼5n孃5)ўU=a#|]緗+ePWx&^6OEGWϓY^{ k?\F;|ӯ~_5|78ڇYBh3G,W8aO1|댃H@#jˆ6@;F 0pStJ!)(/~g Ƨƾ !Q` 4p]ơbRxG/ЯO;p[ǁ}!G 0Q@bRad? a5ymj4 ^`_8!P ve*ޗX&[#Ge - =Dc4ʸclWЛA3F ' xx7pb#cpViH"xdcJpXm {#% Tq.] ;ExAHB ;ndQFl 7M%T0~U];Xڹ׎U\wct'^pj]},QxѾ glՄU&;S Axʁaq[)p ~A\څ @‚=)-\T04\j g@x+# (s1^{n_%wiQ7bLATAգQF=l;` L Da SL^1b X4ЗQ(磐2ѩ CϺScwiq.thFC mBSf}T\*̫O'P0g0ȅ(|cRy7lu`_e*߰gޞT3o6wt*r PIN.`L՚3J4g _=,po64Y4N4NhuqH Ai cl+~D3 sXp_`D1P CAyct1в\lN2_x#Cq p , o=  FLj;LoP0"blHj#I5G Y`;16A =Z WA(x/)|!nA&s19&Q~GA>6&PFs ;a *ѯ*C_%1Gd2!'FG9h'Wa. 1@5adIBi .*Р.QJAC3? ]4x+:fuN Xv`J#|n8 JHca5BQz0$j[| 0$ n0'K`X5LUA50z;aQ #A{ ,=S ,5G!hUɹ;:mXN`9v{Ox~-^@8A7y \$" o$ }B2y(T$yCk4 AYb@۠˩1Pku` f66. 64 #C$``d<Ғn>S/P"]q (R0Ч@cCt[+4*.Č4h{R㓇WP} 0B1r $Cc>iby!DCF@/\Pp#Ψ-w_okڶ¼]CFh[5E ?ϕ@8]A W!5Zc;KU;nF0'iqYOUc1љ~FY+ a_$  Qq!іbx W=A@Hp8z &,2xdpd|fTW(̍cLųC5o7leDOeE釯*sLj= + 00i,exH? "ѝqAR()"~ A/DA˵0 TVNNy'I|L9;Dhn\㽅0(1A/Ckq%B pF̘T8X09Eu>FfAc|X,YP~>Xv`<ND &BA@'$Bd\F! 菇rEb 1H5[M9 ֻ BCπ1܂%b\`ЛUhXBuG34(=s>إaǥXwjrxJT Ѩ򘰡En!D;?G&H` `:^ X chm4(?Ɇsbh Ʀd0Cb6>_[݃77 #@Fg!B0J 7!s:hR`@.phUEbdGQsAy/a-\JuÞi-`ϠMȍ@<{VO)Mз@Ϛ" [ t ]06Y/vŇ? c~/` V9fs+b#<(~瘔Sb p)Px tA?1,c ! zQ5  -)DE`rTڲ{-$ǾOhПFϒsge_e< Tcw  AKD` A?$@ rFrDaV̇yi?mBT*Q0$#*Et10q; 7#2{c, hDaB A,-Xᅑ;Lb KHA!s80 ZT|]w,(H 4Lf0N@@FDEDcXHj+BQQթJ[1aJ *۪&>nVcvL*v cXPc԰8&T!`nŰĂYit(ʳHQ цŶLzdsÅ|pEYΧqأBA/ckk5Z+zkpK xG`[--,g)F c `8G?h6| M$ eE \: Q~!X?Bf!2[d B;d%xIxq$GCʰ9`q&FD}- _*UOG0hM4HEG6=ܤQ!:T;atP#G1̉ ^aU? 4|(Jڊu<sR)4N |!*317c#VAE)JAC)l$ f1{,ph1>"ނP$1Wr0w\u8]qeщxԘj [:!!1mu8֠PV jFZWM׬0\+{}XCi訮tU:*?Bz[Z@CDB+B|.T*B*BEmUB'9bǘKt@ft P(P{xe‰ęFfX\ (7`1F@OH1z’D#7nL2`yxl@qNC?`8G#va*B](b `( dVx`TMð#rXb CSD'RLbcs߼ uiƪS1'kL][iL60Y6s&?<Q=T13+c,-1l7(&bRY"NFNAC1,G@y4-qxմT;%;|_D998= 07E Q qd88b}8 hl }:L;anΩ˿14/BM0.1$bkjuYW o̺B׈Co] SC҄bWpjBnA0q8bjeD.^}87pY Wu! 4&SLfC~1roLNW ay<]c;koWq4]y$}ܪ0'C :]cv{`*>09 <~,> §"L,~߃.+x"nG"J>aJZoಉ5z?W=e8TtP-jA&!U+$W-{ܪՀZD7;.7S]5GKt#,lJX͹C CV,c%AqEGˎc7'ˋ@E'Lw,U2؀(GCfͼu-/HflD2c[`l :uZj<ҼG7솫aKIFllEi\P ntۡ;Cxе ጽdX7?L}E1?8*\4HZrޛCi^Pi`/%䚃īCjK17 |ujӦF1]ѷ<ذ31d~f/{su3\D-[-9S/*-c#װJʐ|OzFOܵkv6RShPi;`$8cOtmQ5=xB=$֓tuaC{t Rl/5# smTA$c$rDE[qiMCZ<$A=-&t`^iFS!Ɋ$pO ]Y-]H!M{)jTЄPWFh,^1+ޅO,ɜmOPKh8D`G#Potentiometer.fzPK58DIЭHPotentiometer.inoPK}IProcessingRGB.fzz000066400000000000000000000452711272363601300332520ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/FritzingPK8D7J}ProcessingRGB.fz}ے#Ǒ?`kl3 +1)id$՚Z uBuu7h6=2 $-S1%w:z7__? ſ_0n/Wd:#4c"׷7ʞ4|}ûpz0~x<ލޥ~=D~|70NUfvs@/&\*ʐY돣[֘4Fs ^9RV hk$ᮙOWo <>/.aUsts}9||]~5eoOVBKaoWy>eM>C`v4GϽ?ַrf'=>(pG-p:/pi#8xΏ[sBfLɸeO T4ry30=b Ÿ)RJČmrML)9n/5e+8=n ;W˪:Br](qI)tЀj"biL_.A|®}Ž}ZnmOiOJ+.} -Jފ޶YrھT1i)Z>s M.(瘆 ;) )i1yGE2?aWE>)8+i+ " 7QNez"N>mEqQN̝Q]( c_hj b\X,3J5a:A6?CZ;e lu2uț=oj-"Wpi \ƜO cqCK!z H춉85V1kAw>n{ILw)=1uAw>n [gǺHb;mg,1 5WǍk\ 1 {7cS%}xƯy sKs4LS?mft0Nح~c3>>)\Ǎkщݛq ;8SSe}ܸO'n3>>1܎ZǍk紜'&.y4}<M/_6xł|oy{ϭ;VR3P53T)*s I2$8~;ir~ݽ?~zyw2cs. ]ϰ}o*6p* f8{mj뉢lXLI˕r&F<17B ōeWj_IkjB.% jZ@M ) (BU&A3%%&,)(,o"K[ l"E1& i@rF»D&!KW{y;ofg pQ z! uۇJ[DJH`9,$#0i]#?$m)0JY=fyz]֚`ƊBV=Zgd[ =wBcgFpo#:_ @N$4aV=Z$i$&$#nࣼ(ix>*7׋R %t`4S~%n|Խ~Z 8uYL4TD"2aMh G4tHkdTt#& 9./tBDDT(k<2:`EMżB^/|Ua+ FJl:j )Ɍ>нQ5[>C%D'b5X.Rn<7!ḧ́EŲ8ǀj3;$xj2wGZK IL (Ig'G=w UJHU|yyzkdlMC/{W/^'gċ:a2|D:uj2ǩum"6BzEt(ٰ^t}`fVR[=yD8~(=csg~g ),E˼ҭeZL3ʤ pd~zz?lqprN-0`#V1_]9l{ ˟W. nJrvAl5̸ߐRh'08Ѐ8->4=ln'S:uZe+/Ӯme\a7B Hmwy\&@LB[QNYմ\:xV 0 -%P{5"sσ8F|CE1ya_GsfDkWC< מף戵\3v wy`uoU~☵)BԂ6U'n)b'^<9s' N3uDc~l>ns7ct_7p7d̈eo1~.>&ю[M&5wifJy׷Jt-2֚dZY\}.9%#wΒ< kn8[ZN)11Np}[d*5uHc pl?Yo$t.pi}~|"c w fdVe^pG_XfF$/zc%?b Cj r8uj-/֩ XȳH%#>dI|VbϐdZKP,SZ-3J~ 1hVLfTsyskf*7TJt| XkFS rPS,Mټ _ҙeYOTxrQxt0$L(r<GZ)55TvLM†)3jJMv} -RAZ*ufR[R"T ²՜6Uϭ)q"?KtEZjJ:(ۣLI-M٧FKR$ZPx1-KKq}ňDj}e \ň߻}D@0~y~㷴%;o߈>]roV꒝[c2\\L 7.[Tm|JE{wk3vއylg4 e'Bl^nTw'B-=$e6X SeÏe=euPe6eW0Q^ZrFfygfT7S'|y'ɴ弝 ~lƂӒryg: #E"ܹߩ.%1a%1R1pu QT",A*O4%a#L~c"uT$h$$ѐI"fXI|S2X[ImN23KMDKW6Wan$f$1CIy>9츰f<0:khw 0)7/lIpb aBQXrBl^#YaJ_]DȺ7"KԵ/ D YX8aH8ᡤ9ZX'ڝΑn$Ƒ Qn5(rޔ Qڰ_}R,Tr]pչH7 lk$qEeܛHhgaTN]ފ/K1xJ;w$6&u IqI-!ys5bMf7;{^葘Zk-BH"iuo&:."ѶRD>o)Vgn#4<֖)DmB/X@-fhA ǮkC{l4/A7`T:"9[HX !OsB4`[J|aXstywKՂ=ɘwOBcnk|rD& {C4Tw#6")`w2T:2t<в21dXoB#'5eXZTtLh ۢR2̢Gc%.kMCUu׋/!j}I,qIa 4vig<|(1pCH`uxdjIBl)^2Z+9`GYYh!t@5pԇwfA!eE͗x|p_\=\>_~?9xF?3/ Z>Ps~8^M>lgos^D SȓE;̰\fh]=˦hΖmD@DBDfÂiY_fDWkF"9Yv n%b^M#,1솘`3%n3$n-7"ؙ=;:E0piMмW"eyGU* 5M軁L, !ԙ[+L@R^*eu"eJd-aY )0C%k5#k9nfiI,,U'FLNHèJd7H[_ԶTĞj= [HUZitL9hQ9d*e[Q VkBڂ/HLFb5"^#:ڄ󎸙̑Z٪iORmN4YŦhǼߠ@3.I-BFgҗ&?B)!KRe&dHrGlWvIo- Io4&-XHmA^$LDdPҴmYkʮ)s.a*Soa`a׾%"P%6嬉@IL(ILښ$1E񎙜HL\灡rO-Jjq8fkŁ-E+%r"."&h4Q'ɹ^HHIښ$lswqI&qǼ~0ɤ"ba>DߚjrOtסH) ^*X\\rށmY(ŵ%U[R 26QmkM;WM%"U[<ܢh@ܺJrY![S(ub_gAHmlYN%U5WkSΛɎ DZD㪭iL[,zuuJb9SKlR@.mmy{YkΫFLgXJbqm"y8$$WmMhhq s2q+^*xfU-J>a+Ln )Љն׉;EGIV=ck 9vIlIlښ$-v+X_J&;fiX2@dqS=D #YğGMDHrOV-#;iv,$-w3]Gn-Hn4$͸=wSsP,r&3$ Ke) EXL Z,N4MX}4Im`$q l9m2/k9/a8'^v5y/㋽_ҳ7_^m:z^}qpy<~m[SjϽW{׫ɇ8 /|3 g)HmkE WS{J}iS e~2a~Ebx/hFOJFد eTyZ3:{9#V':!ni3:uxƠMHUmٮry6x~xkⰣ̚Eܱ1Z4(5Yn5LzQ'b+5ރZ|$ w[ҽɨ螧a6~>CGʉUlsfkY0Õdc3l{p=zǏ^RpY=smBsVq,"֦-k[+bmQx)FQ T5ή?__zIt}6}߼Bv"q+z1x7wh^eX&c,b:zfax3~x^n(}7?_؛o߼Moӻz4/z_ܽ<}yy#_~|t7N/݉DwѥՇ F+#_px=0|W=2><􏟞aɇz|_˞;0?zAAe$_}x|b3}3td88\*OO7{3{ŭpڡ[{x@q?ؿ—?Na.BLho5o=$7ppjYN{5OnyrCfPȻ~xt<=̟Lp*[x ),LF3$3ɀζ} aݱuǮ7H6z 3EYEᎤX38ACNSPfHF5Zf1Qp5gw9x|7p8a(1|-MƟ+@E{0*3G,9=2hDZh0do*V*8P?`RiTTD<^PƳp%<"G0ޝ-82G??% && 0RThԟ=ZLƵpUB݈0g?6~J͒5ا8SfrD"w[kn_ 2@Acc+5/{mP ϢBtP<;6Ly A|LaLuZC xAӟ.- BeǮ߸a=T'}"Nr@3EeDfAyIMIL5UXT,ͨM;||p2p+L.IYO#n# hWݍ'`,u>#s b gQ!8~pYĹWwȩ:ЋϟeyK֗Xᥪ`8nYAcJCo$4n pe]%@"FX@w`܈7Íxǚn@ 3ݘ+v|Դ5-vxll`Q\+sӮҔvڔl 8iWɽ`$b`8 ") f2sMQݼ>hSpDlƫ``e P&j֣8v])^XiB4-Yh̢g|/tk] &|*PKU3TJS-4LoQ(f.4Ѩ CϪQW<1.4h‰% 6"}[0;'jUL"̩'P00ȹ}m 5 0V l4̖LިSǼH7'z2 0DZOp^:,h `Z]@)zC@wNg _%D8Ğd,'5NN8hgAGc a'R!|!{ xDA14lM83y'+5.P\ pƛ$77HRm@f@cvU14ܑL 6CR`4 q]e^y`~~7U %gy wQ}*(qVpUnBO` 1p41wQC; geD:Gᾳ^{^XDwDHm Is@[j&?A;|x h:a` JAT j$MWQ| LpQQd *ݱ{>!  F J3|gg%@K6 sJHca BG`H xxuEx6L$ nP+J۠[4LTA5S0zyBCt8c` LU3J1{U`~}O [%?s3SN@8A  oK$&p@b$@_@CoC" L#P@MN ]ݍ MMx\QpZmɇ'A'hYN#%`3h18 `e@!/RJ #m a,1n+vBpOz|sעM!FQރwxu XNѐ ÀW3j y9ۊmH(*~hڠ#Z:>Eѕa.9Wbn)tDWT`i:n01 `*;`h0+')n9vo.Ѭ_AtʗuAr.Y s?z”q#枰JS1Oc|f0bQ#AQ+e/, ]BRn 0Xq7[!&LApbyCpz}1ڎ@L@ : ;`DZW!5pDU C}0'ǢxDžHR9c© GÎ7 's B rLx*}8!4ݢh5o^ -UC#8|cwQ Onll0+S<_E ё@ܡ 4ULG[_zuƘHD"RLD3 ۅ$d7۲vW> BQP4, zŹ]Hƃ>2h BBd0c+ ;lF;>N;8t>] _ ػa`%.+l)+J*4hUAsdh-K?`  \ (%(G/?5 9 C+װ"v@*xRq(1-Mg H $5a#A"a3NR\WQpǥm3CpP` @ADA7[}ajFZUӼacaކ,󑮇1wBίa*4~b0 \mG$2Dw8 98aeLȍ@<YV$O-Mж@Κ܉>X/-4!zE |7EA/蔋.<5/C4't<*$!8‹e+aP&DL 3E;+ ډE`1s>)F3eoGWzP7o1F9*9#ȵxoSbqi qRg2C'e<]*Iz͡Q)t0{ox]hl nB# bx#40*Yj;mCgA0 !*Nh 0!fȃ30XiȌX|<ш„'H p`z01;,P Q=npC]w,(@ N@a a9􈢍"Q4Wz؃S[1`JSjy8 0C48*l+orRۣo˚T<{Q|H Ӿ 5-M1(>A&U(x=2HyU;bʏ;s(,9+SsMIS݂`hR)<xiX<|DƊilUg|6&%(Mg2w/,Xcù)B- z@3`pq)3U WؒE Ci2;UӊWKM fƆZRyKyCUP [hdl)a/j U- UP- 5, F$T^h\䈎N c,8SA[LGBSo[' \J0ob/͔)Q rY~#c'LID;"Xh 3Y*Fqd9ha *DH9'`pfXVCS q#eQ44Iր~lq#0vGD+YLa\A~Ab훛tY%*LMSzL0Y: 3&f+?Q-gQ9y-4L??cj nAnaұ#'^1';2ٍ e,SVQf4D1"kcܨ+Z g7 n׷O{/!U'Vr1/S>/DF,pD@!b.s`” SBo /p]>>Ti,X,qp~,X8UǢ(ƢkN 'PRŠ~ym0-9|jW^ `&/A 39!#%}2gcHۗ!ND0mA8 ĜW>[_#X_/8hޖxωRQo86 h δgԳЙYlw-[oh".oᔈi:F gi\)\W0b k/Sv  l+0t`_-58OECп}v4?[|znh2;ʎ`鱧/?ͨq>SYd 83XvWKA#~wڋhBvtc4P_p:uvض`VjvQŒV %mڶ-}x4|.ϣO/5e|M>_)y|B}_~~or~ƛ#85[S'+ۡߞ33+Wsj͊2tTJ.+>ky!`nǓGӶfPnru=lTЮ7}y~u4ܔ* Uu f߸šRJ08C!$>3cv+sC_kK/mJ:u|8 )&9AMi?oB(!44'#LI7_oEc%j5KԶ$EǑakCΘ܅4:TW~%PB5,*⫰'1Z5CJ"nեBJܠQB'ވ.Mr&^)3ZMlLY-qD`%nm%nsP o˶4͈N| $GU5#:(Kfw(a8$UXrDfYUܪ dѱJwlu[ذ<#&vf..k[@/-u4q`V-$^$^YmMØr%$Qơ$Q`8B5J%=G%woUJn$B ‘:Cr"smuCݧ!xt\Rl)6c،)痘d Ȓd5#˙G(udxdX"3DH+wnUVnDryJIIb]ksʣذX|IgS'h0m^)D,[@D,iU8JYC*ۃ**eʝUTdF >$er8sQunfb}VxWRu-8Qʨ0sōYŭydI%$6$Ng"Tު TёJ20ed;<mM#+(:17Djh $68$6)WQVݽUe!Yz Pj_:peS\;0eRpb %qp/I5$F$FYmMrCHI̲q@I̲M0I䒫( jrZP^-h7YN#y-#C} ,9z\yוu>9[yǭ] TJY:d!nВn5CKJRmPmLRFL$˝[U!66bIvOlPϜi)EAvH5MheZh(U(UV[0}#,Itq$It5IQS7Fܪ2S1@%ψ_}Y| {Hyu-DiD i"$S|1s`QLHD2ndIDښ%';CD'ǓD'Ē$BZy"F+_=QlZ.rTI1A%b:ED-[D-iVf/sˮ%Qq%Q61d/i_]"sPK8D7J}ProcessingRGB.fzPK>eJfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/Servo.fzz000066400000000000000000000414441272363601300317560ustar00rootroot00000000000000PKܑ8D޽ @ƩServo.fz}F5w昵fH0CRM[-izZ&gdfr$sHVVh_fi#W$2#ۺ0DZe`GLrKpx-Ȉ_**8cCr3⇄[;..=o/5iOOi/AnVJrcnoG(+p$͟f{IO܀6޻Wl2s-]u㛟ǿ|}n5?樂"gߌ&La~{3~qYr޳MFC/p4j&whr?_C\t_4Sd,ݺ/ƌ6ꆘo(eȑ\ӛ'')Nǁzo3 _9xgQ* $XoB>zVSzx?F͏;{h0Z?w~) Ýz\&Jثg BdJqP9 ]VIZfHR??o)$eɳl=TSe5w,+4Ptji5oEAۭoqQn}ZJQuV*++e-ܞ?Wy-~ꮈ -QOAF:o;Pryq_(QDUEBH##%.)JQw%&^<GsyԶNQ㗝8j/8mBm_pҨھ*|b.Qegeܗ*jqwT%QB_x]pr9&{k(upQLU}Y(_ڄ"j/;IJ_pDQ+}٩LmſzJtQ F4PZ O T.c~@,[A .1[UtUEFz8 } ̹lbNeaʐ2c8¢vIQPaMCC*89 5#B"I;Pd¬{yAmC#^NTRhJ*B iB I@Q !,vcIĨ(ePгh(="?Z5\yYcHL4spw1 Q2`NTP(_󣠻7'u a "\/Ac >E/mݭwkئJ3OT*1RWFA\h;#bBImS6%F49'}/4Cymz`.)rmD<$hSOds#J⟚(rA7?1oDҐ$Vt:s-4 9Ihɬ}rq-U"a*N ]/ Vrmɪ-/[Kre n 98xJ#|P)*Aj7"e]'Ǔ-|tg(yCuSyi  "qhi iD1/= s$) &I >ETPsscOR6 ?U96@LFq|#I0b7zi}KsdE/,s>*P;ԭb&6( 8`7f/nWHMC iĐ()i!pJїzޓb(! #eХ&-45[G#)3cy񈸺OKႠjǴ2_Hr9{xs i9,ҴБ`)}J[`6F1`H41=)#R~OgEt/X# <|ͷo twᏻi턞I(o@ %î%Z&įԴT]|gr waWNED(mdr{="T\zO WPLSk# ^Shl89Y)zxnoaSдqb]5 ^ܻlmU͛5H)ʽUޭMK$Llha{sq N5;e78U{ܭ;g.wO.HҔ4hSIw髖tq;?t((DQfN \_]S^]YOz2 Z/LYoӉTxm+bc)S4qVri6@]ungF݌zmIcKSsF~K^r|˾~7-J{f%<m,mF[֚v|fT0# ;'N )?!X0&  K& <-|J1PBqF-Bei?3p=-Z3e{ HHݜD.?9m8ZlN ͩuRj? G}Vֱ/5N TXvu4y!՝79fj|Pu֯x6v)f96}@]5Ce}}F8 qg)]u04aQq5]6.U4e|1#iP/Ȩmb);l ;i rNF-s4xKVKsUm5S V#M4R#9LRm4j>Ԉ܈R_=v49`imÜ3?ޯ;(?7r\*t"D.d\jkVQߺbZ̐ldch7;MkP7 TIԏ}_úC YB,Ei\ KK.J S8LCLjqV\F´_ε qܰj/rPՉ`j1^D?O.k4TunY{ilue&  Qz4V\U"tx-ggp\;SVE~V D>,n~I[7~|T׊&K"CG^mV9f$PHO/7Ai]7-GQ#km^xkxk4qtϖΉ4?l78KaR5S'Dvr8LN(y}y&VaYH?D/ϏXtsn*2L,$vp!)aU&I_HڗSA$lĕAk#k^8t`Z@j;TX3 bSP["]k1DV&zLP7x4YH-_;1o]52_ٯJ894_~L7WPpqyL>KYfoq,۸!r ~߰Ts/2,n:ֳ 7z0Pmoo=uLMetcj#n!a9Q]>QnkEȸO'q׉%}&;p+TY7Isz89YXDH1"xY,r%89dRnrkަN(Չ&43fIP/Db,5unl[hx NYڒɊ[;ښnU٬k} ULE/Vi\׶`q# Z!,rÓXfR_]#3ӲGO9ZqQ%SU}hE/nk)lz-0)Lgc &~M7ת`\g4'k٩䤪Lbhj{-Mkpiv 18ͳy#9=~w/O(6{=cl6[s6LVM=7&0N[o>aq?xe0Y~x|zLof˯/?&y2C+; r( WqAx.vP2Ʊc]SzC:t(ǟs'Jx:Pn1GPWP'}&'U ~ߎ'po w;p?)\9ۺwNO=on&p9q2MI/MµGgZSzb96Xe1=ѢPeeWWzbQLlcAgbpFά'S@ b{¨İ[Yf(&͋rcm4̘ aXʤ.QMkLCb@DoF}X8.p%Ư!&b^CYG=NX^{lF6Yc>ZMlvYFSu}ҝ>)|?B}@ W!ځXpM,5<:;m ;tO9JV5bj} Gv!R]XVt"0ZD(RZݚ*YV8!Ён;42]XF☃'4qQo5#_8xӶC6!݃aee @(.֌.جU;§8I[`)mFupq@Ð?zQe2f!#c J*H|VV=-t'EIxejFhnXO$h'JKm!hBFL֦aϠ$u‘jjđJ|2u&kfS˘D"ґljL?['͉Q\*=;l. I\mXE'ٺʾ Kj?Z'>WSHh5Gh5HheFha1AiB%BQDF!#Eknk̯j'E #ԙJR w s7^1*DR6ۉvbyku<ݘOBA=J)f H"D^m[!Io5 IoeiN&׭dG`naSz teR>~E֢6 YO[$n'LrLNx6%Չ#$$4GjG qa'Wo@J)" ۩ 2NAd!o%9Dl hBL-S\fI[I[#H$k5H$keiN$Țnazzse*#"g;R:iNMwqa\1gQmʳDyf(ԭNyԭɸ$2$24ʪGۍ7[Ri5'!q*'m(7?-2I0\ϖÛn K2S,9it,Ht)}J;圴 Y·Ljs)#̕J䷜e7^o02_icʫ7&ۆ7+}qn5uiUU,vtwRalM#ڌ < /HQ"B%/ L.ʧb|=Q% 4~~/h B9+3=m:=rnƋGWQ6m޿;KQoldOT1:48+~w&i#q|&&jCv3eL'<+Q|W '9Saym!G=E$=|-! c,-tG=o}tX˂=3!0dϴ/ܒ9ZS97-Sb#56w ST쁛>~xܭ6ia|ۦ9.k7?M$. t/V0LZY{vh|wW 'Ik!qcޝ?~ztFx6}Mt8O|ay3_w˯O|o_/;Ώo;_|w~E~ǏGwpwFir^h:|za2X©U λ?zɇN'}qIMK^?ibx0[~*6saw"CaH)i))I5?WWJvG3CeF,{Atp—ߵ}ק` [7w~MMӴ3NۿG2ɌO ;^w o|4>/$Y)e'*)ߠ;IG0clLa$.QWٶK!;&=&2Rd=/{ᎤX3(!'ʝQk lrYC!g_)x)}Eu(/ q>Idj1$>evJ?IEBf5^$\SжřI0Yr(+S),_&SOA>nN_^> "θ;D8~G)'PU[ixH븰1>߷=O.3 pnɢ~W*OjF8Ԁ*lAjFBwKU] Kc Cb7ݒ(c 迸0ϻ;}C(PA` *ÿC/W+ếJ*%YSn3uNˤ:[ĩ<\rTn *p[<^Μ㻥T?Dg0 7*o3#z{N؎4"ڷh-42MLB  ?ThTDnzJh eACx˓-= gx %v0.o7fb @ a#x,x\4 QPЋ.X ~Dv ':R\lL]=x PLs=B@( :̂-R @b%9C|p#7⭳k2kdb.pvmqVvڅ㭋}ChGpWv%QoK*ڽjSt!| 8iWɽb€a1[ pq^]'a]!LQp.D!>a67Hf[i ъ^-@ :cb=ʲcM]|︟e AW;-M#Yhd;N@\A A@qȂ+`W@ gP䭐i1 MrP7}<1. hBG-m\f5a5D1|3N`,aOs |=hgdp޽k: U@0-jSoj1oG)MZ-5A8LgVnyK2erBV lb6lW;*i&!֬!Xbhp̃3`@W\`c0l0=@<`Xct9fBP 0҈_ HC1 p , tt3&wSi xgf(h|"`n( KC a)IЊNEH R{ ޳үX!/I49G(x<  ּ(4n<ш, c Is@[jo`L>Q|,\r 0kP% 5R+f(s (GD ݶ;y{9 Q0 \udVrXu`!J 3,w p;#$>tЃꕎA!Ѡ=h^]^VО| 3^ jD LKh¤>B43GZ_*C EB\аdBV)h5.Zʼ5 "_3lo%vvy 0T𾁖毗HL+8'?^@ CoC!a˃ B#P ACN ]ՍςMMx]Q-r䃄I |ˉpv| H]ƄJ)%)1B[bVr 9#V>ŘB} 2dڢYHRtݔ1FB+1FPnSAb@bQ~'cuc'2VLsLzS 8qbçeXR@R0yiT$kRx׈B$YP}/z s& %S[@18EV%vW `l3Z4 `}^ Ĝ~1.K 2X`DTu!xzsB1GP`y]vʄSbp]"` tػ,O,F40 J/[G:*)#dBr->[K̎{|˃aM_ (h]GP -p?'cWc@{?&2 m"^aGr/K !X뵮 )Ύ]ɀ"+5T >,,Ƥ|6Ut9f *T*SV8[;$l&8I 8G#Ȍ a8/t!Ak|C-@HAb.4dNzQwTaKP {Ql19 쐐7A%1M85O 9`p&d'4x?}4xUʔ}:H fn_g 5Ac`ӠuDuvtGe1 RޯjGTW})J- iNL8` GC@B8r@P[Hd)46KaX^UgSb(7t%i K,3-u^::K0p 1pb}go+Ʋf!:Έ?0;%<;9dC8˂ZLamw1s^P4 :x,޵"'dr'(C;\`ِr",Tı {m c:v¾]$~8+ LR>6A`]aFͲcЗq.jZu^Wy( 9!=j~l WpjB݂Qax8&Qh?eA<\`4p46nl,! 4v$B꽗C8T Cy4ja+gx~U[ؚ K#+[i+lߎ-g_ד~uy?~4Tmˎ9ap_~ͨq>}Rls6ߍ?J+1$ pRъpG=@;K`%_n} /ms#+W{NT;Ei"ĸ&6~_x9pJXb5ni>./OM-5*ɻ,fz_^8HwqUq+4e?1ZwuTd=_|΅Ia9I:l<&g_w;xO[?=gcy0,z7\&;-cY,F,*a2u`YNLv87W{ _㗽1Xph϶ j?Qq/0oT;eej$_N3- Y1MzꖾzεnGuTo)hqٞpפK-,\lĻk'ӿ6ߥO(P"54.- C($3S32-f=ѴEaX-$fTpBD_ 9J楬)hn=THz PP+*lJMH"RG PKܑ8D޽ @ƩServo.fzPK8DiuqX @Servo.inoPKmBfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/Synthesizer.fzz000066400000000000000000000556721272363601300332170ustar00rootroot00000000000000PK8DK! Tpart.sparkfun-electromechanical-buzzer-ns.fzp_69129ed5bcec2b4b26eec3be62bb1125_4.fzpVMo0+YqۭIʪm˶^ j( 6 b<ϼ癱/a@SG."/U4t K؟@&Ze+t=~[ϑ1`3Sx)sN'0i`Xi"d8ID-gh;J{Ub¿6BZd#8$<S$GI-t ^%W݈vjVCM ;%NsZ ;ũYI̲XŢW]U/SNKGRO$3مt 9at> {6VH̐,S½܉͜[Wيo':N@g Lr$,cik:0w&"T;Z(rAn<^}FY(A[n |`aK —rS#K&BAj9KU[)~)݀,jRCPBJȔ&n x_xOR ޢhi=O<٦](-8b%TS!L~-\yqԦ<}bkw\AnEr8#$d"fJO*Ǜǘ~Hp7wFԒ$*9kz?I:Tʺݐ6 -INZo}񛾦瓮[NVi(AdYNvH9X+g)SPK8D0; wsvg.breadboard.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_breadboard.svgVYk@~ϯi Z IIB@!-ZWf%N~}G+-@]"ujp=2U68#;<8mJ.ur((fUmB<nRP2^1ǖ>gy=m^2@!X㺞g> N)up0z<(q} E&WMD/jz܇7EQx"M*& ta+a1-x]F2u0}l$ӆq%t9 UՂ<Ņ:Me\Iu5LٲIˉLƱʓxޤLlU= a`A.N1''`hZZķ:߰ALa:n6&4d#Q'y-`>,:KwV"}f<$b^d[pVi"'RYj:.9B'ۧADŽ  #{X%dd>0> #Czlv-{#[keyXy GUyUso' 3.J@ 4:apFdmUlkw2:x^딶4xp/]olkc4@{\G7nxś]aⶰ ;nwE1sTɮv1zP$snp<(:AEbvX~Gߕf/PK8D/= {C ksvg.icon.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_icon.svgVkoH_q~H+a<-;UJJR~01*N~} u]$.sϹ͢'VkF5Hy :mk=ۼJRU!`ϷpYֺժp( 61P wʁ~p]o @"8]t/Y3+)'$,q,H$`ww$vdRYkD Wb~mDMυ%?ϒ hW4aX؂![ ٮڝ0AakX$/]-]p$(`ny؟Yl{ۚdZJ×:3 jmPdXۘIQށH[]n RM" MH kJD :as'dCVlx2=Nh Zkb3Y;E++ Y$2:*Dc7"Cb$=KcE-=fYjoX/[ iN~6` :K|5~L^OPK8D;2bsvg.pcb.sparkfun-electromechanical_buzzer-12mm-ns_pcb.svg_69129ed5bcec2b4b26eec3be62bb1125_pcb.svg͔Qo0+l 6$$ )r+`GM](Iej |}뇦r&AismlNJzarQ[(}G):%a}+CS^֕G+] iڌǽ&b  &,hCv#.ADqx[)]VHiVI2O'LkѪoV<9j b檕ԩB9CPlm{CUwQFKQ߭(n+.# 'H~7vU%@*@:#' > fS q!~İΈ)_n oENuMgr@kWa8LHa~ N)sd-Z,BeoyU5TAn:LyQ㕟t1y_ϗ} utAiϕ`q&Vxkp[iJ+-;KXPKY8DN\+DISynthesizer.fz}r#G?`93 Z6u[MLEHCX}_o@f@^"=\7fw-gӛ˳f7'z7t[<&W >.>S1Rz:{̸┺Zv1)C(L.+EglB.csV}pf|}سAv?o 'ߍw;~8:9s~5Jj2l,X] bBNtx=]>O2W_*i6Xn||0_L޹/ɯ.opf?z/7 ğ?o4`1ގg>>N=ÿ|_y:pJ.8wzLޕ~OGf~NO~M~.o|S5˦Tj2_c{~eT .RvE.*̙?}z7H]3Ǿ~Ud0=~|sKh&Wy^bOMGhmyçgI`e=|rq?TvwӉӦV5(0gwp֯oȦljH]{8ռ}>{no*N?PߏZtYF.p%5Z.pj(D4R4>n_Qp"Wpz NMGY7LƎ[ɱ)8;n_q \q;+dVeI\ǭd%g7-@MID6&O##Z5rVЊi"W#Z5r>rFc"l:%$=Ԑ=T"R*#DK҇> z,1Ԣqk'Q;(e kn%gnW)anv02>\a}™}jw}Y}ܺ)GO9CO;?x5Sq)c#Km v[S![mA`/}sLCsq bk G#FM8ȝ-=M8 g)Sc)M(}ʹ(~<F'1MO9g~yjKְsޚ7%Ψ/̊ V<⬪YYYG(H [IϐoNKiX[&D3fFߞVnFWW1zhLzGP}c- _YOKҧvE't"9\}g ]Tt'ub);^Q^)루/m*dd8?<.0]!-<.~$?aڔ6ejGQ?1w :i'w/DF?bp;q'39r=X;Ǔ'`bd}۰I.gMCΚxU3=Kצ0) ̆̔4_.<غKWAYRܸ, U[nп$17B5iy iZfԴF*)%%ƟR4,`NJvߪ4z 9i #]Ax.!:hy{gu pU <ZxwDA˙rRP3HAWHAGڳK*Y4t2iՎygn2, iDِ&HK?64Giw!2x&xD$IK:fF=j茇#Q.#ѣƐ^(,s/e\LF**7#"?@Jt|\WWWfwu0.h&BC Mf;SQEJl4Z@IlU#HglT!v"z 郖{Oe}O 1~BTP" 3E:L .ی-_?1u l(TmI1У鈁b.C]DX; MP'DDH:#?(I\41:!陎b:X8υ]=J.J2C)ϬrmؕVh"ZgFTm 1QvȮ( ("3Y&DAkp#8CL(r\?^||\2>*wOv^.P N3KS]ךB`N6(e.  ea >LARaE]z( UH GKNj$PCAC{^(+RhNa:1ٕ2NGG]1P\J5t[[Lz&Y쓡G QO-~$ҙ~O:mϤXWg! ڴUKƅ !R2 fmi3 unui] ٳ0RQQ٧D@ѢWT R2 rMeLa$Lʄ&8ӡ &Q'$r~ 9eMNEѰk2ݡeL4.lvi PL3Z:j.TJw5[Qk-[sEd񍆪fL|7'v׈jWDd"V G:cO I, (Iz8i8]4jǯ)o{L|+O\[U˜R  B<^1g0μڳQ6ƙP؞Y[UN&HZf\3#vnq5R$l?3ꭐ?`~GFyk |k݃N_fō>L-9kT[|9!sZL 98M w?>gȍ-]ש{wCYcto`qRT9ѱEvl2Lwx2NX부9JJwa 9ho Xg5jھƳ}}pb +=pqãz2Z[+3[[HR};WhSM=FN/d:a₩t/]\P/b\osq|||#.BΨ\ ~:'(fٕ֎NFp3rsAza<>._>܍7N6n`LCfӇ‡~k:J}m2"xXi }_ˠg=LH*Zr*_T^UFezNd`}H"o#Gp׊H:X4Tʦ|eU9a3Eʅq\,Z'xJ:*yd¿暐ȌT;2÷ꉚgubjUb(2eJNSvڌP[ʧVd2QJE0X 2q\1Xtmlm)z|u3^Ww~'Hi\f~Vf+#f]Pg4ZuAEvYKnb5(8jQ zv,@CEP0tTs_$ihGұԀ Feq'1c .rL?cu4Б}v ;^MchNJYksIbf=UYWyuhHZ?XYHㄑcrdk!TEGVaUDYFߏ]]]8&H줗@ꑞo#at?YMN~4eo#.;T(o <՞6xIC,2Dt09t0P=fC[ՉVkS'Ց%YA ӂ,cX,>4Ŵ69j鱭Uz/ 4:uh˓f/yw^MJ _FW Zz '"~TTrZlqQ^0J`+V(/؋C]xh.e!Ȅk{YH] l) ?{ SB096nQum"ΨU~.JJX*?cԧdƽ|sGNy>>Z\Ϧ7 rf3<_?E6K=Gp)`{BL}%̈́D1ږ q~ Bzd25 Y_Դъ0~)ʈ^ =>cyUˊ`Q!"Wh}<-Қv J(SuUd\L儑->` F2u kzi^0Ϯ<9; l0A6T.hqU䱿p!5A@<0U7 cG1uȶz}m%Z8kӡr!.H:dQBYԌ\0vL^7 K8܂^)\oMhu ֪cl.QDcCv>7k˳gLL#50FzQ)1q%Q.1uSŎ ;Di;I[~IGLd'.r6$SJPQÇbmjM V82|셯,K8Q%1.N/IܭIܭޚQ$Ͱ,ٟQ3m! .rUy JLi*dlZsxcO|m=NLtwLd-BK'FIǼ?jإ+aHx°'k~N5J֘o(ۺs_%u #u$$VoM(ڒ2ڃpVQꌹ>Jj %FLk,Ê`X!~׹fFK8tFٴfQ^2-=x&u![>$궪n=SL~\| >R'qY!INJ"vc)M쭁UlAh^D2NLmݹ/ÓfGPi[^IboHZC$Im%K-.K+lFBImL)ִ#S_yd"PHF'X܄eHHp Io}[Z77IHIzkGIAah[ϛ# b%UBj,$'梈m1 %L*\ךvka5Fyf&TT/'$%Zֹg8[8[5Hl%g;R$gW ,hZI@c̮5-ՏS]dOV# (Fa9qdi}ȎuFv$zDM?p;~uRύߧ?6?~܍o_}]>^ 7'*2JH"˯׏*2&¹b[_\忻y~|9r~1psa_ HF?v?xw |H0 Fڃ8I-}?~ٌ⛉#$R*gw ƏV`fi*n20rM` _k_:9 pj.]kzH%on|q3]'r?hxppν2ӇB/_ngS W~@f7"LF2NF~w1:$#:I&,ܱM*G<3LH=w_k;ƺcgx5)*?tCQ ? j5Zf1Qp5gw9x|=r8a(ƇH 7Bj1!:e¿5a"8 mNP';mܿ4 xUIN L8v23r'/> o^D4|ZkP{׫n_zyɐetlȌ;&N CÚF}[*OjF8:tAj/FBk_;{1<+o\c(qua)qx0χ~&#qCR@T8b/TV~ _YM&r'OSgDuSJ|'rϢEEa\uܯAÙ>ʕ!a TR??fܷX8v!{:e;Јh?`/ 1 #T3Tp~ ߩ-xOyr5g'*xD`;O ZpdC~~K@L&=Lz?K ` hX>Z\ƵpUB݈0g?6~J͒5G'x6a ^CHnO{ mSx(hlql>WEcuP!YTTqz>Ѐ;)!~c:Wo)) Z?WI|xA/h "-38vƍ>idqC^&JHT`WT OpªTa=TYMs}^>h,C+\ f%)펽q6@[$]w7PwΣY>#s b gQ!8~pY[{ aM{ ]z9Y H]($"/U546>2qJb+6a]^oF00<౬rDIΏ(5ZcZ#] )a"< f)'>;aw G KCHdt(xQ+;WtwoXS hS75s~s8sU׎3ֺv.o\;]ZSK:=jS5!| 8iWɽ`$b8 ")y{ Rt7E& Өn^`)@"60NXVwcF (QV;n{V1 lh*ȊG#Kpm0b K"7j)P @VLZ4hjrr5T!:5:aHYwj&wbqp;څTh_2a#ط Kyجn*CÛ9f97P&a` ؛wM'A*fQ֌`` N:H5E>Q׫a7H -@}B찠IN.`ju՚S 4g _% ,p=Y 4Nj4NhuqH Ai4cjl+nD3B{pT_`DQP M@y,+ M:Ø\elO҈_x#C1 p , dt#&w]Ci XgPg(h|"`l( MC Y`a6A =ZWA(x/oX C 7 _@Ryp Q <(#9 -{@ybQÊz\!Y?PU<3D&́qnU0(~v2D> ts 0A#@HJ%M * Qd *4=ldH1[ 5sQ[b' ځq*Z*<1AT{픐x*::hG`H xxuGEx>`LNFA7%m0, M&eઠ)=҂P#A{ ,`OxJb,Q*E-7Cb㭆ΓՎ9nG' |DbX@,IBB0D@z @ GPQBx| Br* Tjn DTpoj劢׆fc$Z GJ0`7gbqe@1 /RJ #} 40Dx\BHVj'h=|ykѧ#c(GA;< G:Q,'hHab+ x<mMC#BUд@t|Z<-UkMq*\ Do 5pm RS=lo* H0u$ B01 *;:0+')n2 BCd0c+ ;GǎN071]bL/{0 n@Ɂ n/[xʊ_cJ}M,at*Ť9L0JLwyKA ^⃄kX; `@"8Xe# |M9;DHnXIk 5Ԙ88` pfHJ,"h:Fb 1!x3p ۂ1>, @j7P1l,mr@?z(;d0su'D} apT c*h;F&iup vv$Fi %:(˱o`` -!ЮYtZA4-h\B ԾBX'ƫ$*Q.$cXb썯8oA(Wy(Dc†*-f@4Ag8kA "7|^[!zE |7xA/蔋.<58/C6't<*!8‹-"0)2K#S" ~vbv U茝UYSV8zaG 7 g ghax6#H-H4[9@#(#/i")c.b,$dNr4EA BK {(C`v&IҚ'Cj0Z8"]E " >^O-XW_'cnDIae086 R"zȦG6Q{DUvU;bʏkk(,91+WwX>(Sl8mTN K2Vxi*F,QE) AR߁VEUzw =x,ph1>"ނP;ioiGˎ+K1*WU(jWp\ipb+5c1O6 ׊o"[:* 'BbKqhO{VCh]jPe)TJ2Zj~Y*lPyE88\ p2ށN&,83 `݈l-^)Se}#c$ tuC|,Kf8H sp ډ~98UhB+ELEY`V':MjR5E nVa-"% MYK0= 9R}szRsUɯ'jL]Sy3i`tDJ&J`UɂV(XNq3 +c,-!!l7=LZ9v~ 0"= YfRިJᡟƂ5aiNJS&_C@3FXы n8+YtC>xߣW} >0+wu(4yA8́0> & `"ʿ}}$ XձX +ⴁpy,XXcQc5!HT`԰:Vf)̸VfyPt7bK6yq b(1l$C NJNd oP/C4XD0H``gaaNȃ+_C=X_8hVxωZQohJrk4jY@gZ3rtft"(8[bEK.Ve+V]u-1Mo|a"&%2:9D;ˊFLpam1r RxT,Gӂ9W- IM2 " !fI2:X6D5BNSØrISP`l+xaIa sHpN=W(HRYa`]aA͊cH hK}/f~5V"ޚuՅ!;2,2TԄ1݂^a9p(퍊 ~\`48p4nE(! 4&SLfB~!r=PLnczAs mv,[fѯuA*V:b8N>y~/_77W.;KO.c/32wvfgw㛿V$c0Ǚ]}}I2(pRцp=;-}G7aȑ>G1^n8NrWReҬisW|cw&_M{Tz~]^ެ~w3촞`0u: ڔs~%WWQ0.em۟AR3#~4 gC!Qq#:j뽱g~h,8}j?!͗}1k0Uh|Ά dK۩|GIᾲe;ka|?n)|K|K9~fES1gɯ}yd]z<vf R@b`{lִshڞP\YYIԋnCcZ^iM\7UlOݰ{h*l%$n֚XCVzմUѪӔu4ۚZy:MWlm˽Zxl$ Ԏ5"veK- ^KzgW6][{>:bNk ͎^+vp|e;ȺD[D;BY7.clf$&J~0I"kZ>Vu.yaRg. F-eO\LG>`\ck勗HpU^h Q!lOH'U[}bpt̿!kH;Ng):OqQ< oj?_3gD3:ivNUZӜN"Rx<.i7NOu$#)yݜdh#r"XKtmù/ÐXGRZJlk'A} V1+a.^|AcV[NK9Cݧ}GWqp1|~}a7@Q(8>B\єYauᇗ‹263 [pxV&? ioZ[$Q#ei@_[|]\gJ5׊б[L5%|NzMpՙ7d5V7PڔZPZ0Z?I"mH"mִ$#~D51q*2W2cJִۚS3R<0Qsx膹хg"VR^5EIT}(ITޚ$Q%*wH ʅmdf7%*R;%TmSʕT%qÕĔzQkJ}8I\nUD\0)ǂ'l9d\HƜFd2\\8FLKkMK1/rTN/m$*%j*ף\H\5IbrKL0=opQ5Ŵ_թ1%y~ ?)UP͵r]k qy׻CCXmx"H~Z'Jr:I"-=˹/CM@(Zw 'I} Iޚ$P2.߹%2yf\&t;(nI->a[Ni2pL-\bRJm\Zw9I}Iޚ$ra(uU׍cgD7ob3"qcTEj_S@$q\|L_D& +>$!$%:>ǒiOR%/s=D4,7B2e,$(%iN\d8VQMd5m-\=Kr+u )!ȵ$[>$"WRٞ7*s LIFe횩T'I),$u6qޡR蔘[ך6N=wV}B y4MܗJ%!hXqȌZ K`> P"EFvσedcqM K}UVRޚa%{“)[GINUN mrJ8w`?ljTle"hFk3jM>Hʈ9S^y Ke(IL[Tf"Q=п١Te7LI_K5R֭MhT-q6|LW͖,Q[%%iZuR{kPR>ДY,UdK 5kjqzz16QŞ3:rj_M{hM=N6uľWZ%v(')$VRU5icH9Vggo9\gIKth^\]KTIXIYzkNqȸd_xHehRݧO` WPK8DǩR8tSynthesizer.inoQo0+A OӺƴUV>Mba|3)jE%|NzZ7iQxerpT2K3@[2J:IKa=ṔoVe\Yl=2 'OD$T-Yŝ2p /'IT$5²n25$ϐ!SΏSy4v- GL7qt@ Qza dTh\FQ O';3Iuh 9׵PK8DK! Tpart.sparkfun-electromechanical-buzzer-ns.fzp_69129ed5bcec2b4b26eec3be62bb1125_4.fzpPK8D0; wnsvg.breadboard.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_breadboard.svgPK8D/= {C k>svg.icon.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_icon.svgPK8D;2b svg.pcb.sparkfun-electromechanical_buzzer-12mm-ns_pcb.svg_69129ed5bcec2b4b26eec3be62bb1125_pcb.svgPK8DHnUl svg.schematic.sparkfun-electromechanical_buzzer_schematic.svg_69129ed5bcec2b4b26eec3be62bb1125_schematic.svgPKY8DN\+DISynthesizer.fzPK8DǩR8tUSynthesizer.inoPKcAXTwitterSaurus.fzz000066400000000000000000000270541272363601300334470ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/FritzingPKm5D2r-9TwitterSaurus.fz}ks7+x5U{f$ęS3JϹ\2E[PHv~B?H%=M6^{ 4Ňf4}tf:\{g>o2!3ܼ:A~w}fr~s1Dr8zw9d*!Wz0g߉۳7pӇwzp|q֙fP;3yoO v4l\9]p3 uN9fdi[L'4`:^F^p~a?C}N?&˛ߔԍ F'a\}u5[//G߮i,b}W7ԝrno(76<5r8b)< ۼVӟo=f< p:p~3ac(Y]jVg|U>Yڴ3>ߕ,tx3C}S¦_xy>{=2SxCru;YNM7g$:/st /rSt0t+yY;Ya0>H@*ZU 2msGg>+t*+^*B:XaQ?)QOk3?F#΋TT];sæ-*-p/=v6#$=ESjP=}©<ѭ?w~~s+:ח̷ROKWd.i?]B^PA%;$յ=4 M܁.P|CWБç96ҾUi/Nelp4~S l7rUΙv"g:.mv>s*gL'3'_n>MqEZpU#WB(YXͥd@O+G6fwp.8e3-[9WiyDVt4iBSO/jݬv{1׊$>Ѕa -–.K5,HG<.{q; ^A` tDӃG\z>+䆷ӽ R(+&+_3+ODrsR b!H߫'"Д9tx){g# [t"kch݀60#h= ,%Bz&sz~RhbJI߷gxJ`b qQcc9@{!i6 #^-egold6\,[`$b,ߨ=2:VwE԰IGmi5t 3Bb\G9$߳l굗]=UrL|eے4[1v4L.2z?4pB6&7C[Ľu|}W{)F̖|OH/smՙ2"m*\D{hr}_:PV `ꯏUE[_O ._Q X> X%eC2sEjl>˅܄kddsg"ЮX!n,}ML8Ok t;-Ӕ۩5xhcqZ )"x[Pp]}ZUM"pPe67(B$Z e Wo+;nV8IoOZF}![WtZ $!I9,)s eeB(xJ]INNc[|JK]>W <^ʍ$ʜ{L|"dˊJZ #MSmŒbfDֽĎciMzC|;X̐gjKQ%6d׉-6q)]lF15_"""0I+&Ėyjq峲G -u7,.cey=8:Ewk'jc-AGcfDY;!䘤}B9$!;Y{o^M1Tߧ1:Ew&bwxxLҾkɇ[Xom"NNllyX[x$=k lűcqba $ǁXaaz!0nToz9/qhY x}^>N^cp7ϝg(7=ܞ?w*}wFwvs¤. ssu\cyׇ H\K3YSQÿuG+cfv}tLwī\ Wg Y4>z"n:\єG"m'x+p(eftM r@;U+h{e>.*-k3--cςL!eȝߒ=GNlyrbe+򈬬-xӟf 'V1"de,{ome&*jNk2Bd<&i/ӑocTll{pDƖe=jQx$}G;Z -bcqb(qL^#-ƐR8xH*fZAŶX6;}YTL3Iun1*>&-;ST'*TqĒn1`pvTERǑKr0*YɻٖtlBm E7^Ǘ/_Ͽr{?.fOraws͋_G.g ^V7y<]z߇/?fU6yח=wT8ϜUGbM.y)]u=wON:_:e:Tp:6 ׋Q2X|S]Ӊ"~;ө?W^\zoM{A.3੫x.WM ϯ? ӵA׆7=aoNͦLGw~cW8n前1>΂W$w-%<XLάzUzyUpV36{g Mhqcm D-[`L--YWlB>W.y[y#W0f:\:btۯ>W5={['O~gzճ~w~yCGd?|u~99L`? 7E p1j^zxs\ם^L ;?Saa]TO'7Oz?^w2|Ů} wFNN7Wю kRU ZO%& '%Zv >{QƵn?^EP~T@"NVl`7aw7e%AՇ%;EYybהuCUW0bW6E72E7ϴ.qh }S7rl2m\dV X";.܆J"< h $eBA!¹,iQBvUpE-O=ȥ RZ*|,kV4DžӃ[ P뜩F.Y\'lh٭:SZVXsx=hU lU!cOǡ((=ogabE$ҙpNǗ&+  ·QE  "ʻwe&t:g&<Ȯ\E@C9Sj >8 Ft.Ũmp5,qYhMNJ};הiA+T|yP `1bPm\IU9'r3' 8t3 N+Pdݹm"IECNgꄫIpP w*YFh"#XؾeO._څhQE; АoJ 3PKa~@Dny]اe[617pB lzTz^CE$ ਡݞxEu\18jK*˺BWUe!B{QCcTq,`SyhwۋVQ-iAnAB|qg"jɸY>a*(( X&V  G)5Nj*lkPMã<[tzeuQ>t(CiZ(VM侮  >/G[hW\D{ubm/>O1*\Sbb)ͳ.W rI9y !Bk@/XT.{jHBxGΡȆi4&/vl+M +.F4؆h(. ~tuhv+\\.{!|@1p@v94% QE(0r_"7Í~@*Sgkvju2+vڥY}\Sh~)z1kcCMV>tW`Y"`XqkW:$,cH0z\j@|"-Cl?_D[aLL@'dumKY5d!aCDp͈4"SG4] \Ttv+K\ip DrhKjPB][Xaa jt$~5n]\jǠAF Ihc/@P082B\=   K`cc f.цpݻ[ + +[`E3ڮ;jA~0sf3HY->)yQX$?O.`Y fqltWӝA1D8 e,eq9$@(;f`0 >7E (ˡ<.]2k5)A 0#r/>i()HπS,F= 2cƄqj%wxga3mH$!gnH[؝L評KE HOqLuJ:`Q w ՟JB!_I-29'48 Up< H:'zTr-b݆z\RHX%$29znlr(̡ >b#ͥh0kR03HP̦ VԂ*:4Rndn;Ǽg^M@΀BpTDžQX%3^ hiyAKq0#lЃF3(M҃I"QujTaO1#c K! (aZMx$ 3X`Qf暲#;'0m|wF9niUfB'\sFp/K3 + @rÜ2F93U@4'0'北n% ynS4#+@p `$-UdX 'U9GȔ@"thp\Ŏ9ȫcN"EWE-yv(`5u˜ KIJЏ'1AypkNyB4h//D /,'b^JP+(iM08dZ&,ҭbL$i%Ox H?J/t.u17/"jV"j vQmEs?vF T2󂁅gV\iRIS?hW Ad,d8|e :*oϠHǁrA87Μ.鲔.Դ|dsG76(9 {4RVJ?w)24=LNe2ZF"))R<`R*ߜrI@/ \2;PE&jۯ H"&,ב˓DΧg )4AԭRk 0lJACc *'!tFf%+a9>. EvZ6J6~瓮e' R\wkW0@M'态D(IQeb p

A/&-=Si^&XX/C> % Σ=9?|'eNR)x{N9iPUZ8FpDGfl wJKNʤSd1)8(rJ oqЅvrv,c+ƕńAGm~R F aWW_4[Σ!(DEdrU#y=Yngk8 i:TJ"eF/2PAHМ[AKDE~壮 M\ 4Rˆ#oP)R/-ω/ U8Au H9fDdH-M0\M'Um\ K* ]<bF`4Ѳ2$*P A;]eNՏgMM Fc~~W$54,Dc ڋquGЏ`V.RW/w]ԠI7 $TVd|&ի]Ǖd9[QVi0V2Ņ[̈́HrXW^\y6yZi$[sNqIN(S΀xuv.ld5BN'+ɦ|<$)(8:,kbLi'zWZLRi`.fXhԲ:Ƥn/xs6{NκڻLouu)LrkRؤ+|5!qsN0*Lg2vFa? ϧW`Ѹie :LkȂdJz9O)4E'Ӱe,(8UDhՖ Y{=`nq_ ;G'-mѻs;I,[|aø+lF=~on~؀Hty[؇֬؂;qo[ *NK0rfH^'\`۵EC﯇jU*{7}޾j(GZ*P{0oZ}VGnnnzI`z  %|UEC_h/m1_і-FoTɠ3:+~V@텑tDUMh_U#~M"!9p#{2P _e%dqק >+Dc]{Tl[{bT̘f<fT]XT|mFDsB_Eߝ H\ N\l1bǃ${̰=Iaj sQ|Z@´L톂'iY` ֒O]]v-41ډ#WK)y 4ZWt_Dc}xWK〼]-qV,"鸵 8ݝj0> ~epN<815E71Ibm](Y;OPKm5D2r-9TwitterSaurus.fzPK>-fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/000077500000000000000000000000001272363601300304365ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/RGB/000077500000000000000000000000001272363601300310505ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/RGB/RGB.pde000066400000000000000000000026451272363601300321630ustar00rootroot00000000000000import processing.serial.*; import cc.arduino.*; Arduino meinArduino; int potPin=0; int value; int redX = 20; int greenX = 20; int blueX = 20; int redPin = 9; int greenPin = 10; int bluePin = 11; int oldGreen; int oldRed; int oldBlue; void setup() { size(255,300,P3D); println(Serial.list()); // List all the available serial ports: meinArduino = new Arduino(this, Arduino.list()[4], 57600); noStroke(); } void graphics(int theValue){ colorMode(RGB,255); background(0); fill(100); rect(0,20,width,20); rect(0,60,width,20); rect(0,100,width,20); fill(255,0,0); rect(0,20,redX,20); fill(0,255,0); rect(0,60,greenX,20); fill(0,0,255); rect(0,100,blueX,20); colorMode(HSB, 100); fill(value,100,100); rect(0,160,width,height); } void draw(){ value=meinArduino.analogRead(potPin); value=(int)map(value,0,1023,0,100); if (mousePressed==true){ if ((mouseX>0)&&(mouseX<=width)){ if ((mouseY>20)&&(mouseY<40)) { redX = mouseX; } if ((mouseY>60)&&(mouseY<80)) { greenX = mouseX; } if ((mouseY>100)&&(mouseY<120)) { blueX = mouseX; } } } graphics(value); if (redX!=oldRed){ meinArduino.analogWrite(redPin,redX); oldRed=redX; } if (greenX!=oldGreen){ meinArduino.analogWrite(greenPin,greenX); oldGreen=greenX; } if (blueX!=oldBlue){ meinArduino.analogWrite(bluePin,blueX); oldBlue=blueX; } } 000077500000000000000000000000001272363601300332245ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurusTwitterSaurus.pde000066400000000000000000000144721272363601300365730ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurusimport processing.serial.*; // Einbinden der Serial Programmbibliothek import cc.arduino.*; // Einbinden der Arduino Programmbibliothek import ddf.minim.*; // Einbinden der Minim Programmbibliothek für die Soundausgabe /* TwitterSaurus Dieses Programm sucht nach einem Suchbegriff auf Twitter. Wenn ein neuer Beitrag geschrieben wird, wird ein Sound ausgelöst und ein Servo dreht sich. Er bewegt das Maul des Dinosauriers des Fritzing Creator Kits. Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ String myQuery="Dino"; // Begriff, nach dem gesucht werden soll Twitter twitter; // Erzeugt ein Twitter-Objekt Query query; // Erzeugt ein Query-Objekt Arduino arduino; // Erzeugt ein Arduino-Objekt Minim minim; // Erzeugt ein Minim-Objekt AudioSample sound1; // Erzeugt ein AudioSample-Objekt PFont font; // Erzeugt ein PFont-Objekt long timer; // Timer wird angelegt int timeout=1000; // Timeout – wie oft soll geprüft werden? String lastMessage=""; // speichert die letzte Twittermeldung String currentMessage=""; // speichert die aktuelle Twittermeldung TwitterFactory twitterFactory; // Erstelle ein Twitter-Factory Objekt int servoPin=10; // Servo an Arduino-Pin void setup(){ size(400,200); // legt die Größe des Programmfensters fest ConfigurationBuilder cb = new ConfigurationBuilder(); // ConfigurationBuilder für Twitter cb.setOAuthConsumerKey("uzr17kGYqhgNDjTBNQd1qA"); // Hier muss Dein ConsumerKey eingegeben werden cb.setOAuthConsumerSecret("ZZUfCHruwv4d6Tn9uGz0UGebxfn4oQDikv3NeCbd14"); // Hier muss Dein ConsumerSecret eingegeben werden cb.setOAuthAccessToken("221060254-8MJIyVpXotDhemKaJKVN88L1FlCcndToB8y143LU"); // Hier muss Dein AccessToken eingegeben werden cb.setOAuthAccessTokenSecret("A2R7IkcSniMnBHYeRK02umHoIvrsHQAdx4NaMq6toY"); // Hier muss Dein AccessTokenSecret eingegeben werden twitterFactory = new TwitterFactory(cb.build()); // öffnet eine Verbindung zu Twitter twitter = twitterFactory.getInstance(); minim = new Minim(this); // Minim (Bibliothek für die Soundausgabe) wird initialisiert sound1 = minim.loadSample("sound1.wav"); // Sounddatei wird geladen font = loadFont("OCRAStd-14.vlw"); // Font-Datei wird geladen textFont(font); // Font wird initialisiert println(Arduino.list()); // Alle seriellen Geräte werden in einer Liste ausgegeben, die Nummer für das Arduino muss arduino = new Arduino(this, Arduino.list()[4]); // hier übergeben werden: Arduino.list()[nummer] arduino.pinMode(servoPin, Arduino.OUTPUT); // Servo Pin wird im Arduino als Output festgelegt } void alertMe(){ // Methode wird aufgerufen, wenn es eine neue Nachricht gibt sound1.trigger(); // Soundsample wird abgespielt arduino.analogWrite(servoPin, 120); // Servo wird gedreht delay(500); // warten arduino.analogWrite(servoPin, 20); // Servo wird gedreht delay(500); // warten } void twitterConnect(){ // Methode für die Verbindung zu Twitter try { // versuche Query query = new Query(myQuery); QueryResult result = twitter.search(query); for (Status status : result.getTweets()) { println("@" + status.getUser().getScreenName() + ":" + status.getText()); currentMessage="@" + status.getUser().getScreenName() + ":" + status.getText(); } } catch (TwitterException e) { // bei Fehler println("Couldn't connect: " + e); // Fehlermeldung wird ausgegeben }; } void draw(){ // draw Methode ist wie loop in Arduino if (millis()>timer+timeout){ // wenn wartezeit vorüber twitterConnect(); // Methodenaufruf timer=millis(); // timer wird neu gestartet } if (currentMessage.equals(lastMessage) == false) { // wenn aktuelle Nachricht anders als letzte Nachricht ist background(0); // Hindergrund füllen text(currentMessage, 10, 20,width-20,height-40); // Tweet als Text ausgeben alertMe(); // Methodenaufruf lastMessage=currentMessage; // letzteNachricht auf aktuelle Nachricht senden } } 000077500000000000000000000000001272363601300341365ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurus/code000077500000000000000000000000001272363601300341355ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurus/dataOCRAStd-14.vlw000066400000000000000000000752661272363601300363300ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurus/data8  !  " #  $  %  &  ' (  )  *  + , - . /  0  1  2  3  4  5  6  7  8  9  :  ;  < = > ?  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^ _ ` a b  c d  e f  g  h  i  j k  l  m n o p  q  r s t  u v w x y  z {  | }  ~                                                                                                                                                                        1 9:=  >  A B  CD  GH  PQ  R  S T U  X Y  Z[  ^ _ `  a  b  c d e  no  pq  x  yz  {|  }  ~                                 !   "  &  0 9   :   D   !" "  "  " "  " " "+ "H "`  "d "e %      -22 v gH27(2-T (gnN7#2#gNBvv-ZT}(g`7}2 TT}#2(gg2N``T}}#TTZTZ 7722-g27Bnv`n``7#g #N`=7gZ(}g2Z(}#vnn T2((v=-2N#n(gZn} g g g `Z`Z7#n g ggHg#7ZHT``}(g(Z`2BN`nH``=`nZg7(v g`222#7 B7`g`Z77777H22-(HvT#(H} }NH7-22222222222-27Hgggg g g g7 `(2 `-2Z#2gѾ2(g7-2-2Z``Z2 -g7-2-2g`Z27#T2-2-2g7T gN g n g g gg -B 7===N` g`2-2ѣ2-2-2Z`B72N2-2#-27T=222227===n2(22-22#(-272--HN2 v g g`(22#Z-#Z}T2N2-2-27N(2(222=22222-22(27B=H72B7n#H722#7 B7`g`n=HvnB-77g7v 7(B====B-((((-7(BBBBBH nB #nN= vngn(HnHB`Z7g27#Z#72-`-}#` Hv=-22`(2n`(2g`(2g `(2g `(2gZZ(gN}љN#} `7 v=ggg(((B772``g-(2N7#B--7v22H- g-(}72-22g222-222#2----g -`g(n---vZ72727=n2(2(g(-2222(2``Z22N2-22222(--gѣg g g g g gggѣ-gg g g g2 g n g g g ggn2 =(n# Zљ }`#B(-g2-}22BB2T(22222222222(-Z#-22H2222g--2-2Z2(22=22T#v==Z2-2v-##--T`-222272#B}22n727=2222(-22Ѱ2=g-g(-2-}Z =}`#(-7}2vH2-Tn(7# 22nv` # g g g g g g g`(-2222222222222222}(`(-22-2`-2`7Z`-7=Z(-2222}7-2 g(2 `(2 `(2 `(2n22(v#-}N# ZvB7BZTB}}H##((-22g #ZT}- ` g g g`2g#=} vH##Nv }--`ggggg`#(BTv#-}H Nv(#(-`2-v22v22v22v22v22v2-v2`2-v=H gZ Z}gN=vg````````T7-g#7vn}ggg} gя-nggggvZg-g-gnTgZцv gvgggn } 77-BBng 2g 2nBH} = =nNgnZZvgg2 HцT 772#n7Ѿnggggg`Bg Bgg -ggnn# 7=22nv-nggHBg ggg ggTg- g2Z(g`nn Zvgg g g`n7T}Tn ZN=gg g g g g gg nH#n-nggZg2Hgn7ggvg(g ZZ (7=n2gg g g g g`n7TTѰggg=ggZ`gZBg`( g#`-ZT#Z2Bg ggg ggTg- g2Z(BBngg -g -g`HHTg=gggg 7g-gTgngggNHn Bng 2g 2gBHя-2# ZZgg=7g(=g ggZT2 77#vTg v`#22ZHB2--222222- ц`TZ( g2g2g2g2g B =}BZ( g 7`- }g(7(Z(g 2gn2- g`Zg(gZ=n}g(T B T# }-B=v(N(Z H T( g 7`2 }n#2NBTN=Ѿ vg `HnZ n- ѣ gg=`}`H2gg g ѣn-}H n g g g g g g g g g g g g n--gngg`g7 `ggn-`(2 g7N==v77(2# g g 22-`` }}Z7Ѱv2-2T#(--HZH2gTB-(gg=g`ng7 nH g-7 =gn==N7--27B`` (-}T# `nH7-Z-77(BN7Bg g`}H n g g g nn= n g g g g n- gvB((#(vN(2NZvN}Tn}gTn#-77v`}Tn}}=T}`n`ngv g72 g  gngnTZNgnv}`} }-`2Z2#v=27 }NT(TZN gB7v7` BH(g#`7- 2gg````vgg g nZ77777}Tn=` n-g2g#g27`g2g2-g2gg7#Z` H(777= 7v(vn `g `gZgn}-`gnѣ#( g nB#-Z`--(ZBZZgggngљTN-`-2 -2 22222222222222}#- }-2T`72#g n g Ng}H }NTZH#TT#NZ` Hg -Z}v7TT`B}7n}v- 7v7}gZZBN}v `7nT2- }N2 nH#v=-TH n-}--- }N2 nH#v=-(vg( 2Z g-2- }N2 nH#v=-ng(7N(2- }N2 nH#v=-#-77gvv7 2- }N2 nH#v=-=яnn}ng-vѣ`}`=}=ggg((2--T-`#7nN22T(-2#-B--7v22H-gNNѾvN}v T-`}nv#Z`(n-THn#`}nv#Z`(n-(vg(2T`}vn#Z`(n--Tnn22Z#`}}#Z`(n-N}v T-Z}nvv#g g g gggѣ-THn#ZnvvѾ#g g g gggѣ-(vg(2TZ}nnv#g g g gggѣ--Tnn22Z#Zv}v#g g g gggѣ-ng(7(#(22}22ѣT(222-2B-N}v T7(}-n`n`B7nZ# gn7ZTH n-N`-n`n`B7nZ# gn7Z(vg( 2T-v`-n`n`B7nZ# gn7Zng(72((-n`n`B7nZ# gn7Z#-27gg-n(n`n`B7nZ# gn7Zg=N(`77v-27Z-=TH(###}nB} 2=}(2n}#Z((}ZN}v `7Z`2722222222-2-`TH}-Z` 2722222222-2-`(vg(=`Z}2722222222-2-`-Z--`T H-722222222-2-`TH}-Z` 27N-#n}2`g g`#=7(vZ Ѱ-Z##Zg#`B gn gv g  g==Z-g#7vn}ggg} gя`--(TB#Ѿ7vn}ggg} gя7`#v -HѾ2vn}ggg} gяn2( `#BѰ2vn}ggg} gя#-77n`Ѱ2vn}ggg} gя7 ng7n- `2}--- ZTg gяцnv-`TѰNvn} BяZ љv gvgggn } 72#-g=я=nNgnZZvgg2 HцT `--(TB==nNgnZZvgg2 HцT 7`#v -N7ц=nNgnZZvgg2 HцT #-77g`7ц =nNgnZZvgg2 HцT nvѾTvgg g g`n7T-цgTvgg g g`n7TnZ #HT Nvgg g g`n7TTT nn72Tvgg g g`n7Tn2(` #BT2ѾBg ggg ggTg- g2Z(-gBяBngg -g -g`HH`--(TBBBngg -g -g`HH7`#v -N=ѾBngg -g -g`HHn2( Z#=7цBngg -g -g`HH#-77g`7цBngg -g -g`HH2v7ZTNNT`7 -(-2 Bя}B`n ggZ(`T` -gZ( g2g2g2g2g B =}B`--(ZBZ( g2g2g2g2g B =}B7`#v -NT g2g2g2g2g B =}B#-77n`Z g2g2g2g2g B =}B`--(ZBT( g 7`2 }n#2NBTN=#-77n`Z g 7`2 }n#2NBTN=  ѰѰ}} 2#v`(N}g=N}TB gnnHvZ2-}-2g22g=2g 2HH2=Ѿ2#v`(N}g=N}TB} g #7g( HѰvZ2-}-2g22g=2g 2HH-7gTnT-7Hn=TB Z-N#n( 2ZT-g `#2=ZT#`2#( =(2--#2HHN2#N(N n-2v(g-=7B7Z-N#n( 2ZT-g `#2=ZT#`2#( =7(-2HHN2#N(N n-2v(g-B`T7vZ-v2}T2-}(22n22g2222n22n2-g22}T-n}n(# = N 2722-2=2`Z2#-2T22g =2n2=B2vB2-2}N2-`-22nBBg7TNv--n2-`72}T- = 7Z`(n7=-ZZ}22B 2g 2g22B2n#2 NTTH ` g n  n } g ` NNTT `2=(`vN(g7-B7T`NNNT-HH72 #(-TTBBgg NTTH ` g n  n } g ` TZZT  g#g(n`vN(g7-B7T`NNNT-HN72  Zvgg g g`n7Tg}ZZZZ7 ѾHvHn7=22222222222222-2n=#-nHg2 2`}ZZZZ=7= n7=22-2Z#2gv2=B2gZ2-2-2n=#-n-(22222-=-`(2 NHg2--2g2(7=n2g}TTNT` =g`n7T-v#nn` 2g 2g- 2` 2g22g# 2g`T2g 2gN2g2` 72Nn=gTH#`N2n2222=22222222222222----HZN=n``# 2g 2g- 2` 2g22g# 2g`T2g 2gN2g2` 72-(BH##`N2n2222=22222222222222----  Z=(NTN(- (2N=T7gv`(=H(2# ((  T2(T#BB- NT`-BZ ggn-`(-2(T-n-=}---`27v(-N#-2v-T-nљ7vg `g TѰg g} BяZ#22ѣ2}N2-v22g2-22N2(22=22B2- (gTn(gg2g7ggggg=(v22ѣ2}N2-v22g2-22N2(22=22B2- }`(n`Tv(gg2g7gggggNnHZ7Z2n77= n#}#v2(2--(B((Nn=nTT#NTBvT v7-v#2HZ##2n77= g#}#v2(2---H7nn-#NTBvT v2(v#2HT`2-B}T }HvvvB-v#-}(NT##7# я-Zv22T2 77#vTg v` nvvvv`vg-(ѣ72 }B`((TZHZN=n` nvvvv`BN72=n -}H`(((77 vn` 2g 2g 2g 2g 2g 2g 2g 27#(72g2 ((-#77 (#2222222222722 22}=2=-TvngHv-n` 2g 2g 2g 2g 2g 2g 2g 27#(72g2 ((}`T B-}Hg(2222222222722 22}=2=--Tnn22Z-Zg27N-#n}2`g g`-v#vn22ZTTT--``27 ZNZZ``27n#}v( }vn =N}`ngNZTZ``222ZTTT--``27 ZNZZ``2( }vn =N}`ngNZTZ``2B}T }HZ}vn#vѾ#Tg7=n-я-Zv22  vg `HnZ B-(gg=g`ng7 nH 7TvZvNѣ 7`#} -Nя-Zv-2 =HZTNT=g`nn Bng2nZ}2-}2Nng}H}n2( g #H=(#}((( #-N=T=nn2H`#g=H` TZ `g--g22v22Ѱ22-Z22-- ZZZZ`B7B`ZZZZZZ``N =n-vgZ ggggn--n ` ggggg7H--}v2-#(Z72vvBvZ-(2-2}2=7N} HZ 2- 2(2}2B2Z#n2ngѣZ` g gnH2ngѣZ`T-nH Z72272=gn`- Z`(  7` `n(TH7}=}}=}2v(`7(gnn}-=} 2}7n vTn-=7(7v2BT `B}2nN#Zg22 #gH(}-# (TnZ(TT( g2TZ-2Ng2 2v72nZ`N-gZgZ(gN}љN( H7 v=B(( }Z--222222222222222227NTvѣgN}=HgBggZT =vnnvBN-HH(n } gvZ722=BZ7gg}   T nvѰ N TT`gg g T-22g v`v(#B Z`#}gZ(`( #7nZ-g# B7(BNBB(`#-7#g=BH `7=}v} vn#T-B Tv`B v#BNB2`- Zv2#n7(7=(v(}#-gNvn ZBv`H=v#==g  -Zggg gg gg gg g nH#=7=gn g-TgBggg gg gg gg g nHOCRAStdOCRAStdsound1.wav000066400000000000000000000240201272363601300360630ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurus/dataRIFF(WAVEfmt MMdata'~~zzx|sx{z~||}vz{~~uotswxvx~zw|~}|tkhiqzuxz|uejmtsfdokjhs{trnigccdsvoh_ebcbesoZYb^Xbjt}rlfcUSS^by{~{vm^UQIS^nq|{iXTHJRfio{of[JMMQfdn||oeUFGQWZi{~td[IDHRfjt~r_UMHKR]hnxqcWJ?IZbir}tgI;DR^^fsû~wdSDDQVXdn[J>LY^aenrYCADRaZh~Ƶvh_QMDPW_crlXI:ELXcd{{fM>BLZZ_ksŹkL?DLMSdtÿt\JFMSYV_lzq[L5CT[bjo{ǿubH?FOTZi{þztXIAPOValy~oROHKL]dfsbOBEKTYju~obHGV\LZclyõt_NDEHT\m{~m\H>JTYlrv~wqaPMTRVZ_woG1BIVfqwȿ~|zlZNLMOR`j{]H9HK]^iyxh[IKKY^\guoWDADNXix}mbVJNSP\fnzn\MHBTaekkunV?OUL[aguxbOHQH[dioxŶhSGHHSVfoq^X[USIT^ktƹwlZOF>O_pt||o_UTKMSdlktv]N>DK[djs{wi_MELU^hkr÷|hSEJGZ^iu}lN=9GXfjwbRQOMUV_`u½nW<9?Nddnwƹ|cNDBP\ZdqĴlcKAHOOUk~re_OCIWW]kxmUOFROTXtzeaS?ALSftjQINOQWbbn{ƽ~hZNDDKQbhrŶ~_KEJLTUeju}{fV?EKKQfo{j^JHJHTXfy{nYOEMRNXix{u]HKGJTeqzpJ:BLWbtrxŶ|t\KGKIR^qpVJ?HVahho~þ|rogO8L[ajgitze??GKYdmvvhMDJZ\_hcn|dL?CTV`fu~ǾzeQIDHKYfwf[TQKO^_dtgLIFMRegn~~r]ZVRORXks~s^K>FL\ivxljdZVJSXbgw¶xhXEA?Tgjy}wee^OJBZ^mtvƶyvfNHCFZbhv|kaF:G[aegujOH>L]Zdri^LFFY^^blw]I@?GYfn{¶zjZSLEK[^ktyiQ=DNX^_jzƷyhH8EGN_jwIJwrfYMBGZX^lrXF:?L_iev~žzo\FKEHRZfw~tm\LDEEZbq|x_QGCJW_hnsĿxsYFAJQUjorgREBTX^iqwsfUKMR[Z^i{y]F9F]_cgo¶~kZRGOMTZivr_KDGMXejtxycOFKJK^iv|jWKKOZSesq~tVEILNUcqld[NHLTant~zwnYCCNR_grxrdICNSY]sxvfVJQQ\bei~|lXJHNZhw}lbNDHMTbbm{þ¾v\G?JQY_t{hVDFTdnoleciؿoUE0-8K_qtshabvɭ|[?2,.>Wjvxnedm޿lL4($.Jet}ymefvѬ|X/$,E[mz}wkbq߼d?&"'6Ogw|oijϮrL+"-1A]tvljw~[?+#$9Ul{}tko̪nI.$".E_w|qkv㽞~c8#:UnqfmάoF(&Dc|zlft㽘~\52Trrhlө}~tL%&Fd|~nes⼚{]84Usuko~ͨ|~vJ)'Bcz~qkv澜{a<$3Rowhhҫ~|uN++D\y~nitƢz_>*-Kjyoqܴ|{vU4':Yuxqtȣ~eB*!#-Jh~{rtڶ|_;&!:VoynrͪtL*'Ca}uls߻~[:$'6QlzosӮqO4&!.C]uxv|äfJ-$%5Jfztr~ײxV1!)D[rytrƦc:$)8Hdz~srܹ~xO4$$(9Sn|rv̪~fD,""*Fbzrmݸ{|T7$"6Vttlsɦ|jD&#.Fd~yoo⺘||X8'$7TswntΩlG,*Ddz{nmz޹^86UosknѪtI%*B]x~rkzğ~^<$-MlxmqկvR8$;YtxrvȦlG(%+?]ytr|侜}}Y;!"/QnxquϫiD*#.B_w~stܾ|V8((39Jdwzqz׷jI4)'),#%1Jh|}swشtS9%,:Wqxuy˫kF0#"*B`ysn}޼yS5%!%7SlwoyέlH-$/?[uxv|ã~{[;,&!,Ng~|vwزsS8!$6Vpyv}ƤiI+-Id{zv~ڷ|\:&%6Uo{tqͩlH*.Dbxus}ܻ~\<("(7Nh~{uvҳrO3%".?Voxu}ǦbB*$1Mgw}trطvS6""/=Xoxv}̮hI,%&2E^vxt~ܻ|Y<*(+6Mh{yrxԲrS4#/BYpxu~˪gE-"$4Hdxvvۼ~uY;.'*7Tl|x~ϬqR8'!/B_szx}¤cD.(#1Kh~|urճoS9&$,=Wryx}̪fH/$$,Dby~xx޾y\=)"'3Lh~|z|Բ|lO8,**8Ws{y˧}eG/$$/Hdy||غ~vY9(()6Smxx̬}gG2&)Faxxv߻}|a>)$%1Mj~wvײ{xW8("(7Wt|svͩ~}jK6*&,B_zwx߿|cH0'%4Ri~}zѱxZ6#$3I\o~{z}ũjF,&.5Khz{xڶ~]='!,?Wo|urϮztM2%#*D^ywr|ägG,&(1Fe}~wwܸ~xY<(#%6Wt{ux̥~mO0#$1Cbyxxž~dD.!"6Lh|xyն|z]<&"-Pf{||պzbH1-4?Vn|ҳ~z^A.*2G]r{~ƫoT;-*7Kex}{ֺ|eI4.5FZfx}̷v[F84:JZm|~ȱs]D62;N]s~~ªnV>45DXht~ʽoWB;>JZiv}õ|lXIBDK[kx}~ûxl\OHLTany}}vndZUV]gqx}~wqjebchnty|}|vokhimsz~ztnjjlry~ytnllqw|}xtomnrw|~|xsooosy}|yusrtvy~~{xvvwy|~|zxxz{~}|zz{~~||}~~~~000077500000000000000000000000001272363601300333065ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurus2TwitterSaurus2.pde000066400000000000000000000060561272363601300367360ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Processing/TwitterSaurus2/* Just a simple Processing and Twitter thingy majiggy RobotGrrl.com Code licensed under: CC-BY */ // First step is to register your Twitter application at dev.twitter.com // Once registered, you will have the info for the OAuth tokens // You can get the Access token info by clicking on the button on the // right on your twitter app's page // Good luck, and have fun! // This is where you enter your Oauth info static String OAuthConsumerKey = "uzr17kGYqhgNDjTBNQd1qA"; static String OAuthConsumerSecret = "ZZUfCHruwv4d6Tn9uGz0UGebxfn4oQDikv3NeCbd14"; // This is where you enter your Access Token info static String AccessToken = "221060254-8MJIyVpXotDhemKaJKVN88L1FlCcndToB8y143LU"; static String AccessTokenSecret = "A2R7IkcSniMnBHYeRK02umHoIvrsHQAdx4NaMq6toY"; // Just some random variables kicking around String myTimeline; java.util.List statuses = null; User[] friends; TwitterFactory twitterFactory; //Twitter twitter; Twitter twitter = TwitterFactory.getSingleton(); RequestToken requestToken; String[] theSearchTweets = new String[11]; void setup() { size(100,100); background(0); connectTwitter(); //sendTweet("Hey from Simple Processing woop woop #loadedsith #robotgirl"); //getTimeline(); getSearchTweets(); } void draw() { background(0); } // Initial connection void connectTwitter() { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setOAuthConsumerKey(OAuthConsumerKey); cb.setOAuthConsumerSecret( OAuthConsumerSecret ); cb.setOAuthAccessToken( AccessToken); cb.setOAuthAccessTokenSecret( AccessTokenSecret ); twitterFactory = new TwitterFactory(cb.build()); twitter = twitterFactory.getInstance(); println("connected"); } // Sending a tweet void sendTweet(String t) { try { Status status = twitter.updateStatus(t); println("Successfully updated the status to [" + status.getText() + "]."); } catch(TwitterException e) { println("Send tweet: " + e + " Status code: " + e.getStatusCode()); } } // Loading up the access token private static AccessToken loadAccessToken(){ return new AccessToken(AccessToken, AccessTokenSecret); } // Get your tweets void getTimeline() { try { statuses = twitter.getUserTimeline(); } catch(TwitterException e) { println("Get timeline: " + e + " Status code: " + e.getStatusCode()); } for(int i=0; i A2 :NƆE */,A IE={N9ѷ?Nη\ʥ9]N?9Cbyr L\tȭg:ϳd3;ێ.2_p7of42.Y蛣rvҏ`O>s+ηϾazt}1m jljlomGWۡ0|v&,fǘݑ<<٬/ϏWk࿃fyEYA[o˝o秋n9ݛw/jtWw\<]nqXmclf6_@~~^.?/+~헯kJ2Ͽ{_P:h,ś;L,V_/~ظ܈ȧX-粠k/'nqNͿ~zqz:_oӶj^ޮ_f?/67\ ̧5f. mEե\B>bϦ,?f}?Ԍ:;)&rp,g/C]t0T֧ gy6`۵M]{p}~i5o~~mղI}<,ˍb5ʬe"|qշ/b\{ ܽf$>}o} \{q{:Xtid$^|ɟrnwt[7YoNTQ+x)֖!/yY?GGSݐbN}O'?/2 lbq"jΖo7|x@fBG\6,6W }\ZDNyly|jgZ?OcjjzқfQ?Nʔedt׸,+޸O-VNƾ(Wv4 i!6 ȝ;`^> vE|{\3k' "w9_-ՙwO~"eitLdxCz$ĉ&ڹo@KHXG=KruOWoo>*`??;j:=N?/GfbӿG7!BHv*TGUb3o׋Wե_52q"6Fbٍ]]T_&|TG].gjG#k+2Ũqվ^-iEQQF.? nbη;FtiZ=_Cľ jgc{Ǹf}*or5&Yb<7I̫zו@Th~}#FʲƟVn] hƵ_SW/)' IrۙR%Euy5 GRD/q$)c;9C|^/OÞEB1S,(%DD^G"*f ;Vu䩪/[>6?Bu@4l  ~]֕ "IBw<ШNŢh؆/X`iԗM(? ԄLM}F"zUH({h`¶0Qn &qAUF㾏SJ!TԮ\y-Z+o Ws?1OJ\pNCH-aܙV7xMWnBDŽuI\{TTT]GΜʅQ\|#r.Ux,T"hwZr zL0{5 rʲ"\I Zj螵@eYPd}[U`7PW5m31*AV ]iEpWst[ōϫ1ƥ0bO\*7WL SJ-sTɕsI%<ޓW4cFt9X"R+!~,$D}K}jaU.H?^=4P;0=10nՆỊTXXq >qq6DT,C ɏʠFw8tEre%xBWB(VV\W Tb,+a蠗D@ &b&Y>x7ӇÍ.nM쩘 !IlnH"^fq Ӏ^l52\خ,ɕ wvF3pCF&}"[\AA - \3FRX V{+4wa!IB'LsԤwbv[XYh MtK#n@ۀ.kbs\U=ʘO'( $lFLQ| fm1)\uk`x`e#X3R;6yKPmnʕU3'DL&hlE`Iy?6i͠讧;|c+LE8ʖmq!9$ &&AE 8D+#!=Gj(Oc{lKy#py@9B3JDbCHCY 8%ڀ 3s̘whI;HAPHA5 ԕsYEGLX`+zM;`Q o ݟvJp!K ϐ/Фc3#B\4>223h_b\DxT"!W)`lL!` Iz &jXw# ' K }VȀzSG"@L˧gB;rF&*Ͷ`Tpay=6hc&#DLBɃar^K[D7J-F\;Z*a=e%n&=B`t%vuQV&Y3[ϙWPy"Fj)ǒAK@uY,BA4F$! <9S[#ា= $bͯO'&CUMm"zo(bV q{G`xg"Z{=YF$ ҐY{˹'F%!cX)B <"SX/a RC@56KHB@H1Œs c -Հ!!YIn_ K0D@^vc-YUm{ i^S$0j/Mt(Ea5WIK:1 vV jF0phMõ7DdL 72ĒFMrVԍY#D++`KF6!E=5uKGUn{CIC++`?ubBh )0LMAFIh3p3B?$3 T‡<*#Ɔi08FynP\@nrA2/)Х>lrFyG e{ @v fݶy[&LG67\| o ) `b;LGEG߱!0i#0$ܷ3 o+ i {Ϟ2란LW6' W(zN=kJ^o\ `fۡF VKȰ2b$1h=B:_u=v`㹓ӿX*1OCj;3K:Rsqx4h]5뼂 -r {[\t 4c4ChI.:ש U3K0yi*Yf@B(Tsæ*-w kD#ZcN_|^0L)t ]iq_?m" Ït٨Q!40AꎤfV'^p9tm'X_&F@F䳈A@Y:1MB{:tpHةI(m*XHfzWEQro\6@,&=ֶntnQ/-R>Q/X4s28c.,'LtjE*yA9J1QDZ{ܥ-7etYLx ǀG<ta]qlܪ!Vu}U 2_GU:{>H96*m$W rSt|NYk,Rf)^R~A48Bɓ:,VWy¢l!q$JH:0淹9ci9²ţX@DAZE /f=fޡL@]7rizN†.K:%PDnfPvA ,9fDqHfנfHWV)vU:_ܰЕQ˵i˃Dΐ@vC&7OP.(dn?f~E?#Ej|ajǪ b<]AINŐ$u]W5D2VӾ3k\Uo:.U"͞ފx Un9H4n`þ]_CLz0E<a6|G- MÐ2"xHP֘rP;[Qq+=BW%0hd @fr lMȰ',g(9&_Aq`flCqF3]" Ak@ck_ChOc:n@0y:kʈ!ui=f4] |TQX~At:TI8źOm9#%VXMs2O*Jgm(y4}й'56KגT<|8Gf;7/nlJ 2IAJC=}՗Ҙft݂3?LuG~Wڥz;šv)q5Jb[ < @ZWelLX UK 5 \saבkvq;8 _aÉ0%9};zF ՚P@[BuPs1Rjd&MfP()؉/Ckύ4m0ݤm̃Ynןo1i t3#&Huuel((lTYM#`5(8̸uJ8t O8 ͕w`( <á ?Pv~,,qC#> {oFvedyᶌ,[s{{u7qQw>X5O+7 ߋdIMGDHTu^:d!ái7&!y6~x[e-5mW rҏk1Zy7R)Xx@5gfh< D5 lP'VHeZKgMa߷ut' udx,go}齠m醘K>Usm`v'Pc]7sUQU;3EggL(::(Ww(~w @Mx~{Q"̪L4DNw`,) u\SQ;tDxTSX&#<:O=om;`H|6*'5gM>;Q6Re|j '5P.,>+WTE_x.}֛[yҨ+onwAxYߔO׾!c׫1vsSхr2{4L])N_qnGBwp :8goý9>)|{=[>c|$3n>-1/ը5[Y}9&I|V=vۊ>pv|pʿHIr^Iw Ŀ[3:w~;"q=13@مIW'*-.~UDo#t0P>?~b<= /!55i?*yk]*Xh_oMU0~*u}LA!>t?"=Ukz\IUFSQTbPғsQp_SZn?޶\.0K#SF`E7olPM鶗q;u^я ]*vrN_^lrw@N`017忛_qj&LJI]v{/66mV-a 4cM~~)Kqߗx>yo JϦ̡|PKmxEgWorkshop_01_Blink.ino+QIMQU04*LQ(N-)-TRP(OIQ  Ѵ檅(χKL,I /,*t*J$Vj@*}1PK9xEˠ FlipFlop.inoPKmxE  wWorkshop_01_Blink.fzPKmxEg"Workshop_01_Blink.inoPKD#fritzing-app-0.9.3b/sketches/core/Fritzing Workshop/Workshop_02_FlipFlop.fzz000066400000000000000000000331431272363601300271030ustar00rootroot00000000000000PKxEˠ FlipFlop.inoMO@9Ѣ޴I5~ƴ-LDNߝ6B630Ψ.dlKEJys1.5<\[XZ[|ɬsY+4% > A2 :NƆE */,A IE={N_L<3np3oG^&p|q֙fg_Jg:ŗ;WO~y7]~fR1ptu=LH^lNڞ'Win[Թ痗}viw]\0%@;"oʖu⥛ggkջN{_~ko]/?xěoƣoƓ?^|@⼑h*sۿM8/ɳyŋxK;\ݏ.~1 Ggߒw)xǣ_&"Tx.?\o`W5iUŒ+f5wogr2S77_B7_GniU[79B^k:_TpE>m~^Kڝ;s܅KvydƝwLvCz_ݍG?DHwwv6}s7{/7:UZI/2N[PFtʳf8G> ⻞Hu~b)cLW57߄Q!8N3*:%^/A&Yc·O)x h/Y3zEW!3nc_G'qXd[ntٿ\b+*\o`uK*5%\9n,e+ cуklb݅)gDK'&w>aO34l=m8ۥQkٽЗ v/[.Ȣo " ?D LoU|[:j=|MG!kqO¥KN\lkl~3XFgKtuҺMoC_ua?az)X O4 @w캶`f;L(s ں΁^UsksYY""! 33g;ٽf5FOrEtj0+:;}g>̤ƇEpTũ&*(gVхۋo-|׿unޜME'~ُ_}}y[o>]UE:N}{ȩo_k'ġ&h&x;N>}{i]}OSd@;PSM,ͼ?piyrOS>`Ul<a!?_/?gΧ2B4w3i8M}+Jo }|2xxWd{)Ip*M%Y+Agz):7O{!03dS%3H __xZB}ș,+YLkUZg`TLo ޏm%0uJ;&53#׬NϪv~`hMgE\.]*me z|~X$‚04F­o<>TY۔qc:զ훵\wy[} ugKhӝgUڥ|Lҟ=d OE\T=粒.U[B'r>%wmS򍡒.]ܬ.W?C3#3iHO^)ͫ?lymbOp#\jXJ^w=9dmFǙTg ߀~3_Mt+}Q({#LCJTA{*..|{&aZ`;E}/5F9.˳> -G,d;wOfMw!Pg>dzLz]<@8ZVqcU*5#PpLV>0WHAe?(΅8$9.˳ \E5+XUدH$K-,vB#2,{ & rBnҘ𭎖g8XH8eJ˺vXduɒ/{P\i Q%a;x8!2hՠEalKš- bORR٪f%?NbV Zq@f%/bF;] M>jSOO5eqeC!BQ+V-%iƞjfyݘMtAEn}*)GNIC[eJ{VUj%պ + vG˰},̰fk]jvBEKH=$ˉ/J"d=|ZnclԖ.tf`h5Z*XF)>B@eOU~2i2'^ݕżt֯tfi\X;5um l?tHj2利UPeV;~44İp4k]jj.گ]Ud)RUVDJ)3U 3_ĘKZp$&\<_N"͌('d 7^op i9cE? AM>ah"hI׺bբ F4jj>!Gj9')p\@ڄ1`*~_jJ^ jYq b9*/6jm50 Zk,Ϧq#:ђ*jVUFolnV@qD:hyVqx| +EZM?%Vl\jS焴-j *xIJvHqPY7q~Fo Dr#]SŷMDx)YZn8Co 3\E? 1Z?hVocbʺT=¦w5%B M˻۲YA’Uy^7j͠ewk%4*.jjdsPeC`SX+2٘tmYH­ov9YvpU[7%0YEo!fL?2STÄ@!6=VXha")␜.$EJv1ؒmM$U|oҪaFK!2O nV*̖Y5B[f-Yy$J|vt;Ie2 5 iww7er;}s?1Jww֓~]~oٻs+x޹ ??rӳCY܏/|t b'+lJ؜KEV d^M& VcǍ#x1x0 |;߼ǥXbv]UjЂ+eKNezVBZ.ݰ/ߧW7?o'_fwz޽Ktr~P{w:<I)aྜϯwX'c.UwqY+?sNhUgt NGߦp չ6\=ËaY{׆0^ON]{S7q~9y=83x!oS]χxr:6LDTvP^hټzWoF,?Zәo0*c4#afDRf߿l;? {xVܺ_f!pv.@w7UI9ž6 <;)}&TL,sIi>OMwSIgx;\x_'ogs/םD?W߽|zW|O{'}z4|^(o Ubv|eIvx;xapM!K~nr?8~{|wIktӎ/:_/:oNUlXE*+V(?@[oarvu?y{WN^;= 얁{qO+߿qK,T+U7y܅FMEmITe'jЩ`͝+2#. E:ɻ,.KqCH]'2uZuMtj1IIG#2lXZPpi/ᲥB^qYP%2^ ׉G,Uܷ<<ńv0zB$4PVS[8άnˇ7_kuq&*tp0EݲqR؋H٬%E٫WنIB&SU.B q.(}AAz.Ig ";T?Oɺ*Kt;Qtr ƅi+HØ0:`.hĨLu%P*1P+7ʻsM(/agMR@@ &ӿ pv+WpỦ\ω:~T=eWJs-"oDȳ3;VF5?ʅ><;WB&@Kg k c!CXF^2dER\+m0IisxcjI@J " ѶQ}iEiñSAFqAshH}D0BͲF&&uFgFXWhb`h!Q1F+]"b] =(»o.H,5KD.rC% 4ܨ7OO !m*fN|mq. x%q X;Bh7הm@*2.Dr!/=dLB_ E&a [qEqSA\& 7H a ])L1p.S|",-Y4w+8KQ {o dA4&LbT2 "8z B@ 9K.lL(l#쐚`82>L1̫O'R d21 1_BPt rܻ僨[˺+G`Am{I59$tA-H}bIn\SȚ٪7#Ȇ*3nL"`dC"2q9$@RKXp4芷(gȢ 8)e<( .] j6kRA\M3B! ƒ( %8YF= Ɍ2&;T#w:C@:"A8vyv'"H4H'aLUL:&JD+(+o7a'=a%BH&W(A:2qzn91b݊z\C@X%2YIqKkC fN)sH,R$3PĬIA(5HFlG5Hp(ngII,.ECHj懣8δǢ_y+ZdD %JBH }HXFIQI1dO!"GBɫ_ugz Ғ4aZPjfztF~¢RENe@ A"1{@JH2,]ժU-y{̸NP!S]+8b{nZϼg M`ULk!SDb}\ <4 G1F@$dEiZ 7E.Vn7QiD&ZHS(A&Z$TdDWD)& @LH$(m(ڕK9#CZi z<$½)4CNr $TuX^Hbp2X Gdy95՜mhOq(/86a.ChS:IN[It6 y<|\6?mtm&I[$g]&dH#f,τ5eE<' S@Eݧ m<#Pc70"^3!P:#P @ H%c4xdN2Z93eœ8'0'²'/Yupj;9ّQEtABLZ")c9=9B0ڥP}}M ##9<;la 5u KEI?NcuDZƒst<R3Re"ROBL##P~T^KO5xNm"!KJbH"I~ p+)((~uQ>"ɪL!"SP.PTj܏QY, A4oh,mZ8t=.;`Ku'L n T$ 1`D"ڎ$2I D<` j HTU&1c2wB !h%${[\ +IoBX&N\PA*b}KIa, T\6k%x\A-PEDŽ ԏT.r[,]|Y$d9LYqt1!Ppڶ[Ai"eGl*!h,rT Rw %UuT6Ey+݄q!#峀q Ad@-1L"*tRaP.DAzv*4,]#bs#hX|mDk0X  /d09lP Q=n0DqXtk dt dQĸ@$B#q+I{TuVLXBIʠ0}Q !NhWS*UU~lǼQUߡ3Iŏ!ꏈIŏBâMD?TlHJ/%D~kuu(RB$(b]nZ)yt\pˎ+*p>`<[kzJCB/,ܒH`&XE]# 5eqGnsFpƒ|IdH\z{T-pO"S- R80UjMWإJ튁+Al%TCTp,xba3LL; )3ҫ5rkVbl2׊ǖ"k-JCyfoubHjuuB5+KQ UP#P5BB%G$:1qDm!dF*HHy3/*ʀ?X7aU(ܲ\c9FBOHYZ8ROX8"Fu$3 VeȗFIXfCJwډ8ws، HU4!ϕw1! xr&1XN&u&faiG3r8,^e9VtԅNCT1Y&R3a-nVn)qӭܒU.X)cC#7)97Ǵl_ga 0;,fVL 17Ȱ0'΃\xvDMӻ'Fj; T0@MG1W5*{љmi/E5) .V(ZRRڪlErJW"a8,LĤ4X"^<,xOQQ1S0f++l9ZL a"sՎ*`aX*B]$HGts<~J"ʉ3$^,X!e+Ѧxa}9uԄN;4^q72I"mbỐaQ&ҰsjϖǪTr>o?ᔑ+[~q{b=wմ\ XK~`)mP ն72>Tk I>?Pm)},(&+QR p~/X8T|Zx//{zϻ )B%Ɔup2,|]LX. #wɲl RRquyyVGMkT|hD޳♎(ܪ8*q[9 ^s|4^)V.4ZVGw[οX@5СEVs:CZYui}Y$90 ak%,&FYdse|8o/rWh9#"E!?R?\lL!%lRHL!BP.˿RM RnB|!6j=njլvBe]e{(!~?1\1&l\%#(:D:yRJ 2ȏ/D aem-8QcDC|t%4ij*RGr?PEZ^ Ƃbf:҉?'ڦjeo鶖Q>Ztr2ʽac0JBG W0Ns4ǠD;ThLJ+Z߆`ȏ0vD#tnݹ3]XydLMײ@-{_ݍGߤx)7^Lng[O6~wwq?|Su5(#C+ n Q>[EeN+LWUqE*Wә#W*XjkEzQ#+Ent,mcYlC,|R^Tfh'LsS$PdGkB+S >GJºmϐmoRn go2IuʹٶF|%(e>%TsSM4HƧi;\M|¸JA|I&1-/Kla o3E? ;ӜU9NL))yJm*8jQR>LMGϯj1mm}'.;NYze#+i[uqjbh\}8w5NP\⧬SF_krM^1]#Y,4>&9Ey+[v]_U>W\&A]wm}P}]Nח}zǟ?PK;xEg*`Workshop_02_FlipFlop.ino+QH/JMquRS@ A2 :NƆE */,A IE={NUc Workshop_03_FlipFlop-w-Button.fz}#7y nhƌ}H&ΝҴLRW̟,&&f2'*pG$ ֥  s_?ݏ:σpUg q__Oߏo>ۧӛۜˉ)o3LJWf/:nz7g`tsՙW_/_Or?w][= oM߸/:xy5]t WOÛ_ |oU5wO^Nף_iۧW\7n^Sesk\ݓ7ǣA0q_קLK = &+~wo}0xttU5 |^ Fߕ{~k@wFs ܾ#r2W`k5ݏ z_>>}ױc<ȯ{t_z7zӠma2~5[lN+ ֶQeӎ|EMD=-Lb|>iΝG'߼_%?V0s%u9%L<=/x[yxg5w +Uvt߹{ўrC*gv;:| $U9 pUN]oW7ySƭV%-pXU-Eۣ »FȶeV̪\W!FYsTv9-sgg,+b _Fp>J6Jg>y,n,֝OQa:m+Pd+q)sV;ƪizIpuO~ F )2`/]4u5ж¦-ɕe*묭9*sL9 Aڔu(`[nil|j.)˅$RGrN*#z\2 .A=ܖnNw>*xʚP.$0&?}QW-rcnGϥ/U-)eC@hOC)ѡ&[mt؎6̤P%kj|\wc" Gә@BTx3cKNt_A)"Eh^ 2"g7Pr*2/jF53)Tτ杋N*m,+*<\.Y˜fOu܂śzm{ :"\|`m%Э'hJ>CS潚cHڟ4଴uB.k5dYotJ>,}|J>,|HY1hK*eJ>O!B通;sm1\o'3GW2,/7`O0"Dn}*bY *@smJ?DhONߥYgOB3&$ Jծ4- lJ +slX:ih.û]'RNta4;8E}Rga,.ɦXqX"a>+rˍڶg,DW͹J hœ!<*eΝ"4ǎ4cx ^zuxER8~[[oNH?h<1x&rv=iQ=u#mщc-.I<-',7!y a_.lHe)$̮LчG7),vm9z\O^dyX޵o>,RX1“L?KJ s{&$./-a`<ػ?)alha7O>9h:š*Rv<SƼ,/cBm0n?xO\ s(sv߮?H1ykIN$7+ voU>U9u~mXo ncu;CLz!ڰ:Z@Gt8ıeH/-)#+Te[fT\ܣĶVnQ 8ڼq(^Ao E@]c%n vy#Q/Bb#-J1\ъzc~ \#-f7sTR5 岢wP\,% 9fؘKҰ!s"\_tI%~|"_hTJl,q)Ex-Xa 'b@8b;BiMA .N!_'^m!9] "r6pH C jnn9 y&2M\u\_tǚR=K? s,j*fDB,(d(Ë0H^$&X莈D#IF_}7\׌;Nia]]dvށ LGWPx+ KZKAA9Qt,1=Nܥ>eM}O:eP SO.''81? IoڅJgg*kUGQ ~xEZ֬9۫5ȇYc6k""1˥#DG1tȼ/MGќqCh}4ڊ/:ήqyuJmNoOiW%)Ĵ I,VP(s.:iAY5Z4 }9Qqy7͵7VsnhS`jcϐ`'s(%(fHQ=ZHٜ]h2!KQ.ϵ2fD*HVP#MIYU^\ԹKcYXhꍽ,.8ͥpP'2I5!լ c\[RVsmI{ZKYYf*]E"?JX`f$Xh"ZH$Y{,&b_qeiFC>S:Fё} MrKhd'|T'F䗮*V/ 'I36}`V(%k&)CN{frp!SCU_Q74*C4qi3l$/t='}>:R̼G 8q"O>EW$R$@ΰ |bD;)0QAEqKHU$i?,z[/ 'Bόj pQ\=|f x # LީHjUz"DN96unM}OM,%xJME<:*FeQ~'I2WFphHzS/Gs'ZU^4z߾ Oi ҇XY3"k/$y+ VD,_4&Vgb0Br}ο24ebCĬ/u߉DV*: \ 5KorSʉ ;2&RaήR]kۀd푀z/p,*V*yTs*wJ%NmGZWt7997iKOhl$εXnX7yWJqNYl :t-Z>(3fE1):%jrܤ+h8lPSq*QEZ;/4JJcƚ یP^`ibtwİ3j._"yHLfXMŃ$rb`*7q/3\S4:VL|[\,} %[DV#)uy4Tc\ιL$εo?HYaE^A&l@^! eMEP^7^a:P!18 Ste_ դ/Qn_g`l6V_E·VqQy3*xyy2 PѸ+*gc(i(ΈķNi,FYbbfc׉_łd]ꍾ,5/k#g)5R-HoD΃r b,o9JkHX2]qhrٗz/yA u\*$ שy5 MwE3(ZaA}Mn[lqUzG[":'&W۰ j[OЫ5H|L$)1ext`vm֠`C“m doLW=:i&ҮQ{J6%mY/q^lNKI˛J&ՙ (@㚿\?7ZHZ1/MRe;-А5lJF3Zږ#_{gȪP#ѪggHMZ:<Z7đD:m"SKr\xdʁOܔÇqijp0iP@Sz_>>}הäp}~{5~_z7q>ӏ &"4v6~7/'ӧolFD _[6+3|=V|N-Fדk*cqhGpFHY2uiUo4mF q2? >zNulA `B)3p+zK0Q9'Ik! nTuOhN> />0g^_?}`߿zëgYׯ;Cs}u>[I&N+n7Vekgfna4CUP ~q44>ᡓ×oqIMke3mFt 2VF {wC-N:~p-b=L]u5!b_{x[ŐR1h,Ib W~4wG(LN23N$s%mBXwmݵ_*2F2Rg2WK}#ܕ4]kD9ETqF4}6KYC!8_oSP)?۹̡Eu-OQD7 kyZJ/+@ZI<ֲJ_I E o'^D4|NV(z]WJ-}%]3c@|"\ÿ{d]f\96N CShwo?hJkL ܝދap rD疚Pyw)qzNK:5-Bn͈\Έ9 ]g12וN)LKȋʍ?"A&rNjt~ j*gNݙ*WZI~PI'q>bΉ7v@w~Dk,# bF%Tp ~ uߩ-x <^PƳp5<"G0ޝ-82ťG??% && 0R%Thԭ긃<;"q ?Asз4LEƏb~Bÿ3YҺPԀM'~2w#BbA 5z-JCh쵬]ŸEMhVV@B|A쮍AP`˜"sZC xAӭt^ l1WnDX(UQ8ΌCd]/J/%$*0 k lj L+`WaU0 YT Ϧݪz 4"8^`6].mw͈y  0OΪfϙ}!Gr0@KQ!_9~pYĹ{ˑS/"'t/e^5K,rRULH AcjCo$6n&+t(DzyC }Q^kHFz/W6̿EPWvsG[(>e]%@"FX@w܈7Íxǚn@JQ1g8C1Wwr?ckg\;Zs>s8:z޵ic@G*6u—=0F}  Wťx1HeB('Ly}Ѧ،w 8 c[1ލA'LԂGYyɳS| AaGS3uF<Yy4XI)(-%@5Y3f h` -ޫ((K BtjtDs֩ѫb{40Įq АdFo n`Y*C\Û9f9ϗ(_00wͻ[5(kF0[0zKv"tՙa7H -@}B갠If\`5 h@J,=+dZ]@P yp4 ptP\{"рGCP*lB0kVk\,8w3|!0HRm@f@0b~>U1tܑu ux1 cC\FX@ oJKy OQ}*Bl@Z{ * ؃P^S=`%B 1܀|M*19*P~GA(#9 ϭz@ybQݚzܡ!Y?PU<3D&́qnU0(~v2L9 ts 0A#@HJ%MX LpQ p(T2mw㞁gh Q 0tA\wVrXv`J #2 p9%$u`BGh DWuV*'{!/ A/aIhG0)˗WLJt(Xj;HbPj{U+`މ 16ΓՎ9nG' ^"1BD $~}![" Q #|(pB!H! hkt9*Pw5v7> "*875B赡Ymɇ'A'+'• Mpp`$t@ 7PJ)aOo+T\)J $7// /``L#H8}SrB$T;\iPp#Ψ-7_gmo?D:"[=MOkQt@LSܰ*>+d 1\vg*[ʂF04RLIP7l {~x ㎡Cgf%̓!jX!^\,ƮXÒ 6MH @Ȫ8/}Џ[t%b %`!<İI $`@r'ȊH)\4â٬pcfp"/V ".1EgG$ A  AFmMj.2KRD꺵(ƝHGY !zBcqU>IG J=x `v)C^nD2162qZh- ~DBc0  l#_uplH&ey]Й%@W$9u-i>'9vo:l)uIz) sC#aʸhHLSa\'Ћ9B7yFƋz]( zJzFp7cH, V!  " E 8}"swCN`F̺pz]1ڎ@MD : `DZW!5DUb S]pGԽ_k09Lć ׮7Q ZS761]UbŔ7~E/_ t_b):~dOGnuQӳ"5T)"U]x%!^׵{(3]J憢ca0*R y#avʠ' NC"A N*"^];>F;8t11],%,`ꅯ 0^6^ t.Isd-K?` rB (KP_k@/4zA r^ae$UP4bR6V H $5a#A"a·gs')1*PC[րRcJQ6@ɐ8X09EuBcda%0p ۂ1>,aդhbX0|a $swBWa *4qb0 \m$-2DwĈ@Z#?Te9 wA>5N+cKȑAbcЛuhX'ĉ f(+=s>إa';s9Q9*HPyLؐ@@"HS>$0_ D1L;ly t%C@Uݪ F0l@p#l(]: 2` tN`mxa=vzAܺW #@Fg!B0J_#7!s:hR`@. `U#1u (_̍ @x =L9hݰe c 2S##r#W^;  =C )hܰ`@#hCbC|#1o R _ѩ]58/C6'tT&!8‹20)2K#.R" ~vbvk Fa7°k#5r8) MY\K0 9R}s_sNlR0YtML-i`4#Ut0jdAԯr(XNs3 gx8]Vxvga/+&a8K~?+~ )H3tD1U55O TR£mem~_%dJªP5GÈ8WXAQ,\$L8%p)u0ۧѳ I(jcQb-hjcQb;9 A*4zR7KaM>60KI~jW^LM^^*xRP.s{[5Lۗ!L]D0L``WĜW~F뱆c_|qмT0^@ Д>W4*+,33jtf5t"(8[bA+.k5f.2N#?1qaueM|X#&69uj!``p*ÀNGӂ9wMJIM2pE!fI*:X6D5p# ć1%'qLAašЧôvz0#I$ ۄ jV^CRn@[l/f]bCkP45ǃrkB2xkRj?X+4!psLW8G!5 c{Y gcB耣U:`^&ddJXLB/dPY# eʪq6q@/hnDվ(>3ۿݎ K-7ko 䬊NsMټܶj~;K[%[l?5nx.ԶsR\1iopۿU}~j83ѧS$ƓpO IdLas)4]=S)/NLglIӦq4e Ȕ錝yn,t&"rrSCs]lvp7p͙ owͻGo.ɐ5Ӹgմ14$^oO9Zo%q5qr7`CAFkČ*!jF`A&vl!e B2Lb!6"QO>hWr&ƗVA)"{E"VOkA-k7> D&w G&BfGq~{ۗ-u;[M&R7zĶnY&)>ϝ&xE618KE;1W/[Zs'#ssAm2W$7T09ՒEvΒe2Hٕ:mM!dhbUUc Workshop_03_FlipFlop-w-Button.fzPK}xE2\!@Workshop_03_FlipFlop-w-Button.inoPKAfritzing-app-0.9.3b/sketches/core/Fritzing Workshop/Workshop_04_Fade-LED.fzz000066400000000000000000000416331272363601300266360ustar00rootroot00000000000000PKxEˠ FlipFlop.inoMO@9Ѣ޴I5~ƴ-LDNߝ6B630Ψ.dlKEJys1.5<\[XZ[|ɬsY+4% > A2 :NƆE */,A IE={N\cWorkshop_04_Fade-LED.fz}r#G~ ,v4cL}nJNiZ&gO I@CXUz}}p<$#`K 2#soFd837_^2s鋻Ѡs8|6'rrJK1 긿/ލ/&ˋ}~x?y6FWhWKW]%}ë//TnTwѹ on9WRU/4Wo˻IaZ";!]___k}++"KA.jo.o_T-脟_^\W~v9~}r;~ox {7{E#C]s5 ^իhk<~1Hג?z)8^7S _w㻯MIvpw4z~NUob"7si4x3a<ww{j]!`2r?WiM_^U]SJiXkD]9~\ yg1W_Qʮ;"*sF?_ >Vpߠhw;vJ]j|4~ջ^8p9~[c~Cm@+a _MQ FtݍO~UVWwn$!L^tܫg\J؋''Bd]{Mh(r?8%r.)eT+<ܺPyOSt?6n 3=ޛE^qu\g븘*Kg}^ /)bvϷ#_֚% -:Qls8X٦FeƏ[5p72qF#3%}>b*fy4)tE=Bh5`b?#fcg&| ,6G;3 -ؖq+1Q윘A+4?#}&Ic4',y3J3+h=) EО0A*+3-}kҊGN\og,k|t.am1)߸!=E#&cfc3Fgd9%(mz1&kn1*Vs}8kT,xd.ӹ#OM565)lPpEd5ψ]><͖C~I[,6l&M$Fgd#9)8m:1.)kB12)b|9kT,xd.Թ&ZO565)PEe 5ψ]h<.~O7MNyq0&ǧ ݼxWhM^aRӥ]YȗJj}/L.q/}8nty39ׅV_ۏOp23wT73~>n.aG%:npdPB״6H|M %h^37Se {IΗEoYӾU2YPbZl6ѐm N [./Cζ'#uyS#MJsKҧfϭ(D FU.QT(FEJsJ$ QYc ͋0t׀6h'6{6Á @a x>zӑb- 1At׀%6Xx;2H" n3MN)Tn uw< fT8g,wfZp&ҿҰ\PnJo % FXMg%CB5>d,xa3"xGΡ`EƊd7omӡT&Fqp&P0j{kř$]8󴌩0Ghk|`ɬ u20:bg ='w#=\48 йuY߯''Q9֧ǧWD\;GLTWɃ|>pu/~éW8fjY9 űQ(Y=z mlSfŬep2m5a盘0wvƲ"Kk磔XlsaqӟG>iݹnҶ ŏwK:lӲlZ{@YywogaYfp?x3W1ZH_WUۂ6hL,+VXgm=ViȅUɘcB̩E.H0MVׯH Bv6M{dC}tSsOY. ȕ@S!%6CgϿ_!zSaSaiFZw½1[>i*F{A\R)St:4GHܙ#mzO>=L}ax! rSVTk+T?\!B||v6M8;}2/'bW8valnaSVWP\]A`ICsޭ:u3-6y9(Z< ci%3 EOa6F$COv$AN~U߷>м!Qnr6aLjm `+>;(IFk#'iΥ Hqt USd"' JYVg靲)GpN6mϊ¢ YGnԶ=c9'2huUEs Q)_GT/s<N-cu3U/; =3a hhA#x`PfKTPKouQqsZ*y&iAx6l).TyHǃ `D AZ7/3mބ"G+b1F ~֎=oxtCR?'4sH!-PH?)k$䍚p7Qr-OOquEk^s$!8̱ f͈(s XY*Q#QȗaHLrYz-#1ɗ"|eoxow~4nǏ?"'mr~ [큇)Qp5tHQLmSq_VH U;Ķ;Ql~Bq큣3 NS;p dv\tnXqvV:x A\e/lM#0N뜰Za?|݌uY):F%d<ųl O<zeZ Ke% ~Ɇ5#2| ի㛧 m9ClhEy (dkCV@!F&ؘo#H j$Xi440ߑF:&D[+\dA0\аUHT.5SPH]So-I3޸ Fb&`$Z^x#Do7)HF9A !1en6(&H-^~֎dI$E&1P3.F e}EZ#gs=7=a4}2XgK 3m9@>QTQ0T+JxFbXˊՋeY"EbY-,NZdY(Ĕ*윒Uq>ZVoy&\FHʘ4kFVšmM7T,s+SmxO-+V/zcϋK32N.;s67@RrMa)e5-זL[Ҟ`VvY-KWe*ŏD.{&X-`-+V/*2c$IX~,ėj\YѐϔNit+BߟB?izZ;I'?6~9ˊՋng2D=LM)+AIʥc]P,| & gp  ]|Q"ESN?OeE{> P3lX4N"q+/s~eE$RdO7Kޖ 9m5Ŧ3ZEtWF2H*HhS'w*ZV^t H(SM#;dSaS6f)RSĢ%Ƣ+Y n̕\8%%?ԳQ/{܉ğ޷7SZv>ş>VDy֌ae*Ŋ Iʂ-uEDgMU~}Xq18;mDp M٩P$1Kwd#ѬeEDi`M&R.ۤ\r +TXkrx׊6+{$&\)5ʹ `UbyF\(ʝRS[&ǑDV@NM>cZm8s-+V/V>kޕR\uS=-悫]bGƄa&LYQLN C07i%zJjiѴ* dべ&6#XXn$1lİWx6RF&ma*Xj&ʍiL46MNd0S)5ۖ! $0lsV$ѯHlB^* うa0՘/ss 'RVxXW =<6WBYk&TsMz)WT~H ,H4f}B5K@)ذsہWrglqd 0^4B T4 s<^gtwȸ oۧ &"8vsy7M.:ϗ#@~~g0tSZӌ C219'a.`CaoaU L\qHI3[zTP lH"^#E}^xFm!12 nۨ)$B(*$ٻoÛۧj*iNZ937Nn/}2znt?_/OOz>|?|xc+w:Xy(Nrb8g ,~%ïWM[ϔv^Iq0+%}GuOOᮿP+81?Q~poāyÃӆ 쟜9 u_ FK Y3w&~(dFq Q¥jWM˫wS~NJ/Vr>J8<ч!oLc?mske57qр~2m\q}~YQvɠ9^C5 hL(en3Ro&*2i1s͍q2g}|5OיoSo;~ׯ|,_zM7whN{og?鹂=_igӕeY٥;_] :?F_uuo?>:?d?wr Wt~?]uȿu:Y6UZ,`TN .kU nԾ wo hqJkdnn.X=~w+O{R+ +7,\ۚ-;}ӭ_5|9w*Y\yhR&qB%k}['޸)FDx@&`I—R% J(-~Ƨ¶ %zA[@9©#k/4P. wxvIb@ݵ7#h +<9ߞ>gSKY/E|Re?:H;%{?.FN=Ё^|B .z!R / ȵ6KU19#q3>_|MpCܵ7nvkl +.uۑjt}W a3H+ Â&ir\V tb4tW9*iF >R 4Nj4NhuqHAi4cjl+nD3B{pT_`DQP M@y, M:ØYqT@b64>P"=?KA{rÈ TqG֙*m CR`4 q]^ecu84b(!,1 < F Qh*h7`B!{O 1p4X@U`cNH(FK ]Pq!f@+5<`SH1} w*NA #2PB8 XpA`8 cޠ~Ҷ鈐cl84=` Z:>E.SMqê\ D/!pa =RSlg* H0u$ B01 *;B pHgtB 7"b)xp~`Kj7+B7#9'pm bBA?na)`@&1gp1>5# +>"sa2 gI,"!̛E:X6bpK':K4bR\3 V6<L05ݪ,KUrf֢dow"1Gd5AOH+CEEnSWP'݃H?())W Nx!xMɤƝth a,O6: *pC0!V|yah 9wmj@g]Ե`@pTڽ)tp@J6FV%9뙷$gS$U)F-"aSMهq@|`Ģ g9vt$Q*!BRa 17Xq0[!'l& OQ 9)yo{33֞­u=Ph;bs7M5 `i C^L"֔U1G!'LvQ0 \#J0v v  Gǎa4 EL~9&PwvRx>JGCnQ4/* d@01y>:W6"+L^pG5haNvYVSH)閾|B $IVBB8)C?ZׅF=;gLOHD"RT%JBuwᕄ"z];Ct9*8hK1d<+ $8Q =dL?28 "3X{txu sŸ/t|RHzCT!CшIhZ; .ׄCd`U:͝ƨ@ nZK)F1%CrP` @ADA`4[}l pjfZUacn,󑮇i' kq h_aI78tƉÀDp#:8u;;#^8ix$:P#.8h,:~d4.!G݊arAoj_סa'ƫk?* `bKSb8G p«F"A1aC C*#M|I 09C0 4q~a TF[w2ðBeɏhLhv0Fcl :뽲rF_e& 3!HR;(~DP I0$³U 5(~17U/0Eۋ\JuÖ)-`ˠNȍ@\x<7'&[ &"qB A֋ E.je{hĘ1.K 2X|aDBt{ٜPU8/ lZwwP<äLȘB,EH+ ډE`q:6|T 0geoGW:+_G݂aFaZ|@X^z?M [Ar(%*ge_e]*Cs  AҬzox]hʼn@ F2FaVjju|Cgf OQLU1I*DEh#mL`UFy4 5HLRZ8pF09/Q Q=nt$C],(@ Lf0N@@ FDEDbcX@j(+Y(#LtsOi~:4lz*e>Qps$K/5i:N> ll:=Q? :;U~t2?GʍRBY*β PЕ45y Wey)j^@_lfXiƪ8RK)l$ B,Zpc|#EԱB7"f+KU*+8485S Aq2O|' (TM+>T]kj5k>6 ךo"(Z-~.j gB`ƖjryBU *+*V њP˚P`kBPE1RH3d Ԟz:\PF,fhL(77` 1F@OH^p =D#7nH2`oxlGNRš(\#)v@qGhjpQ6"b&,0+Γy];て0kR5[E îTD04Edq.$v7 H͙~eK5:~I1d51 7`ҌTLVU?Qv`9y-:4?\vYaڵ#'o釽c8,eՃJ^e,8 #i8T Dj <%xP)V"J7 n(ַi kw՗* B9z8/&"#S8HB_q.s`Fݳ`r0åK\oFς$EQ"..gǢEYUlz,r,4Ѐ5̒J,7`[,9&^Qs{1S6yy b0l$C NJAd o0m_g߇h0q*v^8`2Ńq\AsB\ :Ư^z|~A>vvSxA5$~CSR\YШxt*ΨЙYl9ͻ[8%bDLJ#KąUt)r#"w 35c9bԩT é" < #8uM x,5)='4TANP&\`r",XT1= kBNEk꡿o$UP0l #,8Yy ImKSj?uUYW ̺BC] !Kbf҄1݂^ax8p(eeD.^}f7][WyuTv)a%3 !Bge0)ǡXe}WNfd87O 0n/>Fu{yѰ5˫A|y,vFo3?oNF`3csۮ.`ڻ.:/ǟpU{׎=Zc9R?'YɁ2 7yMe;<xK7~jw/sR\1hp2xz=o NJpԪR<4k8_O]wNwgF _8;h6f)6Y?&̦ip3"=a`gqcV菴PM>͞$=\6{@}O(N''O COlDLecǏiȶxqDe:a{0L6)ӶyeLGL':Gu`q3?nGFf[-o|#YyXj8eD6cm4yУ+8GM?AO9{2+H@p{dsҊ;Oo-C︎הkJX7W5zhT6lֽ=x}3XO9ԧDˢ{cU"Q+ />V9T|= G?Ow0 ,rup>}j. pP(tB\äB 0UoKi׬iYאD#T8?ӓ3#b6|0Wsu1Cnh9T ?~[teXK@#XFꪚ]UCHk*)VѸq:h1䩶6F}q)ϲiqZyci" #-I`ߎrLs+0+nD VU?B8!9DLC8Åe6TClD|06Ѯ8T/Á'.hcBE"WvDlsWwZV Ko|L߅M &̎}/}+*%v%QLvnt;mF *2MS&|;Mm(cwq4nvd;>^$o7H3FV7['d4!;Incr,EX$Ze+vdWD3*BlĪb y|[WJkJ'ZRboزkԴ^jZLi .g[2bdEr='MV"`SEX^aAFwHܲb0Rjl-wZ[Hxv1;*{QY3hhA -(j#%pםn bL"5!;<-.kqq~^Dlhea[R-y7~z}y~-4QV^GEf.#!a$~r GI]Wٜu! ;!)"mY%__sEċ; fY }C?PKѐxEhV.Workshop_04_Fade-LED.ino 0E|SjA $?(vhu$!)5D&vh\w.YUM%v#cɀl3mIf8H^N#uWfVҾ̋b]Vםh,BJy"Ce8|.Ȳ6PKxEˠ FlipFlop.inoPKxE2\!Workshop_03_FlipFlop-w-Button.inoPKѐxE6{`>\cWorkshop_04_Fade-LED.fzPKѐxEhV.AWorkshop_04_Fade-LED.inoPKqBfritzing-app-0.9.3b/sketches/core/Fritzing Workshop/Workshop_05_Potentiometer.fzz000066400000000000000000000355101272363601300302310ustar00rootroot00000000000000PKxEˠ FlipFlop.inoMO@9Ѣ޴I5~ƴ-LDNߝ6B630Ψ.dlKEJys1.5<\[XZ[|ɬsY+4% > A2 :NƆE */,A IE={Nܜ'MttaI>9|vD.o~׌P7c8ӫ6^3VϷޫӺ%-Vk?'Qx?L]_W.:n x~z5v3­2O߾O B+*U#yPΜ3wn9]gzh2B'j&}o z_ގG߱D,Hדn]|TV(ESŖԽĒ]0ʳat;Ԍq}Hu~b)cLW5؄Q!qgT*5Ը[㛧[+0Lqꁍ[hR+ٯZSKoo^5[-)ȼn^m'BߗSk64xbbQ]SFkU>6w2#%o+i^#ռ?Fa:^i%5T9xżBͿ>^Q/k2u 1{b^&t,wuNGs;Jzh%NF>bvs!DMJnl)v'N6 ;N5}ޢM9ݝW;JO6۠!zܦr&M6MЧmO)"L7T3M C%)}MB/*w[tP)go A~FfpЧmB\))\@6JʧmO4<+xJBS>LlSNw֓6jjfy|Idw(S5q=o|Bp;Z:ZN|U`-t0n M7_d:Wϊ@[7[XXW$dR5vw!#~#9qcyD/֕xT blja׾ciފe*XÍ}օWiqNfrlgf^Ӳdzݹ<k._E, CdWCο_so7Fϻg" 3-9%Bӏl|i:2OFg 2W7I|4; }pڔpQ5ѡ{0μK3.fQMwUPҬr Z[8L\|~?6&ߧ#[˻o27샷M!jmӬ>4jAM=C7ZoL>C%hM>ɇ`|VZ׽e hj֯xpwл wfAzp|x":lUZـ4C|_i;/XRgwݝzf!Z.B)Ffʴ! 7T%d^1m b &F3_皀9-V4>|H|2NCh?ɗB 2Dޙn.cუ5QA&I x&`ߑR<hV6eܘ*eNq̼jmuַ1SiϬ8Xq^v)߭=d4hu5vySp湬EŤ%3ZkJ6&l1]S a?5c^a6럵6xΛL}m mH~f #YpV'lߢUO#&oT&ӏ3[ M_HXϵfjWLš;7m\ &CKK|r{yX<[[̋60l&½Rmp(-=fjN?=ҴȞG쐻9<.P§n{H!.jkIyq1;yq-~vN9۞ڵqvw{P?3=Ș4ef쎦qxZf#2FMO`G<&t[t>&@Ҟva{ƤvMŪEO/mo3UITreza=4CA.uIXR|­͞4IZ&\lޥLdkEC)T'AZ)E-j3j\Yaĺ5!g6\qbO(ZX# '2HɟyE1HQC YK ,5Y㐊lrhY yN)R7>R{-u\j˓>=JstMSvΠB(R&GK!_^AfJn*V-Hh˄0I LR2)u T"kS EzG ܋ٷD~U Eк]JxjRE瘟W./48VC %-TZXSaY ui~IV4Öce ltk#>S. }E'Mؕ7oum9AdX}= OE259áȡǖEa {b MŪEO7Clj{X4sCHxO2sJ,eki̤ǹ0^Ʌkx{n& 8w3||>K?n!5l\>>>Vq-"MAZf^PB8K,X\Avb/Iz2a,,Jgq%ZXOG$%%֩/qPXi gY%a"eXtG0D?BMUL f[Th^ !ɌLUIiQˮSfyKȚnKȖ(Vmdb5I6=˂!-x0C +(?܁D= kqD]'d-^|:֚젟IC)8ܫ9jYKm|mUٖ> dk%ZEHj;ta##"%ZGN ȵpZ@#]@@ew5{hR[N{jkO^˹+f oDP-hÿ26ԡkڳ3ѮXh?+6)>k"rؓ)Ap9e)pkf۠ސrioȖE5ht;Ap-;ٷ eT9A"FBq%k!˶꾢YČHh kSj"E˲-T%!YV; ѥ;٪f?8ObV q@f%lM6tU34PӪM=%>Քɕ Sp)ZFXh㧤U{Zl9S+ɦBl+'r#IH-ڜաeJgVUfeզ|qaڄUs%X2KejSjv "EK >bCR, itkBK&_VMNY+mف4ULpX6kC-#eQEȢHiSjG C˜v-sr>UkRHF<ϙqbmf1#f w\_-qG&a)fک=i^9ަ"-9j,G0cOkF˲6}$TLk/puUS:vWRfw"KM f1qfvZufp[mjq&`eifD DƏ2ܔ{@r>AM>aZ6ZҵXd&^mf'.f +w*Ɯqh&HGnq (}Բg[3o]`#|~بj`µ6qUM ZG Mђ*j bg+^lϸŅ;8P *Zծ7%΅\-Ě}KMr꜐WM?RE4XN)* H濧qP#91[l,ljቧdib1Cl Cy5ک{2Ӈd`X$Lؐ򮦄CHiyj[v#`%GܵQ@Wz_ގGߔͬt4ctsoo{'7']??һ=Www$:Vv;x޹_??r]YO|tK|-WbVP6j6Rծuuo&_kdb<Ϙgl h ߼7o~i38:_q-JQYj8]P:t;,_gwO&o}1('||1\tPy,Rh7C}9_N:\ƫŽgy~?_턆]vFl4m6+d΅cEߛ]?X+]K 27pa8ɉװ+oqzt~>//t^hgsaUhcHϬ_קN[aOuv4.j]cr=Bd; V{;)} TL,sIi'Cs 7SYgx3{[go ?םD?7߿|z{~o;fz_gVij4|^(o U|~beIvx3??©7.Uti2ɷxt|Ah|atdj.P)_B5!~ Ǻ{C$CC Ώ*5ZcZ#+;^SâU+yXxӣ-Ti{I(2ENxcE H2 w7*p^{\7M,3avrֵj\;^q>ѵ$-4cQG*2&D3!9{h0 }2@ %oeqLq,܀"{LR$t%78¹&NP4 ݠ͸[Yҟ@#gqQ{nr;\4blAǣG+.=ҭ1b K@"7f)05@WL^<jKj|9 U9 A*NMNp?/F5A G]ph!')bmR3`U&ׇ):7$,9 r > >_BxM&pSq0nU,VFY=e3ypj3oO9}. '."3O ^5- fjH9! kP i0Nx )%s,8sKMtŏ8Ñkϴ|=Ј&<d&tGIqeTPb6, 0gSg6HN$3sSB֙ 6@ؐű3γ;QGMP*`@|"TGD(X{XEw&oOXI;/I)<8?@cNs[jyCb1PnE=D X$zHĉ5ѠaN)sH,RaY(9"P jR 4j#05E% 3P2̶?qۓ Y]<'wGqi E8i U"4< AKv啐ב*!v4J UJ)A@dH1yKnL@AXF5LU=:#;aQNe`@ A"1z@JHXD!U[V3^;ANu`v{Oz~-0^@$ FqYE ODiiY12EQ,I }a AސCC0ȇ"׈Aжp9 ]Ƴh B @*ɧ'34|ʩxd v|A-fT4.p# RjM#> #%AmCޮ4P\JKdC+( B0!A*#P ! INn,)8 gh <fA۞hOf,"¸bD1^4\edSw@7UPqRIՂ 2LRF."Or\: rQn.8% EQf-2DD't>rЙ#9Ԡ /q`fH 31U=q?_c^:gG77DAau48&6MR$zQd]F> :T;5at_0GʏL0'^aV? ,bb0Oqk4/œb KW3d0ceWҌUXIE) ARߒTE$ b,Zr`D[~=`ުK,;*)WS(+:<: R ig±!rFzUF.4V}͊MõcĚyK弡P^i-flIi/j'(TFP5[#TY,,>rDcRX3n2 "ϼLDe(`Hl/^ZWrr~#S1zFr#)#7W7&I"p.C %ra *7"rj'͍st8b32V"UфI8rA~DW 3)_ f4Dqer,SbX*Y|uC}[Q;2Y}P !|Y]GӿC=b2Uu,VŠl<\2ue,űS[%ӭY_tm =t&ov{UES6eqLb(1lF'%]Ʒ1M]CLL # fVxd&L1(z'W_; 7ձByyy>eDX .V~/Y4}+{&43HaWifv1^w9;K<<*x"^!Q">#77`d.qa;J!/;Ms4o$qAe;m"lnŸŶ<~v!Œܝgޱ}+ Jc3 V?Aµ38,rluLFRTtOg釭 MxXNM.3v`4Yf>, =PBn艚 ZlB #Ex*U߃-$%i bCY/Lir;g׫"`:Ҷmuu^~_ogo#8vS1>;#6Ä//oB#! st#–(Rm"V܎nf%fզYvSŬ)N%̖̥6B6ڻk!L˷3F0yzNR"<>Xa@Njp2a04?x>~3=\x_dIG[/m_ /o$P2>N- Mw!1Ql0] ix^pHun>\NvajM A2 :NƆE */,A IE={N'WOL6{}Һ/&Ot*ՉTv4^[}Δ>88e.sSN*w7O/˖O׽󓓿u9\ތ;{_c7'I RNu퍟UцϭkߚQy:;q991 }|A'Q^O޿9iF}x9 =/o~׏PCqG=z{ZV銙ͭS?hɓ᠗?L]_W~mt j7~wVn}wn}!@޼(?!\рgjD]1.|ws=Μ9gs8cg2NBǛGj&}oOC:]?C?DDHOoo;/7dNw8u^.ޕ^|L$j%}[:j،2~J~ S:p^&O5c\|S'/2tU;MXP"*4NRJpk|45MSr7n 3#ޙENЏf:.jNnnHCyu=-h|VqE#'h|>ZE]|`3*E3GmoTQf8z_S ?+i>/ictM%=\I‡Tb*k ԜҞUjeXMZJl}QCY)3UgGy0󨋤99q1yUj(uv<кq옼ij `t|<+;-`5BןCǞݻJEٹagj|/'oF|ias^ߞ&ewoCT55$#N 6UW#}><]UY3yzayUn|51M5\;9d>kǚ\Wek$cN+JǣumFy^O'/,|r1:esFv=}~MNV>ڬr|yfkǜ[N*\+>r|ZIyU~|YI̵4O5\CI-BkǚoWek%c9Lǣ$my^y~Û?7lugߏqt4c{{*GM+_"j\c'܅mE̟kK(>rQߔK5wOgYC㐉_w8[/+D*߉W>loXIkx{O>g^Դ 9}VO*Խe,Kdu2/}fIqr:SdIU ?_hu0t8_̃tAy*"ٱ{犩R@d e &ՋQB")a-SaM6.A#v8q3@K_jg8!eÀ6vn^ RL7y7Ɗ݆Gj"CuKi"qS"Ky8(I3ƴY{ϊg@+fI#]H4Қ; |C7$NKWmt[!]hWWfSmQɸ~VI/^ N>!J44ȱKzSmPȃHshbբDH,b<t"[(iǃM.{7Qɧxf5It?Cy4_f"rV_# BXKɪD'iwmh'yArYw;L1&MJ<^²ujeib*?]*C꛶5{mhUZ[7 /m b_C]TnM|h3E7|xo6"\oR&:[) e{|;^]⩮rVk'^.טD\M{;;IfsBS aL~!l}ib [Yg/ag,c<2M54; *;6Ac0wU2_NTMo:ikGĒ'AoWēͅgBS 17Ef7ln.cᥜ%q7{[2%w67{[o>>M5o.ͼ2 s8";'( ˛~JlD?Lbe\>t_ؙ/0)P{OX'[훍&yhrS/b!G@WE8B}Fps:h0opNzB>8R^\tS*'_}`?RVoҷYd2uK5G-:Bp֫^jSrQ=Qƍؽ .2Ƃ68Ň jZOy\#x܎19gUj#EzSrTqc\yr凹JjmnwH3ϴCZ֟)+a]PxFCY۲=5ѡ}ER]ZT_/n{ș<ֽZO$R+>X@avH ]]UZHќe gK*#]q˳"HcXLG,eC*C3P =o|[ CުaKD-㡦 yq4CáLE:56{

L}G -/-NU GI@)hb-n\+?{3y]0\W| /lL[*Rmqӫ= kH3D6h*զ3WZ&ljD8U}ry;Ttt>6j]Hì6{2; ZUZPPEsjwE&R.%ISJxD.<-E+]=Ujxך ?PmQ&{5ʹ&H3#V R (*z+Á\> AvM>bgm4kUjчa@GMWULFxKe#ኯ(dMs10S焴͖@j)!2RUu]n0l o;i8n u8E;JEh6kqч!FüvMʫ[3}n6뤖l̢𮺄CH$A~`}!*vV [T5n70u9? H}\ROK6 >0!d*0֊Lf<­\(OI.kK2Zf4q_̟Z[VbθKNSR(.V[[ha"-bj6.q/k Zږ 5Zo;hh UF1ls-}nUMaVKrL;eYy$J3LF2_{ 芴UA2w~'7mfһϿvn@ w#naļݎ'Z7׽'Tn|Һ;pDˋo*݌G_rp啅I8I؜KEp %н^},!Ǝ; n1d|b~x/^`>*n*5Zh2\92{Uy5);FY-u`m.[7'Lnt>|~pt~P;~oPy,vRh׿v1Zkq~jk~g lcqǸ; R0P ?ǒSoj?Ż~~X+8ş8?=7kCot׋?''^îWAsIzAόy7_'dJDe ]M˫sY|a/֩t<=UB&@Kg?Ly,\ 2a⭯ EhlVh 7 BL$0?hrBwi~KΏWr4WGz# |lׄBp~֚PQZr=h6dx.ili .̿b~ҿ:sY: Tj@Y_J l(8F0jk\]V\gb̍qFϪB<)*_!akԥ?!X4Xh{A˫8AEtRwy2e~|_tT'B'%:5[\̏21JCET`YQ`WQ`^*p< R{4bQ5=zU?A>0T.cj;Xl].m̈yB A0@ݡ''eB]mhz S2FȢC) 6L?IiSzcrI@J  ѶQ}iEiõSAFA@ShH}De1iB 5*6a]Ao\g⌀0aG2$9xyl < #a뒜ۢ$Hg`G¦E=! 3( jLjFp/ # &+&(ax 59hsfʐBN b L~7n% 2 qvNm6G?;!rӴ(P0H AKx0E9'U#D )6;Ostu;2";A(*hc'"ᡦvSt "h?DqpC΃l zd8 Ty0;}r !ɉ9 ?̇LN}S$kGZPȍ#/b(29Ο!ꥯMnDɉ*WeUmxjGwa;IsLxԩH?#FHtg wJ_9:пkH/, 9D;$ JTLEEy~ @\ ncQ"݈R㳥ɘ j5ܹ$9`*A(D#A!#/P Fe[0dZTfy@cn*q %q-%֝0J+LQ'ƀ&j;"<:$uD 1@k#SU8h` |ص N+%#sd4Ky ?`q\HU2CI;d""Xƥ9lJLA1 <*oB,<6,R?Rm<\YF!Hh"Jc0wd <ӱJŀpѶ "~)?dS tDc2`0S0-ϾL(W2R< HS Jo1%R JvqJDgGBȂ5(rȍ̀4OHD2(|\ `a8ܕ$Wo=:swai+P2hy,7 z_0E(PAj'+orѩppISc~RUB;f;S9XR1cCxDRZ1.q&Xۥ ׋M+(-DҎ6,d2evJJs>o3V4UUj48 -  @rPW V\x6,4-q~<Nm,ȗZ4AIE_I" C.r 55Zn. D;ʌ"%@Fp1 h{I%;%L iq&F'@>3@#fp,kZh[:-̾ƚFĦIXD1l 6TRبG+*Hj=3mfk,D s"V;+J@+*- 'FKJR<)@4<@|u?C& 3V,XňT4/e;*퓨*'ߣjɅG|"DoIn:!yR,m2b^t\M8ЮV6M׊ǖ·k-m? 5JCeoubHjuH{V:i%BեP(*xEuEBB%G:8F.E83Q[& R(R{D o2֍d6¸ @[1bГ4R3',I 1Lms( lTNS;vna\&R.2@VΓ{][jR5XnDSaFd+p*"% "x!ݎ9Vu4ԅ N]TM1Y&R=[dyJ&kJ !*dIԯr8YN 3+k,-a1l&\{rV~c"=Pu])\X 53Diаŕ Dkʵ OT^dvcس(6W}M>Vwu?L,Dg,R8 Q1!1We A=& KB7K^CG˿C=b2Uu.VŠxm2ue.鹨蛳 Hj70K]+U߸iW'f{ ,_ۋ lPbHNJNl퍽5-]CL,H3+R< 2,̉yppkX=y|^aҼΝ' T0@M|`Jr+=̧⌶/E5) .(SRڪlEżLW"a ,LDR,/i( ()Lf++r1хb,ȩW;au# yx25.<'5ɕ`E1f$^,X!̢WjrI)(2m+5 >Nzr 6]`R⚒tliX)Ϻ+BkP<5IwB5e)mX[9- ] ɍn!]8p ,ø,H7Y #" SQ&㰉eZC`e u]6O5[hz?&λ|bwN'&WwWs^\,-yr.o3S zY#eZlszy]֝OP>}mmysֆL=£7jȔt<~t`q[hC׸|ʈ-NWoOOz^5WMi_ޥr.&ӛptݺ7tk.Yp!Ñ6§ 9bGlN!;vK78-:S0`D\cUD.F0-58Eq茟MAesg+p5EejD8KXLS2}͝o4fEǙ FUw*N`=))]uN哺y5Na]zق*j:TgmZʉLZxtp+j]_97r/ <'UOP<+Vg-y ry&Y,OZ",ūy磖7$17WpeĪh{mpi"Vd4Q.%:N&' MxM.;e>}JuVuXj#5Sż~[1 rsh )\R" 'T}gH#^즆q@r^ mOD\ث\[d" ;fnQzr2\3gފI^ScI8ay /MmUmy)I4#BwڲyuӰH',!ІMA K%c09vMMJ K6oLk-G7hH䚿#7q\N%[[:_D5gަ Qha"u`8c]^ZJ7mcK~ oϥ G@S"5](£BM6`*㍱FP,,_gѽ{U7c B]Oo}o qxݛwWɰ IƮi7z_ALl.uInMRvRN}kf{|q>6p.L YK L |i78it< J|!#ʱ'_;$9ew.H;P <툒Z!X.~ɗ+%RƗuk>@{PXS`av u7/%m 72>\Xa]ϾгxPKBxEld#Workshop_06_Automatic-Lightning.ino_K0)cڊ¨ ʆn9kz.)I!nV O瞓 iL3vNJH^YOgC?פ-pUy: w|v"I*p7%#Z|\zCT(kΖr#; !TZQ!'lP8 ^!п\39s)Wћ`z{3ӗ]dHJqTUdAOwPK xEˠ FlipFlop.inoPKBxE臭26W"Workshop_06_Automatic-Lightning.fzPKBxEld#8Workshop_06_Automatic-Lightning.inoPKp9fritzing-app-0.9.3b/sketches/core/Fritzing Workshop/Workshop_07_Servo.fzz000066400000000000000000000357631272363601300265050ustar00rootroot00000000000000PKRxEˠ FlipFlop.inoMO@9Ѣ޴I5~ƴ-LDNߝ6B630Ψ.dlKEJys1.5<\[XZ[|ɬsY+4% > A2 :NƆE */,A IE={NhrV~}0ھ_ZE盝` *x_6u67*/3(oF)cxzHӕ4_1OWҋ!B[.@W+EVTو9URpOF9Y:DqNqGm|Ló<#}fJ"ia֜JmU)?~(sϟ7Ԫ,%R?ltE3Rg)CCDOX.?q:Q`!뚿\әZ'|&fybzs{Yql|S*$3OQ7t>5sJQ?}>hcD΢u Ed|ʰnD0\gOM' Xίf댉:QytJJu4 'I;y!żizAʢ;̎{痺9Ϗȥ]ApaD}s$%3%Q?>ys)QG>c\ZΖuD|nD\LO'ZXدrz yZ gPL$,Q Ay&ai.3hNH%w&ݤןvٴ;WoKvC\f3BdiӘSBŽHZ8i烀kʥ;;̧: /{3 KVN% 0t ݣ- h﻽w -X9B{/}e؄%KQrw9I][AZʵkڋN!%irV=ɟ{ 9MAaNFmD˺FzHH &ب2XtĎyyp]c^nl!e 61-Pr3&9vmBʔ6.z:Pl%e!6P1-,>yI#1i%)Ky3I=cT!YqO(pS4׃Gg֤I{KDtV+ok8Ƣ+lm? 2'uϫփCFL ǘC%YW8$)7 8BŁx88V4Tr]rѢ>'I$O1J껻R_jĉK(;zЊhz2}n}x4돀c~s&<7A/d Nf{kԽ\wh?-vDPZʺ Š"\xyz%m$6Xxyrؿǃ7)ݦU*,9m_NjƷ| ¯VWWF9bc~Nw./Um7\c\S+ǍZ~b+T C*&zi[jq@,y2?DN8JT9o2ٚ^6^'w; X.Т︤ +b/HP?D ,nU|Z:ido0.]w~|KWBbiVD:2ԓEbӟNZW=?~CիpZ7<c@%jsƗ~惙va]n5>;ϕ ݞ;+B1 33HPibSδPDG Tgw "_|uMjB" .*9APnC.A|ke0y5zwwٟ'oW;/K}f_gTke| ؃=qFa6a7CUeɎ |/޲ e5kV/xpzG 49xԹXju3p:5 0K;C6mߥ={~~Ρ}yKxDNJ}H֯J{Ő> zZL? } ]A2bA&ctRs.IԟP|9s|E|6'yyWR|.`6!Ld.4|=4oSV$:8p/`"G2, eBc 50 wRc]+vjLF+9LTϖIa/K#OWN[9ĕ^SCK^S\t*TFn%Oy*Sf9mSRfT>ܤsp5q͠L8AY v)^d}=@TEW?,qՎ||S6Sk/|_7O0)P{OPRBZ4?J[ԴR @ cRкbgJhoTVHX?N<$VYNrNҦZ[tڭΞrT;.9*yYyh1QNJgZіGoN/0ti:zPVZEnExș<֣8ڄO$R+>Y@: a+V.#P4{~`qxjٓJVU0ZpE*2j'""| rJ %K}\L_6-{e!S/UO0SA.yXq4C-áLE:5 6yB8 O#fW51Hь=?Z|KiE=IfGKQReqRtCjcR54k-afEOGh> -N~|U@I@)h|=\Ke+S=y.+>z' 6j-g5Rnyӫ#֐^1-fl=AC5ljCњzlXÂ5aSD3=p]rڏ{xuPӾJsΘCzKQ2#[{U| ߤ`x N\#,eঢ়ᓆg^jê6Efq6[?a ZW\tGhAٵ$})Ǥ\.Rf,]<1&|v9Tjx׆Ǎ!x$&\<>"FXHUQ2Ub [vsm*|ƼђG6εXnPq.PnYfwƣɷw-ףĊ)ΥFļ4O6+AJ6SCܴ> G/ލg?t޿xrЩ(F[-<kCH)q_?d[Yx;W[,n9s+45oޤ_*%CGƒc?Yq:'sKyeO0>Kt ҟN 콕'a܏'֏w7wV'Ы}ނj%\EGLfRSiN/P& 8 N+nM?W$XO@rO.n&wӭjF  3?Ǩ,ɖR3x*Xs+{ʥ60Fx(,Uñ (8ñX!pX8-y(bC8z,\v:&e@C_omh0Xv64lx&$ ){ؒTBYmұ/p_ku~k^p0qRDo5L`mx /]xE(wr2svWhGyz "*+WCb=fm P|*/5Gexs"/+m0._R(92rb?`R.9h[(4¢ة U#0|;TQ2=Bc2B|n zd:#Њ=a,@GFX@41X04 .x ~T~ 'mW:鹸_ى b] >gףOjŦcx(<"G1$^-w7*,p^x jS7s*qlqWvŸ%>b/I^暲-1h[Wv!r AcU X*@ dXVGa\T*HX(EDW SCmK Vqc|=Z *q7yAv";(G4":U W`K  2W`VS t+%_3V ++Еh~5v]\L|:'P(ц9K.lL!!vH002>L1O'T@h > 1_&踱 pܻ彨[%풕U#/yCPTsCΙ Bב[EaQ<@cǐ۲7C AUPF! ,G=*AӖy]2Iz *Ew,8st,Iahϴ!"xQ1,C&rrf&cP ل0#_ LC N q , et2&wSh zgnHg!(XH@k`6?vu$4$!*0 > TU؄(ޕ='{{JaC"b̒s\(*?1X'RFs[j0@yb1zܒY 8*!H8!&:`樝B?pz5\ sh 5SfHЕiA.*ɡ!.1CJB! !b{R!K΀  #?Z_ WB4y.m!Ac Bh!@WU佊b t$Ș@+1Ħ!xD :y UH Z',Uұ cd }~"1{JH4,]֪U-h Dw #dos ~AY7Ja^Va #02FZZT ^!!b'I?P_"kg 7I0Ј[PtRwӳI jL>9 ?8GF d71b& I`!@Et 7%RJ( Lj$0m0ڕsF3eC+(K1Fa'9 ?w G:Y,B@4b$p4XpcQМI[3qoH^iq"ϘZC,=0w) NWÕhx<MQ"zj &MdRn e!аS^\cchH9COh+hD P86%s+.䴥d@g0i ox!.C:̄m,U rVe zLS&8@eE<& S@@yݐQArr]"RLi dd1 $6 `F@8&>S _!Pcs~\-̬z !ñ1Ύt4-rp=H# "1#A0f9'!hˣ-B #C13v'0ES`'(XCM,B%RQ? ԝA :y8NM$L{DcK %|x0%ic 0?T:HƂB8t)K9]*f z76RrJQ+iUmoyj`4hN,e|H?CFI"11@Q)Oq ꅥ(EA&(u";D PB3dNMF$@^-cfEPF:-ARRVHvZC˝Jb)ۀ%&cA(Fl$2*a-㣩a4Тf 4~g9POt=N;`:0EgcMGQ: I4Q)33Cb{9ԑDk#)Ǫ~|"ZC_Y=[ȹ12VJKJzs 0p₺⫢JQ~)")e4 *\@If<̍(EPyK(ĢӀ U.r[" pED"@Cӏt7LY!&me(GiC@ec 2a4!; %k o;^{eZpJF"$C;,~KܘL"*KE hѫ`GRfADQ~il'_aN<͹ F-3[a0"7O-M( KMYD$F5 EE!#BJ\yl1.K1XzaBLt {9 8q_lїnxA2q)"8H"ِ*]N]cK)@ QrU"hW6_Ʒ4A9T3&ǔIףzz"C=!f/@PHFoDJ1RL-o ^A!I%vVDPK$@Trqtq;ʘߦ4_$ _7&AF&֠i8("" ^_eE #\2=N-:v-! ;RC3H:  5@(ŸHQI-Ap%ԛU0,IIȕBy02Ѭ2LT`j+krR)TeMw+1~$%?F1?)1kXǒ#G$%D JEծ@"iGDrܘL乒q;_v\QV2{V$hV3VŜҤqMܒޑ "9TW;$K#@jL#Nt^c46MȦZ4 i,\r=*P80331ӕ=!!ЧЈ$q3-:ݾ{ȦQ4(6G1 K RZ vmuߊ0G ԋ椄%hV? , %]ɻAhI^'Əp_=Α܍]p& 7VrbY'% KA).lATb,a‘$e({r#S?0oTjMWK+<d-0? a`ڙplPjȹRScY\K[J}Xc#ki;5ԔK U8z[Fbð լ.jD!TKBKB%, UBu>rD' X pf 2SLUB{H}hވ0D1(-dL1Lj) MI8"FAfmC/54%PGlTM}# ")vRۙsGljH7!˕H'YШOuلq֤ֈ~bcp#2" 1#["sYPp b˿}w+uY!,,&T#`(+E1ڍGa7d߷Y- k8' ]Ӽ|?|a=bdl"}6P.ڢ+٢.b;E"4"IEnLl+eim N:ׇҰ Mٔ17)n75}Bä{%% 2MmcQ|9+secVfȩhYI|6}Bh~!(tk+|ak&h؉v7_q7eI( &E2j@4e9_u5ˣ"8jsc^#=;&$QJ8{>Mܜ[(*dQE^g| p㱰ddᶘҥ< 0- 4 /YL/{ b8v@Syo^j aKgOu0I..A?as A TA K%`0X{&q%BƁ88r4DrߙCID㙨O` u=>FWͅfaZ/m Tm.Ѓ4&M1plŚ?%[7 X iÆ9=0cLZ{lJbPFdi<];iw %; 9r؏(!xK4.w?)~'Pp0{tZXu .GCm^ oևW/2cAzD?[!*GCa+;h~GIj{/8c7>a@SY8F.GW"n~uEGZO=O#)"g_~7bÆ8;?^x6÷pOXI8٪gpcU/Eőu Vk׋B+ Kw5)w`:Jϖn>|*FRASKi"&bb)Y^Rlugnê~՝7#6WOODHZL+ oiRgib3,՗^:owNYLߟxsqR(S:#GpPbW:UpHA:rAA/~0W2<>n6]G+ْ*dHUI3+-MƚT9o mdSeE l&)GϞҸp&nft-]2]5]4)^9,<2>]co hb W imœ9%Ϟiw.e|k'>U)㪵v]ͲkxeW )7Y+|Mw\X.ReBv??nk1+p&St;jsB H&{F[^B$)nyA9baaII~h\sW(z36趱Vc^0B.9ARxPKRxEˠ FlipFlop.inoPKkxEf9a8~Workshop_07_Servo.fzPKkxE6 ?:Workshop_07_Servo.inoPK;fritzing-app-0.9.3b/sketches/core/Knock.fzz000066400000000000000000000311711272363601300206460ustar00rootroot00000000000000PKp5DB62Knock.fz}ksƖ&SEFÎTbĹdR93EQ!)o^)>Ň@>uh^{|{=gQo7zƽmlc.bncY}{>l.QhtUwLH;xtWg5POn:瑻7F#q֘&}W;\iN7gŤjc[=jeߍvw5}v/uu5.^ѫ.YGse{Ơ}~=14Zg==;=uW^,N=_,OM=};&NGN'/T^sj{k7o._&/mWUì"^IUjo٥E^Zi{n\\s3{ָi{Y;irz oZ,|'&C7[ŕ7p޶J=/?ߜ Em ͘@iSJ?ejGg'\el2pJegNzf̫IkNZmS"3*sj]ڧh^}Υ)sMa[} 0j-YјJ]HpuuTiBfo=s߀Q¶6gIgN(;RI8dteG[yfk[xY7wZ q q'ᙉ{|-y=ļ>bqϋJhM!c5E]9hdX./km8;f^39jW =%%Ohd#>i3K,)+qPa׃J< a$z& ՘I| J}JF!ԧLa\8c.$!<cC=涜Pm$Jܧā] L+]뙤! qטK|}JE'(9%#P s(~]\BlKJ*)%-oZo,/Zl<uq=]?hkhЪoϲnי9GN gZ>fO=v4uDS`5hj*\D45 .ƓjxE7>t?Y?gVnY*,FNzTPQ`S8Y/gS<Ӷ/J͹Pt6-R6>}71h1u;b|1Ӡ"lBƗuskNI8I6Y#k.I^7PBQstE?Yl}ZʥDp8P> B'j PM^L[$[>Fa-߶V.<Ҫ{խN <@-m {ӈ{2ʓ+v[5t/MV5*5tV$aF'4NJQ D< l6^pB %8W$gO t]>]*}転Ow+ >6OrK)c SYHcm35K˅ \~A8j) kLIjHO +iº8}f9>gZ̦*faM#9Dԩ2f1TǯOD~(fҬQt} c8djɺfh`0\AcL2 : RSg3K>S8I)rzVСwF&}q>R#_i" 'ʁ+T- `}忾׿ϯ?4Ư|׍Sn)l~q֝Ln^Z񽌇￵~uԝ\k'^g_L.ξUϏÇuy0LhO>Ӎ+답:qfk}7ɢEfEEܪ6|xA_5.];˶O/y3.h߼ĕqs"̋ptMQu{tD ve|8 NԉHgNWs 6pg_6Ư4S7n4:tm;u\q;.aٮ 7ҙlō^ԛF;iLnG_B} .3+/2gOӃI6j fz~f8Pх}eݿs^5n.|;~Y{LSݑǁ`im;tZ٠kdUT}P_]^k< FSD8)mF,aǓ5>L> ?Y/ՐVs@rfd5r4Hj++:%E)^ҋqh¹=t-#\Vz@G8`M '6|PLO!~q2iT $qek.փ桐8kHzszL`<мp$Y'A6 vhǣp18d;!Jj{X,xaw:7Q$XOb=Bc" ̯)L7[govr>SC4fR>n<6JOo&koRCL<&Io@F%~bM;8!Mc\X 9L*$s>QO-?:NlY V: z7cuA=118ē>FC&%ULS!ቇ~Ns1I4)r~@$ DNLlImj #X1 iix5Wv4DŦn?>VS8&iCVT+s3IE\"-t)w)ىFuRWPm@̞0%X7:-);M|yzPmEO24`PijEfUٽbf;?~R!.8-Xcd,<Ŵ&b\) *8J' vs=ŠNdoG>Xα~1`GϤ>%E|1PFMax:vu':f~變SàOк\+n]uoh{5ūc &{GjIHjk ث.Y&V;٥EgX_,K V|8 _MF|&p 14M] f>,s dq^H}Z4kMoh߲dz/B^y,L&"fN/BCq c;)b]sR.9,|i#a|߽;E7c6G!G,ᩘMg6l#T>7Fn&w(|bE+rk{darv,!U䂜`d4ENZѸmM6k2i}7\ä}f~2^\ՠ#ar?@2݌'uʍw|3.83a * HS*i6Xi|eZW mPNd/xvBV/dhVz&IuNiu|~4xLi?ģwk#Qc8~d 0N_[Vt{\S8&iFVtiKԂb%32 0:tqı4`QZjT'X&X ZFњ_t𿦸qB^fk&Zz0Ĵ*)ZRښ ``L6:N}e2ZLO|Ac(K_(2ɲh(”Mf3xJU]dĻ=ajs@U#8eqb_ˊK_(n}u.1ΥT420 (3f4' 6S>Vm3#c54|}'e\%b=~[LqQE]US8bI.γʼnY-+ZBőKpOz(|ovf-zNc_Y$uK0IaNXG#bϊ`.qh9JZV(v@6q r1E|}D˲8\Lv|39+PhSzzڵ -Q/k:} l!(,XxZzGj (# <κ-e4rgˊn?VS8&iW:Y~F?dWßW4wK󷬱}b-*S%~zWIq%KNSkfgfw7Yqxoeqo8x 7l^W\Fj8{o7߿u㛨Sn?6x[~Mw2yj2Z?7^gr[y|1f(j Q۩NjwS;Щ7&UǛh{4ht=^Edo{F#*/$1ˇ/fz:2f 7Ù\:d}\gyhx;K9o̩=2qB ^We^qgeقKEQzb:qBѿy]?+3#"q]; "$oN,V*uǩ; SH(Cut;&#G#mS}"Aw(iҷaݱ)ja/rBB%I% Ubn.ewLq܍N)Ʒ% XH١rjj+5i:ߗ$8j_E0EͲUTD%餿9%Tѫr T[Ap2ҳv'/>NL ?{pݡqWǩ2¸W&qS;˻ɚ"֒T8{Ma]X:@Sktoi?0&ԙ :bT&tps꺻ޙKT8]c8Ӿ::Un 7z/\:y: [$ʕp_S"S"wdNo3uN)\W:Dͩ8翍"WAiE)+g긫A?aߜlkJ㡥 5},î]\(B#xNHqYPdb,?T6tHa~rmB:dmjs.ٳrHSz F}1뛥S`O 4y3]w(D5jEW#x@(h8 $/ z-*'ApYUTGEEwڢ[APN?,s) :e׫e't>Rwyeˬ-]vztTݯ!ŜDef}7Q ,ˊۊRLdz*lJPuţjz6o?K|ࠩ#]i;Xm]ٲ.mw݈yB A 0@ם=AwQ,.rro SRFȢ})k7~I 7uș_z!0K~QN*Eї&X?K?("q=r -F[YZ`仼D─0* @ MrR?8hN:驸^ B]=΅(¹n.Hj:"%]7^$rw)#QndOׇOj !m*fJ\\5㌅]:'.]쓸1vDCloK:U&B":C$論^(1,Aq+#(`ʋˤ$c"+)΅6qODu@f l݊0.!I)Z R&REqɝKڷ9D!$ŠD4hP[;$WPxr+0 \ t+_3ʗVJ+ VAMN2IAM,.F5o} < .4Sd}6 *Sӛ9DB\|9nj;=ᝠ[K+`ik&wۑj~q׫Sf5HX 2Ձ[EaA4 c5Mތt#BHF> , _ʐ ^&$䎅gf]q`@" {X 6/h#N0RT.55 JPƇPƒ( %8Yzݳ Ɍ2&;T#w:C@!Au E!7$؝#F@B(1 <}B6Q" ^A]y {ş +)pK%4)Ř'縂GQz Its^7$hVԣ %Y8bȔHD[&&8o椝Bq8 !&%/‘QMIX9 *D@.p#!RjMlIPvP+ rF2g2d>EL Fr (|$3rBF*MB NxD mcCMiqvD1j1NTj'*]ID/!pa"=lk4B$N#7`Hȳ BqEyPH .#`6 i7G4O"R䱌^\ )ra$F9Q5 q$@'dQS-1CM`@X  @OWP?x>f]"6/wGFygC7."_Ojxؐ."/tpgI pb@vC)FCKKj#&IͲ`ԥ&$9#5+YB:3 IOXH+qԕ1{'%Ej",u?ugQؤA&HF$8wZazvB8N&RI@ÈDHIe`AG"5JTd.~-EJꗐns cdԭHK$y `r8^];U EEdn_ H c映bӾ (a*XPy ذ@Ha2 ̗2@(tܑ7L,.$N;ieCH0.ʄˠ$i*C; -]WE:+݄a!#峀aAd@pcDP à]0U#Y/Fx,~9|/<\@%2ZFu&x 7p"'@ԘID87@B0֋Z hȗZ0X0)]5/ĞhN*L8ҋ-"/0(FL)I"·DH ]'vbt"XLǖR qpZW|<]%QaoQbrhL)[4׃xK,]=>f/ ST,Bf)㸓w0&4A9@SDyTzoPҏ4cb6gDiF%}Ȑ{)@wUPyRI<ɢ)! )n-i'F(C~3d,i42_pƣAFj ZBH;0E X.LI4EY @ 3tA  sȈ"" H-#\r؃3{秶bJW-A<L4T vB&/U>U5i~{{c&1c?1UhXǢ Q0R`,t]$IqBg9=Uoa&nIw H0LnA]͢.xq   0ӈ#@ 9D#8 mcG>Ղ 2LRX$"O\;urQf.0%E(!0[d$)2:^O7/rЩ#9A^̐gB{~>4bzu>!f%଩'xqLlI`Q!*b:#viuw#4!͉WO (x>(S06FJR<)@4,|#+Ln*JQ2w/*QO2`a|Id%A潊iv2p5@BC[1 \ߏcik!F:UKjjRij5+16kECKۇ5?6yCMPuG:e${VJ/cj4Sa&-*ǂ"%zP!VD! ›GQ7ۊڒ-k*B%kˇe!` &NW19Y(\&L Aޘ_"ຎa(H--VҊX6Pڢآb;EUآkΜ$h[2ͪTRXqӬnE4O_J؋aLٔ1B7L4m_w01;/X"9̴Ab+01'?; F9'=hUmg{C>&sbi(7<抂FEtSyƤ΢Κ\ \=PRڪlEq~+D"}?1`XxXfNQQ1S0f++l9XLaӐo"sԎ*`a*BH`<\ xR5."'5ɖ`E3 ^ ,!eђ`SM,!g$1E=N jBLa'aM=WLR9 i`.dX`Ԣ8Aeh?u5G]"^9F0<;_[(e0{K7p p$`Gah|pӱH`ܰ][H"GU`^%d`JXB/~AQ#2q6LSy'zWū5'=83{b7>'Vno'j3i8[ܶd&ÛևzϽU+?pmO>Lهn.vsc7sXEָ9ϟ2+9zvϩq6{;%Mp9a~bꕛC 2^7f76HؘjV ?[Qj&N߹۠ \bQH&+%ŽS9u~|1sܪDn Yj5#_ 巓Q{0^sA^s߂N  ~XXhS.崕>wA)n.ܹBd+ڵFJ:}~)l$[? OwF,Q%KJLz 7=V3̯۪yw;iW|ζGb>P_,: !cmV0p(dK>z|U綾%y(چϰks?,$]_GH{%Z:^=KSׯK*_6wO9A'2e RzrQSD;H&O_|OqvN_܅evǎιO;߄'R1>g-:?x^B bK!CyP D)G͑$SEyThp .رfXm_s}$mnSI[m*71h׫_l;Rݾ(Բ74'~6.?dOVNZC-S Ђfo Æ}1])So[?PKp5DB62Knock.fzPK6-2fritzing-app-0.9.3b/sketches/core/LCD.fzz000066400000000000000000000510661272363601300202100ustar00rootroot00000000000000PK8DSQ{ILCD.fz8&1]fAfl*vgںwOYd"CSVp)R" R΢$;oe6>}{ASr1?Lo'O�H?O9atm׬_f"zß~__h>~;q0Ο-K?r}3v;ջlͯOݭT'N\*jY(w1ciuK̭淔;rȱʔp?=ݎ?ۖn|Z_gv(]~qtwW7ӗՏw,2Jh) (D¯-kbV~peR 庋GO/HB$.S%+ BdEm+uS"Rh^B kk2hO<۾J#{jrn@U*BPy]yb5j{ agϧE6W0N8_}>4Y6gSV[^| UݡQɚWeaW;(4}9LTa`2'W FchΨ\UuMt+wvwofWeN 3a˜~02<{Eu[Ut[޺o^L"y[Mt\[Y_Zu!Dn\*N]d9qGiL⻁<7/;Bqo?mkx,nZrc}O[~S3VL98cńʗ6V3B7VDg:ݫztHuzuX7sfU{u{`=a {ajK=4ic ^ [ֲ'^r{{"d+a_Q6O^ Nt=@{C' n[~/c`=Dyڶת%a/TKL=, {xzo]V9DM\aӋn9Q<̃w-+T񾇰z^ez*ssp*>aʼ-`epNaZR_ |z*>W'"Ÿ4=8=UAʰױC_GztQafS-CahR=H}U{h2uAW)~*[ֶ*^}}{h*>ax qNLJiX^<\ŷy,l_*Geo&O͸e{n?O\Re)-Z: DeƤRZYq*e ~{7k %n[QiN='2 Zr[5,\ʆj(>8 eà]`:尨o9Za 3k>>Iנ>)OW+P4N+IV$: X $ix<:uFDONm Xج®>6ﵰ~ [ZشP X2 gNMD?lZ'>;TXOu_D'iI:[:7OىT˔WN$Sԉv2ۭi%m d/f99\}bğ*'ҋ_d'yp4h kSO,0Ix't5.RWd)'?d4H ńx%(j} ?d6y0ba ?Kr*SW$q=>L;iorv2{//_Ƕ4,-9"%c%^=7؞ 2YK- F[J VzA拜Zd;ӨPE—PD—PRD—`Cu JrH]!lill -t`mRDdD#o g~28XcW-p{w[.ypGNnmZm|M_G[p)>}˨/sewn9wX}%Z-"-2 6q|"/4#XK;o%.][e>znZ@{<]h OiU 譨SyӇթ氇p)iK>`m6ogDӦ#֦ 롄  @~+ӿOkxbAK%eQGL]hoFÎzNA&0NIljTW7!NAfNs] QZyX p)mQ'\7&3[ti)/]O/6uo**1:poj?=_ BF{?NtqNx % (~VRm wYT{c[_OVg6# o'JzW mڴDm[Q߲NUJ8!z#iy"1/7ٕ;gdp{<=1v0Q)Wj'}F 3speExs/o/Ů?[{nb@3EJ& .;uaXUIշ Xg"ͤ^5K8LAN&հD*^oIWB.Gk6JΦ0y_O>+qu$LU:U#%K IiJK?A(Ar탛qm #Դzg^/~ygecEb sMR}˻  ugMCU4Tb[nfGAPmFf<G9$DKpZlY薿ׁRb*`XΡһ2Zμt#s" Ճ!ֈC"('yG⢙E阋*zv6u7򴓺$l^Mr5@QO{ mqUF!@I*9{$SM5{0X L"p/Pc:ob͵>7Qunq?a^ǟf~ףturu7_-⯾|ӧO'N__?ur?|j6-?mk{+V/k׸Q >ﶊ{FeC㧹og/s~f6ۯϗ}lj-fj2N>\?=w}+hO޸򋾛~Ͼ*W_}2܎r426~=?'!Y/_!<^<%+n:O+_=/^aMyazk2d֎mES_Q2./itsmo>~ylt=^_izss5~뇫7ܾ~bâ"Д+| ֳݕOE_WŕN`r,9QExad!s뀊Ik8=h5ꭚ-Q'3֎u=`U²lJUy9\uQs{unxI#ySC)&=Syj֥Z(1yZJ0}bKִz@."Ik.1DX>ɻpYzFgnZt*LL`=\2,*ߑ8F`1Qc]kP`엸깂gjnqP`= 鲽GK `[&I[pdXGZD"Ik<# V.bhuK8e%Άo"Ez:FeBN[mS3iYYYKq֬u###j9zu|.։۽UJER$ȎUc55i Ede5 @f2HЂZ‘fh[" UV1}#h&;^Y3!뎏H) ݙ:kMj;Si8nCHyLqݺZrك βN}mok]4*"ؚ=&3aJGF "C;C`PvH$H#𝡤u/@g#r%)1)K0* W$81lرN %k;B7#+[W0h%<0>.* XٺX -HVN4G;^Uߟ8[Q1{!#9]g65pcF.dmFEǹR0#2ψθgN(:NP2y5L"ȎV6uT<(<$k6["1sEU c)#]OFft}m)k# /uuߍmId%XJb@p3^Ĉj*"%R-̨TR3U#ljƒ\Ã2D*1?EbXC)Oj:jtQNw[CIUk(aFʜ9 PP/>æ"]%79n_*)gE"d4Bfn%+|4H3D>Z$J~Uqj%IHL˸;v2 us#sWKg2rd LbjK8r޹-"1uEՀavHx7}RWuVJ],d<%lC0\ךD@rxAōVY Rt- Ieq.W- #klㅲ# t4H3pbUq6&ɪX u #NDon"8&PKUv踭ٿ{!⃉۪.߳j:"ψ!k[=0 AΕ|U fč{gt~-?qTv\;?I)yjjP\jO%׷8'_8j{V#%-: [Xw&'LF<쁱mhr쳰EHD#G;Hd cNKx Yd'ؒi:NG!_WJbrB\)5P:v(L"R]-sr¶Ź֭H E+_J>1)?;E >L}\T{urπzI.IK{@H-|Hj"NubbW 5Zc[ qW*'3Lv0Oi-I-.Z&n|,(TXdv'cn'bdل"B `,R PnyY;Λj}DNztC>bVFHVČwLtU#MEaU%WwgXs`iॉ,ͱ4Y6IBRB(Jg'6krq<c$AQn902DJbgjJuԌO.F5X -K1 'VTH!fUtlqCgmb..k<Qjk;. sSO@ú^]&iqX4-փ"r.wXۚ jAɎ;]^d>(?fj<|ߑ=o ;͏H@ kAblU433q*(QܤXXܞ"i@AWAն!tbښF.ein XZ׋[̓I"ѧV JX6-iT-l2)0Vs!t]e*Z4/ 9( =Q[cUyjd-X_%jvkjEFpW)X']r\tݥbF@ƘV5~B 5;~D$fD&&^-ldښ$Bi③[2:bXl4ۃw Ry,!k3fkMҲ#HM.[ (14H 9mȹvjDDVmMXYBIX[HQ2.LguU*9 JG`]S mWs!xD_S9HDz\IH3BLL03ruexDKxr{vdD&ZDMu##-2yBt5Me=x*D8$ M9i!U[< %&NBX$H}\`$ORoԶ@HقJ֜@J~̍yLdن3$%2I'0ʵ.glV$ښ$F JgI(]f{'9%\BO;ၣZs p<5mG7.DNS6e# b"k^Ζu`D~DVmM ۂ,6YlIZc`>jU1"k%;ɰm_"#rLm0#RcUF" &V\y\J$t-I$tִ&.;Uu7OY*.oKO &mk[s -tJ2ͶEdkbe9]rSk%$jkZ@s"uϳ2$y,F(Nx꯳?O맫wHjje/S/ן'GNNO_AK>9|O9h6 hfplo-AWu%IC+v 3Йd᪯B}ءb׋X$CC26q565(ڝ捆L[;L3mRo4<)u[Rr/`n*ڑGsl; Xۿ^?/q2{{O)) qןn6%&3ܓΚ~fEv;=nѯc[׬Fΰp*D?np5D{&~]QpEԽ:<VB{L{ϺϷnӓ-"US\d\kʓmU3aJ-1)$=}x|7~}oq<fl@C[:TX+^h^uV!2i^۔i'!$N~uuӧO/~x~̮l+WteFiz;ZY~?_xÇQ\BO~z[|W#[0y>%I"2VOo׫f֞vGБ0IɓsgM.ahex4V;ThM Fdd䈛b k_ sblGury ᗼkUw7~xKY  ?9QC{fB e Vq}C/%K1 >>OKflRd)Aho\ø}KeI@P_Cg.H(W^ Kkm %UGm7JS~pua _7b@*( ޸;_eUD.+"gMb;Lvd+RbݗX矕\xg%ge\u^ʙ>|eʕKInPI'q>aom픁@#݈@ex@&`I•̤K(Q2J ۂPG ZxVyG;5#_:s]b2`p{YJ#U@UF:%i@ڸv?R[x&"PgƟ1?_,L'N0e/%G$u^v5j! Τ>WEc%ګ,J*D"N`34 b{mܿn+o))!ZLS^\.t^ 2ckۿT'm=eeЌ ̼Ӆk0\/Z0'TYQ%<^.v JeZ'V6]E]M#v# hݎ'xrh{{1sAȑܽ9L t-Vj72ĹsS"'t+eyK֗X]ᤪ|p"Gxd + =Dc4dq0]No\g @AXXYkk!CG`Ib+^S"+ Bhy 2E xEH2@`7Íxm'3܀6pSQ1kgXC1W6z;ci՘vdy㵵}ig?q4iWic@G*2mS_7h,4NX aح8'.#an>) oiT70 nA2VLdo P&GYqmG{7?yuB` AO[,tPhdɢ QXL43LTLf*Z -ΪQ(4Ѩ CϪQ7X͏h`mh4RDq30;'2>D5UN, aOa ^k7^][%.K2oK)M^ AXLg;/5T v0Y3]@)zC@wNg _%̋{U\)'5NN8hgAGc ua'R!|!{ xDA14pJgp5+5.P\33\!7HRm@f@cv>U14ܑL 6!)b0.21:AT1rTxV(qVpWnB+b hcvT:2"E=@, UⲤDHm Is@[j'7A;H9 tps 0A=@HJ%MX LpQQd *{z؞,ȐC#vGqI3|gg%@K Kvo8*C BG`H 8xWa<9 &`LV4`%c- M&e`)=2yBcF:Xj;9A?a=qXrBP kZ͔ymXw~r1VnkjۛI 0dXRϾ.HLL!"$~}!2x@,z @ GP`QBx}Bhr*tTjn| DTpoj|vEji'Z f9ᯔ`nϠń(I A 0.pC^F81XbV`r >#ZEB Pu XVѐ ab+ x<MEێC%BдAG|Z-U;+M*\rDo 5p#>BS舮TvTUH0$ \0`fch3|jUw`h0+h7Ek,EtQÌe!C5+XXRg8~* w85pdUML34%b LK &1'1|3`k=YF@y`Md:߽gXͼ,ɍݼI]zL E0ư)~ThO,qwdח"`!VPвk&蝌:5".Uə%{˸GVx=2X`\&u  {%E< ^[> /I"tpr Zhx3eAmG4(4C W] 04F Pڙ6%3k.r3O{N} r*{mAf :XSk%k=aۂ[]f?z”#0=abr@ `Ģ#6^c Eao@.GwI1` @lKL<R3 aL~r T]{N3׻tCt4 0=@= kF% y2aXS O@T!0z /cpua; '˜1FTZߣaXC]MO~9&PwR(]8!4=Cрk>C{H4T' 0) G`#q!xe#|k[[nͧS;6ч],Ċ!o\|Bt$YB;})hă=aFؑw h1].E r _H(UDo]p44 m.K%SCѰ0Rty#nvʠ% FC "A F*=E|qm[m cE/t,CW/|M`` h(}54\wa:v(͑e /'@Qb@ě37opɕ¿hyGB" ahVAPO*E#fEI0&<{K}7lJkq ,\kq1Bq0(zJ,"h`:Fb ѳϰ|`mAa Edj7P1l,ېv>uv`N2L. M; HW^3Is^``G 19GUe B VP? Gw N.>F݊nrNo)C'ƩU27hü= 4=\@R8׈Bī<l~rbt,HS<3_ D>}L;%?zI[_.ʠ6 qʆ2Ae i( \wht?bw)4K3;ӄ`>Y>!ȍqHxN0a l?@Ggգ(~16 as/@s*Lo[ujxDnֲz+1|ohrO_jFr'"NnX^[hCbC|#o R _)/}4wTE.ݗ+aPGL 3;+ ډE`1sPk 0;Q˲U-_0tR7o1F9*.s&G)Nŵ$ @4hOK2C'eɻ T}ss ASDixo8]hl veF2FhaT}/r{#2(8?)ʧ*Bd3[dt;d&$x= nGC T`q{D}. [1JOe>qs$Ӿ5-M1ȳKdI!U(x#ӛ2HyU[b.HQ1D:0LY4%Mivbz W%xx_<|DƊilUg|1&R ۗ~ RzSAT üZ0pG$z{ rCc?PoEL,8.E0\U8]pވ-Yj ;..0mVPFqjZJSu,0\K[X#oesPUj(-5T|-2Vm5VU.B_[#T^*/j4.rDG'1B<)-h&cP K %?7a/͔)Q rE~#&c'LID;[h 3e/Ur4 U̯5̓rh'2ᛑa*\M} ( dޔx)dMJc[,ܰ[=%rXb CG b_)־f1Qs1~tg8 EĔ*܃ɚ&K`j! R2Y]> S‘fVx1+01K~=Vzl㫯#7s@4Οjsb!pN%͕xJ7L+~F/Y %( (XQ~mْ~Uq~ DLAidpuޜeI|X#ƛ_ԪT TxTLǩc<5+,'hYh>>/ .|> -seW3Y$ޏK\BN0?c &v ^X+ m: ;alIZ$7.f5K]/fW~5V"u E_|<(w&$(vg1܂V8:p(%G.^}f7]wwWa9u- i Ļ| =Pl8<8 .C*[7e1!l/>GS-gs ci~O~C~O~˧//㇇#{`1z~l"|ԖoqfluXݒ ܥTf32z)k:RXaY1}PVDvNXר)Ws<;Y47B*Ϋ`-yz,Wr^99@IhC%WFfx.qKTD:^AL %2 ~Sif0*na#N֜?-D>cT?iWB!drh9>C}*ǕѧiGh?IC-%}:xtDzO+[ҧ>XmvRظ}*C֧O*Αi=pʁǪI' \~3r i[uH.GX~e<~x2=X_޿a9{vmr ?ik3[aX]d0px`-@u839;v߼cEn C#&LK1kjZ(%;ϦqZq G@TN-777TP9B&U11jZȞI| =<ѓP=%ճ6@ϋD='.p2q#tȺM>([|mhC#C`yXiH[<0nG`жaSHp-H딢-XuPR'qx]ak1c69k$R,oIho\Aְ#Ƒ#FD- m kɠݝ @E@H U[<ĠBIXA5EkWLeGB-&!Zf/qewCadE􈔬y숁`eb`Ue 1>"تhND|LT|H"[`@^r=(BvC#d-tq6)čHĚydlu11Ÿ5EY-,qHT.{>҃+HeͣH LҧGP:ow§1yse8/Z bDE#'%ȓ@1 T ()w+d!#|08GY紤-^HH ӵO wh8f),mS$몭iD2e4]EzV&UUdgbL\ך{VY{yYS([BPTFCR 3N\Dd󻻛֔=bd@cr$ɬ~w8͞h*t&LU )ypo uQ`-ҧHZŽC>Pc>e~1-iYiYA)65Q&!J\ 0J.\r{M7RN %x48b:@ǐ/&~+9[1A;hbN.T04 -PɎٙ&41smZBDBJHcv}y8qr-6iK4|q,q`䬗cR#kN H9I,͎HۚOkHU h['..-k`~LwiMy:gvWoCu]ug(fVCV ߵ W{I{궀__wj`1vK⨺s#lbذښkOP슬wUgT҅_Pv30lyx3PkMSun0dt%6$"{ 0$rA$rjkHVu_*r?>Z|㟪%Kõ6= )MAJepRCu0Dlr,7xgOAGVbkKGJFsJIwhlmS#f~eDMdLزG2M t=^Ny '=5ZXgY~H#b6lHڄSKA)δSvLTe \n!lPU"n02c%gIgCIgmaҳzvwwg~mYق:gUG*`d*>HU~&;@HКǒH%h" 4&onx<|M#6#A'!hcf*{ 4o}ry"cM-# mbHښ歟H W0zFΛu,5 u3fpebPt5 9ڗ ۢHCH)%8o&r!j\ 1dt䑥UQ$%ќr-;>v_|)^$s`.dTD֔/C*Tfqui[װ.+k,CbWYl-59R1j0͓:ML^ NPbښ!+SQ0`19:'H0,Ȅ"(s4%# =k[Ӕ0.\\NEKo6釴yG0e,nmH7 U`626O xCM9Iն^%N.̈} %1+ dq` -1iښl}`rKM}aJLjoLzjW҃77mWު ̤G/*WNP f`m~CK-DzDzYmM x\e-g6,g*X=,<8Qժ U2_ )fbqBݚE2UsZ@$Mu|:OeGz>Nɓ-Kd$-K$4,gOͺrrۄM"!!rJQCjU1QLWjYڔ15yK;?%Nwkր%%jkg%2$26dXw+8{PK8DSQ{ILCD.fzPK4Qfritzing-app-0.9.3b/sketches/core/LED-Matrix.fzz000066400000000000000000001003611272363601300214450ustar00rootroot00000000000000PK͚8DauV[ LED-Matrix.fzr$Ǒ.wiJ{P_(r&%jh%FW70B=cP dYYuA]""dj2#==W/hC^M_ܽļw?N<<iL_56L4Ѽ{̽|u15 _xj>psw?ygO7Oӯ^9[//v{|rߺ?鋳O7zcRW^]Oo^?}B6\qJG.z#Soxw?_ۇw_?|WoۇeBo^=^ ۻo'f?ܿzz{rbWwO7W>}ݣYue^+K/ߧr?t.4)Wg:wGor~r7@/&tRpq3QykJSeyg7H0_n]V7_^|wy槹26o~y300]A hE}O˞^~>tgx;7}z|Iu $0Q¾8BA*y0>%.) PŊ˧M=w|A,>%hJ+=B\鉑)|bnռ^9Kqٻ|Q}~yqMX7uc^? MÎ'WQE>l W=4q'0fc$)W"n;>fo+O Ӕk0nqQ{|pU*Jc,\7DP^V:GN}^{p|ԲV }f/WOU<xm ^@[(Fw{t] &:^5pK6lhR l@.@y {[*SCйFwߖ{t] :T&glhR%pZEIUmUKy|]Z*M-Z*AdCa{|[x=R ljRǗSBWГJ*Pnq74QO#dvrw#Je>lXJ:Xx|vC3:U|YzکTnRJ}*>:[ mUOJ>*|ذJm2*UT/cJ*>&+Pnq74ZNLjR4 gJM2J"yÕڐ|-WMMWdCە:{|x@nR ljRǗiWU+nhRGgt+Ն*nR Ku >pЈ:q{V,UțT*ŐI2FGa{-Y*aӞ MYaڲT'a,I{E)nRGޞNAK2w*E>&-+nRlnhRЪJw:E>lЮ6L8а:iohR%iK"6ж6Lw*|ܭ[S!x6xohR4pG¥RU64qɎ*#R'׫e//>j_u=ͫW>OfzzI]><^/7woo޿~az^.OA͛_/j̗O>]~"'[z2_>9m`Xx0"<jx9͈i._G|~ 0hlf(==wï^M>lu7pAR*\4Vr_^lWvYwV1p|e Љaeu>ue^<ǻl f0#RHÙbÇUv"K@Jb‘NI,3\[; &m81jΙZw2)*YM)grn߂V2D-~Z2(nVϛwg(vҥ,̼we &q% t5 hǾ@ǛǛW?=|{lzBYn&3ω͛ldۿ'ζ| W$^{/r^d#박F(=:" #멣FY 'nw. t$ҞTq,tX}ly"b SoT%adYW"`d]AdRwY$ NyZ$])b z̸sY蓔|YrGa )H!2sY 0UN8B(jSBv_r`^jкf#+Ќ Fxz1^m'Z9Vq֍\ZZO5z~,2Sh=o0gU ThJ'[кzb 32KcSu]*n u0Du 3F/2iG9n^*'BR= +pD殖> gbg\-e޸ι%#;35#;SqD*331\'L/)̸u`Q591̄%'L: mVFS(St(äTqR4n1N=,wp;MܓTQ̥oygCN"@W( Ol%xBTJB11 ;n0,{xDErXȑG(`BXdxm1\a!'r$/aŸCRu!g!Qc9̳8^}v$RRrE hw# he 2_|M*n?K$#17o~/>^~p?nWn B.U#kgqlϱI1i4ɂOg=~F펁 _N±-&a_&_RM+GݳxA0yxcW|mU3A1wc'.-Ǩ`4&TqaA"KTFyC9DYݨHúaAŸ`ɎR5 n0ru-uc A~`hMnU؅A.*.l)P9IHbmEB5ll5GzSw)Î'yK}5-wV?UT J%pex_2^3;{=GVbk3{X=Z:u̎)ΤT<,ᷗl~{>Gon`S3&?:DT'Gw~-/~_ч}kX~V?I<(3?)G'<鄡'=S]{.SbOwI_{̳Z,;si.%fCߗ.h}́~ _;z v@ozxt:wtI-ҷ O~K7+t=_z+/]bNZ_:C_̗n RʛMb^~ &jqs"Ԯzq[ڹL,ʹh!t9 I0,puUwTf;TaaD&1 pD1>ila6E3;diaAэɺ4ֶGCɺ$SHuQ:k VR\s-s-<" qjso V.5. zYR1T VHlgCZSfܪlz`\k jx@HaC |b8T6ŝR4FZgl4ܟm[6b/8ߩV:y6 93F}m[osNlWjܰ,y~ZYXUkA&:)V2Xd%XTc'2ܐLiT^<62`׫M52A & jS;zibWjܨ4ƸeZv>L5ncïwwlAi/|W |ڝהmy$T=#LB)k}ag~' ɰo/>~BUþ7z ə{p+*|{ߧs(II~ʭǠ3Q%5L*]PXXc6Pb=p6ܽJd|1>x0l#e"$Fu:eMƕ `"+ޞAv\+Ibs,m[{7VIhRFɐ$<ڑ 筅@ezD=xż!WCX1jtqkr5:!x F2fxaf*j|XmT >BkQnV wIBSx83_2b ^HV X.[nF6đ|[$A'lOXml2HZ:|}FI6{yMv,AƆH2 qo# R6|[(A֧lG)L٢g\ۆczG9x£l9 +pbȺaWe; xnA2o "zi 11Xi`zũ*L!xh&yAxoA{e+\y$26ʺ+Fƶ Ɓ:tTL#cnG[ijɶ> dèۺhrڼM$o'O޸8y+\zDK6DPf#N6)$)$o ̍@A{-:MSEUttrdl"+F}6`l*? MC Ei}&Por[?KZh!يmm8m\O'diQkk$#?,"p^lŔYq |ťxEGG {m(20 dP.nMe}xvu.IP.kw5l(1j3Юpnأ!mlEn'pw>poKs7pnQN灷U鑾}#odH"pUnd$'$z~&HD I {4m, AdP.ci,Mfiq7hwXّ \bMZNֺlE8fJ,=a,6 Q l8ڶrJ@5|[l}Ќt+=cJsN $ LBkaRbX?ǕTZeoQ]8ҚLHJV hĺώ#I#m[ 7w30w㉂>Ar{#PhY X6D~=!fmm(;PX[};U6V} m%G=~Ӧm oC$m}&QrͬM+oY[#A{-@L-V| V[[3$m$G}`֟#H#m9[C7kSH۶MF_9[jA#A{-@jLM]9YQ#SCX9`j"ec.@~1yo !J!1ޡg;mg"=;+wA{->J L@)6TJXY[[`.$ɆQY`i5YK! F r>wݨnFؖMlnL^]:R.ˑ  ʛ s-DkkM>M(VA,0nvŭԂ#٨*$HJ#my[CdKG ͼ%fIGz4H܀P@ovJuk adOlCt A(2o#Hz1RZ>-OѶkX$ҵu.#AA|Bj?QuL-ij95Ek#=~Ӧk ]Ck}fИQ>5niHDH>"Z ]g3٭֘ZmQn1-dðOu9-$HJ#m[tecD%n(ݶR$uYҎT -6۩ lbo-iqȺQaHՌ"R5Đ{[A\eoV]B~(f1 GߚN<%.]QԴ({tͻ{{"7RRʖiˠvJ6GG4FkjF;MhFlACGG6nAbM"f gHj9|:SіQR b&pBV~ڦү>Hߒ9Pro Yow"Z ee5jdD1 W5 ˊ)JtIA&HGLۑC0)2 5orrunʤCDeƞ)(dH܎(Hܲ7Z@eldl^{S6K SXNs'Z;ih/wkN-OdBvbq)$o Y&o`{Q5ws(DFOAV- } ''KX!ADGH< 8^">R{TEv,xs[5wT2Kr %H֎$HrI* ׹AZA,"ߟ-[[P9Vܘ 17@H8%!71g`*Y;q70%  -HjpYdm~ {ˀ ɀ"l%ߟ [TYm)#R%QVRԭM.e;Q7m7.jnYd, H2@h2^}'0yoGxfuIjyC$=tn}Nn5A%b 7bjYO;M⪅dqYadY* %2 h2 ^,`?N1Aai|~oXYYIj|@"G>D[|+ѕ0dqYdY - w& wu3ߟ HCqP(JTmZ"{+?oSHi۪)o[AࠦuGFL¬Peeo {!#f썩7J˲$<7Z,+Z{@vuEŁqX!`U]o}_ڌTTIˍ"ώ!?4@egќ*h6Vx֕m'hh1rhF1XlEhfAvtA𑋡, ?TA?Ӈؼ m"b ~{Dc.DB!|d#h47 E!H9U)F!E QA!&[xKւ=iF8c67 G;: GCXnABGs0Rzl\)N:"e hF8bc.frRCRl dhZ4'"MД*L{51·KDEާRRܻҴ#!QZH$q,qKKj՞H(*f07҄r_ Y[-'v=Mp)\4LJ2yeܛi]ᢄCWD%EV89mXNtj,vm@$7H*p9),%2 2h(mfppbJ7iM$R`LB$B&w$,{tL}lr M#%a(a ].+j5J Hd? hz7, kkM>elNYHdpcPwܣcp)gLy$цch&b(AҖFN; ˠR 8?c ߺDܿe %Ex¸m8I6ˬA)G(37A׶8RJ2(KL(9|iq!P@9X 9H6)T@tɐ)U6Fٸ{>a7D9v7ytlm`|mN!ZDI"n̖ZZLYAMR A\DM$jG$j0dz$cHhU 6Il$E5uDH)IaKSκBgTJq&yQYaYZ&9Y.cዡ!KqFm"disvlRMlWL6l[(ii3eeG*KHiD$jG$j0dZ-+$j""$j3AMޠJƆJ[WP+yZb4ǒv2d5P<@<-,OA:Xiˣ9> Oky؟ &Z]U|an4 tt)GѰ|oZPNdV(9UqL%KF2"tBGqOZ%娇z#Hz?]+9;p͚~:3OY_%굖t<:ζ,m`~OtI8Avt0Z&9YHMUjF0HjSG6F"fhaƟedQfk [n"[[1cIw9ʀr4h4SNMi$gYF#9K/(6MnaRqdeҬr"+;#dc '"),_g N\ !74u\&b—ٟ&cH#4bvY;GwڣJX\lcwbpk{F w/ Gˈ%XcC>LHn`-+j z7o$2I4J=I+ K@n~N8mK!2HDmY45V>p-[x %! ҦJh M M0N0[N8m& Z#2 9> m[޴MnNIl-%zx^wܣck k`c.6bIxv^w##i0d ,W:ف,| -Cfzh!YfmO$TXi h6K>beU9ʔ&P2Jٖ(+A,X't # 6Smd9Y4 -+Ĭt312T ÒvWN-И-i;: 9[j@1'[j9tL@Jl % l 2hCCQnQn[#J6'1_#bUXmS)er9G5[^<=|Xa.3p32PЃޭ "C)!z(a Sˀ"9>`lYk0]Ssy̦I$0hVxO%UR\Xe$MfF$jk_HJ1dHހ 2c Ё2eκ?1cl׾ZRQFIdY4]$N=hEB)־ đӦi*=T-` ?e fjlrcn"HRBv,p,ȚIxIԖbDIH1 9 Y[Kđzxڢv ؁$?K!;Ⱦݦ#\4Qe`Ԭ3`ji#ÝiFs$+Mjm -@Dmk?4AIN;fX%H2h%Y[V[f)?\t~8M;hj2CV*Kɏ)3wU (W &R;=T-|`8? I3E{V{i3$k O2,,R>rkFs$OĨ[C˼AV% S #'W3$H2Rh%b[Vi[f9ctmf@ԃTkJqXN#Gjdkk_ &@f&j,3h Cˀ?-ֽə.ڈE+Xi!.p$i` qQ%^'Qj{l1Ҫ}%T #x!9[ Z4$n ! n[_xRƤqbNyqh1>|, gӸo-1eVR}%HrnYqZ@!pVQ=h\"ҳ0GGϒS 0h"' 7[HɌ,x 3AlۿnTDHĴwG"Eb4GH> &55DK,(R) U #MTI[AI۲2ODaҶRCdr+i,vT[dE[ySM+aI;Ir di:3h CˀXO$#~ 9[3Y%邸=DFQ1 =XiCw'AT&5%6H2,(R7`P-'6Y3$mjL- [ wŽMlϒiJ6h=ː),N"mXR$1eKy7bCdnk_ 2PrUE$H2 Rh"XX½-]4VyܗECMK:Jڬ%DY\k+$hU  rҡ5 Ar=Gs|xZ_4n{m;'ņN:K:p\ 3!OHjjFs$ԩ Քy3!k9q-d s$9> s[Vƽ)9Hƽ\ #5-i7S 0ak67 ][HH& 9m22 ؁F2e޺?9+"|F^NR8Ҭ}U3t=:,qܦbh M[M0INuZ+΋2F9>&rVm f(B̅iZmTXDV(¡:i 'H־$h91d 2`rhL6v=2!ͻ!Sil8!,EzVt3=4RRb xdWX]Z.!2`}, JN;QL~̈!,}Ya6Y=lc?#L}\5#%S %[V))KZRf1[ #0lk?@j8N>e$gDEs -(]սBE[ A "Db41m7< ][JeݕF;9 [AM=isEd klYjR/ƒ JapNHaswУ#l)1Q LٓFԸ)RMi ck$5E-঴hAemb!aWk*f&qU+fBri9q 29> '[V9+b^8KI0vV!22d+Fs,d,aZ 'Y!9~؁,'n6'[6e@dAEcQœ,Iڞ$ܶ߂ ;#m*ف$.F۳5/0zk?hNYa%K5H2. &"ODaơ IL"#i(<-TJAq(#j{5O}$iCw##h0̠,vh!rB\&gqVྴѶ.X:ߕJL|\5CEٚ,0־CeE"؁-``? 0"Taf}Z>-YP j9YhaE2Q[M}#T˄ T3Y!yYZFZ_4 euYZFgmەjV)ыsY19ˊ^D~V8c$8lk?ZN8mrFsұ h ɖqoN&HaN{ m_׳d2p!]e K'VCQD5Pq%X83f ?ĎӦf,, ; lC3ZEw N,DƛbmV8VPrLmh q_A6Y7Dϒ|s$gq!b@sG,".m"gKW eֳ&PNLq6[AʖH0=2#`mu?c+-(JZ{&jg&q]SUCbz%0=ZZN9iJX"H2F9> W[V(RiUp)M5ڑA*; kQ!k*A2` @ZN9m?ekJEcMLpbhBTʸ WXU4{tM-|f:$B9! R*)\V49lMe@dqd@6gpV?ƚt&HiUwicCVRVŽ.1A7,; lE&R{RVCclZk[j &l)<-`x?# eeܟ.CQ̪ҢG@z$m7e* HwͱZRl|qV6q]'~ ]ˉMdAcY[0AYۂhX*]-$U#m(IcJ_Yzއ2X[/TC6tFzۏBmk_If"($)Am v c!-#`'kOXafiJ6! Y:SmbZH!Z odp3[ Շqk_ ߲bLIK\$\4p=Eܟ|/zL{j"܄4iFބT* %$JY)8]2cEV~ppI-`.#`/ ޟS/]U4V+3kDbdm}0w(?ܳ[.l+ڪpbR%`Pr}rY%H2%l+"=sۼ弉 0-ThXq] cK9{Y _Kt[&4F4TQlV)`KLr%'[?rk)[X[F,X۲`Ʀ 3 ^N^>DV?4y4J!s5QJ}!ZˉX @E0ڲ2ta085JҟhN.֧[ڊO$XdDu$ж(/ [N儒fp\@A2A\_4 La24"n攤G_[UӔdYJAq.ZiG~G2aǩVlYXȌuRz-2Ñ:i_P,11J)ؒYuLA  '׭-}H2@R1z)?3]`^U(- \)@% ol٬:" [@Cݾ ` )ؘn Qһ2lIBh T#vI[)9q)~l\ =V Hw R??`k]KP0z=M~_>|/__|yp?ˋo~ m<>}j>/'Onx|zzj^?+_3N9/.M_;o{\Oӻ-^?||;={cno٧뛧сɧ=`\/|uY:^ͥ_曇g{M}{/!\_?/ϳ}t;}!\6|2ɫNnݹO{e549}>{/݄ytmOE2nTiB+O7Im|P67pV E9/ @ c iNBZ 4H7o&rN(ZeXn2TPfdE;I"^F)dw$6 |n8NCryec'76`ծhNJ P+PY!jE j2G('ĭNdmo/ĭfAv" - qDqvSčI^8VD6[@)ulVNH|!q?=TJ!U9bn#%թ*bf& d\txn]cbs{@ R REVJ> A:JGNNiz2(?Sp2n4`)Jl&"lU:rXo\Cυϓ^Z(/5\+"YeEs, ˀ%Hɀ'Hzspt%^54WD4D2Yܺc $Du/*j[l#X#q_sj}' I[@-'v=ĭ ]ˀ"H9>x-a"M"G @ٴ"e#Y~4Rad#e`:~5`2aMZAV- kˉ#t!AHGs|0Aⶬ7S$qKHX-Y>r$m+Gs$|,Z imUb]#g3,5)!mMDӦm q%H9> q[$i 'IT$ekG֗e=S{׉*Q]/d0k/*![N89m&ˢ R x?c Rh9p-%&(d Hm5}Ն#Aq3)f: ޤdPѾn!dpGGdp9- &H2 h)HE &p"A8m*iE5\ ;vB,!r]HU%GsNKO.fłqR̠ee8 Yv*QA9]V@9٪ #%Tpn|wv30I79w[w${pW/>ݼ~~qԛCY%'R?K_Xߩb}W ۯ~_鷿%?_={/?<7?|sp̬o\?=ӧO'?뛫 w?f^ӻåӑ/~쏷Ggz:y.rwgMG/܁7<;Lz7$qկoă/nxs0:~y3xƳS^翾yM{]_W/ã[7o`S;?>krznL>+}v~Ǜ>^=Lw>, v?H|>m+_7^O4Ds&3 dB)9iu=ne#F+gh BIc'u8@(1"$ ŷn&JsEF)_pkb*\D_Sv?_O?TP7rA¸{8 #C+X⊜Q}b—ED85;_] vD$=w_0fOMJ/J?Ȇ_߻Kx{s'/?>iU}" _ F/£PsTøvZ_Q/srfLÿ/;d̸칰 w:i/gqw #<<%0.H(T|ݷ}18*߿tDQ`|7罡3bÄu@0Sp>#(wj`0L֣2 && z2AUFk\|thA?WgFt?":+aKZ7`O8+Љkm[ POA@Acg̕ES̾QkQ!:]km35Ob]?"|`9E"D/h~*>p"ܠ9. @YXˌw֣+H1~Pr@31YE6s~Q\Ί*ù:_~^>@HeZ^aلqIb eĭy$%DWO;̓ӏӀ)d(*KO.~/%|ޓOϟFN92^Ąu;/`k/U#X$~NCcԇu1L?BIlg9.7VgX6a<&ycc嬡 Cr_X0~}0V,z"ݳ-oSExI8Gg>;en'4>eAtz0/$2tQFX @﯃}FD<nK?&h nz* gph7约_mg4l´./M;Cϛv?HxVW-!{dbUr/X8aa`qD+R$̯A]L8&Aݼ>٦$`3ZAlVws!̠L2Ql=f7~w)_XgAaEYX4rn5N@wJv)P+X dg)`v0oՄPg!]1 MgQ3LI0?F^geF@\8 T{xm0vp`6[!"wǃSP a0Im7 `v;˻WQ:f;,``%F9uۑj tESMA8Lg""Ot^X$'ajt7$讀,WIsExO_T8aqU$0(1Lu@W܌c1 aiOft#C<ͱ t>ep  FEp73A NP= 8,gANAf΁8b`m 3U3Pp9 E,!g2ݱ:@:ܩb !8c@`e` h2c뜣vT} w=\,ATb9!xDF/&, c"Q7Q7O: xdX! !3[~6Bp$Iݡ0U_ F|p?`OS/;1wEf"Rs%o'.|.!jwQht9* 8xt?`^B#L@ T$`{G>&s|p=Wo O7N@^)+H OUkh. pPk9T "ќ@åU% G/?5A/4XAA?HXbAP TF<MHHm |<7w"x \kRcfŃFA  Z0x#agEd AjD* vr;zL; `x9k09jzUvL.#_Á /Z#c8%Rs ]h 5w ,.1F+98 881^]⭆eh2 a]H ƹ_B>4MG)k@!U6$RrBt̏fv|I$a"rGg:~@3$߄K¢ǀ6 +($?’G:D7\w`pTYc/ HCw^:1LӁ%5,aDp W׈4Ͻ~ SX{a P X`d | 0pM  @<ཱི~> whpx#i t L0k^(pQ7>; R nЩ]j4^{ќ¥bcfϲgxA1 ^̰RDp@ېHPx `B:6v6cB-~T |#xS e(0 #*,`9 aۑG6gds9 3<xG8A A(-;B^PQ zq˅PEu @ }'A9 `Q0W&zأѧB2xa3?LbQqvC`*ﻚcuM wouqS:cYL✈ؤSLd`&`W.]@I}z23u5yH\l2v5/\//\QV)Zacj]$q[;$&$u>VXX<I4[,ю46|A@! />HȐ % 5NW(y+>(Cd9!W ) o&i 9`&h#LtrOithExUʖ}8ۅ4j˸X'AgR$zxFmtN2[f6 .HuU;bΕHYjYtsWLY60%Mgu'F̗ٙ/B_ɚ2gj.N1 rn4g?v¤|<0$=pQj~~DS m,SwQ ` t޳`) VdvQchͮw U_H֌>+u y XLqBHT tp[e`^I\r0AJ[Gҿ}}%S(sVmstŹ?l.$T-%uޝ+e)9N-%gy.gHB75i~LHn fbX0,qA91|Fcc_`|ߝ;ODd%f+:KIksMFM;L{~F=/YePpPps]kv-[8%bDJKs4S1S0o6㌉&s: AHP#:K x,qf9ɸ! ")ߣxs\dr"ά%>ΩsBN ?C*,qUaJ`A b@`O}x^1`-yS娫ZxcUV(q r{B&CRbW`kBn0q0bh4MfA<hL䆹]`53:lu{vں.>OyEOחO?=]O^icn0Py+_}Ogn·gj1ypd,qflׯHܥT-jZOoUHo;:bn1p#v;/Nո( b~K|Y|0|>5l&!C9^q>'7w?ӏ|s\Q"Ns\%|"~S(y5H}R_~HLv~'źB_>=\=xwBw(oX ~̮˩rf$,+^pr.^t5UQ]Y }vd;\MdP޷j%o阪Ewn'D:CUgb J^|VUKDRev~[\0Z"eԧZ) \vDv$L0sjc>jnUoqjARQZ4ٹeNIZfgSP$ox%ȹ aLrЪFNӑZΎ56$,H=.'a\ڼ 9}N87^e*NS5TXzM3Viñ+훢6D4?Djkǻ|/ ҋyǻZ%ݍ+1Q/]6Kc]|HJZ.vIo+i>|Ixg IVSK3ej:afXc+aqP"} l_p!dAS>O_pCbfCԧ!ɢO$2k'9}DŽ yI$, )!AN$fҧaw0D}|y-d>u ه>}Qz)ǧwq$>u 9kCa4 f/0> 3Чq3Yֻnd'(YFfna&|?SŸ̳ޙc.r`-?߬p@/.Y(~M$zKġK&mp0 q3 >~f|9$|g~{}ꐿ?۟.>:Iܮo%yMW7OCɥWNԯ]9yk׺) >D^y5̦m<6ޔ RǓm$,:$K2(I/u>_[\iPV L휺8%N-pBMtc l:3LF7LUqN˒tN7 2d,HF oqdqѴ@F0̌uʏ'&%iYvMi䅾#!#X+i.YqOلU 3; UaV{h7q7u`H Bup a %UF ev8ANx|0ANx(IP/89UNh)}(*@a1M8|0|py |P惢~& .p%,=[^3$oכt] ,%$3AGfACscBTq͕, ZF.ə:93Y߻f\T#JTEv0K䩆pPr u#vmT46P@ {nk+@aMX0A:8B[ 8kY1dɶ' Y@A^x|8A^x0^ț)[О*/[ 5%ƻ8oRt} d3$@*$#%Cw#c0yZ ,y.|7!%Lİݺcwu!yOӇi0O[Vl-REؙM5$MffGfYT˯h{h +h267v'HOO$j$jd2'iwGyYP\ָJ'D*aӪr dJq61jșȊHЎp+Gt+a!t  A#iPlFi`&Sf -Dahn?n,-KH溴īaV8Eg5 =rScPoI, _W*0EJ$ozYEYq@Ȫ2(c~8qC|~#c w~me,_8 -Kps+F` رUjNQfi6;g?"W;%d6_VX˄Yqz8(ܵ8Se7Fs ZҋBU^YjzZzڝT+|&"P)@Z;ZqgS5g>Hh1 ZL6p9Ϳs]_㏟ ,u8hʓ^F=̧{,]n\gCR|9D:tߊh2$ Gk)]I^065fXyh9,V' - [)ZL-{ep6ZMcy1VX#zB/@Y>\&4aҪ? 9aFr@*WVn4%x̚se"s0.A?_ п]Mv-f=`&WGP}?{Y<Fp?FS%tiBui?DFwc։eLC049>JSnES5>G k)YIem٨A̎+0im=K)ziq{R%n}\C`s=uá -=uQ6z' 8E`h5 g["f[pnŎPKk4ASFܒdnuivIl=8%u o94jn˸inya3V&$uvw1|$(clAqm߮|AΖFS']_Rw&B(ϣSZ\TH.lFlU~ϊ-I*G9(wq1o:ˤge V!)\ti%u3^Ǯ*y~P|<邜vf%4ui2o'h!9;U9I+N\U%Hl/U_WV  ig#h*PwgOKĖfJP'$Vtv$V3t}q;KSg֒&(0I+HZMh̼+ݗ<>CK)Cu6uNJL[hY$:q+2B[d7NjBV:1ܵ1*!',zLLs`-*qw!hC&A A+ZMvt-hB03i9Z#D*1H-LyY yYp@ZMy@Ks^MJFϩJ.}ƣ,ɑͦ`(V49c6ZF[qjסԱgV~`BJ[Lx='9i@h5W _iKtլQ0;≖w[p(.ض)teq'y@׊sV!]_xG lLbW˭n$! -B ji40Nj诘cm$M'JiuG gW͐#lMɶf˘j3x㖞$J>@Hnr+"${L, R!;S;Dm5ŮhhM&6oz0NI5UҲ+JJX55,1 #pUHGUpn[!?+4\}=L\gEjwrl|BsϡhJt|+)܀fY4!J`iAMĐn5mlLT* ϾZE՞p54%bߥ?wGm-*kj|6ZIU+Z%tkj˭=!gA j)Q{%8֘!:O9^ ܩm)ix̍n+:@ӊcV j_lDoV$mgy? iI椺)!\8{7ڛ&>(ܟsp45WϵÕh\'P4Aǚ$U p5b9ޏi4Y&1އҌm BQ&MF㨞"~gPK͚8DauV[ LED-Matrix.fzPK;fritzing-app-0.9.3b/sketches/core/LED-Matrix_simple.fzz000066400000000000000000000553051272363601300230250ustar00rootroot00000000000000PKq5DX;ZLED-Matrix_simple.fzmsȑ.}a!B`^3ndz|P-ՒVRwOωoUf,x!UmL ̪ۏŻǛ_oz|%Yb$\$ݬ>s/.oox۶}7Wzxo~;["utowoWoOn\|gEcx<]]>\>\uk?V iN/?>.>~|Z_n.]>|(wk[?^\n$}v [k9䱿FYwoWGcRmlw.V:{,{ʷwW&7iOv]w=?\G?.?|zb=6o\\?.~}w>m:Po.`،ؒ9/n/C#\~uaea:1*?[|J cL-~K4c(cqr*$[;9-yjlW|U#ԇ~ kscq2_1+|p;ޭ,\VtpWhQ\zsߟux09vwz-ZwR r"u03P@k"q"%0mhMɜH }9(E5<ֽS6~/u7]<ڗY4fV/6}g%'S3 ]؎u0;V Z 3(9ZY6 ~*0Md<9@ɉB/c]ĨM$}"1s_ jAiGm"D*d%'R#3 UԦR'3SYS5qԺlGT>xz|ϧŧۏ?ʝx1U[gB%k<m:*~Uo׿?|~_]^..Yӿ܍j{?_z_-Ζ%_]\|wJ=[]???|uqL_jyVǛ۳߾͵s}!Hu̡N1X>=]-\ַ|m}헯~?.~5~tJ1櫻_*x(pniWᗯ~Pv#4,y_kLwӯN/şVǿVW׋7_q3.V{Vx+w0Wawg_/ m_W*qb}_KZ}xxzmSvY^="ӷ=&\Lyϗ,]rԿ˷K;?|{`g!oܳ=Rp+Kja~cy]Ob?g;o&Rպy55:6w|k}w?a7`"?{xIMO?/gT/d_e\jLds ٹns pq?&B~mkZZIukowZ} <*αmV}{eϔQx4\V?Xhll^ )%nJ{4Xu])n2T?USj˄۔ $*30(SjokЙꂊL%plTڣlQBʓSR[Mi_S)RiTMئdA&8 cJR5Ƕ%=eR'7Ty:U[j[xt[jp :\ u-RNǶ%5H<[;MgK2%%61,eRrNԺMijoLy}zӲ} < #;iL@Sk<1saΓ'IO7R!iHuBO'sHue j^oϠAҧ72 3JyM=jbD4k#ce񯕽Qӕ?M6JTWOt{%rWRTm{T65$PnCGΦй"InCGNI"B%oCǍ;?vRd#G]GN =R|GɸQGN=mG}QGN|UJKDSP-ou1A_*ifG*/i:Ѭ:=+H(-֧q,t^ZVІ'ڭ/\ rݙ\6K"RzyZFtyyK:vݒNmwH~i+SPo-?[FݍvQXEؖNvoό Dsw/ZZK8 ~~e WIH@׻߸ij-]jwpsk#D"g˫/lbWеAbmYj RRߤam"(T;\5@ 쳀òz 0Dv\& G>`졳].h:Eע'@ h bH!e"GA#cEIODbNޛA1ɖl)dkɳ,HRJƘsgڕ6@X]۝vQ ͓v m.ƾ[IN˶ PΎU` )M620RLN `qv:3- sҶZ A2_K_= sDZ֧X:ad].R{!ɮbt (C(Y"\q#zބ&fG缈Ԥ` 8#̴&hIۇǘ+zk]fd &1vuWG$ZQ2|w =}5Q9e}j7*<$|]McEUJ @@īfc"xʫr^Y[y^\.E^5S^N;y)/l+^YWhlQ&]TbruV۪$L" st dH#H2tk)DG*]L+nV}#L[bHs9i5zUI-*OaKȶ2em!Mg[Db] p,JŜ}"c->*钭Ȓ)KȺ4 |-hW]& >1ڥ}-y!yVZ}E{hb|4 GRc"ØJňBƜ V%QǣaaU%. qīQ#\ZӐ h%P*\=QWyUv o1C]3PvyQtV+M2{U%B5ǡxmWi/8'mRm~I/o5b#Үމ.h1M_nH)_uT`arrċ9iC"1&R-Ao-EW2UiRٮɮHE[h-"ۺN]^hXtl9P]dl&XG-"@1yiH-\QtE-CkQ˵2Kdvqzwi,?>!Y]Ю"#aOv!wrO$\lEfpiWϐ$ tqm`Ex# kċ9i;@ˌJ VU^XKe2Q#~a$]M$jgNȖV" ׋4<:\튰q|kȘİf~A#j=UԘ;% ǘ+yiˤ 5KtӢWWid^u]b~E5$ ]S2~+5"j" TȮ& WLqQMqŋ9iE3$[٨dː [K\pEj^{ Wc." M^XXuߖUZ.H|4?"WHE's!dbIQVJl-IP*ǹtݲ.L.ꎍv5@& >1ڥE\ֵ>Ra[!lW˜vu.Q4A7 tqa p{.#\It.F24^.Ee$xTn3j]K%^ݫ#kݭ'Qu=ewJ{M $FLwkp^KadxZrSd^'@]0C[W詢 )F9$`|T `&`KeY0nR}f+Whn44'm@k?ș;Ƀ|W]]Db>ޛuQYA`9o5̋&`I.*1&c.H%4嫥+I]\DJ{R}55X4`ȆCS :]^'ZZ75$Ih|4?"gQM]M.h1'm» ƘKʻ8aޥu1gjD+5uuߥ,&XDA'ƺtUクvj1t֖ J|u5,&xGzӄ l/=TIt*F2m]m°1& S0AU^RY~d kky3PvdD?b|K-J0klɓ`WR>B)S^DbjCB[V" )E5[zT%i-vOH%5eJLbd:_7l4sҶ?9d`)-DR$[h#W_$_4c>6qáX"̴/1'ma`L#21EԗeDncr1,L~uȾDwS<)fNٯNu˘;m7:w5*rWu$Ԙ> 6tπo_M 3Pv\20M00#ʹ~&s V<{6:ra4ᘓjǘ [ɽrNNMI0]mp g}HD{h|4@GL E"F6F02^z,,!)˿`~Hv:ӫBDTj#rfr+-$Xg܈ATQ;$ C卼ITAc#cR|TҠ^:H S$+:4U r)Vm7:ՈO=iXsHJ'|Mt]D;+':Hc }`Dc>#& HQHŚDBƜ O9cedyXC ,ծ,|5ƥIĥfN)NtijEe,rHOA#!CITAc#cR/>*iP/n?R'c J ȍ,ո}0rInGИ[ɳLKOm-ea,$̀h:.0fhBƜKπ1iXʨҰ1 ΧtD('$$S:&\ :$/v5[K:)%:'#j=U Ó`TPcz 6ty_ 3PvDQvb^ D0#L|j fͪa;lD=p'o5!=ɬy7曋"׫ϘErg31IN/#˳|;P/ʶxɼM𘏦yAz3y=1'mzjTV)ʆNyxT!6y!(SV@$R;yH|+8RzK#=U )="1=_@]3Pv|yQ2Ӳ SY8(n(:@&4I.N`4VFgvƶ#&+lM̀Dh:/tO#r&R`41cNP㥧̨DLQ%b )0mzD$R';%[$X·<0 }Ml$] U! F2g^0CKG(z1ewJA1W:*TגgYK\:i$^nmE'DN^`ռ;IeWY銕3]Db>ޟsg\j(^Iۇ-"1&Fe[ZZ 2y.r]L4a<,4v[Nt{̷.21ߒYZ[Tl+nHmhzE(f[ w|ZTD9iE1Z\+˵F%S'g.ar36F(:(2hۙ4ZɧRb;8ܪ&\9EF97ڮ!B:&xMĘx+ԨgGDΎ]!5ΡHE+*8J(Sk1ergƢ䴼 4+VlATA;# 42FSŌ(SnFRp#]FycCʒ\qo iW)ar@^tf iLvM=&rZL,[iiCְ2rΌv#(F\;'MK2=Ec'OȴtXcIXHƪE5A(0\ߡ{5OesRi2:1&/#GB&Y&4ӜDRDH?,X~\.OF~`Nv+cl@lf?lAn6 ( )t(1Q6K{$5)sv /=S6`Fyꈕ4 ZCƶRAQ:RZhMJϖ5aS4v*[\l-#+HbƬ+e ȑ3fXh+lĬYCg̚uG#GCa Mڹ3J U Qq,.5N^/s+cl8lQ؂ls#?*kFRPblxDtI"Mk5#sv /Is]^a6Ж"1_Vӕ eT=,I3b3&˨ ǘ$,yd:q2Fs3*"S, 6yHdc$h 2'm; &ˆ9422Mxse{$GYZ21'mse٨4M24m+b3vD oYC$$2Fњ^W'l5'M/#r|hp Z6Сё$1gVӕ)sfD 9<I7bެ3͈LjLq 'AȔ5!&&,5T"7H2&É R`HʚDD}}X$fA*fBKꗘC Y"=6#(t'1m6C{:$5Ϛ9i;5s4&I$HV*Eap&vbV& D(ZBf~wgeUY3 (H:iYh+ĬYMW&̚Q#g@tO$2&V܈YL5cqWIIɄY9He1/g\󬄈YƝfdqۏiC&)NfUy#NByY&H'뼭Zdբ`rNߓ"emY2J19~HcMA!gY{rrKE [imsY 9 hX5\4pN6UKBJ$pGF@ Q,$9>: XhZE1'mA$X&\)vX&)O0BEkz]O4}@$:*N+:Xh+?avޥTcj2=ɚD[B&ѱȘljEC&IηS6#FRe4w fD+#f%wbi٧bH#\2_]q ֨]HZv@1렋S^uD&$:@v&I1*82&y PDrhK&[t="kuU-|Ts!I2"NŪ0$E'뾱bd<ИcTBZ@C:"kmEk̴1"2"S#~bD|{-H%f1mHuJavTsc~sҶFwG p0_.XXX-w9QLu<9z\DJ"Wk;# sҶgG4&YIRDܟ(E뭠`jWD(Z-" Nu G EvV-ڎ$q}YMW&\_FB($rCtO7$R&vyLG!cR|Tj jƙVb֛\VB%PGP X K;G#9kF{+怕iAEl[,Z"OE# )t(16O{$5O9i;Dl\I0蕥KeƒLro$7ZIwzG(Z.Ug>9,Z=(;yz'd;<8P:n1$fj2eȢ~H$gMh 6FC$gz\r&H\%:v*dqZU ?,YPwdgܢHКCV2x. \SM"SG)t(16S{:%5G sv!/=fƥjU_q˾L1Q'.o3`5Z\6-&Vu{>94Z2gEL]#;mGʄy4*B"v!5GL5tf4>.7S4NIT|7 DJ*5ܬQ-l(zfv{ۆ}mJrFlU͙hzofF>&gf} ̬I֌(I3*02&;3MUETbxU'[;9vuG(Z<MUBJfA$D9MhPG#H̛teJvF?HBtO/$&VL͈Șlh7kYf3m=#1̴(aO 3A3XFmjdAR.?+hZg#MI )t(qD$:@&|IgΘdi)]h?CE~(:?I>.1]QYP9k YhoBFCgD)=HǚDc4cN>$qF@FdE Jaj$TF"-+`f׉Df(ڟesVXdfqnV7̢ 5۹4^ܡS㻫7}ԬQ$Axa "Ѵ4aԘwBtO'$&(~Iۺۄ1dLfef9 f[p(@r1[,HIs(NMv DcӘC Xӏg`Dc>`19 pGd`MQȏQXX&s'ԗXp~_6frkr~R-R?Y]~X}{GW "̕*Ns{H YƤXˇU>~pz|wş/o.?7qd;(~P |ZIau[QYŒ<}ʺ lYTns{m&WKdZDtïcrN~~r{ Hdܝv(승32P}5o[@#~ͥ\x}8;Qm]K0! z3'ASuq~$=/)84#ĭUJs()\&L@SwSRg+xT.PB3<@) ߿L}1q#3y+[;I\RO;rT*Sytތƅ>yJA9ϓLhnpgZH?U?ֹQF,u#:U" nd \~0@s `_ZdB`<+kp? @83 QtFD&kLUk4:ߴq =d]&St-܍&Gp37<N,sҹ#Nva*2 )o [sP`׹NKomY|z¯U`B|Y4${ , b{-]G,sSCti-Y^`.˲ھWvD]Q"˲h2rӉQ Xxg`6a1aO:Q5ܛo׮S4H1eZN0mb46mmk79A_$_{;vMOK;e9Z')9dN+beW#ko݆#g:9)e02uיrZ5Bߺ?#'e 30 =Dc4c.g7˵9@AXxl7 1 dQ5F5tZ;I"xLThZ?YŭS?` \uup/Eo <(I;Fx?ܨW~nJ&f pv̅3j]µkWI];DGϹvK ^m"Op uS=0DS, `QW0erq$ǠE@WpSL8?)m~ 0.[r{;`;c9z\ז|YD0!BGLGFo<le[1TP0AS3@SOՀf(50 !:5:aHYvj&9 }qp;օ689)`9mR3fsL\̚'0а0eޑ0d&n能v{ʯm+sn%۷ZֽA"."s^l^L.`˚l61p:݂¼ ͺ2`q: g]xC0c`@W@g@3lg |!{ x0RƓ 6.0Z6kRrp* 1f8 oa(Ne;zÈ xW#qG37h3RlI06$:3ew£D'55R`;U>6A g{joBV/$3/FW? |ph(#9ݼU7PA8ɢX 2!'`BxP X:a(`֠JA4 j$]Wqls p42PN==l_nP  N }u%X8zV4yOsa=,a:S Dxr>SL$-m\$t [Ãff`U0Ac`h  E|*!a` AZm%;~jnWBNۿD,pzgvf*G7hA\V##2~tӼFb?";I`/L!ިЇFC‘>Fܠ4^_St9 *vS|x/hyt?76Z%FI0|aS(ƻz$$@^B 7PJaO#%mޮ4h32`)ܓaoѧ# cGAo;4#X(UC4d$pM x|m9Oh_KіYơhmtցh +ׁRWZS 1 DCj ѥzk`"i斤Al{~x^H3 |(d9z)bf,#/<.9ƮXiw&a(z(r B$bNM%)|L9=DnՔL1*P$}YV c$}rP` @CDAa4{#aifZTn4; 6T9tݗ@2ĺp~ sBAA'$BokCB^_/@Z~h*oa` -!p^iKAnb\bЛCh\$szegü#4,um1,p&" 1oayn!Dl#&2O` J`:"/~}0$O> 쾁&qN72ðAcGAw_0&4 : :^o-܎40dx"OA t@tHS8$Q`NT0 `燄AUEbdAգ(~17^Mg7 L7^['x D-{z'"-.9Y/vhC4v1^!FtZη`>_4 AneϋPϘBf  :%o{.'frl)m1b DCoߛza\baEy4p!QqfrLeE'{?rs)8P 8){)qQgI֯ L}wN9<&On{`q$@ 0yìL^6D!(8%RL ⴈ@A8locrK #2{k,dшʄ4&1Ǒ_(Ð(7rqa: hYPݞ>068E$sEI0aa=Eh,jβOh$8{!t8uck6GW /_TOס'Q'70߃fù)- JR7.Wjq\Ma8h]qމ ovLk:~nd5ɂ`YKafL a,uwWa攉y~ o?KK-&-9IJ?| 0EzCjs?+n Us0I`w.5$8:^.nf]1DܚuMx{0%<5d)SW[} .M-c0ǑO޲˽"ċtGǫp9.ӿ.~7w/?|zw޸|Z|SbeZyxJ; X,u%kmxfd%'>7Ww{D]!3m#3 >:ɼrwlN_[Z$^_Y1O(ZweKK}7w?Wަo ʦ7_=bޕeXqU-Itfв`Ӽao/?~X|M.`\߂ۺ ?_HYO]4W.[,S6nz~17H5 $;w1NEvC6iBB2B;Sx_Yo;d%)U;MgVꅘՔ̊:\άtEcә~f%|`V'8Iy!f%'6+1H)*}!f5sqB8sUtfڷtufq8V94JMe%]k ٠5)9'2qNe{&a`g<˃%zgs+}cY͑NPa˗ZeYjv=J A-"cܯkptoQ.WvZtJS~@KÇ;A[x@- FՖ8A[&XjKT- jik[mj]V[z @-S+mib\"[44J9qz4t- ]jKT4gKCeDmiX)vO94igV[Zz4t~-MQ0\Fsn_}uzxz2FqhpF&n +_|dQة>0nsk?or_R%]+a`U&7y~|Z} ۷?>VyҸG\] Xs&HԾb~LYg-/'9 pG#[p䝸2.8¹kGPJI/.~"!* 1LC$z\hh@%yHdKaJ *F(zڍEAT5@GM#o>1%MǨt 8x(!Q5С FVl30ǜ} cl\2 cTP$<]- X}<1ұLSǹlߐz'z r)D{KHI 2Bre!MRWrY-^HhʈBǜ]]i>.3h03βݍp׭qb!QtBFi< !U<ԘXW@VH2%DPVUȂX(p_M/1'mRH=`f\`"e[p!ɭI^T`9<9i9knhjXG{E*V/ڗhzīN4ͅiab[Q \٥|Ztiǜ]4]rbJnT^]C tXV>r9i$XJ'O y0v\te>q-KjIuN(r&VԈ:3iڋ vH\TӠZb 꼸>F2_'2U7főgՋN~Đhڴ^J19|kkyԐ$mƌJ`F$be^4acNڮ<.tLL`bDTgɋC"MONxJ(g#QU;F'@5'MjFHE[#&j2aR nLa c85G̍5tf$,4vLǦ K XTd`({,Hpɰ=`<TF2 F48z"l`ٯEjmwfM[D0cL˷UL aޫ3F(ڟuMU..M$5=Ƚ)aQ蘏^T`;pdxNnAC7$׃5'bDcN>QX:0M)]'BP؆㚩0]^(4lD?Qa]YX=ˆ"|4jcW6PXZIt3D:$ڟE9i$!BDzrpj)}L YddTYZ_)Z4P,.#a9Q옏@[s DMO1Y=FB@,:@ &JIp>*1&|)Ηp s^Um~ԯQ`=sR/(HÐo_h;@j2a: t9R݄i@F^VmHCg&dT dDNCcFi.wKᛳ+! 26P*-ylc^X>K"+vc#̉DAo(vG&##QjX@:_$wW:ҽji8 5'mOȠǘOKr:lkmuxHEʷ%Z4=Q@cнnؑ+ێtA9bڭ+ݨ@Q{ỲZ f$tdaMsn 4F?$` 3"LXT fw K־u^  Xh[G" *& #a#ﵙQ` }SnDc>;>~ʍ BMv X[QKE D }u%#۫s#Rs97*1"s5&D(t;ns%jW%ySHEkz91" 89 yD"kmŏʔ7"1=DθVu9{8tDf#\Yh+r[CgͿ1ظkrA<ݭW}Vf!XA:WG(:ޓ4w㝓ȿ5 _4vT;~ \IXh'ib|4wV nL*[U9 RoVV>Yd^e s0*1&2/I~L\D_PDg"$_HEnצ,l8FdY##85CH¼ȾI6<6M؄ij50umNy .jto~fw"+CT]0HIӇ0>"1&w\̀r<Ze:,E(Z&2>ʌa@_D!$2 㣂MGg|Ji%|&v3>*1 ݟ:kw 6|3="g{ GW/!te-{D|OK{=yK^h;BEЙiq! 9O/S$|gY6)Lo(IKI2Lq$5Β&rGWYA9T[Kpy5b ,bYeKċ9iJ"1&2J;;qa I첫xLHb(9Q/U#nEvu]D޵ՕxUؘ1ˆqU2܆|5&_TAcNVEJ1&yI[FwIJ O.M! `%[wlQtDD+>1%:Rj@Ej"j梉t|P}H2PTV6Gnwf\MИؠPyD|+kڶīQt5ѶaOxȁ* [f^4c>Ԙ~j-pG9;mdix1W}n?KY0#j_`8n;aOyU@8Ou5BGkȺDEhz{X_@͈'.&|5/0$sv cLޕ˻8ޕe(d[(vFHEX׵G"*%&69=~'nD {iM䘏NxQAU Ȃ >49QHxQ`/Axma Wp2^WȼEHxMhc^UGBMκBGhHD{gh|4o}x bEƫs&&2^4QcNn?rIK!^[1ED U`yWh%i.4'm,  LvD ڗh:F;dx!fΥ6R{w#OƜ fCa &MyO%w/kP ܫQ?jx5Ec{5jjCM` D $[HD-1M^ޣ KYUиY+4vhI?# MsT0cLRK4 R_Eh#5:&pO@UlvRΑ5 /QiK܎I Z&)#bN! T¶DoeMmjDP9Nv5ž(bGS/"hqF֌X9HiVhMƜ}"cR-=.JiPU!+W5GƵәIDL>1ƥW !yȿD:ŏhZow:.,k @bT+u}u%#_H4m]m1"Rl\_R5F@bCFsҶ;&O_&2L&hGdZUvLKJk5RHʢP-1'mҩj4VeVB B #g\1 XhT1[IN5Q]A$WXh;^5] vX핱$-V{!xlj/F^ qWCg&-A"c25*//.XbeT$a,ќ}kEc6=s5^E/y8za X~G2&z9iE4Y<Y[v:`Y7,QtZC9il:IudN$Aw!MOncT&b:%2d< xY č9ikquqAkXu֏tQt&[4ѭkPWhCh|4sh#G?k 9,D9Uиazф9i;Kq9%,Zb$SXpr,#YXh8:͋/;bd,%y?(AHDߓcy){ʋrg̭ {l8L1 I9(P'FVES>l׿PKq5DX;ZLED-Matrix_simple.fzPKBmZfritzing-app-0.9.3b/sketches/core/Loop.fzz000066400000000000000000000546431272363601300205230ustar00rootroot00000000000000PKb8D53CVLoop.fzrH&8j;];f"KVeugUwlL[uu)SJTJӔZۇ:pD@ EMD@~u}}B3r2ߞ//o߿>Oo,/>,_d]FhDF]ʓ"}~9qb|2p}v9|qq2Y_'o&'yw}d'O'*3F*u~s2_Z>WR,Ь2onVk73;gʐKޝd]**8cb~fN~:Yݾp~3ؓIf~wo^rbl}uv;ݷgُ˳'pg:jT{$dZ^\r{6{\e>}g˟&SEٍg?/?^Y.n࿹L__׹D^ߞLVWO'۟_aX~r)d=l}}i޾;s>_:|X/ݍϾ_.x?ݟ D?: l.eQ&3m?,f'?5_p{1uc;{55gwg|y?}r.+OLS :e[]7QU7_uŏϗ7ɯOx& QMJfJɗ'Sg^geʥʟVzqq޽*U?zy63geywv~̉b[c+t{;?w*Q'ݳ&őGw'pWQRMcMӈ3nZUG fY*NZ"l:Pa)uP(1D)g8_C}zyd}qXX3#-$FtP"QaHE+u3'>$fX"G*>j>ßE,$b,Ϩ Q0ιz\ㄸ+xHcXne |T0bv8,]R1y8g*&9LL5Q12k5h^H M;,m(.U?o$t>Al\Հ $" X"0df( aCoT-#"`ό5BAs-H<;H<,a<$ʌKҘKDlGL>4!"'더2Tb_Qjsox_þ0\2*DξΔnK_6O zL͔G"WT7Z@x WXFľJ̔W ?#e_̾kAR^hvx[LLZ  S 1O z˔GHX>%a$LL48˸4a^2!r`;,LYwޔ ) :DI3WG ;*<'31S_) ˿Ϳ /_$(*R_-YFSXXhZM%a;,lT6]2#)*!Qf4I y 7TdJY%*S bafHĬ;H &!1&{&fQ9bfqMuJF"1;ĬNLhXE%u#/މ 5c 9b\^ȰB3r$Zz$ZӲ#4މSQIc#h1PMK-ۢez>)[O1bIeKe5ɲ}+#Q:G u!+Y}%ńg9%Uf]|B)aɂL̬f4-Jf©_cZf2SFpC&Cg 21XZh=ރҝNJܒQy!ç 2QѴjvx>밎Jq{ "ԕ=KP8|aw-{ئ#K4G` Nj`&tW$P0%"{+f:Ϥth,2yHcN>os0Fjz` F2Ii$ $"V7:-"c+GhFźlRRbTf^ltp 4CGbKb\7p nݯ~XO_|ӟN?az:;[Vdd6pz}j6Sg~ݛf!l>_g <_~TL8v~_~lr{v3W/w+,aLE- _]{jرݝNד3tuOW`&w''nye_M.L/y՟:||x?a_oW׿_ɻOWWWOy wܜݿwo+-0$d_kӯ'56v^Mnd1XM ݗbܹM~/ׯ&&WnUױ׷|6Abvی4*MHW9׷"3T+Y)g]q5fJaθ'b/EOXa%.<1ܢ+OGt-?s= t߼m5_sy>8]_}_spw1>ܡ3^A;s(O&>kt{ݷ|b~_ggh4~xȤ \z1ny}~z7z~SLYƈǿ7=>=)~xu/ͨu5 >iqXÞh#gdgS5&y癉x5|ԋLFY2srOu>^-!2/U&\go[o]9~y;R8cLoB@N %w&zu?_&A4͝=A~y7/YEc5Ju}^Dmұ"QmyqD$|68ҌFwovuP2d _FY8/8g§ՙ_\tùO7,<#!ĉ: 0!R,TLr˦ҙoƵ~VʞmeKp)>׌0oZ7GȌ`<_ Qi&l#Rfػh'Z:&<mj4kS<Um{{ޏ. TFy kف2~y<(i.TJ **K-KG}_gx.z7&:ϼ"6q-X۾1v3Go<9)sCsR8t/' 8՜0)'0@jwG_=2kڮڤ'mDI!w~אQ&DfxxF&3yRl)fW3|@*R>&|laran4?~/"! SB``=.GnbI7Ȕ/bC)S#gu~FǭpޮM4J"7V8;&Mw#i5#VG,@*u 4lEFޥi]f<,cdBBކVb dc,UR3Ig[npYhS}i6JM^3GT|ӹ/kmFǠY+74ƄJkiCY-|<=pΘ4Ru)>{ ^2Nj\SLRp*鈭mhEow[s3{b+<#˴ե{epJYmERFRن{43\:"b)|K*218{;welnxeė>5+N&Μ/x!&13 <&a { 3vez42ɻz >cDmD8 a f7wN8 I.9I*Œ\q$ "Cs3t,"?5([:ski9 Tk}Jqyi"@qW1bdžʼnÒu3~t.x=3#=33ȭ# jf (I,>\[T)0K5W c?܌= r^r91F I yL*9DΆB##,379S7S~71c3 ęa]9  +"a zG3KΌGH>ڕY@Y7bə3'fH;H,'gJ}۹'3eGά̔ nhJ}+%3Rg;eegԤYU1Ibg`ȋeg8 'vVᨱ##rٙ藝/av&8X嘊"> Cy/H)3W%v%$n0䥲3S%g*9sᨡ##r{V?зZp 9Sg?Q'rV3Nq~%groYXJDκhADB#ѳ%3ys-~m=KK!]|~~;>>f$bǢ8wq"J:nTIϯ4yl(f[x38agN2IQ*vufG~Bܧ .#zƾf8Rt3| H>9hg(_mSFژ{v0ّ)V_n\Qڰ +~$lUhf=/l} Pvt ȼ檼K4TH3=iyW#r#uCi/׶#n)6hfP[ L')˴Rͧ(?`g-ܩ"7,ᯖ-"2#„c4-/~ kYf ~~~3>u[{TcngL;S=Ghgpm'0Hv"*#yܤ67*ePLLQ%#-Di=h?ցv]R>]HrSic|B?jhE4h13"I2OfDԈU#LȌ yjjDUS#xRR#WkY78b7R e+:1xd+dIaf+i-{88Wr:lƘ\*d 3T\E2RHuJtf;Ivr_6='|G/ ̻!Tb})6J%l;.=V4Ƌʔ8CرNqF:*%Nt[*@:{?aՆIPl%S4QŠ'rcBRAAR(1dEo@b薇JFyϤpdFO2X,jd1Ed=jrG,b1Z#jT!9Q(v}/i`qE?2ڱ+石vGvLZ Eq/c9S]I͘$~jX͇໮kHL*/#N*.(u̷s4vׄun96pnm4wcMỒ1+.F~+nօ# QºBh=lKŬ#FcTaJ] mxdbuau[ w)=òu>f\J{X-,:$硢?zL u>a]h=l@s #16@ѫy)<߃D>l#z ^%7HGO}:q#NavnawdNn0N 3[?ƺ16ČߨĢrgϹXd={ai!4׆&ausaXdwUX=暰(=ln M]v,.w%Xl^7E&#Gz>l4i Mg$uV7W~E&1}E.L]v+.wW=:R{u^qzE^ɸKb+.0wuW=ڮ%>ʮdK˺"+.Q+Bu9W6n7su &VruL\;>+m G]LJjWH{\qj /1h.O梮=8{̅]J|\+N uyWl>=ln.N#Fc3cq|ؘ\\x=ln.Nc.]~+N uWl>8|ͅ_)Kbq)axsXl2({E`Q|e`aD4E'akvs)Xlw1X=rH%>ln.R, }mof׷a?/_-|{ߝ^W~rc>gmowTof9/hR}ltO7^*qRn3ᩭs>3,x9R+gy8wgXƘ%|XF' )3' &y7z4S;⟒Zn!uF)g3ogo7Q}3d):{~WIQ\ mv&ϱ~=&rU NٱRA%9(rǀH%J7xًT#cĶ+z CƎb .VWyƹǹU+| Eޘ:&AHoS#ixϔ",r %)A(rZ788#FrȰ !9ٽIQ,gUz >|4sݠG ?'A#H_eĖ,ja9=UsZ!ӗ:eFe: ; XA<NhcW\y=6L8aGgty& !=1OlK}(|U ?i2I- :RIhyT8b`6b$*|LFlq2$-XSB&Уq!Ǝȧ<L14@z~CgYOJO{Q=;Q=:kOE?]E" ,*|D"& Y_0L<aGWu&̇!qP}z核 `3ϩ׏OieC)uTfN?}bFsmV7P|HQ[5-@A~ JF8( ?M28Czq J%뗂rRe~'k3TgJ8hyom24ClCIguG?C^*?OBͬ<ѳ-aFUH:$3TB ͽ(]I_xqxfCդ<23N54R5I+7+gN \PV~  cYL'+ fjQp@#ԩ0j\ ) @҃h(87'[JiSUU.Kiqg`9A*y,dt ).,왔j9_K͆rO*93S֓i|qA0:u SH&Uy+fӝhaLk|JTgVl\Ǔ粪h"KjgḐD=3C)sXM2E{/=䡕~UmH X#H~1dΔ%S<3&e+/8gR8r/2J|5FաJJ9Lix$ _8ztu v )U@嫜ajϻ`|̗eW5{W`RiZ4ǀ{g׽Đ嫸ʟ ?)'&uF)^^LJ/G^uxm6T)Wr'&J3$J/Nj2C9)=`s0R L v3nQ(Ns]OHqX&ۦw{8ׯrLd te78&FD:^IYWnG˃aja٪ʴL:DZ'ĐLS# %Q3J5G(.=/Z!e(lf`3rR>(pa$rX!6!)oV3Njq-2n\l_R2*uޣ4#Zm StKn߹CY8CR 6 L))k*?af y{n$%vü_=7zč9 )_\eMKQ:~k!T$잍$II݌"u /:#.Bf3KeʬmK3FH;Iџ EZgİPg"3-mm2*,o k[Rq`MK)m;e1CjqKbpKͪBfLmMR͗jqFbnAGJjI5/gL:pgHU |XdAbiծ;My5pe`tUB+#!Q`%npEzHRv H,; IrKz{n+=. 4S_BKj36eh*ώIQ3vR؞r>g4o \N⇕HgSnK"2ekcXNU2OL}.cDF39x{txC)/tĕom: Hľ~hk<걐%xW8v"M.dyKs0Ƅ&3GW7Uh۔d^{E0u./){>lj>-$:l$֖]ReϼMDMq_l63iohP/{q^yԣcl)V3K>W3gY92*HꡒiӲ8!Sk3.VB{Ju4)7GƜJsޤwqX1&UE3ECde9F*-h<Zd1X{ڍ [LȒZRZI>IjGjt!Aύ.i웸qڄTdZ327lFG "ȚK d )-CKZ #$|%AH0RV۞ЌW<3UKt l ㅖDֺBnֺֺÐDJCpzNI6,~oר"[2 Mn1,:m; "9q8%d+5$ UEArVmVDZkS[Q##- 6CgloƊmrNJ y2Ѵђi5dS",lwA$ظ4W%:"hgxى$/:֋OkiKICR~һW-nl[~ l[#8KMuMKA>#ֈȔn[5xn-rxIFW³D %3 ʋ'!eD;_R2NIυFLDڃ^21*P'2Yv'2#IckQfh<% dV}MO at!2b;?sG PQ<rZ4d¹L}0I4RDzFozGK/]T⻩@=t_7Kg.lV 6c<#Ma KŅAM.p(y>}hڨ+B,s|24Ib`6ŽqЪ"X3BA~Ea[Z<,w_lqlmMܰV :ݟWWj꼪׷=狋:XTXy5j~}pv;^z}x_/O_@~}er{v3}'N&ܟl:7`O[#j c*xfМ䙒֪>pkz|>o:Ywεc˧UZ`"fܤ&켥_ͯ_+eNrU[n^f1tzy+5ߞ//2rj߾_P?}o~_n~o&_Mg7ٷ?};8}6߾|u^߽>}}OgwW站;qOt?9b}UfO&XO~X,>фYSw_Odh&D2Çńd2VnIvazw?L~/2L2)b6|9s$0:|e L8PťaY`B&FNowcǟΨSW=7o%b>}wvYN'5w ܹ)3P{~svgS5G~BO.Bvx^^=w- =%[wHz*3Eݻ#|pGXw!'}̩?En*JsNFN}{hB(5ksnjpSu#WQ~D' c9jqNNZX)#)Kmؕȸ` '"gN'()2_-̽yZ+/">qJfVs_ԿU_9>%,cƀD8Hw=NqS(iE~S*OjF8%1L /=3<ó1| oG݋q7W܄V+΀ٳNr_s>( Ps?b/T>-}߲eENέ8uS^SJie$N?"A$rJ?U}>9sڇ?`rÆ 8O1~|a?S#3Z f0)&a  ?TTD{*a~* "]>g'JxD`S82G??% && 0RThts (B 4-\hx"P?aI# Np:ӔTYMO7WA geZ^`6q\ͽ@ݱ7# +|\yzqlAȑ?9LEoVj?,Ź>/C#ENxY H]($"/U?%+hl@}Xi!#Ėm`Lsa0-a8'ycYc!QkHF#S"Eѹ(̙~BA@(0tp/]7 Jr<F%<}<܈7N)9?9͹k{];[ђk7w ]PK^զlB>q&Ө{ H o%qDxq)x1He˜('Ly}٦-4oX/{7`;e|=ʊcM>_q~W +]ohJȒG#7 {[c t Dn SjL,V2d h00Y(64 ѩ SϪSbi`݋1.thBC-m\f p Oǃ( HSw})L1  cp޼kzt`4̖Lީ=y;RMn{i}0H -@}Bb‚&*.ek:N1+М|4 X9YCRqRqBS/J3Tf]q3ÀkOBB3bhcФ3y;+5.P\ @ƻ$0HR=9̀ab#mL 6CR`6 q]ecu842pOH(\wV7@, UⴤWDH} Is@[j 栝L~x h:a` JAT j$]WQ LpQ p(T2mw㞁O7dH1K 5s8ł(hrh06+Ļ0WQ0Y)D;Së{zZUOCɩ4 %ahAä,\T3GZo*`9h `%7(wb-V2o y{'N_3ovvYpv$e82 <)oK$&+8#w" Q 3|(pB!H<! hkt9*Pw5nLDTpxԄ+^V|!p~0L_r")7 YFN(.üyRJSa bF R5 F} } 0B0r$}SrB$!&,pA`: c~Fz[Ѷ鈐cl84m1-"DKުѕi] D7b7.) DWnvT`i:n` ZI pHktēh7EoIG J= `;v)CnD2162qZhx素6t%u)U``C<\٩ S`,kS:$.t:Շ$rqx9V6PyKrE!0e y$2c nN F,:}|Fƻr](@9 IrFpׅcHLV!  @D@p6D_Q 9kߞyc.N=P;bs7M5 `i C^L"֔U1G!'La.:?p\+Ex;(4U9:v Q54 t1 8~ N yѐ'[ -@a >SpHar<#1M;ՠ9uscʦbi?SN _!jp]SKщ#]p39c*"]/H"Rm$|}ޖ(3]Jfca0*R y#avAO(2E&U=:E|s)1q_YU~ ^&6PrBVV=Ť9L/0JLw\ (KP_k@/4zA r^aE$UT4bU n@Hc!GD0v6:͝ƨ@ "S b AQ \H!3 ^ 0ȶ`a EX6r@?zXv`<םVG810 \m$2Dw|/_$Fp5!WpItV{^t.HЇ@fi { q 92x&m|xu= fhZx}}KI1¸KS\b¯8$p«F"A1aC Cfs4E% Dt-~@C0WI! 쿁[֧s0aʀd9a@PA4cp2ݡS@0XmA̺ٷ1FB$)`iFnS"(tФ] 5(~17㛨_xm/Z \s* G0#{jxDnγYb} Ƀhzn FІ Ł"509Gb`,>0S.p{ٜ Va#<(ͻ,"0)2K#S" qbv M$ eE 2\`:碠 — 2  cp! w79Hk>jPnp3x3!U=AA-a[8W)s8 7G"hyae0&hq l}lzZHa Gtz[)oW#vT})7K- aN 8` W@;6)Y>(SlX6ݘJKCd +U+}OKJQndg۲O ],DB~n .zOb 7:"NWbq\U8]q-yԘj 1}b(@Nմ╡PYat?}G>|4PZ[RF^n԰N*TGr#T6Be$$TPy!T8\ p2ށN&~+yt@p݈l-)S8B4of8pI"@W7$Ȁ pbTa6#Ab& kX 0Ninaq*\Cc ( dޖx)>k "nƳ >-JD0Ed`4 HQP68k\uJ?΃d51>ɚ&KddF:`c en,'ϸE+c\ZBBn{t̯÷s,_8<˜o.TGpЗP5G-FJI P:f) pVxp)x y}[q7mGa2Y}b*wQhy!0c1$*$:8Nq2V0Ũ4\$L$p(|{uۧѧAp*EQ" l)EYYu.b.ЀG%uZ+e*nN˓fy/ov{EŴ .1a`#opRr }Jx !L\?D0H``+paNȃ+[X#{㱚c-CM86q[ W\[ßS-gڹ:ۇٷ?˻/[|:[r5_,zA17l=vwηb>t>Ryp׷gW:1qflwۃ}ڒ Kޖs?O8t@ӹ>G;,1ʛTkp\l1"'5Ãj>AF,!h iJ{MC_G?.?GhXOCn__m|_VgRcϧT9S}ho(f7zlTN", 3fz{jۭ ?v*11Jد9Hx0ooVY{ou{j3on9תZ;kajcy&4VZl}6}ec׬$f_f?>:]{p'n-mg8bՖX`צ*Sl~tv̪$FJv'ĥF6*1R)q݁ɩW1R̈́.6-\3K?f];9u&<{ 7 FKwLgG?` ~3~m åEUƫ|p>_%(~Pe//?v,xLg7j1C;)N1g_/3S/+!}\_X}1؟jrsXP/?uw:_.~#pW\ޝ_@ƻ3'i~e=Â$ ]]2,ag?}Ʋ'`E +oBV.9#wXuXz*yv(NDd\9Z@HL,qQ Ʒ5F`ԗ>?ĸ+({ @6|/NA$]Nlۂ|:$ke3Fc{i]btU%cm/,+.;s-MxMn0^i<1q$T☘["܊37.{fn,~a9aeTmlwB3!JZ&o4G__A,L*G] ؒиs kTDh4\Jǩh(æaa!-2]ZSGs4񹨑$h:@r9OsrBv1Y',\s.|ϻ)(c{5a&鑦XH~Xn0^b7˨>cʔaL4b=+NT#J]e<[3EGdif? NCxN|8 W):ZiAjq]f{DMۆTElUv!{ѩt 7fE3}¢ kBS~*E&J,07drhZBe^),֡,%Keuci^63YJQ3Ҕdn&Z}f%z>hudrhڇĊwpV]b{[x|:m֎$VVаn%m8haUvioi۠ɋìgTf.Sz+ )֖F<=迪-FFNŖkmT7}/,{4qkDK4d"c]0yn,> }H_R d]"iId*`W=z2\ *dBCJ#6 ;m(>ug"u):Zi}mq]b{DMۆM^vr+}%H`ikl*)oH6Ӿ!Ys?PK'8DY]CLoop.inoKo0slh )mcArP,Qa7 ߻@RrDߴJK'MH+촲 5J 9*mFcܑnEu,::[ׅ)XKUȳЬBȍ4V&az 2'78uUFwIY̸h_7Ө_0 M&OX*qV-YNTK>LpkVNQe%?..t)AsuKJ夢ƹ~6MI(R;m(oS&rTR:ipdr×1x:|&2mv(s?:b9Yaq q)r~IȼhCqԏ7ς+.?BtxFlrGooF(h|S1q7*"y _\|9r~_ PKb8D53CVLoop.fzPK'8DY]CWLoop.inoPKk"Yfritzing-app-0.9.3b/sketches/core/Melody.fzz000066400000000000000000000271131272363601300210330ustar00rootroot00000000000000PKr5D?e+b$ Melody.fz}ksWlV.>巎eˉlYN>@pI K*=^@$(P=؝~2tvt>xq<=yS(B  aA6ҍlzre7hlTOFNK9=z1d'B Ny58hi|v]5O6Jl X G`9~Zy_L|9-\~3M*z(OG_jWTͼqoj@}4=??0o08kŷM~Gi%orEbC5gjvZ-ȣPȥQd1B =Ldziv*%שT5$m!zkzK}_<1_'{ ZI$\U _brO B#RIoεl a na˓;wo/% S:?M_JR3.|_Gbv>V`\ .Vݜߤ8ɑMdpx~bBe=hd/e!)#>P:,0͟lGG0eōE' dp$K{]E ~=Q7nB|Tr' g(s7!~ˑN^#MT_Cv'uAkoƺϛ쯛6\N \nyĺҮ@X,I:XB;X(e!l$rU^~nHD?x<\WԌO 墟a ^(k 0B[܄—Fuh׹S[q*G?7h磁uɢ}&9\hRu4)NE|OOPi !(۠C (-K!7?E[T)ѧ24jS¯p2)^ (|',OQ%RC L"CGTUȐѹ)5ոG$MIz[")͠U^C̽-tOTQ奨rۨrn* sL_Nj~w~zEqᗃ`\=߽{Wl~[^Ĉb9'˯-]rtrALw|xDMt .&UpRpX笗Mލ*_Deg*7}.=}nn%pv!HI~<(Wgt׸g,{fS3Ϧ`5ݜ'i=g○ΖʥIu\0.J2?̆(bx8_iO5y-Ϊ_| Ӯ' .lv[Yll>,B]jZͣNOjԕ>g_MGlo:d9Ұմ6ĦCQ§ lqp|i jTaДΚD+I#MʊF'޸)nY*ZSn&>|ҥt\;ϛ)N4έض/,&Ք 7/ƸU$u }L|ڞ->U-~ZAA** T`UV~nHYgy<\ސx_y,T |0)|К.X”Z1r5hʯeA5˪v<4d^c2)#(ulul"-a+ V2Z=-_%lOf>oPAħ))(BCe{  V=HOQvUEauP&?y-ħlRA2e7&uu ǥi](eByu (^bl^ Fd5"BҜJpF!W*EaB'&DjL Osd766 =,#we/$<͑Y8GqјF;SrJ|j*BY Ȟ"RQ~2"/?"ZN LGЭn˽MLbUol)~)u:&v>!{-?Vto||D|i:|_[o (gWI"t;sUwaמ@ȃω;01[~8e11מD\:\ջWOfM#czd]FTtdr] &D}(Q\~4ϗEPK+M)yE /FyNx좚/Ƴd!ʪpF<|yOw)^9Hjqqz#ϳql,~LKISe1l~?M,Wڊmg?>5tRDO(|e)t}}}R0~BԬϡy״Lݬi?:6>,_JU釚*zgw?˲ }<>}NEɬvɌXyRxb8\:^/]I7Ɍ%JK|֌.U&]7u iO~r愔]QQk/ _KB"]Tސ.n$xE~L(;PT!++[ tGޡ "8{g,,;"󹹙~PC=WB̈սH{RBt"2z.6msu|D؜_9/#*u {H| ڪw-ߍ&w2X,Яx/`:槃}k9;EjH[C͇U-,)28}䓩7Wy |ܚisA9.UTKGtV+mNMd8:* ǔE|Bl{{IoQX>C}m'M@&__u4~a[x.mycJ"[񘘽FD6/On~//g/M'₲PO@:FimeSumï% oG%җW/QJ,>v K]";y|V5OyČ[xlG}î{șqO;D1 ltp4}Z !|wȧ(]Wu,̾8eNq_Ҏ0=}{M]Ōi͟*IիyxIQƛϯUErCkLD^O'X_ܵ4|5|~M1paH MBiឆ]f?a3{oqp_®`-[u]SuǨkO!3zob.IV?mQ{ڧk[T9Sx[Ǡ+BUtuQ}-u7k>^~OOu`v9ގɤ\o gGn۹~|7{ˣ&hwovG&~O >\xvv]>0W>'.HDdХQ^=\ElY vk~\~W?Ⱦ?~ 쫫 8E?VSbN[6P7xg}&|<9Z-x[}D`3]`WokpR凃_'>[=Ru\Y_܄B8SA4rH:Ҳ'\[DaLe,SgF.Uka;ə&%X+XZRW*0t;XJQ rZ,OKnu(8QRU##Ԋ#:7JDzzȔ2P]˴2(vKZz{\)Wr|.8ai_wFFM"/{QZQQ&PLF5 ԁ+ TTJ2 R9vA<'}'cH+ #]Px=*|[TJwOTB`T/llϪ ^3e#C C[@gb GTdYh\}f1j2Rw2L:ﭐ Se"5`DTB(TCJw|s'ݧjv&〙"ўC}V(,%XnK]tkщwctlt3S#hI:fBIA|r&DQdPobRF#IaP%` TU̲J:nΟ"K *m Ҳ~:@{QDqL]p"ieT`"h{m-kF^Lj)iO7քg0W@A@@xkZ l լ NH*8E $R%#1jLwѫ1K<` eS.ǘ;1jys'm!7i9?dBW$H|°,YDhr_ ^+2e!48/Fj#)f+rh8x⌅$##`RZ|Y]+¸ XTm^ۏQ'@s- JO[l.+Bv,6O0- LW --Y"/r7)`dTKBI.xJý~>ɵo(#nuI@.J&Do2!/C{XZMEBqo+!v@ 3]Mqq.$?QF lӆ1γ쭄xu5DŽIIՔclre^5czpG1]v<z4㥉]c |4 Dm S֘1c dd j~BA CG $dbtE@S,]ph!g)& G%m2 `(# ͢80cd%%|9+@Bfb̻$[+{-[`e INmO;f%㨮 "+Ҧ؂'%/KV VvvYKߵf,]su6LaC,uc V*Yh^1[D"ݐUk9pһdPHY:h’i.9@VÑ5R"3bFPj"& s cQ Ƹ$Y˔Lc{CAM1hʥհXKt P[%FK̃j͟b(\GUCJLS;* xfTY&!)_dCniI&#I˳1s=d;ѩaM:@ *){JiV,ۢjM-E{҅Aޙ^EXAV;z>g2(fNvd{R.Y[&@i8 F?X^j~p$7x$hPI&XGd鐨z 7bExː:ǝҪg 9& lL*98InbJ<2%H)e OȺ89#RMl=ޜ_ z>e郏A;cq+2 I w %'|ZkIװ\xxAr /7f9*gR,eK@] d}nT+m9=" E:MǸ24;`&- QƮA6# cg[0`1-jly|i O5֝+LzQeҙAc8.I;2uhN D 1&sW8OX]|صN+g#xX&HzU688 $l3.%$EKK,P jQR{H" B?bvmFR+R0Ot H ;@aGl HB9T6K*pNVkBQbntK#,a{a"r x 7P~ | ĞBID7Q/[pl^X4e:c3"_⅁N5Ry9!1#(GXvפ&eҌ)g1R$p)xN.ZI1APPۮDoG+7-)牊^ "q?in LbS?-j[? jYQFyX014'rk %2oۼ $+cIXaCTHJo l>tʽKc%U(Oyj3 M'kr8xLQa6 luocrl@f Y| 6܃tPЂ`2wG9lPF Pd=nB0iXlNddﳞ0#8 2qHJ SP hpWZ$"\VLXBH` 0}֝q+UܼT<\%_u%iv,*E|h\ޱcp `ǰ#!~ޱcɎ3BykŰ$`7.$)7]ְaI/1$ٰWX4|M+o UÕxE1ZѰVi0V횞 4;HP,n@zM_# ZH" 9FHƒzs0ss\W$*,\3V_::[p\mpsE[nzVW]Mm)/'0҈Ncf0,brvrҘ–)C4waX*¯QH`3oڵ,ޏi]=E[zRuw._|CU_<ύ|5λtDj 7!ĖG "Ӆ< %{>yׁ/{_mo=O6`+oh};'w7QvݚoV܃oK/j\(F߃ߘȲW2 LQqijG)}2.S q'烓AFzpןCD;6E,;PKr5DX Melody.inoSMo0 Wi DGiu@Eadʆ%Uqt`G=RY;?COB',;tuqMh?v眰ߔ@8t*P2ȯOF/~̚yZa7zKB{PKr5D?e+b$ Melody.fzPKr5DX +Melody.inoPKo-fritzing-app-0.9.3b/sketches/core/MidiDrumKit.fzz000066400000000000000000000734621272363601300217740ustar00rootroot00000000000000PK֛8D]XOp MidiDrumKit.fzr#Ǖ}GwX{G_ZqXݲey|QIM$8$Z-y Nf]*p*,znTV"_o~zx~[>yE~5Y<^-o߼wW?|X^_LnVؿ}_#{ 7==޾؟///ūo&~sǻM_>-_MVwśW2kɟ~eO?^ٿڗÛWi-zx5zJ &!Wr@ʈ|z^>^&Owv,sRB_]xHWz1ׯ/x~xq~IW^|>g}zz7_gY~WL_fļ5]`ɍJ (Y}{w}ÿ߭=ݗtK,/ŏwOb8c~_gs?o.v~^Mnz}X=|X~8j2}n~W'W;o`әx߳/}?_,oZ_䟽WbwxyqztsY_SY /7_7o^erbmwܟ<^/~WWڮb}߬WtOgW8i*ﮧKʏu;hE|w==N^Wq'w{߻s+2׿/gv{mtrY٩添3~bX=<Ů $0vد)JX CV2DgWr|~<TInBFs,[5շ0f#aQ½`ٝf5`0+h|sZ?j{Ԭ_Aadfӵݣd'҉Ā$ ? +d(4`~,TZ$4Qh$[m$wZTƄXBV'[[žSp#& fEanle"p%% 'yXIr5W Y,~Y>|17K]?J#NXOG;_duyɞHE r>kr߾O92S6vي?dbL{!3Cݟ6&,<qo*0 \>]&T/i _@3K^[~oǯt#R_N o%,'o8"z05Nw_gFOm|i$Ktr-{k$p̪|C&d ^gKu$m2ձo<6.eqZi{)4YsW3V%~WV1c(\#ӟZ8%|){ 4I@íS'f {gjx'j/Gw,Ƅ#0݉n2^~oPz*l?kÅ %ZBQJNaD])JVӭߗB˷2We-H2 8] B[-4B 0e WɘJ\o1| }.!T?Q.gPy>8/b8z!+kiehYPU f%AұWDV3vyُ,K 7"9UGy̗tt袒07](p5`nbRm (] Nq;vb~p;%l!zfyY:541z`=:U ak~ڧl HGoyBCz+:ݭBgܓf0O{ S5{□s/c?ZKGt9Dntל:wviz}<[/OkI((q]s0xbO9s??ed`'2w?}ͦ;러yP/ڟWv1/5+f?}|V3N@"wDWh}6O1}ȝ썳}9LM^hA@m= w([)A;q7D6B#+K4*!f]: QAȴds~jd*"jۏQ5$j@ 9P%JBi b7/PwST[Z)UP}s 0]XB-lPH.T %F>>@#&Wj.\U!,zsbmA,s}8?<]}͟"6C 2iٍ#Uw#E堧~gy/xV=)O@uTq7.($,C<O„n nEݸ|-#^Q*dJxW(n{@`I={YܥW{yW&wsvi{)^\I" Vtbw0Q% ǢMM؜i1lGZ҈'{DFlړ}/Ϣik$9՜CDC^щ AX:5=]x?EޔMȒzO)a#fl B'5"Sv="c6>М,PPX;?ό*+y%bx]kt65Ӱrֶ| $2: a}p̿vn!i4]DvtϷ?tmdBEQǃ#"s5 G"P/ ?e]9|{+2w tY݂+!)IcK%b׬b>WH64bB䵷1FqyTWMWc_ROY]^0!JܔDhDt>t&&~+H|w*C<+N䋫O.8oܝ9Nn,1Q()zRy+hiH`#}Ѫ`&{~ΒNNҁzJ6ZKڳfΫAMxvW1AIMikW8D[g͚ĤHS>k_w2x*1y\o^?&(2)AzTth+"'%C%犒<ĔʡDZQY'n(vtvbzK@>p@DI"rI#1.0ME4TKk/ڈ"x]b!ӒW.zz)Þl-Kc& k Z' ZOzT:Vf7OJ72F1Z)rx:%b $*:֣nZtn,rtL K#jtevW"kQ ܟ<} VwOEPNEQښ=IOI|g>6Vئ!lmsNPTl)1őp+Zkc5HҮH)Tw/a-e@5hl\lHc T#'#)jǞ$(jhɥȵ*}YC` e*!%b ڵ(?J.}.YaKil( Z ڔ`P|X"*uQmPan;`Zm46%mRzaQDZn5"Xc8J%M9cэ5{$&Gէ10K l"֣@d:`Gb{OhJ&e` 7L$tT8,(EcعB8la-TD4-Oݵ eGP$GlzԶ#% m[ &]IjK|jMM a{3Mƕ@T6&a<16klDEp[m- f5L p-^qMA2"Z(Z*V5lZ#"@\(dHE)FDzħ ںLH3a[ 2Qudg}M8КZUrfרDSN0,殺$#j1ՁLTC=;*VE.zcMhv@A)HsoV6]xiXeAaqQ[jRvVQ&déD[˜,\xrXg{ȗkve& kjnՕ5[k  ZClw-# k ,!T&UMtl"+'"*ʲ:ܙF$WwaA' >Dl{tV я܈,R2ߊ(E턧 =}J _?d*&QS-ŭX64# = T@{+bFYGGF#P`bS=O#"h-Q]d[*&'eEB Ksĵ?QG߀e,%,GD.0KYlV$-vGYEJycL妻cJjjX@+WN*Њۀ[348-jq$ Yj|֓\t6 ` 0G\*.`MG"j]T]"ԛo+VG;,iPrⅵ(796=6~tY92Xa"j9bW/bܶ[ rT,!rT;YO hݶmeƵ ] DAF7 cݤo0B! U(!ҫ\l<$eLV!RM:k~\ a1DB7ծ(;>X}U;[="j otpSg(-}GK.&@4%c+ xEj\Z$jD IDl{t!=c/*E`'cORp$n` m@HtQ ȤV"(pY. D]'ɪb_JƖ;hpr~}ג,kI kT+S M#-EZO r.5 fxKQCVA4E:ؾVYkcel_QVJBj|y30F>E1#]Vyk(;󌶵 -E@GA.!Ev%l̒1CE?ժ !iE؞e҈+EqX-/xD4u\wn5ؓ5>ra1uǰz`_\^]T ᱝe?L|)!Y Jd/o@t:P/(@ttf[]\ V T!-0Pa%JgO6Nv a2oXԥF9逗xScX(оFx%Q&sijc}->VV?~}ɟy;\fgogw߽;SӧOCw{g/绫^]vq#qopmMv'ś~7b[-W_IY<}>ߓOVUby_,;WyE_^<\brc=[gwד_>+ry*zm׏zt|z~Z-ʯ7/adj~y]_Lܮ_ڃ6ǻo}3yawla2|xmeᯖAVj0x;3jf/O?+>=/jʧi<}rw5/<ۜ/[Og^JWl|3|y]_Ͼ6+?[{|[is_KRN{!OˇD 2~_:*9ha!q<]GM-b]\PLk#j|u f1FlBېYoe]R!µ9ш+!kQm>-ޭAe#@!"œ:o#-$SE : ۶Ǟ(+qۏ\t䲖%[ܧ۟\\䟮V/;k|-s 3s]Vluo_x~ch1<߭~{}};V`8J9b}rG;'m}y8qy.֟=Y<2 Nq^ #GXbv}iUf0y $3!Y Ii.|5շ0F\%r{1DHfOsQgGiE2 93^u_"XO˻1r◻AO0%HQBs`JWN<ݗV1;,?!gLv+NӓpZpIcYQ1GI H1B)c¸GIaJS_I(Å̎uшQ>Wcl8\ndfZ;V%3l)ŋL=SYdXr8j?.]ӣcx#h}7d6>P;K| \"mCs7%QE.m=\m{g>Iܶ;O#wJ?~N<՞q`7s. S}uz$9]#/]y< p55-'uʙުR:^'ix%j>H.}]|V]3IQSv5#sj<|Yn@sIұn1u“5sDDʵC XMθNcF dI`YMz '@TE%F5Q rj %rb Pܚd#S\ OGH3DL颰"1%%L) 4\3~Fs#3.6w|^p+dOI2^J᪣ /')Y+>']68--嶰Q E n0.%̻yG룦8{^h(=68rA! fL$@n=efh `[mf6dQQ"]#%ws8⊀-)uLulٮxGlFlP1$/5-YUxSQ.t-UG^G*k.DbstL8QAܒGW \ mr> ]=Mpr#+֜XȒ[c*`䲑İIk0[u4ul%4v)dT`YmGkjŔ(Ȗ<*InM" B $*X)bx I^@War%ŰN [u4=%)y\|v+so?}!?]>{\+ırSݷX~Xm_|?mZl~o~zx~[> dx{}T_߮½_ϟx;߾7o'MgٻM'V_gӧOCO^f;оif?̊9_?.W_?bݻO-ƈ"9{,Sߔ)~sI>[Gr} P(Dyzw!7^~&}[/Y(޺x_Lϯyx]|]~Ɠ>8.'yfvrW~?_82;6ju+?[ B|L~WS5h9RD##y$Q,@~9~ ]^!#7#[R;ПS3>/oy4Ui>dp?X 1X:Aϗk; zO%d~Јs}A>ۙm;ztK~Spwov^Vqnyahww_]G.sĹYnɝndTJWͷ'[fdZp`fJ.g$GT˺4 zޣ~BǻM_>-݆4Sb8)?_Hy?<V?dwȣ(;ee朎kWn,t%.Xcٜ=9gRT%ݠ{t=ݵq#nbb n; [k+<6kE`aϹzR~fRndMRn:.ƴ©L$WI(|"^EI{pjdcU"S>lmf{ؔ%Ly>ٮo[a3f]ގu{>E8)OfM=|X&!V8u#s1oŔ%<>ZV##|gq U$ò:w DI.͸X~f֠o,5OxKX}{ܾ8+2ēKOtFN Ea?3};(1 ''Ҭ(Hs$A\5A+J Ĥ!G}q) =i{.lV́Tzy@6KKGbh&6I.MQIFZw^ibs=d)-q瓦GMw*i{ԉad{ibS=ǝed$'4 whZj2z܉ *ɨFZܣMqin3F[ÆtѴzܹ ȸ3EZw5Mkǝ f:=4=lӯ=74ybh:2дVsB\~JglOyTP7v(g'f=MA\w烦‡mg&7ۣ MoGr[YiB[f{n=iɨCS[MnG!;G4>,4WDS[Mr:W4=llMn / .d&9MMSƝ5l;o4I=uhj |Թi.aN{hr{ظӞ?d;4I1wij |,&{y3IQ&mg6#'MRMƝQ wNi |yJ_~~ߗǗjw?/+*OՇ7^Ϟ/ˏWyb}G7<~_Vu/r9fw?lV?_6kp4ۙ LS]l5U f W$_D߼#펢n15~~lIu}y2ySyeҎWWW$nea?N;{znvfG]=aw{2PR\O${/v,{b$i-q:/gii:u׏jZ'EZwZPT&~t iț$|ﳠCv:NWj"&4M3դܠ, ICI4-N59G |pZ"g#7Jb$a x吙hR\KJ2[d)1FH8v.$e}NӔ#&I4DuNUȈJr*'/7Fr*sgcOձ3>=ʙt*5GM>58%1 L44jGD.0*)OFE\ EǩAhDHL!9(hIGãVx4 l-'x4,lQYʹpj ,c1nrElO9\q "əĄHmy{lC4Ȧ[z$(|"^  |T"ن>[%)3S bbRH>p}=uLg䣚8} {!}=9$fyqsMi?VVz dؓA*L = ÎWbbr2@zQ 5dOH=]U{q{?\#HrƇiɸ>R1|($HrG%;d\fwڏQGz6֐=a)=#=W;a+x{tGZz2ЎJuą>8#ȴGj H;v22ö v$:磲 v܄ɉʨ;Qx7V=!ivSW Hr<#9z*Ho;#98q?0Gl[I~ЏV?\#Ht· C@A QڵgkRFJdcĊb%Z6eqcFa^ږQZ2G)2VPV[W]|Mkq}.ZfTOhUycE8("YqJ#R+6@h 2jիZAT7烖XJ5UqYUV ]Z% Y"2HR,ܚpթ0dΫS쓁Áվ1 n]ֶ㶏oGX b/j/r̈́K+V_iSIKHl\AW>p=XUkIFHzVSJXAL4I4Q)*( JR:'Uħ",e"VPk]#ECj,3xM6!q"'j|xj0aNX\E&,VfH+D4GܭͺdZˊFF5oӛ'Cox01:F6yHJRQɶzz ;xRk $őaMhƼ Ո;@u +qV8/,i,Z[Yy||Y>?LqQ vH+@XKhf`)nA^0'9+?us{=^\ޛ~q{ {\#'ٻys+ó|9k`ֆwp6y,wܟFZ9eGi!5;{XowMNӲJm}07)SNe:qYD ?E=D ݇Q[mϣ1_+)Mqv/.p[}(Sf(7\=;,1(z4Ci1-н3YQxuM.R."z-g"m6! "Й%b!gC}HOQl~{-gEZ  ,yNU<.:tGH3>L*cF[DNE HY~ %ߴ,H ~cCGOeGeLeŰb>@Ej/SHSQ.բX&_^CĄё++X&LHh&?H,}S)@/hzo{䆿'\VM}Mûy"wvzrÉ'}*vV Ek#a cHOۮ۟=o?i!{dϒ>GD+W{J"}$//G}@}u}%7moϹ~UD+XNJ2>U:?_Db"ȜZ^T@tTZ>Hh0PG1ͤJЖE=BFYrK!(@JOrѱPo@ʞtVz N\T7%H|=Oc[kLa\XSS}ೄ1-E@ݙ%QH+WD\}BBĕ2W˦54hs G9#$n"pkpaq>!p@d0aEr0qoWDrZ'w/ްЇ F VyP.UaG[OJr0}8zP[Xȵ]Uv̶θxQ'Xǹdފ=1E CMR$|n=jIπxV [*Nh+I靖0.fjmkoR,\`mr,[7cYRZ>u=l=  ZVMx [uj4:/}HB*k ew}(~\α虍)Ec&lM>uYwdr?('Mn*u׃UG\Nv uqq+, cdmqm08F\s]@tt5{v⼣ ٥cxw0~@p?g \RrY&@p:rWE&FwvM#}-7|&7hBdlgiҲNKPSԓf:I\u4>5 pE6(&+V=Z*%&}A;ey9QzQtحэDL+ [u4>5 pe\0oI4噚զ4 \-yG"CKN&#qM+,L-a4G1e:Gz&@2ԝpmEIF啝A qNgfH÷=spp؍֟TGEl'!p:jjTL:8 m*|K Κf1NlaNjฆфp{@3 ]k9KPzf [x|&nCnr3ɭ(,`z]һYuFdwKC׷܄,m?I p[rrٮHփUG\JV_g82]6uR-)]twiaʭ :(,HSB.dd^:̪ /@fթ!2#qɬ8qd49y"e+QmƘd-MAig1}`(QʢYSf|awRY;֣\lLdD&@LX'F#[QsWM^Ro]BM)M~͖&trkMۭ&VM8jVP3I [rN7@AI᪣ &8E渲.AZ=2U%5Ma4@1 Њ{8y`D9)4䎽eOIl3[&@n%:235rɍ]/$/D~{Zηl&N M6{LE7EVZ䢙MVMpf2CMDf"1imH.,FA@ woo+va܈(A[Q1ՑƵ%.q} ˥澕1L *$WMx%ܷ ʼnN(NF8QeuH) i ߒȬ4rkMͮ؜&IQ\ZR岽p4…W@h 8v0Nwq*2NWK'[E#wֳ%Mge|9pɈdcu?>iQb]p,a^Fણ .%Atdx+KY! e:~Ỵ-wrH8PI±Y9M|,*@o}*ex4  $pфW`Ld+D*طP;]8x|m5&N. #[ p%wG)Xg"@oWpUFpdn+ Pܣ-4D-ҺiDXlk9O[ԕvɸBH׃UG^S!W_ v?umɭSY44)ewhBIdlg8KYZΒ>)N2VMx)xہ7 Fv$R##ͦ7`[bIlE` ibJ @oS>䲡MlhkZ}j:5 J[ʪ%TH~}z|B=W Ɉdj#GK ۑ`4('g"Z&p=0\u426tBo<25ȫ:9*|H0w$[J7L5&./ۭ(_"$T}))| `JWMx57\}j:9 SK m# 6aVPrX;0]܇rɨdc $-3p%w0G5T7\2AG⪣ %dd|ˋ#2\p#֑oݰ*Wh{أc7vf\ J/8.8ʎ&k9K[zՕ8D /#q %qӝ`qy^asT~KM9/iM07ᱝr٨ -vЕi"x䖞(E"&@m%:2Upt&:2uyjIFT \r:'Gp|w9{HBFB%WJ3,IzQN.#&pU:JN uRÄFuM"xf72L -DBфq"Q#B!f%\ʊ24 vp1 4OUlhȅh 8:dBc6Pk!ADCӊ  nvůE ?~ɟy;l:}n?!f_gV׳٧O'Ϸ?ϟ>]3w}~!zuYdv/o~/F1' x(qXO?)/<iSڑ|Mք?ڭůL`j=ݕx5g=s_׋},.>/?>^aߤl(} xciyܕrwϗqHw7LnkMSoF\~_ƾWH!g_gB`D4{G3eÔ!"=+Aď)+k.l`>xyoxZ=O$}N(LW$Q)^dc ^ ynbtqIT#-ROW.X^c+fHjb"W"5uc]}R >x_?!.Nk V> ;˴bN]Gϳ31<[ bQc"K(+S'WG/oĸ,VJ>|N\O~%!܁vI|N 3W,?amx+z'8vV^#dfwDXգ忋H8-_eAݛ)sU}aj%%J!{)&tx@X_+vRDU_HD[_le%DRrHū5qm:v^w֬omgmƮ/Z/vb)fP\~_$YTXAw}jӪyo܂Z?巟m(6゙N2sgztvHee/b^M3eZ?,描w˫ *f7w_iGn䆩ٷ녵wQ}wwWlUf5<ڪ>Qzcꟓ;:%f"\yS{K{{a=Ӎ]uи_E놨z`gjձ/GL+Y3m)bN!_ Gov?=.,~d>!Ƿ;Gct>iq}ͻo&?>Lnյnqp6Uak:ϸi6+\hfZ)qA_`-S{+>Ԕv{ܧ4nKc&J|b 6; vuݝ*wB5gi<(ktv >y%|IYX~Ni5'-:bAq= 'P!O]YxF9形BY3 JaKVMB(5<ð E3>Z--_Y,zL!zOpNow|՟2Dnp>@Ҟ:~::)S:~+G~xӃAD(H^ {z wZfzg(϶ZޕھlvTىއKŶo%{A{ҝztu_kjߕl6.?m&&f mltIZS^w$w{[n^lU-Ajo o}܉hB5ȔN %*=R_OfΖK/w{%vjj1 Ս~Đ ܙW~5frw!u$nO0Y«IPD^ '_SdSe<|q)vcBڳOȅxt dOI19u:ngLBy 8q+^%(dyD\)H#C1uFgԗ,7KZ Vd!zB?%d2%'>i,;}]JQ~j4$Yuj~j_(E8C= Rp J3=M>\Rm}DF;!2XU#C`IS4B1΢ޮi7זNk~3*j<ה9b{#="\k+Y@Yd'Yx_4\<Ƃ c d +4} !;Z>\Ob= WMx;MI^@Pͪ@u#9u|ÿKEk$c0؊ 6 xvym=YZ;@ͷMe* hjQ )㋩FKo*6*l{pV`ܔ6J{޹޽s&1lRD =&@NX'pwa%)Ю-wZaMoJ"f$pu5"n!JQҼP_ US~ru;K\pw]FSs>ݿw9 9}6鼣22$\DW/[S@(ڎ=MAWWM*޺BT]DfܨtM:F;GIsql/;HA"n= /\܆",/5m@)xnN `*hxp\$a.r8wdx4;qƪUʜikpAMu*Apd[W)&)lytXyiX)$geX>+ qM0M1>u PZpJOC֔&:\3NEe+6{W+H_KvB Ł0#Dld԰=ޑYz\,Z; :)*Y*|3 P lCqR4K+lcw8v e=/1E dJ<\ e5pm eUL\]h ԭ#Q~=8V@ח|82 ƛjFּfb@ 2vT0``>>Pm c$d~L;I[u4"Ss>}T}WlLF#E='}2 B#^ ذO$Ѻl-ɕrVŸ&ҳZ,@hVEQ(0pc yE@$"r CpY*{Yœ 7&43 փ!Szxڧ\6/ah ܙ$5p::Va5[(L[\d'oiMF+7hVHr\LzKuQb vi:WMp-&!vn+}Cɷ'!MgRG#aMog jLT -"ŕ$ C=q6NokZ#cD0E&GfPLFHd@+%2GvδLAmطZh84S(1MR.>ei(r,8-&X&UFNHlZ‹0&I\v elK^ >;\7ճWpݘǟhW. AE-8*q bΊخZ(N O*J Ԝ4rJ.y-#7ځbJC$=g 0.NU=hI%BM eLI@ .(SL.1ZRnIx /$pbb֭-F8Һ 2Tp2i{ܗp9 ()pO1l;5P\N[um2 CQèk:b-=s\j3 \@z\MzTKWYn&ep|oeYum +@[u4*T!&"ZSʚ:idiq(MTQ D0MFi}2b;=v\ %}'kpG%T\35$1\\I.K{UG\H*˰rS7ƛ--@[Rh8$G(h&Cm4WzQ4akF6VMxԸGqQMT7 oݦlZhϺi_(ž[w1g{ȣ#4ώ!Av }Z,c{MSABACKTI"N5A/pE_i!\LqE rNCScORF٣:plWק|+RبIWL׉RJ-(#C;GCFMS5F QL|K (#ZeQy(jhm4ĤY]/CՉG #hԺF8@ks Phņ8G:`iXh?d@F-ulNb8*2]:L'\}c8=ߎ>5p} &;13`žs}c ?ZhBDsEdD'X+&m4(#P#.56~kȰH]Eݪ $][C覠dF @%=6$3mt,EFhȈr \PsI詒ԒWvP]Cdu e6n PK8DDVMidiDrumKit.inoXmo7 _A`@{n%f6;Ab4]@ ɶttq5}/}qCH|􈢨4T \Op=%︶Nr!X̛JZҔkȍcZ܀Yn 0;`,L#f *8hp* [n)7n!(2s.F0:BB+PM#$4,O O-пQE+!AX| јXS%W!ȋsIy _nq+ZA!|iJGhn7AMgFـv(}CO?+QI[hx9X4yG> %f1Q|i4p aHCQs[+V)%ؽNe59L>!lāRZFiΫ^F " #*CiA"/Zeȇ.3.ƄhfП(\[Lf/ a\ul0eK62 zCpӜ9 bFH]|FXe`*RUo;Rip6BwYCyWlA_Ѵ2^c|JрY>Eǚ-r<*iyT~JkhdXZ; Ϲ^ս!݌1NGag]i$A=| ; Y+c̴z e9/Kg WqEYCs]謑BIEˆ*!fjqLUꏅUÕ[:}?{0x{v}1.ţFI`hq`e_ҷ Gہ+y~?=y>eܟ,,3ݟ3ۓ,3s{k!las,@ /'s+d[6?8fGr4דCW{u~{ާՅJJC{}2*6]N'yx4qu\]~|aw7y˂E}z_^CE/zۇO|?<=}7>|rwfm۵4olt;OFO?e<~|xëMJ//O _\}l\}_%TdWvXoS+jBWWFk Od^KHyxq0ŇOt:LX~x}5=;sb!llhܳXS5Xפ}:FKj bH*3 y *EB)Zu&eYhJ$76KK?^>{wWn,F 6ӦUb1٬uj=256٭b0n]TS}˵\0K!rcĻ wǻnnz,ŻYNm֫E6IWŻ86T@Pj⼾>?ycX5ly0Bۙ:^xWT)Fx"05v1 鞭 i6f-nnUFiO$l1]3BB-v{ȋ<-D^."E[oKFQlMFQ#q҆צ6x ;pFUGD?"t>]ݯ. [->γ!R?m296x]@cy@#M9a@ވJ#jd z,9 T:x 2 487N/?ón|$fh-__@<- ty{ iL&-ѥy3u)~ssOH?Ɨ֍G!bJ!pOsI8=R[P~I8^|;kܷ\* (Jܓ%3a-w' ne$3^9X %ZB,%e4ٌ 9m@Isݿ|AbhB\f$enڊXe01rleANaI-AwҨ " nyh$UivQMu.C</~vrJ;uS+O;Œ'xe; Y坔hew֬ZNQs}}ީl)WVoӚ5 \ar'3%E;޹h{&y/:ޙR<*w@>@g $K F::Rw.!s,Zxrc:h+(x'Uj5Ɍ睴w;kśM< !GPCn~JuȖ&RS%< 泳;|二xZ;." S%Y=M#KM逤.x6F s 9d >tyM<8(Q̟TDC%Pdq{Rv!׹'5QA8aIה ٹ~-_5|7hѱbσt:yn:#r)Y\!,$SrqVǩ:ƹԔB9P4[GtrبX P΁; sk7EH 4 ðtY'?ND+G paG$Ewf;ϭ=g5{Cӊ>2V 6aW]JnCǂ!ZO͒`2wՓZ/ :^8 &䎞BĦ\KEw&Nh-~X%c[qD-12a7[VmzRlJUf{v xn¡Ԕ ,WyѦG{\G!w=:VW[|fR?ōr0sXb`F$x\E)TЩ&4=݂5u4i8OlӯG]=2ҹTvqFTfVI1եjo:~+NiRXlCzt,y8MnNbAnOg4 Vf6ZպбƜlHGꊶcGbZN ~31p2*X"ƺ)Ǝ4>>3pIX&;+E$&]a[T +/kn,NiŎ#ѯgČ ;^8KD:L- EB ዛ(a,`۟1zu8CQmt9GZ3J !Ij6NjJ[-qō8Lvu;nnG6oukrັ9awQjuBل~uNqav.V)X=+;v+%))G+'rhdlWDtyHaMG+Ŀ@(1f,͕Q\k~M)!7WDU&jOߞߏg~x>^?on&=xBrz!wwY_WpDagϯL;)8{{Sމz5<kbD8QiE^s kԢn "!nff4X .ulXZ:mf11oZ,-#i):ΟV۶YH s@Ă"vᓭu(xQ=Am6vkpmnxiP՜#Y_&^VEtZ2m}bk80u֞qT<~cJ =;| }&ALBƙ9sS\BQ/6>{q&vǹcGs!](GL1 1͈_7ZVtQغ" I==b(v,L-3$8|$}f:]I;8|Q$r&`ȹDǐsǯ]DHHL19({䠍aUJ?0 de U쎽8ڠ8%&Aǵ>mVq2^ $NuA1\2#f GN6X+$z2Jq}.&E +{s"EڄiZ3R, 0/H ̘cYĕr# hGɘ펙m6g S$#m"ŏȸA182;+9ⅎ&;5Q(e}% dfr-70ybU!H^=iL%.HBFTc6U\\'N|īJБǑ.V]ѝV)E mH@0+e OĻ?ӛ~~vxz3}{t|ՇK_}5s erM;lwm5czYw4D S>vG9 ;9X(])FwuB%8aj҃'cD&ۚ,9f=0TW K}͸̰OŪL82IMXQ<"e*MǪL붶@Bڠ I)k2eD֓mL8}&8ix&R:o҇<6v7^ׁw{&Wq¥/wxjaX-:>)щ//_Y5^;խ8bt&7:Gۭ8뻵ywzzoMF|#{ >Xԉ&>6!agX¤2GB .4RN::q v;lR)$5 )pn.P{JKy>绫S\:{{ȯ4Y>'ftzs.;FjƯKwlҞku)Ҡftzoz.;"ip8=d=O1dIԄ`J`ZUqIuDqK캙EwO[hDuīFqƿM5 fcZD+MpM$~ s{kZ0C#&>NVbAF\rݥȀiNbh%‚瑉DO]y҄'䓵\h#en,#v{Qhs_0"wf=c i %?oKse51ך_Snce9vo/7&eeKlށM8 !Ok0tsrqcp i+DR`q, '{f0zVu|x>Ӄs_zwWPnv{>A0h W.+bq!%jMLdaXn*o^5h|ӻ?]~lWsL-K4fACfV0S<_%8Zc窤s^>??M{/NE& 7=\;|f`_7{?{7_p/~~p}v>pӧO><]>NF+8MC0*,V6ތO뇫qy6zT%$!}ϟL?~%{|?\Ay28I^\m6#f=P>3>?~NxI%[↓agv.*{ zgdb p+_ݐ:z9{n\{!mj:\]~|rz+PэK!w c^l2u6z V$+Elo+?{7i*7Ux L.I }2p8%mܵu>"n<1Me"Dё;/ᮤZ3(ND9ETz醢4#KrBN!Zf9nvnvp(5|-MƗVh ^[-F~ߧ̾RWRBfv+.ԉHhwtNYr B Q&\*̽˷<ۙ ߸;Dʵ^@W(UhzDf OkZIgƕ}a:_Шzq=d]ڋQݯ kJq+nB#`a )x@*p G^/~308#wuJuZD~VnY"[;+|/~5|9*Y\yhS&qB%k}['޻)FDx@&`I—R% J(OƧ¶ %zA[@9©#"{/$(`W PJ}4^dB(dqz>Ѐ;)!~k:Wo)) Z WQ|xA/h "-3&vڍ>id! ef|7Y׋D ̂\I5UX** U³i?A 2p+Ml$& t]{3lH@ n<yӗ}!Gr0@KQ!_;~pYĹ/{ȩW:Ћϗ2^ĀE/DD%Fx*&x$|G.ahƷP[a z3Hu(DzyC }]kTau^ )a"58Ŕ9#-T2. M YE xEHD [nD^p#^;&Ц nTn];^gTvĵ.\;]>tjS4!ʄ5`bUr/X,0`X VG_A,I E@WpS m0F$b3-4oX/{7`A2Q eٵ&/.n:A` `,x42hI), SJL,V0` h0@||ҊAh 0$\tjt_ռN@:@С 9K;lDvp 0Oԫ2>D5SN, aOas /|k஽ytt`_ef ^ScގTSu0w C '/ d vpY.Z3q] 䫤GسBK)88!`gAGg a'R!|!{ xDA14,&4 cf˂Sx7҈_ C1 p , dt#&wSCi XgPg(hI@06uvmq𦊡ǀ$Eا":&DG ழ݀=ٟ+b h1 QC? @@Qxnޫ~b+Bɢ!2ipKACK@ f DFR*uhj0<`r4hKD@lk }! C'uGvm%G(hRh0rQ]SB]G^ ҃!d ^@`H19ݠV$4 #h˃j`H vBcF:A{ ,`OX$DT1K(J{KnwުUaA0oD}x[njϜe` G7H@\V#RZW/ V!"qF?O(DE‘>FT$^߂5 P]Q,rC aJ & ZL80P:P ]q (0Ч@cAt[*.Čhg} 0B0r$Ce>)b9!xDCF*@4(8 gTs{ԯw ڶ"r DKǧHR^i z)nX 8 hxHMa z,h?ZO*?#iq]OUa1bLC?ì$Ry1D K1ċ5+xXR[* 9;!' 7n}\!Biea{@wHK*d9B 9a ~,:?p+EOh*;(k1.$A @IE( yBhE@hޢ $Z03 `&1#9D൫=wTԍMa` rbi?"̗Z:_bx?2'ܣ 7ШE邈xQD*EKȿ.2 BCd0c+ ;G׎N071]bLK g3zk{7 le@Qm/Z s* [0-:5b<"7q;KL[ o34 k t ]06Y/6=c~/5`RхZ9dsB@Ua#(ټ/ B 2!c QL84% w.h'f؜i6bEo?za\A60"<8吨L5#i hC);(3((*Pa:N8E&Q "i$A of%|{ɧ뷈:+5Tyˆb*WI#*EtLN(8lyocr̓/xʣA(LAb:(B30r_qQ2"8?vc. EEdAqj|`2u~q `H0">."#RF]bN̋ڊ KT4eaY hZ*6ډڕ69{*廢&-|ݎAޭ`:hpG@AL˜vlP0c8܊n"~ֺt!%u2PdFHA ,L*׮\p9_5\AV)hPhBg4VaNށ 0 Hr~VZqGh3ԌFc `8?h6NHZ4 11A2d'@SuTaK {(C`v`MIz q."i ȡeh 0Z8"]D}. [±JOG?5`'hv l豏#٨G뷈N vNݷϑrԲĀpV? $,t%MAf`hܼdO 5/  /63d,3cyW܌X߃RdK)+l$ B,Zpc|#EԱB"Wb;*SԮD!6TJ /-š>Z D**s* тP˂P`K BPE1RH3d Ԟz:DPF,fhL(77` 1F@OHp =D#7nH2`oxlGNRš(\#)v@qGhjp5!"b&,0+ΓyW;て0ko(,F`*wQhy&0b1$*$:8q2V0 \$L8%p)u0ۧAPLƢ(BX cƢ,>.!'8?c {Xm/ք>0CI'`&4mFXpPpR?`~5V"^uՙ!>;]2,seK7t zqR0~07"}pp58qBHpt2̫,,ӘL +I}Ci>}Rʬmrqf]ܒ &K g4_l\s'eQ 7O||cf;U\gj:[C&x|QK%,f~ŵ9FF8E ~On&-.VU蟰^ty=F9C:]7STuV̟.g7Ow;=Q0Q:/X"?CЏ>5kv+a|c܁x˧8&Y{a 17'9~><N@cj:NOu2O>f6ryD8=0uudޯcxGr;s"8:'pQ{гIzy?B8tB-:&l{Y?q4L0V-]@!-ȉ؏sPZ{wWȼu=3x\:1/Mj&CiҾik4)@ڹiޣgk5iρ&3֤#!</|,]Βa Im Ӯ9bCw-T<`,,Q<0uI5;kO̘Nr<}p/됵v!PKr5DIﭺ;- Mood-Light.fzPK; <fritzing-app-0.9.3b/sketches/core/Op-amp.fzz000066400000000000000000000471161272363601300207400ustar00rootroot00000000000000PK8D+lL Op-amp.fzrH.wv1_2{*n3Yeu󧌢(S!׵}}}p ^$@ u6Hs="^ǻd:߿:9 ~k_v&g$3͘ŕ<]q//4|papu߼ݏޝތghv;|uktֿ_Gv0ݼ:S1RYf8:WRU/PoG/&Iny)C( .+Egl@.}sV~pi=Y't7:;ߦNlg7Wo~qɴmn5 ERါP1=dRt_ ?,ѹ߹^LK˳Edv/Wwֹ.}N3neF:or]6<p;[2=:hOSHw54>?K:@fb񂟝z3y?NUo~~=렗\/e>j!?^c4_9? o~|u&$w/ztn{Of`<>yKg,k4T O~hͿ7]nz8&:_L"Xtj}r߲[?Rxg>2q7}ZY8᷷k XPvi|M֝c3!C0};Q2/ dy4l8< JĆ^Z.i r1kƋn\,Nqf~8pjų:ő''GIqB_>2,#Ȧlk5\K*f~{W4ޤX[N%ѽzI)-u"/QEh:%Xi㇇μ 3L!3ߣve&2c!j:쁛fq:\{C.\h%R i#$~[)ug{!\!oGPx*c9RqM2IKZ!춣d,JPo:nj[齻z77klq׿ξ_oM_|鷯~7}_zo~}z  'f/z>dx6\~77[Azd4eK8ny Ľ~~^~tvؙ}zȯ=wnGf4vT/:aäeY=h/oW~zҹr]^zśxt?"2} B~<~]'ףl<|,>_gWë|Yv<;r{%\|t'ͻ}gv3vwNǎs;oo;wj{w;ctw]n:Ɲw1_'nLc½_w5~Ywǖ\a+nܹ:n{wt<:^áoֽ߰mg:y7?t.}F;cxCm/;퍿~ ]ލ$yn;7Y ~4{?ˆz7C7G~=ݠ_|:e^%* ˆt_=G~`Gl!%pg*q&Smmf=@h854n+j"Byr&3Y(GxT5(ZAAS+ 5UJ6 (%OBjt᫳#R!%y$*Kͻ$3r*{xa,4r.yt"NxM:yS^"I 2_FO$tO3u)d]k}_WQ)UMR5OMJ3 (fY4N` )@B-naIzp>)Vz6cǙ( =aJ}#es'|:b*ww.,2c["籯@Ea̓Eޮ2x)R^>úл:@)' Z4֢Q19ٌmxM*b4-3U?ad}KB1<Khzs+KhuZ-lGnt4p,rhM΄4S)q%=s >L6$<,Ia OOP~f=M5j 1w~8 ] S8u[-3&:]د{c ~icI 68x=jJr25+#vO7 5mbmk|ئ"S~a4+O0ٝ~}Q."Df<xYq+mPwyu[8g4~Nѓ9G`xH| Km鎍HݏcW-9 8=5)RcwdMڧ2#JfEBe,: ,SuT.UKFooں:sWpwJ^&݃윷L|FcZn%C:3)f 6d2f[PT>~QђѮAUUH=~UO:l,]YEq8[sPzGL,,4cjqJ֯tl\^s(_Z" \ԊmFV~YB[4'qWcahRK@lƅ"a>aa4QbcZĕ)s(YԦ&ϖ&ܑ4}ѮDa+AOʘ4F]s%\]9TCejuyH"ɉCMۍ%[&!%!%~ⳏx喹GqAO볫˯l4ߞIYx\d~mYd,a2T+l|u#i֌ kwsՙ'pӾ8ѭ;3~cw?vֹ;9_7hiY⟐K\ :ðzpm.{ث@֝QWZ=J57?}eɪJh藽W'*U,핷0|!ZFJ|ӡҌ94^"k8f+d/Guwv)Kn0ٽ6!iGJkMemSDx}BK$SC>E=8>>-Q`Zǧ!^5hQm0wnt?Nxwz3^E~6?&ه Hp2Թ _ӳ;quA0}_N`Z}*Ka'Uڳ.ue@& "e@r'k2bqEW̵%Mesi~{"@6%m]d.i\$"4@6@s]q$XDnhr mZ7`6Y9FV8:IuɋfiD'G*SEO(zč)%DIt-n@;|bLm V=cZnHh5GZF yv-M$ϦhV ېP2 ^j$KHwVY"jTLu9L>}&kBgQRN4yĭA$ѷ$e{s|8InUpaG 4c\l]1 M-<&਍xrKy**#A+2m]R&8y\A'%`$jV)<6X^V-ERJ&~mKsܪ}IsTI\-nB{}b-oTrDjm[MV#A6h  w`FeTwEL ufGKJ*7\ɦՉ$mB#>-!.5[ё#Q5ܟ5+X=7qp׷M\cڔ|r<aRi*{4DƢĐTX|wFH\H#ʢ1JdhYDaM拺?Xo*KA*)ۈby44Vՙ!LR|JzO o\l0?WFҲh&y0xe~,I7: if#iwϜ+7$̈́OJJ3ʼs᝛*'kz3_mHmM!Mx>QcYjf)$-)5 *NLwRHEF bvvs•n[ABPJ?y44)XR k^ȸ_}3ӄ(z~:ĴxR1J R'6JcpS3Pdaqֵ>}3e4ZBrOVMTڀ`ޤʘG&+./~߿c. D⤃ggK/yإ9/ h]|\,TqfMkܵ[d^rdg"EG2TNFɈnx|fOxKڨ@@M'M[s_EL[>J|[.ʳ)I$+$lZ*vwSZ,͢&Sʄh^rh<_0ߤH.&RC}Y"v {Lo<1[|x$R0i@ɳ&n$CgE%-88۪hl Lq_ Y w#ItRQ@cGH 1{5F#k HITm~Xm5H8ք+XZ k9:΋ei+WU)Uzk.ʧJO; T_kɪhZJ=eZJfH6|6 cm\R&4y9FD`DRmU4S70u!ŨP[BC$㖆e}8{=I H-fͱpfԍ.9_\ޢ~D٢DiI7mk3I,IiO[ N6B| u ~YYKS-B|l:T3ƧFJ6$Ո"ϵ> ?$#ARy\ !ZÛytX@2f!dH X*.lJ$ζoOXIőȳkMF"muG*Ba~x;p׿^G #̵ܺ}* dpys߿:0ݜuo }U@F[7]7ͬ^eX&5pVX~zv}N/hF /ͮ˿ %_ כ?}әoxuhF{oE狛EÇƓwh0{DwQ݌rvbcnpw:bt_uHFΡo>>'O﮻?w2w|w"|nt{!t qϾ\6#nxdI)\=uʙ|ŭpڡ[{z\4!#; Gx]u 1uj.]kz4#q;^^Ow*Z o݈ vD$=w̷tNYrJ?I Ǯ /s/=/w OvED7 odfr*޽U_zysp ~3γN C kwO>gA7d8 P/~u p 難Ÿ7|{a S@*p |P/KD.DI:cSJl|'NgƟE r[o~ jgN -T-hy(Y8u#`gL~Ϗv ɤ Ag)TU9۸wEh@*J݈07(o6~4%Nva ޸CHnO[POűs+ E[B( qz m35bwlG7 wWAxМu l1ű{݈]QvHr|aЌMֽEeDfAyIMI\5UXUU,{y;Ʒok/4p]ơbvތ8G/ЯO;p[}!Gr0@aQaO ymzԫ ^?x!vKޗXᥪ#q{PVz8h|%q0.7\gb @N@AXX9oD%EG1z^ )a"Յw[LpPP}@4tKDfA) onx Ow5܀6pbpvc~ZVv}k>qtkЕ횰ڔM_gB7`bUr/X< a8/.ex'a]MQp.4m .xӠ?cXZ(+7y{_4dtAєA٣%F=l{[a t Dn S*L,V2d h0Q(棐4ѩ CEFo򋱙<c]H%6"}[p;'֫2>D1Q3V RaDu7N wa>tazSv4H"WVux0Sn jx p KBEҽUU@k@=0RU4i`Haec3|kUw '1t&!aVORy1D K1ċ5+xXR[ݜ* -:1b%vW 0n gFГed'.EͳI#!-t|-lp/ &wT@,{A"!VP0hvnCkCQ[||]dJUrv!Rw^θ-s= {`eHMp>IG J= `;v)CnD2162qZh8s^P:: *pC0!Vٹ C`,kS:$.t:Շ$r8<X-:?{JHض$g9V䬊!0ey$l+0.՜_ Xt{Fƫr]( rh@ !)0X8@Cm @D@p6D'(+ߞyCpzb(9ف&YtA@w !B& kJ#Đ@տa,:?p\+Ex;(4U=:v Q5ԷtTX?/cuc'<@hB-Be S._8k09Lć&] sG5hnN\FO—/z:旐_b):~dO[nC;gL/HD"R\Ds ߅%dw۲v/|vQPt, FŹ@[!o$!p\!b!Sd21|iQEڣSǎBancŘ.%,`ꅯ |a`%.+l)+JoUi.ك;2[  (1!:.P ҿ׀^h@$ \Ê!HIšhĴ4; .DŽCd`u>ܛ;IqQ"DEh 8.5@(h &"Cxg 0ȶ`K0"T ; vGN ǜc h_<8uƉ*h;F&iup |j 5BNCS{.HЇ@fi { q 92x&m881^]Q u ܠv) )Fqi*Z@Rx׈B$<&lH~rbvϑI`$c!WX%߇D`6 d9CPA4cp2ݡS@0XmA̺77 #@Fg!B0J_#7!s:hR`@. p`UEbdPsAy/a-\JuÞ)-`ϠMȍ@<yVKLS o35%y`m^(rQC$v1^j#: sWM1GxP`wYzE(aR&dL! "GD@"xX9-Fs3Բ@7#+-_G9#ȵxo&8vxD4p;(3( UX;LX"J+MX "[h$A Of%|{ ޖAI9 P #:hL9YDwi#mL`9"3QH f_c oAXD(sdM U(x=2HyS~is|0'0xU*β9+iJ- FKq0y2qfl܌X%(M2w;HmYd'Q'p7 ߃fC[IaRހ]d[Rt玫*+848%S A`Gէo}' (TM+U]j5K>6 גo"{zwT:JK!{ -š>z uBUk*BUl.TFKB /KBE*/ B5'9bǘK!T@ft P(P{xd23C ֍X6⡙2%Qn>ob|>1%Go]ݐd279R(,?,Rœ(#)~@s jpQt!"b&,0+Γy[{てP5)}Џm"n3nȖBDK"8`< H1͙P68k:"rbɢkbJ >ɚ &Kdd\:`c ej7 gܢL 0KKHus9J?| 0"= Yf0QC?k ¬9j1 / +Q<B阁7*Ċ^dvY­5ۊִKdYkGÈ8WeQn0H$p(ۧѻAp*EQ"N.ES4米ű(SBw0K'Knoxj<}iwK‘) 2, yp u5G?x< Ou791[ Ԑ8OЙ.|tf%t"(8[bE]VeKV]u-1Mo}a"&%yt)r#"w 3%c9?ȩS;h;"< #X:s< vd暏7 '8'&ѹ!r",9N!'X1=̶ [BNEs}o$͋P0l: #,8Yq Im%_ͺլ+f]uahk!t!K}m +85!psLWn8G!5 c{""X/ >3Ǯ,tGǫp:Lv)a&3 !Bw7Pl8p@\Rx ޖjǂoX O[غOgjSVp2]plhytpӟ1q3] .=0ˏu~_;F;eOFף?V$c0ǙՕ]dP.?=J;}GSJd~ndjwOXw8NtIg#s5z첚_ӕ-dnt?7}5D|__{Ebv6, \p{'K⺧]_|Jޝ|9W]ڨwbwgF _8k`Bqv^]o :-tXӆIi$Z=ؗtUԿNOG`){U/pB5M uR%$z Uy 6_6eyN HWN8fx J.v=3GMi}Gyƶ8V׺1F+璿9oq=8lѾֽ-.S>~P;90>D6D$ljߣP(#w}|{\2Fi:-i[Bu4ƬM:6mZ#u?lGZmPkb䰆w_{"(S3̈́{d+*txf5QQm3iXRiw*wMU@뫛S3_JRsyid];/34{˧ݽB* <6۽mU_Ҵ.> SҮs^bٰ XI 2@8cˀTgZvXH!ar0OW$H{ ilM{5ekEr4b&3JJdV8"8g*⯭d"|tM6,W-Li'cۂ)UHdNܲˌj`AHi= u q$i5Xƒm%p[$eVE? 88Ky[RLB&w$Bq9}`ł9a/ -5{(\dR=JdIfULfV#H[M `q 1ikAs7}dy)':S邾rOUĺΑ28($&wddI%5NJJ`Rr&2QM>9RdoK yNz[@D[N)xh2k+JH-qVGH7z akF 6P0͂[`\/T k~vZ..ZN Bzl(ۆjL\/bJ\F&qU226^\²7NqfHC(92V= Cgڐr*פIؘNEqG*\!$LPNk; Q*BdHUeiɖ %J׬P2b@͎)Wt,HX#\a.c5WWW&hOHJF -캴?'3 s2V j̣*0|nXSF F'Gʪ*f8È F;>$>v$ې -.nj`rZ}vqm0_N*\Oц"2ruŕ.M{5=^+ƥRZgMH"GǐT2Y;|n+!!3%mGUc:(Z&{stIbrr9lk+d"h 6jtƨDֶsbM5"CA0[wrmGDe\:yF -&1ARh([QC6,@>䉖Tu9NQAGbT3Yfv}0jf"h]؂Jr7GT&9f) st>8$W00ys0ZT >M`*i?dkN_;:]M8j? FHFㆅԚ%3wtӐ$jDW[!4oх;%^mMXVZr]of4T6Hhuf.H,2h9>x$*SWL I6bߩAZgq~dXUGbcGGDs<;>r$:؛Gctg!F%szO211*X,13df)(ID@k"|Z?Hh[\jXc;2yh[ޜZ_Ig(4g(y3Ѱ4Q|$VvtH:yS3FF$brd lĘ$%{Mϼ2M i-WP׽ϢE^`gGEIl5sLHZ nco\B gH0bo%kb$,/0$[yqSYZZ=0<Jς6K4Ս[EV\ܨgD^e&&ZJ)SX_RRoJÙF]dGOlq:V /w/u`0l~3O.}=DD߲OQ._?Hȑ`CN =JvP@kJ;}G -rrͿ!s?PK 8DXf4 Op-amp.inoe 0SܘP AD"w7M]>m#R'm`eώ$aINPK8D+lL Op-amp.fzPK 8DXf4 LOp-amp.inoPKoMfritzing-app-0.9.3b/sketches/core/Optocoupler.fzz000066400000000000000000000354441272363601300221230ustar00rootroot00000000000000PK48D{:Optocoupler.fz}rFqG>Ku3amw3OqP%qL>Ծ>Ve. =12P/JT}O~0 'Wg4!g`ܟ\ׯ~y]ln'Fj:O IEBhDB/Y4 _]LzY?\:_̇xOy<Fg|8 ^}Wo\5ټ7';>/7TbT{ 7Wg2SNuuZpqyD4ݺ_LK(ޝ<.z߮h2}u+w]O>R!r8>f7߻NgQo4d}M' Fu[0 n{a1 'ͪ9{ܫU}nu|*{o&>pݴ|N= £s(vW6-hAz2ӟW}__h0=%ƗOΘ\Ew=߸_fnt9q7C%wd:Ng'WߝZ-q_W]nAQ`ZTe7^&s{DYUP1*rAPa<~*kr(;pu tCe_גu}XvEz(aXk}8llcJ[j%*Ak_u}Vh2EdSw}"KDAVND_$N!&ʴfkm1 TeTNe`MPFTyl{MLO__뿞OFo6op}qw~~tB7nǏGLw?w<ߎa\//oeiBzyq]8* g KvVu0Ǜ|weֽMM-sC+ Uv8dx7Wf/]Z#d^'}؀z8瓻Sb2OnNWSh 5/FoЩ؝2~ByG%fĂN oûu}zd}Utr}v6pl8&Kwٻ]<Dhr[8TD?ա7tmIz]o}7;7by~C?9/p|MQo|*>72qxMU8O+BʓD=VmRL&+0dljB6:ay6UԚl }@9F?׀83Ӆ\mC7h)0qZDAPt1?ثXX(1(eQz枪V|o?ͧTO>3ՙWč3$_8\sq0,['eV܋Ԓ:yeTMCZ1]P uLg\%:ͅ6T OdfIɂDkk$'4Iar>4u}QBdp>#W qY5č{Rڒi*x\Grft1U9d: [DmNk&шMNQwhݔfSM(N IڄLJ^Dū+Zy$TЀT&{B,bڪ&zt"b 2'QZ(ig׎#uz 6V†N5EF;ؚcs`y0\,,/tdNݫpp|]rpwymj(pwӉhܻ:òg}oa!nxxp<ތ!6;Ӆm6?˺c2;7"?>=jDQ\ O$Ry` p< ]߇VE b0b]be[չ)y/sj[#(_94?jq4<؁v"KۂsijC` uWOgWmtKRrͨϣ,8KX'LH#|& +n#s"9+;4vT]كrU`3!Zd3D;,FTw] F >{"`uצOug/6| A>Qa?R&Ģ(J%| H& [H-J܂D?j-EMDgЄPiz7MFmt㬄Q<ۙyFg YarIc  ПH ,hQC硙E Ac|*$1|@>$.ΕMGCϔJ>bESCI`HRM60y4kN鳋Ks6+>8Kh=<a?vn\,T2O" \ $L:>h_`v2Q`qL1I8ՊbG_P]aAzp5z5b"QcD0u";PF8|,w?%y1~*ys4ЛhHic>q":&!gI,ZJ5>g볼.{3[S&Z^yƄb^O5yd}kM+ fRB߽RGʷ%yћ_YztV"4h1e/fJT0Tڄyڙ§':f%1nXNa'9o_w{HQUUg݇DǙ`EoY!C$(fF#V'#0-i8*DC{H"IHuRGߡqmhwSmk:6GU; GP"Wѫt,R E{ŽW(TĿWܳ W~j4?r3|t.a-kjذ(mLzaYZ,J]4V4mVk/Aij':CSV`mW_,ؙlK@ ~NRe`Z$i?"pzj#'{P3n6bO,S1j7D&L7dDN:^}҉uH4{_8fǔgW X*x2M)xjJAn9ʼٓ j)O,O:B[ܒ &N/%tAȽ>Šn>$zI )ON:ՠvnvmAdtji$O9vl(YtP;Y7ސvPK(9ăIO'&OlL?bO,󦀲M)í"u ^ކGKbp3ha~kY:߇uCJCTgIj-72VQ܀WDpޯkU7˪"k~j (\5ۓj[uֵ)Bi[@_f!nВY[9L,c K3IIiB_O'jzI.KޥHI7GbX iwB<ԊI#t8=PSU15D&h1dkwCe4nQr~[tukeG6>&XT$0\hȄJ3b3xV۲BJJ9j@eǀPR# n;3R]1#Ur7L7ot18h"h5bb/Ɩn`=bP'BmaX,4$* ]@Ļ,`h'F%Gc0ih 'My:NjImŜĘIBK)iM"Dw-~ :ہiLc8 *V7p;ro*H $SZ5ZtkSTX2fXJ/nM=ա|Q#5Z̴r4jEiI*]91sRL|IR!kmH#6AL9Y:RtUd)S aZf_R!۷˖.WW*6kCEIXbPRR4<($dGKF[DYaƓR]!̴ѠR?v委ӊ .D^|]sx%C,j2I-1]WX|矺"P'Laʼ6Ԕx_3K3|!~ }1N'CM (G㦦:Dii)yQp+c\uI̥旔+ruA@Yek6zl0u9qj:>_]^ }] y &+Mo7!kUsBx[&O-:ӖJ)|bߌ}iM+x{}u6~c21, MbHjnP1F)Ն:SeE -Mv-R:^ҒHvn*!IN'?W9`q=s sZSPʜ[í`F.'?f0gˤk!Mnh?ݎ{4!/?_0͋?q=?P?^?~6_G?Σqw~~:#nۿ^w/ݏ?&y2^wnY{beq}?=#//'l"Tt4p08_e eDEqP'0lFtd(쎔8\^ΤƟK%#L8P٣3*HddMZvSW3Wp2֣ih_]O'i)QI/ᑡ̝2}!w~W8~a%RQ,[t)5r  i,_p-'/*k ڿa3Tj|Y mբO:/*}Z쌕¿":y5F$\жř餿NYr B Q/]^^zGAVA̋3odbrʝw V<wtXŒp ϝ%0َ wA,hQaܽ2faC(3Ab*tpgẻދap ]c(::oMДqt7^-K /P̘֣+J/$0Zhwu(L,(0/()(0T \z0`Q%0qJb 6a]^oTgb @ x,x辠IΏȎ5ZaJ#+_S"E+;ň9# vih:̂.R/ @b%9߀p#n75܀6epbpvc~JΖv!91vGGϻv M},hWզhB*8iWɽ`€a1[ p~^\'a]MQp.4m nxӠ?cX}D-zeǎܻy㸟 .7<9(z4ܣ:'S ` RSVUb d)[@1xP䣐0ѩ CEF󋱚7i`]h:4R!x`6. byUׇ87s $) rn|y/&a` ؛wMA f;QV``zCv"tՅa3H -@}B"ﰠIf\`5 h@J,=+dZ]@P yp4 ptP\{"рGCPrlB0kVk\,8w3|!0HRm@f@0b~>U1tܑu ux1 cC\gFX@ oJKy OQ}*Bl@Z{ J ؃PI|!n@&(? x  (TNA=nP, } Is@[j 栝LS|4\b0 0k% *5R+F(V9S\A\" DfgqN4.| @P3ٱ-gHDu7N wa>0X)D;CëUg ^@`H19nP+K`X4LUA5S0z;R#ʠ=Nf0',`%sZȽ%7;o*snx;q!f8y3gr 0TK$&p@b/D uRy@ġ7!QpU(7 dm.@g@D&ToW64 #B$``D8R n>Ha\ƀJ)%)0XbVr 1#Z>EB} Pu XNѐ ab+ x<킶HGc+ơl-@Wu^hVB z A +05v~'gAX6=w?VqP3 \FYI,@)z)b,b5+xXR[qJT†IG J=x `;v)\'< &dRcl dAN :ѰQ_DBcpJ8!O+G<04F ٱwm @gV]:ɩ <N{~S{hvr,m,μMrE\H;4Д} _X~F39v"sL@.0b 0<؆9H~"/(9y>S@q mGl~vCI`0"-aȫ CĚRx*b @;ꏨ{yΏ3ߑyh;)cBSZѱcNSAb@bO N y0OM([DCK|bpH0q|t >lD5N+cK#n09Ǡ7oа&յ^Py}}KI1˜ǥ)&;(Rx׈B$Fs3Բ@7#+g _8N9LTtR&Gȵl&1;vxD4PJ" ?5xO TCsS ASDixo@4bb67l 1:o8+惇KZ -r P #\4NQ,;`rD!fC|'g`T &a2E_ŃD @.`(h'DH| Ba03-2!2XtC܄CP24ǁ-g ,A=JOG'5`'hv lIGmwMDžd6*Q "HySG 9RnZœpf@;r@AW[d) 6-KP2xn F,︠i.sAJo"}U| {P-ph1>"ނP{hoi㇎+K玫+848c2yL?1m?VPFqjZBSYat?}GNz9m*4*aZ[Ck5. U̅X.TF B / BE-*/gB5?r@'N\ p2UBS['2 `ވ4S@10FL! 'LID?"xIf,m/U8ői6@X& `C;8#4b*\dMHc8@Q8+Γy[;て5)5E'nBDS".IN ٷo\sC`1E*|5%L$g*Nc El7 'ܢL 0SKHũI ǎg>{X8,e2X0 |5G-F0! xJLEo7 oݐ~ߖզ/a&+O,|ѻƼ|?|D{Hi) dO!zǗ_ ű@4>Sba(74%7 L:/Y%( V(XR\ڊlAٲJS"'&4D0NweA|X#&6;9uj!``0U^a@tSѴŻf$݁&\~e3I<:X6D50CNć1O 'q kBpn~Sҿ1'`&4mFXpP&>`~yU-Ϻbx㬫, q07!15Rj[,C ~9NW8Gaj,"烃 gcB耣9`^f9dɔ%3 !0ra(SOnc!zAs mL[:ăӯظ֨7;_J[a|~O~.۝7u~3f0 ˎcw Xcr}:O29 +1ƙWfqI2HpRѕˆ{|"4Vs4_7m/N݂4_1aO{"rL@yG-_/?^ ?qڅ{X'5KY@>V4ǨD)v7]M-Qo)Jo)ߌz-,8k`f?W?eJ,[|x\Ip Մeç#x%X}ľa.mlj.R)ӥE{:)hzՒb:F Lt 7Tݓi.X ^n|-ɢQ.fykyXCN.؀J ?JP c}{7B$1U_ee uW D ^Sh,],j. f.H{yp=|m5T/kLtڊ Den`vya,pvؓ">F{OInt*PU*/UDiqsξ,ZbJm)+">5&9¶HڹӞ|wqHnbe|5+rRz!>)F6Kؤ^6lT#-W;4d(<o)zpÌwRҘӴeº|+(hb5i0zz[|$ Q&Ac4Q! R+rAv8QJ-]ED;iR4 Z&3|zW&ldȄsLHVlG.R- UOM>92Xx;coPt8@ȳi!2dYjbJTXb)f:Q퓩+$ ipj |N*z lc%~W/xgi]&T]ZvmJYE݊bōaIK&tD}$XaLO%@px4iw[l݈ M4%OpШZ=*QVõ*?.PK48D{:Optocoupler.fzPK<:fritzing-app-0.9.3b/sketches/core/Parking-Assistant.fzz000066400000000000000000000773051272363601300231540ustar00rootroot00000000000000PKm8DZ;~C Parking-Assistant.fzr#6xoM=! wvnrT**FRUu^[ kZ$AIt}1z{-wf׷'pnקO{4D>gr?p.E{n}..WrwWYroryToe/5]⍻CƘ4zMk_Sސ7\( 7?·wC7v%^|xnq-e/z - __G+ǭtO`XZvwf?{dpb,0Q½Y79| ·H;Ch4oNr-/n0l>*ܾA#V읷^NR'NaYGͺW^P}~ Ӫwx=ya˻7dwf:ߡUAwϴ7ϩٛ+^-*9͙z6Ose6f6OsbIX~;j{|gwU*|;aoQ{Bx2Oټ{゘^lU|!Wĸ}g26ذf)7 1%ǥƕd<^3 Ɔ>n&m^ǍILdw*N)v}<)5EcqFZU'j=nߙ=nMFݿVrݐM0SW7r8!$RS`IVcqx2NT} MI)hͨ2Z-+6d̨5+*)^7J(>eiJ3O0`r{;JI>9~_Ȑ-2 7rTarܞ3+TPT?X OJU6 OwVcqG+I_$9~ő gۦGXiS?gbqJ>S~d&^(Z=nT5>1ESq̸g~Rar][E]D*U\q>^lJ+UZ59P^bvz|)yT~ڊ`Uê=-0=,^MmLT,='#>LG^-&(Nj< Nj;:Lr=n OfWZJ]#/}΄#R'|٬S||zΣY>AIw 댣D%!ͩV !bI|֏JqgX/GZU:S'e-DS+x(^/JOZ!|z%j6$68yIǨ;8FjV-^Mf=Ҙ~ֺe1{W(Y*`rz2jh7$cj{ zؽ'fz;%{2;SLtv~LTv &[U' ޵ `oLZ]%^j{VVɦ !&uu f80Ug8pzNw)n;Ykh]$ݤ\E'M+*Tf մZMpϊ=$Ik MW>{6=~"ө5JMRJ Z#65M&uWkk9n*u/L03ɜ F^!jD|lzſx•~{ae+{W+,^Y4qaNszu/ jwg67$cQ| z'y57'fsb^{s:|@Qp6aj„.^9(^4^ɦN`NiqBH"3.>\7/FdR ɂ 8^0UɧUJ`ec@ez Bl b)e=0Upz;Qw )nFYo]1%lE*N5}T}"s;VI=0%I S#W?6=jϕ"7JSRYMMZ#k7bq;dIz-ǭ^)zCf= Skjl$R鳬:cL; Ot'٦Wf|&XeL+ϪaJ Udc'XKOUK1+'Xֻ li!I!V]e2%^1NTO9}A>n5tu9UWtu9m]|ܡalqx21ް|#TD,yѫԞ7:ї]~Z|^9O<\?l?zZ|w7H>\~nq>Y]]7?X(_~{ivs~៘kՋ٧ݟg8y/kˇſk/X[o~7/{_`$Dqen5_ϩ& ajc/ѧ~f'so5Z6Jm|]r7cx`p{u~jnCh!Zic% i뻇z1WNwXS%tێϺda5_Jo-oS(?8!>cZ}bȇ*1`` q4/kd&Y(rCZz終4􋠢<}uc/2ݞ x!-ly: ߐ#(q(P\>z?[q^%~ uNd5`ϩt_YTGW2iCp|y= <-1 &yt`e'f:0%``v^E!xyXHd(f<{M3/hK%ۍ2N>1.5?*<T+X;-w P òkDWE6H 9NYu&sg5,h.DӐǧshyϸZDN4d=z^xs!Py\ʣ{G0T3Ď-Rsl}1a3g(ⅹ cL/(=t9_txU(4Q Y6qMLc9O7ST ;v>hEbeB$;|ذ9Dl8Dߵյ>.6CEMX}Ɲ˔A!̏m}lr ekLJ)Ջ"ß~v{`˵Xjᚯ?.?p6Dypd>7pG'[Vr#ùq@'A_-FB挸qn֓ 4{ 8h>FT5++-@!1 xj ْ,{қޮPon@ow OBoWu15;xz;zc㬖Of.5 LGop̙hmÍMRnە >XkFJAlP"-Ix3:Y]FZ;& en#v޴ ).ܨF+@=yΓw Cx"Tۧ\,ڮ]V4vjPmܮ(OClu1L̅9C}hgx.q #XSFS6yXNvKnN8'ԓZOMl$>7pF'+NyÔnUE}|b{%aRȣ DC&\,Ooz~  C]a;KڎjvRK#f*KkPmPmtlbқԠCڹR rjNlWv. Pl2jFP zԓ|y@e&۹+FM̘s=]C0=F֘+ɠ]3p{τ=.tfJeG/0qVM{RQzU 0h M[h7-PfcuFVC<OMh$D(O \ѩ *')F(V&4쏶I#D=€fg!ڧ4,OQ7U.3ڇB)'CV%쨳_7P6W7&0j LG3FFƱ씹Iuv㛡N˴Qk?5gtiNVk$'e\@jj.4s}߳J=ڈ|рI3/m7G{~ۂ7Bt7ߣj)JQuArQNq6aVM|yP!JoUaIo9N!J5E0"{deo&{k=aٻbn飲w9sN7_nW )S= ջO4 EyDoQ3U.z 7%Uo1TFQjbZ!r.o7ޏ^`ì.F`v9U\O^lqP/tӽ"[B27E'+5'~#F3IPeOOTVa*рQ W0ۇfz ^-xw?pqp}R>ڟn_\?}/?fnǿ|_~o~5??+y~!w6?n>:o^=yۇ_e ].87[?=o~bӇOi߇}~w9w绋_yhK۰M},|[\럾7wWgv/7a?~^\} ޟ)]\꩏ð)/^n?|A>ؽup~Ь毮o/1r}toAˆxq_ÌC܇ފoPoc{7?Hss{D]Wnz~~qqnzvqz#P~vA3^!<{$>Ԅ(TGIF?_Nq_0t[Dc7G ϣ#џ^lr~AAڒQ8bSxO={{vI56{}/Ua{?~H1yL`9󇟂* k߂Ǣ,) t#$mTj;hp$@ՎJZyh<_&c;q1ZswƫA3PU5gEA7d 7E~B)).>&)m[vFx̚ȧ(L>:|3d6P1 *?jNsh.yc4.5 qLbd[6N t7k TʝmW)nf$ӡ W#!EDmxr"0Li+J+9BAZ`b-(w.mW7R(V¼kUER5{?H[*WEY\7zr*W*)QU"E0iQCI6NJKb SD1{xRzٗJVƀjTnj7gtT78Ǫs _?v]޲V:ڂZFFp2ARMk2HDF6z2Dt B0dL/㠞Aua a!"tb#FAh:8Juc*SEdZU AfOr:Zϵ@TFt4MؓdK8Wp! (fOLqQ^M"EbT)*=-"LS҃Ίf}2#Urtn/{tSkKѦdq#QlCbA$|c\#dSCI &((&HIOFe#J1JT%1VUCt,rD!7o`tNhLs7DP^aYሸvnf7t!-qN$Yfo4:[p3fy h:Mo"[EPi]!dKɢ8<d4KQGQ ٟj^ѝfMuQTn4ȖL'4SiͭO4)Ga~~OXy4<4aSDT'FP`TUG"b Ԏ̡…x%en)ܼG w>:Ou$*>:12$[Z8 j0aK{hm,fy%Z= :j$FEuA(A&`2&{NJqQ~MalRɹdE$»dsG:&Tr[ҍjW=!RjG S\:Jjhα d['J %X-%HJMFeI)V*){R^zs4jwӧY2mO\D4C-T/U^@)OQitBݭsu'.mКIWLKfTnpΩmf8e%u{HRV65{j,u ,K%I*Bx 3 KX/4?K5Y*֎MbIS6>Y ronu﷑v&XZiD( t ioivI6AE)8k!eSFQʩRTY C";ӎTKN30j'frؒ2娙L9 2`QL<K ;ԚN";]:;ב=Y*QS 1E4-XrĔ$ 1tIJ!H(LJ);fTAmWβXJR^\ښYc5 JYe4yn׍Y+7{bt,=e%y*Bx 3 Ox}` bVQ꽜Ӕ1>Mfg4HS7f/'c_:4u745!ڪ%[KF2av4aeTN@4d<LMTF /A݁r2n9#HY*u7+cci%SXjgnj~>`*`_u7k4&m{Šl*@ ֫ԉAeSFOqY@d<LFJ8G_JE7)plL)\vF*yGqnR827L-LF ܝ}x}Ջ@³Ij8i#V*Ő1zAD`o;5FAMǍ}`6z~ !{FvzoXZУ!c(tU (hڔ̪`>BŜFKPa52FoJL-њca(S{1'֎('z ؔZkmkq:iz؀,_VvDX2[sy'Lܩ<9m2G"#Y ING^9Jݓ%oH"UEzb@[RJfi! {sa umbL7oޘ Pj\De N!*vO֗;BsVX8.k1B5&+ZPX葒r"=KRxZk!UFg=)Ed 4 \, HG'5&_N0qDVS^&8742%oNj?H|1.6Q]Y~'ݶN-iAR1)+Lz=n$BXB )YqKMU,+Hݎ*H2ȩR7=dn\4oU*dn1[<2MGXց$K1>m껑̭d1j[a\Av|TA FN!ucz#u6=YVkvkn[FA궤n ԭz6!3H-)k⸑`ң/FV \NZ uK`nGn`dR7jvn\"u[FA궤n ԭt)n/'&k9Yj‘FIH(q[& 9YƇިF6+߫co EJX"%z-}|E)R_[1++oI07j2s3%QY1Y[&9Y&VY[T"J-3mdoےV +]d=΂}w \!{+ [=`Eՠe.>USeeorLIۺJl5rl UJX*%YMPnJh.qƭT g,KnW g[QUSeljٺ-Af@ҖB-IAꓰIZpȶ.QpCVVna%GFzq-,inM $mz| i!Hږ IX$4E8ki\%i[Yč*BK -,m3B4Œ$z| m!H:fR-IB Ɇ$]dͷz)XYzڪb|4`F$”dhG$hd 5 %sRnfۃ+"[`ZF(A5)R}D>B ֕ľ@yowDid.g֪R9'*B(4CJӢ#l`c$ ((ʇ\fEK3հ42N0ēԉ&KJXf!FJj%Jd7HseH-= i?$mc)+7S'CX)cj[Xe?}S'נpSXb i1bAG8qu [5~'G|Rәlۓ͕duGduY%Tg= FU*\HlNKaʄ:m@$s# ak2 :\y8-!"2oN:DvD*,0ZN$99)G(djGdjIpڭo 0! o@,:dJƤ+FHf(pP.HH'7kOqi[i&L' iH~H\P@"pm.S|2ے~Oam`y.m{ON\7ZG9IS0I=Vdir0i #lH*u[5SRĚۨpu 3ŠnVNr>52.F2CU&$HצXѵʾK*8VzW4R(fMB8@̒H׎_E}ngԢB>Z3jdgd ,Qst+Pw*$hBG-њc#Χͧh]r Χ=|Z&(94U, A@ek]t *΍lExI IYk$ka)b k6uA2d-F\V5I% rZ&$9YK#H2RTj D(L-h R2YmNmT5V![K~OWckO}SLZnp)ϗ$Kɏ! =N6є Be@hQ9Za-Χ)ablD^-txW5Z#Epe/JmP >PM0w&҃&R1Z&(95[, A@ek]& ӵDv![<}Uq-m~Z}? Vr`6v0ر.2*Q)[&9QF˄$rҵtkHY[Hei1/ղ} gnbTk%Nq~)Ό1@">~D3 iKЊ!2@0$#i0ELa&eu*2;lٝ~'GђdisiDřziŨږ9@)R1tZ&$94Š#2 lck0EElapސ,GڲIrX]E͓ckI_TrJZԀDFqZZ&,9YƋE$2 |k F4)b\EB(4C\7Dh-S\VxUlk;H>hYJy[fxcdmE8fLj%k{ g˄#'D [@ƖBuMŒ_끔IT!WZռZ595(҈ttx+H25Y $A;ejWSYa280RP:rCBVFJj\-Jnj6D1jCC +2r5U $A;ejW3yY2L[p:vEMYҰ(Ճ)WeZ;a!́į7Hf;6TA FN Ё<-#| Ox= O+%6o-jRC/FV FbU`[Wc\( ֆvUeej@DT-v UˈHZ&a oc\M]KEX⢲Z% Z5iS,lem% -Y' W$'lZ@>uT0uu᭰y ’͸ȸcW]V8,?Prjdj ޓ[cǼ]6OA64dD-*Qi9iZF@4qVx3lWG6%,jL}, H"HVhZ)%[YiYڬ|ކ*2ɲ4=A9eDiM3I|4wU ՏRm m*diQU9,9\-\vv/d!/NC^ v5s4Djl4厷)djwc?ZCZ1\-xUJ*B{ oM\v!4ۋ$'eɪ97 Q|+$;v' 0`鐋 ^e}tfYItbj%;L !^f“f@xD1~ Ö!MsVx:M(m\Ȇ6jj-c(%vO!k&(pXwIcV>DYazmRN6TKe2DSDӶð6[x36T;D7:eh, []#O뒛>Dn䴹- uˀ"H2" Ria6 Jhס!;u˘.2ј.YۨY$d'A(pKQ41LʭSnZ8Nտh-4mcBdm@Y[F A6dm9AJFHZ&KE1_v]m#dW6ix3@d iXX8n)0AΖ GNR r -# gr6y&I$"n \u'kE Sf)kY3mZ)Q)F#I20U6bc:&$řMefmXL- mˈ$HۆbIxa>-HȘ٪S7S)@\r.r06XϳaZzKeflL8t- ]#H׆t$kHt^oYcHhQdlZ_"cKXԂe[̹!zY6",Nս`-6eBdmPY[F$A6dm$%)]8jYmHC(6d=>}*ȑ[dS6bc5kxkNy$RʺLe‘ӦlL<|- _#M?_c0_+N8#=^Bv2hECkJFVeZ[_5ѭd* KT&vN$9M Q5LJR{0?Qㅋ49Bv$h,^hni/4a-@A6"M!Ot`yZ@6l ~hQB5Tjб QO"j)5\V' Qˉ$'KhhZ@6l1ZG|9PEe,&B kdБsjCcVbM98WE Վ5ojS9ZN$9Y D#H2Rak!H:$J|%EV:25BT j WOjs"."kWEV! cˉ''x)[A6lA)[쇒C,XӤt"d,D96d6 ۘz־KHێU H&؉R5 O~6ރ<,}ֆU!NؙwTX-^^7[隐v!!$E֦jSm Ve8ZlajY5$[N$9I6U A?pmؚc)ͱ9|]-_G.r_~qwn&^|x (]{ >-<>:ivs~Ջp٧-[{2O.@A R_;&70N# bs4 <4Fn bF~ 6Ok#yƔ6 R+w7el^t\xFZJ/2ߓt{uFvؓ_(&䄮S&1FJ 5q5x@Y,P"_iDBt>e=./SQ]HJpmؚ Nf&$TCAbd2m`tȕ B-Y(Zp-uSB%Bᱍ t4A b: R!huE[8woo&kz"<fqw_}ChFUCr.?=x7&|3w }l6.oY_ۇ_7B|3Yx`6)Ưn?_|1~Z޺v qwj&\Pݩ}̌q^ߍs;Ľӫ7yH[ū?~irn,>+ty]^wꁛ$k±nO$śha9i gdNög=^[s%n#ٵls(zs/o{%u5 h>ڗn(JsIF)_p_oS"*9 ZϢ|;w@w痡%cehkh Z\wZ+%("©2h`DB3g(]ڬIIiV)p5 akw oso=G= D4~~!wB֋(=nݿח'K'38{&lÝA zϳqwFxxJa 7P wߺ{:|Rb/G݃qWċ 3g&圆N ]AKboT>}߲oh=cQN >ę}`rMn{&}mqn>8sN6FCa TR?͸o;g~pW,Јh?, @&a * "~J:k2Om*S]@0|G$Q@cG??e@L&=L}ZFΖxZɅk _Qm8L“g_Ͽ7~E 'tWÌo~)bph:ÔxνD AᩅvxjSxPhxj{|-z]λ qν <||ؽ6_A#XaL2>p"ܠ9[]ڻAˌ^ҍidoTfc)Jo%$t`;0u`tفi{pxڅղ ѡʞGly_~>Q$eZެmB$1k^{7| m a |pY駹} Gr4`%Y? aˆwh᳁~z4t3ru !<@o>F &]BATdEk#UMH\)r&tz0h| %qp]>(|0#>+?P}ѽ'7:A\n0",Rk?5sG@( K2@"/Ҁ7*h+nxOwk: Asq 8sЎo36v6^h㎗..v{!ݠ)%զB&ߧ9 &J  ,0,V8q\!y5)m!>aa t?6D+zۻFPx&3Qb=ʺ׎|r[:s:D4=wЏhd/ˈpMs'uj+P W {\AhjQ(n7!$àF?eA@\8 xmvp`6kߕCCDxuN 4 rn gcymwh{ݻo̞FYlK3귎q{GS A8Lg""t(^,XIfH.`!}o85$],WIs Mx O_TqRs CN:yp:@W܈`C0 aBhOFt#:&X&p 4ep 0 AE@f QgBSl#),9PL *;Ж>x[ Rh qݽ68v"pp(La3Ql Bx{ "mw?  7Mfl9*~GK  ]>U?ɫ`]=A'迠x0fLzSZEyL~p`Ӄ%:2(5R +i.\S(p(dTv_ T`nGJ+wz t, tuBC0V?`Fh^^Sc`oHcro"D 4 K=LCL#m.*Hǀ?,z; >D *K Wb-V2/ Nw3ovvy`tKˊȰI}#-]^^1 ?""$M/D CoA#/P!4 `dSP}Ws<2XH![P' J 텳\,u~8n(+MȤm*(X;%@y:jC_BXGCc`J5p`xZygg"p040A->t$y:݇IN~@sx`]\6@t}dR 6ٟyKMr0w UM9s@ u `g@6^ϱ+]Ȅ@_ZN@?N@@Akh(-sA" 8D w>Ps{'xLzgbhh;`s# adj=@G ^`Z€W5 0GLPcȏ, 7B>`LM{&}Dji؅/-dw5w=x74 \XPŹ_ 7(70 D!C Bd` HkGЮC8h G s0z$ xq`J{OYcxR+Ȁa¤9LME _(aB$36ȗ /4DAA$ \ú`T Cшt~df9}PHh7l㹹נ(q謓ǥtCFAmGA &D@:P#"p 5 f* ~;d9@t=`x95d@54=tbx`Dp{;(:xKx8 Al 5BއSB.e9<0\ ,`{[9XA& zS]68?qb|wм /E$qKSHP1r pwy (Db _r Y#Mw|I$a rh!KR&\>[6 +tH~ G}8F6; 8?#hX7nBa1Yq #haHNs:R]ùW kDQh8_~ S x7hmZvO]@l3uOZ\!@ #CBCyh8.,0Skxgs#cF]>>x2q4SDp@)U>@Wh'.ұ9݈qsZ5 ƕw _Doa-aerD}-%vu|x^A<< PFSaD-"fIhpq;oL,v)2s.XAy4 a:(#2@_㰗 !@!ratRPEaC `@@аQWmǮN' IA/A` BE8 mrwSC/z~яyϏA~ ?"{~Lt=,=7cN1_z1.1!LZot]@It:LQI΢A2`RKv/um9a *>2$ 8w$H0L H\묻xqGl3=F98>!M2 s/h"@L.;]g!bK4 fF" ` W ) /"iͧ`% `&8Gętu'4{4l"z*e>g@ ܾ:< ڽl:XE}lz;I磺^ ]_Վة3G;s(,ʜ 8xY!T 1eK@P[:F/KwxK0/XƖ*_k):CӥM]dO ON8.B8ORmXC|hi竁+K1_8лbJcۋa!vpH1pS~OL_С⮫iMսXcG:mKگۆ^CiS [Vi/':4`T4bK23j~3*l¨gThXB'L\ pbo a2\"t୳ k %?7bav7͔Q P2Szɗ#'HI8"FI`Xp΂^`6ˁlbqM`vB@Z jp5Jwa&-`V']Ig*,Fdɚsdji`dg9y-4?eyakGN~`>Bcmqp\ÏJKr 9jAi 2Qr5k"pάEh7,*3XBN ?TpqUaJ5yoPI( 5^I>@~}UϺDuUwF]#8}횐y0K}n+4!rsn08Gqj4"7^i0vϢ 8^]ɔD/~a`S8Ep\<)hn]vL[d᭯~څU:?竛틿}~ww[\_i^@1(=/?6CL;fk;o^\\uP5ΌoKw)M#߿sN%)I+*Z9Pop#QwYjvQܫa\[81PW{CZswQ mq԰BzsSW'o~ TLpdzv G/GѵRkk彠pTe ]L9^d'񠺮%Sq¨5;\0Z:7Cp2ݵtu QQ8Ĥ&3|wS]df!u=%EZB>pTW?A+/8ʬ=1 К\Z^~(DHKgrVК4$2Z {6ۄFA5Uv'xGmW2B OچPjH,e^K1Zڎkm{3Zzt8eb^K X?OXy'YzmfBxiǑ-pv\#tZ^K!-FkîvCo%Gtt9'] H)jG#h W\~][.i>If$4.>$ɨ]omz+>?0lY|x O`!~iwˊBܟ~peadџc"yb0O(ܟ?.eAa$fFݟzOS f+Of_:n $sdI&€zK;Tnq?۝2Zc.bB`. T>[0bu[V'զv&۰$K5wƏP\ψC HaZI"B6tF%8fuV"ҍbS߆$K] #mrH/LTHaeE|X+1a@AGQkmMb$VH.(ߎ'rbID E} \8 nؚc A?< \* @L9*pԜs4? D"vO%$?)cCãױܬVUnAFW X~QQkB.z|:~ n!؅y+a0620 T2 ̴mB@\,Su]$뺩5$k.BLu@2ᜰuLX5*`&@+$UVJ}B9hkcqA:)^<} VTV\TK(XwGk>Lks'EJ_k dDdoKK 7ʲ7e͵.Jq( &Z;gL~Oƅqk4( ;:foނ,'=ѹX| t3~ )\ЁnؚRθ?Sv:N]}R" !m#w &S@ݓ+2G19!,CoNz}RMFԚx2JB:l+KppbS̼`yXXZ G^5GO"p$,o쯵r^\ ̐ʐ焱,xC?²q:Mȭ݂Z ݮڳJBb:!=Ήd91{&=փtfP"_\Qo9yqYѴ#[s|ÑٳI9sdSxc*[lC\ 2Ee[:`ϟ)\!C3G29q,?e;uar/fWZ%x!A ; KAFEpE[ׇ};\0UJqvCB|t CB2>z6!^߭k.cqxh* I5,$[H{$e?đr_pFJ|$t Zs$wt.zR!Oq # Rm9>xx="9YBANvɦ*vmm<ӑ>AۥxBq^O;ELdiYZ"$Y7K3=6himg#hɿ/ R(TR5fEf~^9nK?]ǹ$ק@i8VJ\hmY[s6!I BJ!X$kU6VR4"XNfMψ˞>-5~ Tp]J@I̔nLf NŇ_M|(IN %p\h=A#6occqg|%HY!H!T+oXkH)rwN) 5!=7Tp:p,{F`YdpE oe45ydpa%P\p:A$әfj,IM/.~ :qs鯂#949Ri.9!$7ZTVո5b(ո蠗deF&W$YV*7rqŸj` qMNA?vh7Qf-ت[`b[q>]q+٨!6zݾb8wG)>/J)U@fR@bvvgU°i_|4Ӧa7bic9N[`1}膵v:xkIbHaǛt+n09acvz2o+Zo[ocdwc-y^r K?}MօV&}~ؽ}wu7 w8\hX~6u^Km3.;ݵ~ E]?Z к.Qj?Zps=ǗקOOPKQOP+/x ZX>*"BH_@+h8|v!rB1~R=oՍј!Pɣվw7=1c1kF/$k%"T+$^q | G%FUX,cy1S}t1Vͯ% Ysb|b&Ln8*ӫ s%CQWz#$ Y^7E`Sϸfk2 "TZ0“Y&Vc5P.%8ͻa#QKtLˈ+'oLc2ky< Mi5h"08{,1ҰVP!+O)-tM29w5ks_PKm8DZ;~C Parking-Assistant.fzPKBm~fritzing-app-0.9.3b/sketches/core/Photocell.fzz000066400000000000000000000342471272363601300215410ustar00rootroot00000000000000PK8D֬d-85n Photocell.fz}FwıwN`/uhl/Fws&{H%y'NOG'ԝ~l-`L?'h.dW3߾f:S:}~r5|™4t~1ǧ'bO+t|1 *'ɜ /O|t~x~3i$p_4ԢoGǥdp O?X-m{hr/ ٓd0O~CM/ӏpOٺ-/b4OꇾYU{uPu?<~>,P";szv/0;uP7b4mᮗo^TTPo Z҆do'.Љ'R>irz~b$= ٿp:;;VV*o>Y"Ƌo~!y_^'?^$_tM]_򒮪?Bճ~ǏGMg_"X\p.NWo_hsxtxeǗb@Fuo[ 'xdz3Uq?Ϸ^2%|-t~,I>d1EMMd~<p% .A 8'|V]NGgg *kQHQd8?,ý9u^S>N'JϠYgɛY~遼JAN +~4H.[ 8&y>OgeO:%)tO_qBg`֌U.{c/x~ߌHy)MƃfLQ|O|gCIKʕ@3SFgV𮊥uKqbrnOV/͑GUjC^is"zp7ީd˔igO?ALB7?9Xi(çl0Ca!2N gP›3Fs W<>gkJ` o)24$fq/Ja3 j*bݽmʪ"&Ъ˥(^:R>ԕO7ں֬kͲ+-; /Smn^+ MFÒ;}wCV8\+d^+لmӫ| 0}LA~K(yc/\p &o5˸6y_ ͕6E%JXR$9A: @vΉK(3Es8Ϭvk)EfYn:*DM#E{¼Ji (L:f샠cf;/zLb,I87 >h6=s=68/×YMR!Yy>{:IX=/߼+}z'XCl;@nGߠa32ʮ^T Խ,g ͚;bGWdq+Żh;QAXm^`|OzkH\˶M{{󶳴UvbiS*W7T+UOf +uKuXi#i> ]'-}j[Tq9vQvۺlkݙq˔X˕ 4yKNK.0t%<,7D</HPs.\!Gm OY vijs[︼A۵`#|qs<{Wњe!4=ӎůR!hM(4w뭰xp\2e~MEUۯ + "gfߟ$-&$gM-8l oxpfzG"?l o6kщ[yi T{nDNq{|S5}&pT"&7Hl#7]%~iow6D}cg%=8/ji:!b3|+־R_ZiXq'[gۨއ'maD7d'ǝorw¿]}*q[(Pǝo;CԪ:R-NRއڐownXJ^ro}X^}fU+Jn*~ب!Nv tC~mrV*qۦǝow"?L+E~ܹvsF-[T/.J>K%~) 9aqgeeU$fV,n7EF+ǣۻEAb]"̚Vo!2@ܬ*bЂMϔx(Dfx$ӫkmē ^JQ>xM1׮+&>|FKfF(LX@I(?gշlsIޖh;a%KK$}8͹pDB*Q,w9 6!IhM|_r5+aGm<0l Ni9 .jЪz0ɿ)')Br{|1v Iw y`:H%u- źՋt!2kn硦tV>o7V .x Z﷝ʌᝩ(+S{2A{|25÷ky{| 9Ehd&|}g<3/>vv~,z4qsxhx'yl/}.N Ei"r;NdP^+ zo9T%^bɦ6p`;U[} |EꦔɌ;Y) xtHka:_X<PPDG?>JK2&"W2 R[HSs< bIA2N]9a<||wlO -jW;oQwoV׺n{t徸 OYH~'R8T1 ZО̤^zk3֏Ӟ{ͤrgRm&Y{^UU<{7jUk-A7R*L"U;d!P̳\QV7hT4lJ#t:H904썟Og<ݓJT;|V,SoBJ!m&SVE:rՓsTwfC)n%gYhe>^mT}6Ћd0kr^ާ 0j-7f+1wͼ_Qd*ӊp)$3Za ;۴d Wf \pgх ü/`Gi2ƌ|V{&S;_=V~,?ϳ0mݫX1㪕t&^ˆLN[6Dfyu?Zk;T!vu"nu_6z<1 s/u b^cw8ϴGḂnTfnarZz~N?[\ट~n[;]|gڽw改S+O9g=ӹa[=n`|R5/ m}y{(pP;;|1Ys)7NT9`%q\8׺eR'}-rr<ΤҰۥ;_Fܙ;P,cEf_ խ7K$/yFǮ&r! my3U&\< Щ mn5/Cv6TDyn!JNqAG}/#h"><~(<.S\GQRNOށ ̩܈pvC^\}od(MOe½ ڮ;Cj XRli'7q̈́/Maddhe4ҰעXy!S8`]Eάlzm#~tmōo`hv K Cc*`N`Qe"L&8:2p,|߫u0(qӖD@Cyg/A}$8nj n'3i--3n)BvcFLķ1N];B{Nl=9)CH/ GV:37v8qXE]nHxq;iܺmרӣ\Ș:K$8Ba43/Fx֏跨*x7PwY UGŪF~/M=? R%2gۧdzsd4[ϢE];4q3V|Uf}Hi8wOM]ķ[i Jwcķc2irݸg<`k-BapQKهaK7xWps*%|I|s[Q,%3R4m |_[̃vmj$?: א/an'o#Jg*Zi<7L3^8ѶVCKv%_*m{J: ckr l=:s".kmFKE2o|yбdXGӒoZ-elX9wn4,X%uM|)|ԍ!Fܚc\նn%ͽgn"JbcLmZo-~7WoqfpuVo61gL*.3yUGZ+Hێ ^sĐ]%g~tm;Đn[? 7Kld:nMSdJ[NIJj;ܚn =8y .׌q  Z:#Tb'&+u[.iLbZKL +FGrmk]Gڕ6\՚cd-ekI`*bk4F\a5E๖udmGIՎ}E#S67<,YL*$vϫ0F#Mk)۸+QZ1e24ˡeD /ܞ%s!6]x' 8 Mkzaj+dUډ%$;-C[#l1.QM#eN7RA،}[AP꽃a 0}ҶܚnI]R7 %IbaVn1]M[A%Y$r{7D8ɎiDGn>q{H[Si>82_yVںE&Oom1N&z\A$I |t~(7d^hcR=n?\_8gtk|2&Ͽ8K|Wpÿ?$_Ӌ|e?&a?Y̦h8C~(7a.N^,MI>YpuIv(h8A'gq!?0_/A {B! NK9tL)P̜c)mS>c2 y(ֽW plp(Ӓ"8SneC"e@!௷64K\pt|NPTqhyx `6ClIh*wƶb[5d>kuyk^p0q2D]Et2\glCexI “W"XpAxC. (Oo~DC ye yUlx7RuU. H #B;|a#c T 2Gbu-/Zc *|A׫t^ }s1 Е , kޒl\&LҤ"*))08Ox|SMt=:T]{U="m9h,PɴA6].m8n|ö H`" u'|rZ5+=N9-ÛsYt(ŕ~Xiqy?jQ½%y?cj䴷;0/rA̡ߥ(DDa NF$?^#ءфjkvm}WЛVc>2Ǻ!IpSc V8iI/ENThJ>PpT}.P,6KDQ9E 񚀜/0CQnd O7w%ܠ6pbg@aꡝ\g |Chk];^K!vn)% Ux.Dr!4&aI_ D @%0neqLqn"RDt08Ĺ&񉰤nA ޠ ngQNvQ@ 9r\ؘB1lCa6qPe }c|3PN,(aȥ}b |M&qc8wˇQj{5+kF0_0󂇠ǁyD7^]2GL^GnUEMrCVo uS6uW;C((!lPR5N[rNu$遨4ݱ-j6 $Vxx=ӆZ4GŰ Nj ȥ y T$.A&@0gŒP~у0 %83) z`('9ʘPAO5w-蝹!Aކ`:"EQnHNDԑFT$"S?Tb&ۓWPxWnBO pE4X$"QUPcND8>`bH%z5 %".pU B&+"qBniMt(Q; j: 2k% )5RM+e8UC9S\TCC\b ECnC3?"CA= FA tGy$8j hi( *'I](! #*CNA!(=4 BЫ6-{)A@H1Q7W%c(-MCuOXTcH DEb]hXB!U*Z{ 2AeWpĂm !3O%Y7Ja^Va #02FZZU ^!!b'I?P_" 7I0Ј PtRwӳI jL>9 ?8GF d71b& I`!@Et 7%RJ( Lj$0m0ڕsF3eC+(K1Fa'9 ?w G:Y,B@4b$p4XpcQМI[ qoI^/iۣ D[=͟0O[U$^E"z)`VZ"z A +Z%*{TG4@0ՍvMP|/]0NP1L`@)`6 nN2,NaO 04oZK٘.B_5yؘ.B/J"/._g6KrbR C3 W><ֆL0&u,KMLrղdoY)zSc qU1I{G%> ?ugaؤLZMP:9Jjëql "'`=@B8=Mbj]Цtn̳lP9 x`܄q"#A(KG7&JAc:Ra`.DA|v*ؑYѻF_!*{%Ds.H%ȻQ eX%'& b:ꭦ| B-{RsV$ɹQ znQȇ륆u2xۣ)GoKRG ^Щ]5/ȞhN*N#pE6[e{e*^РL1,&zw1$ o:BF1Rqi1 pԺGՠ7#ڕMK{-aP1Fh%POY P4OST, LâPiBsln;hQV޺+CM41i/5RQF>d̽TS:(<*WQhQT`iTJnW .0bG48⫤9!3 C4H4 RZd4Ë2w4Kc~X #z4KGI8E d!qGj`}` ^G;AA "sI0aQ#%hzأ3wҀ%) 2X02F&ZT ,@ vvM._z$IS"KD!Jd41'0\뉆w=y^Ѽ х|*zy XX2np[LRNWuW~S,=-{DLE5pMDp \Ʃ< VQxu a'6.5yUu~-Ww&Nj"wzy@ˎAch鱫Xi|OʠvNgd0Ҋdqc$Np:x xOqK YiH E)0FatjwXZN$'mobV]Sme[=!F]: U>X&*[kՃ]kPdi2ir;,`=6I7qL{CN6v[a#7C"SX*V%Kwm}dnuޭ7JZ̶ hC~}}ňZ#./=zXK=Ynڱ!JkH{9R{z[Zv,f6mH]n{7D;˩U`'>T]WD{anŬ-MH iU[D; ǮW/u{/! T['>Vi3]X4a&tmk*NO޷=}oZaK<1YZYOxL( (HFgwY;\~o/fOpt4`ڻvg珨w͢%+n}w߿o0f>[.<n|O^I~SU­o~:ϫM):QԂz"=)O+Fʹv\2wi%]]07qgP?~t[ʤo]Z}ԛR?uR8Ɠ~mwxk՗B+h8:+_vԃr5?7ВڷIugo@͘}%I>{iȘ1Kʗ=SnDUiK96/ W]+Y;sI{*o*kn GSUpb{^(QX7,}R-'|51OR'=-jŲOMHLا#pvf}ک:i''W +R5iܧݯz pW +i'+SB&MFq٧enNik* *d^q`Vgqw "_;qvn~کi⧝ᯟ`Rgq7Nܧ'v~*~^gq7Mܧ? ~nE| "FPEEw]2]̳}wb^A5|}`x؟jWhQԶWZk$FH }[ÒJ)^MƓ-hs\H:!8.Pw+DK8@ep qN xm9 TՂJwx0=AVѬHĂ%zռ~9_0mwY5lk}ƵF)']U?-|330Llq w̲UIש3sh;u{W6Y({|B0l BBCĭoBw; X*y6)am5g8E@WWT43RRY/ghF -3xİΫǟ[{/oI~VlÐú"Aj4\A=cPw<|YG‡Zg\E*"i*ɂQ"sE#6,uG]4U ?>F+(gF*\QhS ~Zj3M<ϙ-̍nrMr.|*kTXy&g^6ݾǦLxn9󰁑"g=$k ئ2gnm9s阆ݚs˙1GjUwAL%E؟ḁ5B^rSiRlJѭ)E_/4E_G粧}7)7M>MћiRMRLW{rÈ lR: Y]B;g?m|o,RBV'kZ)zi'\&ERu Ei'ѱHb۹$[g.YlIl|97{ :B+X`>aSnI d_@:zaO;wlJ94z٭OF&V.6 WTh|AX X3fcX^O_쫠ط|x2 _s3(%}|!LGE[G0]C=D)|ņ6RUm%Y< [&R/C&kR|24\m鿭AladςOK4$| !XZi->sXW"31Gƨk4֪fj %8rCkL0F k O+]HhZ3 d-?z#&LJQǠ ak6Vno[Op<.i??7ckؚ*i|4lm_xl?LL=G ! _"*Bt6u"Mx`^,XJjBթ 6t$[),IҸ1kD LkigHC M#4߭ʚ"]t_{fb2l Ue]j Z+lvCDjh V kG*"5RͷeX򼙚{80=G ώe<[ԄF%L sY?ξݛmwyW0}u:ΛUNۿPf6{|1(O=nfC:dЛŗK* ( WnkԽvmm]7f*g;a5|vz颶ܵ[A> fhꑴu7G'ݻoZg#Sls /[WU_uGӯÕKEr\^(;f8v'׃Q4߽`w'N W҉ 6DEqht~yf?}QК F`>] u @^׾n"ۻü>HoZ mL<'Xquz׃b0|^\?_w?>ܾ*1exwuUhxu7μOZx|K_[O;o>hH|p#0dx,V @Q |4F+s  dnЖ_9QVp܌DF Wl_t~'?#;\M/u׉uq+JKMƞ1>aY; *_?#J0FL- pb{7yV6OPɉQ˖iTpQ_b{^I!v\nwN"dU.XT@PXE Gٲ&[?bO,PI0bO.A&Jzf%XYCfOC*[/4Sq)qE–U)06;0J)zJ!bCVnqMYE޶:v&oJ aWbW ^n[}rܭ2uȅۄ}(8f + 5.}MC>1ҶL0‘=Vnf+Zr`6bpT1 kczħiUMl`ST6-zoV('}.jZ Q!kD=¶ﰤk{[{i؜v`ҖNb&.L@+u_~LW34 [Ԛ]R=;x\\)uߤ#hxSOoKCX/՛eS>~0\ aR\]$/|֑~׫iH3>e Ĝ<g_|]zP`z%o}߈ů)˾n﮽g H#k;nnjc;ԥ;?[h;K};i}I<-&a",3K/jbkipەJIE(ٰD'`碹ro2"ez. +<%4tn8йrk% %Er$ %ۚ P]=}p]Pc3+ i[o3y &i0!lGۨzmzys#ZAs9xN%=B>FY;ygωNϯ+f]$B'sLJ#V?ۿξça?ƣW_}zfW_]Ͼ}7_Ǐ߶׭yQwyMokw:7݋NǏG'ם&ݻAo;P<]~],ZGIȋ/ OYX鷟ƓYQ+Ɠ^|Aza0lhTh}Ͼo`A3z ;2f9 w™Tӿu8yd8{P^;LhP㬂k9ݒ.^5:+LbV?: H5m`Խn(-UQ |d(sM?5dn oޤ=,$+Je'*)0` ),^"bfd[XIfYT HN c2GpG%-JfiɡI)jcpi.[,Mf p.: { V ʡ=+䠱C]& wXmR4sy]܈y ۂ 0׽=Y>h oSȢWcJ|j%yo{9{ zpe^"$u#|QD w7*,pν$.Ԧ nJ* pXosN.3vIEh ];;AhWjİ}uE"4&aI_R"L 28 @\&0(EDW SC mKfMLw+8` T&`EvɽKYvlCXA1хF -U[p'@ W @\/,[ʭ/0BW0B jtIRYj첸y A ]hC%SȾ ;FgfSxo %,9L d7{t`I`e̜sj3oO9M^- < DnyMrerƎ!kb uS6uW;C( ,T(AӖy]2Iz *Ew,EF-I0g|{@F<$"&rrPA\ĕ`P @,=P=C8`@9̐QƄzmA 6K)h rCf8D@I ojIJ'Q0Eޞ»v#"gҷGXH;/I.49ɏ(u"e$:yR!h㆔"\ B&+"qBniMp(`樝Bsz5\ sh 5SfHЕiA.*ɡ!.1CJB!!b{!K΀ wGvhI q3^ PAO $7^ h+BAzhWm* t$ȘEA7x0JPZ4L1T!53h=:A?aRe# mg3 tUBaVh{&Ɲ;8bAVnZϼg %0/0I}-ͫDLD!!bN~0EN$&E" C#nHoPȊږBNC ]KMBC$.TNQVI|l!rFq0/z9nB/LB؁2%nJ+ZPLAmI`v`+ ). jg2ӛC RLFIA"+` I  :XTp#4gTs{үw%m8ўyVC&a_bD־Wm.5śU^B D<MQ"TR5 j^# 0$ ShZ C!A$xfAcH)jXF^\ X16!Rj6&H>F j$!' 5a %`zHPI`@8 )?+-."Ht`YE*w0/ 104oTHِ.B_h!]^Eh!_\,T@, fZP2d k%f]jB3L]%{/P~G=a!u0 A]Ct3ç\!c:u 0lD&-PF(rQ@ {yE 86T0J!LʋBA1JΎ!)[t -$l: #a{<XvMJy2j8V5Y c2ay LLX)p. OQLX^D#1vCFKY|@GwUH1Ԝ"4X1&˜hL@1 ]{ymNSsodN@0L"C ؃: ?F`$Z"Wƌ5BU# 1rh>ؑ!y)"WEZ@)BPS;ͩt $~F'c"uc/{ ct<b DcK %|"x0%ic 0?T:5jԝzۤuYs*bH(t(Pe9~OG-ب{ٲlAD(""%%+wE.Sq ,eť"_J)o"!͎?ȮED 91q|1hPIESP$cG[P46N9]RNJDNgJiB )9rY(S$}8M T]4;͉eR/']`(i@$3I{J/#9:A)A1?ENdc(Jb&Lѩih_z@ !DˡcA"·gK/)i)+P$Q;K!e%1mapP B#DAQ6ZO}A00BhQ 4~g9POt=L;`:0EgCMGQ: I4A)3S Cbs#)E1(=GRU%yB]hQ A~-Ϣ)B%aR\Rқ'аM n("sG>aҜ&ds%03O)@- *O6,P?Tmite/ d QRN?$$d:͒!! WJrڶm *M~$eBHQ4rX ((` /,Rw+o}P"0Bd|!y e[dA)hL\*: L8^;2 "x׀4Kc#zi߂Єb ➡ԔIDrnT^[1!zE h#R&tJEj 0 E}Yv4(FL1"$ЅE`i:rYwԺՠ7#ڕ2{-aP1F` =[K̎}=Oh(&JIqK"N2ƞ1i:M> lMl:*<$QYBo A δ X9RJҜpPpVB+x>S06-KP92d N,蘒y.sQJ"|U| {X-p)?I"ޢH{[REyj2! +Al!4`0 ArFzUFj M f!Fs-~h)!crPSh(/4T-gla/j'ZY T Ո\_J[!TȲ,}HN8mAd0@B'^&"Ve(#y# 6쥅qAYno$c 9FDOH[8hJ"!P7 2` q.|(I,?fJoH9Ns8B3FTY\).p PMxc<gMjF+iؕ9,edi CUF>e4c,ͻ^35:*1Y M\G0YWdyr&kr( x&~W;v9e, yp?4;?vQa±''^@K'Q:J^ ?B(E )_2i4 FJLEh7 oݐ~ߖf$U_HVFD*TRtMIe&`4C$* tH2qJs+(KP|"0z$LU iEl ?|h``yl*Eߜ$9P[2R]45ܒ4(M,><̳D0,bȘ./`1!MB.MSvX5+Jyr-4Ɠ],r`\~e3A<;X6f5h j''IBjhl}SS&)Bi`]a!1 Ka/f~~̏Rx娫<y`xTG)--a }9 PTN804C("!caɸvᶐҥ,u搅~S—,=zDLE>pMp \Ʃ< V&j asgxªO鉕Kl ә>V2O|O:i2};o_g7_oa*вc1]kн><§O}O׃QwҊdqb~ME-/Iܵ6b12Zbk-pHIKEzc4^H+W{zTksh𹍠%,) 5/#O$d})[ `4^ >ǂiy9QtyA&TYϒH巾{a+Tp&Qy/)Fr6鎦Wm\ລ\oYp ‰pArXB~aᧄ,\򴓾nDU k4̓B`WzbuZ682v/6V31_ w7BWE<9ղOέU?yw'!;DMQv "UsUi'ohT=Y>a;t!ZDYFS*rDS_Ic (IQ Nv>5vYىŽ`k`ZFĉkWl FG juO!9ö=vˉӃ0jH Yi}רpEob#7]Y1.bE'|Mݒkō>c`Jo)6MҔ65یB1kΗn\EZ8yq~˅9/ZKm3!Uޥ683aI~S2vv1P9<adI`8f(Rcz;BuKGVh90&yd{U\BI>!q5CI&&w!Ksɮ:P07y@4ixαdDl+`&-IGo4,nuK17hx[Qd$䓣l+}mmhqKq+8@̆Rakΰ a FcIv$ Yt<5`nI#GLo#9.ڰ =Gir~f܁MaĊ<XLѾ )`0lէ5E6~`q0@pҐIն5ZY{ b z)g]J;2cw&dCb(t'qJwĪiz9LLu)o+gGb05w6W ')U-hZ3bX#IC;䃕5F5 wt6WW .(Djq"Tleͣ6z -Ѭrh}kn>[ؚՐ@ PI!rm&z hҨ(!gZ~ķ9kYF3BF-"ø$VV)nAMaƬ! [+< ICw# <[Vfi0Mz2Su3NἺr5"+ai{,m+KXq`\KMpu͊7ٹ-jͮYx%u$ihqi{]5Ki)!"fTin+p;(hM&<䄮'/jTESV`U W ',}9't S[6!؛]fCͶwwUY 9v4;7+arFJ,p<|0(č B&|,{,i+fxҬ2/-RxeJN nW M!<|IbOZ<ٝnBiڲO[iJmN}HB%1׺2 kVA9VRzbqI*O]q/ 8HC {`?D9l=PiINsH-?^vPKV8Dl2h9 RGB-LED.fzPK89fritzing-app-0.9.3b/sketches/core/Relay.fzz000066400000000000000000000432121272363601300206540ustar00rootroot00000000000000PKA8DhFTRelay.fz}ɑ1wP3d"͖L>m(MohЊU($P@kP dHdFdb'<"=<>#> gt3grz5/?gÿˋw=-~s. ۜМzKC~7y]s/N/fWiM.f4l~3xų?//tKH>7_ȇ?߾nb]|7ޕ]\=?:ftp>W}]lPR(Ce?{kcҼ5ꊘ+ͯ(e-CeyO\ u$57/s7NA1j.໋2׿=Z _'ju}_e/> g!F,9KcT.Q]nĻ( ϻ>2aw}XrEQJ轣>.@9؄mQ\;QY#pw< >2>K#G(.Pg^LRww+FLcG }rUYǩ]Iϻ>Rw9QMw=}t>(k#yQ|GU}lruڽ>:qw<.>NxvGu}t*~QyWG)3R]eϼ>6yޕK{gg-Qmϼ>JSqx;jwkavTݷ(;Wx1ྫྷ^b0a;~^\KM4 qEŵ}[.VY],nxp6}:q_\L׳][w7~.n]a^,F˓؋~v[?,~*^L@0!(%&8 9QrGƖX$Tݣ>#n:,_polSdRB1\P&1bQuju@n[(e&hٞ*/o1lnI-ZΦZcO4̦:1eVb=AuNcD >FP>J0~Q^L92krJr~*)3uĔ9GrgαA8rͯ8Bשpo`H>gRB\\}C"o8!qmn6uvj7: -zog;F8WN Fh_R䊉:L)^Dc?FfmhPTXC.})OC p4esZjG=8>]D\sʺ=a}@# =5{<Ƨ7oøaRN=. OA=I~P[3ȱmɘezfo,v\zLDUu3073UZJU)];w%NkrɔQ[%B(/F[@5J3Hצ5tJO.p>j?pCb߳}xo2Xz|4..c!x絋x8[yl (Z ynr3!r)1EmVa5E,rI3)*qgunԣ @xELgEqS~Ҋ+ݳz+3ʻύFTzdR1FepW2n\>(JF5$Eܓas5:KFMł+h%£(q ("zopBVkv|^˦#M%kA]xt网CV#9wKuOrLQ0]hl@dSL6&i-7:IrIF\k5D6w݃G%ٻcx4dS2k&莸qͦ#Qxw5駚L5ljb5C4LSvifTTӔbZ9G=5;wm%_FHfVN-g/AǮfVOr$晰S0z<)!Ǩe手P&snire%"0}Dy?@y\s#9nwkfߚˁ{.XF{6&ٻO+dNNu `s&݅8j7L-w;Zc~3y ĥ qQ 9Ph_9 {KcpZv{k Hs"H ɤ6"52Nf&莸 Lr$[.zF4}DSL4ò i2js_#6brn4 9M. Nta{nmAtBKiV6ڂ(hґX84*J,4V B»DO$n$3;K ǂc,Vi"&*kp ZJ0~*Ӑ\ "g"> } y/$v2`/>$0>w ȓeAH3Gg7t$YOm@>Y:"K=WtR9_:)p (2$5Mg8rMe8m T*$v9k*XXėHys&4 ƒ'oKMh-wO"Q2yMry[GomN4C긹k:)uP/ŵ[S V >2[s$XgW}.DJ]SMMsރ[PSjJwI+4<ޠT $;LRkW_3槟̆N5,'z||8>/.&hh-ٷewwŧv膆۷ٳއ{7?D~̃MȭarKOy-"~.7];-a {6a =%@eO@_ix7lRNE],faX!|4X XDa !hL)~`ĺN&uRaBTt|fKN9697]Lmb2GR/ TrV.7(!MQ&W=^aj$AkqůGB: VChwl'ڎeG{-X afmn a40ZKq_XU@E0sb uރ)tA1rځӳ?p-%CPBQ:!IGP#\)^m5MuJbcxզK2]3Npc6]l:{p1uߏWuﻟ/_e|?]| /~ 9 NY,?~?|:{7_ncAltȯWx->y|}؍ ,g=ƿihp|48-ƣ}-l}]}?Je/^fPէެwD]_܎Ɵl:t1~~>wʼnw7炐wb:nwI='wNWb4ގFnpMܷ|ϖIW~[܌hѻ=o*Wԝ79(~?L{ʽ(l:+z7Q/o6\3Ÿw78Ɵr迻'}-5~ G{+b b42KXYQsxc&n@q`Z<ߑl?de:YY`2X=՘΍$XX0WFLlREՕm}LnRDbL1]̻J-j+4?+[40|jlyw$ʯjCڰ1 NIv!"64e~f8I.Z&jaiygE)%BcFULJ`T[Uy%c̑I\{1Ʊ6tpqbKJ5&O;֒[o Hʿ7u ZoA8Lݮ̹XVA.'+啳m2Qmѷ'Ɂ-T> +B"&un@ a~bo))$<\Ip I&O$`peg  +7e 䉸pI HnkI1s$n$nr8bW$UI;B)vr$y 6ܑD* d[i{igO~ɭsoCLIJ0%%q-DV:%%mm5mkj"~ ŢpKL-NIL!xVđS+= Qk*lNNՖX52iҢGT֜&RvmbNJ.%H]?3ER "DT$< [Ibn @I*\m$%6pr]٥UMJӊkŒeۖ֜ N, sis/0J޿Ð$%MDUk?RA I,ml,mD8~<`}Nj=T$Z횝Q6NI.`%%T[p[{Io @IJpHܦh&qL$ 5"l|q[BhNрd;NSUjx#2oqJ"qMS֪c\8jkN%i[sh;p ,jBEĻY X; RRdckHd-u mg"ob[1NT-pDۢDDMFblgC"V;Uu5"X?l행"w㔅}ik̿ͺSaّ$յaD|@#I50H.E(Eʚē'[U֦ojh)AISoH12 fSBB_&O.5GrhwXKÍ] .Nꊁ3GF)ua|)*m2w PCx3C-(5!AI)MN^0 FPsܩ[i-m9Bꘛ27 X͵&MmLL޿p%ߚĔZNwkERm5GVEkɕ-y5TiDT":fW70ގ+JQKR!y|%o$$D'_Բlr]4R庇/~gߧdp=-~M]E*b2݋M.o˗ŏBSh!td=~*VzА907Z<gdMGC-\vh8< :Ju~an4aq>tn}A@bh\U fNaC8wK3rTkRrqkG$pFQ 44"\Ӈ_l.Y3$\fgI0fDFhfwrS><wU6EbWצD:M1צ5%Ò|?L7.3 77 ڴl꼻ŧv38qb} </|LXC X 9jtlOWQXƔ=G\;A3nq RڢB*q#}a.0Z[[mTN4J\[fڕ!%$MF+mE;.bڪgr ]-݁%i%{3`oe:Q-s۴͚$cU}v'jڰAhf,l(Bc-PJlj*5(O5$x$ښHj+{)d\Hbja$,LMlhmFbi'DX7I,I޿@$&䩒&}DDV[szHtlej=_̵*"&A!HʺA;SGKY $$-"%yNbL2t$k>U?$Հep%5XMϚ6%q{ÕĔ'D>Jq `Iq9= 'ӫ_\g?o?|ݛx/_}Y6 Oќ_g7ǏGOg.nF;qOt (ͯW>,z'مӑ?]M{ߏ GUNr҇~t0~.~j{Dh|#eʃ8IL_溜7!{:2Lr)b|4 ?Zpk0~toqf5N;Tyk_NH7Ń+Ӯ eFridO\뎭;vIMs#XBL^#K'Yw 7:_Ѩzq=d8]ڋQݯo-+Q܄WNO:1{[/w+[\'qz댩3})%>'rϢE7_?3}x*WZI~PI'q>bƉ锁@#@YF 0$@SϴR! J(/~Ƨ¶ !<-o`< p?Q#@pl )=.10a8=,%*@7В5m\w_E)-܍hyF(g_1?_O3YҺ{O?LwmIbA 5z-JCh쵬]ŸEEhV>6Ly K 6ǂo)) ZȼW^K+/P̘K7"GW1uꯌcd]/J/%$*0 + l* L Opj{0bQ%ܛϡ7A geZ /V0.IY؛g@`~w u-ߞ~Ȝ397)HJO =9&rB?@z1`w Q}IE^ɱ?-W؀C4A[(0vy a0-a8ycYc!}QkHFz/W6̿E (̙. vih:̂.R/ @b%9߀p# 7kJm*fEŜ\յqhŵ ~Kh;}yn)%ä́|g4^x€a1[ p~^\ O¼=) \hiT70\ jA,x+zٻ#Z(+7;_5btATAգF.=|wI)(--@՘Y1b h` -ޫP,G!2MeS$N/|u܎vC#rw؈mm` 6Wep}cx3>@Y@ 0 ^2ky2Vl2VLީS Su2 0DZO^,;,hY%0ej@)FC@w3f  "Ğd,'5'8$41L5h67" [= D*/d0((&<- M:Ø\elO҈_x#C1 p , dt#&w]Ci XgPg(h|"`l( MC Y`a6A =ZWA(x/)|!n@&K19*P~G#?6LH(w٫~b+BWɢ!2ipKAbM0 DI12 @T4.Ѡ.JA}3?lI4. @P3展-|g([?8bo:XE}`zSv4H"W׫:+{ƓcrݠV$4 #h˃j`H vBCt81v6A?)!zqXrBT)ޒ|jyix;w!f8Vy3gr 0T./V!"qF?/(DE‘>FT$߀5 @赡Ymɇ'A'+'‘ Mpp`$$@t%n R@ #%m.W3RI0Zo_^@Z)$>Gp;NA #2pJpFmA /4ǼFzm!!تqhz@t|Z"7u}+Mqê\ DC[@=0򰃥,hN#ԑ4u8пg.'ת0NbLC?ì$@)z)b,bkV𰤶8~* DAI1wA,u?eu mH&5Ʀ@F 4Ck /KjC?[G1RN@z6ʱ?plH&ey] Й-@W$9u%i>'9vo:l)]Ij.Y s?F”q#øO7s|3n0bQ73AQ:$QeB@W3U. @bc a #')BNLApr+=ycpz}1ڎ@MD : ;`DZW!5pDUb S}pԽJGCnQ4/* d@01y>:W6"+L'jœ1 l)VLy#xJb γ¡ tUL/E' wW>4stED*"UH-E]RBv_jʇQhg E`T F2ǕAO(2E&^U=:E|yk sŸ/t,aP/|M`` (}574\Wav(&͑e /'@QbB$368ȗJ@_r4H_z C|0r +s  B$t~$@_F=$po$5FExHp\jLP083$%LDD4pF#g8`ma EX5/66T9t=@29Ǻ"оBI?8tƉÀDp#:8%vv$Fis%:(˱0\h,:~d4.!G݊arAoj_Ua,2* `bqXbrr8JU^# !n!D0:8A0$sEaaEhdz؃bMjy8 02Cx*lhrRۣp;{'c*1t ЎaG@bDaaL;U!EbX]bMDZo5]HIty\oL YHA gy92^reKÅ|pYyΧpأBA ZTD=5^XAa HrYYh9ZX4Rs*(xq~< m,(J-@HAʘ1A2d'@Su,DA BK Ql18'A'1M85@5(C83x3!U֞ t hxUʂ}8H ,; zAXD}vtVIiJCtzU)oW#vaFʍRBª~pHpgPЕ4y geKR)<xi<|0CJ3*34c#VwVQRt)sүAJ";<*|=Y<\8HFoAn(uȵٺRdKUW؊GX Sci2;Uӊ4UW|͊ õۇ5?JCidouP8ڧ\NtUjPR-hEeE5BRF$G tbs)$ Ȍn2> Ԟz:>=Z4SB10͗8B4/G8$M X8g1^0őe6@XsX Nh0ͰX eX).f(Ys{[1K!L,. G"L[x003Ȱ0'/!z7_;4ch|<݌l+PC74%ϕʊ7 LWz9^: :KPpmQpK.Ve+6]m-1MǾ0q2:9D;ˊʱFLpam1r RxT,Gӂ9WKIM2K`ECx\` 9FLcS 'qLAa'Oi'"9_#I" ۄ jVCR.@[x 6{E7j3!YW]Z(aA9!5d)-z&n 1(Fa`l/+#}pp18qBHptJ̫| Y`1f2 /{VCe8t < QxgA7_}~է⋝Kl/ C\ik|8~pnhhnPxsxǃs.z>w9;9ލ&㿮H`3ck"n%ɠ]JEk#Å8\d$5.{_7Y#].NW(l!uK&rfke)gs4t9Mo~qqѯ<7;|ߧŤf,[BϹg?GR巕 ~R]P#}1vn?(_,ftvۻJ-en-Q/N2G|'!a&GuA ]Uꚕw xS-xZn\ߝ=.:"zՒm/_mV2r4Yraأ/)|fFYlvr|ws|+WMlzNHTKb9ڭc/LdULOHQɭgˍΆo@4Zt{AI#H0diTxxJ,HJPri+42K 4͸X CYRLbdfz+i_`Hb'G9MClZ(>kX/==leC8TsO B"YgrL^^r:tWBAlI 8KwpL딪v J-qJR9i]kNQ vn4<4=1k8,1CSS`C+j:3rC!Bj=H*i1$QtLI6k ۊ eJel};fĉݖZgU4]Y7r*2-b񠓭YrTb[[s0[l[<9|{idi lm'u兘V~Jlx5GĆWq= XiH=prhQam? #82FSicvGiQ0mUrsjk{'Er!m;UpK3Rn'֗%@DN%)$Ffritzing-app-0.9.3b/sketches/core/Sabotage-Detection.fzz000066400000000000000000000650761272363601300232550ustar00rootroot00000000000000PK8DjfBSabotage-Detection.fzr#G.wvcF~fL*i9dEHCTZڟ L@fd"֥_ow×'Гrzu˓=Շv J,12%adt{~ O#9~3»˓ˋt$7vv7+e#|=ۯ}3ϳKNFnf7_hbdt3}3DcCg}4}t~Mξ?eHI3jE͟QMPb-OEB- RUӽzN1ߏ?-PAh:|oQɗ։Wzg~@u:ϓy>WzjU|kZ7175}jYl5}_~?/Nϋ{^~`ua+-l#icg[@ʓ >8t{x*`6;! ﹍ 8WqC]:{_k$)߆xa EѦCB"{(M^˻AŅILgC=;oWIٷƓnf7gg>}">?釳};Iz<{?6_C85|^c磇ɗx[݄7<,~{V\<|7>?ׇK.{x:O77ǧFbEcgՎ_L>wu|}~{y^~<xcXirfFRZat~Ղ|7za<>/zV :ߌk6^:(HOwl4zp0Z=~ޏn&O7 }zM?e3qڶםu|t6$ng>A4g3'q;}8+C.wӧ7DuF_Mf8$ť뢃02C&{Q jYs]sq{[DQLZcX'C&mhp)T$jN!auuZ&gq/uj_){Ng:-p65z h8j۝{}5:Xk)bml4U5ŕ)g x|{mqw.gějE oa<LzPы_'GN|㫟mY{)ڍ;6MQjl`2&h/CݿζFz{$F߃tklӧ=cF׆_pCy,xFOL u Y=0o2rE? ʓ®EaK fi( ;g)5+im]v/ o]zKڔnڴLX*l&M  ܦbuL<]:=a|]$w .?&ɏOSOf1d6<^>}?eD:Ηe^G!wΛ!ۻk10; N"M%LhL )e JDs9*_sp])>hIyLcb-7RfP<\u1vթecN8uj|qY2;^ޮ)zгutJ +ɒ~4tX[jV~uouriyWɀ&&]RFQ13ef̆5Ĉ%'f-sr{M"i9y'>qXb̬ (7Q!bd 4qiG@)"tЎlm goHk yYO/mjIl5 Nۂi*ގ2W> a$``: ȘQd^{`9`JF`\תUwehœmSlTr-41G)8%lur }r7s)7G !1hʍ-}ӄ9R0b\Ėb1B_wIvm3[YaxδۖMJaY^|G/mNYPb\ç> StIayuEW4bںU~g?i#FCe׋kRỹ1KͮN|f-{{Mq^$Q~H^|?VX=T|C54z*_&"4._)d^]K .POv]޶PPe7~d7܍:tcFاF8Y9?6?>3o7r$ZMJީZ 6ˑ'].aV-l8K Rhf'i0@!E/-|$qAĕOic̬ H"\(2^¬$a6ŶiwƂ+0m?A3 TjG}yaIÄoEn&r[&tzU{LFS)pV|+]I?қWoް2nH E,ڎvU 4_L{S؁r~ggIlvj/5螳H`iׂOjj(60r8i $#‰vQ9(3 =38]Py̽yބ V!;8ƙps"CWF$2A3W!nE2ti0Km.Ź%]۫+8EFA::WW;!/CҒ X\7m΋";Pp2Is_0DIo0hjTW{RMʚam*@k &3F)?T baFpmnc7 pM-fg3A .eP3|G;剖pڷy9B v1'hl^^2|Ӱq3^W $o4w$Z[B\_bMIaD.^ogs2]Y*#Yu2jgy& :^MΩmhU,i=hWHIfdlRN Ytrܲ34zks@$vP5hB0a[ !iD^v[a^ē9uO::caѝ٧}e)Wsϰ.D{qOΊIœ]Ց|ߊU[R&Ϋ<}L>G5ڡN7ea7L>3&9ZZL>3lF[G&NN>8caWF%q%T+\jbSYr?/=矔(§h_tDZSN3suovjS@/]Փ=k,qg/j%9\&L4Ɋ^n@c%+ɴ4cHo-dVZRYFK)݆ 9g2"`Rj㵑rOcW??fV79&2B~gVZfP`H5U!͈hy) C23(yo#Ι38[WNmALS{IL?Jǝ۞:Mi$J{dr70I.?LNY&K8&! g1&4ʊ^n姼K5Io-dZRPwggy JriPF'U}zQ5Jff9k] `^rb+ִ?2B5 ɆleZJ2X97ɆnLZ㡮K4d4I_D錐}Rq^bmUHb10v,3E5ۗ#997YݛIЁ$*d*\{h X)5dM3`iq-ndV_/tJM(++vJqOh6qRk90'V :&x1ÍoΫ SHFষɶhʆEb%C1ͲL WqYMVu{0X'eI&N%JGT;v\Hcژ1NR.ȊGi 6QRf\ Qs0b͚-;Ewt1ͦ_̞>l$: 0IGE^ƫDV=Oєö0%Q 铠7 H˂NVmLZU<B-pŔ%S{+خ EEDl#R$u2BbEeN6-MHʘ@*&l)fdD0ų1&XEC/J>9͹(Z0h"HQR8$TmuB}Wۀ"  tF asWf.!tGWd8֎0'2ivn؋6sĉh 9na5 06V@L(9ah2 cdq\:uk-IQRD$E >nub̍SX\rX~ "=X,fҊVv$w[L.OmqIDV3$!oƒ+yhZs3#TPT{=0Q6=Rwddt%{ۀ#9[rNt[֕&+|2K,l+W<+oHmM(Qh열[väm%Iu(N-$#gFǝi1?2;kBrXEah(KXMtΊŽE9DMrB%JVJ>JfIGG7~*X2U<:͜.nUDPqi%13!놬KeN,`Jfh`Ih-qS4ց1ZZ Z=,;㚏&:,YaȰ`_6+骹O|-eFSD!_npf): ЊH#D̒DLZ&f3#ӳP$$:;GQKW E )wKkq '9,v]F3ե7BX]5zu Q2ok BZZđږE;k뺔-Q51qQK"%3sK # B.؛\1r6hII % o"-QԪ# %Ӹ6@$GZLE;SR|P.˵Gq[!G{s ]K99/e׌+[L貢( .i e t6丩j<yD2Y+# YQbWbPn٬D+:o؜xUI2@ĭH81b=PoY"0A$w Iؒl-qS7݉y \#m-H&ouF x41#2;k?rE ̬#3r`KRHLiY Q3?Kl*X%QjɠcKԬ0l=S|MmV:Ȅ%hYp1ە Y XҺӥbz/:ޜΩ&5cų2Kn6oEH{\luvZFo78KZj ::G2Yk@2Yd&CPwwf:&kbu0mtb`㾿$[;!Q\dJn\Mr[`!8bLc;|g\,mnע &G͊MLZ@ޛãHNt\ bw1fʽ(9!˂J첂$Ӷ-|jG~n3&{+j8QlEf+dLUi2Ka2k]6&R2kC2aؙʼn ĥNb-/k.6PJcM^mv֖)a&>)ܾQg 2 :ܚӝp[`e d*-uS& {d6mnˌļ&pm3.%/?eD2u_߃co ՘:&mmc̛C4XQB)+6ˬ-IDɬM49Iݡy\ `\7CYgqaD⺭PR)6;8ٸ'&gِV׽u?\s6Q7yS-p솻EKbIZPP_LRLڄ̑Z9<ۜPSr"jdD-9d49MrUo3 S܈ Qs-.qLؼZ n`^mۺ Y Y7GLLYW2&-kEqTQYZb)],`+]'&Ԋ] (PX8۰$-I $M8 ɔ,ɔޛI\ۢF3Kېn'"L6,c1Uy[Z8 .{s<7\H-"\M)dڶo7ɴM8j溳F2kkJ2kpY[]k? lN6zƂ{Oƛr2fxdzHf~qѤ?d[m'²NHo a,:SVZcMtKs$$zoZ@"YSĝI&lf)9a%bFӘpFޭ-9xX(Au aMm ,1IZ Zrq6މ-Z 0Z7c[k/ݗQS uW:ZLyLIiV>L8//-l/fn1tu؎Mnqn nչڄ ,Z9 y3&xG 3AKC2Ak?VMZ9F]Lg'!.* D1-bϓeLa%M铴7G|d%^Z7?21k;21YI1"+ J26g,`d&(2e`LY!Qú@/ήng>o8x@Z $~}o䲏OS?A>'_'wGf"ۯ_o7 3ƥ|\"†E7XծI}V )w)t["Oe#b^w1fꈤv.TܮکߪjI`+ڈWHuʫ(SQUmȱ dzo"9gwN>HŜ]j,ǂ&.TƩڏ5f繸 \Đ\&eM8Zz־Y 葩Y7GLjʻSn`BUkbAff 3,Iaof9p)UWR OJ6WRvbddVq$G88Vt*B hn ͉S3eKi*c1S8v 4),lh\no\&M(lM9jfyH&k-@H&kF2Y[VlY6!u(*dfBr#smeo|m dڶo7DɴM49j֍][ ([7Gcnwcl4~,gR,M]n?܏{pf] X)Fbo8Xwx|{q7"ƭr~*sPB͢~< Ӟo>ߌojQ;]Of?[Qkk_>?z1׉o}C՞eOPpCSO’Nq{-ɮ^roGINrSnM}% Yvrzx>ݏ:0=3y,_N\=0mݕ#>`7a~.Z'xxvÏ>_L^xK/i$flC2GV䅞W47sNo_rzu?~]g?1pq|v웟w#Fٷof7gg>}">?̟xN1FfW76ڛ?p{w5k4k xxa:rpC7x2%J)C73G[sCJl`DGV-1ΘWϏ~Ko5$RtppOY^G [s<}-7|{dx3ҩxno0G׷w"}QWi  +)3)%R:qLތ)"+٥wTÿ,?Hdž  ׂStq臢4l𳁓B1[\C#f"/o'\8U2pAMxʅ?B\BOBW *+tTU~ nOIw^DҸS/(C^'CMlh@; x8ĬǾ =. c O9oX o^«{VǗ,,OcS[;/ԡӚF>*4L6<24 xZcx 1J_iwfx.gc *4kac㧵#CK/xqʄ: 1{z~1_W^O~=UzĶOk="/>k?\F\}:רFqoZklg 9bS, |ox(ٷ84&h#4 `r CL 7L4~0@BLD` g'+xD`38a@0 &W&0%Tht:ozћ MFw w , C+^ &KQ;o tiy 0wϻ}"(8 ȢYLaÊ0D a,#'xA|,@-̻h(S(0 U]?-khlA}xe! =TUm0w:a;xǪ[C  '.?<h$mVN5 %c[cGo>{~8hy "$N! w72p#zqЦnj* op?檦Xmg4\i*];zx1v@C/v>(h߄3:USLܠ*' Ǒ`WPĥ]O $, E@W0S}b`pF b3^- kź {?`;ebl=cM>s?-LyB`TE*[4a1|T`*SA9QLzTU xe*`8> |V!5:aHY7j:> <qp;څ8dF%of`PYUӇJ8' $ r; 6@ }az72Vie5#~˂Q{# ZiEBP uX^ѐa 3j y9gԯw5mQyVC:Fh[5#?GtDCh ѵvvxa3O f6=g;V~xGc  f$ КK]0cixq~p` Kw\U.GF0Z @ap BLi8pqyW` p f gASUVִXLVc仏 ƅXxw\q.)afQt8ŲS%NbƳ(f &47ck8|t]dJutrSwZ̅= {`ehM06IOJ= `?bfB2i72(fӒڰh1 TP=$9өp>?ah 9Vq0m*@gW]4g@g?:xk=a[S?z´#ZW1@cbf0bt/G1v4^y@T#U @ri n !&%cLϙ"0&?C1jn|{3D; àΎt4K԰=@# cF%y2aX3w@T!0z cp SIbB\.o$p\Y!h!3d2|hQE`GH̏=x8t>]@4Ag<NDܰ`@8 Ŏ"0=G`,>0S!j4^=hN|TLCp6;}Z9eb0SDpH(] ӱwZQ5ʌ -(@i䨴e'8r[Xv=MD4N,97QfŤǗū$=(gBQ6?]hl ~R#9 bx"40*Ӈyj?]T*&Mdɢib+ &td\:g wUl73 ¡q1&{r3~`IzóF%~DS 2)CO34D"kcܨ+Zڍg'Po+[3{ U'W 1/S(EG,pD@!b.sdc㔋 SBo 2,ҿC}% XձXq+ⲁX*cq;EYEߝ'NfiIVJuZ+nNmi[tכbbS6Ey bc̑7)> fo|j<}>w&bυ#&LWx1+01'uH6_x'{4cgh?Ll%PCxo868jԸxάg4љWY˨|%wZՖ7_6WJ F i\()BUW8b /=^! #pt`sn,tGë4:.Gy86qRy-~zWū-–Yb3| U/6غ;yOeͿ}8flx˟Mg_>ݜ~L~5Ne|gE(?6d#|q'ۇ*qXZ^ ܕҬnp~V})9#hm#jvT[1{'K u [/-ry|"Ps+0(ok! SS;k ߓ6JuprB2Y,X(r2Uuy_UEڕ;׬Oe䦭l߾eMYثw7٢M| T"}H5k;o`S[}^A諷jSP˼z-lP-zPZR-Şxijtbe2պܪj5MV؄Z32j9jzPى8$ohx0VZgƛW-lmClKU"D7m5pS'tm4K 5<^=hɁڴ~mކY,h'x/&fXA1ko/oDUa_ZHMpY\6(ޘz$[Q.6z]o%] %+i;IvWJ@'RIW$]0$wO$"~Es t_&઻;YZcnB)Ko7^$I"΅-Hd˛C7)I}ĝzW-Bd7W >US$ЧR$'=| Z'zP|@S֧y$_=zhpsoɐ)SìG}-S?>2 ֧Kڄ/L;uybO\v;&XuLY?}yZyDŽ_oC'vkY>mn{j l˽znTਫ਼ʘ>5P>-ܐ<~toujkc]̿߬pc17gO釧s,n> wTﻶ dn a)U1!K-7W߂9 eΖ2$e*;_S5D;%TLպC߃ck\I@A $G:mѥ$#"@!t88z2Zl޴"ɼY6a?+$W15}̈䌺8XP;v~%gW4:L" ZC$^ jmXci!hϓ96dV$>d51[Թ n - 6[(ۨ1kn_NtyCAu<h;Ųd9#%zGߢ u qˬ&R¬gnWpbxs?[ĬVvlVN6]~;3bq!PK3N[hbi9zy@D6&Q䣦$,8mbqSC^@'e S!kˈpHXW2K+sãIa{sxDɴm#)yǴMbsŠAJ۸Zu=ARo[˺t Č!pCLD-6%!ɠ?D8aV ^+:ѝ#H q$g}k`j,ƐI[n-V0X[Y!%Ӷhi[Ƭ6h["vI&ɼE ɼm>x[(q!t6ЕKmjv8ev3nytѸE5'dВ9%Sh9\ab˄ˉ7H(v}m~Q Hy\hܜcg fa*YRʜ*R0 2fѺjGږf\3r6 dvx L%8Zh;^˟H9t.lLZl/+ںH6y5[ 8 U87v)`9>9¶,I.J%J1"l[hyތ-ZڀY] 5wIH*({dR\$S-*\dY43Ů7qpV7%B ;e'lŊcaӲ/#Wl-#n$H.Z>)< ;[Ҙ='?2M;<:Dr@4Mu:ݍD ˸& i eD^dd-!xS8/Q췴!ux9d[ru1<4.^&1ņw#[% Hy-IDB*Ӯ4QqUL^x#n,?ń]2ALmxS/޴]2ATH&GkAue!l^GPmĝ?r6]0%zGիhzsezu\ [KbIS2;d(ɄƩʡ&/{^ SmCH&iSk ;;D;8Y\BEssjs{GrDE,htX3EcZI~߱(&VNҺ:HڢaAƶZI#K-aN֙7;0SAS+ ,Py!/UX/v2lrdn/,w\PH8HjN( Nv839qQz6xp:XXaRfVXC'9ײM,9Z򦖐Z4'v$96\zo&pJL// .eK0A(32lp;-³S0+]A+9w>"kEJ/P2uKini*k\਒[[r-E1Q=pu.BҽNoLJڦRDu]b!NqEB<̔ZȘk9n޸`H&fGLZceucML-N:HZCoLP21%c TKfc5SKd ɌlhDc`dĐ#[4>ʲ~ mCntQAV5i)ju\ fn(1Jؽ3%fq?8-SQ1 IhBɍ]W4^E~I{in\4 ]AI&iL%G>MmPt+⬌ĐRBn&i.JRRR6CV&->Nɲhvgu#HRuD:m˵ޘlYdcoZ0ڷ %SăJ./OܢKC#ncB@ 97mK9rYzoZ@+Qg6qI.K_mc,䌺\Nrh]97reiZ!Ar%8ecu(Π\HV:a#Ӳ(#CP~WZfei3XB#FXcFAtAj(zjGɚ栰eS2kWV!]  tK密&9j&kv5;~Wt;֚zZk!5\sHͩnɚщ +F|jM #'RSMwjזvY90fr 2D9qѽ2][u+!%0wS$Z@LZDG{.Izվ1cHg;Zh&l;YXZ8FY'p$.'>MčcGg\xZZjGВ 3r&:qU Vz.C|Mj2Sǰ rjGѧBv)Q2_k Bs8V&IE%6!H&n-xpgPK\8D:}#qSabotage-Detection.inoTmo0_ᏡC8& RNRҮEBjQb!>;!aQY{qΡ!3]=($7;%00)3ڧ6IlnL45Mf1?H GONr,LZu 6ff\rSe/9Wu]ûC̜BvU`A; rP!uI3hh.ezP h13ξˑTX"Kzu.% 0eKZfݠK|`s|I]UTC[hOӷs4n)= \-]m$.X$x~Lo~[{;;ꐽz5udV] H@Ӆ| 렔2:WTW"Fh6&PK8DjfBSabotage-Detection.fzPK\8D:}#qfSabotage-Detection.inoPKifritzing-app-0.9.3b/sketches/core/Shift_Register.fzz000066400000000000000000001000401272363601300225120ustar00rootroot00000000000000PKȝ8DV: |!t Shift_Register.fzrȕ.Gwஉc_ݞmgv{ft$$[%i$Uey NfEd&%A$ ZߺㇻɧohELfWxo?5o|lrt2~WZ1QwP=޿9q{xzLWxśwS>L^n_f_ |2Kx{緿u~t۫/ި7/Ȋ+N{ygD?>=VWח︢g_w;{e;ϝ,_feT+~~e}_ng?tcr *;ɻzx?]7OWy>3yyO^t9\ݾ^/=9_1˛ه^n 2+KO?|fݼt_.2⫇YÇև>]\ӛqۛϞW߼_ Ki>۳zuEf@];̤yg1W_Qʮ;"g*Ɵ>G7RX/u3חޝ_><ο~>-K/4įۜD??[s L{uhO=|<4#$Tg$0R¾+BLΏ8м]7&Q@{J._LiqϺ1/yvg\j{niηUsno7YDnf|s|YJ3Rsl2yNo-%,̙e˜̗4̖-s~7h>kPA[ N:hy/Z>y)A˼^aZ.a8Qe 6{@n 1lԋ);䭍.ܹBg׶d Vbh#E^"mҕdCs9l'(ݕZ=,Ȓe>p aKV8aǁdu28ν syIBf̭ԥ|pf3l \[&f|ʳ3]Jsl"OsN]-caL,˼b,Jmtf0hyם3z+%}g:^K@ҔjU<~ˇ#ƃoQć=7T)aS uJ|UJ|5J|e)#//MEa%㮄/RFMu!ʸK &/lz=p]!w=l]!10aH0u˶N]^07Tu/MoQjeEJ|=,Dw{i|Kq)<(k/M#ﲳ|]^ 0a޴|O @[WG^^ć]ݰ@|0pܵ {ԅ9dcz,Jf˚V1G^ވۂlW*as E|E|E|쥙,#a/Osq(c/TFMʸka)g/n)>p!o9>l!'8qPHKT]^:7To/OqQk5|0>4\w{y*˄q׺*<+w/O#]^C +8qHu~q׾)W)AKHPBbC |2y|qV+ ZNrofw?^}3v̤yg1W_Qʮ;"gʪ0`7Nj/޼ϳ?ܻ7.ϯn_~sxӥqq|rr7oy|06\V(uC*=ŠReX-"o:Byjʓ4:yJEbz渲%a[}R2-G3KΔ}6aI:s9NKәe{:,o;BY> ޙS ]16BYǧ3 )Xpg#Dgdq*X||d}ɇk w25> QhzDž>y=Bhy*4G[FhоCI"THPg-L]pӣәE(#wӛ''@}={|J#^8d|faѵk#pUpN!!-Iɵ6Ya> w"@C%*.}6Z"UEN;v|x|=їzH|a5(L P @i{$\aaR= {=JԒP,X#ԒTaQ@Yy{6G%dڱ0aWn#Dbe=>n&V! D 9*%.J#r{҄=B \a4]_QB e ||zR0B5Yǧ%uii#/K#u{##/mz )V#/Kg[S6BV4aFyM-?}|y3pr{MNA-ayjd^G^_]_$)>BQG3twyqwr%Mˢ=B}i KKԗ#//oP_,V9cq~c(!|.I#1|=B{dq 4Hm9B4*7%b diO_rF4qPO(4yc=",’F(O/G& ( `?7r~~ǻ]^f7[7?͞>>]Ξ/^ݘfϏۗٯ_~_/MO_O~Ƿ_LJ_Mߞ >'ݼ<~v~?T?ߜӛw?<^TW/Wp/޿M#?nn_L/>̾_ow?q\q_4{^姻ϯv&?ܾ̦Ϗn~6y|MxxXꁝoOqO˧۫_.?yy}Oav3AH>\굏;Oo/^^>t޺]tx Le[7ݻgѼt'b=\Lꭴr|s{2y}~=MIgW0&f|pr3 s5 @]ۜx=p',KK<ݟс_W ygDuk B~=6G~ϗC\-M&L]m9DEF(4lXPJM!֌REik/0uVJ%Oݨh!.Qc%:Y*U-.AA(`ǦNkRƼԡ͊$6,vМ(/5wݰ ݰ-7熅)\`^XVJLYC,~6uVRoDh>1/"13u|nў͝|4tÑU$Jq*,9ljqrtܢ*[_p_sE٬Ķ٩_B:+<3qSkS>+QE_t|~#DL-3!s"';fu tܢ:[R=t[tDnQ^f,[s?Gti]un:+;ja>nQBq2"-:RL>Y(mEVOFYEd GVG[t|ٴZ>9׿rgTu>}?=^Nq^Q5ofW8ϫdtYrPJ*P)S%^ܤLu)"RR+htG?4`8A3D? d Tk;Hw UP'tLHyeTTJсoR1B-pzwZN5_ZolcѳE)7t/8/fIq#lM]jEc78]ʇjC"!zE`*+FmM^ T ^PI܅mE+$M=|Vp>ӌg䍙qF3z3'bg^}HJ_}.ݽo|;CϬܹc ;5߳g\D|lgRN{lz ɺٱJ" A0w%@It=H$X>ܡfg RYig>{m):fM~}cS701F xsDA;M)J|6%8r޳\Dl60y}Q\9Y\v>kxJTi6u ;Y=8KTIfS{l|P1}ϋgf}:ucl0̍Mz]+_-7- *;n0[>g>/zdY=}gwg8-w`c.> PbǍڀiw`Ǎx%*gV}9Y|&< ]Z Y&v?5G Ҹ%~fp?|gߌq>ẏrRl{v /n}ygN 4K|&F^n%8R3g SS8͎<?Yo3ӊ4`s.7x l각 myRYN)b5垙Z|֩^0r,oWF0pVW)ieRٔEȫWʹKlF୯Y7 ڭ'u,祐a j,{s*xgX0%!w}%ދ}$JN$qeSNs^tJU%۶ǽr:IrL%c͑_AS_"7OSw;I짦A%Nw"$vIyi`a4U5Ԏj-s'ٷǽz> ,OvL'K9fb1TzòjTVT.IZ~Ir[UuebFpXZF[E[`KL*.aP-1;$(.14ĂlIA>إ܂l9lb}WIJ>|JE]b>GhEX͒Y!F |K|'{<K8[b ;$g!r|%}CgJ j ڃkõfI0JEjYi;-Irv둂;4*xO׮eSx3tqq7"_zP|x]M>=ܽ\ 7B{V(5m+x۬Qo/*+Y(cgw'L{]4뽼8qOoJ9ޕx{}p۷gjatݻ)e5^^ՂVY3ȇUt႞], ](FN*aV&zV GY/eQXcC-OpLB#]KU]i8 OWFqsN N.I3È+Y~H2'Te+*v4{~t9{>xzy\pQ+NlgJw#ZѫF+Wɾ7xtVD~t*y9${f݃?__VjJsc"TyjɱR81gӬY. EޔSR]#`ێ. ގ;["xXp$+59Y)ɐ=`@J!Xݕ׾tA7R.=)'FJE"%1G `. 8%ԅ^ 8;N$=>:\IGm& A*P2,LqQqKy(KYY 9p.:uw7Po g۷<`.F'(D FrZ 5lc-np$uc"xpq$5p)"ɰ>\,L.Fgf4@$t0Ck㤬 QrottysȌ#p5XOEGaW &'(XSpvdDd'p%3`6sR"b31!:ީd6\XjK5]CKwT46 MH6jE'=:\0IFSYN4Ax2(QTMASef jqoES56~:1uq4,"pj iHޔ΋@aÅdg%$Շ&39݊R̻i)RԬv@K2V|BUk۳gHTPNCUB&چU;;t=/E(Y7I2b*r! 2SĒA |hCJLMHQuQVwR;$7esS )am Lob᧐͞ ~[(*=vڼ)3Z-|),U*M^jX+'i3TMCd+{|ʈS,iń"{CNf5CդwY==ߎ!9 d Yf`XNw#wAOLI0A+Ri5$eFR7ɒTK" G[ ԆDS-cy*(O8lBm3am8=rMYZh<KU6F]NݚI}鼳17bQם>'?Ɓ\^*Cp'Ma%~=YK}V'k=b:l0IN RSēa |Z}WSLR2IeF{[TBš!JUd /YEFˍ EHMbi 0VڐB*5P(ܣc2R{`fEd'%#UJS4T>0밯"D]O*NuDNM q/It3*%z mQmɻ_yy('m '~6o36R2H"C I,bZZH\Xٶ聙 3@rfĔUA ߙ0ݷGc`C)BhA 7S F iY"I%)Rt_m~X-tnIFY$Hp3`I>fBDfҦ,)*"{+58*|Qik=9.P[ -8Wpڲ jdiYL$h  ZBZ͌87q;EI2+j}9f!hckE$ YgdY>+9ZAD5}h*3GIV^gƁ,^ȑX蘳LHXV$HaҴl,-! K4 yYZcOUncm{{+1gljP@hT93c> sfx7kI zdX$ CK r!guh]^M}8icӐb17(#-1ia&RFp1R7 ҴDr4-!,-~ KK!?,M}XBg Vow%Κ]Őo'BqXPPm- ,M[]"HRlVR-! ES4,3E*fن*Q!/+pR1-ud_m&a7hf9KI˺H,AvFNz/Vȁ,!z]r9l^E[GS)4<֎9Bϐ`nwFuWyZ"9m AƖC%dl]&(236R5$x%i]&>Dfifڢl;|җ#]+Е DFs,d"]# mWҵN $Օ Z`|-6_h oK#B .oӇITy]0$ _+sni𵾾׈Y~8i5 cZPl-6[` KKB.KIyYZ@opi*mrGVDb$kdmZC>!HIr(\-! W[h%+n!3]ӟuhH:/yIp$dq9VL)Hg_ YH(¸Z8IG=vŐm9dn $h{ (H:D}YpdMK\Uڸ#i˹Z\[ۻ'3Fێ \"wkrwIFՕlXV(`-6wKo cKlK!ֺlMe:!oF+cyF!+%0̖2bbSnbl2t'dm% D8rڬ-e- sK#ܺȼM6 Lp@1!AJaJq9-H&}UmZ\I*p;2%SiSl HM- .^ڬNf2u{]$X]aƆd- !$KLƗ'tEDiNR4 {K $C0l& 2(̭;Ho68 mTж"\epP7GVJAd-M-`dB X۲h2oW hkQK+CVd>đǧ\ f1ұ䑪y4Tr],n ΁"ޢdJDݠ$hz쌭 #r#غ9> cNFz$w5l,mWuJǞ-R&=~ogS$ktD ς'A"?[uЁѴDq,,|`-! +[XVF*_Kjx4#qi9DbcfRS:~V !=+@0|`7t49ˑޔM`nNNm,8_UFH͖8Vpeun.ekEHG/NdJ(9yl P)[w4G-e΃l5$&u%RQX$,D=:F{Zض@\+J%SmڟA@uGD0֙35;rѨwdƃ-"]E;c9rF^(K[46 2 ZJ9ZR3Y@vci˛eשJމ(҇xMGmkGVRrkBqjzo>^&akҙB5v_:5Y4 uK 'Mr' 2h(>˼77lY[YX>_qk#ZaDVa40Roc9M "j$%lT|;l, r":wZmPrF@]a"!) bEqEԮm֦##{ !;Ko CKZBhقh!(]7@0ḔR He6)Ӳ/ٞġ=Q5pk칛dlWJmBtͶa[Id[d2Q%uGG0֙p433 >i%H܎ͱ[L el|k<ѺrʥnN37GV eK *uKi WKպ9>`̭+v^#ek6"֓yL?ihB֕e!ɭo[5CH\Z&gۋndAq1 &r;5ɉy; V\FFz۴>$a癷Q./3͟wogVj8 `*)#Boi6?K)hk܏ܤ2%pB^HVe=쎎Vz-R6;^ gca8s"t K\Cg:csWFwVBs10לԹj4=ૉY&ظ݀}O9`X٪swl`7NeVZ`w4LJ <MSi]Hk_Հnځǩ4 A-&q1mn% A?ke3G<2r4rPrZ&7-L筭[v`V(f:+bٗmp-660j|lչR")s<& h$H: +#FR pI*%vw=`z\7^U`%uaaԒI%uGWBۣ  1&_ևDJc: h'IH6;$G64QR;5ɤB'ѾJ I!ժ"X"q ;JXZbqIŰ3'+on}D D-l eK$`uyx [^GBQ ߺ"f@HdjyᑕFڃMɝOj5@k;r,L Dqٛ g)  n.-s=n,&9>2J$W+O9,bS FhrW,*iEFRis<r0h%'LMaħ7EIMy<{z>ý{=W׀ |Ŀ ^0Ňo‰o&.>lD~緿}u]߂mz.v7*SIտ*p뱅u/ew O ''|VxA"ZM3)6MNQ3 Ekwz $^^e8G6K \(R򷼶 RH; R9py^0i%L2&#;Lsq p=cXprtˡj­:w7DA MNM%@pM5Η™f]"[k981 j48G3ewxAW$ K ('K2'@ h' 8s'Hfד)T(ތe$80&nhS:$nkF 3;4.%,k K$⺣9> kX?H_J/e,pQ&XАz쭰j-ܪ@ {nP` o? R)AFuGs|(9 5щq5YՕ5DJnSǩIic~jfq9Mcʌ)t!P0ږLrH6AJuGs|8A<D^WL&%drcu'(HZ~[9piGOeX-HzR&wJ|-,,F%z![#c5Odgkefk=>qI+GM?]buUSV4k.@"][`-%j][sZZw4f!7\uY+m*g]ܮ2qXG~7%~p$=AZo?AZ,-Key KHźA*yx*PM. *kH|=򐐎kJlq 3ث$0!+b^wQi'5>% dL`;*s~%Ӽ-/`c+F3F \&!SHBV"1-=%@ uG3B\0;jt,P⻤DU\_AocX8piG' ǖy 2bL.`l,~vl,݁<,d`X,!l`X+u-И:3S۸hlI8Tvq妾1*"+F0*6b[Kez Kf`.vfB\l ƆD āK;a00ԫDX%ӎ?% % uEcR7fs0Paːe/;W2MgS*-KU#.m@KW$`lLn A@0riq$ d6Ҷս1qQ~Nw3b]34[eU0)%M.`,~v,݁<,d`,!l`#$3VN*a+/%ʛ)%o< Gd؈yo1L,L,d`D;rr`b63nʩZ!.>S( \ ae:^%XKVFAIGƒHbǺ 1-Rl|e0Xj4ce3c%[_(3rY3IZYw4#4uCnNw?:b Z f=9JbЬWf04 DqDvh!/J.3 AQ n,NFvVJѕLċÅ3t'8![+T06bn\[j K$Kf`w.R/-)u%TLU-e\>31 ?ҥ0۱]mB0f3x #*鿌Dh[",9[&[ \Bܲa&tf+A*)^.U ӛ *a``WH srq!9RMpk åӦq9r CqP-Oݟ?= @i+n]a27hx\Byȁ+(RiV  ; ɸ?oSfVTᒤ2~G>2V) ̞IBω/,RiV `|; ɸ?3$3=H<MͻދL, zGs$,ɱ<Ȃn~.%6c {K#޺9> {FK~hc&srz*?{/ea@ekh}Jh-Wͮ2IH}LI 20LB eOLJABIFV9$wg)]{b>; k+ $Go9Rr,en_ѷ|`-%6i K&tnY49<'j ⑒ou 7f4.%6i K&纣9> [|NsFJKR7NVL% } L/Q$oklpGN; (AGIB,Xܲhgn6w[ں6k@l,GBVw+80;#$ϽCM ]p^i ,K *M(H@ Fຣ9> [\Ny1a^ c۪)daa2{\=`sMܤ͉`!y\J89m>A&LuGs|@A&,ݨH@c}ZRf}L7N+J^6 s)A\N+\0A>ܲhsdsupa`[ҙb|y띞HENOb^HCEq-NnC*xY!E6 +TΥms>A2JuGs|89>'~xW9Ø߹ss=>=8~iraśn^nL>]}t(e7̛8_K}Uqb¹CMx5_˯wOۇ/~I+r=5_/_//M?o'=?/'Z/'yyy~~?<]<^>K(^~ٽt:~tg\=Mxw%5rx~.^&~In;L~jBy2N;7$q/˯!~$RG#i;,!'ʽͩ?EՇn)JsIFN}}X܄pZ Q۔Ǐ׹㛩 Oԍ a3/HP00@8Z\{Z+%>o("Oe؍OdwNDB3'(ݜƬr_"*e$N Lw{x>C+n:CVK 'x~nAҡaRTP8{G?gOSGxݼu)3pHʭ? "Q%r >9k_OiF37:[\yh92L J>njox]0wD@#"KLT3T0(Pʰ>U>M@D Hީ82G?e@L&=L}XF׸ #L Ƶp oQm5'(m O?,i݀=q C'~27' O-S26ǎI3W:< Om|Ek is!No gݱ: oDx'2 kD֟^O pl>wiʂ.39v^idqCɺ(L0Y5Mkxq ϣB-*oӳu荿|@AH˰7XڄqIb s{5t c a |̝OS @iKHJ q8QgyhDN92^ V- ֗ ȵ?6KU1y_ُOv؄ZK$nϛi=gXX6a<ycc嬡 NCrX0~DsLJZT(ܲc~`"^k~3P1z S0tz0/$:vOoT$VM܈7Íx5 ܄Mg9;ڭiW+M;cіi펷v{]g(%զB*q',&aJ ' ,0,V8v^\/)y}5)c>aa t?6X+zٻVPx&a2Ql=ʚcM>oq~wk)ݯ6X4-uжhdˢspQ MD UzTlR hTa,ު WBb"&BgX`~v.|=s,.0hE% 6"l eyجT8ܙ>d fiX܆|Ӱ$,(0wջqn̞VY?g1oG)MY{qDZE>y1`q&.=VY!a PgAJ (Kx̟Œh]Xa蠎gtŭ08P&D*/`aE D`A<0P)`7e=T.A\" &YU?ݱ{F}:'C:.( 004s9 hgR@K6W, L{&!cX0azQz4J/, @iTz6PP$17sZ3 C gS Gڠ'4\T}Xvt Nރ KQ(\UwZʼ5,X;q^yE`uY3r$A_VCKJEiIqJ^B@Y-q 20`*FT$! phk098*`jx[a!ϛxAuEjf+O~a$|H M4Y1 '(.u78ER6(ưXn`r7T:&o=ܹw|yM! b| N/G4`$aW:LGa9l gםٶ"r퇦GtDKǧRkGtg(nYկ[5y1\v?BS\lo*fnF*#iAl {`*;Ɠ $`K\Ac)xq9|`w5XXR[XӚ* %H8: `bG ;QDb0$zW`&@O/hXSSw3Nk#0F7.]t}+AFwQТ. k8':KPbƳH0+hPhvNFmpYGs;c(`li: n9t:AN@sx`]Atʽd'lSy ry=­yaΎt(i5aa0;&<֔_T! zL;n>EgGi8nlG+xV;Q``1FJgO 1uc'O<¤1NnA45o^$:Tgo8 HAp< 6?nوi"];թ[ÀG6B@)r@WHt7T1Ϳ~dp?p+, 3tWD%"jD"KN]\Bv[}ݞݝ{CNu9*Y 8K d< XW,b`!S`21|-A{)=E|~wM(OӅo0pIX t2k~k@!<lH~aEn !@3?GG"%  |Fg:dIO!? 6 +LH~ΉGs8Fo: &( ଍7̿uoAʺ7W HCw^:1,8) *5(ఋK"`Uƒ5(~!6 ^)^@sSLoF;; p&`[g=ډ Xo[`!zaE Lx?G8.K 0Xa@Ztx 9Fs#cF?>k\ 21bASDp@H*|+tl]bmk?N |#$"82Qq ?Z6 ʴ 8ՉS[!` Txa0?,bQNq쪇Tï3b=Gcjc`@쏈Ӗ k"iKεd`&`W.$9W]F$gQ Qkf1vee9e  *><$ 8w$HL H\묹hqG33F| Ayrv|4!N2 H|A"@LA.aQ;]֢g bK " @`p`G \)p`$x{RNTk RL熫j&̮hhĶ,j5 [>>0m?VaBTӊw[Cf˵5HGzV\TJ[B6.dl)F^n!O+TBr.TBe%%TPy#T8X pl f2\"L07୓ TmM3eZ|)z+\OvD&ԍA Xp΂TA4lb& jX$a`гZ9G#B*\4C} dn/@Ӑ5)}:܈gmÎGR+Zf/] e7A s:F#gdw !m_O„T`%i8ơ sb\n}շznm{|Eu{};-~l%PQ@64Ψi}giϨ%3k \ VLp4Zۖmol"M"OL4D(<{N0 ^hNb%f-MX}g9u.\"`0p9Z c[ύ$ C2?QN(|>ͽsedȉBI|\SgPBN ?C*(XmnX+tv.?+'in8`]aE͚_]x 3헣j9 .QWh(q χ]ׄL](2f@iBn088`hX?ۛ6Ax?hcaq_.]0j 0?4Sb%3.O'@l8Ep\<)hn uL[zgTڙt:>9{}y}7/߿̾ݝ_ן7v Z=^&M~u_R8·wt$cƙvU%YHpRўfdÁ+ڪc1zov1qͩ~`qp&| ҫzm׋Sig9>}}?/vjYw{Y\awo\,:z+:k?n[[Mg> n_H/OO&(H@YXrxͨ-N}sv>UkN[8n7,Br ]rAE)=iJq/wT)50ٴE7I2ݞa8OzD;/-.4RO-:4c\S`2sy~wl{{CZnsnqSOq݀޹cZ^6Fm0ג<޶ۻ^wwsf>AEq y>W@rhN9 o>u|iO=vGo]᫶bha+VM-sq߆JX߯.rUb66:{US7_=쬵[55WW6nۉpYT/](nX*O/wE6a}.!wt!Ro#eY V E! zv$nَVf}9VnvE`d*&b # f9=t+ځCd#Ȱx ZB,dž&Q.DS~p\F)8H[4@#Ҏ"ɲW!]fmݹmN!]J`@mL9,ici,+ U;: UK"'Kr@͋%S@kdL(2,aok _I4 2@m.Wn@b0F 6zFs,ѬX|H|-lѡtOTkSV l@+d  a 9H]gYnNa/䃙*,Z`ݲ;- <:| L'K^ayC#聄lNZi2׾x׹HFf3#T_kb 23=iQ IёIZ"9U k$AK3 swdk aڜC5ҮK\L-zCˬyrDq$Fm!7@ ZBA6'h|43A[HOb;ˊK$h]Y6Gv|tYbX A` A Z"8YZ#K$h+ >%$h A)@,3A)w[Lx\WYZhd,͡Ȋ%HՎ$HRW# 2;w7 AG;_ڲޗLٔJmbH3!=m9mFWLAvtDALN-'ZqJzxƏ,AOyAX-d4 ]Fٕh*112wVR7g)\0l l-~,[{BqH$h >%$hsv"23AS˞,̋>m IZh}SLEҖ۸aZC H%%j#)QdC B%$ksv6#*3Y]GW۹%hZWy֫hZnUDYJP919Z"8YZ4gk >%g ?c ,6Y;V$ž!)B{ܣd="'2 ;:|tnL{^_wŕ6i3#zP"CW4D!"qm~˓qG39IqU9xMILxjvD.ujL ^xnT27/xTQD*οڰuTt{Ti/T!'=>Va$2!^v6b!] eY`dĜxSYM*8( ͑rDs;_p mɻ_yyJò*l.d m &*; io@W-.Njҕ Upl mw4Gó xYϊU$,qFJ+R):qi|=`Ľ屧NPc# A=:0QA]=Uƨѡ%7\Z7QpO.$G$M-f3rD_m]Hԡ6isg} #gHf "nm(CNn{1RS) ]InmB h'Fd&b٢r2JůWm0y!w4Gf[_"8C|t0KA~HiyQ,6vniЅӐ  ]iI r"C, \v]rҢl 1.+vAWr2yXwoE5nb,⣃X BW2lHw|6!u1,Ʃ4W$ebհB,ٯ"*3U]8LN(Eb_ wvA)s'}X`r,s rX ,G}XW2!k}C(3$U",sg}DXV6"JQ/[wfd?r{Qel@r4GN'VY с,vWc9>6fUҚpkycg9>p!!^̄nY/J;mdORc.()W8xzMu&0CR|t(KA)Ƈr|˱%T9vH0py3ynlhVH'DD kT֭mՊB/Hrai.˕.'-m ֿLMVtA~ztlR( Y@mqJY-AXD^@f Ōa kIODfi. .Ԋ),$mE}[Ö6ֿ֚Ll^xAvtpA֖WN.iV\ͦwW۸},,28yЛ̜鲝EJƨ*hBI Ö6v˭al谂-6uyOٜ9#@퐶4i[BAڶxڦ27l)iꤦ/|Q#_͑DxD-6Qc]ֿF.ʯWggov崡0*Q6R0IU"os؃eWofONƤwXljdE.h/mOPPD{M+pLD Un}QsvҖֶQ͎\巙8,mKHa wIaЁ$mh% -S-n7hZY3=-#@`n4qs3xԲ`%H"-C>T(ϵہֺ|b–( _}sZABIst [=z@i;HA }mѵ͜oj8?K,9No#fCm-gVh8R8I piFHԵ&R+q,VT-8j Vڭ Nnێ/ pY_qf%gθ~myuwlьbP1!^T&=⏈dx8.ì&âb 7G 7of9A5wݷ`uU-p9wPUJb[A@ {%u]-Pі-J?~^YnAi﫮+u_Ti#-9D hY99w rIci_2q {Y*c4nќxъse[Z1aX(7=5zD GA`ԍ{q1&(A `&vܺlc&iU-Ͼedh iXҦp `VhIo2xUDHq^g,9>8ˉ{{Y>pғ!35#Fc">kFZS13`QiO#KF%`d ItU12 0En"v䎯ڠԽ6W5u߿u.`_M#o0Fp?9 Ӷ G79f(nX -"EiEVWPQT#Ad&CIg`BiV2E8$PFP5Njҿe-;K>ѿ,jup}9Yۺt),PlqA.r|qc[{A_MAlXcƦBډPU[tB{Y/'li\x~Ғ3wm XZ*>C ij42ũ~Ē( eJG۶G5bmirJ; b4p"Ȁ{g@ѵ415g 3I`kSFJYʥ*J!FoT\\aJb.KˆYrx &8`bɁ[(:F=qv~.9~Z`.zO.j [XMiB*l%,+@nٸ쑾e>srK6(EW$m )@z9R-_c(Dtާ qq5nȽ=323ֱ3z$k}M 1#KIoMU#ˬlw)+wyEdB\sZǭlxO xXzp d&=Zl⍫Kae+[f<[s umCh\W;"Y6e:r6uPBzAH+v*N'{_.ޱIj)+%ZPR?DV| h!e_Mpa Fp`6I'CǏT}/s}= 츤BK)Mv4w/tyr֝TʸeHΥEu_O|k" 푑5ѪⰵIF0m 4-9L2ЈlӚ37je>жa4ӌ@spPK8D蘒 Shift_Register.ino}TN0}Wxim(P+VH+'qԎlRq6t̙0s1X-˂0WN&)gM,+iҠe(Z,+ LfPn@X u9B>01f-Ks80Ѕ=!| vJH d2QC 8by~baE[tឭaEGQfj Ӆw8QHH|u/N̑ڞhQ:OЉ:XV"Ψi4C![K'~bE9ҩK4e eϹƙ6~#"eBVLK8 ~WQR>.<vb4jآ¿;C^ia9a}^X ;ya#Dwr8 t!L'ONђ\I.Ko7hFפrSM؋-& #X8CeݹS< [kO 3gC(|PKȝ8DV: |!t Shift_Register.fzPK8D蘒 ;|Shift_Register.inoPKfritzing-app-0.9.3b/sketches/core/Shift_Register_2x.fzz000066400000000000000000001375151272363601300231440ustar00rootroot00000000000000PK8D1rXg]Shift_Register_2x.fz#&%[iǬҪ֘u#Z-ifȲ2YUdy}} 3^2Sj&d }p?\Ͼ_<<^~j嫿|}Ͽ꽿vPF${jvu ^UU7.ۿ^5/_}?.]\,o?\/?6~jZ^/|վaV?Ͼ]<^tzQz:z}\~&2/n^>.>|\~ 3Kg#|spqvU-=..Ik&rqn_/C5_?\~{5kߺY<|կ~qʌg=_~<=oo*5~:\\^H'/B^XЖ[./ٟ?^_ŇRY{Յvֽ/no{X_֖kٻp]u?{_?^><\]꧅>xU?ՇO񛇻w+gxqqs翿x̢~|O̬>iyWgW˛ޛ=_,Lp.1}۷OZ i'F̊-}u⽵ eY}YH#.wT-?|qHzG=W}w4+7_=+~ yiMY_?էg^}Ňb'2i^* ̅"ZW+kB)U7 Fo+{r|l-c%_LqN6mc^?v8l8{F+]=,vl{x[6c3* 6Bfs!sxm.dmts/ (u ?x^Vg3'?}ΜwW5K=hpS^ t禀ߓbqjq}wULޮanL pYn7n +$^&ny)Uew6&cřd/8q=v3 &-n}(g|e O2ww42f-I=@AUeNtv/ߙӜa:ss9,[omvMZ wrGrծ֊Yvr`.;wݦ.G9ɝ_C9Dgk NKve9ʎnЬ] .uw;WvRvt]gVL% z楑A+nhRxsx. a;w? ?j̙Ofx=ᙻUs$ wtBi{yJfG7w9=_;ՋϽ9}\^,/vKt巋յ.}g%F\2Zhr*㗯~q?no?\-xrƇίi?ݯϖ7˫W_oΚ֎[O?yPDS}8GlMGW8V~ $>A₴yq}Ip-lۻ4uGi OJo?-`{|߀|{xk&h{f2孙}̫L|̼ d{s5lEK9{] yi9@–a~Kfa+ؗ`B7wiI5pD)GSКL+楹f=Bg2,p/m x{ߞev yiL(0s e4M&^Y0M^=|.E7fZ!K (}waKg}U`.p\Dv3^];MB$]KإJ LBҲwM nv]%ErR"Y9plrDuiiV0dr. ] 0܆m`dKK#KsH#:$F&waK$d[1 /KsIa> h^&aBh.)m {KY$)7$@4o{ sJ aIi> h[ Gu vi> dNЌ] ؅-æ?]ڶT"Iiɰ#@5r،.lmVa6X.mMKDJ5d)#@ׯ>Цwadc$opSp B3va ؅+tLJ}ћ_cNjd q{OAC m^xSǕ́] 90CQ0 =)#)^z  o=)p7]/zY: \/yAvOJ60ls,m!)^z ܅x]IigwA'77sO@@KK {›8dspA3wa!$岰rئ/lnm!aIaɰ^|3wa ܅ɰ8s [ [DtNJ[- MK.Q$H8)U$H!g -` o6f7gkG9z7gWۋE5vW.y lϗ|<.qw[p~{jwe=,S?ݯO7w右ŗc0_;?Tx꼫IJg7W;?^< -׋_}蛳ꍧ#t>U:g0njFm7ޛeunf7 +9_ Qіso廊[6&oκKty?>j󒵷U[W3WS%}_/>FwW@]w{:N4ov_OZU>Q88߮v{p}z0¢aw>6mfMG\l7tBǻ:Bʅ>Av! 4kMxQJ3˕:p*> )L5<ХaⱩfk%1]uu'jzo@ǚTeQ3gVجA5{YL5JiL wslVgKM{p r .GL(nJ- =%JE\Wǵ\eRIːI5_Foʤ>>6"feREZ+/P&-A&nLZ&PL2(lAHyʤq9dJ]LP&ɤ1׊uR1 ⺉:i@}:0IF4i "DƑBP'-#餩+_I E:ir ($r(ތPRgǍBF厪L3to3JU] eMJJe@(<-GRs.C(O4 (RP(mJQ'E$.1&`;7}܍V}%".gn. A)mj̝@ ‰JiH}RHFT$#%@CH)u[b)G'tbnj)Rb)%`B4X:ݭ?nK FT\;cҶq‰biH}b Js d$y+u.`܅9NfH \,(RP,mBb)CҒR()-+A_\0K7`Dŕ,ul Xږ3K K!.^pe9Cm!K7ݵH&eRBon5}VqL*AQ#R\73YBqYj:? ZX{Ѯ-  Tj$ZooJ87(duEؤU-qsw}lgQGjk6''B\-ϚZA !N5LUo95Ezͦ욹"':bB5:*#gr[ZDwʫPyYB-BJyQɼe^ĽQ昪k/{zF̻nZ7$d.r `epWa=[ZUw"ʼ9d^;ʉ<̫P]Fq~2ȼL̛Pq "ڗC y] ȼ+)ʉVϒljf^̙^['÷9_W}izY+Yh[4`|ߌ-:fRhyN("A/b."~cR gBmǀ;,٤_n{p)M)U&b6&e=^ry$CIMZMQ+ NXM_r9}C+;9kiB@cv:8fԳ^1dʱ s/QUq);tfu,VBwRߐ#"_"/d絚3Dr:fQ:>5]½<1wBgy`yB7\l1D9r=i*cO'mn7g5YtpNR).|xh?N)b\:f!f1 1 :bww!\]GCB~|~$yH;҂]q- 4yQ!@ =}2PT𖆘's:kY26zV3kBr*jES7|bԚvЬ{?6/jjpfs}WZn^aP-ďZtU5 fwcL;}Gm 5~'V2`N50i{ְ&nSbPmC{sdyd/c!<ޫ?peB'%jt^AALE L=&Sjeۣ\Sfx)n wϻp=t#nn%ۍ96*J`{K&7G^,b|7x)yw+!AɄ9`рd?s͊Ps>d{ &O8IEOO;?ɓmIO{R gAp)珮 Xf[NSKM퓮9į"M7 l Y޲18ւ!mb0v FͮpyS#E <2h048d$/%fx)6-#m- hlD]yjI_da'ERat3#z/t1]-`<뿸"OP6 z7l%"{o8iGK.R8ױv_'ֹjcE+1SK/.{t^JT7Z<'uY+/rGsV@sy?3 j6tu;’U^́NV60v TN8${%HQWU)(JLQz. #X=N&o$jj5~M25*o8F4Lj63F,ɜ cCI[lsvW(A}?{JKAM,$_sg#()K$HGK7x&hNRN디0DZNJz~R- "*Lu`#nY6*/m 퐕ζ<RlFɶIdˡI4ZS! RĒ /$ "JĴTꗏNK#K%EHjiCZVZ-)cFaJ`X|WoyhC>:."lvA4[0L Sē /buC٩1٩ZHZ oH*BڼnSG&E(0S;Ŵ|V%fnAHǭ`doG]1+z+9zcMfȓ+>z@^`hTd&9;_rh@$ABXETN61!m5;_؈Ҧ9P_͚ 䝌ruq!JBmI^-9Ưmv(QVT+u)tbkXgeBbki#9A,CM䆤7bԟBaeԽ1008,${%&u4m[Rm# #}jXP Jf//*g׮9 #Tgf*[dێ% ?-)g͹Ȼu4+gqBy]9 d,]M݁+'KSSc 2UD| 7 U]QU5A0q8i*RUu!ҹv?k2f[q2jtMYiEwm?ܰM"B#A8: n`xi>D8[NZ`RD-.f%шL &JN2x:UCy)6qy^ev8)BV4?GIȼ3*tvŶWy70DaDKCa /muFW"q9yӇPIu@8';_0FLUJ4AbxE1Sus߿MURԷ_qx۳W˟n?_kkż:c?UOjTfߟ_vq9_Zۛ(- &s_+s#F.{\ɶ Qo=n1θa/gy>o_uR] u|rϺǧς{hl?H].A.סTf*%l>_o"&8! К'Ri2^'LP^/ ]SרN ڕRK) FPzk` W`)a'{͹F7 "E/q9)o1$. _eyH**Rû^CSVzB)M#%B ytFcNxPkq=O-uǍ9%ƹ"*/<Ry4zRKܵʲr Ge|z.n usYINV7W\@kNv;qNZ쏻\<F* oWy)˛c0WyEbǪ;{?λtvsu˻It|\gmTp#)oJ rz%s&+Hpn 5N4kY9xXrԩO oDnGyǮ:. jӭ&jV} LizЫ۽\s.\ks\.TŜ]Ё[][TS !ȕBTaT 2©։1Mʟ]QܨkqFIC[ A2F`^.C,̑fSjXz`RP,m6}2iP-EtpRŝZlQ-z3(QK3ji'rKENK EKH,Ul- G޲s.D,̑fAtXj[XiBn2P,EtpR ĝqSl>OqXgPBgSmC9VqL--C-rZZ(TZH- l- IJ^?BRi6NTX0%TKC WKc mH7[OP}ѣ&}:*@-UK2R+'Bɪb:ji.Hu:u6`e 9lJWK9='TK TKQ- k( U7}܍V} 4;`9ܡ&uTZ[2TKPKA 'ZZ"TK3ѷ@d}N@C8=LjZ*P-"ZڤzcEҒR0MꔷQ,z3(Qq;F|n۲܈_X qDP>YԌ˖zYKs d$4u2`܅9l[RbiO, &-3j)䯖 (-+ɤ7[OP}#*;Z)Ҷliajij)ȕBdR7RKʥ9\ Q2yeȥ 9NnN \.U(RP.OrjR41ܠZji*oFOHN97KǍjp} >ӜŽϤRӖ-ebni)j)BY?ɉzSj.(iA9RarZܥWi8΍Z?p/䜳qy!Gb- vXk*Zm#{G$b/fB$e4Z91NIzi:];b,]. MYQs鋯0 x^0QP4y7NЏx:q(u6)u7NVWe:3J9\jɿ?)d)Ns_Kp:eQ(ps02mh\W\85t d*icƷvYPPv yIPy n U혒_a |p&_d16t/uy1Qc;FG-ZFc* ˢa(G.ʝI^С?m;p욑Epe앓/QY,T8mĜOoq> C?v֐g NI)bۼAf!f 2e' 1;*c !旁aGCBÚ|~X9W`܃fM|,&OgGLQ41s k2`"M(<@{s9#Ĩ<6'8|SC O<9OڗC6]ǤU֋*:u0BEgP d4 D/`\/ 1M+?=9&H0ZZ!fęM5gS*gĔH)pÛM;@QkϦ=y6A!~ӎ5qJ,Hd: rMIBx-Ot 81\A7 ę.2[uk 0Qa r7̠l 1S-Nǧ .8:"" !fEq>Ilǯ0 {<l^z}Tݧi.~f.~u=M: u'E)r9wIX>mwcqJs}W1oˇuu0ywqw}W]u~&һOWmTuqO!Sɬ7g[:Go͏_6T 9~鱊H+IB$15 JO.9gH٘G"577 @6B,[->`u MD.o&7:2xo:Mo6T{7 ͊gk8%%BiGs`B?l?T-Dv hp#͡9B.o7:2xoTf,9ۋors|[h6bE El*[E8e9")'baC9Eh3t DG2[N@:Sשms"B{)Nt Ic^ a*pL> roϾZҳŒ\./aߺW|v{~U_я=-]־z}qbchgWwr1@ta1p~7Y݉~ww~=}5 _?]]|~ǫ_ϋ_HJw}q{ps~M&|ywafrC}-.s~UM_μ WV= ǫhwn׳给{=~z=,>\=.rŇӳ?|c".?q9_?\~UHfA^eu=oCunO\_Z>}k=oWrQ@Iw_>U#z]ӻ:jvݯY4B*SYrwNs|W=ϰXX_"`T9g&U9mu3Cqsm9'Vrfs&'߿o!jONq4lIn]hبx 4бeG$]S 9PVDG+'DΙ$,bjН ["M..::eŭQ$D{%'NpJy4j?ȧjo0ӮeMEJca"G4iAO /|$jL[(nfA*Zg1bl1ʤ^Sg_)"î˛Gs 1P (T!(EX2ǐo"vlv1|3cF8m4@dS)Il*㈏5zhr2\Brͭ8aӗmI}~vgqGsv DaD96%/6>]•H9;1wrhЁ#/ (c6E9]g+9 VOE]FI{dYqLe̙ Ը}&ҢRy4P$h: `f\p&BǖaC7Xt$y<_HF6=]lm<פl9:k:i<@0X \/mm5 1n>čaF4"M/t$f}#-d\ i5a:LGͶFZ\!%a Q`%YF2bfkXȁd#/&>p)l8vmr-n6ѶlRBn2Z(QgdHUe*qF[Pql :ncèaF4)bND/vLтSn\(dtWݮ_1Ol?M^9ob97q>#1v&_!##sg 8H:<23xqt |ھAjzpW-;g7/_5G}~s Wˏ%-Yqӄ6Y- `7L}y0M}9WPۆg%Jex„Å ubam1᛬kn#iuYoR69V,HdNHQ!Ltem:(k 9;mrywçD{oԞxRi[Ep?@Ybx{Sr_0i/(d82((E9"H#`bG^ށkA҄N_j;ϙp\*,S-8rWQg< 6o)mP$ [IM3wsi['+E!UVv%xG (k]\p4SX\kQK_4Yl(}dseZ| yRR7fH-*Dpn.g. &Dv¥oS$DNt.p =bduAGBOM27mԅ1@[Q58A$[?D@h!IaUWw&]v`o+PȋewL  qa*L\Hrڹ`&T!r`!"z 16$FebgbSVʶ 2 "C4cXc!иV:&HHr̦qDE$jaZmiZiж9(NQD>5盋8$kߎo@G@))1P֥ N%k"+(JJNgM u#Xo8c NKLƻ7',v<җ;WHَu lG۪7c{) 8 ApTL8  ZA6alj%]WWӡKZPGӷ폻86nZ:dX9?ToP9 `Tm08ZT(9հJ1 SԆ Ku%]?RRgԩ✘$ƺ6`6X Y7=Cͱ6N<ٸuڶzrMǾ QDi)p)p4 M3K\|?e#lZv@7o*ߚmm7x r7 LCnX0 }&'r7O|' u HLV2AޘTanbp # 8P[$\u 6_ 28 .&6)drp4GdrkLn \ڦn%m H-eMLABԥc>fPHh7Xʃ j߭ )@(\?[EN. 8bȧXml x[o1y͙% $ly$lE GseIPI0(ٔ؂ n0P \L@9ѶOr AG06P'`pZȴMXe8W܄\YKJ'-Rݮ:6(8e 8*OA$;SI5 _8 F0 ANqfy!H""B2Y6EM16[oJ*dhr6~ZF98Zl=bh< O8 LE‘Si`iZ@AhG hMLZl<% Z6(bIǼTBi`1 QPapT-,UcɐZADؚq.1[JGvyᛪ5бR-\-Hjp M0蘆gcSm}&"25A>Ei+1C9) $a[!Hq$ da0YXD@ֱ06Mq{8Qv%;ca!3BIL`m<,01SqaD,z*D db@XD@&bbz&͘k=x 2u3ADl1Ҵz7## 2 X$8Y&2L,"p fwijKq(SR N]XAI] X[շJ+2Aj6G%*4P@$uB4Tz8֩pFA_E@XM%O$n1dbH2&WfWNn6JʈeCn Pk i1V[H&.Vbl-Hz6-y3P[2usGW ( QFvaʩF:}9D.qGv ẉ($\0:JGX +b0I+)jl@&3HZ' ҷw@INv}#;)b@OFLf IH7$VxO qrIN%L (Z6L 289h$yꤱ. T\ɨr!7AI0Wr8hv"*M$fmۖxWNW7-Q\+ng0I "TS+IFs%wLY&]{ %R5 o8 H0 CN1AA0E<(+c{()HR߮!l4)<1MHiK)Hն>jpdN"jdKH" då|GeҒ5XաcƋݳsjE-01Ga ,~*3&2^ "BUlJ2q~ng\?3$V \1 ʵ4*hGESfA?E$iIsN4KLDGҚ2kqΦls4!#,2#H$A DN AAEDj+$ě:ttC1"n@ X|&Lr'`-`C0,*3[9q|$c@XD@2Jn4$OVMb,KǤec> jEGԌ +7 $ ;"'T:_ZAEf|"2u]ΧG - -VX訐5`-dD84-*Mh9ZDAndlZ!:qC49چ&X-fm-4oU>U,4#c҄Rl$ ?8 HESg2f 7"rUM?SYWۖI%R-HVH6X(S3Nm}#"Aȩ23Cȁ,"z 1[4A38h6w۝&mgL`mdat,z*u"2L,"p [6t*r}Y%V6O) 1x˂9X! bcJ@ 򲭏ðyY$\ F-"]  IҼi&`i,1KSAM" W#V~n *l F BRwqMc-01[3χ= g8 QRP #n;dj,c-qv@ غy$&.[dTt9FlDz'eA\2 F3r4ZS5EVw#$F$g44Bi 86$Hh/(vd]`(N} =[ !HVL&u(UDŚhPް+xT;foĨڦ ?;&?kl3Ե48l0\ LHf!\2?YZ p4HբRI6Vi1|'FAdaQdiqDǼ,H"w==ITuTx S8 DֆlOyZA6'\͸\m9&s@1VJ`Tmk+N}d-u Y3C`ǁ9i3c2'Z Lj@4mh; M3iZW4.9˜]m'Q0<-uuZBWhR40$D6Q"UPH?kXH݀B RHrMrL@n4)K:/q EA/g*< S \D]msu̘u`H*VoݺumHDȘq[L:7Zh$sm0'j:1Uk!Rʩ L Kwq - ez O-`m8뻀enMѵUQHKlQ 1ézHő)O!k4#[|=slZw8od'h,m0@6vL"G`B*89:x4R6#nS#ҭ27О@6$|3 ̲L $ƈ1 a 7N;R?Ḛ#a94M\D a1'CBvZ%.qI3:& pٚaBHqLp>@fQ4zJC7pR 'zC!K-`8LL 1rh7h+8O߬޴30(`BeL9iU: g$8yx2NW- dE$q;.rcAdjIp6Y g P66RI5d-&v6Y3)$jPp4G$j8QKVHޤK+}J?D8A2wkJLj0P: uԸ& A3m%*FV!{) DN$n Ryai8et=kiVmڸ/WE Aek^'( HʖH?k-ik6CZkWMI!bNyP [|9>Q$ikќIkeh]$2nʼn=,l!g]Ojmj]QlHh1 rN.mudwYIDS%k)<lp9p4$S[k5qGcDU\RcJ,: PNHζ>Lx;Hbf;Fv\3ı4hI*@ B'( -&Y% q3af! .nm0ZL9m&"H"Fφ9> YO BhKi5Q{ l  Da4RR[ WCI| (B`+YZL8m H"`h#H҆OQKzU_*"xWnc2ׄ=8v D%vEDȜXGB'&HE!BՌM e'ašQ9ژ/O 4ٜͪym֬ %!kCk5uEݮp! "I / ˒J Y$&AqֽRAHG}٪>JhXZRoukJLj0V>?CڦÌU+FUk= ~k!{)c+'aoIdm1cls䄬W[!쬴y?Uhqv{~ǻūןqLin_2vu;wANUκ݃8cG/b|1`'L&|BY* o]ǾiLN1_u˦o}- mKݏk4iy8թMRNx!QO G:r̭]6cwqf˻/_->(/Ti es0J?Bށ%H6FEr #j)4S4%SGE]OH ǵ X+Q`1 A;ZDVx0 ?8 JRІK'iC̆^Ёg9>|ྙv &hv융ٙhCN`-w[[ Ƌ';}h##C,Z$9ZtG)ZZD4R4F)O[ Ra :֮#hk) Y 2ahcO #h'(ZL9Zd/Yp4LJ虯==KJۂ A~bZWd?-k3+5›I3. Cf1d%\>Brf65%c{KUf#UI6H+ M0=MpYyI0\EN5\An90\6ey87㉹"n4&MYsf0`6f n4N]pl#5 7+1?i<0n60bt nYY-VS3hbm]>AUbcr 4!©F3 &`,&l,,r`l8p ~:v&[\T3 )sc. XYvNɆraC. fCӘif2qyF0jSUfFQhhQA F0jCE@ GQlLը[\Ofc+I[Fs$`,HϞ&X  "ai͢z H""|B̴ȸO1Br ]hVJ@P>!?%9+ ghgȊs4= G39 @#l6łG% 3ЎaFH{%A#A ?CCLRN_%A3o$h+:?IZjJ =OÜpخ؁Ah pIBp"-Ik:K\SI  PTڭHQI{.`Fx{%E#'E CFFƉIjH8_FvN̟Bv q )h aH!05E GS '4 &fh|qZY`4jugcrKm}"88 H""b͆Rb_ 7G04Y0PNLm}",+!eQYY@p4`l@L\D Ǎf9Sh$VY IYT,ANq`,~v,&` %((FR>IFebJTTvn$ekf"UI*6^ Lph0PRMm}#*+áeq!YXp4Ra)JL:.⦲ `|[QcecZKm}"++ H""bʆO&dV'df_ 7GuPp4%`,. )8 E0RV0Cʢ"h YoX5E[EecRKm}"(( H""balMedAEpSY64! U16D EFe[ &phY\@b90V6MT.1l*S4q2,[4T( fnVPوEd[aF"IGbqr?}Mf/+=A~vpWW._;?T$a[f*32?.>|\>+笥Bhzz_0B1[^]^~_g|~fٟ~v۳zv;;_1ϟ?ς=|8ǫdz3`#_ O6~]#F׽w|`r50yeOZj1׼)Iٜ`$ɻuu55>^y:ջ4_sur^;_NvQ9bjn|0Os.~C_3^Y56 w -;G:5~?)۠}S7ciyZW0cr,X;q%=ƹ~2\ccv|yS{{잙t^ÿb%Q)f*+STbgT)*D'"Hxr䆏^hנB{p=Zk#3JՠF/=>CKGiI V-| &G?;?) 2盥At懩psQ_/R[Rϝ΍bЅ*B0H<(×/!~o/AO? dØbA7kx нL}GRss؏Ϋ+ZJzFkM؁E2.|~ =>va3ua}Pu4jn2݇[o*}V oV6]^з~<mv |ajz}%GK4c,_ŕJWIJ,2o ?vz7s0m$Q}iEigU_wʙ;>"z8h| 5q0]>#`w >AuǦ .MѡIz#;SâU{>BESCGz{ (:t?F^/7ǾtnM̺Xg\~Y׮Kv^h&0qC~z5HhȽS{#n?Ά"zM l'/z vpY;f9FC*F/``W|_eiκ8$ Xgng# gڠ|!==1,ӉIpJw6kR:p* 1f,~G<?[vaV#qGlٙ3m oP$`40 I;~vt'HixR#Bx׀M0ہ6A g{nB?ӃV/$Pd2 ~| qNo@|bKD3v"E cd%!5aBz9~z:a( k0% v jR+F8s XT v20>0 N tu;-qƂ83 ȃ?:ݥwB]G0X)y4X*Va|yEB0} p3pbFDn_A`8coA!zYo=ǡy@yڪ!uV5VQ  ~=gCj ѳmUA`Ca&п,'j0.LCFh@̓!j@ 0v e'9M7z=zlЙNF֒q-LEC#a#"al1(˜\ F-z>#aeF α|!!G3Ý !cH-+P+9hr)}"sOQ 9oϿ,xvg7MQ5htAP3#b@B5 #Đ@_Gߑr_© {;:vQ5et Xl? 莟{+|>e +|j W$/0\A }:tZY=[pr 92x&Xq>q|w} a0/%X/P02)|B,tyLذ~bvMh7~I _`Ę~_tL*I=o8iۗaƠxAgG |e؇4cp  )` ,>~+{o&FB%)`"s:8„0$wY^$FD]bs#(}r7qk.K-3[=-j<*7 ;޳Yc} dpW6qr;n pAņ":|whƘ>1.K,>0`p=lNxpP8 4;uxI1(&A$o ]N.R/O:C@7#+{_G/1FN>ޟf\A'pE0)UA"=)gBQ~[|_$ـ )`Y >dLP Ǣ^0 p #J;XLIQqZDwMaCFQ6&g`tS ~şW0јpp&1ѽ*Aڍ\zNzx-:Ϩ,hLf`8CaFDEE cX(mݕн졫3ݗb; NeEa`8DA.@;w Mjzt'~El<3cc@<A<D4c0bX]MDڞI9=uQ(ƫ^G9,}k3ap:_N\V {8[ql5ij~j@pKx@a 9x^8KaqmFBL^89?0CjK>R &0Xs_ŃE\` 45N)`P"Cf!-2!2x'xIxu>Es dz~GЙkOPiPЈ U>/-|}&kx| 4 V"z|G_їsP"Uۃy1~tFaN 8` U`+-) 6\vK૷MdbƦWr&i,Ć}:hh>ټJcݟDU =-p8c|#E=7`~VGgéR&Ռ{Wp\ypb#S c1OyLv6С}WFΚj_3aF}?}җCCMP5T}-*VWj՜1jdT#בQQ`FQhTgq#:1q̄ʌn2:t{8*\PnlZ!FT߈)A=Gic Kя! msK f$RXfP%š(\# C;/ЌUjl+ELG[`V'>wP5}OFaQ7LaՈl,0"a% ظ_ztU!NH] $$l"Y3Y"J$ ~Wq9Qv<0`wKKXG{8YJ?| 0Ɔ"= UuQ?+2)xfxiX"k(g|hV" O ^ðm~Bc5dF|aUD8ab !*$:$q-2*8`\&, 1|T~@ۧpƢbVeӱ袱8cQcoN"HbZ2/X%lWLKE۫".X)`I#`(I38ab)\8aq<6śO$O\xrx?yfgG{édG' gqF;"Rg \.tpSQK\V}'񅉘FJąSt)J#"w ԑƱFLpaiF(0,G#0 }KqjOutwInX~3$ Q Sad0^p 9$)(j+x`kNa s~W(HTa`0‚Z!)seY_f]2!լg(aCք!Ki}m+4!9[+ _8G!5 c{1"\^},,nBHptFwd<ɔ_~hS1%kAPނ Qc l/p3w}au/~iiϯ?_>~?_a߾}|n;7_c?_{_?_×5ۑLg!![Aֆ'6#í?76W}Gqw~)~෍N'7aioy.k^IδꭻiB8hgZok?/Ϗ{޵L?'4 }.WuT㴯$ _Fqi+߿~qW߸J ?_HU>ۨJ;&[0 \@J{hTW %uJEGL)USiA!>YG_WDhK[VhsyJ&nly!@ˮ!J{kPJ=">;%٤PBmޚgD)m}HLisfqNOXn4]GI9neiL6tjaBsXrv wG T>=˅4[p̅S C4I& [wV^qD H([lMO橐0T492V0JXVC1]kJPtn&ElQtޚEūcGt|vOw;/yc4v)eT3| nk o>_qwNBؠQoZ$>"X$"S~r([_ŋvV-bM'I'qAly%)!,/$T&[HCvi }GPKި#TZPKٖ8u2Y4됖1>ҺaJ:*\=ךLzL |XX kօ+Eɒjk]X:^(gضXm_"W&d.Mg^q"i)CfC\x2nAZ~L*:B]E *nW0+#^;_kKv?) -(`gΏ,<?X*7m5ns7KRsd,g_ҵ-nٚ Q0 UeHcAB;bS5~bdk"V_O 2A8]7NJj^OD14-Ym+XXlޚ*B,6F*o"櫝 [=d[ ҮʺOc{ M\h3xzXN" .VK!*H2V=qzrP9B)(ZEKƲEK~լ=!gAs̈q6l'jCP]p.+͹1 )anvdgI*Zѩb;V ܞxep !mZ(:oM~*úZj+79KQ2몙(~cH(&mNlk2tFBYR<̓zr K/]KUfKlfvrFhФ)ꉪOGgZȌ³ BxVR=v1U|d .]{[_Eh%6|&xe>;]ZdE1M\L !ϵ&D0+cV%}/׉FLW vc2*B iڇ8ثKGh޳X\\x {?}㷿۟N #KX݇f=, @$\W Tkquzn+JݕWUאq,W"B9!jC*k@<QV%mi-Tn[ukehq_@܀dUs1j1ZvylcZD@v IC;y'U5b:|PnޚB~ɡʛ¾ֱ4zkp4EQ$6KfͰ-+ De僨tyʿ_K.S:+DfMB/#Dfmdps)429cT8%M= 8njTyAu nLA!!>/# K!weJ6mVHֲP}cA!ƥi@RbX$2措kTJݼˍ7^ څP0^$V+ T}gA׊x3R#*JA[_r[@ns|=UX싽rj *buo8 g~c!7ke#.G!, #~,(%DfKl@f=u#;#|>]ݳY0P>0 |<_WRBϲ JǾ:#U0Bc6[ 0vel8'Ɗ+7ۊw^ 1Z.t!n1m-_ jZB]IJnwZ1s 10mʨmk+A6oM~ V&Nf[Sm/qR !+)h F?bX7<;n-!e* iǾgU1rBvU  * eҖ-V&g"W\Ao_8MB]y+n|Z[G@@h)`v]IʿLyx;O&bM /V%61oUf!> u![⾻I6C~pfWΒʱ-&DpޚA@63M?2]N 3И{CcDdQ{ 2 '2UN@ɲ!YVk[] 5pv+e2>hTǢ"&Cw6WP %< %Du5SZx5:x7禔"2B+ )ȾZq@ bp2BK J 4[Z%3"A҈ j'vӨ9A麒ұo@WIB9K/esJ(+Tb*a]-G;)mi(]&ES[4ەV´ZB9BoD3Wi{^_9xvjkoJN6ᴎWPa1ug* `ZV{"XA!nӡY4s,|PtzIR6,?sdM=(}6oM~!,[v-LT2uJk#$s63Naz!,K&SGeJJǾe]bAL6oM~ &[nL*qs[&U9"SK՗>~:2[|h3TfJKYv!6+)!e֌ròCO[0ZnLʳKަ"iW ݆TeB GXqA&D$-N 7`Sk ZolT#ڧ,BtU-!nˮ$6t;f*!mڤBB6F*6H$caS-n@-O@KJW{{G4keʭ^>6Q}ǂ頄ڼ5l3ɷejD VaDzv2U3} ɪZB]I(VHDvP{;7&O<D% Dlrb}m,Jòi bUlt6%Di ..etc\v1K[ kKW%Oc+A86F{8XD-كÂ:F%򱬝X1U´BBVHCiovFf>Q]A b !\+#kmHS5X:yV,}@s6?% B4\WZ>˲aY!-]tɳ^5Ξ]6 +cm;HHT1Xq>P,e׆mFE򱬝Yԩhͨd)8K-)(&j٥PM5<F$NTnTF+BB62dxef',8Or%>zNVuJʣU<*B]A n JcL<Vg+*s<-J!JK&S]AU&nxU-!Zˮ$DkDdVw$4 ަ!lum6Q0 Ǿֱ6a\"ƴ aDzB$mXR1{6U㑬{j߿Ǐ$5V׎ǂ8y6, a#i U^kfܛJƨÖlh|捩dv BA]7(VH2jo;&PFl; jGm4s LS]w6<58S 2E "p\qÔ6hA 3ɣƐ{/M[T%9kPۭ($ގQ:x6x`5/*w WԑRW"8H ,{[{P#?C^+\@LqO Og۽7.G~_o:1^`)0 wqV;'j2~\kT^QUUx!ZmKWOL<"B,4,'xe#g%+?ĝehϷ&eA\ de!zˮ+|Tk4euD.[_IfnsJղ2z}[ѺX q)zkTډٳӊ6*u5%] ,ί]}hm,!f* f3dr"$Xj dIQZk %Ϸ&K!YI'/h..T&YRXvq K}[λ}+mLE䖌{?y X w^ 9f2"ǒ2&k_quN.PShwV/B7/ ^mXWɯ`a/dU;%|J?)-%:lG;sɣj"fWԒop'}܄MJA5僘mi MWNj\++.jcG6e; ynePvo/?↩Wh$Ar+'Aiv,Γ[ۭ徑ۜ ɢac0Z#g*Y$e(u{{7\QK0,]Dl0,jg*Q!D ?ךL\ߦgt (I,̰hE8%"-T"Dk$ɯeiE+6A^;ﮖH(0e3sq5y$~Q e; }éé8 x&.mZ>/!Dmθکbt8B馇jJ۹d‰ZS}MxJav#8,)^ּӱ(?IR/I" &VIV۳OjU⾋9ʐblkhy+nbI{c'+(`勐t@^IT$^ަ͊J[_-xM<ﲌN 'lFIoO_qÔ,+M`_QGʈ QpI+A횂M7t 2qҀMq5 GjEp] 萤!ȷC&E=~ S)D`^\oI+nGjz5 jJ_ v˲Aƛ /OmiXT͛tCݳQ;W0kGSEe9degg +n"yiw.A}~Y,qEl#EuDn!s9 *CӁ(i$jTډɶ^(`˲rB]L£4$d5:~*ɪj !pφPiQy,cH1ڌѤۀєh8#KnMUlR SӨB>S IwIXSud-ғ}CC9K!jkZ? jQۜ6٦I0S;v=s(V{/6&b[&K~Âؤ*/)DkhB<)5-hB9ufD2Id]tܠN.hDg 31TA-Y5bWYtQ5!0ˮ%fdd`vwSۏњBB6C4ŷYTeDQqxx.CiA30v{}W&E2uF*XNex-keBox'&o}4NIEV[[A !zӛuYBmg,Lٳ!ɤ-IneBzoj3'l72xyT~^B66I΍eftB >TB%E'j=f:Q|lk ߰6WGVڲ Q[!=79uFlȻpµi?i\ӵqm8e:`y(Z&yl,:1.!:  HXǚR3Zn+JF:@/̅JΜ-z6Ô.M g7o qi4q4gH993yj ,noBĆ(5ٚ<+_KTUCWP cIEG$7Y%8B6oM~!z.69A;VB*BL/1n [VN _&-¶Bd9)hюӚzE=YMnrF9w. f=CiUY4*eP&!MB]\ ʮ.. -8M״v9) uK%;>Fw4gܨx3tA֠8Jڦ3՚Lhڒ&K ZvA!R+%&?rQY"]zE; ڜ6NW&51( | cv l$bjM-leBoz3z[lR< 6:Wƨ8?Rܠ )$jM&Dn)rӪeWBohœ#)VOA[A!h;6 Vy+np/mJh')Ԟc[pm%B]O IɾAM@ͤs9xgm <+(g'xɉ^ΔG ]돑:hEAc41|X^NC\ZլEv&] H`ثi.[oi&#d?*YGk޵9hXdkrEz)5`-Ґۜy5Kަ kU`mVuam횖E;fyԵǫZܙN3T-".HOC[٧Ox,bXQ³ Bx668gCj6.K{栕t.u5"Xh^JtM)!P.$j4d6g$s.`µ)ZA!Xaj+ÚPJ"N:^5jqeWBBVH>vh׻ q.~-DYA!\P*`^7J{XFBEõ|1t̤б\tF`47uImźV&J83y$+M)0/VPR)O)ݛT:%x!h3@ 2pWe{( Jن|YyTTN*Abm$Z2i\C4In(wya=U?TDNpʫ%mINvi5|JC?|^v{?&f&)8?5S+8PkKV8텟GU9J=p"T=w0Qr&*̦vlʂ~HK\dWX>:,+yE@3W7.~FV=*NMlÂ%m7G3QivL7r NFpyNh|tvkb\bRعpvz;S<"pWHeY`$Xp@%.D; F\i&vLpXLXOSۣFksC3֦Պz%򰴥JAaz!gͩd[-X\VǒL{%vfhE-9OJj5ṧgoNo8 J3FnLlJZheGGstޚknt>l/y1&0z U1iNwid30~gQsޔ[. }lpDQOA``ul.4;\[^w vZq0.#Ŋv3aq1 u { mx70AȤy_lF&C%.oaumw&= 6otfۅ׷|OPK8D1rXg]Shift_Register_2x.fzPKϝ8Dj`Hi7Shift_Register_2x.inoPKfritzing-app-0.9.3b/sketches/core/Stepper_Motor.fzz000066400000000000000000000771011272363601300224060ustar00rootroot00000000000000PK28D|_Ӯ} Stepper_Motor.fzr$&rkH;f @gLԽHicYUX$dU_CW8Ov8"3"y!3@D:eKd?w㇛٧hE^wW׷xo77_Ǜ_TMEhDE߼fח+˙cxdgWo׷w>|z5{~Y|/ٟ^^~kWJUH.?_\{+Yq)7:O׋:sn/>سyX\\ÿǻWnxOo߫ٻ뫿\pR׷f~ wފ]ww~}re./{>}veW&L>ڳbq>0OwJO/Y4'>r7xLtg߾~׫ر:17ыåykB7F]s-yȅy_|}{юp; 7ןΥ.nϛyqaqWzYUk~~oᣵU5C;z?iО W^Z$JY ̹_~!Dvoy bu|JT\R7Z- }י=O=w|A? ;o|W镞j짱'OaťŲ7-_ү_d֗]5c#_!]ݡi-hQ-{xV[âs9ݐ.̵alSQ|[ cu2e#만aK1ݨmF'5jypnPQH[+^2ÓU>dj܎“ ȃ?0^8LP)9SE+&qgv>1S.\lI5t{|I|dw>3Qz~䩆1·qpϒC5rs\MGKM~RŠm /p9iKp=53F|QU͸XSiM#6Q6Uƶt*3 'WGD(7ApVVB=58q*mVś*My둛*c^77uw4"FK\O.OxJ==Pيq;c.7Q2npl̔@[>%6W"R|N-'s Āوœ`\p PzۓR~!O:n\FyŽztw؎ՃZGN3i켅ͣ"5x@%6b a'O#cw`O?%>nEJ|tcO/i7b,MvX"Lca=%Ju[)q' :^ Z@k{=n՞+!iX {:.&WET6{jJViwZ,P'gH8v0*8vO{ RiwW,LnX L-S4LbYžvKdz~%>ve{)%i7R,I&FHO4Q, Oft;(#_,Te(LG|Ss0a;gOdgO>EJ|ܫ垞e&#O \['e |hdnah2r{O=„=@6zfwl)zv>erOd ʙv_>% |~OO>ndT!%iB,Im X@O bq2* &qHRZ9އ% |r{N@Oa=87<,M6P"`Rx.{)لs}˂79,R'npXLaiwU|ZGNyh[e} %5~sjx12Gn(FϿmXݰHOa27᰸s={(sXOa*>"tݦ+|܆JOBDϋŪ6vexXў{aqyX%fS+DROa*>|܆JPʸ]{: XO bqҞv"djadڽK!' w)iwD, T&4qO+bx2񾈅|ܨ3bi25+H Cq >r$ʼn{9@{:$'f={z$)wI,T^7I, ȧ)H yraL*b%qcʞam={:&'qs]=rJÔwN,N^0cxRzg')q3= XGL<mb"v/"E>ne|܆|XǽjnI活*)ɥQvg"E>ފ|Ȳ_=j~~7ooEEG6~WwοzÍ OחOwO?7 wƿܼz1~_d{Z>oiO7OO/lvs=?_?-V??,?<\jl_y{o~gopo/>\/|́?,߽L|Ç_}.]Ο?#?.߿{zfd_O77w_[%j|kZ ׏VrwwpǙgtrzZͷ{>|}gl۫ٛw<辽}5p}nQ~z3v?-nn/g܃|?[^Ycfn.,f7׋ݣjvmtt9j!wrUs->?x|XM!PxO?^|3y^?iߟ-݇{]-z<<9m/n>'b޾m /2{ pkϱEWCuŮn[[+*L٭QV򜉺?\_콇z&Yz5CysɕzsyqYZ^j-۷FŻ-:y_~pq{g БWox W\\x7`m0%tfU}䇘w׷O!Wo?_/:tuK8cEjPS(=iՙnA߬3Puy &mz܄S5#psR1#o3Nq;؋'803Nӊ随MkI&0d)Oș_0唋JJO9RQAp5Nԉ''N|Ӧwx2xwϟ.n=|}ɖ2?>Pe4XSdž+;^KB|ތ;c#My& C׸H>֏0l#E^05,b,/f1iX9^"X: i-;52SM*)˽{9\לLy 2#&DemR%opIcЦ" 5\m2%o@fcЦNiIp׏VB% w~ rB_nf.n>ڏ?6\jgه뛽}Y;%INӄ͈$̠ibtl cmDhPn"԰o"1*CVA&c~qJ gW_W'+`/OJz("-[_Ħ.cb!|%Mؗ4.!:C!' ^3'rUJnMT;1Zո9c>gD&&F=A칉)uTѣ#`^nܙe*rTctOxOt' H*4hgn\\B悒UB~u5 6|/X<:wh_f"x#v `T[Olk[.L"2q\.OJ􂑀)Ѕ2$]cE˝0weg)'")|tt̻ QX Zn-$]B}U0O{[w\ %Ek9d0Ⅸu=uqoU(Xh!$vސ%/Rb:_:Ri.w꤈Ⱦ=3JVBؔC\/߱Rb^D Q'Y'7Ķx!dTj[E댔OuړsԵR'k>|\+N k9& LRY9ɁՁ>Zx &]`|puޥqG` S*MRmWn.~-{)#<$:k1ׅuRmRЦ&rI pS]ݦ94T:JhR5;D[[qݯ5ten^/c*ܭB5^-90v:ŝ{E2҄9N svޞe{ԇ%&&NMp9rkiΒ}N2f<ɤ?2IY/,BK~%;^eXZG\0usv˨зT$ڛb'`퍙8o)i64L+yi˘nW+YFRb4%yRDǠLwiIBbզa,tņRJ~\Lkӟ?,>>\./;JJDN*.XD`S:%S)L0Ϗ~e%s;Jt弍wsWJluǧهGW#bUD'Wo ʍ`Y|E{ )]g+f8?B5nSj{yA|%mTͣӔ!&q{%@SuvL QQҊR.iP-Td+Jn=qr6;Zxo 38;ԲJInSQ?{\ƍ:9uRvZ:e' LsK9 o lNA~:&UJH=Dzbs,d a'h}0t \O:}3 a♨k Ȏb*̧UaH0w渲|"p?}*Y2򝑉΅ c۰'C1G8HH1:%G6rK%g& -ӧ~.2wz { TME*V3ɪZ;!6֓Ne**X5sh#NV(=!i+n1TrU !1ȱcؓa#ƍdS&dx#=l$3SxIcHᛉqUiDjg ?J$(ᷳ"6#? D8I!:$QܜH9;F$"G2ʩAN |t;GܵNiv9>Jc z"QȻ'kfml^R`2? F֟svt 9CέɨN7F#C;tyaPyU  J๢hiV E%]Fs\|n0]ɺ8H/!KBɎJ k"p4a#4p)&"Ǩ>^0Mu[e_5dGie{S(ͤ.^nlkm9&1y_*bX&!SE!1!H26z2sciP)&Ƙ=^0kCni-2Lϩ10d sUE9 UlE]).b> w)SB<伹*6bL /l"2l D X; (}"%"֢"yLj8@_|m{B*b @@d ~, D #C>PF2JFSr2Q |9Ѥf.f*um\}L.uಈ)^;Fs櫉8F#p9|TLStQq#|T38#F2LDq |ؑg ݪQr_j2'?鋛W FI#>3sY])٦w3nq`,Þᤥ $o 2rNĎ{ȑsx#txr<4ik 5b8J;)ow۷ov۪79 @z~xKAHدqh#B= OFJx1ēl0r <"#2F2)RN1{t*ݫ1ONA:kfmSSc*Me!SVTTuxSJk 1O{ og;{ B=o3vFHc|1vg v#2A"BGb #0'h}0n,E=)#ޙe(VءE}r]7H;oMA;jMJRQIZMIK`%;8/{Q{yo6y'@- @yĮw䝈 qS% $sI bS;fbe5di@X^Kbix:h4TÞ5p$#d:r !Cwey98k]m{%[}JפV9Q=4&//]CAVBJHt[)(0Xcv{71$K' *=; XԆ{%}"pda#T`'ǘ=bF=]w'˼28e\tgj>R),BK23'5É829IJťD=[;"!~dicC> 'MH?;F%G6i gnkf쓺d4mI>町"5 w o!95`{9uP(mTٶ1=6bcC9 ('K9;$G.M9EV#jz~pw~u粠q+pA#a/`nG*jHyƱbfpgVwPI3. ϛquIwƱԌ<9 pSN&r:Zn lz2Se+{i>]?^Y|"4\bCB܎œRVּۧuIj/?kȝ{+[4^|-w!_tg{5Y{ͺYB-hS=p=UI^QvBx.`p VmIx%8{i-]=zo>>P(| z>`;?0އ9u%}k+{o/L TcQ83ݦkWieT8Q%*Go#{nyN4HZZdx3/9T2hL{젂)*SPejL(SW+Zg PU~rZpv.[x+x#Ko-cNp5rk@x1KLB MJbw rRF+ uB/+:9'1$Erx%9.+^1;N[l(pD"mbSF-PH+6rD 1`%Z+Xҽ *T&k1ebt-LB0y)&s+ ]E+ 6ePZ E9FGQ.JRn;D6WaQŰ۾iTA"D$3kSΰU%a.4 ֫յQcժKkpFQehL1:|N\?OH ՝x#Um% HLt|5Ƌ"U\yZFXCaaARdv0Vp-u등]:Ç/-<"RV~6;Fsկ ţ'DBM.V  /K0=9^HF=Nč1{ȑnZa5fxTNS.Wg.u&5WWuvp7X8X(4Kڼ4ir #J1&AĂ.ppl؎`QWlb/AUgx1.aA._XF!Y m\@غj"M"oSd*Jmd3Ď=!5r$ce2!t` cL/|"h>xtfn>.jmʈ8I$nݦ#f# 2q=X90{go>*j\%BE1L)U2*IAĒ."$Ơ{i)1j־x}{7׋MVh^QwpgqŇf.n>ڷ?r5 cR\5TۖhTnFL5!&9e,\c,=9zQ:,dD%c_ȟƉd>Ir HΦHC> nk Q f܌JMQE+ɣ s^j )r,W>Lmc4CH[PbҞDxZ\7ER1Ntp Q. ?K9Cr#HX6!ؔt5UxH/PR4VyU%X[H2N܍qlFGv"cؠk tm"2Z"8XZV yZ AG0dGZh&T`0;M kˑeHȶf G1MR +w +KAKhy KHB6E}`"#[JX+B1'b)^clDUcdtDr'FʒYFzJ{hntwNo{A07!xJiLC#9O Ⱦv KJҘHȄ[yv3VpY+VM|7-lWMnҘXwVS&>KG da" HVP! ǡR"2Lrh$j=쁨enMh\V[x䘨ؕb!;dYp঱Mz&LS̮ 'i,큌,p`,!xනhĎ,%Zhmhlso\a$v#?$0~IQDn LA?NeEM1=kX_S}`dkmL%:T_GZ=@ Ǡ54B݆LE5rw4SWvaޒDeT&„ \\.6i K$ hW<!C![AV((EH(1XA$뼷=9"bc$q wnxrZ' njfx4Mm=NyNG=\%4Fe S'zoMfdl;%ӎe0E%%uE#Y4MfΜ4a;J-Z,y8mh=mc,-Ӷf -B(σ^V&`-%4si K%hfx&D$f+bK#әY/[TjM'XE0Pti0le4}Ha,2M^&# A ϒrh r"rg7kb4o ζfo;P!%0|BpYW4F7lb%= sb̡=q Vnf\(O/,Hv(TK&'Sj yK#]fx(JE/-op.Qql9խ-\&+cIhme'ɅҶuoyT5(HJm, r1LQ QM@DEwj]IŠEz06$03!f=p=5ѐ^/C!sC*܃FV:j<xT֘r]UK}WbFY0 bbhByjZ4 M M 3X!H Sr„m@Dfʘka6,`"`(re%'Gϊ-0+n*`M"5{*I}Dҵ 攦Ӧl)j Ijo|MdfwƃŐ7G֛)% ZW(*Ր'RBi3Tv;ٚh^ؙL79iKr@##7cM [܅2C;<01nl4p)! ,y'1w zme]ɢT7)E*+!+" 2$wiC,-U8+D%e5G!c FX[?5u8x?N444@w( ܂QDXcgҷ0rޠ!+[01)j-T.`cw4 pE<̽V0)w˳!G7+Xnxa+`!z}B+P1K%@dm $T[u6|gyb] 9ۖ #r6lTp"TV#"kw kK '$hk_cZǦKIP[}-{\PTsq-*5l%C 2prڌ-Q- gfxAζ=pMhYu@k᢯d-:p1w@P"dmPZpD-%6QKg CKк>uˤenFCmJC;Ũ^W[|=9+%b7S 8);2@ a 8rG2"rh,3hZjTu+qoF;,-D5X?s5]fJ<,G$i;EN;A=BPZW4PZa/ umb$"(?Vqʉ1m)fI26 JmPZ$(˪#=MTŰ۔bҹ2dL9]Ui iK&Xfx@A涩37Y"%x=$7"(GVeZXyBqkp v5@ِ]R(pZ]8m@~@0D*w`S)n %UBzGe%cXA6>rmhǔ5 ֦ 1sN WלZb#av [J8`[Y@z^Yj,®^&+$ }wYYTYc%rw+`1!7%Bӎ56%@ܙ=0: 聃e{jG. m˥jHsQ7S&FK1QXlq Qr--T-`;ڦ22PcRI؉sZ,h,#sO!-{K0BUtH+?ĎӦjyj `Zw4C RMe쁪TMnb䖴ʒj'k4[ K22r1aiF$h;$h)a ZrYAn(lM4v4Y_Ն%p5-䏊5-+e5,L!Hdleb2qڌ-I- }fxHA=з:3}k&uH![K6::[GlV&r`Dq4-,`9 lO4LLĉvu`s\ q3& #8R3CDibRif2hi遦4-Wu i9> C;ErMk*ac <-%6OKo OK!ӺFmPԕvwTSd wmH i{rMztm ˨ r2Q9[R9fyn [w4 [/QRQ̴-TE(qT5U%!J*fpR.x[ & ,I݁mEr-=- ofx4AڶC mi[ m/벗Vn-j4'Gb٩q)dGV^̩@V&`uDq 9Lg)$̆MLfg<YWyڶ CLrAj8ZJ8mzArA0v(rrv7wpnQ3S/ўƞڝ'J,>?4WO_>]|﨨(e^ f/߽Z^ULֵ1sb_<_p3xx◴"-n/ﮮo}ˏOowO?Q+忿?w?7|z/r~?=?9^=;s{;ܞ݋/nVG>b'ьT_?=;_ꎟJھKNSwj^ک(%q_~ZBkݰQהy -NPAY5ҏ wc׵yg(rI-%t("2콨`7VDBgVP+[tǬIIiT-RAԸ/ %̝DD'"~/Z(;zۧǭ%u>#gbxZIvƌ=>uŭ@@:0nRwR3a'dx ޳/Gv0Hw9j&\3ax0a\XةCR*{Fgo[ʓX]DϘ}V)gX75\-c듳ob/ά:;Z\9h9ue|2 u׌ڿ|oCwyv3Z Ȅb@wRᎬ/=~~RiΚSXॿ'|QgaџOx 8#Ӽtfɤ ý`PՠH>YӀq ܝAs+Jg#ڃPͿbJwqÒc`MSf{Ӻm੹[POGAS/_R3W? Om^EK|y!N74 ϔ7kt?re ?͝{JwiZf}패T;m̾#Vj-6-+v`z]J֊*:~u7@eZN~لqIbVm_eĮyďŃWO;հOsݝS)5"QT+\}ǹ>NC#DNtsG'b V-֗ȵ{mbƽ?Svxah ƍP5okӛy3a8&ycc*ù? ~Q#ie[]o0w,Rk?Y K?^@(ڥнy!^oI-{oynx OOmpi 7v58smӎo3vuĴ-.L;LPK?h{Z·-!_{dbUr'X8aa1oq+pRH[=z3EsaL'L9}MHfZ1 ~g╉֣lrOW|e_X4mȖE#W \+aY tk)X~he)P@Z hk-a1cqV?~5 j-д&!5:k]v1\+g401]`H 3؈f.=fuTٛ>Dά8p%9 /$1YݠV? % xdX! !gK[~9Bp$yܡ0U_ Fw?2oOS#;d1nE"R+$n.|%P}ΛQhtY*Y 8xp70 X!Bd c[F *W{0iP.} G?6`?A醉)Be黏JV 0 ;2Z7'|A[Iq ^E<Ζ5q1KmGࠄAQ͛0B"p2oh 5 "XZD*r;zH;`x909 yUvL.ށh__Á /Z#?%R5| ֵV~-&7mh\8u ꗡ27`u) )xW~i *qrXJT^ !y Cfu4˓KO?848!KR a$,lu xm2@#,0 xd^4|`0uFgma = ;P3@~xAx`@# 6. #ÂR F\7 bN' KPX@A`/`0EyG? yySӃ/ikR똝ZpS:cZRœؼSWdo&`.]@Iu|2KXuYHXµr2v5_-\o.\AV)Za j^$q[;$&$uVXX<˜I4[,46|4^@! /KȐ 5N(Y#>(C`9!W/&YO^+8GZа5 q5 7"hrٗaO._{6I>6=odF-mx7mr몶N)73#egi͂༂~o*`β)iZ[p,0`7|,yi<#y [.cGጯ"cKq?jy]-B+**--F[BK  cg*hGf0^SgV&3@H iLba2GO|5)`GkLdenlj*Fq`9`A'U,쉂=,r?NY8# l50UXB$, *Ɠ=M}֤tQk~lp#0vGE;YHayda. YA7\_k:\8a`dM9Y1Y?X+a7ml7WN^g߿v¤ז|\<?ǚ$=pQru%~S ^m,SvE{9˖s9̘`SvB00H!uRaiO z\ZN2;~HfpHr3 ;Xj@3os O {moX+tv.?SWHO* 6av&5[AXK]/aw~36"uFpz͞k.Xؚ9[* 'g̣|{G>g450a.`x- 0+4SNf\~!r/U8<6p٧yoxu`B'o]է惽%n.,.ܻe>.?Ͽ׷Λ/nn6/_@1;(=vƕj1"|swwo/n֩Hg6k"nWݒd>]JE#ȠX=pA"hU Eh|ÍV{AUqE b>ŏKls+k>_D* Wo|B|? 3wYͣ70Qvp|Oڻ qO c%fmU󧇋Ƿwf(G@/[rxϨ;{ N] v>k}]@Ilxz°%Qe~o ʨTA_rAAySb5{ #%`Cd4B Q{Q +V1x 17?0`*2j`8T+ZfԪEI',2-»ӐûK8V\T95q6O;3͑2C"Ey`$DŽ/Wf^3q2G*5XIum](bKz ,IrkܴZd z}x;́]3[C]{_f"?=F\3OP>Kv=~iYK[Fttiq#'J`kd}>}iLHO/.WԹȷuf O5ޑN4@CCĊXuOY%\+bANevͮeVej쭗4ˀ$ѥ<%5ċւPFK F޲KEwp?D,DQJ{[)=^`m+D\bG-#v-qZv%Td)"!`"ճXȊT$X탥q22`Yh 'n*ܵ5s۞v%T |,&CDNdA7<$O@apjJЉ;D]ׄ6 ɹD{̓'nEPrٛMMffouN8uݟ`>r,i !mn@P,|,= ZguUk{ `G0:e,! 9l끜ՙəVے[\V5CNV,;GӃ2M^ұwF45Nf}9$52̭X0AR$enF~(갛_(6282@Qdž,:ҽU+Ű2A Hz1p=0uRfxDoߚj\4dF!fxm*c/ws:&R퍿adK- Bu4CeCIAP'AINA_H@sI$q}!`:eB4Am{ p"/[oolqaӹlp]J8mF5!ACO7thZw4RM@2 tӤm \̝i0/| @B68z !KM7$Hllx@6؁llS4=15ZJnXekO@.;H-}٧i'GEcE=O@{%;r}8Ay$Jjυаe7TҨUuLm`$Sȵ=9@5zi/ L% I!ۺV+HeGq=$lE쁲zf: I;޾X12W71O282 9 /KX7l="GΌN A !:K#!ud43|TˎO!,wF3 &YִOGcC^H|x:iρD'ֺV~u@ _Bߦhz`|3&. S됧W5q7!+ʸh\/K6N@ْ9 ƈ/ kQnҦX-Xs5\hGL\腫̕E )MhƓt[IֶYjJgiZ.4D !K-(jJ?k@r aGm//p`,vci ي8 kV@GvȲtRImM_oCTzAZ68`,%|,7lGf;,HHYw4r:8AFgkyJ"%=A|F"(;;.d؁LlpY"8fVV #ȿ5XB-fKz E~mJT4>aQW *| <08N&Nz aBVm!dI!IWiH~~+큩ӦSh HHFTeIrT"%{ i`w4:H7;&UXN[JJ9&l,ZȽ=蓠tpgO% ̘L!L2fR]ǾC02! ]d=5˝h2~LV92X)C{ܓ#i{[VDtlph]h EV9b!8rd 0Zw4VYQ2 %]1; .VF!pF5>W= Ĭ;-P Ng'*L"s࣬\,垳1Ҷfќpmp0AޖH'BsVi>BJ<}AI͖K̝[8ԭ;ۊc-s$ &Ec;!gςբ( ;C.ejnOK(is rqw%L}M\-sP -YKamU 0&) IC0?N#;p|cp- 7$jc պO5 [qi[6 S>&O`iC{'AV)S5@ vھfQ `3ݤXIQHA귩S? @h̆1V.40|R;!$΀"H̨L̺a=1וтŜ`"e !&u큟.;u*.[垥QUnI03Ñyf&e2@>68z``.%r6)p2_f  uG3*m5TGl'-LB(Z}e;- խ&uu?1D+ _Jwvhĺ&%F$VLMT'2Wǀ$]ǾBdBAr)șLe%)iZlCx(861{$j&eRBiS1j;5#} ,fx@>G쁏|LtjDAq\>Xq2vEG3|cka)AdNb2H@.: 30jSoBE-Q ʝ@XRn[   ,%vv`Ltc[džc [#eLfRF,M9[9CIm |rC%djIA$>rX;w"3%w2 A |LIל"K8!~莍Evw; YQ覧1Bp\Ҏ&wcR!q#qj]{#b{ CfCƶlBUҴ خc_X#!`lo_INi٧l7|ylͶc42F qYlA- DSNwPnߢB,Ɉcw4ÃA*;zIe$sIK %-ueiV1~M0V /7SPPΆ$ wh@e]I@ X7wOOwx0@SДiqz 0ŜdR5/F 6Ő`2 :2<=r9I8!bpV?Ը8A/d `'2o&WŅmF7\H-/jl8i(xx<ݭ׷"/lq ï0M\ z뙺EΌѢ(0O_QѢ%[Y]&G4M}>YO~9է߄t")KHBCR?7 -dZ7:\oLtw5+Mhb܏A*_&a wmJ?ȗj#rónRΤyy;j?Oq3Cݐ 3=} awçu,ZA"|A-a%LF;#GRgggxdVT g;^?&w5l>F3vMM#h(3f 19</4fr<$r3&_rWä&{L[2~aB/N0YHVJ&OK0Z|ӝ1\2VB% /S$Qq& FL,Ќ2Uh*eL0&捴V'w5fυ립Ј0GB%LT:G_?ߏSzwMJ…(75O4?rITjo{{ܙM&jQo_f4јnkO:Wq&|9QlQ#p@Ѯٔ)v`9ri35C(4T=J՗v>KY֗f\%XCcsZɪ:U:U(0/_]v gn#ΕPiqӬ#<#,CireH'fr&>ܺ $qne!o7eqmjq0|NtkQ>g7O$ {u5Pz*?ܥc4Dxk׍דkQb] $l*F yv, !-Fvps\aФiʹ"qf5tlqkTŕ(5^%PiA"U~o"Z@IO Q=!}ŤP.($6Zч\ 61w!ڜm:\*^qtՌT fȮu[~-=P khGؤ-LoaRӹ SlmTu.\nbA%kWCpO' 4j=%d j:!XP>*{͏,k@e oj=+™ QI?Poȯ:Ѹ#p3{qі&%v&\ˀȵp51۸9i$}CO69D3,1BKnHp{n~fFU_$q6Q)I=I&5~KN-dLntmlҭiWGa7ֶ+T8V8:-} ]M*OW0uJ8/V-K] +Dʮ\I2.K W'rYA˕oOtCitrbntbpݻs++s~Q^|J&: Z^@$ʾ\Ib.L դ 8w1' ;?fH~Xu$eJѸݎJ?To\ Lj˱#9C9\HA:e&[eG 2)6+(5p'{7&c., V'tpݻk1\hw I QhI}˵F Z &=F V]xțV9as8dIVdžރpo.hh#l31V`DwNz[!-F դG 0k8_eFwٮ%mcDr&;wkZT 59r~zjhdeL{>{~=xGlPp3xZ@}z/siiyK1D~²$#Rx nlcoN'edd' N+*lٻQyN ![Nld j<ݩ,]$6\iON2 T h&KscF }څ2fWAKth@M\  I gsR2>Fm,EVe,VEg 8j,9zw3^*k鑂hY&;Zv]2k$=|#n8ZF nЫxHB'9}Fd@av;h]J4 dId٬ ̀ ȯ֬ dIJ6Zd0@`8۵դEƼKNd1$>of>whE ceHa WO w۫bPK28D|_Ӯ} Stepper_Motor.fzPK>}fritzing-app-0.9.3b/sketches/core/TemperaturSensorLM35.fzz000066400000000000000000000561231272363601300235300ustar00rootroot00000000000000PKr8D6$XXTemperaturSensorLM35.fz}F5wV۶f̒`GIVIjLZ/YJv5$J}y}p $2K s_}/_ьM/f|W_=\o+Hf2}1ыkj4?d^+G"|Z}3»WcxNjh9[N|Ge7z7_fKH_>ή7_R1R˻WW2S꾚Tzn1z)S\Svqyy!Ȕ_\+*8cjzn^$\:=ݪF᧻WDž˛>_O8s2)wrosΥ₳ u\kW|q:?_mzx̵$[|{~{Jz ju}_PEz-Mo=%!9 9͔?TӊUn~zh]7G{N1}|_A;?]s=/>P8U~ͧ;69gIdN&sf]ZtSBњGEރ&X^˻NYƓ:Ol%uxTZY"o9@3L'63f<1?Zޛͅ<>NtG2>8J8L7FУ4׸|qs#'&~ȞYƑ {ScÞ`z{vqd؄o382a{RRQFgGKz{"qlfE7<8Ft҄{p3sb)&~Ȟyq {xFG82L=cTaOL7)pɰ'Lj%ÞzRv{Fp\=<;/}\d3aOnY{uU6>`89k!",Heo3y,T=`g6pt|8gGÞ=+8>qL NÞD{y@{i=;815QgGÞ#{f G'M*8 4/:ܼ|xr`gpl,\ZfŦÞ5?RQʥDZa]|ೇēN{>/j 8U|sS͎:2yq"ʰgǦ={GÞMbL>8J<4|س[yŕkg|1,_dvX_N_G;/__~-Wo^O~\Ll]Յ{tr{ev6|-o}1a`s9`4LfTdi#2nhtYfɸsQ~;d<>Opt~Щk jʶOT?/&(4ㅑR&(:KVϳy3UAemT6f$, m6LmD֩ԩfBއV?Oh6Ę4ҶHQ4&iy֦ 5B3[9򍪧]wJXh}!CBa=Z+}uU0'%=22'Ȥ }L+ʹ@%1vAˎNgѠ3Bw?)wrQޏk?qm9--|^5xŐ!ayft(2s)5g*Dڹ/F.܉|[IVri63#Di9wm'KTQrSdg67:]ACCĆ/,Yr}~i2Z$Kh&&K4"EHd4%K6Ki![RQȖ<- ~Y-f?mo˹E_o*Jz+T,m6f.Sd&e֔,I,Y%Kl>f%)Y$Kd,U hд,ifɶ<kMנ:eK|b%Tl %H0wKXO0%5w.JĴ/G>]ʗ ؄I(9Є |RmQ!D2kԒަKb-K׬eJ b/)ה/ɗ/YK(0a WPb|$.&[@F] &u)aɶs7T&&bH[wG#w|v9v-%*2OF8mY F#!R#(O{9ΑO7 '!!3% Γ~.jM#ioo"ix(tr%Ȕim9q&bg/v\ڤID| 3p*ofXf(I({m8tҥ(IVb$)- ͇i^j$*|OyDR4쪧رZj`&StnH/ JrY6gޗ( )n: ?ϗw..y?OY=ceD=ߢac4-BW!+Hƴ(xϓb)UlFbx۱-;h2jmrѮ!nHf:[а *:{Cg :˸ɘ]nw Z ֻ18+b&.SOz1x3kA%~NVA> iK܉!t{n+GֶZ*7:e1.`~̸>痳'pZ.Ν|v}#ɔVHUfd]Jq)Vk }k_LVRi?pKOwWO]o#woXW5BS)|SIi}㠇)$TdsWYqxz#$q4'b={ff:)~RWQj3]bz Ukl|}~/\Jv&q=(R9'o8L o|y ):4llw3N#VS8IiX_ iڛg.?Wp/\fq{Vyu&,=HbY'm>L !{/|;ʧ䞌4ܓeDTL.]=U~'\쌄MDtkoJ@_aHDt "dmZXLc_bu"{X&>H^,emGb >#%{Xdv w`mz?{;_˛d33|dͿᤛd#&?|?7ݭ r>\fW+<X꿇c7c縛^_V7-+ NOc~trXlt;Ǜr:^8=ΧGwؤ񋇫O#9 tzo/o1?tfZa~w~vA>V;ݏ狇zYbo_>\cV]zn<p3yA<r}fS7rh;ܻrp5]FNӨq>; G`'WFJg˛]P>Da9wK^?z<=UAdu9 d)#޳ƃjxخ{LlLs&ٜfp;^kC,at/s:ޑ߼]ncLfteLʧmC2bs'uP1ɬUpec׌]W(GeJ/_/n9,SGȤ }cV>u3m#_5V6p*#D~3UfcM"ku~O@ؘhcNe'@k[̘8wO2*UxH nTX}Q稟rNw9z)k/ ,Ia2/s38ʥ|c)WS2JdBj=}awSlIњHk]klNM{UڵTקX1t=6sl|-o[CƒZ'j.a=4O(g~cdڷfwoCY2_Tμvf9s%`eKFWʋS I|5AIo}0$UN6Sck03.lV\2r Uȿ>ubIci÷L+.LtvܧXñPR$τ ˔ '|RL4ɶnLZ㥝I Oz%#JWԔdR8O Pk@+o0+H!BIjߕ'~7ģ#ţrOuo @[4eZt>!yN_0Qӄ$ۺ=j_i$҄%xѤ3Z۳i:cU6M.r6R(:vN>:Lg 5$# ˜L4)L4 u{oNPQB,UZ8Q'0D˨AMiVSH_'fPiʴ0Hk Uw#$<$_4銡:8CcVd)oj 6(ik@@BwIC!k,Juf¾mu4K'% i"lIwt{0-^qCLǀPux+{7#:/ޠO+cڽ`/)!b{r)Bql[(YXh֐ 9jgK绅ʪ(FO}|뭆m"G]aTQ "quV ɘRPn3y<2+4ѰB T%+n Ajk틤l^ YБY ;ªȮOvԖ41ʞYT!+:)(Kѱ8I9LecA2YWҝX,0yo#+cȋed] .Y>7 RDb{*;Ό逋BKJe9#_C݀tbfmH!a.b ;(J9GLjzFeJIeivK*'[i]n[6Wsxu4/oeY~|^ϗFw/_WGf]뛯L9^ ;ʆ)Lih45&Jת6 suhǻ䢦"S/&1a+K(:Yd _Rov!tY:ψcnH 2SԢbfrw^Ю}1Y &J~cϡf26)pRn^.t'g@-Cy[4Ϟc ¥&[~rLHR# QK`  t](ܮְ#zFG |$hoEv5f91@l)3)#uT7M]B.$a(qHB$KtݤL4S$SJ569i`e3{ǠU*yzq}ŒɎYaGFD&s2X )Y"F%~za/Nܐw '%6&-r_A(Hkͮ6R:#F):P%nw<^p>ަO:A0 $ nidDJu?^k#[]rGR5Mu(5')*<VQCmwmLiH[HRƭڛT&kSڂW f8'+͐[ B#]MO+~Dj{[K@ C$οKZLVSm1IsZتIUfO:ތ]$ߍfiREdߥ5|&M-I"g;NQ KmB^zm Ⱦ /~ûܝFьV{)ϭ琅O?ή7źTd2C 7ٻeq8LZk \,vqZ\t=gݗ_^gw;J>ϯoFFy3lY._O&?~>anr1q'Njc.޾_,U4ͯ请ߍr?˟^܉dՈh4W$qaf!w 'LJ݇x5|K?8s︸5N;Tqk_= 7y^>|vBLB,KgWᚷ~;8?//n>?)ppΣ2B!U?~gS5W~@g~G/h,C+\ f%Y펽q6@_$_w7PwUcg B)`%,ҟE|Re?:H"g=9&r{2^ĀE/DD%Fx*&oHnYAcJC$4nf Ql |82+ @~\-FZo50)B[c>;ew G KCHdt(xP+;poXS hS7s~s8se׎o3vƵ%.o\;]JW?hׄզlB6qӨ{ H o%qDxq)/S<  n2sOQݼ>hSpDlƫ`e x'LԂGYqw7:x AaGS2eF<hon)%SPX0)P[L1d XВ-@_WFXBe D&@' It?Nc3zy v/n@С 9K;lDvp 0OխWep}cx2>@Y@ 0 #aIn;]ˠ[%gQV`` Nuۑjt}VA8LgVn4T vpY.[3q] 䫤^ 2Z]xCP yp4 ptP\{"рGCPV؄&a÷׸@qYp* 1f4P"=?K[l2:;𮊡㎴3U346h 868vpixRPBxǀp'ENE`sMBkVAUy{ ދ_!/Jyp Q ުEJɢ!2ipKAbM0 DI12 @T4`xThB%Qv>`xE4. @P3:c+9Z,DA;0ΐ[%@K2 p8%$u0ꅎN! =^[x>`LNA7%m0, ]&eઠ)=҂ب=Nf0xOxJb\*[r'ZeNO3ovvYpt$e82 <)o% Db1H"ry@ġ7!QpU(7 dm.@׍ Р\Q,rC aH & zL80p@t%n R@ #%m.W3RI0ZO_^@Z)$>Gp;Z  Ih!,pA`8 c~Bz[Ѷg!!qh` Z:>EҽU+]q*\ D -pk گSS4l* H0u$ B01> *;:0+')n@4Ag8kA "7lX/-tdQHb FtEZ cfWϊP<äLȘB,E;O+ E`s~[ 0geoGWz\7̿o1N9$*r&G):kMbq4mi4,rQfQU c@sn3!hb([d@4ba67l 1<:oCeUZE4x['r3@E'(V1IVfALb6<ķ19Ů^9?)a40p&1a/ !HA!s80 Zdx]7,(@ Lf0N@@ FDEDbcX@j(+\QAՉK[1aJ <ߎAឭdhvLKv cX\cа0&!xeŰĀjt(. 2b0Uv Md9a *ժl$q-,n@mmhax>#H-H4[9@#(#/i")c.XC6=.ݤQ":-;uaFʍRBª~pHpg PЕ4%y gW楸Shxi<|4C 3zUg|eJF,R+ ,'Q'p7 ߃fC[IaRހId[RW*+848%S A@էo}' (TM+^.u5%kICOѷk==;JAŖkr:ѪPʕP[ ђP˒P`kKBPI1RH3dl Ԟz:LPu# xhLb`q0#'h$_p =aI"@W7$A pbTa6#AlT0' @ʡг8#tb*\]c ( dޖ@S>k Fa7YaFdK,N%> 9R}sre 5V~8Qa蚘RO`1Y1Y?X),mYڍ<`~; ~8väcGN~bHqx\ݨJᡟƂ5ai(f@D tSbE/2xdVmEk~zKdYkGÈ8We`Qq0H$p(ۧAp*EQ"NXES4WXXtݩ R%uV+efܜfyPtwbK6yq bXaHB57<5Cپ;DsHiq<:ٓ<;}my< Ou791 Ԑ8OЙVz5^::KPpmQp+.Z+%f.6N0q*:9D;˒FLpamaԩ4RxT,Gӂ9W- I]2+`ECx\` 9FLc O☂f[k-Oi'"9B7FVE( FۅԬ8\vE7j3!YW]X(aA9!cxjRj_[,C NM-c0QH޸KLƱ/ !]t*0ddJLB/dP0)[%kcނ'e: l/>GS%nKSXikb:_Lˇ/ϖM_>ޜ/YLNߋ\vu{ˏ;>w9{0ݟ\Yg6Cv%ɠ]JEk#eݟOA+D|-*ф״]F2VHH5|Ngٯe]y\0p8?tuC+?YRL:Fb2|aL]Q~/w;KQKQ,|~/}rf.[W;r~#)Hv`ַ=nڴ٠Yq rR%NV.3TUn^ZY;PTk^q=T-䛮Q-dؠjժeU-S-۪vnURCS-cݛvEAopղ^q ԪV-ӤSlY^ѩjIyo,5`[ Y qި٠h 9AI8M <2pfjp=U ~b'1 }M=V\>D O;Y~S͸>&ݙ=aV,WDOfT-i!@Q-i>xӞbF+1nioT;-@z2./0&~toO5 TDy&Q{Tn!2++NOO5߫?ݞ[#}r~n~R_i5WWN痳'{qd}k(uAT>~D迵::r{c^;Wi,8yd-#!xb,ǓkvO$Dʣĉ%}>$  xz`@ko_vF_q7E%|DLE|">YX@3LvHV:l$/EbiSnU/ioD؟\Ӡ.$Z8|#ۃ]HXX:$r<]/$)YlnƉꘛk%r(JR*m@Xh%^T5|#LIqDv"k?[JnIʩu{0$ԪI96ymTt8$w $¶!8ύx$RX열k;zYXXmCHgHʫZhPó6Q?K1q#ZĐRیzWE gΣpbfZ(N,yN(Ѹ1$تiG \rעćSr_y<~CaG!%8=ל={j;UMRb+Oe 1-ԈѪHD,d؋XyQk)2%2KUݏ:X>נ(mm8q7 8q^:%>87wN|{|Xa.E N X7Xp}'n ' 8qRb[U1$1Aq z ( #cĢ(K|q k#wτ͇fp h蹘hυe똭%kL]:P>>ux\Blqr^q(vagra %.8'ͅY9` p[fկWzM(YbȨ[5 Kȸ&'2W}j5O"'NR,@iَiqMqըsz.~c/KXQe$dۂ7C)"qC=t]/iK4 mmW 9d!Xb:mIҲ4&ǿO=U,E.a%SGk7ͣJh)'p:;`:sa0%i&_['`](M94kANlW[ (RR%=ۚӲ8$x2U(q < \0JY.NhV;σ`J9n2Ro[}[KSuY)FK-ȕ uphkCLgJ[([@ny(#LIyHIEDI qN@$ FsKYs=`ᵛ)4QM ox[ ڹ~*l ʼn[#ou "5%pڛs}DGT8YMx\EOL <.w`u}8dVZu(*.ALMDrR~$ɺ4*ten]k\REDIUќdIy $.8a cnT/Yӗj:7)$B]rc]SAT/t5")&zɵrːx[n-JFtjH$; 26IkI y@I8theҁ4+[n* XŅdՂT$P8NʴJWU")bǺ-ɮ#1P$mePכA%N}ZlP$eUW*Bc!XqnHF97yY[;& ҎۣKIy I jo4[0:UlTRvڛA%M,$t*c p(R0EhP:dtPfRygT#t2mWחfbn*~"~P*U؉ر/At/5$VM󀒲咃.%q%joǕT,Rg fBKx@M[-tUb\Zer1.3hV i<-a$H0tӤ[M_ m1)cBH"p#IʾU{<[(\󠒲o4*)VP8Rt%U<"b );fGAq܃%i>^^r$N<`7cJ&'Y D MLQ x(N$HvXm567$$? PJ &U{<`ܠ `Iy\Iڛq%%7,B&1ihVU$ݱl&E}t6D3GpͣH*lJ}+'ۇDޚGT"$޶Ii+|F*l2ڜʨ 9wQi# :^V( D,Iy$IjoGTY_m$f<`7ͣJ,МbR|06a#*6̢`k$$p5U2GLl7MhN46?cI"h-I"hmK'h]HbhHbh-GbhbOrY m@. U)&`:@cp촚@ n Og%z^ $fǺ4*/ &5%~G!%hl 5 6Q駱Yp*[3$z$zVכ1%ѳ6$$#3OeO^l±>P*8a*kVP%ڒDVMv&& '/u#5"u Gh悆`DS;#w\XhS->8-1ޜ">(#KYFGTW`Z hZpe2.$8$$fIܗ.)_*4GGtg`Z hZp)Z08Z 8ZK(9͚58sB";vFt8OU)6~Ƭxh9EJZMf:CZZœQ[eGDǓDڂ׌95MkkQmXh}EV)U4mV8:f;DZ@Dڄhg8HZ(HZK8="UEM8|)6~J59EZ &xghHZ pHZxrj_R_kP_k KaYn n,SRFUaM3Dz㊯$$6$p$Q$d0m? 4wPKJ8D}kTemperaturSensorLM35.ino}Tn0+9I#ruK_) A gZ,)N7˺,A ݙY% ɕj XqXak8lVk%JX R[lZ,JkR@ O՗60Jb b!I6MߊDrfT.&X7ZRfc)~^Fc:tfV(}*i5$J'I6vzP=P l*N tjw9:nu̠n;L p}9% (eb]!Mq `~pVYж>hɖV4~;ͽrmƍ7+sRkQBA^e`*y^ubk5q~mIEsCb1ǝBz =`!chޝJG_ܿJt9Pdd!~?*.#?FWū3p)_{'Eo:pq7ݎ&Όw`|qZcWx+nZՙJJwC{}s7z^t5N.^O٫\¿֕+菁MZ޵q>.ygpzOslz`zo6|V`>17&2e#7B^Qs^&f2tKcҜuA̅KryN@YeZ7=/os7p?v ӛM>{p0w\w٠7YrYxx6+Cr!Ӓƽ/YQ󣳇wb2Qž8!"Z8%fKJ=[nÊm B:T+>EB+.[gִԆN._7U-F`O>؅gP'-|XM({}|M'kۺܜ/-;AWlpVRImnWwsò2#&+ھ,k^c5g&u[K&JG":c*Kw5 :>Duu{[D5>I$,Aw=FօbĦ۟ ftsg@i3Aɚ3 ITJ Hcgt$LB:\3BCI bĩ:Cs[M۞ș3 @d=<j? y7acAwPLEI(b#jGڞK0LuQgw8j8'-f?3G<&|Ew6Oog{ӛ-ʽ+ yN`ʦЊo?ƽ'O }f$6ZO_u0J Z~y1[='[|=V105G/s 91TP(Կ!𦚻7J&FSŨ('E%XI nUb9B(kXJU> m+:{j_aj 1;H%=xpP)*d% dxLdHiB(MىـG^Ŏ%Dp!3y#<\l%blc[F d 2օbP,*tDviL2pKHpIX}WK)rjm~ *N,)qyIXDk2ېhfeΆXn.{X_aoZ=#c(:P jwLj^>!vYQ"H-D]S85@dRfP{C8`H]]S9"s#H:mIMM$2QfKi嬀X&1VېTs2}6S% (hEk(梻ϸD؛܈OL9`9gRW$p*}Ɍk>YQbG-D]S85:^adV}Qa=F2LU$RtbX aGTh̸L&Xž"שK%2m.܋MBzRgHjnQtM5vCfl~K'Y .liǑr(1!E+D/UL=(nCʹT"G ;3i#(CBT@7?ȔgYjV,iL8 1Eꩰf`T9fқyba[]7_J~s"1M;|W\J7wfxzSls8Đ\aKE7cF A-Jf7 /.Wt=ZbyƋ柯ߵ޾]߾}Yv߼DB~:k }rϓ/߽ cAl_$/W/p:=vkMz׃Wgrw }],ץStbuο3r4qg_PäxnZxњߏa].Z⧁+p~2kY^bt5Zƭvn\}ߛ_.~8h9E=`As1d聛/qo>E˷ ,dֽè7wu0Ogg8o_wy5PZLi{Mv70ܐ0%"!  uv?W~s- a+H޾kU`D{B;#_^pr!Eټm`k?fU7=x?->.xܽLg7 nP&eR{^#u-xkCu !34[8t4Y>]ܪR*Q͟`P7ݍz͆%oww׷9$oQ;4 M+W{1F1؉ئex {10[dvQms2`)}-:Zn<?u!wֺGޣ]naIWJ'~|0'7Z> 暜\?NeMe-Dppc =GƼEmXG @^pb9UkƮOT&֕¼TlтoɬxXGئNI8R=XvR6" A劉o*+X4g~Ǭ^7䓬>fvC8y #ۄC&ՏN&TZ$FɕmԹo* &3?!LEa O5يc& +o, vpzFg7Ƒ>++}ƯIOl7jҡfՍ/p ɀ)@)}⽹&K t8 qQ^[|%^qh֭/!I;ď7EltR{: $*EPOi6Q!o8N}J/H$Bh64 ale%#r?j5x%[ I;b0wLX,$&ZHDhwJ ^ZJ\r^x\lxYe]f).:إH&rj4Yzܔ"AW y-jZ 47X|r բh秏CC&}.vYb}ܜ.FVGӔN?Rk3$a[rѥࢶ~VkơKaá?{=>SJXɃZ[n:")lW+Ǒ $5.>RlV^JiwJ{Ѱw?j.1$ly10N"!abi_3' @jWeцzsٮ/"a#f~iEgM /sp# .ϭcbd™RLBW@502"C46 s:I{bv"iMV` Y &J' \Yl2R /{YRIo"s}gfUEϝE !u.,9&?;vWbbp]\.U*NJ)NqYG$RS.ƖѴ>дCѴH>ޚ#Ѵ ވ3ZдӴH!Nށ#Gii'<#FJIJOr`pi$J5"isҴ8Wu.4-ՓҴBy"A{t4!hMHz{ = C*z'Iڻ0Hp ȉ4īd:!GJR&2dԴmߺW(,yn*ѶlQ4Ů_ =!;K wrP!z;NJ Wݗ6T)hէcH:b:5<% 7hHNݻBcץr8]Y4QtťT" sFY|r&b|{xwbak'wxCuaoΕE"+YfGy rP{'/Vl3#ev));.rw heC̫†ѻ6< b%ӺgÖ6wRCqVimPC$ℏ ym385{!6~ə8^s3ɺ̑YW[$M~OLJ˲%{ ,O,u19[-F!9YAMK֬ qI}$]@!>R;" 7##:I 1TMXTM1]r2)IhٲD-:6X %i!iQ46e$me6Uٙ% "aK'/Tݰx>.m=Uݓŋ!up4u$-O@2*+[D@N5@5SlEk`PXDZҚu.+o:<}f?'C>={! 7*7bH[e1y=2/cˑnǑ;=p>`E 5쬲H']GJ=:ݐEME "5tqjN"AfVUtYQsiG !G$fți1[5䳼u~1톌U=sw\H?z9I$!N@ϖ_(*Q*k XYhRIo?1$aBf/D4tXQ'i218'd[3S܎&Όw`|olf0t?c6Z1\uӃb88zvpsq.8ӏ9l~5]^AY7u? ~-ݍ>]B`ȗC0HKaSzw]cW8Яն2v-[tb1Rf|hRoFWEvJ8LZՊ]> ۿ/^q)h:y5M׭?pJe|_]-^~J^?_kZ?^toZ~E~׭͋n>tv~ֻ+M]0J:`sEAvS-췡>Lg۫V'?૽hh|"m:REe$&W_vx|Lނ I"T&p&0toyP}= *{oY-2MZuSW9E&sMo'i)G27n fwL-/hiޟ KElĊ3pʏh /HQ(fuD*&SҶI"uޤbቑl,!uG:rw}GP_H5riN}(MF 9p־YLA% <\vsÎ Z|;o@w Tj|Y mբO/*}Z쌕¿ѬWÆ"ႍm'(|N'u ?WWPApW>nN^}iνh8x(+kPUhOۤf OcZIfƕma:.iTø{KeI@P2Cg.H(Tv麻ދap _ %Q1rM]j8:od۔;yCA)P@ *߾oۅ+ếJ"%/М:chtJuK-q"O+7,\ۂ-δSqW_]i>]lor塥Mmbā|#N<}pS##Z fL0C/iC(Q^Ƨ¶ !rzA!g' xD`S82G??% && R R4ju %w5m\ 'hF4 <>@ Oh+}1%k'NP6a !G$r^v5j! [ϕ~k*EAh'ӡAB|A쎍 g)) ZuЫe?t>4\wi}FRuC28F!n6j0/U* U³i;}N{RtVË&K펽q6@[$]O;N,lAȑܿ9LERT+\\ K޻cz:Ћϗ2^ĀE/DD%𑎗brG;xd + =Dc4¸ clכN3 F pt`zSv4H"W׫*'{!ԯtZ^<Ò4aRf/ #- P1v6A?a=S8 ,*E-uk`މ 1VnjϜe`~[ qY OJhi^Db7X@,IBB_;D@z @ GPQBx<! hkt9*Pw5v7> "*875B赡Ymɇ'A'+'‘ Mpp`$t@ 7PJ)aOo+T\)J $7// /``L#H8}SrB$T;\iPp#Ψ-_Jڶ"˪G DKǧHR^i 7߅@tA!k@t,hN#ԑ4u8пg.'ת0 1t&!0+h7EoIG J=x `;v)C^nD2162qZhΨ /[G1RN@z6ʱ/86 $GQ2;MKW@sx`_VBtʝVre$lSy[\ sC#aʸhHX)0.90n0bQ{f9v"s+(PŌ@^BRa ` a !')BNLApr%y=yCNb(9ف&YtA@w !B& kJ #Đ@ꏩ{yΏg#J0Sv  ϣc0XC}M4ŀbA?@ yѐ'[ -@ >pHar<+IM>pG5hjNvY'+S,|HNz P]C?N׆F9gLE "ELD*Q.!:\Bv[+jw(3]J&ca0*R y#avʠ' NC"A N*";>A;8t11],%,`n@Ɂ n/{ʊWDs܅3 0C1i,ex@?F qI_r4H_z C|0r r  B$4o?wA\ #Cd`:͝ƨ@ vZKF1%CrP` @ADA`4[}l pjfZU󴁊acn,󑮇i' cq h_aI;8tƉÀDp#4:8q;;#^8isx$:Pc.8h,:~d4.!G݊arAoja'ƫk?*N.$c8Aر qA(Wy(Dc†*-f."#RF]bN̝ڊ KT4eaY hZ*6ډڕ69{*Ợ&~oܠpVcvLu v c8# ~`DaaL;)1CNnpv7]?kt!%je2J3@ uy6Td:BspYyΧpأBA D=spG Nn@uЊsh{XT .zOb 7:CA$[βRtrUeW؂G8`Kէ>Fc ew/5U|͂ õۇ5?ҖiCUPq[hRZ^0O,TF2bP-5,vPyA<#:1qLmdF7ށN&,e(Ay# 4{iLb`q0#'h$G8pJ"@W7$A̷ pbTa6#Ab(FRD?S U"kB+ELEY`V'8Ma֤Yk@?1 O-RD0퐃 ٷo\sC`q)E*܁ɚLvHdU.LB1Yl7 /Ȋ3 gx8]'0iؑ^1Np2A~W g,8 |5G-Fp&! xJLEo7 oݐ~ߖզ[/!' ]|?|c1=|m/ք>0 ӿ1OBMh0.f1$~/faU=̺bxcUgFxP5d)[,4!psLW8G!5 c{,"烃 gcB耣9`^9ddJ_~;Y0)ǡXeW[N{pfoW}JOl\bkܛ/lì c7sXŠu?r>)qe6s:]&|-J$LpRر,5Ïܦ5 l>Q+ܯat/,A2ʉbU[^pQ} ţw^.uf8tJX&͔B4GZ{1F_Xo \>k/Z߱+~nraEa}EY7Yo2ή[ 3X8.pW>r3"v78沷WzۮO5Q&>BVͻoQ޻ xӭi#j;3"jAA*e/֌Veωu-u \K[m3a1>ҬbClw^n.Vc:N;- JԻlЉu҈gMm^x7)9MvecZv><7دeϧ`jHu FE:4!0&Bvp9imzP/f_VsI7m6/"Ns dWmɶm Nѝ=p؊(Իa!xP(OWl 2 .NWq񈁣>~cɶMAShE4<%%KfaaI1[N^#1 =23q0)ڏ3{DxT f理9o*ܘ香EuSNebce{ rkRS%)CQ)ьxn,/aɧ&=%VUtYmN! XGMl,D UAR$|ÆU?vlu^E#wzHg7l辙Ha>ޚqO͸6cF|=>ې'"ō:I{`1 ?<k)MPKt5Dw5< TiltSwitch.fzPK;5fritzing-app-0.9.3b/sketches/core/Timer.fzz000066400000000000000000001737611272363601300206750ustar00rootroot00000000000000PK8DF;7Timer.fzr$Ǒ&=qPMI$EiggFCRBejC+쓝2" UQH/sOϷ_a9[_??sw>~X<.?Wsw%+Bᒰ<޿SyVz|zYx꓏OŇwoΖ/ge__ٛo-&*ޜ}7->t1ǧOwgX.݂kKot!n4knqio=_7o~zv˟./~⻇˻ʦk{qŕew^/sK͍R\ bR]E{u}/^=-.sᛳ뿾zxys7g?=퓷Xexh~f~bd?-.׉_ͼ+gxekϾ?b𱚏 Օ?^â?<]^/h.'|WM~ٟw_|y5Boof0Ww>č e~G;#1oU }_|/ x9oV3{.pl$7/Z=~AEqvzi?ӗ_{wӧ/9,p.ҽ9TYCZa0_RAzQnuW!߼_i 6mc^?v8/3Sg{T{ kuӡ_Y<`odrbs5kM3q-k{x?sfm@YۜvUBcSDns\)6;{)?L4 l7pE&;{;WycycJ nX@c*kp\Qyx'CINW%|r=Xk H=i^<I7Xn^<I7.YXؼ1%t=mޘHwqb`+ KZ7nqつp{J@ ]y,k'yx W8N.%tp|oyІ@`mPwp~Cq7bLﲶw FS_6y` Qܖ|WNYß~sw-22[=>n@!A>ɛ0m;!#l;!튇r9>/(ű|ؾ+oj W2/9 4; eLhC1wyIe./l!l`Z\ AVM_мc*!h{Ssmcy΀ӊ}ۋ!^/C?<@VђX+oP*GMv-lUyqǐ"yǢw4i͖. ]x vNvt}%e|6naI_"F^Jm^8x%ewx6nycɎ.. -7z P oewt &y/;@x\@Z<sG/`^v#h.z ,,) ]vh.K - -7z Pnšwg H,)/V{,_ ,@)Y 4c6zާŋSw (ehft,l.; ]vkh.kĒ@4xq{* <@Վ, ^>.^x7`.J٭X@NΎF,ЦwtgG`. 4c=w_f첛@\% ,`Vx`Rv;b=W [T.n]({e;j1~CoZhvsk/?}/.xz@Z<&^ 0@\o7`l`J}F[@"oFZf;,sQUyCxH2];,ZUz g.6d.z`wy^f..od7`[\<No7 KޙoџZT![rM 䅐,o l% تyyy}o}_n|uh/|qx~txx|Z>W~Z\\Ln>??ůw/kGyYus/_/oQmս3K4jš^}Ι#WS[u5p7ZNo~Y>]>~Ttޜ=<^^.qT@{uYs|G]?V"+ּU:+{]Ty#TN~{uxo/^,3vɿyԸɲ4`n^{v\}?!'{~iʅ&w.Ct x}[>z RѬ1d>2O2 g|: _Fra?ÿWg:n* 柚\>~qq?yxzw}O˻^a||aq|T_?Wz~[M_y[~z7\=Vyz;g|x|umGPø|*JTmCWuZeyzL\oQ*ꋋ?=//v}G&~߂v Sz`?0dJ VY~.L܉J `bHk_ `J W&F`Z|kaN2t,'4t _sN*&N1"m{`… 7ۻ" h1k]ܥ_5D FSɐnh~XT492XȃAФ<$dӂ=./tBk9 $p)t d3T` -(>Y嗦 Nsn<8]ҿoϷW_Y>}\ƈFF PٗlV~g?sA_]$O|IMtsu9ceX|:\ n'V@~Ǔ|bDĪ[:IzGܸC j 2@8V?^WhjR!#\5qB% D0 1`4lgQ#p)M=7lR_0 "N~oTبs矮>.?zxpEj{Nb5(FeӗrB aV0Π6 ߭291q ]?x '׾/n!_ Ԝi3B™MN}F\1b;#'(c(\j|Eʘs[9Ѷo~oJ=HޫG@Tx(Fy+̌|gZ6r#|ABkU)5n㹤p3k.P lWrEw7!ӨULxd # F 㝀nVIrj]i,6P yyeɹx1漲܀xwbګ*,81/X >,y96HWޠvJa<9Dl-9HlCF)y*769O8Ν#_sSjKv'9DՍ V)UsH@t署Jt?ܬR G75ꌒ{_pg=w|ƺ̙92K^"|zH]5a9减 sX?zaΞ/Γ~yipjvס s©S_{@Vècv~wخiT8uӚD;_9IZ3{{ûR^=\l;{A;`ӂ{A.wYRwYȦ\ "xS.dʙ8txfⵗhoUpbzIDZ yժe/ddV* x~ @[ޣ$m |M?y^[/Lpvo?2݌ȄIմUd2O,pߓ/ 9 ?]rc=LJq#/i!M062ջk*;3՘K^Ӊ90 YO' b: Ĝ 7 0y?=:f1 [ R'j|`SloN YUENP2iB3PS&J] MmB6xAiFSHhz)`7Jf?XAg2_0@ vn j> ?OFv4dszPݠlF@"@⩶m;m|q#5 'Fr:{?J Z!En (׏pJ< xX1M9$<2n)lӝV9}wļO O_qaMtU' 0`ۇ%ёSkkV"@"Ê8ffSV:@ ÂH?e,O(kP^t.ѵgK"wzEi*(9%TiIQ-gS(mGnC|jc m˜YѮZ:)\P0z âg{&ok( q>cgLV )|:<|0| 'jx?kە[I c<9S?RG->?!ϛ0|>v^"SiXu Wx\u!FF q bT?h,Hw F4֮?KYv>*kM .#0b:Fمb=(ȅ(N\p-IZv(/pMᓈ(Neo쌾QE5=R>CI{ eq =nޜ{AnEU .  {FQ@ F,-wz{\nbw1}SQr 1}fƘ~>QX^~(leA~bFL`L7 glw/A}~A}(; -'m.!Ơ~sǎ*z 8>' .ҁ*c41$ bP?0,u/RPZn EWJvP`/ֳ^Pߦ 귥 Cc>I*[1k1T>\a;v=K҃jcs&*"PMڀSsj9Eϓ;AwwƗJoV& V[Wbp9p H:pqw .Zv(@pap\v ˍ"M/\.!`6+g&Lc[/kƒ4h L{I{\0bWc6Å2Ɩ A&YvwZ.篸vub3 Q "Rw|b_@vc܅!|1jk7%M- c`Pdf ;q\M-޳_3(=m{m0~((lCW>a|=8,_/s9m>^ /s͛[ŐwFQJ$1Nmo!w5*͈g7Kj/ .!AL{\0pec{ϲ`4&JI.f'0ر6vώd,0uw|")b2D(v-Y/ӅMuJ0]P;@՚ P2ƻ gٿf0zx7o,+{AR' Ea8õݙ>;wo% 9܂ݎ(ϟ1֝MxƊ0'adu AqB[0T>źEXnI$V鶶T3v^Ey(:E=% uoPˏ `=.~A u7a{[Uvƪ믙UTeP iuk|BlrY/m;Ⱥ ύ" ~ u[z>!QPZ/'f|XY/.z%lf냳3 ėѳver|p/ZY 7/0흊\K/vAv;sv1Od4;cuƐwϩǴy˗E˭(=C[Řw>aJD1F{aл(JUy\۳-o!>zz|x|nB?WMd=SBO)>4M7_(fz87GIUK}|\(Ǘ)7T?k9~TY"%U_OOpuYö DNv#vJ*gs!1l#kbe-J\ a]m{{bme@W ^# 9] MxZ1laҧEHc>! ֞Dr mܦ;B;R"Qǃʫsrw&liCа_ì<[H={ܳz%pM|dԼк3CI5Ӓ77DWO.abqD;~R%Oip.kRAҶHX;cF?E<@3dS) ^I?[T5?-u'AY~a)k{L(K䖁j¬V1h,d!ތojGfbr2\)~"p/lD2n DcG2'I}J{6OUW6ԓy D qO<) N4 ͭݞ E>Q0f'^0:9!ZD!Dm`"nbQ#Tq`%GN:z3\NΕ3l2%Y#=ᫀ(O O)ONM!81D É.MZmJ Ou6oPZY?nT0z_M_~)وڥS0T& `(]_(8*#(dݱu B0`~'7{Jr]@tDaS G "8\P#hH8tY/}(GfM\a%# n&w6\R?T'(۵ڳ}2Ximm%}29 ym!E!לb"&5?gaIv5Xń1:bEhEL:l`GN:1FXĴ*b\g"VIK"&,bʽsT^ByX`b^3 YF3DѤה12&eL ,cʧ:=.cŲBוюUd˴K;:Zs%Ro8ʹPW\vD~hԽShc.k_>+ ^9Q_X⒭6m캭b˷+PVM["-{WlYbcM^X-$͠*jJYd(m}M(l!r q#ZіXБdM_H՘hgn7tՆ!)H4s憹"R{~ڳ{T{#R$o}"?xgcp:iiN; rlv13c܈?m$@БTS29 4in*m'`1S3ϳepAuDb5l $o-팏H9vvI3Ƣ x6Xibt&;d("Dv93_<],@ؑEdԓ)hںzD;iJL/mE3rflc `_CF[ab bg"G,v&!3Ď1z$cgb]Z$fgLJDuո᢭KIjmNV *0;pq Yr18ٮnSaAtd?BGhό#d'bGV=O_t<<1l3nF,?Ɉsڎ| T/TC>q d9nЅ,6e \PI5] !Dv13cF?AO /|$㟎O?UbT39UXfd<-V'^TG/'|2ku\s8$Bր |vPj'%rkLp#'bG=R1OE|ڴӰICd0wqεQ &%)d' [GNjԲ^b5as|4`"Q%NPC!]qixEqD-rNl̝1tD2v DG2&74M(3WLSfٓesTSe{rۃ\VV@GGs2PJ13X Tpٕtac˰!"G4AO 1|$|>.qLӢ"g{OFm TΜJn-sIRGk*g"Qq-dLg"]ϑ`?u-P*PaChTEsJ㕗Vݣ7=I盛r5OﵤrUVs}~?,>uQ]}]"nqk ƄRݝjMp~gg<P~i qm}<;V ڕ!nB,v]6]`8tͯx˧͂W]WЮ]jUϵ4V8F51A$Ji=sWʷߞ;~ h`.9/(Og&5Z)jUm㒈*ʹo5A[msnWfCeHkP[]=V1쿬N(2> WŶ?삪 KiXՄ-RN*8w&: #VX,БtSM;Eb޹E"\Gly"ST|k",S^7\ˮC.1l{1 m! 0p?&#;UZok8 &BGF8͈؁#3gq&nnh۪dfm>VKi)y+bmI&SVJHNMFWsp|4ǁ&enPû@H'u^{}U#kg H'H:;w葌r3@g.V{\\'#鴓-sr*hZ& qܿE "m $ya78I舴##sgш::23xwNB鴼ӵ! nզ0\]{^t#;%ʶ!*޴G96R咋fZC7qU/Zݦ,iNQC7hlS 9;_HF64INl6a٤۷rO6Z~fִ8)1`Vs|4G! Y behf3E4scG4b!MĎ /z$vZx k@I/ĐjvǿPMӚiBWZQʶ\ \jZbc bfưo8d#/&sSM_҆":ZfL5_լ|?klZkIYVQgcp-b$PaWQcJsh>DаˡF4"rL /x$|'mY89uFtH$'eB#Qj ~.d|>-.6#@Б3X,SdY6ط@?삘(P kJt&`+;"| 21xDc2&z E#H2:jjhV@`E^jw]21zsscǽɈ(Iф%+Jʬ^f۞[P:LTlÓYKo ͖pS'ب Hc&GOBQ;eioY{I6dT>1ʋ+Y)5"QvIlVT?uZɛJsPw|4GRP. m-5r|(ذG4>"de!$ ujѤmjFwE3)Zg|i{ZEܟ;Vqv(@rHjFqP{BAE4j9^~{Jtc*Ad۰!BH4j`QD /"N06(%ˉ%ڵ#bu[q2"v-JAqM23\$c92@?g9 {!EkQ BPDm.uX H7>2xnNL#0Y4O)hK?5F(IEQi: jUA,¦"پQes+T/)}*\Pi(d`Q$K qIj $Y P,yI*&n&Gz {9啲|~hE38_[v')Xd㣙b Pc,iA$LPHXGPZ7F-HQG' #(J#HQc RԓGDU1;rASQgKJu2JA"AI%t}j}}%, mJ(Neuk[̈́Tc7(doqϮˇ/,>/xj^l^as2\{,9Yz )TDhIwoB68kl([d;H1z G{# hv3P$sIႥsMNa8aͣ @6ϙY"R&̥F{+lbvwq|..q>$sJ&rCK uatnD&`GӦ>Ս,p2bh_ZSdrv I-.9!{_<^XL(( 5/.xz<>~}n?<黹wwVsoF-5V {pñ⇇_m5LPn #l=ٗ7g:v1Iʤy.:C;]p;xァU`Y_0zxs nTpaDEPt V,G 5bhI'zɤ>N\3wpk |{p)΀q5FDG28 tTZf׺5~AI\Q..o;*2^O;&r-Sg;EOw]t;>^}Xc8 s3AU'wb77ϋ$Jho#N~rvޜ(I܎J65#xHSĶ P RNIjvv}%@?XӰ p&)wQ}e4' hv3,;p~y1Fvz41 2`AԨ2B$";7ྰe mr;6Σp?IƎ ϕeqX:Χގh$2!n+Y6 Z8>[]zeLhb}j@ֶQG{鶳wW'h;;O`2+$ ~ bi0Q  +O_셨Kc9O,Qt0J)#E⼹|Ni(`P>,5Ϧ< JE ϦYO'AQ_ 70J QUń5A"7+Xu5]#ϋ-LG D`È9;,\:os(m"o5<6 68\Y'pcɬ& Sx]L^Mx &0If 09mӌ}\qV; [+xs S`qC36r9̧V5Ư}}V efM0yw . &X]0-^CN$U[`ZmgIRy lfzz WbY"`,z(;2#^QQb3j+ZL|KO_߅}>G,]H|KC{/_ 4Ω(rA܉Q7ꜰHFy";QǷqfITv}rsL_1)G, %&"p3Hs*:TC*Hpk un< 3(]ϹӤp .,1%:'Ie <q$-v (ө>M6WgoY!ȊH)Y,.![CT6Rgg6l "J~Fb&83:(Ut+O^MAirxMG{Pm^lʫ!H͙( {\񑼖:3)u sRq&#tkX#)E2N f]j)aC/_o.ѮO燏O׋sӘ`¦Kکj@c׹ /[Vw#ҙFkFeb Χ?xY.ܗU1+p07,.n?TGsN8-f̾{W׷O^a겲moDdbN0iG%МǨ;pÇ&M;BkʭAEaG;dڗ&F}liS P(Ҟ8[_{\jf aoʂW6qֶQL瓽b@%XG=1 iXB9a"W^(Hh6Z:yWƦ0bVGRp} eSͤUm/Đqv@I.>Iڸǐ q5[qA0] (WQ7 4~@Nij,n{R7AJKDC݄yk)'mWʐc@i(y\,mg;-3^X!V" rA#!iVܕhjV2T 8ʦ!vX9&=7%̑Z;Rb 'Zc}fW7JM*.Οnynv8<_<gj&txWJ=g}&2m f~́JM;m0#^UQJ Eq#ywhfY8u:a o$6jX訦-SGI:Q3o@{&s77jiEJnZJj,"wL8C7(cq7P'"!ɾ9t,Y"ݓ%JCFã%L]Zn! UKJ AEfm Sc)ƽ>zX.||h)L%”9`!͂@uM009قLl{(wRL쥞;P)[D$Aʶl9iS6Bdtaq&\.(QlMJ.lH+ :l6 gQESlZL)&N7Ɖt l) e"˦HLٚm/Qj[ <)vUS@:/z#O$n"9CA\RdA6; })'KXUH=7E>d 0 }$HVMMqKܥ3到#^F#{jwV!{K^` 2 {,% yVEB%oKj)EB|gF3Zy5-L % gd0d^Vyd*0l*HG^8?Z!/X WL]B.sa3'#"\n9[WbiE/H #1LEvMx51L% gGd@dBfוN3{U H$jF+d 3Dⓥֈ/a~%ˆm]Ĥ~i7Uf.NɱH0 ɺ1!fhL efx. Bj8?\!5YH WN@R+и@Br1Ή.τBZH S t))q)(0L 7tІ(SQBƉv)!lBJPItgXb,ϓӜH0;kEH$ H2/1ܼ C "tĐ35oBbvЩIrR'&mG0_Y"G0 G%^aM)>-D@X0) ;MNfiHWYAXѶM+ Wt!3DY/;1)Æ$lGZ(x(NA61Ql.f1>GL\;u0EyoQ!̘?SHMG},U#N؅!7g1A`Ę!i(Dő0<<)5a-f+$+W3j^V)jJ͎4X\mv$AEN;T{%albS*,17!o 'ۆR0ٶ9' o21y/~3&T.vR {jf`d^V$ {k["0A6; } #MMW1E!Ta#(m8Y[4M)FxTԕ`L(FJC5bȕCհ kh]=,w_Y>}ok?yI`2|¿1O!\VΆ1z7) ʹN^_ډt^֎YM+7N7%vV}L{ TJ׵(fu΢dA`43P=IC# D6tzjvs|#S8X&ҚTɧd+@BpE ԰R2IσĚ`@|Νc+o,)\/ HBH#H #!Ȅp+ )l]n9*Uvd_MRb`ˆIXJ {}LU[1.X]9.u⧆hUNqV0/Ӆ̴̀AX&D䅳H2sO+ /td!Iw& 1FGVFD,d+M&`&-+\U~)/F6_v~ c"+Yax4sAV؋/ H27-\+dTt1,BI!)UH #R&!.1)d|/Sn.1(RPC)GV8Sdcma0W؋/@$H2!)J'H &+RvoX!oKb1e H *$ I )Dp$ŕN71a3}iQm1}+N:$iԛƗC فYa$ n^Y!K9x_ A8IP0"\!)\B6ԌI,5ŦB@pzg>_R#D/@VTSL?”L% cȔpK 3}Vz[$gڄ/d aLBzCr=e,uqԪL`߫vkyOXNݘu9hfZPzoJ (g䉑d[0KI-s Q5{;SZ 7(-" w{nrV#Մ=]-1(mFF3N@̖`PD@A6; w$'&ws5w [nAnR@텺ILLZՅŵ0![H鏹8&d0MlC RH(rMJ%u!a iYvWn n†$la ۊy% NLL84΅#lxgYof̵f&abTXER [$$9Yf6smdۖT[GԄm~AF7= q"z(&@UHRdJ0m,P'H"!7I܄ޏyJf$na[cAGc4V%n8i[ZM594-6(UT1 o Jh]=,w_Y>}['Tue -%G٧k ]Y]bJwVH$htz9%_rȋ:7sktnɊιtXہVqg΅l (ʼ,{qt.Hl/P#[xiH5«H@" Ҹhr4.w4. f~U78ilat&LC?ǁ n (h<.L. & [MD_t<)]ljq"-rPKcU~8_0%8`\LP9˔^ r`\n8k-HԙOMzs)$\I|.{k''W<(JeJJU*{u..! vk(}$a $tD\Xmj0%O Y#ѳ@!Oc MLRtqqZɃj0HJK[*a0i"Zun̄TQWs0=(3fڐ{`dL9m/AL06 n438:MRV #%3) F$:dp2y?`p,28dQ(∤=R?aRq-C- uf~@Ai [ f)^a&> L`yx6h k!] fG̡‚"aWФ@X1ĭWq=.FEdIr!?]dK{\px!!&`"Om<퐙Wl2+bh% #BRd XHL`vAR?b0d UQM53-IEa(R^ԅ!ǭNئ>쏻8~ g1)։Iټ#?PJX"6ILJ֎fhVB\c El%$h]=,w_Y>}YGjk&m8]%T sypP P;Pdp+P0$qK i"?>"`m4p6u4kv;R8"P(`-8yRRIݺFZ;a'Ap]HDIA*.)\RY\4p4# RMOTojL;M6Z 4L$J(I$ZX !,bi3n 2hn84~.myN'-T8qYb]M[Z ),LO0>Jǘ>P2 =ga|S8.hNȂ.&6K 'HꆣRmN H]KyBdF&/\A2IxYA wb??A pr,.g- {f~(A64fS7ȲJi.T:J˥oHi9rLa .)NH:`j=.H*&Wltփ,84.*D֗CR'H" h" UIviu\+}@#J#,do3iu.\6y#썯a k'Yד>l ֟#%*#aiܒ"b SFDL1Ivi]3R],mD?~l[6OmVLj-)Qv^Nfw)np[FNL w%ef~/&Dog[I I(n5:B6f{->L3jR4[jw1;q)Bl8.Uq\T JYo)֞JV1φ-QIrxPd2BVTBvKmU¶JB6MmȹJJ,mYg!1aJ#\!YJQ>B2jIS/!z+ &DoSkUK~93WY~ _~}Շ7o>ۛoz˛?R>$3ur|]'g6vrDas`}q'\'ǹn G;1i>]38#Iܝs!Rr*g*Lp;`DtJ$|cLڌrP&) uT?hYM}ܳt@+RuJ^<`ەj4f/DcV9 aT줊Adv\j&ta XkVtګyӊ<V$1asH,DZ3QlSǾkk fwAn~*Byf*!̵R[ݯWt] el.،vlf"]^@N;$:գi=W³cxV 0۽qC#7;R fNi6&1oDAN!NkRFPI k'XS!b+#JhjZB}`'zf7e6hUߚPF͉d^Acr-N2-:)!n+)#NK ԚnJ l,Tƹ&k(֞PVo6{k9eU'㜼z9-+ [I7UJ h Ԛm>4g-WlY ӤLutcsRY8lfNo'demR6 z zkPFފJn&:$nmjM5a"5nBp&Mte\Fv\%gk#-XQhIJqKtlNR;-<.[KCTB*[pM^I(6vdepLHH;l"r((ӶdVJY{wa3f(u;~u: !f+f [1!tZ͇ft|*[*jBw޺KBtǸDn1mH(kgvjo+lt(('h[RBWRFv p[BV@Kߦl'oYte~wk:AN9am%3@c@n-~V$ KR"kBodP5+`wΰZ{վ{E?} ׇ^}xo~|󻿽kՇ>F9^0m ħh*[V etgJqhC?L+ /|× fk'-IXhDCbђfɤO4Eo7zV*>yPwV$7wgf}}K7|yWdi</g,Tَé3>HoնGqƄ D^[ ɵS*=/?/~yuQWt*4PE[+$ew[@ߎ@O.š. w/#>!!h+wD=\I+zs`鐆'^kmސ8Pfj/\xK#YM+(Je苟5X|Ԟxʹ`hl~v?u^ )5fvhR+oFy͞'8Q/՟t9_h~kv#D{QhS)WޚfQZ¯Bұ[*p~ ¯A?ØNَ٬J2Q]Xc’I;m*mL!~#DN|~Lu<ˉ,x8V$*#w 6pAfJ2(m-1Z5|6FvMs\%uZIZ;uEP\+{ 7R;6Irb[e2(m-M1\E`M^OCUׄyQ'9tYTAAO cFsI%lUG =esZ:#kRQJɮAWO ԚPd2rO6yOy^hm)D!TӧBpVjx%mDmjBB6+p[NC qkV09:)d-YU#*0Drg) \IU7US !Ԛn\*©XX.-(PYevX[l1rLx]!;NZJʾBTW@Nl/)Du0S|dB-kel ecllt ̝!@HPzwuZ*7U.]2 ~%c*}EM^OܦѰUW7 LǯW18ʥ41 Kcxkf2n}}?Q.AY\\QIpB*\\@oB)+$}F%F;W3!+$twA5d|h8/*ESN$MY { )[ca..P&.kFJk>~c\&d%y=΅sA1zʒBoJM^PCnYRTWJ:"j;JY=4{ '8mK 7*!ع낞~㼓\!(pg) \I7tK Ԛn>4W`2eՔՔKDz0ӟr$kO*vdg+rbeJJmV>'mڗi[I7UH Ԛm>4 oL\IuQ)U6nAmF)vBӍc/g[5wA)nm^wSBwJ]RxmV &P-kVJyx?3?i#>J}[MୀPmjB7IVf*ʙxBEylc"֔vhl.DpYR̬uҵ/BotB5 |hV@7[Tn3b#^`.[{JY;4{$rolRIJȾ[BW@Ll/(p'CJIY;Gʡ)z DqCtBEךTJF=JH*wldNR{; kb4ح~ݪz$oԄmjB6vߨbL,L[ym^7{V6L-б-a[I7E ԚxeDe^YgNyƮ)n"jH)g~vqȆo\cr|1yG%۔ⶢXMȭM) &t d"@m2LA֝0uiKJr~EضLکwmقE6 BvqT}gܪ%oԄmjByNr}勒uL].N8+ m%fUFNBwqĕԑ}C\U!+&qSkW)9ĹʻpPWXbt판nNDnmV!$twƚ*}}[QCO|w|DE1橒f7jcSIQ遞J䣴" B ]PACʀq8G)飊5sluWiM5ʂ%ٲlp-uӼ3\DEQ]xPkFtdP|Kʾ񭦏B W@L(6f{A!;9yVRC8:O Bi|}V(6hB0i& j<|`F5F;1zmr\u;'|¸pdW/!~+"+$n'VA7^w/TN42"7A.۹dRL.YRk3IguDg3>ؐ}K#kPY(WRUMr5bbB5 t2U*+}\rRVzg3Y?34{(EmxnĸUv=ԟx%DndmjRBvBn+~aUԴ5} CjiIΚ̽p=YQ;nS uJBUDd߬V!N+T-YPANmnp}8XQgjǰeDj)d\;h)VKLF5NI[DdV\`{P'ɠN %JԔ}[5PmjjB 7@NW9dS&5XGaŰJʵ&d ZQK.Nm*Lw /4- \IM7UQ h Ԛ`n:c`Ttܛ5]X^,u UʥKl m.w°.]t<nw6p؅63NIpZS#%kxcUE6u dgReOd:paC4ulSR(VRN cr;V9!+$tZn$|ǚqu8$sI%]TYIIPҭnKϖ7ɟ+"T_1eII[!5wڭ[BV@C(VPG(6MT6۱@,ׅ_-'cn¶ZO[֚c1Fy^\r"75)u+'J [Ԛn:c W̶Ԫtfh yDSDA1KqIt5'܇iF& ȉY~KK = HנŕNVM  aԚńJ'7Uұ*Svl k}.WE7WGFM=YW"%Dn M}j$m4& Nm\W691_FuG)%Rk-,(Yf+iDkZqIR^v:Ʊ\>uBkh+ʯM^Bq^3uy{.\}߻QuWZ=kY`OV^keZʮR}Zyg`zP^MƭGV洴$ձd|! zMhr=ckQ7,dQNkYUZIE7UM ٦l$oɸ6A`\ .<;mmԥn^i|8ԒGiSIni/HnJ;#DiRjSkBTk`Q-31)byRR*u"`kI#`JͬHymÛ}77__M,R>7> ؘҚ oHnM \QюHUDVW^Aq;FAa賏딃y}Gkx.ͣz,O \,fwY"B6#`šV9,vß>~闧HJOuNM;SʨղBɴƾwQ%w V1!|+ $oSk·)I U>|Jzt8Ź#vkN"U]cnZ&Mra9qlFtd8'oTmjJBv24r~}BI\r 1(SR9+!|ۨ@A·k0O6El+&#coLg5kmxDnҒ,ۇijG VX;3x-ێeⱲ'lkWTJ ʾm5<¶"B6f{!\|2n2^RkV?Lx[#کFM"Ԅ؊#kpmj*B6NWW׆aUףsT[{ &b[f#TGMb’.:IҬT]5 A|hV'd KLQ,s")*v6l)Aܶ`V"YF-ܞ)$lRXJʾBW@Gl%Dq󡹞LqT$%KmR@ZZaNB%kVq9Fsfıԙ۶Щgש]R9GiF6Y|M^B̎`V3Q>C %l\MDz*x45ڹz HC̊eK׾L< ;C Q qY(N c.H:·f.rӫSk +FT2BV:WtG9 5RkKgY%ni{Y 65*!8+):ZԚhm:4nivO7:MFi`eZmI%m l$6&7mYbHR}\!Dm%cV#!x+&oSkWUJ M"7ȡK!rdLbRU۽ f)ў"x+fuw+Q?}7ⶩ5 q|hV=EL!nm@co,~VX /߫ >'kPUJ*ʾB(W@Kl'rPv =֘ =t*(Y:V=sBcqvT9)!&CGuQT5yq~PdIwd%?`M^KCɺֻWXY.,*eR5eJ! x؟ A H+)4S B+ "DhSk"Ь@h2DJv2HAV{fvfgl>!=]I%TIǮ5"k%d߰V!X+ "kSkЬk2Ap-K>:׶靱{5~fıă(pplS D+ hSk~Tc8&+f$|=lJRٝ{4klӄ^IZAVR9Gb5 CU>8{QSmF&(B6fK7l"''btp;PdJBVRE o|BJB6f{5!tNFVAʧhK2(pDcAk:mrl ldKƟ#}zM0Y L+)ƴY !>Z:}e>3>Ɲ)l>>IF֜@VDžwhB܇iFKY !@;N<J nU= Ԛ%d0͈H=.[ݣe>-1}msLX%rgKlt|e>:lS3!p+ #nSkeվ]jݫ,c/>Û}7-xI__M##QNL[od2Ni+0WïO${rouzn?k<16mc;":+Mkf]ϕDN'\O(N{CI|7\CirjR, n6 ̯3:B\Q,x㣝F\> Dx@jygox2QelU=I5vX=Tpt )^26y薽ztDVVU ?>m: Ku6kǚv-KیvU Q6ԖcM9mq][Y[:QZ= J1|'Z?9FqjiVեdRuҚ%ԪzP"\7B c[>Lx[(駋 븐}-j+6U#l[eR?667mCBV@?(VPC"VifWھbZ}r(˓F:Ύ(HZsad*mv.e 7\nJeid(XF[:MBJ&y/zN-Yr*B΂r E;.4_q+7 W3Ke) ŨםZN`K閬@#AAotgуp8`'a:o5'mRCxI7tH UN^P}=[< I[sC]~ Q5iOb5ӪSr36(r"AN:|.qn5nMJ [I7UqH 1Ԛ필m>43dSNI/($"AwK{ BGB)Sl}:gŠ$BBg´P:)!d+)#F[!bZMGƽWblPZpF!]/lzI֖D*ⳤ^";Ehta1~Qǖ&m}mPⶩ5+ q|hVڭ6M:ODrNܖf$ mYkoU;(^XPt!`~}\%m%udV#!h+"mSkWЬm;nUڄ" 2i!HSR9pE֞DN;h0yl}\&m%S[wM^Dwm8A1S܉:JKv I5[ij$69._J6MǴ46Tƙxda pvV[2#٤ƕ]c"5 ܔ"@r@Upce_hs/M5KlҚ8.qBWT@'q0njjBwqz 2Ɖԋ2ɨv"+[f#Taą0:U!+(Ƹj a\%!ZƝ`'s+^dn_aL:]" Ȥ[f#d#6q¸jBwqƕT}c\50M^M'*'*c\2ɃeXDžMNƵ'bla7bQcS',pg) !\I5k_JWM= Ԛ텄zSMn2 +3(/t515$Rg8kN'Cb$ߜ,/qg A\IM-UvQ H ԚPn2W@9UN?R@M#z 3T.ZMurr}\*p%eߥ"JB7f{5Rd\te'2iR'QBI:[K mӞ[U!p;NNJJɾv"5|2@l2 q g-9*OtDIۂ5dTgf uBVRQo¸JB7f{5!; o6:_d>*2F לFvyj`ao}\''o%d8$mmj*B&p^suy-ƭH(:;Ns@&Vʨ)ݶdVڇ44k];3ʍ\: fBVRSvp=b B6f{D|2n2:-XM.'rkM5ۂ5[cfV^T>SⵒJo^+5|2^lU6*x>>lt4\1ۆY=&Y_EX>SⶒjonPB5+ |2n2əD| 9Ejg|vjg55WNM>oH+"5thbg2L%]ؕ)\̈́8m}v=pb-i}\'*Dm%eV;!+ $pSkbd\de'-ZSp!n[fY0(cn3%X:!!V+)"f^Y >Z ]Te>)ޙt4jK:q%[{*)kg0]ib쵭B55硳dlz}][1sfr['xkM']K#xZN܈7#/7~"k ʄ^f@I%|6uFҌBV@Jަl/'oy>-3{fftt醟lO%iS%ok3 ̹g㛩*)ogi [I1\UτM^Qnr>W'a_n).c JdǤCNFԌmɚ$vx)I".H0LlmK׾LCJǾ'BV@E֦l$kpհmF05弋ZF!2lTo~eךd}^$wzt,d$x-c AY U5by+Y/QC! k*^¼eۨPOe9?QI%3&ĖG큞HȞ{@JV ۽pPOcYte"ɓÚEU4U\:`64+n^݀VFlAn^Rvgl\s5қK|H}\)Dp%dukB W@Fai*Vl5RAfu'2uYR.#rk)R{滣\x6'=TAT]u;õ B[pЮ*Bٶ\[PrG<CUuEqnH:j_}bZ~ô5- nYnSܖ}Pj'pĄRnSkd rfs'&B@-@nɚB^@. rϝ}\',s%Ee4+\(}vq$GUƵCcq4q}\(p%dWE= Ԛ텄m zp[ָ9l9 8*c2|2Mu!+,{=5tS  ԚbB 0'VN';u6g< kLYI9%}sp-܇iT#okgA/:;oʢmkSLB:[n:AMl[-c^G‚ݴ4mQrF‚x농+cqmJ ![!5wd%P|hV6SRoԋBA3ZIEM)@uYk(H0T7̼Xg\BEe \%ɩ5ԄpljW(y딺X ]t_y'(טBƈW2wqQnIftT8٨P; W)!l+!+#R[lf@NpoN?똵%l'v? aT.ٌPց{E%M 5:߻QU[IEmB W@J(6f{9* W:wT`Y~S]WnUd%kToksT(68dYaW=WuzBɷBR[%PPm:4j%枪mq4TGv~Q`l7lOdV!!\#\ "kӡkfX nɳ2X ˹DjzH:FX7JR5Y> u~'b⣘?DX8;e)pL i|ԐGiSAni/Gn*"jt!l%lY*DpۡKw܊l ^٩:8uMB6i:{ɚd^lF6,伓KĦ:ťğT*kp|BJB6f{5!t+FF tRNNNB -wWt&zH)vnk{6(́UNvHk_&$o%EdV+!~+ &oSkЬo2R m zJ6sAcoG~SRJeGI)!+)#&~ \1!Z͇f|>w:vi\$[B'.nPŸ{sERV# =SQ tMŕ}\%p%udW1!+ &pSkBЬpv* 5wE]8NRvFhlhfMTbTحҌ|öQ[!jZnG`[%&Xe`ә.  z2T!jH(/6{Ԗ>(ez}ӸE}QB+z"g׉ ]RPz"5 d@rMELX.X]BfyFN=SY:딅:I!z+)'>ML Ԛ '@ogiXJQ.;(e<5.k6J)Xg 瞥/^&pm* !\I57tO Ԛ@n>4+\&&gp4%GˤڎRکwrw%ٱi )O B,WRPv[LY9!+#qSkhK\I['WI:[.HJ((%kL$'[faZQ:ǟ//Dqg Q\IQwFB(W@Ll/(:w?}<|o߽ya&\o?xӷc? |?~Qϟ>eO>7pozKQ̾ďe OQaTT.ǀw.NfX\TTtIL~p):gec[$x\8"0NWm*Z{̫>(SpOeRR;k/zAEv^ɾ3x, qV(VtVTUtleb m- !s{c:DOo?ˇW?%7w.SH/~viSX͔sɟ}Ƿ_WP.Bib~7ß/_@>O?_~_/w?=|ÿ?|/oLJ??o7o|~>~^6\m0ѷłK×~3}ӫ0G~_7/?=pwO_/?姇{h}~x8&o?L>/lfLNZōS__/Suv~KscKI7x`N?H׿q ]Ͽ~-}'pzoj7?7?z?~OUa?d隟Òykzax|ן޼+Rv <҇_똑ƾws?Sʇ><(dp<{݊ :d<Ŏx! ϴp O%3%iXڽfNb!Eқ@,aM 5ϥo&OT`y|/2<RKk5 soku;k/^ 2a ^Z<FUT}"ec();9l!o`EE_(cG?0ހs"%J+WU[ 2=x?G `Tzţp:|'Àz̨=3LOi\| aGL aT&}߇߆ KU9*t|;FS+/G.xaRT09u8j}&>VL\O<' ֵoW&%Ē0&?CҐѐ{ѿ<:i">|ɛ]=DiysB k>υ1O߆a\QFm%aUJ <0I?`rPs Ӡ-k~ *YFz@P`sƀiT5(Q&y Rի=ނ%_kQڤ`28^YGvq%Se.}08mAtpmx*QƋo vŏRs-~b[; i h F ?|a 8s> J˜aE8'LqZ4{(X ,G؍&0?Nx O9Na}UkWopK+EVm]{m$"v%߇s^p4+~z}%G9hg,:^ŕ}J ?Ii?No/ _VN;WN rq.z!ڎ$ / X j~$_%'j`C5E-̏mZp؃ Pp'=c=61pa4)£-ܤMzGv"~LV_GfI"1AAHF!oQInHO.7ax7 ̦An&S,a͍];g,v>k1v'^t&\|-u[\$,W-La [08\?K"1"+)Ɓ%8'tV?Ш 48>XAd|=.M`7 v0hȣG*nV`GN7V`n&s+У[ ^l^ \\ "tENMNX~N{/ƷCtD ]h7r\tؘඁvH 2P8a ' 'LaaK ~G;>&px{u[#UW0?0{G;@5G ?N"Ⴆ H[If,vpY`sUx;5ڽKC"^e`i7'Icv,8s 3p%t$:@+<~= V4aL/ڄt1tM*.N@ f݌xE0gSg#m8a|U#qGl378g@,B06$N$Ց(I«WUߩJ|;&D }OA+8}p+?-dDo5.@ )8oq!,_pU T&+-I7av L0 ,d C12T4o9S,* N2*?xc3hCDoR0`4 _kw,8CW4y۟߆IȢkktt >F*@z>p 1wHs{^ %44ô><*8 >aMt@83|OxIJHXXBZު7N!N=Xzhwdmpg& n qYOʤokbMJc? cq" \#np4> 0m4kׂo?76-l IP|.3MrX$IE*eXWZ @oD xąYi5FWSh1}A GuXa!7@ _0A`9lA!gwvbǡhxڪ>uVmU(}?/b :<0=yGx†i l{Z E pH0y1D w,#P/./<ƮAX=jP69#'tF _§1'=:5nK $CxHIg@8p1>+-+}bȯrDX<>˜¼QȦpA_;l ]ES(b׀&ě|t ݀Sz~\$KJM r R8}/d"|"Fd-a*CEbinSw'A&)UP@K::kۤ&-Ʀ``t:5y_[L0{(OţƆp0Aѵ [>iG<#cӄ z`ç z_6Q t&Hإ$8Kr"a!0 }$lb7zEO`x . >G ;\@jXq0V)'2&0'~b L;/5K@q(1lGmN~v!pӴTÁ@'AKr0D9W@U!0{>3NۣS$~oÄ8t11]Jy ^g ݴɁeBW|ȊUmJV@``c)eP ? "ɝA~K? 88:Ŀ4ѯya ! :1a0 T\N}GeIp~LX9{4"9^[" E2%0.wnP( h\ &A* h!|&bAc|X,@#XoC\J;a PaU0DHf;F&y/:$/@Q3$:V^ ֻ h5N+L? c|&4,q1qt}>*܆gׇRRR0 Tk%."HEbic†%)C(xvË$e `!J`:`:VIگ) o7mx#~1(^0opM2204cp  )`MX|X}ۄY)![dc\iR`NopÀ]ZI]H,twM*_̍@c/}xš xwC 2xOʍB|yט>Ojg3냈xswB= Eua1? /uHQKI_U*Cq 4{.G_ISb"DS"0  "xX-|? pco?M |<ºaaEy4p!QؓSn0VVxK404OM9]"S 04's{>&s]+ı0!,"?H>:oۇLciyxTƢ6UOXQ$9**pG pCFQ6&g`c|MTq04U(Z #w= CP=Cs0 uEeNh` {X'0ph@È(Ha OP]9=MuVLX$[ta88. P!`'ήC_]%|x&M~>El>f:xꏤ}L 3,t;nc%ǻV8pC.D;<ߺ U!yLaa1cҝpq|~Jcǻ4ޭҗձg|8S nIHL!qx/K#ٌ"J#y9RXїZh" (c.x!LMwP~ RK2( lA18w/ >@݄q+CFeQ&2_#Lt '4(Dhĉ%yuO8[":ztoρaԱDۏHӇы G@un,RjؙGW[k*"91+W%wV]I7p{J w;Wϻ6v@66a4)1{Cv}Uzb){)-:~0߫;quG gWr\yrbG5b':1>1cbڙrFfjG&_scrd))b ׽fd(B-# VCtP qTG_63r4rTgq#:1q̤ʌn2L(<:> e?X71-!F屾S1zŒ',ID?"y$3 yFIJ=QG@$(;99 jH5JQw1q,0+ΓnˡjRǬ5EHs ^Ȏp\D72Lb?z5V򯱉,&n Y!Y~`G5?P7X);yis,-a)lw=L>z{+[5a8r/4GriƂ5ii/ +Q1 O ^hV"J,| ݬS_C&k'vJ.orb18k0~H\&, 1dke|[J;nNj y@A,?ۋi,ٔsF=R&'GGvOϡl_;&b W"08 œ7ۦXx'wsBt1^L`(Ԑ87>!ͨCɒ:IKRg1Rg \ .&Mmˎ淘K[I$, 1)iܧ(A )\RoXi$֧X#FNô7B@ðT>N#X:xσ}LrǙ/ib;FeCT+TZY%>GBΰ>Ib ni|`kЧôvpO=|_#I" $ޅ\bxI:^fOɔ_~oDc8}m"2`$껱^= l/p3>pW}:moo&?/߾y>ڎcϯۏ=<]PpE;?~z㻟^_d8 1?`0mIZ$_$ЍjmXɰv,T'e:2X'eNϝZxnyil#GGđ~۝䄷Xw_ 6;驋H[ۂ j#}c0k,|(1s0:BbJ69)YHzdYJ,:cYy؋9Vj|=9r3߰Rf **2ז Ņ4k3n:?a%4ɭ^ȭLj^\*VofK6<:3[ Y7IҝhZOEc|!uZh +nz>$UµĿ̧vlĴ?|h F~珿|zsk;1ƋH f?%*0fb{q_Dxy.&220(-u/^}~"n߄c?)~!ǟ_~WU-{'Rf"E#olm@fDSG!᲎xH G!xm.$Oh1Od76'>J3|@YZة:2yNGDeNZsx^i{Fs>mL!iI?#^"'xͭk29TI{t =yDx'd`ljs[(ܑjnD51!l^J ;C *r_#c/d$6wAFLJ"8Hm;j'jV@7_< )ng&i X-]ݺ^msZI7:lȭ|wz;KTVʬM^MzFS.W0>ΈTE~=ϪW6gԃls 6+f3sf,Of8hljA46 4fLcɡ2MJDND֚2XfFDfɆ`{#2_PA6ڱk*b*Y]Ȭa 2ZM'c \OfVU&3G+jxDNҦ%Gm^só "n\q!J\EJ*Ⱦ) YMxjMKԚe@m>4+ j2c b%wzob 5b!hI#ЉѢؒBx}YOgRcm1ƒ!Q*m!BZ'3 MF$|N>Q?k6{jmA[fǜOnm.)oE?,)nJTSxPB B6P+D\*ucXg}풠3j1Rik6*ıg AAuv?g]X}坱2kl.5$Z [IM6MG; ? tAv׫ju*ZW]n T.ԚBd\ +Əǡ>♶s8wS6n<=8:G;gF4!ף&Dk ]HFk ؼ *"QGX^Gou$Z%!~+ %tvA9!v`-g7Qݰgw}lCPQ]st 6.U$c7miNW%{E0-oaۣd{rr`_{۹k_!xZS@GhV0'k1q Q!Pp.DYEe:4}у>߾NXлsQk6Wʒ5[$%&k Uaԑ}c}k ֶZ͇fb-X)% _\|<Յ6͇!\}wv)O*BжUHיּԩ[A@>j"!1lyBvڗIqԚ2B;熉(֩T-5-H886TGaTqcNfNlm.'kEdN|85)k ׾L?֦#U`MW(9gl_7.!x/P{f*GўMG HnDU=!j\Mh\!!uU9Ѿ3 :bF:edmo}ЎjBYle|s)9_;Z]uj&VaM,[֚Q9 BPbzӊjQAY5  r|ϋN/n;kM]ʭ-ZhiF%KAZe1!jM͙f$es951ɬ!Rv!|;w-%l9hI=h[߈|[f{{Nh Wb  hZ:k1KXŗJSn.#71$dX8"S% 0e[p{pL LlXMڦl(Dm󡹞DmjK~\-P?xZz^9i^8{ wv EhbT`꽚H ;bJѾƶesjRU&LP!7K\RՅc ܈N1Dh3eu^06SHW(7Cn.%:C:qADQEX^E3B9vTڸ}]$ċK ,('DnYLerC-ذ)+;7SMi՚-\f#}amm"7, NI1/) ""DěΏJJ^Pzm5Ԛm24ƪUrIi:1Ft e봔Ƥns^L[eMK֬044>iv{4. LEv WE$k_(DrEdgN&p1ԚZBGOܯn-y<O*\PAk2&py iYa}kmuPͥFhlu +O=@Τtd|I;u܏2r{\qU]In-.h Ԛ`.S]sҙBSJyS̜Kq=lǔQLJ.sḍ6ٸ ^vǬ5kf#.ϵiFk#urs1{@>.HjI)NXOixN{e:i\f;R"s׾LoTr"[Sk.B>D h,<*"X+):NP_y',7&q^#1;>0!*8+Ħ bQE)-2\Hnxqm|RĈۋ!ԚBv VUMgJ4a:5HУ?#S3譩2DoϾ9둃2rk \I}uǂ. 2p55 vۋ ԚzLrҬ;F:UyRΫfKxkg :J~^P#NO_xIIŕ{[ql߳Yڦ%bExfݽO^L~ ՕӜml+SI杵B\llr-~4E p"tė)%mdq:|j pR#UiP~4&aYz1Brs~!˭取KlH.fvvwtnM}PJsz{ơpG3GNc_(&nxU+u\Sq7JMݧY2`!k [)(_G)fvz 1ie$\8![GS)$sDqӸ ߸nH\3ȬcZ=׭gO^c<8eI`Z*~)Y@-4'}qs<3~苼>Z$>W"Q^G3jGm Tm5tk]CtCIMhEJUg 6ʽWڢrһb.˴3b."$Tِ&[_L(Ǚc6&S'.j=)ۓ0iTSN^js77@e %᪓ v X2B DEԙwB3M&g+}w!gkʽ-f@Jw\ ]Ta.9-1L"KYJ㾁iLRJ{U F'zx[[΍n\}VQ,[B_(su@k CVrM}Pm{3l)Nj}FS'0'U뮊\o|%qNsoxnWau"l[x*}t#"󬏬F~:"]݌ ޤUWĭBVI˓!@Yx&X5PR4uauďs'$i/JWZo&|Vuk)MdI-a\>7C{fam@-*bm DriNh< sx˫{+vZ>3U &vHZrX#-i⢉ %IB"6]"&}*2{O]G$ i[Cmm]xղqU<7)<ղ.ёar'\ ѵ%{8QEw#[9/2Q&am,Nc#.`HqIn,{8D-³O`,xLv뗰щ铊׺T!w%:H볃l Uʞ1}-^~PK8D}m Timer.inoXmOH_1ڀcb J R88^TN7ɪƶu7N(,5fgfyvfvΆ& 9o]Ƴ/pW1q8\"sW1Zt 'Wrd%71Qw;[/L>*g=#1Wn s x|vcivA65-So z̼׃>ĉ ktH3!!.<.wCw]Fb6wUy{ۼ|?pI|z\{ĖT9MkU9SrkȻHN-.do^(x&Fn'?d 6F%_M?Ap+G swZoiqX"̓ljIEhT#Yj7cl#qkcRcOk 'ʢTcKg/[ٜjBIYf%oQq!M+0(cR!uªg(g7x%耯,OW6|B/ܙ\YH!-7R2,vβiQΖ Ϟ-a I KӬHiiZSMIe<6 Z9R֔+*5X=?[VyO|֪@–F$Ưua3֎%,oZ&+rrGxS$};CP<2]$f ԽvMoT)C%dݙ2N!4Kis~qr$\<%杣ZQlݭFWDz(81{1&s.&Uwfr_o=ͽo>A΃l.6*ol.eF8Y6<7N*=:O:t8)du@WE|}=G?h9z {kUНg]zTYፈRw7uM=@:. RT*qdH`(bA#{ir c\]3-5P.RZ| ڪ8_Z*.~zd/u߿Ic.ҫΧʌJ<\uW2S꾺;t]O/: q_^uo~y?|߾&ӗW/_^*t8'w_:^ut`4俹^с?}Cլj_e"wo߿q/w54x?O7gzON?2foz렗B\*ʐYO;Θ4%V[J{G@YeǷϮn>}_'cE|UtpCtvݯW^BO\$5~伧0ˊwF/iGO.n~0y̧_:z$yg][yVujΤ65tc[8i2}̩I˜,\VHI˻FDžCuۓ+ X8Yߝcou>N1a:m g u,EF,ouv\3ncO;&i0.SiZ1am]3c= 㔈p~VfD:" )d\vtFmڴܭ wYg]Z%f8ҹBy%ER|QiS %뇱 DjyWcF]4Mا* ˅ k1zF]&Mا%7#yc.ƥg\Pϸ2؆7}^UxK?Gdg^,dO[l FM16S'3L E]!ϻFw0>qvoF'F'/ϻZw0>qP1N]3ϻjq0J>a;X;YR%ۈoY Hjz, ǮCОD@K7 gvVcб[I{kh<)}yj" xz)i.Vxg$ *NB)gmTLbb/DA7m#/%*w\h[GucT82q$?B()؁YJ,gbIü`' PUzr<"`6vh1j-+Z883s}7:+e=<̽C}qG'?,_|'z-dx)av-u=MJ}ӽd(2 ,+&~6s+1:~t'b6Tʥ608绀\~Sz,c7g=4àbV h̳W8ߢfںmvbYR}ma'3~6S6ӷ3ن T4kg+SDyL3Lb/sD!OTfF"3?7p,ӷδ0z e6^qU9TMɝ+bs)gz¹M q L{Δ3hhogu3h1OȉyQg.rʔ=T^+.yL93;- 4+eU ='{^.Y%8aĬafI3w­i$p-qV\lRl:uF)a~iֿw>$_:f, )wIoKlWh27Fi6GEʲ>:>}Vm5,z@-:e3?d]βєe?I Pq $c9%4enR8fk'e T`#iʲif$Hii&~Դsqi+M{ؤ#z>g|&2BH H<(7OpFJO:JX^,,6xzxue&םW>U\&UcY_Ed<fHf IV$p@ȉt5&yXcd6Y>B=,EXu95CNFো"Pqj[|O-zUM{;YHNɻ;::TeE*ap)P 1$!HG'27Xgi..my,Q|38 ù,J9X j.,K"BJ11Z*C<4dEf0pAKqj@@x0iCSKZf't)7yfLCSA=:?5$„cj:{06iw#fA]ٛ=fg<  ۏ /- [:SMef hI%M AL|@,%bPlJdi+3|.ȨSd7P$0);ЅĿ!'ifek*܃K=-3D*T4twuoIuK0DNB~WVz۲6DFqخ_QkҰJbIEvF P0G*wT1׆r[l "HZ* ΰO8ˈi)mMk\Yz '/qA$ 86EL !$֚-hX/`d-K<3Q}=p4-P䴘Ijo%V42M#,S3B ZI1C`yi`dt6jzslzFlp$‘ˮ{Q|O˜j]blG*5!V`l푐!Ē&l/J{Z1]Rʗ0<و"Q(,݊OgGjG{T_#U(It@ZCrE6eF@9g<$ bDעDT[UPClh]=MͧJh:)"k,q4qnd>$kp^Ibs4&ijEZ^3 V};*>H ֥xT*i͑jnTl;<:|5P,W"Me2PL-ꘔ*O%j5R,VR%sY翊֡qaJSreo5äN`b;DT5$G/lC.F)v$d13M|"($jo&Fh d@I)ƃQ]x2:v6CgG꼑~| ΋v$-cztI˘6 ;:ZM$fgb^T/ULgL}ƿ?www&:-Қ< U*-M sx!i&ky,LgMu#)jti{ rϟd4[}Nu2xvj)\.NR2-$UOLL4bjfتi{rϟfd9ZM[:Đb] c2/z4QL4\g@z;;,^ JW._O.vމ" aIAJm%xrϞEvVQ6?m.Ɲ+=,PVm\G7Jlձh`*"oNRI`6#[3O}aJ2-0os1"!Ҿzs|$~0L7oG{ o[J-XHV O&/D,Ӛk9F̀w(}Ӊ/qan2}|>?~vn vt r)\̉ɶb7کMo)\~BN.}pi:&;Zi!`8 ߍ<4G܇ '2-]3gn6(8=|y*=:w/_:v8{t/_ݿىc@㑂=":TR\M6?<Lcgv%_{7ڋz(ь6m@Vכ#IYA'iنIZ! 9X̴JD@X|u?U^U!uCH3`+iRIoY4- 計]scg&J7F"ʟ=mD;K<-lH#<:J'*Jj5Oef]4e)׆"h"lGTЫh zlB1S2a.=~3)DَҲm+h[VGXڿ`%ܚ$[7GTrx\KCY)~OJ\+J`OhJ .ˎ$wltRmѶ%JoMeڊN}kH}`jqxjv %8i2@oBȶ}gmVVh&5c>mnkÓDݚĒ˦nm%5"U{s| ImYmK5Jo+ J4Dj{sm;nܶ.UkQ.$$W$'1!qeGL^o uR-6;M[͑P&VG$Kd%%I؞ON-{=yIj2y5Em׆ &׶dr/. $ I,.JDI,I4lbth\Xh\7ǓDEsb\쫲(es[i<@zyqmGJN.Ye\rT%Q$Qjo'-f*^D-uL`ɬ#~Y|okLrer/ʵ-H ^8QPkL.ŵ$$W$eŵIMD&% "B=vkJ,H@zygqq@#r5zJ2{[$F$4h BG*=:5S-S|m;L)%e*Q}"%kUpIm~kM"VZ OkHR}ڛIUH\O;VF͌X 0YA"K 7\&ղ0@Һ 7ɌOg(.NQFHƉ+5)]{k1DI,(I,ڛI=I)f2 ;q?z,(`~=)8r5(aŹD"D&r-(5%U{s|$D!x -6l Ted,&3\71,z%K9.a΁ѵ1o?Ͷgĸ0l9Rg7*z^xBE+%R8mAW(Fc̬ٸ#q#-P z$:D4 t܄X>'G,D&*DK\N>;,3]dWr&d<2kf/эh>P#mrόm9q<,p$qp$Ѫ9>H*zxZl!Z Xei)*y&8$Q,˻Xɭ P6EĎBO*އ۪s"5 ][k1 I,IڛI=A-︝o]C|k%ǰՎ~/"Ӓv-,͌cin~mn/m28# }l7.5u$Bp Gbˢ9-s1 3zp,b_4Y\ Vۛ#!^0 eSl'@ >n_MHR)IlfT[`jiP[l2z)\P\0r ͸$1$1jo(- N„b[Srb[p^odur/Iozyxy5~HmH*!e$h )|ѰcF5i5q#Ѱ ca-o&a,n^*e5 9k2k6)cZKMь0v-!Ah icDbCTqp3uьR ,Y\e06ɃHZ$NH\7=$P$$M![XZ;IHw+qZǥЩ# 1ϼ~MDӢ*mc!M&ͱ&T(iq$M,kC.$yfOy[[{IE~KYV \_t؜fD=㉻ '.}܍-r܍'በ!"E!5 GmkJmph۲h@(-lLkIF'xVbmO%L])X1ODn7ig䍖QEl* XI.J8IQ(%[ JwkEw$m6imDZ~ o+H|y8?o~?~?o^Udok8~>|q}ӧ&?twz ׳tx3nx?oٟ? cθ0xWoh׽`<u~le4̿'~ _B^'ӇwOEXm?}?sk|5+_LY޻)}Uѝ;ã}gi8w;w_:fӤqk<μmex9r?}tSwѠ _Qga:LYn'ƣIߝLs7<=<~ o1*n;SgO_a,?9' Uou\},^Rh 5P}8z,/"*Jya$rLL ܡ02փ({;?: s?wQipTdzM%8E^xyB Yeɘ|H⍊>.=GpC+ GC~ Y@@aAT FXkp? Ag35+uwK[4Vm~$g?MF>CmflbPípp}BCK0 J c}{'޸)FD{@Y, 01 o*V*8P?@4Bw*>-hy(,:D ;5#z.10a8=,%**G{GP6;_K݈07(o6 Jݒu/-`׉7SfrD"w[֠ߚ4z-ΕuP!YT8wh@h/)!ވݱ:,S) ZyA{7 x@].-= B2cc~_j]QvH>'ъЌMֽEeDfAyIMIB OUX-TU<{z۷:h8.i+Xmb$1@ݱw# + u[t?=s HX"YTW+7. q8A݊~{6t5re/ЋϟeQ%K,r폍RULH\nYAcJhF{(-ma]^oz0b @N@C#x,x\4Qpb#cV8iIW"xdc Xm]bĜ BhCx ,"Q$V"=w7" /p#^Q@b7MTT/5B+_g lMhGK];^G1v@χvoK^kjSv!| 84^x€a1[ pq^\ O¼?)B \iT7`m .xӠ?c-;eb=ʊcM>q?ūw";(G4EDm tk\.]Z T+%WJ| 8abatbPLjP>h`݋1. hBG-m\f5 p Oǃ( HS0rn;1_ xLqCܱw*!햬l +.uۑjt}Vzo0ԯ1-@}Bb‚&*c7 @J,߬ohI Ai4cjl+"0 [9 D*/d`рGCP؄.a÷׸@qY* 9f4P"=8K[l2:;a3U3465 q]ecu84ֹ;u9 |eX䍴)"/z:嗐_`b)~dO[Щ.U针xYDZh!!8"z]ʇQh `V F2`$ A"A Aό*"8v|v qbsx|߃a&` va%.+l)+JoU{i.0؃p&v(͑eb '@QbA$367J@_r4Hc\z1 C~0r +j LB&MшYi?wAB Cf`-t>ܛ;IqY" D"S b AƂA(l$?f67 -áaXhV* 0G@19q'D&BA@'$혙9\!oČ@Z# n?4e9 wA15A+cKk49Ǥ7а&<*^/$ "/Mq#? #p«F"A`C Cfq4M% !rd:^a hH(I1$/$:m]?L(/P. W: јs@|oeeYMF2B> < : wY:1DP K ` 5(~6㋬_x}/z s* {0=65b<"7"vcYX>@4g8kF$":7lX/- dQHb FtEZ P A.euwT<âLBXD%AWZN0 ȃAN@?Q.=JxI1gJ~я^ɏa~ G(4,DcC[^ G1C+]R]׻.SEqM!nHa-3+I-Bz#^5 U\UP- 5, F$T^h䈉N,c-8SA[1L&@@୓  %?8nb͔)Q rF4c I8"D"3Y̗*Fqd90 U,̉9,r';pnX E\).V('O UJ0z&n 1vJ`?A>zii4n]YHbWyu- 4SLfR~roL٢p^ Cy-hnD.Ֆ–yœѯqQ6wP;🊕¶6p`|x>6O`4so::;eLoqflyAUP] KjVO컳 fFuc=c!T[LN1Y^rK.|6evoe>'_|t7ۚoרvl{<@[VjG9FdnY٧6Ut2vϷq_ fl;\t"c/Q>xj61u[ϟ^ѥnzdaQ׫NGjٛm`bf?[h G"%fރAd[ӎj`݃T6Wxeԍa !~0< xbla+s6`p VP8;Lͦ+:nӈ2M#T6+P-iL V"Uۿ(.ް'w=:g ts85ٰcq_ c/>O#.>~['^^h՟gd:'}!݁d4qycf8ֻ:C?yqYQ880YqYӕ%^WwX=냴n2O^^ͧ{s /b,w iY*"\7Jɢ OB0R8?{#OK:l'eqF #ԕI%J+q8UpЩuZ|;5PK8DxEvG|TouchSwitch.fzPK<Gfritzing-app-0.9.3b/sketches/core/TrafficLight.fzz000066400000000000000000000562011272363601300221500ustar00rootroot00000000000000PK8DD@\ITrafficLight.fzrɑ&wfP_zgLneCʤVkW @ dj_aDGfeVe]̈rĮKVGx||?\~Wdv{y7/ſ|Ǜở_Uux%T_/&חᗓ뻋7 oy4|nz/W׳7/&׏7o^>2𖿘|~x S9|br5~w ]I#9w$sG}88x7/~^L_~}bpuàw?x߽vvs9~rB˫=^?Ѭ~'~>>ޅ =~fn}ą/>!껿<u3616ps3^;7V\eo_3=3޸o~ B2p~:y=z^|x~v۟XË ) 9}-ha1ŗ}{_-^9ŻɯO4ћ^arnԌ}bp5ogd\>jҼy}^;W}v^od)a*S;lLy"<6m\6ܙu~ؾiY._Z0[_VE>T)DB{CXt(W߇m=.љ}^ϺKFOŒa/,)89*)Xoު } {x_tb}:?l 嘺\ ?al? D/CB-Z wKz Fpa;,CG' 'r9:0):c;M|_>6Zx#J ONzNp"I^6:wk➸ β:Lw>c\Z8>K-$Kcx0exY|ʥXuKEjWlXTxpÒ} Ē#_Tyx$aR} a ˔K(hǗ7iq/˸|yRi>lް6,e¤t}|v],bxuݒc"Ijȗ#Æ ʂ+W"h^Vذ,>EH)-{ ”}ˏRq=*L"ź^uT{Qqq/8*̸HuGԨ4e6lXgT|޸hMh}cyJ?v F票gnXcTҽaQr|%:Bu~ث64*V(aQi>FE"ʑ7*<.ܰL?5G=UG%T뎊˧籗fG>.<3n RqQ*"} aCHAzk/5IW%ヒqL*Mǽ6ȸW'g'#_TK6Q*MGJituo\Yp‡}}~}xq{9kXoo2{͋=.yC~v9zfE/VԿ s\+i]e`j9G"aJ7}T%8c$o.=[jþa&,D%ߋ`7w.~Xߋ݇ KE(:z=znQޮU U̅PpRUi\&+ekZ>e!k($siNRqaB@FI͛t U w¦xը8 1S)K\B$ĞI!3ddZԖ8Ach$ԏCBCq~:ڬDF=6|pq{w=/>y5}nv;+rw=~^E; R|efFP &[o@PƸF*íOy@.%~- hz=7A\x%W_^e<g 9K2kkO]$(Rbofw׷O]dls|}^q=~AsLWQUw\Ͻ`4W1يg281{Ԁg`֬5/3%.\15(5OHB2+ePn/cim[\/SϞDyՓH}̼Pb>#Qlϔ?N4Ff^-?||Sh5WdmOf̓|Iq_'+/{ޔ?:Q>v 'Tdfq|!e9̨Df8Mlf5P6~eKKrVL3d2i5Ku6D)rqfi~<'u݋Njjz~m]*aj@_H#E1GicYB3!EϭS"$(_@QGLۻr|yk4/~M*^sEy*>i+)Q \SB l)¶ܔ_{gP>{|ϼ}HGTlS?!z)y*Y D lP ߬ߊ OʲYVXGb8ԭΘqR gE ϼr<ɖxTZޥpK'368,G3۞?-.7_:^)XSBm_κvuƌ[SS8>7FOlq .d<ޞDG)ή<6>]s yC`ZO^HmSd@0N\lq Aɕ^ᅲ87њ>>>N{{}pY.xq[2_s/SsNMF5aK!qW Cg1҅+!J]PFSq,dp?dyɜt ) 1/TeT9)Xgp^y<ܡKq@Y@ş2+HνG];%yG#ʙT S0)^ATB0gӸbN.vTJyf~J%>;349[ AFsw!EێbA\^¡52 5TA9Qramw7|oɎsAݓduiN}%#XSȞ\xiqMcy&c'q++RheLcŦ kѻ.*P&;l&q6}| ϽIBH[F0&{1bкG.N]t3RP!H3s/gG+-xX? >7qS\U\x7Ϣ,)gsZUScX^hA(i}e*J83蟿!s/h/]7re/XWrif'w/g >`MssR9 b$1iE)ܿy %r}YI#Z1{Żo^þ%DlCB(br+=_`닠7L{2~ [eu%YkmrV4s$Y)w]pҧnu^ED ހ4l%"VKFq ${|^>\xvbǭ/̌RV=a`j4Nu*$)ق~nM+/,l! j25@Fۼ*nPیTJV\)͎s^(>"XlgXYb?(爉:$IJac1N.Fb |s/ENbOdƟ[13XE#H:к7[:dk.ĴFtGGt/hac4*F "c |&rq>lB#L쁅+Y(\T, c3C Ha*>'> ^Iad4Bic7mV[b+mN-b lUbX(6pVL AQbHFfP((|k=P.٪=QZ(\بX[ݪS*q)f .R<:p {A\t7\45`لH(ajn@%#OQAqyH@_E,PdFB˚] cPBA,f쁅,ԷfOC}\kGc{]: }jg"*Ke;KAWK R@=܅cU]+cH&#ᖍ$$c4"c- =uG@^h掸JgZ6?T$e*%=WKD?G۝n$tzHVg BQc49&l8t:xd"Ŗ;N37ĕ(xI?qQg@"k8ߕdA 4\B1z^y33w7/?n%cbI-s2V2YIҭ # ҹR d,V&ӊȔq{`\!q˳W ێ={;Dn>ɔ:skMLVTq_f aUw$V:rf.l;G!'۰I>>x+kMhZ$1J3K5y( I #DĐh)YQ(ZRKlhkKu&,NьK&u\ջ^)OɴFmR $ah~4$!68CCN'* M*&1~bԩh4c 1N;=0[(ݟ]<'Fƥ}eb01 ES 1aژr hZdp9-+i2SE\8,lZB072 5^վ El'㹧C AdJS[##1ڽť6@UGkh'Ƞ%SUNꗸ``ETpĀXve惘WqEed޵8~feK8x P|<,m#rPdb1½1BŞ,'ӊ6ЉWq{MZFsX,eZ%1!JءZe'OW$SM0s!Mr^tMPx<,mc0BsAKs*!7S[B,.W``E|pĀ`r>{(.¶ qt@嶝 "Gl/GcN^=>޽ĴOzș( 4JXrn#ImCdir#Ws… `Ul\=leǐC ӷ- Pe쁭l !i.b޹y\Ch[a}t1@WAJofBmKFo 1!夛Tio5D o]iomcTQݶ9vVeow^^38al)+;Um^8X0[{akeKO:CE|m/1.,Bm>VXcKNI 'r,ٽp6<)6a5>VZE1wç@nZ^(iYXڿXH`r-kTBwwn| -6ږ xKu['AڲnY^&iQڿHHr-c@B|7wn||-5>$Ω|-ãd|sBM+nf͹-Iд;JhBln0Bl$9Yz6~Apn|,љYYtµS}WReu҂v\[!0ڦY=LAbgkvbg#ɲ),s8h%JzbT+ofFv)1ubK/ V bV 1 J:R 2#҈7@͔">&,ʱy\3:%FHtH.+1 Y9D[cm1sQMDAƺ KZіH&d9Z x0Z>:2 J{!r63!3#M\* Tu?Xm-Q[v 86 Q~DifyO4c*]bHz.L8IR(7GVJAH[BQ o˪_sm+/w+A|+iV^V+&+PHY _OBW5cke+Jrԧ@Jr%FuP-f gfp.!%CMr$DFn]in˪ٝܽ="gVnDʄQ^KAmOInHݞ(:yG@$!7&6LuP-f%pib&-MR0鼯%\8:msbpAeRP[cp}16It<Ѷ&Dߊ1GF6@K anyn# Q4à (ۡ kY[ߤFu),r6ح4 BaZƅ6ۂʔU;>9+Y}Ӿ˲ $'/@pB JF*-fdMe&kAGQSzVpc+idiL$PWRmmI2Ibo}ЮciSA n]ibs˪˼[  /*rJ-idq@$Ԗ$ϭBٜ}*h~;[uhܘhr4.ctB4n,!וfx `uB q1䴹\hB\+B\nY5{r:/KY/c+݈LETW!eZQ[rzQ-E,щ6ΘpF6+^֍ -$kBn !Fוf0's6g2oR+I q#JhՐ#2WHJKdn4T8$xa +Q&']KuO(l{q63KrLrZ-g(:$;1(FQDDx26CɈ8Bmeɞ<>%L!WN n4Wn"і$aLV7*];B}+V7&6Jוfx8!&=09ɥrUҪѕa%d`K]rDrM rIme5dD!7&6KuO-fw'Yfx & \iA@>Ԗh\8}/$Pmom´qXxQ[##rdd MÍ# %դCgo)TF<7b\LTմ(1EDDɾ)&󬄓-lU &{kL.995)RÍ 'D&Fbo]iboƸ"e\Hg+9P" B2wP gzBڿ݀ܘrD.gBln,!6וfx39o=3OjWwH #swh} L;䖙3nE@@H!7&6{FuJ-MM,xֻSjbUjAHF[p럅r7J3<[6=95VZ{AJ]͑+"/j}D.wP"' 1! 11\(Bԭ+HBm@DfVodLedjdUeD*ŭSj9Hir/j} .wPs  1A崉\(`B|+B|neC%39JP'jҮqTPgiէ퓧q'J/R'ԗr$(9m)$!6Poa(۲j@Tf{[ZTM/@r4} e;lDm *iJjhKS p,W7&6K֕fxוfx@!>>fN/&+xU;żCv(+ [%4Ղʝw}(U8y3*ue 1Qeauycu#@ 4#@brgt [ 'L2H]^쫹rDH쓑F*f}iM֭ b֍ /'K2+Fu]iFSuy 6~ _7vlɫwW7}(Zctƥ,`SnU%`&8+"Ey$sاis-X^^ܗ%MF1<7g*dۦND[?^]?L./?^ c,`eӓWǫHlExO>̇cz7Qwwۻ7/78돁+Nµ?z{er:q51xE>_߄3^]ܾMo'o/>ti!bN]9iX 8us>V0ĩpޏ׷w{9}ݼY4G#hW{?m׷z^|x^{统AսF._&g߼^L>]| oŒO.P{LlyqOP;WiiSww|nGnt7<<<DqB)TywIiz.>̚/o&\~[^Nfւ1~ۏoO~ٿן<|z7߾}ǗN._w o?|u~곬ߝy8<~tNyoN'/Bwoc$E7M"VOxk_Ml7/tڹPۇo{ec,'`7Z!oӗ_|%}Rk߆NY:B0ͩ{\`&NO/o?:`ΣA<9K?~~*kgofxm}T0IտScÚJWh= 7?.gWɖc|=ygxma(jwNC`zkXE ujpCdz(ᵐ$r +U~ukoZ/#%P&HY&L6Tƛ.^xECTO_JW< >O66ez}ɣؙs>^ÿ/u8g… A:XԯdKi+˜'`tz lAۨFe>| %L`C.~W:"r@yeg:`k2Jgo[Q< 483e0JYG ϣURoOڟצMf5\\ӟu.Rr}KqpgQ>UP?s#* 0!aƓx^~0@vBg\4( "| O+@9*#O x8rˈ~qk@L#L8 v_k `dl~+䓉:lx+8g gcz/𯎇(Ap+,SUx)iAS>xk\  :߼37ŧ6mɄk2!>m.$UxB;|E.O9) ~FE˫h *Aw6]޺A2e5}FZ l@2S3Zh.>&:1Jdee|nq MMOU uM@!"/<?y 'mW:N\DMVZS8ilgnDp=*#peAt/[7nkr|}Jp#[x=ܨ~k8#6vh'W+C;Vh⎗!!vL]x'1Ћ]G.kʅ$,ګQx&a [pqSQ]!E Zt08$DX4h0 @#6`8u x&`LCE:pOW] Z; M#݊h AZ0$15v]\! "qxpp:օ6!^r1`c ط Cjyln`@5Y0N`,aaK7Q0d7^Gne(G00ǠH5GT;zgp@׉[%Kr]rBVo lc6lW;n@aIdE<ʀi .Ipc`8 ז~BzձP`l<40NDkQ°߷k b2N)LDw.V6XnZ A Hk ƃ X7h"ELQ2"2|1w &a#A)Y—1'=1(@?+0"6 kF~Քi$Ɣ杶E6x6bp%?*l"# MG1+884?jSz|l..2Kx&%9;ke~2F bFB섥=2X`\,&u d{# 4SnaF2i17:s Zhx 笡6|%u*pC !^ř C`n^ЦtnENJt6EN~ xC\At&|lto&lS]y+re!0u&#>z& S@#m_rE]c78( xMAl"@H1 +Pj*ϔ5.&wӋ|S@y1dGlNqvCiѠ`?`DZ"W!5pDUb Kgp0C WfwQ s fkxYSA@bQ~ ꎯ>XN{T =@AR )_x `qX<-;5]WIkw&0MjŒ7~5|#!&ô K*cGD8+F)c"Rl4*2s5ob9אVEy34*\WX8̊KSHSr BB%0Bd-CР=ǠH_> qbsx>T/|`M(9pY0T╕ =΀Ǣ9L/0J,pƧ8P_k.,FAr2QM4 UȤPt嗁 HB6acI#sOASbVHYZ˝kJB1GA(l$J3/' -éaXhQ<‚߆Y#]ONy'L&g)PAA'$Bd혙5\&!!^_/F8%p)/9p.H0@1h{LsI52x&UVqp⢹^[74m"s>a9NPq&8oA(E4y(Ēc%&-V<횓$(1 d:ΒRB8~Dm`6 '?ˆc@ІA5sp 2HaP0YnXUAʇ7wJFB$O%`iEnC"tХÀ] ɫ̂H5(~6_z}/z s& %3[@1x;Y|whrt znaЇ EA:ј;|C7T.]5/S5'=T87 lv,[I ,ʤ)d1S$pH@N.Bӱ7͈ Zd&xqexhCfrLFH>\V:1f/`<UbTêWI0"#*E ܜqpq;ʔ ?O#2s!XD4H WXJG!h^/8e2 "?$CIPPOEuȂNb |H0#1."`BEhLkF'SgSڊK4teh <bP1T—ږy "[ˏpGBiˏҘH~lrc8ӹ%D Jׅ6]k9QYRHaZLf书sDž|q%]EgpأBEJcko5V8‰[;$&8I^묹zqGAꁥFs 98h6NZ6 b.4d%NzQwUjE/I)TF@ $SB nO n˸A{ Ѓ2|+s$If " >/Xj]OTY%A'/'A8QbgȦ4>QN  Μ8D#F)͉ L^~X gP0t-{J WM9SҴx*iL4nl܍X-h:7hU[eQUON2`}='1S[jCz'Ui+K51Ķ"j,5$[똱0L|mԬQmKkbl-Obl2֐%=5-AyKP 2e 5*լP+ՈRo)5n)GT(Y\䈉N,c-%83Z1LKAQ)\pވjonZע8R,RG8pJ")P7A"81_j%`lT& DANYS8[ C\).V8M. N 1Tq@xt0;ѧIpiE".\5uk,sgݱIR[%s+mVܜn+p~CثZa/]pʦl^>R gl!Mwaھ3DS‘7%L 2&N)(|{'o4cgb>5Ll%PC~nJk,jZYBg3xI,ZU2pՒZ;mٷXWٷ F$,(,γӸNQA1S8niĤ֧\fN0P0*#0!6Sѵ`I'" pE Sx\bِr*,X.!g8?Ib {Zm7l ^ c:,;am畦c&i> 6I`]aA-P/x1媫Ybxc6Fxz0zM6-i .MH-c2Q*޴)Sc7,t1 9llusv5Rxw}~立ǟf?_nnzA۱ p:l: |}*Qλw׷7t$Yƫ~C%Lp-?iˊ1n =笍%1HBm7M]/<ޖKKb=M*j;Rl._އ?-/O^/+b:kZ+ nX[ ]3SJk%ۻۜ _AB݊.C{]2eemh\~~\ʮRgo j!Vnb .vnwJF3i V3]q'ƴAl`+_+9$^lAl><=Ә}8wȊ j!Э{մs!\)x<_RȃӸԽgΨKmO &o%|dM.k@ISVTN Х`ȳ?n "qn.@] ۋ#i>Hl(w7n$,/;leb)49.MѶXms9ikZ y6[[@>*0w+l؂DCnqFMoguJQh\\ -N=L. Vҽ$㐹#>7P9h!<ልtN]^rUv9lj$DҌ&:639HDlK+ Z|삒D+"Kۋ#X s9`22h |dm`Ʌp7j@byB1X#V4N""eۀ#4wryY̴ͪLnĩO}u%g+"ъPy&RKT6X!68PmT@9Y) "odAaQ A4#`:@tffeidV$&nހIO@d}{P bo Yfo2/Y*'KD$^9mVð'Qq\W\Lp|vsT6MR= ѸU$%Ѹ,8W *DFEp"T.Μ2gs8"aF #DҌ%D:^@lf L@ʞ1ښOY+(ʛ)(K+7Ѹoiϋ-DGrʩR9f#*gL$\O%R(re q4# qΆs9&.5wa+xNaR`m>E|^7G#"R+:/Ph`QWSDJ ,"UlɼlubpZ:\I݈*ih9 33C ڎ &'׶FbgCu =Leڤ{r3 )M"!'Mq(qU 3-2beǠmw3H9lAA+Z-J1l.6 9PK8DD@\ITrafficLight.fzPK=.\fritzing-app-0.9.3b/sketches/core/VirtualColorMixer.fzz000066400000000000000000000435651272363601300232450ustar00rootroot00000000000000PK'8D&KtF9VirtualColorMixer.fz}r#Ǒw#@H;*lyK!V$@ gF'9t2DBj@_/3?|=ft+Wd8O>|o?q`^^=܎z׳7ۻ L͘k7ɇWʞj>-]}0L3>ߌGWzv_z{?=f2_\NW}_-n~2cRûWW2S꾺Hx"7sw?]^ܗz/~M&Woׯ~w ^>W6Zɫf{S)~vjlzvsFftw|绦YdO&}ht?}p.xXL݃w#wg߱ř^g?ߌNFۿY urU/ sڽ6f${RvM9RVW폓''$NK/t?Ӊrrwlt/Kc̮U$o'ѯ.'=e{hVˣ.na4-f{y<(g N.3%g Bd\T;%AS"Ro@r5V=WOb.r?ѫ捞EalijJնXg#>JKd_}Oɟ $sHvRhVJFA:?=u.tZΩ۹:j jm+Ţom"qm+۸Ḕ۸ٸ:1 =1aA&rncyOHE߬6+(nxOnx->vnxO;tnsUbn{*Y:)鶉e4)鶍fbF_AamM/Q[`xE3+._as?NG3DZ-g1jkϢxǪ-g$Jegs_VvyvYd}g1iכEfާ]lyvY?B(5mRfxY}fihާ]\yveY}R]*P'^SO,Fw9ROsrdZEf'-Ud לl/ +zq)8u~UdۣՖ:B籨+4Ӯ%O,N?zL+4Ӯ)R'^Uλ [b,O,:?8Mˢ3Ӯ0; TcO+UfQ̢iWũn;*[jbsTN,J ?zLWNt-f֝XܿW qfA^3lҩ˰Ʉޏ~6V8kxOu5w( jM랢͔`ڐgL•bulճֻ ϲQ`٫[nF%}7-} XVwANSN ~lTY=jиJ?hIiF/rK-S\%oU HjH:a$K4ܹ O.![3h5)pU '"Qʷ;v-5oGYSxѱYpmADCD,>6)2j̹<>^=}-nUV^)Cn(]~PDz#WB>9Z;N`b;7$G#lC6/iv%l$U$aYUcHFVXma@e_|xd/f;<.dCN>92X7쩺RB+1£W)2v%$nwJf*7ȶ2$3BYkIP+4ǰ\5Yi;A܋@BRa7mf,ɑҚ #u/ (CHesPс$1&AlSYd/ J.R},-HCԐ2I8"@suA6"*Oa1:V Bխy҆T5&KҮ YgbhĚmP=&i!I;n:e6hERJ4v-l_6- ͈ ɬN_kJ'eρZr-J"mGG]kQΛÆ4(RjJï ۦs_")VH[U5L0yPM^ϷQ5)v7Mm`Lj iJOK<["ZNFF4G )־i'B =K-4 ZhJ~&qJO\v~,ɓCqm%iK4-eKI:!.LjΘ`ۨ)u/ܩ)IDL#ƮFn)WUi`)kjavҔ=ISǜQBTDO,ҴtKr$2ztII 9ۜz1{ *i^@JIl;jDz'-H=Hi)o{b X̬I0d)"h7' | N 4fkbΖ؅=Ȭ(Q5q!)-l5H•?&b5$bW8I̮R}f,_H-v=>Zj-Ecӵ0I9U2F$%xtJIjbFP .a29$LрrDJ72J*!mM*D]DŽA?e&,8 ^0bjQπiQ"O9[Y5z?ͅuF+J chfmj5^ͱHq^]Yg3Lb4!k|ՠ;72чLى֝2J;d4U)SZCaΔG!0[[Tkb3j3@Dx4i.lZƖ,%]yc$VEi@VST=aD?`8).w-βAlIY*KT"ʖgJ2H~YnM0~F%"e1}4GHgB]lH<:tan(vS>UyQjhNˈ_Qf.3XQW=xaJ9ea1?O6r Z}Zo_˷?m刺}7arpqw۟_EF..3NY,__\|1ȳwo7[A.xȮWx?,z?/ ̏^&wp79\-FEo/Ϸ}~b8o߻wx1^gSx?܃k'a_|;z̿ǿ^ďkAHtvwy6W}w90 S'U]/*_@ ft`vu[|t'-Cox9Gb|a4]Ϧwͨﳫd[La1j>/o]'dO'Ylju{9~xhԻz{H=aplt}p'O>Wpf+O bxӛ=L&tRyruy1.{Ni_Wz%6W6^&??8 urn<[<\޾hVN;^ץ>1݋.xYG+.h<л\Nϔδ#~/>@̈́q^ ;!r S?f,Z~X&-oUDAP7G}2ՙ+L*"E4w5ۭ/|Mi}Ϯh"L(! ]$3eKFS/B2I- Ay[uH85, x4ԍ?RD4"_r-[X(K}'&LkMgUi(ĭ$+iĩ*yFjL2`)kΡ*m *13ъж9LY`dƂMFV)zʴ4(QYfmNīf\6^M,]D^5a26ⱁ2<`3rĆc/&/|oĆk9RΒ gaĆIbK/|\ nΛ q%2_ XBD^5 |-Osն8ZpjEzmӮX8 Y4Ҫ16qD=o$$4LJV XmـeeSdV噢|TiCmk9hͬ؞PLLs2cDߚo-%5%U9>$JmoB\2pgDTNzе '׾L,8}3Ndboz $$5{k)I(I*$UN>FI@mQG2BJY%K.>l,8_eɦț^'k֊ QÉʄg>rbo#5 ! oqk!I$I*ѤC"m %a:GW'ֶԈ@EArk+~ƬMC5r8O}ނ"5 gKFekGeJs|,I(o 8`$=~x&?̡D"w5(}k\hh|ElZ$f II86ڪPRj@DMd2"*[Nw1/}ś<s{&TwIkEΛ$$Vx۪j@d-sI*~8yKwJ.>l=T<J X"2BXHnMysV]@ުQ{[U[[sUeVgl-Bl;T؛/Y\7# -'.%$.(̷V@4&iA8nÅeC<07Njdƭa1ooX)j[\/,1|z0/|n&6n(dXB)@ˁrP&LfF[ s' @ֳ8 -6a%R5΃"܂V/|n7^+h{;}e!3P/dZ3x`M!3U#etQ;*26;_% !dqİE$$'Oq[ nefP\!hfׄ̌%jj[PLͤIs(i{X3b 1Q4Z 7mΛr%v;[+4LJDWq:,[cOL1ߟe%Je2# Gm[,}417⦉VE\rYbcʶDܪOq[Umz1N3g2er44)k9D F^&l碉"*w4ȈQ$q&\iII*$-SIC)[ԙZe2cmblktF\Zi5uNрdS>LUu%$$-akI|I|*$-=_-P$DF'%U#_]WI[ ƕhg'fUF T{溜Wn`l0 ` YyZ\FH'` )tYO*Hc%$q $- ' K2w T;d4u0Il1؁I tFmL^SlmOڦ-Q$dfLmݹ/68EYcp[ayk9I Iٷ4ǒ}l$iFI`(Dr%ە(\T}3N#$rsc]W&)ٳaE[<=fަ)7NҔBٮM٢[[8[U$M+;wSmOŎ+!`%:VZ82i]*͊NNJ-rh-p+B%qXE)iaʆom%5!ieq$%V3 n-Qʚ*'%2)YQk .RV#AddnyؼidIm %N6 'gqLmkER­*$qUZ{rd98|=fgʄ߳I[|@ j9kv1RYv6KiJS<'7ST̒b7/63/.'׳C{|3/_@L~6uxܛ\ލ~'_/oG2:g/īCtC<1).1Tz֍ |~-FtaP{SviO㻇l4E;RB~w]cԬ7F\L+ؚDwێQ$K>܆{|of$!^k$s2ŭkDx]R>bsz|XgNo۝|!Pk2cp!S~ox{4ZbČ]iPktq[~}tx8p'^EffW/ zߏ&٥׽~}/UHݧ }0qwj{Da|{# q̿?/Wfb=hp2ɤ$)S&0~torf۸5:TqkoLH7+v׾us b~z{t|y!\ұfӇI~3^_-9ˌf {^og$+/j|]os%^"֟Ɉ}]d>PضO2!;̀gF[ mRtڎKpGXw!'}ͩ?E凮+J3$g#_NrZXLC\My]- NXNB¸;fA/*5\ xl~JֲJ_I Eܚa7": mNQ';2kS_Re*e$ 'Q&Gx{ӗ>ܫo" }ZV<wYƌp Hw?̸l_،; +[/0R~R30¡ И` R{5 _ދY~ㄡDQ0ኛ~E߰\0;}C(PA` *ߡ+_WVQk0A\S:g+8矕U.mIK~ f=9+[\yhS&B%cƽ|Sa)FDx@&IŸiC:(QN%O !<ѵ7rП3G( @8N pdC~K@L&=Lv?K ` Ѩ| H\ƵpUB݈0g?6JbINn ޸CHnO[ 6j)C<48Rc+ :,J&DŃ8qh@hϔ߉ݱ +G7 ^ƛ04>4 "XfLq֣+j}ɘ_fL+f|3Y׊D ̂ӥΓ[0?5a4a= 4J7/Co"`ص^0l\ݱFܘG@ +y˿=} HߜX"YT7+\>q}z6t=rUvgb`E/DD%Fx*&oHahK(-Їav3mA/>e@! N",:?8h ^0)³pG?G[.` uiKDf) onx Owُ5܀5pS11g8C>Wvz?ckgk\;Zr>s8:z޵_"V !?{31*W,0`X VG_AW<œ0?]MQp.D?a6HfZi ފ^/@6c|=ʊcMUฟ]? lhJA٣%F.=|if(Х `ȇf(P5C, 4X@`x_BtM'4NNMN~V/5܎uC#/ۂ%R,\b 0kP% 5R+F(>s ( p(42P=l,ɐo C'5Gql% >Q3[ p8#$u0ꕎN!A{% uhH׈*TPFSamWcs㽠#C{S/W6Z9FAB$`0ʉpv| 8 P ]~ (Чo+4\)J $7/ -``L#HV  IxB8tXlp; cޡ}X~tD1r1-"DKZU([K q DCj ѕUe"v|Al {~x $8$3:JI 7"aR "2`1v  ' a_d `YG@qDLaX18+p7\#2k?ՀâA$E:`|G.QEЇC( QyAC p €fcxx0d"&U!9H]%{ǸS-s|DVC섄=2X`\$&8uK {# ;! /I"vppZh8_P: `J8C0C<糾 ]`,kS:$.t:͇$r8<XUr_+Y ۖ,g꒜u0xc$Lm rEx! tg0l(v ސ(G ]0b Ί0<ؖD97frœ(@Eߵ% ^C ia{@wHK*d9B 9a -u/}p~;;-HzCT!]шy!4]@HkB!GF0vÖ6͝ƨ@ ~ZK)F1g΂A(h$J폑}aifZU\@PX|x9֝+T 0N $`9\!莇8ؐj 5Bã,v.HЇq͢ An%ȠY1L1M24q>qbë04(KI1¸KS,P1rƷE'p›F"1aC Cfy4M% @GtL*Ix$ں<#~Q^` XC6&ChM9` tN`mxa6rA̺޷& FB$)`iFn]"tpHvK{Q# ,E1h_FB S{qš 4 P2 ;  =Ysqp'.1Y/ \ Ę1.K 2X|aD\uy9 Qp ^-K/B 2!c QL)8p$yJ ^] 'frlmc@-~0 |jПA9%" ?x704qs AKD`A?$@\'E2FaVb/rw2(8)ʇ*>G14IDT0KDaCFߑ6&geNy4 O g!ha /asvX AzAnbt$C\7,@ @ߡ" FDEDbcX(~ mݕAnި`ƱAi>,G1.1&`]HItq\?t(JA!a 23ev5X\8\AW)8Zq h)V8zaG 7 gg(q#Ȍ 5'҈bl/4BjK>R &a2"/A#HL5N\+"GȌ"0f A` >  ^!n¡ y=Ԡ ]@f gB=AAaOp,RnDa10XCK4h豍#nRQ? :-W#v|aFRBª~pHpgPЕ4-)t66X/ŝX1- / dR߁ޖUUzw =x,p8c|#EֱB w"~D!KU%AiIP[*}I uU5JK*T*%%"( 1Љc̥g*X 30(0{xt23C ֍X6⥙2%Qnob`|1%Go]ݐd2/xlGbaN`~>8#aZ ]. X).f(<.7h Ug(,F`<0ֈlV"XP  77\u1N#$&)=L0Y: K&+?U-WQ9y-:4 ?X:?ci aAaұ#'V>a82 i,XSfQfh4D1"kcܨP+z g7 fOT/!U'fr.C>/Tz,pDD.b-s` KB0n OpM>>T/r_,qi_4(K}q;EQE'NM PðRŒ~0,9|fW^L`&/A K1!'%} oP/oC0;/0`"ŃqA9!|Fc5v=z?`y[;wDf<'fkGyP\`Q:JQ/K@gVBg .5.V |iʾlɾ٪ξ3"/LĤ4DxNWuϩ]m;ٌei]BU.R=XJ5 Wqp fX0K"˺f n+<]UxVzq3聊w*˽ޮL튮+\>iݞanVCd.5?"kDn|;7euĬ#u3C|΂maˢc7[gY^KbT>-$ D6+uTJjڬl~c(]وsW'*ԕ*w)uR0C gf7ZO9fnbȮfvdgL1v^O@ƴak if/їo^v}9unfؕ/޽4mM>1r+'_^Oq_򏷗N+̬g>-fv|yv^w+#N/g({~%7)rU^]8ֽL44^$TP2K4fCEHh*WUe?ΤH'8'!bGE"gK%haEd)>d7]4ޏƟ@ɒb&q6&xyZ,OqE3NJ'amMDTѸDݢ,Qu(t6lĵ -XkTΕDZ G29ίu>`ɭ`\DL?vGH[H[d\œ{[okDO;:$$|x} mEChHhUi$Y&ŸR5wPK'8D&KtF9VirtualColorMixer.fzPKBGfritzing-app-0.9.3b/sketches/core/VoltageRegulator_7800series.fzz000066400000000000000000000237211272363601300247620ustar00rootroot00000000000000PKu5D=3'VoltageRegulator_7800series.fz]mȑ~F«R̋/<=sko۳}PKX$K\*@!""f|5x*̬753l17m#LDl-ޟ͐.&8؝0l2-ѽv񆋑;3n??'w$[LlKͼ(a>\wo|r7o۹3gcoGT |e>|6Y\/\XCGOˇj9_l7WɆ=c1kw8,36ct7;w| 2@f4ulvS7RsÑ[ ~<pz:p7оOWS lb|*lϩy?.W@L^mܾc 1-p3VC%7Ca-W/lq:n{3u F˵;i83tm ;ӻ y3N7K|vosAƧ&O\r$3)MǘI`?#-·^vt! G'c᱔ᅽiytSǚNƈM#cmv{{SXT0 `Qb>H`ඩ3H` y$hZkCjt[Cr (HVhI`|A&'t|@1$&$oejIDH$$A Z 0% %q z,-ɘ0.- ɐ3 'HK? 0-Ș0- CkIꖄ}W?ZQJ(ГӒh=,-$CeK<_{b<`dqI_`۸4%#W@-a$CQ) Vڒ`5_%8]ے\bhIG>}h#- elIGĆ{iIGaDZ iIGĆiI.J_KB@tg$DU%a %U}:=- Ӓ` ԞlI@u8/$92^rx?"gAŜWY>Gf/}#/KfcuoV]{Oai,Z2?~%nqAp52yyP-#f-LnCoitr_̽~n| ʽ$4h9-&o_>xw}x_|Gku?`lL_W` _7G 2|KVGHr=f TCy@ތ4/:S}.6o_a>1VW&-nX} l<8 ?V4mj"[F B)ԧ/?6+BjHJ_w%c$fCyb̶woFk]$-`g߅}no w,N?߂ƃo :ӾLOMe6gSyJ#X?W=E|N-O"r3L=13HHe+,OYb#Bf#%;C/P#6Af+#ηGT~|*mt$6u'Q(x~S>;V>8 >䁔f8ࠒ?u 3:< X9&"_BnNMD-tDؾV,A0[ 2j(D0': Mq{|I0E\;D #_6?߂)<|vCq yUHƖv( #X̰%b9@ vHQ6_I_mO( l)e9k"?1p[Gz$X$vqS8 ,'q7ܯ^ 7ͷ˿^}U48w}>^:^Lر\]l>x!\uda]ƍ Sw}?\,{WΊ#5w}T KW/ñ+5"mK:"{dUXjY.ٴU~bٻ}zꨅ@ʯdpcNypS>5n\# )P|PTa$dV ~T߽¸e9$5ɍeJu@ {<̇}A{O8Fmnz2fuOMZpĜvDjE;/3w'%{)ƿ6sׅ&:8>diQ#u˻M-!*pbr}3}ʷ%A8.qyukuP@;b.?2X.qjX@',GAݽ\pVD$-\5="{t6х9z}.;)԰ޒN+Zrf,Pjdu.BPM{Bk% )A['F{th̉~j ~"5^Wܙ t?洹jN;,u:̄o\f?>h& ]5vs:٨ /W%,~u,;6BW/?lF' 9ŒUVB#{B|,'486 O?PȶlBEt4mn=dC  FN$0-D57TVT`E-æ JTP{2}vRi?Dg=4dZ]?Vgiz5϶Vy^ *&Ujڪz*)]q uN c3'(Fbq:^LVΪ׫*ꚥ쪕zݪ/PS@֮:sD`wDMLokH.v; Hn!p 7g,bmsAd G^i@*O0XZru|ye'Q/yJвԻBN?R hA6rBh'(->EQb_ވ_A]؆wZrB!|hO+l cy j4]BEt3aL}Uz^IJ^#($N:K2;dc[An):q1|tj0I[k@,ۉL9ȦMΈ'b뉉s|N O?^h +szwgX䁗.Q|Bzݝb t>LMӉl*!dʺV-e:H z˶A]ÀO8,ūV)8 )"eȡBQOj xofkj ^ [3'RëjnyjYZ'wg;ܽf*g1ovijP=wl8棻\`Ccέ3 V w #-&6&ayG0*LКB^ʷ G7@d;ILqml sSd,bljy8ۚRo i\5ϔ(V4Mb w:{~&f?\@ .Q( {"UUޖ$$'a=r7aq[((,jm5>[<~'=! a ~ B9r0O__\.64ܞب>|ƸJ+GR; +Xf*yOw*dQ`hg§Ċ#r,"tI2fåN3"oc*> LYcCaN¾m`mDSEˈ CBC10lD[ eAGr̅m %)pKE *ɏiP<,p&hXc, v, Oʠe)p&٤ M4Mb&3gc!?$ia, ʶtl@CO &>ְIIcdɳÎMf4m)t iMlgll{|&36Q'kf6)I!FEd[{PMfa a.Yݿ#MX.L]O^Ur$RTX&Uf4m+v jMҴJ  AI:VFhQ#^ZiRVddv:LAш0X KHe2;ˇyh' 4=wʇL|/eIv||#['3;tqi>ID:h8DIbC &em#"G}RIBWP.dtBn&=s?`tƯ+ ~W _zFoyW#zh ? >}O9Ã<4ƽNO[rD^WMը-z~ _6o+g兇l2^1ۼZ,ד٢-W{wbΗ39kC %:ޯ~ޯ6F54k57.φ{UGAq웵;yыj_{^ ]wXVۈ ރ*z C-mm-nud\O]y+j3]>ƭk =c7a}6_!~v8䧠}5vqS(B*Xq6V_3"8y*J]Q($ Y bUL WRX, Hgʅx艾䌦$EbKdf0m9DwM*OR e %b+ HN0#Mľ;Ԍټ2&^t6-E$)v\<2x**J K:WCYk Y,Z~obSD~=!)SnU9(ϙ,0 $~I`.@sRdRC[lj`Y1Y;ڴ5ŏKs gf6m)!kMA6dgRp^11j{Da/fKEZxψ=;B4 4 |P *$qXwR 09 b{L3dlj=)C6 (۪Lڠ>BNpb4m s4 _Fșq3r6`s3Y]qb i: :uzōA-ꊙ)`03thrlyJD1;?sG̲[p|WϽ:Gl ⨳0&wud0i"UCZ$`Sda'za;>^m>*T#Ms-X5R_5뤾]Y:#)h3<;bGy3D@K%,$M˲0# )QАB&g3`VL(zh.mR#%GQP'#A 'T)c!k<8|BVb$qAt *mA#R/Qʘ1%=V(jb S{(uqLIϣ%QgKsIu~{.xrcs3UWNw@aK[آ.s0<"X.L[5Tvtz'V,׮ٙ9dp۲UĮ, ܪEcYMG (~Gϕ|)#MunfJ`BKͼA鞶&J'Y|'k1>滷iJЭ}R:TbMLC"'a#iYMIuHZD@W –Q1lU P \ 8#<StfuZE;T*QL CjdhjUI7Ŏ&(#gYMnjX$m 9KPDQ񲌦eC8>62 ry^fR0K:C)u3J(YF!@C'i(5o᳠8͎hQ}Kbmo8f1FvcdYM3"Qg#+h6++'#eM˒2P %H A% IzDcl[-hZ}_ TZwY2eī)&+Ît_uGsP03+.锬' HAa0) (nt6n)C` DGc>/-{R \j"ĦkO0qּ3IoZn{L\iIG`G܏*icObGD-jm2 ߇#|XGfKf$BufM @|iYjT̎j7=ՅX͑@Q) :=N" }Ign}:@Eu*NpV:մrM.Cx- jdԢnFa&Gvr,w:>.[Fp&Rħh E ^3⊋UQRA+'tVHEx.bg#+j7.>ꖑb@H}$$׏˟d> ջmFbލe9FVmX ݷa2?C'/xiԞ| i؀Sw6zO΢DS ?H恺dA* f9RsD(`D^,gak>'K$'do$1Ul7?SdH3=C ď(kOi3<>ء t q1^Z崍M"Jc秲s}C'A$@@>b.TZI t'0Eg;u0m#鮐F~nUTc[2rW7J5T3:մYPy Zo:Va6u&ӔL;)%Wz#~ H0HGp zѬըgoZDLG]iMz/gt+~ pIjl(:-l e|/:-|mc^Ǜj@ m*F3 W7:ڕT 6phWA"Ԩvռg'Ǒe:FlZ XW5bjuba8aFD9m]Ǜa0eB]ѱLhF_pI- :Y{@,d@>bſ:ƕٴd\au1WYl6'qüc\Ǜ 1 9ebӮs ^R(WVr0C'ANQ'b5. J#f[jNEc^MKW,= \YǾb_օ88HjQ"f^T 30(Ѵ㫠KjjZTI'1(Q'j&b@2qBMRi0 LjDut,iy:v){I|ϗ)q3c%DS:RviYRFtaRB.OJ/t,iyj>t H85\{ lg?ZDm&"q:Nٴ̭:INv! lNvrQG;޴ &~#d v\ѱ8`N>:YX2 $=ȷI3Ŷrƾ}(Q2ZQg%Hw(6TX;˺|5+%H(=#g^R!ZV goq]ɶ_-@["Zq ;l!3FEL6rH0(B]):QY*6f ȦFԵ/u;= uOAG]ʐ#Lب&:QzOHd5GͣxzύEy֟Ƒ۝xRÙ~XP%Ld3Xn <*UAnY۹A cn04^$TA:CV ]>dUDV">׍({CMui^E$]< ̲3Tۡ |0KԯRr|kFJ{ pD9`4 } eBW 2l Xp( i bYυS!a;n{>F)[t"[6~VbCr;Vur3URҲJnӾ)l@C3g[/0ܱ̦Y .t֒ے ohmFX6࡯6F#Oѽ}rF1b|oi(`G@z6"51†0"8x_ ף,DzYfWJ\}GƒȘ^Yfiu"!~iLpv3$ )È쌩]ᆨvNXhYM; @5Gˑ, cG౤Rr(RS:lَute,.%?SDMKV %K0\>U|K*#TU K0DlAm-;uL5)=iw,_APKu5D=3'VoltageRegulator_7800series.fzPKLo'fritzing-app-0.9.3b/sketches/core/VoltageRegulator_with_switch.fzz000066400000000000000000000403571272363601300255110ustar00rootroot00000000000000PKu5D],O@{VoltageRegulator_with_switch.fz}k#vu7, H#kwU2f4$[_ X|nY`NL|~:wl>>~{E"rݍOұfs}y^hGb8}{wxջЖlb&Y:7Ue|xG,V}|y^u;ضž`lz:ξ">rǫp3 nyWf_Uw/mݣ`1W_?iiL?CJSl0Dt2wᯏ\}coLdOÛ]^~Ib~($߹q9x;;"l-Θ47Fs-9R2WEs;=>?ϰ~rax}xM&dfO7t6L>d,*k<̓?}b) L܄jh1ܱq)_u>[!":;.@|^ɦOՑ飛l젋MUĉtqPs7m$}8}z¥kF_Ji:&]Hdnrms( ɘ,`.5Dؗ7fý\Fȅ$J/$Kr R-[|!b #j[ C-^HrdjB2#aS_H^d xp!I%r#-B2"aS_H>dɠ##B!aN_H*d3"q!YjdPa\Hc3Ys!繐mYalIۤGl{6/%qKI|LpFޅ>6AMg*%u!: keKal/$ TAq!i P ^H*VqZ>MVP~3ȫ$((HjTD >\")502%$KvY[>WeOb) >*]`y o}Ƀ0)=[O:l(Wuct}J..s1m'rLmހ3O.U!~s -4nƽbvBŚki&6ܚKZs%{dEXSG%pqJ" f3Rk:k}2y~KgbsՅj>llc[-[Fl6KC,ڹ2,*w\jJv.!NMnkFAr+r[*ҙ~=:rU3SڭzlJu䆢q>&'1䳭z4m~b u j>r x=ͦZ|<>lK^uO; Y_mGr~Z$>Ȧt+UQFn:H)@Gʬps̨{` \EF>ٻ*ʕɘ'9'"A08!x$x\!)L)eQE,ʅuYVf4Vxꈈ*!dTHDۙ{&odCS!ȖQG1YwW w 5e$$}H>s][|AE ˫ U?CΫDTz%=Ug̅rIS=bQ(7UA>{fmAՑٖ{lemV օEB)lP6gԏA:!3kGck˛%u~8l;?6~핲ý^1pql|~F*ʦvU}U~9NĶ1RW]OtmOX+zVX%n>l{m9kwD=c&dY0e]"1{D1r3R S…V0=pB,4_ OTlc͕] aK/~/p3\g@Ƀ0CߺO'oU-j,yurI]34ApIƷrI7rɒ,r,'D.Iu4ɬ].\BUܖ%͐5 S֡qIo㒜rɽk-,)zI78NO&I hYJ&MPh uIS3`GZ@CqI P(dM&EdQ5+0˼ DTV(ĢGߟei3=faT;aĨR?CVن([,ZP85ԛ1B*=h l]r1-l4D i%'JMYZIS6 Rͼr4piE)ckqaBHhRC[>Y*aMO %J "'v|bGDP85j3hIס;´ōK# Eb˄EUBhn&AbO,u~Oo;??^wz?uk!"׫byy^x4D|1\\O8iq1z\ \%EϓQgɖu<_S3woaF:OQe6xró ~o:wϯz=o;<οǿ^O e4X$c^=Ng%q0?ӧW鷯7bdt(|1?tkolأS6{;8?GΠ תAaơc2u4ݧ{G9p{}Ϻ#f丳r> 24YB>vG<3 e)0V'ۆP ʤcY>)HqZm7(ϬVqdvTZa(tf]ucck#7}V_4Jz`1"#vߔHN>v=J"FU8)]5'"/jΥ)֦:V(G-t( eZEM1QH Eh0F?I# 5[qxcX(zI7>N+ˡ!pq|hi!5RMZ-T}&eTSv'9.-4?GT&-nxHdeƶPmV(oA:4iqEFF ,䀥j.5E&HqryxcU-\*z Y7@j䙼搦9zڎdg{q揖Y7Dmge.]&*LV.Y]x5s8I8kQаG]s PlƢ MtC "ePi "LMA2Ve43E\q~7"6u9AbKߨC+9Jc2, KJ+$vvLMoƢUiWڼNGYa S'#N-aTL,ny2DctTm>=e%Ew#nu!8ƽE =i xrbK ["BD%Tt ۖknYу79)@IݻnspAFJgކ(od10l=dͷ e( DiYIO M#0).w pZZfd$iW\IZ) ,&ܹl){XY)EqrҢ-G'uX / )0HiIZIі&逑$ i$gVJh,jircZxi "547ji2FX/V|IKj[~VZp~OwovՑ+g\Eq__( d?R!УƎg/"FsܞQ!3ZUV'eR@NUsh@)n{Ң`_ߪL $1Hg̫A ܋b^ˆn-*+6oѮ!@PNUB.B.Q/CcoJ+_c-k78;-kI8>ֻrBsӲ2$YdCD2$d GȚ .zkzMmp_8<Ŏ5@R׸,U*k78;@TG6]Azn:;RviXN# F,$NF<iYY#dGå0IjޢtWxyͬK嶘iS3 att=rӜ튚.M7ųgތ,AB֮+Q} Hߒ-++z0zA:XDNF-*-ūmbdf\svvyQ?N_ϟ;}<ο??M_?,OG}B@S}a 樂%:C9~(srp6=.#:3w;ܘ>.zw O?=;,WǷp2MVGޓAsb6u+/ߔ䗔dM=E1uOA0V0#) C>0#e)eCc)*qABOETj7wnXXM*dDFC(( gͯ_T%~TVM9ZmQw/wAQ(V,l$P0a?~W @؟8v?E8QJ;Ze*cZQmXvl>>~Wpz;~O? __;_~םC#ׯ:_x8ۂ}Wh+Wzl`ЫΟo7Ώ' ZUH*lyc'/㫽؂ݧC k#Rꓧ=%q4KZ!+b#Kn27O݀:޺!iɨw3z?~zLJaYS{W-IVbkt._K$u$LD21A=Ng!S]屽M*z<2Md$I$b_`+ib{eQɥ md-B ?;Q2~o{ڭs=N]e3'5yu⮈#nc[Qw Uƽ FJkL R;1 ;[owbX_kJڎ+n|BS~{0aϻ΅Lԡ0(P@0Cb'TwGwsw}wD. "$vR fcJ[hyYȅyyocaVw ,TtiYK' TR7fܵp`C{[;e0ve0)&a W2 .?TDnrJ ۂP˳#A>|v8Dwj1G&tfd0ᶔF*YВgkP6 4wF4 <>ٸA Oh+]1%c`pMSf^rD"XWHP5h^sPOXjs_4Z/"7h/z6LIX`k!h_?X0hsnX} xA.ͽ B2ckۿvF]QHf?+l9MEdlrfMdV^ʜFlY;R2N- 'VP.ILVm{Ԉy 0|±OeroO{VݛSHW ymROk/nA{ ݌z9\)@ ]BAX_`kwmbr>Ebgxd z8i\ %sa]n1`Nr29+\A]^iAIJ`+^S" G؟P t0/Y c(xQX"w7 iuv&M)3˛v|Ѵטv4gy>ڎ޴8zδ+4Dm6y77LLx  `Wĥb,H E@W0Sm`0͍m ~ 0NZ1 0O`0l=kMycEr AW[,:[42g̢:+5@TA @A$@mPj*9Urt(BBa$DC'LI4?F.7h`m.4hBEqc0;'j2>D5>``6# =jJ ؃PIl!n@&(? x f& (xAdݠh80fL=R+P43X`Kf @T4ՠ{TAj^; i]T(0lwױ䨱с~D+Z*} *F/C4d$pJ<錣<9=v"--5:Z$h)m]hV# 7ÍhxM#PRU1TnSK 4CỀVB/Иn$R0)Z`)4bFkVq6܏ đ 8:Uq41 ^:`)@@ !Mb'1|3`k=G@|I`MDY:߽gXͼѻy{9w"/D EE]sϖ%*y1Hv})bg:L;ukYsYB*쁧g܊tl?0ࢇqBXb,?0.&u |= E< ^[> /I"tPSA4Ԇf7Ѭ ' =ˉ+Ϻ86L $GQ2vMK:>9mAl:XSk%z¶9uAu0[=q +4E8. 4#6^c 'Ej^,  @be n %&)|LLAcyS@xz]1ڎlOL,E  `DZW!5DU C]0'Ծm!:輗xwMhă=aVؙׅF=[cLEs"yTD*Q&!:`LBqU͏‡Qh, E W ԥF2AK(2E&^h{4xvm{mѧ~_b)y ^(9pY!esGYQkUh@c~ء4G2xĀ7gbo pIhv VkX; C<8Y%` |M9x<{K}7,Jkq ꦮ5Ԙ8` P%N DD40z#gX > 0S0"b5O66d9tݧ@09Ǽ"PBItÀDpG;z&iupp !vv$z0isx$PU̱'0]0b4Zai=[Pt+9:i6 ?`r 7\UA Rܠz#)z34O\. i3_LD>}L_wc~awDYF!x`GTP7 0Fo 쎷^9܂fQlg_&FB$!iFnS J;?%<³VDգ(~160E݋s.pHvÖ)-`ˠNȍ@xm-'&h[ $q"rBA֋ E.je{hD1.K 2X|aDDtVo{POCp6e+aPGL ƒGD`+"X93*j@#+K_طG݄aF}?sc|@^z=M]A{/%2ce(e&]C}s ASDi/{o޸N?I6I#$7B >d <(8?)J^0ʆ "&ATb6ڎ18%Μ/xmʡ(LAb8ȗB^/8a2"?vc, Ez@dAq{jP}`0 ܇y `H#6."#{RF\%:1.8HP( qp& `C8&/=M%|I1;)Q^Nz ? *k+zaG &7 i]9jXTRs3(@yrv< mLHR-@HAĿ 2\^; Qn"ž!2-2:2XtC܄KAehQ8=]i " >. [1JOG7QTK'hz lIGmwMrIuTjDyrzU[br,wsW,MIn}'?p5Z/S/| KKXUg4Iր~lq3 ##,0\A쮏AtULQ:ثk:ncQ`h\;0Yɘʤ X& ~v9yc4W?'?\cj n^baܵ%'^9$;Ka?)n Us4FO3tf,BJŊVv})fQ 6Aa>%DRP5QT~bItpps=+׽U.&L A1\ Dv0zK\s+ⲁry.\ΊsQs6g T7?Wjɯ'[2e9.kC#7R %Kfk}?>D{L!t 2Lqp5_y=VI6?wSxB65~CU\=?zɛyt?GgCg \6 p2֖es㛭Ʒi:qF 34SFENf.sK粟1ބ/S;" l+a1Z0$ށ&l0 w3 ^,!'̂%~Nuq 9$!(P~VXtv.?\Sӿѓ%7uzXpRԥ.` ~5V"uթޯ>w&o QJr^ cBpt<&]r?Xۅ2Q 7=6m}W~|VX簏r3>A#FVDS&B{3a$L$ijr=Lj:it.(  'b39@9E&n?,c(4Gҕ CN~ !V< i*+zᬁGá@ HLY3qdb]DVIVɖsRN&ֻ2 hlie6,c,nX|,P i@|l/Ch{LL,Phwab`G1TL,Q DQ1fZ"vִz9'r[咰S9&a+bI!Lm6+-5 i+SPlCht VH[hw _Fm T|ݚ;ɭf|4T7p!*Bvq?O_Ff6a%t\f=jR hZ Y2迱H8>v>Rۏ)/dȘ{vvw׼IIOMOG5jk6W+1BJ9$l2ԎUw,([ŠaIt#i/%Б2D~4f7GPLFHDI/C;Nw+Zl$>]ڧӐɾ.j:&ۅ3G fyF;y;y56.J ֮ !40ZUVtgc Xȯtͱ.ewSL*-RyJ66ZҘӆ&I{r5DAa%0aꑽQ'ܾa{@mWIу^aIs$ \ 9{k'Dh|"_aBG.;\bj%ed* ^{"vg}.@d.!F3jFMNc*jiδl= ,P#B "7Is\5'1$9AK 61̠LE~fkL+݅ő> $P1T&h4IwCg.f=ɢy>:汚VpCE(-C+)z(oL#J#A,n#+f0{9Wܒӆ@ѣIҮYk Rc&u3vz<6tH zM/2=aZVVp&+TX=bwͅ0F cxjiaȱ 6_ GEץpZ8ґT6ZiwZ 9! #| S.W뛟^|CozXw_Go݇dtrϻ-i|]~3? G4`9~s2y[x?}YGVsh<'ʟ 6N~}{,o_\ӑC^aJU/ w^'&"\5ɽ3֨;b4ݓ["'*s{yY tgwo'V뿆5L/^x^&&0q닧x _,fxx&+CWbC`=N Jܛ>Ny^_<f2;Y->\&J؛G'DBM'DkUPb^uJ$\R P\ĺytGB[~*f'ڰM^av@ ݨTN.G8{8;z#9[߀@ζn6śE>-mt1)$\[ckaYHywe"kr C-5in3`n E/Z,m K"5mnWCDmv2; n*.6q+6$aQᮦٌ8P_?h3y ͵~;m Cm6UidҴ9WUg lGڑyݏOO]pkܣI֜Sd;Z{IZB% $^_]|1HD;# \IBA\I~Rwsau?UCW#.6&U{qحt]P۞Ưq/%,ɻ ok)A-}-$s-R(DWߣ]V2վ`D[PS#\IHI\ILP0 ֟>!+ w?>^I>߾}c[Bߦ}1\Lwd9|-VK+{S y|`Ͼԃo_g7K}}hܗD0z'k 0HYpXG;&WѲw;r]ac4ANәkc snzxwA+ќIB'oJDB t{M`!a&D95,kWõPHlǕ%{EЧe/KCg>sĩzéPfT¨"F hJHIp{@ ({fF$FJJ}8䉷G=bmKztFGNsg q"W\Ts1v Yo]2&hX $TqKMB8+`W'?%U1TU*< 9@+v fQ1َSV( yt<' 2qJwWD"7D h?~|ÎVn)?.OvJ^8?<1Y;f1'ŌN+ۋ0KJA.>_U%nr՟?oůw~O/z7p"!w|H>dx3߽<.Wxܭn>{_kSQ8TݭiWBUZt8V\Y_l8^.s<{i>ozя_|b>2z\~%1>pw/狷ٗx*y;Z>V/_om~Ub7] ngoSG=/+My2-?LWNl8M{#7Uo=͜zѢzL7IG+1}i6oz^߹λ3?zF+,f}3Z&'I@u_?M'{c}ʗ}0xFx\2'_dl;ׂ&Y'G#=>P݃2]eW&37:O$ |^5Z sQ4Q;{Cjl"S 􅳘J%$n)T@wUF7;5{%wBcG̪Ҫ,|b4i#UV.BfVDbOs zƥAC\H!),.6ZWj6H>ͧCT nT@IKfQWýoz zz].?[Yž;UF04"||>ƓAliһ1ˏ9mџKÂϙإ/:ٱy{yuu&lıؓcD s\@ Ik')@{ K;9+V~JPoޝI"5€_N)jR,6_Z, 5~'ߞߊٟ2aRv0l7`Ӎ6CѰ  sqqon޷V##t66*<`Hf5t1,<#0 RF}uE}s$tB?#!T}Tjؼ`x|]i!~߫0;T}G|ؓ#]H:ZScr0^:Qy%n_IC(񞢱!X۩Q.h I&:{5)`V7E/%FK/<~ܞrҙ;8~/L6=іr (6sX)B*$:ɩs ,_N[Ҷc+{wy=4dB *leب?[ ]zK^+B-ŏs[@0mAVir6z(%EA9\K:>P|hk=֙bB-μ4;ƹ1ug1vZ7I"oŵ诉nb"eG$΍tI>M7ܷ5t77!H;dR imcbN6ulӚ[R 4 v| *6ijBE]0Ur͂4{4#>A<*aXfim"8UD[ qdszюln4vqnY?d RPwG67!H[R inzhn(NZЀSdo:j9y<0Rswg@:LeD˄kOuN%:OUHG;+j)nsTbpRT,QXIP#݊ύHBۃKqnY:yvV:1'G $^iu-Q)`T:)8I: y2yL:yy4"O;RN0x#%+9r(g[P7:ڲX}dJZ%i:Ӟ$αEL)xn(ii6Iى*=vRfe>WDE!i"t8F$NH"%KB61{(|;~ BNu[\LJ#鎠E:7<淬X:󶉾&P}OXѢMXA.iv3W/<ԁRL$L|.d.f79:vOl[xI;udiV4ێs})+sFAv+G;#^01K)nI*ȚhԎhGSGyFy=Z _aWHO&8w" $[uuDhGKU1ÓHl$12V K'ǰ})NkONj^秢&5 Bp~*Jw\h3O VXpզ( IX/K5F՘D^H:yܾOoi$22$tֆUs tGDwmK,b1DB IBV/vV3ILJ:rXV=|miu MOiBӜKEѧ %Rɀ"0R612cj]L킃5+cipb{Gf*+hi W sh6L''G=фXpD0@q.n:wT񱱢ױbn\лU ipޡ5*E+wqlszeF<#ѸMҮ/m2=wvgњ{Ċ 31xĘOE|D.HGEς?$; idӜ'~tFUbUA rOs(B13Fsӵnc NNv(ہ ~v&g K"l{ҦwR6'|8'Oth@'өQ;< u~,b&,aH۳Ɯ8WX]GI"t鴒uYbIG P30:`8Б7)qO3;nh}n)\HԬ|Z, &,rkSJ鄉MD77N<@8<_A;`=߹lQt$>zxq{UX$It&nLf:!pDvcg9)pĐɷʘ b&YoJ =;E7Ѳ!u fg [ H;@l=xXIڹ8="p1$_9ܱ,Nt,h="ƎF=A,NiadHswڴL(X(@m˩? Q;:6ՑҢOo'KĘdEENlXi;ph)J6:hy_ۭ,;m.w@#/x:<= k|?Y n4ɿt o"$j$ݤgs] L9fDxHXYY1qM> ~2R/w@H,ONt*zG%d;BHIWfeYYM_ىF)XM>" R4Ih>a؄f*I‹SNq1?Znv(J՟07d%&DXfvb 8ѣ="A32!bEO@Ď6I* ~4IŚރ[;Ly`s40ぱdeESsEM)M)ʮmǔBl dgge7E$tuz wFu XYZ&i"&Yj]xmblq#hLW9E귥4GҎŎ=Isw}Iܐ˒-KlBlMΎݱEk8!="N't䬬 Y&iB"A&əm]xD`*7+-znv(JW=uTeюQ#jۋ&jqI{$(1DGʝhhr8MҎ@ 3ҌH:D >P<ӑi;Ҁ-ǃaʊtdgژc!t$l{Ѫmnr\# oX'dc;UZzW(H@I|E#fBo_q|~<vlh;8@şڑqԑE pJқ{ݬkW~1U|Y um7Y׮b,$j@hFL K?wqyR8vHX"-D/;;*VVD[Ye+?*x:t0Z.N9rK,XH:)ݼ&Y?91RisON(]%&9vƈJU, ؑ' k'eudT3Wd]?K *tm{-N,i+gb!-;XZi81M>  4DLFq`aDHrxH(ZYP8ix0alHJaDKwl{ڤ,NhOm9Mjn+-z6'xIڇLs@dca6[$VR}IacdeE32>Gm!L!VD)E[ڑE뒳H=N";Zgvh}~)~IڇH0A~fFVo⤬=q$ꄌHvB,BJl ԯ1k>&CKojB#T,8w( Xɠfca`A6 :5$_4L~q@9I) DUv,ns2*v<*v\hs@-SN .PwE)d;Zh4YpIPFdiGFJϓE m&)S4űXMƞHV$R [z㝨MIɷmv]=+;KT@OGȶIbHXɲcX7e1R*+tILY>cP1"JQ (V㑱eeE'L$CxY$g\zcMPQ&Ewl{Ѻ H=" LfQ+-Z?g)IڇHpAFmD-#O'>7#@h;)0+tN{УBx#AsYpFFM0, q/Z&~< P[aq@񯲢'H9kM>HBR`G$^v6,Nh+X,(Xič6I v4 GqRIa:,ʎƎ=!Ю,)vq%;nhmn'G3d2dihZ4-NiIE#MҴ8v؜:wBBD!p``Q5O,s@$}-+9@',*ZVpf'ڄP14`!_,v?E@J\QP0hw(Fhq d\z֘FW{M>lł -N&[<IbMqY#aEq" *'$^cbGʊ 'vu՟0"ʮ|mAi[Q$F vZ0FhH3k֦%+-z$X&i"A&yh8xR4Ž Mi'ևȥ.+o'L"qܴl TCwlkѺ,RhH 섬!c1ҢY&i"&YgDsFH")cNBicdcGʊ)xoKęfǷV}lC,O֢YIW&fGtlNGJ !m,ihmHMrU&{!pVc1+-zbvJ՟01d!ىa at7#YM dqF{$]E'd5$dGOzYi8!M>E#MwQ2c.ՇQcceE볱sNX$gmz}{zR;Rh]R)vGIY$qnRVYi,Rhad`HML&²bJ46 Wm B Ѳ㡱eeEO@>ɷhO?kΝ- BEr6ȹ.8ZpB6@xE^6EG#l2&8;=zft 8Z&i dמ1HSLq6ssM,]JԆXN ,[͟>?v*v-SK0[=dAEB)3p+l6=?LoV*2i1s{7_>&V_ogNIǯ? 7y~|Y}ٟ(׋_zoz?㛗?}>_ mWhByÇo/FOr }Aw=ܻbo'gw|Ԅ~/Vf ~|%+|/z }n:BEe$_/79{Б0Ic7{g%{٣pڡG{fU#=#{ܤWCfbIuzyG봁l2{{LK9m xd(Fd>Ԑɻf]r:m9^L&w$ϊb[xOgD_x v]@?4ᔴ}aݵu׮7xH6s(:v}G#ܕ4]kD9ETz醢4c6SjTnvnap(5| -MƗVh ^[-Ƥ~ߧ̾RWRBfu5A$\жř0h%(+U), LvUx{oyZK/"qwp +kP{׫n_zyI%tϓό+g& u_Ш?zq=d]ڋQW^ Kkk %WG]Ǹ7~)=tה0CA)P@ *\߱oEs rY'qz댩3})%/ĉ<(rDns"w>UkP#W9sڇ*-TWEc uP!YT8}ph@hϔ?ݵ+G7 Wq|xA/hkݥD([fLv֣+J}ɘ~aЌ&zQz(!QYP`S`S`V`?xR MVk߃A9*ٴ>{JeZ/V0.I̺.mw͈y  0Oߞ8gr$oNS,dJs8Ak ݍ9rB?@zR H($"/U46>,7qJbs6a]^o00@AXX9oo#kpviH"xek pXu bƜ Bh{ ,"Q$V"w7" iu/X hS7s~s8sy׎3vvvkGs];^8G;1vGGϻvK:Uaɛ˄`4^X a8/.e Xē0oA(8D?a67Hf[i ފ^n,>e|=ʲkM޻q?ŋ nx49shdΣkA8n1:g 2K"5j)P[L̙34g h0@(QHw B!gѩe~1Vwbq8څTh_2a#ط Kyجf^!͜xpe 3x [xG^00wͻ[9(ێ`6`Nm}t}W ú6H -@}BbaAL\`5 h@J,p=+dZ]@P yp4 ptP\{"рGCP؄&a׬׸@qYp* 1fB a(FN=9̀a|b#mL 6!)b0Ɔή2ݱ:AT1TV(hܕ==`%B 1܀|MbLsTЏ*?1P'PFsU?EJsJd@W`4z Va{CK@ f DFR*uhj0<`r4hKD@lk }&C@> N J >Q3V Ra䁭DuN wa> `zSv4H"W׫z*'{!otZ^<Ò4aRf/ #- P1v6A?a=S8 ,F!U+Ze^N'ov;OV;~,SN@8A 7u `"g$ }Tq2yHT$yCkD AY`@[˩0PY0Q E h+H>8 ?A?/X9`nϠń(#e@1 /RJ #} 40Dx\BHVjx&h=|y{ѧ#c(GA;T#h(G4d$pJpFmA O5ǼBzYж~tD1|6Okt@t)nXsA w!5Be,hN#ԑ4u8пg.'ת0 1t&!aV )n'9vo:Zɭ}I|m[s[$U)FM#a¸OE ?s E$QuB@3y{[I1ij0<؞D9g3OaN}b 併| O9[{vg:ndj 5# 0D)' bCN탫?a,:?p+EOh*;(k1.$A @I9P:tмEH4Tag.)LcG#s!xe#kW+Mͩ.Ŋ)o3_>k!rBB8Rt!p~)C;gLEs"yLDj-UE2݅QhgL E`T F2ǕAO(2E&^U=:E|}+ sŸ/t|>a&0@wJ\Vt{SV{Uh.كpv(&͑e /'@QbB$368ct\R% ҿ׀^h@$ \ò!HIšh2k4]; .ׄCd`솭u><;IqQ"D,!bJA(h$# +0`iƂ݆Y#]N ל"о ~pT c*h;F&i up 1vv$Fp6!Ht*˱g0\p -!ЮYtZA4-h\B ԾC89'NWqxU0C37] Hu\cg~. a<^5 * \඘B@02d ̗2cL?@C0gI! *h F0l@p#l(]: 2` tN`mxav|Yb[ #@Fg!B0J_#7!s:hR`@. `U#1u (_̍ @:zE s.P%кaePFGF ^x0:8A0$sEaaEh RFU'潟ڊ KT4eaY hZ*6ډڕ69{U˗yM*|oܠpcA;9;1c"Ӱ0&!|`mpv7]?kt!%ve2J3@ ue6:^r ֆ sd9a *tFekӓV8‰[;$&8IB+¢mFZ0ӈbl)T &a2"/A"HL hjNT "L| Ba!3-2!2XtC܄KAZe 0Z8"]D}n JOG?5`'hv l豏#٨G^AU툝+?#Fe!̉ ^~pHpgPЕ49y W %{RxiX<|q!c[w_K{SLt-sү@J/"D~>{XT .zOb 7:CJ$[R ֎+8489S Aq2@Eէg>Fk ew/4U|͜ 5ۇ5?Җӟӆ\Ci=dou0cKqhO{V<ѢPʵP[ ќP˜P`31Љc̥g*h 3X(=u2aX6쥙29Qn߈)=A#zc $ tuC|,Kf8p *DH9Osp8B3,VCW iq3eYt¬Iր~loq#0uvoD6G Y°-" p &!Ao\׆:\S' L]&k0Y: k&˥?Qv`9y-:4?\väkGN^1?{X:IqXʌ#Rx闱jZ4CLN f)2P*YxtC-MPXU_B&+O, ]|?|& N 1\ DuLiA$S(c1VeͱhrcQ:vV"9[Th@u7KaM??*%y?ٿ9.8eg 5FdII)tlK!LD06K``WĜW~Fv-Ǫz?y;T0@ Д> h }δgЙYlg זּ/oEܥ)t'&bRY".<\Gq"0(bpp0s_6È . >FNAE0*0 :SѴ`]s@Z8" 3$::X6D5p# ć1%'qLAašЧôvz0#II( Fۅ,܀%?ϺYW ͺBCʝ [CR2^ cp 8 Q?dA>8zipi8vm!8:^a,ӘL +I}Cu8t Sy4^ℰgl~>]/n5-Wj'Si]\cp籧}ݤa>}Rm|1}3}Yؑgƞö[w)ݹ܏G>8OsY 7'-yvͩ*g\zpŪvo,~E޺w%2#sQR4%~Z[{ܾ 6}K9~f?_ESyq}^{k4Ipnr3]'3mo^6mj1"v滘zx@(JLu}AV$n^ؘ|!# t-p9U³on-V6R[{^38iLD4s?u6c=Hɱϧ'Cvsr>R^]l:5.ܶ[[O=.]>3tbU1>0(s:gFxkc>tYӖtmOׯ'ܸ6E&xxW%]m]hNϷ]lNkhO-XE3Bx2?w $9HLb?šD0sG̕_'+Xgv=SswgC̙.rq%A>hY(J+IK|\%[d7"PRAw2Ri$ōcce'! 6rb9N}a#qD*IT#ױ"H5/4|IJFe*nƈjaQ9$Na%\*RjF=;zVVVo阎.DxZBeQqj5jnM(/jCҘI+s^Xɷ3Bńc p4IψXb@5%cfi$]hwqXAf㸍23 3~OitP g̟yvqgi&u\ls*Saf'ʗDv<fV7ybċcΟLɀ6o{>d B7WotbLxrwMH-9w_]6.|yYʗϳl8>yz3O?~;rs/a0{il̯Ǐ亣mkY_eR /]z7Tk?߭n:eR29%eȿ\Ƿ91\}M&o8cmyO?|t3:ɤvj?Tۥzo<\9}/=Rh&'//}=%ew#oWGm/oq7>σ_=ns' 'Vggp[8~_7Zͳ#O߽yM7hᱫ?~n;NVf6w-O^+{-s&s-i#"oo_w3u\[q%zU8V;&o:G彉=UNN:Se9ꔐVS\'zrN:aNRtx>/|N(zDNs_zXd%n>$S"SoQOhD('O PJMJbx=YMq'mޓO;)B>T"TbL{R"uA{OR\>AS"SxeoޓޓhW HUՓ +m?uJSD',-eO;e)N?I*~iKqi'.E⧝pR>)X)%0E)NaS6T$1fvStzO"Sl>T(bSNgRO;):7IiN=IMIӚNlNOcv{OrSt{OzS77ŴvFy3>&|e|񋯫\Ըx>}?/Gv.ޱwZfzP(kZ5oaKcʦ> 6Ijt6n3yӾɃ;YD7bodv9ңa g]?3xd%Rq,i0О9K}EHnPJ˝r%mgm/ҙ卻rF0M>{ a3[P@_)m)q:I+!S)Kׂ% 0#,ɶCķ)F^$=0%xiA|te2mZ)H^:.ftG7&T"C4e-D4U5kܰaʙ{<ʟIb$Y5*@ "[{sC )oD)Xا'4*f[R åEQQ`!] S~ A&〩 yjv>^xHX3oX6yOw2{3>-2յ6+ 1WXdǟg!ڒ;#1 Ahl~չ{p6&!7/jPU7 <-TOƳ]XpD6=i&tXɃC+!2E8LYES*رq|l$4tefPQA ,b<4nnnlyh'RK|4ɬ/(a)$峗}1:xnem(^J_I;Jla# y$F4U6K2\YOf4B->\Kw0Ez#qjx<)Z9k~TW$V^z{{kXR.BO;Zpx84-ڛ#cM-vx醇h:-Plޛu)MzTădwOA%#!NJ  #IsN8if9iT*Lu&y0cw8P,I0-$O.b;M;:bHH\gp1ڝ<.ǐx&nBz0+Y@JJƂ*m:{Ȱ?@&؅pZ/7db`ord8zrevuU;d*,=b F&vPփgU5^V: (h{9:f؀~V~KrI3iP9ضZ+"!csrFlim ,I ־6YlYdNkkXyu=,֑/ AOP[Oin^eрoiWo<|} . }z}zk߆{ ÎeϟvQb7.W 'Nܷ_rGwҪ#x? AX4&OWӏ8ӧi0D|gf?>/>wg+Nx1X܏ݼR˿_NU>*_gSkX~8,=ȕe`QE_Ed1{EVFp'A%2!?d An6uiG 5kiHWރQ*MCȘeDѪ(=O" eFҴo/cYeۃHY AE RFj)n )+/نį7Mbiqd g!OEܴo/ͣyS3ޢ B ^"v/.uA?Bm(Zv9o3,7.}r-%|ŰHš5+yje-"D.hCg@61)Im#Z@nCgAת][ͫZve\11eGVD؈P)UEԡLRcGx۸l$!F߃#8es6,eɞjs%آT<Y6LJfx],,eywP:dKPr޼Mmkk<('E!¶Ft-tLЪ + &XTioUYhn"kb ~,Ce"P&#e%GcZt`i'n-=_C"j-yMsHZ A$E FL-$Ra&d! KPҎrkd]oXV9pQʰYAXZ@BmyLl- C{<qT㉛<%쬧=Tr-:\>sj!Iղ2l+q;H "Mղlf-@qrof2kfW_TqZR]\Y3u%HYv)7kBΛmbg-robg:կeisX\Zc)q[oBGM T)avɎ\S-R(!&7K-"Zbj4"Djajcٍyz%*mMWV1hvp'oG.E DD˲DtKFQ X7FEY"fa:fah=u;V \Z"Euv]ߧłe| :COx&(Y@BM9XYȂn!z|=+yHfview#O, GZ#T }r MlJ\U *vUK;Kl)ZnE@IwHbimDFѳpY7cEJ j`fcf "#Z.4nM D*Y 1 ʨFDQbEem"2ٖ B RVMAl!e2\oNFmI$6O*bgA" zӀ#eg@D!7I"QA Q6alj!zG+y!hi-tѰ4R-02IEk6Ra ֜)V_ ( "iѨvL $gKҶ-h-qroh+fjh[r +a1͒!zIYeoYcqR6K xH!v8,!v&wmݦ%B zVMBA6Es/>}r_.ogů+{ߍnYZ;'ɺ_<jZγ-Om4kϓ'۝ֳqGl ^|9V\,z憎ZuurYdz~xsgʺw1w%nؿcoWByA/CNw''ru`U"﷽R ,U<(qT{G"]8qwo ynmTjxGOBXo Pbwb۞ ~Ǽ݁ w~{ƍߧa֟~d֐*)ǽ< c;Dw>Yy2CD:,ӟnoWeoX6]؛ߛiɯY‚n&g><=іDזGYw:k"X!ڬAפ8٦5im=d+1>i֏I:[N",kcMgMxNGiuTդ8UHL|g?fǫeQM{vyOl˓ =4g/X1ArקIB))C "B@C#+iۡ (5mT~n  B&Leh2= OD&96"Cmx7ch#yFE0p96!+ʺx8Ĥ^+sd}g'o/n3"N;Sat7ni|r܇G`F"ܬߛGT9R"łgOD*]d N䙣#jiz%B){nٻ//Wi?KU7 xV+o׾E`L lV0޾ru2ynZMB;:I+Qۀ193⥣8xy+̈m˭7n[|b$,>~0g<^`h -tZ[a$Gu_BczQ n )O`F% lT50$dVnvb \Q88ZjEfnMXˋ?Bvuލ`LU@[I{R&6Z}+erTiоGkl4m@!QmW.w鎐Ziܼy:tӗWt!)ky3pRDv8 Q+FXJF KŞ!ٻxe #2@fp#҉I-Af"q3"*x!5j*yq3*X-/ca=.gbZY5@ZnβzMblfCb&$q(k FQXAI{s(ɤ>OmqV X_)}i &&A+*ɎY5EV.2m Tɼ׀K嵰Qb/dL%ՁUc={ HWQGTUgI-Y$Z`ql^wprb"+YⅥbO.?X!j_}i7/+J md5{\_%PӠCʹ.C y1U>`(+*jcq ]ʔatCe$fU9"ɘm 8K#Ԋ˹I{s2bY'GN׋76_-3e RLGy)M RӴ#!fJ8+$LWTLm̔_ߵ/1(_JiHt{o}{:3}yFEFB3RDD tljD0ryUI"H })KD!vJ3U*똢/] 幽'[8(7Cu!To]G9S?/ bԑ!B\D WU,ʴU`mRYaMnJU0&%DGghBD`A"N"'w"o/__ӻ矿|wto?}]^~k8~x~{yL˟~wblrHn7xo_['Opz~Tt4x=+npuk1~Z_y2|0,>?K/b >Oay6~o%+eWӛ92uގ<~ۯg/o/ɯ9?xrwxz4=Xsn4\L߲OWbXa|(}1f?ë?tO/^VˏUG~ܽa|1zOow_5oN@۞wiνoǏϋ</>}r_.o8xa|3] {Z]혲0n_z?q]b t{NMBV/2g7PEpy:yZ`S|Щk NG+{E֬N^mA_`ԱehqfNkk鈆Y5$RCbWE/fٶgYGB65Όd"diǙqƙh>Y3@#oMXohCV3I!W~Gm~a՛o&enҧ8ܨ? ϱ|=)Bhek&ݙt3?;Yc,=F[{ |QA" rÐ}LzFį+KDjF"~/zhnZ.ĤI?EX4к撜^Tr$cak#v4CB#J\9 җ?ԉ1Rl7XRFmxbU, 0a&oN.F_θOJ2+!WYl6^[ޓrb-ǫy3 vc@"ׯO{lB*SufaGOO]3߽vs+GU}WY i\8p֓tKeuua4S%\l_z-Ś"uLی4l@gҎeq Fhh B+D[܈1Wm&Â6WVn'ř!_VG M%(Z"L1%B^ x,l*2ؽK2)-BfȮ'7MdX-(;DQ i*RAILX em?RD[*"ɠ"nф\&*g¹_D5!Kd7MdX-2(:D" i2A*-XbC3f_AAwoOG5HEE"Fd;3, Oʴ4 1ܩ&Q{u:&'>LU[4V-V Dð2_hhQEZhFB Se+և腠I˒$H ALM!jv9u 2*3aDJTJ!cI>U۳2Q jqjkL!-U;45HDj+fjjcy|2iDժ2ReUo=U۠jaW IL-nH!-S;3Gbju\Ŕ l 0:ALT,QO:SxZ\oUXv 0D.DjvKqZ lfZf{00XΨiG˞rϬ2-(Z#+Wo.M$m08E3Ov I?XZ@ҟ{!= r!CXZ fzCh-,-u-]X1UQtb) ^uΆgg7 u­U`k)o-"%M3"h {zPmM49ey.z*WUG#%D "gHX"DZ {

ݝőPTrP+K2J@ZKXr5ӉEB66ESaxG-Fm-*@SYMBd)_V.'>KH%D* ~1p &j ڄkuZ BѵroblI5061c[+tmEd-:h+'U!hT]$k Ѵ Ѵ6AiFaZ A ܛf9옡eeh/åȲ(GuﳠijѴ@TRXTO%d& Q ([7QM@Tǔ-89C/Vh~UxRds<'NvWg@:^-)m! A 6\ ;"p-roG9CS wK;nXII]-i!KO'Jf 7 HpeU=h`Ht )%T-/8R3fškBM@9[vikHÕ{<[q8[ rPS׊WLjT"G=>qD6adÎ.`'+h+a|78+Jey)8u"v,T@%(9IݍqBܭ-ND--똻+w QmYA=|qPr*$rnu"> y3Rޖcb Th[hB16i[֑YB [7̓ M@LM m*A.ҳD-2"niji!VEܺn;nmbyS7zmkD{<m+SbvLdX[o(OD#DjAْݏ*4o֛Tmp\1B)1ukOΛ n-Qronxx_kC{u<3N -=^2@h;hmyS4ӊB VVMAlSk`e϶dG&{)Vg<ɶ>L/W&wm Y;R 'ֺbo-roboEYj`ocf+_xuin9*t?ZvFU特5 1<ɡr[_[[BML9g[7L DʽiDΎ9:{i:~2~Y \_'v6Y:y8xB 2,>NWԡi8 830zx>7ʼnk6U8y9BLsRq\b^氵J{M Ō-8P{[d e/ŗ7zxx6|.cզ$鑯nC^`6On{?5MǤ@g5wotEXcpD:'>OxLYu{{;`ݽ9p@fɮ~:.ԩA^{E^: TS:ij8u84D}4,$&lg>,ݥ_.ft8xONS + " J'Yƥ$/(wil.o;log0OWs S8lY}ο:,Ɵ'ߛiɯ̟;?ƮYz=TVԋܡ4džx|QSzSz'h7Vx I$ `ܬ.^bŰ`Ġ5Sbxbؚ3҆2N<<Cډ 拏2/4a+ESobTSOƳ^8K*\jY\{|yO6 G4W0bmjG:/>hSWڴouvxYx8و$e붅z(_7`*UѴ=$zP$`-ʈӯAQiי~j0KzRW44ۮ }lyJXHǽ"116**Ć2P 2Mx(ڹĤ2ѠX$Y+ U]\iݶA۷Yσ琐~R ¸a0ǓzHfEj1LJ]vqyd00'ӧ~zz3yCξz77o?}=x//~{Xg?dj|*xT.INL8vMx{yZ s/"qWp|&Vi{B[~ke8^B$ /¸셲tA(iԯ\ä{Jm cހN1LЅ4bT:W\ ;Rw/5 _+WNnpP %(~Ӓ80.d& ^SJl'vϢU-7sq5NƎZ.MHqS>B1 N8Ӏ#C&Uû ޼=@ OeoO3[u:T8AY tV‹MW̪-mw71 +yza>J02@ԟUae_50 (o÷#gz30f+X?6KUُ%46>0qm0wy:#`[pi@ N]~ ?jy іI::I"xd#c*0XR?8ŃpSrpP}.@2:‚.r  @bSr<F<=n;?,i 7%sv3827抦ngl5li ];9'sc0m4iWcvMm 8Id@0 Kʀ(+_xc"+)΅>qODF RfZe?cX[X;n]%wiYDaBȪ'=Ma:(Z4iIW rҭ T `ȧe*SAZ Da*85 j-!5:aHY6j]v167bv TDS-m2gicx2>@Y@0 #o a, OU@0{Qef qo^put} a Hn4ɔ v0YmV@9zC@wNg _X5Yߠgiи4 g]xCP cg@W܈@c@1l0`ڳT#|!{ xA12c pJ-k5P\ pƛ on(N{rC xW-pG3ר32lI7$Ʊ;PG"HÓj³<;(wlG8+(*7`B!+ hc v4nmeD:ᾫ y bѨG%Bhx0Lz eÄ9hH?p4\ |0 0k%G *5MWqls piT2N=lP F {cJ@?C>+Zj<'Q]SBMGC^h<Cë{pVa# Z=zɽ _6E 0Чr$Cc`>ib9!xDCF@/\gG0Q[s1?~PҶ!؊~hޠ#:u|:S#:M[rGt+nX qnuDCh ѥƎ"s掤l{vX$$3:œ4h7Gk,EtQÌEe,48~9Q"q$gȪ% zh0)0-1 !]8Ob a D"kZ~'!̳H=;,.Ŧ8w̢.1EagY 8{Ab`!Vpj N5"Wsg!H{{d3pуVneC!tt<ڃ \ciulJLftpr Zhx 3ŒՏhq T0=9Ӧ|qc@r0@.iS: gVpe2>9A2vvY iN+=a[U?z´q#枰RW\'Л1B37#A4w^U@ +FwI1` @bOL< 17gJ˜ (ɻwYg ޯw!CiX`0"-ȫ ǚs*! 0{xΎ!/mGx;(P {4zko" t _ N R!N](p[DCO5:|@f#atcl4?l0"صtTөW6\CH9r=DGKXp/T19~`O[ȻN}pXVQV,H/EW"ZI?.r%!{~(jwШ27u9*AT8xpW-A@H0Z  2xd0TxfT(cGΜBal}E.ꅯ |a`%.b)+Jo5yI\4G2CbA1` CfuNj7 ̗2QO?8 g:fIfCM⤝]Am4(&? +Cd蛆s;|?H}iBa,D@zgȍqHN0a. p0Dςk@Qbl'W^9ν8an3!#r#lhОI9V" ?:U˜L"ʇV?+M☘ "H7J!eZC4 O*eWA0^CABT0+aCFQ6g`ؕ ;cg F&b8(#3s_q%0$`\zNzx-ix],(@ 4N@a a9􈢍$"q4Wz؃3b2paK?Ѽ1pT 'jWCnPԤ҇17(ܳ1^׀FXW_4g(|H^NiM1i:&; lpQ;D ;}>GʍR+¬~0X0g PД4-Slm^w -/  ^64zUgr5&} J4_= ";>|>Y4 &DOzoAn(u| UvnTÕ᪗ W؂E pC>Fc etiYjVj5 66 ׂm<{zwT: U z -->z ye-BMWBb%T B [!TY\ d8X p2MBso['(\0ob/ZhS喫FL )W#]OvD& Af}/r4 U"5"r'"a1[ M]} ( d~( 55Eg-ÒGS<a" r훛Wr5f2I*1Y4ML0YSd^IpLDCQ>ۍ)ixa 0SKXp s 9YL?| 0$=tYzu%~S 2)EO34D1"kch)V" On!_߶l-ߡW"YK$ªP5qʗK!$Q!!qïX0"a”áKܫoFIi0Uq,܊l`u>Ma,ǢZEם 'NӒ(V\Ӓ< '6{UŰ l1a0G `SC01;.0ae㸂 sB\,ŋOT<8}9~ Ou791 8N%5 5̟,3/x , 蜂b _-[oi"noHd'&bPY".<\yq0/b0p0˴ X#&6/sBa<*@Ԃ1W͗SYi>/\|[yfȩ0`I|Sa~LaNwJ9{9M&O* X,:% =M5(F[|oy=TێpKH7M^^ZY{S/}3M,8t%_ #x"ʜ+WSQu>GTQu>G:Ez|TQu>G:U|TQu>F-U|TQu>G:U|TVBb$U|T!b]NTQu>|ܥx:|C:U3Qu>xT]0G:U|TQu>G:U[U|TQu>4:U|T7U|TQuVˢ'L|Y7h9l4wʅς{?H|:> ,RQT p=}z_/*n5X}՛F+)Oȟ۞iS:~ +Pn<&^5 mA>%sk/OΎ+kz%1Y*f|J{ثMׂ@RɛboZWW4[zhUVVAjz~E^L/PZ6^,tReojb`o[*\FKUMV5)QxWzVҧ]JKl(ZI@bvD+SH.g-rU>s-ɆcF{ra6rJr+LŰ`$X`+ D|E,Ƀ/FH ,ѾvFo?VH|1ג>.^kvlׯyEk$AuŠ7߭1l<]`=tHgauCLB5MuXύZ_0?3R~Ggq}F&zܝ!v?Oh>x<}2uaa(nnn\dFN7{MyޑXMnJ j ؚK6J&類W P8܊kݳ!ܔjp%R,铠Cr{JVuyt0fY2ݙ%R *H_}ClhZWCM t}W#aܔjqVL&iW=&:䀒R$qmI+vUv`31 IDۏN,z9GZFLL-MuL[][DƮ>}rtҷu6iѴ6!cle "m{S+y(HZWAlKWz2{o!BưZk1$:@"kc6ɚK ,l[$MQRbgM{ssZ#-/k%3}[k$Cp V3H{%o UGtKa>0_`F,q({9=@FU;L(('30.'q ] X6{r|ucvl4$CH!8PӖPXWKxXiAn)e1޴uVz;JXwU,{Њ*XDKa<-iBa\@0k8feo2:r 7d;@!n8P4M(9*1fOA!R1b^7̓eE#YvLL@HwX<ԁQgG H(Ͷ{DR(/еN`Z bKDZ3"KHcf^1/ {jKڧVZ|$uI ` qroZ=iYtCfel[|K*gyvAqԨ1(qhjpʖΎb -&!%غL/LjjFCuBq<0bْ5QN܆+GRbC@Y#-(,!VR xzWǒUaJznz['Gتjd鮫F:bk Z3. Djn, u"l}PlE k|m5x_EU (¶]q#eUn%Eg)]T'Bq@[`r!6U· 6 (r׹C {P5PNٚ}u_\h:ƧEƴ"nKojeU,Cl+(!8P% 9[F !h"lQZ Eskkcf֒we3܄X[ A/VclzqfmU(-a Ѷl:X| DV٨s_ d+ [Ik mcfs,N-2~'G٪Zf-qqPBlq [KrAldYA6"l;}PE [I4ֲMw\w$ `V3uXy7ZC,nQ񵍪Lr6LNpZ(Ba6ljBUF~h˲:j\y/qs_T+y JkY;5"p}NJ [Pz@5YUj՚ve ui4\M'ӚgQ[E -z#dZqO8(4q²$;FI0zÍX8ahBy,!V(+6 i7D)H(e ";R[r qZʚf5d2, ]T$m;zAhBTy,!*(l-gTi׊ gK7ݙAp# ePƤkA|ܛXia |Lw, &*rc~UgvXA|M8W>WQ]]J)Lvǐ:ceTd׹f4$JeAfB'2-y%>jʢ4*ڃc6Al.d2O26>9kd1(U/&&&nZ NETGxBq4#-G˂H+:˜x(e1w5TE99Ÿ%R5wPKg8DH;color-fader.inoTmO0_qS:aЩ" ؤ1K46RDN@}g_.Jks=q+.ձ*%ҕnG,Õ ""t0dlJgjiC{ `-pyr$>p.5r$y]%hZ AZŮ,\[iZJ*V ːDp%uIw\ KhRe޸A% [r]{]sTlʻZ3zQ,A6/%|Ue^|(p4|C3SZKU`t;~sLq8,;$XaLPLM:LiGIjrHǠ* !~HOŘj-R!ٌ+FxټǖL¬Gy2dZhs' \M'zM\|ϯ#|^\n2[ WR(hG~5xھ SP FQ"ד(>t0o?o7'7zjʊmx}7x=[;LKarZIadr<_MW2V'͞-tyU/d| yo|_v8~-c] nO8 r:tlz;yvmef2[|obx=zLڷzxev=ߓN{Gtb6jj~[9l_ghK/>o}Kloo.LYE+M{Vx5i\ڝPj???v~U li_ܨ|P~dq?Y/ ~HBxz1NL~ODRT[78\*YAc ~$,UB_?9N{XLkۓ7>L~N'@z^?evcq)!(y.'=eust9JU{w;Mi8%m9AIsFXYW5¨NH8Nkv!&iz>_U*ZQ5{#69ʠQcqjim1jc:1F51O6gb3JѶ[v8s\-&'9f3JLSUC587R{ʀS~`OC0wQ<#Rܗ#0)?ڱ3/#jT^\=q:G|zцusPVh6{G4\T{dLJ ޅ;.6qT b1~>Q鳜~]UmO/7P_⑏؎7am*Ԧ0=vO8fF{QMדjܫr2?|=^~GŎ~X\Ͼf|?}yշzw}YMJ?C+AH׫udzxy;׋Wzj6Yo}fVۯNbG{Ҧ+x &=<] AVy: Ƴ`׶\뇏`}7^擉+YNWKbN̹3o~e??,דdd6^ۻL~?X ƃa[J:]?$/ݗaFgշ؉wuA]@l[ru@쿛ZYV jg̵n=GN6jǒnhaIH3D) SpWv뛘+*(($ KwwX[ך0G9'̅ c+kzdQH:5 Ff& %S/)?9WO,`ȓ? "PK (B 0 Yy[!q_E;Hy"?G6P_sy6-PV$MzGI$I$MSH-i0M+HQ`̌ ' ݒnRicx®F]\\ DTiL,#ZCi-dHEdKw%d^aH\" 9&,ME>V֛]w(X392t̑uWIdKdr"84K /O h_-P 5e n=gS"% +`REI`}aY#(%KRdu:v%v/;&t]έՆǨG (!dLj 5qĭG-P 5e {2#U&::̞|̦b{BG^,%pX69?ʅ7ߞQRP2ɑC%ĭQQz3HȂR6g6{;BX:2XBvu=h|ALB҇Sd))Y3M. aBsdDz6ߞr2ɑ12qW U=4W-Ħb{GFKαlmG2d$l4}4O-#T()cvC0msYAJC\q/cɋef~VMJ K@nt.v <FLp4n)7*?+sl-#K#? pƤYYo@q9}~߂IYܝ}ƒH͚K|XI3ʲY _.HBLv舼,eXCpo2eB<˸Q^qi=Z&i>"-; $Ҳf`2eČ3i"1 8"1k<"1ˈ9V0-3\ZX1# ٮ8XX֛@LqAv&fB/D.фjtxtLg`~h&!JKxMHgǂEǚC2>vei| ױ>AِQ-*HVi;2+l!A cjo!ƃ$$e4_D/Hg Ig>mq3"Je$ Ņ#rcQB=>"RW)ES4Fd{8 h9{ݢ JMۑ7-SfM Xp)q<Nt>TbJQJM%FY8!1 ČZsdZoZ֨Iތ JX&ՒAc8pskEՉ t۩@_a$R׉5j270lo%Fq Z"wkK^6sc9.o; 5"m K:RZʯ֯y6&L"g$1+-_y:s(%0"ԲH[we{ē4Ml#<wDXlƣ/EP r7u#Hhy>Gky}%q '0,/֯UC 8ڙl;4GҘ$|_^c]eWL SD *%U+G,z窵ʕYvoꆎ8W)x>AkyXzPl* T¹BBFvQDnɻ$~6%ݪGfK7$,_H\O$LH98y 5^16vQ„nɻHb^mSKIx O K(eG(\7 (ۃG-rHZNs N.maVua6w\-ۘ(M9\T{F-Sg@\3PS֚^oC|Xap?բMJ H !48Y996Os/dd Fn`53iZYoj2Ae .Be*m}&XN#K S"KkN^,Kj\{_m.(2ܣjv"DDq5vUEdˋ \+WutkWY&D#ppYn cRm%uؚ(]LSt֔摭)5'/lk: $P$sL,b?^)i[\i&qǵ9?6FJ:\B km_ ^ZiORTqU|-`H|4ytM>CUjhꈙPú RD"ZcyZ+ }ˢiui̩3Pt+^b-adh ,+ +lW@Y"IA%fŲ4ʉIp\9d ɚ&Q\q3"Iy)@J(*5?RqCSYD ->9[3PR9©̚BfjSbf-d|9|-kgd$*!)TR$*JE#_kukA@Ekq-P%r>]"gkN^ZUd>nl !9#0$.'S'ĵ2IJɘhfY4!Vڛ3A(%/orާb1u Kg$/ !O_a=1߅Q_=}0SG q"85u[bWIm%vu C>{ꓱ'fK dP6$UOƒӰ-8{Mi6":^j[s.[&~DSC5;,}ɪ>,ZJ5Rcua.P'H)YE I:ܧF*8jX/`sN|^[Vm[erYNDe[wcɑ 1ؼ9`5%P C1}5iMǚ)龎z/qjˮVfk"aJXsvE\{hϸSsv9ƚ6 Ěk:˖XsNcEĚu{Ejb1Hlڎ5NĚ\}X 6oNk8جN{*YcH@f]<9xZ =]阫uUc۽˾O_&:;Fg, Rxl##+Dic6%Jv3_o(@[ ND nNk6@J4JK?܈5ExZ#Zddk(F0 Vj\b@qƶ`m@r6blƥg#74MD1Ǖ5Z9LR#ոyT_c&|0Ꚙkͯ)e7*s@h =?rF2'sǑQS'.謵MvVg`Vy822K2%tE٢RWfߏ"nNKCe;(vXܻ(-CwdLL}2{n(%~a>6("d磄l4(5`R⣄ &uG)(xGdːWzG _ǩ7W;XVm+meeYRqN伌$r&ea.I;`9#Hw392 #Hw`ճ/ѩ*μh{ʼ۸Y=bV xzEp m$= #L[yʼۨR=#8T\IHR=#4i|FFs2^M}$zfFhIvzvFhx 34yh)m-+i&~:{ckG^z<̾sOq6ɍU4~cDF]s5| rRe{x7o_~YM.|toK?\NFߏg7nt%aW_^O'#jG,Z(j0 ?n)S:aqv>wk @h9Y->.&x^~W謻= "nl4 (-7bȤL3q(K,WsOve_8]UatReଫsD9Y"diXDV^lvU^6r9tJ%uex7n,ծDOof׎(ۚyu~mI&񟂙iѺ+RQ+>))MAvEHYϜ 9Er0R`?DpwHl88w ?j 8Nݾ #twOs9@p~>={ T3Pb;@99+QIj4p-n.FMJQHi QM_#:xniikO"࿎ t}G z.3G>)#0 "/#RЈ])Vm)h2UDbjPeDdHh '%y;@I"8yD*t#9D6z72P[E )"7M9nz$Fy?xhѣ1:*E#tM杆9#mjӥ*1*DeQvNPELI%DR'T,4{v|<Y S9\C#T?4HdF:'NIk 4=CrDGgA_ 16"P}&`CAGa!ʘ h!16!F̻#PPCpɇdv/)6~]_ןKra`>WOGq%yoc~@ۜcOfMq %av-5VPь@9%D`1; W ],a´rݒ %K >,iآH҃ O*/# VNg)tE2nq25<^%Lp钴A`Ф1޸sIx]'#H\ 8WDN@ҖI:G'%I*ߓ|,Dopndo%9p%%;v1@Fh*Zjl}4![;'mp`eT5 ). aF3#M#ք 8y(wT|Ck'-J3J&'|t\XD4BuqF\T|c;2H0[LGXFۅHkG;g1$l3ꉨ)CXYj1R@VLfn~ y[4 *q[fX!IHgg;I"5> p@YGz75ElY@IjKibxR P=~0M6D΢}>"mv$[ټyU2ܪi8 4w 7Hj=B"-IcfsGAV3lEnݛ+f6E|lZf8%B*.wRD42Cb ^zBHb_Cv!3v!-2 zBe}^6CʨDu"AN#Cޑf Cm,E0tEfы!KNͲ#Z\v_6b"O!7p[O0w mx_ %K䪽L{o+/X>7V,ijFS [9瓸7j'`)@fit;3N l̆N/ʼn2 &aq-KqcKq ʋqQğiܾHP0ֆ)%8Q=@Ik4.=G*k{]IJ,v" 0(wm&i&зP%1%6[a}⌑;[FDݛ$ q;Ki~m2 lluWQJ2"<Ol;rە$RC94w4!S c8-3 FDwʹO–HŕZtQ8O8wFwʹOH{Daqg RH sӉa.PPH4JHZ("/l{mKo*5(/{!$Rڑ$2ԏ&qAeĐs8eq~)ITAoMy|$%<.pݍp91 w75Hics0DF+-IDD RR/yj"e ![&tY$hyq 㦼ne:ъnq~|[f32`><^] >g'*7 5<H'&ۻu~J8ILSc~u7~q >Mb+z:}|]?P}?~nt;ovp4;}󷃿zYwૻh3O_ㇻjdOE#{3]nl8u2,VG^ |00}\U/~{X,׃go? |H|ήp!0V՛?n3kt\7HJTO߲~dƏ_;ö́)79&k=+cϿQ0#+k&鵿C_NꟷyvUAI;d&O\'M쟫d2=dTSW#qfoسʕK&InPI'q?am딁@#݈@x@&`IJ P* e;0>O-hy(YXu#`gLvύv ɤ Ag)TU]nڸ|Eh@*J݈07(k}6nyJwqݒpva 9HdoN[sPOITj|J{ֆګ, *DyC#ϔĸZ,SSC4twN-ˇre`ˌɏ}kGDj?idMU[fkJ[N&JHT`n LxWFwoPeJ7ܴC\'"8N`6_M[ؙk@a~ uQOFusMq'oŤNv,wDS(ˏ-7d{6`tA8AѣFn<l{a[b t Df SJL,V0` 7Q(60ѩ CmFWi`틃1.thBCqSp;'jT\"̪'P00y ޑ0$ 7;׭QV`i[Kj%D[ c1Ds Pؼ0If\`5Ek:N1+М|43Y sg)88!7`gAGg ua'R!|!{ xDA14IФ3y( @ƹ$70HR9̀ab#mL 6!)b0ƆΏ21:AT10 FS?`ڣUpUoBO 1p4و1 QC? 62"p[u߼(T˂zܡ!Yt?PU3D&́qn7(nv2}@,a Y()FQJt]1F * Qd *4==lnȐEcjf_G~J >Q3dV RamDuI:XE}`uBG DaVa<9  0&~C4%m0, ]&eઠ)=2;Q#A{ ,`=S8 ,A!hUd{뭦ʼ5 <인!f8޶z3k 0𤔷nHLT!"F?w" Q #|(pB!H<! hkt9*Pw5n DTpx劢׆fc$z GJ0`7gcq 0.pc@^Fha,1n+vBŅpOz|rrrעO!FQ.wh G:Q,+hH|0`+ x<ݖ=YVCi-@,;υ@tA\ @VcϖJA#AX6=g?VqGCgf%$MћOC`C ?`K0# a]d d` @ Ffb%vW 0n gFГEd%!){EKE}wXi.[J>\VE0}țXRy=D# ɮ?kXA[&背:k.7Ef T "u({ƭHg̾"B'ć!""^7P'-݃H?().WNx MɤƝth X˜͏Q T AL̝.plH&e~\Й@%9u!=i>%9⁶Og ~:XSk%K#aǒ[Y,f?F”#0"a[]9q@~`Ģ36s EW$QMB@3E, @bo a v$')|NLAs[.}{YS׻tCt4r0=@= cF% y2aXS w@T!0z ڇhh8q;  eDT1FPeSAb@b@ >I>O]OQ4O_ =UCF @|وaf"ض[A3sj&0 7bŔ7~\|C $0p]C?aoNؑw d1-"]/H"Rm$|Pz{óQhk,L E`T F2ǕAO(2E&U=:E|sl{m sŸ/t,Uv^6PrBVV= + 0C1i,ex(O?"ޝI|KA ^⃄kX; `@"8X坦sK$0rw/ݰ{s+)1*P}2ǥm3}rP` @ADA`4{# aifZDVY΂݆*H1XwBW0*tqb|a@"ڎIg _Hxa5!WpKt{pz$ZC]Ki { q92x&"4q.qb^G34=s>%aĥ)tj9Y>,p*WyLO@<"ٜ#M~|'09}0H chܜ? (/P,~D +ChM9` tN``=wŷ|%}3!0dx"OA t@tH5rc^)0& 8V^$FE1hDh{aZ7 Ԉg^b} ɂhܰ`@#hCbG|#1O R )o;/}6ǿh!8ƒݼC 2>c QL8,% w.'frl,1A`PˢU=_00ob rHT\fLw#7{ 4m$ (YD!gFW*Ez;Sτ%tyn8]hl vF2DaVMi=]\^0ڨ$ *Et̆Q0q;r t4GdY|ш„$YZ8 #wGz Aߘš$H"CdAq{j|`2u~q`H0">."#{RF]fbN'Wڊ KT4e$tP8Ѭ1T 'jW6GW 5i vh԰`Ё@;;&r c۱a[ 7V K !X냦 )ΏMɁ"?*ɥa-kYp97\^V)hPh8ZmjzJ@/,ޞ 0$9eZq3ԊJ#0^h4BjK>R &a2"/A"H 誷NW(e&_>Af3[d Bd%xIx q$O ؂kOiz4l<:*e>QH ;͏MԱߑgۗȦ6*Q{DwErvU[b.wHQ2Ā\Yt%MAn`hÍy[^'@_<ΐ܌m^ߘF߃E$pf>{,ph1>"ނPPE:8.p㸪\qPJ[1Nt\].1m;VPFqjZBWYat}{G笣QZ[Tl)vi/k: Tb2Z~Y*lPyA<88\ p2MBSV&nCt@n$l)SFL> 7#COX~&If,s}K1^0őe6@s'Oefốb*\]b ( d@S.k ǎFa7<لaFd p+"% eYK0}s$fP6؛k:qbɢkb >ɚ&KddF:`e wEj7 '\Fc%N.}9D.,n-Q-qqpq6SҊo˶O~FUtZvd*d[Yޝ#xEa;Ƴ->HN_.@OK`opv7H{ x6!+tNGhlӠuQBHFi YpaE<!/&"/0 3^aT/4]-.S0P PK8D%hLenpnPowerTransistor.fzPK8D!¤)LnpnPowerTransistor.inoPKMfritzing-app-0.9.3b/sketches/core/obsolete/000077500000000000000000000000001272363601300206575ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/obsolete/AutomaticLightingSystem.fzz000066400000000000000000000256711272363601300262460ustar00rootroot00000000000000PKo5D4#+AutomaticLightingSystem.fzY㸑S(ʱp{{ ;WLꖧT!g/@R*"<,Ɔ J/|vF!7j@O~o+ߛէx+d>>O7Wy\ۛzngçmpw:nj/&^_~?}s%Q &&?7?m?~UW?wvo~߳{qu5ֶ ۧf_%zK|=v?''@NFeOq{o~xyY}MW}?wjo)tPw>mo||Vkqu?%1|7to]?.E}}!WWaa&w8 !`HWkmͿvOowawكv7~B_^L9y~<٤*+/e]=\[_WNc{/+e5HjE![*{!1 69TȾgI $ݚR͏^v?g'kfWCKߔns[ҝ{BD,yb>ŷJ +&x9huW0n8:ky:}T}|U lTflݾWK M"8_h [ۺƾuֿs$+ n'Ʋq+\l{[/m {enFm?ZGvB7.v;-ʽ; f [h8[sm߆c~o3ݿ] [/m(f04pmF|簩Zk sew07/;pkACHBMcŻmT%@8!My{~  bR5!ոpvR/bJDl+.%Z/$^H&H/$Z/% đz!) .R@jENFˆn[12Caj1rC<GdÎ YH|;/۝lK}ȧxs?"j>ͣW=3כBR=Ljp! I -ĥ$| IpFho( Ip@̭ԏFHz))಩[pL0|텤c> d27ۧL0$4|*Ȕa/$ĸ/CG₥^I WNXꕣ'OoO}q~LJm|<_Mg5Ǎ~#1lwS7w:*isCB憪HrB/y?~G/FI6 Kaꟸ7帿>naqOYyD)꿫v{~n66<ɠg|(BUKNZDnIj$BQ5R$!(y47Ԍ0𗃺=-حB_8`nmqhq"S!+LrRBDBx8őDNH̉(9xFQs o*IK<\!})cfV4[Z$ca%'cb( D 3% E! Z&+4ˣ᠅Yoo '| $"!ɧ Q$l;eH'9Fܼ9칲КY`Y'?efMWp\EJw0(в8z4 m֏'?%`1=ZS. OT3 O~`Kwuc$L:G j!ջ8.S#Iw7k B-[睧_&Qgએx*Mewt~K.]vW}=_YMr AG* FY,𰠇QzG&ԣTzS8)qq~Wz26XQ w؍5Č%bÚ!NCqDa)DE4qf@LХJ vU@L{v*ϚơK7Ț݈8 5q"(rY$P("Svf^Ri 8C#a.ꁳ%3PzGTUO ׾ )bxOyQ QHHw$΃aR S;*Sr!}_&%RSޮeyS1w~J%Po&[u2,ն(EEI RDYpw()}:GB7BG$_H#~IWO٫3 4./N,pآC:2o6ul*(]./  o?J)ywyGRU k*9V8A$-i En(D 4 &"wl |I9'$sNp9[B t[$q?}ۯ7Tr|`%)q{<&`B27mY M#jSmcec! bHZr~ùlfW#NM_ oY)uo(BdE8Me!vNlȓU8JչZ֤." )w"s%$HrVuB !Trޏ_=}~tAh?` E43b5|#VF;7f]&2t }oqt>~#2v^zizk)|\/SOw>_1֛us땯fքsd>Xo2#4`1F"^ܯH~yU#bb^eΧ=K#5EGbLoY2kq0uCba,Otl?aJ1cTy,3D?H_7qpP Iql΃OHwݟ$rp|+[=4fjFX%2"Q`H~D=+0M_Mr^Eh~+{@~ HD`.7ܫW4t'YF ȧ}@O@NWh]on> p= O9<̳f>@ Āl zdM'J,?"uf^"0W}_k[×:ְbr`cF%f򚀼Wu@^R"]% yQs~@~u9"sμv5~C^C .3"ϗWifX9㤦 7ef,+͘M ^jo+8{3w`;hqLp ]K/N- ET"_$D)pDXE$Av4 /Z5pok}`r@1" P 0A Nys`d `%g^|0g )_Z4bGx󠽳$ѧQbzN"ݰX';=)Q&T8t ↭]ՐSQɓ1乎BE=yxNCLu* QZ EF=ur$Xb7oRs^si[[\Tye'ֵ${qKqg UҐS\٘9tCūT|Vd@CL{.00诲Jy d4GbSČCmbݮ+ b9uO, H;&0riIA&a< 5`a4-V V@hm@"2&[4`‚"b%4SHvy!2'2PfDy^-.J$ar1A`_oJ9p1\=TV,Xr5Ob` &ʵp6` (uC,@WC\vl]&!d"HYy*g'jdYJ BB/S ǠÖؚ`Y·d0MT͑Xop}o{jMbPqk1Ilw0HI-yraˤG[ȑ KIF4Dy&%*'}NyմAtO~ah>iq6 .)6M>}!\?PX J74Fsz"CK3^@ϯ##RDH E/c8 5#q6g쒵;(:+tӐ zc T?ܱtk lM%KY?;Z8~tdE>C0QLwUzC?l|?){uqs绛?펻zx흺ݰר2IL-oAAJiCsE#4KŬMz `͇7`@!qجQa=c3F^P=ctglHI) ʼf0{DJkeTYꑤHh$?S+4EFY}a(7324'~w{:u5Vju9?cV?mۛYJApj_|:eQ zoE!In𬾢?_V? I& .922`lv~ͼ5CC/p o0F8A8 ёqGOtXd Y~tNprfk\#.rQq\"Dh.s,Bfe.R3Q1 ,sM>6w|+dSo5GU4:x' R6IP")BC|D D}UN,/Gf⽞ZǮ>ǯ_GU %:uzcw4amB!YԶ DL"IZ$ n8ZoMř4e&-vsrh߀0g=ZIdN?ӎ?~^P@ o"+3~//@]FK?a>#$e(rэr$UW}nGCvh>;اpe^VSr%uM&YnNXG4 (?N]!"ጄ8nh8iMi-0hmLEpLBEU%i ^2@聭4> 7jq"ٰ{i!1"oei XfȢ[Je"TFA$Y{ - Zg,Z#b/b/Jd H W(L\YPtx}׾[X󧍚ix={ T MGHz\kQXZ@ވPw6^$#cY0.00( J9FHp4b["Gh}*hơ^٫JJj6ԊS*, HMo6<A4Q% 8hcjK6 GBL`ZzqF믉LC~Kn&`mZY=2a&Pq-H<{heX''Dz}*˵{:3 LqڭX`-v>ov?]߷]>ۇ[E5-~A2+JőKYsP4@9AUnu7nlO7)1`炠NkRY=,5t&`n+^3:=צk}窽z{ 6K E[;$M[BW讚d(dY-% [}M_?؄b+4 S_./N0sD 믍2T<A@VCP: GZvSPN/S:b oԮ,a~ٙ/)ױwIk_ʛISѡG"WʻYJJXL0y뒧1Đ:ք9Xa8ԪdM3~ <膴H*B gps,1r]?hjP HdVT F(FbJ@(%f)IU R "taGDbx@_`>N8Ժiᐊƹ YB)AARU"D$ISO`C-+Cw,]swՑYlDX:$LCC:aj|dVNIiJ(D0(Ld1ixʤOiSN'zYm6UPVD9>_CYwܚ͠lfKCih(KdPcD qT XJo=KmY{gLa!%yÌay%A,jW arfPqҭ @,kKijiPpx xcbԊK 8CIQ>BftHfb2Ӳ̃Ϯ[{dz\f(ҲrʡpSH4` eCX:"LC`4ḵkȘx6r #BsjQZT;gyd}~b:>;w=KAvYIΪPqw` ڇjK7iji=]q1,̀- p1ZEw_Gy-aVb2O`Y @R;+I NRR":v[@2D)T "yKc0+Ѻd46UZh#)>9z5w,ݚàHb=BXzLC@0ᒵ; F.8?FedJ/7zw|1cCT@!;0 Ѕ0πֻ'5Pʀ2 7"foC)M;s2Q.Xk%鹐׽υx,ڻ XXHl]arx3 V9fh81"3"`2Wä*H|2_sֻë'0P_#PBT[fDY`Y)%GT&%"Y:"\f | 2(2&,Bgg]2..O:tfjT]v,r\&㋌FJW0h͜Zp)%X=axD3 c*!.Y 11memsKG\PD?dG3l(J~֯(n1Wf:rn(Ts?1_h zc4F9Nt:<@`0e%k'001%qT`Ңˠ M; 8t(@+&0 9Xl]GêZsL q9 ~La}\azx3 `ʆKa@cLcrXapA5Nvdq$fSq5M4zaTzޠ5I fETqҀd*ntC *%@>T(cy30k_4I(I*D ܫZO*ԧ+_4;ᰑlLMcawx2 m?kwqCׁ.‘/3*ui KvOΝxZh7&} 4.R |#3h1B⎥L;f/pfj9T>\v $!#~`YEwKc֯.Olf.gL g0ֺd.& ЬE%3 cʀceݤCX#LC4⒵1#cEC ]+1Ho# g<{g` [Ϧ:g-$e`=Uf2⎥[3d-BXzLC1ᒵ;F.{  Vu6_-+ch<3bc2 }$*xV=πj;ng@tDՏN!,OjTC\vR###Aj$ /ZQ9:{8t:dh~֯ΦZ{@Y䳒ޔ;+YPk:%X5Aё髀A4t".YAю1lBX"R x{:w=#YɌC(3@咵 YyJ$Q$FaMa@K0 Z8f:@9|ᒵpƮ(L(b{w)kbơlMgzYl$z,_M]kdC`;nD@&!,=el%] eP0*!IӷB!Wz։g <]LfD(*f|j/dM^ "CX5Œ D2 ejQ\,@$cDb#0 ؒNʧf%SI kzຘ8;`UmaV]H_Exږaю%lL]BXLCS;4CÑ1K ^0AW R8$z' y2&ԘrȀl/hēB2B%*v/咑[B^G/P^PT1n_#`t|&\t0Hrc p}B1_~vy3Z[;ӮGH{VZѬzq0⎥sRC4dz6 axJ3 ?T>\vN@j`jЛMdPKo5D4#+AutomaticLightingSystem.fzPKH[+fritzing-app-0.9.3b/sketches/core/obsolete/Blink-SMD.fzz000066400000000000000000000077541272363601300231070ustar00rootroot00000000000000PKo5DS5z Blink-SMD.fz][s8~_a:UAH3dzRtwәe pdkJ1`cv c?$Iх3t(vb;6uZ0T `yݞp`6uvZVxCs9^~i?ր6V@N˳:n't`Evt| YFKۭ\9>nYn5~svSSPUY@b 6/^DR@Co% Pj{\& /0&*#G)O':.WۏOWg@%Loϓ"I"E~CF ztsq& )N>&?kYab\?Q7j Hc0zfU-z,a8 vDзڴOf4HyZ^Mt>bs;ާΝ G'IhhІG{ 45\/X"#Y+ٮy~O'E`Cԃ 5~"-k?6t.{z[&q邨ogO t끑ܢ̜^/Z>\],CpC+b2i: Kh~zZ$inKԕnؖ#}i {-73L~F6k4`^ v',Xٕ]0'iefM76jiL%h:}gE & \ SXMId&ǵ4yaEC RBm;''b,j{OĚg{#:LQ 5&REsNB.1jG #չ6pQԋBE XPMB!؜kPC(k` CMէ0ldܼ(2kusa,\LVAdZZ5B<6~biaC LK)i_HV: *u7lgG~6C3I 5X9hkz.Q_C)2A܊h#êN#BR;dX;¨K$2DѧO% %Y`BԚ`Gɬ%s2K }2K#Y,UzZ"Z-csYUA-bQQ2|5 G !)Р㪫;BF\~N*;KCE8 "szFFⱎ1P4/?Em`X{2v֦m9p%Ī(XХ VEM&Hڅ[~Eb[v6KR %`'d4 ugXz0N}k}4E3jc "Y|N7>D)>/O!V7v)(;f pt} 52*>nѬh[|QnI#s(Qv!D|. ES] -C츢NdpHM_Ǣԝ7>ad̢l~M .R)kZQ>?0%kU&76)>a,=*4Ϫ֠S"ƁN-t_Y EeaϜjV7% 7*AP;[T;W*dr 1U]f~}`8WKN1-H.g k ,ui`gKz~cR^ѐxt1^?gB:$[rYu{BipТzSFVaP7VOdIV}yvߟU!Wz{BՖ淃~Y3.9׻:Nӻ\'Ikk:teE} sjEP$ǬU+-(y4K&[ؗW:pT ok]a *NP1Yƭ 5^ۯQq}6חbSL0{k5H g%EwW=>\Q;WaG̢u+L1X2[^5N֫~b9S`j4rXanfk痫1ÞlG[* jE])* ׳?]tuyDIC>s3Kŗ#pԏ"!Id=s`}JLP,vTf<6?5CY; }SaASi ~i쳗IG: v[$lm4FAY^c&9N4V'iJ$TVˡbj?ovRiu cdܼ=`cLVL%KƲе)E̩dVc MSqEf)bAx"F9:4U"NXψucqLdfqNp19D<9a+ 4qMn;)D "KT.I%)4c[7Vr-+[c3=wYCp8;3 6jȂ]^^sܫNԙeY&tbd|y6=srτ{5Ϛ r!/I|'v2e*dItPKo5D; Blink-SMD.inoN0<ōiU5QvHٍrYwG ^}ypѼ^x !Zr)!#:eσSV>-3=O[!DᨱI@yeaa b1( _jH| 4X iv˔u.=~Ԩ%cg=wNh$7\ȰͫzgIKH_nIn7 dY׮(yNn\vWK{WeB۠HC?8mz=[RD^Ktvۤ6%& ӘivlDng3%.?xj|}/g[K:=#v8= D˝ve:k/nKP.lGu67.e>Qr=$ݒ ?x]vbsQt(ۋO4v!_*B<_>Q.dļ(TΔwRzKn[SJ<Aqka8,S+t&pp{_XWA7Kƃ54q$l4uuiܳ ?Zer}XK`EJLDݒO[n!bZFKz"]UU@u,$M8LwTUP}Ec[J凬_.K}׋xj\޸T=,$UifNݠ{(; XkaFK,o Wj 46-oP7 Zمv z7TgEz82 |gbg~Ҡn:rEXwid]^CĠV2 ৉#| O뱟&%$ /Ha&JAAڈt?=Nl;4^(+1evq=ߋ(0%bwQfCFLC4;z!cA$1C@M zӊ !)V p`Ov#9w! ʻ̴|ש Ru#NIv nk7q@v+wϚ[ys>Y Nͥ7 0EjVi]yIV*a;59e}l]t,w؈dQ(=vH&6 7nJQAhI M),McfA"W"M5TY };T_ZxH 8ǽVwGq/6߁vUCQ/@cokC282fLk,ɗPCe.^/z^SԵ%4m 8d:R͉+f:B@Z> IܡowWh35~K!NyZUAB/~|`_KuQSiMH;ŷl&/AIVA0_ hʻ6*vAѣ9fb0kXC2802헹-dm&v>%vʶlKp e6MVTk_ӀZbTpaAOt:xЁhnKŎ[ZT;zlofXV,KPhEݱx,C47[e 3LE>T"Hr|>f>!&6ڢ"/a9ѭWBliQc+jHOƠfm=S@ EkX^]LhE]R.aAd"l9@4mdb][BnЁV+ʣ:> (627+D؆(~Mb&c@Ķ'K`g ƁV:XөQG9l./"ip%֭ۍ-N`Dj@|yӚ֗ΘMF~ aI]MlzV_JM_ґ(鹢\\]H_/%A_*-en'T@PPH Fݣx{Gk#@y$N fsP0p7Ypf%]?}Tm[ 7f\SV4-N,U$[:X?[R?z*<=dr8SJܾ}Ir";vƼEXI>wSEZ>cl[{!.߲U cنno U\JSx&Fp1&u"x1ω@֭cBID2'UrUD9yuFXC@_}o\[D0_2x:!pe޲J#]4;v0K377譡FxS.d|v6ggg[wYwuB,y\RM &_ĪQ&c=2+nT\K-d]ZQ踦Z2zn2SMoWhbܙ`mieM4Y4oM{|OAUQ\ T͐MT2}+4S+{8覙J8:2S#cLQ)HRo,]wM`SiY ׮ճA*HxHlQR5\ ผ5en ?Q9I,N߹M-<2ѲWN@o"?Z#|3J)+ڻ8!n~Bivy.;{ 'qɩQ{3tN"ɉӭ6PKo5D; Blink.inoN0<ōiU5QvHٍrYwG ^}ypѼ^x !Zr)!#:eσSV>-3=O[!DᨱI@yeaa b1( _jH| 4X iv˔u.=~Ԩ%cg=wNh$7\ȰͫzgIm>is7M01bo=FLd2}ǑdCL-yUiѷ7c!t"mq#ǙKpw&SKL4٦miP&ҙ¨XA(%}vPq P@i`,Z'0e,]z$11,; +0 zONT@QÊ.A @s+eXcXCy^i1඙ #Y*N0#ʌ;U0ǎ H: +1]t,`1NJq*dɞtI$Pbs!$[,ST5C9T@߂I׳Ip|g^nCYQ*jp@ -_e#*PcALLᗓ᪣XwŇ{hb@Ŋw%U_Fo[ұL}G V8l6-F {>U<=bFm\2@p-FxP4 n0U%֎)CEEwD+dcT`rjkjee 4 &pNUGwt*iax9+ ;*/(}1>> QʂD}l&%Uʠ ǸS*L܉doӉS@OԥJc (@#( uS`N@!B%Yw@ȓnS{<ʒ'W4&OF={_>ڿ>{۫o^0] =7`Z&__ݜ[F{ޓoۏN6B)|3q;~*דSs\/Mӵmh޷h+ FnWg|0yN-`ϹЖ+.k3Mno݊oU]<}A0usz>sa!/ȃ3L 񦉼ۅX ӄ:&3-x-έ8{7L:W}IEF8޸75n9wiCmy܋\M_;cM3Cm<̼\{OⰫ}}-[m.=qi7jӕ]hฃE W~/E??bWA B?h5`)(h6 o蛜iɷ4V *Sj׃4)tJ/ rvX<<)sff{i udPsl"j$75!c9E[Hyg':N֣0I`hHPD1qҞldq*(P*VPk:Vc3(ȸD4f*::؍NdP0zEGZg|Fd'86uH ,{#:U- s *%kqИi#S')6J8"N,#2u*Nxp݂U'.ZrypA޶FdYU˹w­BLVDtf0A%J}kԪZSJ݇ZI;>3Xi3={^#cX`ιFL6I$DU#WM!u?>ܳbV{.@4FNWpUMJ) h|f*ezz?˨p8>闧ƮML JƎtDqcJ3;A;bBr7𧒰˳+ڐ!paTM*tYZ&@*ͲTv Npm*)v ]UAX׋r85MJ*HTM? !;G|ЏUu7ͨ McT9MeT0aBAwEMAWF~_L ~aGQ׌OR31ݐokg=vNu׋ >s'/1b~ ٤CFC Y!|ʌ SR0@fؘ8Q!F]ѴW~%+ l&m/AWbz0gQ E|{+|mśPJW%aEQ_is<SQ#K)G J>Pnbuݑ@K#6.(;JÿLgH_j-gj`3b<fߝ˞daq䧧}g72GwBϊ_?۵I"Dˇ"?2hsO#H$6M7̝]{w7*GD 4jq m/ĹdN pƇ\ގsDg*PQ 4b79&ᬥN.=ÙmpdRĭs6=L຾FQ.=E7\| 2z⁏$j{ :>麫f٫)Tۑ0w]glϿ|~ϲ aޯf1o6$ A9{GNBFN%KtWDu%Bd0%~3*{{[!Y7꫱G\a~enCyTY%WXŕJ2( gڙQ|*|=!N@"11UjGk@6W_@<p씍J:jV~%d\ꭁSd\ҍVSBe&r ױ'O ͫN/0ۜ2_(5]4GCսn[fjU乖4(OMyrVƜ 'a-pPY"sD}m]xw̹ލ&g wbܘnhwF*gla=mrl;粷9=p~mQ_-J $4Qxi0ĵYxrjk;N82z`?&i"e ی(Ή ? <6`!ce͝I KqlT'O]ki0WWI62x`17U1;G tN11*f:L<bܢ,k=x^F )ߟU~ゑ9 ˦;}|ZA8sivUxՀNuzb yb01b]1:AtDG61 ;a:sŴNFG&"e#*JL֧ΩdAI7v3ЩLk%@b'r17 *]rZ9k)m̵֎IXtISd ->NN! K[}P\2B1$uF7a鴿Μ?ܨ^ug?ףex 35/~&!~ݻIp<|wg}%uݒ< Öl_-AH ,Be #â4V8;*!'x,. RYޭj\Bn %U5AXVj|4Z-x5s%/9f #PPGTXM"(k-q )Tv$)`Т6n4K%2Dgn#!D  :#T98ĸ꼪] [.>pVM&>̯ѪJe [^L0mZ%*˻F}K(q8z(V ǸU\uqo<ΪJ>VU0cUMM'tYf#1l-mz| `˳*(Qku\06cH)?D#Vתh0 LUp|H\}[Seǂt2qN2M;M6=]gCBE}J ȡ, #lRj$ܷ2YHeHRTt  ;$ 4{ 3mGr*V$SeEQ\u+ Wa!۷.mq`dU;"R#U5,0=.l%0I* §Q9 (~PM(6\H_<-?MDBLtA`<*;cR)~P/@SI僧Jt;C&%ZضeE@JJoz#ԑq)(t?0qܦB%9q)s7jbxs7%Ts~&ۗ0{Өj86M ?nv/:ԋ_oZOpD?{n}qV½|J~9h1˧mz'~.//Wkme۫owv5\>|I# ~=zb0xxx@ -VOr:@6 Aco4љC ]_>~=߿(+k|@[˯)q_'<Y}q7?޽O,;m. /Q_2x{Ij[ZoNf8sZܻ㰵++"jRmb[`aزTS|Y4v6߈2,pyswc3"Tt/7F}!)~=6xX)Glh Sٶ*^,c.N_#++lzF2 Yl0ߵw g#Fi_ގ~ȲͩȚ'b#/K$AQB_:dO?|#$Tbٽ9PχkOޑE/e8DQ)6@ĩ67~h?: ~(鬮]Qk#rϦ,if5d@&6F_3*W-6=zCS>38+O-T[-BdV֬trۗ+]ە;C.mSAл1\hJwn#Ldd^ĩu8UnӮ{)?my5n1@X#%JSWpg dQn7e`:7†Pߢh=Y r&{9 Nj|?s7z/_{xwY_ٗѷPKo5D&KUButton-SMD.ino}O0W-K: 4h+M,&فk>$GFk@lUV ZjDmj(-l 5FJ@KktpFz %t5 $,D 1hS\v9YX֓M.:>i ȑvhJ#WEs KNkc`zځl{%{+XBz +{I veG0wv&W >6]:'AK]ifpkMA7'U1:eR%/Z]@iR`bS%E*=Ee>T4m;C(ij( _eS6~PE #kzyXLGoV"mZUF5eox'd?+Ic-ZEb'R }AE'͇y }7_h8v;'}'f>z@f/7H8m%Y/]V/#noE(~~T VinoV *=;zƝhIxE?E w2~>5nP?u*loSU[b0B<PKo5D}٢eS). Button-SMD.fzPKo5D&KU~Button-SMD.inoPKw. fritzing-app-0.9.3b/sketches/core/obsolete/Chaserlights.fzz000066400000000000000000000423071272363601300240400ustar00rootroot00000000000000PKo5D'zGDChaserlights.fzm۸vdr"x'vNy~w7ˇ; kp )^jy|ޭnU~LW}evk]i ?V׿_?,A߯n~zZnOut˗M5kZdC˶{.tPDKayBZrNHJz1E|)bf&o^}9߮o><.vś7oY<߾[ܬ7Ұݗ竗&UN~XZo3jgןl[~'/׏L 91_|ChG@x"&ՐYI;luZ'Հ :-jt'kh\2(XO: ,/Nc cY{L=E|T.#wgJE8ޙ&0 S3 &Оn([ etu4‚ִ7̖a9Iuuv(GZO (CrVKBYTw%^wPVx>dѪ KVw`֋xR@cSa(] 1'SG.2_֦hx2v[eUm&^4z&^4 |Yjuq8kqej=_֟θBi^t֝N0K!}ρ 9( vٗ=03@m0k}DR܆Rk%4~ KK3H}g?8Yቭ^g(=Ygj Jӱ:C( } 2Os8wa8!'N̂OVtLzJS4>32d 8{YLTOAoϾC:AU:A/QkD:HE\sϗu;CsPڳoa.sթqIGw`Jh(aejJ!_VV8U~wpw_fjnUjG^Wvwt̼wx;ay' -U׻^lrx춺g]}y. Oԑ'w;6bxVmdh(ֹvtrgJ,Ҥ#q$E)h((ֹ^L eQ4c(4N}+XnW[]"Zzr]{v}q͛76ݗ/ϷR?C7k-ٻϳa1u}~/q,w]Ki*^Ѧu,|^_dnIz-oRE1b*ݤ$blxi{<qo(#!yQb^GBP!H,j@ ^=Α䪔=`Nbm^-jj_]vD>7E}naI3V9?.jt?jr,n]3ܭ:u EM s,bdbb#VbP&̫ #~0Rߑw&$#n-?L­wf(J#B$DrňCL(2Sl]m짥}1}r@WG찈&;;uXHgrh>%q 4bZXl8)u}WIz я%DG8$B Iu@(>! .]eDz&??{noԵG^ha;,"UM+j[(6sxs>7|L:?۾nr4pyay;?g} dy}Hm7zYOŇsKtH":|? +Xۖ@r ta$NZY̡v3ֺYPc\~}㎂<'F Κ@UFiTY+x$շ;Zko7ֹP|uGq)Iyhaƃ|ꢧB jlӥ-y}-wǜWLfi2.w0 M9Rf"Y/&4[݇IF[6ǠUa2+np]nxu+r,xVmc$Hn*@qt SHNwZgQBVMq'iu{z' t ntnMU6Ktq=oHޡ[͎;8EO+MX*y0vH0}MR̘>GQ64l=c(Lj~n xx9(L:[p;Ґق 8t̯,!άʯޒ z1 e{ mW<1ӛR]F.MoXʞ3L~]L(AC0]]TH K7쀎7d.ϱV| v097YMt+8\a4)ʙD~l1R2{ &$B׼:|7naW7]RNH7B9ʁͭQZ.nQo8s `8W=];9\l%)9Ҩܥs|_gJ88d`@g#ʎ dhγ(qT3lvƳT(Zr1k_Yn@vRƼ֝ݒ>b6 >Q ˗2qx,h:mh?Xzmxeuz"O@1GTмQn&nRu&%T\@3uE^7,.^pp@d$l^7 ^w'j)^#-(uOwn{45Ozv7evwSe`v?i:mX򐭰fn6d:!N ddv1}c 'Ќn=;@h(Ǜ#*[:ҁMGT9y*8ǒ#n8#up-7 M.vv,i}pݝ@W÷M`wc,OI=״95m{v a߳[!s ֦tyXiIhFpBECYf`uYVD8T`=npw]U_@ݒA Z݇m;ʬ6Kڧnuu Vwa&)GV>juY=-I9N31 'QB>wZhg*PPVrVâ1676uE˜6D"ү!k]o֬RJ<5짴'DNjMιuSlzz#)xQ+s}҃ݵm97G[f~;q!:kcL֣IO9:O*q,B- |nͫK-MND¬iX | S;_Ѿ2ε4E=jB R5?9)A kNZ9d|',mG m"XP(5@,47'#%c(˃{ɞHj , J*Lv/<*dXikqR f(rYIg U-E(닖S~$_zU ߁%WdȒ@JFlH]|U|ux'xhXN0xT KkQ_c. ,y%˞׬%KIJȒ1;Y2ɒ Y@ âSBE@+hId%5lPTJ?`( hrmhĜBE2e簲:Z+ U ?eXnC֢]&͔eJa0+)HQڧJH;zDփ'mL<Z#h*७1`%b}{$u< 1J,7C#YCL U _BX8%,(uQ 3fNwnx8aE'"Un3k(OCXSJaL'9 'ZHI8j=^ e-:"oE ')~>|:v+K4)0%̀a!IF"X;I 92[c(>F:0%0X!Q@11g;J(mpZ}v0JYH$XJ7ZN4P5ğ`5AbD0 Ca֢s4PUï(QL 4L fhL]vt\ 4` e-!'CT(5p0KګI_5e)6ETIpy`/f)G,gs$6XE1(CS v) Qq0N#qHDb֢3@г0a4UERk}o@0KѶ Oh` |/`m~En ^r`a/ޘp#%9dfaR .k./N)f_II] e):SEጆ/A*2~!ond؆hLR3qɩ/5V1 uO2&#a֢]^0Uг0Dal WX(!U<0݀J x!~VC fe01r#\GCuRgq )~K~]:0 _ND%Eևm'ӉpFMBE 28a +-> T7r\pLF|X$2 L[qv&bيvHALfƯWUxÝ" F6 r-RX{&@hP+`tc ROs0R.VUJ dSH0սa.*zkͫK8k Qϫ'phuнdnϦǽ02@ʂ0rD(+o1TD9~ӈVkm <] .{mT(Z/Gek]NW@ZfV Ș4#B"EaQT(=#H3,m GfzhA`zmS J_ΦR'uL'9q`Ȭ 2F~\gwi$ЬP 0ucJQ(v5 e@܇-eҪa֢0lia4`X:"I e)Jt"^WMM@`I#z}l֢ rL)`J<+0 {q\y Ժl~NQ0kѪ'J{cMt^MoO| f)Z%!![_Pv( ӐE &E0͇hdĞF  EUdk5](1 a)XIխNodki!T2k;ZYGvzZXR#@5ğpۏi +~G BۏU F9=(Ln>~v.^~\||<͛w~qww)?߾[zΣeuRjbαbHtolmOۻqSM}$GL=c8H+TW(9Jb8}H15a- / H訸>Dz[:zaI&GZ^uu2ѠF VZL'A)HCu^4(\IFwKڠbP JLA5 s&݈ Jcwy n8bQ+Ӆݍ 94(U[Ǜנ Gհݨ#-mP-xoP&HyڳK0$~]Kgn ˑwM`hU ~z~}1!l'Lf{4-̱H^AW)wV_«nixJs5ڮ=,SP(wrgؘJ0Ogbt-q>pqyR%C#)h"2۝w"y B{""74O4q6I8=z}ĂwOq1w 8NI=Ξ-)lbH9&8Cܷ!TtA'72d(!(o=BɉB|*K&[2Q'c]?lSOfNb(XyZ5ce^fSd \?_ǯ0$S@O2l)Y[Q3Kk[ԙ,k+}:im+:t":oY[QsH[$?{쭨uQZQv=[A2Q" y^Wy*cAkdPZQ0v e`lr;fnLN]}^v8M ;+3*#ڳ=xzÖr5ݼ3AP6}`T^v\/–{E{ g}ᔢ]6,@vAZ9V/y Ӯ(ICI̡TQ~6 Lw hpv/#N:PMD:&J".B7" 88f+ڼGu!Aϣ4pQ,Bs^Nf!1¬E3a@ )P2RP]ӏ0[5J3~h0V4`(ыDQRWW'kܟr*TţWP^U*E-^يgk{yxE/+ʇ+Z/xe-:qWcm4!7%xUGl]/ $JԈUE+[Q#lm/ƀ|%|tJUR+k ս` ˔K糁H{"y|tIi6 HzD'L8®(0mۇo67jL=̎Q(uQ?jيv S>&e@90fqE+憠0~_!Oyѓ#qY/rV,'1ahӣ*X2=uZLRyYCk0E%hi/2hj!yL c7{(zxy/3?\pv*#gפ~vfL=̎(mUQ=gيyG5:d,f,tyX2"4Edwj8To{U .DB*鼟EX:yZpix2満 ǔ:^:jV{ L)5<e04T1e&^ʹ׌|kk<1N8®9(Z2:K_4З4Dcav @yvXVu tL<ʮ%g +Q6pc592̘E`8H7@ s`feezvLʌ~)S9D<r":JH@[rn0%dJ>HȐ< `>&:eUFL/%)`<0EN)bZWyANÑPnL)g)&RADG pj+N*Ȕ}BQpugmAC!x, >%`y(ؠV@A r(h0v 0cJ>c *5DtԐ= L RA(2$J88@r8H\⠐eũ9*-3Z튃̍8Xx+/qF0d`8@cJ>#l!펃@5dJ>hȀ8Zu uhTPf*2FP2 A lW?,i?82HQ0TƔ(CQ?ڊv@1h7@01$R8@rȜh@Sd  I/ Zv'A1hC@Ղv"IHA[0TBs` >]r0x͵0G eQ0hYNqU>`вV>```ʼ gCA1hAP QCڊvA2hP4dH u!p}GwIqө1zE^,$>Ҕl]bu4&MjBRJ4ZAҌ8lνwΦ E7A=DGTj+ڜ&"Ev H6C)G|y!#<ȟ32Ң|xħ"l,ӔڟV=@ F7_A=DGg+ښE_QCJS+Y#G3ڠ6^!kDΣR rRhvdgc|@Uó0wTtQ@ي>Wǀ'"x7Dr:"`5 X&o?fjVanF64JQ<(N;R_YN*3 F9\FU?DGg+ʇ_QCBGSTD8.$h$k([⿱f? քevz\CjTm&PEó0QW'PtQ?ي&Wǐp!? $"_ŧB #08"H3\៑m~2kg{w*h-ʆga m@į(я!6C)!#JHT(2QcDȪlV\(BhdU4< 3pCtԏ|e01 fut`9 ? S0@_;8mdNa>-NYH ( lEP+`c81 uP\|ds>lD?eU GK(G팿0Bo2&#i(:ja `ee sRkj%`GD$PhZODC&3hp4(1)KɠF5 Sf2ge B:!HVl"C3XmTX4b-5#E!A˯:`2d3J֞aF4^]#=&#SvFG*x HlؓHRJ B"NXW.ؕkjvRN64M@#ec7%Jg wR1wɪL5{F=Hɗ(_6mE;fU@kJhCW;~:eKz""+; 8=N8kw p;~B>Q +X 9[%_RhO{!TR:r:託Uem7>޾BmNN)gcĎra,MggWX׽tz fN"pWv{~ބO*3Dn1h pn:":H`[Ȕ]Fph/04NY4dN3ehHR8RRܭ @X^4!%IS z<>ĵh5a ^DLo0p%qaEîj":IC[@BQ"5) uQr{ $JZ^4:-6nvU[8풭rHscsPR(m=!@hg(]joTxdG(O[mE;oU{C5  ElӐqur6wyNi2r@֋f7VEateȈח;$!adHfrNr0[ ЊhCG,ULDG1 (j+:`aUɀ3Hrr)g)H^+$U*R61~:[̨W1h7HQ GV;D'i+= T;+)*!j bbThL>oX6OyS!)\v fF$y =ggi֔}N&gݪ!rżKtԯυ_ОPkHV{:!it{46W#[gh PFga$3?|R1bL)MPdQ<}ڊvO1h7ȾBю!p.4Nu +o42li9/oo?]0>=`gh23PvQNڊ>L>F ȉBAD1=rCy>S]`kS Υ=hT ,)P0[ \M6ɋB,G܃rV;\Sx$eא 0);DrtW $eAbS: U ju1[Ȣ$ Ov8!٣ipT}A`ߓx&P"P'' N0[k*Pd@N4_'Q'J mðyn0~aȒڪ2|4#FJ'qR<̨YR1h7HZW]HDG! h+=i TH" i}}S{W|=oz<{~\yu{5xai$&,\i֬|>=>>.7WE_͖O7>7~c.Ͷg7WbgwWwLzs~s~u] SP_1~/߇7F4"%œqz?<ڿsK]OJ?UѦ ~>`p$~37Z?iX-btta9w}fvOӬG>ƻ|13OۛrT,44f?{>T15H1~.tdL'M^Vט".?\"?)‚*ߧWvca`H_\Ox^vb(_Č#CR~_$u CW9Ç^=@_(F2bëDoFb]w~xE/GlNWK*b|{yxKűU51ƅDQ&`kFLTb Kᅮa_L _/yx>8Fz{x1kOJMA$~M M58?>nV\?J^0qz%IFpsŇ ` [ *bLmLh0jGQFJ=n^zYݯ STS஺H,}rwJ jĊPf(Zh栵~ky|QU(ӂҪZ%3TAg[Q]C]ΤcJ|y ZPKo5D'zGDChaserlights.fzPK=tDfritzing-app-0.9.3b/sketches/core/obsolete/Curvy-Parking-Assistant.fzz000066400000000000000000000651241272363601300260720ustar00rootroot00000000000000PKp5DKViV. Curvy-Parking-Assistant.fzmsȑ| 9}nX3=1;{{8()qM\5#4 ݨBD/3+Ǜ͗w]?=>| #[  k տ??=?~|Wxǫ盿f~ ^߯?\m_[}+__V>뻇?Ȭ7JW{mWo{{n5UJRUFYWOW߼˿dLJWo^>]=o^.חwW<=?~p3j?|_~?\חŏ5szuu֏֛MH^l탤ȌW 1}Web]ۛo^_9OuB!ƌ'Rs߯__x#zr~vt7 #$+>ʾ ~K=_d-c=|?cpwϏwxB 3xmM{C&%Z2Q42}TZOOFp{$wS-5*SMŇ`8Ƨov3<ՎO˼aVETrAo}Yw[(dt!vI-n[|Nȝn-x7w}|KG-Y4맻73K#SDH6§?޾75b2ZfhuTqAu>=OO뛻g ?erhhҴ8am47} 4ܵNt}]>`pQ_=nV99YAҧiU-Px=+Vp{Ȋ.(&:-a<6`hhQohQx}ط\~c%R[fVr~uȪFF_w}G`_wu?IqWY$T"+DBo< Ђ;k˷~;.֒Eܻ7 3 _MfvMw!Ҙmܨa_ʿY̫} ?M7S֒IEcYZGl֊%&Px:CP uXPdiG`b6skH.MogPz;/-qhg0%vnV\Q AlrKwwxO`3,Y[3ю3,@<ϳPRhEkkGsT1μm˲3.?5bŬmsvv*a翝 q鿝 P55h[;@v6[8 "c3X Y6v*~Y J!I1k!XИ}wG;;5vFώ }6j}v3og7;C6j}v3J -{Lng ݡۨu߀?GvFoJW[_cM:@omq-G|aRwUvMmNm؞tj6a6.XH~H_K mh0[Qmz%\HޛFwXk.ZBtV뼑!,Z!,Z/jAfS]^|N{saX:2c缐.jy!=".Zl(a[J#/wZ Q8Kъg)-@y)BK+ p(͵f6ih۵i-i5 ;ZeKc"(NKiy'CpZJ_N#C,)ZD\4y)@W.ȥ4B].h sKBB"&FFNBxo)=@2-Y&Cpbd : tIR^7aWI{;Ǚ Rus0 ,ِx^gFt; wgj0[QA|M\HϳFt=븣T.Y B:>빙!,Y!T,hhٌ@}>\q_ðuun.Oz!}wBE$]H?>P㶔h}1%f^HO,+ZD;;D R:7Rz :- [/?pXJ!(G.iV`tZJlNi}5WZeK$(uQKs7CZJ3C,cZDd3y)]Ӏ׬.ojʥtN^MiK=g L˜BL#HNfN=xE(p)}Ԁ?-Z6Cp@ff-@M x1R(VVO*z}yz^W~Qg׿W?=_WOW߼yzrtyɦ9~yj|ѯ7w7"//hxξ`g8;z}?}NÐ&[f?DyEAn;jo>Q+7.uxm{i|h|q}M8Ɇpwb-Ȇ6a]ϓݺi/~>x`$K_52fJ"K:^_MW .CN7}g}LJSvѴhdz _6!_ecF#ĿȱAƘ, ]EˡD&8BBԤKp0֡bZ$a4įuV<UD_0$/Q7^(Hp"|̡-Ǐ?'/*ӆߐE!ɠGW>!Å|,ż|Y"m=U{}z^݇6?\i崱LU R$-Q9F yDfyl^G P;y~===l<\wwqIc(_W$q=Q oWg/1u3;bpʝBBڧhd1Ұ ~+'KY`BruyN{H8] {VnY1>dC ysît)!m昇Lg!yuK~$gl脘1%yg+tϹ1ٓ'L<>>l|e=ɯ.6OʰmOw/woo?2*p{dO΃(yk*ӓ}g ?e DBGovƈ.otv穧t͕j47Gm 3czZ?.NlW}V^ 7}%@>CF 4.}u*>;h5z.އ"(1h?_^y4kTsROϏO|p!R;?px7_tpbK^1U <ޯk#Du"˙藿0L}>IB,=h [dN@h^ߔؚ3Z#r%Jo4]Шqa)b6v]+1uMl={E'`F|2’{Ά//>nP wfQ4RZ}1% 8R02=+OBS7SD23&-Web1ʖͪt6TҿQ>ӕbG" Q*@JJ #)Ī>A[Q4l|̔:GϝkӌL )<&XRI(b*BB\PQv!k5ut D!- .J2ϕuVQ*m&zR؋Pij(þ''_Nۛ4GO،OOOp85ʑ{x%Ҕij ٍ%(?A1I |D@딠'(8tEY*ԗ!%+;c&4nMHA\ ]V)s:E-i|A=]wtm6IUE] K 1 lj CI]}Q }3hYJXzezOXX)7vWx @MXw',qmH9+qYWL 46:"_1)_dh4KgW0(&,d#aASИxM`0_+B *u#(A2{CTu0eұ.dxjybeGte+Ia(tEMLWHY^!PXtEE+v=KӬtɑŞ/+ۄytG HWeԵ–8um(0u(`0_36:&_164xZ~$|N )"t+HWuk(.LbLY278Xlqpvᣈ2.=3Qr}TOXpv]8&c1~14 9|Ğ`2ChՓ++Q(I >]LvTEg+vV?xU27[\D  U=+ FzhSֵlal0lP8 ήMHV\*Ven|{+ؘ+ӹ(nA„0&\,`(盩I\t09-UATf foŨ /''Ϥ Vʾ+,*=vܺ*s~IV1\"ΎYJVfsmuLY@B"\&+lEEh\EȮ+HRvVӲn mtDbjpojp<VSf¨ L;+T=YqTaVVZB(NX  %ifg+42%"IjlBT"d* "扴i-eLvPV2gP ROT* Lj :%*:.m?N޽d'.-fSyoeم.}./y;/\@o>e+Kv'lŦ`̓_6Dzܻ|{x!?5k.\[1OBSZ!4 f9<-Zu 68nap0١R4lhu@V?© ŜN91ъ/B6O T\*mlLMVrD,{шbEW;6]S~ӧ=wK)haL"KŲXaEFlo #e /H؜51d{ UmmǙ[ &{.S#1V(p˝rQ ސ MhH4ٸtX+t^2P2ZG&%IA@4?]`HbIBdwY!)QdH-/#6DF@WU \ܯZ-!JȨE TTnDa6՘͡!cRHMeȚבbd3%dtbnj6챜SyX?sq^"HUj{`dIeS}[7ƱKDVnmqЕF98Nm5Bpx3Ҕ=֬pQ\aD0JU"?`_#`Ƈ[D0U iU#e8jT.6"&~pw`m]7Ũso[DbδOv"$ս;̢ HRڥ@$)-I1ʊFFQIRHw(ս@' R0"d"CL&TL0d1RgcrP Pdg7"QJE) 8@)j2m㤣!&XNN,56^KAkϦ8Kd)16/Ut"#%)7rHOb$@Jr.+,59T@";*R\PNtm.ɁDpP.<|2Tx$)헤쀤!)6jOR<\|L#Iu qIM@R='qIREZO6+enVRŨb00 NccFP ?LaTU.1nS&b[T('F~/}mDRv)Vi#Խ{G tlvuQSZ&GqU3Rw$DַVQ#U"T'L.Df(z  #!˒>AՁDhT5W ‘ɱw(2 T(GQ dseejJR$zM V0St448Ij}f-<)ƈFƖɞF EbKc',$u>t9a N'<; ऱ_p89f"-ϡߍ)IQu'!gh)uJF,Par{Ǻzq=PXGrz@>`)Pz([YS"9Qj)F2l'٠2C:^z[T$ec(0 :u 5W e@遊 E(;a627\!{2JH,Pav:^ vJ=z&ʧ)3N {=b$=TNAXN, QsQ595|C#*U{2ԉ/=`IJl Dfa"7,Χ*˳3NofA"Ȥ~.hYyY:qt}M=';Omt<{¸) V@ EZ!4 f٩gAX+ԂՉ`4,E(NEft*"#1:ZGTo> OlQdEˊqẒ9GD$ yqQ""PʙC4CZ~M;|h1>JtN{dc`FQ\#hM$@2I%Fd(F-eRkH\CC _T%Ao?>8rջ6zz~wW?=oޭ_]tuqU,V O`:6+%exɠ;4lSN8[L:>m$㑞 8 DObz\tahS?M<\UZhCgEpyva784ԏx`Fy sNgp56e"w zfSy7Iye 8n1n <t71IAL.)e&HFH$KLKOg2vME)Bw ;U.)USvbaV"\Lu. Q!1^9Tvߤ)bv"o0.(n-R*;>,U 9T=Y;&Uϥ ԊYR +ע4CVZM\bv"PWQJJf,W:*Xv/p2~> }V>}O~J6纹р<>W I۞p[7jW.c DlXٜ˳0ވ7eH$JToa MJ[\.Î\R不"(̈́3Tz\rQ$zP)%^,fe(R:D9)Щ҄0D9݉V!J"">Rd[.gg(ᏭXQl%`tks8k,I$V$t8;CI`Qr?hVK( 2V nL7[Id U*y E.<b;d0AnWIS$ 7{J%&s8;FEVġX.]X,Pbo2`)Uj=4V^x$+嗬fnGl+d oPERJs sI$'ZdYvxxP5jeƝvvK쪱0fc9`lU bJB%;xLLtu\,*1ٹep#Xi`5C. Q, 3hZJM}YbB]j\uOY^_8 Ԓ=J-*ɀ8Ş+4|&ɫS\] vQpLO]QDx\Z)yF WRWKc]aEHE. bFO#dʒhBTI}ո Wg`#0zJTuALUv@oŊhLzGtN- :'!U҅jxKǩU*HVԡ3VvPvZ M&ES/<# FM$6p]q zAlմXJ%QwaP65F]!@L4I#JP 8W! 01 E\){O0px z%$&쁜ԓU+EOEXjcu 㩶nhɰ`SZ"OI*Å JP]U7Vێ*EVn*H"$4ѵ*Q۫L0Nj[pjB(;C 8c<<(~,[b#* Z.¤ya`R٠:vMfө!a:d"]1?h])yDk-v\:,BTL)w@mOx3 Bq^3fZ&:+Z wd&Պ{2g041 F*dߣ?JX 2,S~>J{7D8a塀 y|V #XLhO L y2Y"% ELvuLh%.gqEnoqfi/|?w?o.Ww} xuf?~fӛV79|<]o/mZ{v;^=;{vY?l ^||~ʮ]^tw%WOWO__sGwYǛyuqnu{~ş^~u񛗻/Ӧw7_qo{L󻻇+>n6j?_nj?x?Wwٽ k?Sf?^=ŷ//wNVyr)ge~WO_$3=ޯ/nJ}<^dKŇÍ}ѓ_/ro?yXdJfUkA}Z.nSΤ[bcW!1IOYAO+\?/>j6r\HUȭG]qȡwÕ "bH-Sܭs*SJDOsN;n,v![wR;o)06+2[=A9Ƕīэf?'Ᾱyϓ2ГvdZYň)=h3"'uJK'׃TCyХѠR`Xdȅ(yJCL>'0!&coc"yoH@PHZ  V6Q^"J;:Sd;9^ Qo -G[KEXL g|R<6,uC(OS""/. CgMݹ6Rƽr)DHrȥv31jҕӋ|+vPFdgh0 0D(-}1U%@*_ %rJSue)=e poicgBT=^UW'}DG,"-;50RaGJ\`yNܘWz{J#.Δ8"AX9R)]H32Tʰi%νRnd: :ᅡahwvp&8rVN#mtI#,Xxl]k$uW"Y"-\RLvPz>KBJ5wDZQqERSޡkx^;(n}"*s^V4*aOuʋa1 ]{XYUĥ Jn1 K_,0:*֒-xMJ^0;0 (ٟFұkSxlq_u?cUEy"&;w`U#Fy>k aF=m]bUz9PC4ERH9 DEX.PchW"Hѥ౪DLNsoJ݉ Z#*y/хj^a EfH V/&P qT'}@%oX㵩ǃS1+MIQx$X%)&~0e)&c48q[ :Ԗ*z[,0ҍ]+MF:&jrGTH:JEBS-پ<b\iDDlhݜ (g2! DDnl2  Q-8GxT"ΫW?t xRNI/ Fh Xl.$c$1F_40SuʋCZP z] wG-&:/TˆtD)i򈥾s{ :iHbJD ]s)"\M:j֝ߍu$2PP)Z'Ѡv?ńf .˝\ ƃ~8??N78_|U߽}u=>d?z}|{xGrn~^e xz~V狇kdc?o.d}`2 ! .v1U& ŋ]vU<ɢ%[~ڡ~i{z{،Uo_NXe_fڔ#A1|Ḧ^yp{̈́TӎTN.#I bd[8RGmw<L*:|& |sA$H鲧} A*{/-//X J#^h>hP$̿D~<,MG^aЪSv|8_ͺRǵeVNAXLHpߎ(tyxr%+RFʥ 7VQʩuIn-fCl5-g; xS/iCɩ5(ĺA>&/l}_L y7a%>qUSTQdLGPgW `1hwP]͢Aɜ C4>gaLU1ghcΓ#b@J|ƜDƙA|s.,SpXmu1D5 tM|ͯqap4eqYTId}˞䇉˺I>h~[N-\ǟ/Т?t9q`SuSUT ha:xZ Y1k>z2AOY$XkI:@.z(1@%Ar{Țx%Ϳ'~\j'^QL.Wu򵬿JYbZ,e D=&B繜 '~f'^҆_L ց.kvm14p:9SlUGE82Z1p]M"O"Y_)=CFv}`ˍO?_nP^PVZA+n݀y/!c`.oחnW@k%'fzNgAͿ =O s}ο 6yZhmk voeOÍ3ł^u|U܁`:OU֓lpctOQ5xUxCӨt~,`9$wﳿah_`><g 9{=݆ss꩗V"0{K@+erJ+D /gaZ{h%26T_=6غ1aj%25 .hN3TgSPAu"eAw rOsƽZ::rwm5fD;s{P6:Y[M쭫*Օ Nng@ċ@kK0ue eE/8{wMRDmtu}ܻz ЌaL3̃kk%ƫwjllȁ-?[ bã349g`KGb$7$i51IJ?hT910 ?2/q) :kBs, i lZd&6#=O8w 'ŖF9ܮO҈*+XXL "Y၉![MX=0B2Ft "IOxS"ڥ V&BsN4[H8H{2"SGYeEAPvٹ1%"Y 6d-9@Kٹ$рoh%\vn4n^Pшʆ7{.BuA\FgV7e 9)u#E]q8P.ur eLJEX2X6h75Q+ BBNAM"&F\Py>R! WtK mʱXDd~C҉`ȵ BPp+ 48˴CVS\ rh2J~Z,b I0<–[g gTn΢H o18uƥ]嘀`-n0bIl@y$ߙqDY=+G, :dU(8dPBYrri,녢, ᑲg@(H>,:zHQcf,Q ͊oJ` 3MkSqDY= G, 2xU'0Ҭ8 dȍgȅ?Ƣuf,6H[F2ujT~۴^L&^@EW ѭ3zV+AQIsx$jJĂ"0KV j`UB"LeYẈFL;.E0iv"%+ ;RmdEbSU"tIGٙDhʄIV)ƜWz1eDLV@#Xnr!aFӆnratLIt{(i VϤ):VΒ}*A[@H)D2}*bZT*AUdTݓ L)=S!u31tPEXwP*(*9?TF\(S*O}VSR1OO%OAч< lTVhڇST NEW GݽB)Α[C+Fsa9&^&\}Z QBTX06,RJwz ߗLL'`RH2޷JJw ce"+-w @sGUJcE+x%^avWV9xu$1W!"TWP垺0|/`9/$`A ,*R %",牰Vw|u7eηJwBLHS8<T XV'f Լ Xpcry,, b,j1Ã!U.X +-ʆeѸ/{լoOgM% \p!\D+80ϟv1^P*~يyJgԛYh>&5# &{d"omh_ύvnD]?a[+0ʡGp E + d^m^_퐠cN"P*^a4ոdpd/&Gߑ`nnD-0#}(mQ|XC0lODU)؁DxRP(DE(i ZLNDE0<玆D!*[iqeQ|}G T[yBC*s%=WJ\"FM 3g|UOPU) PEWXaDzrȝp|}71ٍ(?3Ui}Obq$=\cN%ʓJmY>8 㲃Mt|y=OUPj\!O54DQd|>X;ɧObN_NQtB)8MN0S1:8FX ,N*ۣ~_dͬMXSITRI_+?t|{xؖ//z|~ppy|rVɪ|?ǟX7ck/}yL [pH"׬Ҽ,Q"Ol39aRp#rNHLLo'1a,aIPE'zaӡmqFG&>mr')v| cI@î;>b˺,[bۗF"s,/WE*B:ޞSf|uV)T7KEрV,֙¥˜4-q;-FQ7,M^3B(nWhʕ^r[$)pwFzF[4g*5e& `pC]׃ 8OuHٱWJ_ԕAXʽ3uG9uGh@kd"rp)Ɔ+iVx*ݤߐ?T! 0A+P%@U6.G.NƹM /=`*EĆ+DGDk\^z:\ȍ W2<• W\ql.D l :DR0LMŠ]^z2]*_2]9X ]^u PpU"0^,mKO+zj?3_)|E(bKXUE=yS~Y@V),Gmf^"YS%J@/NzW*JDh:Q%W W@"rcÕ p<ÕW0]o֚RI0R늡Ʉd+PG, V'*FV4eLl5[U W$BՉ*&d*dc+ JQ$y $ |s@j*6pdVV0S󹖈V' FV=D% V0u"bCÕ`uFDUNU0"rc T^XOϏo>_<\}X绛W?]JI]9̈7_~}z_i{/7_O/~Z?=>|+.׏7wJ_ O.oG?| ry?~uW?3wyzzwr]xi/^t!fs`vl/?>n>|7}uܾT"ӧW|SeZUC$Kk/-_wcm Z3?\Y|{d7>_G׬ޯWoMyCvbg kwwWOk^X^ti~}X..nmm~&1.W5^Q[k)[)B]\z}Oh`{+)l_a=9bLq4&1ޯџ0}G_u6!zO%de׈s⍾awo` zd_×?2$_ uܞ]&(ToCHҏ֩۟vǞwvbPKp5DKViV. Curvy-Parking-Assistant.fzPKHifritzing-app-0.9.3b/sketches/core/obsolete/EDGY-motor-control-stepper.fzz000066400000000000000000000444561272363601300264530ustar00rootroot00000000000000PKp5D6F1EDGY-motor-control-stepper.fzmsƱOTov\^\fzo퐿>-rY^ݬ7w?׻xyjܬ|Xbxyq5^n-~o_y~^g_}ov?Www_]R^x|޸ޭn|yqk|oXc*^)U޿Y=lnŵۅ]aˇdR؅|dv~xvo/v5^lO맧oַOۗvY^8|:la;7"{*<//ۋMq1_?'{q|J">ڟg?Z+Nfbǃ.?}goݼ3g &~}rkAda#y HLuzy7ˏ^~tKFɂ B 5僚u d')!][,$ȹ!5Z+4 9mWH:Fv>YMR%渄RD7V|KVM&QM䘑#=M&aM|OA[mtCV\hnld8NQ*r˧j@}Mk*ؿ5 a0.?95-Յo(c\"i,{Rm Cݘ |^<Yn`~%~aov\/U?l%us(~{ܑΪ4k hP=]fP] }ǩy-+7纐RH7v?t݃ $ZAZAZKf- ]u- ]u-͛RGуTPqv:ϵ4JF:c}ZRGɃ֥5Odnk)ΠAJ@OP)塔q(%p56;o6˗%n 6~C[j"v4ﰤܬpACA9i W %RE$||Y6 Zp45qÇښ.{]?Ϋ;)J./׷GWpnVK3tO(=qjQղo77Wvh[ǃNTuXܾBc%>kk/OqSfz& RqV@p&+|IURt2۱VLJSWc6sy,hɑ 9zx{K_HR!Mf#?fȱc}_Uȱ\YlewP8q4tbuR1D(rn-C+N,>gnT|LnT FvP(ɹ1zč +9ߩEs - c6D܋ gHEs!; =yL>s~-e I\jaiO-EK[idmđ ƮđbdQĒGIrsfIMF^ `VDaWaZarI0{ĺÑWtnaҽy1ĒG`W< WfI+Ҩv,TXZbI xcEbPYSbIEϚ%= `5,%%q,_YFno{p>K$eKMG`Wy =hT%@;D"{%z ^7vZ$z Ԋ{4%xZD`:QQZpˆۑg8z4/7B_)s !:r=0z: bIt'txs&B U`(I]ߢ6\wdA]RÞ$$"ɨcj2,]9QwhP zՎ8La1]1GØkZ%I!U(=PX*|C#isP(q;41nc$zVITEMBaC6@HHDDB޻ L0)7aS}F1"bRrHb[HmW0BaPcj =r i(;]9R.g2ĠX$$fIsM 0u0&SwE0 4`-*WxzA0`bWoH1+zAQЋ%wo׃r yxto YSR>rݶ|_?_?l.~^n^V맯 q|]߭~B~Eo7/??~_}}뫫o~?~{Awo~ C۫o7V/Wvhte_tt1w ;~"랝?~w7JD~{Ⱦo^.w?ۛ|}7/1F_9[f)pA_X]ҿ_^_o؍Y>]<,7׷~Yۍ~ߠ8X™jÿ_n7SundXa_=Mnb_>O]XKi%^PD##&Q,nnd/_w|pն/#ה#3_R3,L:/ Fw rgQﲟ$w ?AIח?j;!@%dӈscB7~U])V뗭5v_îrxtɪ7'!W7{\vxbeeه0}ݦJ幑 ]Ib[fd="]SPu}c%\B$=!/'abuMп0ͷViWg䬁 J/ G(sSCf-eJϫ'B->q"֕Q{1_۷WޔL[JC)Iۖb-E+?Zjv%+C"x&j8;(]Rʸ+g@)G@ 5eLZ*or@@i)RhEp9|UٻH3C5uĄCuա߰CCJ$X@ʁ$, bv^1j3eĔޡ3֋]\Li0&2%(%20~d”]{*B$x1bdhT'lL೹~~|$7x_P9P%3 ֮Szce<ޡ3V]7Vj띾 )ʖ{s# Bk#yْ|I>&ҿYn.޿m1'f'j$eb5b"HTXcA+O/#!aI~H푰)? }@RW葹@\@1z^g>5w; o? 7jUrF{&J>{KDJu;%zZgkw-)ݮ['Q$b*<sGKݯ_}Zfׇ]am] $]-Yns%2tWq|.._K{߭^)x}-Ϸw]LtwjҔdsRw <ݚ_jmuZ om!lӷ)[ԨFTt#SiVV#a 仭vN_<Yn`})KBXʀ$LnTۺt)}_mgr07:G / AC+e6d\=RW,8XjDȲm]:o)թahh_q3Kp+xTS@L2(gzZDTmf=N_G"vyFrԡ8qXgmޥZa,XQrm}±Ю!p`.U;ʞ;4<^S\+@x=/KJg'> x ѠHF(w>YJ< E-Do748vTAb2vwL"a{'@0D5ַy)5f]*i*݆$F9`Zo*awh0MRgik}()9/0yh x ӠH90ͺ>5Z"l*»tGCP! if\H0 0+.iD)…ZW |Cmiq3C4:NOiIfyP-HGϤQN-ΊxPшحx(=rFt&PDgpJ "!G6RTRqF0!zCFlõ dl|p` \91`e*1PvN"\G1QTRAR ک3@ <}CshjP析 D]F].; ة}!QV޾b]h2lQųgٻoOHS,@Q ߲6oh4يIE?FqSC)^q3<(ySzSN>.ܮWYt)#{{S мC@b2vg@cx"cC@I-*'# 5G1`I|*13,{c{ZdٯfD;(I h=khLΟ눣Ժ"Tx Ms+sڮZ0+XdD`S0b26=5\ؙy2!L dxTbUK.{K~>] E`%\|ZJ]aѝ|*̙ ^7y?#sy xlsnF/37LD9TH+!s#D9cH+О[޻N6jbL>+5?+'ŘDX% &zĢ3Q,GB0T'P ظv#b&.@RaV(]s6ʶBXB#nOaGæ-)NŰZðZ <H V;4ĀjF< b@bb w:DӑH74Ā HL>ĀȈ$&%1 QwXg ֿJb0ph81c諣nտT¯ڡS.1RF.F".p₩1[nZEdgA!!:$4RT3, B N\%d?;3bMZ\B!q9r$(C"]9U-[6omՋ3c@qq.(B15o"Hڪglg[\Pc&ca@Z0wF$=IӃLшНA `MX!b S?b2 DCF16.Q V(8'X |CAl?_ұ~bE;r K'Ma@%#Cw0 r15"Ha@#&ca@4dD ba'kv<0GWMt)%}s>1j<*7zמN V?4`E<9CU+F/p1z]f\ K$ H(ٮT?ڊRbm#] 4G5G2jNFjxSD 6&.>bTAQ,NGB0vH=2EA0MtL  0bvl"}BDݿ}1)1zKh|ڈ}`C_@"C.ЬDWC |C#`@#&c.ĸ%.L db|CÉkgF\'O#p^<%s U,]CC fcΡ/ zy"H]0#&c# ݵVs82♯j}V" ~TD3A}>Wc_0#Cw/ j1@Cz|Cԏ}ѐ(Lq)L0I`ܮ^ 4llhycJR$ KMf8:\k&4*Gv c?qtz\ƨ`'!WT0(z15:"H ڮg2Sڇ¸BeD)*D֭!VޔVh}>|C` f}ٔa0 0Xj}a0;G!=`a0O0 2"{L(1T úфD!l*+% JaSm'RDS'#HP:y49Ռx ݙÀ鈡>9sP'p =&`BlXlݡO`0b76Dૣnzy4=} U)aWT@#oh8qԌȍ U7C-E-BZ;,`DaWտ}-`2Jur N Z=Tc'M\@e#C-1t4nPň5Kj[|\܂QN0C#-ps>ohӑWϼdZdVKQ 4X}jl4LeB(z"H.g2S&Bˌ]0f~"Ի\""ycD]Q.;L+q0DuqWvH:l\kHgr kGra*Tx }8 !ޮt5q5)1fqH%B7Lʛ[ĭ]=]eyz.ﯟ-7OOW߬o?.,oVO5?8Y9yP3@#Ϣ0HQ!5fX{qnMիM8Ŵ8"D =8Ձ&Y^yQuTr RJO;-y$k|Hs'pӹSFL S^ͭZ MnJbMrܡTtwUE{WմO)c;F@#=1? øu *b$nD'pӮH{0 eZwϲ8+#n352,gj䉾fS2#ῡPw9ftCn9,<>/#Z3u;#L.(Ԗ`a&_\<gY5#z`3I"&HҶN$iD-HZy$m jI$ >u! Rl[HPH"YH>g6Jۢ2E4 wGgf7u9$m_0PsHfN!i]FvB2e417 AҶdx9C^ }wB Ki៮z͎i>v: RƑ1yy*H'WzԝCiyDw5IO&Н@oq5fڧ4%PL. ;\ Jgv[{<13M@簮;Ԧ1w,u-kϑB5DlhY Pw MbtW[C8bm-tP1{l Fga25 Mq5vVÚ1}=8k:fDP1;Ԥ?CUc`Y#Z z@Y  y $g8n 7d aJI25B>(}LsqR%s).t\T KR5TX:ΐK 撮[)YicWeg1c3QjV甡4S~TB$; D$5ɻa3ILg[)'iȜ$9fQK6l+$#Pr6f{9% )<fSCʈKR֞K?( %}(VKݎe0׻쐁*(կq@g`g@I+sB tKՕzS璉4@= \G#_3Ho[Gcd%HyI:e&Mgk .LrKLT--0Kz&J{RR%CM:%O gh(3U璭ŚKؐrh撱璳4@Il+m\?|oe~s|zl_vءͽ &cնj0aUy틻Y1(ٷdk/<*]Gy(%k5w|.͵5]뇵8v]=ؙPwy~]mcfi|:A3ɳ',/e4իFVSKA._J=*-52[{x\j( †Ԑ;S2_.d)Eoo\l. bq`cC&wcG%6Nxtj'9n!A:ؔIAbwx$;;ow259)4wgef8W[]smMλmC<F`Rn(L/PDޭ|Gv3=\ }f!Ĺ:kHMlu ٦iӣ<*&@U,}4i1A9}!)FȲr?q +C /)K CGTx}͇l%cQ4kBLqskbI8v4qpRh H5ҍ~(NdX BQxܰ0*"ÍV̝C;X]bޡ}dB֍ =vb8#mlE&J2*f6s˞9X/d&fpc~k-SX '+OeP-yptcz Ò!]{a U"VD` CCa@#*k,1&q'J_Ye*(aIc`ݦ]ޡR7IC`dwȫtȕ‘B_ S0E1\|rFZ7oFȊOzAQ|L." :O.+1@ў"ɴpD_P^fJvL6)5dFz{U%D@11.A۝:lgQMAk:\,qֆ#59 Xj2S2tźst'(AXc {7ez[v\z\Ixo:z\TPS$.PWMD]/ϝ9\+AP/D'gW*y0T;1t8;E TU40Pl^$ |}B xa|ܖ_H/5ۓ %^ǽ8D1Y?ݨ٫hؕz{Azx,]韢^h DVN~a1  jGPڭUhȘ5nCWՃmB=#b v`'8熯nґWb/!/;(GƕB,ՈpZ] vd^PcLR_8yH({oHÎ VAL  b1ؘ} E$o~d2;3jqf`&4B"H]N<c@u#&ccPcDx\͕҃Vx%;~@A2%"9|_ aF%~Haهu4 à0BG$8,zs>'pާy0T -DM3|YLO4ݬWe~ENQuP}"@A4)M p!6E:F11(-Z<)]cDgp)cޡ2 s*&k9&60abeQOpz;V G`H W1o"Ee1{dIHO MdmL &9%"=ib"+!X{ |LLd!ݏcjGL>%JD?䱑%[HteJ&4 qM =e0c)ḃʬ:Bu| QRP;9QY`*!X{ ~L' sBD7t4) 1YPhͨh^Y\?\<}6=}vnޯv>C]|ښma ݬ/ۥ^@v|?wPKp5D~EDGY-motor-control-stepper.inomRM0WUIv9HPEvM&ѲBwNZRC8{o 592_5:{4Ѕ TԶt獶@Hɡv:th/a,1ȀEm̀4H-[A;qD\.NS*M镦(2+4 v7]"T"Y(VPR'3!n.Zϰc DAH]QkX1; mL~.nJF?mvA鐭$ \Jk`{,Tth7.O{eK:'BbsDYsÛ9g1J0F2x{/jzGE*Y%TX-/ȹTkvOf|Bqثe>e.@UΪ׼"~iysRQnʇ/#%`9-DGQpG<brzY99op!*9; ^_pe8jQNƾ!+d{PKp5D6F1EDGY-motor-control-stepper.fzPKp5D~>FEDGY-motor-control-stepper.inoPKHfritzing-app-0.9.3b/sketches/core/obsolete/EDGY-motor-control-two-motors.fzz000066400000000000000000000437161272363601300271210ustar00rootroot00000000000000PKp5DfET EDGY-motor-control-two-motors.fzmsƵέwV??$v&7V$RKq  O< \Eh4_s|}xX-w?Vv47KD(NYoݛ?an~͛_vWo>yxgMsyF֛7Ѿ٭w7,oMYC|>i5koH<߾_=.nia'_{y͉[vn>1;ۇUmVK}Ê狩x9ۛsܕry/7|O8kn7L ۍV_wqkgۻÛҝߟm_~ޯr~쟯◧nu}ӛf&Mݯ?8EblNR.uNxX~Z]gϽ[mWݧEozC)GF؋ƔZKl7[-}om[]z׶O[˦5--i[): Y-GxO W+w] G$#E篛]z%\_j}mA{][>Ƶbxx:3&FM$w&Idp2Ѿ$w&|u&5ı(kvB\ zgۓDH}eLZ a3Z@5r!Uʹ$5}KOj>ބ=sIfphB' 8]? fzi(Ejig4Fkg˨ Գmi3HdkRy RxKbg\Tߙg6yB&6 f3`JsUA( ,pzۧWB!;viÈID_$yr|=. I.#I#akj߿roP?o/% >Iw3niȏ}9}`3 & .%I|8FZ8?JFZoUw'{q}i'T{&__eu9kŌ=Z y ?I=D*z r ^>*ɀ|]z}`9vE1B0H1th`/fՁw.:9“VՁ+C\x; flz<,&3FY̦ ``^0 thPq1! #ޠ~%5jETpv7(#P2kn/d ї4kWMqe'?U+d-s4iЃـ>EC&WZ?-wj+$JP?NpZWj}i wKei0 Z_jK3Hؗ]̪/ xsc\;R # hz\:СMi`ƎjsHÇd4t2-̰Yyf4O ΘU_>@A:tF ԃmh 0 j`R Cf e3쐔BIѠP%}P%^]U]RPw׻v|5+Z*ȰTt05;)תo $/ uHzsEB=>rwx%@RѾ-7KԻ[np?gWQf}Zޮ\OY}^Е +\=p{FYj K0@$)ȟPAל~rgbQD;E*;I7hsp%kShR\?\iCt*J!P]@Z}Q*}Δ(\wW UՋTkJ}J\Щ y:eDp7O%F4K:IE5#tXsqp}b10&ɳ%KQKM"Rc_HsOGp,7&E L ah\@G&ѱI߲%i‰J%2wEtlazj._1\e]YA U6ű U9r켊c:_*W,Rpbq\*x8J]iAGIydhGpJt ЈsL>4\t=w:BSłc}DUjV9=RtjSD޺=ϫ~uܼ_bn޼&̻۟IS)EEP1DyFI1,Ey$UG@oP#}F\^JDrKS_lqpS#9wu_ )"P|7vٟɧRmez< evGlSZ%Gj # !Rt{qmS51`XCo}c vҨ"DǙ]Z߈DT˅ }Te+)%3D#JX2ӕдg9֞s4%3'|YY[#VP!uNg' hl 1feGz\_M,3Ud:+f] :u|i⇪"˃6zj*AWLgiShw1E9`>֋~6NSlD@cMAQs%0 "pI0? ^2n"4抸cRK 1rPy6~4L'YcR ӓEb&;Dݣ4*B4SB!sLTjJPdLtfLNU#ʼnQs}l^iDŠNF;ASV"Jb9Reww0S)cPЌ2N xPu|\}ID [cv>P H 7)Y]e3kdpIMM K Sì{aetOya-ՐpP) |L` >R,.$4f8La11%$V(Iu7E6W#zn_#sŒ 2p#c^i>4jT;熶KC)0RDԏ5}=]oFu+g@l0_^#}yxS&b$_:! #ajc]һ` Fq[`oA#gmn- #neqH2FSÐyF'#tsye?f_}}VI~,~Y?oV)#wd;BQ!_W{L2ܺ=mH&qLuƿ>-_wNdi(Qn.Z;{4Y"3#ytk#NkQ9Դw>jI5ArHq3k=LvxY8ܳMG^m$ xVi֗7GbS;~%QEXoc>g׏~<6#rh=fO/$U90R|W AdDSD- t| \*o邏0M&x8KN5 I>†v [4L=k?9T1IP20ݦAM*u6?|%xQ e" vkp/uܩALT`T"'ɂ*Ȗ'F/՘v^!4:RGz$Ք_xȁ5F3F3sڒ{늻 O3DoуȑZDbljI|H6뇃$l8P79onFi"ȌDcDWrc&H5X(Bب)o{/\(?fP]?-$ Jf5P!-LdˌaMRl5fD(y&P%- B4}%xy$hՀ<1v:Xo:ˌ !x8fL^(Rj 7R4J!˜h`@99c8Cpz5vN3R#8\Է9THd硸y·!dua8(a8(A]FOȍAx8&7X&Rj7R45!ոJFʩE@U4Vh ⲔiPyy&FLF+Erlς1(V*#;0iP\{~Zo~U.,d%Iq:Ǐ"?zL#kt8raqgEsBD6E @V<fǔz<9HWZ&O9%su!4%'+0% Fz}802Y /H{t$$lz>=|y X$ytE@%1GUX$Y0bv^.€uirh=SRh;Bt$`LƅV Iٍ]L{ؽN&2Mc_e-Gd%Hc=, -LOu^Eޱ)Վ U>UJuւ5X\HiFH^+FҌMV{wՒ~on$a\(4ҭ V_WÇwLbCi-45,1WPhSh,eMN^4Bciű\1Iʌ1Rګ?g1HmӾԼ xI_)TBi}M։ r% `/ҿ.I-s3D-8mKLuvxBx_z Ƨ,/N3D=fn\)iRǞ]kcjT#tl{]a@!X,y]n(?R|ϴ+h ąlZ!4SG qi ԓ+ƢQy=-ww TS-@8t3 jvfkT*"Zzԛ֐ 7_.n0$,f+' ()d/v%HL1}o,FpL%Y͝YV! Ljeƥ o7aLVZaX"yԔ,O_sF򙆐=W_*D1L<{ v6e!$H:( b[a(F:'bַE0"LHo!b pbd䅰 >,uF4E@PƬ{B`>^'oHlav"l25yя<KHA5CDMnx%#՘Ƀ,Ay;v >!]J®DcA#L;ǁ-/ֽ$}znQr`%$\_taXvdFg )gs6E"|LƁU|iĽ {16")#+7oḿWT|$ӓS`GfqkZ1@ q@0 .lsSsjETX %ŗ"yM낻R2 D/OOR&{?2ǴYMJYMS[)"#ӯ sR&U"0R] Fߚ ƴY51S.Aю̝TaPB"(W(DS8 `"yM{yNJ`6) C. f%%%%a#՛\-*'8-APnE3\eWr54Gg˦F:^/!9=AяMZ @eb5vT#-)ehc"yM{T3)FZ@=ZOIIsdt4iO@*'8-AvED -6mfaÑi>ސT?Brv{H!#B8 H3L"#SqM&J%i1qgѦMqpB/լl=vdzӞlTGrtC882^3fwPkƕl׬GO2io,*A9p%?I"B[=,UB[!YHd>Hby/z8?+"@_>k[R?P 粶"\ӯ b)|+#wL{sBt`B~E?FqC):q3d<(MeuL^dѕ&9+״r$i$ P5 - +"ԊG~H$ϴP٭ ~gg@iEbu`3f͠=30;19Y(#)u81c05$Gr$*F) Fce41Yd>LS@Br6=m5\1qE x",xfbe91jq3Yd^3Z]EV0kfhi8/ct$9G&d2%9Ǥpc2h!z9"L۷:ϤpcD"5A9BfI\5xհ`d0$G^/^CT.!s+IegP3i+DQcdFEj9״1Nث0($i mK9D'T6!<}^qƍ׃pdG5rMsW!93mV$!y0(1Q<.]6϶"b.BZT)ꥠ9^=%2ϴ.d&3(~xQ^ʖ$r8j+җC-vqPѭ XL `#ϴ/π0 2"]8KbWT x0,\΁C¼)lɒ|_/M*5Q%ƺC ViĀjF8n b@bb?=|I 3H Hbr\cu4Ltv`#yM3Ds%FόwG_f;#r'u#U%1ePEՎ6DF/i!9[nH]V cBZ ")*1Dr"B ث7D:iLT|sgF]WX}TX3d/ԸRD9TL=$ҥm`)zQhV|5 FH:Wit\ms]SW!93mԍ3A3#^Pc<sg8& 8ɦ2i1(HP\kSX:|`g`Y1̎a(fW<|uKE6S4qtk"S3XQ9 3S@Br(DDFDq3 *QV(l6(xXEwi(3EL~(VKw<+A$rx?49ՌpݚĀ$w!z:"L{P g$ lgDQEV8XļI f@HΖ1a$1"v9a>HbG^,Xi_)8Z6DRtHb>$S=Brl a'baWiEIҎԡdA%`ú3IE@./yvǚ]i)&& D]0NU)l$ݸ<;+kE0^==| -_ #R  SHbA 2(Ĕ:IXs\ 橂{$S@Brv{""㑘;1I Q+D`M*gD{%b> }H>ĀTbnI,Xg$Vmڗ`F8nMb@%z :DAG1i!9 !#( QAˣ(8gXB|Al|_/)d?1UIn)u؎VmÀJF8na@bj ;=|1 ~30 ar\ c00LhmB|UVsn}DCY._^>rϴ?i9XaDŰBC*Θ*aa nh^n3Tɘ8DG0i !9z_f\ K$ -H(YI) ս}/iM]WLSI % KGI|UAxW`'(Q H]#RWi_)8"_`#2ϴ?sT}@O$$H$hVfj ő a@uf;<x&`$@e++#"Hqd 0D{UVh.}/_ f}z4zļV>r_0_X*85spo~_>k_@#$g`@$dDwcę)gu 4!lJF{a>6TIѯ€d&:BCpÀ*aCcPѭ1 \Lw!z9"L{P g &`dCld;lV3`0l W^7\D&"b|`F*v!z5"zLLPuc<2l\悑P*|<Fgz.k{5H\>e TE/2ؚn7݇UilW Iss,x6-9\U% Sw\_@U$Gt2>sgڛ*FHή"PUcDU*)nise"~yM6F$6$W8z VfYe]'y8C{u.٫ڴYD*嚞V= W93mӘ)HLjfƅ0]R1DaH,,H^ֽ}d0i%0aaHnMaLu4U(Ĉpt4G^,rXi502[ :?DG$1iߥ0[ ƒ4&0晕"HHѽ }d1iݬaH,&0cԬa qX?êME$4e0 qyk7=|͒WDG1il#$?D=FdqsH rbA Asr(jW03H`V%l,a)BduW=CܚX?ŪM{&Up] w!z="L{&Xxa0$!)JSR4X&Mho\n6UHĄ֊kڴoDO:\|9CxDOS4BrvE+1ʵ`vpawV]y{~ޯr~X&߭כw<ݤVz|cjIF "I9IBDSDUAVU}a[GFԑj544KcG8ܫ#A #G{5v׿:h@:R;v`/ц.kP'f։Nwԉ@ܶ7$RCGz5=YFzͬ ؍jFP֍&ơ[ps/(&^7Za}!`%lo?İoц;}҆"&}Td^$O^O K &`2B9iHy}4.dO V @SvvHo o1;C!=E03Cו0kuir_됉2uTG `:vB c,m}b _S eDjAf_<ԫ5#5M90j!.J$Uh6 >7!kvl_Cn6CՕ `jCFfR=quȃuKW8VŹɼF1,IڑRz)nsp7$vɓgʌ\"w虄-3ŠAwD,N+3 Vu4O3S吱4'L/$B z'<r5&:5ZId\’3 %홄$y$ T%ןc,ҠH;\*It :pVaÏbASyg3 A257 $z&aHƏHw뙄" P:$#F߭g4@i> |IH|?UR`y&aI '0wE4̀$8w%t>ӐfgԻU S:_JJ\zJc.w+9+M5#` Em Y. .R ǺxC溪iE4VHп]7wi7響ZNHP=lLrL6׵ۇgRows=#I#=7UO黟D䂰þt~|^,C>lmcuRDϛiyσ˛|Kny'쯘j̍/?9 I=5>>5:7(u !UBwør:caK7)$HQQ#ITI :}TD2)((J‘:R:U3ǭ ȈHd 3bvxǜ\@FZԥQHBHBwR2mǥMsY$T'Hge"X#) eAq:A&}wg|9& ͨS$LEN]?48kNxW;R,S.K:)T' D3&@'I4dӞD GHn x_lTb~)P"D9AYVIS.H~ZJ)$!(nѬ` X`sd>b H0i?*8sAL1Yd$ C%Z" qϭݤ hFİi_ !9)01&q9@0ZB9Iʤ#58P Chv&CaGU/4:~aKFn;W,R؊ EHheƅ, DR !H32 1lk{ŋœ| gxщ".aW_1F\ 8yz ȸ-_)czy A^*!9/(2":. 31qBh)*DpL$2Da" EdPrMNd=[ac7x0E8:#\ F**=F*1ƴ"Lg`ĸ0-X:jCk? V&h58hU}dbS&E*xZ)^\Ҹ,QГs8xM|cFHTԥǥ.D10&U!-L.>y(Br9шG_#/C\8}"ݸ0l+YI7 _^Q~ƴL!яI̝$&RQ~vd0k_0B6k_jF]1w}A=ս"b.w/R89vubyT4twDj Fk#F+.~{Ԙ~g:@:D+T Ը>%>Eʇv5zl]֢]bXB0v֡PtczUY7&ȨrT&,nΓݸT*M{}F!L}Qy k Gx!loCF_uaMD]*z:1%8 ▾8--}uk pm1&sQc ̥Os|hUc-U>| H^^`Dmk$0B#yLP4WhF |ȲTDńM^]$?jLP4W֑D/QyHJsսF.iC_Cr9Aq?oϿ[^|qu}ov{}bw׿+w5vCHnE`~>l?6j3c D&>fN<}irՔjM]x^7rXwg_?51F_l죫auu׻.+2'yW_o|_ϷjS6L,ݝ_?ɎyF|% )'NB`D4-q/#f_)DG+Z-I/)~ѕNмx~m7xڮs'%PYd9iHZS_k["{a6hU|AeRv+qi ┨FZ&0_^a5;KlTʞsL' GUzԞ(֞) V> ezpfւ WsUIY8m""dvkJ] 3JˊnʢvU_9\Y.>ZxXKLwBZ߳H8=JA݇9t ~JS4J!{ )&*@X_=oϸK3gJ/| E{B")ġI٫kIRz[UzCheƳk^xO2~{Nw1]J#B*>y%__};~_/-K_ןErOgwkwRK798{X>Iܫ㐬+$RۂtPj?,VvDdgfVaz|0VeVcR>XՎO~9g ժiƯ+@5`9$ds_S-ȩX"&3*mI0JT9MrVcvu/%F-Ovj {Vw,})'[`Ko~ϫ~npe9}XgkPKp5Dkmh!EDGY-motor-control-two-motors.inoK 0{n֫ J{q$J 6E n(xLi/)D8gu*uiD:UN<%Y+ 1+ر,2sWjK4ߊ2rK<BK# _Q4R[srYs͕XJ1уSm6VFx3ԒvPۺ%*6zߢrU|wVl+/i8PKp5DfET EDGY-motor-control-two-motors.fzPKp5Dkmh!EEDGY-motor-control-two-motors.inoPKGfritzing-app-0.9.3b/sketches/core/obsolete/EDGY-motor-control.fzz000066400000000000000000000253771272363601300247740ustar00rootroot00000000000000PKp5Dۆq7)XEDGY-motor-control.fzksF_>',2 m;ޙ/J$%Rd_@ TzHdee}~wlv^H#|D"dy@ۋ6p`RZܼxXۋf}1ykp-n櫻aZެ=,Vl?V x]7񗣛ϏO݌[wm'QIҖ|96 :Zlң3nnib D@[}7qmg}Sd/]^-l zvhr{\u{]{2O_MKzL zr;Mdej :JwqzF5fF?YoZ"ucɇv)@7UMuO$0f&Es(ôVƱEO(ç3UWL;;{J ^ÀBDb$RlnC,FP}5՝l*2@|O2>.wot+)ѐn15儚&~#i6BTjPMeé-®֏ +5SֆG724QUDaV9_6fy< B`a1O &/$o0e1eQپ%G+Yvql6wǻ 5=N>zQlߨŠ:|lvxJC ]sNuYW quC;TWq@݇}wnz7w @ ?;}' $B $nb,.@) xeY _Q@HSX"$*#`SNBdrí+>t!⸾u r,Q܊A]zOFukc,~:=@#ܲMO΃,f`֓aN΃a#5De힂mb;sr#l")Gt[w~~u|p81~^]&(ڗ`.S'[pGGwo?Z`;[^G_r{ { xlHeo0N6uhUsXn~;E3HC3+4c !'А;6#%Q@cud\]RJb` 1w@2jؘSZƘL]HGozE/RTftP JfƱq3:8eĠ) kA 6-[(WGq"%)c2u] =(@ػ @iZ-&d+"U F>/)S Cڈ8\#|9*v5QL `Uĭ4*|xఐx陑n2-ϊ9`X[5iqt;Иi>DHkr""f8юnk]ak hZ#ы[vhFoV_20}ͮ'#įGr׌ۄ1WX45vyZJX]~(&,=-Vʃ|r]TONj)ߗcPdpSlzI&¹ (83#3{+%L*ȩ PWí<;`YXʯ"%#Ux̬ G?CμM[+c2"_8~AL1Ƒ08>QJt&ݢB45vydt M bVL$W)a$ID"06sXG=՗01Vҕ vX[C?yc#tόM`ץd=P E(- c5z1`iٰ̍Ùua@Rư"0k")1sM" !ډE /oӶS`0UcL^@/rY}^ Dn,"Fibq\SdhzP`$ 5 鈩/ [pHNHr鈥bNG VܴYg |@bަ-I lԪjta= ;q-D=F9=\zZ*so mZ Pd`C=t@U]( Za6%3وm0tĒ 5E@Fh6VpbP/SW3'.XW S|DObGn:L,IwRF v,# *TDS_M[MApV*X6m9 T5dE9zC//zQUF92*=H0NDlG@/_Ӷ+g 5uDLAL*1 ,~h`!1sHD휎_ަmSa ƘLRQ'w1%gA3U*)l%s;(4w5ohiv}=+ЪkD4jh۴R.NޞhN0"}і&m,er=MMJ LLF BE#򛹆ĜN["""\t5\5@WqӺ3L F5ކhm6O1#QQY`T7b*5P# `M :ڊ> W3z= ȕU"Qs5>50WqӺ}v)թ!Mz$1&+DBWu }fFDA :E]@]ަͩ \[H0-J5W$$230 ^! ~E)`Bmȭ\iF;zCiK!f4U%bD N3q.'+KikC| 3='V>Rg:{nyRSţG@Q=X "t jΊY!ڹżM {|f(V{b |bb $)&'V0dYɊ:Zy(;i(P d,6P(,fB\{*r1c>ή×qUB!1sMXkC?yc2umXe)hl*0<(ƒ(y @m6SqIgdx} i KQE,s;U^4ހ5Xqvy0%dD;#Иi4E1&SRQk `AdPP$/H3e2 k9 6m;-VSºVBPӒAHW< srj"YhGㄣaMMԐִZ@۴LkNяhKc 9P!egل)K܁B4a2Ҙ|=GLdԁƊ123ף1(qk%h۴%Վ1.Aя~h-@֍fB_jybׄ|sB6elgf[lbef}^n xOU9}}hqc*%Je(H##$N.}ŎڨrEse 0o^l?#6w30Fz [R {]d1T;VD9 p{p7vp:; ll .3íx\Tނps/` ~@|>NlN:^|36)qEYdsK*z4'.2) *%R&W`KfBe<DF>ozv٨JYP~ry6E $N,Ѯzy hJ ۟;_Ξ*UGG=}tw, ODM)qsEbfZ D$yțRxut6BBN=AHTc<NiFi1XiG!*`|ZҴ~GcrJ7 l qIudi08RY|SD)R,E*!TWWʻ # 035q(GJ I⏉t6vcnpR0fYMd"yyD F"[u'oxi#Xh+Jh @mZF"cr؆| HJ;Q7YHټčOIKBrKZܟHFMA NgFgyD$ID4CH)**M;d+*9'߳b@ gIh$kZ-'3[a]F$Os_NעYJ )S%A5NxIJj_),_2,-% h/SmOAɕLkpU0F \Yݴ.\ a$hƊE6ƑRLa&n5KM3΢ALLecg1i, 8(?} DۓHvhMe*3jhm%[$rE3, ,ޔ8^HE&-6޽Xv"~pIIIܭԱ9̃D X[zN'B~X3-UF.m(: ~$n42q/0xkG\ @ەB.%暞F'ju{I2@,e4.Lmγ`xVJdW$HAk;#vvÄc$*yhZ)^`MZ\BRj% sYOI"֖6=_ o"984l/ՄA@NoՏHA LY:Y~L;-W?~=sˉes(v +cZ"6N(f+z0y >)a1%Nl4y!yG QEEܬfT1nЧ/+/lpINcIY֒y^:a7qz;paTLlwpQaFLF56l`Qq(,'Uq,`vwzFK}`ft7h88 +*J&x FƘԥ1&SGl0'~iĕ RRxSS V vԙ@Ncv +lvB*1KBPzłAp g#@XEӜ@dLEI_IdFD8&e4RTWGB+״=| H9ㄯ1Ufaڅlyy%/1K*ÓsbjLj؅A'uԴ=w1Y`@KcP+p-wJˡ$\:k2J]Ǯۚ ]r/OV@ucbn| eG"(¤0 b+b ­G !q#!Ȋ^rCnh 艰G'Hq?A݇*{` ~JK4,z8J>'Vxuam;,ub -r쁱O:{cwYnߥ(dM/_yt 2nj5&˿XdZmo֛WY~?$~YTXA#(܀7nz37?}*vyXO7N~If,nqt |pGE\Nُ커WvlY.W2*.KMg7}jLf)~m'˯Yu7`G?GS %Yx s>S539I/o%|gTYM6i`iCiOǶSvhS V,k*N p_ٷo>}X[Zmȶ&[s?PKp5DEDGY-motor-control.ino 0q<]3(yl`9GsRJ^pc^BopƍƦbN 7v2nTo\T3n˭{_+wrXp)i?Z5d;#͙ŏ5߸oy6/[wZIԱRx[k=yB˯q|Y; oGGN|W[ә=o_0MlO߹++#tkϾ:D8VN I+b}}~1ItSgf%F|LZwwqo Gk!Z! -p6 s.nn1񑶙JLDWx:v=uwvߛ9f~3[knZ|w6x܌R~z+^LCbx'k郵MA K][A0j9 ? ]|N`!DJ( :Btcd0T*^zƩ ؑt)&qY=VۏcF2[Pk&1CB{tAFgy0z(NćEr~ ( \I`2,}_L&mUl=v1q%tmE(b\dTBzڦsϮjԳ*tein61{%,"T6Tw Qҩ n0|\ w(@ڃiEl+ w2Πƌ@zi|RŽҰSІҎ=pd umuAJC&\+b\ PcQºAXxp8Z{Yd3^u/bɊFapeDHJj$!Xp,,~} udDlI_³,Q gOş i7S0vR(ZWB$n3Az3(46M "#,q@EzjB%! x&j )ŤHV oqn`fD]v Tt! ff<;6Ւ)_H9;EF Go Jp!tY%/U]jByMgE[Y!8ѭ|*Z !O'pQI}jP ]gAF.T4!;w[K~< , FUg?D }wem#tu'<s?a+k1>\P$B9uB b׆UZt87Q8|7usWBJNa^k;)X\JEw 1/̋I^d)q>7WB0Ly7ӠN{ `oX_9N_0 V_-٫8i`& Fg $hTozYDEݏ(,̋7+imKԐ^ ʅ! .gXg|d@͠綷zоZ2D޽LR7Ӧ7( e>Vy]HP@=fB&Eϴ9Us}xUFL@rSt1sw۹cK`K7Qb(0CI"YvC J uzA$֜Uk){*Eǟ∘FHBeUJTe$j1l8:D~MaQxi9֞EK\lUњkqMt 4"6^H$ [۵FH^-KF$_̫qtHR,AvM)},[@sq #H7d,4 \N%yN!,R֨ }vFQ:$c /dlt;@Cbz!d|@ƊH-u^4r´!x4H1[ ނ,e`^`@M(J0C8b_AP,QMrbdXcyey2 %-t9m?qeJV4Z#][zM+c [EE{ʸZq,a4H1, vt2&@ H-È k51Sk%pc 1h1;#:>M_ADZnɤR8w\rDB6圞.dδN740U LTVˑ#E[zJJNA,ƢY~Lσ`]cF~_EEkJb|NvCK`(F,z pGGCah]k+*Z{A'm }Q:)!*R#i$JuSa4?.w ;?@2"aF4]m&L!hղ9z6@֤`G]}2$ 70QJЩbxE a6UʲRXIePhЈ0;|l\,hm.&vGգv^XQ 015O>%@Qh ]eU:mY>mgAskޞ#tu'~@X*N9ֲ~z?{Ӎz||Y̝;1Od?ן5RW=O&דɛOo??@T{z'{pO]Nn[{MD,(.s4՘lCx/׆! Jc09Qяa+:O~G7&He5ŗF][Fß.G%GX!+wW?zTr7+k|3}]n;gvXyNR^}f[rKE+i {GX AF0MBl,ngF߃3La~/o [XE)@x-XΟa bs!^a˿JaEOҚ,S9t5|iKڽDo&o-έ]OgƝݭ-G@/[oKQ ;n8U*3]]jޝ `RKRڎ<ܸg1t2êA]}H1G;Gp]E j%hjxg;q%h}#\~,f~"U;Y_M|^]S&BtOřXD'[3L[۵F!Y#5"od D'~T DZdD!fAGUUZUʭ!=ٹsxggU8QubcS5{Ea: 6cP$v(,vxV3$C]XL܁8Vu-\~Po}_G^L|n{/Gʳ*"rgP-Je,J2=J[2ݰP6 !}vVQCZP; EV95,uÍ'B!Fx,ZGJ-{_> *˖|/Cv~Ǩt,̴ B__v_quգ{7i~'Iiwu=mv0'FvvfMfĀ$YOjd#Qb~B{?;]dl[yɚTL<v}]T~E1O>%׀"8R3섺uфt +_A(Iۧ VM[tFiM!* 18jzjXT>5TGS"8RB/&!PKp5D)<n Fading.fzPK7,fritzing-app-0.9.3b/sketches/core/obsolete/FlipFlop.fzz000066400000000000000000000244211272363601300231300ustar00rootroot00000000000000PKp5Di(}] FlipFlop.fz}ks6ZOdNUJIlMw'LLsvt2m"K^Iۯ_$"Aח4<5[%}닇ʟn/;[n/0rFA"ވr@뻫D?ߏOF]jr=_qA2~܂Lf,l{%́]@v4?LZn퀅]%e@Ӏ]>0&t!h1]B`v4 tcӥ3DiJVz6b#=:u U CC %4Y0r" VIH ÄFQ)N=vJбYG>o,D㣯^>x%s K^?228<[N q3Wdxm f/ }/Ii< `$G+u;E1Iؾ 6dnPV6ȑM1h lbj nΌӘ+g(H|XG9fxiFq̓3?͐sx)Ya@Pph?vZd DF4z!PdȆX0b F9_vf@GvP<ժuSju. ouimiwv k~TC fKyE~UV^5fk* 6'^.UHKg..+XCʧO|7+5Q)n+ JU?W|ֹMsY3xk;lȓa22{NG4d`s9U`n1?PJɀ1W'><4v|ħh4QګI9ѰXg5(rHHpeW7 >RCL)ObVU̱Q;bsEH&.A%&,W^L7| Vư,N+1[NjDta؎} jN-see'~""v"1% ӪC3ڂz@WHB=|z,{(n[^}5"-vru;2ڃH7oz +hlrnϓ?|u!WL|.iyxZ?~rPa7&OM-uЀ6~ԭI ]N#tZE~"id.AVK?~_ )khx(#Ә6rd:9(9 g4Oz>L7ynTopLAc$.߿i*:vJ'F3٩{K5(IX&&)/ \&#qW#TaQǖ_ꫠD'T*eܘp8d4"=hN6e F $EmVJvjjyQαvd̸m jGvC2c)9R.hHJkG6~hDɱ=<]Zi::-e07~b<PΫ6mTPphl@q\"_dO?OO]uH pjdwcxc5?\hEm.=oݞb9re2zg!O7 GIjXƧP 0nJi0KX _\r4[:b}:<"_, oSC/Dsj:=)b2ɗ8Co#gEN\gf$gRn椑5ö4w mM]`Vy'#h7Ͱ -ՒaEA.E K{1;ɤka_@b/" (Xasm۱Erht$MC#A"F/|ѝ؊xKݹ>YCH$$诜 b2J\4]PXYq.ŋƓ: QSPJh0J\2} [+,8|JAʩ#N ȗT*8qBic91VY])U*bUV8/*DDA(K YHN|i U`R𔱍6.J[|2?`RIN|cFdt'&CxTroM2p]F$'6Ih@'l ȤICD$M>S %FN4HK"K x`GC"a|r8JYQT<2de,d0:,Y3Dd-5U(K*MCÒJb!x)u]ޫW_ҕ~YO7ͺO* w_g8B˨"I=T|Q:^q 't<*a `+Q[aT.(!:9͔xtd5̑9xtiQ>4i`A6w6 ;Q!lyͲLHE1ͅj&~S{> m i3^rhA8` aC)AHB^aAԨt*B:y9=;5LwR1p6-C-'o7 O!$?87]7ӽr̴&C6XED2r""1֥CPaCM CI TQC 8aMLCM TaƼDKd^ w!I)`.#sCCBݦ0:ĉ/z=:2KDX$JO I!u#nz-~ZWdXQ<$Q *(1v7عNqd4T(s!]11 Q0Xȡ,,{ʥ uį^Cu CuhQ(Jg7̻$ޅv\aQAk2O_k5ZezIs2‹,j{oLς{ĒCncbH$ !}ܓ:2ci^y7 K2 !s%d B6D۫3S܊`0кnGFx_*VEq}=( rwN#dܔ"S")=ύVKCl`C5!Oƶۮ빗L^ьձͤPq^UI5u KLCmHt`Ka j*[a 3r|##W/aֆ0 z|" We#2X@x=ɪ aii(jGFE[Hhs#7eeR$..,2E1uh~XmQqt >U'%XCm a1DMr,P[ `f{YHAF,64Г/idrdcv\>GUxD?@Q AN:VSj'r"q)\TNCqÆ ,u!j*h%ZtWYlHipq)<&)>-[w`_F8ZF14?i8˘8VuU ˬVLÑpQq 09DM-r)ʮ 1$)rUSF$1 H1eKC#Q=厌D]௢@HҠv"WUWlYUЊ *` Xy*(`9c:X)T\UPP=Qij90tK&QnSB#XUVܠPfl&$F偰oJe/4z!zmk2Ҝ/}{3ҞamҢjWr6Yj'}lmuYqXA7>S&Nr<6DNbTSwxP\nKSSgg c}pdXꀡ3$D#F<;<6$?6Z瞧n%r(HG6YRps5-WjD8b|߫IF''XûS]苶kQo~ֶuB{{&k]+/Ʈu_'.,F˭"ryj=814K>Ak#,6N Wz\q^]־j<ni 77u9Ier)CGM9CGKq7Bayc`7πT+sG羦;5O2 &X/xDܸД{Qs;WʜZ3 T+ bT*/ʵyK f CR$@F+O`[=LD#)]$Ǣ-#b!=Kr+ڂݮGKԮ.]uXJ^-CmrzE֫8XrJ`5{[SATF M:x M:ehG}[m՛: 4] ,: i?CqyYӦ 0\!I;㹠Cc* -Tγ8 9$r<ЏY91 C/ʃH:dn]8Qt:0u"BcE=w$SCĀ٪hH.vvrC$c#$C3,!4 &x I%`УkcuU}4 &b GE@0`K,SGOfe_8v{vzfxz׊n/]/7!?;\;-{y܁,ݨ{  xD*CH4tJO -0yXSVtw  ) މ##}KW5?On;jVnjýr P/Pz%t@adHҾ$B:$b/PKp5Di(}] FlipFlop.fzPK9(fritzing-app-0.9.3b/sketches/core/obsolete/Fortuneteller.fzz000066400000000000000000002515011272363601300242500ustar00rootroot00000000000000PKp5D(fRV Fortuneteller.fzr#I{ NizdFDTK6]J9XOO?2&Ek&"Y]x2Ё~=wo>?|Ϸrtp7 xHՈhz_7ቯ/n?ϼxuOWoG14nWo.>7o>z{o.{ak{|뛻 M'Uft&?}͗ ߼i7~2?w7JO^)m-얿\fyy?=__tCEZe7wpc!-qßn>{C}pZ^]xs*?w77OoEŗ^?<޼;[aǟ~}/߼G{qwcwQ^ym§7Ϗ?]$iMmo.~JӍ"۟+[x￝’I;9gm&һ4=3+WOsȳ[%9,<󷋈]z7UjYE_H:A t1}t_n_ovX9xqo䒹͛e]?g5𸣰W lNj*VBl!-:4̀lU/ Z" XЛj z3™J=djs AلL'ja7I}^580`AoU"?XЛyKcT%pn}Dqޖ A3.a{3g.aEUN!3'.yȼeCx津bȴe~X mܬѭJbȡu[ߞ3!J.P;UR}=r_<\^ieع%\%t.Ņb\* WHjp.pݬ籢4Q 8K9 )WRr.˥\O}9z|*p!< Ϥp)_8gXMI9j|o<5{^kWԢo#׶g9ł'ci}-'gR'ʋRZ>g[)XD ZLWHs \ c]t-jRXϤ"pER3)\TLj+Ɇ:tgRX70K `\ {CUzϤo>~}ҿ/w¿:΄\muiⲭsjZ% \®\*Wʹp/Xx*ϥp+)79ZUrNa=rXxeX HIM*1WSpުքՈkPcm|jc}J{YKzaJ TΤrpu~T3^C&,I`}.s!\Ъ$.|&Uc$<ORK8W18?KugGKWnO7&/ /W/><=|x}S_V7Ϸw7%-Xق]^Wo~ww)WF+z"CLL+G>4AR6S_JR5"Rr &_?>?^=]J~{H{#IʷwiJJ5*sٜ߼0mH-,(4<]R`"~joM3ѷ_Z7Z&} (=+d=}wi@OUfApɈbG.HT5vEȟ$Hў5Q+1D))szR46K!8 zI/2b f[YPZ7:*p^q9o2X 4E&<&tuIC#^wo|sHo?-y{rxLJr|7w7yo.vu}^zO?x_U+掠qF a/K'yXμI;ڴ%\a~7k *6^"Lh}g@"'{}yIH Ye0X2L˜K?klݝynPq[X#6|>?nL| %]ǫ99MG7Q" XɪRQd2}}7?[+xV S7Sw{קǛW-|mg)7Wz/wxce4Uj_/ˇz4'MvQ9aJm\ -2,Ç/T,rQk[#xv}^dYٴIk54Y mB1& zNLjumii`#i/&sK;NUدƼqW4 HhgӯI~-ӫ6#݉ދmffN ݾe0}(®p:Z"xݴS2ksxKC+wőV =W cijd/&3`=L8t=3P7G(|J:tsx{=IWڠ 2-CuZvuyq1ſ=k&:4!hOĵTjToBhRNm|mRqid6Mh6ɤ gRdG,d.9 !'tYDE[\D }%aO\2P6㧻Jlsub)6' %"I)![\TPWRrGxHyڨM&\[O$ &ZJ &heʶWe}ۍK&4J3-r3 H}OivZy K_̗׭=כc,l)i9A_upsl60*4 ~ծMQ%j;XZ OoBa9o+aԱ2𪣍ޖ"&(2ff|ԍk Y\?]QVy]\t|ʭ+D3ϱ1R/!W=dIUbO^'k$ڙokIDUBэ F[ᥱa'r9a;FlUr'26v l׎l$yN5c-5z\XhD{M&QN| ìkL,>'/)16o_W_W "yl}d(D qEt跱)Vq"'Z^ uڅthB~!U baVo֧Vk%Zg3tգzUӕkUDl>5zU\`v_|%U{髎vW}LWulT2BQ"p'C X8ID4.\ݲG)+ujڟi c14Zt> 9nW"9ۧʊcsrORfYB9~TZќat?$3A'—3мLWqa:來=֫C^0Xg=?.s}%?.lyӶ9qO>xnH߼A誾xEz_s]=Sup^jcinULW]5Zy:c2UXh7ww?\ }H.>\}})sTNF:[ctjctj˫ȣdѩn8CF!0]Wi{ѩG=:~JOj!:K!GTRZ67H5"Ď{*>nR+mtƽm9UkUC+ *:jPCE.PJch8V' lљCa*y{Hс#E9HѯӿhE_|HW}{HF߽BȻ&LCD:7ƇV7Ƈک|+kGУ'>qm yEOll=LAGOdvr,ziq! Kw\621ѓ}ïl@TΨx~ U_@1U|A%fZ:ELca@^WVW:TYXIkssJ+kd{{xexϕ qϣkWjf;mlɺf n 7幹Xng~>dhA\wh zyU>;-P.yҌg%r J{lk#Pʜ$PKOcvSm-Üwq7l||nRX 9lt,'ږ8efFn]m9Sn\VCk5%[V<XYCcw?u[/l;Z75*ci|iex:ZM-uĵCk ֫_I/J|w}y}J8Bc 2ޟntYeWy*r,~-/Wi!.>ޭJ}zrY<)׍tvLpLS& hՆtN j>NT=eN7u+SwyƼ6vNz ~[ zv̈@.RAۜN̒C]f^ט0Mڗ]j6r&]o~ u%Ks>++55{K$2u"d2Ntb(G ,nŵjBY-irL1K6ɨۍf.EotWD 66O `%Z[tphB-hGwj (|qW&haO@;Gw0irwaʔpW:9] f)!j鮎ww swu~u3Oo* }hjpmFwg~ (*;>Z&wrtZ:I"i7+28QC-Օ2Շg4kkQWEufv_fW7Oo3L?C'I!#a]~>=euDUViďPv/t*y]t7pzCv?ԫK >HC:FasZN>CV1:0cE̕hts1F42*\)HH{@/9ؗ1o̩= ~/y2?sW@R~1m(10G%luP{o ֳVe^!87")ǣ=c l}v&\8I^1RS#|1> eR-T3A5qY ӳ!Qh:Lswɘ̇Ɨ{ʍڠȔh*d9RG@/Y=z=ͭŦ$Cp#:sgd)fsE 8FyH`[qR2qK/-[Z٣[@Cgj~b(swgLبS|(nZe;2 xh?YތőRꎵC^LI8̞gKzvAٳG8; eU6]u 9Mq stcD3q۰gc>N+2&n̍!MMqC_@3#H,8x$p/VO0O2#p#Ǵ#< hgrčvQǭuTd*-jH,ں9fgW{)I.w$Ljm2K-Mc˷[ӝvd }Y xH?aO߸1 6 ЊeCc,wFƑZ =W^pcD R1Gvčsn][Z-#Mօ|yf /ÖC+a\T@`pQ6lӿ[q?V,ρnU,8זpay2ȳfw'}6^±Ս4w#CŬU׉g&f6z@` tp5rr^e`q5|e:qcIYg=8/)WG`:#&TzIvNiWБv.P>ڵ!dZ=CGUk*D6^X}3Cjݑw yWR;ȻqyWwdOkwȻtQ_5Z n?yWYAw_6]q\3*:5vPZ Aw_:GHb%ܿ4~}+(Ufֵ?F3vJНWEA*H$Z킟FVMXXvtgѮj㈴+Fԕ;pcl@)H1V5QF9c_cP-I1S زv'J /P| PF63t=~|ϚɥN*‘FlUfxᦜiZ3dlazԨ'DwOȖ:;H̢8.13u\h!Լ{mNowwG4o1)؉r`A:F3tFp$9٪1NNd9ByȾ!n|[ Cw@_+j IPJTjJ[j/MB3nkV֋ɜjJ=MCu+Y[7F3tGU+d Gx08V7Huj- ݙj Cr:jRJk؅`fd thpH^LD4N/ӐļEDUiLk+pAȴz̴DHxV-QFpeDP3#Zͪ5$-"Z 1q#ZFTB3\9P,oEk(zZ CwZ>^eZZWSeكÎl ݑl ÑdȨl˕2Ϩr%[cVedV [ ɖˢ9PnMZ1㇔=3~޴lA0ȿzeRUG׋ÑQ+`{Z"?rC`bǐ=ص ,VmdNbLFVlD1X;kuH^Lfle21o\FgM XxbeDci%r&z=Hv/mҡ`p$bUԋ^{XvOKc$]}C~&Uzj[ ptI^ed* eUPUp^/&sW=t$_4ѭ ?+Ft辑Jac8^1j|mgr=M ?U)v IڛD*cz%& aQ 'C ߍ%_} !yW\3/_DA{Ƚ1U+f GGj"_=IW*`c=8II+qCM?ˀƞ?$}7pu,t$_/&sЗk`©zVF^{Q򯝳F|螱Zqc8^2j}mit= ;U+x I_xQysIy4)ڹd~d_C}UZ:$1oZ}bgȾֆ[RbD#Z>tW1 o-j |2VH2$m^1QJtTAyybhn1)mm^#z1FNtDw_zၪ$"~+kD{-wЫN*A^ۙvOc$a}C;$Mb^1I;2 U0coOM>ި(<3_{-M_gHb<_M HxTNčsK2cd^ˇ3#M^ɣ^;v?;c$\}CwzU CQj`㈄K#.WZ悒aüv=Rޡ䉼#Y lAX &VZ^ d___:}ܫRŽ^[vOca}C|U CW5rL|\ C[mˇ-ʽ_:5# W%qbµa43F7twU)^ IېJ0dl:V"kvz~Jv?^~]}CdX'.I֮eYԱ^ZQA#vHt< Ñt*ڪpkOSijԫoR ?9-TQ|JB9@=Z= Kwg$mM/v~4@˗h>[fLজ`<۷K'ԊÑZĄ`G3if4o*ŋ!IyL-qLeL0 ۳|~m~ȶöѭXiļ Z^ V\:BzI'NɼK'[p$9ݪ1NLv3vHLj!IyU hn#ӭ*ꋬY A, tkCG7tUiHbކnm0[6,ni0-X`ҭ:Qc8ޜnU'[vO;c[}C[uƐ ݪ4H-nEQfҋHuVmasG7tv=jz~JۮUi{8oMdDc"Ă;Ϧk<3K#^hA1I5*89{Z# gSk}d~ k4!:SPb~0e'JSAo&]ld1[Ѓ Fz|N;1҃Cv?cd}Cο6&6&㟓۰AЃ3A#=K`]Xc[z΍]GR|ޤN;X1CvO[cd}C N Y(%"}O۰'YA:Kۆ$lގ7"%H0;cH ݛ Ñt(FJphCiho()A<1TA ry|@ob]ܝ.0]=5##x1_ڝс+Q\&n?#鍙B-qꢃ} 2LA$-Y-(rDbqt5ɲf 57f;R{SjТП#|͜,/VL6fXAv#=[:tOzV+ G/VGqbZvi~ĬoĬVZ㈄̊2UEdY{Il$hW&5;ұӱSŴ iաΟ&Gel)#ټ2JQd88`V k=퐑 ݟU CZP-U𳅣D1i6HaJv?wc}CN+a bLRp|NR }-Z{;f]`[YvOb$W}Ctg{V8"G&S^rfjF5:՚~gOr*41X 9B\Cv>\nTˇnwuQZji[ohw_)ǫGU^5NFjD2ZJZ~HN+V=LrjIj ̰VFW+616HowgQZȍ3l _Dy8|9iWkwѪXKhU z j;l8Ck7sU5p9}ϢITl:U @D#ǩl.y#r'vgIpdTR Drzzhqr->E&D^~H?#z\Q 1;SkR{d*PR-*6jQ3<<򀾡[Ջ]3CZSXb/bΧ@ `Mm p$\ Ǫİ{7t@pbHR ֱlKӪbPdoiUżx-;_2-:.S5v^X@1` h!^ tojq=-q =RW 5ɼ⑙WgJ" c/!x]@TJwv?||`}C>  qHނJκ{m&fBwNSd>"\'?v^+BH2IoLjA}12{T+ I[Z`,-QYZ=r I)chaӗv?|di}CNPeizz^YmQgL1Aa𳱍^^Չ!Ñ89? {!#?z~V' I[Z`bU+ 1ic^I#/?/;eeq+fʇS^3l}yY2Io7?Nv7?*!I{xY%qL>b|LF(?7Q:|lG>7t>VX[vd̠Wz'Pa;{G|o[FJ1!GFV/#@!I{ Ff*i5{bF^l6&X^ Ñd81{#z:V' I[бZPtý351ڌ..چG-nvN*X:I۰J2OƬI־y)K:">&v>u$eˇMĎHzsRV n@lOi|o8Y2$io*cr#ww NfqQɄ*FQ7Wy{MIHzON=HY8fadɰr!@&rSHlpM-;FFt辌Rb'EY1fkv=͎ ݟUCv*iO|iS>vHٗLJ?]_}՛n?<oWwdP#hp/"7"FmiS /~黫/7ӯ??]WÇO'>=?wo/_/&o7_F}_|oyxW_~zӗަsH?)!D6~}{_c'}mK#/(/Kt^~^B\H݅rmoߕ7۴)_zw3yuO(?q?Q~IkoRbtq_onbdo{Hr\ѡQ]*㾛wx෻]¦ԥԍoTGMt#MW |>rͦowuL EWOW_skҗR5 #.; E2XZ(> pϿɨ#uc .ҿϿqK*4) sѾYz*N;RGi&Mm{n8?}YHX7VU ;\6i\x#қ$e ̀otI?/t..od?hߤ9ʸF9},#۷|16ڸd\BΥa$.e^aq,' mgNKkt glӣqM^؄z}]oTPKZ F?&-F7/kTw{~+ i.a(ĴHEHP,ceݾqRy[2 ; 5_IڔŴj-ozoELO?[LF(vYqx_ ͏ϫy*qV~EO?jή/^|* i^mvm6C֚bwWO&nEߓ~z|Wop直*d^ M)p젤QM<<~9 5!W}j2K|2Vmk?3lFx$Y MP_1ryj{̜hV ega^IӬ'n^h&*|ʩ>%sZ#?߾O/IS:HIRņ/]d|[]$3UdC6Hi hlF^ou6\c$up7e{w^dy{4*s&OL$1Nv etLI_^'/σ̓D̃Qu2uidD̿ČSJ&f2D%OMuDG{^;>5Ǚu:9q1ek<+ ^Cb3tA12fjLA:ג^1ƧC"ms2[SwBc,h WLCX!$J3+_BY %)_wcw 4$Md4"B6M ݒX 2{?|'".o9HTKȏCfE<ŝߍrN~&/c<$18 l.? 1dSN2 Y]˜k LnHE~l% sDei+p/VX"oO7OaɤJ ׸Tu(tKi2֭TYczG9b`Ld}yxx&w cf;<1M&u+ RI->JZ։xb_XEpK ^2y\w #6dZ98t~&,PlNU#Ua ܕD`SY<!V V^yTc>/cxa-p,m(*[W_V0˃"?V@4"aV%la hp~a\Klw!u?pV,C"##6LbH}OŖ5[BkgU*hu^JDL5*!G6/(,1bV!hUYv8O`q`1YqP.@Gck)@2R 5Ω1 >T+S~QܬYUX"p˫-Y8n!)Ns*.P*0- ,OGd,?s&Uߥ-2Id~4%B˶϶ T `vFR` Ka'-.iI,rG)XKQ,KJ~Uu9q~[Rym,S1=gkWWa-iu8L@J3: R +* mHe$8&~$-KXt0!e eIVNovAi@"= C4R-/l%ak2!!^dQ/VX0|¤47=5.K8h +dQ]FS3hKCб;\a[E *v |ȉ,Ize?PP$ﰀTõI ygP]I"̖WTCeJ&#V4Ũ Lp&pfG;tsَPB'3"p9oioz0%23yT0@nxH*RkA7g77x0׎r`ö:K $1r˔-2-lKx<|P]>)QT0" 0pK؇Ft"-0͂}`DGDI 5e7HOb Mv$-,) sTI$\ L[@;r!Yp`8 {XCxtM(`A SA#9kO&2*7wVF~TXʀ0e{} W; cQ7aW@DbhG 0$&c w-MAo"MldVdvt^=,mi~Me\]Ɓ*,I[)אSLCih&a\!VP\մ4qJa۬ys`s1=2C3 Q '`+HXѽP!㉅T 'Q4HѸ¤Q#c?-͚XwQå IeQS=—F+5K=~)B\Ċďlhci`C UfQXvl%T&4Yr &MKcNǑtupT4O Z̉qz$ VUࣟzr;甸3SЏ ݆ rSM#> xn阽nAg"L `U(H[DK,x{qP~1@p4ta|ԖE0u*:81B"c`rf(S!kK-7lS⭊t]_tn.(zo$b8es݂4ӞHIS bEC@u螧l 8z=P0 , m4Vw [NKiȳ\g=2,lc#ld+hhiؙjx&1-v,M|'Y@irLU:oZ&dmHcېe T$PLwŎ}88xȂiʔ"oC ɜ.]x3dI]a|lՖ,4tL@%IVHK6G.@LQG=DcTx":J4N`^ņLX`{Rt」9Aj ]BZVIZj)hFlFh9 AIa9?E exzYGRwM+A'#6aBe2)8Nţ RXl YJ t{J( C{>_ \uyF)zV ⻼lilLZjq~HHBG( vh8ީRЦ eaW0%%(Ԫ0 ,ф6-*mfVS9:HY\ŀ_I&储Gy:`o q'G4]^/,C*b ˰: D*`KDe0aP  d4m&a1w3vbA`aIg,7DO^^&'SCTPH-<1Pa@ȁeRCek/P#3#P,~Ѵ `4S5+Qde98 2M3 9BnWH(R$0)zgA T^yf4[{0¥hCoL]`*GT-Thf:}W\pS}ZDw Øwy ]״46WJ $"@+,w4oVg)ME %mC7tbD'LWQ,èQ z&^+Ŏ4 `8f#T~" .3J1 dġ3pQ< a)(rІat$C;,% NģrG AgU2H T^ l5Kv ,2GD,34-짹׈=ɍ'!n,A#@9 }aFsAZPWhAg}j.%UHzaeq_4揘BElV?$ɁeCƳ*y]3+3b*a{Ff((lC9\7  (IJ^KMJp #7 % E:̗ 1 @ t=/ۧgJ=P nt(P gbp"ǖ{ų`rV"xD[hPQX08<`D0 {,X GmNHk4 *d v}ؔe)WtQ3 uhe(@2C "pNBW%\*IXj~r{ o)ˣFKt'Js{zJ]` ڣw idHW'htK&&1ى5M}8)h`_)! hf \0r)j(z6uPWBӼC 6Zd)|bIqLHI+ $HT, l=oVo; _`ibM.lA(?t,}N)(ԈOU$f!s$9ImLhf>nqnɩ1E%[%-6lBZ<4Qd1$wT}"S %8% &nPINyWWBqslų 62*ܽd,*3V=ejLȌWLP:aCpW2 @J`ә SXv؞[F%H{ TOh3̃IDJԤ7-dR734 9 )b@"#%2’/СPBF;\Q7:.2iL}qvKEU/sA 22kClUzlj`j1?+@#BɃ/yвIh(ꗏ5ysMqT@YnQlM:0`9x-06t+ Uz'Ł F@g8gfr3;ۊ2uLejed8&SbLύ 4M!#ھu[Ag` @$X 0_j]% K c: Zo c˙QAwI>jƴϰL-1[X{okɖ| f!+Xl̑bt'V!=@uD1#0InIwە%lI[KM$]LZgb40$U(^STft:x$2L"@J'L|ci˞Cs+jM(M,8Xìa"AG:4քe|@Q/jֹ&#/]!V(ec% (tDbƌqVDfe"JkYʀYZ,F6L6/5Ħ@(P2c|W.hG2 Jޠl j&NZ#K0 bm!b ̟eKPf t3JaJ4K T0f,) I32`SxD=,~V3&ot?.;u"B"SSLfG6k l]KДUHi`v h30^1ii;!΍% !]+Lrf.wPhG*N z{ܙ qr4i!6b$-[JNuKdC`0le8YL=YC[wh8EQ?WRLT" K=K,e7AZ,LT`]R8;. d{p!U!(2 QKJeٺZѶodg˒( mzt f-ًopx;q"@c|=,kP*T&ױ²UVQisSKiA*V5łҁp3p~+]BOD l@7+`%^ 戗0v gIۗYtlS(HcDgT?p9m^e&M[0)_ dڲjf#PB@茒`6p^"9,aAXӕ2JZ I摴X^!̕3[ !A=mMep 1x7Zz:NY£+4iPfns-ɯg0˸+YAs9SvҥZfO6ju,rInQj*"K>~"RA Pn hYFMOG4!=Z(;Yj겛,-*3-E΃B"T \y=i;*ݟBۤS̄M!3aS46 ܀aOttVg>ҿ['Xm{ .& 0O T2`T7[df]N2.)KvX-\Hh56W؊Sb3*@jw|M#&1'ה 1JC4Yz&\8]'mwƠBdV^{Vǣ* ?aF& ʖ`Y*3q]~7'Y .iPj =\Yq6! ӀL`JS$ gP3,* @#q24DX8(z2#1X3 !U0+qx}! æ.ӢgʐJ#Qw +}"BJ Du+ v-$MCR8,,K ƵH9HmͨT`e- dz3k :VX0Ais<%A'|1, XN}Y4,joqyJJ~2N :i7(i,1 Rp#[.qg;8>1ȡ70* m~;Yɞ>, V2;Q8"r_:f\9FEfvdEDVDŽ\Z904}RlPgXa s'# @H`n"˵eMͶ 2%4`PXtxԄg bS8 gCёmQIlVͳيgfrXǃђȁ51SZY3icERzh[^q[ НM=R`6+"BpYfm2JgOi6R#e1+ЩRjr||Ak/MΚg|Jl\GYvA󦫑q0V(buYtVta̚0;>RYWj0n|1gd]1Gx >8f2H/O>Pd9OzCF5eJ; EaHF`zk"R".SBm21,bU(jyR`^uܐ.Jc KTc2 c5`nai㩤o $O(uG9iu-z5"l\M0ݜPLsSYf>13L/6_6,ƌVe.0̾Ҵ\|hW@)A!{&eb2FۘϤ:VQuh5$RQ U|'@^y,:90 4qXJl!VѰ w(9 hЪIK"˶RQZ$]O׸tWuS-pW^ (1H+̂>`і( Ò"(c8{nak"TICnu,D̙fM)ߘvzBl1 ؄~dWoH!'vvr8*d.$WClZ!BjE]ӹ3cG1hqb5]4Fr#hJ2cȦ)J'Wh7_Ʈ (3nJ7%1DicLCXۘnSkJgdžBv#}ftkH`IO<[ɔ.c$YKRF~):Q5DvƴL L)im/,Yqw tɮ0f6,Uj@eo21מ]S;gi/3u`%30=p=TJG6hNX`%UG~6Er,_o@6tj"0*}Ea9/y5ȃZ_6=+srrE'1G^/m)KS# hc'siAY^6.QD08l\~3KnȠvdQYuo~d*`4bR'sٕYL A蒘rto iJ>|\Ӓd-sfet;%ŔLT4YzfW쇀͔QuBA辋lZұ!-_~YX"jE"g}$x2f 5b u0;SRD9'=߀b._D1҈Q~-W4bζ1@~IH]yF Q&-g/Y$_A'jS JȤ :6gNEL"J:,eɂA|^Pwi18&_.BЖe.4Jಜ;d+&Ƀ:=i?aƢNa=}d7$ |i-€8 < d[?TnQͰ$QãuҖ#4F%ڀҲ4cr)=+r`N?!av˒' AARG L%/}00Y2PF+jan8g%CU.my[%t"󅕀RR'-b&\\aP~T"•F:j!&; F>/[TL1y=K T-S:zCf˔62.fP!)mb|وv{]EQzæOL!a_lOrIEd#vܘoz'Y\fBa6{$aF034cKGXh>}`-d7MZ% ӞIj߳(|, cU֥kѨ2;@)) cl_B[&`|iA\Ȃ^B.d1y}^lPZoUi4-3k |wű)04H'DzsyR*0g{*ue-1vVp]L)1ɔ%o\gY ^SHx@ 9"/|^9 [)RLa5UZ(omRHuXY(GhVo;2ڟDs5*%07h4l#N0Q9o d["RzOmڷG} 9] ,~,ccƤ+ATTѤ 1 9AjkUzB6iY<C۔GVL!SꜴ"tfS;a(w6l o;:5JlT7pN5g2HBJiG ^+FKVaҫЁ54p !4Գ!AfVl7hK"I\", Zicu\q"*L'&N%`R<£̈'[fNC V=&{f}`,>1Z$و7u,ͱ Tv!y4(f Ƌe- RO% 3Ki43h~~a ?#L\Mؘ4CjK~0%+c,w2LVEÌ'&ZKr#;!4hGWP'YrobU]+3eE د﷏'xF]F.!U͜=vB-F?4X(Ԟd|Ug=:߯q]k(bEq]FQ))A(~fغق1Ydagb"C-\[4i=j@Ǟ*2͆jflKxg?+0Ȕ0ARt46'9]rٜ2IN!B{O7R~swM4R&rHSyT/?M``⻝j6? 5\[F〿=䇪Egt_!e_8<)eg]Ȟti#p XכşQ^KsVkbSWw-ļjK1Lc+&44wmsIfUNVk0q{ &^EXyR靆·qD_ڴWVpn%ɌRVUQt)v4HW:4'0=Q?o^9cŞ6 8w>KT-3펛FX8st HT@FJ&GLoBðG/AL &(uzC i7T =:Ƶ|tjj>rܕ X= XG9Hda&d}{@ yͯC3؄ }-  6F~[-MAlP+ƉЗ7ގ@A?zAݎg>A#|+~X&uRB3(")tY0ء6PK?Ybgb˰ulswu< 4N4J\/ M=XGpe4`Gf-OC֪A `\6E/D ǮP36ܼ݃ë9A8ͱ@a䡮gmt-qt -d—BXP`7H.@)ju0StƝkv@@aF54fP'o½W7Xp!)OO b*:ǡ#,{䨫K;vA{`%8Ȝ_V-j;+Q,=cg 'm!OffI8:tńHbwdt&tgُA=CGr%זrv95>>y#92Aem=x8t2 ZysyXג^Lْzbsh>%l?s킶߼O|=ܜ\: ~2ߗ=3T('gb;h_iU 7rSZ$4BZ|@Ydn>;Q1Q(lZY3Tk~/否Q[y ,& x{c՘: $TgɰIw4:Q>5!0`_+)04Λ1A/QH8u#[A`wY?]z_ϥw#xX!1UzXaː`4j)apMsf2P?㲖e"Tj^)X+=竫!^tKQq{e4M^ohQ9>Sceɦ\!Wh`hK#;1}tf|͂]:aɦcgHC5]xN#n4헇b&vKwOohCp_;w]M4Pn #;{f`@eJL^unG`(R-4^m16UH(du%U+|VKuKxLoz40T` xř,D7;=2ܙakd;#X@w:Bxs*_u2=,|=$jj/?Sٴ O=K1c/`V_C܎\ѝ ,`AT?(Upn!?j4ԯ7t]9z 7>m;tjlaH!Q.lzO}_(%H멐ZML|-Dcܣ&tc|i8 <51HsY=U )RR nR-`nPuOD'E4y$(*E=Qz,f K:T-gh(m[4_iKҺSz;H0-d $@Oi>qbD(%AUuHIQC胀-沵'b1R?i"Ԝޏ d*B MXuNQ%@#&<5(vRƵYX6?t &ѿ%EtsAI+Jx91@K Ag/0~`CC3kCk&M0Dɀo}K2J B2 rh CGlHEQcxBD3$|:Q@i s݋ʯ8j KM{F8' xN9!jܷ%]鞋b-Fkxk;@cF30R7ګ{Yzq#,'+ xs͒…D-sGbr~rV,1ʰQ!)'Ibi".R%R۩Lq찐*cH&4FU9$@\]΁t72Ej 2HHreOu&RkB!dy=z 8 RCaK#zQ!"2g #ٚ~,%3dZk!QwP]I4qGϞtϦ3]!5gNe;"Əjm 1_]wU&yPh2KEB.i\BPnƛg{:22>jtj.LAͲ@s5{,b,('M/"ltMZ4 4?cm^Vo;eUi[*/b0u̶N^PdZ^0}p]./vTxZŘ c&[Dq❭a  (Ɗ; Xl]/6XmrPʱ~9'b?4[X/U/F, (z_\ZЈPNB~1Q/C ז/63N#IEX Kޗ*wH@ |ߠiz1TDĝ˪{6n/9uJ'8F0Tc謪J-]Nyr h%+\ Rr%O~i9未]9vq/bZA͈U*%TzAd^rXPc?FTpL~@ kb WBeQUjhaB+vl*u9nVmdœmb =tkǜ PhQ T J|xF򁕟BVY\q$Uݎf:7*ka+韢 2o^ ò+>H4Y<RG@JRT=*Vh'@ZcCMCyhHK"hKS&p*HL .Eǃ&dVqm-kZM]1w\jnzA*̆p-wKv;K$vdL/\<$54#l|ޞdL<A9o\CeTefrԒ8YEN*崜 B8p MQ y@EI&(pbMw5`)l톂&stoGTkeњ}Fj/"&HFcߠa[X6ﰦ^S׽ h uku: ~XhaMBҚxx5xheޢBh*u2A'O6͉ urJߡ!3&T~36Dh`byyŃ+|"ydD:+ \x?wUpV+H*.i4Ԑwd[{2od>|7zdcQenL=ר☰ORr< ev FtvxuPPREa%Y50Kƙ({5%?ʳ((:{QT248t[:(Yёoknx]DxcHĨ"Vn\|eӊf?2ԥ;MVW2P#mjcr1NQ@fԡӾ3jPPXY z"E0̒)i.^Gx9"mimUDŽmm tsF륈~!i" CjA$zuʽk6D%6ARGƻĮHYn U$ 4آ"X؞ l 2VQ miၓV E YMD!Pjb={ ,拏oKWtsN W^nV:xmw.J`x}7#*"@_ (T-qXXٚ3. v~(eg%_V1U}mēͻ i{fsulB f=4!"S{mD6 :8]4?WTzd"Ņ@ܔ{H4bVϦ|-XNHm_ TB)aOA* 㵖-^ \PBglMg6jšZw(:Yhjp"5Юt|+yML.֔мtlEIxu9ț4,}\_AO 9ěXhn;zel옵IYR /t^QFdwu\.{Rɷ':&0@袘_khV{Y[10OCڽ4s]ݍF􊳺Xu=&oِ,]m!y~P!Go<XdS7qD-$zy_^v9MIuBxΩ6ТLp( J< 8.-F:%$ }5^xa˨KM'?(%<NoRԲIs\Rj7^>B'EWQܷ^(Kl/=.~vV"r1܋,9T;  X :P ,MҿR9$*^)ke  w-!!RAhG&@5"m)xHi26ҪqQP2NƲaxxA\ վu?8>x$=,KϨ[6kRdB|Q !j^M݀˔xEh!б2 ؀pGAny-2L\q>`{rdWA.9G3iH1OW ;"L&hfʃ~^ޘJl=9=^pW].&ǯ[?;]fAz!5sMU?bpMK !\KUX:5I#3Kд$t{5ΊTBMG<[B;3uqhX ̒c¼jc~W{6z J5 lvq=qsسפ+P%)(\oXChP=”E:2\^I#T8Q5h1s󂬮i)(\ή}w 0.-#੧xY2k;>i3"bQ@%c|'{#&SҘb~Y~Y ©p"+ZFx%5&ZpfbzJLOS * -P 1|az %QUT 3+A@mRbE+"\#Lc3)-QIVڝcKᾁ&Ph̅ϓ~ew$*=4dt1dD_BeETC/"t-\tr{j#tLR}[0EadczK.a 9MR,+?c^:"k0v0d0T3j QkT-(f噳90Ǭu^;dbϑ*e C mViUl`X>r7Y'W~ 6I!`T ً$%:n$,~=:VErnA$p1x@r>.(+K„Tu0?paYhFcBYJ^*XYHp6KH7#pC2<@.ZZ=>Q9Cx2-VŖ0(ti)؎դ s$&f'tCgXgƺ ,qu5WkbBФc'tTM]ㅍku:d%q%PNg3*w&i,d= bnJ!v'Je{G۳*B" p b=27wT4b8 %%F?I'C5E- "ߺ%p7]R@3ʫ_ӑRs]k@lO.< rˤXǟCBbSb0\Pԋz]?7/E{ z D J! . RCb^o1;FW ɹXI <ƞˊI UY؆wcD! (Bn1F+~'` Su\HI “IB!g>܌.NH:̺GjDALP힅+ 7 tAC[ke¾(V/(~RUcpp*JJf{bg`D4@# ZE9ɮ!ѳP[q{@/gPȭ"=d*ƍav Tb.<>W7#ޡ:vs5b߈uE-JϜ r=ocͤ0|cLx G(݃rUFٽIq+1ӨzE&QU'TmCnvK;BH*rrQrC?Y *ɩ I L&54@+7PQ'`p1Z 7^*eb`_v:M&$H^:K;?WCd0zX>.\ K pBxи8 bΗI}=>,=:X_,~"#ZBM~A:_13ͯ,%˜$  5xY|B]=(;ك!d{ As͊/j@s7l.8ԡЧ4-28wU[p^4Ŵ;[*d: rF\DTH~ /~.!UULt5mt^&4cLѬHn*inq[.r_f.c߅DZ$Jt$&| i@袿l UwlZRF ww/e+5_0Ja#a[(@-)l OwlWzRM|nJx6O'[\u_ fRo-}J:Y[1՜*t-yͱU"O\~쓏-ф/MR|<_SvؤT@0OǙV T$j' W׋r)"i"(' [r=K0 ǚPjUhֳx"S]+%Py -n]x@6㮩TS^Zm# UD`cG.@?f.|Ax\rGDzM+q ZMOeUQ.s)M%"  zwަP}>(xa ކ (NF=_OJW>6߅cѾ 2啭Y^be"nd! PZu<~U;" 6<V2@ \!v#&&+3*H >TBSԺ<%QoS&('nR+e9kHi@*>@> A*M;Ly4GDihjO@8E%Ǫt~B˱<0+p`+MYI* @Eӡʶpb~ Ov\GOR7 Ƶ7~1mI2oj~w= 7V&u<bH"i:ȍSOSXD7#4[c}yAWXbo aL>Gj/*-4DZhg* mAl_hō i=kov[[젡9Fp[3 NɵsV0Ldc=u(p $_ ͷSMP ެJ6ży!Qz[ߧڬR{wK8 v(鈜cSbJ+"U߃7y`C]zFXH,DzrEyЖ<0B4E>Ke0҉pUΝӰ| q/mjTW%J SL79_ ۏȦP>ĉأ33nGu 1ئ(oIxiGŇݸ16t" '}%S(MVe@0 >`DTQ(09T)E\I=a**yX8C2?d)~˰9H]?C78(`]0ܔX& {=lh;y2{-!RqsQA7-'BkˤC%_ 5EGTIKQN+\iC7<)ڲ}u Cgٓ"a#P*}6}1oaF\o{Rڧ Hկ]l!y9.}8;)[ƿeG1'ߣo)-~B1)~SWNɹRA^9YAU_qLc+T|}\ykS5zx@ hg,J+ 0t俁$32rQnvl.`νnXVh>?2&&hFhHS¤|IT˚A[R.h Xz0,*g:Eel=Y*2,6t#c2,we]7#jS\OH$꒟~ZHWtfumWМt> 4~#ҝx]m}QF7s[C[Q$XL "'V}YӶ^[ȋQmxBW؝ lV8;hxJbrφ.OVLRj6mE$UA>ւJS"-:f՞T,0B@\Yn0Et|EE&\ upvp4 \`E56{~]]ϡ DfzJpɊ! '*)j [E QklKsO݀dbڣjL[n6#!\g D߈q|bFˌ>>;~W8,yvȤq'Տy2N2? ʑ6ק.]d`k@$mw 6j7..䐤BCqb*׸T!#^<{>97J8[yH0"ZZ Nۜ xtЊ`1.9Bćo^iet ,Q2:IӬavN"20VRRpqb2K5j{ž6f]fS#CGv`0ڈ=F =A6Lj L_w!Hqn-[ DgǕW^aIi1}:W̲V[̂/~r9o# il&CD@YmZuڊozKѪZ ąv3ȬC\s-O*8js%@IȂkļ N!,fTPE+ FQY"vgH5нnBdZ*AUdzi* T ;sVt5n((v D,xfa6=E]qAȽd4}l3%bɹ(nL˔+h9b4f*Al4xRz HPDjS6xj.Zj9$BRn ŝJo_GLZ /IHr@RjԴ8?f".[ s'DAZO " {{B%!+Vp.8?"u]KHz)e2UI.ɭg _b:f/r=$%ThXn]>F-~m#-ze޾#f09v0!>ZB63XЏ5Ex))J+Sp C\N[ѵi&mZU D82sO `Ơ@ag{3MlʓD5oۼUz1u5,'8 #D{!w4 L _ Q hٟD^F"(}ucco;B- 1xov+VJ5쌘غf5P}`C_qvR f}=dF@~P6!UTI}.>2 &јͷ_D&S+(ɗQp&bI~cϥt&j0H׽W\Xִs MktpO3MX`?hP1Eں[1*`UpByZL[1ԄN1ߣD"[B︸6ΌY7Kdlp0f]wds/$6Q@AfeCb0KW=CLeý4TQ$&P$O!{j[z }]uhu͔$Kq lm"ɇ G&Ll4l ZDW;7b)-fLhXBNe5|Mܭ/0PX'Fj rRJ≃Inr&c0KM|&&`Us1%2=uW/+"ʩX"|mӏs"Y1:5´^֚pk01Ur2CA(d!8sEomم 27VIe&5>6z25@:]mඃ90j$n8t7 A` r6Hz؍J31([ D5s \ӍO|]7[0F!2Ȉ>$ }9GOۉ=DIDi",|_[sQ"%GPbT@ZW0&4F=.S/G7,sy Tew ~E;X1+ >{`}MS qi鉓+w6lPBk-TE67z5j9jbN0c|}_W\Iՠ@;,QBwt?AdeW^+~3czhNajLTtMO+x}9N}ym߁dJ,4WLmL `-ƌhq 5{):ڼ!,52,_HTb1)PRZDB]m6ٮg4#J-~.z]hhhwS7 E'ے;7}uGs9)h<VXU16)[a * Zc)B9iz O|܅| ,tfXu'HȁXd+r#S7nA _".7M[F@ ,0{Dyl\fԠQ|{3n$BW7偐4|ѹMA+irLW1⪐ j^Fkd*SnhdBt 8m >O0t$@y/_sS}/:^jrbz86S܇E(ߑ[tݨMqmmm &C6[o, ·.*6;.;GAO6mmKtQ"Ps/A10#[rwV҂0`)NQRӸ`4N4!j3T{ 3f⡾4x'7Hm"zIu_Km'9%L>n`RL m>uv.7wPG{ ΏrDWXA/GI6vAiwd Νtkɒʚ?gŵY]%= Y >og|P*a(0N_Dq^ڿ *AR2"LL4r8FhFÌXDoE\yȆUP/l5*Q|Ü?j8G@TT^3SLld Īx |=it|C(0xuJ7ϭ&Q$#IVwy)_ k/.ع cPf}:9F߾ؽ9ISXv硑ozFSc%af)[i.~4.Z٪*ZDR儢P9Oq4>ZY̱swT97>s0˾ i <%!};;ۡMb@m@'e8oB # H)]V(~1k'u!>;[pX*Bkxa*ctiDN&F󐆬$:%gRɡ<3jwfI,[PnU月 Z:l<ݠ *r;PQ{pҎz0ȅ<}}Y5La,DSv0apS- /s/Er|ƘB32IGiD!3_iqaS<ͷ9 tT fՅW#J*]Z+15\$Sa!['KB1C Udepjvq bDk [Olb4^a 4yU`*PboG7h]XZˋͨ5=T6iLb쉨($LZV$&p^`Ty=yR> G T 8abn|ƾtV)QFV`ΥN7ki3Z)f" e9+V ,b[yHآg9^-1 Gq--S4-0isvCjd2!h "b- zlF&v==ʆ%X۫==$T ߼.H<BuD/@Q`㹍ͣR_~K'3Mo v~`;X VS*\4G" ܠ--Έ}JT_)ٿX{njR(=oȺ1P-]pLw27R?4,+({kĹ陼nxȃT$\ i3j=lw5A纍yGBY )U'e y3wΑ%PX('6!"QL*b&ŔǏ1E@۠*'N 'ehuIQZ0Id;4EPEGWIgˆ7Hbz OJMcq tX||Xͯ%2I05n%\Hz#|unmСJRghZ>P{a4@B@\Y yrzY𬅭?>d^NoY R_iګ֌`s1ԨuM)[e9TMrSx_ sCM+ :~EҾsALgE]U.*paR0#L@jH_=aEp92l ƪѢ3n`o# -q$lVUG#H7p}Pϗԕ Ln)O[J @ Wc;%ecW|&`_J%d&Kd]2J +hkM"#-A`4vo|-j86} UPg]xc[TŊ\sJYf 1҇UVjatw!n4O,a PKX);wzl[8Q\Cj@uQ'bZwGel(U"E5P!4l{hIm 2"I -rv-U`qe%uuMM U t@?7]L!Xktt凉l{Uu%&_KyP RYWU Zؓ9 \#>L9R``+aRh@,>t/G4=ulTsI4IcRZLfW,yJE[|-{[&;o5kK+J4mBAz hhSPڹw ܭu!5a;X_v0 FoY3W,=A~GY*HSgy]Yp9P,;\: Ba u?>@6m@qb](7n:^ּJ[Eٿ>.+{XPd&3sáY,_[_?i`F$JipLB%CJnSq{3֑:%߬~X|D1(S'W **whV(lw53Vgp$HL%"D i%aJ|4}]9]"^ 3"xd.;Nlt |7 '{Md\[S#xE󠤅P@O./"k''> &S=+F Fhad@>Ow Ҫ|Q'U>ӜQ Qe__A\@O'Tya?"Fn:n[1yzОs Rf>8,iQ*{7⚽O{, ~( .ưL+?e.%yJ.e 1wSU;"'f vKJC)~>w{ۓ"W z_&`zybK>AK $YU/us9fw˜RRR2}},2nlˏqVCS #daAݿ^ZZz9I ])ɐx,MiG:&8m(,ӊdj#${߷h`;FNn|7 Ua?(sj_Ilؓ]DUѷ[P(ٶݛ+&2=mWҩohX~tN$F`Jm=jԧbhize <*u8:Q/xPCsj=B׈=O!V9H# RPxܿz^̵=itU$e׹Wl2Mc 1چթ xռZ( 8Qvaߠj?ď VYz*z?xmZE= q[U7OT6P5Wر~%//aЫHLr)U(MKGK Ti*P3jJ[rNb_)8_I M@\X$Sj`9&?iYRpĀJ '#5/sAuxݨ,̠ @cմ E-7UDxg%).MN8w:>w7lL(VC<x;i9Z86!rb"/;Q d`J,ECvsk9&i*e #G1v pӦ6`$(Jݣ<7JLtFFy3٤%c  i0IB4MB2U,SP.? Z{*Ew1f'%%p>]QΪp"!g:vߡYx:q-aSI}T|mTdp:Eւ`,|h s[=}7Acgc5*{Ezx„6뵁Oow]ȡF4X>mlH4)}&Lg(ȫ NU< Y4D, >z $,:#y'Ҏ[IPBznv\gݖT+@з]ף{+o&rQ!U-(0V!bү{Ц-ٍ$/TAcfiUsz*QJ9 N*6^9 ]kKSGd:-E ( Hʪh0{RNa5;;2Yߧ¿v\:1?"Nlns2u P`LDbEv)d*.*6;/Q9.wm^u!iDn'ŬCz_4UE < flUBt!4u O=KQp0F0y^Fcbށ2{Ya")\K]q( ?$Z)R:`W]1`@S2~[vB19eqz;V y =u1ƪP€j T0jF?(UPPRR[Kg ln>!GO_FT2ms1C,SI-\j5\5c# =u|}x蚝D&> $>ej5&> yAQ06Lv@[6#yeiAEӖIuJL\1f偍O6iVzO9DzJ]8rdEM_3wc<>6;$5O22 Lf`wm3tDt:h^@ @%(]Wdv D[U Q,}"]M:y fDtuŽQ @.7uN¬Ȫ^37:/B1}PliU hiuѷy;trmRGiC0UbPn+]1rRS$~#q @uI`pMpczٔ@%OM Z~ih'׌ձ{Džb֑6o$u6 ]76խE-9>#š)] h>CxHLscV@١@wqBJSb.Cf7(02 kc̊`'$%))SP5QT&|E'Ѧ_sC%̑ԨV$҄R"JխuJ:P 4tfcCDrTE٨J'`&Szzx@=t./7fNhbT)xҡv֐q4{kmr8D?Hkɓ/o!V9nե KPT  }BW/=SĆ 2NטGw(?s삥S]} wDe !sY&*?$[޽9Y?p,EGF7=t4Sp‰P\82pe~2Z!QM R$G/Ij0϶Yޚj*0S$)v97mDw*ƌ:.7q)1^'V=wU$EGAH“ZO,C.5>vTdF~A07(41f~ "&zP/xuFipzl&f=S.0Q"Fw4œzKd5Fs,~[ g0Ue zJ~ 4aP2JNmcqH~p[{C6o"ZRJIM0PM3ObAK6wV p!1݆Y2*@_Qk-R#9O&e3݆d'8 Gi(6W>`~[@#A Tpq`617 #\;UXf[zB HD1nKշT'Bt5D7XZ\sWzsl޹\r4߀ȱۇ N0?ʴmzE~< vOФBQb1FrM3: 1|h1,(hpR sZ8Ҥxa5d 9EeTi{Egԥ&*#4؇@0Up!|a}mzj txqMmE _M%MxZ:,H/LHO]Sv'@zW7֎86'7Å34R6IÃP Sd Q894߀xc" m,˂ BfKsiAWXx(U+8]Wv6[awZ[n`‹mr#Lh1ﴫ]Mu@p~sQD9{UX~4ȹ83 CDHWZ$iġ0Ȩ$(t (o<`\ip.X)}NفFw(p-&B:CpeO.TdpW2GFotCB(jޓg+ַ/DgFp7\EІ;@o24/TWxm[a)cݳ,Ceړ~bQT gF%}UԳ':YRS#X{iZ`l+AזK>UvydFOkPrHbrkS3 U*ACs!ytUwSn#DFӻGY<#8Z@3lw[;0X8({ٸĄWMhǺISdɐu哯fBhaZݦ5JT$tj FMPڇ}sdk?iBDh$.Wh3Q9\4*ζ6-m^6.`7{%#j-}Q\Q& 4jΆϢR+ J> S)vB7Rc&$U˷ h=~UalI.*Lf*, FS-,,Jܵ_In^l)+&NUP*I)tb-ޗf*A_-Gbc/pFiPQq;-Xzf+E9fkzHej;[Dݳc鱍b ܗ=xW`rIv+JrW3U Wı([F,)ˢ&Rb9ZJHݹSf \{⣚B:&g; }TKϠbbv6Zn@+o/uY* JLsw5\FS3 Jr\h+TdDl lrz4_Az"~t'CCeܡ=)ЇBZ.4Bk8SrGE؜yxf@8f,.cRSP֕6퇅h4X‘USs VOSp}j5(? ]L-m8ަ;f:Hϫ%h销Q[,p{y` Г;z/fD/~VҿUe@L9T6n/#Qt\R)XR6M7CB*PPzڽ֭];cO7t@Y,5"gy'B;:{ВETTP[ݦi\Sq;_omC\²QWXoZ+i ˔f&e+YJPV-)4E!Qa/DY[*K5ӧ ql!+B]!bA&ĥj bXWGAR Dѱ+40cRТQ#ue]&%GfO܂[>äuL+G rJ-z4] ξ܅;s/ʳt5DGrʟ_1WB/G~ *~뉃#,`|{򫃒gg38EmGU [6Z6EVk8q,no7=(UAFE!c߶E 16Af^QV%|p7`w=eVԁ?s)ෙSn4X!3(籙iͱ%zIJ0%eHKrH:, =lEs=r@'CmS:8h*19ڶ(h LfOui?q sxğS|[Z.5wH"2o*f_gvy-J{eWuLٴ9Yծyh0yYgʹ۫~g؁ &ՒPu/jcz84L5L[%.QńgD!!;\aO}6G* ˄Ð+ͱc~C[]RuKأUmK9YMGNc@E **Gv" `Vɡw4\g향 oQZW<Y%mP?3`9YaѵdݚURs3ݾwҜfx$!xZzVnr!jd8̧XႷf1%jTb*΁qS+gz9&Եa_sRcHb:+!f{Cju+8D{$jh>WYn!=N{Njo H(+V!@:[bRޘvlǎm؛^{ji12Q>LCV~x5[1a̹w%Ҩkm݆͛KtM5lu2gGzJ*ccxMgb  U@F >$̬`0w=r$ɵޯ+js>'flF~c*v%Y@wscǎb8H?;|PVdr`S7_ [$Evey>vCۉ NϹCنlU6U 98 *Z<,HuffeˉG} *? ِ5-kd?kd7taSEQ=3@$vZ"31R A*!u6$3Ąo4K*,HY(ɔb"/E[ ㋨C=#HQę@ӊU WOK11ņlh5}z2@1Zlԩ# S休RL%AP3wl笫\lۻIk{yCTlKV$+Z6\Mm\dOK)%g,\ H"+*YpBA$d:*&=tMIjtaډ5Q3G9QB5FS+DInM] eRzO >bZIЊ+D"¸rR;<7Ѫy"!`; j ԧnR[#"dH44>o+6$kXBj`ǹ&t$r{ZԂ&g,Ϊ;,4LdIVDN(@yYn_ݮ'BA, ' ,tL 5g)d[sE( uci>=bɕ2vOi -c ?w`(6% e(18-(ɖCA|()sA9K -X:\f.3!6X;[d>cgjgΞ2 ,fv%M(Xflͬ_ͬʆef0lۘٲ1.3[7folZ̬Z}03W33l]xfVI;ﰳicgjg1CWCL8ZcAݑ>o m\ fhC7n m w~MM0qci,GWXk~u'kYkͫuUb^UX**yxtbuc]ƺ<ֺis;6qcZƴմZۦu% +L5u2joָ1a5iZ6UmBmմǾָuc[WU{2B1"TNWI*+ȳP!*ڿ P<$uy}Ʈ'^Kwc81vaZ3+D2{cߌ1ㅈ*ymƾv*sN]z(0vYFJN, s bWAe9gV  ʣ#]nuگ;ڸEys;~τ l],Pq3+l]غoo`JVAm[кaBꆬk Y'-d8Dօ Z+N7hkR0EкB}7zBRuihsuY2'uuoغSb Z.|Lк7[ӄe9U Űu~F [WEaeCހil}f񦶥utP Zw Ⱥ,|[кWZ7I0)dt37Z$arx [ l=ށh6 '| [nu)#)^{WG[".ML#]訜wnE +&nкpsA Z97\N ZEк2Yu6 Z`mҬ15i\I}1˜:+ݭະ`F=OJw+*4aR[f|׳q0s[&fD\ \W4%peZyڙZN%cy9u0]W#Tҍ_ ʾoi g n:;_WJ|=W#8Xq %a0T*:D؅v{6}Z7v7Q|dJTY`W;=1몋)5٘D]6q5`5dJY<vOv}zR Vأh\jGYv-+#`f naO*.]ApPoC--̛Pb1Gѐ1т='!G+%i{?/;m@V0)nE@Cb pwo Q {hcJfT!%*Nl\M]U<Ȯd@~>kx"s?L&q &qu!Nr'sjIv݋P :G80 O:a6+ͩ&0 v?D I1!EOWۖv}sђwl6ʜG$Y?{]/]55{<潜DUJ;?N)OniхIɣ_ )fj)p$cM9 Sϛ;kCʺ.G3U ^&5/ڤY釉}q{kZlr6m[^զRhRʦRU\bTe-eUuH{R( PU"mJFS`\7jmMYbjX)ݛ*B: Iv(K-?m?=H20-e,M,(\^.d,b\OZv#h$Iˈ5Ē%LV}DI@Am^` ٲ Yl%@&z.,d7x Lȭ7$6/Q)S/>(Rcd^)H7F SqhR.Wz ÍjeFL(syZWmwa{:LC1te q] rپK*OU[$`E $hCbq͇(d ,Ss0x Uj J̒SC[,yL*Lch!LQi4NF A,VחL^URBx*#9_^-٬΄eY]0_^%p2tN 6ActzfݛBn7(rHW1 oK9]i'xZr7iiNŵ`KW pUiӪ4tZeff7sn[fA%vV n7\igQ {bj#\PӆOI8GWa*R.Z1jαbԂ0 No0jeN F6 4Dݷ5G5jDUa\olD I'aQ-u}Q,֘)w-vM@vN)mD" |"mwB]o#Z-MW Ati }-FMxbtzF-IQ16w zc8Q3Fܳanӿ EӉP9#LKBe dXv BV_, Ogu9v'-[+n:a 8T$]iEBuh$ &%RnŦrjQ.pTo2uނӜ.dvV}~3PM2w-:A7mXdfhiŪ;NQNx=>ZX'Ah)xZ0iݬK{ħwt \>Ok4+-Z4q>O֎vf neMY?7t,[ncsg*ٝƦMzAS"^شNp V*d:O{Ņ-2-Iȴf*-"I\"4 4}=g1.!W{"21abe֊-(͊SfICq ͝Wm7R]^5a)^.VP%F%HWSDRI4 %R-]Kӯ e,9+R fHTY> +TKy@U\2ݰH:yr>v>5JϯHwFr!/],R]#k291]gH{֮ƙtR6ɢ ]#풩T!+DPE.#ume !Jչ !y݇DQՔ[N6vW>Lb3mSIҭf0dKLb?B@AXO 64/VU3nu_+7H-qB)֠`ud'fl~ &<%U뗗SYM ʥԦ _gfizm%֫gz\5Mˤ/CjZYVF"0fzw,T,g|kbcov9iB:&%+=d;9IrkwCnevlUKjԡ0iC0\qS!J_jJMQݍԊ)I|ꄙKUX+@p~-rV+R4GLI<,5Kq/. y"LZSaBk`Ą퇈 nMh xg mƇ  s@Y!K=@s@sL6ɽ0QmN[5VlEIB1*ǻ% M#Kۀp)[ƜL]|eV38iՁlefZQ]tbzfxIdE<( nuqTҬa`qtCJ ٤?ك[%Aa*FZWuXM&f+/IEW辔(zdJ=,^|J9B%'"e0SK 6#{L>BIoD\~bѦ+csvnEh0=o*dORyM>*Nxk H9W o#v3ӣ Qv]{bC` КgWY,*H>ߦf8SdBӒw@}k5PgU5k@_Xz33E!)ʊw>2™oQBl-`( d %u@R(U US(W\{)%GcO&-(_ ~'KӔ[i"Q. g-gr0ZβArXqlSum@:RH3mS5h^wy߼nF*\0h]r]DU,9R=kZԚg&DɑeL9n3^Lz7ra26{RNn-"W MDVqpZvk2hտy `D6MeZ9FDaUQ, M#"({or`*m:tw1h)ӟoↈ@ gD~G\X]`Г:tFo3nYu&A?B{1d-TōU˕["RALd\@w@Y ^1Sy KId~1†ZP(|dTbT7qrͶb95M*J h_Ź7"l*H}aS.#Tm&*S4ɸ/"ls"̴ܴ “\-UuZr9!|U/FN0dI//r|'Jkq{N >‰b9 n2vE7հy'AQ#>!>VWiZ9[-:Ccvݖ)ۯȤSo5) tTԐ0I 0WB+7g '7Xd*14O~v JbT&n(N e7_ fWÐD +s fq~I+U\_#JW+3n-VQrU.IZ&c5$h3  zwn{lbIn؃23c/&4HvoVs'Jc&BxSUJW9-JdmAÜSE nrLyeϦ'jax<{#7Ny JMb.l jiIDv_E5R`p&HM$`40 D~@~^.#5-k&nnl}I&D_Nf1qߵb$9ZrnK[_`_jXbŪPbZ1{t ꁊ."$źq)' +֕XQdnLHRZ޷Sں[ðRO,0Òҫ7,3ֈ:@+$&3TDW-2 %HP`鯊mjkQdwӨ sMHpZ$˄K<4琅n941q04gu՜ge#}I*/:fWoiÝg>avsz#W25۪HJf()=$DyBmt&|LKIZmjjt8Q4jxX*"Heig;0a%Qay-#,'2%B2DC _qY y|v" ޜ 3.B*fP`*kc%o!~i6CwV{A."c,,đ溔 P 1g| ݢt(3Jt ,u+Y]1YdH< U/Ip!}>hjk+{^yTJyͲL{ oʨV Bu,R+l<{fMWo Ά&kߠ4g\^2û벡kqb&n9*5xBͿH@6-):yaJxkR"7i uc&TA5M!R3tvG5^}-&M9R׌)Km rTOz<Tl 3ge'| !U`<paآ HeT`"dnhL6.e-S4Y#7j'RjNO\ܨa($(yyQ¢]L5HT$AQN**GO,eJ\%Y:HӋPBX- !he*Z'|ZdfjN纗ZԢ=PI(wIx5`p&THdɷ h[E$ڻHEĖOӯY q=΄nnu'ʈc3[!mRƂDU6SdkUD"ڙo ʈFm[qpr ܧշty",=.5jdYyX]&Uce7{$AZBTh`)\Љ+!Ek@{I3 EZs:t~dܪ,W|鰘 {Bqf,D6o{l?8Qc4&07"?.A.|Y_O15✊"&9X6QdԷTo "i41nxI/j6PA?.{ { UMM\0$c "=7)5?%Djwtfh1je N- )D)T / ;&ۚC9e$/B]َr[+㹃'fR%}AWLQ)7I!Ms*We)共<۸sLtIir}+QX6YꃕO[I*HoO"' K^yVA40T!^ZeTCr39 1L ¢ ~;aq;rv'󂲎*{ް&/h/wcLv&-h~giIHE^83ΔcaoO˸{ڑÁ7üF&kMKp_႓5W {݀64( U=4,#UA:iY3 I) Qe|jX<&m?#\ko,ّ͢NE?K)Kks! ӡ ɚxEG?]mphѾɽjF_! ^ ?63'0PPpKhf<GIJSVUsJZ[_uY_S:%*IXs7X/<lqeuQk3:\]7Gҋ7\`aG mo@f˯)l?sVwA[α5AOl]CAcTBe m `@]VODg .F[M͙ÌK媖f[&SS@?4IzH&)7!+Mp@^5J\EiwG{xbkeJX3NZF`+* v =ԶAa~))+2cCFT@ET lD**06ETxEhE=P ++ t&t:eFV@K /P!u8C4@jp`b>mbs*09< Pلoo lD?HS@ 噞+  %~;kwLE# ;"ŐN) _KQ@rBݍO{֓gTID9ͻeDω4bYvI1~}[QlJ+Yay\I 붤jtqMI|vW% I1+iq U_B:"Ԛ᪡HdP>MK$V r gq-.ٯJ.»PQg]i y)p.R唦7`&Ti [I.~N47:?j"Ƌe`MDmh{O[> 0B}Ywz$@+$!ՅР,) rzUT[-wʔߖ\{@D/Gۿ޿y飽Rdr.t50L#v@K(Tt}Nfz|>f781GG5;|iX!^/Uǻa!gخ'򞇧UgG8x۵ #b_s5u4';QdcAn3+/+8]-ODDžG?.q0 vq7"1BPs\8+43{'̷]pɮO'㸃 @EQ}׷9 $? (M\1C8t~5|8?0ןirѵpH68P+3E|%/mW$}pbf>T_ oq,<~,T%x |?㸁r|>.1b??CL8vJ9}k럯߽ےIcf:+{l;lkU ̪sdI*"-Pp ft- )")c_ZKϛzNY{@<$8H BmLrLQ1 f>>xsTKe&+lɣ/C0$0C|&AQ# Pa]9n2iC/hLl$=ML݌_(2ʾ${BP9NdlطO3::I 7@6-b9D\:hJ~i.$›BYV<U{k:cmD5pJT%ՂKz\4 ,|#F-ytҭ{K1L=Fݶ8~0d@l+O47 ;sMmDA'=!#AOqD8f=<4tTνK3w!͊1Lco3dօݨ0R$ Np|ϓ 0lw!~Vpw>0[*z@㙌AOޫAcB{C`=N`;1_I-(mCŚN# RRcV@Y_!q|‰9UvR9H,'mLelL<_x[TSҙ#@=#ɱ Yz8@V6K;UuޓIQpV#+f "zVRIXD2e W:B\*p:tDȪJcp0I:qaI3n"@1àpшvPL(xF\d9ityV |06NlcPEJR?5p#U6G,1X,brd+7j86 .JSx 1EL43OsER O@hX<ff F0jI6Gsoq{˶#$1ni< l2P AsM*V D!@4pN.|"iM+I\ZK^@v=E]y$"\]$ڋ}ZmdD` 'ÍK4qhUF$Yn'}@ *9L5:E{{vd?p|Y]WEK!^SÃ'd]۸&&<+ DDh3$DfYvOٕ /̮[`M B:N}'nvf*`q[ö:B'!9Ռ1Щ=%;DalSJD\0Rڶ#$LffOeb~!"a*fÄ.s\t@O2L6_ʇI/V^'m~lz1J 65qwôm?H D*:{smT=h_6)Nu𶀐 S!q CuS2a-cFRt 2MrE(qJcLyE:(9zH >3Ew):pȜ -q;%K}WXK$x.# mj`dT4$'TOep3"u &@agO7H:7fN=A$eDD'~ cXcR382pcGM~r<`B1elk}HjN \:`- 3qZ̤&ICG3Y?<^*I=ae7+e#BU"C|J,&eSEdsPL #N(2a@ dn %YIyB k 煤]WOҞ{i>X0-z1xa*Zx$=`u3bp#xU*P.v`**N,M 34pF2i#KL*-l 0bب' bc 6 I \i 5B!Sli`#҉rQAM1U"S[QIU!Jr"^/4`c 9. 1/bƓt`|,uBP*<:ߍH2),+`UZDq dEY+ 3!C]K=#j8ǀ5X1X?P6Ak.&n.'.0_|RSU"rO&;KD khX( '*W,O)BIK&Ҕp4{$ADkJWcDA#elic87ʢQfLm!Jp<RUAĄ;J}vN1ȭZ7asH&%RcAΓ^sL`u#P*1)b6|E1V7 ~?ޤBkJċ8U H2c5D92I" D>dL:J@xa v2-k7ЏTFƀ^Tfjюd';ԙX҂tX@pcqɛ "&uS "wa@H,C., z+b.I Ԋj[eƀ9*oE7ߢu~!RE5>(wĨII4c/ gh P<w"p`Q։g#'?(]g!q仩.9z<,-]+pPdؾׅ+؅ +ÉGƾݸccw˰YNԎ\LF .} "*8m |qY1rcV\_gpc;;մ;gt$y0jFVá \ե NO0UiUܰ@ 0gt@w[2 CM)b;Ҹ K͢>". Kӥ܀4I4YLJH27֤LVֈU@f3*EJ_k/[_8mU "Ӏ!6lŸL>1.՘دG7 >YfNQ3 .8BBOnE h.5ACkYWxRF%~7ؔɰ6*a eՔpHaSyF9cWq6_Bfx>+8danLN8U&)@3l#=eu3}0Ή)$ŋNE#9ZYpҋW3reG 4-R =^+](Q_X =LXQnj!^Z)ܰn%RdY&"%Ʋ]5BAtFaHRtPRfVa лOXҍ$oFo&uվ)(574ȏRڱ3 &) 4,XpA_~/FR\f dr̸/T<}*ɼd= =]L6V0ڴ9ᩪQƦўDc[)3:(/2]JOBV ݋cnR &xH$ې%]TޤIׅ݀RĪn1h4΅@o+T )碎1@H<ly= "rд)^ꡙUkeKBEEx> 1FXUH3ckD ,J-r{du^nnjJ$\I%q_y1<>>:aljˆ= YI /F6doMbRK&R/ P X='QĆE%ɭAnDT&YaǏ3x Ȩ M,>1t6^{GU1*9DHȸ ٳ8Yafuԑ $XXuCV@LQRQt7]FvFj ]CKH<Y@gql [E2>X_کU@vWziw4^白OڭJN <VD5/rr4A06"ij( W('67CWFOHY ;*9Äɛ#Xvh 9 |A+l)  @#Y`x%;+N,mHLwW9+s5X/[G wI#!JYAyr#E*Vħa=Օ(/δ;zǖ= FҐ7 BS]4auM$M@c㛙1ou2sN"V3[J0SGp1^L|` 빳vb33S3Xς]RN5BU%E->ZB0.N9D Yp6TFAkE•!b^YE[:ԧ*R CB5+rnqaai<)R;z*^A`1g ;9)[V@kCS<'C@KD'!|pgϲa~!헤;AWC 32ҟ*/^"t 'J&\E| Zמ^4D_7#HjKdh[5&fċ^LzM#R\.Gc + R)8Fݍ<9pAXdˊCjCR aT10WoOd H&ݦ3EF@;TEUGi{L֦63SԱhp%J7~dWP|;1@ڒQ&%ճ%XZTDhcp 1|(9gy@!2#,kSJԌ |1]ݢhQUJFi#b/`ё%v,*TLMF>2? iŦeBf9Xce<@y,oƬNh1H;,?Rr$/&2 wt}Cev7ydF(SGX=D[fư[,zpDMīGdtHwgijA5G\Ꝕ˙ :6b4pq1-M,;Aɩ\ H哀%aզO}E"ldޚ8) bIW'T,A\ :D7Y1B$Gu$&bY$R*6雧:ؠANT`Ia#@@N +R[M1w#IТAqdܞ0yjkģJ7*ȃqIˤۨa'*[5, vlj,r&=g'm8T>7R$lAY`pJ>eI+/Wf.rN7P"ӄ< A&Z [&h-U"ET)NL^X+|:GLibsǥNI ޙ*j2( JHf'@w, 7;'f95ơ~\M%Zkxa7'ʪs< Z.>2FjcdUcW9\&" d(`2=6RBQz\^6a>G$mjU؆&@5ݙ.kUbTK'5wԬ%r:h\w0u0c)8cen=R"`Ԓ8,,[bQ6TT(Aij>U$GdtHˊΥB׊&HO3fg ec<&4YXUAx>6Y$u$^6F( JM?J`tT+G}@g#8576]^.ZD?Szp[/* 0Aʹ\Bg\RzWpn#7~jDlO9eϴM@. V:tŰf< @rZ>OgJMu|?q؏Qv[Ȟ2#JZ%E1l G9P=}T6WҍcW\q& Wx{@, A0 JwFDZ&?t>\i|;>?\,|lJ[1 nr/@vz(y1@6y˾8E-lTWq0v<Ǫ??GV_ mE&C k<cMZk9$Xεi9G<1<),>%djr_&춁ʌΞ(bCU #RUO]@8ZvAa/M- }{; LW6A-WGIOv#i?TI~ z?r;o~5@L]E 0Sf㸢G2ք*p|>#Hn~"w$(~9S@sp<Ж1Q<2*sWԸoo}mCV㌽+Pgb8G_γ7IӞPsewˇ^Cx>sUSF K {%*9LjzL\7̋W˟0tʴз8؊*χ \WxЌ>f^$L,ŒoMמ+^ft_UbX6><WKA.Ǡ}q'}'A) h<>Ta+ YV)}q<ǭZ8Fo\6 2usA5"}n\".blaW@" LB*r|͠NA&y X(@vl&Û+8F4X|Ѵj~6rL=V0E;GQ@gL45 Hql ~UBí=Dǫ`M;ކ $QSo? ;h3,/Odb~XȄ_."Ly&>\>]?0(fbmzu3޿p/?|_ދBCoո[xEzw~Ӈ?]wwo_4G]__>ϯp޽x=z|9Rxy=o>߼yÇr^ˏoÇw/ƥ{?˛w o䀈m*!Oo>oKoͿp?g\}g\Ӈ9g1AAD?Χ[ɯxyOOkYAW&^EE1Tv<?f|9>.q)7~)~yvͨ.Ӈׇo(~.O-=#{SeMy*a Q.uO @C&BcXeO;=ak@?gp_??\}zw]Z[WF}c oo_IJ0xb@Ď2;o:oO% ?ǧ?^eɷ7o? |ÛOlY;Oĸy=CP밬,OmG?y?ҺWCy Ogk7~i"=p8t7 4JT/WOR;H#B Wۣya%[4/Ȃ|&WFb{h(g (}/s-⟣Eaf_\SfRB0D;3Q/<*-X =w٩VVod=~A歋*aUO| yB,WfWfۙ*i4 adok=z 4is򠶇KbJK11ӧ?Oz}S5I\?ڹ :?;G"B"D(oT>McEqh~n]̷.ha.=e-~-BsRGUx>scEjJH 'x<s>>ws7:ڗbAfprp/#8EFc3!%Z7/G#ϝz%w=F`5!&ߧ L˼y)R22k9yDvH-ooؒLshvthuiBhT\35ò,eſ7E.o"gq| y4ˏ=Gd/a0^%e4 V|ǎ.[e.x<~pܥɦ@Ze9|yj9;:Ѳj=h B,W NJXp̻"ũ0P!I/4vqԛT?Ge. !݃Rl}0շoDsdS)PtFD[˵<*{/|?29(Ġr SP.x|̀,}倬\n@dR&[K$H쳧!?zΕVB0@`>scisc 5!Og] efǚ@Md,xمZ4_]YYwO#2[޿I9l=shvA hhw,B^?dXLtG]ٸXn&+>,^d(vF㉏'E1|gc}/YKv&(,^f4$` aF`3>6V&B1][y{'~ܳs>>P=)v>1I.xK|L~RPKp5D(fRV Fortuneteller.fzPK>Rfritzing-app-0.9.3b/sketches/core/obsolete/H-Bridge.fzz000066400000000000000000000460611272363601300230020ustar00rootroot00000000000000PKp5DL'عK H-Bridge.fzYs$`p^oGmiYĝx/ .Ev7AeKRUDfe"$Ihba'x /p~_??/O\bAX!oE߇绗/o_iO\ $#s/׏o.ߑˋzۛ]C_vlc|Q^R"z}aw{q/?=ǧ??Ɨ7׷ݷO=?&yxx\y}W=ſwyOZ>nW ] k,[ xiv÷?~k_틗gkT,u_jSn}i_/??=qR~75]ۯ\/K+pzV8~܇OxG avSؽ{oN;B=b/ԗ_K_|?o>Ǐ?y/ןxY/o˭\!~ pUioƶto<_qyEHrsyK60_H`lLc۝:,$GLRnG,O#=;<[7\еK~e:;B;"׬Om <78`|xd/g,qZqm\/cyx_6 QDq"u5L7aIS7oo Bp߅9>6LR>aWɡD*gb}]}@b|RR_d-aa1ܝp$"O׿OUD)+W JJ,4)!I6KCnU*b|5,zD(zD,i1NZӺ1ߦm`M*V $"i;Ȟb;6uĺ;OIrǤI)LN(&D$ȓu[%$e։6t޶ jaVkF-M}eL^vr[)LL[)K63F%1v.H)$P[Rf`0ZD~ǐ\ٹ1" : \~BR{)яV\vB5.Yt^ARqAsņ7V* ;z#ei\ll(y)1 [/Hu.-\NZ=opsIaS5V Os9{+ 8:)#L$}#EEd# 8]5A(X!.>]϶Ta9pmp߹0UqJ}J֡&pb+ Ir5a4wz@6RQ8\Syw {"J]a N~¨׭ptd+)}Hv1\0*\:T.F6Ri8n()5L@GRm7Ro8(ٹpɊ6vTT}#UcRy8|k=L@R}S"P dR7Rl 1 WՆRب7l|S_^o׎_-_r7z޿<}}ݿ\}~~}){az?>~䇫/MzO_?UըvT?]RU^lGF/hůN 2J5 1EZbrd~KVbgY|(x}~.{W;Ono />?|*E)O_o^ׇmZ~JEưHFd~!")s(CpZۇ7XyN9+}׌`K@[R0UnY:np2,H!o*bEkzm~Rr"D#Q ŢBN˯hxO5Gy$R,y Rߡ #wǵk\tcbNJ7b*1ʃ\[3zj$tu# _ʷ!dJuƘB%t%ObLӡ[S1 at !fDlj11\> Vx d\y)1J@P̘;Iq@QWN|xQf!UOAX%@ۈwU5p'/^% +GsݗycbkVU8xTujLZ%:EݜTeLxSJ0%|F3b]1NMUS]]-SsWnbYmEewǯoϰC/N\qofȍ+b^( $}o( m( =jix4~#Xzao05'p[:?3NZtPn0xF'H00.J+7PI7Pz  w624y tatPm䡀w߰K7O|Bm~'m3xt 7I>P|洛;' ^D,9ap'A- 8~%`O d'pٜru2s~d bm'@9^&3<uV voVb(bH`/,[KɺU`9[6A*Ct‘PYJ *Fd"6пڠ/V9up8"hsJ>Sӎ ^ft60Tp4UppJ$dz%edl-%KHNT@b#UfMV5$g'"!i3pSMZ Q6qw:,b) ^OI`" #LτM䥤l`pIiA;A@hX5A=eVdl-%G"NSjB3L B$"4p >|ԟ; 9| ӨP})P}&CZ3֘BZ|i1k݌Yoj?, Ԁ~<&F::Gdc2]1'! Q I@RRݙN姝=+f9ww`>Rq .TCr>ӞUK>!PETAZ^ ,-]XWgh\ʢroIF6{9]ш,\FHGNW<ҥ4AhtIZ K[t:ݼ%HIl;K 5K0N?Ȍ#L^eԌ9$MᏌՀKy!l֔9{6FL~dY;ؘ9W1y*tX32a:u# aj0)œ* Ɣj!C4bVI4Sr/%LP5u_bڸ`*d/zh*<0pBl %?dڐgeiv<2gk;SetnU5pvn@`59SLyՔB*~hǨ=w0 9g Rb?[a&C0FX+I̜9lz>dr(`5D[dKZ*zL;pc܉0gOʄ0ݨvtlDH)iQH׮UVi}fL1Hd2Ty;$X9 U11w#)xdⳁ!z ̙#Ll\Oo2V-Vpz=iY3.íW&Z+r^CTJRhFNW5q`J2)^{&f]E__&t ~FXk6(ft l7 u $#.١K ]1ܔ7P K~Òo +[hgth\C qyQ#QIkүI?Ѥ_l8,D"z+tgG0\3Q3a2rmѿ \_?N7g>樑{{_.6hs?[ȑB')̟L̏h("Zq$//'_}uZ??}zqc4/ /~Q0/  ;~fGWvC]0wPx/A$i8r!cL2fWT+G Yw WW0W-FU+2k/zYy@w,GI07[>ĔYyלZJV v\,Yy3Cr^t p# W_Fʲ J^Ii1Ѐ!mE5r C!㺜;F(TI*̏8TٽX^^6P CU-q8byb/ KFtbsTʖOTH G8$PwMap%el%uR(Vk#od.I$)ZཌX(=D, .&BcJ)*aE&-Gt-D~{Le:Př窖tih[VG&͆鰊߭^FP3QوM~!t#tX9 WLT=Ba;5ppׇǧT?~N',N-҇մ>ƠE(U2z(9\M."ƓC|i$5Ua/ß#8ԔLV#~Èj`"m5yG56f2C {w񙤸.5 jyx~zXmχŞV)Ş;e:1cn+%׹jgUSYjB0 !^A"o %^f o ePx¶՘Tx*+ j̫~M{@fٯr%/3_F9 /m$$^o\onˣ3Wی\ ]ro. q!.`C?PNxQ^$opa6ۛwa]QD0νJ(7-sLĸ^]Turounݛ>{s2ͭ˧l5^1@!u^R~Kx^{^KK'gə59sS{i̡xFFvpf7#L`ݳ%&L{-O\J6$@ Ju_PKH1-[Mpk$6ݼi`:dℏćL!AgF#Lgu$'yxOO؁9 Nvcr~<Ɉx/+ k;cFN)GNWAbgy;RYdM,J,z’D9[3s'Ғб?>ahN/q=Q(gazB!{ͮ0;y<ړ%ˬ>α 1Cމ<RoU-.%Y!Z=][% 8E,!C{ ŞT)S6{ξʿƀO <-BeHh]ç4F9\&?g)@7>,3<-:#)lgRR&Rwu|d\J2ߙ*stds&kS#n}%ǥ?o/ GFlEhFHK1jN#+;.2S pU%7gFRX} {!˘E+\]fe+N/VH.#PVBTgj3>;TDhzC2LPVVVl(noUj 8= H.dP>V4.9ee{~h xãBHiGXrэƝ9c,xwr;_e2p͑~$t$ ov¼ '\7oRafq‘%ZHT%mH MU@>p$Z!-r@{1K̴Z4?:m.y婫o8THӢ*[z EQIAQ@x v;1r`#q@no /3 3uzgQB=2+~$1ĂfK|Ka4QqXy6mM[x!!=fEe/`"h'%5ξD04xy4AэT4trYd_R(f0\4 -_&!Lf2h&]c&l^klq\ D`JaJޞ`#ֵM5tRgj-%f fD/@R1b' ^Nb0#%oO/ "K29L$&,%bh rM,CraF3 a#HF ӫ͖ X2ՐmnVm\F 7RFܛ,q,,[*cYtHmSH ){֏f29L"ajG:D7@ 8Kedk.SQb ݱ_C*25$tPArŞKg#ɬ[;xAf_:*0Dr2ّXRLj##!,tf]+Wsk+10c Xh"eQ3`STHӣʠhA4qļ%Fئ26;9;0U=R2(  `z5=)fkb&1$3Y %oO!1 R#cƱ٥8S%#:~ ~Q-6Q V{c./W"PH{a1 M;:QfQRkZ,6;(Ӵg]ҹJ麧mż%FM$RYJuu#<\2⑒' Hf&3(C8Riu:Ru`_Te&s3Y{(۪L6_@jݘ5ӘԗƀG:McP$-/pȀrܜc.S)y{AQ8&dXS [9x]|ޞQ{xAȜi4+~4&$rOMVVcל$>2""+5'd<ؕd[[e.̜I6 ~LE;2 \IȺkE\?V7D6_3LCd? eNoMO SGwfU٪a:sS0{? k .ShOɔ-F0ƀ`25hF^T@TB _\!dPw>= ,J7K2$C`$p=/ B45R&S6+”LY)%# u{La=&#X Z@z9ͻ+By?JssͮB4(:b]&{"./Kd7,b* {jD0C0"er%"-9Fq w?p)c{r$0(0B1W}uM$>#Du#eA\œ}qWR)z|Rh+!5/W o:, zRu aB Ht`$RXH1!#tX=r@̱uK`4$ Qɀd I҉ UTJ*?Ӊfbf2`?:=,cRXFeN"He9Ab| hE=@ L+YP$yjL0=ˠH@.a+(Ob{/N[9FU rXZ6/i EvҕRtXTzH:^p5$:V egVsaNϏ_ zLB/ {ϠG@>s)\F)9J! o})z8_mth.Sca&{{Ş[Pj8tT1IӀu$,ɑVDguc K4Nl6?e?*{xDr/E3v7'SPzj֥-;t6{2XDeKd h%(5PXoe0$OAlvְ֣ǐeN r q3(L`8@Õ=t`* Qgh29M-V^EX7_VjP_ޟAԀJJ: jP$>+K46*'@% m mPZwd(P/آs3LiM4eZ["¦5=1mvmk4/Ւt<=ӀB!39M |)|DBZ!Wiv/?=?A\erקW=~,pǏW??Dwgߟ~^.T(/ן\Vv/?_ZdLV gHzKUꚮR>MwܭT ^xFB7K8GZ#}MWbϞ.iZwL8JN؞=8k aDhH=F^qFͲLn9ABt<ݳ; F4^!D+e* )y{J M3 F\IQ@RVf29M8l a/+L^k*Ex,%b D@x9xye:,9XLԘZUeHkuUjWL) ˎcSY2g9MW17.3 B~}W+) /.EdwL[.J`*F!a-lѲ=ɂٍ2l9MH+W1W8FWO)$ ΩkЋMֈ90rOie@.!LHۣFH @g*Ҭ51-5o,6?(X鰂\H#a `Į:zϣGF1p\HG@Sa{k0 c١Kd `gX]"T”="y;-)8!#үYYƯni0zD9V@'y2E)y{ {э:<kL#պƈ(R0ǻ/b؈k@#@Sys?y;ˁԃyCyoixPJM<%\%Pxh# 21Nw4V4i;ɂgVg}/{CL1%ĝcBM2cv׼)SF-V>"v) wP2^)yc֊`PUq`Q!GD}dk&\gtu:M2i9MvfD jo 9Ed+I~w]HICF]"k_tX:zPZ-8J;{ǎLb.aȍ"߂ !QE1aX3MTꚻfbdrF"W׎*TWw膝J8{lnSXH RD) yt2s=HFH’ D lL Ԛ=(tԊtb| ҁ]Qg:MA VD':c3F&.S7R!+p(cP 鸤k%u.B^ƀEU& 3quzLHӬf: :Bx/2kL`-F@̢"0f1-Iw;SiNO5"IE&-pP0o#p MZKg4$-jEFae [PE#)oL[ FAЖP$e[VWb=ֹW|B-#l^Ճs׉2x~(rV=KPF(bӆ%#vޡfCG10St-&f0LS ٣7_#=Z좖P˜11fNםpm)9,:H=;rDy ǐLf.a鈎 QhA#XeF93I|e"0 fSE(WV2Y!)6lQ 9 ͺ Q٢t$%xz,ё؄{Lh.SPIHHJ IHX 0O,r֙![Wy:7K@i1H $~G Ԋ-0Dg,@8' \ -F H,p1pHc.>7 DX3^z)CѡKMHӣs6Yh0A$7[1f[tM.'Grs?041'doBёu(_Md2szT5I9IH:L¡3utL:f:Y3C=%>{ˀkivjEs7ScNAgX)"6[Ads*d={HH 0752ĚH aG0K1Fspfz: |Ī6 I!F)Ǝ0Ѻ#2:M=J'%SJ2&L=HI@V+XS5Vd7%*Uz-4̌ft<=+GmϔC($q| Nz)DAVJ#] "LIShew޴šTL )1bk%^@ݭXfRx4CeWronŲHO-Ɉ2GDZ4_@%@< &%gWy@a)ЊOɑ%HNM1H"ʀ#Sd3,LEhX%JQe*T=%DIBEHbPl3Spobkp#~KPZ ]s)T8p 8>,0g$C'!퀓Q: SA2L#@$%oO)/ PxXbM ,qd%:iL(TfE1S)Cޔb ;!$}"#k )xgwy;&3EgD4'B⑒!Ykl2eꏅ@e$%oB02 9@, 5P(q;"&h*b|ӣ2.u9%gP#%oOBpHOX2B2'D Ȉ0pUo?PKp5DL'عK H-Bridge.fzPK9Kfritzing-app-0.9.3b/sketches/core/obsolete/LiquidCrystalDisplay.fzz000066400000000000000000000405361272363601300255410ustar00rootroot00000000000000PKq5DB5@LiquidCrystalDisplay.fz}mƑv!(i,iWZ+d:=P7fs4]*D P񚅮qnI(|Tsz"qd7q;dIsz^"sY@ $kI8|G(LO뙤wLī D?$q;W3w:KLk 1G1 $7]Ԧڧlq$_$#fy2$̔;BН|sy2~s=ß38'~M7xKηYsҽa5bƝKwoO$|Lm8?nG3IPr6NMmKPjjQ%ۅ=㺵OJ,ެnA<Y$ 9o/\$׮JHf ԞI2 ~!q0ș$0ggn4L#ݹa_lj>j,Hf&.<#]cm>}M K.I|Xr 2dqghӺ?o. H2oʸ s(|xi.$[]S\Yl&yK+ysI u)D/̷^zwɼwz) ~WyX|j>a<ׇa[c~{,\Wpح6_\}]-~Y߿ׯ(C"P y ww!5!;'?[?nүpesxw_|Bdo_|x ?߿=~¿_.j{Z/jAYO;?[>|>0?]}uX?</nf{|?~~kclchH%zӉ/`?Y.bק?MiܧXJ.Dt|s&<ݷ+T2Q_n}}Y^o~~{ؿ&?|XT'G=y7__Lݼ0~~؞~i{a #Ks',s]/9Gc!~'x廥D-@b?W!0"!{S[ֻ%CD }Pw-I/$:f " SX*Oo~W}vwɐ1a^6W$/HL͒WS}"wrX}h/ə\]OOP< ϏǧqzDEHNה"I*2J!b bᧄ#CC=%>%fU2O?߄iK.9{XKTwBhԣHpzz/1r2D^[RbW羐aBWA"y}x&aP J ')_? >evz!4U|!w)[U|Cˌ'_Sz֤M^XOxXH%T GP#ͯjwa{z?'ُǛ/?} +sWoYި]ݯ$6U$[U?VOznsxXv7=+Vw74+#^ċǛwۍ^ٟ+?}ݭʭUX骯_I>;|y^X?3>H-Mq)}_(l@ivfH VQX$UR֕OѨЀ)D\Rj[K.Gd7kX]qiW,[>noO̮mw{pߎ?"631; ´k~M)H>$S?jj%o|Jg\Njm)7˯|G$5M~\Xc ,KFPʶrnT[%Wz,F1W\U$ | LXq@ vϥ s4+\z]h.qRmmiaISIHN_J=Jz @5.MZOJ躺\rt]UȎ&e}yTHRnxKe| \KZz.zb2k7ԩTۉaHFQ1)4PQԡ[65ʑ9, lTm1-2  !y.js %Q{v]zն\$e*]Kkt)xU:q! Dž%HԘKg~ 4'K"b"fHn""IcW_1r˿#|Q$ͫ/ۧxxlK12_|w9C %0ʉ (`yu}zg_7gʍv=΍j5 RzaWL5i&`U/ԒE]Q3=ϡ֋ڿ<y;{y&nXa}/?:O5o=G$>rةc *O%5@Է)ϡ֋HI1UZP }kjZPf-~Uvsqw92P %TzHwZ]i@ѥ| ,J&oH>` (mIcXsy*@U8W #SH&UqGI0>YiNj"(6eכ,UA݄KrFEd%>h~{;ד_k7x/J :5phrSIst}wUo4T6$ALzY%_om$R(L %\= E?A0@4[ 'b]3]6.28E)4 k:ZayF f{ #s0Ρ)N/ g4Hq J!T(Su jg=9^]΀/hFu ~"MTS-TG di0 0|+F??3)RʬmŽH1hgnkۧ/L saIc دfG 1I"&1csX&\c} .ڭO$Md{!JMI:׶n,¯GaHHAD ~Zj앱C dxswBeIl/@\{E1SW*VTf섊 B̓=?<}nQsMh؞v3n#8.hnK)N 49ܑ6cdYY3 X!%BC$WJsS^Y|+& #Blʼ/j L$y7|qJe`2 F+x%E*JY[ޭ5~7vOm>Sgg>-.{L>gO!{1S-q+]9=ȀϜ J q~h<4-\S׾g}6KTמB ac3=IgJ*RE;nLŠTԿlc.Q P(d|ޓLp00(1  -R҉۴p(8NQ% $ j_/iw5Iv3_en>O6bՏPp0ZLNh$qv>aE̹'\gs ǽ 7[R@aH7BX|aP }@)D6(꩗ P@qtBTO,hXpMPc|&Y~KqS1T\X+.y v IT=#䷱QSCL4_DH/T}Jb6nql '_Vnj^'oc傗+f\uYjK _fZ2X6\4jHʥHLѳ}I*ѐ"*Y?l9OƼW_ QcH^8 L%`0ε0PyP a.Cnnk>6 zxVf̈  8ƃvZS)|\) "zw à0깚 "V8X#+sXi-hXPF AAC`m"ÇlxṖe0FƀC:hL+ `ϔq-ϳFU t՗- ɰ"~T e '[/ mU eL2Fg[m23-i=Xxe̸T d F mLjIjn:QIb\ϣݥ Q#\ige<כ߶m.]]rF'{q)٥tဳ࣒er9r'nO鸹[mnfal=ݽ-:&ş&[~i-8 WVZ>(Zpp&d2_}y@*O󀂍տ5;T*d q@B\>'SϏ7J18ѨO>(XP7BÍ NAaPrݸK?lH?\6Za(",kD_(31Ҿ<4DO/_i=t%ugdctF QʒKF5#y:$ Ϭ=nNge(5<}~ ikXZ(iPqvlָucì.Kn^̈́5̊rԠ(%֞ƪ[<͖ tXs wܐsѻlZbK _ fZ >f̈T l i˜␝(N[\! jJ$=DI lOڢE[&Mpv+-_}Gmz_Ypp_wq<:N<'7/ O[=K˙= ; 踨Df:Wpe!:ьkD H4A% a !6DZ Ŵ^R̥@ s&/2NX2c+P^g`gXXˌy\. L~ΉTCO*-3 .C )s)dtHု˰E3ȸΐ5Yd={] [)#J.ൡV A*(jRªbEխ\nT"; 8rYQ#(5Pr#"M˥`C9JᒷK˕E(Xa@ P0rp?ZK~`6*X>?o$WK/ :bպ’_zu1++fxլPRi[Vgx6J_kO ,>^4bB. 8vJZy%F n+Sz5OuA]tFያE 14J؉D!pSGwS#/W 1z}6K q%6SOF}b 1\}6l#895J KZ޴< IʔGVB)vxqqs(\zp8ySIIS@j#'Q-D\9c¦@cԵ!:.<7pvr0^ܰ}R(aSm(FknknJZ9r"Ri#IRu6< g/|rB%1:/R^zyteye!:.<:pvxhjɔi=B4-Ha&%x; AVPkhFXrQ N}xDKMD+2N pGłCt\pxe3  fT(28e8NYy, Ԧ6__^P9ruQ|9OfZvuQHaU 用L૯%'`SOˎzaRJ5.6  ]v-v3igU9 XY*w<]{sFژ;*h! 2XXu $#0֢fg],E<;` d  0 mBUBp\FMt [yvg-C2|Kpmגói=te0Nj_ڣ(Ohjr0u``q\a/W0l W z v*uV1($ [k:<; 9<7DEOJ9pvvhZ`ИĢXq@A B&h)d Qʠ[^WR.R%Z;)eMO*M}N %^Qɭ =`1? O"Rj:Spehi#F4  )0T :`$kAK`l(3\rd [M ϸ,ŠC3<ۺ\ 2ˆSUh_kO Š /~i Qp80+1h!rV7cwV QDjX֊|ܮ\!g!p xH- Vf!^GYJd8j (г7pvrhJpBB)IC^Ѡ, /m3u]p>g5&ap7\ruӨ}!:МhWȈ-] 6 .P峙#FcY]@Bt\Wx~e3  frc0n#[N ͕CĢTT͝jzU}yzu6`0\6mưTu1]&.0AO@)RZIJDDAaRJF K)lęWk-=L!.y6`Ppd@F`!Rd aL߿{YM=~C5^q~SXkx>X{]! a`V330=]5`00l1HU,PFZozf5 ,]p#206l  v41+:J#& P$CZ'{2f5J p^j8vıֹ>f1; h%SuCRDY%bm`Dva`%o{5bf. FTT"(]y=]]9A7o +`sgex@fe "bIq~`l>3 r3_ 1 !`ӄlU۹eͯ+(H<  `ٽݤ4t˷ y! ĀýD"({ 3IaWzL€†SnPt IkةZG$"*Rg8X녔`Vnb7# ƫ5vH:CHZ/<vE\Ix b\"";8":2\̓3㎮d@c@.& 1 GX-|%!^ce刭DYM,7Ӄy"兲5*fŎ}y06t0 '&BX41 j빗ʹ{ (l9a@cH5la 81$fu0$)(^ [)g`!UEvHMBHDH,2}x*.-GD-&4ఊe`w~=ϟU^ 1 31(.B^=F6y-q_vxe5}(uV9ۗIMkRF&x䊉8 F0.6V[q!a]D!D``0d8#2yfPt -҃r@VD҃ywܫq>ep+pYL21~8 VSBEO\Q$L!Jtyi3N~ڈPri&`hm!ۘ#)}%MPfo=98 #e$札<紘 ˎy$Ppdz&XJ f8o65E: YǀL }ǁdߝ+ǥOhՓ&b 5B^Tyf1 xI( xLN2O.X̺8d,I[c61ᒷǠ ȀLL3"ٳΫ*&'6VR!RJ>2&'cD8F] /eWQv}-i`!OW1:3+\ @)rd ]9XpۍCdpd@b35-1q(.QXXNqb8 J2dO6M`\tsb bb'W\IKPX͒gzFg^1/w'7$]pb4i!:jp| fڜ.9 l Hc`b uX d٘zMN*!L$tͩm҃͐j aK(+aZhپ4v ,`l XͶ._dnOlņ@!%o7g[``cH5pI@e%&Y&38oy=kW/ .lŌuO0  eᐡ@J\{ۧx21 S풷Q#TX@a_r `0Og_:l=D`K K2S88ʷA,a6(־b6%7(.yXP'll ]m|PIXpӍbPpct"f!:pDG!gc6Q1ᒷ/AA!IҊݩM혗oyʹLKޖfbl"1_-c}zOJM;`;:L{A(SӲ-+ jvdѾc6qZeG=3>vI Hi)YP*T7Ivw<DGb6a2ᒷ/ # qbVL]*32Jr^HI#<`2=aKޖG?`aЪ0 Ib^iKGɀ;e:LGɀK 4(+S0XYF]w7f&Dp{sG^"`OWQԕF S&Qe1ń zv854gG=3v(ftL sC_$7 ".y[̵t(Њ¤pO K&*[PKq5DB5@LiquidCrystalDisplay.fzPKEAfritzing-app-0.9.3b/sketches/core/obsolete/Loop-SMD.fzz000066400000000000000000000400761272363601300227530ustar00rootroot00000000000000PKq5Dɤt= Loop-SMD.fz}ms6+d*Nӝ[;I'uR۴-Jtw/ 2E (-"|>8y9xyzͯr5[_]` a0ABar1]7/VϻG聯,Ww嫋\n]Że:z.~5/F{mz?].z|wIx9e_kwͯҏ.R]2~wcz⯻7䗕1dNty?'oz5fkKbN,|xfr.${iXڽueKz'ܤt42FS/F$1?¬ .7j^˒~73.9LWγ8lCboi`Si7f<*`3`6\fΖE4Kq(->eqPv._LM` CCRS6H`jF6ajLͪIV3y ԅoHKڏ"B@i1Η<%@sK:@yh$G yhE !yp]Ξߒs,oqˆ L=lc;ۅb 9_v !oyn"wo8;!6 :H ٰp23)oG{Bx[^6P   R:CwxB;VJpo.5vpr a9m{|;8wvpGeOjKn@Y\{AdĶ©|c_ 5A+pxxH킂X>m<z9*:kJP 3CAghCi$44ZmT# C֛= AmJJ}(@y(AeJ @JFA)Ac9jAep Sl#ʍ+32xPz6PVMj2ի@jP6@8#p =QφgT CPbrCi!>}CP p#Gб:o=.J{f9,^`7J׊L?&2'CiZCi!!c(\+>|2&Ķp4,<1ulЛk?Z|/k/ vwwW?ׯ.9[?d/=L׷.^Lj~y&zefL'PtC~ү ^Z>GvAnzzu!h1QGYf?$I>FrAuPwz9]v0Xl~vg@)Ԋ7lmVҵ|^;`d,~^8O%0ՠѡdĶJi_A8Cr4ƚ"n2llPFl;H{ B1΂;`D{x;W.L4Tp[#. "V5(7E#H 8^;4`{DBXvǪ(VEaUHόO]_|Z" Ի_j2`ϴ6=x@Dk󼊑j1t%RK]YjM;R+3Oq+[ uXHUcO>)\]meS#I5jw hZqhIp1e-Dd̩:229Tx]jc,)"D #L&qIC525EZNY^mJ֞;(Ț"F``wѩ1D#+89$0u0uQ':Ц!z`B2%Q{i9t^RsuMԇj@¾(4m:_qfMO9Hkv18ΞgUH7bL!fC_mB_vl=K"~Y%^LEU76@Y~ ZbFWDkd;%a-FШbZk`Hh)Sמ֨4O#=1EScȈS&A"S ';=`Nc%T?IPbK|SB`LsBg> R-cabaߩ~<9H "S{R GZ1կ|** I'X@QwdYv$[Ρxs'0;;Z@߉*shQ0AB550B푛VF9c01EyTsG889$0u0uSDAPq h!LJX4vL &΀H[^Dþe*!2LL 18JU>T*{4SDUlH!Yl5;%-z*UdftFD ղRs*[7K|ţ-TS#T\֙{T ӄ#r &F7tѻL"!ȜC :X;هCApTI)TR&}i~QrB&H[{dXطJeСRT#"r G(SOAJS[MT#FuA8{qGEm#rNj(SUOaFGT+k_57CZuqgw/ۛ?۟~_wGd7].nɇ˛?N~v}d^.j}u{gogٗ7?g>}u_[; r|a]:Zz0cjU>xe /}1zX×Wd7'nqiyg|=5?46Ͽ]L_(Dzʇtvs~1~b?|}Yf ˛|^<|b^t^^Xnye_Yus#kݯffGj~?ܥ_Vj5.ͷ\ܙG>f7nx=F?wӵy#LGKf 4]Y(lK@2VINb*"Dtz<8oH*} -~60aVI RbV#pӉk ;fohtT>3XQ{6lExe@G OMLEoR`z 3HAfƵA} \-E.Rtyĉ%HEՓi-* /Y:U3_b `*kpʒB1l0O+Kp>EW$04fRRHnrBN{8u2vd$xDzX!HVRHmV"Ř 7;j,wx?L=͒@o3#%$E +%rZ;崭H Ccss9Nj[ݣowAS#Y:Fs!`E50,in(iE챬Pc!91ݱn9Wg>ܙC4/# 0¬Y4<0,vYƳvy.J1DX1"x%cNR9:jtvpn>P`EwtJw HT]YS `mjV6Oy) u)!e"2AGL,SjS@2%H$JT@1bpvQ)ET)3j|˥pAz_tDUeh 'A`PO}&%$ϑfNR$QlpHӺD3PcѳTDw tza aPQr_+&z_tD.U%)gk"TF>Ez=A9>7B'*JPCѯ U$#T!'(Rt(|YG?dx"TVCm?3`VYXݽȮNǏ^^ظb]E9I\0EY > #ZmC";|3lmu{@zՑ2msnRDiJe/VGWH@( ؞R!S 57@u5jp,Qm*Nb96, F8 ^8tE%ε5$.@FHFGTK)l3;ZܵV6l~$F Ȧ~t{ܧd3hx5ɝOAA,>ϕO0:kj 0~3`~XKI, `:Ȫ3Š}`(, Zu3(0j%J I0=EqSDNyP_膮T@9H!8&') +L:zdQ-((XEns¢z8c`Y }ceM!9y[C76ܓzgS!1ҔU#*keE#*zJ \_7R`C0p [)p^ 0%$3.^nl!*q{( V ~WB!Bu<{qǯCw+0SƆ{ty8WBWQke(֍?z,5t<(6`1D {Ry~,]w_ů`FƆ +-:w/rLA0[s\hN rULa9UcCX1A򪣑Bqg$TCO ڸB3d:@Ȣ_W Ll RtǢ_%pYԘj6'Θ'l̈rz&ً;6 TU*rRlj̹J0%6K|*Pб(G Ѣ($Aay!lT@"pcŌ^I( #e+ U+dz`+ \@#*zb+µqb'@NYrUdV- W0"p;W W@Cftˬ ̪XB˧*,%=o*@ $^$WiʫJ͝cŚIݑ\=QL0ꫴ!WLt/b˼v`AHv176\a%[0,AO>d%=%>'X+LC=+}.gIO֩!U+P(,8vu"NlQX"$gO ۹BYi ̪,$iW,)y5`]%$wwHЮzB Zմ, *J椫fbwVCOE. jSUşZU#Xy!PpAC~pP ATmS &ʎHc-r*'Br=+Onr">qOB yD;Hʇ ^pm\Eby > )2r.!9~L, btǮTK]q9G@Jta,-CDL %gi?*A )bRU#% aLr*lU9+hg`WHAb9b#oQ`s+LpA+%.#ŚJj^O~:O,.ߧs÷Y7O?xAv]mcj~YZ~_].t^ĐBgQb*мWr}73{O?yu^?xܳsB .%f>f+s a0[h>O_]<9?wǿj.:Jxl>Z}e~clwk׋twBK/V- |& gQOjW&utF՛@;ClE\8R`vWߥ7MU?gkc(vXn6/'[ TbY_P}#Q-O+c{546xt%x<Db\:~O>,tɇq7Rz7;3sDZ~:}slg L݇smg 4YķwfnrS,!%2-I.M8Xza>膋6pBP C_Egވj[q*'x'08 Vx89RbI.8gܐ6cq.^Y}*.Syl}LB Jȇ$B3D9?T$YϽ;m5qz;3so{q({Oqz[iu,S̍%ObG%\^(ޠ?\o{ ܄souQ7Ue+5J/c겞(=o@aqpp^<Q~~1JAO\b3Gzw3Or]gIϧ8Y H"EzTQ"oӟZ3j3#V IĄzsoH}ۛ; tk~μ#mo?5z Oz޶,7 C7aaQ $HA,R-.R&\c C_Egވ"moŵz qz#UgԫFEU_hO?$?w r ZIOzOP,ѸH$wHIS'&i=\svf˛sM {`q(OzH3Vh}c>Fm^ ̨7ξ1N_~ϽמF\ C_E2:؈V8qz36P_&vqD$Z77]kwaE,8 B'=7 $Bf7R]m!Lϑj/E%FQbB}@a35Q^;ݙA/s?(}ۻOMud\BL j67f fmd?J_'S'EF :!9%@"$c7`V@Cfqn_˭^dVV{׬ &y$Jf/U ߀]<4tcY&h%^W0&$v̎`9Hz#X0"`CEU T?)I%RPPB~ 7`W0"pcEU񸑖U˽- Dߦh<6Ԯ/ Br=>Sz240N4)a Krqha;TSUS'&Hcc&/HYM* ն&Hn0)ۘj {?0hFю34i$=(="ɃE|!9, fO {@YDqfHy$O,}4%±O q|?ϊTjKa "9մ7 W"$ޏCĈzNtǡC)`jIJ|!\ȡPz244EP#ĖCiv>Px{>2r'p((}( ;#"xE)JDYDɦmj!9~$ &Hdg?! PtH:.0H$*z/$ޓDdО$ HITR8,r(Ihv@1eur2TzHʆ@`bD86P@J!gͧ8A)T4<\{? $BQ(m Pc->aDrP"%7 BrS ?U.CdgőX-OUvшsIrLiedReCcR"`W})!(*$Ox)Q8Q Ori;EH~-۹Uu[!ʺJMv\]mRwr ф8Uߺ_* K }T%edSeCOl(17+TGDƥ(Br{݀OT~ §T!<&!-% % vLw*L HdA(ΨpTj`!jUSL UTmPbV$O o@`EHEv Js$EZ4T7<8?@Ci]z?[Ūϡl_gVN% @@2ǙE,wpPz! {ERJ<5TR0(]m#*_=0}8=/o"PR !۟GAx "%"$\}c4P! UOэFf]) ʠt!V~$) U./%*#.@PYd*J &Rnlh!* W:"ASxЕTwJHS@C`FQB}\їȤʆpl-OQDƟKBs] Ftȟ70du$\xc}!^H H+(UvE"")I&4UKX؀r*IS@#C(M\d*vpLOM (Ddl5J"@8.=Ig狋%ۊr*k^~~#V ) E}S*P-`0D!IQM,#*Z gwC<(բCg!yA5D4BQcM%YC?\"j7ʧ{!y )$Oc琢D##*z%64\< ~TsDH}! &Fnl8R[`0(F }^P.}ٴLJ)bmN(Rlcy%*[9V#JQSOS\ ~T/.EP}ɴ EATm&ʤZd{Rj"@ ɯעǴ"jCr5 >Hʆ(E '?Gӂ_D &:Oq+\`Hý0 /@6`'$JSHʆÄpl+?A[GZp$/@A8O P'{⍭D8{"P*VC9&ӫSƢH<;'fl  *ң02t<<bçM`݊L਌Puї TL#E8ݏ$}&HJE*D vd-I HUh8={@!('pr0Srs6y!G~3M:O߰O={X. ?p1cz>};&6j?$cyxeHl7iYHZ5ybsťG]z8?,fu\.s.>C$Y HG{9OFox?*Or[SHW6myrq" k~hfk‘$z,QHX`7`aKA6ŀD xy(!D-YlUεq_>~bi̪_ׯw\3l5{ws62^G>zmox$9<j\\>=;#YVWՃF泪_kh $9֞Ű:؝wk}[wa3Wn^?PcC>qm<H؃,q jby?'Y(lb@&xO؁ Rnܽguܸ7mToxlkVnm\dSddHJO(>Z:dFe F_.F_w 4hmx6ښ ːV6[v32*lY%_qWj"4U}b7SL܎D2(ӶQ#ʴB,EQekD bo fQ&24QNQ&Ӡ%ʜ۱G$ev.jE݋D(ʀs1DZQѧ(#He _iww0$$Sv2 t$*֊;ZWfWU%$R*΁i*;|nǢfz-2Ũ`s=(nFL?[Fl19AQ:\\7>/_zf_wG'דɛߌ~ 2|G߮/&> -7r51'vhb>t|ffիbQثGnS(Woyz0|2 [rIn/{=4?̾ѧ0g(t>5j;zn+vYތfWfw.i:/܎kzvhmf;ǏZ|A[cc)c`_`%D!%Tـ[aP`ɻiAWO^SnW{ Pt6/rs􃹏 ~#Ͽ#կD#2/UfB"J*Ĺ5??|Rf[ծjjmLvBqpq P#VoI3Y|Z_^#6EÆȆunZCPv8||#kCj~o6ql:ef2f=A)sj ħlv2Uz'GMth{5Hʨv!k,p}F=gwyvtg~(^c8"ogI֬#jGd^gO֠:5 opz\GlH{P W: `34C {eiVV?Jؽ0-99CufJ WΖWф PwIpq%B=^A00 QGuvnusߨ= 7Mw77ߨ;s v]qzbVG&̻tt\gP4wls]]P9wVTjs튂61ǭ ϵ+ SfP/HΒiA-)T4nI-G-)rT^:PkPezK@Eݒj[YR`DjWJ [qޯF3c_0&߼ޤZ{k_,?m}ie~JPKq5DY]C Loop-SMD.inoKo0slh )mcArP,Qa7 ߻@RrDߴJK'MH+촲 5J 9*mFcܑnEu,::[ׅ)XKUȳЬBȍ4V&az 2'78uUFwIY̸h_7Ө_0 M&OX*qV-YNTK>LpkVNQe%?..t)AsuKJ夢ƹ~6MI(R;m(oS&rTR:ipdr×1x:|&2mv(s?:b9Yaq q)r~IȼhCqԏ7ς+.?BtxFlrGooF(h|S1q7*"y _\|9r~_ PKq5Dɤt= Loop-SMD.fzPKq5DY]C =Loop-SMD.inoPKs?fritzing-app-0.9.3b/sketches/core/obsolete/MagicBoard.fzz000066400000000000000000000255311272363601300234100ustar00rootroot00000000000000PKq5Dmvo* MagicBoard.fzmo8ߧ0}gϤz],ݛAn$qgӟ(Ɏ$K-ꡨ$eUv7?7 4+D5\]7m{w>߼~ͅ@L2Bn}|~z|sߒU~ٿ~stsNq޳b}OG{*կۗO\H_,sYл__˓3߯iM/VW7?>?~\nswc:ۯc }X=^=\7Wuݯǿf.~~kʥuiwٿh"xyzw?|鯛s1]?O.rS^?WoV_!]U?~y~6?<}WJ_ͦK6m,}+e a_*NZb}-!]c+csz\k//˻/n/ov埯~z]Xt)g=!.jkƬ՗;_8/|6//Tk&Kj605ח-$UM8x:hu*#!1ֹ֧cq}huopg\T!#uO{V1}?c "aloPV~+[IM`lشaUCM06!?]UDJ&!/&c^A;~n !/&/¦!/lW@\jl(7A4@V6 ya;>1'0lva$dOĕKoa'!KU lMrtBU0vO>. h ^V #odUr!DZ&J"y nvOO=y1 PO L ZJX5U1:,ƀwvqlԐtЎ˚Q|jkz!QTN4-%# UGN0 m)Mqⱔy㥓8`i mQrμƏJ ڍ ZJ41f:, x7~!>]Dl6s[1v: x]/$zjk Ox8 C>cu2q F0d! vK=htCBO>cDuBFSDN*K<;̱RW$!`cwg p$ ]: 2dp,%Ldᰪ6`Cj(JBjdZHv %?:y-ďmrtb';`])4X9x3+t~ڥhi]'*y -{ms_'Lk4x,ć #h!^J=[%Y _Pyyy|ڿ7PgX(E"F*^$7""#ڼccɓq őDD)(&];b$8sFɅҗb ,rdS3FA0udCҜE*% L?TE)I* IIhaJJfjxsC8e!8S`IBʼ9FT'sʑ+ZeefzJ]u)WĢ+rZA2[?| hO1bD S22\.%#0_&Y/ZTL$,e{@}{L%J繓$IQ8߂.'kcN쩂/1H~g?1^D$sJ2I@ );W["#cZX79`՘1dCxzV4mC{,:`4#&RTj&4;.>:J|;k%HqV$L*%bvxdˆNWi0T°ug2?uR.YI ߐTwRWIQ>aKO^:tCUR+\̗_TaLpY p]b 2HP˲"ZLGo:;!;ÉْsR%)H{#SF±, U6ʾ!t#uR#ccb$+W x%%_ȇ%WEȑ IE߀%ɰ͓"0UAKc*ˢPOy5-Ad6Ge{h, 'C=4C CE ]jb0uc$ZȑG8,IR^ dI(DcZJKIT77j=k'=F t+ 䡴l" 2m zbIw;ՕHQ!3O69OR(ʈRL-j-ъi-e2oQBFOA=IL#&a C`vH"%t<@Z&elG8ﲱS b`eHfɌS6u]8݀ᛋMM?+(jSά*BZFnY@Ed2@;GXOPJ׹p("$!SJYzvxPw G@B2t*"!fnyĵHHT4Cg0ҙm|ӰP C7u ô=Yx3H?!8fz݄!"~Lckb:$"M֛5G$'I $)Gq6\ C@75` L_pLљ%1Ikɚ#.oKDIu hlӘvi$p.(OQ"YzaP5 'K|LeA"ES$˃d2e(D( 2`  PFCYDZgX{(Q%(kF(;tqfFu(FQMGD p얰E>BZbZjʓ0wj$ lC60#$C+BѐXl9R&1١$%2+Rb$,DI^JƬ8#,Cb@5$3S@ g*D! $4FK[0#ՆzÀFHXw.K&`aQRU581r9Z^wM5bD@a-`ʓ_A]-Q@RtLz",s3MC

yiӮ}=ۊKZPkl( )+*ŕ6/ݎy2Q0( h?ZٯEƢ[:1:a-ѵ Դ5_t#چF'-6QAy h, pӯ~ k*G]4]7 RDlC}C{@!}g>rsYIcBZGqpRTq\;fG: S^򸚚+9S>|D "<7"Ԣdt&m64LZcycs9:FJrW\1-;N!;+vӼS@D *pܑ G7&\*jMXrk1ZxnF Lݙ) G<.1ng9N$!^'^rJu2X$ SF$"$"ehLC:lf"W&N$ sFI64&ԮQ.(1nqo QzMX$tMu<7]カ"KE"yYzP .P1GQ~cV8zy:7bm'L% @bthj4J%2(bŋFR46X̲t7_ yjxD{D2ƑLr{ CX{m[&3] D%ܗOGJr)f@̝)IOmr`j'rL<_ZmwjC|\7rb1czS֡|b`5c^RR,oLr;|nn5?ϛv_.|~<ۗǏw'_<^LeS; K%#N424)}6[z`FILDlXta'|tvǗsnrtZDj3.2|wtF;;$p*w9*anLlMIMtQ7X"w x)AcyTQJ'r;Wv/~ݤxzk]~>o7sU͵~ﻻ͋ڡ<"g?X3"`%>?o/7_jeeSn}"̔V%rvάOEAo*$,^b?S6{|V$Bz /Pm9W|{5?|=7ϫR Xβ8sW7{Xϋvo] iAF1hǔu  p-ps:zƍ6M{O]ꚼ|Y=^=(ofssG'EWT~`s lb616a7jo*~X رMءݤ5M]D v> gh!*!4#]jb@uc,61HJ]paYE׌@UG5S%5 \ <RD۳F"Z: mv z R'&KԝDB^"b*ǝehFT1>!2#X'E#c*#qe1Wd=}4_ Zm$Ͽ')ijU,4⌒[Vr2)ډ bfXՠʚI % . X } v-(5yg&t3yYYW:%]E+UоRTV2ՠhhldc0l[S݄$W\"{| 7?@zq6Է((^Q+"f/~ 2ԯ%LP HǂESB%4MKzmG֫ S=75TdW5s 6iMqwa.QWQH]' 8WLڡ,,#,C#{Q_&kR⯁_6uIC 4GT0 6s&VmY䡖\C UW{5Bd&0L_ u|H?e^P#3%9BhiSV A1鍸D[*qi6ԏjFHvM8cƣp(N%J%-C=_ fw覞+6s!,C_@#`3C^Mj34BsZޛ5C=C]@5#pSCՍqɫW|>~G_P-پޓa^k؝"T"CQLM :Bsm8Gm3IzI{biyW"1|G;\u`-Pwś&k/-xSI )8;CƒJg'gt1".A"BO|3 &nҲtWnWseIMOox^{ #]T'sZZT[KeJT>c)Yr(/P)]u0-' ]܂A*߱e!M&~B)]f1̂AeCf 촎[:̘֧l]wq(n{ R^to' E*i=8Ws)ڣLD)XfyITyMfTs֖ 2|v5@>WǬû` trYlh23;>`㑁m5T)A@>WnA v1?|7v."E-7*KvEeb-bZ/ptuNG_6%yx,O7%?wO˗曋ݯfϛ߶V6%%L_3)(IE#MEc˩A6CBy=m)¨Ngۯw?553eD/#A;Vh@^FLnu ]\لK gS$1<-ov(,;E_Sa8+ cqmvSa#%r()y;3a?0A!Dhfo#x`o;)7:ٝD:NMR9ӄ KemmZkS%3J<͜je!ya$pH,h#]٢0 hfOĚ8ҕc fvAl ̗51z ٖp !Kā]dgc0ZAgK!/Y49 zTʳ^B&zb1;I|1,$`ەӕ`KtTٙd_:ٙXFgb Sǎ0/o~|wlE/kŎn7$Dt#ӳCLUJ<$ʺ޳vv_i~zuS8DFq}VD53<De5}Щ 5`ϐ|4fT~\`vf3FߕgnZW\k!ѕ?:w VW͵iS3Ԁ<:Ijס uKIV>Yu:]^jƣtbm^‰JHdh$egbL'T4P C)@esllmS. Mi]@dTqx8Ɯ2?,2Yd.UdmFs]N~L Nu]R୥?Dڸ%X+$Ibv׾B#Lf!1 t̄{"=$mho !Y<7œ*Gg=FT 6B$q-N2M7ُT2NvFBcPWD#HJJ[4"ZvkG Ήm ʁYM^ړȎWy;)9ƢPc@5#$k7 ;CzGBvaGHnX#cLr9UXXV ;)%h%u-D$ݼvm#vmP9I*`#vҾq!!8:q$Ej!qqK"ATFxYıơ=!U8Fv>_7Ukch8֤$[!ra;X}2A &3m GGND 5$$C)!#˜x.y#s@kΉ""8 586UILt]cuf*R+Ht)#YmC ”}B5xp9U Q Ƒ2y<;KG8lUcGT;B9M 4٪"SE"!چ"P 6Ș8!@oB`3VF)\ݒBiq\ͱB̜PZIJzT,sYcY- $0$hU&kG2'!P dFD!CO)R6JIHvC0R2fo2 *s[4fw4k`C|8U%(9>56i:I_A TB*!!YB"#HO1h:@)L1 0"1@FaNF"Tbmr 1$f FVʊ((V<3?0[6rP"TڀE($vKޘUJ6`%4ֳ 4HL͖(oZ)l21*z̎<<7vG+9\9E(H )(q|EtbPϒ%hba%F8Tu&P ㅪX3nIO݊iZ:nB,$hq1T&#mKb.6k#%m-8>'Mehidqj n"PjG~<1Sl&L Y}:eVPKq5Dmvo* MagicBoard.fzPK;+fritzing-app-0.9.3b/sketches/core/obsolete/Matrix.fzz000066400000000000000000001247411272363601300226670ustar00rootroot00000000000000PKq5D+cQg Matrix.fzksv| դNةwIRI9NKfe+{f4%i| [-M{ͮT<=hZ?Wp{ux|J,o 5[B{su7??_}}|T_?M⭟o?;|߿+{sU=͏=?8\=?(t/nt7Wݿ78O9N>~?V?b.w˧>|xx?ĝzss1?~,p|?/~z[MplSY<ݼI|÷?˳{}Dox??=|8TsnC~??ڷ־___~ٯnjW/뱾^^z]qgAٷVw-U}sۿSuï7=ou;޼{x<퇻^S.MRsXk~[kW9w~><|<?~s`DQUc5~J+oc$]O͌w>k{:6} ڽPC1tB=lh8BQIeвe-2Z;Pk(z'q]mq'A]:g N¹̵h0eHqtIWYMFB୕L4ޅsj Hb!YԨ6dKaWiA[}Yr/:r4>v,L~4fUAn 6o'P.@:gcj8ADIWK{'a\᥍\[dw5PNg? N¹0u΂-을t E0kѠNº"صY;l']Y:g6Y N»Vl0Gl0Y-( js54:UV/׭RӭV8r[1YD θ:sPY/n:b@d֋;YT]ܜ: dqwf܁4Vqf3kzrZ-άn> 폋m^]׍_Tas9/nڵ1T€ܣ;Sg>%qqs "_fX;/YB4ퟸ60ȝ U@Lc Fb?#iŏA`Ep_o\8zO1qFz'q:`n4w]$q@1qـFz'ьz 6s7N cw^l#eŁc/.06..%N0>휳R |; YRj+{'^0;VoPj W{; T06)`v`|J %<F~c`t":8>qIF(.z[1Jq(N Mbk{'ъF+氶w8@5wIncQ9-m\\6r1# F/.,47/&Y݀#m9+ r(aņwl( H,}! o'ьxƕL/DD5i}l'zỗy<<=|y|wx|tS+ p8f OO??87~)a}Y~k6D2STG++c:PI¶C1̾_qNxwYß3 qR͘ȾwYn{-Ëܾy5JψВ?4.p`~&4WEarf`_TWkƼŕKmOWI][%-rmTc=G|}Kإt^L%LGlYH3VG٢Qql@ ݫld`.pEq&hJ'δP h G5МԱZ$yeP^7"45Ζ.0^ș#N/Og`_ 9-8w&h u YT="fM;1ы/g_#]U120vf\8]zJHLàK+R ]J[eݍ9? l57f!VD #Ota e8 H#No쑇g`W7Ho l89#d-g:TP]Kb#YK|[%:7_>lUR[H0u=XJlW%Zeɚ#r,ei3{}ֽk)4ڧ%,_J\hH~KWvإ4ΪKS>ƈNʐ]Ja@a-%R|VM7?ПW+xA_Dx].khȮf|\א:Vg䛄|"ZĿƙQʟr(%ڿeB*kOf5 ¤̖kݝjRYW5%$-84Yl_ε*^ŃQU) enpIJ,)Kk!-f$$%ٴh栠&V4kg7ﻙUfhgY@5:rc5JJiCh=SYѠ( Fu*:J0\B<kSʏd!9YzAёY4!){Ɖ)>@BdBM;60|5l ,04ڇSIG0*:fl#qQ&]OdgY{S8rF0O eF(c+&A( 1EʖrjRABY`h$AU|,=iFEwqMntՋ!ҞZig5WBÈfhU>rx Ifi  Dqfg  6LeMF$0 lPlT5Z[i5tXB010uI $58 R+/ȬD[VZkN4PzƕjR@` I>DD֯(f\hb})-< pd)@#!I4 &36OQŧz*TMEdXjLu2وֈd9Ft2&&ͮP e@$KvAQաNm8Xzy8@E3򑓵'ˠHHJ4i@A3WhQKPƅqZCu"S ȍ|+);GN  lʚDI`0ZȭQYF``c`2!IIji۞ Aj{{D[ZkNY_(iXajRh'L%ң#g+q&! ̭@ k ]}S8rF(Tl3+`@֭*G)BQuوky!-8uqN?f'#2P%ңfPTd}+K+6۝@+1HfdT>rw IHf%3 e3mM/qBVK)#"ZkhϬ5@%4-!lB&͎OB2 uU|,=Ϡxvn3D- S͠πGN֞@fP$!3U k %UoPH$BWqg32ud!]gAMlv5Gz  4p+'Znҵ !mxajH#)-m=%u0RB CxPGijx< := lǙvT\ $%a3XTGt{u!֒8JZVNjXѬz:TOe@%$'khfӢf8XExb_-i} 5|s2F F6N>f_# fLeF JJskٟ~V(n<pJB0,0uIBRsigJz*Q%Ke)aG5K!ZkhͭTc>Kȶ6_MJ8C6G*$Xz$[[;ULtwfJ5S5$34ڑGcHe:-I>+R~vaev.$VQQ#g=@fUYܜb%aCh=SYсEI4RMig_<fm$< LfUIFià45Bv?ϒx5b%`Ch0$K5ö43N14I֪VPs`FN֮ DžG3#DB&cV:ׯǧ>Wۺl)d >BJT3uiG)ۍesٕ^C9 Ƚ͠#tXZRk"m78ɸӺ{zERYpY!Ie~8[4j'O~>OR%dpj},,Rl[B?==<~*ʃQY9?KE>_ o"7/_='UA$(9[!pyʕz{8j[ҏfW'T )dlL'{:<zɋU)R/t]((ԋِ?}.>>qCbwX $m%-5Зж[(&6lso?G0J ŒkڗMZ6hkogXmkcŎ|Ro$ߎ鴻U5ۊ pͭsw_}qW9[QlCK#/t ]z>6 !mRxp!Aٌӹ3BB"B!<sz͊a\prÔ  zrmq4ʙù #8Ń{gRDyW{ +h ts?AH\/mwamȃdgKA'גp0q Az9鴻1.t!؃7 v_0N߰EaJ/Z Ul]iSON^'lAt`y.rn ,onж[[8a-lky'L=@8KAp5ו^@ &!, ]B݈3XInCYoC+ vЅ$BBd!hM/Y'Bҳv xXy=! A\`г tx,],B(uf : a } BXxo .!8;[Z{c q,:D_NZNNkuDD@'=x0 a]x0x> !ABXx/XHpv..y.[*[ޝ01Ѕ Lʵ YfB`Z!HʄNn sA.dӅ0a.C+Jt liKi@w}kp;@}- \i"fsr!̩ѷD1HB@]x0a}x0NJn<e?NDPDD`tZF*/5ɋ ы3 j)FXE@/BЎo-9]NޑᬖZF'2:7,8nж[Zzcp"@:īaN ׉ BNk*#v4KÃN;q+i۽xp%-:)Rym`eaÒDH{-NڻpfDLQvCS5:98ؑV]68شu_*œI"(S6΍w p}t"$}m-/,6HESo^)Ye$".Mz$ȩ ve?NEs|TJmE@/BwtA#J{O}E< od/ʬE`f8iG^T'"lozF}ׄsir3G[9~Te$[}x@yx.(g '؍s oq}'o OEoЮK ƬZ$cn~*~In_:5^x^rk% @t0';nサÓw+ <F\KhshhӋ+NL~YCpyb GN|ۡi]^Fyhk )Qq5gbBRi|/)LN6Tk 9d)kvW`蠐\= naHL% %z8L-]_8!$(v6%jHN#x[Ǭ%oV6wQ"06*'KK}5rQW`hL2L}_l(Nl_- E.eS:r2DבLZ2@Э+r\XecsO zBY,m{I$й_p#c#5@_FE9qSpM+aí]2(H+hEEҘMeBcu7UH{rt`R2Bu-aEW:rE |怴15:fkԖ5u 0p$Wڣ5N?oG& uՑ|,=2rYt%)jύ%\ZBıxgPڣ(GBSi{8 GDT2g책ZC)lY_(l IR^_T>Xؠ΢*Ӭw/LZư3T9Y{? z$0G@Zxqd3D)Wd=e kU9@=@l@l gH *#Xz4A@V}P]4 EP:BYkh4AU=ʠ(H:(֥2NeնѪ#DǂC{f`u"xg(66U@ŰBCcF#c( Ds u f!յk#Ѝ>ᝁר`#!sar(a[Kf,qa{U @O~g5!- Jō¦)ir$9JP^KͺˏK0K-<`ZƒG3LV!8l[F7瘝!u*q7f%5ļ{gX5orлϴ&.۠@VgVLxy3#Vwބ&ͫU|3JI c ^NtQr/s)JeDKV<.>ek ! z'3UՀsA QqM%CWؙHB41sn\3E9=cN2g: ,,A9%uv#+>I99L[9g9C!9:S9#rUD0v29}հ)7."!'#Szw=|d-9oZB%LOz7`z9W 0-_W0m!s̩,h3ݠPBʶB8usiQ cB՚Xj.˂ s%|VRcxټ}C{f{uaz.,{Joqd$6"V*̗pª(YfO;vɷ谄T@Zଡ଼Dv Gż- f&sxm yXU˪^DCNԈw~ܭNG-c2MsjC$^@wR2Id:&ZN?' IBC㠌ݔ–eTMtd:RY**$Y"̞SwCʮ)1)kjX6bYhhՎ -J,%2`t,˩*p# JGLx26j?+xCIK2M{;zG{2 02C+& rאH3c!-L5QS bGeΎFD2{rzXflT9 2C2DH"mMU&xkF-M,`joqroVߵ0D+^EUPiT5o=1DX, isMR.nFBiD%\2M,6 oLuc¦QQ'&n%H\|$$NCq)>gqi5fpsv՜L3"J˾$#_G _X祩o4ݎ9bhG`T>03D@=0TkmTD%ypIS#N!J#][=4׎ :y$θ3D+HJL[Th 9=cN#IE9{^m5PVNnI'dHR?HF=( }KWe.t)XɏB M.>FYٱ {U嵣$:CCC?.:UbG'J3"Nku߯,X{G(hxsrTT)O(߈9<\.>ce&4GCS%ehR9 sݫ7u s.s]B ݫzd`tL̤͆w[霂hYsu*\cAȂa6.Xt\K v=)qQ,BcT|<{ H@6(΋Z eHYAPD 86,84+L#NGB41iRB~tu1:5)[ Y#}e{k/Y#$:.z٫谌l睊wuU }8ţW'G@*69J= 8j3?\C/P 3UNU9̩negdcⰦ++m ums |Ǟ`bvO3'3X0Wɲ~K%Zn3j̟Xа33jDjG؉ ?`e,S: 3|WB?O7p滇w_>>7|xon?~SdjSTo6HF骎6iGV GKDw/A0Ϥ牨AyR%ɮ.N W^:k>Rj~zj>3Y#8C~I^sUq9jGfFʣc=뜿c$tYNG.sPj>lX@Gle5q )5ށʆ #ȯ.Z`Ujħ<xY?[170ejr,q[qYXV[~_V+wO̲]eYձnDճ<.}d6':޲ zYM"F}QYqY|Y8Eּj%#8E ]wjJU1ئtU9s=+hؚ[ٚ$0U̚…Xk=Ћ*geUYձhJC:Y]'+v4[UPyy.>,z\V8Tv Ϲ{7z>)ys s%#L\<]ynjlN/v]aeP/`O Ꮻl=B`͗ "+) :Qu!,?N# .C1KV l PM"P{tހ0p,mŇ:K Z:m@ezN G ,٥cH,maZ<-k -6V%X1llڰeh:9o C- KG/ ұKkjRkh0PㆲL:bDdt~ S ,PM19a]ILj.,3pӹtA4HAv.2BظL؏6Al/5ZZ>$ePv\KPBe&|KG Y:fp-AZZ:"fK %Q1#ctiؘ6:fkiᵴ2 -;K/^)5/@%Z, 5, 5CeҚk)7 [Ce)-5̫BC.ٔZ-6-69@vŋ3 L[Kǟ @<-ARJ6|ZZy=+Rkw/>rxxztj7?K)mfꂬ%Fk%U%j]c٪ zAfB6Ȝ^h ß }elӄ9?T;9|UE%;'_݄fͫIzFALN?6nZ5Okuz^v |`9:iXۀNq{KN?lv#,ynQ IcnÏIGY3:bQ+n lynSW>26` Iŝ:rn[dܖo("%R u1T2}7cp719ppK;,Q1lC1+in&iU=>rĭĮ.aVS u'k l'ښ}I SNK[+2>mM G tlAՋ|,ݳ:jU}FqR TRQ-l-‘ H[M[-&wOjbh&},9/뱅 I"Wkhl— RY˝ר dQrX&4rXk*kq\#9 na-8rDHR {|B bm *GN bp#!q[uEa  PTaHaAkZ`ݍRjHbCG>M vE\N`a U2Ck T7rvg8ڑR;Àz1ی `Tt*H637/!MfU+]g3Z#]+F^sB/$/T4Y`XLʹkJ|ԲBW[(54GNX#$!~.0 _mM]5%䢖55o82t ?5w% nƥ~Ջ|,=>Vl\=bXE_/%Ej oT4rhC ‘dbOZg%rHl1J}#w!xb +FޒHG\\2X`h ciQSTPSajpY+1o1 $U:z$1IԌ.e_CcnHg+KښB hXz Rl[! @\]P$#%a9@{jLse1 !Vkh4lYxzBEO%lף+04 G>]`Dc}j֋Z}y\@ ^dF3 k4W^yEC FzB~~$^7];= k2yu$2 ䷦Z vFX:  P ŽD4T,ZBXBdV9YcV )Hj-3Ěkf$Qk )hd a `sգb}ّC# rc!PTcBWP1J^@ jd P#%aX[ }IIl"Wph|@a_ Vk( 醋A -T3؜%+Gk /T=r(I^e|eJ@Ze;S!U jj5;+8t0`sW9,q""C|,]o80ʵЌZW@Bd֭nS7rvM9.!|79(]ð a%L5ھ:b[[c~ M I=+5O)Jp]1e9@\5p,1)ɡצ8!Ue@T=%lfc8W zg8-[RHIcCca ~cz S u PTXssV,vQ54ŀGN֮ǥXEGa7MMTv8A.#_X E1hrzXX [A -PTAtM?i qG01YQ~ ı@#'kq00H5Gi`X*i}O$1*C$[y6 >YR9@AdBRlƘm8|#+Ӽy<i )RI.Ar}r[ ,xgػ>HB,)!HxunL0ZHY\oƥ54#w(9Yk;FEz J\q$48&*~/$/7ґ;`H ^6-xY:/Ҝ ClZx<1FE{%zW)g 7^M\܍Y`谖g*ŏcGPD nH -ۚ mS LfU IHWLJ rkee͇TYUÿ54'`aNqz% 7!/04T/X? ~Yn Q m3Wkh |dq.8(,QpdY-o? ~߿9>BzsLJb/yoo~_N#j]6͈x#iu_ʛ</qHVbet7{٤%Uǯ]ٜI[c;fijɬJ@#wk1FDt "#"Pڀ5$:J9th!Li>D0+04u l%:NKBCYπHIJ>K~1ڭ%bƮ+5+/xof o`|%KȎpr٭Cp= XސH9A\ ]SNrd"' 3xȘbN dpsk.Ȋ7m024M(6\aklS4r6`*-p~H@ ]aG,)90A]4v@aX Qq@HgSv k^WUzqv+ &*ܭ X=6s!`TC26` I .1A*n/QT^pbr:ʚћ@ad~ڋf\Vh?il2Kf&n&7MKt j f&n&)IHes׉Ľ50HF gguH]=4W'`s> Hg "8ͽQ6wuNFzd EF6 )&;b'"1II) ,N3ҕo Ue#&.{9o،>d3$}{3lо9*[džfT$GD27t/0&wA7 qz~Sŏy{lvx9RXX7Y{*Kw:BRfU1lMTBr7M'&:RLBCd@$'kOqdȬyYKA.hϺF8֪OTe쿕8=jd6[/*(îcB>ʑz)C9ACt F9ȀJH^ W1Q~vGe(n}aB [?DקGTjF;HcPc@#'kOqя8l8A U=#DEQLB ӿl0"u[2k} cG& FŰgxnn> *TwTн0 ŖzL1*8\y)4Ow} i}wZQw9;"Z7{7TP5yv?Ċr`JN֮J/Rc95Q!GXB 56ݍԋ @ZUs:vЫcnTM#PڪkȎ)UEG ch}`JNV,%)1A^4< 4pzd*, y_(SFؓ*<][T<0U%'kTQlOS#䌝gxZo׋{h,6}6bc Ũ@Fґkxzk"=ёz@$'kQдXf&/>PkvD+*I4Hg%? t fwA4ڧ,g>K2iXrnׁf\X5+^3u>[G XԽ lEeR#,4tXJ+πU,ݻ>*IJ4Xy&Ŝ{:SMP%zB*:|^B\\5`bjTz`GN֞P$d+X)$ȉ ` IN֞P$!+ .)KMehtf72E.efHC>e?2ÈRK+Cp}ԡP  Ij# -44ЀIN֞@hP$%A0hձ}^! ߴ&ېJYft1[IX&.Q@1tDbYh谎`e`2"Id4S5hGPtߴ#f|,bQ"}뿃)9Y&X&Dz#Q14tL=5=2)B))j}p*@oИm KmoZLAcx>=j|)p!dt0j k:TH!9Y{+ &:RMBC d=0jRY@6PcW:AlmDH'.8cQO@ ϰVځaɬf EBYDG Yh谐`e`2YBёTfSowV rrqug%HymÃoySxA-V6*DSh:BYׁDD6sEJa)AZ`2Ѐ(I:6+X$sj"^p^kBM% }H Vҋw ڑ;[EFqv[|x0#'kޚJPt &.ƃ)(9Y&(]L D8SjZLX/% 0q%}DVT|!L5@>Vh@ң f7DD V Mf]>""@KjJa)de3 J@^95xQŗ֡Zqy ï|,=B1JJD?X?L5[ɉy0$'kC?UZ'cpZe/B[b(}wcȺ/sz;>=jP$aouТxQ0JlK$NC,]Dx4!9Y{ ldj#,44̀IN֞ƃ&)L$38Ip4cc٪]U7TdCǢYc?dEXl0~x,*cR4%LYNo5DɤA|d#'KO/ ұ~) H]%S ~. /n2Ŝ'NQWd `rNpk\yW`8X%shL!nTcndBc*)!ȅ x`JHNQ!$B%cprQJ<SJ㤭?k0=4IG!,=QQD*Je@$'kw"ШlH-A< 3ZGt$%),ugg6zLޮhIbj%E7|S:\0/#\kc- ڌ]~,84.^ld Pc#+ :R@BC%GN/ E?R:)ԀA y[%TDv'Q@!ca/[H!ِg;Mz?N&ȇ `GN֞ˆ@$d+F)$ȉ 8` IN֞@$!,,0 +wTiv-\tfzB8 g?(XB9V5*8}pg@#'kwu!(*Tx:$9Y{D@BRg90`Z-Q_wX*βeȺQ¬G;lY[- @aؒmsG6r؆f įи J'ŠGJ(S̩ABELUYA bA \ nQf>χ#fuq|$)cBbEC=H@N)$Ȋ ` IN֞@$!0mbљLJ()QN7>B"[ig FRKn%}IGGRJqD SUnMӫX]a"R"TJ*9Y{<%:g$xB*"9Y{rgH_s_8q%*z2H^Cc\\+݈ԧ{EW]m𧨎SdĭHLS hH@ soԌ =%f]fI`>\.[Ge,452_zGn][4h6;908tL3‘݀6,8SBt .gSBr I< kl*J)q(SwKߎO =,Qwz`A^0ukCrf)W"묭-(HN5[*CjFN֮ݟ$d#%$:RHBC IN֮ I#zԙgjKr퀄#ѫK;ms,;:w*rM:( 7g3ޚdБʁ:5'3")K L1iL =@*t|S2esw;!8R 'Bd֭>S:rvAaHA@ ] LkD̩ v*xHib3X0"ŒҀy8ͨ xgYaG [鈎0dnm2y\XLL1&-G qδuSb/k]B( |/3ӣMt(!bYw%!vJ<'3D` EM(Mё:)3#N8"$)AQsjxuʼnID][Rxz}λA&twΉ8,~>h9Y KP/Ⱀ(!A< w#=HB:4-I!I"{jq+tH NaM%wz:RB4Rk"6EQq=U vd :( W"^TErI_,118%7(u(Q <2^(vkP%؅ND> F. ё : / _0m%BSj:C++" Cj&IEh X/bW[pM gE ]S9r6DX#':RN CCp̭ XCRH C,6Zk.uF!ZyUpPcљ4B~!Mf]4)9Y{ '#:RF CC@B2x HB.KZJ /P[I%^M3~㿽tBCRGrd/ /2Ŝ<(d>ΝuZUȄ0u0U:KC CR%4:]9|d)DBB!ёBd)DHҢN\*S ɵ3-#TqR^2yz.\=庇+}+s̞rzfXL7 !pL_fE 1'QeH)A 1R')IccCvC"`ȽR6g] WȈIʑ z)#5+44.ldn^"!) L1 a*޸FFSsr)*5֩nL=4Ag>o@Yī7q" (kDs0#'kwnRTt +.੃)*9Y{R裩rv-%qF )DV'Cٍ]0{hlBY7Kk(өFhovтC |d횀 ޚPSt PM5MtDKצ YJ8Vynvlvij15(a^TelYWJhi`D6lv6BYphdH%L/L:R?BC)aGޚt/ /2ŜL!1ayYv$(̴.ˮ px&!XL?]={yēFO]} x`jGN֞@c#H":RECCT=HBV,10\v̈uRDNmS"v 4Ⱥ] Ze]14 0jٙrpjO¡\Gw}Xe@$'kO2(*U98-ёZpg@$'kO3(ZRYp*Jɬ|x|S:h jX\l}JfK}Y+ڟ^NRP$Uv޳,842ln>" S) aq0#sC"H` ^,e95ЁkNR%7 =w0=w+IG}λCׁ5PF F|d횀` [Sjw05%'k4e5P^^ PKq5Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzp[o6ǟOayEQeQ\Izҭh{2(V ;ĖZq(Њ<8&1Š-ҳ'At}v=y|%Yx3\ff&u [7SCCuxEiV8w":sfSgy"VML[A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$XMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKq5Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgn8@\C@DԅT`HMSl؅oc12dN]|+#<IM(Oo?2:͗KN%Y|q姛K.e^\$kz2ʮȿ/*/J-{_փ&5ܽYB_.כ9,cgQkntӼ{1 m#HKZu^Mgz F; ӣXy]/cޜ}!NCхN狲 6a72rׯ<]oEf2];$M`ssAF^i(sH%6E{X~x:OӲH߾?'R|j y'*W>?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tqzR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKq5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKq5D+cQg Matrix.fzPKq5Dt#Sxgpart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPKq5D,搮#]]msvg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKq5Do#Qrsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKq5D'0 Mwsvg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKq5DZU+UC{svg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPK fritzing-app-0.9.3b/sketches/core/obsolete/Nightlight.fzz000066400000000000000000000264251272363601300235240ustar00rootroot00000000000000PKr5D,' Nightlight.fz[6W(jbNi}1xfu`5.Iݿ~ R R<&Xh#TWx26p_4w+D5\?,y/,~ =~{#Mxۇ?Er??|{|{<n)>EHzi}q:6Yq{>|=!||df1khlAۛvro#zW3~)|?N4 Uu_q}nᗿ%n?)޾w ?S?ַ{ 1ZUu}6</-w )KbyVie1 ç?o*QP;-~Pe ֘7э.nw kx~W}B_o^O9n<VXen.nR~+| wLdI,Dfcc֯z˽H$ >Z%s8z~i1ҷʩgcpqh~_hor+h~NIL?}X0"R*]fn+[aS+[Xف+pWvF4[d̀=**n\n$GL$Kd~|u{~ͽ Ɲf+39H[olz&EwYfQC`?ܟpWÀ{dub6ž.va} 憻gӪ]PZU!*FWjt'JU,zt݁fWDCƺͲ.7-}Mw3w bu 6([c#ZmkhDuf+ .&:hm7~97݆lk5\H\+#[l ߏ-h6[1~~V0 鰹}ERa|?25B0d[،uőS>Xjy@}okF H0V!L~5B 􃿬gY>7LjNj . u]R8$INa81oęD0B\7?3UƭJCgRȍ+χefg=^}Xo({C7̓ל0`Ubj%ҞIvih{06dS, ! L} ï9PUE|7$焩]l` *xIfnP2 ՞IL]ꩪLKӸʩIjߝ*~ڇ>~1MA-+.-7i<w$^T`y.1%X! 뚵neڻիM*FVksA+VvB*@K0zXIgmw:u^"h$AX?#%snH-'u$$es$6)QQydqw<12KGbpl;)cE&QFԚaUAU ,n C#J91<Dٵm>}2"}v~{s={3ݛŧۧQ?ſ_|ŧ&W=7"ڧEKcq0x~(sd MHXQd65MmioHo_ ,yX738G71 3͐4Y|u،yQ$SOG.czޭk$.~VB퍆/@wڷڇ.מ^Wɗ"˙/q !װ ɜŜj"a޿kasv^$ܨ!1Z*{jIv8?^q=m:N_aZwOf Hh>>ctSty|9iܥxl9_/,[=U~@c߱f߰yzj&DdHdsCF4LF%H1q6ƺBYy2'Z!a,4bx# @F[6es{b֭+n_~ v ;VҴtu{`NGntPfi4 {ڻuեbNꞎ{3IdLxuSuCrAsH=u=mOsS_5_? Nk>>/4mZ$f:1ҫyt٠'ҩ*MvidJ`jW?&@?M2?ӎz*OM}|A!.bF쬽r0ivZ9}0eaXt{ o/ٚj;蟞Xk T}VM w~mE=_毁?>60΁=7Ԯ$(3{Z [=R\mH.G$_oK#D#hZ?Zbk*=?P?KYZ?riȄ]Q@cOs^׏l0bւ^!:9`&.u$I / +%i/QmBYS)BKIV_Jb*oQ;Ʈ%]<yWLJZ&%dH,<9gbg$| NjG@K!ef~"(dԹ$#ƃW j|; ;D΀gO޸!`DN 0ɓ vix2mВ;wآԑ9'w<ӦdF~* o^BfƛpIٝz3XUT\)#$@0biYN*ʠg{?\A۱t^p P< ~Lo7JͩLJYIEvhf5IW6倕+xk'd@(O!.F!6g bY JV:ԍUv WGDajS\&4D;<ن:ΖhdnZnQ2ԅ|gjF$!`(-'9y$CLHuFYaYm+ǐ\ e .~ĜFȨ)!3Y_x.+NuWj6I&-,Cg[Y+7A0v18%a49c'+xR+ EH-Y\5>l-F.J:z1 7fDfpk֡4{tm璥3Z|@inz2q#Mg#7E4022>]* hҋ1kqg dk ggg- na!̢[ϊF:3 cDvSg] c|ED 9I8Rg ȔnpSA\2pEE3GR"$K[۝iﱚ 4؅ (T˘ ^֒%(놱 zm~ j(P'q h/Ѐxy vT-np\n|cйvT1DH5hv@ZљNtYH2.2@>~O8#$Ո#ѤN8'ɒt9#vSBvWr4MνZ #Պ;]'OЙʅ#vS0H=,E 3*ȃhc~\#Ȓ$3e@~[s͸JҵɀĩC YA*&dUE93Z`X<>1C3F;- \F-1ʩL jƀhPNܸ<ANLMiPSA&uU3(E3;-K|I%KRKߔ*DkL"Q-e:~ ҷNWMNb-On,SvceKq{x7 IuChw’)1/i~156,̈́~3YYUsuZWő{.XW|'Foz )^D.qd:pxM55t&Y7^@._0ZV6xˊ9'D˪~',R]0^V#VURfU$Gxt+/6XJ"K7+ UGͼ.\"S\ƖCӫ<ǟ_7cԊ,>뻧}H}!< QOX&IKٜAsd_X7w0~:P=М6zVa`9q ޝDBu'PsaFt> ,;V>m &@9}iMVA2Ou7j؃>zԈ(\HưfivPkAp/kO֨FX 5&0 1NflEi47#JG.P6Tu/wMKE;bfk=w64L UDn"xgd$ݻ)ymn w7H6#M⴨M s[wC{( %kFp~%H +)X DU;^HRB U2p|Ye v&K" Ȧjl@h1"j/GTFCᎥ/OoÕ9tPh%P<)L@[~E7x`V9HU<\v12 lX@P~)%_?|d& pV_#am#a골 SJ;n H6F Z6$Ca평ȶ) |d $`$dH6F0 (b,8Oy%xHW"dF2t| wjt( Vd] zcq n`L3Qu|ݎ) Tpډ ~qOthG+.Yj"$E8鼞CgJ*-@$rN`LYsD#6m[ Rv3ʥ278",'A79XfZ/&X?ܱrʀ+Q5 X4ͭ:^A xf|V>Ȑ6h3g 6c`Cd#< C\ZPV.A{_01ᎥɀbM4Ctx!FS;\5 D?d0S'2`?2~wKbX(u|>'.C? ?ݿlmDg߿_}_W?,y]G/jj5RiDV|ZYۋ#dW^T#x8FR7)<bϻx?..@%jWy}5'ZDpEbV^ mҷ j(T^s{U ogL 6tDT&uJ]u{aU ﯠAl~))G4+WŰ/ R+P s&Hn6!e4LF5Yq=<ņ$λ 1=s臰JbOۆqfTmo}a[@c,&wOan9`,>뻧K,|xqIahڊL_N]fLM,ևSKe[]8`o_8i`tιq :16?&4] :^Verahm;dTG۞tâݍx7pY9 R<ҽan{Þ7Lu,[2ѫ _IVRս %M9դt%MEդ]+ rcBZH`$(v&uEmx&Qfq35|]i* _v3(=Ym#I5; rs4W /Hۆ.Y W9Įa3uZ((ZtdzIUlYd."u>‚ۜNGQmH-pjKʆ>@K7:)I=&Ȓ7+|W!8: (ϧ/yll,V1{C"⒵˃"]_ zNpo db貆&R#T{ԝP]6`ԴDRFd]2ЮJÖY",/&OPAIG&8 2NÔǭ XBbS>=1/w`ZYK~Ib{ yon^’eqs ƿU`_,D9TL%6T¾o:RJu8W;H6,ыĀbGVI􈨾`r5 iJHԠjtm IU{Ix裄*H$hxj$lIiNRlJ3N3LJ3Nj)NiV6EK $ nz&=Lm0qe^ML҅Jr|SI@v1CtOv/hٟw/\"iMKR$SWtql J#T 3ʚ ^0uay1 A>vi* 4ݝ`Q.roQO߳b*/LRQgRp(z&HpQÏJ rX0|w橄~7YE\2zi:z>ؗȎ.چ 8Tqeph S 43UR$A.< a/2 V$rl||$&]<#:nZA 苿N6Pd*l/'x ɌICdtfAk,0$&`hj@ŽXiNQf]w,(<8R%Oh'ПYK7N  H&Fʑ79TDJK־"-kDv@%k_ELdDoƈC)Lj(#\0!XD펥#"'H26=!LpW!ĆTDvT=@!LqW@! ֲqxm9_¦űEHe*iF51Y,[ Go1cV]c0cqii kV.8c$:ۃnrN) ̜g\yo},P*{F- Y ^8i9uab2-UE\yՑN J2Uw.z";'B֌XD7tIN "2$p9pҦ|eDI f;M[4HG:4`TJ*N8x1Qc@5uk |Ȏ6{<|8nm1 =o9e!F1r$:T7 EKV|w֥Buɹc) ȉS #( $Hx)z9`i+3V7Hx1^ÇT9]EEvㄖɌ'$C9;UBIV~cb։Ϩ _^]:bЮم@%ci [h=jT3\6QS{Q>< چvM}0]K 0 l7 /%K{BޤE3ˇ:P$;Y!`㨡Ry|THpZd~9 GDI93>k:ԍ'όBȎB6ԉ' BёP,I:Ĝrl?`V EB%HEoOC;G apqHwDV9LJUYIv /`#05%k_Ì@td]DvT=aK־ɐsʑ#>CZX#՞A2XަgŌc&/֋[h KHOZMdG5hKΨ )!Q,,#zYow=[~]O'2C? ?ݿlmg߿_}_W?A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$"-e3OCKO̪T-]I&Y%ٽ/eLfdɬ`@Sm2% g>>O?o)D#n w7^_x~ Nۏ{?==|=|߿3{?_}?n?}8wo|xxĝzbq(M?}cû:Uo~~K|χV\{'N;ō~χ7&χ/ŷ_/SPǛLJz(_ۧS|Co;T~Ϸy>W׍o\ />?V| *:pW\EY{P}o{{GA;m߾|S?]Sq#]n|<|z~o?hwgo>߾Nru_OcWKa5zV \˯z{/txxx~Ma*a_1V|"2Z7 =T=S<R6>]0ޏ@vqwh{ާh 16><<.7l9vx͞.elݵ5s >c[mȶ_zҺRYق%hbKl(Y'f˽s^ S,&+:q`c}2Scf%9xPQNG,e];)~"42QRԹBwȝ⏚ o'h^ ^ "Ͻ/m zK{"IB1%,m,Oʒ0^JsKq>Xy~=_ͧC㵗ZSp+>;Sވ]~GQuN~|:<>]n|<|z~oOwg]IxCY4_=?8OcWKaOzѸ@jAVkũq3_A=%Vcgu;7~?${2\0ŭZYfXFɬkqg]9uqs$ŭ^"ŝu} V@u֋絸e|¼XeY^;j`l338ŝuM%nx.ZYIJ*o26wO2`n pQQ8X)Ga[Yg=@~) cu÷WHN:|ga l B4xDɷOHbk6S_fl28?ɳŏIbi_3kv =p6#7UF:gu6_;kk8;jw8טиnB#ul\Fz'#zi1_;ak`\7IɋkCFs Lx*|;ԓNRǔIⰂ@IۻRCE(5$*(5Ĺ+5;_RCIO(j9`Ӫ*<ͷU&wĊUk7+N%S#%Z:Y^~{~yz>|x(>If߿w?⿷o>?g[w?tLk]*K6Sn?\;W -A3z{n"CS%X6 {3c~P}c74& @oL)U['Y%vRU*yyyd0V%GA&܌gMݧFcfLN*YZE1 oŘdsCfMzfd`>Dz@1xi Ƥɜ33]H5InQ~CQNwBLkU~DؑTo C'!U%B[.d&b 3TDUzW}qYDp,CMFTV#YFE3<#Y¬$yLeP_76 vzw -9any(~>-[|RNKr32JbTP=O;3ы 1=5fw29O3.0z4 40+{Sڂ0<,Y4P[CiJPV|Lu-q/G0tQy~~|^\W%)&VG%[x_D ݫx7ut _!5]"^`/ e\x)`e,+ΓeIJ-A+_ٲ\&ّ/& ]^|iK#h{ C{rVFu%1@|}7%+);1+ZAL]B@\]2j\,ޡ{ 'U/x$ ЬtAp M% \^<3 ŗR4E񩥙(aڗja2'V KL-Ov}Ej%?&ZKeg*M=<*F?%FŒhWKp^cxݍc\[w9p;T:>7?w|sO?]xGHn݁\Sּq77Ϗ:|:O_>;<}͇/kdz%p>|.˨Ɨ)^{eu"//Y} qmlaҹ/h2ttOD%1rX\-YK|;yp sO? 4-w\03dI8@P:a˒5 ΐT.{sփk)4ljoI'%9tdKR{JNҥ)pƊ,pN@mzp)$lgK.l)d_k +iX},T8Kn1R%|-: i? +Y[[bz/X,$i)`גB%9^񂅻46R,,1WU\JJA`yua{KV'Ap6%St, -4xBhn+qT+,abFu#Vjo>==<~|yYUTC/pV%~u:"UCZew'ͱ~d|ĻOqǡ@o<~{_}(~w qcovwtxgt}AYZY^d_2O/ۡ[||h>ݜ&ӛ?-n4ii#IiMI6G}kl.Eq"_VOC"Ihh# g[AR7?]V{@=?^8 ld 6 mҹ|\%B 6NP{PS/F^c{6x;!84[?kd AM򂾈6/F\"]Ʃ]އ]#h5 "8kdx팮108˻&rmY[T5 PR1S%9'BhNsފSO_¡%N]K:"csOKBu}0ɼf.ז+e.nu'4Ş&= F `ƙVKi\5 J8jW5 R8jLVϻ%WWƹW$ݿ௤o|? یVi_+iį3+O5tc)KiQG) 5 R8lt) 6i# 'Q8l FWԔ0Ki㤰zZب41 J85l~r(AM6N^;ElS$J9=6NYIrn6)6*JPY qe_\?]?</>˓sO,Pܮ{ރU*/[t5 YW75Rkyz:r|8Om`f߿wosN8/O-SjP+7$?^H}LS;mAǯ>?~9$'U])ɰ;4"%wޔ0F+77 &<7BRc`di0xh(*a#Z/_q@`RJQFat"$cukxl$'KO{@"`OL(0̖PS3'WZr(%| FEo6ky@v63‘'6xPx\(@gưN* 7L+nQRjy׈ yCٔekO 6-WWePUIԠI4㚜#RK>딝5Be+#uTe`$$&mb(4{._ܗ\;qq"w¨iF|,TRjN[\E:.(80DI`0Z(“بWҶ1Zg踄`fg2iI $58 R6ȬD[WZg A4PzD†jRpd3L!H΍//nƅ&VaLsGD&,.iLVGoՋ*\"2Ns &[\LdFD&[-p6$ `jR3ePTIIep"*+9"fhT>rp f<.(h -h"+_Ɨ$x;qj\D!c|r2>c5JJih,1 Rǎ(9HfޡdT>rp Hf%3 e3]M,_—DGQhƓvk< UEL LkJBB"$0Nk e59ӢռCG=IJiPd$&3׺%?[I :Ch/L]B؄\Mj2[d@$Xz2AQxvUjUQθ.^Z3< j zddEA"?Y}d#tj0*q7g32pe!]g^Mlq53Gz  4p+릍':a* 5!mxajH#1-n?Hjo?RB Cxҡgxf`d3aGKj,[g 34ˀH>^ `4$9i-3Ւת.կBgʼCé d3@2l\4c0L]/U^;AJʈ:0}';'SImrgR-pd5qA곁Lo$08 1縖gŋP۠5ḕ`g2iIDRsqϔ%D,Yn-LPHha4js֗ ip$`(ⶐ\M*8h@$KO38"ʻSDwbg08\1@2< j vdXG?bbe@ GP6 Ӟh3ЁY Acz+,nfRC↰Hi$ AQ-TxpδR3ŏ/P +6 y&4xEDQZ (0( ԪzNl}+g0qscaR8R⎰gA4( R|v^j˳:ToM k5l]bh0u#'k  H;"bq g,oeAYv#\7;ETJeN;] lYam l6[*&5-4Fc`$KONi""ɉ,Z:Q )˃HdޡDV;rF(d\*SPLThKqIiƍIUl+N4 VҰlAƁ3 J"uW<ֺB; jޡ ɤe4 \\F .jnZ"(w4r֦Yg Dhs;óJ jJf["y`I>n 3khg(czhPUJ;Ĉd+`FNnDžg3.DD&cN9ׯS²Vfl)d>BxǦ˴HᲥBRNbS眏k͠Gr;alZRk"pF qu9HeޡgdnΠH4(FvϞRb:7~po<_c!|?>ce>pP(o>|<|/7?CP ޿4Y„vWf]7opϧkNSp*6bOŗO{j-7]di[!ZQHn)f =:􁽜TغrXݵퟮw_>>+oO?]ߝw/Çc93S]_^U<ϽO?')-C凕,%%qe4sD   Ux?j#_zuQ^^>\myz /O1Ɖfur$JNy|84gw?.p NG?j#w3Obm5|܉o`NMz*7[mﲳo5Tؗf5-N̸BY"D,\1CrPg >=Vo߾lVn><&U$NN&~Ufά^>_?~C&౥}_[A}хa-6Ʀ0017!͵>$o°]10<,'C0LyRg\p0P0 o7us&=MA#\»i AwO|110΍BG!p!yō!S kIx5k NO7F1Ac{PҶO q B&Vq7s҅To9KfAAB`cכBЎ!ea!lﰙ*w.-]a|cr_CA[xf8a9VeP>؀8~ oB}_C>E?FZf2g5?(??hڏ}_ VҶ2ƣ+ 8b3 ,  WSqje$h !jÙ}xH%UusC\  zxϊ,CI 2%[-;(Cf aG ]m ȣ-n2R Ւ6}$8m226kAc0ek?~>/$e+?qRAi/DXcCK92u!_.8cS, BwB'8+^0:],(B,a1O%F6΍#k?G>!?F*<) &|ڜbK =1Q̺20) G*lˈQl0'b'Zu8݄[zGQb%g(e_ #`1Fع߷'?R~ -Fh #@y #icZy.7 %a #{ b#3XI"{,4Zvbp+ ~xiq=2N/rG|(<C1fպ;ľpS!}iWk{UPW{ƫGW ͻœ﫿p{|Kf߿/%ʠ(Ͽ{hÝ0Y,-rJmw_<>抮:u|pq{S2B- cέb~F3dd8rt⯋PʰpC%#Z@RkR0~K.(q^0r7 BGmlynӍ+e{34ZZRҖ^1j!,^1[g - ,hq AŭQD.Q؞ӽ0#ɑP lZ8ڴm]1 ɬX9 .8ʇyLr+wZ ]*z̢t<{H]ޡs P13uMM(9 2-yW :SYsUoO FL";Fm|9/JNk@0@ f6ґX~yn ϕt!``1lt"V6~ !zydF>FBKhshhkЋ+N„cQЏ/T8r2[2HM(EU]'UHm-ޯFQTZ,&Sc_j=~rl)kqg訐 \ n$+a$Q V'U'dĆ& \ SC_t$U}K"@D캁4^ƼCK`FdiXO9jZ;!{y#|,1Ɇf -mix#p*sDа:$ґ%FeҖRuŅlYOS(Ϗk.Gb0L?.-%:LJ91(qqjb aa63xؘǖIe <GCҟ, ɔύ%\FJXghx0 jdYp#"=ޣJ"jaU Rg걕:C),աY_(l YR Ư9׌0(.n\6OItXN#LQ7X o(|:V֞+z>B(ל`F Eǩ`2B[&\h0U)suX9GXYtb'4:x?xjgZT CQ灬CwgV=?,ͬry&U|5;)QnsW>xJ*i(,@^UV?suf ]ٚމ`L$~(!*vI! ݫaLe$"r@7 B(W~/^: SPmo.fI=I;:LJ)@I:9L[:H{ՐLK:}C*"y;SHǥNqatNFR's;p>w2Cȝ7!5'd0m[\4"wzUC05LK;7F ݫdblސ;MdTwJ l+|_iZҮQf]Rm=SjM,ǰ%'*9L̍7WaJc,"Ws|1]7k+^㾾> c`[DζVJ͏ls 3{"ݜN$l- \o(K]A3 kPhx+(HqՎ^e[' N[Ǿd_}C^@şw*T;KTʮ(1= VcXe@#+CKIJ t%" @TLM!uPC[^L.?;:UD\fJwWeŏv$Ʉ LM&r$%:+L!"`]0&M)8WhNL%F٨9sfez fez&s5EvB-+$ĚůYV})@TEWxU1Bx5M)U=ÅE j5S sŸD_ȤL2ډO0H6o!ḿ\n$h$5o.o:.ț\|DMr|.+ڭ'dvx欃;<' o^1͈86(~ /gG 5X'IoG0`2Ԝ>PӻD@=0TFA$ tuu]]i e->kh'T;ь.A" S8r2S@4Lc@N(!?xM6kH_Cj9 jkkW0 9f*@:.#ۇj5|cxpU#9]7zT8Ǩ'NE`V8R`!AlT 3N{4Ngʒ%cUUI['m1uyU3Vn%#?$Μ^pi'R1uKF"Tq޻gԵ?/sAgC'`+g,Ԏ:|c7dx@bS9 3|?Op/ oOwg] ݿ; #ٺ$-N*hP[g9oOOw|d~dL?sVLj >.>u}S5 W<_>@LqU#Οϝ MŒ HNd1.8K>\\U/tJՌ[ .8u%oy]|LIʌ Nu+g%Oy%{J#\=qӝtte&k9ӿe)'[9{cug Wf ǃ{y'S| YV<0A/߲HeXŲ2^VV$U+;|dŒ Π} Ƀ֗R`OL jRHʷFN_JT,+Hj%,˲gnʧ%31yS7N׼ \pubR03i:Ts1$mxL:;n/hJ%,$5re5éW,N=MΊ*J&L -9;W2I7'SM_%JVy$x\ 4aIN~YXs(zZR& EUb3ĊC \i z'D݅8tJ]Uq!!VU!_^çPoߞz}n><Z.eovwtx.^ec'cʩ+S Bc=~0?~C<]10?޿t*ͼS[u^Z;/P M%Xi#~y_%K浄}*(ӴwZ& YXyR^T'O 6Kڅ# MR'[y4νk')Yw%vF>Ab MVO#kjckh儍5Ilf:4Zԇ)ce3ӡkVXyީT,ɏskGkg֎=!ɯlq0$}Bj; :0az,bvدB:&b9cguaJ&Y~CzHLbl]N(̥'LFZU-2oyuȰzؼ{翥3?0Bpswͮ8r3ǖ\F LҌv 77 |&TCG0?ͮa~T$`q{]Kgbz~L}l-m~(j|-Ar&n F]KP3yLLtil1]Z=_|t-AZZ;k||-|9.{,{-M}ۘ}gЍ}lkn\k_Rv3c*8lKO^pgb)k7[jWo>`+@2YK3\@F]@]rHZ]he.%uV}T@AGvzfڰ ^Ꙭk_FqkW? E/k`dicK lݭKoFkߌ'B "{KIPNئ=_ͧC㵗xˇ?p۲U/|矿~×b_ϝ]7Oq)űygLg֧ۭ_N'+rbwY5sD½Uo,@Ғ&ʖyUĈukvQ%a@?xMa߹=v̾_<>(f9L~|s{\^GAiUV4國?m&>c#Kw^pdY)m)}%-=Ïu6tW5r0"[m-םp%yśH[ޡ->0:uܒ ikf8–gh`` ^cW+6;_`S0"oad $o=G4rF-ɀVy2CsQM d?Nےˆl^ً)`vVړkRK}b8L/RTRq\9 n簎p9Lk"騆=x1.Wh@ -9Y{2Q "n[d#ei[eVJ@ 7aNn}7bhHMb˻m  K?Xnc\1(Xի58l$rd& Ս{f'\`a@jm0*r:H+7/԰<a5agI a<6Is;5W:zT9jBH^@E#K~֋ e h}]QBi!T紾P*9YgcG~u%^rB._X8d9ه@ `.dm-а/zgъ͑k@,˭~Ps GDԒ] ju*ܢSPe' !xyǺLM o#.,qa344TC¤9Q()_5|0$2ʑ{OiLE|)({$j'_Wx¯_]7~rZq/ dKjk) ӃgZcQ/(J9l+,IWp"'kψwAe@jS[Elo+X1\I:CaV-FYl-vDrgh wA|,=Fz׋ܓ_u6U/rt%QA*W^GEA,ÕH^{i>\ywl)pe@ߚ* ;34x/,`lN\-PjT5wZj//W=rv tb0ojH"gk;QWHkJR_eL-3 -AL`Kգf0lk*G>rE57}G{yb/FN֞yE9b `u,񡘔Dʶ*~awCu>(9Y{Z+yknm/Ц@5#KO~͙kT/_ʃ0ꑓ'(H<+cRپNC=*>lFq5 ZBBCe9YT9lq"287]qӅ'k4h.g#hNzC0e9},k#3sE6. Go!(M<|ud?٣"ۃ=4 VQS=bNCm| :aINҹcGBGn qS̶w.Gh)!!XzT@rt.CCQ!^vTAu0uܖ2m0< k/pTa.-10 ]g:ᜐ#m6mq{4LJI)=94ik`) Ԉ\DIAILv% ML=qf⑚3CUޡ;@(!b\$5l+ 6C!y;Wț&rX3)oX87Ty ܚc32s`u㽬FWS mL&H.W216SSӦv4]$geeEU4:8d2"lzUA0u6CDZ!kvUB21v"5#irr.;vjXծZv Odqj{gi!Ӌ6!Mi]ȜCfmќ~v.0d H$#%PVFkkTQ1\+bh@㧟 ;(Z*KŜ,'Z$Ʊ ıTT|̧j$ xC6tl4ݎvrXgY#9O34a&.*s}ɾm6} pH_g ;I|NIEˉu QqC36э֜Q|, p/J(Pݸv0;t.d9YnI`q ݸ金IBM`HrDޓT ~!b]\BQG0 F4$ kX?ʀO<ZqƴiޡX!FFV~Ĩsmm3P9Y{]*G}1^T:d""0lnMa0XP3@ ~DELc‹u?U~4v%Wc7ȼC` SKǀ$'` -XS:11 eX9摍fXlہm<|C`JGNX:D>q42AI\D%vʹ üCZ?(&bbs$&1l$/0pN-SCw(8CHL(ӑ Jb)3J{+f0Oq")V 51'kEf$-LLC`²)|C>sY$9$ #*7G' bYÅ!N'`N \X:tB_s4̈0 Yi!XM\Xn Bg訖`ˉ$ L ;3W exCJ%$y9T5Ǒ8ROAub`&𜃁A^#opdJ״@95Z\5b!}O).V344^T=d@wtҖra74<4T>r ""!GqV"ӎ.uܩ+&5˰E[Ubp4 vfaZ:!yJ ƕc&怔@F%V TJ v*x5w:CI zd2( "F&*Ra1>~U8?N',[)3tDY ˦FʀHd"vMɔcl[y&2FFC"r|؀qP8t6M7ƘDU\ogIm'.79o;%%QsL'<@TA5O/Pl~3Wghp |d5XBb^Aamsy6/;}މ6r(Z[O!yH 8]lH&.hS3t\J0704AuxQ0pc"UhI}aےt p$ Aѐ (#)` ֻaâ/˜*-KFL9pPλL|tÅm#yFjF>" sȶTTHנ/lU5 T>r( _+۸K|ِ*EW\1;Jy[-uo#Q18V,D v4Hǯ>?~9t]\IjsI: (4EWmno(=iμ6<%8pVJ+#m%gD:#P> 9Y{r Ą$8UVk1\v9aܰaFssLh'ϟ.v'5  -uPdlA8 Yh@'74/̀(HL0G9(qĴ&ҽtTytBllN7rw ѳDwjsf$Jf} 5rPRhʖW! Ԑ|,=Gz2 BT`B dtFEYљ FY3DAgwμCW3Mr :@:/mJgm|Cq td98D>"\f9sDP^sITB fAN֞Ce@R7OaJJ_㈤p"3J"1Q2o !3 0h22 0.p>nYտmhbQfegޡ38',=ʹvs6kK +ϒ=Z_j2ag7#|$$#O20}ytC:d&Y xdbtPJG4-Qb7żCXyR !b66I%DRNWC)%AՏ|<ĠhG{*@ 1‘g b Ӫ#T9P"Abޡa a&rd =}`ؙ0K:=t.%'m#YQ. A@b@$#O&1(⑜\z P3H xD$1;=y= h%*`x0Y{e{=]k%k9,R}Yw Fm1HkgU2{_^rc99 2$>qPaoP ` xƘR Ea֓i0bg ;tUPQ9DnN^qN %ԸaRǜՍ)l+gdf1b( TRINf D|Cu3dD=bYD=зvѕ-5n;YC5lp:$}g$31\pGXJ:BgU#$3( ¼ Kg2I  p\Wuĺ#I7BC<u١ .yN=Jy643ի+X<ìFb( TR΀Hj: rET+l:.fɤ lvoX_m:,qbH7™w"m>ќ™Je 4Jg') L%!AQt挨@g7t\9 iPD$1:"*A< 3g g*2YxT`Sv9 :dn6wKqbJR26zQ<c@#9- v 5nz€hGL! !ӂeLgkJmDEgdݨY_,)ύpaOQa + m"0?48<SBT"#,Q>Ro 1fGf](HAn8;Tgޡ+YPvs֗ gr¼g2vybg !h0|,=ҠHB`oDz#i+`L 30 D4I#cڕ v)Ip*-. AH d)Y Ҵ¶酥&JHjCI c醚`ƕ5C:#hU.kq?c3n-6$Qǖ+N"W!訆 L%~ ;|CG53d D9"2bL`0IrlQTdsi--T(IU;N`ޡbFĜ=Fz dgj.Hg;k-H{ۂPn0, ) AՐ|,=:GA> A|C GN\#p5$&d&!Y,2As9 t+%6g|Nsx H,Ǯ!T F}:R8Xx {ߔ6bS?1t۞@6P^RohXhd9D8bX0"uk澔%qk UL%kdžyAl2icJMtyAJ(c-9KՄ\@ƌf٦c  - XC )[D3M#f U:&LkEK]Y6&.N3B, oj-xbZ12ڐѬ<>'vSN},/`~# gTJpILg tE|C[Տ=#rECbZ2d(IJTiWLn3\μCOB2[̪r1VCR"C_y0Z}SXSy x$Czca0u#'CO00dd8ŲǔBowJO0a;4< GNC [ CX=5 TI'&7%AՐ|,=ɀsx!* A0 ` 9Y{Qt˂eLFf R l'd&2Ŝ='.f"`(>LX+dyߔѠ*I>DE1ژ/}d42a*HN֞DE"2܄QPӚ.I+G qJhF%:Ȣ91;n83ÀTNFÙ",Bn3:f8N4(EZ"YРP . tA|CWhS>rvC@.=*"seUJ^N lTM([\P:0>Jg*'Kψz#nTLDkӫezlqkDQ$A QΆ t6|CGb$Ae#Z}xe), hM/S@->B;48*f9sstSȶԦMl)9I$Fyƣwh`V\H> Q^ohp` dxFFb♊g  "pU]lZv93l9* pBJPʼ'+Iٿ,7c|#c#oЀ,P~oYzkݒbivUvպ=E`rNlqwhx{ lN֞c@6x/Xxhy%:y whh{ZI荨@o74=Ps( D4AiKLG'K򅢅GźWU-ҼC!%:9˅41^O=Iz8iJaTJdF"#;}"*AD G4g h,DWJZ%{s lqawhX.#-=<ڽ%ueBR`4 "[D?4 ~dlH cu e2 =#"1D0 Q٭$a ؖ [|+w m>`?)CM)/0b1ڀaC|Ԑ|,=Gb[~@Q7t0#'kO104qyLR<~-'8]yVU6 RABjA2OŐ֊*p_Y"eF b`$#u4,Pol \I x$}鹿@be Yw,-i|'Yzpa$G;LҴD lX2=eN 폾aڶ#%'18ѮD^셽z@e#cKVm@o74<ldnmXC %hn^ ERǬHF06lqwh0i r0AXg2Oe}a;! 0⑏' G@X$2`" 9Y{FD IJ e\\;[:nUiW h(K,{lq=bw0)=~OB$[ ɒ=!7gQ::iG{*i&$zA4**F>Fa`"Yρ 7|CGc< xdF@""X"c{1Z(4NP#b!~_PeSGzăP#%fPD$%±FQrA*Xz2AIn t;|C#a@#'k0(úau3~郰Ϗ"?o~pxtGa_n>|9.-{DbU|9s ZWp<Dn63 ? tO^u *XߊcƜO/v\K?f:WЕتr%]obm уQ :<_]ʔ#)sg+EQ xPjadoTW.,JopG%gm5Lƞ2}S VcSI E0'z*}eq~c$E@( "( E u#cJzlϬ$Sj-.WE" O\!bBPODJ<+0)aC4A#UHZ\i^@r&ֻ燏_}~rfB<4q/T(aQaO̳^ȋJAt&Dx3K<26` ‘*@ ݟZi@#&nN;ᙚCjuic_HFn F'Zcw*ݜ"{뗔A-yzb 4HH> bP#yMX2D gg:vΔ#h)o mFWt y@g0wrۤ2?H#-5-?awh0T|,m-+Ϣ $ tH|CW 5򑓵-[RLinORwh8y:.ҨŐM) ci!AJ 4򑓵Қ)&i Ҧd%t 3`:olƭ| ;QT$&Ȍ-zŬ%N!TYլ[qɉtf,B|(v02fҼveI^䴇,4ň=(af҅zMCkH>vcVp Й4_k%ZFH$88#>?([B+=μ0$O'4#4$#h> S@r@&#4v[BVPgsdφB$ųT(Q?1"gfAS 2H, BT`Rg@ALe0*ֿbT:ct)Yvulg^e'wڛ8[ ΆwӴ7{p֣(XGVt4:^t"-,QH!43LS DDbLh#-v3ĭ"&ϻMq h^`DK3FDGR蠘0Sic8+=μ%3T D?k BT`RgLg Ĥ3Dtvxb(\gD(Q?18rެ% di?h`C3ft2- , 1E2AeBZ'-̶KhJ>ߢֱͼh0ۋ=O[AGNwED6F H]ͪf d|UJИׇ16Dc‘5#}CF|D* {XD"Y=M/ׇE0VXt0bDr=fO4{ vف,L8TXQpga HNޞuDDb2d6@#M-# fTpk^Q&vG|q,r`8*bgȪnZl&a*xz4 8T`Qg^}T;rCяf##@0f)v"*)TbS,M0E(߫dM(>E7{Y4<7&0ogm]@dMaa9~sdRU$yC#|2Pp*8 iҘ]8-uQاeTa4OwOTa+)kD3w%D7 F4L%Ӫ ZLPH!4) Yy[BLh.BV"%iEk\L tDbEA) n[40$#OhQC#| @dmQ mi&.Y l.RwmvpD=8Gpe$&Ș& 0Ҧβ# >T=6<  &m-; i<ї4*JqD7 Na I>h("uCF"| 0$'oO4 h.2I@J\W&ZnɢPv;RԠ&f#Dކ&Qn~vuf/ohJ#$X04=)">*`lo%I6QU jO6հyF7'oOAM6͞>*JhWF5 f4L) DF3ZrX tD=@D$&Ȉ1MP",JG % b#+ isANޞibĸ>jJS0di*%xz4HrH ULc "#11F4i=n#Ivlf^g"s4Kmh4#Mάo^t|8"Gώ : LȔaFNޞ` 3QB X_ & 2TNޞ`fO ISl\^tԏ|<=@DX w .@aPvĤu1A:CBvShY[J@6~2i0izH9yoy2Qَ8į1%X"UPRW͉o_Z:9=y]xJcxx`vKFb :T`Qpgcꑓ{$E P WhlGqNU%B1ĔIf3!o':c+Itٍ]QA;#O5 d1TW6R'B|1e:rE0av񔵄ʃjcbe^lLp{eaHCX.#k0KZ27 &3L D9Ytt6Ԑ=@t$&E.8uwQPFګmt@f2󚆓J[;Kfjx8J*):l[R9Tox UICe0*BC"| 9y{HLF\g F;;cadX:0H;LZH@Z!hH]w1kXbնCiP"#xD#*0)3]0%$'oO4i&r-F'0M*B ʮ;ԲHw|veQTޛTkhzĸ -hƀ=% J}|6((X)(=JH>f0C0󙆃~`!1LF3f+Vf4(e1'oOȞiYf 'lbDQ<&" J6džgC$h1v)86\ȹ1^7=\6"=k|NAe⑏SpǦD*0(P3 ߷* 9y{ HL(ӑA@d*j!7hXWSͮu]kaV)X Ȱ|l+7<6c^`T|<=@#5*0(D3]0%$'oO!2Id.*qJ3!2Hsvgpi:D6;`YœzTgF)Z즱M SB24fPH!3d{XA"B㑡A@3iر}l`j%%quw/ yMz@7+PT́c݋ۍv,T۲ّU~@C|<=:[",8PGa2i0JHNޞ-C`"3:Jn=jl֣d^s*'oG2mCѭ HMC |F> c0c#cs =`$&&00luק ]IJoZtڛ[ (H_0֗[1br6Q$۲ ->_4t"X"ɱ,4Q1H3iEP  EFW gªaDbdZf Y4.fQ?[4dl4P6u~`(Ô|<=`d#1 = L@2L FDb"Yvќ*$]jz 4VXvwƒbXz46k~d Q 8PaG!0i1PSGD'TTֳ2%&3O١T!1i2 -Db/X4@Vxw bڑoV 3dC| d0%$'oO٬"#1,nhN U =b4њn4jG{gsӎ(i$'oO2OfO4d'Ր=&(Pv (C|<=Pd#1*0(H3] H""1,nhN--U 'nA֕skU0ֵZ*}&Pt]ʘ!DeD)ήE_Sj@#1-yȣ0t틘 ёLKC`r4:ng^Zv3eW&.)ìvHiCLB+} e!x`vF-᪡Cg>f򑓷{D2Q4\&q±=QɇaTYGB yMLY̒G9y[]t.Kk J!(CgBP4TEbUC$|*9y[JPZь!PZ.h5j$B6;*5]0rJ:Cgzeٕ ف"+"*0)x3]0$'o"1-rKioݪb+ܴsTwy[QUS#Ҽ}T~@*$z5e4[D')oJiRGS$hDF#|*!9y{HDL,2 L[IEh-qzPpͤͮV kaۼvL+:_|T8qN0g#zzA3N2m=e_g/no~}qvyͻ_|zx/~Ż5_˷o;/ً?7۳#ًjWyfpmY^} ~E'_*Z8aJgf 5}Y`hWIS}U7A&7\C+/ Ff/;>Ur3%֙$W8j`? glU["y__|ׯv&zn8QpqPyĞz.^ܼ9˛J]=R}r{U5Fx7ϝ]UKŇ쨘f?^jVz^Y-uhXYCXKn2+%9ʚz~3N/37H[ %tMRqQ#QU`kLS}wLj5n/>|_UwwzE͗^~|~~ߵЛWͧ祇hkdѸwUooPG֒h5 LS!ۻkְ5['sՆRG]zZ^9ϯ=|{{׫_*ѨOlȋTruU}cnסNӟ߾|\S}HޔKoμBisڛ?z1o|`]KͧWw/q%?]]>yKv|W!Ս%\NTgӘdzi#n8ٺbsߐK9>%\~;~"IϾ7iol"-ǀN$=;^j '(lY{/ Ė 6fUgMH !%݄knl)4VMhi$Ѷ 4Bhc# apV 896b$ &89IKG1G5 %W[<Վ8lk:u8f"5MR.}g74~ٝnXphLhaEE{d"jա)߇D~h5(dF')N|W_xi OJ(x(*)GpJu;'(r R$鞝7+dgRl9&uTah.REq[Ji1hˤˇ1GWA{8TZ:9G3`*8AK2E0W$;"OgY>D(i_f%OB3NGuPKr5D,搮#]svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgoO8)|$Nb;eeYH+ݩw҄6R7~)&Ʊgf2R+>XMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKr5Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgn8@\C@DԅT`HMSl؅oc12dN]|+#<IM(Oo?2:͗KN%Y|q姛K.e^\$kz2ʮȿ/*/J-{_փ&5ܽYB_.כ9,cgQkntӼ{1 m#HKZu^Mgz F; ӣXy]/cޜ}!NCхN狲 6a72rׯ<]oEf2];$M`ssAF^i(sH%6E{X~x:OӲH߾?'R|j y'*W>?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tqzR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKr5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKr5Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPKs5Dl2ie Ping-Pong.fzPKr5D,搮#]tsvg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKr5Do#QΡsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKr5D'0 Msvg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKr5DZU+UZsvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPK"fritzing-app-0.9.3b/sketches/core/obsolete/Pingpong.fzz000066400000000000000000001602151272363601300232000ustar00rootroot00000000000000PKs5Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzp[o6ǟOayEQeQ\Izҭh{2(V ;ĖZq(Њ<8&1Š-ҳ'At}v=y|%Yx3\ff&u [7SCCuxEiV8w":sfSgy"VML[A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$%&S!l:eﻱ?q2g88SJ,@aI4zPLk5pG~*#rG][ѯKԆ|2e a_vA`>$LCx-r+q.&7[Y>~n̉=o[Xg]h hM$jP~AdJs X\ }o ъ+=̀}@md}Q>dF9@A |ES O)/ph:Äs4ith}bLӰ~ n ;T 6c}ETՒfLv2EKK q/ jx,x23\SDzgcnIf@iMdx-ƞ3E4s&l` 9뤤oݸ]ճ%{JPL`GvH0Lߑ 8#Vh8h gé + Crֽ H@X!0X+ngD XI6V'Df#~;fUݑߜPˆgt+B6;Kif U*qɮ8y2JyD6;ʴ6oHPh-ݼ][Qyn^T-$~vDVooW]>IT+EN$QE* ]PKs5D#ho Pingpong.fz[su~>U2GXIvxHc 6Yay O@55 ,"k!\7_/n^=|(}"TA 7ݵyz_쿏R՗}Mg>\|ǯ7Çފ~Ά_=|x~x|_pǿ/ҝxzp/.7߲DE7?o[k~&[eF0o'io/ʼ|y^=zxm6߮3gze/;a/jzu^|./}8}w>.2k ÈUmÇD:-͈Tʢ{H$O]m{|1Aֹ֧q}huhy1=<>7Kެ9V ۻi}m6d[mk]K, [3l4}Ow">͆b5hںnl;Z*ݗٙ׶Hkm}vPֶ>6gNY:e͖iiIy]؏ kۤuZk۞~D9Gٔu~Slf3l!h6sg`lf۴4;es|`@"H}O2sYٛ y+JIcߗtXTm-aeg@VN#R0Ȕ=@Llՙ IDI#R۔%Cֶa==av簶30{$-9m8Y ='K/'F LגyhB|۷14Aۿ~|z?W_ w+Y!kc~M!'o# CIн$}&[1Q)9yb蔅pz#B %n#FRC籶HhYo$+C,v{@IO) LH7߱DY/c?0$'`蔅$$I(jL_Hdo]:e u:y 'qs#az@/Le΢H}RIH}W:F;  FR$ַDdgn%kYcN2eYo$1~!nKF&V2<i zezw?>C?4+VThr6b0uQ}V586/'Pr@:ecj8NڶFK籷Hjio$S,퍤~@IO) LJ7h߹TYSSXIy ً`b{ aIO) IJ` ؘv`߾`1uʂmǑ΍wY[7~$v|pF;HRFa< 7inL_TLl6vo%!?+乕IK$NXJj~*ٽ[IJϯ$W|GZ]/^^^|DN?/i}jqk^OVP{c c~jΔ>\er򺿽ze|p x}]}}͐Ou_~w?^/U p|Q@F:.^ Sr`w>H!ړ9(I/nN:9s0vL&eZ;̾S',ni-4(^<ŝt2=HUҋNg yI/&^I_T)R^.tĀ] 5as7IZm62JqXʶٖS"Ea q.IL4OfȍT }c3|#]@ƆMSC r x>lUneDӾGĮRIo詩[mc)K6F)HbGob0D*7ة8ob k{#񊝺 ĭFb;uZLamo$nkmc⼑),.vnI0zqÒNscb 8n)X:e !@bVl{#q!a}ņb3Ql nD3vRl0vWl0>ņѧYiOGo-yzrtr5+!9s8f O_>8~)a}X~k6DOD;u,%~bv6{//}|7JˈВ?4.p`*笤 )Gar&`OklBGsWحDF]]岈mu]T(\XQ7Qd0K%A7zԌ'Mݤ- Fc`Km׻b<5fbd[o!(F/5d=4#c7F+ҍxTGţJc`Pe:JgTCs}mLG#Dn!݈=/MC0K&љ2T"Sdl);[=Rr\Dn0ʓɠ``&8s(,u4:(M (G˛%C;52 ̺(EsŻa+Ui}~W{D"6#;n-=Y +SAo&@O4 -H[$gNH\B%W" x`˘{~i}~{P#ZYWFqaҹlŸ5 <%ѩ}xPƬ%B|.%,U DaTBtk9l{:3{-ZJCk.yvqޛGE{rb-lKihEOlcq wJ@bYԝHj̚&Zȷ6{/IdP5صd%ٝ 1 8p%(_l9h 5Tҗ6)Zi/-sy3;G{&9/(I鿄beeZ^:xKĀ8"ׁEϓ?P=+Y>!89?|x]<+s~əO"w6Q4z>Їo?V}ݧ/o?6!.{Hds?oo_옏VҶҷ6n^ﳿ2(lNuk<Bp}}ӱ掽XD@#onB7p rMXfRH~tʡ6O6kĽ@+j rKM}t~WWrx.1}ұnZDvq- ܨwu吖]_,ʁ,]OՙX:<@X#X7u% 5z"g2T%!nz/^bD\5@58ꈤ"!T}y9L=>\8R|R`I׍=s - \qA%&KiΥ4JZ9̤R@wvP.=ؤ[# yRZI+t>mqA]௤\6P\ؕ#]:o,Xn,]xwv列ΧCQ/q/Kiȗ 5R_ 烍gW麳Pc`ؕ``l ;r$LSvXN%h+ix;DL҅RR.њf;u2n {qE3#(ė9={ۡsr?Z‚ M[6986#Io4"[}z|}}}s,Rs)i%6hSS-$o!ɨH:V&d&o ՅCvG 5%m^B C<,ƙNo*j(%{N&3$~epYfmql.&?VjSSK1D6f"˹xXŋfTe?Tc |ꓙS-P >eg}Q mp֞ pVʒh0$K p"7ݠg\dRњ<:LHhG<&s<.i gưZ(]+.&NA& d}"upBհlx&D:ȠH:{dFC?2㚜sŋigF0 fP#%k?,#!1,nC6GuIP rհѹ"hm N AJBg@צSrJa-SYр( Fܶ8i6[B010eI $58 ReUdV9BRZm Gh0d!a 5B iդ)#g`*I:DE(θjWrjCg8>))Y{H\#eŅ2  mTvw߷Q,Wqb+ALeӡGW=NΠYlP6+BY`T($XYʌDZYË;G6bT4*)Y{y f<.(h2-i"/0$ˑ3E@ mӗ&I' %S3 l6 -(2% < VV91ӏr\#_K@010e) Ĥ Lb>@$|:BѴc=x#x5)lt~?Y`4J{G5BQhSTcӡ`C(36t3‘D5BP⦚Yʴ9DzmV$ڞԼ{l~"eӡLB\f'>WF#N2J{AQ̘%=:,0u;m'D#NG3򑒵Ahi\4P,S6SD $ZiC=2"ՆP3x6UQ L]1-0SO0e* !AhyT#SF^jک ̢EGbbi@U(M6t RBnB؈6^M 4h: c|EE糝;Q7q&T6S(RYm4򑒵 f& M͊N`|r6SHD:.zch֧ gyBj˂JR`keaH–, 0Km2 Lef+Jf@$qkVP ҆:Ok)e$#"Ymt$30Rv2\$%9aiLTFұt}uU#!֒8STM+Qolt4BYpt(*!)Yq If6.1d5]/_;2LʈVڦ0MN';%S g]d匳ɒR:)g-SY2"L(t{Y0>kXq-dk#AM9s@)UkDy,־&ۃP̩hBU˳>WFS4Xsml6:o,0t9P%I= ,gZsѵ3CURwL35B=?*)Y7Xbtl#E'% go=bx"g-x~ WY\ͦJJi"v Le3(JeVΩ3LËGRCT[I>,0EϠH4J+@4e1@ajgg0sqS€qW>$L: ci݉dq(;MY)j<8SjCy)q&0Xm f0u#%k H;"d1 -ΔǯDbp]*,Ų{Ӿ=^ Yl@|V6VL ]i,0tt,;,Nd'aJ c0 vdqP#&ɸTP$UThK q]c|2kq٬}p9dI)(mtWNA8Ɩ, h@d@3Dx(rjm;avz%0d4 \\HR$/*{2}t6 Ѭ6tYo |n1@%)ltW$f0U$K4O543NэfPE\a$`FJ.)ǙG1=DDcZ]C#ml&XmYc|jo iG֏>FuE ;t4$]#2w^ cknHsuZB1lcYM:‘:Ah$f믏ӛl:Wד̞sv?sn&Z{z~d5 Tïw7;K.eeoe5')n8Z}ffA3 l|# o 6f2=Y&6/GueYԚ1ʕ[!c窕!mu6:>6[ d: ~uPKP<kx| ~?1`ؖ, GEv"9?-C8醣 ƭ"KgZ5ѝjau:îxˊb餣)z񅂣\PtZ0'J%>L%> YT3 *D :_,4[<YπHHL5pA,8[3YĠ(GD㱣5XcfRjvy. A YXHG TR1ɍ <[HZ$Q kQzuHbݢ$̲$D:bXŕwDZј1K8zP?YM)^<[V5<"3 &\O=ɡ .}#abM1e* BY&CԴ gdn@6 LfQ6 LFWT0#Z{%MxG 0v*.6Blejb`P-ĎC< 3♒.Tv1a9JT6S* @5$K&3(4M݅d֞Cdv29*HJ@gPT$")`0RK!-2k$Sʒz}NXHAsBmqY!l\ݒmLH1iۛW zH.6[i6 z` 5y9ɍ:BI[YH8'wG5j9NW{tu7$X?-Qia^5: TARF"1MGf4 Ѥ$PSu8B.Z?qʉuRZO8DF !itϗіz f1#fmh$B{#Y@gV@ 6BCgF )Y{AQ;W@(Ms$VY%]3o(m7FiV؄+ kPVaq%JAJk:` I:9p"5<>2Zp S?R6?5` gZ3MAP1Y+tPEj߫rd%Nȃ#,@6 fzaYoi *5Iʎ b" ?D4S cghPddiD `C3A"3hVC (Vzƨ;óqh-}ZrA*bJBgɘNjm {(*>ҹdQbar$3|S i9΁+!X?5lCMkd6m,ꘔ`#SA 5aUhn#(p7R85` f&2)hf8a?2ɌӭM0v' N' NG@6 hjz/CEJL} BMC~vtiq?:}? eeGfYy{ZybLX'0T > &|dm )֗qm` Ӳp ;YN Oqz XK~b?4XaIFZ` HJ.IH}t4>:f:Tk/ƈĺr`#ATƀZUDRfyP=޹|N(K{d.6F64tFŽrk5VלT'/i~JkJM'M*4tu7VMoBخnJ+ߴ _|{5^vcn"40R2[Ky}c}nT'Zwcմu +qߴ _|c5^m'QkeljֺzWغ* 0J"%kSk6mڮ&J2C'naH:nYpdiXQ 1N& [\g 5Y$Ok{(%oeRfvm<-SŶO M+p׾µ0Vn%bwP)jR_{ MEqSQ7UƦ 6&^l%XB_wb{.My}fi;:@ }C'ajH:|]5?İ}7L IC@d$&vzRZÕ. 3l ̀GJֶX;D\kAP]-鰑2X[EKnRȄmIJX8<^ lC3 ﺇf B[:ߴ<Րt, ~,~h6qR&@%3 AQtqfV=h%L:lP6FL",H9^lиMhy2)^ޘ"Cj+mPy?Rl-T}I FdtY3QzhMϦP›⬦B]{ӴyUsc6M;&I oPwD*K-GQؐ+J-7Yc~BNX9CoZT?ұt4wVQ ֦ͻXJHJf@d$&Ŏ QؿS `7[lnJfH9pl5Eszn:B@k:`H: hPI6s|SAR>"LGn`deuiFlK_q-r?[ Grh:'궚Vw3h{,yI =DQ_N[.ʝl'`45MѺqY9͜)V YkGtYM[i^) 8)nIF)'/I-A uTs=TyqwI[s ̵a5= k0bzcUU$%Y]rb-wUt,(֩5`mpؼdEE"bQ1 FU.]%t}C%(/p:-P)9}pYrZCG!!5Lg05$K3 κv!jZNYp tSAR:"1LD3hQvtwdPnΖg1:sO]75ֆ!YMO8aXgHNd9~t"?YЩ|85L|:iCSЧcJ.=pI q 2 39kj/6tCv 6WMs:`ijBޖXel\ڼZ^X2fn(z锅RX{jr7Q&j,aLZf]$'V&HQw w<^l۩U4 r^rj|@({zSU[Msoڼ?Ž1 DD3l77;)3Դ~Sˎ* Yd?%k7_s;AL 6!X1X&H:#Ԏ=$~$]рIia^Ku'H!^ ~J= ƺuIaC'Ôt,?tnPjx! S6R# YeFUN} nQɷܧ%|an.%FPyI^5}:/>FZh:,Î˶PPv0Â)+O2/͛n~ӊvIܼ-ĝwW)IN{*kLisb M;7W[M;WX{gyUsgPT,=aLxI]GJYV0)Y{H\=դؿbVpt,?@p 5- dmLؾxd!@$&E``v4(#}2 d=L6@ Ĥ`jLW2ˬ $7Ul,5H|I*像?W8 Ԉ P5XH-b,bSjZ Щ<VARvo"1y,r"Q;)K8nDƖA& @g|%8^l kSHnm$Q5Db!EetT##0 ߽~{O=t==?f:~Ç/g?5{'fߓ;Rkq{9?][==\xz||ԫ{ iCNnPZĺ3Ѧv3~x}|g߳!8χׅ]_0d -AQ|Ҵ9D8VDI~q|2x:Dw}?z.#|xQ' s?66,}ѥ g=,w|;JuqNaa5m~G,?JwmR(|(w#zmd?G/ܽ}+[D8%?oo_ZC-h hEˊCv$XUtѦMXLJ 6#h)ܑ9VϞ{Vb" YAxޕ}ʏĐo=]WY87KY%N|<}YnF:?E,T؉9pyVt99kw*nEqÈPElp2*>37;50Mv̲{g)wL=+ݳꞕcRQRv,G*c#zCM]W`1-ɲ$|֕\\FzwJtѢs3*+*3tҎv"h&\۷ZG+9inh.䝴#h6Cmzm{wK.ZUsu2]@}&Bh~\-{/p q 1mJVrZ*p`DawF-zha<:h>WA@U1ug ڨ[ ϖ ]vhAǢ@-:h[6,xAL S,(uuD ڜ?,!T숡LSZ~ j|О"un#tТc3* :*Vv5 PB|R+Ҿm.Feg}a y߬} }mڰo) uhh}mVvHZؑ+7uҊw'mRʕ<*GҊ֚HE/-Tg><'U7vgU獅ValHZΫeGJE/mpBN A2EzFMvC60q/}Қۅ1]V~,zpo*PZ@¿Vfrlʡ|L{idK^Zxϩ3* 9*{Mbi)-7 ʖvR:4z)3MvCr OD/-nZWsӎy&秊nZnU(ibا" M HsG%7j7[w:U ݴ覅(yI芛#[Ezr%p mrKj^OfN]a??~C[ѝ߲g;Vdi \6t SFlC'tl'c-It']\qb|BEC.qʹ@ tT9R2ul  \;.'EU]-@HovS#oZ}5ad]jP5X@ ))-DW;1r€NIᵃ.I-5 j=jB:̜ p#%C=$+/%z[zYf F k񔒥%sU|QW`l#%s8%w[m 5S9"WmD#ґ%th|f2@2v\蚟Z~Z&G JjU)sEͩJf{\_ !Vͧ}~׈0at?dqzb ̈<>-0VA906ʾ; ' ʌ-`z6iE]EɁ"L҇DjE},˰7JhU:24ai8Ld&i0wV;s2D]&_ ,4td~!%BYDU=14}**$ Y"̞R+uwvW1ar He t$ehTS.* MGSf^&eztY&c~DuRSD"/l!'3]t p^*S}uQ*_JAuXсp`t`,fTQ5Ɯ ^Oa,fphp`,f`2(Sĭ*$LckQ fsq CEST}J졢!"c6OfWȘ_.I_hDJaL(iT)st:Rf`hx eLuc񂥓L,Vt g&s3ejH'iPΦq8'9R;ʣyko c!#\ǎchC_ T6(3D/Q}!eҨư[ISLl!l]k=4ʜibGTfhVjh {FAhTPV|&k 7G}#j2E{A$x# A34tꑀA3f"3葉%i e)GTkֆNBbAK 0T vi)$Y5Cf"3P՝en*280bUV{Ҏ0id*&plmofIUږ̭aϔ|Ϻfm(1@ڇv4n'wpecqod53x ʉMK!2ҕ g3>zD/_w$&d+JWXJ}Ҫ(dʧabTY>![sUNd.' >Ȅ 2w1/f_ƢBUO=H9]0BӇMVNX:o1Q; cPMT f*?o^er򺿽ze|pCw>7S?ΩZdrjz0Ma5HW`䇎ۜ =+\(~Y~x <v%?y_Aռ|1UU5/-N /6m҂vqVmaskn}-͔dھk0!}k݀}-@g fAgS.{[ =tz )\Dz뢒̬ 4<^lŴ-emz\֍iޣrvCB;:V/Ϻn5E#Ŗ&{\w/㐒vmGع/hE(,&PƼkЋ̺ז3uT/mz^͋b\\ng]Gu_4mloC!=Ԗvz @ymTccv!Y ;D*=‡J@ڤkYish ֪ ßd[!g`& V*T[̢~tv$ 9}UznS^j7 PffqJ y/FI8S귂'*vJ$zCu{eVWD8 \:˷L{ll}sog)m] -B7tPwu4"u@EOȴȳZ)0$i?#^#9 #S~Ϋ&aKyPؐF ^nlku7)B_;jA |MlFyXCP#^}`C]uSr(ڵ MLo0Ur߯y7V8i/7kGx*ZIXCn@0$njg eSc:@jT7$!@C-ù~Q*%yS۾G)uF(%yO[s'u=Q'+:IM*>G8v5 79CqA!<پP(1|A} NbU'Q''ǽ,u>!KmGIȭ`鹣:A'ޭ;PGu}km_q#}l8C^"%[C/KKuyK׹`5L|pD,=iy]lDn4;*s5.3[;[yvz᠆&rkaz 40tswvЮ/-CD;nF~ ܞ?%xǷ|y||z~}ۙ moaR<8bB㗫/?|aj;/ojI+Z3F`ߊx8ˈE =Ae}_>_\|,|CA?Mw_^|s¹I{|{~B>桿NQ6=5 %۵xw-A{^w^g>pՊiZTR8K)c=UuROO+:+J1fuВbȒ(4T>ҵ4ct%y #ccEޤC"/StMAS\Hڀ%$mj댊Juiq"w.rGіPyaʜ"m bMk [* Mx3 n?"X%`F:>&W1C6QE4CwKmv٫6t/BjGDr4.{ |XY .VRU%%x5]j[M68&nh kNV0ұ kP-XtK;쏿sŰŠ%BЂZpE#%kO#1i+I0Ph*źAqIr%F |AW6l<2dpynڑ8V`s-z[V\X{$'y`HE,%` $oݗG4rF-ɀw]v=1EQgyY-IjCfy) 8{-W{֏h6rX`$X'э9&Ӛija/@,TAd2 fwUGDc .(k#KQV)ltA \ 3-],d/g<덁WiJu]4xIjSYF:>,sWP1$5ˌ ĮT3Rv>UWb1_\Cfy5" $[ap3HYӲE:]D+VGP#fE̪ ; hd!@#"k'\ kbR~t(]Xх +[ &dmхM MF zT8ұ! 10,y!.W9RvctT.}Wc͡ju.G暩+2WmԲҙ*Nj!su%ƮbixCXQXBRQp+rrAe@ˇTVۘ%V# gCɰeu;/5l@?6 [Mj:F:]`4cy쪉mRx;SSPU/RvkF.('[5g_b-Gt̏U:qG:^ k0x5d2 ׆uA ؾ jc8*$WE1V>#G:̂y]`5#%k7!&u $:Bu1#RD]3jC'Su00%k-@qiyn:*G:gE5/!v#g/FJnE9b `յ'ſDJ6P_iXQ)Yߡb]"8p6^S+iՌt, \\RjC)Yב݈G\y,eL@jN鱢gXAq5%.:C C)Y۫0_:ܴeĮ aF:.0ZҌ" BE/Kq ӑ HJW[j>(_X${1$L1LˊA:ܴ<_SՍt,]R !a W4RvI6Μ8L^@kdۢ"5hpCL jCWZbH^AkkC:#xN/K5P24 [k]ӹ fdj;wE6twqSUB%JJ(jPek +8tzAy:^ ++viN$ЩF:.08ъ^^'YFJ.9Sژ襍X|Q VPdjO}!IcKb(1ZG26␹6A ,OwW4tDaէ:eOCDp>iI-OaNyʧ06{Ps㵶oh(ѺQڐXLxP_Tԍi<-0e’XN֌St`vmeNՀ; o+VdɢS굋P8%+hU=T;N#unZζw`K#ȖCOE _~͆^>=^|hs '6/vjU w1J,VrAM3|x}|g\fsVċ3Q/D /j\^iτVnnm\7׿Llx]L+6VBzc+|!ϓvǭ$—k;Qtqe'm=чo?\.qrKM!yۗ?1$~AVҷʗLJ )I>[OYK~ngGW~؆}?gAz=D'9qʘ|m=eYٯ5%LX>W}V|َ@C1{0?$3v'_hٸxzi<}\vw]ʁZ?tɿ@BZy/u_Fו #E E܊NQݚk96$YD0CTTsI SjX$IJ0(Sm\ˊ. qZey;$:=i-xc|0&q&aia0H %%Z{\ZT˓aVFkZv )Oxc7Tx9}/8tPb}sarTV7 _`ԣ!ꑎ{bQ[O&l>BC=ȠHHD"kXp(Sȴ#LWNDIܢU'q}/Q$&!nZ0N,s#ǕJ­jU26S$.0ezY\>i,l^DUU8Ô (DVOJK7W*_p󸥾Yo f 7YCܴH~SOGcy hoxMלFyCOp_Hf,8tP Iڀu$" ,`2G2ēfCDH8&=^ l8$J7Ӎ<:ǀG:}HE8`@YH9՜`$$T=R2( " (JU$aW֨]q¸?XV:CB Yo z-UNM7ͷh:9y k馣2(ڱ<)gJe$t{A?D 0 (ĭ XF⡙ufcRcLTuW%"]{V2I +,[a*NʔX(M(R:k-SpXV72s,PcʕZ(F-M"Dr)2\岑jC SYdD?qPԶm^ØD[29`ߎC}oe1Q'$ЉoP5#KW X腚ӹU: fd5X7bK\A!Fo=BV%z&`aJy7N74:^`7Ak^9ˈ\O܀jFJwE7٠]PKs5D,搮#]svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgoO8)|$Nb;eeYH+ݩw҄6R7~)&Ʊgf2R+>XMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKs5D5WePsvg.breadboard.starter-poti2.svg_b74d7502644af62082cfb907907ef696_breadboard.svg]Yo$Ǒ~m?yŒ i,Yh/'i PIqdzGDY%7 hgEeEy͟~y>o~{Ys?~gǟw_7뚿맇͗~84H 7=Y/׿||8>5}*EYϷwytB>}?B0. Ǯi>ݾycbZ1y{Ͼxx?}<.~./} ӏbvzsp:ᝲXW]{|}o5?\?;>x{R~ܾ?yo@ٮy{ys>LJ[у\TbaX&>dN<e {߳0h(KLA [j||LJ&|Yj){G&f P`<Gi\7_ki&;Dzp4\ܚ)aʨ'qaN)PWN& KS>d+RETF'XT)H(FQc|sab@w;J]?kRgDc_<NJ_?a?Hp\Zmg~ K ,|^#^?4vSyx[Ǖ  :XC&`լlM~.+?҉e?G*  .z(9j*˶tMdq!~nz<(9XoiEZ7weDI44m*O9ޛ,T qb>D=~AZB5=blEF B? ]'+{[Ug5P1{l gmcMUC}U sJzGt3q`6kr%/mdٌ&XMĬXM&Ŗ6-0[f-4Al: HNN*!T ?C6 _@F^~OT镊}Lrpx <\e^r7*beeg,)7* up2㤘ݷO4ƴCT0Npfkj4\A0$hH4mD0pf4`*@T:D( z ,䃀%ќ 晌4!C<| ^#"ɩM+"_1sFK3$2F*XBʳ4T 4'uMC8*Z8j؊RK1xܠV՚7# T,6вe敮=`P8}Y>rЂ9H$29U}I4aF4+IF ာp! l'ĈUJHڎhʎHs<@c\W~4M21T.,4/eLFU̖jT.UT?KTK](# 4Բڣ-[-ou+tU{YxKA3ks[^AD^}9ӒOI$YGj_!T#QB깼l7ïwa$ HwCl90NYM2/PNd#rw3zc Fx0H%э@^!;4ސE`HX0#dQ Q6ԁk !(f-9Ei**lR+d h?HɤinUV .WNC1(qJRcH7w?qC˩MU<ٲlӌ9gR"r&QhJfcLcЊDVF㼦aO> ω׏+$`ozx!vE+@P8Y V9Lf+R1bUΆf+ҝ܈xn 0x@3RBDUvj1u55xg05-F+j0l\Z -kE3@6!ύ G9t!52t3@YQ] x.a2 \ DZV6vp]EMY^$bE3 b]1J W;G5 Aّ!#+[hZrkkU9^9&WS0kҝoReG7^CErxHTVCI֤;W t:f'1%GEk K) KZ(栰5zkæZΥ@4+|QZ)Sժb9b05+ xfRcB+kMs@3 VU8kBYMkYO\6c8!dz `)1w2fB-a4&SNͰ[cf 9"1y,E땟`B1LwЛcE3zq\!ZUXakhl+W"wTؚ|y:,/Zh!`iTk/Y7cck-[uX4yC%wЛ㱼c@QZvF,Z%kIs@{U栕l5]Q+lMs5%\oH [FdzTQ+lMsl^ܺIEza$ᗂhyxLn(xI+/汨5ys46Kuw^ ;?xږ5ys,6/.zi*-خ.YsDbrs$6|Q7~kYzYaibȴf5^q\Z/V3N^jeRm.[};mmO;0wnxaܲV߹Ujm.5LfgzbqyYN1Ȥwl/1惆`>nOfG'nqԌ6;y>^אn=w ʘ0y/ @@lCLLw.'_Dqo<<p<3.OnT>!S);~|,QE W`tJCwJ"#PUYqqAs*ֆR<,h5$yҞiI{iG`F {}eYίˇߧ /d~ @h6fwW]t_ RȜ/3'w:7=l 4GMnZϥVyZmpWm(NtN3С}BoeI,I[(2ڗ\ .eHņ߫ V=Կ1v |C22*)-ƇQ3c?Czӟ!ҷnN P?ű:bl4hF|0/CJb2ضiM͠a$.U{•t?&6(QI9R )hf> %(aC/)T &4%{U|@_l$ep56^i(_ a DZ"OIs#)]Sjj=!KŌY ͉sfh(T(s㒸wL0G$N5&4'jYf?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tq}JͻovC/c\WÛp8=vW=W|y(<%Xv?GlWKɅvn皡἗kUÏow;(o~5ww~n^?N\.OSwxӃ^_Tb[^O1JA hڼ~4;쪂VUo|w:+e;KksE=s.4hv@̴wN6W<(l}dfG#c +kstW@t[.0r[@~X9=J6A#1hɑRK$'%'J<]CveKqe )쀹c !E*y$fFDTܴ&9;3x9H,mHodJ5첔cV{E㿧ʐw\|E}-~@uѕ1[w*Yh+#z@0X诲9odݛ:X-Y[ 1… n…P q? (?{#tBnV:hSlƿe :5`&ˌFFg l/,8dxxdVu,YaH~@th[d9rpQ"Ey8RO[1E>fNM|ml̖kj׻]Ejbx|hˢ{ Ϩ՗X~w{[Gӈ^L5W!uKވw4äbmsa;h@N7BJ߂47`^M+3n dπɰ۪ Zw%P VlE2;;DZVFYY3ō6|T^CUaJ]e2 3 p2 n5Nv>^?n>@`:pulcw?vLjPu0p8XTC=zK-R/=u=Sg*eZ|6>OXVReHm.?J AMY!k!Y}q!4*%j5A6P%igrKK>w9x [ 50ʁQ*0+<v¬%aD4 m!&{NjTΪV陕 i܉5#Xʢ=iWY#h*CsMîhq^f T9[?t-03+|pM2P嶦XߜCz4{<\OA FXo9WV̳8Bev3E701(LalCJM*,XW Su 6Fc#0 rrbsjhTcmv;VV`Ў.f6@\0 &kܸbvfOj D $cUP/ &% uM HI*p[d T0]T >w2 ATjWd:7Rl)GLRG4Eft0RSik$fE[DW81R'h -(q,5$߄QT 'oge.%SBNkx'ZXeĉOF1;K#*eX>k@p9eUd#ĦM)M ZhR:݀ |anc3gOK\Nu=?˜<yh͘fuaZ1Qd*дӀGT)9Y!M"Ќ#۠j1"@3&s 44&Di\/Cu"H&Mr={ÚD ڐ@v-:bx9akd%$ّ,FR.hIVn ڊAϰL# $5DsZ"YiLt:ɲ wĈ^Ȑj 8 JuvBUr'U#+="icKf4De>HLeeH5fy'e  pE+EУʺb+ی` T1B{4B"?L-rЕ?*4Qтƒ+4}Yd) -o^[rA?Fi24_3-4.vܤ$--;E>Z |P77n$ڠ04DFM4k⦯#'[Qఢe$هg_Jt8jDCT4@'TS$kRxXmsqOe @B7ӂ\\9d5hfGC'xZ6{H n&,16\tG+ӿ7Wk%љVaEH.!Ĩ lή.*NJhJRpvcpnMద%$E l= R8)z70F4 ly6Lɚ<ִ̓;[[D](M# '*e s%t[G*lmr8Ő4/%fF2T)7k7U9&.̈fz8A`[G+lebkǚT)y,5! NB{b\iU c㔍dc2h)BK8y,5aK1] cJ0JsL1]Qq [s:8\C\$dz rz%\H&je|].|ȅO$yN&,Ub6`.(%vƷ6rbJ 奂=U,@Eֆ.c[0ŔEJ7[[`i?FK/15) ${R3[[jI+w# r9ksF/q5aKZKFyiIO2Z2Kl om%-+W"reKVdڨe܁yĵd:6jkYx\뙊EZ\5H[(I::j+B` Hʵو8$V,tMF,c:o_:vKl om2v_%ݪ%"Yژe9,ަJK7ލG:|.=^#80^:TUiꝱzSUkq"s0Kc乗f[L{kln٢LWq|"N@Oh֙:尐M%s~nКXq:"/s4\&\VOҫ3;2Az+EBdž0oGwfiyr9(0i^7q'>ݦRsW_}wMo@>O5w].hgLߥ|^Xqc|$m _Mvnl }ݸi~VEdq/茀x(pGZ64H3]rd3jf繎; :pw7 |Ҿ* ѡtc~k)UHoN wAጕ Z0UZ309ՇmWEiD%E[ZnMw|"b}d 83?] Do&hF,kgL_ᛐ^V5Pq~j.?bj>#g:5=pHq CL#QN#ibz PS_ۢ(uٙpf0J,VBclHc6 |NaTf۲t_7kj}Bja +dz";20xD!)sCw);C%\OT1g5X#)BJV)ՙ+vRوI -3Ug]憗J?H/PKs5D'0 Msvg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgmo0W\iѤbl%UjJ}RT %մ$ `kTEl2 y.ƃelӣÀkJ5+35y$6IEوQv?p3UC\)F8Ռ!"2N/` i1&QYC\VteL-L3QyV\?NIm5,Y($WD/wL^ecc5yOTxkQV\M pOP.jY!T'Vo9#ehedaTXyTnS0?y'njYE봈"TRƴYing8j~1({F[Fo9ppۦU¥ ZWܯw\Tz)^x y &qCkEh}'102??\;NiL٧("D͔t7'QL~ =߳R%1^1,zR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKs5DEsvg.pcb.starter-poti-pcb.svg_b74d7502644af62082cfb907907ef696_pcb.svgmO0)nދ8vIFҦ!mB+v|e/b|w4>ZMLtN889>_˷3( t8Jfo3}#"WeU(>_ٶЦ|p.x ' NW2π8G.UNɲn66 cA62SGKElؤ,TLIaD)Z&ˣ(tUr'P Ŗ!ru)bYQ宥؜j 1 IJqi\2% 3t#gLetus܆؈01]W\Vq ]IHo((vw-ϵ@;YP#LsmI%ްzF#Vn`JuQәYgKQ]{FCw;aV؛?^9}}=k[~_%FtNKѩ4)ISj׆*ea@?jܣ^$<^DoD4_ 5Ч^ۼzͯ2-S#߁BTOTZ ?sa8>r=:ukxGDZ?ҿ5}V,֑X?6f t 3v뿚PKs5DxKsvg.schematic.basic_poti.svg_b74d7502644af62082cfb907907ef696_schematic.svgT]k0}ﯸS /e6( Fj#I+;NҬ4c~Ws1~Yuc8e#6s|n'bi2;muh]=/{p_R%e|}\C.&h|{L!ڔ9Pg[4mdٶ4-J"W0F≱ėMj9jzI5=߿\4JAwp*Kme)Wan8_ IB,iDi=9ƾ09#W0u 2qf~5·Kbb_k EofB)Oi^-Z v( ;xBt1)0h^LzW DLH ?c4NSV+c{XwQdD3cAE$Ox&xn z!2y'g!J`8̡ ?;6:NF7p&2er,Qb<qPYPt3^]PKs5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKs5Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPKs5D L 9part.pot-small.fzp_e55c4e524988a0331c383c6d9b004770_8.fzpPKs5D#ho Pingpong.fzPKs5D,搮#]Wrsvg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKs5D5WePwsvg.breadboard.starter-poti2.svg_b74d7502644af62082cfb907907ef696_breadboard.svgPKs5Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKs5D2H#WaC!svg.icon.starter-poti.svg_b74d7502644af62082cfb907907ef696_icon.svgPKs5D'0 MTsvg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKs5DEsvg.pcb.starter-poti-pcb.svg_b74d7502644af62082cfb907907ef696_pcb.svgPKs5DxKsvg.schematic.basic_poti.svg_b74d7502644af62082cfb907907ef696_schematic.svgPKs5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPK sfritzing-app-0.9.3b/sketches/core/obsolete/Potentiometer.fzz000066400000000000000000000265761272363601300242700ustar00rootroot00000000000000PKs5DQg5,Potentiometer.fz[sFϧPh}`Dލ9;CITPlݟT$@5 ]ؘUz\=6/KvioVj~ܹ__Wwۛk W$쿽IFݓ}.H/w+OjVlfhG~s+6_Hk!ӿ>gл_7oɹn7aW7w~z}=뫏v_6_g yzy?:o.nq[;v,^~Inn?)I^t}ßXͧpuws=3?_ms$m/>'[VX}XDx>uw\yp|-?Œ-{u"X+vO}X$H}}rlOYcn{)y޿߿pgr<>×SN?4[g/ۍYXW7]~-_l5Պ $l./^H`Lia8$GLnEo#u;\WmQR3J_toe8kλ9-􋓲KK}f>'wV+fx>Ѹy&;̼tvxf\ZR1$He͓:Z|X%VX$.`{浭 aN!Zl] C^-tV! t[ ymӰֶ kp|@?lڭw݊'Uǃ`kn7[ƺf!?"I6ż>w^G1 lMEnljiƺpTyMam0V^ʆ!궦hUTR$cI#//[]PڥY3C\HzYԇˆY1:ZCZ I65tRRB8! ɪ!"Kɩ6x"1: 7I;-%14RrWB:ӨS1:J/$nir0Bs0 ɜοҦ06Ǥi0 RҦ'Sxr!Ӻ *Ү0uҎӉT^HQ>@ ai/$C$j(R4jW$RR^SÈh.%^H:է1:/SS>aB́V*{ca/$@f!Uk):ZOd]6Ww߬w.y]Y[^ajwZLnV?||u봝1Qkwj_#eǑ1Ff~%4;w}y͚zsyh'E)V޾m]w¹Ml{U~γܸ2H+b޽L ɏ]u7w7ԌA(?-xkZ_`Z_`q+FC"(^9y"vYMcΨS )ݰu9Mcs0LQ搐9)oΔ0Oj dfdzOΘ0>c7#$iGOQWyOQ̕FLW\L߿+o?&?ɯwgK߿_l?rAsܭee盧ĭ4;_o?ٟ~LU2ogˁl%ȰXۦՈQ}8%T1 +ϯ! 4#T@uf_'Ju?ŘWfǙO ,Mˤ5n1B^%“+cVCK`)^pwƏ:Z6m\,/̲g;on05Qzy}F8;z4wXfQƚ| ^p鉶Jm|1QK^}UYilXw >}TJush}J4Ď^)/;u2Svy}ӟWL|G2UtNp.d 8X.r`Kh\װ&;7SX1[KS~]W.>KՅ#Νp KB t.1FRf1|W)-T[X1FBIbvc+(6bLeoh4w p:8/u,;,X4f3-捲SĎO rNAox`tެfMS=8r%R͞"0vhvˁ}_Bs?NPIeQ0mJA@Ӗx}pBJitV[Krw4Wn^|c>1>8dt }_Hs?NWtpA8t2Z\:k]t4|=O!'wC\RB̆b L:2e\")2z='F=bdfGNp] M/'4xLܡ [aa/&: J,R,?eXK8x eipfpN {޴{S4|ˁ}C_C3?T_pZD={Gå+uh,GyW ?>9>>M௤'+ih"">јwNӓvI|Bۓ9"r%=6/.=a*=mO q}O Xg&= c4b-.0tT5DZd HFX3MILy9O[khGJҰ9wC})mt&e0͝{p(CB5#F$qCiY$kcB=$2k'bhͬ@m'=+Of>;Ĵ#@A ҵ:AgYO3ݏp\0 80eYDtGN>BHit FBB2t+##BKĎ i379"W@ݙU!FʣG"'3i$]s`ZWII8Yq*f΀I8IwfN7(m=9mނ0WsTdEDκw"ϡ7ӘxfYvyPhPZYNg=%Ρjh`$#NA*&0X纝x5&3M d hPddDJtTJSO%Ԝduؔ[$xn;厳C2ܖDKO z+b6udj칛?WО%vPcycR1 hF`LqL.*˴6m֨l4+(!FLÎFC #Oj$vT7P~ C^*Md~m82j5\f9*2K %2WD)`sy@MMV թ^Z5"qO@J?2%%e"dzP=וk } y^D %ݕHHJ9bMIJ"zauƈ>R܋R>sN1W=2|$_uD;$gh P Ҁc=v6=I7 hnm1Q9#K*;bg`IS^5mgB'0Lk_8٣6 .a$sC[(Ѷ"e*QYQ)*I"S9輜#Bx6?,!V}y!;uDPB:s0ˌC'*%N iyxkX5A#z(ǭ79uWF*!Y "!aX#bQ@0i"S(ء,Q62rN@Qa>$a ̯WxN9=]+ 2&3wۋ%w9/xh߿R6kwxkc`_T-#W#b/`1-G 흉zJH֮Z#+gZCKd #uzHBՑp,}:K\hvM'$GHn.!atL#brE50] iuA ;{S̼C@ee@p0 !$b5B8HU:OJuE,<Y0`e tLFdA@A~ 181"0 FLR:IjX"^,ViRFb5xTHn cSOcVR`"GVa C9@=# !Xҏ"zz!|C*HHn gTdL8tѰK"sEPe*W}xdH:w*)m~_Ѹ@R1)r$&9rFU3?#MOjp$KC5y#7)7'U k'ߌF-`d$"n27*nC@"]Ȟ#yOv}.x0#\[A(X ʡ0I`*t",@?" E*$[L^=Hgsh@#pk֐qL jdvn&1oWD뇈MzoJ 5Fb^3QqUHE# QYwQTV=YMO("k]EJ!AFuCJ,],9~x쩛ZŽ" c:]9Yv5+64cseO2F(cv`HF&ވDB dHFƤ4=.HTt1+oL/FrV(-g27$k_@i|lJ#,y3BƬn9*)s6XE#L- ҭ1 Li:@Q#L `Fw6oXv&nT 4_4&±4m^H1=>DO#"o2i#BLQL0DM14D%;?߮6a[G `Uy`AZVRwP,>#WJŒ D-&Ǯn.bD ў\,@$cL2AbVREʧts_BDPN8J7e[LG(&jW e@], |6361dL(vVSPLrPyH꡽7p,? 7t f0$$k%3@"2e e dfO?17^wۻOOɳe߯m_6gx0w}6r`˵kRUT DeV"ֵ~j)$Q5ΪvtD2}"4'²)q^8' Qe2'L\Ƽ>~1%V4K}﷦=3YKkbuRNa^l-+B"nY}²y^^SZ\*cH~!x(˪ZQܬV]V@A=dmgt9]t1X{P !ygu=)$ks̚,-.-I<'Ԅ !NcpġYz.1=m F?ʉϟ ՉA؛`P!85TRk۰fސR9 b SVVSU&n#x £|B֓sRTd+*UZoy#TZ*(l, Zei=N>|ffBM`Ջɶ(Ի&qodՌ7 F5t1ޮd#GEhEEZ5QQ'X#ę\vb( " ,-S1I8*ţ ~-ܽTfGpaIF# é=Xq%XƈxF=)W;ʎҊ:vEL L+\ea) w 2JduYC#dA /8b1=i]_E+ЈWPn}QK`,CMj$C4Ĕt:#tyomqA11bnj<_4%LɂQ&mdrtٟ^;@uF1j.WbE6=Hۃ -WMG@~5pEk,=,1YWpK2iNDKu,@3/٬"MfI1zt8|f4yQז=ݎ~$^_0*2qT?ADfdWѢ9NEv ڗXsq0V)!k%#tBΞUD꡽rp#KG2 15s=DO#ohd\ڗ Fo'`Ԃ*&Nh{h>^F9ɠ>al2SBd(+YZ @J%G#('hEvoB ksCsH8Yp5dj8燈~H3ՏݺTHgLgxID1*X+]UYP"3Pg/Ϝh'5+UI F0~B@dʡ} cvlH?f^D: Og@$$k_PEEƤZJ5"+Yv8l c(!Y8r 88+f*$,&b cDF1Z/""|CW6} 1MhU(dNX礘!Zd4+RH־c-ҽc֌+h Q촆stHgC{L n =ݐgL ڗxFAa֙*ȕ E'bZ#H<,MEe3Z#.x6ГR\Pg|o` I8ngPDdz!7t8)!Y8"!#™g#L]EX\;Gv峉(Y|쁔}g}b4Vod;!hV=7Ԑp,h =HgL m Igzd:qtUR>̈́APwFg;`G:Og f/ې}T1Z|}TI±tkF"o>)/7?U}AQ| Iq:"E8&갚W gAWdT7y4p,ݾ|tN1DO#o8S:B%@cLi)aF\8UDa膔Y}5qLGe.pSCT6UP6J_Qrd c`EA&~D> Od g s:r?}5sF}XQ!*R=/(恏!Y[T_gF7rS6aY7E` B\Ha`B+ Wld؁bE bJ#D}dmQanOX%\ʎe7OOG$d6uSlw?^o3v(I~6Jg7ZYdE}Nvl$^۷҈~ݼnn⮭5i~xxM^s 0!|>zEvA ?)~{wy}SꟉkE}6dw]7O,zn[ou?Q@晈.p)f͌wy0K VWpA'#r؜OK+I^IE]{ ^PH9rtV h%)+撜!/gSb3逦l&7RdiT{n n, `)nZJ?>XJ3?ƥ:1R(XKRT51TAD1Wa U>"R0!ɼA~~0$ 2KdT~G_kҐiXdKI @khf Ϡ2;UT>^eeOjVlE?՟wo۟ӯ*i/f:Wa<~JҢr2/,V+T +jm| yp]SU#>]ɷw[PKs5DQg5,Potentiometer.fzPK>*-fritzing-app-0.9.3b/sketches/core/obsolete/Processing_RGB.fzz000066400000000000000000000503151272363601300242240ustar00rootroot00000000000000PKs5D L 9part.pot-small.fzp_e55c4e524988a0331c383c6d9b004770_8.fzpWN0}+,͵W)͊$$V9ɴxđgvMIi%gϙIK*l~Z9, ϖ_?oYd-,ׯ]P-wYg9φ>%&S!l:eﻱ?q2g88SJ,@aI4zPLk5pG~*#rG][ѯKԆ|2e a_vA`>$LCx-r+q.&7[Y>~n̉=o[Xg]h hM$jP~AdJs X\ }o ъ+=̀}@md}Q>dF9@A |ES O)/ph:Äs4ith}bLӰ~ n ;T 6c}ETՒfLv2EKK q/ jx,x23\SDzgcnIf@iMdx-ƞ3E4s&l` 9뤤oݸ]ճ%{JPL`GvH0Lߑ 8#Vh8h gé + Crֽ H@X!0X+ngD XI6V'Df#~;fUݑߜPˆgt+B6;Kif U*qɮ8y2JyD6;ʴ6oHPh-ݼ][Qyn^T-$~vDVooW]>IT+EN$QE* ]PKs5D⫫ZProcessing_RGB.fz][㶕~_ą;'v\IjS۩˔ZzT$ER@˶.xx~wo7קxܮ_W_nY DrMVszY??&μOfOϛlv^|yl!^.RX wYyƳf]~n>wS}VDy_/?%_6wwcmY~$?og;r_yiW珫ox_^,ZC*qU7n?[D8|6Q+~i? Lۻ?m:os5) xw7yi56m^ /~w?lcaz%͇'K;rw%ey/嚁gO-)ܾ>b1^=~gG@Jvk=~o_jQi 7ߗ36I4NN~1{ce? ŀ6n|e:#Q6퟽|c55>Ѳl 0 #ՇU=䪍2%fo5dcVNr62gq* NFوJNCeNHlioXF`2N[eǃYv/K:ݲ!'5>]Pxd$Vi;өSvE>d}&C& HU}1}ɤijiYOn0Sz]$3O@zRl^f^wmxJ<_WqÙ Ur؛bY<`(=8 e}poZ][͒wR[ V]goBa5)2(Fc#,QX@EHdzF 6CYA"c Ab' аPީ=de ZW68ӕ]=Ex\wP" +FYf p@U%FW\RKr\Pp (Hp$1H(ׁ簐c|f=H( )CbTI'5Q<Z#p"LZ#hQ&eq{Hp LvAX!2'MPE)j OsL #C!K<B.ltD):QJ1`Ri&iH%HF+`!Jr4rz!S@zb2@K6bUB{SNg9p8Dh_)~|0mO,K 7+aX^]kaz Ȅ (a^yFaa)pCx& 4*/y*1A4kZL,yE$W)e0נN['N7Rukv߷2Ld9HzknyS4)<\xIW6 )~iWѣwae=D nUb )Ť0<\jSL(fC ɕ8D3DUbp%dEc<ŴZ> wDf 0/ρ8O:u.GŨeбBv8W a>҃LOvH-A-u]$L981&<֓'bQ<ͬmCE1>d2Of6.A+ᩦкo#d*e~)0Kw1߰t[q gsq5|7׼<q)07- ̷!ͤpow9n/gy^߿_"ju~xx0ttG}n2YϞwSM~=OjO'|9KXBN'W057&;O8N54 cEiT芶$_і7Vr} 8gBT4ɻTqIwM8ρ 602 O lV9e(SH?͛Kյ\v|Z5BFϙTw.ohɄ̝LxP*f5b0saŴNS"T kipzWQ???v?YVOz}H\ e/;TEX^Ke[~ WЧ}nd?8; p%Z96-Yq >"21 X`њkL)ARQPjGW1Hr= @2F#B FH0I<:.+4 RV־!‚ضi@|3g؇7v]vۍxQo`R: c-za=\(t cOKA^joal8H0PFH-"0 -Cp/&ۙD49 %8 c('y #Fy RaH07D >-0ه7.3 #Ƌ- gFuة_[GC8oad->*oab8f #-Q1aI4H`aa`aGyD(][HɁ$dx #pC}8pc[첻0r I@咚OT5"\$\GZoͅ$DH 7M) J$b2l)KrY p,ԐzE72gJrIi-cCURqZdP^\Wj:V*j\WFxnB\CNJ( ; T=cJ-DŽѶa32!9YmD,>>+x<03HRn "fuH.4SO1-VP쉧M(ҝ_b822żfбBxUWg7 (K={-$a/@2Մ{]7~Q7L(t=AnWO7zk;Id;-O8:EhdWOH0RAM`5WFg+D&ӳ]5iSlIJdƆצ@}mr-n{BPM# C au(n-v΍̺ƖAg;Zݲq7t+<{\9M24ʊ2SsB">fZ0HjI|9$xh99KUtB5Y {'Tl3lGbP,ƍpf;ȶs4*qvZYiHՇm,#}ҭR/zϙmNS#q?MU$4f2ns;IؙKDS$ݤ#jKHߡ5jA~sC FukoupRe(뭝nu-W؛ZaaT.0a #a9K% #R^B!5zlY4tt'CLwF{䥮Oa}l@tYQE(䜤%uJc!S᪮ H!%(@"BM$4&ĤNѐ'JI0!*0ǎWc7ľ op>&(ĺH2Dr(,{v[)x%XTHK_4vu#`u؁ x;1tWKTr ОRP;SO.y5XRcYM.Z?fD sv(ga Zo: :U34$Ю窡"I]ɡ`IRhPRRCNJNQ^ـx_#CEFv4:;\ 1&%K$#Y$av$yTEE$ YH襰ڋ/Xtdoȋ<8A)^ s < 'a Cm`wp- 30$b$GsoxVF`M*ttId+MPH0 =* =<3X蒪mWSP#: Q'^Cm~zu"J `@!^ax1C9f]Cϩ ji 9:]QKܸNgN/ΐI"c:ݦ&'^eanmNCTmE!^]̫amzϼNDyA (̫2,xu\xWsJ:.iچ{W! Bɐv("h L 3u9z4"=yqGF켋6uFɾnuD5 3;- @#.P3ơgrq8\Ҳ xtI[ aa Qx(ac_MC_=9⎌~ՁA!U]DWЋLpGu`cud񁇍C^0a%-[eՁ.W^^(LP rb,ԫqԫ̼#^A ; /WIPpG-A߫ 2 p6Nz s ::^LWsERJJ:!؝+Y:'TU 짻ӓkOTOСVtDu|;RIG J푂;ikD9bӴ=`K;RcIzkgVh}vA)$; r}mw>l4č=pTE -SrD87 2G$k_e]]뻫@jC-y"A֭w+GdoI #q7Jl6#7YYC7ξ :{o זHڀN~EaSL : +"N8cB'i1 S(v+E, ӉCvCڶILFmU$_%BxH`u @\Ҷ] ;RۻpGT`Fe`!)FLgV@ibS嘗—@Q_'Cs\ԫ D*KuXIXv,ۛ&j` ]"@^#J)UCaYDy)OXX撙, qxY;oy̳ěDGBd(bR^=7, &s!H+ÓBN:Ꭶ!2twI"$,R(!FD0 5<To~{Ys?~gǟw_7뚿맇͗~84H 7=Y/׿||8>5}*EYϷwytB>}?B0. Ǯi>ݾycbZ1y{Ͼxx?}<.~./} ӏbvzsp:ᝲXW]{|}o5?\?;>x{R~ܾ?yo@ٮy{ys>LJ[у\TbaX&>dN<e {߳0h(KLA [j||LJ&|Yj){G&f P`<Gi\7_ki&;Dzp4\ܚ)aʨ'qaN)PWN& KS>d+RETF'XT)H(FQc|sab@w;J]?kRgDc_<NJ_?a?Hp\Zmg~ K ,|^#^?4vSyx[Ǖ  :XC&`լlM~.+?҉e?G*  .z(9j*˶tMdq!~nz<(9XoiEZ7weDI44m*O9ޛ,T qb>D=~AZB5=blEF B? ]'+{[Ug5P1{l gmcMUC}U sJzGt3q`6kr%/mdٌ&XMĬXM&Ŗ6-0[f-4Al: HNN*!T ?C6 _@F^~OT镊}Lrpx <\e^r7*beeg,)7* up2㤘ݷO4ƴCT0Npfkj4\A0$hH4mD0pf4`*@T:D( z ,䃀%ќ 晌4!C<| ^#"ɩM+"_1sFK3$2F*XBʳ4T 4'uMC8*Z8j؊RK1xܠV՚7# T,6вe敮=`P8}Y>rЂ9H$29U}I4aF4+IF ာp! l'ĈUJHڎhʎHs<@c\W~4M21T.,4/eLFU̖jT.UT?KTK](# 4Բڣ-[-ou+tU{YxKA3ks[^AD^}9ӒOI$YGj_!T#QB깼l7ïwa$ HwCl90NYM2/PNd#rw3zc Fx0H%э@^!;4ސE`HX0#dQ Q6ԁk !(f-9Ei**lR+d h?HɤinUV .WNC1(qJRcH7w?qC˩MU<ٲlӌ9gR"r&QhJfcLcЊDVF㼦aO> ω׏+$`ozx!vE+@P8Y V9Lf+R1bUΆf+ҝ܈xn 0x@3RBDUvj1u55xg05-F+j0l\Z -kE3@6!ύ G9t!52t3@YQ] x.a2 \ DZV6vp]EMY^$bE3 b]1J W;G5 Aّ!#+[hZrkkU9^9&WS0kҝoReG7^CErxHTVCI֤;W t:f'1%GEk K) KZ(栰5zkæZΥ@4+|QZ)Sժb9b05+ xfRcB+kMs@3 VU8kBYMkYO\6c8!dz `)1w2fB-a4&SNͰ[cf 9"1y,E땟`B1LwЛcE3zq\!ZUXakhl+W"wTؚ|y:,/Zh!`iTk/Y7cck-[uX4yC%wЛ㱼c@QZvF,Z%kIs@{U栕l5]Q+lMs5%\oH [FdzTQ+lMsl^ܺIEza$ᗂhyxLn(xI+/汨5ys46Kuw^ ;?xږ5ys,6/.zi*-خ.YsDbrs$6|Q7~kYzYaibȴf5^q\Z/V3N^jeRm.[};mmO;0wnxaܲV߹Ujm.5LfgzbqyYN1Ȥwl/1惆`>nOfG'nqԌ6;y>^אn=w ʘ0y/ @@lCLLw.'_Dqo<<p<3.OnT>!S);~|,QE W`tJCwJ"#PUYqqAs*ֆR<,h5$yҞiI{iG`F {}eYίˇߧ /d~ @h6fwW]t_ RȜ/3'w:7=l 4GMnZϥVyZmpWm(NtN3С}BoeI,I[(2ڗ\ .eHņ߫ V=Կ1v |C22*)-ƇQ3c?Czӟ!ҷnN P?ű:bl4hF|0/CJb2ضiM͠a$.U{•t?&6(QI9R )hf> %(aC/)T &4%{U|@_l$ep56^i(_ a DZ"OIs#)]Sjj=!KŌY ͉sfh(T(s㒸wL0G$N5&4'jYf}JͻovC/c\WÛp8=vW=W|y(<%Xv?GlWKɅvn皡἗kUÏow;(o~5ww~n^?N\.OSwxӃ^_Tb[^O1JA hڼ~4;쪂VUo|w:+e;KksE=s.4hv@̴wN6W<(l}dfG#c +kstW@t[.0r[@~X9=J6A#1hɑRK$'%'J<]CveKqe )쀹c !E*y$fFDTܴ&9;3x9H,mHodJ5첔cV{E㿧ʐw\|E}-~@uѕ1[w*Yh+#z@0X诲9odݛ:X-Y[ 1… n…P q? (?{#tBnV:hSlƿe :5`&ˌFFg l/,8dxxdVu,YaH~@th[d9rpQ"Ey8RO[1E>fNM|ml̖kj׻]Ejbx|hˢ{ Ϩ՗X~w{[Gӈ^L5W!uKވw4äbmsa;h@N7BJ߂47`^M+3n dπɰ۪ Zw%P VlE2;;DZVFYY3ō6|T^CUaJ]e2 3 p2 n5Nv>^?n>@`:pulcw?vLjPu0p8XTC=zK-R/=u=Sg*eZ|6>OXVReHm.?J AMY!k!Y}q!4*%j5A6P%igrKK>w9x [ 50ʁQ*0+<v¬%aD4 m!&{NjTΪV陕 i܉5#Xʢ=iWY#h*CsMîhq^f T9[?t-03+|pM2P嶦XߜCz4{<\OA FXo9WV̳8Bev3E701(LalCJM*,XW Su 6Fc#0 rrbsjhTcmv;VV`Ў.f6@\0 &kܸbvfOj D $cUP/ &% uM HI*p[d T0]T >w2 ATjWd:7Rl)GLRG4Eft0RSik$fE[DW81R'h -(q,5$߄QT 'oge.%SBNkx'ZXeĉOF1;K#*eX>k@p9eUd#ĦM)M ZhR:݀ |anc3gOK\Nu=?˜<yh͘fuaZ1Qd*дӀGT)9Y!M"Ќ#۠j1"@3&s 44&Di\/Cu"H&Mr={ÚD ڐ@v-:bx9akd%$ّ,FR.hIVn ڊAϰL# $5DsZ"YiLt:ɲ wĈ^Ȑj 8 JuvBUr'U#+="icKf4De>HLeeH5fy'e  pE+EУʺb+ی` T1B{4B"?L-rЕ?*4Qтƒ+4}Yd) -o^[rA?Fi24_3-4.vܤ$--;E>Z |P77n$ڠ04DFM4k⦯#'[Qఢe$هg_Jt8jDCT4@'TS$kRxXmsqOe @B7ӂ\\9d5hfGC'xZ6{H n&,16\tG+ӿ7Wk%љVaEH.!Ĩ lή.*NJhJRpvcpnMద%$E l= R8)z70F4 ly6Lɚ<ִ̓;[[D](M# '*e s%t[G*lmr8Ő4/%fF2T)7k7U9&.̈fz8A`[G+lebkǚT)y,5! NB{b\iU c㔍dc2h)BK8y,5aK1] cJ0JsL1]Qq [s:8\C\$dz rz%\H&je|].|ȅO$yN&,Ub6`.(%vƷ6rbJ 奂=U,@Eֆ.c[0ŔEJ7[[`i?FK/15) ${R3[[jI+w# r9ksF/q5aKZKFyiIO2Z2Kl om%-+W"reKVdڨe܁yĵd:6jkYx\뙊EZ\5H[(I::j+B` Hʵو8$V,tMF,c:o_:vKl om2v_%ݪ%"Yژe9,ަJK7ލG:|.=^#80^:TUiꝱzSUkq"s0Kc乗f[L{kln٢LWq|"N@Oh֙:尐M%s~nКXq:"/s4\&\VOҫ3;2Az+EBdž0oGwfiyr9(0i^7q'>ݦRsW_}wMo@>O5w].hgLߥ|^Xqc|$m _Mvnl }ݸi~VEdq/茀x(pGZ64H3]rd3jf繎; :pw7 |Ҿ* ѡtc~k)UHoN wAጕ Z0UZ309ՇmWEiD%E[ZnMw|"b}d 83?] Do&hF,kgL_ᛐ^V5Pq~j.?bj>#g:5=pHq CL#QN#ibz PS_ۢ(uٙpf0J,VBclHc6 |NaTf۲t_7kj}Bja +dz";20xD!)sCw);C%\OT1g5X#)BJV)ՙ+vRوI -3Ug]憗J?H/PKs5DEsvg.pcb.starter-poti-pcb.svg_b74d7502644af62082cfb907907ef696_pcb.svgmO0)nދ8vIFҦ!mB+v|e/b|w4>ZMLtN889>_˷3( t8Jfo3}#"WeU(>_ٶЦ|p.x ' NW2π8G.UNɲn66 cA62SGKElؤ,TLIaD)Z&ˣ(tUr'P Ŗ!ru)bYQ宥؜j 1 IJqi\2% 3t#gLetus܆؈01]W\Vq ]IHo((vw-ϵ@;YP#LsmI%ްzF#Vn`JuQәYgKQ]{FCw;aV؛?^9}}=k[~_%FtNKѩ4)ISj׆*ea@?jܣ^$<^DoD4_ 5Ч^ۼzͯ2-S#߁BTOTZ ?sa8>r=:ukxGDZ?ҿ5}V,֑X?6f t 3v뿚PKs5DxKsvg.schematic.basic_poti.svg_b74d7502644af62082cfb907907ef696_schematic.svgT]k0}ﯸS /e6( Fj#I+;NҬ4c~Ws1~Yuc8e#6s|n'bi2;muh]=/{p_R%e|}\C.&h|{L!ڔ9Pg[4mdٶ4-J"W0F≱ėMj9jzI5=߿\4JAwp*Kme)Wan8_ IB,iDi=9ƾ09#W0u 2qf~5·Kbb_k EofB)Oi^-Z v( ;xBt1)0h^LzW DLH ?c4NSV+c{XwQdD3cAE$Ox&xn z!2y'g!J`8̡ ?;6:NF7p&2er,Qb<qPYPt3^]PKs5D L 9part.pot-small.fzp_e55c4e524988a0331c383c6d9b004770_8.fzpPKs5D⫫ZProcessing_RGB.fzPKs5D5WePsvg.breadboard.starter-poti2.svg_b74d7502644af62082cfb907907ef696_breadboard.svgPKs5D2H#WaC4svg.icon.starter-poti.svg_b74d7502644af62082cfb907907ef696_icon.svgPKs5DE5Hsvg.pcb.starter-poti-pcb.svg_b74d7502644af62082cfb907907ef696_pcb.svgPKs5DxKKsvg.schematic.basic_poti.svg_b74d7502644af62082cfb907907ef696_schematic.svgPK6Nfritzing-app-0.9.3b/sketches/core/obsolete/Servo.fzz000066400000000000000000000270051272363601300225140ustar00rootroot00000000000000PKs5D!z-Servo.fz}ksF\u=a&]vϝ$HbEjH^~3@_I&] /E=7qLn6`]W//0R_"!b2_D\7]۽IFս>wWWˋ_K.&e~ypu9'=.VMw`y}1-vK-*fkD|1ݽH)!_%"9̗o뷛6\ίn֏zo [-s`tAj~.7:.&ۻïK|Y,Of u@}A=4]p?- /g<wqxo~@珻.m3 /X̿_/}_nqnlFc\]'gLI]%&‹DLNgfVVG}gyKpn Tk]Bo%<̍R^=}_ٿٛ}mg^}h<\_ή֛`|yAs9{&Jq;׏L?UsKlȹ8,sqUN'-ʇ>'%>+ʉQ(9qS"ek LVZůt 8Uf›X'׳M]?na:hD5?$abͨ[oF\Tτ(c"|L)Na$|⅏{qo's(I*bD)b~_-vLfo J^AɊ ]=&=KM,|̥$lOG#j̀@x#>ji/n C.6nZ*a2i7@Rz@Bܯ,EU"&$4b Q$kBpڡ몰j>24$n)XD@D> *YrT23 6hRNvQU`ءkK1d@ji>X 2˒ BaSKXvĎZm^`DU I@}drscUPU*T(2E:8/Vi+pM+; w|T\e[ 1JKx)S 9H dʔheF@$Zhӕ ȈVϟGk" ~km_]UżWuV鿡WRj0þKC0F2 kuҏƻ^vM^hq(zR .bVyI!ah rk %U $?)kE٨R[GJ %#"aU$Zg)n5.=c5aoYEѭbg! 3OD+P`irq0 7!Q}¬^5ZUe0E֤(ɒmzs?''АSblk)ҏLGf2Rl?CvS7@d|ܽ>$(vq ^EI*Q6ؙ-$q 4O s#Κ"!q`e^lgi.?Xr{ϖBiwЮ6mf]m*NކB}[SxVSwۨr,#{u,!̮[\"Jz~"O8K766>fk1P';^ \i1Y=_XEs=$j;wrop- KRޭʩԷ}Q9Zg7z2l .-tz0ʝ*ߔ;ԀQN}c`ΞzJ(d&|3 =%΢."'L")qکdj(.pS-d'.P)D ͈+VXR/#\7z~y0xcNQS"C&r\Ӕ„wl=Z3F3dOD\G>a#LH?C _q4hRkYiJ H8A݄$hdsP[:%I@1:lhdhMokE[HN~}%e o70=ySH˗iS&dkU)ArA V.QA Q3 ʀjzY9e{OMh < H/3Ũ.<,%{#GkjQ;$G|\z~E!ɚ)L`xIAq$%U :nV*ڹHFXv}ԨX`/ڮa X`cԊH_.ԎvLʓ=a)æ G5M^vN8TU2Ze F &W[kۇ"8Ai[Җv8Vόa .#f9zY"ڵPXӀct']6=_=K$0*GJEk0 HL)9qc,LF,?8I`k+`A P8Gm7J 9af[988MpPHXh@ 3 &m{E%`- $`vT hhvF*`;ƧD^B{c_eݣSbMΙ#qc(6k_%x׳߷f;:x?{zVzjfXnp}3o珻`9Y>aZVGJh;'.HDFm'FmXTo5rw&cWf촙ڷ6ك_LmL~(+s]/Z >MwJ=? K20L |M:K#YQN߮'Z[˰qy_}˷V$vs&t$P 1!ⱽ|$yO@=pnmgH`1{hFdh?3vf&Ruz 6,{M(/YYd0.igݩD B}ޞ쀺=gɣQsFmϵnĝaQښ˭Pm>[%Lg;é46J]pa3qradV ̨)6Ί$"nt 5q\#\GG0fN;h{Ve7Md Qp. zQ+H)Aq/\3 a fJ4]?qN0_#_c!*d {\'f_Q l.L&+3|j"fؤ9WPPdH"&%b #8jJU:]pq_G(-'0m`$eT@f1\R$a9jt HI}>7%ȕ}l:)q+ +!R$FNߘ?-6q0GŊE.!h*|`?,1t, 'ؑRaMڮN"1Fc> >F |S>>Yr;x?1VpVN@|{9lį@p/3 Afc|'rq*W%.2;Q7)jIhh Z j9 A9"*m{uԶL&#T|&`M0q%,C 'd Fb;DGeMΙ#1d@6fz $'e)M`3"aN&Z4+17W@(6/k(kՎtf KhGDh,\5#>33#\pfI o@g^83jPmSY9Df4E/4sJ#O4ȗq[qUtL{ U&m;bߢنHZ߮ >ji:-z3_G͞aq;#ʰ'+O w^Xw rr}?wv_f>|@ZongcE*,tX2sc>=7>#H&np=SZqL_J"VRBę.%z^=u5kO44̚#5Q܆\3i֌ê᪡X8𪡟^UCjs]5™֍tu(bu`z 1|FܺQw(znCjȝlɉ' }~&ӪUdt̩Jlf/hr'nbY{-x.d %L|J:ŵO^XhcK&M7]&ecFڢb>CPTqZS7OJl<|hxأ䚩pcl ǼD{ &E7`^P`c@EK`^SꥀKeKLra^\;U*jG䈟/9%R|)Y~aX‘HUGOh:/jAp' c3vhx8V&jEk䀟/C} $FXȐ|9%]RH[WvDTk2HQ?_2Ɗ];#)aBPSV"!s#%a B:|n-=ʠ b]qLk3a04(2 CSC34C#iWH|e;^V*ھ1P5]n'dYSACjjfqyw' \D!:Zx+sA9:#L\S.R(yqlN|HnH8Wh{88vMprީ\r=gX"bIG dkq\ķ聈- p|L5+oAVt-|+a;ydLbZ L"mZ%2\#uq ,ڤ& WC| >UrZ9/ڌXh>QptLk!XIM8a9 8H<]H*D}ո^ҙ-M#ĆXzGJE'=Iɇ!)qS< ::ѱ|nt ,jأt bMǺXh<0b1((2$%3=qK0lǘl@T:6֋3c@ K0iqBxYhW^>t}^:Fe]pL;- 6i -"2Be'\%A*utʺ:sT]2UlҶRPfggM$bf;:f/y &أ~km3DGñ26`MN٧dd|`F 0ԩ<GU2$bd;FV&#)893cdMFZ`QQL`B=K6Fc]qCt=+큎$  Iɼ)@rc,[R)p@ƩTQ$hY#GJE2Zc\5 LdE==Q! |eX,=i|Z:,4`ԀNMD2acȀlT dyqCGAѣi2$c+큏la <\ 9Yw(Xz (LGɀ ؼ;tee=2bd!I,GX܎` NژrTNMnr dHUtu..g\̕\,=\ &jأ\ bH"iq`e=p0ap0(1$4 p CpwA)Q%TubݩܑMnBFr¢bjwN:(}cd]PGu?DG2 (ؤ&  H]dy qA%kPnw $-#>Pnmμz[߿m%{Xd7T1J5xIL u'h:&jM0Ct4FG+MN3/R@t);8x>LbF7RÊό/Ը`ˁ#9%s?GN,{4]0;ǫ F+͸rDL@@ekoV \ nfo "*AoK`Ff<ƙj{Jkszd^9l&ov%01 5 ?F&pL;TPcHF6p7[ոkwTP 6iAOI#A/e%!Evr;r/9&أ際p@dݹh8V&C8(LI RȀM)o̚vy„kLQR9f_|wd ZBIvw=NK4DGsѴ2{;@&m'g퉁[0r]އuk, f\rie쟉:wVL">XI'c0!M9cQiXhd &vؤ{fPcHB6p!9GݹY᎖vkT#Q?cZIiYWH[Ws,_k\#P$GuqL{\#PIM8s4=rq׊8Я%٣eၤHZh39Rhrϗ̇@^zH*,EU#zv3nfƙW酢u;~/y &أă #s!: o>lv@y9pn!`h=Iqe blYhT34&m7I5/uq,_hX2DG2 eP&m7I(r0oҜ@`S!LD&1꺭\%F<'0lmv)K~JDaJ~47+,It쀋"#sf8~V&ڽPI   kSPcWGFCv#k;9j k|{d}a|G+u (jأ{dPcd2DGq2{d@&m7`PcHe8ؿַuR/|{ج5>>MVŇb~|G OHOɜn3p@L2T\`cXɞ?ӿXϏ`]W/$9 VW嗏?}Ɯ?Ͽ^Of׳ٛL~ 2|9n{z6zs;{?-3-831B5Wۗ_zQ9;~6H TX_~1dѡL#IOSB*ri3].0ӖrB%;ZnW3zSrL/Wn7u+\_'b=oj=N_.W XeCILn=hȏL!ӯ(C˻+|v3s$K[YC'R"L(>O ׇfT2Ы>$N?׷I!C[VD$sW(z!2iYEo'37}|ڠ,>1]J"%(]qWj7k_ſ/L~]|/?zɟ?\%3mzLPi-۝LzζfY_=z:n6gm#2B#CTُ@Bz!.B7j3e2 ߧTmx8"ެǷU: U.2?FOT Ώ7ϦDSjee VˇTKovf{^:xxs^z5ޛ.Xy?0 6&mTшxy5 AJ8 /T3h:v_OB}TUxiN0?l  N~h&fd4晨tIK|"/&kN#ieixCSͨ-,~ ]Poe=Jj=Ը`m9VPgTz5j\A>)zwh)/DRVeq4V[(PTǤby>+r°EUr{>;'XHZ$i',_J`$ JJ㌕nnG5)*dSTInST)O3o2 Q&تI}[`^>bJxQ:>7DT& U&HlM1Xؘ0뤫1Q8iQwQ$ IǑDzBDEkoEOzqS>o?\\:& s>͗S,|╽{]S*¸rX E;NPKs5D!z-Servo.fzPK6-fritzing-app-0.9.3b/sketches/core/obsolete/Synthesizer.fzz000066400000000000000000000360401272363601300237440ustar00rootroot00000000000000PKt5DK! Tpart.sparkfun-electromechanical-buzzer-ns.fzp_69129ed5bcec2b4b26eec3be62bb1125_4.fzpVMo0+YqۭIʪm˶^ j( 6 b<ϼ癱/a@SG."/U4t K؟@&Ze+t=~[ϑ1`3Sx)sN'0i`Xi"d8ID-gh;J{Ub¿6BZd#8$<S$GI-t ^%W݈vjVCM ;%NsZ ;ũYI̲XŢW]U/SNKGRO$3مt 9at> {6VH̐,S½܉͜[Wيo':N@g Lr$,cik:0w&"T;Z(rAn<^}FY(A[n |`aK —rS#K&BAj9KU[)~)݀,jRCPBJȔ&n x_xOR ޢhi=O<٦](-8b%TS!L~-\yqԦ<}bkw\AnEr8#$d"fJO*Ǜǘ~Hp7wFԒ$*9kz?I:Tʺݐ6 -INZo}񛾦瓮[NVi(AdYNvH9X+g)SPKt5D0; wsvg.breadboard.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_breadboard.svgVYk@~ϯi Z IIB@!-ZWf%N~}G+-@]"ujp=2U68#;<8mJ.ur((fUmB<nRP2^1ǖ>gy=m^2@!X㺞g> N)up0z<(q} E&WMD/jz܇7EQx"M*& ta+a1-x]F2u0}l$ӆq%t9 UՂ<Ņ:Me\Iu5LٲIˉLƱʓxޤLlU= a`A.N1''`hZZķ:߰ALa:n6&4d#Q'y-`>,:KwV"}f<$b^d[pVi"'RYj:.9B'ۧADŽ  #{X%dd>0> #Czlv-{#[keyXy GUyUso' 3.J@ 4:apFdmUlkw2:x^딶4xp/]olkc4@{\G7nxś]aⶰ ;nwE1sTɮv1zP$snp<(:AEbvX~Gߕf/PKt5D/= {C ksvg.icon.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_icon.svgVkoH_q~H+a<-;UJJR~01*N~} u]$.sϹ͢'VkF5Hy :mk=ۼJRU!`ϷpYֺժp( 61P wʁ~p]o @"8]t/Y3+)'$,q,H$`ww$vdRYkD Wb~mDMυ%?ϒ hW4aX؂![ ٮڝ0AakX$/]-]p$(`ny؟Yl{ۚdZJ×:3 jmPdXۘIQށH[]n RM" MH kJD :as'dCVlx2=Nh Zkb3Y;E++ Y$2:*Dc7"Cb$=KcE-=fYjoX/[ iN~6` :K|5~L^OPKt5D;2bsvg.pcb.sparkfun-electromechanical_buzzer-12mm-ns_pcb.svg_69129ed5bcec2b4b26eec3be62bb1125_pcb.svg͔Qo0+l 6$$ )r+`GM](Iej |}뇦r&AismlNJzarQ[(}G):%a}+CS^֕G+] iڌǽ&b  &,hCv#.ADqx[)]VHiVI2O'LkѪoV<9j b檕ԩB9CPlm{CUwQFKQ߭(n+.# 'H~7vU%@*@:#' > fS q!~İΈ)_n oENuMgr@kWa8LHa~ N)sd-Z,BeoyU5TAn:LyQ㕟t1y_ϗ} utAiϕ`q&Vxkp[iJ+-;KXPKt5DWw3'$Synthesizer.fz[o8S쾙lU=b=3})؎R>bJݥCh i>7;}?#8>yoVzsj/?U>>.O\} v^ $#dwtZ뿽\_ŗfy?le^_v7__}ݞ>&MCo~_ӝӏ_?wf}?^!cws@ۂWC`s ַu~ӗKx<<F;@v*yK>:O I:Ou!)o[p}>1[|mOO^㵷aٯ+%e˾I7//=^Ӄ16=B܅Wawo[*vKw,Kqm7 ?7:|8lݻw7?&y}2Kw.G?pnCOeFWo1=/]}߮BS@WB11^H`LTfe>$ԑ} GIEr}/8=k;f}~K1 CFM}mnkICc cdiI߳& s Yl)a<ˌ x`[i62=Rj.[t5$*b"Lb dd3vs6f"B1@ؚ TD|ke_SP19agPM9Ƹ&`*Ba >N5(sd}z%-i_o B۱"J"w*h#9i9PR}&SC a^/65gEiLCĴz751mɘvQzB|{&Xgφ)x sM$Σ7|8D<cF\3φ<8!?aBt^p /Е9< sbd/ŏw66,ƥ cVDkkx\/ģgk ޏ_ ^_8;:d"|{MF0ƅz!KFҁ2޼lL2J{2R(KWួCS0Uչ_!șx0c `8㈲ x+J)'Qmk0i ج ҟL dpdvvk EKY`ߺ43xRV+nQg'-26Йh) *[ZMej0Cz)++[yLTwćo nS6t8=ܺeՏOzx9;4|@1; 1zѸck׾<|xEYŽCzڬCFMyr {D) Uxz;}ӕ; f|gho)c☆ߘ $fO,3̛S\2Š~4u-:7@גDC{*22U{2'<"#+8VpՈ, B4ݧq͂8#-]&;p`Ԣ\0& EZ1Ę䢑ftseaJ01UeQAIY*)Q!U&BBf{4YVרvKW!Lz8Sp̄oQ7:t\٠iE=2!ᙦWp\eDhE~i-;CKCzx]V+ӣe7ѥif@ tHte.)p<~NӔD:% wW (/%A\+<:o]2ƑU aiFotxYߴ94ufh*X$8W"ݕѡeuP-ѲZ~jشOt&TŰkHX+!p"|.v[2U~+s!no RPiE-L1FXIB%#F<%́+4mkǕڤ@eb\IHRK$ *U~c3g쬑0 MYljL dC\Z945stX`hcdXJܛ9@SUH@zM9Ai"|1A hjt;٫~|e~j|^qp:^A~ߚkYАh%gV+[l0rY #T6ZgU㘰G<`J'_v֩#ǘS1zegGM"=ϮK7Y2:wf`RP}u,u/0"0 M batMLV㥲\1p :k0bHաFT%)1Iwqִ!oTK:'|i5vu:Hd:,2l>-%@4BIر,mzLa||:Y`Z,!c!>7&bz DF\٦0͗Up:ȋB2K//VppYљ~aMӋKjA伓yBDܙ*; JCӥ T6ʮ@%N.U9,11Oꂚ#$eGze),H!%bD .y:$*4-bbPuQ.)2'%SH]YX.U;015X-]Q'cK[c,l 0֡zHǤʨ/'|DlQ{Ytn}B|(b a<s̳/Q[DrtAG^Ʀ+.(6:ʦ4p("824_P3|kU&1d.LM{T .p WkD1i/* vrګpҋr_ ^+Y3D}^_= ndK 1}HUX>O#y{Ǜ?ǧn>>Dwxܝo 7?vSk|A b˼OP.Q}<뷻\O+4U9_lVoc!bɬY d_X”Wip?i +} w#J T\=聰1%z@XRSG̈X7/x=Zϻ(SP/\/veKsD/R*2зVqyStf1; ]e0Hmo<L7Mv7Ai׽&/D,ꇫO]DJd_JEyIԼr$^^i B[ZhixouWåf #_tls4^ ~$͍I!pn'$ c W::1pKLg>&e&>tjwxmx8/7qƬ0qS8XMP<4N =sCb7x$B IEm"%D" G^uŽ\(=&>gʹ(3-̃p Mu*,ix]mTߝ%%nrkp/]m^HWH+2 xpXGCjP>^8W?qNJNV9T҇PJʥX _Ԏd=BW\Cb OPLfkejniʠd:4ڰhKβ}I5\̩DT0ԷrinmL#2p!vLl? dCr8P}R{A6Yyd4ؠvhнCMٰM@I Q$k#Ái 5b;xQG0JM*2H0v~HCnfÔ0wlv͹q{SSX7Izq$tNa \L_*$1!uBhW&5.}R 6ÁXA0&kEU1Al\ǁX,&}P(gZ/\u^Q9&36JA:랝j$WB~*ec#7uQL8(O+яt rM;kMnsx eX&a`z3;ý V)YܭZS\ֽ2Sn)ZHISZ5 9D34U>1rcDk  ._Yf*[KT ʣ { (u0XѴO$T-`bgD#Kz0`pEW"'4FEce9e<78ӠKr1; v/C8/&{2'aŖe^#l]R:r XGeDN”˭ XFơ2FYɤO܊6 P[ $l/xX?z}s_[oV,; Ekns׻*mr zۮW߃jiS}wޤL&%0EJg~)@6EC$D%c]Jl!%'GϠ/IWb6eU=wmlQBAVPx{( .f(MTcjG'#%db aE^ ‹钗 `]SG9(lRf:@L9o*( !EW@x!3&z4g+OmrE<6౉.Os**2PZaaZ ! H+6 H# 9b uH~<@8!DIO+}x$|y6! 'GO{WzIR81q f*eyҤBH Abő2zZ(ʱ R bₓd[“SU ,Q|w*?.tfDP`ucQV#C<"#3$)%.0RT98]Eqi2eq|k&#~?qeh7ciQ4`bP,Bλ.ش?P ڢY WFd25.QL mE^3uvPfl/ح)&l{,6`XC.U6vhcQW0@0XJ`%봃 Ai`&+!#a Va)8'!؇4ݻJ>>t?İG$ |v9ŀ?+҇&^aHr`ˁpg}(k۷<=HwhٸE\zU&t>$9IG(Q~Q~=r#j^Nj^NB]NX0f^OԗV " FPx|>s%ixu9}ͼq]3 dM`=*4nX.|pStHCxWN߮pf=S9|X-ohyr)iE"J *ȞůED)IᏚ[kn6N˺Vg [;tZ{^UtFY}H3uVR$T)[s[k`̓xhAǷt-d=0gT}$?b/!g\<IO(\cށP&k4ciXN]FdOqyMM{*#6Y;%#8`:B_+[XeHkD]y x~p.!Yw4e8WXRY*x1i*kvKWT@CdJ)ǐ-NXR֧ IcVI(m}O`>`]a?<֕(0(t~[ +%*&" -*.J)cI| .؂JM͛SdOr\/no 5#) '˩)ctfq*?;m&l".ېuģT.1V\v[̔ U)lv( r#(h845_hd> 1"QN1>⃪!Eڡ_yS[q}^ӓP5Qi^pWPQdPv9`PЙi}d0&kfZ+3`=Eġ=a^/ ˲Րѐq108 #r W:ˡ}o c MN`"0_L6YG6f"!{.S8akqpcL!pKΦB` :*o1oMtwYD |^&bԪPЙI 햮Z 1mS:hvh(ǘ5rW"Drn=/C./J,ʛpMb9EWM!c E^gz:3 jnm1W|aw=.bǘ/G94}AʱY#~VuQ:*oZgdum]t5wwMT ׊\ù$XMt_m_@4+$LMBYt_m\`@cD#c>ec̺ TJQx bGܙ(hf=u1r(Q )V^NybPJ$*\qegR)WVԙIish bwђ=E1ih"LCkVh֘L-2}0u|0/H1.i9YszxjH#}^_{fcʽdhb>J"2!K2{ew  `l^Cd? qij?d픆@paSu>pX*ёp~~}@ LeZ!D: #WsE0 :X.kq@ѐ ȞJaP-DIƤB GS )4*Lۖgsd HyQd( ÀZi:Bg&AaFH$X+|845폀@&k7cҟ?2idJQ@%/߉s禼iA\jK55v1Rr(ZD#g𪶚Bg&/a[l2tXM(1&)ŽGBZZ4',zq 4yK76 `N)~M9%@cN9a[ ᘇ;ȇ)MMʇMnPcD0<Oid* ONB$2(,9>xSsvg.icon.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_icon.svgPKt5D;2b svg.pcb.sparkfun-electromechanical_buzzer-12mm-ns_pcb.svg_69129ed5bcec2b4b26eec3be62bb1125_pcb.svgPKt5DHnUl svg.schematic.sparkfun-electromechanical_buzzer_schematic.svg_69129ed5bcec2b4b26eec3be62bb1125_schematic.svgPKt5DWw3'$Synthesizer.fzPK&8fritzing-app-0.9.3b/sketches/core/obsolete/TwitterSaurus.fzz000066400000000000000000000126421272363601300242640ustar00rootroot00000000000000PKu5DӨ¬ NNTwitterSaurus.fz]m8~/S5d[tVҙIR3d2R40 t~ A5[iu|$=:ёt5Fw3\|vVk[\ h,b3ܡI1Fc긓cpp.]b\>ܼB=#,躷MwH=u٨g?/+gC3ܑ?q H3սyoV,d-F7[]~pc2f=c"E>e .zb0_nW`Y>gC4Uį+oʙy/ OVl3lԙ|wx,;_=Û͏w'O;LS>̉uy⟲}ï gVDI`d e~mTWf%\-12Ttf#|,᦭錯{+}랱(#0Wڻ[ɇ_CoR/{҂z' fEe1<:tT;q ׯDV'_Od?GԖ_P`oXV-Eal[R1x ߯{0!4L_ZV4pTIw0~ΤV,?=[wcC8]g/ P)/(`Z:EF !oq@FH+sc g(;P@VHR|f>$M˷ƷH}Es{OHNfRWЎJ-ZiiF0c|(̅@5rKY79WQvj]ak./Ksb{ڑurg_.y봊Ow IiCI2;%Pe0g ce*g]%7:eyXHcIJ2| DiZйWZk-䑺[ݲkpq%M)wzᾬڃ8MI ΓkJ{e(m86MXCKsYȊD\\D>4!o" bA  fVG0/]I03@9+L 9Z k~fZ'7-ri@eŠlF%o_nЦr_.@5.I%ňs Ngv- 0?t}F}AhmgKԃ<[R厪g wc>*QonhAuTP*vHIZ'F4[7 ;BBomrN'|#c[ bH<F S9GtTP*'Hs,,GIɺ| @>A$1acGR+K=d}mDc8ԕBIʎ>cce'';Cc+˂F?eԶl.ao'+;)fCE5J'2C5‘&k \  0ͮ律\ xYGΚM5NZ<.|$ ֞$håZ֎;-nv2AXљXhC:eDnqS2̧<$ l{&wo⽛&UYLʻJ~uX< ̝ރ;=~0,@eq}vgo:dS~z:X:?m>3 g1Fbrų8R7?n>/~b|ng}ܘOr7_lVjõ)M%(o2?G22v!a%KX~XK vKY\^_sMdT b_[F?HUјm!+/yX$,3;~(^ȁty e߉a-kwu=\9b[2U]0,)?彤=G[ þ|}a?} #ϡ:2; !JP%|#gg n0TwLY$t+;Ⱦ.W$m,~., }RVr|`s&Ab!- QN넏h؉GNJDO~AdC8&($'k6[Zp2&eN8YwமZ[BHRVTRJH`0{~SKN=j&czD#cE1MMM&yЃaDam䇖i^T Ek;S$a̍k+ ͸X H D*v[Q'Ot1]p|D}t|H1=M>`rLi]xYN"VHH$lԑB&&)#e2M=7)N',#e0EǎN9~ѓgDX:1NN;i~GGNJDkAdC&($k4Kp O蕢)iь[i% XMggcbG{,]ivݎ~`&kB4hX/LI]x_ӹ'1yCoϹOm?Σ?ېm?7;eϽ{i(<a4r}%q{1G!poH#H~.Z2'#B}SUDH!zM+籗u}ufTiF/9A]bWb<BkY Sˢŵ\k^Wee;j]2zU(a\F <i& U`͐{~&~[g7&Oϸw̉SErx*G@`T{~*'87T'4RoΣՑO*p]3oФՋdoP2_dTm Zama*J f~cH1 ,vv`'5u4IZt(]PcѥeJ%0w[gøkL]0,m0YZJK,]}Ia:MZR;QҪ5VҒkk,K$_hRuQ8SיjeEޗ:*ȭ3w[C4Ձ[Ӛy3@/Ni͂SkRi &Lz *y[}(ɐ-8 PYl,i~GDeQL6 V4q0oZb7ae2p8=[7pd],i50ch6:"(W,X$ Дm#eku$H'p#!:@d}mDcї$a ?K:X$zZbX&k[A/4n ]4ӳ<G= o:?GjM9C8bwQSi5F}Nfritzing-app-0.9.3b/sketches/core/obsolete/earthquakeDetection.fzz000066400000000000000000000201131272363601300254000ustar00rootroot00000000000000PKp5D#\?earthquakeDetection.fz]mw6_UMD ov=['mi{4_rd%U8~I/DR34sb p>gf0xy|r5 NWz 0Xt,Oj7 ]kE|3y_ ť=šp;#z:##۱r\ O?Bsq`47hb˩uz"_6 }KthѽJv5uWVx!u b&gFTn!FUFҶL@͙mXܣa]1s~_K-y 0› NRѝ AL#_{R/}MtntG[溎;(at&o>{P/buE|,SkoWV6. %ͷ͵]Yv3JzƋϩ0X4f-*y"a}FbuPt.&Nߌſ8>r1)Y4yӿit;A3% -'X8b Me=zǏWʔ q*Y7=̥V8L&P-GyI)eBӁx uEQTN_6)X1Q}cF,,y;_6:jcE^7U,_h4I'rVq+Ɇ\,&Zxl]b:~z?AΟ^]'06mNbNRXnkaX/ Xܤ u` /fT 8zIlBaI?[Y[/'F+gڟhvKyz:F|-kEwY}&D{3ׂZH|q6b{vT&΅7@FtR&aG,d$N ѕfܟ-&%#z%Q"~7΄M}㕐0t1׷|c89VheTMx_jm{u `1, XSEn@8[wӉyF݁T6^dWI) yb3FHMQoڝ]͟*Urbs'/hAܧJ@T\}vYcs2U3}7XΒra9UWڀ5-GC Ĩ} E#Q]>#J1ScF4L1b@r5Qg=9;Pljd)TA*zuX|d]\{A[RI*R1Bq(#uIhV!ni!ӪTюxI.RMlz0LV/-Kp+V%Z -dVnʄS~)N|dϪ QE5'K=bQ1JDORE!v"6;&H 5Zh=cO6 z/U +Xz!aOE-ÂzD]>@rGѐ<<<Z4h7w {0:ih1,V3ܭh7 MP+J$MY&E/Ęؘ.ի͞N|4>Zw͏珦atbB}W6Whv+_c0+0ʑpZ^lXҘf2dGA_lnQ4ohaF 1pUPD 'DUWbaE9Tt-ڱg*\!.Xl6(ٷqN*jI]Kߖ^εD2nB-F^OnPDJq2Ģ^%5[PY4JlG+ B|^lj&|lgPiktT>CXԛy8 *0FoF-AfaoSaC$#|stݡ$晼6yT> ܨn\d=mYUeZ4܇*n ^|ppW.Nj;Ҍsi2̌[*d$,iyaҐT]ժ.H쨢+$eQEu3JcjVj>jMK#8k&"86r zr&}FxlcNKY7Aq&CF!Ey-)l{7bi ll7:,Kb^)VÏ>'_Ar8Ev!DVF#@Sxl'day&J6/_>/_artIR;:BE\B|N6N{aqdXG`"1ꫩlRL®l6cTOD2g ms*Q+xu 0 i &Q#NhI?V+2&Ek$lueT $"ڔN6HiL#hUڦMЁV!0xL$lu%M^9^zv|9+ { ̩% FO9K 4T &\XCq &D2wsjQ &F`v'dO)eO&H4͚ӸG1(HL :{jˎleO@v;2{* vGWeO@!x86{*ZЫ'Ih=5hspw`sHD0y sGmFI)*;a7(p .ZϽ3״U8?R +Pm@Y]NyEr[c'0>)-V@ q…quLZHf-Ū@abQ(r^SsBTslj;@-B5@I1+Z;<L|\aatjwxK1M.鵮3 4~k]돯1B"q&R0as۾ZóRihyˎ!祳"ٔۊC k i8A8'=Fc춞EHk11MBDfIIX1iSfIa&K[Zm%]OP hOeܨ8֟)4\΍zk/FNX; VӆBXe.O6n_:Ē7ĚV;’t $m *# J%[7V7ݏ7E <"V%rPٙ!"of ĬN@EtـTӃA%D*2_ SrMڷl "yLZ[ λLu Bnz  H{T  Pxjڠ@ѹcldy8 &fOL#kǨTc5h3=Q/z(Qn3T<ӭʴf,,V Hxţ@!G bxy"έ`K;,6hY]; @}_ W]#s}-A@3\}Q*Zn'0 O&ЩO{("E V@hK{Ǫ7 `&9+&F2$cR;sIp6#=8 ,G޳-hsmO(%>`GZRR!k4 .yqxs3R dSY>\-kэ6Ny4 DZXԻ_ӫ y>|ʯ__g_nfg.g)1k|LWgoˡϮgRT^Wo+?͹|+?jb׿<מ_DȝI+hy=uQPEdL*ug]WGPqafz)y|z):_}N'ܪ:|/~y|w_}KJ #2V|m__?=ח~7o_??x{7}LJ7?ӛǟ_ūo7|}67o?+y߼뛏oŸZqn>?oۗyo[Wo?=>/9>뻯FWL~|lZ/Ͽ>|ݮ^o_j+?(ߙ?o_"q~qĚ?LzOoi?͗o'mpNŧo޾Ȗ?~8cE/N}y3l1޹p ;n& ~9>Jj8=Nk>=^ahs))==gC @fV h3#/ۋBj¤D[z+&hA916R 1b0FsH0߹oÞр.N 00O#> BJN$S#8a$=Tp)By)FNa~S$Y܂7ƈӮѠ&;.R`M^M'锢DN5a)7c5%MLj)e7)b=T'1婎D%pE89RX/fa-%1wGz9 qND*.4UwfÃO\(lr + ʷf8"z%r% -I;[gE ȒFE>/*Ni=s*SWΎorȓOㅡc3Bns,ݚzxlz]ϔȎB>?bD]0TSܚ} }˪ =չ쌙D6cuϐ"V1C]*ղ?i޾y{LRT(dk`sϖQkVfHфi2B] FTI*XƳa08QZV)0 7X!r3r B% ije3`@np%d+ B^ U$! (Tc3D3D"xՂUeU<+V* WTK|?eQCDqUEVWĪ`Ī`M$*"z i[S0IurMQlT &R`sZ ZdSS]@>5@)an +BY~" mMQE^3OijEosjTTZ9>P"JϥjZznVZy|+e$LcI$K*J*ւIʳ Zm~KRT0ȧlV^TGa,!uUV W9NE(O&D@춾]`.oܱ("P/~}EST @lg(QS鸝DYƾZm@.*Rr[ElJfb@$4:0P M(s%;mYNZ#%G]F}WĉINI5HG Rb}8)1Ӷ^]9`DJ[[l,#KY3{.]a&)]Oϳ nm}@'0*zt+93B l+1\8Cϭ=)CO8%]V/WQvRyΈ0ұ'eq՞d>(GUHgJ~MȰwǕŏ:Ҕ+MQzH4Ny4Cb!$Jg g9Y2YBZX-(q IL3Lΐ"Y2{;p8ڠq! Z۳ aـk)-0zElmcqccԵCCIfWazmXf }-s*TVε? 'U'Lb4Pc4$(VI 'C Nqb B^Lr3PdU3$ȧDVEo?06P1STRj[pIҫzzLN2w@ePStQI@C4Vi((Ή P:.J('\vkWO0Ak#E>O<'^qƓ19F+ǵLCS..\R;݊) J)7@Bxujdn~#ѠFh* L@lۆ*8,('L/'F0HՆmT&` mX-:"la~{V"?F"k[FEV_]ƣR(v+'#z v=1.MKeՏf1-Rs.{ڪVk8ll YƖX^j,ybIцro͍p:SzDdt 45 =&Ѓk ,FsZBTcT!'M95*F`u|)^1cCOye36]z*|: X "yNiS[)&;J/1-J*3M)kPdkPRSUY;w ]NgSx,aOMN?[*'m.:ߖxkqU?W\X[T Iaw<@RbؤyD5_ 2:Cc <TL-g0R. FVCfH)Ü,e%8 l7b|ݫ Ѥu jE춞Qh4N*?t7uPrM)b'5^G4T@lgPQM) aAnq8w7݀,/.fdWћQOkسR'&a7lBUQ+=AcxWWV xJ5#ߺķR$(X |*E-&cϹ%<$Ό#P}UhB'@SF>ye+C8ZBa{%76A]zm5U?=BM%[(@F 7"ERKE }w_|Wʮg,d"7SbV-W%:Ɵ&u ĖNKFFnS4 K;124tF~.~PR1:Ŀ[,3?8PVCmt ߇^;eδBl;1t:+K+5ie2XB^pg*8~ @!=\T&1;- ]܈-F,9,(7k7b^c- = bK'_߃ƙ (1d5 X#H.R(˟=-&1Gz,ȓMiqzT~S߅"8O |I6@^59xO.e5I/_f ɦ@^3̞dS+Svj lj [1݃ OG2BI*`z?@m]pAרot"Stŗ.EmTW><*ga,f}T[ }#|gB-jyT?VE><P3daWw'j0urOzkBXt)$azi?o3mDdž_jwL-+.<HJzK qΘSqcчASIO/uמOzBZIǩ`B-Nq;@JHGDocAě1s*+I#[زpu !תrhP%vrv#k4xwJ%=u- ^'Zزp"[tpب[زpN+x][.NwC-GtHCtR]khZCS(ZzCG /c-k 54/T _KO踻@H-4xQK7eCZI/heh:,\zxvWIhNR̎`!nWIx2ݟ}XO@qе}H U!ૐjO$k Ⱥnq7_K8Wjw [R'3,Z:<ѵw+C.0qyhAZG1x}KQ:9ki!&k e-moHz3PK+5@LB-L XKKiyܵ4Fki+ZKյ^ ͥG- y૾y㗯o>}>{ho_~忿{|7_˿z|Ƿ_^}~ëK/~쾑p߼kbgUv=Wdwzͯ_}!?DKΟ;?zW[t}2{}|.[&û_:|vZn_UN2عJ0*e?zLzc4Y1Fcr(afQB_ %#u(`s5l9#-Bl1!{% B!bBAI:[Èɖ0ѢD>$s'@'-rvZ6t-8@KMx1N *CiٴzyljX1"h2YE!"N끯KK i9۔ӕ/j3`j 4 7h)Noh ! o*S#El2͕QFI2rt#;X{54@-׿CQ;ocarM[{D-:/==\r;Q!8YɐADw^^N%,BdCk#jcb>]'lml-͆.>=j9ktFQ'ο:նFK'/$pQN.aDknÈF6#KDvƊIf65|tiXA"nrYu!p1D&mDM.g,d#oK&[ JŒ~fRL6.;<'^yAg9K8xY@LgШ;򶬖FK'a%'F7ٻzd2LL \-@@<EyAۛFKFSA{ d94cti.mYM>Ew#ԗ1rG#i/9qe{#RP|C#j$o.'0)RQYsj FͻNh=ÁPޖxk-0 G6[{D-97[yAG[MTǁ0Zq N0 't$oKP[7[TL,:bJh7}WKDLBzQOHM>o.u jo-pyޑ|<(-A H'A8¶6z[ 1I:!R[PI4;zVW6F"K8\vzuō Eq~q4y$L&>iT(tL6 w\IR+L:ZcD:q@D:+ v (!/(n>J@`iHMFb$F)L\gތNal׸T2{m w\i(5p3@/&"K vF c$^TTti0H":gJk*j: 3eȵ8TtĝQ!kF t#ѴSdiXvR*!D>ݔw $6qДn6sXG_/ 0- MRsp DEbHv}v6[S{D-zpCnM7Q G7 :83ÂzfP8Ӵk"=0x~o̓8ў^<(uv fCohp66$30p:npMFlh[>QcCѥ 3j.L=P l+7^hd( ee$ 2dQFFbAHuZ"@M q:$6If-h8ߊ!G։L%I 's׏νpMBF+L9 x!09b&<"Hoiy5DFl3" EgQрm?7ҩD: &4 Cm(N|+wx!+/pk* BygBZ6۸BFN \ZhXrbuxCE2h>N@`jXOJee4 ) Qj<5qVقlp\Q0u \k<t*&E #T41YZ'P 04R(.. yA?&KD FT|F'0QN)~>JkOѱ&"9/ ?ѱ{k~v=i%W^з֬B{~Q!ue`Ё\j!WmYsC|˯ق]mޚ !ru=P :ܚ=XP9, 5Y 㚍m@F:5  B{x!+/hШPti%7:%hʥq1M)C#P_I>|H3LgCȉomq* .4]ZƂL8Y4%N/zt\^u \G6OX9 N P(gdi@XaO E#ѥuSc|Z*p-Cfdg6{n 6+?HH;ݤu hbG7{ցx݅?0'yoeK7qZÄɳrICl%oIk/ʽCx;cp;B [$J@`i0M7YU7Hs$ 0mCK'ǀ{9Dԝ=n=#[jn8F1K+9O11Ve>dM|m_k'tpTgvlq́$oCy2 G L")Sa$:ff&&R6')㱥:`5Ktr^j5ITwd \ڂ M,E8JP-}#$Z$-!LSs.B]uU'v.ݪCFqŖGNj)j2SB& IbN+P1F %S&S%jԚ4Ax|v6Eq)E zR9i+4'㞙g6 t#Tj] A L9!p#bT38uHK1cy CT YxL;{6d̕ iu5Z.a2bJjJI^pG$S"DO&Bσcþ(n ]%UV] ]5:(A'ԞS&mKUkUU VŷR&&RTccH q?C$A QZGx tUK[L?`Q!F>]VT) DWIOyC[㟴P7} Y$lt,ehH⺊r8*$RO:Upª)Vʰ~XQ! Q'rO2Tk4jyk)aBEUvtΫ4xh7N90I#ܜp„ c>_j\ʄz"YOϩQXue抏{$#Oݒ$n\9nċ"Kh *x% Jdfvε2JҁZaP 켚  02)+%U7;PXCQC{c-n ͔&ZgeE`H,8]gI ŸcDq$:i)ƃ(dt.NG愋K8n@n;QkA}4 AGK$94)ДHadHRPs?بt ٨;͖''JT 1,{*=9!M&LE7foC9f2Nd}FJ+b:;CUU̫*3*H\ByDtL"+'O,(4Ӳw@-?p¤Tpl#MIr9'NR)b]MsйD>Tszs"54K+4t&qVWCRO-9ơFXX`.,?!m .Hd΄߫.'iA9G\dTy Uq& 5vPTP1IxjN`'ƮsTNϩk qO)>VtJ` |$rD%#V 'X '-(I21Q4?s"M6N>v'Gϼ6x>-ՎH3Ej~xLR;o6BvO!cO)c׳Pl*0>\pLG92u0R{ E9?@EW`$@/8qFTc'ҏs2&ڟ 42j5H bz]BL(5; 0?2/8'TL DZ낾2rWt9roulsKyѴ!36hrH֠ rcI,o,ytBIpTI3|˻? JMd`hRbHiBq5p>uP1ISOՏrIW쁜Фǟ>?~x|xeo ٙ__y կ?快K{zם2WQ1r9~;p-'pG' =m?:-Xq=1iN8uj|jGK7gB\ ǥgD*V .E!߯*^_FB!~x`MEPer}3S]Do>7]"\CDiծX~$d0U{؋IezuҐcbƒcAzuYѽ$rDz/L9kRl~-9io?,#9FA? vF/nyA/;^%]Ō+hϳ_g{&!ēDo$,ҟwrnx q' .Bnh= &cG'h$>,igQI|TqMoèjp &Dq|{}{;ǏTˆ*S)dN?y<8W 󵹭hHQ}o_qk g~z? VV'Ytn^г_/hlҘTEz%z >qA!O~?^!;T3/wmmZh=^iu_q/ЋI н~%v.)"><}n9U&k+%;нa5gzA-,a^K7U A[ӈ]24<U<[! JO-Y ?L+eH8ݠRiƥɠ%V6:v ,7!㓚 Z>֯с }dPM$%L 4+Էd~;گ=c Z>zӁ~F7jnи ;­)-g7hܠ_ܠk~n]>( ZI<+ *C=)HлA$ nP%c_v 6A Wܠ0oB' 7گėB5 /KVyARCto_„{$( 7䯖ėt^ 𤛘a%<[W_1\=LkGp [ppsb%;d KNhߏU6,+!{Òyla97LY\x楶| DޗazpEcwxZPSRJc/AʪFղjx:dwԽQ-tǷ_Bs}#fwptakY;B?;~"[]S ~5ѩGtO~@^vN/]M:??[6Gft&1 ׂotJB d/GOb3~ H0ܹJSxЩ V\*`!`yA$ӝ ꠀFF5y¥*IAx, @l׍8Z@& r#GHK-Y8|G@l;MD^t$SKQ'YJ)*cLaHmcLn禫)/Gӿ|n49Ar"5\g:|;+LL, aGa>0P/8͍4E^Kr)ˌp%#GYxy@z8PsbN5fR' G:#uYLvnԴ'uw;v8$*p&3n!B@ 41-JLC'G@=G.$P7@F.UW3ody#!_Dg6/8 ܸbVԟ\^S[JYu镚8sK@>3wot~02XGήҤv]F51v9&xQʮcՉnT!+{q!9@B^ P^+_Q\| Lm2jtH[C I3jhy+e[ Ήc_~%el\[C;'>1LJ|h:詮e> 9bFou#1mx/d||j"KKĆ6Ɔf^]l+< :PBN|hc[r3![u2҆7xlT!Z!T(\+|s+go+"NKzqҍFl=(p]]k§ͮV0b-i bbD3)DpOp>FYեˌ*bIpEPTȈ uE=CPH?vʈ.7F4 FB`D){sΈ#BP#r32>n\̈ F$bfګm6~#-'Y"!J!@fL\DKF6Ff^5rxa"> 7j.i9C30ѡth×!F29J] e14d71=Y6&CN [vvTNZ;C"܂c:@q;ҹЗM<6^!0I =wpjGLKU. h2o0>i~NΫ NP&s]@PپdomkA:8Ra 01D ?vCu?:dp/&f^!`"b6>Ե0!qkS0GŃ*s!$#LFl4a9NlBSS%&J 7\J Ku9Ih'V eؒH=)p/pBQN$Cjl ǥ(Q^;9O J4M .}ZLΐp"ʜا@H(.:4=%vL*.Qϵ츔 $To01ԾBp)9J(0S3D#B RMX0c+P4 5>) <P0zLu דaƞI(' FO惉윦DF 9>d&GXgqG ez+ }}*45*pejCy:ux/&f^!`Bb/Gm 5OT0 EF]O!v%yͤ1%bƫ[:?ahNa鍇?$3\Rr)v,&gŠ/TvI@S^,KSqCéَDIa`HVhrjvr &Jec Q^I4mW_'?Cj;͔Q0>;A)k79>33%C!9=KIa\jYMF4 )@ (a$qJLRv'qi(6EQKSpu˨( q -ec1 )r"04)H &?aNYu!1O4I[&1M~bJmLn\7$Zmyd3yFͲ}UjxSV@ڼVb3]M` M߁ƚ)*-4 :G V/YTmDf^"C'DqauI"]WuFJ'*@I1B(`OPT7+~TM'^% wr('w]@S!,'m4mMSʁ k*n zҦyUuUqK$#nth4RT?mrjAB[R;y踆)25H k A 15MQE+(2g5'N)'$ܳ(IUfJx¶IX9 Z@FSVKV !Bc%x|гKA BZB&c'eRA*or#5=?4vvGd`1…=Wd3%eU;YMVw@_hjv (^1trz( `` (``2 Oee`(PKGY^DWn$GLC{nnU) } f45Lc5 XQ>u%XHƂ !ChdTY<ʒġbi ӥUtpw+JeKV(]z x ţV!J%0t%0{C RdQyK9q%zBވޑ$$S)ҞY0vD' {$H})%A O_ JAZJ1E(.FS #)p%9~ `)&>(R 76\(VXqBPIC`gCi $n gpN%$^QY؍ w*WaE5ڑN VlE&f^)"bCҒj%^WeNa \?K*&D6SRRE Ɔ%+D?)c̥$LZt% ێ 7]J3HH+1C_MY.v& Lc?0XQ~&͕`! ƣ(MW&3F6u s, Ce9C:5Pu*3Ay@9Xhv5d ZtgzLp8ъj#\4y5n0\(akKSt{Tt]H*0Q m*`"&chQReP@Hn>"8&L!P1b`B0B\@f\Pq{LNTj9U 5\-L0Lu+Z4I5:I):ARA*fPuk#ϚTP4Ȩ271X K#oS֧ZszL4C}L&coRcZZW] hjvEV0S@Equu%VHۘR+x2`F$"GK}}?Ti}W˫_}_yW~}9GC|?߼׿ׇM*JٹkZ)lF~c?#߼ŏ_~v/·!+諧 q[^zEڇe?-r=1icz]|jG-.lLDiG5dOI/~;|pӛ[3׆g@Jw׵⎿BD< gw{<(1ǂF5&P}ahK> I`+ _G;D:_`=>x_k; ӂ(uHvs֬`_fe/5Wl-J^/dm7-#8>i!>~wDta.1zznR~r$>n7Ɨ;AB?{-n Vf"!~u̴P-Vz] ̲{t'9c/H_%(ĈS<"r5ڡΔ54#E:}ߜ~w @?wϞpP$oerb{t-Mrf @8SX3Vleeo`Iqp%ӀiW[Ht#CťDᲧL~NO)1>g\Ls¥{?KXW+[ݝWV.5;(Q#d.&*zw0½˕2oDDf7EE~-"͹n2%wheoZpt5.醞VyH Rb|DoN`nXdЀ+^2ղ!&R2gp-FH1 z7c3E{PI(LND()V v1o#v;~%FQ.H}EXOi`7KE,YP_<`͏;Td+X-"ݥ3$1jPl:(>uFޑ5Cy'炘,ciؿ ƏB:? eZѫ}j=P8~̢yk ~reg}=VkŶ>P.su;5w^5iB藤?&w[q\|_ Krqamɹ5.h䇛n)UB]cE xa1g#mUk'$gnleWD0/jO0F0g`D-m +ue274zU W]@?TL\Rp?|O\_7:KOsN޹|O;g o kF0/劈}9]y+䖐p}k쒘qK01/R) H=929AX").gDqBރKSƪnVu<_ͫbPKu5D PQ1 treeduino.fzPK:&Rfritzing-app-0.9.3b/sketches/index.xml000066400000000000000000000447111272363601300177530ustar00rootroot00000000000000 fritzing-app-0.9.3b/src/000077500000000000000000000000001272363601300150715ustar00rootroot00000000000000fritzing-app-0.9.3b/src/autoroute/000077500000000000000000000000001272363601300171205ustar00rootroot00000000000000fritzing-app-0.9.3b/src/autoroute/autorouteprogressdialog.cpp000066400000000000000000000140661272363601300246270ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "autorouteprogressdialog.h" #include "../debugdialog.h" #include "zoomcontrols.h" #include #include #include #include #include #include static const int ScrollAmount = 40; ArrowButton::ArrowButton(int scrollX, int scrollY, ZoomableGraphicsView * view, const QString & path) : QLabel() { m_scrollX = scrollX; m_scrollY = scrollY; m_view = view; setPixmap(QPixmap(path)); } void ArrowButton::mousePressEvent(QMouseEvent *event) { Q_UNUSED(event); if (m_scrollX != 0) { QScrollBar * scrollBar = m_view->horizontalScrollBar(); scrollBar->setValue(scrollBar->value() - m_scrollX); } else if (m_scrollY != 0) { QScrollBar * scrollBar = m_view->verticalScrollBar(); scrollBar->setValue(scrollBar->value() - m_scrollY); } } ///////////////////////////////////// AutorouteProgressDialog::AutorouteProgressDialog(const QString & title, bool zoomAndPan, bool stopButton, bool bestButton, bool spin, ZoomableGraphicsView * view, QWidget *parent) : QDialog(parent) { Qt::WindowFlags flags = windowFlags(); flags ^= Qt::WindowCloseButtonHint; flags ^= Qt::WindowContextHelpButtonHint; setWindowFlags(flags); this->setWindowTitle(title); QVBoxLayout * vLayout = new QVBoxLayout(this); m_progressBar = new QProgressBar(this); vLayout->addWidget(m_progressBar); m_spinLabel = NULL; m_spinBox = NULL; if (spin) { QFrame * frame = new QFrame(this); m_spinLabel = new QLabel(this); m_spinBox = new QSpinBox(this); m_spinBox->setMinimum(1); m_spinBox->setMaximum(99999); connect(m_spinBox, SIGNAL(valueChanged(int)), this, SLOT(internalSpinChange(int))); QHBoxLayout * hBoxLayout = new QHBoxLayout(frame); hBoxLayout->addStretch(); hBoxLayout->addWidget(m_spinLabel); hBoxLayout->addWidget(m_spinBox); vLayout->addWidget(frame); } m_message = new QLabel(this); vLayout->addWidget(m_message); m_message2 = new QLabel(this); vLayout->addWidget(m_message2); if (zoomAndPan) { QGroupBox * groupBox = new QGroupBox(tr("zoom and pan controls")); QHBoxLayout *lo2 = new QHBoxLayout(groupBox); lo2->setSpacing(1); lo2->setMargin(0); //TODO: use the zoom slider instead lo2->addWidget(new ZoomControls(view, groupBox)); lo2->addSpacerItem(new QSpacerItem ( 10, 0, QSizePolicy::Expanding)); QFrame * frame = new QFrame(); QGridLayout *gridLayout = new QGridLayout(frame); QString imgPath = ":/resources/images/icons/arrowButton%1.png"; ArrowButton * label = new ArrowButton(0, -ScrollAmount, view, imgPath.arg("Up")); gridLayout->addWidget(label, 0, 1); label = new ArrowButton(0, ScrollAmount, view, imgPath.arg("Down")); gridLayout->addWidget(label, 2, 1); label = new ArrowButton(-ScrollAmount, 0, view, imgPath.arg("Left")); gridLayout->addWidget(label, 0, 0, 3, 1); label = new ArrowButton(ScrollAmount, 0, view, imgPath.arg("Right")); gridLayout->addWidget(label, 0, 2, 3, 1); lo2->addWidget(frame); vLayout->addSpacing(7); vLayout->addWidget(groupBox); vLayout->addSpacing(7); } //QPushButton * button = new QPushButton(tr("Skip current trace"), this); //connect(button, SIGNAL(clicked()), this, SLOT(sendSkip())); //vLayout->addWidget(button); m_buttonBox = new QDialogButtonBox(stopButton ? QDialogButtonBox::Ok | QDialogButtonBox::Cancel : QDialogButtonBox::Cancel); if (stopButton) { m_buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Stop Now")); connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(sendStop())); } if (bestButton) { QPushButton * best = new QPushButton(tr("Best So Far")); m_buttonBox->addButton(best, QDialogButtonBox::ActionRole); connect(best, SIGNAL(clicked()), this, SLOT(sendBest())); } m_buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(sendCancel())); vLayout->addWidget(m_buttonBox); this->setLayout(vLayout); } AutorouteProgressDialog::~AutorouteProgressDialog() { } void AutorouteProgressDialog::setMinimum(int minimum) { m_progressBar->setMinimum(minimum); } void AutorouteProgressDialog::setMaximum(int maximum) { m_progressBar->setMaximum(maximum); } void AutorouteProgressDialog::setValue(int value) { m_progressBar->setValue(value); } void AutorouteProgressDialog::sendSkip() { emit skip(); } void AutorouteProgressDialog::sendCancel() { emit cancel(); } void AutorouteProgressDialog::sendStop() { emit stop(); } void AutorouteProgressDialog::sendBest() { emit best(); } void AutorouteProgressDialog::closeEvent(QCloseEvent *event) { sendCancel(); QDialog::closeEvent(event); } void AutorouteProgressDialog::setMessage(const QString & text) { m_message->setText(text); } void AutorouteProgressDialog::setMessage2(const QString & text) { m_message2->setText(text); } void AutorouteProgressDialog::setSpinLabel(const QString & text) { m_spinLabel->setText(text); } void AutorouteProgressDialog::setSpinValue(int value) { m_spinBox->setValue(value); } void AutorouteProgressDialog::internalSpinChange(int value) { emit spinChange(value); } void AutorouteProgressDialog::disableButtons() { m_buttonBox->setEnabled(false); } fritzing-app-0.9.3b/src/autoroute/autorouteprogressdialog.h000066400000000000000000000045561272363601300242770ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef AUTOROUTEPROGRESSDIALOG_H #define AUTOROUTEPROGRESSDIALOG_H #include #include #include #include #include class AutorouteProgressDialog : public QDialog { Q_OBJECT public: AutorouteProgressDialog(const QString & title, bool zoomAndPan, bool stopButton, bool bestButton, bool spin, class ZoomableGraphicsView * view, QWidget *parent = 0); ~AutorouteProgressDialog(); protected: void closeEvent(QCloseEvent *); public slots: void setMinimum(int); void setMaximum(int); void setValue(int); void sendSkip(); void sendCancel(); void sendStop(); void setSpinLabel(const QString &); void setMessage(const QString &); void setMessage2(const QString &); void setSpinValue(int); void disableButtons(); void sendBest(); signals: void skip(); void cancel(); void stop(); void best(); void spinChange(int); protected slots: void internalSpinChange(int); protected: QProgressBar * m_progressBar; QLabel * m_spinLabel; QLabel * m_message; QLabel * m_message2; QSpinBox * m_spinBox; QDialogButtonBox * m_buttonBox; }; class ArrowButton : public QLabel { Q_OBJECT public: ArrowButton(int scrollX, int scrollY, ZoomableGraphicsView * view, const QString & path); protected: void mousePressEvent(QMouseEvent *event); protected: int m_scrollX; int m_scrollY; ZoomableGraphicsView * m_view; }; #endif fritzing-app-0.9.3b/src/autoroute/autorouter.cpp000066400000000000000000000371771272363601300220540ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.a Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6947 $: $Author: irascibl@gmail.com $: $Date: 2013-04-03 06:45:22 +0200 (Mi, 03. Apr 2013) $ ********************************************************************/ #include "autorouter.h" #include "../sketch/pcbsketchwidget.h" #include "../debugdialog.h" #include "../items/symbolpaletteitem.h" #include "../items/virtualwire.h" #include "../items/tracewire.h" #include "../items/jumperitem.h" #include "../items/via.h" #include "../utils/graphicsutils.h" #include "../connectors/connectoritem.h" #include "../items/moduleidnames.h" #include "../processeventblocker.h" #include "../referencemodel/referencemodel.h" #include #include #include const QString Autorouter::MaxCyclesName("cmrouter/maxcycles"); Autorouter::Autorouter(PCBSketchWidget * sketchWidget) { m_sketchWidget = sketchWidget; m_useBest = m_stopTracing = m_cancelTrace = m_cancelled = false; } Autorouter::~Autorouter(void) { } void Autorouter::cleanUpNets() { foreach (QList * connectorItems, m_allPartConnectorItems) { delete connectorItems; } m_allPartConnectorItems.clear(); } void Autorouter::updateRoutingStatus() { RoutingStatus routingStatus; routingStatus.zero(); m_sketchWidget->updateRoutingStatus(routingStatus, true); } TraceWire * Autorouter::drawOneTrace(QPointF fromPos, QPointF toPos, double width, ViewLayer::ViewLayerPlacement viewLayerPlacement) { //DebugDialog::debug(QString("trace %1,%2 %3,%4").arg(fromPos.x()).arg(fromPos.y()).arg(toPos.x()).arg(toPos.y())); #ifndef QT_NO_DEBUG if (qAbs(fromPos.x() - toPos.x()) < 0.01 && qAbs(fromPos.y() - toPos.y()) < 0.01) { DebugDialog::debug("zero length trace", fromPos); } #endif long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setWireFlags(m_sketchWidget->getTraceFlag()); viewGeometry.setAutoroutable(true); viewGeometry.setLoc(fromPos); QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y()); viewGeometry.setLine(line); ItemBase * trace = m_sketchWidget->addItem(m_sketchWidget->referenceModel()->retrieveModelPart(ModuleIDNames::WireModuleIDName), viewLayerPlacement, BaseCommand::SingleView, viewGeometry, newID, -1, NULL); if (trace == NULL) { // we're in trouble DebugDialog::debug("autorouter unable to draw one trace"); return NULL; } // addItem calls trace->setSelected(true) so unselect it (TODO: this may no longer be necessar) trace->setSelected(false); TraceWire * traceWire = dynamic_cast(trace); if (traceWire == NULL) { DebugDialog::debug("autorouter unable to draw one trace as trace"); return NULL; } m_sketchWidget->setClipEnds(traceWire, false); traceWire->setColorString(m_sketchWidget->traceColor(viewLayerPlacement), 1.0, false); traceWire->setWireWidth(width, m_sketchWidget, m_sketchWidget->getWireStrokeWidth(traceWire, width)); return traceWire; } void Autorouter::cancel() { m_cancelled = true; } void Autorouter::cancelTrace() { m_cancelTrace = true; } void Autorouter::stopTracing() { m_stopTracing = true; } void Autorouter::useBest() { m_useBest = m_stopTracing = true; } void Autorouter::initUndo(QUndoCommand * parentCommand) { // autoroutable traces, jumpers and vias are saved on the undo command and deleted // non-autoroutable traces, jumpers and via are not deleted QList toDelete; QList collidingItems; if (m_pcbType) { collidingItems = m_sketchWidget->scene()->collidingItems(m_board); foreach (QGraphicsItem * item, collidingItems) { JumperItem * jumperItem = dynamic_cast(item); if (jumperItem == NULL) continue; if (jumperItem->getAutoroutable()) { addUndoConnection(false, jumperItem, parentCommand); toDelete.append(jumperItem); continue; } // deal with the traces connecting the jumperitem to the part QList both; foreach (ConnectorItem * ci, jumperItem->connector0()->connectedToItems()) both.append(ci); foreach (ConnectorItem * ci, jumperItem->connector1()->connectedToItems()) both.append(ci); foreach (ConnectorItem * connectorItem, both) { TraceWire * w = qobject_cast(connectorItem->attachedTo()); if (w == NULL) continue; if (!w->isTraceType(m_sketchWidget->getTraceFlag())) continue; QList wires; QList ends; w->collectChained(wires, ends); foreach (Wire * wire, wires) { // make sure the jumper item doesn't lose its wires wire->setAutoroutable(false); } } } foreach (QGraphicsItem * item, collidingItems) { Via * via = dynamic_cast(item); if (via == NULL) continue; if (via->getAutoroutable()) { addUndoConnection(false, via, parentCommand); toDelete.append(via); continue; } // deal with the traces connecting the via to the part QList both; foreach (ConnectorItem * ci, via->connectorItem()->connectedToItems()) both.append(ci); foreach (ConnectorItem * ci, via->connectorItem()->getCrossLayerConnectorItem()->connectedToItems()) both.append(ci); foreach (ConnectorItem * connectorItem, both) { TraceWire * w = qobject_cast(connectorItem->attachedTo()); if (w == NULL) continue; if (!w->isTraceType(m_sketchWidget->getTraceFlag())) continue; QList wires; QList ends; w->collectChained(wires, ends); foreach (Wire * wire, wires) { // make sure the via doesn't lose its wires wire->setAutoroutable(false); } } } } else { collidingItems = m_sketchWidget->scene()->items(); foreach (QGraphicsItem * item, collidingItems) { SymbolPaletteItem * netLabel = dynamic_cast(item); if (netLabel == NULL) continue; if (!netLabel->isOnlyNetLabel()) continue; if (netLabel->getAutoroutable()) { addUndoConnection(false, netLabel, parentCommand); toDelete.append(netLabel); continue; } // deal with the traces connecting the netlabel to the part foreach (ConnectorItem * connectorItem, netLabel->connector0()->connectedToItems()) { TraceWire * w = qobject_cast(connectorItem->attachedTo()); if (w == NULL) continue; if (!w->isTraceType(m_sketchWidget->getTraceFlag())) continue; QList wires; QList ends; w->collectChained(wires, ends); foreach (Wire * wire, wires) { // make sure the netlabel doesn't lose its wires wire->setAutoroutable(false); } } } } QList visited; foreach (QGraphicsItem * item, collidingItems) { TraceWire * traceWire = dynamic_cast(item); if (traceWire == NULL) continue; if (!traceWire->isTraceType(m_sketchWidget->getTraceFlag())) continue; if (traceWire->getAutoroutable()) continue; if (visited.contains(traceWire)) continue; QList wires; QList ends; traceWire->collectChained(wires, ends); foreach (Wire * wire, wires) { visited << qobject_cast(wire); if (wire->getAutoroutable()) { wire->setAutoroutable(false); } } } foreach (QGraphicsItem * item, collidingItems) { TraceWire * traceWire = dynamic_cast(item); if (traceWire == NULL) continue; if (!traceWire->isTraceType(m_sketchWidget->getTraceFlag())) continue; if (!traceWire->getAutoroutable()) continue; toDelete.append(traceWire); addUndoConnection(false, traceWire, parentCommand); } foreach (ItemBase * itemBase, toDelete) { m_sketchWidget->makeDeleteItemCommand(itemBase, BaseCommand::CrossView, parentCommand); } foreach (ItemBase * itemBase, toDelete) { m_sketchWidget->deleteItem(itemBase, true, true, false); } } void Autorouter::addUndoConnection(bool connect, Via * via, QUndoCommand * parentCommand) { addUndoConnection(connect, via->connectorItem(), BaseCommand::CrossView, parentCommand); addUndoConnection(connect, via->connectorItem()->getCrossLayerConnectorItem(), BaseCommand::CrossView, parentCommand); } void Autorouter::addUndoConnection(bool connect, SymbolPaletteItem * netLabel, QUndoCommand * parentCommand) { addUndoConnection(connect, netLabel->connector0(), BaseCommand::CrossView, parentCommand); } void Autorouter::addUndoConnection(bool connect, JumperItem * jumperItem, QUndoCommand * parentCommand) { addUndoConnection(connect, jumperItem->connector0(), BaseCommand::CrossView, parentCommand); addUndoConnection(connect, jumperItem->connector1(), BaseCommand::CrossView, parentCommand); } void Autorouter::addUndoConnection(bool connect, TraceWire * traceWire, QUndoCommand * parentCommand) { addUndoConnection(connect, traceWire->connector0(), BaseCommand::CrossView, parentCommand); addUndoConnection(connect, traceWire->connector1(), BaseCommand::CrossView, parentCommand); } void Autorouter::addUndoConnection(bool connect, ConnectorItem * connectorItem, BaseCommand::CrossViewType crossView, QUndoCommand * parentCommand) { foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { VirtualWire * vw = qobject_cast(toConnectorItem->attachedTo()); if (vw != NULL) continue; ChangeConnectionCommand * ccc = new ChangeConnectionCommand(m_sketchWidget, crossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), connectorItem->attachedToID(), connectorItem->connectorSharedID(), ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), connect, parentCommand); ccc->setUpdateConnections(false); } } void Autorouter::restoreOriginalState(QUndoCommand * parentCommand) { QUndoStack undoStack; undoStack.push(parentCommand); undoStack.undo(); } void Autorouter::clearTracesAndJumpers() { QList toDelete; foreach (QGraphicsItem * item, (m_board == NULL) ? m_sketchWidget->scene()->items() : m_sketchWidget->scene()->collidingItems(m_board)) { if (m_pcbType) { JumperItem * jumperItem = dynamic_cast(item); if (jumperItem != NULL) { if (jumperItem->getAutoroutable()) { toDelete.append(jumperItem); } continue; } Via * via = dynamic_cast(item); if (via != NULL) { if (via->getAutoroutable()) { toDelete.append(via); } continue; } } else { SymbolPaletteItem * netLabel = dynamic_cast(item); if (netLabel != NULL && netLabel->isOnlyNetLabel()) { if (netLabel->getAutoroutable()) { toDelete.append(netLabel); } continue; } } TraceWire * traceWire = dynamic_cast(item); if (traceWire != NULL) { if (traceWire->isTraceType(m_sketchWidget->getTraceFlag()) && traceWire->getAutoroutable()) { toDelete.append(traceWire); } continue; } } foreach (ItemBase * itemBase, toDelete) { m_sketchWidget->deleteItem(itemBase, true, true, false); } } void Autorouter::doCancel(QUndoCommand * parentCommand) { emit setProgressMessage(tr("Routing canceled! Now cleaning up...")); ProcessEventBlocker::processEvents(); restoreOriginalState(parentCommand); cleanUpNets(); } void Autorouter::addToUndo(QUndoCommand * parentCommand) { QList wires; QList jumperItems; QList vias; QList netLabels; foreach (QGraphicsItem * item, (m_board == NULL) ? m_sketchWidget->scene()->items() : m_sketchWidget->scene()->collidingItems(m_board)) { TraceWire * wire = dynamic_cast(item); if (wire != NULL) { if (!wire->getAutoroutable()) continue; if (!wire->isTraceType(m_sketchWidget->getTraceFlag())) continue; m_sketchWidget->setClipEnds(wire, true); wire->update(); addWireToUndo(wire, parentCommand); wires.append(wire); continue; } if (m_pcbType) { JumperItem * jumperItem = dynamic_cast(item); if (jumperItem != NULL) { jumperItems.append(jumperItem); if (!jumperItem->getAutoroutable()) { continue; } jumperItem->saveParams(); QPointF pos, c0, c1; jumperItem->getParams(pos, c0, c1); new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, ModuleIDNames::JumperModuleIDName, jumperItem->viewLayerPlacement(), jumperItem->getViewGeometry(), jumperItem->id(), false, -1, parentCommand); new ResizeJumperItemCommand(m_sketchWidget, jumperItem->id(), pos, c0, c1, pos, c0, c1, parentCommand); new CheckStickyCommand(m_sketchWidget, BaseCommand::SingleView, jumperItem->id(), false, CheckStickyCommand::RemoveOnly, parentCommand); continue; } Via * via = dynamic_cast(item); if (via != NULL) { vias.append(via); if (!via->getAutoroutable()) { continue; } new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, ModuleIDNames::ViaModuleIDName, via->viewLayerPlacement(), via->getViewGeometry(), via->id(), false, -1, parentCommand); new CheckStickyCommand(m_sketchWidget, BaseCommand::SingleView, via->id(), false, CheckStickyCommand::RemoveOnly, parentCommand); new SetPropCommand(m_sketchWidget, via->id(), "hole size", via->holeSize(), via->holeSize(), true, parentCommand); continue; } } else { SymbolPaletteItem * netLabel = dynamic_cast(item); if (netLabel != NULL && netLabel->isOnlyNetLabel()) { netLabels.append(netLabel); if (!netLabel->getAutoroutable()) { continue; } new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, netLabel->moduleID(), netLabel->viewLayerPlacement(), netLabel->getViewGeometry(), netLabel->id(), false, -1, parentCommand); new SetPropCommand(m_sketchWidget, netLabel->id(), "label", netLabel->label(), netLabel->label(), true, parentCommand); continue; } } } foreach (TraceWire * traceWire, wires) { //traceWire->debugInfo("trace"); addUndoConnection(true, traceWire, parentCommand); } foreach (JumperItem * jumperItem, jumperItems) { addUndoConnection(true, jumperItem, parentCommand); } foreach (Via * via, vias) { addUndoConnection(true, via, parentCommand); } foreach (SymbolPaletteItem * netLabel, netLabels) { addUndoConnection(true, netLabel, parentCommand); } } void Autorouter::addWireToUndo(Wire * wire, QUndoCommand * parentCommand) { if (wire == NULL) return; new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, ModuleIDNames::WireModuleIDName, wire->viewLayerPlacement(), wire->getViewGeometry(), wire->id(), false, -1, parentCommand); new CheckStickyCommand(m_sketchWidget, BaseCommand::SingleView, wire->id(), false, CheckStickyCommand::RemoveOnly, parentCommand); new WireWidthChangeCommand(m_sketchWidget, wire->id(), wire->width(), wire->width(), parentCommand); new WireColorChangeCommand(m_sketchWidget, wire->id(), wire->colorString(), wire->colorString(), wire->opacity(), wire->opacity(), parentCommand); } void Autorouter::setMaxCycles(int maxCycles) { m_maxCycles = maxCycles; QSettings settings; settings.setValue(MaxCyclesName, maxCycles); } fritzing-app-0.9.3b/src/autoroute/autorouter.h000066400000000000000000000064741272363601300215150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef AUTOROUTER_H #define AUTOROUTER_H #include #include #include #include #include #include #include #include #include #include "../viewgeometry.h" #include "../viewlayer.h" #include "../connectors/connectoritem.h" #include "../commands.h" class Autorouter : public QObject { Q_OBJECT public: Autorouter(class PCBSketchWidget *); virtual ~Autorouter(void); virtual void start()=0; public: static const QString MaxCyclesName; protected: virtual void cleanUpNets(); virtual void updateRoutingStatus(); virtual class TraceWire * drawOneTrace(QPointF fromPos, QPointF toPos, double width, ViewLayer::ViewLayerPlacement); void initUndo(QUndoCommand * parentCommand); void addUndoConnection(bool connect, class SymbolPaletteItem *, QUndoCommand * parentCommand); void addUndoConnection(bool connect, class JumperItem *, QUndoCommand * parentCommand); void addUndoConnection(bool connect, class Via *, QUndoCommand * parentCommand); void addUndoConnection(bool connect, TraceWire *, QUndoCommand * parentCommand); void addUndoConnection(bool connect, ConnectorItem *, BaseCommand::CrossViewType, QUndoCommand * parentCommand); void restoreOriginalState(QUndoCommand * parentCommand); void doCancel(QUndoCommand * parentCommand); void clearTracesAndJumpers(); void addToUndo(QUndoCommand * parentCommand); void addWireToUndo(Wire * wire, QUndoCommand * parentCommand); public slots: virtual void cancel(); virtual void cancelTrace(); virtual void stopTracing(); virtual void useBest(); virtual void setMaxCycles(int); signals: void setMaximumProgress(int); void setProgressValue(int); void wantTopVisible(); void wantBottomVisible(); void wantBothVisible(); void setProgressMessage(const QString &); void setProgressMessage2(const QString &); void setCycleMessage(const QString &); void setCycleCount(int); void disableButtons(); protected: class PCBSketchWidget * m_sketchWidget; QList< QList* > m_allPartConnectorItems; bool m_cancelled; bool m_cancelTrace; bool m_stopTracing; bool m_useBest; bool m_bothSidesNow; int m_maximumProgressPart; int m_currentProgressPart; QGraphicsItem * m_board; int m_maxCycles; double m_keepoutPixels; QRectF m_maxRect; bool m_pcbType; }; #endif fritzing-app-0.9.3b/src/autoroute/autoroutersettingsdialog.cpp000066400000000000000000000313751272363601300250070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ /////////////////////////////////////// // todo: // save and reload as settings // enable/disable custom on radio presses // change wording on custom via // actually modify the autorouter // enable single vs. double-sided settings /////////////////////////////////////// #include "autoroutersettingsdialog.h" #include #include #include #include #include #include #include #include #include "../items/tracewire.h" #include "../items/via.h" #include "../fsvgrenderer.h" #include "../sketch/pcbsketchwidget.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "drc.h" const QString AutorouterSettingsDialog::AutorouteTraceWidth = "autorouteTraceWidth"; AutorouterSettingsDialog::AutorouterSettingsDialog(QHash & settings, QWidget *parent) : QDialog(parent) { m_traceWidth = settings.value(AutorouteTraceWidth).toInt(); Via::initHoleSettings(m_holeSettings); m_holeSettings.ringThickness = settings.value(Via::AutorouteViaRingThickness); m_holeSettings.holeDiameter = settings.value(Via::AutorouteViaHoleSize); this->setWindowTitle(QObject::tr("Autorouter Settings")); QVBoxLayout * windowLayout = new QVBoxLayout(); this->setLayout(windowLayout); QGroupBox * prodGroupBox = new QGroupBox(tr("Production type"), this); QVBoxLayout * prodLayout = new QVBoxLayout(); prodGroupBox->setLayout(prodLayout); m_homebrewButton = new QRadioButton(tr("homebrew"), this); connect(m_homebrewButton, SIGNAL(clicked(bool)), this, SLOT(production(bool))); m_professionalButton = new QRadioButton(tr("professional"), this); connect(m_professionalButton, SIGNAL(clicked(bool)), this, SLOT(production(bool))); m_customButton = new QRadioButton(tr("custom"), this); connect(m_customButton, SIGNAL(clicked(bool)), this, SLOT(production(bool))); m_customFrame = new QFrame(this); QHBoxLayout * customFrameLayout = new QHBoxLayout(this); m_customFrame->setLayout(customFrameLayout); customFrameLayout->addSpacing(5); QFrame * innerFrame = new QFrame(this); QVBoxLayout * innerFrameLayout = new QVBoxLayout(this); innerFrame->setLayout(innerFrameLayout); QWidget * traceWidget = createTraceWidget(); QWidget * keepoutWidget = createKeepoutWidget(settings.value(DRC::KeepoutSettingName)); QWidget * viaWidget = createViaWidget(); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); innerFrameLayout->addWidget(traceWidget); innerFrameLayout->addWidget(keepoutWidget); innerFrameLayout->addWidget(viaWidget); customFrameLayout->addWidget(innerFrame); prodLayout->addWidget(m_homebrewButton); prodLayout->addWidget(m_professionalButton); prodLayout->addWidget(m_customButton); prodLayout->addWidget(m_customFrame); windowLayout->addWidget(prodGroupBox); windowLayout->addSpacerItem(new QSpacerItem(1, 10, QSizePolicy::Preferred, QSizePolicy::Expanding)); windowLayout->addWidget(buttonBox); enableCustom(initProductionType()); } AutorouterSettingsDialog::~AutorouterSettingsDialog() { } void AutorouterSettingsDialog::production(bool checked) { Q_UNUSED(checked); QString units; if (sender() == m_homebrewButton) { enableCustom(false); changeHoleSize(sender()->property("holesize").toString() + "," + sender()->property("ringthickness").toString()); setTraceWidth(16); setDefaultKeepout(); } else if (sender() == m_professionalButton) { enableCustom(false); changeHoleSize(sender()->property("holesize").toString() + "," + sender()->property("ringthickness").toString()); setTraceWidth(24); setDefaultKeepout(); } else if (sender() == m_customButton) { enableCustom(true); } } void AutorouterSettingsDialog::enableCustom(bool enable) { m_customFrame->setVisible(enable); } bool AutorouterSettingsDialog::initProductionType() { m_homebrewButton->setChecked(false); m_professionalButton->setChecked(false); int custom = 0; QString keepoutString = getKeepoutString(); double mils = TextUtils::convertToInches(keepoutString) * 1000; if (qAbs(mils - DRC::KeepoutDefaultMils) >= 1) { custom++; } double standard = GraphicsUtils::pixels2mils(Wire::STANDARD_TRACE_WIDTH, GraphicsUtils::SVGDPI); if (qAbs(m_traceWidth - standard) >= 1) { custom++; } custom++; // assume the holesize/ringthickness won't match double rt = TextUtils::convertToInches(m_holeSettings.ringThickness); double hs = TextUtils::convertToInches(m_holeSettings.holeDiameter); foreach (QString name, m_holeSettings.holeThing->holeSizeKeys) { // have to loop through all values to set up the two buttons QStringList values = m_holeSettings.holeThing->holeSizes.value(name).split(","); QString ringThickness = values[1]; QString holeSize = values[0]; if (!name.isEmpty() && !ringThickness.isEmpty() && !holeSize.isEmpty()) { QRadioButton * button = NULL; if (name.contains("home", Qt::CaseInsensitive)) button = m_homebrewButton; else if (name.contains("standard", Qt::CaseInsensitive)) button = m_professionalButton; if (button) { button->setProperty("ringthickness", ringThickness); button->setProperty("holesize", holeSize); double krt = TextUtils::convertToInches(ringThickness); double khs = TextUtils::convertToInches(holeSize); if (qAbs(rt - krt) < 0.001 && qAbs(hs - khs) < 0.001) { // holesize/ringthickness match after all if (--custom == 0) { button->setChecked(true); } } } } } m_customButton->setChecked(custom > 0); return custom > 0; } void AutorouterSettingsDialog::widthEntry(const QString & text) { int w = TraceWire::widthEntry(text, sender()); if (w == 0) return; m_traceWidth = w; } void AutorouterSettingsDialog::changeHoleSize(const QString & newSize) { QString s = newSize; PaletteItem::setHoleSize(s, false, m_holeSettings); } void AutorouterSettingsDialog::changeUnits(bool) { QString newVal = PaletteItem::changeUnits(m_holeSettings); } void AutorouterSettingsDialog::changeDiameter() { if (PaletteItem::changeDiameter(m_holeSettings, sender())) { QLineEdit * edit = qobject_cast(sender()); changeHoleSize(edit->text() + m_holeSettings.currentUnits() + "," + m_holeSettings.ringThickness); } } void AutorouterSettingsDialog::changeThickness() { if (PaletteItem::changeThickness(m_holeSettings, sender())) { QLineEdit * edit = qobject_cast(sender()); changeHoleSize(m_holeSettings.holeDiameter + "," + edit->text() + m_holeSettings.currentUnits()); } } void AutorouterSettingsDialog::setTraceWidth(int width) { for (int i = 0; i > m_traceWidthComboBox->count(); i++) { if (m_traceWidthComboBox->itemData(i).toInt() == width) { m_traceWidthComboBox->setCurrentIndex(i); return; } } } QWidget * AutorouterSettingsDialog::createKeepoutWidget(const QString & keepoutString) { QGroupBox * keepoutGroupBox = new QGroupBox(tr("Keepout"), this); QVBoxLayout * vLayout = new QVBoxLayout(); QLabel * label = new QLabel(tr("Keepout is the minimum distance between copper elements on different nets.")); //label->setWordWrap(true); // setting wordwrap here seems to break the layout vLayout->addWidget(label); label = new QLabel(tr("A keepout of 0.01 inch (0.254 mm) is a good default.")); vLayout->addWidget(label); label = new QLabel(tr("Note: the smaller the keepout, the slower the DRC and Autorouter will run.")); vLayout->addWidget(label); QFrame * frame = new QFrame; QHBoxLayout * frameLayout = new QHBoxLayout; m_keepoutSpinBox = new QDoubleSpinBox; m_keepoutSpinBox->setDecimals(4); m_keepoutSpinBox->setLocale(QLocale::C); connect(m_keepoutSpinBox, SIGNAL(valueChanged(double)), this, SLOT(keepoutEntry())); frameLayout->addWidget(m_keepoutSpinBox); m_inRadio = new QRadioButton("in"); frameLayout->addWidget(m_inRadio); connect(m_inRadio, SIGNAL(clicked()), this, SLOT(toInches())); m_mmRadio = new QRadioButton("mm"); frameLayout->addWidget(m_mmRadio); connect(m_mmRadio, SIGNAL(clicked()), this, SLOT(toMM())); m_keepoutMils = TextUtils::convertToInches(keepoutString) * 1000; if (keepoutString.endsWith("mm")) { toMM(); m_mmRadio->setChecked(true); } else { toInches(); m_inRadio->setChecked(true); } frame->setLayout(frameLayout); vLayout->addWidget(frame); keepoutGroupBox->setLayout(vLayout); return keepoutGroupBox; } QWidget * AutorouterSettingsDialog::createTraceWidget() { QGroupBox * traceGroupBox = new QGroupBox(tr("Trace width"), this); QBoxLayout * traceLayout = new QVBoxLayout(); m_traceWidthComboBox = TraceWire::createWidthComboBox(m_traceWidth, traceGroupBox); connect(m_traceWidthComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(widthEntry(const QString &))); traceLayout->addWidget(m_traceWidthComboBox); traceGroupBox->setLayout(traceLayout); return traceGroupBox; } QWidget * AutorouterSettingsDialog::createViaWidget() { QGroupBox * viaGroupBox = new QGroupBox("Via size", this); QVBoxLayout * viaLayout = new QVBoxLayout(); QWidget * viaWidget = Hole::createHoleSettings(viaGroupBox, m_holeSettings, true, "", true); connect(m_holeSettings.sizesComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(changeHoleSize(const QString &))); connect(m_holeSettings.mmRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool))); connect(m_holeSettings.inRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool))); connect(m_holeSettings.diameterEdit, SIGNAL(editingFinished()), this, SLOT(changeDiameter())); connect(m_holeSettings.thicknessEdit, SIGNAL(editingFinished()), this, SLOT(changeThickness())); viaLayout->addWidget(viaWidget); viaGroupBox->setLayout(viaLayout); return viaGroupBox; } void AutorouterSettingsDialog::toInches() { m_keepoutSpinBox->blockSignals(true); m_keepoutSpinBox->setRange(.001, 1); m_keepoutSpinBox->setSingleStep(.001); m_keepoutSpinBox->setValue(m_keepoutMils / 1000); m_keepoutSpinBox->blockSignals(false); } void AutorouterSettingsDialog::toMM() { m_keepoutSpinBox->blockSignals(true); m_keepoutSpinBox->setRange(.001 * 25.4, 1); m_keepoutSpinBox->setSingleStep(.01); m_keepoutSpinBox->setValue(m_keepoutMils * 25.4 / 1000); m_keepoutSpinBox->blockSignals(false); } void AutorouterSettingsDialog::keepoutEntry() { double k = m_keepoutSpinBox->value(); if (m_inRadio->isChecked()) { m_keepoutMils = k * 1000; } else { m_keepoutMils = k * 1000 / 25.4; } } void AutorouterSettingsDialog::setDefaultKeepout() { m_keepoutMils = DRC::KeepoutDefaultMils; double inches = DRC::KeepoutDefaultMils / 1000; if (m_inRadio->isChecked()) { m_keepoutSpinBox->setValue(inches); } else { m_keepoutSpinBox->setValue(inches * 25.4); } } QHash AutorouterSettingsDialog::getSettings() { QHash settings; settings.insert(DRC::KeepoutSettingName, getKeepoutString()); settings.insert(Via::AutorouteViaHoleSize, m_holeSettings.holeDiameter); settings.insert(Via::AutorouteViaRingThickness, m_holeSettings.ringThickness); settings.insert(AutorouteTraceWidth, QString::number(m_traceWidth)); return settings; } QString AutorouterSettingsDialog::getKeepoutString() { double k = m_keepoutSpinBox->value(); if (m_inRadio->isChecked()) { return QString("%1in").arg(k); } else { return QString("%1mm").arg(k); } } fritzing-app-0.9.3b/src/autoroute/autoroutersettingsdialog.h000066400000000000000000000046671272363601300244600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef AUTOROUTERSETTINGSDIALOG_H #define AUTOROUTERSETTINGSDIALOG_H #include #include #include #include #include #include #include "../items/via.h" class AutorouterSettingsDialog : public QDialog { Q_OBJECT public: AutorouterSettingsDialog(QHash & settings, QWidget *parent = 0); ~AutorouterSettingsDialog(); QHash getSettings(); protected slots: void production(bool); void widthEntry(const QString &); void changeUnits(bool); void changeHoleSize(const QString &); void changeDiameter(); void changeThickness(); void toInches(); void toMM(); void keepoutEntry(); protected: void enableCustom(bool enable); bool initProductionType(); void setTraceWidth(int newWidth); QWidget * createViaWidget(); QWidget * createTraceWidget(); QWidget * createKeepoutWidget(const QString & keepoutString); QString getKeepoutString(); void setDefaultKeepout(); protected: QRadioButton * m_homebrewButton; QRadioButton * m_professionalButton; QRadioButton * m_customButton; HoleSettings m_holeSettings; QFrame * m_customFrame; QComboBox * m_traceWidthComboBox; int m_traceWidth; double m_keepoutMils; bool m_inches; QDoubleSpinBox * m_keepoutSpinBox; QRadioButton * m_inRadio; QRadioButton * m_mmRadio; public: static const QString AutorouteTraceWidth; }; #endif // AUTOROUTERSETTINGSDIALOG_H fritzing-app-0.9.3b/src/autoroute/cmrouter/000077500000000000000000000000001272363601300207605ustar00rootroot00000000000000fritzing-app-0.9.3b/src/autoroute/cmrouter/DBcell.cpp000066400000000000000000000274051272363601300226210ustar00rootroot00000000000000/* * DBcell.c -- * * Place and Delete subcells * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #include #include #include "tile.h" int placeCellFunc(Tile *, UserData); int deleteCellFunc(Tile *, UserData); Tile * clipCellTile(Tile * tile, Plane * plane, TileRect * rect); void cellTileMerge(Tile * tile, Plane * plane, int direction); bool ctbListMatch (Tile *tp1, Tile *tp2); struct searchArg { TileRect * rect; Plane * plane; QGraphicsItem * body; Tile::TileType type; }; #define TOPLEFT 10 #define TOPLEFTRIGHT 11 #define TOPBOTTOM 12 #define TOPBOTTOMLEFT 14 #define TOPBOTTOMLEFTRIGHT 15 /* * ---------------------------------------------------------------------------- * * DBPlaceCell -- * * Add a CellUse to the subcell tile plane of a CellDef. * Assumes prior check that the new CellUse is not an exact duplicate * of one already in place. * * Results: * None. * * Side effects: * Modifies the subcell tile plane of the given CellDef. * Resets the plowing delta of the CellUse to 0. Sets the * CellDef's parent pointer to point to the parent def. * * ---------------------------------------------------------------------------- */ void DBPlaceCell (Plane * plane, TileRect * rect, QGraphicsItem * body, Tile::TileType type) /* argument to TiSrArea(), placeCellFunc() */ /* argument to TiSrArea(), placeCellFunc() */ { struct searchArg arg; /* argument to placeCellFunc() */ arg.rect = rect; arg.plane = plane; arg.body = body; arg.type = type; (void) TiSrArea((Tile *) NULL, plane, rect, placeCellFunc, (UserData) &arg); } /* * ---------------------------------------------------------------------------- * DBDeleteCell -- * * Remove a CellUse from the subcell tile plane of a CellDef. * * Results: * None. * * Side effects: * Modifies the subcell tile plane of the CellDef, sets the * parent pointer of the deleted CellUse to NULL. * ---------------------------------------------------------------------------- */ void DBDeleteCell (Plane * plane, TileRect * rect) /* argument to TiSrArea(), deleteCellFunc() */ /* argument to TiSrArea(), deleteCellFunc() */ { struct searchArg arg; /* argument to deleteCellFunc() */ (void) TiSrArea((Tile *) NULL, plane, rect, deleteCellFunc, (UserData) &arg); } /* * ---------------------------------------------------------------------------- * placeCellFunc -- * * Add a new subcell to a tile. * Clip the tile with respect to the subcell's bounding box. * Insert the new CellTileBody into the linked list in ascending order * based on the celluse pointer. * This function is passed to TiSrArea. * * Results: * 0 is always returned. * * Side effects: * Modifies the subcell tile plane of the appropriate CellDef. * Allocates a new CellTileBody. * ---------------------------------------------------------------------------- */ int placeCellFunc (Tile * tile, UserData data) /* target tile */ /* celluse, rect, plane */ { struct searchArg * arg = (struct searchArg *) data; Tile * tp = clipCellTile (tile, arg->plane, arg->rect); TiSetType(tp, arg->type); TiSetBody(tp, arg->body); /* merge tiles back into the the plane */ /* requires that TiSrArea visit tiles in NW to SE wavefront */ if ( RIGHT(tp) == arg->rect->xmaxi) { if (YMIN(tp) == arg->rect->ymini) cellTileMerge (tp, arg->plane, TOPBOTTOMLEFTRIGHT); else cellTileMerge (tp, arg->plane, TOPLEFTRIGHT); } else if (YMIN(tp) == arg->rect->ymini) cellTileMerge (tp, arg->plane, TOPBOTTOMLEFT); else cellTileMerge (tp, arg->plane, TOPLEFT); return 0; } /* * ---------------------------------------------------------------------------- * deleteCellFunc -- * * Remove a subcell from a tile. * This function is passed to TiSrArea. * * Results: * Always returns 0. * * Side effects: * Modifies the subcell tile plane of the appropriate CellDef. * Deallocates a CellTileBody. * ---------------------------------------------------------------------------- */ int deleteCellFunc (Tile * tile, UserData data) { struct searchArg * arg = (struct searchArg *) data; /* merge tiles back into the the plane */ /* requires that TiSrArea visit tiles in NW to SE wavefront */ if ( RIGHT(tile) == arg->rect->xmaxi) { if (YMIN(tile) == arg->rect->ymini) cellTileMerge (tile, arg->plane, TOPBOTTOMLEFTRIGHT); else cellTileMerge (tile, arg->plane, TOPLEFTRIGHT); } else if (YMIN(tile) == arg->rect->ymini) cellTileMerge (tile, arg->plane, TOPBOTTOMLEFT); else cellTileMerge (tile, arg->plane, TOPLEFT); return (0); } /* * ---------------------------------------------------------------------------- * clipCellTile -- * * Clip the given tile against the given rectangle. * * Results: * Returns a pointer to the clipped tile. * * Side effects: * Modifies the database plane that contains the given tile. * ---------------------------------------------------------------------------- */ Tile * clipCellTile (Tile * tile, Plane * plane, TileRect * rect) { Tile * newtile; if (YMAX(tile) > rect->ymaxi) { newtile = TiSplitY (tile, rect->ymaxi); /* no merge */ } if (YMIN(tile) < rect->ymini) { newtile = tile; tile = TiSplitY (tile, rect->ymini); /* no merge */ } if (RIGHT(tile) > rect->xmaxi) { newtile = TiSplitX (tile, rect->xmaxi); cellTileMerge (newtile, plane, TOPBOTTOM); } if (LEFT(tile) < rect->xmini) { newtile = tile; tile = TiSplitX (tile, rect->xmini); cellTileMerge (newtile, plane, TOPBOTTOM); } return (tile); } /* clipCellTile */ /* * ---------------------------------------------------------------------------- * cellTileMerge -- * * Merge the given tile with its plane in the directions specified. * * Results: * None. * * Side effects: * Modifies the database plane that contains the given tile. * ---------------------------------------------------------------------------- */ void cellTileMerge (Tile * tile, Plane * plane, int direction) /* YMAX = 8, YMIN = 4, LEFT = 2, RIGHT = 1 */ { TilePoint topleft, bottomright; Tile * dummy, * tpleft, * tpright, * tp1, * tp2; topleft.xi = LEFT(tile); topleft.yi = YMAX(tile); bottomright.xi = RIGHT(tile); bottomright.yi = YMIN(tile); if ((direction >> 1) % 2) /* LEFT */ { tpright = tile; tpleft = BL(tpright); while (YMIN(tpleft) < topleft.yi) /* go up left edge */ { if (ctbListMatch (tpleft, tpright)) { if (YMIN(tpleft) < YMIN(tpright)) { dummy = tpleft; tpleft = TiSplitY (tpleft, YMIN(tpright)); } else if (YMIN(tpleft) > YMIN(tpright)) { dummy = tpright; tpright = TiSplitY (tpright, YMIN(tpleft)); } if (YMAX(tpleft) > YMAX(tpright)) { dummy = TiSplitY (tpleft, YMAX(tpright)); } else if (YMAX(tpright) > YMAX(tpleft)) { dummy = TiSplitY (tpright, YMAX(tpleft)); } // if (plane->pl_hint == tpright) plane->pl_hint = tpleft; TiJoinX (tpleft, tpright, plane); /* tpright disappears */ tpright = RT(tpleft); if (YMIN(tpright) < topleft.yi) tpleft = BL(tpright); else tpleft = tpright; /* we're off the top of the tile */ /* this will break the while loop */ } /* if (ctbListMatch (tpleft, tpright)) */ else tpleft = RT(tpleft); } /* while */ tile = tpleft; /* for TiSrPoint in next IF statement */ } if (direction % 2) /* RIGHT */ { tpright = TiSrPoint (tile, plane, bottomright.xi, bottomright.yi); tpleft = TiSrPoint (tpright, plane, bottomright.xi - MINDIFF, bottomright.yi); while (YMIN(tpright) < topleft.yi) /* go up right edge */ { if (ctbListMatch (tpleft, tpright)) { if (YMIN(tpright) < YMIN(tpleft)) { dummy = tpright; tpright = TiSplitY (tpright, YMIN(tpleft)); } else if (YMIN(tpleft) < YMIN(tpright)) { dummy = tpleft; tpleft = TiSplitY (tpleft, YMIN(tpright)); } if (YMAX(tpright) > YMAX(tpleft)) { dummy = TiSplitY (tpright, YMAX(tpleft)); } else if (YMAX(tpleft) > YMAX(tpright)) { dummy = TiSplitY (tpleft, YMAX(tpright)); } // if (plane->pl_hint == tpright) plane->pl_hint = tpleft; TiJoinX (tpleft, tpright, plane); /* tpright disappears */ tpright = RT(tpleft); while (LEFT(tpright) > bottomright.xi) tpright = BL(tpright); /* tpleft can be garbage if we're off the top of the loop, */ /* but it doesn't matter since the expression tests tpright */ tpleft = BL(tpright); } /* if (ctbListMatch (tpleft, tpright)) */ else { tpright = RT(tpright); while (LEFT(tpright) > bottomright.xi) tpright = BL(tpright); tpleft = BL(tpright); /* left side merges may have */ /* created more tiles */ } } /* while */ tile = tpright; /* for TiSrPoint in next IF statement */ } if ((direction >> 3) % 2) /* YMAX */ { tp1 = TiSrPoint (tile, plane, topleft.xi, topleft.yi); /* merge across top */ tp2 = TiSrPoint (tile, plane, topleft.xi, topleft.yi - MINDIFF);/* top slice of original tile */ if ((LEFT(tp1) == LEFT(tp2) ) && (RIGHT(tp1) == RIGHT(tp2)) && (ctbListMatch (tp1, tp2) )) { // if (plane->pl_hint == tp2) plane->pl_hint = tp1; TiJoinY (tp1, tp2, plane); } tile = tp1; /* for TiSrPoint in next IF statement */ } if ((direction >> 2) % 2) /* YMIN */ { /* bottom slice of orig tile */ tp1 = TiSrPoint (tile, plane, bottomright.xi - MINDIFF, bottomright.yi); tp2 = TiSrPoint (tile, plane, bottomright.xi - MINDIFF, bottomright.yi - MINDIFF); /* merge across bottom */ if ((LEFT(tp1) == LEFT(tp2) ) && (RIGHT(tp1) == RIGHT(tp2)) && (ctbListMatch (tp1, tp2) )) { // if (plane->pl_hint == tp2) plane->pl_hint = tp1; TiJoinY (tp1, tp2, plane); } } } /* * ---------------------------------------------------------------------------- * ctbListMatch -- * * Compare two linked lists of CellTileBodies, assuming that they are * sorted in ascending order by celluse pointers. * * Results: * True if the tiles have identical lists of CellTileBodies. * * Side effects: * None. * ---------------------------------------------------------------------------- */ bool ctbListMatch (Tile *tp1, Tile *tp2) { return (tp1->ti_body == tp2->ti_body) && (tp1->ti_type == tp2->ti_type); } /* * ---------------------------------------------------------------------------- * TiInsertTile -- * * create a tile with the given rect and insert it into the plane. * * Results: * the new Tile. * * Side effects: * Modifies the database plane that contains the given tile. * ---------------------------------------------------------------------------- */ Tile* TiInsertTile(Plane * plane, TileRect * rect, QGraphicsItem * body, Tile::TileType type) { DBPlaceCell(plane, rect, body, type); return TiSrPoint(NULL, plane, rect->xmini, rect->ymini); } fritzing-app-0.9.3b/src/autoroute/cmrouter/cmrouter.cpp000066400000000000000000000531651272363601300233360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** This router is based on the one described in Contour: A Tile-based Gridless Router http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-95-3.pdf Plus additional ideas from An Efficient Tile-Based ECO Router with Routing Graph Reduction http://www.cis.nctu.edu.tw/~ylli/paper/f69-li.pdf The corner stitching code is a modified version of code from the Magic VLSI Layout Tool http://opencircuitdesign.com/magic/ ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ // TODO: // // run separate beginning overlap check with half keepout width? // // would be nice to eliminate ratsnests as we go // // think of orderings like simulated annealing or genetic algorithms // // option to turn off propagation feedback? // remove debugging output and extra calls to processEvents // // still seeing a few thin tiles going across the board: // this is because the thick tiles above and below are wider than the thin tile // // slide corner: if dogleg is too close to other connectors, slide it more towards the middle // // bugs: // why does the same routing task give different results (qSort?) // especially annoying in schematic view when sometimes wires flow along wires and sometimes don't, for the same routing task // border seems asymmetric // still some funny shaped routes (thin tile problem?) // jumper item: sometimes one end doesn't route // schematic view: some lines still overlap // split_original_wire.fz shouldn't have two jumpers // stepper motor example: not putting jumper item in space to the left of a connector where there is clearly open space // is this due to a blocking thin tile? // some traces violate drc // // catching 1st repeat to end rip-up-and-reroute is not valid // // longer route than expected: // It is possible that the shortest tile route is actually longer than the shortest crow-fly route. // For example, in the following case, route ABC will reach goal before ABDEF: // ------- // | A | // ------------------------------------ // | B | // ------------------------------------ // | | | D | // | | -------------- // | C | | E | // | | ------------------- // | | | F | // ------------------------------------------ // | GOAL | // ------------------------------------------ // There is a way to deal with this in the following paper: http://eie507.eie.polyu.edu.hk/projects/sp-tiles-00980255.pdf // // #include "cmrouter.h" #include "../../sketch/pcbsketchwidget.h" #include "../../debugdialog.h" #include "../../items/virtualwire.h" #include "../../items/tracewire.h" #include "../../items/jumperitem.h" #include "../../items/via.h" #include "../../items/resizableboard.h" #include "../../utils/graphicsutils.h" #include "../../utils/graphutils.h" #include "../../utils/textutils.h" #include "../../connectors/connectoritem.h" #include "../../items/moduleidnames.h" #include "../../processeventblocker.h" #include "../../svg/groundplanegenerator.h" #include "../../svg/svgfilesplitter.h" #include "../../fsvgrenderer.h" #include "tile.h" #include "tileutils.h" #include #include #include #include //#include // forces a dependency on qt 4.7 #include #include static const int MaximumProgress = 1000; static int TileStandardWireWidth = 0; static int TileHalfStandardWireWidth = 0; static double StandardWireWidth = 0; static double HalfStandardWireWidth = 0; static const double CloseEnough = 0.5; static const int GridEntryAlpha = 128; //static qint64 seedNextTime = 0; //static qint64 propagateUnitTime = 0; static const int DefaultMaxCycles = 10; #ifndef QT_NO_DEBUG #define DGI(item) drawGridItem(item) #else #define DGI(item) Q_UNUSED(item) #endif static inline double dot(const QPointF & p1, const QPointF & p2) { return (p1.x() * p2.x()) + (p1.y() * p2.y()); } bool tilePointRectXLessThan(TilePointRect * tpr1, TilePointRect * tpr2) { return tpr1->tilePoint.xi <= tpr2->tilePoint.xi; } bool tilePointRectYGreaterThan(TilePointRect * tpr1, TilePointRect * tpr2) { return tpr1->tilePoint.yi >= tpr2->tilePoint.yi; } /////////////////////////////////////////////// // // tile functions static inline void infoTile(const QString & message, Tile * tile) { if (tile == NULL) { DebugDialog::debug("infoTile: tile is NULL"); return; } DebugDialog::debug(QString("tile:%1 lb:%2 bl:%3 tr:%4 rt%5") .arg((long) tile, 0, 16) .arg((long) tile->ti_lb, 0, 16) .arg((long) tile->ti_bl, 0, 16) .arg((long) tile->ti_tr, 0, 16) .arg((long) tile->ti_rt, 0, 16)); DebugDialog::debug(QString("%1 tile:%2 l:%3 t:%4 w:%5 h:%6 type:%7 body:%8") .arg(message) .arg((long) tile, 0, 16) .arg(LEFT(tile)) .arg(YMIN(tile)) .arg(WIDTH(tile)) .arg(HEIGHT(tile)) .arg(TiGetType(tile)) .arg((long) TiGetBody(tile), 0, 16) ); } static inline void infoTileRect(const QString & message, const TileRect & tileRect) { DebugDialog::debug(QString("%1 l:%2 t:%3 w:%4 h:%5") .arg(message) .arg(tileRect.xmini) .arg(tileRect.ymini) .arg(tileRect.xmaxi - tileRect.xmini) .arg(tileRect.ymaxi - tileRect.ymini) ); } static inline int manhattan(TileRect & tr1, TileRect & tr2) { int dx = qAbs(tr1.xmaxi - tr2.xmaxi); dx = qMin(qAbs(tr1.xmaxi - tr2.xmini), dx); dx = qMin(qAbs(tr1.xmini - tr2.xmaxi), dx); dx = qMin(qAbs(tr1.xmini - tr2.xmini), dx); int dy = qAbs(tr1.ymaxi - tr2.ymaxi); dy = qMin(qAbs(tr1.ymaxi - tr2.ymini), dy); dy = qMin(qAbs(tr1.ymini - tr2.ymaxi), dy); dy = qMin(qAbs(tr1.ymini - tr2.ymini), dy); return dx + dy; } static inline GridEntry * TiGetGridEntry(Tile * tile) { return dynamic_cast(TiGetClient(tile)); } void extendToBounds(TileRect & from, TileRect & to) { // bail if it already extends to or past the bounds if (from.xmini <= to.xmini) return; if (from.xmaxi >= to.xmaxi) return; if (from.ymini <= to.ymini) return; if (from.ymaxi >= to.ymaxi) return; int which = 0; int dmin = from.xmini - to.xmini; if (to.xmaxi - from.xmaxi < dmin) { which = 1; dmin = to.xmaxi - from.xmaxi; } if (from.ymini - to.ymini < dmin) { which = 2; dmin = from.ymini - to.ymini; } if (to.ymaxi - from.ymaxi < dmin) { which = 3; dmin = to.ymaxi - from.ymaxi; } switch(which) { case 0: from.xmini = to.xmini; return; case 1: from.xmaxi = to.xmaxi; return; case 2: from.ymini = to.ymini; return; case 3: from.ymaxi = to.ymaxi; return; default: break; } } //////////////////////////////////////////////////////////////////// // // tile crawling functions int checkAlready(Tile * tile, UserData userData) { switch (TiGetType(tile)) { case Tile::SPACE: case Tile::SPACE2: case Tile::SCHEMATICWIRESPACE: case Tile::BUFFER: return 0; default: break; } QList * tiles = (QList *) userData; tiles->append(tile); return 0; } int prepDeleteTile(Tile * tile, UserData userData) { switch(TiGetType(tile)) { case Tile::DUMMYLEFT: case Tile::DUMMYRIGHT: case Tile::DUMMYTOP: case Tile::DUMMYBOTTOM: return 0; default: break; } //infoTile("prep delete", tile); QSet * tiles = (QSet *) userData; tiles->insert(tile); return 0; } //////////////////////////////////////////////////////////////////// GridEntry::GridEntry(QRectF & r, QGraphicsItem * parent) : QGraphicsRectItem(r, parent) { m_drawn = false; setAcceptedMouseButtons(Qt::NoButton); setAcceptHoverEvents(false); } bool GridEntry::drawn() { return m_drawn; } void GridEntry::setDrawn(bool d) { m_drawn = d; } //////////////////////////////////////////////////////////////////// CMRouter::CMRouter(PCBSketchWidget * sketchWidget, ItemBase * board, bool adjustIf) : QObject() { m_board = board; m_sketchWidget = sketchWidget; m_unionPlane = m_union90Plane = NULL; if (m_board) { m_maxRect = m_board->sceneBoundingRect(); } else { m_maxRect = m_sketchWidget->scene()->itemsBoundingRect(); if (adjustIf) { m_maxRect.adjust(-m_maxRect.width() / 2, -m_maxRect.height() / 2, m_maxRect.width() / 2, m_maxRect.height() / 2); } } QMatrix matrix90; matrix90.rotate(90); m_maxRect90 = matrix90.mapRect(m_maxRect); qrectToTile(m_maxRect, m_tileMaxRect); setUpWidths(m_sketchWidget->getAutorouterTraceWidth()); } CMRouter::~CMRouter() { } Plane * CMRouter::initPlane(bool rotate90) { Tile * bufferTile = TiAlloc(); TiSetType(bufferTile, Tile::BUFFER); TiSetBody(bufferTile, NULL); QRectF bufferRect(rotate90 ? m_maxRect90 : m_maxRect); TileRect br; qrectToTile(bufferRect, br); bufferRect.adjust(-bufferRect.width(), -bufferRect.height(), bufferRect.width(), bufferRect.height()); //DebugDialog::debug("max rect", m_maxRect); //DebugDialog::debug("max rect 90", m_maxRect90); int l = fasterRealToTile(bufferRect.left()); int t = fasterRealToTile(bufferRect.top()); int r = fasterRealToTile(bufferRect.right()); int b = fasterRealToTile(bufferRect.bottom()); SETLEFT(bufferTile, l); SETYMIN(bufferTile, t); // TILE is Math Y-axis not computer-graphic Y-axis Plane * thePlane = TiNewPlane(bufferTile, br.xmini, br.ymini, br.xmaxi, br.ymaxi); SETRIGHT(bufferTile, r); SETYMAX(bufferTile, b); // TILE is Math Y-axis not computer-graphic Y-axis // do not use InsertTile here TiInsertTile(thePlane, &thePlane->maxRect, NULL, Tile::SPACE); //infoTileRect("insert", thePlane->maxRect); return thePlane; } /* void CMRouter::shortenUs(QList & allPoints, JSubedge * subedge) { // TODO: this could be implemented recursively as a child tile space // with the goals being the sides of the U-shape and the obstacles copied in from the parent tile space // for now just look for a straight line int ix = 0; while (ix < allPoints.count() - 3) { QPointF p0 = allPoints.at(ix); QPointF p1 = allPoints.at(ix + 1); QPointF p2 = allPoints.at(ix + 2); QPointF p3 = allPoints.at(ix + 3); ix += 1; TileRect tileRect; if (p1.x() == p2.x()) { if ((p0.x() > p1.x() && p3.x() > p2.x()) || (p0.x() < p1.x() && p3.x() < p2.x())) { // opening to left or right bool targetGreater; if (p0.x() < p1.x()) { // opening left targetGreater = false; realsToTile(tileRect, qMax(p0.x(), p3.x()), qMin(p0.y(), p3.y()), p2.x(), qMax(p0.y(), p3.y())); } else { // opening right targetGreater = true; realsToTile(tileRect, p2.x(), qMin(p0.y(), p3.y()), qMin(p0.x(), p3.x()), qMax(p0.y(), p3.y())); } if (findShortcut(tileRect, true, targetGreater, subedge, allPoints, ix - 1)) { ix--; } } else { // not a U-shape continue; } } else if (p1.y() == p2.y()) { if ((p0.y() > p1.y() && p3.y() > p2.y()) || (p0.y() < p1.y() && p3.y() < p2.y())) { // opening to top or bottom bool targetGreater; if (p0.y() < p1.y()) { // opening top targetGreater = false; realsToTile(tileRect, qMin(p0.x(), p3.x()), qMax(p0.y(), p3.y()), qMax(p0.x(), p3.x()), p2.y()); } else { // opening bottom targetGreater = true; realsToTile(tileRect, qMin(p0.x(), p3.x()), p2.y(), qMax(p0.x(), p3.x()), qMin(p0.y(), p3.y())); } if (findShortcut(tileRect, false, targetGreater, subedge, allPoints, ix - 1)) { ix--; } } else { // not a U-shape continue; } } } } */ GridEntry * CMRouter::drawGridItem(Tile * tile) { return NULL; if (tile == NULL) return NULL; QRectF r; tileToQRect(tile, r); GridEntry * gridEntry = TiGetGridEntry(tile); if (gridEntry == NULL) { gridEntry = new GridEntry(r, NULL); gridEntry->setZValue(m_sketchWidget->getTopZ()); TiSetClient(tile, gridEntry); } else { QRectF br = gridEntry->boundingRect(); if (br != r) { gridEntry->setRect(r); gridEntry->setDrawn(false); } } if (gridEntry->drawn()) return gridEntry; QColor c; switch (TiGetType(tile)) { case Tile::SPACE: c = QColor(255, 255, 0, GridEntryAlpha); break; case Tile::SPACE2: c = QColor(200, 200, 0, GridEntryAlpha); break; case Tile::SOURCE: c = QColor(0, 255, 0, GridEntryAlpha); break; case Tile::DESTINATION: c = QColor(0, 0, 255, GridEntryAlpha); break; case Tile::SCHEMATICWIRESPACE: c = QColor(255, 192, 203, GridEntryAlpha); break; case Tile::OBSTACLE: c = QColor(60, 60, 60, GridEntryAlpha); break; default: c = QColor(255, 0, 0, GridEntryAlpha); break; } gridEntry->setPen(c); gridEntry->setBrush(QBrush(c)); if (gridEntry->scene() == NULL) { m_sketchWidget->scene()->addItem(gridEntry); } gridEntry->show(); gridEntry->setDrawn(true); ProcessEventBlocker::processEvents(); return gridEntry; } Tile * CMRouter::addTile(NonConnectorItem * nci, Tile::TileType type, Plane * thePlane, QList & alreadyTiled, CMRouter::OverlapType overlapType) { QRectF r = nci->attachedTo()->mapRectToScene(nci->rect()); TileRect tileRect; realsToTile(tileRect, r.left() - m_keepoutPixels, r.top() - m_keepoutPixels, r.right() + m_keepoutPixels, r.bottom() + m_keepoutPixels); Tile * tile = insertTile(thePlane, tileRect, alreadyTiled, nci, type, overlapType); DGI(tile); return tile; } void CMRouter::hideTiles() { foreach (QGraphicsItem * item, m_sketchWidget->items()) { GridEntry * gridEntry = dynamic_cast(item); if (gridEntry) gridEntry->setVisible(false); } } void CMRouter::clearPlane(Plane * thePlane) { if (thePlane == NULL) return; QSet tiles; //infoTileRect("clear", thePlane->maxRect); TiSrArea(NULL, thePlane, &thePlane->maxRect, prepDeleteTile, &tiles); foreach (Tile * tile, tiles) { TiFree(tile); } TiFreePlane(thePlane); } void CMRouter::displayBadTiles(QList & alreadyTiled) { //hideTiles(); foreach (Tile * tile, alreadyTiled) { TileRect tileRect; TiToRect(tile, &tileRect); displayBadTileRect(tileRect); } displayBadTileRect(m_overlappingTileRect); } void CMRouter::displayBadTileRect(TileRect & tileRect) { QRectF r; tileRectToQRect(tileRect, r); GridEntry * gridEntry = new GridEntry(r, NULL); gridEntry->setZValue(m_sketchWidget->getTopZ()); QColor c(255, 0, 0, GridEntryAlpha); gridEntry->setPen(c); gridEntry->setBrush(QBrush(c)); m_sketchWidget->scene()->addItem(gridEntry); gridEntry->show(); ProcessEventBlocker::processEvents(); } Tile * CMRouter::insertTile(Plane * thePlane, QRectF & rect, QList & alreadyTiled, QGraphicsItem * item, Tile::TileType tileType, CMRouter::OverlapType overlapType) { TileRect tileRect; qrectToTile(rect, tileRect); return insertTile(thePlane, tileRect, alreadyTiled, item, tileType, overlapType); } Tile * CMRouter::insertTile(Plane * thePlane, TileRect & tileRect, QList &, QGraphicsItem * item, Tile::TileType tileType, CMRouter::OverlapType overlapType) { //infoTileRect("insert tile", tileRect); if (tileRect.xmaxi - tileRect.xmini <= 0 || tileRect.ymaxi - tileRect.ymini <= 0) { DebugDialog::debug("attempting to insert zero width tile"); return NULL; } if (tileRect.xmaxi > thePlane->maxRect.xmaxi) { tileRect.xmaxi = thePlane->maxRect.xmaxi; } if (tileRect.xmini < thePlane->maxRect.xmini) { tileRect.xmini = thePlane->maxRect.xmini; } if (tileRect.ymaxi > thePlane->maxRect.ymaxi) { tileRect.ymaxi = thePlane->maxRect.ymaxi; } if (tileRect.ymini < thePlane->maxRect.ymini) { tileRect.ymini = thePlane->maxRect.ymini; } if (tileRect.xmaxi - tileRect.xmini <= 0 || tileRect.ymaxi - tileRect.ymini <= 0) { return NULL; } bool gotOverlap = false; if (overlapType != CMRouter::IgnoreAllOverlaps) { //TiSrArea(NULL, thePlane, &tileRect, checkAlready, &alreadyTiled); } if (gotOverlap) { m_overlappingTileRect = tileRect; DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!! overlaps not allowed !!!!!!!!!!!!!!!!!!!!!!"); return NULL; } Tile * newTile = TiInsertTile(thePlane, &tileRect, item, tileType); insertUnion(tileRect, item, tileType); DGI(newTile); return newTile; } bool CMRouter::overlapsOnly(QGraphicsItem *, QList & alreadyTiled) { bool doClip = false; for (int i = alreadyTiled.count() - 1; i >= 0; i--) { Tile * intersectingTile = alreadyTiled.at(i); if (dynamic_cast(TiGetBody(intersectingTile)) != NULL || dynamic_cast(TiGetBody(intersectingTile)) != NULL) { doClip = true; continue; } alreadyTiled.removeAt(i); } return doClip; } bool CMRouter::allowEquipotentialOverlaps(QGraphicsItem * item, QList & alreadyTiled) { bool collected = false; QList equipotential; Wire * w = dynamic_cast(item); if (w) { equipotential.append(w->connector0()); } else { ConnectorItem * ci = dynamic_cast(item); equipotential.append(ci); } foreach (Tile * intersectingTile, alreadyTiled) { QGraphicsItem * bodyItem = TiGetBody(intersectingTile); ConnectorItem * ci = dynamic_cast(bodyItem); if (ci != NULL) { if (!collected) { ConnectorItem::collectEqualPotential(equipotential, false, ViewGeometry::NoFlag); collected = true; } if (!equipotential.contains(ci)) { // overlap not allowed //infoTile("intersecting", intersectingTile); return false; } } else { Wire * w = dynamic_cast(bodyItem); if (w == NULL) return false; if (!collected) { ConnectorItem::collectEqualPotential(equipotential, false, ViewGeometry::NoFlag); collected = true; } if (!equipotential.contains(w->connector0())) { // overlap not allowed //infoTile("intersecting", intersectingTile); return false; } } } return true; } void CMRouter::clearGridEntries() { foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) { GridEntry * gridEntry = dynamic_cast(item); if (gridEntry == NULL) continue; delete gridEntry; } } bool CMRouter::insideV(const QPointF & check, const QPointF & vertex) { // form the V from p2 QPointF lv(vertex.x() - 10 + vertex.y() - check.y(), check.y() + 10); QPointF rv(vertex.x() + 10 - vertex.y() + check.y(), check.y() + 10); // the rest of this from: http://www.blackpawn.com/texts/pointinpoly/default.html QPointF v0 = rv - vertex; QPointF v1 = lv - vertex; QPointF v2 = check - vertex; // Compute dot products double dot00 = dot(v0, v0); double dot01 = dot(v0, v1); double dot02 = dot(v0, v2); double dot11 = dot(v1, v1); double dot12 = dot(v1, v2); // Compute barycentric coordinates double invDenom = 1 / (dot00 * dot11 - dot01 * dot01); double u = (dot11 * dot02 - dot01 * dot12) * invDenom; double v = (dot00 * dot12 - dot01 * dot02) * invDenom; // Check if point is in or on triangle return (u >= 0) && (v >= 0) && (u + v <= 1); } void CMRouter::insertUnion(TileRect & tileRect, QGraphicsItem *, Tile::TileType tileType) { if (m_unionPlane == NULL) return; if (tileType == Tile::SPACE) return; if (tileType == Tile::SPACE2) return; TiInsertTile(m_unionPlane, &tileRect, NULL, Tile::OBSTACLE); //infoTileRect("union", tileRect); TileRect tileRect90; tileRotate90(tileRect, tileRect90); if (tileRect90.xmaxi > m_union90Plane->maxRect.xmaxi) { tileRect90.xmaxi = m_union90Plane->maxRect.xmaxi; } if (tileRect90.xmini < m_union90Plane->maxRect.xmini) { tileRect90.xmini = m_union90Plane->maxRect.xmini; } if (tileRect90.ymaxi > m_union90Plane->maxRect.ymaxi) { tileRect90.ymaxi = m_union90Plane->maxRect.ymaxi; } if (tileRect90.ymini < m_union90Plane->maxRect.ymini) { tileRect90.ymini = m_union90Plane->maxRect.ymini; } if (tileRect90.xmaxi - tileRect90.xmini <= 0 || tileRect90.ymaxi - tileRect90.ymini <= 0) { return; } TiInsertTile(m_union90Plane, &tileRect90, NULL, Tile::OBSTACLE); } void CMRouter::drawTileRect(TileRect & tileRect, QColor & color) { QRectF r; tileRectToQRect(tileRect, r); GridEntry * gridEntry = new GridEntry(r, NULL); gridEntry->setZValue(m_sketchWidget->getTopZ()); gridEntry->setPen(color); gridEntry->setBrush(QBrush(color)); m_sketchWidget->scene()->addItem(gridEntry); gridEntry->show(); //ProcessEventBlocker::processEvents(); } TileRect CMRouter::boardRect() { return m_tileMaxRect; } void CMRouter::setUpWidths(double width) { StandardWireWidth = width; TileStandardWireWidth = fasterRealToTile(StandardWireWidth); HalfStandardWireWidth = StandardWireWidth / 2; TileHalfStandardWireWidth = fasterRealToTile(HalfStandardWireWidth); } void CMRouter::setKeepout(double keepout) { m_keepoutPixels = keepout; } void CMRouter::drcClean() { clearGridEntries(); if (m_unionPlane) { clearPlane(m_unionPlane); m_unionPlane = NULL; } if (m_union90Plane) { clearPlane(m_union90Plane); m_union90Plane = NULL; } } fritzing-app-0.9.3b/src/autoroute/cmrouter/cmrouter.h000066400000000000000000000067561272363601300230070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6705 $: $Author: irascibl@gmail.com $: $Date: 2012-12-12 15:36:22 +0100 (Mi, 12. Dez 2012) $ ********************************************************************/ #ifndef JROUTER_H #define JROUTER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../viewgeometry.h" #include "../../viewlayer.h" #include "../autorouter.h" #include "tile.h" struct TilePointRect { TilePoint tilePoint; TileRect tileRect; }; class GridEntry : public QGraphicsRectItem { public: public: GridEntry(QRectF &, QGraphicsItem * parent); bool drawn(); void setDrawn(bool); protected: bool m_drawn; }; //////////////////////////////////// class CMRouter : public QObject { Q_OBJECT public: CMRouter(class PCBSketchWidget *, ItemBase * board, bool adjustIf); ~CMRouter(void); public: enum OverlapType { IgnoreAllOverlaps = 0, ClipAllOverlaps, ReportAllOverlaps, AllowEquipotentialOverlaps }; public: Plane * initPlane(bool rotate90); Tile * insertTile(Plane* thePlane, QRectF &tileRect, QList &alreadyTiled, QGraphicsItem *, Tile::TileType type, CMRouter::OverlapType); TileRect boardRect(); void setKeepout(double); void drcClean(); protected: GridEntry * drawGridItem(Tile * tile); void hideTiles(); void displayBadTiles(QList & alreadyTiled); void displayBadTileRect(TileRect & tileRect); Tile * addTile(class NonConnectorItem * nci, Tile::TileType type, Plane *, QList & alreadyTiled, CMRouter::OverlapType); Tile * insertTile(Plane* thePlane, TileRect &tileRect, QList &alreadyTiled, QGraphicsItem *, Tile::TileType type, CMRouter::OverlapType); void clearGridEntries(); void cleanPoints(QList & allPoints, Plane *); bool insideV(const QPointF & check, const QPointF & vertex); void makeAlignTiles(QMultiHash &, Plane * thePlane); bool overlapsOnly(QGraphicsItem * item, QList & alreadyTiled); void clearPlane(Plane * thePlane); bool allowEquipotentialOverlaps(QGraphicsItem * item, QList & alreadyTiled); void insertUnion(TileRect & tileRect, QGraphicsItem *, Tile::TileType tileType); void drawTileRect(TileRect & tileRect, QColor & color); void setUpWidths(double width); protected: TileRect m_tileMaxRect; QRectF m_maxRect90; TileRect m_overlappingTileRect; QList m_planes; Plane * m_unionPlane; Plane * m_union90Plane; ItemBase * m_board; QRectF m_maxRect; QString m_error; PCBSketchWidget * m_sketchWidget; double m_keepoutPixels; }; #endif fritzing-app-0.9.3b/src/autoroute/cmrouter/search.cpp000066400000000000000000000035151272363601300227350ustar00rootroot00000000000000/* * search.c -- * * Point searching. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #include #include "tile.h" /* * -------------------------------------------------------------------- * * TiSrPoint -- * * Search for a point. * * Results: * A pointer to the tile containing the point. * The bottom and left edge of a tile are considered part of * the tile; the top and right edge are not. * * Side effects: * Updates the hint tile in the supplied plane to point * to the tile found. * * -------------------------------------------------------------------- */ Tile * TiSrPoint(Tile * hintTile, Plane * plane, int x, int y) /* Pointer to tile at which to begin search. * If this is NULL, use the hint tile stored * with the plane instead. */ /* Plane (containing hint tile pointer) */ /* Point for which to search */ { Tile *tp = (hintTile) ? hintTile : plane->pl_hint; TilePoint point; point.xi = x; point.yi = y; plane->pl_hint = tp = gotoPoint(tp, point); return(tp); } fritzing-app-0.9.3b/src/autoroute/cmrouter/search2.cpp000066400000000000000000000160511272363601300230160ustar00rootroot00000000000000/* * search2.c -- * * Area searching. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #include #include "tile.h" int MINDIFF = 1; /* -------------------- Local function headers ------------------------ */ int tiSrAreaEnum(Tile *enumRT, int enumBottom, TileRect *rect, TileCallback, UserData arg); /* * -------------------------------------------------------------------- * * TiSrArea -- * * Find all tiles contained in or incident upon a given area. * Applies the given procedure to all tiles found. The procedure * should be of the following form: * * int * func(tile, cdata) * Tile *tile; * UserData cdata; * { * } * * Func normally should return 0. If it returns 1 then the search * will be aborted. * * THIS PROCEDURE IS OBSOLETE EXCEPT FOR THE SUBCELL PLANE. USE * DBSrPaintArea() IF YOU WANT TO SEARCH FOR PAINT TILES. * * Results: * 0 is returned if the search completed normally. 1 is returned * if it aborted. * * Side effects: * Whatever side effects result from application of the * supplied procedure. * * NOTE: * The procedure called is free to do anything it wishes to tiles * which have already been visited in the area search, but it must * not affect anything about tiles not visited other than possibly * corner stitches to tiles already visited. * * ************************************************************************* * ************************************************************************* * **** **** * **** WARNING **** * **** **** * **** This code is INCREDIBLY sensitive to modification! **** * **** Change it only with the utmost caution, or you'll **** * **** be verrry sorry! **** * **** **** * ************************************************************************* * ************************************************************************* * * -------------------------------------------------------------------- */ int TiSrArea(Tile *hintTile, Plane *plane, TileRect *rect, TileCallback tileCallback, UserData arg) /* Tile at which to begin search, if not NULL. * If this is NULL, use the hint tile supplied * with plane. */ /* Plane in which tiles lie. This is used to * provide a hint tile in case hintTile == NULL. * The hint tile in the plane is updated to be * the last tile visited in the area enumeration. */ /* Area to search */ /* Function to apply at each tile */ /* Additional argument to pass to (*func)() */ { TilePoint here; Tile *tp, *enumTR, *enumTile; int enumRight, enumBottom; /* * We will scan from top to bottom along the left hand edge * of the search area, searching for tiles. Each tile we * find in this search will be enumerated. */ here.xi = rect->xmini; here.yi = rect->ymaxi - MINDIFF; enumTile = hintTile ? hintTile : plane->pl_hint; plane->pl_hint = enumTile = gotoPoint(enumTile, here); while (here.yi >= rect->ymini) { /* * Find the tile (tp) immediately below the one to be * enumerated (enumTile). This must be done before we enumerate * the tile, as the filter function applied to enumerate * it can result in its deallocation or modification in * some other way. */ here.yi = YMIN(enumTile) - MINDIFF; tp = enumTile; plane->pl_hint = tp = gotoPoint(tp, here); enumRight = RIGHT(enumTile); enumBottom = YMIN(enumTile); enumTR = TR(enumTile); if ((*tileCallback)(enumTile, arg)) return 1; /* * If the right boundary of the tile being enumerated is * inside of the search area, recursively enumerate * tiles to its right. */ if (enumRight < rect->xmaxi) if (tiSrAreaEnum(enumTR, enumBottom, rect, tileCallback, arg)) return 1; enumTile = tp; } return 0; } /* * -------------------------------------------------------------------- * * tiSrAreaEnum -- * * Perform the recursive edge search of the tile which has just been * enumerated in an area search. The arguments passed are the RT * corner stitch and bottom coordinate of the tile just enumerated. * * Results: * 0 is returned if the search completed normally, 1 if * it was aborted. * * Side effects: * Attempts to enumerate recursively each tile found in walking * along the right edge of the tile just enumerated. Whatever * side effects occur result from the application of the client's * filter function. * * -------------------------------------------------------------------- */ int tiSrAreaEnum( Tile *enumRT, int enumBottom, TileRect *rect, TileCallback tileCallback, UserData arg) /* TR corner stitch of tile just enumerated */ /* Bottom coordinate of tile just enumerated */ /* Area to search */ /* Function to apply at each tile */ /* Additional argument to pass to (*func)() */ { Tile *tp, *tpLB, *tpTR; int tpRight, tpNextTop, tpBottom, srchBottom; int atBottom = (enumBottom <= rect->ymini); /* * Begin examination of tiles along right edge. * A tile to the right of the one being enumerated is enumerable if: * - its bottom lies at or above that of the tile being enumerated, or, * - the bottom of the tile being enumerated lies at or below the * bottom of the search rectangle. */ if ((srchBottom = enumBottom) < rect->ymini) srchBottom = rect->ymini; for (tp = enumRT, tpNextTop = YMAX(tp); tpNextTop > srchBottom; tp = tpLB) { /* * Since the client's filter function may result in this tile * being deallocated or otherwise modified, we must extract * all the information we will need from the tile before we * apply the filter function. */ tpLB = LB(tp); tpNextTop = YMAX(tpLB); /* Since YMAX(tpLB) comes from tp */ if (YMIN(tp) < rect->ymaxi && (atBottom || YMIN(tp) >= enumBottom)) { /* * We extract more information from the tile, which we will use * after applying the filter function. */ tpRight = RIGHT(tp); tpBottom = YMIN(tp); tpTR = TR(tp); if ((*tileCallback)(tp, arg)) return 1; /* * If the right boundary of the tile being enumerated is * inside of the search area, recursively enumerate * tiles to its right. */ if (tpRight < rect->xmaxi) if (tiSrAreaEnum(tpTR, tpBottom, rect, tileCallback, arg)) return 1; } } return 0; } fritzing-app-0.9.3b/src/autoroute/cmrouter/tile.cpp000066400000000000000000000445011272363601300224250ustar00rootroot00000000000000/* * tile.c -- * * Basic tile manipulation * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #include #include #include #include "tile.h" void dupTileBody(Tile * oldtp, Tile * newtp); /* * Debugging version of TiSetBody() macro in tile.h * Includes sanity check that a tile at "infinity" * is not being set to a type other than space. */ /* void TiSetBody(tp, b) Tile *tp; BodyPointer b; { if (b != (BodyPointer)0 && b != (BodyPointer)(-1)) if (RIGHT(tp) == INFINITY || YMAX(tp) == INFINITY || LEFT(tp) == MINFINITY || YMIN(tp) == MINFINITY) TxError("Error: Tile at infinity set to non-space value %d\n", (int)b); tp->ti_body = b; } */ /* * Rectangle that defines the maximum extent of any plane. * No tile created by the user should ever extend outside of * this area. */ int TINFINITY = (std::numeric_limits::max() >> 2) - 4; int MINFINITY = -TINFINITY; /* * -------------------------------------------------------------------- * * TiNewPlane -- * * Allocate and initialize a new tile plane. * * Results: * A newly allocated Plane with all corner stitches set * appropriately. * * Side effects: * Adjusts the corner stitches of the Tile supplied to * point to the appropriate bounding tile in the newly * created Plane. * * -------------------------------------------------------------------- */ Plane * TiNewPlane(Tile *tile, int minx, int miny, int maxx, int maxy) /* Tile to become initial tile of plane. * May be NULL. */ { Plane *newplane; static Tile *infinityTile = (Tile *) NULL; newplane = new Plane; newplane->pl_top = TiAlloc(); newplane->pl_right = TiAlloc(); newplane->pl_bottom = TiAlloc(); newplane->pl_left = TiAlloc(); newplane->maxRect.xmini = minx; newplane->maxRect.ymini = miny; newplane->maxRect.xmaxi = maxx; newplane->maxRect.ymaxi = maxy; /* * Since the lower left coordinates of the TR and RT * stitches of a tile are used to determine its upper right, * we must give the boundary tiles a meaningful TR and RT. * To make certain that these tiles don't have zero width * or height, we use a dummy tile at (TINFINITY+1,TINFINITY+1). */ if (infinityTile == (Tile *) NULL) { infinityTile = TiAlloc(); SETLEFT(infinityTile, TINFINITY+1); SETYMIN(infinityTile, TINFINITY+1); } if (tile) { SETRT(tile, newplane->pl_top); SETTR(tile, newplane->pl_right); SETLB(tile, newplane->pl_bottom); SETBL(tile, newplane->pl_left); } SETLEFT(newplane->pl_bottom, MINFINITY); SETYMIN(newplane->pl_bottom, MINFINITY); SETRT(newplane->pl_bottom, tile); SETTR(newplane->pl_bottom, newplane->pl_right); SETLB(newplane->pl_bottom, BADTILE); SETBL(newplane->pl_bottom, newplane->pl_left); TiSetBody(newplane->pl_bottom, 0); TiSetType(newplane->pl_bottom, Tile::DUMMYBOTTOM); SETLEFT(newplane->pl_top, MINFINITY); SETYMIN(newplane->pl_top, TINFINITY); SETRT(newplane->pl_top, infinityTile); SETTR(newplane->pl_top, newplane->pl_right); SETLB(newplane->pl_top, tile); SETBL(newplane->pl_top, newplane->pl_left); TiSetBody(newplane->pl_top, 0); TiSetType(newplane->pl_bottom, Tile::DUMMYTOP); SETLEFT(newplane->pl_left, MINFINITY); SETYMIN(newplane->pl_left, MINFINITY); SETRT(newplane->pl_left, newplane->pl_top); SETTR(newplane->pl_left, tile); SETLB(newplane->pl_left, newplane->pl_bottom); SETBL(newplane->pl_left, BADTILE); TiSetBody(newplane->pl_left, 0); TiSetType(newplane->pl_bottom, Tile::DUMMYLEFT); SETLEFT(newplane->pl_right, TINFINITY); SETYMIN(newplane->pl_right, MINFINITY); SETRT(newplane->pl_right, newplane->pl_top); SETTR(newplane->pl_right, infinityTile); SETLB(newplane->pl_right, newplane->pl_bottom); SETBL(newplane->pl_right, tile); TiSetBody(newplane->pl_right, 0); TiSetType(newplane->pl_bottom, Tile::DUMMYRIGHT); newplane->pl_hint = tile; return (newplane); } /* * -------------------------------------------------------------------- * * TiFreePlane -- * * Free the storage associated with a tile plane. * Only the plane itself and its four border tiles are deallocated. * * Results: * None. * * Side effects: * Frees memory. * * -------------------------------------------------------------------- */ void TiFreePlane(Plane *plane) /* Plane to be freed */ { TiFree(plane->pl_left); TiFree(plane->pl_right); TiFree(plane->pl_top); TiFree(plane->pl_bottom); delete plane; } /* * -------------------------------------------------------------------- * * TiToRect -- * * Convert a tile to a rectangle. * * Results: * None. * * Side effects: * Sets *rect to the bounding box for the supplied tile. * * -------------------------------------------------------------------- */ void TiToRect(Tile *tile, TileRect *rect) /* Tile whose bounding box is to be stored in *rect */ /* Pointer to rect to be set to bounding box */ { rect->xmini = LEFT(tile); rect->xmaxi = RIGHT(tile); rect->ymini = YMIN(tile); rect->ymaxi = YMAX(tile); } /* * -------------------------------------------------------------------- * * TiSplitX -- * * Given a tile and an X coordinate, split the tile into two * along a line running vertically through the given coordinate. * * Results: * Returns the new tile resulting from the splitting, which * is the tile occupying the right-hand half of the original * tile. * * Side effects: * Modifies the corner stitches in the database to reflect * the presence of two tiles in place of the original one. * * -------------------------------------------------------------------- */ Tile * TiSplitX(Tile *tile, int x) /* Tile to be split */ /* X coordinate of split */ { Tile *newtile; Tile *tp; //ASSERT(x > LEFT(tile) && x < RIGHT(tile), "TiSplitX"); newtile = TiAlloc(); dupTileBody(tile, newtile); SETLEFT(newtile, x); SETYMIN(newtile, YMIN(tile)); SETBL(newtile, tile); SETTR(newtile, TR(tile)); SETRT(newtile, RT(tile)); /* * Adjust corner stitches along the right edge */ for (tp = TR(tile); BL(tp) == tile; tp = LB(tp)) SETBL(tp, newtile); SETTR(tile, newtile); /* * Adjust corner stitches along the top edge */ for (tp = RT(tile); LEFT(tp) >= x; tp = BL(tp)) SETLB(tp, newtile); SETRT(tile, tp); /* * Adjust corner stitches along the bottom edge */ for (tp = LB(tile); RIGHT(tp) <= x; tp = TR(tp)) /* nothing */; SETLB(newtile, tp); while (RT(tp) == tile) { SETRT(tp, newtile); tp = TR(tp); } return (newtile); } /* * -------------------------------------------------------------------- * * TiSplitY -- * * Given a tile and a Y coordinate, split the tile into two * along a horizontal line running through the given coordinate. * * Results: * Returns the new tile resulting from the splitting, which * is the tile occupying the top half of the original * tile. * * Side effects: * Modifies the corner stitches in the database to reflect * the presence of two tiles in place of the original one. * * -------------------------------------------------------------------- */ Tile * TiSplitY(Tile *tile, int y) /* Tile to be split */ /* Y coordinate of split */ { Tile *newtile; Tile *tp; //ASSERT(y > YMIN(tile) && y < YMAX(tile), "TiSplitY"); newtile = TiAlloc(); dupTileBody(tile, newtile); SETLEFT(newtile, LEFT(tile)); SETYMIN(newtile, y); SETLB(newtile, tile); SETRT(newtile, RT(tile)); SETTR(newtile, TR(tile)); /* * Adjust corner stitches along top edge */ for (tp = RT(tile); LB(tp) == tile; tp = BL(tp)) SETLB(tp, newtile); SETRT(tile, newtile); /* * Adjust corner stitches along right edge */ for (tp = TR(tile); YMIN(tp) >= y; tp = LB(tp)) SETBL(tp, newtile); SETTR(tile, tp); /* * Adjust corner stitches along left edge */ for (tp = BL(tile); YMAX(tp) <= y; tp = RT(tp)) /* nothing */; SETBL(newtile, tp); while (TR(tp) == tile) { SETTR(tp, newtile); tp = RT(tp); } return (newtile); } /* * -------------------------------------------------------------------- * * TiSplitX_Left -- * * Given a tile and an X coordinate, split the tile into two * along a line running vertically through the given coordinate. * Intended for use when plowing to the left. * * Results: * Returns the new tile resulting from the splitting, which * is the tile occupying the left-hand half of the original * tile. * * Side effects: * Modifies the corner stitches in the database to reflect * the presence of two tiles in place of the original one. * * -------------------------------------------------------------------- */ Tile * TiSplitX_Left(Tile *tile, int x) /* Tile to be split */ /* X coordinate of split */ { Tile *newtile; Tile *tp; //ASSERT(x > LEFT(tile) && x < RIGHT(tile), "TiSplitX"); newtile = TiAlloc(); dupTileBody(tile, newtile); SETLEFT(newtile, LEFT(tile)); SETLEFT(tile, x); SETYMIN(newtile, YMIN(tile)); SETBL(newtile, BL(tile)); SETLB(newtile, LB(tile)); SETTR(newtile, tile); SETBL(tile, newtile); /* Adjust corner stitches along the left edge */ for (tp = BL(newtile); TR(tp) == tile; tp = RT(tp)) SETTR(tp, newtile); /* Adjust corner stitches along the top edge */ for (tp = RT(tile); LEFT(tp) >= x; tp = BL(tp)) /* nothing */; SETRT(newtile, tp); for ( ; LB(tp) == tile; tp = BL(tp)) SETLB(tp, newtile); /* Adjust corner stitches along the bottom edge */ for (tp = LB(tile); RIGHT(tp) <= x; tp = TR(tp)) SETRT(tp, newtile); SETLB(tile, tp); return (newtile); } /* * -------------------------------------------------------------------- * * TiSplitY_Bottom -- * * Given a tile and a Y coordinate, split the tile into two * along a horizontal line running through the given coordinate. * Used when plowing down. * * Results: * Returns the new tile resulting from the splitting, which * is the tile occupying the bottom half of the original * tile. * * Side effects: * Modifies the corner stitches in the database to reflect * the presence of two tiles in place of the original one. * * -------------------------------------------------------------------- */ Tile * TiSplitY_Bottom(Tile *tile, int y) /* Tile to be split */ /* Y coordinate of split */ { Tile *newtile; Tile *tp; //ASSERT(y > YMIN(tile) && y < YMAX(tile), "TiSplitY"); newtile = TiAlloc(); dupTileBody(tile, newtile); SETLEFT(newtile, LEFT(tile)); SETYMIN(newtile, YMIN(tile)); SETYMIN(tile, y); SETRT(newtile, tile); SETLB(newtile, LB(tile)); SETBL(newtile, BL(tile)); SETLB(tile, newtile); /* Adjust corner stitches along bottom edge */ for (tp = LB(newtile); RT(tp) == tile; tp = TR(tp)) SETRT(tp, newtile); /* Adjust corner stitches along right edge */ for (tp = TR(tile); YMIN(tp) >= y; tp = LB(tp)) /* nothing */; SETTR(newtile, tp); for ( ; BL(tp) == tile; tp = LB(tp)) SETBL(tp, newtile); /* Adjust corner stitches along left edge */ for (tp = BL(tile); YMAX(tp) <= y; tp = RT(tp)) SETTR(tp, newtile); SETBL(tile, tp); return (newtile); } /* * -------------------------------------------------------------------- * * TiJoinX -- * * Given two tiles sharing an entire common vertical edge, replace * them with a single tile occupying the union of their areas. * * Results: * None. * * Side effects: * The first tile is simply relinked to reflect its new size. * The second tile is deallocated. Corner stitches in the * neighboring tiles are updated to reflect the new structure. * If the hint tile pointer in the supplied plane pointed to * the second tile, it is adjusted to point instead to the * first. * * -------------------------------------------------------------------- */ void TiJoinX(Tile *tile1, Tile *tile2, Plane *plane) /* First tile, remains allocated after call */ /* Second tile, deallocated by call */ /* Plane in which hint tile is updated */ { Tile *tp; /* * Basic algorithm: * * Update all the corner stitches in the neighbors of tile2 * to point to tile1. * Update the corner stitches of tile1 along the shared edge * to be those of tile2. * Change the bottom or left coordinate of tile1 if appropriate. * Deallocate tile2. */ //ASSERT(YMIN(tile1)==YMIN(tile2) && YMAX(tile1)==YMAX(tile2), "TiJoinX"); //ASSERT(LEFT(tile1)==RIGHT(tile2) || RIGHT(tile1)==LEFT(tile2), "TiJoinX"); /* * Update stitches along top of tile */ for (tp = RT(tile2); LB(tp) == tile2; tp = BL(tp)) SETLB(tp, tile1); /* * Update stitches along bottom of tile */ for (tp = LB(tile2); RT(tp) == tile2; tp = TR(tp)) SETRT(tp, tile1); /* * Update stitches along either left or right, depending * on relative position of the two tiles. */ //ASSERT(LEFT(tile1) != LEFT(tile2), "TiJoinX"); if (LEFT(tile1) < LEFT(tile2)) { for (tp = TR(tile2); BL(tp) == tile2; tp = LB(tp)) SETBL(tp, tile1); SETTR(tile1, TR(tile2)); SETRT(tile1, RT(tile2)); } else { for (tp = BL(tile2); TR(tp) == tile2; tp = RT(tp)) SETTR(tp, tile1); SETBL(tile1, BL(tile2)); SETLB(tile1, LB(tile2)); SETLEFT(tile1, LEFT(tile2)); } if (plane->pl_hint == tile2) plane->pl_hint = tile1; TiFree(tile2); } /* * -------------------------------------------------------------------- * * TiJoinY -- * * Given two tiles sharing an entire common horizontal edge, replace * them with a single tile occupying the union of their areas. * * Results: * None. * * Side effects: * The first tile is simply relinked to reflect its new size. * The second tile is deallocated. Corner stitches in the * neighboring tiles are updated to reflect the new structure. * If the hint tile pointer in the supplied plane pointed to * the second tile, it is adjusted to point instead to the * first. * * -------------------------------------------------------------------- */ void TiJoinY(Tile *tile1, Tile *tile2, Plane *plane) /* First tile, remains allocated after call */ /* Second tile, deallocated by call */ /* Plane in which hint tile is updated */ { if (tile1 == tile2) { return; } Tile *tp; /* * Basic algorithm: * * Update all the corner stitches in the neighbors of tile2 * to point to tile1. * Update the corner stitches of tile1 along the shared edge * to be those of tile2. * Change the bottom or left coordinate of tile1 if appropriate. * Deallocate tile2. */ //ASSERT(LEFT(tile1)==LEFT(tile2) && RIGHT(tile1)==RIGHT(tile2), "TiJoinY"); //ASSERT(YMAX(tile1)==YMIN(tile2) || YMIN(tile1)==YMAX(tile2), "TiJoinY"); /* * Update stitches along right of tile. */ for (tp = TR(tile2); BL(tp) == tile2; tp = LB(tp)) SETBL(tp, tile1); /* * Update stitches along left of tile. */ for (tp = BL(tile2); TR(tp) == tile2; tp = RT(tp)) SETTR(tp, tile1); /* * Update stitches along either top or bottom, depending * on relative position of the two tiles. */ //ASSERT(YMIN(tile1) != YMIN(tile2), "TiJoinY"); if (YMIN(tile1) < YMIN(tile2)) { for (tp = RT(tile2); LB(tp) == tile2; tp = BL(tp)) SETLB(tp, tile1); SETRT(tile1, RT(tile2)); SETTR(tile1, TR(tile2)); } else { for (tp = LB(tile2); RT(tp) == tile2; tp = TR(tp)) SETRT(tp, tile1); SETLB(tile1, LB(tile2)); SETBL(tile1, BL(tile2)); SETYMIN(tile1, YMIN(tile2)); } if (plane->pl_hint == tile2) plane->pl_hint = tile1; TiFree(tile2); } /* * -------------------------------------------------------------------- * * TiAlloc --- * * Memory allocation for tiles * * Results: * Pointer to an initialized memory location for a tile. * * -------------------------------------------------------------------- */ Tile * TiAlloc() { Tile *newtile; newtile = new Tile; TiSetClient(newtile, 0); TiSetBody(newtile, 0); TiSetType(newtile, Tile::NOTYPE); //qDebug() << "alloc" << (long) newtile; SETLB(newtile, 0); SETBL(newtile, 0); SETRT(newtile, 0); SETTR(newtile, 0); return (newtile); } /* * -------------------------------------------------------------------- * * TiFree --- * * Release memory allocation for tiles * * Results: * None. * * -------------------------------------------------------------------- */ void TiFree(Tile *tp) { delete tp; } Tile* gotoPoint(Tile * tp, TilePoint p) { if (p.yi < YMIN(tp)) do tp = LB(tp); while (p.yi < YMIN(tp)); else while (p.yi >= YMAX(tp)) tp = RT(tp); if (p.xi < LEFT(tp)) do { do tp = BL(tp); while (p.xi < LEFT(tp)); if (p.yi < YMAX(tp)) break; do tp = RT(tp); while (p.yi >= YMAX(tp)); } while (p.xi < LEFT(tp)); else while (p.xi >= RIGHT(tp)) { do tp = TR(tp); while (p.xi >= RIGHT(tp)); if (p.yi >= YMIN(tp)) break; do tp = LB(tp); while (p.yi < YMIN(tp)); } return tp; } /* * ---------------------------------------------------------------------------- * dupTileBody -- * * Duplicate the body of an old tile as the body for a new tile. * * Results: * None. * * Side effects: * Allocates new CellTileBodies unless the old tile was a space tile. * ---------------------------------------------------------------------------- */ void dupTileBody (Tile * oldtp, Tile * newtp) { TiSetBody(newtp, TiGetBody(oldtp)); TiSetType(newtp, TiGetType(oldtp)); } fritzing-app-0.9.3b/src/autoroute/cmrouter/tile.h000066400000000000000000000212451272363601300220720ustar00rootroot00000000000000/* * tile.h -- * * Definitions of the basic tile structures * The definitions in this file are all that is visible to * the Ti (tile) modules. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* * * rcsid "$Header: /usr/cvsroot/magic-7.5/tiles/tile.h,v 1.7 2010/05/03 15:16:57 tim Exp $" */ #ifndef _TILES_H #define _TILES_H #include #include // note: Tile uses math axes as opposed to computer graphic axes. In other words y increases up. struct TileRect { int xmini; int ymini; int xmaxi; int ymaxi; }; struct TilePoint { int xi; int yi; }; /* * A tile is the basic unit used for representing both space and * solid area in a plane. It has the following structure: * * RT * ^ * | * ------------------------- * | | ---> TR * | | * | | * | (lower left) | * BL <--- ------------------------- * | * v * LB * * The (x, y) coordinates of the lower left corner of the tile are stored, * along with four "corner stitches": RT, TR, BL, LB. * * Space tiles are distinguished at a higher level by having a distinguished * tile body. */ typedef void * UserData; struct Tile { enum TileType { NOTYPE = 0, BUFFER = 1, SPACE, SPACE2, SCHEMATICWIRESPACE, SOURCE, DESTINATION, OBSTACLE, DUMMYLEFT = 999999, DUMMYTOP, DUMMYRIGHT, DUMMYBOTTOM }; struct Tile *ti_lb; /* Left bottom corner stitch */ struct Tile *ti_bl; /* Bottom left corner stitch */ struct Tile *ti_tr; /* Top right corner stitch */ struct Tile *ti_rt; /* Right top corner stitch */ TilePoint ti_ll; /* Lower left coordinate */ TileType ti_type; /* another free field */ QGraphicsItem * ti_body; /* Body of tile */ QGraphicsItem * ti_client; /* This space for hire. */ }; /* * The following macros make it appear as though both * the lower left and upper right coordinates of a tile * are stored inside it. */ inline Tile* LB(Tile* tileP) { return tileP->ti_lb; } inline Tile* BL(Tile* tileP) { return tileP->ti_bl; } inline Tile* TR(Tile* tileP) { return tileP->ti_tr; } inline Tile* RT(Tile* tileP) { return tileP->ti_rt; } inline void SETLB(Tile* tileP, Tile * val) { tileP->ti_lb = val; if (val == tileP) { int a = 0; a = a + 1; return; }} inline void SETBL(Tile* tileP, Tile * val) { tileP->ti_bl = val; if (val == tileP) { int a = 0; a = a + 1; return; }} inline void SETTR(Tile* tileP, Tile * val) { tileP->ti_tr = val; if (val == tileP) { int a = 0; a = a + 1; return; }} inline void SETRT(Tile* tileP, Tile * val) { tileP->ti_rt = val; if (val == tileP) { int a = 0; a = a + 1; return; }} inline int YMIN(Tile* tileP) { return tileP->ti_ll.yi; } inline int LEFT(Tile* tileP) { return tileP->ti_ll.xi; } inline int YMAX(Tile* tileP) { return YMIN(RT(tileP)); } inline int RIGHT(Tile* tileP) { return LEFT(TR(tileP)); } inline int WIDTH(Tile* tileP) { return RIGHT(tileP) - LEFT(tileP); } inline int HEIGHT(Tile* tileP) { return YMAX(tileP) - YMIN(tileP); } inline void SETYMIN(Tile* tileP, int val) { tileP->ti_ll.yi = val; } inline void SETLEFT(Tile* tileP, int val) { tileP->ti_ll.xi = val; } inline void SETYMAX(Tile* tileP, int val) { SETYMIN(RT(tileP), val); } inline void SETRIGHT(Tile* tileP, int val) { SETLEFT(TR(tileP), val); } typedef void (*TileFunc)(Tile *); /* ----------------------- Tile planes -------------------------------- */ /* * A plane of tiles consists of the four special tiles needed to * surround all internal tiles on all sides. Logically, these * tiles appear as below, except for the fact that all are located * off at infinity. * * -------------------------------------- * |\ /| * | \ / | * | \ TOP / | * | \ / | * | \ / | * | -------------------------- | * | | | | * |LEFT | |RIGHT| * | | | | * | -------------------------- | * | / \ | * | / \ | * | / BOTTOM \ | * | / \ | * |/ \| * -------------------------------------- */ struct Plane { Tile *pl_left; /* Left pseudo-tile */ Tile *pl_top; /* Top pseudo-tile */ Tile *pl_right; /* Right pseudo-tile */ Tile *pl_bottom; /* Bottom pseudo-tile */ Tile *pl_hint; /* Pointer to a "hint" at which to * begin searching. */ TileRect maxRect; }; /* * The following coordinate, TINFINITY, is used to represent a * tile location outside of the tile plane. * * It must be possible to represent TINFINITY+1 as well as * INFINITY. * * Also, because locations involving TINFINITY may be transformed, * it is desirable that additions and subtractions of small integers * from either TINFINITY or MINFINITY not cause overflow. * * Consequently, we define TINFINITY to be the largest integer * representable in wordsize - 2 bits. */ extern int TINFINITY; extern int MINFINITY; extern int MINDIFF; typedef int (*TileCallback)(Tile *, UserData); /* ------------------------ Flags, etc -------------------------------- */ #define BADTILE ((Tile *) 0) /* Invalid tile pointer */ /* ============== Function headers and external interface ============= */ /* * The following macros and procedures should be all that are * ever needed by modules other than the tile module. */ Plane *TiNewPlane(Tile *tile, int minx, int miny, int maxx, int maxy); void TiFreePlane(Plane *plane); void TiToRect(Tile *tile, TileRect *rect); Tile *TiSplitX(Tile *tile, int x); Tile *TiSplitY(Tile *tile, int y); Tile *TiSplitX_Left(Tile *tile, int x); Tile *TiSplitY_Bottom(Tile *tile, int y); void TiJoinX(Tile *tile1, Tile *tile2, Plane *plane); void TiJoinY(Tile *tile1, Tile *tile2, Plane *plane); int TiSrArea(Tile *hintTile, Plane *plane, TileRect *rect, TileCallback, UserData arg); Tile *TiSrPoint( Tile * hintTile, Plane * plane, int x, int y); Tile* TiInsertTile(Plane *, TileRect * rect, QGraphicsItem * body, Tile::TileType type); #define TiBottom(tileP) (YMIN(tileP)) #define TiLeft(tileP) (LEFT(tileP)) #define TiTop(tileP) (YMAX(tileP)) #define TiRight(tileP) (RIGHT(tileP)) inline Tile::TileType TiGetType(Tile * tileP) { return tileP->ti_type; } inline void TiSetType(Tile *tileP, Tile::TileType t) { tileP->ti_type = t; } inline QGraphicsItem * TiGetBody(Tile * tileP) { return tileP->ti_body; } /* See diagnostic subroutine version in tile.c */ inline void TiSetBody(Tile *tileP, QGraphicsItem *b) { tileP->ti_body = b; } inline QGraphicsItem * TiGetClient(Tile * tileP) { return tileP->ti_client ; } inline void TiSetClient(Tile *tileP, QGraphicsItem * b) { tileP->ti_client = b; } Tile *TiAlloc(); void TiFree(Tile *); /* #define EnclosePoint(tile,point) ((LEFT(tile) <= (point)->p_x ) && \ ((point)->p_x < RIGHT(tile)) && \ (YMIN(tile) <= (point)->p_y ) && \ ((point)->p_y < YMAX(tile) )) #define EnclosePoint4Sides(tile,point) ((LEFT(tile) <= (point)->p_x ) && \ ((point)->p_x <= RIGHT(tile)) && \ (YMIN(tile) <= (point)->p_y ) && \ ((point)->p_y <= YMAX(tile) )) */ /* The four macros below are for finding next tile RIGHT, UP, LEFT or DOWN * from current tile at a given coordinate value. * * For example, NEXT_TILE_RIGHT points tResult to tile to right of t * at y-coordinate y. */ #define NEXT_TILE_RIGHT(tResult, t, y) \ for ((tResult) = TR(t); YMIN(tResult) > (y); (tResult) = LB(tResult)) \ /* Nothing */; #define NEXT_TILE_UP(tResult, t, x) \ for ((tResult) = RT(t); LEFT(tResult) > (x); (tResult) = BL(tResult)) \ /* Nothing */; #define NEXT_TILE_LEFT(tResult, t, y) \ for ((tResult) = BL(t); YMAX(tResult) <= (y); (tResult) = RT(tResult)) \ /* Nothing */; #define NEXT_TILE_DOWN(tResult, t, x) \ for ((tResult) = LB(t); RIGHT(tResult) <= (x); (tResult) = TR(tResult)) \ /* Nothing */; //#define TiSrPointNoHint(plane, point) (TiSrPoint((Tile *) NULL, plane, point)) Tile * gotoPoint(Tile * tile, TilePoint p); #endif /* _TILES_H */ fritzing-app-0.9.3b/src/autoroute/cmrouter/tileutils.h000066400000000000000000000054631272363601300231570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 2979 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef TILEUTILS #define TILEUTILS #include "tile.h" static const int TILEFACTOR = 1000; inline int fasterRealToTile(double x) { return qRound(x * TILEFACTOR); } inline void realsToTile(TileRect & tileRect, double l, double t, double r, double b) { tileRect.xmini = fasterRealToTile(l); tileRect.ymini = fasterRealToTile(t); tileRect.xmaxi = fasterRealToTile(r); tileRect.ymaxi = fasterRealToTile(b); } inline void qrectToTile(QRectF & rect, TileRect & tileRect) { realsToTile(tileRect, rect.left(), rect.top(), rect.right(), rect.bottom()); } inline int realToTile(double x) { return qRound(x * TILEFACTOR); } inline double tileToReal(int x) { return x / ((double) TILEFACTOR); } inline void tileRectToQRect(TileRect & tileRect, QRectF & rect) { rect.setCoords(tileToReal(tileRect.xmini), tileToReal(tileRect.ymini), tileToReal(tileRect.xmaxi), tileToReal(tileRect.ymaxi)); } inline void tileToQRect(Tile * tile, QRectF & rect) { TileRect tileRect; TiToRect(tile, &tileRect); tileRectToQRect(tileRect, rect); } inline void tileRotate90(TileRect & tileRect, TileRect & tileRect90) { // x' = x*cos - y*sin // y' = x*sin + y*cos // where cos90 = 0 and sin90 = 1 (effectively clockwise) // rotate top right corner of rect tileRect90.xmini = -tileRect.ymaxi; tileRect90.ymini = tileRect.xmini; // swap width and height tileRect90.xmaxi = tileRect90.xmini + (tileRect.ymaxi - tileRect.ymini); tileRect90.ymaxi = tileRect90.ymini + (tileRect.xmaxi - tileRect.xmini); } inline void tileUnrotate90(TileRect & tileRect90, TileRect & tileRect) { tileRect.xmini = tileRect90.ymini; tileRect.ymaxi = -tileRect90.xmini; // swap width and height tileRect.xmaxi = tileRect.xmini + (tileRect90.ymaxi - tileRect90.ymini); tileRect.ymini = tileRect.ymaxi - (tileRect90.xmaxi - tileRect90.xmini); } #endif fritzing-app-0.9.3b/src/autoroute/drc.cpp000066400000000000000000001221611272363601300203770ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.a Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6991 $: $Author: irascibl@gmail.com $: $Date: 2013-04-26 15:24:41 +0200 (Fr, 26. Apr 2013) $ ********************************************************************/ #include "drc.h" #include "../connectors/svgidlayer.h" #include "../sketch/pcbsketchwidget.h" #include "../debugdialog.h" #include "../items/virtualwire.h" #include "../items/tracewire.h" #include "../items/via.h" #include "../utils/graphicsutils.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../connectors/connectoritem.h" #include "../items/moduleidnames.h" #include "../processeventblocker.h" #include "../fsvgrenderer.h" #include "../viewlayer.h" #include "../processeventblocker.h" #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////// // // // deal with invisible connectors // // if a part is already overlapping, leave it out of future checking? // // /////////////////////////////////////////// const QString DRC::AlsoNet = "alsoNet"; const QString DRC::NotNet = "notnet"; const QString DRC::Net = "net"; const uchar DRC::BitTable[] = { 128, 64, 32, 16, 8, 4, 2, 1 }; bool pixelsCollide(QImage * image1, QImage * image2, QImage * image3, int x1, int y1, int x2, int y2, uint clr, QList & points) { bool result = false; const uchar * bits1 = image1->constScanLine(0); const uchar * bits2 = image2->constScanLine(0); int bytesPerLine = image1->bytesPerLine(); for (int y = y1; y < y2; y++) { int offset = y * bytesPerLine; for (int x = x1; x < x2; x++) { //QRgb p1 = image1->pixel(x, y); //if (p1 == 0xffffffff) continue; //QRgb p2 = image2->pixel(x, y); //if (p2 == 0xffffffff) continue; int byteOffset = (x >> 3) + offset; uchar mask = DRC::BitTable[x & 7]; if (*(bits1 + byteOffset) & mask) continue; if (*(bits2 + byteOffset) & mask) continue; image3->setPixel(x, y, clr); //DebugDialog::debug(QString("p1:%1 p2:%2").arg(p1, 0, 16).arg(p2, 0, 16)); result = true; if (points.count() < 1000) { points.append(QPointF(x, y)); } } } return result; } QStringList getNames(CollidingThing * collidingThing) { QStringList names; QList itemBases; if (collidingThing->nonConnectorItem) { itemBases << collidingThing->nonConnectorItem->attachedTo(); } foreach (ItemBase * itemBase, itemBases) { if (itemBase) { itemBase = itemBase->layerKinChief(); QString name = QObject::tr("Part %1 '%2'").arg(itemBase->title()) .arg(itemBase->instanceTitle()); names << name; } } return names; } void allGs(QDomElement & element) { element.setTagName("g"); QDomElement child = element.firstChildElement(); while (!child.isNull()) { allGs(child); child = child.nextSiblingElement(); } } /////////////////////////////////////////////// DRCResultsDialog::DRCResultsDialog(const QString & message, const QStringList & messages, const QList & collidingThings, QGraphicsPixmapItem * displayItem, QImage * displayImage, PCBSketchWidget * sketchWidget, QWidget *parent) : QDialog(parent) { setAttribute(Qt::WA_DeleteOnClose, true); m_messages = messages; m_sketchWidget = sketchWidget; m_displayItem = displayItem; if (m_displayItem) { m_displayItem->setFlags(0); } m_displayImage = displayImage; m_collidingThings = collidingThings; this->setWindowTitle(tr("DRC Results")); QVBoxLayout * vLayout = new QVBoxLayout(this); QLabel * label = new QLabel(message); label->setWordWrap(true); vLayout->addWidget(label); label = new QLabel(tr("Click on an item in the list to highlight of overlap it refers to.")); label->setWordWrap(true); vLayout->addWidget(label); label = new QLabel(tr("Note: the list items and the red highlighting will not update as you edit your sketch--you must rerun the DRC. The highlighting will disappear when you close this dialog.")); label->setWordWrap(true); vLayout->addWidget(label); QListWidget *listWidget = new QListWidget(); for (int ix = 0; ix < messages.count(); ix++) { QListWidgetItem * item = new QListWidgetItem(messages.at(ix)); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); item->setData(Qt::UserRole, ix); listWidget->addItem(item); } vLayout->addWidget(listWidget); connect(listWidget, SIGNAL(itemPressed(QListWidgetItem *)), this, SLOT(pressedSlot(QListWidgetItem *))); connect(listWidget, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(releasedSlot(QListWidgetItem *))); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); connect(buttonBox, SIGNAL(accepted()), this, SLOT(close())); vLayout->addWidget(buttonBox); this->setLayout(vLayout); } DRCResultsDialog::~DRCResultsDialog() { if (m_displayItem != NULL && m_sketchWidget != NULL) { delete m_displayItem; } if (m_displayImage != NULL) { delete m_displayImage; } foreach (CollidingThing * collidingThing, m_collidingThings) { delete collidingThing; } m_collidingThings.clear(); } void DRCResultsDialog::pressedSlot(QListWidgetItem * item) { if (item == NULL) return; int ix = item->data(Qt::UserRole).toInt(); CollidingThing * collidingThing = m_collidingThings.at(ix); foreach (QPointF p, collidingThing->atPixels) { m_displayImage->setPixel(p.x(), p.y(), 2 /* 0xffffff00 */); } QPixmap pixmap = QPixmap::fromImage(*m_displayImage); m_displayItem->setPixmap(pixmap); if (collidingThing->nonConnectorItem) { m_sketchWidget->selectItem(collidingThing->nonConnectorItem->attachedTo()); } } void DRCResultsDialog::releasedSlot(QListWidgetItem * item) { if (item == NULL) return; int ix = item->data(Qt::UserRole).toInt(); CollidingThing * collidingThing = m_collidingThings.at(ix); foreach (QPointF p, collidingThing->atPixels) { m_displayImage->setPixel(p.x(), p.y(), 1 /* 0x80ff0000 */); } QPixmap pixmap = QPixmap::fromImage(*m_displayImage); m_displayItem->setPixmap(pixmap); } /////////////////////////////////////////// const QString DRC::KeepoutSettingName("DRC_Keepout"); const double DRC::KeepoutDefaultMils = 10; /////////////////////////////////////////////// static QString CancelledMessage; /////////////////////////////////////////// DRC::DRC(PCBSketchWidget * sketchWidget, ItemBase * board) { CancelledMessage = tr("DRC was cancelled."); m_cancelled = false; m_sketchWidget = sketchWidget; m_board = board; m_displayItem = NULL; m_displayImage = NULL; m_plusImage = NULL; m_minusImage = NULL; } DRC::~DRC(void) { if (m_displayItem) { delete m_displayItem; } if (m_plusImage) { delete m_plusImage; } if (m_minusImage) { delete m_minusImage; } if (m_displayImage) { delete m_displayImage; } foreach (QDomDocument * doc, m_masterDocs) { delete doc; } } QStringList DRC::start(bool showOkMessage, double keepoutMils) { QString message; QStringList messages; QList collidingThings; bool result = startAux(message, messages, collidingThings, keepoutMils); if (result) { if (messages.count() == 0) { message = tr("Your sketch is ready for production: there are no connectors or traces that overlap or are too close together."); } else { message = tr("The areas on your board highlighted in red are connectors and traces which may overlap or be too close together. ") + tr("Reposition them and run the DRC again to find more problems"); } } #ifndef QT_NO_DEBUG m_displayImage->save(FolderUtils::getTopLevelUserDataStorePath() + "/testDRCDisplay.png"); #endif emit wantBothVisible(); emit setProgressValue(m_maxProgress); emit hideProgress(); if (showOkMessage) { if (messages.count() == 0) { QMessageBox::information(m_sketchWidget->window(), tr("Fritzing"), message); } else { DRCResultsDialog * dialog = new DRCResultsDialog(message, messages, collidingThings, m_displayItem, m_displayImage, m_sketchWidget, m_sketchWidget->window()); dialog->show(); } } else {} m_displayItem = NULL; m_displayImage = NULL; return messages; } bool DRC::startAux(QString & message, QStringList & messages, QList & collidingThings, double keepoutMils) { bool bothSidesNow = m_sketchWidget->boardLayers() == 2; QList visited; QList< QList > equis; QList< QList > singletons; foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!connectorItem->attachedTo()->isEverVisible()) continue; if (connectorItem->attachedTo()->getRatsnest()) continue; if (visited.contains(connectorItem)) continue; QList equi; equi.append(connectorItem); ConnectorItem::collectEqualPotential(equi, bothSidesNow, (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ m_sketchWidget->getTraceFlag()); visited.append(equi); if (equi.count() == 1) { singletons.append(equi); continue; } ItemBase * firstPart = connectorItem->attachedTo()->layerKinChief(); bool gotTwo = false; foreach (ConnectorItem * equ, equi) { if (equ->attachedTo()->layerKinChief() != firstPart) { gotTwo = true; break; } } if (!gotTwo) { singletons.append(equi); continue; } equis.append(equi); } m_maxProgress = equis.count() + singletons.count() + 1; if (bothSidesNow) m_maxProgress *= 2; emit setMaximumProgress(m_maxProgress); int progress = 1; emit setProgressValue(progress); ProcessEventBlocker::processEvents(); double dpi = qMax((double) 250, 1000 / keepoutMils); // turns out making a variable dpi doesn't work due to vector-to-raster issues QRectF boardRect = m_board->sceneBoundingRect(); QRectF sourceRes(0, 0, boardRect.width() * dpi / GraphicsUtils::SVGDPI, boardRect.height() * dpi / GraphicsUtils::SVGDPI); QSize imgSize(qCeil(sourceRes.width()), qCeil(sourceRes.height())); m_plusImage = new QImage(imgSize, QImage::Format_Mono); m_plusImage->fill(0xffffffff); m_minusImage = new QImage(imgSize, QImage::Format_Mono); m_minusImage->fill(0); m_displayImage = new QImage(imgSize, QImage::Format_Indexed8); m_displayImage->setColor(0, 0); m_displayImage->setColor(1, 0x80ff0000); m_displayImage->setColor(2, 0xffffff00); m_displayImage->fill(0); if (!makeBoard(m_minusImage, sourceRes)) { message = tr("Fritzing error: unable to render board svg."); return false; } extendBorder(1, m_minusImage); // since the resolution = keepout, extend by 1 QList layerSpecs; layerSpecs << ViewLayer::NewBottom; if (bothSidesNow) layerSpecs << ViewLayer::NewTop; int emptyMasterCount = 0; foreach (ViewLayer::ViewLayerPlacement viewLayerPlacement, layerSpecs) { if (viewLayerPlacement == ViewLayer::NewTop) { emit wantTopVisible(); m_plusImage->fill(0xffffffff); } else emit wantBottomVisible(); LayerList viewLayerIDs = ViewLayer::copperLayers(viewLayerPlacement); viewLayerIDs.removeOne(ViewLayer::GroundPlane0); viewLayerIDs.removeOne(ViewLayer::GroundPlane1); RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = renderThing.selectedItems = renderThing.renderBlocker = false; QString master = m_sketchWidget->renderToSVG(renderThing, m_board, viewLayerIDs); if (master.isEmpty()) { if (++emptyMasterCount == layerSpecs.count()) { message = tr("No traces or connectors to check"); return false; } progress++; continue; } QDomDocument * masterDoc = new QDomDocument(); m_masterDocs.insert(viewLayerPlacement, masterDoc); QString errorStr; int errorLine; int errorColumn; if (!masterDoc->setContent(master, &errorStr, &errorLine, &errorColumn)) { message = tr("Unexpected SVG rendering failure--contact fritzing.org"); return false; } ProcessEventBlocker::processEvents(); if (m_cancelled) { message = CancelledMessage; return false; } QDomElement root = masterDoc->documentElement(); SvgFileSplitter::forceStrokeWidth(root, 2 * keepoutMils, "#000000", true, false); ItemBase::renderOne(masterDoc, m_plusImage, sourceRes); ProcessEventBlocker::processEvents(); if (m_cancelled) { message = CancelledMessage; return false; } QList atPixels; if (pixelsCollide(m_plusImage, m_minusImage, m_displayImage, 0, 0, imgSize.width(), imgSize.height(), 1 /* 0x80ff0000 */, atPixels)) { CollidingThing * collidingThing = findItemsAt(atPixels, m_board, viewLayerIDs, keepoutMils, dpi, true, NULL); QString msg = tr("Too close to a border (%1 layer)") .arg(viewLayerPlacement == ViewLayer::NewTop ? ItemBase::TranslatedPropertyNames.value("top") : ItemBase::TranslatedPropertyNames.value("bottom")) ; emit setProgressMessage(msg); messages << msg; collidingThings << collidingThing; updateDisplay(); } emit setProgressValue(progress++); ProcessEventBlocker::processEvents(); if (m_cancelled) { message = CancelledMessage; return false; } } // we are checking all the singletons at once // but the DRC will miss it if any of them overlap each other while (singletons.count() > 0) { QList combined; QList singleton = singletons.takeFirst(); ItemBase * chief = singleton.at(0)->attachedTo()->layerKinChief(); combined.append(singleton); for (int ix = singletons.count() - 1; ix >= 0; ix--) { QList candidate = singletons.at(ix); if (candidate.at(0)->attachedTo()->layerKinChief() == chief) { combined.append(candidate); singletons.removeAt(ix); } } equis.append(combined); } int index = 0; foreach (ViewLayer::ViewLayerPlacement viewLayerPlacement, layerSpecs) { if (viewLayerPlacement == ViewLayer::NewTop) emit wantTopVisible(); else emit wantBottomVisible(); QDomDocument * masterDoc = m_masterDocs.value(viewLayerPlacement, NULL); if (masterDoc == NULL) continue; LayerList viewLayerIDs = ViewLayer::copperLayers(viewLayerPlacement); viewLayerIDs.removeOne(ViewLayer::GroundPlane0); viewLayerIDs.removeOne(ViewLayer::GroundPlane1); foreach (QList equi, equis) { bool inLayer = false; foreach (ConnectorItem * equ, equi) { if (viewLayerIDs.contains(equ->attachedToViewLayerID())) { inLayer = true; break; } } if (!inLayer) { progress++; continue; } // we have a net; m_plusImage->fill(0xffffffff); m_minusImage->fill(0xffffffff); splitNet(masterDoc, equi, m_minusImage, m_plusImage, sourceRes, viewLayerPlacement, index++, keepoutMils); QHash rects; QList wires; foreach (ConnectorItem * equ, equi) { if (viewLayerIDs.contains(equ->attachedToViewLayerID())) { if (equ->attachedToItemType() == ModelPart::Wire) { Wire * wire = qobject_cast(equ->attachedTo()); if (!wires.contains(wire)) { wires.append(wire); // could break diagonal wires into a series of rects rects.insert(equ, wire->sceneBoundingRect()); } } else { rects.insert(equ, equ->sceneBoundingRect()); } } } ProcessEventBlocker::processEvents(); if (m_cancelled) { message = CancelledMessage; return false; } foreach (ConnectorItem * equ, rects.keys()) { QRectF rect = rects.value(equ).intersected(boardRect); double l = (rect.left() - boardRect.left()) * dpi / GraphicsUtils::SVGDPI; double t = (rect.top() - boardRect.top()) * dpi / GraphicsUtils::SVGDPI; double r = (rect.right() - boardRect.left()) * dpi / GraphicsUtils::SVGDPI; double b = (rect.bottom() - boardRect.top()) * dpi / GraphicsUtils::SVGDPI; //DebugDialog::debug(QString("l:%1 t:%2 r:%3 b:%4").arg(l).arg(t).arg(r).arg(b)); QList atPixels; if (pixelsCollide(m_plusImage, m_minusImage, m_displayImage, l, t, r, b, 1 /* 0x80ff0000 */, atPixels)) { #ifndef QT_NO_DEBUG m_plusImage->save(FolderUtils::getTopLevelUserDataStorePath() + QString("/collidePlus%1_%2.png").arg(viewLayerPlacement).arg(index)); m_minusImage->save(FolderUtils::getTopLevelUserDataStorePath() + QString("/collideMinus%1_%2.png").arg(viewLayerPlacement).arg(index)); #endif CollidingThing * collidingThing = findItemsAt(atPixels, m_board, viewLayerIDs, keepoutMils, dpi, false, equ); QStringList names = getNames(collidingThing); QString name0 = names.at(0); QString msg = tr("%1 is overlapping (%2 layer)") .arg(name0) .arg(viewLayerPlacement == ViewLayer::NewTop ? ItemBase::TranslatedPropertyNames.value("top") : ItemBase::TranslatedPropertyNames.value("bottom")) ; messages << msg; collidingThings << collidingThing; emit setProgressMessage(msg); updateDisplay(); } } emit setProgressValue(progress++); ProcessEventBlocker::processEvents(); if (m_cancelled) { message = CancelledMessage; return false; } } } checkHoles(messages, collidingThings, dpi); checkCopperBoth(messages, collidingThings, dpi); return true; } bool DRC::makeBoard(QImage * image, QRectF & sourceRes) { LayerList viewLayerIDs; viewLayerIDs << ViewLayer::Board; RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = renderThing.selectedItems = renderThing.renderBlocker = false; QString boardSvg = m_sketchWidget->renderToSVG(renderThing, m_board, viewLayerIDs); if (boardSvg.isEmpty()) { return false; } QByteArray boardByteArray; QString tempColor("#ffffff"); QStringList exceptions; exceptions << "none" << ""; if (!SvgFileSplitter::changeColors(boardSvg, tempColor, exceptions, boardByteArray)) { return false; } QSvgRenderer renderer(boardByteArray); QPainter painter; painter.begin(image); painter.setRenderHint(QPainter::Antialiasing, false); DebugDialog::debug("boardbounds", sourceRes); renderer.render(&painter /*, sourceRes */); painter.end(); // board should be white, borders should be black #ifndef QT_NO_DEBUG image->save(FolderUtils::getTopLevelUserDataStorePath() + "/testDRCBoard.png"); #endif return true; } void DRC::splitNet(QDomDocument * masterDoc, QList & equi, QImage * minusImage, QImage * plusImage, QRectF & sourceRes, ViewLayer::ViewLayerPlacement viewLayerPlacement, int index, double keepoutMils) { // deal with connectors on the same part, even though they are not on the same net // in other words, make sure there are no overlaps of connectors on the same part QList net; QList alsoNet; QList notNet; Markers markers; markers.outID = AlsoNet; markers.inTerminalID = markers.inSvgID = markers.inSvgAndID = markers.inNoID = Net; splitNetPrep(masterDoc, equi, markers, net, alsoNet, notNet, true); foreach (QDomElement element, notNet) element.setTagName("g"); foreach (QDomElement element, alsoNet) element.setTagName("g"); foreach (QDomElement element, net) { // want the normal size SvgFileSplitter::forceStrokeWidth(element, -2 * keepoutMils, "#000000", false, false); } ItemBase::renderOne(masterDoc, plusImage, sourceRes); foreach (QDomElement element, net) { // restore to keepout size SvgFileSplitter::forceStrokeWidth(element, 2 * keepoutMils, "#000000", false, false); } #ifndef QT_NO_DEBUG plusImage->save(FolderUtils::getTopLevelUserDataStorePath() + QString("/splitNetPlus%1_%2.png").arg(viewLayerPlacement).arg(index)); #else Q_UNUSED(viewLayerPlacement); Q_UNUSED(index); #endif // now want notnet foreach (QDomElement element, net) { element.removeAttribute("net"); element.setTagName("g"); } foreach (QDomElement element, alsoNet) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } foreach (QDomElement element, notNet) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } ItemBase::renderOne(masterDoc, minusImage, sourceRes); #ifndef QT_NO_DEBUG minusImage->save(FolderUtils::getTopLevelUserDataStorePath() + QString("/splitNetMinus%1_%2.png").arg(viewLayerPlacement).arg(index)); #endif // master doc restored to original state foreach (QDomElement element, net) { element.setTagName(element.attribute("former")); } } void DRC::splitNetPrep(QDomDocument * masterDoc, QList & equi, const Markers & markers, QList & net, QList & alsoNet, QList & notNet, bool checkIntersection) { QMultiHash partSvgIDs; QMultiHash partTerminalIDs; QHash bothIDs; QMultiHash itemBases; QSet wireIDs; foreach (ConnectorItem * equ, equi) { ItemBase * itemBase = equ->attachedTo(); if (itemBase == NULL) continue; if (itemBase->itemType() == ModelPart::Wire) { wireIDs.insert(QString::number(itemBase->id())); } if (equ->connector() == NULL) { // this shouldn't happen itemBase->debugInfo("!!!!!!!!!!!!!!!!!!!!!!!!!!!!! missing connector !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); equ->debugInfo("missing connector"); continue; } QString sid = QString::number(itemBase->id()); SvgIdLayer * svgIdLayer = equ->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID()); partSvgIDs.insert(sid, svgIdLayer->m_svgId); if (!svgIdLayer->m_terminalId.isEmpty()) { partTerminalIDs.insert(sid, svgIdLayer->m_terminalId); bothIDs.insert(sid + svgIdLayer->m_svgId, svgIdLayer->m_terminalId); } itemBases.insert(sid, itemBase); } QList todo; todo << masterDoc->documentElement(); bool firstTime = true; while (!todo.isEmpty()) { QDomElement element = todo.takeFirst(); //QString string; //QTextStream stream(&string); //element.save(stream, 0); QDomElement child = element.firstChildElement(); while (!child.isNull()) { todo << child; child = child.nextSiblingElement(); } if (firstTime) { // don't include the root element firstTime = false; continue; } QString partID = element.attribute("partID"); if (!partID.isEmpty()) { QStringList svgIDs = partSvgIDs.values(partID); QStringList terminalIDs = partTerminalIDs.values(partID); if (svgIDs.count() == 0) { markSubs(element, NotNet); } else if (wireIDs.contains(partID)) { markSubs(element, Net); } else { splitSubs(masterDoc, element, partID, markers, svgIDs, terminalIDs, itemBases.values(partID), bothIDs, checkIntersection); } } if (element.tagName() == "g") { element.removeAttribute("net"); continue; } element.setAttribute("former", element.tagName()); if (element.attribute("net") == Net) { net.append(element); } else if (element.attribute("net") == AlsoNet) { alsoNet.append(element); } else { // assume the element belongs to notNet notNet.append(element); } } } void DRC::markSubs(QDomElement & root, const QString & mark) { QList todo; todo << root; while (!todo.isEmpty()) { QDomElement element = todo.takeFirst(); element.setAttribute("net", mark); QDomElement child = element.firstChildElement(); while (!child.isNull()) { todo << child; child = child.nextSiblingElement(); } } } void DRC::splitSubs(QDomDocument * doc, QDomElement & root, const QString & partID, const Markers & markers, const QStringList & svgIDs, const QStringList & terminalIDs, const QList & itemBases, QHash & bothIDs, bool checkIntersection) { //QString string; //QTextStream stream(&string); //root.save(stream, 0); QStringList notSvgIDs; QStringList notTerminalIDs; if (checkIntersection && itemBases.count() > 0) { ItemBase * itemBase = itemBases.at(0); foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { SvgIdLayer * svgIdLayer = connectorItem->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID()); if (!svgIDs.contains(svgIdLayer->m_svgId)) { notSvgIDs.append(svgIdLayer->m_svgId); } if (!svgIdLayer->m_terminalId.isEmpty()) { if (!terminalIDs.contains(svgIdLayer->m_terminalId)) { notTerminalIDs.append(svgIdLayer->m_terminalId); } } } } // split subelements of a part into separate nets QList todo; QList netElements; todo << root; while (!todo.isEmpty()) { QDomElement element = todo.takeFirst(); QString svgID = element.attribute("id"); if (!svgID.isEmpty()) { if (svgIDs.contains(svgID)) { if (bothIDs.value(partID + svgID).isEmpty()) { // no terminal point markSubs(element, markers.inSvgID); } else { // privilege the terminal point over the pin/pad markSubs(element, markers.inSvgAndID); } netElements << element; // save these for intersection checks // all children are marked so don't add these to todo continue; } else if (notSvgIDs.contains(svgID)) { markSubs(element, markers.outID); // all children are marked so don't add these to todo continue; } else if (terminalIDs.contains(svgID)) { markSubs(element, markers.inTerminalID); continue; } else if (notTerminalIDs.contains(svgID)) { markSubs(element, markers.outID); continue; } } QDomElement child = element.firstChildElement(); while (!child.isNull()) { todo << child; child = child.nextSiblingElement(); } } QList toCheck; todo << root; while (!todo.isEmpty()) { QDomElement element = todo.takeFirst(); QString net = element.attribute("net"); if (net == AlsoNet) continue; else if (net == Net) continue; else if (net == NotNet) continue; else if (!checkIntersection) { element.setAttribute("net", markers.outID); } else if (element.tagName() != "g") { element.setAttribute("oldid", element.attribute("id")); element.setAttribute("id", QString("_____toCheck_____%1").arg(toCheck.count())); toCheck << element; } QDomElement child = element.firstChildElement(); while (!child.isNull()) { todo << child; child = child.nextSiblingElement(); } } if (toCheck.count() > 0) { // deal with elements that are effectively part of a connector, but aren't separately labeled // such as a rect which overlaps a circle QDomElement masterRoot = doc->documentElement(); QString svg = QString("\n") .arg(masterRoot.attribute("width")) .arg(masterRoot.attribute("height")) .arg(masterRoot.attribute("viewBox")); QString string; QTextStream stream(&string); root.save(stream, 0); svg += string; svg += ""; QSvgRenderer renderer; renderer.load(svg.toUtf8()); QList netRects; foreach (QDomElement element, netElements) { QString id = element.attribute("id"); QRectF r = renderer.matrixForElement(id).mapRect(renderer.boundsOnElement(id)); netRects << r; } QList checkRects; foreach (QDomElement element, toCheck) { QString id = element.attribute("id"); QRectF r = renderer.matrixForElement(id).mapRect(renderer.boundsOnElement(id)); checkRects << r; element.setAttribute("id", element.attribute("oldid")); } for (int i = 0; i < checkRects.count(); i++) { QRectF checkr = checkRects.at(i); QDomElement checkElement = toCheck.at(i); bool gotOne = false; double carea = checkr.width() * checkr.height(); foreach (QRectF netr, netRects) { QRectF sect = netr.intersected(checkr); if (sect.isEmpty()) continue; double area = sect.width() * sect.height(); double netArea = netr.width() * netr.height(); if ((area > netArea * .5) && (netArea * 2 > carea)) { markSubs(checkElement, markers.inNoID); gotOne = true; break; } } if (!gotOne) { markSubs(checkElement, markers.outID); } } } } void DRC::updateDisplay() { QPixmap pixmap = QPixmap::fromImage(*m_displayImage); if (m_displayItem == NULL) { m_displayItem = new QGraphicsPixmapItem(pixmap); m_displayItem->setPos(m_board->sceneBoundingRect().topLeft()); m_sketchWidget->scene()->addItem(m_displayItem); m_displayItem->setZValue(5000); m_displayItem->setScale(m_board->sceneBoundingRect().width() / m_displayImage->width()); // GraphicsUtils::SVGDPI / dpi m_displayItem->setVisible(true); } else { m_displayItem->setPixmap(pixmap); } ProcessEventBlocker::processEvents(); } void DRC::cancel() { m_cancelled = true; } CollidingThing * DRC::findItemsAt(QList & atPixels, ItemBase * board, const LayerList & viewLayerIDs, double keepoutMils, double dpi, bool skipHoles, ConnectorItem * already) { Q_UNUSED(board); Q_UNUSED(viewLayerIDs); Q_UNUSED(keepoutMils); Q_UNUSED(dpi); Q_UNUSED(skipHoles); CollidingThing * collidingThing = new CollidingThing; collidingThing->nonConnectorItem = already; collidingThing->atPixels = atPixels; return collidingThing; } void DRC::extendBorder(double keepout, QImage * image) { // TODO: scanlines // keepout in terms of the board grid size QImage copy = image->copy(); int h = image->height(); int w = image->width(); int ikeepout = qCeil(keepout); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (copy.pixel(x, y) != 0xff000000) { continue; } for (int dy = y - ikeepout; dy <= y + ikeepout; dy++) { if (dy < 0) continue; if (dy >= h) continue; for (int dx = x - ikeepout; dx <= x + ikeepout; dx++) { if (dx < 0) continue; if (dx >= w) continue; // extend border by keepout image->setPixel(dx, dy, 0); } } } } } void DRC::checkHoles(QStringList & messages, QList & collidingThings, double dpi) { QRectF boardRect = m_board->sceneBoundingRect(); foreach (QGraphicsItem * item, m_sketchWidget->scene()->collidingItems(m_board)) { NonConnectorItem * nci = dynamic_cast(item); if (nci == NULL) continue; QRectF ncibr = nci->sceneBoundingRect(); if (boardRect.contains(ncibr)) continue; if (nci->attachedToItemType() == ModelPart::Wire) continue; if (nci->attachedToItemType() == ModelPart::CopperFill) continue; // TODO: skip pad part, logo item, smds QRectF ir = boardRect.intersected(ncibr); int x = qFloor((ir.left() - boardRect.left()) * dpi / GraphicsUtils::SVGDPI); if (x < 0) x = 0; int y = qFloor((ir.top() - boardRect.top()) * dpi / GraphicsUtils::SVGDPI); if (y < 0) y = 0; int w = qCeil(ir.width() * dpi / GraphicsUtils::SVGDPI); if (x + w > m_displayImage->width()) w = m_displayImage->width() - x; int h = qCeil(ir.height() * dpi / GraphicsUtils::SVGDPI); if (y + h > m_displayImage->height()) h = m_displayImage->height() - y; CollidingThing * collidingThing = new CollidingThing; collidingThing->nonConnectorItem = nci; for (int iy = 0; iy < h; iy++) { for (int ix = 0; ix < w; ix++) { QPoint p(ix + x, iy + y); m_displayImage->setPixel(p, 1); collidingThing->atPixels << p; } } QStringList names = getNames(collidingThing); QString name0 = names.at(0); QString msg = tr("A hole in %1 may lie outside the border of the board and would be clipped.") .arg(name0) ; messages << msg; collidingThings << collidingThing; emit setProgressMessage(msg); updateDisplay(); } } void DRC::checkCopperBoth(QStringList & messages, QList & collidingThings, double dpi) { QRectF boardRect = m_board->sceneBoundingRect(); QList visited; foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isEverVisible()) continue; if (itemBase->modelPart()->isCore()) continue; itemBase = itemBase->layerKinChief(); if (visited.contains(itemBase)) continue; visited << itemBase; QRectF ibr = item->sceneBoundingRect(); if (!boardRect.intersects(ibr)) continue; QString fzpPath = itemBase->modelPart()->path(); QFile file(fzpPath); if (!file.open(QFile::ReadOnly)) { DebugDialog::debug(QString("unable to open %1").arg(fzpPath)); continue; } QString fzp = file.readAll(); file.close(); bool copper0 = fzp.contains("copper0"); bool copper1 = fzp.contains("copper1"); if (!copper1 && !copper0) continue; QString svgPath = itemBase->fsvgRenderer()->filename(); QFile file2(svgPath); if (!file2.open(QFile::ReadOnly)) { DebugDialog::debug(QString("itembase svg file open failure %1").arg(itemBase->id())); continue; } QString svg = file2.readAll(); file2.close(); QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("itembase svg xml failure %1 %2 %3 %4").arg(itemBase->id()).arg(errorStr).arg(errorLine).arg(errorColumn)); continue; } QSet missing; QDomElement root = doc.documentElement(); if (copper1) { foreach(ConnectorItem * ci, missingCopper("copper1", ViewLayer::Copper1, itemBase, root)) { missing << ci; } } if (copper0) { foreach (ConnectorItem * ci, missingCopper("copper0", ViewLayer::Copper0, itemBase, root)) { missing << ci; } } foreach (ConnectorItem * ci, missing) { QRectF ir = boardRect.intersected(ci->sceneBoundingRect()); int x = qFloor((ir.left() - boardRect.left()) * dpi / GraphicsUtils::SVGDPI); if (x < 0) x = 0; int y = qFloor((ir.top() - boardRect.top()) * dpi / GraphicsUtils::SVGDPI); if (y < 0) y = 0; int w = qCeil(ir.width() * dpi / GraphicsUtils::SVGDPI); if (x + w > m_displayImage->width()) w = m_displayImage->width() - x; int h = qCeil(ir.height() * dpi / GraphicsUtils::SVGDPI); if (y + h > m_displayImage->height()) h = m_displayImage->height() - y; CollidingThing * collidingThing = new CollidingThing; collidingThing->nonConnectorItem = ci; for (int iy = 0; iy < h; iy++) { for (int ix = 0; ix < w; ix++) { QPoint p(ix + x, iy + y); m_displayImage->setPixel(p, 1); collidingThing->atPixels << p; } } QStringList names = getNames(collidingThing); QString name0 = names.at(0); QString msg = tr("Connector %1 on %2 should have both copper top and bottom layers, but the svg only specifies one layer.") .arg(ci->connectorSharedName()) .arg(name0) ; messages << msg; collidingThings << collidingThing; emit setProgressMessage(msg); updateDisplay(); } } } QList DRC::missingCopper(const QString & layerName, ViewLayer::ViewLayerID viewLayerID, ItemBase * itemBase, const QDomElement & root) { QDomElement copperElement = TextUtils::findElementWithAttribute(root, "id", layerName); QList missing; foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { SvgIdLayer * svgIdLayer = connectorItem->connector()->fullPinInfo(itemBase->viewID(), viewLayerID); if (svgIdLayer == NULL) { DebugDialog::debug(QString("missing pin info for %1").arg(itemBase->id())); missing << connectorItem; continue; } QDomElement element = TextUtils::findElementWithAttribute(copperElement, "id", svgIdLayer->m_svgId); if (element.isNull()) { missing << connectorItem; } } return missing; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/drc.h�������������������������������������������������������������0000664�0000000�0000000�00000011201�12723636013�0020034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef DRC_H #define DRC_H #include #include #include #include #include #include #include #include #include #include #include "../svg/svgfilesplitter.h" #include "../viewlayer.h" struct CollidingThing { QPointer nonConnectorItem; QList atPixels; }; struct Markers { QString inSvgID; QString inSvgAndID; QString inTerminalID; QString inNoID; QString outID; }; class DRC : public QObject { Q_OBJECT public: DRC(class PCBSketchWidget *, class ItemBase * board); virtual ~DRC(void); QStringList start(bool showOkMessage, double keepoutMils); public: static void splitNetPrep(QDomDocument * masterDoc, QList & equi, const Markers &, QList & net, QList & alsoNet, QList & notNet, bool checkIntersection); static void extendBorder(double keepoutImagePixels, QImage * image); public slots: void cancel(); signals: void hideProgress(); void setMaximumProgress(int); void setProgressValue(int); void wantTopVisible(); void wantBottomVisible(); void wantBothVisible(); void setProgressMessage(const QString &); public: static const QString NotNet; static const QString AlsoNet; static const QString Net; static const uchar BitTable[]; static const QString KeepoutSettingName; static const double KeepoutDefaultMils; protected: bool makeBoard(QImage *, QRectF & sourceRes); void splitNet(QDomDocument *, QList & , QImage * minusImage, QImage * plusImage, QRectF & sourceRes, ViewLayer::ViewLayerPlacement viewLayerPlacement, int index, double keepoutMils); void updateDisplay(); bool startAux(QString & message, QStringList & messages, QList &, double keepoutMils); CollidingThing * findItemsAt(QList &, ItemBase * board, const LayerList & viewLayerIDs, double keepout, double dpi, bool skipHoles, ConnectorItem * already); void checkHoles(QStringList & messages, QList & collidingThings, double dpi); void checkCopperBoth(QStringList & messages, QList & collidingThings, double dpi); QList missingCopper(const QString & layerName, ViewLayer::ViewLayerID, ItemBase *, const QDomElement & svgRoot); protected: static void markSubs(QDomElement & root, const QString & mark); static void splitSubs(QDomDocument *, QDomElement & root, const QString & partID, const Markers &, const QStringList & svgIDs, const QStringList & terminalIDs, const QList &, QHash & both, bool checkIntersection); protected: class PCBSketchWidget * m_sketchWidget; class ItemBase * m_board; double m_keepout; QImage * m_plusImage; QImage * m_minusImage; QImage * m_displayImage; QGraphicsPixmapItem * m_displayItem; QHash m_masterDocs; bool m_cancelled; int m_maxProgress; }; class DRCResultsDialog : public QDialog { Q_OBJECT public: DRCResultsDialog(const QString & message, const QStringList & messages, const QList &, QGraphicsPixmapItem * displayItem, QImage * displayImage, class PCBSketchWidget * sketchWidget, QWidget *parent = 0); ~DRCResultsDialog(); protected slots: void pressedSlot(QListWidgetItem *); void releasedSlot(QListWidgetItem *); protected: QStringList m_messages; QList m_collidingThings; QPointer m_sketchWidget; QGraphicsPixmapItem * m_displayItem; QImage * m_displayImage; }; #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/eventeater.cpp����������������������������������������������������0000664�0000000�0000000�00000004576�12723636013�0022002�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include "eventeater.h" #include "../debugdialog.h" EventEater::EventEater(QObject * parent) : QObject(parent) { } bool EventEater::eventFilter(QObject *obj, QEvent *event) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::HoverEnter: case QEvent::HoverLeave: case QEvent::WinEventAct: case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: case QEvent::GraphicsSceneMouseDoubleClick: case QEvent::GraphicsSceneMouseMove: case QEvent::GraphicsSceneMousePress: case QEvent::GraphicsSceneMouseRelease: case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::NonClientAreaMouseButtonPress: case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseMove: { bool gotOne = false; foreach (QWidget * widget, m_allowedWidgets) { for (QObject * parentObj = obj; parentObj != NULL; parentObj = parentObj->parent()) { if (parentObj == widget) { gotOne = true; break; } } } if (!gotOne) { // filter out the event return true; } } break; default: break; } return QObject::eventFilter(obj, event); } void EventEater::allowEventsIn(QWidget * widget) { m_allowedWidgets.append(widget); } ����������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/eventeater.h������������������������������������������������������0000664�0000000�0000000�00000002526�12723636013�0021440�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #ifndef EVENTEATER_H #define EVENTEATER_H #include #include #include #include class EventEater : public QObject { Q_OBJECT public: EventEater(QObject * parent = 0); void allowEventsIn(QWidget *); protected: bool eventFilter(QObject *obj, QEvent *event); protected: QList m_allowedWidgets; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/mazerouter/�������������������������������������������������������0000775�0000000�0000000�00000000000�12723636013�0021315�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/mazerouter/mazerouter.cpp�����������������������������������������0000664�0000000�0000000�00000366075�12723636013�0024237�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6966 $: $Author: irascibl@gmail.com $: $Date: 2013-04-15 11:25:51 +0200 (Mo, 15. Apr 2013) $ ********************************************************************/ // with lots of suggestions from http://cc.ee.ntu.edu.tw/~ywchang/Courses/PD/unit6.pdf // and some help from http://workbench.lafayette.edu/~nestorj/cadapplets/MazeApplet/src/ // more (unused) suggestions at http://embedded.eecs.berkeley.edu/Alumni/pinhong/ee244/4-routing.PDF // TODO: // // jumper placement must be away from vias and vv // how to create test case // // test if source touches target // // add max cycles to settings dialog? // // figure out what is taking so long once the router is creating traces // change connection is calling ratsnestconnect for every pair and calls collectEqualPotential // // net reordering/rip-up-and-reroute // is there a better way than move back by one? // // raster back to vector // curve-fitting? use a bezier? http://kobus.ca/seminars/ugrad/NM5_curve_s02.pdf // // dynamic cost function based on distance to any target point? // // allow multiple routes to reach GridTarget--eliminate all queued GridPoints with greater cost // // what happens with expanded schematic frame? // #include "mazerouter.h" #include "../../sketch/pcbsketchwidget.h" #include "../../debugdialog.h" #include "../../items/virtualwire.h" #include "../../items/tracewire.h" #include "../../items/jumperitem.h" #include "../../items/symbolpaletteitem.h" #include "../../items/via.h" #include "../../items/resizableboard.h" #include "../../utils/graphicsutils.h" #include "../../utils/graphutils.h" #include "../../utils/textutils.h" #include "../../utils/folderutils.h" #include "../../connectors/connectoritem.h" #include "../../items/moduleidnames.h" #include "../../processeventblocker.h" #include "../../svg/groundplanegenerator.h" #include "../../svg/svgfilesplitter.h" #include "../../fsvgrenderer.h" #include "../drc.h" #include "../../connectors/svgidlayer.h" #include #include #include #include #include ////////////////////////////////////// static const int OptimizeFactor = 2; //static int routeNumber = 0; static QString CancelledMessage; static const int DefaultMaxCycles = 100; static const GridValue GridBoardObstacle = std::numeric_limits::max(); static const GridValue GridPartObstacle = GridBoardObstacle - 1; static const GridValue GridSource = GridBoardObstacle - 2; static const GridValue GridTarget = GridBoardObstacle - 3; static const GridValue GridAvoid = GridBoardObstacle - 4; static const GridValue GridTempObstacle = GridBoardObstacle - 5; static const GridValue GridSourceFlag = (GridBoardObstacle / 2) + 1; static const uint Layer1Cost = 100; static const uint CrossLayerCost = 100; static const uint ViaCost = 2000; static const uint AvoidCost = 7; static const uchar GridPointDone = 1; static const uchar GridPointStepYPlus = 2; static const uchar GridPointStepYMinus = 4; static const uchar GridPointStepXPlus = 8; static const uchar GridPointStepXMinus = 16; static const uchar GridPointStepStart = 32; static const uchar GridPointJumperLeft = 64; static const uchar GridPointJumperRight = 128; //static const uchar GridPointNorth = 2; //static const uchar GridPointEast = 4; //static const uchar GridPointSouth = 8; //static const uchar GridPointWest = 16; static const uchar JumperStart = 1; static const uchar JumperEnd = 2; static const uchar JumperLeft = 4; static const uchar JumperRight = 8; static const double MinTraceManhattanLength = 0.1; // pixels //////////////////////////////////////////////////////////////////// QPointF getStepPoint(QPointF p, uchar flags, double gridPixels) { if ((flags & GridPointStepXPlus) != 0) { p.setX(p.x() + (gridPixels / 2)); return p; } if ((flags & GridPointStepXMinus) != 0) { p.setX(p.x() - (gridPixels / 2)); return p; } if ((flags & GridPointStepYPlus) != 0) { p.setY(p.y() + (gridPixels / 2)); return p; } p.setY(p.y() - (gridPixels / 2)); return p; } uchar getStepFlag(GridPoint & gp1, GridPoint & gp2) { if (gp1.y == gp2.y) { double avg = (gp1.x + gp2.x) / 2.0; return (avg > gp1.x) ? GridPointStepXPlus : GridPointStepXMinus; } double avg = (gp1.y + gp2.y) / 2.0; return (avg > gp1.y) ? GridPointStepYPlus : GridPointStepYMinus; } QPointF getPixelCenter(GridPoint & gp, QPointF & topLeft, double gridPixels) { return QPointF((gp.x * gridPixels) + topLeft.x() + (gridPixels / 2), (gp.y * gridPixels) + topLeft.y() + (gridPixels / 2)); } uint getColor(GridValue val) { if (val == GridBoardObstacle) return 0xff000000; else if (val == GridPartObstacle) return 0xff404040; else if (val == 0) return 0; else if (val == GridSource) return 0xff00ff00; else if (val == GridTarget) return 0xffff0000; else if (val == GridAvoid) return 0xff808080; else if (val == GridTempObstacle) return 0xff00ffff; else if (val & GridSourceFlag) return 0xff60d060; else return 0xffff6060; } void fastCopy(QImage * from, QImage * to) { uchar * fromBits = from->scanLine(0); uchar * toBits = to->scanLine(0); long bytes = from->bytesPerLine() * from->height(); memcpy(toBits, fromBits, bytes); } bool atLeast(const QPointF & p1, const QPointF & p2) { return (qAbs(p1.x() - p2.x()) >= MinTraceManhattanLength) || (qAbs(p1.y() - p2.y()) >= MinTraceManhattanLength); } void printOrder(const QString & msg, QList & order) { QString string(msg); foreach (int i, order) { string += " " + QString::number(i); } DebugDialog::debug(string); } QString getPartID(const QDomElement & element) { QString partID = element.attribute("partID"); if (!partID.isEmpty()) return partID; QDomNode parent = element.parentNode(); if (parent.isNull()) return ""; return getPartID(parent.toElement()); } bool idsMatch(const QDomElement & element, QMultiHash & partIDs) { QString partID = getPartID(element); QStringList IDs = partIDs.values(partID); if (IDs.count() == 0) return false; QDomElement tempElement = element; while (tempElement.attribute("partID").isEmpty()) { QString id = tempElement.attribute("id"); if (!id.isEmpty() && IDs.contains(id)) return true; tempElement = tempElement.parentNode().toElement(); } return false; } bool byPinsWithin(Net * n1, Net * n2) { if (n1->pinsWithin < n2->pinsWithin) return true; if (n1->pinsWithin > n2->pinsWithin) return false; return n1->net->count() <= n2->net->count(); } bool byOrder(Trace & t1, Trace & t2) { return (t1.order < t2.order); } /* inline double initialCost(QPoint p1, QPoint p2) { //return qAbs(p1.x() - p2.x()) + qAbs(p1.y() - p2.y()); return qSqrt(GraphicsUtils::distanceSqd(p1, p2)); } */ inline double distanceCost(const QPoint & p1, const QPoint & p2) { return GraphicsUtils::distanceSqd(p1, p2); } inline double manhattanCost(const QPoint & p1, const QPoint & p2) { return qMax(qAbs(p1.x() - p2.x()), qAbs(p1.y() - p2.y())); } inline int gridPointInt(Grid * grid, GridPoint & gp) { return (gp.z * grid->x * grid->y) + (gp.y * grid->x) + gp.x; } bool jumperWillFit(GridPoint & gridPoint, const Grid * grid, int halfSize) { for (int z = 0; z < grid->z; z++) { for (int y = -halfSize; y <= halfSize; y++) { int py = gridPoint.y + y; if (py < 0) return false; if (py >= grid->y) return false; for (int x = -halfSize; x <= halfSize; x++) { int px = x + gridPoint.x; if (px < 0) return false; if (px >= grid->x) return false; GridValue val = grid->at(px, py, z); if (val == GridPartObstacle || val == GridBoardObstacle || val == GridSource || val == GridTarget || val == GridAvoid|| val == GridTempObstacle) return false; } } } return true; } bool schematicJumperWillFitAux(GridPoint & gridPoint, const Grid * grid, int halfSize, int xl, int xr) { int underCount = 0; for (int y = -halfSize; y <= halfSize; y++) { if (y < 0) return false; if (y >= grid->y) return false; for (int x = xl; x <= xr; x++) { if (x < 0) return false; if (x >= grid->x) return false; GridValue val = grid->at(gridPoint.x + x, gridPoint.y + y, 0); if (val == GridPartObstacle || val == GridBoardObstacle || val == GridSource || val == GridTarget || val == GridAvoid|| val == GridTempObstacle) { return false; } if (val < gridPoint.baseCost) { if (++underCount > xr - xl) return false; } } } return true; } bool schematicJumperWillFit(GridPoint & gridPoint, const Grid * grid, int halfSize) { if (schematicJumperWillFitAux(gridPoint, grid, halfSize, -(halfSize * 2), 0)) { gridPoint.flags |= GridPointJumperLeft; return true; } if (schematicJumperWillFitAux(gridPoint, grid, halfSize, 0, halfSize * 2)) { gridPoint.flags |= GridPointJumperRight; return true; } return false; } void initMarkers(Markers & markers, bool pcbType) { markers.outID = DRC::NotNet; markers.inTerminalID = pcbType ? DRC::AlsoNet : DRC::Net; markers.inSvgID = DRC::Net; markers.inSvgAndID = pcbType ? DRC::Net : DRC::AlsoNet; markers.inNoID = DRC::Net; } //////////////////////////////////////////////////////////////////// bool GridPoint::operator<(const GridPoint& other) const { // make sure lower cost is first return qCost > other.qCost; } GridPoint::GridPoint(QPoint p, int zed) { z = zed; x = p.x(); y = p.y(); flags = 0; } GridPoint::GridPoint() { flags = 0; } //////////////////////////////////////////////////////////////////// Grid::Grid(int sx, int sy, int sz) { x = sx; y = sy; z = sz; data = (GridValue *) malloc(x * y * z * sizeof(GridValue)); // calloc initializes grid to 0 } GridValue Grid::at(int sx, int sy, int sz) const { return *(data + (sz * y * x) + (sy * x) + sx); } void Grid::setAt(int sx, int sy, int sz, GridValue value) { *(data + (sz * y * x) + (sy * x) + sx) = value; } QList Grid::init(int sx, int sy, int sz, int width, int height, const QImage & image, GridValue value, bool collectPoints) { QList points; const uchar * bits1 = image.constScanLine(0); int bytesPerLine = image.bytesPerLine(); for (int iy = sy; iy < sy + height; iy++) { int offset = iy * bytesPerLine; for (int ix = sx; ix < sx + width; ix++) { int byteOffset = (ix >> 3) + offset; uchar mask = DRC::BitTable[ix & 7]; //if (routeNumber > 40) { // DebugDialog::debug(QString("image %1 %2, %3").arg(image.width()).arg(image.height()).arg(image.isNull())); // DebugDialog::debug(QString("init point %1 %2 %3").arg(ix).arg(iy).arg(sz)); // DebugDialog::debug(QString("init grid %1 %2 %3, %4").arg(x).arg(y).arg(z).arg((long) data, 0, 16)); //} if ((*(bits1 + byteOffset)) & mask) continue; //if (routeNumber > 40) DebugDialog::debug("after mask"); setAt(ix, iy, sz, value); //if (routeNumber > 40) // DebugDialog::debug("set"); if (collectPoints) { points.append(QPoint(ix, iy)); } } } return points; } QList Grid::init4(int sx, int sy, int sz, int width, int height, const QImage * image, GridValue value, bool collectPoints) { // pixels are 4 x 4 bits QList points; const uchar * bits1 = image->constScanLine(0); int bytesPerLine = image->bytesPerLine(); for (int iy = sy; iy < sy + height; iy++) { int offset = iy * bytesPerLine * 4; for (int ix = sx; ix < sx + width; ix++) { int byteOffset = (ix >> 1) + offset; uchar mask = ix & 1 ? 0x0f : 0xf0; if ((*(bits1 + byteOffset) & mask) != mask) ; else if ((*(bits1 + byteOffset + bytesPerLine) & mask) != mask) ; else if ((*(bits1 + byteOffset + bytesPerLine + bytesPerLine) & mask) != mask) ; else if ((*(bits1 + byteOffset + bytesPerLine + bytesPerLine + bytesPerLine) & mask) != mask) ; else continue; // "pixel" is all white setAt(ix, iy, sz, value); if (collectPoints) { points.append(QPoint(ix, iy)); } } } return points; } void Grid::copy(int fromIndex, int toIndex) { memcpy(((uchar *) data) + toIndex * x * y * sizeof(GridValue), ((uchar *) data) + fromIndex * x * y * sizeof(GridValue), x * y * sizeof(GridValue)); } void Grid::clear() { memset(data, 0, x * y * z * sizeof(GridValue)); } void Grid::free() { if (data) { ::free(data); data = NULL; } } //////////////////////////////////////////////////////////////////// Score::Score() { totalRoutedCount = totalViaCount = 0; anyUnrouted = false; reorderNet = -1; } void Score::setOrdering(const NetOrdering & _ordering) { reorderNet = -1; if (ordering.order.count() > 0) { bool remove = false; for (int i = 0; i < ordering.order.count(); i++) { if (!remove && (ordering.order.at(i) == _ordering.order.at(i))) continue; remove = true; int netIndex = ordering.order.at(i); traces.remove(netIndex); int c = routedCount.value(netIndex); routedCount.remove(netIndex); totalRoutedCount -= c; c = viaCount.value(netIndex); viaCount.remove(netIndex); totalViaCount -= c; } } ordering = _ordering; //printOrder("new ", ordering.order); } //////////////////////////////////////////////////////////////////// static const long IDs[] = { 1452191, 9781580, 9781600, 9781620, 9781640, 9781660, 9781680, 9781700 }; static inline bool hasID(ConnectorItem * s) { for (unsigned int i = 0; i < sizeof(IDs) / sizeof(long); i++) { if (s->attachedToID() == IDs[i]) return true; } return false; } void ConnectionThing::add(ConnectorItem * s, ConnectorItem * d) { //if (hasID(s) || hasID(d)) { // s->debugInfo("addc"); // d->debugInfo("\t"); //} sd.insert(s, d); sd.insert(d, s); } void ConnectionThing::remove(ConnectorItem * s) { //if (hasID(s)) { // s->debugInfo("remc"); //} sd.remove(s); } void ConnectionThing::remove(ConnectorItem * s, ConnectorItem * d) { //if (hasID(s) || hasID(d)) { // s->debugInfo("remc"); // d->debugInfo("\t"); //} sd.remove(s, d); sd.remove(d, s); } bool ConnectionThing::multi(ConnectorItem * s) { //if (hasID(s)) { // s->debugInfo("mulc"); //} return sd.values(s).count() > 1; } QList ConnectionThing::values(ConnectorItem * s) { //if (hasID(s)) { // s->debugInfo("valc"); //} QList result; foreach (ConnectorItem * d, sd.values(s)) { if (d == NULL) continue; if (sd.values(d).count() == 0) continue; result << d; //if (hasID(s)) d->debugInfo("\t"); } return result; } //////////////////////////////////////////////////////////////////// MazeRouter::MazeRouter(PCBSketchWidget * sketchWidget, QGraphicsItem * board, bool adjustIf) : Autorouter(sketchWidget) { m_netLabelIndex = -1; m_grid = NULL; m_displayItem[0] = m_displayItem[1] = NULL; m_boardImage = m_spareImage = m_spareImage2 = m_displayImage[0] = m_displayImage[1] = NULL; CancelledMessage = tr("Autorouter was cancelled."); QSettings settings; m_maxCycles = settings.value(MaxCyclesName, DefaultMaxCycles).toInt(); m_bothSidesNow = sketchWidget->routeBothSides(); m_pcbType = sketchWidget->autorouteTypePCB(); m_board = board; m_temporaryBoard = false; if (m_board) { m_maxRect = m_board->sceneBoundingRect(); } else { QRectF itemsBoundingRect; foreach(QGraphicsItem *item, m_sketchWidget->scene()->items()) { if (!item->isVisible()) continue; itemsBoundingRect |= item->sceneBoundingRect(); } m_maxRect = itemsBoundingRect; // itemsBoundingRect is not reliable. m_sketchWidget->scene()->itemsBoundingRect(); if (adjustIf) { m_maxRect.adjust(-m_maxRect.width() / 2, -m_maxRect.height() / 2, m_maxRect.width() / 2, m_maxRect.height() / 2); } m_board = new QGraphicsRectItem(m_maxRect); m_board->setVisible(false); m_sketchWidget->scene()->addItem(m_board); m_temporaryBoard = true; } m_standardWireWidth = m_sketchWidget->getAutorouterTraceWidth(); /* // for debugging leave the last result hanging around QList pixmapItems; foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) { QGraphicsPixmapItem * pixmapItem = dynamic_cast(item); if (pixmapItem) pixmapItems << pixmapItem; } foreach (QGraphicsPixmapItem * pixmapItem, pixmapItems) { delete pixmapItem; } */ ViewGeometry vg; vg.setWireFlags(m_sketchWidget->getTraceFlag()); ViewLayer::ViewLayerID bottom = sketchWidget->getWireViewLayerID(vg, ViewLayer::NewBottom); m_viewLayerIDs << bottom; if (m_bothSidesNow) { ViewLayer::ViewLayerID top = sketchWidget->getWireViewLayerID(vg, ViewLayer::NewTop); m_viewLayerIDs.append(top); } } MazeRouter::~MazeRouter() { foreach (QDomDocument * doc, m_masterDocs) { delete doc; } if (m_displayItem[0]) { delete m_displayItem[0]; } if (m_displayItem[1]) { delete m_displayItem[1]; } if (m_displayImage[0]) { delete m_displayImage[0]; } if (m_displayImage[1]) { delete m_displayImage[1]; } if (m_temporaryBoard && m_board != NULL) { delete m_board; } if (m_grid) { m_grid->free(); delete m_grid; } if (m_boardImage) { delete m_boardImage; } if (m_spareImage) { delete m_spareImage; } if (m_spareImage2) { delete m_spareImage2; } } void MazeRouter::start() { if (m_pcbType) { if (m_board == NULL) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Cannot autoroute: no board (or multiple boards) found")); return; } m_jumperWillFitFunction = jumperWillFit; m_costFunction = distanceCost; m_traceColors[0] = 0xa0F28A00; m_traceColors[1] = 0xa0FFCB33; } else { m_jumperWillFitFunction = schematicJumperWillFit; m_costFunction = manhattanCost; m_traceColors[0] = m_traceColors[1] = 0xa0303030; } m_keepoutPixels = m_sketchWidget->getKeepout(); // 15 mils space (in pixels) m_gridPixels = qMax(m_standardWireWidth, m_keepoutPixels); m_keepoutMils = m_keepoutPixels * GraphicsUtils::StandardFritzingDPI / GraphicsUtils::SVGDPI; m_keepoutGrid = m_keepoutPixels / m_gridPixels; m_keepoutGridInt = qCeil(m_keepoutGrid); double ringThickness, holeSize; m_sketchWidget->getViaSize(ringThickness, holeSize); int gridViaSize = qCeil((ringThickness + ringThickness + holeSize + m_keepoutPixels + m_keepoutPixels) / m_gridPixels); m_halfGridViaSize = gridViaSize / 2; QSizeF jumperSize = m_sketchWidget->jumperItemSize(); int gridJumperSize = qCeil((qMax(jumperSize.width(), jumperSize.height()) + m_keepoutPixels + m_keepoutPixels) / m_gridPixels); m_halfGridJumperSize = gridJumperSize / 2; emit setMaximumProgress(m_maxCycles); emit setProgressMessage(""); emit setCycleMessage("round 1 of:"); emit setCycleCount(m_maxCycles); m_sketchWidget->ensureTraceLayersVisible(); QHash indexer; m_sketchWidget->collectAllNets(indexer, m_allPartConnectorItems, false, m_bothSidesNow); removeOffBoardAnd(m_pcbType, true, m_bothSidesNow); if (m_allPartConnectorItems.count() == 0) { QString message = m_pcbType ? QObject::tr("No connections (on the PCB) to route.") : QObject::tr("No connections to route."); QMessageBox::information(NULL, QObject::tr("Fritzing"), message); Autorouter::cleanUpNets(); return; } QUndoCommand * parentCommand = new QUndoCommand("Autoroute"); new CleanUpWiresCommand(m_sketchWidget, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(m_sketchWidget, CleanUpWiresCommand::UndoOnly, parentCommand); initUndo(parentCommand); NetList netList; int totalToRoute = 0; for (int i = 0; i < m_allPartConnectorItems.count(); i++) { Net * net = new Net; net->net = m_allPartConnectorItems[i]; //foreach (ConnectorItem * connectorItem, *(net->net)) { // connectorItem->debugInfo("all parts"); //} QList todo; todo.append(*(net->net)); while (todo.count() > 0) { ConnectorItem * first = todo.takeFirst(); QList equi; equi.append(first); ConnectorItem::collectEqualPotential(equi, m_bothSidesNow, (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ m_sketchWidget->getTraceFlag()); foreach (ConnectorItem * equ, equi) { todo.removeOne(equ); //equ->debugInfo("equi"); } net->subnets.append(equi); } if (net->subnets.count() < 2) { // net is already routed continue; } net->pinsWithin = findPinsWithin(net->net); netList.nets << net; totalToRoute += net->net->count() - 1; } qSort(netList.nets.begin(), netList.nets.end(), byPinsWithin); NetOrdering initialOrdering; int ix = 0; foreach (Net * net, netList.nets) { // id is the same as the order in netList initialOrdering.order << ix; net->id = ix++; } if (m_bothSidesNow) { emit wantBothVisible(); } ProcessEventBlocker::processEvents(); // to keep the app from freezing if (m_cancelled || m_stopTracing) { restoreOriginalState(parentCommand); cleanUpNets(netList); return; } QSizeF gridSize(m_maxRect.width() / m_gridPixels, m_maxRect.height() / m_gridPixels); QSize boardImageSize(qCeil(gridSize.width()), qCeil(gridSize.height())); m_grid = new Grid(boardImageSize.width(), boardImageSize.height(), m_bothSidesNow ? 2 : 1); if (m_grid->data == NULL) { QMessageBox::information(NULL, QObject::tr("Fritzing"), "Out of memory--unable to proceed"); restoreOriginalState(parentCommand); cleanUpNets(netList); return; } m_boardImage = new QImage(boardImageSize.width() * 4, boardImageSize.height() * 4, QImage::Format_Mono); m_spareImage = new QImage(boardImageSize.width() * 4, boardImageSize.height() * 4, QImage::Format_Mono); if (m_temporaryBoard) { m_boardImage->fill(0xffffffff); } else { m_boardImage->fill(0); QRectF r4(QPointF(0, 0), gridSize * 4); makeBoard(m_boardImage, m_keepoutGrid * 4, r4); } GraphicsUtils::drawBorder(m_boardImage, 4); ProcessEventBlocker::processEvents(); // to keep the app from freezing if (m_cancelled || m_stopTracing) { restoreOriginalState(parentCommand); cleanUpNets(netList); return; } m_displayImage[0] = new QImage(boardImageSize, QImage::Format_ARGB32); m_displayImage[0]->fill(0); m_displayImage[1] = new QImage(boardImageSize, QImage::Format_ARGB32); m_displayImage[1]->fill(0); QString message; bool gotMasters = makeMasters(message); if (m_cancelled || m_stopTracing || !gotMasters) { restoreOriginalState(parentCommand); cleanUpNets(netList); return; } QList allOrderings; allOrderings << initialOrdering; Score bestScore; Score currentScore; int run = 0; for (; run < m_maxCycles && run < allOrderings.count(); run++) { QString msg= tr("best so far: %1 of %2 routed").arg(bestScore.totalRoutedCount).arg(totalToRoute); if (m_pcbType) { msg += tr(" with %n vias", "", bestScore.totalViaCount); } emit setProgressMessage(msg); emit setCycleMessage(tr("round %1 of:").arg(run + 1)); emit setProgressValue(run); ProcessEventBlocker::processEvents(); currentScore.setOrdering(allOrderings.at(run)); currentScore.anyUnrouted = false; routeNets(netList, false, currentScore, gridSize, allOrderings); if (bestScore.ordering.order.count() == 0) { bestScore = currentScore; } else { if (currentScore.totalRoutedCount > bestScore.totalRoutedCount) { bestScore = currentScore; } else if (currentScore.totalRoutedCount == bestScore.totalRoutedCount && currentScore.totalViaCount < bestScore.totalViaCount) { bestScore = currentScore; } } if (m_cancelled || bestScore.anyUnrouted == false || m_stopTracing) break; } emit disableButtons(); //DebugDialog::debug("done running"); if (m_cancelled) { doCancel(parentCommand); return; } if (m_stopTracing) { QString msg = tr("Routing stopped!"); msg += " "; if (m_useBest) msg += tr("Use best so far..."); emit setProgressMessage(msg); if (m_useBest) { routeNets(netList, true, bestScore, gridSize, allOrderings); } } else if (!bestScore.anyUnrouted) { emit setProgressMessage(tr("Routing complete!")); emit setProgressValue(m_maxCycles); } else { emit setCycleMessage(tr("round %1 of:").arg(run)); QString msg; if (run < m_maxCycles) msg = tr("Routing unsuccessful; stopping at round %1.").arg(run); else msg = tr("Routing reached maximum round %1.").arg(m_maxCycles); msg += " "; msg += tr("Use best so far..."); emit setProgressMessage(msg); printOrder("best ", bestScore.ordering.order); routeNets(netList, true, bestScore, gridSize, allOrderings); emit setProgressValue(m_maxCycles); } ProcessEventBlocker::processEvents(); if (m_grid) { m_grid->free(); delete m_grid; m_grid = NULL; } if (m_boardImage) { delete m_boardImage; m_boardImage = NULL; } if (m_spareImage) { delete m_spareImage; m_spareImage = NULL; } m_boardImage = new QImage(m_maxRect.width() * OptimizeFactor, m_maxRect.height() * OptimizeFactor, QImage::Format_Mono); m_spareImage = new QImage(m_maxRect.width() * OptimizeFactor, m_maxRect.height() * OptimizeFactor, QImage::Format_Mono); m_spareImage2 = new QImage(m_maxRect.width() * OptimizeFactor, m_maxRect.height() * OptimizeFactor, QImage::Format_Mono); if (m_temporaryBoard) { m_boardImage->fill(0xffffffff); } else { m_boardImage->fill(0); QRectF r2(0, 0, m_boardImage->width(), m_boardImage->height()); makeBoard(m_boardImage, m_keepoutPixels * 2, r2); } GraphicsUtils::drawBorder(m_boardImage, 2); createTraces(netList, bestScore, parentCommand); cleanUpNets(netList); new CleanUpRatsnestsCommand(m_sketchWidget, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(m_sketchWidget, CleanUpWiresCommand::RedoOnly, parentCommand); m_sketchWidget->blockUI(true); m_commandCount = BaseCommand::totalChildCount(parentCommand); emit setMaximumProgress(m_commandCount); emit setProgressMessage2(tr("Preparing undo...")); if (m_displayItem[0]) { m_displayItem[0]->setVisible(false); } if (m_displayItem[1]) { m_displayItem[1]->setVisible(false); } ProcessEventBlocker::processEvents(); m_cleanupCount = 0; m_sketchWidget->pushCommand(parentCommand, this); m_sketchWidget->blockUI(false); m_sketchWidget->repaint(); DebugDialog::debug("\n\n\nautorouting complete\n\n\n"); } int MazeRouter::findPinsWithin(QList * net) { int count = 0; QRectF r; foreach (ConnectorItem * connectorItem, *net) { r |= connectorItem->sceneBoundingRect(); } foreach (QGraphicsItem * item, m_sketchWidget->scene()->items(r)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (net->contains(connectorItem)) continue; count++; } return count; } bool MazeRouter::makeBoard(QImage * boardImage, double keepoutGrid, const QRectF & renderRect) { LayerList viewLayerIDs; viewLayerIDs << ViewLayer::Board; RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString boardSvg = m_sketchWidget->renderToSVG(renderThing, m_board, viewLayerIDs); if (boardSvg.isEmpty()) { return false; } QByteArray boardByteArray; QString tempColor("#ffffff"); QStringList exceptions; exceptions << "none" << ""; if (!SvgFileSplitter::changeColors(boardSvg, tempColor, exceptions, boardByteArray)) { return false; } QSvgRenderer renderer(boardByteArray); QPainter painter; painter.begin(boardImage); painter.setRenderHint(QPainter::Antialiasing, false); renderer.render(&painter, renderRect); painter.end(); // board should be white, borders should be black #ifndef QT_NO_DEBUG //boardImage->save(FolderUtils::getUserDataStorePath("") + "/mazeMakeBoard.png"); #endif // extend it given that the board image is * 4 DRC::extendBorder(keepoutGrid, boardImage); #ifndef QT_NO_DEBUG //boardImage->save(FolderUtils::getUserDataStorePath("") + "/mazeMakeBoard2.png"); #endif return true; } bool MazeRouter::makeMasters(QString & message) { QList layerSpecs; layerSpecs << ViewLayer::NewBottom; if (m_bothSidesNow) layerSpecs << ViewLayer::NewTop; foreach (ViewLayer::ViewLayerPlacement viewLayerPlacement, layerSpecs) { LayerList viewLayerIDs = m_sketchWidget->routingLayers(viewLayerPlacement); RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = renderThing.selectedItems = renderThing.renderBlocker = false; QString master = m_sketchWidget->renderToSVG(renderThing, m_board, viewLayerIDs); if (master.isEmpty()) { continue; } QDomDocument * masterDoc = new QDomDocument(); m_masterDocs.insert(viewLayerPlacement, masterDoc); QString errorStr; int errorLine; int errorColumn; if (!masterDoc->setContent(master, &errorStr, &errorLine, &errorColumn)) { message = tr("Unexpected SVG rendering failure--contact fritzing.org"); return false; } ProcessEventBlocker::processEvents(); if (m_cancelled) { message = CancelledMessage; return false; } QDomElement root = masterDoc->documentElement(); SvgFileSplitter::forceStrokeWidth(root, 2 * m_keepoutMils, "#000000", true, true); //QString forDebugging = masterDoc->toByteArray(); //DebugDialog::debug("master " + forDebugging); } return true; } bool MazeRouter::routeNets(NetList & netList, bool makeJumper, Score & currentScore, const QSizeF gridSize, QList & allOrderings) { RouteThing routeThing; routeThing.netElements[0] = NetElements(); routeThing.netElements[1] = NetElements(); routeThing.r = QRectF(QPointF(0, 0), gridSize); routeThing.r4 = QRectF(QPointF(0, 0), gridSize * 4); routeThing.layerSpecs << ViewLayer::NewBottom; if (m_bothSidesNow) routeThing.layerSpecs << ViewLayer::NewTop; bool result = true; initTraceDisplay(); bool previousTraces = false; foreach (int netIndex, currentScore.ordering.order) { if (m_cancelled || m_stopTracing) { return false; } Net * net = netList.nets.at(netIndex); /* DebugDialog::debug(QString("routing net %1, subnets %2, traces %3, routed %4") .arg(netIndex) .arg(net->subnets.count()) .arg(currentScore.traces.values(netIndex).count()) .arg(currentScore.routedCount.value(netIndex)) ); */ if (currentScore.routedCount.value(netIndex) == net->subnets.count() - 1) { // this net was fully routed in a previous run foreach (Trace trace, currentScore.traces.values(netIndex)) { displayTrace(trace); } previousTraces = true; continue; } if (previousTraces) { updateDisplay(0); if (m_bothSidesNow) updateDisplay(1); } if (currentScore.routedCount.value(netIndex) > 0) { // should only be here when makeJumpers = true // remove the set of routed traces for this net--the net was not completely routed // we didn't get all the way through before currentScore.totalRoutedCount -= currentScore.routedCount.value(netIndex); currentScore.routedCount.insert(netIndex, 0); currentScore.totalViaCount -= currentScore.viaCount.value(netIndex); currentScore.viaCount.insert(netIndex, 0); currentScore.traces.remove(netIndex); } //foreach (ConnectorItem * connectorItem, *(net->net)) { // if (connectorItem->attachedTo()->layerKinChief()->id() == 12407630) { // connectorItem->debugInfo("what"); // break; // } //} QList< QList > subnets; foreach (QList subnet, net->subnets) { QList copy(subnet); subnets.append(copy); } //DebugDialog::debug("find nearest pair"); findNearestPair(subnets, routeThing.nearest); QPointF ip = routeThing.nearest.ic->sceneAdjustedTerminalPoint(NULL) - m_maxRect.topLeft(); routeThing.gridSourcePoint = QPoint(ip.x() / m_gridPixels, ip.y() / m_gridPixels); QPointF jp = routeThing.nearest.jc->sceneAdjustedTerminalPoint(NULL) - m_maxRect.topLeft(); routeThing.gridTargetPoint = QPoint(jp.x() / m_gridPixels, jp.y() / m_gridPixels); m_grid->clear(); m_grid->init4(0, 0, 0, m_grid->x, m_grid->y, m_boardImage, GridBoardObstacle, false); if (m_bothSidesNow) { m_grid->copy(0, 1); } QList traces = currentScore.traces.values(); if (m_pcbType) { traceObstacles(traces, netIndex, m_grid, m_keepoutGridInt); } else { traceAvoids(traces, netIndex, routeThing); } foreach (ViewLayer::ViewLayerPlacement viewLayerPlacement, routeThing.layerSpecs) { int z = viewLayerPlacement == ViewLayer::NewBottom ? 0 : 1; QDomDocument * masterDoc = m_masterDocs.value(viewLayerPlacement); //QString before = masterDoc->toString(); Markers markers; initMarkers(markers, m_pcbType); DRC::splitNetPrep(masterDoc, *(net->net), markers, routeThing.netElements[z].net, routeThing.netElements[z].alsoNet, routeThing.netElements[z].notNet, true); foreach (QDomElement element, routeThing.netElements[z].net) { element.setTagName("g"); } foreach (QDomElement element, routeThing.netElements[z].alsoNet) { element.setTagName("g"); } //QString after = masterDoc->toString(); //DebugDialog::debug("obstacles from board"); m_spareImage->fill(0xffffffff); ItemBase::renderOne(masterDoc, m_spareImage, routeThing.r4); #ifndef QT_NO_DEBUG //m_spareImage->save(FolderUtils::getUserDataStorePath("") + QString("/obstacles%1_%2.png").arg(netIndex, 2, 10, QChar('0')).arg(viewLayerPlacement)); #endif m_grid->init4(0, 0, z, m_grid->x, m_grid->y, m_spareImage, GridPartObstacle, false); //DebugDialog::debug("obstacles from board done"); prepSourceAndTarget(masterDoc, routeThing, subnets, z, viewLayerPlacement); } //updateDisplay(m_grid, 0); //if (m_bothSidesNow) updateDisplay(m_grid, 1); //DebugDialog::debug(QString("before route one %1").arg(netIndex)); routeThing.unrouted = false; if (!routeOne(makeJumper, currentScore, netIndex, routeThing, allOrderings)) { result = false; } //DebugDialog::debug(QString("after route one %1 %2").arg(netIndex).arg(result)); while (result && subnets.count() > 2) { /* DebugDialog::debug(QString("\nnearest %1 %2").arg(routeThing.nearest.i).arg(routeThing.nearest.j)); routeThing.nearest.ic->debugInfo("\ti"); routeThing.nearest.jc->debugInfo("\tj"); int ix = 0; foreach (QList subnet, subnets) { foreach(ConnectorItem * connectorItem, subnet) { connectorItem->debugInfo(QString::number(ix)); } ix++; } */ result = routeNext(makeJumper, routeThing, subnets, currentScore, netIndex, allOrderings); } routeThing.netElements[0].net.clear(); routeThing.netElements[0].notNet.clear(); routeThing.netElements[0].alsoNet.clear(); routeThing.netElements[1].net.clear(); routeThing.netElements[1].notNet.clear(); routeThing.netElements[1].alsoNet.clear(); routeThing.sourceQ = std::priority_queue(); routeThing.targetQ = std::priority_queue(); if (result == false) break; } return result; } bool MazeRouter::routeOne(bool makeJumper, Score & currentScore, int netIndex, RouteThing & routeThing, QList & allOrderings) { //DebugDialog::debug("start route()"); Trace newTrace; int viaCount; routeThing.bestDistanceToSource = routeThing.bestDistanceToTarget = std::numeric_limits::max(); //DebugDialog::debug(QString("jumper d %1, %2").arg(routeThing.bestDistanceToSource).arg(routeThing.bestDistanceToTarget)); newTrace.gridPoints = route(routeThing, viaCount); if (m_cancelled || m_stopTracing) { return false; } //DebugDialog::debug("after route()"); if (newTrace.gridPoints.count() == 0) { if (makeJumper) { routeJumper(netIndex, routeThing, currentScore); } else { routeThing.unrouted = true; if (currentScore.reorderNet < 0) { for (int i = 0; i < currentScore.ordering.order.count(); i++) { if (currentScore.ordering.order.at(i) == netIndex) { if (moveBack(currentScore, i, allOrderings)) { currentScore.reorderNet = netIndex; } break; } } } currentScore.anyUnrouted = true; if (currentScore.reorderNet >= 0) { // rip up and reroute unless this net is already the first one on the list return false; } // unable to move the 0th net so keep going } } else { insertTrace(newTrace, netIndex, currentScore, viaCount, true); updateDisplay(0); if (m_bothSidesNow) updateDisplay(1); } //DebugDialog::debug("end routeOne()"); return true; } bool MazeRouter::routeNext(bool makeJumper, RouteThing & routeThing, QList< QList > & subnets, Score & currentScore, int netIndex, QList & allOrderings) { bool result = true; QList combined; if (routeThing.unrouted) { if (routeThing.nearest.i < routeThing.nearest.j) { subnets.removeAt(routeThing.nearest.j); combined = subnets.takeAt(routeThing.nearest.i); } else { combined = subnets.takeAt(routeThing.nearest.i); subnets.removeAt(routeThing.nearest.j); } } else { combined.append(subnets.at(routeThing.nearest.i)); combined.append(subnets.at(routeThing.nearest.j)); if (routeThing.nearest.i < routeThing.nearest.j) { subnets.removeAt(routeThing.nearest.j); subnets.removeAt(routeThing.nearest.i); } else { subnets.removeAt(routeThing.nearest.i); subnets.removeAt(routeThing.nearest.j); } } subnets.prepend(combined); routeThing.nearest.i = 0; routeThing.nearest.j = -1; routeThing.nearest.distance = std::numeric_limits::max(); findNearestPair(subnets, 0, combined, routeThing.nearest); QPointF ip = routeThing.nearest.ic->sceneAdjustedTerminalPoint(NULL) - m_maxRect.topLeft(); routeThing.gridSourcePoint = QPoint(ip.x() / m_gridPixels, ip.y() / m_gridPixels); QPointF jp = routeThing.nearest.jc->sceneAdjustedTerminalPoint(NULL) - m_maxRect.topLeft(); routeThing.gridTargetPoint = QPoint(jp.x() / m_gridPixels, jp.y() / m_gridPixels); routeThing.sourceQ = std::priority_queue(); routeThing.targetQ = std::priority_queue(); if (!m_pcbType) { QList traces = currentScore.traces.values(); traceAvoids(traces, netIndex, routeThing); } foreach (ViewLayer::ViewLayerPlacement viewLayerPlacement, routeThing.layerSpecs) { int z = viewLayerPlacement == ViewLayer::NewBottom ? 0 : 1; QDomDocument * masterDoc = m_masterDocs.value(viewLayerPlacement); prepSourceAndTarget(masterDoc, routeThing, subnets, z, viewLayerPlacement); } // redraw traces from this net foreach (Trace trace, currentScore.traces.values(netIndex)) { foreach (GridPoint gridPoint, trace.gridPoints) { m_grid->setAt(gridPoint.x, gridPoint.y, gridPoint.z, GridSource); gridPoint.qCost = gridPoint.baseCost = /* initialCost(QPoint(gridPoint.x, gridPoint.y), routeThing.gridTarget) + */ 0; gridPoint.flags = 0; //DebugDialog::debug(QString("pushing trace %1 %2 %3, %4, %5").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z).arg(gridPoint.qCost).arg(routeThing.pq.size())); routeThing.sourceQ.push(gridPoint); } } //updateDisplay(m_grid, 0); //if (m_bothSidesNow) updateDisplay(m_grid, 1); routeThing.unrouted = false; result = routeOne(makeJumper, currentScore, netIndex, routeThing, allOrderings); return result; } bool MazeRouter::moveBack(Score & currentScore, int index, QList & allOrderings) { if (index == 0) { return false; // nowhere to move back to } QList order(currentScore.ordering.order); //printOrder("start", order); int netIndex = order.takeAt(index); //printOrder("minus", order); for (int i = index - 1; i >= 0; i--) { bool done = true; order.insert(i, netIndex); //printOrder("plus ", order); foreach (NetOrdering ordering, allOrderings) { bool gotOne = true; for (int j = 0; j < order.count(); j++) { if (order.at(j) != ordering.order.at(j)) { gotOne = false; break; } } if (gotOne) { done = false; break; } } if (done == true) { NetOrdering newOrdering; newOrdering.order = order; allOrderings.append(newOrdering); //printOrder("done ", newOrdering.order); /* const static int watch[] = { 0, 1, 2, 3, 6, 12, 14, 4, 5, 7, 8, 9, 10, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 }; // {0, 1, 2, 4, 3, 12, 6, 14, 5, 7, 8, 9, 10, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64}; bool matches = true; for (int i = 0; i < order.count(); i++) { if (order.at(i) != watch[i]) { matches = false; break; } } if (matches) { DebugDialog::debug("order matches"); } */ return true; } order.removeAt(i); } return false; } void MazeRouter::prepSourceAndTarget(QDomDocument * masterDoc, RouteThing & routeThing, QList< QList > & subnets, int z, ViewLayer::ViewLayerPlacement viewLayerPlacement) { foreach (QDomElement element, routeThing.netElements[z].notNet) { element.setTagName("g"); } foreach (QDomElement element, routeThing.netElements[z].alsoNet) { element.setTagName("g"); } //QString debug = masterDoc->toString(4); foreach (QDomElement element, routeThing.netElements[z].net) { // QString str; // QTextStream stream(&str); // element.save(stream, 0); // DebugDialog::debug(str); SvgFileSplitter::forceStrokeWidth(element, -2 * m_keepoutMils, "#000000", false, false); } QList li = subnets.at(routeThing.nearest.i); QList sourcePoints = renderSource(masterDoc, z, viewLayerPlacement, m_grid, routeThing.netElements[z].net, li, GridSource, true, routeThing.r4); foreach (QPoint p, sourcePoints) { GridPoint gridPoint(p, z); gridPoint.qCost = gridPoint.baseCost = /* initialCost(p, routeThing.gridTarget) + */ 0; //DebugDialog::debug(QString("pushing source %1 %2 %3, %4, %5").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z).arg(gridPoint.qCost).arg(routeThing.pq.size())); routeThing.sourceQ.push(gridPoint); } QList lj = subnets.at(routeThing.nearest.j); QList targetPoints = renderSource(masterDoc, z, viewLayerPlacement, m_grid, routeThing.netElements[z].net, lj, GridTarget, true, routeThing.r4); foreach (QPoint p, targetPoints) { GridPoint gridPoint(p, z); gridPoint.qCost = gridPoint.baseCost = /* initialCost(p, routeThing.gridTarget) + */ 0; //DebugDialog::debug(QString("pushing source %1 %2 %3, %4, %5").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z).arg(gridPoint.qCost).arg(routeThing.pq.size())); routeThing.targetQ.push(gridPoint); } foreach (QDomElement element, routeThing.netElements[z].net) { SvgFileSplitter::forceStrokeWidth(element, 2 * m_keepoutMils, "#000000", false, false); } // restore masterdoc foreach (QDomElement element, routeThing.netElements[z].net) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } foreach (QDomElement element, routeThing.netElements[z].notNet) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } foreach (QDomElement element, routeThing.netElements[z].alsoNet) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } } void MazeRouter::findNearestPair(QList< QList > & subnets, Nearest & nearest) { nearest.distance = std::numeric_limits::max(); nearest.i = nearest.j = -1; nearest.ic = nearest.jc = NULL; for (int i = 0; i < subnets.count() - 1; i++) { QList inet = subnets.at(i); findNearestPair(subnets, i, inet, nearest); } } void MazeRouter::findNearestPair(QList< QList > & subnets, int inetix, QList & inet, Nearest & nearest) { for (int j = inetix + 1; j < subnets.count(); j++) { QList jnet = subnets.at(j); foreach (ConnectorItem * ic, inet) { QPointF ip = ic->sceneAdjustedTerminalPoint(NULL); ConnectorItem * icc = ic->getCrossLayerConnectorItem(); foreach (ConnectorItem * jc, jnet) { ConnectorItem * jcc = jc->getCrossLayerConnectorItem(); if (jc == ic || jcc == ic) continue; QPointF jp = jc->sceneAdjustedTerminalPoint(NULL); double d = qSqrt(GraphicsUtils::distanceSqd(ip, jp)) / m_gridPixels; if (ic->attachedToViewLayerID() != jc->attachedToViewLayerID()) { if (jcc != NULL || icc != NULL) { // may not need a via d += CrossLayerCost; } else { // requires at least one via d += ViaCost; } } else { if (jcc != NULL && icc != NULL && ic->attachedToViewLayerID() == ViewLayer::Copper1) { // route on the bottom when possible d += Layer1Cost; } } if (d < nearest.distance) { nearest.distance = d; nearest.i = inetix; nearest.j = j; nearest.ic = ic; nearest.jc = jc; } } } } } QList MazeRouter::renderSource(QDomDocument * masterDoc, int z, ViewLayer::ViewLayerPlacement viewLayerPlacement, Grid * grid, QList & netElements, QList & subnet, GridValue value, bool clearElements, const QRectF & renderRect) { if (clearElements) { foreach (QDomElement element, netElements) { element.setTagName("g"); } } m_spareImage->fill(0xffffffff); QMultiHash partIDs; QMultiHash terminalIDs; QList terminalPoints; QRectF itemsBoundingRect; foreach (ConnectorItem * connectorItem, subnet) { ItemBase * itemBase = connectorItem->attachedTo(); SvgIdLayer * svgIdLayer = connectorItem->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID()); partIDs.insert(QString::number(itemBase->id()), svgIdLayer->m_svgId); if (!svgIdLayer->m_terminalId.isEmpty()) { terminalIDs.insert(QString::number(itemBase->id()), svgIdLayer->m_terminalId); terminalPoints << connectorItem; } itemsBoundingRect |= connectorItem->sceneBoundingRect(); } foreach (QDomElement element, netElements) { if (idsMatch(element, partIDs)) { element.setTagName(element.attribute("former")); } else if (idsMatch(element, terminalIDs)) { element.setTagName(element.attribute("former")); } } int x1 = qFloor((itemsBoundingRect.left() - m_maxRect.left()) / m_gridPixels); int y1 = qFloor((itemsBoundingRect.top() - m_maxRect.top()) / m_gridPixels); int x2 = qCeil((itemsBoundingRect.right() - m_maxRect.left()) / m_gridPixels); int y2 = qCeil((itemsBoundingRect.bottom() - m_maxRect.top()) / m_gridPixels); ItemBase::renderOne(masterDoc, m_spareImage, renderRect); #ifndef QT_NO_DEBUG //static int rsi = 0; //m_spareImage->save(FolderUtils::getUserDataStorePath("") + QString("/rendersource%1_%2.png").arg(rsi++,3,10,QChar('0')).arg(z)); #endif QList points = grid->init4(x1, y1, z, x2 - x1, y2 - y1, m_spareImage, value, true); // terminal point hack (mostly for schematic view) foreach (ConnectorItem * connectorItem, terminalPoints) { if (ViewLayer::specFromID(connectorItem->attachedTo()->viewLayerID()) != viewLayerPlacement) { continue; } QPointF p = connectorItem->sceneAdjustedTerminalPoint(NULL); QRectF r = connectorItem->attachedTo()->sceneBoundingRect().adjusted(-m_keepoutPixels, -m_keepoutPixels, m_keepoutPixels, m_keepoutPixels); QPointF closest(p.x(), r.top()); double d = qAbs(p.y() - r.top()); int dx = 0; int dy = -1; if (qAbs(p.y() - r.bottom()) < d) { d = qAbs(p.y() - r.bottom()); closest = QPointF(p.x(), r.bottom()); dy = 1; dx = 0; } if (qAbs(p.x() - r.left()) < d) { d = qAbs(p.x() - r.left()); closest = QPointF(r.left(), p.y()); dx = -1; dy = 0; } if (qAbs(p.x() - r.right()) < d) { d = qAbs(p.x() - r.right()); closest = QPointF(r.right(), p.y()); dy = 0; dx = 1; } double y1, y2, x1, x2; double yp = (p.y() - m_maxRect.top()) / m_gridPixels; double xp = (p.x() - m_maxRect.left()) / m_gridPixels; double yc = (closest.y() - m_maxRect.top()) / m_gridPixels; double xc = (closest.x() - m_maxRect.left()) / m_gridPixels; if (closest.x() == p.x()) { x1 = x2 = qRound((p.x() - m_maxRect.left()) / m_gridPixels); y1 = (yp < yc) ? qFloor(yp) : qCeil(yp); y2 = (yp < yc) ? qCeil(yc) : qFloor(yc); if (y2 < y1) { double temp = y2; y2 = y1; y1 = temp; } } else { y1 = y2 = qRound((p.y() - m_maxRect.top()) / m_gridPixels); x1 = (xp < xc) ? qFloor(xp) : qCeil(xp); x2 = (xp < xc) ? qCeil(xc) : qFloor(xc); if (x2 < x1) { double temp = x2; x2 = x1; x1 = temp; } } int xo = qRound(xc); int yo = qRound(yc); // remove obstacles we can draw through while (true) { xo += dx; yo += dy; if (grid->at(xo, yo, z) != GridAvoid) break; grid->setAt(xo, yo, z, 0); } for (int iy = y1; iy <= y2; iy++) { for (int ix = x1; ix <= x2; ix++) { GridValue val = grid->at(ix, iy, z); if (val == GridPartObstacle || val == GridAvoid) { // make an empty path up to the source point grid->setAt(ix, iy, z, 0); } } } int xr = qRound(xp); int yr = qRound(yp); if (grid->at(xr, yr, z) != GridBoardObstacle) { grid->setAt(xr, yr, z, value); points << QPoint(xr, yr); } } return points; } QList MazeRouter::route(RouteThing & routeThing, int & viaCount) { //DebugDialog::debug(QString("start route() %1").arg(routeNumber++)); viaCount = 0; GridPoint done; bool result = false; while (!routeThing.sourceQ.empty() && !routeThing.targetQ.empty()) { GridPoint gp = routeThing.sourceQ.top(); GridPoint gpt = routeThing.targetQ.top(); if (gpt.qCost < gp.qCost) { gp = gpt; routeThing.targetQ.pop(); routeThing.targetValue = GridSource; routeThing.sourceValue = GridTarget; } else { routeThing.sourceQ.pop(); routeThing.targetValue = GridTarget; routeThing.sourceValue = GridSource; } if (gp.flags & GridPointDone) { done = gp; result = true; break; } expand(gp, routeThing); if (m_cancelled || m_stopTracing) { break; } } //DebugDialog::debug(QString("routing result %1").arg(result)); QList points; if (!result) { //updateDisplay(m_grid, 0); //DebugDialog::debug(QString("done routing no points")); return points; } done.baseCost = std::numeric_limits::max(); // make sure this is the largest value for either traceback QList sourcePoints = traceBack(done, m_grid, viaCount, GridTarget, GridSource); // trace back to source QList targetPoints = traceBack(done, m_grid, viaCount, GridSource, GridTarget); // trace back to target if (sourcePoints.count() == 0 || targetPoints.count() == 0) { DebugDialog::debug("traceback zero points"); return points; } else { targetPoints.takeFirst(); // redundant point foreach (GridPoint gp, targetPoints) points.prepend(gp); points.append(sourcePoints); } clearExpansion(m_grid); //DebugDialog::debug(QString("done with route() %1").arg(points.count())); return points; } QList MazeRouter::traceBack(GridPoint gridPoint, Grid * grid, int & viaCount, GridValue sourceValue, GridValue targetValue) { //DebugDialog::debug(QString("traceback %1 %2 %3").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z)); QList points; points << gridPoint; while (true) { if (gridPoint.baseCost == targetValue) { // done break; } // can only be one neighbor with lower value GridPoint next = traceBackOne(gridPoint, grid, -1, 0, 0, sourceValue, targetValue); if (next.baseCost == GridBoardObstacle) { next = traceBackOne(gridPoint, grid, 1, 0, 0, sourceValue, targetValue); if (next.baseCost == GridBoardObstacle) { next = traceBackOne(gridPoint, grid, 0, -1, 0, sourceValue, targetValue); if (next.baseCost == GridBoardObstacle) { next = traceBackOne(gridPoint, grid, 0, 1, 0, sourceValue, targetValue); if (next.baseCost == GridBoardObstacle) { next = traceBackOne(gridPoint, grid, 0, 0, -1, sourceValue, targetValue); if (next.baseCost == GridBoardObstacle) { next = traceBackOne(gridPoint, grid, 0, 0, 1, sourceValue, targetValue); if (next.baseCost == GridBoardObstacle) { // traceback failed--is this possible? points.clear(); break; } } } } } } //if (grid->at(next.x - 1, next.y, next.z) != GridObstacle) next.flags |= GridPointWest; //if (grid->at(next.x + 1, next.y, next.z) != GridObstacle) next.flags |= GridPointEast; //if (grid->at(next.x, next.y - 1, next.z) != GridObstacle) next.flags |= GridPointNorth; //if (grid->at(next.x, next.y + 1, next.z) != GridObstacle) next.flags |= GridPointSouth; points << next; if (next.z != gridPoint.z) viaCount++; gridPoint = next; } /* QString costs("costs "); foreach (GridPoint gridPoint, points) { costs += QString::number(gridPoint.baseCost) + " "; } DebugDialog::debug(costs); */ return points; } GridPoint MazeRouter::traceBackOne(GridPoint & gridPoint, Grid * grid, int dx, int dy, int dz, GridValue sourceValue, GridValue targetValue) { GridPoint next; next.baseCost = GridBoardObstacle; next.x = gridPoint.x + dx; if (next.x < 0 || next.x >= grid->x) { return next; } next.y = gridPoint.y + dy; if (next.y < 0 || next.y >= grid->y) { return next; } next.z = gridPoint.z + dz; if (next.z < 0 || next.z >= grid->z) { return next; } GridValue nextval = grid->at(next.x, next.y, next.z); if (nextval == GridBoardObstacle || nextval == GridPartObstacle || nextval == sourceValue || nextval == 0 || nextval == GridTempObstacle) return next; if (nextval == targetValue) { // done! next.baseCost = targetValue; return next; } if (targetValue == GridSource) { if ((nextval & GridSourceFlag) == 0) return next; nextval ^= GridSourceFlag; } else { if (nextval & GridSourceFlag) return next; } if (nextval < gridPoint.baseCost) { next.baseCost = nextval; } return next; } void MazeRouter::expand(GridPoint & gridPoint, RouteThing & routeThing) { //static bool debugit = false; //if (routeNumber > 41 && routeThing.pq.size() > 8200) debugit = true; //if (debugit) { // DebugDialog::debug(QString("expand %1 %2 %3, %4").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z).arg(routeThing.pq.size())); //} if (gridPoint.x > 0) expandOne(gridPoint, routeThing, -1, 0, 0, false); if (gridPoint.x < m_grid->x - 1) expandOne(gridPoint, routeThing, 1, 0, 0, false); if (gridPoint.y > 0) expandOne(gridPoint, routeThing, 0, -1, 0, false); if (gridPoint.y < m_grid->y - 1) expandOne(gridPoint, routeThing, 0, 1, 0, false); if (m_bothSidesNow) { if (gridPoint.z > 0) expandOne(gridPoint, routeThing, 0, 0, -1, true); if (gridPoint.z < m_grid->z - 1) expandOne(gridPoint, routeThing, 0, 0, 1, true); } //if (debugit) { // DebugDialog::debug("expand done"); //} } void MazeRouter::expandOne(GridPoint & gridPoint, RouteThing & routeThing, int dx, int dy, int dz, bool crossLayer) { GridPoint next; next.x = gridPoint.x + dx; next.y = gridPoint.y + dy; next.z = gridPoint.z + dz; //DebugDialog::debug(QString("expand one %1,%2,%3 cl:%4").arg(next.x).arg(next.y).arg(next.z).arg(crossLayer)); bool writeable = false; bool avoid = false; GridValue nextval = m_grid->at(next.x, next.y, next.z); if (nextval == GridPartObstacle || nextval == GridBoardObstacle || nextval == routeThing.sourceValue || nextval == GridTempObstacle) { //DebugDialog::debug("exit expand one"); return; } if (nextval == routeThing.targetValue) { //DebugDialog::debug("found grid target"); next.flags |= GridPointDone; } else if (nextval == 0) { writeable = true; } else if (nextval == GridAvoid) { bool contains = true; for (int i = 1; i <= 3; i++) { if (!routeThing.avoids.contains(((next.y - (i * dy)) * m_grid->x) + next.x - (i * dx))) { contains = false; break; } } if (contains) { // do not allow more than 3 in a row in the same direction? return; } avoid = writeable = true; if (dx == 0) { if (m_grid->at(next.x - 1, next.y, next.z) == GridAvoid) { m_grid->setAt(next.x - 1, next.y, next.z, GridTempObstacle); } if (m_grid->at(next.x + 1, next.y, next.z) == GridAvoid) { m_grid->setAt(next.x + 1, next.y, next.z, GridTempObstacle); } } else { if (m_grid->at(next.x, next.y - 1, next.z) == GridAvoid) { m_grid->setAt(next.x, next.y - 1, next.z, GridTempObstacle); } if (m_grid->at(next.x, next.y + 1, next.z) == GridAvoid) { m_grid->setAt(next.x, next.y + 1, next.z, GridTempObstacle); } } } else { // already been here: see if source and target expansions have intersected if (routeThing.sourceValue == GridSource) { if (nextval & GridSourceFlag) return; next.flags |= GridPointDone; } else { if ((nextval & GridSourceFlag) == 0) return; next.flags |= GridPointDone; } } // any way to skip viaWillFit or put it off until actually needed? if (crossLayer) { if (!viaWillFit(next, m_grid)) return; // only way to cross layers is with a via //QPointF center = getPixelCenter(next, m_maxRect.topLeft(), m_gridPixels); //DebugDialog::debug(QString("via will fit %1,%2,%3 %4,%5").arg(next.x).arg(next.y).arg(next.z).arg(center.x()).arg(center.y())); } next.baseCost = gridPoint.baseCost; if (crossLayer) { next.baseCost += ViaCost; } else if (avoid) { next.baseCost += AvoidCost; } next.baseCost++; /* int increment = 5; // assume because of obstacles around the board that we can never be off grid from (next.x, next.y) switch(grid->at(next.x - 1, next.y, next.z)) { case GridObstacle: case GridSource: case GridTarget: increment--; default: break; } switch(grid->at(next.x + 1, next.y, next.z)) { case GridObstacle: case GridSource: case GridTarget: increment--; default: break; } switch(grid->at(next.x, next.y - 1, next.z)) { case GridObstacle: case GridSource: case GridTarget: increment--; default: break; } switch(grid->at(next.x, next.y + 1, next.z)) { case GridObstacle: case GridSource: case GridTarget: increment--; default: break; } next.cost += increment; */ if (nextval == routeThing.targetValue) { next.qCost = next.baseCost; } else { double d = (m_costFunction)(QPoint(next.x, next.y), (routeThing.sourceValue == GridSource) ? routeThing.gridTargetPoint : routeThing.gridSourcePoint); next.qCost = next.baseCost + d; if (routeThing.sourceValue == GridSource) { if (d < routeThing.bestDistanceToTarget) { //DebugDialog::debug(QString("best d target %1, %2,%3").arg(d).arg(next.x).arg(next.y)); routeThing.bestDistanceToTarget = d; routeThing.bestLocationToTarget = next; } } else { if (d < routeThing.bestDistanceToSource) { //DebugDialog::debug(QString("best d source %1, %2,%3").arg(d).arg(next.x).arg(next.y)); routeThing.bestDistanceToSource = d; routeThing.bestLocationToSource = next; } } } // can think about pushing multiple points here //DebugDialog::debug(QString("pushing next %1 %2 %3, %4, %5").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z).arg(gridPoint.qCost).arg(routeThing.pq.size())); if (routeThing.sourceValue == GridSource) routeThing.sourceQ.push(next); else routeThing.targetQ.push(next); if (writeable) { GridValue flag = (routeThing.sourceValue == GridSource) ? GridSourceFlag : 0; m_grid->setAt(next.x, next.y, next.z, next.baseCost | flag); } //DebugDialog::debug("done expand one"); //if (routeThing.searchForJumper) { // updateDisplay(next); //} } bool MazeRouter::viaWillFit(GridPoint & gridPoint, Grid * grid) { for (int y = -m_halfGridViaSize; y <= m_halfGridViaSize; y++) { int py = y + gridPoint.y; if (py < 0) return false; if (py >= grid->y) return false; for (int x = -m_halfGridViaSize; x <= m_halfGridViaSize; x++) { int px = x + gridPoint.x; if (px < 0) return false; if (px >= grid->x) return false; for (int z = 0; z < grid->z; z++) { GridValue val = grid->at(px, py, z); if (val == GridPartObstacle || val == GridBoardObstacle || val == GridSource || val == GridTarget || val == GridTempObstacle || val == GridAvoid) return false; } } } return true; } void MazeRouter::updateDisplay(int iz) { QPixmap pixmap = QPixmap::fromImage(*m_displayImage[iz]); if (m_displayItem[iz] == NULL) { m_displayItem[iz] = new QGraphicsPixmapItem(pixmap); m_displayItem[iz]->setFlag(QGraphicsItem::ItemIsSelectable, false); m_displayItem[iz]->setFlag(QGraphicsItem::ItemIsMovable, false); //m_displayItem[iz]->setPos(iz == 1 ? m_maxRect.topLeft() : m_maxRect.topRight()); m_displayItem[iz]->setPos(m_maxRect.topLeft()); m_sketchWidget->scene()->addItem(m_displayItem[iz]); m_displayItem[iz]->setZValue(5000); //m_displayItem[iz]->setZValue(m_sketchWidget->viewLayers().value(iz == 0 ? ViewLayer::Copper0 : ViewLayer::Copper1)->nextZ()); m_displayItem[iz]->setScale(m_gridPixels); // m_maxRect.width() / m_displayImage[iz]->width() m_displayItem[iz]->setVisible(true); } else { m_displayItem[iz]->setPixmap(pixmap); } ProcessEventBlocker::processEvents(); } void MazeRouter::updateDisplay(Grid * grid, int iz) { m_displayImage[iz]->fill(0); for (int y = 0; y < grid->y; y++) { for (int x = 0; x < grid->x; x++) { uint color = getColor(grid->at(x, y, iz)); if (color) m_displayImage[iz]->setPixel(x, y, color); } } updateDisplay(iz); } void MazeRouter::updateDisplay(GridPoint & gridPoint) { //static int counter = 0; //if (counter++ % 2 == 0) { uint color = getColor(m_grid->at(gridPoint.x, gridPoint.y, gridPoint.z)); if (color) { m_displayImage[gridPoint.z]->setPixel(gridPoint.x, gridPoint.y, color); updateDisplay(gridPoint.z); } //} } void MazeRouter::clearExpansion(Grid * grid) { // TODO: keep a list of expansion points instead? for (int z = 0; z < grid->z; z++) { for (int y = 0; y < grid->y; y++) { for (int x = 0; x < grid->x; x++) { GridValue val = grid->at(x, y, z); if (val == 0 || val == GridPartObstacle || val == GridBoardObstacle) ; else grid->setAt(x, y, z, 0); } } } } void MazeRouter::initTraceDisplay() { m_displayImage[0]->fill(0); m_displayImage[1]->fill(0); } void MazeRouter::displayTrace(Trace & trace) { if (trace.gridPoints.count() == 0) { DebugDialog::debug("trace with no points"); return; } int lastz = trace.gridPoints.at(0).z; foreach (GridPoint gridPoint, trace.gridPoints) { if (gridPoint.z != lastz) { for (int y = -m_halfGridViaSize; y <= m_halfGridViaSize; y++) { for (int x = -m_halfGridViaSize; x <= m_halfGridViaSize; x++) { m_displayImage[1]->setPixel(x + gridPoint.x, y + gridPoint.y, 0x80ff0000); } } lastz = gridPoint.z; } else { m_displayImage[lastz]->setPixel(gridPoint.x, gridPoint.y, m_traceColors[lastz]); } } if (trace.flags) { GridPoint gridPoint = trace.gridPoints.first(); int xl, xr; if (trace.flags & JumperLeft) { xl = -(m_halfGridJumperSize * 2); xr = 0; } else if (trace.flags & JumperRight) { xl = 0; xr = m_halfGridJumperSize * 2; } else { xl = -m_halfGridJumperSize; xr = m_halfGridJumperSize; } for (int y = -m_halfGridJumperSize; y <= m_halfGridJumperSize; y++) { for (int x = xl; x <= xr; x++) { m_displayImage[0]->setPixel(x + gridPoint.x, y + gridPoint.y, 0x800000ff); } } } } void MazeRouter::traceObstacles(QList & traces, int netIndex, Grid * grid, int ikeepout) { // treat traces from previous nets as obstacles foreach (Trace trace, traces) { if (trace.netIndex == netIndex) continue; int lastZ = trace.gridPoints.at(0).z; foreach (GridPoint gridPoint, trace.gridPoints) { if (gridPoint.z != lastZ) { for (int y = -m_halfGridViaSize; y <= m_halfGridViaSize; y++) { for (int x = -m_halfGridViaSize; x <= m_halfGridViaSize; x++) { grid->setAt(gridPoint.x + x, gridPoint.y + y, 0, GridBoardObstacle); grid->setAt(gridPoint.x + x, gridPoint.y + y, 1, GridBoardObstacle); } } lastZ = gridPoint.z; } else { for (int y = -ikeepout; y <= ikeepout; y++) { for (int x = -ikeepout; x <= ikeepout; x++) { grid->setAt(gridPoint.x + x, gridPoint.y + y, gridPoint.z, GridBoardObstacle); } } } } if (trace.flags) { GridPoint gridPoint = trace.gridPoints.first(); // jumper is always centered in this case for (int y = -m_halfGridJumperSize; y <= m_halfGridJumperSize; y++) { for (int x = -m_halfGridJumperSize; x <= m_halfGridJumperSize; x++) { grid->setAt(gridPoint.x + x, gridPoint.y + y, 0, GridBoardObstacle); if (m_bothSidesNow) { grid->setAt(gridPoint.x + x, gridPoint.y + y, 1, GridBoardObstacle); } } } } } } void MazeRouter::traceAvoids(QList & traces, int netIndex, RouteThing & routeThing) { // treat traces from previous nets as semi-obstacles routeThing.avoids.clear(); foreach (Trace trace, traces) { if (trace.netIndex == netIndex) continue; foreach (GridPoint gridPoint, trace.gridPoints) { for (int y = -m_keepoutGridInt; y <= m_keepoutGridInt; y++) { for (int x = -m_keepoutGridInt; x <= m_keepoutGridInt; x++) { GridValue val = m_grid->at(gridPoint.x + x, gridPoint.y + y, 0); if (val == GridPartObstacle || val == GridBoardObstacle || val == GridSource || val == GridTarget) continue; m_grid->setAt(gridPoint.x + x, gridPoint.y + y, 0, GridAvoid); routeThing.avoids.insert(((gridPoint.y + y) * m_grid->x) + x + gridPoint.x); } } } if (trace.flags) { GridPoint gridPoint = trace.gridPoints.first(); int xl, xr; if (gridPoint.flags & GridPointJumperLeft) { xl = -(m_halfGridJumperSize * 2); xr = 0; } else { xl = 0; xr = m_halfGridJumperSize * 2; } for (int y = -m_halfGridJumperSize; y <= m_halfGridJumperSize; y++) { for (int x = xl; x <= xr; x++) { m_grid->setAt(gridPoint.x + x, gridPoint.y + y, 0, GridBoardObstacle); } } } } } void MazeRouter::cleanUpNets(NetList & netList) { foreach(Net * net, netList.nets) { delete net; } netList.nets.clear(); Autorouter::cleanUpNets(); } void MazeRouter::createTraces(NetList & netList, Score & bestScore, QUndoCommand * parentCommand) { QPointF topLeft = m_maxRect.topLeft(); QMultiHash allVias; QMultiHash allJumperItems; QMultiHash allNetLabels; QMultiHash > > allBundles; ConnectionThing connectionThing; emit setMaximumProgress(bestScore.ordering.order.count() * 2); emit setProgressMessage2(tr("Optimizing traces...")); int progress = 0; foreach (int netIndex, bestScore.ordering.order) { emit setProgressValue(progress++); //DebugDialog::debug(QString("tracing net %1").arg(netIndex)); QList traces = bestScore.traces.values(netIndex); qSort(traces.begin(), traces.end(), byOrder); TraceThing traceThing; traceThing.jumperItem = NULL; traceThing.netLabel = NULL; traceThing.topLeft = m_maxRect.topLeft(); int newTraceIndex = 0; Net * net = netList.nets.at(netIndex); for (int tix = 0; tix < traces.count(); tix++) { Trace trace = traces.at(tix); QList gridPoints = trace.gridPoints; // TODO: nicer curve-fitting removeColinear(gridPoints); removeSteps(gridPoints); if (trace.flags & JumperStart) { Trace trace2 = traces.at(tix + 1); traceThing.nextTraceStart = trace2.gridPoints.first(); } createTrace(trace, gridPoints, traceThing, connectionThing, net); QList< QPointer > bundle; if (traceThing.newTraces.count() > newTraceIndex) { ViewLayer::ViewLayerID viewLayerID = traceThing.newTraces.at(newTraceIndex)->viewLayerID(); for (; newTraceIndex < traceThing.newTraces.count(); newTraceIndex++) { TraceWire * traceWire = traceThing.newTraces.at(newTraceIndex); if (traceWire->viewLayerID() != viewLayerID) { allBundles.insert(netIndex, bundle); bundle.clear(); viewLayerID = traceWire->viewLayerID(); } bundle << traceWire; } } else { DebugDialog::debug("create trace failed"); } allBundles.insert(netIndex, bundle); } foreach (SymbolPaletteItem * netLabel, traceThing.newNetLabels) { allNetLabels.insert(netIndex, netLabel); } foreach (Via * via, traceThing.newVias) { allVias.insert(netIndex, via); } foreach (JumperItem * jumperItem, traceThing.newJumperItems) { allJumperItems.insert(netIndex, jumperItem); } } //DebugDialog::debug("before optimize"); optimizeTraces(bestScore.ordering.order, allBundles, allVias, allJumperItems, allNetLabels, netList, connectionThing); //DebugDialog::debug("after optimize"); foreach (SymbolPaletteItem * netLabel, allNetLabels) { addNetLabelToUndo(netLabel, parentCommand); } foreach (Via * via, allVias) { addViaToUndo(via, parentCommand); } foreach (JumperItem * jumperItem, allJumperItems) { addJumperToUndo(jumperItem, parentCommand); } foreach (QList< QPointer > bundle, allBundles) { foreach (TraceWire * traceWire, bundle) { addWireToUndo(traceWire, parentCommand); } } foreach (ConnectorItem * source, connectionThing.sd.uniqueKeys()) { foreach (ConnectorItem * dest, connectionThing.values(source)) { addConnectionToUndo(source, dest, parentCommand); } } QList modelParts; foreach (QList< QPointer > bundle, allBundles) { foreach (TraceWire * traceWire, bundle) { if (traceWire) { modelParts << traceWire->modelPart(); delete traceWire; } } } foreach (Via * via, allVias.values()) { via->removeLayerKin(); modelParts << via->modelPart(); delete via; } foreach (JumperItem * jumperItem, allJumperItems.values()) { jumperItem->removeLayerKin(); modelParts << jumperItem->modelPart(); delete jumperItem; } foreach (SymbolPaletteItem * netLabel, allNetLabels.values()) { modelParts << netLabel->modelPart(); delete netLabel; } foreach (ModelPart * modelPart, modelParts) { modelPart->setParent(NULL); delete modelPart; } DebugDialog::debug("create traces complete"); } void MazeRouter::createTrace(Trace & trace, QList & gridPoints, TraceThing & traceThing, ConnectionThing & connectionThing, Net * net) { //DebugDialog::debug(QString("create trace net:%1").arg(net->id)); if (trace.flags & JumperStart) { if (m_pcbType) { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; ItemBase * itemBase = m_sketchWidget->addItem(m_sketchWidget->referenceModel()->retrieveModelPart(ModuleIDNames::JumperModuleIDName), ViewLayer::NewTop, BaseCommand::SingleView, viewGeometry, newID, -1, NULL); traceThing.jumperItem = dynamic_cast(itemBase); traceThing.jumperItem->setAutoroutable(true); m_sketchWidget->scene()->addItem(traceThing.jumperItem); QPointF c1 = getPixelCenter(trace.gridPoints.first(), traceThing.topLeft, m_gridPixels); QPointF c2 = getPixelCenter(traceThing.nextTraceStart, traceThing.topLeft, m_gridPixels); traceThing.jumperItem->resize(c1, c2); traceThing.newJumperItems << traceThing.jumperItem; } else { traceThing.netLabel = makeNetLabel(trace.gridPoints.first(), NULL, trace.flags); traceThing.newNetLabels << traceThing.netLabel; } } else if (trace.flags & JumperEnd) { if (m_pcbType) { // keep the jumperItem we have from JumperStart } else { traceThing.netLabel = makeNetLabel(trace.gridPoints.first(), traceThing.netLabel, trace.flags); traceThing.newNetLabels << traceThing.netLabel; } } else traceThing.jumperItem = NULL; bool onTraceS, onTraceD; QPointF traceAnchorS, traceAnchorD; ConnectorItem * sourceConnectorItem = NULL; if (traceThing.jumperItem) { onTraceS = onTraceD = false; sourceConnectorItem = (trace.flags & JumperStart) ? traceThing.jumperItem->connector0() : traceThing.jumperItem->connector1(); } else if (traceThing.netLabel) { sourceConnectorItem = traceThing.netLabel->connector0(); } else { sourceConnectorItem = findAnchor(gridPoints.first(), traceThing, net, traceAnchorS, onTraceS, NULL); } if (sourceConnectorItem == NULL) { DebugDialog::debug("missing source connector"); return; } ConnectorItem * destConnectorItem = findAnchor(gridPoints.last(), traceThing, net, traceAnchorD, onTraceD, sourceConnectorItem); if (destConnectorItem == NULL) { /* GridPoint gp = gridPoints.last(); for (int x = gp.x - 5; x < gp.x + 5; x++) { m_displayImage[gp.z]->setPixel(x, gp.y, 0xff000000); } for (int y = gp.y - 5; y < gp.y + 5; y++) { m_displayImage[gp.z]->setPixel(gp.x, y, 0xff000000); } updateDisplay(gp.z); */ DebugDialog::debug("missing dest connector"); return; } //if (sourceConnectorItem->attachedTo() == destConnectorItem->attachedTo()) { // sourceConnectorItem->debugInfo("source"); // destConnectorItem->debugInfo("dest"); //} QPointF sourcePoint = sourceConnectorItem->sceneAdjustedTerminalPoint(NULL); QPointF destPoint = destConnectorItem->sceneAdjustedTerminalPoint(NULL); bool skipFirst = false; bool skipLast = false; GridPoint gp = gridPoints.last(); QPointF center = getPixelCenter(gp, traceThing.topLeft, m_gridPixels); if (!atLeast(center, destPoint)) { // don't need this last point skipLast = true; } gp = gridPoints.first(); center = getPixelCenter(gp, traceThing.topLeft, m_gridPixels); if (!atLeast(center, sourcePoint)) { skipFirst = true; } // convert grid-based points to normal svg-space points and add the inbetween points necessitated by removeSteps() QList newPoints; for (int i = 0; i < gridPoints.count(); i++) { GridPoint gp1 = gridPoints.at(i); QPointF c1 = getPixelCenter(gp1, traceThing.topLeft, m_gridPixels); PointZ v1(c1, gp1.z); newPoints << v1; if ((gp1.flags & GridPointStepStart) == 0) continue; GridPoint gp2 = gridPoints.at(i + 1); QPointF c2 = getPixelCenter(gp2, traceThing.topLeft, m_gridPixels); QPointF p = getStepPoint(c1, gp1.flags, m_gridPixels); PointZ v2(p, gp1.z); newPoints << v2; QPointF q = getStepPoint(c2, gp2.flags, m_gridPixels); PointZ v3(q, gp2.z); newPoints << v3; //DebugDialog::debug(QString("remove2 %1,%2 %3,%4").arg(c1.x()).arg(c1.y()).arg(p.x()).arg(p.y())); //DebugDialog::debug(QString("\t%1,%2 %3,%4").arg(q.x()).arg(q.y()).arg(c2.x()).arg(c2.y())); } if (skipLast) { newPoints.takeLast(); } PointZ point = newPoints.takeFirst(); if (skipFirst) { point = newPoints.takeFirst(); } int lastz = point.z; ConnectorItem * nextSource = NULL; if (onTraceS) { if (!atLeast(sourcePoint, traceAnchorS)) { onTraceS = false; } else if (atLeast(point.p, traceAnchorS)) { onTraceS = false; } } if (onTraceS) { TraceWire * traceWire1 = drawOneTrace(sourcePoint, traceAnchorS, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(sourceConnectorItem, traceWire1->connector0()); traceThing.newTraces << traceWire1; TraceWire* traceWire2 = drawOneTrace(traceAnchorS, point.p, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(traceWire1->connector1(), traceWire2->connector0()); nextSource = traceWire2->connector1(); traceThing.newTraces << traceWire2; } else { TraceWire * traceWire = drawOneTrace(sourcePoint, point.p, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(sourceConnectorItem, traceWire->connector0()); nextSource = traceWire->connector1(); traceThing.newTraces << traceWire; } foreach (PointZ newPoint, newPoints) { if (newPoint.z == lastz) { TraceWire * traceWire = drawOneTrace(point.p, newPoint.p, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(nextSource, traceWire->connector0()); nextSource = traceWire->connector1(); traceThing.newTraces << traceWire; } else { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; double ringThickness, holeSize; m_sketchWidget->getViaSize(ringThickness, holeSize); double halfVia = (ringThickness + ringThickness + holeSize) / 2; viewGeometry.setLoc(QPointF(newPoint.p.x() - halfVia - Hole::OffsetPixels, newPoint.p.y() - halfVia - Hole::OffsetPixels)); ItemBase * itemBase = m_sketchWidget->addItem(m_sketchWidget->referenceModel()->retrieveModelPart(ModuleIDNames::ViaModuleIDName), ViewLayer::NewTop, BaseCommand::SingleView, viewGeometry, newID, -1, NULL); //DebugDialog::debug(QString("back from adding via %1").arg((long) itemBase, 0, 16)); Via * via = qobject_cast(itemBase); via->setAutoroutable(true); via->setHoleSize(QString("%1in,%2in") .arg(holeSize / GraphicsUtils::SVGDPI) .arg(ringThickness / GraphicsUtils::SVGDPI), false); connectionThing.add(nextSource, via->connectorItem()); nextSource = via->connectorItem(); traceThing.newVias << via; lastz = newPoint.z; } point = newPoint; } if (onTraceD) { if (!atLeast(destPoint, traceAnchorD)) { onTraceD = false; } else if (!atLeast(point.p, traceAnchorD)) { onTraceD = false; } } if (onTraceD) { TraceWire * traceWire1 = drawOneTrace(point.p, traceAnchorD, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(nextSource, traceWire1->connector0()); traceThing.newTraces << traceWire1; TraceWire * traceWire2 = drawOneTrace(traceAnchorD, destPoint, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(traceWire1->connector1(), traceWire2->connector0()); connectionThing.add(traceWire2->connector1(), destConnectorItem); traceThing.newTraces << traceWire2; } else { TraceWire * traceWire = drawOneTrace(point.p, destPoint, m_standardWireWidth, lastz == 0 ? ViewLayer::NewBottom : ViewLayer::NewTop); connectionThing.add(nextSource, traceWire->connector0()); connectionThing.add(traceWire->connector1(), destConnectorItem); traceThing.newTraces << traceWire; } } ConnectorItem * MazeRouter::findAnchor(GridPoint gp, TraceThing & traceThing, Net * net, QPointF & p, bool & onTrace, ConnectorItem * already) { QRectF gridRect(gp.x * m_gridPixels + traceThing.topLeft.x(), gp.y * m_gridPixels + traceThing.topLeft.y(), m_gridPixels, m_gridPixels); ConnectorItem * connectorItem = findAnchor(gp, gridRect, traceThing, net, p, onTrace, already); if (connectorItem != NULL) { //if (connectorItem->attachedToID() == 9781620) { // connectorItem->debugInfo("9781620"); //} if (connectorItem->attachedToItemType() != ModelPart::Wire) { return connectorItem; } // otherwise keep looking } gridRect.adjust(-m_gridPixels, -m_gridPixels, m_gridPixels, m_gridPixels); return findAnchor(gp, gridRect, traceThing, net, p, onTrace, already); } ConnectorItem * MazeRouter::findAnchor(GridPoint gp, const QRectF & gridRect, TraceThing & traceThing, Net * net, QPointF & p, bool & onTrace, ConnectorItem * already) { ConnectorItem * alreadyCross = NULL; if (already != NULL) alreadyCross = already->getCrossLayerConnectorItem(); QList traceWires; QList traceConnectorItems; foreach (QGraphicsItem * item, m_sketchWidget->scene()->items(gridRect)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem) { if (!connectorItem->attachedTo()->isEverVisible()) continue; if (connectorItem == already) continue; if (connectorItem == alreadyCross) continue; if (already != NULL && connectorItem->attachedTo() == already->attachedTo()) { ConnectorItem * cross = connectorItem->getCrossLayerConnectorItem(); if (cross != NULL) { if (cross == already) continue; if (cross == alreadyCross) continue; } } //connectorItem->debugInfo("candidate"); bool isCandidate = (gp.z == 0 && m_sketchWidget->attachedToBottomLayer(connectorItem)) || (gp.z == 1 && m_sketchWidget->attachedToTopLayer(connectorItem)) ; if (!isCandidate) continue; bool traceConnector = false; if (net->net->contains(connectorItem)) ; else { TraceWire * traceWire = qobject_cast(connectorItem->attachedTo()); if (traceWire == NULL) { Via * via = qobject_cast(connectorItem->attachedTo()->layerKinChief()); if (via == NULL) isCandidate = false; else isCandidate = traceThing.newVias.contains(via); } else { traceConnector = isCandidate = traceThing.newTraces.contains(traceWire); } } if (!isCandidate) continue; if (traceConnector) { traceConnectorItems << connectorItem; continue; } else { //if (traceConnectorItems.count() > 0) { // connectorItem->debugInfo("chose not trace"); //} onTrace = false; p = connectorItem->sceneAdjustedTerminalPoint(NULL); return connectorItem; } } TraceWire * traceWire = dynamic_cast(item); if (traceWire == NULL) continue; if (!traceWire->isEverVisible()) continue; // only do traces if no connectorItem is found traceWires.append(traceWire); } if (traceConnectorItems.count() > 0) { //if (traceConnectorItems.count() > 1) { // foreach (ConnectorItem * connectorItem, traceConnectorItems) { // connectorItem->debugInfo("on trace"); // } //} onTrace = false; ConnectorItem * connectorItem = traceConnectorItems.takeLast(); p = connectorItem->sceneAdjustedTerminalPoint(NULL); return connectorItem; } foreach (TraceWire * traceWire, traceWires) { //traceWire->debugInfo("trace candidate"); bool isCandidate = (gp.z == 0 && m_sketchWidget->attachedToBottomLayer(traceWire->connector0())) || (gp.z == 1 && m_sketchWidget->attachedToTopLayer(traceWire->connector0())); if (!isCandidate) continue; if (traceThing.newTraces.contains(traceWire)) ; else if (net->net->contains(traceWire->connector0())) ; else continue; onTrace = true; QPointF center = gridRect.center(); QPointF p0 = traceWire->connector0()->sceneAdjustedTerminalPoint(NULL); QPointF p1 = traceWire->connector1()->sceneAdjustedTerminalPoint(NULL); double d0 = GraphicsUtils::distanceSqd(p0, center); double d1 = GraphicsUtils::distanceSqd(p1, center); double dx, dy, distanceSegment; bool atEndpoint; GraphicsUtils::distanceFromLine(center.x(), center.y(), p0.x(), p0.y(), p1.x(), p1.y(), dx, dy, distanceSegment, atEndpoint); if (atEndpoint) { DebugDialog::debug("at endpoint shouldn't happen"); } p.setX(dx); p.setY(dy); if (d0 <= d1) { return traceWire->connector0(); } else { return traceWire->connector1(); } } DebugDialog::debug("overlap not found"); return NULL; } void MazeRouter::removeColinear(QList & gridPoints) { // eliminate redundant colinear points int ix = 0; while (ix < gridPoints.count() - 2) { GridPoint p1 = gridPoints[ix]; GridPoint p2 = gridPoints[ix + 1]; if (p1.z == p2.z) { GridPoint p3 = gridPoints[ix + 2]; if (p2.z == p3.z) { if (p1.x == p2.x && p2.x == p3.x) { gridPoints.removeAt(ix + 1); continue; } else if (p1.y == p2.y && p2.y == p3.y) { gridPoints.removeAt(ix + 1); continue; } } } ix++; } } void MazeRouter::removeSteps(QList & gridPoints) { // eliminate 45 degree runs for (int ix = 0; ix < gridPoints.count() - 2; ix++) { removeStep(ix, gridPoints); } } void MazeRouter::removeStep(int ix, QList & gridPoints) { GridPoint p1 = gridPoints[ix]; GridPoint p2 = gridPoints[ix + 1]; if (p1.z != p2.z) return; GridPoint p3 = gridPoints[ix + 2]; if (p2.z != p3.z) return; int dx1 = p2.x - p1.x; int dy1 = p2.y - p1.y; if ((qAbs(dx1) == 1 && dy1 == 0) || (dx1 == 0 && qAbs(dy1) == 1)) ; else return; int dx2 = p3.x - p2.x; int dy2 = p3.y - p2.y; bool step = false; if (dx1 == 0) { step = (dy2 == 0 && qAbs(dx2) == 1); } else { step = (dx2 == 0 && qAbs(dy2) == 1); } if (!step) return; int count = 1; int flag = 0; for (int jx = ix + 3; jx < gridPoints.count(); jx++, flag++) { GridPoint p4 = gridPoints[jx]; int dx3 = p4.x - p3.x; int dy3 = p4.y - p3.y; if (flag % 2 == 0) { if (dx3 == dx1 && dy3 == dy1) { count++; } else break; } else { if (dx3 == dx2 && dy3 == dy2) { count++; } else break; } p2 = p3; p3 = p4; } gridPoints[ix].flags = getStepFlag(gridPoints[ix], gridPoints[ix + 1]) | GridPointStepStart; int jx = ix + count + 1; gridPoints[jx].flags = getStepFlag(gridPoints[jx], gridPoints[jx - 1]); /* QPointF topLeft = m_maxRect.topLeft(); DebugDialog::debug(QString("removing %1").arg(count)); for (int i = 0; i < count + 2; i++) { QPointF p = getPixelCenter(gridPoints[ix + i], topLeft, m_gridPixels); DebugDialog::debug(QString("\t%1,%2 %3,%4").arg(gridPoints[ix + i].x).arg(gridPoints[ix + i].y).arg(p.x()).arg(p.y())); } */ while (--count >= 0) { gridPoints.removeAt(ix + 1); } } void MazeRouter::addConnectionToUndo(ConnectorItem * from, ConnectorItem * to, QUndoCommand * parentCommand) { if (from == NULL || to == NULL) return; ChangeConnectionCommand * ccc = new ChangeConnectionCommand(m_sketchWidget, BaseCommand::CrossView, from->attachedToID(), from->connectorSharedID(), to->attachedToID(), to->connectorSharedID(), ViewLayer::specFromID(from->attachedToViewLayerID()), true, parentCommand); ccc->setUpdateConnections(false); } void MazeRouter::addViaToUndo(Via * via, QUndoCommand * parentCommand) { new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, ModuleIDNames::ViaModuleIDName, via->viewLayerPlacement(), via->getViewGeometry(), via->id(), false, -1, parentCommand); new SetPropCommand(m_sketchWidget, via->id(), "hole size", via->holeSize(), via->holeSize(), true, parentCommand); new CheckStickyCommand(m_sketchWidget, BaseCommand::SingleView, via->id(), false, CheckStickyCommand::RemoveOnly, parentCommand); } void MazeRouter::addJumperToUndo(JumperItem * jumperItem, QUndoCommand * parentCommand) { jumperItem->saveParams(); QPointF pos, c0, c1; jumperItem->getParams(pos, c0, c1); new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, ModuleIDNames::JumperModuleIDName, jumperItem->viewLayerPlacement(), jumperItem->getViewGeometry(), jumperItem->id(), false, -1, parentCommand); new ResizeJumperItemCommand(m_sketchWidget, jumperItem->id(), pos, c0, c1, pos, c0, c1, parentCommand); new CheckStickyCommand(m_sketchWidget, BaseCommand::SingleView, jumperItem->id(), false, CheckStickyCommand::RemoveOnly, parentCommand); } void MazeRouter::addNetLabelToUndo(SymbolPaletteItem * netLabel, QUndoCommand * parentCommand) { new AddItemCommand(m_sketchWidget, BaseCommand::CrossView, netLabel->moduleID(), netLabel->viewLayerPlacement(), netLabel->getViewGeometry(), netLabel->id(), false, -1, parentCommand); new SetPropCommand(m_sketchWidget, netLabel->id(), "label", netLabel->getLabel(), netLabel->getLabel(), true, parentCommand); } void MazeRouter::insertTrace(Trace & newTrace, int netIndex, Score & currentScore, int viaCount, bool incRouted) { if (newTrace.gridPoints.count() == 0) { DebugDialog::debug("trace with no points"); return; } //DebugDialog::debug(QString("insert trace %1").arg(newTrace.gridPoints.count())); newTrace.netIndex = netIndex; newTrace.order = currentScore.traces.values(netIndex).count(); currentScore.traces.insert(netIndex, newTrace); if (incRouted) { currentScore.routedCount.insert(netIndex, currentScore.routedCount.value(netIndex) + 1); currentScore.totalRoutedCount++; } currentScore.viaCount.insert(netIndex, currentScore.viaCount.value(netIndex, 0) + viaCount); currentScore.totalViaCount += viaCount; displayTrace(newTrace); //DebugDialog::debug(QString("done insert trace")); } void MazeRouter::incCommandProgress() { emit setProgressValue(m_cleanupCount++); int modulo = m_commandCount / 100; if (modulo > 0 && m_cleanupCount % modulo == 0) { ProcessEventBlocker::processEvents(); } //DebugDialog::debug(QString("cleanup:%1, cc:%2").arg(m_cleanupCount).arg(m_commandCount)); } void MazeRouter::setMaxCycles(int maxCycles) { Autorouter::setMaxCycles(maxCycles); emit setMaximumProgress(maxCycles); } SymbolPaletteItem * MazeRouter::makeNetLabel(GridPoint & center, SymbolPaletteItem * pairedNetLabel, uchar traceFlags) { // flags & JumperLeft means position the netlabel to the left of center, the netlabel points right if (m_netLabelIndex < 0) { m_netLabelIndex = 0; foreach (QGraphicsItem * item, m_sketchWidget->scene()->items()) { SymbolPaletteItem * netLabel = dynamic_cast(item); if (netLabel == NULL || !netLabel->isOnlyNetLabel()) continue; bool ok; int ix = netLabel->getLabel().toInt(&ok); if (ok && ix > m_netLabelIndex) m_netLabelIndex = ix; } } if (pairedNetLabel == NULL) { m_netLabelIndex++; } long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; ItemBase * itemBase = m_sketchWidget->addItem(m_sketchWidget->referenceModel()->retrieveModelPart(traceFlags & JumperLeft ? ModuleIDNames::NetLabelModuleIDName : ModuleIDNames::LeftNetLabelModuleIDName), ViewLayer::NewBottom, BaseCommand::SingleView, viewGeometry, newID, -1, NULL); SymbolPaletteItem * netLabel = dynamic_cast(itemBase); netLabel->setAutoroutable(true); netLabel->setLabel(QString::number(m_netLabelIndex)); QPointF tl = m_maxRect.topLeft(); QPointF c1 = getPixelCenter(center, tl, m_gridPixels); QSizeF size = netLabel->boundingRect().size(); int x = c1.x(); if (traceFlags & JumperLeft) { x -= size.width(); } netLabel->setPos(x, c1.y() - (size.height() / 2)); //DebugDialog::debug(QString("ix:%1 tl:%2 %3,%4").arg(m_netLabelIndex).arg(traceFlags).arg(x).arg(c1.y() - (size.height() / 2))); netLabel->saveGeometry(); return netLabel; } void MazeRouter::routeJumper(int netIndex, RouteThing & routeThing, Score & currentScore) { if (routeThing.bestDistanceToTarget == std::numeric_limits::max() || routeThing.bestDistanceToSource == std::numeric_limits::max()) { // never got started on this route return; } //DebugDialog::debug(QString("route jumper %1, %2").arg(routeThing.bestDistanceToSource).arg(routeThing.bestDistanceToTarget)); //updateDisplay(0); //if (m_bothSidesNow) updateDisplay(1); bool routeBothEnds = true; Trace sourceTrace; if (!m_pcbType) { // is there already a net label on this net? // TODO: which subnet has the jumper? foreach (Trace trace, currentScore.traces.values(netIndex)) { if (trace.flags & JumperStart) { sourceTrace = trace; routeBothEnds = false; break; } } } //updateDisplay(m_grid, 0); //if (m_bothSidesNow) updateDisplay(m_grid, 1); GridPoint gp1 = lookForJumper(routeThing.bestLocationToTarget, GridSource, routeThing.gridTargetPoint); if (gp1.baseCost == GridBoardObstacle) return; GridPoint gp2 = lookForJumper(routeThing.bestLocationToSource, GridTarget, routeThing.gridSourcePoint); if (gp2.baseCost == GridBoardObstacle) return; int sourceViaCount; if (routeBothEnds) { sourceTrace.flags = JumperStart; if (gp1.flags & GridPointJumperLeft) sourceTrace.flags |= JumperLeft; else if (gp1.flags & GridPointJumperRight) sourceTrace.flags |= JumperRight; sourceTrace.gridPoints = traceBack(gp1, m_grid, sourceViaCount, GridTarget, GridSource); // trace back to source } Trace destTrace; destTrace.flags = JumperEnd; if (gp2.flags & GridPointJumperLeft) destTrace.flags |= JumperLeft; else if (gp2.flags & GridPointJumperRight) destTrace.flags |= JumperRight; int targetViaCount; destTrace.gridPoints = traceBack(gp2, m_grid, targetViaCount, GridSource, GridTarget); // trace back to target if (routeBothEnds) { insertTrace(sourceTrace, netIndex, currentScore, sourceViaCount, false); } insertTrace(destTrace, netIndex, currentScore, targetViaCount, true); updateDisplay(0); if (m_bothSidesNow) updateDisplay(1); clearExpansion(m_grid); } GridPoint MazeRouter::lookForJumper(GridPoint initial, GridValue targetValue, QPoint targetLocation) { QSet already; std::priority_queue pq; initial.qCost = 0; pq.push(initial); already.insert(gridPointInt(m_grid, initial)); while (!pq.empty()) { GridPoint gp = pq.top(); pq.pop(); gp.baseCost = m_grid->at(gp.x, gp.y, gp.z); /* GridValue bc = gp.baseCost; if (targetValue == GridSource) bc ^= GridSourceFlag; bc *= 3; if (bc > 255) bc = 255; m_displayImage[gp.z]->setPixel(gp.x, gp.y, 0xff000000 | (bc << 16) | (bc << 8) | bc); updateDisplay(gp.z); */ if ((*m_jumperWillFitFunction)(gp, m_grid, m_halfGridJumperSize)) { if (targetValue == GridSource) gp.baseCost ^= GridSourceFlag; //m_displayImage[gp.z]->setPixel(gp.x, gp.y, 0xff0000ff); //updateDisplay(gp.z); //updateDisplay(gp.z); //QPointF p = getPixelCenter(gp, m_maxRect.topLeft(), m_gridPixels); //DebugDialog::debug(QString("jumper location %1").arg(gp.flags), p); return gp; } if (gp.x > 0) expandOneJ(gp, pq, -1, 0, 0, targetValue, targetLocation, already); if (gp.x < m_grid->x - 1) expandOneJ(gp, pq, 1, 0, 0, targetValue, targetLocation, already); if (gp.y > 0) expandOneJ(gp, pq, 0, -1, 0, targetValue, targetLocation, already); if (gp.y < m_grid->y - 1) expandOneJ(gp, pq, 0, 1, 0, targetValue, targetLocation, already); if (m_bothSidesNow) { if (gp.z > 0) expandOneJ(gp, pq, 0, 0, -1, targetValue, targetLocation, already); if (gp.z < m_grid->z - 1) expandOneJ(gp, pq, 0, 0, 1, targetValue, targetLocation, already); } } GridPoint failed; failed.baseCost = GridBoardObstacle; return failed; } void MazeRouter::expandOneJ(GridPoint & gridPoint, std::priority_queue & pq, int dx, int dy, int dz, GridValue targetValue, QPoint targetLocation, QSet & already) { GridPoint next; next.x = gridPoint.x + dx; next.y = gridPoint.y + dy; next.z = gridPoint.z + dz; int gpi = gridPointInt(m_grid, next); if (already.contains(gpi)) return; already.insert(gpi); GridValue nextval = m_grid->at(next.x, next.y, next.z); if (nextval == GridPartObstacle || nextval == GridBoardObstacle || nextval == GridSource || nextval == GridTempObstacle || nextval == GridTarget || nextval == GridAvoid) return; else if (nextval == 0) return; else { // already been here: see if it's the right expansion if (targetValue == GridSource) { if ((nextval & GridSourceFlag) == 0) return; } else { if (nextval & GridSourceFlag) return; } } double d = (m_costFunction)(QPoint(next.x, next.y), targetLocation); next.qCost = d; next.baseCost = 0; //DebugDialog::debug(QString("pushing next %1 %2 %3, %4, %5").arg(gridPoint.x).arg(gridPoint.y).arg(gridPoint.z).arg(gridPoint.qCost).arg(routeThing.pq.size())); pq.push(next); } void MazeRouter::removeOffBoardAnd(bool isPCBType, bool removeSingletons, bool bothSides) { QRectF boardRect; if (m_board) boardRect = m_board->sceneBoundingRect(); // remove any vias or jumperItems that will be deleted, also remove off-board items for (int i = m_allPartConnectorItems.count() - 1; i >= 0; i--) { QList * connectorItems = m_allPartConnectorItems.at(i); if (removeSingletons) { if (connectorItems->count() < 2) { connectorItems->clear(); } else if (connectorItems->count() == 2) { if (connectorItems->at(0) == connectorItems->at(1)->getCrossLayerConnectorItem()) { connectorItems->clear(); } } } for (int j = connectorItems->count() - 1; j >= 0; j--) { ConnectorItem * connectorItem = connectorItems->at(j); //connectorItem->debugInfo("pci"); bool doRemove = false; if (connectorItem->attachedToItemType() == ModelPart::Via) { Via * via = qobject_cast(connectorItem->attachedTo()->layerKinChief()); doRemove = via->getAutoroutable(); } else if (connectorItem->attachedToItemType() == ModelPart::Jumper) { JumperItem * jumperItem = qobject_cast(connectorItem->attachedTo()->layerKinChief()); doRemove = jumperItem->getAutoroutable(); } else if (connectorItem->attachedToItemType() == ModelPart::Symbol) { SymbolPaletteItem * netLabel = qobject_cast(connectorItem->attachedTo()->layerKinChief()); doRemove = netLabel->getAutoroutable() && netLabel->isOnlyNetLabel(); } if (!bothSides && connectorItem->attachedToViewLayerID() == ViewLayer::Copper1) doRemove = true; if (!doRemove && isPCBType) { if (!connectorItem->sceneBoundingRect().intersects(boardRect)) { doRemove = true; } } if (doRemove) { connectorItems->removeAt(j); } } if (connectorItems->count() == 0) { m_allPartConnectorItems.removeAt(i); delete connectorItems; } } } void MazeRouter::optimizeTraces(QList & order, QMultiHash > > & bundles, QMultiHash & vias, QMultiHash & jumperItems, QMultiHash & netLabels, NetList & netList, ConnectionThing & connectionThing) { QList layerSpecs; layerSpecs << ViewLayer::NewBottom; if (m_bothSidesNow) layerSpecs << ViewLayer::NewTop; QRectF r2(0, 0, m_boardImage->width(), m_boardImage->height()); QPointF topLeft = m_maxRect.topLeft(); //QList order2(order); //order2.append(order); int progress = order.count(); foreach (int netIndex, order) { emit setProgressValue(progress++); Net * net = netList.nets.at(netIndex); foreach (ViewLayer::ViewLayerPlacement layerSpec, layerSpecs) { fastCopy(m_boardImage, m_spareImage); QDomDocument * masterDoc = m_masterDocs.value(layerSpec); //QString before = masterDoc->toString(); Markers markers; initMarkers(markers, m_pcbType); NetElements netElements; DRC::splitNetPrep(masterDoc, *(net->net), markers, netElements.net, netElements.alsoNet, netElements.notNet, true); foreach (QDomElement element, netElements.net) { element.setTagName("g"); } foreach (QDomElement element, netElements.alsoNet) { element.setTagName("g"); } ItemBase::renderOne(masterDoc, m_spareImage, r2); //QString after = masterDoc->toString(); foreach (QDomElement element, netElements.net) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } foreach (QDomElement element, netElements.alsoNet) { element.setTagName(element.attribute("former")); element.removeAttribute("net"); } foreach (QDomElement element, netElements.notNet) { element.removeAttribute("net"); } QPainter painter; painter.begin(m_spareImage); QPen pen = painter.pen(); pen.setColor(0xff000000); QBrush brush(QColor(0xff000000)); painter.setBrush(brush); foreach (int otherIndex, order) { if (otherIndex == netIndex) continue; foreach(QList< QPointer > bundle, bundles.values(otherIndex)) { if (bundle.count() == 0) continue; if (ViewLayer::specFromID(bundle.at(0)->viewLayerID()) != layerSpec) continue; pen.setWidthF((bundle.at(0)->width() + m_keepoutPixels + m_keepoutPixels) * OptimizeFactor); painter.setPen(pen); foreach (TraceWire * traceWire, bundle) { if (traceWire == NULL) continue; QPointF p1 = (traceWire->connector0()->sceneAdjustedTerminalPoint(NULL) - topLeft) * OptimizeFactor; QPointF p2 = (traceWire->connector1()->sceneAdjustedTerminalPoint(NULL) - topLeft) * OptimizeFactor; painter.drawLine(p1, p2); } } painter.setPen(Qt::NoPen); foreach (Via * via, vias.values(otherIndex)) { QPointF p = (via->connectorItem()->sceneAdjustedTerminalPoint(NULL) - topLeft) * OptimizeFactor; double rad = ((via->connectorItem()->sceneBoundingRect().width() / 2) + m_keepoutPixels) * OptimizeFactor; painter.drawEllipse(p, rad, rad); } foreach (JumperItem * jumperItem, jumperItems.values(otherIndex)) { QPointF p = (jumperItem->connector0()->sceneAdjustedTerminalPoint(NULL) - topLeft) * OptimizeFactor; double rad = ((jumperItem->connector0()->sceneBoundingRect().width() / 2) + m_keepoutPixels) * OptimizeFactor; painter.drawEllipse(p, rad, rad); p = (jumperItem->connector1()->sceneAdjustedTerminalPoint(NULL) - topLeft) * OptimizeFactor; painter.drawEllipse(p, rad, rad); } foreach (SymbolPaletteItem * netLabel, netLabels.values(otherIndex)) { QRectF r = netLabel->sceneBoundingRect(); painter.drawRect((r.left() - topLeft.x() - m_keepoutPixels) * OptimizeFactor, (r.top() - topLeft.y() - m_keepoutPixels) * OptimizeFactor, (r.width() + m_keepoutPixels) * OptimizeFactor, (r.height() + m_keepoutPixels) * OptimizeFactor); } } foreach (SymbolPaletteItem * netLabel, netLabels.values(netIndex)) { QRectF r = netLabel->sceneBoundingRect(); painter.drawRect((r.left() - topLeft.x() - m_keepoutPixels) * OptimizeFactor, (r.top() - topLeft.y() - m_keepoutPixels) * OptimizeFactor, (r.width() + m_keepoutPixels) * OptimizeFactor, (r.height() + m_keepoutPixels) * OptimizeFactor); } painter.end(); #ifndef QT_NO_DEBUG //m_spareImage->save(FolderUtils::getUserDataStorePath("") + QString("/optimizeObstacles%1_%2.png").arg(netIndex, 2, 10, QChar('0')).arg(layerSpec)); #endif // finally test all combinations of each bundle // identify traces in bundles with source and dest that cannot be deleted // remove source/dest (delete and keep QPointers?) // schematic view must replace with two 90-degree lines foreach(QList< QPointer > bundle, bundles.values(netIndex)) { if (bundle.count() == 0) continue; for (int i = bundle.count() - 1; i >= 0; i--) { TraceWire * traceWire = bundle.at(i); if (traceWire == NULL) bundle.removeAt(i); } if (ViewLayer::specFromID(bundle.at(0)->viewLayerID()) != layerSpec) { // all wires in a single bundle are in the same layer continue; } /* QList tos = connectionThing.values(bundle.first()->connector0()); foreach (ConnectorItem * to, tos) { if (to->attachedToItemType() == ModelPart::Via) { to->debugInfo("start hooked to via"); } } tos = connectionThing.values(bundle.last()->connector1()); foreach (ConnectorItem * to, tos) { if (to->attachedToItemType() == ModelPart::Via) { to->debugInfo("end hooked to via"); } } */ QVector points(bundle.count() + 1, QPointF(0, 0)); QVector splits(bundle.count() + 1, false); int index = 0; foreach (TraceWire * traceWire, bundle) { if (connectionThing.multi(traceWire->connector0())) splits.replace(index, true); points.replace(index, traceWire->connector0()->sceneAdjustedTerminalPoint(NULL)); index++; if (connectionThing.multi(traceWire->connector1())) splits.replace(index, true); points.replace(index, traceWire->connector1()->sceneAdjustedTerminalPoint(NULL)); } splits.replace(0, false); splits.replace(splits.count() - 1, true); QList pointsSoFar; QList bundleSoFar; int startIndex = 0; for (int pix = 0; pix < points.count(); pix++) { pointsSoFar << points.at(pix); if (pix < points.count() - 1) { bundleSoFar << bundle.at(pix); } if (splits.at(pix)) { reducePoints(pointsSoFar, topLeft, bundleSoFar, startIndex, pix, connectionThing, netIndex, layerSpec); pointsSoFar.clear(); bundleSoFar.clear(); pointsSoFar << points.at(pix); if (pix < points.count() - 1) bundleSoFar << bundle.at(pix); startIndex = pix; } } } } } } void MazeRouter::reducePoints(QList & points, QPointF topLeft, QList & bundle, int startIndex, int endIndex, ConnectionThing & connectionThing, int netIndex, ViewLayer::ViewLayerPlacement layerSpec) { Q_UNUSED(netIndex); Q_UNUSED(layerSpec); #ifndef QT_NO_DEBUG //int inc = 0; #endif double width = bundle.at(0)->width() * OptimizeFactor; for (int separation = endIndex - startIndex; separation > 1; separation--) { for (int ix = 0; ix < points.count() - separation; ix++) { QPointF p1 = (points.at(ix) - topLeft) * OptimizeFactor; QPointF p2 = (points.at(ix + separation) - topLeft) * OptimizeFactor; double minX = qMax(0.0, qMin(p1.x(), p2.x()) - width); double minY = qMax(0.0, qMin(p1.y(), p2.y()) - width); double maxX = qMin(m_spareImage2->width() - 1.0, qMax(p1.x(), p2.x()) + width); double maxY = qMin(m_spareImage2->height() - 1.0, qMax(p1.y(), p2.y()) + width); int corners = 1; if (!m_pcbType) { if (qAbs(p1.x() - p2.x()) >= 1 && qAbs(p1.y() - p2.y()) >= 1) { corners = 2; if (separation == 2) continue; // since we already have two lines, do nothing } } for (int corner = 0; corner < corners; corner++) { m_spareImage2->fill(0xffffffff); QPainter painter; painter.begin(m_spareImage2); QPen pen = painter.pen(); pen.setColor(0xff000000); pen.setWidthF(width); painter.setPen(pen); if (corners == 1) { painter.drawLine(p1, p2); } else { if (corner == 0) { // vertical then horizontal painter.drawLine(p1.x(), p1.y(), p1.x(), p2.y()); painter.drawLine(p1.x(), p2.y(), p2.x(), p2.y()); } else { // horizontal then vertical painter.drawLine(p1.x(), p1.y(), p2.x(), p1.y()); painter.drawLine(p2.x(), p1.y(), p2.x(), p2.y()); } } painter.end(); #ifndef QT_NO_DEBUG //m_spareImage2->save(FolderUtils::getUserDataStorePath("") + QString("/optimizeTrace%1_%2_%3.png").arg(netIndex,2,10,QChar('0')).arg(layerSpec).arg(inc++,3,10,QChar('0'))); #endif bool overlaps = false; for (int y = minY; y <= maxY && !overlaps; y++) { for (int x = minX; x <= maxX && !overlaps; x++) { if (m_spareImage->pixel(x, y) == 0xffffffff) continue; if (m_spareImage2->pixel(x, y) == 0xffffffff) continue; overlaps = true; } } if (overlaps) continue; TraceWire * traceWire = bundle.at(ix); TraceWire * last = bundle.at(ix + separation - 1); TraceWire * next = bundle.at(ix + 1); QList newDests = connectionThing.values(last->connector1()); QPointF unop_p2 = (p2 / OptimizeFactor) + topLeft; if (corners == 2) { TraceWire * afterNext = bundle.at(ix + 2); QPointF middle; if (corner == 0) { middle.setX(p1.x()); middle.setY(p2.y()); } else { middle.setX(p2.x()); middle.setY(p1.y()); } middle = (middle / OptimizeFactor) + topLeft; traceWire->setLineAnd(QLineF(QPointF(0, 0), middle - traceWire->pos()), traceWire->pos(), true); traceWire->saveGeometry(); traceWire->update(); next->setLineAnd(QLineF(QPointF(0, 0), unop_p2 - middle), middle, true); next->saveGeometry(); next->update(); foreach (ConnectorItem * newDest, newDests) { connectionThing.add(next->connector1(), newDest); } connectionThing.remove(next->connector1(), afterNext->connector0()); for (int i = 1; i < separation - 1; i++) { TraceWire * tw = bundle.takeAt(ix + 2); connectionThing.remove(tw->connector0()); connectionThing.remove(tw->connector1()); ModelPart * modelPart = tw->modelPart(); delete tw; modelPart->setParent(NULL); delete modelPart; points.removeAt(ix + 2); } points.replace(ix + 1, middle); } else { traceWire->setLineAnd(QLineF(QPointF(0, 0), unop_p2 - traceWire->pos()), traceWire->pos(), true); traceWire->saveGeometry(); traceWire->update(); foreach (ConnectorItem * newDest, newDests) { connectionThing.add(traceWire->connector1(), newDest); } connectionThing.remove(traceWire->connector1(), next->connector0()); for (int i = 0; i < separation - 1; i++) { TraceWire * tw = bundle.takeAt(ix + 1); connectionThing.remove(tw->connector0()); connectionThing.remove(tw->connector1()); ModelPart * modelPart = tw->modelPart(); delete tw; modelPart->setParent(NULL); delete modelPart; points.removeAt(ix + 1); } } break; } } } } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/mazerouter/mazerouter.h�������������������������������������������0000664�0000000�0000000�00000022561�12723636013�0023671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #ifndef MAZEROUTER_H #define MAZEROUTER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../viewgeometry.h" #include "../../viewlayer.h" #include "../../commands.h" #include "../autorouter.h" typedef quint64 GridValue; struct GridPoint { int x, y, z; GridValue baseCost; double qCost; uchar flags; bool operator<(const GridPoint&) const; GridPoint(QPoint, int); GridPoint(); }; struct PointZ { QPointF p; int z; PointZ(QPointF _p, int _z) { p = _p; z = _z; } }; struct Net { QList* net; QList< QList > subnets; int pinsWithin; int id; }; struct NetList { QList nets; }; struct Trace { int netIndex; int order; uchar flags; QList gridPoints; Trace() { flags = 0; } }; struct NetOrdering { QList order; }; struct Score { NetOrdering ordering; QMultiHash traces; QHash routedCount; QHash viaCount; int totalRoutedCount; int totalViaCount; int reorderNet; bool anyUnrouted; Score(); void setOrdering(const NetOrdering &); }; struct Nearest { int i, j; double distance; ConnectorItem * ic; ConnectorItem * jc; }; struct Grid { GridValue * data; int x; int y; int z; Grid(int x, int y, int layers); GridValue at(int x, int y, int z) const; void setAt(int x, int y, int z, GridValue value); QList init(int x, int y, int z, int width, int height, const QImage &, GridValue value, bool collectPoints); QList init4(int x, int y, int z, int width, int height, const QImage *, GridValue value, bool collectPoints); void clear(); void free(); void copy(int fromIndex, int toIndex); }; struct NetElements { QList net; QList alsoNet; QList notNet; }; struct RouteThing { QRectF r; QRectF r4; QList layerSpecs; Nearest nearest; std::priority_queue sourceQ; std::priority_queue targetQ; QPoint gridSourcePoint; QPoint gridTargetPoint; GridValue sourceValue; GridValue targetValue; double bestDistanceToTarget; double bestDistanceToSource; GridPoint bestLocationToTarget; GridPoint bestLocationToSource; bool unrouted; NetElements netElements[2]; QSet avoids; }; struct TraceThing { QList newTraces; QList newVias; QList newJumperItems; QList newNetLabels; JumperItem * jumperItem; SymbolPaletteItem * netLabel; QPointF topLeft; GridPoint nextTraceStart; }; struct ConnectionThing { QMultiHash > sd; void add(ConnectorItem * s, ConnectorItem * d); void remove(ConnectorItem * s); void remove(ConnectorItem * s, ConnectorItem * d); bool multi(ConnectorItem * s); QList values(ConnectorItem * s); }; typedef bool (*JumperWillFitFunction)(GridPoint &, const Grid *, int halfSize); typedef double (*CostFunction)(const QPoint & p1, const QPoint & p2); //////////////////////////////////// class MazeRouter : public Autorouter { Q_OBJECT public: MazeRouter(class PCBSketchWidget *, QGraphicsItem * board, bool adjustIf); ~MazeRouter(void); void start(); protected: void setUpWidths(double width); int findPinsWithin(QList * net); bool makeBoard(QImage *, double keepout, const QRectF & r); bool makeMasters(QString &); bool routeNets(NetList &, bool makeJumper, Score & currentScore, const QSizeF gridSize, QList & allOrderings); bool routeOne(bool makeJumper, Score & currentScore, int netIndex, RouteThing &, QList & allOrderings); void findNearestPair(QList< QList > & subnets, Nearest &); void findNearestPair(QList< QList > & subnets, int i, QList & inet, Nearest &); QList renderSource(QDomDocument * masterDoc, int z, ViewLayer::ViewLayerPlacement, Grid * grid, QList & netElements, QList & subnet, GridValue value, bool clearElements, const QRectF & r); QList route(RouteThing &, int & viaCount); void expand(GridPoint &, RouteThing &); void expandOne(GridPoint &, RouteThing &, int dx, int dy, int dz, bool crossLayer); bool viaWillFit(GridPoint &, Grid * grid); QList traceBack(GridPoint, Grid *, int & viaCount, GridValue sourceValue, GridValue targetValue); GridPoint traceBackOne(GridPoint &, Grid *, int dx, int dy, int dz, GridValue sourceValue, GridValue targetValue); void updateDisplay(int iz); void updateDisplay(Grid *, int iz); void updateDisplay(GridPoint &); void clearExpansion(Grid * grid); void prepSourceAndTarget(QDomDocument * masterdoc, RouteThing &, QList< QList > & subnets, int z, ViewLayer::ViewLayerPlacement); bool moveBack(Score & currentScore, int index, QList & allOrderings); void displayTrace(Trace &); void initTraceDisplay(); void traceObstacles(QList & traces, int netIndex, Grid * grid, int ikeepout); void traceAvoids(QList & traces, int netIndex, RouteThing & routeThing); bool routeNext(bool makeJumper, RouteThing &, QList< QList > & subnets, Score & currentScore, int netIndex, QList & allOrderings); void cleanUpNets(NetList &); void createTraces(NetList & netList, Score & bestScore, QUndoCommand * parentCommand); void createTrace(Trace &, QList &, TraceThing &, ConnectionThing &, Net *); void removeColinear(QList & gridPoints); void removeSteps(QList & gridPoints); void removeStep(int ix, QList & gridPoints); ConnectorItem * findAnchor(GridPoint gp, TraceThing &, Net * net, QPointF & p, bool & onTrace, ConnectorItem * already); ConnectorItem * findAnchor(GridPoint gp, const QRectF &, TraceThing &, Net * net, QPointF & p, bool & onTrace, ConnectorItem * already); void addConnectionToUndo(ConnectorItem * from, ConnectorItem * to, QUndoCommand * parentCommand); void addViaToUndo(Via *, QUndoCommand * parentCommand); void addJumperToUndo(JumperItem *, QUndoCommand * parentCommand); void routeJumper(int netIndex, RouteThing &, Score & currentScore); void insertTrace(Trace & newTrace, int netIndex, Score & currentScore, int viaCount, bool incRouted); SymbolPaletteItem * makeNetLabel(GridPoint & center, SymbolPaletteItem * pairedNetLabel, uchar traceFlags); void addNetLabelToUndo(SymbolPaletteItem * netLabel, QUndoCommand * parentCommand); GridPoint lookForJumper(GridPoint initial, GridValue targetValue, QPoint targetLocation); void expandOneJ(GridPoint & gridPoint, std::priority_queue & pq, int dx, int dy, int dz, GridValue targetValue, QPoint targetLocation, QSet & already); void removeOffBoardAnd(bool isPCBType, bool removeSingletons, bool bothSides); void optimizeTraces(QList & order, QMultiHash > > &, QMultiHash &, QMultiHash &, QMultiHash &, NetList &, ConnectionThing &); void reducePoints(QList & points, QPointF topLeft, QList & bundle, int startIndex, int endIndex, ConnectionThing &, int netIndex, ViewLayer::ViewLayerPlacement); public slots: void incCommandProgress(); void setMaxCycles(int); protected: LayerList m_viewLayerIDs; QHash m_masterDocs; double m_keepoutMils; double m_keepoutGrid; int m_keepoutGridInt; int m_halfGridViaSize; int m_halfGridJumperSize; double m_gridPixels; double m_standardWireWidth; QImage * m_displayImage[2]; QImage * m_boardImage; QImage * m_spareImage; QImage * m_spareImage2; QGraphicsPixmapItem * m_displayItem[2]; bool m_temporaryBoard; CostFunction m_costFunction; JumperWillFitFunction m_jumperWillFitFunction; uint m_traceColors[2]; Grid * m_grid; int m_cleanupCount; int m_netLabelIndex; int m_commandCount; }; #endif �����������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/autoroute/panelizer.cpp�����������������������������������������������������0000664�0000000�0000000�00000201503�12723636013�0021616�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "panelizer.h" #include "../debugdialog.h" #include "../sketch/pcbsketchwidget.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../utils/folderutils.h" #include "../utils/folderutils.h" #include "../items/resizableboard.h" #include "../items/logoitem.h" #include "../items/groundplane.h" #include "../fsvgrenderer.h" #include "../fapplication.h" #include "../svg/gerbergenerator.h" #include "../referencemodel/referencemodel.h" #include "../version/version.h" #include "../processeventblocker.h" #include "../connectors/connectoritem.h" #include "../connectors/svgidlayer.h" #include "cmrouter/tileutils.h" #include #include #include #include #include #include #include static int OutlineLayer = 0; static int SilkTopLayer = 0; static QString PanelizerOutputPath; static QSet PanelizerFileNames; /////////////////////////////////////////////////////////// void collectTexts(const QString & svg, QStringList & strings) { QDomDocument doc; doc.setContent(svg); QDomElement root = doc.documentElement(); QDomNodeList domNodeList = root.elementsByTagName("text"); for (int i = 0; i < domNodeList.count(); i++) { QDomElement textElement = domNodeList.at(i).toElement(); QString string; QDomNodeList childList = textElement.childNodes(); for (int j = 0; j < childList.count(); j++) { QDomNode child = childList.item(j); if (child.isText()) { string.append(child.nodeValue()); } } strings.append(string); } } bool byOptionalPriority(PanelItem * p1, PanelItem * p2) { return p1->optionalPriority > p2->optionalPriority; } bool areaGreaterThan(PanelItem * p1, PanelItem * p2) { return p1->boardSizeInches.width() * p1->boardSizeInches.height() > p2->boardSizeInches.width() * p2->boardSizeInches.height(); } int allSpaces(Tile * tile, UserData userData) { QList * tiles = (QList *) userData; if (TiGetType(tile) == Tile::SPACE) { tiles->append(tile); return 0; } tiles->clear(); return 1; // stop the search } int allObstacles(Tile * tile, UserData userData) { if (TiGetType(tile) == Tile::OBSTACLE) { QList * obstacles = (QList *) userData; obstacles->append(tile); } return 0; } static int PlanePairIndex = 0; static double Worst = std::numeric_limits::max() / 4; int roomOn(Tile * tile, TileRect & tileRect, BestPlace * bestPlace) { int w = tileRect.xmaxi - tileRect.xmini; int h = tileRect.ymaxi - tileRect.ymini; if (bestPlace->width <= w && bestPlace->height <= h) { bestPlace->bestTile = tile; return 1; } TileRect temp; temp.xmini = tileRect.xmini; temp.xmaxi = temp.xmini + bestPlace->width; temp.ymini = tileRect.ymini; temp.ymaxi = temp.ymini + bestPlace->height; QList spaces; TiSrArea(tile, bestPlace->plane, &temp, allSpaces, &spaces); if (spaces.count()) { bestPlace->bestTile = tile; return 1; } return 0; } int roomAnywhere(Tile * tile, UserData userData) { if (TiGetType(tile) != Tile::SPACE) return 0; BestPlace * bestPlace = (BestPlace *) userData; TileRect tileRect; TiToRect(tile, &tileRect); return roomOn(tile, tileRect, bestPlace); } int roomOnTop(Tile * tile, UserData userData) { if (TiGetType(tile) != Tile::SPACE) return 0; BestPlace * bestPlace = (BestPlace *) userData; TileRect tileRect; TiToRect(tile, &tileRect); if (tileRect.ymini != bestPlace->maxRect.ymini) return 0; return roomOn(tile, tileRect, bestPlace); } int roomOnBottom(Tile * tile, UserData userData) { if (TiGetType(tile) != Tile::SPACE) return 0; BestPlace * bestPlace = (BestPlace *) userData; TileRect tileRect; TiToRect(tile, &tileRect); if (tileRect.ymaxi != bestPlace->maxRect.ymaxi) return 0; return roomOn(tile, tileRect, bestPlace); } int roomOnLeft(Tile * tile, UserData userData) { if (TiGetType(tile) != Tile::SPACE) return 0; BestPlace * bestPlace = (BestPlace *) userData; TileRect tileRect; TiToRect(tile, &tileRect); if (tileRect.xmini != bestPlace->maxRect.xmini) return 0; return roomOn(tile, tileRect, bestPlace); } int roomOnRight(Tile * tile, UserData userData) { if (TiGetType(tile) != Tile::SPACE) return 0; BestPlace * bestPlace = (BestPlace *) userData; TileRect tileRect; TiToRect(tile, &tileRect); if (tileRect.xmaxi != bestPlace->maxRect.xmaxi) return 0; return roomOn(tile, tileRect, bestPlace); } BestPlace::BestPlace() { bestTile = NULL; bestArea = Worst; } PanelItem::PanelItem() { produced = 0; boardID = 0; refPanelItem = NULL; } PanelItem::PanelItem(PanelItem * from) { this->produced = from->produced; this->boardName = from->boardName; this->path = from->path; this->required = from->required; this->maxOptional = from->maxOptional; this->optionalPriority = from->optionalPriority; this->boardSizeInches = from->boardSizeInches; this->boardID = from->boardID; this->refPanelItem = from; } ///////////////////////////////////////////////////////////////////////////////// void Panelizer::panelize(FApplication * app, const QString & panelFilename, bool customPartsOnly) { QString msg = "panelize"; if (customPartsOnly) msg += " custom parts"; initPanelizerOutput(panelFilename, msg); QFile panelizerFile(panelFilename); QFileInfo info(panelFilename); QDir copyDir = info.absoluteDir(); copyDir.cd("copies"); if (!copyDir.exists()) { writePanelizerOutput(QString("unable to create 'copies' folder in '%1'").arg(info.absoluteDir().absolutePath())); return; } QString errorStr; int errorLine; int errorColumn; DebugDialog::setEnabled(true); QDomDocument panelizerDocument; if (!panelizerDocument.setContent(&panelizerFile, true, &errorStr, &errorLine, &errorColumn)) { writePanelizerOutput(QString("Unable to parse '%1': '%2' line:%3 column:%4").arg(panelFilename).arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement panelizerRoot = panelizerDocument.documentElement(); if (panelizerRoot.isNull() || panelizerRoot.tagName() != "panelizer") { writePanelizerOutput(QString("root element is not 'panelizer'")); return; } PanelParams panelParams; if (!initPanelParams(panelizerRoot, panelParams)) return; QFileInfo pinfo(panelFilename); QDir outputDir = pinfo.absoluteDir(); if (customPartsOnly) { outputDir.mkdir("custom"); outputDir.cd("custom"); } outputDir.mkdir("svg"); outputDir.mkdir("gerber"); outputDir.mkdir("fz"); QDir svgDir(outputDir); svgDir.cd("svg"); if (!svgDir.exists()) { writePanelizerOutput(QString("unable to create svg folder in '%1'").arg(pinfo.absolutePath())); return; } DebugDialog::debug(QString("svg folder '%1'\n").arg(svgDir.absolutePath())); QDir gerberDir(outputDir); gerberDir.cd("gerber"); if (!gerberDir.exists()) { writePanelizerOutput(QString("unable to create gerber folder in '%1'").arg(pinfo.absolutePath())); return; } DebugDialog::debug(QString("gerber folder '%1'\n").arg(gerberDir.absolutePath())); QDir fzDir(outputDir); fzDir.cd("fz"); if (!fzDir.exists()) { writePanelizerOutput(QString("unable to create fz folder in '%1'").arg(pinfo.absolutePath())); return; } DebugDialog::debug(QString("fz folder '%1'\n").arg(fzDir.absolutePath())); QDomElement boards = panelizerRoot.firstChildElement("boards"); QDomElement board = boards.firstChildElement("board"); if (board.isNull()) { writePanelizerOutput(QString("no elements found")); return; } QHash fzzFilePaths; QDomElement paths = panelizerRoot.firstChildElement("paths"); QDomElement path = paths.firstChildElement("path"); if (path.isNull()) { writePanelizerOutput(QString("no elements found")); return; } collectFiles(pinfo.absoluteDir(), path, fzzFilePaths); if (fzzFilePaths.count() == 0) { writePanelizerOutput(QString("no fzz files found in paths")); return; } board = boards.firstChildElement("board"); if (!checkBoards(board, fzzFilePaths)) return; app->createUserDataStoreFolderStructures(); app->registerFonts(); app->loadReferenceModel("", false); QList layerThingList; layerThingList.append(LayerThing("outline", ViewLayer::outlineLayers(), SVG2gerber::ForOutline, GerberGenerator::OutlineSuffix)); layerThingList.append(LayerThing("copper_top", ViewLayer::copperLayers(ViewLayer::NewTop), SVG2gerber::ForCopper, GerberGenerator::CopperTopSuffix)); layerThingList.append(LayerThing("copper_bottom", ViewLayer::copperLayers(ViewLayer::NewBottom), SVG2gerber::ForCopper, GerberGenerator::CopperBottomSuffix)); layerThingList.append(LayerThing("mask_top", ViewLayer::maskLayers(ViewLayer::NewTop), SVG2gerber::ForMask, GerberGenerator:: MaskTopSuffix)); layerThingList.append(LayerThing("mask_bottom", ViewLayer::maskLayers(ViewLayer::NewBottom), SVG2gerber::ForMask, GerberGenerator::MaskBottomSuffix)); layerThingList.append(LayerThing("paste_mask_top", ViewLayer::maskLayers(ViewLayer::NewTop), SVG2gerber::ForPasteMask, GerberGenerator:: PasteMaskTopSuffix)); layerThingList.append(LayerThing("paste_mask_bottom", ViewLayer::maskLayers(ViewLayer::NewBottom), SVG2gerber::ForPasteMask, GerberGenerator::PasteMaskBottomSuffix)); layerThingList.append(LayerThing("silk_top", ViewLayer::silkLayers(ViewLayer::NewTop), SVG2gerber::ForSilk, GerberGenerator::SilkTopSuffix)); layerThingList.append(LayerThing("silk_bottom", ViewLayer::silkLayers(ViewLayer::NewBottom), SVG2gerber::ForSilk, GerberGenerator::SilkBottomSuffix)); layerThingList.append(LayerThing("drill", ViewLayer::drillLayers(), SVG2gerber::ForDrill, GerberGenerator::DrillSuffix)); for (int i = 0; i < layerThingList.count(); i++) { LayerThing layerThing = layerThingList.at(i); if (layerThing.name.compare("outline") == 0) OutlineLayer = i; else if (layerThing.name.compare("silk_top") == 0) SilkTopLayer = i; } QList refPanelItems; board = boards.firstChildElement("board"); if (!openWindows(board, fzzFilePaths, app, panelParams, fzDir, svgDir, refPanelItems, layerThingList, customPartsOnly, copyDir)) return; QList planePairs; QList insertPanelItems; int duplicates = bestFitLoop(refPanelItems, panelParams, customPartsOnly, planePairs, insertPanelItems, svgDir); foreach (PanelItem * panelItem, insertPanelItems) { if (panelItem->refPanelItem) { panelItem->refPanelItem->produced += duplicates; } } QString custom = customPartsOnly ? "custom." : ""; foreach (PlanePair * planePair, planePairs) { planePair->layoutSVG += ""; QString fname = svgDir.absoluteFilePath(QString("%1%2.panel_%3.layout.svg").arg(custom).arg(panelParams.prefix).arg(planePair->index)); TextUtils::writeUtf8(fname, planePair->layoutSVG); } foreach (PlanePair * planePair, planePairs) { for (int i = 0; i < layerThingList.count(); i++) { planePair->svgs << TextUtils::makeSVGHeader(1, GraphicsUtils::StandardFritzingDPI, planePair->panelWidth, planePair->panelHeight); } QList needToRotate; foreach (PanelItem * panelItem, insertPanelItems) { if (panelItem->planePair != planePair) continue; DebugDialog::debug(QString("placing %1 on panel %2").arg(panelItem->boardName).arg(planePair->index)); doOnePanelItem(planePair, layerThingList, panelItem, svgDir); } DebugDialog::debug("after placement"); QString prefix = QString("%1%2.panel_%3").arg(custom).arg(panelParams.prefix).arg(planePair->index); for (int i = 0; i < planePair->svgs.count(); i++) { if (planePair->svgs.at(i).isEmpty()) continue; planePair->svgs.replace(i, planePair->svgs.at(i) + ""); QString fname = svgDir.absoluteFilePath(QString("%1.%2.svg").arg(prefix).arg(layerThingList.at(i).name)); TextUtils::writeUtf8(fname, planePair->svgs.at(i)); QString suffix = layerThingList.at(i).suffix; DebugDialog::debug("converting " + prefix + " " + suffix); QSizeF svgSize(planePair->panelWidth, planePair->panelHeight); SVG2gerber::ForWhy forWhy = layerThingList.at(i).forWhy; if (forWhy == SVG2gerber::ForMask || forWhy == SVG2gerber::ForPasteMask) forWhy = SVG2gerber::ForCopper; GerberGenerator::doEnd(planePair->svgs.at(i), 2, layerThingList.at(i).name, forWhy, svgSize * GraphicsUtils::StandardFritzingDPI, gerberDir.absolutePath(), prefix, suffix, false); DebugDialog::debug("after converting " + prefix + " " + suffix); } QDomDocument doc; QString merger = planePair->svgs.at(OutlineLayer); merger.replace("black", "#90f0a0"); merger.replace("#000000", "#90f0a0"); merger.replace("fill-opacity=\"0.5\"", "fill-opacity=\"1\""); //DebugDialog::debug("outline identification"); //DebugDialog::debug(merger); //DebugDialog::debug(""); TextUtils::mergeSvg(doc, merger, ""); merger = planePair->svgs.at(SilkTopLayer); merger.replace("black", "#909090"); merger.replace("#000000", "#909090"); //DebugDialog::debug("silk identification"); //DebugDialog::debug(merger); //DebugDialog::debug(""); TextUtils::mergeSvg(doc, merger, ""); merger = planePair->layoutSVG; // layout merger.replace("'red'", "'none'"); // hide background rect //DebugDialog::debug("layout identification"); //DebugDialog::debug(merger); //DebugDialog::debug(""); TextUtils::mergeSvg(doc, merger, ""); merger = TextUtils::mergeSvgFinish(doc); int endix = merger.lastIndexOf(""); QString text = "%3 panel %4 %5 %6\n"; text = text .arg(planePair->panelWidth * GraphicsUtils::StandardFritzingDPI) .arg(planePair->panelHeight * GraphicsUtils::StandardFritzingDPI + (250 / 2)) .arg(outputDir.dirName()) .arg(planePair->index) .arg(panelParams.prefix) .arg(QTime::currentTime().toString()) ; merger.insert(endix, text); QString fname = svgDir.absoluteFilePath(QString("%1.%2.svg").arg(prefix).arg("identification")); TextUtils::writeUtf8(fname, merger); // save to pdf QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); QString pdfname = fname; pdfname.replace(".svg", ".pdf"); printer.setOutputFileName(pdfname); int res = printer.resolution(); // now convert to pdf QSvgRenderer svgRenderer; svgRenderer.load(merger.toLatin1()); double trueWidth = planePair->panelWidth; double trueHeight = planePair->panelHeight; QRectF target(0, 0, trueWidth * res, trueHeight * res); QSizeF psize((target.width() + printer.paperRect().width() - printer.width()) / res, (target.height() + printer.paperRect().height() - printer.height()) / res); printer.setPaperSize(psize, QPrinter::Inch); QPainter painter; if (painter.begin(&printer)) { svgRenderer.render(&painter, target); } painter.end(); } boards = panelizerRoot.firstChildElement("boards"); board = boards.firstChildElement("board"); while (!board.isNull()) { foreach (PanelItem * panelItem, refPanelItems) { if (panelItem->path.endsWith("/" + board.attribute("name"))) { board.setAttribute("produced", panelItem->produced); break; } } board = board.nextSiblingElement("board"); } TextUtils::writeUtf8(panelFilename, panelizerDocument.toString(4)); QString message = QString("Panelizer finished: %1 panel(s), with %2 additional copy(ies) for each panel").arg(planePairs.count()).arg(duplicates - 1); writePanelizerOutput("--------------------------------"); writePanelizerOutput(message); QMessageBox::information(NULL, QObject::tr("Fritzing"), message); } void Panelizer::bestFit(QList & insertPanelItems, PanelParams & panelParams, QList & planePairs, bool customPartsOnly) { foreach (PanelItem * panelItem, insertPanelItems) { bestFitOne(panelItem, panelParams, planePairs, true, customPartsOnly); } } bool Panelizer::bestFitOne(PanelItem * panelItem, PanelParams & panelParams, QList & planePairs, bool createNew, bool customPartsOnly) { //DebugDialog::debug(QString("panel %1").arg(panelItem->boardName)); BestPlace bestPlace1, bestPlace2; bestPlace1.rotate90 = bestPlace2.rotate90 = false; bestPlace1.width = bestPlace2.width = realToTile(panelItem->boardSizeInches.width() + panelParams.panelSpacing); bestPlace1.height = bestPlace2.height = realToTile(panelItem->boardSizeInches.height() + panelParams.panelSpacing); int ppix = 0; while (ppix < planePairs.count()) { PlanePair * planePair = planePairs.at(ppix); bestPlace1.plane = planePair->thePlane; bestPlace2.plane = planePair->thePlane90; bestPlace1.maxRect = planePair->tilePanelRect; TiSrArea(NULL, planePair->thePlane, &planePair->tilePanelRect, placeBestFit, &bestPlace1); if (customPartsOnly) { // make sure board is rotated bestPlace1.bestTile = NULL; DebugDialog::debug("forcing rotation"); } if (bestPlace1.bestTile == NULL) { bestPlace2.maxRect = planePair->tilePanelRect90; TiSrArea(NULL, planePair->thePlane90, &planePair->tilePanelRect90, placeBestFit, &bestPlace2); } if (bestPlace1.bestTile == NULL && bestPlace2.bestTile == NULL ) { if (++ppix < planePairs.count()) { // try next panel continue; } if (!createNew) { return false; } // create next panel planePair = makePlanePair(panelParams, true); planePairs << planePair; DebugDialog::debug(QString("ran out of room placing %1").arg(panelItem->boardName)); continue; } bool use2 = false; if (bestPlace1.bestTile == NULL) { use2 = true; } else if (bestPlace2.bestTile == NULL) { } else { // never actually get here use2 = bestPlace2.bestArea < bestPlace1.bestArea; } if (use2) { tileUnrotate90(bestPlace2.bestTileRect, bestPlace1.bestTileRect); bestPlace1.rotate90 = !bestPlace2.rotate90; } panelItem->x = tileToReal(bestPlace1.bestTileRect.xmini) ; panelItem->y = tileToReal(bestPlace1.bestTileRect.ymini); panelItem->rotate90 = bestPlace1.rotate90; DebugDialog::debug(QString("setting rotate90:%1 %2").arg(panelItem->rotate90).arg(panelItem->path)); panelItem->planePair = planePair; TileRect tileRect; tileRect.xmini = bestPlace1.bestTileRect.xmini; tileRect.ymini = bestPlace1.bestTileRect.ymini; if (bestPlace1.rotate90) { tileRect.xmaxi = tileRect.xmini + bestPlace1.height; tileRect.ymaxi = tileRect.ymini + bestPlace1.width; } else { tileRect.ymaxi = tileRect.ymini + bestPlace1.height; tileRect.xmaxi = tileRect.xmini + bestPlace1.width; } double w = panelItem->boardSizeInches.width(); double h = panelItem->boardSizeInches.height(); if (panelItem->rotate90) { w = h; h = panelItem->boardSizeInches.width(); } planePair->layoutSVG += QString("\n") .arg(panelItem->x * GraphicsUtils::StandardFritzingDPI) .arg(panelItem->y * GraphicsUtils::StandardFritzingDPI) .arg(GraphicsUtils::StandardFritzingDPI * w) .arg(GraphicsUtils::StandardFritzingDPI * h); QStringList strings = QFileInfo(panelItem->path).completeBaseName().split("_"); if (strings.count() >= 5) { QString start = strings.takeFirst(); QStringList middle; middle << strings.at(0) << strings.at(1) << strings.at(2); strings.removeAt(0); strings.removeAt(0); strings.removeAt(0); QString end = strings.join("_"); strings.clear(); strings << start << middle.join(" ") << end; } double cx = GraphicsUtils::StandardFritzingDPI * (panelItem->x + (w / 2)); int fontSize1 = 250; int fontSize2 = 150; int fontSize = fontSize1; double cy = GraphicsUtils::StandardFritzingDPI * (panelItem->y + (h / 2)); cy -= ((strings.count() - 1) * fontSize2 / 2); foreach (QString string, strings) { planePair->layoutSVG += QString("%4\n") .arg(cx) .arg(cy) .arg(fontSize) .arg(string) .arg(OCRAFontName); cy += fontSize; if (fontSize == fontSize1) fontSize = fontSize2; } TiInsertTile(planePair->thePlane, &tileRect, NULL, Tile::OBSTACLE); TileRect tileRect90; tileRotate90(tileRect, tileRect90); TiInsertTile(planePair->thePlane90, &tileRect90, NULL, Tile::OBSTACLE); return true; } DebugDialog::debug("bestFitOne should never reach here"); return false; } PlanePair * Panelizer::makePlanePair(PanelParams & panelParams, bool big) { PlanePair * planePair = new PlanePair; if (big) { foreach (PanelType * panelType, panelParams.panelTypes) { if (panelType->name == "big") { planePair->panelWidth = panelType->width; planePair->panelHeight = panelType->height; break; } } } else { foreach (PanelType * panelType, panelParams.panelTypes) { if (panelType->name == "small") { planePair->panelWidth = panelType->width; planePair->panelHeight = panelType->height; break; } } } // for debugging planePair->layoutSVG = TextUtils::makeSVGHeader(1, GraphicsUtils::StandardFritzingDPI, planePair->panelWidth, planePair->panelHeight); planePair->index = PlanePairIndex++; Tile * bufferTile = TiAlloc(); TiSetType(bufferTile, Tile::BUFFER); TiSetBody(bufferTile, NULL); QRectF panelRect(0, 0, planePair->panelWidth + panelParams.panelSpacing - panelParams.panelBorder, planePair->panelHeight + panelParams.panelSpacing - panelParams.panelBorder); int l = fasterRealToTile(panelRect.left() - 10); int t = fasterRealToTile(panelRect.top() - 10); int r = fasterRealToTile(panelRect.right() + 10); int b = fasterRealToTile(panelRect.bottom() + 10); SETLEFT(bufferTile, l); SETYMIN(bufferTile, t); planePair->thePlane = TiNewPlane(bufferTile, l, t, r, b); SETRIGHT(bufferTile, r); SETYMAX(bufferTile, b); qrectToTile(panelRect, planePair->tilePanelRect); TiInsertTile(planePair->thePlane, &planePair->tilePanelRect, NULL, Tile::SPACE); QMatrix matrix90; matrix90.rotate(90); QRectF panelRect90 = matrix90.mapRect(panelRect); Tile * bufferTile90 = TiAlloc(); TiSetType(bufferTile90, Tile::BUFFER); TiSetBody(bufferTile90, NULL); l = fasterRealToTile(panelRect90.left() - 10); t = fasterRealToTile(panelRect90.top() - 10); r = fasterRealToTile(panelRect90.right() + 10); b = fasterRealToTile(panelRect90.bottom() + 10); SETLEFT(bufferTile90, l); SETYMIN(bufferTile90, t); planePair->thePlane90 = TiNewPlane(bufferTile90, l, t, r, b); SETRIGHT(bufferTile90, r); SETYMAX(bufferTile90, b); qrectToTile(panelRect90, planePair->tilePanelRect90); TiInsertTile(planePair->thePlane90, &planePair->tilePanelRect90, NULL, Tile::SPACE); return planePair; } void Panelizer::collectFiles(const QDir & outputFolder, QDomElement & path, QHash & fzzFilePaths) { QList dirList; dirList << outputFolder; while (!path.isNull()) { QDomNode node = path.firstChild(); if (!node.isText()) { DebugDialog::debug(QString("missing text in element")); return; } QString p = node.nodeValue().trimmed(); DebugDialog::debug("p folder " + p); QString savep = p; if (savep.startsWith(".")) { p = outputFolder.absolutePath() + "/" + savep; } QDir dir(p); if (!dir.exists()) { DebugDialog::debug(QString("Directory '%1' doesn't exist").arg(p)); return; } dirList << dir; path = path.nextSiblingElement("path"); } foreach (QDir dir, dirList) { DebugDialog::debug("directory " + dir.absolutePath()); QStringList filepaths; QStringList filters("*" + FritzingBundleExtension); FolderUtils::collectFiles(dir, filters, filepaths, false); foreach (QString filepath, filepaths) { QFileInfo fileInfo(filepath); fzzFilePaths.insert(fileInfo.fileName(), filepath); } } } bool Panelizer::checkBoards(QDomElement & board, QHash & fzzFilePaths) { while (!board.isNull()) { QString boardname = board.attribute("name"); //DebugDialog::debug(QString("board %1").arg(boardname)); bool ok; int optional = board.attribute("maxOptionalCount", "").toInt(&ok); if (!ok) { writePanelizerOutput(QString("maxOptionalCount for board '%1' not an integer: '%2'").arg(boardname).arg(board.attribute("maxOptionalCount"))); return false; } int optionalPriority = board.attribute("optionalPriority", "").toInt(&ok); Q_UNUSED(optionalPriority); if (!ok) { writePanelizerOutput(QString("optionalPriority for board '%1' not an integer: '%2'").arg(boardname).arg(board.attribute("optionalPriority"))); return false; } int required = board.attribute("requiredCount", "").toInt(&ok); if (!ok) { writePanelizerOutput(QString("required for board '%1' not an integer: '%2'").arg(boardname).arg(board.attribute("maxOptionalCount"))); return false; } if (optional > 0 || required> 0) { QString path = fzzFilePaths.value(boardname, ""); if (path.isEmpty()) { writePanelizerOutput(QString("File for board '%1' not found in search paths").arg(boardname)); return false; } } else { writePanelizerOutput(QString("skipping board '%1'").arg(boardname)); } board = board.nextSiblingElement("board"); } return true; } bool Panelizer::openWindows(QDomElement & boardElement, QHash & fzzFilePaths, FApplication * app, PanelParams & panelParams, QDir & fzDir, QDir & svgDir, QList & refPanelItems, QList & layerThingList, bool customPartsOnly, QDir & copyDir) { QDir rotateDir(svgDir); QDir norotateDir(svgDir); rotateDir.mkdir("rotate"); rotateDir.cd("rotate"); norotateDir.mkdir("norotate"); norotateDir.cd("norotate"); PanelType * bigPanelType = NULL; foreach (PanelType * panelType, panelParams.panelTypes) { if (panelType->name == "big") { bigPanelType = panelType; break; } } if (bigPanelType == NULL) { writePanelizerOutput("No panel types defined"); return false; } while (!boardElement.isNull()) { int required = boardElement.attribute("requiredCount", "").toInt(); int optional = boardElement.attribute("maxOptionalCount", "").toInt(); int priority = boardElement.attribute("optionalPriority", "").toInt(); if (customPartsOnly) { optional = 0; if (required > 1) required = 1; } if (required == 0 && optional == 0) { boardElement = boardElement.nextSiblingElement("board"); continue; } QString boardName = boardElement.attribute("name"); QString originalPath = fzzFilePaths.value(boardName, ""); QFileInfo originalInfo(originalPath); QString copyPath = copyDir.absoluteFilePath(boardName); QFileInfo copyInfo(copyPath); if (!copyInfo.exists()) { writePanelizerOutput(QString("failed to load copy'%1'").arg(copyPath)); return false; } if (!originalInfo.exists()) { writePanelizerOutput(QString("failed to find original'%1'").arg(originalPath)); return false; } if (originalInfo.lastModified() > copyInfo.lastModified()) { writePanelizerOutput(QString("copy %1 is not up to date--rerun the inscriber").arg(copyPath)); return false; } MainWindow * mainWindow = app->openWindowForService(false, 3); mainWindow->setCloseSilently(true); FolderUtils::setOpenSaveFolderAux(fzDir.absolutePath()); if (!mainWindow->loadWhich(copyPath, false, false, false, "")) { writePanelizerOutput(QString("failed to load '%1'").arg(copyPath)); return false; } if (customPartsOnly && !mainWindow->hasAnyAlien() && !mainWindow->hasCustomBoardShape() && (mainWindow->pcbView()->checkLoadedTraces() == 0) && (checkDonuts(mainWindow, false) == 0) && (checkText(mainWindow, false) == 0) ) { mainWindow->close(); delete mainWindow, boardElement = boardElement.nextSiblingElement("board"); continue; } foreach (QGraphicsItem * item, mainWindow->pcbView()->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemBase->setMoveLock(false); } QList boards = mainWindow->pcbView()->findBoard(); foreach (ItemBase * boardItem, boards) { PanelItem * panelItem = new PanelItem; panelItem->boardName = boardName; panelItem->path = originalPath; panelItem->required = required; panelItem->maxOptional = optional; panelItem->optionalPriority = priority; panelItem->boardID = boardItem->id(); QRectF sbr = boardItem->layerKinChief()->sceneBoundingRect(); panelItem->boardSizeInches = sbr.size() / GraphicsUtils::SVGDPI; DebugDialog::debug(QString("board size inches c %1, %2, %3") .arg(panelItem->boardSizeInches.width()) .arg(panelItem->boardSizeInches.height()) .arg(copyPath)); /* QSizeF boardSize = boardItem->size(); ResizableBoard * resizableBoard = qobject_cast(boardItem->layerKinChief()); if (resizableBoard != NULL) { panelItem->boardSizeInches = resizableBoard->getSizeMM() / 25.4; DebugDialog::debug(QString("board size inches a %1, %2, %3") .arg(panelItem->boardSizeInches.width()) .arg(panelItem->boardSizeInches.height()) .arg(path), boardItem->sceneBoundingRect()); } else { panelItem->boardSizeInches = boardSize / GraphicsUtils::SVGDPI; DebugDialog::debug(QString("board size inches b %1, %2, %3") .arg(panelItem->boardSizeInches.width()) .arg(panelItem->boardSizeInches.height()) .arg(path), boardItem->sceneBoundingRect()); } */ bool tooBig = false; if (panelItem->boardSizeInches.width() >= bigPanelType->width) { tooBig = panelItem->boardSizeInches.width() >= bigPanelType->height; if (!tooBig) { tooBig = panelItem->boardSizeInches.height() >= bigPanelType->width; } } if (!tooBig) { if (panelItem->boardSizeInches.height() >= bigPanelType->height) { tooBig = panelItem->boardSizeInches.height() >= bigPanelType->width; if (!tooBig) { tooBig = panelItem->boardSizeInches.width() >= bigPanelType->height; } } } if (tooBig) { writePanelizerOutput(QString("board is too big for panel '%1'").arg(originalPath)); return false; } makeSVGs(mainWindow, boardItem, boardName, layerThingList, norotateDir, copyInfo); refPanelItems << panelItem; } // now save the rotated version mainWindow->pcbView()->selectAllItems(true, false); QMatrix matrix; mainWindow->pcbView()->rotateX(90, false, NULL); foreach (ItemBase * boardItem, boards) { makeSVGs(mainWindow, boardItem, boardName, layerThingList, rotateDir, copyInfo); } mainWindow->close(); delete mainWindow, boardElement = boardElement.nextSiblingElement("board"); } return true; } bool Panelizer::initPanelParams(QDomElement & root, PanelParams & panelParams) { panelParams.prefix = root.attribute("prefix"); if (panelParams.prefix.isEmpty()) { writePanelizerOutput(QString("Output file prefix not specified")); return false; } QDomElement panels = root.firstChildElement("panels"); QDomElement panel = panels.firstChildElement("panel"); while (!panel.isNull()) { PanelType * panelType = new PanelType; panelType->name = panel.attribute("name"); bool ok; panelType->width = TextUtils::convertToInches(panel.attribute("width"), &ok, false); if (!ok) { writePanelizerOutput(QString("Can't parse panel width '%1'").arg(panel.attribute("width"))); return false; } panelType->height = TextUtils::convertToInches(panel.attribute("height"), &ok, false); if (!ok) { writePanelizerOutput(QString("Can't parse panel height '%1'").arg(panel.attribute("height"))); return false; } panelType->c1 = panel.attribute("c1").toDouble(&ok); if (!ok) { writePanelizerOutput(QString("Can't parse panel c1 '%1'").arg(panel.attribute("c1"))); return false; } panelType->c2 = panel.attribute("c2").toDouble(&ok); if (!ok) { writePanelizerOutput(QString("Can't parse panel c2 '%1'").arg(panel.attribute("c2"))); return false; } panelParams.panelTypes << panelType; panel = panel.nextSiblingElement("panel"); } if (panelParams.panelTypes.count() == 0) { writePanelizerOutput(QString("No panel types defined.")); return false; } bool ok; panelParams.panelSpacing = TextUtils::convertToInches(root.attribute("spacing"), &ok, false); if (!ok) { writePanelizerOutput(QString("Can't parse panel spacing '%1'").arg(root.attribute("spacing"))); return false; } panelParams.panelBorder = TextUtils::convertToInches(root.attribute("border"), &ok, false); if (!ok) { writePanelizerOutput(QString("Can't parse panel border '%1'").arg(root.attribute("border"))); return false; } return true; } int Panelizer::placeBestFit(Tile * tile, UserData userData) { if (TiGetType(tile) != Tile::SPACE) return 0; BestPlace * bestPlace = (BestPlace *) userData; TileRect tileRect; TiToRect(tile, &tileRect); int w = tileRect.xmaxi - tileRect.xmini; int h = tileRect.ymaxi - tileRect.ymini; if (bestPlace->width > w && bestPlace->height > w) { return 0; } int fitCount = 0; bool fit[4]; double area[4]; for (int i = 0; i < 4; i++) { fit[i] = false; area[i] = Worst; } if (w >= bestPlace->width && h >= bestPlace->height) { fit[0] = true; area[0] = (w * h) - (bestPlace->width * bestPlace->height); fitCount++; } if (h >= bestPlace->width && w >= bestPlace->height) { fit[1] = true; area[1] = (w * h) - (bestPlace->width * bestPlace->height); fitCount++; } if (!fit[0] && w >= bestPlace->width) { // see if adjacent tiles below are open TileRect temp; temp.xmini = tileRect.xmini; temp.xmaxi = temp.xmini + bestPlace->width; temp.ymini = tileRect.ymini; temp.ymaxi = temp.ymini + bestPlace->height; if (temp.ymaxi < bestPlace->maxRect.ymaxi) { QList spaces; TiSrArea(tile, bestPlace->plane, &temp, allSpaces, &spaces); if (spaces.count()) { int y = temp.ymaxi; foreach (Tile * t, spaces) { if (YMAX(t) > y) y = YMAX(t); // find the bottom of the lowest open tile } fit[2] = true; fitCount++; area[2] = (w * (y - temp.ymini)) - (bestPlace->width * bestPlace->height); } } } if (!fit[1] && w >= bestPlace->height) { // see if adjacent tiles below are open TileRect temp; temp.xmini = tileRect.xmini; temp.xmaxi = temp.xmini + bestPlace->height; temp.ymini = tileRect.ymini; temp.ymaxi = temp.ymini + bestPlace->width; if (temp.ymaxi < bestPlace->maxRect.ymaxi) { QList spaces; TiSrArea(tile, bestPlace->plane, &temp, allSpaces, &spaces); if (spaces.count()) { int y = temp.ymaxi; foreach (Tile * t, spaces) { if (YMAX(t) > y) y = YMAX(t); // find the bottom of the lowest open tile } fit[3] = true; fitCount++; area[3] = (w * (y - temp.ymini)) - (bestPlace->width * bestPlace->height); } } } if (fitCount == 0) return 0; // area is white space remaining after board has been inserteds int result = -1; for (int i = 0; i < 4; i++) { if (area[i] < bestPlace->bestArea) { result = i; break; } } if (result < 0) return 0; // current bestArea is better bestPlace->bestTile = tile; bestPlace->bestTileRect = tileRect; if (fitCount == 1 || (bestPlace->width == bestPlace->height)) { if (fit[0] || fit[2]) { bestPlace->rotate90 = false; } else { bestPlace->rotate90 = true; } bestPlace->bestArea = area[result]; return 0; } if (TiGetType(BL(tile)) == Tile::BUFFER) { // this is a leftmost tile // select for creating the biggest area after putting in the tile; double a1 = (w - bestPlace->width) * (bestPlace->height); double a2 = (h - bestPlace->height) * w; double a = qMax(a1, a2); double b1 = (w - bestPlace->height) * (bestPlace->width); double b2 = (h - bestPlace->width) * w; double b = qMax(b1, b2); bestPlace->rotate90 = (a < b); if (bestPlace->rotate90) { bestPlace->bestArea = fit[1] ? area[1] : area[3]; } else { bestPlace->bestArea = fit[0] ? area[0] : area[2]; } return 0; } TileRect temp; temp.xmini = bestPlace->maxRect.xmini; temp.xmaxi = tileRect.xmini - 1; temp.ymini = tileRect.ymini; temp.ymaxi = tileRect.ymaxi; QList obstacles; TiSrArea(tile, bestPlace->plane, &temp, allObstacles, &obstacles); int maxBottom = 0; foreach (Tile * obstacle, obstacles) { if (YMAX(obstacle) > maxBottom) maxBottom = YMAX(obstacle); } if (tileRect.ymini + bestPlace->width <= maxBottom && tileRect.ymini + bestPlace->height <= maxBottom) { // use the max length if (bestPlace->width >= bestPlace->height) { bestPlace->rotate90 = true; bestPlace->bestArea = fit[1] ? area[1] : area[3]; } else { bestPlace->rotate90 = false; bestPlace->bestArea = fit[0] ? area[0] : area[2]; } return 0; } if (tileRect.ymini + bestPlace->width > maxBottom && tileRect.ymini + bestPlace->height > maxBottom) { // use the min length if (bestPlace->width <= bestPlace->height) { bestPlace->rotate90 = true; bestPlace->bestArea = fit[1] ? area[1] : area[3]; } else { bestPlace->rotate90 = false; bestPlace->bestArea = fit[0] ? area[0] : area[2]; } return 0; } if (tileRect.ymini + bestPlace->width <= maxBottom) { bestPlace->rotate90 = true; bestPlace->bestArea = fit[1] ? area[1] : area[3]; return 0; } bestPlace->rotate90 = false; bestPlace->bestArea = fit[0] ? area[0] : area[2]; return 0; } void Panelizer::addOptional(int optionalCount, QList & refPanelItems, QList & insertPanelItems, PanelParams & panelParams, QList & planePairs) { if (optionalCount == 0) return; QList copies(refPanelItems); qSort(copies.begin(), copies.end(), byOptionalPriority); while (optionalCount > 0) { int pool = 0; int priority = -1; foreach(PanelItem * panelItem, copies) { if (panelItem->maxOptional > 0) { if (priority == -1) priority = panelItem->optionalPriority; if (panelItem->optionalPriority == priority) { pool += panelItem->maxOptional; } else break; } } if (pool == 0) break; int ix = qFloor(qrand() * pool / (double) RAND_MAX); int soFar = 0; foreach (PanelItem * panelItem, copies) { if (panelItem->maxOptional == 0) continue; if (panelItem->optionalPriority != priority) continue; if (ix >= soFar && ix < soFar + panelItem->maxOptional) { PanelItem * copy = new PanelItem(panelItem); if (bestFitOne(copy, panelParams, planePairs, false, false)) { // got one panelItem->maxOptional--; optionalCount--; insertPanelItems.append(copy); } else { // don't bother trying this one again optionalCount -= panelItem->maxOptional; panelItem->maxOptional = 0; } break; } soFar += panelItem->maxOptional; } } } ///////////////////////////////////////////////////////////////////////////////// void Panelizer::inscribe(FApplication * app, const QString & panelFilename, bool drc, bool noMessages) { initPanelizerOutput(panelFilename, "inscribe"); QFile file(panelFilename); QFileInfo info(panelFilename); QDir copyDir = info.absoluteDir(); copyDir.mkdir("copies"); copyDir.cd("copies"); if (!copyDir.exists()) { DebugDialog::debug(QString("unable to create 'copies' folder in '%1'").arg(info.absoluteDir().absolutePath())); return; } QString errorStr; int errorLine; int errorColumn; DebugDialog::setEnabled(true); QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { writePanelizerOutput(QString("Unable to parse '%1': '%2' line:%3 column:%4").arg(panelFilename).arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement root = domDocument.documentElement(); if (root.isNull() || root.tagName() != "panelizer") { writePanelizerOutput(QString("root element is not 'panelizer'")); return; } PanelParams panelParams; if (!initPanelParams(root, panelParams)) return; QDir outputDir = QDir::temp(); QDir fzDir(outputDir); fzDir.cd("fz"); if (!fzDir.exists()) { writePanelizerOutput(QString("unable to create fz folder in '%1'").arg(outputDir.absolutePath())); return; } DebugDialog::debug(QString("fz folder '%1'\n").arg(fzDir.absolutePath())); QDomElement boards = root.firstChildElement("boards"); QDomElement board = boards.firstChildElement("board"); if (board.isNull()) { writePanelizerOutput(QString("no elements found")); return; } QHash fzzFilePaths; QDomElement paths = root.firstChildElement("paths"); QDomElement path = paths.firstChildElement("path"); if (path.isNull()) { writePanelizerOutput(QString("no elements found")); return; } QFileInfo pinfo(panelFilename); collectFiles(pinfo.absoluteDir(), path, fzzFilePaths); if (fzzFilePaths.count() == 0) { writePanelizerOutput(QString("no fzz files found in paths")); return; } board = boards.firstChildElement("board"); if (!checkBoards(board, fzzFilePaths)) return; app->createUserDataStoreFolderStructures(); app->registerFonts(); app->loadReferenceModel("", false); board = boards.firstChildElement("board"); while (!board.isNull()) { MainWindow * mainWindow = inscribeBoard(board, fzzFilePaths, app, fzDir, drc, noMessages, copyDir); if (mainWindow) { mainWindow->setCloseSilently(true); mainWindow->close(); delete mainWindow; } board = board.nextSiblingElement("board"); } writePanelizerFilenames(panelFilename); // TODO: delete temp fz folder } MainWindow * Panelizer::inscribeBoard(QDomElement & board, QHash & fzzFilePaths, FApplication * app, QDir & fzDir, bool drc, bool noMessages, QDir & copyDir) { QString boardName = board.attribute("name"); int optional = board.attribute("maxOptionalCount", "").toInt(); int required = board.attribute("requiredCount", "").toInt(); if (optional <= 0 && required <= 0) return NULL; QString originalPath = fzzFilePaths.value(boardName, ""); QFileInfo originalInfo(originalPath); QString copyPath = copyDir.absoluteFilePath(originalInfo.fileName()); QFileInfo copyInfo(copyPath); if (!copyInfo.exists()) { } else { DebugDialog::debug(""); //DebugDialog::debug(oldPath); //DebugDialog::debug(copyPath); DebugDialog::debug(QString("%1 original=%2, copy=%3").arg(originalInfo.fileName()).arg(originalInfo.lastModified().toString()).arg(copyInfo.lastModified().toString())); if (originalInfo.lastModified() <= copyInfo.lastModified()) { DebugDialog::debug(QString("copy %1 is up to date").arg(copyPath)); return NULL; } } QFile file(originalPath); bool ok = FolderUtils::slamCopy(file, copyPath); if (!ok) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("unable to copy file '%1' to '%2'.").arg(originalPath).arg(copyPath)); return NULL; } MainWindow * mainWindow = app->openWindowForService(false, 3); FolderUtils::setOpenSaveFolderAux(fzDir.absolutePath()); if (!mainWindow->loadWhich(copyPath, false, false, false, "")) { writePanelizerOutput(QString("failed to load '%1'").arg(copyPath)); return mainWindow; } // performance optimization mainWindow->pcbView()->setGridSize("0.1in"); mainWindow->pcbView()->showGrid(false); QList items = mainWindow->pcbView()->selectAllObsolete(); if (items.count() > 0) { QFileInfo info(copyPath); writePanelizerOutput(QString("%2 ... %1 obsolete items").arg(items.count()).arg(info.fileName())); } int moved = mainWindow->pcbView()->checkLoadedTraces(); if (moved > 0) { QFileInfo info(originalPath); QString message = QObject::tr("%2 ... %1 wires moved from their saved position").arg(moved).arg(info.fileName()); if (!noMessages) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), message); } writePanelizerOutput(message); collectFilenames(info.fileName()); } foreach (QGraphicsItem * item, mainWindow->pcbView()->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemBase->setMoveLock(false); } QString fritzingVersion = mainWindow->fritzingVersion(); VersionThing versionThing; versionThing.majorVersion = 0; versionThing.minorVersion = 7; versionThing.minorSubVersion = 0; versionThing.releaseModifier = ""; VersionThing versionThingFz; Version::toVersionThing(fritzingVersion, versionThingFz); bool oldGround = !Version::greaterThan(versionThing, versionThingFz); bool filled = false; QList boards = mainWindow->pcbView()->findBoard(); bool wasOne = false; foreach (ItemBase * boardItem, boards) { mainWindow->pcbView()->selectAllItems(false, false); boardItem->setSelected(true); if (boardItem->prop("layers").compare("1") == 0) { mainWindow->swapLayers(boardItem, 2, "", 0); ProcessEventBlocker::processEvents(); wasOne = true; } LayerList groundLayers; groundLayers << ViewLayer::GroundPlane0 << ViewLayer::GroundPlane1; foreach (ViewLayer::ViewLayerID viewLayerID, groundLayers) { QString fillType = mainWindow->pcbView()->characterizeGroundFill(viewLayerID); if (fillType == GroundPlane::fillTypeNone) { mainWindow->copperFill(viewLayerID); ProcessEventBlocker::processEvents(); filled = true; } else if ((fillType == GroundPlane::fillTypeGround) && oldGround) { mainWindow->groundFill(viewLayerID); ProcessEventBlocker::processEvents(); filled = true; } } } if (filled) { mainWindow->saveAsAux(copyPath); DebugDialog::debug(QString("%1 filled:%2").arg(copyPath).arg(filled)); } checkDonuts(mainWindow, !noMessages); checkText(mainWindow, !noMessages); if (drc) { foreach (ItemBase * boardItem, boards) { mainWindow->pcbView()->resetKeepout(); mainWindow->pcbView()->selectAllItems(false, false); boardItem->setSelected(true); QStringList messages = mainWindow->newDesignRulesCheck(false); if (messages.count() > 0) { QFileInfo info(mainWindow->fileName()); writePanelizerOutput(QString("%2 ... %1 drc complaints").arg(messages.count()).arg(info.fileName())); collectFilenames(info.fileName()); //foreach (QString message, messages) { // writePanelizerOutput("\t" + message); //} } } } return mainWindow; } void Panelizer::makeSVGs(MainWindow * mainWindow, ItemBase * board, const QString & boardName, QList & layerThingList, QDir & saveDir, QFileInfo & copyInfo) { try { QString maskTop; QString maskBottom; QStringList texts; QMultiHash treatAsCircle; bool needsRedo = false; int missing = 0; foreach (LayerThing layerThing, layerThingList) { QString name = layerThing.name; QString filename = saveDir.absoluteFilePath(QString("%1_%2_%3.svg").arg(boardName).arg(board->id()).arg(name)); QFileInfo info(filename); if (info.exists()) { if (info.lastModified() <= copyInfo.lastModified()) { // need to save these files again needsRedo = true; break; } } else { missing++; } } if (!needsRedo) { if (missing < layerThingList.count()) { // assume some number of missing files wouldn't have been written out anyway // but if all are missing, then they were never written in the first place return; } } foreach (LayerThing layerThing, layerThingList) { QString name = layerThing.name; QString filename = saveDir.absoluteFilePath(QString("%1_%2_%3.svg").arg(boardName).arg(board->id()).arg(name)); SVG2gerber::ForWhy forWhy = layerThing.forWhy; QList copperLogoItems, holes; switch (forWhy) { case SVG2gerber::ForPasteMask: mainWindow->pcbView()->hideHoles(holes); case SVG2gerber::ForMask: mainWindow->pcbView()->hideCopperLogoItems(copperLogoItems); default: break; } RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString one = mainWindow->pcbView()->renderToSVG(renderThing, board, layerThing.layerList); QString clipString; bool wantText = false; switch (forWhy) { case SVG2gerber::ForOutline: one = GerberGenerator::cleanOutline(one); break; case SVG2gerber::ForPasteMask: mainWindow->pcbView()->restoreCopperLogoItems(copperLogoItems); mainWindow->pcbView()->restoreCopperLogoItems(holes); one = mainWindow->pcbView()->makePasteMask(one, board, GraphicsUtils::StandardFritzingDPI, layerThing.layerList); if (one.isEmpty()) continue; forWhy = SVG2gerber::ForCopper; break; case SVG2gerber::ForMask: mainWindow->pcbView()->restoreCopperLogoItems(copperLogoItems); one = TextUtils::expandAndFill(one, "black", GerberGenerator::MaskClearanceMils * 2); forWhy = SVG2gerber::ForCopper; if (name.contains("bottom")) { maskBottom = one; } else { maskTop = one; } break; case SVG2gerber::ForSilk: wantText = true; if (name.contains("bottom")) { clipString = maskBottom; } else { clipString = maskTop; } break; case SVG2gerber::ForCopper: case SVG2gerber::ForDrill: treatAsCircle.clear(); foreach (QGraphicsItem * item, mainWindow->pcbView()->scene()->collidingItems(board)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!connectorItem->isPath()) continue; if (connectorItem->radius() == 0) continue; treatAsCircle.insert(connectorItem->attachedToID(), connectorItem); } wantText = true; break; default: wantText = true; break; } if (wantText) { collectTexts(one, texts); //DebugDialog::debug("one " + one); } QString two = GerberGenerator::clipToBoard(one, board, name, forWhy, clipString, true, treatAsCircle); treatAsCircle.clear(); if (two.isEmpty()) continue; TextUtils::writeUtf8(filename, two); } if (texts.count() > 0) { QString filename = saveDir.absoluteFilePath(QString("%1_%2_%3.txt").arg(boardName).arg(board->id()).arg("texts")); TextUtils::writeUtf8(filename, texts.join("\n")); } } catch (const char * msg) { DebugDialog::debug(QString("panelizer error 1 %1 %2").arg(boardName).arg(msg)); } catch (const QString & msg) { DebugDialog::debug(QString("panelizer error 2 %1 %2").arg(boardName).arg(msg)); } catch (...) { DebugDialog::debug(QString("panelizer error 3 %1").arg(boardName)); } } void Panelizer::doOnePanelItem(PlanePair * planePair, QList & layerThingList, PanelItem * panelItem, QDir & svgDir) { try { for (int i = 0; i < planePair->svgs.count(); i++) { QString name = layerThingList.at(i).name; QString rot = panelItem->rotate90 ? "rotate" : "norotate"; QString filename = svgDir.absoluteFilePath(QString("%1/%2_%3_%4.svg").arg(rot).arg(panelItem->boardName).arg(panelItem->boardID).arg(name)); QFile file(filename); if (file.open(QFile::ReadOnly)) { QString one = file.readAll(); if (one.isEmpty()) continue; int left = one.indexOf("", left + 1); int right = one.lastIndexOf("<"); one = QString("\n").arg(panelItem->x * GraphicsUtils::StandardFritzingDPI).arg(panelItem->y * GraphicsUtils::StandardFritzingDPI) + one.mid(left + 1, right - left - 1) + "\n"; planePair->svgs.replace(i, planePair->svgs.at(i) + one); } else { DebugDialog::debug(QString("panelizer error? 1 %1 %2").arg(filename).arg("one text not found")); } } } catch (const char * msg) { DebugDialog::debug(QString("panelizer error 1 %1 %2").arg(panelItem->boardName).arg(msg)); } catch (const QString & msg) { DebugDialog::debug(QString("panelizer error 2 %1 %2").arg(panelItem->boardName).arg(msg)); } catch (...) { DebugDialog::debug(QString("panelizer error 3 %1").arg(panelItem->boardName)); } } void Panelizer::shrinkLastPanel( QList & planePairs, QList & insertPanelItems, PanelParams & panelParams, bool customPartsOnly) { PlanePair * lastPlanePair = planePairs.last(); PlanePair * smallPlanePair = makePlanePair(panelParams, false); smallPlanePair->index = lastPlanePair->index; QList smallPlanePairs; smallPlanePairs << smallPlanePair; bool canFitSmaller = true; QList smallPanelItems; foreach (PanelItem * panelItem, insertPanelItems) { if (panelItem->planePair != lastPlanePair) continue; PanelItem * copy = new PanelItem(panelItem); smallPanelItems << copy; if (!bestFitOne(copy, panelParams, smallPlanePairs, false, customPartsOnly)) { canFitSmaller = false; } } if (canFitSmaller) { foreach (PanelItem * panelItem, insertPanelItems) { if (panelItem->planePair != lastPlanePair) continue; insertPanelItems.removeOne(panelItem); delete panelItem; } planePairs.removeOne(lastPlanePair); delete lastPlanePair; insertPanelItems.append(smallPanelItems); planePairs.append(smallPlanePair); } else { foreach (PanelItem * copy, smallPanelItems) { delete copy; } delete smallPlanePair; } } int Panelizer::checkText(MainWindow * mainWindow, bool displayMessage) { QHash svgHash; QList missing; foreach (QGraphicsItem * item, mainWindow->pcbView()->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isEverVisible()) continue; double factor; QString itemSvg = itemBase->retrieveSvg(itemBase->viewLayerID(), svgHash, false, GraphicsUtils::StandardFritzingDPI, factor); if (itemSvg.isEmpty()) continue; QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (!doc.setContent(itemSvg, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("itembase svg failure %1").arg(itemBase->id())); continue; } QDomElement root = doc.documentElement(); QDomNodeList domNodeList = root.elementsByTagName("path"); for (int i = 0; i < domNodeList.count(); i++) { QDomElement textElement = domNodeList.at(i).toElement(); if (textElement.attribute("fill").isEmpty() && textElement.attribute("stroke").isEmpty() && textElement.attribute("stroke-width").isEmpty()) { missing.append(itemBase); break; } } } if (displayMessage && missing.count() > 0) { mainWindow->pcbView()->selectAllItems(false, false); mainWindow->pcbView()->selectItems(missing); QMessageBox::warning(NULL, "Text", QString("There are %1 possible instances of parts with elements missing stroke/fill/stroke-width attributes").arg(missing.count())); } if (missing.count() > 0) { QFileInfo info(mainWindow->fileName()); writePanelizerOutput(QString("%2 ... There are %1 possible instances of parts with elements missing stroke/fill/stroke-width attributes") .arg(missing.count()).arg(info.fileName()) ); collectFilenames(info.fileName()); } return missing.count(); } int Panelizer::checkDonuts(MainWindow * mainWindow, bool displayMessage) { QList donuts; foreach (QGraphicsItem * item, mainWindow->pcbView()->scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!connectorItem->attachedTo()->isEverVisible()) continue; if (connectorItem->isPath() && connectorItem->getCrossLayerConnectorItem() != NULL) { // && connectorItem->radius() == 0 connectorItem->debugInfo("possible donut"); connectorItem->attachedTo()->debugInfo("\t"); donuts << connectorItem; } } if (displayMessage && donuts.count() > 0) { mainWindow->pcbView()->selectAllItems(false, false); QSet itemBases; foreach (ConnectorItem * connectorItem, donuts) { itemBases.insert(connectorItem->attachedTo()); } mainWindow->pcbView()->selectItems(itemBases.toList()); QMessageBox::warning(NULL, "Donuts", QString("There are %1 possible donut connectors").arg(donuts.count() / 2)); } if (donuts.count() > 0) { QFileInfo info(mainWindow->fileName()); writePanelizerOutput(QString("%2 ... %1 possible donuts").arg(donuts.count() / 2).arg(info.fileName())); collectFilenames(mainWindow->fileName()); } return donuts.count() / 2; } int Panelizer::bestFitLoop(QList & refPanelItems, PanelParams & panelParams, bool customPartsOnly, QList & returnPlanePairs, QList & returnInsertPanelItems, const QDir & svgDir) { int optionalCount = 0; foreach (PanelItem * panelItem, refPanelItems) { optionalCount += panelItem->maxOptional; } QDir intermediates(svgDir); intermediates.mkdir("intermediates"); intermediates.cd("intermediates"); double bestCost = 0; double bestDivisor = 1; bool firstTime = true; for (int divisor = 1; true; divisor++) { QList planePairs; QList insertPanelItems; PlanePairIndex = 0; // reset to zero for each new list of PlanePairs bool stillMoreThanOne = false; foreach (PanelItem * panelItem, refPanelItems) { int count = (panelItem->required + divisor - 1) / divisor; if (count > 1) stillMoreThanOne = true; for (int i = 0; i < count; i++) { PanelItem * copy = new PanelItem(panelItem); insertPanelItems.append(copy); } } planePairs << makePlanePair(panelParams, true); qSort(insertPanelItems.begin(), insertPanelItems.end(), areaGreaterThan); bestFit(insertPanelItems, panelParams, planePairs, customPartsOnly); shrinkLastPanel(planePairs, insertPanelItems, panelParams, customPartsOnly); double cost = calcCost(panelParams, planePairs, divisor); foreach (PlanePair * planePair, planePairs) { planePair->layoutSVG += ""; QString fname = intermediates.absoluteFilePath(QString("%3.divisor_%4.cost_%1.panel_%2.layout_.svg") .arg(panelParams.prefix).arg(planePair->index).arg(divisor).arg(cost) ); TextUtils::writeUtf8(fname, planePair->layoutSVG); // make sure to leave layoutSVG without a trailing since optionals are added later planePair->layoutSVG.chop(6); } DebugDialog::debug(""); DebugDialog::debug(QString("%1 panels, %2 additional copies of each: cost %3").arg(planePairs.count()).arg(divisor - 1).arg(cost)); DebugDialog::debug(""); if (firstTime) { bestCost = cost; bestDivisor = 1; returnPlanePairs = planePairs; returnInsertPanelItems = insertPanelItems; firstTime = false; } else { if (cost < bestCost) { bestCost = cost; bestDivisor = divisor; foreach (PlanePair * planePair, returnPlanePairs) { delete planePair; } foreach (PanelItem * panelItem, returnInsertPanelItems) { delete panelItem; } returnPlanePairs.clear(); returnInsertPanelItems.clear(); returnPlanePairs = planePairs; returnInsertPanelItems = insertPanelItems; } } if (customPartsOnly) break; if (planePairs.count() == 1) break; if (!stillMoreThanOne) break; } addOptional(optionalCount, refPanelItems, returnInsertPanelItems, panelParams, returnPlanePairs); return bestDivisor; } double Panelizer::calcCost(PanelParams & panelParams, QList & planePairs, int divisor) { double total = 0; foreach (PlanePair * planePair, planePairs) { foreach (PanelType * panelType, panelParams.panelTypes) { if (planePair->panelWidth == panelType->width && planePair->panelHeight == panelType->height) { total += panelType->c1; total += panelType->c2 * (divisor - 1); break; } } } return total; } void Panelizer::writePanelizerOutput(const QString & message) { DebugDialog::debug(message); if (PanelizerOutputPath.length() > 0) { QFile file(PanelizerOutputPath); if (file.open(QFile::Append)) { QTextStream out(&file); out.setCodec("UTF-8"); out << message << "\n"; file.close(); } } } void Panelizer::initPanelizerOutput(const QString & panelFilename, const QString & msg) { PanelizerFileNames.clear(); QFileInfo info(panelFilename); PanelizerOutputPath = info.absoluteDir().absoluteFilePath("panelizer_output.txt"); QDateTime dt = QDateTime::currentDateTime(); writePanelizerOutput(QString("\n--------- %1 --- %2 ---") .arg(msg).arg(dt.toString()) ); } void Panelizer::collectFilenames(const QString & filename) { if (filename.endsWith(".fz")) { PanelizerFileNames.insert(filename + "z"); } else PanelizerFileNames.insert(filename); } void Panelizer::writePanelizerFilenames(const QString & panelFilename) { if (PanelizerFileNames.count() == 0) return; QFileInfo info(panelFilename); QDateTime dt = QDateTime::currentDateTime(); QString path = info.absoluteDir().absoluteFilePath("panelizer_files_%1.txt").arg(dt.toString().replace(":", ".")); QFile file(path); if (file.open(QFile::WriteOnly)) { QTextStream out(&file); out.setCodec("UTF-8"); foreach (QString name, PanelizerFileNames) { out << name << "\n"; } file.close(); } } fritzing-app-0.9.3b/src/autoroute/panelizer.h000066400000000000000000000117171272363601300212710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6968 $: $Author: irascibl@gmail.com $: $Date: 2013-04-15 17:44:14 +0200 (Mo, 15. Apr 2013) $ ********************************************************************/ #ifndef PANELIZER_H #define PANELIZER_H #include #include #include "../mainwindow/mainwindow.h" #include "../items/itembase.h" #include "cmrouter/tile.h" struct PlanePair { Plane * thePlane; Plane * thePlane90; TileRect tilePanelRect; TileRect tilePanelRect90; double panelWidth; double panelHeight; QStringList svgs; QString layoutSVG; int index; }; struct PanelItem { // per window QString boardName; QString path; int required; int maxOptional; int optionalPriority; int produced; QSizeF boardSizeInches; long boardID; PanelItem * refPanelItem; // per instance double x, y; bool rotate90; PlanePair * planePair; PanelItem(); PanelItem(PanelItem * from); }; struct BestPlace { Tile * bestTile; TileRect bestTileRect; TileRect maxRect; int width; int height; double bestArea; bool rotate90; Plane* plane; BestPlace(); }; struct PanelType { double width; double height; double c1; double c2; QString name; }; struct PanelParams { QList panelTypes; double panelSpacing; double panelBorder; QString prefix; }; struct LayerThing { LayerList layerList; QString name; SVG2gerber::ForWhy forWhy; QString suffix; LayerThing(const QString & n, LayerList ll, SVG2gerber::ForWhy fw, const QString & s) { layerList = ll; name = n; forWhy = fw; suffix = s; } }; class Panelizer { public: static void panelize(class FApplication *, const QString & panelFilename, bool customPartsOnly); static void inscribe(class FApplication *, const QString & panelFilename, bool drc, bool noMessages); static int placeBestFit(Tile * tile, UserData userData); static int checkDonuts(MainWindow *, bool displayMessage); static int checkText(MainWindow *, bool displayMessage); protected: static bool initPanelParams(QDomElement & root, PanelParams &); static PlanePair * makePlanePair(PanelParams &, bool big); static void collectFiles(const QDir & outputDir, QDomElement & path, QHash & fzzFilePaths); static bool checkBoards(QDomElement & board, QHash & fzzFilePaths); static bool openWindows(QDomElement & board, QHash & fzzFilePaths, class FApplication *, PanelParams &, QDir & fzDir, QDir & svgDir, QList & refPanelItems, QList & layerThingList, bool customPartsOnly, QDir & copyDir); static void bestFit(QList & insertPanelItems, PanelParams &, QList &, bool customPartsOnly); static bool bestFitOne(PanelItem * panelItem, PanelParams & panelParams, QList & planePairs, bool createNew, bool customPartsOnly); static void addOptional(int optionalCount, QList & refPanelItems, QList & insertPanelItems, PanelParams &, QList &); static class MainWindow * inscribeBoard(QDomElement & board, QHash & fzzFilePaths, FApplication * app, QDir & fzDir, bool drc, bool noMessages, QDir & copyDir); static void doOnePanelItem(PlanePair * planePair, QList & layerThingList, PanelItem * panelItem, QDir & svgDir); static void makeSVGs(MainWindow *, ItemBase *, const QString & boardName, QList & layerThingList, QDir & saveDir, QFileInfo & copyInfo); static void shrinkLastPanel( QList & planePairs, QList & insertPanelItems, PanelParams &, bool customPartsOnly); static int bestFitLoop(QList & refPanelItems, PanelParams &, bool customPartsOnly, QList & returnPlanePairs, QList & returnInsertPanelItems, const QDir & svgDir); static double calcCost(PanelParams &, QList &, int divisor); static void writePanelizerOutput(const QString & message); static void initPanelizerOutput(const QString & filename, const QString & initialMsg); static void collectFilenames(const QString & filenames); static void writePanelizerFilenames(const QString & panelFilename); }; #endif fritzing-app-0.9.3b/src/autoroute/zoomcontrols.cpp000066400000000000000000000066411272363601300224030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "zoomcontrols.h" #include "../debugdialog.h" #include "../utils/zoomslider.h" ZoomButton::ZoomButton(QBoxLayout::Direction dir, ZoomButton::ZoomType type, ZoomableGraphicsView* view, QWidget *parent) : QLabel(parent) { QString imgPath = ":/resources/images/icons/partsEditorZoom%1%2Button.png"; QString typeStr = type==ZoomButton::ZoomIn? "In": "Out"; QString dirStr; if(dir == QBoxLayout::LeftToRight || dir == QBoxLayout::RightToLeft) { dirStr = "Hor"; } else if(dir == QBoxLayout::TopToBottom || dir == QBoxLayout::BottomToTop) { dirStr = "Ver"; } imgPath = imgPath.arg(typeStr).arg(dirStr); m_step = 5*ZoomSlider::ZoomStep; m_type = type; m_owner = view; connect(this, SIGNAL(clicked()), this, SLOT(zoom()) ); setPixmap(QPixmap(imgPath)); } void ZoomButton::zoom() { int inOrOut = m_type == ZoomButton::ZoomIn? 1: -1; m_owner->relativeZoom(inOrOut*m_step, false); m_owner->ensureFixedToBottomRightItems(); } void ZoomButton::mousePressEvent(QMouseEvent *event) { //QLabel::mousePressEvent(event); Q_UNUSED(event); emit clicked(); } void ZoomButton::enterEvent(QEvent *event) { QLabel::enterEvent(event); } void ZoomButton::leaveEvent(QEvent *event) { QLabel::leaveEvent(event); } /////////////////////////////////////////////////////////// ZoomControlsPrivate::ZoomControlsPrivate(ZoomableGraphicsView* view, QBoxLayout::Direction dir, QWidget *parent) : QFrame(parent) { //setObjectName("zoomControls"); m_zoomInButton = new ZoomButton(dir, ZoomButton::ZoomIn, view, this); m_zoomOutButton = new ZoomButton(dir, ZoomButton::ZoomOut, view, this); m_boxLayout = new QBoxLayout(dir,this); m_boxLayout->addWidget(m_zoomInButton); m_boxLayout->addWidget(m_zoomOutButton); m_boxLayout->setMargin(2); m_boxLayout->setSpacing(2); setStyleSheet("background-color: transparent;"); } /////////////////////////////////////////////////////////// ZoomControls::ZoomControls(ZoomableGraphicsView *view, QWidget *parent) : ZoomControlsPrivate(view, QBoxLayout::RightToLeft, parent) { m_zoomLabel = new QLabel(this); m_zoomLabel->setFixedWidth(35); connect(view, SIGNAL(zoomChanged(double)),this,SLOT(updateLabel(double))); m_boxLayout->insertWidget(1,m_zoomLabel); // in the middle m_boxLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Minimum)); // at the beginning updateLabel(view->currentZoom()); } void ZoomControls::updateLabel(double zoom) { m_zoomLabel->setText(QString("%1%").arg((int)zoom)); } fritzing-app-0.9.3b/src/autoroute/zoomcontrols.h000066400000000000000000000042331272363601300220430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ZOOMCONTROLS_H_ #define ZOOMCONTROLS_H_ #include #include #include "../sketch/zoomablegraphicsview.h" class ZoomButton : public QLabel { Q_OBJECT public: enum ZoomType {ZoomIn, ZoomOut}; public: ZoomButton(QBoxLayout::Direction dir, ZoomButton::ZoomType type, ZoomableGraphicsView* view, QWidget *parent); signals: void clicked(); protected slots: void zoom(); protected: void enterEvent(QEvent *event); void leaveEvent(QEvent *event); void mousePressEvent(QMouseEvent *event); ZoomableGraphicsView *m_owner; double m_step; ZoomButton::ZoomType m_type; }; class ZoomControlsPrivate : public QFrame { public: ZoomControlsPrivate(ZoomableGraphicsView*, QBoxLayout::Direction = QBoxLayout::TopToBottom, QWidget *parent=0); ZoomButton *zoomInButton(); ZoomButton *zoomOutButton(); protected: ZoomButton *m_zoomInButton; ZoomButton *m_zoomOutButton; QBoxLayout *m_boxLayout; }; class ZoomControls : public ZoomControlsPrivate { Q_OBJECT public: ZoomControls(ZoomableGraphicsView *view, QWidget *parent); protected slots: void updateLabel(double zoom); protected: QLabel *m_zoomLabel; }; #endif /* ZOOMCONTROLS_H_ */ fritzing-app-0.9.3b/src/commands.cpp000066400000000000000000001745711272363601300174150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #include "commands.h" #include "debugdialog.h" #include "sketch/sketchwidget.h" #include "waitpushundostack.h" #include "items/wire.h" #include "connectors/connectoritem.h" #include "items/moduleidnames.h" #include "utils/bezier.h" ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// CommandProgress::CommandProgress() { m_active = false; } void CommandProgress::setActive(bool active) { m_active = active; } bool CommandProgress::active() { return m_active; } void CommandProgress::emitUndo() { emit incUndo(); } void CommandProgress::emitRedo() { emit incRedo(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// int SelectItemCommand::selectItemCommandID = 3; int ChangeNoteTextCommand::changeNoteTextCommandID = 5; int BaseCommand::nextIndex = 0; CommandProgress BaseCommand::m_commandProgress; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// BaseCommand::BaseCommand(BaseCommand::CrossViewType crossViewType, SketchWidget* sketchWidget, QUndoCommand *parent) : QUndoCommand(parent) { m_skipFirstRedo = m_undoOnly = m_redoOnly = false; m_crossViewType = crossViewType; m_sketchWidget = sketchWidget; m_parentCommand = parent; m_index = BaseCommand::nextIndex++; } BaseCommand::~BaseCommand() { foreach (BaseCommand * baseCommand, m_commands) { delete baseCommand; } m_commands.clear(); } BaseCommand::CrossViewType BaseCommand::crossViewType() const { return m_crossViewType; } void BaseCommand::setCrossViewType(BaseCommand::CrossViewType crossViewType) { m_crossViewType = crossViewType; } SketchWidget* BaseCommand::sketchWidget() const { return m_sketchWidget; } QString BaseCommand::getDebugString() const { return QString("%1 %2").arg(getParamString()).arg(text()); } void BaseCommand::setUndoOnly() { m_undoOnly = true; } void BaseCommand::setRedoOnly() { m_redoOnly = true; } void BaseCommand::setSkipFirstRedo() { m_skipFirstRedo = true; } QString BaseCommand::getParamString() const { return QString("%1 %2") .arg(m_sketchWidget->viewName()) .arg((m_crossViewType == BaseCommand::SingleView) ? "single-view" : "cross-view"); } int BaseCommand::subCommandCount() const { return m_commands.count(); } const BaseCommand * BaseCommand::subCommand(int ix) const { if (ix < 0) return NULL; if (ix >= m_commands.count()) return NULL; return m_commands.at(ix); } void BaseCommand::addSubCommand(BaseCommand * subCommand) { m_commands.append(subCommand); #ifndef QT_NO_DEBUG if (m_sketchWidget != NULL) { m_sketchWidget->undoStack()->writeUndo(subCommand, 4, this); } #endif } const QUndoCommand * BaseCommand::parentCommand() const { return m_parentCommand; } void BaseCommand::subUndo() { for (int i = m_commands.count() - 1; i >= 0; i--) { m_commands[i]->undo(); } } void BaseCommand::subRedo() { foreach (BaseCommand * command, m_commands) { command->redo(); } } void BaseCommand::subUndo(int index) { if (index < 0 || index >= m_commands.count()) return; m_commands[index]->undo(); } void BaseCommand::subRedo(int index) { if (index < 0 || index >= m_commands.count()) return; m_commands[index]->redo(); } int BaseCommand::index() const { return m_index; } void BaseCommand::undo() { if (m_commandProgress.active()) m_commandProgress.emitUndo(); } void BaseCommand::redo() { if (m_commandProgress.active()) m_commandProgress.emitRedo(); } CommandProgress * BaseCommand::initProgress() { m_commandProgress.setActive(true); return &m_commandProgress; } void BaseCommand::clearProgress() { m_commandProgress.setActive(false); } int BaseCommand::totalChildCount(const QUndoCommand * command) { int cc = command->childCount(); int tcc = cc; for (int i = 0; i < cc; i++) { tcc += totalChildCount(command->child(i)); } return tcc; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// AddDeleteItemCommand::AddDeleteItemCommand(SketchWidget* sketchWidget, BaseCommand::CrossViewType crossViewType, QString moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, qint64 id, long modelIndex, QUndoCommand *parent) : BaseCommand(crossViewType, sketchWidget, parent) { m_dropOrigin = NULL; m_moduleID = moduleID; m_viewGeometry = viewGeometry; m_itemID = id; m_modelIndex = modelIndex; m_viewLayerPlacement = viewLayerPlacement; } QString AddDeleteItemCommand::getParamString() const { return BaseCommand::getParamString() + QString(" moduleid:%1 id:%2 modelindex:%3 flags:%4") .arg(m_moduleID) .arg(m_itemID) .arg(m_modelIndex) .arg(m_viewGeometry.flagsAsInt()); } long AddDeleteItemCommand::itemID() const { return m_itemID; } void AddDeleteItemCommand::setDropOrigin(SketchWidget * sketchWidget) { m_dropOrigin = sketchWidget; } SketchWidget * AddDeleteItemCommand::dropOrigin() { return m_dropOrigin; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// AddItemCommand::AddItemCommand(SketchWidget* sketchWidget, BaseCommand::CrossViewType crossViewType, QString moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, qint64 id, bool updateInfoView, long modelIndex, QUndoCommand *parent) : AddDeleteItemCommand(sketchWidget, crossViewType, moduleID, viewLayerPlacement, viewGeometry, id, modelIndex, parent) { m_module = false; m_updateInfoView = updateInfoView; } void AddItemCommand::undo() { m_sketchWidget->deleteItem(m_itemID, true, true, false); BaseCommand::undo(); } void AddItemCommand::redo() { if (!m_skipFirstRedo) { m_sketchWidget->addItem(m_moduleID, m_viewLayerPlacement, m_crossViewType, m_viewGeometry, m_itemID, m_modelIndex, this); } m_skipFirstRedo = false; BaseCommand::redo(); } QString AddItemCommand::getParamString() const { return "AddItemCommand " + AddDeleteItemCommand::getParamString() + QString(" loc:%1,%2 pt1:%3,%4 pt2:%5,%6") .arg(m_viewGeometry.loc().x()).arg(m_viewGeometry.loc().y()) .arg(m_viewGeometry.line().p1().x()).arg(m_viewGeometry.line().p1().y()) .arg(m_viewGeometry.line().p2().x()).arg(m_viewGeometry.line().p2().y()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// DeleteItemCommand::DeleteItemCommand(SketchWidget* sketchWidget,BaseCommand::CrossViewType crossViewType, QString moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, qint64 id, long modelIndex, QUndoCommand *parent) : AddDeleteItemCommand(sketchWidget, crossViewType, moduleID, viewLayerPlacement, viewGeometry, id, modelIndex, parent) { } void DeleteItemCommand::undo() { m_sketchWidget->addItem(m_moduleID, m_viewLayerPlacement, m_crossViewType, m_viewGeometry, m_itemID, m_modelIndex, this); BaseCommand::undo(); } void DeleteItemCommand::redo() { m_sketchWidget->deleteItem(m_itemID, true, m_crossViewType == BaseCommand::CrossView, false); BaseCommand::redo(); } QString DeleteItemCommand::getParamString() const { return "DeleteItemCommand " + AddDeleteItemCommand::getParamString(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// MoveItemCommand::MoveItemCommand(SketchWidget* sketchWidget, long itemID, ViewGeometry & oldG, ViewGeometry & newG, bool updateRatsnest, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_updateRatsnest = updateRatsnest; m_itemID = itemID; m_old = oldG; m_new = newG; } void MoveItemCommand::undo() { m_sketchWidget->moveItem(m_itemID, m_old, m_updateRatsnest); BaseCommand::undo(); } void MoveItemCommand::redo() { m_sketchWidget->moveItem(m_itemID, m_new, m_updateRatsnest); BaseCommand::redo(); } QString MoveItemCommand::getParamString() const { return QString("MoveItemCommand ") + BaseCommand::getParamString() + QString(" id:%1 old.x:%2 old.y:%3 old.px:%4 old.py:%5 new.x:%6 new.y:%7 new.px:%8 new.py:%9") .arg(m_itemID) .arg(m_old.loc().x()) .arg(m_old.loc().y()) .arg(m_old.line().p2().x()) .arg(m_old.line().p2().y()) .arg(m_new.loc().x()) .arg(m_new.loc().y()) .arg(m_new.line().p2().x()) .arg(m_new.line().p2().y()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// SimpleMoveItemCommand::SimpleMoveItemCommand(SketchWidget* sketchWidget, long itemID, QPointF & oldP, QPointF & newP, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_old = oldP; m_new = newP; } void SimpleMoveItemCommand::undo() { m_sketchWidget->simpleMoveItem(m_itemID, m_old); BaseCommand::undo(); } void SimpleMoveItemCommand::redo() { m_sketchWidget->simpleMoveItem(m_itemID, m_new); BaseCommand::redo(); } QString SimpleMoveItemCommand::getParamString() const { return QString("SimpleMoveItemCommand ") + BaseCommand::getParamString() + QString(" id:%1 old.x:%2 old.y:%3 new.x:%4 new.y:%5") .arg(m_itemID) .arg(m_old.x()) .arg(m_old.y()) .arg(m_new.x()) .arg(m_new.y()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// MoveItemsCommand::MoveItemsCommand(SketchWidget* sketchWidget, bool updateRatsnest, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_updateRatsnest = updateRatsnest; } void MoveItemsCommand::undo() { foreach(MoveItemThing moveItemThing, m_items) { m_sketchWidget->moveItem(moveItemThing.id, moveItemThing.oldPos, m_updateRatsnest); } foreach (long id, m_wires.keys()) { m_sketchWidget->updateWire(id, m_wires.value(id), m_updateRatsnest); } BaseCommand::undo(); } void MoveItemsCommand::redo() { foreach(MoveItemThing moveItemThing, m_items) { m_sketchWidget->moveItem(moveItemThing.id, moveItemThing.newPos, m_updateRatsnest); } foreach (long id, m_wires.keys()) { m_sketchWidget->updateWire(id, m_wires.value(id), m_updateRatsnest); } BaseCommand::redo(); } void MoveItemsCommand::addWire(long id, const QString & connectorID) { m_wires.insert(id, connectorID); } void MoveItemsCommand::addItem(long id, const QPointF & oldPos, const QPointF & newPos) { MoveItemThing moveItemThing; moveItemThing.id = id; moveItemThing.oldPos = oldPos; moveItemThing.newPos = newPos; m_items.append(moveItemThing); } QString MoveItemsCommand::getParamString() const { return QString("MoveItemsCommand ") + BaseCommand::getParamString() + QString(" items:%1 wires:%2") .arg(m_items.count()) .arg(m_wires.count()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// RotateItemCommand::RotateItemCommand(SketchWidget* sketchWidget, long itemID, double degrees, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_degrees = degrees; } void RotateItemCommand::undo() { m_sketchWidget->rotateItem(m_itemID, -m_degrees); BaseCommand::undo(); } void RotateItemCommand::redo() { m_sketchWidget->rotateItem(m_itemID, m_degrees); BaseCommand::redo(); } QString RotateItemCommand::getParamString() const { return QString("RotateItemCommand ") + BaseCommand::getParamString() + QString(" id:%1 by:%2") .arg(m_itemID) .arg(m_degrees); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// FlipItemCommand::FlipItemCommand(SketchWidget* sketchWidget, long itemID, Qt::Orientations orientation, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_orientation = orientation; } void FlipItemCommand::undo() { redo(); BaseCommand::undo(); } void FlipItemCommand::redo() { m_sketchWidget->flipItem(m_itemID, m_orientation); BaseCommand::redo(); } QString FlipItemCommand::getParamString() const { return QString("FlipItemCommand ") + BaseCommand::getParamString() + QString(" id:%1 by:%2") .arg(m_itemID) .arg(m_orientation); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeConnectionCommand::ChangeConnectionCommand(SketchWidget * sketchWidget, BaseCommand::CrossViewType crossView, long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, QUndoCommand * parent) : BaseCommand(crossView, sketchWidget, parent) { //DebugDialog::debug(QString("ccc: from %1 %2; to %3 %4").arg(fromID).arg(fromConnectorID).arg(toID).arg(toConnectorID) ); m_enabled = true; m_fromID = fromID; m_fromConnectorID = fromConnectorID; m_toID = toID; m_toConnectorID = toConnectorID; m_connect = connect; m_updateConnections = true; m_viewLayerPlacement = viewLayerPlacement; } void ChangeConnectionCommand::undo() { if (m_enabled) { m_sketchWidget->changeConnection(m_fromID, m_fromConnectorID, m_toID, m_toConnectorID, m_viewLayerPlacement, !m_connect, m_crossViewType == CrossView, m_updateConnections); BaseCommand::undo(); } } void ChangeConnectionCommand::redo() { if (m_enabled) { m_sketchWidget->changeConnection(m_fromID, m_fromConnectorID, m_toID, m_toConnectorID, m_viewLayerPlacement, m_connect, m_crossViewType == CrossView, m_updateConnections); BaseCommand::redo(); } } void ChangeConnectionCommand::setUpdateConnections(bool updatem) { m_updateConnections = updatem; } void ChangeConnectionCommand::disable() { m_enabled = false; } QString ChangeConnectionCommand::getParamString() const { return QString("ChangeConnectionCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 connid:%2 toid:%3 connid:%4 vlspec:%5 connect:%6") .arg(m_fromID) .arg(m_fromConnectorID) .arg(m_toID) .arg(m_toConnectorID) .arg(m_viewLayerPlacement) .arg(m_connect); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeWireCommand::ChangeWireCommand(SketchWidget* sketchWidget, long fromID, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, bool updateConnections, bool updateRatsnest, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_updateRatsnest = updateRatsnest; m_fromID = fromID; m_oldLine = oldLine; m_newLine = newLine; m_oldPos = oldPos; m_newPos = newPos; m_updateConnections = updateConnections; } void ChangeWireCommand::undo() { if (!m_redoOnly) { m_sketchWidget->changeWire(m_fromID, m_oldLine, m_oldPos, m_updateConnections, m_updateRatsnest); } BaseCommand::undo(); } void ChangeWireCommand::redo() { if (!m_undoOnly) { m_sketchWidget->changeWire(m_fromID, m_newLine, m_newPos, m_updateConnections, m_updateRatsnest); } BaseCommand::redo(); } QString ChangeWireCommand::getParamString() const { return QString("ChangeWireCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 oldp:%2,%3 newP:%4,%5 oldr:%7,%8,%9,%10 newr:%11,%12,%13,%14") .arg(m_fromID) .arg(m_oldPos.x()).arg(m_oldPos.y()) .arg(m_newPos.x()).arg(m_newPos.y()) .arg(m_oldLine.x1()).arg(m_oldLine.y1()).arg(m_oldLine.x2()).arg(m_oldLine.y2()) .arg(m_newLine.x1()).arg(m_newLine.y1()).arg(m_newLine.x2()).arg(m_newLine.y2()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeWireCurveCommand::ChangeWireCurveCommand(SketchWidget* sketchWidget, long fromID, const Bezier * oldBezier, const Bezier * newBezier, bool wasAutoroutable, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_wasAutoroutable = wasAutoroutable; m_oldBezier = m_newBezier = NULL; if (oldBezier) { m_oldBezier = new Bezier; m_oldBezier->copy(oldBezier); } if (newBezier) { m_newBezier = new Bezier; m_newBezier->copy(newBezier); } } void ChangeWireCurveCommand::undo() { if (!m_redoOnly) { m_sketchWidget->changeWireCurve(m_fromID, m_oldBezier, m_wasAutoroutable); } BaseCommand::undo(); } void ChangeWireCurveCommand::redo() { if (!m_undoOnly) { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else { m_sketchWidget->changeWireCurve(m_fromID, m_newBezier, false); } } BaseCommand::redo(); } QString ChangeWireCurveCommand::getParamString() const { QString oldBezier; QString newBezier; if (m_oldBezier) { oldBezier += QString("(%1,%2)").arg(m_oldBezier->cp0().x()).arg(m_oldBezier->cp0().y()); oldBezier += QString("(%1,%2)").arg(m_oldBezier->cp1().x()).arg(m_oldBezier->cp1().y()); } if (m_newBezier) { newBezier += QString("(%1,%2)").arg(m_newBezier->cp0().x()).arg(m_newBezier->cp0().y()); newBezier += QString("(%1,%2)").arg(m_newBezier->cp1().x()).arg(m_newBezier->cp1().y()); } return QString("ChangeWireCurveCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 oldp:%2 newp:%3") .arg(m_fromID) .arg(oldBezier) .arg(newBezier) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeLegCommand::ChangeLegCommand(SketchWidget* sketchWidget, long fromID, const QString & fromConnectorID, const QPolygonF & oldLeg, const QPolygonF & newLeg, bool relative, bool active, const QString & why, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_why = why; m_simple = false; m_fromID = fromID; m_oldLeg = oldLeg; m_newLeg = newLeg; m_relative = relative; m_fromConnectorID = fromConnectorID; m_active = active; } void ChangeLegCommand::undo() { if (!m_redoOnly) { m_sketchWidget->changeLeg(m_fromID, m_fromConnectorID, m_oldLeg, m_relative, m_why); } BaseCommand::undo(); } void ChangeLegCommand::setSimple() { m_simple = true; } void ChangeLegCommand::redo() { if (!m_undoOnly) { if (m_simple) { m_sketchWidget->changeLeg(m_fromID, m_fromConnectorID, m_newLeg, m_relative, m_why); } else { m_sketchWidget->recalcLeg(m_fromID, m_fromConnectorID, m_newLeg, m_relative, m_active, m_why); } } BaseCommand::redo(); } QString ChangeLegCommand::getParamString() const { QString oldLeg; QString newLeg; foreach (QPointF p, m_oldLeg) { oldLeg += QString("(%1,%2)").arg(p.x()).arg(p.y()); } foreach (QPointF p, m_newLeg) { newLeg += QString("(%1,%2)").arg(p.x()).arg(p.y()); } return QString("ChangeLegCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 fromc:%2 %3 old:%4 new:%5") .arg(m_fromID) .arg(m_fromConnectorID) .arg(m_why) .arg(oldLeg) .arg(newLeg) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// MoveLegBendpointCommand::MoveLegBendpointCommand(SketchWidget* sketchWidget, long fromID, const QString & fromConnectorID, int index, QPointF oldPos, QPointF newPos, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_oldPos = oldPos; m_newPos = newPos; m_fromConnectorID = fromConnectorID; m_index = index; } void MoveLegBendpointCommand::undo() { if (!m_redoOnly) { m_sketchWidget->moveLegBendpoint(m_fromID, m_fromConnectorID, m_index, m_oldPos); } BaseCommand::undo(); } void MoveLegBendpointCommand::redo() { if (!m_undoOnly) { m_sketchWidget->moveLegBendpoint(m_fromID, m_fromConnectorID, m_index, m_newPos); } BaseCommand::redo(); } QString MoveLegBendpointCommand::getParamString() const { return QString("MoveLegBendpointCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 fromc:%2 ix:%3 old:%4,%5 new:%6,%7") .arg(m_fromID) .arg(m_fromConnectorID) .arg(m_index) .arg(m_oldPos.x()) .arg(m_oldPos.y()) .arg(m_newPos.x()) .arg(m_newPos.y()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeLegCurveCommand::ChangeLegCurveCommand(SketchWidget* sketchWidget, long fromID, const QString & connectorID, int index, const Bezier * oldBezier, const Bezier * newBezier, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_oldBezier = m_newBezier = NULL; if (oldBezier) { m_oldBezier = new Bezier; m_oldBezier->copy(oldBezier); } if (newBezier) { m_newBezier = new Bezier; m_newBezier->copy(newBezier); } m_fromConnectorID = connectorID; m_index = index; } void ChangeLegCurveCommand::undo() { m_sketchWidget->changeLegCurve(m_fromID, m_fromConnectorID, m_index, m_oldBezier); BaseCommand::undo(); } void ChangeLegCurveCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else if (!m_undoOnly) { m_sketchWidget->changeLegCurve(m_fromID, m_fromConnectorID, m_index, m_newBezier); } BaseCommand::redo(); } QString ChangeLegCurveCommand::getParamString() const { QString oldBezier; QString newBezier; if (m_oldBezier) { oldBezier += QString("(%1,%2)").arg(m_oldBezier->cp0().x()).arg(m_oldBezier->cp0().y()); oldBezier += QString("(%1,%2)").arg(m_oldBezier->cp1().x()).arg(m_oldBezier->cp1().y()); } if (m_newBezier) { newBezier += QString("(%1,%2)").arg(m_newBezier->cp0().x()).arg(m_newBezier->cp0().y()); newBezier += QString("(%1,%2)").arg(m_newBezier->cp1().x()).arg(m_newBezier->cp1().y()); } return QString("ChangeLegCurveCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 oldp:%2 newp:%3") .arg(m_fromID) .arg(oldBezier) .arg(newBezier) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeLegBendpointCommand::ChangeLegBendpointCommand(SketchWidget* sketchWidget, long fromID, const QString & connectorID, int oldCount, int newCount, int index, QPointF pos, const Bezier * bezier0, const Bezier * bezier1, const Bezier * bezier2, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_bezier0 = m_bezier1 = m_bezier2 = NULL; if (bezier0 != NULL) { m_bezier0 = new Bezier; m_bezier0->copy(bezier0); } if (bezier1 != NULL) { m_bezier1 = new Bezier; m_bezier1->copy(bezier1); } if (bezier2 != NULL) { m_bezier2 = new Bezier; m_bezier2->copy(bezier2); } m_fromConnectorID = connectorID; m_index = index; m_oldCount = oldCount; m_newCount = newCount; m_pos = pos; } void ChangeLegBendpointCommand::undo() { if (m_newCount < m_oldCount) { m_sketchWidget->addLegBendpoint(m_fromID, m_fromConnectorID, m_index, m_pos, m_bezier0, m_bezier1); } else { m_sketchWidget->removeLegBendpoint(m_fromID, m_fromConnectorID, m_index, m_bezier0); } BaseCommand::undo(); } void ChangeLegBendpointCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else { if (m_newCount > m_oldCount) { m_sketchWidget->addLegBendpoint(m_fromID, m_fromConnectorID, m_index, m_pos, m_bezier1, m_bezier2); } else { m_sketchWidget->removeLegBendpoint(m_fromID, m_fromConnectorID, m_index, m_bezier2); } } BaseCommand::redo(); } QString ChangeLegBendpointCommand::getParamString() const { QString bezier; if (m_bezier0) { bezier += QString("(%1,%2)").arg(m_bezier0->cp0().x()).arg(m_bezier0->cp0().y()); bezier += QString("(%1,%2)").arg(m_bezier0->cp1().x()).arg(m_bezier0->cp1().y()); } return QString("ChangeLegBendpointCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 newp:%2") .arg(m_fromID) .arg(bezier) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// RotateLegCommand::RotateLegCommand(SketchWidget* sketchWidget, long fromID, const QString & fromConnectorID, const QPolygonF & oldLeg, bool active, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_oldLeg = oldLeg; m_fromConnectorID = fromConnectorID; m_active = active; } void RotateLegCommand::undo() { BaseCommand::undo(); } void RotateLegCommand::redo() { m_sketchWidget->rotateLeg(m_fromID, m_fromConnectorID, m_oldLeg, m_active); BaseCommand::redo(); } QString RotateLegCommand::getParamString() const { QString oldLeg; foreach (QPointF p, m_oldLeg) { oldLeg += QString("(%1,%2)").arg(p.x()).arg(p.y()); } return QString("RotateLegCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 fromc:%2 old:%3") .arg(m_fromID) .arg(m_fromConnectorID) .arg(oldLeg) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeLayerCommand::ChangeLayerCommand(SketchWidget *sketchWidget, long fromID, double oldZ, double newZ, ViewLayer::ViewLayerID oldLayer, ViewLayer::ViewLayerID newLayer, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_oldZ = oldZ; m_newZ = newZ; m_oldLayer = oldLayer; m_newLayer = newLayer; } void ChangeLayerCommand::undo() { m_sketchWidget->changeLayer(m_fromID, m_oldZ, m_oldLayer); BaseCommand::undo(); } void ChangeLayerCommand::redo() { m_sketchWidget->changeLayer(m_fromID, m_newZ, m_newLayer); BaseCommand::redo(); } QString ChangeLayerCommand::getParamString() const { return QString("ChangeLayerCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 oldZ:%2 newZ:%3 oldL:%4 newL:%5") .arg(m_fromID) .arg(m_oldZ) .arg(m_newZ) .arg(m_oldLayer) .arg(m_newLayer) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// SelectItemCommand::SelectItemCommand(SketchWidget* sketchWidget, SelectItemType type, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_type = type; m_updated = false; } int SelectItemCommand::id() const { return selectItemCommandID; } void SelectItemCommand::setSelectItemType(SelectItemType type) { m_type = type; } void SelectItemCommand::copyUndo(SelectItemCommand * sother) { this->m_undoIDs.clear(); for (int i = 0; i < sother->m_undoIDs.size(); i++) { this->m_undoIDs.append(sother->m_undoIDs[i]); } } void SelectItemCommand::copyRedo(SelectItemCommand * sother) { this->m_redoIDs.clear(); for (int i = 0; i < sother->m_redoIDs.size(); i++) { this->m_redoIDs.append(sother->m_redoIDs[i]); } } void SelectItemCommand::clearRedo() { m_redoIDs.clear(); } bool SelectItemCommand::mergeWith(const QUndoCommand *other) { // "this" is earlier; "other" is later if (other->id() != id()) { return false; } const SelectItemCommand * sother = dynamic_cast(other); if (sother == NULL) return false; if (sother->crossViewType() != this->crossViewType()) { return false; } this->m_redoIDs.clear(); for (int i = 0; i < sother->m_redoIDs.size(); i++) { this->m_redoIDs.append(sother->m_redoIDs[i]); } this->setText(sother->text()); return true; } void SelectItemCommand::undo() { selectAllFromStack(m_undoIDs, true, true); BaseCommand::undo(); } void SelectItemCommand::redo() { switch( m_type ){ case NormalSelect: selectAllFromStack(m_redoIDs, true, true); break; case NormalDeselect: selectAllFromStack(m_redoIDs, false, false); break; case SelectAll: m_sketchWidget->selectAllItems(true, m_crossViewType == BaseCommand::CrossView); break; case DeselectAll: m_sketchWidget->selectAllItems(false, m_crossViewType == BaseCommand::CrossView); break; } BaseCommand::redo(); } void SelectItemCommand::selectAllFromStack(QList & stack, bool select, bool updateInfoView) { m_sketchWidget->clearSelection(); for (int i = 0; i < stack.size(); i++) { m_sketchWidget->selectItem(stack[i], select, updateInfoView, m_crossViewType == BaseCommand::CrossView); } } void SelectItemCommand::addUndo(long id) { m_undoIDs.append(id); } void SelectItemCommand::addRedo(long id) { if(m_type == NormalSelect) { m_redoIDs.append(id); } } QString SelectItemCommand::getParamString() const { return QString("SelectItemCommand ") + BaseCommand::getParamString() + QString(" type:%1") .arg(m_type); } bool SelectItemCommand::updated() { return m_updated; } void SelectItemCommand::setUpdated(bool updated) { m_updated = updated; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeZCommand::ChangeZCommand(SketchWidget* sketchWidget, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { } ChangeZCommand::~ChangeZCommand() { foreach (RealPair * realpair, m_triplets) { delete realpair; } m_triplets.clear(); } void ChangeZCommand::addTriplet(long id, double oldZ, double newZ) { m_triplets.insert(id, new RealPair (oldZ, newZ)); } void ChangeZCommand::undo() { m_sketchWidget->changeZ(m_triplets, first); BaseCommand::undo(); } void ChangeZCommand::redo() { m_sketchWidget->changeZ(m_triplets, second); BaseCommand::redo(); } double ChangeZCommand::first(RealPair * pair) { return pair->first; } double ChangeZCommand::second(RealPair * pair) { return pair->second; } QString ChangeZCommand::getParamString() const { return QString("ChangeZCommand ") + BaseCommand::getParamString(); } //////////////////////////////////////////singlev/////////////////////////////////////////////////////////////////////// CheckStickyCommand::CheckStickyCommand(SketchWidget* sketchWidget, BaseCommand::CrossViewType crossViewType, long itemID, bool checkCurrent, CheckType checkType, QUndoCommand *parent) : BaseCommand(crossViewType, sketchWidget, parent) { m_itemID = itemID; m_skipFirstRedo = true; m_checkType = checkType; m_checkCurrent = checkCurrent; } CheckStickyCommand::~CheckStickyCommand() { foreach (StickyThing * stickyThing, m_stickyList) { delete stickyThing; } m_stickyList.clear(); } void CheckStickyCommand::undo() { if (m_checkType == RedoOnly) return; foreach (StickyThing * stickyThing, m_stickyList) { if (m_checkType == RemoveOnly) { stickyThing->sketchWidget->stickem(stickyThing->fromID, stickyThing->toID, !stickyThing->stickem); } else { stickyThing->sketchWidget->stickem(stickyThing->fromID, stickyThing->toID, stickyThing->stickem); } } BaseCommand::undo(); } void CheckStickyCommand::redo() { if (m_checkType == UndoOnly) return; if (m_skipFirstRedo) { m_sketchWidget->checkSticky(m_itemID, m_crossViewType == BaseCommand::CrossView, m_checkCurrent, this); m_skipFirstRedo = false; } else { foreach (StickyThing * stickyThing, m_stickyList) { stickyThing->sketchWidget->stickem(stickyThing->fromID, stickyThing->toID, stickyThing->stickem); } } BaseCommand::redo(); } QString CheckStickyCommand::getParamString() const { return QString("CheckStickyCommand ") + BaseCommand::getParamString() + QString("id:%1") .arg(this->m_stickyList.count()); } void CheckStickyCommand::stick(SketchWidget * sketchWidget, long fromID, long toID, bool stickem) { StickyThing * stickyThing = new StickyThing; stickyThing->sketchWidget = sketchWidget; stickyThing->fromID = fromID; stickyThing->toID = toID; stickyThing->stickem = stickem; m_stickyList.append(stickyThing); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// CleanUpWiresCommand::CleanUpWiresCommand(SketchWidget* sketchWidget, CleanUpWiresCommand::Direction direction, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_direction = direction; } void CleanUpWiresCommand::undo() { foreach (RatsnestConnectThing rct, m_ratsnestConnectThings) { m_sketchWidget->ratsnestConnect(rct.id, rct.connectorID, !rct.connect, true); } if (m_sketchWidgets.count() > 0) { subUndo(); } if (m_direction == UndoOnly) { m_sketchWidget->cleanUpWires(m_crossViewType == BaseCommand::CrossView, NULL); } BaseCommand::undo(); } void CleanUpWiresCommand::redo() { foreach (RatsnestConnectThing rct, m_ratsnestConnectThings) { m_sketchWidget->ratsnestConnect(rct.id, rct.connectorID, rct.connect, true); } if (m_sketchWidgets.count() > 0) { subRedo(); } if (m_direction == RedoOnly) { m_sketchWidget->cleanUpWires(m_crossViewType == BaseCommand::CrossView, this); } BaseCommand::redo(); } void CleanUpWiresCommand::addRatsnestConnect(long id, const QString & connectorID, bool connect) { RatsnestConnectThing rct; rct.id = id; rct.connectorID = connectorID; rct.connect = connect; m_ratsnestConnectThings.append(rct); } void CleanUpWiresCommand::addRoutingStatus(SketchWidget * sketchWidget, const RoutingStatus & oldRoutingStatus, const RoutingStatus & newRoutingStatus) { addSubCommand(new RoutingStatusCommand(sketchWidget, oldRoutingStatus, newRoutingStatus, NULL)); } void CleanUpWiresCommand::setDirection(CleanUpWiresCommand::Direction direction) { m_direction = direction; } CleanUpWiresCommand::Direction CleanUpWiresCommand::direction() { return m_direction; } void CleanUpWiresCommand::addTrace(SketchWidget * sketchWidget, Wire * wire) { if (m_parentCommand) { for (int i = 0; i < m_parentCommand->childCount(); i++) { const DeleteItemCommand * command = dynamic_cast(m_parentCommand->child(i)); if (command == NULL) continue; if (command->itemID() == wire->id()) { return; } } } m_sketchWidgets.insert(sketchWidget); addSubCommand(new WireColorChangeCommand(sketchWidget, wire->id(), wire->colorString(), wire->colorString(), wire->opacity(), wire->opacity(), NULL)); addSubCommand(new WireWidthChangeCommand(sketchWidget, wire->id(), wire->width(), wire->width(), NULL)); foreach (ConnectorItem * toConnectorItem, wire->connector0()->connectedToItems()) { addSubCommand(new ChangeConnectionCommand(sketchWidget, BaseCommand::CrossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), wire->id(), "connector0", ViewLayer::specFromID(wire->viewLayerID()), false, NULL)); } foreach (ConnectorItem * toConnectorItem, wire->connector1()->connectedToItems()) { addSubCommand(new ChangeConnectionCommand(sketchWidget, BaseCommand::CrossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), wire->id(), "connector1", ViewLayer::specFromID(wire->viewLayerID()), false, NULL)); } addSubCommand(new DeleteItemCommand(sketchWidget, BaseCommand::CrossView, ModuleIDNames::WireModuleIDName, wire->viewLayerPlacement(), wire->getViewGeometry(), wire->id(), wire->modelPart()->modelIndex(), NULL)); } bool CleanUpWiresCommand::hasTraces(SketchWidget * sketchWidget) { return m_sketchWidgets.contains(sketchWidget); } QString CleanUpWiresCommand::getParamString() const { return QString("CleanUpWiresCommand ") + BaseCommand::getParamString() + QString(" direction %1").arg(m_direction); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// CleanUpRatsnestsCommand::CleanUpRatsnestsCommand(SketchWidget* sketchWidget, CleanUpWiresCommand::Direction direction, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { if (direction == CleanUpWiresCommand::UndoOnly) m_undoOnly = true; if (direction == CleanUpWiresCommand::RedoOnly) m_redoOnly = true; } void CleanUpRatsnestsCommand::undo() { if (m_undoOnly) { m_sketchWidget->cleanupRatsnests(true); } BaseCommand::undo(); } void CleanUpRatsnestsCommand::redo() { if (m_redoOnly) { m_sketchWidget->cleanupRatsnests(true); } BaseCommand::redo(); } QString CleanUpRatsnestsCommand::getParamString() const { return QString("CleanUpRatsnestsCommand ") + BaseCommand::getParamString() ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// WireColorChangeCommand::WireColorChangeCommand(SketchWidget* sketchWidget, long wireId, const QString &oldColor, const QString &newColor, double oldOpacity, double newOpacity, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_wireId = wireId; m_oldColor = oldColor; m_newColor = newColor; m_oldOpacity = oldOpacity; m_newOpacity = newOpacity; } void WireColorChangeCommand::undo() { m_sketchWidget->changeWireColor(m_wireId, m_oldColor, m_oldOpacity); BaseCommand::undo(); } void WireColorChangeCommand::redo() { m_sketchWidget->changeWireColor(m_wireId, m_newColor, m_newOpacity); BaseCommand::redo(); } QString WireColorChangeCommand::getParamString() const { return QString("WireColorChangeCommand ") + BaseCommand::getParamString() + QString(" id:%1 oldcolor:%2 oldop:%3 newcolor:%4 newop:%5") .arg(m_wireId).arg(m_oldColor).arg(m_oldOpacity).arg(m_newColor).arg(m_newOpacity); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// WireWidthChangeCommand::WireWidthChangeCommand(SketchWidget* sketchWidget, long wireId, double oldWidth, double newWidth, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_wireId = wireId; m_oldWidth = oldWidth; m_newWidth = newWidth; } void WireWidthChangeCommand::undo() { m_sketchWidget->changeWireWidth(m_wireId, m_oldWidth); BaseCommand::undo(); } void WireWidthChangeCommand::redo() { m_sketchWidget->changeWireWidth(m_wireId, m_newWidth); BaseCommand::redo(); } QString WireWidthChangeCommand::getParamString() const { return QString("WireWidthChangeCommand ") + BaseCommand::getParamString() + QString(" id:%1 oldw:%2 neww:%3") .arg(m_wireId).arg(m_oldWidth).arg(m_newWidth); } /////////////////////////////////////////// RoutingStatusCommand::RoutingStatusCommand(SketchWidget * sketchWidget, const RoutingStatus & oldRoutingStatus, const RoutingStatus & newRoutingStatus, QUndoCommand * parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_oldRoutingStatus = oldRoutingStatus; m_newRoutingStatus = newRoutingStatus; } void RoutingStatusCommand::undo() { m_sketchWidget->forwardRoutingStatus(m_oldRoutingStatus); BaseCommand::undo(); } void RoutingStatusCommand::redo() { m_sketchWidget->forwardRoutingStatus(m_newRoutingStatus); BaseCommand::redo(); } QString RoutingStatusCommand::getParamString() const { return QString("RoutingStatusCommand ") + BaseCommand::getParamString() + QString(" oldnet:%1 oldnetrouted:%2 oldconnectors:%3 oldjumpers:%4 newnet:%51 newnetrouted:%6 newconnectors:%7 newjumpers:%8 ") .arg(m_oldRoutingStatus.m_netCount).arg(m_oldRoutingStatus.m_netRoutedCount).arg(m_oldRoutingStatus.m_connectorsLeftToRoute).arg(m_oldRoutingStatus.m_jumperItemCount) .arg(m_newRoutingStatus.m_netCount).arg(m_newRoutingStatus.m_netRoutedCount).arg(m_newRoutingStatus.m_connectorsLeftToRoute).arg(m_newRoutingStatus.m_jumperItemCount); } /////////////////////////////////////////////// ShowLabelFirstTimeCommand::ShowLabelFirstTimeCommand(SketchWidget *sketchWidget, CrossViewType crossView, long id, bool oldVis, bool newVis, QUndoCommand *parent) : BaseCommand(crossView, sketchWidget, parent) { m_itemID = id; m_oldVis = oldVis; m_newVis = newVis; } void ShowLabelFirstTimeCommand::undo() { BaseCommand::undo(); } void ShowLabelFirstTimeCommand::redo() { m_sketchWidget->showLabelFirstTime(m_itemID, m_newVis, true); BaseCommand::redo(); } QString ShowLabelFirstTimeCommand::getParamString() const { return QString("ShowLabelFirstTimeCommand ") + BaseCommand::getParamString() + QString(" id:%1 %2 %3") .arg(m_itemID).arg(m_oldVis).arg(m_newVis); } /////////////////////////////////////////////// RestoreLabelCommand::RestoreLabelCommand(SketchWidget *sketchWidget,long id, QDomElement & element, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = id; m_element = element; // seems to be safe to keep a copy of the element even though the document may no longer exist } void RestoreLabelCommand::undo() { BaseCommand::undo(); } void RestoreLabelCommand::redo() { m_sketchWidget->restorePartLabel(m_itemID, m_element); BaseCommand::redo(); } QString RestoreLabelCommand::getParamString() const { return QString("RestoreLabelCommand ") + BaseCommand::getParamString() + QString(" id:%1") .arg(m_itemID); } /////////////////////////////////////////////// MoveLabelCommand::MoveLabelCommand(SketchWidget *sketchWidget, long id, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = id; m_oldPos = oldPos; m_newPos = newPos; m_oldOffset = oldOffset; m_newOffset = newOffset; } void MoveLabelCommand::undo() { m_sketchWidget->movePartLabel(m_itemID, m_oldPos, m_oldOffset); BaseCommand::undo(); } void MoveLabelCommand::redo() { m_sketchWidget->movePartLabel(m_itemID, m_newPos, m_newOffset); BaseCommand::redo(); } QString MoveLabelCommand::getParamString() const { return QString("MoveLabelCommand ") + BaseCommand::getParamString() + QString(" id:%1") .arg(m_itemID); } /////////////////////////////////////////////// MoveLockCommand::MoveLockCommand(SketchWidget *sketchWidget, long id, bool oldLock, bool newLock, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = id; m_oldLock = oldLock; m_newLock = newLock; } void MoveLockCommand::undo() { m_sketchWidget->setMoveLock(m_itemID, m_oldLock); BaseCommand::undo(); } void MoveLockCommand::redo() { m_sketchWidget->setMoveLock(m_itemID, m_newLock); BaseCommand::redo(); } QString MoveLockCommand::getParamString() const { return QString("MoveLockCommand ") + BaseCommand::getParamString() + QString(" id:%1 o:%2 n:%3") .arg(m_itemID) .arg(m_oldLock) .arg(m_newLock); } /////////////////////////////////////////////// ChangeLabelTextCommand::ChangeLabelTextCommand(SketchWidget *sketchWidget, long id, const QString & oldText, const QString & newText, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_itemID = id; m_oldText = oldText; m_newText = newText; } void ChangeLabelTextCommand::undo() { m_sketchWidget->setInstanceTitle(m_itemID, m_newText, m_oldText, false, true); BaseCommand::undo(); } void ChangeLabelTextCommand::redo() { m_sketchWidget->setInstanceTitle(m_itemID, m_oldText, m_newText, false, true); BaseCommand::redo(); } QString ChangeLabelTextCommand::getParamString() const { return QString("ChangeLabelTextCommand ") + BaseCommand::getParamString() + QString(" id:%1 old:%2 new:%3") .arg(m_itemID).arg(m_oldText).arg(m_newText); } /////////////////////////////////////////////// IncLabelTextCommand::IncLabelTextCommand(SketchWidget *sketchWidget, long id, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_itemID = id; } void IncLabelTextCommand::undo() { // only used when creating new parts via paste BaseCommand::undo(); } void IncLabelTextCommand::redo() { if (!m_skipFirstRedo) { m_sketchWidget->incInstanceTitle(m_itemID); } BaseCommand::redo(); } QString IncLabelTextCommand::getParamString() const { return QString("IncLabelTextCommand ") + BaseCommand::getParamString() + QString(" id:%1") .arg(m_itemID); }/////////////////////////////////////////////// ChangeNoteTextCommand::ChangeNoteTextCommand(SketchWidget *sketchWidget, long id, const QString & oldText, const QString & newText, QSizeF oldSize, QSizeF newSize, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_itemID = id; m_oldText = oldText; m_newText = newText; m_oldSize = oldSize; m_newSize = newSize; } void ChangeNoteTextCommand::undo() { m_sketchWidget->setNoteText(m_itemID, m_oldText); if (m_oldSize != m_newSize) { m_sketchWidget->resizeNote(m_itemID, m_oldSize); } BaseCommand::undo(); } void ChangeNoteTextCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; return; } m_sketchWidget->setNoteText(m_itemID, m_newText); if (m_oldSize != m_newSize) { m_sketchWidget->resizeNote(m_itemID, m_newSize); } BaseCommand::redo(); } int ChangeNoteTextCommand::id() const { return changeNoteTextCommandID; } bool ChangeNoteTextCommand::mergeWith(const QUndoCommand *other) { // "this" is earlier; "other" is later if (other->id() != id()) { return false; } const ChangeNoteTextCommand * sother = dynamic_cast(other); if (sother == NULL) return false; if (sother->m_itemID != m_itemID) { // this is not the same label so don't merge return false; } m_newSize = sother->m_newSize; m_newText = sother->m_newText; return true; } QString ChangeNoteTextCommand::getParamString() const { return QString("ChangeNoteTextCommand ") + BaseCommand::getParamString() + QString(" id:%1 old:%2 new:%3") .arg(m_itemID).arg(m_oldText).arg(m_newText); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// RotateFlipLabelCommand::RotateFlipLabelCommand(SketchWidget* sketchWidget, long itemID, double degrees, Qt::Orientations orientation, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_degrees = degrees; m_orientation = orientation; } void RotateFlipLabelCommand::undo() { m_sketchWidget->rotateFlipPartLabel(m_itemID, -m_degrees, m_orientation); BaseCommand::undo(); } void RotateFlipLabelCommand::redo() { m_sketchWidget->rotateFlipPartLabel(m_itemID, m_degrees, m_orientation); BaseCommand::redo(); } QString RotateFlipLabelCommand::getParamString() const { return QString("RotateFlipLabelCommand ") + BaseCommand::getParamString() + QString(" id:%1 degrees:%2 orientation:%3") .arg(m_itemID).arg(m_degrees).arg(m_orientation); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ResizeNoteCommand::ResizeNoteCommand(SketchWidget* sketchWidget, long itemID, const QSizeF & oldSize, const QSizeF & newSize, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_oldSize = oldSize; m_newSize = newSize; } void ResizeNoteCommand::undo() { m_sketchWidget->resizeNote(m_itemID, m_oldSize); BaseCommand::undo(); } void ResizeNoteCommand::redo() { m_sketchWidget->resizeNote(m_itemID, m_newSize); BaseCommand::redo(); } QString ResizeNoteCommand::getParamString() const { return QString("ResizeNoteCommand ") + BaseCommand::getParamString() + QString(" id:%1 oldsz:%2 %3 newsz:%4 %5") .arg(m_itemID).arg(m_oldSize.width()).arg(m_oldSize.height()).arg(m_newSize.width()).arg(m_newSize.height()); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ResizeBoardCommand::ResizeBoardCommand(SketchWidget * sketchWidget, long itemID, double oldWidth, double oldHeight, double newWidth, double newHeight, QUndoCommand * parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_oldWidth = oldWidth; m_newWidth = newWidth; m_oldHeight = oldHeight; m_newHeight = newHeight; } void ResizeBoardCommand::undo() { if (!m_redoOnly) { m_sketchWidget->resizeBoard(m_itemID, m_oldWidth, m_oldHeight); } BaseCommand::undo(); } void ResizeBoardCommand::redo() { if (!m_undoOnly) { m_sketchWidget->resizeBoard(m_itemID, m_newWidth, m_newHeight); } BaseCommand::redo(); } QString ResizeBoardCommand::getParamString() const { return QString("ResizeBoardCommand ") + BaseCommand::getParamString() + QString(" id:%1 ow:%2 oh:%3 nw:%4 nh:%5") .arg(m_itemID) .arg(m_oldWidth) .arg(m_oldHeight) .arg(m_newWidth) .arg(m_newHeight); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// TransformItemCommand::TransformItemCommand(SketchWidget *sketchWidget, long id, const QMatrix & oldMatrix, const QMatrix & newMatrix, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = id; m_oldMatrix = oldMatrix; m_newMatrix = newMatrix; } void TransformItemCommand::undo() { m_sketchWidget->transformItem(m_itemID, m_oldMatrix); BaseCommand::undo(); } void TransformItemCommand::redo() { m_sketchWidget->transformItem(m_itemID, m_newMatrix); BaseCommand::redo(); } QString TransformItemCommand::getParamString() const { return QString("TransformItemCommand ") + BaseCommand::getParamString() + QString(" id:%1") .arg(m_itemID); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// SetResistanceCommand::SetResistanceCommand(SketchWidget * sketchWidget, long itemID, QString oldResistance, QString newResistance, QString oldPinSpacing, QString newPinSpacing, QUndoCommand * parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_itemID = itemID; m_oldResistance = oldResistance; m_newResistance = newResistance; m_oldPinSpacing = oldPinSpacing; m_newPinSpacing = newPinSpacing; } void SetResistanceCommand::undo() { m_sketchWidget->setResistance(m_itemID, m_oldResistance, m_oldPinSpacing, true); BaseCommand::undo(); } void SetResistanceCommand::redo() { m_sketchWidget->setResistance(m_itemID, m_newResistance, m_newPinSpacing, true); BaseCommand::redo(); } QString SetResistanceCommand::getParamString() const { return QString("SetResistanceCommand ") + BaseCommand::getParamString() + QString(" id:%1 ov:%2 nv:%3") .arg(m_itemID) .arg(m_oldResistance) .arg(m_newResistance); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// SetPropCommand::SetPropCommand(SketchWidget * sketchWidget, long itemID, QString prop, QString oldValue, QString newValue, bool redraw, QUndoCommand * parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_redraw = redraw; m_itemID = itemID; m_prop = prop; m_oldValue = oldValue; m_newValue = newValue; } void SetPropCommand::undo() { m_sketchWidget->setProp(m_itemID, m_prop, m_oldValue, m_redraw, true); BaseCommand::undo(); } void SetPropCommand::redo() { m_sketchWidget->setProp(m_itemID, m_prop, m_newValue, m_redraw, true); BaseCommand::redo(); } QString SetPropCommand::getParamString() const { return QString("SetPropCommand ") + BaseCommand::getParamString() + QString(" id:%1 p:%2 o:%3 n:%4") .arg(m_itemID) .arg(m_prop) .arg(m_oldValue) .arg(m_newValue); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ResizeJumperItemCommand::ResizeJumperItemCommand(SketchWidget * sketchWidget, long itemID, QPointF oldPos, QPointF oldC0, QPointF oldC1, QPointF newPos, QPointF newC0, QPointF newC1, QUndoCommand * parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = itemID; m_oldPos = oldPos; m_newPos = newPos; m_oldC0 = oldC0; m_newC0 = newC0; m_oldC1 = oldC1; m_newC1 = newC1; } void ResizeJumperItemCommand::undo() { m_sketchWidget->resizeJumperItem(m_itemID, m_oldPos, m_oldC0, m_oldC1); BaseCommand::undo(); } void ResizeJumperItemCommand::redo() { m_sketchWidget->resizeJumperItem(m_itemID, m_newPos, m_newC0, m_newC1); BaseCommand::redo(); } QString ResizeJumperItemCommand::getParamString() const { return QString("ResizeJumperItemCommand ") + BaseCommand::getParamString() + QString(" id:%1 op:%2,%3 oc0:%4,%5 oc1:%6,%7 np:%8,%9 nc0:%10,%11 nc1:%12,%13") .arg(m_itemID) .arg(m_oldPos.x()).arg(m_oldPos.y()) .arg(m_oldC0.x()).arg(m_oldC0.y()) .arg(m_oldC1.x()).arg(m_oldC1.y()) .arg(m_newPos.x()).arg(m_newPos.y()) .arg(m_newC0.x()).arg(m_newC0.y()) .arg(m_newC1.x()).arg(m_newC1.y()) ; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ShowLabelCommand::ShowLabelCommand(class SketchWidget *sketchWidget, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { } void ShowLabelCommand::undo() { foreach (long id, m_idStates.keys()) { m_sketchWidget->showPartLabel(id, (m_idStates.value(id) & 2) != 0); } BaseCommand::undo(); } void ShowLabelCommand::redo() { foreach (long id, m_idStates.keys()) { m_sketchWidget->showPartLabel(id, (m_idStates.value(id) & 1) != 0); } BaseCommand::redo(); } void ShowLabelCommand::add(long id, bool prev, bool post) { int v = 0; if (prev) v += 2; if (post) v += 1; m_idStates.insert(id, v); } QString ShowLabelCommand::getParamString() const { return QString("ShowLabelCommand ") + BaseCommand::getParamString(); } /////////////////////////////////////////////// LoadLogoImageCommand::LoadLogoImageCommand(SketchWidget *sketchWidget, long id, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_itemID = id; m_oldFilename = oldFilename; m_newFilename = newFilename; m_oldAspectRatio = oldAspectRatio; m_oldSvg = oldSvg; m_addName = addName; } void LoadLogoImageCommand::undo() { if (!m_redoOnly) { m_sketchWidget->loadLogoImage(m_itemID, m_oldSvg, m_oldAspectRatio, m_oldFilename); } BaseCommand::undo(); } void LoadLogoImageCommand::redo() { if (m_newFilename.isEmpty()) { } else if (!m_undoOnly) { m_sketchWidget->loadLogoImage(m_itemID, m_newFilename, m_addName); } BaseCommand::redo(); } QString LoadLogoImageCommand::getParamString() const { return QString("LoadLogoImageCommand ") + BaseCommand::getParamString() + QString(" id:%1 old:%2 new:%3") .arg(m_itemID).arg(m_oldFilename).arg(m_newFilename); } /////////////////////////////////////////////// ChangeBoardLayersCommand::ChangeBoardLayersCommand(SketchWidget *sketchWidget, int oldLayers, int newLayers, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_oldLayers = oldLayers; m_newLayers = newLayers; } void ChangeBoardLayersCommand::undo() { m_sketchWidget->changeBoardLayers(m_oldLayers, true); for (int i = childCount() - 1; i >= 0; i--) { ((QUndoCommand *) child(i))->undo(); } BaseCommand::undo(); } void ChangeBoardLayersCommand::redo() { m_sketchWidget->changeBoardLayers(m_newLayers, true); for (int i = 0; i < childCount(); i++) { ((QUndoCommand *) child(i))->redo(); } BaseCommand::redo(); } QString ChangeBoardLayersCommand::getParamString() const { return QString("ChangeBoardLayersCommand ") + BaseCommand::getParamString() + QString(" old:%1 new:%2") .arg(m_oldLayers).arg(m_newLayers); } /////////////////////////////////////////////// SetDropOffsetCommand::SetDropOffsetCommand(SketchWidget *sketchWidget, long id, QPointF dropOffset, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_itemID = id; m_dropOffset = dropOffset; } void SetDropOffsetCommand::undo() { // only used when creating new parts BaseCommand::undo(); } void SetDropOffsetCommand::redo() { m_sketchWidget->setItemDropOffset(m_itemID, m_dropOffset); BaseCommand::redo(); } QString SetDropOffsetCommand::getParamString() const { return QString("SetDropOffsetCommand ") + BaseCommand::getParamString() + QString(" id:%1 %2,%3") .arg(m_itemID).arg(m_dropOffset.x()).arg(m_dropOffset.y()); } /////////////////////////////////////////////// RenamePinsCommand::RenamePinsCommand(SketchWidget *sketchWidget, long id, const QStringList & oldOnes, const QStringList & newOnes, bool singleRow, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_oldLabels = oldOnes; m_newLabels = newOnes; m_itemID = id; m_singleRow = singleRow; } void RenamePinsCommand::undo() { m_sketchWidget->renamePins(m_itemID, m_oldLabels, m_singleRow); BaseCommand::undo(); } void RenamePinsCommand::redo() { m_sketchWidget->renamePins(m_itemID, m_newLabels, m_singleRow); BaseCommand::redo(); } QString RenamePinsCommand::getParamString() const { return QString("RenamePinsCommand ") + BaseCommand::getParamString() + QString(" id:%1 sr:%2") .arg(m_itemID).arg(m_singleRow); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// GroundFillSeedCommand::GroundFillSeedCommand(SketchWidget* sketchWidget, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { setText(QObject::tr("Set Ground Fill Seed")); } void GroundFillSeedCommand::undo() { foreach(GFSThing gfsThing, m_items) { m_sketchWidget->setGroundFillSeed(gfsThing.id, gfsThing.connectorID, !gfsThing.seed); } BaseCommand::undo(); } void GroundFillSeedCommand::redo() { foreach(GFSThing gfsThing, m_items) { m_sketchWidget->setGroundFillSeed(gfsThing.id, gfsThing.connectorID, gfsThing.seed); } BaseCommand::redo(); } void GroundFillSeedCommand::addItem(long id, const QString & connectorID, bool seed) { GFSThing gfsThing; gfsThing.id = id; gfsThing.connectorID = connectorID; gfsThing.seed = seed; m_items.append(gfsThing); } QString GroundFillSeedCommand::getParamString() const { return QString("GroundFillSeedCommand ") + BaseCommand::getParamString() + QString(" items:%1") .arg(m_items.count()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// WireExtrasCommand::WireExtrasCommand(SketchWidget* sketchWidget, long fromID, const QDomElement & oldExtras, const QDomElement & newExtras, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_newExtras = newExtras; m_oldExtras = oldExtras; } void WireExtrasCommand::undo() { if (!m_redoOnly) { m_sketchWidget->setWireExtras(m_fromID, m_oldExtras); } BaseCommand::undo(); } void WireExtrasCommand::redo() { if (!m_undoOnly) { m_sketchWidget->setWireExtras(m_fromID, m_newExtras); } BaseCommand::redo(); } QString WireExtrasCommand::getParamString() const { return QString("WireExtrasCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 ") .arg(m_fromID) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// HidePartLayerCommand::HidePartLayerCommand(SketchWidget *sketchWidget, long fromID, ViewLayer::ViewLayerID layerID, bool wasHidden, bool isHidden, QUndoCommand *parent) : BaseCommand(BaseCommand::SingleView, sketchWidget, parent) { m_fromID = fromID; m_wasHidden = wasHidden; m_isHidden = isHidden; m_layerID = layerID; } void HidePartLayerCommand::undo() { m_sketchWidget->hidePartLayer(m_fromID, m_layerID, m_wasHidden); BaseCommand::undo(); } void HidePartLayerCommand::redo() { m_sketchWidget->hidePartLayer(m_fromID, m_layerID, m_isHidden); BaseCommand::redo(); } QString HidePartLayerCommand::getParamString() const { return QString("HidePartLayerCommand ") + BaseCommand::getParamString() + QString(" fromid:%1 l:%2 was:%3 is:%4") .arg(m_fromID) .arg(m_layerID) .arg(m_wasHidden) .arg(m_isHidden) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// AddSubpartCommand::AddSubpartCommand(SketchWidget *sketchWidget, CrossViewType crossView, long id, long subpartID, QUndoCommand *parent) : BaseCommand(crossView, sketchWidget, parent) { m_itemID = id; m_subpartItemID = subpartID; } void AddSubpartCommand::undo() { if (!m_redoOnly) { m_sketchWidget->addSubpart(m_itemID, m_subpartItemID, true); } BaseCommand::undo(); } void AddSubpartCommand::redo() { if (!m_undoOnly) { m_sketchWidget->addSubpart(m_itemID, m_subpartItemID, true); } BaseCommand::redo(); } QString AddSubpartCommand::getParamString() const { return QString("AddSubpartCommand ") + BaseCommand::getParamString() + QString(" id:%1 subpart id:%2") .arg(m_itemID) .arg(m_subpartItemID) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PackItemsCommand::PackItemsCommand(SketchWidget *sketchWidget, int columns, const QList & ids, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, sketchWidget, parent) { m_ids = ids; m_columns = columns; m_firstTime = true; } void PackItemsCommand::undo() { BaseCommand::undo(); } void PackItemsCommand::redo() { if (m_firstTime) { m_sketchWidget->packItems(m_columns, m_ids, m_parentCommand, true); m_firstTime = false; } BaseCommand::redo(); } QString PackItemsCommand::getParamString() const { return QString("PackItemsCommand ") + BaseCommand::getParamString() + QString(" columns:%1 count:%2") .arg(m_columns) .arg(m_ids.count()) ; } //////////////////////////////////// TemporaryCommand::TemporaryCommand(const QString & text) : QUndoCommand(text) { m_enabled = true; } TemporaryCommand::~TemporaryCommand() { } void TemporaryCommand::setEnabled(bool enabled) { m_enabled = enabled; } void TemporaryCommand::redo() { if (m_enabled) { QUndoCommand::redo(); } } fritzing-app-0.9.3b/src/commands.h000066400000000000000000000645021272363601300170520ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify\ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #ifndef COMMANDS_H #define COMMANDS_H #include #include #include #include "viewgeometry.h" #include "viewlayer.h" #include "routingstatus.h" #include "utils/misc.h" #include "items/itembase.h" ///////////////////////////////////////////// class CommandProgress : public QObject { Q_OBJECT public: CommandProgress(); void setActive(bool); bool active(); void emitUndo(); void emitRedo(); signals: void incUndo(); void incRedo(); protected: bool m_active; }; ///////////////////////////////////////////// class BaseCommand : public QUndoCommand { public: enum CrossViewType { SingleView, CrossView }; public: BaseCommand(BaseCommand::CrossViewType, class SketchWidget*, QUndoCommand* parent); ~BaseCommand(); BaseCommand::CrossViewType crossViewType() const; void setCrossViewType(BaseCommand::CrossViewType); class SketchWidget* sketchWidget() const; int subCommandCount() const; const BaseCommand * subCommand(int index) const; virtual QString getDebugString() const; const QUndoCommand * parentCommand() const; void addSubCommand(BaseCommand * subCommand); void subUndo(); void subRedo(); void subUndo(int index); void subRedo(int index); int index() const; void setUndoOnly(); void setRedoOnly(); void setSkipFirstRedo(); void undo(); void redo(); static int totalChildCount(const QUndoCommand *); static CommandProgress * initProgress(); static void clearProgress(); protected: virtual QString getParamString() const; static int nextIndex; protected: BaseCommand::CrossViewType m_crossViewType; class SketchWidget *m_sketchWidget; QList m_commands; QUndoCommand * m_parentCommand; int m_index; bool m_undoOnly; bool m_redoOnly; bool m_skipFirstRedo; static CommandProgress m_commandProgress; }; ///////////////////////////////////////////// class AddDeleteItemCommand : public BaseCommand { public: AddDeleteItemCommand(class SketchWidget * sketchWidget, BaseCommand::CrossViewType, QString moduleID, ViewLayer::ViewLayerPlacement, ViewGeometry &, qint64 id, long modelIndex, QUndoCommand *parent); long itemID() const; void setDropOrigin(class SketchWidget *); class SketchWidget * dropOrigin(); protected: QString getParamString() const; protected: QString m_moduleID; long m_itemID; ViewGeometry m_viewGeometry; long m_modelIndex; class SketchWidget * m_dropOrigin; ViewLayer::ViewLayerPlacement m_viewLayerPlacement; }; ///////////////////////////////////////////// class AddItemCommand : public AddDeleteItemCommand { public: AddItemCommand(class SketchWidget *sketchWidget, BaseCommand::CrossViewType, QString moduleID, ViewLayer::ViewLayerPlacement, ViewGeometry &, qint64 id, bool updateInfoView, long modelIndex, QUndoCommand *parent); void undo(); void redo(); void addRestoreIndexesCommand(class RestoreIndexesCommand *); protected: QString getParamString() const; protected: bool m_updateInfoView; bool m_module; RestoreIndexesCommand * m_restoreIndexesCommand; }; ///////////////////////////////////////////// class DeleteItemCommand : public AddDeleteItemCommand { public: DeleteItemCommand(class SketchWidget *sketchWidget, BaseCommand::CrossViewType, QString moduleID, ViewLayer::ViewLayerPlacement, ViewGeometry &, qint64 id, long modelIndex, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; }; ///////////////////////////////////////////// class MoveItemCommand : public BaseCommand { public: MoveItemCommand(class SketchWidget *sketchWidget, long id, ViewGeometry & oldG, ViewGeometry & newG, bool updateRatsnest, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: bool m_updateRatsnest; long m_itemID; ViewGeometry m_old; ViewGeometry m_new; }; ///////////////////////////////////////////// class SimpleMoveItemCommand : public BaseCommand { public: SimpleMoveItemCommand(class SketchWidget *sketchWidget, long id, QPointF & oldP, QPointF & newP, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QPointF m_old; QPointF m_new; }; ///////////////////////////////////////////// struct MoveItemThing { long id; QPointF oldPos; QPointF newPos; }; class MoveItemsCommand : public BaseCommand { public: MoveItemsCommand(class SketchWidget *sketchWidget, bool updateRatsnest, QUndoCommand *parent); void undo(); void redo(); void addItem(long id, const QPointF & oldPos, const QPointF & newPos); void addWire(long id, const QString & connectorID); protected: QString getParamString() const; protected: bool m_updateRatsnest; QHash m_wires; QList m_items; }; ///////////////////////////////////////////// class RotateItemCommand : public BaseCommand { public: RotateItemCommand(class SketchWidget *sketchWidget, long id, double degrees, QUndoCommand *parent); void undo(); void redo(); protected: virtual QString getParamString() const; protected: long m_itemID; double m_degrees; }; ///////////////////////////////////////////// class FlipItemCommand : public BaseCommand { public: FlipItemCommand(class SketchWidget *sketchWidget, long id, Qt::Orientations orientation, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; Qt::Orientations m_orientation; }; ///////////////////////////////////////////// class TransformItemCommand : public BaseCommand { public: TransformItemCommand(class SketchWidget *sketchWidget, long id, const class QMatrix & oldMatrix, const class QMatrix & newMatrix, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QMatrix m_oldMatrix; QMatrix m_newMatrix; }; ///////////////////////////////////////////// class ChangeConnectionCommand : public BaseCommand { public: ChangeConnectionCommand(class SketchWidget * sketchWidget, BaseCommand::CrossViewType, long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, QUndoCommand * parent); void undo(); void redo(); void setUpdateConnections(bool updatem); void disable(); protected: QString getParamString() const; protected: long m_fromID; long m_toID; QString m_fromConnectorID; QString m_toConnectorID; bool m_connect; bool m_updateConnections; ViewLayer::ViewLayerPlacement m_viewLayerPlacement; bool m_enabled; }; ///////////////////////////////////////////// class ChangeWireCommand : public BaseCommand { public: ChangeWireCommand(class SketchWidget *sketchWidget, long fromID, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, bool updateConnections, bool updateRatsnest, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: bool m_updateRatsnest; long m_fromID; QLineF m_newLine; QLineF m_oldLine; QPointF m_newPos; QPointF m_oldPos; bool m_updateConnections; }; ///////////////////////////////////////////// class ChangeWireCurveCommand : public BaseCommand { public: ChangeWireCurveCommand(class SketchWidget *sketchWidget, long fromID, const class Bezier * oldBezier, const class Bezier * newBezier, bool wasAutoroutable, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_fromID; class Bezier * m_newBezier; class Bezier * m_oldBezier; bool m_wasAutoroutable; }; ///////////////////////////////////////////// class ChangeLegCommand : public BaseCommand { public: ChangeLegCommand(class SketchWidget *sketchWidget, long fromID, const QString & fromConnectorID, const QPolygonF & oldLeg, const QPolygonF & newLeg, bool relative, bool active, const QString & why, QUndoCommand *parent); void undo(); void redo(); void setSimple(); protected: QString getParamString() const; protected: QString m_fromConnectorID; long m_fromID; QPolygonF m_newLeg; QPolygonF m_oldLeg; bool m_relative; bool m_active; bool m_simple; QString m_why; }; ///////////////////////////////////////////// class MoveLegBendpointCommand : public BaseCommand { public: MoveLegBendpointCommand(class SketchWidget *sketchWidget, long fromID, const QString & fromConnectorID, int index, QPointF oldPos, QPointF newPos, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_fromConnectorID; long m_fromID; QPointF m_newPos; QPointF m_oldPos; }; ///////////////////////////////////////////// class ChangeLegCurveCommand : public BaseCommand { public: ChangeLegCurveCommand(class SketchWidget *sketchWidget, long fromID, const QString & fromConnectorID, int index, const class Bezier * oldBezier, const class Bezier * newBezier, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_fromID; class Bezier * m_newBezier; class Bezier * m_oldBezier; QString m_fromConnectorID; int m_index; }; ///////////////////////////////////////////// class ChangeLegBendpointCommand : public BaseCommand { public: ChangeLegBendpointCommand(class SketchWidget *sketchWidget, long fromID, const QString & fromConnectorID, int oldCount, int newCount, int index, QPointF pos, const class Bezier *, const class Bezier *, const class Bezier *, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_fromID; class Bezier * m_bezier0; class Bezier * m_bezier1; class Bezier * m_bezier2; QString m_fromConnectorID; int m_index; int m_oldCount; int m_newCount; QPointF m_pos; }; ///////////////////////////////////////////// class RotateLegCommand : public BaseCommand { public: RotateLegCommand(class SketchWidget *sketchWidget, long fromID, const QString & fromConnectorID, const QPolygonF & oldLeg, bool active, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_fromConnectorID; long m_fromID; QPolygonF m_oldLeg; bool m_active; }; ///////////////////////////////////////////// class ChangeLayerCommand : public BaseCommand { public: ChangeLayerCommand(class SketchWidget *sketchWidget, long fromID, double oldZ, double newZ, ViewLayer::ViewLayerID oldLayer, ViewLayer::ViewLayerID newLayer, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_fromID; double m_newZ; double m_oldZ; ViewLayer::ViewLayerID m_newLayer; ViewLayer::ViewLayerID m_oldLayer; }; ///////////////////////////////////////////// class SelectItemCommand : public BaseCommand { public: enum SelectItemType { NormalSelect, NormalDeselect, SelectAll, DeselectAll }; public: SelectItemCommand(class SketchWidget *sketchWidget, SelectItemType type, QUndoCommand *parent); void undo(); void redo(); void addUndo(long id); void addRedo(long id); void clearRedo(); int id() const; bool mergeWith(const QUndoCommand *other); void copyUndo(SelectItemCommand * sother); void copyRedo(SelectItemCommand * sother); void setSelectItemType(SelectItemType); bool updated(); void setUpdated(bool); protected: void selectAllFromStack(QList & stack, bool select, bool updateInfoView); QString getParamString() const; protected: QList m_undoIDs; QList m_redoIDs; SelectItemType m_type; bool m_updated; protected: static int selectItemCommandID; }; ///////////////////////////////////////////// class ChangeZCommand : public BaseCommand { public: ChangeZCommand(class SketchWidget *sketchWidget, QUndoCommand *parent); ~ChangeZCommand(); void addTriplet(long id, double oldZ, double newZ); void undo(); void redo(); protected: QString getParamString() const; protected: static double first(RealPair *); static double second(RealPair *); protected: QHash m_triplets; }; struct StickyThing { SketchWidget * sketchWidget; bool stickem; long fromID; long toID; }; ///////////////////////////////////////////// class CheckStickyCommand : public BaseCommand { public: enum CheckType { UndoOnly = 0, RedoOnly, RemoveOnly }; public: CheckStickyCommand(class SketchWidget *sketchWidget, BaseCommand::CrossViewType, long itemID, bool checkCurrent, CheckType, QUndoCommand *parent); ~CheckStickyCommand(); void undo(); void redo(); void stick(SketchWidget *, long fromID, long toID, bool stickem); protected: QString getParamString() const; protected: long m_itemID; QList m_stickyList; bool m_checkCurrent; CheckType m_checkType; }; ///////////////////////////////////////////// class WireColorChangeCommand : public BaseCommand { public: WireColorChangeCommand( SketchWidget* sketchWidget, long wireId, const QString &oldColor, const QString &newColor, double oldOpacity, double newOpacity, QUndoCommand *parent=0); void undo(); void redo(); protected: QString getParamString() const; protected: long m_wireId; QString m_oldColor; QString m_newColor; double m_oldOpacity; double m_newOpacity; }; ///////////////////////////////////////////// class WireWidthChangeCommand : public BaseCommand { public: WireWidthChangeCommand( SketchWidget* sketchWidget, long wireId, double oldWidth, double newWidth, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_wireId; double m_oldWidth; double m_newWidth; }; ///////////////////////////////////////////// class RoutingStatusCommand : public BaseCommand { public: RoutingStatusCommand(class SketchWidget *, const RoutingStatus & oldRoutingStatus, const RoutingStatus & newRoutingStatus, QUndoCommand * parent); void undo(); void redo(); protected: QString getParamString() const; protected: RoutingStatus m_oldRoutingStatus; RoutingStatus m_newRoutingStatus; }; ///////////////////////////////////////////// struct RatsnestConnectThing { long id; QString connectorID; bool connect; }; class CleanUpWiresCommand : public BaseCommand { public: enum Direction { UndoOnly = 0, RedoOnly, Noop }; public: CleanUpWiresCommand(class SketchWidget * sketchWidget, CleanUpWiresCommand::Direction, QUndoCommand * parent); void undo(); void redo(); void addRoutingStatus(SketchWidget *, const RoutingStatus & oldRoutingStatus, const RoutingStatus & newRoutingStatus); void setDirection(CleanUpWiresCommand::Direction); void addTrace(SketchWidget * sketchWidget, Wire * wire); bool hasTraces(SketchWidget *); void addRatsnestConnect(long id, const QString & connectorID, bool connect); CleanUpWiresCommand::Direction direction(); protected: QString getParamString() const; protected: QSet m_sketchWidgets; QList m_ratsnestConnectThings; CleanUpWiresCommand::Direction m_direction; }; ///////////////////////////////////////////// class CleanUpRatsnestsCommand : public BaseCommand { public: CleanUpRatsnestsCommand(class SketchWidget * sketchWidget, CleanUpWiresCommand::Direction, QUndoCommand * parent); void undo(); void redo(); protected: QString getParamString() const; }; ///////////////////////////////////////////// class RestoreLabelCommand : public BaseCommand { public: RestoreLabelCommand(class SketchWidget *sketchWidget, long id, QDomElement &, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QDomElement m_element; }; ///////////////////////////////////////////// class ShowLabelFirstTimeCommand : public BaseCommand { public: ShowLabelFirstTimeCommand(class SketchWidget *sketchWidget, CrossViewType crossView, long id, bool oldVis, bool newVis, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; bool m_oldVis; bool m_newVis; }; ///////////////////////////////////////////// class MoveLabelCommand : public BaseCommand { public: MoveLabelCommand(class SketchWidget *sketchWidget, long id, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QPointF m_oldPos; QPointF m_newPos; QPointF m_oldOffset; QPointF m_newOffset; }; ///////////////////////////////////////////// class MoveLockCommand : public BaseCommand { public: MoveLockCommand(class SketchWidget *sketchWidget, long id, bool oldLock, bool newLock, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; bool m_oldLock; bool m_newLock; }; ///////////////////////////////////////////// class IncLabelTextCommand : public BaseCommand { public: IncLabelTextCommand(class SketchWidget *sketchWidget, long id, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; }; ///////////////////////////////////////////// class ChangeLabelTextCommand : public BaseCommand { public: ChangeLabelTextCommand(class SketchWidget *sketchWidget, long id, const QString & oldText, const QString & newText, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QString m_oldText; QString m_newText; }; ///////////////////////////////////////////// class ChangeNoteTextCommand : public BaseCommand { public: ChangeNoteTextCommand(class SketchWidget *sketchWidget, long id, const QString & oldText, const QString & newText, QSizeF oldSize, QSizeF newSize, QUndoCommand *parent); void undo(); void redo(); int id() const; bool mergeWith(const QUndoCommand *other); protected: QString getParamString() const; protected: long m_itemID; QString m_oldText; QString m_newText; QSizeF m_oldSize; QSizeF m_newSize; static int changeNoteTextCommandID; }; ///////////////////////////////////////////// class RotateFlipLabelCommand : public BaseCommand { public: RotateFlipLabelCommand(class SketchWidget *sketchWidget, long id, double degrees, Qt::Orientations, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; double m_degrees; Qt::Orientations m_orientation; }; ///////////////////////////////////////////// class ResizeNoteCommand : public BaseCommand { public: ResizeNoteCommand(class SketchWidget *sketchWidget, long id, const QSizeF & oldSize, const QSizeF & newSize, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QSizeF m_oldSize; QSizeF m_newSize; }; ///////////////////////////////////////////// class ResizeBoardCommand : public BaseCommand { public: ResizeBoardCommand(class SketchWidget *, long itemID, double oldWidth, double oldHeight, double newWidth, double newHeight, QUndoCommand * parent); void undo(); void redo(); protected: QString getParamString() const; protected: double m_oldWidth; double m_oldHeight; double m_newWidth; double m_newHeight; long m_itemID; }; ///////////////////////////////////////////// class SetResistanceCommand : public BaseCommand { public: SetResistanceCommand(class SketchWidget *, long itemID, QString oldResistance, QString newResistance, QString oldPinSpacing, QString newPinSpacing, QUndoCommand * parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_oldResistance; QString m_newResistance; QString m_oldPinSpacing; QString m_newPinSpacing; long m_itemID; }; ///////////////////////////////////////////// class SetPropCommand : public BaseCommand { public: SetPropCommand(class SketchWidget *, long itemID, QString prop, QString oldValue, QString newValue, bool redraw, QUndoCommand * parent); void undo(); void redo(); protected: QString getParamString() const; protected: bool m_redraw; QString m_prop; QString m_oldValue; QString m_newValue; long m_itemID; }; ///////////////////////////////////////////// class ResizeJumperItemCommand : public BaseCommand { public: ResizeJumperItemCommand(class SketchWidget *, long itemID, QPointF oldPos, QPointF oldC0, QPointF oldC1, QPointF newPos, QPointF newC0, QPointF newC1, QUndoCommand * parent); void undo(); void redo(); protected: QString getParamString() const; protected: QPointF m_oldPos; QPointF m_oldC0; QPointF m_oldC1; QPointF m_newPos; QPointF m_newC0; QPointF m_newC1; long m_itemID; }; ///////////////////////////////////////////// class ShowLabelCommand : public BaseCommand { public: ShowLabelCommand(class SketchWidget *sketchWidget, QUndoCommand *parent); void undo(); void redo(); void add(long id, bool prev, bool post); protected: QString getParamString() const; protected: QHash m_idStates; }; ///////////////////////////////////////////// class LoadLogoImageCommand : public BaseCommand { public: LoadLogoImageCommand(class SketchWidget *sketchWidget, long id, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QString m_oldSvg; QSizeF m_oldAspectRatio; QString m_oldFilename; QString m_newFilename; bool m_addName; }; ///////////////////////////////////////////// class ChangeBoardLayersCommand : public BaseCommand { public: ChangeBoardLayersCommand(class SketchWidget *sketchWidget, int oldLayers, int newLayers, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: int m_oldLayers; int m_newLayers; }; ///////////////////////////////////////////// class SetDropOffsetCommand : public BaseCommand { public: SetDropOffsetCommand(class SketchWidget *sketchWidget, long id, QPointF dropOffset, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QPointF m_dropOffset; }; ///////////////////////////////////////////// class RenamePinsCommand : public BaseCommand { public: RenamePinsCommand(class SketchWidget *sketchWidget, long id, const QStringList & oldOnes, const QStringList & newOnes, bool singleRow, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; QStringList m_oldLabels; QStringList m_newLabels; bool m_singleRow; }; ///////////////////////////////////////////// struct GFSThing { long id; QString connectorID; bool seed; }; class GroundFillSeedCommand : public BaseCommand { public: GroundFillSeedCommand(class SketchWidget *sketchWidget, QUndoCommand *parent); void undo(); void redo(); void addItem(long id, const QString & connectorID, bool seed); protected: QString getParamString() const; protected: QList m_items; }; ///////////////////////////////////////////// class WireExtrasCommand : public BaseCommand { public: WireExtrasCommand(class SketchWidget *sketchWidget, long fromID, const QDomElement & oldExtras, const QDomElement & newExtras, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_fromID; QDomElement m_oldExtras; QDomElement m_newExtras; }; ///////////////////////////////////////////// class HidePartLayerCommand : public BaseCommand { public: HidePartLayerCommand(class SketchWidget *sketchWidget, long fromID, ViewLayer::ViewLayerID layerID, bool wasHidden, bool isHidden, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_fromID; double m_wasHidden; double m_isHidden; ViewLayer::ViewLayerID m_layerID; ViewLayer::ViewLayerID m_oldLayer; }; ///////////////////////////////////////////// class TemporaryCommand : public QUndoCommand { public: TemporaryCommand(const QString & text); ~TemporaryCommand(); void setEnabled(bool); void redo(); protected: bool m_enabled; }; ///////////////////////////////////////////// class AddSubpartCommand : public BaseCommand { public: AddSubpartCommand(class SketchWidget *sketchWidget, CrossViewType crossView, long id, long subpartID, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: long m_itemID; long m_subpartItemID; }; ///////////////////////////////////////////// class PackItemsCommand : public BaseCommand { public: PackItemsCommand(class SketchWidget *sketchWidget, int columns, const QList & ids, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: int m_columns; QList m_ids; bool m_firstTime; }; ///////////////////////////////////////////// #endif // COMMANDS_H fritzing-app-0.9.3b/src/connectors/000077500000000000000000000000001272363601300172465ustar00rootroot00000000000000fritzing-app-0.9.3b/src/connectors/bus.cpp000066400000000000000000000034021272363601300205420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #include "bus.h" #include "busshared.h" #include "../debugdialog.h" #include "connectoritem.h" #include "../model/modelpart.h" Bus::Bus(BusShared * busShared, ModelPart * modelPart) : QObject() { m_busShared = busShared; m_modelPart = modelPart; } const QString & Bus::id() const { if (m_busShared == NULL) return ___emptyString___; return m_busShared->id(); } const QList & Bus::connectors() const { return m_connectors; } void Bus::addConnector(Connector * connector) { // the list of connectors which make up the bus m_connectors.append(connector); } ModelPart * Bus::modelPart() { return m_modelPart; } void Bus::addSubConnector(Connector * subConnector) { m_subConnector = subConnector; } Connector * Bus::subConnector() const { return m_subConnector; }fritzing-app-0.9.3b/src/connectors/bus.h000066400000000000000000000032021272363601300202050ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #ifndef BUS_H #define BUS_H #include #include #include #include #include #include #include class Bus : public QObject { Q_OBJECT public: Bus(class BusShared *, class ModelPart *); const QString & id() const; void addConnector(class Connector *); class ModelPart * modelPart(); const QList & connectors() const; Connector * subConnector() const; void addSubConnector(Connector *); protected: QList m_connectors; class Connector * m_subConnector; BusShared * m_busShared; QPointer m_modelPart; }; #endif fritzing-app-0.9.3b/src/connectors/busshared.cpp000066400000000000000000000047661272363601300217470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6309 $: $Author: cohen@irascible.com $: $Date: 2012-08-17 11:16:43 +0200 (Fr, 17. Aug 2012) $ ********************************************************************/ #include "busshared.h" #include "connectorshared.h" #include "../debugdialog.h" #include "connectoritem.h" #include "../utils/textutils.h" BusShared::BusShared(const QString & id) { m_id = id; } BusShared::BusShared(const QDomElement & busElement, const QHash > & connectorHash) { m_id = busElement.attribute("id"); QDomElement connector = busElement.firstChildElement("nodeMember"); while (!connector.isNull()) { initConnector(connector, connectorHash); connector = connector.nextSiblingElement("nodeMember"); } } void BusShared::initConnector(QDomElement & connector, const QHash > & connectorHash) { QString id = connector.attribute("connectorId"); if (id.isNull()) return; if (id.isEmpty()) return; ConnectorShared * connectorShared = connectorHash.value(id); if (connectorShared == NULL) { QDomDocument document = connector.ownerDocument(); DebugDialog::debug(QString("no connector is found for bus nodeMember %1 in %2") .arg(id) .arg(TextUtils::elementToString(document.documentElement()))); return; } m_connectors.append(connectorShared); connectorShared->setBus(this); } void BusShared::addConnectorShared(ConnectorShared * connectorShared) { m_connectors.append(connectorShared); connectorShared->setBus(this); } const QString & BusShared::id() const { return m_id; } const QList & BusShared::connectors() { return m_connectors; } fritzing-app-0.9.3b/src/connectors/busshared.h000066400000000000000000000032361272363601300214030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6309 $: $Author: cohen@irascible.com $: $Date: 2012-08-17 11:16:43 +0200 (Fr, 17. Aug 2012) $ ********************************************************************/ #ifndef BUSSHARED_H #define BUSSHARED_H #include #include #include #include #include #include class BusShared { public: BusShared(const QDomElement & busElement, const QHash > & connectorHash); BusShared(const QString & id); const QString & id() const; const QList & connectors(); void addConnectorShared(class ConnectorShared *); protected: void initConnector(QDomElement & connector, const QHash > & connectorHash); protected: QString m_id; QList m_connectors; }; #endif fritzing-app-0.9.3b/src/connectors/connector.cpp000066400000000000000000000226251272363601300217530ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #include "connector.h" #include "connectorshared.h" #include "connectoritem.h" #include "../debugdialog.h" #include "../model/modelpart.h" #include "bus.h" #include "../fsvgrenderer.h" #include "ercdata.h" QHash Connector::Names; static const QList EmptySvgIdLayerList; static inline int QuickHash(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { return (1000 * viewID) + viewLayerID; } Connector::Connector( ConnectorShared * connectorShared, ModelPart * modelPart) { m_modelPart = modelPart; m_connectorShared = connectorShared; m_bus = NULL; } Connector::~Connector() { //DebugDialog::debug(QString("deleting connector %1 %2").arg((long) this, 0, 16).arg(connectorSharedID())); foreach (ConnectorItem * connectorItem, m_connectorItems.values()) { connectorItem->clearConnector(); } } void Connector::initNames() { if (Names.count() == 0) { Names.insert(Connector::Male, "male"); Names.insert(Connector::Female, "female"); Names.insert(Connector::Wire, "wire"); Names.insert(Connector::Pad, "pad"); } } Connector::ConnectorType Connector::connectorTypeFromName(const QString & name) { QHashIterator i(Names); while (i.hasNext()) { i.next(); if (name.compare(i.value(), Qt::CaseInsensitive ) == 0) { return i.key(); } } return Connector::Unknown; } const QString & Connector::connectorNameFromType(ConnectorType type) { return Names[type]; } Connector::ConnectorType Connector::connectorType() const { if (m_connectorShared != NULL) { return m_connectorShared->connectorType(); } return Connector::Unknown; } ConnectorShared * Connector::connectorShared() { return m_connectorShared; } void Connector::addViewItem(ConnectorItem * item) { //item->debugInfo(QString("add view item c:%1 ci:%2 b:%3").arg((long) this, 0, 16).arg((long) item, 0, 16).arg((long) m_bus.data(), 0, 16)); m_connectorItems.insert(QuickHash(item->attachedToViewID(), item->attachedToViewLayerID()), item); } void Connector::removeViewItem(ConnectorItem * item) { //DebugDialog::debug(QString("remove view item c:%1 ci:%2 b:%3").arg((long) this, 0, 16).arg((long) item, 0, 16).arg((long) m_bus.data(), 0, 16)); m_connectorItems.remove(QuickHash(item->attachedToViewID(), item->attachedToViewLayerID())); } void Connector::connectTo(Connector * connector) { if (this->modelPart() == NULL) { DebugDialog::debug("connecting bus connector 1"); } else if (connector->modelPart() == NULL) { DebugDialog::debug("connecting bus connector 2"); } if (!m_toConnectors.contains(connector)) { m_toConnectors.append(connector); } if (!connector->m_toConnectors.contains(this)) { connector->m_toConnectors.append(this); } } void Connector::disconnectFrom(Connector * connector) { m_toConnectors.removeOne(connector); connector->m_toConnectors.removeOne(this); } void Connector::saveAsPart(QXmlStreamWriter & writer) { writer.writeStartElement("connector"); writer.writeAttribute("id", connectorShared()->id()); writer.writeAttribute("type", connectorShared()->connectorTypeString()); writer.writeAttribute("name", connectorShared()->sharedName()); writer.writeTextElement("description", connectorShared()->description()); writer.writeTextElement("replacedby", connectorShared()->replacedby()); writer.writeStartElement("views"); QMultiHash pins = m_connectorShared->pins(); foreach (ViewLayer::ViewID currView, pins.uniqueKeys()) { writer.writeStartElement(ViewLayer::viewIDXmlName(currView)); foreach (SvgIdLayer * svgIdLayer, pins.values(currView)) { writer.writeStartElement("p"); writeLayerAttr(writer, svgIdLayer->m_svgViewLayerID); writeSvgIdAttr(writer, currView, svgIdLayer->m_svgId); writeTerminalIdAttr(writer, currView, svgIdLayer->m_terminalId); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndElement(); } void Connector::writeLayerAttr(QXmlStreamWriter &writer, ViewLayer::ViewLayerID viewLayerID) { writer.writeAttribute("layer",ViewLayer::viewLayerXmlNameFromID(viewLayerID)); } void Connector::writeSvgIdAttr(QXmlStreamWriter &writer, ViewLayer::ViewID view, QString connId) { Q_UNUSED(view); writer.writeAttribute("svgId",connId); } void Connector::writeTerminalIdAttr(QXmlStreamWriter &writer, ViewLayer::ViewID view, QString terminalId) { if((view == ViewLayer::BreadboardView || view == ViewLayer::SchematicView) && (!terminalId.isNull() && !terminalId.isEmpty()) ) { writer.writeAttribute("terminalId",terminalId); } else { return; } } const QList & Connector::toConnectors() { return m_toConnectors; } ConnectorItem * Connector::connectorItemByViewLayerID(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { return m_connectorItems.value(QuickHash(viewID, viewLayerID), NULL); } ConnectorItem * Connector::connectorItem(ViewLayer::ViewID viewID) { foreach (ConnectorItem * connectorItem, m_connectorItems.values()) { if (connectorItem->attachedToViewID() == viewID) return connectorItem; } return NULL; } bool Connector::connectionIsAllowed(Connector* that) { Connector::ConnectorType thisConnectorType = connectorType(); Connector::ConnectorType thatConnectorType = that->connectorType(); if (thisConnectorType == Connector::Unknown) return false; if (thatConnectorType == Connector::Unknown) return false; // unknowns are celebate if (thisConnectorType == Connector::Wire) return true; if (thatConnectorType == Connector::Wire) return true; // wires are bisexual return (thisConnectorType != thatConnectorType); // otherwise heterosexual } const QString & Connector::connectorSharedID() const { if (m_connectorShared == NULL) return ___emptyString___; return m_connectorShared->id(); } const QString & Connector::connectorSharedName() const { if (m_connectorShared == NULL) return ___emptyString___; if (!m_connectorLocalName.isEmpty()) { return m_connectorLocalName; } return m_connectorShared->sharedName(); } const QString & Connector::connectorSharedDescription() const { if (m_connectorShared == NULL) return ___emptyString___; return m_connectorShared->description(); } const QString & Connector::connectorSharedReplacedby() const { if (m_connectorShared == NULL) return ___emptyString___; return m_connectorShared->replacedby(); } ErcData * Connector::connectorSharedErcData() { if (m_connectorShared == NULL) return NULL; return m_connectorShared->ercData(); } const QString & Connector::busID() { if (m_bus == NULL) return ___emptyString___; return m_bus->id(); } Bus * Connector::bus() { return m_bus; } void Connector::setBus(Bus * bus) { m_bus = bus; } void Connector::unprocess(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { SvgIdLayer * svgIdLayer = m_connectorShared->fullPinInfo(viewID, viewLayerID); if (svgIdLayer != NULL) { svgIdLayer->unprocess(); } } SvgIdLayer * Connector::fullPinInfo(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { if (m_connectorShared == NULL) return NULL; return m_connectorShared->fullPinInfo(viewID, viewLayerID); } long Connector::modelIndex() { if (m_modelPart != NULL) return m_modelPart->modelIndex(); DebugDialog::debug(QString("saving bus connector item: how is this supposed to work?")); return 0; } ModelPart * Connector::modelPart() { return m_modelPart; } int Connector::connectorItemCount() { return m_connectorItems.count(); } QList< QPointer > Connector::viewItems() { return m_connectorItems.values(); } const QString & Connector::legID(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { if (m_connectorShared) return m_connectorShared->legID(viewID, viewLayerID); return ___emptyString___; } void Connector::setConnectorLocalName(const QString & name) { if (m_connectorShared != NULL && name.compare(m_connectorShared->sharedName()) == 0) { m_connectorLocalName.clear(); return; } m_connectorLocalName = name; } const QString & Connector::connectorLocalName() { return m_connectorLocalName; } const QList Connector::svgIdLayers() const { if (m_connectorShared) return m_connectorShared->svgIdLayers(); return EmptySvgIdLayerList; } void Connector::addPin(ViewLayer::ViewID viewID, const QString & svgId, ViewLayer::ViewLayerID viewLayerId, const QString & terminalId, const QString & legId, bool hybrid) { if (m_connectorShared) m_connectorShared->addPin(viewID, svgId, viewLayerId, terminalId, legId, hybrid); } fritzing-app-0.9.3b/src/connectors/connector.h000066400000000000000000000071261272363601300214170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #ifndef CONNECTOR_H #define CONNECTOR_H #include #include #include #include #include #include #include #include "../viewlayer.h" class Connector : public QObject { Q_OBJECT public: enum ConnectorType { Male, Female, Wire, Pad, Unknown }; public: Connector(class ConnectorShared *, class ModelPart * modelPart); ~Connector(); Connector::ConnectorType connectorType() const; void addViewItem(class ConnectorItem *); void removeViewItem(class ConnectorItem *); class ConnectorShared * connectorShared(); void connectTo(Connector *); void disconnectFrom(Connector *); void saveAsPart(QXmlStreamWriter & writer); const QList & toConnectors(); ConnectorItem * connectorItemByViewLayerID(ViewLayer::ViewID, ViewLayer::ViewLayerID); ConnectorItem * connectorItem(ViewLayer::ViewID); bool connectionIsAllowed(Connector* that); const QString & connectorSharedID() const; const QString & connectorSharedName() const; const QString & connectorSharedDescription() const; const QString & connectorSharedReplacedby() const; class ErcData * connectorSharedErcData(); const QString & busID(); class Bus * bus(); void setBus(class Bus *); long modelIndex(); ModelPart * modelPart(); int connectorItemCount(); void unprocess(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID); class SvgIdLayer * fullPinInfo(ViewLayer::ViewID viewId, ViewLayer::ViewLayerID viewLayerID); const QList svgIdLayers() const; QList< QPointer > viewItems(); const QString & legID(ViewLayer::ViewID, ViewLayer::ViewLayerID); void setConnectorLocalName(const QString &); const QString & connectorLocalName(); void addPin(ViewLayer::ViewID, const QString & svgId, ViewLayer::ViewLayerID, const QString & terminalId, const QString & legId, bool hybrid); public: static void initNames(); static const QString & connectorNameFromType(ConnectorType); static ConnectorType connectorTypeFromName(const QString & name); protected: void writeLayerAttr(QXmlStreamWriter &writer, ViewLayer::ViewLayerID); void writeSvgIdAttr(QXmlStreamWriter &writer, ViewLayer::ViewID view, QString connId); void writeTerminalIdAttr(QXmlStreamWriter &writer, ViewLayer::ViewID view, QString terminalId); protected: QPointer m_connectorShared; QHash< int, QPointer > m_connectorItems; QList m_toConnectors; QPointer m_modelPart; QPointer m_bus; QString m_connectorLocalName; protected: static QHash Names; }; #endif fritzing-app-0.9.3b/src/connectors/connectoritem.cpp000066400000000000000000002365561272363601300226440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ /* rubberBand TODO: * show connectors under * undo moves & connections * adjust position so that connectorItem is in the center of connected-to connectorItem * save and load * alt/meta/ctrl to drag out a wire * rubberBand drag * update connections needs to be smarter (has to to with connecting to wires) look again at attachedMoved() * arrow key moves * hover: trigger the usual part hover highlight * drag selection should work as normal * rubberBand drag when part is stretched between two or more parts, some not being dragged correctly * if a part is locked, disable dragging the leg * fzp has "legId" (and someday? "maxlength" or is that in the svg) in

element put the leg definition as a line in the svg, with connectorNleg then on loading, remove the leg, and change the viewbox height and width this is tricky. Better if the leg extends outside the viewbox, then can easily tell which end is draggable then draw the leg as now * figure out how to make the connector longer or its clickable area bigger, or if you click on the wire within a few pixels.. since it's easy to grab, no need for some kind of fast disconnect * hover color makes a mess when dragging leg * put legItem back into connector item * export: retrieve svg must remove the rubberBand element * make it a polygon instead of a line * what to do when line length is zero * renderToSVG: make sure sceneBoundingRect is including legs * delete/undo delete * clean up pixel turds * rotate/flip do not disconnect should transform around center of the itemBase with no legs * leg cursor feedback * move behavior: what to do when dragging a leg: bendpoints * complex bent leg fails after 2nd rotate * rotate/flip undo failure * when itembase is rotated leg or bendpoint drag behavior is screwed up * rotate target is not correct * bendpoint redo (after adding multiple bendpoints) is failing * subclass leg connectoritem? not for the moment * remove bendpoint: right click, double click * bendpoints: shift-90 degree? * copy/paste connected and not * bad crash when swapping back to unrubberBand. probably some kind of boundingRect issue... * crash: swappable, swappable, undo, redo * swapping parts with rubberBand legs, can assume pins will always line up (unless legs can have diffent max distances) * no-no * no-yes * yes-no * yes-yes * rotate rubberBand, swap rubberBand, undo: crash of the item being undone. it's a prepareGeometryChange() bug * swapping when original is rotated crash swapping 3v battery for 4.8 when 3v is rotated 45 * click selection behavior should be as if selecting the part click on leg should select part * update bug when a rubberBand part has all legs connected and the part is dragged within a particular region, the part body stops updating-- but the legs follow the phantom part until the part jumps into position * export: resistors and other custom generated parts with legs (retrieve svg) curve: undo/redo at mouse release curve is killed * curve: save/load * curve: copy/paste * curve:export * curve: make straight function * curve: fix connector indicator * curve: fix connector click region * curve: connector region is not following when dragging connector when dragging to breadboard from parts bin, don't get final alignment to breadboard survival in parts editor swapping: keep bends? bendpoints: align to grid? resistor: leg y-coordinate is slightly off parts to modify ** LEDs (obsolete 5 colors and 15 SMD versions) maintain color when switching from obsolete ** RGB LEDs ** resistors ** tantalum caps ** electrolytic caps ** ceramic caps ** diodes ** tilt sensor ** temperature sensor ** light sensor ** reed switch ** 2aa battery ** 4aaa battery ** 9v battery ** stepper motors ** servo ** dc motor ** piezo ** loudspeaker ** mic ** solenoid ** peltier ** distance sensor ** transistors ** FETs ** voltage regulator ** resonator ** inductor ** crystal ------------------------------------------------- rubberBand drag with snap-disconnect after a certain length is reached parts editor support */ /////////////////////////////////////////////////////// #include "connectoritem.h" #include #include #include #include #include #include #include #include #include #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "bus.h" #include "../items/wire.h" #include "../items/virtualwire.h" #include "../model/modelpart.h" #include "../utils/graphicsutils.h" #include "../utils/graphutils.h" #include "../utils/textutils.h" #include "../utils/ratsnestcolors.h" #include "../utils/bezier.h" #include "../utils/bezierdisplay.h" #include "../utils/cursormaster.h" #include "ercdata.h" ///////////////////////////////////////////////////////// static Bezier UndoBezier; static BezierDisplay * TheBezierDisplay = NULL; static const double StandardLegConnectorDrawEnabledLength = 5; // pixels static const double StandardLegConnectorDetectLength = 9; // pixels QList ConnectorItem::m_equalPotentialDisplayItems; const QList ConnectorItem::emptyConnectorItemList; static double MAX_DOUBLE = std::numeric_limits::max(); bool wireLessThan(ConnectorItem * c1, ConnectorItem * c2) { if (c1->connectorType() == c2->connectorType()) { // if they're the same type return the topmost return c1->zValue() > c2->zValue(); } if (c1->connectorType() == Connector::Female) { // choose the female first return true; } if (c2->connectorType() == Connector::Female) { // choose the female first return false; } if (c1->connectorType() == Connector::Male) { // choose the male first return true; } if (c2->connectorType() == Connector::Male) { // choose the male first return false; } if (c1->connectorType() == Connector::Pad) { // choose the pad first return true; } if (c2->connectorType() == Connector::Pad) { // choose the pad first return false; } // Connector::Wire last return c1->zValue() > c2->zValue(); } QColor addColor(QColor & color, int offset) { QColor rgb = color.toRgb(); rgb.setRgb(qMax(0, qMin(rgb.red() + offset, 255)), qMax(0, qMin(rgb.green() + offset, 255)),qMax(0, qMin(rgb.blue() + offset, 255))); // convert back to same color spec as original color return rgb.convertTo(color.spec()); } ///////////////////////////////////////////////////////////// ConnectorItemAction::ConnectorItemAction(QAction * action) : QAction(action) { m_connectorItem = NULL; this->setText(action->text()); this->setStatusTip(action->statusTip()); this->setCheckable(action->isCheckable()); } ConnectorItemAction::ConnectorItemAction(const QString & title, QObject * parent) : QAction(title, parent) { m_connectorItem = NULL; } void ConnectorItemAction::setConnectorItem(ConnectorItem * c) { m_connectorItem = c; } ConnectorItem * ConnectorItemAction::connectorItem() { return m_connectorItem; } ///////////////////////////////////////////////////////// ConnectorItem::ConnectorItem( Connector * connector, ItemBase * attachedTo ) : NonConnectorItem(attachedTo) { // initialize m_connectorT, otherwise will trigger qWarning("QLine::unitVector: New line does not have unit length"); // TODO: figure out why paint is being called with m_connectorT not initialized m_groundFillSeed = false; m_connectorDetectT = m_connectorDrawT = 0; m_draggingCurve = m_draggingLeg = m_rubberBandLeg = m_bigDot = m_hybrid = false; m_hoverEnterSpaceBarWasPressed = m_spaceBarWasPressed = false; m_overConnectorItem = NULL; m_connectorHovering = false; m_connector = connector; if (connector != NULL) { connector->addViewItem(this); } setAcceptHoverEvents(true); this->setCursor((attachedTo && attachedTo->itemType() == ModelPart::Wire) ? *CursorMaster::BendpointCursor : *CursorMaster::MakeWireCursor); //DebugDialog::debug(QString("%1 attached to %2") //.arg(this->connector()->connectorShared()->id()) //.arg(attachedTo->modelPartShared()->title()) ); } ConnectorItem::~ConnectorItem() { m_equalPotentialDisplayItems.removeOne(this); //DebugDialog::debug(QString("deleting connectorItem %1").arg((long) this, 0, 16)); foreach (ConnectorItem * connectorItem, m_connectedTo) { if (connectorItem != NULL) { //DebugDialog::debug(QString("temp remove %1 %2").arg(this->attachedToID()).arg(connectorItem->attachedToID())); connectorItem->tempRemove(this, this->attachedToID() != connectorItem->attachedToID()); } } if (this->connector() != NULL) { this->connector()->removeViewItem(this); } clearCurves(); } void ConnectorItem::hoverEnterEvent ( QGraphicsSceneHoverEvent * event ) { //debugInfo("connector hoverEnter"); /* QRectF sbr = this->sceneBoundingRect(); QPointF p = event->scenePos(); debugInfo(QString("hover %1, %2 %3 %4 %5, %6 %7") .arg((long) this, 0, 16) .arg(sbr.left()) .arg(sbr.top()) .arg(sbr.width()) .arg(sbr.height()) .arg(p.x()) .arg(p.y()) ); */ InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) { m_hoverEnterSpaceBarWasPressed = true; event->ignore(); return; } //DebugDialog::debug("---CI set override cursor"); CursorMaster::instance()->addCursor(this, cursor()); bool setDefaultCursor = true; m_hoverEnterSpaceBarWasPressed = false; setHoverColor(); if (infoGraphicsView != NULL) { infoGraphicsView->hoverEnterConnectorItem(event, this); if (m_rubberBandLeg) { updateLegCursor(event->pos(), event->modifiers()); setDefaultCursor = false; } } if (this->m_attachedTo != NULL) { if (this->attachedToItemType() == ModelPart::Wire) { updateWireCursor(event->modifiers()); setDefaultCursor = false; } m_attachedTo->hoverEnterConnectorItem(event, this); } if (setDefaultCursor) CursorMaster::instance()->addCursor(this, *CursorMaster::MakeWireCursor); } void ConnectorItem::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) { if (m_hoverEnterSpaceBarWasPressed) { event->ignore(); return; } QList visited; restoreColor(visited); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->hoverLeaveConnectorItem(event, this); } CursorMaster::instance()->removeCursor(this); if (this->m_attachedTo != NULL) { m_attachedTo->hoverLeaveConnectorItem(event, this); } //DebugDialog::debug("------CI restore override cursor"); CursorMaster::instance()->removeCursor(this); } void ConnectorItem::hoverMoveEvent ( QGraphicsSceneHoverEvent * event ) { if (m_hoverEnterSpaceBarWasPressed) { event->ignore(); return; } if (this->m_attachedTo != NULL) { m_attachedTo->hoverMoveConnectorItem(event, this); } if (m_rubberBandLeg) { updateLegCursor(event->pos(), event->modifiers()); } } Connector * ConnectorItem::connector() { return m_connector; } void ConnectorItem::clearConnectorHover() { m_connectorHovering = false; } void ConnectorItem::connectorHover(ItemBase * itemBase, bool hovering) { m_connectorHovering = hovering; if (hovering) { setHoverColor(); // could make this light up buses as well } else { QList visited; restoreColor(visited); } if (this->m_attachedTo != NULL) { m_attachedTo->connectorHover(this, itemBase, hovering); } } bool ConnectorItem::connectorHovering() { return m_connectorHovering; } void ConnectorItem::connectTo(ConnectorItem * connected) { if (m_connectedTo.contains(connected)) return; m_connectedTo.append(connected); //DebugDialog::debug(QString("connect to cc:%4 this:%1 to:%2 %3").arg((long) this, 0, 16).arg((long) connected, 0, 16).arg(connected->attachedTo()->modelPartShared()->title()).arg(m_connectedTo.count()) ); QList visited; restoreColor(visited); if (m_attachedTo != NULL) { m_attachedTo->connectionChange(this, connected, true); } } ConnectorItem * ConnectorItem::removeConnection(ItemBase * itemBase) { QList visited; for (int i = 0; i < m_connectedTo.count(); i++) { if (m_connectedTo[i]->attachedTo() == itemBase) { ConnectorItem * removed = m_connectedTo[i]; m_connectedTo.removeAt(i); if (m_attachedTo != NULL) { m_attachedTo->connectionChange(this, removed, false); } restoreColor(visited); DebugDialog::debug(QString("remove from:%1 to:%2 count%3") .arg((long) this, 0, 16) .arg(itemBase->modelPartShared()->title()) .arg(m_connectedTo.count()) ); return removed; } } return NULL; } void ConnectorItem::removeConnection(ConnectorItem * connectedItem, bool emitChange) { if (connectedItem == NULL) return; m_connectedTo.removeOne(connectedItem); QList visited; restoreColor(visited); if (emitChange) { m_attachedTo->connectionChange(this, connectedItem, false); } } void ConnectorItem::tempConnectTo(ConnectorItem * item, bool applyColor) { if (!m_connectedTo.contains(item)) m_connectedTo.append(item); if(applyColor) { QList visited; restoreColor(visited); } } void ConnectorItem::tempRemove(ConnectorItem * item, bool applyColor) { m_connectedTo.removeOne(item); if(applyColor) { QList visited; restoreColor(visited); } } void ConnectorItem::restoreColor(QList & visited) { if (visited.contains(this)) return; visited.append(this); if (!attachedTo()->isEverVisible()) return; QList connectorItems; connectorItems.append(this); collectEqualPotential(connectorItems, true, getSkipFlags()); visited.append(connectorItems); QSet attachedTo; foreach (ConnectorItem * connectorItem, connectorItems) { if (connectorItem->isEverVisible()) { if (connectorItem->attachedToItemType() != ModelPart::Wire) { attachedTo.insert(connectorItem->attachedTo()->layerKinChief()); } } } foreach (ConnectorItem * connectorItem, connectorItems) { if (connectorItem->isEverVisible()) { //QString how; if (attachedTo.count() <= 1) { if (connectorItem->connectorType() == Connector::Female) { if (connectorItem->connectionsCount() > 0) connectorItem->setUnconnectedColor(); else connectorItem->setNormalColor(); //how = "normal"; } else { connectorItem->setUnconnectedColor(); //how = "unconnected"; } } else { connectorItem->setConnectedColor(); //how = "connected"; } } } /* DebugDialog::debug(QString("restore color dobus:%1 bccount:%2 docross:%3 cid:'%4' '%5' id:%6 '%7' vid:%8 vlid:%9 %10") .arg(doBuses) .arg(busConnectionCount) .arg(doCross) .arg(this->connectorSharedID()) .arg(this->connectorSharedName()) .arg(this->attachedToID()) .arg(this->attachedToInstanceTitle()) .arg(this->attachedToViewID()) .arg(this->attachedToViewLayerID()) .arg(how) ); */ } void ConnectorItem::setConnectedColor() { if (m_attachedTo == NULL) return; QBrush brush; QPen pen; m_attachedTo->getConnectedColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect); //DebugDialog::debug(QString("set connected %1 %2").arg(attachedToID()).arg(pen->width())); setColorAux(brush, pen, true); } void ConnectorItem::setNormalColor() { if (m_attachedTo == NULL) return; QBrush brush; QPen pen; m_attachedTo->getNormalColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect); //DebugDialog::debug(QString("set normal %1 %2").arg(attachedToID()).arg(pen->width())); setColorAux(brush, pen, false); } void ConnectorItem::setUnconnectedColor() { if (m_attachedTo == NULL) return; QBrush brush; QPen pen; //DebugDialog::debug(QString("set unconnected %1").arg(attachedToID()) ); m_attachedTo->getUnconnectedColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect); setColorAux(brush, pen, true); } void ConnectorItem::setHoverColor() { if (m_attachedTo == NULL) return; QBrush brush; QPen pen; m_attachedTo->getHoverColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect); setColorAux(brush, pen, true); } void ConnectorItem::setColorAux(const QBrush & brush, const QPen & pen, bool paint) { //debugInfo(QString("setColorAux %1 %2").arg(brush.color().name()).arg(pen.color().name())); m_paint = paint; this->setBrush(brush); this->setPen(pen); update(); } void ConnectorItem::setColorAux(const QColor &color, bool paint) { setColorAux(QBrush(color), QPen(color), paint); } void ConnectorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { //DebugDialog::debug("in connectorItem mouseReleaseEvent"); clearEqualPotentialDisplay(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (m_rubberBandLeg && m_draggingLeg) { m_draggingLeg = false; if (m_insertBendpointPossible) { // didn't move far enough; bail return; } if (m_moveCount == 0) { // never moved return; } ConnectorItem * to = releaseDrag(); if (m_draggingCurve) { m_draggingCurve = false; if (TheBezierDisplay) { delete TheBezierDisplay; TheBezierDisplay = NULL; } if (infoGraphicsView != NULL) { infoGraphicsView->prepLegCurveChange(this, m_draggingLegIndex, &UndoBezier, m_legCurves.at(m_draggingLegIndex), false); } return; } if (m_oldPolygon.count() < m_legPolygon.count()) { // we inserted a bendpoint InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->prepLegBendpointChange(this, m_oldPolygon.count(), m_legPolygon.count(), m_draggingLegIndex, m_legPolygon.at(m_draggingLegIndex), &UndoBezier, m_legCurves.at(m_draggingLegIndex -1), m_legCurves.at(m_draggingLegIndex), false); } return; } bool changeConnections = m_draggingLegIndex == m_legPolygon.count() - 1; if (to != NULL && changeConnections) { // center endpoint in the target connectorItem reposition(to->sceneAdjustedTerminalPoint(NULL), m_draggingLegIndex); } if (infoGraphicsView != NULL) { infoGraphicsView->prepLegBendpointMove(this, m_draggingLegIndex, mapToScene(m_oldPolygon.at(m_draggingLegIndex)), mapToScene(m_legPolygon.at(m_draggingLegIndex)), to, changeConnections); } return; } if (this->m_attachedTo != NULL && m_attachedTo->acceptsMouseReleaseConnectorEvent(this, event)) { m_attachedTo->mouseReleaseConnectorEvent(this, event); return; } QGraphicsRectItem::mouseReleaseEvent(event); } void ConnectorItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { if (m_rubberBandLeg) { int bendpointIndex; CursorLocation cursorLocation = findLocation(event->pos(), bendpointIndex); switch (cursorLocation) { case InBendpoint: if (bendpointIndex < m_legPolygon.count() - 1) { removeBendpoint(bendpointIndex); } break; case InSegment: insertBendpoint(event->pos(), bendpointIndex); break; default: break; } return; } if (this->m_attachedTo != NULL && m_attachedTo->acceptsMouseDoubleClickConnectorEvent(this, event)) { m_attachedTo->mouseDoubleClickConnectorEvent(this); return; } QGraphicsRectItem::mouseDoubleClickEvent(event); } void ConnectorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { m_moveCount++; if (m_rubberBandLeg && m_draggingLeg) { if (m_draggingCurve) { Bezier * bezier = m_legCurves.at(m_draggingLegIndex); if (bezier != NULL && !bezier->isEmpty()) { prepareGeometryChange(); bezier->recalc(event->pos()); calcConnectorEnd(); update(); if (TheBezierDisplay) TheBezierDisplay->updateDisplay(this, bezier); return; } } QPointF currentPos = event->scenePos(); QPointF buttonDownPos = event->buttonDownScenePos(Qt::LeftButton); if (m_insertBendpointPossible) { if (qSqrt(GraphicsUtils::distanceSqd(currentPos, buttonDownPos)) >= QApplication::startDragDistance()) { insertBendpointAux(m_holdPos, m_draggingLegIndex); m_insertBendpointPossible = false; } else { return; } } if (event->modifiers() & Qt::ShiftModifier && m_draggingLegIndex > 0 && m_draggingLegIndex < m_legPolygon.count() - 1) { bool bendpoint = false; QPointF initialPos = mapToScene(m_legPolygon.at(m_draggingLegIndex - 1)); QPointF otherInitialPos = mapToScene(m_legPolygon.at(m_draggingLegIndex + 1)); QPointF p1(initialPos.x(), otherInitialPos.y()); double d = GraphicsUtils::distanceSqd(p1, currentPos); if (d <= 144) { bendpoint = true; currentPos = p1; } else { p1.setX(otherInitialPos.x()); p1.setY(initialPos.y()); d = GraphicsUtils::distanceSqd(p1, currentPos); if (d <= 144) { bendpoint = true; currentPos = p1; } } if (!bendpoint) { currentPos = GraphicsUtils::calcConstraint(initialPos, currentPos); } } reposition(m_holdPos + currentPos - buttonDownPos, m_draggingLegIndex); QList exclude; findConnectorUnder(true, true, exclude, true, this); return; } if (this->m_attachedTo != NULL && m_attachedTo->acceptsMouseMoveConnectorEvent(this, event)) { m_attachedTo->mouseMoveConnectorEvent(this, event); return; } QGraphicsRectItem::mouseMoveEvent(event); } void ConnectorItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { m_draggingCurve = m_draggingLeg = false; m_moveCount = 0; if (event->button() != Qt::LeftButton) { QGraphicsRectItem::mousePressEvent(event); return; } if (m_attachedTo->filterMousePressConnectorEvent(this, event)) { event->ignore(); return; } clearEqualPotentialDisplay(); InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL && infographics->spaceBarIsPressed()) { event->ignore(); return; } m_equalPotentialDisplayItems.append(this); collectEqualPotential(m_equalPotentialDisplayItems, true, ViewGeometry::NoFlag); //m_equalPotentialDisplayItems.removeAt(0); // not sure whether to leave the clicked one in or out of the list QList visited; //DebugDialog::debug("_______________________"); foreach (ConnectorItem * connectorItem, m_equalPotentialDisplayItems) { connectorItem->showEqualPotential(true, visited); //connectorItem->debugInfo("display eqp"); } if (m_rubberBandLeg && this->m_attachedTo != NULL && m_attachedTo->acceptsMousePressLegEvent(this, event)) { if (legMousePressEvent(event)) return; } if (this->m_attachedTo != NULL && m_attachedTo->acceptsMousePressConnectorEvent(this, event)) { m_attachedTo->mousePressConnectorEvent(this, event); return; } QGraphicsRectItem::mousePressEvent(event); } int ConnectorItem::connectionsCount() { return m_connectedTo.count(); } void ConnectorItem::attachedMoved(bool includeRatsnest, QList & already) { //DebugDialog::debug("attached moved"); if (!this->isVisible()) return; QSet allTo; allTo.insert(this); foreach (ConnectorItem * toConnectorItem, m_connectedTo) { allTo.insert(toConnectorItem); foreach (ConnectorItem * subTo, toConnectorItem->connectedToItems()) { allTo.insert(subTo); } } allTo.remove(this); foreach (ConnectorItem * toConnectorItem, allTo) { ItemBase * itemBase = toConnectorItem->attachedTo(); if (itemBase == NULL) continue; if (!itemBase->isVisible()) { //this->debugInfo("continue"); //itemBase->debugInfo(" "); continue; } if (itemBase->getRatsnest() && !includeRatsnest) { continue; } toConnectorItem->attachedTo()->connectedMoved(this, toConnectorItem, already); } } ConnectorItem * ConnectorItem::firstConnectedToIsh() { if (m_connectedTo.count() <= 0) return NULL; foreach (ConnectorItem * connectorItem, m_connectedTo) { if (connectorItem->attachedTo()->getRatsnest()) continue; if (!connectorItem->isVisible()) continue; return connectorItem; } // TODO: not sure whether to return invisible connectors // TODO: get rid of this function? foreach (ConnectorItem * connectorItem, m_connectedTo) { if (connectorItem->attachedTo()->getRatsnest()) continue; return connectorItem; } return NULL; } void ConnectorItem::setTerminalPoint(QPointF p) { m_terminalPoint = p; } QPointF ConnectorItem::terminalPoint() { return m_terminalPoint; } QPointF ConnectorItem::adjustedTerminalPoint() { if (m_legPolygon.count() < 2) { return m_terminalPoint + this->rect().topLeft(); } return m_legPolygon.last(); } QPointF ConnectorItem::sceneAdjustedTerminalPoint(ConnectorItem * connectee) { if ((connectee != NULL) && !m_circular && !m_shape.isEmpty() && (connectee->attachedToItemType() == ModelPart::Wire)) { Wire * wire = qobject_cast(connectee->attachedTo()); if ((wire != NULL) && !wire->getRatsnest()) { QPointF anchor = wire->otherConnector(connectee)->sceneAdjustedTerminalPoint(NULL); double newX = 0, newY = 0, newDistance = MAX_DOUBLE; int count = m_shape.elementCount(); QPointF prev; for (int i = 0; i < count; i++) { QPainterPath::Element el = m_shape.elementAt(i); if (el.isMoveTo()) { prev = this->mapToScene(QPointF(el)); } else { QPointF current = this->mapToScene(QPointF(el)); double candidateX, candidateY, candidateDistance; bool atEndpoint; GraphicsUtils::distanceFromLine(anchor.x(), anchor.y(), prev.x(), prev.y(), current.x(), current.y(), candidateX, candidateY, candidateDistance, atEndpoint); if (candidateDistance < newDistance) { newX = candidateX; newY = candidateY; newDistance = candidateDistance; //DebugDialog::debug(QString("anchor:%1,%2; new:%3,%4; %5").arg(anchor.x()).arg(anchor.y()).arg(newX).arg(newY).arg(newDistance)); } prev = current; } } //DebugDialog::debug(QString("anchor:%1,%2; new:%3,%4; %5\n\n").arg(anchor.x()).arg(anchor.y()).arg(newX).arg(newY).arg(newDistance)); return QPointF(newX, newY); } } return this->mapToScene(adjustedTerminalPoint()); } bool ConnectorItem::connectedTo(ConnectorItem * connectorItem) { return this->m_connectedTo.contains(connectorItem); } const QList< QPointer > & ConnectorItem::connectedToItems() { return m_connectedTo; } void ConnectorItem::setHidden(bool hide) { m_hidden = hide; setHiddenOrInactive(); } void ConnectorItem::setHybrid(bool h) { m_hybrid = h; setHiddenOrInactive(); } bool ConnectorItem::isHybrid() { return m_hybrid; } void ConnectorItem::setBigDot(bool bd) { m_bigDot = bd; //if (bd) { // this->debugInfo("big dot"); //} } bool ConnectorItem::isBigDot() { return m_bigDot; } void ConnectorItem::setInactive(bool inactivate) { m_inactive = inactivate; setHiddenOrInactive(); } void ConnectorItem::setHiddenOrInactive() { if (m_hidden || m_inactive || m_hybrid || m_layerHidden) { this->setAcceptedMouseButtons(Qt::NoButton); this->unsetCursor(); setAcceptHoverEvents(false); } else { this->setAcceptedMouseButtons(ALLMOUSEBUTTONS); this->setCursor(attachedToItemType() == ModelPart::Wire ? *CursorMaster::BendpointCursor : *CursorMaster::MakeWireCursor); setAcceptHoverEvents(true); } this->update(); } ConnectorItem * ConnectorItem::overConnectorItem() { return m_overConnectorItem; } void ConnectorItem::setOverConnectorItem(ConnectorItem * connectorItem) { m_overConnectorItem = connectorItem; } const QString & ConnectorItem::connectorSharedID() { if (m_connector == NULL) return ___emptyString___; return m_connector->connectorSharedID(); } const QString & ConnectorItem::connectorSharedReplacedby() { if (m_connector == NULL) return ___emptyString___; return m_connector->connectorSharedReplacedby(); } ErcData * ConnectorItem::connectorSharedErcData() { if (m_connector == NULL) return NULL; return m_connector->connectorSharedErcData(); } const QString & ConnectorItem::connectorSharedName() { if (m_connector == NULL) return ___emptyString___; return m_connector->connectorSharedName(); } const QString & ConnectorItem::connectorSharedDescription() { if (m_connector == NULL) return ___emptyString___; return m_connector->connectorSharedDescription(); } const QString & ConnectorItem::busID() { if (m_connector == NULL) return ___emptyString___; return m_connector->busID(); } ModelPartShared * ConnectorItem::modelPartShared() { if (m_attachedTo == NULL) return NULL; return m_attachedTo->modelPartShared(); } ModelPart * ConnectorItem::modelPart() { if (m_attachedTo == NULL) return NULL; return m_attachedTo->modelPart(); } Bus * ConnectorItem::bus() { if (m_connector == NULL) return NULL; return m_connector->bus(); } ViewLayer::ViewLayerID ConnectorItem::attachedToViewLayerID() { if (m_attachedTo == NULL) return ViewLayer::UnknownLayer; return m_attachedTo->viewLayerID(); } ViewLayer::ViewLayerPlacement ConnectorItem::attachedToViewLayerPlacement() { if (m_attachedTo == NULL) return ViewLayer::UnknownPlacement; return m_attachedTo->viewLayerPlacement(); } ViewLayer::ViewID ConnectorItem::attachedToViewID() { if (m_attachedTo == NULL) return ViewLayer::UnknownView; return m_attachedTo->viewID(); } Connector::ConnectorType ConnectorItem::connectorType() { if (m_connector == NULL) return Connector::Unknown; return m_connector->connectorType(); } bool ConnectorItem::chained() { foreach (ConnectorItem * toConnectorItem, m_connectedTo) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { return true; } } return false; } void ConnectorItem::saveInstance(QXmlStreamWriter & writer) { if (m_connectedTo.count() <= 0 && !m_rubberBandLeg && !m_groundFillSeed) { // no need to save if there's no connection return; } writer.writeStartElement("connector"); writer.writeAttribute("connectorId", connectorSharedID()); writer.writeAttribute("layer", ViewLayer::viewLayerXmlNameFromID(attachedToViewLayerID())); if (m_groundFillSeed) { writer.writeAttribute("groundFillSeed", "true"); } writer.writeStartElement("geometry"); QPointF p = this->pos(); writer.writeAttribute("x", QString::number(p.x())); writer.writeAttribute("y", QString::number(p.y())); writer.writeEndElement(); if (m_rubberBandLeg && m_legPolygon.count() > 1) { writer.writeStartElement("leg"); for (int i = 0; i < m_legPolygon.count(); i++) { QPointF p = m_legPolygon.at(i); writer.writeStartElement("point"); writer.writeAttribute("x", QString::number(p.x())); writer.writeAttribute("y", QString::number(p.y())); writer.writeEndElement(); Bezier * bezier = m_legCurves.at(i); if (bezier == NULL) { writer.writeStartElement("bezier"); writer.writeEndElement(); } else { bezier->write(writer); } } writer.writeEndElement(); } if (m_connectedTo.count() > 0) { writer.writeStartElement("connects"); foreach (ConnectorItem * connectorItem, this->m_connectedTo) { if (connectorItem->attachedTo()->getRatsnest()) continue; connectorItem->writeConnector(writer, "connect"); } writer.writeEndElement(); } writeOtherElements(writer); writer.writeEndElement(); } void ConnectorItem::writeConnector(QXmlStreamWriter & writer, const QString & elementName) { //DebugDialog::debug(QString("write connector %1").arg(this->attachedToID())); writer.writeStartElement(elementName); writer.writeAttribute("connectorId", connectorSharedID()); writer.writeAttribute("modelIndex", QString::number(connector()->modelIndex())); writer.writeAttribute("layer", ViewLayer::viewLayerXmlNameFromID(attachedToViewLayerID())); writer.writeEndElement(); } void ConnectorItem::writeOtherElements(QXmlStreamWriter & writer) { Q_UNUSED(writer); } bool ConnectorItem::wiredTo(ConnectorItem * target, ViewGeometry::WireFlags skipFlags) { QList connectorItems; connectorItems.append(this); collectEqualPotential(connectorItems, true, skipFlags); return connectorItems.contains(target); } Wire * ConnectorItem::directlyWiredTo(ConnectorItem * source, ConnectorItem * target, ViewGeometry::WireFlags flags) { QList visited; return directlyWiredToAux(source, target, flags, visited); } Wire * ConnectorItem::directlyWiredToAux(ConnectorItem * source, ConnectorItem * target, ViewGeometry::WireFlags flags, QList & visited) { if (visited.contains(source)) return NULL; QList equals; equals << source; ConnectorItem * cross = source->getCrossLayerConnectorItem(); if (cross) { if (!visited.contains(cross)) { equals << cross; } } visited.append(equals); foreach (ConnectorItem * fromItem, equals) { foreach (ConnectorItem * toConnectorItem, fromItem->m_connectedTo) { ItemBase * toItem = toConnectorItem->attachedTo(); if (toItem == NULL) { continue; // shouldn't happen } if (toItem->itemType() != ModelPart::Wire) continue; Wire * wire = qobject_cast(toItem); if (!wire->hasAnyFlag(flags)) continue; ConnectorItem * otherEnd = wire->otherConnector(toConnectorItem); bool isChained = false; foreach (ConnectorItem * otherConnectorItem, otherEnd->m_connectedTo) { if (target == otherConnectorItem) { return wire; } if (target->getCrossLayerConnectorItem() == otherConnectorItem) { return wire; } if (otherConnectorItem->attachedToItemType() == ModelPart::Wire) { //DebugDialog::debug(QString("wired from %1 to %2").arg(wire->id()).arg(otherConnectorItem->attachedToID())); isChained = true; } } if (isChained) { if (ConnectorItem::directlyWiredToAux(otherEnd, target, flags, visited)) { return wire; } } } } return NULL; } bool ConnectorItem::isConnectedToPart() { QList tempItems; tempItems << this; ConnectorItem * thisCrossConnectorItem = this->getCrossLayerConnectorItem(); QList busConnectedItems; Bus * b = bus(); if (b != NULL) { attachedTo()->busConnectorItems(b, this, busConnectedItems); } for (int i = 0; i < tempItems.count(); i++) { ConnectorItem * connectorItem = tempItems[i]; if ((connectorItem != this) && (connectorItem != thisCrossConnectorItem) && !busConnectedItems.contains(connectorItem)) { switch (connectorItem->attachedToItemType()) { case ModelPart::Symbol: case ModelPart::SchematicSubpart: case ModelPart::Jumper: case ModelPart::Part: case ModelPart::CopperFill: case ModelPart::Board: case ModelPart::Breadboard: case ModelPart::ResizableBoard: case ModelPart::Via: if (connectorItem->attachedTo()->isEverVisible()) { return true; } break; default: break; } } ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem(); if (crossConnectorItem != NULL) { if (!tempItems.contains(crossConnectorItem)) { tempItems.append(crossConnectorItem); } } foreach (ConnectorItem * cto, connectorItem->connectedToItems()) { if (tempItems.contains(cto)) continue; tempItems.append(cto); } Bus * bus = connectorItem->bus(); if (bus != NULL) { QList busConnectedItems; connectorItem->attachedTo()->busConnectorItems(bus, connectorItem, busConnectedItems); foreach (ConnectorItem * busConnectedItem, busConnectedItems) { if (!tempItems.contains(busConnectedItem)) { tempItems.append(busConnectedItem); } } } } return false; } void ConnectorItem::collectEqualPotential(QList & connectorItems, bool crossLayers, ViewGeometry::WireFlags skipFlags) { // collects all the connectors at the same potential // allows direct connections or wired connections //DebugDialog::debug("__________________"); QList tempItems = connectorItems; connectorItems.clear(); for (int i = 0; i < tempItems.count(); i++) { ConnectorItem * connectorItem = tempItems[i]; //connectorItem->debugInfo("testing eqp"); Wire * fromWire = (connectorItem->attachedToItemType() == ModelPart::Wire) ? qobject_cast(connectorItem->attachedTo()) : NULL; if (fromWire != NULL) { if (fromWire->hasAnyFlag(skipFlags)) { // don't add this kind of wire continue; } } else { if (crossLayers) { ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem(); if (crossConnectorItem != NULL) { if (!tempItems.contains(crossConnectorItem)) { tempItems.append(crossConnectorItem); } } } } // this one's a keeper connectorItems.append(connectorItem); //connectorItem->debugInfo("collect"); foreach (ConnectorItem * cto, connectorItem->connectedToItems()) { if (tempItems.contains(cto)) continue; if ((skipFlags & ViewGeometry::NormalFlag) && (fromWire == NULL) && (cto->attachedToItemType() != ModelPart::Wire)) { // direct (part-to-part) connections not allowed continue; } tempItems.append(cto); } Bus * bus = connectorItem->bus(); if (bus != NULL) { QList busConnectedItems; connectorItem->attachedTo()->busConnectorItems(bus, connectorItem, busConnectedItems); #ifndef QT_NO_DEBUG if (connectorItem->attachedToItemType() == ModelPart::Wire && busConnectedItems.count() != 2) { connectorItem->debugInfo("bus is missing"); //busConnectedItems.clear(); //connectorItem->attachedTo()->busConnectorItems(bus, busConnectedItems); } #endif foreach (ConnectorItem * busConnectedItem, busConnectedItems) { if (!tempItems.contains(busConnectedItem)) { tempItems.append(busConnectedItem); } } } } } void ConnectorItem::collectParts(QList & connectorItems, QList & partsConnectors, bool includeSymbols, ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (connectorItems.count() == 0) return; //DebugDialog::debug("___________________________"); switch (viewLayerPlacement) { case ViewLayer::NewTop: case ViewLayer::NewBottom: case ViewLayer::NewTopAndBottom: break; default: DebugDialog::debug(QString("collect parts unknown spec %1").arg(viewLayerPlacement)); viewLayerPlacement = ViewLayer::NewTopAndBottom; break; } foreach (ConnectorItem * connectorItem, connectorItems) { if (connectorItem->isHybrid()) { continue; } ItemBase * candidate = connectorItem->attachedTo(); switch (candidate->itemType()) { case ModelPart::Symbol: case ModelPart::SchematicSubpart: if (!includeSymbols) break; case ModelPart::Jumper: case ModelPart::Part: case ModelPart::CopperFill: case ModelPart::Board: case ModelPart::ResizableBoard: case ModelPart::Via: collectPart(connectorItem, partsConnectors, viewLayerPlacement); break; default: break; } } } void ConnectorItem::collectPart(ConnectorItem * connectorItem, QList & partsConnectors, ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (partsConnectors.contains(connectorItem)) return; ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem(); if (crossConnectorItem != NULL) { if (partsConnectors.contains(crossConnectorItem)) { return; } if (viewLayerPlacement == ViewLayer::NewTopAndBottom) { partsConnectors.append(crossConnectorItem); /* DebugDialog::debug(QString("collecting both: %1 %2 %3 %4") .arg(crossConnectorItem->attachedToID()) .arg(crossConnectorItem->connectorSharedID()) .arg(crossConnectorItem->attachedToViewLayerID()) .arg((long)crossConnectorItem->attachedTo(), 0, 16) ); */ } else if (viewLayerPlacement == ViewLayer::NewTop) { if (connectorItem->attachedToViewLayerID() == ViewLayer::Copper1) { } else { connectorItem = crossConnectorItem; } } else if (viewLayerPlacement == ViewLayer::NewBottom) { if (connectorItem->attachedToViewLayerID() == ViewLayer::Copper0) { } else { connectorItem = crossConnectorItem; } } } /* DebugDialog::debug(QString("collecting part: %1 %2 %3 %4") .arg(connectorItem->attachedToID()) .arg(connectorItem->connectorSharedID()) .arg(connectorItem->attachedToViewLayerID()) .arg((long) connectorItem->attachedTo(), 0, 16) ); */ partsConnectors.append(connectorItem); } void ConnectorItem::updateTooltip() { if (attachedToItemType() != ModelPart::Wire) { QString name = connectorSharedName(); bool isInt = false; name.toInt(&isInt); QString descr = connectorSharedDescription(); if (name.compare(descr, Qt::CaseInsensitive) == 0) { descr = ""; } else { descr = ":" + descr; } QString id = connectorSharedID(); int ix = IntegerFinder.indexIn(id); if (ix < 0 || isInt) { id = ""; } else { if (attachedTo()->modelPart()->hasZeroConnector()) { id = QString::number(IntegerFinder.cap(0).toInt() + 1); } else { id = IntegerFinder.cap(0); } if (!id.isEmpty()) { id = " (" + id + ")"; } } QString tt = QString("%2%3%1
%4") .arg(id) .arg(name) .arg(descr) .arg(attachedToTitle()); setToolTip(tt); return; } QList connectors; foreach(ConnectorItem * toConnectorItem, m_connectedTo) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) { connectors.append(toConnectorItem); } } if (connectors.count() == 0) { setToolTip(""); return; } QString connections = QString("

"; setToolTip(ItemBase::ITEMBASE_FONT_PREFIX + connections + ItemBase::ITEMBASE_FONT_SUFFIX); } void ConnectorItem::clearConnector() { m_connector = NULL; } bool ConnectorItem::connectionIsAllowed(ConnectorItem * other) { if (!connector()->connectionIsAllowed(other->connector())) return false; if (!m_attachedTo->connectionIsAllowed(other)) return false; foreach (ConnectorItem * toConnectorItem, connectedToItems()) { if (!toConnectorItem->attachedTo()->connectionIsAllowed(other)) { return false; } } return true; } void ConnectorItem::showEqualPotential(bool show, QList & visited) { if (!show) { restoreColor(visited); return; } QBrush brush; QPen pen; m_attachedTo->getEqualPotentialColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect); //DebugDialog::debug(QString("set eqp %1 %2 %3").arg(attachedToID()).arg(pen->width()).arg(pen->color().name())); setColorAux(brush, pen, true); } void ConnectorItem::clearEqualPotentialDisplay() { //DebugDialog::debug(QString("clear eqp3")); QList visited; foreach (ConnectorItem * connectorItem, m_equalPotentialDisplayItems) { connectorItem->restoreColor(visited); } m_equalPotentialDisplayItems.clear(); } bool ConnectorItem::isEverVisible() { return m_attachedTo->isEverVisible(); } bool ConnectorItem::isGrounded(ConnectorItem * c1, ConnectorItem * c2) { QList connectorItems; if (c1 != NULL) { connectorItems.append(c1); } if (c2 != NULL) { connectorItems.append(c2); } collectEqualPotential(connectorItems, true, ViewGeometry::NoFlag); foreach (ConnectorItem * end, connectorItems) { if (end->isGrounded()) return true; } return false; } bool ConnectorItem::isGrounded() { QString name = connectorSharedName(); return ((name.compare("gnd", Qt::CaseInsensitive) == 0) || // (name.compare("-", Qt::CaseInsensitive) == 0) || (name.compare("vss", Qt::CaseInsensitive) == 0) || (name.compare("ground", Qt::CaseInsensitive) == 0) ); } ConnectorItem * ConnectorItem::getCrossLayerConnectorItem() { if (m_connector == NULL) return NULL; if (m_attachedTo == NULL) return NULL; if (m_attachedTo->viewID() != ViewLayer::PCBView) return NULL; ViewLayer::ViewLayerID viewLayerID = attachedToViewLayerID(); if (viewLayerID == ViewLayer::Copper0) { return m_connector->connectorItemByViewLayerID(this->attachedToViewID(), ViewLayer::Copper1); } if (viewLayerID == ViewLayer::Copper1) { return m_connector->connectorItemByViewLayerID(this->attachedToViewID(), ViewLayer::Copper0); } return NULL; } bool ConnectorItem::isInLayers(ViewLayer::ViewLayerPlacement viewLayerPlacement) { return ViewLayer::copperLayers(viewLayerPlacement).contains(attachedToViewLayerID()); } bool ConnectorItem::isCrossLayerConnectorItem(ConnectorItem * candidate) { if (candidate == NULL) return false; ConnectorItem * cross = getCrossLayerConnectorItem(); return cross == candidate; } bool ConnectorItem::isCrossLayerFrom(ConnectorItem * candidate) { return !ViewLayer::canConnect(this->attachedToViewLayerID(), candidate->attachedToViewLayerID()); } bool isGrey(QColor color) { if (qAbs(color.red() - color.green()) > 16) return false; if (qAbs(color.red() - color.blue()) > 16) return false; if (qAbs(color.green() - color.blue()) > 16) return false; if (color.red() < 0x60) return false; if (color.red() > 0xA0) return false; return true; } void ConnectorItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) { if (m_hybrid) return; if (doNotPaint()) return; if (m_legPolygon.count() > 1) { paintLeg(painter); return; } if (m_effectively == EffectivelyUnknown) { if (!m_circular && m_shape.isEmpty()) { if (this->attachedTo()->viewID() == ViewLayer::PCBView) { QRectF r = rect(); if (qAbs(r.width() - r.height()) < 0.01) m_effectively = EffectivelyCircular; else m_effectively = EffectivelyRectangular; } } } NonConnectorItem::paint(painter, option, widget); } void ConnectorItem::paintLeg(QPainter * painter) { QPen lpen = legPen(); painter->setPen(lpen); bool hasCurves = false; foreach (Bezier * bezier, m_legCurves) { if (bezier != NULL && !bezier->isEmpty()) { hasCurves = true; break; } } // draw the leg first paintLeg(painter, hasCurves); if (m_legPolygon.count() > 2) { // draw bendpoint indicators double halfWidth = lpen.widthF() / 2; painter->setPen(Qt::NoPen); QColor c = addColor(m_legColor, (qGray(m_legColor.rgb()) < 64) ? 80 : -64); painter->setBrush(c); for (int i = 1; i < m_legPolygon.count() - 1; i++) { painter->drawEllipse(m_legPolygon.at(i), halfWidth, halfWidth); } painter->setBrush(Qt::NoBrush); } if (m_attachedTo->inHover()) { // hover highlight lpen.setColor((qGray(m_legColor.rgb()) < 48) ? QColor(255, 255, 255) : QColor(0, 0, 0)); painter->setOpacity(ItemBase::HoverOpacity); painter->setPen(lpen); paintLeg(painter, hasCurves); } // now draw the connector Bezier * bezier = m_legCurves.at(m_legCurves.count() - 2); bool connectorIsCurved = (bezier != NULL && !bezier->isEmpty()); Bezier left, right; QPainterPath path; if (connectorIsCurved) { bezier->split(m_connectorDrawT, left, right); path.moveTo(right.endpoint0()); path.cubicTo(right.cp0(), right.cp1(), right.endpoint1()); } if (!isGrey(m_legColor)) { // draw an undercolor so the connectorColor will be visible on top of the leg color lpen.setColor(0x8c8c8c); // TODO: don't hardcode color painter->setOpacity(1); painter->setPen(lpen); if (connectorIsCurved) { painter->drawPath(path); } else { painter->drawLine(m_connectorDrawEnd, m_legPolygon.last()); } } QPen pen = this->pen(); pen.setWidthF(m_legStrokeWidth); pen.setCapStyle(Qt::RoundCap); painter->setOpacity(m_opacity); painter->setPen(pen); if (connectorIsCurved) { painter->drawPath(path); } else { painter->drawLine(m_connectorDrawEnd, m_legPolygon.last()); } } void ConnectorItem::paintLeg(QPainter * painter, bool hasCurves) { if (hasCurves) { for (int i = 0; i < m_legPolygon.count() -1; i++) { Bezier * bezier = m_legCurves.at(i); if (bezier && !bezier->isEmpty()) { QPainterPath path; path.moveTo(m_legPolygon.at(i)); path.cubicTo(bezier->cp0(), bezier->cp1(), m_legPolygon.at(i + 1)); painter->drawPath(path); } else { painter->drawLine(m_legPolygon.at(i), m_legPolygon.at(i + 1)); } } } else { painter->drawPolyline(m_legPolygon); } } ConnectorItem * ConnectorItem::chooseFromSpec(ViewLayer::ViewLayerPlacement viewLayerPlacement) { ConnectorItem * crossConnectorItem = getCrossLayerConnectorItem(); if (crossConnectorItem == NULL) return this; ViewLayer::ViewLayerID basis = ViewLayer::Copper0; switch (viewLayerPlacement) { case ViewLayer::NewTop: basis = ViewLayer::Copper1; break; case ViewLayer::NewBottom: basis = ViewLayer::Copper0; break; default: DebugDialog::debug(QString("unusual viewLayerPlacement %1").arg(viewLayerPlacement)); basis = ViewLayer::Copper0; break; } if (this->attachedToViewLayerID() == basis) { return this; } if (crossConnectorItem->attachedToViewLayerID() == basis) { return crossConnectorItem; } return this; } bool ConnectorItem::connectedToWires() { foreach (ConnectorItem * toConnectorItem, connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { return true; } } ConnectorItem * crossConnectorItem = getCrossLayerConnectorItem(); if (crossConnectorItem == NULL) return false; foreach (ConnectorItem * toConnectorItem, crossConnectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { return true; } } return false; } void ConnectorItem::displayRatsnest(QList & partConnectorItems, ViewGeometry::WireFlags myFlag) { bool formerColorWasNamed = false; bool gotFormerColor = false; QColor formerColor; VirtualWire * vw = NULL; foreach (ConnectorItem * fromConnectorItem, partConnectorItems) { foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { vw = qobject_cast(toConnectorItem->attachedTo()); if (vw != NULL) break; } if (vw != NULL) break; } if (vw != NULL) { formerColorWasNamed = vw->colorWasNamed(); formerColor = vw->color(); gotFormerColor = true; clearRatsnestDisplay(partConnectorItems); } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; if (partConnectorItems.count() < 2) return; QStringList connectorNames; ConnectorItem::collectConnectorNames(partConnectorItems, connectorNames); QColor color; bool colorWasNamed = RatsnestColors::findConnectorColor(connectorNames, color); if (!colorWasNamed) { if (!formerColorWasNamed && gotFormerColor) { color = formerColor; } else { infoGraphicsView->getRatsnestColor(color); } } ConnectorPairHash result; GraphUtils::chooseRatsnestGraph(&partConnectorItems, (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ myFlag, result); foreach (ConnectorItem * key, result.uniqueKeys()) { foreach (ConnectorItem * value, result.values(key)) { VirtualWire * vw = infoGraphicsView->makeOneRatsnestWire(key, value, false, color, false); if (vw) { vw->setColorWasNamed(colorWasNamed); } } } } void ConnectorItem::clearRatsnestDisplay(QList & connectorItems) { QSet ratsnests; foreach (ConnectorItem * fromConnectorItem, connectorItems) { if (fromConnectorItem == NULL) continue; foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { VirtualWire * vw = qobject_cast(toConnectorItem->attachedTo()); if (vw != NULL) { ratsnests.insert(vw); } } } foreach (VirtualWire * vw, ratsnests.values()) { ConnectorItem * c1 = vw->connector0()->firstConnectedToIsh(); if (c1 != NULL) { vw->connector0()->tempRemove(c1, false); c1->tempRemove(vw->connector0(), false); } ConnectorItem * c2 = vw->connector1()->firstConnectedToIsh(); if (c2 != NULL) { vw->connector1()->tempRemove(c2, false); c2->tempRemove(vw->connector1(), false); } //vw->debugInfo("removing rat 1"); vw->scene()->removeItem(vw); delete vw; } } void ConnectorItem::collectConnectorNames(QList & connectorItems, QStringList & connectorNames) { foreach(ConnectorItem * connectorItem, connectorItems) { if (!connectorNames.contains(connectorItem->connectorSharedName())) { connectorNames.append(connectorItem->connectorSharedName()); //DebugDialog::debug("name " + connectorItem->connectorSharedName()); } } } double ConnectorItem::calcClipRadius() { if (m_circular) { return radius() - (strokeWidth() / 2.0); } if (m_effectively == EffectivelyCircular) { double rad = rect().width() / 2; return rad - (rad / 5); } return 0; } bool ConnectorItem::isEffectivelyCircular() { return m_circular || m_effectively == EffectivelyCircular; } void ConnectorItem::debugInfo(const QString & msg) { #ifndef QT_NO_DEBUG QPointF p = sceneAdjustedTerminalPoint(NULL); QString s = QString("%1 cid:%2 cname:%3 title:%4 id:%5 type:%6 inst:%7 vlid:%8 vid:%9 spec:%10 flg:%11 hy:%12 bus:%13 r:%14 sw:%15 pos:(%16 %17)") .arg(msg) .arg(this->connectorSharedID()) .arg(this->connectorSharedName()) .arg(this->attachedToTitle()) .arg(this->attachedToID()) .arg(this->attachedToItemType()) .arg(this->attachedToInstanceTitle()) .arg(this->attachedToViewLayerID()) .arg(this->attachedToViewID()) .arg(this->attachedToViewLayerPlacement()) .arg(this->attachedTo()->wireFlags()) .arg(this->m_hybrid) .arg((long) this->bus(), 0, 16) .arg(this->m_radius) .arg(this->m_strokeWidth) .arg(p.x()) .arg(p.y()) ; //s.replace(" ", "_"); DebugDialog::debug(s); #else Q_UNUSED(msg); #endif } double ConnectorItem::minDimension() { QRectF r = this->boundingRect(); return qMin(r.width(), r.height()); } ConnectorItem * ConnectorItem::findConnectorUnder(bool useTerminalPoint, bool allowAlready, const QList & exclude, bool displayDragTooltip, ConnectorItem * other) { QList items = useTerminalPoint ? this->scene()->items(this->sceneAdjustedTerminalPoint(NULL)) : this->scene()->items(mapToScene(this->rect())); // only wires use rect QList candidates; // for the moment, take the topmost ConnectorItem that doesn't belong to me foreach (QGraphicsItem * item, items) { ConnectorItem * connectorItemUnder = dynamic_cast(item); if (connectorItemUnder == NULL) continue; if (connectorItemUnder->connector() == NULL) continue; // shouldn't happen if (attachedTo()->childItems().contains(connectorItemUnder)) continue; // don't use own connectors if (!this->connectionIsAllowed(connectorItemUnder)) { continue; } if (!allowAlready) { if (connectorItemUnder->connectedToItems().contains(this)) { continue; // already connected } } if (exclude.contains(connectorItemUnder)) continue; candidates.append(connectorItemUnder); } ConnectorItem * candidate = NULL; if (candidates.count() == 1) { candidate = candidates[0]; } else if (candidates.count() > 0) { qSort(candidates.begin(), candidates.end(), wireLessThan); candidate = candidates[0]; } if (m_overConnectorItem != NULL && candidate != m_overConnectorItem) { m_overConnectorItem->connectorHover(NULL, false); } if (candidate != NULL && candidate != m_overConnectorItem) { candidate->connectorHover(NULL, true); } m_overConnectorItem = candidate; if (candidate == NULL) { if (this->connectorHovering()) { this->connectorHover(NULL, false); } } else { if (!this->connectorHovering()) { this->connectorHover(NULL, true); } } if (displayDragTooltip) { displayTooltip(m_overConnectorItem, other); } return m_overConnectorItem; } void ConnectorItem::displayTooltip(ConnectorItem * ci, ConnectorItem * other) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; // Activate tooltip for destination connector. based on a patch submitted by bryant.mairs QString text; if (ci && ci->connectorHovering()) { if (other) { text = QString("%1: %2\n%3: %4") .arg(other->attachedToInstanceTitle()) .arg(other->connectorSharedName()) .arg(ci->attachedToInstanceTitle()) .arg(ci->connectorSharedName()); } else { text = QString("%1: %2").arg(ci->attachedToInstanceTitle()).arg(ci->connectorSharedName()); } } else { if (other) { text = QString("%1: %2") .arg(other->attachedToInstanceTitle()) .arg(other->connectorSharedName()); } } // Now use Qt's tooltip functionality to display our tooltip. // The tooltip text is first cleared as only a change in tooltip // text will update its position. // A rect is generated to smooth out position updates. // NOTE: Increasing this rect will cause the tooltip to disappear // and not reappear until another pixel move after the move that // disabled it. QPoint sp = QCursor::pos(); QToolTip::showText(sp, "", infoGraphicsView); if (!text.isEmpty()) { QPoint q = infoGraphicsView->mapFromGlobal(sp); QRect r(q.x(), q.y(), 1, 1); QToolTip::showText(sp, text, infoGraphicsView, r); } } ConnectorItem * ConnectorItem::releaseDrag() { ConnectorItem * result = m_overConnectorItem; if (m_overConnectorItem != NULL) { m_overConnectorItem->connectorHover(NULL, false); // clean up setOverConnectorItem(NULL); clearConnectorHover(); QList visited; restoreColor(visited); } attachedTo()->clearConnectorHover(); return result; } void ConnectorItem::rotateLeg(const QPolygonF & poly, bool active) { resetLeg(poly, false, active, "rotate"); } void ConnectorItem::resetLeg(const QPolygonF & poly, bool relative, bool active, const QString & why) { if (!m_rubberBandLeg) return; ConnectorItem * target = NULL; foreach (ConnectorItem * connectorItem, this->m_connectedTo) { if (connectorItem->connectorType() == Connector::Female) { target = connectorItem; break; } } if (target == NULL) { setLeg(poly, relative, why); return; } if (!active) { repositionTarget(); return; } if (why.compare("swap") == 0) { setLeg(poly, relative, why); repositionTarget(); return; } //DebugDialog::debug("connectorItem prepareGeometryChange 1"); prepareGeometryChange(); QPointF sceneNewLast = target->sceneAdjustedTerminalPoint(NULL); QPointF sceneOldLast = poly.last(); for (int i = 1; i < m_legPolygon.count(); i++) { m_legPolygon.replace(i, mapFromScene(poly.at(i) - sceneOldLast + sceneNewLast)); } calcConnectorEnd(); update(); } void ConnectorItem::setLeg(const QPolygonF & poly, bool relative, const QString & why) { Q_UNUSED(why); if (!m_rubberBandLeg) return; repoly(poly, relative); update(); } const QPolygonF & ConnectorItem::leg() { static QPolygonF emptyPoly; if (!m_rubberBandLeg) return emptyPoly; return m_legPolygon; } bool ConnectorItem::isDraggingLeg() { return m_draggingLeg; } QString ConnectorItem::makeLegSvg(QPointF offset, double dpi, double printerScale, bool blackOnly) { if (!m_rubberBandLeg) return ""; QString data("M"); QPointF p = m_legPolygon.at(0); data += TextUtils::pointToSvgString(mapToScene(p), offset, dpi, printerScale); for (int i = 1; i < m_legPolygon.count(); i++) { QPointF p = m_legPolygon.at(i); Bezier * bezier = m_legCurves.at(i - 1); if (bezier == NULL || bezier->isEmpty()) { data += "L"; data += TextUtils::pointToSvgString(mapToScene(p), offset, dpi, printerScale); } else { data += "C"; data += TextUtils::pointToSvgString(mapToScene(bezier->cp0()), offset, dpi, printerScale); data += " "; data += TextUtils::pointToSvgString(mapToScene(bezier->cp1()), offset, dpi, printerScale); data += " "; data += TextUtils::pointToSvgString(mapToScene(p), offset, dpi, printerScale); } } QString path = QString("\n") .arg(blackOnly ? "black" : m_legColor.name()) .arg(m_legStrokeWidth * dpi / printerScale) .arg(data); return path; } QPolygonF ConnectorItem::sceneAdjustedLeg() { if (!m_rubberBandLeg) return QPolygonF(); QPolygonF poly; foreach (QPointF p, m_legPolygon) { poly.append(mapToScene(p)); } return poly; } void ConnectorItem::prepareToStretch(bool activeStretch) { m_activeStretch = activeStretch; m_oldPolygon = sceneAdjustedLeg(); } void ConnectorItem::stretchBy(QPointF howMuch) { if (!m_rubberBandLeg) return; Q_UNUSED(howMuch); resetLeg(m_oldPolygon, false, m_activeStretch, "move"); // if update isn't called here then legs repaint, but body doesn't // so you get a weird "headless" effect m_attachedTo->update(); } void ConnectorItem::stretchDone(QPolygonF & oldLeg, QPolygonF & newLeg, bool & active) { oldLeg = m_oldPolygon; newLeg = sceneAdjustedLeg(); active = m_activeStretch; } void ConnectorItem::moveDone(int & index0, QPointF & oldPos0, QPointF & newPos0, int & index1, QPointF & oldPos1, QPointF & newPos1) { index0 = (m_activeStretch) ? 1 : m_legPolygon.count() - 1; oldPos0 = m_oldPolygon.at(index0); newPos0 = mapToScene(m_legPolygon.at(index0)); index1 = m_legPolygon.count() - 1; oldPos1 = m_oldPolygon.at(index1); newPos1 = mapToScene(m_legPolygon.at(index1)); } QRectF ConnectorItem::boundingRect() const { if (m_legPolygon.count() < 2) return NonConnectorItem::boundingRect(); return shape().controlPointRect(); } QPainterPath ConnectorItem::hoverShape() const { return shapeAux(2 * m_legStrokeWidth); } QPainterPath ConnectorItem::shape() const { return shapeAux(m_legStrokeWidth); } QPainterPath ConnectorItem::shapeAux(double width) const { if (m_legPolygon.count() < 2) return NonConnectorItem::shape(); QPainterPath path; path.moveTo(m_legPolygon.at(0)); for (int i = 1; i < m_legPolygon.count(); i++) { Bezier * bezier = m_legCurves.at(i - 1); if (bezier != NULL && !bezier->isEmpty()) { path.cubicTo(bezier->cp0(), bezier->cp1(), m_legPolygon.at(i)); } else { path.lineTo(m_legPolygon.at(i)); } } QPen pen = legPen(); return GraphicsUtils::shapeFromPath(path, pen, width, false); } void ConnectorItem::repositionTarget() { // this connector is connected to another part which is being dragged foreach (ConnectorItem * connectorItem, this->m_connectedTo) { if (connectorItem->connectorType() == Connector::Female) { reposition(connectorItem->sceneAdjustedTerminalPoint(NULL), m_legPolygon.count() - 1); break; } } } void ConnectorItem::reposition(QPointF sceneDestPos, int draggingIndex) { //DebugDialog::debug("connectorItem prepareGeometryChange 2"); prepareGeometryChange(); //foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point b %1 %2").arg(p.x()).arg(p.y())); QPointF dest = mapFromScene(sceneDestPos); m_legPolygon.replace(draggingIndex, dest); //foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point a %1 %2").arg(p.x()).arg(p.y())); calcConnectorEnd(); } void ConnectorItem::repoly(const QPolygonF & poly, bool relative) { //DebugDialog::debug("connectorItem prepareGeometryChange 3"); prepareGeometryChange(); //foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point b %1 %2").arg(p.x()).arg(p.y())); m_legPolygon.clear(); clearCurves(); foreach (QPointF p, poly) { m_legPolygon.append(relative ? p : mapFromScene(p)); m_legCurves.append(NULL); } //foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point a %1 %2").arg(p.x()).arg(p.y())); calcConnectorEnd(); } void ConnectorItem::calcConnectorEnd() { if (m_legPolygon.count() < 2) { m_connectorDrawEnd = m_connectorDetectEnd = QPointF(0,0); return; } QPointF p1 = m_legPolygon.last(); QPointF p0 = m_legPolygon.at(m_legPolygon.count() - 2); double dx = p1.x() - p0.x(); double dy = p1.y() - p0.y(); double lineLen = qSqrt((dx * dx) + (dy * dy)); double drawlen = qMax(0.5, qMin(lineLen, StandardLegConnectorDrawEnabledLength)); double detectlen = qMax(0.5, qMin(lineLen, StandardLegConnectorDetectLength)); Bezier * bezier = m_legCurves.at(m_legCurves.count() - 2); if (bezier == NULL || bezier->isEmpty()) { m_connectorDrawEnd = QPointF(p1 - QPointF(dx * drawlen / lineLen, dy * drawlen / lineLen)); m_connectorDetectEnd = QPointF(p1 - QPointF(dx * detectlen / lineLen, dy * detectlen / lineLen)); return; } bezier->set_endpoints(p0, p1); m_connectorDetectT = m_connectorDrawT = 0; double blen = bezier->computeCubicCurveLength(1.0, 24); if (blen < StandardLegConnectorDetectLength) { return; } m_connectorDetectT = findT(bezier, blen, StandardLegConnectorDetectLength); m_connectorDrawT = findT(bezier, blen, StandardLegConnectorDrawEnabledLength); } double ConnectorItem::findT(Bezier * bezier, double blen, double length) { // use binary search to find a value for t double tmax = 1.0; double tmin = 0; double t = 1.0 - (length / blen); while (true) { double l = bezier->computeCubicCurveLength(t, 24); if (qAbs(blen - length - l) < .0001) { return t; } if (blen - length - l > 0) { // too short tmin = t; t = (t + tmax) / 2; } else { // too long tmax = t; t = (t + tmin) / 2; } } return t; } const QString & ConnectorItem::legID(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { if (m_connector) return m_connector->legID(viewID, viewLayerID); return ___emptyString___; } void ConnectorItem::setRubberBandLeg(QColor color, double strokeWidth, QLineF parentLine) { // assumes this is only called once, when the connector is first set up /* this->debugInfo(QString("set rubber band leg %1 %2 %3 %4") .arg(parentLine.p1().x()) .arg(parentLine.p1().y()) .arg(parentLine.p2().x()) .arg(parentLine.p2().y()) ); */ m_rubberBandLeg = true; setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); setAcceptedMouseButtons(ALLMOUSEBUTTONS); // p1 is always the start point closest to the body. setPos(parentLine.p1()); m_legPolygon.append(QPointF(0,0)); m_legPolygon.append(parentLine.p2() - parentLine.p1()); m_legCurves.append(NULL); m_legCurves.append(NULL); m_legStrokeWidth = strokeWidth; m_legColor = color; reposition(m_attachedTo->mapToScene(parentLine.p2()), 1); this->setCircular(false); } bool ConnectorItem::hasRubberBandLeg() const { return m_rubberBandLeg; } void ConnectorItem::killRubberBandLeg() { // this is a hack; see the caller for explanation prepareGeometryChange(); m_rubberBandLeg = false; m_legPolygon.clear(); clearCurves(); } QPen ConnectorItem::legPen() const { if (!m_rubberBandLeg) return QPen(); QPen pen; pen.setWidthF(m_legStrokeWidth); pen.setColor(m_legColor); pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin); return pen; } bool ConnectorItem::legMousePressEvent(QGraphicsSceneMouseEvent *event) { m_insertBendpointPossible = false; if (attachedTo()->moveLock()) { event->ignore(); return true; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->prepLegSelection(this->attachedTo()); } int bendpointIndex; CursorLocation cursorLocation = findLocation(event->pos(), bendpointIndex); switch (cursorLocation) { case InConnector: if (event->modifiers() & altOrMetaModifier()) return false; m_holdPos = mapToScene(m_legPolygon.last()); m_draggingLeg = true; m_draggingLegIndex = m_legPolygon.count() - 1; m_oldPolygon = m_legPolygon; QGraphicsRectItem::mousePressEvent(event); return true; case InSegment: if (curvyWiresIndicated(event->modifiers())) { m_draggingLegIndex = bendpointIndex - 1; Bezier * bezier = m_legCurves.at(m_draggingLegIndex); if (bezier == NULL) { bezier = new Bezier(); m_legCurves.replace(m_draggingLegIndex, bezier); } UndoBezier.copy(bezier); if (bezier->isEmpty()) { QPointF p0 = m_legPolygon.at(m_draggingLegIndex); QPointF p1 = m_legPolygon.at(m_draggingLegIndex + 1); bezier->initToEnds(p0, p1); } bezier->initControlIndex(event->pos(), m_legStrokeWidth); m_draggingCurve = m_draggingLeg = true; TheBezierDisplay = new BezierDisplay; TheBezierDisplay->initDisplay(this, bezier); return true; } else { m_insertBendpointPossible = true; } // must continue on to InBendpoint case InBendpoint: m_draggingLegIndex = bendpointIndex; m_holdPos = event->scenePos(); m_oldPolygon = m_legPolygon; m_draggingLeg = true; QGraphicsRectItem::mousePressEvent(event); return true; case InOrigin: case InNotFound: default: event->ignore(); return true; } } ConnectorItem::CursorLocation ConnectorItem::findLocation(QPointF location, int & bendpointIndex) { QPainterPath path; Bezier * bezier = m_legCurves.at(m_legCurves.count() - 2); if (bezier == NULL || bezier->isEmpty()) { path.moveTo(m_connectorDetectEnd); path.lineTo(m_legPolygon.last()); } else { Bezier left, right; bezier->split(m_connectorDetectT, left, right); path.moveTo(right.endpoint0()); path.cubicTo(right.cp0(), right.cp1(), right.endpoint1()); } QPen pen = legPen(); path = GraphicsUtils::shapeFromPath(path, pen, m_legStrokeWidth, false); if (path.contains(location)) { return InConnector; } double wSqd = 4 * m_legStrokeWidth * m_legStrokeWidth; // hover distance for (int i = 0; i < m_legPolygon.count() - 1; i++) { QPainterPath path; path.moveTo(m_legPolygon.at(i)); Bezier * bezier = m_legCurves.at(i); if (bezier != NULL && !bezier->isEmpty()) { path.cubicTo(bezier->cp0(), bezier->cp1(), m_legPolygon.at(i + 1)); } else { path.lineTo(m_legPolygon.at(i + 1)); } path = GraphicsUtils::shapeFromPath(path, pen, m_legStrokeWidth, false); if (path.contains(location)) { double d = GraphicsUtils::distanceSqd(m_legPolygon.at(i), location); if (d <= wSqd) { bendpointIndex = i; if (i == 0) { return InOrigin; } return InBendpoint; } else { d = GraphicsUtils::distanceSqd(m_legPolygon.at(i + 1), location); if (d <= wSqd) { bendpointIndex = i + 1; return InBendpoint; } } bendpointIndex = i + 1; return InSegment; } } return InNotFound; } void ConnectorItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { if (m_hidden || m_inactive || m_hybrid) { event->ignore(); return; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setActiveConnectorItem(this); } if ((acceptedMouseButtons() & Qt::RightButton) == 0) { event->ignore(); return; } if (!m_rubberBandLeg) { event->ignore(); return; } int bendpointIndex; CursorLocation cursorLocation = findLocation(event->pos(), bendpointIndex); switch (cursorLocation) { case InSegment: { QMenu menu; QAction * addAction = menu.addAction(tr("Add bendpoint")); addAction->setData(1); Bezier * bezier = m_legCurves.at(bendpointIndex - 1); if (bezier != NULL && !bezier->isEmpty()) { QAction * straightenAction = menu.addAction(tr("Straighten curve")); straightenAction->setData(2); } QAction *selectedAction = menu.exec(event->screenPos()); if (selectedAction) { if (selectedAction->data().toInt() == 1) { insertBendpoint(event->pos(), bendpointIndex); } else if (selectedAction->data().toInt() == 2) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { Bezier newBezier; infoGraphicsView->prepLegCurveChange(this, bendpointIndex - 1,bezier, &newBezier, true); } } } } return; case InBendpoint: if (bendpointIndex < m_legPolygon.count() - 1) { QMenu menu; menu.addAction(tr("Remove bendpoint")); QAction *selectedAction = menu.exec(event->screenPos()); if (selectedAction) { removeBendpoint(bendpointIndex); } return; } default: break; } event->ignore(); return; } void ConnectorItem::insertBendpoint(QPointF p, int bendpointIndex) { prepareGeometryChange(); m_oldPolygon = m_legPolygon; insertBendpointAux(p, bendpointIndex); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->prepLegBendpointChange(this, m_oldPolygon.count(), m_legPolygon.count(), bendpointIndex, p, &UndoBezier, m_legCurves.at(bendpointIndex - 1), m_legCurves.at(bendpointIndex), false); } calcConnectorEnd(); update(); } Bezier * ConnectorItem::insertBendpointAux(QPointF p, int bendpointIndex) { UndoBezier.clear(); m_legPolygon.insert(bendpointIndex, p); m_legCurves.insert(bendpointIndex, NULL); Bezier * bezier = m_legCurves.at(bendpointIndex - 1); if (bezier == NULL || bezier->isEmpty()) return NULL; QPointF p0 = m_legPolygon.at(bendpointIndex - 1); QPointF p1 = m_legPolygon.at(bendpointIndex + 1); bezier->set_endpoints(p0, p1); UndoBezier.copy(bezier); double t = bezier->findSplit(p, m_legStrokeWidth); Bezier left, right; bezier->split(t, left, right); replaceBezier(bendpointIndex - 1, &left); replaceBezier(bendpointIndex, &right); return NULL; } void ConnectorItem::removeBendpoint(int bendpointIndex) { prepareGeometryChange(); Bezier b0, b1; b0.copy(m_legCurves.at(bendpointIndex - 1)); QPointF p0 = m_legPolygon.at(bendpointIndex - 1); QPointF p1 = m_legPolygon.at(bendpointIndex); b0.set_endpoints(p0, p1); b1.copy(m_legCurves.at(bendpointIndex)); p0 = m_legPolygon.at(bendpointIndex); p1 = m_legPolygon.at(bendpointIndex + 1); b0.set_endpoints(p0, p1); m_oldPolygon = m_legPolygon; QPointF p = m_legPolygon.at(bendpointIndex); m_legPolygon.remove(bendpointIndex); Bezier b2 = b0.join(&b1); replaceBezier(bendpointIndex - 1, &b2); Bezier * bezier = m_legCurves.at(bendpointIndex); m_legCurves.remove(bendpointIndex); if (bezier) delete bezier; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->prepLegBendpointChange(this, m_oldPolygon.count(), m_legPolygon.count(), bendpointIndex, p, &b0, &b1, &b2, false); } calcConnectorEnd(); update(); } void ConnectorItem::clearCurves() { foreach (Bezier * bezier, m_legCurves) { if (bezier != NULL) delete bezier; } m_legCurves.clear(); } void ConnectorItem::changeLegCurve(int index, const Bezier *newBezier) { prepareGeometryChange(); replaceBezier(index, newBezier); calcConnectorEnd(); update(); } void ConnectorItem::addLegBendpoint(int index, QPointF p, const Bezier * bezierLeft, const Bezier * bezierRight) { prepareGeometryChange(); m_legPolygon.insert(index, p); m_legCurves.insert(index, NULL); replaceBezier(index - 1, bezierLeft); replaceBezier(index, bezierRight); calcConnectorEnd(); update(); } void ConnectorItem::removeLegBendpoint(int index, const Bezier * newBezier) { prepareGeometryChange(); m_legPolygon.remove(index); Bezier * bezier = m_legCurves.at(index); if (bezier) delete bezier; m_legCurves.remove(index); replaceBezier(index - 1, newBezier); calcConnectorEnd(); update(); } void ConnectorItem::moveLegBendpoint(int index, QPointF p) { m_legPolygon.replace(index, mapFromScene(p)); calcConnectorEnd(); update(); } const QVector & ConnectorItem::beziers() { return m_legCurves; } void ConnectorItem::replaceBezier(int index, const Bezier * newBezier) { Bezier * bezier = m_legCurves.at(index); if (bezier == NULL && newBezier == NULL) { } else if (bezier && newBezier) { bezier->copy(newBezier); } else if (newBezier) { bezier = new Bezier; m_legCurves.replace(index , bezier); bezier->copy(newBezier); } else if (bezier) { bezier->clear(); } } void ConnectorItem::cursorKeyEvent(Qt::KeyboardModifiers modifiers) { if (m_rubberBandLeg) { if (m_draggingLeg) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);; if (infoGraphicsView) { QPoint p = infoGraphicsView->mapFromGlobal(QCursor::pos()); QPointF r = infoGraphicsView->mapToScene(p); // DebugDialog::debug(QString("got key event %1").arg(keyEvent->modifiers())); updateLegCursor(mapFromScene(r), modifiers); } } else if (attachedToItemType() == ModelPart::Wire) { updateWireCursor(modifiers); } } void ConnectorItem::updateWireCursor(Qt::KeyboardModifiers modifiers) { //DebugDialog::debug("uwc"); QCursor cursor = *CursorMaster::BendpointCursor; if (isBendpoint()) { //DebugDialog::debug("uwc bend"); if (modifiers & altOrMetaModifier()) { //DebugDialog::debug("uwc alt"); Wire * wire = qobject_cast(attachedTo()); if (wire != NULL && wire->canChainMultiple()) { //DebugDialog::debug("uwc make wire"); cursor = *CursorMaster::MakeWireCursor; } } } CursorMaster::instance()->addCursor(this, cursor); } void ConnectorItem::updateLegCursor(QPointF p, Qt::KeyboardModifiers modifiers) { int bendpointIndex; CursorLocation cursorLocation = findLocation(p, bendpointIndex); QCursor cursor; switch (cursorLocation) { case InOrigin: cursor = *attachedTo()->getCursor(modifiers); break; case InBendpoint: cursor = *CursorMaster::BendpointCursor; break; case InSegment: cursor = curvyWiresIndicated(modifiers) ? *CursorMaster::MakeCurveCursor : *CursorMaster::NewBendpointCursor; break; case InConnector: cursor = (modifiers & altOrMetaModifier()) ? *CursorMaster::MakeWireCursor : *CursorMaster::BendlegCursor; break; default: cursor = Qt::ArrowCursor; break; } CursorMaster::instance()->addCursor(this, cursor); } bool ConnectorItem::curvyWiresIndicated(Qt::KeyboardModifiers modifiers) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return true; return infoGraphicsView->curvyWiresIndicated(modifiers); } bool ConnectorItem::isBendpoint() { if (connectionsCount() == 0) return false; foreach (ConnectorItem * ci, connectedToItems()) { if (ci->attachedToItemType() != ModelPart::Wire) { return false; } } return true; } void ConnectorItem::setConnectorLocalName(const QString & name) { if (m_connector) { m_connector->setConnectorLocalName(name); } } bool ConnectorItem::isGroundFillSeed() { return m_groundFillSeed; } void ConnectorItem::setGroundFillSeed(bool seed) { m_groundFillSeed = seed; ConnectorItem * cross = this->getCrossLayerConnectorItem(); if (cross) cross->m_groundFillSeed = seed; } ViewGeometry::WireFlags ConnectorItem::getSkipFlags() { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return ViewGeometry::RatsnestFlag; return (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ infoGraphicsView->getTraceFlag(); } fritzing-app-0.9.3b/src/connectors/connectoritem.h000066400000000000000000000231431272363601300222730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef CONNECTORITEM_H #define CONNECTORITEM_H #include "nonconnectoritem.h" #include "connector.h" #include "../utils/cursormaster.h" #include #include class LegItem; class ConnectorItemAction : public QAction { Q_OBJECT public: ConnectorItemAction(QAction *); ConnectorItemAction(const QString & text, QObject * parent); void setConnectorItem(ConnectorItem *); ConnectorItem * connectorItem(); protected: ConnectorItem * m_connectorItem; }; class ConnectorItem : public NonConnectorItem, public CursorKeyListener { Q_OBJECT public: ConnectorItem(Connector *, ItemBase* attachedTo); ~ConnectorItem(); Connector * connector(); void connectorHover(class ItemBase *, bool hovering); bool connectorHovering(); void clearConnectorHover(); void connectTo(ConnectorItem *); int connectionsCount(); void attachedMoved(bool includeRatsnest, QList & already); ConnectorItem * removeConnection(ItemBase *); void removeConnection(ConnectorItem *, bool emitChange); ConnectorItem * firstConnectedToIsh(); void setTerminalPoint(QPointF); QPointF terminalPoint(); QPointF adjustedTerminalPoint(); QPointF sceneAdjustedTerminalPoint(ConnectorItem * anchor); bool connectedTo(ConnectorItem *); const QList< QPointer > & connectedToItems(); void setHidden(bool hidden); void setInactive(bool inactivate); ConnectorItem * overConnectorItem(); void setOverConnectorItem(ConnectorItem *); ViewLayer::ViewLayerID attachedToViewLayerID(); ViewLayer::ViewLayerPlacement attachedToViewLayerPlacement(); ViewLayer::ViewID attachedToViewID(); const QString & connectorSharedID(); const QString & connectorSharedName(); const QString & connectorSharedDescription(); const QString & connectorSharedReplacedby(); class ErcData * connectorSharedErcData(); const QString & busID(); ModelPartShared * modelPartShared(); ModelPart * modelPart(); class Bus * bus(); void tempConnectTo(ConnectorItem * item, bool applyColor); void tempRemove(ConnectorItem * item, bool applyColor); Connector::ConnectorType connectorType(); bool chained(); void saveInstance(QXmlStreamWriter & ); void writeConnector(QXmlStreamWriter & writer, const QString & elementName); bool wiredTo(ConnectorItem *, ViewGeometry::WireFlags skipFlags); void clearConnector(); bool connectionIsAllowed(ConnectorItem * other); void restoreColor(QList & visited); void showEqualPotential(bool show, QList & visited); void setHoverColor(); bool isGrounded(); ConnectorItem * chooseFromSpec(ViewLayer::ViewLayerPlacement); bool connectedToWires(); bool isCrossLayerConnectorItem(ConnectorItem * candidate); bool isCrossLayerFrom(ConnectorItem * candidate); bool isInLayers(ViewLayer::ViewLayerPlacement); ConnectorItem * getCrossLayerConnectorItem(); void displayRatsnest(QList & partsConnectorItems, ViewGeometry::WireFlags myFlag); void clearRatsnestDisplay(QList & connectorItems); double calcClipRadius(); bool isEffectivelyCircular(); void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ); void debugInfo(const QString & msg); double minDimension(); void setHybrid(bool); bool isHybrid(); void setBigDot(bool); bool isBigDot(); ConnectorItem * findConnectorUnder(bool useTerminalPoint, bool allowAlready, const QList & exclude, bool displayDragTooltip, ConnectorItem * other); ConnectorItem * releaseDrag(); // rubberBand leg functions bool isDraggingLeg(); void setRubberBandLeg(QColor color, double strokeWidth, QLineF parentLine); bool hasRubberBandLeg() const; void rotateLeg(const QPolygonF &, bool active); void setLeg(const QPolygonF &, bool relative, const QString & why); void resetLeg(const QPolygonF &, bool relative, bool active, const QString & why); const QPolygonF & leg(); QString makeLegSvg(QPointF offset, double dpi, double printerScale, bool blackOnly); QPolygonF sceneAdjustedLeg(); void prepareToStretch(bool activeStretch); void stretchBy(QPointF howMuch); void stretchDone(QPolygonF & oldLeg, QPolygonF & newLeg, bool & active); void moveDone(int & index0, QPointF & oldPos0, QPointF & newPos0, int & index1, QPointF & oldPos1, QPointF & newPos1); void killRubberBandLeg(); // hack; see caller QRectF boundingRect() const; const QString & legID(ViewLayer::ViewID, ViewLayer::ViewLayerID); QPainterPath shape() const; QPainterPath hoverShape() const; void changeLegCurve(int index, const class Bezier *); void addLegBendpoint(int index, QPointF, const class Bezier *, const class Bezier *); void removeLegBendpoint(int index, const class Bezier *); void moveLegBendpoint(int index, QPointF); const QVector & beziers(); bool isBendpoint(); void cursorKeyEvent(Qt::KeyboardModifiers modifiers); void setConnectorLocalName(const QString & name); void updateTooltip(); bool isGroundFillSeed(); void setGroundFillSeed(bool); protected: void hoverEnterEvent( QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent( QGraphicsSceneHoverEvent * event ); void hoverMoveEvent( QGraphicsSceneHoverEvent * event ); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void setNormalColor(); void setConnectedColor(); void setUnconnectedColor(); void setColorAux(const QBrush & brush, const QPen & pen, bool paint); void setColorAux(const QColor &color, bool paint=true); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); void writeOtherElements(QXmlStreamWriter & writer); static class Wire * directlyWiredToAux(ConnectorItem * source, ConnectorItem * target, ViewGeometry::WireFlags flags, QList & visited); bool isEverVisible(); void setHiddenOrInactive(); bool isConnectedToPart(); void displayTooltip(ConnectorItem * over, ConnectorItem * other); void reposition(QPointF sceneDestPos, int draggingIndex); void repositionTarget(); void calcConnectorEnd(); QPen legPen() const; bool legMousePressEvent(QGraphicsSceneMouseEvent *event); void repoly(const QPolygonF & poly, bool relative); QPainterPath shapeAux(double width) const; ViewGeometry::WireFlags getSkipFlags(); enum CursorLocation { InNotFound = 0, InConnector, InBendpoint, InOrigin, InSegment }; CursorLocation findLocation(QPointF, int & bendpointIndex); void insertBendpoint(QPointF pos, int bendpointIndex); Bezier * insertBendpointAux(QPointF p, int bendpointIndex); void removeBendpoint(int bendpointIndex); void clearCurves(); void paintLeg(QPainter * painter); void paintLeg(QPainter * painter, bool hasCurves); void replaceBezier(int index, const Bezier * newBezier); void updateLegCursor(QPointF p, Qt::KeyboardModifiers modifiers); void updateWireCursor(Qt::KeyboardModifiers modifiers); bool curvyWiresIndicated(Qt::KeyboardModifiers); double findT(Bezier * bezier, double blen, double length); protected: QPointer m_connector; QList< QPointer > m_connectedTo; QPointF m_terminalPoint; QPointer m_overConnectorItem; bool m_connectorHovering; bool m_spaceBarWasPressed; bool m_hoverEnterSpaceBarWasPressed; bool m_hybrid; bool m_bigDot; bool m_rubberBandLeg; QPolygonF m_oldPolygon; bool m_draggingLeg; bool m_draggingCurve; int m_draggingLegIndex; bool m_activeStretch; QPointF m_holdPos; QPolygonF m_legPolygon; QVector m_legCurves; double m_legStrokeWidth; QColor m_legColor; bool m_insertBendpointPossible; QPointF m_connectorDetectEnd; QPointF m_connectorDrawEnd; double m_connectorDrawT; double m_connectorDetectT; bool m_groundFillSeed; int m_moveCount; protected: static QList m_equalPotentialDisplayItems; protected: static void collectPart(ConnectorItem * connectorItem, QList & partsConnectors, ViewLayer::ViewLayerPlacement); public: static void collectEqualPotential(QList & connectorItems, bool crossLayers, ViewGeometry::WireFlags skipFlags); static void collectParts(QList & connectorItems, QList & partsConnectors, bool includeSymbols, ViewLayer::ViewLayerPlacement); static void clearEqualPotentialDisplay(); static bool isGrounded(ConnectorItem * c1, ConnectorItem * c2); static void collectConnectorNames(QList & connectorItems, QStringList & connectorNames); static class Wire * directlyWiredTo(ConnectorItem * source, ConnectorItem * target, ViewGeometry::WireFlags flags); public: static const QList emptyConnectorItemList; }; Q_DECLARE_METATYPE(ConnectorItem*); #endif fritzing-app-0.9.3b/src/connectors/connectorshared.cpp000066400000000000000000000160001272363601300231300ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #include "connectorshared.h" #include "../debugdialog.h" #include "connector.h" #include "busshared.h" #include "ercdata.h" #include ConnectorShared::ConnectorShared() { m_id = ""; m_name = ""; m_typeString = ""; m_type = Connector::Unknown; m_description = ""; m_bus = NULL; m_ercData = NULL; } ConnectorShared::ConnectorShared( const QDomElement & domElement ) { m_ercData = NULL; m_id = domElement.attribute("id", ""); m_name = domElement.attribute("name", ""); m_replacedby = domElement.attribute("replacedby", ""); //DebugDialog::debug(QString("\tname:%1 id:%2").arg(m_name).arg(m_id)); m_typeString = domElement.attribute("type", ""); m_type = Connector::connectorTypeFromName(m_typeString); m_description = domElement.firstChildElement("description").text(); QDomElement erc = domElement.firstChildElement("erc"); if (!erc.isNull()) { m_ercData = new ErcData(erc); } loadPins(domElement); if (m_type == Connector::Unknown) { foreach (SvgIdLayer * svgIdLayer, m_pins.values()) { if (svgIdLayer->m_svgId.endsWith("pad")) { m_type = Connector::Pad; m_typeString = Connector::connectorNameFromType(m_type); break; } } } m_bus = NULL; } ConnectorShared::~ConnectorShared() { foreach (SvgIdLayer * svgIdLayer, m_pins.values()) { delete svgIdLayer; } m_pins.clear(); if (m_ercData) { delete m_ercData; } } const QString & ConnectorShared::id() const { return m_id; } void ConnectorShared::setId(QString id) { m_id = id; } const QString & ConnectorShared::description() { return m_description; } void ConnectorShared::setDescription(QString description) { m_description = description; } const QString & ConnectorShared::sharedName() { return m_name; } void ConnectorShared::setSharedName(QString name) { m_name = name; } Connector::ConnectorType ConnectorShared::connectorType() { return m_type; } const QString & ConnectorShared::connectorTypeString() { return m_typeString; } void ConnectorShared::setConnectorType(QString type) { m_typeString = type; m_type = Connector::connectorTypeFromName(type); } void ConnectorShared::setConnectorType(Connector::ConnectorType type) { m_typeString = Connector::connectorNameFromType(type); m_type = type; } const QString & ConnectorShared::replacedby() const { return m_replacedby; } void ConnectorShared::setReplacedby(QString replacedby) { m_replacedby = replacedby; } const QMultiHash & ConnectorShared::pins() { return m_pins; } void ConnectorShared::insertPin(ViewLayer::ViewID layer, SvgIdLayer * svgIdLayer) { m_pins.insert(layer, svgIdLayer); } void ConnectorShared::addPin(ViewLayer::ViewID viewID, const QString & svgId, ViewLayer::ViewLayerID viewLayerID, const QString & terminalId, const QString & legId, bool hybrid) { SvgIdLayer * svgIdLayer = new SvgIdLayer(viewID); svgIdLayer->m_svgViewLayerID = viewLayerID; svgIdLayer->m_svgId = svgId; svgIdLayer->m_terminalId = terminalId; svgIdLayer->m_hybrid = hybrid; svgIdLayer->m_legId = legId; m_pins.insert(viewID, svgIdLayer); // DebugDialog::debug(QString("insert a %1 %2 %3").arg(layer).arg(connectorId).arg(viewLayerID)); } void ConnectorShared::removePins(ViewLayer::ViewID layer) { m_pins.remove(layer); if (m_pins.values(layer).size() != 0) { throw "ConnectorShared::removePins"; } } void ConnectorShared::removePin(ViewLayer::ViewID layer, SvgIdLayer * svgIdLayer) { m_pins.remove(layer, svgIdLayer); } SvgIdLayer * ConnectorShared::fullPinInfo(ViewLayer::ViewID viewId, ViewLayer::ViewLayerID viewLayerID) { QList svgLayers = m_pins.values(viewId); foreach ( SvgIdLayer * svgIdLayer, svgLayers) { if (svgIdLayer->m_svgViewLayerID == viewLayerID) { return svgIdLayer; } } return NULL; } const QString & ConnectorShared::legID(ViewLayer::ViewID viewId, ViewLayer::ViewLayerID viewLayerID) { QList svgLayers = m_pins.values(viewId); foreach ( SvgIdLayer * svgIdLayer, svgLayers) { if (svgIdLayer->m_svgViewLayerID == viewLayerID) { return svgIdLayer->m_legId; } } return ___emptyString___; } void ConnectorShared::loadPins(const QDomElement & domElement) { //if(m_domElement == NULL) return; // TODO: this is view-related stuff and it would be nice if the model didn't know about it QDomElement viewsTag = domElement.firstChildElement("views"); loadPin(viewsTag.firstChildElement("breadboardView"),ViewLayer::BreadboardView); loadPin(viewsTag.firstChildElement("schematicView"),ViewLayer::SchematicView); loadPin(viewsTag.firstChildElement("pcbView"),ViewLayer::PCBView); } void ConnectorShared::loadPin(QDomElement elem, ViewLayer::ViewID viewID) { QDomElement pinElem = elem.firstChildElement("p"); while (!pinElem.isNull()) { //QString temp; //QTextStream stream(&temp); //pinElem.save(stream, 0); //stream.flush(); QString svgId = pinElem.attribute("svgId"); //svgId = svgId.left(svgId.lastIndexOf(QRegExp("\\d"))+1); QString layer = pinElem.attribute("layer"); SvgIdLayer * svgIdLayer = new SvgIdLayer(viewID); svgIdLayer->m_hybrid = (pinElem.attribute("hybrid").compare("yes") == 0); svgIdLayer->m_legId = pinElem.attribute("legId"); svgIdLayer->m_svgId = svgId; svgIdLayer->m_svgViewLayerID = ViewLayer::viewLayerIDFromXmlString(layer); //DebugDialog::debug(QString("svg id view layer id %1, %2").arg(svgIdLayer->m_viewLayerID).arg(layer)); svgIdLayer->m_terminalId = pinElem.attribute("terminalId"); //if (!svgIdLayer->m_terminalId.isEmpty()) { // DebugDialog::debug("terminalid " + svgIdLayer->m_terminalId); //} m_pins.insert(viewID, svgIdLayer); //DebugDialog::debug(QString("insert b %1 %2 %3").arg(viewId).arg(svgId).arg(layer)); pinElem = pinElem.nextSiblingElement("p"); } } void ConnectorShared::setBus(BusShared * bus) { m_bus = bus; } BusShared * ConnectorShared::bus() { return m_bus; } const QString & ConnectorShared::busID() { if (m_bus == NULL) return ___emptyString___; return m_bus->id(); } ErcData * ConnectorShared::ercData() { return m_ercData; } const QList ConnectorShared::svgIdLayers() const { return m_pins.values(); } fritzing-app-0.9.3b/src/connectors/connectorshared.h000066400000000000000000000056211272363601300226040ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #ifndef CONNECTORSHARED_H #define CONNECTORSHARED_H #include #include #include #include "../viewlayer.h" #include "connector.h" #include "svgidlayer.h" class ConnectorShared : public QObject { Q_OBJECT public: ConnectorShared(); ConnectorShared(const QDomElement & domElement); ~ConnectorShared(); const QString & id() const; void setId(QString id); const QString & description(); void setDescription(QString description); const QString & sharedName(); void setSharedName(QString name); const QString & connectorTypeString(); void setConnectorType(QString type); void setConnectorType(Connector::ConnectorType); Connector::ConnectorType connectorType(); const QString & replacedby() const; void setReplacedby(QString); const QString & legID(ViewLayer::ViewID viewId, ViewLayer::ViewLayerID viewLayerID); const QMultiHash &pins(); SvgIdLayer * fullPinInfo(ViewLayer::ViewID viewId, ViewLayer::ViewLayerID viewLayerID); const QList svgIdLayers() const; void addPin(ViewLayer::ViewID, const QString & svgId, ViewLayer::ViewLayerID, const QString & terminalId, const QString & legId, bool hybrid); void insertPin(ViewLayer::ViewID layer, SvgIdLayer * svgIdLayer); void removePins(ViewLayer::ViewID layer); void removePin(ViewLayer::ViewID layer, SvgIdLayer * svgIdLayer); class BusShared * bus(); void setBus(class BusShared *); const QString & busID(); class ErcData * ercData(); protected: void loadPins(const QDomElement & domElement); void loadPin(QDomElement elem, ViewLayer::ViewID viewId); QString m_description; QString m_id; QString m_name; QString m_typeString; QString m_replacedby; Connector::ConnectorType m_type; QString m_ercType; class ErcData * m_ercData; class BusShared * m_bus; QMultiHash m_pins; }; static QList ___emptyConnectorSharedList___; #endif fritzing-app-0.9.3b/src/connectors/ercdata.cpp000066400000000000000000000116151272363601300213610ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "ercdata.h" ValidReal::ValidReal() { m_ok = false; } bool ValidReal::isValid() { return m_ok; } double ValidReal::value() { return m_value; } void ValidReal::setValue(double v) { m_value = v; m_ok = true; } bool ValidReal::setValue(const QString & v) { m_value = v.toDouble(&m_ok); return m_ok; } ////////////////////////////////////////////////////////// ErcData::ErcData(const QDomElement & ercElement) { m_eType = UnknownEType; m_currentFlow = UnknownFlow; m_ignore = Never; QString eType = ercElement.attribute("etype"); if (eType.compare("VCC", Qt::CaseInsensitive) == 0) { m_eType = VCC; } else if (eType.compare("ground", Qt::CaseInsensitive) == 0) { m_eType = Ground; } QString ig = ercElement.attribute("ignore"); if (ig.compare("ifUnconnected", Qt::CaseInsensitive) == 0) { m_ignore = IfUnconnected; } else if (ig.compare("always", Qt::CaseInsensitive) == 0) { m_ignore = Always; } QDomElement ercChild = ercElement.firstChildElement(); while (!ercChild.isNull()) { QString nodeName = ercChild.nodeName(); if (nodeName.compare("voltage", Qt::CaseInsensitive) == 0) { readVoltage(ercChild); } else if (nodeName.compare("current", Qt::CaseInsensitive) == 0) { readCurrent(ercChild); } ercChild = ercChild.nextSiblingElement(); } } void ErcData::readCurrent(QDomElement & currentElement) { m_currentMin.setValue(currentElement.attribute("valueMin")); m_currentMax.setValue(currentElement.attribute("valueMax")); m_current.setValue(currentElement.attribute("value")); QString flow = currentElement.attribute("flow"); if (flow.compare("source", Qt::CaseInsensitive) == 0) { m_currentFlow = Source; } else if (flow.compare("sink", Qt::CaseInsensitive) == 0) { m_currentFlow = Sink; } else { m_currentFlow = UnknownFlow; } } void ErcData::readVoltage(QDomElement & voltageElement) { m_voltageMin.setValue(voltageElement.attribute("valueMin")); m_voltageMax.setValue(voltageElement.attribute("valueMax")); m_voltage.setValue(voltageElement.attribute("value")); } bool ErcData::writeToElement(QDomElement & ercElement, QDomDocument & doc) { switch (m_eType) { case Ground: ercElement.setAttribute("etype", "ground"); writeCurrent(ercElement, doc); break; case VCC: ercElement.setAttribute("etype", "VCC"); writeCurrent(ercElement, doc); writeVoltage(ercElement, doc); break; default: return false; } return true; } void ErcData::writeCurrent(QDomElement & parent, QDomDocument & doc) { if (m_current.isValid() || m_currentMin.isValid() || m_currentMax.isValid() || m_currentFlow != UnknownFlow) { QDomElement currentElement = doc.createElement("current"); parent.appendChild(currentElement); if (m_current.isValid()) { currentElement.setAttribute("value", QString::number(m_current.value())); } if (m_currentMin.isValid()) { currentElement.setAttribute("valueMin", QString::number(m_currentMin.value())); } if (m_currentMax.isValid()) { currentElement.setAttribute("valueMax", QString::number(m_currentMax.value())); } switch (m_currentFlow) { case Source: currentElement.setAttribute("flow", "source"); break; case Sink: currentElement.setAttribute("flow", "sink"); break; default: break; } } } void ErcData::writeVoltage(QDomElement & parent, QDomDocument & doc) { if (m_voltage.isValid() || m_voltageMin.isValid() || m_voltageMax.isValid()) { QDomElement voltageElement = doc.createElement("voltage"); parent.appendChild(voltageElement); if (m_voltage.isValid()) { voltageElement.setAttribute("value", QString::number(m_voltage.value())); } if (m_voltageMin.isValid()) { voltageElement.setAttribute("valueMin", QString::number(m_voltageMin.value())); } if (m_voltageMax.isValid()) { voltageElement.setAttribute("valueMax", QString::number(m_voltageMax.value())); } } } ErcData::EType ErcData::eType() { return m_eType; } ErcData::Ignore ErcData::ignore() { return m_ignore; } fritzing-app-0.9.3b/src/connectors/ercdata.h000066400000000000000000000040001272363601300210140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ERCDATA_H #define ERCDATA_H #include #include #include #include class ValidReal { public: ValidReal(); bool isValid(); double value(); void setValue(double); bool setValue(const QString &); protected: bool m_ok; double m_value; }; class ErcData { public: enum EType { Ground, VCC, UnknownEType }; enum CurrentFlow { Source, Sink, UnknownFlow }; enum Ignore { Never, Always, IfUnconnected }; public: ErcData(const QDomElement & ercElement); bool writeToElement(QDomElement & ercElement, QDomDocument & doc); EType eType(); Ignore ignore(); protected: void readVoltage(QDomElement &); void readCurrent(QDomElement &); void writeVoltage(QDomElement &, QDomDocument &); void writeCurrent(QDomElement &, QDomDocument &); protected: EType m_eType; Ignore m_ignore; ValidReal m_voltage; ValidReal m_voltageMin; ValidReal m_voltageMax; ValidReal m_current; ValidReal m_currentMin; ValidReal m_currentMax; CurrentFlow m_currentFlow; }; #endif fritzing-app-0.9.3b/src/connectors/nonconnectoritem.cpp000066400000000000000000000136601272363601300233440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "nonconnectoritem.h" #include #include #include #include #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "../utils/graphicsutils.h" #include "../model/modelpart.h" //static const double EffectiveAdjustment = 1.25; static const double EffectiveAdjustmentFactor = 5.0 / 15.0; ///////////////////////////////////////////////////////// NonConnectorItem::NonConnectorItem(ItemBase * attachedTo) : QGraphicsRectItem(attachedTo) { m_effectively = EffectivelyUnknown; m_radius = m_strokeWidth = 0; m_layerHidden = m_isPath = m_inactive = m_hidden = false; m_attachedTo = attachedTo; setAcceptHoverEvents(false); setAcceptedMouseButtons(Qt::NoButton); setFlag(QGraphicsItem::ItemIsMovable, false); setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsFocusable, false); } NonConnectorItem::~NonConnectorItem() { } ItemBase * NonConnectorItem::attachedTo() { return m_attachedTo; } bool NonConnectorItem::doNotPaint() { return (m_hidden || m_inactive || !m_paint || m_layerHidden); } void NonConnectorItem::paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget ) { if (doNotPaint()) return; painter->setOpacity(m_opacity); /* DebugDialog::debug(QString("id:%1 %2 w:%3 %4 c:%5 ec:%6 er:%7 neg:%8 w:%9") .arg(attachedToID()) .arg(attachedToTitle()) .arg(pen().width()) .arg(pen().color().name()) .arg(m_circular) .arg(m_effectivelyCircular) .arg(m_effectivelyRectangular) .arg(m_negativePenWidth) .arg(rect().width()) ); */ if (m_circular) { painter->setBrush(brush()); if (m_negativePenWidth < 0) { // for wires painter->setPen(Qt::NoPen); if (!m_negativeOffsetRect) { painter->drawEllipse(rect().center(), m_negativePenWidth, m_negativePenWidth); } else { int pw = m_negativePenWidth + 1; painter->drawEllipse(rect().adjusted(-pw, -pw, pw, pw)); } } else { // for parts QRectF r = rect(); if (r.width() > 0 && r.height() > 0) { double delta = .66 * m_strokeWidth; painter->setPen(pen()); painter->drawEllipse(r.adjusted(delta, delta, -delta, -delta)); } } } else if (!m_shape.isEmpty()) { painter->setBrush(brush()); painter->setPen(pen()); painter->drawPath(m_shape); } else if (m_effectively == EffectivelyCircular) { QRectF r = rect(); if (r.width() > 0 && r.height() > 0) { painter->setBrush(brush()); painter->setPen(pen()); double delta = r.width() * EffectiveAdjustmentFactor; painter->drawEllipse(r.adjusted(delta, delta, -delta, -delta)); } } else if (m_effectively == EffectivelyRectangular) { QRectF r = rect(); if (r.width() > 0 && r.height() > 0) { painter->setBrush(brush()); painter->setPen(pen()); double delta = qMin(r.width(), r.height()) * EffectiveAdjustmentFactor; painter->drawRect(r.adjusted(delta, delta, -delta, -delta)); } } else { QGraphicsRectItem::paint(painter, option, widget); } } void NonConnectorItem::setHidden(bool hide) { m_hidden = hide; this->update(); } bool NonConnectorItem::hidden() { return m_hidden; } void NonConnectorItem::setLayerHidden(bool hide) { m_layerHidden = hide; this->update(); } bool NonConnectorItem::layerHidden() { return m_layerHidden; } void NonConnectorItem::setInactive(bool inactivate) { m_inactive = inactivate; this->update(); } bool NonConnectorItem::inactive() { return m_inactive; } long NonConnectorItem::attachedToID() { if (attachedTo() == NULL) return -1; return attachedTo()->id(); } const QString & NonConnectorItem::attachedToTitle() { if (attachedTo() == NULL) return ___emptyString___; return attachedTo()->title(); } const QString & NonConnectorItem::attachedToInstanceTitle() { if (attachedTo() == NULL) return ___emptyString___; return attachedTo()->instanceTitle(); } void NonConnectorItem::setCircular(bool circular) { m_circular = circular; } void NonConnectorItem::setRadius(double radius, double strokeWidth) { m_radius = radius; m_strokeWidth = strokeWidth; m_circular = (m_radius > 0); } void NonConnectorItem::setIsPath(bool path) { m_isPath = path; } bool NonConnectorItem::isPath() { return m_isPath; } double NonConnectorItem::radius() { return m_radius; } double NonConnectorItem::strokeWidth() { return m_strokeWidth; } QPainterPath NonConnectorItem::shape() const { if (m_circular || m_effectively == EffectivelyCircular) { QPainterPath path; path.addEllipse(rect()); return GraphicsUtils::shapeFromPath(path, pen(), pen().widthF(), true); } else if (!m_shape.isEmpty()) { return m_shape; } return QGraphicsRectItem::shape(); } void NonConnectorItem::setShape(QPainterPath & pp) { // so far only used by GroundPlane m_shape = GraphicsUtils::shapeFromPath(pp, pen(), pen().widthF(), true); } int NonConnectorItem::attachedToItemType() { if (m_attachedTo == NULL) return ModelPart::Unknown; return m_attachedTo->itemType(); } fritzing-app-0.9.3b/src/connectors/nonconnectoritem.h000066400000000000000000000047521272363601300230130ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef NONCONNECTORITEM_H #define NONCONNECTORITEM_H #include #include #include #include #include #include #include "../items/itembase.h" class NonConnectorItem : public QObject, public QGraphicsRectItem { Q_OBJECT public: NonConnectorItem(ItemBase* attachedTo); ~NonConnectorItem(); ItemBase * attachedTo(); virtual void setHidden(bool hidden); bool hidden(); virtual void setInactive(bool inactivate); bool inactive(); virtual void setLayerHidden(bool hidden); bool layerHidden(); long attachedToID(); const QString & attachedToTitle(); const QString & attachedToInstanceTitle(); void setCircular(bool); void setRadius(double radius, double strokeWidth); double radius(); double strokeWidth(); void setShape(QPainterPath &); void paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ); QPainterPath shape() const; void setIsPath(bool); bool isPath(); int attachedToItemType(); protected: bool doNotPaint(); enum Effectively { EffectivelyCircular = 1, EffectivelyRectangular, EffectivelyPolygonal, EffectivelyUnknown }; protected: QPointer m_attachedTo; bool m_hidden; bool m_layerHidden; bool m_inactive; bool m_paint; double m_opacity; bool m_circular; Effectively m_effectively; double m_radius; double m_strokeWidth; double m_negativePenWidth; bool m_negativeOffsetRect; QPainterPath m_shape; bool m_isPath; }; #endif fritzing-app-0.9.3b/src/connectors/svgidlayer.cpp000066400000000000000000000060771272363601300221350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "svgidlayer.h" SvgIdLayer::SvgIdLayer(ViewLayer::ViewID viewID) { m_viewID = viewID; m_pointRectBottom.processed = m_pointRectTop.processed = m_path = m_hybrid = false; m_radius = m_strokeWidth = 0; } SvgIdLayer * SvgIdLayer::copyLayer() { SvgIdLayer * toSvgIdLayer = new SvgIdLayer(m_viewID); *toSvgIdLayer = *this; return toSvgIdLayer; } QPointF SvgIdLayer::point(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewBottom) return m_pointRectBottom.point; return m_pointRectTop.point; } QRectF SvgIdLayer::rect(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewBottom) return m_pointRectBottom.rect; return m_pointRectTop.rect; } bool SvgIdLayer::processed(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewBottom) return m_pointRectBottom.processed; return m_pointRectTop.processed; } bool SvgIdLayer::svgVisible(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewBottom) return m_pointRectBottom.svgVisible; return m_pointRectTop.svgVisible; } void SvgIdLayer::unprocess() { m_pointRectTop.processed = m_pointRectBottom.processed = false; } void SvgIdLayer::setInvisible(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewBottom) { m_pointRectBottom.svgVisible = false; m_pointRectBottom.processed = true; return; } m_pointRectTop.svgVisible = false; m_pointRectTop.processed = true; } void SvgIdLayer::setPointRect(ViewLayer::ViewLayerPlacement viewLayerPlacement, QPointF point, QRectF rect, bool svgVisible) { if (viewLayerPlacement == ViewLayer::NewBottom) { m_pointRectBottom.svgVisible = svgVisible; m_pointRectBottom.processed = true; m_pointRectBottom.point = point; m_pointRectBottom.rect = rect; return; } m_pointRectTop.svgVisible = svgVisible; m_pointRectTop.processed = true; m_pointRectTop.point = point; m_pointRectTop.rect = rect; } fritzing-app-0.9.3b/src/connectors/svgidlayer.h000066400000000000000000000037451272363601300216010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef SVGIDLAYER_H #define SVGIDLAYER_H #include #include "../viewlayer.h" struct PointRect { QRectF rect; QPointF point; bool processed; bool svgVisible; }; class SvgIdLayer { public: SvgIdLayer(ViewLayer::ViewID); SvgIdLayer * copyLayer(); QPointF point(ViewLayer::ViewLayerPlacement); QRectF rect(ViewLayer::ViewLayerPlacement); bool processed(ViewLayer::ViewLayerPlacement); bool svgVisible(ViewLayer::ViewLayerPlacement); void unprocess(); void setInvisible(ViewLayer::ViewLayerPlacement); void setPointRect(ViewLayer::ViewLayerPlacement, QPointF, QRectF, bool svgVisible); public: QString m_svgId; QString m_terminalId; ViewLayer::ViewID m_viewID; ViewLayer::ViewLayerID m_svgViewLayerID; bool m_hybrid; double m_radius; double m_strokeWidth; QString m_legId; QString m_legColor; double m_legStrokeWidth; QLineF m_legLine; bool m_path; protected: PointRect m_pointRectBottom; PointRect m_pointRectTop; protected: }; #endif fritzing-app-0.9.3b/src/debugdialog.cpp000066400000000000000000000127611272363601300200520ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include "debugdialog.h" #include "utils/folderutils.h" #include #include #include #include #include #include #include DebugDialog* DebugDialog::singleton = NULL; QFile DebugDialog::m_file; #ifdef QT_NO_DEBUG bool DebugDialog::m_enabled = false; #else bool DebugDialog::m_enabled = true; #endif QEvent::Type DebugEventType = (QEvent::Type) (QEvent::User + 1); class DebugEvent : public QEvent { public: QString m_message; QObject * m_ancestor; DebugDialog::DebugLevel m_debugLevel; DebugEvent(QString message, DebugDialog::DebugLevel debugLevel, QObject * ancestor) : QEvent(DebugEventType) { this->m_message = message; this->m_ancestor = ancestor; this->m_debugLevel = debugLevel; } }; DebugDialog::DebugDialog(QWidget *parent) : QDialog(parent) { // Let's set the icon this->setWindowIcon(QIcon(QPixmap(":resources/images/fritzing_icon.png"))); singleton = this; m_debugLevel = DebugDialog::Debug; setWindowTitle(tr("for debugging")); resize(400, 300); m_textEdit = new QTextEdit(this); m_textEdit->setGeometry(QRect(10, 10, 381, 281)); QString path = FolderUtils::getTopLevelUserDataStorePath(); path += "/debug.txt"; m_file.setFileName(path); m_file.remove(); } DebugDialog::~DebugDialog() { if (m_textEdit) { delete m_textEdit; } } bool DebugDialog::event(QEvent *e) { if (e->type() == DebugEventType) { this->m_textEdit->append(((DebugEvent *) e)->m_message); emit debugBroadcast(((DebugEvent *) e)->m_message, ((DebugEvent *) e)->m_debugLevel,((DebugEvent *) e)->m_ancestor); // need to delete these events at some point... // but it's tricky if the message is being used elsewhere return true; } else { return QDialog::event(e); } } void DebugDialog::resizeEvent(QResizeEvent *e) { int w = this->width(); int h = this->height(); QRect geom = this->m_textEdit->geometry(); geom.setWidth(w - geom.left() - geom.left()); geom.setHeight( h - geom.top() - geom.top()); this->m_textEdit->setGeometry(geom); return QDialog::resizeEvent(e); } void DebugDialog::debug(QString prefix, const QPointF &point, DebugLevel debug, QObject *ancestor) { QString msg = prefix+QString(" point: x=%1 y=%2").arg(point.x()).arg(point.y()); DebugDialog::debug(msg,debug,ancestor); } void DebugDialog::debug(QString prefix, const QRectF &rect, DebugLevel debug, QObject *ancestor) { QString msg = prefix+QString(" rect: x=%1 y=%2 w=%3 h=%4") .arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); DebugDialog::debug(msg,debug,ancestor); } void DebugDialog::debug(QString prefix, const QPoint &point, DebugLevel debug, QObject *ancestor) { QString msg = prefix+QString(" point: x=%1 y=%2").arg(point.x()).arg(point.y()); DebugDialog::debug(msg,debug,ancestor); } void DebugDialog::debug(QString prefix, const QRect &rect, DebugLevel debug, QObject *ancestor) { QString msg = prefix+QString(" rect: x=%1 y=%2 w=%3 h=%4") .arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height()); DebugDialog::debug(msg,debug,ancestor); } void DebugDialog::debug(QString message, DebugLevel debugLevel, QObject * ancestor) { if (!m_enabled) return; if (singleton == NULL) { new DebugDialog(); //singleton->show(); } if (debugLevel < singleton->m_debugLevel) { return; } qDebug() << message; if (m_file.open(QIODevice::Append | QIODevice::Text)) { QTextStream out(&m_file); out.setCodec("UTF-8"); out << message << "\n"; m_file.close(); } DebugEvent* de = new DebugEvent(message, debugLevel, ancestor); QCoreApplication::postEvent(singleton, de); } void DebugDialog::hideDebug() { if (singleton != NULL) { singleton->hide(); } } void DebugDialog::showDebug() { if (singleton == NULL) { new DebugDialog(); } singleton->show(); } void DebugDialog::closeDebug() { if (singleton != NULL) { singleton->close(); } } bool DebugDialog::visible() { if (singleton == NULL) return false; return singleton->isVisible(); } bool DebugDialog::connectToBroadcast(QObject * receiver, const char* slot) { if (singleton == NULL) { new DebugDialog(); } return connect(singleton, SIGNAL(debugBroadcast(const QString &, QObject *)), receiver, slot ); } void DebugDialog::setDebugLevel(DebugLevel debugLevel) { if (singleton == NULL) { new DebugDialog(); } singleton->m_debugLevel = debugLevel; } void DebugDialog::cleanup() { if (singleton) { delete singleton; singleton = NULL; } } bool DebugDialog::enabled() { return m_enabled; } void DebugDialog::setEnabled(bool enabled) { m_enabled = enabled; } fritzing-app-0.9.3b/src/debugdialog.h000066400000000000000000000045751272363601300175230ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef DEBUGDIALOG_H #define DEBUGDIALOG_H #include #include #include #include #include #include "utils/misc.h" class DebugDialog : public QDialog { Q_OBJECT private: DebugDialog(QWidget *parent = 0); ~DebugDialog(); public: enum DebugLevel { Debug, Info, Warning, Error }; public: static void debug(QString, const QPointF &point, DebugLevel = Debug, QObject * ancestor = 0); static void debug(QString, const QRectF &rect, DebugLevel = Debug, QObject * ancestor = 0); static void debug(QString, const QPoint &point, DebugLevel = Debug, QObject * ancestor = 0); static void debug(QString, const QRect &rect, DebugLevel = Debug, QObject * ancestor = 0); static void debug(QString, DebugLevel = Debug, QObject * ancestor = 0); static void hideDebug(); static void showDebug(); static void closeDebug(); static bool visible(); static bool connectToBroadcast(QObject * receiver, const char* slot); static void setDebugLevel(DebugLevel); static void cleanup(); static void setEnabled(bool); static bool enabled(); protected: bool event ( QEvent * e ); void resizeEvent ( QResizeEvent * event ); protected: static DebugDialog* singleton; static QFile m_file; static bool m_enabled; QPointer m_textEdit; DebugLevel m_debugLevel; signals: void debugBroadcast(const QString & message, DebugLevel, QObject * ancestor); }; #endif fritzing-app-0.9.3b/src/dialogs/000077500000000000000000000000001272363601300165135ustar00rootroot00000000000000fritzing-app-0.9.3b/src/dialogs/groundfillseeddialog.cpp000066400000000000000000000135131272363601300234100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include "groundfillseeddialog.h" #include "../debugdialog.h" #include "../connectors/connectoritem.h" #include "../sketch/pcbsketchwidget.h" #include #include #include ///////////////////////////////////////////////////////// GroundFillSeedDialog::GroundFillSeedDialog(PCBSketchWidget * sketchWidget, QList & connectorItems, const QString & intro, QWidget *parent) : QDialog(parent) { m_sketchWidget = sketchWidget; m_connectorItems = connectorItems; m_activeConnectorItem = NULL; m_doFill = false; this->setWindowTitle(QObject::tr("Ground Fill Seed Editor")); QVBoxLayout * vLayout = new QVBoxLayout(this); if (!intro.isEmpty()) { QLabel * label = new QLabel(intro); label->setWordWrap(true); vLayout->addWidget(label); } QLabel * label = new QLabel(tr("The difference between a 'ground fill' and plain 'copper fill' is that in a ground fill, " "the flooded area includes traces and connectors that are connected to 'ground' connectors. " "Ground connectors are usually labeled 'GND' or 'ground' but sometimes this is not the case. " "It also may be that there are multiple nets with a ground connector, " "and you might only want one of the nets to be filled.\n\n" "This dialog collects only connectors labeled 'GND' or 'ground', as well as connectors already chosen as seeds.\n\n" "Click an item to highlight its connections in the sketch.\n\n" "It is also possible to choose a connector as a ground fill seed by right-clicking a connector and " "choosing the 'Set Ground Fill Seed' context menu option.")); label->setWordWrap(true); vLayout->addWidget(label); m_listWidget = new QListWidget(this); int ix = 0; foreach (ConnectorItem * connectorItem, connectorItems) { QListWidgetItem *item = new QListWidgetItem; item->setData(Qt::DisplayRole, connectorItem->connectorSharedName()); item->setData(Qt::CheckStateRole, connectorItem->isGroundFillSeed() ? Qt::Checked : Qt::Unchecked); item->setData(Qt::UserRole, ix++); connectorItem->updateTooltip(); item->setToolTip(connectorItem->toolTip()); m_listWidget->addItem(item); } connect(m_listWidget, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(clickedSlot(QListWidgetItem *))); connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(changedSlot(QListWidgetItem *))); vLayout->addWidget(m_listWidget); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Yes | QDialogButtonBox::Cancel); QPushButton * cancelButton = buttonBox->button(QDialogButtonBox::Cancel); cancelButton->setText(tr("Cancel")); cancelButton->setDefault(false); QPushButton * okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(tr("OK")); okButton->setDefault(true); m_doFillButton = buttonBox->button(QDialogButtonBox::Yes); m_doFillButton->setDefault(false); connect(m_doFillButton, SIGNAL(clicked(bool)), this, SLOT(doFill(bool))); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vLayout->addWidget(buttonBox); this->setLayout(vLayout); changedSlot(NULL); } GroundFillSeedDialog::~GroundFillSeedDialog() { } void GroundFillSeedDialog::changedSlot(QListWidgetItem *) { bool checked = false; for (int i = 0; i < m_listWidget->count(); i++) { if (m_listWidget->item(i)->checkState() == Qt::Checked) { checked = true; break; } } m_doFillButton->setText(checked ? tr("OK and ground fill") : tr("OK and copper fill")); } void GroundFillSeedDialog::clickedSlot(QListWidgetItem * item) { int ix = -1; if (item != NULL) { ix = item->data(Qt::UserRole).toInt(); } showEqualPotential(m_activeConnectorItem, false); m_activeConnectorItem = (ix >= 0 && ix < m_connectorItems.count()) ? m_connectorItems.at(ix) : NULL; showEqualPotential(m_activeConnectorItem, true); } void GroundFillSeedDialog::showEqualPotential(ConnectorItem * connectorItem, bool show) { if (connectorItem) { QList connectorItems; connectorItems.append(connectorItem); ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::NoFlag); QList visited; foreach (ConnectorItem * ci, connectorItems) { ci->showEqualPotential(show, visited); } } } void GroundFillSeedDialog::accept() { showEqualPotential(m_activeConnectorItem, false); QDialog::accept(); } void GroundFillSeedDialog::reject() { showEqualPotential(m_activeConnectorItem, false); QDialog::reject(); } void GroundFillSeedDialog::getResults(QList & results) { for (int i = 0; i < m_listWidget->count(); i++) { results.append(m_listWidget->item(i)->checkState() == Qt::Checked); } } bool GroundFillSeedDialog::getFill() { return m_doFill; } void GroundFillSeedDialog::doFill(bool) { m_doFill = true; accept(); } fritzing-app-0.9.3b/src/dialogs/groundfillseeddialog.h000066400000000000000000000035441272363601300230600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #ifndef GROUNDFILLSEEDDIALOG_H #define GROUNDFILLSEEDDIALOG_H #include #include #include #include #include class GroundFillSeedDialog : public QDialog { Q_OBJECT public: GroundFillSeedDialog(class PCBSketchWidget *, QList &, const QString & intro, QWidget *parent = 0); ~GroundFillSeedDialog(); void getResults(QList & results); bool getFill(); public slots: void clickedSlot(QListWidgetItem *); void changedSlot(QListWidgetItem *); void accept(); void reject(); void doFill(bool); protected: void showEqualPotential(ConnectorItem * connectorItem, bool show); protected: PCBSketchWidget * m_sketchWidget; QList m_connectorItems; QListWidget * m_listWidget; ConnectorItem * m_activeConnectorItem; bool m_doFill; QPushButton * m_doFillButton; }; #endif fritzing-app-0.9.3b/src/dialogs/pinlabeldialog.cpp000066400000000000000000000212731272363601300221720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include "pinlabeldialog.h" #include "../debugdialog.h" #include #include #include #include #include #include #include // TODO: // // would be nice to update schematic view with new pin labels // // reasons for not doing save: // undo would require a lot of work to implement and would require saving the old dom document // every instance of the part in all open documents would have to be updated PinLabelUndoCommand::PinLabelUndoCommand(PinLabelDialog * pinLabelDialog, int index, QLineEdit * lineEdit, const QString & previous, const QString & next) : QUndoCommand() { m_pinLabelDialog = pinLabelDialog; m_previous = previous; m_next = next; m_index = index; m_lineEdit = lineEdit; } void PinLabelUndoCommand::undo() { m_pinLabelDialog->setLabelText(m_index, m_lineEdit, m_previous); } void PinLabelUndoCommand::redo() { m_pinLabelDialog->setLabelText(m_index, m_lineEdit, m_next); } ///////////////////////////////////////////////////////// PinLabelDialog::PinLabelDialog(const QStringList & labels, bool singleRow, const QString & chipLabel, bool isCore, QWidget *parent) : QDialog(parent) { m_isCore = isCore; m_labels = labels; m_doSaveAs = true; this->setWindowTitle(QObject::tr("Pin Label Editor")); QVBoxLayout * vLayout = new QVBoxLayout(this); QScrollArea * scrollArea = new QScrollArea(this); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QFrame * frame = new QFrame(this); QHBoxLayout * hLayout = new QHBoxLayout(frame); QFrame * labelsFrame = initLabels(labels, singleRow, chipLabel); QFrame * textFrame = new QFrame(); QVBoxLayout * textLayout = new QVBoxLayout(frame); QLabel * label = new QLabel("" + tr("

Pin Label Editor

") + tr("

Click on a label next to a pin number to rename that pin.") + " " + tr("You can use the tab key to move through the labels in order.

") + ""); label->setMaximumWidth(150); label->setWordWrap(true); textLayout->addWidget(label); textLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); textFrame->setLayout(textLayout); hLayout->addWidget(labelsFrame); hLayout->addSpacing(15); hLayout->addWidget(textFrame); frame->setLayout(hLayout); scrollArea->setWidget(frame); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); QPushButton * cancelButton = buttonBox->button(QDialogButtonBox::Cancel); cancelButton->setText(tr("Cancel")); cancelButton->setDefault(false); m_saveAsButton = buttonBox->button(QDialogButtonBox::Save); m_saveAsButton->setText(tr("Save")); m_saveAsButton->setEnabled(false); m_saveAsButton->setDefault(false); m_undoButton = new QPushButton(tr("Undo")); m_undoButton->setEnabled(false); m_undoButton->setDefault(false); m_redoButton = new QPushButton(tr("Redo")); m_redoButton->setEnabled(false); m_redoButton->setDefault(false); buttonBox->addButton(m_undoButton, QDialogButtonBox::ResetRole); buttonBox->addButton(m_redoButton, QDialogButtonBox::ResetRole); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vLayout->addWidget(scrollArea); vLayout->addWidget(buttonBox); this->setLayout(vLayout); connect(buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonClicked(QAbstractButton *))); connect(&m_undoStack, SIGNAL(canRedoChanged(bool)), this, SLOT(undoChanged(bool))); connect(&m_undoStack, SIGNAL(canUndoChanged(bool)), this, SLOT(undoChanged(bool))); connect(&m_undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(undoChanged(bool))); } PinLabelDialog::~PinLabelDialog() { } QFrame * PinLabelDialog::initLabels(const QStringList & labels, bool singleRow, const QString & chipLabel) { QFrame * frame = new QFrame(); QVBoxLayout * vLayout = new QVBoxLayout(); QLabel * label = new QLabel("

" + chipLabel + "

"); label->setAlignment(Qt::AlignCenter); QFrame * subFrame = new QFrame(); if (singleRow) { QGridLayout * gridLayout = new QGridLayout(); gridLayout->setMargin(0); gridLayout->setSpacing(3); for (int i = 0; i < labels.count(); i++) { makeOnePinEntry(i, labels.at(i), Qt::AlignLeft, i, gridLayout); } subFrame->setLayout(gridLayout); } else { QHBoxLayout * hLayout = new QHBoxLayout(); QFrame * lFrame = new QFrame(); QGridLayout * lLayout = new QGridLayout; lLayout->setMargin(0); lLayout->setSpacing(3); for (int i = 0; i < labels.count() / 2; i++) { makeOnePinEntry(i, labels.at(i), Qt::AlignLeft, i, lLayout); } lFrame->setLayout(lLayout); QFrame * rFrame = new QFrame(); QGridLayout * rLayout = new QGridLayout; rLayout->setMargin(0); rLayout->setSpacing(3); int row = labels.count() - 1; for (int i = labels.count() / 2; i < labels.count(); i++) { makeOnePinEntry(i, labels.at(i), Qt::AlignRight, row--, rLayout); } rFrame->setLayout(rLayout); hLayout->addWidget(lFrame); hLayout->addSpacing(15); hLayout->addWidget(rFrame); subFrame->setLayout(hLayout); } vLayout->addWidget(label); vLayout->addWidget(subFrame); frame->setLayout(vLayout); return frame; } void PinLabelDialog::makeOnePinEntry(int index, const QString & text, Qt::Alignment alignment, int row, QGridLayout * gridLayout) { QLineEdit * label = new QLineEdit(); label->setText(QString::number(index + 1)); label->setMaximumWidth(20); label->setMinimumWidth(20); label->setFrame(false); label->setEnabled(false); QLineEdit * lEdit = new QLineEdit(); lEdit->setMaximumWidth(65); lEdit->setAlignment(alignment); lEdit->setText(text); lEdit->setProperty("index", index); lEdit->setProperty("prev", text); connect(lEdit, SIGNAL(editingFinished()), this, SLOT(labelChanged())); connect(lEdit, SIGNAL(textEdited(const QString &)), this, SLOT(labelEdited(const QString &))); if (alignment == Qt::AlignLeft) { label->setAlignment(Qt::AlignRight); gridLayout->addWidget(label, row, 0); gridLayout->addWidget(lEdit, row, 1); } else { label->setAlignment(Qt::AlignLeft); gridLayout->addWidget(lEdit, row, 0); gridLayout->addWidget(label, row, 1); } } void PinLabelDialog::labelChanged() { QLineEdit * lineEdit = qobject_cast(sender()); if (lineEdit == NULL) return; bool ok; int index = lineEdit->property("index").toInt(&ok); if (!ok) return; if (index < 0) return; if (index >= m_labels.count()) return; PinLabelUndoCommand * pluc = new PinLabelUndoCommand(this, index, lineEdit, lineEdit->property("prev").toString(), lineEdit->text()); lineEdit->setProperty("prev", lineEdit->text()); m_undoStack.push(pluc); } void PinLabelDialog::labelEdited(const QString &) { m_saveAsButton->setEnabled(true); } const QStringList & PinLabelDialog::labels() { return m_labels; } void PinLabelDialog::setLabelText(int index, QLineEdit * lineEdit, const QString & text) { m_labels.replace(index, text); lineEdit->setText(text); lineEdit->setProperty("prev", text); } void PinLabelDialog::buttonClicked(QAbstractButton * button) { if (button == m_undoButton) { m_undoStack.undo(); } else if (button == m_redoButton) { m_undoStack.redo(); } } bool PinLabelDialog::doSaveAs() { return m_doSaveAs; } void PinLabelDialog::undoChanged(bool) { bool redo = false; bool undo = false; bool saveAs = false; if (m_undoStack.canUndo()) { saveAs = undo = true; } if (m_undoStack.canRedo()) { redo = true; } m_saveAsButton->setEnabled(saveAs); m_undoButton->setEnabled(undo); m_redoButton->setEnabled(redo); } void PinLabelDialog::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Escape: case Qt::Key_Enter: case Qt::Key_Return: return; default: QDialog::keyPressEvent(e); } } fritzing-app-0.9.3b/src/dialogs/pinlabeldialog.h000066400000000000000000000046561272363601300216450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #ifndef PINLABELDIALOG_H #define PINLABELDIALOG_H #include #include #include #include #include #include #include #include #include class PinLabelDialog : public QDialog { Q_OBJECT public: PinLabelDialog(const QStringList & labels, bool singleRow, const QString & chipLabel, bool isCore, QWidget *parent = 0); ~PinLabelDialog(); const QStringList & labels(); void setLabelText(int index, QLineEdit *, const QString & text); bool doSaveAs(); protected slots: void labelChanged(); void buttonClicked(QAbstractButton *); void undoChanged(bool); void labelEdited(const QString &) ; protected: QFrame * initLabels(const QStringList & labels, bool singleRow, const QString & chipLabel); void makeOnePinEntry(int index, const QString & label, Qt::Alignment alignment, int row, QGridLayout *); void keyPressEvent(QKeyEvent *e); protected: bool m_isCore; QStringList m_labels; QUndoStack m_undoStack; QPushButton * m_saveAsButton; QPushButton * m_undoButton; QPushButton * m_redoButton; bool m_doSaveAs; }; class PinLabelUndoCommand : public QUndoCommand { public: PinLabelUndoCommand(PinLabelDialog *, int index, QLineEdit *, const QString & previous, const QString & next); void undo(); void redo(); protected: QString m_previous; QString m_next; PinLabelDialog * m_pinLabelDialog; int m_index; QLineEdit * m_lineEdit; }; #endif fritzing-app-0.9.3b/src/dialogs/prefsdialog.cpp000066400000000000000000000423321272363601300215220ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6972 $: $Author: irascibl@gmail.com $: $Date: 2013-04-18 07:18:04 +0200 (Do, 18. Apr 2013) $ ********************************************************************/ #include "prefsdialog.h" #include "../debugdialog.h" #include "translatorlistmodel.h" #include "../items/itembase.h" #include "../utils/clickablelabel.h" #include "setcolordialog.h" #include "../sketch/zoomablegraphicsview.h" #include "../mainwindow/mainwindow.h" #include "../utils/folderutils.h" #include #include #include #include #include #include #include #include #include #include #include #define MARGIN 5 #define FORMLABELWIDTH 195 #define SPACING 15 PrefsDialog::PrefsDialog(const QString & language, QWidget *parent) : QDialog(parent) { this->setWindowTitle(QObject::tr("Preferences")); m_name = language; m_cleared = false; m_wheelMapping = (int) ZoomableGraphicsView::wheelMapping(); } PrefsDialog::~PrefsDialog() { } void PrefsDialog::initViewInfo(int index, const QString & viewName, const QString & shortName, bool curvy) { m_viewInfoThings[index].index = index; m_viewInfoThings[index].viewName = viewName; m_viewInfoThings[index].shortName = shortName; m_viewInfoThings[index].curvy = curvy; } void PrefsDialog::initLayout(QFileInfoList & languages, QList platforms) { m_tabWidget = new QTabWidget(); m_general = new QWidget(); m_breadboard = new QWidget(); m_schematic = new QWidget(); m_pcb = new QWidget(); m_code = new QWidget(); m_tabWidget->setObjectName("preDia_tabs"); m_tabWidget->addTab(m_general, tr("General")); m_tabWidget->addTab(m_breadboard, m_viewInfoThings[0].viewName); m_tabWidget->addTab(m_schematic, m_viewInfoThings[1].viewName); m_tabWidget->addTab(m_pcb, m_viewInfoThings[2].viewName); m_tabWidget->addTab(m_code, tr("Code View")); QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->addWidget(m_tabWidget); initGeneral(m_general, languages); initBreadboard(m_breadboard, &m_viewInfoThings[0]); initSchematic(m_schematic, &m_viewInfoThings[1]); initPCB(m_pcb, &m_viewInfoThings[2]); initCode(m_code, platforms); m_platforms = platforms; QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vLayout->addWidget(buttonBox); this->setLayout(vLayout); } void PrefsDialog::initGeneral(QWidget * widget, QFileInfoList & languages) { QVBoxLayout * vLayout = new QVBoxLayout(); // TODO: if no translation files found, don't put up the translation part of this dialog vLayout->addWidget(createLanguageForm(languages)); vLayout->addWidget(createColorForm()); vLayout->addWidget(createZoomerForm()); vLayout->addWidget(createAutosaveForm()); vLayout->addWidget(createOtherForm()); widget->setLayout(vLayout); } void PrefsDialog::initBreadboard(QWidget * widget, ViewInfoThing * viewInfoThing) { QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->addWidget(createCurvyForm(viewInfoThing)); vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); widget->setLayout(vLayout); } void PrefsDialog::initSchematic(QWidget * widget, ViewInfoThing * viewInfoThing) { QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->addWidget(createCurvyForm(viewInfoThing)); vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); widget->setLayout(vLayout); } void PrefsDialog::initPCB(QWidget * widget, ViewInfoThing * viewInfoThing) { QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->addWidget(createCurvyForm(viewInfoThing)); vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); widget->setLayout(vLayout); } void PrefsDialog::initCode(QWidget * widget, QList platforms) { QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->addWidget(createProgrammerForm(platforms)); vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); widget->setLayout(vLayout); } QWidget * PrefsDialog::createZoomerForm() { QGroupBox * zoomer = new QGroupBox(tr("Mouse Wheel Behavior"), this ); QHBoxLayout * zhlayout = new QHBoxLayout(); zhlayout->setSpacing(SPACING); QFrame * frame = new QFrame(); frame->setFixedWidth(FORMLABELWIDTH); QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->setSpacing(0); vLayout->setMargin(0); for (int i = 0; i < 3; i++) { m_wheelLabel[i] = new QLabel(); vLayout->addWidget(m_wheelLabel[i]); } updateWheelText(); frame->setLayout(vLayout); zhlayout->addWidget(frame); QPushButton * pushButton = new QPushButton(tr("Change Wheel Behavior"), this); connect(pushButton, SIGNAL(clicked()), this, SLOT(changeWheelBehavior())); zhlayout->addWidget(pushButton); zoomer->setLayout(zhlayout); return zoomer; } QWidget * PrefsDialog::createAutosaveForm() { QGroupBox * autosave = new QGroupBox(tr("Autosave"), this ); QHBoxLayout * zhlayout = new QHBoxLayout(); zhlayout->setSpacing(SPACING); QCheckBox * box = new QCheckBox(tr("Autosave every:")); box->setChecked(MainWindow::AutosaveEnabled); zhlayout->addWidget(box); zhlayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding)); QSpinBox * spinBox = new QSpinBox; spinBox->setMinimum(1); spinBox->setMaximum(60); spinBox->setValue(MainWindow::AutosaveTimeoutMinutes); spinBox->setMaximumWidth(80); zhlayout->addWidget(spinBox); QLabel * label = new QLabel(tr("minutes")); zhlayout->addWidget(label); autosave->setLayout(zhlayout); connect(box, SIGNAL(clicked(bool)), this, SLOT(toggleAutosave(bool))); connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(changeAutosavePeriod(int))); return autosave; } QWidget * PrefsDialog::createLanguageForm(QFileInfoList & languages) { QGroupBox * formGroupBox = new QGroupBox(tr("Language")); QVBoxLayout *layout = new QVBoxLayout(); QComboBox* comboBox = new QComboBox(this); m_translatorListModel = new TranslatorListModel(languages, this); comboBox->setModel(m_translatorListModel); comboBox->setCurrentIndex(m_translatorListModel->findIndex(m_name)); connect(comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeLanguage(int))); layout->addWidget(comboBox); QLabel * ll = new QLabel(); ll->setMinimumHeight(45); ll->setWordWrap(true); ll->setText(QObject::tr("Please note that a new language setting will not take effect " "until the next time you run Fritzing.")); layout->addWidget(ll); formGroupBox->setLayout(layout); return formGroupBox; } QWidget* PrefsDialog::createColorForm() { QGroupBox * formGroupBox = new QGroupBox(tr("Colors")); QVBoxLayout *layout = new QVBoxLayout(); layout->setSpacing(0); layout->setMargin(0); QFrame * f1 = new QFrame(); QHBoxLayout * h1 = new QHBoxLayout(); h1->setSpacing(SPACING); QLabel * c1 = new QLabel(QObject::tr("Connected highlight color")); c1->setWordWrap(true); c1->setFixedWidth(FORMLABELWIDTH); h1->addWidget(c1); QColor connectedColor = ItemBase::connectedColor(); ClickableLabel * cl1 = new ClickableLabel(tr("%1 (click to change...)").arg(connectedColor.name()), this); connect(cl1, SIGNAL(clicked()), this, SLOT(setConnectedColor())); cl1->setPalette(QPalette(connectedColor)); cl1->setAutoFillBackground(true); cl1->setMargin(MARGIN); h1->addWidget(cl1); f1->setLayout(h1); layout->addWidget(f1); QFrame * f2 = new QFrame(); QHBoxLayout * h2 = new QHBoxLayout(); h2->setSpacing(SPACING); QLabel * c2 = new QLabel(QObject::tr("Unconnected highlight color")); c2->setWordWrap(true); c2->setFixedWidth(FORMLABELWIDTH); h2->addWidget(c2); QColor unconnectedColor = ItemBase::unconnectedColor(); ClickableLabel * cl2 = new ClickableLabel(tr("%1 (click to change...)").arg(unconnectedColor.name()), this); connect(cl2, SIGNAL(clicked()), this, SLOT(setUnconnectedColor())); cl2->setPalette(QPalette(unconnectedColor)); cl2->setAutoFillBackground(true); cl2->setMargin(MARGIN); h2->addWidget(cl2); f2->setLayout(h2); layout->addWidget(f2); formGroupBox->setLayout(layout); return formGroupBox; } QWidget* PrefsDialog::createOtherForm() { QGroupBox * formGroupBox = new QGroupBox(tr("Clear Settings")); QHBoxLayout *layout = new QHBoxLayout(); layout->setSpacing(SPACING); QFrame * frame = new QFrame; QVBoxLayout * vlayout = new QVBoxLayout(); vlayout->setMargin(0); vlayout->setSpacing(0); QLabel * clearLabel = new QLabel(QObject::tr("Clear all saved settings and close this dialog immediately.")); clearLabel->setWordWrap(true); clearLabel->setFixedWidth(FORMLABELWIDTH); vlayout->addWidget(clearLabel); vlayout->addSpacing(SPACING); clearLabel = new QLabel(QObject::tr("This action does not delete any files; it restores settings to their default values.")); clearLabel->setWordWrap(true); clearLabel->setFixedWidth(FORMLABELWIDTH); vlayout->addWidget(clearLabel); vlayout->addSpacing(SPACING); clearLabel = new QLabel(QObject::tr("There is no undo for this action, and no further warning!!!!")); clearLabel->setWordWrap(true); clearLabel->setFixedWidth(FORMLABELWIDTH); vlayout->addWidget(clearLabel); frame->setLayout(vlayout); layout->addWidget(frame); QPushButton * clear = new QPushButton(QObject::tr("Clear Settings"), this); clear->setMaximumWidth(220); connect(clear, SIGNAL(clicked()), this, SLOT(clear())); layout->addWidget(clear); formGroupBox->setLayout(layout); return formGroupBox; } QWidget* PrefsDialog::createProgrammerForm(QList platforms) { QGroupBox * formGroupBox = new QGroupBox(tr("Platform Support")); QVBoxLayout *layout = new QVBoxLayout(); layout->setSpacing(SPACING); foreach (Platform * platform, platforms) { QLabel *platformLb = new QLabel(""); platformLb->setTextFormat(Qt::RichText); platformLb->setText(tr("%1").arg(platform->getName())); layout->addWidget(platformLb); QFrame * locationFrame = new QFrame(formGroupBox); QHBoxLayout * locationLayout = new QHBoxLayout(); locationLayout->setMargin(0); locationLayout->setSpacing(0); locationFrame->setLayout(locationLayout); QLabel *locationLb = new QLabel(tr("Location:")); locationLayout->addWidget(locationLb); locationLayout->addSpacing(SPACING); QLineEdit * locationLE = new QLineEdit(locationFrame); locationLE->setText(platform->getCommandLocation()); locationLayout->addWidget(locationLE); m_programmerLEs.insert(platform->getName(), locationLE); QPushButton * locateBtn = new QPushButton(tr("..."),locationFrame); locationLayout->addWidget(locateBtn); locateBtn->setProperty("platform", platform->getName()); connect(locateBtn, SIGNAL(clicked()), this, SLOT(chooseProgrammer())); layout->addWidget(locationFrame); QLabel *hintLb = new QLabel(""); hintLb->setTextFormat(Qt::RichText); hintLb->setOpenExternalLinks(true); hintLb->setText(tr("You need to have
%2 (version %3 or newer) installed.") .arg(platform->getDownloadUrl().toString()) .arg(platform->getIdeName()) .arg(platform->getMinVersion())); layout->addWidget(hintLb); layout->addSpacing(SPACING); } formGroupBox->setLayout(layout); return formGroupBox; } void PrefsDialog::chooseProgrammer() { QObject * button = sender(); if (button == NULL) return; QString platformName = sender()->property("platform").toString(); Platform * platform; foreach (Platform *p, m_platforms) { if (p->getName().compare(platformName) == 0) { platform = p; break; } } QString filename = FolderUtils::getOpenFileName( this, tr("Select a programmer (executable) for %1").arg(platform->getName()), FolderUtils::openSaveFolder(), "(Programmer *)" ); if (!filename.isEmpty()) { m_programmerLEs.value(platformName)->setText(filename); platform->setCommandLocation(filename); } } void PrefsDialog::changeLanguage(int index) { const QLocale * locale = m_translatorListModel->locale(index); if (locale) { m_name = locale->name(); m_settings.insert("language", m_name); } } void PrefsDialog::clear() { m_cleared = true; accept(); } bool PrefsDialog::cleared() { return m_cleared; } void PrefsDialog::setConnectedColor() { QColor cc = ItemBase::connectedColor(); QColor scc = ItemBase::standardConnectedColor(); SetColorDialog setColorDialog(tr("Connected Highlight"), cc, scc, false, this); int result = setColorDialog.exec(); if (result == QDialog::Rejected) return; QColor c = setColorDialog.selectedColor(); m_settings.insert("connectedColor", c.name()); ClickableLabel * cl = qobject_cast(sender()); if (cl) { cl->setPalette(QPalette(c)); } } void PrefsDialog::setUnconnectedColor() { QColor cc = ItemBase::unconnectedColor(); QColor scc = ItemBase::standardUnconnectedColor(); SetColorDialog setColorDialog(tr("Unconnected Highlight"), cc, scc, false, this); int result = setColorDialog.exec(); if (result == QDialog::Rejected) return; QColor c = setColorDialog.selectedColor(); m_settings.insert("unconnectedColor", c.name()); ClickableLabel * cl = qobject_cast(sender()); if (cl) { cl->setPalette(QPalette(c)); } } QHash & PrefsDialog::settings() { return m_settings; } QHash & PrefsDialog::tempSettings() { return m_tempSettings; } void PrefsDialog::changeWheelBehavior() { if (++m_wheelMapping >= ZoomableGraphicsView::WheelMappingCount) { m_wheelMapping = 0; } m_settings.insert("wheelMapping", QString("%1").arg(m_wheelMapping)); updateWheelText(); } void PrefsDialog::updateWheelText() { QString text; #ifdef Q_OS_MAC QString cKey = tr("Command"); #else QString cKey = tr("Control"); #endif switch((ZoomableGraphicsView::WheelMapping) m_wheelMapping) { case ZoomableGraphicsView::ScrollPrimary: default: text = tr("no keys down = scroll\nshift key swaps scroll axis\nAlt or %1 key = zoom").arg(cKey); break; case ZoomableGraphicsView::ZoomPrimary: text = tr("no keys down = zoom\nAlt or %1 key = scroll\nshift key swaps scroll axis").arg(cKey); break; } QStringList strings = text.split('\n'); for (int i = 0; i < 3; i++) { m_wheelLabel[i]->setText(strings.at(i)); } } void PrefsDialog::toggleAutosave(bool checked) { m_settings.insert("autosaveEnabled", QString("%1").arg(checked)); } void PrefsDialog::changeAutosavePeriod(int value) { m_settings.insert("autosavePeriod", QString("%1").arg(value)); } QWidget* PrefsDialog::createCurvyForm(ViewInfoThing * viewInfoThing) { QGroupBox * groupBox = new QGroupBox(tr("Curvy vs. straight wires")); QVBoxLayout *layout = new QVBoxLayout; QLabel * label1 = new QLabel(tr("When you mouse-down and drag on a wire or the leg of a part (as opposed to a connector or a bendpoint) " "do you want to change the curvature of the wire (or leg) or drag out a new bendpoint?")); label1->setWordWrap(true); layout->addWidget(label1); QLabel * label2 = new QLabel(tr("This checkbox sets the default behavior. " "You can switch back to the non-default behavior by holding down the Control key (Mac: Command key) when you drag.")); label2->setWordWrap(true); layout->addWidget(label2); layout->addSpacing(10); QCheckBox * checkbox = new QCheckBox(tr("Curvy wires and legs")); checkbox->setProperty("index", viewInfoThing->index); checkbox->setChecked(viewInfoThing->curvy); connect(checkbox, SIGNAL(clicked()), this, SLOT(curvyChanged())); layout->addWidget(checkbox); groupBox->setLayout(layout); return groupBox; } void PrefsDialog::curvyChanged() { QCheckBox * checkBox = qobject_cast(sender()); if (checkBox == NULL) return; ViewInfoThing * viewInfoThing = &m_viewInfoThings[sender()->property("index").toInt()]; m_settings.insert(QString("%1CurvyWires").arg(viewInfoThing->shortName), checkBox->isChecked() ? "1" : "0"); } fritzing-app-0.9.3b/src/dialogs/prefsdialog.h000066400000000000000000000061711272363601300211700ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6732 $: $Author: irascibl@gmail.com $: $Date: 2012-12-27 08:26:48 +0100 (Do, 27. Dez 2012) $ ********************************************************************/ #ifndef PREFSDIALOG_H #define PREFSDIALOG_H #include #include #include #include #include #include #include #include #include #include "../program/platform.h" struct ViewInfoThing { QString viewName; QString shortName; int index; bool curvy; }; class PrefsDialog : public QDialog { Q_OBJECT public: PrefsDialog(const QString & language, QWidget *parent = 0); ~PrefsDialog(); bool cleared(); QHash & settings(); QHash & tempSettings(); void initLayout(QFileInfoList & languages, QList platforms); void initViewInfo(int index, const QString & viewName, const QString & shortName, bool curvy); protected: QWidget * createLanguageForm(QFileInfoList & languages); QWidget* createOtherForm(); QWidget* createColorForm(); QWidget * createZoomerForm(); QWidget * createAutosaveForm(); QWidget *createProgrammerForm(QList platforms); void updateWheelText(); void initGeneral(QWidget * general, QFileInfoList & languages); void initBreadboard(QWidget *, ViewInfoThing *); void initSchematic(QWidget *, ViewInfoThing *); void initPCB(QWidget *, ViewInfoThing *); void initCode(QWidget *widget, QList platforms); QWidget * createCurvyForm(ViewInfoThing *); protected slots: void changeLanguage(int); void clear(); void setConnectedColor(); void setUnconnectedColor(); void changeWheelBehavior(); void toggleAutosave(bool); void changeAutosavePeriod(int); void curvyChanged(); void chooseProgrammer(); protected: QPointer m_tabWidget; QPointer m_general; QPointer m_breadboard; QPointer m_schematic; QPointer m_pcb; QPointer m_code; QPointer m_wheelLabel[3]; QList m_platforms; QHash m_programmerLEs; QHash m_settings; QHash m_tempSettings; QString m_name; class TranslatorListModel * m_translatorListModel; bool m_cleared; int m_wheelMapping; ViewInfoThing m_viewInfoThings[3]; }; #endif fritzing-app-0.9.3b/src/dialogs/quotedialog.cpp000066400000000000000000000270321272363601300215400ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 7003 $: $Author: irascibl@gmail.com $: $Date: 2013-04-29 11:54:36 +0200 (Mo, 29. Apr 2013) $ ********************************************************************/ #include "quotedialog.h" #include "../debugdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////// static CountCost TheCountCost[QuoteDialog::MessageCount]; static double TheArea; static int TheBoardCount; static QList Counts; static bool QuoteSucceeded = false; double hundredths(double d) { double h = (int) (d * 100); return h / ((double) 100); } QChar EuroSymbol(0x20AC); QChar ThinSpaceSymbol(0x2009); ////////////////////////////////////////////////////// LabelThing::LabelThing(const QString & text, const QString & released, const QString & pressed, const QString & hover, QWidget * parent) : QLabel(text, parent) { m_state = RELEASED; m_releasedImage = QPixmap(released); m_pressedImage = QPixmap(pressed); m_hoverImage = QPixmap(hover); setMinimumSize(m_releasedImage.width(), m_releasedImage.height()); setMaximumSize(m_releasedImage.width(), m_releasedImage.height()); } void LabelThing::enterEvent(QEvent * event) { m_state = HOVER; update(); QLabel::enterEvent(event); } void LabelThing::leaveEvent(QEvent * event) { m_state = RELEASED; update(); QLabel::enterEvent(event); } void LabelThing::mousePressEvent(QMouseEvent * event) { m_state = PRESSED; update(); QLabel::mousePressEvent(event); } void LabelThing::mouseReleaseEvent(QMouseEvent * event) { m_state = RELEASED; update(); QLabel::mouseReleaseEvent(event); emit clicked(); } void LabelThing::paintEvent(QPaintEvent * event) { // use 'new QPainter()' so that delete is invoked before the QLabel::paintEvent call, otherwise the label isn't painted on mac QPainter * painter = new QPainter(this); switch (m_state) { case PRESSED: painter->drawPixmap(0, 0, m_pressedImage); setContentsMargins(0, 4, 0, 0); break; case HOVER: painter->drawPixmap(0, 0, m_hoverImage); break; case RELEASED: painter->drawPixmap(0, 0, m_releasedImage); setContentsMargins(0, 0, 0, 0); break; } delete painter; DebugDialog::debug(QString("paint event %1").arg(m_state)); QLabel::paintEvent(event); } ////////////////////////////////////////////////////// // from http://qt.onyou.ch/2010/07/08/hide-vertical-grid-lines-in-qtableview/ // custom item delegate to draw grid lines around cells class CustomDelegate : public QStyledItemDelegate { public: CustomDelegate(QTableView* tableView); protected: void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; private: QPen _gridPen; }; CustomDelegate::CustomDelegate(QTableView* tableView) { // create grid pen int gridHint = tableView->style()->styleHint(QStyle::SH_Table_GridLineColor, new QStyleOptionViewItemV4()); QColor gridColor(gridHint); _gridPen = QPen(gridColor, 0, tableView->gridStyle()); } void CustomDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyledItemDelegate::paint(painter, option, index); if (index.row() < 2) { QPen oldPen = painter->pen(); painter->setPen(_gridPen); // paint vertical lines //painter->drawLine(option.rect.topRight(), option.rect.bottomRight()); //vertical lines are disabled // paint horizontal lines painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight()); //draw only horizontal line painter->setPen(oldPen); } } ////////////////////////////////////////////////////// QuoteDialog::QuoteDialog(bool full, QWidget *parent) : QDialog(parent) { initCounts(); QFile styleSheet(":/resources/styles/fritzing.qss"); if (!styleSheet.open(QIODevice::ReadOnly)) { DebugDialog::debug("Unable to open :/resources/styles/fritzing.qss"); } else { this->setStyleSheet(styleSheet.readAll()); } setWindowTitle(tr("Fritzing Fab Quote")); QVBoxLayout * vLayout = new QVBoxLayout(this); QLabel * label = new QLabel(tr("Order your PCB from Fritzing Fab")); label->setObjectName("quoteOrder"); vLayout->addWidget(label); m_messageLabel = new QLabel(tr("")); m_messageLabel->setObjectName("quoteMessage"); vLayout->addWidget(m_messageLabel); m_tableWidget = new QTableWidget(3, MessageCount + 1); m_tableWidget->setObjectName("quoteTable"); m_tableWidget->setShowGrid(false); m_tableWidget->verticalHeader()->setVisible(false); m_tableWidget->horizontalHeader()->setVisible(false); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) m_tableWidget->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents); m_tableWidget->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); #else m_tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); m_tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); #endif m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tableWidget->setItemDelegate(new CustomDelegate(m_tableWidget)); QStringList labels; labels << tr("Copies") << tr("Price per board") << tr("Price"); int ix = 0; foreach (QString labl, labels) { QTableWidgetItem * item = new QTableWidgetItem(labl); item->setFlags(0); m_tableWidget->setItem(ix, 0, item); ix += 1; } vLayout->addWidget(m_tableWidget); QString additional = tr("Please note that prices do not include shipping,
"); additional += tr("possible additional taxes, or the checking fee.
"); additional += tr("For more information on pricing see http://fab.fritzing.org/pricing."); label = new QLabel(additional); label->setObjectName("quoteAdditional"); label->setOpenExternalLinks(true); vLayout->addWidget(label); if (!full) label->setVisible(false); QFrame * frame = new QFrame; QHBoxLayout * buttonLayout = new QHBoxLayout(); LabelThing * labelThing = new LabelThing(tr("Visit Fritzing Fab"), ":/resources/images/icons/fabquote_button_release.png", ":/resources/images/icons/fabquote_button_press.png", ":/resources/images/icons/fabquote_button_hover.png" ); labelThing->setObjectName("quoteVisitButton"); connect(labelThing, SIGNAL(clicked()), this, SLOT(visitFritzingFab()), Qt::QueuedConnection); buttonLayout->addWidget(labelThing); buttonLayout->addSpacing(90); label = new QLabel(); QPixmap order(":/resources/images/icons/Toolbar_Icon_Order.png"); label->setPixmap(order); buttonLayout->addWidget(label); buttonLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding)); frame->setLayout(buttonLayout); vLayout->addWidget(frame); if (!full) frame->setVisible(false); vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding)); this->setLayout(vLayout); this->adjustSize(); } QuoteDialog::~QuoteDialog() { } void QuoteDialog::setCountCost(int index, int count, double cost) { //DebugDialog::debug(QString("quote dialog set count cost %1 %2").arg(count).arg(cost)); if (index < 0) return; if (index >= MessageCount) return; TheCountCost[index].count = count; TheCountCost[index].cost = cost; } void QuoteDialog::setArea(double area, int boardCount) { TheBoardCount = boardCount; TheArea = area; } void QuoteDialog::setText() { //DebugDialog::debug("quote dialog set text"); QString msg = tr("The total area of the %n PCB(s) in this sketch is", "", TheBoardCount); msg += tr(" %1 cm%3 (%2 in%3).
") .arg(hundredths(TheArea)) .arg(hundredths(TheArea / (2.54 * 2.54))) .arg(QChar(178)) ; msg += tr("Use Fritzing Fab to produce a PCB from your sketch.
"); msg += tr("Take advantage of our quantity discount:"); m_messageLabel->setText(msg); for (int i = 0; i < MessageCount; i++) { int count = TheCountCost[i].count; double cost = TheCountCost[i].cost; if (count == 0) continue; if (cost == 0) continue; QTableWidgetItem * item = new QTableWidgetItem(QString::number(count)); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); item->setFlags(0); m_tableWidget->setItem(0, i + 1, item); item = new QTableWidgetItem(QString("%1%2%3").arg(hundredths(cost/count), 13, 'F', 2).arg(ThinSpaceSymbol).arg(EuroSymbol)); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); item->setFlags(0); m_tableWidget->setItem(1, i + 1, item); item = new QTableWidgetItem(QString("%1%2%3").arg(hundredths(cost), 13, 'F', 2).arg(ThinSpaceSymbol).arg(EuroSymbol)); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); QFont font = item->font(); font.setBold(true); item->setFont(font); item->setFlags(0); m_tableWidget->setItem(2, i + 1, item); } // this is some bullshit because QTableWidget seems to allocate a huge amount of whitespace at right and bottom of the table int w = 10; for (int i = 0; i < m_tableWidget->columnCount(); i++) w += m_tableWidget->columnWidth(i); int h = 4; for (int i = 0; i < m_tableWidget->rowCount(); i++) h += m_tableWidget->rowHeight(i); m_tableWidget->setMaximumSize(QSize(w, h)); m_tableWidget->setMinimumSize(QSize(w, h)); this->adjustSize(); } void QuoteDialog::visitFritzingFab() { QDesktopServices::openUrl(QString("http://fab.fritzing.org/fritzing-fab")); } QString QuoteDialog::countArgs() { initCounts(); QString countArgs; foreach (int c, Counts) { countArgs += QString::number(c) + ","; } countArgs.chop(1); return countArgs; } void QuoteDialog::setQuoteSucceeded(bool succeeded) { //DebugDialog::debug(QString("quote succeeded %1").arg(succeeded)); QuoteSucceeded = succeeded; } bool QuoteDialog::quoteSucceeded() { //DebugDialog::debug(QString("check quote succeeded %1").arg(QuoteSucceeded)); return QuoteSucceeded; } void QuoteDialog::initCounts() { if (Counts.isEmpty()) { Counts << 1 << 2 << 5 << 10; } } fritzing-app-0.9.3b/src/dialogs/quotedialog.h000066400000000000000000000045031272363601300212030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef QUOTEDIALOG_H #define QUOTEDIALOG_H #include #include #include struct CountCost { int count; double cost; }; class LabelThing : public QLabel { Q_OBJECT public: LabelThing(const QString & text, const QString & released, const QString & pressed, const QString & hover, QWidget * parent = NULL); void enterEvent(QEvent * event); void leaveEvent(QEvent * event); void mousePressEvent(QMouseEvent * event); void mouseReleaseEvent(QMouseEvent * event); void paintEvent(QPaintEvent * event); public: enum State { RELEASED, PRESSED, HOVER }; signals: void clicked(); protected: QPixmap m_releasedImage; QPixmap m_pressedImage; QPixmap m_hoverImage; State m_state; }; class QuoteDialog : public QDialog { Q_OBJECT public: QuoteDialog(bool full, QWidget *parent = 0); ~QuoteDialog(); void setText(); public: static void setArea(double area, int boardCount); static void setCountCost(int index, int count, double cost); static QString countArgs(); static void setQuoteSucceeded(bool); static bool quoteSucceeded(); protected: static void initCounts(); public: static const int MessageCount = 4; protected slots: void visitFritzingFab(); protected: QLabel * m_messageLabel; QTableWidget * m_tableWidget; }; #endif fritzing-app-0.9.3b/src/dialogs/recoverydialog.cpp000066400000000000000000000154331272363601300222430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include "recoverydialog.h" #include "../utils/folderutils.h" #include "../debugdialog.h" CenteredTreeWidget::CenteredTreeWidget(QWidget * parent) : QTreeWidget(parent) { // no luck getting this styling to work from fritzing.qss setStyleSheet("::item { margin-left: 8px; margin-right: 8px; margin-top: 1px; margin-bottom: 1px; }"); } QStyleOptionViewItem CenteredTreeWidget::viewOptions() const { // alignment not possible from a stylesheet QStyleOptionViewItem styleOptionViewItem = QTreeWidget::viewOptions(); styleOptionViewItem.displayAlignment = Qt::AlignCenter; return styleOptionViewItem; } RecoveryDialog::RecoveryDialog(QFileInfoList fileInfoList, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) { m_recoveryList = new CenteredTreeWidget(this); m_recoveryList->setColumnCount(3); m_recoveryList->setSelectionMode(QAbstractItemView::ExtendedSelection); m_recoveryList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_recoveryList->setSortingEnabled(false); m_recoveryList->setDragEnabled(false); m_recoveryList->setUniformRowHeights(true); // Small optimization m_recoveryList->setIconSize(QSize(0,0)); m_recoveryList->setRootIsDecorated(false); QStringList headerLabels; headerLabels << tr("File") << tr("Last backup") << tr("Last saved"); m_recoveryList->setHeaderLabels(headerLabels); m_recoveryList->header()->setDefaultAlignment(Qt::AlignCenter); m_recoveryList->header()->setDragEnabled(false); m_recoveryList->header()->setDragDropMode(QAbstractItemView::NoDragDrop); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) m_recoveryList->header()->setResizeMode(QHeaderView::ResizeToContents); m_recoveryList->header()->setMovable(false); #else m_recoveryList->header()->setSectionResizeMode(QHeaderView::ResizeToContents); m_recoveryList->header()->setSectionsMovable(false); #endif connect(m_recoveryList, SIGNAL(itemSelectionChanged()), this, SLOT(updateRecoverButton())); QTreeWidgetItem *item; foreach (QFileInfo fileInfo, fileInfoList) { item = new QTreeWidgetItem; QString originalFileName = getOriginalFileName(fileInfo.absoluteFilePath()); DebugDialog::debug(QString("Creating option for recoveryDialog file %1").arg(originalFileName)); QFileInfo originalFileInfo(originalFileName); item->setData(0, Qt::UserRole, fileInfo.absoluteFilePath()); if (originalFileInfo.exists()) { item->setText(0, originalFileInfo.fileName()); item->setToolTip(0, originalFileInfo.absoluteFilePath()); item->setData(1, Qt::UserRole, originalFileInfo.absoluteFilePath()); item->setText(2, originalFileInfo.lastModified().toString()); } else { item->setText(0, originalFileName); item->setText(2, tr("file not saved")); item->setData(1, Qt::UserRole, originalFileName); } item->setText(1, fileInfo.lastModified().toString()); DebugDialog::debug(QString("Displaying recoveryDialog text %1 and data %2").arg(item->text(0)).arg(item->data(0,Qt::UserRole).value())); m_recoveryList->addTopLevelItem(item); m_fileList << item; } QVBoxLayout *layout = new QVBoxLayout(); QLabel * label = new QLabel; label->setWordWrap(true); label->setText(tr("

Fritzing may have crashed, but some of the changes to the following files may be recovered.

" "

The date and time each file was backed-up is displayed. " "If the file was saved, that date and time is also listed for comparison.

" "

The original files are still on your disk, if they were ever saved. " "You can choose whether to overwrite the original file after you load its recovery file.

" "

Select any files you want to recover from the list below.

" )); layout->addWidget(label); layout->addWidget(m_recoveryList); QHBoxLayout *buttonLayout = new QHBoxLayout(); layout->addLayout(buttonLayout); m_recover = new QPushButton(tr("&Recover")); m_recover->setDefault(true); m_recover->setEnabled(false); m_recover->setMaximumWidth(130); connect(m_recover, SIGNAL(clicked()), this, SLOT(accept())); m_ignore = new QPushButton(tr("&Ignore")); m_ignore->setMaximumWidth(130); connect(m_ignore, SIGNAL(clicked()), this, SLOT(reject())); buttonLayout->addSpacerItem(new QSpacerItem(0,0, QSizePolicy::Expanding)); buttonLayout->addWidget(m_recover); buttonLayout->addWidget(m_ignore); buttonLayout->addSpacerItem(new QSpacerItem(0,0, QSizePolicy::Expanding)); setLayout(layout); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); } QList RecoveryDialog::getFileList() { return m_fileList; } QString RecoveryDialog::getOriginalFileName(const QString & path) { QString originalName; QFile file(path); if (!file.open(QFile::ReadOnly)) { // TODO: not sure how else to handle this... DebugDialog::debug(QString("unable to open recovery file %1").arg(path)); return originalName; } QXmlStreamReader xml(&file); xml.setNamespaceProcessing(false); while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::StartElement) { QString name = xml.name().toString(); if (name == "originalFileName") { originalName = xml.readElementText(); break; } } } file.close(); return originalName; } void RecoveryDialog::updateRecoverButton() { if (m_recoveryList->selectedItems().isEmpty()) { m_recover->setEnabled(false); } else { m_recover->setEnabled(true); } } fritzing-app-0.9.3b/src/dialogs/recoverydialog.h000066400000000000000000000034721272363601300217100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef RECOVERYDIALOG_H #define RECOVERYDIALOG_H #include #include #include #include #include class CenteredTreeWidget : public QTreeWidget { Q_OBJECT public: CenteredTreeWidget(QWidget *parent = 0); protected: QStyleOptionViewItem viewOptions () const; }; class RecoveryDialog : public QDialog { Q_OBJECT public: RecoveryDialog(QFileInfoList fileList, QWidget *parent = 0, Qt::WindowFlags flags = 0); QList getFileList(); protected: QString getOriginalFileName(const QString & path); QList m_fileList; QTreeWidget *m_recoveryList; QPushButton *m_recover; QPushButton *m_ignore; protected slots: void updateRecoverButton(); }; #endif // RECOVERYDIALOG_H fritzing-app-0.9.3b/src/dialogs/setcolordialog.cpp000066400000000000000000000115651272363601300222410ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "setcolordialog.h" #include "../debugdialog.h" #include "../utils/clickablelabel.h" #include #include #include #include #include #include #include #include static const int MARGIN = 5; static const int BUTTON_WIDTH = 150; ///////////////////////////////////// SetColorDialog::SetColorDialog(const QString & message, QColor & currentColor, QColor & standardColor, bool askPrefs, QWidget *parent) : QDialog(parent) { m_prefsCheckBox = NULL; m_message = message; m_currentColor = m_selectedColor = currentColor; m_standardColor = standardColor; this->setWindowTitle(tr("%1 Color...").arg(message)); QVBoxLayout * vLayout = new QVBoxLayout(this); QLabel * label = new QLabel(tr("Choose %1 color:").arg(message.toLower())); vLayout->addWidget(label); QFrame * f4 = new QFrame(); QHBoxLayout * hLayout4 = new QHBoxLayout(f4); m_selectedColorLabel = new QLabel(); setColor(currentColor); hLayout4->addWidget(m_selectedColorLabel); m_selectedColorLabel->setMargin(MARGIN); vLayout->addWidget(f4); QFrame * f2 = new QFrame(); QHBoxLayout * hLayout2 = new QHBoxLayout(f2); QPushButton *button2 = new QPushButton(tr("Reset to default")); button2->setFixedWidth(BUTTON_WIDTH); connect(button2, SIGNAL(clicked()), this, SLOT(selectStandard())); hLayout2->addWidget(button2); m_standardColorLabel = new ClickableLabel(tr("Default color (%1)").arg(standardColor.name()), this); connect(m_standardColorLabel, SIGNAL(clicked()), this, SLOT(selectStandard())); m_standardColorLabel->setPalette(QPalette(standardColor)); m_standardColorLabel->setAutoFillBackground(true); m_standardColorLabel->setMargin(MARGIN); hLayout2->addWidget(m_standardColorLabel); vLayout->addWidget(f2); QFrame * f3 = new QFrame(); QHBoxLayout * hLayout3 = new QHBoxLayout(f3); QPushButton *button3 = new QPushButton(tr("Pick custom color ...")); connect(button3, SIGNAL(clicked()), this, SLOT(selectCustom())); hLayout3->addWidget(button3); setCustomColor(currentColor); vLayout->addWidget(f3); QSpacerItem * spacerItem = new QSpacerItem( 0, 10 ); vLayout->addSpacerItem(spacerItem); if (askPrefs) { QFrame * f5 = new QFrame(); QHBoxLayout * hLayout5 = new QHBoxLayout(f5); m_prefsCheckBox = new QCheckBox(tr("Make this the default %1 color").arg(message.toLower())); hLayout5->addWidget(m_prefsCheckBox); vLayout->addWidget(f5); } QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vLayout->addWidget(buttonBox); this->setLayout(vLayout); } SetColorDialog::~SetColorDialog() { } const QColor & SetColorDialog::selectedColor() { return m_selectedColor; } void SetColorDialog::selectCurrent() { setColor(m_currentColor); } void SetColorDialog::selectStandard() { setColor(m_standardColor); } void SetColorDialog::selectLastCustom() { setColor(m_customColor); } void SetColorDialog::selectCustom() { QColor color = QColorDialog::getColor ( m_selectedColor, this, tr("Pick custom %1 color").arg(m_message.toLower())); if (!color.isValid()) return; setColor(color); setCustomColor(color); } bool SetColorDialog::isPrefsColor() { if (m_prefsCheckBox == NULL) return false; return m_prefsCheckBox->isChecked(); } void SetColorDialog::setCustomColor(const QColor & color) { m_customColor = color; } void SetColorDialog::setColor(const QColor & color) { m_selectedColor = color; m_selectedColorLabel->setText(tr("Current color (%1)").arg(color.name())); m_selectedColorLabel->setPalette(QPalette(color)); m_selectedColorLabel->setAutoFillBackground(true); } fritzing-app-0.9.3b/src/dialogs/setcolordialog.h000066400000000000000000000035421272363601300217020ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SETCOLORDIALOG_H #define SETCOLORDIALOG_H #include #include #include #include class SetColorDialog : public QDialog { Q_OBJECT public: SetColorDialog(const QString & message, QColor & currentColor, QColor & standardColor, bool askPrefs, QWidget *parent = 0); ~SetColorDialog(); const QColor & selectedColor(); bool isPrefsColor(); protected: void setColor(const QColor &); void setCustomColor(const QColor &); protected slots: void selectCurrent(); void selectCustom(); void selectLastCustom(); void selectStandard(); protected: QString m_message; QColor m_currentColor; QColor m_standardColor; QColor m_selectedColor; QColor m_customColor; QLabel * m_currentColorLabel; QLabel * m_standardColorLabel; QLabel * m_customColorLabel; QLabel * m_selectedColorLabel; QCheckBox * m_prefsCheckBox; }; #endif fritzing-app-0.9.3b/src/dialogs/translatorlistmodel.cpp000066400000000000000000000240011272363601300233220ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "translatorlistmodel.h" #include "../debugdialog.h" static QVariant emptyVariant; QHash TranslatorListModel::m_languages; QList TranslatorListModel::m_localeList; // More languages written in their own language can be found // at http://www.mozilla.com/en-US/firefox/all.html // recipe for translating from mozilla strings into source code via windows: // 1. copy the string from the mozilla page into wordpad and save it as a unicode text file // 2. open that unicode text file as a binary file (e.g. in msvc) // 3. ignore the first two bytes (these are a signal that says "I'm unicode") // 4. initialize an array of ushort using the rest of the byte pairs // 5. don't forget to reverse the order of the bytes in each pair. // to add a new language to fritzing, find its two-letter language code: http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes // then in the translations.pri file, add a new line translations/fritzing_XX.ts where you substitute the two letters for "XX". // If the language has multiple dialects, then you will need to add _YY to distinguish TranslatorListModel::TranslatorListModel(QFileInfoList & fileInfoList, QObject* parent) : QAbstractListModel(parent) { if (m_languages.count() == 0) { m_languages.insert("english", tr("English - %1").arg("English")); m_languages.insert("german", tr("German - %1").arg("Deutsch")); m_languages.insert("hungarian", tr("Hungarian - %1").arg("Magyar")); m_languages.insert("estonian", tr("Estonian - %1").arg("Eesti keel")); m_languages.insert("dutch", tr("Dutch - %1").arg("Nederlands")); m_languages.insert("italian", tr("Italian - %1").arg("Italiano")); m_languages.insert("polish", tr("Polish - %1").arg("Polski")); m_languages.insert("swedish", tr("Swedish - %1").arg("Svenska")); m_languages.insert("galician", tr("Galician - %1").arg("Galego")); m_languages.insert("indonesian", tr("Indonesian - %1").arg("Bahasa Indonesia")); m_languages.insert("danish", tr("Danish - %1").arg("Dansk")); ushort t1[] = { 0x65e5, 0x672c, 0x8a9e, 0 }; m_languages.insert("japanese", tr("Japanese - %1").arg(QString::fromUtf16(t1))); ushort t2[] = { 0x0420, 0x0443, 0x0441, 0x0441, 0x043a, 0x0438, 0x0439, 0 }; m_languages.insert("russian", tr("Russian - %1").arg(QString::fromUtf16(t2))); ushort t3[] = { 0x05e2, 0x05d1, 0x05e8, 0x05d9, 0x05ea, 0 }; m_languages.insert("hebrew", tr("Hebrew - %1").arg(QString::fromUtf16(t3))); ushort t4[] = { 0x0639, 0x0631, 0x0628, 0x064a, 0 }; m_languages.insert("arabic", tr("Arabic - %1").arg(QString::fromUtf16(t4))); ushort t5[] = { 0x0939, 0x093f, 0x0928, 0x094d, 0x0926, 0x0940, 0x0020, 0x0028, 0x092d, 0x093e, 0x0930, 0x0924, 0x0029, 0 }; m_languages.insert("hindi", tr("Hindi - %1").arg(QString::fromUtf16(t5))); ushort t6[] = { 0x4e2d, 0x6587, 0x0020, 0x0028, 0x7b80, 0x4f53, 0x0029, 0 }; m_languages.insert("chinese_china", tr("Chinese (Simplified) - %1").arg(QString::fromUtf16(t6))); ushort t7[] = { 0x6b63, 0x9ad4, 0x4e2d, 0x6587, 0x0020, 0x0028, 0x7e41, 0x9ad4, 0x0029, 0 }; m_languages.insert("chinese_taiwan", tr("Chinese (Traditional) - %1").arg(QString::fromUtf16(t7))); ushort t8[] = { 0x010C, 0x65, 0x0161, 0x74, 0x69, 0x6E, 0x61, 0 }; m_languages.insert("czech", tr("Czech - %1").arg(QString::fromUtf16(t8))); ushort t9[] = { 0x52, 0x6F, 0x6D, 0xE2, 0x6E, 0x0103, 0 }; m_languages.insert("romanian", tr("Romanian - %1").arg(QString::fromUtf16(t9))); ushort t10[] = { 0x0E44, 0x0E17, 0x0E22, 0 }; m_languages.insert("thai", tr("Thai - %1").arg(QString::fromUtf16(t10))); ushort t11[] = { 0x0395, 0x03BB, 0x03BB, 0x03B7, 0x03BD, 0x03B9, 0x03BA, 0x03AC, 0}; m_languages.insert("greek", tr("Greek - %1").arg(QString::fromUtf16(t11))); ushort t12[] = { 0x0411, 0x044A, 0x043B, 0x0433, 0x0430, 0x0440, 0x0441, 0x043A, 0x0438, 0 }; m_languages.insert("bulgarian", tr("Bulgarian - %1").arg(QString::fromUtf16(t12))); ushort t13[] = { 0xD55C, 0xAD6D, 0xC5B4, 0}; m_languages.insert("korean", tr("Korean - %1").arg(QString::fromUtf16(t13))); ushort t14[] = { 0x53, 0x6C, 0x6F, 0x76, 0x65, 0x6E, 0x010D, 0x69, 0x6E, 0x61, 0}; m_languages.insert("slovak", tr("Slovak - %1").arg(QString::fromUtf16(t14))); ushort t15[] = { 0x09AC, 0x09BE, 0x0982, 0x09B2, 0x09BE, 0 }; m_languages.insert("bengali", tr("Bengali - %1").arg(QString::fromUtf16(t15))); ushort t16[] = { 0x0641, 0x0627, 0x0631, 0x0633, 0x06CC, 0 }; m_languages.insert("persian", tr("Persian - %1").arg(QString::fromUtf16(t16))); ushort t17[] = {0x53, 0x6C, 0x6F, 0x76, 0x65, 0x6E, 0x0161, 0x010D, 0x69, 0x6E, 0x61, 0 }; m_languages.insert("slovenian", tr("Slovenian - %1").arg(QString::fromUtf16(t17))); ushort t18[] = { 0x092E, 0x0930, 0x093E, 0x0920, 0x0940, 0 }; m_languages.insert("marathi", tr("Marathi - %1").arg(QString::fromUtf16(t18))); ushort t19[] = {0x0443, 0x043A, 0x0440, 0x0430, 0x0457, 0x043D, 0x0441, 0x044C, 0x043A, 0x0430, 0x0020, 0x043C, 0x043E, 0x0432, 0x0430, 0}; m_languages.insert("ukrainian", tr("Ukrainian - %1").arg(QString::fromUtf16(t19))); ushort t20[] = { 0x46, 0x72, 0x61, 0x6E, 0xE7, 0x61, 0x69, 0x73, 0 }; m_languages.insert("french", tr("French - %1").arg(QString::fromUtf16(t20))); ushort t21[] = { 0x45, 0x73, 0x70, 0x61, 0xF1, 0x6F, 0x6C, 0 }; m_languages.insert("spanish", tr("Spanish - %1").arg(QString::fromUtf16(t21))); ushort t22[] = { 0x50, 0x6F, 0x72, 0x74, 0x75, 0x67, 0x75, 0xEA, 0x73, 0x20, 0x28, 0x45, 0x75, 0x72, 0x6F, 0x70, 0x65, 0x75, 0x29, 0 }; m_languages.insert("portuguese_portugal", tr("Portuguese (European)- %1").arg(QString::fromUtf16(t22))); ushort t23[] = { 0x50, 0x6F, 0x72, 0x74, 0x75, 0x67, 0x75, 0xEA, 0x73, 0x20, 0x28, 0x64, 0x6F, 0x20, 0x42, 0x72, 0x61, 0x73, 0x69, 0x6C, 0x29, 0 }; m_languages.insert("portuguese_brazil", tr("Portuguese (Brazilian) - %1").arg(QString::fromUtf16(t23))); ushort t24[] = { 0x54, 0xFC, 0x72, 0x6B, 0xE7, 0x65, 0 }; m_languages.insert("turkish", tr("Turkish - %1").arg(QString::fromUtf16(t24))); ushort t25[] = { 0x043C, 0x0430, 0x043A, 0x0435, 0x0434, 0x043E, 0x043D, 0x0441, 0x043A, 0x0438, 0x20, 0x0458, 0x0430, 0x0437, 0x0438, 0x043A, 0 }; m_languages.insert("macedonian", tr("Macedonian - %1").arg(QString::fromUtf16(t25))); ushort t26[] = { 0x0441, 0x0440, 0x043F, 0x0441, 0x043a, 0x0438, 0x0020, 0x0458, 0x20, 0x0435, 0x0437, 0x0438, 0x043A, 0 }; m_languages.insert("serbian", tr("Serbian - %1").arg(QString::fromUtf16(t26))); ushort t27[] = { 0x0627, 0x0631, 0x062f, 0x0648, 0 }; m_languages.insert("urdu", tr("Urdu - %1").arg(QString::fromUtf16(t27))); } if (m_localeList.count() == 0) { foreach (QFileInfo fileinfo, fileInfoList) { QString name = fileinfo.completeBaseName(); name.replace("fritzing_", ""); QStringList names = name.split("_"); if (names.count() > 1) { name = names[0] + "_" + names[1].toUpper(); } QLocale * locale = new QLocale(name); m_localeList.append(locale); } } } TranslatorListModel::~TranslatorListModel() { } void TranslatorListModel::cleanup() { foreach (QLocale * locale, m_localeList) { delete locale; } m_localeList.clear(); } QVariant TranslatorListModel::data ( const QModelIndex & index, int role) const { if (role == Qt::DisplayRole && index.row() >= 0 && index.row() < m_localeList.count()) { QString languageString = QLocale::languageToString(m_localeList.at(index.row())->language()); QString countryString = QLocale::countryToString(m_localeList.at(index.row())->country()); //DebugDialog::debug(QString("language %1 %2").arg(languageString).arg(countryString)); // QLocale::languageToString() only returns an English string, // so put it through a language-dependent hash table. QString trLanguageString = m_languages.value(languageString.toLower(), ""); if (trLanguageString.isEmpty()) { trLanguageString = m_languages.value(languageString.toLower() + "_" + countryString.toLower(), ""); } if (trLanguageString.isEmpty()) { foreach (QString key, m_languages.keys()) { if (key.startsWith(languageString.toLower())) { return m_languages.value(key); } } return languageString; } return trLanguageString; } return emptyVariant; } int TranslatorListModel::rowCount ( const QModelIndex & parent) const { Q_UNUSED(parent); return m_localeList.count(); } const QLocale * TranslatorListModel::locale( int index) { if (index < 0 || index >= m_localeList.count()) return NULL; return m_localeList.at(index); } int TranslatorListModel::findIndex(const QString & language) { int ix = 0; foreach (QLocale * locale, m_localeList) { //DebugDialog::debug(QString("find index %1 %2").arg(language).arg(locale->name())); if (language.compare(locale->name()) == 0) return ix; ix++; } ix = 0; foreach (QLocale * locale, m_localeList) { if (locale->name().startsWith("en")) return ix; ix++; } return 0; } fritzing-app-0.9.3b/src/dialogs/translatorlistmodel.h000066400000000000000000000032201272363601300227670ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef TRANSLATORLISTMODEL_H #define TRANSLATORLISTMODEL_H #include #include #include #include class TranslatorListModel : public QAbstractListModel { Q_OBJECT public: TranslatorListModel(QFileInfoList &, QObject* parent = 0); ~TranslatorListModel(); QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; int rowCount ( const QModelIndex & parent = QModelIndex() ) const ; const QLocale * locale(int index); int findIndex(const QString & language); static void cleanup(); protected: static QList m_localeList; static QHash m_languages; }; #endif fritzing-app-0.9.3b/src/dock/000077500000000000000000000000001272363601300160115ustar00rootroot00000000000000fritzing-app-0.9.3b/src/dock/layerpalette.cpp000066400000000000000000000106511272363601300212130ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "layerpalette.h" #include ViewLayerCheckBox::ViewLayerCheckBox(QWidget * parent) : QCheckBox(parent) { } ViewLayerCheckBox::~ViewLayerCheckBox() { } void ViewLayerCheckBox::setViewLayer(ViewLayer * viewLayer) { m_viewLayer = viewLayer; } ViewLayer * ViewLayerCheckBox::viewLayer() { return m_viewLayer; } ////////////////////////////////////// LayerPalette::LayerPalette(QWidget * parent) : QScrollArea(parent) { m_hideAllLayersAct = m_showAllLayersAct = NULL; QFrame * frame = new QFrame(this); m_mainLayout = new QVBoxLayout(); m_mainLayout->setSizeConstraint( QLayout::SetMinAndMaxSize ); m_mainLayout -> setObjectName("LayerWindowFrame"); for (int i = 0; i < ViewLayer::ViewLayerCount; i++) { ViewLayerCheckBox * cb = new ViewLayerCheckBox(this); connect(cb, SIGNAL(clicked(bool)), this, SLOT(setLayerVisibility(bool))); m_checkBoxes.append(cb); cb -> setObjectName("LayerCheckBox"); cb->setVisible(false); } m_groupBox = new QGroupBox(""); m_groupBox -> setObjectName("LayerWindowList"); QVBoxLayout * groupLayout = new QVBoxLayout(); groupLayout -> setObjectName("LayerWindowBoxes"); m_showAllWidget = new QCheckBox(tr("show all layers")); connect(m_showAllWidget, SIGNAL(clicked(bool)), this, SLOT(setAllLayersVisible(bool))); groupLayout->addWidget(m_showAllWidget); m_groupBox->setLayout(groupLayout); m_mainLayout->addWidget(m_groupBox); frame->setLayout(m_mainLayout); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setWidget(frame); } LayerPalette::~LayerPalette() { } void LayerPalette::resetLayout(LayerHash & viewLayers, LayerList & keys) { m_mainLayout->removeWidget(m_groupBox); foreach (ViewLayerCheckBox * cb, m_checkBoxes) { m_mainLayout->removeWidget(cb); cb->setVisible(false); } int ix = 0; foreach (ViewLayer::ViewLayerID key, keys) { ViewLayer * viewLayer = viewLayers.value(key); //if (viewLayer->parentLayer()) continue; ViewLayerCheckBox * cb = m_checkBoxes[ix++]; cb->setText(viewLayer->action()->text()); cb->setViewLayer(viewLayer); cb->setVisible(true); m_mainLayout->addWidget(cb); } m_mainLayout->addWidget(m_groupBox); m_mainLayout->invalidate(); } void LayerPalette::updateLayerPalette(LayerHash & viewLayers, LayerList & keys) { m_showAllWidget->setEnabled(m_showAllLayersAct->isEnabled() || m_hideAllLayersAct->isEnabled()); m_showAllWidget->setChecked(!m_showAllLayersAct->isEnabled()); int ix = 0; foreach (ViewLayer::ViewLayerID key, keys) { ViewLayer * viewLayer = viewLayers.value(key); //if (viewLayer->parentLayer()) continue; ViewLayerCheckBox * cb = m_checkBoxes[ix++]; cb->setChecked(viewLayer->action()->isChecked()); cb->setEnabled(viewLayer->action()->isEnabled()); } } void LayerPalette::setLayerVisibility(bool) { ViewLayerCheckBox * cb = qobject_cast(sender()); if (cb == NULL) return; // want to toggle layer individually in this case cb->viewLayer()->setIncludeChildLayers(false); cb->viewLayer()->action()->trigger(); cb->viewLayer()->setIncludeChildLayers(true); } void LayerPalette::setShowAllLayersAction(QAction * action) { m_showAllLayersAct = action; } void LayerPalette::setHideAllLayersAction(QAction * action) { m_hideAllLayersAct = action; } void LayerPalette::setAllLayersVisible(bool vis) { if (vis) { if (m_showAllLayersAct) { m_showAllLayersAct->trigger(); } } else { if (m_hideAllLayersAct) { m_hideAllLayersAct->trigger(); } } } fritzing-app-0.9.3b/src/dock/layerpalette.h000066400000000000000000000040601272363601300206550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef LAYERPALETTE_H #define LAYERPALETTE_H #include #include #include #include #include #include #include #include "../viewlayer.h" class ViewLayerCheckBox : public QCheckBox { Q_OBJECT public: ViewLayerCheckBox(QWidget * parent = NULL); ~ViewLayerCheckBox(); void setViewLayer(ViewLayer *); ViewLayer * viewLayer(); protected: ViewLayer * m_viewLayer; }; class LayerPalette : public QScrollArea { Q_OBJECT public: LayerPalette(QWidget * parent = NULL); ~LayerPalette(); void updateLayerPalette(LayerHash & viewLayers, LayerList & keys); void resetLayout(LayerHash & viewLayers, LayerList & keys); void setShowAllLayersAction(QAction *); void setHideAllLayersAction(QAction *); protected slots: void setLayerVisibility(bool vis); void setAllLayersVisible(bool vis); protected: QCheckBox * m_showAllWidget; QList m_checkBoxes; QVBoxLayout * m_mainLayout; QGroupBox * m_groupBox; QAction *m_showAllLayersAct; QAction *m_hideAllLayersAct; }; #endif fritzing-app-0.9.3b/src/eagle/000077500000000000000000000000001272363601300161465ustar00rootroot00000000000000fritzing-app-0.9.3b/src/eagle/fritzing2eagle.cpp000066400000000000000000000031261272363601300215700ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6469 $: $Author: irascibl@gmail.com $: $Date: 2012-09-23 00:54:30 +0200 (So, 23. Sep 2012) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include "../items/itembase.h" #include "../sketch/pcbsketchwidget.h" #include "fritzing2eagle.h" Fritzing2Eagle* Fritzing2Eagle::singleton = NULL; Fritzing2Eagle::Fritzing2Eagle(PCBSketchWidget *m_pcbGraphicsView) { QList partList; m_pcbGraphicsView->collectParts(partList); singleton = this; } /* void Fritzing2Eagle::showOutputInfo(PCBSketchWidget m_pcbGraphicsView) { if (singleton == NULL) { new Fritzing2Eagle(); } } */ fritzing-app-0.9.3b/src/eagle/fritzing2eagle.h000066400000000000000000000024551272363601300212410ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FRITZING2EAGLE_H #define FRITZING2EAGLE_H #include #include class Fritzing2Eagle { // Q_OBJECT public: Fritzing2Eagle(PCBSketchWidget *m_pcbGraphicsView); /* public: static void showOutputInfo(PCBSketchWidget m_pcbGraphicsView); */ protected: static Fritzing2Eagle* singleton; }; #endif fritzing-app-0.9.3b/src/fapplication.cpp000066400000000000000000002006431272363601300202530ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6975 $: $Author: irascibl@gmail.com $: $Date: 2013-04-19 12:51:22 +0200 (Fr, 19. Apr 2013) $ ********************************************************************/ #include "fapplication.h" #include "debugdialog.h" #include "utils/misc.h" #include "mainwindow/mainwindow.h" #include "fsplashscreen.h" #include "version/version.h" #include "dialogs/prefsdialog.h" #include "fsvgrenderer.h" #include "version/versionchecker.h" #include "version/updatedialog.h" #include "itemdrag.h" #include "items/wire.h" #include "partsbinpalette/binmanager/binmanager.h" #include "help/tipsandtricks.h" #include "utils/folderutils.h" #include "utils/lockmanager.h" #include "utils/fmessagebox.h" #include "dialogs/translatorlistmodel.h" #include "partsbinpalette/partsbinview.h" #include "partsbinpalette/svgiconwidget.h" #include "partsbinpalette/partsbinpalettewidget.h" #include "items/moduleidnames.h" #include "partsbinpalette/searchlineedit.h" #include "utils/ratsnestcolors.h" #include "utils/cursormaster.h" #include "utils/textutils.h" #include "utils/graphicsutils.h" #include "infoview/htmlinfoview.h" #include "svg/gedaelement2svg.h" #include "svg/kicadmodule2svg.h" #include "svg/kicadschematic2svg.h" #include "svg/gerbergenerator.h" #include "installedfonts.h" #include "items/pinheader.h" #include "items/partfactory.h" #include "items/propertydef.h" #include "dialogs/recoverydialog.h" #include "processeventblocker.h" #include "autoroute/panelizer.h" #include "sketch/sketchwidget.h" #include "sketch/pcbsketchwidget.h" #include "help/firsttimehelpdialog.h" #include "help/aboutbox.h" #include "version/partschecker.h" // dependency injection :P #include "referencemodel/sqlitereferencemodel.h" #define CurrentReferenceModel SqliteReferenceModel #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LINUX_32 #define PLATFORM_NAME "linux-32bit" #endif #ifdef LINUX_64 #define PLATFORM_NAME "linux-64bit" #endif #ifdef Q_OS_WIN #ifdef WIN64 #define PLATFORM_NAME "windows-64bit" #else #define PLATFORM_NAME "windows" #endif #endif #ifdef Q_OS_MAC #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) || defined(QT_MAC_USE_COCOA) #define PLATFORM_NAME "mac-os-x-105" #else #define PLATFORM_NAME "mac-os-x-104" #endif #endif #ifdef Q_OS_WIN #ifndef QT_NO_DEBUG #define WIN_DEBUG #endif #endif static const double LoadProgressStart = 0.085; static const double LoadProgressEnd = 0.6; //////////////////////////////////////////////////// FServer::FServer(QObject *parent) : QTcpServer(parent) { } void FServer::incomingConnection(int socketDescriptor) { emit newConnection(socketDescriptor); } //////////////////////////////////////////////////// QMutex FServerThread::m_busy; FServerThread::FServerThread(int socketDescriptor, QObject *parent) : QThread(parent), m_socketDescriptor(socketDescriptor) { } void FServerThread::run() { QTcpSocket * socket = new QTcpSocket(); if (!socket->setSocketDescriptor(m_socketDescriptor)) { emit error(socket->error()); DebugDialog::debug(QString("Socket error %1 %2").arg(socket->error()).arg(socket->errorString())); socket->deleteLater(); return; } socket->waitForReadyRead(); QString header; while (socket->canReadLine()) { header += socket->readLine(); } DebugDialog::debug("header " + header); QStringList tokens = header.split(QRegExp("[ \r\n][ \r\n]*"), QString::SkipEmptyParts); if (tokens.count() <= 0) { writeResponse(socket, 400, "Bad Request", "", ""); return; } if (tokens[0] != "GET") { writeResponse(socket, 405, "Method Not Allowed", "", ""); return; } if (tokens.count() < 2) { writeResponse(socket, 400, "Bad Request", "", ""); return; } QStringList params = tokens.at(1).split("/", QString::SkipEmptyParts); QString command = params.takeFirst(); if (params.count() == 0) { writeResponse(socket, 400, "Bad Request", "", ""); return; } QString subFolder = params.join("/"); bool fixSubFolder = false; QString mimeType; if (command == "svg") { mimeType = "image/svg+xml"; } else if (command == "gerber") { } else if (command == "svg-tcp") { fixSubFolder = true; } else if (command == "gerber-tcp") { fixSubFolder = true; } else { writeResponse(socket, 400, "Bad Request", "", ""); return; } if (fixSubFolder) { // replace "/" that was removed from "http:/blah" above int ix = subFolder.indexOf(":/"); if (ix >= 0) { subFolder.insert(ix + 1, "/"); } } int waitInterval = 100; // 100ms to wait int timeoutSeconds = 2 * 60; // timeout after 2 minutes int attempts = timeoutSeconds * 1000 / waitInterval; // timeout a bool gotLock = false; for (int i = 0; i < attempts; i++) { if (m_busy.tryLock()) { gotLock = true; break; } } if (!gotLock) { writeResponse(socket, 503, "Service Unavailable", "", "Server busy."); return; } DebugDialog::debug(QString("emitting do command %1 %2").arg(command).arg(subFolder)); QString result; int status; emit doCommand(command, subFolder, result, status); m_busy.unlock(); if (status != 200) { writeResponse(socket, status, "failed", "", result); } else if (command.endsWith("tcp")) { QString filename = result; mimeType = "application/zip"; QFile file(filename); if (file.open(QFile::ReadOnly)) { QString response = QString("HTTP/1.0 %1 %2\r\n").arg(200).arg("ok"); response += QString("Content-Type: %1; charset=\"utf-8\"\r\n").arg(mimeType); response += QString("Content-Length: %1\r\n").arg(file.size()); response += QString("\r\n"); socket->write(response.toUtf8()); int buffersize = 8192; long remaining = file.size(); while (remaining >= buffersize) { QByteArray bytes = file.read(buffersize); socket->write(bytes); remaining -= buffersize; } if (remaining > 0) { QByteArray bytes = file.read(remaining); socket->write(bytes); } socket->disconnectFromHost(); socket->waitForDisconnected(); socket->deleteLater(); file.close(); } else { writeResponse(socket, 500, "failed", "", "local zip failure (2)"); } QFileInfo info(filename); QDir dir = info.dir(); FolderUtils::rmdir(dir); } else { writeResponse(socket, 200, "Ok", mimeType, result); } } void FServerThread::writeResponse(QTcpSocket * socket, int code, const QString & codeString, const QString & mimeType, const QString & message) { QString type = mimeType; if (type.isEmpty()) type = "text/plain"; QString response = QString("HTTP/1.0 %1 %2\r\n").arg(code).arg(codeString); response += QString("Content-Type: %1; charset=\"utf-8\"\r\n").arg(type); response += QString("Content-Length: %1\r\n").arg(message.count()); response += QString("\r\n%1").arg(message); socket->write(response.toUtf8()); socket->disconnectFromHost(); socket->waitForDisconnected(); socket->deleteLater(); } //////////////////////////////////////////////////// RegenerateDatabaseThread::RegenerateDatabaseThread(const QString & dbFileName, QDialog * progressDialog, ReferenceModel * referenceModel) { m_dbFileName = dbFileName; m_referenceModel = referenceModel; m_progressDialog = progressDialog; } const QString RegenerateDatabaseThread::error() const { return m_error; } QDialog * RegenerateDatabaseThread::progressDialog() const { return m_progressDialog; } ReferenceModel * RegenerateDatabaseThread::referenceModel() const { return m_referenceModel; } void RegenerateDatabaseThread::run() { QTemporaryFile file("XXXXXX.db"); QString fileName; if (file.open()) { fileName = file.fileName(); file.close(); } else { m_error = tr("Unable to open temporary file"); return; } bool ok = ((FApplication *) qApp)->loadReferenceModel(fileName, true, m_referenceModel); if (!ok) { m_error = tr("Database failure"); return; } if (QFile::exists(m_dbFileName)) { ok = QFile::remove(m_dbFileName); if (!ok) { m_error = tr("Unable to remove original db file %1").arg(m_dbFileName); return; } } ok = QFile::copy(fileName, m_dbFileName); if (!ok) { m_error = tr("Unable to copy database file %1").arg(m_dbFileName); return; } } //////////////////////////////////////////////////// FApplication::FApplication( int & argc, char ** argv) : QApplication(argc, argv) { m_fServer = NULL; m_spaceBarIsPressed = false; m_mousePressed = false; m_referenceModel = NULL; m_started = false; m_updateDialog = NULL; m_lastTopmostWindow = NULL; m_serviceType = NoService; m_splash = NULL; m_arguments = arguments(); } bool FApplication::init() { //foreach (QString argument, m_arguments) { //DebugDialog::debug(QString("argument %1").arg(argument)); //} m_serviceType = NoService; QList toRemove; for (int i = 0; i < m_arguments.length(); i++) { if ((m_arguments[i].compare("-h", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-help", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--help", Qt::CaseInsensitive) == 0)) { return false; } if ((m_arguments[i].compare("-e", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-examples", Qt::CaseInsensitive) == 0)|| (m_arguments[i].compare("--examples", Qt::CaseInsensitive) == 0)) { DebugDialog::setEnabled(true); m_serviceType = ExampleService; m_outputFolder = " "; // otherwise program will bail out toRemove << i; } if ((m_arguments[i].compare("-d", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-debug", Qt::CaseInsensitive) == 0)|| (m_arguments[i].compare("--debug", Qt::CaseInsensitive) == 0)) { DebugDialog::setEnabled(true); toRemove << i; } if (i + 1 >= m_arguments.length()) continue; if ((m_arguments[i].compare("-f", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-folder", Qt::CaseInsensitive) == 0)|| (m_arguments[i].compare("--folder", Qt::CaseInsensitive) == 0)) { FolderUtils::setApplicationPath(m_arguments[i + 1]); // delete these so we don't try to process them as files later toRemove << i << i + 1; } if ((m_arguments[i].compare("-pp", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-pa", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-parts", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--parts", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--partsparent", Qt::CaseInsensitive) == 0)) { FolderUtils::setAppPartsPath(m_arguments[i + 1]); // delete these so we don't try to process them as files later toRemove << i << i + 1; } if ((m_arguments[i].compare("-ov", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-ow", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-of", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--ov", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--ow", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--of", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--overridefolder", Qt::CaseInsensitive) == 0) ) { PaletteModel::setFzpOverrideFolder(m_arguments[i + 1]); // delete these so we don't try to process them as files later toRemove << i << i + 1; } if ((m_arguments[i].compare("-geda", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--geda", Qt::CaseInsensitive) == 0)) { m_serviceType = GedaService; m_outputFolder = m_arguments[i + 1]; toRemove << i << i + 1; } //if ((m_arguments[i].compare("-drc", Qt::CaseInsensitive) == 0) || // (m_arguments[i].compare("--drc", Qt::CaseInsensitive) == 0)) { // m_serviceType = DRCService; // m_outputFolder = m_arguments[i + 1]; // toRemove << i << i + 1; //} if ((m_arguments[i].compare("-db", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-database", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--database", Qt::CaseInsensitive) == 0)) { m_serviceType = DatabaseService; m_outputFolder = m_arguments[i + 1]; toRemove << i << i + 1; } if ((m_arguments[i].compare("-kicad", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--kicad", Qt::CaseInsensitive) == 0)) { m_serviceType = KicadFootprintService; m_outputFolder = m_arguments[i + 1]; toRemove << i << i + 1; } if ((m_arguments[i].compare("-kicadschematic", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--kicadschematic", Qt::CaseInsensitive) == 0)) { m_serviceType = KicadSchematicService; m_outputFolder = m_arguments[i + 1]; toRemove << i << i + 1; } if ((m_arguments[i].compare("-svg", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--svg", Qt::CaseInsensitive) == 0)) { m_serviceType = SvgService; DebugDialog::setEnabled(true); m_outputFolder = m_arguments[i + 1]; toRemove << i << i + 1; } if ((m_arguments[i].compare("-port", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("--port", Qt::CaseInsensitive) == 0)) { DebugDialog::setEnabled(true); bool ok; int p = m_arguments[i + 1].toInt(&ok); if (ok) { m_portNumber = p; } toRemove << i << i + 1; if (i + 2 < m_arguments.count()) { if (ok) { m_portRootFolder = m_arguments[i + 2]; m_serviceType = PortService; } toRemove << i + 2; } m_outputFolder = m_arguments[i + 1]; } if ((m_arguments[i].compare("-g", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-gerber", Qt::CaseInsensitive) == 0)|| (m_arguments[i].compare("--gerber", Qt::CaseInsensitive) == 0)) { m_serviceType = GerberService; DebugDialog::setEnabled(true); m_outputFolder = m_arguments[i + 1]; toRemove << i << i + 1; } if ((m_arguments[i].compare("-p", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-panel", Qt::CaseInsensitive) == 0)|| (m_arguments[i].compare("--panel", Qt::CaseInsensitive) == 0)) { m_serviceType = PanelizerService; m_panelFilename = m_arguments[i + 1]; m_outputFolder = " "; // otherwise program will bail out m_panelizerCustom = false; toRemove << i << i + 1; } if ((m_arguments[i].compare("-pc", Qt::CaseInsensitive) == 0)) { m_serviceType = PanelizerService; m_panelFilename = m_arguments[i + 1]; m_outputFolder = " "; // otherwise program will bail out m_panelizerCustom = true; toRemove << i << i + 1; } if ((m_arguments[i].compare("-i", Qt::CaseInsensitive) == 0) || (m_arguments[i].compare("-inscription", Qt::CaseInsensitive) == 0)|| (m_arguments[i].compare("--inscription", Qt::CaseInsensitive) == 0)) { m_serviceType = InscriptionService; m_panelFilename = m_arguments[i + 1]; m_outputFolder = " "; // otherwise program will bail out toRemove << i << i + 1; } if (m_arguments[i].compare("-ep", Qt::CaseInsensitive) == 0) { m_externalProcessPath = m_arguments[i + 1]; toRemove << i << i + 1; } if (m_arguments[i].compare("-eparg", Qt::CaseInsensitive) == 0) { m_externalProcessArgs << m_arguments[i + 1]; toRemove << i << i + 1; } if (m_arguments[i].compare("-epname", Qt::CaseInsensitive) == 0) { m_externalProcessName = m_arguments[i + 1]; toRemove << i << i + 1; } } while (toRemove.count() > 0) { int ix = toRemove.takeLast(); m_arguments.removeAt(ix); } m_started = false; m_lastTopmostWindow = NULL; connect(&m_activationTimer, SIGNAL(timeout()), this, SLOT(updateActivation())); m_activationTimer.setInterval(10); m_activationTimer.setSingleShot(true); QCoreApplication::setOrganizationName("Fritzing"); QCoreApplication::setOrganizationDomain("fritzing.org"); QCoreApplication::setApplicationName("Fritzing"); installEventFilter(this); // tell app where to search for plugins (jpeg export and sql lite) m_libPath = FolderUtils::getLibraryPath(); QApplication::addLibraryPath(m_libPath); /*QFile file("libpath.txt"); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&file); out << m_libPath; file.close(); }*/ // !!! translator must be installed before any widgets are created !!! m_translationPath = FolderUtils::getApplicationSubFolderPath("translations"); bool loaded = findTranslator(m_translationPath); Q_UNUSED(loaded); Q_INIT_RESOURCE(phoenixresources); MainWindow::initNames(); FSvgRenderer::initNames(); ViewLayer::initNames(); RatsnestColors::initNames(); Wire::initNames(); ItemBase::initNames(); ViewLayer::initNames(); Connector::initNames(); BinManager::initNames(); PaletteModel::initNames(); SvgIconWidget::initNames(); PinHeader::initNames(); CursorMaster::initCursors(); #ifdef Q_OS_MAC #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) || defined(QT_MAC_USE_COCOA) m_buildType = " Cocoa"; #else m_buildType = " Carbon"; #endif #else m_buildType = QString(PLATFORM_NAME).contains("64") ? "64" : "32"; #endif AboutBox::initBuildType(m_buildType); return true; } FApplication::~FApplication(void) { cleanupBackups(); clearModels(); if (m_updateDialog) { delete m_updateDialog; } FSvgRenderer::cleanup(); ViewLayer::cleanup(); ViewLayer::cleanup(); ItemBase::cleanup(); Wire::cleanup(); DebugDialog::cleanup(); ItemDrag::cleanup(); Version::cleanup(); TipsAndTricks::cleanup(); FirstTimeHelpDialog::cleanup(); TranslatorListModel::cleanup(); FolderUtils::cleanup(); SearchLineEdit::cleanup(); RatsnestColors::cleanup(); HtmlInfoView::cleanup(); SvgIconWidget::cleanup(); PartFactory::cleanup(); PartsBinView::cleanup(); PropertyDefMaster::cleanup(); CursorMaster::cleanup(); LockManager::cleanup(); PartsBinPaletteWidget::cleanup(); } void FApplication::clearModels() { if (m_referenceModel) { m_referenceModel->clearPartHash(); delete m_referenceModel; } } bool FApplication::spaceBarIsPressed() { return ((FApplication *) qApp)->m_spaceBarIsPressed; } bool FApplication::eventFilter(QObject *obj, QEvent *event) { // check whether the space bar is down. //qDebug() << "event" << event->type(); Q_UNUSED(obj); switch (event->type()) { case QEvent::MouseButtonPress: //DebugDialog::debug("button press"); m_mousePressed = true; break; case QEvent::MouseButtonRelease: //DebugDialog::debug("button release"); m_mousePressed = false; break; case QEvent::Drop: // at least under Windows, the MouseButtonRelease event is not triggered if the Drop event is triggered m_mousePressed = false; break; case QEvent::KeyPress: { //DebugDialog::debug(QString("key pressed %1 %2").arg(m_mousePressed).arg(QApplication::mouseButtons())); if (!m_mousePressed && !m_spaceBarIsPressed) { QKeyEvent * kevent = static_cast(event); if (!kevent->isAutoRepeat() && (kevent->key() == Qt::Key_Space)) { m_spaceBarIsPressed = true; //DebugDialog::debug("spacebar pressed"); CursorMaster::instance()->block(); setOverrideCursor(Qt::OpenHandCursor); emit spaceBarIsPressedSignal(true); } } } break; case QEvent::KeyRelease: { //DebugDialog::debug(QString("key released %1 %2").arg(m_mousePressed).arg(QApplication::mouseButtons())); if (m_spaceBarIsPressed) { QKeyEvent * kevent = static_cast(event); if (!kevent->isAutoRepeat() && (kevent->key() == Qt::Key_Space)) { m_spaceBarIsPressed = false; //DebugDialog::debug("spacebar pressed"); restoreOverrideCursor(); CursorMaster::instance()->unblock(); emit spaceBarIsPressedSignal(false); } } } break; default: break; } return false; } bool FApplication::event(QEvent *event) { switch (event->type()) { case QEvent::FileOpen: { QString path = static_cast(event)->file(); DebugDialog::debug(QString("file open %1").arg(path)); if (m_started) { loadNew(path); } else { m_filesToLoad.append(path); } } return true; default: return QApplication::event(event); } } bool FApplication::findTranslator(const QString & translationsPath) { QSettings settings; QString suffix = settings.value("language").toString(); if (suffix.isEmpty()) { suffix = QLocale::system().name(); // Returns the language and country of this locale as a string of the form "language_country", where language is a lowercase, two-letter ISO 639 language code, and country is an uppercase, two-letter ISO 3166 country code. } else { QLocale::setDefault(QLocale(suffix)); } bool loaded = m_translator.load(QString("fritzing_") + suffix.toLower(), translationsPath); DebugDialog::debug(QString("translation %1 loaded %2 from %3").arg(suffix).arg(loaded).arg(translationsPath)); if (loaded) { QApplication::installTranslator(&m_translator); } return loaded; } void FApplication::registerFonts() { registerFont(":/resources/fonts/DroidSans.ttf", true); registerFont(":/resources/fonts/DroidSans-Bold.ttf", false); registerFont(":/resources/fonts/DroidSansMono.ttf", false); registerFont(":/resources/fonts/OCRA.ttf", true); // "Droid Sans" // "Droid Sans Mono" /* QFontDatabase database; QStringList families = database.families ( ); foreach (QString string, families) { DebugDialog::debug(string); // should print out the name of the fonts you loaded } */ } bool FApplication::loadReferenceModel(const QString & databaseName, bool fullLoad) { m_referenceModel = new CurrentReferenceModel(); ItemBase::setReferenceModel(m_referenceModel); connect(m_referenceModel, SIGNAL(loadedPart(int, int)), this, SLOT(loadedPart(int, int))); return loadReferenceModel(databaseName, fullLoad, m_referenceModel); } bool FApplication::loadReferenceModel(const QString & databaseName, bool fullLoad, ReferenceModel * referenceModel) { QDir dir = FolderUtils::getAppPartsSubFolder(""); QString dbPath = dir.absoluteFilePath("parts.db"); QFileInfo info(dbPath); bool dbExists = info.exists(); QString sha; if (fullLoad) { // fullLoad == true means we are creating the parts database // so get the sha for last commit of the parts folder and store it in the database // this sha will be used to determine whether the user's parts folder can be updated from the remote repo sha = PartsChecker::getSha(dir.absolutePath()); if (sha.isEmpty()) { return false; } referenceModel->setSha(sha); } // loads local parts, resource parts, and any other parts in files not in the db--these part override db parts with the same moduleID bool ok = referenceModel->loadAll(databaseName, fullLoad, dbExists); if (ok && databaseName.isEmpty()) { QFile file(dir.absoluteFilePath("parts.db")); if (file.exists()) { referenceModel->loadFromDB(dbPath); } } return ok; } MainWindow * FApplication::openWindowForService(bool lockFiles, int initialTab) { // our MainWindows use WA_DeleteOnClose so this has to be added to the heap (via new) rather than the stack (for local vars) MainWindow * mainWindow = MainWindow::newMainWindow(m_referenceModel, "", false, lockFiles, initialTab); // this is also slow mainWindow->setReportMissingModules(false); mainWindow->noBackup(); mainWindow->noSchematicConversion(); return mainWindow; } int FApplication::serviceStartup() { if (m_outputFolder.isEmpty()) { return -1; } switch (m_serviceType) { case PortService: initService(); { MainWindow * sketch = MainWindow::newMainWindow(m_referenceModel, "", true, true, -1); if (sketch) { sketch->show(); sketch->clearFileProgressDialog(); } } return 1; case GedaService: runGedaService(); return 0; case DRCService: runDRCService(); return 0; case DatabaseService: runDatabaseService(); return 0; case KicadFootprintService: runKicadFootprintService(); return 0; case KicadSchematicService: runKicadSchematicService(); return 0; case GerberService: runGerberService(); return 0; case SvgService: runSvgService(); return 0; case PanelizerService: runPanelizerService(); return 0; case InscriptionService: runInscriptionService(); return 0; case ExampleService: runExampleService(); return 0; default: DebugDialog::debug("unknown service"); return -1; } } void FApplication::runGerberService() { initService(); runGerberServiceAux(); } void FApplication::runGerberServiceAux() { QDir dir(m_outputFolder); QString s = dir.absolutePath(); QStringList filters; filters << "*" + FritzingBundleExtension; QStringList filenames = dir.entryList(filters, QDir::Files); foreach (QString filename, filenames) { QString filepath = dir.absoluteFilePath(filename); MainWindow * mainWindow = openWindowForService(false, 3); m_started = true; FolderUtils::setOpenSaveFolderAux(m_outputFolder); if (mainWindow->loadWhich(filepath, false, false, false, "")) { QFileInfo info(filepath); GerberGenerator::exportToGerber(info.completeBaseName(), m_outputFolder, NULL, mainWindow->pcbView(), false); } mainWindow->setCloseSilently(true); mainWindow->close(); } } void FApplication::initService() { createUserDataStoreFolderStructures(); registerFonts(); loadReferenceModel("", false); } void FApplication::runSvgService() { initService(); runSvgServiceAux(); } void FApplication::runSvgServiceAux() { QDir dir(m_outputFolder); QString s = dir.absolutePath(); QStringList filters; filters << "*" + FritzingBundleExtension; QStringList filenames = dir.entryList(filters, QDir::Files); foreach (QString filename, filenames) { QString filepath = dir.absoluteFilePath(filename); MainWindow * mainWindow = openWindowForService(false, -1); m_started = true; FolderUtils::setOpenSaveFolderAux(m_outputFolder); if (mainWindow->loadWhich(filepath, false, false, false, "")) { QFileInfo info(filepath); QList ids; ids << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView; foreach (ViewLayer::ViewID id, ids) { QString fn = QString("%1_%2.svg").arg(info.completeBaseName()).arg(ViewLayer::viewIDNaturalName(id)); QString svgPath = dir.absoluteFilePath(fn); mainWindow->setCurrentView(id); mainWindow->exportSvg(GraphicsUtils::StandardFritzingDPI, false, false, svgPath); } } mainWindow->setCloseSilently(true); mainWindow->close(); } } void FApplication::runDatabaseService() { createUserDataStoreFolderStructures(); DebugDialog::setEnabled(true); QString partsDB = m_outputFolder; // m_outputFolder is actually a full path ending in ".db" QFile::remove(partsDB); loadReferenceModel(partsDB, true); } void FApplication::runGedaService() { try { QDir dir(m_outputFolder); QStringList filters; filters << "*.fp"; QStringList filenames = dir.entryList(filters, QDir::Files); foreach (QString filename, filenames) { QString filepath = dir.absoluteFilePath(filename); QString newfilepath = filepath; newfilepath.replace(".fp", ".svg"); GedaElement2Svg geda; QString svg = geda.convert(filepath, false); TextUtils::writeUtf8(newfilepath, svg); } } catch (const QString & msg) { DebugDialog::debug(msg); } catch (...) { DebugDialog::debug("who knows"); } } void FApplication::runDRCService() { m_started = true; initService(); DebugDialog::setEnabled(true); try { QDir dir(m_outputFolder); QStringList filters; filters << "*.fzz"; QStringList filenames = dir.entryList(filters, QDir::Files); foreach (QString filename, filenames) { QString filepath = dir.absoluteFilePath(filename); MainWindow * mainWindow = openWindowForService(false, 3); if (mainWindow == NULL) continue; mainWindow->setCloseSilently(true); if (!mainWindow->loadWhich(filepath, false, false, false, "")) { DebugDialog::debug(QString("failed to load '%1'").arg(filepath)); mainWindow->close(); delete mainWindow; continue; } mainWindow->showPCBView(); int moved = mainWindow->pcbView()->checkLoadedTraces(); if (moved > 0) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("%1 wires moved from their saved position in %2.").arg(moved).arg(filepath)); DebugDialog::debug(QString("\ncheckloadedtraces %1\n").arg(filepath)); } Panelizer::checkDonuts(mainWindow, true); Panelizer::checkText(mainWindow, true); QList boards = mainWindow->pcbView()->findBoard(); foreach (ItemBase * boardItem, boards) { mainWindow->pcbView()->selectAllItems(false, false); boardItem->setSelected(true); mainWindow->newDesignRulesCheck(false); } mainWindow->close(); delete mainWindow; } } catch (const QString & msg) { DebugDialog::debug(msg); } catch (...) { DebugDialog::debug("who knows"); } } void FApplication::runKicadFootprintService() { QDir dir(m_outputFolder); QStringList filters; filters << "*.mod"; QStringList filenames = dir.entryList(filters, QDir::Files); foreach (QString filename, filenames) { QString filepath = dir.absoluteFilePath(filename); QStringList moduleNames = KicadModule2Svg::listModules(filepath); foreach (QString moduleName, moduleNames) { KicadModule2Svg kicad; try { QString svg = kicad.convert(filepath, moduleName, false); if (svg.isEmpty()) { DebugDialog::debug("svg is empty " + filepath + " " + moduleName); continue; } foreach (QChar c, QString("<>:\"/\\|?*")) { moduleName.remove(c); } QString newFilePath = dir.absoluteFilePath(moduleName + "_" + filename); newFilePath.replace(".mod", ".svg"); if (!TextUtils::writeUtf8(newFilePath, svg)) { DebugDialog::debug("unable to open file " + newFilePath); } } catch (const QString & msg) { DebugDialog::debug(msg); } catch (...) { DebugDialog::debug("who knows"); } } } } void FApplication::runKicadSchematicService() { QDir dir(m_outputFolder); QStringList filters; filters << "*.lib"; QStringList filenames = dir.entryList(filters, QDir::Files); foreach (QString filename, filenames) { QString filepath = dir.absoluteFilePath(filename); QStringList defNames = KicadSchematic2Svg::listDefs(filepath); foreach (QString defName, defNames) { KicadSchematic2Svg kicad; try { QString svg = kicad.convert(filepath, defName); if (svg.isEmpty()) { DebugDialog::debug("svg is empty " + filepath + " " + defName); continue; } foreach (QChar c, QString("<>:\"/\\|?*")) { defName.remove(c); } //DebugDialog::debug(QString("converting %1 %2").arg(defName).arg(filename)); QString newFilePath = dir.absoluteFilePath(defName + "_" + filename); newFilePath.replace(".lib", ".svg"); QFile file(newFilePath); if (!TextUtils::writeUtf8(newFilePath, svg)) { DebugDialog::debug("unable to open file " + newFilePath); } } catch (const QString & msg) { DebugDialog::debug(msg); } catch (...) { DebugDialog::debug("who knows"); } } } } int FApplication::startup() { //DebugDialog::setEnabled(true); QString splashName = ":/resources/images/splash/splash_screen_start.png"; QDateTime now = QDateTime::currentDateTime(); if (now.date().month() == 4 && now.date().day() == 1) { QString aSplashName = ":/resources/images/splash/april1st.png"; QFileInfo info(aSplashName); if (info.exists()) { splashName = aSplashName; } } QPixmap pixmap(splashName); FSplashScreen splash(pixmap); m_splash = &splash; ProcessEventBlocker::processEvents(); // seems to need this (sometimes?) to display the splash screen initSplash(splash); ProcessEventBlocker::processEvents(); // DebugDialog::debug("Data Location: "+QDesktopServices::storageLocation(QDesktopServices::DataLocation)); registerFonts(); if (m_progressIndex >= 0) splash.showProgress(m_progressIndex, LoadProgressStart); ProcessEventBlocker::processEvents(); #ifdef Q_OS_WIN // associate .fz file with fritzing app on windows (xp only--vista is different) // TODO: don't change settings if they're already set? // TODO: only do this at install time? QSettings settings1("HKEY_CLASSES_ROOT\\Fritzing", QSettings::NativeFormat); settings1.setValue(".", "Fritzing Application"); foreach (QString extension, fritzingExtensions()) { QSettings settings2("HKEY_CLASSES_ROOT\\" + extension, QSettings::NativeFormat); settings2.setValue(".", "Fritzing"); } QSettings settings3("HKEY_CLASSES_ROOT\\Fritzing\\shell\\open\\command", QSettings::NativeFormat); settings3.setValue(".", QString("\"%1\" \"%2\"") .arg(QDir::toNativeSeparators(QApplication::applicationFilePath())) .arg("%1") ); #endif cleanFzzs(); createUserDataStoreFolderStructures(); ProcessEventBlocker::processEvents(); loadReferenceModel("", false); ProcessEventBlocker::processEvents(); QString prevVersion; { // put this in a block so that QSettings is closed QSettings settings; prevVersion = settings.value("version").toString(); QString currVersion = Version::versionString(); if(prevVersion != currVersion) { QVariant pid = settings.value("pid"); QVariant language = settings.value("language"); settings.clear(); if (!pid.isNull()) { settings.setValue("pid", pid); } if (!language.isNull()) { settings.setValue("language", language); } } } //bool fabEnabled = settings.value(ORDERFABENABLED, QVariant(false)).toBool(); //if (!fabEnabled) { QNetworkAccessManager * manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(gotOrderFab(QNetworkReply *))); manager->get(QNetworkRequest(QUrl(QString("http://fab.fritzing.org/launched%1").arg(Version::makeRequestParamsString(true))))); //} if (m_progressIndex >= 0) splash.showProgress(m_progressIndex, LoadProgressEnd); //DebugDialog::debug("after createUserDataStoreFolderStructure"); if (m_progressIndex >= 0) splash.showProgress(m_progressIndex, 0.65); ProcessEventBlocker::processEvents(); DebugDialog::debug("load something"); loadSomething(prevVersion); m_started = true; if (m_progressIndex >= 0) splash.showProgress(m_progressIndex, 0.99); ProcessEventBlocker::processEvents(); splash.hide(); m_splash = NULL; m_updateDialog = new UpdateDialog(); m_updateDialog->setRepoPath(FolderUtils::getAppPartsSubFolderPath(""), m_referenceModel->sha()); connect(m_updateDialog, SIGNAL(enableAgainSignal(bool)), this, SLOT(enableCheckUpdates(bool))); connect(m_updateDialog, SIGNAL(installNewParts()), this, SLOT(installNewParts())); checkForUpdates(false); return 0; } void FApplication::registerFont(const QString &fontFile, bool reallyRegister) { int id = QFontDatabase::addApplicationFont(fontFile); if(id > -1 && reallyRegister) { QStringList familyNames = QFontDatabase::applicationFontFamilies(id); QFileInfo finfo(fontFile); foreach (QString family, familyNames) { InstalledFonts::InstalledFontsNameMapper.insert(family, finfo.completeBaseName()); InstalledFonts::InstalledFontsList << family; DebugDialog::debug(QString("registering font family: %1 %2").arg(family).arg(finfo.completeBaseName())); } } } void FApplication::finish() { QString currVersion = Version::versionString(); QSettings settings; settings.setValue("version", currVersion); } void FApplication::loadNew(QString path) { MainWindow * mw = MainWindow::newMainWindow(m_referenceModel, path, true, true, -1); if (!mw->loadWhich(path, false, true, true, "")) { mw->close(); } mw->clearFileProgressDialog(); } void FApplication::loadOne(MainWindow * mw, QString path, int loaded) { if (loaded == 0) { mw->showFileProgressDialog(path); mw->loadWhich(path, true, true, true, ""); } else { loadNew(path); } } void FApplication::preferences() { // delay keeps OS 7 from crashing? QTimer::singleShot(30, this, SLOT(preferencesAfter())); } void FApplication::preferencesAfter() { QDir dir(m_translationPath); QStringList nameFilters; nameFilters << "*.qm"; QFileInfoList languages = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); QSettings settings; QString language = settings.value("language").toString(); if (language.isEmpty()) { language = QLocale::system().name(); } MainWindow * mainWindow = NULL; foreach (MainWindow * mw, orderedTopLevelMainWindows()) { mainWindow = mw; break; } if (mainWindow == NULL) return; // shouldn't happen (maybe on the mac) PrefsDialog prefsDialog(language, NULL); // TODO: use the topmost MainWindow as parent int ix = 0; foreach (SketchWidget * sketchWidget, mainWindow->sketchWidgets()) { prefsDialog.initViewInfo(ix++, sketchWidget->viewName(), sketchWidget->getShortName(), sketchWidget->curvyWires()); } QList platforms = mainWindow->programmingWidget()->getAvailablePlatforms(); prefsDialog.initLayout(languages, platforms); if (QDialog::Accepted == prefsDialog.exec()) { updatePrefs(prefsDialog); } } void FApplication::updatePrefs(PrefsDialog & prefsDialog) { QSettings settings; if (prefsDialog.cleared()) { settings.clear(); return; } QHash hash = prefsDialog.settings(); QList mainWindows = orderedTopLevelMainWindows(); foreach (QString key, hash.keys()) { settings.setValue(key, hash.value(key)); if (key.compare("connectedColor") == 0) { QColor c(hash.value(key)); ItemBase::setConnectedColor(c); foreach (MainWindow * mainWindow, mainWindows) { mainWindow->redrawSketch(); } } else if (key.compare("unconnectedColor") == 0) { QColor c(hash.value(key)); ItemBase::setUnconnectedColor(c); foreach (MainWindow * mainWindow, mainWindows) { mainWindow->redrawSketch(); } } else if (key.compare("wheelMapping") == 0) { ZoomableGraphicsView::setWheelMapping((ZoomableGraphicsView::WheelMapping) hash.value(key).toInt()); } else if (key.compare("autosavePeriod") == 0) { MainWindow::setAutosavePeriod(hash.value(key).toInt()); } else if (key.compare("autosaveEnabled") == 0) { MainWindow::setAutosaveEnabled(hash.value(key).toInt()); } else if (key.contains("curvy", Qt::CaseInsensitive)) { foreach (MainWindow * mainWindow, mainWindows) { foreach (SketchWidget * sketchWidget, mainWindow->sketchWidgets()) { if (key.contains(sketchWidget->getShortName())) { sketchWidget->setCurvyWires(hash.value(key).compare("1") == 0); } } } } } } void FApplication::initSplash(FSplashScreen & splash) { QPixmap progress(":/resources/images/splash/splash_progressbar.png"); m_progressIndex = splash.showPixmap(progress, "progress"); if (m_progressIndex >= 0) splash.showProgress(m_progressIndex, 0); // put this above the progress indicator QString msg1 = QObject::tr("" "© 2007-%1 Fritzing" "") .arg(Version::year()); splash.showMessage(msg1, "fritzingText", Qt::AlignLeft | Qt::AlignTop); QString msg2 = QObject::tr("" "Version %1.%2.%3 (%4%5) %6" "") .arg(Version::majorVersion()) .arg(Version::minorVersion()) .arg(Version::minorSubVersion()) .arg(Version::modifier()) .arg(Version::shortDate()) .arg(m_buildType); splash.showMessage(msg2, "versionText", Qt::AlignRight | Qt::AlignTop); #ifdef Q_OS_MAC // remove the splash screen flag on OS-X as workaround for the reported bug // https://bugreports.qt.io/browse/QTBUG-49576 splash.setWindowFlags(splash.windowFlags() & (~Qt::SplashScreen)); #endif splash.show(); } struct Thing { QString moduleID; ViewLayer::ViewID viewID; ViewLayer::ViewLayerID viewLayerID; }; void FApplication::checkForUpdates() { checkForUpdates(true); } void FApplication::checkForUpdates(bool atUserRequest) { enableCheckUpdates(false); VersionChecker * versionChecker = new VersionChecker(); QSettings settings; if (!atUserRequest) { // if I've already been notified about these updates, don't bug me again QString lastMainVersionChecked = settings.value("lastMainVersionChecked").toString(); if (!lastMainVersionChecked.isEmpty()) { versionChecker->ignore(lastMainVersionChecked, false); } QString lastInterimVersionChecked = settings.value("lastInterimVersionChecked").toString(); if (!lastInterimVersionChecked.isEmpty()) { versionChecker->ignore(lastInterimVersionChecked, true); } } QString atom = QString("http://fritzing.org/download/feed/atom/%1/%2") .arg(PLATFORM_NAME) .arg(Version::makeRequestParamsString(true)); DebugDialog::debug(atom); versionChecker->setUrl(atom); m_updateDialog->setAtUserRequest(atUserRequest); m_updateDialog->setVersionChecker(versionChecker); if (atUserRequest) { m_updateDialog->exec(); } } void FApplication::enableCheckUpdates(bool enabled) { //DebugDialog::debug("before enable check updates"); foreach (QWidget *widget, QApplication::topLevelWidgets()) { MainWindow *mainWindow = qobject_cast(widget); if (mainWindow) { mainWindow->enableCheckUpdates(enabled); } } //DebugDialog::debug("after enable check updates"); } void FApplication::createUserDataStoreFolderStructures() { FolderUtils::createUserDataStoreFolders(); FolderUtils::copyBin(BinManager::MyPartsBinLocation, BinManager::MyPartsBinTemplateLocation); FolderUtils::copyBin(BinManager::SearchBinLocation, BinManager::SearchBinTemplateLocation); PartFactory::initFolder(); } void FApplication::changeActivation(bool activate, QWidget * originator) { if (!activate) return; //DebugDialog::debug(QString("change activation %1 %2").arg(activate).arg(originator->metaObject()->className())); FritzingWindow * fritzingWindow = qobject_cast(originator); if (fritzingWindow == NULL) { fritzingWindow = qobject_cast(originator->parent()); } if (fritzingWindow == NULL) return; m_orderedTopLevelWidgets.removeOne(fritzingWindow); m_orderedTopLevelWidgets.push_front(fritzingWindow); m_activationTimer.stop(); m_activationTimer.start(); } void FApplication::updateActivation() { //DebugDialog::debug("updating activation"); FritzingWindow * prior = m_lastTopmostWindow; m_lastTopmostWindow = NULL; if (m_orderedTopLevelWidgets.count() > 0) { m_lastTopmostWindow = qobject_cast(m_orderedTopLevelWidgets.at(0)); } if (prior == m_lastTopmostWindow) { //DebugDialog::debug("done updating activation"); return; } //DebugDialog::debug(QString("last:%1, new:%2").arg((long) prior, 0, 16).arg((long) m_lastTopmostWindow.data(), 0, 16)); MainWindow * priorMainWindow = qobject_cast(prior); if (priorMainWindow != NULL) { priorMainWindow->saveDocks(); } MainWindow * lastTopmostMainWindow = qobject_cast(m_lastTopmostWindow); if (lastTopmostMainWindow != NULL) { lastTopmostMainWindow->restoreDocks(); //DebugDialog::debug("restoring active window"); } //DebugDialog::debug("done 2 updating activation"); } void FApplication::topLevelWidgetDestroyed(QObject * object) { QWidget * widget = qobject_cast(object); if (widget) { m_orderedTopLevelWidgets.removeOne(widget); } } void FApplication::closeAllWindows2() { /* Ok, near as I can tell, here's what's going on. When you quit fritzing, the function QApplication::closeAllWindows() is invoked. This goes through the top-level window list in random order and calls close() on each window, until some window says "no". The QGraphicsProxyWidgets must contain top-level windows, and at least on the mac, their response to close() seems to be setVisible(false). The random order explains why different icons disappear, or sometimes none at all. So the hack for now is to call the windows in non-random order. Eventually, maybe the SvgIconWidget class could be rewritten so that it's not using QGraphicsProxyWidget, which is really not intended for hundreds of widgets. (SvgIconWidget has been rewritten) */ // this code modified from QApplication::closeAllWindows() bool did_close = true; QWidget *w; while((w = QApplication::activeModalWidget()) && did_close) { if(!w->isVisible()) break; did_close = w->close(); } if (!did_close) return; QWidgetList list = QApplication::topLevelWidgets(); for (int i = 0; did_close && i < list.size(); ++i) { w = list.at(i); FritzingWindow *fWindow = qobject_cast(w); if (fWindow == NULL) continue; if (w->isVisible() && w->windowType() != Qt::Desktop) { did_close = w->close(); list = QApplication::topLevelWidgets(); i = -1; } } if (!did_close) return; list = QApplication::topLevelWidgets(); for (int i = 0; did_close && i < list.size(); ++i) { w = list.at(i); if (w->isVisible() && w->windowType() != Qt::Desktop) { did_close = w->close(); list = QApplication::topLevelWidgets(); i = -1; } } } bool FApplication::runAsService() { if (m_serviceType == PortService) { DebugDialog::setEnabled(true); initServer(); //return false; } return m_serviceType != NoService; } void FApplication::loadedPart(int loaded, int total) { if (total == 0) return; if (m_splash == NULL) return; //DebugDialog::debug(QString("loaded %1 %2").arg(loaded).arg(total)); if (m_progressIndex >= 0) m_splash->showProgress(m_progressIndex, LoadProgressStart + ((LoadProgressEnd - LoadProgressStart) * loaded / (double) total)); } void FApplication::externalProcessSlot(QString &name, QString &path, QStringList &args) { name = m_externalProcessName; path = m_externalProcessPath; args = m_externalProcessArgs; } bool FApplication::notify(QObject *receiver, QEvent *e) { try { //qDebug() << QString("notify %1 %2").arg(receiver->metaObject()->className()).arg(e->type()); return QApplication::notify(receiver, e); } catch (char const *str) { FMessageBox::critical(NULL, tr("Fritzing failure"), tr("Fritzing caught an exception %1 from %2 in event %3") .arg(str).arg(receiver->objectName()).arg(e->type())); } catch (std::exception& exp) { // suggested in https://code.google.com/p/fritzing/issues/detail?id=2698 qDebug() << QString("notify %1 %2").arg(receiver->metaObject()->className()).arg(e->type()); FMessageBox::critical(NULL, tr("Fritzing failure"), tr("Fritzing caught an exception from %1 in event %2: %3").arg(receiver->objectName()).arg(e->type()).arg(exp.what())); } catch (...) { FMessageBox::critical(NULL, tr("Fritzing failure"), tr("Fritzing caught an exception from %1 in event %2").arg(receiver->objectName()).arg(e->type())); } closeAllWindows2(); QApplication::exit(-1); abort(); return false; } void FApplication::loadSomething(const QString & prevVersion) { // At this point we're trying to determine what sketches to load from one of the following sources: // Only one of these sources will actually provide sketches to load and they're listed in order of priority: // We found sketch backups to recover // Files were double-clicked // The last opened sketch (obsolete) // A new blank sketch Q_UNUSED(prevVersion); initFilesToLoad(); // sets up m_filesToLoad from the command line on PC and Linux; mac uses a FileOpen event instead initBackups(); DebugDialog::debug("checking for backups"); QList sketchesToLoad = recoverBackups(); if (sketchesToLoad.isEmpty()) { // Check for double-clicked files to load DebugDialog::debug(QString("check files to load %1").arg(m_filesToLoad.count())); foreach (QString filename, m_filesToLoad) { DebugDialog::debug(QString("Loading non-service file %1").arg(filename)); MainWindow *mainWindow = MainWindow::newMainWindow(m_referenceModel, filename, true, true, -1); mainWindow->loadWhich(filename, true, true, true, ""); if (filename.endsWith(FritzingSketchExtension) || filename.endsWith(FritzingBundleExtension)) { } else { mainWindow->addDefaultParts(); } sketchesToLoad << mainWindow; } } ProcessEventBlocker::processEvents(); // Find any previously open sketches to reload if (sketchesToLoad.isEmpty()) { // new logic here, we no longer open the most recent sketch, since it can be reached in one click from the welcome page // DebugDialog::debug(QString("load last open")); //sketchesToLoad = loadLastOpenSketch(); } MainWindow * newBlankSketch = NULL; if (sketchesToLoad.isEmpty()) { DebugDialog::debug(QString("create empty sketch")); newBlankSketch = MainWindow::newMainWindow(m_referenceModel, "", true, true, -1); if (newBlankSketch) { // make sure to start an empty sketch with a board newBlankSketch->addDefaultParts(); // do this before call to show() sketchesToLoad << newBlankSketch; } } ProcessEventBlocker::processEvents(); DebugDialog::debug(QString("finish up sketch loading")); // Finish loading the sketches and show them to the user foreach (MainWindow* sketch, sketchesToLoad) { sketch->show(); sketch->clearFileProgressDialog(); } if (newBlankSketch) { newBlankSketch->hideTempPartsBin(); // new empty sketch defaults to welcome view newBlankSketch->showWelcomeView(); } } QList FApplication::loadLastOpenSketch() { QList sketches; QSettings settings; if(settings.value("lastOpenSketch").isNull()) return sketches; QString lastSketchPath = settings.value("lastOpenSketch").toString(); if(!QFileInfo(lastSketchPath).exists()) { settings.remove("lastOpenSketch"); return sketches; } DebugDialog::debug(QString("Loading last open sketch %1").arg(lastSketchPath)); settings.remove("lastOpenSketch"); // clear the preference, in case the load crashes MainWindow *mainWindow = MainWindow::newMainWindow(m_referenceModel, lastSketchPath, true, true, -1); mainWindow->loadWhich(lastSketchPath, true, true, true, ""); sketches << mainWindow; settings.setValue("lastOpenSketch", lastSketchPath); // the load works, so restore the preference return sketches; } QList FApplication::recoverBackups() { QFileInfoList backupList; LockManager::checkLockedFiles("backup", backupList, m_lockedFiles, false, LockManager::FastTime); for (int i = backupList.size() - 1; i >=0; i--) { QFileInfo fileInfo = backupList.at(i); if (!fileInfo.fileName().endsWith(FritzingSketchExtension)) { backupList.removeAt(i); } } QList recoveredSketches; if (backupList.size() == 0) return recoveredSketches; RecoveryDialog recoveryDialog(backupList); int result = (QMessageBox::StandardButton)recoveryDialog.exec(); QList fileItems = recoveryDialog.getFileList(); DebugDialog::debug(QString("Recovering %1 files from recoveryDialog").arg(fileItems.size())); foreach (QTreeWidgetItem * item, fileItems) { QString backupName = item->data(0, Qt::UserRole).value(); if (result == QDialog::Accepted && item->isSelected()) { QString originalBaseName = item->text(0); DebugDialog::debug(QString("Loading recovered sketch %1").arg(originalBaseName)); QString originalPath = item->data(1, Qt::UserRole).value(); QString fileExt; QString bundledFileName = FolderUtils::getSaveFileName(NULL, tr("Please specify an .fzz file name to save to (cancel will delete the backup)"), originalPath, tr("Fritzing (*%1)").arg(FritzingBundleExtension), &fileExt); if (!bundledFileName.isEmpty()) { MainWindow *currentRecoveredSketch = MainWindow::newMainWindow(m_referenceModel, originalBaseName, true, true, -1); currentRecoveredSketch->mainLoad(backupName, bundledFileName, true); currentRecoveredSketch->saveAsShareable(bundledFileName, true); currentRecoveredSketch->setCurrentFile(bundledFileName, true, true); recoveredSketches << currentRecoveredSketch; /* if (originalPath.startsWith(untitledFileName())) { DebugDialog::debug(QString("Comparing untitled documents: %1 %2").arg(filename).arg(untitledFileName())); QRegExp regexp("\\d+"); int ix = regexp.indexIn(filename); int untitledSketchNumber = ix >= 0 ? regexp.cap(0).toInt() : 1; untitledSketchNumber++; DebugDialog::debug(QString("%1 untitled documents open, currently thinking %2").arg(untitledSketchNumber).arg(UntitledSketchIndex)); UntitledSketchIndex = UntitledSketchIndex >= untitledSketchNumber ? UntitledSketchIndex : untitledSketchNumber; } */ } } QFile::remove(backupName); } return recoveredSketches; } void FApplication::initFilesToLoad() { for (int i = 1; i < m_arguments.length(); i++) { QFileInfo fileinfo(m_arguments[i]); if (fileinfo.exists() && !fileinfo.isDir()) { m_filesToLoad << m_arguments[i]; } } } void FApplication::initBackups() { LockManager::initLockedFiles("backup", MainWindow::BackupFolder, m_lockedFiles, LockManager::FastTime); } void FApplication::cleanupBackups() { LockManager::releaseLockedFiles(MainWindow::BackupFolder, m_lockedFiles); } void FApplication::gotOrderFab(QNetworkReply * networkReply) { int responseCode = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (responseCode == 200) { QSettings settings; settings.setValue(ORDERFABENABLED, QVariant(true)); } networkReply->manager()->deleteLater(); networkReply->deleteLater(); } void FApplication::runPanelizerService() { m_started = true; Panelizer::panelize(this, m_panelFilename, m_panelizerCustom); } void FApplication::runInscriptionService() { m_started = true; bool drc = false; bool noMessages = false; foreach (QString arg, m_arguments) { if (arg.compare("-drc", Qt::CaseInsensitive) == 0) { drc = true; } if (arg.compare("-nm", Qt::CaseInsensitive) == 0) { noMessages = true; } } Panelizer::inscribe(this, m_panelFilename, drc, noMessages); } QList FApplication::orderedTopLevelMainWindows() { QList mainWindows; foreach (QWidget * widget, m_orderedTopLevelWidgets) { MainWindow * mainWindow = qobject_cast(widget); if (mainWindow) mainWindows.append(mainWindow); } return mainWindows; } void FApplication::runExampleService() { m_started = true; initService(); QDir sketchesDir(FolderUtils::getApplicationSubFolderPath("sketches")); runExampleService(sketchesDir); } void FApplication::runExampleService(QDir & dir) { QStringList nameFilters; nameFilters << ("*" + FritzingBundleExtension); // FritzingSketchExtension QFileInfoList fileList = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); foreach (QFileInfo fileInfo, fileList) { QString path = fileInfo.absoluteFilePath(); DebugDialog::debug("sketch file " + path); MainWindow * mainWindow = openWindowForService(false, -1); if (mainWindow == NULL) continue; FolderUtils::setOpenSaveFolderAux(dir.absolutePath()); if (!mainWindow->loadWhich(path, false, false, true, "")) { DebugDialog::debug(QString("failed to load")); } else { QList items = mainWindow->selectAllObsolete(false); if (items.count() > 0) { mainWindow->swapObsolete(false, items); } mainWindow->saveAsAux(path); // path + "z" mainWindow->setCloseSilently(true); mainWindow->close(); } } QFileInfoList dirList = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks); foreach (QFileInfo dirInfo, dirList) { QDir dir(dirInfo.filePath()); runExampleService(dir); } } void FApplication::cleanFzzs() { QHash lockedFiles; QString folder; LockManager::initLockedFiles("fzz", folder, lockedFiles, LockManager::SlowTime); QFileInfoList backupList; LockManager::checkLockedFiles("fzz", backupList, lockedFiles, true, LockManager::SlowTime); LockManager::releaseLockedFiles(folder, lockedFiles); } void FApplication::initServer() { FMessageBox::BlockMessages = true; m_fServer = new FServer(this); connect(m_fServer, SIGNAL(newConnection(int)), this, SLOT(newConnection(int))); m_fServer->listen(QHostAddress::Any, m_portNumber); } void FApplication::newConnection(int socketDescription) { FServerThread *thread = new FServerThread(socketDescription, this); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); connect(thread, SIGNAL(doCommand(const QString &, const QString &, QString &, int &)), this, SLOT(doCommand(const QString &, const QString &, QString &, int &)), Qt::BlockingQueuedConnection); thread->start(); } void FApplication::doCommand(const QString & command, const QString & params, QString & result, int & status) { status = 200; result = ""; QDir dir(m_portRootFolder); QString subfolder = params; if (command.endsWith("tcp")) { subfolder = TextUtils::getRandText(); dir.mkdir(subfolder); } dir.cd(subfolder); m_outputFolder = dir.absolutePath(); if (command.endsWith("tcp")) { QEventLoop loop; QNetworkAccessManager networkManager; QObject::connect(&networkManager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit())); QUrl url = params; QNetworkReply* reply = networkManager.get(QNetworkRequest(url)); loop.exec(); status = 404; if (reply->error() == QNetworkReply::NoError) { if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt() == 200) { if (reply->isReadable()) { int buffersize = 8192; QStringList components = params.split("/"); QString filename = components.last(); QFile file(dir.absoluteFilePath(filename)); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { status = 200; while (reply->bytesAvailable() >= buffersize) { QByteArray bytes = reply->read(buffersize); file.write(bytes); } if (reply->bytesAvailable() > 0) { file.write(reply->readAll()); } file.close(); } else { result = "unable to save to local file"; } } else { result = "response unreadable"; } } else { result = QString("bad response from url server %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toUInt()); } } else { result = QString("url get failed %1").arg(reply->error()); } reply->deleteLater(); } if (!result.isEmpty()) { // network problem return; } if (command.startsWith("svg")) { runSvgServiceAux(); QStringList nameFilters; nameFilters << ("*.svg"); QFileInfoList fileList = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); if (fileList.count() > 0) { QFile file(fileList.at(0).absoluteFilePath()); if (file.open(QFile::ReadOnly)) { result = file.readAll(); } } } else if (command.startsWith("gerber")) { runGerberServiceAux(); QStringList nameFilters; nameFilters << ("*.txt"); QFileInfoList fileList = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); if (fileList.count() > 0) { QFile file(fileList.at(0).absoluteFilePath()); if (file.open(QFile::ReadOnly)) { result = file.readAll(); } } } if (command.endsWith("tcp")) { QStringList skipSuffixes(".zip"); skipSuffixes << ".fzz"; QString filename = dir.absoluteFilePath(subfolder + ".zip"); if (FolderUtils::createZipAndSaveTo(dir, filename, skipSuffixes)) { status = 200; result = filename; } else { status = 500; result = "local zip failure"; } } } void FApplication::regeneratePartsDatabase() { QMessageBox messageBox(NULL); messageBox.setWindowTitle(tr("Regenerate parts database?")); messageBox.setText(tr("Regenerating the parts database will take some minutes and you will have to restart Fritzing\n\n") + tr("Would you like to regenerate the parts database?\n") ); messageBox.setInformativeText("This option is a last resort in case Fritzing's is more-or-less unable to display parts. " "You may be better off downloading the latest Fritzing release."); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); messageBox.setDefaultButton(QMessageBox::Yes); messageBox.setIcon(QMessageBox::Question); messageBox.setWindowModality(Qt::WindowModal); messageBox.setButtonText(QMessageBox::Yes, tr("Regenerate")); messageBox.setButtonText(QMessageBox::No, tr("Cancel")); if ((QMessageBox::StandardButton) messageBox.exec() != QMessageBox::Yes) { return; } FileProgressDialog * fileProgressDialog = new FileProgressDialog(tr("Regenerating parts database..."), 0, NULL); // these don't seem very accurate (i.e. when progress is at 100%, there is still a lot of work pending) // so we are leaving progress indeterminate at present //connect(referenceModel, SIGNAL(partsToLoad(int)), fileProgressDialog, SLOT(setMaximum(int))); //connect(referenceModel, SIGNAL(loadedPart(int,int)), fileProgressDialog, SLOT(setValue(int))); regeneratePartsDatabaseAux(fileProgressDialog); } void FApplication::regeneratePartsDatabaseAux(QDialog * progressDialog) { ReferenceModel * referenceModel = new CurrentReferenceModel(); QDir dir = FolderUtils::getAppPartsSubFolder(""); QString dbPath = dir.absoluteFilePath("parts.db"); RegenerateDatabaseThread *thread = new RegenerateDatabaseThread(dbPath, progressDialog, referenceModel); connect(thread, SIGNAL(finished()), this, SLOT(regenerateDatabaseFinished())); FMessageBox::BlockMessages = true; thread->start(); } void FApplication::regenerateDatabaseFinished() { RegenerateDatabaseThread * thread = qobject_cast(sender()); if (thread == NULL) return; QDialog * progressDialog = thread->progressDialog(); if (progressDialog == m_updateDialog) { m_updateDialog->installFinished(thread->error()); } else { if (thread->error().isEmpty()) { QTimer::singleShot(50, Qt::PreciseTimer, this, SLOT(quit())); } else { thread->referenceModel()->deleteLater(); QMessageBox::warning(NULL, QObject::tr("Regenerate database failed"), thread->error()); } if (progressDialog) { thread->progressDialog()->close(); thread->progressDialog()->deleteLater(); } } thread->deleteLater(); } void FApplication::installNewParts() { regeneratePartsDatabaseAux(m_updateDialog); } fritzing-app-0.9.3b/src/fapplication.h000066400000000000000000000146001272363601300177140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6940 $: $Author: irascibl@gmail.com $: $Date: 2013-03-26 14:00:34 +0100 (Di, 26. Mrz 2013) $ ********************************************************************/ #ifndef FAPPLICATION_H #define FAPPLICATION_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "referencemodel/referencemodel.h" class FileProgressDialog; class FServer : public QTcpServer { Q_OBJECT public: FServer(QObject *parent = 0); signals: void newConnection(int socketDescriptor); protected: void incomingConnection(int socketDescriptor); }; class FServerThread : public QThread { Q_OBJECT public: FServerThread(int socketDescriptor, QObject *parent); void run(); void setDone(); signals: void error(QTcpSocket::SocketError socketError); void doCommand(const QString & command, const QString & params, QString & result, int & status); protected: void writeResponse(QTcpSocket *, int code, const QString & codeString, const QString & mimeType, const QString & message); protected: int m_socketDescriptor; bool m_done; protected: static QMutex m_busy; }; //////////////////////////////////////////////////// class RegenerateDatabaseThread : public QThread { Q_OBJECT public: RegenerateDatabaseThread(const QString & dbFileName, QDialog *progressDialog, ReferenceModel *referenceModel); const QString error() const; QDialog * progressDialog() const; ReferenceModel * referenceModel() const; protected: void run() Q_DECL_OVERRIDE; protected: QString m_dbFileName; QString m_error; QDialog * m_progressDialog; ReferenceModel * m_referenceModel; }; //////////////////////////////////////////////////// class FApplication : public QApplication { Q_OBJECT public: FApplication(int & argc, char ** argv); ~FApplication(void); public: bool init(); int startup(); int serviceStartup(); void finish(); bool loadReferenceModel(const QString & databaseName, bool fullLoad); bool loadReferenceModel(const QString & databaseName, bool fullLoad, ReferenceModel * referenceModel); void registerFonts(); class MainWindow * openWindowForService(bool lockFiles, int initialTab); bool runAsService(); public: static bool spaceBarIsPressed(); signals: void spaceBarIsPressedSignal(bool); public slots: void preferences(); void preferencesAfter(); void checkForUpdates(); void checkForUpdates(bool atUserRequest); void enableCheckUpdates(bool enabled); void createUserDataStoreFolderStructures(); void changeActivation(bool activate, QWidget * originator); void updateActivation(); void topLevelWidgetDestroyed(QObject *); void closeAllWindows2(); void loadedPart(int loaded, int total); void externalProcessSlot(QString & name, QString & path, QStringList & args); void gotOrderFab(QNetworkReply *); void newConnection(int socketDescriptor); void doCommand(const QString & command, const QString & params, QString & result, int & status); void regeneratePartsDatabase(); void regenerateDatabaseFinished(); void installNewParts(); protected: bool eventFilter(QObject *obj, QEvent *event); bool event(QEvent *event); bool findTranslator(const QString & translationsPath); void loadNew(QString path); void loadOne(class MainWindow *, QString path, int loaded); void initSplash(class FSplashScreen & splash); void registerFont(const QString &fontFile, bool reallyRegister); void clearModels(); bool notify(QObject *receiver, QEvent *e); void initService(); void runDRCService(); void runGedaService(); void runDatabaseService(); void runKicadFootprintService(); void runKicadSchematicService(); void runGerberService(); void runGerberServiceAux(); void runSvgService(); void runSvgServiceAux(); void runPanelizerService(); void runInscriptionService(); void runExampleService(); void runExampleService(QDir &); QList recoverBackups(); QList loadLastOpenSketch(); void doLoadPrevious(MainWindow *); void loadSomething(const QString & previousVersion); void initFilesToLoad(); void initBackups(); void cleanupBackups(); void updatePrefs(class PrefsDialog & prefsDialog); QList orderedTopLevelMainWindows(); void cleanFzzs(); void initServer(); void regeneratePartsDatabaseAux(QDialog * progressDialog); enum ServiceType { PanelizerService = 1, InscriptionService, GerberService, GedaService, KicadSchematicService, KicadFootprintService, ExampleService, DatabaseService, SvgService, PortService, DRCService, NoService }; protected: bool m_spaceBarIsPressed; bool m_mousePressed; QTranslator m_translator; ReferenceModel * m_referenceModel; bool m_started; QStringList m_filesToLoad; QString m_libPath; QString m_translationPath; class UpdateDialog * m_updateDialog; QTimer m_activationTimer; QPointer m_lastTopmostWindow; QList m_orderedTopLevelWidgets; QStringList m_arguments; QStringList m_externalProcessArgs; QString m_externalProcessName; QString m_externalProcessPath; ServiceType m_serviceType; int m_progressIndex; class FSplashScreen * m_splash; QString m_outputFolder; QString m_portRootFolder; QString m_panelFilename; QHash m_lockedFiles; bool m_panelizerCustom; int m_portNumber; FServer * m_fServer; QString m_buildType; }; #endif fritzing-app-0.9.3b/src/fsplashscreen.cpp000066400000000000000000000133561272363601300204450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision$: $Author$: $Date$ ********************************************************************/ #include "fsplashscreen.h" #include "utils/misc.h" #include "debugdialog.h" #include #include #include #include FSplashScreen::FSplashScreen(const QPixmap & pixmap, Qt::WindowFlags f ) : QSplashScreen(pixmap, f) { QFile file(":/resources/images/splash/splash.xml"); if (!file.open(QFile::ReadOnly | QFile::Text)) { DebugDialog::debug("unable to load splash.xml: " + file.errorString()); return; } QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse splash.xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement root = domDocument.documentElement(); int sliceDelay = root.attribute("sliceDelaySeconds", "0").toInt(); if (sliceDelay > 0) { QTimer::singleShot(sliceDelay * 1000, this, SLOT(displaySlice())); } QDomElement item = root.firstChildElement("item"); while (!item.isNull()) { QString id = item.attribute("id"); if (!id.isEmpty()) { int x = item.attribute("x", "0").toInt(); int y = item.attribute("y", "0").toInt(); int width = item.attribute("width", "0").toInt(); int height = item.attribute("height", "0").toInt(); QString colorName = item.attribute("color"); MessageThing * messageThing = new MessageThing(); messageThing->rect.setCoords(x, y, x + width, y + height); if (colorName.isEmpty()) { messageThing->color = QColor(0, 0, 0); } else { messageThing->color.setNamedColor(colorName); } m_items.insert(id, messageThing); } item = item.nextSiblingElement("item"); } } FSplashScreen::~FSplashScreen() { foreach (MessageThing * messageThing, m_messages) { delete messageThing; } foreach (MessageThing * messageThing, m_items) { delete messageThing; } foreach (PixmapThing * pixmapThing, m_pixmaps) { delete pixmapThing; } } void FSplashScreen::showMessage(const QString &message, const QString & id, int alignment) { MessageThing * itemMessageThing = m_items.value(id); if (itemMessageThing == NULL) return; MessageThing * messageThing = new MessageThing; messageThing->alignment = alignment; messageThing->color = itemMessageThing->color; messageThing->rect = itemMessageThing->rect;; messageThing->message = message; m_messages.append(messageThing); repaint(); } int FSplashScreen::showPixmap(const QPixmap & pixmap, const QString & id) { MessageThing * itemMessageThing = m_items.value(id); if (itemMessageThing == NULL) return -1; PixmapThing * pixmapThing = new PixmapThing; pixmapThing->rect = QRect(itemMessageThing->rect.topLeft(), pixmap.size()); pixmapThing->pixmap = pixmap; m_pixmaps.append(pixmapThing); repaint(); return m_pixmaps.count() - 1; } void FSplashScreen::showProgress(int index, double progress) { if (index < 0) return; if (index >= m_pixmaps.count()) return; int w = (int) (this->width() * progress); PixmapThing * pixmapThing = m_pixmaps[index]; pixmapThing->rect.setWidth(w); repaint(); } void FSplashScreen::drawContents ( QPainter * painter ) { // copied from QSplashScreen::drawContents painter->setRenderHint ( QPainter::Antialiasing, true ); // TODO: might need to be in the stylesheet? // pixmaps first, since they go beneath text foreach (PixmapThing * pixmapThing, m_pixmaps) { painter->drawPixmap(pixmapThing->rect, pixmapThing->pixmap); //DebugDialog::debug(QString("pixmapthing %1 %2").arg(pixmapThing->pixmap.width()).arg(pixmapThing->pixmap.height()), pixmapThing->rect); } foreach (MessageThing * messageThing, m_messages) { painter->setPen(messageThing->color); if (Qt::mightBeRichText(messageThing->message)) { QTextDocument doc; #ifdef QT_NO_TEXTHTMLPARSER doc.setPlainText(messageThing->message); #else doc.setHtml(messageThing->message); #endif doc.setTextWidth(messageThing->rect.width()); QTextCursor cursor(&doc); cursor.select(QTextCursor::Document); QTextBlockFormat fmt; fmt.setAlignment(Qt::Alignment(messageThing->alignment)); cursor.mergeBlockFormat(fmt); painter->save(); painter->translate(messageThing->rect.topLeft()); doc.drawContents(painter); painter->restore(); } else { painter->drawText(messageThing->rect, messageThing->alignment, messageThing->message); } } } void FSplashScreen::displaySlice() { QString fname = ":/resources/images/splash/fab_slice%1.png"; int highest = 0; for (int i = 1; i < 100; i++) { QFileInfo info(fname.arg(i)); if (info.exists()) { highest = i; } else break; } if (highest == 0) return; QPixmap bar(":/resources/images/splash/fab_logo_bar.png"); if (bar.isNull()) return; srand ( time(NULL) ); int ix = (rand() % highest) + 1; QPixmap slice(fname.arg(ix)); if (slice.isNull()) return; showPixmap(bar, "logoBar"); showPixmap(slice, "slice"); } fritzing-app-0.9.3b/src/fsplashscreen.h000066400000000000000000000034361272363601300201100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision$: $Author$: $Date$ ********************************************************************/ #ifndef FSPLASHSCREEN_H #define FSPLASHSCREEN_H #include #include #include struct MessageThing { QString message; QRect rect; int alignment; QColor color; }; struct PixmapThing { QPixmap pixmap; QRect rect; }; class FSplashScreen : public QSplashScreen { Q_OBJECT public: FSplashScreen(const QPixmap & pixmap = QPixmap(), Qt::WindowFlags f = 0); ~FSplashScreen(); void showMessage(const QString &message, const QString & id, int alignment = Qt::AlignLeft); int showPixmap(const QPixmap &pixmap, const QString & id); void showProgress(int index, double progress); // progress is from 0.0 to 1.0; protected: void drawContents ( QPainter * painter ); public slots: void displaySlice(); protected: QPixmap m_pixmap; QList m_messages; QList m_pixmaps; QHash m_items; }; #endif fritzing-app-0.9.3b/src/fsvgrenderer.cpp000066400000000000000000000653331272363601300203030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6988 $: $Author: irascibl@gmail.com $: $Date: 2013-04-26 12:05:48 +0200 (Fr, 26. Apr 2013) $ ********************************************************************/ #include "fsvgrenderer.h" #include "debugdialog.h" #include "svg/svgfilesplitter.h" #include "utils/textutils.h" #include "utils/graphicsutils.h" #include "utils/folderutils.h" #include "connectors/svgidlayer.h" #include #include #include #include #include #include ///////////////////////////////////////////// QString FSvgRenderer::NonConnectorName("nonconn"); static ConnectorInfo VanillaConnectorInfo; FSvgRenderer::FSvgRenderer(QObject * parent) : QSvgRenderer(parent) { m_defaultSizeF = QSizeF(0,0); } FSvgRenderer::~FSvgRenderer() { clearConnectorInfoHash(m_connectorInfoHash); clearConnectorInfoHash(m_nonConnectorInfoHash); } void FSvgRenderer::initNames() { VanillaConnectorInfo.gotPath = VanillaConnectorInfo.gotCircle = false; } void FSvgRenderer::clearConnectorInfoHash(QHash & hash) { foreach (ConnectorInfo * connectorInfo, hash.values()) { delete connectorInfo; } hash.clear(); } void FSvgRenderer::cleanup() { } QByteArray FSvgRenderer::loadSvg(const QString & filename) { LoadInfo loadInfo; loadInfo.filename = filename; return loadSvg(loadInfo); } QByteArray FSvgRenderer::loadSvg(const LoadInfo & loadInfo) { if (!QFileInfo(loadInfo.filename).exists() || !QFileInfo(loadInfo.filename).isFile()) { return QByteArray(); } QFile file(loadInfo.filename); if (!file.open(QFile::ReadOnly | QFile::Text)) { return QByteArray(); } QByteArray contents = file.readAll(); file.close(); if (contents.length() <= 0) return QByteArray(); return loadAux(contents, loadInfo); } bool FSvgRenderer::loadSvgString(const QString & svg) { QByteArray byteArray(svg.toUtf8()); QByteArray result = loadSvg(byteArray, "", true); return (!result.isEmpty()); } bool FSvgRenderer::loadSvgString(const QString & svg, QString & newSvg) { QByteArray byteArray(svg.toUtf8()); QByteArray result = loadSvg(byteArray, "", true); newSvg = QString(result); return (!result.isEmpty()); } QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const QString & filename, bool findNonConnectors) { LoadInfo loadInfo; loadInfo.filename = filename; loadInfo.findNonConnectors = findNonConnectors; return loadAux(contents, loadInfo); } QByteArray FSvgRenderer::loadSvg(const QByteArray & contents, const LoadInfo & loadInfo) { return loadAux(contents, loadInfo); } QByteArray FSvgRenderer::loadAux(const QByteArray & theContents, const LoadInfo & loadInfo) { QByteArray cleanContents(theContents); bool cleaned = false; QString string(cleanContents); if (TextUtils::fixMuch(string, false)) { cleaned = true; } if (TextUtils::fixPixelDimensionsIn(string)) { cleaned = true; } if (cleaned) { cleanContents = string.toUtf8(); } if (loadInfo.connectorIDs.count() > 0 || !loadInfo.setColor.isEmpty() || loadInfo.findNonConnectors) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(cleanContents, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("renderer loadAux failed %1 %2 %3 %4").arg(loadInfo.filename).arg(errorStr).arg(errorLine).arg(errorColumn)); } bool resetContents = false; QDomElement root = doc.documentElement(); if (!loadInfo.setColor.isEmpty()) { QDomElement element = TextUtils::findElementWithAttribute(root, "id", loadInfo.colorElementID); if (!element.isNull()) { QStringList exceptions; exceptions << "black" << "#000000"; SvgFileSplitter::fixColorRecurse(element, loadInfo.setColor, exceptions); resetContents = true; } } if (loadInfo.connectorIDs.count() > 0) { bool init = initConnectorInfo(doc, loadInfo); resetContents = resetContents || init; } if (loadInfo.findNonConnectors) { initNonConnectorInfo(doc, loadInfo.filename); } if (resetContents) { cleanContents = TextUtils::removeXMLEntities(doc.toString()).toUtf8(); } } /* cleanContents = doc.toByteArray(); //QFile file("all.txt"); //if (file.open(QIODevice::Append)) { //QTextStream t(&file); //t << cleanContents; //file.close(); //} */ //DebugDialog::debug(cleanContents.data()); return finalLoad(cleanContents, loadInfo.filename); } QByteArray FSvgRenderer::finalLoad(QByteArray & cleanContents, const QString & filename) { QXmlStreamReader xml(cleanContents); bool result = determineDefaultSize(xml); if (!result) { return QByteArray(); } result = QSvgRenderer::load(cleanContents); if (result) { m_filename = filename; return cleanContents; } return QByteArray(); } bool FSvgRenderer::fastLoad(const QByteArray & contents) { return QSvgRenderer::load(contents); } const QString & FSvgRenderer::filename() { return m_filename; } QPixmap * FSvgRenderer::getPixmap(QSvgRenderer * renderer, QSize size) { QPixmap *pixmap = new QPixmap(size); pixmap->fill(Qt::transparent); QPainter painter(pixmap); // preserve aspect ratio QSizeF def = renderer->defaultSize(); FSvgRenderer * frenderer = qobject_cast(renderer); if (frenderer) { def = frenderer->defaultSizeF(); } double newW = size.width(); double newH = newW * def.height() / def.width(); if (newH > size.height()) { newH = size.height(); newW = newH * def.width() / def.height(); } QRectF bounds((size.width() - newW) / 2.0, (size.height() - newH) / 2.0, newW, newH); renderer->render(&painter, bounds); painter.end(); return pixmap; } bool FSvgRenderer::determineDefaultSize(QXmlStreamReader & xml) { QSizeF size = parseForWidthAndHeight(xml); m_defaultSizeF = QSizeF(size.width() * GraphicsUtils::SVGDPI, size.height() * GraphicsUtils::SVGDPI); return (size.width() != 0 && size.height() != 0); } QSizeF FSvgRenderer::parseForWidthAndHeight(QXmlStreamReader & xml) { QSizeF size = TextUtils::parseForWidthAndHeight(xml); if (size.width() != 0 && size.height() != 0) return size; QIODevice * device = xml.device(); DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); DebugDialog::debug("bad width and/or bad height in svg:"); if (device) { device->reset(); QString string(device->readAll()); DebugDialog::debug(string); } DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); return size; } QSizeF FSvgRenderer::defaultSizeF() { if (m_defaultSizeF.width() == 0 && m_defaultSizeF.height() == 0) { return defaultSize(); } return m_defaultSizeF; } void FSvgRenderer::initNonConnectorInfo(QDomDocument & domDocument, const QString & filename) { clearConnectorInfoHash(m_nonConnectorInfoHash); QDomElement root = domDocument.documentElement(); initNonConnectorInfoAux(root, filename); } void FSvgRenderer::initNonConnectorInfoAux(QDomElement & element, const QString & filename) { QString id = element.attribute("id"); if (id.startsWith(NonConnectorName, Qt::CaseInsensitive)) { ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, filename, false); m_nonConnectorInfoHash.insert(id, connectorInfo); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { initNonConnectorInfoAux(child, filename); child = child.nextSiblingElement(); } } bool FSvgRenderer::initConnectorInfo(QDomDocument & domDocument, const LoadInfo & loadInfo) { bool result = false; clearConnectorInfoHash(m_connectorInfoHash); QDomElement root = domDocument.documentElement(); initConnectorInfoAux(root, loadInfo); if (loadInfo.terminalIDs.count() > 0) { initTerminalInfoAux(root, loadInfo); } if (loadInfo.legIDs.count() > 0) { initLegInfoAux(root, loadInfo, result); } return result; } void FSvgRenderer::initLegInfoAux(QDomElement & element, const LoadInfo & loadInfo, bool & gotOne) { QString id = element.attribute("id"); if (!id.isEmpty()) { int ix = loadInfo.legIDs.indexOf(id); if (ix >= 0) { //DebugDialog::debug("init leg info " + id); //foreach (QString lid, legIDs) { // DebugDialog::debug("\tleg id:" + lid); //} element.setTagName("g"); // don't want this element to actually be drawn gotOne = true; ConnectorInfo * connectorInfo = m_connectorInfoHash.value(loadInfo.connectorIDs.at(ix), NULL); if (connectorInfo) { //QString temp; //QTextStream stream(&temp); //element.save(stream, 0); //DebugDialog::debug("\t matched " + connectorIDs.at(ix) + " " + temp); connectorInfo->legMatrix = TextUtils::elementToMatrix(element); connectorInfo->legColor = element.attribute("stroke"); connectorInfo->legLine = QLineF(); connectorInfo->legStrokeWidth = 0; initLegInfoAux(element, connectorInfo); } // don't return here, might miss other legs } } QDomElement child = element.firstChildElement(); while (!child.isNull()) { initLegInfoAux(child, loadInfo, gotOne); child = child.nextSiblingElement(); } } bool FSvgRenderer::initLegInfoAux(QDomElement & element, ConnectorInfo * connectorInfo) { bool ok; double sw = element.attribute("stroke-width").toDouble(&ok); if (!ok) return false; double x1 = element.attribute("x1").toDouble(&ok); if (!ok) return false; double y1 = element.attribute("y1").toDouble(&ok); if (!ok) return false; double x2 = element.attribute("x2").toDouble(&ok); if (!ok) return false; double y2 = element.attribute("y2").toDouble(&ok); if (!ok) return false; connectorInfo->legStrokeWidth = sw; connectorInfo->legLine = QLineF(x1, y1, x2, y2); return true; } void FSvgRenderer::initTerminalInfoAux(QDomElement & element, const LoadInfo & loadInfo) { QString id = element.attribute("id"); if (!id.isEmpty()) { int ix = loadInfo.terminalIDs.indexOf(id); if (ix >= 0) { ConnectorInfo * connectorInfo = m_connectorInfoHash.value(loadInfo.connectorIDs.at(ix), NULL); if (connectorInfo) { connectorInfo->terminalMatrix = TextUtils::elementToMatrix(element); } // don't return here, might miss other terminal ids } } QDomElement child = element.firstChildElement(); while (!child.isNull()) { initTerminalInfoAux(child, loadInfo); child = child.nextSiblingElement(); } } void FSvgRenderer::initConnectorInfoAux(QDomElement & element, const LoadInfo & loadInfo) { QString id = element.attribute("id"); if (!id.isEmpty()) { if (loadInfo.connectorIDs.contains(id)) { ConnectorInfo * connectorInfo = initConnectorInfoStruct(element, loadInfo.filename, loadInfo.parsePaths); m_connectorInfoHash.insert(id, connectorInfo); } // don't return here, might miss other connectors } QDomElement child = element.firstChildElement(); while (!child.isNull()) { initConnectorInfoAux(child, loadInfo); child = child.nextSiblingElement(); } } ConnectorInfo * FSvgRenderer::initConnectorInfoStruct(QDomElement & connectorElement, const QString & filename, bool parsePaths) { ConnectorInfo * connectorInfo = new ConnectorInfo(); connectorInfo->radius = connectorInfo->strokeWidth = 0; connectorInfo->gotPath = connectorInfo->gotCircle = false; if (connectorElement.isNull()) return connectorInfo; connectorInfo->matrix = TextUtils::elementToMatrix(connectorElement); initConnectorInfoStructAux(connectorElement, connectorInfo, filename, parsePaths); return connectorInfo; } bool FSvgRenderer::initConnectorInfoStructAux(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename, bool parsePaths) { if (element.nodeName().compare("circle") == 0) { return initConnectorInfoCircle(element, connectorInfo, filename); } if (element.nodeName().compare("path") == 0) { if (!parsePaths) return false; return initConnectorInfoPath(element, connectorInfo, filename); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { if (initConnectorInfoStructAux(child, connectorInfo, filename, parsePaths)) return true; child = child.nextSiblingElement(); } return false; } bool FSvgRenderer::initConnectorInfoPath(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename) { QString id = element.attribute("id"); if (id.isEmpty()) return false; // shouldn't be here QString stroke = element.attribute("stroke"); if (stroke == "none") return false; // cannot be a circle with a hole in the center connectorInfo->gotPath = true; double sw = TextUtils::getStrokeWidth(element, 1); if (!stroke.isEmpty()) element.setAttribute("stroke", "black"); QString fill = element.attribute("fill"); if (!fill.isEmpty() && (fill != "none")) element.setAttribute("fill", "black"); QDomDocument doc = element.ownerDocument(); FSvgRenderer renderer; QByteArray byteArray = doc.toByteArray(); renderer.finalLoad(byteArray, filename); QRectF bounds = renderer.boundsOnElement(id); static const int dim = 101; int width = dim; int height = dim; if (bounds.width() - bounds.height() / (bounds.width() + bounds.height()) > .01) { height = (int) (float(dim) * bounds.height() / bounds.width()); } else if (bounds.height() - bounds.width() / (bounds.width() + bounds.height()) > .01) { width = (int) (float(dim) * bounds.width() / bounds.height()); } QImage image(width, height, QImage::Format_Mono); image.fill(0xffffffff); QPainter painter; painter.begin(&image); renderer.render(&painter, id); painter.end(); #ifndef QT_NO_DEBUG //image.save(FolderUtils::getUserDataStorePath("") + "/donutcheck.png"); #endif if (!fill.isEmpty()) element.setAttribute("fill", fill); if (!stroke.isEmpty()) element.setAttribute("stroke", stroke); //DebugDialog::debug(QString("checking connector path %1").arg(id)); int lxStart = -1, lxEnd = -1, rxStart = -1, rxEnd = -1; for (int x = 0; x < width; x++) { if (image.pixel(x, height / 2) == 0xff000000) { lxStart = x; break; } } if (lxStart < 0) return false; if (lxStart >= width / 2) return false; // not an ellipse for (int x = lxStart + 1; x < width; x++) { if (image.pixel(x, height / 2) == 0xff000000) { lxEnd = x; } else break; } if (lxEnd < 0) return false; if (lxEnd > width / 2) return false; // not an ellipse for (int x = lxEnd + 1; x < width; x++) { if (image.pixel(x, height / 2) == 0xff000000) { rxStart = x; break; } } if (rxStart < 0) return false; if (rxStart < height / 2) return false; // not an ellipse; for (int x = rxStart + 1; x < width; x++) { if (image.pixel(x, height / 2) == 0xff000000) { rxEnd = x; } else break; } if (rxEnd < 0) return false; if (qAbs(rxEnd - rxStart - (lxEnd - lxStart)) > 1) return false; // sides are not symmetric int lyStart = -1, lyEnd = -1, ryStart = -1, ryEnd = -1; for (int y = 0; y < height; y++) { if (image.pixel(width / 2, y) == 0xff000000) { lyStart = y; break; } } if (lyStart < 0) return false; if (lyStart >= height / 2) return false; // not an ellipse for (int y = lyStart + 1; y < height; y++) { if (image.pixel(width / 2, y) == 0xff000000) { lyEnd = y; } else break; } if (lyEnd < 0) return false; if (lyEnd > height / 2) return false; // not an ellipse for (int y = lyEnd + 1; y < height; y++) { if (image.pixel(width / 2, y) == 0xff000000) { ryStart = y; break; } } if (ryStart < 0) return false; if (ryStart < height / 2) return false; // not an ellipse for (int y = ryStart + 1; y < height; y++) { if (image.pixel(width / 2, y) == 0xff000000) { ryEnd = y; } else break; } if (ryEnd < 0) return false; if (qAbs((ryEnd - ryStart) - (lyEnd - lyStart)) > 1) return false; // tops not symmetric if (qAbs((rxStart - lxEnd) - (ryStart - lyEnd)) > 1) return false; // inner drill hole is not circular double r = (qMin(bounds.width(), bounds.height()) - sw) / 2; QMatrix matrix = TextUtils::elementToMatrix(element); if (!matrix.isIdentity()) { QRectF r1(0,0,r,r); QRectF r2 = matrix.mapRect(r1); if (r2.width() != r1.width()) { r = r2.width(); sw = sw * r2.width() / r1.width(); } } connectorInfo->radius = r; connectorInfo->strokeWidth = sw; connectorInfo->gotCircle = true; //DebugDialog::debug(QString("got connector path %1").arg(id)); return true; } bool FSvgRenderer::initConnectorInfoCircle(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename) { Q_UNUSED(filename); bool ok; element.attribute("cx").toDouble(&ok); if (!ok) return false; element.attribute("cy").toDouble(&ok); if (!ok) return false; double r = element.attribute("r").toDouble(&ok); if (!ok) return false; double sw = TextUtils::getStrokeWidth(element, 1); QMatrix matrix = TextUtils::elementToMatrix(element); if (!matrix.isIdentity()) { QRectF r1(0,0,r,r); QRectF r2 = matrix.mapRect(r1); if (r2.width() != r1.width()) { r = r2.width(); sw = sw * r2.width() / r1.width(); } } //DebugDialog::debug("got a circle"); connectorInfo->gotCircle = true; //connectorInfo->cbounds.setRect(cx - r - (sw / 2.0), cy - r - (sw / 2.0), (r * 2) + sw, (r * 2) + sw); connectorInfo->radius = r; connectorInfo->strokeWidth = sw; return true; } ConnectorInfo * FSvgRenderer::getConnectorInfo(const QString & connectorID) { return m_connectorInfoHash.value(connectorID, &VanillaConnectorInfo); } bool FSvgRenderer::setUpConnector(SvgIdLayer * svgIdLayer, bool ignoreTerminalPoint, ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (svgIdLayer == NULL) return false; //if (svgIdLayer->m_viewID = ViewLayer::SchematicView) { // DebugDialog::debug("delete me please"); //} if (svgIdLayer->processed(viewLayerPlacement)) { // hybrids are not visible in some views return svgIdLayer->svgVisible(viewLayerPlacement) || svgIdLayer->m_hybrid; } QString connectorID = svgIdLayer->m_svgId; // boundsOnElement seems to include any matrix on the element itself. // I would swear this wasn't true before Qt4.7, but maybe I am crazy QRectF bounds = this->boundsOnElement(connectorID); if (bounds.isNull() && !svgIdLayer->m_hybrid) { // hybrids can have zero size svgIdLayer->setInvisible(viewLayerPlacement); DebugDialog::debug("renderer::setupconnector: null bounds"); return false; } QSizeF defaultSizeF = this->defaultSizeF(); QRectF viewBox = this->viewBoxF(); ConnectorInfo * connectorInfo = getConnectorInfo(connectorID); /* DebugDialog::debug(QString("connectorid:%1 m11:%2 m12:%3 m21:%4 m22:%5 dx:%6 dy:%7") .arg(connectorID) .arg(connectorInfo->matrix.m11()) .arg(connectorInfo->matrix.m12()) .arg(connectorInfo->matrix.m21()) .arg(connectorInfo->matrix.m22()) .arg(connectorInfo->matrix.dx()) .arg(connectorInfo->matrix.dy()), bounds); */ /*DebugDialog::debug(QString("identity matrix %11 %1 %2, viewbox: %3 %4 %5 %6, bounds: %7 %8 %9 %10, size: %12 %13").arg(m_modelPart->title()).arg(connectorSharedID()) .arg(viewBox.x()).arg(viewBox.y()).arg(viewBox.width()).arg(viewBox.height()) .arg(bounds.x()).arg(bounds.y()).arg(bounds.width()).arg(bounds.height()) .arg(viewID) .arg(defaultSizeF.width()).arg(defaultSizeF.height()) ); */ // some strangeness in the way that svg items and non-svg items map to screen space // might be a qt problem. //QMatrix matrix0 = connectorInfo->matrix * this->matrixForElement(connectorID); //QRectF r1 = matrix0.mapRect(bounds); QMatrix elementMatrix = this->matrixForElement(connectorID); QRectF r1 = elementMatrix.mapRect(bounds); if (connectorInfo != NULL) { if (connectorInfo->gotCircle) { QLineF l(0,0,connectorInfo->radius, 0); QLineF lm = elementMatrix.map(l); svgIdLayer->m_radius = lm.length() * defaultSizeF.width() / viewBox.width(); QLineF k(0,0,connectorInfo->strokeWidth, 0); QLineF km = elementMatrix.map(k); svgIdLayer->m_strokeWidth = km.length() * defaultSizeF.width() / viewBox.width(); //bounds = connectorInfo->cbounds; } if (connectorInfo->gotPath) { svgIdLayer->m_path = true; } } /* svgIdLayer->m_rect.setRect(r1.x() * defaultSize.width() / viewBox.width(), r1.y() * defaultSize.height() / viewBox.height(), r1.width() * defaultSize.width() / viewBox.width(), r1.height() * defaultSize.height() / viewBox.height()); */ QRectF svgRect(r1.x() * defaultSizeF.width() / viewBox.width(), r1.y() * defaultSizeF.height() / viewBox.height(), r1.width() * defaultSizeF.width() / viewBox.width(), r1.height() * defaultSizeF.height() / viewBox.height()); //if (!svgIdLayer->m_svgVisible) { //DebugDialog::debug("not vis"); //} QPointF terminal = calcTerminalPoint(svgIdLayer->m_terminalId, svgRect, ignoreTerminalPoint, viewBox, connectorInfo->terminalMatrix); svgIdLayer->setPointRect(viewLayerPlacement, terminal, svgRect, !bounds.isNull()); calcLeg(svgIdLayer, viewBox, connectorInfo); return true; } void FSvgRenderer::calcLeg(SvgIdLayer * svgIdLayer, const QRectF & viewBox, ConnectorInfo * connectorInfo) { if (svgIdLayer->m_legId.isEmpty()) return; svgIdLayer->m_legColor = connectorInfo->legColor; QSizeF defaultSizeF = this->defaultSizeF(); svgIdLayer->m_legStrokeWidth = connectorInfo->legStrokeWidth * defaultSizeF.width() / viewBox.width(); /* DebugDialog::debug( QString("calcleg leg %1 %2 %3 %4") .arg(connectorInfo->legLine.p1().x()) .arg(connectorInfo->legLine.p1().y()) .arg(connectorInfo->legLine.p2().x()) .arg(connectorInfo->legLine.p2().y()) ); */ QMatrix matrix = this->matrixForElement(svgIdLayer->m_legId) * connectorInfo->legMatrix; QPointF p1 = matrix.map(connectorInfo->legLine.p1()); QPointF p2 = matrix.map(connectorInfo->legLine.p2()); double x1 = p1.x() * defaultSizeF.width() / viewBox.width(); double y1 = p1.y() * defaultSizeF.height() / viewBox.height(); double x2 = p2.x() * defaultSizeF.width() / viewBox.width(); double y2 = p2.y() * defaultSizeF.height() / viewBox.height(); QPointF center(defaultSizeF.width() / 2, defaultSizeF.height() / 2); double d1 = GraphicsUtils::distanceSqd(QPointF(x1, y1), center); double d2 = GraphicsUtils::distanceSqd(QPointF(x2, y2), center); // find the end which is closer to the center of the viewBox (which shouldn't include the leg) if (d1 <= d2) { svgIdLayer->m_legLine = QLineF(x1, y1, x2, y2); } else { svgIdLayer->m_legLine = QLineF(x2, y2, x1, y1); } } QPointF FSvgRenderer::calcTerminalPoint(const QString & terminalId, const QRectF & connectorRect, bool ignoreTerminalPoint, const QRectF & viewBox, QMatrix & terminalMatrix) { Q_UNUSED(terminalMatrix); QPointF terminalPoint = connectorRect.center() - connectorRect.topLeft(); // default spot is centered if (ignoreTerminalPoint) { return terminalPoint; } if (terminalId.isNull() || terminalId.isEmpty()) { return terminalPoint; } if (!this->elementExists(terminalId)) { DebugDialog::debug(QString("missing expected terminal point element %1").arg(terminalId)); return terminalPoint; } QRectF tBounds = this->boundsOnElement(terminalId); if (tBounds.isNull()) { return terminalPoint; } QSizeF defaultSizeF = this->defaultSizeF(); //DebugDialog::debug( QString("terminal %5 rect %1,%2,%3,%4").arg(tBounds.x()). //arg(tBounds.y()). //arg(tBounds.width()). //arg(tBounds.height()). //arg(terminalID) ); // matrixForElement only grabs parent matrices, not any transforms in the element itself //QMatrix tMatrix = this->matrixForElement(terminalId) ; //* terminalMatrix; //QRectF terminalRect = tMatrix.mapRect(tBounds); QRectF terminalRect = this->matrixForElement(terminalId).mapRect(tBounds); QPointF c = terminalRect.center(); QPointF q(c.x() * defaultSizeF.width() / viewBox.width(), c.y() * defaultSizeF.height() / viewBox.height()); terminalPoint = q - connectorRect.topLeft(); //DebugDialog::debug( QString("terminalagain %3 rect %1,%2 ").arg(terminalPoint.x()). //arg(terminalPoint.y()). //arg(terminalID) ); return terminalPoint; } QList FSvgRenderer::setUpNonConnectors(ViewLayer::ViewLayerPlacement viewLayerPlacement) { QList list; if (m_nonConnectorInfoHash.count() == 0) return list; foreach (QString nonConnectorID, m_nonConnectorInfoHash.keys()) { SvgIdLayer * svgIdLayer = new SvgIdLayer(ViewLayer::PCBView); svgIdLayer->m_svgId = nonConnectorID; QRectF bounds = this->boundsOnElement(nonConnectorID); if (bounds.isNull()) { delete svgIdLayer; continue; } QSizeF defaultSizeF = this->defaultSizeF(); QSize defaultSize = this->defaultSize(); if ((bounds.width()) == defaultSizeF.width() && (bounds.height()) == defaultSizeF.height()) { delete svgIdLayer; continue; } QRectF viewBox = this->viewBoxF(); ConnectorInfo * connectorInfo = m_nonConnectorInfoHash.value(nonConnectorID, NULL); if (connectorInfo && connectorInfo->gotCircle) { svgIdLayer->m_radius = connectorInfo->radius * defaultSizeF.width() / viewBox.width(); svgIdLayer->m_strokeWidth = connectorInfo->strokeWidth * defaultSizeF.width() / viewBox.width(); //bounds = connectorInfo->cbounds; } // matrixForElement only grabs parent matrices, not any transforms in the element itself //QMatrix matrix0 = connectorInfo->matrix * this->matrixForElement(nonConnectorID); //QRectF r1 = matrix0.mapRect(bounds); QRectF r1 = this->matrixForElement(nonConnectorID).mapRect(bounds); QRectF svgRect(r1.x() * defaultSize.width() / viewBox.width(), r1.y() * defaultSize.height() / viewBox.height(), r1.width() * defaultSize.width() / viewBox.width(), r1.height() * defaultSize.height() / viewBox.height()); QPointF center = svgRect.center() - svgRect.topLeft(); svgIdLayer->setPointRect(viewLayerPlacement, center, svgRect, true); list.append(svgIdLayer); } return list; } fritzing-app-0.9.3b/src/fsvgrenderer.h000066400000000000000000000110211272363601300177310ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FSVGRENDERER_H #define FSVGRENDERER_H #include #include #include #include #include #include #include "viewlayer.h" struct ConnectorInfo { bool gotCircle; double radius; double strokeWidth; QMatrix matrix; //QRectF cbounds; QMatrix terminalMatrix; QMatrix legMatrix; QString legColor; QLineF legLine; double legStrokeWidth; bool gotPath; }; typedef QHash RendererHash; struct LoadInfo { QString filename; QStringList connectorIDs; QStringList terminalIDs; QStringList legIDs; QString setColor; QString colorElementID; bool findNonConnectors; bool parsePaths; LoadInfo() { findNonConnectors = parsePaths = false; } }; class FSvgRenderer : public QSvgRenderer { Q_OBJECT public: FSvgRenderer(QObject * parent = 0); ~FSvgRenderer(); QByteArray loadSvg(const LoadInfo &); QByteArray loadSvg(const QString & filename); QByteArray loadSvg( const QByteArray & contents, const LoadInfo &); // for SvgSplitter loads QByteArray loadSvg( const QByteArray & contents, const QString & filename, bool findNonConnectors); // for SvgSplitter loads bool loadSvgString(const QString & svg); bool loadSvgString(const QString & svg, QString & newSvg); bool fastLoad(const QByteArray & contents); QByteArray finalLoad(QByteArray & cleanContents, const QString & filename); const QString & filename(); QSizeF defaultSizeF(); bool setUpConnector(class SvgIdLayer * svgIdLayer, bool ignoreTerminalPoint, ViewLayer::ViewLayerPlacement); QList setUpNonConnectors(ViewLayer::ViewLayerPlacement); public: static void cleanup(); static QSizeF parseForWidthAndHeight(QXmlStreamReader &); static QPixmap * getPixmap(QSvgRenderer * renderer, QSize size); static void initNames(); protected: bool determineDefaultSize(QXmlStreamReader &); QByteArray loadAux (const QByteArray & contents, const LoadInfo &); bool initConnectorInfo(QDomDocument &, const LoadInfo &); ConnectorInfo * initConnectorInfoStruct(QDomElement & connectorElement, const QString & filename, bool parsePaths); bool initConnectorInfoStructAux(QDomElement &, ConnectorInfo * connectorInfo, const QString & filename, bool parsePaths); bool initConnectorInfoCircle(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename); bool initConnectorInfoPath(QDomElement & element, ConnectorInfo * connectorInfo, const QString & filename); void initNonConnectorInfo(QDomDocument & domDocument, const QString & filename); void initNonConnectorInfoAux(QDomElement & element, const QString & filename); void initTerminalInfoAux(QDomElement & element, const LoadInfo &); void initLegInfoAux(QDomElement & element, const LoadInfo &, bool & gotOne); void initConnectorInfoAux(QDomElement & element, const LoadInfo &); QPointF calcTerminalPoint(const QString & terminalId, const QRectF & connectorRect, bool ignoreTerminalPoint, const QRectF & viewBox, QMatrix & terminalMatrix); bool initLegInfoAux(QDomElement & element, ConnectorInfo * connectorInfo); void calcLeg(SvgIdLayer *, const QRectF & viewBox, ConnectorInfo * connectorInfo); ConnectorInfo * getConnectorInfo(const QString & connectorID); void clearConnectorInfoHash(QHash & hash); protected: QString m_filename; QSizeF m_defaultSizeF; QHash m_connectorInfoHash; QHash m_nonConnectorInfoHash; public: static QString NonConnectorName; }; #endif fritzing-app-0.9.3b/src/help/000077500000000000000000000000001272363601300160215ustar00rootroot00000000000000fritzing-app-0.9.3b/src/help/aboutbox.cpp000066400000000000000000000233551272363601300203600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6968 $: $Author: irascibl@gmail.com $: $Date: 2013-04-15 17:44:14 +0200 (Mo, 15. Apr 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include "aboutbox.h" #include "../debugdialog.h" #include "../version/version.h" #include "../utils/expandinglabel.h" AboutBox* AboutBox::Singleton = NULL; static const int AboutWidth = 390; static const int AboutText = 210; QString AboutBox::BuildType; AboutBox::AboutBox(QWidget *parent) : QWidget(parent) { Singleton = this; // To make the application not quit when the window closes this->setAttribute(Qt::WA_QuitOnClose, false); setFixedSize(AboutWidth, 430); // the background color setStyleSheet("background-color: #E8E8E8"); // the new Default Font QFont smallFont("Droid Sans", 11); QFont extraSmallFont("Droid Sans", 9); extraSmallFont.setLetterSpacing(QFont::PercentageSpacing, 92); // Big Icon QLabel *logoShield = new QLabel(this); logoShield->setPixmap(QPixmap(":/resources/images/AboutBoxLogoShield.png")); logoShield->setGeometry(17, 8, 356, 128); // Version String QLabel *versionMain = new QLabel(this); versionMain->setText(tr("Version %1.%2.%3 (%4%5 %6) %7 [Qt %8]") .arg(Version::majorVersion()) .arg(Version::minorVersion()) .arg(Version::minorSubVersion()) .arg(Version::modifier()) .arg(Version::revision()) .arg(Version::date()) .arg(BuildType) .arg(QT_VERSION_STR) ); versionMain->setFont(smallFont); versionMain->setGeometry(45, 150, 300, 20); versionMain->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); versionMain->setTextInteractionFlags(Qt::TextSelectableByMouse); // Link to website QLabel *linkToFritzing = new QLabel(this); linkToFritzing->setText(tr("www.fritzing.org")); linkToFritzing->setOpenExternalLinks(true); linkToFritzing->setFont(smallFont); linkToFritzing->setGeometry(45, 168, 300, 18); linkToFritzing->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); // Copyright messages QLabel *copyrightGNU = new QLabel(this); copyrightGNU->setText(tr("GNU GPL v3 on the code and CreativeCommons:BY-SA on the rest")); copyrightGNU->setFont(extraSmallFont); copyrightGNU->setGeometry(0, 398, AboutWidth, 16); copyrightGNU->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); QLabel *CC = new QLabel(this); QPixmap cc(":/resources/images/aboutbox_CC.png"); CC->setPixmap(cc); CC->setGeometry(30, this->height() - cc.height(), cc.width(), cc.height()); QLabel *copyrightFritzing = new QLabel(this); copyrightFritzing->setText(tr("2007-%1 Fritzing").arg(Version::year())); copyrightFritzing->setFont(extraSmallFont); copyrightFritzing->setGeometry(30, 414, AboutWidth - 30 - 30, 16); copyrightFritzing->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); // Scrolling Credits Text // moved data out of credits.txt so we could apply translation QString data = QString("








") + "

" + tr("Fritzing is made by: ") + tr("Prof. Reto Wettach, André Knörig, Myriel Milicevic, ") + tr("Zach Eveland, Dirk van Oosterbosch, ") + tr("Jonathan Cohen, Marcus Paeschke, Omer Yosha, ") + tr("Travis Robertson, Stefan Hermann, Brendan Howell, ") + tr("Mariano Crowe, Johannes Landstorfer, ") + tr("Jenny Chowdhury, Lionel Michel, Fabian Althaus, Jannis Leidel, ") + tr("Bryant Mairs, Uleshka Asher, and Daniel Tzschentke. ") + "

" + "

" + tr("Special thanks goes out to: ") + tr("Jussi Ängeslevä, Massimo Banzi, Ayah Bdeir, ") + tr("Durrell Bishop, David Cuartielles, Fabian Hemmert, ") + tr("Gero Herkenrath, Jeff Hoefs, Tom Hulbert, ") + tr("Tom Igoe, Hans-Peter Kadel, Till Savelkoul, ") + tr("Jan Sieber, Yaniv Steiner, Olaf Val, ") + tr("Michaela Vieser and Julia Werner.") + "

" + "

" + tr("Thanks to Kurt Badelt and Miguel Solis for the Spanish translation, ") + tr("to Gianluca Urgese for the Italian translation, ") + tr("to Nuno Pessanha Santos for the Portuguese (European) translation, ") + tr("to Yuelin and Ninjia for the Chinese (Simplified) translation, ") + tr("to Hiroshi Suzuki for the Japanese translation, ") + tr("to Robert Lee for the Chinese (Traditional) translation, ") + tr("to Vladimir Savinov for the Russian translation, " ) + tr("to Steven Noppe and Davy Uittenbogerd for the Dutch translation, " ) + tr("to Josef Dustira for the Czech translation, " ) + tr("to Jinbuhm Kim for the Korean translation, " ) + tr("to Ĺubomír Duchoň for the Slovak translation, " ) + tr("to Alexander Kaltsas for the Greek translation, " ) + tr("to Lionel Michel, Yvan Kloster, Alexandre Dussart, and Roald Baudoux for the French translation, " ) + tr("to Cihan Mete Bahadır for the Turkish translation, " ) + tr("to Nikolay Stankov for the Bulgarian translation, " ) + tr("and to Arthur Zanona, Nuno Pessanha Santos, Leandro Nunes, and Gabriel Ferreira for the Portuguese (Brazilian) translation. " ) + "

" + "

" + tr("Fritzing is made possible with funding from the ") + tr("MWFK Brandenburg, the sponsorship of the Design ") + tr("Department of Bauhaus-University Weimar, ") + tr("IxDS, an anonymous donor, Parallax, Picaxe, Sparkfun, ") + tr("and from each purchase of a Fritzing Starter Kit or a PCB from Fritzing Fab.") + "

" + "

" + tr("Special thanks goes out as well to all the students ") + tr("and alpha testers who were brave enough to give ") + tr("Fritzing a test spin. ") + "

" + tr("







"); QPixmap fadepixmap(":/resources/images/aboutbox_scrollfade.png"); m_expandingLabel = new ExpandingLabel(this, AboutWidth); m_expandingLabel->setObjectName("aboutText"); m_expandingLabel->setLabelText(data); m_expandingLabel->setFont(smallFont); m_expandingLabel->setGeometry(0, AboutText, AboutWidth, fadepixmap.height()); // setAlignment only aligns the "current paragraph" // the QTextCursor code aligns all paragraphs QTextCursor cursor(m_expandingLabel->document()); cursor.select(QTextCursor::Document); QTextBlockFormat fmt; fmt.setAlignment(Qt::AlignCenter); cursor.mergeBlockFormat(fmt); // Add a fade out and a fade in the scrollArea QLabel *scrollFade = new QLabel(this); scrollFade->setPixmap(fadepixmap); scrollFade->setGeometry(0, AboutText, AboutWidth, fadepixmap.height()); scrollFade->setStyleSheet("background-color: none"); // auto scroll timer initialization m_restartAtTop = false; m_startTime = QTime::currentTime(); m_autoScrollTimer = new QTimer(this); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) m_autoScrollTimer->setTimerType(Qt::PreciseTimer); #endif connect(m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(scrollCredits())); } void AboutBox::resetScrollAnimation() { // Only called when the window is newly loaded m_autoScrollTimer->start(35); m_startTime.start(); } void AboutBox::scrollCredits() { if (m_startTime.elapsed() >= 0 ) { //int max = m_scrollArea->verticalScrollBar()->maximum(); //int v = m_scrollArea->widget()->sizeHint().height(); if (m_restartAtTop) { // Reset at the top m_startTime.start(); m_restartAtTop = false; m_expandingLabel->verticalScrollBar()->setValue(0); return; } if (m_expandingLabel->verticalScrollBar()->value() >= m_expandingLabel->verticalScrollBar()->maximum()) { // go and reset // m_startTime.start(); m_restartAtTop = true; } else { m_expandingLabel->verticalScrollBar()->setValue(m_expandingLabel->verticalScrollBar()->value() + 1); } } } void AboutBox::initBuildType(const QString & buildType) { BuildType = buildType; } void AboutBox::hideAbout() { //DebugDialog::debug("the AboutBox gets a hide action triggered"); if (Singleton != NULL) { Singleton->hide(); } } void AboutBox::showAbout() { //DebugDialog::debug("the AboutBox gets a show action triggered"); if (Singleton == NULL) { new AboutBox(); } // scroll text now to prevent a flash of text if text was visible the last time the about box was open Singleton->m_expandingLabel->verticalScrollBar()->setValue(0); Singleton->show(); } void AboutBox::closeAbout() { //DebugDialog::debug("the AboutBox gets a close action triggered"); // Note: not every close triggers this function. we better listen to closeEvent if (Singleton != NULL) { Singleton->close(); } } void AboutBox::closeEvent(QCloseEvent *event) { // called when the window is about to close //DebugDialog::debug("the AboutBox gets a closeEvent"); m_autoScrollTimer->stop(); event->accept(); } void AboutBox::keyPressEvent ( QKeyEvent * event ) { if ((event->key() == Qt::Key_W) && (event->modifiers() & Qt::ControlModifier) ) { // We get the ctrl + W / command + W key event //DebugDialog::debug("W key!"); this->closeAbout(); } } void AboutBox::show() { QWidget::show(); m_restartAtTop = true; resetScrollAnimation(); } fritzing-app-0.9.3b/src/help/aboutbox.h000066400000000000000000000034411272363601300200170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ABOUTBOX_H #define ABOUTBOX_H #include #include #include #include class AboutBox : public QWidget { Q_OBJECT private: AboutBox(QWidget *parent = 0); void resetScrollAnimation(); private: //QScrollArea *m_scrollArea; class ExpandingLabel * m_expandingLabel; bool m_restartAtTop; QTime m_startTime; QTimer *m_autoScrollTimer; public: static void hideAbout(); static void showAbout(); static void closeAbout(); // Maybe we don't need close as a public method (we only want to hide) static void initBuildType(const QString &); public slots: void scrollCredits(); protected: static AboutBox* Singleton; static QString BuildType; protected: void closeEvent ( QCloseEvent * event ); void keyPressEvent ( QKeyEvent * event ); void show(); }; #endif fritzing-app-0.9.3b/src/help/firsttimehelpdialog.cpp000066400000000000000000000130021272363601300225600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include #include #include "firsttimehelpdialog.h" static FirstTimeHelpDialog * Singleton = NULL; static ViewLayer::ViewID TheViewID = ViewLayer::BreadboardView; static QHash TheTexts; FirstTimeHelpDialog::FirstTimeHelpDialog(QWidget *parent) : QDialog(parent) { // Let's set the icon this->setWindowIcon(QIcon(QPixmap(":resources/images/fritzing_icon.png"))); this->setObjectName("firstTimeHelpDialog"); setWindowTitle(tr("First Time Help")); m_label = new QLabel(); m_label->setWordWrap(true); m_label->setObjectName("firstTimeHelpDialogText"); QVBoxLayout * vLayout = new QVBoxLayout(); vLayout->addWidget(m_label); this->setLayout(vLayout); this->setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); } FirstTimeHelpDialog::~FirstTimeHelpDialog() { } void FirstTimeHelpDialog::setViewID(ViewLayer::ViewID viewID) { TheViewID = viewID; if (Singleton) { Singleton->m_label->setText(TheTexts.value(TheViewID, "")); } } void FirstTimeHelpDialog::showFirstTimeHelp() { init(); Singleton->m_label->setText(TheTexts.value(TheViewID, "")); Singleton->show(); } void FirstTimeHelpDialog::hideFirstTimeHelp() { init(); Singleton->hide(); } void FirstTimeHelpDialog::cleanup() { if (Singleton) { delete Singleton; Singleton = NULL; } } void FirstTimeHelpDialog::init() { if (TheTexts.count() == 0) { QString bbText = tr( "
" "The Breadboard View is meant to look like a real-life breadboard prototype." "

" "Begin by dragging a part from the Parts Bin, which is over at the top right. " "Then pull in more parts, connecting them by placing them on the breadboard or clicking on the connectors and dragging wires. " "The process is similar to how you would arrange things in the physical world. " "

" "After you're finished creating your sketch in the breadboard view, try the other views. " "You can switch views by clicking the Tabs at the top of the window. " "Because different views have different purposes, parts will look different in the other views." ); QString schText = tr( "Welcome to the Schematic View" "

" "This is a more abstract way to look at components and connections than the Breadboard View. " "You have the same elements as you have on your breadboard, " "they just look different. This representation is closer to the traditional diagrams used by engineers." "

" "After you have drawn wires between parts, you can press <Shift>-click with the mouse to create bend points and tidy up your connections. " "The Schematic View can help you check that you have made the right connections between components. " "You can also print out your schematic for documentation." ); QString pcbText = tr( "The PCB View is where you layout the components will on a physical PCB (Printed Circuit Board)." "

" "PCBs can be made at home or in a small lab using DIY etching processes. " "They also can be sent to professional PCB manufacturing services for more precise fabrication. " "
" "
" "The first thing you will need is a board to place your parts on. " "There should already be one in your sketch, but if not, " "drag in the board icon from the Parts Bin. The icon matches thie image to the right: " "" "" "
" "

" "To lay out your PCB, arrange all the components so they fit nicely on the board. " "Then try to shift them around to minimize the length and confusion of connections. " "You can also resize rectangular boards. " "
" "
" "Once the parts are sorted out, you connect them with copper traces. " "You can drag out a trace from individual connections or use " "the autorouter to generate them. " "The Autoroute button is at the bottom of the window. The button matches the image to the right:" "" "" "
" ); TheTexts.insert(ViewLayer::BreadboardView, bbText); TheTexts.insert(ViewLayer::SchematicView, schText); TheTexts.insert(ViewLayer::PCBView, pcbText); } if (Singleton == NULL) { Singleton = new FirstTimeHelpDialog(); } } fritzing-app-0.9.3b/src/help/firsttimehelpdialog.h000066400000000000000000000027321272363601300222350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FIRSTTIMEHELPDIALOG_H #define FIRSTTIMEHELPDIALOG_H #include #include #include "../viewlayer.h" class FirstTimeHelpDialog : public QDialog { Q_OBJECT private: FirstTimeHelpDialog(QWidget *parent = 0); ~FirstTimeHelpDialog(); protected: static void init(); public: static void setViewID(ViewLayer::ViewID); static void showFirstTimeHelp(); static void hideFirstTimeHelp(); static void cleanup(); protected: QLabel * m_label; }; #endif fritzing-app-0.9.3b/src/help/tipsandtricks.cpp000066400000000000000000000300251272363601300214070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include #include #include #include #include "tipsandtricks.h" TipsAndTricks* TipsAndTricks::Singleton = NULL; QList TipsAndTricks::TipSets; TipsAndTricks::TipsAndTricks(QWidget *parent) : QDialog(parent) { initTipSets(); // Let's set the icon this->setWindowIcon(QIcon(QPixmap(":resources/images/fritzing_icon.png"))); QString html(""); html += QString("

%1

").arg(tr("Fritzing Tips and Tricks")); html += "
    "; foreach (TipSet * tipSet, TipSets) { html += QString("
  • %1

      ").arg(tipSet->heading); foreach (QString tip, tipSet->tips) { html += QString("
    • %1
    • ").arg(tip); } html += "
  • "; } html += "
"; html += ""; Singleton = this; setWindowTitle(tr("Tips and Tricks")); resize(600, 500); m_textEdit = new QTextEdit(); m_textEdit->setReadOnly(true); m_textEdit->setHtml(html); QVBoxLayout * vLayout = new QVBoxLayout(this); vLayout->addWidget(m_textEdit); } void TipsAndTricks::initTipSets() { if (TipSets.count() > 0) return; QTime now = QTime::currentTime(); qsrand(now.msec()); QString localStorage = tr("Fritzing stores files for custom parts, generated parts, and for other temporary and long-term purposes in a 'local storage folder'. " "On Mac and Linux this is usually ~/.config/Fritzing/. " "Under Windows Vista and above, it is something like C:\\Users\\[user name]\\AppData\\Roaming\\Fritzing\\; " "under Windows XP is something like C:\\Documents and Settings\\[user name]\\Application Data\\Fritzing\\."); TipSet * ts = new TipSet; ts->heading = tr("examples"); ts->tips << tr("Get a jump start by looking at the example circuits under File > Examples."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("parts"); ts->tips << tr("Can't find your part? Search for it by clicking the magnifier icon in the Parts Bin and type in some keywords"); ts->tips << tr("If you can't find a part in the Parts Bin, the Generic IC is your friend. Drag it onto your sketch, then use the widgets in the Inspector to: choose from among 25 different through-hole and SMD packages; change the pin label; and--for DIPs and SIPs--change the number of pins. You can also change the pin names with the Pin Label editor"); ts->tips << tr("An icon in the parts bin may actually represent multiple related parts. So when you drag an icon from the parts bin into a sketch, make sure you look at the inspector. The inspector will display the range of choices available for you to modify a part, or swap it for a related part. The parts bin icon will also be a little 'stack' and not just a flat icon."); ts->tips << tr("The Inspector Window--which lets you change the properties of parts--is only enabled for parts that are in a sketch (not for parts still in a Parts Bin)."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("moving and selection"); ts->tips << tr("To constrain the motion of a part to horizontal or vertical, hold down the shift key as you drag it."); ts->tips << tr("If you're having trouble selecting a part or a wire (segment), try selecting the part that's in the way and send it to the back: use the Raise and Lower functions on the Part menu or the context menu (right-click menu)."); ts->tips << tr("To more precisely move a selection of parts, use the arrow keys. Shift-arrow moves by 10 units."); ts->tips << tr("Click on the mouse position indicator in the status bar (at the bottom) to toggle the units from in to mm to px."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("curves and bendable legs"); ts->tips << tr("In Breadboard view, to drag a part with bendable legs while keeping it connected to the breadboard, hold the Alt (Linux: Meta) key down when you start dragging."); ts->tips << tr("In Breadboard view, to drag out a wire from the end of a bendable leg, drag with the Alt (Linux: Meta) key down."); ts->tips << tr("In Breadboard or PCB view, to add a curve to a wire or bendable leg, drag with the Control (Mac: Command) key down. You can set whether curvy wires are the default in Preferences."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("rotation"); ts->tips << tr("To free-rotate a part in Breadboard or PCB view, select it, then hover your mouse near one of the corners until you see the rotate cursor. Mouse down and that corner will follow your mouse as you drag."); ts->tips << tr("To free-rotate a logo text or image item in PCB view hold down the Alt (Linux: meta) key and free-rotate as usual."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("layers and views"); ts->tips << tr("To drag the canvas, hold down the space bar and drag with the mouse."); ts->tips << tr("To toggle the visibility of layer in a view, go to the view menu and choose one of the view layer items. Or open up the Layers palette from the Window menu."); ts->tips << tr("When you export images from Fritzing, you can choose which layers are exported. Before you choose 'Export...', go into the 'View' menu and hide the layers you don't want to be visible."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("part labels"); ts->tips << tr("To edit a part label, double-click it, or use the text input widget in the inspector window."); ts->tips << tr("To display different properties in a part label, as well as rotate it, or change the font, right-click the label."); ts->tips << tr("To move a part label independently from its part, select the part first--both the part and the label will be highlighted. Once the label is selected you can drag it."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("wires and bendpoints"); ts->tips << tr("To add a bendpoint to a wire, double-click where you want the bendpoint."); ts->tips << tr("To delete a bendpoint from a wire, double-click it."); ts->tips << tr("In Schematic or PCB view, if you drag from a bendpoint with the Alt (Linux: Meta) key down, you will drag out a new wire from that bendpoint."); ts->tips << tr("To drag a wire segment (a section of a wire between two bendpoints), drag it with the Alt (Linux: Meta) key down. If you also hold down the shift key, the wire segment will be constrained to horizontal or vertical motion."); ts->tips << tr("Use shift-drag on a wire end or bendpoint to constrain its wire segment to an angle of 45 degrees (or some multiple of 45 degrees). If the wire segment is connected to other wire segments, the segment you're dragging will snap to make 90 degree angles with the neighboring wire segment."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("connections"); ts->tips << tr("To see all the connectors connected to a given connector, hold the mouse down on the connector--all the connections will be highlighted."); ts->tips << tr("A ratsnest line (very thin 'wire') between connections in one view means that those connections are somehow connected in another view."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("parts editor"); ts->tips << tr("Check out Parts Editor Help under the Help Menu."); ts->tips << tr("Before using the Parts Editor, see whether a Generic IC, Mystery Part, or Pin Header will do the job. Once you drop one of these into your sketch, you can change the number of pins, pin spacing, and other properties."); ts->tips << tr("In the Parts Editor, to select a graphic underneath another graphic, use the mouse wheel while holding down the shift key."); ts->tips << tr("You can use the Parts Editor to find the SVG file for a part's image for a given view. In the Parts Editor, click on the tab for that view, then use Show in Folder under the File Menu"); ts->tips << localStorage; ts->tips << tr("Do not store your custom part files in the Fritzing installation folder. If you upgrade Fritzing, these files will probably be deleted. Also, files in the Fritzing installation folder will not be saved in sketch (.fzz) files, so you won't be able to share them."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("pcb layout"); ts->tips << tr("Always lead a trace straight out of a pin. This helps to prevent short circuits."); ts->tips << tr("Through-hole parts can be traced from either side of a PCB."); ts->tips << tr("It makes life easier to route traces horizontally on one side of a PCB and vertically on the other side."); ts->tips << tr("Route traces in 45-degree angles to reduce noise."); ts->tips << tr("If Fritzing is missing your special part and you don't want to build one by yourself, then use pinheaders as connectors and the grid to align them."); ts->tips << tr("You can put your own Logo in the silkscreen of your PCB. Just use the Logo part of the core library and select your own file. SVG is the best format."); ts->tips << tr("Use copper-blocker parts to mask out areas that you want free of copper fill."); ts->tips << tr("To change trace width, select a trace then use the width combo box in the Inspector. You can use the drop-down or just type in a number (from 8 to 128)."); ts->tips << tr("To create a custom shape for your PCB, import an SVG file."); ts->tips << tr("To make a stylish Arduino shield in no time, switch the shape of the PCB from a rectangle to the Arduino"); TipSets.append(ts); ts = new TipSet; ts->heading = tr("pcb production"); ts->tips << tr("Smaller PCBs are more affortable than larger ones. Save space and money."); ts->tips << tr("Have your PCB quickly and easily produced with Fritzing Fab. Hover over the 'Fabricate' button to get a quote."); ts->tips << tr("When using the Fritzing Fab Service, If there are empty areas of the PCB that you do not want filled with copper, use the copper-blocker part. This resizable part will mask out copper fill in the rectangle it covers."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("notes"); ts->tips << tr("To insert a clickable URL into a note, select some text in the note and type ctrl-l (Mac: command-l). (That's 'l' as in the first letter of 'link'.) To modify an existing link select the linked text and use ctrl-l (command-l) again."); TipSets.append(ts); ts = new TipSet; ts->heading = tr("local file storage"); ts->tips << localStorage; TipSets.append(ts); } TipsAndTricks::~TipsAndTricks() { } void TipsAndTricks::hideTipsAndTricks() { if (Singleton != NULL) { Singleton->hide(); } } void TipsAndTricks::showTipsAndTricks() { if (Singleton == NULL) { new TipsAndTricks(); } Singleton->show(); } void TipsAndTricks::cleanup() { if (Singleton) { delete Singleton; Singleton = NULL; } } const QString & TipsAndTricks::randomTip() { int tipCount = 0; foreach (TipSet * tipSet, TipSets) { tipCount += tipSet->tips.count(); } if (tipCount == 0) return ___emptyString___; int ix = qrand() % tipCount; tipCount = 0; foreach (TipSet * tipSet, TipSets) { int count = tipSet->tips.count(); if (tipCount + count > ix) { return tipSet->tips.at(ix - tipCount); } tipCount += count; } // should not happen return ___emptyString___; } fritzing-app-0.9.3b/src/help/tipsandtricks.h000066400000000000000000000031341272363601300210550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef TIPSANDTRICKS_H #define TIPSANDTRICKS_H #include #include #include #include #include #include "../utils/misc.h" struct TipSet { QString heading; QStringList tips; }; class TipsAndTricks : public QDialog { Q_OBJECT private: TipsAndTricks(QWidget *parent = 0); ~TipsAndTricks(); public: static void hideTipsAndTricks(); static void showTipsAndTricks(); static void cleanup(); static const QString & randomTip(); static void initTipSets(); protected: static TipsAndTricks* Singleton; static QList TipSets; QTextEdit* m_textEdit; }; #endif fritzing-app-0.9.3b/src/infoview/000077500000000000000000000000001272363601300167175ustar00rootroot00000000000000fritzing-app-0.9.3b/src/infoview/htmlinfoview.cpp000066400000000000000000001102451272363601300221410ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include #include #include #include #include #include #include "htmlinfoview.h" #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "../connectors/connectorshared.h" #include "../connectors/connector.h" #include "../fsvgrenderer.h" #include "../utils/flineedit.h" #include "../items/moduleidnames.h" #include "../items/paletteitem.h" #include "../utils/clickablelabel.h" #include "../utils/textutils.h" #define HTML_EOF "\n" QPixmap * NoIcon = NULL; const int HtmlInfoView::STANDARD_ICON_IMG_WIDTH = 32; const int HtmlInfoView::STANDARD_ICON_IMG_HEIGHT = 32; const int IconSpace = 0; static const int MaxSpinBoxWidth = 60; static const int AfterSpinBoxWidth = 5; ///////////////////////////////////// QLabel * addLabel(QHBoxLayout * hboxLayout, QPixmap * pixmap) { QLabel * label = new QLabel(); label->setObjectName("iconLabel"); label->setAutoFillBackground(true); label->setPixmap(*pixmap); label->setFixedSize(pixmap->size()); hboxLayout->addWidget(label); hboxLayout->addSpacing(IconSpace); return label; } QString format3(double d) { return QString("%1").arg(d, 0, 'f', 3); } ////////////////////////////////////// TagLabel::TagLabel(QWidget * parent) : QLabel(parent) { } QSize TagLabel::sizeHint() const { QSize hint = QLabel::sizeHint(); QString t = text(); if (t.isEmpty()) return hint; QFontMetricsF fm(this->font()); double textWidth = fm.width(t); QWidget * w = this->window(); QPoint pos(0,0); pos = this->mapTo(w, pos); int myWidth = w->width() - pos.x() - 10; if (textWidth < myWidth) { return hint; } int lines = qCeil(textWidth / myWidth); int h = lines * fm.height(); return QSize(myWidth, h); } ////////////////////////////////////// HtmlInfoView::HtmlInfoView(QWidget * parent) : QScrollArea(parent) { this->setWidgetResizable(true); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_lastTitleItemBase = NULL; m_lastTagsModelPart = NULL; m_lastConnectorItem = NULL; m_lastIconItemBase = NULL; m_lastPropsModelPart = NULL; m_lastPropsItemBase = NULL; m_tinyMode = false; m_partTitle = NULL; m_partUrl = NULL; m_partVersion = NULL; m_lockCheckbox = NULL; m_stickyCheckbox = NULL; m_connDescr = NULL; m_tagsTextLabel = NULL; m_lastSwappingEnabled = false; m_lastItemBase = NULL; m_setContentTimer.setSingleShot(true); m_setContentTimer.setInterval(10); connect(&m_setContentTimer, SIGNAL(timeout()), this, SLOT(setContent())); m_currentItem = NULL; m_currentSwappingEnabled = false; m_layerWidget = NULL; } HtmlInfoView::~HtmlInfoView() { foreach (PropThing * propThing, m_propThings) { delete propThing; } m_propThings.clear(); } void HtmlInfoView::init(bool tinyMode) { m_tinyMode = tinyMode; QFrame * mainFrame = new QFrame(this); mainFrame->setObjectName("infoViewMainFrame"); QVBoxLayout *vlo = new QVBoxLayout(mainFrame); vlo->setSpacing(0); vlo->setContentsMargins(0, 0, 0, 0); vlo->setSizeConstraint( QLayout::SetMinAndMaxSize ); /* Part Title */ m_titleEdit = new FLineEdit(mainFrame); m_titleEdit->setObjectName("instanceTitleEditor"); m_titleEdit->setToolTip(tr("Change the part label here")); m_titleEdit->setAlignment(Qt::AlignLeft); connect(m_titleEdit, SIGNAL(editingFinished()), this, SLOT(setInstanceTitle()), Qt::QueuedConnection); connect(m_titleEdit, SIGNAL(mouseEnter()), this, SLOT(instanceTitleEnter())); connect(m_titleEdit, SIGNAL(mouseLeave()), this, SLOT(instanceTitleLeave())); connect(m_titleEdit, SIGNAL(editable(bool)), this, SLOT(instanceTitleEditable(bool))); setInstanceTitleColors(m_titleEdit, QColor(0xaf, 0xaf, 0xb4), QColor(0x00, 0x00, 0x00)); //b3b3b3, 575757 m_titleEdit->setAutoFillBackground(true); vlo->addWidget(m_titleEdit); if (tinyMode) m_titleEdit->setVisible(false); /* Part Icons */ if (NoIcon == NULL) { NoIcon = new QPixmap(":/resources/images/icons/noicon.png"); } QFrame * iconFrame = new QFrame(mainFrame); iconFrame->setObjectName("iconFrame"); QHBoxLayout * hboxLayout = new QHBoxLayout(); hboxLayout->setContentsMargins(0, 0, 0, 0); hboxLayout->addSpacing(IconSpace); m_icon1 = addLabel(hboxLayout, NoIcon); m_icon1->setToolTip(tr("Part breadboard view image")); m_icon2 = addLabel(hboxLayout, NoIcon); m_icon2->setToolTip(tr("Part schematic view image")); m_icon3 = addLabel(hboxLayout, NoIcon); m_icon3->setToolTip(tr("Part pcb view image")); QVBoxLayout * versionLayout = new QVBoxLayout(); QHBoxLayout * subVersionLayout = new QHBoxLayout(); m_partVersion = new QLabel(); m_partVersion->setObjectName("infoViewPartVersion"); m_partVersion->setToolTip(tr("Part version number")); m_partVersion->setOpenExternalLinks(false); m_partVersion->setAlignment(Qt::AlignLeft | Qt::AlignTop); connect(m_partVersion, SIGNAL(linkActivated(const QString &)), this, SLOT(clickObsolete(const QString &))); subVersionLayout->addWidget(m_partVersion, 0, Qt::AlignLeft); subVersionLayout->addStretch(1); versionLayout->addLayout(subVersionLayout); hboxLayout->addLayout(versionLayout); hboxLayout->addSpacerItem(new QSpacerItem(IconSpace, 1, QSizePolicy::Expanding)); iconFrame->setLayout(hboxLayout); vlo->addWidget(iconFrame); m_partUrl = new TagLabel(this); m_partUrl->setWordWrap(false); m_partUrl->setObjectName("infoViewPartUrl"); m_partUrl->setOpenExternalLinks(true); vlo->addWidget(m_partUrl); if (tinyMode) m_partUrl->setVisible(false); m_partTitle = new TagLabel(this); m_partTitle->setWordWrap(true); m_partTitle->setObjectName("infoViewPartTitle"); m_partTitle->setOpenExternalLinks(true); vlo->addWidget(m_partTitle); if (tinyMode) m_partTitle->setVisible(false); m_placementLabel = new QLabel(tr("Placement"), NULL); m_placementLabel->setObjectName("expandableViewLabel"); vlo->addWidget(m_placementLabel); m_placementFrame = new QFrame(this); m_placementFrame->setObjectName("infoViewPropertyFrame"); m_placementLayout = new QGridLayout(m_placementFrame); m_placementLayout->setSpacing(0); m_placementLayout->setContentsMargins(0, 0, 0, 0); m_layerLabel = new QLabel(tr("pcb layer"), this); m_layerLabel->setObjectName("propNameLabel"); m_layerLabel->setWordWrap(true); m_placementLayout->addWidget(m_layerLabel, 0, 0); m_layerFrame = new QFrame(this); m_layerFrame->setObjectName("propValueFrame"); m_layerLayout = new QVBoxLayout(m_layerFrame); m_layerLayout->setSpacing(0); m_layerLayout->setContentsMargins(0, 0, 0, 0); m_layerFrame->setLayout(m_layerLayout); m_placementLayout->addWidget(m_layerFrame, 0, 1); makeLocationFrame(); m_placementLayout->addWidget(m_locationLabel, 1, 0); m_placementLayout->addWidget(m_locationFrame, 1, 1); makeRotationFrame(); m_placementLayout->addWidget(m_rotationLabel, 2, 0); m_placementLayout->addWidget(m_rotationFrame, 2, 1); makeLockFrame(); m_placementLayout->addWidget(m_lockLabel, 3, 0); m_placementLayout->addWidget(m_lockFrame, 3, 1); m_placementFrame->setLayout(m_placementLayout); vlo->addWidget(m_placementFrame); m_propLabel = new QLabel(tr("Properties"), NULL); m_propLabel->setObjectName("expandableViewLabel"); vlo->addWidget(m_propLabel); m_propFrame = new QFrame(this); m_propFrame->setObjectName("infoViewPropertyFrame"); m_propLayout = new QGridLayout(m_propFrame); m_propLayout->setSpacing(0); m_propLayout->setContentsMargins(0, 0, 0, 0); m_propFrame->setLayout(m_propLayout); vlo->addWidget(m_propFrame); m_tagLabel = new QLabel(tr("Tags"), NULL); m_tagLabel->setObjectName("expandableViewLabel"); vlo->addWidget(m_tagLabel); if (tinyMode) m_tagLabel->setVisible(false); m_tagsTextLabel = new TagLabel(this); m_tagsTextLabel->setWordWrap(true); m_tagsTextLabel->setObjectName("tagsValue"); vlo->addWidget(m_tagsTextLabel); if (tinyMode) m_tagsTextLabel->setVisible(false); m_connLabel = new QLabel(tr("Connections"), NULL); m_connLabel->setObjectName("expandableViewLabel"); vlo->addWidget(m_connLabel); if (tinyMode) m_connLabel->setVisible(false); m_connFrame = new QFrame(this); m_connFrame->setObjectName("connectionsFrame"); QGridLayout * connLayout = new QGridLayout(m_connFrame); connLayout->setSpacing(0); connLayout->setContentsMargins(0, 0, 0, 0); m_connFrame->setLayout(connLayout); QLabel * descrLabel = new QLabel(tr("conn."), this); descrLabel->setObjectName("connectionsLabel"); m_connDescr = new QLabel(this); m_connDescr->setObjectName("connectionsValue"); connLayout->addWidget(descrLabel, 0, 0); connLayout->addWidget(m_connDescr, 0, 1); QLabel * nameLabel = new QLabel(tr("name"), this); nameLabel->setObjectName("connectionsLabel"); m_connName = new QLabel(this); m_connName->setObjectName("connectionsValue"); connLayout->addWidget(nameLabel, 1, 0); connLayout->addWidget(m_connName, 1, 1); QLabel * typeLabel = new QLabel(tr("type"), this); typeLabel->setObjectName("connectionsLabel"); m_connType = new QLabel(this); m_connType->setObjectName("connectionsValue"); connLayout->addWidget(typeLabel, 2, 0); connLayout->addWidget(m_connType, 2, 1); vlo->addWidget(m_connFrame); if (m_tinyMode) m_connFrame->setVisible(false); vlo->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); mainFrame->setLayout(vlo); this->setWidget(mainFrame); } void HtmlInfoView::cleanup() { if (NoIcon) { delete NoIcon; NoIcon = NULL; } } void HtmlInfoView::viewItemInfo(InfoGraphicsView *, ItemBase* item, bool swappingEnabled) { m_setContentTimer.stop(); m_lastItemBase = m_pendingItemBase = item; m_lastSwappingEnabled = m_pendingSwappingEnabled = swappingEnabled; m_setContentTimer.start(); } void HtmlInfoView::hoverEnterItem(InfoGraphicsView *, QGraphicsSceneHoverEvent *, ItemBase * item, bool swappingEnabled) { m_setContentTimer.stop(); m_pendingItemBase = item; m_pendingSwappingEnabled = swappingEnabled; m_setContentTimer.start(); } void HtmlInfoView::hoverLeaveItem(InfoGraphicsView *, QGraphicsSceneHoverEvent *, ItemBase * itemBase){ Q_UNUSED(itemBase); //DebugDialog::debug(QString("hoverLeaveItem itembase %1").arg(itemBase ? itemBase->instanceTitle() : "NULL")); m_setContentTimer.stop(); m_pendingItemBase = m_lastItemBase; m_pendingSwappingEnabled = m_lastSwappingEnabled; m_setContentTimer.start(); } void HtmlInfoView::viewConnectorItemInfo(QGraphicsSceneHoverEvent *, ConnectorItem * connectorItem) { int count = connectorItem ? connectorItem->connectionsCount() : 0; if (m_lastConnectorItem == connectorItem && m_lastConnectorItemCount == count) return; m_lastConnectorItem = connectorItem; m_lastConnectorItemCount = count; Connector * connector = NULL; if (connectorItem) { if (connectorItem->attachedTo() != m_lastItemBase) { return; } connector = connectorItem->connector(); } if (m_connDescr) { m_connDescr->setText(connectorItem ? tr("connected to %n item(s)", "", connectorItem->connectionsCount()) : ""); m_connName->setText(connector ? connector->connectorSharedName() : ""); m_connType->setText(connector ? Connector::connectorNameFromType(connector->connectorType()) : ""); } } void HtmlInfoView::hoverEnterConnectorItem(InfoGraphicsView *igv, QGraphicsSceneHoverEvent *event, ConnectorItem * item, bool swappingEnabled) { Q_UNUSED(event) Q_UNUSED(swappingEnabled) Q_UNUSED(igv) viewConnectorItemInfo(event, item); } void HtmlInfoView::hoverLeaveConnectorItem(InfoGraphicsView *igv, QGraphicsSceneHoverEvent *event, ConnectorItem *connItem) { Q_UNUSED(event); Q_UNUSED(connItem); Q_UNUSED(igv); viewConnectorItemInfo(event, NULL); } void HtmlInfoView::appendStuff(ItemBase* item, bool swappingEnabled) { Wire *wire = qobject_cast(item); if (wire) { appendWireStuff(wire, swappingEnabled); } else { appendItemStuff(item, swappingEnabled); } } void HtmlInfoView::appendWireStuff(Wire* wire, bool swappingEnabled) { if (wire == NULL) return; ModelPart *modelPart = wire->modelPart(); if (modelPart == NULL) return; if (modelPart->modelPartShared() == NULL) return; QString autoroutable = wire->getAutoroutable() ? tr("(autoroutable)") : ""; QString nameString = tr("Wire"); if (swappingEnabled) { if (wire->getRatsnest()) { nameString = tr("Ratsnest wire"); } else if(wire->getTrace()) { nameString = tr("Trace wire %1").arg(autoroutable); } } else { nameString = modelPart->description(); } partTitle(nameString, modelPart->version(), modelPart->url(), modelPart->isObsolete()); m_lockFrame->setVisible(false); m_lockLabel->setVisible(false); m_locationFrame->setVisible(false); m_locationLabel->setVisible(false); m_rotationFrame->setVisible(false); m_rotationLabel->setVisible(false); setUpTitle(wire); setUpIcons(wire, swappingEnabled); displayProps(modelPart, wire, swappingEnabled); bool hasLayer = (wire->viewID() == ViewLayer::PCBView) && wire->getViewGeometry().getPCBTrace(); m_placementFrame->setVisible(hasLayer); m_placementLabel->setVisible(hasLayer); addTags(modelPart); } void HtmlInfoView::appendItemStuff(ItemBase* base, bool swappingEnabled) { if (base == NULL) return; appendItemStuff(base, base->modelPart(), swappingEnabled); } void HtmlInfoView::appendItemStuff(ItemBase * itemBase, ModelPart * modelPart, bool swappingEnabled) { if (modelPart == NULL) return; if (modelPart->modelPartShared() == NULL) return; setUpTitle(itemBase); setUpIcons(itemBase, swappingEnabled); QString nameString; if (swappingEnabled) { nameString = (itemBase) ? itemBase->getInspectorTitle() : modelPart->title(); } else { nameString = modelPart->description(); } partTitle(nameString, modelPart->version(), modelPart->url(), modelPart->isObsolete()); m_lockFrame->setVisible(swappingEnabled); m_lockLabel->setVisible(swappingEnabled); m_lockCheckbox->setChecked(itemBase->moveLock()); m_locationFrame->setVisible(swappingEnabled); m_locationLabel->setVisible(swappingEnabled); m_locationFrame->setDisabled(itemBase->moveLock()); m_rotationFrame->setVisible(swappingEnabled); m_rotationLabel->setVisible(swappingEnabled); m_rotationFrame->setEnabled(itemBase->rotationAllowed()); setLocation(itemBase); setRotation(itemBase); if (itemBase->isBaseSticky()) { m_stickyCheckbox->setVisible(true); m_stickyCheckbox->setChecked(itemBase->isSticky()); } else { m_stickyCheckbox->setVisible(false); } displayProps(modelPart, itemBase, swappingEnabled); addTags(modelPart); m_placementLabel->setVisible(swappingEnabled); m_placementFrame->setVisible(swappingEnabled); } void HtmlInfoView::setContent() { m_setContentTimer.stop(); //DebugDialog::debug(QString("start updating %1").arg(QTime::currentTime().toString("HH:mm:ss.zzz"))); if (m_pendingItemBase == NULL) { setNullContent(); m_setContentTimer.stop(); return; } //DebugDialog::debug(QString("pending %1").arg(m_pendingItemBase->title())); m_currentSwappingEnabled = m_pendingSwappingEnabled; appendStuff(m_pendingItemBase, m_pendingSwappingEnabled); setCurrentItem(m_pendingItemBase); if (!m_tinyMode) { m_connFrame->setVisible(m_pendingSwappingEnabled); m_tagLabel->setVisible(true); m_connLabel->setVisible(m_pendingSwappingEnabled); } m_propLabel->setVisible(true); m_propFrame->setVisible(true); m_setContentTimer.stop(); //DebugDialog::debug(QString("end updating %1").arg(QTime::currentTime().toString("HH:mm:ss.zzz"))); } QSize HtmlInfoView::sizeHint() const { return QSize(DockWidthDefault, InfoViewHeightDefault); } void HtmlInfoView::setCurrentItem(ItemBase * item) { m_currentItem = item; } void HtmlInfoView::unregisterCurrentItem() { m_setContentTimer.stop(); setCurrentItem(NULL); m_pendingItemBase = NULL; m_setContentTimer.start(); } void HtmlInfoView::unregisterCurrentItemIf(long id) { if (m_currentItem == NULL) { return; } if (m_currentItem->id() == id) { unregisterCurrentItem(); } } ItemBase *HtmlInfoView::currentItem() { return m_currentItem; } void HtmlInfoView::reloadContent(InfoGraphicsView * infoGraphicsView) { if(m_currentItem) { viewItemInfo(infoGraphicsView, m_currentItem, m_currentSwappingEnabled); } } void HtmlInfoView::setNullContent() { setUpTitle(NULL); partTitle("", "", "", false); setUpIcons(NULL, false); displayProps(NULL, NULL, false); addTags(NULL); viewConnectorItemInfo(NULL, NULL); m_connFrame->setVisible(false); m_propFrame->setVisible(false); m_propLabel->setVisible(false); m_placementFrame->setVisible(false); m_placementLabel->setVisible(false); m_tagLabel->setVisible(false); m_connLabel->setVisible(false); } void HtmlInfoView::setInstanceTitle() { FLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; if (!edit->isEnabled()) return; if (m_currentItem == NULL) return; m_currentItem->setInspectorTitle(m_partTitle->text(), edit->text()); } void HtmlInfoView::instanceTitleEnter() { FLineEdit * edit = qobject_cast(sender()); if (edit->isEnabled()) { setInstanceTitleColors(edit, QColor(0xeb, 0xeb, 0xee), QColor(0x00, 0x00, 0x00)); //c8c8c8, 575757 } } void HtmlInfoView::instanceTitleLeave() { FLineEdit * edit = qobject_cast(sender()); if (edit->isEnabled()) { setInstanceTitleColors(edit, QColor(0xd2, 0xd2, 0xd7), QColor(0x00, 0x00, 0x00)); //b3b3b3, 575757 } } void HtmlInfoView::instanceTitleEditable(bool editable) { FLineEdit * edit = qobject_cast(sender()); if (editable) { setInstanceTitleColors(edit, QColor(0xff, 0xff, 0xff), QColor(0x00, 0x00, 0x00)); //fcfcfc, 000000 } else { setInstanceTitleColors(edit, QColor(0xd2, 0xd2, 0xd7), QColor(0x00, 0x00, 0x00)); //b3b3b3, 57575 } } void HtmlInfoView::setInstanceTitleColors(FLineEdit * edit, const QColor & base, const QColor & text) { edit->setStyleSheet(QString("background: rgb(%1,%2,%3); color: rgb(%4,%5,%6);") .arg(base.red()).arg(base.green()).arg(base.blue()) .arg(text.red()).arg(text.green()).arg(text.blue()) ); } void HtmlInfoView::setUpTitle(ItemBase * itemBase) { if (itemBase == m_lastTitleItemBase) { if (itemBase == NULL) return; if (itemBase->instanceTitle().compare(m_titleEdit->text()) == 0) return; } m_lastTitleItemBase = itemBase; bool titleEnabled = true; if (itemBase != NULL) { QString title = itemBase->getInspectorTitle(); if (title.isEmpty()) { // assumes a part with an empty title only comes from the parts bin palette titleEnabled = false; title = itemBase->title(); } if (itemBase->viewID() == ViewLayer::IconView) titleEnabled = false; m_titleEdit->setText(title); } else { titleEnabled = false; m_titleEdit->setText(""); } m_titleEdit->setEnabled(titleEnabled); // helps keep it left aligned? m_titleEdit->setCursorPosition(0); } void HtmlInfoView::setUpIcons(ItemBase * itemBase, bool swappingEnabled) { if (m_lastIconItemBase == itemBase) return; m_lastIconItemBase = itemBase; QPixmap *pixmap1 = NULL; QPixmap *pixmap2 = NULL; QPixmap *pixmap3 = NULL; QSize size = NoIcon->size(); if (itemBase) { itemBase->getPixmaps(pixmap1, pixmap2, pixmap3, swappingEnabled, size); } QPixmap* use1 = pixmap1; QPixmap* use2 = pixmap2; QPixmap* use3 = pixmap3; if (use1 == NULL) use1 = NoIcon; if (use2 == NULL) use2 = NoIcon; if (use3 == NULL) use3 = NoIcon; m_icon1->setPixmap(*use1); m_icon2->setPixmap(*use2); m_icon3->setPixmap(*use3); if (pixmap1 != NULL) delete pixmap1; if (pixmap2 != NULL) delete pixmap2; if (pixmap3 != NULL) delete pixmap3; } void HtmlInfoView::addTags(ModelPart * modelPart) { if (m_tagsTextLabel == NULL) return; if (m_lastTagsModelPart == modelPart) return; m_lastTagsModelPart = modelPart; if (modelPart == NULL || modelPart->tags().isEmpty()) { m_tagsTextLabel->setText(""); return; } m_tagsTextLabel->setText(modelPart->tags().join(", ")); } void HtmlInfoView::partTitle(const QString & title, const QString & version, const QString & url, bool isObsolete) { if (m_partTitle == NULL) return; if (m_lastPartTitle == title && m_lastPartVersion == version) return; m_lastPartTitle = title; m_lastPartVersion = version; if (!m_tinyMode) { if (url.isEmpty()) { m_partUrl->setVisible(false); m_partUrl->setText(""); } else { m_partUrl->setText(QString("%1").arg(url)); m_partUrl->setVisible(true); } } m_partTitle->setText(title); if (!version.isEmpty()) { m_partVersion->setText(tr("v. %1 %2").arg(version).arg(isObsolete ? QString("%1").arg(tr("obsolete")) : "")); } else m_partVersion->setText(""); } void HtmlInfoView::displayProps(ModelPart * modelPart, ItemBase * itemBase, bool swappingEnabled) { bool repeatPossible = (modelPart == m_lastPropsModelPart && itemBase == m_lastPropsItemBase && swappingEnabled == m_lastPropsSwappingEnabled); if (repeatPossible && modelPart == NULL && itemBase == NULL) { //DebugDialog::debug("display props bail"); return; } m_propLayout->setEnabled(false); if (repeatPossible) { DebugDialog::debug(QString("repeat possible %1").arg(repeatPossible)); } bool wantDebug = false; #ifndef QT_NO_DEBUG wantDebug = true; #endif QStringList keys; QHash properties = getPartProperties(modelPart, itemBase, wantDebug, keys); QString family = properties.value("family", "").toLower(); bool sl = false; if (keys.contains("layer")) { keys.removeOne("layer"); sl = (itemBase->viewID() == ViewLayer::PCBView); } showLayers(sl, itemBase, family, properties.value("layer", ""), swappingEnabled); int ix = 0; foreach(QString key, keys) { if (ix >= m_propThings.count()) { //DebugDialog::debug(QString("new prop thing %1").arg(ix)); PropThing * propThing = new PropThing; propThing->m_plugin = NULL; m_propThings.append(propThing); QLabel * propNameLabel = new QLabel(this); propNameLabel->setObjectName("propNameLabel"); propNameLabel->setWordWrap(true); propThing->m_name = propNameLabel; m_propLayout->addWidget(propNameLabel, ix, 0); QFrame * valueFrame = new QFrame(this); valueFrame->setObjectName("propValueFrame"); QVBoxLayout * vlayout = new QVBoxLayout(valueFrame); vlayout->setSpacing(0); vlayout->setContentsMargins(0, 0, 0, 0); propThing->m_layout = vlayout; propThing->m_frame = valueFrame; QLabel * propValueLabel = new QLabel(valueFrame); propValueLabel->setObjectName("propValueLabel"); propValueLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); vlayout->addWidget(propValueLabel); propThing->m_value = propValueLabel; m_propLayout->addWidget(valueFrame, ix, 1); } PropThing * propThing = m_propThings.at(ix++); QWidget * oldPlugin = propThing->m_plugin; propThing->m_plugin = NULL; QString value = properties.value(key,""); QString translatedName = ItemBase::translatePropertyName(key); QString resultKey, resultValue; QWidget * resultWidget = oldPlugin; bool result = false; bool hide = false; if (itemBase != NULL) { result = itemBase->collectExtraInfo(propThing->m_name->parentWidget(), family, key, value, swappingEnabled, resultKey, resultValue, resultWidget, hide); } QString newName; QString newValue; QWidget * newWidget = NULL; if (result && !hide) { newName = resultKey; if (resultWidget) { newWidget = resultWidget; if (resultWidget != oldPlugin) { propThing->m_layout->addWidget(resultWidget); } else { oldPlugin = NULL; } //DebugDialog::debug(QString("adding %1 %2").arg(newName).arg((long) resultWidget, 0, 16)); propThing->m_plugin = resultWidget; } else { newValue = resultValue; } } else { newName = translatedName; newValue = value; } if (oldPlugin) { clearPropThingPlugin(propThing, oldPlugin); } if (propThing->m_name->text().compare(newName) != 0) { propThing->m_name->setText(newName); } propThing->m_name->setVisible(!hide); propThing->m_frame->setVisible(!hide); if (newWidget == NULL && propThing->m_value->text().compare(newValue) != 0) { propThing->m_value->setText(newValue); } propThing->m_value->setVisible(newWidget == NULL && !hide); } for (int jx = ix; jx < m_propThings.count(); jx++) { PropThing * propThing = m_propThings.at(jx); propThing->m_name->setVisible(false); propThing->m_value->setVisible(false); propThing->m_frame->setVisible(false); if (propThing->m_plugin) { propThing->m_plugin->setVisible(false); } } m_propLayout->setEnabled(true); /* foreach (PropThing * propThing, m_propThings) { if (propThing->m_layout->count() > 1) { DebugDialog::debug(QString("too many %1").arg(propThing->m_layout->count())); } } */ } void HtmlInfoView::clearPropThingPlugin(PropThing * propThing) { if (propThing->m_plugin) { clearPropThingPlugin(propThing, propThing->m_plugin); propThing->m_plugin = NULL; } } void HtmlInfoView::clearPropThingPlugin(PropThing * propThing, QWidget * plugin) { //DebugDialog::debug(QString("clearing %1").arg((long) plugin, 0, 16)); propThing->m_layout->removeWidget(plugin); plugin->blockSignals(true); plugin->setVisible(false); // seems to trigger an unwanted focus out signal plugin->deleteLater(); } QHash HtmlInfoView::getPartProperties(ModelPart * modelPart, ItemBase * itemBase, bool wantDebug, QStringList & keys) { QHash properties; QString family; QString partNumber; if (modelPart != NULL && itemBase != NULL) { properties = itemBase->prepareProps(modelPart, wantDebug, keys); } return properties; } void HtmlInfoView::changeLock(bool lockState) { if (m_currentItem == NULL) return; if (m_currentItem->itemType() == ModelPart::Wire) return; m_currentItem->setMoveLock(lockState); m_locationFrame->setDisabled(lockState); m_rotationFrame->setDisabled(lockState); } void HtmlInfoView::changeSticky(bool lockState) { if (m_currentItem == NULL) return; if (m_currentItem->itemType() == ModelPart::Wire) return; m_currentItem->setLocalSticky(lockState); } void HtmlInfoView::clickObsolete(const QString &) { emit clickObsoleteSignal(); } void HtmlInfoView::showLayers(bool show, ItemBase * itemBase, const QString & family, const QString & value, bool swappingEnabled) { if (m_layerWidget) { m_layerLayout->removeWidget(m_layerWidget); m_layerWidget->blockSignals(true); m_layerWidget->setVisible(false); // seems to trigger an unwanted focus out signal m_layerWidget->deleteLater(); m_layerWidget = NULL; } if (itemBase == NULL) show = false; m_layerFrame->setVisible(show); m_layerLabel->setVisible(show); if (!show) return; QString resultKey, resultValue; bool hide; bool result = itemBase->collectExtraInfo(m_layerLabel->parentWidget(), family, "layer", value, swappingEnabled, resultKey, resultValue, m_layerWidget, hide); if (result && m_layerWidget != NULL) { m_layerLayout->addWidget(m_layerWidget); } } void HtmlInfoView::makeLockFrame() { m_lockLabel = new QLabel(" ", this); m_lockLabel->setObjectName("propNameLabel"); m_lockLabel->setWordWrap(true); m_lockFrame = new QFrame(this); QHBoxLayout * lockLayout = new QHBoxLayout(); lockLayout->setSpacing(0); lockLayout->setContentsMargins(0, 0, 0, 0); m_lockFrame->setObjectName("propValueFrame"); m_lockCheckbox = new QCheckBox(tr("Locked")); m_lockCheckbox->setObjectName("infoViewLockCheckbox"); m_lockCheckbox->setToolTip(tr("Change the locked state of the part in this view. A locked part can't be moved.")); connect(m_lockCheckbox, SIGNAL(clicked(bool)), this, SLOT(changeLock(bool))); lockLayout->addWidget(m_lockCheckbox); lockLayout->addSpacing(10); m_stickyCheckbox = new QCheckBox(tr("Sticky")); m_stickyCheckbox->setObjectName("infoViewLockCheckbox"); m_stickyCheckbox->setToolTip(tr("Change the \"sticky\" state of the part in this view. When a sticky part is moved, objects on top of it also move.")); connect(m_stickyCheckbox, SIGNAL(clicked(bool)), this, SLOT(changeSticky(bool))); lockLayout->addWidget(m_stickyCheckbox); lockLayout->addSpacerItem(new QSpacerItem(1,1, QSizePolicy::Expanding)); m_lockFrame->setLayout(lockLayout); } void HtmlInfoView::makeLocationFrame() { m_locationLabel = new QLabel(tr("location"), this); m_locationLabel->setObjectName("propNameLabel"); m_locationLabel->setWordWrap(true); m_locationFrame = new QFrame(this); QHBoxLayout * locationLayout = new QHBoxLayout(); locationLayout->setSpacing(0); locationLayout->setContentsMargins(0, 0, 0, 0); m_locationFrame->setObjectName("propValueFrame"); m_xEdit = new QDoubleSpinBox; m_xEdit->setDecimals(3); m_xEdit->setRange(-99999.999, 99999.999); m_xEdit->setKeyboardTracking(false); m_xEdit->setObjectName("infoViewDoubleSpinBox"); m_xEdit->setMaximumWidth(MaxSpinBoxWidth); m_xEdit->setMinimumWidth(MaxSpinBoxWidth); m_xEdit->setLocale(QLocale::C); locationLayout->addWidget(m_xEdit); locationLayout->addSpacing(AfterSpinBoxWidth); m_yEdit = new QDoubleSpinBox; m_yEdit->setDecimals(3); m_yEdit->setRange(-99999.999, 99999.999); m_yEdit->setKeyboardTracking(false); m_yEdit->setObjectName("infoViewDoubleSpinBox"); m_yEdit->setMaximumWidth(MaxSpinBoxWidth); m_yEdit->setMinimumWidth(MaxSpinBoxWidth); m_yEdit->setLocale(QLocale::C); locationLayout->addWidget(m_yEdit); locationLayout->addSpacing(3); m_unitsLabel = new ClickableLabel("px", this); m_unitsLabel->setObjectName("infoViewSpinBoxLabel"); m_unitsLabel->setCursor(Qt::PointingHandCursor); locationLayout->addWidget(m_unitsLabel); locationLayout->addSpacerItem(new QSpacerItem(1,1, QSizePolicy::Expanding)); m_locationFrame->setLayout(locationLayout); connect(m_xEdit, SIGNAL(valueChanged(double)), this, SLOT(xyEntry())); connect(m_yEdit, SIGNAL(valueChanged(double)), this, SLOT(xyEntry())); connect(m_unitsLabel, SIGNAL(clicked()), this, SLOT(unitsClicked())); unitsClicked(); // increments from px so that default is inches } void HtmlInfoView::makeRotationFrame() { m_rotationLabel = new QLabel(tr("rotation"), this); m_rotationLabel->setObjectName("propNameLabel"); m_rotationLabel->setWordWrap(true); m_rotationFrame = new QFrame(this); QHBoxLayout * rotationLayout = new QHBoxLayout(); rotationLayout->setSpacing(0); rotationLayout->setContentsMargins(0, 0, 0, 0); m_rotationFrame->setObjectName("propValueFrame"); m_rotEdit = new QDoubleSpinBox; m_rotEdit->setDecimals(1); m_rotEdit->setRange(-360, 360); m_rotEdit->setKeyboardTracking(false); m_rotEdit->setObjectName("infoViewDoubleSpinBox"); m_rotEdit->setMaximumWidth(MaxSpinBoxWidth); m_rotEdit->setMinimumWidth(MaxSpinBoxWidth); m_rotEdit->setLocale(QLocale::C); rotationLayout->addWidget(m_rotEdit); rotationLayout->addSpacing(AfterSpinBoxWidth); QLabel * label = new QLabel(tr("degrees"), this); label->setObjectName("infoViewSpinBoxLabel"); rotationLayout->addWidget(label); rotationLayout->addSpacerItem(new QSpacerItem(1,1, QSizePolicy::Expanding)); m_rotationFrame->setLayout(rotationLayout); connect(m_rotEdit, SIGNAL(valueChanged(double)), this, SLOT(rotEntry())); } void HtmlInfoView::unitsClicked() { QString units = m_unitsLabel->text(); QString xs = m_xEdit->text(); QString ys = m_yEdit->text(); double x = TextUtils::convertToInches(xs + units); double y = TextUtils::convertToInches(ys + units); double dpi = 1; double step = 1; if (units.compare("mm") == 0) { units = "px"; dpi = 90; step = 1; } else if (units.compare("px") == 0) { units = "in"; dpi = 1; step = 0.1; } else if (units.compare("in") == 0) { units = "mm"; dpi = 25.4; step = 1; } else { units = "in"; dpi = 1; step = 0.1; } m_unitsLabel->setText(units); m_xEdit->blockSignals(true); m_yEdit->blockSignals(true); m_xEdit->setSingleStep(step); m_yEdit->setSingleStep(step); if (!xs.isEmpty()) m_xEdit->setValue(x * dpi); if (!ys.isEmpty()) m_yEdit->setValue(y * dpi); m_xEdit->blockSignals(false); m_yEdit->blockSignals(false); } void HtmlInfoView::setLocation(ItemBase * itemBase) { if (itemBase == NULL) { m_xEdit->blockSignals(true); m_yEdit->blockSignals(true); m_xEdit->setEnabled(false); m_yEdit->setEnabled(false); m_xEdit->setValue(0); m_yEdit->setValue(0); m_xEdit->blockSignals(false); m_yEdit->blockSignals(false); return; } m_xEdit->setEnabled(!itemBase->moveLock()); m_yEdit->setEnabled(!itemBase->moveLock()); QString units = m_unitsLabel->text(); QPointF loc = itemBase->pos(); if (units == "px") { } else if (units == "in") { loc /= 90; } else if (units == "mm") { loc /= (90 / 25.4); } m_xEdit->blockSignals(true); m_yEdit->blockSignals(true); m_xEdit->setValue(loc.x()); m_yEdit->setValue(loc.y()); m_xEdit->blockSignals(false); m_yEdit->blockSignals(false); } void HtmlInfoView::setRotation(ItemBase * itemBase) { if (itemBase == NULL) { m_rotEdit->blockSignals(true); m_rotEdit->setEnabled(false); m_rotEdit->setValue(0); m_rotEdit->blockSignals(false); return; } double step = 90; bool enabled = true; if (itemBase->moveLock()) enabled = false; else if (!itemBase->rotationAllowed()) enabled = false; else { if (itemBase->rotation45Allowed()) step = 45; if (itemBase->freeRotationAllowed()) step = 1; } m_rotEdit->setEnabled(enabled); m_rotEdit->setSingleStep(step); QTransform transform = itemBase->transform(); double angle = atan2(transform.m12(), transform.m11()) * 180 / M_PI; m_rotEdit->blockSignals(true); m_rotEdit->setValue(angle); m_rotEdit->blockSignals(false); } void HtmlInfoView::updateLocation(ItemBase * itemBase) { if (itemBase == NULL) return; if (itemBase != m_lastItemBase) return; setLocation(itemBase); } void HtmlInfoView::updateRotation(ItemBase * itemBase) { if (itemBase == NULL) return; if (itemBase != m_lastItemBase) return; setRotation(itemBase); } void HtmlInfoView::xyEntry() { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(m_lastItemBase); if (infoGraphicsView == NULL) return; DebugDialog::debug(QString("xedit %1 %2 %3").arg(m_xEdit->text()).arg(m_yEdit->text()).arg(sender() == m_xEdit)); double x = TextUtils::convertToInches(m_xEdit->text() + m_unitsLabel->text()); double y = TextUtils::convertToInches(m_yEdit->text() + m_unitsLabel->text()); if (infoGraphicsView != NULL && m_lastItemBase != NULL) { infoGraphicsView->moveItem(m_lastItemBase, x * 90, y * 90); } } void HtmlInfoView::rotEntry() { if (m_lastItemBase != NULL) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(m_lastItemBase); if (infoGraphicsView == NULL) return; double newAngle = m_rotEdit->value(); if (m_rotEdit->singleStep() == 1) { } else { newAngle = qFloor(newAngle / m_rotEdit->singleStep()) * m_rotEdit->singleStep(); } QTransform transform = m_lastItemBase->transform(); double angle = atan2(transform.m12(), transform.m11()) * 180 / M_PI; infoGraphicsView->rotateX(newAngle - angle, false, m_lastItemBase); } } fritzing-app-0.9.3b/src/infoview/htmlinfoview.h000066400000000000000000000143231272363601300216060ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6609 $: $Author: irascibl@gmail.com $: $Date: 2012-10-30 14:21:08 +0100 (Di, 30. Okt 2012) $ ********************************************************************/ #ifndef HTMLINFOVIEW_H #define HTMLINFOVIEW_H #include #include #include #include #include #include #include #include #include #include #include "../items/itembase.h" #include "../items/wire.h" #include "../connectors/connectoritem.h" #include "../referencemodel/referencemodel.h" struct PropThing { QPointer m_name; QPointer m_frame; QPointer m_value; QPointer m_plugin; QPointer m_layout; }; class TagLabel : public QLabel { Q_OBJECT public: TagLabel(QWidget * parent); protected: QSize sizeHint() const; }; class HtmlInfoView : public QScrollArea { Q_OBJECT public: HtmlInfoView(QWidget * parent = 0); ~HtmlInfoView(); QSize sizeHint() const; void setContent(const QString& html); ItemBase *currentItem(); void reloadContent(class InfoGraphicsView *); void viewItemInfo(class InfoGraphicsView *, ItemBase* item, bool swappingEnabled); void updateLocation(ItemBase *); void updateRotation(ItemBase *); void hoverEnterItem(class InfoGraphicsView *, QGraphicsSceneHoverEvent * event, ItemBase * item, bool swappingEnabled); void hoverLeaveItem(class InfoGraphicsView *, QGraphicsSceneHoverEvent * event, ItemBase * item); void hoverEnterConnectorItem(class InfoGraphicsView *, QGraphicsSceneHoverEvent * event, ConnectorItem * item, bool swappingEnabled); void hoverLeaveConnectorItem(class InfoGraphicsView *, QGraphicsSceneHoverEvent * event, ConnectorItem * item); void unregisterCurrentItem(); void unregisterCurrentItemIf(long id); void init(bool tinyMode); public: static const int STANDARD_ICON_IMG_WIDTH; static const int STANDARD_ICON_IMG_HEIGHT; static void cleanup(); static QHash getPartProperties(ModelPart * modelPart, ItemBase * itemBase, bool wantDebug, QStringList & keys); signals: void clickObsoleteSignal(); protected slots: void setContent(); void setInstanceTitle(); void instanceTitleEnter(); void instanceTitleLeave(); void instanceTitleEditable(bool editable); void changeLock(bool); void changeSticky(bool); void clickObsolete(const QString &); void xyEntry(); void unitsClicked(); void rotEntry(); protected: void appendStuff(ItemBase* item, bool swappingEnabled); //finds out if it's a wire or something else void appendWireStuff(Wire* wire, bool swappingEnabled); void appendItemStuff(ItemBase* base, bool swappingEnabled); void appendItemStuff(ItemBase * base, ModelPart * modelPart, bool swappingEnabled); void setInstanceTitleColors(class FLineEdit * edit, const QColor & base, const QColor & text); void setCurrentItem(ItemBase *); void setNullContent(); void setUpTitle(ItemBase *); void setUpIcons(ItemBase *, bool swappingEnabled); void addTags(ModelPart * modelPart); void partTitle(const QString & title, const QString & version, const QString & url, bool obsolete); void displayProps(ModelPart * modelPart, ItemBase * itemBase, bool swappingEnabled); void clearPropThingPlugin(PropThing * propThing); void clearPropThingPlugin(PropThing * propThing, QWidget * plugin); void viewConnectorItemInfo(QGraphicsSceneHoverEvent * event, ConnectorItem* item); void showLayers(bool show, ItemBase *, const QString & family, const QString & value, bool swappingEnabled); void makeLockFrame(); void makeLocationFrame(); void makeRotationFrame(); void setLocation(ItemBase *); void setRotation(ItemBase *); protected: QPointer m_currentItem; bool m_currentSwappingEnabled; // previous item (possibly hovered over) QTimer m_setContentTimer; QPointer m_lastItemBase; bool m_lastSwappingEnabled; // previous item (selected) class FLineEdit * m_titleEdit; QLabel * m_icon1; QLabel * m_icon2; QLabel * m_icon3; QLabel * m_partTitle; QLabel * m_partUrl; QLabel * m_partVersion; QLabel * m_tagsTextLabel; QLabel * m_connDescr; QLabel * m_connName; QLabel * m_connType; QLabel * m_propLabel; QLabel * m_placementLabel; QLabel * m_tagLabel; QLabel * m_connLabel; QLabel * m_layerLabel; QLabel * m_lockLabel; QLabel * m_locationLabel; QLabel * m_rotationLabel; QFrame * m_connFrame; QFrame * m_propFrame; QFrame * m_placementFrame; QFrame * m_layerFrame; QFrame * m_lockFrame; QFrame * m_locationFrame; QFrame * m_rotationFrame; QCheckBox * m_lockCheckbox; QCheckBox * m_stickyCheckbox; QGridLayout * m_propLayout; QGridLayout * m_placementLayout; QVBoxLayout * m_layerLayout; QList m_propThings; QPointer m_pendingItemBase; bool m_pendingSwappingEnabled; QWidget * m_layerWidget; QDoubleSpinBox * m_xEdit; QDoubleSpinBox * m_yEdit; QLabel * m_unitsLabel; QDoubleSpinBox * m_rotEdit; // note: these m_last items should only be checked for equality and not otherwise accessed ItemBase * m_lastTitleItemBase; QString m_lastPartTitle; QString m_lastPartVersion; ModelPart * m_lastTagsModelPart; int m_lastConnectorItemCount; ConnectorItem * m_lastConnectorItem; ItemBase * m_lastIconItemBase; ModelPart * m_lastPropsModelPart; ItemBase * m_lastPropsItemBase; bool m_lastPropsSwappingEnabled; bool m_tinyMode; }; #endif fritzing-app-0.9.3b/src/installedfonts.h000066400000000000000000000025651272363601300203030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef INSTALLEDFONTS_H #define INSTALLEDFONTS_H #include #include #include class InstalledFonts { public: static QSet InstalledFontsList; static QMultiHash InstalledFontsNameMapper; // family name to filename; SVG files seem to have to use filename // note: these static variables are initialized in fapplication.cpp }; #endif fritzing-app-0.9.3b/src/itemdrag.cpp000066400000000000000000000036221272363601300173740ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6264 $: $Author: cohen@irascible.com $: $Date: 2012-08-07 17:59:08 +0200 (Di, 07. Aug 2012) $ ********************************************************************/ #include "itemdrag.h" #include "debugdialog.h" ItemDrag * ItemDrag::Singleton = new ItemDrag(); ItemDrag::ItemDrag(QObject * parent) : QObject(parent) { m_originator = NULL; m_originatorIsTempBin = false; } void ItemDrag::__dragIsDone() { m_cache.clear(); emit dragIsDoneSignal(this); } void ItemDrag::cleanup() { if (Singleton) { delete Singleton; Singleton = NULL; } } QHash & ItemDrag::cache() { return Singleton->m_cache; } ItemDrag * ItemDrag::singleton() { return Singleton; } void ItemDrag::dragIsDone() { Singleton->__dragIsDone(); } void ItemDrag::setOriginator(QWidget * originator) { Singleton->m_originator = originator; } QWidget* ItemDrag::originator() { return Singleton->m_originator; } void ItemDrag::setOriginatorIsTempBin(bool itb) { Singleton->m_originatorIsTempBin = itb; } bool ItemDrag::originatorIsTempBin() { return Singleton->m_originatorIsTempBin; } fritzing-app-0.9.3b/src/itemdrag.h000066400000000000000000000033221272363601300170360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6264 $: $Author: cohen@irascible.com $: $Date: 2012-08-07 17:59:08 +0200 (Di, 07. Aug 2012) $ ********************************************************************/ #ifndef ITEMDRAG_H #define ITEMDRAG_H #include #include #include #include class ItemDrag : public QObject { Q_OBJECT protected: ItemDrag(QObject * parent = 0); void __dragIsDone(); public: static ItemDrag * singleton(); static QHash & cache(); static void dragIsDone(); static void cleanup(); static void setOriginator(QWidget *); static QWidget * originator(); static bool originatorIsTempBin(); static void setOriginatorIsTempBin(bool); signals: void dragIsDoneSignal(ItemDrag *); protected: QHash m_cache; QPointer m_originator; bool m_originatorIsTempBin; protected: static ItemDrag * Singleton; }; #endif fritzing-app-0.9.3b/src/items/000077500000000000000000000000001272363601300162125ustar00rootroot00000000000000fritzing-app-0.9.3b/src/items/breadboard.cpp000066400000000000000000000032731272363601300210100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "breadboard.h" Breadboard::Breadboard( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { } Breadboard::~Breadboard() { } ItemBase::PluralType Breadboard::isPlural() { return Plural; } bool Breadboard::rotation45Allowed() { return false; } void Breadboard::hoverUpdate() { } void Breadboard::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); } bool Breadboard::stickyEnabled() { return false; } bool Breadboard::canFindConnectorsUnder() { return false; } fritzing-app-0.9.3b/src/items/breadboard.h000066400000000000000000000031521272363601300204510ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef BREADBOARD_H #define BREADBOARD_H #include "paletteitem.h" class Breadboard : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Breadboard(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Breadboard(); PluralType isPlural(); void hoverUpdate(); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool stickyEnabled(); bool canFindConnectorsUnder(); public: bool rotation45Allowed(); }; #endif fritzing-app-0.9.3b/src/items/capacitor.cpp000066400000000000000000000154511272363601300206710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "capacitor.h" #include "../utils/textutils.h" #include "../utils/focusoutcombobox.h" #include "../utils/boundedregexpvalidator.h" #include "../sketch/infographicsview.h" #include "partlabel.h" // TODO // save into parts bin Capacitor::Capacitor( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { PropertyDefMaster::initPropertyDefs(modelPart, m_propertyDefs); } Capacitor::~Capacitor() { } ItemBase::PluralType Capacitor::isPlural() { return Plural; } bool Capacitor::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { foreach (PropertyDef * propertyDef, m_propertyDefs.keys()) { if (prop.compare(propertyDef->name, Qt::CaseInsensitive) == 0) { returnProp = TranslatedPropertyNames.value(prop); if (returnProp.isEmpty()) { returnProp = propertyDef->name; } FocusOutComboBox * focusOutComboBox = new FocusOutComboBox(); focusOutComboBox->setEnabled(swappingEnabled); focusOutComboBox->setEditable(propertyDef->editable); focusOutComboBox->setObjectName("infoViewComboBox"); QString current = m_propertyDefs.value(propertyDef); if (propertyDef->editable) { focusOutComboBox->setToolTip(tr("Select from the dropdown, or type in a %1 value").arg(returnProp)); } if (propertyDef->numeric) { if (!current.isEmpty()) { double val = TextUtils::convertFromPowerPrefixU(current, propertyDef->symbol); if (!propertyDef->menuItems.contains(val)) { propertyDef->menuItems.append(val); } } foreach(double q, propertyDef->menuItems) { QString s = TextUtils::convertToPowerPrefix(q) + propertyDef->symbol; focusOutComboBox->addItem(s); } } else { if (!current.isEmpty()) { if (!propertyDef->sMenuItems.contains(current)) { propertyDef->sMenuItems.append(current); } } focusOutComboBox->addItems(propertyDef->sMenuItems); } if (!current.isEmpty()) { int ix = focusOutComboBox->findText(current); if (ix < 0) { focusOutComboBox->addItem(current); ix = focusOutComboBox->findText(current); } focusOutComboBox->setCurrentIndex(ix); } if (propertyDef->editable) { BoundedRegExpValidator * validator = new BoundedRegExpValidator(focusOutComboBox); validator->setSymbol(propertyDef->symbol); validator->setConverter(TextUtils::convertFromPowerPrefix); if (propertyDef->maxValue > propertyDef->minValue) { validator->setBounds(propertyDef->minValue, propertyDef->maxValue); } QString pattern = QString("((\\d{1,3})|(\\d{1,3}\\.)|(\\d{1,3}\\.\\d{1,2}))[%1]{0,1}[%2]{0,1}") .arg(TextUtils::PowerPrefixesString) .arg(propertyDef->symbol); validator->setRegExp(QRegExp(pattern)); focusOutComboBox->setValidator(validator); connect(focusOutComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(propertyEntry(const QString &))); } else { connect(focusOutComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(simplePropertyEntry(const QString &))); } this->m_comboBoxes.insert(propertyDef, focusOutComboBox); returnValue = focusOutComboBox->currentText(); returnWidget = focusOutComboBox; return true; } } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void Capacitor::propertyEntry(const QString & text) { FocusOutComboBox * focusOutComboBox = qobject_cast(sender()); if (focusOutComboBox == NULL) return; foreach (PropertyDef * propertyDef, m_comboBoxes.keys()) { if (m_comboBoxes.value(propertyDef) == focusOutComboBox) { QString utext = text; if (propertyDef->numeric) { double val = TextUtils::convertFromPowerPrefixU(utext, propertyDef->symbol); if (!propertyDef->menuItems.contains(val)) { // info view is redrawn, so combobox is recreated, so the new item is added to the combo box menu propertyDef->menuItems.append(val); } } else { if (!propertyDef->sMenuItems.contains(text)) { // info view is redrawn, so combobox is recreated, so the new item is added to the combo box menu propertyDef->sMenuItems.append(text); } } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, propertyDef->name, "", m_propertyDefs.value(propertyDef, ""), utext, true); } break; } } } void Capacitor::setProp(const QString & prop, const QString & value) { foreach (PropertyDef * propertyDef, m_propertyDefs.keys()) { if (prop.compare(propertyDef->name, Qt::CaseInsensitive) == 0) { m_propertyDefs.insert(propertyDef, value); modelPart()->setLocalProp(propertyDef->name, value); if (m_partLabel) m_partLabel->displayTextsIf(); return; } } PaletteItem::setProp(prop, value); } void Capacitor::simplePropertyEntry(const QString & text) { FocusOutComboBox * focusOutComboBox = qobject_cast(sender()); if (focusOutComboBox == NULL) return; foreach (PropertyDef * propertyDef, m_comboBoxes.keys()) { if (m_comboBoxes.value(propertyDef) == focusOutComboBox) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, propertyDef->name, "", m_propertyDefs.value(propertyDef, ""), text, true); } break; } } } void Capacitor::getProperties(QHash & hash) { foreach (PropertyDef * propertyDef, m_propertyDefs.keys()) { hash.insert(propertyDef->name, m_propertyDefs.value(propertyDef)); } } fritzing-app-0.9.3b/src/items/capacitor.h000066400000000000000000000037461272363601300203420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef CAPACITOR_H #define CAPACITOR_H #include "paletteitem.h" #include "propertydef.h" class Capacitor : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Capacitor(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Capacitor(); PluralType isPlural(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); void setProp(const QString & prop, const QString & value); void getProperties(QHash &); public slots: void propertyEntry(const QString & text); void simplePropertyEntry(const QString & text); protected: QHash m_propertyDefs; QHash m_comboBoxes; }; #endif // CAPACITOR_H fritzing-app-0.9.3b/src/items/clipablewire.cpp000066400000000000000000000335571272363601300213750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "clipablewire.h" #include "../connectors/connectoritem.h" #include "../model/modelpart.h" #include "../utils/graphicsutils.h" #include #include static double connectorRectClipInset = 0.5; ///////////////////////////////////////////////////////// #define CONVEX /* ======= Crossings algorithm ============================================ */ // from: http://tog.acm.org/GraphicsGems//gemsiv/ptpoly_haines/ptinpoly.c #define XCOORD 0 #define YCOORD 1 /* Shoot a test ray along +X axis. The strategy, from MacMartin, is to * compare vertex Y values to the testing point's Y and quickly discard * edges which are entirely to one side of the test ray. * * Input 2D polygon _pgon_ with _numverts_ number of vertices and test point * _point_, returns 1 if inside, 0 if outside. WINDING and CONVEX can be * defined for this test. */ int CrossingsTest( double pgon[][2], int numverts, double point[2] ) { #ifdef WINDING register int crossings ; #endif register int j, yflag0, yflag1, inside_flag, xflag0 ; register double ty, tx, *vtx0, *vtx1 ; #ifdef CONVEX register int line_flag ; #endif tx = point[XCOORD] ; ty = point[YCOORD] ; vtx0 = pgon[numverts-1] ; /* get test bit for above/below X axis */ yflag0 = ( vtx0[YCOORD] >= ty ) ; vtx1 = pgon[0] ; #ifdef WINDING crossings = 0 ; #else inside_flag = 0 ; #endif #ifdef CONVEX line_flag = 0 ; #endif for ( j = numverts+1 ; --j ; ) { yflag1 = ( vtx1[YCOORD] >= ty ) ; /* check if endpoints straddle (are on opposite sides) of X axis * (i.e. the Y's differ); if so, +X ray could intersect this edge. */ if ( yflag0 != yflag1 ) { xflag0 = ( vtx0[XCOORD] >= tx ) ; /* check if endpoints are on same side of the Y axis (i.e. X's * are the same); if so, it's easy to test if edge hits or misses. */ if ( xflag0 == ( vtx1[XCOORD] >= tx ) ) { /* if edge's X values both right of the point, must hit */ #ifdef WINDING if ( xflag0 ) crossings += ( yflag0 ? -1 : 1 ) ; #else if ( xflag0 ) inside_flag = !inside_flag ; #endif } else { /* compute intersection of pgon segment with +X ray, note * if >= point's X; if so, the ray hits it. */ if ( (vtx1[XCOORD] - (vtx1[YCOORD]-ty)* ( vtx0[XCOORD]-vtx1[XCOORD])/(vtx0[YCOORD]-vtx1[YCOORD])) >= tx ) { #ifdef WINDING crossings += ( yflag0 ? -1 : 1 ) ; #else inside_flag = !inside_flag ; #endif } } #ifdef CONVEX /* if this is second edge hit, then done testing */ if ( line_flag ) goto Exit ; /* note that one edge has been hit by the ray's line */ line_flag = true; #endif } /* move to next pair of vertices, retaining info as possible */ yflag0 = yflag1 ; vtx0 = vtx1 ; vtx1 += 2 ; } #ifdef CONVEX Exit: ; #endif #ifdef WINDING /* test if crossings is not zero */ inside_flag = (crossings != 0) ; #endif return( inside_flag ) ; } ///////////////////////////////////////////////////////// ClipableWire::ClipableWire( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool initLabel ) : Wire(modelPart, viewID, viewGeometry, id, itemMenu, initLabel) { m_clipEnds = false; m_trackHoverItem = NULL; m_justFilteredEvent = NULL; m_cachedOriginalLine.setPoints(QPointF(-99999,-99999), QPointF(-99999,-99999)); } const QLineF & ClipableWire::getPaintLine() { if (!m_clipEnds) { return Wire::getPaintLine(); } QLineF originalLine = this->line(); //qDebug() << "line" << originalLine.p1() + pos() << originalLine.p2() + pos() << pos(); if (m_cachedOriginalLine == originalLine) { // qDebug() << " cachedline a" << m_cachedLine.p1() + pos() << m_cachedLine.p2() + pos() << pos(); return m_cachedLine; } int t0c = 0; ConnectorItem* to0 = NULL; foreach (ConnectorItem * toConnectorItem, m_connector0->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) { to0 = toConnectorItem; break; } t0c++; } int t1c = 0; ConnectorItem* to1 = NULL; foreach (ConnectorItem * toConnectorItem, m_connector1->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) { to1 = toConnectorItem; break; } t1c++; } if ((to0 == NULL && to1 == NULL) || // no need to clip an unconnected wire (to0 == NULL && t0c == 0) || // dragging out a wire, no need to clip (to1 == NULL && t1c == 0) || // dragging out a wire, no need to clip m_dragEnd) // dragging out a wire, no need to clip { return Wire::getPaintLine(); } QPointF p1 = originalLine.p1(); QPointF p2 = originalLine.p2(); //if (this->viewID() == ViewLayer::PCBView) { // qDebug() << "before" << "p1:" << p1 << "p2:" << p2 << "pos:" << pos() << "pp1:" << pos() + p1 << "pp2:" << pos() + p2; // m_connector0->debugInfo(" c0"); // m_connector1->debugInfo(" c1"); // if (to0) to0->debugInfo(" to0"); // if (to1) to1->debugInfo(" to1"); //} calcClip(p1, p2, to0, to1); //if (this->viewID() == ViewLayer::PCBView) { // qDebug() << "after" << p1 << p2 << this->pos(); // if (to0) to0->debugInfo(" to0"); // if (to1) to1->debugInfo(" to1"); //} m_cachedOriginalLine = originalLine; m_cachedLine.setPoints(p1, p2); //qDebug() << " cachedline b" << m_cachedLine.p1() + pos() << m_cachedLine.p2() + pos() << pos(); return m_cachedLine; } void ClipableWire::setClipEnds(bool clipEnds ) { if (m_clipEnds != clipEnds) { prepareGeometryChange(); m_clipEnds = clipEnds; } } void ClipableWire::calcClip(QPointF & p1, QPointF & p2, ConnectorItem * c1, ConnectorItem * c2) { if (c1 != NULL && c2 != NULL && c1->isEffectivelyCircular() && c2->isEffectivelyCircular()) { //qDebug() << "clause 1" << p1 << p2 << c1->calcClipRadius() + (m_pen.width() / 2.0) << c2->calcClipRadius() + (m_pen.width() / 2.0); //c1->debugInfo(" c1"); //c2->debugInfo(" c2"); GraphicsUtils::shortenLine(p1, p2, c1->calcClipRadius() + (m_pen.width() / 2.0), c2->calcClipRadius() + (m_pen.width() / 2.0)); return; } if (c1 != NULL && c1->isEffectivelyCircular()) { //qDebug() << "clause 2" << p1 << p2 << c1->calcClipRadius() + (m_pen.width() / 2.0) << 0; //c1->debugInfo(" c1"); GraphicsUtils::shortenLine(p1, p2, c1->calcClipRadius() + (m_pen.width() / 2.0), 0); p2 = findIntersection(c2, p2); return; } if (c2 != NULL && c2->isEffectivelyCircular()) { //qDebug() << "clause 3" << p1 << p2 << 0 << c2->calcClipRadius() + (m_pen.width() / 2.0); //c2->debugInfo(" c2"); GraphicsUtils::shortenLine(p1, p2, 0, c2->calcClipRadius() + (m_pen.width() / 2.0)); p1 = findIntersection(c1, p1); return; } //qDebug() << "clause 4" << p1 << p2; //if (c1) c1->debugInfo(" c1"); //if (c2) c2->debugInfo(" c2"); p1 = findIntersection(c1, p1); p2 = findIntersection(c2, p2); } QPointF ClipableWire::findIntersection(ConnectorItem * connectorItem, const QPointF & p) { if (connectorItem == NULL) return p; QRectF r = connectorItem->rect(); r.adjust(connectorRectClipInset, connectorRectClipInset, -connectorRectClipInset, -connectorRectClipInset); // inset it a little bit so the wire touches QPolygonF poly = this->mapFromScene(connectorItem->mapToScene(r)); QLineF l1 = this->line(); int count = poly.count(); for (int i = 0; i < count; i++) { QLineF l2(poly[i], poly[(i + 1) % count]); QPointF intersectingPoint; if (l1.intersect(l2, &intersectingPoint) == QLineF::BoundedIntersection) { return intersectingPoint; } } return this->mapFromScene(connectorItem->mapToScene(r.center())); } bool ClipableWire::filterMousePressConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) { m_justFilteredEvent = NULL; if (!m_clipEnds) return false; if (m_viewID != ViewLayer::PCBView) return false; ConnectorItem * to = NULL; foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) { to = toConnectorItem; break; } } if (to == NULL) return false; if (insideInnerCircle(to, event->scenePos()) || !insideSpoke(this, event->scenePos())) { m_justFilteredEvent = event; event->ignore(); return true; } return false; } void ClipableWire::mousePressEvent(QGraphicsSceneMouseEvent *event) { if ((long) event == (long) m_justFilteredEvent) { event->ignore(); return; } Wire::mousePressEvent(event); } void ClipableWire::hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event , ConnectorItem * item) { //Wire::hoverEnterConnectorItem(event, item); // track mouse move events for hover redirecting ConnectorItem * to = NULL; foreach (ConnectorItem * toConnectorItem, item->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) { to = toConnectorItem; break; } } if (to) { m_trackHoverItem = to; m_trackHoverLastWireItem = NULL; m_trackHoverLastItem = NULL; dispatchHover(event->scenePos()); } } void ClipableWire::hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { Q_UNUSED(item); Q_UNUSED(event); dispatchHoverAux(false, NULL); m_trackHoverItem = NULL; //Wire::hoverLeaveConnectorItem(event, item); } void ClipableWire::hoverMoveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { if (m_trackHoverItem) { dispatchHover(event->scenePos()); } Wire::hoverMoveConnectorItem(event, item); } void ClipableWire::dispatchHover(QPointF scenePos) { bool inInner = false; ClipableWire * inWire = NULL; if (insideInnerCircle(m_trackHoverItem, scenePos)) { //DebugDialog::debug("got inner circle"); inInner = true; } else { foreach (ConnectorItem * toConnectorItem, m_trackHoverItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue; ClipableWire * w = dynamic_cast(toConnectorItem->attachedTo()); if (w == NULL) continue; if (w->getRatsnest()) continue; // is there a better way to check this? if (insideSpoke(w, scenePos)) { //DebugDialog::debug("got inside spoke"); inWire = w; break; } } } dispatchHoverAux(inInner, inWire); } void ClipableWire::dispatchHoverAux(bool inInner, Wire * inWire) { if (m_trackHoverItem == NULL) return; if (inInner) { if (m_trackHoverLastItem == m_trackHoverItem) { // no change return; } if (m_trackHoverLastWireItem) { ((ItemBase *) m_trackHoverLastWireItem)->hoverLeaveConnectorItem(); m_trackHoverLastWireItem = NULL; } m_trackHoverLastItem = m_trackHoverItem; m_trackHoverLastItem->setHoverColor(); m_trackHoverLastItem->attachedTo()->hoverEnterConnectorItem(); } else if (inWire) { if (m_trackHoverLastWireItem == inWire) { // no change return; } if (m_trackHoverLastItem) { QList visited; m_trackHoverLastItem->restoreColor(visited); m_trackHoverLastItem->attachedTo()->hoverLeaveConnectorItem(); m_trackHoverLastItem = NULL; } if (m_trackHoverLastWireItem) { ((ItemBase *) m_trackHoverLastWireItem)->hoverLeaveConnectorItem(); } m_trackHoverLastWireItem = inWire; ((ItemBase *) m_trackHoverLastWireItem)->hoverEnterConnectorItem(); } else { //DebugDialog::debug("got none"); if (m_trackHoverLastItem != NULL) { QList visited; m_trackHoverLastItem->restoreColor(visited); m_trackHoverLastItem->attachedTo()->hoverLeaveConnectorItem(); m_trackHoverLastItem = NULL; } if (m_trackHoverLastWireItem != NULL) { ((ItemBase *) m_trackHoverLastWireItem)->hoverLeaveConnectorItem(); m_trackHoverLastWireItem = NULL; } } } bool ClipableWire::insideInnerCircle(ConnectorItem * connectorItem, QPointF scenePos) { QPointF localPos = connectorItem->mapFromScene(scenePos); double rad = connectorItem->radius(); if (rad <= 0) return false; rad -= ((connectorItem->strokeWidth() / 2.0)); // shrink it a little bit QRectF r = connectorItem->rect(); QPointF c = r.center(); if ( (localPos.x() - c.x()) * (localPos.x() - c.x()) + (localPos.y() - c.y()) * (localPos.y() - c.y()) < rad * rad ) { // inside the inner circle return true; } return false; } bool ClipableWire::insideSpoke(ClipableWire * wire, QPointF scenePos) { QLineF l = wire->line(); QLineF normal = l.normalVector(); normal.setLength(wire->width() / 2.0); double parallelogram[4][2]; parallelogram[0][XCOORD] = normal.p2().x(); parallelogram[0][YCOORD] = normal.p2().y(); parallelogram[1][XCOORD] = normal.p1().x() - normal.dx(); parallelogram[1][YCOORD] = normal.p1().y() - normal.dy(); parallelogram[2][XCOORD] = parallelogram[1][XCOORD] + l.dx(); parallelogram[2][YCOORD] = parallelogram[1][YCOORD] + l.dy(); parallelogram[3][XCOORD] = parallelogram[0][XCOORD] + l.dx(); parallelogram[3][YCOORD] = parallelogram[0][YCOORD] + l.dy(); QPointF mp = wire->mapFromScene(scenePos); double point[2]; point[XCOORD] = mp.x(); point[YCOORD] = mp.y(); if (CrossingsTest(parallelogram, 4, point)) { return true; } return false; } fritzing-app-0.9.3b/src/items/clipablewire.h000066400000000000000000000045471272363601300210370ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef CLIPABLEWIRE_H #define CLIPABLEWIRE_H #include "wire.h" #include class ClipableWire : public Wire { public: ClipableWire( ModelPart * modelPart, ViewLayer::ViewID, const ViewGeometry & , long id, QMenu* itemMenu, bool initLabel); void setClipEnds(bool); const QLineF & getPaintLine(); bool filterMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); void mousePressEvent(QGraphicsSceneMouseEvent *event); void hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, class ConnectorItem * item); void hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, class ConnectorItem * item); void hoverMoveConnectorItem(QGraphicsSceneHoverEvent * event, class ConnectorItem * item); protected: bool insideInnerCircle(ConnectorItem * connectorItem, QPointF localPos); bool insideSpoke(ClipableWire * wire, QPointF scenePos); void dispatchHover(QPointF scenePos); void dispatchHoverAux(bool inInner, Wire * inWire); QPointF findIntersection(ConnectorItem * connectorItem, const QPointF & p); void calcClip(QPointF & p1, QPointF & p2, ConnectorItem * c1, ConnectorItem * c2); protected: bool m_clipEnds; QLineF m_cachedLine; QLineF m_cachedOriginalLine; QGraphicsSceneMouseEvent * m_justFilteredEvent; QPointer m_trackHoverItem; QPointer m_trackHoverLastItem; QPointer m_trackHoverLastWireItem; }; #endif fritzing-app-0.9.3b/src/items/dip.cpp000066400000000000000000000427311272363601300175010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "dip.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../utils/familypropertycombobox.h" #include "../utils/schematicrectconstants.h" #include "../connectors/connectoritem.h" #include "../sketch/infographicsview.h" #include "../fsvgrenderer.h" #include "pinheader.h" #include "partfactory.h" #include static QStringList Spacings; static int MinSipPins = 2; static int MaxSipPins = 128; static int MinDipPins = 4; static int MaxDipPins = 128; ////////////////////////////////////////////////// Dip::Dip( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : MysteryPart(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { } Dip::~Dip() { } bool Dip::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { bool result = MysteryPart::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); if (prop.compare("chip label", Qt::CaseInsensitive) == 0) { returnProp = tr("chip label"); } return result; } bool Dip::isDIP() { QString package = modelPart()->properties().value("package", ""); if (package.indexOf("dip", 0, Qt::CaseInsensitive) >= 0) return true; if (package.indexOf("sip", 0, Qt::CaseInsensitive) >= 0) return false; return family().contains("generic ic", Qt::CaseInsensitive); } bool Dip::otherPropsChange(const QMap & propsMap) { QString layout = modelPart()->properties().value("package", ""); return (layout.compare(propsMap.value("package", "")) != 0); } const QStringList & Dip::spacings() { if (Spacings.count() == 0) { Spacings << "300mil" << "400mil" << "600mil" << "900mil"; } return Spacings; } QString Dip::genSipFZP(const QString & moduleid) { return genxFZP(moduleid, "generic_sip_fzpTemplate", MinSipPins, MaxSipPins, 1); } QString Dip::genDipFZP(const QString & moduleid) { return genxFZP(moduleid, "generic_dip_fzpTemplate", MinDipPins, MaxDipPins, 2); } QStringList Dip::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("pins", Qt::CaseInsensitive) == 0) { QStringList values; value = modelPart()->properties().value("pins"); if (isDIP()) { for (int i = MinDipPins; i <= MaxDipPins; i += 2) { values << QString::number(i); } } else { for (int i = MinSipPins; i <= MaxSipPins; i++) { values << QString::number(i); } } return values; } return MysteryPart::collectValues(family, prop, value); } QString Dip::genModuleID(QMap & currPropsMap) { QString spacing = currPropsMap.value("pin spacing", "300mil"); QString value = currPropsMap.value("package"); QString pins = currPropsMap.value("pins"); if (pins.isEmpty()) pins = "16"; // pick something safe QString moduleID; if (value.contains("sip", Qt::CaseInsensitive)) { return QString("generic_sip_%1_%2").arg(pins).arg(spacing); } else { int p = pins.toInt(); if (p < 4) p = 4; if (p % 2 == 1) p--; return QString("generic_ic_dip_%1_%2").arg(p).arg(spacing); } } QString Dip::retrieveSchematicSvg(QString & svg) { bool hasLocal = false; QStringList labels = getPinLabels(hasLocal); if (hasLocal) { if (this->isDIP()) { svg = makeSchematicSvg(labels); //DebugDialog::debug("make dip " + svg); } else { svg = MysteryPart::makeSchematicSvg(labels, true); } } return TextUtils::replaceTextElement(svg, "label", m_chipLabel); } QString Dip::makeSchematicSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); if (pieces.count() != 5) return ""; QString spacing; int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacing); QStringList labels; for (int i = 0; i < pins; i++) { labels << QString::number(i + 1); } if (expectedFileName.contains("sip", Qt::CaseInsensitive)) { if (expectedFileName.contains(PartFactory::OldSchematicPrefix)) { return MysteryPart::obsoleteMakeSchematicSvg(labels, true); } return MysteryPart::makeSchematicSvg(labels, true); } if (expectedFileName.contains(PartFactory::OldSchematicPrefix)) { return obsoleteMakeSchematicSvg(labels); } return makeSchematicSvg(labels); } QString Dip::makeSchematicSvg(const QStringList & labels) { QDomDocument fakeDoc; QList lefts; QList rights; for (int i = 0; i < labels.count() / 2; i++) { QDomElement element = fakeDoc.createElement("contact"); element.setAttribute("connectorIndex", i); element.setAttribute("name", labels.at(i)); lefts.append(element); int j = labels.count() - i - 1; element = fakeDoc.createElement("contact"); element.setAttribute("connectorIndex", j); element.setAttribute("name", labels.at(j)); rights.append(element); } QList empty; QStringList busNames; QString ic("IC"); return SchematicRectConstants::genSchematicDIP(empty, empty, lefts, rights, empty, busNames, ic, false, false, SchematicRectConstants::simpleGetConnectorName); } QString Dip::obsoleteMakeSchematicSvg(const QStringList & labels) { int pins = labels.count(); int increment = GraphicsUtils::StandardSchematicSeparationMils; int border = 30; double totalHeight = (pins * increment / 2) + increment + border; int pinWidth = increment + increment + border; int centralWidth = increment * 4; int pinTextWidth = 0; int defaultLabelWidth = 30; int labelFontSize = 130; double textMax = defaultLabelWidth; QFont font("Droid Sans", labelFontSize * 72 / GraphicsUtils::StandardFritzingDPI, QFont::Normal); QFontMetricsF fm(font); for (int i = 0; i < labels.count(); i++) { double w = fm.width(labels.at(i)); if (w > textMax) textMax = w; } if (textMax > defaultLabelWidth) { pinTextWidth += (textMax - defaultLabelWidth) * 2 * 1000 / 72; } centralWidth += pinTextWidth; int totalWidth = centralWidth + pinWidth; QString header("\n" "\n" "\n" "\n" "IC\n"); QString svg = header .arg(totalHeight / 1000) .arg(totalHeight) .arg(totalHeight - border) .arg(totalWidth / 1000.0) .arg(totalWidth) .arg(centralWidth) .arg(totalWidth / 2.0) ; QString repeatL("\n" "\n" "\n" "%5\n"); QString repeatR("\n" "\n" "\n" "%5\n"); int y = 300; for (int i = 0; i < pins / 2; i++) { svg += repeatL.arg(15 + y).arg(y).arg(i).arg(y + 50).arg(labels.at(i)).arg(labelFontSize); svg += repeatR .arg(15 + y) .arg(y) .arg(pins - i - 1) .arg(y + 50) .arg(labels.at(pins - i - 1)) .arg(labelFontSize) .arg(totalWidth - 300) .arg(totalWidth - 15) .arg(totalWidth - 300) .arg(totalWidth - 30) .arg(totalWidth - 390) ; y += increment; } svg += "\n"; svg += "\n"; return svg; } QString Dip::makeBreadboardSvg(const QString & expectedFileName) { if (expectedFileName.contains("_sip_")) return makeBreadboardSipSvg(expectedFileName); if (expectedFileName.contains("_dip_")) return makeBreadboardDipSvg(expectedFileName); return ""; } QString Dip::makeBreadboardSipSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); if (pieces.count() != 5) return ""; int pins = pieces.at(2).toInt(); int increment = 10; double totalWidth = (pins * increment); QString svg = TextUtils::incrementTemplate(":/resources/templates/generic_sip_bread_template.txt", 1, increment * (pins - 2), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); QString repeat("\n" "\n" "\n"); QString repeats; if (pins > 2) { repeats = TextUtils::incrementTemplateString(repeat, pins - 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::incCopyPinFunction, NULL); } return svg.arg(totalWidth / 100).arg(pins - 1).arg(repeats); } QString Dip::makeBreadboardDipSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); if (pieces.count() != 6) return ""; int pins = pieces.at(3).toInt(); int increment = 10; double spacing = TextUtils::convertToInches(pieces.at(4)) * 100; QString repeatB("\n" "\n" " \n"); QString repeatT("\n" "\n" "\n"); QString header("\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" "\n" "\n" "\n" "\n" "\n" "IC\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" ".percent.2\n" ".percent.3\n" "\n" "\n"); // header came from a 300mil dip, so base case is spacing - (increment * 3) header = TextUtils::incrementTemplateString(header, 1, spacing - (increment * 3), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); header = header .arg(TextUtils::getViewBoxCoord(header, 3) / 100.0) .arg(pins - 1) .arg((pins / 2) - 1) .arg(pins / 2) .arg(OCRAFontName); header.replace("{{", "["); header.replace("}}", "]"); header = TextUtils::incrementTemplateString(header, 1, (spacing - (increment * 3)) / 2, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); header.replace("{", "["); header.replace("}", "]"); header.replace(".percent.", "%"); QString svg = TextUtils::incrementTemplateString(header, 1, increment * ((pins - 4) / 2), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); repeatB = TextUtils::incrementTemplateString(repeatB, 1, spacing - (increment * 3), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); repeatB.replace("{", "["); repeatB.replace("}", "]"); int userData[2]; userData[0] = pins - 1; userData[1] = 1; QString repeatTs = TextUtils::incrementTemplateString(repeatT, (pins - 4) / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData); QString repeatBs = TextUtils::incrementTemplateString(repeatB, (pins - 4) / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::incCopyPinFunction, NULL); return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100.0).arg(repeatTs).arg(repeatBs); } bool Dip::changePinLabels(bool singleRow, bool sip) { if (m_viewID != ViewLayer::SchematicView) return true; bool hasLocal = false; QStringList labels = getPinLabels(hasLocal); if (labels.count() == 0) return true; QString svg; if (singleRow) { svg = MysteryPart::makeSchematicSvg(labels, sip); } else { svg = Dip::makeSchematicSvg(labels); } QString chipLabel = modelPart()->localProp("chip label").toString(); if (!chipLabel.isEmpty()) { svg =TextUtils::replaceTextElement(svg, "label", chipLabel); } QTransform transform = untransform(); resetLayerKin(svg); resetConnectors(); retransform(transform); return true; } void Dip::addedToScene(bool temporary) { MysteryPart::addedToScene(temporary); if (this->isDIP()) { resetConnectors(); } } void Dip::swapEntry(const QString & text) { FamilyPropertyComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; bool sip = moduleID().contains("sip"); bool dip = moduleID().contains("dip"); if (comboBox->prop().contains("package", Qt::CaseInsensitive)) { sip = text.contains("sip", Qt::CaseInsensitive); dip = text.contains("dip", Qt::CaseInsensitive); if (!dip && !sip) { // one of the generic smds PaletteItem::swapEntry(text); return; } } if (sip) { generateSwap(text, genModuleID, genSipFZP, makeBreadboardSvg, makeSchematicSvg, PinHeader::makePcbSvg); } else if (dip) { generateSwap(text, genModuleID, genDipFZP, makeBreadboardSvg, makeSchematicSvg, makePcbDipSvg); } PaletteItem::swapEntry(text); } fritzing-app-0.9.3b/src/items/dip.h000066400000000000000000000046351272363601300171470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef DIP_H #define DIP_H #include "mysterypart.h" class Dip : public MysteryPart { Q_OBJECT public: Dip(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Dip(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); QStringList collectValues(const QString & family, const QString & prop, QString & value); bool changePinLabels(bool singleRow, bool sip); void addedToScene(bool temporary); public: static QString genSipFZP(const QString & moduleid); static QString genDipFZP(const QString & moduleid); static QString genModuleID(QMap & currPropsMap); static QString makeSchematicSvg(const QString & expectedFileName); static QString makeSchematicSvg(const QStringList & labels); static QString makeBreadboardSvg(const QString & expectedFileName); static QString makeBreadboardSipSvg(const QString & expectedFileName); static QString makeBreadboardDipSvg(const QString & expectedFileName); static QString obsoleteMakeSchematicSvg(const QStringList & labels); public slots: void swapEntry(const QString & text); protected: bool isDIP(); bool otherPropsChange(const QMap & propsMap); const QStringList & spacings(); QString retrieveSchematicSvg(QString & svg); }; #endif fritzing-app-0.9.3b/src/items/groundplane.cpp000066400000000000000000000153271272363601300212440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "groundplane.h" #include "../connectors/connectoritem.h" #include "../fsvgrenderer.h" #include "../model/modelpart.h" #include "../utils/graphicsutils.h" #include "../svg/svgfilesplitter.h" #include "../svg/groundplanegenerator.h" #include "../sketch/infographicsview.h" #include "../layerattributes.h" #include // TODO: // // delete part if copper fill fails, and remove item from undo stack // maybe that means the generator has to kick in on the dropped item, and if it fails then // there is no undo stack to worry about; if it succeeds, then copy the svg prop. // // does single poly need a connector? // QString GroundPlane::fillTypeIndividual = "individual"; QString GroundPlane::fillTypeGround = "ground"; QString GroundPlane::fillTypePlain = "plain"; QString GroundPlane::fillTypeNone = "none"; static QString IconSvg; void loadIconSvg() { if (IconSvg.isEmpty()) { QFile f(":resources/parts/svg/core/icon/groundplane.svg"); if (f.open(QFile::ReadOnly)) { IconSvg = f.readAll(); f.close(); } } } ///////////////////////////////////////////////////////// GroundPlane::GroundPlane( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_connector0 = NULL; } bool GroundPlane::setUpImage(ModelPart * modelPart, const LayerHash & viewLayers, LayerAttributes & layerAttributes) { bool result = PaletteItem::setUpImage(modelPart, viewLayers, layerAttributes); if (layerAttributes.doConnectors) { foreach (ConnectorItem * item, cachedConnectorItems()) { // there is only one m_connector0 = item; break; } } return result; } void GroundPlane::saveParams() { } void GroundPlane::getParams() { } QString GroundPlane::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { QString xml = ""; if (viewLayerID == ViewLayer::GroundPlane0 || viewLayerID == ViewLayer::GroundPlane1) { xml = prop("svg"); if (!xml.isEmpty()) { return PaletteItemBase::normalizeSvg(xml, viewLayerID, blackOnly, dpi, factor); } } return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } ConnectorItem * GroundPlane::connector0() { return m_connector0; } bool GroundPlane::hasCustomSVG() { switch (m_viewID) { case ViewLayer::PCBView: return true; default: return ItemBase::hasCustomSVG(); } } void GroundPlane::setProp(const QString & prop, const QString & value) { if (prop.compare("svg", Qt::CaseInsensitive) == 0) { setSvg(value); return; } PaletteItemBase::setProp(prop, value); } void GroundPlane::addedToScene(bool temporary) { if (m_viewLayerID == ViewLayer::GroundPlane0 || m_viewLayerID == ViewLayer::GroundPlane1) { if (this->scene()) { QString svg; if (temporary) { loadIconSvg(); svg = IconSvg; } else { svg = prop("svg"); } if (!svg.isEmpty()) { setSvgAux(svg); } } } PaletteItem::addedToScene(temporary); } void GroundPlane::setSvg(const QString & svg) { modelPart()->setLocalProp("svg", svg); setSvgAux(svg); } void GroundPlane::setSvgAux(const QString & svg) { if (svg.isEmpty()) return; if (m_viewID == ViewLayer::BreadboardView || m_viewID == ViewLayer::SchematicView) return; QString xmlName = ViewLayer::viewLayerXmlNameFromID(m_viewLayerID); SvgFileSplitter splitter; QString cpy = svg; bool result = splitter.splitString(cpy, xmlName); if (result) { resetRenderer(svg); if (m_connector0) { QPainterPath painterPath = splitter.painterPath(GraphicsUtils::SVGDPI, GroundPlaneGenerator::ConnectorName); m_connector0->setRect(painterPath.boundingRect()); m_connector0->setShape(painterPath); } //QPainterPath painterPath = splitter.painterPath(GraphicsUtils::SVGDPI, xmlName); //this->setShape(painterPath); } } QString GroundPlane::svg() { return prop("svg"); } bool GroundPlane::hasPartLabel() { return false; } void GroundPlane::loadLayerKin( const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement viewLayerPlacement) { Q_UNUSED(viewLayers); Q_UNUSED(viewLayerPlacement); // GroundPlane is only on one side or another, not both } bool GroundPlane::hasPartNumberProperty() { return false; } bool GroundPlane::rotationAllowed() { return false; } bool GroundPlane::rotation45Allowed() { return false; } ItemBase::PluralType GroundPlane::isPlural() { return Singular; } bool GroundPlane::canEditPart() { return false; } QString GroundPlane::generateSvg() { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return ""; QPointF q = this->boundingRect().center() + this->pos(); //QPointF r = this->pos() + m_dropOffset; return infoGraphicsView->generateCopperFillUnit(this, q); } void GroundPlane::setDropOffset(QPointF offset) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; m_dropOffset = offset; modelPart()->setLocalProp("fillType", fillTypeIndividual); QString svg = generateSvg(); bool resolve = true; if (svg.isEmpty()) { loadIconSvg(); svg = IconSvg; resolve = false; } setSvg(svg); infoGraphicsView->resolveTemporary(resolve, this); } void GroundPlane::setShape(QPainterPath & pp) { QPen pen; m_shape = GraphicsUtils::shapeFromPath(pp, pen, pen.widthF(), true); } QPainterPath GroundPlane::shape() const { if (!m_shape.isEmpty()) { return m_shape; } return PaletteItemBase::shape(); } ViewLayer::ViewID GroundPlane::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::PCBView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } fritzing-app-0.9.3b/src/items/groundplane.h000066400000000000000000000046341272363601300207100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef GROUNDPLANE_H #define GROUNDPLANE_H #include "paletteitem.h" class GroundPlane : public PaletteItem { public: GroundPlane( ModelPart * modelPart, ViewLayer::ViewID, const ViewGeometry & , long id, QMenu* itemMenu, bool doLabel); bool setUpImage(ModelPart* modelPart, const LayerHash & viewLayers, LayerAttributes &); void saveParams(); void getParams(); QString retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); class ConnectorItem * connector0(); bool hasCustomSVG(); void setProp(const QString & prop, const QString & value); QString svg(); bool hasPartLabel(); void loadLayerKin( const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement viewLayerPlacement); void addedToScene(bool temporary); bool hasPartNumberProperty(); bool rotationAllowed(); bool rotation45Allowed(); PluralType isPlural(); bool canEditPart(); void setDropOffset(QPointF offset); void setShape(QPainterPath &); QPainterPath shape() const; public: static QString fillTypeIndividual; static QString fillTypeGround; static QString fillTypePlain; static QString fillTypeNone; protected: void setSvg(const QString &); void setSvgAux(const QString &); QString generateSvg(); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); protected: ConnectorItem * m_connector0; QPointF m_dropOffset; QPainterPath m_shape; }; #endif fritzing-app-0.9.3b/src/items/hole.cpp000066400000000000000000000234311272363601300176500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "hole.h" #include "../utils/graphicsutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../commands.h" #include "../utils/textutils.h" #include "../viewlayer.h" #include "partlabel.h" #include "../connectors/nonconnectoritem.h" #include "../connectors/svgidlayer.h" #include #include #include #include #include #include #include #include #include #include const double Hole::OffsetPixels = 2; static HoleClassThing TheHoleThing; ////////////////////////////////////////////////// Hole::Hole( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { PaletteItem::setUpHoleSizes("hole", TheHoleThing); } Hole::~Hole() { } void Hole::setProp(const QString & prop, const QString & value) { if (prop.compare("hole size", Qt::CaseInsensitive) == 0) { setHoleSize(value, false); return; } PaletteItem::setProp(prop, value); } QString Hole::holeSize() { return m_holeSettings.holeSize(); } void Hole::setHoleSize(QString newSize, bool force) { //DebugDialog::debug(QString("old holesize %1").arg(viewID()) + holeSize(), sceneBoundingRect()); //foreach (QGraphicsItem * childItem, childItems()) { // DebugDialog::debug(QString(" child"), childItem->sceneBoundingRect()); //} if (PaletteItem::setHoleSize(newSize, force, m_holeSettings)) { setBoth(m_holeSettings.holeDiameter, m_holeSettings.ringThickness); modelPart()->setLocalProp("hole size", newSize); if (m_partLabel) m_partLabel->displayTextsIf(); } //DebugDialog::debug(QString("new holesize %1 ").arg(viewID()) + holeSize(), sceneBoundingRect()); //foreach (QGraphicsItem * childItem, childItems()) { // DebugDialog::debug(QString(" child"), childItem->sceneBoundingRect()); //} } QRectF Hole::getRect(const QString & newSize) { QString s = newSize; QStringList sizes = getSizes(s, m_holeSettings); if (sizes.count() < 2) return boundingRect(); double diameter = TextUtils::convertToInches(sizes.at(0)); double ringThickness = TextUtils::convertToInches(sizes.at(1)); double dim = (diameter + ringThickness + ringThickness) * GraphicsUtils::SVGDPI; //DebugDialog::debug(QString("get rect %1 %2").arg(newSize).arg(dim)); return QRectF(0, 0, dim, dim); } void Hole::setBoth(const QString & holeDiameter, const QString & ringThickness) { if (this->m_viewID != ViewLayer::PCBView) return; ItemBase * otherLayer = setBothSvg(holeDiameter, ringThickness); // there's only one NonConnectorItem foreach (SvgIdLayer * svgIdLayer, fsvgRenderer()->setUpNonConnectors(viewLayerPlacement())) { if (svgIdLayer == NULL) continue; setBothNonConnectors(this, svgIdLayer); if (otherLayer != NULL) { setBothNonConnectors(otherLayer, svgIdLayer); } delete svgIdLayer; } } ItemBase * Hole::setBothSvg(const QString & holeDiameter, const QString & ringThickness) { QString svg = makeSvg(holeDiameter, ringThickness, m_viewLayerID, true); resetRenderer(svg); //DebugDialog::debug("both"); //DebugDialog::debug(svg); QString setColor; QStringList noIDs; QString osvg; ItemBase * otherLayer = NULL; foreach (ItemBase * layerKin, m_layerKin) { if (layerKin->hasNonConnectors()) { otherLayer = layerKin; break; } } if (otherLayer) { osvg = makeSvg(holeDiameter, ringThickness, otherLayer->viewLayerID(), true); //DebugDialog::debug(osvg); otherLayer->resetRenderer(osvg); } //DebugDialog::debug("other"); return otherLayer; } void Hole::setBothNonConnectors(ItemBase * itemBase, SvgIdLayer * svgIdLayer) { foreach (QGraphicsItem * child, itemBase->childItems()) { NonConnectorItem * nonConnectorItem = dynamic_cast(child); if (nonConnectorItem == NULL) continue; //DebugDialog::debug(QString("hole set rect %1").arg(m_viewID), svgIdLayer->m_rect); nonConnectorItem->setRect(svgIdLayer->rect(viewLayerPlacement())); nonConnectorItem->setRadius(svgIdLayer->m_radius, svgIdLayer->m_strokeWidth); break; } } QString Hole::makeSvg(const QString & holeDiameter, const QString & ringThickness, ViewLayer::ViewLayerID viewLayerID, bool includeHole) { double offsetDPI = OffsetPixels / GraphicsUtils::SVGDPI; double hd = TextUtils::convertToInches(holeDiameter); double rt = TextUtils::convertToInches(ringThickness); double wInches = hd + rt + rt + offsetDPI + offsetDPI; QString svg = TextUtils::makeSVGHeader(1, GraphicsUtils::StandardFritzingDPI, wInches, wInches); hd *= GraphicsUtils::StandardFritzingDPI; rt *= GraphicsUtils::StandardFritzingDPI; offsetDPI *= GraphicsUtils::StandardFritzingDPI; QString setColor; if (viewLayerID == ViewLayer::Copper0) { setColor = ViewLayer::Copper0Color; } else if (viewLayerID == ViewLayer::Copper1) { setColor = ViewLayer::Copper1Color; } svg += QString("").arg(ViewLayer::viewLayerXmlNameFromID(viewLayerID)); QString id = makeID(); if (hd == 0) { svg += QString("") .arg(rt + offsetDPI) .arg(rt) .arg(setColor) .arg(id); } else { svg += QString("") .arg((hd / 2) + rt + offsetDPI) .arg((hd / 2) + (rt / 2)) .arg(rt) .arg(setColor) .arg(id) .arg(rt == 0 ? "black" : "none") ; if (includeHole) { svg += QString("") // set the drill attribute to 0 for gerber translation .arg((hd / 2) + rt + offsetDPI) .arg(hd / 2); } } svg += ""; //DebugDialog::debug("hole svg " + svg); return svg; } QString Hole::makeID() { return FSvgRenderer::NonConnectorName + "0"; } QString Hole::getProperty(const QString & key) { if (key.compare("hole size", Qt::CaseInsensitive) == 0) { return m_holeSettings.holeDiameter; } return PaletteItem::getProperty(key); } void Hole::addedToScene(bool temporary) { if (this->scene()) { setHoleSize(m_holeSettings.holeSize(), true); } return PaletteItem::addedToScene(temporary); } bool Hole::hasCustomSVG() { switch (m_viewID) { case ViewLayer::PCBView: return true; default: return ItemBase::hasCustomSVG(); } } ItemBase::PluralType Hole::isPlural() { return Plural; } QString Hole::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { if (m_viewID != ViewLayer::PCBView || (viewLayerID != ViewLayer::Copper0 && viewLayerID != ViewLayer::Copper1)) { return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QStringList holeSize = m_modelPart->localProp("hole size").toString().split(","); if (holeSize.length() == 2) { QString svg = makeSvg(holeSize.at(0), holeSize.at(1), viewLayerID, false); if (!svg.isEmpty()) { return PaletteItemBase::normalizeSvg(svg, viewLayerID, blackOnly, dpi, factor); } } return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } bool Hole::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("hole size", Qt::CaseInsensitive) == 0) { return collectHoleSizeInfo(TheHoleThing.holeSizeValue, parent, swappingEnabled, returnProp, returnValue, returnWidget); } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void Hole::changeHoleSize(const QString & newSize) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { QRectF holeRect = getRect(holeSize()); QRectF newHoleRect = getRect(newSize); infoGraphicsView->setHoleSize(this, "hole size", tr("hole size"), holeSize(), newSize, holeRect, newHoleRect, true); } } bool Hole::canEditPart() { return false; } bool Hole::hasPartNumberProperty() { return false; } bool Hole::rotationAllowed() { return false; } bool Hole::rotation45Allowed() { return false; } bool Hole::canFindConnectorsUnder() { return false; } ViewLayer::ViewID Hole::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::PCBView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } void Hole::changeUnits(bool) { QString newVal = PaletteItem::changeUnits(m_holeSettings); modelPart()->setLocalProp("hole size", newVal); } QRectF Hole::trueSceneBoundingRect() { QRectF r = sceneBoundingRect(); return r.adjusted(OffsetPixels, OffsetPixels, -OffsetPixels, -OffsetPixels); } fritzing-app-0.9.3b/src/items/hole.h000066400000000000000000000055471272363601300173250ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef HOLE_H #define HOLE_H #include #include #include #include #include #include "paletteitem.h" class Hole : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Hole(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Hole(); QString getProperty(const QString & key); void setProp(const QString & prop, const QString & value); void setHoleSize(QString holeSize, bool force); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool hasCustomSVG(); PluralType isPlural(); QString retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool canEditPart(); void addedToScene(bool temporary); bool hasPartNumberProperty(); QString holeSize(); bool rotationAllowed(); bool rotation45Allowed(); bool canFindConnectorsUnder(); QRectF trueSceneBoundingRect(); protected slots: void changeHoleSize(const QString &); void changeUnits(bool); protected: QString makeSvg(const QString & holeDiameter, const QString & ringThickness, ViewLayer::ViewLayerID, bool includeHole); virtual QString makeID(); ItemBase * setBothSvg(const QString & holeDiameter, const QString & ringThickness); void setBothNonConnectors(ItemBase * itemBase, SvgIdLayer * svgIdLayer); virtual void setBoth(const QString & holeDiameter, const QString & thickness); QRectF getRect(const QString & newSize); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); public: static const double OffsetPixels; }; #endif fritzing-app-0.9.3b/src/items/itembase.cpp000066400000000000000000002140201272363601300205060ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include "itembase.h" #include "partfactory.h" #include "../debugdialog.h" #include "../model/modelpart.h" #include "../connectors/connectoritem.h" #include "../connectors/connectorshared.h" #include "../sketch/infographicsview.h" #include "../connectors/connector.h" #include "../connectors/bus.h" #include "partlabel.h" #include "../layerattributes.h" #include "../fsvgrenderer.h" #include "../svg/svgfilesplitter.h" #include "../svg/svgflattener.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../utils/cursormaster.h" #include "../utils/clickablelabel.h" #include "../utils/familypropertycombobox.h" #include "../referencemodel/referencemodel.h" #include #include #include #include #include #include #include #include #include #include ///////////////////////////////// static QRegExp NumberMatcher; static QHash NumberMatcherValues; static const double InactiveOpacity = 0.4; bool numberValueLessThan(QString v1, QString v2) { return NumberMatcherValues.value(v1, 0) <= NumberMatcherValues.value(v2, 0); } static QSvgRenderer MoveLockRenderer; static QSvgRenderer StickyRenderer; ///////////////////////////////// class NameTriple { public: NameTriple(const QString & _xmlName, const QString & _viewName, const QString & _naturalName) { m_xmlName = _xmlName; m_viewName = _viewName; m_naturalName = _naturalName; } QString & xmlName() { return m_xmlName; } QString & viewName() { return m_viewName; } QString & naturalName() { return m_naturalName; } protected: QString m_xmlName; QString m_naturalName; QString m_viewName; }; ///////////////////////////////// const QString ItemBase::ITEMBASE_FONT_PREFIX = ""; const QString ItemBase::ITEMBASE_FONT_SUFFIX = ""; QHash ItemBase::TranslatedPropertyNames; QPointer ItemBase::TheReferenceModel = NULL; QString ItemBase::PartInstanceDefaultTitle; const QList ItemBase::EmptyList; const QColor ItemBase::HoverColor(0,0,0); const double ItemBase::HoverOpacity = .20; const QColor ItemBase::ConnectorHoverColor(0,0,255); const double ItemBase::ConnectorHoverOpacity = .40; const QColor StandardConnectedColor(0, 255, 0); const QColor StandardUnconnectedColor(255, 0, 0); QPen ItemBase::NormalPen(QColor(255,0,0)); QPen ItemBase::HoverPen(QColor(0, 0, 255)); QPen ItemBase::ConnectedPen(StandardConnectedColor); QPen ItemBase::UnconnectedPen(StandardUnconnectedColor); QPen ItemBase::ChosenPen(QColor(255,0,0)); QPen ItemBase::EqualPotentialPen(QColor(255,255,0)); QBrush ItemBase::NormalBrush(QColor(255,0,0)); QBrush ItemBase::HoverBrush(QColor(0,0,255)); QBrush ItemBase::ConnectedBrush(StandardConnectedColor); QBrush ItemBase::UnconnectedBrush(StandardUnconnectedColor); QBrush ItemBase::ChosenBrush(QColor(255,0,0)); QBrush ItemBase::EqualPotentialBrush(QColor(255,255,0)); const double ItemBase::NormalConnectorOpacity = 0.4; static QHash CachedValues; /////////////////////////////////////////////////// ItemBase::ItemBase( ModelPart* modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu ) : QGraphicsSvgItem() { m_fsvgRenderer = NULL; m_superpart = NULL; m_acceptsMousePressLegEvent = true; m_squashShape = false; //DebugDialog::debug(QString("itembase %1 %2").arg(id).arg((long) static_cast(this), 0, 16)); m_hasRubberBandLeg = m_moveLock = m_hoverEnterSpaceBarWasPressed = m_spaceBarWasPressed = false; m_moveLockItem = NULL; m_stickyItem = NULL; m_swappable = m_everVisible = true; m_rightClickedConnector = NULL; m_partLabel = NULL; m_itemMenu = itemMenu; m_hoverCount = m_connectorHoverCount = m_connectorHoverCount2 = 0; m_viewID = viewID; m_modelPart = modelPart; if (m_modelPart) { m_modelPart->addViewItem(this); } m_id = id; m_canFlipHorizontal = m_canFlipVertical = m_sticky = m_inRotation = m_inactive = m_layerHidden = m_hidden = false; setCursor(*CursorMaster::MoveCursor); m_viewGeometry.set(viewGeometry); setAcceptHoverEvents ( true ); m_zUninitialized = true; } ItemBase::~ItemBase() { //DebugDialog::debug(QString("deleting itembase %1 %2 %3").arg((long) this, 0, 16).arg(m_id).arg((long) m_modelPart, 0, 16)); if (m_partLabel) { delete m_partLabel; m_partLabel = NULL; } foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { toConnectorItem->tempRemove(connectorItem, true); } } foreach (ItemBase * itemBase, m_stickyList) { itemBase->addSticky(this, false); } if (m_modelPart != NULL) { m_modelPart->removeViewItem(this); } if (m_fsvgRenderer) { delete m_fsvgRenderer; } } void ItemBase::setTooltip() { if(m_modelPart) { QString title = instanceTitle(); if(!title.isNull() && !title.isEmpty()) { setInstanceTitleTooltip(title); } else { setDefaultTooltip(); } } else { setDefaultTooltip(); } } void ItemBase::removeTooltip() { this->setToolTip(___emptyString___); } bool ItemBase::zLessThan(ItemBase * & p1, ItemBase * & p2) { return p1->z() < p2->z(); } qint64 ItemBase::getNextID() { return ModelPart::nextIndex() * ModelPart::indexMultiplier; // make sure we leave room for layerkin inbetween } qint64 ItemBase::getNextID(qint64 index) { qint64 temp = index * ModelPart::indexMultiplier; // make sure we leave room for layerkin inbetween ModelPart::updateIndex(index); return temp; } QSizeF ItemBase::size() { return m_size; } qint64 ItemBase::id() const { return m_id; } void ItemBase::resetID() { m_id = m_modelPart->modelIndex() * ModelPart::indexMultiplier; } double ItemBase::z() { return getViewGeometry().z(); } ModelPart * ItemBase::modelPart() { return m_modelPart; } void ItemBase::setModelPart(ModelPart * modelPart) { m_modelPart = modelPart; } ModelPartShared * ItemBase::modelPartShared() { if (m_modelPart == NULL) return NULL; return m_modelPart->modelPartShared(); } void ItemBase::initNames() { if (NumberMatcher.isEmpty()) { NumberMatcher.setPattern(QString("(([0-9]+(\\.[0-9]*)?)|\\.[0-9]+)([\\s]*([") + TextUtils::PowerPrefixesString + "]))?"); } if (TranslatedPropertyNames.count() == 0) { TranslatedPropertyNames.insert("family", tr("family")); TranslatedPropertyNames.insert("type", tr("type")); TranslatedPropertyNames.insert("model", tr("model")); TranslatedPropertyNames.insert("size", tr("size")); TranslatedPropertyNames.insert("color", tr("color")); TranslatedPropertyNames.insert("resistance", tr("resistance")); TranslatedPropertyNames.insert("capacitance", tr("capacitance")); TranslatedPropertyNames.insert("inductance", tr("inductance")); TranslatedPropertyNames.insert("voltage", tr("voltage")); TranslatedPropertyNames.insert("current", tr("current")); TranslatedPropertyNames.insert("power", tr("power")); TranslatedPropertyNames.insert("pin spacing", tr("pin spacing")); TranslatedPropertyNames.insert("rated power", tr("rated power")); TranslatedPropertyNames.insert("rated voltage", tr("rated voltage")); TranslatedPropertyNames.insert("rated current", tr("rated current")); TranslatedPropertyNames.insert("version", tr("version")); TranslatedPropertyNames.insert("package", tr("package")); TranslatedPropertyNames.insert("shape", tr("shape")); TranslatedPropertyNames.insert("form", tr("form")); TranslatedPropertyNames.insert("part number", tr("part number")); TranslatedPropertyNames.insert("maximum resistance", tr("maximum resistance")); TranslatedPropertyNames.insert("pins", tr("pins")); TranslatedPropertyNames.insert("spacing", tr("spacing")); TranslatedPropertyNames.insert("pin spacing", tr("pin spacing")); TranslatedPropertyNames.insert("frequency", tr("frequency")); TranslatedPropertyNames.insert("processor", tr("processor")); TranslatedPropertyNames.insert("variant", tr("variant")); TranslatedPropertyNames.insert("layers", tr("layers")); TranslatedPropertyNames.insert("tolerance", tr("tolerance")); TranslatedPropertyNames.insert("descr", tr("descr")); TranslatedPropertyNames.insert("filename", tr("filename")); TranslatedPropertyNames.insert("title", tr("title")); TranslatedPropertyNames.insert("date", tr("date")); TranslatedPropertyNames.insert("rev", tr("rev")); TranslatedPropertyNames.insert("sheet", tr("sheet")); TranslatedPropertyNames.insert("project", tr("project")); TranslatedPropertyNames.insert("banded", tr("banded")); TranslatedPropertyNames.insert("top", tr("top")); TranslatedPropertyNames.insert("bottom", tr("bottom")); TranslatedPropertyNames.insert("copper bottom", tr("copper bottom")); TranslatedPropertyNames.insert("copper top", tr("copper top")); TranslatedPropertyNames.insert("silkscreen bottom", tr("silkscreen bottom")); TranslatedPropertyNames.insert("silkscreen top", tr("silkscreen top")); // TODO: translate more known property names from fzp files and resource xml files } PartInstanceDefaultTitle = tr("Part"); QSettings settings; QString colorName = settings.value("ConnectedColor").toString(); if (!colorName.isEmpty()) { QColor color; color.setNamedColor(colorName); setConnectedColor(color); } colorName = settings.value("UnconnectedColor").toString(); if (!colorName.isEmpty()) { QColor color; color.setNamedColor(colorName); setUnconnectedColor(color); } } void ItemBase::saveInstance(QXmlStreamWriter & streamWriter) { streamWriter.writeStartElement(ViewLayer::viewIDXmlName(m_viewID)); streamWriter.writeAttribute("layer", ViewLayer::viewLayerXmlNameFromID(m_viewLayerID)); if (m_moveLock) { streamWriter.writeAttribute("locked", "true"); } if (m_superpart) { streamWriter.writeAttribute("superpart", QString::number(m_superpart->modelPart()->modelIndex())); } if (m_viewLayerPlacement == ViewLayer::NewBottom && m_viewID == ViewLayer::PCBView) { streamWriter.writeAttribute("bottom", "true"); } this->saveGeometry(); writeGeometry(streamWriter); if (m_partLabel) { m_partLabel->saveInstance(streamWriter); } QList itemBases; itemBases.append(this); itemBases.append(layerKinChief()->layerKin()); foreach (ItemBase * itemBase, itemBases) { if (itemBase->layerHidden()) { streamWriter.writeStartElement("layerHidden"); streamWriter.writeAttribute("layer", ViewLayer::viewLayerXmlNameFromID(itemBase->viewLayerID())); streamWriter.writeEndElement(); } } bool saveConnectorItems = false; foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->connectionsCount() > 0 || connectorItem->hasRubberBandLeg() || connectorItem->isGroundFillSeed()) { saveConnectorItems = true; break; } } if (saveConnectorItems) { streamWriter.writeStartElement("connectors"); foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { connectorItem->saveInstance(streamWriter); } streamWriter.writeEndElement(); } streamWriter.writeEndElement(); } void ItemBase::writeGeometry(QXmlStreamWriter & streamWriter) { streamWriter.writeStartElement("geometry"); streamWriter.writeAttribute("z", QString::number(z())); this->saveInstanceLocation(streamWriter); // do not write attributes here streamWriter.writeEndElement(); } ViewGeometry & ItemBase::getViewGeometry() { return m_viewGeometry; } ViewLayer::ViewID ItemBase::viewID() { return m_viewID; } QString & ItemBase::viewIDName() { return ViewLayer::viewIDName(m_viewID); } ViewLayer::ViewLayerID ItemBase::viewLayerID() const { return m_viewLayerID; } void ItemBase::setViewLayerID(const QString & layerName, const LayerHash & viewLayers) { //DebugDialog::debug(QString("using z %1").arg(layerName)); setViewLayerID(ViewLayer::viewLayerIDFromXmlString(layerName), viewLayers); } void ItemBase::setViewLayerID(ViewLayer::ViewLayerID viewLayerID, const LayerHash & viewLayers) { m_viewLayerID = viewLayerID; if (m_zUninitialized) { ViewLayer * viewLayer = viewLayers.value(m_viewLayerID); if (viewLayer != NULL) { m_zUninitialized = false; if (!viewLayer->alreadyInLayer(m_viewGeometry.z())) { m_viewGeometry.setZ(viewLayer->nextZ()); } } } //DebugDialog::debug(QString("using z: %1 z:%2 lid:%3").arg(title()).arg(m_viewGeometry.z()).arg(m_viewLayerID) ); } void ItemBase::removeLayerKin() { } void ItemBase::hoverEnterConnectorItem(QGraphicsSceneHoverEvent * , ConnectorItem * ) { //DebugDialog::debug(QString("hover enter c %1").arg(instanceTitle())); hoverEnterConnectorItem(); } void ItemBase::hoverEnterConnectorItem() { //DebugDialog::debug(QString("hover enter c %1").arg(instanceTitle())); m_connectorHoverCount++; hoverUpdate(); } void ItemBase::hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * , ConnectorItem * ) { hoverLeaveConnectorItem(); } void ItemBase::hoverMoveConnectorItem(QGraphicsSceneHoverEvent * , ConnectorItem * ) { } void ItemBase::hoverLeaveConnectorItem() { //DebugDialog::debug(QString("hover leave c %1").arg(instanceTitle())); m_connectorHoverCount--; hoverUpdate(); } void ItemBase::clearConnectorHover() { m_connectorHoverCount2 = 0; hoverUpdate(); } void ItemBase::connectorHover(ConnectorItem *, ItemBase *, bool hovering) { //DebugDialog::debug(QString("hover c %1 %2").arg(hovering).arg(instanceTitle())); if (hovering) { m_connectorHoverCount2++; } else { m_connectorHoverCount2--; } // DebugDialog::debug(QString("m_connectorHoverCount2 %1 %2").arg(instanceTitle()).arg(m_connectorHoverCount2)); hoverUpdate(); } void ItemBase::hoverUpdate() { this->update(); } void ItemBase::mousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { } void ItemBase::mouseDoubleClickConnectorEvent(ConnectorItem *) { } void ItemBase::mouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { } void ItemBase::mouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { } bool ItemBase::filterMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return false; } bool ItemBase::acceptsMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return true; } bool ItemBase::acceptsMousePressLegEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return m_acceptsMousePressLegEvent; } void ItemBase::setAcceptsMousePressLegEvent(bool b) { m_acceptsMousePressLegEvent = b; } bool ItemBase::acceptsMouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return false; } bool ItemBase::acceptsMouseDoubleClickConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return false; } bool ItemBase::acceptsMouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return false; } void ItemBase::connectionChange(ConnectorItem * onMe, ConnectorItem * onIt, bool connect) { Q_UNUSED(onMe); Q_UNUSED(onIt); Q_UNUSED(connect); } void ItemBase::connectedMoved(ConnectorItem * from, ConnectorItem * to, QList & already) { Q_UNUSED(from); Q_UNUSED(to); Q_UNUSED(already); } ItemBase * ItemBase::extractTopLevelItemBase(QGraphicsItem * item) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) return NULL; if (itemBase->topLevel()) return itemBase; return NULL; } bool ItemBase::topLevel() { return (this == this->layerKinChief()); } void ItemBase::setHidden(bool hide) { m_hidden = hide; updateHidden(); foreach (QGraphicsItem * item, childItems()) { NonConnectorItem * nonconnectorItem = dynamic_cast(item); if (nonconnectorItem == NULL) continue; nonconnectorItem->setHidden(hide); } } void ItemBase::setInactive(bool inactivate) { m_inactive = inactivate; updateHidden(); foreach (QGraphicsItem * item, childItems()) { NonConnectorItem * nonconnectorItem = dynamic_cast(item); if (nonconnectorItem == NULL) continue; nonconnectorItem->setInactive(inactivate); } } void ItemBase::setLayerHidden(bool layerHidden) { m_layerHidden = layerHidden; updateHidden(); foreach (QGraphicsItem * item, childItems()) { NonConnectorItem * nonconnectorItem = dynamic_cast(item); if (nonconnectorItem == NULL) continue; nonconnectorItem->setLayerHidden(layerHidden); } } void ItemBase::updateHidden() { setAcceptedMouseButtons(m_hidden || m_inactive || m_layerHidden ? Qt::NoButton : ALLMOUSEBUTTONS); setAcceptHoverEvents(!(m_hidden || m_inactive || m_layerHidden)); update(); } bool ItemBase::hidden() { return m_hidden; } bool ItemBase::layerHidden() { return m_layerHidden; } bool ItemBase::inactive() { return m_inactive; } void ItemBase::collectConnectors(ConnectorPairHash & connectorHash, SkipCheckFunction skipCheckFunction) { // Is this modelpart check obsolete? ModelPart * modelPart = this->modelPart(); if (modelPart == NULL) return; // collect all the connectorItem pairs foreach (ConnectorItem * fromConnectorItem, cachedConnectorItems()) { foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { if (skipCheckFunction && skipCheckFunction(toConnectorItem)) continue; connectorHash.insert(fromConnectorItem, toConnectorItem); } ConnectorItem * crossConnectorItem = fromConnectorItem->getCrossLayerConnectorItem(); if (crossConnectorItem == NULL) continue; foreach (ConnectorItem * toConnectorItem, crossConnectorItem->connectedToItems()) { if (skipCheckFunction && skipCheckFunction(toConnectorItem)) continue; connectorHash.insert(crossConnectorItem, toConnectorItem); } } } ConnectorItem * ItemBase::findConnectorItemWithSharedID(const QString & connectorID) { Connector * connector = modelPart()->getConnector(connectorID); if (connector) { return connector->connectorItem(m_viewID); } return NULL; } ConnectorItem * ItemBase::findConnectorItemWithSharedID(const QString & connectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement) { ConnectorItem * connectorItem = findConnectorItemWithSharedID(connectorID); if (connectorItem) { return connectorItem->chooseFromSpec(viewLayerPlacement); } return NULL; } void ItemBase::hoverEnterEvent ( QGraphicsSceneHoverEvent * event ) { // debugInfo("itembase hover enter"); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) { m_hoverEnterSpaceBarWasPressed = true; event->ignore(); return; } m_hoverEnterSpaceBarWasPressed = false; m_hoverCount++; //debugInfo(QString("inc hover %1").arg(m_hoverCount)); hoverUpdate(); if (infoGraphicsView != NULL) { infoGraphicsView->hoverEnterItem(event, this); } } void ItemBase::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) { //DebugDialog::debug(QString("hover leave %1").arg(instanceTitle())); if (m_hoverEnterSpaceBarWasPressed) { event->ignore(); return; } m_hoverCount--; //debugInfo(QString("dec hover %1").arg(m_hoverCount)); hoverUpdate(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->hoverLeaveItem(event, this); } } void ItemBase::updateConnections(bool includeRatsnest, QList & already) { Q_UNUSED(already) Q_UNUSED(includeRatsnest); } void ItemBase::updateConnections(ConnectorItem * connectorItem, bool includeRatsnest, QList & already) { if (!already.contains(connectorItem)) { already << connectorItem; connectorItem->attachedMoved(includeRatsnest, already); } else { connectorItem->debugInfo("already"); } } const QString & ItemBase::title() { if (m_modelPart == NULL) return ___emptyString___; return m_modelPart->title(); } const QString & ItemBase::constTitle() const { if (m_modelPart == NULL) return ___emptyString___; return m_modelPart->title(); } const QString & ItemBase::spice() const { if (m_modelPart == NULL) return ___emptyString___; return m_modelPart->spice(); } const QString & ItemBase::spiceModel() const { if (m_modelPart == NULL) return ___emptyString___; return m_modelPart->spiceModel(); } bool ItemBase::getRatsnest() { return m_viewGeometry.getRatsnest(); } QList ItemBase::buses() { QList busList; if (m_modelPart == NULL) return busList; foreach (Bus * bus, m_modelPart->buses().values()) { busList.append(bus); } return busList; } void ItemBase::busConnectorItems(class Bus * bus, ConnectorItem * fromConnectorItem, QList & items) { Q_UNUSED(fromConnectorItem) if (bus == NULL) return; foreach (Connector * connector, bus->connectors()) { foreach (ConnectorItem * connectorItem, connector->viewItems()) { if (connectorItem != NULL) { //connectorItem->debugInfo(QString("on the bus %1").arg((long) connector, 0, 16)); if (connectorItem->attachedTo() == this) { items.append(connectorItem); } } } } if (m_superpart || m_subparts.count() > 0) { Connector * connector = bus->subConnector(); if (connector) { foreach (ConnectorItem * connectorItem, connector->viewItems()) { if (connectorItem != NULL) { //connectorItem->debugInfo(QString("on the bus %1").arg((long) connector, 0, 16)); if (connectorItem->attachedToViewID() == m_viewID) { items.append(connectorItem); } } } } } /* if (items.count() > 0) { fromConnectorItem->debugInfo("bus"); foreach (ConnectorItem * ci, items) { ci->debugInfo("\t"); } } */ } int ItemBase::itemType() const { if (m_modelPart == NULL) return ModelPart::Unknown; return m_modelPart->itemType(); } bool ItemBase::inHover() { return (!m_inactive && (m_connectorHoverCount > 0 || m_hoverCount > 0 || m_connectorHoverCount2 > 0)); } void ItemBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (inHover()) { //DebugDialog::debug(QString("chc:%1 hc:%2 chc2:%3").arg(m_connectorHoverCount).arg(m_hoverCount).arg(m_connectorHoverCount2)); layerKinChief()->paintHover(painter, option, widget); } //else { //DebugDialog::debug("no hover"); //} if (m_inactive) { painter->save(); painter->setOpacity(InactiveOpacity); } paintBody(painter, option, widget); if (option->state & QStyle::State_Selected) { layerKinChief()->paintSelected(painter, option, widget); } if (m_inactive) { painter->restore(); } } void ItemBase::paintBody(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); // Qt's SVG renderer's defaultSize is not correct when the svg has a fractional pixel size fsvgRenderer()->render(painter, boundingRectWithoutLegs()); } void ItemBase::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { paintHover(painter, option, widget, hoverShape()); } void ItemBase::paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget); GraphicsUtils::qt_graphicsItem_highlightSelected(painter, option, boundingRect(), hoverShape()); } void ItemBase::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, const QPainterPath & shape) { Q_UNUSED(widget); Q_UNUSED(option); painter->save(); if (m_connectorHoverCount > 0 || m_connectorHoverCount2 > 0) { painter->setOpacity(ConnectorHoverOpacity); painter->fillPath(shape, QBrush(ConnectorHoverColor)); } else { painter->setOpacity(HoverOpacity); painter->fillPath(shape, QBrush(HoverColor)); } painter->restore(); } void ItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event) { InfoGraphicsView *infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) { event->ignore(); return; } //scene()->setItemIndexMethod(QGraphicsScene::NoIndex); //setCacheMode(QGraphicsItem::DeviceCoordinateCache); QGraphicsSvgItem::mousePressEvent(event); } void ItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { m_rightClickedConnector = NULL; // calling parent class so that multiple selection will work // haven't yet discovered any nasty side-effect QGraphicsSvgItem::mouseReleaseEvent(event); //scene()->setItemIndexMethod(QGraphicsScene::BspTreeIndex); // setCacheMode(QGraphicsItem::NoCache); } void ItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *) { } void ItemBase::setItemPos(QPointF & loc) { setPos(loc); } bool ItemBase::stickyEnabled() { return true; } bool ItemBase::isSticky() { return isBaseSticky() && isLocalSticky(); } bool ItemBase::isBaseSticky() { return m_sticky; // to cancel sticky return false; } void ItemBase::setSticky(bool s) { m_sticky = s; } bool ItemBase::isLocalSticky() { if (layerKinChief() != this) { return layerKinChief()->isLocalSticky(); } QString stickyVal = modelPart()->localProp("sticky").toString(); // return (stickyVal.compare("false") != 0); // defaults to true return (stickyVal.compare("true") == 0); // defaults to false } void ItemBase::setLocalSticky(bool s) { // dirty the window? // undo command? if (layerKinChief() != this) { layerKinChief()->setLocalSticky(s); return; } modelPart()->setLocalProp("sticky", s ? "true" : "false"); if (s) { if (m_stickyItem == NULL) { if (!StickyRenderer.isValid()) { QString fn(":resources/images/part_sticky.svg"); bool success = StickyRenderer.load(fn); Q_UNUSED(success); //DebugDialog::debug(QString("sticky load success %1").arg(success)); } m_stickyItem = new QGraphicsSvgItem(); m_stickyItem->setAcceptHoverEvents(false); m_stickyItem->setAcceptedMouseButtons(Qt::NoButton); m_stickyItem->setSharedRenderer(&StickyRenderer); m_stickyItem->setPos(m_moveLockItem == NULL ? 0 : m_moveLockItem->boundingRect().width() + 1, 0); m_stickyItem->setZValue(-99999); m_stickyItem->setParentItem(this); m_stickyItem->setVisible(true); } } else { if (m_stickyItem) { delete m_stickyItem; m_stickyItem = NULL; } } update(); } void ItemBase::addSticky(ItemBase * stickyBase, bool stickem) { stickyBase = stickyBase->layerKinChief(); //this->debugInfo(QString("add sticky %1:").arg(stickem)); //sticky->debugInfo(QString(" to")); if (stickem) { if (!isBaseSticky()) { foreach (ItemBase * oldstickingTo, m_stickyList.values()) { if (oldstickingTo == stickyBase) continue; oldstickingTo->addSticky(this, false); } m_stickyList.clear(); } m_stickyList.insert(stickyBase->id(), stickyBase); } else { m_stickyList.remove(stickyBase->id()); } } ItemBase * ItemBase::stickingTo() { if (isBaseSticky()) return NULL; if (m_stickyList.count() < 1) return NULL; if (m_stickyList.count() > 1) { DebugDialog::debug(QString("error: sticky list > 1 %1").arg(title())); } return *m_stickyList.begin(); } QList< QPointer > ItemBase::stickyList() { return m_stickyList.values(); } bool ItemBase::alreadySticking(ItemBase * itemBase) { return m_stickyList.value(itemBase->layerKinChief()->id(), NULL) != NULL; } ConnectorItem* ItemBase::newConnectorItem(Connector *connector) { return newConnectorItem(this, connector); } ConnectorItem* ItemBase::newConnectorItem(ItemBase * layerKin, Connector *connector) { return new ConnectorItem(connector, layerKin); } ConnectorItem * ItemBase::anyConnectorItem() { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { return connectorItem; } return NULL; } const QString & ItemBase::instanceTitle() const { if (m_modelPart) { return m_modelPart->instanceTitle(); } return ___emptyString___; } void ItemBase::setInstanceTitle(const QString &title, bool initial) { setInstanceTitleAux(title, initial); if (m_partLabel) { m_partLabel->setPlainText(title); } } void ItemBase::updatePartLabelInstanceTitle() { if (m_partLabel) { m_partLabel->setPlainText(instanceTitle()); } } void ItemBase::setInstanceTitleAux(const QString &title, bool initial) { if (m_modelPart) { m_modelPart->setInstanceTitle(title, initial); } setInstanceTitleTooltip(title); // InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); // if (infographics != NULL) { // infographics->setItemTooltip(this, title); // } } QString ItemBase::label() { if(m_modelPart) { return m_modelPart->label(); } return ___emptyString___; } void ItemBase::updateTooltip() { setInstanceTitleTooltip(instanceTitle()); } void ItemBase::setInstanceTitleTooltip(const QString &text) { setToolTip(""+text+"

" + ITEMBASE_FONT_PREFIX + title()+ ITEMBASE_FONT_SUFFIX); } void ItemBase::setDefaultTooltip() { if (m_modelPart) { if (m_viewID == ViewLayer::IconView) { QString base = ITEMBASE_FONT_PREFIX + "%1" + ITEMBASE_FONT_SUFFIX; if(m_modelPart->itemType() != ModelPart::Wire) { this->setToolTip(base.arg(m_modelPart->title())); } else { this->setToolTip(base.arg(m_modelPart->title() + " (" + m_modelPart->moduleID() + ")")); } return; } QString title = ItemBase::PartInstanceDefaultTitle; QString inst = instanceTitle(); if(!inst.isNull() && !inst.isEmpty()) { title = inst; } else { QString defaultTitle = label(); if(!defaultTitle.isNull() && !defaultTitle.isEmpty()) { title = defaultTitle; } } ensureUniqueTitle(title, false); setInstanceTitleTooltip(instanceTitle()); } } void ItemBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { if ((acceptedMouseButtons() & Qt::RightButton) == 0) { event->ignore(); return; } if (m_hidden || m_inactive || m_layerHidden) { event->ignore(); return; } scene()->clearSelection(); setSelected(true); if (m_itemMenu != NULL) { m_rightClickedConnector = NULL; foreach (QGraphicsItem * item, scene()->items(event->scenePos())) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (connectorItem->attachedTo() == this) { m_rightClickedConnector = connectorItem; break; } } m_itemMenu->exec(event->screenPos()); } } bool ItemBase::hasConnectors() { return cachedConnectorItems().count() > 0; } bool ItemBase::hasNonConnectors() { foreach (QGraphicsItem * childItem, childItems()) { if (dynamic_cast(childItem) != NULL) return true; } return false; } bool ItemBase::canFlip(Qt::Orientations orientations) { bool result = true; if (orientations & Qt::Horizontal) { result = result && m_canFlipHorizontal; } if (orientations & Qt::Vertical) { result = result && m_canFlipVertical; } return result; } bool ItemBase::canFlipHorizontal() { return m_canFlipHorizontal && !m_moveLock; } void ItemBase::setCanFlipHorizontal(bool cf) { m_canFlipHorizontal = cf; } bool ItemBase::canFlipVertical() { return m_canFlipVertical && !m_moveLock; } void ItemBase::setCanFlipVertical(bool cf) { m_canFlipVertical = cf; } bool ItemBase::rotationAllowed() { return !m_moveLock; } bool ItemBase::rotation45Allowed() { return !m_moveLock; } bool ItemBase::freeRotationAllowed() { return false; } void ItemBase::clearModelPart() { m_modelPart = NULL; } void ItemBase::hidePartLabel() { InfoGraphicsView *infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) infoGraphicsView->hidePartLabel(this); } void ItemBase::showPartLabel(bool showIt, ViewLayer* viewLayer) { if (m_partLabel) { m_partLabel->showLabel(showIt, viewLayer); } } void ItemBase::partLabelChanged(const QString & newText) { // sent from part label after inline edit InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); QString oldText = modelPart()->instanceTitle(); setInstanceTitleAux(newText, false); if (infographics != NULL) { infographics->partLabelChanged(this, oldText, newText); } } bool ItemBase::isPartLabelVisible() { if (m_partLabel == NULL) return false; if (!hasPartLabel()) return false; if (!m_partLabel->initialized()) return false; return m_partLabel->isVisible(); } void ItemBase::clearPartLabel() { m_partLabel = NULL; } void ItemBase::restorePartLabel(QDomElement & labelGeometry, ViewLayer::ViewLayerID viewLayerID) { if (m_partLabel) { m_partLabel->setPlainText(instanceTitle()); if (!labelGeometry.isNull()) { m_partLabel->restoreLabel(labelGeometry, viewLayerID); //m_partLabel->setPlainText(instanceTitle()); } } } void ItemBase::movePartLabel(QPointF newPos, QPointF newOffset) { if (m_partLabel) { m_partLabel->moveLabel(newPos, newOffset); } } void ItemBase::partLabelSetHidden(bool hide) { if (m_partLabel) { m_partLabel->setHidden(hide); } } void ItemBase::partLabelMoved(QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->partLabelMoved(this, oldPos, oldOffset, newPos, newOffset); } } void ItemBase::rotateFlipPartLabel(double degrees, Qt::Orientations orientation) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->rotateFlipPartLabel(this, degrees, orientation); } } void ItemBase::doRotateFlipPartLabel(double degrees, Qt::Orientations orientation) { if (m_partLabel) { m_partLabel->rotateFlipLabel(degrees, orientation); } } bool ItemBase::isSwappable() { return m_swappable; } void ItemBase::setSwappable(bool swappable) { m_swappable = swappable; } void ItemBase::ensureUniqueTitle(const QString & title, bool force) { if (force || instanceTitle().isEmpty() || instanceTitle().isNull()) { setInstanceTitle(modelPart()->getNextTitle(title), true); } } QVariant ItemBase::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value) { switch (change) { case QGraphicsItem::ItemSelectedChange: if (m_partLabel) { m_partLabel->ownerSelected(value.toBool()); } break; default: break; } return QGraphicsSvgItem::itemChange(change, value); } void ItemBase::cleanup() { } const QList & ItemBase::layerKin() { return EmptyList; } ItemBase * ItemBase::layerKinChief() { return this; } void ItemBase::rotateItem(double degrees, bool includeRatsnest) { //this->debugInfo(QString("\trotating item %1").arg(degrees)); transformItem(QTransform().rotate(degrees), includeRatsnest); } void ItemBase::flipItem(Qt::Orientations orientation) { int xScale; int yScale; if(orientation == Qt::Vertical) { xScale = 1; yScale = -1; } else if(orientation == Qt::Horizontal) { xScale = -1; yScale = 1; } else { return; } transformItem(QTransform().scale(xScale,yScale), false); } void ItemBase::transformItem(const QTransform & currTransf, bool includeRatsnest) { //debugInfo("transform item " + TextUtils::svgMatrix(currTransf)); QTransform trns = getViewGeometry().transform(); //debugInfo("\t" + TextUtils::svgMatrix(trns)); if (m_hasRubberBandLeg) { prepareGeometryChange(); } QRectF rect = this->boundingRectWithoutLegs(); //debugInfo(QString("\t bounding rect w:%1, h:%2").arg(rect.width()).arg(rect.height())); double x = rect.width() / 2.0; double y = rect.height() / 2.0; QTransform transf = QTransform().translate(-x, -y) * currTransf * QTransform().translate(x, y); getViewGeometry().setTransform(getViewGeometry().transform()*transf); this->setTransform(getViewGeometry().transform()); if (!m_hasRubberBandLeg) { QList already; updateConnections(includeRatsnest, already); } trns = getViewGeometry().transform(); //debugInfo("\t" + TextUtils::svgMatrix(trns)); update(); } void ItemBase::transformItem2(const QMatrix & matrix) { QTransform transform(matrix); transformItem(transform, false); } void ItemBase::collectWireConnectees(QSet & wires) { Q_UNUSED(wires); } bool ItemBase::collectFemaleConnectees(QSet & items) { Q_UNUSED(items); return false; // means no male connectors } void ItemBase::prepareGeometryChange() { // made public so it can be invoked from outside ItemBase class //debugInfo("itembase prepare geometry change"); QGraphicsSvgItem::prepareGeometryChange(); } void ItemBase::saveLocAndTransform(QXmlStreamWriter & streamWriter) { streamWriter.writeAttribute("x", QString::number(m_viewGeometry.loc().x())); streamWriter.writeAttribute("y", QString::number(m_viewGeometry.loc().y())); GraphicsUtils::saveTransform(streamWriter, m_viewGeometry.transform()); } FSvgRenderer * ItemBase::setUpImage(ModelPart * modelPart, LayerAttributes & layerAttributes) { // at this point "this" has not yet been added to the scene, so one cannot get back to the InfoGraphicsView ModelPartShared * modelPartShared = modelPart->modelPartShared(); if (modelPartShared == NULL) { layerAttributes.error = tr("model part problem"); return NULL; } //if (modelPartShared->moduleID() == "df9d072afa2b594ac67b60b4153ff57b_29" && viewID == ViewLayer::PCBView) { // DebugDialog::debug("here i am now"); //} //DebugDialog::debug(QString("setting z %1 %2") //.arg(this->z()) //.arg(ViewLayer::viewLayerNameFromID(viewLayerID)) ); //DebugDialog::debug(QString("set up image elapsed (1) %1").arg(t.elapsed()) ); QString filename = PartFactory::getSvgFilename(modelPart, modelPartShared->imageFileName(layerAttributes.viewID, layerAttributes.viewLayerID), true, true); //#ifndef QT_NO_DEBUG //DebugDialog::debug(QString("set up image elapsed (2) %1").arg(t.elapsed()) ); //#endif if (filename.isEmpty()) { //QString deleteme = modelPartShared->domDocument()->toString(); layerAttributes.error = tr("file for %1 %2 not found").arg(modelPartShared->title()).arg(modelPartShared->moduleID()); return NULL; } LoadInfo loadInfo; switch (layerAttributes.viewID) { case ViewLayer::PCBView: loadInfo.colorElementID = ViewLayer::viewLayerXmlNameFromID(layerAttributes.viewLayerID); switch (layerAttributes.viewLayerID) { case ViewLayer::Copper0: modelPartShared->connectorIDs(layerAttributes.viewID, layerAttributes.viewLayerID, loadInfo.connectorIDs, loadInfo.terminalIDs, loadInfo.legIDs); loadInfo.setColor = ViewLayer::Copper0Color; loadInfo.findNonConnectors = loadInfo.parsePaths = true; break; case ViewLayer::Copper1: modelPartShared->connectorIDs(layerAttributes.viewID, layerAttributes.viewLayerID, loadInfo.connectorIDs, loadInfo.terminalIDs, loadInfo.legIDs); loadInfo.setColor = ViewLayer::Copper1Color; loadInfo.findNonConnectors = loadInfo.parsePaths = true; break; case ViewLayer::Silkscreen1: loadInfo.setColor = ViewLayer::Silkscreen1Color; break; case ViewLayer::Silkscreen0: loadInfo.setColor = ViewLayer::Silkscreen0Color; break; default: break; } break; case ViewLayer::BreadboardView: modelPartShared->connectorIDs(layerAttributes.viewID, layerAttributes.viewLayerID, loadInfo.connectorIDs, loadInfo.terminalIDs, loadInfo.legIDs); break; default: // don't need connectorIDs() for schematic view since these parts do not have bendable legs or connectors with drill holes break; } FSvgRenderer * newRenderer = new FSvgRenderer(); QDomDocument flipDoc; getFlipDoc(modelPart, filename, layerAttributes.viewLayerID, layerAttributes.viewLayerPlacement, flipDoc, layerAttributes.orientation); QByteArray bytesToLoad; if (layerAttributes.viewLayerID == ViewLayer::Schematic) { bytesToLoad = SvgFileSplitter::hideText(filename); } else if (layerAttributes.viewLayerID == ViewLayer::SchematicText) { bool hasText = false; bytesToLoad = SvgFileSplitter::showText(filename, hasText); if (!hasText) { return NULL; } } else if ((layerAttributes.viewID != ViewLayer::IconView) && modelPartShared->hasMultipleLayers(layerAttributes.viewID)) { QString layerName = ViewLayer::viewLayerXmlNameFromID(layerAttributes.viewLayerID); // need to treat create "virtual" svg file for each layer SvgFileSplitter svgFileSplitter; bool result; if (flipDoc.isNull()) { result = svgFileSplitter.split(filename, layerName); } else { QString f = flipDoc.toString(); result = svgFileSplitter.splitString(f, layerName); } if (result) { bytesToLoad = svgFileSplitter.byteArray(); } } else { // only one layer, just load it directly if (flipDoc.isNull()) { QFile file(filename); file.open(QFile::ReadOnly); bytesToLoad = file.readAll(); } else { bytesToLoad = flipDoc.toByteArray(); } } QByteArray resultBytes; if (!bytesToLoad.isEmpty()) { if (makeLocalModifications(bytesToLoad, filename)) { if (layerAttributes.viewLayerID == ViewLayer::Schematic) { bytesToLoad = SvgFileSplitter::hideText2(bytesToLoad); } else if (layerAttributes.viewLayerID == ViewLayer::SchematicText) { bool hasText; bytesToLoad = SvgFileSplitter::showText2(bytesToLoad, hasText); } } loadInfo.filename = filename; resultBytes = newRenderer->loadSvg(bytesToLoad, loadInfo); } layerAttributes.setLoaded(resultBytes); #ifndef QT_NO_DEBUG // DebugDialog::debug(QString("set up image elapsed (2.3) %1").arg(t.elapsed()) ); #endif if (resultBytes.isEmpty()) { delete newRenderer; layerAttributes.error = tr("unable to create renderer for svg %1").arg(filename); newRenderer = NULL; } //DebugDialog::debug(QString("set up image elapsed (3) %1").arg(t.elapsed()) ); if (newRenderer) { layerAttributes.setFilename(newRenderer->filename()); if (layerAttributes.createShape) { createShape(layerAttributes); } } return newRenderer; } void ItemBase::updateConnectionsAux(bool includeRatsnest, QList & already) { //DebugDialog::debug("update connections"); foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { updateConnections(connectorItem, includeRatsnest, already); } } void ItemBase::figureHover() { } QString ItemBase::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { Q_UNUSED(viewLayerID); Q_UNUSED(svgHash); Q_UNUSED(blackOnly); Q_UNUSED(dpi); factor = 1; return ""; } bool ItemBase::hasConnections() { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->connectionsCount() > 0) return true; } return false; } void ItemBase::getConnectedColor(ConnectorItem *, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { brush = ConnectedBrush; pen = ConnectedPen; opacity = 0.2; negativePenWidth = 0; negativeOffsetRect = true; } void ItemBase::getNormalColor(ConnectorItem *, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { brush = NormalBrush; pen = NormalPen; opacity = NormalConnectorOpacity; negativePenWidth = 0; negativeOffsetRect = true; } void ItemBase::getUnconnectedColor(ConnectorItem *, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { brush = UnconnectedBrush; pen = UnconnectedPen; opacity = 0.3; negativePenWidth = 0; negativeOffsetRect = true; } void ItemBase::getHoverColor(ConnectorItem *, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { brush = HoverBrush; pen = HoverPen; opacity = NormalConnectorOpacity; negativePenWidth = 0; negativeOffsetRect = true; } void ItemBase::getEqualPotentialColor(ConnectorItem *, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { brush = EqualPotentialBrush; pen = EqualPotentialPen; opacity = 1.0; negativePenWidth = 0; negativeOffsetRect = true; } void ItemBase::slamZ(double newZ) { double z = qFloor(m_viewGeometry.z()) + newZ; m_viewGeometry.setZ(z); setZValue(z); } bool ItemBase::isEverVisible() { return m_everVisible; } void ItemBase::setEverVisible(bool v) { m_everVisible = v; } bool ItemBase::connectionIsAllowed(ConnectorItem * other) { return ViewLayer::canConnect(this->viewLayerID(), other->attachedToViewLayerID()); } QString ItemBase::getProperty(const QString & key) { if (m_modelPart == NULL) return ""; QString result = m_modelPart->localProp(key).toString(); if (!result.isEmpty()) return result; return m_modelPart->properties().value(key, ""); } ConnectorItem * ItemBase::rightClickedConnector() { return m_rightClickedConnector; } QColor ItemBase::connectedColor() { return ConnectedPen.color(); } QColor ItemBase::unconnectedColor() { return UnconnectedPen.color(); } QColor ItemBase::standardConnectedColor() { return StandardConnectedColor; } QColor ItemBase::standardUnconnectedColor() { return StandardUnconnectedColor; } void ItemBase::setConnectedColor(QColor & c) { ConnectedPen.setColor(c); ConnectedBrush.setColor(c); } void ItemBase::setUnconnectedColor(QColor & c) { UnconnectedPen.setColor(c); UnconnectedBrush.setColor(c); } QString ItemBase::translatePropertyName(const QString & key) { return TranslatedPropertyNames.value(key.toLower(), key); } bool ItemBase::canEditPart() { return false; } bool ItemBase::hasCustomSVG() { return false; } void ItemBase::setProp(const QString & prop, const QString & value) { if (!m_modelPart) return; //DebugDialog::debug(QString("setting prop %1 %2").arg(prop).arg(value)); m_modelPart->setLocalProp(prop, value); } QString ItemBase::prop(const QString & p) { if (m_modelPart == NULL) return ""; return m_modelPart->localProp(p).toString(); } bool ItemBase::isObsolete() { if (modelPart() == NULL) return false; return modelPart()->isObsolete(); } bool ItemBase::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { Q_UNUSED(hide); // assume this is set by the caller (HtmlInfoView) returnWidget = NULL; returnProp = ItemBase::translatePropertyName(prop); returnValue = value; if (prop.compare("family", Qt::CaseInsensitive) == 0) { return true; } if (prop.compare("id", Qt::CaseInsensitive) == 0) { return true; } #ifndef QT_NO_DEBUG if (prop.compare("svg", Qt::CaseInsensitive) == 0 || prop.compare("fzp" , Qt::CaseInsensitive) == 0) { QFileInfo fileInfo(value); if (fileInfo.exists()) { ClickableLabel * label = new ClickableLabel(fileInfo.fileName(), parent); label->setProperty("path", value); label->setToolTip(value); connect(label, SIGNAL(clicked()), this, SLOT(showInFolder())); returnWidget = label; } return true; } #endif QString tempValue = value; QStringList values = collectValues(family, prop, tempValue); if (values.count() > 1) { FamilyPropertyComboBox * comboBox = new FamilyPropertyComboBox(family, prop, parent); comboBox->setObjectName("infoViewComboBox"); comboBox->addItems(values); comboBox->setCurrentIndex(comboBox->findText(tempValue)); comboBox->setEnabled(swappingEnabled); comboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(swapEntry(const QString &))); returnWidget = comboBox; m_propsMap.insert(prop, tempValue); return true; } return true; } void ItemBase::swapEntry(const QString & text) { FamilyPropertyComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; m_propsMap.insert(comboBox->prop(), text); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->swap(comboBox->family(), comboBox->prop(), m_propsMap, this); } } void ItemBase::setReferenceModel(ReferenceModel * rm) { TheReferenceModel = rm; } QStringList ItemBase::collectValues(const QString & family, const QString & prop, QString & value) { Q_UNUSED(value); if (TheReferenceModel == NULL) return ___emptyStringList___; QStringList values = CachedValues.value(family + prop, QStringList()); if (values.count() > 0) return values; values = TheReferenceModel->propValues(family, prop, true); // sort values numerically NumberMatcherValues.clear(); bool ok = true; foreach(QString opt, values) { int ix = NumberMatcher.indexIn(opt); if (ix < 0) { ok = false; break; } double n = TextUtils::convertFromPowerPrefix(NumberMatcher.cap(1) + NumberMatcher.cap(5), ""); NumberMatcherValues.insert(opt, n); } if (ok) { qSort(values.begin(), values.end(), numberValueLessThan); } CachedValues.insert(family + prop, values); //debugInfo("cached " + prop); //foreach(QString v, values) { // DebugDialog::debug("\t" + v); //} return values; } void ItemBase::resetValues(const QString & family, const QString & prop) { CachedValues.remove(family + prop); } bool ItemBase::hasPartLabel() { return true; } const QString & ItemBase::filename() { return m_filename; } void ItemBase::setFilename(const QString & fn) { m_filename = fn; } ItemBase::PluralType ItemBase::isPlural() { return ItemBase::NotSure; } ViewLayer::ViewLayerPlacement ItemBase::viewLayerPlacement() const { return m_viewLayerPlacement; } void ItemBase::setViewLayerPlacement(ViewLayer::ViewLayerPlacement viewLayerPlacement) { m_viewLayerPlacement = viewLayerPlacement; } ViewLayer::ViewLayerID ItemBase::partLabelViewLayerID() { if (m_partLabel == NULL) return ViewLayer::UnknownLayer; if (!m_partLabel->initialized()) return ViewLayer::UnknownLayer; return m_partLabel->viewLayerID(); } QString ItemBase::makePartLabelSvg(bool blackOnly, double dpi, double printerScale) { if (m_partLabel == NULL) return ""; if (!m_partLabel->initialized()) return ""; return m_partLabel->makeSvg(blackOnly, dpi, printerScale, true); } QPointF ItemBase::partLabelScenePos() { if (m_partLabel == NULL) return QPointF(); if (!m_partLabel->initialized()) return QPointF(); return m_partLabel->scenePos(); } QRectF ItemBase::partLabelSceneBoundingRect() { if (m_partLabel == NULL) return QRectF(); if (!m_partLabel->initialized()) return QRectF(); return m_partLabel->sceneBoundingRect(); } bool ItemBase::getFlipDoc(ModelPart * modelPart, const QString & filename, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement viewLayerPlacement, QDomDocument & flipDoc, Qt::Orientations orientation) { if (!modelPart->flippedSMD()) { // add copper1 layer to THT if it is missing fixCopper1(modelPart, filename, viewLayerID, viewLayerPlacement, flipDoc); } if (viewLayerPlacement == ViewLayer::NewBottom) { if (modelPart->flippedSMD()) { if (viewLayerID == ViewLayer::Copper0) { SvgFlattener::flipSMDSvg(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper0), GraphicsUtils::SVGDPI, orientation); return true; } else if (viewLayerID == ViewLayer::Silkscreen0) { SvgFlattener::flipSMDSvg(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen1), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen0), GraphicsUtils::SVGDPI, orientation); return true; } return false; } if (modelPart->itemType() == ModelPart::Part) { if (viewLayerID == ViewLayer::Copper0) { SvgFlattener::replaceElementID(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper0), ""); //QString t1 = flipDoc.toString(); SvgFlattener::flipSMDSvg(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper0), GraphicsUtils::SVGDPI, orientation); //QString t2 = flipDoc.toString(); return true; } if (viewLayerID == ViewLayer::Copper1) { SvgFlattener::replaceElementID(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1), ""); //QString t1 = flipDoc.toString(); SvgFlattener::flipSMDSvg(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper0), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1), GraphicsUtils::SVGDPI, orientation); //QString t2 = flipDoc.toString(); return true; } else if (viewLayerID == ViewLayer::Silkscreen0) { SvgFlattener::flipSMDSvg(filename, "", flipDoc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen1), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen0), GraphicsUtils::SVGDPI, orientation); return true; } } } return false; } bool ItemBase::fixCopper1(ModelPart * modelPart, const QString & filename, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement viewLayerPlacement, QDomDocument & doc) { Q_UNUSED(viewLayerPlacement); if (viewLayerID != ViewLayer::Copper1) return false; if (!modelPart->needsCopper1()) return false; return TextUtils::addCopper1(filename, doc, ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper0), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1)); } void ItemBase::calcRotation(QTransform & rotation, QPointF center, ViewGeometry & vg2) { vg2.setLoc(GraphicsUtils::calcRotation(rotation, center, pos(), boundingRectWithoutLegs().center())); } void ItemBase::updateConnectors() { if (!isEverVisible()) return; QList visited; foreach(ConnectorItem * connectorItem, cachedConnectorItems()) { connectorItem->restoreColor(visited); } //DebugDialog::debug(QString("set up connectors restore:%1").arg(count)); } const QString & ItemBase::moduleID() { if (m_modelPart) return m_modelPart->moduleID(); return ___emptyString___; } bool ItemBase::moveLock() { return m_moveLock; } void ItemBase::setMoveLock(bool moveLock) { m_moveLock = moveLock; if (moveLock) { if (m_moveLockItem == NULL) { if (!MoveLockRenderer.isValid()) { QString fn(":resources/images/part_lock.svg"); bool success = MoveLockRenderer.load(fn); DebugDialog::debug(QString("movelock load success %1").arg(success)); } m_moveLockItem = new QGraphicsSvgItem(); m_moveLockItem->setAcceptHoverEvents(false); m_moveLockItem->setAcceptedMouseButtons(Qt::NoButton); m_moveLockItem->setSharedRenderer(&MoveLockRenderer); m_moveLockItem->setPos(0,0); m_moveLockItem->setZValue(-99999); m_moveLockItem->setParentItem(this); m_moveLockItem->setVisible(true); } } else { if (m_moveLockItem) { delete m_moveLockItem; m_moveLockItem = NULL; } } if (m_stickyItem) { m_stickyItem->setPos(m_moveLockItem == NULL ? 0 : m_moveLockItem->boundingRect().width() + 1, 0); } update(); } void ItemBase::debugInfo(const QString & msg) const { #ifndef QT_NO_DEBUG debugInfo2(msg); #else Q_UNUSED(msg); #endif } void ItemBase::debugInfo2(const QString & msg) const { DebugDialog::debug(QString("%1 ti:'%2' id:%3 it:'%4' vid:%9 vlid:%5 spec:%6 x:%11 y:%12 z:%10 flg:%7 gi:%8") .arg(msg) .arg(this->constTitle()) .arg(this->id()) .arg(this->instanceTitle()) .arg(this->viewLayerID()) .arg(this->viewLayerPlacement()) .arg(this->wireFlags()) .arg((long) dynamic_cast(this), 0, 16) .arg(m_viewID) .arg(this->zValue()) .arg(this->pos().x()) .arg(this->pos().y()) ); /* foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem) connectorItem->debugInfo("\t"); } */ } void ItemBase::addedToScene(bool temporary) { if (this->scene() && instanceTitle().isEmpty() && !temporary) { setTooltip(); if (isBaseSticky() && isLocalSticky()) { // ensure icon is visible setLocalSticky(true); } } } bool ItemBase::hasPartNumberProperty() { return true; } void ItemBase::collectPropsMap(QString & family, QMap & propsMap) { QHash properties; properties = m_modelPart->properties(); family = properties.value("family", ""); foreach (QString key, properties.keys()) { if (key.compare("family") == 0) continue; if (key.compare("id") == 0) continue; QString value = properties.value(key,""); QString tempValue = value; QStringList values = collectValues(family, key, tempValue); propsMap.insert(key, tempValue); DebugDialog::debug(QString("props map %1 %2").arg(key).arg(tempValue)); } } void ItemBase::setDropOffset(QPointF) { } bool ItemBase::hasRubberBandLeg() const { return m_hasRubberBandLeg; } bool ItemBase::sceneEvent(QEvent *event) { return QGraphicsSvgItem::sceneEvent(event); } const QList & ItemBase::cachedConnectorItems() { if (m_cachedConnectorItems.isEmpty()) { foreach (QGraphicsItem * childItem, childItems()) { ConnectorItem * connectorItem = dynamic_cast(childItem); if (connectorItem != NULL) m_cachedConnectorItems.append(connectorItem); } } return m_cachedConnectorItems; } const QList & ItemBase::cachedConnectorItemsConst() const { return m_cachedConnectorItems; } void ItemBase::clearConnectorItemCache() { m_cachedConnectorItems.clear(); } void ItemBase::killRubberBandLeg() { if (!hasRubberBandLeg()) return; prepareGeometryChange(); foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { connectorItem->killRubberBandLeg(); } } ViewGeometry::WireFlags ItemBase::wireFlags() const { return m_viewGeometry.wireFlags(); } QRectF ItemBase::boundingRectWithoutLegs() const { return boundingRect(); } QRectF ItemBase::boundingRect() const { FSvgRenderer * frenderer = fsvgRenderer(); if (frenderer == NULL) { return QGraphicsSvgItem::boundingRect(); } QSizeF s = frenderer->defaultSizeF(); QRectF r(0,0, s.width(), s.height()); return r; } QPainterPath ItemBase::hoverShape() const { return shape(); } const QCursor * ItemBase::getCursor(Qt::KeyboardModifiers) { return CursorMaster::MoveCursor; } PartLabel * ItemBase::partLabel() { return m_partLabel; } void ItemBase::doneLoading() { } QString ItemBase::family() { return modelPart()->family(); } QPixmap * ItemBase::getPixmap(QSize size) { return FSvgRenderer::getPixmap(renderer(), size); } FSvgRenderer * ItemBase::fsvgRenderer() const { if (m_fsvgRenderer) return m_fsvgRenderer; FSvgRenderer * f = qobject_cast(renderer()); if (f == NULL) { DebugDialog::debug("shouldn't happen: missing fsvgRenderer"); } return f; } void ItemBase::setSharedRendererEx(FSvgRenderer * newRenderer) { if (newRenderer != m_fsvgRenderer) { setSharedRenderer(newRenderer); // original renderer is deleted if it is not shared if (m_fsvgRenderer) delete m_fsvgRenderer; m_fsvgRenderer = newRenderer; } else { update(); } m_size = newRenderer->defaultSizeF(); //debugInfo(QString("set size %1, %2").arg(m_size.width()).arg(m_size.height())); } bool ItemBase::reloadRenderer(const QString & svg, bool fastLoad) { if (!svg.isEmpty()) { //DebugDialog::debug(svg); prepareGeometryChange(); bool result = fastLoad ? fsvgRenderer()->fastLoad(svg.toUtf8()) : fsvgRenderer()->loadSvgString(svg.toUtf8()); if (result) { update(); } return result; } return false; } bool ItemBase::resetRenderer(const QString & svg) { // use resetRenderer instead of reloadRender because if the svg size changes, with reloadRenderer the new image seems to be scaled to the old bounds // what I don't understand is why the old renderer causes a crash if it is deleted here QString nothing; return resetRenderer(svg, nothing); } bool ItemBase::resetRenderer(const QString & svg, QString & newSvg) { // use resetRenderer instead of reloadRender because if the svg size changes, with reloadRenderer the new image seems to be scaled to the old bounds // what I don't understand is why the old renderer causes a crash if it is deleted here FSvgRenderer * newRenderer = new FSvgRenderer(); bool result = newRenderer->loadSvgString(svg, newSvg); if (result) { //DebugDialog::debug("reloaded"); //DebugDialog::debug(newSvg); setSharedRendererEx(newRenderer); } else { delete newRenderer; } return result; } void ItemBase::getPixmaps(QPixmap * & pixmap1, QPixmap * & pixmap2, QPixmap * & pixmap3, bool swappingEnabled, QSize size) { pixmap1 = getPixmap(ViewLayer::BreadboardView, swappingEnabled, size); pixmap2 = getPixmap(ViewLayer::SchematicView, swappingEnabled, size); pixmap3 = getPixmap(ViewLayer::PCBView, swappingEnabled, size); } QPixmap * ItemBase::getPixmap(ViewLayer::ViewID vid, bool swappingEnabled, QSize size) { ItemBase * vItemBase = NULL; if (viewID() == vid) { if (!isEverVisible()) return NULL; } else { vItemBase = modelPart()->viewItem(vid); if (vItemBase && !vItemBase->isEverVisible()) return NULL; } vid = useViewIDForPixmap(vid, swappingEnabled); if (vid == ViewLayer::UnknownView) return NULL; if (viewID() == vid) { return getPixmap(size); } if (vItemBase) { return vItemBase->getPixmap(size); } if (!modelPart()->hasViewFor(vid)) return NULL; QString baseName = modelPart()->hasBaseNameFor(vid); if (baseName.isEmpty()) return NULL; QString filename = PartFactory::getSvgFilename(modelPart(), baseName, true, true); if (filename.isEmpty()) { return NULL; } QSvgRenderer renderer(filename); QPixmap * pixmap = new QPixmap(size); pixmap->fill(Qt::transparent); QPainter painter(pixmap); // preserve aspect ratio QSize def = renderer.defaultSize(); double newW = size.width(); double newH = newW * def.height() / def.width(); if (newH > size.height()) { newH = size.height(); newW = newH * def.width() / def.height(); } QRectF bounds((size.width() - newW) / 2.0, (size.height() - newH) / 2.0, newW, newH); renderer.render(&painter, bounds); painter.end(); return pixmap; } ViewLayer::ViewID ItemBase::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::BreadboardView) { return ViewLayer::IconView; } return vid; } bool ItemBase::makeLocalModifications(QByteArray &, const QString & ) { // a bottleneck for modifying part svg xml at setupImage time return false; } void ItemBase::showConnectors(const QStringList & connectorIDs) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorIDs.contains(connectorItem->connectorSharedID())) { connectorItem->setVisible(true); } } } void ItemBase::setItemIsSelectable(bool selectable) { setFlag(QGraphicsItem::ItemIsSelectable, selectable); } bool ItemBase::inRotation() { return m_inRotation; } void ItemBase::setInRotation(bool val) { m_inRotation = val; } void ItemBase::addSubpart(ItemBase * sub) { this->debugInfo("super"); sub->debugInfo("\t"); m_subparts.append(sub); sub->setSuperpart(this); foreach (ConnectorItem * connectorItem, sub->cachedConnectorItems()) { Bus * subbus = connectorItem->bus(); Connector * subconnector = NULL; if (subbus == NULL) { subconnector = connectorItem->connector(); if (subconnector) { subbus = new Bus(NULL, NULL); subconnector->setBus(subbus); } } Connector * connector = modelPart()->getConnector(connectorItem->connectorSharedID()); if (connector) { if (subbus) subbus->addSubConnector(connector); if (subconnector) { Bus * bus = connector->bus(); if (bus == NULL) { bus = new Bus(NULL, NULL); connector->setBus(bus); } bus->addSubConnector(subconnector); } } } } void ItemBase::setSuperpart(ItemBase * super) { m_superpart = super; } ItemBase * ItemBase::superpart() { return m_superpart; } ItemBase * ItemBase::findSubpart(const QString & connectorID, ViewLayer::ViewLayerPlacement spec) { foreach (ItemBase * itemBase, m_subparts) { ConnectorItem * connectorItem = itemBase->findConnectorItemWithSharedID(connectorID, spec); if (connectorItem) return itemBase; } return NULL; } const QList< QPointer > & ItemBase::subparts() { return m_subparts; } QHash ItemBase::prepareProps(ModelPart * modelPart, bool wantDebug, QStringList & keys) { m_propsMap.clear(); // TODO: someday get local props QHash props = modelPart->properties(); QString family = props.value("family", "").toLower(); // ensure family is first; keys = props.keys(); keys.removeOne("family"); keys.push_front("family"); // ensure part number is last QString partNumber = props.value(ModelPartShared::PartNumberPropertyName, "").toLower(); keys.removeOne(ModelPartShared::PartNumberPropertyName); if (wantDebug) { props.insert("id", QString("%1 %2 %3") .arg(QString::number(id())) .arg(modelPart->moduleID()) .arg(ViewLayer::viewLayerNameFromID(viewLayerID())) ); keys.insert(1, "id"); int insertAt = 2; PaletteItemBase * paletteItemBase = qobject_cast(this); if (paletteItemBase != NULL) { props.insert("svg", paletteItemBase->filename()); keys.insert(insertAt++, "svg"); } props.insert("class", this->metaObject()->className()); keys.insert(insertAt++, "class"); if (modelPart->modelPartShared()) { props.insert("fzp", modelPart->path()); keys.insert(insertAt++, "fzp"); } } // ensure part number is last if (hasPartNumberProperty()) { keys.append(ModelPartShared::PartNumberPropertyName); } return props; } void ItemBase::setSquashShape(bool squashShape) { m_squashShape = squashShape; } void ItemBase::createShape(LayerAttributes & layerAttributes) { switch (layerAttributes.viewID) { case ViewLayer::SchematicView: case ViewLayer::PCBView: break; default: return; } if (!isEverVisible()) return; QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(layerAttributes.loaded(), &errorStr, &errorLine, &errorColumn)) { return; } QDomElement root = doc.documentElement(); QRectF viewBox; double w, h; TextUtils::ensureViewBox(doc, 1, viewBox, true, w, h, true); double svgDPI = viewBox.width() / w; int selectionExtra = layerAttributes.viewID == ViewLayer::SchematicView ? 20 : 10; SvgFileSplitter::forceStrokeWidth(root, svgDPI * selectionExtra / GraphicsUtils::SVGDPI, "#000000", true, false); double imageDPI = GraphicsUtils::SVGDPI; QRectF sourceRes(0, 0, w * imageDPI, h * imageDPI); QSize imgSize(qCeil(sourceRes.width()), qCeil(sourceRes.height())); QImage image(imgSize, QImage::Format_Mono); image.fill(0xffffffff); renderOne(&doc, &image, sourceRes); QBitmap bitmap = QBitmap::fromImage(image); QRegion region(bitmap); m_selectionShape.addRegion(region); #ifndef QT_NODEBUG //QFileInfo info(layerAttributes.filename()); //bitmap.save(FolderUtils::getUserDataStorePath("") + "/bitmap." + info.completeBaseName() + "." + QString::number(layerAttributes.viewLayerID) + ".png"); //image.save(FolderUtils::getUserDataStorePath("") + "/image." + info.completeBaseName() + "." + QString::number(layerAttributes.viewLayerID) + ".png"); #endif } const QPainterPath & ItemBase::selectionShape() { return m_selectionShape; } void ItemBase::setTransform2(const QTransform & transform) { setTransform(transform); } void ItemBase::renderOne(QDomDocument * masterDoc, QImage * image, const QRectF & renderRect) { QByteArray byteArray = masterDoc->toByteArray(); QSvgRenderer renderer(byteArray); QPainter painter; painter.begin(image); painter.setRenderHint(QPainter::Antialiasing, false); painter.setRenderHint(QPainter::SmoothPixmapTransform, false); renderer.render(&painter, renderRect); painter.end(); } void ItemBase::initLayerAttributes(LayerAttributes & layerAttributes, ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool doConnectors, bool doCreateShape) { layerAttributes.viewID = viewID; layerAttributes.viewLayerID = viewLayerID; layerAttributes.viewLayerPlacement = viewLayerPlacement; layerAttributes.doConnectors = doConnectors; layerAttributes.createShape = doCreateShape; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { layerAttributes.orientation = infoGraphicsView->smdOrientation(); } } void ItemBase::showInFolder() { QString path = sender()->property("path").toString(); if (!path.isEmpty()) { FolderUtils::showInFolder(path); QClipboard *clipboard = QApplication::clipboard(); if (clipboard != NULL) { clipboard->setText(path); } } } QString ItemBase::getInspectorTitle() { QString t = instanceTitle(); if (!t.isEmpty()) return t; return title(); } void ItemBase::setInspectorTitle(const QString & oldText, const QString & newText) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; DebugDialog::debug(QString("set instance title to %1").arg(newText)); infoGraphicsView->setInstanceTitle(id(), oldText, newText, true, false); } fritzing-app-0.9.3b/src/items/itembase.h000066400000000000000000000416411272363601300201620ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Licensetriple along with Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef ITEMBASE_H #define ITEMBASE_H #include #include #include #include #include #include #include #include #include #include #include #include #include "../viewgeometry.h" #include "../viewlayer.h" #include "../utils/misc.h" class ConnectorItem; typedef QMultiHash ConnectorPairHash; typedef bool (*SkipCheckFunction)(ConnectorItem *); class ItemBase : public QGraphicsSvgItem { Q_OBJECT public: enum PluralType { Singular, Plural, NotSure }; public: ItemBase(class ModelPart*, ViewLayer::ViewID, const ViewGeometry &, long id, QMenu * itemMenu); virtual ~ItemBase(); qint64 id() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); double z(); virtual void saveGeometry() = 0; ViewGeometry & getViewGeometry(); ViewGeometry::WireFlags wireFlags() const; virtual bool itemMoved() = 0; QSizeF size(); class ModelPart * modelPart(); void setModelPart(class ModelPart *); class ModelPartShared * modelPartShared(); virtual void writeXml(QXmlStreamWriter &) {} virtual void saveInstance(QXmlStreamWriter &); virtual void saveInstanceLocation(QXmlStreamWriter &) = 0; virtual void writeGeometry(QXmlStreamWriter &); virtual void moveItem(ViewGeometry &) = 0; virtual void setItemPos(QPointF & pos); virtual void rotateItem(double degrees, bool includeRatsnest); virtual void flipItem(Qt::Orientations orientation); virtual void transformItem(const QTransform &, bool includeRatsnest); virtual void transformItem2(const QMatrix &); virtual void removeLayerKin(); ViewLayer::ViewID viewID(); QString & viewIDName(); ViewLayer::ViewLayerID viewLayerID() const; void setViewLayerID(ViewLayer::ViewLayerID, const LayerHash & viewLayers); void setViewLayerID(const QString & layerName, const LayerHash & viewLayers); bool topLevel(); void collectConnectors(ConnectorPairHash & connectorHash, SkipCheckFunction); virtual void busConnectorItems(class Bus * bus, ConnectorItem *, QList & items); virtual void setHidden(bool hidden); virtual void setLayerHidden(bool hidden); bool hidden(); bool layerHidden(); virtual void setInactive(bool inactivate); bool inactive(); ConnectorItem * findConnectorItemWithSharedID(const QString & connectorID, ViewLayer::ViewLayerPlacement); ConnectorItem * findConnectorItemWithSharedID(const QString & connectorID); void updateConnections(ConnectorItem *, bool includeRatsnest, QList & already); virtual void updateConnections(bool includeRatsnest, QList & already); virtual const QString & title(); const QString & constTitle() const; bool getRatsnest(); QList buses(); int itemType() const; // wanted this to return ModelPart::ItemType but couldn't figure out how to get it to compile virtual bool isSticky(); virtual bool isBaseSticky(); virtual void setSticky(bool); void setLocalSticky(bool); bool isLocalSticky(); virtual void addSticky(ItemBase *, bool stickem); virtual ItemBase * stickingTo(); virtual QList< QPointer > stickyList(); virtual bool alreadySticking(ItemBase * itemBase); virtual bool stickyEnabled(); ConnectorItem * anyConnectorItem(); const QString & instanceTitle() const; QString label(); virtual void updateTooltip(); void setTooltip(); void removeTooltip(); bool hasConnectors(); bool hasNonConnectors(); bool hasConnections(); bool canFlip(Qt::Orientations); bool canFlipHorizontal(); void setCanFlipHorizontal(bool); bool canFlipVertical(); void setCanFlipVertical(bool); virtual void clearModelPart(); virtual bool hasPartLabel(); ViewLayer::ViewLayerID partLabelViewLayerID(); void clearPartLabel(); bool isPartLabelVisible(); void restorePartLabel(QDomElement & labelGeometry, ViewLayer::ViewLayerID); // on loading from a file void movePartLabel(QPointF newPos, QPointF newOffset); // coming down from the command object void partLabelMoved(QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset); // coming up from the label void partLabelSetHidden(bool hide); void rotateFlipPartLabel(double degrees, Qt::Orientations); // coming up from the label void doRotateFlipPartLabel(double degrees, Qt::Orientations); // coming down from the command object QString makePartLabelSvg(bool blackOnly, double dpi, double printerScale); QPointF partLabelScenePos(); QRectF partLabelSceneBoundingRect(); virtual bool isSwappable(); virtual void setSwappable(bool); void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void collectWireConnectees(QSet & wires); virtual bool collectFemaleConnectees(QSet & items); void prepareGeometryChange(); virtual void resetID(); void updateConnectionsAux(bool includeRatsnest, QList & already); void hoverEnterEvent( QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent( QGraphicsSceneHoverEvent * event ); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); virtual void figureHover(); virtual QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); virtual void slamZ(double newZ); bool isEverVisible(); void setEverVisible(bool); virtual bool connectionIsAllowed(ConnectorItem *); virtual bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); virtual QString getProperty(const QString & key); ConnectorItem * rightClickedConnector(); virtual bool canEditPart(); virtual bool hasCustomSVG(); virtual void setProp(const QString & prop, const QString & value); QString prop(const QString & p); bool isObsolete(); virtual QHash prepareProps(ModelPart *, bool wantDebug, QStringList & keys); void resetValues(const QString & family, const QString & prop); const QString & filename(); void setFilename(const QString &); virtual PluralType isPlural(); ViewLayer::ViewLayerPlacement viewLayerPlacement() const; void setViewLayerPlacement(ViewLayer::ViewLayerPlacement); virtual void calcRotation(QTransform & rotation, QPointF center, ViewGeometry &); void updateConnectors(); const QString & moduleID(); bool moveLock(); virtual void setMoveLock(bool); void debugInfo(const QString & msg) const; void debugInfo2(const QString & msg) const; virtual void addedToScene(bool temporary); virtual bool hasPartNumberProperty(); void collectPropsMap(QString & family, QMap &); virtual bool rotationAllowed(); virtual bool rotation45Allowed(); virtual bool freeRotationAllowed(); void ensureUniqueTitle(const QString &title, bool force); virtual void setDropOffset(QPointF offset); bool hasRubberBandLeg() const; void killRubberBandLeg(); bool sceneEvent(QEvent *event); void clearConnectorItemCache(); const QList & cachedConnectorItems(); const QList & cachedConnectorItemsConst() const; bool inHover(); virtual QRectF boundingRectWithoutLegs() const; QRectF boundingRect() const; virtual QPainterPath hoverShape() const; virtual const QCursor * getCursor(Qt::KeyboardModifiers); class PartLabel * partLabel(); virtual void doneLoading(); QString family(); QPixmap * getPixmap(QSize size); class FSvgRenderer * fsvgRenderer() const; void setSharedRendererEx(class FSvgRenderer *); bool reloadRenderer(const QString & svg, bool fastload); bool resetRenderer(const QString & svg); bool resetRenderer(const QString & svg, QString & newSvg); void getPixmaps(QPixmap * &, QPixmap * &, QPixmap * &, bool swappingEnabled, QSize); class FSvgRenderer * setUpImage(class ModelPart * modelPart, class LayerAttributes &); void showConnectors(const QStringList &); void setItemIsSelectable(bool selectable); virtual bool inRotation(); virtual void setInRotation(bool); const QString & spice() const; const QString & spiceModel() const; void addSubpart(ItemBase *); void setSuperpart(ItemBase *); ItemBase * superpart(); ItemBase * findSubpart(const QString & connectorID, ViewLayer::ViewLayerPlacement); const QList< QPointer > & subparts(); void setSquashShape(bool); const QPainterPath & selectionShape(); virtual void setTransform2(const QTransform &); void initLayerAttributes(LayerAttributes & layerAttributes, ViewLayer::ViewID, ViewLayer::ViewLayerID, ViewLayer::ViewLayerPlacement, bool doConnectors, bool doCreateShape); virtual QString getInspectorTitle(); virtual void setInspectorTitle(const QString & oldText, const QString & newText); public: virtual void getConnectedColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); virtual void getUnconnectedColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); virtual void getNormalColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); virtual void getHoverColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); virtual void getEqualPotentialColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); protected: static QPen NormalPen; static QPen HoverPen; static QPen ConnectedPen; static QPen UnconnectedPen; static QPen ChosenPen; static QPen EqualPotentialPen; static QBrush HoverBrush; static QBrush NormalBrush; static QBrush ConnectedBrush; static QBrush UnconnectedBrush; static QBrush ChosenBrush; static QBrush EqualPotentialBrush; static const double NormalConnectorOpacity; public: static QColor connectedColor(); static QColor unconnectedColor(); static QColor standardConnectedColor(); static QColor standardUnconnectedColor(); static void setConnectedColor(QColor &); static void setUnconnectedColor(QColor &); public: virtual void hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item); virtual void hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item); virtual void hoverMoveConnectorItem(QGraphicsSceneHoverEvent * event, class ConnectorItem * item); void hoverEnterConnectorItem(); void hoverLeaveConnectorItem(); virtual void connectorHover(ConnectorItem *, ItemBase *, bool hovering); virtual void clearConnectorHover(); virtual void hoverUpdate(); virtual bool filterMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual void mousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual void mouseDoubleClickConnectorEvent(ConnectorItem *); virtual void mouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual void mouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual bool acceptsMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual bool acceptsMousePressLegEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual void setAcceptsMousePressLegEvent(bool); virtual bool acceptsMouseDoubleClickConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual bool acceptsMouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual bool acceptsMouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual void connectionChange(ConnectorItem * onMe, ConnectorItem * onIt, bool connect); virtual void connectedMoved(ConnectorItem * from, ConnectorItem * to, QList & already); virtual ItemBase * layerKinChief(); virtual const QList & layerKin(); virtual void findConnectorsUnder() = 0; virtual ConnectorItem* newConnectorItem(class Connector *connector); virtual ConnectorItem* newConnectorItem(ItemBase * layerkin, Connector *connector); virtual void setInstanceTitle(const QString &title, bool initial); void updatePartLabelInstanceTitle(); public slots: void showPartLabel(bool show, ViewLayer *); void hidePartLabel(); void partLabelChanged(const QString &newText); virtual void swapEntry(const QString & text); void showInFolder(); public: static bool zLessThan(ItemBase * & p1, ItemBase * & p2); static qint64 getNextID(); static qint64 getNextID(qint64 fromIndex); protected: void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event ); virtual void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, const QPainterPath & shape); virtual void paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual void paintBody(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value); virtual QStringList collectValues(const QString & family, const QString & prop, QString & value); void setInstanceTitleTooltip(const QString& text); virtual void setDefaultTooltip(); void setInstanceTitleAux(const QString & title, bool initial); void saveLocAndTransform(QXmlStreamWriter & streamWriter); QPixmap * getPixmap(ViewLayer::ViewID, bool swappingEnabled, QSize size); virtual ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); virtual bool makeLocalModifications(QByteArray & svg, const QString & filename); void updateHidden(); void createShape(LayerAttributes & layerAttributes); protected: static bool getFlipDoc(ModelPart * modelPart, const QString & filename, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement, QDomDocument &, Qt::Orientations); static bool fixCopper1(ModelPart * modelPart, const QString & filename, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement, QDomDocument &); protected: QSizeF m_size; qint64 m_id; ViewGeometry m_viewGeometry; QPointer m_modelPart; ViewLayer::ViewID m_viewID; ViewLayer::ViewLayerID m_viewLayerID; int m_connectorHoverCount; int m_connectorHoverCount2; int m_hoverCount; bool m_hidden; bool m_layerHidden; bool m_inactive; bool m_sticky; QHash< long, QPointer > m_stickyList; QMenu *m_itemMenu; bool m_canFlipHorizontal; bool m_canFlipVertical; bool m_zUninitialized; QPointer m_partLabel; bool m_spaceBarWasPressed; bool m_hoverEnterSpaceBarWasPressed; bool m_everVisible; ConnectorItem * m_rightClickedConnector; QMap m_propsMap; QString m_filename; ViewLayer::ViewLayerPlacement m_viewLayerPlacement; bool m_moveLock; bool m_hasRubberBandLeg; QList m_cachedConnectorItems; QGraphicsSvgItem * m_moveLockItem; QGraphicsSvgItem * m_stickyItem; FSvgRenderer * m_fsvgRenderer; bool m_acceptsMousePressLegEvent; bool m_swappable; bool m_inRotation; QPointer m_superpart; QList< QPointer > m_subparts; bool m_squashShape; QPainterPath m_selectionShape; protected: static long nextID; static QPointer TheReferenceModel; public: static const QString ITEMBASE_FONT_PREFIX; static const QString ITEMBASE_FONT_SUFFIX; static QHash TranslatedPropertyNames; static QString PartInstanceDefaultTitle; static const QList EmptyList; const static QColor HoverColor; const static double HoverOpacity; const static QColor ConnectorHoverColor; const static double ConnectorHoverOpacity; public: static void initNames(); static void cleanup(); static ItemBase * extractTopLevelItemBase(QGraphicsItem * thing); static QString translatePropertyName(const QString & key); static void setReferenceModel(class ReferenceModel *); static void renderOne(QDomDocument *, QImage *, const QRectF & renderRect); }; Q_DECLARE_METATYPE( ItemBase* ); // so we can stash them in a QVariant #endif fritzing-app-0.9.3b/src/items/jumperitem.cpp000066400000000000000000000415531272363601300211070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include "jumperitem.h" #include "../connectors/connectoritem.h" #include "../fsvgrenderer.h" #include "../model/modelpart.h" #include "../utils/graphicsutils.h" #include "../svg/svgfilesplitter.h" #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "../layerattributes.h" static QString Copper0LayerTemplate = ""; static QString JumperWireLayerTemplate = ""; static const QString ShadowColor ="#1b5bb3"; static const QString JumperColor = "#418dd9"; static QHash Colors; QString makeWireImage(double w, double h, double r0x, double r0y, double r1x, double r1y, const QString & layerName, const QString & color, double thickness) { return JumperWireLayerTemplate .arg(w).arg(h) .arg(w * 1000).arg(h * 1000) .arg(r0x).arg(r0y).arg(r1x).arg(r1y) .arg(layerName) .arg(color) .arg(thickness); } void shorten(QRectF r0, QPointF r0c, QPointF r1c, double & r0x, double & r0y, double & r1x, double & r1y) { double radius = r0.width() / 2.0; GraphicsUtils::shortenLine(r0c, r1c, radius, radius); r0x = GraphicsUtils::pixels2mils(r0c.x(), GraphicsUtils::SVGDPI); r0y = GraphicsUtils::pixels2mils(r0c.y(), GraphicsUtils::SVGDPI); r1x = GraphicsUtils::pixels2mils(r1c.x(), GraphicsUtils::SVGDPI); r1y = GraphicsUtils::pixels2mils(r1c.y(), GraphicsUtils::SVGDPI); } // TODO: // ignore during autoroute? // ignore during other connections? // don't let footprints overlap during dragging ///////////////////////////////////////////////////////// JumperItem::JumperItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_originalClickItem = NULL; if (Colors.isEmpty()) { Colors.insert(ViewLayer::Copper0, ViewLayer::Copper0Color); Colors.insert(ViewLayer::Copper1, ViewLayer::Copper1Color); Colors.insert(ViewLayer::PartImage, JumperColor); Colors.insert(ViewLayer::Silkscreen1, ViewLayer::Silkscreen1Color); } m_otherItem = m_connector0 = m_connector1 = m_dragItem = NULL; if (Copper0LayerTemplate.isEmpty()) { QFile file(":/resources/templates/jumper_copper0LayerTemplate.txt"); if (file.open(QFile::ReadOnly)) { Copper0LayerTemplate = file.readAll(); file.close(); } } if (JumperWireLayerTemplate.isEmpty()) { QFile file(":/resources/templates/jumper_jumperwiresLayerTemplate.txt"); if (file.open(QFile::ReadOnly)) { JumperWireLayerTemplate = file.readAll(); file.close(); } } } JumperItem::~JumperItem() { } QRectF JumperItem::boundingRect() const { if (m_viewID != ViewLayer::PCBView) { return PaletteItem::boundingRect(); } return shape().controlPointRect(); } void JumperItem::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_viewID != ViewLayer::PCBView) { PaletteItem::paintHover(painter, option, widget); return; } ItemBase::paintHover(painter, option, widget, hoverShape()); } void JumperItem::paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_viewID != ViewLayer::PCBView) { PaletteItem::paintSelected(painter, option, widget); return; } GraphicsUtils::qt_graphicsItem_highlightSelected(painter, option, boundingRect(), hoverShape()); } QPainterPath JumperItem::hoverShape() const { if (m_viewID != ViewLayer::PCBView) { return PaletteItem::hoverShape(); } QPainterPath path; QPointF c0 = m_connector0->rect().center(); QPointF c1 = m_connector1->rect().center(); path.moveTo(c0); path.lineTo(c1); QRectF rect = m_connector0->rect(); double dx = m_connectorTL.x(); double dy = m_connectorTL.y(); rect.adjust(-dx, -dy, dx, dy); QPen pen; pen.setCapStyle(Qt::RoundCap); return GraphicsUtils::shapeFromPath(path, pen, rect.width(), false); } QPainterPath JumperItem::shape() const { return hoverShape(); } bool JumperItem::setUpImage(ModelPart * modelPart, const LayerHash & viewLayers, LayerAttributes & layerAttributes) { bool result = PaletteItem::setUpImage(modelPart, viewLayers, layerAttributes); if (layerAttributes.doConnectors) { foreach (ConnectorItem * item, cachedConnectorItems()) { item->setCircular(true); if (item->connectorSharedName().contains('0')) { m_connector0 = item; } else if (item->connectorSharedName().contains('1')) { m_connector1 = item; } } m_connectorTL = m_connector0->rect().topLeft(); m_connectorBR = boundingRect().bottomRight() - m_connector1->rect().bottomRight(); initialResize(layerAttributes.viewID); } return result; } void JumperItem::initialResize(ViewLayer::ViewID viewID) { if (viewID != ViewLayer::PCBView) return; bool ok; double r0x = m_modelPart->localProp("r0x").toDouble(&ok); if (!ok) return; double r0y = m_modelPart->localProp("r0y").toDouble(&ok); if (!ok) return; double r1x = m_modelPart->localProp("r1x").toDouble(&ok); if (!ok) return; double r1y = m_modelPart->localProp("r1y").toDouble(&ok); if (!ok) return; resizeAux(GraphicsUtils::mils2pixels(r0x, GraphicsUtils::SVGDPI), GraphicsUtils::mils2pixels(r0y, GraphicsUtils::SVGDPI), GraphicsUtils::mils2pixels(r1x, GraphicsUtils::SVGDPI), GraphicsUtils::mils2pixels(r1y, GraphicsUtils::SVGDPI)); } bool JumperItem::mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event) { m_originalClickItem = originalItem; mousePressEvent(event); return (m_dragItem != NULL); } void JumperItem::mouseMoveEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event) { Q_UNUSED(originalItem); mouseMoveEvent(event); } void JumperItem::mouseReleaseEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event) { Q_UNUSED(originalItem); mouseReleaseEvent(event); } void JumperItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { PaletteItemBase * originalItem = m_originalClickItem; m_originalClickItem = NULL; InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL && infographics->spaceBarIsPressed()) { event->ignore(); return; } m_dragItem = NULL; QRectF rect = m_connector0->rect(); double dx = m_connectorTL.x(); double dy = m_connectorTL.y(); rect.adjust(-dx, -dy, dx, dy); if (rect.contains(event->pos())) { m_dragItem = m_connector0; m_otherItem = m_connector1; } else { rect = m_connector1->rect(); dx = m_connectorBR.x(); dy = m_connectorBR.y(); rect.adjust(-dx, -dy, dx, dy); if (rect.contains(event->pos())) { m_dragItem = m_connector1; m_otherItem = m_connector0; } else { if (originalItem) return ItemBase::mousePressEvent(event); return PaletteItem::mousePressEvent(event); } } m_dragStartScenePos = event->scenePos(); m_dragStartThisPos = this->pos(); m_dragStartConnectorPos = this->mapToScene(m_dragItem->rect().topLeft()); m_dragStartCenterPos = this->mapToScene(m_dragItem->rect().center()); m_otherPos = this->mapToScene(m_otherItem->rect().topLeft()); } void JumperItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (m_dragItem == NULL) return; // TODO: make sure the two connectors don't overlap QPointF d = event->scenePos() - m_dragStartScenePos; QPointF p = m_dragStartConnectorPos + d; emit alignMe(this, p); QPointF myPos(qMin(p.x(), m_otherPos.x()) - m_connectorTL.x(), qMin(p.y(), m_otherPos.y()) - m_connectorTL.y()); this->setPos(myPos); QRectF r = m_otherItem->rect(); r.moveTo(mapFromScene(m_otherPos)); m_otherItem->setRect(r); ConnectorItem * cross = m_otherItem->getCrossLayerConnectorItem(); if (cross) cross->setRect(r); r = m_dragItem->rect(); r.moveTo(mapFromScene(p)); m_dragItem->setRect(r); cross = m_dragItem->getCrossLayerConnectorItem(); if (cross) cross->setRect(r); resize(); QList already; ItemBase::updateConnections(m_dragItem, true, already); } void JumperItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { m_dragItem = NULL; PaletteItem::mouseReleaseEvent(event); } QString JumperItem::makeSvg(ViewLayer::ViewLayerID viewLayerID) { QRectF r0 = m_connector0->rect(); QRectF r1 = m_connector1->rect(); QRectF r = r0.united(r1); double w = GraphicsUtils::pixels2ins(r.width() + m_connectorTL.x() + m_connectorBR.x(), GraphicsUtils::SVGDPI); double h = GraphicsUtils::pixels2ins(r.height() + m_connectorTL.y() + m_connectorBR.y(), GraphicsUtils::SVGDPI); QPointF r0c = r0.center(); QPointF r1c = r1.center(); double r0x = GraphicsUtils::pixels2mils(r0c.x(), GraphicsUtils::SVGDPI); double r0y = GraphicsUtils::pixels2mils(r0c.y(), GraphicsUtils::SVGDPI); double r1x = GraphicsUtils::pixels2mils(r1c.x(), GraphicsUtils::SVGDPI); double r1y = GraphicsUtils::pixels2mils(r1c.y(), GraphicsUtils::SVGDPI); modelPart()->setLocalProp("r0x", r0x); modelPart()->setLocalProp("r0y", r0y); modelPart()->setLocalProp("r1x", r1x); modelPart()->setLocalProp("r1y", r1y); switch (viewLayerID) { case ViewLayer::Copper0: case ViewLayer::Copper1: return Copper0LayerTemplate .arg(w).arg(h) .arg(w * GraphicsUtils::StandardFritzingDPI).arg(h * GraphicsUtils::StandardFritzingDPI) .arg(r0x).arg(r0y).arg(r1x).arg(r1y) .arg(ViewLayer::viewLayerXmlNameFromID(viewLayerID)) .arg(Colors.value(viewLayerID)); //case ViewLayer::Silkscreen0: case ViewLayer::Silkscreen1: shorten(r0, r0c, r1c, r0x, r0y, r1x, r1y); return makeWireImage(w, h, r0x, r0y, r1x, r1y, ViewLayer::viewLayerXmlNameFromID(viewLayerID), Colors.value(viewLayerID), 21); case ViewLayer::PartImage: { shorten(r0, r0c, r1c, r0x, r0y, r1x, r1y); QString svg = makeWireImage(w, h, r0x, r0y, r1x, r1y, ViewLayer::viewLayerXmlNameFromID(viewLayerID), ShadowColor, 40); QString svg2 = makeWireImage(w, h, r0x, r0y, r1x, r1y, "", Colors.value(viewLayerID), 20); int ix1 = svg.indexOf("", ix1); int ix3 = svg2.indexOf("", ix3); svg.insert(ix2 + 1, svg2.mid(ix3, ix4 - ix3 + 1)); return svg; } default: break; } return ___emptyString___; } void JumperItem::resize(QPointF p0, QPointF p1) { QPointF p = calcPos(p0, p1); resize(p, p0 - p, p1 - p); } void JumperItem::resize() { if (m_viewID != ViewLayer::PCBView) return; if (m_connector0 == NULL) return; if (m_connector1 == NULL) return; prepareGeometryChange(); QString s = makeSvg(ViewLayer::Copper0); //DebugDialog::debug(s); resetRenderer(s); foreach (ItemBase * itemBase, m_layerKin) { switch(itemBase->viewLayerID()) { case ViewLayer::PartImage: case ViewLayer::Copper1: case ViewLayer::Silkscreen1: //case ViewLayer::Silkscreen0: { s = makeSvg(itemBase->viewLayerID()); itemBase->resetRenderer(s); } break; default: break; } } // DebugDialog::debug(QString("fast load result %1 %2").arg(result).arg(s)); } void JumperItem::saveParams() { m_itemPos = pos(); m_itemC0 = m_connector0->rect().center(); m_itemC1 = m_connector1->rect().center(); } void JumperItem::getParams(QPointF & p, QPointF & c0, QPointF & c1) { p = m_itemPos; c0 = m_itemC0; c1 = m_itemC1; } void JumperItem::resize(QPointF p, QPointF nc0, QPointF nc1) { resizeAux(nc0.x(), nc0.y(), nc1.x(), nc1.y()); DebugDialog::debug(QString("jumper item set pos %1 %2, %3").arg(this->id()).arg(p.x()).arg(p.y()) ); setPos(p); } void JumperItem::resizeAux(double r0x, double r0y, double r1x, double r1y) { prepareGeometryChange(); QRectF r0 = m_connector0->rect(); QRectF r1 = m_connector1->rect(); QPointF c0 = r0.center(); QPointF c1 = r1.center(); r0.translate(r0x - c0.x(), r0y - c0.y()); r1.translate(r1x - c1.x(), r1y - c1.y()); m_connector0->setRect(r0); m_connector1->setRect(r1); ConnectorItem * cc0 = m_connector0->getCrossLayerConnectorItem(); if (cc0 != NULL) { cc0->setRect(r0); } ConnectorItem * cc1 = m_connector1->getCrossLayerConnectorItem(); if (cc1 != NULL) { cc1->setRect(r1); } resize(); } QSizeF JumperItem::footprintSize() { QRectF r0 = m_connector0->rect(); return r0.size(); } QString JumperItem::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { QString xml = ""; switch (viewLayerID) { case ViewLayer::Copper0: case ViewLayer::Copper1: case ViewLayer::PartImage: //case ViewLayer::Silkscreen0: case ViewLayer::Silkscreen1: xml = makeSvg(viewLayerID); default: break; } if (!xml.isEmpty()) { return PaletteItemBase::normalizeSvg(xml, viewLayerID, blackOnly, dpi, factor); } return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } void JumperItem::setAutoroutable(bool ar) { m_viewGeometry.setAutoroutable(ar); } bool JumperItem::getAutoroutable() { return m_viewGeometry.getAutoroutable(); } ConnectorItem * JumperItem::connector0() { return m_connector0; } ConnectorItem * JumperItem::connector1() { return m_connector1; } bool JumperItem::hasCustomSVG() { switch (m_viewID) { case ViewLayer::PCBView: return true; default: return ItemBase::hasCustomSVG(); } } bool JumperItem::inDrag() { return m_dragItem != NULL; } void JumperItem::loadLayerKin( const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement viewLayerPlacement) { PaletteItem::loadLayerKin(viewLayers, viewLayerPlacement); resize(); } ItemBase::PluralType JumperItem::isPlural() { return Singular; } void JumperItem::addedToScene(bool temporary) { if (m_connector0 == NULL) return; if (m_connector1 == NULL) return; ConnectorItem * cc0 = m_connector0->getCrossLayerConnectorItem(); if (cc0 != NULL) { cc0->setRect(m_connector0->rect()); } ConnectorItem * cc1 = m_connector1->getCrossLayerConnectorItem(); if (cc1 != NULL) { cc1->setRect(m_connector1->rect()); } PaletteItem::addedToScene(temporary); } void JumperItem::rotateItem(double degrees, bool updateRatsnest) { Q_UNUSED(updateRatsnest); QPointF tc0, tc1; QTransform rotation; rotation.rotate(degrees); rotateEnds(rotation, tc0, tc1); resize(tc0, tc1); } void JumperItem::calcRotation(QTransform & rotation, QPointF center, ViewGeometry & vg2) { QPointF tc0, tc1; rotateEnds(rotation, tc0, tc1); QPointF p = calcPos(tc0, tc1); QPointF myCenter = mapToScene(boundingRect().center()); QTransform transf = QTransform().translate(-center.x(), -center.y()) * rotation * QTransform().translate(center.x(), center.y()); QPointF q = transf.map(myCenter); vg2.setLoc(p + q - myCenter); } void JumperItem::rotateEnds(QTransform & rotation, QPointF & tc0, QPointF & tc1) { ConnectorItem * cc0 = m_connector0; QRectF r0 = cc0->rect(); QPointF c0 = cc0->mapToScene(r0.center()); ConnectorItem * cc1 = m_connector1; QRectF r1 = cc1->rect(); QPointF c1 = cc1->mapToScene(r1.center()); QPointF c((c0.x() + c1.x()) / 2, (c0.y() + c1.y()) / 2); QTransform transf = QTransform().translate(-c.x(), -c.y()) * rotation * QTransform().translate(c.x(), c.y()); tc0 = transf.map(c0); tc1 = transf.map(c1); } QPointF JumperItem::calcPos(QPointF p0, QPointF p1) { QRectF r0 = m_connector0->rect(); QPointF p(qMin(p0.x(), p1.x()) - (r0.width() / 2) - m_connectorTL.x(), qMin(p0.y(), p1.y()) - (r0.height() / 2) - m_connectorTL.y()); return p; } QPointF JumperItem::dragOffset() { return m_dragStartConnectorPos - m_dragStartCenterPos; } void JumperItem::saveInstanceLocation(QXmlStreamWriter & streamWriter) { streamWriter.writeAttribute("x", QString::number(m_viewGeometry.loc().x())); streamWriter.writeAttribute("y", QString::number(m_viewGeometry.loc().y())); streamWriter.writeAttribute("wireFlags", QString::number(m_viewGeometry.flagsAsInt())); GraphicsUtils::saveTransform(streamWriter, m_viewGeometry.transform()); } bool JumperItem::hasPartNumberProperty() { return false; } ViewLayer::ViewID JumperItem::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::PCBView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } fritzing-app-0.9.3b/src/items/jumperitem.h000066400000000000000000000074261272363601300205550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6994 $: $Author: irascibl@gmail.com $: $Date: 2013-04-27 14:25:24 +0200 (Sa, 27. Apr 2013) $ ********************************************************************/ #ifndef JUMPERITEM_H #define JUMPERITEM_H #include "paletteitem.h" class JumperItem : public PaletteItem { Q_OBJECT public: JumperItem( ModelPart * modelPart, ViewLayer::ViewID, const ViewGeometry & , long id, QMenu* itemMenu, bool doLabel); ~JumperItem(); QPainterPath shape() const; QPainterPath hoverShape() const; bool setUpImage(ModelPart* modelPart, const LayerHash & viewLayers, LayerAttributes &); void saveParams(); void getParams(QPointF & pos, QPointF & c0, QPointF & c1); void resize(QPointF pos, QPointF c0, QPointF c1); void resize(QPointF p0, QPointF p1); QSizeF footprintSize(); QString retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool getAutoroutable(); void setAutoroutable(bool); class ConnectorItem * connector0(); class ConnectorItem * connector1(); bool hasCustomSVG(); bool inDrag(); void loadLayerKin( const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement); PluralType isPlural(); void addedToScene(bool temporary); void rotateItem(double degrees, bool includeRatsnest); void calcRotation(QTransform & rotation, QPointF center, ViewGeometry &); QPointF dragOffset(); void saveInstanceLocation(QXmlStreamWriter & streamWriter); bool hasPartNumberProperty(); QRectF boundingRect() const; bool mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *); void mouseMoveEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *); void mouseReleaseEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *); protected: void resize(); QString makeSvg(ViewLayer::ViewLayerID); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void resizeAux(double r0x, double r0y, double r1x, double r1y); void rotateEnds(QTransform & rotation, QPointF & tc0, QPointF & tc1); QPointF calcPos(QPointF p0, QPointF p1); void initialResize(ViewLayer::ViewID); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); signals: void alignMe(JumperItem *, QPointF & p); protected: QPointer m_dragItem; QPointer m_connector0; QPointer m_connector1; QPointer m_otherItem; QPointF m_dragStartScenePos; QPointF m_dragStartThisPos; QPointF m_dragStartConnectorPos; QPointF m_dragStartCenterPos; QPointF m_otherPos; QPointF m_connectorTL; QPointF m_connectorBR; QPointF m_itemPos; QPointF m_itemC0; QPointF m_itemC1; QPointer m_originalClickItem; }; #endif fritzing-app-0.9.3b/src/items/layerkinpaletteitem.cpp000066400000000000000000000366121272363601300230020ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include "layerkinpaletteitem.h" #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "../layerattributes.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/folderutils.h" #include "../svg/svgfilesplitter.h" #include //////////////////////////////////////////////// static QString IDString("-_-_-text-_-_-%1"); //////////////////////////////////////////////// LayerKinPaletteItem::LayerKinPaletteItem(PaletteItemBase * chief, ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu* itemMenu) : PaletteItemBase(modelPart, viewID, viewGeometry, id, itemMenu) { m_passMouseEvents = false; m_layerKinChief = chief; setFlag(QGraphicsItem::ItemIsSelectable, true); m_modelPart->removeViewItem(this); // we don't need to save layerkin } void LayerKinPaletteItem::initLKPI(LayerAttributes & layerAttributes, const LayerHash & viewLayers) { setViewLayerPlacement(layerAttributes.viewLayerPlacement); m_ok = setUpImage(m_modelPart, viewLayers, layerAttributes); //DebugDialog::debug(QString("lk accepts hover %1 %2 %3 %4 %5").arg(title()).arg(m_viewID).arg(m_id).arg(viewLayerID).arg(this->acceptHoverEvents())); } QVariant LayerKinPaletteItem::itemChange(GraphicsItemChange change, const QVariant &value) { //DebugDialog::debug(QString("lk item change %1 %2").arg(this->id()).arg(change)); if (m_layerKinChief != NULL) { if (change == ItemSelectedChange) { bool selected = value.toBool(); if (m_blockItemSelectedChange && m_blockItemSelectedValue == selected) { m_blockItemSelectedChange = false; } else { m_layerKinChief->syncKinSelection(selected, this); } } //else if (change == ItemVisibleHasChanged && value.toBool()) { //this->setSelected(m_layerKinChief->syncSelected()); //this->setPos(m_offset + m_layerKinChief->syncMoved()); //} else if (change == ItemPositionHasChanged) { m_layerKinChief->syncKinMoved(this->m_offset, value.toPointF()); } } return PaletteItemBase::itemChange(change, value); } void LayerKinPaletteItem::setOffset(double x, double y) { m_offset.setX(x); m_offset.setY(y); this->setPos(this->pos() + m_offset); } ItemBase * LayerKinPaletteItem::layerKinChief() { return m_layerKinChief; } bool LayerKinPaletteItem::ok() { return m_ok; } void LayerKinPaletteItem::updateConnections(bool includeRatsnest, QList & already) { if (m_layerKinChief) { m_layerKinChief->updateConnections(includeRatsnest, already); } else { DebugDialog::debug("chief deleted before layerkin"); } } void LayerKinPaletteItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { m_passMouseEvents = m_layerKinChief->mousePressEventK(this, event); return; //ItemBase::mousePressEvent(event); } void LayerKinPaletteItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (m_passMouseEvents) { m_layerKinChief->mouseMoveEventK(this, event); return; } PaletteItemBase::mouseMoveEvent(event); } void LayerKinPaletteItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (m_passMouseEvents) { m_layerKinChief->mouseReleaseEventK(this, event); return; } PaletteItemBase::mouseReleaseEvent(event); } void LayerKinPaletteItem::setHidden(bool hide) { ItemBase::setHidden(hide); m_layerKinChief->figureHover(); } void LayerKinPaletteItem::setInactive(bool inactivate) { ItemBase::setInactive(inactivate); m_layerKinChief->figureHover(); } void LayerKinPaletteItem::clearModelPart() { m_layerKinChief->clearModelPart(); } bool LayerKinPaletteItem::stickyEnabled() { return m_layerKinChief->stickyEnabled(); } bool LayerKinPaletteItem::isSticky() { return m_layerKinChief->isSticky(); } bool LayerKinPaletteItem::isBaseSticky() { return m_layerKinChief->isBaseSticky(); } void LayerKinPaletteItem::setSticky(bool s) { m_layerKinChief->setSticky(s); } void LayerKinPaletteItem::addSticky(ItemBase * sticky, bool stickem) { m_layerKinChief->addSticky(sticky, stickem); } ItemBase * LayerKinPaletteItem::stickingTo() { return m_layerKinChief->stickingTo(); } QList< QPointer > LayerKinPaletteItem::stickyList() { return m_layerKinChief->stickyList(); } bool LayerKinPaletteItem::alreadySticking(ItemBase * itemBase) { return m_layerKinChief->alreadySticking(itemBase); } void LayerKinPaletteItem::resetID() { long offset = m_id % ModelPart::indexMultiplier; ItemBase::resetID(); m_id += offset; } QString LayerKinPaletteItem::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { return m_layerKinChief->retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } ConnectorItem* LayerKinPaletteItem::newConnectorItem(Connector *connector) { return m_layerKinChief->newConnectorItem(this, connector); } bool LayerKinPaletteItem::isSwappable() { return m_layerKinChief->isSwappable(); } void LayerKinPaletteItem::setSwappable(bool swappable) { m_layerKinChief->setSwappable(swappable); } bool LayerKinPaletteItem::inRotation() { return layerKinChief()->inRotation(); } void LayerKinPaletteItem::setInRotation(bool val) { layerKinChief()->setInRotation(val); } ////////////////////////////////////////////////// SchematicTextLayerKinPaletteItem::SchematicTextLayerKinPaletteItem(PaletteItemBase * chief, ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu* itemMenu) : LayerKinPaletteItem(chief, modelPart, viewID, viewGeometry, id, itemMenu) { } void SchematicTextLayerKinPaletteItem::cacheLoaded(const LayerAttributes & layerAttributes) { this->setProperty("textSvg", layerAttributes.loaded()); } void SchematicTextLayerKinPaletteItem::setTransform2(const QTransform & currTransf) { transformItem(currTransf, false); } void SchematicTextLayerKinPaletteItem::transformItem(const QTransform & currTransf, bool includeRatsnest) { Q_UNUSED(currTransf); Q_UNUSED(includeRatsnest); if (m_textThings.count() == 0) { initTextThings(); } double rotation; QTransform chiefTransform = layerKinChief()->transform(); // assume chief already has rotation bool isFlipped = GraphicsUtils::isFlipped(chiefTransform.toAffine(), rotation); QString svg; if (isFlipped) { svg = makeFlipTextSvg(); } if (svg.isEmpty()) { svg = this->property("textSvg").toByteArray(); } if (rotation >= 135 && rotation <= 225) { svg = vflip(svg, isFlipped); } reloadRenderer(svg, true); QPointF p = layerKinChief()->sceneBoundingRect().topLeft(); QTransform transform; QRectF bounds = boundingRect(); transform.translate(bounds.width() / 2, bounds.height() / 2); transform.rotate(rotation); transform.translate(bounds.width() / -2, bounds.height() / -2); this->setTransform(transform); //QMatrix m1 = chiefTransform.toAffine(); //layerKinChief()->debugInfo("chief " + TextUtils::svgMatrix(m1)); //m1 = transform.toAffine(); //debugInfo("\t " + TextUtils::svgMatrix(m1)); } void SchematicTextLayerKinPaletteItem::initTextThings() { QByteArray textSvg = this->property("textSvg").toByteArray(); QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (!doc.setContent(textSvg, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse schematic text: %1 %2 %3:\n%4").arg(errorStr).arg(errorLine).arg(errorColumn).arg(QString(textSvg))); return; } QDomElement root = doc.documentElement(); QDomNodeList nodeList = root.elementsByTagName("text"); QList texts; for (int i = 0; i < nodeList.count(); i++) { texts.append(nodeList.at(i).toElement()); } positionTexts(texts); } QString SchematicTextLayerKinPaletteItem::makeFlipTextSvg() { QByteArray textSvg = this->property("textSvg").toByteArray(); QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (!doc.setContent(textSvg, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse schematic text: %1 %2 %3:\n%4").arg(errorStr).arg(errorLine).arg(errorColumn).arg(QString(textSvg))); return ""; } QDomElement root = doc.documentElement(); QDomNodeList nodeList = root.elementsByTagName("text"); QList texts; for (int i = 0; i < nodeList.count(); i++) { texts.append(nodeList.at(i).toElement()); } int ix = 0; foreach (QDomElement text, texts) { QDomElement g = text.ownerDocument().createElement("g"); text.parentNode().insertAfter(g, text); g.appendChild(text); QMatrix m = m_textThings[ix++].flipMatrix; TextUtils::setSVGTransform(g, m); } return doc.toString(); } #define MINMAX(mx, my) \ if (mx < minX) minX = mx; \ if (mx > maxX) maxX = mx; \ if (my < minY) minY = my; \ if (my > maxY) maxY = my; void SchematicTextLayerKinPaletteItem::positionTexts(QList & texts) { // TODO: reuse these values unless the pin labels have changed //QString id = IDString.arg(0); //if (this->property(id.toUtf8().constData()).isValid()) { // // calculated this already // return; //} m_textThings.clear(); foreach (QDomElement text, texts) { text.setTagName("g"); } QRectF br = boundingRect(); QImage image(qCeil(br.width()) * 2, qCeil(br.height()) * 2, QImage::Format_Mono); // schematic text is so small it doesn't render unless bitmap is double-sized foreach (QDomElement text, texts) { TextThing textThing; QRectF viewBox; QMatrix matrix; renderText(image, text, textThing.minX, textThing.minY, textThing.maxX, textThing.maxY, matrix, viewBox); double newX = (image.width() - textThing.maxX) * viewBox.width() / image.width(); double oldX = textThing.minX * viewBox.width() / image.width(); QMatrix inv = matrix.inverted(); QMatrix t = QMatrix().translate(newX - oldX, 0); textThing.flipMatrix = matrix * t * inv; QRectF r(textThing.minX * viewBox.width() / image.width(), textThing.minY * viewBox.height() / image.height(), (textThing.maxX - textThing.minX) * viewBox.width() / image.width(), (textThing.maxY - textThing.minY) * viewBox.height() / image.height()); textThing.newRect = inv.mapRect(r); m_textThings.append(textThing); } foreach (QDomElement text, texts) { text.setTagName("text"); } } void SchematicTextLayerKinPaletteItem::renderText(QImage & image, QDomElement & text, int & minX, int & minY, int & maxX, int & maxY, QMatrix & matrix, QRectF & viewBox) { QString oldid = text.attribute("id"); text.setAttribute("id", IDString); // TODO: handle inherited fill/stroke values QString oldFill = text.attribute("fill"); text.setAttribute("fill", "black"); QString oldStroke = text.attribute("stroke"); text.setAttribute("stroke", "black"); text.setTagName("text"); image.fill(0xffffffff); QByteArray byteArray = text.ownerDocument().toByteArray(); QSvgRenderer renderer(byteArray); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing, false); renderer.render(&painter /*, sourceRes */); painter.end(); #ifndef QT_NO_DEBUG image.save(FolderUtils::getTopLevelUserDataStorePath() + "/renderText.png"); #endif viewBox = renderer.viewBoxF(); double x = text.attribute("x").toDouble(); double y = text.attribute("y").toDouble(); QPointF xy(x, y); matrix = renderer.matrixForElement(IDString); QPointF mxy = matrix.map(xy); QPointF p(image.width() * mxy.x() / viewBox.width(), image.height() * mxy.y() / viewBox.height()); QPoint iq((int) p.x(), (int) p.y()); minX = image.width() + 1; maxX = -1; minY = image.height() + 1; maxY = -1; // spiral around q int limit = qMax(image.width(), image.height()); for (int lim = 0; lim < limit; lim++) { int t = qMax(0, iq.y() - lim); int b = qMin(iq.y() + lim, image.height() - 1); int l = qMax(0, iq.x() - lim); int r = qMin(iq.x() + lim, image.width() - 1); for (int iy = t; iy <= b; iy++) { if (image.pixel(l, iy) == 0xff000000) { MINMAX(l, iy); } if (image.pixel(r, iy) == 0xff000000) { MINMAX(r, iy); } } for (int ix = l + 1; ix < r; ix++) { if (image.pixel(ix, t) == 0xff000000) { MINMAX(ix, t); } if (image.pixel(ix, b) == 0xff000000) { MINMAX(ix, b); } } } text.setTagName("g"); if (oldid.isEmpty()) text.removeAttribute("id"); else text.setAttribute("id", oldid); if (oldFill.isEmpty()) text.removeAttribute("fill"); else text.setAttribute("fill", oldFill); if (oldStroke.isEmpty()) text.removeAttribute("stroke"); else text.setAttribute("stroke", oldStroke); } void SchematicTextLayerKinPaletteItem::clearTextThings() { m_textThings.clear(); } QString SchematicTextLayerKinPaletteItem::vflip(const QString & svg, bool isFlipped) { Q_UNUSED(isFlipped); QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse schematic text: %1 %2 %3:\n%4").arg(errorStr).arg(errorLine).arg(errorColumn).arg(QString(svg))); return svg; } QDomElement root = doc.documentElement(); QDomNodeList nodeList = root.elementsByTagName("text"); QList texts; for (int i = 0; i < nodeList.count(); i++) { texts.append(nodeList.at(i).toElement()); } int ix = 0; foreach (QDomElement text, texts) { QDomElement g = text.ownerDocument().createElement("g"); text.parentNode().insertAfter(g, text); g.appendChild(text); QRectF r = m_textThings[ix].newRect; ix++; QMatrix m; m.translate(r.center().x(), r.center().y()); QMatrix inv = m.inverted(); QMatrix matrix = inv * QMatrix().rotate(180) * m; TextUtils::setSVGTransform(g, matrix); } return doc.toString(); } void SchematicTextLayerKinPaletteItem::setInitialTransform(const QTransform & matrix) { setTransform2(matrix); } fritzing-app-0.9.3b/src/items/layerkinpaletteitem.h000066400000000000000000000067401272363601300224460ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef LAYERKINPALETTEITEM_H #define LAYERKINPALETTEITEM_H #include "paletteitembase.h" #include class LayerKinPaletteItem : public PaletteItemBase { Q_OBJECT public: LayerKinPaletteItem(PaletteItemBase * chief, ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu); void setOffset(double x, double y); ItemBase * layerKinChief(); bool ok(); void setHidden(bool hidden); void setInactive(bool inactivate); void clearModelPart(); void initLKPI(LayerAttributes &, const LayerHash &viewLayers); bool isSticky(); bool isBaseSticky(); void setSticky(bool); void addSticky(ItemBase *, bool stickem); ItemBase * stickingTo(); QList > stickyList(); bool alreadySticking(ItemBase * itemBase); bool stickyEnabled(); void resetID(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool isSwappable(); void setSwappable(bool); bool inRotation(); void setInRotation(bool); protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value); void updateConnections(bool includeRatsnest, QList & already); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); ConnectorItem* newConnectorItem(class Connector *connector); protected: QPointer m_layerKinChief; bool m_ok; bool m_passMouseEvents; }; struct TextThing { int minX; int maxX; int minY; int maxY; QRectF newRect; QMatrix flipMatrix; }; class SchematicTextLayerKinPaletteItem : public LayerKinPaletteItem { Q_OBJECT public: SchematicTextLayerKinPaletteItem(PaletteItemBase * chief, ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu); void transformItem(const QTransform &, bool includeRatsnest); void clearTextThings(); void setTransform2(const QTransform &); public: static void renderText(QImage &, QDomElement & text, int & minX, int & minY, int & maxX, int & maxY, QMatrix &, QRectF & viewBox); protected: QString makeFlipTextSvg(); void positionTexts(QList & texts); void initTextThings(); QString vflip(const QString & svg, bool isFlipped); void setInitialTransform(const QTransform &); void cacheLoaded(const LayerAttributes &); protected: bool m_flipped; QList m_textThings; }; #endif fritzing-app-0.9.3b/src/items/led.cpp000066400000000000000000000123501272363601300174630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "led.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../commands.h" #include "../layerattributes.h" #include "moduleidnames.h" #include "partlabel.h" static QHash BreadboardSvg; static QHash IconSvg; LED::LED( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { } LED::~LED() { } QString LED::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { switch (viewLayerID) { case ViewLayer::Breadboard: case ViewLayer::Icon: break; default: return Capacitor::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QString svg = getColorSVG(prop("color"), viewLayerID); if (svg.isEmpty()) return ""; return PaletteItemBase::normalizeSvg(svg, viewLayerID, blackOnly, dpi, factor); } void LED::addedToScene(bool temporary) { if (this->scene()) { setColor(prop("color")); } return Capacitor::addedToScene(temporary); } bool LED::hasCustomSVG() { switch (m_viewID) { case ViewLayer::BreadboardView: case ViewLayer::IconView: return true; default: return ItemBase::hasCustomSVG(); } } bool LED::canEditPart() { return true; } ItemBase::PluralType LED::isPlural() { return Plural; } void LED::setProp(const QString & prop, const QString & value) { Capacitor::setProp(prop, value); if (prop.compare("color") == 0) { setColor(value); } } void LED::slamColor(QDomElement & element, const QString & colorString) { QString id = element.attribute("id"); if (id.startsWith("color_")) { element.setAttribute("fill", colorString); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { slamColor(child, colorString); child = child.nextSiblingElement(); } } void LED::setColor(const QString & color) { switch (m_viewLayerID) { case ViewLayer::Breadboard: case ViewLayer::Icon: break; default: return; } reloadRenderer(getColorSVG(color, m_viewLayerID),true); QString title = modelPart()->modelPartShared()->title(); // bypass any local title by going to modelPartShared if (title.startsWith("red", Qt::CaseInsensitive)) { title.remove(0, 3); QStringList strings = color.split(" "); modelPart()->setLocalTitle(strings.at(0) + title); } else if (title.endsWith("blue", Qt::CaseInsensitive)) { title.chop(4); QStringList strings = color.split(" "); modelPart()->setLocalTitle(title + strings.at(0)); } } QString LED::getColorSVG(const QString & color, ViewLayer::ViewLayerID viewLayerID) { QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(viewLayerID == ViewLayer::Breadboard ? BreadboardSvg.value(m_filename) : IconSvg.value(m_filename), &errorStr, &errorLine, &errorColumn)) { return ""; } QString colorString; foreach (PropertyDef * propertyDef, m_propertyDefs.keys()) { if (propertyDef->name.compare("color") == 0) { colorString = propertyDef->adjuncts.value(color, ""); break; } } if (colorString.isEmpty()) return ""; QDomElement root = domDocument.documentElement(); slamColor(root, colorString); return domDocument.toString(); } bool LED::setUpImage(ModelPart * modelPart, const LayerHash & viewLayers, LayerAttributes & layerAttributes) { bool result = Capacitor::setUpImage(modelPart, viewLayers, layerAttributes); if (layerAttributes.viewLayerID == ViewLayer::Breadboard && BreadboardSvg.value(m_filename).isEmpty() && result) { BreadboardSvg.insert(m_filename, QString(layerAttributes.loaded())); } else if (layerAttributes.viewLayerID == ViewLayer::Icon && IconSvg.value(m_filename).isEmpty() && result) { IconSvg.insert(m_filename, QString(layerAttributes.loaded())); } return result; } const QString & LED::title() { m_title = prop("color") + " LED"; return m_title; } ViewLayer::ViewID LED::useViewIDForPixmap(ViewLayer::ViewID vid, bool swappingEnabled) { if (swappingEnabled && vid == ViewLayer::BreadboardView) { return vid; } return ItemBase::useViewIDForPixmap(vid, swappingEnabled); } fritzing-app-0.9.3b/src/items/led.h000066400000000000000000000041611272363601300171310ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef LED_H #define LED_H #include #include #include #include #include "capacitor.h" class LED : public Capacitor { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call LED(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~LED(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool hasCustomSVG(); bool canEditPart(); PluralType isPlural(); void addedToScene(bool temporary); void setProp(const QString & prop, const QString & value); bool setUpImage(ModelPart* modelPart, const LayerHash & viewLayers, LayerAttributes &); const QString & title(); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); protected: void setColor(const QString & color); void slamColor(QDomElement & element, const QString & colorString); QString getColorSVG(const QString & color, ViewLayer::ViewLayerID); protected: QString m_title; }; #endif fritzing-app-0.9.3b/src/items/logoitem.cpp000066400000000000000000001066071272363601300205470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "logoitem.h" #include "../utils/graphicsutils.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../utils/fmessagebox.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../svg/gerbergenerator.h" #include "moduleidnames.h" #include "../svg/groundplanegenerator.h" #include "../utils/cursormaster.h" #include "../debugdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static QStringList LogoImageNames; static QStringList Logo0ImageNames; static QStringList NewLogoImageNames; static QStringList Copper0ImageNames; static QStringList Copper1ImageNames; LogoItem::LogoItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : ResizableBoard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { if (LogoImageNames.count() == 0) { // TODO: load these names from somewhere LogoImageNames << "new Made with Fritzing" << "new Fritzing icon" << "OHANDA logo" << "OSHW logo"; Logo0ImageNames << "new Made with Fritzing 0" << "new Fritzing icon 0" << "OHANDA logo 0" << "OSHW logo 0"; } m_svgOnly = false; m_standardizeColors = true; m_inLogoEntry = QTime::currentTime().addSecs(-10); m_aspectRatioCheck = NULL; m_keepAspectRatio = true; m_hasLogo = (modelPart->moduleID().endsWith(ModuleIDNames::LogoTextModuleIDName)); m_logo = modelPart->localProp("logo").toString(); if (m_hasLogo && m_logo.isEmpty()) { m_logo = modelPart->properties().value("logo", "logo"); modelPart->setLocalProp("logo", m_logo); } if (m_modelPart->moduleID() == ModuleIDNames::LogoImageModuleIDName) { m_modelPart->modelPartShared()->setModuleID("new" + ModuleIDNames::LogoImageModuleIDName); } else if (m_modelPart->moduleID() == ModuleIDNames::Silkscreen0LogoImageModuleIDName) { m_modelPart->modelPartShared()->setModuleID("new" + ModuleIDNames::Silkscreen0LogoImageModuleIDName); } else if (m_modelPart->moduleID() == ModuleIDNames::Copper1LogoImageModuleIDName) { m_modelPart->modelPartShared()->setModuleID("new" + ModuleIDNames::Copper1LogoImageModuleIDName); } else if (m_modelPart->moduleID() == ModuleIDNames::Copper0LogoImageModuleIDName) { m_modelPart->modelPartShared()->setModuleID("new" + ModuleIDNames::Copper0LogoImageModuleIDName); } else if (m_modelPart->moduleID() == ModuleIDNames::SchematicLogoImageModuleIDName) { m_modelPart->modelPartShared()->setModuleID("new" + ModuleIDNames::SchematicLogoImageModuleIDName); } else if (m_modelPart->moduleID() == ModuleIDNames::BreadboardLogoImageModuleIDName) { m_modelPart->modelPartShared()->setModuleID("new" + ModuleIDNames::BreadboardLogoImageModuleIDName); } } LogoItem::~LogoItem() { } bool LogoItem::hasLogo() { return m_hasLogo; } QString LogoItem::getShapeForRenderer(const QString & svg) { return svg; } void LogoItem::addedToScene(bool temporary) { if (this->scene()) { setInitialSize(); m_aspectRatio.setWidth(this->boundingRect().width()); m_aspectRatio.setHeight(this->boundingRect().height()); m_originalFilename = filename(); QString svg = prop("shape"); if (!svg.isEmpty()) { QString arString = modelPart()->localProp("aspectratio").toString(); if (!arString.isEmpty()) { m_aspectRatio = modelPart()->localProp("aspectratio").toSizeF(); } else { bool okw, okh; double w = m_modelPart->localProp("width").toDouble(&okw); double h = m_modelPart->localProp("height").toDouble(&okh); if (okw && okh) { m_aspectRatio = QSizeF(w, h); } } if (resetRenderer(getShapeForRenderer(svg))) { } else { DebugDialog::debug("bad shape in " + m_originalFilename + " " + svg); } } else { QFile f(m_originalFilename); if (f.open(QFile::ReadOnly)) { QString svg = f.readAll(); f.close(); modelPart()->setLocalProp("shape", svg); modelPart()->setLocalProp("lastfilename", m_originalFilename); initImage(); } } } return ResizableBoard::addedToScene(temporary); } bool LogoItem::canRetrieveLayer(ViewLayer::ViewLayerID viewLayerID) { return viewLayerID == layer(); } QStringList & LogoItem::getImageNames() { return isBottom() ? Logo0ImageNames : LogoImageNames; } QStringList & LogoItem::getNewImageNames() { return NewLogoImageNames; } QString LogoItem::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { if (canRetrieveLayer(viewLayerID)) { QString svg = prop("shape"); if (!svg.isEmpty()) { QString xmlName = ViewLayer::viewLayerXmlNameFromID(viewLayerID); SvgFileSplitter splitter; bool result = splitter.splitString(svg, xmlName); if (!result) { return ""; } double scaleX = 1; double scaleY = 1; if (m_hasLogo) { QDomDocument doc = splitter.domDocument(); QDomElement root = doc.documentElement(); QDomElement g = root.firstChildElement("g"); QDomElement text = g.firstChildElement("text"); // TODO: this is really a hack and resizing should change a scale factor rather than the coordinates // but it's not clear how to deal with existing sketches QString viewBox = root.attribute("viewBox"); double w = TextUtils::convertToInches(root.attribute("width")); double h = TextUtils::convertToInches(root.attribute("height")); QStringList coords = viewBox.split(" ", QString::SkipEmptyParts); double sx = w / coords.at(2).toDouble(); double sy = h / coords.at(3).toDouble(); if (qAbs(sx - sy) > .001) { // change vertical dpi to match horizontal dpi // y coordinate is really intended in relation to font size so leave it be scaleY = sy / sx; root.setAttribute("viewBox", QString("0 0 %1 %2").arg(coords.at(2)).arg(h / sx)); } } result = splitter.normalize(dpi, xmlName, blackOnly, factor); if (!result) { return ""; } QString string = splitter.elementString(xmlName); if (scaleY == 1) return string; QTransform t; t.scale(scaleX, scaleY); return TextUtils::svgTransform(string, t, false, ""); } } return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QStringList LogoItem::collectValues(const QString & family, const QString & prop, QString & value) { QStringList values = ResizableBoard::collectValues(family, prop, value); bool copperTopOK = true; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) copperTopOK = false; else if (infoGraphicsView->boardLayers() == 1) copperTopOK = false; QStringList newValues; if (prop.compare("layer", Qt::CaseInsensitive) == 0) { foreach (QString xmlName, values) { if (xmlName == ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1) && copperTopOK == false) continue; newValues << Board::convertFromXmlName(xmlName); } value = Board::convertFromXmlName(value); return newValues; } return values; } bool LogoItem::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (m_hasLogo) { if (prop.compare("logo", Qt::CaseInsensitive) == 0) { returnProp = tr("text"); returnValue = m_logo; QLineEdit * edit = new QLineEdit(parent); edit->setObjectName("infoViewLineEdit"); edit->setText(m_logo); edit->setEnabled(swappingEnabled); connect(edit, SIGNAL(editingFinished()), this, SLOT(logoEntry())); returnWidget = edit; return true; } } else { if (prop.compare("filename", Qt::CaseInsensitive) == 0) { setupLoadImage(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget); return true; } } if (prop.compare("shape", Qt::CaseInsensitive) == 0) { returnWidget = setUpDimEntry(true, !m_hasLogo, false, returnWidget); returnWidget->setEnabled(swappingEnabled); returnProp = tr("shape"); return true; } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } bool LogoItem::checkImage(const QString & filename) { Q_UNUSED(filename); return true; } void LogoItem::prepLoadImageAux(const QString & fileName, bool addName) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->loadLogoImage(this, prop("shape"), m_aspectRatio, prop("lastfilename"), fileName, addName); } } bool LogoItem::reloadImage(const QString & incomingSvg, const QSizeF & aspectRatio, const QString & fileName, bool addName) { QString svg = incomingSvg; if (isBottom()) { if (!svg.contains("_flipped_")) { svg = flipSvg(svg); } } QString shape = getShapeForRenderer(svg); bool result = resetRenderer(shape); if (result) { if (aspectRatio == QSizeF(0, 0)) { QRectF r = fsvgRenderer()->viewBoxF(); m_aspectRatio.setWidth(r.width()); m_aspectRatio.setHeight(r.height()); } else { m_aspectRatio = aspectRatio; } modelPart()->setLocalProp("aspectratio", m_aspectRatio); modelPart()->setLocalProp("shape", svg); modelPart()->setLocalProp("logo", ""); modelPart()->setLocalProp("lastfilename", fileName); QSizeF size = fsvgRenderer()->defaultSizeF(); double mmW = GraphicsUtils::pixels2mm(size.width(), GraphicsUtils::SVGDPI); double mmH = GraphicsUtils::pixels2mm(size.height(), GraphicsUtils::SVGDPI); modelPart()->setLocalProp("width", mmW); modelPart()->setLocalProp("height", mmH); modelPart()->setLocalProp("originalWidth", mmW); modelPart()->setLocalProp("originalHeight", mmH); if (addName) { if (!getNewImageNames().contains(fileName, Qt::CaseInsensitive)) { getNewImageNames().append(fileName); if (m_fileNameComboBox) { bool wasBlocked = m_fileNameComboBox->blockSignals(true); while (m_fileNameComboBox->count() > 0) { m_fileNameComboBox->removeItem(0); } setFileNameItems(); m_fileNameComboBox->blockSignals(wasBlocked); } } } m_logo = ""; setWidthAndHeight(qRound(mmW * 10) / 10.0, qRound(mmH * 10) / 10.0); return true; } else { // restore previous (not sure whether this is necessary) resetRenderer(getShapeForRenderer(prop("shape"))); unableToLoad(fileName, tr("due to a rendering error")); return false; } } void LogoItem::loadImage(const QString & fileName, bool addName) { QString svg; if (fileName.endsWith(".svg")) { QFile f(fileName); if (f.open(QFile::ReadOnly)) { svg = f.readAll(); } if (svg.isEmpty()) { unableToLoad(fileName, tr("because the svg is empty")); return; } if (svg.contains("coreldraw", Qt::CaseInsensitive) && svg.contains("cdata", Qt::CaseInsensitive)) { unableToLoad(fileName, tr("because this appears to be a SVG file exported from CorelDRAW without choosing the 'presentation attributes' setting")); return; } TextUtils::fixMuch(svg, true); TextUtils::fixPixelDimensionsIn(svg); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(svg, true, &errorStr, &errorLine, &errorColumn)) { unableToLoad(fileName, tr("due to an xml problem: %1 line:%2 column:%3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { unableToLoad(fileName, tr("because the file has no root element")); return; } if (root.tagName() != "svg") { unableToLoad(fileName, tr("because the file has no element")); return; } if (m_standardizeColors) { QStringList exceptions; exceptions << "none" << ""; QString toColor(colorString()); SvgFileSplitter::changeColors(root, toColor, exceptions); // todo: change opacity? } QRectF vb; double w, h; TextUtils::ensureViewBox(domDocument, GraphicsUtils::SVGDPI, vb, true, w, h, false); QDomElement layerElement = TextUtils::findElementWithAttribute(root, "id", layerName()); if (layerElement.isNull()) { QList rootChildren; QDomNode rootChild = root.firstChild(); while (!rootChild.isNull()) { rootChildren.append(rootChild); rootChild = rootChild.nextSibling(); } QDomElement topG = domDocument.createElement("g"); topG.setAttribute("id", layerName()); root.appendChild(topG); foreach (QDomNode node, rootChildren) { topG.appendChild(node); } } svg = TextUtils::removeXMLEntities(domDocument.toString()); } else { QImage image(fileName); if (image.isNull()) { unableToLoad(fileName, tr("for unknown reasons--possibly the image file is corrupted")); return; } if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_Mono); } double res = image.dotsPerMeterX() / GraphicsUtils::InchesPerMeter; if (this->m_standardizeColors) { GroundPlaneGenerator gpg; gpg.setLayerName(layerName()); gpg.setMinRunSize(1, 1); gpg.scanImage(image, image.width(), image.height(), 1, res, colorString(), false, false, QSizeF(0, 0), 0, QPointF(0, 0)); if (gpg.newSVGs().count() < 1) { FMessageBox::information( NULL, tr("Unable to display"), tr("Unable to display image from %1").arg(fileName) ); return; } svg = gpg.mergeSVGs("", layerName()); } else { svg = TextUtils::makeSVGHeader(res, res, image.width(), image.height()); int ix = svg.lastIndexOf(">"); svg.replace(ix, 1, "xmlns:xlink='http://www.w3.org/1999/xlink'>"); QString type = "png"; QByteArray bytes; if (fileName.endsWith(".png") || fileName.endsWith(".jpg") || fileName.endsWith("jpeg")) { if (!fileName.endsWith(".png")) type = "jpg"; QFile file(fileName); file.open(QFile::ReadOnly); bytes = file.readAll(); } else { QBuffer buffer(&bytes); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); // writes image into bytes in PNG format } svg += QString("\n"; svg += ""; } } reloadImage(svg, QSizeF(0, 0), fileName, addName); } bool LogoItem::resizeMM(double mmW, double mmH, const LayerHash & viewLayers) { Q_UNUSED(viewLayers); if (mmW == 0 || mmH == 0) { return false; } // DebugDialog::debug(QString("resize mm %1 %2").arg(mmW).arg(mmH)); QRectF r = this->boundingRect(); if (qAbs(GraphicsUtils::pixels2mm(r.width(), GraphicsUtils::SVGDPI) - mmW) < .001 && qAbs(GraphicsUtils::pixels2mm(r.height(), GraphicsUtils::SVGDPI) - mmH) < .001) { return false; } double inW = GraphicsUtils::mm2mils(mmW) / 1000; double inH = GraphicsUtils::mm2mils(mmH) / 1000; // TODO: deal with aspect ratio QString svg = prop("shape"); if (svg.isEmpty()) return false; QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(svg, &errorStr, &errorLine, &errorColumn)) { return false; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { return false; } if (root.tagName() != "svg") { return false; } root.setAttribute("width", QString::number(inW) + "in"); root.setAttribute("height", QString::number(inH) + "in"); svg = TextUtils::removeXMLEntities(domDocument.toString()); QString shape = getShapeForRenderer(svg); bool result = resetRenderer(shape); if (result) { modelPart()->setLocalProp("shape", svg); modelPart()->setLocalProp("width", mmW); modelPart()->setLocalProp("height", mmH); } setWidthAndHeight(qRound(mmW * 10) / 10.0, qRound(mmH * 10) / 10.0); return true; } void LogoItem::setProp(const QString & prop, const QString & value) { if (prop.compare("logo", Qt::CaseInsensitive) == 0) { setLogo(value, false); return; } ResizableBoard::setProp(prop, value); } void LogoItem::setLogo(QString logo, bool force) { if (!force && m_logo.compare(logo) == 0) return; if (!isEverVisible()) { return; } QString svg; QFile f(m_originalFilename); if (f.open(QFile::ReadOnly)) { svg = f.readAll(); } if (svg.isEmpty()) return; QSizeF oldSize = m_size; QXmlStreamReader streamReader(svg); QSizeF oldSvgSize = fsvgRenderer() ? fsvgRenderer()->viewBoxF().size() : QSizeF(0, 0); DebugDialog::debug(QString("size %1 %2, %3 %4").arg(m_size.width()).arg(m_size.height()).arg(oldSvgSize.width()).arg(oldSvgSize.height())); svg = hackSvg(svg, logo); QXmlStreamReader newStreamReader(svg); bool ok = rerender(svg); m_logo = logo; modelPart()->setLocalProp("logo", logo); modelPart()->setLocalProp("shape", svg); if (ok && !force) { QSizeF newSvgSize = fsvgRenderer()->viewBoxF().size(); QSizeF newSize = newSvgSize * oldSize.height() / oldSvgSize.height(); DebugDialog::debug(QString("size %1 %2, %3 %4").arg(m_size.width()).arg(m_size.height()).arg(newSize.width()).arg(newSize.height())); // set the new text to approximately the same height as the original // if the text is non-proportional that will be lost LayerHash layerHash; resizeMM(GraphicsUtils::pixels2mm(newSize.width(), GraphicsUtils::SVGDPI), GraphicsUtils::pixels2mm(newSize.height(), GraphicsUtils::SVGDPI), layerHash); //DebugDialog::debug(QString("size %1 %2").arg(m_size.width()).arg(m_size.height())); } } bool LogoItem::rerender(const QString & svg) { QString newSvg; bool result = resetRenderer(svg); if (result) { QRectF r = fsvgRenderer()->viewBoxF(); m_aspectRatio.setWidth(r.width()); m_aspectRatio.setHeight(r.height()); } return result; } QString LogoItem::getProperty(const QString & key) { if (key.compare("logo", Qt::CaseInsensitive) == 0) { return m_logo; } return PaletteItem::getProperty(key); } const QString & LogoItem::logo() { return m_logo; } bool LogoItem::canEditPart() { return false; } bool LogoItem::hasPartLabel() { return false; } void LogoItem::logoEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; logoEntryAux(edit->text()); } void LogoItem::logoEntryAux(const QString & text) { if (text.compare(this->logo()) == 0) return; // m_inLogoEntry is a hack because a focus out event was being triggered on m_widthEntry when a user hit the return key in logoentry // this triggers a call to widthEntry() which causes all kinds of havoc. (bug in version 0.7.1 and possibly earlier) m_inLogoEntry = QTime::currentTime().addSecs(1); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "logo", tr("text"), this->logo(), text, true); } } void LogoItem::initImage() { if (m_hasLogo) { setLogo(m_logo, true); return; } loadImage(m_originalFilename, false); } QString LogoItem::hackSvg(const QString & svg, const QString & logo) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) return svg; QDomElement root = doc.documentElement(); root.setAttribute("width", QString::number(logo.length() * 0.1) + "in"); QString viewBox = root.attribute("viewBox"); QStringList coords = viewBox.split(" ", QString::SkipEmptyParts); coords[2] = QString::number(logo.length() * 10); root.setAttribute("viewBox", coords.join(" ")); QStringList exceptions; exceptions << "none" << ""; QString toColor(colorString()); SvgFileSplitter::changeColors(root, toColor, exceptions); QDomNodeList domNodeList = root.elementsByTagName("text"); for (int i = 0; i < domNodeList.count(); i++) { QDomElement node = domNodeList.item(i).toElement(); if (node.isNull()) continue; if (node.attribute("id").compare("label") != 0) continue; node.setAttribute("x", QString::number(logo.length() * 5)); QDomNodeList childList = node.childNodes(); for (int j = 0; j < childList.count(); j++) { QDomNode child = childList.item(i); if (child.isText()) { child.setNodeValue(logo); modelPart()->setLocalProp("width", logo.length() * 0.1 * 25.4); QString h = root.attribute("height"); bool ok; modelPart()->setLocalProp("height", TextUtils::convertToInches(h, &ok, false) * 25.4); if (!isBottom()) return doc.toString(); return flipSvg(doc.toString()); } } } if (!isBottom()) return svg; return flipSvg(svg); } void LogoItem::widthEntry() { if (QTime::currentTime() < m_inLogoEntry) return; QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; double w = edit->text().toDouble(); double oldW = m_modelPart->localProp("width").toDouble(); if (w == oldW) return; double h = m_modelPart->localProp("height").toDouble(); if (m_keepAspectRatio) { h = w * m_aspectRatio.height() / m_aspectRatio.width(); } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->resizeBoard(w, h, true); } } void LogoItem::heightEntry() { if (QTime::currentTime() < m_inLogoEntry) return; QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; double h = edit->text().toDouble(); double oldH = m_modelPart->localProp("height").toDouble(); if (h == oldH) return; setHeight(h); } void LogoItem::setHeight(double h) { double w = m_modelPart->localProp("width").toDouble(); if (m_keepAspectRatio) { w = h * m_aspectRatio.width() / m_aspectRatio.height(); } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->resizeBoard(w, h, true); } } bool LogoItem::stickyEnabled() { return true; } ItemBase::PluralType LogoItem::isPlural() { return Plural; } ViewLayer::ViewLayerID LogoItem::layer() { return isBottom() ? ViewLayer::Silkscreen0 : ViewLayer::Silkscreen1; } QString LogoItem::colorString() { return isBottom() ? ViewLayer::Silkscreen0Color : ViewLayer::Silkscreen1Color; } QString LogoItem::layerName() { return ViewLayer::viewLayerXmlNameFromID(layer()); } double LogoItem::minWidth() { double zoom = 1; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { zoom = infoGraphicsView->currentZoom() / 100; if (zoom == 0) zoom = 1; } return ResizableBoard::CornerHandleSize * 2 / zoom; } double LogoItem::minHeight() { double zoom = 1; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { zoom = infoGraphicsView->currentZoom() / 100; if (zoom == 0) zoom = 1; } return ResizableBoard::CornerHandleSize * 2 / zoom; } bool LogoItem::freeRotationAllowed(Qt::KeyboardModifiers modifiers) { if ((modifiers & altOrMetaModifier()) == 0) return false; if (!isSelected()) return false; return true; } bool LogoItem::freeRotationAllowed() { return true; } ResizableBoard::Corner LogoItem::findCorner(QPointF scenePos, Qt::KeyboardModifiers modifiers) { ResizableBoard::Corner corner = ResizableBoard::findCorner(scenePos, modifiers); if (corner == ResizableBoard::NO_CORNER) return corner; if (modifiers & altOrMetaModifier()) { // free rotate setCursor(*CursorMaster::RotateCursor); return ResizableBoard::NO_CORNER; } return corner; } void LogoItem::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { PaletteItem::paintHover(painter, option, widget); } bool LogoItem::isBottom() { QString layer = modelPart()->properties().value("layer"); return layer.contains("0") || layer.contains("bottom"); } QString LogoItem::flipSvg(const QString & svg) { QString newSvg = svg; // need the end quote in the replace since the xml for silkscreen1 is just "silkscreen" newSvg.replace("\"" + ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen1) + "\"", "\"" + ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen0) + "\""); newSvg.replace("'" + ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen1) + "'", "'" + ViewLayer::viewLayerXmlNameFromID(ViewLayer::Silkscreen0) + "'"); newSvg.replace(ViewLayer::Silkscreen1Color, ViewLayer::Silkscreen0Color, Qt::CaseInsensitive); return flipSvgAux(newSvg); } QString LogoItem::flipSvgAux(QString & newSvg) { QMatrix m; QSvgRenderer renderer(newSvg.toUtf8()); QRectF bounds = renderer.viewBoxF(); m.translate(bounds.center().x(), bounds.center().y()); QMatrix mMinus = m.inverted(); QMatrix cm = mMinus * QMatrix().scale(-1, 1) * m; int gix = newSvg.indexOf("prop("lastfilename"); if (QFile::exists(lastfilename)) return lastfilename; DebugDialog::debug(QString("logo item image '%1' doesn't exist").arg(lastfilename)); return ""; } QString LogoItem::getInspectorTitle() { if (hasLogo()) return m_logo; return ResizableBoard::getInspectorTitle(); } void LogoItem::setInspectorTitle(const QString & oldText, const QString & newText) { if (!hasLogo()) return ResizableBoard::setInspectorTitle(oldText, newText); logoEntryAux(newText); } /////////////////////////////////////////////////////////////////////// SchematicLogoItem::SchematicLogoItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : LogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { } SchematicLogoItem::~SchematicLogoItem() { } ViewLayer::ViewLayerID SchematicLogoItem::layer() { return (m_hasLogo ? ViewLayer::SchematicText : ViewLayer::Schematic); } QString SchematicLogoItem::colorString() { return "black"; } bool SchematicLogoItem::isBottom() { return false; } /////////////////////////////////////////////////////////////////////// BreadboardLogoItem::BreadboardLogoItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : LogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_color = "#787878"; QString color = modelPart->localProp("color").toString(); if (color.isEmpty()) { color = modelPart->properties().value("color"); if (color.isEmpty()) color = m_color; modelPart->setLocalProp("color", color); } m_color = color; m_standardizeColors = false; } BreadboardLogoItem::~BreadboardLogoItem() { } ViewLayer::ViewLayerID BreadboardLogoItem::layer() { return ViewLayer::Breadboard; } QString BreadboardLogoItem::colorString() { return m_color; } bool BreadboardLogoItem::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (m_hasLogo) { if (prop.compare("color", Qt::CaseInsensitive) == 0) { returnProp = tr("color"); returnValue = m_color; QPushButton * button = new QPushButton(tr("Set text color")); button->setObjectName("infoViewButton"); connect(button, SIGNAL(pressed()), this, SLOT(changeTextColor())); returnWidget = button; return true; } } return LogoItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void BreadboardLogoItem::changeTextColor() { QColor color(m_color); QColor newColor = QColorDialog::getColor(color, NULL, tr("Select text color")); if (!newColor.isValid()) return; if (newColor.name().compare(m_color, Qt::CaseInsensitive) == 0) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "color", tr("color"), m_color, newColor.name(), true); } } void BreadboardLogoItem::setProp(const QString & prop, const QString & value) { if (m_hasLogo && prop.compare("color", Qt::CaseInsensitive) == 0) { m_color = value; modelPart()->setLocalProp("color", m_color); setLogo(m_logo, true); return; } LogoItem::setProp(prop, value); } bool BreadboardLogoItem::isBottom() { return false; } /////////////////////////////////////////////////////////////////////// CopperLogoItem::CopperLogoItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : LogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { // TODO: load these names from somewhere if (Copper1ImageNames.count() == 0) { Copper1ImageNames << "new Made with Fritzing copper1" << "new Fritzing icon copper1" << "OHANDA logo copper1" << "OSHW logo copper1"; } if (Copper0ImageNames.count() == 0) { Copper0ImageNames << "new Made with Fritzing copper0" << "new Fritzing icon copper0" << "OHANDA logo copper0" << "OSHW logo copper1"; } m_hasLogo = (modelPart->moduleID().endsWith(ModuleIDNames::LogoTextModuleIDName)); m_logo = modelPart->localProp("logo").toString(); if (m_hasLogo && m_logo.isEmpty()) { m_logo = modelPart->properties().value("logo", "logo"); modelPart->setLocalProp("logo", m_logo); } } CopperLogoItem::~CopperLogoItem() { } ViewLayer::ViewLayerID CopperLogoItem::layer() { return isBottom() ? ViewLayer::Copper0 : ViewLayer::Copper1; } QString CopperLogoItem::colorString() { return isBottom() ? ViewLayer::Copper0Color : ViewLayer::Copper1Color; } QStringList & CopperLogoItem::getImageNames() { return isBottom() ? Copper0ImageNames : Copper1ImageNames; } QString CopperLogoItem::flipSvg(const QString & svg) { QString newSvg = svg; newSvg.replace(ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper1), ViewLayer::viewLayerXmlNameFromID(ViewLayer::Copper0)); newSvg.replace(ViewLayer::Copper1Color, ViewLayer::Copper0Color, Qt::CaseInsensitive); return flipSvgAux(newSvg); } //////////////////////////////////////////// // todo: // // don't mess with colors at all? BoardLogoItem::BoardLogoItem(ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : LogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_hasLogo = false; m_svgOnly = true; m_standardizeColors = false; if (BoardImageNames.count() == 0) { BoardImageNames << "circle_pcb"; } } BoardLogoItem::~BoardLogoItem() { } QString BoardLogoItem::getShapeForRenderer(const QString & svg) { return ResizableBoard::getShapeForRenderer(svg, m_viewLayerID); } ViewLayer::ViewLayerID BoardLogoItem::layer() { return ViewLayer::Board; } QString BoardLogoItem::colorString() { return ViewLayer::BoardColor; } QStringList & BoardLogoItem::getImageNames() { return BoardImageNames; } QStringList & BoardLogoItem::getNewImageNames() { return NewBoardImageNames; } bool BoardLogoItem::resizeMM(double mmW, double mmH, const LayerHash & viewLayers) { bool result = LogoItem::resizeMM(mmW, mmH, viewLayers); if (!result) return result; reloadLayerKin(mmW, mmH); return result; } void BoardLogoItem::reloadLayerKin(double mmW, double mmH) { foreach (ItemBase * itemBase, m_layerKin) { if (itemBase->viewLayerID() == ViewLayer::Silkscreen1 || itemBase->viewLayerID() == ViewLayer::Silkscreen0) { QString svg = ResizableBoard::getShapeForRenderer(prop("shape"), itemBase->viewLayerID()); if (!svg.isEmpty()) { itemBase->prepareGeometryChange(); if (itemBase->resetRenderer(svg)) { itemBase->modelPart()->setLocalProp("width", mmW); itemBase->modelPart()->setLocalProp("height", mmH); } } } } } bool BoardLogoItem::canRetrieveLayer(ViewLayer::ViewLayerID viewLayerID) { return LogoItem::canRetrieveLayer(viewLayerID) || viewLayerID == LogoItem::layer() || viewLayerID == ViewLayer::Silkscreen0 || viewLayerID == ViewLayer::Silkscreen1; } bool BoardLogoItem::reloadImage(const QString & svg, const QSizeF & aspectRatio, const QString & fileName, bool addName) { bool result; if (!svg.contains(GerberGenerator::MagicBoardOutlineID, Qt::CaseInsensitive)) { QString nsvg = setBoardOutline(svg); result = LogoItem::reloadImage(nsvg, aspectRatio, fileName, addName); } else { result = LogoItem::reloadImage(svg, aspectRatio, fileName, addName); } if (!result) return result; reloadLayerKin(prop("width").toDouble(), prop("height").toDouble()); return result; } bool BoardLogoItem::checkImage(const QString & filename) { // override logoitem return ResizableBoard::checkImage(filename); } bool BoardLogoItem::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("shape", Qt::CaseInsensitive) == 0) { Board::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); returnWidget = setUpDimEntry(true, true, false, returnWidget); returnWidget->setEnabled(swappingEnabled); m_aspectRatioCheck->setEnabled(false); m_aspectRatioCheck->setChecked(true); m_aspectRatioCheck->setVisible(false); m_aspectRatioLabel->setVisible(false); returnProp = tr("shape"); return true; } return LogoItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } bool BoardLogoItem::isBottom() { return false; } fritzing-app-0.9.3b/src/items/logoitem.h000066400000000000000000000137341272363601300202120ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef LOGOITEM_H #define LOGOITEM_H #include #include #include #include #include #include #include #include "resizableboard.h" class LogoItem : public ResizableBoard { Q_OBJECT public: LogoItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~LogoItem(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool resizeMM(double w, double h, const LayerHash & viewLayers); QString getProperty(const QString & key); void setLogo(QString logo, bool force); const QString & logo(); bool canEditPart(); void setProp(const QString & prop, const QString & value); bool hasPartLabel(); void loadImage(const QString & fileName, bool addName); virtual bool reloadImage(const QString & svg, const QSizeF & aspectRatio, const QString & fileName, bool addName); bool stickyEnabled(); PluralType isPlural(); void addedToScene(bool temporary); void setHeight(double h); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool hasLogo(); QString getNewLayerFileName(const QString & newLayer); QString getInspectorTitle(); void setInspectorTitle(const QString & oldText, const QString & newText); protected slots: void logoEntry(); void widthEntry(); void heightEntry(); protected: virtual QString hackSvg(const QString & svg, const QString & logo); void initImage(); void prepLoadImageAux(const QString & fileName, bool addName); virtual ViewLayer::ViewLayerID layer(); virtual QString colorString(); virtual QString layerName(); bool rerender(const QString & svg); double minWidth(); double minHeight(); bool freeRotationAllowed(Qt::KeyboardModifiers modifiers); bool freeRotationAllowed(); ResizableBoard::Corner findCorner(QPointF p, Qt::KeyboardModifiers); virtual QString getShapeForRenderer(const QString & svg); virtual bool canRetrieveLayer(ViewLayer::ViewLayerID viewLayerID); bool checkImage(const QString & filename); QStringList & getImageNames(); QStringList & getNewImageNames(); virtual bool isBottom(); virtual QString flipSvg(const QString & svg); QString flipSvgAux(QString & newSvg); QStringList collectValues(const QString & family, const QString & prop, QString & value); void logoEntryAux(const QString & newText); protected: QString m_logo; bool m_hasLogo; QString m_originalFilename; QTime m_inLogoEntry; bool m_standardizeColors; }; class CopperLogoItem : public LogoItem { Q_OBJECT public: CopperLogoItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~CopperLogoItem(); protected: ViewLayer::ViewLayerID layer(); QString colorString(); QStringList & getImageNames(); QString flipSvg(const QString & svg); }; class SchematicLogoItem : public LogoItem { Q_OBJECT public: SchematicLogoItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~SchematicLogoItem(); protected: ViewLayer::ViewLayerID layer(); QString colorString(); bool isBottom(); }; class BreadboardLogoItem : public LogoItem { Q_OBJECT public: BreadboardLogoItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~BreadboardLogoItem(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); void setProp(const QString & prop, const QString & value); public slots: void changeTextColor(); protected: ViewLayer::ViewLayerID layer(); QString colorString(); bool isBottom(); protected: QString m_color; }; class BoardLogoItem : public LogoItem { Q_OBJECT public: BoardLogoItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~BoardLogoItem(); bool resizeMM(double w, double h, const LayerHash & viewLayers); bool reloadImage(const QString & svg, const QSizeF & aspectRatio, const QString & fileName, bool addName); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); protected: ViewLayer::ViewLayerID layer(); QString colorString(); QStringList & getImageNames(); QStringList & getNewImageNames(); QString getShapeForRenderer(const QString & svg); bool canRetrieveLayer(ViewLayer::ViewLayerID viewLayerID); void reloadLayerKin(double mmW, double mmH); bool checkImage(const QString & filename); bool isBottom(); }; #endif fritzing-app-0.9.3b/src/items/moduleidnames.cpp000066400000000000000000000122061272363601300215450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6947 $: $Author: irascibl@gmail.com $: $Date: 2013-04-03 06:45:22 +0200 (Mi, 03. Apr 2013) $ ********************************************************************/ #include "moduleidnames.h" ///////////////////////////////// const QString ModuleIDNames::ModuleIDNameSuffix = "ModuleID"; const QString ModuleIDNames::RulerModuleIDName = "RulerModuleID"; const QString ModuleIDNames::BreadboardModuleIDName = "BreadboardModuleID"; const QString ModuleIDNames::FullPlusBreadboardModuleIDName = "Breadboard-RSR03MB102-ModuleID"; const QString ModuleIDNames::RectanglePCBModuleIDName = "RectanglePCBModuleID"; const QString ModuleIDNames::TwoSidedRectanglePCBModuleIDName = "TwoLayerRectanglePCBModuleID"; const QString ModuleIDNames::EllipsePCBModuleIDName = "EllipsePCBModuleID"; const QString ModuleIDNames::TwoSidedEllipsePCBModuleIDName = "TwoLayerEllipsePCBModuleID"; const QString ModuleIDNames::NoteModuleIDName = "NoteModuleID"; const QString ModuleIDNames::WireModuleIDName = "WireModuleID"; const QString ModuleIDNames::JumperModuleIDName = "JumperModuleID"; const QString ModuleIDNames::GroundPlaneModuleIDName = "GroundPlaneModuleID"; const QString ModuleIDNames::GroundModuleIDName = "GroundModuleID"; const QString ModuleIDNames::PowerModuleIDName = "PowerModuleID"; const QString ModuleIDNames::TwoPowerModuleIDName = "2PowerModuleID"; const QString ModuleIDNames::NetLabelModuleIDName = "NetLabelModuleID"; const QString ModuleIDNames::PowerLabelModuleIDName = "PowerLabelModuleID"; const QString ModuleIDNames::LeftNetLabelModuleIDName = "LeftNetLabelModuleID"; const QString ModuleIDNames::JustPowerModuleIDName = "JustPowerModuleID"; const QString ModuleIDNames::ResistorModuleIDName = "ResistorModuleID"; const QString ModuleIDNames::LogoTextModuleIDName = "LogoTextModuleID"; const QString ModuleIDNames::LogoImageModuleIDName = "LogoImageModuleID"; const QString ModuleIDNames::Silkscreen0LogoImageModuleIDName = "Silkscreen0LogoImageModuleID"; const QString ModuleIDNames::Copper1LogoTextModuleIDName = "Copper1LogoTextModuleID"; const QString ModuleIDNames::Copper1LogoImageModuleIDName = "Copper1LogoImageModuleID"; const QString ModuleIDNames::Copper0LogoTextModuleIDName = "Copper0LogoTextModuleID"; const QString ModuleIDNames::Copper0LogoImageModuleIDName = "Copper0LogoImageModuleID"; const QString ModuleIDNames::SchematicLogoTextModuleIDName = "SchematicLogoTextModuleID"; const QString ModuleIDNames::SchematicLogoImageModuleIDName = "SchematicLogoImageModuleID"; const QString ModuleIDNames::BreadboardLogoTextModuleIDName = "BreadboardLogoTextModuleID"; const QString ModuleIDNames::BreadboardLogoImageModuleIDName = "BreadboardLogoImageModuleID"; const QString ModuleIDNames::BoardLogoImageModuleIDName = "BoardLogoImageModuleID"; const QString ModuleIDNames::OneLayerBoardLogoImageModuleIDName = "OneLayerBoardLogoImageModuleID"; const QString ModuleIDNames::HoleModuleIDName = "HoleModuleID"; const QString ModuleIDNames::ViaModuleIDName = "ViaModuleID"; const QString ModuleIDNames::PadModuleIDName = "PadModuleID"; const QString ModuleIDNames::Copper0PadModuleIDName = "Copper0PadModuleID"; const QString ModuleIDNames::CapacitorModuleIDName = "CapacitorModuleID"; const QString ModuleIDNames::CrystalModuleIDName = "CrystalModuleID"; const QString ModuleIDNames::ZenerDiodeModuleIDName = "ZenerDiodeModuleID"; const QString ModuleIDNames::ThermistorModuleIDName = "ThermistorModuleID"; const QString ModuleIDNames::PotentiometerModuleIDName = "PotentiometerModuleID"; const QString ModuleIDNames::InductorModuleIDName = "InductorModuleID"; const QString ModuleIDNames::LEDModuleIDName = "LEDModuleID"; const QString ModuleIDNames::ColorFluxLEDModuleIDName = "LEDSuperfluxModuleID"; const QString ModuleIDNames::ColorLEDModuleIDName = "ColorLEDModuleID"; const QString ModuleIDNames::PerfboardModuleIDName = "PerfboardModuleID"; const QString ModuleIDNames::StripboardModuleIDName = "StripboardModuleID"; const QString ModuleIDNames::Stripboard2ModuleIDName = "Stripboard2ModuleID"; const QString ModuleIDNames::SpacerModuleIDName = "__spacer__"; const QString ModuleIDNames::SchematicFrameModuleIDName = "SchematicFrameModuleID"; const QString ModuleIDNames::CopperBlockerModuleIDName = "BlockerModuleID"; const QString ModuleIDNames::Copper0BlockerModuleIDName = "Copper0BlockerModuleID"; const QString ModuleIDNames::Copper1BlockerModuleIDName = "Copper1BlockerModuleID"; fritzing-app-0.9.3b/src/items/moduleidnames.h000066400000000000000000000071171272363601300212170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Licensetriple along with Fritzing. If not, see . ******************************************************************** $Revision: 6947 $: $Author: irascibl@gmail.com $: $Date: 2013-04-03 06:45:22 +0200 (Mi, 03. Apr 2013) $ ********************************************************************/ #ifndef MODULEIDNAMES_H #define MODULEIDNAMES_H #include class ModuleIDNames { public: static const QString ModuleIDNameSuffix; static const QString RulerModuleIDName; static const QString BreadboardModuleIDName; static const QString FullPlusBreadboardModuleIDName; static const QString RectanglePCBModuleIDName; static const QString TwoSidedRectanglePCBModuleIDName; static const QString EllipsePCBModuleIDName; static const QString TwoSidedEllipsePCBModuleIDName; static const QString NoteModuleIDName; static const QString WireModuleIDName; static const QString JumperModuleIDName; static const QString GroundPlaneModuleIDName; static const QString GroundModuleIDName; static const QString PowerModuleIDName; static const QString TwoPowerModuleIDName; static const QString NetLabelModuleIDName; static const QString PowerLabelModuleIDName; static const QString LeftNetLabelModuleIDName; static const QString JustPowerModuleIDName; static const QString ResistorModuleIDName; static const QString LogoTextModuleIDName; static const QString LogoImageModuleIDName; static const QString Silkscreen0LogoImageModuleIDName; static const QString Copper0LogoTextModuleIDName; static const QString Copper0LogoImageModuleIDName; static const QString Copper1LogoTextModuleIDName; static const QString Copper1LogoImageModuleIDName; static const QString SchematicLogoTextModuleIDName; static const QString SchematicLogoImageModuleIDName; static const QString BreadboardLogoTextModuleIDName; static const QString BreadboardLogoImageModuleIDName; static const QString BoardLogoImageModuleIDName; static const QString OneLayerBoardLogoImageModuleIDName; static const QString HoleModuleIDName; static const QString ViaModuleIDName; static const QString PadModuleIDName; static const QString Copper0PadModuleIDName; static const QString CapacitorModuleIDName; static const QString CrystalModuleIDName; static const QString ZenerDiodeModuleIDName; static const QString ThermistorModuleIDName; static const QString PotentiometerModuleIDName; static const QString InductorModuleIDName; static const QString LEDModuleIDName; static const QString ColorLEDModuleIDName; static const QString ColorFluxLEDModuleIDName; static const QString PerfboardModuleIDName; static const QString StripboardModuleIDName; static const QString Stripboard2ModuleIDName; static const QString SpacerModuleIDName; static const QString SchematicFrameModuleIDName; static const QString CopperBlockerModuleIDName; static const QString Copper1BlockerModuleIDName; static const QString Copper0BlockerModuleIDName; }; #endif fritzing-app-0.9.3b/src/items/mysterypart.cpp000066400000000000000000000577071272363601300213410ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "mysterypart.h" #include "../utils/graphicsutils.h" #include "../utils/familypropertycombobox.h" #include "../utils/schematicrectconstants.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../commands.h" #include "../utils/textutils.h" #include "../layerattributes.h" #include "partlabel.h" #include "pinheader.h" #include "partfactory.h" #include "../connectors/connectoritem.h" #include "../svg/svgfilesplitter.h" #include #include #include #include static QStringList Spacings; static QRegExp Digits("(\\d)+"); static QRegExp DigitsMil("(\\d)+mil"); static const int MinSipPins = 1; static const int MaxSipPins = 128; static const int MinDipPins = 4; static const int MaxDipPins = 128; static HoleClassThing TheHoleThing; // TODO // save into parts bin MysteryPart::MysteryPart( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_chipLabel = modelPart->localProp("chip label").toString(); if (m_chipLabel.isEmpty()) { m_chipLabel = modelPart->properties().value("chip label", "?"); modelPart->setLocalProp("chip label", m_chipLabel); } setUpHoleSizes("mystery", TheHoleThing); } MysteryPart::~MysteryPart() { } void MysteryPart::setProp(const QString & prop, const QString & value) { if (prop.compare("chip label", Qt::CaseInsensitive) == 0) { setChipLabel(value, false); return; } PaletteItem::setProp(prop, value); } void MysteryPart::setChipLabel(QString chipLabel, bool force) { if (!force && m_chipLabel.compare(chipLabel) == 0) return; m_chipLabel = chipLabel; QString svg; switch (this->m_viewID) { case ViewLayer::BreadboardView: svg = makeSvg(chipLabel, true); if (!svg.isEmpty()) { resetRenderer(svg); } break; case ViewLayer::SchematicView: { QTransform transform = untransform(); svg = makeSvg(chipLabel, false); svg = retrieveSchematicSvg(svg); resetLayerKin(svg); retransform(transform); } break; default: break; } modelPart()->setLocalProp("chip label", chipLabel); if (m_partLabel) m_partLabel->displayTextsIf(); } QString MysteryPart::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { QString svg = PaletteItem::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); switch (viewLayerID) { case ViewLayer::Breadboard: case ViewLayer::Icon: return TextUtils::replaceTextElement(svg, "label", m_chipLabel); case ViewLayer::Schematic: svg = retrieveSchematicSvg(svg); return TextUtils::removeSVGHeader(svg); default: break; } return svg; } QString MysteryPart::retrieveSchematicSvg(QString & svg) { bool hasLocal = false; QStringList labels = getPinLabels(hasLocal); if (hasLocal) { svg = makeSchematicSvg(labels, false); } return TextUtils::replaceTextElement(svg, "label", m_chipLabel); } QString MysteryPart::makeSvg(const QString & chipLabel, bool replace) { QString path = filename(); QFile file(filename()); QString svg; if (file.open(QFile::ReadOnly)) { svg = file.readAll(); file.close(); if (!replace) return svg; return TextUtils::replaceTextElement(svg, "label", chipLabel); } return ""; } QStringList MysteryPart::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("layout", Qt::CaseInsensitive) == 0) { // TODO: translate these QStringList values; values << "Single Row" << "Double Row"; value = values.at(moduleID().contains("sip", Qt::CaseInsensitive) ? 0 : 1); return values; } if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) { QStringList values; QString spacing; TextUtils::getPinsAndSpacing(moduleID(), spacing); if (isDIP()) { foreach (QString f, spacings()) { values.append(f); } } else { values.append(spacing); } value = spacing; return values; } if (prop.compare("pins", Qt::CaseInsensitive) == 0) { QStringList values; value = modelPart()->properties().value("pins"); if (moduleID().contains("dip")) { for (int i = MinDipPins; i <= MaxDipPins; i += 2) { values << QString::number(i); } } else { for (int i = MinSipPins; i <= MaxSipPins; i++) { values << QString::number(i); } } return values; } return PaletteItem::collectValues(family, prop, value); } bool MysteryPart::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("chip label", Qt::CaseInsensitive) == 0) { returnProp = tr("label"); returnValue = m_chipLabel; QLineEdit * e1 = new QLineEdit(parent); e1->setEnabled(swappingEnabled); e1->setText(m_chipLabel); connect(e1, SIGNAL(editingFinished()), this, SLOT(chipLabelEntry())); e1->setObjectName("infoViewLineEdit"); returnWidget = e1; return true; } if (prop.compare("hole size", Qt::CaseInsensitive) == 0) { return collectHoleSizeInfo(TheHoleThing.holeSizeValue, parent, swappingEnabled, returnProp, returnValue, returnWidget); } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } QString MysteryPart::getProperty(const QString & key) { if (key.compare("chip label", Qt::CaseInsensitive) == 0) { return m_chipLabel; } return PaletteItem::getProperty(key); } QString MysteryPart::chipLabel() { return m_chipLabel; } void MysteryPart::addedToScene(bool temporary) { if (this->scene()) { setChipLabel(m_chipLabel, true); } PaletteItem::addedToScene(temporary); } const QString & MysteryPart::title() { return m_chipLabel; } bool MysteryPart::hasCustomSVG() { switch (m_viewID) { case ViewLayer::BreadboardView: case ViewLayer::SchematicView: case ViewLayer::IconView: case ViewLayer::PCBView: return true; default: return ItemBase::hasCustomSVG(); } } void MysteryPart::chipLabelEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; if (edit->text().compare(this->chipLabel()) == 0) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "chip label", tr("chip label"), this->chipLabel(), edit->text(), true); } } bool MysteryPart::isDIP() { QString layout = modelPart()->properties().value("layout", ""); return (layout.indexOf("double", 0, Qt::CaseInsensitive) >= 0); } bool MysteryPart::otherPropsChange(const QMap & propsMap) { QString layout = modelPart()->properties().value("layout", ""); return (layout.compare(propsMap.value("layout", "")) != 0); } const QStringList & MysteryPart::spacings() { if (Spacings.count() == 0) { Spacings << "100mil" << "200mil" << "300mil" << "400mil" << "500mil" << "600mil" << "700mil" << "800mil" << "900mil"; } return Spacings; } ItemBase::PluralType MysteryPart::isPlural() { return Plural; } QString MysteryPart::genSipFZP(const QString & moduleid) { return genxFZP(moduleid, "mystery_part_sipFzpTemplate", MinSipPins, MaxSipPins, 1); } QString MysteryPart::genDipFZP(const QString & moduleid) { return genxFZP(moduleid, "mystery_part_dipFzpTemplate", MinDipPins, MaxDipPins, 2); } QString MysteryPart::genxFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int step) { QString spacingString; TextUtils::getPinsAndSpacing(moduleid, spacingString); QString result = PaletteItem::genFZP(moduleid, templateName, minPins, maxPins, step, false); result.replace(".percent.", "%"); result = result.arg(spacingString); return hackFzpHoleSize(result, moduleid); } QString MysteryPart::hackFzpHoleSize(const QString & fzp, const QString & moduleid) { int hsix = moduleid.lastIndexOf(HoleSizePrefix); if (hsix >= 0) { return PaletteItem::hackFzpHoleSize(fzp, moduleid, hsix); } return fzp; } QString MysteryPart::genModuleID(QMap & currPropsMap) { QString value = currPropsMap.value("layout"); bool single = value.contains("single", Qt::CaseInsensitive); QString pins = currPropsMap.value("pins"); QString spacing = currPropsMap.value("pin spacing", "300mil"); if (single) { return QString("mystery_part_sip_%1_100mil").arg(pins); } else { int p = pins.toInt(); if (p < 4) p = 4; if (p % 2 == 1) p--; return QString("mystery_part_dip_%1_%2").arg(p).arg(spacing); } } QString MysteryPart::makeSchematicSvg(const QString & expectedFileName) { bool sip = expectedFileName.contains("sip", Qt::CaseInsensitive); QStringList pieces = expectedFileName.split("_"); int pins = pieces.at(2).toInt(); QStringList labels; for (int i = 0; i < pins; i++) { labels << QString::number(i + 1); } if (expectedFileName.contains(PartFactory::OldSchematicPrefix)) { return obsoleteMakeSchematicSvg(labels, sip); } return makeSchematicSvg(labels, sip); } QString MysteryPart::makeSchematicSvg(const QStringList & labels, bool sip) { QDomDocument fakeDoc; QList lefts; for (int i = 0; i < labels.count(); i++) { QDomElement element = fakeDoc.createElement("contact"); element.setAttribute("connectorIndex", i); element.setAttribute("name", labels.at(i)); lefts.append(element); } QList empty; QStringList busNames; QString titleText = sip ? "IC" : "?"; QString svg = SchematicRectConstants::genSchematicDIP(empty, empty, lefts, empty, empty, busNames, titleText, false, false, SchematicRectConstants::simpleGetConnectorName); if (sip) return svg; // add the mystery part graphic QDomDocument doc; if (!doc.setContent(svg)) return svg; QRectF viewBox; double w, h; TextUtils::ensureViewBox(doc, GraphicsUtils::StandardFritzingDPI, viewBox, false, w, h, false); double newUnit = 1000 * SchematicRectConstants::NewUnit / 25.4; double rectStroke = 2 * 1000 * SchematicRectConstants::RectStrokeWidth / 25.4; QString circle = QString("\n"); circle += QString("?\n"); circle = circle .arg(viewBox.width() - rectStroke - (newUnit / 2)) .arg(rectStroke + (newUnit / 2)) .arg(newUnit / 2) .arg(rectStroke + (newUnit / 2) + (newUnit / 3)) // offset so the text appears in the center of the circle .arg(SchematicRectConstants::FontFamily) .arg(newUnit) ; QDomDocument temp; temp.setContent(QString("" + circle + "")); QDomElement root = doc.documentElement(); QDomElement schematic = TextUtils::findElementWithAttribute(root, "id", "schematic"); if (schematic.isNull()) return svg; QDomElement tempRoot = temp.documentElement(); schematic.appendChild(tempRoot); return doc.toString(1); } QString MysteryPart::obsoleteMakeSchematicSvg(const QStringList & labels, bool sip) { int increment = GraphicsUtils::StandardSchematicSeparationMils; // 7.5mm; int border = 30; double totalHeight = (labels.count() * increment) + increment + border; int textOffset = 50; int repeatTextOffset = 50; int fontSize = 255; int labelFontSize = 130; int defaultLabelWidth = 30; QString labelText = "?"; double textMax = defaultLabelWidth; QFont font("Droid Sans", labelFontSize * 72 / GraphicsUtils::StandardFritzingDPI, QFont::Normal); QFontMetricsF fm(font); for (int i = 0; i < labels.count(); i++) { double w = fm.width(labels.at(i)); if (w > textMax) textMax = w; } textMax = textMax * GraphicsUtils::StandardFritzingDPI / 72; int totalWidth = (5 * increment) + border; int innerWidth = 4 * increment; if (textMax > defaultLabelWidth) { totalWidth += (textMax - defaultLabelWidth); innerWidth += (textMax - defaultLabelWidth); } QString header("\n" "\n" "\n" "\n" "%6\n"); if (!sip) { header += "\n" "?\n"; } else { labelText = "IC"; fontSize = 235; textOffset = 0; } QString svg = header .arg(totalHeight / GraphicsUtils::StandardFritzingDPI) .arg(totalHeight) .arg(totalHeight - border) .arg((totalHeight / 2) + textOffset) .arg(fontSize) .arg(labelText) .arg(totalWidth / 1000.0) .arg(totalWidth) .arg(innerWidth) .arg(totalWidth - 200) .arg(increment + textMax + ((totalWidth - increment - textMax) / 2.0)) ; QString repeat("\n" "\n" "\n" "%5\n"); for (int i = 0; i < labels.count(); i++) { svg += repeat .arg(increment + (border / 2) + (i * increment)) .arg(increment + (i * increment)) .arg(i) .arg(increment + repeatTextOffset + (i * increment)) .arg(labels.at(i)) .arg(labelFontSize); } svg += "\n"; svg += "\n"; return svg; } QString MysteryPart::makeBreadboardSvg(const QString & expectedFileName) { if (expectedFileName.contains("_sip_")) return makeBreadboardSipSvg(expectedFileName); if (expectedFileName.contains("_dip_")) return makeBreadboardDipSvg(expectedFileName); return ""; } QString MysteryPart::makeBreadboardDipSvg(const QString & expectedFileName) { QString spacingString; int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString); double spacing = TextUtils::convertToInches(spacingString) * 100; int increment = 10; QString repeatT("\n" "\n"); QString repeatB("\n" "\n"); QString header("\n" "\n" "\n" ".percent.2\n" "\n" "\n" "?\n" "\n" "?\n" ".percent.3\n" "\n" "\n"); header = TextUtils::incrementTemplateString(header, 1, spacing - increment, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); header = header.arg(TextUtils::getViewBoxCoord(header, 3) / 100.0); if (spacing == 10) { header.replace("{{6.0}}", "8.0"); header.replace("{{7.5}}", "5.5"); header.replace("{{9.2}}", "7.2"); } else { header.replace("{{6.0}}", QString::number(6.0 + ((spacing - increment) / 2))); header.replace("{{7.5}}", "7.5"); header.replace("{{9.2}}", "9.2"); } header.replace(".percent.", "%"); header.replace("{", "["); header.replace("}", "]"); QString svg = TextUtils::incrementTemplateString(header, 1, increment * ((pins - 4) / 2), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); repeatB = TextUtils::incrementTemplateString(repeatB, 1, spacing - increment, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); repeatB.replace("{", "["); repeatB.replace("}", "]"); int userData[2]; userData[0] = pins; userData[1] = 1; QString repeatTs = TextUtils::incrementTemplateString(repeatT, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData); QString repeatBs = TextUtils::incrementTemplateString(repeatB, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100).arg(repeatTs).arg(repeatBs); } QString MysteryPart::makeBreadboardSipSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); if (pieces.count() != 6) return ""; int pins = pieces.at(2).toInt(); int increment = 10; QString header("\n" "\n" "\n" "\n" "\n" "?\n" "\n" "?\n" "%2\n" "\n" "\n"); QString svg = TextUtils::incrementTemplateString(header, 1, increment * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); QString repeat("\n" "\n"); QString repeats = TextUtils::incrementTemplateString(repeat, pins, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100).arg(repeats); } bool MysteryPart::changePinLabels(bool singleRow, bool sip) { Q_UNUSED(singleRow); if (m_viewID != ViewLayer::SchematicView) return true; bool hasLocal = false; QStringList labels = getPinLabels(hasLocal); if (labels.count() == 0) return true; QTransform transform = untransform(); QString svg = MysteryPart::makeSchematicSvg(labels, sip); QString chipLabel = modelPart()->localProp("chip label").toString(); if (!chipLabel.isEmpty()) { svg =TextUtils::replaceTextElement(svg, "label", chipLabel); } resetLayerKin(svg); retransform(transform); return true; } void MysteryPart::swapEntry(const QString & text) { FamilyPropertyComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; QString layout = m_propsMap.value("layout"); if (comboBox->prop().contains("layout", Qt::CaseInsensitive)) { layout = text; } if (layout.isEmpty()) { if (moduleID().contains("sip", Qt::CaseInsensitive)) { layout = "single"; } } if (layout.contains("single", Qt::CaseInsensitive)) { generateSwap(text, genModuleID, genSipFZP, makeBreadboardSvg, makeSchematicSvg, PinHeader::makePcbSvg); } else { generateSwap(text, genModuleID, genDipFZP, makeBreadboardSvg, makeSchematicSvg, makePcbDipSvg); } PaletteItem::swapEntry(text); } QString MysteryPart::makePcbDipSvg(const QString & expectedFileName) { QString spacingString; int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString); if (pins == 0) return ""; QString header("\n" "\n" "Fritzing footprint SVG\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n"); double outerBorder = 10; double silkSplitTop = 100; double offsetX = 60; double offsetY = 60; double spacing = TextUtils::convertToInches(spacingString) * GraphicsUtils::StandardFritzingDPI; double totalWidth = 120 + spacing; double totalHeight = (100 * pins / 2) + (outerBorder * 2); double center = totalWidth / 2; QString svg = header.arg(totalWidth / GraphicsUtils::StandardFritzingDPI).arg(totalHeight / GraphicsUtils::StandardFritzingDPI).arg(totalWidth).arg(totalHeight) .arg(totalHeight - outerBorder).arg(totalWidth - outerBorder) .arg(center - (silkSplitTop / 2)).arg(center + (silkSplitTop / 2)); QString circle("\n"); int y = offsetY; for (int i = 0; i < pins / 2; i++) { svg += circle.arg(offsetX).arg(y).arg(i); svg += circle.arg(totalWidth - offsetX).arg(y).arg(pins - 1 - i); y += 100; } svg += "\n"; svg += "\n"; int hsix = expectedFileName.indexOf(HoleSizePrefix); if (hsix >= 0) { return hackSvgHoleSizeAux(svg, expectedFileName); } return svg; } fritzing-app-0.9.3b/src/items/mysterypart.h000066400000000000000000000070061272363601300207710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef MYSTERYPART_H #define MYSTERYPART_H #include #include #include #include #include "paletteitem.h" class MysteryPart : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call MysteryPart(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~MysteryPart(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); QString getProperty(const QString & key); void setProp(const QString & prop, const QString & value); void setChipLabel(QString label, bool force); QString chipLabel(); const QString & title(); bool hasCustomSVG(); PluralType isPlural(); void addedToScene(bool temporary); bool changePinLabels(bool singleRow, bool sip); public slots: void chipLabelEntry(); void swapEntry(const QString & text); public: static QString genSipFZP(const QString & moduleid); static QString genDipFZP(const QString & moduleid); static QString genModuleID(QMap & currPropsMap); static QString makeSchematicSvg(const QString & expectedFileName); static QString makeSchematicSvg(const QStringList & labels, bool sip); static QString obsoleteMakeSchematicSvg(const QStringList & labels, bool sip); static QString makeBreadboardSvg(const QString & expectedFileName); static QString makeBreadboardSipSvg(const QString & expectedFileName); static QString makeBreadboardDipSvg(const QString & expectedFileName); static QString makePcbDipSvg(const QString & expectedFileName); protected: QString makeSvg(const QString & chipLabel, bool replace); QStringList collectValues(const QString & family, const QString & prop, QString & value); virtual bool isDIP(); virtual bool otherPropsChange(const QMap & propsMap); virtual const QStringList & spacings(); virtual QString retrieveSchematicSvg(QString & svg); protected: static QString hackFzpHoleSize(const QString & fzp, const QString & moduleid); static QString genxFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int step); protected: static int NoExcusePins; protected: QString m_chipLabel; QString m_title; }; #endif fritzing-app-0.9.3b/src/items/note.cpp000066400000000000000000000600611272363601300176660ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "note.h" #include "../debugdialog.h" #include "../sketch/infographicsview.h" #include "../model/modelpart.h" #include "../utils/resizehandle.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../fsvgrenderer.h" #include #include #include #include #include #include #include #include #include #include #include // TODO: // ** search for ModelPart:: and fix up // check which menu items don't apply // ** selection // ** delete // ** move // ** undo delete + text // ** resize // ** undo resize // anchor // ** undo change text // ** undo selection // ** undo move // ** layers and z order // ** hide and show layer // ** save and load // format: bold, italic, size (small normal large huge), color?, // undo format // heads-up controls // copy/paste // ** z-order manipulation // hover // ** multiple selection // ** icon in taskbar (why does it show up as text until you update it?) const int Note::emptyMinWidth = 40; const int Note::emptyMinHeight = 25; const int Note::initialMinWidth = 140; const int Note::initialMinHeight = 45; const int borderWidth = 7; const int TriangleOffset = 7; const double InactiveOpacity = 0.5; QString Note::initialTextString; QRegExp UrlTag(""); /////////////////////////////////////// void findA(QDomElement element, QList & aElements) { if (element.tagName().compare("a", Qt::CaseInsensitive) == 0) { aElements.append(element); return; } QDomElement c = element.firstChildElement(); while (!c.isNull()) { findA(c, aElements); c = c.nextSiblingElement(); } } QString addText(const QString & text, bool inUrl) { if (text.isEmpty()) return ""; return QString("%2\n") .arg(inUrl ? "#0000ff" : "#000000") .arg(TextUtils::convertExtendedChars(TextUtils::escapeAnd(text))) ; } /////////////////////////////////////// class NoteGraphicsTextItem : public QGraphicsTextItem { public: NoteGraphicsTextItem(QGraphicsItem * parent = NULL); protected: void focusInEvent(QFocusEvent *); void focusOutEvent(QFocusEvent *); }; NoteGraphicsTextItem::NoteGraphicsTextItem(QGraphicsItem * parent) : QGraphicsTextItem(parent) { const QTextFrameFormat format = document()->rootFrame()->frameFormat(); QTextFrameFormat altFormat(format); altFormat.setMargin(0); // so document never thinks a mouse click is a move event document()->rootFrame()->setFrameFormat(altFormat); } void NoteGraphicsTextItem::focusInEvent(QFocusEvent * event) { InfoGraphicsView * igv = InfoGraphicsView::getInfoGraphicsView(this); if (igv != NULL) { igv->setNoteFocus(this, true); } QApplication::instance()->installEventFilter((Note *) this->parentItem()); QGraphicsTextItem::focusInEvent(event); DebugDialog::debug("note focus in"); } void NoteGraphicsTextItem::focusOutEvent(QFocusEvent * event) { InfoGraphicsView * igv = InfoGraphicsView::getInfoGraphicsView(this); if (igv != NULL) { igv->setNoteFocus(this, false); } QApplication::instance()->removeEventFilter((Note *) this->parentItem()); QGraphicsTextItem::focusOutEvent(event); DebugDialog::debug("note focus out"); } ////////////////////////////////////////// LinkDialog::LinkDialog(QWidget *parent) : QDialog(parent) { this->setWindowTitle(QObject::tr("Edit link")); QVBoxLayout * vLayout = new QVBoxLayout(this); QGroupBox * formGroupBox = new QGroupBox(this); QFormLayout * formLayout = new QFormLayout(); m_urlEdit = new QLineEdit(this); m_urlEdit->setFixedHeight(25); m_urlEdit->setFixedWidth(200); formLayout->addRow(tr("url:"), m_urlEdit ); m_textEdit = new QLineEdit(this); m_textEdit->setFixedHeight(25); m_textEdit->setFixedWidth(200); formLayout->addRow(tr("text:"), m_textEdit ); formGroupBox->setLayout(formLayout); vLayout->addWidget(formGroupBox); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vLayout->addWidget(buttonBox); this->setLayout(vLayout); } LinkDialog::~LinkDialog() { } void LinkDialog::setText(const QString & text) { m_textEdit->setText(text); } void LinkDialog::setUrl(const QString & url) { m_urlEdit->setText(url); } QString LinkDialog::text() { return m_textEdit->text(); } QString LinkDialog::url() { return m_urlEdit->text(); } ///////////////////////////////////////////// Note::Note( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu* itemMenu) : ItemBase(modelPart, viewID, viewGeometry, id, itemMenu) { m_charsAdded = 0; if (initialTextString.isEmpty()) { initialTextString = tr("[write your note here]"); } m_inResize = NULL; this->setCursor(Qt::ArrowCursor); setFlag(QGraphicsItem::ItemIsSelectable, true); if (viewGeometry.rect().width() == 0 || viewGeometry.rect().height() == 0) { m_rect.setRect(0, 0, Note::initialMinWidth, Note::initialMinHeight); } else { m_rect.setRect(0, 0, viewGeometry.rect().width(), viewGeometry.rect().height()); } m_pen.setWidth(borderWidth); m_pen.setCosmetic(false); m_pen.setBrush(QColor(0xfa, 0xbc, 0x4f)); m_brush.setColor(QColor(0xff, 0xe9, 0xc8)); m_brush.setStyle(Qt::SolidPattern); setPos(m_viewGeometry.loc()); QPixmap pixmap(":/resources/images/icons/noteResizeGrip.png"); m_resizeGrip = new ResizeHandle(pixmap, Qt::SizeFDiagCursor, false, this); connect(m_resizeGrip, SIGNAL(mousePressSignal(QGraphicsSceneMouseEvent *, ResizeHandle *)), this, SLOT(handleMousePressSlot(QGraphicsSceneMouseEvent *, ResizeHandle *))); connect(m_resizeGrip, SIGNAL(mouseMoveSignal(QGraphicsSceneMouseEvent *, ResizeHandle *)), this, SLOT(handleMouseMoveSlot(QGraphicsSceneMouseEvent *, ResizeHandle *))); connect(m_resizeGrip, SIGNAL(mouseReleaseSignal(QGraphicsSceneMouseEvent *, ResizeHandle *)), this, SLOT(handleMouseReleaseSlot(QGraphicsSceneMouseEvent *, ResizeHandle *))); //connect(m_resizeGrip, SIGNAL(zoomChangedSignal(double)), this, SLOT(handleZoomChangedSlot(double))); m_graphicsTextItem = new NoteGraphicsTextItem(); QFont font("Droid Sans", 9, QFont::Normal); m_graphicsTextItem->setFont(font); m_graphicsTextItem->document()->setDefaultFont(font); m_graphicsTextItem->setParentItem(this); m_graphicsTextItem->setVisible(true); m_graphicsTextItem->setPlainText(initialTextString); m_graphicsTextItem->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); m_graphicsTextItem->setCursor(Qt::IBeamCursor); m_graphicsTextItem->setOpenExternalLinks(true); connectSlots(); positionGrip(); setAcceptHoverEvents(true); } void Note::saveGeometry() { m_viewGeometry.setRect(boundingRect()); m_viewGeometry.setLoc(this->pos()); m_viewGeometry.setSelected(this->isSelected()); m_viewGeometry.setZ(this->zValue()); } bool Note::itemMoved() { return (this->pos() != m_viewGeometry.loc()); } void Note::saveInstanceLocation(QXmlStreamWriter & streamWriter) { QRectF rect = m_viewGeometry.rect(); QPointF loc = m_viewGeometry.loc(); streamWriter.writeAttribute("x", QString::number(loc.x())); streamWriter.writeAttribute("y", QString::number(loc.y())); streamWriter.writeAttribute("width", QString::number(rect.width())); streamWriter.writeAttribute("height", QString::number(rect.height())); } void Note::moveItem(ViewGeometry & viewGeometry) { this->setPos(viewGeometry.loc()); } void Note::findConnectorsUnder() { } void Note::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget); if (m_hidden) return; if (m_inactive) { painter->save(); painter->setOpacity(InactiveOpacity); } painter->setPen(Qt::NoPen); painter->setBrush(m_brush); QPolygonF poly; poly.append(m_rect.topLeft()); poly.append(m_rect.topRight() + QPointF(-TriangleOffset, 0)); poly.append(m_rect.topRight() + QPointF(0, TriangleOffset)); poly.append(m_rect.bottomRight()); poly.append(m_rect.bottomLeft()); painter->drawPolygon(poly); painter->setBrush(m_pen.brush()); poly.clear(); poly.append(m_rect.topRight() + QPointF(-TriangleOffset, 0)); poly.append(m_rect.topRight() + QPointF(0, TriangleOffset)); poly.append(m_rect.topRight() + QPointF(-TriangleOffset, TriangleOffset)); painter->drawPolygon(poly); painter->setPen(m_pen); poly.clear(); poly.append(QPointF(0, m_rect.bottom() - borderWidth / 2.0)); poly.append(QPointF(m_rect.right(), m_rect.bottom() - borderWidth / 2.0)); painter->drawPolygon(poly); if (option->state & QStyle::State_Selected) { GraphicsUtils::qt_graphicsItem_highlightSelected(painter, option, boundingRect(), QPainterPath()); } if (m_inactive) { painter->restore(); } } QRectF Note::boundingRect() const { return m_rect; } QPainterPath Note::shape() const { QPainterPath path; path.addRect(boundingRect()); return path; } void Note::positionGrip() { QSizeF gripSize = m_resizeGrip->boundingRect().size(); QSizeF sz = this->boundingRect().size(); QPointF p(sz.width() - gripSize.width(), sz.height() - gripSize.height()); m_resizeGrip->setPos(p); m_graphicsTextItem->setPos(TriangleOffset / 2, TriangleOffset / 2); m_graphicsTextItem->setTextWidth(sz.width() - TriangleOffset); } void Note::mousePressEvent(QGraphicsSceneMouseEvent * event) { InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL && infographics->spaceBarIsPressed()) { m_spaceBarWasPressed = true; event->ignore(); return; } m_spaceBarWasPressed = false; m_inResize = NULL; ItemBase::mousePressEvent(event); } void Note::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { if (m_spaceBarWasPressed) { event->ignore(); return; } ItemBase::mouseMoveEvent(event); } void Note::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { if (m_spaceBarWasPressed) { event->ignore(); return; } ItemBase::mouseReleaseEvent(event); } void Note::contentsChangeSlot(int position, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved); m_charsAdded = charsAdded; m_charsPosition = position; } void Note::forceFormat(int position, int charsAdded) { disconnectSlots(); QTextCursor textCursor = m_graphicsTextItem->textCursor(); QTextCharFormat f; QFont font("Droid Sans", 9, QFont::Normal); f.setFont(font); f.setFontFamily("Droid Sans"); f.setFontPointSize(9); int cc = m_graphicsTextItem->document()->characterCount(); textCursor.setPosition(position, QTextCursor::MoveAnchor); if (position + charsAdded >= cc) { charsAdded--; } textCursor.setPosition(position + charsAdded, QTextCursor::KeepAnchor); //textCursor.setCharFormat(f); textCursor.mergeCharFormat(f); //DebugDialog::debug(QString("setting font tc:%1,%2 params:%3,%4") //.arg(textCursor.anchor()).arg(textCursor.position()) //.arg(position).arg(position + charsAdded)); /* textCursor = m_graphicsTextItem->textCursor(); for (int i = 0; i < charsAdded; i++) { textCursor.setPosition(position + i, QTextCursor::MoveAnchor); f = textCursor.charFormat(); DebugDialog::debug(QString("1format %1 %2 %3").arg(f.fontPointSize()).arg(f.fontFamily()).arg(f.fontWeight())); //f.setFont(font); //f.setFontPointSize(9); //f.setFontWeight(QFont::Normal); //textCursor.setCharFormat(f); //QTextCharFormat g = textCursor.charFormat(); //DebugDialog::debug(QString("2format %1 %2 %3").arg(g.fontPointSize()).arg(g.fontFamily()).arg(g.fontWeight())); } */ connectSlots(); } void Note::contentsChangedSlot() { //DebugDialog::debug(QString("contents changed ") + m_graphicsTextItem->document()->toPlainText()); if (m_charsAdded > 0) { forceFormat(m_charsPosition, m_charsAdded); } InfoGraphicsView *infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { QString oldText; if (m_modelPart) { oldText = m_modelPart->instanceText(); } QSizeF oldSize = m_rect.size(); QSizeF newSize = oldSize; checkSize(newSize); infoGraphicsView->noteChanged(this, oldText, m_graphicsTextItem->document()->toHtml(), oldSize, newSize); } if (m_modelPart) { m_modelPart->setInstanceText(m_graphicsTextItem->document()->toHtml()); } } void Note::checkSize(QSizeF & newSize) { QSizeF gripSize = m_resizeGrip->boundingRect().size(); QSizeF size = m_graphicsTextItem->document()->size(); if (size.height() + gripSize.height() + gripSize.height() > m_rect.height()) { prepareGeometryChange(); m_rect.setHeight(size.height() + gripSize.height() + gripSize.height()); newSize.setHeight(m_rect.height()); positionGrip(); this->update(); } } void Note::disconnectSlots() { disconnect(m_graphicsTextItem->document(), SIGNAL(contentsChanged()), this, SLOT(contentsChangedSlot())); disconnect(m_graphicsTextItem->document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(contentsChangeSlot(int, int, int))); } void Note::connectSlots() { connect(m_graphicsTextItem->document(), SIGNAL(contentsChanged()), this, SLOT(contentsChangedSlot()), Qt::DirectConnection); connect(m_graphicsTextItem->document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(contentsChangeSlot(int, int, int)), Qt::DirectConnection); } void Note::setText(const QString & text, bool check) { // disconnect the signal so it doesn't fire recursively disconnectSlots(); QString oldText = text; m_graphicsTextItem->document()->setHtml(text); connectSlots(); if (check) { QSizeF newSize; checkSize(newSize); forceFormat(0, m_graphicsTextItem->document()->characterCount()); } } QString Note::text() { return m_graphicsTextItem->document()->toHtml(); } void Note::setSize(const QSizeF & size) { prepareGeometryChange(); m_rect.setWidth(size.width()); m_rect.setHeight(size.height()); positionGrip(); } void Note::setHidden(bool hide) { ItemBase::setHidden(hide); m_graphicsTextItem->setVisible(!hide); m_resizeGrip->setVisible(!hide); } void Note::setInactive(bool inactivate) { ItemBase::setInactive(inactivate); } bool Note::eventFilter(QObject * object, QEvent * event) { if (event->type() == QEvent::Shortcut || event->type() == QEvent::ShortcutOverride) { if (!object->inherits("QGraphicsView")) { event->accept(); return true; } } if (event->type() == QEvent::KeyPress) { QKeyEvent * kevent = static_cast(event); if (kevent->matches(QKeySequence::Bold)) { QTextCursor textCursor = m_graphicsTextItem->textCursor(); QTextCharFormat cf = textCursor.charFormat(); bool isBold = cf.fontWeight() == QFont::Bold; QTextCharFormat textCharFormat; textCharFormat.setFontWeight(isBold ? QFont::Normal : QFont::Bold); textCursor.mergeCharFormat(textCharFormat); event->accept(); return true; } if (kevent->matches(QKeySequence::Italic)) { QTextCursor textCursor = m_graphicsTextItem->textCursor(); QTextCharFormat cf = textCursor.charFormat(); QTextCharFormat textCharFormat; textCharFormat.setFontItalic(!cf.fontItalic()); textCursor.mergeCharFormat(textCharFormat); event->accept(); return true; } if ((kevent->key() == Qt::Key_L) && (kevent->modifiers() & Qt::ControlModifier)) { QTimer::singleShot(75, this, SLOT(linkDialog())); event->accept(); return true; } } return false; } void Note::linkDialog() { QTextCursor textCursor = m_graphicsTextItem->textCursor(); bool gotUrl = false; if (textCursor.anchor() == textCursor.selectionStart()) { // the selection returns empty since we're between characters // so select one character forward or one character backward // to see whether we're in a url int wasAnchor = textCursor.anchor(); bool atEnd = textCursor.atEnd(); bool atStart = textCursor.atStart(); if (!atStart) { textCursor.setPosition(wasAnchor - 1, QTextCursor::KeepAnchor); QString html = textCursor.selection().toHtml(); if (UrlTag.indexIn(html) >= 0) { gotUrl = true; } } if (!gotUrl && !atEnd) { textCursor.setPosition(wasAnchor + 1, QTextCursor::KeepAnchor); QString html = textCursor.selection().toHtml(); if (UrlTag.indexIn(html) >= 0) { gotUrl = true; } } textCursor.setPosition(wasAnchor, QTextCursor::MoveAnchor); } else { QString html = textCursor.selection().toHtml(); DebugDialog::debug(html); if (UrlTag.indexIn(html) >= 0) { gotUrl = true; } } LinkDialog ld; QString originalText; QString originalUrl; if (gotUrl) { originalUrl = UrlTag.cap(1); ld.setUrl(originalUrl); QString html = m_graphicsTextItem->toHtml(); // assumes html is in xml form QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(html, &errorStr, &errorLine, &errorColumn)) { return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { return; } if (root.tagName() != "html") { return; } DebugDialog::debug(html); QList aElements; findA(root, aElements); foreach (QDomElement a, aElements) { // TODO: if multiple hrefs point to the same url this will only find the first one QString href = a.attribute("href"); if (href.isEmpty()) { href = a.attribute("HREF"); } if (href.compare(originalUrl) == 0) { QString text; if (TextUtils::findText(a, text)) { ld.setText(text); break; } else { return; } } } } int result = ld.exec(); if (result == QDialog::Accepted) { if (gotUrl) { int from = 0; while (true) { QTextCursor cursor = m_graphicsTextItem->document()->find(originalText, from); if (cursor.isNull()) { // TODO: tell the user return; } QString html = cursor.selection().toHtml(); if (html.contains(originalUrl)) { cursor.insertHtml(QString("%2").arg(ld.url()).arg(ld.text())); break; } from = cursor.selectionEnd(); } } else { textCursor.insertHtml(QString("%2").arg(ld.url()).arg(ld.text())); } } } void Note::handleZoomChangedSlot(double scale) { Q_UNUSED(scale); positionGrip(); } void Note::handleMousePressSlot(QGraphicsSceneMouseEvent * event, ResizeHandle * resizeHandle) { if (m_spaceBarWasPressed) return; saveGeometry(); QSizeF sz = this->boundingRect().size(); resizeHandle->setResizeOffset(this->pos() + QPointF(sz.width(), sz.height()) - event->scenePos()); m_inResize = resizeHandle; } void Note::handleMouseMoveSlot(QGraphicsSceneMouseEvent * event, ResizeHandle * resizeHandle) { Q_UNUSED(resizeHandle); if (!m_inResize) return; double minWidth = emptyMinWidth; double minHeight = emptyMinHeight; QSizeF gripSize = m_resizeGrip->boundingRect().size(); QSizeF minSize = m_graphicsTextItem->document()->size() + gripSize + gripSize; if (minSize.height() > minHeight) minHeight = minSize.height(); QRectF rect = boundingRect(); rect.moveTopLeft(this->pos()); double oldX1 = rect.x(); double oldY1 = rect.y(); double newX = event->scenePos().x() + m_inResize->resizeOffset().x(); double newY = event->scenePos().y() + m_inResize->resizeOffset().y(); QRectF newR; if (newX - oldX1 < minWidth) { newX = oldX1 + minWidth; } if (newY - oldY1 < minHeight) { newY = oldY1 + minHeight; } newR.setRect(0, 0, newX - oldX1, newY - oldY1); prepareGeometryChange(); m_rect = newR; positionGrip(); } void Note::handleMouseReleaseSlot(QGraphicsSceneMouseEvent * event, ResizeHandle * resizeHandle) { Q_UNUSED(resizeHandle); Q_UNUSED(event); if (!m_inResize) return; m_inResize = NULL; InfoGraphicsView *infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->noteSizeChanged(this, m_viewGeometry.rect().size(), m_rect.size()); } } bool Note::hasPartLabel() { return false; } bool Note::stickyEnabled() { return false; } bool Note::hasPartNumberProperty() { return false; } bool Note::rotationAllowed() { return false; } bool Note::rotation45Allowed() { return false; } QString Note::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { Q_UNUSED(svgHash); factor = 1; switch (viewLayerID) { case ViewLayer::BreadboardNote: if (viewID() != ViewLayer::BreadboardView) return ""; break; case ViewLayer::PcbNote: if (viewID() != ViewLayer::PCBView) return ""; break; case ViewLayer::SchematicNote: if (viewID() != ViewLayer::SchematicView) return ""; break; default: return ""; } QString svg = ""; QString penColor = blackOnly ? "#000000" : m_pen.color().name(); double penWidth = m_pen.widthF() * dpi / GraphicsUtils::SVGDPI; QString brushColor = blackOnly ? "none" : m_brush.color().name(); svg += QString("") .arg(penWidth / 2) .arg(penWidth / 2) .arg((m_rect.width() * dpi / GraphicsUtils::SVGDPI) - penWidth) .arg((m_rect.height() * dpi / GraphicsUtils::SVGDPI) - penWidth) .arg(brushColor) .arg(penColor) .arg(penWidth) ; QTextCursor textCursor = m_graphicsTextItem->textCursor(); QSizeF gripSize = m_resizeGrip->boundingRect().size(); double docLeft = gripSize.width(); double docTop = gripSize.height(); for (QTextBlock block = m_graphicsTextItem->document()->begin(); block.isValid(); block = block.next()) { QTextLayout * layout = block.layout(); double left = block.blockFormat().leftMargin() + docLeft + layout->position().x(); double top = block.blockFormat().topMargin() + docTop + layout->position().y(); for (int i = 0; i < layout->lineCount(); i++) { QTextLine line = layout->lineAt(i); QRectF r = line.rect(); int start = line.textStart(); int count = line.textLength(); QString soFar; svg += QString("\n") .arg((left + r.left()) * dpi / GraphicsUtils::SVGDPI) .arg((top + r.top() + line.ascent()) * dpi / GraphicsUtils::SVGDPI) .arg("Droid Sans") .arg(line.ascent() * dpi / GraphicsUtils::SVGDPI) ; bool inUrl = false; for (int i = 0; i < count; i++) { textCursor.setPosition(i + start + block.position(), QTextCursor::MoveAnchor); textCursor.setPosition(i + start + block.position() + 1, QTextCursor::KeepAnchor); QString html = textCursor.selection().toHtml(); if (UrlTag.indexIn(html) >= 0) { if (inUrl) { soFar += block.text().mid(start + i, 1); continue; } svg += addText(soFar, false); soFar = block.text().mid(start + i, 1); inUrl = true; } else { if (!inUrl) { soFar += block.text().mid(start + i, 1); continue; } svg += addText(soFar, !blackOnly); soFar = block.text().mid(start + i, 1); inUrl = false; } } svg += addText(soFar, inUrl && !blackOnly); svg += ""; } } svg += ""; return svg; } ViewLayer::ViewID Note::useViewIDForPixmap(ViewLayer::ViewID, bool) { return ViewLayer::IconView; } void Note::addedToScene(bool temporary) { positionGrip(); return ItemBase::addedToScene(temporary); } fritzing-app-0.9.3b/src/items/note.h000066400000000000000000000074221272363601300173350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef NOTE_H #define NOTE_H #include #include #include #include #include #include #include #include #include #include #include "../items/itembase.h" class Note : public ItemBase { Q_OBJECT public: Note(class ModelPart*, ViewLayer::ViewID, const ViewGeometry &, long id, QMenu * itemMenu); void saveGeometry(); bool itemMoved(); void saveInstanceLocation(QXmlStreamWriter &); void moveItem(ViewGeometry &); void findConnectorsUnder(); void setText(const QString & text, bool checkSize); QString text(); void setSize(const QSizeF & size); void setHidden(bool hide); void setInactive(bool inactivate); bool hasPartLabel(); bool stickyEnabled(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); QPainterPath shape() const; bool hasPartNumberProperty(); bool rotationAllowed(); bool rotation45Allowed(); void addedToScene(bool temporary); protected: QRectF boundingRect() const; void positionGrip(); void mousePressEvent ( QGraphicsSceneMouseEvent * event ); void mouseMoveEvent ( QGraphicsSceneMouseEvent * event ); void mouseReleaseEvent ( QGraphicsSceneMouseEvent * event ); bool eventFilter(QObject * object, QEvent * event); void checkSize(QSizeF & newSize); void connectSlots(); void disconnectSlots(); void forceFormat(int position, int charsAdded); QString retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); protected slots: void contentsChangeSlot(int position, int charsAdded, int charsRemoved); void contentsChangedSlot(); void linkDialog(); void handleZoomChangedSlot(double scale); void handleMousePressSlot(QGraphicsSceneMouseEvent * event, class ResizeHandle * resizeHandle); void handleMouseMoveSlot(QGraphicsSceneMouseEvent * event, class ResizeHandle * resizeHandle); void handleMouseReleaseSlot(QGraphicsSceneMouseEvent * event, class ResizeHandle * resizeHandle); public: static const int emptyMinWidth; static const int emptyMinHeight; static const int initialMinWidth; static const int initialMinHeight; static QString initialTextString; protected: QRectF m_rect; QPen m_pen; QBrush m_brush; class ResizeHandle * m_resizeGrip; class ResizeHandle * m_inResize; QGraphicsTextItem * m_graphicsTextItem; int m_charsAdded; int m_charsPosition; }; class LinkDialog : public QDialog { Q_OBJECT public: LinkDialog(QWidget *parent = 0); ~LinkDialog(); void setUrl(const QString &); void setText(const QString &); QString text(); QString url(); protected: QLineEdit * m_urlEdit; QLineEdit * m_textEdit; }; #endif fritzing-app-0.9.3b/src/items/pad.cpp000066400000000000000000000306671272363601300174760ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ // TODO: // flip for one-sided board #include "pad.h" #include "../utils/graphicsutils.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "moduleidnames.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static QString PadTemplate = ""; static double OriginalWidth = 28; static double OriginalHeight = 32; static double TheOffset = 4; Pad::Pad( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : ResizableBoard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_decimalsAfter = 2; m_copperBlocker = false; } Pad::~Pad() { } QString Pad::makeLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { Q_UNUSED(milsW); Q_UNUSED(milsH); switch (viewLayerID) { case ViewLayer::Copper0: case ViewLayer::Copper1: break; default: return ""; } double wpx = mmW > 0 ? GraphicsUtils::mm2pixels(mmW) : OriginalWidth; double hpx = mmH > 0 ? GraphicsUtils::mm2pixels(mmH) : OriginalHeight; QString connectAt = m_modelPart->localProp("connect").toString(); QRectF terminal; double minW = qMin((double) 1.0, wpx / 3); double minH = qMin((double) 1.0, hpx / 3); if (connectAt.compare("center", Qt::CaseInsensitive) == 0) { terminal.setRect(2, 2, wpx, hpx); } else if (connectAt.compare("north", Qt::CaseInsensitive) == 0) { terminal.setRect(2, 2, wpx, minH); } else if (connectAt.compare("south", Qt::CaseInsensitive) == 0) { terminal.setRect(2, 2 + hpx - minH, wpx, minH); } else if (connectAt.compare("east", Qt::CaseInsensitive) == 0) { terminal.setRect(2 + wpx - minW, 2, minW, hpx); } else if (connectAt.compare("west", Qt::CaseInsensitive) == 0) { terminal.setRect(2, 2, minW, hpx); } QString blockerColor = (viewLayerID == ViewLayer::Copper0) ? "#A26A00" : "#aF8B33"; QString copperColor = (viewLayerID == ViewLayer::Copper0) ? ViewLayer::Copper0Color : ViewLayer::Copper1Color; QString svg = QString("\n" "\n" "\n" ) .arg(wpx + TheOffset) .arg(hpx + TheOffset) .arg(wpx) .arg(hpx) .arg(ViewLayer::viewLayerXmlNameFromID(viewLayerID)) .arg(copperBlocker() ? blockerColor : copperColor) .arg(copperBlocker() ? 0.0 : 1.0) .arg(copperBlocker() ? "zzz" : "connector0") .arg(copperBlocker() ? blockerColor : "none") .arg(copperBlocker() ? TheOffset : 0) ; if (copperBlocker()) { svg += QString("\n") .arg(TheOffset) .arg(TheOffset) .arg(wpx) .arg(hpx) .arg(blockerColor) ; svg += QString("\n") .arg(wpx) .arg(TheOffset) .arg(TheOffset) .arg(hpx) .arg(blockerColor) ; } else { svg += QString("\n") .arg(terminal.left()) .arg(terminal.top()) .arg(terminal.width()) .arg(terminal.height()) ; } svg += "\n"; //DebugDialog::debug("pad svg: " + svg); return svg; } QString Pad::makeNextLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { Q_UNUSED(mmW); Q_UNUSED(mmH); Q_UNUSED(milsW); Q_UNUSED(milsH); Q_UNUSED(viewLayerID); return ""; } QString Pad::makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH) { return makeLayerSvg(moduleID().compare(ModuleIDNames::Copper0PadModuleIDName) == 0 ? ViewLayer::Copper0 : ViewLayer::Copper1, mmW, mmH, milsW, milsH); } ViewLayer::ViewID Pad::theViewID() { return ViewLayer::PCBView; } double Pad::minWidth() { return 1; } double Pad::minHeight() { return 1; } QString Pad::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { return ResizableBoard::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QStringList Pad::collectValues(const QString & family, const QString & prop, QString & value) { QStringList values = ResizableBoard::collectValues(family, prop, value); QStringList newValues; if (prop.compare("layer", Qt::CaseInsensitive) == 0) { foreach (QString xmlName, values) { newValues << Board::convertFromXmlName(xmlName); } value = Board::convertFromXmlName(value); return newValues; } return values; } bool Pad::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("shape", Qt::CaseInsensitive) == 0) { returnWidget = setUpDimEntry(false, false, false, returnWidget); returnWidget->setEnabled(swappingEnabled); returnProp = tr("shape"); return true; } if (!copperBlocker()) { if (prop.compare("connect to", Qt::CaseInsensitive) == 0) { QComboBox * comboBox = new QComboBox(); comboBox->setObjectName("infoViewComboBox"); comboBox->setEditable(false); comboBox->setEnabled(swappingEnabled); comboBox->addItem(tr("center"), "center"); comboBox->addItem(tr("north"), "north"); comboBox->addItem(tr("east"), "east"); comboBox->addItem(tr("south"), "south"); comboBox->addItem(tr("west"), "west"); QString connectAt = m_modelPart->localProp("connect").toString(); for (int i = 0; i < comboBox->count(); i++) { if (comboBox->itemData(i).toString().compare(connectAt) == 0) { comboBox->setCurrentIndex(i); break; } } connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(terminalPointEntry(const QString &))); returnWidget = comboBox; returnProp = tr("connect to"); return true; } } bool result = PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); if (prop.compare("layer") == 0 && returnWidget != NULL) { bool disabled = true; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView && infoGraphicsView->boardLayers() == 2) disabled = false; returnWidget->setDisabled(disabled); } return result; } void Pad::setProp(const QString & prop, const QString & value) { if (prop.compare("connect to", Qt::CaseInsensitive) == 0) { modelPart()->setLocalProp("connect", value); resizeMMAux(m_modelPart->localProp("width").toDouble(), m_modelPart->localProp("height").toDouble()); return; } ResizableBoard::setProp(prop, value); } bool Pad::canEditPart() { return false; } bool Pad::hasPartLabel() { return true; } bool Pad::stickyEnabled() { return true; } ItemBase::PluralType Pad::isPlural() { return Plural; } bool Pad::rotationAllowed() { return true; } bool Pad::rotation45Allowed() { return true; } bool Pad::freeRotationAllowed(Qt::KeyboardModifiers modifiers) { if ((modifiers & altOrMetaModifier()) == 0) return false; if (!isSelected()) return false; return true; } bool Pad::freeRotationAllowed() { return true; } bool Pad::hasPartNumberProperty() { return false; } void Pad::setInitialSize() { double w = m_modelPart->localProp("width").toDouble(); if (w == 0) { // set the size so the infoGraphicsView will display the size as you drag modelPart()->setLocalProp("width", GraphicsUtils::pixels2mm(OriginalWidth, GraphicsUtils::SVGDPI)); modelPart()->setLocalProp("height", GraphicsUtils::pixels2mm(OriginalHeight, GraphicsUtils::SVGDPI)); modelPart()->setLocalProp("connect", "center"); } } void Pad::resizeMMAux(double mmW, double mmH) { ResizableBoard::resizeMMAux(mmW, mmH); resetConnectors(NULL, NULL); } void Pad::addedToScene(bool temporary) { if (this->scene()) { setInitialSize(); resizeMMAux(m_modelPart->localProp("width").toDouble(), m_modelPart->localProp("height").toDouble()); } return PaletteItem::addedToScene(temporary); } void Pad::terminalPointEntry(const QString &) { QComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; QString value = comboBox->itemData(comboBox->currentIndex()).toString(); QString connectAt = m_modelPart->localProp("connect").toString(); if (connectAt.compare(value) == 0) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "connect to", tr("connect to"), connectAt, value, true); } } ResizableBoard::Corner Pad::findCorner(QPointF scenePos, Qt::KeyboardModifiers modifiers) { //return ResizableBoard::NO_CORNER; ResizableBoard::Corner corner = ResizableBoard::findCorner(scenePos, modifiers); if (corner == ResizableBoard::NO_CORNER) return corner; if (modifiers & altOrMetaModifier()) { // free rotate setCursor(*CursorMaster::RotateCursor); return ResizableBoard::NO_CORNER; } return corner; } void Pad::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { PaletteItem::paintHover(painter, option, widget); } bool Pad::copperBlocker() { return m_copperBlocker; } ////////////////////////////////////////// CopperBlocker::CopperBlocker( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Pad(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_copperBlocker = true; } CopperBlocker::~CopperBlocker() { } bool CopperBlocker::hasPartLabel() { return false; } QPainterPath CopperBlocker::hoverShape() const { if (m_viewID != ViewLayer::PCBView) { return PaletteItem::hoverShape(); } QRectF r = boundingRect(); QPainterPath path; double half = TheOffset / 2; path.moveTo(half, half); path.lineTo(r.width() - half, half); path.lineTo(r.width() - half, r.height() - half); path.lineTo(half, r.height() - half); path.closeSubpath(); path.moveTo(0, 0); path.lineTo(r.width(), r.height()); path.moveTo(r.width(), 0); path.lineTo(0, r.height()); path.moveTo(0, 0); QPen pen; pen.setCapStyle(Qt::RoundCap); return GraphicsUtils::shapeFromPath(path, pen, TheOffset, false); } QPainterPath CopperBlocker::shape() const { return hoverShape(); } QString CopperBlocker::makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH) { return makeLayerSvg(moduleID().compare(ModuleIDNames::Copper1BlockerModuleIDName) == 0 ? ViewLayer::Copper1 : ViewLayer::Copper0, mmW, mmH, milsW, milsH); }fritzing-app-0.9.3b/src/items/pad.h000066400000000000000000000067541272363601300171430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef PAD_H #define PAD_H #include #include #include #include #include #include #include #include "resizableboard.h" class Pad : public ResizableBoard { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Pad(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Pad(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool canEditPart(); void setProp(const QString & prop, const QString & value); bool hasPartLabel(); bool stickyEnabled(); PluralType isPlural(); bool rotationAllowed(); bool rotation45Allowed(); bool freeRotationAllowed(Qt::KeyboardModifiers); bool freeRotationAllowed(); bool hasPartNumberProperty(); void setInitialSize(); void addedToScene(bool temporary); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool copperBlocker(); protected slots: void terminalPointEntry(const QString &); protected: double minWidth(); double minHeight(); ViewLayer::ViewID theViewID(); QString makeLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH); QString makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH); QString makeNextLayerSvg(ViewLayer::ViewLayerID, double mmW, double mmH, double milsW, double milsH); void resizeMMAux(double w, double h); ResizableBoard::Corner findCorner(QPointF, Qt::KeyboardModifiers); QStringList collectValues(const QString & family, const QString & prop, QString & value); protected: bool m_copperBlocker; }; class CopperBlocker : public Pad { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call CopperBlocker(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~CopperBlocker(); bool hasPartLabel(); QPainterPath shape() const; QPainterPath hoverShape() const; protected: QString makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH); }; #endif fritzing-app-0.9.3b/src/items/paletteitem.cpp000066400000000000000000001453331272363601300212440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include "paletteitem.h" #include "../debugdialog.h" #include "../viewgeometry.h" #include "../sketch/infographicsview.h" #include "layerkinpaletteitem.h" #include "../fsvgrenderer.h" #include "partlabel.h" #include "partfactory.h" #include "../commands.h" #include "../connectors/connectoritem.h" #include "../connectors/connector.h" #include "../connectors/svgidlayer.h" #include "../layerattributes.h" #include "../dialogs/pinlabeldialog.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../utils/familypropertycombobox.h" #include "../svg/svgfilesplitter.h" #include "wire.h" #include #include #include #include #include #include #include #include #include #include #include #include #include ///////////////////////////////////////////////// QString HoleSettings::currentUnits() { if (mmRadioButton->isChecked()) return QObject::tr("mm"); return QObject::tr("in"); } QString HoleSettings::holeSize() { return QString("%1,%2").arg(holeDiameter).arg(ringThickness); } ///////////////////////////////////////////////// static QRegExp LabelFinder("id=['|\"]label['|\"]"); static bool ByIDParseSuccessful = true; static QRegExp ConnectorFinder("connector\\d+pin"); const QString PaletteItem::HoleSizePrefix("_hs_"); int findNumber(const QString & string) { int ix = string.indexOf(IntegerFinder); if (ix < 0) { return -1; } int result = IntegerFinder.cap(0).toInt(); int length = IntegerFinder.cap(0).length(); int jx = string.lastIndexOf(IntegerFinder); if (jx >= ix + length) { return -1; } return result; } bool byID(Connector * c1, Connector * c2) { int i1 = findNumber(c1->connectorSharedID()); if (i1 < 0) { ByIDParseSuccessful = false; return true; } int i2 = findNumber(c2->connectorSharedID()); if (i2 < 0) { ByIDParseSuccessful = false; return true; } if (i2 == i1 && c1 != c2) { // should not be two connectors with the same number ByIDParseSuccessful = false; return true; } return i1 <= i2; } ///////////////////////////////////////////////// PaletteItem::PaletteItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItemBase(modelPart, viewID, viewGeometry, id, itemMenu) { m_flipCount = 0; if(doLabel) { m_partLabel = new PartLabel(this, NULL); m_partLabel->setVisible(false); } else { m_partLabel = NULL; } } PaletteItem::~PaletteItem() { if (m_partLabel) { delete m_partLabel; } } bool PaletteItem::renderImage(ModelPart * modelPart, ViewLayer::ViewID viewID, const LayerHash & viewLayers, ViewLayer::ViewLayerID viewLayerID, bool doConnectors, QString & error) { LayerAttributes layerAttributes; initLayerAttributes(layerAttributes, viewID, viewLayerID, viewLayerPlacement(), doConnectors, true); bool result = setUpImage(modelPart, viewLayers, layerAttributes); error = layerAttributes.error; m_syncMoved = this->pos(); return result; } void PaletteItem::loadLayerKin(const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (m_modelPart == NULL) return; ModelPartShared * modelPartShared = m_modelPart->modelPartShared(); if (modelPartShared == NULL) return; qint64 id = m_id + 1; ViewGeometry viewGeometry = m_viewGeometry; viewGeometry.setZ(-1); foreach (ViewLayer::ViewLayerID viewLayerID, viewLayers.keys()) { if (viewLayerID == m_viewLayerID) continue; if (!m_modelPart->hasViewFor(m_viewID, viewLayerID)) continue; if (m_modelPart->itemType() == ModelPart::CopperFill) { if (viewLayerPlacement == ViewLayer::NewTop) { if (ViewLayer::bottomLayers().contains(viewLayerID)) continue; } else { if (ViewLayer::topLayers().contains(viewLayerID)) continue; } } else if (m_modelPart->flippedSMD()) { if (viewLayerPlacement == ViewLayer::NewTop) { if (ViewLayer::bottomLayers().contains(viewLayerID)) continue; } else { if (ViewLayer::topLayers().contains(viewLayerID)) continue; } } else if (m_modelPart->itemType() == ModelPart::Part) { // through hole part if (ViewLayer::silkLayers().contains(viewLayerID)) { if (viewLayerPlacement == ViewLayer::NewTop) { if (ViewLayer::bottomLayers().contains(viewLayerID)) continue; } else { if (ViewLayer::topLayers().contains(viewLayerID)) continue; } } } makeOneKin(id, viewLayerID, viewLayerPlacement, viewGeometry, viewLayers); } } void PaletteItem::makeOneKin(qint64 & id, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, const LayerHash & viewLayers) { LayerAttributes layerAttributes; initLayerAttributes(layerAttributes, m_viewID, viewLayerID, viewLayerPlacement, true, true); LayerKinPaletteItem * lkpi = newLayerKinPaletteItem(this, m_modelPart, viewGeometry, id, m_itemMenu, viewLayers, layerAttributes); if (lkpi->ok()) { DebugDialog::debug(QString("adding layer kin %1 %2 %3 %4") .arg(id).arg(m_viewID).arg(viewLayerID) .arg((long) lkpi, 0, 16) ); addLayerKin(lkpi); id++; } else { delete lkpi; } } void PaletteItem::addLayerKin(LayerKinPaletteItem * lkpi) { m_layerKin.append(lkpi); } void PaletteItem::removeLayerKin() { // assumes paletteitem is still in scene for (int i = 0; i < m_layerKin.size(); i++) { //DebugDialog::debug(QString("removing kin %1 %2").arg(m_layerKin[i]->id()).arg(m_layerKin[i]->z())); this->scene()->removeItem(m_layerKin[i]); delete m_layerKin[i]; } m_layerKin.clear(); } void PaletteItem::syncKinSelection(bool selected, PaletteItemBase * originator) { PaletteItemBase::syncKinSelection(selected, originator); foreach (ItemBase * lkpi, m_layerKin) { if (lkpi != originator && lkpi->isSelected() != selected) { qobject_cast(lkpi)->blockItemSelectedChange(selected); lkpi->setSelected(selected); } } if (this != originator && this->isSelected() != selected) { this->blockItemSelectedChange(selected); this->setSelected(selected); } } QVariant PaletteItem::itemChange(GraphicsItemChange change, const QVariant &value) { //DebugDialog::debug(QString("chief item change %1 %2").arg(this->id()).arg(change)); if (m_layerKin.count() > 0) { if (change == ItemSelectedChange) { bool selected = value.toBool(); if (m_blockItemSelectedChange && m_blockItemSelectedValue == selected) { m_blockItemSelectedChange = false; } else { syncKinSelection(selected, this); } } //else if (change == ItemVisibleHasChanged && value.toBool()) { //this->setSelected(syncSelected()); //this->setPos(m_offset + syncMoved()); //} else if (change == ItemPositionHasChanged) { this->syncKinMoved(this->m_offset, value.toPointF()); } } if (m_partLabel && m_partLabel->initialized()) { if (change == ItemPositionHasChanged) { m_partLabel->ownerMoved(value.toPointF()); } else if (change == ItemSelectedChange) { m_partLabel->update(); } } return PaletteItemBase::itemChange(change, value); } const QList & PaletteItem::layerKin() { return m_layerKin; } void PaletteItem::rotateItem(double degrees, bool includeRatsnest) { PaletteItemBase::rotateItem(degrees, includeRatsnest); for (int i = 0; i < m_layerKin.count(); i++) { m_layerKin[i]->rotateItem(degrees, includeRatsnest); } } void PaletteItem::flipItem(Qt::Orientations orientation) { PaletteItemBase::flipItem(orientation); foreach (ItemBase * lkpi, m_layerKin) { lkpi->flipItem(orientation); } } void PaletteItem::transformItem2(const QMatrix & matrix) { PaletteItemBase::transformItem2(matrix); foreach (ItemBase * lkpi, m_layerKin) { lkpi->transformItem2(matrix); } } void PaletteItem::setTransforms() { // only ever called when loading from file // jrc 14 july 2013: this call seems redundant--transforms have already been set up by now QTransform transform = getViewGeometry().transform(); if (transform.isIdentity()) return; //debugInfo("set transforms " + TextUtils::svgMatrix(transform)); //debugInfo("\t " + TextUtils::svgMatrix(this->transform())); setTransform(transform); for (int i = 0; i < m_layerKin.count(); i++) { //debugInfo("\t " + TextUtils::svgMatrix(m_layerKin[i]->getViewGeometry().transform())); //debugInfo("\t " + TextUtils::svgMatrix(m_layerKin[i]->transform())); m_layerKin[i]->setTransform2(m_layerKin[i]->getViewGeometry().transform()); } } void PaletteItem::moveItem(ViewGeometry & viewGeometry) { PaletteItemBase::moveItem(viewGeometry); for (int i = 0; i < m_layerKin.count(); i++) { m_layerKin[i]->moveItem(viewGeometry); } } void PaletteItem::setItemPos(QPointF & loc) { PaletteItemBase::setItemPos(loc); for (int i = 0; i < m_layerKin.count(); i++) { m_layerKin[i]->setItemPos(loc); } } void PaletteItem::updateConnections(bool includeRatsnest, QList & already) { updateConnectionsAux(includeRatsnest, already); foreach (ItemBase * lkpi, m_layerKin) { lkpi->updateConnectionsAux(includeRatsnest, already); } } bool PaletteItem::collectFemaleConnectees(QSet & items) { bool hasMale = PaletteItemBase::collectFemaleConnectees(items); foreach (ItemBase * lkpi, m_layerKin) { if (lkpi->collectFemaleConnectees(items)) { hasMale = true; } } return hasMale; } void PaletteItem::collectWireConnectees(QSet & wires) { PaletteItemBase::collectWireConnectees(wires); foreach (ItemBase * lkpi, m_layerKin) { qobject_cast(lkpi)->collectWireConnectees(wires); } } bool PaletteItem::mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event) { //DebugDialog::debug("layerkinchief got mouse press event"); /* if (acceptsMousePressConnectorEvent(NULL, event) && isBuriedConnectorHit(event) ) return; foreach(LayerKinPaletteItem * lkpi, m_layerKin) { if (lkpi->isBuriedConnectorHit(event)) return; } */ return PaletteItemBase::mousePressEventK(originalItem, event); } void PaletteItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL && infographics->spaceBarIsPressed()) { event->ignore(); return; } mousePressEventK(this, event); } void PaletteItem::syncKinMoved(QPointF offset, QPointF newPos) { Q_UNUSED(offset); // ignore offset--should all be zeros now //DebugDialog::debug(QString("sync kin moved %1 %2").arg(offset.x()).arg(offset.y()) ); //m_syncMoved = pos - offset; //if (newPos != pos()) { setPos(newPos); foreach (ItemBase * lkpi, m_layerKin) { lkpi->setPos(newPos); } //} } void PaletteItem::setInstanceTitle(const QString& title, bool initial) { ItemBase::setInstanceTitle(title, initial); foreach (ItemBase * lkpi, m_layerKin) { lkpi->setInstanceTitle(title, initial); } } void PaletteItem::setHidden(bool hide) { ItemBase::setHidden(hide); figureHover(); } void PaletteItem::setInactive(bool inactivate) { ItemBase::setInactive(inactivate); figureHover(); } void PaletteItem::figureHover() { setAcceptHoverEvents(true); setAcceptedMouseButtons(ALLMOUSEBUTTONS); foreach(ItemBase * lkpi, m_layerKin) { lkpi->setAcceptHoverEvents(true); lkpi->setAcceptedMouseButtons(ALLMOUSEBUTTONS); } } void PaletteItem::clearModelPart() { foreach (ItemBase * lkpi, m_layerKin) { lkpi->setModelPart(NULL); } ItemBase::clearModelPart(); } void PaletteItem::resetID() { ItemBase::resetID(); foreach (ItemBase * lkpi, m_layerKin) { lkpi->resetID(); } } void PaletteItem::slamZ(double z) { PaletteItemBase::slamZ(z); foreach (ItemBase * lkpi, m_layerKin) { lkpi->slamZ(z); } } void PaletteItem::resetImage(InfoGraphicsView * infoGraphicsView) { foreach (Connector * connector, modelPart()->connectors()) { connector->unprocess(this->viewID(), this->viewLayerID()); } LayerAttributes layerAttributes; initLayerAttributes(layerAttributes, viewID(), viewLayerID(), viewLayerPlacement(), true, !m_selectionShape.isEmpty()); this->setUpImage(modelPart(), infoGraphicsView->viewLayers(), layerAttributes); foreach (ItemBase * layerKin, m_layerKin) { resetKinImage(layerKin, infoGraphicsView); } } void PaletteItem::resetKinImage(ItemBase * layerKin, InfoGraphicsView * infoGraphicsView) { foreach (Connector * connector, modelPart()->connectors()) { connector->unprocess(layerKin->viewID(), layerKin->viewLayerID()); } LayerAttributes layerAttributes; initLayerAttributes(layerAttributes, layerKin->viewID(), layerKin->viewLayerID(), layerKin->viewLayerPlacement(), true, !layerKin->selectionShape().isEmpty()); qobject_cast(layerKin)->setUpImage(modelPart(), infoGraphicsView->viewLayers(), layerAttributes); } QString PaletteItem::genFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int steps, bool smd) { QString FzpTemplate = ""; QString ConnectorFzpTemplate = ""; QFile file1(QString(":/resources/templates/%1.txt").arg(templateName)); file1.open(QFile::ReadOnly); FzpTemplate = file1.readAll(); file1.close(); if (smd) { FzpTemplate.replace("", ""); } QFile file2(":/resources/templates/generic_sip_connectorFzpTemplate.txt"); file2.open(QFile::ReadOnly); ConnectorFzpTemplate = file2.readAll(); file2.close(); if (smd) { ConnectorFzpTemplate.replace("

", ""); } QStringList ss = moduleid.split("_"); int count = 0; foreach (QString s, ss) { bool ok; int c = s.toInt(&ok); if (ok) { count = c; break; } } if (count > maxPins || count < minPins) return ""; if (count % steps != 0) return ""; QString middle; for (int i = 0; i < count; i++) { middle += ConnectorFzpTemplate.arg(i).arg(i + 1); } return FzpTemplate.arg(count).arg(middle); } bool PaletteItem::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("editable pin labels", Qt::CaseInsensitive) == 0 && value.compare("true") == 0) { returnProp = ""; returnValue = value; QPushButton * button = new QPushButton(tr("Edit Pin Labels")); button->setObjectName("infoViewButton"); connect(button, SIGNAL(pressed()), this, SLOT(openPinLabelDialog())); button->setEnabled(swappingEnabled); returnWidget = button; return true; } bool result = PaletteItemBase::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); if (prop.compare("layer") == 0 && modelPart()->flippedSMD() && returnWidget != NULL) { bool disabled = true; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView && infoGraphicsView->boardLayers() == 2) disabled = false; returnWidget->setDisabled(disabled); } return result; } QStringList PaletteItem::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("layer") == 0) { if (modelPart()->flippedSMD()) { QStringList values = PaletteItemBase::collectValues(family, prop, value); for (int ix = values.count() - 1; ix >= 0; ix--) { if (values.at(ix).isEmpty()) { values.removeAt(ix); } } if (values.count() < 2) { ItemBase * itemBase = modelPart()->viewItem(ViewLayer::PCBView); if (itemBase) { values.clear(); values << TranslatedPropertyNames.value("bottom") << TranslatedPropertyNames.value("top"); if (itemBase->viewLayerID() == ViewLayer::Copper0) { value = values.at(0); } else { value = values.at(1); } } } return values; } if (modelPart()->itemType() == ModelPart::Part) { QStringList values = PaletteItemBase::collectValues(family, prop, value); if (values.count() == 0) { ItemBase * itemBase = modelPart()->viewItem(ViewLayer::PCBView); if (itemBase) { values << TranslatedPropertyNames.value("bottom") << TranslatedPropertyNames.value("top"); if (itemBase->viewLayerPlacement() == ViewLayer::NewBottom) { value = values.at(0); } else { value = values.at(1); } } } return values; } } return PaletteItemBase::collectValues(family, prop, value); } void PaletteItem::openPinLabelDialog() { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) { QMessageBox::warning( NULL, tr("Fritzing"), tr("Unable to proceed; unable to find top level view.") ); return; } QStringList labels; QList sortedConnectors = sortConnectors(); if (sortedConnectors.count() == 0) { QMessageBox::warning( NULL, tr("Fritzing"), tr("Unable to proceed; part connectors do no have standard IDs.") ); return; } foreach (Connector * connector, sortedConnectors) { labels.append(connector->connectorSharedName()); } QString chipLabel = modelPart()->localProp("chip label").toString(); if (chipLabel.isEmpty()) { chipLabel = instanceTitle(); } bool singleRow = isSingleRow(cachedConnectorItems()); PinLabelDialog pinLabelDialog(labels, singleRow, chipLabel, modelPart()->isCore(), NULL); int result = pinLabelDialog.exec(); if (result != QDialog::Accepted) return; QStringList newLabels = pinLabelDialog.labels(); if (newLabels.count() != sortedConnectors.count()) { QMessageBox::warning( NULL, tr("Fritzing"), tr("Label mismatch. Nothing was saved.") ); return; } infoGraphicsView->renamePins(this, labels, newLabels, singleRow); } void PaletteItem::renamePins(const QStringList & labels, bool singleRow) { QList sortedConnectors = sortConnectors(); for (int i = 0; i < labels.count(); i++) { Connector * connector = sortedConnectors.at(i); connector->setConnectorLocalName(labels.at(i)); } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); infoGraphicsView->changePinLabels(this, singleRow); } bool PaletteItem::isSingleRow(const QList & connectorItems) { if (connectorItems.count() == 2) { // no way to tell? so default to double return false; } else if (connectorItems.count() % 2 == 0) { QPointF p = connectorItems.at(0)->sceneAdjustedTerminalPoint(NULL); double slope = 0; for (int i = 1; i < connectorItems.count(); i++) { QPointF q = connectorItems.at(i)->sceneAdjustedTerminalPoint(NULL); if (p == q) continue; double newSlope = q.x() == p.x() ? std::numeric_limits::max() : (q.y() - p.y()) / (q.x() - p.x()); if (i == 1) { slope = newSlope; } else { double d = qAbs(newSlope - slope); if (d != 0 && d / qMax(qAbs(slope), qAbs(newSlope)) > 0.01) { return false; } } } } return true; } QList PaletteItem::sortConnectors() { QList sortedConnectors; foreach (Connector * connector, modelPart()->connectors().values()) { sortedConnectors.append(connector); } ByIDParseSuccessful = true; qSort(sortedConnectors.begin(), sortedConnectors.end(), byID); if (!ByIDParseSuccessful || sortedConnectors.count() == 0) { sortedConnectors.clear(); } return sortedConnectors; } bool PaletteItem::changePinLabels(bool singleRow, bool sip) { Q_UNUSED(singleRow); Q_UNUSED(sip); if (m_viewID != ViewLayer::SchematicView) return true; return false; } QStringList PaletteItem::getPinLabels(bool & hasLocal) { hasLocal = false; QStringList labels; QList sortedConnectors = sortConnectors(); if (sortedConnectors.count() == 0) return labels; foreach (Connector * connector, sortedConnectors) { labels.append(connector->connectorSharedName()); if (!connector->connectorLocalName().isEmpty()) { hasLocal = true; } } return labels; } void PaletteItem::resetConnectors() { if (m_viewID != ViewLayer::SchematicView) return; FSvgRenderer * renderer = fsvgRenderer(); if (renderer == NULL) return; QSizeF size = renderer->defaultSizeF(); // pixels QRectF viewBox = renderer->viewBoxF(); foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { SvgIdLayer * svgIdLayer = connectorItem->connector()->fullPinInfo(m_viewID, m_viewLayerID); if (svgIdLayer == NULL) continue; QRectF bounds = renderer->boundsOnElement(svgIdLayer->m_svgId); QPointF p(bounds.left() * size.width() / viewBox.width(), bounds.top() * size.height() / viewBox.height()); QRectF r = connectorItem->rect(); r.moveTo(p.x(), p.y()); connectorItem->setRect(r); } } void PaletteItem::resetConnectors(ItemBase * otherLayer, FSvgRenderer * otherLayerRenderer) { // there's only one connector foreach (Connector * connector, m_modelPart->connectors().values()) { if (connector == NULL) continue; connector->unprocess(m_viewID, m_viewLayerID); SvgIdLayer * svgIdLayer = connector->fullPinInfo(m_viewID, m_viewLayerID); if (svgIdLayer == NULL) continue; bool result = fsvgRenderer()->setUpConnector(svgIdLayer, false, viewLayerPlacement()); if (!result) continue; resetConnector(this, svgIdLayer); } if (otherLayer) { foreach (Connector * connector, m_modelPart->connectors().values()) { if (connector == NULL) continue; connector->unprocess(m_viewID, otherLayer->viewLayerID()); SvgIdLayer * svgIdLayer = connector->fullPinInfo(m_viewID, otherLayer->viewLayerID()); if (svgIdLayer == NULL) continue; bool result = otherLayerRenderer->setUpConnector(svgIdLayer, false, viewLayerPlacement()); if (!result) continue; resetConnector(otherLayer, svgIdLayer); } } } void PaletteItem::resetConnector(ItemBase * itemBase, SvgIdLayer * svgIdLayer) { QList already; foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { //DebugDialog::debug(QString("via set rect %1").arg(itemBase->viewID()), svgIdLayer->m_rect); connectorItem->setRect(svgIdLayer->rect(viewLayerPlacement())); connectorItem->setTerminalPoint(svgIdLayer->point(viewLayerPlacement())); connectorItem->setRadius(svgIdLayer->m_radius, svgIdLayer->m_strokeWidth); connectorItem->setIsPath(svgIdLayer->m_path); connectorItem->attachedMoved(false, already); break; } } bool PaletteItem::collectHoleSizeInfo(const QString & defaultHoleSizeValue, QWidget * parent, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget) { returnProp = tr("hole size"); returnValue = m_modelPart->localProp("hole size").toString(); if (returnValue.isEmpty()) { returnValue = defaultHoleSizeValue; } QWidget * frame = createHoleSettings(parent, m_holeSettings, swappingEnabled, returnValue, true); connect(m_holeSettings.sizesComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(changeHoleSize(const QString &))); connect(m_holeSettings.mmRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool))); connect(m_holeSettings.inRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool))); connect(m_holeSettings.diameterEdit, SIGNAL(editingFinished()), this, SLOT(changeDiameter())); connect(m_holeSettings.thicknessEdit, SIGNAL(editingFinished()), this, SLOT(changeThickness())); returnWidget = frame; return true; } void PaletteItem::setUpHoleSizes(const QString & type, HoleClassThing & holeThing) { if (holeThing.holeSizes.count() == 0) { setUpHoleSizesAux(holeThing, type); } initHoleSettings(m_holeSettings, &holeThing); QStringList localHoleSize = modelPart()->localProp("hole size").toString().split(","); if (localHoleSize.count() == 2) { m_holeSettings.ringThickness = localHoleSize.at(1); m_holeSettings.holeDiameter = localHoleSize.at(0); } else { QString hs = modelPart()->properties().value("hole size"); localHoleSize = hs.split(","); if (localHoleSize.count() == 2) { modelPart()->setLocalProp("hole size", hs); m_holeSettings.ringThickness = localHoleSize.at(1); m_holeSettings.holeDiameter = localHoleSize.at(0); } else { m_holeSettings.ringThickness = holeThing.ringThickness; m_holeSettings.holeDiameter = holeThing.holeSize; } } } void PaletteItem::setUpHoleSizesAux(HoleClassThing & holeThing, const QString & type) { QFile file(":/resources/vias.xml"); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("failed loading properties %1 line:%2 col:%3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) return; if (root.tagName() != "vias") return; QDomElement ve = root.firstChildElement("via"); while (!ve.isNull()) { QString rt = ve.attribute("ringthickness"); QString hs = ve.attribute("holesize"); QString name = ve.attribute("name"); QString isOK = ve.attribute(type); if (isOK.toInt() == 1) { if (ve.attribute(type + "default").compare("yes") == 0) { if (holeThing.ringThickness.isEmpty()) { holeThing.ringThickness = rt; } if (holeThing.holeSize.isEmpty()) { holeThing.holeSize = hs; } } bool ok; double val = TextUtils::convertToInches(ve.attribute(type + "ringthicknesslow"), &ok, false); if (ok) { holeThing.ringThicknessRange.setX(val); } val = TextUtils::convertToInches(ve.attribute(type + "ringthicknesshigh"), &ok, false); if (ok) { holeThing.ringThicknessRange.setY(val); } val = TextUtils::convertToInches(ve.attribute(type + "holediameterlow"), &ok, false); if (ok) { holeThing.holeDiameterRange.setX(val); } val = TextUtils::convertToInches(ve.attribute(type + "holediameterhigh"), &ok, false); if (ok) { holeThing.holeDiameterRange.setY(val); } holeThing.holeSizes.insert(name, QString("%1,%2").arg(hs).arg(rt)); holeThing.holeSizeKeys.append(name); } ve = ve.nextSiblingElement("via"); } holeThing.holeSizeValue = QString("%1,%2").arg(holeThing.holeSize).arg(holeThing.ringThickness); } QWidget * PaletteItem::createHoleSettings(QWidget * parent, HoleSettings & holeSettings, bool swappingEnabled, const QString & currentHoleSize, bool advanced) { static const int RowHeight = 21; holeSettings.diameterEdit = NULL; holeSettings.thicknessEdit = NULL; holeSettings.mmRadioButton = NULL; holeSettings.inRadioButton = NULL; holeSettings.diameterValidator = NULL; holeSettings.thicknessValidator = NULL; QFrame * frame = new QFrame(parent); frame->setObjectName("infoViewPartFrame"); QVBoxLayout * vBoxLayout = new QVBoxLayout(frame); vBoxLayout->setMargin(0); vBoxLayout->setContentsMargins(0, 0, 0, 0); vBoxLayout->setSpacing(0); holeSettings.sizesComboBox = new QComboBox(frame); holeSettings.sizesComboBox->setEditable(false); holeSettings.sizesComboBox->setObjectName("infoViewComboBox"); foreach (QString key, holeSettings.holeThing->holeSizeKeys) { holeSettings.sizesComboBox->addItem(key); } holeSettings.sizesComboBox->setEnabled(swappingEnabled); vBoxLayout->addWidget(holeSettings.sizesComboBox); if (advanced) { vBoxLayout->addSpacing(4); QFrame * hFrame = new QFrame(frame); QHBoxLayout * hLayout = new QHBoxLayout(hFrame); hLayout->setMargin(0); QGroupBox * subFrame = new QGroupBox(tr("advanced settings"), frame); subFrame->setObjectName("infoViewGroupBox"); QGridLayout * gridLayout = new QGridLayout(subFrame); gridLayout->setMargin(0); QGroupBox * rbFrame = new QGroupBox("", subFrame); rbFrame->setObjectName("infoViewGroupBox"); QVBoxLayout * vbl = new QVBoxLayout(rbFrame); vbl->setMargin(0); holeSettings.inRadioButton = new QRadioButton(tr("in"), subFrame); gridLayout->addWidget(holeSettings.inRadioButton, 0, 2); holeSettings.inRadioButton->setObjectName("infoViewRadioButton"); holeSettings.mmRadioButton = new QRadioButton(tr("mm"), subFrame); gridLayout->addWidget(holeSettings.mmRadioButton, 1, 2); holeSettings.inRadioButton->setObjectName("infoViewRadioButton"); vbl->addWidget(holeSettings.inRadioButton); vbl->addWidget(holeSettings.mmRadioButton); rbFrame->setLayout(vbl); gridLayout->addWidget(rbFrame, 0, 2, 2, 1, Qt::AlignVCenter); holeSettings.diameterEdit = new QLineEdit(subFrame); holeSettings.diameterEdit->setMinimumHeight(RowHeight); holeSettings.diameterValidator = new QDoubleValidator(holeSettings.diameterEdit); holeSettings.diameterValidator->setNotation(QDoubleValidator::StandardNotation); holeSettings.diameterEdit->setValidator(holeSettings.diameterValidator); gridLayout->addWidget(holeSettings.diameterEdit, 0, 1); holeSettings.diameterEdit->setObjectName("infoViewLineEdit"); QLabel * label = new QLabel(tr("Hole Diameter")); label->setMinimumHeight(RowHeight); label->setObjectName("infoViewGroupBoxLabel"); gridLayout->addWidget(label, 0, 0); holeSettings.thicknessEdit = new QLineEdit(subFrame); holeSettings.thicknessEdit->setMinimumHeight(RowHeight); holeSettings.thicknessValidator = new QDoubleValidator(holeSettings.thicknessEdit); holeSettings.thicknessValidator->setNotation(QDoubleValidator::StandardNotation); holeSettings.thicknessEdit->setValidator(holeSettings.thicknessValidator); gridLayout->addWidget(holeSettings.thicknessEdit, 1, 1); holeSettings.thicknessEdit->setObjectName("infoViewLineEdit"); label = new QLabel(tr("Ring Thickness")); label->setMinimumHeight(RowHeight); gridLayout->addWidget(label, 1, 0); label->setObjectName("infoViewLabel"); gridLayout->setContentsMargins(10, 2, 0, 2); gridLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 3); gridLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum), 1, 3); hLayout->addWidget(subFrame); hLayout->addSpacerItem(new QSpacerItem(1,1,QSizePolicy::Expanding,QSizePolicy::Minimum)); vBoxLayout->addWidget(hFrame); holeSettings.mmRadioButton->setEnabled(swappingEnabled); holeSettings.inRadioButton->setEnabled(swappingEnabled); holeSettings.diameterEdit->setEnabled(swappingEnabled); holeSettings.thicknessEdit->setEnabled(swappingEnabled); if (currentHoleSize.contains("mm")) { holeSettings.mmRadioButton->setChecked(true); } else { holeSettings.inRadioButton->setChecked(true); } } updateEditTexts(holeSettings); updateValidators(holeSettings); updateSizes(holeSettings); return frame; } void PaletteItem::updateEditTexts(HoleSettings & holeSettings) { if (holeSettings.diameterEdit == NULL) return; if (holeSettings.thicknessEdit == NULL) return; if (holeSettings.mmRadioButton == NULL) return; double hd = TextUtils::convertToInches(holeSettings.holeDiameter); double rt = TextUtils::convertToInches(holeSettings.ringThickness); QString newVal; if (holeSettings.currentUnits() == "in") { newVal = QString("%1,%2").arg(hd).arg(rt); } else { newVal = QString("%1,%2").arg(hd * 25.4).arg(rt * 25.4); } QStringList sizes = newVal.split(","); holeSettings.diameterEdit->setText(sizes.at(0)); holeSettings.thicknessEdit->setText(sizes.at(1)); } void PaletteItem::updateSizes(HoleSettings & holeSettings) { if (holeSettings.sizesComboBox == NULL) return; int newIndex = -1; QPointF current(TextUtils::convertToInches(holeSettings.holeDiameter), TextUtils::convertToInches(holeSettings.ringThickness)); for (int ix = 0; ix < holeSettings.sizesComboBox->count(); ix++) { QString key = holeSettings.sizesComboBox->itemText(ix); QString value = holeSettings.holeThing->holeSizes.value(key, ""); QStringList sizes; if (value.isEmpty()) { sizes = key.split(","); } else { sizes = value.split(","); } if (sizes.count() < 2) continue; QPointF p(TextUtils::convertToInches(sizes.at(0)), TextUtils::convertToInches(sizes.at(1))); if (p == current) { newIndex = ix; break; } } if (newIndex < 0) { QString newItem = holeSettings.holeDiameter + "," + holeSettings.ringThickness; holeSettings.sizesComboBox->addItem(newItem); newIndex = holeSettings.sizesComboBox->findText(newItem); holeSettings.holeThing->holeSizes.insert(newItem, newItem); holeSettings.holeThing->holeSizeKeys.prepend(newItem); } // don't want to trigger another undo command bool wasBlocked = holeSettings.sizesComboBox->blockSignals(true); holeSettings.sizesComboBox->setCurrentIndex(newIndex); holeSettings.sizesComboBox->blockSignals(wasBlocked); } void PaletteItem::updateValidators(HoleSettings & holeSettings) { if (holeSettings.diameterValidator == NULL) return; if (holeSettings.thicknessValidator == NULL) return; if (holeSettings.mmRadioButton == NULL) return; QString units = holeSettings.currentUnits(); double multiplier = (units == "mm") ? 25.4 : 1.0; holeSettings.diameterValidator->setRange(holeSettings.holeThing->holeDiameterRange.x() * multiplier, holeSettings.holeThing->holeDiameterRange.y() * multiplier, 3); holeSettings.thicknessValidator->setRange(holeSettings.holeThing->ringThicknessRange.x() * multiplier, holeSettings.holeThing->ringThicknessRange.y() * multiplier, 3); } void PaletteItem::initHoleSettings(HoleSettings & holeSettings, HoleClassThing * holeThing) { holeSettings.holeThing = holeThing; holeSettings.diameterEdit = holeSettings.thicknessEdit = NULL; holeSettings.diameterValidator = holeSettings.thicknessValidator = NULL; holeSettings.inRadioButton = holeSettings.mmRadioButton = NULL; holeSettings.sizesComboBox = NULL; } bool PaletteItem::setHoleSize(QString & holeSize, bool force, HoleSettings & holeSettings) { QStringList sizes = getSizes(holeSize, holeSettings); if (sizes.count() < 2) return false; if (!force && (holeSettings.holeDiameter.compare(sizes.at(0)) == 0) && (holeSettings.ringThickness.compare(sizes.at(1)) == 0)) { return false; } holeSettings.holeDiameter = sizes.at(0); holeSettings.ringThickness = sizes.at(1); updateEditTexts(holeSettings); updateValidators(holeSettings); updateSizes(holeSettings); return true; } QStringList PaletteItem::getSizes(QString & holeSize, HoleSettings & holeSettings) { QStringList sizes; QString hashedHoleSize = holeSettings.holeThing->holeSizes.value(holeSize); if (hashedHoleSize.isEmpty()) { sizes = holeSize.split(","); } else { sizes = hashedHoleSize.split(","); holeSize = sizes[0] + "," + sizes[1]; } return sizes; } void PaletteItem::changeHoleSize(const QString & newSize) { if (this->m_viewID != ViewLayer::PCBView) { PaletteItem * paletteItem = qobject_cast(modelPart()->viewItem(ViewLayer::PCBView)); if (paletteItem == NULL) return; paletteItem->changeHoleSize(newSize); return; } QString holeSize = newSize; QStringList sizes = getSizes(holeSize, m_holeSettings); if (sizes.count() != 2) return; QString svg = hackSvgHoleSize(sizes.at(0), sizes.at(1)); if (svg.isEmpty()) return; // figure out the new filename QString newModuleID = appendHoleSize(moduleID(), sizes.at(0), sizes.at(1)); QString newFzpFilename = newModuleID + ".fzp"; QString newSvgFilename = "pcb/" + newModuleID + ".svg"; QString fzp = hackFzpHoleSize(newModuleID, newSvgFilename, sizes.at(0) + "," + sizes.at(1)); if (fzp.isEmpty()) return; if (!TextUtils::writeUtf8(PartFactory::fzpPath() + newFzpFilename, fzp)) { return; } if (!TextUtils::writeUtf8(PartFactory::partPath() + newSvgFilename, svg)) { return; } m_propsMap.insert("hole size", newSize); m_propsMap.insert("moduleID", newModuleID); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->swap(family(), newModuleID, m_propsMap, this); } } QString PaletteItem::hackFzpHoleSize(const QString & fzp, const QString & moduleid, int hsix) { QString errorStr; int errorLine; int errorColumn; QDomDocument document; bool result = document.setContent(fzp, &errorStr, &errorLine, &errorColumn); if (!result) { DebugDialog::debug(QString("bad fzp in %1:%2").arg(moduleid).arg(fzp)); } QStringList strings = moduleid.mid(hsix).split("_"); return hackFzpHoleSize(document, moduleid, "pcb/" + moduleid + ".svg", strings.at(2) + "," + strings.at(3)); } QString PaletteItem::hackFzpHoleSize(const QString & newModuleID, const QString & pcbFilename, const QString & newSize) { QFile file(modelPart()->path()); QString errorStr; int errorLine; int errorColumn; QDomDocument document; bool result = document.setContent(&file, &errorStr, &errorLine, &errorColumn); if (!result) { DebugDialog::debug(QString("bad doc fzp in %1:%2 %3 %4").arg(newModuleID).arg(errorStr).arg(errorLine).arg(errorColumn)); } return hackFzpHoleSize(document, newModuleID, pcbFilename, newSize); } QString PaletteItem::hackFzpHoleSize(QDomDocument & document, const QString & newModuleID, const QString & pcbFilename, const QString & newSize) { QDomElement root = document.documentElement(); root.setAttribute("moduleId", newModuleID); QDomElement views = root.firstChildElement("views"); QDomElement pcbView = views.firstChildElement("pcbView"); QDomElement layers = pcbView.firstChildElement("layers"); if (layers.isNull()) return ""; layers.setAttribute("image", pcbFilename); QDomElement properties = root.firstChildElement("properties"); QDomElement prop = properties.firstChildElement("property"); bool gotProp = false; while (!prop.isNull()) { QString name = prop.attribute("name"); if (name.compare("hole size", Qt::CaseInsensitive) == 0) { gotProp = true; TextUtils::replaceChildText(prop, newSize); break; } prop = prop.nextSiblingElement("property"); } if (!gotProp) return ""; return TextUtils::removeXMLEntities(document.toString()); } QString PaletteItem::hackSvgHoleSizeAux(const QString & svg, const QString & expectedFileName) { QDomDocument document; document.setContent(svg); QFileInfo info(expectedFileName); QString baseName = info.completeBaseName(); int hsix = baseName.indexOf(HoleSizePrefix); QStringList strings = baseName.mid(hsix).split("_"); return hackSvgHoleSize(document, strings.at(2), strings.at(3)); } QString PaletteItem::hackSvgHoleSize(const QString & holeDiameter, const QString & ringThickness) { QFile file(filename()); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse pcb svg xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return ""; } return hackSvgHoleSize(domDocument, holeDiameter, ringThickness); } QString PaletteItem::hackSvgHoleSize(QDomDocument & domDocument, const QString & holeDiameter, const QString & ringThickness) { QDomElement root = domDocument.documentElement(); double w = TextUtils::convertToInches(root.attribute("width")); QStringList vb = root.attribute("viewBox").split(" "); if (vb.count() != 4) return ""; double wp = vb.at(2).toDouble(); if (wp == 0) return ""; double dpi = wp / w; double rt = TextUtils::convertToInches(ringThickness) * dpi; double hs = TextUtils::convertToInches(holeDiameter) * dpi; double rad = (hs + rt) / 2; QDomNodeList circles = root.elementsByTagName("circle"); for (int i = 0; i < circles.count(); i++) { QDomElement circle = circles.at(i).toElement(); QString id = circle.attribute("id"); if (ConnectorFinder.indexIn(id) == 0) { circle.setAttribute("r", QString::number(rad)); circle.setAttribute("stroke-width", QString::number(rt)); } } QDomNodeList rects = root.elementsByTagName("rect"); for (int i = 0; i < rects.count(); i++) { QDomElement rect = rects.at(i).toElement(); QString id = rect.attribute("id"); if (id.compare("square") == 0) { double oldWidth = rect.attribute("width").toDouble(); double oldX = rect.attribute("x").toDouble(); double oldY = rect.attribute("y").toDouble(); rect.setAttribute("width", QString::number(rad * 2)); rect.setAttribute("height", QString::number(rad * 2)); rect.setAttribute("x", QString::number(oldX + (oldWidth / 2) - rad)); rect.setAttribute("y", QString::number(oldY + (oldWidth / 2) - rad)); rect.setAttribute("stroke-width", QString::number(rt)); } } return TextUtils::removeXMLEntities(domDocument.toString()); } QString PaletteItem::appendHoleSize(const QString & moduleid, const QString & holeSize, const QString & ringThickness) { QString baseName = moduleid; int ix = baseName.lastIndexOf(HoleSizePrefix); if (ix >= 0) { baseName.truncate(ix); } return baseName + QString("%1%2_%3").arg(HoleSizePrefix).arg(holeSize).arg(ringThickness); } void PaletteItem::generateSwap(const QString & text, GenModuleID genModuleID, GenFzp genFzp, GenSvg makeBreadboardSvg, GenSvg makeSchematicSvg, GenSvg makePcbSvg) { FamilyPropertyComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; QMap propsMap(m_propsMap); propsMap.insert(comboBox->prop(), text); QString newModuleID = genModuleID(propsMap); if (!newModuleID.contains("smd", Qt::CaseInsensitive)) { // add hole size int ix = moduleID().indexOf(HoleSizePrefix); if (ix >= 0) { newModuleID.append(moduleID().mid(ix)); } } QString path; if (!PartFactory::fzpFileExists(newModuleID, path)) { QString fzp = genFzp(newModuleID); TextUtils::writeUtf8(path, fzp); QDomDocument doc; doc.setContent(fzp); QHash viewNames; QDomElement root = doc.documentElement(); QDomElement views = root.firstChildElement("views"); QDomElement view = views.firstChildElement(); while (!view.isNull()) { viewNames.insert(view.tagName(), view.attribute("image", "")); view = view.nextSiblingElement(); } QString name = viewNames.value("breadboardView", ""); if (!PartFactory::svgFileExists(name, path)) { QString svg = makeBreadboardSvg(name); TextUtils::writeUtf8(path, svg); } name = viewNames.value("schematicView", ""); if (!PartFactory::svgFileExists(name, path)) { QString svg = makeSchematicSvg(name); TextUtils::writeUtf8(path, svg); } name = viewNames.value("pcbView", ""); if (!PartFactory::svgFileExists(name, path)) { QString svg = makePcbSvg(name); TextUtils::writeUtf8(path, svg); } } m_propsMap.insert("moduleID", newModuleID); } void PaletteItem::changeUnits(bool) { QString newVal = changeUnits(m_holeSettings); changeHoleSize(newVal); } QString PaletteItem::changeUnits(HoleSettings & holeSettings) { double hd = TextUtils::convertToInches(holeSettings.holeDiameter); double rt = TextUtils::convertToInches(holeSettings.ringThickness); QString newVal; if (holeSettings.currentUnits() == "in") { newVal = QString("%1in,%2in").arg(hd).arg(rt); } else { newVal = QString("%1mm,%2mm").arg(hd * 25.4).arg(rt * 25.4); } QStringList sizes = newVal.split(","); holeSettings.ringThickness = sizes.at(1); holeSettings.holeDiameter = sizes.at(0); updateValidators(holeSettings); updateSizes(holeSettings); updateEditTexts(holeSettings); return newVal; } void PaletteItem::changeThickness() { if (changeThickness(m_holeSettings, sender())) { QLineEdit * edit = qobject_cast(sender()); changeHoleSize(m_holeSettings.holeDiameter + "," + edit->text() + m_holeSettings.currentUnits()); } } bool PaletteItem::changeThickness(HoleSettings & holeSettings, QObject * sender) { QLineEdit * edit = qobject_cast(sender); if (edit == NULL) return false; double newValue = edit->text().toDouble(); QString temp = holeSettings.ringThickness; temp.chop(2); double oldValue = temp.toDouble(); return (newValue != oldValue); } void PaletteItem::changeDiameter() { if (changeDiameter(m_holeSettings, sender())) { QLineEdit * edit = qobject_cast(sender()); changeHoleSize(edit->text() + m_holeSettings.currentUnits() + "," + m_holeSettings.ringThickness); } } bool PaletteItem::changeDiameter(HoleSettings & holeSettings, QObject * sender) { QLineEdit * edit = qobject_cast(sender); if (edit == NULL) return false; double newValue = edit->text().toDouble(); QString temp = holeSettings.holeDiameter; temp.chop(2); double oldValue = temp.toDouble(); return (newValue != oldValue); } bool PaletteItem::makeLocalModifications(QByteArray & svg, const QString & filename) { // a bottleneck for modifying part svg xml at setupImage time // for saved-as-new-part parts (i.e. that are no longer MysteryParts) that still have a chip-label or custom pin names // also handles adding a title if there is a label id in the switch (m_viewID) { case ViewLayer::PCBView: return false; default: if (itemType() != ModelPart::Part) return false; if (filename.startsWith("icon")) return false; break; } bool gotChipLabel = false; QString chipLabel = modelPart()->properties().value("chip label", ""); if (!chipLabel.isEmpty()) { svg = TextUtils::replaceTextElement(svg, "label", chipLabel); gotChipLabel = true; } bool modified = false; if (m_viewID == ViewLayer::SchematicView) { QString value = modelPart()->properties().value("editable pin labels", ""); if (value.compare("true") == 0) { bool hasLayout, sip; QStringList labels = sipOrDipOrLabels(hasLayout, sip); if (labels.count() > 0) { svg = PartFactory::makeSchematicSipOrDipOr(labels, hasLayout, sip).toUtf8(); modified = true; } } gotChipLabel = true; } if (gotChipLabel) return modified; int rix = svg.indexOf("label"); if (rix >= 0) { rix = qMax(0, rix - 4); // backup for id=" int ix = svg.indexOf("id=\"label\"", rix); if (ix < 0) { ix = svg.indexOf("id='label'", rix); } if (ix >= 0) { int tix = svg.lastIndexOf("title()); modified = true; } } } return modified; } QStringList PaletteItem::sipOrDipOrLabels(bool & hasLayout, bool & sip) { hasLayout = sip = false; bool hasLocal = false; QStringList labels = getPinLabels(hasLocal); if (labels.count() == 0) return labels; // part was formerly a mystery part or generic ic ... QHash properties = modelPart()->properties(); foreach (QString key, properties.keys()) { QString value = properties.value(key); if (key.compare("layout", Qt::CaseInsensitive) == 0) { // was a mystery part hasLayout = true; break; } if (key.compare("package") == 0) { // was a generic ic sip = value.contains("sip", Qt::CaseInsensitive); } } return labels; } void PaletteItem::resetLayerKin(const QString & svg) { QString svgNoText = SvgFileSplitter::hideText3(svg); resetRenderer(svgNoText); foreach (ItemBase * lkpi, layerKin()) { if (lkpi->viewLayerID() == ViewLayer::SchematicText) { bool hasText; QString svgText = SvgFileSplitter::showText3(svg, hasText); lkpi->resetRenderer(svgText); lkpi->setProperty("textSvg", svgText); qobject_cast(lkpi)->clearTextThings(); break; } } } QTransform PaletteItem::untransform() { //DebugDialog::debug("untransform"); QTransform chiefTransform = this->transform(); chiefTransform.setMatrix(chiefTransform.m11(), chiefTransform.m12(), chiefTransform.m13(), chiefTransform.m21(), chiefTransform.m22(), chiefTransform.m23(), 0, 0, chiefTransform.m33()); bool identity = chiefTransform.isIdentity(); if (!identity) { QTransform invert = chiefTransform.inverted(); transformItem(invert, false); foreach (ItemBase * lkpi, layerKin()) { lkpi->transformItem(invert, false); } } return chiefTransform; } void PaletteItem::retransform(const QTransform & chiefTransform) { //DebugDialog::debug("retransform"); if (!chiefTransform.isIdentity()) { transformItem(chiefTransform, false); foreach (ItemBase * lkpi, layerKin()) { lkpi->transformItem(chiefTransform, false); } } } fritzing-app-0.9.3b/src/items/paletteitem.h000066400000000000000000000170731272363601300207100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef PALETTEITEM_H #define PALETTEITEM_H #include #include #include #include #include #include #include #include #include "paletteitembase.h" #include "../viewlayer.h" typedef QString (*GenModuleID)(QMap & currPropsMap); typedef QString (*GenSvg)(const QString & expectedFileName); typedef QString (*GenFzp)(const QString &); struct HoleClassThing { QString holeSize; QString ringThickness; QString holeSizeValue; QHash holeSizes; QList holeSizeKeys; // need this for menu item ordering purposes QPointF ringThicknessRange; QPointF holeDiameterRange; }; struct HoleSettings { QString holeDiameter; QString ringThickness; HoleClassThing * holeThing; QPointer diameterValidator; QPointer thicknessValidator; QPointer diameterEdit; QPointer thicknessEdit; QPointer inRadioButton; QPointer mmRadioButton; QPointer sizesComboBox; QString currentUnits(); QString holeSize(); }; class PaletteItem : public PaletteItemBase { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call PaletteItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~PaletteItem(); void removeLayerKin(); void addLayerKin(class LayerKinPaletteItem * lkpi); const QList & layerKin(); virtual void loadLayerKin(const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement); void rotateItem(double degrees, bool includeRatsnest); void flipItem(Qt::Orientations orientation); void moveItem(ViewGeometry & viewGeometry); void transformItem2(const QMatrix &); void setItemPos(QPointF & pos); bool renderImage(ModelPart * modelPart, ViewLayer::ViewID viewID, const LayerHash & viewLayers, ViewLayer::ViewLayerID, bool doConnectors, QString & error); void setTransforms(); void syncKinMoved(QPointF offset, QPointF loc); void setInstanceTitle(const QString&, bool initial); bool swap(ModelPart* newModelPart, const LayerHash &layerHash, bool reinit, class SwapCommand *); void setHidden(bool hidden); void setInactive(bool inactivate); bool collectFemaleConnectees(QSet & items); void collectWireConnectees(QSet & wires); void clearModelPart(); bool mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *); void resetID(); void slamZ(double z); void resetImage(class InfoGraphicsView *); void resetKinImage(ItemBase * layerKin, InfoGraphicsView * infoGraphicsView); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); QStringList collectValues(const QString & family, const QString & prop, QString & value); virtual bool changePinLabels(bool singleRow, bool sip); QStringList getPinLabels(bool & hasLocal); void renamePins(const QStringList & labels, bool singleRow); void resetConnectors(); void resetConnectors(ItemBase * otherLayer, FSvgRenderer * otherLayerRenderer); void resetConnector(ItemBase * itemBase, SvgIdLayer * svgIdLayer); QStringList sipOrDipOrLabels(bool & hasLayout, bool & sip); void resetLayerKin(const QString & svg); QTransform untransform(); void retransform(const QTransform &); public: static QString genFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int steps, bool smd); static QWidget * createHoleSettings(QWidget * parent, HoleSettings &, bool swappingEnabled, const QString & currentHoleSize, bool advanced); static void updateValidators(HoleSettings &); static void updateEditTexts(HoleSettings &); static void updateSizes(HoleSettings &); static void initHoleSettings(HoleSettings & holeSettings, HoleClassThing *); static bool setHoleSize(QString & holeSize, bool force, HoleSettings & holeSettings); static QString changeUnits(HoleSettings &); static bool changeDiameter(HoleSettings & holeSettings, QObject * sender); static bool changeThickness(HoleSettings & holeSettings, QObject * sender); signals: void pinLabelSwap(ItemBase *, const QString & moduleID); protected slots: void openPinLabelDialog(); protected: void syncKinSelection(bool selected, PaletteItemBase * originator); QVariant itemChange(GraphicsItemChange change, const QVariant &value); void updateConnections(bool includeRatsnest, QList & already); void mousePressEvent(QGraphicsSceneMouseEvent *event); void figureHover(); bool isSingleRow(const QList & connectorItems); QList sortConnectors(); QString hackSvgHoleSize(const QString & holeDiameter, const QString & ringThickness); QString hackFzpHoleSize(const QString & moduleID, const QString & pcbFilename, const QString & holeSize); QString appendHoleSize(const QString & moduleID, const QString & holeSize, const QString & ringThickness); void generateSwap(const QString & text, GenModuleID, GenFzp, GenSvg makeBreadboardSvg, GenSvg makeSchematicSvg, GenSvg makePcbSvg); bool makeLocalModifications(QByteArray & svg, const QString & filename); void makeOneKin(qint64 & id, ViewLayer::ViewLayerID, ViewLayer::ViewLayerPlacement, ViewGeometry &, const LayerHash &); protected: void setUpHoleSizes(const QString & type, HoleClassThing &); void setUpHoleSizesAux(HoleClassThing &, const QString & type); bool collectHoleSizeInfo(const QString & defaultHoleSizeValue, QWidget * parent, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget); static QStringList getSizes(QString & holeSize, HoleSettings &); static QString hackFzpHoleSize(QDomDocument & document, const QString & newModuleID, const QString & pcbFilename, const QString & newSize); static QString hackFzpHoleSize(const QString & fzp, const QString & moduleid, int hsix); static QString hackSvgHoleSize(QDomDocument & domDocument, const QString & holeDiameter, const QString & ringThickness); static QString hackSvgHoleSizeAux(const QString & svg, const QString & expectedFileName); protected slots: virtual void changeHoleSize(const QString &); virtual void changeUnits(bool); void changeDiameter(); void changeThickness(); public: static const QString HoleSizePrefix; protected: QList m_layerKin; HoleSettings m_holeSettings; int m_flipCount; }; #endif fritzing-app-0.9.3b/src/items/paletteitembase.cpp000066400000000000000000000613041272363601300220720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include "paletteitembase.h" #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "../fsvgrenderer.h" #include "../svg/svgfilesplitter.h" #include "../layerattributes.h" #include "layerkinpaletteitem.h" #include "../connectors/connectoritem.h" #include "../connectors/svgidlayer.h" #include "wire.h" #include "partlabel.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../utils/cursormaster.h" #include #include #include #include #include #include #include #include #include static QPointF RotationCenter; static QPointF RotationAxis; static QTransform OriginalTransform; PaletteItemBase::PaletteItemBase(ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu ) : ItemBase(modelPart, viewID, viewGeometry, id, itemMenu) { m_syncSelected = false; m_offset.setX(0); m_offset.setY(0); m_blockItemSelectedChange = false; this->setPos(viewGeometry.loc()); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); setAcceptHoverEvents(true); if (hasPartNumberProperty()) { QString savedValue = modelPart->localProp(ModelPartShared::PartNumberPropertyName).toString(); if (savedValue.isEmpty()) { savedValue = modelPart->properties().value(ModelPartShared::PartNumberPropertyName, ""); if (!savedValue.isEmpty()) { modelPart->setLocalProp(ModelPartShared::PartNumberPropertyName, savedValue); } } } } QRectF PaletteItemBase::boundingRectWithoutLegs() const { return QRectF(0, 0, m_size.width(), m_size.height()); } QRectF PaletteItemBase::boundingRect() const { if (!hasRubberBandLeg()) { return QRectF(0, 0, m_size.width(), m_size.height()); } //debugInfo(QString("bounding rect %1 %2 %3 %4").arg(r.left()).arg(r.top()).arg(r.width()).arg(r.height())); return shape().controlPointRect(); } QPainterPath PaletteItemBase::hoverShape() const { // TODO: figure out real shape of svg QPainterPath path; path.addRect(0, 0, m_size.width(), m_size.height()); if (!hasRubberBandLeg()) return path; foreach (ConnectorItem * connectorItem, cachedConnectorItemsConst()) { if (connectorItem->hasRubberBandLeg()) { path.addPath(connectorItem->mapToParent(connectorItem->hoverShape())); } } path.setFillRule(Qt::WindingFill); return path; } QPainterPath PaletteItemBase::shape() const { // TODO: figure out real shape of svg QPainterPath path; if (m_squashShape) return path; path.addRect(0, 0, m_size.width(), m_size.height()); if (!hasRubberBandLeg()) return path; foreach (ConnectorItem * connectorItem, cachedConnectorItemsConst()) { if (connectorItem->hasRubberBandLeg()) { path.addPath(connectorItem->mapToParent(connectorItem->shape())); } } path.setFillRule(Qt::WindingFill); return path; } void PaletteItemBase::saveGeometry() { m_viewGeometry.setLoc(this->pos()); m_viewGeometry.setSelected(this->isSelected()); m_viewGeometry.setZ(this->zValue()); } bool PaletteItemBase::itemMoved() { return (this->pos() != m_viewGeometry.loc()); } void PaletteItemBase::moveItem(ViewGeometry & viewGeometry) { this->setPos(viewGeometry.loc()); QList already; updateConnections(false, already); } void PaletteItemBase::saveInstanceLocation(QXmlStreamWriter & streamWriter) { saveLocAndTransform(streamWriter); } void PaletteItemBase::syncKinSelection(bool selected, PaletteItemBase * originator) { Q_UNUSED(originator); m_syncSelected = selected; } void PaletteItemBase::syncKinMoved(QPointF offset, QPointF newPos) { Q_UNUSED(offset); Q_UNUSED(newPos); } QPointF PaletteItemBase::syncMoved() { return m_syncMoved; } void PaletteItemBase::blockItemSelectedChange(bool selected) { m_blockItemSelectedChange = true; m_blockItemSelectedValue = selected; } bool PaletteItemBase::syncSelected() { return m_syncSelected; } void PaletteItemBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_hidden || m_layerHidden) return; ItemBase::paint(painter, option, widget); } void PaletteItemBase::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_hidden || m_layerHidden) return; if (!hasRubberBandLeg()) { ItemBase::paintHover(painter, option, widget); return; } QPainterPath path; path.addRect(0, 0, m_size.width(), m_size.height()); ItemBase::paintHover(painter, option, widget, path); } void PaletteItemBase::paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_hidden || m_layerHidden) return; if (!hasRubberBandLeg()) { ItemBase::paintSelected(painter, option, widget); return; } QRectF r(0, 0, m_size.width(), m_size.height()); GraphicsUtils::qt_graphicsItem_highlightSelected(painter, option, r, QPainterPath()); } void PaletteItemBase::mousePressConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->mousePressConnectorEvent(connectorItem, event); } } bool PaletteItemBase::acceptsMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent * event) { Q_UNUSED(event); //if (m_viewID != ViewLayer::PCBView) { return true; //} } bool PaletteItemBase::mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event) { Q_UNUSED(originalItem); setInRotation(false); QPointF corner; if (freeRotationAllowed(event->modifiers()) && inRotationLocation(event->scenePos(), event->modifiers(), corner)) { this->saveGeometry(); setInRotation(true); RotationCenter = mapToScene(this->boundingRectWithoutLegs().center()); RotationAxis = event->scenePos(); OriginalTransform = this->transform(); /* DebugDialog::debug(QString("%11:in rotation:%1,%2 a:%3,%4 t:%5,%6,%7,%8 %9,%10") .arg(RotationCenter.x()).arg(RotationCenter.y()) .arg(RotationAxis.x()).arg(RotationAxis.y()) .arg(OriginalTransform.m11()).arg(OriginalTransform.m12()).arg(OriginalTransform.m21()).arg(OriginalTransform.m22()) .arg(OriginalTransform.dx()).arg(OriginalTransform.dy()) .arg((long) this, 0, 16) ); */ this->debugInfo("in rotation"); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) infoGraphicsView->setAnyInRotation(); return false; } ItemBase::mousePressEvent(event); if (canFindConnectorsUnder()) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { connectorItem->setOverConnectorItem(NULL); } } return false; } void PaletteItemBase::mouseMoveEventK(PaletteItemBase *, QGraphicsSceneMouseEvent *) { } void PaletteItemBase::mouseReleaseEventK(PaletteItemBase *, QGraphicsSceneMouseEvent *) { } void PaletteItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (!inRotation()) { ItemBase::mouseMoveEvent(event); return; } // TODO: doesn't account for scaling // see http://www.gamedev.net/topic/441695-transform-matrix-decomposition/ double originalAngle = atan2(OriginalTransform.m12(), OriginalTransform.m11()) * 180 / M_PI; double a1 = atan2(RotationAxis.y() - RotationCenter.y(), RotationAxis.x() - RotationCenter.x()); double a2 = atan2(event->scenePos().y() - RotationCenter.y(), event->scenePos().x() - RotationCenter.x()); double deltaAngle = (a2 - a1) * 180 / M_PI; //DebugDialog::debug(QString("original:%1 delta:%2").arg(originalAngle).arg(deltaAngle)); switch (m_viewID) { case ViewLayer::BreadboardView: case ViewLayer::PCBView: { double nearest = qRound((originalAngle + deltaAngle) / 45) * 45; if (qAbs(originalAngle + deltaAngle - nearest) < 6) { deltaAngle = nearest - originalAngle; //DebugDialog::debug(QString("\tdelta angle %1").arg(deltaAngle)); } } break; case ViewLayer::SchematicView: { double nearest = qRound((originalAngle + deltaAngle) / 90) * 90; deltaAngle = nearest - originalAngle; } break; default: return; } ItemBase * chief = layerKinChief(); // restore viewGeometry to original angle chief->getViewGeometry().setTransform(OriginalTransform); foreach (ItemBase * itemBase, chief->layerKin()) { itemBase->getViewGeometry().setTransform(OriginalTransform); } //DebugDialog::debug(QString("rotating item %1 da:%2 oa:%3 %4").arg(QTime::currentTime().toString("HH:mm:ss.zzz")).arg(deltaAngle).arg(originalAngle).arg((long) this, 0, 16)); chief->rotateItem(deltaAngle, true); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(chief); if (infoGraphicsView) infoGraphicsView->updateRotation(chief); } void PaletteItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (!inRotation()) { ItemBase::mouseReleaseEvent(event); return; } setInRotation(false); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) { // TODO: doesn't account for scaling // see: http://www.gamedev.net/topic/441695-transform-matrix-decomposition/ double originalAngle = atan2(OriginalTransform.m12(), OriginalTransform.m11()) * 180 / M_PI; double currentAngle = atan2(transform().m12(), transform().m11()) * 180 / M_PI; this->layerKinChief()->rotateItem(originalAngle - currentAngle, true); // put it back; undo command will redo it saveGeometry(); infoGraphicsView->triggerRotate(this->layerKinChief(), currentAngle - originalAngle); } } bool PaletteItemBase::canFindConnectorsUnder() { return true; } void PaletteItemBase::findConnectorsUnder() { if (!canFindConnectorsUnder()) return; foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { switch (connectorItem->connector()->connectorType()) { case Connector::Female: case Connector::Pad: continue; default: break; } connectorItem->findConnectorUnder(true, false, ConnectorItem::emptyConnectorItemList, false, NULL); } } bool PaletteItemBase::collectFemaleConnectees(QSet & items) { bool hasMale = false; foreach (ConnectorItem * item, cachedConnectorItems()) { if (item->connectorType() == Connector::Male) { hasMale = true; continue; } if (item->connectorType() != Connector::Female) continue; foreach (ConnectorItem * toConnectorItem, item->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) continue; if (!toConnectorItem->attachedTo()->isVisible()) continue; items.insert(toConnectorItem->attachedTo()); } } return hasMale; } void PaletteItemBase::collectWireConnectees(QSet & wires) { foreach (ConnectorItem * item, cachedConnectorItems()) { foreach (ConnectorItem * toConnectorItem, item->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { if (toConnectorItem->attachedTo()->isVisible()) { wires.insert(qobject_cast(toConnectorItem->attachedTo())); } } } } } bool PaletteItemBase::setUpImage(ModelPart * modelPart, const LayerHash & viewLayers, LayerAttributes & layerAttributes) { FSvgRenderer * renderer = ItemBase::setUpImage(modelPart, layerAttributes); if (renderer == NULL) { return false; } cacheLoaded(layerAttributes); m_canFlipVertical = modelPart->canFlipVertical(layerAttributes.viewID); m_canFlipHorizontal = modelPart->canFlipHorizontal(layerAttributes.viewID); m_filename = layerAttributes.filename(); //DebugDialog::debug(QString("filename %1").arg(m_filename) ); setSticky(modelPart->anySticky(layerAttributes.viewID)); QString elementID = ViewLayer::viewLayerXmlNameFromID(layerAttributes.viewLayerID); setViewLayerID(elementID, viewLayers); //DebugDialog::debug(QString("setting layer %1 view:%2 z:%3").arg(modelPart->title()).arg(viewID).arg(this->z()) ); this->setZValue(this->z()); //DebugDialog::debug("loaded"); //DebugDialog::debug(layerAttributes.loaded()); this->setSharedRendererEx(renderer); m_svg = true; if (layerAttributes.doConnectors) { setUpConnectors(renderer, modelPart->ignoreTerminalPoints()); } if (!m_viewGeometry.transform().isIdentity()) { setInitialTransform(m_viewGeometry.transform()); update(); } return true; } void PaletteItemBase::setUpConnectors(FSvgRenderer * renderer, bool ignoreTerminalPoints) { clearConnectorItemCache(); if (m_viewID == ViewLayer::PCBView && ViewLayer::isNonCopperLayer(m_viewLayerID)) { //DebugDialog::debug(QString("skip connectors: %1 vid:%2 vlid:%3") // .arg(this->title()) // .arg(m_viewID) // .arg(m_viewLayerID) // ); // don't waste time return; } foreach (Connector * connector, m_modelPart->connectors().values()) { if (connector == NULL) continue; //DebugDialog::debug(QString("id:%1 vid:%2 vlid:%3") // .arg(connector->connectorSharedID()) // .arg(m_viewID) // .arg(m_viewLayerID) // ); SvgIdLayer * svgIdLayer = connector->fullPinInfo(m_viewID, m_viewLayerID); if (svgIdLayer == NULL) { DebugDialog::debug(QString("svgidlayer fail %1 vid:%2 vlid:%3 %4") .arg(connector->connectorSharedID()) .arg(m_viewID) .arg(m_viewLayerID) .arg(m_modelPart->path()) ); continue; } bool result = renderer->setUpConnector(svgIdLayer, ignoreTerminalPoints, viewLayerPlacement()); if (!result) { DebugDialog::debug(QString("setup connector fail %1 vid:%2 vlid:%3 %4") .arg(connector->connectorSharedID()) .arg(m_viewID) .arg(m_viewLayerID) .arg(m_modelPart->path()) ); continue; } ConnectorItem * connectorItem = newConnectorItem(connector); connectorItem->setHybrid(svgIdLayer->m_hybrid); connectorItem->setRect(svgIdLayer->rect(viewLayerPlacement())); connectorItem->setTerminalPoint(svgIdLayer->point(viewLayerPlacement())); connectorItem->setRadius(svgIdLayer->m_radius, svgIdLayer->m_strokeWidth); connectorItem->setIsPath(svgIdLayer->m_path); if (!svgIdLayer->m_legId.isEmpty()) { m_hasRubberBandLeg = true; connectorItem->setRubberBandLeg(QColor(svgIdLayer->m_legColor), svgIdLayer->m_legStrokeWidth, svgIdLayer->m_legLine); } //DebugDialog::debug(QString("terminal point %1 %2").arg(terminalPoint.x()).arg(terminalPoint.y()) ); } foreach (SvgIdLayer * svgIdLayer, renderer->setUpNonConnectors(viewLayerPlacement())) { if (svgIdLayer == NULL) continue; NonConnectorItem * nonConnectorItem = new NonConnectorItem(this); //DebugDialog::debug( QString("in layer %1 with z %2") //.arg(ViewLayer::viewLayerNameFromID(m_viewLayerID)) //.arg(this->zValue()) ); nonConnectorItem->setRect(svgIdLayer->rect(viewLayerPlacement())); nonConnectorItem->setRadius(svgIdLayer->m_radius, svgIdLayer->m_strokeWidth); nonConnectorItem->setIsPath(svgIdLayer->m_path); //DebugDialog::debug(QString("terminal point %1 %2").arg(terminalPoint.x()).arg(terminalPoint.y()) ); delete svgIdLayer; } } void PaletteItemBase::connectedMoved(ConnectorItem * from, ConnectorItem * to, QList & already) { // not sure this is necessary any longer return; if (from->connectorType() != Connector::Female) return; // female connectors really only operate in breadboard view if (m_viewID != ViewLayer::BreadboardView) return; QTransform t = this->transform(); if (t.isRotating()) { // map a square and see if it's a diamond QRectF test(0, 0, 10, 10); QRectF result = t.mapRect(test); if (result.width() != test.width() || result.height() != test.height()) { return; } } QPointF fromTerminalPoint = from->sceneAdjustedTerminalPoint(to); QPointF toTerminalPoint = to->sceneAdjustedTerminalPoint(from); if (fromTerminalPoint == toTerminalPoint) return; this->setPos(this->pos() + fromTerminalPoint - toTerminalPoint); updateConnections(false, already); } void PaletteItemBase::hoverEnterEvent ( QGraphicsSceneHoverEvent * event ) { // debugInfo("hover enter paletteitembase"); ItemBase::hoverEnterEvent(event); if (event->isAccepted()) { if (hasRubberBandLeg()) { //DebugDialog::debug("---pab set override cursor"); CursorMaster::instance()->addCursor(this, cursor()); bool connected = false; foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->connectionsCount() > 0) { connected = true; break; } } } checkFreeRotation(event->modifiers(), event->scenePos()); } } void PaletteItemBase::hoverMoveEvent ( QGraphicsSceneHoverEvent * event ) { checkFreeRotation(event->modifiers(), event->scenePos()); } void PaletteItemBase::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) { if (hasRubberBandLeg()) { //DebugDialog::debug("------pab restore override cursor"); } CursorMaster::instance()->removeCursor(this); ItemBase::hoverLeaveEvent(event); } void PaletteItemBase::cursorKeyEvent(Qt::KeyboardModifiers modifiers) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); QPoint p = infoGraphicsView->mapFromGlobal(QCursor::pos()); checkFreeRotation(modifiers, infoGraphicsView->mapToScene(p)); if (hasRubberBandLeg()) { QCursor cursor; if (modifiers & altOrMetaModifier()) { cursor = *CursorMaster::RubberbandCursor; } else { cursor = *CursorMaster::MoveCursor; } CursorMaster::instance()->addCursor(this, cursor); } } void PaletteItemBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { ItemBase::contextMenuEvent(event); } LayerKinPaletteItem *PaletteItemBase::newLayerKinPaletteItem(PaletteItemBase * chief, ModelPart * modelPart, const ViewGeometry & viewGeometry, long id, QMenu* itemMenu, const LayerHash & viewLayers, LayerAttributes & layerAttributes) { LayerKinPaletteItem *lk = NULL; if (layerAttributes.viewLayerID == ViewLayer::SchematicText) { lk = new SchematicTextLayerKinPaletteItem(chief, modelPart, layerAttributes.viewID, viewGeometry, id, itemMenu); } else { lk = new LayerKinPaletteItem(chief, modelPart, layerAttributes.viewID, viewGeometry, id, itemMenu); } lk->initLKPI(layerAttributes, viewLayers); return lk; } QString PaletteItemBase::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { QString xmlName = ViewLayer::viewLayerXmlNameFromID(viewLayerID); QString path = filename(); Qt::Orientations orientation = Qt::Vertical; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) { orientation = infoGraphicsView->smdOrientation(); } QDomDocument flipDoc; getFlipDoc(modelPart(), path, viewLayerID, m_viewLayerPlacement, flipDoc, orientation); //DebugDialog::debug(QString("path: %1").arg(path)); QString svg = svgHash.value(path + xmlName, ""); if (!svg.isEmpty()) return svg; SvgFileSplitter splitter; bool result; if (flipDoc.isNull()) { result = splitter.split(path, xmlName); } else { QString f = flipDoc.toString(); result = splitter.splitString(f, xmlName); } if (!result) { return ""; } if (hasRubberBandLeg()) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (!connectorItem->hasRubberBandLeg()) continue; splitter.gReplace(connectorItem->legID(m_viewID, m_viewLayerID)); } } result = splitter.normalize(dpi, xmlName, blackOnly, factor); if (!result) { return ""; } svg = splitter.elementString(xmlName); svgHash.insert(path + xmlName, svg); return svg; } bool PaletteItemBase::canEditPart() { if (itemType() == ModelPart::Part) return true; return ItemBase::canEditPart(); } bool PaletteItemBase::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare(ModelPartShared::PartNumberPropertyName, Qt::CaseInsensitive) == 0) { returnProp = TranslatedPropertyNames.value(prop); QLineEdit * lineEdit = new QLineEdit(); lineEdit->setEnabled(swappingEnabled); QString current = m_modelPart->localProp(ModelPartShared::PartNumberPropertyName).toString(); lineEdit->setText(current); connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(partPropertyEntry())); lineEdit->setObjectName("infoViewLineEdit"); returnWidget = lineEdit; returnValue = current; return true; } return ItemBase::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void PaletteItemBase::setProp(const QString & prop, const QString & value) { if (prop.compare(ModelPartShared::PartNumberPropertyName) == 0) { modelPart()->setLocalProp(ModelPartShared::PartNumberPropertyName, value); if (m_partLabel) m_partLabel->displayTextsIf(); return; } ItemBase::setProp(prop, value); } void PaletteItemBase::partPropertyEntry() { QLineEdit * lineEdit = qobject_cast(sender()); if (lineEdit == NULL) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, ModelPartShared::PartNumberPropertyName, "", m_modelPart->localProp(ModelPartShared::PartNumberPropertyName).toString(), lineEdit->text(), true); } } const QCursor * PaletteItemBase::getCursor(Qt::KeyboardModifiers modifiers) { if (hasRubberBandLeg()) { if ((modifiers & altOrMetaModifier())) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->connectionsCount() > 0) { return CursorMaster::RubberbandCursor; } } } } //DebugDialog::debug("returning move cursor"); return CursorMaster::MoveCursor; } bool PaletteItemBase::freeRotationAllowed(Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); //if ((modifiers & altOrMetaModifier()) == 0) return false; if (!isSelected()) return false; if (this->moveLock()) return false; return rotation45Allowed(); } bool PaletteItemBase::freeRotationAllowed() { if (viewID() == ViewLayer::SchematicView) return false; return rotation45Allowed(); } bool PaletteItemBase::inRotationLocation(QPointF scenePos, Qt::KeyboardModifiers modifiers, QPointF & returnPoint) { if (!freeRotationAllowed(modifiers)) return false; if (m_viewID == ViewLayer::SchematicView) return false; QRectF r = this->boundingRectWithoutLegs(); QPolygonF polygon; polygon.append(mapToScene(r.topLeft())); polygon.append(mapToScene(r.topRight())); polygon.append(mapToScene(r.bottomRight())); polygon.append(mapToScene(r.bottomLeft())); foreach (QPointF p, polygon) { double dsqd = GraphicsUtils::distanceSqd(p, scenePos); if (dsqd < 9) { returnPoint = p; return true; } } return false; } void PaletteItemBase::checkFreeRotation(Qt::KeyboardModifiers modifiers, QPointF scenePos) { if (!freeRotationAllowed(modifiers)) return; QPointF returnPoint; bool inCorner = inRotationLocation(scenePos, modifiers, returnPoint); if (inCorner) { CursorMaster::instance()->addCursor(this, *CursorMaster::RotateCursor); } else { CursorMaster::instance()->addCursor(this, cursor()); } } QString PaletteItemBase::normalizeSvg(QString & svg, ViewLayer::ViewLayerID viewLayerID, bool blackOnly, double dpi, double & factor) { QString xmlName = ViewLayer::viewLayerXmlNameFromID(viewLayerID); SvgFileSplitter splitter; bool result = splitter.splitString(svg, xmlName); if (!result) { return ""; } result = splitter.normalize(dpi, xmlName, blackOnly, factor); if (!result) { return ""; } return splitter.elementString(xmlName); } void PaletteItemBase::setInitialTransform(const QTransform &matrix) { setTransform(matrix); } void PaletteItemBase::cacheLoaded(const LayerAttributes &) { } /* void PaletteItemBase::setPos(const QPointF & pos) { ItemBase::setPos(pos); DebugDialog::debug(QString("set pos %1 %2, %3").arg(this->id()).arg(pos.x()).arg(pos.y()) ); } void PaletteItemBase::setPos(double x, double y) { ItemBase::setPos(x, y); } */ fritzing-app-0.9.3b/src/items/paletteitembase.h000066400000000000000000000117771272363601300215500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef PALETTEITEMBASE_H #define PALETTEITEMBASE_H #include #include #include #include "../model/modelpart.h" #include "itembase.h" #include "../utils/cursormaster.h" class LayerKinPaletteItem; class PaletteItemBase : public ItemBase, public CursorKeyListener { Q_OBJECT public: PaletteItemBase(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu); void saveGeometry(); bool itemMoved(); virtual void saveInstanceLocation(QXmlStreamWriter &); void moveItem(ViewGeometry &); virtual void syncKinSelection(bool selected, PaletteItemBase *originator); virtual void syncKinMoved(QPointF offset, QPointF loc); void blockItemSelectedChange(bool selected); bool syncSelected(); QPointF syncMoved(); void mousePressConnectorEvent(class ConnectorItem *, QGraphicsSceneMouseEvent *); bool acceptsMousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual bool setUpImage(ModelPart* modelPart, const LayerHash & viewLayers, LayerAttributes &); void connectedMoved(ConnectorItem * from, ConnectorItem * to, QList & already); bool collectFemaleConnectees(QSet & items); void collectWireConnectees(QSet & wires); virtual bool mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event); virtual void mouseMoveEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event); virtual void mouseReleaseEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent(QGraphicsSceneHoverEvent * event ); void hoverMoveEvent(QGraphicsSceneHoverEvent * event ); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); //bool isBuriedConnectorHit(QGraphicsSceneMouseEvent *event); QRectF boundingRect() const; QRectF boundingRectWithoutLegs() const; QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); virtual bool canEditPart(); QPainterPath shape() const; QPainterPath hoverShape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); void setProp(const QString & prop, const QString & value); const QCursor * getCursor(Qt::KeyboardModifiers); void cursorKeyEvent(Qt::KeyboardModifiers modifiers); /* // for debugging void setPos(const QPointF & pos); void setPos(double x, double y); */ protected: static QString normalizeSvg(QString & svg, ViewLayer::ViewLayerID viewLayerID, bool blackOnly, double dpi, double & factor); protected: void setUpConnectors(FSvgRenderer *, bool ignoreTerminalPoints); void findConnectorsUnder(); virtual bool canFindConnectorsUnder(); bool inRotationLocation(QPointF scenePos, Qt::KeyboardModifiers modifiers, QPointF & returnPoint); virtual bool freeRotationAllowed(Qt::KeyboardModifiers modifiers); bool freeRotationAllowed(); void checkFreeRotation(Qt::KeyboardModifiers modifiers, QPointF scenePos); virtual LayerKinPaletteItem * newLayerKinPaletteItem(PaletteItemBase * chief, ModelPart *, const ViewGeometry &, long id, QMenu* itemMenu, const LayerHash &, LayerAttributes &); virtual void setInitialTransform(const QTransform &); virtual void cacheLoaded(const LayerAttributes &); protected slots: void partPropertyEntry(); protected: bool m_blockItemSelectedChange; bool m_blockItemSelectedValue; QPointF m_offset; bool m_syncSelected; QPointF m_syncMoved; bool m_svg; }; #endif fritzing-app-0.9.3b/src/items/partfactory.cpp000066400000000000000000000616531272363601300212670ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6962 $: $Author: irascibl@gmail.com $: $Date: 2013-04-14 00:08:36 +0200 (So, 14. Apr 2013) $ ********************************************************************/ #include "partfactory.h" #include "../debugdialog.h" #include "../viewgeometry.h" #include "../model/modelpart.h" #include "../model/modelbase.h" #include "paletteitem.h" #include "symbolpaletteitem.h" #include "wire.h" #include "virtualwire.h" #include "tracewire.h" #include "jumperitem.h" #include "resizableboard.h" #include "logoitem.h" #include "schematicframe.h" #include "resistor.h" #include "moduleidnames.h" #include "mysterypart.h" #include "groundplane.h" #include "note.h" #include "ruler.h" #include "dip.h" #include "pinheader.h" #include "screwterminal.h" #include "hole.h" #include "via.h" #include "pad.h" #include "capacitor.h" #include "perfboard.h" #include "breadboard.h" #include "stripboard.h" #include "led.h" #include "schematicsubpart.h" #include "layerkinpaletteitem.h" #include "../utils/folderutils.h" #include "../utils/lockmanager.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include static QString PartFactoryFolderPath; static QHash LockedFiles; static QString SvgFilesDir = "svg"; static QHash SubpartOffsets; const QString PartFactory::OldSchematicPrefix("0.3.schem."); ItemBase * PartFactory::createPart( ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, QMenu * wireMenu, bool doLabel) { modelPart->setModelIndexFromMultiplied(id); // make sure the model index is synched with the id; this is not always the case when parts are first created. ItemBase * itemBase = createPartAux(modelPart, viewID, viewGeometry, id, itemMenu, wireMenu, doLabel); if (itemBase) { itemBase->setViewLayerPlacement(viewLayerPlacement); } return itemBase; } ItemBase * PartFactory::createPartAux( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, QMenu * wireMenu, bool doLabel) { switch (modelPart->itemType()) { case ModelPart::Wire: { bool ratsnest = viewGeometry.getRatsnest(); if (ratsnest) { return new VirtualWire(modelPart, viewID, viewGeometry, id, wireMenu); } if (viewGeometry.getAnyTrace()) { TraceWire * traceWire = new TraceWire(modelPart, viewID, viewGeometry, id, wireMenu); return traceWire; } return new Wire(modelPart, viewID, viewGeometry, id, wireMenu, false); } case ModelPart::Note: return new Note(modelPart, viewID, viewGeometry, id, NULL); case ModelPart::CopperFill: return new GroundPlane(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Jumper: return new JumperItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::ResizableBoard: return new ResizableBoard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Board: return new Board(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Logo: if (modelPart->moduleID().contains("copper", Qt::CaseInsensitive)) { return new CopperLogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } else if (modelPart->moduleID().contains("schematic", Qt::CaseInsensitive)) { return new SchematicLogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } else if (modelPart->moduleID().contains("breadboard", Qt::CaseInsensitive)) { return new BreadboardLogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } // make sure "board" match comes after "breadboard" match else if (modelPart->moduleID().contains("board", Qt::CaseInsensitive)) { return new BoardLogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } return new LogoItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Ruler: return new Ruler(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Symbol: if (modelPart->moduleID().contains(ModuleIDNames::NetLabelModuleIDName)) { return new NetLabel(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } return new SymbolPaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Via: return new Via(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::Hole: return new Hole(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); case ModelPart::SchematicSubpart: return new SchematicSubpart(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); default: { QString moduleID = modelPart->moduleID(); if (moduleID.endsWith(ModuleIDNames::ModuleIDNameSuffix)) { if (moduleID.endsWith(ModuleIDNames::ResistorModuleIDName)) { return new Resistor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::CapacitorModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::CrystalModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::ThermistorModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::ZenerDiodeModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::PotentiometerModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::InductorModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::TwoPowerModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::ColorLEDModuleIDName)) { return new LED(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::ColorFluxLEDModuleIDName)) { return new LED(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::LEDModuleIDName)) { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::PerfboardModuleIDName)) { return new Perfboard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::StripboardModuleIDName)) { return new Stripboard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::Stripboard2ModuleIDName)) { return new Stripboard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::SchematicFrameModuleIDName)) { return new SchematicFrame(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::PadModuleIDName)) { return new Pad(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (moduleID.endsWith(ModuleIDNames::CopperBlockerModuleIDName)) { return new CopperBlocker(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } // must get the subclasses first if (modelPart->itemType() == ModelPart::Breadboard) { return new Breadboard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } } // TODO: use the list in properties.xml if (moduleID == "alps-starter-pot9mm") { return new Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } QString family = modelPart->properties().value("family", ""); if (family.compare("mystery part", Qt::CaseInsensitive) == 0) { return new MysteryPart(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (family.compare("screw terminal", Qt::CaseInsensitive) == 0) { return new ScrewTerminal(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (family.compare("pin header", Qt::CaseInsensitive) == 0) { return new PinHeader(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } if (family.compare("generic IC", Qt::CaseInsensitive) == 0) { return new Dip(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } return new PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel); } } } QString PartFactory::getSvgFilename(ModelPart * modelPart, const QString & baseName, bool generate, bool handleSubparts) { QStringList tempPaths; QString postfix = "/"+ SvgFilesDir +"/%1/"+ baseName; QString userStore = FolderUtils::getUserPartsPath()+postfix; QString pfPath = PartFactory::folderPath() + postfix; if(!modelPart->path().isEmpty()) { QString path = modelPart->path(); QDir dir(path); // is a path to a filename dir.cdUp(); // lop off the filename dir.cdUp(); // parts root tempPaths << dir.absolutePath() + postfix; tempPaths << FolderUtils::getAppPartsSubFolderPath("")+postfix; // some svgs may still be in the fritzing parts folder, though the other svgs are in the user folder if (tempPaths.at(0).compare(userStore) != 0) { tempPaths << userStore; } if (tempPaths.at(0).compare(pfPath) != 0) { tempPaths << pfPath; } //DebugDialog::debug("temp path"); //foreach (QString tempPath, tempPaths) { // DebugDialog::debug(tempPath); //} } else { DebugDialog::debug("modelPart with no path--this shouldn't happen"); tempPaths << FolderUtils::getAppPartsSubFolderPath("")+postfix; tempPaths << userStore; } tempPaths << ":resources/parts/svg/%1/" + baseName; //DebugDialog::debug(QString("got tempPath %1").arg(tempPath)); QString filename; bool exists = false; foreach (QString tempPath, tempPaths) { foreach (QString possibleFolder, ModelPart::possibleFolders()) { filename = tempPath.arg(possibleFolder); if (QFileInfo(filename).exists()) { exists = true; if (possibleFolder == "obsolete") { DebugDialog::debug(QString("module %1:%2 obsolete svg %3").arg(modelPart->title()).arg(modelPart->moduleID()).arg(filename)); } break; } } if (exists) break; } if (!exists && generate) { filename = PartFactory::getSvgFilename(baseName); } if (handleSubparts && modelPart->modelPartShared() && modelPart->modelPartShared()->hasSubparts()) { ModelPartShared * superpart = modelPart->modelPartShared(); QString schematicName = superpart->imageFileName(ViewLayer::SchematicView); QString originalPath = getSvgFilename(modelPart, schematicName, true, false); foreach (ModelPartShared * mps, superpart->subparts()) { QString schematicFileName = mps->imageFileName(ViewLayer::SchematicView); if (schematicFileName.isEmpty()) continue; QString path = partPath() + schematicFileName; QFileInfo info(path); if (info.exists()) { mps->setSubpartOffset(SubpartOffsets.value(path, QPointF(0, 0))); continue; } QFile file(originalPath); QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(&file, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("xml failure %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); continue; } QDomElement root = doc.documentElement(); QDomElement top = showSubpart(root, mps->subpartID()); fixSubpartBounds(top, mps); SubpartOffsets.insert(path, mps->subpartOffset()); TextUtils::writeUtf8(path, doc.toString(4)); } } return filename; } QString PartFactory::getSvgFilename(const QString & fileName) { QString osFileName(fileName); osFileName.remove(OldSchematicPrefix); if (osFileName.startsWith("pcb/dip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &MysteryPart::makePcbDipSvg); } if (osFileName.startsWith("pcb/mystery_part_", Qt::CaseInsensitive)) { if (osFileName.contains("dip")) { return getSvgFilenameAux(fileName, &MysteryPart::makePcbDipSvg); } return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("breadboard/mystery_part_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &MysteryPart::makeBreadboardSvg); } if (osFileName.startsWith("breadboard/screw_terminal_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &ScrewTerminal::makeBreadboardSvg); } if (osFileName.startsWith("schematic/screw_terminal_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &ScrewTerminal::makeSchematicSvg); } if (osFileName.startsWith("pcb/screw_terminal_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &ScrewTerminal::makePcbSvg); } if (osFileName.startsWith("breadboard/generic_sip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &Dip::makeBreadboardSvg); } if (osFileName.startsWith("schematic/mystery_part_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &MysteryPart::makeSchematicSvg); } if (osFileName.startsWith("schematic/generic_sip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &MysteryPart::makeSchematicSvg); } if (osFileName.startsWith("pcb/generic_sip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("schematic/generic_ic_dip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &Dip::makeSchematicSvg); } if (osFileName.startsWith("breadboard/generic_ic_dip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &Dip::makeBreadboardSvg); } if (osFileName.startsWith("pcb/generic_ic_dip_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &MysteryPart::makePcbDipSvg); } if (osFileName.startsWith("pcb/nsjumper_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("pcb/jumper_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("pcb/shrouded_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("pcb/molex_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("pcb/longpad_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } if (osFileName.startsWith("bread/molex_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makeBreadboardSvg); } if (osFileName.startsWith("bread/longpad_", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makeBreadboardSvg); } if (osFileName.contains("pin_header", Qt::CaseInsensitive)) { if (osFileName.contains("schematic", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makeSchematicSvg); } else if (osFileName.contains("bread", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makeBreadboardSvg); } else if (osFileName.contains("pcb", Qt::CaseInsensitive)) { return getSvgFilenameAux(fileName, &PinHeader::makePcbSvg); } } if (osFileName.contains("perfboard", Qt::CaseInsensitive) || osFileName.contains("stripboard", Qt::CaseInsensitive)) { if (osFileName.contains("icon")) return fileName; return getSvgFilenameAux(fileName, &Perfboard::makeBreadboardSvg); } return ""; } bool PartFactory::svgFileExists(const QString & expectedFileName, QString & path) { QString p = FolderUtils::getAppPartsSubFolderPath("") + "/"+ SvgFilesDir + "/core/"; if (QFileInfo(p + expectedFileName).exists()) { path = expectedFileName; return true; } path = partPath() + expectedFileName; QFileInfo info(path); return info.exists(); } QString PartFactory::getSvgFilenameAux(const QString & expectedFileName, GenSvg genSvg) { QString path; if (svgFileExists(expectedFileName, path)) return path; QString svg = (*genSvg)(expectedFileName); if (TextUtils::writeUtf8(path, svg)) { return path; } return ""; } bool PartFactory::fzpFileExists(const QString & moduleID, QString & path) { QString expectedFileName = moduleID + FritzingPartExtension; path = FolderUtils::getAppPartsSubFolderPath("") + "/core/" + expectedFileName; if (QFileInfo(path).exists()) { path = expectedFileName; return true; } path = fzpPath() + expectedFileName; QFileInfo info(path); return info.exists(); } QString PartFactory::getFzpFilenameAux(const QString & moduleID, QString (*getFzp)(const QString &)) { QString path; if (fzpFileExists(moduleID, path)) return path; QString fzp = (*getFzp)(moduleID); if (TextUtils::writeUtf8(path, fzp)) { return path; } return ""; } QString PartFactory::getFzpFilename(const QString & moduleID) { QString filename = fzpPath() + moduleID + ".fzp"; QFileInfo info(filename); if (info.exists()) return filename; if (moduleID.endsWith(ModuleIDNames::PerfboardModuleIDName)) { return getFzpFilenameAux(moduleID, &Perfboard::genFZP); } if (moduleID.endsWith(ModuleIDNames::StripboardModuleIDName)) { return getFzpFilenameAux(moduleID, &Stripboard::genFZP); } if (moduleID.endsWith(ModuleIDNames::Stripboard2ModuleIDName)) { return getFzpFilenameAux(moduleID, &Stripboard::genFZP); } if (moduleID.startsWith("generic_ic_dip")) { return getFzpFilenameAux(moduleID, &Dip::genDipFZP); } if (moduleID.startsWith("screw_terminal")) { return getFzpFilenameAux(moduleID, &ScrewTerminal::genFZP); } if (moduleID.startsWith("generic_sip")) { return getFzpFilenameAux(moduleID, &Dip::genSipFZP); } if (moduleID.contains("_pin_header_") && moduleID.startsWith("generic_")) { return getFzpFilenameAux(moduleID, &PinHeader::genFZP); } if (moduleID.startsWith("mystery_part")) { if (moduleID.contains("dip", Qt::CaseInsensitive)) { return getFzpFilenameAux(moduleID, &MysteryPart::genDipFZP); } else { return getFzpFilenameAux(moduleID, &MysteryPart::genSipFZP); } } return ""; } void PartFactory::initFolder() { LockManager::initLockedFiles("partfactory", PartFactoryFolderPath, LockedFiles, LockManager::SlowTime); QFileInfoList backupList; LockManager::checkLockedFiles("partfactory", backupList, LockedFiles, true, LockManager::SlowTime); FolderUtils::makePartFolderHierarchy(PartFactoryFolderPath, "core"); FolderUtils::makePartFolderHierarchy(PartFactoryFolderPath, "contrib"); } void PartFactory::cleanup() { LockManager::releaseLockedFiles(PartFactoryFolderPath, LockedFiles); } ModelPart * PartFactory::fixObsoleteModuleID(QDomDocument & domDocument, QDomElement & instance, QString & moduleIDRef, ModelBase * referenceModel) { // TODO: less hard-coding if (moduleIDRef.startsWith("generic_male")) { ModelPart * modelPart = referenceModel->retrieveModelPart(moduleIDRef); if (modelPart != NULL) { instance.setAttribute("moduleIdRef", moduleIDRef); QDomElement prop = domDocument.createElement("property"); instance.appendChild(prop); prop.setAttribute("name", "form"); prop.setAttribute("value", PinHeader::MaleFormString); return modelPart; } } if (moduleIDRef.startsWith("generic_rounded_female")) { ModelPart * modelPart = referenceModel->retrieveModelPart(moduleIDRef); if (modelPart != NULL) { instance.setAttribute("moduleIdRef", moduleIDRef); QDomElement prop = domDocument.createElement("property"); instance.appendChild(prop); prop.setAttribute("name", "form"); prop.setAttribute("value", PinHeader::FemaleRoundedFormString); return modelPart; } } return NULL; } QString PartFactory::folderPath() { return PartFactoryFolderPath; } QString PartFactory::fzpPath() { return PartFactoryFolderPath + "/core/"; } QString PartFactory::partPath() { return PartFactoryFolderPath + "/svg/core/"; } QString PartFactory::makeSchematicSipOrDipOr(const QStringList & labels, bool hasLayout, bool sip) { if (hasLayout) { return MysteryPart::makeSchematicSvg(labels, false); } if (sip) { return MysteryPart::makeSchematicSvg(labels, true); } return Dip::makeSchematicSvg(labels); } QDomElement PartFactory::showSubpart(QDomElement & root, const QString & subpart) { if (subpart.isEmpty()) return ___emptyElement___; QString id = root.attribute("id"); if (id == subpart) { return root; } QDomElement top; QDomElement child = root.firstChildElement(); while (!child.isNull()) { QDomElement candidate = showSubpart(child, subpart); if (!candidate.isNull()) { top = candidate; } child = child.nextSiblingElement(); } if (root.tagName() != "g" && root.tagName() != "svg") { root.setTagName("g"); } return top; } void PartFactory::fixSubpartBounds(QDomElement & top, ModelPartShared * mps) { if (top.isNull()) return; QString transform = top.attribute("transform", ""); top.removeAttribute("transform"); QDomElement g = top.ownerDocument().createElement("g"); if (!transform.isEmpty()) g.setAttribute("transform", transform); QDomElement child = top.firstChildElement(); while (!child.isNull()) { g.appendChild(child); child = top.firstChildElement(); } top.appendChild(g); QSvgRenderer renderer; QByteArray byteArray = top.ownerDocument().toByteArray(); bool loaded = renderer.load(byteArray); if (!loaded) return; QRectF viewBox; double wignore, hignore; TextUtils::ensureViewBox(top.ownerDocument(), 1, viewBox, false, wignore, hignore, false); double sWidth, sHeight, vbWidth, vbHeight; QDomDocument doc = top.ownerDocument(); TextUtils::getSvgSizes(doc, sWidth, sHeight, vbWidth, vbHeight); QMatrix m = renderer.matrixForElement(mps->subpartID()); QRectF elementBounds = renderer.boundsOnElement(mps->subpartID()); QRectF bounds = m.mapRect(elementBounds); // bounds is in terms of the whole svg // unfortunately, QSvgRenderer doesn't deal with text bounds int w = qCeil(sWidth * GraphicsUtils::SVGDPI); int h = qCeil(sWidth * GraphicsUtils::SVGDPI); QImage image(w,h, QImage::Format_Mono); QDomNodeList nodeList = top.elementsByTagName("text"); QList texts; for (int i = 0; i < nodeList.count(); i++) { texts.append(nodeList.at(i).toElement()); } int ix = 0; foreach (QDomElement text, texts) { text.setTagName("g"); } ix = 0; foreach (QDomElement text, texts) { int minX, minY, maxX, maxY; QMatrix matrix; QRectF viewBox2; SchematicTextLayerKinPaletteItem::renderText(image, text, minX, minY, maxX, maxY, matrix, viewBox2); QRectF r(minX * viewBox.width() / image.width(), minY * viewBox.height() / image.height(), (maxX - minX) * viewBox.width() / image.width(), (maxY - minY) * viewBox.height() / image.height()); bounds |= r; } foreach (QDomElement text, texts) { text.setTagName("text"); } QDomElement root = top.ownerDocument().documentElement(); double newW = sWidth * bounds.width() / vbWidth; double newH = sHeight * bounds.height() / vbHeight; root.setAttribute("width", QString("%1in").arg(newW)); root.setAttribute("height", QString("%1in").arg(newH)); root.setAttribute("viewBox", QString("0 0 %1 %2").arg(bounds.width()).arg(bounds.height())); if (bounds.left() != 0 || bounds.top() != 0) { QDomElement g2 = top.ownerDocument().createElement("g"); g2.appendChild(g); top.appendChild(g2); g2.setAttribute("transform", QString("translate(%1,%2)").arg(-bounds.left()).arg(-bounds.top())); } mps->setSubpartOffset(QPointF(bounds.left() * sWidth / vbWidth, bounds.top() * sHeight / vbHeight)); } fritzing-app-0.9.3b/src/items/partfactory.h000066400000000000000000000052431272363601300207250ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6958 $: $Author: irascibl@gmail.com $: $Date: 2013-04-07 22:56:44 +0200 (So, 07. Apr 2013) $ ********************************************************************/ #ifndef PARTFACTORY_H #define PARTFACTORY_H #include #include #include #include "../viewlayer.h" #include "paletteitem.h" class PartFactory { public: static class ItemBase * createPart(class ModelPart *, ViewLayer::ViewLayerPlacement, ViewLayer::ViewID, const class ViewGeometry & viewGeometry, long id, QMenu * itemMenu, QMenu * wireMenu, bool doLabel); static QString getSvgFilename(class ModelPart *, const QString & filename, bool generate, bool handleSubparts); static QString getFzpFilename(const QString & moduleID); static void initFolder(); static void cleanup(); static class ModelPart * fixObsoleteModuleID(QDomDocument & domDocument, QDomElement & instance, QString & moduleIDRef, class ModelBase * referenceModel); static QString folderPath(); static QString fzpPath(); static QString partPath(); static bool svgFileExists(const QString & expectedFileName, QString & path); static bool fzpFileExists(const QString & moduleID, QString & path); static QString makeSchematicSipOrDipOr(const QStringList & labels, bool hasLayout, bool sip); static QString getSvgFilename(const QString & filename); protected: static QString getFzpFilenameAux(const QString & moduleID, GenFzp); static QString getSvgFilenameAux(const QString & expectedFileName, GenSvg); static class ItemBase * createPartAux(class ModelPart *, ViewLayer::ViewID, const class ViewGeometry & viewGeometry, long id, QMenu * itemMenu, QMenu * wireMenu, bool doLabel); static QDomElement showSubpart(QDomElement & root, const QString & subpart); static void fixSubpartBounds(QDomElement &, ModelPartShared *); public: static const QString OldSchematicPrefix; }; #endif fritzing-app-0.9.3b/src/items/partlabel.cpp000066400000000000000000000603611272363601300206720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "partlabel.h" #include "../items/itembase.h" #include "../viewgeometry.h" #include "../debugdialog.h" #include "../sketch/infographicsview.h" #include "../model/modelpart.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../installedfonts.h" #include "../fsvgrenderer.h" #include #include #include #include #include #include #include // TODO: // ** selection: coordinate with part selection: it's a layerkin // ** select a part, highlight its label; click a label, highlight its part // ** viewinfo update when selected // ** viewinfo for wires // ** graphics (esp. drag area vs. edit area) // ** html info box needs to update when view switches // ** undo delete text // ** undo change text // ** undo move // ** layers and z order // ** hide and show layer // ** sync hide/show checkbox with visibility state // ** save and load // ** text color needs to be separate in separate views // ** hide silkscreen should hide silkscreen label // ** delete owner: delete label // ** make label single-line (ignore enter key) // ** rotate/flip // ** undo rotate/flip // format: bold, italic, size (small normal large huge), color?, // undo format // heads-up controls // rotate/flip may need to be relative to part? // copy/paste? // z-order manipulation? // hover? // show = autoselect? // undo delete show? // close focus on enter/return? // -- multiple selection? // -- undo select // -- export to svg for export diy (silkscreen layer is not exported) enum PartLabelAction { PartLabelRotate45CW = 1, PartLabelRotate90CW, PartLabelRotate135CW, PartLabelRotate180, PartLabelRotate45CCW, PartLabelRotate90CCW, PartLabelRotate135CCW, PartLabelFlipHorizontal, PartLabelFlipVertical, PartLabelEdit, PartLabelFontSizeTiny, PartLabelFontSizeSmall, PartLabelFontSizeMedium, PartLabelFontSizeLarge, PartLabelDisplayLabelText, PartLabelHide }; ///////////////////////////////////////////// static QMultiHash AllPartLabels; static const QString LabelTextKey = ""; static const double InactiveOpacity = 0.4; /////////////////////////////////////////// PartLabel::PartLabel(ItemBase * owner, QGraphicsItem * parent) : QGraphicsSvgItem(parent) { m_renderer = NULL; m_owner = owner; m_spaceBarWasPressed = false; m_text = m_displayText = ""; m_inactive = m_hidden = m_initialized = false; m_displayKeys.append(LabelTextKey); if (m_owner->hasPartNumberProperty() && m_owner->viewID() != ViewLayer::PCBView) { m_displayKeys.append(ModelPartShared::PartNumberPropertyName); } setFlag(QGraphicsItem::ItemIsSelectable, false); setFlag(QGraphicsItem::ItemIsMovable, false); // don't move this in the standard QGraphicsItem way setVisible(false); m_viewLayerID = ViewLayer::UnknownLayer; setAcceptHoverEvents(true); AllPartLabels.insert(m_owner->id(), this); } PartLabel::~PartLabel() { AllPartLabels.remove(m_owner->id(), this); if (m_owner) { m_owner->clearPartLabel(); } } void PartLabel::showLabel(bool showIt, ViewLayer * viewLayer) { if (showIt == this->isVisible()) return; if (showIt && !m_initialized) { if (m_owner == NULL) return; if (m_owner->scene() == NULL) return; bool flipped = (viewLayer->viewLayerID() == ViewLayer::Silkscreen0Label); if (m_owner->viewID() != ViewLayer::PCBView) { foreach (QString dk, m_owner->modelPart()->displayKeys()) { if (!m_displayKeys.contains(dk)) { m_displayKeys.append(dk); } } } this->setZValue(viewLayer->nextZ()); m_viewLayerID = viewLayer->viewLayerID(); m_owner->scene()->addItem(this); setUpText(); setPlainText(m_owner->instanceTitle()); m_initialized = true; QRectF obr = m_owner->boundingRect(); QRectF tbr = QGraphicsSvgItem::boundingRect(); QPointF initial = (flipped) ? m_owner->pos() + QPointF(-tbr.width(), -tbr.height()) : m_owner->pos() + QPointF(obr.width(), -tbr.height()); this->setPos(initial); m_offset = initial - m_owner->pos(); if (flipped) { transformLabel(QTransform().scale(-1,1)); } } setVisible(showIt); } QPainterPath PartLabel::shape() const { QRectF t = boundingRect(); QPainterPath path; path.addRect(t); return path; } void PartLabel::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (!m_owner->isSelected()) { event->ignore(); return; } InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL && infographics->spaceBarIsPressed()) { m_spaceBarWasPressed = true; event->ignore(); return; } m_spaceBarWasPressed = false; if (!this->isSelected()) { this->setSelected(true); } m_doDrag = true; m_initialPosition = pos(); m_initialOffset = m_offset; } void PartLabel::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (!m_owner->isSelected()) { event->ignore(); return; } if (m_spaceBarWasPressed) { event->ignore(); return; } if (m_doDrag) { QPointF currentParentPos = mapToParent(mapFromScene(event->scenePos())); QPointF buttonDownParentPos = mapToParent(mapFromScene(event->buttonDownScenePos(Qt::LeftButton))); setPos(m_initialPosition + currentParentPos - buttonDownParentPos); m_offset = this->pos() - m_owner->pos(); return; } QGraphicsSvgItem::mouseMoveEvent(event); } void PartLabel::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (!m_owner->isSelected()) { event->ignore(); return; } if (m_spaceBarWasPressed) { event->ignore(); return; } if (m_doDrag) { m_owner->partLabelMoved(m_initialPosition, m_initialOffset, pos(), m_offset); } QGraphicsSvgItem::mouseReleaseEvent(event); } void PartLabel::setPlainText(const QString & text) { m_text = text; displayTexts(); } void PartLabel::displayTextsIf() { if (!m_initialized) return; if (m_hidden) return; if (!isVisible()) return; displayTexts(); } void PartLabel::displayTexts() { QStringList texts; foreach (QString key, m_displayKeys) { QString t; if (key.compare(LabelTextKey) == 0) { t = m_text; } else { t = m_owner->getProperty(key); } if (!t.isEmpty()) texts.append(t); } QString text = texts.join("\n"); if (text.length() == 0) { text = "?"; // make sure there's something visible } m_displayText = text; resetSvg(); } bool PartLabel::initialized() { return m_initialized; } void PartLabel::ownerMoved(QPointF newPos) { this->setPos(m_offset + newPos); } void PartLabel::setHidden(bool hide) { if (!m_initialized) return; m_hidden = hide; setHiddenOrInactive(); } void PartLabel::setInactive(bool inactivate) { if (!m_initialized) return; m_inactive = inactivate; setHiddenOrInactive(); } void PartLabel::setHiddenOrInactive() { bool hide = m_hidden || m_inactive || !m_owner->isSelected(); setAcceptedMouseButtons(hide ? Qt::NoButton : ALLMOUSEBUTTONS); setAcceptHoverEvents(!hide); update(); } ViewLayer::ViewLayerID PartLabel::viewLayerID() { return m_viewLayerID; } bool PartLabel::hidden() { return m_hidden; } bool PartLabel::inactive() { return m_inactive; } void PartLabel::saveInstance(QXmlStreamWriter & streamWriter) { if (!m_initialized) return; streamWriter.writeStartElement("titleGeometry"); streamWriter.writeAttribute("visible", isVisible() ? "true" : "false"); streamWriter.writeAttribute("x", QString::number(pos().x())); streamWriter.writeAttribute("y", QString::number(pos().y())); streamWriter.writeAttribute("z", QString::number(zValue())); streamWriter.writeAttribute("xOffset", QString::number(m_offset.x())); streamWriter.writeAttribute("yOffset", QString::number(m_offset.y())); streamWriter.writeAttribute("textColor", m_color.name()); streamWriter.writeAttribute("fontSize", QString::number(m_font.pointSizeF())); GraphicsUtils::saveTransform(streamWriter, transform()); foreach (QString key, m_displayKeys) { streamWriter.writeStartElement("displayKey"); streamWriter.writeAttribute("key", key); streamWriter.writeEndElement(); } streamWriter.writeEndElement(); } void PartLabel::restoreLabel(QDomElement & labelGeometry, ViewLayer::ViewLayerID viewLayerID) { m_viewLayerID = viewLayerID; m_initialized = true; m_owner->scene()->addItem(this); setVisible(labelGeometry.attribute("visible").compare("true") == 0); QPointF p = pos(); bool ok = false; double x = labelGeometry.attribute("x").toDouble(&ok); if (ok) p.setX(x); double y = labelGeometry.attribute("y").toDouble(&ok); if (ok) p.setY(y); setPos(p); x = labelGeometry.attribute("xOffset").toDouble(&ok); if (ok) m_offset.setX(x); y = labelGeometry.attribute("yOffset").toDouble(&ok); if (ok) m_offset.setY(y); double z = labelGeometry.attribute("z").toDouble(&ok); if (ok) this->setZValue(z); //ignore the textColor attribute so the labels are always set from standard colors //QColor c; //c.setNamedColor(labelGeometry.attribute("textColor")); //setBrush(QBrush(c)); setUpText(); m_initialized = true; double fs = labelGeometry.attribute("fontSize").toDouble(&ok); if (!ok) { InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL) { fs = infographics->getLabelFontSizeMedium(); ok = true; } } if (ok) { m_font.setPointSizeF(fs); } m_displayKeys.clear(); QDomElement displayKey = labelGeometry.firstChildElement("displayKey"); while (!displayKey.isNull()) { m_displayKeys.append(displayKey.attribute("key")); displayKey = displayKey.nextSiblingElement("displayKey"); } if (m_displayKeys.length() == 0) { m_displayKeys.append(LabelTextKey); if (m_owner->hasPartNumberProperty()) m_displayKeys.append(ModelPartShared::PartNumberPropertyName); } displayTexts(); QTransform t; if (GraphicsUtils::loadTransform(labelGeometry.firstChildElement("transform"), t)) { setTransform(t); } } void PartLabel::moveLabel(QPointF newPos, QPointF newOffset) { this->setPos(newPos); m_offset = newOffset; } ItemBase * PartLabel::owner() { return m_owner; } void PartLabel::initMenu() { // todo: make this a static var? QAction *editAct = m_menu.addAction(tr("Edit")); editAct->setData(QVariant(PartLabelEdit)); editAct->setStatusTip(tr("Edit label text")); QAction *hideAct = m_menu.addAction(tr("Hide")); hideAct->setData(QVariant(PartLabelHide)); hideAct->setStatusTip(tr("Hide part label")); m_menu.addSeparator(); QMenu * dvmenu = m_menu.addMenu(tr("Display Values")); QMenu * rlmenu = m_menu.addMenu(tr("Flip/Rotate")); QMenu * fsmenu = m_menu.addMenu(tr("Font Size")); bool include45 = (m_owner != NULL) && (m_owner->viewID() == ViewLayer::PCBView); if (include45) { QAction *rotate45cwAct = rlmenu->addAction(tr("Rotate 45° Clockwise")); rotate45cwAct->setData(QVariant(PartLabelRotate45CW)); rotate45cwAct->setStatusTip(tr("Rotate the label by 45 degrees clockwise")); } QAction *rotate90cwAct = rlmenu->addAction(tr("Rotate 90° Clockwise")); rotate90cwAct->setData(QVariant(PartLabelRotate90CW)); rotate90cwAct->setStatusTip(tr("Rotate the label by 90 degrees clockwise")); if (include45) { QAction *rotate135cwAct = rlmenu->addAction(tr("Rotate 135° Clockwise")); rotate135cwAct->setData(QVariant(PartLabelRotate135CW)); rotate135cwAct->setStatusTip(tr("Rotate the label by 135 degrees clockwise")); } QAction *rotate180Act = rlmenu->addAction(tr("Rotate 180°")); rotate180Act->setData(QVariant(PartLabelRotate180)); rotate180Act->setStatusTip(tr("Rotate the label by 180 degrees")); if (include45) { QAction *rotate135ccwAct = rlmenu->addAction(tr("Rotate 135° Counter Clockwise")); rotate135ccwAct->setData(QVariant(PartLabelRotate135CCW)); rotate135ccwAct->setStatusTip(tr("Rotate the label by 135 degrees counter clockwise")); } QAction *rotate90ccwAct = rlmenu->addAction(tr("Rotate 90° Counter Clockwise")); rotate90ccwAct->setData(QVariant(PartLabelRotate90CCW)); rotate90ccwAct->setStatusTip(tr("Rotate current selection 90 degrees counter clockwise")); if (include45) { QAction *rotate45ccwAct = rlmenu->addAction(tr("Rotate 45° Counter Clockwise")); rotate45ccwAct->setData(QVariant(PartLabelRotate45CCW)); rotate45ccwAct->setStatusTip(tr("Rotate the label by 45 degrees counter clockwise")); } QAction *flipHorizontalAct = rlmenu->addAction(tr("Flip Horizontal")); flipHorizontalAct->setData(QVariant(PartLabelFlipHorizontal)); flipHorizontalAct->setStatusTip(tr("Flip label horizontally")); QAction *flipVerticalAct = rlmenu->addAction(tr("Flip Vertical")); flipVerticalAct->setData(QVariant(PartLabelFlipVertical)); flipVerticalAct->setStatusTip(tr("Flip label vertically")); m_tinyAct = fsmenu->addAction(tr("Tiny")); m_tinyAct->setData(QVariant(PartLabelFontSizeTiny)); m_tinyAct->setStatusTip(tr("Set font size to tiny")); m_tinyAct->setCheckable(true); m_tinyAct->setChecked(false); m_smallAct = fsmenu->addAction(tr("Small")); m_smallAct->setData(QVariant(PartLabelFontSizeSmall)); m_smallAct->setStatusTip(tr("Set font size to small")); m_smallAct->setCheckable(true); m_smallAct->setChecked(false); m_mediumAct = fsmenu->addAction(tr("Medium")); m_mediumAct->setData(QVariant(PartLabelFontSizeMedium)); m_mediumAct->setStatusTip(tr("Set font size to medium")); m_mediumAct->setCheckable(true); m_mediumAct->setChecked(false); m_largeAct = fsmenu->addAction(tr("Large")); m_largeAct->setData(QVariant(PartLabelFontSizeLarge)); m_largeAct->setStatusTip(tr("Set font size to large")); m_largeAct->setCheckable(true); m_largeAct->setChecked(false); m_labelAct = dvmenu->addAction(tr("Label text")); m_labelAct->setData(QVariant(PartLabelDisplayLabelText)); m_labelAct->setCheckable(true); m_labelAct->setChecked(true); m_labelAct->setStatusTip(tr("Display the text of the label")); dvmenu->addSeparator(); QHash properties = m_owner->modelPart()->properties(); foreach (QString key, properties.keys()) { QString translatedName = ItemBase::translatePropertyName(key); QAction * action = dvmenu->addAction(translatedName); action->setData(QVariant(key)); action->setCheckable(true); action->setChecked(false); action->setStatusTip(tr("Display the value of property %1").arg(translatedName)); m_displayActs.append(action); } } void PartLabel::rotateFlipLabel(double degrees, Qt::Orientations orientation) { if (degrees != 0) { transformLabel(QTransform().rotate(degrees)); } else { int xScale, yScale; if (orientation == Qt::Vertical) { xScale = 1; yScale = -1; } else if(orientation == Qt::Horizontal) { xScale = -1; yScale = 1; } else return; transformLabel(QTransform().scale(xScale,yScale)); } } void PartLabel::transformLabel(QTransform currTransf) { QRectF rect = this->boundingRect(); double x = rect.width() / 2.0; double y = rect.height() / 2.0; QTransform transf = transform() * QTransform().translate(-x, -y) * currTransf * QTransform().translate(x, y); setTransform(transf); } void PartLabel::setUpText() { InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL) { infographics->getLabelFont(m_font, m_color, m_owner); } } QVariant PartLabel::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value) { switch (change) { case QGraphicsItem::ItemSceneHasChanged: if (this->scene()) { } break; default: break; } return QGraphicsSvgItem::itemChange(change, value); } void PartLabel::ownerSelected(bool selected) { bool hide = !selected; if (m_hidden || m_inactive) { hide = true; } setAcceptedMouseButtons(hide ? Qt::NoButton : ALLMOUSEBUTTONS); setAcceptHoverEvents(!hide); } void PartLabel::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { if (m_hidden || m_inactive || !m_owner->isSelected()) { event->ignore(); return; } if (m_menu.isEmpty()) { initMenu(); } m_labelAct->setChecked(m_displayKeys.contains(LabelTextKey)); foreach (QAction * displayAct, m_displayActs) { QString data = displayAct->data().toString(); displayAct->setChecked(m_displayKeys.contains(data)); } m_tinyAct->setChecked(false); m_smallAct->setChecked(false); m_mediumAct->setChecked(false); m_largeAct->setChecked(false); InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics != NULL) { int fs = m_font.pointSize(); if (fs == infographics->getLabelFontSizeTiny()) { m_tinyAct->setChecked(true); } else if (fs == infographics->getLabelFontSizeSmall()) { m_smallAct->setChecked(true); } else if (fs == infographics->getLabelFontSizeMedium()) { m_mediumAct->setChecked(true); } else if (fs == infographics->getLabelFontSizeLarge()) { m_largeAct->setChecked(true); } } QAction *selectedAction = m_menu.exec(event->screenPos()); if (selectedAction == NULL) return; PartLabelAction action = (PartLabelAction) selectedAction->data().toInt(); switch (action) { case PartLabelRotate45CW: case PartLabelRotate45CCW: case PartLabelRotate90CW: case PartLabelRotate90CCW: case PartLabelRotate135CW: case PartLabelRotate135CCW: case PartLabelRotate180: case PartLabelFlipHorizontal: case PartLabelFlipVertical: rotateFlip(action); break; case PartLabelEdit: partLabelEdit(); break; case PartLabelHide: partLabelHide(); break; case PartLabelFontSizeTiny: case PartLabelFontSizeSmall: case PartLabelFontSizeMedium: case PartLabelFontSizeLarge: setFontSize(action); resetSvg(); break; case PartLabelDisplayLabelText: setLabelDisplay(LabelTextKey); break; default: setLabelDisplay(selectedAction->data().toString()); break; } } void PartLabel::rotateFlip(int action) { double degrees = 0; Qt::Orientations orientation = 0; switch (action) { case PartLabelRotate45CW: degrees = 45; break; case PartLabelRotate90CW: degrees = 90; break; case PartLabelRotate135CW: degrees = 135; break; case PartLabelRotate90CCW: degrees = 270; break; case PartLabelRotate135CCW: degrees = 225; break; case PartLabelRotate180: degrees = 180; break; case PartLabelRotate45CCW: degrees = 315; break; case PartLabelFlipHorizontal: orientation = Qt::Horizontal; break; case PartLabelFlipVertical: orientation = Qt::Vertical; break; default: break; } m_owner->rotateFlipPartLabel(degrees, orientation); } void PartLabel::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { if (!m_owner->isSelected()) { event->ignore(); return; } m_doDrag = false; partLabelEdit(); } void PartLabel::partLabelHide() { m_owner->hidePartLabel(); } void PartLabel::partLabelEdit() { bool ok; QString oldText = m_text; QString text = QInputDialog::getText((QGraphicsView *) this->scene()->parent(), tr("Set label for %1").arg(m_owner->title()), tr("Label text:"), QLineEdit::Normal, oldText, &ok); if (ok && (oldText.compare(text) != 0)) { if (m_owner) { m_owner->partLabelChanged(text); foreach (PartLabel * p, AllPartLabels.values(m_owner->id())) { p->setPlainText(text); } } } } void PartLabel::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_hidden) return; if (m_inactive) { painter->save(); painter->setOpacity(InactiveOpacity); } if (m_owner->isSelected()) { GraphicsUtils::qt_graphicsItem_highlightSelected(painter, option, boundingRect(), shape()); } QGraphicsSvgItem::paint(painter, option, widget); if (m_inactive) { painter->restore(); } } void PartLabel::setFontSize(int action) { InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this); if (infographics == NULL) return; double fs = 0; switch (action) { case PartLabelFontSizeTiny: fs = infographics->getLabelFontSizeTiny(); break; case PartLabelFontSizeSmall: fs = infographics->getLabelFontSizeSmall(); break; case PartLabelFontSizeMedium: fs = infographics->getLabelFontSizeMedium(); break; case PartLabelFontSizeLarge: fs = infographics->getLabelFontSizeLarge(); break; default: return; } m_font.setPointSize(fs); } void PartLabel::setFontPointSize(double pointSize) { m_font.setPointSize(pointSize); resetSvg(); } void PartLabel::setLabelDisplay(const QString & key) { if (m_displayKeys.contains(key)) { m_displayKeys.removeOne(key); } else { m_displayKeys.append(key); } displayTexts(); } QString mapToSVGWeight(int w) { int percent = 50; switch (w) { case QFont::Light: percent = 25; break; case QFont::Normal: return "normal"; // 50 case QFont::DemiBold: percent = 63; break; case QFont::Bold: return "bold"; // 75 case QFont::Black: percent = 87; break; default: return "normal"; } return QString::number((qRound(8 * percent / 100.0) * 100) + 100); // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit } QString mapToSVGStyle(QFont::Style style) { switch (style) { case QFont::StyleNormal: return "normal"; case QFont::StyleOblique: return "oblique"; case QFont::StyleItalic: return "italic"; default: return "normal"; } } QString PartLabel::makeSvg(bool blackOnly, double dpi, double printerScale, bool includeTransform) { double w, h; QString svg = makeSvgAux(blackOnly, dpi, printerScale, w, h); if (includeTransform) { QTransform t = transform(); if (!t.isIdentity()) { svg = TextUtils::svgTransform(svg, t, false, QString()); } } return svg; } QString PartLabel::makeSvgAux(bool blackOnly, double dpi, double printerScale, double & w, double & h) { if (m_displayText.isEmpty()) return ""; double pixels = m_font.pointSizeF() * printerScale / 72; double y = pixels * 0.75; //DebugDialog::debug(QString("initial y:%1").arg(y)); QString svg = QString("") .arg(m_font.pointSizeF() * dpi / 72) .arg(mapToSVGStyle(m_font.style())) .arg(mapToSVGWeight(m_font.weight())) .arg(blackOnly ? "#000000" : m_color.name()) .arg(InstalledFonts::InstalledFontsNameMapper.value(m_font.family())) .arg(ViewLayer::viewLayerXmlNameFromID(m_viewLayerID) ); w = 0; QStringList texts = m_displayText.split("\n"); foreach (QString t, texts) { QString t1 = TextUtils::convertExtendedChars(TextUtils::escapeAnd(t)); svg += QString("%2") .arg(y * dpi / printerScale) .arg(t1); y += pixels; w = qMax(w, t.length() * pixels * 0.75); //DebugDialog::debug(QString("\t%1, %2").arg(w).arg(y)); } svg += ""; h = y - (pixels / 2); //QFontInfo fontInfo(m_font); //DebugDialog::debug(QString("%1 match:%2 ps:%3 sty:%4 w:%5") // .arg(fontInfo.family()) // .arg(fontInfo.exactMatch()) // .arg(fontInfo.pointSizeF()) // .arg(fontInfo.style()) // .arg(fontInfo.weight()) // ); return svg; //DebugDialog::debug(QString("final:%1 %2 %3").arg(w).arg(h).arg(m_font.toString())); } void PartLabel::resetSvg() { double w, h; QString innerSvg = makeSvgAux(false, GraphicsUtils::StandardFritzingDPI, GraphicsUtils::SVGDPI, w, h); if (innerSvg.isEmpty()) return; QString svg = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, w, h) + innerSvg + "\n"; if (m_renderer == NULL) { m_renderer = new QSvgRenderer(this); } // using renderer()->load() doesn't seem to work, so keep a separate shared renderer as a workaround bool loaded = m_renderer->load(svg.toUtf8()); if (loaded) { setSharedRenderer(m_renderer); } } fritzing-app-0.9.3b/src/items/partlabel.h000066400000000000000000000072161272363601300203370ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #ifndef PARTLABEL_H #define PARTLABEL_H #include #include #include #include #include #include #include #include #include #include #include #include "../viewlayer.h" class PartLabel : public QGraphicsSvgItem { Q_OBJECT public: PartLabel(class ItemBase * owner, QGraphicsItem * parent = 0 ); // itembase is not the parent ~PartLabel(); void setPlainText(const QString & text); void showLabel(bool showIt, ViewLayer *); QPainterPath shape() const; bool initialized(); void ownerMoved(QPointF newPos); void setHidden(bool hide); bool hidden(); void setInactive(bool inactivate); bool inactive(); ViewLayer::ViewLayerID viewLayerID(); void saveInstance(QXmlStreamWriter & streamWriter); void restoreLabel(QDomElement & labelGeometry, ViewLayer::ViewLayerID); void moveLabel(QPointF newPos, QPointF newOffset); class ItemBase * owner(); void rotateFlipLabel(double degrees, Qt::Orientations orientation); QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value); void ownerSelected(bool selected); void displayTexts(); void displayTextsIf(); QString makeSvg(bool blackOnly, double dpi, double printerScale, bool includeTransform); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void setFontPointSize(double pointSize); protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); void transformLabel(QTransform currTransf); void setUpText(); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void initMenu(); void partLabelEdit(); void setFontSize(int action); void rotateFlip(int action); void setLabelDisplay(const QString & key); void setHiddenOrInactive(); void partLabelHide(); void resetSvg(); QString makeSvgAux(bool blackOnly, double dpi, double printerScale, double & w, double & h); protected: QPointer m_owner; bool m_initialized; bool m_spaceBarWasPressed; bool m_doDrag; QPointF m_initialPosition; QPointF m_initialOffset; QPointF m_offset; ViewLayer::ViewLayerID m_viewLayerID; bool m_hidden; bool m_inactive; QMenu m_menu; QString m_text; QString m_displayText; QStringList m_displayKeys; QAction * m_tinyAct; QAction * m_smallAct; QAction * m_mediumAct; QAction * m_largeAct; QAction * m_labelAct; QList m_displayActs; QColor m_color; QFont m_font; QSvgRenderer * m_renderer; }; #endif fritzing-app-0.9.3b/src/items/perfboard.cpp000066400000000000000000000252051272363601300206660ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "perfboard.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/boundedregexpvalidator.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../commands.h" #include "../debugdialog.h" #include "moduleidnames.h" #include "partlabel.h" #include #include #include #include #include #include static const int ConnectorIDJump = 1000; static const int MaxXDimension = 199; static const int MinXDimension = 5; static const int MaxYDimension = 199; static const int MinYDimension = 5; static const int WarningSize = 2000; static const QString OneHole("M%1,%2a%3,%3 0 1 %5 %4,0 %3,%3 0 1 %5 -%4,0z\n"); bool Perfboard::m_gotWarning = false; ///////////////////////////////////////////////////////////////////// Perfboard::Perfboard( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_size = modelPart->localProp("size").toString(); if (m_size.isEmpty()) { m_size = modelPart->properties().value("size", "20.20"); modelPart->setLocalProp("size", m_size); } } Perfboard::~Perfboard() { } void Perfboard::setProp(const QString & prop, const QString & value) { if (prop.compare("size") != 0) { Capacitor::setProp(prop, value); return; } switch (this->m_viewID) { case ViewLayer::BreadboardView: if (value.compare(m_size) != 0) { QString temp = value; QString svg = makeBreadboardSvg(temp); reloadRenderer(svg, false); //DebugDialog::debug(svg); } break; default: break; } m_size = value; modelPart()->setLocalProp("size", value); if (m_partLabel) m_partLabel->displayTextsIf(); } QString Perfboard::makeBreadboardSvg(const QString & size) { QString BreadboardLayerTemplate = ""; QString ConnectorTemplate = ""; if (BreadboardLayerTemplate.isEmpty()) { QFile file(":/resources/templates/perfboard_boardLayerTemplate.txt"); file.open(QFile::ReadOnly); BreadboardLayerTemplate = file.readAll(); file.close(); } if (ConnectorTemplate.isEmpty()) { QFile file(":/resources/templates/perfboard_connectorTemplate.txt"); file.open(QFile::ReadOnly); ConnectorTemplate = file.readAll(); file.close(); } int x, y; getXY(x, y, size); QString middle; QString holes; double radius = 17.5; int sweepflag = 0; int top = 100; for (int iy = 0; iy < y; iy++) { int left = 100; for (int jx = 0; jx < x; jx++) { middle += ConnectorTemplate.arg(left).arg(top).arg(jx).arg(iy).arg(QString::number((iy * ConnectorIDJump) + jx)); holes += OneHole .arg(left - radius) .arg(top) .arg(radius) .arg(2 * radius) .arg(sweepflag); left += 100; } top += 100; } QString svg = BreadboardLayerTemplate .arg((x / 10.0) + 0.1) .arg((y / 10.0) + 0.1) .arg((x * 100) + 100) .arg((y * 100) + 100) .arg(holes) .arg(x * 100 - 8 + 100) .arg(y * 100 - 8 + 100) .arg(middle); return svg; } QString Perfboard::genFZP(const QString & moduleid) { QString ConnectorFzpTemplate = ""; QString FzpTemplate = ""; if (ConnectorFzpTemplate.isEmpty()) { QFile file(":/resources/templates/perfboard_connectorFzpTemplate.txt"); file.open(QFile::ReadOnly); ConnectorFzpTemplate = file.readAll(); file.close(); } if (FzpTemplate.isEmpty()) { QFile file(":/resources/templates/perfboard_fzpTemplate.txt"); file.open(QFile::ReadOnly); FzpTemplate = file.readAll(); file.close(); } int x, y; getXY(x, y, moduleid); QString middle; for (int iy = 0; iy < y; iy++) { for (int jx = 0; jx < x; jx++) { middle += ConnectorFzpTemplate.arg(jx).arg(iy).arg(QString::number((iy * ConnectorIDJump) + jx)); } } return FzpTemplate.arg(x).arg(y).arg(middle); } bool Perfboard::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("size", Qt::CaseInsensitive) == 0) { returnProp = tr("size"); returnValue = m_size; m_propsMap.insert("size", m_size); int x, y; getXY(x, y, m_size); QFrame * frame = new QFrame(); QVBoxLayout * vboxLayout = new QVBoxLayout(); vboxLayout->setAlignment(Qt::AlignLeft); vboxLayout->setSpacing(1); vboxLayout->setContentsMargins(0, 3, 0, 0); vboxLayout->setMargin(0); QFrame * subframe1 = new QFrame(); QHBoxLayout * hboxLayout1 = new QHBoxLayout(); hboxLayout1->setAlignment(Qt::AlignLeft); hboxLayout1->setContentsMargins(0, 0, 0, 0); hboxLayout1->setSpacing(2); QLabel * l1 = new QLabel(getColumnLabel()); l1->setMargin(0); l1->setObjectName("infoViewLabel"); m_xEdit = new QLineEdit(); m_xEdit->setEnabled(swappingEnabled); QIntValidator * validator = new QIntValidator(m_xEdit); validator->setRange(MinXDimension, MaxXDimension); m_xEdit->setObjectName("infoViewLineEdit"); m_xEdit->setValidator(validator); m_xEdit->setMaxLength(5); m_xEdit->setText(QString::number(x)); QFrame * subframe2 = new QFrame(); QHBoxLayout * hboxLayout2 = new QHBoxLayout(); hboxLayout2->setAlignment(Qt::AlignLeft); hboxLayout2->setContentsMargins(0, 0, 0, 0); hboxLayout2->setSpacing(2); QLabel * l2 = new QLabel(getRowLabel()); l2->setMargin(0); l2->setObjectName("infoViewLabel"); m_yEdit = new QLineEdit(); m_yEdit->setEnabled(swappingEnabled); validator = new QIntValidator(m_yEdit); validator->setRange(MinYDimension, MaxYDimension); m_yEdit->setObjectName("infoViewLineEdit"); m_yEdit->setValidator(validator); m_yEdit->setMaxLength(5); m_yEdit->setText(QString::number(y)); hboxLayout1->addWidget(l1); hboxLayout1->addWidget(m_xEdit); hboxLayout2->addWidget(l2); hboxLayout2->addWidget(m_yEdit); subframe1->setLayout(hboxLayout1); subframe2->setLayout(hboxLayout2); if (returnWidget != NULL) vboxLayout->addWidget(qobject_cast(returnWidget)); vboxLayout->addWidget(subframe1); vboxLayout->addWidget(subframe2); m_setButton = new QPushButton (tr("set board size")); m_setButton->setObjectName("infoViewButton"); connect(m_setButton, SIGNAL(pressed()), this, SLOT(changeBoardSize())); m_setButton->setEnabled(false); vboxLayout->addWidget(m_setButton); connect(m_xEdit, SIGNAL(editingFinished()), this, SLOT(enableSetButton())); connect(m_yEdit, SIGNAL(editingFinished()), this, SLOT(enableSetButton())); frame->setLayout(vboxLayout); returnWidget = frame; return true; } return Capacitor::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void Perfboard::addedToScene(bool temporary) { if (this->scene()) { QString temp = m_size; m_size = ""; setProp("size", temp); } return Capacitor::addedToScene(temporary); } bool Perfboard::canEditPart() { return false; } void Perfboard::changeBoardSize() { if (!m_gotWarning) { int x = m_xEdit->text().toInt(); int y = m_yEdit->text().toInt(); if (x * y >= WarningSize) { m_gotWarning = true; QMessageBox messageBox(NULL); messageBox.setWindowTitle(tr("Performance Warning")); messageBox.setText(tr("Performance of perfboards and stripboards with more than approximately 2000 holes can be slow. Are you sure ?\n" "\nNote: this warning will not be repeated during this session." )); messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); messageBox.setDefaultButton(QMessageBox::Cancel); messageBox.setIcon(QMessageBox::Warning); messageBox.setWindowModality(Qt::WindowModal); messageBox.setButtonText(QMessageBox::Ok, tr("Set new size")); messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel")); QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec(); if (answer != QMessageBox::Ok) { getXY(x, y, m_size); m_xEdit->setText(QString::number(x)); m_yEdit->setText(QString::number(y)); return; } } } QString newSize = QString("%1.%2").arg(m_xEdit->text()).arg(m_yEdit->text()); m_propsMap.insert("size", newSize); foreach (QString key, m_propsMap.keys()) { DebugDialog::debug("prop " + key + " " + m_propsMap.value(key)); } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->swap(family(), "size", m_propsMap, this); } } ItemBase::PluralType Perfboard::isPlural() { return Plural; } void Perfboard::enableSetButton() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; int x, y; getXY(x, y, m_size); int vx = m_xEdit->text().toInt(); int vy = m_yEdit->text().toInt(); m_setButton->setEnabled(vx != x || vy != y); } QString Perfboard::genModuleID(QMap & currPropsMap) { QString size = currPropsMap.value("size"); return size + ModuleIDNames::PerfboardModuleIDName; } bool Perfboard::getXY(int & x, int & y, const QString & s) { QRegExp re("(\\d+)\\.(\\d+)"); int ix = re.indexIn(s); if (ix < 0) return false; bool ok; x = re.cap(1).toInt(&ok); if (!ok) return false; y = re.cap(2).toInt(&ok); return ok; } bool Perfboard::rotation45Allowed() { return false; } void Perfboard::hoverUpdate() { } void Perfboard::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); } bool Perfboard::stickyEnabled() { return false; } bool Perfboard::canFindConnectorsUnder() { return false; } QString Perfboard::getRowLabel() { return tr("rows"); } QString Perfboard::getColumnLabel() { return tr("columns"); } fritzing-app-0.9.3b/src/items/perfboard.h000066400000000000000000000051301272363601300203260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef PERFBOARD_H #define PERFBOARD_H #include #include #include #include #include #include #include "capacitor.h" class Perfboard : public Capacitor { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Perfboard(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Perfboard(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool canEditPart(); PluralType isPlural(); void addedToScene(bool temporary); void setProp(const QString & prop, const QString & value); void hoverUpdate(); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool stickyEnabled(); bool canFindConnectorsUnder(); bool rotation45Allowed(); protected: virtual QString getRowLabel(); virtual QString getColumnLabel(); public: static QString genFZP(const QString & moduleID); static QString makeBreadboardSvg(const QString & size); static QString genModuleID(QMap & currPropsMap); protected slots: void changeBoardSize(); void enableSetButton(); protected: static bool getXY(int & x, int & y, const QString & s); protected: static bool m_gotWarning; protected: QString m_size; QPointer m_xEdit; QPointer m_yEdit; QPointer m_setButton; }; #endif fritzing-app-0.9.3b/src/items/pinheader.cpp000066400000000000000000001204121272363601300206550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "pinheader.h" #include "../utils/graphicsutils.h" #include "../fsvgrenderer.h" #include "../commands.h" #include "../utils/textutils.h" #include "partlabel.h" #include "partfactory.h" #include "../sketch/infographicsview.h" #include "../connectors/connectoritem.h" #include "../connectors/connector.h" #include "../utils/familypropertycombobox.h" #include #include #include #include #include ////////////////////////////////////////////////// QString doubleCopyPinFunction(int pin, const QString & argString, void *) { return argString.arg(pin * 2).arg(pin * 2 + 1).arg(pin * 2 + 1).arg(pin * 2 + 2); } QString stdIncCopyPinFunction(int pin, const QString & argString, void *) { return argString.arg(pin).arg(pin + 1); } ////////////////////////////////////////////////// static QStringList Forms; QString PinHeader::FemaleFormString; QString PinHeader::FemaleRoundedFormString; QString PinHeader::MaleFormString; QString PinHeader::ShroudedFormString; QString PinHeader::LongPadFormString; QString PinHeader::MolexFormString; static int MinPins = 1; static int MinShroudedPins = 6; static int MaxPins = 64; static QMap Spacings; static QString ShroudedSpacing; static HoleClassThing TheHoleThing; PinHeader::PinHeader( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { setUpHoleSizes("pinheader", TheHoleThing); m_form = modelPart->localProp("form").toString(); if (m_form.isEmpty()) { m_form = modelPart->properties().value("form", FemaleFormString); modelPart->setLocalProp("form", m_form); } } PinHeader::~PinHeader() { } void PinHeader::initNames() { if (FemaleFormString.isEmpty()) { FemaleFormString = FemaleSymbolString + " (female)"; FemaleRoundedFormString = FemaleSymbolString + " (female rounded)"; MaleFormString = MaleSymbolString + " (male)"; ShroudedFormString = MaleSymbolString + " (shrouded male)"; LongPadFormString = "long pad"; MolexFormString = "molex"; } } QStringList PinHeader::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("form", Qt::CaseInsensitive) == 0) { QStringList values; foreach (QString f, forms()) { values.append(f); } value = m_form; return values; } if (prop.compare("rows", Qt::CaseInsensitive) == 0) { QStringList values; values.append("single"); values.append("double"); value = (moduleID().contains("double") || moduleID().contains("shrouded")) ? "double" : "single"; return values; } if (prop.compare("position", Qt::CaseInsensitive) == 0) { QStringList values; values.append("center"); values.append("alternating"); value = (moduleID().contains("alternating")) ? "alternating" : "center"; return values; } if (prop.compare("package", Qt::CaseInsensitive) == 0) { QStringList values; values.append("through-hole"); values.append("SMD"); value = moduleID().contains("smd") ? "SMD" : "through-hole"; return values; } if (prop.compare("pins", Qt::CaseInsensitive) == 0) { QStringList values; value = modelPart()->properties().value("pins"); int step = 1; int minP = MinPins; if (m_form.contains("shrouded")) { minP = MinShroudedPins; step = 2; } if (m_form.contains("double")) { step = 2; minP = 2; } for (int i = minP; i <= MaxPins; i += step) { values << QString::number(i); } return values; } if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) { initSpacings(); QStringList values; value = modelPart()->properties().value("pin spacing"); if (m_form.contains("shrouded")) { values.append(value); } else { foreach (QString key, Spacings.keys()) { values.append(Spacings.value(key)); } } return values; } return PaletteItem::collectValues(family, prop, value); } QString PinHeader::getProperty(const QString & key) { if (key.compare("form", Qt::CaseInsensitive) == 0) { return m_form; } return PaletteItem::getProperty(key); } void PinHeader::addedToScene(bool temporary) { if (this->scene()) { //setForm(m_form, true); } return PaletteItem::addedToScene(temporary); } const QString & PinHeader::form() { return m_form; } const QStringList & PinHeader::forms() { if (Forms.count() == 0) { Forms << FemaleFormString << FemaleRoundedFormString << MaleFormString << ShroudedFormString << LongPadFormString << MolexFormString; } return Forms; } ItemBase::PluralType PinHeader::isPlural() { return Plural; } QString PinHeader::genFZP(const QString & moduleID) { initSpacings(); QString useModuleID = moduleID; int hsix = useModuleID.lastIndexOf(HoleSizePrefix); if (hsix >= 0) useModuleID.truncate(hsix); QStringList pieces = useModuleID.split("_"); QString spacing = pieces.at(pieces.count() - 1); QString result = PaletteItem::genFZP(useModuleID, "generic_female_pin_header_fzpTemplate", MinPins, MaxPins, 1, useModuleID.contains("smd")); result.replace(".percent.", "%"); QString form = MaleFormString; QString formBread = "male"; QString formText = formBread; QString formSchematic = formBread; QString formModule = formBread; QString formPackage = useModuleID.contains("smd", Qt::CaseInsensitive) ? "smd" : "tht"; QString formPosition = useModuleID.contains("alternating", Qt::CaseInsensitive) ? "alternating" : ""; bool isDouble = useModuleID.contains("shrouded") || useModuleID.contains("double"); QString formRow = isDouble ? "double" : "single"; if (useModuleID.contains("rounded")) { form = FemaleRoundedFormString; if (useModuleID.contains("smd")) { formText = formRow + " row SMD rounded female"; formModule = formText.toLower(); formModule.replace(' ', '_'); formBread = "rounded_female"; } else { if (isDouble) { formBread = formModule = "double_row_rounded_female"; formText = "double row rounded female"; } else { formBread = formModule = "rounded_female"; formText = "rounded female"; if (!formPosition.isEmpty()) { formText += " alternating"; formModule += "_alternating"; } } } formSchematic = "female"; } else if (useModuleID.contains("longpad")) { form = LongPadFormString; formBread = formModule = "longpad"; formText = "longpad"; if (!formPosition.isEmpty()) { formText += " alternating"; formModule += "_alternating"; } } else if (useModuleID.contains("molex")) { form = MolexFormString; formBread = formModule = "molex"; formText = "molex"; } else if (useModuleID.contains("female")) { form = FemaleFormString; if (useModuleID.contains("smd")) { formText = formRow + " row SMD female"; formModule = formText.toLower(); formModule.replace(' ', '_'); formBread = "female"; } else { if (isDouble) { formBread = formModule = "double_row_female"; formText = "double row female"; } else { formBread = formModule = formText = "female"; if (!formPosition.isEmpty()) { formText += " alternating"; formModule += "_alternating"; } } } formSchematic = "female"; } else if (useModuleID.contains("shrouded")) { form = ShroudedFormString; formText = formBread = formModule = "shrouded"; } else if (useModuleID.contains("smd")) { formText = formRow + " row SMD male"; formModule = formText.toLower(); formModule.replace(' ', '_'); } else if (isDouble) { formBread = formModule = "double_row_male"; formText = "double row male"; } else if (!formPosition.isEmpty()) { formText += " alternating"; formModule += "_alternating"; } result = result .arg(Spacings.value(spacing, "")) .arg(spacing) .arg(form) .arg(formBread) .arg(formText) .arg(formSchematic) .arg(formModule) .arg(formRow) .arg(formPackage) .arg(formPosition); ; if (useModuleID.contains("smd")) { result.replace("nsjumper", QString("smd_%1_row_pin_header").arg(formRow)); result.replace("jumper", QString("smd_%1_row_pin_header").arg(formRow)); } else if (useModuleID.contains("shrouded")) { result.replace("nsjumper", "shrouded"); result.replace("jumper", "shrouded"); } else if (useModuleID.contains("longpad")) { result.replace("nsjumper", formModule); result.replace("jumper", formModule); } else if (useModuleID.contains("molex")) { result.replace("nsjumper", "molex"); result.replace("jumper", "molex"); } else if (isDouble) { result.replace("jumper", "jumper_double"); } else if (!formPosition.isEmpty()) { result.replace("nsjumper", "nsjumper_alternating"); result.replace("jumper", "jumper_alternating"); } if (hsix >= 0) { return hackFzpHoleSize(result, moduleID, hsix); } return result; } QString PinHeader::genModuleID(QMap & currPropsMap) { initSpacings(); QString pins = currPropsMap.value("pins"); int p = pins.toInt(); QString spacing = currPropsMap.value("pin spacing"); if (spacing.isEmpty()) spacing = ShroudedSpacing; QString form = currPropsMap.value("form").toLower(); QString package = currPropsMap.value("package").toLower(); QString row = currPropsMap.value("row").toLower(); QString position = currPropsMap.value("position").toLower(); QString formWord = "male"; bool isDouble = false; if (form.contains("shrouded")) { if (p < MinShroudedPins) { pins = QString::number(p = MinShroudedPins); } isDouble = true; spacing = ShroudedSpacing; formWord = "shrouded"; } else if (form.contains("long pad")) { formWord = "longpad"; if (position.contains("alternating")) { formWord += "_alternating"; } } else if (form.contains("molex")) { formWord = "molex"; } else if (form.contains("female")) { QString ff = form.contains("rounded") ? "rounded_female" : "female"; if (package.contains("smd")) { if (row.contains("single", Qt::CaseInsensitive)) { formWord = "single_row_smd_" + ff; } else { isDouble = true; formWord = "double_row_smd_" + ff; } } else { if (row.contains("single", Qt::CaseInsensitive)) { formWord = ff; if (position.contains("alternating")) { formWord += "_alternating"; } } else { isDouble = true; formWord = "double_row_" + ff; } } } else if (package.contains("smd")) { if (row.contains("single")) { formWord = "single_row_smd_male"; } else { isDouble = true; formWord = "double_row_smd_male"; } } else if (row.contains("double", Qt::CaseInsensitive)) { formWord = "double_row_male"; isDouble = true; } else if (position.contains("alternating")) { formWord += "_alternating"; } if (isDouble && (p % 2 == 1)) { pins = QString::number(p + 1); } foreach (QString key, Spacings.keys()) { if (Spacings.value(key).compare(spacing, Qt::CaseInsensitive) == 0) { return QString("generic_%1_pin_header_%2_%3").arg(formWord).arg(pins).arg(key); } } return ""; } QString PinHeader::makePcbSvg(const QString & originalExpectedFileName) { QString expectedFileName = originalExpectedFileName; int hsix = expectedFileName.indexOf(HoleSizePrefix); if (hsix >= 0) { expectedFileName.truncate(hsix); } initSpacings(); if (expectedFileName.contains("smd")) { return makePcbSMDSvg(expectedFileName); } QString spacingString; int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString); if (pins == 0) return ""; // only one spacing for mystery parts. if (expectedFileName.contains("mystery")) spacingString = "100mil"; QString svg; if (expectedFileName.contains("shrouded")) { svg = makePcbShroudedSvg(pins); } else if (expectedFileName.contains("longpad")) { svg = makePcbLongPadSvg(pins, expectedFileName.contains("alternating")); } else if (expectedFileName.contains("molex")) { svg = makePcbMolexSvg(pins, spacingString); } else { static QString pcbLayerTemplate = ""; static QString pcbLayerTemplate2 = ""; if (pcbLayerTemplate.isEmpty()) { QFile file(":/resources/templates/jumper_pcb_svg_template.txt"); file.open(QFile::ReadOnly); pcbLayerTemplate = file.readAll(); file.close(); QFile file2(":/resources/templates/jumper_pcb_svg_2nd_template.txt"); file2.open(QFile::ReadOnly); pcbLayerTemplate2 = file2.readAll(); file2.close(); } bool isDouble = expectedFileName.contains("double"); QString useTemplate = isDouble ? pcbLayerTemplate2 : pcbLayerTemplate; double outerBorder = 15; double innerBorder = outerBorder / 2; double silkStrokeWidth = 10; double standardRadius = 27.5; double radius = 29; double copperStrokeWidth = 20; double totalWidth = (outerBorder * 2) + (silkStrokeWidth * 2) + (innerBorder * 2) + (standardRadius * 2) + copperStrokeWidth; double center = totalWidth / 2; double spacing = TextUtils::convertToInches(spacingString) * GraphicsUtils::StandardFritzingDPI; QString middle; bool addSquare = false; if (expectedFileName.contains("nsjumper")) { } else if (expectedFileName.contains("jumper")) { addSquare = true; } else { DebugDialog::debug(QString("square: expected filename is confusing %1").arg(expectedFileName)); } double lockOffset = 5; // mils double useLock = 0; if (!isDouble && expectedFileName.contains("alternating")) { useLock = lockOffset; } if (addSquare) { middle += QString( "\n") .arg(radius * 2) .arg(copperStrokeWidth) .arg(center - radius + useLock) .arg(center - radius); } QString circle("\n"); if (isDouble) { for (int i = 0; i < pins / 2; i++) { middle += circle .arg(center) .arg(center + (i * spacing)) .arg(i) .arg(radius) .arg(copperStrokeWidth); } for (int i = 0; i < pins / 2; i++) { middle += circle .arg(center) .arg(center + (i * spacing)) .arg(i) .arg(radius) .arg(copperStrokeWidth); } for (int i = pins / 2; i < pins; i++) { middle += circle .arg(center + 100) .arg(center + ((pins - i - 1) * spacing)) .arg(i) .arg(radius) .arg(copperStrokeWidth); } } else { for (int i = 0; i < pins; i++) { middle += circle .arg(center + (i % 2 == 0 ? useLock : -useLock)) .arg(center + (i * spacing)) .arg(i) .arg(radius) .arg(copperStrokeWidth); } } double totalHeight = totalWidth + (pins * spacing) - spacing; double originalTotalWidth = totalWidth; if (isDouble) { totalHeight = totalWidth + (spacing * pins / 2) - spacing; totalWidth += 100; } svg = useTemplate .arg(totalWidth / GraphicsUtils::StandardFritzingDPI) .arg(totalHeight / GraphicsUtils::StandardFritzingDPI) .arg(totalWidth) .arg(totalHeight) .arg(totalWidth - outerBorder - (silkStrokeWidth / 2)) .arg(totalHeight - outerBorder - (silkStrokeWidth / 2)) .arg(originalTotalWidth - outerBorder - (silkStrokeWidth / 2)) .arg(silkStrokeWidth) .arg(silkStrokeWidth / 2) .arg(middle); } if (hsix >= 0) { return hackSvgHoleSizeAux(svg, originalExpectedFileName); } return svg; } void PinHeader::initSpacings() { if (Spacings.count() == 0) { ShroudedSpacing = "0.1in (2.54mm)"; Spacings.insert("1mm", "0.03937in (1.0mm)"); Spacings.insert("1.25mm", "0.04921in (1.25mm)"); Spacings.insert("50mil", "0.05in (1.27mm)"); Spacings.insert("1.5mm", "0.0591in (1.5mm)"); Spacings.insert("2mm", "0.07874in (2mm)"); Spacings.insert("2.5mm", "0.09843in (2.5mm)"); Spacings.insert("100mil", ShroudedSpacing); Spacings.insert("150mil", "0.15in (3.81mm)"); Spacings.insert("0.156in", "0.156in (3.96mm)"); Spacings.insert("200mil", "0.2in (5.08mm)"); } } QString PinHeader::makeSchematicSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); if (pieces.count() < 7) return ""; bool useOldSchematic = expectedFileName.contains(PartFactory::OldSchematicPrefix); QString spacingString; int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString); QString form = expectedFileName.contains("female") ? "female" :"male"; bool sizeTenth = expectedFileName.contains("10thin") || !useOldSchematic; // some parts before version 0.8.5 used the 0.1 grid bool isDouble = sizeTenth && expectedFileName.contains("double"); double width, unitHeight; double divisor = isDouble ? 2 : 1; if (sizeTenth) { width = isDouble ? 0.5 : 0.2; unitHeight = GraphicsUtils::StandardSchematicSeparation10thinMils / 1000; // inches } else { unitHeight = GraphicsUtils::StandardSchematicSeparationMils / 1000; // inches width = 0.87; } double unitHeightPoints = unitHeight * 72; QString header("\n" "\n" "\n"); QString svg = header.arg(unitHeight * pins / divisor).arg(unitHeightPoints * pins / divisor).arg(width).arg(width * 72); QString templateFile = QString(":/resources/templates/generic_%1_%2%3pin_header_schem_template.txt") .arg(form.contains("female") ? "female" : "male") .arg(sizeTenth ? "10thin_" : "") .arg(isDouble ? "double_" : "") ; if (sizeTenth) { if (isDouble) { svg += TextUtils::incrementTemplate(templateFile, pins / 2, unitHeightPoints, TextUtils::standardMultiplyPinFunction, doubleCopyPinFunction, NULL); } else { svg += TextUtils::incrementTemplate(templateFile, pins, unitHeightPoints, TextUtils::standardMultiplyPinFunction, stdIncCopyPinFunction, NULL); } } else { svg += TextUtils::incrementTemplate(templateFile, pins, unitHeightPoints, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); } svg += "\n"; return svg; } QString PinHeader::makeBreadboardSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); if (pieces.count() < 7) return ""; int pinIndex = pieces.count() - 3; int pins = pieces.at(pinIndex).toInt(); if (expectedFileName.contains("shrouded")) { return makeBreadboardShroudedSvg(pins); } if (expectedFileName.contains("double") && !expectedFileName.contains("smd", Qt::CaseInsensitive)) { //return makeBreadboardDoubleSvg(expectedFileName, pins); } double unitHeight = 0.1; // inches double unitHeightPoints = unitHeight * 10000; QString header("\n" "\n" "\n"); QString fileForm; if (expectedFileName.contains("round")) { fileForm = "rounded_female"; header += "\n"; } else if (expectedFileName.contains("female")) { fileForm = "female"; header += "\n"; } else { fileForm = "male"; } QString svg = header.arg(unitHeight * pins).arg(unitHeightPoints * pins); svg += TextUtils::incrementTemplate(QString(":/resources/templates/generic_%1_pin_header_bread_template.txt").arg(fileForm), pins, unitHeightPoints, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); svg += "\n"; return svg; } QString PinHeader::makeBreadboardDoubleSvg(const QString & expectedFileName, int pins) { QString header("\n" "\n" "\n"); QString fileForm; if (expectedFileName.contains("round")) { fileForm = "rounded_female"; header += "\n"; } else if (expectedFileName.contains("female")) { fileForm = "female"; header += "\n"; } else { fileForm = "male"; } double unitHeight = 0.1; // inches double unitHeightPoints = unitHeight * 10000; int userData[2]; userData[0] = pins; userData[1] = 1; QString svg = header.arg(unitHeight * pins / 2).arg(unitHeightPoints * pins / 2); svg += TextUtils::incrementTemplate(QString(":/resources/templates/generic_%1_pin_header_bread_template.txt").arg(fileForm), pins / 2, unitHeightPoints, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData); svg += TextUtils::incrementTemplate(QString(":/resources/templates/generic_%1_pin_header_bread_2nd_template.txt").arg(fileForm), pins / 2, unitHeightPoints, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); svg += "\n"; return svg; } QString PinHeader::makeBreadboardShroudedSvg(int pins) { QString header("\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "%2\n" "%3\n" "\n" "\n" "\n" ); QString repeatT("\n" "\n" "\n" "\n" "\n" "\n" ); QString repeatB("\n" "\n" "\n" "\n" "\n" "\n" ); double increment = 100; // 0.1in QString svg = TextUtils::incrementTemplateString(header, 1, increment * (pins - 2) / 2, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); svg.replace("{", "["); svg.replace("}", "]"); svg = TextUtils::incrementTemplateString(svg, 1, increment * (pins - 2) / 4, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); int userData[2]; userData[0] = pins; userData[1] = 1; QString repeatTs = TextUtils::incrementTemplateString(repeatT, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData); QString repeatBs = TextUtils::incrementTemplateString(repeatB, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 1000.0).arg(repeatTs).arg(repeatBs); } QString PinHeader::makePcbShroudedSvg(int pins) { QString header("\n" "" "\n" "\n" "\n" "%2\n" "%3\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" ); QString repeatL = "\n"; QString repeatR = "\n"; double increment = 1000; // 0.1in QString svg = TextUtils::incrementTemplateString(header, 1, increment * (pins - 2) / 2, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); svg.replace("{", "["); svg.replace("}", "]"); svg = TextUtils::incrementTemplateString(svg, 1, increment * (pins - 2) / 4, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); int userData[2]; userData[0] = pins; userData[1] = 1; QString repeatLs = TextUtils::incrementTemplateString(repeatR, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData); QString repeatRs = TextUtils::incrementTemplateString(repeatL, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); return svg.arg(TextUtils::getViewBoxCoord(svg, 3) / 10000.0).arg(repeatLs).arg(repeatRs); } QString PinHeader::makePcbLongPadSvg(int pins, bool lock) { if (lock) return makePcbLongPadLockSvg(pins); double dpi = 25.4; double originalHeight = 0.108; // inches double increment = 0.1; // inches QString header("\n" "\n" "\n" "\n" "%3\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" ); double cy = 1.3716; double cy2 = 0.8128; QString repeat = "\n" "\n"; QString repeats; for (int i = 0; i < pins; i++) { repeats += repeat.arg(i).arg(cy).arg(cy2); cy += increment * dpi; cy2 += increment * dpi; } double totalHeight = originalHeight + ((pins - 1) * increment); double lineOffset = 0.1016; // already in dpi return header.arg(totalHeight).arg(totalHeight * dpi).arg(repeats).arg(totalHeight * dpi - lineOffset); } QString PinHeader::makePcbLongPadLockSvg(int pins) { double dpi = 25.4; double originalHeight = 0.108; // inches double increment = 0.1; // inches QString header("\n" "\n" "\n" "\n" "%3\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "%4\n" "%5\n" "\n" "\n" ); QString bottom("\n" "\n" "\n" "\n" "\n"); bottom = TextUtils::incrementTemplateString(bottom, 1, increment * dpi * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); QString between("\n"); QString betweens = TextUtils::incrementTemplateString(between, pins - 1, increment * dpi, TextUtils::standardMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); double ncy = 1.3716; double ncx = 1.524; double ncy2 = 0.8636; double ncx2 = 0; double offset = 0.254; QString repeat("\n" "\n"); QString repeats; for (int i = 0; i < pins; i++) { double useOffset = (i % 2 == 1) ? offset : 0; repeats += repeat.arg(i).arg(ncy).arg(ncx + useOffset).arg(ncx2 + useOffset).arg(ncy2); ncy += increment * dpi; ncy2 += increment * dpi; } double totalHeight = originalHeight + ((pins - 1) * increment); return header.arg(totalHeight).arg(totalHeight * dpi).arg(repeats).arg(bottom).arg(betweens); } QString PinHeader::makePcbMolexSvg(int pins, const QString & spacingString) { double dpi = 25.4; double originalHeight = 0.105; // inches double increment = 0.1; // inches bool ok; double inc = TextUtils::convertToInches(spacingString, &ok, false); if (ok) increment = inc; QString header("\n" "\n" "\n" "\n" "\n" "%3\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" ); double cy = 1.3335; // already in dpi QString repeat = "\n"; QString repeats; for (int i = 0; i < pins; i++) { repeats += repeat.arg(i).arg(cy + (i * increment * dpi)); } double totalHeight = originalHeight + ((pins - 1) * increment); double strokeWidth = .127; // already in dpi double h = 0.0001; return header.arg(totalHeight).arg(totalHeight * dpi).arg(repeats).arg((totalHeight * dpi) - strokeWidth).arg(qMax(h, (totalHeight * dpi) - cy - cy)); } QString PinHeader::makePcbSMDSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); int pins = pieces.at(5).toInt(); QString spacingString = pieces.at(6); bool singleRow = expectedFileName.contains("single"); double spacing = TextUtils::convertToInches(spacingString) * GraphicsUtils::StandardFritzingDPI; QString header("\n" "\n" "\n" "\n" "\n" "\n"); double baseWidth = 152.7559; // mils double totalHeight = 283.4646; double totalWidth = baseWidth + ((pins - 1) * spacing); double rectHeight = 102.047256; double y = 141.823; if (!singleRow) { totalHeight = 393.7; totalWidth = baseWidth + ((pins - 2) * spacing / 2); rectHeight = 200; y = 251.9685; } QString svg = header.arg(totalWidth / GraphicsUtils::StandardFritzingDPI) .arg(totalWidth) .arg(totalWidth - 4) .arg(totalHeight / GraphicsUtils::StandardFritzingDPI) .arg(totalHeight) .arg(rectHeight); double x = 51.18110; if (singleRow) { for (int i = 0; i < pins; i++) { double ay = (i % 2 == 0) ? 0 : y; svg += QString("\n").arg(i).arg(x).arg(ay); x += spacing; } } else { double holdX = x; for (int i = 0; i < pins / 2; i++) { svg += QString("\n").arg(pins - 1 - i).arg(x); x += spacing; } x = holdX; for (int i = 0; i < pins / 2; i++) { svg += QString("\n").arg(i).arg(x).arg(y); x += spacing; } } svg += "\n"; return svg; } bool PinHeader::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("hole size", Qt::CaseInsensitive) == 0) { if (moduleID().contains("smd", Qt::CaseInsensitive)) { // or call the ancestor function? return false; } else { return collectHoleSizeInfo(TheHoleThing.holeSizeValue, parent, swappingEnabled, returnProp, returnValue, returnWidget); } } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void PinHeader::swapEntry(const QString & text) { generateSwap(text, genModuleID, genFZP, makeBreadboardSvg, makeSchematicSvg, makePcbSvg); PaletteItem::swapEntry(text); } fritzing-app-0.9.3b/src/items/pinheader.h000066400000000000000000000060341272363601300203250ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef PINHEADER_H #define PINHEADER_H #include #include #include #include #include "paletteitem.h" class PinHeader : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call PinHeader(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~PinHeader(); QString getProperty(const QString & key); const QString & form(); PluralType isPlural(); void addedToScene(bool temporary); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); protected: QStringList collectValues(const QString & family, const QString & prop, QString & value); public slots: void swapEntry(const QString & text); public: static QString FemaleFormString; static QString FemaleRoundedFormString; static QString MaleFormString; static QString ShroudedFormString; static QString LongPadFormString; static QString MolexFormString; public: static void initNames(); static QString genFZP(const QString & moduleid); static QString makePcbSvg(const QString & expectedFileName); static QString makePcbShroudedSvg(int pins); static QString makePcbLongPadSvg(int pins, bool lock); static QString makePcbLongPadLockSvg(int pins); static QString makePcbMolexSvg(int pins, const QString & spacingString); static QString makePcbSMDSvg(const QString & expectedFileName); static QString makeSchematicSvg(const QString & expectedFileName); static QString makeBreadboardSvg(const QString & expectedFileName); static QString makeBreadboardShroudedSvg(int pins); static QString makeBreadboardDoubleSvg(const QString & expectedFileName, int pins); protected: static const QStringList & forms(); static void initSpacings(); static QString genModuleID(QMap & currPropsMap); protected: QString m_form; }; #endif fritzing-app-0.9.3b/src/items/propertydef.cpp000066400000000000000000000107731272363601300212710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6141 $: $Author: cohen@irascible.com $: $Date: 2012-07-04 21:20:05 +0200 (Mi, 04. Jul 2012) $ ********************************************************************/ #include "propertydef.h" #include "../debugdialog.h" #include "../model/modelpart.h" #include "../utils/textutils.h" #include #include #include #include QList PropertyDefMaster::PropertyDefs; void PropertyDefMaster::loadPropertyDefs() { QFile file(":/resources/properties.xml"); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("failed loading properties %1 line:%2 col:%3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) return; if (root.tagName() != "properties") return; QDomElement propertyElement = root.firstChildElement("property"); while (!propertyElement.isNull()) { PropertyDef * propertyDef = new PropertyDef; propertyDef->name = propertyElement.attribute("name"); PropertyDefs.append(propertyDef); propertyDef->symbol = propertyElement.attribute("symbol"); propertyDef->minValue = propertyElement.attribute("minValue").toDouble(); propertyDef->maxValue = propertyElement.attribute("maxValue").toDouble(); propertyDef->defaultValue = propertyElement.attribute("defaultValue"); propertyDef->editable = propertyElement.attribute("editable", "").compare("yes") == 0; propertyDef->numeric = propertyElement.attribute("numeric", "").compare("yes") == 0; QDomElement menuItem = propertyElement.firstChildElement("menuItem"); while (!menuItem.isNull()) { QString val = menuItem.attribute("value"); if (propertyDef->numeric) { propertyDef->menuItems.append(val.toDouble()); } else { propertyDef->sMenuItems.append(val); } QString adjunct = menuItem.attribute("adjunct"); if (!adjunct.isEmpty()) { propertyDef->adjuncts.insert(val, adjunct); } menuItem = menuItem.nextSiblingElement("menuItem"); } QDomElement suffixElement = propertyElement.firstChildElement("suffix"); while (!suffixElement.isNull()) { QString suffix = suffixElement.attribute("suffix"); propertyDef->suffixes.append(suffix); suffixElement = suffixElement.nextSiblingElement("suffix"); } propertyElement = propertyElement.nextSiblingElement("property"); } } void PropertyDefMaster::cleanup() { foreach (PropertyDef * propertyDef, PropertyDefs) { delete propertyDef; } PropertyDefs.clear(); } void PropertyDefMaster::initPropertyDefs(ModelPart * modelPart, QHash & propertyDefs) { if (PropertyDefs.count() == 0) { loadPropertyDefs(); } foreach (PropertyDef * propertyDef, PropertyDefs) { foreach (QString suffix, propertyDef->suffixes) { if (!modelPart->moduleID().endsWith(suffix, Qt::CaseInsensitive)) continue; //DebugDialog::debug(QString("%1 %2").arg(suffix).arg(modelPart->moduleID())); QString defaultValue; if (propertyDef->numeric) { if (!propertyDef->defaultValue.isEmpty()) { defaultValue = TextUtils::convertToPowerPrefix(propertyDef->defaultValue.toDouble()) + propertyDef->symbol; } } else { defaultValue = propertyDef->defaultValue; } QString savedValue = modelPart->localProp(propertyDef->name).toString(); if (savedValue.isEmpty()) { savedValue = modelPart->properties().value(propertyDef->name.toLower(), defaultValue); if (!savedValue.isEmpty()) { modelPart->setLocalProp(propertyDef->name, savedValue); } } // caches the current value propertyDefs.insert(propertyDef, savedValue); } } } fritzing-app-0.9.3b/src/items/propertydef.h000066400000000000000000000031571272363601300207340ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PROPERTYDEF_H #define PROPERTYDEF_H #include #include #include struct PropertyDef { QString name; QString symbol; double minValue; double maxValue; QString defaultValue; bool numeric; bool editable; QList menuItems; QStringList sMenuItems; QHash adjuncts; QStringList suffixes; }; class PropertyDefMaster { public: static void initPropertyDefs(class ModelPart *, QHash & propertyDefs); static void cleanup(); protected: static void loadPropertyDefs(); protected: static QList PropertyDefs; }; #endif // PROPERTYDEF_H fritzing-app-0.9.3b/src/items/resistor.cpp000066400000000000000000000350321272363601300205730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include "resistor.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/focusoutcombobox.h" #include "../utils/boundedregexpvalidator.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../commands.h" #include "../layerattributes.h" #include "moduleidnames.h" #include "partlabel.h" #include "../debugdialog.h" #include #include static QStringList Resistances; static QHash PinSpacings; static QHash ColorBands; static QString OhmSymbol(QChar(0x03A9)); static QString PlusMinusSymbol(QChar(0x0B1)); static QHash Tolerances; // TODO // save into parts bin // other manifestations of "220"? Resistor::Resistor( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_changingPinSpacing = false; if (Resistances.count() == 0) { Resistances << QString("0") + OhmSymbol << QString("1") + OhmSymbol << QString("1.5") + OhmSymbol << QString("2.2") + OhmSymbol << QString("3.3") + OhmSymbol << QString("4.7") + OhmSymbol << QString("6.8") + OhmSymbol << QString("10") + OhmSymbol << QString("15") + OhmSymbol << QString("22") + OhmSymbol << QString("33") + OhmSymbol << QString("47") + OhmSymbol << QString("68") + OhmSymbol << QString("100") + OhmSymbol << QString("150") + OhmSymbol << QString("220") + OhmSymbol << QString("330") + OhmSymbol << QString("470") + OhmSymbol << QString("680") + OhmSymbol << QString("1k") + OhmSymbol << QString("1.5k") + OhmSymbol << QString("2.2k") + OhmSymbol << QString("3.3k") + OhmSymbol << QString("4.7k") + OhmSymbol << QString("6.8k") + OhmSymbol << QString("10k") + OhmSymbol << QString("15k") + OhmSymbol << QString("22k") + OhmSymbol << QString("33k") + OhmSymbol << QString("47k") + OhmSymbol << QString("68k") + OhmSymbol << QString("100k") + OhmSymbol << QString("150k") + OhmSymbol << QString("220k") + OhmSymbol << QString("330k") + OhmSymbol << QString("470k") + OhmSymbol << QString("680k") + OhmSymbol << QString("1M") + OhmSymbol; } if (PinSpacings.count() == 0) { PinSpacings.insert("100 mil (stand-up right)", "pcb/axial_stand0_2_100mil_pcb.svg"); PinSpacings.insert("100 mil (stand-up left)", "pcb/axial_stand1_2_100mil_pcb.svg"); PinSpacings.insert("200 mil", "pcb/axial_lay_2_200mil_pcb.svg"); PinSpacings.insert("300 mil", "pcb/axial_lay_2_300mil_pcb.svg"); PinSpacings.insert("400 mil", "pcb/axial_lay_2_400mil_pcb.svg"); PinSpacings.insert("500 mil", "pcb/axial_lay_2_500mil_pcb.svg"); PinSpacings.insert("600 mil", "pcb/axial_lay_2_600mil_pcb.svg"); PinSpacings.insert("800 mil", "pcb/axial_lay_2_800mil_pcb.svg"); } if (ColorBands.count() == 0) { ColorBands.insert(0, QColor(0, 0, 0)); ColorBands.insert(1, QColor(138, 61, 6)); ColorBands.insert(2, QColor(196, 8, 8)); ColorBands.insert(3, QColor(255, 77, 0)); ColorBands.insert(4, QColor(255, 213, 0)); ColorBands.insert(5, QColor(0, 163, 61)); ColorBands.insert(6, QColor(0, 96, 182)); ColorBands.insert(7, QColor(130, 16, 210)); ColorBands.insert(8, QColor(140, 140, 140)); ColorBands.insert(9, QColor(255, 255, 255)); ColorBands.insert(-1, QColor(173, 159, 78)); ColorBands.insert(-2, QColor(192, 192, 192)); } if (Tolerances.count() == 0) { // TODO: move this into properties.xml Tolerances.insert(PlusMinusSymbol + "0.05%", QColor(140, 140, 140)); Tolerances.insert(PlusMinusSymbol + "0.1%", QColor(130, 16, 210)); Tolerances.insert(PlusMinusSymbol + "0.25%", QColor(0, 96, 182)); Tolerances.insert(PlusMinusSymbol + "0.5%", QColor(0, 163, 61)); Tolerances.insert(PlusMinusSymbol + "1%", QColor(138, 61, 6)); Tolerances.insert(PlusMinusSymbol + "2%", QColor(196, 8, 8)); Tolerances.insert(PlusMinusSymbol + "5%", QColor(173, 159, 78)); Tolerances.insert(PlusMinusSymbol + "10%", QColor(192, 192, 192)); Tolerances.insert(PlusMinusSymbol + "20%", QColor(0xdb, 0xb4, 0x77)); } m_ohms = modelPart->localProp("resistance").toString(); if (m_ohms.isEmpty()) { m_ohms = modelPart->properties().value("resistance", "220"); modelPart->setLocalProp("resistance", m_ohms); } m_pinSpacing = modelPart->localProp("pin spacing").toString(); if (m_pinSpacing.isEmpty()) { m_pinSpacing = modelPart->properties().value("pin spacing", "400 mil"); modelPart->setLocalProp("pin spacing", m_pinSpacing); } updateResistances(m_ohms); } Resistor::~Resistor() { } void Resistor::setResistance(QString resistance, QString pinSpacing, bool force) { QString tolerance = prop("tolerance"); if (resistance.endsWith(OhmSymbol)) { resistance.chop(1); } modelPart()->setLocalTitle(resistance + " " + OhmSymbol + " " + tr("Resistor")); switch (this->m_viewID) { case ViewLayer::BreadboardView: if (force || resistance.compare(m_ohms) != 0) { QString svg = makeSvg(resistance, m_viewLayerID); //DebugDialog::debug(svg); reloadRenderer(svg, false); } break; case ViewLayer::PCBView: if (force || pinSpacing.compare(m_pinSpacing) != 0) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) break; if (modelPart()->properties().value("package").compare("tht", Qt::CaseInsensitive) == 0) { // pinspacing is irrelevant for SMD resistors QString filename = PinSpacings.value(pinSpacing, ""); if (filename.isEmpty()) break; QString original = modelPart()->imageFileName(m_viewID); modelPart()->setImageFileName(m_viewID, filename); m_changingPinSpacing = true; resetImage(infoGraphicsView); m_changingPinSpacing = false; modelPart()->setImageFileName(m_viewID, original); QList already; updateConnections(false, already); } } break; default: break; } m_ohms = resistance; m_pinSpacing = pinSpacing; modelPart()->setLocalProp("resistance", resistance); modelPart()->setLocalProp("pin spacing", pinSpacing); modelPart()->setLocalProp("tolerance", tolerance); updateResistances(m_ohms); if (m_partLabel) m_partLabel->displayTextsIf(); } QString Resistor::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { switch (viewLayerID) { case ViewLayer::Breadboard: case ViewLayer::Icon: break; default: return Capacitor::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QString svg = makeSvg(m_ohms, viewLayerID); return PaletteItemBase::normalizeSvg(svg, viewLayerID, blackOnly, dpi, factor); } QString Resistor::makeSvg(const QString & resistance, ViewLayer::ViewLayerID viewLayerID) { QString moduleID = this->moduleID(); double ohms = TextUtils::convertFromPowerPrefix(resistance, OhmSymbol); QString sohms = QString::number(ohms, 'e', 3); int firstband = sohms.at(0).toLatin1() - '0'; int secondband = sohms.at(2).toLatin1() - '0'; int thirdband = sohms.at(3).toLatin1() - '0'; int temp = (firstband * 10) + secondband; if (moduleID.contains("5Band")) { temp = (temp * 10) + thirdband; } int multiplier = (temp == 0) ? 0 : log10(ohms / temp); QString tolerance = prop("tolerance"); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; QString fn = (viewLayerID == ViewLayer::Breadboard) ? m_breadboardSvgFile : m_iconSvgFile; QFile file(fn); if (!domDocument.setContent(&file, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("makesvg failed %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return ""; } QDomElement root = domDocument.documentElement(); setBands(root, firstband, secondband, thirdband, multiplier, tolerance); return domDocument.toString(); } void Resistor::setBands(QDomElement & element, int firstband, int secondband, int thirdband, int multiplier, const QString & tolerance) { QString id = element.attribute("id"); if (!id.isEmpty()) { if (id.compare("band_1_st") == 0) { element.setAttribute("fill", ColorBands.value(firstband, Qt::black).name()); } else if (id.compare("band_2_nd") == 0) { element.setAttribute("fill", ColorBands.value(secondband, Qt::black).name()); } else if (id.compare("band_3") == 0) { element.setAttribute("fill", ColorBands.value(thirdband, Qt::black).name()); } else if (id.compare("band_rd_multiplier") == 0) { element.setAttribute("fill", ColorBands.value(multiplier, Qt::black).name()); } else if (id.compare("gold_band") == 0) { element.setAttribute("fill", Tolerances.value(tolerance, QColor(173, 159, 78)).name()); } } QDomElement child = element.firstChildElement(); while (!child.isNull()) { setBands(child, firstband, secondband, thirdband, multiplier, tolerance); child = child.nextSiblingElement(); } } bool Resistor::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("resistance", Qt::CaseInsensitive) == 0) { returnProp = tr("resistance"); FocusOutComboBox * focusOutComboBox = new FocusOutComboBox(); focusOutComboBox->setEnabled(swappingEnabled); focusOutComboBox->setEditable(true); QString current = m_ohms + OhmSymbol; focusOutComboBox->addItems(Resistances); focusOutComboBox->setCurrentIndex(focusOutComboBox->findText(current)); BoundedRegExpValidator * validator = new BoundedRegExpValidator(focusOutComboBox); validator->setSymbol(OhmSymbol); validator->setConverter(TextUtils::convertFromPowerPrefix); validator->setBounds(0, 9900000000.0); validator->setRegExp(QRegExp(QString("((\\d{1,3})|(\\d{1,3}\\.)|(\\d{1,3}\\.\\d{1,2}))[\\x%1umkMG]{0,1}[\\x03A9]{0,1}").arg(TextUtils::MicroSymbolCode, 4, 16, QChar('0')))); focusOutComboBox->setValidator(validator); connect(focusOutComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(resistanceEntry(const QString &))); focusOutComboBox->setObjectName("infoViewComboBox"); focusOutComboBox->setToolTip(tr("You can either type in a resistance value, or select one from the drop down. Format nnn.dP where P is one of 'umkMG'")); returnValue = current; returnWidget = focusOutComboBox; return true; } return Capacitor::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } QString Resistor::getProperty(const QString & key) { if (key.compare("resistance", Qt::CaseInsensitive) == 0) { return m_ohms + OhmSymbol; } if (key.compare("pin spacing", Qt::CaseInsensitive) == 0) { return m_pinSpacing; } return Capacitor::getProperty(key); } QString Resistor::resistance() { return m_ohms; } QString Resistor::pinSpacing() { return m_pinSpacing; } void Resistor::addedToScene(bool temporary) { if (this->scene()) { setResistance(m_ohms, m_pinSpacing, true); } return Capacitor::addedToScene(temporary); } const QString & Resistor::title() { m_title = QString("%1%2 Resistor").arg(m_ohms).arg(OhmSymbol); return m_title; } void Resistor::updateResistances(QString r) { if (!Resistances.contains(r + OhmSymbol)) { Resistances.append(r + OhmSymbol); } } ConnectorItem* Resistor::newConnectorItem(Connector *connector) { if (m_changingPinSpacing) { return connector->connectorItemByViewLayerID(viewID(), viewLayerID()); } return Capacitor::newConnectorItem(connector); } ConnectorItem* Resistor::newConnectorItem(ItemBase * layerKin, Connector *connector) { if (m_changingPinSpacing) { return connector->connectorItemByViewLayerID(viewID(), layerKin->viewLayerID()); } return Capacitor::newConnectorItem(layerKin, connector); } bool Resistor::hasCustomSVG() { switch (m_viewID) { case ViewLayer::BreadboardView: case ViewLayer::IconView: return true; default: return ItemBase::hasCustomSVG(); } } bool Resistor::canEditPart() { return true; } QStringList Resistor::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) { QStringList values; foreach (QString f, PinSpacings.keys()) { values.append(f); } value = m_pinSpacing; return values; } return Capacitor::collectValues(family, prop, value); } void Resistor::resistanceEntry(const QString & text) { //DebugDialog::debug(QString("resistance entry %1").arg(text)); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setResistance(text, ""); } } ItemBase::PluralType Resistor::isPlural() { return Plural; } void Resistor::setProp(const QString & prop, const QString & value) { Capacitor::setProp(prop, value); if (prop.compare("tolerance") == 0) { setResistance(m_ohms, m_pinSpacing, true); } } bool Resistor::setUpImage(ModelPart * modelPart, const LayerHash & viewLayers, LayerAttributes & layerAttributes) { bool result = Capacitor::setUpImage(modelPart, viewLayers, layerAttributes); if (layerAttributes.viewLayerID == ViewLayer::Breadboard) { if (result && m_breadboardSvgFile.isEmpty()) m_breadboardSvgFile = layerAttributes.filename(); } else if (layerAttributes.viewLayerID == ViewLayer::Icon) { if (result && m_iconSvgFile.isEmpty()) m_iconSvgFile = layerAttributes.filename(); } return result; } ViewLayer::ViewID Resistor::useViewIDForPixmap(ViewLayer::ViewID vid, bool swappingEnabled) { if (swappingEnabled && vid == ViewLayer::BreadboardView) { return vid; } return ItemBase::useViewIDForPixmap(vid, swappingEnabled); } fritzing-app-0.9.3b/src/items/resistor.h000066400000000000000000000060531272363601300202410ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef RESISTOR_H #define RESISTOR_H #include #include #include #include #include "capacitor.h" class Resistor : public Capacitor { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Resistor(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Resistor(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); QString getProperty(const QString & key); void setResistance(QString resistance, QString pinSpacing, bool force); QString resistance(); QString pinSpacing(); const QString & title(); bool hasCustomSVG(); bool canEditPart(); PluralType isPlural(); void addedToScene(bool temporary); void setProp(const QString & prop, const QString & value); bool setUpImage(ModelPart* modelPart, const LayerHash & viewLayers, LayerAttributes &); protected: QString makeSvg(const QString & ohms, ViewLayer::ViewLayerID viewLayerID); void updateResistances(QString r); ConnectorItem* newConnectorItem(class Connector *connector); ConnectorItem* newConnectorItem(ItemBase * layerkin, Connector *connector); QStringList collectValues(const QString & family, const QString & prop, QString & value); void setBands(QDomElement & element, int firstband, int secondband, int thirdband, int multiplier, const QString & tolerance); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); public slots: void resistanceEntry(const QString & text); public: static double toOhms(const QString & ohmsString, void * data); protected: QString m_ohms; QString m_pinSpacing; QString m_title; bool m_changingPinSpacing; QString m_iconSvgFile; QString m_breadboardSvgFile; }; #endif fritzing-app-0.9.3b/src/items/resizableboard.cpp000066400000000000000000001356631272363601300217240ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "resizableboard.h" #include "../utils/resizehandle.h" #include "../utils/graphicsutils.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../commands.h" #include "moduleidnames.h" #include "../layerattributes.h" #include "../debugdialog.h" #include "../svg/gerbergenerator.h" #include #include #include #include #include #include #include #include #include #include #include QList PaperSizeNames; QList PaperSizeDimensions; static QHash NamesToXmlNames; static QHash XmlNamesToNames; static QHash BoardLayerTemplates; static QHash SilkscreenLayerTemplates; static QHash Silkscreen0LayerTemplates; static const int LineThickness = 8; static const QRegExp HeightExpr("height=\\'\\d*px"); static QString StandardCustomBoardExplanation; QStringList Board::BoardImageNames; QStringList Board::NewBoardImageNames; const double ResizableBoard::CornerHandleSize = 7.0; static const double JND = (double) 0.01; QString Board::OneLayerTranslated; QString Board::TwoLayersTranslated; Board::Board( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_svgOnly = true; m_fileNameComboBox = NULL; if (isBoard(modelPart)) { if (modelPart->localProp("layers").isNull()) { modelPart->setLocalProp("layers", modelPart->properties().value("layers")); } if (itemType() == ModelPart::Board && !modelPart->properties().keys().contains("filename")) { // deal with old style custom boards modelPart->modelPartShared()->setProperty("filename", "", false); } } if (StandardCustomBoardExplanation.isEmpty()) { StandardCustomBoardExplanation = tr("\n\nA custom board svg typically has one or two silkscreen layers and one board layer.\n") + tr("Have a look at the circle_pcb.svg file in your Fritzing installation folder at parts/svg/core/pcb/.\n\n"); } if (NamesToXmlNames.count() == 0) { NamesToXmlNames.insert("copper bottom", "copper0"); NamesToXmlNames.insert("copper top", "copper1"); NamesToXmlNames.insert("silkscreen bottom", "silkscreen0"); NamesToXmlNames.insert("silkscreen top", "silkscreen"); foreach (QString key, NamesToXmlNames.keys()) { XmlNamesToNames.insert(NamesToXmlNames.value(key), key); } } } Board::~Board() { } void Board::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget); Q_UNUSED(option); painter->save(); painter->setOpacity(0); painter->fillPath(this->hoverShape(), QBrush(HoverColor)); painter->restore(); } QStringList Board::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("layers", Qt::CaseInsensitive) == 0) { QString realValue = modelPart()->localProp("layers").toString(); if (!realValue.isEmpty()) { value = realValue; } QStringList result; if (OneLayerTranslated.isEmpty()) { OneLayerTranslated = tr("one layer (single-sided)"); } if (TwoLayersTranslated.isEmpty()) { TwoLayersTranslated = tr("two layers (double-sided)"); } result.append(OneLayerTranslated); result.append(TwoLayersTranslated); if (value == "1") { value = OneLayerTranslated; } else if (value == "2") { value = TwoLayersTranslated; } return result; } QStringList result = PaletteItem::collectValues(family, prop, value); if (prop.compare("shape", Qt::CaseInsensitive) == 0 && isBoard(this) && !this->moduleID().contains(ModuleIDNames::BoardLogoImageModuleIDName)) { result.removeAll("Custom Shape"); } return result; } bool Board::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("filename", Qt::CaseInsensitive) == 0 && isBoard(this)) { setupLoadImage(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget); return true; } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } bool Board::rotation45Allowed() { return false; } bool Board::stickyEnabled() { return false; } ItemBase::PluralType Board::isPlural() { return Plural; } bool Board::canFindConnectorsUnder() { return false; } bool Board::isBoard(ItemBase * itemBase) { if (qobject_cast(itemBase) == NULL) return false; return isBoard(itemBase->modelPart()); } bool Board::isBoard(ModelPart * modelPart) { if (modelPart == NULL) return false; switch (modelPart->itemType()) { case ModelPart::Board: return true; case ModelPart::ResizableBoard: return true; case ModelPart::Logo: return modelPart->family().contains("pcb", Qt::CaseInsensitive); default: return false; } } void Board::setupLoadImage(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget) { Q_UNUSED(returnValue); Q_UNUSED(value); Q_UNUSED(prop); Q_UNUSED(family); Q_UNUSED(parent); returnProp = tr("image file"); QFrame * frame = new QFrame(); frame->setObjectName("infoViewPartFrame"); QVBoxLayout * vboxLayout = new QVBoxLayout(); vboxLayout->setContentsMargins(0, 0, 0, 0); vboxLayout->setSpacing(0); vboxLayout->setMargin(0); QComboBox * comboBox = new QComboBox(); comboBox->setObjectName("infoViewComboBox"); comboBox->setEditable(false); comboBox->setEnabled(swappingEnabled); m_fileNameComboBox = comboBox; setFileNameItems(); connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(fileNameEntry(const QString &))); QPushButton * button = new QPushButton (tr("load image file")); button->setObjectName("infoViewButton"); connect(button, SIGNAL(pressed()), this, SLOT(prepLoadImage())); button->setEnabled(swappingEnabled); vboxLayout->addWidget(comboBox); vboxLayout->addWidget(button); frame->setLayout(vboxLayout); returnWidget = frame; returnProp = ""; } void Board::setFileNameItems() { if (m_fileNameComboBox == NULL) return; m_fileNameComboBox->addItems(getImageNames()); m_fileNameComboBox->addItems(getNewImageNames()); int ix = 0; foreach (QString name, getImageNames()) { if (prop("lastfilename").contains(name)) { m_fileNameComboBox->setCurrentIndex(ix); return; } ix++; } foreach (QString name, getNewImageNames()) { if (prop("lastfilename").contains(name)) { m_fileNameComboBox->setCurrentIndex(ix); return; } ix++; } } QStringList & Board::getImageNames() { return BoardImageNames; } QStringList & Board::getNewImageNames() { return NewBoardImageNames; } void Board::fileNameEntry(const QString & filename) { foreach (QString name, getImageNames()) { if (filename.compare(name) == 0) { QString f = FolderUtils::getAppPartsSubFolderPath("") + "/svg/core/pcb/" + filename + ".svg"; prepLoadImageAux(f, false); return; } } prepLoadImageAux(filename, true); } void Board::prepLoadImage() { QString imagesStr = tr("Images"); imagesStr += " ("; if (!m_svgOnly) { QList supportedImageFormats = QImageReader::supportedImageFormats(); foreach (QByteArray ba, supportedImageFormats) { imagesStr += "*." + QString(ba) + " "; } } if (!imagesStr.contains("svg")) { imagesStr += "*.svg"; } imagesStr += ")"; QString fileName = FolderUtils::getOpenFileName( NULL, tr("Select an image file to load"), "", imagesStr ); if (fileName.isEmpty()) return; if (!checkImage(fileName)) return; prepLoadImageAux(fileName, true); } bool Board::checkImage(const QString & filename) { QFile file(filename); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { unableToLoad(filename, tr("due to an xml problem: %1 line:%2 column:%3").arg(errorStr).arg(errorLine).arg(errorColumn)); return false; } QDomElement root = domDocument.documentElement(); if (root.tagName() != "svg") { unableToLoad(filename, tr("because the xml is not correctly formatted")); return false; } QList elements; TextUtils::findElementsWithAttribute(root, "id", elements); int layers = 0; QList boardElements; int silk0Layers = 0; int silk1Layers = 0; bool boardHasChildren = false; foreach (QDomElement element, elements) { QString id = element.attribute("id"); ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(id); if (viewLayerID != ViewLayer::UnknownLayer) { layers++; if (viewLayerID == ViewLayer::Board) { boardElements << element; if (element.childNodes().count() > 0) { boardHasChildren = true; } } else if (viewLayerID == ViewLayer::Silkscreen1) { silk1Layers++; } else if (viewLayerID == ViewLayer::Silkscreen0) { silk0Layers++; } } } if (boardElements.count() == 1 && !boardHasChildren) { unableToLoad(filename, tr("the element contains no shape elements") + StandardCustomBoardExplanation); return false; } if ((boardElements.count() == 1) && ((silk1Layers == 1) || (silk0Layers == 1))) { moreCheckImage(filename); return true; } if (boardElements.count() > 1) { unableToLoad(filename, tr("because there are multiple layers") + StandardCustomBoardExplanation); return false; } if (silk1Layers > 1) { unableToLoad(filename, tr("because there are multiple layers") + StandardCustomBoardExplanation); return false; } if (silk0Layers > 1) { unableToLoad(filename, tr("because there are multiple layers") + StandardCustomBoardExplanation); return false; } if (layers > 0 && boardElements.count() == 0) { unableToLoad(filename, tr("because there is no layer") + StandardCustomBoardExplanation); return false; } if (layers == 0 && root.childNodes().count() == 0) { unableToLoad(filename, tr("the svg contains no shape elements") + StandardCustomBoardExplanation); return false; } if (layers == 0 || (boardElements.count() == 1 && silk1Layers == 0 && silk1Layers == 0)) { bool result = canLoad(filename, tr("but the pcb itself will have no silkscreen layer") + StandardCustomBoardExplanation); if (result) moreCheckImage(filename); return result; } unableToLoad(filename, tr("the svg doesn't fit the custom board format") + StandardCustomBoardExplanation); return false; } void Board::moreCheckImage(const QString & filename) { QFile file(filename); file.open(QFile::ReadOnly); QString svg = file.readAll(); file.close(); QString nsvg = setBoardOutline(svg); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; domDocument.setContent(nsvg, &errorStr, &errorLine, &errorColumn); QDomElement element = TextUtils::findElementWithAttribute(domDocument.documentElement(), "id", GerberGenerator::MagicBoardOutlineID); int subpaths = 1; int mCount = 0; if (element.tagName() == "path") { QString originalPath = element.attribute("d", "").trimmed(); if (GerberGenerator::MultipleZs.indexIn(originalPath) >= 0) { QStringList ds = element.attribute("d").split("z", QString::SkipEmptyParts); subpaths = ds.count(); foreach (QString d, ds) { if (d.trimmed().startsWith("m", Qt::CaseInsensitive)) mCount++; } } } QString msg = tr("The custom shape has been loaded, and you will see the new board shortly.

"); msg += tr("Before actual PCB production we recommend that you test your custom shape by using the 'File > Export for Production > Extended Gerber' option. "); msg += tr("Check the resulting contour file with a Gerber-viewer application to make sure the shape came out as expected.

"); msg += tr("The rest of this message concerns 'cutouts'. "); msg += tr("These are circular or irregularly-shaped holes that you can optionally incorporate into a custom PCB shape.

"); if (subpaths == 1) { msg += tr("The custom shape has no cutouts."); } else { msg += tr("The custom shape has %n cutouts.", "", subpaths - 1); if (subpaths != mCount) { msg += tr("
However, the cutouts may not be formatted correctly."); } } msg += tr("

If you intended your custom shape to have cutouts and you did not get the expected result, "); msg += tr("it is because Fritzing requires that you make cutouts using a shape 'subtraction' or 'difference' operation in your vector graphics editor."); QMessageBox::information(NULL, "Custom Shape", msg); } QString Board::setBoardOutline(const QString & svg) { QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(svg, true, &errorStr, &errorLine, &errorColumn)) { return svg; } QDomElement root = domDocument.documentElement(); if (root.tagName() != "svg") { return svg; } QDomElement board = TextUtils::findElementWithAttribute(root, "id", "board"); if (board.isNull()) { board = root; } QList leaves; TextUtils::collectLeaves(board, leaves); if (leaves.count() == 1) { QDomElement leaf = leaves.at(0); leaf.setAttribute("id", GerberGenerator::MagicBoardOutlineID); return TextUtils::removeXMLEntities(domDocument.toString()); } int ix = 0; QStringList ids; foreach (QDomElement leaf, leaves) { ids.append(leaf.attribute("id", "")); leaf.setAttribute("id", QString("____%1____").arg(ix++)); } QSvgRenderer renderer(domDocument.toByteArray()); ix = 0; foreach (QDomElement leaf, leaves) { leaf.setAttribute("id", ids.at(ix++)); } double maxArea = 0; int maxIndex = -1; for (int i = 0; i < leaves.count(); i++) { QRectF r = renderer.boundsOnElement(QString("____%1____").arg(i)); if (r.width() * r.height() > maxArea) { maxArea = r.width() * r.height(); maxIndex = i; } } if (maxIndex >= 0) { QDomElement leaf = leaves.at(maxIndex); leaf.setAttribute("id", GerberGenerator::MagicBoardOutlineID); return TextUtils::removeXMLEntities(domDocument.toString()); } return svg; } void Board::unableToLoad(const QString & fileName, const QString & reason) { QMessageBox::information( NULL, tr("Unable to load"), tr("Unable to load image from %1 %2").arg(fileName).arg(reason) ); } bool Board::canLoad(const QString & fileName, const QString & reason) { QMessageBox::StandardButton answer = QMessageBox::question( NULL, tr("Can load, but"), tr("The image from %1 can be loaded, but %2\nUse the file?").arg(fileName).arg(reason), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); return answer == QMessageBox::Yes; } void Board::prepLoadImageAux(const QString & fileName, bool addName) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->loadLogoImage(this, "", QSizeF(0,0), "", fileName, addName); } } ViewLayer::ViewID Board::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::PCBView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } QString Board::convertToXmlName(const QString & name) { foreach (QString key, ItemBase::TranslatedPropertyNames.keys()) { if (name.compare(ItemBase::TranslatedPropertyNames.value(key), Qt::CaseInsensitive) == 0) { return NamesToXmlNames.value(key); } } return name; } QString Board::convertFromXmlName(const QString & xmlName) { QString result = ItemBase::TranslatedPropertyNames.value(XmlNamesToNames.value(xmlName)); if (result.isEmpty()) return xmlName; return result; } /////////////////////////////////////////////////////////// ResizableBoard::ResizableBoard( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Board(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { fixWH(); m_keepAspectRatio = false; m_widthEditor = m_heightEditor = NULL; m_aspectRatioCheck = NULL; m_aspectRatioLabel = NULL; m_revertButton = NULL; m_paperSizeComboBox = NULL; m_corner = ResizableBoard::NO_CORNER; m_currentScale = 1.0; m_decimalsAfter = 1; } ResizableBoard::~ResizableBoard() { } void ResizableBoard::addedToScene(bool temporary) { loadTemplates(); if (this->scene()) { setInitialSize(); } Board::addedToScene(temporary); } void ResizableBoard::loadTemplates() { if (!BoardLayerTemplates.value(moduleID(), "").isEmpty()) return; QFile file(m_filename); if (!file.open(QIODevice::ReadOnly)) return; QString svg = file.readAll(); if (svg.isEmpty()) return; BoardLayerTemplates.insert(moduleID(), getShapeForRenderer(svg, ViewLayer::Board)); SilkscreenLayerTemplates.insert(moduleID(), getShapeForRenderer(svg, ViewLayer::Silkscreen1)); Silkscreen0LayerTemplates.insert(moduleID(), getShapeForRenderer(svg, ViewLayer::Silkscreen0)); } double ResizableBoard::minWidth() { return 0.25 * GraphicsUtils::SVGDPI; } double ResizableBoard::minHeight() { return 0.25 * GraphicsUtils::SVGDPI; } void ResizableBoard::mousePressEvent(QGraphicsSceneMouseEvent * event) { m_corner = ResizableBoard::NO_CORNER; if (m_spaceBarWasPressed) { Board::mousePressEvent(event); return; } double right = m_size.width(); double bottom = m_size.height(); m_resizeMousePos = event->scenePos(); m_resizeStartPos = pos(); m_resizeStartSize = m_size; m_resizeStartTopLeft = mapToScene(0, 0); m_resizeStartTopRight = mapToScene(right, 0); m_resizeStartBottomLeft = mapToScene(0, bottom); m_resizeStartBottomRight = mapToScene(right, bottom); m_corner = findCorner(event->scenePos(), event->modifiers()); switch (m_corner) { case ResizableBoard::NO_CORNER: Board::mousePressEvent(event); return; default: break; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) { setInitialSize(); infoGraphicsView->viewItemInfo(this); } } void ResizableBoard::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { if (m_corner == ResizableBoard::NO_CORNER) { Board::mouseMoveEvent(event); return; } QPointF zero = mapToScene(0, 0); QPointF ds = mapFromScene(zero + event->scenePos() - m_resizeMousePos); QPointF newPos; QSizeF size = m_resizeStartSize; switch (m_corner) { case ResizableBoard::BOTTOM_RIGHT: size.setWidth(size.width() + ds.x()); size.setHeight(size.height() + ds.y()); break; case ResizableBoard::TOP_RIGHT: size.setWidth(size.width() + ds.x()); size.setHeight(size.height() - ds.y()); break; case ResizableBoard::BOTTOM_LEFT: size.setWidth(size.width() - ds.x()); size.setHeight(size.height() + ds.y()); break; case ResizableBoard::TOP_LEFT: size.setWidth(size.width() - ds.x()); size.setHeight(size.height() - ds.y()); break; default: break; } if (size.width() < minWidth()) { DebugDialog::debug("to min width"); size.setWidth(minWidth()); } if (size.height() < minHeight()) { DebugDialog::debug("to min height"); size.setHeight(minHeight()); } if (m_keepAspectRatio) { double cw = size.height() * m_aspectRatio.width() / m_aspectRatio.height(); double ch = size.width() * m_aspectRatio.height() / m_aspectRatio.width(); if (ch < minHeight()) { DebugDialog::debug("from min height"); size.setWidth(cw); } else if (cw < minWidth()) { DebugDialog::debug("from min width"); size.setHeight(ch); } else { // figure out which one changes the area least double a1 = cw * size.height(); double a2 = ch * size.width(); double ac = m_size.width() * m_size.height(); if (qAbs(ac - a1) <= qAbs(ac - a2)) { size.setWidth(cw); } else { size.setHeight(ch); } } } bool changePos = (m_corner != ResizableBoard::BOTTOM_RIGHT); bool changeTransform = !this->transform().isIdentity(); LayerHash lh; QSizeF oldSize = m_size; resizePixels(size.width(), size.height(), lh); if (changePos) { if (changeTransform) { QTransform oldT = transform(); DebugDialog::debug(QString("t old m:%1 p:%2,%3 sz:%4,%5") .arg(TextUtils::svgMatrix(oldT)) .arg(pos().x()).arg(pos().y()) .arg(oldSize.width()).arg(oldSize.height())); double sw = size.width() / 2; double sh = size.height() / 2; QMatrix m(oldT.m11(), oldT.m12(), oldT.m21(), oldT.m22(), 0, 0); ds = m.inverted().map(ds); QTransform newT = QTransform().translate(-sw, -sh) * QTransform(m) * QTransform().translate(sw, sh); QList kin; kin << this->layerKinChief(); foreach (ItemBase * lk, this->layerKinChief()->layerKin()) { kin << lk; } foreach (ItemBase * itemBase, kin) { itemBase->getViewGeometry().setTransform(newT); itemBase->setTransform(newT); } QTransform t = transform(); DebugDialog::debug(QString("t new m:%1 p:%2,%3 sz:%4,%5") .arg(TextUtils::svgMatrix(t)) .arg(pos().x()).arg(pos().y()) .arg(size.width()).arg(size.height())); } QPointF actual; QPointF desired; switch (m_corner) { case ResizableBoard::TOP_RIGHT: actual = mapToScene(0, size.height()); desired = m_resizeStartBottomLeft; break; case ResizableBoard::BOTTOM_LEFT: actual = mapToScene(size.width(), 0); desired = m_resizeStartTopRight; break; case ResizableBoard::TOP_LEFT: actual = mapToScene(size.width(), size.height()); desired = m_resizeStartBottomRight; break; default: break; } setPos(pos() + desired - actual); } } void ResizableBoard::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { if (m_corner == ResizableBoard::NO_CORNER) { Board::mouseReleaseEvent(event); return; } event->accept(); m_corner = ResizableBoard::NO_CORNER; setKinCursor(Qt::ArrowCursor); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) { infoGraphicsView->viewItemInfo(this); } } ViewLayer::ViewID ResizableBoard::theViewID() { return ViewLayer::PCBView; } void ResizableBoard::resizePixels(double w, double h, const LayerHash & viewLayers) { resizeMM(GraphicsUtils::pixels2mm(w, GraphicsUtils::SVGDPI), GraphicsUtils::pixels2mm(h, GraphicsUtils::SVGDPI), viewLayers); } bool ResizableBoard::resizeMM(double mmW, double mmH, const LayerHash & viewLayers) { if (mmW == 0 || mmH == 0) { LayerAttributes layerAttributes; this->initLayerAttributes(layerAttributes, m_viewID, m_viewLayerID, m_viewLayerPlacement, true, true); setUpImage(modelPart(), viewLayers, layerAttributes); modelPart()->setLocalProp("height", QVariant()); modelPart()->setLocalProp("width", QVariant()); // do the layerkin return false; } QRectF r = this->boundingRect(); if (qAbs(GraphicsUtils::pixels2mm(r.width(), GraphicsUtils::SVGDPI) - mmW) < .001 && qAbs(GraphicsUtils::pixels2mm(r.height(), GraphicsUtils::SVGDPI) - mmH) < .001) { return false; } resizeMMAux(mmW, mmH); return true; } void ResizableBoard::resizeMMAux(double mmW, double mmH) { double milsW = GraphicsUtils::mm2mils(mmW); double milsH = GraphicsUtils::mm2mils(mmH); QString s = makeFirstLayerSvg(mmW, mmH, milsW, milsH); bool result = resetRenderer(s); if (result) { modelPart()->setLocalProp("width", mmW); modelPart()->setLocalProp("height", mmH); double tens = pow(10.0, m_decimalsAfter); setWidthAndHeight(qRound(mmW * tens) / tens, qRound(mmH * tens) / tens); } // DebugDialog::debug(QString("fast load result %1 %2").arg(result).arg(s)); foreach (ItemBase * itemBase, m_layerKin) { QString s = makeNextLayerSvg(itemBase->viewLayerID(), mmW, mmH, milsW, milsH); if (!s.isEmpty()) { result = itemBase->resetRenderer(s); if (result) { itemBase->modelPart()->setLocalProp("width", mmW); itemBase->modelPart()->setLocalProp("height", mmH); } } } } void ResizableBoard::loadLayerKin( const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement viewLayerPlacement) { loadTemplates(); Board::loadLayerKin(viewLayers, viewLayerPlacement); double w = m_modelPart->localProp("width").toDouble(); if (w != 0) { resizeMM(w, m_modelPart->localProp("height").toDouble(), viewLayers); } } void ResizableBoard::setInitialSize() { double w = m_modelPart->localProp("width").toDouble(); if (w == 0) { // set the size so the infoGraphicsView will display the size as you drag QSizeF sz = this->boundingRect().size(); modelPart()->setLocalProp("width", GraphicsUtils::pixels2mm(sz.width(), GraphicsUtils::SVGDPI)); modelPart()->setLocalProp("height", GraphicsUtils::pixels2mm(sz.height(), GraphicsUtils::SVGDPI)); } } QString ResizableBoard::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { double w = m_modelPart->localProp("width").toDouble(); if (w != 0) { double h = m_modelPart->localProp("height").toDouble(); QString xml = makeLayerSvg(viewLayerID, w, h, GraphicsUtils::mm2mils(w), GraphicsUtils::mm2mils(h)); if (!xml.isEmpty()) { return PaletteItemBase::normalizeSvg(xml, viewLayerID, blackOnly, dpi, factor); } } return Board::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QSizeF ResizableBoard::getSizeMM() { double w = m_modelPart->localProp("width").toDouble(); double h = m_modelPart->localProp("height").toDouble(); return QSizeF(w, h); } QString ResizableBoard::makeLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { switch (viewLayerID) { case ViewLayer::Board: return makeBoardSvg(mmW, mmH, milsW, milsH); case ViewLayer::Silkscreen1: case ViewLayer::Silkscreen0: return makeSilkscreenSvg(viewLayerID, mmW, mmH, milsW, milsH); break; default: return ""; } } QString ResizableBoard::makeNextLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { if (viewLayerID == ViewLayer::Silkscreen1) return makeSilkscreenSvg(viewLayerID, mmW, mmH, milsW, milsH); if (viewLayerID == ViewLayer::Silkscreen0) return makeSilkscreenSvg(viewLayerID, mmW, mmH, milsW, milsH); return ""; } QString ResizableBoard::makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH) { return makeBoardSvg(mmW, mmH, milsW, milsH); } QString ResizableBoard::makeBoardSvg(double mmW, double mmH, double milsW, double milsH) { Q_UNUSED(milsW); Q_UNUSED(milsH); return makeSvg(mmW, mmH, BoardLayerTemplates.value(moduleID())); } QString ResizableBoard::makeSilkscreenSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { Q_UNUSED(milsW); Q_UNUSED(milsH); if (viewLayerID == ViewLayer::Silkscreen0) return makeSvg(mmW, mmH, Silkscreen0LayerTemplates.value(moduleID())); return makeSvg(mmW, mmH, SilkscreenLayerTemplates.value(moduleID())); } QString ResizableBoard::makeSvg(double mmW, double mmH, const QString & layerTemplate) { if (layerTemplate.isEmpty()) return ""; QDomDocument doc; if (!doc.setContent(layerTemplate)) return ""; QDomElement root = doc.documentElement(); static const QString mmString("%1mm"); root.setAttribute("width", mmString.arg(mmW)); root.setAttribute("height", mmString.arg(mmH)); root.setAttribute("viewBox", QString("0 0 %1 %2").arg(mmW).arg(mmH)); QList leaves; TextUtils::collectLeaves(root, leaves); if (leaves.count() > 1) return ""; QDomElement leaf = leaves.at(0); bool ok; double strokeWidth = leaf.attribute("stroke-width").toDouble(&ok); if (!ok) return ""; if (layerTemplate.contains("localProp("width").toDouble(); double h = modelPart()->localProp("height").toDouble(); m_boardSize = QSizeF(w, h); m_boardPos = pos(); } void ResizableBoard::getParams(QPointF & p, QSizeF & s) { p = m_boardPos; s = m_boardSize; } bool ResizableBoard::hasCustomSVG() { return theViewID() == m_viewID; } bool ResizableBoard::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { bool result = Board::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); if (prop.compare("shape", Qt::CaseInsensitive) == 0) { returnProp = tr("shape"); if (!m_modelPart->localProp("height").isValid()) { // display uneditable width and height QFrame * frame = new QFrame(); frame->setObjectName("infoViewPartFrame"); QVBoxLayout * vboxLayout = new QVBoxLayout(); vboxLayout->setAlignment(Qt::AlignLeft); vboxLayout->setSpacing(0); vboxLayout->setMargin(0); vboxLayout->setContentsMargins(0, 3, 0, 0); double tens = pow(10.0, m_decimalsAfter); QRectF r = this->boundingRect(); double w = qRound(GraphicsUtils::pixels2mm(r.width(), GraphicsUtils::SVGDPI) * tens) / tens; QLabel * l1 = new QLabel(tr("width: %1mm").arg(w)); l1->setMargin(0); l1->setObjectName("infoViewLabel"); double h = qRound(GraphicsUtils::pixels2mm(r.height(), GraphicsUtils::SVGDPI) * tens) / tens; QLabel * l2 = new QLabel(tr("height: %1mm").arg(h)); l2->setMargin(0); l2->setObjectName("infoViewLabel"); if (returnWidget) vboxLayout->addWidget(qobject_cast(returnWidget)); vboxLayout->addWidget(l1); vboxLayout->addWidget(l2); frame->setLayout(vboxLayout); returnValue = l1->text() + "," + l2->text(); returnWidget = frame; return true; } returnWidget = setUpDimEntry(false, false, false, returnWidget); returnWidget->setEnabled(swappingEnabled); return true; } return result; } QStringList ResizableBoard::collectValues(const QString & family, const QString & prop, QString & value) { return Board::collectValues(family, prop, value); } void ResizableBoard::paperSizeChanged(int index) { QComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; QModelIndex modelIndex = comboBox->model()->index(index,0); QSizeF size = comboBox->model()->data(modelIndex, Qt::UserRole).toSizeF(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->resizeBoard(size.width(), size.height(), true); } } void ResizableBoard::widthEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; double w = edit->text().toDouble(); double oldW = m_modelPart->localProp("width").toDouble(); if (w == oldW) return; double h = m_modelPart->localProp("height").toDouble(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->resizeBoard(w, h, true); } } void ResizableBoard::heightEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; double h = edit->text().toDouble(); double oldH = m_modelPart->localProp("height").toDouble(); if (h == oldH) return; double w = m_modelPart->localProp("width").toDouble(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->resizeBoard(w, h, true); } } bool ResizableBoard::hasPartNumberProperty() { return false; } void ResizableBoard::paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_hidden || m_layerHidden) return; Board::paintSelected(painter, option, widget); // http://www.gamedev.net/topic/441695-transform-matrix-decomposition/ double m11 = painter->worldTransform().m11(); double m12 = painter->worldTransform().m12(); double scale = m_currentScale = qSqrt((m11 * m11) + (m12 * m12)); // assumes same scaling for both x and y double scalefull = CornerHandleSize / scale; double scalehalf = scalefull / 2; double bottom = m_size.height(); double right = m_size.width(); QPen pen; pen.setWidthF(1.0 / scale); pen.setColor(QColor(0, 0, 0)); QBrush brush(QColor(255, 255, 255)); painter->setPen(pen); painter->setBrush(brush); QPolygonF poly; // upper left poly.append(QPointF(0, 0)); poly.append(QPointF(0, scalefull)); poly.append(QPointF(scalehalf, scalefull)); poly.append(QPointF(scalehalf, scalehalf)); poly.append(QPointF(scalefull, scalehalf)); poly.append(QPointF(scalefull, 0)); painter->drawPolygon(poly); // upper right poly.clear(); poly.append(QPointF(right, 0)); poly.append(QPointF(right, scalefull)); poly.append(QPointF(right - scalehalf, scalefull)); poly.append(QPointF(right - scalehalf, scalehalf)); poly.append(QPointF(right - scalefull, scalehalf)); poly.append(QPointF(right - scalefull, 0)); painter->drawPolygon(poly); // lower left poly.clear(); poly.append(QPointF(0, bottom - scalefull)); poly.append(QPointF(0, bottom)); poly.append(QPointF(scalefull, bottom)); poly.append(QPointF(scalefull, bottom - scalehalf)); poly.append(QPointF(scalehalf, bottom - scalehalf)); poly.append(QPointF(scalehalf, bottom - scalefull)); painter->drawPolygon(poly); // lower right poly.clear(); poly.append(QPointF(right, bottom - scalefull)); poly.append(QPointF(right, bottom)); poly.append(QPointF(right - scalefull, bottom)); poly.append(QPointF(right - scalefull, bottom - scalehalf)); poly.append(QPointF(right - scalehalf, bottom - scalehalf)); poly.append(QPointF(right - scalehalf, bottom - scalefull)); painter->drawPolygon(poly); } bool ResizableBoard::inResize() { return m_corner != ResizableBoard::NO_CORNER; } void ResizableBoard::figureHover() { setAcceptHoverEvents(true); setAcceptedMouseButtons(ALLMOUSEBUTTONS); foreach(ItemBase * lkpi, m_layerKin) { lkpi->setAcceptHoverEvents(false); lkpi->setAcceptedMouseButtons(Qt::NoButton); } } void ResizableBoard::hoverEnterEvent( QGraphicsSceneHoverEvent * event ) { Board::hoverEnterEvent(event); } void ResizableBoard::hoverMoveEvent( QGraphicsSceneHoverEvent * event ) { Board::hoverMoveEvent(event); m_corner = findCorner(event->scenePos(), event->modifiers()); QCursor cursor; switch (m_corner) { case ResizableBoard::BOTTOM_RIGHT: case ResizableBoard::TOP_LEFT: case ResizableBoard::TOP_RIGHT: case ResizableBoard::BOTTOM_LEFT: //DebugDialog::debug("setting scale cursor"); cursor = *CursorMaster::ScaleCursor; break; default: //DebugDialog::debug("setting other cursor"); cursor = Qt::ArrowCursor; break; } setKinCursor(cursor); } void ResizableBoard::hoverLeaveEvent( QGraphicsSceneHoverEvent * event ) { setKinCursor(Qt::ArrowCursor); //DebugDialog::debug("setting arrow cursor"); Board::hoverLeaveEvent(event); } ResizableBoard::Corner ResizableBoard::findCorner(QPointF scenePos, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); if (!this->isSelected()) return ResizableBoard::NO_CORNER; if (this->moveLock()) return ResizableBoard::NO_CORNER; double d = CornerHandleSize / m_currentScale; double d2 = d * d; double right = m_size.width(); double bottom = m_size.height(); //DebugDialog::debug(QString("size %1 %2").arg(right).arg(bottom)); QPointF q = mapToScene(right, bottom); if (GraphicsUtils::distanceSqd(scenePos, q) <= d2) { return ResizableBoard::BOTTOM_RIGHT; } q = mapToScene(0, 0); if (GraphicsUtils::distanceSqd(scenePos, q) <= d2) { return ResizableBoard::TOP_LEFT; } q = mapToScene(right, 0); if (GraphicsUtils::distanceSqd(scenePos, q) <= d2) { return ResizableBoard::TOP_RIGHT; } q = mapToScene(0, bottom); if (GraphicsUtils::distanceSqd(scenePos, q) <= d2) { return ResizableBoard::BOTTOM_LEFT; } return ResizableBoard::NO_CORNER; } void ResizableBoard::setKinCursor(QCursor & cursor) { ItemBase * chief = this->layerKinChief(); chief->setCursor(cursor); foreach (ItemBase * itemBase, chief->layerKin()) { itemBase->setCursor(cursor); } } void ResizableBoard::setKinCursor(Qt::CursorShape cursor) { ItemBase * chief = this->layerKinChief(); chief->setCursor(cursor); foreach (ItemBase * itemBase, chief->layerKin()) { itemBase->setCursor(cursor); } } QFrame * ResizableBoard::setUpDimEntry(bool includeAspectRatio, bool includeRevert, bool includePaperSizes, QWidget * & returnWidget) { double tens = pow(10.0, m_decimalsAfter); double w = qRound(m_modelPart->localProp("width").toDouble() * tens) / tens; // truncate to 1 decimal point double h = qRound(m_modelPart->localProp("height").toDouble() * tens) / tens; // truncate to 1 decimal point QFrame * frame = new QFrame(); frame->setObjectName("infoViewPartFrame"); QVBoxLayout * vboxLayout = new QVBoxLayout(); vboxLayout->setAlignment(Qt::AlignLeft); vboxLayout->setSpacing(1); vboxLayout->setContentsMargins(0, 3, 0, 0); QFrame * subframe1 = new QFrame(); QHBoxLayout * hboxLayout1 = new QHBoxLayout(); hboxLayout1->setAlignment(Qt::AlignLeft); hboxLayout1->setContentsMargins(0, 0, 0, 0); hboxLayout1->setSpacing(2); QFrame * subframe2 = new QFrame(); QHBoxLayout * hboxLayout2 = new QHBoxLayout(); hboxLayout2->setAlignment(Qt::AlignLeft); hboxLayout2->setContentsMargins(0, 0, 0, 0); hboxLayout2->setSpacing(2); QLabel * l1 = new QLabel(tr("width(mm)")); l1->setMargin(0); l1->setObjectName("infoViewLabel"); QLineEdit * e1 = new QLineEdit(); QDoubleValidator * validator = new QDoubleValidator(e1); validator->setRange(0.1, 999.9, m_decimalsAfter); validator->setNotation(QDoubleValidator::StandardNotation); validator->setLocale(QLocale::C); e1->setObjectName("infoViewLineEdit"); e1->setValidator(validator); e1->setMaxLength(4 + m_decimalsAfter); e1->setText(QString::number(w)); QLabel * l2 = new QLabel(tr("height(mm)")); l2->setMargin(0); l2->setObjectName("infoViewLabel"); QLineEdit * e2 = new QLineEdit(); validator = new QDoubleValidator(e1); validator->setRange(0.1, 999.9, m_decimalsAfter); validator->setNotation(QDoubleValidator::StandardNotation); validator->setLocale(QLocale::C); e2->setObjectName("infoViewLineEdit"); e2->setValidator(validator); e2->setMaxLength(4 + m_decimalsAfter); e2->setText(QString::number(h)); hboxLayout1->addWidget(l1); hboxLayout1->addWidget(e1); hboxLayout2->addWidget(l2); hboxLayout2->addWidget(e2); subframe1->setLayout(hboxLayout1); subframe2->setLayout(hboxLayout2); if (returnWidget != NULL) vboxLayout->addWidget(returnWidget); connect(e1, SIGNAL(editingFinished()), this, SLOT(widthEntry())); connect(e2, SIGNAL(editingFinished()), this, SLOT(heightEntry())); m_widthEditor = e1; m_heightEditor = e2; vboxLayout->addWidget(subframe1); vboxLayout->addWidget(subframe2); if (includeAspectRatio || includeRevert || includePaperSizes) { QFrame * subframe3 = new QFrame(); QHBoxLayout * hboxLayout3 = new QHBoxLayout(); hboxLayout3->setAlignment(Qt::AlignLeft); hboxLayout3->setContentsMargins(0, 0, 0, 0); hboxLayout3->setSpacing(0); if (includeAspectRatio) { QLabel * l3 = new QLabel(tr("keep aspect ratio")); l3->setMargin(0); l3->setObjectName("infoViewLabel"); QCheckBox * checkBox = new QCheckBox(); checkBox->setChecked(m_keepAspectRatio); checkBox->setObjectName("infoViewCheckBox"); hboxLayout3->addWidget(l3); hboxLayout3->addWidget(checkBox); connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(keepAspectRatio(bool))); m_aspectRatioCheck = checkBox; m_aspectRatioLabel = l3; } if (includeRevert) { QPushButton * pb = new QPushButton(tr("Revert")); pb->setObjectName("infoViewButton"); hboxLayout3->addWidget(pb); connect(pb, SIGNAL(clicked(bool)), this, SLOT(revertSize(bool))); double w = modelPart()->localProp("width").toDouble(); double ow = modelPart()->localProp("originalWidth").toDouble(); double h = modelPart()->localProp("height").toDouble(); double oh = modelPart()->localProp("originalHeight").toDouble(); pb->setEnabled(qAbs(w - ow) > JND || qAbs(h - oh) > JND); m_revertButton = pb; } if (includePaperSizes) { initPaperSizes(); QLabel * l3 = new QLabel(tr("size")); l3->setMargin(0); l3->setObjectName("infoViewLabel"); m_paperSizeComboBox = new QComboBox(); m_paperSizeComboBox->setObjectName("infoViewComboBox"); m_paperSizeComboBox->setEditable(false); m_paperSizeComboBox->setMinimumWidth(150); m_paperSizeComboBox->addItem(tr("custom")); for (int i = 0; i < PaperSizeNames.count(); i++) { QSizeF dim = PaperSizeDimensions.at(i); m_paperSizeComboBox->addItem(PaperSizeNames.at(i), dim); } QModelIndex modelIndex = m_paperSizeComboBox->model()->index(0,0); m_paperSizeComboBox->model()->setData(modelIndex, 0, Qt::UserRole - 1); // to make it selectable again use Qt::ItemIsSelectable | Qt::ItemIsEnabled) updatePaperSizes(w, h); connect(m_paperSizeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(paperSizeChanged(int))); hboxLayout3->addWidget(l3); hboxLayout3->addWidget(m_paperSizeComboBox); } subframe3->setLayout(hboxLayout3); vboxLayout->addWidget(subframe3); } frame->setLayout(vboxLayout); return frame; } void ResizableBoard::fixWH() { bool okw, okh; QString wstr = m_modelPart->localProp("width").toString(); QString hstr = m_modelPart->localProp("height").toString(); double w = wstr.toDouble(&okw); double h = hstr.toDouble(&okh); //DebugDialog::debug(QString("w:%1 %2 ok:%3 h:%4 %5 ok:%6") //.arg(wstr).arg(w).arg(okw) //.arg(hstr).arg(h).arg(okh)); if ((!okw && !wstr.isEmpty()) || qIsNaN(w) || qIsInf(w) || (!okh && !hstr.isEmpty()) || qIsNaN(h) || qIsInf(h)) { DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); DebugDialog::debug("bad width or height in ResizableBoard or subclass " + wstr + " " + hstr); DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); m_modelPart->setLocalProp("width", ""); m_modelPart->setLocalProp("height", ""); } } void ResizableBoard::setWidthAndHeight(double w, double h) { if (m_widthEditor) { m_widthEditor->setText(QString::number(w)); } if (m_heightEditor) { m_heightEditor->setText(QString::number(h)); } updatePaperSizes(w, h); } QString ResizableBoard::getShapeForRenderer(const QString & svg, ViewLayer::ViewLayerID viewLayerID) { QString xmlName = ViewLayer::viewLayerXmlNameFromID(viewLayerID); SvgFileSplitter splitter; QString xml = svg; bool result = splitter.splitString(xml, xmlName); if (result) { xml = splitter.elementString(xmlName); } else { xml = ""; } QString header("\n" ""; //DebugDialog::debug("resizableBoard " + header); return header; } void ResizableBoard::keepAspectRatio(bool checkState) { m_keepAspectRatio = checkState; } void ResizableBoard::revertSize(bool) { double ow = modelPart()->localProp("originalWidth").toDouble(); double oh = modelPart()->localProp("originalHeight").toDouble(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->resizeBoard(ow, oh, true); m_revertButton->setEnabled(false); } } void ResizableBoard::updatePaperSizes(double w, double h) { if (m_paperSizeComboBox == NULL) return; int currentIndex = 0; for (int i = 0; i < PaperSizeNames.count(); i++) { QSizeF dim = PaperSizeDimensions.at(i); if (qAbs(w - dim.width()) < 0.1 && qAbs(h - dim.height()) < 0.1) { currentIndex = i + 1; } } QString custom = tr("custom"); if (currentIndex == 0) { custom = QString("%1x%2").arg(w).arg(h); } m_paperSizeComboBox->setItemText(0, custom); m_paperSizeComboBox->setCurrentIndex(currentIndex); } void ResizableBoard::initPaperSizes() { if (PaperSizeNames.count() == 0) { PaperSizeNames << tr("A0 (1030x1456)") << tr("A1 (728x1030)") << tr("A2 (515x728)") << tr("A3 (364x515)") << tr("A4 (257x364)") << tr("A5 (182x257)") << tr("A6 (128x182)") << tr("Letter (8.5x11)") << tr("Legal (8.5x14)") << tr("Ledger (17x11)") << tr("Tabloid (11x17)"); PaperSizeDimensions << QSizeF(1030,1456) << QSizeF(728,1030) << QSizeF(515,728) << QSizeF(364,515) << QSizeF(257,364) << QSizeF(182,257) << QSizeF(128,182) << QSizeF(215.9,279.4) << QSizeF(215.9,355.6) << QSizeF(432,279) << QSizeF(279,432); } } �����������������������������������������������������������������������������fritzing-app-0.9.3b/src/items/resizableboard.h������������������������������������������������������0000664�0000000�0000000�00000016250�12723636013�0021357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef RESIZABLEBOARD_H #define RESIZABLEBOARD_H #include #include #include #include #include #include #include #include #include #include #include "paletteitem.h" class Board : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Board(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Board(); QStringList collectValues(const QString & family, const QString & prop, QString & value); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool rotation45Allowed(); bool stickyEnabled(); PluralType isPlural(); bool canFindConnectorsUnder(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); protected: void setupLoadImage(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget); void setFileNameItems(); virtual QStringList & getImageNames(); virtual QStringList & getNewImageNames(); virtual bool checkImage(const QString & filename); void unableToLoad(const QString & fileName, const QString & reason); bool canLoad(const QString & fileName, const QString & reason); virtual void prepLoadImageAux(const QString & fileName, bool addName); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); protected: static void moreCheckImage(const QString & filename); static QString setBoardOutline(const QString & svg); public: static QString OneLayerTranslated; static QString TwoLayersTranslated; public: static bool isBoard(ItemBase *); static bool isBoard(ModelPart *); static QString convertToXmlName(const QString &); static QString convertFromXmlName(const QString &); protected slots: void prepLoadImage(); void fileNameEntry(const QString & filename); protected: QPointer m_fileNameComboBox; bool m_svgOnly; protected: static QStringList BoardImageNames; static QStringList NewBoardImageNames; }; class ResizableBoard : public Board { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call ResizableBoard(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~ResizableBoard(); virtual bool resizeMM(double w, double h, const LayerHash & viewLayers); void resizePixels(double w, double h, const LayerHash & viewLayers); void loadLayerKin(const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement); virtual void setInitialSize(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); void saveParams(); void getParams(QPointF &, QSizeF &); bool hasCustomSVG(); QSizeF getSizeMM(); void addedToScene(bool temporary); bool hasPartNumberProperty(); void paintSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool inResize(); void figureHover(); public slots: virtual void widthEntry(); virtual void heightEntry(); void keepAspectRatio(bool checkState); void revertSize(bool); virtual void paperSizeChanged(int); protected: enum Corner { NO_CORNER = 0, TOP_LEFT , TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }; protected: void mousePressEvent(QGraphicsSceneMouseEvent * event ); void mouseMoveEvent(QGraphicsSceneMouseEvent * event ); void mouseReleaseEvent(QGraphicsSceneMouseEvent * event ); void hoverEnterEvent(QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent(QGraphicsSceneHoverEvent * event ); void hoverMoveEvent(QGraphicsSceneHoverEvent * event ); QString makeBoardSvg(double mmW, double mmH, double milsW, double milsH); QString makeSilkscreenSvg(ViewLayer::ViewLayerID, double mmW, double mmH, double milsW, double milsH); QString makeSvg(double mmW, double mmH, const QString & layerTemplate); QStringList collectValues(const QString & family, const QString & prop, QString & value); virtual void loadTemplates(); virtual double minWidth(); virtual double minHeight(); virtual ViewLayer::ViewID theViewID(); virtual QString makeLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH); virtual QString makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH); virtual QString makeNextLayerSvg(ViewLayer::ViewLayerID, double mmW, double mmH, double milsW, double milsH); virtual void resizeMMAux(double w, double h); virtual ResizableBoard::Corner findCorner(QPointF p, Qt::KeyboardModifiers); void setKinCursor(QCursor &); void setKinCursor(Qt::CursorShape); QFrame * setUpDimEntry(bool includeAspectRatio, bool includeRevert, bool includePaperSizes, QWidget * & returnWidget); void fixWH(); void setWidthAndHeight(double w, double h); QString getShapeForRenderer(const QString & svg, ViewLayer::ViewLayerID viewLayerID); void initPaperSizes(); void updatePaperSizes(double width, double height); protected: static const double CornerHandleSize; Corner m_corner; QSizeF m_boardSize; QPointF m_boardPos; QPointer m_widthEditor; QPointer m_heightEditor; bool m_keepAspectRatio; QSizeF m_aspectRatio; double m_currentScale; QPointer m_aspectRatioCheck; QPointer m_aspectRatioLabel; QPointer m_revertButton; QPointer m_paperSizeComboBox; QPointF m_resizeMousePos; QSizeF m_resizeStartSize; QPointF m_resizeStartPos; QPointF m_resizeStartTopLeft; QPointF m_resizeStartBottomRight; QPointF m_resizeStartTopRight; QPointF m_resizeStartBottomLeft; int m_decimalsAfter; }; #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������fritzing-app-0.9.3b/src/items/ruler.cpp�������������������������������������������������������������0000664�0000000�0000000�00000036545�12723636013�0020064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "ruler.h" #include "../utils/graphicsutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "moduleidnames.h" #include "../utils/textutils.h" #include "../utils/boundedregexpvalidator.h" #include #include #include #include #include #include #include static const int IndexCm = 0; static const int IndexIn = 1; static QString DefaultWidth = ""; Ruler::Ruler( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { m_widthEditor = NULL; m_unitsEditor = NULL; m_widthValidator = NULL; QString w = modelPart->localProp("width").toString(); if (w.isEmpty()) { if (DefaultWidth.isEmpty()) { DefaultWidth = modelPart->properties().value("width", "10cm"); } m_modelPart->setLocalProp("width", DefaultWidth); } } Ruler::~Ruler() { } void Ruler::resizeMM(double magnitude, double unitsFlag, const LayerHash & viewLayers) { // note this really isn't resizeMM but resizeUnits Q_UNUSED(viewLayers); double w = TextUtils::convertToInches(prop("width")); QString units((unitsFlag == IndexCm) ? "cm" : "in"); double newW = TextUtils::convertToInches(QString::number(magnitude) + units); if (w == newW) return; // save local prop incase render fails so we can revert back to orignal. QString orignalProp = m_modelPart->localProp("width").toString(); // set local prop so makeSvg has the current mag and units. modelPart()->setLocalProp("width", QVariant(QString::number(magnitude) + units)); QString s = makeSvg(newW); bool result = resetRenderer(s); if (!result) { // if render error restore orginal prop modelPart()->setLocalProp("width", QVariant(orignalProp)); } // DebugDialog::debug(QString("fast load result %1 %2").arg(result).arg(s)); } QString Ruler::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { double w = TextUtils::convertToInches(m_modelPart->localProp("width").toString()); if (w != 0) { QString xml; switch (viewLayerID) { case ViewLayer::BreadboardRuler: case ViewLayer::SchematicRuler: case ViewLayer::PcbRuler: xml = makeSvg(w); break; default: break; } if (!xml.isEmpty()) { return PaletteItemBase::normalizeSvg(xml, viewLayerID, blackOnly, dpi, factor); } } return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } QString Ruler::makeSvg(double inches) { const double cm = 1 / 2.54; const double offset = 0.125; const double mmW = inches * 25.4;// 1/10 centimeter constant const double mmmW = inches * 254;// 1/100 centimeter constant // get units int units = m_modelPart->localProp("width").toString().contains("cm") ? IndexCm : IndexIn; QString svg = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, (inches + offset + offset) * GraphicsUtils::SVGDPI, GraphicsUtils::SVGDPI/2); int counter; if (units == IndexCm) { counter = 0; svg += ""; // 1/100 centimeter spacing for (int i = 0; i <= qCeil(mmmW); i++) { double h = cm / 12; double h2 = h - (cm / 12); double x = (offset + ((double)i / 254)) * GraphicsUtils::StandardFritzingDPI; if (i % 5 == 0) { h = cm / 6; } if (i % 10 != 0 ) { svg += QString("\n") .arg(x) .arg(h * GraphicsUtils::StandardFritzingDPI); // Uncomment if you want 1/100cm scale on both sides. /*svg += QString("\n") .arg(x) .arg((GraphicsUtils::StandardFritzingDPI/2)-(h * GraphicsUtils::StandardFritzingDPI)) .arg((GraphicsUtils::StandardFritzingDPI/2)-(h2 * GraphicsUtils::StandardFritzingDPI));*/ } } svg += ""; svg += ""; counter = 0; // centimeters and millimeters spacing and text for (int i = 0; i <= qCeil(mmW); i++) { double h = cm / 4; double h2 = h - (cm / 4); double x = (offset + (i / 25.4)) * GraphicsUtils::StandardFritzingDPI; if (i % 10 == 0) { h = cm / 2; double y = (GraphicsUtils::StandardFritzingDPI/4)+35;//(h + .101) * GraphicsUtils::StandardFritzingDPI; svg += QString("%3") .arg(x) .arg(y) .arg(QString::number(counter++)); if (counter == 1) { svg += QString("cm").arg(x + 103).arg(y); } } else if (i % 5 == 0) { h = 3 * cm / 8; } svg += QString("\n") .arg(x) .arg(h * GraphicsUtils::StandardFritzingDPI); svg += QString("\n") .arg(x) .arg((GraphicsUtils::StandardFritzingDPI/2)-(h * GraphicsUtils::StandardFritzingDPI)) .arg((GraphicsUtils::StandardFritzingDPI/2)-(h2 * GraphicsUtils::StandardFritzingDPI)); } svg += "\n"; svg += QString("1/100").arg((GraphicsUtils::StandardFritzingDPI * offset / 2.0) + 7).arg(30); svg += ""; } else { svg += ""; counter = 0; // 1/16, 1/8, 1/4, & 1 inch spacing and text for (int i = 0; i <= inches * 16; i++) { double h = 0.085; double x = (offset + (i / 16.0)) * GraphicsUtils::StandardFritzingDPI; if (i % 16 == 0) { // I wonder if QT compiler would see that this is just a constant, and not perform the runtime math? // If not calculating out the value could save some cycles:) h = 0.196875;//.125 + (1.15 / 16); double y = (GraphicsUtils::StandardFritzingDPI/4)+35;//(h + .101) * GraphicsUtils::StandardFritzingDPI; svg += QString("%3") .arg(x) .arg(y) .arg(QString::number(counter++)); if (counter == 1) { svg += QString("in").arg(x + 103).arg(y); } } else if (i % 8 == 0) { h = 0.17812;//.125 + (.85 / 16); } else if (i % 4 == 0) { h = 0.14062;//.125 + (.25 / 16); } svg += QString("\n") .arg(x) .arg(h * GraphicsUtils::StandardFritzingDPI); svg += QString("\n") .arg(x) .arg((GraphicsUtils::StandardFritzingDPI/2)-(h * GraphicsUtils::StandardFritzingDPI)) .arg((GraphicsUtils::StandardFritzingDPI/2)); } // 1/10 inch spacing and text for (int i = 0; i <= inches * 10; i++) { double x = (offset + (i / 10.0)) * GraphicsUtils::StandardFritzingDPI; double h = .125 + (.9 / 16); double h2 = h - (cm / 6); if (i % 10 != 0 && i % 5 != 0) { svg += QString("\n") .arg(x) .arg((h * GraphicsUtils::StandardFritzingDPI)) .arg((h2 * GraphicsUtils::StandardFritzingDPI)); } } svg += "\n"; svg += QString("1/16").arg((GraphicsUtils::StandardFritzingDPI * offset / 2.0) + 7).arg(30); svg += QString("1/10").arg((GraphicsUtils::StandardFritzingDPI * offset / 2.0) + 7).arg(140); svg += ""; } svg += ""; return svg; } bool Ruler::hasCustomSVG() { switch (m_viewID) { case ViewLayer::PCBView: case ViewLayer::SchematicView: case ViewLayer::BreadboardView: return true; default: return ItemBase::hasCustomSVG(); } } bool Ruler::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { bool result = PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); if (prop.compare("width", Qt::CaseInsensitive) == 0) { returnProp = tr("width"); int units = m_modelPart->localProp("width").toString().contains("cm") ? IndexCm : IndexIn; QLineEdit * e1 = new QLineEdit(); QDoubleValidator * validator = new QDoubleValidator(e1); validator->setRange(1.0, 20 * ((units == IndexCm) ? 2.54 : 1), 2); validator->setNotation(QDoubleValidator::StandardNotation); validator->setLocale(QLocale::C); e1->setValidator(validator); e1->setEnabled(swappingEnabled); QString temp = m_modelPart->localProp("width").toString(); temp.chop(2); e1->setText(temp); e1->setObjectName("infoViewLineEdit"); e1->setMaximumWidth(80); m_widthEditor = e1; m_widthValidator = validator; // Radio Buttons QRadioButton *radioCm = new QRadioButton(tr("&cm")); QRadioButton *radioIn = new QRadioButton(tr("&in")); // set radio button object names radioCm->setObjectName("cm"); radioIn->setObjectName("in"); // update pointer and set checked if (units == IndexIn) { radioIn->setChecked(true); m_unitsEditor = radioIn; } else { radioCm->setChecked(true); m_unitsEditor = radioCm; } // spacer to keep radio buttons together when resizing Inspector Window QSpacerItem *item = new QSpacerItem(1,1, QSizePolicy::Expanding, QSizePolicy::Fixed); QHBoxLayout * hboxLayout = new QHBoxLayout(); hboxLayout->setAlignment(Qt::AlignRight); hboxLayout->setContentsMargins(0, 0, 0, 0); hboxLayout->setSpacing(5); hboxLayout->setMargin(0); hboxLayout->addWidget(e1); hboxLayout->addWidget(radioCm); hboxLayout->addWidget(radioIn); hboxLayout->addSpacerItem(item); QFrame * frame = new QFrame(); frame->setLayout(hboxLayout); frame->setObjectName("infoViewPartFrame"); connect(e1, SIGNAL(editingFinished()), this, SLOT(widthEntry())); connect(radioCm, SIGNAL(clicked()), this, SLOT(unitsEntry())); connect(radioIn, SIGNAL(clicked()), this, SLOT(unitsEntry())); returnValue = temp + QString::number(units); returnWidget = frame; return true; } return result; } void Ruler::widthEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; QString t = edit->text(); QString w = prop("width"); w.chop(2); if (t.compare(w) == 0) { return; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { // get current object units int units = (m_unitsEditor->objectName() == "cm") ? IndexCm : IndexIn; DefaultWidth = edit->text() + m_unitsEditor->objectName(); infoGraphicsView->resizeBoard(edit->text().toDouble(), units, false); } } void Ruler::unitsEntry() { // get clicked object QRadioButton * obj = qobject_cast(sender()); if (obj == NULL) return; // update pointer m_unitsEditor = obj; QString units = obj->objectName(); double inches = TextUtils::convertToInches(prop("width")); // save local prop incase render fails so we can revert back to orignal. QString orignalProp = m_modelPart->localProp("width").toString(); if (units == "in") { // set local prop so makeSvg has the current width and units. modelPart()->setLocalProp("width", QVariant(QString::number(inches) + "in")); QString s = makeSvg(inches); bool result = resetRenderer(s); if (result) { m_widthEditor->setText(QString::number(inches)); m_widthValidator->setTop(20); } else { // if render error restore orginal prop modelPart()->setLocalProp("width", QVariant(orignalProp)); } } else { // set local prop so makeSvg has the current width and units. modelPart()->setLocalProp("width", QVariant(QString::number(inches * 2.54) + "cm")); QString s = makeSvg(inches); bool result = resetRenderer(s); if (result) { m_widthEditor->setText(QString::number(inches * 2.54)); m_widthValidator->setTop(20 * 2.54); } else { // if render error restore orginal prop modelPart()->setLocalProp("width", QVariant(orignalProp)); } } DefaultWidth = prop("width"); } bool Ruler::stickyEnabled() { return false; } bool Ruler::hasPartLabel() { return false; } ItemBase::PluralType Ruler::isPlural() { return Singular; } void Ruler::addedToScene(bool temporary) { if (this->scene()) { LayerHash viewLayers; QString w = prop("width"); modelPart()->setLocalProp("width", ""); // makes sure resizeMM will do the work double inches = TextUtils::convertToInches(w); if (w.endsWith("cm")) { resizeMM(inches * 2.54, IndexCm, viewLayers); } else { resizeMM(inches, IndexIn, viewLayers); } } return PaletteItem::addedToScene(temporary); } bool Ruler::hasPartNumberProperty() { return false; } bool Ruler::canFindConnectorsUnder() { return false; } ViewLayer::ViewID Ruler::useViewIDForPixmap(ViewLayer::ViewID, bool) { return ViewLayer::IconView; } fritzing-app-0.9.3b/src/items/ruler.h000066400000000000000000000046311272363601300175200ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef RULER_H #define RULER_H #include #include #include #include #include #include #include "paletteitem.h" class Ruler : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Ruler(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Ruler(); void resizeMM(double magnitude, double unitsFlag, const LayerHash & viewLayers); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool hasCustomSVG(); bool stickyEnabled(); bool hasPartLabel(); PluralType isPlural(); void addedToScene(bool temporary); bool hasPartNumberProperty(); bool canFindConnectorsUnder(); public slots: void widthEntry(); void unitsEntry(); protected: QString makeSvg(double inches); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); protected: QPointer m_widthEditor; QPointer m_unitsEditor; QPointer m_widthValidator; }; #endif fritzing-app-0.9.3b/src/items/schematicframe.cpp000066400000000000000000000402271272363601300216760ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ // TODO: // // direct editing (eventually) #include "schematicframe.h" #include "partfactory.h" #include "../utils/graphicsutils.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../svg/svgfilesplitter.h" #include "../debugdialog.h" #include "moduleidnames.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static TemplateThing SchematicTemplate; static TemplateThing OldSchematicTemplate; QHash FrameProps; static const QString DisplayFormat("dd MMM yyyy hh:mm:ss"); SchematicFrame::SchematicFrame( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : ResizableBoard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { if (FrameProps.count() == 0) { FrameProps.insert("descr", ""); FrameProps.insert("project", tr("Project")); FrameProps.insert("filename", tr("Filename")); FrameProps.insert("date", tr("Date")); FrameProps.insert("sheet", tr("Sheet")); FrameProps.insert("rev", tr("Rev")); } foreach (QString prop, FrameProps.keys()) { if (modelPart->localProp(prop).toString().isEmpty()) { modelPart->setLocalProp(prop, modelPart->properties().value(prop)); } } if (modelPart->localProp("date").toString().isEmpty()) { QDateTime dt = QDateTime::currentDateTime(); modelPart->setLocalProp("date", QString::number(dt.toTime_t())); } if (modelPart->localProp("sheet").toString().isEmpty()) { modelPart->setLocalProp("sheet","1/1"); } m_wrapInitialized = false; m_textEdit = new QTextEdit(); m_textEdit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); m_textEdit->setAcceptRichText(false); m_textEdit->setLineWrapMode(QTextEdit::FixedPixelWidth); } SchematicFrame::~SchematicFrame() { if (m_textEdit) { delete m_textEdit; } } void SchematicFrame::loadTemplates() { if (SchematicTemplate.svgTemplate.isEmpty()) { loadTemplate(":/resources/templates/schematic_frame_template.txt", ":/resources/parts/svg/core/schematic/frame.svg", SchematicTemplate); loadTemplate(":/resources/templates/0.3.schem.schematic_frame_template.txt", ":/resources/parts/svg/obsolete/schematic/0.3.schem.frame.svg", OldSchematicTemplate); } } void SchematicFrame::loadTemplate(const QString & tPath, const QString & fPath, TemplateThing & templateThing) { QFile file2(fPath); file2.open(QFile::ReadOnly); QString original = file2.readAll(); file2.close(); templateThing.size = QSizeF(TextUtils::getViewBoxCoord(original, 2), TextUtils::getViewBoxCoord(original, 3)); QFile file(tPath); file.open(QFile::ReadOnly); templateThing.svgTemplate = file.readAll(); file.close(); /* static QRegExp NumberFinder("(=['\"][\\[\\{]{0,1})(\\d+(\\.\\d){0,1})[\\]\\}]{0,1}['\"]"); SchematicTemplate = OldSchematicTemplate; int pos = 0; while (true) { int ix = NumberFinder.indexIn(SchematicTemplate, pos); if (ix < 0) break; double d = NumberFinder.cap(2).toDouble(); QString d3 = QString("%1").arg(d / 3, 0, 'g', 5); int offset = NumberFinder.cap(0).count(); if (SchematicTemplate.indexOf("version", ix - 7) < 0) { SchematicTemplate.replace(ix + NumberFinder.cap(1).count(), NumberFinder.cap(2).count(), d3); offset += d3.count() - NumberFinder.cap(2).count(); } pos = ix + offset; } DebugDialog::debug("schematic template " + SchematicTemplate); */ QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(templateThing.svgTemplate, true, &errorStr, &errorLine, &errorColumn)) { return; } QDomElement root = domDocument.documentElement(); QDomElement descr =TextUtils::findElementWithAttribute(root, "id", "descr"); QString xString = descr.attribute("x"); xString = xString.mid(1, xString.length() - 2); // remove the brackets; templateThing.margin = xString.toInt() * 2; templateThing.fontSize = descr.attribute("font-size").toDouble(); QDomNodeList nodeList = root.elementsByTagName("rect"); for (int i = 0; i < nodeList.count(); i++) { double sw = nodeList.at(i).toElement().attribute("stroke-width").toDouble(); if (sw != 0) templateThing.strokeWidth = sw; break; } } QString SchematicFrame::makeLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { Q_UNUSED(mmW); Q_UNUSED(mmH); if (SchematicTemplate.svgTemplate.isEmpty()) return ""; switch (viewLayerID) { case ViewLayer::SchematicFrame: break; default: return ""; } TemplateThing & templateThing = moduleID().contains(PartFactory::OldSchematicPrefix) ? OldSchematicTemplate : SchematicTemplate; if (!m_wrapInitialized) { m_wrapInitialized = true; QFont font = m_textEdit->font(); font.setFamily("Droid Sans"); font.setWeight(QFont::Normal); font.setPointSizeF(72.0 * templateThing.fontSize / GraphicsUtils::StandardFritzingDPI); m_textEdit->setFont(font); m_textEdit->setLineWrapColumnOrWidth(GraphicsUtils::SVGDPI * (templateThing.size.width() - templateThing.margin) / GraphicsUtils::StandardFritzingDPI); } if (milsW < templateThing.size.width()) milsW = templateThing.size.width(); if (milsH < templateThing.size.height()) milsH = templateThing.size.height(); QString svg = templateThing.svgTemplate.arg(milsW / 1000).arg(milsH / 1000).arg(milsW).arg(milsH).arg(milsW - templateThing.strokeWidth).arg(milsH - templateThing.strokeWidth); svg = TextUtils::incrementTemplateString(svg, 1, milsW - templateThing.size.width(), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); svg.replace("{", "["); svg.replace("}", "]"); svg = TextUtils::incrementTemplateString(svg, 1, milsH - templateThing.size.height(), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); QHash hash; foreach (QString propp, FrameProps.keys()) { hash.insert(propp, prop(propp)); QString label = FrameProps.value(propp); if (!label.isEmpty()) { hash.insert(propp + " label", label); } } // figure out the width and the font QString string = prop("descr"); m_textEdit->setPlainText(string); QTextCursor textCursor = m_textEdit->cursorForPosition(QPoint(0,0)); QTextLayout * textLayout = textCursor.block().layout(); int lc2 = textLayout->lineCount(); if (lc2 > 1) { QTextLine textLine = textLayout->lineAt(0); hash.insert("descr", string.left(textLine.textLength())); textLine = textLayout->lineAt(1); string = string.mid(textLine.textStart(), textLine.textLength()); hash.insert("descr+", string); } QDateTime dt; dt.setTime_t(modelPart()->localProp("date").toUInt()); hash.insert("date", dt.toString(DisplayFormat)); DebugDialog::debug("svg " + svg); return TextUtils::convertExtendedChars(TextUtils::replaceTextElements(svg, hash)); } QString SchematicFrame::makeNextLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH) { Q_UNUSED(mmW); Q_UNUSED(mmH); Q_UNUSED(milsW); Q_UNUSED(milsH); Q_UNUSED(viewLayerID); return ""; } QString SchematicFrame::makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH) { return makeLayerSvg(ViewLayer::SchematicFrame, mmW, mmH, milsW, milsH); } ViewLayer::ViewID SchematicFrame::theViewID() { return ViewLayer::SchematicView; } double SchematicFrame::minWidth() { double w = moduleID().contains(PartFactory::OldSchematicPrefix) ? OldSchematicTemplate.size.width() : SchematicTemplate.size.width(); return w * GraphicsUtils::SVGDPI / GraphicsUtils::StandardFritzingDPI; } double SchematicFrame::minHeight() { double h = moduleID().contains(PartFactory::OldSchematicPrefix) ? OldSchematicTemplate.size.height() : SchematicTemplate.size.height(); return h * GraphicsUtils::SVGDPI / GraphicsUtils::StandardFritzingDPI; } void SchematicFrame::addedToScene(bool temporary) { if (prop("filename").isEmpty()) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { modelPart()->setLocalProp("filename", infoGraphicsView->filenameIf()); } } ResizableBoard::addedToScene(temporary); resizeMMAux(m_modelPart->localProp("width").toDouble(), m_modelPart->localProp("height").toDouble()); } QString SchematicFrame::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { return ResizableBoard::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); } bool SchematicFrame::makeLineEdit(QWidget * parent, const QString & family, const QString & propp, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget) { Q_UNUSED(value); Q_UNUSED(family); returnProp = ItemBase::TranslatedPropertyNames.value(propp.toLower()); returnValue = prop(propp); QLineEdit * e1 = new QLineEdit(parent); e1->setObjectName("infoViewLineEdit"); e1->setProperty("prop", QVariant(propp)); e1->setText(returnValue); e1->setEnabled(swappingEnabled); connect(e1, SIGNAL(editingFinished()), this, SLOT(propEntry())); returnWidget = e1; return true; } bool SchematicFrame::collectExtraInfo(QWidget * parent, const QString & family, const QString & propp, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (propp.compare("shape", Qt::CaseInsensitive) == 0) { returnWidget = setUpDimEntry(false, false, true, returnWidget); returnWidget->setEnabled(swappingEnabled); returnProp = tr("shape"); return true; } if (propp.compare("date", Qt::CaseInsensitive) == 0) { QDateTimeEdit * dateTimeEdit = new QDateTimeEdit(QDateTime::currentDateTime(), parent); QString d = prop("date"); if (!d.isEmpty()) { QDateTime dateTime; dateTime.setTime_t(d.toUInt()); dateTimeEdit->setDateTime(dateTime); } //dateTimeEdit->setCalendarPopup(true); dateTimeEdit->setDisplayFormat(DisplayFormat); connect(dateTimeEdit, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(dateTimeEntry(QDateTime))); dateTimeEdit->setObjectName("infoViewDateEdit"); dateTimeEdit->setEnabled(swappingEnabled); returnProp = ItemBase::TranslatedPropertyNames.value(propp.toLower()); returnValue = prop(propp); returnWidget = dateTimeEdit; return true; } if (propp.compare("sheet", Qt::CaseInsensitive) == 0) { QString value = prop("sheet"); QStringList strings = value.split("/"); if (strings.count() != 2) { strings.clear(); strings << "1" << "1"; } QFrame * frame = new QFrame(parent); QSpinBox * spin1 = new QSpinBox(frame); spin1->setMinimum(1); spin1->setMaximum(999); spin1->setValue(strings[0].toInt()); connect(spin1, SIGNAL(valueChanged(int)), this, SLOT(sheetEntry(int))); spin1->setObjectName("infoViewSpinner"); spin1->setProperty("role", "numerator"); spin1->setEnabled(swappingEnabled); QSpinBox * spin2 = new QSpinBox(frame); spin2->setMinimum(1); spin2->setMaximum(999); spin2->setValue(strings[1].toInt()); connect(spin2, SIGNAL(valueChanged(int)), this, SLOT(sheetEntry(int))); spin2->setObjectName("infoViewSpinner"); spin2->setProperty("role", "denominator"); spin2->setEnabled(swappingEnabled); QLabel * label = new QLabel(parent); label->setText(tr("of")); label->setObjectName("infoViewOfLabel"); label->setAlignment(Qt::AlignCenter); QHBoxLayout * hBoxLayout = new QHBoxLayout(frame); hBoxLayout->addWidget(spin1); hBoxLayout->addWidget(label); hBoxLayout->addWidget(spin2); hBoxLayout->addStretch(); frame->setLayout(hBoxLayout); returnProp = ItemBase::TranslatedPropertyNames.value(propp.toLower()); returnValue = prop(propp); returnWidget = frame; return true; } if (FrameProps.keys().contains(propp)) { return makeLineEdit(parent, family, propp, value, swappingEnabled, returnProp, returnValue, returnWidget); } return PaletteItem::collectExtraInfo(parent, family, propp, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void SchematicFrame::setProp(const QString & prop, const QString & value) { ResizableBoard::setProp(prop, value); if (FrameProps.keys().contains(prop)) { modelPart()->setLocalProp(prop, value); resizeMMAux(modelPart()->localProp("width").toDouble(), modelPart()->localProp("height").toDouble()); } } bool SchematicFrame::canEditPart() { return false; } bool SchematicFrame::hasPartLabel() { return false; } bool SchematicFrame::stickyEnabled() { return false; } ItemBase::PluralType SchematicFrame::isPlural() { return Plural; } bool SchematicFrame::rotationAllowed() { return false; } bool SchematicFrame::rotation45Allowed() { return false; } bool SchematicFrame::hasPartNumberProperty() { return false; } void SchematicFrame::setInitialSize() { double w = m_modelPart->localProp("width").toDouble(); if (w == 0) { QSizeF size = moduleID().contains(PartFactory::OldSchematicPrefix) ? OldSchematicTemplate.size : SchematicTemplate.size; // set the size so the infoGraphicsView will display the size as you drag modelPart()->setLocalProp("width", 25.4 * size.width() / GraphicsUtils::StandardFritzingDPI); modelPart()->setLocalProp("height", 25.4 * size.height() / GraphicsUtils::StandardFritzingDPI); } } void SchematicFrame::propEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; QString propp = edit->property("prop").toString(); if (propp.isEmpty()) return; QString current = prop(propp); if (edit->text().compare(current) == 0) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, propp, ItemBase::TranslatedPropertyNames.value(propp), current, edit->text(), true); } } void SchematicFrame::dateTimeEntry(QDateTime dateTime) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "date", tr("date"), prop("date"), QString::number(dateTime.toTime_t()), true); } } void SchematicFrame::sheetEntry(int value) { QString role = sender()->property("role").toString(); QString sheet = prop("sheet"); QStringList strings = sheet.split("/"); if (strings.count() != 2) { strings.clear(); strings << "1" << "1"; } if (role.compare("numerator") == 0) { strings[0] = QString::number(value); } else if (role.compare("denominator") == 0) { strings[1] = QString::number(value); } else return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "sheet", tr("sheet"), prop("sheet"), strings.at(0) + "/" + strings[1], true); } } ViewLayer::ViewID SchematicFrame::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::SchematicView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } fritzing-app-0.9.3b/src/items/schematicframe.h000066400000000000000000000063611272363601300213440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef SCHEMATIC_FRAME_H #define SCHEMATIC_FRAME_H #include #include #include #include #include #include #include #include "resizableboard.h" struct TemplateThing { QString svgTemplate; QSizeF size; double margin; double fontSize; double strokeWidth; }; class SchematicFrame : public ResizableBoard { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call SchematicFrame(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~SchematicFrame(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool canEditPart(); void setProp(const QString & prop, const QString & value); bool hasPartLabel(); bool stickyEnabled(); PluralType isPlural(); void addedToScene(bool temporary); bool rotationAllowed(); bool rotation45Allowed(); bool hasPartNumberProperty(); void setInitialSize(); protected slots: void propEntry(); void dateTimeEntry(QDateTime); void sheetEntry(int); protected: double minWidth(); double minHeight(); ViewLayer::ViewID theViewID(); QString makeLayerSvg(ViewLayer::ViewLayerID viewLayerID, double mmW, double mmH, double milsW, double milsH); QString makeFirstLayerSvg(double mmW, double mmH, double milsW, double milsH); QString makeNextLayerSvg(ViewLayer::ViewLayerID, double mmW, double mmH, double milsW, double milsH); bool makeLineEdit(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); void loadTemplates(); protected: static void loadTemplate(const QString & tPath, const QString & fPath, TemplateThing &); protected: QTextEdit * m_textEdit; bool m_wrapInitialized; protected: }; #endif fritzing-app-0.9.3b/src/items/schematicsubpart.cpp000066400000000000000000000045651272363601300222710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09 Mrz 2013) $ ********************************************************************/ #include "schematicsubpart.h" SchematicSubpart::SchematicSubpart( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { } SchematicSubpart::~SchematicSubpart() { } void SchematicSubpart::hoverEnterEvent (QGraphicsSceneHoverEvent * event) { PaletteItem::hoverEnterEvent(event); if (m_superpart) { foreach (ItemBase * itemBase, m_superpart->subparts()) { if (itemBase != this) { SchematicSubpart * subpart = qobject_cast(itemBase); if (subpart) subpart->simpleHoverEnterEvent(event); } } } } void SchematicSubpart::hoverLeaveEvent (QGraphicsSceneHoverEvent * event) { PaletteItem::hoverLeaveEvent(event); if (m_superpart) { foreach (ItemBase * itemBase, m_superpart->subparts()) { if (itemBase != this) { SchematicSubpart * subpart = qobject_cast(itemBase); if (subpart) subpart->simpleHoverLeaveEvent(event); } } } } void SchematicSubpart::simpleHoverEnterEvent (QGraphicsSceneHoverEvent * event) { PaletteItem::hoverEnterEvent(event); } void SchematicSubpart::simpleHoverLeaveEvent (QGraphicsSceneHoverEvent * event) { PaletteItem::hoverLeaveEvent(event); } fritzing-app-0.9.3b/src/items/schematicsubpart.h000066400000000000000000000032501272363601300217240ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09 Mrz 2013) $ ********************************************************************/ #ifndef SCHEMATICSUBPART_H #define SCHEMATICSUBPART_H #include "paletteitem.h" class SchematicSubpart : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call SchematicSubpart(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~SchematicSubpart(); void hoverEnterEvent(QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent( QGraphicsSceneHoverEvent * event ); protected: void simpleHoverEnterEvent(QGraphicsSceneHoverEvent * event ); void simpleHoverLeaveEvent( QGraphicsSceneHoverEvent * event ); }; #endif fritzing-app-0.9.3b/src/items/screwterminal.cpp000066400000000000000000000276151272363601300216100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "screwterminal.h" #include "../utils/graphicsutils.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "../commands.h" #include "../utils/textutils.h" #include "../utils/schematicrectconstants.h" #include "partlabel.h" #include "partfactory.h" #include #include #include #include static const int MinPins = 2; static const int MaxPins = 20; static QHash Spacings; static HoleClassThing TheHoleThing; ScrewTerminal::ScrewTerminal( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { setUpHoleSizes("screwterminal", TheHoleThing); } ScrewTerminal::~ScrewTerminal() { } QStringList ScrewTerminal::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("pins", Qt::CaseInsensitive) == 0) { QStringList values; value = modelPart()->properties().value("pins"); for (int i = MinPins; i <= MaxPins; i++) { values << QString::number(i); } return values; } if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) { QStringList values; value = modelPart()->properties().value("pin spacing"); initSpacings(); return Spacings.values(); } return PaletteItem::collectValues(family, prop, value); } ItemBase::PluralType ScrewTerminal::isPlural() { return Plural; } QString ScrewTerminal::genFZP(const QString & moduleid) { initSpacings(); QString useModuleID = moduleid; int hsix = useModuleID.lastIndexOf(HoleSizePrefix); if (hsix >= 0) useModuleID.truncate(hsix); QStringList pieces = useModuleID.split("_"); QString result = PaletteItem::genFZP(useModuleID, "screw_terminal_fzpTemplate", MinPins, MaxPins, 1, false); result.replace(".percent.", "%"); QString spacing = pieces.at(3); result = result.arg(spacing).arg(Spacings.value(spacing, "")); if (hsix >= 0) { return hackFzpHoleSize(result, moduleid, hsix); } return result; } QString ScrewTerminal::genModuleID(QMap & currPropsMap) { initSpacings(); QString spacing = currPropsMap.value("pin spacing"); QString pins = currPropsMap.value("pins"); foreach (QString key, Spacings.keys()) { if (Spacings.value(key).compare(spacing, Qt::CaseInsensitive) == 0) { return QString("screw_terminal_%1_%2").arg(pins).arg(key); } } return ""; } void ScrewTerminal::initSpacings() { if (Spacings.count() == 0) { Spacings.insert("3.5mm", "0.137in (3.5mm)"); Spacings.insert("5.0mm", "0.197in (5.0mm)"); Spacings.insert("100mil", "0.1in (2.54mm)"); Spacings.insert("200mil", "0.2in (5.08mm)"); Spacings.insert("300mil", "0.3in (7.62mm)"); } } QString ScrewTerminal::makeBreadboardSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); int pins = pieces.at(2).toInt(); double increment = 0.1; // inches double incrementPoints = 100; QString header("\n" "\n" "\n" "\n" "\n" "\n"); QString svg = TextUtils::incrementTemplateString(header.arg(increment * pins), 1, incrementPoints * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); svg += TextUtils::incrementTemplate(":/resources/templates/screw_terminal_bread_template.txt", pins, incrementPoints, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); svg += "\n"; return svg; } QString ScrewTerminal::makeSchematicSvg(const QString & expectedFileName) { if (expectedFileName.contains(PartFactory::OldSchematicPrefix)) { return obsoleteMakeSchematicSvg(expectedFileName); } QStringList pieces = expectedFileName.split("_"); int pins = pieces.at(2).toInt(); double increment = SchematicRectConstants::NewUnit / 25.4; double incrementPoints = 72 * increment; // 72 dpi double pinWidthPoints = 72 * SchematicRectConstants::PinWidth / 25.4; double pinLength = 2 * increment; double pinLengthPoints = 2 * incrementPoints; QString header("\n" "\n" "\n"); header = header.arg(increment + pinLength).arg(incrementPoints + pinLengthPoints); header.replace("percent", "%"); QString repeat("\n" "\n" "\n" ); repeat = repeat .arg(SchematicRectConstants::PinColor) .arg(pinWidthPoints) .arg(pinWidthPoints / 2) .arg(pinLengthPoints - pinWidthPoints) .arg(incrementPoints / 2) .arg((incrementPoints - pinWidthPoints) / 2) .arg(pinLengthPoints + (incrementPoints / 2) - pinWidthPoints) .arg((incrementPoints / 2) - pinWidthPoints) ; repeat.replace("percent", "%"); QString svg = TextUtils::incrementTemplateString(header.arg(increment * pins).arg(incrementPoints), 1, incrementPoints * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); svg += TextUtils::incrementTemplateString(repeat, pins, incrementPoints, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); svg += "\n"; return svg; } QString ScrewTerminal::obsoleteMakeSchematicSvg(const QString & expectedFileName) { QStringList pieces = expectedFileName.split("_"); int pins = pieces.at(2).toInt(); double increment = GraphicsUtils::StandardSchematicSeparationMils / 1000; double incrementPoints = increment * 72; // 72 dpi QString header("\n" "\n" "\n"); QString repeat("\n" "\n" "\n" "\n"); QString svg = TextUtils::incrementTemplateString(header.arg(increment * pins).arg(incrementPoints), 1, incrementPoints * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); svg += TextUtils::incrementTemplateString(repeat, pins, incrementPoints, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL); svg += "\n"; return svg; } QString ScrewTerminal::makePcbSvg(const QString & originalExpectedFileName) { QString expectedFileName = originalExpectedFileName; int hsix = expectedFileName.indexOf(HoleSizePrefix); if (hsix >= 0) { expectedFileName.truncate(hsix); } QStringList pieces = expectedFileName.split("_"); int pins = pieces.at(2).toInt(); bool ok; double spacing = TextUtils::convertToInches(pieces.at(3), &ok, false); if (!ok) return ""; double dpi = 1000; double width = spacing < 0.15 ? 0.256 : 0.48; double verticalX = spacing < 0.15 ? 0.196 : 0.38; double centerX = (verticalX * dpi / 2) + 10; double initialY = (spacing * dpi / 2) + 25; QString header("\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n"); QString repeat("\n"); header = header.arg(width) .arg(width * dpi) .arg(pins * spacing + 0.04) .arg((width - 0.02) * dpi) .arg(verticalX * dpi) .arg(centerX - 30) .arg(initialY - 30); QString svg = TextUtils::incrementTemplateString(header, 1, pins * dpi * spacing, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL); for (int i = 0; i < pins; i++) { svg += repeat.arg(centerX).arg(initialY + (i * dpi * spacing)).arg(i); } svg += "\n\n\n"; if (hsix >= 0) { return hackSvgHoleSizeAux(svg, originalExpectedFileName); } return svg; } bool ScrewTerminal::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("hole size", Qt::CaseInsensitive) == 0) { return collectHoleSizeInfo(TheHoleThing.holeSizeValue, parent, swappingEnabled, returnProp, returnValue, returnWidget); } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void ScrewTerminal::swapEntry(const QString & text) { generateSwap(text, genModuleID, genFZP, makeBreadboardSvg, makeSchematicSvg, makePcbSvg); PaletteItem::swapEntry(text); } fritzing-app-0.9.3b/src/items/screwterminal.h000066400000000000000000000043201272363601300212410ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef SCREWTERMINAL_H #define SCREWTERMINAL_H #include "paletteitem.h" class ScrewTerminal : public PaletteItem { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call ScrewTerminal(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~ScrewTerminal(); PluralType isPlural(); QStringList collectValues(const QString & family, const QString & prop, QString & value); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); public slots: void swapEntry(const QString & text); public: static QString genFZP(const QString & moduleid); static QString genModuleID(QMap & currPropsMap); static QString makeBreadboardSvg(const QString & expectedFileName); static QString makeSchematicSvg(const QString & expectedFileName); static QString obsoleteMakeSchematicSvg(const QString & expectedFileName); static QString makePcbSvg(const QString & expectedFileName); protected: static void initSpacings(); }; #endif fritzing-app-0.9.3b/src/items/stripboard.cpp000066400000000000000000000612251272363601300210750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "stripboard.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/familypropertycombobox.h" #include "../svg/gerbergenerator.h" #include "../fsvgrenderer.h" #include "../sketch/infographicsview.h" #include "moduleidnames.h" #include "../connectors/connectoritem.h" #include "../connectors/busshared.h" #include "../connectors/connectorshared.h" #include "../debugdialog.h" #include #include ////////////////////////////////////////////////// // TODO: // // new cursors, new hover states? // disconnect and reconnect affected parts // swapping static QCursor * SpotFaceCutterCursor = NULL; static QCursor * MagicWandCursor = NULL; static bool ShiftDown = false; static QPointF OriginalShiftPos; static bool ShiftX = false; static bool ShiftY = false; static bool SpaceBarWasPressed = false; static const double MinMouseMove = 2; static QPainterPath HPath; static QPainterPath VPath; static QString HorizontalString("horizontal strips"); static QString VerticalString("vertical strips"); ///////////////////////////////////////////////////////////////////// Stripbit::Stripbit(const QPainterPath & path, int x, int y, bool horizontal, QGraphicsItem * parent = 0) : QGraphicsPathItem(path, parent) { if (SpotFaceCutterCursor == NULL) { QBitmap bitmap(":resources/images/cursor/spot_face_cutter.bmp"); QBitmap bitmapm(":resources/images/cursor/spot_face_cutter_mask.bmp"); SpotFaceCutterCursor = new QCursor(bitmap, bitmapm, 0, 0); } if (MagicWandCursor == NULL) { QBitmap bitmap(":resources/images/cursor/magic_wand.bmp"); QBitmap bitmapm(":resources/images/cursor/magic_wand_mask.bmp"); MagicWandCursor = new QCursor(bitmap, bitmapm, 0, 0); } setZValue(-999); // beneath connectorItems setPen(Qt::NoPen); // TODO: don't hardcode this color setBrush(QColor(0xbc, 0x94, 0x51)); // QColor(0xc4, 0x9c, 0x59) m_horizontal = horizontal; m_x = x; m_y = y; m_inHover = m_removed = false; setAcceptHoverEvents(true); setAcceptedMouseButtons(Qt::LeftButton); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, false); } Stripbit::~Stripbit() { } void Stripbit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { double newOpacity = 1; if (m_removed) { if (m_inHover) newOpacity = 0.50; else newOpacity = 0.00; } else { if (m_inHover) newOpacity = 0.40; else newOpacity = 1.00; } double opacity = painter->opacity(); painter->setOpacity(newOpacity); QGraphicsPathItem::paint(painter, option, widget); painter->setOpacity(opacity); } void Stripbit::mousePressEvent(QGraphicsSceneMouseEvent *event) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) { event->ignore(); return; } if (!event->buttons() && Qt::LeftButton) { event->ignore(); return; } if (dynamic_cast(this->parentItem())->moveLock()) { event->ignore(); return; } if (event->modifiers() & Qt::ShiftModifier) { ShiftDown = true; ShiftX = ShiftY = false; OriginalShiftPos = event->scenePos(); } event->accept(); dynamic_cast(this->parentItem())->initCutting(this); m_removed = !m_removed; m_inHover = false; m_changed = true; update(); //DebugDialog::debug("got press"); } void Stripbit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); dynamic_cast(this->parentItem())->reinitBuses(true); } void Stripbit::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (!event->buttons() && Qt::LeftButton) return; if (ShiftDown && !(event->modifiers() & Qt::ShiftModifier)) { ShiftDown = false; } //DebugDialog::debug("got move"); Stripbit * other = NULL; QPointF p = event->scenePos(); if (ShiftDown) { if (ShiftX) { // moving along x, constrain y p.setY(OriginalShiftPos.y()); } else if (ShiftY) { // moving along y, constrain x p.setX(OriginalShiftPos.x()); } else { double dx = qAbs(p.x() - OriginalShiftPos.x()); double dy = qAbs(p.y() - OriginalShiftPos.y()); if (dx - dy > MinMouseMove) { ShiftX = true; } else if (dy - dx > MinMouseMove) { ShiftY = true; } } } if (!ShiftDown && (event->modifiers() & Qt::ShiftModifier)) { ShiftDown = true; ShiftX = ShiftY = false; OriginalShiftPos = event->scenePos(); } foreach (QGraphicsItem * item, scene()->items(p)) { other = dynamic_cast(item); if (other) break; } if (!other) return; //DebugDialog::debug("got other"); if (other->removed() == m_removed) return; //DebugDialog::debug("change other"); other->setRemoved(m_removed); other->setChanged(true); other->update(); dynamic_cast(this->parentItem())->restoreRowColors(other); } void Stripbit::hoverEnterEvent( QGraphicsSceneHoverEvent * event ) { if (dynamic_cast(this->parentItem())->moveLock()) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) { SpaceBarWasPressed = true; return; } SpaceBarWasPressed = false; setCursor(m_removed ? *MagicWandCursor : *SpotFaceCutterCursor); Q_UNUSED(event); m_inHover = true; update(); } void Stripbit::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) { if (dynamic_cast(this->parentItem())->moveLock()) return; if (SpaceBarWasPressed) return; unsetCursor(); Q_UNUSED(event); m_inHover = false; update(); } bool Stripbit::horizontal() { return m_horizontal; } void Stripbit::setRemoved(bool removed) { m_removed = removed; } bool Stripbit::removed() { return m_removed; } void Stripbit::setChanged(bool changed) { m_changed = changed; } bool Stripbit::changed() { return m_changed; } int Stripbit::y() { return m_y; } int Stripbit::x() { return m_x; } QString Stripbit::makeRemovedString() { return QString("%1.%2%3 ").arg(m_x).arg(m_y).arg(m_horizontal ? 'h' : 'v'); } ///////////////////////////////////////////////////////////////////// StripConnector::StripConnector() { down = right = NULL; connectorItem = NULL; } struct StripLayout { QString name; int rows; int columns; QString buses; StripLayout(QString name, int rows, int columns, QString buses); }; StripLayout::StripLayout(QString name_, int rows_, int columns_, QString buses_) { name = name_; rows = rows_; columns = columns_; buses = buses_; } static QList StripLayouts; ///////////////////////////////////////////////////////////////////// Stripboard::Stripboard( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Perfboard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { getXY(m_x, m_y, m_size); if (StripLayouts.count() == 0) { initStripLayouts(); } m_layout = modelPart->localProp("layout").toString(); if (m_layout.isEmpty()) { m_layout = modelPart->properties().value("m_layout"); if (!m_layout.isEmpty()) { modelPart->setLocalProp("layout", m_layout); } } } Stripboard::~Stripboard() { foreach (StripConnector * sc, m_strips) { delete sc; } m_strips.clear(); } QString Stripboard::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { QString svg = Perfboard::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); if (svg.isEmpty()) return svg; if (!svg.contains(GerberGenerator::MagicBoardOutlineID)) return svg; /* QFile file(filename()); if (!file.open(QFile::ReadOnly | QFile::Text)) { return svg; } QString originalSvg = file.readAll(); file.close(); QString stripSvg = originalSvg.left(svg.indexOf(">") + 1); */ QString stripSvg; foreach(QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; if (stripbit->removed()) continue; QPointF p = stripbit->pos(); QRectF r = stripbit->boundingRect(); stripSvg += QString("\n") .arg(p.x() * dpi / GraphicsUtils::SVGDPI) .arg(p.y() * dpi / GraphicsUtils::SVGDPI) .arg(r.width() * dpi / GraphicsUtils::SVGDPI ) .arg(r.height() * dpi / GraphicsUtils::SVGDPI) .arg(r.height() * dpi * .5 / GraphicsUtils::SVGDPI) .arg(blackOnly ? "black" : "#c49c59") ; } svg.truncate(svg.lastIndexOf("")); return svg + stripSvg + "\n"; } QString Stripboard::genFZP(const QString & moduleid) { QString fzp = Perfboard::genFZP(moduleid); fzp.replace(ModuleIDNames::PerfboardModuleIDName, ModuleIDNames::Stripboard2ModuleIDName); fzp.replace("Perfboard", "Stripboard"); fzp.replace("perfboard", "stripboard"); fzp.replace("stripboard.svg", "perfboard.svg"); // replaced just above; restore it QString findString(""); int ix = fzp.indexOf(findString); if (ix > 0) { fzp.insert(ix + findString.count(), ""); if (moduleid.endsWith(ModuleIDNames::StripboardModuleIDName)) { fzp.insert(ix + findString.count(), "yes"); } } return fzp; } bool Stripboard::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { return Perfboard::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void Stripboard::addedToScene(bool temporary) { Perfboard::addedToScene(temporary); if (this->scene() == NULL) return; if (temporary) return; if (m_viewID != ViewLayer::BreadboardView) return; QList items = childItems(); if (HPath.isEmpty()) { makeInitialPath(); } int count = m_x * m_y; for (int i = 0; i < count; i++) { m_strips.append(new StripConnector); } bool oldStyle = false; if (moduleID().endsWith(ModuleIDNames::StripboardModuleIDName) || !modelPart()->properties().value("oldstyle", "").isEmpty()) { modelPart()->modelPartShared()->setModuleID(QString("%1.%2%3").arg(m_x).arg(m_y).arg(ModuleIDNames::Stripboard2ModuleIDName)); oldStyle = true; m_layout = HorizontalString; } QString config = prop("buses"); QString additionalConfig; foreach (ConnectorItem * ci, cachedConnectorItems()) { int cx, cy; getXY(cx, cy, ci->connectorSharedName()); StripConnector * sc = getStripConnector(cx, cy); sc->connectorItem = ci; if (cx < m_x - 1) { Stripbit * stripbit = new Stripbit(HPath, cx, cy, true, this); QRectF r = ci->rect(); stripbit->setPos(r.center().x(), r.top()); stripbit->setVisible(true); sc->right = stripbit; if (!oldStyle) { additionalConfig += stripbit->makeRemovedString(); } } if (cy < m_y - 1) { Stripbit * stripbit = new Stripbit(VPath, cx, cy, false, this); QRectF r = ci->rect(); stripbit->setPos(r.left(), r.center().y()); stripbit->setVisible(true); sc->down = stripbit; if (oldStyle) { additionalConfig += stripbit->makeRemovedString(); } } } if (config.isEmpty() || oldStyle) { config += additionalConfig; setProp("buses", config); } if (m_layout.isEmpty()) m_layout = VerticalString; QStringList removed = config.split(" ", QString::SkipEmptyParts); foreach (QString name, removed) { int cx, cy; if (getXY(cx, cy, name)) { StripConnector * sc = getStripConnector(cx, cy); bool vertical = name.contains("v"); if (vertical) { if (sc->down != NULL) sc->down->setRemoved(true); } else { if (sc->right != NULL) sc->right->setRemoved(true); } } } reinitBuses(false); } QString Stripboard::genModuleID(QMap & currPropsMap) { QString size = currPropsMap.value("size"); return size + ModuleIDNames::Stripboard2ModuleIDName; } void Stripboard::initCutting(Stripbit *) { m_beforeCut.clear(); foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; stripbit->setChanged(false); if (stripbit->removed()) { m_beforeCut += stripbit->makeRemovedString(); } } } void appendConnectors(QList & connectorItems, ConnectorItem * connectorItem) { if (connectorItem == NULL) return; foreach (ConnectorItem * ci, connectorItem->connectedToItems()) { connectorItems.append(ci); } } void Stripboard::reinitBuses(bool triggerUndo) { if (triggerUndo) { QString afterCut; QSet affectedConnectors; int changeCount = 0; bool connect = true; foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; if (stripbit->removed()) { afterCut += stripbit->makeRemovedString(); } } collectTo(affectedConnectors); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { QString changeType = (connect) ? tr("Restored") : tr("Cut") ; QString changeText = tr("%1 %n strip(s)", "", changeCount).arg(changeType); QList affected = affectedConnectors.toList(); infoGraphicsView->changeBus(this, connect, m_beforeCut, afterCut, affected, changeText, m_layout, m_layout); // affectedConnectors is used for updating ratsnests; it's just a wild guess } return; } if (viewID() != ViewLayer::BreadboardView) return; foreach (BusShared * busShared, m_buses) delete busShared; m_buses.clear(); foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; connectorItem->connector()->connectorShared()->setBus(NULL); connectorItem->connector()->setBus(NULL); continue; } } QString busPropertyString; foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; if (stripbit->removed()) { busPropertyString += stripbit->makeRemovedString(); } } QList visited; for (int iy = 0; iy < m_y; iy++) { for (int ix = 0; ix < m_x; ix++) { StripConnector * sc = getStripConnector(ix, iy); if (visited.contains(sc->connectorItem)) continue; QList connected; collectConnected(ix, iy, connected); visited.append(connected); nextBus(connected); } } modelPart()->clearBuses(); modelPart()->initBuses(); modelPart()->setLocalProp("buses", busPropertyString); QList visited2; foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { connectorItem->restoreColor(visited2); } update(); } void Stripboard::collectConnected(int ix, int iy, QList & connected) { StripConnector * sc = getStripConnector(ix, iy); if (connected.contains(sc->connectorItem)) return; connected << sc->connectorItem; if (sc->right != NULL && !sc->right->removed()) { collectConnected(ix + 1, iy, connected); } if (sc->down != NULL && !sc->down->removed()) { collectConnected(ix, iy + 1, connected); } StripConnector * left = ix > 0 ? getStripConnector(ix - 1, iy) : NULL; if (left != NULL && left->right != NULL && !left->right->removed()) { collectConnected(ix - 1, iy, connected); } StripConnector * up = iy > 0 ? getStripConnector(ix, iy - 1) : NULL; if (up != NULL && up->down != NULL && !up->down->removed()) { collectConnected(ix, iy - 1, connected); } } void Stripboard::nextBus(QList & soFar) { if (soFar.count() > 1) { BusShared * busShared = new BusShared(QString::number(m_buses.count())); m_buses.append(busShared); foreach (ConnectorItem * connectorItem, soFar) { busShared->addConnectorShared(connectorItem->connector()->connectorShared()); } } soFar.clear(); } void Stripboard::setProp(const QString & prop, const QString & value) { if (prop.compare("buses") == 0) { QStringList removed = value.split(" ", QString::SkipEmptyParts); foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; QString removedString = stripbit->makeRemovedString(); removedString.chop(1); // remove trailing space bool remove = removed.contains(removedString); stripbit->setRemoved(remove); if (remove) { removed.removeOne(removedString); } } reinitBuses(false); return; } if (prop.compare("layout") == 0) { m_layout = value; } Perfboard::setProp(prop, value); } void Stripboard::getConnectedColor(ConnectorItem * ci, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { Perfboard::getConnectedColor(ci, brush, pen, opacity, negativePenWidth, negativeOffsetRect); opacity *= .66667; } void Stripboard::restoreRowColors(Stripbit * stripbit) { // TODO: find a quick way to update the buses (and connectorItem colors) in just the row... Q_UNUSED(stripbit); //int y = stripbit->y(); //stripbit = m_firstColumn.at(y); //ci->restoreColor(false, 0, false); } QString Stripboard::getRowLabel() { return tr("rows"); } QString Stripboard::getColumnLabel() { return tr("columns"); } void Stripboard::makeInitialPath() { ConnectorItem * ciFirst = NULL; ConnectorItem * ciNextH = NULL; ConnectorItem * ciNextV = NULL; foreach (ConnectorItem * ci, cachedConnectorItems()) { int cx, cy; getXY(cx, cy, ci->connectorSharedName()); if (cy == 0 && cx == 0) { ciFirst = ci; break; } } foreach (ConnectorItem * ci, cachedConnectorItems()) { int cx, cy; getXY(cx, cy, ci->connectorSharedName()); if (cy == 0 && cx == 1) { ciNextH = ci; if (ciNextV) break; } else if (cy == 1 && cx == 0) { ciNextV = ci; if (ciNextH) break; } } if (ciFirst == NULL) return; if (ciNextH == NULL) return; if (ciNextV == NULL) return; QRectF r1 = ciFirst->rect(); QRectF rh = ciNextH->rect(); double h = r1.height(); double w = rh.center().x() - r1.center().x(); r1.moveTo(-(r1.width() / 2), 0); rh.moveTo(w - (rh.width() / 2), 0); HPath.addRect(0, 0, w / 2, h); HPath.arcTo(r1, 90, -180); HPath.addRect(w / 2, 0, w / 2, h); HPath.moveTo(w, 0); HPath.arcTo(rh, 90, 180); r1 = ciFirst->rect(); QRectF rv = ciNextV->rect(); h = rv.center().y() - r1.center().y(); w = r1.width(); r1.moveTo(0, -(r1.height() / 2)); rv.moveTo(0, h - (rv.height() / 2)); VPath.addRect(0, 0, w, h / 2); VPath.arcTo(r1, 0, -180); VPath.addRect(0, h / 2, w, h / 2); VPath.moveTo(0, h); VPath.arcTo(rv, 0, 180); } StripConnector * Stripboard::getStripConnector(int ix, int iy) { return m_strips.at((iy * m_x) + ix); } void Stripboard::swapEntry(const QString & text) { FamilyPropertyComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; if (comboBox->prop().compare("layout", Qt::CaseInsensitive) == 0) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; QString afterCut; if (text.compare(HorizontalString, Qt::CaseInsensitive) == 0) { foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; if (!stripbit->horizontal()) { afterCut += stripbit->makeRemovedString(); } } } else if (text.compare(VerticalString, Qt::CaseInsensitive) == 0) { foreach (QGraphicsItem * item, childItems()) { Stripbit * stripbit = dynamic_cast(item); if (stripbit == NULL) continue; if (stripbit->horizontal()) { afterCut += stripbit->makeRemovedString(); } } } else { for (int i = 0; i < StripLayouts.count(); i++) { if (StripLayouts.at(i).name.compare(text) == 0) { StripLayout stripLayout = StripLayouts.at(i); QMap propsMap; propsMap.insert("size", QString("%1.%2").arg(stripLayout.columns).arg(stripLayout.rows)); propsMap.insert("type", "Stripboard"); propsMap.insert("buses", stripLayout.buses); propsMap.insert("layout", text); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->swap(family(), "size", propsMap, this); } return; } } } if (!afterCut.isEmpty()) { initCutting(NULL); QString changeText = tr("%1 layout").arg(text); QSet affectedConnectors; collectTo(affectedConnectors); QList affected = affectedConnectors.toList(); infoGraphicsView->changeBus(this, true, m_beforeCut, afterCut, affected, changeText, m_layout, text); // affectedConnectors is used for updating ratsnests; it's just a wild guess } return; } Perfboard::swapEntry(text); } void Stripboard::collectTo(QSet & affectedConnectors) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { affectedConnectors.insert(toConnectorItem); } } } void Stripboard::initStripLayouts() { QFile file(":/resources/templates/stripboards.xml"); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse stripboards.xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement root = domDocument.documentElement(); QDomElement stripboard = root.firstChildElement("stripboard"); while (!stripboard.isNull()) { QString name = stripboard.attribute("name"); bool rok; int rows = stripboard.attribute("rows").toInt(&rok); bool cok; int columns = stripboard.attribute("columns").toInt(&cok); QString buses = stripboard.attribute("buses"); stripboard = stripboard.nextSiblingElement("stripboard"); if (!rok) continue; if (!cok) continue; if (name.isEmpty() || buses.isEmpty()) continue; StripLayout stripLayout(name, rows, columns, buses); StripLayouts.append(stripLayout); } } QStringList Stripboard::collectValues(const QString & family, const QString & prop, QString & value) { QStringList values = Perfboard::collectValues(family, prop, value); if (prop.compare("layout", Qt::CaseInsensitive) == 0) { foreach (StripLayout stripLayout, StripLayouts) { values.append(stripLayout.name); value = m_layout; } } return values; } fritzing-app-0.9.3b/src/items/stripboard.h000066400000000000000000000074471272363601300205500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef STRIPBOARD_H #define STRIPBOARD_H #include #include #include #include "perfboard.h" class ConnectorItem; class Stripbit : public QGraphicsPathItem { public: Stripbit(const QPainterPath & path, int x, int y, bool horizontal, QGraphicsItem * parent); ~Stripbit(); bool horizontal(); void setRemoved(bool); bool removed(); void setChanged(bool); bool changed(); int y(); int x(); QString makeRemovedString(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent( QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent( QGraphicsSceneHoverEvent * event ); protected: bool m_removed; bool m_inHover; int m_x; int m_y; bool m_changed; bool m_horizontal; }; struct StripConnector { ConnectorItem * connectorItem; Stripbit * down; Stripbit * right; StripConnector(); }; class Stripboard : public Perfboard { Q_OBJECT public: // after calling this constructor if you want to render the loaded svg (either from model or from file), MUST call Stripboard(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Stripboard(); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); void addedToScene(bool temporary); void setProp(const QString & prop, const QString & value); void reinitBuses(bool triggerUndo); void initCutting(Stripbit *); void getConnectedColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); void restoreRowColors(Stripbit * stripbit); void swapEntry(const QString & text); QStringList collectValues(const QString & family, const QString & prop, QString & value); protected: void nextBus(QList & soFar); QString getRowLabel(); QString getColumnLabel(); void makeInitialPath(); void collectConnected(int x, int y, QList & connected); StripConnector * getStripConnector(int x, int y); void collectTo(QSet &); void initStripLayouts(); public: static QString genFZP(const QString & moduleID); static QString genModuleID(QMap & currPropsMap); protected: QList m_strips; QList m_buses; QString m_beforeCut; int m_x; int m_y; QString m_layout; }; #endif fritzing-app-0.9.3b/src/items/symbolpaletteitem.cpp000066400000000000000000000515471272363601300224750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "symbolpaletteitem.h" #include "../debugdialog.h" #include "../connectors/connectoritem.h" #include "../connectors/bus.h" #include "moduleidnames.h" #include "../fsvgrenderer.h" #include "../utils/textutils.h" #include "../utils/focusoutcombobox.h" #include "../utils/graphicsutils.h" #include "../sketch/infographicsview.h" #include "partlabel.h" #include "partfactory.h" #include "layerkinpaletteitem.h" #include "../svg/svgfilesplitter.h" #include #include #include #define VOLTAGE_HASH_CONVERSION 1000000 #define FROMVOLTAGE(v) ((long) (v * VOLTAGE_HASH_CONVERSION)) static QMultiHash > LocalVoltages; // Qt doesn't do Hash keys with double static QMultiHash > LocalNetLabels; static QList< QPointer > LocalGrounds; static QList Voltages; double SymbolPaletteItem::DefaultVoltage = 5; ///////////////////////////////////////////////////// /* FocusBugLineEdit::FocusBugLineEdit(QWidget * parent) : QLineEdit(parent) { connect(this, SIGNAL(editingFinished()), this, SLOT(editingFinishedSlot())); m_lastEditingFinishedEmit = QTime::currentTime(); } FocusBugLineEdit::~FocusBugLineEdit() { } void FocusBugLineEdit::editingFinishedSlot() { QTime now = QTime::currentTime(); int d = m_lastEditingFinishedEmit.msecsTo(now); DebugDialog::debug(QString("dtime %1").arg(d)); if (d < 1000) { return; } m_lastEditingFinishedEmit = now; emit safeEditingFinished(); } */ //////////////////////////////////////// SymbolPaletteItem::SymbolPaletteItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { if (Voltages.count() == 0) { Voltages.append(0.0); Voltages.append(3.3); Voltages.append(5.0); Voltages.append(12.0); } m_connector0 = m_connector1 = NULL; m_voltage = 0; m_voltageReference = (modelPart->properties().value("type").compare("voltage reference") == 0); if (modelPart->moduleID().endsWith(ModuleIDNames::NetLabelModuleIDName)) { m_isNetLabel = true; } else { m_isNetLabel = modelPart->moduleID().endsWith(ModuleIDNames::PowerLabelModuleIDName); bool ok; double temp = modelPart->localProp("voltage").toDouble(&ok); if (ok) { m_voltage = temp; } else { temp = modelPart->properties().value("voltage").toDouble(&ok); if (ok) { m_voltage = SymbolPaletteItem::DefaultVoltage; } modelPart->setLocalProp("voltage", m_voltage); } if (!Voltages.contains(m_voltage)) { Voltages.append(m_voltage); } } } SymbolPaletteItem::~SymbolPaletteItem() { if (m_isNetLabel) { foreach (QString key, LocalNetLabels.uniqueKeys()) { if (m_connector0) { LocalNetLabels.remove(key, m_connector0); } if (m_connector1) { LocalNetLabels.remove(key, m_connector1); } LocalNetLabels.remove(key, NULL); // cleans null QPointers } } else { if (m_connector0) LocalGrounds.removeOne(m_connector0); if (m_connector1) LocalGrounds.removeOne(m_connector1); LocalGrounds.removeOne(NULL); // cleans null QPointers foreach (long key, LocalVoltages.uniqueKeys()) { if (m_connector0) { LocalVoltages.remove(key, m_connector0); } if (m_connector1) { LocalVoltages.remove(key, m_connector1); } LocalVoltages.remove(key, NULL); // cleans null QPointers } } } void SymbolPaletteItem::removeMeFromBus(double v) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (m_isNetLabel) { LocalNetLabels.remove(getLabel(), connectorItem); } else { double nv = useVoltage(connectorItem); if (nv == v) { //connectorItem->debugInfo(QString("remove %1").arg(useVoltage(connectorItem))); bool gotOne = LocalGrounds.removeOne(connectorItem); int count = LocalVoltages.remove(FROMVOLTAGE(v), connectorItem); LocalVoltages.remove(FROMVOLTAGE(v), NULL); if (count == 0 && !gotOne) { DebugDialog::debug(QString("removeMeFromBus failed %1 %2 %3 %4") .arg(this->id()) .arg(connectorItem->connectorSharedID()) .arg(v).arg(nv)); } } } } LocalGrounds.removeOne(NULL); // keep cleaning these out } ConnectorItem* SymbolPaletteItem::newConnectorItem(Connector *connector) { ConnectorItem * connectorItem = PaletteItemBase::newConnectorItem(connector); if (connector->connectorSharedID().compare("connector0") == 0) { m_connector0 = connectorItem; } else if (connector->connectorSharedID().compare("connector1") == 0) { m_connector1 = connectorItem; } else { return connectorItem; } if (m_isNetLabel) { LocalNetLabels.insert(getLabel(), connectorItem); } else if (connectorItem->isGrounded()) { LocalGrounds.append(connectorItem); //connectorItem->debugInfo("new ground insert"); } else { LocalVoltages.insert(FROMVOLTAGE(useVoltage(connectorItem)), connectorItem); //connectorItem->debugInfo(QString("new voltage insert %1").arg(useVoltage(connectorItem))); } return connectorItem; } void SymbolPaletteItem::busConnectorItems(Bus * bus, ConnectorItem * fromConnectorItem, QList & items) { if (bus == NULL) return; PaletteItem::busConnectorItems(bus, fromConnectorItem, items); //foreach (ConnectorItem * bc, items) { //bc->debugInfo(QString("bc %1").arg(bus->id())); //} QList< QPointer > mitems; if (m_isNetLabel) { mitems.append(LocalNetLabels.values(getLabel())); } else if (bus->id().compare("groundbus", Qt::CaseInsensitive) == 0) { mitems.append(LocalGrounds); } else { mitems.append(LocalVoltages.values(FROMVOLTAGE(m_voltage))); } foreach (ConnectorItem * connectorItem, mitems) { if (connectorItem == NULL) continue; if (connectorItem->scene() == this->scene()) { items.append(connectorItem); //connectorItem->debugInfo(QString("symbol bus %1").arg(bus->id())); } } } double SymbolPaletteItem::voltage() { return m_voltage; } void SymbolPaletteItem::setProp(const QString & prop, const QString & value) { if (prop.compare("voltage", Qt::CaseInsensitive) == 0) { setVoltage(value.toDouble()); return; } if (prop.compare("label", Qt::CaseInsensitive) == 0 && m_isNetLabel) { setLabel(value); return; } PaletteItem::setProp(prop, value); } void SymbolPaletteItem::setLabel(const QString & label) { removeMeFromBus(0); m_modelPart->setLocalProp("label", label); foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { LocalNetLabels.insert(label, connectorItem); } QTransform transform = untransform(); QString svg = makeSvg(this->viewLayerID()); resetRenderer(svg); resetLayerKin(); resetConnectors(NULL, NULL); retransform(transform); } void SymbolPaletteItem::setVoltage(double v) { removeMeFromBus(m_voltage); m_voltage = v; m_modelPart->setLocalProp("voltage", v); if (!Voltages.contains(v)) { Voltages.append(v); } if (m_isNetLabel) { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { LocalNetLabels.insert(QString::number(m_voltage), connectorItem); } } else { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->isGrounded()) { LocalGrounds.append(connectorItem); //connectorItem->debugInfo("ground insert"); } else { LocalVoltages.insert(FROMVOLTAGE(v), connectorItem); //connectorItem->debugInfo(QString("voltage insert %1").arg(useVoltage(connectorItem))); } } } if (m_viewID == ViewLayer::SchematicView) { if (m_voltageReference || m_isNetLabel) { QTransform transform = untransform(); QString svg = makeSvg(viewLayerID()); reloadRenderer(svg, false); resetLayerKin(); retransform(transform); if (m_partLabel) m_partLabel->displayTextsIf(); } } } QString SymbolPaletteItem::makeSvg(ViewLayer::ViewLayerID viewLayerID) { QString path = filename(); QFile file(filename()); QString svg; if (file.open(QFile::ReadOnly)) { svg = file.readAll(); file.close(); svg = replaceTextElement(svg); if (viewLayerID == ViewLayer::SchematicText) { bool hasText; return SvgFileSplitter::showText3(svg, hasText); } else { return SvgFileSplitter::hideText3(svg); } } return ""; } QString SymbolPaletteItem::replaceTextElement(QString svg) { double v = ((int) (m_voltage * 1000)) / 1000.0; return TextUtils::replaceTextElement(svg, "label", QString::number(v) + "V"); } QString SymbolPaletteItem::getProperty(const QString & key) { if (key.compare("voltage", Qt::CaseInsensitive) == 0) { return QString::number(m_voltage); } return PaletteItem::getProperty(key); } double SymbolPaletteItem::useVoltage(ConnectorItem * connectorItem) { return (connectorItem->connectorSharedName().compare("GND", Qt::CaseInsensitive) == 0) ? 0 : m_voltage; } ConnectorItem * SymbolPaletteItem::connector0() { return m_connector0; } ConnectorItem * SymbolPaletteItem::connector1() { return m_connector1; } void SymbolPaletteItem::addedToScene(bool temporary) { if (this->scene()) { setVoltage(m_voltage); } return PaletteItem::addedToScene(temporary); } QString SymbolPaletteItem::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { QString svg = PaletteItem::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor); if (m_voltageReference) { switch (viewLayerID) { case ViewLayer::Schematic: return replaceTextElement(svg); default: break; } } return svg; } bool SymbolPaletteItem::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if ((prop.compare("voltage", Qt::CaseInsensitive) == 0) && (moduleID().compare(ModuleIDNames::GroundModuleIDName) != 0)) { FocusOutComboBox * edit = new FocusOutComboBox(parent); edit->setEnabled(swappingEnabled); int ix = 0; foreach (double v, Voltages) { edit->addItem(QString::number(v)); if (v == m_voltage) { edit->setCurrentIndex(ix); } ix++; } QDoubleValidator * validator = new QDoubleValidator(edit); validator->setRange(-9999.99, 9999.99, 2); validator->setLocale(QLocale::C); validator->setNotation(QDoubleValidator::StandardNotation); validator->setLocale(QLocale::C); edit->setValidator(validator); edit->setObjectName("infoViewComboBox"); connect(edit, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(voltageEntry(const QString &))); returnWidget = edit; returnValue = m_voltage; returnProp = tr("voltage"); return true; } if (prop.compare("label", Qt::CaseInsensitive) == 0 && m_isNetLabel) { QLineEdit * edit = new QLineEdit(parent); edit->setEnabled(swappingEnabled); edit->setText(getLabel()); edit->setObjectName("infoViewLineEdit"); connect(edit, SIGNAL(editingFinished()), this, SLOT(labelEntry())); returnWidget = edit; returnValue = getLabel(); returnProp = tr("label"); return true; } return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void SymbolPaletteItem::voltageEntry(const QString & text) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setVoltage(text.toDouble(), true); } } void SymbolPaletteItem::labelEntry() { QLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; QString current = getLabel(); if (edit->text().compare(current) == 0) return; if (edit->text().isEmpty()) { QMessageBox::warning(NULL, tr("Net labels"), tr("Net labels cannot be blank")); return; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "label", ItemBase::TranslatedPropertyNames.value("label"), current, edit->text(), true); } } ItemBase::PluralType SymbolPaletteItem::isPlural() { return Singular; } bool SymbolPaletteItem::hasPartNumberProperty() { return false; } ViewLayer::ViewID SymbolPaletteItem::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::SchematicView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } bool SymbolPaletteItem::hasPartLabel() { return !m_isNetLabel; } bool SymbolPaletteItem::isOnlyNetLabel() { return false; } QString SymbolPaletteItem::getLabel() { if (m_voltageReference) { return QString::number(m_voltage); } return modelPart()->localProp("label").toString(); } QString SymbolPaletteItem::getDirection() { return modelPart()->localProp("direction").toString(); } void SymbolPaletteItem::setAutoroutable(bool ar) { m_viewGeometry.setAutoroutable(ar); } bool SymbolPaletteItem::getAutoroutable() { return m_viewGeometry.getAutoroutable(); } void SymbolPaletteItem::resetLayerKin() { foreach (ItemBase * lkpi, layerKin()) { if (lkpi->viewLayerID() == ViewLayer::SchematicText) { QString svg = makeSvg(lkpi->viewLayerID()); lkpi->resetRenderer(svg); lkpi->setProperty("textSvg", svg); qobject_cast(lkpi)->clearTextThings(); break; } } } /////////////////////////////////////////////////////////////// NetLabel::NetLabel( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : SymbolPaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { QString label = getLabel(); if (label.isEmpty()) { label = modelPart->properties().value("label"); if (label.isEmpty()) { label = tr("net label"); } modelPart->setLocalProp("label", label); } setInstanceTitle(label, true); // direction is now obsolete, new netlabels use flip QString direction = getDirection(); if (direction.isEmpty()) { direction = modelPart->properties().value("direction"); if (direction.isEmpty()) { direction = modelPart->moduleID().contains("left", Qt::CaseInsensitive) ? "left" : "right"; } modelPart->setLocalProp("direction", direction); } } NetLabel::~NetLabel() { } QString NetLabel::makeSvg(ViewLayer::ViewLayerID viewLayerID) { DebugDialog::debug("moduleid " + this->moduleID()); double divisor = moduleID().contains(PartFactory::OldSchematicPrefix) ? 1 : 3; double labelFontSize = 200 /divisor; double totalHeight = 300 / divisor; double arrowWidth = totalHeight / 2; double strokeWidth = 10 / divisor; double halfStrokeWidth = strokeWidth / 2; double labelOffset = 20 / divisor; double labelBaseLine = 220 / divisor; QFont font("Droid Sans", labelFontSize * 72 / GraphicsUtils::StandardFritzingDPI, QFont::Normal); QFontMetricsF fm(font); double textWidth = fm.width(getLabel()) * GraphicsUtils::StandardFritzingDPI / 72; double totalWidth = textWidth + arrowWidth + labelOffset; QString header("\n" "\n" "\n" ); bool goLeft = (getDirection() == "left"); // direction is now obsolete; this is left over from 0.7.12 and earlier double offset = goLeft ? arrowWidth : 0; QString svg = header.arg(totalWidth / 1000) .arg(totalHeight / 1000) .arg(totalWidth) .arg(totalHeight) .arg(ViewLayer::viewLayerXmlNameFromID(viewLayerID)) ; if (viewLayerID == ViewLayer::SchematicText) { svg += QString("%4\n") .arg(labelOffset + offset) .arg(labelBaseLine) .arg(labelFontSize) .arg(getLabel()); } else { QString pin = QString("\n"); QString terminal = QString("\n"); QString points = QString("%1,%2 %3,%4 %5,%4 %5,%6 %3,%6"); if (goLeft) { points = points.arg(halfStrokeWidth).arg(totalHeight / 2) .arg(arrowWidth).arg(halfStrokeWidth) .arg(totalWidth - halfStrokeWidth).arg(totalHeight - halfStrokeWidth); terminal = terminal.arg(0).arg(totalHeight / 2); pin = pin.arg(0).arg(0).arg(arrowWidth).arg(totalHeight); } else { points = points.arg(totalWidth - halfStrokeWidth).arg(totalHeight / 2) .arg(totalWidth - arrowWidth).arg(halfStrokeWidth) .arg(halfStrokeWidth).arg(totalHeight - halfStrokeWidth); terminal = terminal.arg(totalWidth).arg(totalHeight / 2); pin = pin.arg(totalWidth - arrowWidth - 0.1).arg(0).arg(arrowWidth).arg(totalHeight); } svg += pin; svg += terminal; svg += QString("\n") .arg(strokeWidth) .arg(points); } svg += "\n\n"; return svg; } void NetLabel::addedToScene(bool temporary) { if (this->scene() && m_viewID == ViewLayer::SchematicView) { // do not understand why plan setLabel() doesn't work the same as the Mystery Part setChipLabel() in addedToScene() if (!this->transform().isIdentity()) { // need to establish correct bounding rect here or setLabel will screw up alignment // this seems to solve half of the cases QString svg = makeSvg(this->viewLayerID()); resetRenderer(svg); resetLayerKin(); QRectF r = boundingRect(); //debugInfo(QString("added to scene %1,%2").arg(r.width()).arg(r.height())); QTransform chiefTransform = this->transform(); QTransform local; local.setMatrix(chiefTransform.m11(), chiefTransform.m12(), chiefTransform.m13(), chiefTransform.m21(), chiefTransform.m22(), chiefTransform.m23(), 0, 0, chiefTransform.m33()); if (!local.isIdentity()) { double x = r.width() / 2.0; double y = r.height() / 2.0; QTransform transf = QTransform().translate(-x, -y) * local * QTransform().translate(x, y); if (qAbs(chiefTransform.dx() - transf.dx()) > .01 || qAbs(chiefTransform.dy() - transf.dy()) > .01) { DebugDialog::debug("got the translation bug here"); this->getViewGeometry().setTransform(transf); this->setTransform2(transf); } } } setLabel(getLabel()); } // deliberately skip SymbolPaletteItem::addedToScene() return PaletteItem::addedToScene(temporary); } QString NetLabel::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor) { Q_UNUSED(svgHash); QString svg = makeSvg(viewLayerID); return PaletteItemBase::normalizeSvg(svg, viewLayerID, blackOnly, dpi, factor); } ItemBase::PluralType NetLabel::isPlural() { return Plural; } bool NetLabel::isOnlyNetLabel() { return true; } QString NetLabel::getInspectorTitle() { return getLabel(); } void NetLabel::setInspectorTitle(const QString & oldText, const QString & newText) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; infoGraphicsView->setProp(this, "label", ItemBase::TranslatedPropertyNames.value("label"), oldText, newText, true); } fritzing-app-0.9.3b/src/items/symbolpaletteitem.h000066400000000000000000000072701272363601300221340ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef SYMBOLPALETTEITEM_H #define SYMBOLPALETTEITEM_H #include "paletteitem.h" /* #include class FocusBugLineEdit : public QLineEdit { Q_OBJECT public: FocusBugLineEdit(QWidget * parent = NULL); ~FocusBugLineEdit(); signals: void safeEditingFinished(); protected slots: void editingFinishedSlot(); protected: QTime m_lastEditingFinishedEmit; }; */ class SymbolPaletteItem : public PaletteItem { Q_OBJECT public: SymbolPaletteItem(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~SymbolPaletteItem(); ConnectorItem* newConnectorItem(class Connector *connector); void busConnectorItems(class Bus * bus, ConnectorItem *, QList & items); double voltage(); void setProp(const QString & prop, const QString & value); void setVoltage(double); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); QString getProperty(const QString & key); ConnectorItem * connector0(); ConnectorItem * connector1(); PluralType isPlural(); void addedToScene(bool temporary); bool hasPartNumberProperty(); virtual bool isOnlyNetLabel(); bool hasPartLabel(); bool getAutoroutable(); void setAutoroutable(bool); void setLabel(const QString &); QString getLabel(); QString getDirection(); public: static double DefaultVoltage; public slots: void voltageEntry(const QString & text); void labelEntry(); protected: void removeMeFromBus(double voltage); double useVoltage(ConnectorItem * connectorItem); virtual QString makeSvg(ViewLayer::ViewLayerID); QString replaceTextElement(QString svg); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); void resetLayerKin(); protected: double m_voltage; QPointer m_connector0; QPointer m_connector1; bool m_voltageReference; bool m_isNetLabel; }; class NetLabel : public SymbolPaletteItem { Q_OBJECT public: NetLabel(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~NetLabel(); void addedToScene(bool temporary); QString retrieveSvg(ViewLayer::ViewLayerID, QHash & svgHash, bool blackOnly, double dpi, double & factor); PluralType isPlural(); bool isOnlyNetLabel(); QString getInspectorTitle(); void setInspectorTitle(const QString & oldText, const QString & newText); protected: QString makeSvg(ViewLayer::ViewLayerID); }; #endif fritzing-app-0.9.3b/src/items/tracewire.cpp000066400000000000000000000153621272363601300207120ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "tracewire.h" #include "../sketch/infographicsview.h" #include "../connectors/connectoritem.h" #include "../utils/focusoutcombobox.h" #include const int TraceWire::MinTraceWidthMils = 8; const int TraceWire::MaxTraceWidthMils = 128; ///////////////////////////////////////////////////////// TraceWire::TraceWire( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu ) : ClipableWire(modelPart, viewID, viewGeometry, id, itemMenu, true) { m_canChainMultiple = true; m_wireDirection = TraceWire::NoDirection; } TraceWire::~TraceWire() { } QComboBox * TraceWire::createWidthComboBox(double m, QWidget * parent) { QComboBox * comboBox = new FocusOutComboBox(parent); // new QComboBox(parent); comboBox->setEditable(true); QIntValidator * intValidator = new QIntValidator(comboBox); intValidator->setRange(MinTraceWidthMils, MaxTraceWidthMils); comboBox->setValidator(intValidator); comboBox->setToolTip(tr("Select from the dropdown, or type in any value from %1 to %2").arg(MinTraceWidthMils).arg(MaxTraceWidthMils)); int ix = 0; if (!Wire::widths.contains(m)) { Wire::widths.append(m); qSort(Wire::widths.begin(), Wire::widths.end()); } foreach(long widthValue, Wire::widths) { QString widthName = Wire::widthTrans.value(widthValue, ""); QVariant val((int) widthValue); comboBox->addItem(widthName.isEmpty() ? QString::number(widthValue) : widthName, val); if (qAbs(m - widthValue) < .01) { comboBox->setCurrentIndex(ix); } ix++; } return comboBox; } bool TraceWire::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("width", Qt::CaseInsensitive) == 0) { if (viewID() != ViewLayer::PCBView) { // only in pcb view for now hide = true; return false; } returnProp = tr("width"); QComboBox * comboBox = createWidthComboBox(mils(), parent); comboBox->setEnabled(swappingEnabled); comboBox->setObjectName("infoViewComboBox"); connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(widthEntry(const QString &))); returnWidget = comboBox; returnValue = comboBox->currentText(); return true; } bool result = ClipableWire::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); if (prop.compare("layer") == 0 && returnWidget != NULL) { bool disabled = !canSwitchLayers(); if (!disabled) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL || infoGraphicsView->boardLayers() == 1) disabled = true; } returnWidget->setDisabled(disabled); } return result; return result; } void TraceWire::widthEntry(const QString & text) { int w = widthEntry(text, sender()); if (w == 0) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->changeWireWidthMils(QString::number(w)); } } int TraceWire::widthEntry(const QString & text, QObject * sender) { QComboBox * comboBox = qobject_cast(sender); if (comboBox == NULL) return 0; int w = comboBox->itemData(comboBox->currentIndex()).toInt(); if (w == 0) { // user typed in a number w = text.toInt(); } if (!Wire::widths.contains(w)) { Wire::widths.append(w); qSort(Wire::widths.begin(), Wire::widths.end()); } return w; } void TraceWire::setColorFromElement(QDomElement & element) { switch (m_viewLayerID) { case ViewLayer::Copper0Trace: element.setAttribute("color", ViewLayer::Copper0WireColor); break; case ViewLayer::Copper1Trace: element.setAttribute("color", ViewLayer::Copper1WireColor); break; case ViewLayer::SchematicTrace: //element.setAttribute("color", "#000000"); default: break; } Wire::setColorFromElement(element); } bool TraceWire::canSwitchLayers() { QList wires; QList ends; collectChained(wires, ends); foreach (ConnectorItem * end, ends) { if (end->getCrossLayerConnectorItem() == NULL) return false; } return true; } void TraceWire::setWireDirection(TraceWire::WireDirection wireDirection) { m_wireDirection = wireDirection; } TraceWire::WireDirection TraceWire::wireDirection() { return m_wireDirection; } TraceWire * TraceWire::getTrace(ConnectorItem * connectorItem) { return qobject_cast(connectorItem->attachedTo()); } void TraceWire::setSchematic(bool schematic) { m_viewGeometry.setSchematicTrace(schematic); } QHash TraceWire::prepareProps(ModelPart * modelPart, bool wantDebug, QStringList & keys) { QHash props = ClipableWire::prepareProps(modelPart, wantDebug, keys); if (m_viewID != ViewLayer::PCBView) return props; if (!m_viewGeometry.getPCBTrace()) return props; keys.append("layer"); props.insert("layer", ViewLayer::topLayers().contains(m_viewLayerID) ? "top" : "bottom"); return props; } QStringList TraceWire::collectValues(const QString & family, const QString & prop, QString & value) { if (prop.compare("layer") == 0) { QStringList values = ClipableWire::collectValues(family, prop, value); if (values.count() == 0) { values << TranslatedPropertyNames.value("bottom") << TranslatedPropertyNames.value("top"); if (ViewLayer::bottomLayers().contains(m_viewLayerID)) { value = values.at(0); } else { value = values.at(1); } } return values; } return ClipableWire::collectValues(family, prop, value); } fritzing-app-0.9.3b/src/items/tracewire.h000066400000000000000000000044711272363601300203560ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef TRACEWIRE_H #define TRACEWIRE_H #include "clipablewire.h" class TraceWire : public ClipableWire { Q_OBJECT public: TraceWire( ModelPart * modelPart, ViewLayer::ViewID, const ViewGeometry & , long id, QMenu* itemMenu ); ~TraceWire(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool canSwitchLayers(); void setSchematic(bool schematic); public: static TraceWire * getTrace(ConnectorItem *); static class QComboBox * createWidthComboBox(double currentMils, QWidget * parent); static int widthEntry(const QString & text, QObject * sender); QHash prepareProps(ModelPart *, bool wantDebug, QStringList & keys); QStringList collectValues(const QString & family, const QString & prop, QString & value); public: enum WireDirection { NoDirection, Vertical, Horizontal, Diagonal }; void setWireDirection(TraceWire::WireDirection); TraceWire::WireDirection wireDirection(); public: static const int MinTraceWidthMils; static const int MaxTraceWidthMils; protected: void setColorFromElement(QDomElement & element); protected slots: void widthEntry(const QString & text); protected: WireDirection m_wireDirection; }; #endif fritzing-app-0.9.3b/src/items/via.cpp000066400000000000000000000100071272363601300174730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "via.h" #include "../utils/graphicsutils.h" #include "../fsvgrenderer.h" #include "../utils/textutils.h" #include "../viewlayer.h" #include "../connectors/connectoritem.h" #include "../connectors/svgidlayer.h" #include static HoleClassThing TheHoleThing; const QString Via::AutorouteViaHoleSize = "autorouteViaHoleSize"; const QString Via::AutorouteViaRingThickness = "autorouteViaRingThickness"; QString Via::DefaultAutorouteViaHoleSize; QString Via::DefaultAutorouteViaRingThickness; Via::Via( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel) : Hole(modelPart, viewID, viewGeometry, id, itemMenu, doLabel) { QSettings settings; QString ringThickness = settings.value(AutorouteViaRingThickness, "").toString(); QString holeSize = settings.value(AutorouteViaHoleSize, "").toString(); bool holeSizeWasEmpty = holeSize.isEmpty(); bool ringThicknessWasEmpty = holeSize.isEmpty(); PaletteItem::setUpHoleSizes("via", TheHoleThing); if (ringThicknessWasEmpty) { settings.setValue(AutorouteViaRingThickness, TheHoleThing.ringThickness); DefaultAutorouteViaRingThickness = TheHoleThing.ringThickness; } if (holeSizeWasEmpty) { settings.setValue(AutorouteViaHoleSize, TheHoleThing.holeSize); DefaultAutorouteViaHoleSize = TheHoleThing.holeSize; } //DebugDialog::debug(QString("creating via %1 %2 %3").arg((long) this, 0, 16).arg(id).arg(m_viewID)); } Via::~Via() { //DebugDialog::debug(QString("deleting via %1 %2 %3").arg((long) this, 0, 16).arg(m_id).arg(m_viewID)); } void Via::initHoleSettings(HoleSettings & holeSettings) { // called only by AutorouterSettingsDialog PaletteItem::initHoleSettings(holeSettings, &TheHoleThing); } void Via::setBoth(const QString & holeDiameter, const QString & ringThickness) { if (this->m_viewID != ViewLayer::PCBView) return; ItemBase * otherLayer = setBothSvg(holeDiameter, ringThickness); resetConnectors(otherLayer, otherLayer->fsvgRenderer()); double hd = TextUtils::convertToInches(holeDiameter) * GraphicsUtils::SVGDPI; double rt = TextUtils::convertToInches(ringThickness) * GraphicsUtils::SVGDPI; ConnectorItem * ci = connectorItem(); ci->setRadius((hd / 2) + (rt / 2), rt); ci->getCrossLayerConnectorItem()->setRadius((hd / 2) + (rt / 2), rt); } QString Via::makeID() { return "connector0pin"; } void Via::setAutoroutable(bool ar) { m_viewGeometry.setAutoroutable(ar); } bool Via::getAutoroutable() { return m_viewGeometry.getAutoroutable(); } ConnectorItem * Via::connectorItem() { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { return connectorItem; } return NULL; } void Via::saveInstanceLocation(QXmlStreamWriter & streamWriter) { streamWriter.writeAttribute("x", QString::number(m_viewGeometry.loc().x())); streamWriter.writeAttribute("y", QString::number(m_viewGeometry.loc().y())); streamWriter.writeAttribute("wireFlags", QString::number(m_viewGeometry.flagsAsInt())); GraphicsUtils::saveTransform(streamWriter, m_viewGeometry.transform()); } fritzing-app-0.9.3b/src/items/via.h000066400000000000000000000034611272363601300171460ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef VIA_H #define VIA_H #include "hole.h" class Via : public Hole { Q_OBJECT public: Via(ModelPart *, ViewLayer::ViewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel); ~Via(); void setAutoroutable(bool); bool getAutoroutable(); ConnectorItem * connectorItem(); void saveInstanceLocation(QXmlStreamWriter & streamWriter); public: static const QString AutorouteViaHoleSize; static const QString AutorouteViaRingThickness; static QString DefaultAutorouteViaHoleSize; static QString DefaultAutorouteViaRingThickness; public: static void initHoleSettings(HoleSettings & holeSettings); protected: QString makeID(); void setBoth(const QString & holeDiameter, const QString & thickness); protected: static void setBothConnectors(ItemBase * itemBase, SvgIdLayer * svgIdLayer); }; #endif fritzing-app-0.9.3b/src/items/virtualwire.cpp000066400000000000000000000102551272363601300212760ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "virtualwire.h" #include "../connectors/connectoritem.h" #include "../model/modelpart.h" const double VirtualWire::ShapeWidthExtra = 4; VirtualWire::VirtualWire( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu ) : ClipableWire(modelPart, viewID, viewGeometry, id, itemMenu, false) { // note: at this point in fritzing development, the VirtualWire class is only ever used for ratsnest wires modelPart->setLocalProp("ratsnest", "true"); m_colorWasNamed = false; setFlag(QGraphicsItem::ItemIsSelectable, false); } VirtualWire::~VirtualWire() { } void VirtualWire::paint (QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget ) { if (m_hidden) return; m_hoverCount = m_connectorHoverCount = 0; // kills any highlighting Wire::paint(painter, option, widget); } void VirtualWire::connectionChange(ConnectorItem * onMe, ConnectorItem * onIt, bool connect) { checkVisibility(onMe, onIt, connect); } FSvgRenderer * VirtualWire::setUpConnectors(ModelPart * modelPart, ViewLayer::ViewID viewID) { FSvgRenderer * renderer = Wire::setUpConnectors(modelPart, viewID); hideConnectors(); return renderer; } void VirtualWire::hideConnectors() { // m_connector0 and m_connector1 may not yet be initialized foreach (ConnectorItem * item, cachedConnectorItems()) { item->setHidden(true); } } void VirtualWire::inactivateConnectors() { // m_connector0 and m_connector1 may not yet be initialized foreach (ConnectorItem * item, cachedConnectorItems()) { item->setInactive(true); } } void VirtualWire::setInactive(bool inactivate) { ItemBase::setInactive(inactivate); if (!inactivate) { inactivateConnectors(); } } void VirtualWire::setHidden(bool hide) { ItemBase::setHidden(hide); if (!hide) { hideConnectors(); } } void VirtualWire::tempRemoveAllConnections() { ConnectorItem * connectorItem = connector0(); for (int j = connectorItem->connectedToItems().count() - 1; j >= 0; j--) { connectorItem->connectedToItems()[j]->tempRemove(connectorItem, false); connectorItem->tempRemove(connectorItem->connectedToItems()[j], false); } connectorItem = connector1(); for (int j = connectorItem->connectedToItems().count() - 1; j >= 0; j--) { connectorItem->connectedToItems()[j]->tempRemove(connectorItem, false); connectorItem->tempRemove(connectorItem->connectedToItems()[j], false); } } void VirtualWire::mousePressEvent(QGraphicsSceneMouseEvent *event) { // ignore clicks where our connectors are supposed to be // so the click can percolate to some other graphicsitem that can use it QList items = scene()->items(event->scenePos()); if (items.contains(m_connector0) || items.contains(m_connector1)) { event->ignore(); return; } // set selectable flag temporarily here so that dragging out a bendpoint is enabled setFlag(QGraphicsItem::ItemIsSelectable, true); ClipableWire::mousePressEvent(event); setFlag(QGraphicsItem::ItemIsSelectable, false); } void VirtualWire::setColorWasNamed(bool colorWasNamed) { m_colorWasNamed = colorWasNamed; } bool VirtualWire::colorWasNamed() { return m_colorWasNamed; } QPainterPath VirtualWire::shape() const { return shapeAux(m_hoverStrokeWidth); } fritzing-app-0.9.3b/src/items/virtualwire.h000066400000000000000000000036051272363601300207440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef VIRTUALWIRE_H #define VIRTUALWIRE_H #include "clipablewire.h" class VirtualWire : public ClipableWire { Q_OBJECT public: VirtualWire( ModelPart * modelPart, ViewLayer::ViewID, const ViewGeometry & , long id, QMenu* itemMenu ); ~VirtualWire(); void setHidden(bool hidden); void setInactive(bool inactivate); void tempRemoveAllConnections(); void setColorWasNamed(bool); bool colorWasNamed(); QPainterPath shape() const; protected: void paint (QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget ); void mousePressEvent(QGraphicsSceneMouseEvent *event); void connectionChange(ConnectorItem * onMe, ConnectorItem * onIt, bool connect); class FSvgRenderer * setUpConnectors(class ModelPart *, ViewLayer::ViewID); void hideConnectors(); void inactivateConnectors(); public: static const double ShapeWidthExtra; protected: bool m_colorWasNamed; }; #endif fritzing-app-0.9.3b/src/items/wire.cpp000066400000000000000000001552211272363601300176720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 7000 $: $Author: irascibl@gmail.com $: $Date: 2013-04-29 07:24:08 +0200 (Mo, 29. Apr 2013) $ ********************************************************************/ /* curvy To Do curvy to begin with? would have to vary with some function of angle and distance could convert control points to t values? turn curvature on/off per view --------------------------------------------------------- later: clippable wire gerber autorouter warning in PCB view modify parameters (tension, unit area)? */ ///////////////////////////////////////////////////////////////// #include "wire.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../debugdialog.h" #include "../sketch/infographicsview.h" #include "../connectors/connectoritem.h" #include "../connectors/svgidlayer.h" #include "../fsvgrenderer.h" #include "partlabel.h" #include "../model/modelpart.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/bezier.h" #include "../utils/bezierdisplay.h" #include "../utils/cursormaster.h" #include "../utils/ratsnestcolors.h" #include "../layerattributes.h" #include QVector Wire::TheDash; QVector RatDash; QBrush BandedBrush(QColor(255, 255, 255)); QHash Wire::colorTrans; QStringList Wire::colorNames; QHash Wire::widthTrans; QList Wire::widths; double Wire::STANDARD_TRACE_WIDTH; double Wire::HALF_STANDARD_TRACE_WIDTH; double Wire::THIN_TRACE_WIDTH; const double DefaultHoverStrokeWidth = 4; static Bezier UndoBezier; static BezierDisplay * TheBezierDisplay = NULL; //////////////////////////////////////////////////////////// bool alphaLessThan(QColor * c1, QColor * c2) { return c1->alpha() < c2->alpha(); } void debugCompare(ItemBase * it) { Wire * wire = dynamic_cast(it); if (wire) { QRectF r0 = wire->connector0()->rect(); QRectF r1 = wire->connector1()->rect(); if (qAbs(r0.left() - r1.left()) < 0.1 && qAbs(r0.right() - r1.right()) < 0.1 && qAbs(r0.top() - r1.top()) < 0.1 && qAbs(r0.bottom() - r1.bottom()) < 0.1) { wire->debugInfo("zero wire"); if (wire->viewID() == ViewLayer::PCBView) { DebugDialog::debug("in pcb"); } } } } ///////////////////////////////////////////////////////////// WireAction::WireAction(QAction * action) : QAction(action) { m_wire = NULL; this->setText(action->text()); this->setStatusTip(action->statusTip()); this->setCheckable(action->isCheckable()); } WireAction::WireAction(const QString & title, QObject * parent) : QAction(title, parent) { m_wire = NULL; } void WireAction::setWire(Wire * w) { m_wire = w; } Wire * WireAction::wire() { return m_wire; } ///////////////////////////////////////////////////////////// Wire::Wire( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu* itemMenu, bool initLabel) : ItemBase(modelPart, viewID, viewGeometry, id, itemMenu) { m_banded = false; m_bezier = NULL; m_displayBendpointCursor = m_canHaveCurve = true; m_hoverStrokeWidth = DefaultHoverStrokeWidth; m_connector0 = m_connector1 = NULL; m_partLabel = initLabel ? new PartLabel(this, NULL) : NULL; m_canChainMultiple = false; setFlag(QGraphicsItem::ItemIsSelectable, true ); m_connectorHover = NULL; m_opacity = 1.0; m_ignoreSelectionChange = false; //DebugDialog::debug(QString("aix line %1 %2 %3 %4").arg(this->viewGeometry().line().x1()) //.arg(this->viewGeometry().line().y1()) //.arg(this->viewGeometry().line().x2()) //.arg(this->viewGeometry().line().y2()) ); //DebugDialog::debug(QString("aix loc %1 %2").arg(this->viewGeometry().loc().x()) //.arg(this->viewGeometry().loc().y()) ); setPos(m_viewGeometry.loc()); m_dragCurve = m_dragEnd = false; } Wire::~Wire() { if (m_bezier) { delete m_bezier; } } FSvgRenderer * Wire::setUp(ViewLayer::ViewLayerID viewLayerID, const LayerHash & viewLayers, InfoGraphicsView * infoGraphicsView) { ItemBase::setViewLayerID(viewLayerID, viewLayers); FSvgRenderer * svgRenderer = setUpConnectors(m_modelPart, m_viewID); if (svgRenderer != NULL) { initEnds(m_viewGeometry, svgRenderer->viewBox(), infoGraphicsView); //debugCompare(this); } setZValue(this->z()); return svgRenderer; } void Wire::saveGeometry() { m_viewGeometry.setSelected(this->isSelected()); m_viewGeometry.setLine(this->line()); m_viewGeometry.setLoc(this->pos()); m_viewGeometry.setZ(this->zValue()); } bool Wire::itemMoved() { if (m_viewGeometry.loc() != this->pos()) return true; if (this->line().dx() != m_viewGeometry.line().dx()) return false; if (this->line().dy() != m_viewGeometry.line().dy()) return false; return (this->line() != m_viewGeometry.line()); } void Wire::moveItem(ViewGeometry & viewGeometry) { this->setPos(viewGeometry.loc()); this->setLine(viewGeometry.line()); } void Wire::initEnds(const ViewGeometry & vg, QRectF defaultRect, InfoGraphicsView * infoGraphicsView) { double penWidth = 1; foreach (ConnectorItem * item, cachedConnectorItems()) { if (item->connectorSharedID().endsWith("0")) { penWidth = item->rect().width(); m_connector0 = item; //item->debugInfo("connector 0"); } else { //item->debugInfo("connector 1"); m_connector1 = item; } } if (m_connector0 == NULL || m_connector1 == NULL) { // should never happen return; } if ((vg.line().length() == 0) && (vg.line().x1() == -1)) { this->setLine(defaultRect.left(), defaultRect.top(), defaultRect.right(), defaultRect.bottom()); } else { this->setLine(vg.line()); } setConnector0Rect(); setConnector1Rect(); m_viewGeometry.setLine(this->line()); QBrush brush(QColor(0, 0, 0)); QPen pen(brush, penWidth, Qt::SolidLine, Qt::RoundCap); this->setPen(pen); m_pen.setCapStyle(Qt::RoundCap); m_shadowPen.setCapStyle(Qt::RoundCap); if (infoGraphicsView != NULL) { infoGraphicsView->initWire(this, penWidth); } prepareGeometryChange(); } void Wire::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget ) { if (m_hidden) return; ItemBase::paint(painter, option, widget); } void Wire::paintBody(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget ) { Q_UNUSED(option); Q_UNUSED(widget); QPainterPath painterPath; if (m_bezier && !m_bezier->isEmpty()) { QLineF line = this->line(); painterPath.moveTo(line.p1()); painterPath.cubicTo(m_bezier->cp0(), m_bezier->cp1(), line.p2()); /* DebugDialog::debug(QString("c0x:%1,c0y:%2 c1x:%3,c1y:%4 p0x:%5,p0y:%6 p1x:%7,p1y:%8 px:%9,py:%10") .arg(m_controlPoints.at(0).x()) .arg(m_controlPoints.at(0).y()) .arg(m_controlPoints.at(1).x()) .arg(m_controlPoints.at(1).y()) .arg(m_line.p1().x()) .arg(m_line.p1().y()) .arg(m_line.p2().x()) .arg(m_line.p2().y()) .arg(pos().x()) .arg(pos().y()) ); */ } painter->setOpacity(m_inactive ? m_opacity / 2 : m_opacity); if (hasShadow()) { painter->save(); painter->setPen(m_shadowPen); if (painterPath.isEmpty()) { QLineF line = this->line(); painter->drawLine(line); } else { painter->drawPath(painterPath); } painter->restore(); } // DebugDialog::debug(QString("pen width %1 %2").arg(m_pen.widthF()).arg(m_viewID)); if (m_banded) { QBrush brush = m_pen.brush(); m_pen.setStyle(Qt::SolidLine); m_pen.setBrush(BandedBrush); painter->setPen(m_pen); if (painterPath.isEmpty()) { painter->drawLine(getPaintLine()); } else { painter->drawPath(painterPath); } m_pen.setBrush(brush); m_pen.setDashPattern(TheDash); m_pen.setCapStyle(Qt::FlatCap); } if (getRatsnest()) { m_pen.setDashPattern(RatDash); } painter->setPen(m_pen); if (painterPath.isEmpty()) { painter->drawLine(getPaintLine()); } else { painter->drawPath(painterPath); } if (m_banded) { m_pen.setStyle(Qt::SolidLine); m_pen.setCapStyle(Qt::RoundCap); } } void Wire::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget); Q_UNUSED(option); painter->save(); if ((m_connectorHoverCount > 0 && !(m_dragEnd || m_dragCurve)) || m_connectorHoverCount2 > 0) { painter->setOpacity(.50); painter->fillPath(this->hoverShape(), QBrush(ConnectorHoverColor)); } else { painter->setOpacity(HoverOpacity); painter->fillPath(this->hoverShape(), QBrush(HoverColor)); } painter->restore(); } QPainterPath Wire::hoverShape() const { return shapeAux(m_hoverStrokeWidth); } QPainterPath Wire::shape() const { return shapeAux(m_pen.widthF()); } QPainterPath Wire::shapeAux(double width) const { QPainterPath path; if (m_line == QLineF()) { return path; } path.moveTo(m_line.p1()); if (m_bezier == NULL || m_bezier->isEmpty()) { path.lineTo(m_line.p2()); } else { path.cubicTo(m_bezier->cp0(), m_bezier->cp1(), m_line.p2()); } //DebugDialog::debug(QString("using hoverstrokewidth %1 %2").arg(m_id).arg(m_hoverStrokeWidth)); return GraphicsUtils::shapeFromPath(path, m_pen, width, false); } QRectF Wire::boundingRect() const { if (m_pen.widthF() == 0.0) { const double x1 = m_line.p1().x(); const double x2 = m_line.p2().x(); const double y1 = m_line.p1().y(); const double y2 = m_line.p2().y(); double lx = qMin(x1, x2); double rx = qMax(x1, x2); double ty = qMin(y1, y2); double by = qMax(y1, y2); return QRectF(lx, ty, rx - lx, by - ty); } return hoverShape().controlPointRect(); } void Wire::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { //DebugDialog::debug("checking press event"); emit wireSplitSignal(this, event->scenePos(), this->pos(), this->line()); } void Wire::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setActiveWire(this); } ItemBase::contextMenuEvent(event); } void Wire::mousePressEvent(QGraphicsSceneMouseEvent *event) { ItemBase::mousePressEvent(event); } void Wire::initDragCurve(QPointF scenePos) { if (m_bezier == NULL) { m_bezier = new Bezier(); } UndoBezier.copy(m_bezier); m_dragCurve = true; m_dragEnd = false; QPointF p0 = connector0()->sceneAdjustedTerminalPoint(NULL); QPointF p1 = connector1()->sceneAdjustedTerminalPoint(NULL); if (m_bezier->isEmpty()) { m_bezier->initToEnds(mapFromScene(p0), mapFromScene(p1)); } else { m_bezier->set_endpoints(mapFromScene(p0), mapFromScene(p1)); } m_bezier->initControlIndex(mapFromScene(scenePos), m_pen.widthF()); TheBezierDisplay = new BezierDisplay; TheBezierDisplay->initDisplay(this, m_bezier); } bool Wire::initNewBendpoint(QPointF scenePos, Bezier & left, Bezier & right) { if (m_bezier == NULL || m_bezier->isEmpty()) { UndoBezier.clear(); return false; } QPointF p0 = connector0()->sceneAdjustedTerminalPoint(NULL); QPointF p1 = connector1()->sceneAdjustedTerminalPoint(NULL); m_bezier->set_endpoints(mapFromScene(p0), mapFromScene(p1)); UndoBezier.copy(m_bezier); double t = m_bezier->findSplit(mapFromScene(scenePos), m_pen.widthF()); m_bezier->split(t, left, right); return true; } void Wire::initDragEnd(ConnectorItem * connectorItem, QPointF scenePos) { Q_UNUSED(scenePos); saveGeometry(); QLineF line = this->line(); m_drag0 = (connectorItem == m_connector0); //debugInfo("setting drag end to true"); m_dragEnd = true; m_dragCurve = false; if (m_drag0) { m_wireDragOrigin = line.p2(); //DebugDialog::debug(QString("drag near origin %1 %2").arg(m_wireDragOrigin.x()).arg(m_wireDragOrigin.y()) ); } else { m_wireDragOrigin = line.p1(); //DebugDialog::debug(QString("drag far origin %1 %2").arg(m_wireDragOrigin.x()).arg(m_wireDragOrigin.y()) ); //DebugDialog::debug(QString("drag far other %1 %2").arg(line.p2().x()).arg(line.p2().y()) ); } if (connectorItem->chained()) { QList chained; QList ends; collectChained(chained, ends); // already saved the first one for (int i = 1; i < chained.count(); i++) { chained[i]->saveGeometry(); } } } void Wire::mouseReleaseConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) { Q_UNUSED(event); Q_UNUSED(connectorItem); releaseDrag(); } void Wire::mouseMoveConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) { mouseMoveEventAux(this->mapFromItem(connectorItem, event->pos()), event->modifiers()); } void Wire::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { mouseMoveEventAux(event->pos(), event->modifiers()); } void Wire::mouseMoveEventAux(QPointF eventPos, Qt::KeyboardModifiers modifiers) { if (m_spaceBarWasPressed) { return; } if (m_dragCurve) { prepareGeometryChange(); dragCurve(eventPos, modifiers); update(); if (TheBezierDisplay) TheBezierDisplay->updateDisplay(this, m_bezier); return; } if (m_dragEnd == false) { return; } //debugInfo("dragging wire"); ConnectorItem * whichConnectorItem; ConnectorItem * otherConnectorItem; if (m_drag0) { whichConnectorItem = m_connector0; otherConnectorItem = m_connector1; } else { whichConnectorItem = m_connector1; otherConnectorItem = m_connector0; } if ((modifiers & Qt::ShiftModifier) != 0) { QPointF initialPos = mapFromScene(otherConnectorItem->sceneAdjustedTerminalPoint(NULL)); bool bendpoint = isBendpoint(whichConnectorItem); if (bendpoint) { bendpoint = false; foreach (ConnectorItem * ci, whichConnectorItem->connectedToItems()) { Wire * w = qobject_cast(ci->attachedTo()); ConnectorItem * oci = w->otherConnector(ci); QPointF otherInitialPos = mapFromScene(oci->sceneAdjustedTerminalPoint(NULL)); QPointF p1(initialPos.x(), otherInitialPos.y()); double d = GraphicsUtils::distanceSqd(p1, eventPos); if (d <= 144) { bendpoint = true; eventPos = p1; break; } p1.setX(otherInitialPos.x()); p1.setY(initialPos.y()); d = GraphicsUtils::distanceSqd(p1, eventPos); if (d <= 144) { bendpoint = true; eventPos = p1; break; } } } if (!bendpoint) { eventPos = GraphicsUtils::calcConstraint(initialPos, eventPos); } } if (m_drag0) { QPointF p = this->mapToScene(eventPos); QGraphicsSvgItem::setPos(p.x(), p.y()); this->setLine(0, 0, m_wireDragOrigin.x() - p.x() + m_viewGeometry.loc().x(), m_wireDragOrigin.y() - p.y() + m_viewGeometry.loc().y() ); //DebugDialog::debug(QString("drag0 wdo:(%1,%2) p:(%3,%4) vg:(%5,%6) l:(%7,%8)") // .arg(m_wireDragOrigin.x()).arg(m_wireDragOrigin.y()) // .arg(p.x()).arg(p.y()) // .arg(m_viewGeometry.loc().x()).arg(m_viewGeometry.loc().y()) // .arg(line().p2().x()).arg(line().p2().y()) // ); } else { this->setLine(m_wireDragOrigin.x(), m_wireDragOrigin.y(), eventPos.x(), eventPos.y()); //DebugDialog::debug(QString("drag1 wdo:(%1,%2) ep:(%3,%4) p:(%5,%6) l:(%7,%8)") // .arg(m_wireDragOrigin.x()).arg(m_wireDragOrigin.y()) // .arg(eventPos.x()).arg(eventPos.y()) // .arg(pos().x()).arg(pos().y()) // .arg(line().p2().x()).arg(line().p2().y()) // ); } setConnector1Rect(); QSet allTo; allTo.insert(whichConnectorItem); foreach (ConnectorItem * toConnectorItem, whichConnectorItem->connectedToItems()) { Wire * chainedWire = qobject_cast(toConnectorItem->attachedTo()); if (chainedWire == NULL) continue; allTo.insert(toConnectorItem); foreach (ConnectorItem * subTo, toConnectorItem->connectedToItems()) { allTo.insert(subTo); } } allTo.remove(whichConnectorItem); // TODO: this could all be determined once at mouse press time if (allTo.count() == 0) { // dragging one end of the wire // don't allow wire to connect back to something the other end is already directly connected to // an alternative would be to exclude all connectors in the net connected by the same kind of trace QList wires; QList ends; collectChained(wires, ends); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); //DebugDialog::debug("------------------------"); QList exclude; foreach (ConnectorItem * end, ends) { exclude << end; foreach (ConnectorItem * ci, end->connectedToItems()) { // if there is a wire growing out of one of the excluded ends, exclude the attached end exclude << ci; } foreach (ConnectorItem * toConnectorItem, end->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue; Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (w->getRatsnest()) continue; if (!w->isTraceType(infoGraphicsView->getTraceFlag())) continue; //w->debugInfo("what wire"); QList ends2; QList wires2; w->collectChained(wires2, ends2); exclude.append(ends2); foreach (ConnectorItem * e2, ends2) { foreach (ConnectorItem * ci, e2->connectedToItems()) { // if there is a wire growing out of one of the excluded ends, exclude that end of the wire exclude << ci; } } foreach (Wire * w2, wires2) { exclude.append(w2->cachedConnectorItems()); } } } // but allow to restore connections at this end (collect chained above got both ends of this wire) foreach (ConnectorItem * toConnectorItem, whichConnectorItem->connectedToItems()) { if (ends.contains(toConnectorItem)) exclude.removeAll(toConnectorItem); } //DebugDialog::debug(""); //DebugDialog::debug("__________________"); //foreach (ConnectorItem * end, exclude) end->debugInfo("exclude"); ConnectorItem * originatingConnector = NULL; if (otherConnectorItem) { foreach (ConnectorItem * toConnectorItem, otherConnectorItem->connectedToItems()) { if (ends.contains(toConnectorItem)) { originatingConnector = toConnectorItem; break; } } } whichConnectorItem->findConnectorUnder(false, true, exclude, true, originatingConnector); } else { // dragging a bendpoint foreach (ConnectorItem * toConnectorItem, allTo) { Wire * chained = qobject_cast(toConnectorItem->attachedTo()); if (chained) { chained->simpleConnectedMoved(whichConnectorItem, toConnectorItem); } } } } QRectF Wire::connector0Rect(const QLineF & line) { QRectF rect = m_connector0->rect(); rect.moveTo(0 - (rect.width() / 2.0), 0 - (rect.height() / 2.0) ); return rect; } QRectF Wire::connector1Rect(const QLineF & line) { QRectF rect = m_connector1->rect(); rect.moveTo(line.dx() - (rect.width() / 2.0), line.dy() - (rect.height() / 2.0) ); return rect; } void Wire::setConnector0Rect() { QRectF rect = m_connector0->rect(); rect.moveTo(0 - (rect.width() / 2.0), 0 - (rect.height() / 2.0) ); m_connector0->setRect(rect); //debugCompare(this); // QPointF p = m_connector0->mapToScene(m_connector0->rect().center()); // m_connector0->debugInfo(QString("c0:%1 %2").arg(p.x()).arg(p.y())); // p = m_connector1->mapToScene(m_connector1->rect().center()); // m_connector1->debugInfo(QString("c1:%1 %2").arg(p.x()).arg(p.y())); } void Wire::setConnector1Rect() { QRectF rect = m_connector1->rect(); rect.moveTo(this->line().dx() - (rect.width() / 2.0), this->line().dy() - (rect.height() / 2.0) ); m_connector1->setRect(rect); //debugCompare(this); // QPointF p = m_connector0->mapToScene(m_connector0->rect().center()); // m_connector0->debugInfo(QString("c0:%1 %2").arg(p.x()).arg(p.y())); // p = m_connector1->mapToScene(m_connector1->rect().center()); // m_connector1->debugInfo(QString("c1:%1 %2").arg(p.x()).arg(p.y())); } void Wire::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (m_spaceBarWasPressed) { return; } //debugInfo("wire release drag"); if (releaseDrag()) return; ItemBase::mouseReleaseEvent(event); } bool Wire::releaseDrag() { if (m_dragEnd == false && m_dragCurve == false) return false; if (m_dragCurve) { delete TheBezierDisplay; TheBezierDisplay = NULL; m_dragCurve = false; ungrabMouse(); if (UndoBezier != *m_bezier) { emit wireChangedCurveSignal(this, &UndoBezier, m_bezier, false); } return true; } //debugInfo("clearing drag end"); m_dragEnd = false; ConnectorItem * from = (m_drag0) ? m_connector0 : m_connector1; ConnectorItem * to = from->releaseDrag(); QLineF newLine = this->line(); QLineF oldLine = m_viewGeometry.line(); QPointF oldPos = m_viewGeometry.loc(); QPointF newPos = this->pos(); if (newLine != oldLine || oldPos != newPos) { emit wireChangedSignal(this, oldLine, newLine, oldPos, newPos, from, to); } return true; } void Wire::saveInstanceLocation(QXmlStreamWriter & streamWriter) { QLineF line = m_viewGeometry.line(); QPointF loc = m_viewGeometry.loc(); streamWriter.writeAttribute("x", QString::number(loc.x())); streamWriter.writeAttribute("y", QString::number(loc.y())); streamWriter.writeAttribute("x1", QString::number(line.x1())); streamWriter.writeAttribute("y1", QString::number(line.y1())); streamWriter.writeAttribute("x2", QString::number(line.x2())); streamWriter.writeAttribute("y2", QString::number(line.y2())); streamWriter.writeAttribute("wireFlags", QString::number(m_viewGeometry.flagsAsInt())); } void Wire::writeGeometry(QXmlStreamWriter & streamWriter) { ItemBase::writeGeometry(streamWriter); streamWriter.writeStartElement("wireExtras"); streamWriter.writeAttribute("mils", QString::number(mils())); streamWriter.writeAttribute("color", m_pen.brush().color().name()); streamWriter.writeAttribute("opacity", QString::number(m_opacity)); streamWriter.writeAttribute("banded", m_banded ? "1" : "0"); if (m_bezier) m_bezier->write(streamWriter); streamWriter.writeEndElement(); } void Wire::setExtras(QDomElement & element, InfoGraphicsView * infoGraphicsView) { if (element.isNull()) return; bool ok; double w = element.attribute("width").toDouble(&ok); if (ok) { setWireWidth(w, infoGraphicsView, infoGraphicsView->getWireStrokeWidth(this, w)); } else { w = element.attribute("mils").toDouble(&ok); if (ok) { double wpix = GraphicsUtils::mils2pixels(w, GraphicsUtils::SVGDPI); setWireWidth(wpix, infoGraphicsView, infoGraphicsView->getWireStrokeWidth(this, wpix)); } } m_banded = (element.attribute("banded", "") == "1"); setColorFromElement(element); QDomElement bElement = element.firstChildElement("bezier"); Bezier bezier = Bezier::fromElement(bElement); if (!bezier.isEmpty()) { prepareGeometryChange(); m_bezier = new Bezier; m_bezier->copy(&bezier); QPointF p0 = connector0()->sceneAdjustedTerminalPoint(NULL); QPointF p1 = connector1()->sceneAdjustedTerminalPoint(NULL); m_bezier->set_endpoints(mapFromScene(p0), mapFromScene(p1)); } } void Wire::setColorFromElement(QDomElement & element) { QString colorString = element.attribute("color"); if (colorString.isNull() || colorString.isEmpty()) return; bool ok; double op = element.attribute("opacity").toDouble(&ok); if (!ok) { op = 1.0; } setColorString(colorString, op, false); } void Wire::hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event , ConnectorItem * item) { m_connectorHover = item; ItemBase::hoverEnterConnectorItem(event, item); } void Wire::hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { m_connectorHover = NULL; ItemBase::hoverLeaveConnectorItem(event, item); } void Wire::hoverEnterEvent ( QGraphicsSceneHoverEvent * event ) { ItemBase::hoverEnterEvent(event); CursorMaster::instance()->addCursor(this, cursor()); //DebugDialog::debug("---wire set override cursor"); updateCursor(event->modifiers()); } void Wire::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) { ItemBase::hoverLeaveEvent(event); //DebugDialog::debug("------wire restore override cursor"); CursorMaster::instance()->removeCursor(this); } void Wire::connectionChange(ConnectorItem * onMe, ConnectorItem * onIt, bool connect) { checkVisibility(onMe, onIt, connect); bool movable = true; foreach (ConnectorItem * connectedTo, m_connector0->connectedToItems()) { if (connectedTo->attachedToItemType() != ModelPart::Wire) { movable = false; break; } } if (movable) { foreach (ConnectorItem * connectedTo, m_connector1->connectedToItems()) { if (connectedTo->attachedToItemType() != ModelPart::Wire) { movable = false; break; } } } } void Wire::mouseDoubleClickConnectorEvent(ConnectorItem * connectorItem) { int chained = 0; foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { chained++; } else { return; } } if (chained == 1) { // near as I can tell, this is to eliminate the overrides from the connectorItem and then from the wire itself emit wireJoinSignal(this, connectorItem); } } void Wire::mousePressConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) { //DebugDialog::debug("checking press connector event"); if (m_canChainMultiple && event->modifiers() & altOrMetaModifier()) { // dragging a wire out of a bendpoint InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->mousePressConnectorEvent(connectorItem, event); } return; } connectorItem->setOverConnectorItem(NULL); initDragEnd(connectorItem, event->scenePos()); } void Wire::simpleConnectedMoved(ConnectorItem * to) { // to is this wire, from is something else simpleConnectedMoved(to->firstConnectedToIsh(), to); } void Wire::simpleConnectedMoved(ConnectorItem * from, ConnectorItem * to) { if (from == NULL) return; //if (from) from->debugInfo("connected moved from"); //if (to) to->debugInfo("\tto"); // to is this wire, from is something else QPointF p1, p2; calcNewLine(from, to, p1, p2); /* QPointF oldPos = this->pos(); QPointF newPos = p1; QLineF oldLine = this->line(); QLineF newLine(0, 0, p2.x() - p1.x(), p2.y() - p1.y()); if (qAbs(oldPos.x() - newPos.x()) > 1.75 || qAbs(oldPos.y() - newPos.y()) > 1.75 || qAbs(oldLine.x1() - newLine.x1()) > 1.75 || qAbs(oldLine.x2() - newLine.x2()) > 1.75 || qAbs(oldLine.y1() - newLine.y1()) > 1.75 || qAbs(oldLine.y2() - newLine.y2()) > 1.75 ) { DebugDialog::debug("line changed"); calcNewLine(from,to,p1,p2); } */ this->setPos(p1); this->setLine(0,0, p2.x() - p1.x(), p2.y() - p1.y() ); //debugInfo(QString("set line %1 %2, %3 %4, vis:%5").arg(p1.x()).arg(p1.y()).arg(p2.x()).arg(p2.y()).arg(isVisible()) ); setConnector1Rect(); } void Wire::calcNewLine(ConnectorItem * from, ConnectorItem * to, QPointF & p1, QPointF & p2) { // to is this wire, from is something else if (to == m_connector0) { p1 = from->sceneAdjustedTerminalPoint(to); ConnectorItem * otherFrom = m_connector1->firstConnectedToIsh(); if (otherFrom == NULL) { p2 = m_connector1->mapToScene(m_connector1->rect().center()); } else { p2 = otherFrom->sceneAdjustedTerminalPoint(m_connector1); } } else { p2 = from->sceneAdjustedTerminalPoint(to); ConnectorItem * otherFrom = m_connector0->firstConnectedToIsh(); if (otherFrom == NULL) { p1 = m_connector0->mapToScene(m_connector0->rect().center()); } else { p1 = otherFrom->sceneAdjustedTerminalPoint(m_connector0); } } } void Wire::connectedMoved(ConnectorItem * from, ConnectorItem * to, QList & already) { Q_UNUSED(already); // "from" is the connector on the part // "to" is the connector on the wire //from->debugInfo("connected moved"); //to->debugInfo("\tconnected moved"); if (from->connectedToItems().contains(to) || to->connectedToItems().contains(from)) { simpleConnectedMoved(from, to); } else { //from->debugInfo("not connected"); //to->debugInfo("\t"); } } FSvgRenderer * Wire::setUpConnectors(ModelPart * modelPart, ViewLayer::ViewID viewID) { clearConnectorItemCache(); LayerAttributes layerAttributes; this->initLayerAttributes(layerAttributes, viewID, m_viewLayerID, m_viewLayerPlacement, false, false); FSvgRenderer * renderer = ItemBase::setUpImage(modelPart, layerAttributes); if (renderer == NULL) { return NULL; } foreach (Connector * connector, modelPart->connectors().values()) { if (connector == NULL) continue; SvgIdLayer * svgIdLayer = connector->fullPinInfo(viewID, m_viewLayerID); if (svgIdLayer == NULL) continue; bool result = renderer->setUpConnector(svgIdLayer, false, viewLayerPlacement()); if (!result) continue; ConnectorItem * connectorItem = newConnectorItem(connector); connectorItem->setRect(svgIdLayer->rect(viewLayerPlacement())); connectorItem->setTerminalPoint(svgIdLayer->point(viewLayerPlacement())); m_originalConnectorRect = svgIdLayer->rect(viewLayerPlacement()); connectorItem->setCircular(true); //DebugDialog::debug(QString("terminal point %1 %2").arg(terminalPoint.x()).arg(terminalPoint.y()) ); } return renderer; } /* void Wire::setPos(const QPointF & pos) { ItemBase::setPos(pos); } */ void Wire::setLineAnd(QLineF line, QPointF pos, bool useLine) { this->setPos(pos); if (useLine) this->setLine(line); setConnector1Rect(); } ConnectorItem * Wire::otherConnector(ConnectorItem * oneConnector) { if (oneConnector == m_connector0) return m_connector1; return m_connector0; } ConnectorItem * Wire::connector0() { return m_connector0; } ConnectorItem * Wire::connector1() { return m_connector1; } void Wire::findConnectorsUnder() { foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->connectionsCount() > 0) continue; // only check free ends connectorItem->findConnectorUnder(true, false, ConnectorItem::emptyConnectorItemList, false, NULL); } } void Wire::collectChained(QList & chained, QList & ends ) { chained.append(this); for (int i = 0; i < chained.count(); i++) { Wire * wire = chained[i]; collectChained(wire->m_connector1, chained, ends); collectChained(wire->m_connector0, chained, ends); } } void Wire::collectChained(ConnectorItem * connectorItem, QList & chained, QList & ends) { if (connectorItem == NULL) return; foreach (ConnectorItem * connectedToItem, connectorItem->connectedToItems()) { Wire * wire = qobject_cast(connectedToItem->attachedTo()); if (wire == NULL) { if (!ends.contains(connectedToItem)) { ends.append(connectedToItem); } continue; } if (chained.contains(wire)) continue; chained.append(wire); } } void Wire::collectWires(QList & wires) { if (wires.contains(this)) return; wires.append(this); //DebugDialog::debug(QString("collecting wire %1").arg(this->id()) ); collectWiresAux(wires, m_connector0); collectWiresAux(wires, m_connector1); } void Wire::collectWiresAux(QList & wires, ConnectorItem * start) { foreach (ConnectorItem * toConnectorItem, start->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { qobject_cast(toConnectorItem->attachedTo())->collectWires(wires); } } } bool Wire::stickyEnabled() { QList wires; QList ends; this->collectChained(wires, ends); foreach (ConnectorItem * connector, ends) { if (connector->connectionsCount() > 0) { return false; } } return true; } bool Wire::getTrace() { return m_viewGeometry.getAnyTrace(); } bool Wire::getRouted() { return m_viewGeometry.getRouted(); } void Wire::setRouted(bool routed) { m_viewGeometry.setRouted(routed); } void Wire::setRatsnest(bool ratsnest) { m_viewGeometry.setRatsnest(ratsnest); } void Wire::setAutoroutable(bool ar) { m_viewGeometry.setAutoroutable(ar); } bool Wire::getAutoroutable() { return m_viewGeometry.getAutoroutable(); } void Wire::setNormal(bool normal) { m_viewGeometry.setNormal(normal); } bool Wire::getNormal() { return m_viewGeometry.getNormal(); } void Wire::setColor(const QColor & color, double op) { m_pen.setBrush(QBrush(color)); m_opacity = op; m_colorName = color.name(); this->update(); } void Wire::setShadowColor(QColor & color, bool restore) { m_shadowBrush = QBrush(color); m_shadowPen.setBrush(m_shadowBrush); m_bendpointPen.setBrush(m_shadowBrush); m_bendpoint2Pen.setBrush(m_shadowBrush); QList visited; if (restore) { if (m_connector0) m_connector0->restoreColor(visited); if (m_connector1) m_connector1->restoreColor(visited); } this->update(); } const QColor & Wire::color() { return m_pen.brush().color(); } void Wire::setWireWidth(double width, InfoGraphicsView * infoGraphicsView, double hoverStrokeWidth) { if (m_pen.widthF() == width) return; prepareGeometryChange(); setPenWidth(width, infoGraphicsView, hoverStrokeWidth); QList visited; if (m_connector0) m_connector0->restoreColor(visited); if (m_connector1) m_connector1->restoreColor(visited); update(); } double Wire::width() { return m_pen.widthF(); } double Wire::shadowWidth() { return m_shadowPen.widthF(); } double Wire::mils() { return 1000 * m_pen.widthF() / GraphicsUtils::SVGDPI; } void Wire::setColorString(QString colorName, double op, bool restore) { // sets a color using the name (.e. "red") // note: colorName is associated with a Fritzing color, not a Qt color QString colorString = RatsnestColors::wireColor(m_viewID, colorName); if (colorString.isEmpty()) { colorString = colorName; } QColor c; c.setNamedColor(colorString); setColor(c, op); m_colorName = colorName; QString shadowColorString = RatsnestColors::shadowColor(m_viewID, colorName); if (shadowColorString.isEmpty()) { shadowColorString = colorString; } c.setNamedColor(shadowColorString); setShadowColor(c, restore); } QString Wire::hexString() { return m_pen.brush().color().name(); } QString Wire::shadowHexString() { return m_shadowPen.brush().color().name(); } QString Wire::colorString() { return m_colorName; } void Wire::initNames() { if (colorNames.count() > 0) return; TheDash.clear(); TheDash << 10 << 8; RatDash.clear(); RatDash << 2 << 2; widths << 8 << 12 << 16 << 24 << 32 << 48; int i = 0; widthTrans.insert(widths[i++], tr("super fine (8 mil)")); widthTrans.insert(widths[i++], tr("extra thin (12 mil)")); THIN_TRACE_WIDTH = GraphicsUtils::mils2pixels(widths[i], GraphicsUtils::SVGDPI); widthTrans.insert(widths[i++], tr("thin (16 mil)")); STANDARD_TRACE_WIDTH = GraphicsUtils::mils2pixels(widths[i], GraphicsUtils::SVGDPI); widthTrans.insert(widths[i++], tr("standard (24 mil)")); widthTrans.insert(widths[i++], tr("thick (32 mil)")); widthTrans.insert(widths[i++], tr("extra thick (48 mil)")); HALF_STANDARD_TRACE_WIDTH = STANDARD_TRACE_WIDTH / 2.0; // need a list because a hash table doesn't guarantee order colorNames.append(tr("blue")); colorNames.append(tr("red")); colorNames.append(tr("black")); colorNames.append(tr("yellow")); colorNames.append(tr("green")); colorNames.append(tr("grey")); colorNames.append(tr("white")); colorNames.append(tr("orange")); colorNames.append(tr("ochre")); colorNames.append(tr("cyan")); colorNames.append(tr("brown")); colorNames.append(tr("purple")); colorNames.append(tr("pink")); // need this hash table to translate from user's language to internal color name colorTrans.insert(tr("blue"), "blue"); colorTrans.insert(tr("red"), "red"); colorTrans.insert(tr("black"), "black"); colorTrans.insert(tr("yellow"), "yellow"); colorTrans.insert(tr("green"), "green"); colorTrans.insert(tr("grey"), "grey"); colorTrans.insert(tr("white"), "white"); colorTrans.insert(tr("orange"), "orange"); colorTrans.insert(tr("ochre"), "ochre"); colorTrans.insert(tr("cyan"), "cyan"); colorTrans.insert(tr("brown"), "brown"); colorTrans.insert(tr("purple"), "purple"); colorTrans.insert(tr("pink"), "pink"); } bool Wire::hasFlag(ViewGeometry::WireFlag flag) { return m_viewGeometry.hasFlag(flag); } bool Wire::isTraceType(ViewGeometry::WireFlag flag) { return hasFlag(flag); } bool Wire::hasAnyFlag(ViewGeometry::WireFlags flags) { return m_viewGeometry.hasAnyFlag(flags); } Wire * Wire::findTraced(ViewGeometry::WireFlags flags, QList & ends) { QList chainedWires; this->collectChained(chainedWires, ends); if (ends.count() != 2) { DebugDialog::debug(QString("wire in jumper or trace must have two ends") ); return NULL; } return ConnectorItem::directlyWiredTo(ends[0], ends[1], flags); } void Wire::setWireFlags(ViewGeometry::WireFlags wireFlags) { m_viewGeometry.setWireFlags(wireFlags); } double Wire::opacity() { return m_opacity; } void Wire::setOpacity(double opacity) { m_opacity = opacity; this->update(); } bool Wire::draggingEnd() { return m_dragEnd || m_dragCurve; } void Wire::setCanChainMultiple(bool can) { m_canChainMultiple = can; } bool Wire::canChangeColor() { if (getRatsnest()) return false; if (!getTrace()) return true; return (this->m_viewID == ViewLayer::SchematicView); } void Wire::collectDirectWires(QList & wires) { bool firstRound = false; if (!wires.contains(this)) { wires.append(this); firstRound = true; } QList junctions; if (firstRound) { // collect up to any junction collectDirectWires(m_connector0, wires, junctions); collectDirectWires(m_connector1, wires, junctions); return; } // second round: deal with junctions foreach (Wire * wire, wires) { junctions << wire->connector0() << wire->connector1(); } int ix = 0; while (ix < junctions.count()) { ConnectorItem * junction = junctions.at(ix++); QSet jwires; foreach (ConnectorItem * toConnectorItem, junction->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) break; Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (!wires.contains(w)) jwires << w; bool onlyWiresConnected = true; foreach (ConnectorItem * toToConnectorItem, toConnectorItem->connectedToItems()) { if (toToConnectorItem->attachedToItemType() != ModelPart::Wire) { onlyWiresConnected = false; break; } w = qobject_cast(toToConnectorItem->attachedTo()); if (!wires.contains(w)) jwires << w; } if (!onlyWiresConnected) break; } if (jwires.count() == 1) { // there is a junction of > 2 wires and all wires leading to it except one are already on the delete list Wire * w = jwires.values().at(0); wires << w; w->collectDirectWires(w->connector0(), wires, junctions); w->collectDirectWires(w->connector1(), wires, junctions); } } } void Wire::collectDirectWires(ConnectorItem * connectorItem, QList & wires, QList & junctions) { if (connectorItem->connectionsCount() == 0) return; if (connectorItem->connectionsCount() > 1) { if (!junctions.contains(connectorItem)) junctions.append(connectorItem); return; } ConnectorItem * toConnectorItem = connectorItem->connectedToItems().at(0); if (toConnectorItem->attachedToItemType() != ModelPart::Wire) return; if (toConnectorItem->connectionsCount() != 1) { if (!junctions.contains(connectorItem)) junctions.append(connectorItem); return; } Wire * nextWire = qobject_cast(toConnectorItem->attachedTo()); if (wires.contains(nextWire)) return; wires.append(nextWire); nextWire->collectDirectWires(nextWire->otherConnector(toConnectorItem), wires, junctions); } QVariant Wire::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == ItemSelectedChange) { if (m_partLabel) { m_partLabel->update(); } if (!m_ignoreSelectionChange) { QList chained; QList ends; collectChained(chained, ends); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView) { infoGraphicsView->setIgnoreSelectionChangeEvents(true); } // DebugDialog::debug(QString("original wire selected %1 %2").arg(value.toBool()).arg(this->id())); foreach (Wire * wire, chained) { if (wire != this ) { wire->setIgnoreSelectionChange(true); wire->setSelected(value.toBool()); wire->setIgnoreSelectionChange(false); // DebugDialog::debug(QString("wire selected %1 %2").arg(value.toBool()).arg(wire->id())); } } if (infoGraphicsView) { infoGraphicsView->setIgnoreSelectionChangeEvents(false); } } } return ItemBase::itemChange(change, value); } void Wire::cleanup() { } void Wire::getConnectedColor(ConnectorItem * connectorItem, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) { connectorItem->setBigDot(false); ItemBase::getConnectedColor(connectorItem, brush, pen, opacity, negativePenWidth, negativeOffsetRect); int count = 0; bool bendpoint = true; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) { return; } foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (w->isTraceType(infoGraphicsView->getTraceFlag())) { count++; } } else { // for drawing a big dot on the end of a part connector in schematic view if the part is connected to more than one trace bendpoint = false; if (toConnectorItem->connectionsCount() > 1) { if (infoGraphicsView->hasBigDots()) { int c = 0; foreach (ConnectorItem * totoConnectorItem, toConnectorItem->connectedToItems()) { if (totoConnectorItem->attachedToItemType() == ModelPart::Wire) { Wire * w = qobject_cast(totoConnectorItem->attachedTo()); if (w && w->isTraceType(ViewGeometry::SchematicTraceFlag) && w->isTraceType(infoGraphicsView->getTraceFlag())) { c++; } } } if (c > 1) { count = 2; break; } } } count = 0; break; } } if (count == 0) { return; } // connectorItem is a bendpoint or connects to a multiply connected connector //if (!bendpoint) { //DebugDialog::debug(QString("big dot %1 %2 %3").arg(this->id()).arg(connectorItem->connectorSharedID()).arg(count)); //} brush = m_shadowBrush; opacity = 1.0; if (count > 1) { // only ever reach here when drawing a connector that is connected to more than one trace pen = m_bendpoint2Pen; negativePenWidth = m_bendpoint2Width; negativeOffsetRect = m_negativeOffsetRect; connectorItem->setBigDot(true); } else { negativeOffsetRect = m_negativeOffsetRect; negativePenWidth = m_bendpointWidth; pen = m_bendpointPen; } } void Wire::setPenWidth(double w, InfoGraphicsView * infoGraphicsView, double hoverStrokeWidth) { m_hoverStrokeWidth = hoverStrokeWidth; //DebugDialog::debug(QString("setting hoverstrokewidth %1 %2").arg(m_id).arg(m_hoverStrokeWidth)); m_pen.setWidthF(w); infoGraphicsView->getBendpointWidths(this, w, m_bendpointWidth, m_bendpoint2Width, m_negativeOffsetRect); m_bendpointPen.setWidthF(qAbs(m_bendpointWidth)); m_bendpoint2Pen.setWidthF(qAbs(m_bendpoint2Width)); m_shadowPen.setWidthF(w + 2); } bool Wire::connectionIsAllowed(ConnectorItem * to) { if (!ItemBase::connectionIsAllowed(to)) return false; Wire * w = qobject_cast(to->attachedTo()); if (w == NULL) return true; if (w->getRatsnest()) return false; return m_viewID != ViewLayer::BreadboardView; } bool Wire::isGrounded() { return ConnectorItem::isGrounded(connector0(), connector1()); } bool Wire::acceptsMouseDoubleClickConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return true; } bool Wire::acceptsMouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return true; } bool Wire::acceptsMouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { return true; } void Wire::setIgnoreSelectionChange(bool ignore) { m_ignoreSelectionChange = ignore; } bool Wire::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide) { if (prop.compare("width", Qt::CaseInsensitive) == 0) { // don't display width property hide = true; return false; } if (prop.compare("color", Qt::CaseInsensitive) == 0) { returnProp = tr("color"); if (canChangeColor()) { QComboBox * comboBox = new QComboBox(parent); comboBox->setEditable(false); comboBox->setEnabled(swappingEnabled); comboBox->setObjectName("infoViewComboBox"); int ix = 0; QString englishCurrColor = colorString(); foreach(QString transColorName, Wire::colorNames) { QString englishColorName = Wire::colorTrans.value(transColorName); bool ok = (this->m_viewID != ViewLayer::SchematicView || englishColorName.compare("white", Qt::CaseInsensitive) != 0); if (ok) { comboBox->addItem(transColorName, QVariant(englishColorName)); if (englishColorName.compare(englishCurrColor, Qt::CaseInsensitive) == 0) { comboBox->setCurrentIndex(ix); } ix++; } } connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(colorEntry(const QString &))); if (this->hasShadow()) { QCheckBox * checkBox = new QCheckBox(tr("Banded")); checkBox->setChecked(m_banded); checkBox->setObjectName("infoViewCheckBox"); connect(checkBox, SIGNAL(clicked(bool)), this, SLOT(setBandedProp(bool))); QFrame * frame = new QFrame(parent); QHBoxLayout * hboxLayout = new QHBoxLayout; hboxLayout->addWidget(comboBox); hboxLayout->addWidget(checkBox); frame->setLayout(hboxLayout); returnWidget = frame; } else { returnWidget = comboBox; } returnValue = comboBox->currentText(); return true; } else { returnWidget = NULL; returnValue = colorString(); return true; } } return ItemBase::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide); } void Wire::colorEntry(const QString & text) { Q_UNUSED(text); QComboBox * comboBox = qobject_cast(sender()); if (comboBox == NULL) return; QString color = comboBox->itemData(comboBox->currentIndex()).toString(); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->changeWireColor(color); } } bool Wire::hasPartLabel() { return false; } ItemBase::PluralType Wire::isPlural() { return Plural; } void Wire::checkVisibility(ConnectorItem * onMe, ConnectorItem * onIt, bool connect) { if (connect) { if (!onIt->attachedTo()->isVisible()) { this->setVisible(false); } else { ConnectorItem * other = otherConnector(onMe); foreach (ConnectorItem * toConnectorItem, other->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) continue; if (!toConnectorItem->attachedTo()->isVisible()) { this->setVisible(false); break; } } } } } bool Wire::canSwitchLayers() { return false; } bool Wire::hasPartNumberProperty() { return false; } bool Wire::rotationAllowed() { return false; } bool Wire::rotation45Allowed() { return false; } void Wire::addedToScene(bool temporary) { ItemBase::addedToScene(temporary); InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView == NULL) return; infoGraphicsView->newWire(this); } void Wire::setConnectorDimensions(double width, double height) { setConnectorDimensionsAux(connector0(), width, height); setConnectorDimensionsAux(connector1(), width, height); } void Wire::setConnectorDimensionsAux(ConnectorItem * connectorItem, double width, double height) { QPointF p = connectorItem->rect().center(); QRectF r(p.x() - (width / 2), p.y() - (height / 2), width, height); connectorItem->setRect(r); connectorItem->setTerminalPoint(r.center() - r.topLeft()); //debugCompare(connectorItem->attachedTo()); } void Wire::originalConnectorDimensions(double & width, double & height) { width = m_originalConnectorRect.width(); height = m_originalConnectorRect.height(); } bool Wire::isBendpoint(ConnectorItem * connectorItem) { return connectorItem->isBendpoint(); } double Wire::hoverStrokeWidth() { return m_hoverStrokeWidth; } const QLineF & Wire::getPaintLine() { return m_line; } /*! Returns the item's line, or a null line if no line has been set. \sa setLine() */ QLineF Wire::line() const { return m_line; } /*! Sets the item's line to be the given \a line. \sa line() */ void Wire::setLine(const QLineF &line) { //if (line.length() < 0.5) { // debugInfo("zero line"); //} if (m_line == line) return; prepareGeometryChange(); m_line = line; update(); } void Wire::setLine(double x1, double y1, double x2, double y2) { setLine(QLineF(x1, y1, x2, y2)); } /*! Returns the item's pen, or a black solid 0-width pen if no pen has been set. \sa setPen() */ QPen Wire::pen() const { return m_pen; } /*! Sets the item's pen to \a pen. If no pen is set, the line will be painted using a black solid 0-width pen. \sa pen() */ void Wire::setPen(const QPen &pen) { if (pen.widthF() != m_pen.widthF()) { prepareGeometryChange(); } m_pen = pen; update(); } bool Wire::canHaveCurve() { return m_canHaveCurve && !getRatsnest(); } void Wire::dragCurve(QPointF eventPos, Qt::KeyboardModifiers) { m_bezier->recalc(eventPos); } void Wire::changeCurve(const Bezier * bezier) { prepareGeometryChange(); if (m_bezier == NULL) m_bezier = new Bezier; m_bezier->copy(bezier); update(); } bool Wire::isCurved() { return (m_bezier != NULL) && !m_bezier->isEmpty(); } const Bezier * Wire::curve() { return m_bezier; } const Bezier * Wire::undoCurve() { return &UndoBezier; } QPolygonF Wire::sceneCurve(QPointF offset) { QPolygonF poly; if (m_bezier == NULL) return poly; if (m_bezier->isEmpty()) return poly; poly.append(m_line.p1() + pos() - offset); poly.append(m_bezier->cp0() + pos() - offset); poly.append(m_bezier->cp1() + pos() - offset); poly.append(m_line.p2() + pos() - offset); return poly; } bool Wire::hasShadow() { if (getRatsnest()) return false; if (getTrace()) return false; return m_pen.widthF() != m_shadowPen.widthF(); } void Wire::cursorKeyEvent(Qt::KeyboardModifiers modifiers) { if (m_dragEnd || m_dragCurve) return; InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);; if (infoGraphicsView) { QPoint p = infoGraphicsView->mapFromGlobal(QCursor::pos()); QPointF r = infoGraphicsView->mapToScene(p); // DebugDialog::debug(QString("got key event %1").arg(keyEvent->modifiers())); updateCursor(modifiers); } } void Wire::updateCursor(Qt::KeyboardModifiers modifiers) { if (m_connectorHover) { return; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); bool segment = false; int totalConnections = 0; foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { totalConnections += connectorItem->connectionsCount(); } if (totalConnections == 2 && modifiers & altOrMetaModifier()) { segment = true; foreach (ConnectorItem * connectorItem, cachedConnectorItems()) { if (connectorItem->connectionsCount() != 1) { segment = false; break; } ConnectorItem * toConnectorItem = connectorItem->connectedToItems().at(0); if (toConnectorItem->attachedToItemType() != ModelPart::Wire) { segment = false; break; } } } if (segment) { // dragging a segment of wire between bounded by two other wires CursorMaster::instance()->addCursor(this, *CursorMaster::RubberbandCursor); } else if (totalConnections == 0) { // only in breadboard view CursorMaster::instance()->addCursor(this, *CursorMaster::MoveCursor); } else if (infoGraphicsView != NULL && infoGraphicsView->curvyWiresIndicated(modifiers)) { CursorMaster::instance()->addCursor(this, *CursorMaster::MakeCurveCursor); } else if (m_displayBendpointCursor) { CursorMaster::instance()->addCursor(this, *CursorMaster::NewBendpointCursor); } } bool Wire::canChainMultiple() { return m_canChainMultiple; } ViewLayer::ViewID Wire::useViewIDForPixmap(ViewLayer::ViewID vid, bool) { if (vid == ViewLayer::BreadboardView) { return ViewLayer::IconView; } return ViewLayer::UnknownView; } void Wire::setDisplayBendpointCursor(bool dbc) { m_displayBendpointCursor = dbc; } bool Wire::banded() { return m_banded; } void Wire::setBanded(bool banded) { m_banded = banded; QList chained; QList ends; collectChained(chained, ends); foreach (Wire * w, chained) { w->m_banded = banded; w->update(); } } void Wire::setBandedProp(bool banded) { InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL) { infoGraphicsView->setProp(this, "banded", ItemBase::TranslatedPropertyNames.value("banded"), m_banded ? "Yes" : "No", banded ? "Yes" : "No", true); } } void Wire::setProp(const QString & prop, const QString & value) { if (prop.compare("banded", Qt::CaseInsensitive) == 0) { setBanded(value == "Yes"); return; } ItemBase::setProp(prop, value); } fritzing-app-0.9.3b/src/items/wire.h000066400000000000000000000235571272363601300173450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 7000 $: $Author: irascibl@gmail.com $: $Date: 2013-04-29 07:24:08 +0200 (Mo, 29. Apr 2013) $ ********************************************************************/ #ifndef WIRE_H #define WIRE_H #include #include #include #include #include #include #include #include #include #include "itembase.h" #include "../utils/cursormaster.h" class WireAction : public QAction { Q_OBJECT public: WireAction(QAction *); WireAction(const QString & text, QObject * parent); void setWire(Wire *); Wire * wire(); protected: Wire * m_wire; }; class Wire : public ItemBase, public CursorKeyListener { Q_OBJECT public: Wire(class ModelPart *, ViewLayer::ViewID, const ViewGeometry &, long id, QMenu * itemMenu, bool initLabel); virtual ~Wire(); // for debugging //void setPos(const QPointF & pos); // for debugging QLineF line() const; virtual void setLine(const QLineF &line); inline void setLine(double x1, double y1, double x2, double y2); QPen pen() const; void setPen(const QPen &pen); void saveGeometry(); bool itemMoved(); void saveInstanceLocation(QXmlStreamWriter &); void writeGeometry(QXmlStreamWriter &); void moveItem(ViewGeometry & viewGeometry); void hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, class ConnectorItem * item); void hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, class ConnectorItem * item); void initDragEnd(ConnectorItem * dragEnd, QPointF scenePos); void initDragCurve(QPointF scenePos); bool initNewBendpoint(QPointF scenePos, class Bezier & left, class Bezier & right); void connectedMoved(ConnectorItem * from, ConnectorItem * to, QList & already); void setLineAnd(QLineF line, QPointF pos, bool useLine); ConnectorItem * otherConnector(ConnectorItem *); ConnectorItem * connector0(); ConnectorItem * connector1(); virtual class FSvgRenderer * setUp(ViewLayer::ViewLayerID viewLayerID, const LayerHash & viewLayers, class InfoGraphicsView *); void findConnectorsUnder(); void collectChained(QList &, QList & ends); void collectWires(QList & wires); bool stickyEnabled(); void setPcbPenBrush(QBrush & brush); bool getRouted(); void setRouted(bool); void setRatsnest(bool); void setAutoroutable(bool); bool getAutoroutable(); void setNormal(bool); bool getNormal(); bool getTrace(); bool isTraceType(ViewGeometry::WireFlag); bool hasFlag(ViewGeometry::WireFlag); bool hasAnyFlag(ViewGeometry::WireFlags); void setWireFlags(ViewGeometry::WireFlags); QString colorString(); QString hexString(); QString shadowHexString(); void setColorString(QString, double opacity, bool restore); virtual void setColor(const QColor &, double opacity); double opacity(); void setOpacity(double opacity); const QColor & color(); void setWireWidth(double width, InfoGraphicsView *, double hoverStrokeWidth); void setPenWidth(double width, InfoGraphicsView *, double hoverStrokeWidth); double width(); double shadowWidth(); double mils(); void setExtras(QDomElement &, InfoGraphicsView *); Wire * findTraced(ViewGeometry::WireFlags flags, QList & ends); bool draggingEnd(); void simpleConnectedMoved(ConnectorItem * to); void simpleConnectedMoved(ConnectorItem * from, ConnectorItem * to); void setCanChainMultiple(bool); bool canChangeColor(); void collectDirectWires(QList & wires); bool isGrounded(); bool collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide); bool hasPartLabel(); PluralType isPlural(); virtual bool canSwitchLayers(); void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ); void paintBody(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); bool hasPartNumberProperty(); bool rotationAllowed(); bool rotation45Allowed(); void addedToScene(bool temporary); void setConnectorDimensions(double width, double height); void originalConnectorDimensions(double & width, double & height); double hoverStrokeWidth(); QPainterPath hoverShape() const; QPainterPath shape() const; QRectF boundingRect() const; virtual const QLineF & getPaintLine(); bool canHaveCurve(); void changeCurve(const class Bezier *); bool isCurved(); const class Bezier * curve(); const class Bezier * undoCurve(); QPolygonF sceneCurve(QPointF offset); bool hasShadow(); bool canChainMultiple(); void cursorKeyEvent(Qt::KeyboardModifiers modifiers); void setDisplayBendpointCursor(bool); bool banded(); void setBanded(bool); void setProp(const QString & prop, const QString & value); void setConnector0Rect(); void setConnector1Rect(); QRectF connector0Rect(const QLineF & line); QRectF connector1Rect(const QLineF & line); protected slots: void colorEntry(const QString & text); void setBandedProp(bool); public: static double STANDARD_TRACE_WIDTH; static double HALF_STANDARD_TRACE_WIDTH; static double THIN_TRACE_WIDTH; public: static void initNames(); static void cleanup(); static QVector TheDash; protected: void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEventAux(QPointF eventPos, Qt::KeyboardModifiers); void dragCurve(QPointF eventPos, Qt::KeyboardModifiers); void mousePressEvent(QGraphicsSceneMouseEvent *event); void paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void initEnds(const ViewGeometry &, QRectF defaultRect, class InfoGraphicsView *); void connectionChange(ConnectorItem * onMe, ConnectorItem * onIt, bool connect); void mousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); void mouseDoubleClickConnectorEvent(ConnectorItem *); void mouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); void mouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); bool acceptsMouseDoubleClickConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); bool acceptsMouseMoveConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); bool acceptsMouseReleaseConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual class FSvgRenderer * setUpConnectors(class ModelPart *, ViewLayer::ViewID); void collectChained(ConnectorItem * connectorItem, QList & chained, QList & ends); void collectWiresAux(QList & wires, ConnectorItem * start); void setShadowColor(QColor &, bool restore); void calcNewLine(ConnectorItem * from, ConnectorItem * to, QPointF & p1, QPointF & p2); void collectDirectWires(ConnectorItem * connectorItem, QList & wires, QList & junctions); QVariant itemChange(GraphicsItemChange change, const QVariant &value); void getConnectedColor(ConnectorItem *, QBrush &, QPen &, double & opacity, double & negativePenWidth, bool & negativeOffsetRect); bool connectionIsAllowed(ConnectorItem *); bool releaseDrag(); void setIgnoreSelectionChange(bool); virtual void setColorFromElement(QDomElement & element); void checkVisibility(ConnectorItem * onMe, ConnectorItem * onIt, bool connect); void setConnectorDimensionsAux(ConnectorItem *, double width, double height); bool isBendpoint(ConnectorItem * connectorItem); QPainterPath shapeAux(double width) const; void hoverLeaveEvent( QGraphicsSceneHoverEvent * event ); void hoverEnterEvent( QGraphicsSceneHoverEvent * event ); void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); void updateCursor(Qt::KeyboardModifiers); ViewLayer::ViewID useViewIDForPixmap(ViewLayer::ViewID, bool swappingEnabled); protected: QLineF m_line; QPen m_pen; QPointF m_wireDragOrigin; bool m_dragEnd; bool m_dragCurve; bool m_drag0; QPointer m_connectorHover; QPointer m_connector0; QPointer m_connector1; QString m_colorName; QPen m_shadowPen; QBrush m_shadowBrush; QPen m_bendpointPen; QPen m_bendpoint2Pen; double m_bendpointWidth; double m_bendpoint2Width; bool m_negativeOffsetRect; double m_opacity; bool m_canChainMultiple; bool m_ignoreSelectionChange; bool m_markedDeleted; QRectF m_originalConnectorRect; double m_hoverStrokeWidth; bool m_canHaveCurve; class Bezier * m_bezier; bool m_displayBendpointCursor; bool m_banded; public: static QStringList colorNames; static QHash colorTrans; static QHash widthTrans; static QList widths; signals: void wireChangedSignal(Wire* me, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, ConnectorItem * from, ConnectorItem * to); void wireChangedCurveSignal(Wire* me, const Bezier * oldB, const Bezier * newB, bool triggerFirstTime); void wireSplitSignal(Wire* me, QPointF newPos, QPointF oldPos, const QLineF & oldLine); void wireJoinSignal(Wire* me, ConnectorItem * clickedConnectorItem); }; #endif fritzing-app-0.9.3b/src/layerattributes.cpp000066400000000000000000000027711272363601300210270ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6926 $: $Author: irascibl@gmail.com $: $Date: 2013-03-10 21:27:34 +0100 (So, 10. Mrz 2013) $ ********************************************************************/ #include "layerattributes.h" #include "debugdialog.h" LayerAttributes::LayerAttributes() { orientation = Qt::Vertical; createShape = true; } const QString & LayerAttributes::filename() { return m_filename; } void LayerAttributes::setFilename(const QString & filename) { m_filename = filename; } const QByteArray & LayerAttributes::loaded() const { return m_loaded; } void LayerAttributes::clearLoaded() { m_loaded.clear(); } void LayerAttributes::setLoaded(const QByteArray & loaded) { m_loaded = loaded; } fritzing-app-0.9.3b/src/layerattributes.h000066400000000000000000000031441272363601300204670ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #ifndef LAYERATTRIBUTES_H #define LAYERATTRIBUTES_H #include #include #include "viewlayer.h" class LayerAttributes { public: LayerAttributes(); const QString & filename(); void setFilename(const QString &); const QByteArray & loaded() const; void clearLoaded(); void setLoaded(const QByteArray &); protected: QString m_filename; QByteArray m_loaded; public: QString error; ViewLayer::ViewID viewID; ViewLayer::ViewLayerID viewLayerID; ViewLayer::ViewLayerPlacement viewLayerPlacement; bool doConnectors; Qt::Orientations orientation; bool createShape; }; #endif fritzing-app-0.9.3b/src/lib/000077500000000000000000000000001272363601300156375ustar00rootroot00000000000000fritzing-app-0.9.3b/src/lib/qtsysteminfo/000077500000000000000000000000001272363601300204045ustar00rootroot00000000000000fritzing-app-0.9.3b/src/lib/qtsysteminfo/QtSystemInfo.cpp000066400000000000000000000337211272363601300235230ustar00rootroot00000000000000/* * File: QtSystemInfo.cpp * Author: Adrian Łubik (adrian5632@gmail.com) * License: GNU LGPL v2 or newer * Copyright: (c) 2010 Adrian Łubik */ #include "QtSystemInfo.h" #include static const char *UNKNOWN = "UNKNOWN"; class QtSystemInfoPrivate { public: QtSystemInfoPrivate() {} virtual ~QtSystemInfoPrivate() {} virtual QtSystemInfo::ArchitectureType architecture() = 0; virtual QtSystemInfo::SystemType systemType() = 0; virtual QString architectureName() = 0; virtual QString kernelName() = 0; virtual QString kernelVersion() = 0; virtual QString systemName() = 0; virtual QString systemVersion() = 0; }; // -------------------------------------------------- // // Any Unix // // -------------------------------------------------- // #ifdef Q_OS_UNIX #include class QtSystemInfoUnix: public QtSystemInfoPrivate { public: QtSystemInfoUnix():m_kernelName(UNKNOWN), m_kernelVersion(UNKNOWN), m_architectureName(UNKNOWN), m_architecture(QtSystemInfo::UnknownArchitectureType) { struct utsname buffer; if (uname(&buffer) == 0) { m_kernelVersion = buffer.release; m_kernelName = buffer.sysname; m_architectureName = buffer.machine; if (m_architectureName == "i386") m_architecture = QtSystemInfo::I386; else if (m_architectureName == "i486") m_architecture = QtSystemInfo::I486; else if (m_architectureName == "i586") m_architecture = QtSystemInfo::I586; else if (m_architectureName == "i686") m_architecture = QtSystemInfo::I686; else if (m_architectureName == "x86_64") m_architecture = QtSystemInfo::X86_64; else if (m_architectureName == "ppc") m_architecture = QtSystemInfo::MPPC; else if (m_architectureName == "ppc64") m_architecture = QtSystemInfo::MPPC64; } } virtual QString architectureName() { return m_architectureName; } virtual QtSystemInfo::ArchitectureType architecture() { return m_architecture; } virtual QString kernelName() { return m_kernelName; } virtual QString kernelVersion() { return m_kernelVersion; } virtual QtSystemInfo::SystemType systemType() { return QtSystemInfo::UnknownUnix; } virtual QString systemName() { return "Unix"; } virtual QString systemVersion() { return UNKNOWN; } private: QString m_kernelName; QString m_kernelVersion; QString m_architectureName; QtSystemInfo::ArchitectureType m_architecture; }; #endif // -------------------------------------------------- // // Mac OS X // // -------------------------------------------------- // #ifdef Q_OS_MAC #include class QtSystemInfoMac: public QtSystemInfoUnix { public: QtSystemInfoMac() {} virtual QString systemName() { return "Mac OS X"; } virtual QString systemVersion() { switch (QSysInfo::MacintoshVersion) { case QSysInfo::MV_10_0: return "10.0"; case QSysInfo::MV_10_1: return "10.1"; case QSysInfo::MV_10_2: return "10.2"; case QSysInfo::MV_10_3: return "10.3"; case QSysInfo::MV_10_4: return "10.4"; case QSysInfo::MV_10_5: return "10.5"; case QSysInfo::MV_10_6: return "10.6"; default: return UNKNOWN; } } virtual QtSystemInfo::SystemType systemType() { return QtSystemInfo::MacOSX; } }; #endif // -------------------------------------------------- // // Linux // // -------------------------------------------------- // #ifdef Q_OS_LINUX #include #include static bool scanSuSEReleaseFile(QString &name, QString &version) { name.clear(); version.clear(); QFile releaseFile("/etc/SuSE-release"); if (releaseFile.exists()) { name = "SuSE"; if (releaseFile.open(QFile::ReadOnly)) { while (!releaseFile.atEnd()) { QString line = releaseFile.readLine(500).trimmed(); if (line.startsWith("VERSION = ")) { version = line.remove(0, 10); releaseFile.close(); return true; } } releaseFile.close(); } } return false; } static bool scanRedHatReleaseFile(QString &name, QString &version) { name.clear(); version.clear(); QFile releaseFile("/etc/redhat-release"); if (releaseFile.exists() && releaseFile.open(QFile::ReadOnly)) { QRegExp fedoraRegExp("(^Fedora)\\s+[release]*\\s+([0-9]{1,2})\\s+" ); QRegExp pclinuxosRegExp("(^PCLinuxOS)\\s+release\\s+([0-9]{4})\\s+\\(PCLinuxOS\\)\\s+for"); QString line = releaseFile.readLine(500); releaseFile.close(); if (line.contains(fedoraRegExp)) { name = fedoraRegExp.cap(1); version = fedoraRegExp.cap(2); return true; } else if (line.contains(pclinuxosRegExp)) { name = pclinuxosRegExp.cap(1); version = pclinuxosRegExp.cap(2); return true; } } return false; } static bool scanLSBReleaseFile(QString &name, QString &version) { name.clear(); version.clear(); QFile releaseFile("/etc/lsb-release"); if (releaseFile.exists() && releaseFile.open(QFile::ReadOnly)) { while (!releaseFile.atEnd()) { QString line = releaseFile.readLine(500).trimmed(); if (line.startsWith("DISTRIB_ID=") && line.count() > 11) { name = line.mid(11); if (!name.isEmpty() && !version.isEmpty()) { releaseFile.close(); return true; } } else if (line.startsWith("DISTRIB_RELEASE=") && line.count() > 16) { version = line.mid(16); if (!name.isEmpty() && !version.isEmpty()) { releaseFile.close(); return true; } } } } return false; } static bool scanDebianReleaseFile(QString &name, QString &version) { name.clear(); version.clear(); QFile releaseFile("/etc/debian_version"); if (releaseFile.exists() && releaseFile.open(QFile::ReadOnly)) { QRegExp debianRegExp("(^[0-9]{1}\\.[0-9]\\s*$)"); QString line = releaseFile.readLine(100); releaseFile.close(); if (line.startsWith("lenny/sid")) { name = "Debian"; version = "4.0"; return true; } else if (line.contains(debianRegExp)) { name = "Debian"; version = debianRegExp.cap(1); return true; } } return false; } class QtSystemInfoLinux: public QtSystemInfoUnix { public: QtSystemInfoLinux() { if (scanSuSEReleaseFile(m_distributionName, m_distributionVersion)) return; if (scanRedHatReleaseFile(m_distributionName, m_distributionVersion)) return; if (scanLSBReleaseFile(m_distributionName, m_distributionVersion)) return; if (scanDebianReleaseFile(m_distributionName, m_distributionVersion)) return; m_distributionName = "Linux"; m_distributionVersion = UNKNOWN; } virtual QtSystemInfo::SystemType systemType() { return QtSystemInfo::Linux; } virtual QString systemName() { return m_distributionName; } virtual QString systemVersion() { return m_distributionVersion; } private: QString m_distributionName; QString m_distributionVersion; }; #endif // -------------------------------------------------- // // BSD // // -------------------------------------------------- // #if defined(Q_OS_BSD4) && !defined(Q_OS_DARWIN) #include #include static QString getFreeBSDDistro() { if (QFile::exists("/usr/PCBSD")) return "PCBSD"; return "FreeBSD"; } class QtSystemInfoBSD: public QtSystemInfoUnix { public: QtSystemInfoBSD() {} virtual QtSystemInfo::SystemType systemType() { # if defined(Q_OS_OPENBSD) return QtSystemInfo::OpenBSD; # elif defined(Q_OS_NETBSD) return QtSystemInfo::NetBSD; # elif defined(Q_OS_FREEBSD) return QtSystemInfo::FreeBSD; # else return QtSystemInfo::UnknownBSD; # endif } virtual QString systemName() { # if defined(Q_OS_OPENBSD) return "OpenBSD"; # elif defined(Q_OS_NETBSD) return "NetBSD"; # elif defined(Q_OS_FREEBSD) return getFreeBSDDistro(); # else return "BSD4"; # endif } virtual QString systemVersion() { QString version = kernelVersion(); QRegExp versionRx("^(\\d+\\.\\d+(\\.\\d+)?)"); if (version.contains(versionRx)) version = versionRx.cap(1); return version; } }; #endif // -------------------------------------------------- // // Windows // // -------------------------------------------------- // #ifdef Q_OS_WIN #include class QtSystemInfoWindows: public QtSystemInfoPrivate { public: QtSystemInfoWindows() {} virtual QtSystemInfo::SystemType systemType() { # ifdef Q_OS_WINCE return QtSystemInfo::WindowsCE; # else return QtSystemInfo::Windows; # endif } virtual QtSystemInfo::ArchitectureType architecture() { # if defined(Q_OS_WINCE) return QtSystemInfo::UnknownArchitectureType; # elif defined(Q_OS_WIN64) return QtSystemInfo::X86_64; # else return QtSystemInfo::I386; # endif } virtual QString architectureName() { switch (architecture()) { case QtSystemInfo::X86_64: return "x86_64"; case QtSystemInfo::I386: return "i386"; default: return UNKNOWN; } } virtual QString kernelName() { if (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) return "NT"; else if (QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) return "CE"; return UNKNOWN; } virtual QString kernelVersion() { switch (QSysInfo::WindowsVersion) { case QSysInfo::WV_4_0: return "4.0"; case QSysInfo::WV_5_0: return "5.0"; case QSysInfo::WV_5_1: return "5.1"; case QSysInfo::WV_5_2: return "5.2"; case QSysInfo::WV_6_0: return "6.0"; case QSysInfo::WV_6_1: return "6.1"; case QSysInfo::WV_CE: return "1.0"; case QSysInfo::WV_CENET: return ".NET"; case QSysInfo::WV_CE_5: return "5.x"; case QSysInfo::WV_CE_6: return "6.x"; default: return UNKNOWN; } } virtual QString systemName() { # ifdef Q_OS_WINCE return "Windows CE"; # else return "Windows"; # endif } virtual QString systemVersion() { switch (QSysInfo::WindowsVersion) { case QSysInfo::WV_NT: return "NT"; case QSysInfo::WV_2000: return "2000"; case QSysInfo::WV_XP: return "XP"; case QSysInfo::WV_2003: # ifdef Q_OS_WIN64 return "XP Proffessional 64"; # else return "2003 Server"; # endif case QSysInfo::WV_VISTA: return "Vista / Server 2008"; case QSysInfo::WV_WINDOWS7: return "7 / Server 2008 R2"; default: return UNKNOWN; } } }; #endif // -------------------------------------------------- // // Main class // // -------------------------------------------------- // QtSystemInfo::QtSystemInfo(QObject *parent): QObject(parent), #if defined(Q_OS_UNIX) # if defined(Q_OS_MAC) d(new QtSystemInfoMac()) # elif defined(Q_OS_LINUX) d(new QtSystemInfoLinux()) # elif defined(Q_OS_BSD4) d(new QtSystemInfoBSD) # else d(new QtSystemInfoUnix()) # endif #elif defined(Q_OS_WIN) d(new QtSystemInfoWindows()) #endif { } QtSystemInfo::~QtSystemInfo() { delete d; } QString QtSystemInfo::architectureName() const { return d->architectureName(); } QtSystemInfo::ArchitectureType QtSystemInfo::architecture() const { return d->architecture(); } QString QtSystemInfo::kernelVersion() const { return d->kernelVersion(); } QString QtSystemInfo::kernelName() const { return d->kernelName(); } QString QtSystemInfo::systemName() const { return d->systemName(); } QString QtSystemInfo::systemVersion() const { return d->systemVersion(); } QtSystemInfo::SystemType QtSystemInfo::systemType() const { return d->systemType(); } QString QtSystemInfo::getSystemInformation(QString format) { if (format.isEmpty()) format = "%SYS_NAME %SYS_VERSION (%KERN_NAME %KERN_VERSION %ARCH)."; format.replace("%SYS_NAME", systemName(), Qt::CaseInsensitive); format.replace("%SYS_VERSION", systemVersion(), Qt::CaseInsensitive); format.replace("%SYS_RELEASE", systemVersion(), Qt::CaseInsensitive); format.replace("%KERN_NAME", kernelName(), Qt::CaseInsensitive); format.replace("%KERN_RELEASE", kernelVersion(), Qt::CaseInsensitive); format.replace("%KERN_VERSION", kernelVersion(), Qt::CaseInsensitive); format.replace("%ARCH", architectureName(), Qt::CaseInsensitive); return format; } fritzing-app-0.9.3b/src/lib/qtsysteminfo/QtSystemInfo.h000066400000000000000000000043201272363601300231610ustar00rootroot00000000000000/* * File: QtSystemInfo.h * Author: Adrian Łubik (adrian5632@gmail.com) * License: GNU LGPL v2 or newer * Copyright: (c) 2010 Adrian Łubik */ #ifndef QTSYSTEMINFO_H #define QTSYSTEMINFO_H #define QTSYSTEMINFO_VERSION 0x009000 #include #include class QtSystemInfoPrivate; class QtSystemInfo: public QObject { public: enum SystemType { UnknownType = -1, UnknownUnix, MacOSX, Windows, WindowsCE, Linux, UnknownBSD, FreeBSD, OpenBSD, NetBSD }; enum ArchitectureType { UnknownArchitectureType = -1, I386, I486, I586, I686, X86_64, MPPC, MPPC64 }; QtSystemInfo(QObject *parent = 0); ~QtSystemInfo(); /** * @return Name of currently running system's kernel. */ QString kernelName() const; /** * @return Version of currently running system's kernel. */ QString kernelVersion() const; /** * @return Architecture of the running system. */ QString architectureName() const; /** * @return Type of currently running system's architecture. */ ArchitectureType architecture() const; /** * @return Type of currently running system. */ SystemType systemType() const; /** * @return Name of currently running system. */ QString systemName() const; /** * @return Version of currently running system. */ QString systemVersion() const; /** * @param format A format of the information to be returned. * The following strings will be replaced: * %SYS_NAME - replaced with system name. * %SYS_VERSION - replaced with system version. * %KERN_NAME - replaced with kernel name. * %KERN_VERSION - replaced with kernel version. * %ARCH - replaced with architecture. * If no format given, the method will default to: %SYS_NAME %SYS_VERSION (%KERN_NAME %KERN_VERSION %ARCH). * * @return Formatted information about the running system. */ QString getSystemInformation(QString format = QString()); private: QtSystemInfoPrivate *d; }; #endif fritzing-app-0.9.3b/src/lib/quazip/000077500000000000000000000000001272363601300171505ustar00rootroot00000000000000fritzing-app-0.9.3b/src/lib/quazip/crypt.h000066400000000000000000000113031272363601300204600ustar00rootroot00000000000000/* crypt.h -- base code for crypt/uncrypt ZIPfile Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This code is a modified version of crypting code in Infozip distribution The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). If you don't need crypting in your application, just define symbols NOCRYPT and NOUNCRYPT. This code support the "Traditional PKWARE Encryption". The new AES encryption added on Zip format by Winzip (see the page http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong Encryption is not supported. */ #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) /*********************************************************************** * Return the next byte in the pseudo-random sequence */ static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) { #ifndef _WINDOWS (void) pcrc_32_tab; /* avoid "unused parameter" warning */ #endif unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an * unpredictable manner on 16-bit systems; not a problem * with any known compiler so far, though */ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); } /*********************************************************************** * Update the encryption keys with the next byte of plain text */ static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) { (*(pkeys+0)) = CRC32((*(pkeys+0)), c); (*(pkeys+1)) += (*(pkeys+0)) & 0xff; (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; { register int keyshift = (int)((*(pkeys+1)) >> 24); (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); } return c; } /*********************************************************************** * Initialize the encryption keys and the random header according to * the given password. */ static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) { *(pkeys+0) = 305419896L; *(pkeys+1) = 591751049L; *(pkeys+2) = 878082192L; while (*passwd != '\0') { update_keys(pkeys,pcrc_32_tab,(int)*passwd); passwd++; } } #define zdecode(pkeys,pcrc_32_tab,c) \ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) #define zencode(pkeys,pcrc_32_tab,c,t) \ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED #define RAND_HEAD_LEN 12 /* "last resort" source for second part of crypt seed pattern */ # ifndef ZCR_SEED2 # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ # endif static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) const char *passwd; /* password string */ unsigned char *buf; /* where to write header */ int bufSize; unsigned long* pkeys; const unsigned long* pcrc_32_tab; unsigned long crcForCrypting; { int n; /* index in random header */ int t; /* temporary */ int c; /* random byte */ unsigned char header[RAND_HEAD_LEN-2]; /* random header */ static unsigned calls = 0; /* ensure different random header each time */ if (bufSize> 7) & 0xff; header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); } /* Encrypt random header (last two bytes is high word of crc) */ init_keys(passwd, pkeys, pcrc_32_tab); for (n = 0; n < RAND_HEAD_LEN-2; n++) { buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); } buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); return n; } #endif fritzing-app-0.9.3b/src/lib/quazip/ioapi.c000066400000000000000000000102771272363601300204240ustar00rootroot00000000000000/* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant */ #include #include #include #include #include "ioapi.h" /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif voidpf ZCALLBACK fopen_file_func OF(( voidpf opaque, const char* filename, int mode)); uLong ZCALLBACK fread_file_func OF(( voidpf opaque, voidpf stream, void* buf, uLong size)); uLong ZCALLBACK fwrite_file_func OF(( voidpf opaque, voidpf stream, const void* buf, uLong size)); long ZCALLBACK ftell_file_func OF(( voidpf opaque, voidpf stream)); long ZCALLBACK fseek_file_func OF(( voidpf opaque, voidpf stream, uLong offset, int origin)); int ZCALLBACK fclose_file_func OF(( voidpf opaque, voidpf stream)); int ZCALLBACK ferror_file_func OF(( voidpf opaque, voidpf stream)); voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) voidpf opaque; const char* filename; int mode; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif FILE* file = NULL; const char* mode_fopen = NULL; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename!=NULL) && (mode_fopen != NULL)) file = fopen(filename, mode_fopen); return file; } uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) voidpf opaque; voidpf stream; void* buf; uLong size; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif uLong ret; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) voidpf opaque; voidpf stream; const void* buf; uLong size; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif uLong ret; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } long ZCALLBACK ftell_file_func (opaque, stream) voidpf opaque; voidpf stream; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif long ret; ret = ftell((FILE *)stream); return ret; } long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) voidpf opaque; voidpf stream; uLong offset; int origin; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif int fseek_origin=0; long ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : fseek_origin = SEEK_CUR; break; case ZLIB_FILEFUNC_SEEK_END : fseek_origin = SEEK_END; break; case ZLIB_FILEFUNC_SEEK_SET : fseek_origin = SEEK_SET; break; default: return -1; } ret = 0; fseek((FILE *)stream, offset, fseek_origin); return ret; } int ZCALLBACK fclose_file_func (opaque, stream) voidpf opaque; voidpf stream; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif int ret; ret = fclose((FILE *)stream); return ret; } int ZCALLBACK ferror_file_func (opaque, stream) voidpf opaque; voidpf stream; { #ifndef _WINDOWS (void) opaque; /* avoid "unused parameter" warning */ #endif int ret; ret = ferror((FILE *)stream); return ret; } void fill_fopen_filefunc (pzlib_filefunc_def) zlib_filefunc_def* pzlib_filefunc_def; { pzlib_filefunc_def->zopen_file = fopen_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell_file = ftell_file_func; pzlib_filefunc_def->zseek_file = fseek_file_func; pzlib_filefunc_def->zclose_file = fclose_file_func; pzlib_filefunc_def->zerror_file = ferror_file_func; pzlib_filefunc_def->opaque = NULL; } fritzing-app-0.9.3b/src/lib/quazip/ioapi.h000066400000000000000000000050271272363601300204260ustar00rootroot00000000000000/* ioapi.h -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant */ #ifndef _ZLIBIOAPI_H #define _ZLIBIOAPI_H #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #ifndef ZCALLBACK #if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif #ifdef __cplusplus extern "C" { #endif #ifdef _Z_OF #undef OF #define OF _Z_OF #endif typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); #define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) #define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) #define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) #define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) #define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) #ifdef __cplusplus } #endif #endif fritzing-app-0.9.3b/src/lib/quazip/quazip.cpp000066400000000000000000000202171272363601300211670ustar00rootroot00000000000000/* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2007 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. **/ #include #include "quazip.h" QuaZip::QuaZip(): fileNameCodec(QTextCodec::codecForLocale()), commentCodec(QTextCodec::codecForLocale()), mode(mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK) { } QuaZip::QuaZip(const QString& zipName): fileNameCodec(QTextCodec::codecForLocale()), commentCodec(QTextCodec::codecForLocale()), zipName(zipName), mode(mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK) { } QuaZip::~QuaZip() { if(isOpen()) close(); } bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi) { zipError=UNZ_OK; if(isOpen()) { qWarning("QuaZip::open(): ZIP already opened"); return false; } switch(mode) { case mdUnzip: unzFile_f=unzOpen2(QFile::encodeName(zipName).constData(), ioApi); if(unzFile_f!=NULL) { this->mode=mode; return true; } else { zipError=UNZ_OPENERROR; return false; } case mdCreate: case mdAppend: case mdAdd: zipFile_f=zipOpen2(QFile::encodeName(zipName).constData(), mode==mdCreate?APPEND_STATUS_CREATE: mode==mdAppend?APPEND_STATUS_CREATEAFTER: APPEND_STATUS_ADDINZIP, NULL, ioApi); if(zipFile_f!=NULL) { this->mode=mode; return true; } else { zipError=UNZ_OPENERROR; return false; } default: qWarning("QuaZip::open(): unknown mode: %d", (int)mode); return false; break; } } void QuaZip::close() { zipError=UNZ_OK; switch(mode) { case mdNotOpen: qWarning("QuaZip::close(): ZIP is not open"); return; case mdUnzip: zipError=unzClose(unzFile_f); break; case mdCreate: case mdAppend: case mdAdd: zipError=zipClose(zipFile_f, commentCodec->fromUnicode(comment).constData()); break; default: qWarning("QuaZip::close(): unknown mode: %d", (int)mode); return; } if(zipError==UNZ_OK) mode=mdNotOpen; } void QuaZip::setZipName(const QString& zipName) { if(isOpen()) { qWarning("QuaZip::setZipName(): ZIP is already open!"); return; } this->zipName=zipName; } int QuaZip::getEntriesCount()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode"); return -1; } unz_global_info globalInfo; if((fakeThis->zipError=unzGetGlobalInfo(unzFile_f, &globalInfo))!=UNZ_OK) return zipError; return (int)globalInfo.number_entry; } QString QuaZip::getComment()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode"); return QString(); } unz_global_info globalInfo; QByteArray comment; if((fakeThis->zipError=unzGetGlobalInfo(unzFile_f, &globalInfo))!=UNZ_OK) return QString(); comment.resize(globalInfo.size_comment); if((fakeThis->zipError=unzGetGlobalComment(unzFile_f, comment.data(), comment.size()))!=UNZ_OK) return QString(); return commentCodec->toUnicode(comment); } bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs) { zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode"); return false; } if(fileName.isNull()) { hasCurrentFile_f=false; return true; } // Unicode-aware reimplementation of the unzLocateFile function if(unzFile_f==NULL) { zipError=UNZ_PARAMERROR; return false; } if(fileName.length()>MAX_FILE_NAME_LENGTH) { zipError=UNZ_PARAMERROR; return false; } bool sens; if(cs==csDefault) { #ifdef Q_OS_WIN sens=false; #else sens=true; #endif } else sens=cs==csSensitive; QString lower, current; if(!sens) lower=fileName.toLower(); hasCurrentFile_f=false; for(bool more=goToFirstFile(); more; more=goToNextFile()) { current=getCurrentFileName(); if(current.isNull()) return false; if(sens) { if(current==fileName) break; } else { if(current.toLower()==lower) break; } } return hasCurrentFile_f; } bool QuaZip::goToFirstFile() { zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); return false; } zipError=unzGoToFirstFile(unzFile_f); hasCurrentFile_f=zipError==UNZ_OK; return hasCurrentFile_f; } bool QuaZip::goToNextFile() { zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); return false; } zipError=unzGoToNextFile(unzFile_f); hasCurrentFile_f=zipError==UNZ_OK; if(zipError==UNZ_END_OF_LIST_OF_FILE) zipError=UNZ_OK; return hasCurrentFile_f; } bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode"); return false; } unz_file_info info_z; QByteArray fileName; QByteArray extra; QByteArray comment; if(info==NULL) return false; if(!isOpen()||!hasCurrentFile()) return false; if((fakeThis->zipError=unzGetCurrentFileInfo(unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK) return false; fileName.resize(info_z.size_filename); extra.resize(info_z.size_file_extra); comment.resize(info_z.size_file_comment); if((fakeThis->zipError=unzGetCurrentFileInfo(unzFile_f, NULL, fileName.data(), fileName.size(), extra.data(), extra.size(), comment.data(), comment.size()))!=UNZ_OK) return false; info->versionCreated=info_z.version; info->versionNeeded=info_z.version_needed; info->flags=info_z.flag; info->method=info_z.compression_method; info->crc=info_z.crc; info->compressedSize=info_z.compressed_size; info->uncompressedSize=info_z.uncompressed_size; info->diskNumberStart=info_z.disk_num_start; info->internalAttr=info_z.internal_fa; info->externalAttr=info_z.external_fa; info->name=fileNameCodec->toUnicode(fileName); info->comment=commentCodec->toUnicode(comment); info->extra=extra; info->dateTime=QDateTime( QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday), QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec)); return true; } QString QuaZip::getCurrentFileName()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->zipError=UNZ_OK; if(mode!=mdUnzip) { qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode"); return QString(); } if(!isOpen()||!hasCurrentFile()) return QString(); QByteArray fileName(MAX_FILE_NAME_LENGTH, 0); if((fakeThis->zipError=unzGetCurrentFileInfo(unzFile_f, NULL, fileName.data(), fileName.size(), NULL, 0, NULL, 0))!=UNZ_OK) return QString(); return fileNameCodec->toUnicode(fileName.constData()); } fritzing-app-0.9.3b/src/lib/quazip/quazip.h000066400000000000000000000354241272363601300206420ustar00rootroot00000000000000#ifndef QUA_ZIP_H #define QUA_ZIP_H /* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2007 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. **/ #include #include #include "zip.h" #include "unzip.h" #include "quazipfileinfo.h" // just in case it will be defined in the later versions of the ZIP/UNZIP #ifndef UNZ_OPENERROR // define additional error code #define UNZ_OPENERROR -1000 #endif /// ZIP archive. /** \class QuaZip quazip.h * This class implements basic interface to the ZIP archive. It can be * used to read table contents of the ZIP archive and retreiving * information about the files inside it. * * You can also use this class to open files inside archive by passing * pointer to the instance of this class to the constructor of the * QuaZipFile class. But see QuaZipFile::QuaZipFile(QuaZip*, QObject*) * for the possible pitfalls. * * This class is indended to provide interface to the ZIP subpackage of * the ZIP/UNZIP package as well as to the UNZIP subpackage. But * currently it supports only UNZIP. * * The use of this class is simple - just create instance using * constructor, then set ZIP archive file name using setFile() function * (if you did not passed the name to the constructor), then open() and * then use different functions to work with it! Well, if you are * paranoid, you may also wish to call close before destructing the * instance, to check for errors on close. * * You may also use getUnzFile() and getZipFile() functions to get the * ZIP archive handle and use it with ZIP/UNZIP package API directly. * * This class supports localized file names inside ZIP archive, but you * have to set up proper codec with setCodec() function. By default, * locale codec will be used, which is probably ok for UNIX systems, but * will almost certainly fail with ZIP archives created in Windows. This * is because Windows ZIP programs have strange habit of using DOS * encoding for file names in ZIP archives. For example, ZIP archive * with cyrillic names created in Windows will have file names in \c * IBM866 encoding instead of \c WINDOWS-1251. I think that calling one * function is not much trouble, but for true platform independency it * would be nice to have some mechanism for file name encoding auto * detection using locale information. Does anyone know a good way to do * it? **/ class QuaZip { public: /// Useful constants. enum Constants { MAX_FILE_NAME_LENGTH=256 /**< Maximum file name length. Taken from \c UNZ_MAXFILENAMEINZIP constant in unzip.c. */ }; /// Open mode of the ZIP file. enum Mode { mdNotOpen, ///< ZIP file is not open. This is the initial mode. mdUnzip, ///< ZIP file is open for reading files inside it. mdCreate, ///< ZIP file was created with open() call. mdAppend, /**< ZIP file was opened in append mode. This refers to * \c APPEND_STATUS_CREATEAFTER mode in ZIP/UNZIP package * and means that zip is appended to some existing file * what is useful when that file contains * self-extractor code. This is obviously \em not what * you whant to use to add files to the existing ZIP * archive. **/ mdAdd ///< ZIP file was opened for adding files in the archive. }; /// Case sensitivity for the file names. /** This is what you specify when accessing files in the archive. * Works perfectly fine with any characters thanks to Qt's great * unicode support. This is different from ZIP/UNZIP API, where * only US-ASCII characters was supported. **/ enum CaseSensitivity { csDefault=0, ///< Default for platform. Case sensitive for UNIX, not for Windows. csSensitive=1, ///< Case sensitive. csInsensitive=2 ///< Case insensitive. }; private: QTextCodec *fileNameCodec, *commentCodec; QString zipName; QString comment; Mode mode; union { unzFile unzFile_f; zipFile zipFile_f; }; bool hasCurrentFile_f; int zipError; // not (and will not be) implemented QuaZip(const QuaZip& that); // not (and will not be) implemented QuaZip& operator=(const QuaZip& that); public: /// Constructs QuaZip object. /** Call setName() before opening constructed object. */ QuaZip(); /// Constructs QuaZip object associated with ZIP file \a zipName. QuaZip(const QString& zipName); /// Destroys QuaZip object. /** Calls close() if necessary. */ ~QuaZip(); /// Opens ZIP file. /** Argument \a ioApi specifies IO function set for ZIP/UNZIP * package to use. See unzip.h, zip.h and ioapi.h for details. By * passing NULL (the default) you just tell package to use the * default API which works just fine on UNIX platforms. I have tried * it on win32-g++ platform too and it seems it works fine there * too, so I see no reason to use win32 IO API included in original * ZIP/UNZIP package. * * ZIP archive file name will be converted to 8-bit encoding using * Qt's QFile::encodeName() function before passing it to the * ZIP/UNZIP package API. * * Returns \c true if successful, \c false otherwise. * * Argument \a mode specifies open mode of the ZIP archive. See Mode * for details. Note that there is zipOpen2() function in the * ZIP/UNZIP API which accepts \a globalcomment argument, but it * does not use it anywhere, so this open() function does not have this * argument. See setComment() if you need to set global comment. * * \note ZIP/UNZIP API open calls do not return error code - they * just return \c NULL indicating an error. But to make things * easier, quazip.h header defines additional error code \c * UNZ_ERROROPEN and getZipError() will return it if the open call * of the ZIP/UNZIP API returns \c NULL. **/ bool open(Mode mode, zlib_filefunc_def *ioApi =NULL); /// Closes ZIP file. /** Call getZipError() to determine if the close was successful. */ void close(); /// Sets the codec used to encode/decode file names inside archive. /** This is necessary to access files in the ZIP archive created * under Windows with non-latin characters in file names. For * example, file names with cyrillic letters will be in \c IBM866 * encoding. **/ void setFileNameCodec(QTextCodec *fileNameCodec) {this->fileNameCodec=fileNameCodec;} /// Sets the codec used to encode/decode file names inside archive. /** \overload * Equivalent to calling setFileNameCodec(QTextCodec::codecForName(codecName)); **/ void setFileNameCodec(const char *fileNameCodecName) {fileNameCodec=QTextCodec::codecForName(fileNameCodecName);} /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getFileNameCodec()const {return fileNameCodec;} /// Sets the codec used to encode/decode comments inside archive. /** This codec defaults to locale codec, which is probably ok. **/ void setCommentCodec(QTextCodec *commentCodec) {this->commentCodec=commentCodec;} /// Sets the codec used to encode/decode comments inside archive. /** \overload * Equivalent to calling setCommentCodec(QTextCodec::codecForName(codecName)); **/ void setCommentCodec(const char *commentCodecName) {commentCodec=QTextCodec::codecForName(commentCodecName);} /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getCommentCodec()const {return commentCodec;} /// Returns the name of the ZIP file. /** Returns null string if no ZIP file name has been set. * \sa setZipName() **/ QString getZipName()const {return zipName;} /// Sets the name of the ZIP file. /** Does nothing if the ZIP file is open. * * Does not reset error code returned by getZipError(). **/ void setZipName(const QString& zipName); /// Returns the mode in which ZIP file was opened. Mode getMode()const {return mode;} /// Returns \c true if ZIP file is open, \c false otherwise. bool isOpen()const {return mode!=mdNotOpen;} /// Returns the error code of the last operation. /** Returns \c UNZ_OK if the last operation was successful. * * Error code resets to \c UNZ_OK every time you call any function * that accesses something inside ZIP archive, even if it is \c * const (like getEntriesCount()). open() and close() calls reset * error code too. See documentation for the specific functions for * details on error detection. **/ int getZipError()const {return zipError;} /// Returns number of the entries in the ZIP central directory. /** Returns negative error code in the case of error. The same error * code will be returned by subsequent getZipError() call. **/ int getEntriesCount()const; /// Returns global comment in the ZIP file. QString getComment()const; /// Sets global comment in the ZIP file. /** Comment will be written to the archive on close operation. * * \sa open() **/ void setComment(const QString& comment) {this->comment=comment;} /// Sets the current file to the first file in the archive. /** Returns \c true on success, \c false otherwise. Call * getZipError() to get the error code. **/ bool goToFirstFile(); /// Sets the current file to the next file in the archive. /** Returns \c true on success, \c false otherwise. Call * getZipError() to determine if there was an error. * * Should be used only in QuaZip::mdUnzip mode. * * \note If the end of file was reached, getZipError() will return * \c UNZ_OK instead of \c UNZ_END_OF_LIST_OF_FILE. This is to make * things like this easier: * \code * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { * // do something * } * if(zip.getZipError()==UNZ_OK) { * // ok, there was no error * } * \endcode **/ bool goToNextFile(); /// Sets current file by its name. /** Returns \c true if successful, \c false otherwise. Argument \a * cs specifies case sensitivity of the file name. Call * getZipError() in the case of a failure to get error code. * * This is not a wrapper to unzLocateFile() function. That is * because I had to implement locale-specific case-insensitive * comparison. * * Here are the differences from the original implementation: * * - If the file was not found, error code is \c UNZ_OK, not \c * UNZ_END_OF_LIST_OF_FILE (see also goToNextFile()). * - If this function fails, it unsets the current file rather than * resetting it back to what it was before the call. * * If \a fileName is null string then this function unsets the * current file and return \c true. Note that you should close the * file first if it is open! See * QuaZipFile::QuaZipFile(QuaZip*,QObject*) for the details. * * Should be used only in QuaZip::mdUnzip mode. * * \sa setFileNameCodec(), CaseSensitivity **/ bool setCurrentFile(const QString& fileName, CaseSensitivity cs =csDefault); /// Returns \c true if the current file has been set. bool hasCurrentFile()const {return hasCurrentFile_f;} /// Retrieves information about the current file. /** Fills the structure pointed by \a info. Returns \c true on * success, \c false otherwise. In the latter case structure pointed * by \a info remains untouched. If there was an error, * getZipError() returns error code. * * Should be used only in QuaZip::mdUnzip mode. * * Does nothing and returns \c false in any of the following cases. * - ZIP is not open; * - ZIP does not have current file; * - \a info is \c NULL; * * In all these cases getZipError() returns \c UNZ_OK since there * is no ZIP/UNZIP API call. **/ bool getCurrentFileInfo(QuaZipFileInfo* info)const; /// Returns the current file name. /** Equivalent to calling getCurrentFileInfo() and then getting \c * name field of the QuaZipFileInfo structure, but faster and more * convenient. * * Should be used only in QuaZip::mdUnzip mode. **/ QString getCurrentFileName()const; /// Returns \c unzFile handle. /** You can use this handle to directly call UNZIP part of the * ZIP/UNZIP package functions (see unzip.h). * * \warning When using the handle returned by this function, please * keep in mind that QuaZip class is unable to detect any changes * you make in the ZIP file state (e. g. changing current file, or * closing the handle). So please do not do anything with this * handle that is possible to do with the functions of this class. * Or at least return the handle in the original state before * calling some another function of this class (including implicit * destructor calls and calls from the QuaZipFile objects that refer * to this QuaZip instance!). So if you have changed the current * file in the ZIP archive - then change it back or you may * experience some strange behavior or even crashes. **/ unzFile getUnzFile() {return unzFile_f;} /// Returns \c zipFile handle. /** You can use this handle to directly call ZIP part of the * ZIP/UNZIP package functions (see zip.h). Warnings about the * getUnzFile() function also apply to this function. **/ zipFile getZipFile() {return zipFile_f;} }; #endif fritzing-app-0.9.3b/src/lib/quazip/quazipfile.cpp000066400000000000000000000244731272363601300220370ustar00rootroot00000000000000/* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2007 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. **/ #include "quazipfile.h" using namespace std; QuaZipFile::QuaZipFile(): zip(NULL), internal(true), zipError(UNZ_OK) { } QuaZipFile::QuaZipFile(QObject *parent): QIODevice(parent), zip(NULL), internal(true), zipError(UNZ_OK) { } QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent): QIODevice(parent), internal(true), zipError(UNZ_OK) { zip=new QuaZip(zipName); Q_CHECK_PTR(zip); } QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName, QuaZip::CaseSensitivity cs, QObject *parent): QIODevice(parent), internal(true), zipError(UNZ_OK) { zip=new QuaZip(zipName); Q_CHECK_PTR(zip); this->fileName=fileName; this->caseSensitivity=cs; } QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent): QIODevice(parent), zip(zip), internal(false), zipError(UNZ_OK) { } QuaZipFile::~QuaZipFile() { if(isOpen()) close(); if(internal) delete zip; } QString QuaZipFile::getZipName()const { return zip==NULL?QString():zip->getZipName(); } QString QuaZipFile::getActualFileName()const { setZipError(UNZ_OK); if(zip==NULL||(openMode()&WriteOnly)) return QString(); QString name=zip->getCurrentFileName(); if(name.isNull()) setZipError(zip->getZipError()); return name; } void QuaZipFile::setZipName(const QString& zipName) { if(isOpen()) { qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name"); return; } if(zip!=NULL&&internal) delete zip; zip=new QuaZip(zipName); Q_CHECK_PTR(zip); internal=true; } void QuaZipFile::setZip(QuaZip *zip) { if(isOpen()) { qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP"); return; } if(this->zip!=NULL&&internal) delete this->zip; this->zip=zip; this->fileName=QString(); internal=false; } void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs) { if(zip==NULL) { qWarning("QuaZipFile::setFileName(): call setZipName() first"); return; } if(!internal) { qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip"); return; } if(isOpen()) { qWarning("QuaZipFile::setFileName(): can not set file name for already opened file"); return; } this->fileName=fileName; this->caseSensitivity=cs; } void QuaZipFile::setZipError(int zipError)const { QuaZipFile *fakeThis=(QuaZipFile*)this; // non-const fakeThis->zipError=zipError; if(zipError==UNZ_OK) fakeThis->setErrorString(QString()); else fakeThis->setErrorString(QString("ZIP/UNZIP API error %1").arg(zipError)); } bool QuaZipFile::open(OpenMode mode) { return open(mode, NULL); } bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password) { resetZipError(); if(isOpen()) { qWarning("QuaZipFile::open(): already opened"); return false; } if(mode&Unbuffered) { qWarning("QuaZipFile::open(): Unbuffered mode is not supported"); return false; } if((mode&ReadOnly)&&!(mode&WriteOnly)) { if(internal) { if(!zip->open(QuaZip::mdUnzip)) { setZipError(zip->getZipError()); return false; } if(!zip->setCurrentFile(fileName, caseSensitivity)) { setZipError(zip->getZipError()); zip->close(); return false; } } else { if(zip==NULL) { qWarning("QuaZipFile::open(): zip is NULL"); return false; } if(zip->getMode()!=QuaZip::mdUnzip) { qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", (int)mode, (int)zip->getMode()); return false; } if(!zip->hasCurrentFile()) { qWarning("QuaZipFile::open(): zip does not have current file"); return false; } } setZipError(unzOpenCurrentFile3(zip->getUnzFile(), method, level, (int)raw, password)); if(zipError==UNZ_OK) { setOpenMode(mode); this->raw=raw; return true; } else return false; } qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); return false; } bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info, const char *password, quint32 crc, int method, int level, bool raw, int windowBits, int memLevel, int strategy) { zip_fileinfo info_z; resetZipError(); if(isOpen()) { qWarning("QuaZipFile::open(): already opened"); return false; } if((mode&WriteOnly)&&!(mode&ReadOnly)) { if(internal) { qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach"); return false; } if(zip==NULL) { qWarning("QuaZipFile::open(): zip is NULL"); return false; } if(zip->getMode()!=QuaZip::mdCreate&&zip->getMode()!=QuaZip::mdAppend&&zip->getMode()!=QuaZip::mdAdd) { qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", (int)mode, (int)zip->getMode()); return false; } info_z.tmz_date.tm_year=info.dateTime.date().year(); info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1; info_z.tmz_date.tm_mday=info.dateTime.date().day(); info_z.tmz_date.tm_hour=info.dateTime.time().hour(); info_z.tmz_date.tm_min=info.dateTime.time().minute(); info_z.tmz_date.tm_sec=info.dateTime.time().second(); info_z.dosDate = 0; info_z.internal_fa=(uLong)info.internalAttr; info_z.external_fa=(uLong)info.externalAttr; setZipError(zipOpenNewFileInZip3(zip->getZipFile(), zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, info.extraLocal.constData(), info.extraLocal.length(), info.extraGlobal.constData(), info.extraGlobal.length(), zip->getCommentCodec()->fromUnicode(info.comment).constData(), method, level, (int)raw, windowBits, memLevel, strategy, password, (uLong)crc)); if(zipError==UNZ_OK) { writePos=0; setOpenMode(mode); this->raw=raw; if(raw) { this->crc=crc; this->uncompressedSize=info.uncompressedSize; } return true; } else return false; } qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); return false; } bool QuaZipFile::isSequential()const { return true; } qint64 QuaZipFile::pos()const { if(zip==NULL) { qWarning("QuaZipFile::pos(): call setZipName() or setZip() first"); return -1; } if(!isOpen()) { qWarning("QuaZipFile::pos(): file is not open"); return -1; } if(openMode()&ReadOnly) return unztell(zip->getUnzFile()); else return writePos; } bool QuaZipFile::atEnd()const { if(zip==NULL) { qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first"); return false; } if(!isOpen()) { qWarning("QuaZipFile::atEnd(): file is not open"); return false; } if(openMode()&ReadOnly) return unzeof(zip->getUnzFile())==1; else return true; } qint64 QuaZipFile::size()const { if(!isOpen()) { qWarning("QuaZipFile::atEnd(): file is not open"); return -1; } if(openMode()&ReadOnly) return raw?csize():usize(); else return writePos; } qint64 QuaZipFile::csize()const { unz_file_info info_z; setZipError(UNZ_OK); if(zip==NULL||zip->getMode()!=QuaZip::mdUnzip) return -1; setZipError(unzGetCurrentFileInfo(zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); if(zipError!=UNZ_OK) return -1; return info_z.compressed_size; } qint64 QuaZipFile::usize()const { unz_file_info info_z; setZipError(UNZ_OK); if(zip==NULL||zip->getMode()!=QuaZip::mdUnzip) return -1; setZipError(unzGetCurrentFileInfo(zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); if(zipError!=UNZ_OK) return -1; return info_z.uncompressed_size; } bool QuaZipFile::getFileInfo(QuaZipFileInfo *info) { if(zip==NULL||zip->getMode()!=QuaZip::mdUnzip) return false; zip->getCurrentFileInfo(info); setZipError(zip->getZipError()); return zipError==UNZ_OK; } void QuaZipFile::close() { resetZipError(); if(zip==NULL||!zip->isOpen()) return; if(!isOpen()) { qWarning("QuaZipFile::close(): file isn't open"); return; } if(openMode()&ReadOnly) setZipError(unzCloseCurrentFile(zip->getUnzFile())); else if(openMode()&WriteOnly) if(isRaw()) setZipError(zipCloseFileInZipRaw(zip->getZipFile(), uncompressedSize, crc)); else setZipError(zipCloseFileInZip(zip->getZipFile())); else { qWarning("Wrong open mode: %d", (int)openMode()); return; } if(zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen); else return; if(internal) { zip->close(); setZipError(zip->getZipError()); } } qint64 QuaZipFile::readData(char *data, qint64 maxSize) { setZipError(UNZ_OK); qint64 bytesRead=unzReadCurrentFile(zip->getUnzFile(), data, (unsigned)maxSize); if(bytesRead<0) setZipError((int)bytesRead); return bytesRead; } qint64 QuaZipFile::writeData(const char* data, qint64 maxSize) { setZipError(ZIP_OK); setZipError(zipWriteInFileInZip(zip->getZipFile(), data, (uint)maxSize)); if(zipError!=ZIP_OK) return -1; else { writePos+=maxSize; return maxSize; } } fritzing-app-0.9.3b/src/lib/quazip/quazipfile.h000066400000000000000000000455141272363601300215030ustar00rootroot00000000000000#ifndef QUA_ZIPFILE_H #define QUA_ZIPFILE_H /* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2008 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. **/ #include #include "quazip.h" #include "quazipnewinfo.h" /// A file inside ZIP archive. /** \class QuaZipFile quazipfile.h * This is the most interesting class. Not only it provides C++ * interface to the ZIP/UNZIP package, but also integrates it with Qt by * subclassing QIODevice. This makes possible to access files inside ZIP * archive using QTextStream or QDataStream, for example. Actually, this * is the main purpose of the whole QuaZIP library. * * You can either use existing QuaZip instance to create instance of * this class or pass ZIP archive file name to this class, in which case * it will create internal QuaZip object. See constructors' descriptions * for details. Writing is only possible with the existing instance. * * \section quazipfile-sequential Sequential or random-access? * * At the first thought, QuaZipFile has fixed size, the start and the * end and should be therefore considered random-access device. But * there is one major obstacle to making it random-access: ZIP/UNZIP API * does not support seek() operation and the only way to implement it is * through reopening the file and re-reading to the required position, * but this is prohibitely slow. * * Therefore, QuaZipFile is considered to be a sequential device. This * has advantage of availability of the ungetChar() operation (QIODevice * does not implement it properly for non-sequential devices unless they * support seek()). Disadvantage is a somewhat strange behaviour of the * size() and pos() functions. This should be kept in mind while using * this class. * **/ class QuaZipFile: public QIODevice { Q_OBJECT private: QuaZip *zip; QString fileName; QuaZip::CaseSensitivity caseSensitivity; bool raw; qint64 writePos; // these two are for writing raw files ulong uncompressedSize; quint32 crc; bool internal; int zipError; // these are not supported nor implemented QuaZipFile(const QuaZipFile& that); QuaZipFile& operator=(const QuaZipFile& that); void resetZipError()const {setZipError(UNZ_OK);} // const, but sets zipError! void setZipError(int zipError)const; protected: /// Implementation of the QIODevice::readData(). qint64 readData(char *data, qint64 maxSize); /// Implementation of the QIODevice::writeData(). qint64 writeData(const char *data, qint64 maxSize); public: /// Constructs a QuaZipFile instance. /** You should use setZipName() and setFileName() or setZip() before * trying to call open() on the constructed object. **/ QuaZipFile(); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object. * * You should use setZipName() and setFileName() or setZip() before * trying to call open() on the constructed object. **/ QuaZipFile(QObject *parent); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object and \a * zipName specifies ZIP archive file name. * * You should use setFileName() before trying to call open() on the * constructed object. * * QuaZipFile constructed by this constructor can be used for read * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. **/ QuaZipFile(const QString& zipName, QObject *parent =NULL); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object, \a * zipName specifies ZIP archive file name and \a fileName and \a cs * specify a name of the file to open inside archive. * * QuaZipFile constructed by this constructor can be used for read * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. * * \sa QuaZip::setCurrentFile() **/ QuaZipFile(const QString& zipName, const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault, QObject *parent =NULL); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object. * * \a zip is the pointer to the existing QuaZip object. This * QuaZipFile object then can be used to read current file in the * \a zip or to write to the file inside it. * * \warning Using this constructor for reading current file can be * tricky. Let's take the following example: * \code * QuaZip zip("archive.zip"); * zip.open(QuaZip::mdUnzip); * zip.setCurrentFile("file-in-archive"); * QuaZipFile file(&zip); * file.open(QIODevice::ReadOnly); * // ok, now we can read from the file * file.read(somewhere, some); * zip.setCurrentFile("another-file-in-archive"); // oops... * QuaZipFile anotherFile(&zip); * anotherFile.open(QIODevice::ReadOnly); * anotherFile.read(somewhere, some); // this is still ok... * file.read(somewhere, some); // and this is NOT * \endcode * So, what exactly happens here? When we change current file in the * \c zip archive, \c file that references it becomes invalid * (actually, as far as I understand ZIP/UNZIP sources, it becomes * closed, but QuaZipFile has no means to detect it). * * Summary: do not close \c zip object or change its current file as * long as QuaZipFile is open. Even better - use another constructors * which create internal QuaZip instances, one per object, and * therefore do not cause unnecessary trouble. This constructor may * be useful, though, if you already have a QuaZip instance and do * not want to access several files at once. Good example: * \code * QuaZip zip("archive.zip"); * zip.open(QuaZip::mdUnzip); * // first, we need some information about archive itself * QByteArray comment=zip.getComment(); * // and now we are going to access files inside it * QuaZipFile file(&zip); * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { * file.open(QIODevice::ReadOnly); * // do something cool with file here * file.close(); // do not forget to close! * } * zip.close(); * \endcode **/ QuaZipFile(QuaZip *zip, QObject *parent =NULL); /// Destroys a QuaZipFile instance. /** Closes file if open, destructs internal QuaZip object (if it * exists and \em is internal, of course). **/ virtual ~QuaZipFile(); /// Returns the ZIP archive file name. /** If this object was created by passing QuaZip pointer to the * constructor, this function will return that QuaZip's file name * (or null string if that object does not have file name yet). * * Otherwise, returns associated ZIP archive file name or null * string if there are no name set yet. * * \sa setZipName() getFileName() **/ QString getZipName()const; /// Returns a pointer to the associated QuaZip object. /** Returns \c NULL if there is no associated QuaZip or it is * internal (so you will not mess with it). **/ QuaZip* getZip()const; /// Returns file name. /** This function returns file name you passed to this object either * by using * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) * or by calling setFileName(). Real name of the file may differ in * case if you used case-insensitivity. * * Returns null string if there is no file name set yet. This is the * case when this QuaZipFile operates on the existing QuaZip object * (constructor QuaZipFile(QuaZip*,QObject*) or setZip() was used). * * \sa getActualFileName **/ QString getFileName()const {return fileName;} /// Returns case sensitivity of the file name. /** This function returns case sensitivity argument you passed to * this object either by using * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) * or by calling setFileName(). * * Returns unpredictable value if getFileName() returns null string * (this is the case when you did not used setFileName() or * constructor above). * * \sa getFileName **/ QuaZip::CaseSensitivity getCaseSensitivity()const {return caseSensitivity;} /// Returns the actual file name in the archive. /** This is \em not a ZIP archive file name, but a name of file inside * archive. It is not necessary the same name that you have passed * to the * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*), * setFileName() or QuaZip::setCurrentFile() - this is the real file * name inside archive, so it may differ in case if the file name * search was case-insensitive. * * Equivalent to calling getCurrentFileName() on the associated * QuaZip object. Returns null string if there is no associated * QuaZip object or if it does not have a current file yet. And this * is the case if you called setFileName() but did not open the * file yet. So this is perfectly fine: * \code * QuaZipFile file("somezip.zip"); * file.setFileName("somefile"); * QString name=file.getName(); // name=="somefile" * QString actual=file.getActualFileName(); // actual is null string * file.open(QIODevice::ReadOnly); * QString actual=file.getActualFileName(); // actual can be "SoMeFiLe" on Windows * \endcode * * \sa getZipName(), getFileName(), QuaZip::CaseSensitivity **/ QString getActualFileName()const; /// Sets the ZIP archive file name. /** Automatically creates internal QuaZip object and destroys * previously created internal QuaZip object, if any. * * Will do nothing if this file is already open. You must close() it * first. **/ void setZipName(const QString& zipName); /// Returns \c true if the file was opened in raw mode. /** If the file is not open, the returned value is undefined. * * \sa open(OpenMode,int*,int*,bool,const char*) **/ bool isRaw()const {return raw;} /// Binds to the existing QuaZip instance. /** This function destroys internal QuaZip object, if any, and makes * this QuaZipFile to use current file in the \a zip object for any * further operations. See QuaZipFile(QuaZip*,QObject*) for the * possible pitfalls. * * Will do nothing if the file is currently open. You must close() * it first. **/ void setZip(QuaZip *zip); /// Sets the file name. /** Will do nothing if at least one of the following conditions is * met: * - ZIP name has not been set yet (getZipName() returns null * string). * - This QuaZipFile is associated with external QuaZip. In this * case you should call that QuaZip's setCurrentFile() function * instead! * - File is already open so setting the name is meaningless. * * \sa QuaZip::setCurrentFile **/ void setFileName(const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault); /// Opens a file for reading. /** Returns \c true on success, \c false otherwise. * Call getZipError() to get error code. * * \note Since ZIP/UNZIP API provides buffered reading only, * QuaZipFile does not support unbuffered reading. So do not pass * QIODevice::Unbuffered flag in \a mode, or open will fail. **/ virtual bool open(OpenMode mode); /// Opens a file for reading. /** \overload * Argument \a password specifies a password to decrypt the file. If * it is NULL then this function behaves just like open(OpenMode). **/ bool open(OpenMode mode, const char *password) {return open(mode, NULL, NULL, false, password);} /// Opens a file for reading. /** \overload * Argument \a password specifies a password to decrypt the file. * * An integers pointed by \a method and \a level will receive codes * of the compression method and level used. See unzip.h. * * If raw is \c true then no decompression is performed. * * \a method should not be \c NULL. \a level can be \c NULL if you * don't want to know the compression level. **/ bool open(OpenMode mode, int *method, int *level, bool raw, const char *password =NULL); /// Opens a file for writing. /** \a info argument specifies information about file. It should at * least specify a correct file name. Also, it is a good idea to * specify correct timestamp (by default, current time will be * used). See QuaZipNewInfo. * * Arguments \a password and \a crc provide necessary information * for crypting. Note that you should specify both of them if you * need crypting. If you do not, pass \c NULL as password, but you * still need to specify \a crc if you are going to use raw mode * (see below). * * Arguments \a method and \a level specify compression method and * level. * * If \a raw is \c true, no compression is performed. In this case, * \a crc and uncompressedSize field of the \a info are required. * * Arguments \a windowBits, \a memLevel, \a strategy provide zlib * algorithms tuning. See deflateInit2() in zlib. **/ bool open(OpenMode mode, const QuaZipNewInfo& info, const char *password =NULL, quint32 crc =0, int method =Z_DEFLATED, int level =Z_DEFAULT_COMPRESSION, bool raw =false, int windowBits =-MAX_WBITS, int memLevel =DEF_MEM_LEVEL, int strategy =Z_DEFAULT_STRATEGY); /// Returns \c true, but \ref quazipfile-sequential "beware"! virtual bool isSequential()const; /// Returns current position in the file. /** Implementation of the QIODevice::pos(). When reading, this * function is a wrapper to the ZIP/UNZIP unztell(), therefore it is * unable to keep track of the ungetChar() calls (which is * non-virtual and therefore is dangerous to reimplement). So if you * are using ungetChar() feature of the QIODevice, this function * reports incorrect value until you get back characters which you * ungot. * * When writing, pos() returns number of bytes already written * (uncompressed unless you use raw mode). * * \note Although * \ref quazipfile-sequential "QuaZipFile is a sequential device" * and therefore pos() should always return zero, it does not, * because it would be misguiding. Keep this in mind. * * This function returns -1 if the file or archive is not open. * * Error code returned by getZipError() is not affected by this * function call. **/ virtual qint64 pos()const; /// Returns \c true if the end of file was reached. /** This function returns \c false in the case of error. This means * that you called this function on either not open file, or a file * in the not open archive or even on a QuaZipFile instance that * does not even have QuaZip instance associated. Do not do that * because there is no means to determine whether \c false is * returned because of error or because end of file was reached. * Well, on the other side you may interpret \c false return value * as "there is no file open to check for end of file and there is * no end of file therefore". * * When writing, this function always returns \c true (because you * are always writing to the end of file). * * Error code returned by getZipError() is not affected by this * function call. **/ virtual bool atEnd()const; /// Returns file size. /** This function returns csize() if the file is open for reading in * raw mode, usize() if it is open for reading in normal mode and * pos() if it is open for writing. * * Returns -1 on error, call getZipError() to get error code. * * \note This function returns file size despite that * \ref quazipfile-sequential "QuaZipFile is considered to be sequential device", * for which size() should return bytesAvailable() instead. But its * name would be very misguiding otherwise, so just keep in mind * this inconsistence. **/ virtual qint64 size()const; /// Returns compressed file size. /** Equivalent to calling getFileInfo() and then getting * compressedSize field, but more convenient and faster. * * File must be open for reading before calling this function. * * Returns -1 on error, call getZipError() to get error code. **/ qint64 csize()const; /// Returns uncompressed file size. /** Equivalent to calling getFileInfo() and then getting * uncompressedSize field, but more convenient and faster. See * getFileInfo() for a warning. * * File must be open for reading before calling this function. * * Returns -1 on error, call getZipError() to get error code. **/ qint64 usize()const; /// Gets information about current file. /** This function does the same thing as calling * QuaZip::getCurrentFileInfo() on the associated QuaZip object, * but you can not call getCurrentFileInfo() if the associated * QuaZip is internal (because you do not have access to it), while * you still can call this function in that case. * * File must be open for reading before calling this function. * * Returns \c false in the case of an error. **/ bool getFileInfo(QuaZipFileInfo *info); /// Closes the file. /** Call getZipError() to determine if the close was successful. **/ virtual void close(); /// Returns the error code returned by the last ZIP/UNZIP API call. int getZipError()const {return zipError;} }; #endif fritzing-app-0.9.3b/src/lib/quazip/quazipfileinfo.h000066400000000000000000000044461272363601300223560ustar00rootroot00000000000000#ifndef QUA_ZIPFILEINFO_H #define QUA_ZIPFILEINFO_H /* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2007 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. **/ #include #include /// Information about a file inside archive. /** Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to * fill this structure. */ struct QuaZipFileInfo { /// File name. QString name; /// Version created by. quint16 versionCreated; /// Version needed to extract. quint16 versionNeeded; /// General purpose flags. quint16 flags; /// Compression method. quint16 method; /// Last modification date and time. QDateTime dateTime; /// CRC. quint32 crc; /// Compressed file size. quint32 compressedSize; /// Uncompressed file size. quint32 uncompressedSize; /// Disk number start. quint16 diskNumberStart; /// Internal file attributes. quint16 internalAttr; /// External file attributes. quint32 externalAttr; /// Comment. QString comment; /// Extra field. QByteArray extra; }; #endif fritzing-app-0.9.3b/src/lib/quazip/quazipnewinfo.cpp000066400000000000000000000037611272363601300225620ustar00rootroot00000000000000/* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2007 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. */ #include #include "quazipnewinfo.h" QuaZipNewInfo::QuaZipNewInfo(const QString& name): name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0) { } QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file): name(name), internalAttr(0), externalAttr(0) { QFileInfo info(file); QDateTime lm = info.lastModified(); if (!info.exists()) dateTime = QDateTime::currentDateTime(); else dateTime = lm; } void QuaZipNewInfo::setFileDateTime(const QString& file) { QFileInfo info(file); QDateTime lm = info.lastModified(); if (info.exists()) dateTime = lm; } fritzing-app-0.9.3b/src/lib/quazip/quazipnewinfo.h000066400000000000000000000100551272363601300222210ustar00rootroot00000000000000#ifndef QUA_ZIPNEWINFO_H #define QUA_ZIPNEWINFO_H /* -- A kind of "standard" GPL license statement -- QuaZIP - a Qt/C++ wrapper for the ZIP/UNZIP package Copyright (C) 2005-2007 Sergey A. Tachenov 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- A kind of "standard" GPL license statement ends here -- See COPYING file for GPL. You are also permitted to use QuaZIP under the terms of LGPL (see COPYING.LGPL). You are free to choose either license, but please note that QuaZIP makes use of Qt, which is not licensed under LGPL. So if you are using Open Source edition of Qt, you therefore MUST use GPL for your code based on QuaZIP, since it would be also based on Qt in this case. If you are Qt commercial license owner, then you are free to use QuaZIP as long as you respect either GPL or LGPL for QuaZIP code. **/ #include #include /// Information about a file to be created. /** This structure holds information about a file to be created inside * ZIP archive. At least name should be set to something correct before * passing this structure to * QuaZipFile::open(OpenMode,const QuaZipNewInfo&,int,int,bool). **/ struct QuaZipNewInfo { /// File name. /** This field holds file name inside archive, including path relative * to archive root. **/ QString name; /// File timestamp. /** This is the last file modification date and time. Will be stored * in the archive central directory. It is a good practice to set it * to the source file timestamp instead of archive creating time. Use * setFileDateTime() or QuaZipNewInfo(const QString&, const QString&). **/ QDateTime dateTime; /// File internal attributes. quint16 internalAttr; /// File external attributes. quint32 externalAttr; /// File comment. /** Will be encoded using QuaZip::getCommentCodec(). **/ QString comment; /// File local extra field. QByteArray extraLocal; /// File global extra field. QByteArray extraGlobal; /// Uncompressed file size. /** This is only needed if you are using raw file zipping mode, i. e. * adding precompressed file in the zip archive. **/ ulong uncompressedSize; /// Constructs QuaZipNewInfo instance. /** Initializes name with \a name, dateTime with current date and * time. Attributes are initialized with zeros, comment and extra * field with null values. **/ QuaZipNewInfo(const QString& name); /// Constructs QuaZipNewInfo instance. /** Initializes name with \a name and dateTime with timestamp of the * file named \a file. If the \a file does not exists or its timestamp * is inaccessible (e. g. you do not have read permission for the * directory file in), uses current date and time. Attributes are * initialized with zeros, comment and extra field with null values. * * \sa setFileDateTime() **/ QuaZipNewInfo(const QString& name, const QString& file); /// Sets the file timestamp from the existing file. /** Use this function to set the file timestamp from the existing * file. Use it like this: * \code * QuaZipFile zipFile(&zip); * QFile file("file-to-add"); * file.open(QIODevice::ReadOnly); * QuaZipNewInfo info("file-name-in-archive"); * info.setFileDateTime("file-to-add"); // take the timestamp from file * zipFile.open(QIODevice::WriteOnly, info); * \endcode * * This function does not change dateTime if some error occured (e. g. * file is inaccessible). **/ void setFileDateTime(const QString& file); }; #endif fritzing-app-0.9.3b/src/lib/quazip/unzip.c000066400000000000000000001402431272363601300204650ustar00rootroot00000000000000/* unzip.c -- IO for uncompress .zip files using zlib Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant Read unzip.h for more info */ /* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of compatibility with older software. The following is from the original crypt.c. Code woven in by Terry Thorsen 1/2003. */ /* Copyright (c) 1990-2000 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in zip.h) for terms of use. If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html */ /* crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) */ /* This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). */ #include #include #include #include #include "unzip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef CASESENSITIVITYDEFAULT_NO # if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) # define CASESENSITIVITYDEFAULT_NO # endif #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (16384) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; /* unz_file_info_interntal contain internal info about a file in zipfile*/ typedef struct unz_file_info_internal_s { uLong offset_curfile;/* relative offset of local header 4 bytes */ } unz_file_info_internal; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ uLong offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ uLong pos_local_extrafield; /* position in the local extra field in read*/ uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ uLong rest_read_compressed; /* number of byte to be decompressed */ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ uLong compression_method; /* compression method (0==store) */ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip_read_info_s; /* unz_s contain internal information about the zipfile */ typedef struct { zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ unz_global_info gi; /* public global information */ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ uLong num_file; /* number of the current file in the zipfile*/ uLong pos_in_central_dir; /* pos of the current file in the central dir*/ uLong current_file_ok; /* flag about the usability of the current file*/ uLong central_pos; /* position of the beginning of the central dir*/ uLong size_central_dir; /* size of the central directory */ uLong offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info cur_file_info; /* public info about the current file in zip*/ unz_file_info_internal cur_file_info_internal; /* private info about it*/ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const unsigned long* pcrc_32_tab; # endif } unz_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. IN assertion: the stream s has been sucessfully opened for reading. */ local int unzlocal_getByte OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; int *pi; { unsigned char c; int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return UNZ_OK; } else { if (ZERROR(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unzlocal_getShort OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unzlocal_getLong OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal (fileName1,fileName2) const char* fileName1; const char* fileName2; { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) const char* fileName1; const char* fileName2; int iCaseSensitivity; { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local uLong unzlocal_SearchCentralDir OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream)); local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; { unsigned char* buf; uLong uSizeFile; uLong uBackRead; uLong uMaxBack=0xffff; /* maximum size of global comment */ uLong uPosFound=0; if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) const char *path; zlib_filefunc_def* pzlib_filefunc_def; { unz_s us; unz_s *s; uLong central_pos,uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ uLong number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; if (unz_copyright[0]!=' ') return NULL; if (pzlib_filefunc_def==NULL) fill_fopen_filefunc(&us.z_filefunc); else us.z_filefunc = *pzlib_filefunc_def; us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==0) err=UNZ_ERRNO; if (ZSEEK(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* zipfile comment length */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE(s->z_filefunc, s->filestream); TRYFREE(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) unzFile file; unz_global_info *pglobal_info; { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; *pglobal_info=s->gi; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easilty) */ local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) uLong ulDosDate; tm_unz* ptm; { uLong uDate; uDate = (uLong)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, unz_file_info *pfile_info, unz_file_info_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); local int unzlocal_GetCurrentFileInfoInternal (file, pfile_info, pfile_info_internal, szFileName, fileNameBufferSize, extraField, extraFieldBufferSize, szComment, commentBufferSize) unzFile file; unz_file_info *pfile_info; unz_file_info_internal *pfile_info_internal; char *szFileName; uLong fileNameBufferSize; void *extraField; uLong extraFieldBufferSize; char *szComment; uLong commentBufferSize; { unz_s* s; unz_file_info file_info; unz_file_info_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (ZSEEK(s->z_filefunc, s->filestream, s->pos_in_central_dir+s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek -= uSizeRead; } if ((err==UNZ_OK) && (extraField!=NULL)) { uLong uSizeRead ; if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - uSizeRead; } else lSeek+=file_info.size_file_extra; if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek+=file_info.size_file_comment - uSizeRead; } else lSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetCurrentFileInfo (file, pfile_info, szFileName, fileNameBufferSize, extraField, extraFieldBufferSize, szComment, commentBufferSize) unzFile file; unz_file_info *pfile_info; char *szFileName; uLong fileNameBufferSize; void *extraField; uLong extraFieldBufferSize; char *szComment; uLong commentBufferSize; { return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile (file) unzFile file; { int err=UNZ_OK; unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzGoToNextFile (file) unzFile file; { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzipStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) unzFile file; const char *szFileName; int iCaseSensitivity; { unz_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info cur_file_infoSaved; unz_file_info_internal cur_file_info_internalSaved; uLong num_fileSaved; uLong pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; /* Save the current state */ num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; cur_file_infoSaved = s->cur_file_info; cur_file_info_internalSaved = s->cur_file_info_internal; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } } /* We failed, so restore the state of the 'current file' to where we * were. */ s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; s->cur_file_info = cur_file_infoSaved; s->cur_file_info_internal = cur_file_info_internalSaved; return err; } /* /////////////////////////////////////////// // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) // I need random access // // Further optimization could be realized by adding an ability // to cache the directory in memory. The goal being a single // comprehensive file read to put the file I need in a memory. */ /* typedef struct unz_file_pos_s { uLong pos_in_zip_directory; // offset in file uLong num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos(file, file_pos) unzFile file; unz_file_pos* file_pos; { unz_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; file_pos->pos_in_zip_directory = s->pos_in_central_dir; file_pos->num_of_file = s->num_file; return UNZ_OK; } extern int ZEXPORT unzGoToFilePos(file, file_pos) unzFile file; unz_file_pos* file_pos; { unz_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; /* jump to the right spot */ s->pos_in_central_dir = file_pos->pos_in_zip_directory; s->num_file = file_pos->num_of_file; /* set the current file */ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); /* return results */ s->current_file_ok = (err == UNZ_OK); return err; } /* // Unzip Helper Functions - should be here? /////////////////////////////////////////// */ /* Read the local header of the current zipfile Check the coherency of the local header and info in the end of central directory about this file store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, poffset_local_extrafield, psize_local_extrafield) unz_s* s; uInt* piSizeVar; uLong *poffset_local_extrafield; uInt *psize_local_extrafield; { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) unzFile file; int* method; int* level; int raw; const char* password; { int err=UNZ_OK; uInt iSizeVar; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uLong offset_local_extrafield; /* offset of the local extra field */ uInt size_local_extrafield; /* size of the local extra field */ # ifndef NOUNCRYPT char source[12]; # else if (password != NULL) return UNZ_PARAMERROR; # endif if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip_read_info_s*) ALLOC(sizeof(file_in_zip_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; pfile_in_zip_read_info->raw=raw; if (pfile_in_zip_read_info->read_buffer==NULL) { TRYFREE(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if (method!=NULL) *method = (int)s->cur_file_info.compression_method; if (level!=NULL) { *level = 6; switch (s->cur_file_info.flag & 0x06) { case 6 : *level = 1; break; case 4 : *level = 2; break; case 2 : *level = 9; break; } } if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->filestream=s->filestream; pfile_in_zip_read_info->z_filefunc=s->z_filefunc; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=1; else { TRYFREE(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; # ifndef NOUNCRYPT if (password != NULL) { int i; s->pcrc_32_tab = get_crc_table(); init_keys(password,s->keys,s->pcrc_32_tab); if (ZSEEK(s->z_filefunc, s->filestream, s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, SEEK_SET)!=0) return UNZ_INTERNALERROR; if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) return UNZ_INTERNALERROR; for (i = 0; i<12; i++) zdecode(s->keys,s->pcrc_32_tab,source[i]); s->pfile_in_zip_read->pos_in_zipfile+=12; s->encrypted=1; } # endif return UNZ_OK; } extern int ZEXPORT unzOpenCurrentFile (file) unzFile file; { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword (file, password) unzFile file; const char* password; { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) unzFile file; int* method; int* level; int raw; { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int ZEXPORT unzReadCurrentFile (file, buf, len) unzFile file; voidp buf; unsigned len; { int err=UNZ_OK; uInt iRead = 0; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && (!(pfile_in_zip_read_info->raw))) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; if ((len>pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in) && (pfile_in_zip_read_info->raw)) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (ZSEEK(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->read_buffer, uReadThis)!=uReadThis) return UNZ_ERRNO; # ifndef NOUNCRYPT if(s->encrypted) { uInt i; for(i=0;iread_buffer[i] = zdecode(s->keys,s->pcrc_32_tab, pfile_in_zip_read_info->read_buffer[i]); } # endif pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; } if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { uInt uDoCopy,i ; if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) return (iRead==0) ? UNZ_EOF : iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, pfile_in_zip_read_info->stream.next_out, uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else { uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell (file) unzFile file; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof (file) unzFile file; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) unzFile file; voidp buf; unsigned len; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uInt read_now; uLong size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (ZSEEK(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, buf,read_now)!=read_now) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile (file) unzFile file; { int err=UNZ_OK; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && (!pfile_in_zip_read_info->raw)) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised) inflateEnd(&pfile_in_zip_read_info->stream); pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) unzFile file; char *szComment; uLong uSizeBuf; { unz_s* s; uLong uReadThis ; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* Additions by RX '2004 */ extern uLong ZEXPORT unzGetOffset (file) unzFile file; { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return 0; if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) if (s->num_file==s->gi.number_entry) return 0; return s->pos_in_central_dir; } extern int ZEXPORT unzSetOffset (file, pos) unzFile file; uLong pos; { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } fritzing-app-0.9.3b/src/lib/quazip/unzip.h000066400000000000000000000312221272363601300204660ustar00rootroot00000000000000/* unzip.h -- IO for uncompress .zip files using zlib Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. Multi volume ZipFile (span) are not supported. Encryption compatible with pkzip 2.04g only supported Old compressions used by old PKZip 1.x are not supported I WAIT FEEDBACK at mail info@winimage.com Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* for more info about .ZIP format, see http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip http://www.info-zip.org/pub/infozip/doc/ PkWare has also a specification at : ftp://ftp.pkware.com/probdesc.zip */ #ifndef _unz_H #define _unz_H #ifdef __cplusplus extern "C" { #endif #include #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__ *unzFile; #else typedef voidp unzFile; #endif #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) /* tm_unz contain date/time info */ typedef struct tm_unz_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info_s { uLong number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ uLong compressed_size; /* compressed size 4 bytes */ uLong uncompressed_size; /* uncompressed size 4 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info; extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, const char* fileName2, int iCaseSensitivity)); /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen OF((const char *path)); /* Open a Zip file. path contain the full pathname (by example, on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile ZEXPORT unzOpen2 OF((const char *path, zlib_filefunc_def* pzlib_filefunc_def)); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose OF((unzFile file)); /* Close a ZipFile opened with unzipOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, unz_global_info *pglobal_info)); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalComment OF((unzFile file, char *szComment, uLong uSizeBuf)); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToNextFile OF((unzFile file)); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzLocateFile OF((unzFile file, const char *szFileName, int iCaseSensitivity)); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ /* ****************************************** */ /* Ryan supplied functions */ /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain somes info about the current file if szFileName!=NULL, the filemane string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, const char* password)); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, int* method, int* level, int raw)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, int* method, int* level, int raw, const char* password)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len)); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern z_off_t ZEXPORT unztell OF((unzFile file)); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof OF((unzFile file)); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, voidp buf, unsigned len)); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ /***************************************************************************/ /* Get the current file offset */ extern uLong ZEXPORT unzGetOffset (unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz_H */ fritzing-app-0.9.3b/src/lib/quazip/zip.c000066400000000000000000001111321272363601300201150ustar00rootroot00000000000000/* zip.c -- IO on .zip files using zlib Version 1.01e, February 12th, 2005 27 Dec 2004 Rolf Kalbermatter Modification to zipOpen2 to support globalComment retrieval. Copyright (C) 1998-2005 Gilles Vollant Read zip.h for more info */ #include #include #include #include #include #include "zip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef VERSIONMADEBY # define VERSIONMADEBY (0x0) /* platform depedent */ #endif #ifndef Z_BUFSIZE #define Z_BUFSIZE (16384) #endif #ifndef Z_MAXFILENAMEINZIP #define Z_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif /* #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) */ /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef DEF_MEM_LEVEL #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif #endif const char zip_copyright[] = " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; #define SIZEDATA_INDATABLOCK (4096-(4*4)) #define LOCALHEADERMAGIC (0x04034b50) #define CENTRALHEADERMAGIC (0x02014b50) #define ENDHEADERMAGIC (0x06054b50) #define FLAG_LOCALHEADER_OFFSET (0x06) #define CRC_LOCALHEADER_OFFSET (0x0e) #define SIZECENTRALHEADER (0x2e) /* 46 */ typedef struct linkedlist_datablock_internal_s { struct linkedlist_datablock_internal_s* next_datablock; uLong avail_in_this_block; uLong filled_in_this_block; uLong unused; /* for future use and alignement */ unsigned char data[SIZEDATA_INDATABLOCK]; } linkedlist_datablock_internal; typedef struct linkedlist_data_s { linkedlist_datablock_internal* first_block; linkedlist_datablock_internal* last_block; } linkedlist_data; typedef struct { z_stream stream; /* zLib stream structure for inflate */ int stream_initialised; /* 1 is stream is initialised */ uInt pos_in_buffered_data; /* last written byte in buffered_data */ uLong pos_local_header; /* offset of the local header of the file currenty writing */ char* central_header; /* central header data for the current file */ uLong size_centralheader; /* size of the central header for cur file */ uLong flag; /* flag of the file currently writing */ int method; /* compression method of file currenty wr.*/ int raw; /* 1 for directly writing raw data */ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ uLong dosDate; uLong crc32; int encrypt; #ifndef NOCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const unsigned long* pcrc_32_tab; int crypt_header_size; #endif } curfile_info; typedef struct { zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ linkedlist_data central_dir;/* datablock with central dir in construction*/ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ curfile_info ci; /* info on the file curretly writing */ uLong begin_pos; /* position of the beginning of the zipfile */ uLong add_position_when_writting_offset; uLong number_entry; #ifndef NO_ADDFILEINEXISTINGZIP char *globalcomment; #endif } zip_internal; #ifndef NOCRYPT #define INCLUDECRYPTINGCODE_IFCRYPTALLOWED #include "crypt.h" #endif local linkedlist_datablock_internal* allocate_new_datablock() { linkedlist_datablock_internal* ldi; ldi = (linkedlist_datablock_internal*) ALLOC(sizeof(linkedlist_datablock_internal)); if (ldi!=NULL) { ldi->next_datablock = NULL ; ldi->filled_in_this_block = 0 ; ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; } return ldi; } local void free_datablock(ldi) linkedlist_datablock_internal* ldi; { while (ldi!=NULL) { linkedlist_datablock_internal* ldinext = ldi->next_datablock; TRYFREE(ldi); ldi = ldinext; } } local void init_linkedlist(ll) linkedlist_data* ll; { ll->first_block = ll->last_block = NULL; } #if 0 // unused local void free_linkedlist(ll) linkedlist_data* ll; { free_datablock(ll->first_block); ll->first_block = ll->last_block = NULL; } #endif local int add_data_in_datablock(ll,buf,len) linkedlist_data* ll; const void* buf; uLong len; { linkedlist_datablock_internal* ldi; const unsigned char* from_copy; if (ll==NULL) return ZIP_INTERNALERROR; if (ll->last_block == NULL) { ll->first_block = ll->last_block = allocate_new_datablock(); if (ll->first_block == NULL) return ZIP_INTERNALERROR; } ldi = ll->last_block; from_copy = (unsigned char*)buf; while (len>0) { uInt copy_this; uInt i; unsigned char* to_copy; if (ldi->avail_in_this_block==0) { ldi->next_datablock = allocate_new_datablock(); if (ldi->next_datablock == NULL) return ZIP_INTERNALERROR; ldi = ldi->next_datablock ; ll->last_block = ldi; } if (ldi->avail_in_this_block < len) copy_this = (uInt)ldi->avail_in_this_block; else copy_this = (uInt)len; to_copy = &(ldi->data[ldi->filled_in_this_block]); for (i=0;ifilled_in_this_block += copy_this; ldi->avail_in_this_block -= copy_this; from_copy += copy_this ; len -= copy_this; } return ZIP_OK; } /****************************************************************************/ #ifndef NO_ADDFILEINEXISTINGZIP /* =========================================================================== Inputs a long in LSB order to the given file nbByte == 1, 2 or 4 (byte, short or long) */ local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong x, int nbByte)); local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong x; int nbByte; { unsigned char buf[4]; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 (X Roche) */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) return ZIP_ERRNO; else return ZIP_OK; } local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte)); local void ziplocal_putValue_inmemory (dest, x, nbByte) void* dest; uLong x; int nbByte; { unsigned char* buf=(unsigned char*)dest; int n; for (n = 0; n < nbByte; n++) { buf[n] = (unsigned char)(x & 0xff); x >>= 8; } if (x != 0) { /* data overflow - hack for ZIP64 */ for (n = 0; n < nbByte; n++) { buf[n] = 0xff; } } } /****************************************************************************/ local uLong ziplocal_TmzDateToDosDate(ptm,dosDate) const tm_zip* ptm; uLong dosDate; { #ifndef _WINDOWS (void) dosDate; /* avoid "unused parameter" warning */ #endif uLong year = (uLong)ptm->tm_year; if (year>1980) year-=1980; else if (year>80) year-=80; return (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); } /****************************************************************************/ local int ziplocal_getByte OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; int *pi; { unsigned char c; int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return ZIP_OK; } else { if (ZERROR(*pzlib_filefunc_def,filestream)) return ZIP_ERRNO; else return ZIP_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int ziplocal_getShort OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i; int err; err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } local int ziplocal_getLong OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i; int err; err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==ZIP_OK) err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==ZIP_OK) err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==ZIP_OK) err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==ZIP_OK) *pX = x; else *pX = 0; return err; } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local uLong ziplocal_SearchCentralDir OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream)); local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; { unsigned char* buf; uLong uSizeFile; uLong uBackRead; uLong uMaxBack=0xffff; /* maximum size of global comment */ uLong uPosFound=0; if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } #endif /* !NO_ADDFILEINEXISTINGZIP*/ /************************************************************/ extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def) const char *pathname; int append; zipcharpc* globalcomment; zlib_filefunc_def* pzlib_filefunc_def; { zip_internal ziinit; zip_internal* zi; int err=ZIP_OK; if (pzlib_filefunc_def==NULL) fill_fopen_filefunc(&ziinit.z_filefunc); else ziinit.z_filefunc = *pzlib_filefunc_def; ziinit.filestream = (*(ziinit.z_filefunc.zopen_file)) (ziinit.z_filefunc.opaque, pathname, (append == APPEND_STATUS_CREATE) ? (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); if (ziinit.filestream == NULL) return NULL; ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream); ziinit.in_opened_file_inzip = 0; ziinit.ci.stream_initialised = 0; ziinit.number_entry = 0; ziinit.add_position_when_writting_offset = 0; init_linkedlist(&(ziinit.central_dir)); zi = (zip_internal*)ALLOC(sizeof(zip_internal)); if (zi==NULL) { ZCLOSE(ziinit.z_filefunc,ziinit.filestream); return NULL; } /* now we add file in a zipfile */ # ifndef NO_ADDFILEINEXISTINGZIP ziinit.globalcomment = NULL; if (append == APPEND_STATUS_ADDINZIP) { uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ uLong size_central_dir; /* size of the central directory */ uLong offset_central_dir; /* offset of start of central directory */ uLong central_pos,uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ uLong number_entry; uLong number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ uLong size_comment; central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); if (central_pos==0) err=ZIP_ERRNO; if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; /* the signature, already checked */ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK) err=ZIP_ERRNO; /* number of this disk */ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK) err=ZIP_ERRNO; /* number of the disk with the start of the central directory */ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central dir on this disk */ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK) err=ZIP_ERRNO; /* total number of entries in the central dir */ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK) err=ZIP_ERRNO; if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=ZIP_BADZIPFILE; /* size of the central directory */ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK) err=ZIP_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK) err=ZIP_ERRNO; /* zipfile global comment length */ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK) err=ZIP_ERRNO; if ((central_pos0) { ziinit.globalcomment = ALLOC(size_comment+1); if (ziinit.globalcomment) { size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); ziinit.globalcomment[size_comment]=0; } } byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); ziinit.add_position_when_writting_offset = byte_before_the_zipfile; { uLong size_central_dir_to_read = size_central_dir; size_t buf_size = SIZEDATA_INDATABLOCK; void* buf_read = (void*)ALLOC(buf_size); if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) err=ZIP_ERRNO; while ((size_central_dir_to_read>0) && (err==ZIP_OK)) { uLong read_this = SIZEDATA_INDATABLOCK; if (read_this > size_central_dir_to_read) read_this = size_central_dir_to_read; if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this) err=ZIP_ERRNO; if (err==ZIP_OK) err = add_data_in_datablock(&ziinit.central_dir,buf_read, (uLong)read_this); size_central_dir_to_read-=read_this; } TRYFREE(buf_read); } ziinit.begin_pos = byte_before_the_zipfile; ziinit.number_entry = number_entry_CD; if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) err=ZIP_ERRNO; } if (globalcomment) { *globalcomment = ziinit.globalcomment; } # endif /* !NO_ADDFILEINEXISTINGZIP*/ if (err != ZIP_OK) { # ifndef NO_ADDFILEINEXISTINGZIP TRYFREE(ziinit.globalcomment); # endif /* !NO_ADDFILEINEXISTINGZIP*/ TRYFREE(zi); return NULL; } else { *zi = ziinit; return (zipFile)zi; } } extern zipFile ZEXPORT zipOpen (pathname, append) const char *pathname; int append; { return zipOpen2(pathname,append,NULL,NULL); } extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, windowBits, memLevel, strategy, password, crcForCrypting) zipFile file; const char* filename; const zip_fileinfo* zipfi; const void* extrafield_local; uInt size_extrafield_local; const void* extrafield_global; uInt size_extrafield_global; const char* comment; int method; int level; int raw; int windowBits; int memLevel; int strategy; const char* password; uLong crcForCrypting; { zip_internal* zi; uInt size_filename; uInt size_comment; uInt i; int err = ZIP_OK; # ifdef NOCRYPT if (password != NULL) return ZIP_PARAMERROR; # endif if (file == NULL) return ZIP_PARAMERROR; if ((method!=0) && (method!=Z_DEFLATED)) return ZIP_PARAMERROR; zi = (zip_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); if (err != ZIP_OK) return err; } if (filename==NULL) filename="-"; if (comment==NULL) size_comment = 0; else size_comment = (uInt)strlen(comment); size_filename = (uInt)strlen(filename); if (zipfi == NULL) zi->ci.dosDate = 0; else { if (zipfi->dosDate != 0) zi->ci.dosDate = zipfi->dosDate; else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); } zi->ci.flag = 0; if ((level==8) || (level==9)) zi->ci.flag |= 2; if ((level==2)) zi->ci.flag |= 4; if ((level==1)) zi->ci.flag |= 6; if (password != NULL) zi->ci.flag |= 1; zi->ci.crc32 = 0; zi->ci.method = method; zi->ci.encrypt = 0; zi->ci.stream_initialised = 0; zi->ci.pos_in_buffered_data = 0; zi->ci.raw = raw; zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ; zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader); ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); /* version info */ ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ if (zipfi==NULL) ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); else ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); if (zipfi==NULL) ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); else ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4); for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = *(((const char*)extrafield_global)+i); for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ size_extrafield_global+i) = *(comment+i); if (zi->ci.central_header == NULL) return ZIP_INTERNALERROR; /* write the local header */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4); if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2); if ((err==ZIP_OK) && (size_filename>0)) if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) err = ZIP_ERRNO; if ((err==ZIP_OK) && (size_extrafield_local>0)) if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local) !=size_extrafield_local) err = ZIP_ERRNO; zi->ci.stream.avail_in = (uInt)0; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; zi->ci.stream.total_in = 0; zi->ci.stream.total_out = 0; if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { zi->ci.stream.zalloc = (alloc_func)0; zi->ci.stream.zfree = (free_func)0; zi->ci.stream.opaque = (voidpf)0; if (windowBits>0) windowBits = -windowBits; err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); if (err==Z_OK) zi->ci.stream_initialised = 1; } # ifndef NOCRYPT zi->ci.crypt_header_size = 0; if ((err==Z_OK) && (password != NULL)) { unsigned char bufHead[RAND_HEAD_LEN]; unsigned int sizeHead; zi->ci.encrypt = 1; zi->ci.pcrc_32_tab = get_crc_table(); /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); zi->ci.crypt_header_size = sizeHead; if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) err = ZIP_ERRNO; } # endif if (err==Z_OK) zi->in_opened_file_inzip = 1; return err; } extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw) zipFile file; const char* filename; const zip_fileinfo* zipfi; const void* extrafield_local; uInt size_extrafield_local; const void* extrafield_global; uInt size_extrafield_global; const char* comment; int method; int level; int raw; { return zipOpenNewFileInZip3 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, raw, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0); } extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level) zipFile file; const char* filename; const zip_fileinfo* zipfi; const void* extrafield_local; uInt size_extrafield_local; const void* extrafield_global; uInt size_extrafield_global; const char* comment; int method; int level; { return zipOpenNewFileInZip2 (file, filename, zipfi, extrafield_local, size_extrafield_local, extrafield_global, size_extrafield_global, comment, method, level, 0); } local int zipFlushWriteBuffer(zi) zip_internal* zi; { int err=ZIP_OK; if (zi->ci.encrypt != 0) { #ifndef NOCRYPT uInt i; int t; for (i=0;ici.pos_in_buffered_data;i++) zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); #endif } if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) !=zi->ci.pos_in_buffered_data) err = ZIP_ERRNO; zi->ci.pos_in_buffered_data = 0; return err; } extern int ZEXPORT zipWriteInFileInZip (file, buf, len) zipFile file; const void* buf; unsigned len; { zip_internal* zi; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.stream.next_in = (void*)buf; zi->ci.stream.avail_in = len; zi->ci.crc32 = crc32(zi->ci.crc32,buf,len); while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) { if (zi->ci.stream.avail_out == 0) { if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } if(err != ZIP_OK) break; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { uLong uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_NO_FLUSH); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } else { uInt copy_this,i; if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) copy_this = zi->ci.stream.avail_in; else copy_this = zi->ci.stream.avail_out; for (i=0;ici.stream.next_out)+i) = *(((const char*)zi->ci.stream.next_in)+i); { zi->ci.stream.avail_in -= copy_this; zi->ci.stream.avail_out-= copy_this; zi->ci.stream.next_in+= copy_this; zi->ci.stream.next_out+= copy_this; zi->ci.stream.total_in+= copy_this; zi->ci.stream.total_out+= copy_this; zi->ci.pos_in_buffered_data += copy_this; } } } return err; } extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32) zipFile file; uLong uncompressed_size; uLong crc32; { zip_internal* zi; uLong compressed_size; int err=ZIP_OK; if (file == NULL) return ZIP_PARAMERROR; zi = (zip_internal*)file; if (zi->in_opened_file_inzip == 0) return ZIP_PARAMERROR; zi->ci.stream.avail_in = 0; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) while (err==ZIP_OK) { uLong uTotalOutBefore; if (zi->ci.stream.avail_out == 0) { if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) err = ZIP_ERRNO; zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; zi->ci.stream.next_out = zi->ci.buffered_data; } uTotalOutBefore = zi->ci.stream.total_out; err=deflate(&zi->ci.stream, Z_FINISH); zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; } if (err==Z_STREAM_END) err=ZIP_OK; /* this is normal */ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) if (zipFlushWriteBuffer(zi)==ZIP_ERRNO) err = ZIP_ERRNO; if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) { err=deflateEnd(&zi->ci.stream); zi->ci.stream_initialised = 0; } if (!zi->ci.raw) { crc32 = (uLong)zi->ci.crc32; uncompressed_size = (uLong)zi->ci.stream.total_in; } compressed_size = (uLong)zi->ci.stream.total_out; # ifndef NOCRYPT compressed_size += zi->ci.crypt_header_size; # endif ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ ziplocal_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ if (zi->ci.stream.data_type == Z_ASCII) ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); ziplocal_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ if (err==ZIP_OK) err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header, (uLong)zi->ci.size_centralheader); free(zi->ci.central_header); if (err==ZIP_OK) { long cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); if (ZSEEK(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; if (err==ZIP_OK) err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ if (err==ZIP_OK) /* compressed size, unknown */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); if (err==ZIP_OK) /* uncompressed size, unknown */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); if (ZSEEK(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) err = ZIP_ERRNO; } zi->number_entry ++; zi->in_opened_file_inzip = 0; return err; } extern int ZEXPORT zipCloseFileInZip (file) zipFile file; { return zipCloseFileInZipRaw (file,0,0); } extern int ZEXPORT zipClose (file, global_comment) zipFile file; const char* global_comment; { zip_internal* zi; int err = 0; uLong size_centraldir = 0; uLong centraldir_pos_inzip; uInt size_global_comment; if (file == NULL) return ZIP_PARAMERROR; zi = (zip_internal*)file; if (zi->in_opened_file_inzip == 1) { err = zipCloseFileInZip (file); } #ifndef NO_ADDFILEINEXISTINGZIP if (global_comment==NULL) global_comment = zi->globalcomment; #endif if (global_comment==NULL) size_global_comment = 0; else size_global_comment = (uInt)strlen(global_comment); centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream); if (err==ZIP_OK) { linkedlist_datablock_internal* ldi = zi->central_dir.first_block ; while (ldi!=NULL) { if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) if (ZWRITE(zi->z_filefunc,zi->filestream, ldi->data,ldi->filled_in_this_block) !=ldi->filled_in_this_block ) err = ZIP_ERRNO; size_centraldir += ldi->filled_in_this_block; ldi = ldi->next_datablock; } } free_datablock(zi->central_dir.first_block); if (err==ZIP_OK) /* Magic End */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); if (err==ZIP_OK) /* number of this disk */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* number of the disk with the start of the central directory */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); if (err==ZIP_OK) /* total number of entries in the central dir */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); if (err==ZIP_OK) /* size of the central directory */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); if (err==ZIP_OK) /* zipfile comment length */ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); if ((err==ZIP_OK) && (size_global_comment>0)) if (ZWRITE(zi->z_filefunc,zi->filestream, global_comment,size_global_comment) != size_global_comment) err = ZIP_ERRNO; if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0) if (err == ZIP_OK) err = ZIP_ERRNO; #ifndef NO_ADDFILEINEXISTINGZIP TRYFREE(zi->globalcomment); #endif TRYFREE(zi); return err; } fritzing-app-0.9.3b/src/lib/quazip/zip.h000066400000000000000000000212211272363601300201210ustar00rootroot00000000000000/* zip.h -- IO for compress .zip files using zlib Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant This unzip package allow creates .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. Multi volume ZipFile (span) are not supported. Encryption compatible with pkzip 2.04g only supported Old compressions used by old PKZip 1.x are not supported For uncompress .zip file, look at unzip.h I WAIT FEEDBACK at mail info@winimage.com Visit also http://www.winimage.com/zLibDll/unzip.html for evolution Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* for more info about .ZIP format, see http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip http://www.info-zip.org/pub/infozip/doc/ PkWare has also a specification at : ftp://ftp.pkware.com/probdesc.zip */ #ifndef _zip_H #define _zip_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #if defined(STRICTZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagzipFile__ { int unused; } zipFile__; typedef zipFile__ *zipFile; #else typedef voidp zipFile; #endif #define ZIP_OK (0) #define ZIP_EOF (0) #define ZIP_ERRNO (Z_ERRNO) #define ZIP_PARAMERROR (-102) #define ZIP_BADZIPFILE (-103) #define ZIP_INTERNALERROR (-104) #ifndef DEF_MEM_LEVEL # if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 # else # define DEF_MEM_LEVEL MAX_MEM_LEVEL # endif #endif /* default memLevel */ /* tm_zip contain date/time info */ typedef struct tm_zip_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_zip; typedef struct { tm_zip tmz_date; /* date in understandable format */ uLong dosDate; /* if dos_date == 0, tmu_date is used */ /* uLong flag; */ /* general purpose bit flag 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ } zip_fileinfo; typedef const char* zipcharpc; #define APPEND_STATUS_CREATE (0) #define APPEND_STATUS_CREATEAFTER (1) #define APPEND_STATUS_ADDINZIP (2) extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); /* Create a zipfile. pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip will be created at the end of the file. (useful if the file contain a self extractor code) if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will add files in existing zip (be sure you don't add file that doesn't exist) If the zipfile cannot be opened, the return value is NULL. Else, the return value is a zipFile Handle, usable with other function of this zip package. */ /* Note : there is no delete function into a zipfile. If you want delete file into a zipfile, you must open a zipfile, and create another Of couse, you can use RAW reading and writing to copy the file you did not want delte */ extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc_def)); extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level)); /* Open a file in the ZIP for writing. filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local contains the extrafield data the the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data the the local header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) */ extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw)); /* Same than zipOpenNewFileInZip, except if raw=1, we write raw file */ extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, const char* filename, const zip_fileinfo* zipfi, const void* extrafield_local, uInt size_extrafield_local, const void* extrafield_global, uInt size_extrafield_global, const char* comment, int method, int level, int raw, int windowBits, int memLevel, int strategy, const char* password, uLong crcForCtypting)); /* Same than zipOpenNewFileInZip2, except windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 password : crypting password (NULL for no crypting) crcForCtypting : crc of file to compress (needed for crypting) */ extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, const void* buf, unsigned len)); /* Write data in the zipfile */ extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); /* Close the current file in the zipfile */ extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, uLong uncompressed_size, uLong crc32)); /* Close the current file in the zipfile, for fiel opened with parameter raw=1 in zipOpenNewFileInZip2 uncompressed_size and crc32 are value for the uncompressed size */ extern int ZEXPORT zipClose OF((zipFile file, const char* global_comment)); /* Close the zipfile */ #ifdef __cplusplus } #endif #endif /* _zip_H */ fritzing-app-0.9.3b/src/main.cpp000066400000000000000000000166001272363601300165240ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include "fapplication.h" #include "version/version.h" #include "debugdialog.h" #include "utils/folderutils.h" #ifdef Q_OS_WIN #ifndef QT_NO_DEBUG #include "windows.h" #endif #endif #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QtMsgHandler originalMsgHandler; #define ORIGINAL_MESSAGE_HANDLER(TYPE, MSG) originalMsgHandler((TYPE), (MSG)) #else QtMessageHandler originalMsgHandler; #define ORIGINAL_MESSAGE_HANDLER(TYPE, MSG) originalMsgHandler((TYPE), context, (MSG)) #endif void writeCrashMessage(const char * msg) { QString path = FolderUtils::getTopLevelUserDataStorePath(); path += "/fritzingcrash.txt"; QFile file(path); if (file.open(QIODevice::Append | QIODevice::Text)) { QTextStream out(&file); out << QString(msg) << "\n"; file.close(); } } #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) void writeCrashMessage(const QString & msg) { writeCrashMessage(msg.toStdString().c_str()); } #endif #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) void fMessageHandler(QtMsgType type, const char *msg) #else void fMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString & msg) #endif { switch (type) { case QtDebugMsg: ORIGINAL_MESSAGE_HANDLER(type, msg); break; case QtWarningMsg: ORIGINAL_MESSAGE_HANDLER(type, msg); break; case QtCriticalMsg: ORIGINAL_MESSAGE_HANDLER(type, msg); break; case QtFatalMsg: { writeCrashMessage(msg); } // don't abort ORIGINAL_MESSAGE_HANDLER(QtWarningMsg, msg); } } int main(int argc, char *argv[]) { #ifdef _MSC_VER // just for the MS compiler #define WIN_CHECK_LEAKS #endif #ifdef Q_OS_WIN #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) originalMsgHandler = qInstallMsgHandler(fMessageHandler); #else originalMsgHandler = qInstallMessageHandler(fMessageHandler); #endif #ifndef QT_NO_DEBUG #ifdef WIN_CHECK_LEAKS HANDLE hLogFile; QString path = FolderUtils::getTopLevelUserDataStorePath() + "/fritzing_leak_log.txt"; std::wstring wstr = path.toStdWString(); LPCWSTR ptr = wstr.c_str(); hLogFile = CreateFile(ptr, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, hLogFile); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, hLogFile); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, hLogFile); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); //_CrtSetBreakAlloc(323809); // sets a break when this memory id is allocated #endif #endif #endif int result = 0; try { //QApplication::setGraphicsSystem("raster"); #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif FApplication * app = new FApplication(argc, argv); if (app->init()) { //DebugDialog::setDebugLevel(DebugDialog::Error); if (app->runAsService()) { // for example: -g C:\Users\jonathan\fritzing2\fz\Test_multiple.fz -go C:\Users\jonathan\fritzing2\fz\gerber result = app->serviceStartup(); if (result == 1) { result = app->exec(); } } else { result = app->startup(); if (result == 0) { result = app->exec(); } } app->finish(); } else { qDebug() << "\n" "Fritzing version " << Version::versionString() << " , Qt version " << QT_VERSION_STR << "\n" "\n" "usage: fritzing [-d] [-f path] filename\n" " fritzing [-f path] -geda folder\n" " fritzing [-f path] -gerber folder\n" " fritzing [-f path] -kicad folder\n" " fritzing [-f path] -kicadschematic folder\n" " fritzing [-f path] -svg folder\n" " fritzing [-f path] -port number\n" "\n" "user options:\n" " d,debug : runs Fritzing in debug mode, providing additional debug information\n" //" drc filename : runs a design rule check on the given sketch file\n" " f,folder : path to folder containing Fritzing parts, sketches, bins, & translations folders}]\n" " geda path : converts all gEDA footprint (.fp) files in folder to Fritzing SVGs}]\n" " g,gerber path : exports all sketches in folder to Gerber, in the same folder\n" " h,help : print this help message\n" " kicad path : converts all Kicad footprint (.mod) files in folder to Fritzing SVGs}]\n" " kicadschematic path: converts all Kicad schematic (.lib) files in folder to Fritzing SVGs}]\n" " port : runs Fritzing as a server process under \n" " svg path : exports all sketches in folder to SVGs of all views, in the same folder\n" "\n" "developer options:\n" " db path : rebuilds the internal parts database at the given path\n" " e,examples path : prepares all sketches in the folder to be included as examples\n" " ep path : external process at \n" " eparg args : external process arguments\n" " epname name : external process menu item name\n" "\n" "The -geda/-kicad/-kicadschematic/-gerber/svg options all exit Fritzing after the conversion process is complete;\n" "these options are mutually exclusive.\n" "\n" "Usually, the Fritzing executable is stored in the same folder that contains the parts/bins/sketches/translations folders,\n" "or the executable is in a child folder of the p/b/s/t folder.\n" "If this is not the case, use the -f option to point to the p/b/s/t folder.\n" "\n" "The -ep option creates a menu item to launch an external process,\n" "and puts the standard output of that process into a dialog window in Fritzing.\n" "The process path follows the -ep argument; the name of the menu item follows the -epname argument;\n" "and any arguments to pass to the external process are provided in the -eparg argments.\n" "\n"; } delete app; } catch (char const *str) { writeCrashMessage(str); } catch (...) { result = -1; } return result; } fritzing-app-0.9.3b/src/mainwindow/000077500000000000000000000000001272363601300172455ustar00rootroot00000000000000fritzing-app-0.9.3b/src/mainwindow/fdockwidget.cpp000066400000000000000000000040251272363601300222440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "fdockwidget.h" #include "../debugdialog.h" FDockWidget::FDockWidget( const QString & title, QWidget * parent) : QDockWidget(title, parent) { m_state = false; setObjectName(title.trimmed().toLower().remove(" ")); setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); } void FDockWidget::saveState() { m_state = this->isFloating() && this->isVisible(); //DebugDialog::debug(QString("dock save state %1 %2").arg(windowTitle()).arg(m_state)); } void FDockWidget::restoreState() { //DebugDialog::debug(QString("dock restore state %1 %2").arg(windowTitle()).arg(m_state)); if (m_state) { this->setVisible(true); } } bool FDockWidget::event(QEvent * e) { switch (e->type()) { case QEvent::WindowActivate: emit dockChangeActivationSignal(true, this); break; case QEvent::WindowDeactivate: emit dockChangeActivationSignal(false, this); break; default: break; } return QDockWidget::event(e); } void FDockWidget::moveEvent(QMoveEvent *event) { QDockWidget::moveEvent(event); emit positionChanged(); } fritzing-app-0.9.3b/src/mainwindow/fdockwidget.h000066400000000000000000000030031272363601300217040ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FDOCKWIDGET_H #define FDOCKWIDGET_H #include #include #include #include class FDockWidget : public QDockWidget { Q_OBJECT public: FDockWidget(const QString & title, QWidget * parent = 0); void saveState(); public slots: void restoreState(); protected: bool event(QEvent *event); void moveEvent(QMoveEvent *); signals: void dockChangeActivationSignal(bool activate, QWidget * originator); void positionChanged(); protected: bool m_state; QString m_name; }; #endif fritzing-app-0.9.3b/src/mainwindow/fritzingwindow.cpp000066400000000000000000000174641272363601300230510ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6609 $: $Author: irascibl@gmail.com $: $Date: 2012-10-30 14:21:08 +0100 (Di, 30. Okt 2012) $ ********************************************************************/ #include #include #include #include #include #include #include "fritzingwindow.h" #include "../debugdialog.h" #include "../utils/misc.h" #include "../utils/folderutils.h" #include "../utils/fileprogressdialog.h" const QString FritzingWindow::QtFunkyPlaceholder = QLatin1String("[*]"); // this is some weird hack Qt uses in window titles as a placeholder to setr the modified state QString FritzingWindow::ReadOnlyPlaceholder(" [READ-ONLY] "); static QString ___fritzingTitle___; QStringList FritzingWindow::OtherKnownExtensions; FritzingWindow::FritzingWindow(const QString &untitledFileName, int &untitledFileCount, QString fileExt, QWidget * parent, Qt::WindowFlags f) : QMainWindow(parent, f) { ___fritzingTitle___ = QObject::tr("Fritzing"); m_readOnly = false; // Let's set the icon this->setWindowIcon(QIcon(QPixmap(":resources/images/fritzing_icon.png"))); QString fn = untitledFileName; if(untitledFileCount > 1) { fn += " " + QString::number(untitledFileCount); } fn += fileExt; setFileName(fn); untitledFileCount++; setTitle(); m_undoStack = new WaitPushUndoStack(this); connect(m_undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(undoStackCleanChanged(bool)) ); } void FritzingWindow::createCloseAction() { m_closeAct = new QAction(tr("&Close Window"), this); m_closeAct->setShortcut(tr("Ctrl+W")); m_closeAct->setStatusTip(tr("Close the current sketch")); connect(m_closeAct, SIGNAL(triggered()), this, SLOT(close())); } void FritzingWindow::setTitle() { setWindowTitle(tr("%1 - %2") .arg(QFileInfo(m_fwFilename).fileName()+(m_readOnly?ReadOnlyPlaceholder:"")+QtFunkyPlaceholder) .arg(fritzingTitle())); } // returns true if the user wanted to save the file bool FritzingWindow::save() { bool result; if (FolderUtils::isEmptyFileName(m_fwFilename, untitledFileName())) { result = saveAs(); } else if (m_readOnly) { result = saveAs(); } else { result = saveAsAux(m_fwFilename); } return result; } bool FritzingWindow::saveAs() { return saveAs(m_fwFilename, m_readOnly); } bool FritzingWindow::save(const QString & filename, bool readOnly) { if (FolderUtils::isEmptyFileName(filename, untitledFileName())) { return saveAs(filename, readOnly); } else if (m_readOnly) { return saveAs(filename, readOnly); } else { return saveAsAux(filename); } } bool FritzingWindow::saveAs(const QString & filename, bool readOnly) { DebugDialog::debug(QString("current path: %1").arg(QDir::currentPath())); QString fileExt; QString path; QString untitledBase = untitledFileName(); if(readOnly) { path = defaultSaveFolder() + "/" + QFileInfo(filename).fileName(); } else if(filename.startsWith(untitledBase, Qt::CaseInsensitive)) { path = defaultSaveFolder() + "/" + filename; } else if(filename.isNull() || filename.isEmpty()) { path = defaultSaveFolder(); } else { path = filename; } DebugDialog::debug(QString("current file: %1").arg(filename)); QString newFilename = FolderUtils::getSaveFileName( this, tr("Specify a file name"), path, getExtensionString(), &fileExt ); if (newFilename.isEmpty()) return false; // Cancel pressed if (readOnly && (newFilename.compare(filename, Qt::CaseInsensitive) == 0)) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("The file '%1' is read-only; please use a different filename.") .arg(newFilename) ); return false; } FileProgressDialog progress("Saving...", 0, this); QStringList extensions = getExtensions(); bool hasExtension = false; foreach (QString extension, extensions) { if(alreadyHasExtension(newFilename, extension)) { hasExtension = true; break; } } if (!hasExtension) { newFilename += extensions[0]; } return saveAsAux(newFilename); } void FritzingWindow::undoStackCleanChanged(bool isClean) { setWindowModified(!isClean); } bool FritzingWindow::alreadyHasExtension(const QString &fileName, const QString &fileExt) { // TODO: Make something prettier to manage all the supported formats at once if(!fileExt.isEmpty()) { return fileName.endsWith(fileExt); } else { foreach (QString extension, fritzingExtensions()) { if (fileName.endsWith(extension)) return true; } foreach (QString extension, OtherKnownExtensions) { if (fileName.endsWith(extension)) return true; } return false; } } bool FritzingWindow::anyModified() { return isWindowModified(); } bool FritzingWindow::beforeClosing(bool showCancel, bool & discard) { discard = false; if (anyModified()) { QMessageBox::StandardButton reply = beforeClosingMessage(m_fwFilename, showCancel); if (reply == QMessageBox::Save) { return save(); } else if (reply == QMessageBox::Discard) { discard = true; return true; } else { return false; } } else { return true; } } QMessageBox::StandardButton FritzingWindow::beforeClosingMessage(const QString & filename, bool showCancel) { QMessageBox messageBox(this); setBeforeClosingText(filename, messageBox); QMessageBox::StandardButtons buttons = QMessageBox::Save | QMessageBox::Discard; if (showCancel) { buttons |= QMessageBox::Cancel; } messageBox.setStandardButtons(buttons); messageBox.setDefaultButton(QMessageBox::Save); if (m_fwFilename.startsWith(untitledFileName())) { messageBox.setButtonText(QMessageBox::Save, tr("Save...")); } else { messageBox.setButtonText(QMessageBox::Save, tr("Save")); } messageBox.setButtonText(QMessageBox::Discard, tr("Don't Save")); if (showCancel) { messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel")); } messageBox.setIcon(QMessageBox::Warning); messageBox.setWindowModality(Qt::WindowModal); messageBox.button(QMessageBox::Discard)->setShortcut(tr("Ctrl+D")); return (QMessageBox::StandardButton) messageBox.exec(); } void FritzingWindow::setBeforeClosingText(const QString & filename, QMessageBox & messageBox) { QString basename = QFileInfo(filename).fileName(); messageBox.setWindowTitle(tr("Save \"%1\"").arg(basename)); messageBox.setText(tr("Do you want to save the changes you made in the document \"%1\"?").arg(basename)); messageBox.setInformativeText(tr("Your changes will be lost if you don't save them.")); } void FritzingWindow::setReadOnly(bool readOnly) { bool hasChanged = m_readOnly != readOnly; m_readOnly = readOnly; if(hasChanged) { emit readOnlyChanged(readOnly); } } const QString FritzingWindow::fritzingTitle() { return ___fritzingTitle___; } const QString & FritzingWindow::fileName() { return m_fwFilename; } void FritzingWindow::setFileName(const QString & filename) { m_fwFilename = filename; } void FritzingWindow::notClosableForAWhile() { } fritzing-app-0.9.3b/src/mainwindow/fritzingwindow.h000066400000000000000000000057361272363601300225150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6496 $: $Author: irascibl@gmail.com $: $Date: 2012-09-30 02:00:34 +0200 (So, 30. Sep 2012) $ ********************************************************************/ #ifndef FRITZINGWINDOW_H_ #define FRITZINGWINDOW_H_ #include #include #include #include #include "../waitpushundostack.h" #include "../utils/misc.h" #include "../utils/bundler.h" class FritzingWindow : public QMainWindow, public Bundler { Q_OBJECT public: FritzingWindow(const QString &untitledFileName, int &untitledFileCount, QString fileExt, QWidget * parent = 0, Qt::WindowFlags f = 0); const QString &fileName(); void setFileName(const QString &); virtual void notClosableForAWhile(); static bool alreadyHasExtension(const QString &fileName, const QString &extension=___emptyString___); signals: void readOnlyChanged(bool isReadOnly); protected slots: void undoStackCleanChanged(bool isClean); virtual bool save(); virtual bool saveAs(); protected: bool save(const QString & filename, bool readOnly); bool saveAs(const QString & filename, bool readOnly); virtual void setTitle(); virtual const QString fritzingTitle(); virtual const QString fileExtension() = 0; virtual const QString untitledFileName() = 0; virtual int &untitledFileCount() = 0; virtual const QString defaultSaveFolder() = 0; virtual QString getExtensionString() = 0; virtual QStringList getExtensions() = 0; virtual bool saveAsAux(const QString & fileName) = 0; virtual bool beforeClosing(bool showCancel, bool & discard); // returns true if close, false if cancel QMessageBox::StandardButton beforeClosingMessage(const QString & filename, bool showCancel); virtual void setBeforeClosingText(const QString & filename, QMessageBox & messageBox); void createCloseAction(); void setReadOnly(bool readOnly); virtual bool anyModified(); protected: class WaitPushUndoStack * m_undoStack; QString m_fwFilename; bool m_readOnly; QAction *m_closeAct; QDir m_tempDir; QStatusBar *m_statusBar; protected: static QStringList OtherKnownExtensions; public: static QString ReadOnlyPlaceholder; static const QString QtFunkyPlaceholder; }; #endif /* FRITZINGWINDOW_H_ */ fritzing-app-0.9.3b/src/mainwindow/mainwindow.cpp000066400000000000000000003243761272363601300221440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6995 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 00:56:34 +0200 (So, 28. Apr 2013) $lo ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "../debugdialog.h" #include "../connectors/connector.h" #include "../partsbinpalette/partsbinpalettewidget.h" #include "fdockwidget.h" #include "../infoview/htmlinfoview.h" #include "../waitpushundostack.h" #include "../layerattributes.h" #include "../sketch/breadboardsketchwidget.h" #include "../sketch/schematicsketchwidget.h" #include "../sketch/pcbsketchwidget.h" #include "../sketch/welcomeview.h" #include "../svg/svgfilesplitter.h" #include "../utils/folderutils.h" #include "../utils/fmessagebox.h" #include "../utils/lockmanager.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../items/mysterypart.h" #include "../items/moduleidnames.h" #include "../items/pinheader.h" #include "../items/perfboard.h" #include "../items/stripboard.h" #include "../items/partfactory.h" #include "../dock/layerpalette.h" #include "../items/paletteitem.h" #include "../items/virtualwire.h" #include "../items/screwterminal.h" #include "../items/dip.h" #include "../processeventblocker.h" #include "../sketchtoolbutton.h" #include "../partsbinpalette/binmanager/binmanager.h" #include "../fsvgrenderer.h" #include "../utils/fsizegrip.h" #include "../utils/expandinglabel.h" #include "../utils/autoclosemessagebox.h" #include "../utils/fileprogressdialog.h" #include "../utils/clickablelabel.h" #include "../items/resizableboard.h" #include "../items/resistor.h" #include "../items/logoitem.h" #include "../utils/zoomslider.h" #include "../partseditor/pemainwindow.h" #include "../help/firsttimehelpdialog.h" FTabWidget::FTabWidget(QWidget * parent) : QTabWidget(parent) { QTabBar * tabBar = new FTabBar; tabBar->setObjectName("mainTabBar"); //connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabIndexChanged(int))); setTabBar(tabBar); } //int FTabWidget::addTab(QWidget * page, const QIcon & icon, const QIcon & hoverIcon, const QIcon & inactiveIcon, const QString & label) //{ // // assumes tabs are not deleted or reordered // m_inactiveIcons << inactiveIcon; // m_hoverIcons << hoverIcon; // m_icons << icon; // return QTabWidget::addTab(page, icon, label); //} //void FTabWidget::tabIndexChanged(int index) { // for (int i = 0; i < this->count(); ++i) { // if (i == index) setTabIcon(i, m_icons.at(i)); // else setTabIcon(i, m_inactiveIcons.at(i)); // } //} FTabBar::FTabBar() : QTabBar() { m_firstTime = true; } void FTabBar::paintEvent(QPaintEvent * event) { // this is a hack to left-align the tab text by adding spaces to the text // center-alignment is hard-coded deep into the way the tab is drawn in qcommonstyle.cpp static int offset = 15; // derived this empirically, no idea where it comes from if (m_firstTime) { m_firstTime = false; // TODO: how to append spaces from the language direction for (int i = 0; i < this->count(); ++i) { QStyleOptionTabV3 tab; initStyleOption(&tab, 0); //DebugDialog::debug(QString("state %1").arg(tab.state)); QString text = tabText(i); int added = 0; while (true) { QRect r = tab.fontMetrics.boundingRect(text); if (r.width() + iconSize().width() + offset >= tabRect(i).width()) { if (added) { text.chop(1); setTabText(i, text); } break; } text += " "; added++; } } } QTabBar::paintEvent(event); /* return; // this code mostly lifted from QTabBar::paintEvent QStyleOptionTabBarBaseV2 optTabBase; optTabBase.init(this); optTabBase.shape = this->shape(); optTabBase.documentMode = this->documentMode(); QStylePainter p(this); int selected = this->currentIndex(); for (int i = 0; i < this->count(); ++i) optTabBase.tabBarRect |= tabRect(i); optTabBase.selectedTabRect = tabRect(selected); p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase); for (int i = 0; i < this->count(); ++i) { if (i == selected) continue; QStyleOptionTabV3 tab; initStyleOption(&tab, i); drawTab(p, tab, i); } // Draw the selected tab last to get it "on top" if (selected >= 0) { QStyleOptionTabV3 tab; initStyleOption(&tab, selected); drawTab(p, tab, selected); } */ } /* void FTabBar::drawTab(QStylePainter & p, QStyleOptionTabV3 & tabV3, int index) { //tabV3.iconSize = m_pixmaps.at(index).size(); p.drawControl(QStyle::CE_TabBarTabShape, tabV3); //p.drawPixmap(tabV3.rect.left(), tabV3.rect.top(), m_pixmaps.at(index)); //QRect tr = tabV3.rect; //int alignment = Qt::AlignLeft | Qt::TextShowMnemonic; //if (!this->style()->styleHint(QStyle::SH_UnderlineShortcut, &tabV3, this)) // alignment |= Qt::TextHideMnemonic; //p.drawItemText(tr, alignment, tabV3.palette, tabV3.state & QStyle::State_Enabled, tabV3.text, QPalette::WindowText); //p.drawControl(QStyle::CE_TabBarTabLabel, tabV3); } */ /////////////////////////////////////////////// struct MissingSvgInfo { QString requestedPath; QStringList connectorSvgIds; ModelPart * modelPart; bool equal; }; bool byConnectorCount(MissingSvgInfo & m1, MissingSvgInfo & m2) { if (m1.connectorSvgIds.count() == m2.connectorSvgIds.count() && m1.modelPart != m2.modelPart) { m1.equal = m2.equal = true; } return (m1.connectorSvgIds.count() > m2.connectorSvgIds.count()); } /////////////////////////////////////////////// #define ZIP_PART QString("part.") #define ZIP_SVG QString("svg.") /////////////////////////////////////////////// // SwapTimer explained: http://code.google.com/p/fritzing/issues/detail?id=1431 SwapTimer::SwapTimer() : QTimer() { } void SwapTimer::setAll(const QString & family, const QString & prop, QMap & propsMap, ItemBase * itemBase) { m_family = family; m_prop = prop; m_propsMap = propsMap; m_itemBase = itemBase; } const QString & SwapTimer::family() { return m_family; } const QString & SwapTimer::prop() { return m_prop; } QMap SwapTimer::propsMap() { return m_propsMap; } ItemBase * SwapTimer::itemBase() { return m_itemBase; } /////////////////////////////////////////////// const QString MainWindow::UntitledSketchName = "Untitled Sketch"; int MainWindow::UntitledSketchIndex = 1; int MainWindow::CascadeFactorX = 21; int MainWindow::CascadeFactorY = 19; static const int MainWindowDefaultWidth = 840; static const int MainWindowDefaultHeight = 600; int MainWindow::AutosaveTimeoutMinutes = 10; // in minutes bool MainWindow::AutosaveEnabled = true; QString MainWindow::BackupFolder; QRegExp MainWindow::GuidMatcher = QRegExp("[A-Fa-f0-9]{32}"); ///////////////////////////////////////////// MainWindow::MainWindow(ReferenceModel *referenceModel, QWidget * parent) : FritzingWindow(untitledFileName(), untitledFileCount(), fileExtension(), parent) { m_noSchematicConversion = m_useOldSchematic = m_convertedSchematic = false; m_initialTab = 1; m_rolloverQuoteDialog = NULL; setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); setDockOptions(QMainWindow::AnimatedDocks); m_sizeGrip = new FSizeGrip(this); m_topDock = NULL; m_bottomDock = NULL; m_dontKeepMargins = true; m_settingsPrefix = "main/"; m_showWelcomeAct = m_showProgramAct = m_raiseWindowAct = m_showPartsBinIconViewAct = m_showAllLayersAct = m_hideAllLayersAct = m_rotate90cwAct = m_showBreadboardAct = m_showSchematicAct = m_showPCBAct = NULL; m_fileMenu = m_editMenu = m_partMenu = m_windowMenu = m_pcbTraceMenu = m_schematicTraceMenu = m_breadboardTraceMenu = m_viewMenu = NULL; m_infoView = NULL; m_addedToTemp = false; setAcceptDrops(true); m_activeWire = NULL; m_activeConnectorItem = NULL; m_swapTimer.setInterval(30); m_swapTimer.setParent(this); m_swapTimer.setSingleShot(true); connect(&m_swapTimer, SIGNAL(timeout()), this, SLOT(swapSelectedTimeout())); m_closeSilently = false; m_orderFabAct = NULL; m_viewFromButtonWidget = m_activeLayerButtonWidget = NULL; m_programView = m_programWindow = NULL; m_welcomeView = NULL; m_windowMenuSeparator = NULL; m_schematicWireColorMenu = m_breadboardWireColorMenu = NULL; m_checkForUpdatesAct = NULL; m_fileProgressDialog = NULL; m_currentGraphicsView = NULL; m_comboboxChanged = false; // Add a timer for autosaving m_backingUp = m_autosaveNeeded = false; connect(&m_autosaveTimer, SIGNAL(timeout()), this, SLOT(backupSketch())); m_autosaveTimer.start(AutosaveTimeoutMinutes * 60 * 1000); m_fireQuoteTimer.setSingleShot(true); connect(&m_fireQuoteTimer, SIGNAL(timeout()), this, SLOT(fireQuote())); resize(MainWindowDefaultWidth, MainWindowDefaultHeight); m_backupFileNameAndPath = MainWindow::BackupFolder + "/" + TextUtils::getRandText() + FritzingSketchExtension; // Connect the undoStack to our autosave stuff connect(m_undoStack, SIGNAL(indexChanged(int)), this, SLOT(autosaveNeeded(int))); connect(m_undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(undoStackCleanChanged(bool))); // Create dot icons m_dotIcon = QIcon(":/resources/images/dot.png"); m_emptyIcon = QIcon(); m_currentWidget = NULL; m_firstOpen = true; m_statusBar = new QStatusBar(this); setStatusBar(m_statusBar); m_statusBar->setSizeGripEnabled(false); QSettings settings; m_locationLabelUnits = settings.value("LocationInches", "in").toString(); // leave the m_orderFabEnabled check in case we turn off the fab button in the future m_orderFabEnabled = true; // settings.value(ORDERFABENABLED, QVariant(false)).toBool(); m_locationLabel = new ClickableLabel("", this); m_locationLabel->setObjectName("LocationLabel"); connect(m_locationLabel, SIGNAL(clicked()), this, SLOT(locationLabelClicked())); m_locationLabel->setCursor(Qt::PointingHandCursor); m_statusBar->addPermanentWidget(m_locationLabel); m_zoomSlider = new ZoomSlider(ZoomableGraphicsView::MaxScaleValue, m_statusBar); connect(m_zoomSlider, SIGNAL(zoomChanged(double)), this, SLOT(updateViewZoom(double))); m_statusBar->addPermanentWidget(m_zoomSlider); setAttribute(Qt::WA_DeleteOnClose, true); #ifdef Q_OS_MAC //setAttribute(Qt::WA_QuitOnClose, false); // restoring this temporarily (2008.12.19) #endif m_dontClose = m_closing = false; m_referenceModel = referenceModel; m_sketchModel = new SketchModel(true); QShortcut * shortcut = new QShortcut(QKeySequence(tr("Ctrl+R", "Rotate Clockwise")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCW())); shortcut = new QShortcut(QKeySequence(tr("Alt+Ctrl+R", "Rotate Clockwise")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCWRubberBand())); shortcut = new QShortcut(QKeySequence(tr("Meta+Ctrl+R", "Rotate Clockwise")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCWRubberBand())); shortcut = new QShortcut(QKeySequence(tr("Shift+Ctrl+R", "Rotate Counterclockwise")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCCW())); shortcut = new QShortcut(QKeySequence(tr("Alt+Shift+Ctrl+R", "Rotate Counterclockwise")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCCWRubberBand())); shortcut = new QShortcut(QKeySequence(tr("Meta+Shift+Ctrl+R", "Rotate Counterclockwise")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(rotateIncCCWRubberBand())); shortcut = new QShortcut(QKeySequence(tr("Shift+Ctrl+Tab", "Toggle Active Layer")), this); connect(shortcut, SIGNAL(activated()), this, SLOT(toggleActiveLayer())); connect(this, SIGNAL(changeActivationSignal(bool, QWidget *)), qApp, SLOT(changeActivation(bool, QWidget *)), Qt::DirectConnection); connect(this, SIGNAL(destroyed(QObject *)), qApp, SLOT(topLevelWidgetDestroyed(QObject *))); connect(this, SIGNAL(externalProcessSignal(QString &, QString &, QStringList &)), qApp, SLOT(externalProcessSlot(QString &, QString &, QStringList &)), Qt::DirectConnection); } QWidget * MainWindow::createTabWidget() { //return new QStackedWidget(this); FTabWidget * tabWidget = new FTabWidget(this); tabWidget->setObjectName("sketch_tabs"); return tabWidget; } void MainWindow::addTab(QWidget * widget, const QString & label) { //Q_UNUSED(label); //qobject_cast(m_tabWidget)->addWidget(widget); qobject_cast(m_tabWidget)->addTab(widget, label); } void MainWindow::addTab(QWidget * widget, const QString & iconPath, const QString & label, bool withIcon) { if (!withIcon) { addTab(widget, label); return; } FTabWidget * tabWidget = qobject_cast(m_tabWidget); if (tabWidget == NULL) { addTab(widget, label); return; } //Q_UNUSED(label); //qobject_cast(m_tabWidget)->addWidget(widget); QIcon icon; QPixmap pixmap(iconPath); icon.addPixmap(pixmap, QIcon::Normal, QIcon::On); QString inactivePath = iconPath; inactivePath.replace("Active", "Inactive"); QPixmap inactivePixmap(inactivePath); icon.addPixmap(inactivePixmap, QIcon::Normal, QIcon::Off); QString hoverPath = iconPath; hoverPath.replace("Active", "Hover"); QPixmap hoverPixmap(hoverPath); icon.addPixmap(hoverPixmap, QIcon::Active, QIcon::On); tabWidget->addTab(widget, icon, label); } int MainWindow::currentTabIndex() { return qobject_cast(m_tabWidget)->currentIndex(); } void MainWindow::setCurrentTabIndex(int index) { qobject_cast(m_tabWidget)->setCurrentIndex(index); } QWidget * MainWindow::currentTabWidget() { return qobject_cast(m_tabWidget)->currentWidget(); } void MainWindow::init(ReferenceModel *referenceModel, bool lockFiles) { m_tabWidget = createTabWidget(); // FTabWidget(this); setCentralWidget(m_tabWidget); m_referenceModel = referenceModel; m_restarting = false; if (m_fileProgressDialog) { m_fileProgressDialog->setValue(2); } initLockedFiles(lockFiles); initWelcomeView(); initSketchWidgets(true); initProgrammingWidget(); m_undoView = new QUndoView(); m_undoGroup = new QUndoGroup(this); m_undoView->setGroup(m_undoGroup); m_undoGroup->setActiveStack(m_undoStack); initDock(); initMenus(); moreInitDock(); createZoomOptions(m_breadboardWidget); createZoomOptions(m_schematicWidget); createZoomOptions(m_pcbWidget); m_breadboardWidget->setToolbarWidgets(getButtonsForView(ViewLayer::BreadboardView)); m_schematicWidget->setToolbarWidgets(getButtonsForView(ViewLayer::SchematicView)); m_pcbWidget->setToolbarWidgets(getButtonsForView(ViewLayer::PCBView)); initStyleSheet(); m_breadboardGraphicsView->setItemMenu(breadboardItemMenu()); m_breadboardGraphicsView->setWireMenu(breadboardWireMenu()); m_pcbGraphicsView->setWireMenu(pcbWireMenu()); m_pcbGraphicsView->setItemMenu(pcbItemMenu()); m_schematicGraphicsView->setItemMenu(schematicItemMenu()); m_schematicGraphicsView->setWireMenu(schematicWireMenu()); if (m_infoView) { m_breadboardGraphicsView->setInfoView(m_infoView); m_pcbGraphicsView->setInfoView(m_infoView); m_schematicGraphicsView->setInfoView(m_infoView); } // make sure to set the connections after the views have been created connect(m_tabWidget, SIGNAL(currentChanged ( int )), this, SLOT(tabWidget_currentChanged( int ))); connectPairs(); setInitialView(); this->installEventFilter(this); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(95); } QSettings settings; if(!settings.value(m_settingsPrefix + "state").isNull()) { restoreState(settings.value(m_settingsPrefix + "state").toByteArray()); restoreGeometry(settings.value(m_settingsPrefix + "geometry").toByteArray()); } setMinimumSize(0,0); m_tabWidget->setMinimumWidth(500); m_tabWidget->setMinimumWidth(0); connect(this, SIGNAL(readOnlyChanged(bool)), this, SLOT(applyReadOnlyChange(bool))); m_setUpDockManagerTimer.setSingleShot(true); connect(&m_setUpDockManagerTimer, SIGNAL(timeout()), this, SLOT(keepMargins())); m_setUpDockManagerTimer.start(1000); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(98); } } MainWindow::~MainWindow() { // Delete backup of this sketch if one exists. QFile::remove(m_backupFileNameAndPath); delete m_sketchModel; dontKeepMargins(); m_setUpDockManagerTimer.stop(); foreach (LinkedFile * linkedFile, m_linkedProgramFiles) { delete linkedFile; } m_linkedProgramFiles.clear(); if (!m_fzzFolder.isEmpty()) { LockManager::releaseLockedFiles(m_fzzFolder, m_fzzFiles); FolderUtils::rmdir(m_fzzFolder); } } void MainWindow::initLockedFiles(bool lockFiles) { LockManager::initLockedFiles("fzz", m_fzzFolder, m_fzzFiles, lockFiles ? LockManager::SlowTime : 0); if (lockFiles) { QFileInfoList backupList; LockManager::checkLockedFiles("fzz", backupList, m_fzzFiles, true, LockManager::SlowTime); } } void MainWindow::initSketchWidgets(bool withIcons) { //DebugDialog::debug("init sketch widgets"); // all this belongs in viewLayer.xml m_breadboardGraphicsView = new BreadboardSketchWidget(ViewLayer::BreadboardView, this); initSketchWidget(m_breadboardGraphicsView); m_breadboardWidget = new SketchAreaWidget(m_breadboardGraphicsView,this); addTab(m_breadboardWidget, ":/resources/images/icons/TabWidgetBreadboardActive_icon.png", tr("Breadboard"), withIcons); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(11); } m_schematicGraphicsView = new SchematicSketchWidget(ViewLayer::SchematicView, this); initSketchWidget(m_schematicGraphicsView); m_schematicWidget = new SketchAreaWidget(m_schematicGraphicsView, this); addTab(m_schematicWidget, ":/resources/images/icons/TabWidgetSchematicActive_icon.png", tr("Schematic"), withIcons); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(20); } m_pcbGraphicsView = new PCBSketchWidget(ViewLayer::PCBView, this); initSketchWidget(m_pcbGraphicsView); m_pcbWidget = new SketchAreaWidget(m_pcbGraphicsView, this); addTab(m_pcbWidget, ":/resources/images/icons/TabWidgetPcbActive_icon.png", tr("PCB"), withIcons); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(29); } } void MainWindow::initMenus() { // This is the magic translation that changes all the shortcut text on the menu items // to the native language instead of "Ctrl", so the German menu items will now read "Strg" // You don't actually have to translate every menu item in the .ts file, you can just leave it as "Ctrl". QShortcut::tr("Ctrl", "for naming shortcut keys on menu items"); QShortcut::tr("Alt", "for naming shortcut keys on menu items"); QShortcut::tr("Shift", "for naming shortcut keys on menu items"); QShortcut::tr("Meta", "for naming shortcut keys on menu items"); //DebugDialog::debug("create menus"); createActions(); createMenus(); //DebugDialog::debug("create toolbars"); createStatusBar(); //DebugDialog::debug("after creating status bar"); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(91); } } void MainWindow::initSketchWidget(SketchWidget * sketchWidget) { sketchWidget->setSketchModel(m_sketchModel); sketchWidget->setReferenceModel(m_referenceModel); sketchWidget->setUndoStack(m_undoStack); sketchWidget->setChainDrag(true); // enable bend points sketchWidget->initGrid(); sketchWidget->addViewLayers(); } void MainWindow::connectPairs() { connectPair(m_breadboardGraphicsView, m_schematicGraphicsView); connectPair(m_breadboardGraphicsView, m_pcbGraphicsView); connectPair(m_schematicGraphicsView, m_breadboardGraphicsView); connectPair(m_schematicGraphicsView, m_pcbGraphicsView); connectPair(m_pcbGraphicsView, m_breadboardGraphicsView); connectPair(m_pcbGraphicsView, m_schematicGraphicsView); connect(m_pcbGraphicsView, SIGNAL(groundFillSignal()), this, SLOT(groundFill())); connect(m_pcbGraphicsView, SIGNAL(copperFillSignal()), this, SLOT(copperFill())); connect(m_pcbGraphicsView, SIGNAL(swapBoardImageSignal(SketchWidget *, ItemBase *, const QString &, const QString &, bool)), this, SLOT(swapBoardImageSlot(SketchWidget *, ItemBase *, const QString &, const QString &, bool))); connect(m_breadboardGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *))); connect(m_schematicGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *))); connect(m_pcbGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *))); connect(m_breadboardGraphicsView, SIGNAL(setActiveConnectorItemSignal(ConnectorItem *)), this, SLOT(setActiveConnectorItem(ConnectorItem *))); connect(m_schematicGraphicsView, SIGNAL(setActiveConnectorItemSignal(ConnectorItem *)), this, SLOT(setActiveConnectorItem(ConnectorItem *))); connect(m_pcbGraphicsView, SIGNAL(setActiveConnectorItemSignal(ConnectorItem *)), this, SLOT(setActiveConnectorItem(ConnectorItem *))); bool succeeded = connect(m_pcbGraphicsView, SIGNAL(routingStatusSignal(SketchWidget *, const RoutingStatus &)), this, SLOT(routingStatusSlot(SketchWidget *, const RoutingStatus &))); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(routingStatusSignal(SketchWidget *, const RoutingStatus &)), this, SLOT(routingStatusSlot(SketchWidget *, const RoutingStatus &))); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(routingStatusSignal(SketchWidget *, const RoutingStatus &)), this, SLOT(routingStatusSlot(SketchWidget *, const RoutingStatus &))); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(swapSignal(const QString &, const QString &, QMap &, ItemBase *)), this, SLOT(swapSelectedDelay(const QString &, const QString &, QMap &, ItemBase *))); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(swapSignal(const QString &, const QString &, QMap &, ItemBase *)), this, SLOT(swapSelectedDelay(const QString &, const QString &, QMap &, ItemBase *))); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(swapSignal(const QString &, const QString &, QMap &, ItemBase *)), this, SLOT(swapSelectedDelay(const QString &, const QString &, QMap &, ItemBase *))); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(dropPasteSignal(SketchWidget *)), this, SLOT(dropPaste(SketchWidget *))); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(dropPasteSignal(SketchWidget *)), this, SLOT(dropPaste(SketchWidget *))); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(dropPasteSignal(SketchWidget *)), this, SLOT(dropPaste(SketchWidget *))); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(subSwapSignal(SketchWidget *, ItemBase *, const QString &, ViewLayer::ViewLayerPlacement, long &, QUndoCommand *)), this, SLOT(subSwapSlot(SketchWidget *, ItemBase *, const QString &, ViewLayer::ViewLayerPlacement, long &, QUndoCommand *)), Qt::DirectConnection); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(updateLayerMenuSignal()), this, SLOT(updateLayerMenuSlot())); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(changeBoardLayersSignal(int, bool )), this, SLOT(changeBoardLayers(int, bool ))); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(boardDeletedSignal()), this, SLOT(boardDeletedSlot())); succeeded = succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_breadboardGraphicsView, SLOT(spaceBarIsPressedSlot(bool))); succeeded = succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_schematicGraphicsView, SLOT(spaceBarIsPressedSlot(bool))); succeeded = succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_pcbGraphicsView, SLOT(spaceBarIsPressedSlot(bool))); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double))); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double))); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double))); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(filenameIfSignal(QString &)), this, SLOT(filenameIfSlot(QString &)), Qt::DirectConnection); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(filenameIfSignal(QString &)), this, SLOT(filenameIfSlot(QString &)), Qt::DirectConnection); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(filenameIfSignal(QString &)), this, SLOT(filenameIfSlot(QString &)), Qt::DirectConnection); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(getDroppedItemViewLayerPlacementSignal(ModelPart *, ViewLayer::ViewLayerPlacement &)), m_pcbGraphicsView, SLOT(getDroppedItemViewLayerPlacement(ModelPart *, ViewLayer::ViewLayerPlacement &)), Qt::DirectConnection); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(getDroppedItemViewLayerPlacementSignal(ModelPart *, ViewLayer::ViewLayerPlacement &)), m_pcbGraphicsView, SLOT(getDroppedItemViewLayerPlacement(ModelPart *, ViewLayer::ViewLayerPlacement &)), Qt::DirectConnection); if (!succeeded) { DebugDialog::debug("connectPair failed"); } } void MainWindow::connectPair(SketchWidget * signaller, SketchWidget * slotter) { bool succeeded = connect(signaller, SIGNAL(itemAddedSignal(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *)), slotter, SLOT(itemAddedSlot(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *))); succeeded = succeeded && connect(signaller, SIGNAL(itemDeletedSignal(long)), slotter, SLOT(itemDeletedSlot(long)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(clearSelectionSignal()), slotter, SLOT(clearSelectionSlot())); succeeded = succeeded && connect(signaller, SIGNAL(itemSelectedSignal(long, bool)), slotter, SLOT(itemSelectedSlot(long, bool))); succeeded = succeeded && connect(signaller, SIGNAL(selectAllItemsSignal(bool, bool)), slotter, SLOT(selectAllItems(bool, bool))); succeeded = succeeded && connect(signaller, SIGNAL(wireDisconnectedSignal(long, QString)), slotter, SLOT(wireDisconnectedSlot(long, QString))); succeeded = succeeded && connect(signaller, SIGNAL(wireConnectedSignal(long, QString, long, QString)), slotter, SLOT(wireConnectedSlot(long, QString, long, QString))); succeeded = succeeded && connect(signaller, SIGNAL(changeConnectionSignal(long, QString, long, QString, ViewLayer::ViewLayerPlacement, bool, bool)), slotter, SLOT(changeConnectionSlot(long, QString, long, QString, ViewLayer::ViewLayerPlacement, bool, bool))); succeeded = succeeded && connect(signaller, SIGNAL(copyBoundingRectsSignal(QHash &)), slotter, SLOT(copyBoundingRectsSlot(QHash &)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(cleanUpWiresSignal(CleanUpWiresCommand *)), slotter, SLOT(cleanUpWiresSlot(CleanUpWiresCommand *)) ); succeeded = succeeded && connect(signaller, SIGNAL(cleanupRatsnestsSignal(bool)), slotter, SLOT(cleanupRatsnests(bool)) ); succeeded = succeeded && connect(signaller, SIGNAL(checkStickySignal(long, bool, bool, CheckStickyCommand *)), slotter, SLOT(checkSticky(long, bool, bool, CheckStickyCommand *)) ); succeeded = succeeded && connect(signaller, SIGNAL(disconnectAllSignal(QList, QHash &, QUndoCommand *)), slotter, SLOT(disconnectAllSlot(QList, QHash &, QUndoCommand *)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(setResistanceSignal(long, QString, QString, bool)), slotter, SLOT(setResistance(long, QString, QString, bool))); succeeded = succeeded && connect(signaller, SIGNAL(makeDeleteItemCommandPrepSignal(ItemBase *, bool , QUndoCommand * )), slotter, SLOT(makeDeleteItemCommandPrepSlot(ItemBase * , bool , QUndoCommand * ))); succeeded = succeeded && connect(signaller, SIGNAL(makeDeleteItemCommandFinalSignal(ItemBase *, bool , QUndoCommand * )), slotter, SLOT(makeDeleteItemCommandFinalSlot(ItemBase * , bool , QUndoCommand * ))); succeeded = succeeded && connect(signaller, SIGNAL(setPropSignal(long, const QString &, const QString &, bool, bool)), slotter, SLOT(setProp(long, const QString &, const QString &, bool, bool))); succeeded = succeeded && connect(signaller, SIGNAL(setInstanceTitleSignal(long, const QString &, const QString &, bool, bool )), slotter, SLOT(setInstanceTitle(long, const QString &, const QString &, bool, bool ))); succeeded = succeeded && connect(signaller, SIGNAL(setVoltageSignal(double, bool )), slotter, SLOT(setVoltage(double, bool ))); succeeded = succeeded && connect(signaller, SIGNAL(showLabelFirstTimeSignal(long, bool, bool )), slotter, SLOT(showLabelFirstTime(long, bool, bool ))); succeeded = succeeded && connect(signaller, SIGNAL(changeBoardLayersSignal(int, bool )), slotter, SLOT(changeBoardLayers(int, bool ))); succeeded = succeeded && connect(signaller, SIGNAL(deleteTracesSignal(QSet &, QHash &, QList &, bool, QUndoCommand *)), slotter, SLOT(deleteTracesSlot(QSet &, QHash &, QList &, bool, QUndoCommand *)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(ratsnestConnectSignal(long, const QString &, bool, bool)), slotter, SLOT(ratsnestConnect(long, const QString &, bool, bool )), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(updatePartLabelInstanceTitleSignal(long)), slotter, SLOT(updatePartLabelInstanceTitleSlot(long))); succeeded = succeeded && connect(signaller, SIGNAL(changePinLabelsSignal(ItemBase *, bool)), slotter, SLOT(changePinLabelsSlot(ItemBase *, bool))); succeeded = succeeded && connect(signaller, SIGNAL(collectRatsnestSignal(QList &)), slotter, SLOT(collectRatsnestSlot(QList &)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(removeRatsnestSignal(QList &, QUndoCommand *)), slotter, SLOT(removeRatsnestSlot(QList &, QUndoCommand *)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(canConnectSignal(Wire *, ItemBase *, bool &)), slotter, SLOT(canConnect(Wire *, ItemBase *, bool &)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(swapStartSignal(SwapThing &, bool)), slotter, SLOT(swapStart(SwapThing &, bool)), Qt::DirectConnection); succeeded = succeeded && connect(signaller, SIGNAL(packItemsSignal(int, const QList &, QUndoCommand *, bool)), slotter, SLOT(packItems(int, const QList &, QUndoCommand *, bool))); succeeded = succeeded && connect(signaller, SIGNAL(addSubpartSignal(long, long, bool)), slotter, SLOT(addSubpart(long, long, bool))); if (!succeeded) { DebugDialog::debug("connectPair failed"); } } void MainWindow::setCurrentFile(const QString &filename, bool addToRecent, bool setAsLastOpened) { setFileName(filename); if (setAsLastOpened) { QSettings settings; settings.setValue("lastOpenSketch",filename); m_useOldSchematic = false; if (m_convertedSchematic) { m_convertedSchematic = false; QString gridSize = QString("%1in").arg(m_schematicGraphicsView->defaultGridSizeInches()); m_schematicGraphicsView->setGridSize(gridSize); setCurrentTabIndex(2); m_schematicGraphicsView->resizeLabels(); m_schematicGraphicsView->resizeWires(); m_schematicGraphicsView->updateWires(); setWindowModified(true); } else { QStringList files = settings.value("lastTabList").toStringList(); for (int ix = files.count() - 1; ix >= 0; ix--) { if (files[ix].mid(1) == filename) { bool ok; int lastTab = files[ix].left(1).toInt(&ok); if (ok) { setCurrentTabIndex(lastTab); } } } } } updateRaiseWindowAction(); setTitle(); if(addToRecent) { QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); files.removeAll(filename); files.prepend(filename); while (files.size() > MaxRecentFiles) files.removeLast(); settings.setValue("recentFileList", files); QTimer::singleShot(1, this, SLOT(updateWelcomeViewRecentList())); // TODO: if lastTab file is not on recent list, remove it from the settings } foreach (QWidget *widget, QApplication::topLevelWidgets()) { MainWindow *mainWin = qobject_cast(widget); if (mainWin) mainWin->updateRecentFileActions(); } } void MainWindow::createZoomOptions(SketchAreaWidget* parent) { connect(parent->contentView(), SIGNAL(zoomChanged(double)), this, SLOT(updateZoomSlider(double))); connect(parent->contentView(), SIGNAL(zoomOutOfRange(double)), this, SLOT(updateZoomOptionsNoMatterWhat(double))); } ExpandingLabel * MainWindow::createRoutingStatusLabel(SketchAreaWidget * parent) { ExpandingLabel * routingStatusLabel = new ExpandingLabel(m_pcbWidget); connect(routingStatusLabel, SIGNAL(mousePressSignal(QMouseEvent*)), this, SLOT(routingStatusLabelMousePress(QMouseEvent*))); connect(routingStatusLabel, SIGNAL(mouseReleaseSignal(QMouseEvent*)), this, SLOT(routingStatusLabelMouseRelease(QMouseEvent*))); routingStatusLabel->setTextInteractionFlags(Qt::NoTextInteraction); routingStatusLabel->setCursor(Qt::WhatsThisCursor); routingStatusLabel->viewport()->setCursor(Qt::WhatsThisCursor); routingStatusLabel->setObjectName(SketchAreaWidget::RoutingStateLabelName); routingStatusLabel->setToolTip(tr("Click to highlight unconnected parts")); parent->setRoutingStatusLabel(routingStatusLabel); RoutingStatus routingStatus; routingStatus.zero(); routingStatusSlot(qobject_cast(parent->contentView()), routingStatus); return routingStatusLabel; } SketchToolButton *MainWindow::createRotateButton(SketchAreaWidget *parent) { QList rotateMenuActions; rotateMenuActions << m_rotate45ccwAct << m_rotate90ccwAct << m_rotate180Act << m_rotate90cwAct << m_rotate45cwAct; SketchToolButton * rotateButton = new SketchToolButton("Rotate",parent, rotateMenuActions); rotateButton->setDefaultAction(m_rotate90ccwAct); rotateButton->setText(tr("Rotate")); m_rotateButtons << rotateButton; return rotateButton; } SketchToolButton *MainWindow::createShareButton(SketchAreaWidget *parent) { SketchToolButton *shareButton = new SketchToolButton("Share",parent, m_shareOnlineAct); shareButton->setText(tr("Share")); shareButton->setObjectName("shareProjectButton"); shareButton->setEnabledIcon(); // seems to need this to display button icon first time return shareButton; } SketchToolButton *MainWindow::createFlipButton(SketchAreaWidget *parent) { QList flipMenuActions; flipMenuActions << m_flipHorizontalAct << m_flipVerticalAct; SketchToolButton *flipButton = new SketchToolButton("Flip",parent, flipMenuActions); flipButton->setText(tr("Flip")); m_flipButtons << flipButton; return flipButton; } SketchToolButton *MainWindow::createAutorouteButton(SketchAreaWidget *parent) { SketchToolButton *autorouteButton = new SketchToolButton("Autoroute",parent, m_newAutorouteAct); autorouteButton->setText(tr("Autoroute")); autorouteButton->setEnabledIcon(); // seems to need this to display button icon first time return autorouteButton; } SketchToolButton *MainWindow::createOrderFabButton(SketchAreaWidget *parent) { SketchToolButton *orderFabButton = new SketchToolButton("Order",parent, m_orderFabAct); orderFabButton->setText(tr("Fabricate")); orderFabButton->setObjectName("orderFabButton"); orderFabButton->setEnabledIcon();// seems to need this to display button icon first time connect(orderFabButton, SIGNAL(entered()), this, SLOT(orderFabHoverEnter())); connect(orderFabButton, SIGNAL(left()), this, SLOT(orderFabHoverLeave())); return orderFabButton; } QWidget *MainWindow::createActiveLayerButton(SketchAreaWidget *parent) { QList actions; actions << m_activeLayerBothAct << m_activeLayerBottomAct << m_activeLayerTopAct; m_activeLayerButtonWidget = new QStackedWidget; // m_activeLayerButtonWidget->setMaximumWidth(90); m_activeLayerButtonWidget->setObjectName("activeLayerButton"); SketchToolButton * button = new SketchToolButton("ActiveLayer", parent, actions); button->setDefaultAction(m_activeLayerBottomAct); button->setText(tr("Both Layers")); m_activeLayerButtonWidget->addWidget(button); button = new SketchToolButton("ActiveLayerB", parent, actions); button->setDefaultAction(m_activeLayerTopAct); button->setText(tr("Bottom Layer")); m_activeLayerButtonWidget->addWidget(button); button = new SketchToolButton("ActiveLayerT", parent, actions); button->setDefaultAction(m_activeLayerBothAct); button->setText(tr("Top Layer")); m_activeLayerButtonWidget->addWidget(button); return m_activeLayerButtonWidget; } QWidget *MainWindow::createViewFromButton(SketchAreaWidget *parent) { QList actions; actions << m_viewFromAboveAct << m_viewFromBelowAct; m_viewFromButtonWidget = new QStackedWidget; // m_viewFromButtonWidget->setMaximumWidth(95); m_viewFromButtonWidget->setObjectName("viewFromButton"); SketchToolButton * button = new SketchToolButton("ViewFromT", parent, actions); button->setDefaultAction(m_viewFromBelowAct); button->setText(tr("View from Above")); button->setEnabledIcon(); // seems to need this to display button icon first time m_viewFromButtonWidget->addWidget(button); button = new SketchToolButton("ViewFromB", parent, actions); button->setDefaultAction(m_viewFromAboveAct); button->setText(tr("View from Below")); button->setEnabledIcon(); // seems to need this to display button icon first time m_viewFromButtonWidget->addWidget(button); return m_viewFromButtonWidget; } SketchToolButton *MainWindow::createNoteButton(SketchAreaWidget *parent) { SketchToolButton *noteButton = new SketchToolButton("Notes",parent, m_addNoteAct); noteButton->setObjectName("noteButton"); noteButton->setText(tr("Add a note")); noteButton->setEnabledIcon(); // seems to need this to display button icon first time return noteButton; } SketchToolButton *MainWindow::createExportEtchableButton(SketchAreaWidget *parent) { QList actions; actions << m_exportEtchablePdfAct << m_exportEtchableSvgAct << m_exportGerberAct; SketchToolButton *exportEtchableButton = new SketchToolButton("Diy",parent, actions); exportEtchableButton->setObjectName("exportButton"); exportEtchableButton->setDefaultAction(m_exportEtchablePdfAct); exportEtchableButton->setText(tr("Export for PCB")); exportEtchableButton->setEnabledIcon(); // seems to need this to display button icon first time return exportEtchableButton; } QWidget *MainWindow::createToolbarSpacer(SketchAreaWidget *parent) { QFrame *toolbarSpacer = new QFrame(parent); QHBoxLayout *spacerLayout = new QHBoxLayout(toolbarSpacer); spacerLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding)); return toolbarSpacer; } QList MainWindow::getButtonsForView(ViewLayer::ViewID viewId) { QList retval; SketchAreaWidget *parent; switch(viewId) { case ViewLayer::BreadboardView: parent = m_breadboardWidget; break; case ViewLayer::SchematicView: parent = m_schematicWidget; break; case ViewLayer::PCBView: parent = m_pcbWidget; break; default: return retval; } switch(viewId) { case ViewLayer::BreadboardView: case ViewLayer::SchematicView: retval << createNoteButton(parent); default: break; } retval << createRotateButton(parent); switch (viewId) { case ViewLayer::BreadboardView: retval << createFlipButton(parent) << createRoutingStatusLabel(parent); break; case ViewLayer::SchematicView: retval << createFlipButton(parent) << createAutorouteButton(parent) << createRoutingStatusLabel(parent); break; case ViewLayer::PCBView: retval << createViewFromButton(parent) << createActiveLayerButton(parent) << createAutorouteButton(parent) << createExportEtchableButton(parent) << createRoutingStatusLabel(parent) ; if (m_orderFabEnabled) { m_orderFabButton = createOrderFabButton(parent); retval << m_orderFabButton; } break; default: break; } retval << createShareButton(parent); return retval; } void MainWindow::updateZoomSlider(double zoom) { m_zoomSlider->setValue(zoom); } SketchAreaWidget *MainWindow::currentSketchArea() { if (m_currentGraphicsView == NULL) return NULL; return dynamic_cast(m_currentGraphicsView->parent()); } void MainWindow::updateZoomOptionsNoMatterWhat(double zoom) { m_zoomSlider->setValue(zoom); } void MainWindow::updateViewZoom(double newZoom) { m_comboboxChanged = true; if(m_currentGraphicsView) m_currentGraphicsView->absoluteZoom(newZoom); } void MainWindow::createStatusBar() { m_statusBar->showMessage(tr("Ready")); } void MainWindow::tabWidget_currentChanged(int index) { SketchAreaWidget * widgetParent = dynamic_cast(currentTabWidget()); if (widgetParent == NULL) return; m_currentWidget = widgetParent; if (m_locationLabel) { m_locationLabel->setText(""); } QStatusBar *sb = statusBar(); connect(sb, SIGNAL(messageChanged(const QString &)), this, SLOT(showStatusMessage(const QString &))); widgetParent->addStatusBar(m_statusBar); if(sb != m_statusBar) sb->hide(); if (m_breadboardGraphicsView) m_breadboardGraphicsView->setCurrent(false); if (m_schematicGraphicsView) m_schematicGraphicsView->setCurrent(false); if (m_pcbGraphicsView) m_pcbGraphicsView->setCurrent(false); SketchWidget *widget = qobject_cast(widgetParent->contentView()); if(m_currentGraphicsView) { m_currentGraphicsView->saveZoom(m_zoomSlider->value()); disconnect( m_currentGraphicsView, SIGNAL(selectionChangedSignal()), this, SLOT(updateTransformationActions()) ); } m_currentGraphicsView = widget; if (m_programView) { hideShowProgramMenu(); } hideShowTraceMenu(); updateEditMenu(); if (m_showBreadboardAct) { QList actions; if (m_welcomeView) actions << m_showWelcomeAct; actions << m_showBreadboardAct << m_showSchematicAct << m_showPCBAct; if (m_programView) actions << m_showProgramAct; setActionsIcons(index, actions); } if (widget == NULL) { m_firstTimeHelpAct->setEnabled(false); return; } m_zoomSlider->setValue(m_currentGraphicsView->retrieveZoom()); FirstTimeHelpDialog::setViewID(m_currentGraphicsView->viewID()); m_firstTimeHelpAct->setEnabled(true); connect( m_currentGraphicsView, // don't connect directly to the scene here, connect to the widget's signal SIGNAL(selectionChangedSignal()), this, SLOT(updateTransformationActions()) ); updateActiveLayerButtons(); m_currentGraphicsView->setCurrent(true); // !!!!!! hack alert !!!!!!! // this item update loop seems to deal with a qt update bug: // if one view is visible and you change something in another view, // the change might not appear when you switch views until you move the item in question foreach(QGraphicsItem * item, m_currentGraphicsView->items()) { item->update(); } updateLayerMenu(true); updateTraceMenu(); updateTransformationActions(); setTitle(); if (m_infoView) { m_currentGraphicsView->updateInfoView(); } // update issue with 4.5.1?: is this still valid (4.6.x?) m_currentGraphicsView->updateConnectors(); QTimer::singleShot(10, this, SLOT(initZoom())); } void MainWindow::setActionsIcons(int index, QList & actions) { for (int i = 0; i < actions.count(); i++) { // DebugDialog::debug(QString("setting icon %1 %2").arg(i).arg(index == i)); // setting the icons seems to be broken in Qt 5, so use checkMarks instead // note that we used dots instead of checkMarks originally because // we hoped it was clearer that the items were mutually exclusive // note that using QIcon() instead of m_emptyIcon does no good // (and we used the m_emptyIcon to preserve the space at the left) // actions[i]->setIcon(index == i ? m_dotIcon : m_emptyIcon); actions[i]->setChecked(index == i); } } void MainWindow::closeEvent(QCloseEvent *event) { if (m_dontClose) { event->ignore(); return; } if (m_programWindow) { m_programWindow->close(); if (m_programWindow->isVisible()) { event->ignore(); return; } } if (!m_closeSilently) { bool whatWithAliens = whatToDoWithAlienFiles(); bool discard; if(!beforeClosing(true, discard) || !whatWithAliens ||!m_binManager->beforeClosing()) { event->ignore(); return; } if(whatWithAliens && m_binManager->hasAlienParts()) { m_binManager->createIfMyPartsNotExists(); } } //DebugDialog::debug(QString("top level windows: %1").arg(QApplication::topLevelWidgets().size())); /* foreach (QWidget * widget, QApplication::topLevelWidgets()) { QMenu * menu = qobject_cast(widget); if (menu != NULL) { continue; // QMenus are always top level widgets, even if they have parents... } DebugDialog::debug(QString("top level widget %1 %2 %3") .arg(widget->metaObject()->className()) .arg(widget->windowTitle()) .arg(widget->toolTip()) ); } */ m_closing = true; int count = 0; foreach (QWidget *widget, QApplication::topLevelWidgets()) { if (widget == this) continue; if (qobject_cast(widget) == NULL) continue; count++; } if (count == 0) { DebugDialog::closeDebug(); } QSettings settings; settings.setValue(m_settingsPrefix + "state",saveState()); settings.setValue(m_settingsPrefix + "geometry",saveGeometry()); saveLastTabList(); QMainWindow::closeEvent(event); } void MainWindow::saveLastTabList() { QSettings settings; QStringList files = settings.value("lastTabList").toStringList(); for (int ix = files.count() - 1; ix >= 0; ix--) { if (files[ix].mid(1) == m_fwFilename) files.removeAt(ix); } files.prepend(QString("%1%2").arg(this->currentTabIndex()).arg(m_fwFilename)); while (files.size() > MaxRecentFiles) files.removeLast(); settings.setValue("lastTabList", files); } bool MainWindow::whatToDoWithAlienFiles() { if (m_alienFiles.size() > 0) { QString basename = QFileInfo(m_fwFilename).fileName(); QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Save %1").arg(basename), m_alienPartsMsg .arg(basename), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); // TODO: translate button text if (reply == QMessageBox::Yes) { return true; } else if (reply == QMessageBox::No) { foreach(QString pathToRemove, m_alienFiles) { QFile::remove(pathToRemove); } m_alienFiles.clear(); recoverBackupedFiles(); emit alienPartsDismissed(); return true; } else { return false; } } else { return true; } } void MainWindow::acceptAlienFiles() { m_alienFiles.clear(); } bool MainWindow::eventFilter(QObject *object, QEvent *event) { if (object == this && (event->type() == QEvent::KeyPress // || event->type() == QEvent::KeyRelease || event->type() == QEvent::ShortcutOverride)) { //DebugDialog::debug(QString("event filter %1").arg(event->type()) ); updatePartMenu(); updateTraceMenu(); // On the mac, the first time the delete key is pressed, to be used as a shortcut for QAction m_deleteAct, // for some reason, the enabling of the m_deleteAct in UpdateEditMenu doesn't "take" until the next time the event loop is processed // Thereafter, the delete key works as it should. // So this call to processEvents() makes sure m_deleteAct is enabled. ProcessEventBlocker::processEvents(); } #if defined(Q_OS_MAC) && (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) // Need to process Backspace on Mac to workaround bug in Qt5 // See http://qt-project.org/forums/viewthread/36174 if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Backspace) { Qt::KeyboardModifiers modifiers = keyEvent->modifiers(); if (modifiers == Qt::NoModifier) { doDelete(); return true; } if (modifiers == Qt::AltModifier) { doDeleteMinus(); return true; } } } #endif return QMainWindow::eventFilter(object, event); } const QString MainWindow::untitledFileName() { return UntitledSketchName; } int &MainWindow::untitledFileCount() { return UntitledSketchIndex; } const QString MainWindow::fileExtension() { return FritzingBundleExtension; } const QString MainWindow::defaultSaveFolder() { return FolderUtils::openSaveFolder(); } bool MainWindow::undoStackIsEmpty() { return m_undoStack->count() == 0; } void MainWindow::setInfoViewOnHover(bool infoViewOnHover) { m_breadboardGraphicsView->setInfoViewOnHover(infoViewOnHover); m_schematicGraphicsView->setInfoViewOnHover(infoViewOnHover); m_pcbGraphicsView->setInfoViewOnHover(infoViewOnHover); m_binManager->setInfoViewOnHover(infoViewOnHover); } void MainWindow::loadBundledSketch(const QString &fileName, bool addToRecent, bool setAsLastOpened, bool checkObsolete) { QString error; if(!FolderUtils::unzipTo(fileName, m_fzzFolder, error)) { FMessageBox::warning( this, tr("Fritzing"), tr("Unable to open '%1': %2").arg(fileName).arg(error) ); return; } QDir dir(m_fzzFolder); QString binFileName = dir.absoluteFilePath(QFileInfo(BinManager::TempPartsBinTemplateLocation).fileName()); m_binManager->setTempPartsBinLocation(binFileName); FolderUtils::copyBin(binFileName, BinManager::TempPartsBinTemplateLocation); QStringList namefilters; namefilters << "*"+FritzingSketchExtension; QFileInfoList entryInfoList = dir.entryInfoList(namefilters); if (entryInfoList.count() == 0) { FMessageBox::warning( this, tr("Fritzing"), tr("No Sketch found in '%1'").arg(fileName) ); return; } QFileInfo sketchInfo = entryInfoList[0]; QString sketchName = dir.absoluteFilePath(sketchInfo.fileName()); namefilters.clear(); namefilters << "*" + FritzingPartExtension; entryInfoList = dir.entryInfoList(namefilters); namefilters.clear(); namefilters << "*.svg"; QFileInfoList svgEntryInfoList = dir.entryInfoList(namefilters); m_addedToTemp = false; QList missing; QList missingModelParts; foreach (QFileInfo fzpInfo, entryInfoList) { QFile file(dir.absoluteFilePath(fzpInfo.fileName())); if (!file.open(QFile::ReadOnly)) { DebugDialog::debug(QString("unable to open %1: %2").arg(file.fileName())); continue; } // TODO: could be more efficient by using a streamreader QString fzp = file.readAll(); file.close(); QString moduleID = TextUtils::parseForModuleID(fzp); if (moduleID.isEmpty()) { DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); DebugDialog::debug(QString("unable to find module id in %1: %2").arg(file.fileName()).arg(fzp)); DebugDialog::debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); continue; } ModelPart * mp = m_referenceModel->retrieveModelPart(moduleID); if (mp == NULL) { QDomDocument doc; if (!doc.setContent(fzp)) { DebugDialog::debug(QString("unable to parse fzp in %1: %2").arg(file.fileName()).arg(fzp)); continue; } mp = copyToPartsFolder(fzpInfo, false, PartFactory::folderPath(), "contrib"); if (mp == NULL) { DebugDialog::debug(QString("unable to create model part in %1: %2").arg(file.fileName()).arg(fzp)); continue; } QDomElement root = doc.documentElement(); QDomElement views = root.firstChildElement("views"); QDomElement view = views.firstChildElement(); while (!view.isNull()) { QDomElement layers = view.firstChildElement("layers"); QString path = layers.attribute("image", ""); if (!path.isEmpty()) { bool copied = copySvg(path, svgEntryInfoList); if (!copied) { DebugDialog::debug(QString("missing svg %1").arg(path)); MissingSvgInfo msi; msi.equal = false; msi.modelPart = mp; missingModelParts << mp; msi.requestedPath = path; ViewLayer::ViewID viewID = ViewLayer::idFromXmlName(view.tagName()); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QString id, terminalID; ViewLayer::getConnectorSvgIDs(connector, viewID, id, terminalID); if (!id.isEmpty()) { msi.connectorSvgIds.append(id); } connector = connector.nextSiblingElement("connector"); } missing << msi; } } view = view.nextSiblingElement(); } mp->setFzz(true); } if (!missingModelParts.contains(mp)) { m_binManager->addToTempPartsBin(mp); m_addedToTemp = true; } } qSort(missing.begin(), missing.end(), byConnectorCount); foreach (MissingSvgInfo msi, missing) { if (msi.equal) { // two or more parts have the same number of connectors--so we can't figure out how to assign them continue; } int slash = msi.requestedPath.indexOf("/"); QString suffix = msi.requestedPath.mid(slash + 1); QString prefix = msi.requestedPath.left(slash); for (int jx = svgEntryInfoList.count() - 1; jx >= 0; jx--) { QFileInfo svgInfo = svgEntryInfoList.at(jx); if (!svgInfo.fileName().contains(prefix, Qt::CaseInsensitive)) continue; QFile svgfile(svgInfo.absoluteFilePath()); QDomDocument svgDoc; if (!svgDoc.setContent(&svgfile)) continue; QList elements; QDomElement root = svgDoc.documentElement(); TextUtils::findElementsWithAttribute(root, "id", elements); if (elements.count() < msi.connectorSvgIds.count()) continue; QStringList ids; foreach (QDomElement element, elements) { ids << element.attribute("id"); } bool allGood = true; foreach (QString id, msi.connectorSvgIds) { if (!ids.contains(id)) { allGood = false; break; } } if (!allGood) continue; QString destPath = copyToSvgFolder(svgInfo, false, PartFactory::folderPath(), "contrib"); // copy file with original name if (!destPath.isEmpty()) { QFileInfo destInfo(destPath); QFile file(destPath); DebugDialog::debug(QString("found missing %1").arg(destPath)); FolderUtils::slamCopy(file, destInfo.absoluteDir().absoluteFilePath(suffix)); // make another copy that has the name used in the fzp file svgEntryInfoList.removeAt(jx); break; } } } foreach (ModelPart * mp, missingModelParts) { m_binManager->addToTempPartsBin(mp); m_addedToTemp = true; } if (!m_addedToTemp) { m_binManager->hideTempPartsBin(); } // the bundled itself this->mainLoad(sketchName, "", checkObsolete); setCurrentFile(fileName, addToRecent, setAsLastOpened); } bool MainWindow::copySvg(const QString & path, QFileInfoList & svgEntryInfoList) { int slash = path.indexOf("/"); QString subpath = path.mid(slash + 1); bool gotOne = false; for (int jx = svgEntryInfoList.count() - 1; jx >= 0; jx--) { QFileInfo svgInfo = svgEntryInfoList.at(jx); if (svgInfo.fileName().contains(subpath)) { copyToSvgFolder(svgInfo, false, PartFactory::folderPath(), "contrib"); svgEntryInfoList.removeAt(jx); // jrc 30 oct 2012: not sure why we can't just return at this point--can there be other matching files? gotOne = true; } } if (gotOne) return true; // deal with a bug in which all the svg files exist in the fzz but the fz file points to the wrong name // most of the time it's just a GUID difference DebugDialog::debug(QString("svg matching fz path %1 not found").arg(path)); int guidix = GuidMatcher.lastIndexIn(subpath); if (guidix < 0) return false; QString originalGuid = GuidMatcher.cap(0); QString tryPath = subpath; tryPath.replace(guidix, originalGuid.length(), "%%%%"); for (int jx = svgEntryInfoList.count() - 1; jx >= 0; jx--) { QFileInfo svgInfo = svgEntryInfoList.at(jx); QString tempPath = svgInfo.fileName(); guidix = GuidMatcher.lastIndexIn(tempPath); if (guidix < 0) continue; tempPath.replace(guidix, GuidMatcher.cap(0).length(), "%%%%"); if (!tempPath.contains(tryPath)) continue; QString destPath = copyToSvgFolder(svgInfo, false, PartFactory::folderPath(), "contrib"); if (!destPath.isEmpty()) { QFile file(destPath); guidix = GuidMatcher.lastIndexIn(destPath); destPath.replace(guidix, GuidMatcher.cap(0).length(), originalGuid); FolderUtils::slamCopy(file, destPath); DebugDialog::debug(QString("found matching svg %1").arg(destPath)); svgEntryInfoList.removeAt(jx); return true; } } return false; } bool MainWindow::loadBundledNonAtomicEntity(const QString &fileName, Bundler* bundler, bool addToBin, bool dontAsk) { QDir destFolder = QDir::temp(); FolderUtils::createFolderAndCdIntoIt(destFolder, TextUtils::getRandText()); QString unzipDirPath = destFolder.path(); QString error; if(!FolderUtils::unzipTo(fileName, unzipDirPath, error)) { FMessageBox::warning( this, tr("Fritzing"), tr("Unable to open shareable '%1': %2").arg(fileName).arg(error) ); // gotta return now, or loadBundledSketchAux will crash return false; } QDir unzipDir(unzipDirPath); if (bundler->preloadBundledAux(unzipDir, dontAsk)) { QList mps = moveToPartsFolder(unzipDir,this,addToBin,true,FolderUtils::getUserPartsPath(), "contrib", false); // the bundled itself bundler->loadBundledAux(unzipDir,mps); } FolderUtils::rmdir(unzipDirPath); return true; } /* void MainWindow::loadBundledPartFromWeb() { QMainWindow *mw = new QMainWindow(); QString url = "http://localhost:8081/parts_gen/choose"; QWebView *view = new QWebView(mw); mw->setCentralWidget(view); view->setUrl(QUrl(url)); mw->show(); mw->raise(); } */ ModelPart* MainWindow::loadBundledPart(const QString &fileName, bool addToBin) { QDir destFolder = QDir::temp(); FolderUtils::createFolderAndCdIntoIt(destFolder, TextUtils::getRandText()); QString unzipDirPath = destFolder.path(); QString error; if(!FolderUtils::unzipTo(fileName, unzipDirPath, error)) { FMessageBox::warning( this, tr("Fritzing"), tr("Unable to open shareable part '%1': %2").arg(fileName).arg(error) ); return NULL; } QDir unzipDir(unzipDirPath); QList mps; try { mps = moveToPartsFolder(unzipDir, this, addToBin, true, FolderUtils::getUserPartsPath(), "user", true); } catch (const QString & msg) { FMessageBox::warning( this, tr("Fritzing"), msg ); return NULL; } if (mps.count() != 1) { // if this fails, that means that the bundled was wrong FMessageBox::warning( this, tr("Fritzing"), tr("Unable to load part from '%1'").arg(fileName) ); return NULL; } FolderUtils::rmdir(unzipDirPath); return mps[0]; } void MainWindow::saveBundledPart(const QString &moduleId) { QString modIdToExport; ModelPart* mp; if(moduleId.isEmpty()) { if (m_currentGraphicsView == NULL) return; PaletteItem *selectedPart = m_currentGraphicsView->getSelectedPart(); mp = selectedPart->modelPart(); modIdToExport = mp->moduleID(); } else { modIdToExport = moduleId; mp = m_referenceModel->retrieveModelPart(moduleId); } QString partTitle = mp->title(); QString fileExt; QString path = defaultSaveFolder()+"/"+partTitle+FritzingBundledPartExtension; QString bundledFileName = FolderUtils::getSaveFileName( this, tr("Specify a file name"), path, tr("Fritzing Part (*%1)").arg(FritzingBundledPartExtension), &fileExt ); if (bundledFileName.isEmpty()) return; // Cancel pressed if(!alreadyHasExtension(bundledFileName, FritzingBundledPartExtension)) { bundledFileName += FritzingBundledPartExtension; } QDir destFolder = QDir::temp(); FolderUtils::createFolderAndCdIntoIt(destFolder, TextUtils::getRandText()); QString dirToRemove = destFolder.path(); QString aux = QFileInfo(bundledFileName).fileName(); QString destPartPath = // remove the last "z" from the extension destFolder.path()+"/"+aux.left(aux.size()-1); DebugDialog::debug("saving part temporarily to "+destPartPath); //bool wasModified = isWindowModified(); //QString prevFileName = m_fileName; //saveAsAux(destPartPath); //m_fileName = prevFileName; //setWindowModified(wasModified); setTitle(); saveBundledAux(mp, destFolder); QStringList skipSuffixes; if(!FolderUtils::createZipAndSaveTo(destFolder, bundledFileName, skipSuffixes)) { FMessageBox::warning( this, tr("Fritzing"), tr("Unable to export %1 to shareable sketch").arg(bundledFileName) ); } FolderUtils::rmdir(dirToRemove); } QStringList MainWindow::saveBundledAux(ModelPart *mp, const QDir &destFolder) { QStringList names; QString partPath = mp->path(); QFile file(partPath); QString fn = ZIP_PART + QFileInfo(partPath).fileName(); names << fn; FolderUtils::slamCopy(file, destFolder.path()+"/"+fn); QList viewIDs; viewIDs << ViewLayer::IconView << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView; foreach (ViewLayer::ViewID viewID, viewIDs) { QString basename = mp->hasBaseNameFor(viewID); if (basename.isEmpty()) continue; QString filename = PartFactory::getSvgFilename(mp, basename, true, true); if (filename.isEmpty()) continue; QFile file(filename); basename.replace("/", "."); QString fn = ZIP_SVG + basename; names << fn; FolderUtils::slamCopy(file, destFolder.path()+"/"+fn); } return names; } QList MainWindow::moveToPartsFolder(QDir &unzipDir, MainWindow* mw, bool addToBin, bool addToAlien, const QString & prefixFolder, const QString &destFolder, bool importingSinglePart) { QStringList namefilters; QList retval; if (mw == NULL) { throw tr("MainWindow::moveToPartsFolder mainwindow missing"); } namefilters.clear(); namefilters << ZIP_PART+"*"; QList partEntryInfoList = unzipDir.entryInfoList(namefilters); if (importingSinglePart && partEntryInfoList.count() > 0) { QString moduleID = TextUtils::parseFileForModuleID(partEntryInfoList[0].absoluteFilePath()); if (!moduleID.isEmpty() && m_referenceModel->retrieveModelPart(moduleID) != NULL) { throw tr("There is already a part with id '%1' loaded into Fritzing.").arg(moduleID); } } namefilters.clear(); namefilters << ZIP_SVG+"*"; foreach(QFileInfo file, unzipDir.entryInfoList(namefilters)) { // svg files //DebugDialog::debug("unzip svg " + file.absoluteFilePath()); mw->copyToSvgFolder(file, addToAlien, prefixFolder, destFolder); } foreach(QFileInfo file, partEntryInfoList) { // part files //DebugDialog::debug("unzip part " + file.absoluteFilePath()); ModelPart * mp = mw->copyToPartsFolder(file, addToAlien, prefixFolder, destFolder); retval << mp; if (addToBin) { // should only be here when adding single new part m_binManager->addToMyParts(mp); } } return retval; } QString MainWindow::copyToSvgFolder(const QFileInfo& file, bool addToAlien, const QString & prefixFolder, const QString &destFolder) { QFile svgfile(file.filePath()); // let's make sure that we remove just the suffix QString fileName = file.fileName().remove(QRegExp("^"+ZIP_SVG)); QString viewFolder = fileName.left(fileName.indexOf(".")); fileName.remove(0, viewFolder.length() + 1); QString destFilePath = prefixFolder+"/svg/"+destFolder+"/"+viewFolder+"/"+fileName; backupExistingFileIfExists(destFilePath); if(FolderUtils::slamCopy(svgfile, destFilePath)) { if (addToAlien) { m_alienFiles << destFilePath; } return destFilePath; } return ""; } ModelPart* MainWindow::copyToPartsFolder(const QFileInfo& file, bool addToAlien, const QString & prefixFolder, const QString &destFolder) { QFile partfile(file.filePath()); // let's make sure that we remove just the suffix QString destFilePath = prefixFolder+"/"+destFolder+"/"+file.fileName().remove(QRegExp("^"+ZIP_PART)); backupExistingFileIfExists(destFilePath); if(FolderUtils::slamCopy(partfile, destFilePath)) { if (addToAlien) { m_alienFiles << destFilePath; m_alienPartsMsg = tr("Do you want to keep the imported parts?"); } } ModelPart *mp = m_referenceModel->loadPart(destFilePath, true); mp->setAlien(true); return mp; } void MainWindow::binSaved(bool hasPartsFromBundled) { if(hasPartsFromBundled) { // the bin will need those parts, so just keep them m_alienFiles.clear(); resetTempFolder(); } } #undef ZIP_PART #undef ZIP_SVG void MainWindow::backupExistingFileIfExists(const QString &destFilePath) { if(QFileInfo(destFilePath).exists()) { if(m_tempDir.path() == ".") { m_tempDir = QDir::temp(); FolderUtils::createFolderAndCdIntoIt(m_tempDir, TextUtils::getRandText()); DebugDialog::debug("debug folder for overwritten files: "+m_tempDir.path()); } QString fileBackupName = QFileInfo(destFilePath).fileName(); m_filesReplacedByAlienOnes << destFilePath; QFile file(destFilePath); bool alreadyExists = file.exists(); FolderUtils::slamCopy(file, m_tempDir.path()+"/"+fileBackupName); if(alreadyExists) { file.remove(destFilePath); } } } void MainWindow::recoverBackupedFiles() { foreach(QString originalFilePath, m_filesReplacedByAlienOnes) { QFile file(m_tempDir.path()+"/"+QFileInfo(originalFilePath).fileName()); if(file.exists(originalFilePath)) { file.remove(); } FolderUtils::slamCopy(file, originalFilePath); } resetTempFolder(); } void MainWindow::resetTempFolder() { if(m_tempDir.path() != ".") { FolderUtils::rmdir(m_tempDir); m_tempDir = QDir::temp(); } m_filesReplacedByAlienOnes.clear(); } void MainWindow::routingStatusSlot(SketchWidget * sketchWidget, const RoutingStatus & routingStatus) { m_routingStatus = routingStatus; QString theText; if (routingStatus.m_netCount == 0) { theText = tr("No connections to route"); } else if (routingStatus.m_netCount == routingStatus.m_netRoutedCount) { if (routingStatus.m_jumperItemCount == 0) { theText = tr("Routing completed"); } else { theText = tr("Routing completed using %n jumper part(s)", "", routingStatus.m_jumperItemCount); } } else { theText = tr("%1 of %2 nets routed - %n connector(s) still to be routed", "", routingStatus.m_connectorsLeftToRoute) .arg(routingStatus.m_netRoutedCount) .arg(routingStatus.m_netCount); } dynamic_cast(sketchWidget->parent())->routingStatusLabel()->setLabelText(theText); updateTraceMenu(); } void MainWindow::applyReadOnlyChange(bool isReadOnly) { Q_UNUSED(isReadOnly); //m_saveAct->setDisabled(isReadOnly); } const QString MainWindow::fritzingTitle() { if (m_currentGraphicsView == NULL) { return FritzingWindow::fritzingTitle(); } QString fritzing = FritzingWindow::fritzingTitle(); return tr("%1 - [%2]").arg(fritzing).arg(m_currentGraphicsView->viewName()); } QAction *MainWindow::raiseWindowAction() { return m_raiseWindowAct; } void MainWindow::raiseAndActivate() { if(isMinimized()) { showNormal(); } raise(); QTimer::singleShot(20, this, SLOT(activateWindowAux())); } void MainWindow::activateWindowAux() { activateWindow(); } void MainWindow::updateRaiseWindowAction() { QString actionText; QFileInfo fileInfo(m_fwFilename); if(fileInfo.exists()) { int lastSlashIdx = m_fwFilename.lastIndexOf("/"); int beforeLastSlashIdx = m_fwFilename.left(lastSlashIdx).lastIndexOf("/"); actionText = beforeLastSlashIdx > -1 && lastSlashIdx > -1 ? "..." : ""; actionText += m_fwFilename.right(m_fwFilename.size()-beforeLastSlashIdx-1); } else { actionText = m_fwFilename; } m_raiseWindowAct->setText(actionText); m_raiseWindowAct->setToolTip(m_fwFilename); m_raiseWindowAct->setStatusTip("raise \""+m_fwFilename+"\" window"); } QSizeGrip *MainWindow::sizeGrip() { return m_sizeGrip; } QStatusBar *MainWindow::realStatusBar() { return m_statusBar; } void MainWindow::moveEvent(QMoveEvent * event) { FritzingWindow::moveEvent(event); emit mainWindowMoved(this); } bool MainWindow::event(QEvent * e) { switch (e->type()) { case QEvent::WindowActivate: emit changeActivationSignal(true, this); break; case QEvent::WindowDeactivate: emit changeActivationSignal(false, this); break; default: break; } return FritzingWindow::event(e); } void MainWindow::resizeEvent(QResizeEvent * event) { if (m_sizeGrip) { m_sizeGrip->rearrange(); } FritzingWindow::resizeEvent(event); } void MainWindow::enableCheckUpdates(bool enabled) { if (m_checkForUpdatesAct != NULL) { m_checkForUpdatesAct->setEnabled(enabled); } } void MainWindow::swapSelectedDelay(const QString & family, const QString & prop, QMap & currPropsMap, ItemBase * itemBase) { //DebugDialog::debug("swap selected delay"); m_swapTimer.stop(); m_swapTimer.setAll(family, prop, currPropsMap, itemBase); m_swapTimer.start(); } void MainWindow::swapSelectedTimeout() { if (sender() == &m_swapTimer) { QMap map = m_swapTimer.propsMap(); swapSelectedMap(m_swapTimer.family(), m_swapTimer.prop(), map, m_swapTimer.itemBase()); } } void MainWindow::swapSelectedMap(const QString & family, const QString & prop, QMap & currPropsMap, ItemBase * itemBase) { if (itemBase == NULL) return; QString generatedModuleID = currPropsMap.value("moduleID"); bool logoPadBlocker = false; if (generatedModuleID.isEmpty()) { if (prop.compare("layer") == 0) { if (family.compare("logo") == 0 || family.compare("pad") == 0 || family.contains("blocker", Qt::CaseInsensitive)) { QString value = Board::convertToXmlName(currPropsMap.value(prop)); if (value.contains("copper1") && m_currentGraphicsView->boardLayers() == 1) { QMessageBox::warning( this, tr("No copper top layer"), tr("The copper top (copper 1) layer is not available on a one-sided board. Please switch the board to double-sided or choose the copper bottom (copper 0) layer.") ); return; } // use the xml name currPropsMap.insert(prop, value); logoPadBlocker = true; } else if (itemBase->itemType() == ModelPart::Wire) { // assume this option is disabled for a one-sided board, so we would not get here? m_pcbGraphicsView->changeTraceLayer(itemBase, false, NULL); return; } } } if (generatedModuleID.isEmpty()) { if (family.compare("Prototyping Board", Qt::CaseInsensitive) == 0) { if (prop.compare("size", Qt::CaseInsensitive) == 0 || prop.compare("type", Qt::CaseInsensitive) == 0) { QString size = currPropsMap.value("size"); QString type = currPropsMap.value("type"); if (type.compare("perfboard", Qt::CaseInsensitive) == 0) { generatedModuleID = Perfboard::genModuleID(currPropsMap); } else if (type.compare("stripboard", Qt::CaseInsensitive) == 0) { generatedModuleID = Stripboard::genModuleID(currPropsMap); } } } } bool swapLayer = false; ViewLayer::ViewLayerPlacement newViewLayerPlacement = ViewLayer::UnknownPlacement; if (prop.compare("layer") == 0 && !logoPadBlocker) { if (itemBase->modelPart()->flippedSMD() || itemBase->itemType() == ModelPart::Part) { ItemBase * viewItem = itemBase->modelPart()->viewItem(ViewLayer::PCBView); if (viewItem) { ViewLayer::ViewLayerPlacement vlp = (currPropsMap.value(prop) == ItemBase::TranslatedPropertyNames.value("bottom") ? ViewLayer::NewBottom : ViewLayer::NewTop); if (viewItem->viewLayerPlacement() != newViewLayerPlacement) { swapLayer = true; newViewLayerPlacement = vlp; } } } } if (!generatedModuleID.isEmpty()) { ModelPart * modelPart = m_referenceModel->retrieveModelPart(generatedModuleID); if (modelPart == NULL) { if (!m_referenceModel->genFZP(generatedModuleID, m_referenceModel)) { return; } } swapSelectedAux(itemBase->layerKinChief(), generatedModuleID, swapLayer, newViewLayerPlacement, currPropsMap); return; } if (swapLayer) { swapSelectedAux(itemBase->layerKinChief(), itemBase->moduleID(), true, newViewLayerPlacement, currPropsMap); return; } if ((prop.compare("package", Qt::CaseSensitive) != 0) && swapSpecial(prop, currPropsMap)) { return; } foreach (QString key, currPropsMap.keys()) { QString value = currPropsMap.value(key); m_referenceModel->recordProperty(key, value); } QString moduleID = m_referenceModel->retrieveModuleIdWith(family, prop, true); bool exactMatch = m_referenceModel->lastWasExactMatch(); if (moduleID.isEmpty()) { QMessageBox::information( this, tr("Sorry!"), tr( "No part with those characteristics.\n" "We're working to avoid this message, and only let you choose between properties that do exist") ); return; } itemBase = itemBase->layerKinChief(); if(!exactMatch) { AutoCloseMessageBox::showMessage(this, tr("No exactly matching part found; Fritzing chose the closest match.")); } swapSelectedAux(itemBase, moduleID, false, ViewLayer::UnknownPlacement, currPropsMap); } bool MainWindow::swapSpecial(const QString & theProp, QMap & currPropsMap) { ItemBase * itemBase = m_infoView->currentItem(); QString pinSpacing, resistance; int layers = 0; foreach (QString key, currPropsMap.keys()) { if (key.compare("layers", Qt::CaseInsensitive) == 0) { if (!Board::isBoard(itemBase)) continue; QString value = currPropsMap.value(key, ""); if (value.compare(Board::OneLayerTranslated) == 0) { layers = 1; } else if (value.compare(Board::TwoLayersTranslated) == 0) { layers = 2; } } if (key.compare("resistance", Qt::CaseInsensitive) == 0) { resistance = currPropsMap.value(key); continue; } if (key.compare("pin spacing", Qt::CaseInsensitive) == 0) { pinSpacing = currPropsMap.value(key); continue; } } if (layers != 0) { currPropsMap.insert("layers", QString::number(layers)); if (theProp.compare("layers") == 0) { QString msg = (layers == 1) ? tr("Change to single layer pcb") : tr("Change to two layer pcb"); swapLayers(itemBase, layers, msg, SketchWidget::PropChangeDelay); return true; } } if (!resistance.isEmpty() || !pinSpacing.isEmpty()) { if (theProp.contains("band", Qt::CaseInsensitive)) { // swap 4band for 5band or vice versa. return false; } Resistor * resistor = qobject_cast(itemBase); if (resistor != NULL) { m_currentGraphicsView->setResistance(resistance, pinSpacing); return true; } } return false; } void MainWindow::swapLayers(ItemBase * itemBase, int layers, const QString & msg, int delay) { QUndoCommand* parentCommand = new QUndoCommand(msg); new CleanUpWiresCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand); m_pcbGraphicsView->swapLayers(itemBase, layers, parentCommand); // need to defer execution so the content of the info view doesn't change during an event that started in the info view m_undoStack->waitPush(parentCommand, delay); } void MainWindow::swapSelectedAux(ItemBase * itemBase, const QString & moduleID, bool useViewLayerPlacement, ViewLayer::ViewLayerPlacement overrideViewLayerPlacement, QMap & propsMap) { QUndoCommand* parentCommand = new QUndoCommand(tr("Swapped %1 with module %2").arg(itemBase->instanceTitle()).arg(moduleID)); new CleanUpWiresCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(m_breadboardGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand); ViewLayer::ViewLayerPlacement viewLayerPlacement = itemBase->viewLayerPlacement(); ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID); if (m_pcbGraphicsView->boardLayers() == 2) { if (modelPart->flippedSMD()) { //viewLayerPlacement = m_pcbGraphicsView->dropOnBottom() ? ViewLayer::NewBottom : ViewLayer::NewTop; if (useViewLayerPlacement) viewLayerPlacement = overrideViewLayerPlacement; } else if (modelPart->itemType() == ModelPart::Part) { //viewLayerPlacement = m_pcbGraphicsView->dropOnBottom() ? ViewLayer::NewBottom : ViewLayer::NewTop; if (useViewLayerPlacement) viewLayerPlacement = overrideViewLayerPlacement; } } else { if (modelPart->flippedSMD()) { viewLayerPlacement = ViewLayer::NewBottom; } else if (modelPart->itemType() == ModelPart::Part) { //viewLayerPlacement = m_pcbGraphicsView->dropOnBottom() ? ViewLayer::NewBottom : ViewLayer::NewTop; if (useViewLayerPlacement) viewLayerPlacement = overrideViewLayerPlacement; } } swapSelectedAuxAux(itemBase, moduleID, viewLayerPlacement, propsMap, parentCommand); // need to defer execution so the content of the info view doesn't change during an event that started in the info view m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } void MainWindow::swapBoardImageSlot(SketchWidget * sketchWidget, ItemBase * itemBase, const QString & filename, const QString & moduleID, bool addName) { QUndoCommand* parentCommand = new QUndoCommand(tr("Change image to %2").arg(filename)); QMap propsMap; long newID = swapSelectedAuxAux(itemBase, moduleID, itemBase->viewLayerPlacement(), propsMap, parentCommand); LoadLogoImageCommand * cmd = new LoadLogoImageCommand(sketchWidget, newID, "", QSizeF(0,0), filename, filename, addName, parentCommand); cmd->setRedoOnly(); // need to defer execution so the content of the info view doesn't change during an event that started in the info view m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } void MainWindow::subSwapSlot(SketchWidget * sketchWidget, ItemBase * itemBase, const QString & newModuleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, long & newID, QUndoCommand * parentCommand) { Q_UNUSED(sketchWidget); QMap propsMap; newID = swapSelectedAuxAux(itemBase, newModuleID, viewLayerPlacement, propsMap, parentCommand); } long MainWindow::swapSelectedAuxAux(ItemBase * itemBase, const QString & moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, QMap & propsMap, QUndoCommand * parentCommand) { long modelIndex = ModelPart::nextIndex(); QList sketchWidgets; // master view must go last, since it creates the delete command, and possibly has all the local props switch (itemBase->viewID()) { case ViewLayer::SchematicView: sketchWidgets << m_pcbGraphicsView << m_breadboardGraphicsView << m_schematicGraphicsView; break; case ViewLayer::PCBView: sketchWidgets << m_schematicGraphicsView << m_breadboardGraphicsView << m_pcbGraphicsView; break; default: sketchWidgets << m_schematicGraphicsView << m_pcbGraphicsView << m_breadboardGraphicsView; break; } SwapThing swapThing; swapThing.firstTime = true; swapThing.itemBase = itemBase; swapThing.newModelIndex = modelIndex; swapThing.newModuleID = moduleID; swapThing.viewLayerPlacement = viewLayerPlacement; swapThing.parentCommand = parentCommand; swapThing.propsMap = propsMap; swapThing.bbView = m_breadboardGraphicsView; long newID = 0; for (int i = 0; i < 3; i++) { long tempID = sketchWidgets[i]->setUpSwap(swapThing, i == 2); if (newID == 0 && tempID != 0) newID = tempID; } // TODO: z-order? return newID; } void MainWindow::svgMissingLayer(const QString & layername, const QString & path) { QMessageBox::warning( this, tr("Fritzing"), tr("Svg %1 is missing a '%2' layer. " "For more information on how to create a custom board shape, " "see the tutorial at http://fritzing.org/learning/tutorials/designing-pcb/pcb-custom-shape/.") .arg(path) .arg(layername) ); } void MainWindow::addDefaultParts() { if (m_pcbGraphicsView == NULL) return; m_pcbGraphicsView->addDefaultParts(); m_breadboardGraphicsView->addDefaultParts(); m_schematicGraphicsView->addDefaultParts(); } MainWindow * MainWindow::newMainWindow(ReferenceModel *referenceModel, const QString & displayPath, bool showProgress, bool lockFiles, int initialTab) { MainWindow * mw = new MainWindow(referenceModel, NULL); if (showProgress) { mw->showFileProgressDialog(displayPath); } if (initialTab >= 0) mw->setInitialTab(initialTab); mw->init(referenceModel, lockFiles); return mw; } void MainWindow::clearFileProgressDialog() { if (m_fileProgressDialog) { m_fileProgressDialog->close(); delete m_fileProgressDialog; m_fileProgressDialog = NULL; } } void MainWindow::setFileProgressPath(const QString & path) { if (m_fileProgressDialog) m_fileProgressDialog->setMessage(tr("loading %1").arg(path)); } FileProgressDialog * MainWindow::fileProgressDialog() { return m_fileProgressDialog; } void MainWindow::showFileProgressDialog(const QString & path) { m_fileProgressDialog = new FileProgressDialog(tr("Loading..."), 200, this); m_fileProgressDialog->setBinLoadingChunk(50); if (!path.isEmpty()) { setFileProgressPath(QFileInfo(path).fileName()); } else { setFileProgressPath(tr("new sketch")); } } const QString &MainWindow::selectedModuleID() { if(m_currentGraphicsView) { return m_currentGraphicsView->selectedModuleID(); } else { return ___emptyString___; } } void MainWindow::redrawSketch() { QList visited; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { item->update(); ConnectorItem * c = dynamic_cast(item); if (c != NULL) { c->restoreColor(visited); } } } void MainWindow::statusMessage(QString message, int timeout) { QStatusBar * sb = realStatusBar(); if (sb != NULL) { sb->showMessage(message, timeout); } } void MainWindow::dropPaste(SketchWidget * sketchWidget) { Q_UNUSED(sketchWidget); pasteInPlace(); } void MainWindow::updateLayerMenuSlot() { updateLayerMenu(true); } bool MainWindow::save() { bool result = FritzingWindow::save(); if (result) { QSettings settings; settings.setValue("lastOpenSketch", m_fwFilename); } return result; } bool MainWindow::saveAs() { bool convertSchematic = false; if (m_schematicGraphicsView != NULL && m_schematicGraphicsView->isOldSchematic()) { QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Schematic conversion"), tr("Saving this sketch will convert it to the new schematic graphics standard. Go ahead and convert?"), QMessageBox::Yes | QMessageBox::No); if (reply != QMessageBox::Yes) { return false; } convertSchematic = true; } bool result = false; result = FritzingWindow::saveAs(); if (result) { QSettings settings; settings.setValue("lastOpenSketch", m_fwFilename); if (convertSchematic) { MainWindow * mw = revertAux(); QString gridSize = QString("%1in").arg(m_schematicGraphicsView->defaultGridSizeInches()); mw->m_schematicGraphicsView->setGridSize(gridSize); mw->m_schematicGraphicsView->resizeLabels(); mw->m_schematicGraphicsView->resizeWires(); mw->m_schematicGraphicsView->updateWires(); mw->save(); } } return result; } void MainWindow::changeBoardLayers(int layers, bool doEmit) { Q_UNUSED(doEmit); Q_UNUSED(layers); updateActiveLayerButtons(); if (m_currentGraphicsView) m_currentGraphicsView->updateConnectors(); } void MainWindow::updateActiveLayerButtons() { if (m_activeLayerButtonWidget != NULL) { int index = activeLayerIndex(); bool enabled = index >= 0; m_activeLayerButtonWidget->setCurrentIndex(index); m_activeLayerButtonWidget->setVisible(enabled); m_activeLayerBothAct->setEnabled(enabled); m_activeLayerBottomAct->setEnabled(enabled); m_activeLayerTopAct->setEnabled(enabled); QList actions; actions << m_activeLayerBothAct << m_activeLayerBottomAct << m_activeLayerTopAct; setActionsIcons(index, actions); } if (m_viewFromButtonWidget != NULL) { if (m_pcbGraphicsView) { bool viewFromBelow = m_pcbGraphicsView->viewFromBelow(); int index = (viewFromBelow ? 1 : 0); m_viewFromButtonWidget->setCurrentIndex(index); m_viewFromButtonWidget->setVisible(true); m_viewFromBelowToggleAct->setChecked(viewFromBelow); m_viewFromBelowAct->setChecked(viewFromBelow); m_viewFromAboveAct->setChecked(viewFromBelow); m_viewFromBelowToggleAct->setEnabled(true); m_viewFromBelowAct->setEnabled(true); m_viewFromAboveAct->setEnabled(true); } } } int MainWindow::activeLayerIndex() { if (m_currentGraphicsView == NULL) return -1; if (m_currentGraphicsView->boardLayers() == 2 || activeLayerWidgetAlwaysOn()) { bool copper0Visible = m_currentGraphicsView->layerIsActive(ViewLayer::Copper0); bool copper1Visible = m_currentGraphicsView->layerIsActive(ViewLayer::Copper1); if (copper0Visible && copper1Visible) { return 0; } else if (copper1Visible) { return 2; } else if (copper0Visible) { return 1; } } return -1; } bool MainWindow::activeLayerWidgetAlwaysOn() { return false; } /** * A slot for saving a copy of the current sketch to a temp location. * This should be called every X minutes as well as just before certain * events, such as saves, part imports, file export/printing. This relies * on the m_autosaveNeeded variable and the undoStack being dirty for * an autosave to be attempted. */ void MainWindow::backupSketch() { if (ProcessEventBlocker::isProcessing()) { // don't want to autosave during autorouting, for example return; } if (m_autosaveNeeded && !m_undoStack->isClean()) { m_autosaveNeeded = false; // clear this now in case the save takes a really long time DebugDialog::debug(QString("%1 autosaved as %2").arg(m_fwFilename).arg(m_backupFileNameAndPath)); statusBar()->showMessage(tr("Backing up '%1'").arg(m_fwFilename), 2000); ProcessEventBlocker::processEvents(); m_backingUp = true; connectStartSave(true); m_sketchModel->save(m_backupFileNameAndPath, false); connectStartSave(false); m_backingUp = false; } } /** * This function is used to trigger an autosave at the next autosave * timer event. It is connected to the QUndoStack::indexChanged(int) * signal so that any change to the undo stack triggers autosaves. * This function can be called independent of this signal and * still work properly. */ void MainWindow::autosaveNeeded(int index) { Q_UNUSED(index); //DebugDialog::debug(QString("Triggering autosave")); m_autosaveNeeded = true; } /** * delete the backup file when the undostack is clean. */ void MainWindow::undoStackCleanChanged(bool isClean) { // DebugDialog::debug(QString("Clean status changed to %1").arg(isClean)); if (isClean) { QFile::remove(m_backupFileNameAndPath); } } void MainWindow::setAutosavePeriod(int minutes) { setAutosave(minutes, AutosaveEnabled); } void MainWindow::setAutosaveEnabled(bool enabled) { setAutosave(AutosaveTimeoutMinutes, enabled); } void MainWindow::setAutosave(int minutes, bool enabled) { AutosaveTimeoutMinutes = minutes; AutosaveEnabled = enabled; foreach (QWidget * widget, QApplication::topLevelWidgets()) { MainWindow * mainWindow = qobject_cast(widget); if (mainWindow == NULL) continue; mainWindow->m_autosaveTimer.stop(); if (qobject_cast(widget)) { continue; } if (AutosaveEnabled) { // is there a way to get the current timer offset so that all the timers aren't running in sync? // or just add some random time... mainWindow->m_autosaveTimer.start(AutosaveTimeoutMinutes * 60 * 1000); } } } bool MainWindow::hasLinkedProgramFiles(const QString & filename, QStringList & linkedProgramFiles) { QFile file(filename); file.open(QFile::ReadOnly); QXmlStreamReader xml(&file); xml.setNamespaceProcessing(false); bool done = false; while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name().toString().compare("program") == 0) { linkedProgramFiles.append(xml.readElementText()); break; } if (xml.name().toString().compare("views") == 0) { done = true; break; } if (xml.name().toString().compare("instances") == 0) { done = true; break; } default: break; } if (done) break; } return linkedProgramFiles.count() > 0; } QString MainWindow::getExtensionString() { return tr("Fritzing (*%1)").arg(FritzingBundleExtension) + ";;" + \ tr("Fritzing uncompressed (*%1)").arg(FritzingSketchExtension); } QStringList MainWindow::getExtensions() { QStringList extensions; extensions.append(FritzingBundleExtension); extensions.append(FritzingSketchExtension); return extensions; } void MainWindow::routingStatusLabelMousePress(QMouseEvent* event) { routingStatusLabelMouse(event, true); } void MainWindow::routingStatusLabelMouseRelease(QMouseEvent* event) { routingStatusLabelMouse(event, false); } void MainWindow::routingStatusLabelMouse(QMouseEvent*, bool show) { //if (show) DebugDialog::debug("-------"); if (m_currentGraphicsView == NULL) return; QSet toShow; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { VirtualWire * vw = dynamic_cast(item); if (vw == NULL) continue; foreach (ConnectorItem * connectorItem, vw->connector0()->connectedToItems()) { toShow.insert(connectorItem); } foreach (ConnectorItem * connectorItem, vw->connector1()->connectedToItems()) { toShow.insert(connectorItem); } } QList visited; foreach (ConnectorItem * connectorItem, toShow) { //if (show) { // DebugDialog::debug(QString("unrouted %1 %2 %3 %4") // .arg(connectorItem->attachedToInstanceTitle()) // .arg(connectorItem->attachedToID()) // .arg(connectorItem->attachedTo()->title()) // .arg(connectorItem->connectorSharedName())); //} if (connectorItem->isActive() && connectorItem->isVisible() && !connectorItem->hidden() && !connectorItem->layerHidden()) { connectorItem->showEqualPotential(show, visited); } else { connectorItem = connectorItem->getCrossLayerConnectorItem(); if (connectorItem) connectorItem->showEqualPotential(show, visited); } } if (!show && toShow.count() == 0) { QMessageBox::information(this, tr("Unrouted connections"), tr("There are no unrouted connections in this view.")); } } void MainWindow::setReportMissingModules(bool b) { if (m_sketchModel) { m_sketchModel->setReportMissingModules(b); } } void MainWindow::boardDeletedSlot() { activeLayerBottom(); } void MainWindow::cursorLocationSlot(double xinches, double yinches, double width, double height) { if (m_locationLabel) { QString units; double x, y, w, h; QHash precision; precision["mm"] = 1; precision["px"] = 0; precision["in"] = 3; m_locationLabel->setProperty("location", QSizeF(xinches, xinches)); if (m_locationLabelUnits.compare("mm") == 0) { units = "mm"; x = xinches * 25.4; y = yinches * 25.4; w = width * 25.4; h = height * 25.4; } else if (m_locationLabelUnits.compare("px") == 0) { units = "px"; x = xinches * GraphicsUtils::SVGDPI; y = yinches * GraphicsUtils::SVGDPI; w = width * GraphicsUtils::SVGDPI; h = height * GraphicsUtils::SVGDPI; } else { units = "in"; x = xinches; y = yinches; w = width; h = height; } if ( w*h == 0.0) { m_locationLabel->setText(tr("(x,y)=(%1, %2) %3") .arg(x, 0, 'f', precision[units]) .arg(y, 0, 'f', precision[units]) .arg(units) ); } else { m_locationLabel->setText(tr("(x, y)=(%1, %2)\t(width, height)=(%3, %4) %5") .arg(x, 0, 'f', precision[units]) .arg(y, 0, 'f', precision[units]) .arg(w, 0, 'f', precision[units]) .arg(h, 0, 'f', precision[units]) .arg(units) ); } } } void MainWindow::locationLabelClicked() { if (m_locationLabelUnits.compare("mm") == 0) { m_locationLabelUnits = "px"; } else if (m_locationLabelUnits.compare("px") == 0) { m_locationLabelUnits = "in"; } else if (m_locationLabelUnits.compare("in") == 0) { m_locationLabelUnits = "mm"; } else { m_locationLabelUnits = "in"; } if (m_locationLabel) { QVariant variant = m_locationLabel->property("location"); if (variant.isValid()) { QSizeF size = variant.toSizeF(); cursorLocationSlot(size.width(), size.height()); } else { cursorLocationSlot(0.0, 0.0); } } QSettings settings; settings.setValue("LocationInches", QVariant(m_locationLabelUnits)); } void MainWindow::filenameIfSlot(QString & filename) { filename = QFileInfo(fileName()).fileName(); } QList MainWindow::sketchWidgets() { QList list; list << m_breadboardGraphicsView << m_schematicGraphicsView << m_pcbGraphicsView; return list; } void MainWindow::setCloseSilently(bool cs) { m_closeSilently = cs; } PCBSketchWidget * MainWindow::pcbView() { return m_pcbGraphicsView; } void MainWindow::noBackup() { m_autosaveTimer.stop(); } void MainWindow::hideTempPartsBin() { if (m_binManager) m_binManager->hideTempPartsBin(); } void MainWindow::setActiveWire(Wire * wire) { m_activeWire = wire; } void MainWindow::setActiveConnectorItem(ConnectorItem * connectorItem) { m_activeConnectorItem = connectorItem; } const QString & MainWindow::fritzingVersion() { if (m_sketchModel) return m_sketchModel->fritzingVersion(); return ___emptyString___; } void MainWindow::dragEnterEvent(QDragEnterEvent *event) { const QMimeData* mimeData = event->mimeData(); if (mimeData->hasUrls()) { QStringList pathList; QList urlList = mimeData->urls(); // extract the local paths of the files for (int i = 0; i < urlList.size() && i < 32; ++i) { QString fn = urlList.at(i).toLocalFile(); foreach (QString ext, fritzingExtensions()) { if (fn.endsWith(ext)) { event->acceptProposedAction(); return; } } if (fn.endsWith("txt")) { QFile file(fn); if (file.open(QFile::ReadOnly)) { QTextStream stream(&file); while (!stream.atEnd()) { QString line = stream.readLine().trimmed(); foreach (QString ext, fritzingExtensions()) { if (line.endsWith(ext)) { event->acceptProposedAction(); return; } } } } } } } } void MainWindow::dropEvent(QDropEvent *event) { const QMimeData* mimeData = event->mimeData(); if (mimeData->hasUrls()) { QStringList pathList; QList urlList = mimeData->urls(); // extract the local paths of the files for (int i = 0; i < urlList.size() && i < 32; ++i) { mainLoadAux(urlList.at(i).toLocalFile()); } return; } } bool MainWindow::hasAnyAlien() { return m_addedToTemp; } void MainWindow::initStyleSheet() { QString suffix = getStyleSheetSuffix(); QFile styleSheet(QString(":/resources/styles/%1.qss").arg(suffix)); if (!styleSheet.open(QIODevice::ReadOnly)) { DebugDialog::debug(QString("Unable to open :/resources/styles/%1.qss").arg(suffix)); } else { QString platformDependantStyle = ""; QString platformDependantStylePath; #ifdef Q_OS_LINUX if(style()->metaObject()->className()==QString("OxygenStyle")) { QFile oxygenStyleSheet(QString(":/resources/styles/linux-kde-oxygen-%1.qss").arg(suffix)); if(oxygenStyleSheet.open(QIODevice::ReadOnly)) { platformDependantStyle += oxygenStyleSheet.readAll(); } } platformDependantStylePath = QString(":/resources/styles/linux-%1.qss").arg(suffix); #endif #ifdef Q_OS_MAC platformDependantStylePath = QString(":/resources/styles/mac-%1.qss").arg(suffix); #endif #ifdef Q_OS_WIN platformDependantStylePath = QString(":/resources/styles/win-%1.qss").arg(suffix); #endif QFile platformDependantStyleSheet(platformDependantStylePath); if(platformDependantStyleSheet.open(QIODevice::ReadOnly)) { platformDependantStyle += platformDependantStyleSheet.readAll(); } setStyleSheet(styleSheet.readAll()+platformDependantStyle); } } QString MainWindow::getStyleSheetSuffix() { return "fritzing"; } void MainWindow::addToMyParts(ModelPart * modelPart) { if (modelPart != NULL) m_binManager->addToMyParts(modelPart); } bool MainWindow::anyUsePart(const QString & moduleID) { foreach (QWidget *widget, QApplication::topLevelWidgets()) { MainWindow *mainWindow = qobject_cast(widget); if (mainWindow == NULL) continue; if (mainWindow->usesPart(moduleID)) { return true; } } return false; } bool MainWindow::usesPart(const QString & moduleID) { if (m_currentGraphicsView == NULL) return false; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase != NULL && itemBase->moduleID().compare(moduleID) == 0) { return true; } } return false; } bool MainWindow::updateParts(const QString & moduleID, QUndoCommand * parentCommand) { if (m_currentGraphicsView == NULL) return false; QSet itemBases; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->moduleID().compare(moduleID) != 0) continue; itemBases.insert(itemBase->layerKinChief()); } QMap propsMap; foreach (ItemBase * itemBase, itemBases) { swapSelectedAuxAux(itemBase, moduleID, itemBase->viewLayerPlacement(), propsMap, parentCommand); } return true; } void MainWindow::showStatusMessage(const QString & message) { if (sender() == m_statusBar) { return; } if (message == m_statusBar->currentMessage()) { return; } //DebugDialog::debug("show message " + message); m_statusBar->blockSignals(true); m_statusBar->showMessage(message); m_statusBar->blockSignals(false); } void MainWindow::updatePartsBin(const QString & moduleID) { m_binManager->reloadPart(moduleID); } bool MainWindow::hasCustomBoardShape() { if (m_pcbGraphicsView == NULL) return false; return m_pcbGraphicsView->hasCustomBoardShape(); } void MainWindow::selectPartsWithModuleID(ModelPart * modelPart) { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->selectItemsWithModuleID(modelPart); } void MainWindow::addToSketch(QList & modelParts) { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->addToSketch(modelParts); } void MainWindow::initProgrammingWidget() { m_programView = new ProgramWindow(this); connect(m_programView, SIGNAL(linkToProgramFile(const QString &, Platform *, bool, bool)), this, SLOT(linkToProgramFile(const QString &, Platform *, bool, bool))); m_programView->setup(); SketchAreaWidget * sketchAreaWidget = new SketchAreaWidget(m_programView, this, false, true); addTab(sketchAreaWidget, ":/resources/images/icons/TabWidgetCodeActive_icon.png", tr("Code"), true); } ProgramWindow *MainWindow::programmingWidget() { return m_programView; } void MainWindow::orderFabHoverEnter() { m_fireQuoteTimer.stop(); if (!QuoteDialog::quoteSucceeded()) return; if (m_rolloverQuoteDialog && m_rolloverQuoteDialog->isVisible()) return; m_fireQuoteTimer.setInterval(fireQuoteDelay()); m_fireQuoteTimer.start(); } void MainWindow::fireQuote() { m_fireQuoteTimer.stop(); if (!QuoteDialog::quoteSucceeded()) return; m_rolloverQuoteDialog = m_pcbGraphicsView->quoteDialog(m_pcbWidget); if (m_rolloverQuoteDialog == NULL) return; //DebugDialog::debug("enter fab button"); //QWidget * toolbar = m_pcbWidget->toolbar(); //QRect r = toolbar->geometry(); //QPointF p = toolbar->parentWidget()->mapToGlobal(r.topLeft()); Qt::WindowFlags flags = m_rolloverQuoteDialog->windowFlags(); flags = Qt::Window | Qt::Dialog | Qt::FramelessWindowHint; m_rolloverQuoteDialog->setWindowFlags(flags); m_rolloverQuoteDialog->show(); // seems to require setting position after show() QRect b = m_orderFabButton->geometry(); QPoint bt = m_orderFabButton->parentWidget()->mapToGlobal(b.topRight()); QRect t = m_pcbWidget->toolbar()->geometry(); QPoint gt = m_pcbWidget->toolbar()->parentWidget()->mapToGlobal(t.topLeft()); QRect q = m_rolloverQuoteDialog->geometry(); // I don't understand why--perhaps due to the windowFlags--but q is already in global coordinates q.moveBottom(gt.y() - 20); q.moveRight(bt.x()); m_rolloverQuoteDialog->setGeometry(q); } void MainWindow::orderFabHoverLeave() { m_fireQuoteTimer.stop(); //DebugDialog::debug("leave fab button"); if (m_rolloverQuoteDialog) { m_rolloverQuoteDialog->hide(); } } void MainWindow::initWelcomeView() { m_welcomeView = new WelcomeView(this); m_welcomeView->setObjectName("WelcomeView"); SketchAreaWidget * sketchAreaWidget = new SketchAreaWidget(m_welcomeView, this, false, false); addTab(sketchAreaWidget, ":/resources/images/icons/TabWidgetWelcomeActive_icon.png", tr("Welcome"), true); } void MainWindow::setInitialView() { // do this the first time, since the current_changed signal wasn't sent int tab = 0; tabWidget_currentChanged(tab+1); tabWidget_currentChanged(tab); // default to breadboard view this->setCurrentTabIndex(m_initialTab); } void MainWindow::updateWelcomeViewRecentList(bool doEmit) { if (m_welcomeView) { m_welcomeView->updateRecent(); if (doEmit) { foreach (QWidget *widget, QApplication::topLevelWidgets()) { MainWindow *mainWin = qobject_cast(widget); if (mainWin && mainWin != this) { mainWin->updateWelcomeViewRecentList(false); } } } } } void MainWindow::initZoom() { if (m_currentGraphicsView == NULL) return; if (m_currentGraphicsView->everZoomed()) return; if (!m_currentGraphicsView->isVisible()) return; bool parts = false; foreach (QGraphicsItem * item, m_currentGraphicsView->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isEverVisible()) continue; parts = true; break; } if (parts) { m_currentGraphicsView->fitInWindow(); } m_currentGraphicsView->setEverZoomed(true); } int MainWindow::fireQuoteDelay() { return m_fireQuoteDelay; } void MainWindow::setFireQuoteDelay(int delay) { m_fireQuoteDelay = delay; } void MainWindow::noSchematicConversion() { m_noSchematicConversion = true; } void MainWindow::setInitialTab(int tab) { m_initialTab = tab; } fritzing-app-0.9.3b/src/mainwindow/mainwindow.h000066400000000000000000000764471272363601300216140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6999 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 14:14:07 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fritzingwindow.h" #include "sketchareawidget.h" #include "../viewlayer.h" #include "../program/programwindow.h" #include "../svg/svg2gerber.h" #include "../routingstatus.h" QT_BEGIN_NAMESPACE class QAction; class QListWidget; class QMenu; QT_END_NAMESPACE class FSizeGrip; typedef class FDockWidget * (*DockFactory)(const QString & title, QWidget * parent); bool sortPartList(class ItemBase * b1, class ItemBase * b2); static const QString ORDERFABENABLED = "OrderFabEnabled"; class FTabWidget : public QTabWidget { Q_OBJECT public: FTabWidget(QWidget * parent = NULL); //int addTab(QWidget * page, const QIcon & icon, const QIcon & hoverIcon, const QIcon & inactiveIcon, const QString & label); protected slots: //void tabIndexChanged(int index); protected: //QList m_inactiveIcons; //QList m_hoverIcons; //QList m_icons; }; class FTabBar : public QTabBar { Q_OBJECT public: FTabBar(); void paintEvent(QPaintEvent *); protected: bool m_firstTime; }; class SwapTimer : public QTimer { Q_OBJECT public: SwapTimer(); void setAll(const QString & family, const QString & prop, QMap & propsMap, ItemBase *); const QString & family(); const QString & prop(); QMap propsMap(); ItemBase * itemBase(); protected: QString m_family; QString m_prop; QMap m_propsMap; QPointer m_itemBase; }; struct GridSizeThing { QLineEdit * lineEdit; QDoubleValidator * validator; QRadioButton * mmButton; QRadioButton * inButton; double defaultGridSize; QString gridSizeText; QString viewName; QString shortName; GridSizeThing(const QString & viewName, const QString & shortName, double defaultSize, const QString & gridSizeText); }; class GridSizeDialog : public QDialog { Q_OBJECT public: GridSizeDialog(GridSizeThing *); GridSizeThing * gridSizeThing(); protected: GridSizeThing * m_gridSizeThing; }; struct TraceMenuThing { int boardCount; int boardSelectedCount; bool jiEnabled; bool exEnabled; bool viaEnabled; bool exChecked; bool gfrEnabled; bool gfsEnabled; TraceMenuThing() { boardCount = 0; boardSelectedCount = 0; gfsEnabled = false; jiEnabled = false; exEnabled = false; exChecked = true; viaEnabled = false; gfrEnabled = false; } }; class MainWindow : public FritzingWindow { Q_OBJECT Q_PROPERTY(int fireQuoteDelay READ fireQuoteDelay WRITE setFireQuoteDelay DESIGNABLE true) public: MainWindow(class ReferenceModel *referenceModel, QWidget * parent); MainWindow(QFile & fileToLoad); ~MainWindow(); void mainLoad(const QString & fileName, const QString & displayName, bool checkObsolete); bool loadWhich(const QString & fileName, bool setAsLastOpened, bool addToRecent, bool checkObsolete, const QString & displayName); void notClosableForAWhile(); QAction *raiseWindowAction(); QSizeGrip *sizeGrip(); QStatusBar *realStatusBar(); void enableCheckUpdates(bool enabled); void getPartsEditorNewAnd(ItemBase * fromItem); void addDefaultParts(); void init(ReferenceModel *referenceModel, bool lockFiles); void showFileProgressDialog(const QString & path); void setFileProgressPath(const QString & path); void clearFileProgressDialog(); class FileProgressDialog * fileProgressDialog(); const QString &selectedModuleID(); void saveDocks(); void restoreDocks(); void redrawSketch(); // if we consider a part as the smallest ("atomic") entity inside // fritzing, then this functions may help with the bundle tasks // on the complex entities: sketches, bins, modules (?) void saveBundledNonAtomicEntity(QString &filename, const QString &extension, Bundler *bundler, const QList &partsToSave, bool askForFilename, const QString & destFolderPath, bool saveModel, bool deleteLeftovers); bool loadBundledNonAtomicEntity(const QString &filename, Bundler *bundler, bool addToBin, bool dontAsk); void saveAsShareable(const QString & path, bool saveModel); void setCurrentFile(const QString &fileName, bool addToRecent, bool setAsLastOpened); void setReportMissingModules(bool); QList sketchWidgets(); ProgramWindow * programmingWidget(); void setCloseSilently(bool); class PCBSketchWidget * pcbView(); void noBackup(); void swapSelectedAux(ItemBase * itemBase, const QString & moduleID, bool useViewLayerPlacement, ViewLayer::ViewLayerPlacement, QMap & propsMap); void swapLayers(ItemBase * itemBase, int layers, const QString & msg, int delay); bool saveAsAux(const QString & fileName); void swapObsolete(bool displayFeedback, QList &); QList selectAllObsolete(bool displayFeedback); void hideTempPartsBin(); const QString & fritzingVersion(); void removeGroundFill(ViewLayer::ViewLayerID, QUndoCommand * parentCommand); void groundFill(ViewLayer::ViewLayerID); void copperFill(ViewLayer::ViewLayerID); bool hasAnyAlien(); void exportSvg(double res, bool selectedItems, bool flatten, const QString & filename); void setCurrentView(ViewLayer::ViewID); bool usesPart(const QString & moduleID); bool anyUsePart(const QString & moduleID); bool updateParts(const QString & moduleID, QUndoCommand * parentCommand); void updatePartsBin(const QString & moduleID); bool hasCustomBoardShape(); void selectPartsWithModuleID(ModelPart *); void addToSketch(QList &); QStringList newDesignRulesCheck(bool showOkMessage); int fireQuoteDelay(); void setFireQuoteDelay(int); void setInitialTab(int); void noSchematicConversion(); public: static void initNames(); static MainWindow * newMainWindow(ReferenceModel *referenceModel, const QString & displayPath, bool showProgress, bool lockFiles, int initialTab); static void setAutosavePeriod(int); static void setAutosaveEnabled(bool); signals: void alienPartsDismissed(); void mainWindowMoved(QWidget *); void changeActivationSignal(bool activate, QWidget * originator); void externalProcessSignal(QString & name, QString & path, QStringList & args); public slots: void ensureClosable(); ModelPart* loadBundledPart(const QString &fileName, bool addToBin); void acceptAlienFiles(); void statusMessage(QString message, int timeout); void showPCBView(); void groundFill(); void removeGroundFill(); void copperFill(); void setOneGroundFillSeed(); void setGroundFillSeeds(); void clearGroundFillSeeds(); void changeBoardLayers(int layers, bool doEmit); void selectAllObsolete(); void swapObsolete(); void swapBoardImageSlot(SketchWidget * sketchWidget, ItemBase * itemBase, const QString & filename, const QString & moduleID, bool addName); void updateTraceMenu(); virtual void updateExportMenu(); virtual void updateFileMenu(); void showStatusMessage(const QString &); void orderFabHoverEnter(); void orderFabHoverLeave(); void setGroundFillKeepout(); void oldSchematicsSlot(const QString & filename, bool & useOldSchematics); void showWelcomeView(); protected slots: void mainLoad(); void revert(); void openRecentOrExampleFile(); void openRecentOrExampleFile(const QString & filename, const QString & actionText); void print(); void doExport(); void exportEtchable(); void about(); void tipsAndTricks(); void firstTimeHelp(); void copy(); void cut(); void paste(); void pasteInPlace(); void duplicate(); void doDelete(); void doDeleteMinus(); void selectAll(); void deselect(); void zoomIn(); void zoomOut(); void fitInWindow(); void actualSize(); void hundredPercentSize(); void alignToGrid(); void showGrid(); void setGridSize(); void setBackgroundColor(); void showBreadboardView(); void showSchematicView(); void showProgramView(); void showPartsBinIconView(); void showPartsBinListView(); virtual void updateEditMenu(); virtual void updateLayerMenu(bool resetLayout = false); void updatePartMenu(); virtual void updateWireMenu(); void updateTransformationActions(); void updateRecentFileActions(); virtual void tabWidget_currentChanged(int index); void createNewSketch(); void minimize(); void toggleToolbar(bool toggle); void togglePartLibrary(bool toggle); void toggleInfo(bool toggle); void toggleUndoHistory(bool toggle); void toggleDebuggerOutput(bool toggle); void openHelp(); void openDonate(); void openExamples(); void openPartsReference(); void visitFritzingDotOrg(); void partsEditorHelp(); virtual void updateWindowMenu(); void pageSetup(); void sendToBack(); void sendBackward(); void bringForward(); void bringToFront(); void alignLeft(); void alignRight(); void alignVerticalCenter(); void alignTop(); void alignHorizontalCenter(); void alignBottom(); void rotate90cw(); void rotate90ccw(); void rotate180(); void rotate45ccw(); void rotate45cw(); void rotateIncCCW(); void rotateIncCW(); void rotateIncCCWRubberBand(); void rotateIncCWRubberBand(); void flipHorizontal(); void flipVertical(); void showAllLayers(); void hideAllLayers(); void addBendpoint(); void convertToVia(); void convertToBendpoint(); void flattenCurve(); void disconnectAll(); void openInPartsEditorNew(); void openNewPartsEditor(class PaletteItem *); void updateZoomSlider(double zoom); void updateZoomOptionsNoMatterWhat(double zoom); void updateViewZoom(double newZoom); void setInfoViewOnHover(bool infoViewOnHover); void updateItemMenu(); void newAutoroute(); void orderFab(); void activeLayerTop(); void activeLayerBottom(); void activeLayerBoth(); void toggleActiveLayer(); void createTrace(); void excludeFromAutoroute(); void selectAllTraces(); void showUnrouted(); void selectAllCopperFill(); void updateRoutingStatus(); void selectAllExcludedTraces(); void selectAllIncludedTraces(); void selectAllJumperItems(); void selectAllVias(); void shareOnline(); void saveBundledPart(const QString &moduleId=___emptyString___); QStringList saveBundledAux(ModelPart *mp, const QDir &destFolder); void binSaved(bool hasAlienParts); void routingStatusSlot(class SketchWidget *, const RoutingStatus &); void applyReadOnlyChange(bool isReadOnly); void raiseAndActivate(); void activateWindowAux(); void showPartLabels(); void addNote(); void reportBug(); void enableDebug(); void tidyWires(); void changeWireColor(bool checked); void startSaveInstancesSlot(const QString & fileName, ModelPart *, QXmlStreamWriter &); void loadedViewsSlot(class ModelBase *, QDomElement & views); void loadedRootSlot(const QString & filename, ModelBase *, QDomElement & views); void obsoleteSMDOrientationSlot(); void exportNormalizedSVG(); void exportNormalizedFlattenedSVG(); void dumpAllParts(); void testConnectors(); void launchExternalProcess(); bool externalProcess(QString & name, QString & path, QStringList & args); void processError(QProcess::ProcessError processError); void processFinished(int exitCode, QProcess::ExitStatus exitStatus); void processReadyRead(); void processStateChanged(QProcess::ProcessState newState); void throwFakeException(); void dropPaste(SketchWidget *); void openProgramWindow(); void linkToProgramFile(const QString & filename, Platform * platform, bool addLink, bool strong); QStringList newDesignRulesCheck(); void subSwapSlot(SketchWidget *, ItemBase *, const QString & newModuleID, ViewLayer::ViewLayerPlacement, long & newID, QUndoCommand * parentCommand); void updateLayerMenuSlot(); bool save(); bool saveAs(); virtual void backupSketch(); void undoStackCleanChanged(bool isClean); void autosaveNeeded(int index = 0); void changeTraceLayer(); void routingStatusLabelMousePress(QMouseEvent*); void routingStatusLabelMouseRelease(QMouseEvent*); void selectMoveLock(); void moveLock(); void setSticky(); void autorouterSettings(); void boardDeletedSlot(); void cursorLocationSlot(double, double, double=0.0, double=0.0); void locationLabelClicked(); void swapSelectedMap(const QString & family, const QString & prop, QMap & currPropsMap, ItemBase *); void swapSelectedDelay(const QString & family, const QString & prop, QMap & currPropsMap, ItemBase *); void swapSelectedTimeout(); void filenameIfSlot(QString & filename); void openURL(); void setActiveWire(class Wire *); void setActiveConnectorItem(class ConnectorItem *); void gridUnits(bool); void restoreDefaultGrid(); void checkLoadedTraces(); void keepMargins(); void dockChangeActivation(bool activate, QWidget * originator); void addToMyParts(ModelPart *); void hidePartSilkscreen(); void fabQuote(); void findPartInSketch(); void fireQuote(); void setViewFromBelowToggle(); void setViewFromBelow(); void setViewFromAbove(); void updateWelcomeViewRecentList(bool doEmit = true); virtual void initZoom(); protected: void initSketchWidget(SketchWidget *); virtual void initProgrammingWidget(); virtual void createActions(); virtual void createFileMenuActions(); void createExportActions(); void createRaiseWindowActions(); void createOrderFabAct(); void createOpenExampleMenu(); void createActiveLayerActions(); void populateMenuFromXMLFile(QMenu *parentMenu, QStringList &actionsTracker, const QString &folderPath, const QString &indexFileName); QHash indexAvailableElements(QDomElement &domElem, const QString &srcPrefix, QStringList & actionsTracker, const QString & localeName); void populateMenuWithIndex(const QHash &, QMenu * parentMenu, QDomElement &domElem, const QString & localeName); void populateMenuFromFolderContent(QMenu *parentMenu, const QString &path); void createOpenRecentMenu(); void createEditMenuActions(); void createPartMenuActions(); virtual void createViewMenuActions(bool showWelcome); void createWindowMenuActions(); void createHelpMenuActions(); virtual void createMenus(); void createStatusBar(); virtual void connectPairs(); void connectPair(SketchWidget * signaller, SketchWidget * slotter); void closeEvent(QCloseEvent * event); void saveAsAuxAux(const QString & fileName); void printAux(QPrinter &printer, bool removeBackground, bool paginate); void exportAux(QString fileName, QImage::Format format, int quality, bool removeBackground); void notYetImplemented(QString action); bool eventFilter(QObject *obj, QEvent *event); void setActionsIcons(int index, QList &); void exportToEagle(); void exportToGerber(); void exportBOM(); void exportNetlist(); void exportSpiceNetlist(); void exportSvg(double res, bool selectedItems, bool flatten); void exportSvgWatermark(QString & svg, double res); void exportEtchable(bool wantPDF, bool wantSVG); virtual QList getButtonsForView(ViewLayer::ViewID viewId); const QString untitledFileName(); int &untitledFileCount(); const QString fileExtension(); QString getExtensionString(); QStringList getExtensions(); const QString defaultSaveFolder(); bool undoStackIsEmpty(); void createTraceMenuActions(); void hideShowTraceMenu(); void hideShowProgramMenu(); void updatePCBTraceMenu(QGraphicsItem *, TraceMenuThing &); QList moveToPartsFolder(QDir &unzipDir, MainWindow* mw, bool addToBin, bool addToAlien, const QString & prefixFolder, const QString &destFolder, bool importingSinglePart); QString copyToSvgFolder(const QFileInfo& file, bool addToAlien, const QString & prefixFolder, const QString &destFolder); ModelPart* copyToPartsFolder(const QFileInfo& file, bool addToAlien, const QString & prefixFolder, const QString &destFolder); void closeIfEmptySketch(MainWindow* mw); bool whatToDoWithAlienFiles(); void backupExistingFileIfExists(const QString &destFilePath); void recoverBackupedFiles(); void resetTempFolder(); void saveLastTabList(); virtual QMenu *breadboardItemMenu(); virtual QMenu *schematicItemMenu(); virtual QMenu *pcbItemMenu(); virtual QMenu *pcbWireMenu(); virtual QMenu *schematicWireMenu(); virtual QMenu *breadboardWireMenu(); QMenu *viewItemMenuAux(QMenu* menu); void createZoomOptions(SketchAreaWidget* parent); class SketchToolButton *createRotateButton(SketchAreaWidget *parent); SketchToolButton *createShareButton(SketchAreaWidget *parent); SketchToolButton *createFlipButton(SketchAreaWidget *parent); SketchToolButton *createAutorouteButton(SketchAreaWidget *parent); SketchToolButton *createOrderFabButton(SketchAreaWidget *parent); QWidget *createActiveLayerButton(SketchAreaWidget *parent); QWidget *createViewFromButton(SketchAreaWidget *parent); class ExpandingLabel * createRoutingStatusLabel(SketchAreaWidget *); SketchToolButton *createExportEtchableButton(SketchAreaWidget *parent); SketchToolButton *createNoteButton(SketchAreaWidget *parent); QWidget *createToolbarSpacer(SketchAreaWidget *parent); SketchAreaWidget *currentSketchArea(); const QString fritzingTitle(); virtual void updateRaiseWindowAction(); void moveEvent(QMoveEvent * event); bool event(QEvent *); void resizeEvent(QResizeEvent * event); QString genIcon(SketchWidget *, LayerList & partViewLayerIDs, LayerList & wireViewLayerIDs); bool alreadyOpen(const QString & fileName); void svgMissingLayer(const QString & layername, const QString & path); long swapSelectedAuxAux(ItemBase * itemBase, const QString & moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, QMap & propsMap, QUndoCommand * parentCommand); bool swapSpecial(const QString & prop, QMap & currPropsMap); void enableAddBendpointAct(QGraphicsItem *); class FileProgressDialog * exportProgress(); QString constructFileName(const QString & differentiator, const QString & extension); bool isGroundFill(ItemBase * itemBase); QString getBoardSvg(ItemBase * board, int res, LayerList &); QString mergeBoardSvg(QString & svg, ItemBase * board, int res, bool flip, LayerList &); void updateActiveLayerButtons(); int activeLayerIndex(); bool hasLinkedProgramFiles(const QString & filename, QStringList & linkedProgramFiles); void pasteAux(bool pasteInPlace); void routingStatusLabelMouse(QMouseEvent*, bool show); class Wire * retrieveWire(); class ConnectorItem * retrieveConnectorItem(); QString getBomProps(ItemBase *); ModelPart * findReplacedby(ModelPart * originalModelPart); void groundFillAux(bool fillGroundTraces, ViewLayer::ViewLayerID viewLayerID); void groundFillAux2(bool fillGroundTraces); void connectStartSave(bool connect); void loadBundledSketch(const QString &fileName, bool addToRecent, bool setAsLastOpened, bool checkObsolete); void dropEvent(QDropEvent *event); void dragEnterEvent(QDragEnterEvent *event); void mainLoadAux(const QString & fileName); QWidget * createGridSizeForm(GridSizeThing *); void massageOutput(QString & svg, bool doMask, bool doSilk, bool doPaste, QString & maskTop, QString & maskBottom, const QString & fileName, ItemBase * board, int dpi, const LayerList &); virtual void initLockedFiles(bool lockFiles); virtual void initSketchWidgets(bool withIcons); virtual void initWelcomeView(); virtual void initDock(); virtual void initMenus(); virtual void moreInitDock(); virtual void setInitialView(); virtual void createFileMenu(); virtual void createEditMenu(); virtual void createPartMenu(); virtual void createViewMenu(); virtual void createWindowMenu(); virtual void createTraceMenus(); virtual void createHelpMenu(); virtual void createRotateSubmenu(QMenu * parentMenu); virtual void createZOrderSubmenu(QMenu * parentMenu); // virtual void createZOrderWireSubmenu(QMenu * parentMenu); virtual void createAlignSubmenu(QMenu * parentMenu); virtual void createAddToBinSubmenu(QMenu * parentMenu); virtual void populateExportMenu(); // dock management void createDockWindows(); void dontKeepMargins(); class FDockWidget * makeDock(const QString & title, QWidget * widget, int dockMinHeight, int dockDefaultHeight, Qt::DockWidgetArea area = Qt::RightDockWidgetArea, DockFactory dockFactory = NULL); class FDockWidget * dockIt(FDockWidget* dock, int dockMinHeight, int dockDefaultHeight, Qt::DockWidgetArea area = Qt::RightDockWidgetArea); FDockWidget *newTopWidget(); FDockWidget *newBottomWidget(); void removeMargin(FDockWidget* dock); void addTopMargin(FDockWidget* dock); void addBottomMargin(FDockWidget* dock); void dockMarginAux(FDockWidget* dock, const QString &name, const QString &style); void initStyleSheet(); virtual QString getStyleSheetSuffix(); virtual QWidget * createTabWidget(); virtual void addTab(QWidget * widget, const QString & label); virtual void addTab(QWidget * widget, const QString & iconPath, const QString & label, bool withIcon); virtual int currentTabIndex(); virtual void setCurrentTabIndex(int); virtual QWidget * currentTabWidget(); virtual bool activeLayerWidgetAlwaysOn(); bool copySvg(const QString & path, QFileInfoList & svgEntryInfoList); void checkSwapObsolete(QList &, bool includeUpdateLaterMessage); QMessageBox::StandardButton oldSchematicMessage(const QString & filename); MainWindow * revertAux(); protected: static void removeActionsStartingAt(QMenu *menu, int start=0); static void setAutosave(int, bool); protected: QUndoGroup *m_undoGroup; QUndoView *m_undoView; QPointer m_breadboardWidget; QPointer m_breadboardGraphicsView; QPointer m_schematicWidget; QPointer m_schematicGraphicsView; QPointer m_pcbWidget; QPointer m_pcbGraphicsView; QPointer m_welcomeWidget; class WelcomeView * m_welcomeView; QPointer m_binManager; QPointer m_tabWidget; QPointer m_referenceModel; QPointer m_sketchModel; QPointer m_infoView; QPointer m_toolbar; bool m_closing; bool m_dontClose; bool m_firstOpen; QPointer m_currentWidget; QPointer m_currentGraphicsView; //QToolBar *m_fileToolBar; //QToolBar *m_editToolBar; QAction *m_raiseWindowAct; // Fritzing Menu QMenu *m_fritzingMenu; QAction *m_aboutAct; QAction *m_preferencesAct; QAction *m_quitAct; QAction *m_exceptionAct; // File Menu enum { MaxRecentFiles = 10 }; QMenu *m_fileMenu; QAction *m_newAct; QAction *m_openAct; QAction *m_revertAct; QMenu *m_openRecentFileMenu; QAction *m_openRecentFileActs[MaxRecentFiles]; QMenu *m_openExampleMenu; QAction *m_saveAct; QAction *m_saveAsAct; QAction *m_pageSetupAct; QAction *m_printAct; QAction *m_shareOnlineAct; QAction * m_launchExternalProcessAct; QMenu *m_zOrderMenu; QMenu *m_zOrderWireMenu; QAction *m_bringToFrontAct; QAction *m_bringForwardAct; QAction *m_sendBackwardAct; QAction *m_sendToBackAct; class WireAction *m_bringToFrontWireAct; class WireAction *m_bringForwardWireAct; class WireAction *m_sendBackwardWireAct; class WireAction *m_sendToBackWireAct; QMenu *m_alignMenu; QAction * m_alignVerticalCenterAct; QAction * m_alignHorizontalCenterAct; QAction * m_alignTopAct; QAction * m_alignLeftAct; QAction * m_alignBottomAct; QAction * m_alignRightAct; // Export Menu QMenu *m_exportMenu; QAction *m_exportJpgAct; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QAction *m_exportPsAct; #endif QAction *m_exportPngAct; QAction *m_exportPdfAct; QAction *m_exportEagleAct; QAction *m_exportGerberAct; QAction *m_exportEtchablePdfAct; QAction *m_exportEtchableSvgAct; QAction *m_exportBomAct; QAction *m_exportNetlistAct; QAction *m_exportSpiceNetlistAct; QAction *m_exportSvgAct; // Edit Menu QMenu *m_editMenu; QAction *m_undoAct; QAction *m_redoAct; QAction *m_cutAct; QAction *m_copyAct; QAction *m_pasteAct; QAction *m_pasteInPlaceAct; QAction *m_duplicateAct; QAction *m_deleteAct; QAction *m_deleteMinusAct; class WireAction *m_deleteWireAct; class WireAction *m_deleteWireMinusAct; QAction *m_selectAllAct; QAction *m_deselectAct; QAction *m_addNoteAct; // Part Menu QMenu *m_partMenu; QAction *m_infoViewOnHoverAction; QAction *m_exportNormalizedSvgAction; QAction *m_exportNormalizedFlattenedSvgAction; QAction *m_dumpAllPartsAction; QAction *m_testConnectorsAction; QAction *m_openInPartsEditorNewAct; QMenu *m_addToBinMenu; QMenu *m_rotateMenu; QAction *m_rotate90cwAct; QAction *m_rotate180Act; QAction *m_rotate90ccwAct; QAction *m_rotate45ccwAct; QAction *m_rotate45cwAct; QAction *m_moveLockAct; QAction *m_stickyAct; QAction *m_selectMoveLockAct; QAction *m_flipHorizontalAct; QAction *m_flipVerticalAct; QAction *m_showPartLabelAct; QAction *m_saveBundledPart; QAction *m_disconnectAllAct; QAction *m_selectAllObsoleteAct; QAction *m_swapObsoleteAct; QAction *m_findPartInSketchAct; QAction * m_openProgramWindowAct; QAction *m_addBendpointAct; QAction *m_convertToViaAct; QAction *m_convertToBendpointAct; QAction * m_convertToBendpointSeparator; QAction *m_flattenCurveAct; QAction *m_showAllLayersAct; QAction *m_hideAllLayersAct; QAction *m_hidePartSilkscreenAct; QAction *m_regeneratePartsDatabaseAct; // View Menu QMenu *m_viewMenu; QAction *m_zoomInAct; QAction *m_zoomInShortcut; QAction *m_zoomOutAct; QAction *m_fitInWindowAct; QAction *m_actualSizeAct; QAction *m_100PercentSizeAct; QAction *m_alignToGridAct; QAction *m_showGridAct; QAction *m_setGridSizeAct; QAction *m_setBackgroundColorAct; QAction *m_showWelcomeAct; QAction *m_showBreadboardAct; QAction *m_showSchematicAct; QAction *m_showProgramAct; QAction *m_showPCBAct; QAction *m_showPartsBinIconViewAct; QAction *m_showPartsBinListViewAct; //QAction *m_toggleToolbarAct; int m_numFixedActionsInViewMenu; // Window Menu QMenu *m_windowMenu; QAction *m_minimizeAct; QAction *m_togglePartLibraryAct; QAction *m_toggleInfoAct; QAction *m_toggleUndoHistoryAct; QAction *m_toggleDebuggerOutputAct; QAction *m_windowMenuSeparator; // Trace Menu QMenu *m_pcbTraceMenu; QMenu *m_schematicTraceMenu; QMenu *m_breadboardTraceMenu; QAction *m_newAutorouteAct; QAction *m_orderFabAct; QAction *m_activeLayerTopAct; QAction *m_activeLayerBottomAct; QAction *m_activeLayerBothAct; QAction *m_viewFromBelowToggleAct; QAction *m_viewFromBelowAct; QAction *m_viewFromAboveAct; class WireAction *m_createTraceWireAct; class WireAction *m_createWireWireAct; QAction *m_createJumperAct; QAction *m_changeTraceLayerAct; class WireAction *m_changeTraceLayerWireAct; QAction *m_excludeFromAutorouteAct; class WireAction *m_excludeFromAutorouteWireAct; QAction * m_showUnroutedAct; QAction *m_selectAllTracesAct; QAction *m_selectAllWiresAct; QAction *m_selectAllCopperFillAct; QAction *m_updateRoutingStatusAct; QAction *m_selectAllExcludedTracesAct; QAction *m_selectAllIncludedTracesAct; QAction *m_selectAllJumperItemsAct; QAction *m_selectAllViasAct; QAction *m_groundFillAct; QAction *m_removeGroundFillAct; QAction *m_copperFillAct; class ConnectorItemAction *m_setOneGroundFillSeedAct; QAction *m_setGroundFillSeedsAct; QAction *m_clearGroundFillSeedsAct; QAction *m_setGroundFillKeepoutAct; QAction *m_newDesignRulesCheckAct; QAction *m_autorouterSettingsAct; QAction *m_fabQuoteAct; QAction *m_tidyWiresAct; QAction *m_checkLoadedTracesAct; // Help Menu QMenu *m_helpMenu; QAction *m_openHelpAct; QAction *m_openDonateAct; QAction *m_examplesAct; QAction *m_partsRefAct; QAction *m_visitFritzingDotOrgAct; QAction *m_checkForUpdatesAct; QAction *m_aboutQtAct; QAction *m_reportBugAct; QAction *m_enableDebugAct; QAction *m_partsEditorHelpAct; QAction *m_tipsAndTricksAct; QAction *m_firstTimeHelpAct; // Wire Color Menu QMenu * m_breadboardWireColorMenu; QMenu * m_schematicWireColorMenu; // Dot icons QIcon m_dotIcon; QIcon m_emptyIcon; QList m_rotateButtons; QList m_flipButtons; QStackedWidget * m_activeLayerButtonWidget; QStackedWidget * m_viewFromButtonWidget; bool m_comboboxChanged; bool m_restarting; QStringList m_alienFiles; QString m_alienPartsMsg; QStringList m_filesReplacedByAlienOnes; QStringList m_openExampleActions; QPointer m_sizeGrip; QTimer m_setUpDockManagerTimer; QPointer m_fileProgressDialog; QPointer m_zoomSlider; QPointer m_locationLabel; QString m_locationLabelUnits; QByteArray m_externalProcessOutput; QPointer m_layerPalette; QPointer m_programWindow; QPointer m_programView; QList m_linkedProgramFiles; QString m_backupFileNameAndPath; QTimer m_autosaveTimer; QTimer m_fireQuoteTimer; bool m_autosaveNeeded; bool m_backingUp; QString m_bundledSketchName; RoutingStatus m_routingStatus; bool m_orderFabEnabled; bool m_closeSilently; QString m_fzzFolder; QHash m_fzzFiles; SwapTimer m_swapTimer; QPointer m_activeWire; QPointer m_activeConnectorItem; bool m_addedToTemp; QString m_settingsPrefix; bool m_convertedSchematic; bool m_useOldSchematic; bool m_noSchematicConversion; int m_initialTab; // dock management QList m_docks; FDockWidget* m_topDock; FDockWidget* m_bottomDock; QString m_oldTopDockStyle; QString m_oldBottomDockStyle; bool m_dontKeepMargins; QPointer m_rolloverQuoteDialog; bool m_obsoleteSMDOrientation; QWidget * m_orderFabButton; int m_fireQuoteDelay; public: static int AutosaveTimeoutMinutes; static bool AutosaveEnabled; static QString BackupFolder; static const int DockMinWidth; static const int DockMinHeight; protected: static const QString UntitledSketchName; static int UntitledSketchIndex; static int CascadeFactorX; static int CascadeFactorY; static QRegExp GuidMatcher; }; #endif fritzing-app-0.9.3b/src/mainwindow/mainwindow_dock.cpp000066400000000000000000000176541272363601300231420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include "mainwindow.h" #include "../utils/fsizegrip.h" #include "../utils/misc.h" #include "../partsbinpalette/binmanager/binmanager.h" #include "../infoview/htmlinfoview.h" #include "../dock/layerpalette.h" #include "../mainwindow/fdockwidget.h" #include "../utils/fileprogressdialog.h" #include "../debugdialog.h" ///////////////////////////////////// static const int PartsBinMinHeight = 100; static const int UndoHistoryDefaultHeight = 70; static const int UndoHistoryMinHeight = UndoHistoryDefaultHeight; const int MainWindow::DockMinWidth = 130; const int MainWindow::DockMinHeight = 30; /////////////////////////////////////// void MainWindow::dockChangeActivation(bool activate, QWidget * originator) { Q_UNUSED(activate); Q_UNUSED(originator); if (!m_closing) { m_sizeGrip->rearrange(); } } void MainWindow::createDockWindows() { QWidget * widget = new QWidget(); widget->setMinimumHeight(0); widget->setMaximumHeight(0); makeDock(BinManager::Title, m_binManager, PartsBinMinHeight, PartsBinHeightDefault/*, Qt::LeftDockWidgetArea*/); makeDock(tr("Inspector"), m_infoView, InfoViewMinHeight, InfoViewHeightDefault); makeDock(tr("Undo History"), m_undoView, UndoHistoryMinHeight, UndoHistoryDefaultHeight)->hide(); m_undoView->setMinimumSize(DockMinWidth, UndoHistoryMinHeight); makeDock(tr("Layers"), m_layerPalette, DockMinWidth, DockMinHeight)->hide(); m_layerPalette->setMinimumSize(DockMinWidth, DockMinHeight); m_layerPalette->setShowAllLayersAction(m_showAllLayersAct); m_layerPalette->setHideAllLayersAction(m_hideAllLayersAct); if (m_programView == NULL) { m_windowMenu->addSeparator(); m_windowMenu->addAction(m_openProgramWindowAct); } #ifndef QT_NO_DEBUG m_windowMenu->addSeparator(); m_windowMenu->addAction(m_toggleDebuggerOutputAct); #endif m_windowMenuSeparator = m_windowMenu->addSeparator(); } FDockWidget * MainWindow::makeDock(const QString & title, QWidget * widget, int dockMinHeight, int dockDefaultHeight, Qt::DockWidgetArea area, DockFactory dockFactory) { FDockWidget * dock = ((dockFactory) ? dockFactory(title, this) : new FDockWidget(title, this)); dock->setObjectName(title); dock->setWidget(widget); widget->setParent(dock); widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); connect(dock, SIGNAL(positionChanged()), this, SLOT(keepMargins())); connect(dock, SIGNAL(topLevelChanged(bool)), this, SLOT(keepMargins())); connect(dock, SIGNAL(visibilityChanged(bool)), this, SLOT(keepMargins())); return dockIt(dock, dockMinHeight, dockDefaultHeight, area); } FDockWidget *MainWindow::dockIt(FDockWidget* dock, int dockMinHeight, int dockDefaultHeight, Qt::DockWidgetArea area) { dock->setAllowedAreas(area); addDockWidget(area, dock); if (m_windowMenu) { m_windowMenu->addAction(dock->toggleViewAction()); } dock->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); dock->setMinimumSize(DockMinWidth, dockMinHeight); dock->resize(DockWidthDefault, dockDefaultHeight); connect(dock, SIGNAL(dockChangeActivationSignal(bool, QWidget *)), this, SLOT(dockChangeActivation(bool, QWidget *))); connect(dock, SIGNAL(destroyed(QObject *)), qApp, SLOT(topLevelWidgetDestroyed(QObject *))); connect(dock, SIGNAL(dockChangeActivationSignal(bool, QWidget *)), qApp, SLOT(changeActivation(bool, QWidget *)), Qt::DirectConnection); m_docks << dock; return dock; } FDockWidget *MainWindow::newTopWidget() { int topMostY = 10000; FDockWidget *topWidget = NULL; foreach(FDockWidget* dock, m_docks) { if(/*!dock->isFloating() && dock->isVisible() &&*/ dockWidgetArea(dock) == Qt::RightDockWidgetArea && dock->pos().y() < topMostY) { topMostY = dock->pos().y(); topWidget = dock; } } return topWidget; } FDockWidget *MainWindow::newBottomWidget() { int bottomMostY = -1; FDockWidget *bottomWidget = NULL; foreach(FDockWidget* dock, m_docks) { if(!dock->isFloating() && dock->isVisible() && dockWidgetArea(dock) == Qt::RightDockWidgetArea && dock->pos().y() > bottomMostY) { bottomMostY = dock->pos().y(); bottomWidget = dock; } } return bottomWidget; } void MainWindow::keepMargins() { if (m_dontKeepMargins) return; /*FDockWidget* newTopWidget = this->newTopWidget(); if(m_topDock != newTopWidget) { removeMargin(m_topDock); m_topDock = newTopWidget; if(m_topDock) m_oldTopDockStyle = m_topDock->styleSheet(); addTopMargin(m_topDock); }*/ FDockWidget* newBottomWidget = this->newBottomWidget(); if(m_bottomDock != newBottomWidget) { removeMargin(m_bottomDock); m_bottomDock = newBottomWidget; if(m_bottomDock) m_oldBottomDockStyle = m_bottomDock->styleSheet(); addBottomMargin(m_bottomDock); m_sizeGrip->raise(); } } void MainWindow::removeMargin(FDockWidget* dock) { if(dock) { dockMarginAux(dock, "", m_oldBottomDockStyle); } } void MainWindow::addTopMargin(FDockWidget* dock) { if(dock) dockMarginAux(dock, "topMostDock", dock->widget()->styleSheet()); } void MainWindow::addBottomMargin(FDockWidget* dock) { if(dock) { if(qobject_cast(dock->widget())) { // already has enought space } else { dockMarginAux(dock, "bottomMostDock", dock->widget()->styleSheet()); } } } void MainWindow::dockMarginAux(FDockWidget* dock, const QString &name, const QString &style) { if(dock) { dock->widget()->setObjectName(name); dock->widget()->setStyleSheet(style); dock->setStyleSheet(dock->styleSheet()); } else { qWarning() << tr("Couldn't get the dock widget"); } } void MainWindow::dontKeepMargins() { m_dontKeepMargins = true; } void MainWindow::initDock() { m_layerPalette = new LayerPalette(this); m_infoView = new HtmlInfoView(); m_infoView->init(false); connect(m_infoView, SIGNAL(clickObsoleteSignal()), this, SLOT(selectAllObsolete())); //DebugDialog::debug("after html view"); m_binManager = new BinManager(m_referenceModel, m_infoView, m_undoStack, this); m_binManager->initStandardBins(); //DebugDialog::debug("after creating bins"); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(89); } } void MainWindow::moreInitDock() { //DebugDialog::debug("create view switcher"); createDockWindows(); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(93); } } void MainWindow::saveDocks() { for (int i = 0; i < children().count(); i++) { FDockWidget * dock = qobject_cast(children()[i]); if (dock == NULL) continue; //DebugDialog::debug(QString("saving dock %1").arg(dock->windowTitle())); dock->saveState(); if (dock->isFloating() && dock->isVisible()) { //DebugDialog::debug(QString("hiding dock %1").arg(dock->windowTitle())); dock->hide(); } } } void MainWindow::restoreDocks() { for (int i = 0; i < children().count(); i++) { FDockWidget * dock = qobject_cast(children()[i]); if (dock == NULL) continue; // DebugDialog::debug(QString("restoring dock %1").arg(dock->windowTitle())); dock->restoreState(); } } fritzing-app-0.9.3b/src/mainwindow/mainwindow_export.cpp000066400000000000000000001636451272363601300235450ustar00rootroot00000000000000/******************************************************************* skw Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "../debugdialog.h" #include "../waitpushundostack.h" #include "../help/aboutbox.h" #include "../autoroute/autorouteprogressdialog.h" #include "../items/virtualwire.h" #include "../items/jumperitem.h" #include "../items/via.h" #include "../fsvgrenderer.h" #include "../items/note.h" #include "../items/partfactory.h" #include "../eagle/fritzing2eagle.h" #include "../sketch/breadboardsketchwidget.h" #include "../sketch/schematicsketchwidget.h" #include "../sketch/pcbsketchwidget.h" #include "../partsbinpalette/binmanager/binmanager.h" #include "../utils/expandinglabel.h" #include "../infoview/htmlinfoview.h" #include "../utils/bendpointaction.h" #include "../sketch/fgraphicsscene.h" #include "../utils/fileprogressdialog.h" #include "../svg/svgfilesplitter.h" #include "../version/version.h" #include "../help/tipsandtricks.h" #include "../dialogs/setcolordialog.h" #include "../utils/folderutils.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../connectors/ercdata.h" #include "../items/moduleidnames.h" #include "../utils/zoomslider.h" #include "../dock/layerpalette.h" #include "../program/programwindow.h" #include "../utils/autoclosemessagebox.h" #include "../svg/gerbergenerator.h" #include "../processeventblocker.h" static QString eagleActionType = ".eagle"; static QString gerberActionType = ".gerber"; static QString jpgActionType = ".jpg"; #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) static QString psActionType = ".ps"; #endif static QString pdfActionType = ".pdf"; static QString pngActionType = ".png"; static QString svgActionType = ".svg"; static QString bomActionType = ".html"; static QString netlistActionType = ".xml"; static QString spiceNetlistActionType = ".cir"; static QHash filePrintFormats; static QHash fileExportFormats; static QHash fileExtFormats; static QRegExp AaCc("[aAcC]"); static QRegExp LabelNumber("([^\\d]+)(.*)"); static const double InchesPerMeter = 39.3700787; //////////////////////////////////////////////////////// bool sortPartList(ItemBase * b1, ItemBase * b2) { bool result = b1->instanceTitle().toLower() < b2->instanceTitle().toLower(); int ix1 = LabelNumber.indexIn(b1->instanceTitle()); if (ix1 < 0) return result; QString label1 = LabelNumber.cap(1); QString number1 = LabelNumber.cap(2); int ix2 = LabelNumber.indexIn(b2->instanceTitle()); if (ix2 < 0) return result; QString label2 = LabelNumber.cap(1); QString number2 = LabelNumber.cap(2); if (label2.compare(label1, Qt::CaseInsensitive) != 0) return result; bool ok; double d1 = number1.toDouble(&ok); if (!ok) return result; double d2 = number2.toDouble(&ok); if (!ok) return result; return d1 < d2; } ///////////////////////////////////////////////////////// void MainWindow::initNames() { OtherKnownExtensions << jpgActionType << pdfActionType << pngActionType << svgActionType << bomActionType << netlistActionType << spiceNetlistActionType; filePrintFormats[pdfActionType] = QPrinter::PdfFormat; fileExportFormats[pngActionType] = QImage::Format_ARGB32; fileExportFormats[jpgActionType] = QImage::Format_RGB32; fileExtFormats[pdfActionType] = tr("PDF (*.pdf)"); fileExtFormats[pngActionType] = tr("PNG Image (*.png)"); fileExtFormats[jpgActionType] = tr("JPEG Image (*.jpg)"); fileExtFormats[svgActionType] = tr("SVG Image (*.svg)"); fileExtFormats[bomActionType] = tr("BoM Text File (*.html)"); #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) OtherKnownExtensions << psActionType; filePrintFormats[psActionType] = QPrinter::PostScriptFormat; fileExtFormats[psActionType] = tr("PostScript (*.ps)"); #endif QSettings settings; AutosaveEnabled = settings.value("autosaveEnabled", QString("%1").arg(AutosaveEnabled)).toBool(); AutosaveTimeoutMinutes = settings.value("autosavePeriod", QString("%1").arg(AutosaveTimeoutMinutes)).toInt(); } void MainWindow::print() { if (m_currentWidget->contentView() == m_programView) { m_programView->print(); } if (m_currentGraphicsView == NULL) return; #ifndef QT_NO_PRINTER QPrinter printer(QPrinter::HighResolution); QPrintDialog *printDialog = new QPrintDialog(&printer, this); if (printDialog->exec() == QDialog::Accepted) { m_statusBar->showMessage(tr("Printing...")); printAux(printer, true, true); m_statusBar->showMessage(tr("Ready"), 2000); } else { return; } #endif } void MainWindow::exportEtchable() { if (sender() == NULL) return; bool wantSvg = sender()->property("svg").toBool(); exportEtchable(!wantSvg, wantSvg); } void MainWindow::exportEtchable(bool wantPDF, bool wantSVG) { int boardCount; ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount); if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to export etchable.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Etchable export can only handle one board at a time--please select the board you want to export.")); return; } RoutingStatus routingStatus; m_pcbGraphicsView->updateRoutingStatus(NULL, routingStatus, true); if (routingStatus.m_connectorsLeftToRoute > 0) { QMessageBox msgBox(this); msgBox.setWindowModality(Qt::WindowModal); msgBox.setText(tr("All traces have not yet been routed.")); msgBox.setInformativeText(tr("Do you want to proceed anyway?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.button(QMessageBox::Yes)->setText(tr("Proceed")); msgBox.button(QMessageBox::No)->setText(tr("Cancel")); msgBox.setDefaultButton(QMessageBox::Yes); int ret = msgBox.exec(); if (ret != QMessageBox::Yes) return; } QString path = defaultSaveFolder(); QString extFmt = (wantPDF) ? fileExtFormats.value(pdfActionType) : fileExtFormats.value(svgActionType); QString fileExt = extFmt; QString suffix = (wantPDF) ? pdfActionType : svgActionType; QString prefix = ""; if (boardCount > 1) { prefix = QString("%1_%2_").arg(board->instanceTitle()).arg(board->id()); } QString exportDir = QFileDialog::getExistingDirectory(this, tr("Choose a folder for exporting"), defaultSaveFolder(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (exportDir.isEmpty()) return; FolderUtils::setOpenSaveFolder(exportDir); FileProgressDialog * fileProgressDialog = exportProgress(); QRectF r = board->sceneBoundingRect(); QSizeF boardImageSize(r.width(), r.height()); QStringList fileNames; fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_copper_bottom%1", suffix)); fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_mask_bottom%1", suffix)); fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_paste_mask_bottom%1", suffix)); if (m_pcbGraphicsView->boardLayers() > 1) { fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_copper_top%1", suffix)); fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_mask_top%1", suffix)); fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_paste_mask_top%1", suffix)); } fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_silk_top%1", suffix)); fileNames.append(exportDir + "/" + constructFileName(prefix + "etch_silk_bottom%1", suffix)); QString maskTop, maskBottom; QList copperLogoItems, holes; for (int ix = 0; ix < fileNames.count(); ix++) { bool doMask = false; bool doSilk = false; bool doPaste = false; QString fileName = fileNames[ix]; LayerList viewLayerIDs; if (fileName.contains("copper_bottom")) { viewLayerIDs << ViewLayer::GroundPlane0 << ViewLayer::Copper0 << ViewLayer::Copper0Trace; } else if (fileName.contains("mask_bottom")) { doMask = true; viewLayerIDs << ViewLayer::Copper0; doPaste = fileName.contains("paste"); } else if (fileName.contains("copper_top")) { viewLayerIDs << ViewLayer::GroundPlane1 << ViewLayer::Copper1 << ViewLayer::Copper1Trace; } else if (fileName.contains("mask_top")) { viewLayerIDs << ViewLayer::Copper1; doMask = true; doPaste = fileName.contains("paste"); } else if (fileName.contains("silk_top")) { viewLayerIDs << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label; doSilk = true; } else if (fileName.contains("silk_bottom")) { viewLayerIDs << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen0Label; doSilk = true; } if (doMask) { m_pcbGraphicsView->hideCopperLogoItems(copperLogoItems); } if (doPaste) { m_pcbGraphicsView->hideHoles(holes); } if (wantSVG) { RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::IllustratorDPI; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString svg = m_pcbGraphicsView->renderToSVG(renderThing, board, viewLayerIDs); massageOutput(svg, doMask, doSilk, doPaste, maskTop, maskBottom, fileName, board, GraphicsUtils::IllustratorDPI, viewLayerIDs); QString merged = mergeBoardSvg(svg, board, GraphicsUtils::IllustratorDPI, false, viewLayerIDs); TextUtils::writeUtf8(fileName.arg(""), merged); merged = mergeBoardSvg(svg, board, GraphicsUtils::IllustratorDPI, true, viewLayerIDs); TextUtils::writeUtf8(fileName.arg("_mirror"), merged); } else { QString svg; QList flips; flips << false << true; foreach (bool flip, flips) { QString mirror = flip ? "_mirror" : ""; QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(filePrintFormats[fileExt]); printer.setOutputFileName(fileName.arg(mirror)); int res = printer.resolution(); if (svg.isEmpty()) { RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = res; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; svg = m_pcbGraphicsView->renderToSVG(renderThing, board, viewLayerIDs); massageOutput(svg, doMask, doSilk, doPaste, maskTop, maskBottom, fileName, board, res, viewLayerIDs); } QString merged = mergeBoardSvg(svg, board, res, flip, viewLayerIDs); // now convert to pdf QSvgRenderer svgRenderer; svgRenderer.load(merged.toLatin1()); double trueWidth = boardImageSize.width() / GraphicsUtils::SVGDPI; double trueHeight = boardImageSize.height() / GraphicsUtils::SVGDPI; QRectF target(0, 0, trueWidth * res, trueHeight * res); QSizeF psize((target.width() + printer.paperRect().width() - printer.width()) / res, (target.height() + printer.paperRect().height() - printer.height()) / res); printer.setPaperSize(psize, QPrinter::Inch); QPainter painter; if (painter.begin(&printer)) { svgRenderer.render(&painter, target); } painter.end(); } } if (doMask) { m_pcbGraphicsView->restoreCopperLogoItems(copperLogoItems); } if (doPaste) { m_pcbGraphicsView->restoreCopperLogoItems(holes); } } m_statusBar->showMessage(tr("Sketch exported"), 2000); delete fileProgressDialog; /* int width = m_pcbGraphicsView->width(); if (m_pcbGraphicsView->verticalScrollBar()->isVisible()) { width -= m_pcbGraphicsView->verticalScrollBar()->width(); } int height = m_pcbGraphicsView->height(); if (m_pcbGraphicsView->horizontalScrollBar()->isVisible()) { height -= m_pcbGraphicsView->horizontalScrollBar()->height(); } double trueWidth = width / m_printerScale; double trueHeight = height / m_printerScale; // set everything to a 1200 dpi resolution QSize imgSize(trueWidth * 1200, trueHeight * 1200); QImage image(imgSize, QImage::Format_RGB32); image.setDotsPerMeterX(1200 * GraphicsUtils::InchesPerMeter); image.setDotsPerMeterY(1200 * GraphicsUtils::InchesPerMeter); QPainter painter; QColor color; color = m_pcbGraphicsView->background(); m_pcbGraphicsView->setBackground(QColor::fromRgb(255,255,255,255)); m_pcbGraphicsView->scene()->clearSelection(); m_pcbGraphicsView->saveLayerVisibility(); m_pcbGraphicsView->setAllLayersVisible(false); m_pcbGraphicsView->setLayerVisible(ViewLayer::Copper0, true); m_pcbGraphicsView->hideConnectors(true); painter.begin(&image); m_pcbGraphicsView->render(&painter); painter.end(); QSvgGenerator svgGenerator; svgGenerator.setFileName("c:/fritzing2/testsvggenerator.svg"); svgGenerator.setSize(QSize(width * 8, height * 8)); QPainter svgPainter(&svgGenerator); m_pcbGraphicsView->render(&svgPainter); svgPainter.end(); m_pcbGraphicsView->hideConnectors(false); m_pcbGraphicsView->setBackground(color); m_pcbGraphicsView->restoreLayerVisibility(); // TODO: restore the selection QRgb black = 0; for (int x = 0; x < imgSize.width(); x++) { for (int y = 0; y < imgSize.height(); y++) { QRgb p = image.pixel(x, y); if (p != 0xffffffff) { image.setPixel(x, y, black); } } } bool result = image.save(fileName); if (!result) { QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save %1").arg(fileName) ); } */ } QString MainWindow::mergeBoardSvg(QString & svg, ItemBase * board, int res, bool flip, LayerList & viewLayerIDs) { QString boardSvg = getBoardSvg(board, res, viewLayerIDs); LayerList outlineLayerIDs = ViewLayer::outlineLayers(); RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = res; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString outlineSvg = m_pcbGraphicsView->renderToSVG(renderThing, board, outlineLayerIDs); outlineSvg = GerberGenerator::cleanOutline(outlineSvg); outlineSvg = TextUtils::slamStrokeAndFill(outlineSvg, "black", "0.5", "none"); if (!boardSvg.isEmpty() && !outlineSvg.isEmpty()) { boardSvg = TextUtils::mergeSvg(boardSvg, outlineSvg, "", false); } else if (boardSvg.isEmpty()) { boardSvg = outlineSvg; } return TextUtils::convertExtendedChars(TextUtils::mergeSvg(boardSvg, svg, "", flip)); } QString MainWindow::getBoardSvg(ItemBase * board, int res, LayerList & viewLayerIDs) { if (board == NULL) return ___emptyString___; board = board->layerKinChief(); QList boardLayers; boardLayers << board; foreach (ItemBase * lk, board->layerKin()) { boardLayers << lk; } bool gotOne = false; foreach (ItemBase * boardLayer, boardLayers) { if (viewLayerIDs.contains(boardLayer->viewLayerID())) { gotOne = true; break; } } if (!gotOne) return ""; m_pcbGraphicsView->setIgnoreSelectionChangeEvents(true); QList items = m_pcbGraphicsView->scene()->selectedItems(); foreach (QGraphicsItem * item, items) { item->setSelected(false); } board->setSelected(true); RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = res; renderThing.selectedItems = renderThing.hideTerminalPoints = true; renderThing.renderBlocker = false; QString svg = m_pcbGraphicsView->renderToSVG(renderThing, board, viewLayerIDs); board->setSelected(false); foreach (QGraphicsItem * item, items) { item->setSelected(true); } m_pcbGraphicsView->setIgnoreSelectionChangeEvents(false); return svg; } void MainWindow::doExport() { QAction * action = qobject_cast(sender()); if (action == NULL) return; QString actionType = action->data().toString(); QString path = defaultSaveFolder(); if (actionType.compare(eagleActionType) == 0) { exportToEagle(); return; } if (actionType.compare(gerberActionType) == 0) { exportToGerber(); return; } if (actionType.compare(bomActionType) == 0) { exportBOM(); return; } if (actionType.compare(netlistActionType) == 0) { exportNetlist(); return; } if (actionType.compare(spiceNetlistActionType) == 0) { exportSpiceNetlist(); return; } if (actionType.compare(svgActionType) == 0) { exportSvg(GraphicsUtils::IllustratorDPI, false, false); return; } #ifndef QT_NO_PRINTER QString fileExt; QString extFmt = fileExtFormats.value(actionType); DebugDialog::debug(QString("file export string %1").arg(extFmt)); QString fileName = FolderUtils::getSaveFileName(this, tr("Export..."), path+"/"+constructFileName("", actionType), extFmt, &fileExt ); if (fileName.isEmpty()) { return; //Cancel pressed } else { FileProgressDialog * fileProgressDialog = exportProgress(); DebugDialog::debug(fileExt+" selected to export"); if(!alreadyHasExtension(fileName, actionType)) { fileName += actionType; } if(filePrintFormats.contains(actionType)) { // PDF or PS QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(filePrintFormats[actionType]); printer.setOutputFileName(fileName); m_statusBar->showMessage(tr("Exporting...")); printAux(printer, true, false); m_statusBar->showMessage(tr("Sketch exported"), 2000); } else { // PNG... DebugDialog::debug(QString("format: %1 %2").arg(fileExt).arg(fileExportFormats[actionType])); int quality = (actionType == pngActionType ? 1 : 100); exportAux(fileName,fileExportFormats[actionType], quality, true); } delete fileProgressDialog; } #endif } void MainWindow::exportAux(QString fileName, QImage::Format format, int quality, bool removeBackground) { if (m_currentGraphicsView == NULL) return; double resMultiplier = 3; QRectF itemsBoundingRect; foreach(QGraphicsItem *item, m_currentGraphicsView->scene()->items()) { if (!item->isVisible()) continue; itemsBoundingRect |= item->sceneBoundingRect(); } QRectF source = itemsBoundingRect; // m_currentGraphicsView->scene()->itemsBoundingRect(); QGraphicsItem * watermark = m_currentGraphicsView->addWatermark(":resources/images/watermark_fritzing_outline.svg"); if (watermark) { watermark->setPos(source.right() - watermark->boundingRect().width(), source.bottom()); source.adjust(0, 0, 0, watermark->boundingRect().height()); } int width = source.width(); int height = source.height(); /* int width = m_currentGraphicsView->width(); if (m_currentGraphicsView->verticalScrollBar()->isVisible()) { width -= m_currentGraphicsView->verticalScrollBar()->width(); } int height = m_currentGraphicsView->height(); if (m_currentGraphicsView->horizontalScrollBar()->isVisible()) { height -= m_currentGraphicsView->horizontalScrollBar()->height(); } */ QSize imgSize(width * resMultiplier, height * resMultiplier); QImage image(imgSize,format); image.setDotsPerMeterX(InchesPerMeter * GraphicsUtils::SVGDPI * resMultiplier); image.setDotsPerMeterY(InchesPerMeter * GraphicsUtils::SVGDPI * resMultiplier); QPainter painter; QColor color; if (removeBackground) { color = m_currentGraphicsView->background(); m_currentGraphicsView->setBackground(QColor::fromRgb(255,255,255,255)); } painter.begin(&image); //m_currentGraphicsView->render(&painter); QRectF target(0, 0, imgSize.width(), imgSize.height()); m_currentGraphicsView->scene()->render(&painter, target, source, Qt::KeepAspectRatio); painter.end(); //image.save(FolderUtils::getUserDataStorePath("") + "/export.png"); if (removeBackground) { m_currentGraphicsView->setBackground(color); } if (watermark) { delete watermark; } QImageWriter imageWriter(fileName); if (imageWriter.supportsOption(QImageIOHandler::Description)) { imageWriter.setText("", TextUtils::CreatedWithFritzingString); } imageWriter.setQuality(quality); bool result = imageWriter.write(image); if (!result) { QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save %1").arg(fileName) ); } } void MainWindow::printAux(QPrinter &printer, bool removeBackground, bool paginate) { if (m_currentGraphicsView == NULL) return; int res = printer.resolution(); double scale2 = res / GraphicsUtils::SVGDPI; DebugDialog::debug(QString("p.w:%1 p.h:%2 pager.w:%3 pager.h:%4 paperr.w:%5 paperr.h:%6 source.w:%7 source.h:%8") .arg(printer.width()) .arg(printer.height()) .arg(printer.pageRect().width()) .arg(printer.pageRect().height()) .arg(printer.paperRect().width()) .arg(printer.paperRect().height()) .arg(printer.width() / scale2) .arg(printer.height() / scale2) ); // oSceneStart oSceneEnd: shows only what's visible in the viewport, not the entire view //QPointF oSceneStart = m_currentGraphicsView->mapToScene(QPoint(0,0)); //QPointF oSceneEnd = m_currentGraphicsView->mapToScene(QPoint(m_currentGraphicsView->viewport()->width(), m_currentGraphicsView->viewport()->height())); //QRectF source(oSceneStart, oSceneEnd); QRectF itemsBoundingRect; foreach(QGraphicsItem *item, m_currentGraphicsView->scene()->items()) { if (!item->isVisible()) continue; itemsBoundingRect |= item->sceneBoundingRect(); } QRectF source = itemsBoundingRect; // m_currentGraphicsView->scene()->itemsBoundingRect(); DebugDialog::debug("items bounding rect", source); DebugDialog::debug("scene items bounding rect", m_currentGraphicsView->scene()->itemsBoundingRect()); QGraphicsItem * watermark = m_currentGraphicsView->addWatermark(":resources/images/watermark_fritzing_outline.svg"); if (watermark) { watermark->setPos(source.right() - watermark->boundingRect().width(), source.bottom()); source.adjust(0, 0, 0, watermark->boundingRect().height()); } QRectF target(0, 0, source.width() * scale2, source.height() * scale2); if (!paginate) { QSizeF psize((target.width() + printer.paperRect().width() - printer.width()) / res, (target.height() + printer.paperRect().height() - printer.height()) / res); printer.setPaperSize(psize, QPrinter::Inch); } QPainter painter; if (!painter.begin(&printer)) { if (watermark) { delete watermark; } QMessageBox::warning(this, tr("Fritzing"), tr("Cannot print to %1").arg(printer.docName())); return; } QColor color; if(removeBackground) { color = m_currentGraphicsView->background(); m_currentGraphicsView->setBackground(QColor::fromRgb(255,255,255,255)); } QList selItems = m_currentGraphicsView->scene()->selectedItems(); foreach(QGraphicsItem *item, selItems) { item->setSelected(false); } if (paginate) { int xPages = qCeil(target.width() / printer.width()); int yPages = qCeil(target.height() / printer.height()); int lastPage = xPages * yPages; int xSourcePage = qFloor(printer.width() / scale2); int ySourcePage = qFloor(printer.height() / scale2); int page = 0; for (int iy = 0; iy < yPages; iy++) { for (int ix = 0; ix < xPages; ix++) { // render to printer: QRectF pSource((ix * xSourcePage) + source.left(), (iy * ySourcePage) + source.top(), qMin(xSourcePage, (int) source.width() - (ix * xSourcePage)), qMin(ySourcePage, (int) source.height() - (iy * ySourcePage))); QRectF pTarget(0, 0, pSource.width() * scale2, pSource.height() * scale2); m_currentGraphicsView->scene()->render(&painter, pTarget, pSource, Qt::KeepAspectRatio); if (++page < lastPage) { printer.newPage(); } } } } else { m_currentGraphicsView->scene()->render(&painter, target, source, Qt::KeepAspectRatio); } foreach(QGraphicsItem *item, selItems) { item->setSelected(true); } if(removeBackground) { m_currentGraphicsView->setBackground(color); } if (watermark) { delete watermark; } DebugDialog::debug(QString("source w:%1 h:%2 target w:%5 h:%6 pres:%3 screenres:%4") .arg(source.width()) .arg(source.height()).arg(res).arg(this->physicalDpiX()) .arg(target.width()).arg(target.height()) ); //#ifndef QT_NO_CONCURRENT //QProgressDialog dialog; //dialog.setLabelText(message); // // Create a QFutureWatcher and conncect signals and slots. //QFutureWatcher futureWatcher; //QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset())); //QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel())); //QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog, SLOT(setRange(int, int))); //QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog, SLOT(setValue(int))); // // Start the computation. //futureWatcher.setFuture(QtConcurrent::run(painter,&QPainter::end)); //dialog.exec(); // //futureWatcher.waitForFinished(); //#endif //#ifdef QT_NO_CONCURRENT painter.end(); //#endif } bool MainWindow::saveAsAux(const QString & fileName) { QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("Fritzing"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } file.close(); setReadOnly(false); //FritzingWindow::saveAsAux(fileName); saveAsAuxAux(fileName); m_autosaveNeeded = false; undoStackCleanChanged(true); m_statusBar->showMessage(tr("Saved '%1'").arg(fileName), 2000); setCurrentFile(fileName, true, true); if(m_restarting && !m_fwFilename.isEmpty()) { QSettings settings; settings.setValue("lastOpenSketch",m_fwFilename); } // mark the stack clean so we update the window dirty flag m_undoStack->setClean(); // slam it here in case we were modified due to m_linkedProgramFiles changes setWindowModified(false); m_saveAct->setEnabled(true); return true; } void MainWindow::saveAsAuxAux(const QString & fileName) { QApplication::setOverrideCursor(Qt::WaitCursor); connectStartSave(true); m_programView->saveAll(); QDir dir(this->m_fzzFolder); QStringList nameFilters("*" + FritzingSketchExtension); QFileInfoList fileList = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); foreach (QFileInfo fileInfo, fileList) { QFile file(fileInfo.absoluteFilePath()); file.remove(); } QString fzName = dir.absoluteFilePath(QFileInfo(fileName).completeBaseName() + FritzingSketchExtension); m_sketchModel->save(fzName, false); saveLastTabList(); saveAsShareable(fileName, false); connectStartSave(false); QApplication::restoreOverrideCursor(); } void MainWindow::saveAsShareable(const QString & path, bool saveModel) { QString filename = path; QHash saveParts; foreach (QGraphicsItem * item, m_pcbGraphicsView->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->modelPart() == NULL) { continue; } if (itemBase->modelPart()->isCore()) continue; if (itemBase->moduleID().contains(PartFactory::OldSchematicPrefix)) continue; saveParts.insert(itemBase->moduleID(), itemBase->modelPart()); } if(alreadyHasExtension(filename, FritzingSketchExtension)) { saveBundledNonAtomicEntity(filename, FritzingSketchExtension, this, saveParts.values(), false, m_fzzFolder, saveModel, true); } else { saveBundledNonAtomicEntity(filename, FritzingBundleExtension, this, saveParts.values(), false, m_fzzFolder, saveModel, true); } } void MainWindow::saveBundledNonAtomicEntity(QString &filename, const QString &extension, Bundler *bundler, const QList &partsToSave, bool askForFilename, const QString & destFolderPath, bool saveModel, bool deleteLeftovers) { bool result; QStringList names; QString fileExt; QString path = defaultSaveFolder() + "/" + QFileInfo(filename).fileName()+"z"; QString bundledFileName = askForFilename ? FolderUtils::getSaveFileName(this, tr("Specify a file name"), path, tr("Fritzing (*%1)").arg(extension), &fileExt) : filename; if (bundledFileName.isEmpty()) return; // Cancel pressed FileProgressDialog progress("Saving...", 0, this); if(!alreadyHasExtension(bundledFileName, extension)) { bundledFileName += extension; } ProcessEventBlocker::processEvents(); QDir destFolder; QString dirToRemove; if (destFolderPath.isEmpty()) { destFolder = QDir::temp(); FolderUtils::createFolderAndCdIntoIt(destFolder, TextUtils::getRandText()); dirToRemove = destFolder.path(); } else { destFolder = QDir(destFolderPath); } QString aux = QFileInfo(bundledFileName).fileName(); QString destSketchPath; if (fritzingBundleExtensions().contains(extension)) { destSketchPath = // remove the last "z" from the extension destFolder.path()+"/"+aux.left(aux.size()-1); } else { destSketchPath = destFolder.path()+"/"+aux; } DebugDialog::debug("saving entity temporarily to "+destSketchPath); QStringList skipSuffixes; if (extension.compare(FritzingBundleExtension) == 0 || \ extension.compare(FritzingSketchExtension) == 0 ) { for (int i = 0; i < m_linkedProgramFiles.count(); i++) { LinkedFile * linkedFile = m_linkedProgramFiles.at(i); QFileInfo fileInfo(linkedFile->linkedFilename); QFile file(linkedFile->linkedFilename); FolderUtils::slamCopy(file, destFolder.absoluteFilePath(fileInfo.fileName())); } skipSuffixes << FritzingBinExtension << FritzingBundleExtension; } if (saveModel) { QString prevFileName = filename; ProcessEventBlocker::processEvents(); bundler->saveAsAux(destSketchPath); filename = prevFileName; } foreach(ModelPart* mp, partsToSave) { names.append(saveBundledAux(mp, destFolder)); } if (deleteLeftovers) { QStringList nameFilters; nameFilters << ("*" + FritzingPartExtension) << "*.svg"; QDir dir(destFolder); QStringList fileList = dir.entryList(nameFilters, QDir::Files | QDir::NoSymLinks); foreach (QString fileName, fileList) { if (!names.contains(fileName)) { QFile::remove(dir.absoluteFilePath(fileName)); } } } ProcessEventBlocker::processEvents(); if (fritzingBundleExtensions().contains(extension)) { result = FolderUtils::createZipAndSaveTo(destFolder, bundledFileName, skipSuffixes); } else { result = FolderUtils::createFZAndSaveTo(destFolder, bundledFileName, skipSuffixes); } if(!result) { QMessageBox::warning( this, tr("Fritzing"), tr("Unable to export %1 as shareable").arg(bundledFileName) ); } if (!dirToRemove.isEmpty()) { FolderUtils::rmdir(dirToRemove); } } void MainWindow::createExportActions() { m_saveAct = new QAction(tr("&Save"), this); m_saveAct->setShortcut(tr("Ctrl+S")); m_saveAct->setStatusTip(tr("Save the current sketch")); connect(m_saveAct, SIGNAL(triggered()), this, SLOT(save())); m_saveAsAct = new QAction(tr("&Save As..."), this); m_saveAsAct->setShortcut(tr("Shift+Ctrl+S")); m_saveAsAct->setStatusTip(tr("Save the current sketch")); connect(m_saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); m_shareOnlineAct = new QAction(tr("Share online..."), this); m_shareOnlineAct->setStatusTip(tr("Post a project to the Fritzing website")); connect(m_shareOnlineAct, SIGNAL(triggered()), this, SLOT(shareOnline())); m_exportJpgAct = new QAction(tr("JPG..."), this); m_exportJpgAct->setData(jpgActionType); m_exportJpgAct->setStatusTip(tr("Export the visible area of the current sketch as a JPG image")); connect(m_exportJpgAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportPngAct = new QAction(tr("PNG..."), this); m_exportPngAct->setData(pngActionType); m_exportPngAct->setStatusTip(tr("Export the visible area of the current sketch as a PNG image")); connect(m_exportPngAct, SIGNAL(triggered()), this, SLOT(doExport())); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) m_exportPsAct = new QAction(tr("PostScript..."), this); m_exportPsAct->setData(psActionType); m_exportPsAct->setStatusTip(tr("Export the visible area of the current sketch as a PostScript image")); connect(m_exportPsAct, SIGNAL(triggered()), this, SLOT(doExport())); #endif m_exportPdfAct = new QAction(tr("PDF..."), this); m_exportPdfAct->setData(pdfActionType); m_exportPdfAct->setStatusTip(tr("Export the visible area of the current sketch as a PDF image")); connect(m_exportPdfAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportSvgAct = new QAction(tr("SVG..."), this); m_exportSvgAct->setData(svgActionType); m_exportSvgAct->setStatusTip(tr("Export the current sketch as an SVG image")); connect(m_exportSvgAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportBomAct = new QAction(tr("List of parts (&Bill of Materials)..."), this); m_exportBomAct->setData(bomActionType); m_exportBomAct->setStatusTip(tr("Save a Bill of Materials (BoM)/Shopping List as text")); connect(m_exportBomAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportNetlistAct = new QAction(tr("XML Netlist..."), this); m_exportNetlistAct->setData(netlistActionType); m_exportNetlistAct->setStatusTip(tr("Save a netlist in XML format")); connect(m_exportNetlistAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportSpiceNetlistAct = new QAction(tr("SPICE Netlist..."), this); m_exportSpiceNetlistAct->setData(spiceNetlistActionType); m_exportSpiceNetlistAct->setStatusTip(tr("Save a netlist in SPICE format")); connect(m_exportSpiceNetlistAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportEagleAct = new QAction(tr("Eagle..."), this); m_exportEagleAct->setData(eagleActionType); m_exportEagleAct->setStatusTip(tr("Export the current sketch to Eagle CAD")); connect(m_exportEagleAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportGerberAct = new QAction(tr("Extended Gerber (RS-274X)..."), this); m_exportGerberAct->setData(gerberActionType); m_exportGerberAct->setStatusTip(tr("Export the current sketch to Extended Gerber format (RS-274X) for professional PCB production")); connect(m_exportGerberAct, SIGNAL(triggered()), this, SLOT(doExport())); m_exportEtchablePdfAct = new QAction(tr("Etchable (PDF)..."), this); m_exportEtchablePdfAct->setStatusTip(tr("Export the current sketch to PDF for DIY PCB production (photoresist)")); m_exportEtchablePdfAct->setProperty("svg", false); connect(m_exportEtchablePdfAct, SIGNAL(triggered()), this, SLOT(exportEtchable())); m_exportEtchableSvgAct = new QAction(tr("Etchable (SVG)..."), this); m_exportEtchableSvgAct->setStatusTip(tr("Export the current sketch to SVG for DIY PCB production (photoresist)")); m_exportEtchableSvgAct->setProperty("svg", true); connect(m_exportEtchableSvgAct, SIGNAL(triggered()), this, SLOT(exportEtchable())); /*m_pageSetupAct = new QAction(tr("&Page Setup..."), this); m_pageSetupAct->setShortcut(tr("Shift+Ctrl+P")); m_pageSetupAct->setStatusTip(tr("Setup the current sketch page")); connect(m_pageSetupAct, SIGNAL(triggered()), this, SLOT(pageSetup()));*/ m_printAct = new QAction(tr("&Print..."), this); m_printAct->setShortcut(tr("Ctrl+P")); m_printAct->setStatusTip(tr("Print the current view")); connect(m_printAct, SIGNAL(triggered()), this, SLOT(print())); } void MainWindow::exportToEagle() { QString text = tr("This will soon provide an export of your Fritzing sketch to the EAGLE layout " "software. If you'd like to have more exports to your favourite EDA tool, please let " "us know, or contribute."); /* QString text = tr("The Eagle export module is very experimental. If anything breaks or behaves " "strangely, please let us know."); */ QMessageBox::information(this, tr("Fritzing"), text); Fritzing2Eagle eagle = Fritzing2Eagle(m_pcbGraphicsView); /* QList partList; // bail out if something is wrong // TODO: show an error in QMessageBox if(m_currentWidget == NULL) { return; } m_pcbGraphicsView->collectParts(partList); QString exportInfoString = tr("parts include:\n"); QString exportString = tr("GRID INCH 0.005\n"); for(int i=0; i < partList.size(); i++){ QString label = partList.at(i)->instanceTitle(); QString desc = partList.at(i)->title(); QHash properties = partList.at(i)->modelPartShared()->properties(); QString package = properties["package"]; if (package == NULL) { package = tr("*** package not specified ***"); } exportInfoString += label + tr(" which is a ") + desc + tr(" in a ") + package + tr(" package.\n"); } QMessageBox::information(this, tr("Fritzing"), exportInfoString); */ /* QFile fp( fileName ); fp.open(QIODevice::WriteOnly); fp.write(bom.toUtf8(),bom.length()); fp.close(); */ /* GRID INCH 0.005 USE '/Applications/eclipse/eagle/lbr/fritzing.lbr'; ADD RESISTOR@fritzing 'R_1' R0.000 (2.3055117 2.1307087); ADD LED@fritzing 'L_2' R0.000 (5.423622 2.425197); GRID LAST; */ } void MainWindow::exportSvg(double res, bool selectedItems, bool flatten) { QString path = defaultSaveFolder(); QString fileExt; QString fileName = FolderUtils::getSaveFileName(this, tr("Export SVG..."), path+"/"+constructFileName("", svgActionType), fileExtFormats[svgActionType], &fileExt ); if (fileName.isEmpty()) return; exportSvg(res, selectedItems, flatten, fileName); } void MainWindow::exportSvg(double res, bool selectedItems, bool flatten, const QString & fileName) { FileProgressDialog * fileProgressDialog = exportProgress(); LayerList viewLayerIDs; foreach (ViewLayer * viewLayer, m_currentGraphicsView->viewLayers()) { if (viewLayer == NULL) continue; if (!viewLayer->visible()) continue; viewLayerIDs << viewLayer->viewLayerID(); } RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = false; renderThing.dpi = res; renderThing.selectedItems = selectedItems; renderThing.hideTerminalPoints = true; renderThing.renderBlocker = false; QString svg = m_currentGraphicsView->renderToSVG(renderThing, NULL, viewLayerIDs); if (svg.isEmpty()) { // tell the user something reasonable return; } if (selectedItems == false && flatten == false) { exportSvgWatermark(svg, res); } TextUtils::writeUtf8(fileName, TextUtils::convertExtendedChars(svg)); delete fileProgressDialog; } void MainWindow::exportSvgWatermark(QString & svg, double res) { QFile file(":/resources/images/watermark_fritzing_outline.svg"); if (!file.open(QFile::ReadOnly)) return; QString watermarkSvg = file.readAll(); file.close(); if (!watermarkSvg.contains(""; svg = TextUtils::mergeSvg(newSvg, svg, "", false); } void MainWindow::exportBOM() { // bail out if something is wrong // TODO: show an error in QMessageBox if (m_currentWidget == NULL) { return; } QString bomTemplate; QFile file(":/resources/templates/bom.html"); if (file.open(QFile::ReadOnly)) { bomTemplate = file.readAll(); file.close(); } else { return; } QString bomRowTemplate; QFile file2(":/resources/templates/bom_row.html"); if (file2.open(QFile::ReadOnly)) { bomRowTemplate = file2.readAll(); file2.close(); } else { return; } QList partList; QList descrList; QMultiHash descrs; m_currentGraphicsView->collectParts(partList); qSort(partList.begin(), partList.end(), sortPartList); foreach (ItemBase * itemBase, partList) { if (itemBase->itemType() != ModelPart::Part) continue; QString desc = itemBase->title() + "%%%%%" + getBomProps(itemBase); // keeps different parts separate if there are no properties descrs.insert(desc, itemBase); if (!descrList.contains(desc)) { descrList.append(desc); } } QString assemblyString; foreach (ItemBase * itemBase, partList) { if (itemBase->itemType() != ModelPart::Part) continue; assemblyString += bomRowTemplate.arg(itemBase->instanceTitle()).arg(itemBase->title()).arg(getBomProps(itemBase)); } QString shoppingListString; foreach (QString descr, descrList) { QList itemBases = descrs.values(descr); QStringList split = descr.split("%%%%%"); shoppingListString += bomRowTemplate.arg(itemBases.count()).arg(split.at(0)).arg(split.at(1)); } QString bom = bomTemplate .arg("Fritzing Bill of Materials") .arg(QFileInfo(m_fwFilename).fileName()) .arg(m_fwFilename) .arg(QDateTime::currentDateTime().toString("dddd, MMMM d yyyy, hh:mm:ss")) .arg(assemblyString) .arg(shoppingListString) .arg(QString("%1.%2.%3").arg(Version::majorVersion()).arg(Version::minorVersion()).arg(Version::minorSubVersion())); QString path = defaultSaveFolder(); QString fileExt; QString extFmt = fileExtFormats.value(bomActionType); QString fname = path+"/"+constructFileName("bom", bomActionType); DebugDialog::debug(QString("fname %1\n%2").arg(fname).arg(extFmt)); QString fileName = FolderUtils::getSaveFileName(this, tr("Export Bill of Materials (BoM)..."), fname, extFmt, &fileExt ); if (fileName.isEmpty()) { return; //Cancel pressed } FileProgressDialog * fileProgressDialog = exportProgress(); DebugDialog::debug(fileExt+" selected to export"); if(!alreadyHasExtension(fileName, bomActionType)) { fileName += bomActionType; } if (!TextUtils::writeUtf8(fileName, bom)) { QMessageBox::warning(this, tr("Fritzing"), tr("Unable to save BOM file, but the text is on the clipboard.")); } QFileInfo info(fileName); if (info.exists()) { QDesktopServices::openUrl(QString("file:///%1").arg(fileName)); } QClipboard *clipboard = QApplication::clipboard(); if (clipboard != NULL) { clipboard->setText(bom); } delete fileProgressDialog; } void MainWindow::exportSpiceNetlist() { if (m_schematicGraphicsView == NULL) return; // examples: // http://www.allaboutcircuits.com/vol_5/chpt_7/8.html // http://cutler.eecs.berkeley.edu/classes/icbook/spice/UserGuide/elements_fr.html // http://www.csd.uoc.gr/~hy422/2011s/datasheets/ngspice-user-manual.pdf QString path = defaultSaveFolder(); QString fileExt; QString extFmt = fileExtFormats.value(spiceNetlistActionType); QString fname = path + "/" + constructFileName("spice", spiceNetlistActionType); //DebugDialog::debug(QString("fname %1\n%2").arg(fname).arg(extFmt)); QString fileName = FolderUtils::getSaveFileName(this, tr("Export SPICE Netlist..."), fname, extFmt, &fileExt ); if (fileName.isEmpty()) { return; //Cancel pressed } static QRegExp curlies("\\{([^\\}]*)\\}"); QFileInfo fileInfo(m_fwFilename); QString output = fileInfo.completeBaseName(); output += "\n"; QHash indexer; QList< QList* > netList; this->m_schematicGraphicsView->collectAllNets(indexer, netList, true, false); //DebugDialog::debug("_______________"); QSet itemBases; QList * ground = NULL; foreach (QList * net, netList) { if (net->count() < 2) continue; foreach (ConnectorItem * ci, *net) { //ci->debugInfo("net"); if (ci->isGrounded()) { ground = net; } itemBases.insert(ci->attachedTo()); } //DebugDialog::debug("_______________"); } if (ground) { // make sure ground is index zero netList.removeOne(ground); netList.prepend(ground); } //DebugDialog::debug("_______________"); //DebugDialog::debug("_______________"); DebugDialog::debug("_______________"); foreach (QList * net, netList) { if (net->count() < 2) continue; foreach (ConnectorItem * ci, *net) { ci->debugInfo("net"); } DebugDialog::debug("_______________"); } //DebugDialog::debug("_______________"); //DebugDialog::debug("_______________"); foreach (ItemBase * itemBase, itemBases) { QString spice = itemBase->spice(); if (spice.isEmpty()) continue; while (true) { int ix = curlies.indexIn(spice); if (ix < 0) break; QString token = curlies.cap(1).toLower(); QString replacement; if (token == "instancetitle") { replacement = itemBase->instanceTitle(); if (ix > 0 && replacement.at(0).toLower() == spice.at(ix - 1).toLower()) { // if the type letter is repeated replacement = replacement.mid(1); } replacement.replace(" ", "_"); } else if (token.startsWith("net ")) { QString cname = token.mid(4).trimmed(); foreach (ConnectorItem * ci, itemBase->cachedConnectorItems()) { if (ci->connectorSharedID().toLower() == cname) { int ix = -1; foreach (QList * net, netList) { ix++; if (net->contains(ci)) break; } replacement = QString::number(ix); break; } } } else { QVariant variant = itemBase->modelPart()->localProp(token); if (variant.isNull()) { replacement = itemBase->modelPart()->properties().value(token, ""); } else { replacement = variant.toString(); } } spice.replace(ix, curlies.cap(0).count(), replacement); DebugDialog::debug("spice " + spice); } output += spice; } output += "\n"; // remove redundant models QStringList models; foreach (ItemBase * itemBase, itemBases) { QString spiceModel = itemBase->spiceModel(); if (spiceModel.isEmpty()) continue; if (models.contains(spiceModel, Qt::CaseInsensitive)) continue; models.append(spiceModel); } foreach (QString model, models) { output += model; output += "\n"; } QString incl = ".include"; if (output.contains(incl, Qt::CaseInsensitive)) { QStringList lines = output.split("\n"); QList paths; paths << FolderUtils::getAppPartsSubFolder(""); paths << QDir(FolderUtils::getUserPartsPath()); QString output2; foreach (QString line, lines) { int ix = line.toLower().indexOf(incl); if (ix < 0) { output2 += line + "\n"; continue; } QString temp = line; temp.replace(ix, incl.length(), ""); QString filename = temp.trimmed(); bool gotOne = false; foreach (QDir dir, paths) { foreach (QString folder, ModelPart::possibleFolders()) { QDir sub(dir); sub.cd(folder); sub.cd("spicemodels"); if (QFile::exists(sub.absoluteFilePath(filename))) { output2 += incl.toUpper() + " " + QDir::toNativeSeparators(sub.absoluteFilePath(filename)) + "\n"; gotOne = true; break; } } if (gotOne) break; } // can't find the include file, so just restore the original line if (!gotOne) { output2 += line + "\n"; } } output = output2; } output += ".TRAN 1ms 100ms\n"; output += "* .AC DEC 100 100 1MEG\n"; output += ".END\n"; foreach (QList * net, netList) { delete net; } netList.clear(); QClipboard *clipboard = QApplication::clipboard(); if (clipboard != NULL) { clipboard->setText(output); } //DebugDialog::debug(fileExt + " selected to export"); if(!alreadyHasExtension(fileName, spiceNetlistActionType)) { fileName += spiceNetlistActionType; } TextUtils::writeUtf8(fileName, output); } void MainWindow::exportNetlist() { QHash indexer; QList< QList* > netList; this->m_currentGraphicsView->collectAllNets(indexer, netList, true, m_currentGraphicsView->boardLayers() > 1); QDomDocument doc; doc.setContent(QString("\n") + TextUtils::CreatedWithFritzingXmlComment); QDomElement netlist = doc.createElement("netlist"); doc.appendChild(netlist); netlist.setAttribute("sketch", QFileInfo(m_fwFilename).fileName()); netlist.setAttribute("date", QDateTime::currentDateTime().toString()); // TODO: filter out 'ignore' connectors QList< QList* > deleteNets; foreach (QList * net, netList) { QList deleteItems; foreach (ConnectorItem * connectorItem, *net) { ErcData * ercData = connectorItem->connectorSharedErcData(); if (ercData == NULL) continue; if (ercData->ignore() == ErcData::Always) { deleteItems.append(connectorItem); } else if ((ercData->ignore() == ErcData::IfUnconnected) && (net->count() == 1)) { deleteItems.append(connectorItem); } } foreach (ConnectorItem * connectorItem, deleteItems) { net->removeOne(connectorItem); } if (net->count() == 0) { deleteNets.append(net); } } foreach (QList * net, deleteNets) { netList.removeOne(net); } foreach (QList * net, netList) { QDomElement netElement = doc.createElement("net"); netlist.appendChild(netElement); foreach (ConnectorItem * connectorItem, *net) { QDomElement connector = doc.createElement("connector"); netElement.appendChild(connector); connector.setAttribute("id", connectorItem->connectorSharedID()); connector.setAttribute("name", connectorItem->connectorSharedName()); QDomElement part = doc.createElement("part"); connector.appendChild(part); ItemBase * itemBase = connectorItem->attachedTo(); part.setAttribute("id", itemBase->id()); part.setAttribute("label", itemBase->instanceTitle()); part.setAttribute("title", itemBase->title()); ErcData * ercData = connectorItem->connectorSharedErcData(); if (ercData != NULL) { QDomElement erc = doc.createElement("erc"); if (ercData->writeToElement(erc, doc)) { connector.appendChild(erc); } } } } foreach (QList * net, netList) { delete net; } netList.clear(); QString path = defaultSaveFolder(); QString fileExt; QString extFmt = fileExtFormats.value(netlistActionType); QString fname = path + "/" +constructFileName("netlist", netlistActionType); //DebugDialog::debug(QString("fname %1\n%2").arg(fname).arg(extFmt)); QString fileName = FolderUtils::getSaveFileName(this, tr("Export Netlist..."), fname, extFmt, &fileExt ); if (fileName.isEmpty()) { return; //Cancel pressed } FileProgressDialog * fileProgressDialog = exportProgress(); //DebugDialog::debug(fileExt + " selected to export"); if(!alreadyHasExtension(fileName, netlistActionType)) { fileName += netlistActionType; } QFile fp( fileName ); fp.open(QIODevice::WriteOnly); fp.write(doc.toByteArray()); fp.close(); QClipboard *clipboard = QApplication::clipboard(); if (clipboard != NULL) { clipboard->setText(doc.toByteArray()); } delete fileProgressDialog; } FileProgressDialog * MainWindow::exportProgress() { return (new FileProgressDialog("Exporting...", 0, this)); } void MainWindow::exportNormalizedSVG() { exportSvg(GraphicsUtils::StandardFritzingDPI, true, false); } void MainWindow::exportNormalizedFlattenedSVG() { exportSvg(GraphicsUtils::StandardFritzingDPI, true, true); } QString MainWindow::getBomProps(ItemBase * itemBase) { if (itemBase == NULL) return ""; QStringList keys; QHash properties = HtmlInfoView::getPartProperties(itemBase->modelPart(), itemBase, false, keys); QString pString; foreach (QString key, keys) { if (key.compare("family") == 0) continue; QString value = properties.value(key); QWidget widget; QWidget * resultWidget = NULL; QString resultKey, resultValue; bool hide; itemBase->collectExtraInfo(&widget, properties.value("family"), key, value, false, resultKey, resultValue, resultWidget, hide); if (resultValue.isEmpty()) continue; pString += ItemBase::translatePropertyName(resultKey) + " " + resultValue + "; "; } if (pString.length() > 2) pString.chop(2); return pString; } void MainWindow::exportToGerber() { //NOTE: this assumes just one board per sketch int boardCount; ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount); // barf an error if there's no board if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to export to Gerber.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Gerber export can only handle one board at a time--please select the board you want to export.")); return; } QString exportDir = QFileDialog::getExistingDirectory(this, tr("Choose a folder for exporting"), defaultSaveFolder(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (exportDir.isEmpty()) return; FileProgressDialog * fileProgressDialog = exportProgress(); FolderUtils::setOpenSaveFolder(exportDir); m_pcbGraphicsView->saveLayerVisibility(); m_pcbGraphicsView->setAllLayersVisible(true); QFileInfo info(m_fwFilename); QString prefix = info.completeBaseName(); if (boardCount > 1) { prefix += QString("_%1_%2").arg(board->instanceTitle()).arg(board->id()); } GerberGenerator::exportToGerber(prefix, exportDir, board, m_pcbGraphicsView, true); m_pcbGraphicsView->restoreLayerVisibility(); m_statusBar->showMessage(tr("Sketch exported to Gerber"), 2000); delete fileProgressDialog; } void MainWindow::connectStartSave(bool doConnect) { if (doConnect) { connect(m_sketchModel->root(), SIGNAL(startSaveInstances(const QString &, ModelPart *, QXmlStreamWriter &)), this, SLOT(startSaveInstancesSlot(const QString &, ModelPart *, QXmlStreamWriter &)), Qt::DirectConnection); } else { disconnect(m_sketchModel->root(), SIGNAL(startSaveInstances(const QString &, ModelPart *, QXmlStreamWriter &)), this, SLOT(startSaveInstancesSlot(const QString &, ModelPart *, QXmlStreamWriter &))); } } QString MainWindow::constructFileName(const QString & differentiator, const QString & suffix) { QString fn = QFileInfo(m_fwFilename).completeBaseName(); fn += "_" + (differentiator.isEmpty() ? m_currentGraphicsView->getShortName() : differentiator); return fn + suffix; } void MainWindow::massageOutput(QString & svg, bool doMask, bool doSilk, bool doPaste, QString & maskTop, QString & maskBottom, const QString & fileName, ItemBase * board, int dpi, const LayerList & viewLayerIDs) { if (doPaste) { // must test doPaste first, since doMask will also be true svg = pcbView()->makePasteMask(svg, board, dpi, viewLayerIDs); } else if (doSilk) { QString use = (fileName.contains("bottom")) ? maskBottom : maskTop; use = TextUtils::expandAndFill(use, "white", GerberGenerator::MaskClearanceMils * 2 * dpi / 1000); svg = TextUtils::mergeSvg(svg, use, "", false); } else if (doMask) { if (fileName.contains("bottom")) maskBottom = svg; else maskTop = svg; svg = TextUtils::expandAndFill(svg, "black", GerberGenerator::MaskClearanceMils * 2 * dpi / 1000); } } void MainWindow::dumpAllParts() { if (m_currentGraphicsView == NULL) return; QList already; foreach (QGraphicsItem * item, m_currentGraphicsView->items()) { ItemBase * ib = dynamic_cast(item); if (ib == NULL) continue; ItemBase * chief = ib->layerKinChief(); if (already.contains(chief)) continue; already << chief; QList itemBases; itemBases << chief; itemBases.append(chief->layerKin()); foreach (ItemBase * itemBase, itemBases) { itemBase->debugInfo(""); foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { if (connectorItem->connectionsCount() > 0) { connectorItem->debugInfo("\t"); foreach (ConnectorItem * to, connectorItem->connectedToItems()) { to->debugInfo("\t\t"); } } } } } } fritzing-app-0.9.3b/src/mainwindow/mainwindow_menu.cpp000066400000000000000000004726311272363601300231660ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6999 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 14:14:07 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "../debugdialog.h" #include "../waitpushundostack.h" #include "../partseditor/pemainwindow.h" #include "../help/aboutbox.h" #include "../autoroute/mazerouter/mazerouter.h" #include "../autoroute/autorouteprogressdialog.h" #include "../autoroute/drc.h" #include "../items/virtualwire.h" #include "../items/resizableboard.h" #include "../items/jumperitem.h" #include "../items/via.h" #include "../fsvgrenderer.h" #include "../items/note.h" #include "../eagle/fritzing2eagle.h" #include "../sketch/breadboardsketchwidget.h" #include "../sketch/schematicsketchwidget.h" #include "../sketch/pcbsketchwidget.h" #include "../partsbinpalette/binmanager/binmanager.h" #include "../utils/expandinglabel.h" #include "../infoview/htmlinfoview.h" #include "../utils/bendpointaction.h" #include "../sketch/fgraphicsscene.h" #include "../utils/fmessagebox.h" #include "../utils/fileprogressdialog.h" #include "../svg/svgfilesplitter.h" #include "../version/version.h" #include "../svg/groundplanegenerator.h" #include "../help/tipsandtricks.h" #include "../dialogs/setcolordialog.h" #include "../utils/folderutils.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../connectors/ercdata.h" #include "../items/moduleidnames.h" #include "../utils/zoomslider.h" #include "../dock/layerpalette.h" #include "../program/programwindow.h" #include "../utils/autoclosemessagebox.h" #include "../processeventblocker.h" #include "../sketchtoolbutton.h" #include "../help/firsttimehelpdialog.h" //////////////////////////////////////////////////////// // help struct to create the example menu from a xml file struct SketchDescriptor { SketchDescriptor(const QString &_id, const QString &_name, const QString &_src, QAction * _action) { id = _id; name = _name; src = _src; action = _action; } QString id; QString name; QString src; QAction * action; }; bool sortSketchDescriptors(SketchDescriptor * s1, SketchDescriptor * s2){ return s1->name.toLower() < s2->name.toLower(); } QDomElement getBestLanguageChild(const QString & localeName, const QDomElement & parent) { QDomElement language = parent.firstChildElement("language"); QDomElement backupLang; while (!language.isNull()) { if (language.attribute("country") == "en") backupLang = language; if (localeName.endsWith(language.attribute("country"))) { return language; } language = language.nextSiblingElement("language"); } return backupLang; } //////////////////////////////////////////////////////// GridSizeThing::GridSizeThing(const QString & vName, const QString & sName, double defaultSize, const QString & gsText) { defaultGridSize = defaultSize; viewName = vName; shortName = sName; gridSizeText = gsText; } GridSizeDialog::GridSizeDialog(GridSizeThing * gridSizeThing) : QDialog() { m_gridSizeThing = gridSizeThing; } GridSizeThing * GridSizeDialog::gridSizeThing() { return m_gridSizeThing; } ///////////////////////////////////////////////////////// void MainWindow::closeIfEmptySketch(MainWindow* mw) { int cascFactorX; int cascFactorY; // close empty sketch window if user opens from a file if (FolderUtils::isEmptyFileName(mw->m_fwFilename, untitledFileName()) && mw->undoStackIsEmpty()) { QTimer::singleShot(0, mw, SLOT(close()) ); cascFactorX = 0; cascFactorY = 0; } else { cascFactorX = CascadeFactorX; cascFactorY = CascadeFactorY; } mw->move(x()+cascFactorX,y()+cascFactorY); mw->show(); } void MainWindow::mainLoad() { QString path; // if it's the first time load is called use Documents folder if(m_firstOpen){ path = defaultSaveFolder(); m_firstOpen = false; } else { path = ""; } QString fileName = FolderUtils::getOpenFileName( this, tr("Select a Fritzing File to Open"), path, tr("Fritzing Files (*%1 *%2 *%3 *%4 *%5);;Fritzing (*%1);;Fritzing Shareable (*%2);;Fritzing Part (*%3);;Fritzing Bin (*%4);;Fritzing Shareable Bin (*%5)") .arg(FritzingSketchExtension) .arg(FritzingBundleExtension) .arg(FritzingBundledPartExtension) .arg(FritzingBinExtension) .arg(FritzingBundledBinExtension) ); if (fileName.isEmpty()) return; if (fileName.endsWith(FritzingBundledPartExtension)) { m_binManager->importPartToMineBin(fileName); return; } if (fileName.endsWith(FritzingBinExtension) || fileName.endsWith(FritzingBundledBinExtension)) { m_binManager->openBinIn(fileName, false); return; } mainLoadAux(fileName); } void MainWindow::mainLoadAux(const QString & fileName) { if (fileName.isNull()) return; if (fileName.endsWith(".txt")) { QFileInfo info(fileName); QFile file(fileName); if (file.open(QFile::ReadOnly)) { QTextStream stream(&file); while (!stream.atEnd()) { QString line = stream.readLine().trimmed(); foreach (QString ext, fritzingExtensions()) { if (line.endsWith(ext)) { QFileInfo lineInfo(line); if (lineInfo.exists()) { mainLoadAux(line); } else { QFileInfo lineInfo(info.absoluteDir().absoluteFilePath(line)); if (lineInfo.exists()) { mainLoadAux(lineInfo.absoluteFilePath()); } } break; } } } } return; } if (!fileName.endsWith(FritzingSketchExtension) && !fileName.endsWith(FritzingBundleExtension)) { loadWhich(fileName, false, false, true, ""); return; } if (alreadyOpen(fileName)) return; QFile file(fileName); if (!file.exists()) { FMessageBox::warning(this, tr("Fritzing"), tr("Cannot find file %1.") .arg(fileName)); return; } if (!file.open(QFile::ReadOnly | QFile::Text)) { FMessageBox::warning(this, tr("Fritzing"), tr("Cannot read file 1 %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } file.close(); MainWindow* mw = newMainWindow(m_referenceModel, fileName, true, true, -1); mw->loadWhich(fileName, true, true, true, ""); mw->clearFileProgressDialog(); closeIfEmptySketch(mw); } void MainWindow::revert() { QMessageBox::StandardButton answer = QMessageBox::question( this, tr("Revert?"), tr("This operation can not be undone--you will lose all of your changes." "\n\nGo ahead and revert?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ); // TODO: make button texts translatable if (answer != QMessageBox::Yes) { return; } revertAux(); } MainWindow * MainWindow::revertAux() { MainWindow* mw = newMainWindow( m_referenceModel, fileName(), true, true, this->currentTabIndex()); mw->setGeometry(this->geometry()); QFileInfo info(fileName()); if (info.exists() || !FolderUtils::isEmptyFileName(this->m_fwFilename, untitledFileName())) { mw->loadWhich(fileName(), true, true, true, ""); } else { mw->addDefaultParts(); mw->show(); mw->hideTempPartsBin(); } mw->clearFileProgressDialog(); // TODO: restore zoom, scroll, etc. for each view mw->setCurrentTabIndex(currentTabIndex()); this->setCloseSilently(true); this->close(); return mw; } bool MainWindow::loadWhich(const QString & fileName, bool setAsLastOpened, bool addToRecent, bool checkObsolete, const QString & displayName) { if (!QFileInfo(fileName).exists()) { FMessageBox::warning(NULL, tr("Fritzing"), tr("File '%1' not found").arg(fileName)); return false; } bool result = false; if (fileName.endsWith(FritzingSketchExtension)) { QFileInfo info(fileName); QString bundledFileName; mainLoad(fileName, displayName, checkObsolete); result = true; QFile file(fileName); QDir dest(m_fzzFolder); FolderUtils::slamCopy(file, dest.absoluteFilePath(info.fileName())); // copy the .fz file directly setCurrentFile(fileName, false, false); } else if(fileName.endsWith(FritzingBundleExtension)) { loadBundledSketch(fileName, addToRecent, setAsLastOpened, checkObsolete); result = true; } else if ( fileName.endsWith(FritzingBinExtension) || fileName.endsWith(FritzingBundledBinExtension) ) { m_binManager->load(fileName); result = true; } else if (fileName.endsWith(FritzingPartExtension)) { notYetImplemented(tr("directly loading parts")); } else if (fileName.endsWith(FritzingBundledPartExtension)) { loadBundledPart(fileName, true); result = true; } if (result) { this->show(); } return result; } void MainWindow::mainLoad(const QString & fileName, const QString & displayName, bool checkObsolete) { if (m_fileProgressDialog) { m_fileProgressDialog->setMaximum(200); m_fileProgressDialog->setValue(102); } this->show(); ProcessEventBlocker::processEvents(); QString displayName2 = displayName; if (displayName.isEmpty()) { QFileInfo fileInfo(fileName); displayName2 = fileInfo.fileName(); } if (m_fileProgressDialog) { m_fileProgressDialog->setMessage(tr("loading %1 (model)").arg(displayName2)); m_fileProgressDialog->setValue(110); } ProcessEventBlocker::processEvents(); QList modelParts; connect(m_sketchModel, SIGNAL(loadedViews(ModelBase *, QDomElement &)), this, SLOT(loadedViewsSlot(ModelBase *, QDomElement &)), Qt::DirectConnection); connect(m_sketchModel, SIGNAL(loadedRoot(const QString &, ModelBase *, QDomElement &)), this, SLOT(loadedRootSlot(const QString &, ModelBase *, QDomElement &)), Qt::DirectConnection); connect(m_sketchModel, SIGNAL(obsoleteSMDOrientationSignal()), this, SLOT(obsoleteSMDOrientationSlot()), Qt::DirectConnection); connect(m_sketchModel, SIGNAL(oldSchematicsSignal(const QString &, bool &)), this, SLOT(oldSchematicsSlot(const QString &, bool &)), Qt::DirectConnection); m_obsoleteSMDOrientation = false; m_sketchModel->loadFromFile(fileName, m_referenceModel, modelParts, true); //DebugDialog::debug("core loaded"); disconnect(m_sketchModel, SIGNAL(loadedViews(ModelBase *, QDomElement &)), this, SLOT(loadedViewsSlot(ModelBase *, QDomElement &))); disconnect(m_sketchModel, SIGNAL(loadedRoot(const QString &, ModelBase *, QDomElement &)), this, SLOT(loadedRootSlot(const QString &, ModelBase *, QDomElement &))); disconnect(m_sketchModel, SIGNAL(obsoleteSMDOrientationSignal()), this, SLOT(obsoleteSMDOrientationSlot())); ProcessEventBlocker::processEvents(); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(155); m_fileProgressDialog->setMessage(tr("loading %1 (breadboard)").arg(displayName2)); } QList newIDs; m_breadboardGraphicsView->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, false, NULL, false, newIDs); ProcessEventBlocker::processEvents(); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(170); m_fileProgressDialog->setMessage(tr("loading %1 (pcb)").arg(displayName2)); } newIDs.clear(); m_pcbGraphicsView->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, false, NULL, false, newIDs); ProcessEventBlocker::processEvents(); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(185); m_fileProgressDialog->setMessage(tr("loading %1 (schematic)").arg(displayName2)); } newIDs.clear(); m_schematicGraphicsView->setConvertSchematic(m_convertedSchematic); m_schematicGraphicsView->setOldSchematic(this->m_useOldSchematic); m_schematicGraphicsView->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, false, NULL, false, newIDs); m_schematicGraphicsView->setConvertSchematic(false); if (m_sketchModel->checkForReversedWires()) { m_pcbGraphicsView->checkForReversedWires(); m_schematicGraphicsView->checkForReversedWires(); m_breadboardGraphicsView->checkForReversedWires(); } ProcessEventBlocker::processEvents(); if (m_fileProgressDialog) { m_fileProgressDialog->setValue(198); } if (m_obsoleteSMDOrientation) { QSet toConvert; foreach (QGraphicsItem * item, m_pcbGraphicsView->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemBase = itemBase->layerKinChief(); if (itemBase->modelPart()->flippedSMD() && itemBase->viewLayerPlacement() == ViewLayer::NewBottom) { toConvert.insert(itemBase); } } QList already; foreach (ItemBase * itemBase, toConvert) { PaletteItem * paletteItem = qobject_cast(itemBase); if (paletteItem == NULL) continue; // shouldn't happen paletteItem->rotateItem(180, true); } } if (m_programView) { QFileInfo fileInfo(m_fwFilename); m_programView->linkFiles(m_linkedProgramFiles, fileInfo.absoluteDir().absolutePath()); } if (!m_useOldSchematic && checkObsolete) { if (m_pcbGraphicsView) { QList items = m_pcbGraphicsView->selectAllObsolete(); if (items.count() > 0) { checkSwapObsolete(items, true); } } } initZoom(); } void MainWindow::copy() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->copy(); } void MainWindow::cut() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->cut(); } void MainWindow::pasteInPlace() { pasteAux(true); } void MainWindow::paste() { pasteAux(false); } void MainWindow::pasteAux(bool pasteInPlace) { if (m_currentGraphicsView == NULL) return; QClipboard *clipboard = QApplication::clipboard(); if (clipboard == NULL) { // shouldn't happen return; } const QMimeData* mimeData = clipboard->mimeData(QClipboard::Clipboard); if (mimeData == NULL) return; if (!mimeData->hasFormat("application/x-dnditemsdata")) return; QByteArray itemData = mimeData->data("application/x-dnditemsdata"); QList modelParts; QHash boundingRects; if (m_sketchModel->paste(m_referenceModel, itemData, modelParts, boundingRects, false)) { QUndoCommand * parentCommand = new QUndoCommand("Paste"); QList sketchWidgets; sketchWidgets << m_breadboardGraphicsView << m_schematicGraphicsView << m_pcbGraphicsView; sketchWidgets.removeOne(m_currentGraphicsView); sketchWidgets.prepend(m_currentGraphicsView); QList newIDs; foreach (SketchWidget * sketchWidget, sketchWidgets) { newIDs.clear(); QRectF r; QRectF boundingRect = boundingRects.value(sketchWidget->viewName(), r); sketchWidget->loadFromModelParts(modelParts, BaseCommand::SingleView, parentCommand, true, pasteInPlace ? &r : &boundingRect, false, newIDs); } foreach (long id, newIDs) { new IncLabelTextCommand(m_breadboardGraphicsView, id, parentCommand); } m_breadboardGraphicsView->setPasting(true); m_pcbGraphicsView->setPasting(true); m_schematicGraphicsView->setPasting(true); m_undoStack->push(parentCommand); m_breadboardGraphicsView->setPasting(false); m_pcbGraphicsView->setPasting(false); m_schematicGraphicsView->setPasting(false); } m_currentGraphicsView->updateInfoView(); } void MainWindow::duplicate() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->copy(); paste(); //m_currentGraphicsView->duplicate(); } void MainWindow::doDelete() { //DebugDialog::debug(QString("invoking do delete") ); if (m_currentGraphicsView != NULL) { m_currentGraphicsView->deleteSelected(retrieveWire(), false); } } void MainWindow::doDeleteMinus() { if (m_currentGraphicsView != NULL) { m_currentGraphicsView->deleteSelected(retrieveWire(), true); } } void MainWindow::selectAll() { if (m_currentGraphicsView != NULL) { m_currentGraphicsView->selectDeselectAllCommand(true); } } void MainWindow::deselect() { if (m_currentGraphicsView != NULL) { m_currentGraphicsView->selectDeselectAllCommand(false); } } void MainWindow::about() { AboutBox::showAbout(); } void MainWindow::tipsAndTricks() { TipsAndTricks::showTipsAndTricks(); } void MainWindow::firstTimeHelp() { if (m_currentGraphicsView == NULL) return; FirstTimeHelpDialog::setViewID(m_currentGraphicsView->viewID()); FirstTimeHelpDialog::showFirstTimeHelp(); } void MainWindow::createActions() { createRaiseWindowActions(); createFileMenuActions(); createEditMenuActions(); createPartMenuActions(); createViewMenuActions(true); createWindowMenuActions(); createHelpMenuActions(); createTraceMenuActions(); } void MainWindow::createRaiseWindowActions() { m_raiseWindowAct = new QAction(m_fwFilename, this); m_raiseWindowAct->setCheckable(true); connect( m_raiseWindowAct, SIGNAL(triggered()), this, SLOT(raiseAndActivate())); updateRaiseWindowAction(); } void MainWindow::createFileMenuActions() { m_newAct = new QAction(tr("New"), this); m_newAct->setShortcut(tr("Ctrl+N")); m_newAct->setStatusTip(tr("Create a new sketch")); connect(m_newAct, SIGNAL(triggered()), this, SLOT(createNewSketch())); m_openAct = new QAction(tr("&Open..."), this); m_openAct->setShortcut(tr("Ctrl+O")); m_openAct->setStatusTip(tr("Open a Fritzing sketch (.fzz, .fz), or load a Fritzing part (.fzpz), or a Fritzing parts bin (.fzb, .fzbz)")); connect(m_openAct, SIGNAL(triggered()), this, SLOT(mainLoad())); m_revertAct = new QAction(tr("Revert"), this); m_revertAct->setStatusTip(tr("Reload the sketch")); connect(m_revertAct, SIGNAL(triggered()), this, SLOT(revert())); createOpenRecentMenu(); createOpenExampleMenu(); createCloseAction(); createExportActions(); createOrderFabAct(); QString name; QString path; QStringList args; if (externalProcess(name, path, args)) { m_launchExternalProcessAct = new QAction(name, this); m_launchExternalProcessAct->setStatusTip(tr("Shell launch %1").arg(path)); connect(m_launchExternalProcessAct, SIGNAL(triggered()), this, SLOT(launchExternalProcess())); } #ifndef QT_NO_DEBUG m_exceptionAct = new QAction(tr("throw test exception"), this); m_exceptionAct->setStatusTip(tr("throw a fake exception to see what happens")); connect(m_exceptionAct, SIGNAL(triggered()), this, SLOT(throwFakeException())); #endif m_quitAct = new QAction(tr("&Quit"), this); m_quitAct->setShortcut(tr("Ctrl+Q")); m_quitAct->setStatusTip(tr("Quit the application")); connect(m_quitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows2())); m_quitAct->setMenuRole(QAction::QuitRole); } void MainWindow::createOpenExampleMenu() { m_openExampleMenu = new QMenu(tr("&Open Example"), this); QString folderPath = FolderUtils::getApplicationSubFolderPath("sketches")+"/"; populateMenuFromXMLFile(m_openExampleMenu, m_openExampleActions, folderPath, "index.xml"); } void MainWindow::populateMenuFromXMLFile(QMenu *parentMenu, QStringList &actionsTracker, const QString &folderPath, const QString &indexFileName) { QDomDocument dom; QFile file(folderPath+indexFileName); dom.setContent(&file); QDomElement domElem = dom.documentElement(); QDomElement indexDomElem = domElem.firstChildElement("sketches"); QDomElement taxonomyDomElem = domElem.firstChildElement("categories"); QLocale locale; QString localeName = locale.name().toLower(); // get default translation, a string of the form "language_country" where country is a two-letter code. QHash index = indexAvailableElements(indexDomElem, folderPath, actionsTracker, localeName); QList sketchDescriptors(index.values()); qSort(sketchDescriptors.begin(), sketchDescriptors.end(), sortSketchDescriptors); if (sketchDescriptors.size() > 0) { // set up the "all" category QDomElement all = dom.createElement("category"); taxonomyDomElem.appendChild(all); QDomElement language = dom.createElement("language"); language.setAttribute("name", tr("All")); all.appendChild(language); foreach (SketchDescriptor * sketchDescriptor, sketchDescriptors) { QDomElement sketch = dom.createElement("sketch"); sketch.setAttribute("id", sketchDescriptor->id); all.appendChild(sketch); } } populateMenuWithIndex(index, parentMenu, taxonomyDomElem, localeName); foreach (SketchDescriptor * sketchDescriptor, index.values()) { delete sketchDescriptor; } } QHash MainWindow::indexAvailableElements(QDomElement &domElem, const QString &srcPrefix, QStringList & actionsTracker, const QString & localeName) { QHash retval; QDomElement sketch = domElem.firstChildElement("sketch"); // TODO: eventually need to deal with language/country differences like pt_br vs. pt_pt while(!sketch.isNull()) { const QString id = sketch.attribute("id"); QDomElement bestLang = getBestLanguageChild(localeName, sketch); QString name = bestLang.attribute("name"); QString srcAux = bestLang.attribute("src"); // if it's an absolute path, don't prefix it const QString src = QFileInfo(srcAux).exists()? srcAux: srcPrefix+srcAux; if(QFileInfo(src).exists()) { actionsTracker << name; QAction * action = new QAction(name, this); action->setData(src); connect(action,SIGNAL(triggered()),this,SLOT(openRecentOrExampleFile())); retval[id] = new SketchDescriptor(id,name,src, action); } sketch = sketch.nextSiblingElement("sketch"); } return retval; } void MainWindow::populateMenuWithIndex(const QHash &index, QMenu * parentMenu, QDomElement &domElem, const QString & localeName) { // note: the element here is not the same as the element in indexAvailableElements() QDomElement e = domElem.firstChildElement(); while(!e.isNull()) { if (e.nodeName() == "sketch") { QString id = e.attribute("id"); if (!id.isEmpty()) { if(index[id]) { SketchDescriptor * sketchDescriptor = index[id]; parentMenu->addAction(sketchDescriptor->action); } else { qWarning() << tr("MainWindow::populateMenuWithIndex: couldn't load example with id='%1'").arg(id); } } } else if (e.nodeName() == "category") { QDomElement bestLang = getBestLanguageChild(localeName, e); QString name = bestLang.attribute("name"); QMenu * currMenu = new QMenu(name, parentMenu); parentMenu->addMenu(currMenu); populateMenuWithIndex(index, currMenu, e, localeName); } else if (e.nodeName() == "separator") { parentMenu->addSeparator(); } else if (e.nodeName() == "url") { QDomElement bestLang = getBestLanguageChild(localeName, e); QAction * action = new QAction(bestLang.attribute("name"), this); action->setData(bestLang.attribute("href")); connect(action, SIGNAL(triggered()), this, SLOT(openURL())); parentMenu->addAction(action); } e = e.nextSiblingElement(); } } void MainWindow::populateMenuFromFolderContent(QMenu * parentMenu, const QString &path) { QDir *currDir = new QDir(path); QStringList content = currDir->entryList(QDir::AllEntries | QDir::NoDotAndDotDot); if(content.size() > 0) { for(int i=0; i < content.size(); i++) { QString currFile = content.at(i); QString currFilePath = currDir->absoluteFilePath(currFile); if(QFileInfo(currFilePath).isDir()) { QMenu * currMenu = new QMenu(currFile, parentMenu); parentMenu->addMenu(currMenu); populateMenuFromFolderContent(currMenu, currFilePath); } else { QString actionText = QFileInfo(currFilePath).completeBaseName(); m_openExampleActions << actionText; QAction * currAction = new QAction(actionText, this); currAction->setData(currFilePath); connect(currAction,SIGNAL(triggered()),this,SLOT(openRecentOrExampleFile())); parentMenu->addAction(currAction); } } } else { parentMenu->setEnabled(false); } delete currDir; } void MainWindow::createOpenRecentMenu() { m_openRecentFileMenu = new QMenu(tr("&Open Recent Files"), this); for (int i = 0; i < MaxRecentFiles; ++i) { m_openRecentFileActs[i] = new QAction(this); m_openRecentFileActs[i]->setVisible(false); connect(m_openRecentFileActs[i], SIGNAL(triggered()),this, SLOT(openRecentOrExampleFile())); } for (int i = 0; i < MaxRecentFiles; ++i) { m_openRecentFileMenu->addAction(m_openRecentFileActs[i]); } updateRecentFileActions(); } void MainWindow::updateFileMenu() { m_printAct->setEnabled(m_currentGraphicsView != NULL || m_currentWidget->contentView() == m_programView); updateRecentFileActions(); m_orderFabAct->setEnabled(true); m_revertAct->setEnabled(m_undoStack->canUndo()); } void MainWindow::updateRecentFileActions() { QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); int ix = 0; for (int i = 0; i < files.size(); ++i) { QFileInfo finfo(files[i]); if (!finfo.exists()) continue; QString text = tr("&%1 %2").arg(ix + 1).arg(finfo.fileName()); m_openRecentFileActs[ix]->setText(text); m_openRecentFileActs[ix]->setData(files[i]); m_openRecentFileActs[ix]->setVisible(true); m_openRecentFileActs[ix]->setStatusTip(files[i]); if (++ix >= (int) MaxRecentFiles) { break; } } for (int j = ix; j < MaxRecentFiles; ++j) { m_openRecentFileActs[j]->setVisible(false); } m_openRecentFileMenu->setEnabled(ix > 0); } void MainWindow::createEditMenuActions() { m_undoAct = m_undoGroup->createUndoAction(this, tr("Undo")); m_undoAct->setShortcuts(QKeySequence::Undo); m_undoAct->setText(tr("Undo")); m_redoAct = m_undoGroup->createRedoAction(this, tr("Redo")); m_redoAct->setShortcuts(QKeySequence::Redo); m_redoAct->setText(tr("Redo")); m_cutAct = new QAction(tr("&Cut"), this); m_cutAct->setShortcut(QKeySequence::Cut); m_cutAct->setStatusTip(tr("Cut selection")); connect(m_cutAct, SIGNAL(triggered()), this, SLOT(cut())); m_copyAct = new QAction(tr("&Copy"), this); m_copyAct->setShortcut(QKeySequence::Copy); m_copyAct->setStatusTip(tr("Copy selection")); connect(m_copyAct, SIGNAL(triggered()), this, SLOT(copy())); m_pasteAct = new QAction(tr("&Paste"), this); m_pasteAct->setShortcut(QKeySequence::Paste); m_pasteAct->setStatusTip(tr("Paste clipboard contents")); connect(m_pasteAct, SIGNAL(triggered()), this, SLOT(paste())); m_pasteInPlaceAct = new QAction(tr("Paste in Place"), this); m_pasteInPlaceAct->setShortcut(tr("Ctrl+Shift+V")); m_pasteInPlaceAct->setStatusTip(tr("Paste clipboard contents in place")); connect(m_pasteInPlaceAct, SIGNAL(triggered()), this, SLOT(pasteInPlace())); m_duplicateAct = new QAction(tr("&Duplicate"), this); m_duplicateAct->setShortcut(tr("Ctrl+D")); m_duplicateAct->setStatusTip(tr("Duplicate selection")); connect(m_duplicateAct, SIGNAL(triggered()), this, SLOT(duplicate())); m_deleteAct = new QAction(tr("&Delete"), this); m_deleteAct->setStatusTip(tr("Delete selection")); connect(m_deleteAct, SIGNAL(triggered()), this, SLOT(doDelete())); #ifdef Q_OS_MAC m_deleteAct->setShortcut(Qt::Key_Backspace); #else m_deleteAct->setShortcut(QKeySequence::Delete); #endif m_deleteMinusAct = new QAction(tr("Delete Minus"), this); m_deleteMinusAct->setStatusTip(tr("Delete selection without attached wires")); connect(m_deleteMinusAct, SIGNAL(triggered()), this, SLOT(doDeleteMinus())); #ifdef Q_OS_MAC m_deleteMinusAct->setShortcut(Qt::Key_Backspace | Qt::AltModifier); #endif m_deleteWireAct = new WireAction(m_deleteAct); m_deleteWireAct->setText(tr("&Delete Wire")); connect(m_deleteWireAct, SIGNAL(triggered()), this, SLOT(doDelete())); m_deleteWireMinusAct = new WireAction(m_deleteMinusAct); m_deleteWireMinusAct->setText(tr("Delete Wire up to bendpoints")); connect(m_deleteWireMinusAct, SIGNAL(triggered()), this, SLOT(doDeleteMinus())); m_selectAllAct = new QAction(tr("&Select All"), this); m_selectAllAct->setShortcut(QKeySequence::SelectAll); m_selectAllAct->setStatusTip(tr("Select all elements")); connect(m_selectAllAct, SIGNAL(triggered()), this, SLOT(selectAll())); m_deselectAct = new QAction(tr("&Deselect"), this); m_deselectAct->setStatusTip(tr("Deselect")); connect(m_deselectAct, SIGNAL(triggered()), this, SLOT(deselect())); m_addNoteAct = new QAction(tr("Add Note"), this); m_addNoteAct->setStatusTip(tr("Add a note")); connect(m_addNoteAct, SIGNAL(triggered()), this, SLOT(addNote())); m_preferencesAct = new QAction(tr("&Preferences..."), this); m_preferencesAct->setStatusTip(tr("Show the application's about box")); m_preferencesAct->setMenuRole(QAction::PreferencesRole); // make sure this is added to the correct menu on mac connect(m_preferencesAct, SIGNAL(triggered()), QApplication::instance(), SLOT(preferences())); } void MainWindow::createPartMenuActions() { m_openInPartsEditorNewAct = new QAction(tr("Edit (new parts editor)"), this); m_openInPartsEditorNewAct->setStatusTip(tr("Open the new parts editor on an existing part")); connect(m_openInPartsEditorNewAct, SIGNAL(triggered()), this, SLOT(openInPartsEditorNew())); m_disconnectAllAct = new QAction(tr("Disconnect All Wires"), this); m_disconnectAllAct->setStatusTip(tr("Disconnect all wires connected to this connector")); connect(m_disconnectAllAct, SIGNAL(triggered()), this, SLOT(disconnectAll())); #ifndef QT_NO_DEBUG m_infoViewOnHoverAction = new QAction(tr("Update InfoView on hover"), this); m_infoViewOnHoverAction->setCheckable(true); bool infoViewOnHover = true; m_infoViewOnHoverAction->setChecked(infoViewOnHover); setInfoViewOnHover(infoViewOnHover); connect(m_infoViewOnHoverAction, SIGNAL(toggled(bool)), this, SLOT(setInfoViewOnHover(bool))); m_exportNormalizedSvgAction = new QAction(tr("Export Normalized SVG"), this); m_exportNormalizedSvgAction->setStatusTip(tr("Export 1000 dpi SVG of this part in this view")); connect(m_exportNormalizedSvgAction, SIGNAL(triggered()), this, SLOT(exportNormalizedSVG())); m_exportNormalizedFlattenedSvgAction = new QAction(tr("Export Normalized Flattened SVG"), this); m_exportNormalizedFlattenedSvgAction->setStatusTip(tr("Export 1000 dpi Flattened SVG of this part in this view")); connect(m_exportNormalizedFlattenedSvgAction, SIGNAL(triggered()), this, SLOT(exportNormalizedFlattenedSVG())); m_dumpAllPartsAction = new QAction(tr("Dump all parts"), this); m_dumpAllPartsAction->setStatusTip(tr("Debug dump all parts in this view")); connect(m_dumpAllPartsAction, SIGNAL(triggered()), this, SLOT(dumpAllParts())); m_testConnectorsAction = new QAction(tr("Test Connectors"), this); m_testConnectorsAction->setStatusTip(tr("Connect all connectors to a single test part")); connect(m_testConnectorsAction, SIGNAL(triggered()), this, SLOT(testConnectors())); #endif m_rotate45cwAct = new QAction(tr("Rotate 45° Clockwise"), this); m_rotate45cwAct->setStatusTip(tr("Rotate current selection 45 degrees clockwise")); connect(m_rotate45cwAct, SIGNAL(triggered()), this, SLOT(rotate45cw())); m_rotate90cwAct = new QAction(tr("Rotate 90° Clockwise"), this); m_rotate90cwAct->setStatusTip(tr("Rotate the selected parts by 90 degrees clockwise")); connect(m_rotate90cwAct, SIGNAL(triggered()), this, SLOT(rotate90cw())); m_rotate180Act = new QAction(tr("Rotate 180°"), this); m_rotate180Act->setStatusTip(tr("Rotate the selected parts by 180 degrees")); connect(m_rotate180Act, SIGNAL(triggered()), this, SLOT(rotate180())); m_rotate90ccwAct = new QAction(tr("Rotate 90° Counter Clockwise"), this); m_rotate90ccwAct->setStatusTip(tr("Rotate current selection 90 degrees counter clockwise")); connect(m_rotate90ccwAct, SIGNAL(triggered()), this, SLOT(rotate90ccw())); m_rotate45ccwAct = new QAction(tr("Rotate 45° Counter Clockwise"), this); m_rotate45ccwAct->setStatusTip(tr("Rotate current selection 45 degrees counter clockwise")); connect(m_rotate45ccwAct, SIGNAL(triggered()), this, SLOT(rotate45ccw())); m_flipHorizontalAct = new QAction(tr("&Flip Horizontal"), this); m_flipHorizontalAct->setStatusTip(tr("Flip current selection horizontally")); connect(m_flipHorizontalAct, SIGNAL(triggered()), this, SLOT(flipHorizontal())); m_flipVerticalAct = new QAction(tr("&Flip Vertical"), this); m_flipVerticalAct->setStatusTip(tr("Flip current selection vertically")); connect(m_flipVerticalAct, SIGNAL(triggered()), this, SLOT(flipVertical())); m_bringToFrontAct = new QAction(tr("Bring to Front"), this); m_bringToFrontAct->setShortcut(tr("Shift+Ctrl+]")); m_bringToFrontAct->setStatusTip(tr("Bring selected object(s) to front of their layer")); connect(m_bringToFrontAct, SIGNAL(triggered()), this, SLOT(bringToFront())); m_bringToFrontWireAct = new WireAction(m_bringToFrontAct); connect(m_bringToFrontWireAct, SIGNAL(triggered()), this, SLOT(bringToFront())); m_bringForwardAct = new QAction(tr("Bring Forward"), this); m_bringForwardAct->setShortcut(tr("Ctrl+]")); m_bringForwardAct->setStatusTip(tr("Bring selected object(s) forward in their layer")); connect(m_bringForwardAct, SIGNAL(triggered()), this, SLOT(bringForward())); m_bringForwardWireAct = new WireAction(m_bringForwardAct); connect(m_bringForwardWireAct, SIGNAL(triggered()), this, SLOT(bringForward())); m_sendBackwardAct = new QAction(tr("Send Backward"), this); m_sendBackwardAct->setShortcut(tr("Ctrl+[")); m_sendBackwardAct->setStatusTip(tr("Send selected object(s) back in their layer")); connect(m_sendBackwardAct, SIGNAL(triggered()), this, SLOT(sendBackward())); m_sendBackwardWireAct = new WireAction(m_sendBackwardAct); connect(m_sendBackwardWireAct, SIGNAL(triggered()), this, SLOT(sendBackward())); m_sendToBackAct = new QAction(tr("Send to Back"), this); m_sendToBackAct->setShortcut(tr("Shift+Ctrl+[")); m_sendToBackAct->setStatusTip(tr("Send selected object(s) to the back of their layer")); connect(m_sendToBackAct, SIGNAL(triggered()), this, SLOT(sendToBack())); m_sendToBackWireAct = new WireAction(m_sendToBackAct); connect(m_sendToBackWireAct, SIGNAL(triggered()), this, SLOT(sendToBack())); m_alignLeftAct = new QAction(tr("Align Left"), this); m_alignLeftAct->setStatusTip(tr("Align selected items at the left")); connect(m_alignLeftAct, SIGNAL(triggered()), this, SLOT(alignLeft())); m_alignHorizontalCenterAct = new QAction(tr("Align Horizontal Center"), this); m_alignHorizontalCenterAct->setStatusTip(tr("Align selected items at the horizontal center")); connect(m_alignHorizontalCenterAct, SIGNAL(triggered()), this, SLOT(alignHorizontalCenter())); m_alignRightAct = new QAction(tr("Align Right"), this); m_alignRightAct->setStatusTip(tr("Align selected items at the right")); connect(m_alignRightAct, SIGNAL(triggered()), this, SLOT(alignRight())); m_alignTopAct = new QAction(tr("Align Top"), this); m_alignTopAct->setStatusTip(tr("Align selected items at the top")); connect(m_alignTopAct, SIGNAL(triggered()), this, SLOT(alignTop())); m_alignVerticalCenterAct = new QAction(tr("Align Vertical Center"), this); m_alignVerticalCenterAct->setStatusTip(tr("Align selected items at the vertical center")); connect(m_alignVerticalCenterAct, SIGNAL(triggered()), this, SLOT(alignVerticalCenter())); m_alignBottomAct = new QAction(tr("Align Bottom"), this); m_alignBottomAct->setStatusTip(tr("Align selected items at the bottom")); connect(m_alignBottomAct, SIGNAL(triggered()), this, SLOT(alignBottom())); m_moveLockAct = new QAction(tr("Lock Part"), this); m_moveLockAct->setStatusTip(tr("Prevent a part from being moved")); m_moveLockAct->setCheckable(true); connect(m_moveLockAct, SIGNAL(triggered()), this, SLOT(moveLock())); m_stickyAct = new QAction(tr("Sticky"), this); m_stickyAct->setStatusTip(tr("If a \"sticky\" part is moved, parts on top of it are also moved")); m_stickyAct->setCheckable(true); connect(m_stickyAct, SIGNAL(triggered()), this, SLOT(setSticky())); m_selectMoveLockAct = new QAction(tr("Select All Locked Parts"), this); m_selectMoveLockAct->setStatusTip(tr("Select all parts that can't be moved")); connect(m_selectMoveLockAct, SIGNAL(triggered()), this, SLOT(selectMoveLock())); m_showPartLabelAct = new QAction(tr("&Show part label"), this); m_showPartLabelAct->setStatusTip(tr("Show/hide the label for the selected parts")); connect(m_showPartLabelAct, SIGNAL(triggered()), this, SLOT(showPartLabels())); m_saveBundledPart = new QAction(tr("&Export..."), this); m_saveBundledPart->setStatusTip(tr("Export selected part")); connect(m_saveBundledPart, SIGNAL(triggered()), this, SLOT(saveBundledPart())); m_addBendpointAct = new BendpointAction(tr("Add Bendpoint"), this); m_addBendpointAct->setStatusTip(tr("Add a bendpoint to the selected wire")); connect(m_addBendpointAct, SIGNAL(triggered()), this, SLOT(addBendpoint())); m_convertToViaAct = new BendpointAction(tr("Convert Bendpoint to Via"), this); m_convertToViaAct->setStatusTip(tr("Convert the bendpoint to a via")); connect(m_convertToViaAct, SIGNAL(triggered()), this, SLOT(convertToVia())); m_convertToBendpointAct = new QAction(tr("Convert Via to Bendpoint"), this); m_convertToBendpointAct->setStatusTip(tr("Convert the via to a bendpoint")); connect(m_convertToBendpointAct, SIGNAL(triggered()), this, SLOT(convertToBendpoint())); m_flattenCurveAct = new BendpointAction(tr("Straighten Curve"), this); m_flattenCurveAct->setStatusTip(tr("Straighten the curve of the selected wire")); connect(m_flattenCurveAct, SIGNAL(triggered()), this, SLOT(flattenCurve())); m_selectAllObsoleteAct = new QAction(tr("Select outdated parts"), this); m_selectAllObsoleteAct->setStatusTip(tr("Select outdated parts")); connect(m_selectAllObsoleteAct, SIGNAL(triggered()), this, SLOT(selectAllObsolete())); m_swapObsoleteAct = new QAction(tr("Update selected parts"), this); m_swapObsoleteAct->setStatusTip(tr("Update selected parts")); connect(m_swapObsoleteAct, SIGNAL(triggered()), this, SLOT(swapObsolete())); m_findPartInSketchAct = new QAction(tr("Find part in sketch..."), this); m_findPartInSketchAct->setStatusTip(tr("Search for parts in a sketch by matching text")); connect(m_findPartInSketchAct, SIGNAL(triggered()), this, SLOT(findPartInSketch())); m_hidePartSilkscreenAct = new QAction(tr("Hide part silkscreen"), this); m_hidePartSilkscreenAct->setStatusTip(tr("Hide/show the silkscreen layer for only this part")); connect(m_hidePartSilkscreenAct, SIGNAL(triggered()), this, SLOT(hidePartSilkscreen())); m_regeneratePartsDatabaseAct = new QAction(tr("Regenerate parts database ..."), this); m_regeneratePartsDatabaseAct->setStatusTip(tr("Regenerate the parts database (should only be used if your parts database is broken)")); connect(m_regeneratePartsDatabaseAct, SIGNAL(triggered()), qApp, SLOT(regeneratePartsDatabase())); } void MainWindow::createViewMenuActions(bool showWelcome) { m_zoomInAct = new QAction(tr("&Zoom In"), this); m_zoomInAct->setShortcut(tr("Ctrl++")); m_zoomInAct->setStatusTip(tr("Zoom in")); connect(m_zoomInAct, SIGNAL(triggered()), this, SLOT(zoomIn())); // instead of creating a filter to grab the shortcut, let's create a new action // and append it to the window m_zoomInShortcut = new QAction(this); m_zoomInShortcut->setShortcut(tr("Ctrl+=")); connect(m_zoomInShortcut, SIGNAL(triggered()), this, SLOT(zoomIn())); this->addAction(m_zoomInShortcut); m_zoomOutAct = new QAction(tr("&Zoom Out"), this); m_zoomOutAct->setShortcut(tr("Ctrl+-")); m_zoomOutAct->setStatusTip(tr("Zoom out")); connect(m_zoomOutAct, SIGNAL(triggered()), this, SLOT(zoomOut())); m_fitInWindowAct = new QAction(tr("&Fit in Window"), this); m_fitInWindowAct->setShortcut(tr("Ctrl+0")); m_fitInWindowAct->setStatusTip(tr("Fit in window")); connect(m_fitInWindowAct, SIGNAL(triggered()), this, SLOT(fitInWindow())); m_actualSizeAct = new QAction(tr("&Actual Size"), this); m_actualSizeAct->setStatusTip(tr("Actual (real world physical) size")); connect(m_actualSizeAct, SIGNAL(triggered()), this, SLOT(actualSize())); m_100PercentSizeAct = new QAction(tr("100% Size"), this); m_100PercentSizeAct->setShortcut(tr("Shift+Ctrl+0")); m_100PercentSizeAct->setStatusTip(tr("100% (pixel) size")); connect(m_100PercentSizeAct, SIGNAL(triggered()), this, SLOT(hundredPercentSize())); m_alignToGridAct = new QAction(tr("Align to Grid"), this); m_alignToGridAct->setStatusTip(tr("Align items to grid when dragging")); m_alignToGridAct->setCheckable(true); connect(m_alignToGridAct, SIGNAL(triggered()), this, SLOT(alignToGrid())); m_showGridAct = new QAction(tr("Show Grid"), this); m_showGridAct->setStatusTip(tr("Show the grid")); m_showGridAct->setCheckable(true); connect(m_showGridAct, SIGNAL(triggered()), this, SLOT(showGrid())); m_setGridSizeAct = new QAction(tr("Set Grid Size..."), this); m_setGridSizeAct->setStatusTip(tr("Set the size of the grid in this view")); connect(m_setGridSizeAct, SIGNAL(triggered()), this, SLOT(setGridSize())); m_setBackgroundColorAct = new QAction(tr("Set Background Color..."), this); m_setBackgroundColorAct->setStatusTip(tr("Set the background color of this view")); connect(m_setBackgroundColorAct, SIGNAL(triggered()), this, SLOT(setBackgroundColor())); QStringList controls; controls << tr("Ctrl+1") << tr("Ctrl+2") << tr("Ctrl+3") << tr("Ctrl+4") << tr("Ctrl+5"); int controlIndex = 0; if (showWelcome) { m_showWelcomeAct = new QAction(tr("&Show Welcome"), this); m_showWelcomeAct->setShortcut(controls.at(controlIndex++)); m_showWelcomeAct->setStatusTip(tr("Show the welcome view")); m_showWelcomeAct->setCheckable(true); connect(m_showWelcomeAct, SIGNAL(triggered()), this, SLOT(showWelcomeView())); } m_showBreadboardAct = new QAction(tr("&Show Breadboard"), this); m_showBreadboardAct->setShortcut(controls.at(controlIndex++)); m_showBreadboardAct->setStatusTip(tr("Show the breadboard view")); m_showBreadboardAct->setCheckable(true); connect(m_showBreadboardAct, SIGNAL(triggered()), this, SLOT(showBreadboardView())); m_showSchematicAct = new QAction(tr("&Show Schematic"), this); m_showSchematicAct->setShortcut(controls.at(controlIndex++)); m_showSchematicAct->setStatusTip(tr("Show the schematic view")); m_showSchematicAct->setCheckable(true); connect(m_showSchematicAct, SIGNAL(triggered()), this, SLOT(showSchematicView())); m_showPCBAct = new QAction(tr("&Show PCB"), this); m_showPCBAct->setShortcut(controls.at(controlIndex++)); m_showPCBAct->setStatusTip(tr("Show the PCB view")); m_showPCBAct->setCheckable(true); connect(m_showPCBAct, SIGNAL(triggered()), this, SLOT(showPCBView())); if (m_programView) { m_showProgramAct = new QAction(tr("Show Code"), this); m_showProgramAct->setShortcut(controls.at(controlIndex++)); m_showProgramAct->setStatusTip(tr("Show the code (programming) view")); m_showProgramAct->setCheckable(true); connect(m_showProgramAct, SIGNAL(triggered()), this, SLOT(showProgramView())); QList viewMenuActions; if (m_welcomeView) viewMenuActions << m_showWelcomeAct; viewMenuActions << m_showBreadboardAct << m_showSchematicAct << m_showPCBAct << m_showProgramAct; m_programView->createViewMenuActions(viewMenuActions); } m_showPartsBinIconViewAct = new QAction(tr("Show Parts Bin Icon View"), this); m_showPartsBinIconViewAct->setStatusTip(tr("Display the parts bin in an icon view")); m_showPartsBinIconViewAct->setCheckable(true); connect(m_showPartsBinIconViewAct, SIGNAL(triggered()), this, SLOT(showPartsBinIconView())); m_showPartsBinListViewAct = new QAction(tr("Show Parts Bin List View"), this); m_showPartsBinListViewAct->setStatusTip(tr("Display the parts bin in a list view")); m_showPartsBinListViewAct->setCheckable(true); connect(m_showPartsBinListViewAct, SIGNAL(triggered()), this, SLOT(showPartsBinListView())); m_showAllLayersAct = new QAction(tr("&Show All Layers"), this); m_showAllLayersAct->setStatusTip(tr("Show all the available layers for the current view")); connect(m_showAllLayersAct, SIGNAL(triggered()), this, SLOT(showAllLayers())); m_hideAllLayersAct = new QAction(tr("&Hide All Layers"), this); m_hideAllLayersAct->setStatusTip(tr("Hide all the layers of the current view")); connect(m_hideAllLayersAct, SIGNAL(triggered()), this, SLOT(hideAllLayers())); } void MainWindow::createWindowMenuActions() { m_minimizeAct = new QAction(tr("&Minimize"), this); m_minimizeAct->setShortcut(tr("Ctrl+M")); m_minimizeAct->setStatusTip(tr("Minimize current window")); connect(m_minimizeAct, SIGNAL(triggered(bool)), this, SLOT(minimize())); /* m_toggleToolbarAct = new QAction(tr("&Toolbar"), this); m_toggleToolbarAct->setShortcut(tr("Shift+Ctrl+T")); m_toggleToolbarAct->setCheckable(true); m_toggleToolbarAct->setChecked(true); m_toggleToolbarAct->setStatusTip(tr("Toggle Toolbar visibility")); connect(m_toggleToolbarAct, SIGNAL(triggered(bool)), this, SLOT(toggleToolbar(bool))); */ m_toggleDebuggerOutputAct = new QAction(tr("Debugger Output"), this); m_toggleDebuggerOutputAct->setCheckable(true); connect(m_toggleDebuggerOutputAct, SIGNAL(triggered(bool)), this, SLOT(toggleDebuggerOutput(bool))); m_openProgramWindowAct = new QAction(tr("Open programming window"), this); m_openProgramWindowAct->setStatusTip(tr("Open microcontroller programming window")); connect(m_openProgramWindowAct, SIGNAL(triggered()), this, SLOT(openProgramWindow())); } void MainWindow::createHelpMenuActions() { m_openHelpAct = new QAction(tr("Online Tutorials"), this); m_openHelpAct->setShortcut(tr("Ctrl+?")); m_openHelpAct->setStatusTip(tr("Open Fritzing help")); connect(m_openHelpAct, SIGNAL(triggered(bool)), this, SLOT(openHelp())); m_openDonateAct = new QAction(tr("Donate to Fritzing"), this); m_openDonateAct->setStatusTip(tr("Open Fritzing donation web page")); connect(m_openDonateAct, SIGNAL(triggered(bool)), this, SLOT(openDonate())); m_examplesAct = new QAction(tr("Online Projects Gallery"), this); m_examplesAct->setStatusTip(tr("Open Fritzing examples")); connect(m_examplesAct, SIGNAL(triggered(bool)), this, SLOT(openExamples())); m_partsRefAct = new QAction(tr("Online Parts Reference"), this); m_partsRefAct->setStatusTip(tr("Open Parts Reference")); connect(m_partsRefAct, SIGNAL(triggered(bool)), this, SLOT(openPartsReference())); /*m_visitFritzingDotOrgAct = new QAction(tr("Visit fritzing.org"), this); m_visitFritzingDotOrgAct->setStatusTip(tr("www.fritzing.org")); connect(m_visitFritzingDotOrgAct, SIGNAL(triggered(bool)), this, SLOT(visitFritzingDotOrg()));*/ m_checkForUpdatesAct = new QAction(tr("Check for updates..."), this); m_checkForUpdatesAct->setStatusTip(tr("Check whether a newer version of Fritzing is available for download")); connect(m_checkForUpdatesAct, SIGNAL(triggered()), QApplication::instance(), SLOT(checkForUpdates())); m_aboutAct = new QAction(tr("&About"), this); m_aboutAct->setStatusTip(tr("Show the application's about box")); connect(m_aboutAct, SIGNAL(triggered()), this, SLOT(about())); m_aboutAct->setMenuRole(QAction::AboutRole); m_tipsAndTricksAct = new QAction(tr("Tips, Tricks and Shortcuts"), this); m_tipsAndTricksAct->setStatusTip(tr("Display some handy Fritzing tips and tricks")); connect(m_tipsAndTricksAct, SIGNAL(triggered()), this, SLOT(tipsAndTricks())); m_firstTimeHelpAct = new QAction(tr("First Time Help"), this); m_firstTimeHelpAct->setStatusTip(tr("Display First Time Help")); connect(m_firstTimeHelpAct, SIGNAL(triggered()), this, SLOT(firstTimeHelp())); m_aboutQtAct = new QAction(tr("&About Qt"), this); m_aboutQtAct->setStatusTip(tr("Show Qt's about box")); connect(m_aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); m_reportBugAct = new QAction(tr("Report a bug..."), this); m_reportBugAct->setStatusTip(tr("Report a but you've found in Fritzing")); connect(m_reportBugAct, SIGNAL(triggered()), this, SLOT(reportBug())); m_enableDebugAct = new QAction(tr("Enable debugging log"), this); m_enableDebugAct->setStatusTip(tr("Report a but you've found in Fritzing")); m_enableDebugAct->setCheckable(true); m_enableDebugAct->setChecked(DebugDialog::enabled()); connect(m_enableDebugAct, SIGNAL(triggered()), this, SLOT(enableDebug())); m_partsEditorHelpAct = new QAction(tr("Parts Editor Help"), this); m_partsEditorHelpAct->setStatusTip(tr("Display Parts Editor help in a browser")); connect(m_partsEditorHelpAct, SIGNAL(triggered(bool)), this, SLOT(partsEditorHelp())); } void MainWindow::createMenus() { createFileMenu(); createEditMenu(); createPartMenu(); createViewMenu(); m_programView->initMenus(menuBar()); createWindowMenu(); createTraceMenus(); createHelpMenu(); } void MainWindow::createRotateSubmenu(QMenu * parentMenu) { QMenu *rotateMenu = parentMenu->addMenu(tr("Rotate")); rotateMenu->addAction(m_rotate45cwAct); rotateMenu->addAction(m_rotate90cwAct); rotateMenu->addAction(m_rotate180Act); rotateMenu->addAction(m_rotate90ccwAct); rotateMenu->addAction(m_rotate45ccwAct); } void MainWindow::createZOrderSubmenu(QMenu * parentMenu) { QMenu *zOrderMenu = parentMenu->addMenu(tr("Raise and Lower")); zOrderMenu->addAction(m_bringToFrontAct); zOrderMenu->addAction(m_bringForwardAct); zOrderMenu->addAction(m_sendBackwardAct); zOrderMenu->addAction(m_sendToBackAct); } /* void MainWindow::createZOrderWireSubmenu(QMenu * parentMenu) { QMenu *zOrderWireMenu = parentMenu->addMenu(tr("Raise and Lower")); zOrderWireMenu->addAction(m_bringToFrontWireAct); zOrderWireMenu->addAction(m_bringForwardWireAct); zOrderWireMenu->addAction(m_sendBackwardWireAct); zOrderWireMenu->addAction(m_sendToBackWireAct); } */ void MainWindow::createAlignSubmenu(QMenu * parentMenu) { QMenu *alignMenu = parentMenu->addMenu(tr("Align")); alignMenu->addAction(m_alignLeftAct); alignMenu->addAction(m_alignHorizontalCenterAct); alignMenu->addAction(m_alignRightAct); alignMenu->addAction(m_alignTopAct); alignMenu->addAction(m_alignVerticalCenterAct); alignMenu->addAction(m_alignBottomAct); } void MainWindow::createAddToBinSubmenu(QMenu * parentMenu) { QMenu *addToBinMenu = parentMenu->addMenu(tr("&Add to bin...")); addToBinMenu->setStatusTip(tr("Add selected part to bin")); QList acts = m_binManager->openedBinsActions(selectedModuleID()); addToBinMenu->addActions(acts); } void MainWindow::createFileMenu() { m_fileMenu = menuBar()->addMenu(tr("&File")); m_fileMenu->addAction(m_newAct); m_fileMenu->addAction(m_openAct); m_fileMenu->addAction(m_revertAct); m_fileMenu->addMenu(m_openRecentFileMenu); m_fileMenu->addMenu(m_openExampleMenu); m_fileMenu->addSeparator(); m_fileMenu->addAction(m_closeAct); m_fileMenu->addAction(m_saveAct); m_fileMenu->addAction(m_saveAsAct); m_fileMenu->addAction(m_shareOnlineAct); if (m_orderFabEnabled) { m_fileMenu->addAction(m_orderFabAct); } m_fileMenu->addSeparator(); m_exportMenu = m_fileMenu->addMenu(tr("&Export")); connect(m_exportMenu, SIGNAL(aboutToShow()), this, SLOT(updateExportMenu())); //m_fileMenu->addAction(m_pageSetupAct); m_fileMenu->addAction(m_printAct); QString name; QString path; QStringList args; if (externalProcess(name, path, args)) { m_fileMenu->addSeparator(); m_fileMenu->addAction(m_launchExternalProcessAct); } #ifndef QT_NO_DEBUG m_fileMenu->addSeparator(); m_fileMenu->addAction(m_exceptionAct); #endif m_fileMenu->addSeparator(); m_fileMenu->addAction(m_quitAct); connect(m_fileMenu, SIGNAL(aboutToShow()), this, SLOT(updateFileMenu())); populateExportMenu(); m_exportMenu->addAction(m_exportBomAct); m_exportMenu->addAction(m_exportNetlistAct); m_exportMenu->addAction(m_exportSpiceNetlistAct); //m_exportMenu->addAction(m_exportEagleAct); } void MainWindow::populateExportMenu() { QMenu * imageMenu = m_exportMenu->addMenu(tr("as Image")); imageMenu->addAction(m_exportPngAct); imageMenu->addAction(m_exportJpgAct); imageMenu->addSeparator(); imageMenu->addAction(m_exportSvgAct); imageMenu->addAction(m_exportPdfAct); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) imageMenu->addAction(m_exportPsAct); #endif QMenu * productionMenu = m_exportMenu->addMenu(tr("for Production")); productionMenu->addAction(m_exportEtchablePdfAct); productionMenu->addAction(m_exportEtchableSvgAct); productionMenu->addSeparator(); productionMenu->addAction(m_exportGerberAct); } void MainWindow::createEditMenu() { m_editMenu = menuBar()->addMenu(tr("&Edit")); m_editMenu->addAction(m_undoAct); m_editMenu->addAction(m_redoAct); m_editMenu->addSeparator(); m_editMenu->addAction(m_cutAct); m_editMenu->addAction(m_copyAct); m_editMenu->addAction(m_pasteAct); m_editMenu->addAction(m_pasteInPlaceAct); m_editMenu->addAction(m_duplicateAct); m_editMenu->addAction(m_deleteAct); m_editMenu->addAction(m_deleteMinusAct); m_editMenu->addSeparator(); m_editMenu->addAction(m_selectAllAct); m_editMenu->addAction(m_deselectAct); m_editMenu->addSeparator(); m_editMenu->addAction(m_addNoteAct); m_editMenu->addSeparator(); m_editMenu->addAction(m_preferencesAct); updateEditMenu(); connect(m_editMenu, SIGNAL(aboutToShow()), this, SLOT(updateEditMenu())); } void MainWindow::createPartMenu() { m_partMenu = menuBar()->addMenu(tr("&Part")); connect(m_partMenu, SIGNAL(aboutToShow()), this, SLOT(updatePartMenu())); m_partMenu->addAction(m_openInPartsEditorNewAct); m_partMenu->addSeparator(); m_partMenu->addAction(m_saveBundledPart); m_partMenu->addSeparator(); m_partMenu->addAction(m_flipHorizontalAct); m_partMenu->addAction(m_flipVerticalAct); createRotateSubmenu(m_partMenu); createZOrderSubmenu(m_partMenu); //createZOrderWireSubmenu(m_partMenu); createAlignSubmenu(m_partMenu); m_partMenu->addAction(m_moveLockAct); m_partMenu->addAction(m_stickyAct); m_partMenu->addAction(m_selectMoveLockAct); m_partMenu->addSeparator(); createAddToBinSubmenu(m_partMenu); m_partMenu->addAction(m_showPartLabelAct); m_partMenu->addSeparator(); m_partMenu->addAction(m_selectAllObsoleteAct); m_partMenu->addAction(m_swapObsoleteAct); m_partMenu->addSeparator(); m_partMenu->addAction(m_findPartInSketchAct); #ifndef QT_NO_DEBUG m_partMenu->addAction(m_dumpAllPartsAction); #endif m_partMenu->addSeparator(); m_partMenu->addAction(m_regeneratePartsDatabaseAct); } void MainWindow::createViewMenu() { m_viewMenu = menuBar()->addMenu(tr("&View")); m_viewMenu->addAction(m_zoomInAct); m_viewMenu->addAction(m_zoomOutAct); m_viewMenu->addAction(m_fitInWindowAct); m_viewMenu->addAction(m_actualSizeAct); m_viewMenu->addAction(m_100PercentSizeAct); m_viewMenu->addSeparator(); m_viewMenu->addAction(m_alignToGridAct); m_viewMenu->addAction(m_showGridAct); m_viewMenu->addAction(m_setGridSizeAct); m_viewMenu->addAction(m_setBackgroundColorAct); m_viewMenu->addSeparator(); if (m_welcomeView) m_viewMenu->addAction(m_showWelcomeAct); m_viewMenu->addAction(m_showBreadboardAct); m_viewMenu->addAction(m_showSchematicAct); m_viewMenu->addAction(m_showPCBAct); if (m_programView) m_viewMenu->addAction(m_showProgramAct); m_viewMenu->addSeparator(); if (m_binManager) { m_viewMenu->addAction(m_showPartsBinIconViewAct); m_viewMenu->addAction(m_showPartsBinListViewAct); m_viewMenu->addSeparator(); } connect(m_viewMenu, SIGNAL(aboutToShow()), this, SLOT(updateLayerMenu())); m_numFixedActionsInViewMenu = m_viewMenu->actions().size(); } void MainWindow::createWindowMenu() { m_windowMenu = menuBar()->addMenu(tr("&Window")); m_windowMenu->addAction(m_minimizeAct); m_windowMenu->addSeparator(); //m_windowMenu->addAction(m_toggleToolbarAct); updateWindowMenu(); connect(m_windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu())); } void MainWindow::createTraceMenus() { m_pcbTraceMenu = menuBar()->addMenu(tr("&Routing")); m_pcbTraceMenu->addAction(m_newAutorouteAct); m_pcbTraceMenu->addAction(m_newDesignRulesCheckAct); m_pcbTraceMenu->addAction(m_autorouterSettingsAct); m_pcbTraceMenu->addAction(m_fabQuoteAct); QMenu * groundFillMenu = m_pcbTraceMenu->addMenu(tr("Ground Fill")); groundFillMenu->addAction(m_copperFillAct); groundFillMenu->addAction(m_groundFillAct); groundFillMenu->addAction(m_removeGroundFillAct); groundFillMenu->addAction(m_setGroundFillSeedsAct); groundFillMenu->addAction(m_clearGroundFillSeedsAct); groundFillMenu->addAction(m_setGroundFillKeepoutAct); //m_pcbTraceMenu->addAction(m_updateRoutingStatusAct); m_pcbTraceMenu->addSeparator(); m_pcbTraceMenu->addAction(m_viewFromBelowToggleAct); m_pcbTraceMenu->addAction(m_activeLayerBothAct); m_pcbTraceMenu->addAction(m_activeLayerBottomAct); m_pcbTraceMenu->addAction(m_activeLayerTopAct); m_pcbTraceMenu->addSeparator(); m_pcbTraceMenu->addAction(m_changeTraceLayerAct); m_pcbTraceMenu->addAction(m_excludeFromAutorouteAct); m_pcbTraceMenu->addSeparator(); m_pcbTraceMenu->addAction(m_showUnroutedAct); m_pcbTraceMenu->addAction(m_selectAllTracesAct); m_pcbTraceMenu->addAction(m_selectAllExcludedTracesAct); m_pcbTraceMenu->addAction(m_selectAllIncludedTracesAct); m_pcbTraceMenu->addAction(m_selectAllJumperItemsAct); m_pcbTraceMenu->addAction(m_selectAllViasAct); m_pcbTraceMenu->addAction(m_selectAllCopperFillAct); //m_pcbTraceMenu->addSeparator(); //m_pcbTraceMenu->addAction(m_checkLoadedTracesAct); m_schematicTraceMenu = menuBar()->addMenu(tr("&Routing")); m_schematicTraceMenu->addAction(m_newAutorouteAct); m_schematicTraceMenu->addAction(m_excludeFromAutorouteAct); m_schematicTraceMenu->addAction(m_showUnroutedAct); m_schematicTraceMenu->addAction(m_selectAllTracesAct); m_schematicTraceMenu->addAction(m_selectAllExcludedTracesAct); m_schematicTraceMenu->addAction(m_selectAllIncludedTracesAct); //m_schematicTraceMenu->addAction(m_updateRoutingStatusAct); #ifndef QT_NO_DEBUG m_schematicTraceMenu->addAction(m_tidyWiresAct); #endif m_breadboardTraceMenu = menuBar()->addMenu(tr("&Routing")); m_breadboardTraceMenu->addAction(m_showUnroutedAct); m_breadboardTraceMenu->addAction(m_selectAllWiresAct); updateTraceMenu(); connect(m_pcbTraceMenu, SIGNAL(aboutToShow()), this, SLOT(updateTraceMenu())); connect(m_schematicTraceMenu, SIGNAL(aboutToShow()), this, SLOT(updateTraceMenu())); connect(m_breadboardTraceMenu, SIGNAL(aboutToShow()), this, SLOT(updateTraceMenu())); menuBar()->addSeparator(); } void MainWindow::createHelpMenu() { m_helpMenu = menuBar()->addMenu(tr("&Help")); m_helpMenu->addAction(m_openHelpAct); m_helpMenu->addAction(m_examplesAct); m_helpMenu->addAction(m_partsRefAct); m_helpMenu->addSeparator(); m_helpMenu->addAction(m_partsEditorHelpAct); m_helpMenu->addSeparator(); m_helpMenu->addAction(m_checkForUpdatesAct); m_helpMenu->addSeparator(); m_helpMenu->addAction(m_reportBugAct); m_helpMenu->addAction(m_enableDebugAct); m_helpMenu->addSeparator(); m_helpMenu->addAction(m_aboutAct); m_helpMenu->addAction(m_openDonateAct); m_helpMenu->addAction(m_tipsAndTricksAct); m_helpMenu->addAction(m_firstTimeHelpAct); #ifndef QT_NO_DEBUG m_helpMenu->addAction(m_aboutQtAct); #endif } void MainWindow::updateLayerMenu(bool resetLayout) { if (m_viewMenu == NULL) return; if (m_showAllLayersAct == NULL) return; QList actions; actions << m_zoomInAct << m_zoomOutAct << m_zoomInShortcut << m_fitInWindowAct << m_actualSizeAct << m_100PercentSizeAct << m_alignToGridAct << m_showGridAct << m_setGridSizeAct << m_setBackgroundColorAct; bool enabled = (m_currentGraphicsView != NULL); foreach (QAction * action, actions) action->setEnabled(enabled); actions.clear(); if (m_showPartsBinIconViewAct) { if (m_binManager) { m_showPartsBinIconViewAct->setEnabled(true); m_showPartsBinListViewAct->setEnabled(true); actions << m_showPartsBinIconViewAct << m_showPartsBinListViewAct; setActionsIcons(m_binManager->currentViewIsIconView() ? 0 : 1, actions); } else { m_showPartsBinIconViewAct->setEnabled(false); m_showPartsBinListViewAct->setEnabled(false); } } removeActionsStartingAt(m_viewMenu, m_numFixedActionsInViewMenu); if (m_showAllLayersAct) m_viewMenu->addAction(m_showAllLayersAct); if (m_hideAllLayersAct) m_viewMenu->addAction(m_hideAllLayersAct); m_hideAllLayersAct->setEnabled(false); m_showAllLayersAct->setEnabled(false); if (m_currentGraphicsView == NULL) { return; } m_alignToGridAct->setChecked(m_currentGraphicsView->alignedToGrid()); m_showGridAct->setChecked(m_currentGraphicsView->showingGrid()); LayerHash viewLayers = m_currentGraphicsView->viewLayers(); LayerList keys = viewLayers.keys(); // make sure they're in ascending order when inserting into the menu qSort(keys.begin(), keys.end()); foreach (ViewLayer::ViewLayerID key, keys) { ViewLayer * viewLayer = viewLayers.value(key); //DebugDialog::debug(QString("Layer: %1 is %2").arg(viewLayer->action()->text()).arg(viewLayer->action()->isEnabled())); if (viewLayer != NULL) { if (viewLayer->parentLayer()) continue; m_viewMenu->addAction(viewLayer->action()); disconnect(viewLayer->action(), SIGNAL(triggered()), this, SLOT(updateLayerMenu())); connect(viewLayer->action(), SIGNAL(triggered()), this, SLOT(updateLayerMenu())); } } if (keys.count() <= 0) return; ViewLayer *prev = viewLayers.value(keys[0]); if (prev == NULL) { // jrc: I think prev == NULL is actually a side effect from an earlier bug // but I haven't figured out the cause yet // at any rate, when this bug occurs, keys[0] is some big negative number that looks like an // uninitialized or scrambled layerID DebugDialog::debug(QString("updateAllLayersActions keys[0] failed %1").arg(keys[0]) ); return; } bool sameState = prev->action()->isChecked(); bool checked = prev->action()->isChecked(); //DebugDialog::debug(QString("Layer: %1 is %2").arg(prev->action()->text()).arg(prev->action()->isChecked())); for (int i = 1; i < keys.count(); i++) { ViewLayer *viewLayer = viewLayers.value(keys[i]); //DebugDialog::debug(QString("Layer: %1 is %2").arg(viewLayer->action()->text()).arg(viewLayer->action()->isChecked())); if (viewLayer != NULL) { if (prev != NULL && prev->action()->isChecked() != viewLayer->action()->isChecked() ) { // if the actions aren't all checked or unchecked I don't bother about the "checked" variable sameState = false; break; } else { sameState = true; checked = viewLayer->action()->isChecked(); } prev = viewLayer; } } //DebugDialog::debug(QString("sameState: %1").arg(sameState)); //DebugDialog::debug(QString("checked: %1").arg(checked)); if (sameState) { if(checked) { m_hideAllLayersAct->setEnabled(true); } else { m_showAllLayersAct->setEnabled(true); } } else { m_showAllLayersAct->setEnabled(true); m_hideAllLayersAct->setEnabled(true); } if (resetLayout) { m_layerPalette->resetLayout(viewLayers, keys); } m_layerPalette->updateLayerPalette(viewLayers, keys); } void MainWindow::updateWireMenu() { // assumes update wire menu is only called when right-clicking a wire // and that wire is cached by the menu in Wire::mousePressEvent Wire * wire = m_activeWire; m_activeWire = NULL; if (wire) { enableAddBendpointAct(wire); } bool enableAll = true; bool deleteOK = false; bool createTraceOK = false; bool excludeOK = false; bool enableZOK = true; bool gotRat = false; bool ctlOK = false; if (wire != NULL) { if (wire->getRatsnest()) { QList ends; Wire * jt = wire->findTraced(m_currentGraphicsView->getTraceFlag(), ends); createTraceOK = (jt == NULL) || (!jt->getTrace()); deleteOK = true; gotRat = true; enableZOK = false; } else if (wire->getTrace()) { deleteOK = true; excludeOK = true; m_excludeFromAutorouteWireAct->setChecked(!wire->getAutoroutable()); if (m_currentGraphicsView == m_pcbGraphicsView && m_currentGraphicsView->boardLayers() > 1) { if (wire->canSwitchLayers()) { if (ViewLayer::topLayers().contains(wire->viewLayerID())) { m_changeTraceLayerWireAct->setText(tr("Move to bottom layer")); } else { m_changeTraceLayerWireAct->setText(tr("Move to top layer")); } ctlOK = true; } } } else { deleteOK = true; } } QMenu* wireColorMenu = (m_currentGraphicsView == m_breadboardGraphicsView ? m_breadboardWireColorMenu : m_schematicWireColorMenu); if (wire) { wireColorMenu->setEnabled(true); QString colorString = wire->colorString(); //DebugDialog::debug("wire colorstring " + colorString); foreach (QAction * action, wireColorMenu->actions()) { QString colorName = action->data().toString(); //DebugDialog::debug("colorname " + colorName); action->setChecked(colorName.compare(colorString) == 0); } } else { wireColorMenu->setEnabled(false); } m_bringToFrontWireAct->setWire(wire); m_bringForwardWireAct->setWire(wire); m_sendBackwardWireAct->setWire(wire); m_sendToBackWireAct->setWire(wire); m_createTraceWireAct->setWire(wire); m_createWireWireAct->setWire(wire); m_deleteWireAct->setWire(wire); m_deleteWireMinusAct->setWire(wire); m_excludeFromAutorouteWireAct->setWire(wire); m_changeTraceLayerWireAct->setWire(wire); m_bringToFrontWireAct->setEnabled(enableZOK); m_bringForwardWireAct->setEnabled(enableZOK); m_sendBackwardWireAct->setEnabled(enableZOK); m_sendToBackWireAct->setEnabled(enableZOK); m_createTraceWireAct->setEnabled(enableAll && createTraceOK); m_createWireWireAct->setEnabled(enableAll && createTraceOK); m_deleteWireAct->setEnabled(enableAll && deleteOK); m_deleteWireMinusAct->setEnabled(enableAll && deleteOK && !gotRat); m_excludeFromAutorouteWireAct->setEnabled(enableAll && excludeOK); m_changeTraceLayerAct->setEnabled(ctlOK); m_changeTraceLayerWireAct->setEnabled(ctlOK); if (gotRat) { m_deleteWireAct->setText(tr("Delete Ratsnest Line")); } else { m_deleteWireAct->setText(tr("Delete Wire")); } } void MainWindow::updatePartMenu() { if (m_partMenu == NULL) return; if (m_currentGraphicsView == NULL) { foreach (QAction * action, m_partMenu->actions()) { action->setEnabled(false); } return; } ItemCount itemCount = m_currentGraphicsView->calcItemCount(); bool enable = true; bool zenable = true; if (itemCount.selCount <= 0) { zenable = enable = false; } else { if (itemCount.itemsCount == itemCount.selCount) { // if all items are selected // z-reordering is a no-op zenable = false; } } m_alignLeftAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1); m_alignRightAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1); m_alignTopAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1); m_alignBottomAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1); m_alignVerticalCenterAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1); m_alignHorizontalCenterAct->setEnabled(itemCount.selCount - itemCount.wireCount > 1); //DebugDialog::debug(QString("enable layer actions %1")upat.arg(enable)); m_bringToFrontAct->setEnabled(zenable); m_bringForwardAct->setEnabled(zenable); m_sendBackwardAct->setEnabled(zenable); m_sendToBackAct->setEnabled(zenable); m_moveLockAct->setEnabled(itemCount.selCount > 0 && itemCount.selCount > itemCount.wireCount); m_moveLockAct->setChecked(itemCount.moveLockCount > 0); m_selectMoveLockAct->setEnabled(true); m_showPartLabelAct->setEnabled((itemCount.hasLabelCount > 0) && enable); m_showPartLabelAct->setText(itemCount.visLabelCount == itemCount.hasLabelCount ? tr("Hide part label") : tr("Show part label")); m_showPartLabelAct->setData(itemCount.visLabelCount != itemCount.hasLabelCount); bool renable = (itemCount.selRotatable > 0); bool renable45 = (itemCount.sel45Rotatable > 0); //DebugDialog::debug(QString("enable rotate (2) %1").arg(enable)); m_rotate90cwAct->setEnabled(renable && enable); m_rotate180Act->setEnabled(renable && enable); m_rotate90ccwAct->setEnabled(renable && enable); m_rotate45ccwAct->setEnabled(renable && renable45 && enable); m_rotate45cwAct->setEnabled(renable && renable45 && enable); m_flipHorizontalAct->setEnabled(enable && (itemCount.selHFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView)); m_flipVerticalAct->setEnabled(enable && (itemCount.selVFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView)); updateItemMenu(); updateEditMenu(); bool ctbpVisible = false; bool ctbpEnabled = false; if (itemCount.selCount == 1) { ItemBase * itemBase = dynamic_cast(m_currentGraphicsView->scene()->selectedItems()[0]); enableAddBendpointAct(itemBase); Via * via = qobject_cast(itemBase->layerKinChief()); if (via) { ctbpVisible = true; int count = 0; QList viaConnectorItems; viaConnectorItems << via->connectorItem(); if (via->connectorItem()->getCrossLayerConnectorItem()) { viaConnectorItems << via->connectorItem()->getCrossLayerConnectorItem(); } foreach (ConnectorItem * viaConnectorItem, viaConnectorItems) { foreach (ConnectorItem * connectorItem, viaConnectorItem->connectedToItems()) { Wire * wire = qobject_cast(connectorItem->attachedTo()); if (wire == NULL) continue; if (wire->getRatsnest()) continue; if (wire->isTraceType(m_currentGraphicsView->getTraceFlag())) { count++; if (count > 1) { ctbpEnabled = true; break; } } } if (count > 1) break; } } m_openInPartsEditorNewAct->setEnabled(itemBase->canEditPart()); m_stickyAct->setVisible(itemBase->isBaseSticky()); m_stickyAct->setEnabled(true); m_stickyAct->setChecked(itemBase->isBaseSticky() && itemBase->isLocalSticky()); QList itemBases; itemBases.append(itemBase); itemBases.append(itemBase->layerKinChief()->layerKin()); bool hpsa = false; foreach (ItemBase * lkpi, itemBases) { if (lkpi->viewLayerID() == ViewLayer::Silkscreen1 || lkpi->viewLayerID() == ViewLayer::Silkscreen0) { hpsa = true; m_hidePartSilkscreenAct->setText(lkpi->layerHidden() ? tr("Show part silkscreen") : tr("Hide part silkscreen")); break; } } m_hidePartSilkscreenAct->setEnabled(hpsa); } else { m_hidePartSilkscreenAct->setEnabled(false); m_stickyAct->setVisible(false); m_openInPartsEditorNewAct->setEnabled(false); } m_convertToBendpointAct->setEnabled(ctbpEnabled); m_convertToBendpointAct->setVisible(ctbpVisible); m_convertToBendpointSeparator->setVisible(ctbpVisible); // TODO: only enable if there is an obsolete part in the sketch m_selectAllObsoleteAct->setEnabled(true); m_swapObsoleteAct->setEnabled(itemCount.obsoleteCount > 0); m_findPartInSketchAct->setEnabled(m_currentGraphicsView != NULL); m_regeneratePartsDatabaseAct->setEnabled(true); m_openProgramWindowAct->setEnabled(true); } void MainWindow::updateTransformationActions() { // update buttons in sketch toolbar at bottom if (m_currentGraphicsView == NULL) return; if (m_rotate90cwAct == NULL) return; ItemCount itemCount = m_currentGraphicsView->calcItemCount(); bool enable = (itemCount.selRotatable > 0); bool renable = (itemCount.sel45Rotatable > 0); //DebugDialog::debug(QString("enable rotate (1) %1").arg(enable)); m_rotate90cwAct->setEnabled(enable); m_rotate180Act->setEnabled(enable); m_rotate90ccwAct->setEnabled(enable); m_rotate45ccwAct->setEnabled(enable && renable); m_rotate45cwAct->setEnabled(enable && renable); foreach(SketchToolButton* rotateButton, m_rotateButtons) { rotateButton->setEnabled(enable); } m_flipHorizontalAct->setEnabled((itemCount.selHFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView)); m_flipVerticalAct->setEnabled((itemCount.selVFlipable > 0) && (m_currentGraphicsView != m_pcbGraphicsView)); enable = m_flipHorizontalAct->isEnabled() || m_flipVerticalAct->isEnabled(); foreach(SketchToolButton* flipButton, m_flipButtons) { flipButton->setEnabled(enable); } } void MainWindow::updateItemMenu() { if (m_currentGraphicsView == NULL) return; ConnectorItem * activeConnectorItem = m_activeConnectorItem; m_activeConnectorItem = NULL; QList items = m_currentGraphicsView->scene()->selectedItems(); int selCount = 0; ItemBase * itemBase = NULL; foreach(QGraphicsItem * item, items) { ItemBase * ib = ItemBase::extractTopLevelItemBase(item); if (ib == NULL) continue; selCount++; if (selCount == 1) itemBase = ib; else if (selCount > 1) break; } PaletteItem *selected = qobject_cast(itemBase); bool enabled = (selCount == 1) && (selected != NULL); m_saveBundledPart->setEnabled(enabled && !selected->modelPart()->isCore()); // can't open wire in parts editor enabled &= selected != NULL && itemBase != NULL && itemBase->canEditPart(); m_disconnectAllAct->setEnabled(enabled && m_currentGraphicsView->canDisconnectAll() && (itemBase->rightClickedConnector() != NULL)); bool gfsEnabled = false; if (activeConnectorItem) { if (activeConnectorItem->attachedToItemType() != ModelPart::CopperFill) { gfsEnabled = true; m_setOneGroundFillSeedAct->setChecked(activeConnectorItem->isGroundFillSeed()); } } m_setOneGroundFillSeedAct->setEnabled(gfsEnabled); m_setOneGroundFillSeedAct->setConnectorItem(activeConnectorItem); } void MainWindow::updateEditMenu() { if (m_currentGraphicsView == NULL) { foreach (QAction * action, m_editMenu->actions()) { action->setEnabled(action == m_preferencesAct); } return; } foreach (QAction * action, m_editMenu->actions()) { action->setEnabled(true); } QClipboard *clipboard = QApplication::clipboard(); m_pasteAct->setEnabled(false); m_pasteInPlaceAct->setEnabled(false); if (clipboard != NULL) { const QMimeData *mimeData = clipboard->mimeData(QClipboard::Clipboard); if (mimeData != NULL) { if (mimeData->hasFormat("application/x-dnditemsdata")) { m_pasteAct->setEnabled(true); m_pasteInPlaceAct->setEnabled(true); //DebugDialog::debug(QString("paste enabled: true")); } } } const QList items = m_currentGraphicsView->scene()->selectedItems(); bool copyActsEnabled = false; bool deleteActsEnabled = false; foreach (QGraphicsItem * item, items) { if (m_currentGraphicsView->canDeleteItem(item, items.count())) { deleteActsEnabled = true; } if (m_currentGraphicsView->canCopyItem(item, items.count())) { copyActsEnabled = true; } } //DebugDialog::debug(QString("enable cut/copy/duplicate/delete %1 %2 %3").arg(copyActsEnabled).arg(deleteActsEnabled).arg(m_currentWidget->viewID()) ); m_deleteAct->setEnabled(deleteActsEnabled); m_deleteMinusAct->setEnabled(deleteActsEnabled); m_deleteAct->setText(tr("Delete")); m_cutAct->setEnabled(deleteActsEnabled && copyActsEnabled); m_copyAct->setEnabled(copyActsEnabled); m_duplicateAct->setEnabled(copyActsEnabled); } void MainWindow::updateTraceMenu() { if (m_pcbTraceMenu == NULL) return; bool tEnabled = false; bool twEnabled = false; bool ctlEnabled = false; bool arEnabled = false; TraceMenuThing traceMenuThing; if (m_currentGraphicsView != NULL) { QList items = m_currentGraphicsView->scene()->items(); foreach (QGraphicsItem * item, items) { Wire * wire = dynamic_cast(item); if (wire == NULL) { if (m_currentGraphicsView == m_pcbGraphicsView) { updatePCBTraceMenu(item, traceMenuThing); } continue; } if (!wire->isEverVisible()) continue; if (wire->getRatsnest()) { //rEnabled = true; //if (wire->isSelected()) { //ctEnabled = true; //} } else if (wire->isTraceType(m_currentGraphicsView->getTraceFlag())) { arEnabled = true; tEnabled = true; twEnabled = true; if (wire->isSelected()) { traceMenuThing.exEnabled = true; if (wire->getAutoroutable()) { traceMenuThing.exChecked = false; } } if (m_currentGraphicsView == m_pcbGraphicsView && m_currentGraphicsView->boardLayers() > 1) { if (wire->canSwitchLayers()) { ctlEnabled = true; } } } } } if (!arEnabled) { if (m_currentGraphicsView != NULL) { arEnabled = m_currentGraphicsView->hasAnyNets(); } } bool anyOrNo = (traceMenuThing.boardCount >= 1 || m_currentGraphicsView != m_pcbGraphicsView); m_excludeFromAutorouteAct->setEnabled(traceMenuThing.exEnabled); m_excludeFromAutorouteAct->setChecked(traceMenuThing.exChecked); m_changeTraceLayerAct->setEnabled(ctlEnabled); m_orderFabAct->setEnabled(traceMenuThing.boardCount > 0); m_showUnroutedAct->setEnabled(true); m_selectAllTracesAct->setEnabled(tEnabled && anyOrNo); m_selectAllWiresAct->setEnabled(tEnabled && anyOrNo); m_selectAllCopperFillAct->setEnabled(traceMenuThing.gfrEnabled && traceMenuThing.boardCount >= 1); m_selectAllExcludedTracesAct->setEnabled(tEnabled && anyOrNo); m_selectAllIncludedTracesAct->setEnabled(tEnabled && anyOrNo); m_selectAllJumperItemsAct->setEnabled(traceMenuThing.jiEnabled && traceMenuThing.boardCount >= 1); m_selectAllViasAct->setEnabled(traceMenuThing.viaEnabled && traceMenuThing.boardCount >= 1); m_tidyWiresAct->setEnabled(twEnabled); QString sides; if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0) && m_pcbGraphicsView->layerIsActive(ViewLayer::Copper1)) { sides = tr("top and bottom"); } else if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0)) { sides = tr("bottom"); } else sides = tr("top"); QString groundFillString = tr("Ground Fill (%1)").arg(sides); QString copperFillString = tr("Copper Fill (%1)").arg(sides); m_groundFillAct->setEnabled(traceMenuThing.boardCount >= 1); m_groundFillAct->setText(groundFillString); m_copperFillAct->setEnabled(traceMenuThing.boardCount >= 1); m_copperFillAct->setText(copperFillString); m_removeGroundFillAct->setEnabled(traceMenuThing.gfrEnabled && traceMenuThing.boardCount >= 1); // TODO: set and clear enabler logic m_setGroundFillSeedsAct->setEnabled(traceMenuThing.gfsEnabled && traceMenuThing.boardCount >= 1); m_clearGroundFillSeedsAct->setEnabled(traceMenuThing.gfsEnabled && traceMenuThing.boardCount >= 1); m_newDesignRulesCheckAct->setEnabled(traceMenuThing.boardCount >= 1); m_checkLoadedTracesAct->setEnabled(true); m_autorouterSettingsAct->setEnabled(m_currentGraphicsView == m_pcbGraphicsView); m_updateRoutingStatusAct->setEnabled(true); m_fabQuoteAct->setEnabled(m_currentGraphicsView == m_pcbGraphicsView); } void MainWindow::updatePCBTraceMenu(QGraphicsItem * item, TraceMenuThing & traceMenuThing) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) return; if (!itemBase->isEverVisible()) return; if (!traceMenuThing.gfsEnabled) { traceMenuThing.gfsEnabled = itemBase->itemType() != ModelPart::CopperFill && itemBase->hasConnectors(); } if (Board::isBoard(itemBase)) { traceMenuThing.boardCount++; if (itemBase->isSelected()) traceMenuThing.boardSelectedCount++; } switch (itemBase->itemType()) { case ModelPart::Jumper: traceMenuThing.jiEnabled = true; if (itemBase->isSelected()) { traceMenuThing.exEnabled = true; if (qobject_cast(itemBase->layerKinChief())->getAutoroutable()) { traceMenuThing.exChecked = false; } } break; case ModelPart::Via: traceMenuThing.viaEnabled = true; if (itemBase->isSelected()) { traceMenuThing.exEnabled = true; if (qobject_cast(itemBase->layerKinChief())->getAutoroutable()) { traceMenuThing.exChecked = false; } } break; case ModelPart::CopperFill: traceMenuThing.gfrEnabled = true; default: break; } } void MainWindow::zoomIn() { m_zoomSlider->zoomIn(); } void MainWindow::zoomOut() { m_zoomSlider->zoomOut(); } void MainWindow::fitInWindow() { if (m_currentGraphicsView == NULL) return; double newZoom = m_currentGraphicsView->fitInWindow(); m_zoomSlider->setValue(newZoom); } void MainWindow::hundredPercentSize() { m_currentGraphicsView->absoluteZoom(100); m_zoomSlider->setValue(100); } void MainWindow::actualSize() { QMessageBox::information(this, tr("Actual Size"), tr("It doesn't seem to be possible to automatically determine the actual physical size of the monitor, so " "'actual size' as currently implemented is only a guess. " "Your best bet would be to drag out a ruler part, then place a real (physical) ruler on top and zoom until they match up." )); int dpi = this->physicalDpiX(); int l = this->logicalDpiX(); DebugDialog::debug(QString("actual size %1 %2").arg(dpi).arg(l)); // remember the parameter to the next two functions is a percent m_currentGraphicsView->absoluteZoom(dpi * 100.0 / GraphicsUtils::SVGDPI); m_zoomSlider->setValue(dpi * 100.0 / GraphicsUtils::SVGDPI); } void MainWindow::showWelcomeView() { setCurrentTabIndex(0); } void MainWindow::showBreadboardView() { int ix = (m_welcomeView == NULL) ? 0 : 1; setCurrentTabIndex(ix); } void MainWindow::showSchematicView() { int ix = (m_welcomeView == NULL) ? 1 : 2; setCurrentTabIndex(ix); } void MainWindow::showPCBView() { int ix = (m_welcomeView == NULL) ? 2 : 3; setCurrentTabIndex(ix); } void MainWindow::showProgramView() { int ix = (m_welcomeView == NULL) ? 3 : 4; setCurrentTabIndex(ix); } void MainWindow::setCurrentView(ViewLayer::ViewID viewID) { if (viewID == ViewLayer::BreadboardView) showBreadboardView(); else if (viewID == ViewLayer::SchematicView) showSchematicView(); else if (viewID == ViewLayer::PCBView) showPCBView(); } void MainWindow::showPartsBinIconView() { if (m_binManager) m_binManager->toIconView(); } void MainWindow::showPartsBinListView() { if (m_binManager) m_binManager->toListView(); } void MainWindow::openHelp() { QDesktopServices::openUrl(QString("http://fritzing.org/learning/")); } void MainWindow::openDonate() { QDesktopServices::openUrl(QString("http://fritzing.org/shop/donations/")); } void MainWindow::openExamples() { QDesktopServices::openUrl(QString("http://fritzing.org/projects/")); } void MainWindow::openPartsReference() { QDesktopServices::openUrl(QString("http://fritzing.org/parts/")); } void MainWindow::visitFritzingDotOrg() { QDesktopServices::openUrl(QString("http://www.fritzing.org")); } void MainWindow::reportBug() { QDesktopServices::openUrl(QString("https://github.com/fritzing/fritzing-app/issues")); } void MainWindow::partsEditorHelp() { QDir dir = FolderUtils::getApplicationSubFolder("help"); QString path = dir.absoluteFilePath("parts_editor_help.html"); if (QFileInfo(path).exists()) { QDesktopServices::openUrl(QString("file:///%1").arg(path)); } } void MainWindow::enableDebug() { DebugDialog::setEnabled(m_enableDebugAct->isChecked()); if (!m_windowMenu->actions().contains(m_toggleDebuggerOutputAct)) { m_windowMenu->insertSeparator(m_windowMenuSeparator); m_windowMenu->insertAction(m_windowMenuSeparator, m_toggleDebuggerOutputAct); toggleDebuggerOutput(true); } } void MainWindow::openNewPartsEditor(PaletteItem * paletteItem) { foreach (QWidget *widget, QApplication::topLevelWidgets()) { PEMainWindow * peMainWindow = qobject_cast(widget); if (peMainWindow == NULL) continue; if (peMainWindow->editsModuleID(paletteItem->moduleID())) { if (peMainWindow->isMinimized()) peMainWindow->showNormal(); else peMainWindow->show(); peMainWindow->raise(); return; } } PEMainWindow * peMainWindow = new PEMainWindow(m_referenceModel, NULL); peMainWindow->init(m_referenceModel, false); if (peMainWindow->setInitialItem(paletteItem)) { peMainWindow->show(); peMainWindow->raise(); connect(peMainWindow, SIGNAL(addToMyPartsSignal(ModelPart *)), this, SLOT(addToMyParts(ModelPart *))); } else { delete peMainWindow; } } void MainWindow::getPartsEditorNewAnd(ItemBase * fromItem) { PaletteItem * paletteItem = qobject_cast(fromItem); if (paletteItem == NULL) return; openNewPartsEditor(paletteItem); } void MainWindow::openInPartsEditorNew() { if (m_currentGraphicsView == NULL) return; PaletteItem *selectedPart = m_currentGraphicsView->getSelectedPart(); openNewPartsEditor(selectedPart); } void MainWindow::createNewSketch() { MainWindow* mw = newMainWindow(m_referenceModel, "", true, true, -1); mw->move(x()+CascadeFactorX,y()+CascadeFactorY); ProcessEventBlocker::processEvents(); mw->addDefaultParts(); mw->show(); mw->hideTempPartsBin(); QSettings settings; settings.remove("lastOpenSketch"); mw->clearFileProgressDialog(); } void MainWindow::minimize() { this->showMinimized(); } void MainWindow::toggleToolbar(bool toggle) { Q_UNUSED(toggle); /*if(toggle) { this->m_fileToolBar->show(); this->m_editToolBar->show(); } else { this->m_fileToolBar->hide(); this->m_editToolBar->hide(); }*/ } void MainWindow::togglePartLibrary(bool toggle) { if(toggle) { m_binManager->show(); } else { m_binManager->hide(); } } void MainWindow::toggleInfo(bool toggle) { if(toggle) { ((QDockWidget*)m_infoView->parent())->show(); } else { ((QDockWidget*)m_infoView->parent())->hide(); } } void MainWindow::toggleUndoHistory(bool toggle) { if(toggle) { ((QDockWidget*)m_undoView->parent())->show(); } else { ((QDockWidget*)m_undoView->parent())->hide(); } } void MainWindow::toggleDebuggerOutput(bool toggle) { if (toggle) { DebugDialog::showDebug(); } else { } } void MainWindow::updateWindowMenu() { m_toggleDebuggerOutputAct->setChecked(DebugDialog::visible()); foreach (QWidget * widget, QApplication::topLevelWidgets()) { MainWindow * mainWindow = qobject_cast(widget); if (mainWindow == NULL) continue; QAction *action = mainWindow->raiseWindowAction(); if (action != NULL) { action->setChecked(action == m_raiseWindowAct); m_windowMenu->addAction(action); } } } void MainWindow::pageSetup() { notYetImplemented(tr("Page Setup")); } void MainWindow::notYetImplemented(QString action) { QMessageBox::warning(this, tr("Fritzing"), tr("Sorry, \"%1\" has not been implemented yet").arg(action)); } void MainWindow::rotateIncCW() { if (m_currentGraphicsView == NULL) return; if (m_rotate45cwAct->isEnabled()) { rotate45cw(); } else if (m_rotate90cwAct->isEnabled()) { rotate90cw(); } } void MainWindow::rotateIncCWRubberBand() { if (m_currentGraphicsView == NULL) return; if (m_rotate45cwAct->isEnabled()) { m_currentGraphicsView->rotateX(45, true, NULL); } else if (m_rotate90cwAct->isEnabled()) { m_currentGraphicsView->rotateX(90, true, NULL); } } void MainWindow::rotateIncCCW() { if (m_currentGraphicsView == NULL) return; if (m_rotate45ccwAct->isEnabled()) { m_currentGraphicsView->rotateX(315, true, NULL); } else if (m_rotate90ccwAct->isEnabled()) { m_currentGraphicsView->rotateX(270, true, NULL); } } void MainWindow::rotateIncCCWRubberBand() { if (m_currentGraphicsView == NULL) return; if (m_rotate45ccwAct->isEnabled()) { m_currentGraphicsView->rotateX(315, true, NULL); } else if (m_rotate90ccwAct->isEnabled()) { m_currentGraphicsView->rotateX(270, true, NULL); } } void MainWindow::rotate90cw() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->rotateX(90, false, NULL); } void MainWindow::rotate90ccw() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->rotateX(270, false, NULL); } void MainWindow::rotate45ccw() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->rotateX(315, false, NULL); } void MainWindow::rotate45cw() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->rotateX(45, false, NULL); } void MainWindow::rotate180() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->rotateX(180, false, NULL); } void MainWindow::flipHorizontal() { m_currentGraphicsView->flipX(Qt::Horizontal, false); } void MainWindow::flipVertical() { m_currentGraphicsView->flipX(Qt::Vertical, false); } void MainWindow::sendToBack() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->sendToBack(); } void MainWindow::sendBackward() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->sendBackward(); } void MainWindow::bringForward() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->bringForward(); } void MainWindow::bringToFront() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->bringToFront(); } void MainWindow::alignLeft() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignItems(Qt::AlignLeft); } void MainWindow::alignVerticalCenter() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignItems(Qt::AlignVCenter); } void MainWindow::alignRight() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignItems(Qt::AlignRight); } void MainWindow::alignTop() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignItems(Qt::AlignTop); } void MainWindow::alignHorizontalCenter() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignItems(Qt::AlignHCenter); } void MainWindow::alignBottom() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignItems(Qt::AlignBottom); } void MainWindow::showAllLayers() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->setAllLayersVisible(true); updateLayerMenu(); } void MainWindow::hideAllLayers() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->setAllLayersVisible(false); updateLayerMenu(); } void MainWindow::openURL() { QAction *action = qobject_cast(sender()); if (action == NULL) return; QString href = action->data().toString(); if (href.isEmpty()) return; QDesktopServices::openUrl(href); } void MainWindow::openRecentOrExampleFile() { QAction *action = qobject_cast(sender()); if (action) { openRecentOrExampleFile(action->data().toString(), action->text()); } } void MainWindow::openRecentOrExampleFile(const QString & filename, const QString & actionText) { if (alreadyOpen(filename)) { return; } if (!QFileInfo(filename).exists()) { QMessageBox::warning(NULL, tr("Fritzing"), tr("File '%1' not found").arg(filename)); return; } MainWindow* mw = newMainWindow(m_referenceModel, filename, true, true, -1); bool readOnly = m_openExampleActions.contains(actionText); mw->setReadOnly(readOnly); mw->loadWhich(filename, !readOnly,!readOnly,!readOnly, ""); mw->clearFileProgressDialog(); closeIfEmptySketch(mw); } void MainWindow::removeActionsStartingAt(QMenu * menu, int start) { QList actions = menu->actions(); if(start == 0) { menu->clear(); } else { for(int i=start; i < actions.size(); i++) { menu->removeAction(actions.at(i)); } } } void MainWindow::hideShowProgramMenu() { if (m_currentWidget == NULL) return; bool show = m_programView == NULL || m_currentWidget->contentView() != m_programView; //if (m_fileMenu) m_fileMenu->menuAction()->setVisible(show); if (m_viewMenu) { m_viewMenu->menuAction()->setVisible(show); m_viewMenu->setEnabled(show); } if (m_partMenu) { m_partMenu->menuAction()->setVisible(show); m_partMenu->setEnabled(show); } if (m_editMenu) { m_editMenu->menuAction()->setVisible(show); m_editMenu->setEnabled(show); m_copyAct->setEnabled(show); m_cutAct->setEnabled(show); m_selectAllAct->setEnabled(show); m_undoAct->setEnabled(show); m_redoAct->setEnabled(show); } if (m_programView) m_programView->showMenus(!show); } void MainWindow::hideShowTraceMenu() { if (m_pcbTraceMenu) m_pcbTraceMenu->menuAction()->setVisible(m_currentGraphicsView == m_pcbGraphicsView); if (m_schematicTraceMenu) m_schematicTraceMenu->menuAction()->setVisible(m_currentGraphicsView == m_schematicGraphicsView); if (m_breadboardTraceMenu) m_breadboardTraceMenu->menuAction()->setVisible(m_currentGraphicsView == m_breadboardGraphicsView); } void MainWindow::createTraceMenuActions() { m_newAutorouteAct = new QAction(tr("Autoroute"), this); m_newAutorouteAct->setStatusTip(tr("Autoroute connections...")); m_newAutorouteAct->setShortcut(tr("Shift+Ctrl+A")); connect(m_newAutorouteAct, SIGNAL(triggered()), this, SLOT(newAutoroute())); createOrderFabAct(); createActiveLayerActions(); QAction * traceAct = new QAction(tr("&Create trace from ratsnest"), this); traceAct->setStatusTip(tr("Create a trace from the ratsnest line")); m_createTraceWireAct = new WireAction(traceAct); connect(m_createTraceWireAct, SIGNAL(triggered()), this, SLOT(createTrace())); traceAct = new QAction(tr("&Create wire from ratsnest"), this); traceAct->setStatusTip(tr("Create a wire from the ratsnest line")); m_createWireWireAct = new WireAction(traceAct); connect(m_createWireWireAct, SIGNAL(triggered()), this, SLOT(createTrace())); m_excludeFromAutorouteAct = new QAction(tr("Do not autoroute"), this); m_excludeFromAutorouteAct->setStatusTip(tr("When autorouting, do not rip up this trace wire, via, or jumper item")); connect(m_excludeFromAutorouteAct, SIGNAL(triggered()), this, SLOT(excludeFromAutoroute())); m_excludeFromAutorouteAct->setCheckable(true); m_excludeFromAutorouteWireAct = new WireAction(m_excludeFromAutorouteAct); connect(m_excludeFromAutorouteWireAct, SIGNAL(triggered()), this, SLOT(excludeFromAutoroute())); m_changeTraceLayerAct = new QAction(tr("Move to other side of the board"), this); m_changeTraceLayerAct->setStatusTip(tr("Move selected traces to the other side of the board (note: the 'first' trace will be moved and the rest will follow to the same side)")); connect(m_changeTraceLayerAct, SIGNAL(triggered()), this, SLOT(changeTraceLayer())); m_changeTraceLayerWireAct = new WireAction(m_changeTraceLayerAct); connect(m_changeTraceLayerWireAct, SIGNAL(triggered()), this, SLOT(changeTraceLayer())); m_showUnroutedAct = new QAction(tr("Show unrouted"), this); m_showUnroutedAct->setStatusTip(tr("Highlight all unrouted connectors")); connect(m_showUnroutedAct, SIGNAL(triggered()), this, SLOT(showUnrouted())); m_selectAllTracesAct = new QAction(tr("Select All Traces"), this); m_selectAllTracesAct->setStatusTip(tr("Select all trace wires")); connect(m_selectAllTracesAct, SIGNAL(triggered()), this, SLOT(selectAllTraces())); m_selectAllWiresAct = new QAction(tr("Select All Wires"), this); m_selectAllWiresAct->setStatusTip(tr("Select all wires")); connect(m_selectAllWiresAct, SIGNAL(triggered()), this, SLOT(selectAllTraces())); m_selectAllCopperFillAct = new QAction(tr("Select All CopperFill"), this); m_selectAllCopperFillAct->setStatusTip(tr("Select all copper fill items")); connect(m_selectAllCopperFillAct, SIGNAL(triggered()), this, SLOT(selectAllCopperFill())); m_updateRoutingStatusAct = new QAction(tr("Force Update Routing Status and Ratsnests"), this); m_updateRoutingStatusAct->setStatusTip(tr("Recalculate routing status and ratsnest wires (in case the auto-update isn't working correctly)")); connect(m_updateRoutingStatusAct, SIGNAL(triggered()), this, SLOT(updateRoutingStatus())); m_selectAllExcludedTracesAct = new QAction(tr("Select All \"Don't Autoroute\" Traces"), this); m_selectAllExcludedTracesAct->setStatusTip(tr("Select all trace wires excluded from autorouting")); connect(m_selectAllExcludedTracesAct, SIGNAL(triggered()), this, SLOT(selectAllExcludedTraces())); m_selectAllIncludedTracesAct = new QAction(tr("Select All Autoroutable Traces"), this); m_selectAllIncludedTracesAct->setStatusTip(tr("Select all trace wires that can be changed during autorouting")); connect(m_selectAllIncludedTracesAct, SIGNAL(triggered()), this, SLOT(selectAllIncludedTraces())); m_selectAllJumperItemsAct = new QAction(tr("Select All Jumpers"), this); m_selectAllJumperItemsAct->setStatusTip(tr("Select all jumper item parts")); connect(m_selectAllJumperItemsAct, SIGNAL(triggered()), this, SLOT(selectAllJumperItems())); m_selectAllViasAct = new QAction(tr("Select All Vias"), this); m_selectAllViasAct->setStatusTip(tr("Select all via parts")); connect(m_selectAllViasAct, SIGNAL(triggered()), this, SLOT(selectAllVias())); m_tidyWiresAct = new QAction(tr("Tidy Wires"), this); m_tidyWiresAct->setStatusTip(tr("Tidy selected wires")); connect(m_tidyWiresAct, SIGNAL(triggered()), this, SLOT(tidyWires())); m_groundFillAct = new QAction(tr("Ground Fill"), this); m_groundFillAct->setStatusTip(tr("Fill empty regions of the copper layer--fill will include all traces connected to a GROUND")); connect(m_groundFillAct, SIGNAL(triggered()), this, SLOT(groundFill())); m_copperFillAct = new QAction(tr("Copper Fill"), this); m_copperFillAct->setStatusTip(tr("Fill empty regions of the copper layer--not including traces connected to a GROUND")); connect(m_copperFillAct, SIGNAL(triggered()), this, SLOT(copperFill())); m_removeGroundFillAct = new QAction(tr("Remove Copper Fill"), this); m_removeGroundFillAct->setStatusTip(tr("Remove the copper fill")); connect(m_removeGroundFillAct, SIGNAL(triggered()), this, SLOT(removeGroundFill())); m_setGroundFillSeedsAct = new QAction(tr("Choose Ground Fill Seed(s)..."), this); m_setGroundFillSeedsAct->setStatusTip(tr("Fill empty regions of the copper layer--fill will include all traces connected to the seeds")); connect(m_setGroundFillSeedsAct, SIGNAL(triggered()), this, SLOT(setGroundFillSeeds())); m_setOneGroundFillSeedAct = new ConnectorItemAction(tr("Set Ground Fill Seed"), this); m_setOneGroundFillSeedAct->setStatusTip(tr("Treat this connector and its connections as a 'ground' during ground fill.")); m_setOneGroundFillSeedAct->setCheckable(true); connect(m_setOneGroundFillSeedAct, SIGNAL(triggered()), this, SLOT(setOneGroundFillSeed())); m_clearGroundFillSeedsAct = new ConnectorItemAction(tr("Clear Ground Fill Seeds"), this); m_clearGroundFillSeedsAct->setStatusTip(tr("Clear ground fill seeds--enable copper fill only.")); connect(m_clearGroundFillSeedsAct, SIGNAL(triggered()), this, SLOT(clearGroundFillSeeds())); m_setGroundFillKeepoutAct = new QAction(tr("Set Ground Fill Keepout..."), this); m_setGroundFillKeepoutAct->setStatusTip(tr("Set the minimum distance between ground fill and traces or connectors")); connect(m_setGroundFillKeepoutAct, SIGNAL(triggered()), this, SLOT(setGroundFillKeepout())); m_newDesignRulesCheckAct = new QAction(tr("Design Rules Check (DRC)"), this); m_newDesignRulesCheckAct->setStatusTip(tr("Highlights any parts that are too close together for safe board production")); m_newDesignRulesCheckAct->setShortcut(tr("Shift+Ctrl+D")); connect(m_newDesignRulesCheckAct, SIGNAL(triggered()), this, SLOT(newDesignRulesCheck())); m_checkLoadedTracesAct = new QAction(tr("Check Loaded Traces"), this); m_checkLoadedTracesAct->setStatusTip(tr("Select any traces where the screen location doesn't match the actual location. Only needed for sketches autorouted with version 0.7.10 or earlier")); connect(m_checkLoadedTracesAct, SIGNAL(triggered()), this, SLOT(checkLoadedTraces())); m_autorouterSettingsAct = new QAction(tr("Autorouter/DRC settings..."), this); m_autorouterSettingsAct->setStatusTip(tr("Set autorouting parameters including keepout...")); connect(m_autorouterSettingsAct, SIGNAL(triggered()), this, SLOT(autorouterSettings())); m_fabQuoteAct = new QAction(tr("Fritzing Fab Quote..."), this); m_fabQuoteAct->setStatusTip(tr("How much would it could to produce a PCB from this sketch with Fritzing Fab")); connect(m_fabQuoteAct, SIGNAL(triggered()), this, SLOT(fabQuote())); } void MainWindow::createActiveLayerActions() { m_viewFromBelowToggleAct = new QAction(tr("View from below"), this); m_viewFromBelowToggleAct->setStatusTip(tr("View the PCB from the bottom layers upwards")); m_viewFromBelowToggleAct->setCheckable(true); m_viewFromBelowToggleAct->setChecked(false); connect(m_viewFromBelowToggleAct, SIGNAL(triggered()), this, SLOT(setViewFromBelowToggle())); m_viewFromBelowAct = new QAction(tr("View from below"), this); m_viewFromBelowAct->setStatusTip(tr("View the PCB from the bottom layers upwards")); m_viewFromBelowAct->setCheckable(true); connect(m_viewFromBelowAct, SIGNAL(triggered()), this, SLOT(setViewFromBelow())); m_viewFromAboveAct = new QAction(tr("View from above"), this); m_viewFromAboveAct->setStatusTip(tr("View the PCB from the top layers downwards")); m_viewFromAboveAct->setCheckable(true); connect(m_viewFromAboveAct, SIGNAL(triggered()), this, SLOT(setViewFromAbove())); m_activeLayerBothAct = new QAction(tr("Set both copper layers clickable"), this); m_activeLayerBothAct->setStatusTip(tr("Set both copper layers clickable")); m_activeLayerBothAct->setShortcut(tr("Shift+Ctrl+3")); m_activeLayerBothAct->setCheckable(true); connect(m_activeLayerBothAct, SIGNAL(triggered()), this, SLOT(activeLayerBoth())); m_activeLayerTopAct = new QAction(tr("Set copper top layer clickable"), this); m_activeLayerTopAct->setStatusTip(tr("Set copper top layer clickable")); m_activeLayerTopAct->setShortcut(tr("Shift+Ctrl+2")); m_activeLayerTopAct->setCheckable(true); connect(m_activeLayerTopAct, SIGNAL(triggered()), this, SLOT(activeLayerTop())); m_activeLayerBottomAct = new QAction(tr("Set copper bottom layer clickable"), this); m_activeLayerBottomAct->setStatusTip(tr("Set copper bottom layer clickable")); m_activeLayerBottomAct->setShortcut(tr("Shift+Ctrl+1")); m_activeLayerBottomAct->setCheckable(true); connect(m_activeLayerBottomAct, SIGNAL(triggered()), this, SLOT(activeLayerBottom())); } void MainWindow::activeLayerBoth() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; pcbSketchWidget->setLayerActive(ViewLayer::Copper1, true); pcbSketchWidget->setLayerActive(ViewLayer::Copper0, true); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, true); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, true); AutoCloseMessageBox::showMessage(this, tr("Copper Top and Copper Bottom layers are both active")); updateActiveLayerButtons(); } void MainWindow::activeLayerTop() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; pcbSketchWidget->setLayerActive(ViewLayer::Copper1, true); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, true); pcbSketchWidget->setLayerActive(ViewLayer::Copper0, false); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, false); AutoCloseMessageBox::showMessage(this, tr("Copper Top layer is active")); updateActiveLayerButtons(); } void MainWindow::activeLayerBottom() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; pcbSketchWidget->setLayerActive(ViewLayer::Copper1, false); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, false); pcbSketchWidget->setLayerActive(ViewLayer::Copper0, true); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, true); AutoCloseMessageBox::showMessage(this, tr("Copper Bottom layer is active")); updateActiveLayerButtons(); } void MainWindow::toggleActiveLayer() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; int index = activeLayerIndex(); switch (index) { case 0: activeLayerBottom(); return; case 1: activeLayerTop(); return; case 2: activeLayerBoth(); return; default: return; } } void MainWindow::createOrderFabAct() { if (m_orderFabAct != NULL) return; m_orderFabAct = new QAction(tr("Order a PCB..."), this); m_orderFabAct->setStatusTip(tr("Order a PCB created from your sketch--from fabulous Fritzing Fab")); connect(m_orderFabAct, SIGNAL(triggered()), this, SLOT(orderFab())); } void MainWindow::newAutoroute() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; ItemBase * board = NULL; if (pcbSketchWidget->autorouteTypePCB()) { int boardCount; board = pcbSketchWidget->findSelectedBoard(boardCount); if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use the autorouter.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please select the board you want to autoroute. The autorouter can only handle one board at a time.")); return; } } dynamic_cast(pcbSketchWidget->parent())->routingStatusLabel()->setText(tr("Autorouting...")); bool copper0Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper0); bool copper1Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper1); AutorouteProgressDialog progress(tr("Autorouting Progress..."), true, true, true, true, pcbSketchWidget, this); progress.setModal(true); progress.show(); QRect pr = progress.frameGeometry(); QRect wr = this->frameGeometry(); progress.move(wr.right() - pr.width(), pr.top()); pcbSketchWidget->scene()->clearSelection(); pcbSketchWidget->setIgnoreSelectionChangeEvents(true); Autorouter * autorouter = NULL; autorouter = new MazeRouter(pcbSketchWidget, board, true); connect(autorouter, SIGNAL(wantTopVisible()), this, SLOT(activeLayerTop()), Qt::DirectConnection); connect(autorouter, SIGNAL(wantBottomVisible()), this, SLOT(activeLayerBottom()), Qt::DirectConnection); connect(autorouter, SIGNAL(wantBothVisible()), this, SLOT(activeLayerBoth()), Qt::DirectConnection); connect(&progress, SIGNAL(cancel()), autorouter, SLOT(cancel()), Qt::DirectConnection); connect(&progress, SIGNAL(skip()), autorouter, SLOT(cancelTrace()), Qt::DirectConnection); connect(&progress, SIGNAL(stop()), autorouter, SLOT(stopTracing()), Qt::DirectConnection); connect(&progress, SIGNAL(best()), autorouter, SLOT(useBest()), Qt::DirectConnection); connect(&progress, SIGNAL(spinChange(int)), autorouter, SLOT(setMaxCycles(int)), Qt::DirectConnection); connect(autorouter, SIGNAL(setMaximumProgress(int)), &progress, SLOT(setMaximum(int)), Qt::DirectConnection); connect(autorouter, SIGNAL(setProgressValue(int)), &progress, SLOT(setValue(int)), Qt::DirectConnection); connect(autorouter, SIGNAL(setProgressMessage(const QString &)), &progress, SLOT(setMessage(const QString &))); connect(autorouter, SIGNAL(setProgressMessage2(const QString &)), &progress, SLOT(setMessage2(const QString &))); connect(autorouter, SIGNAL(setCycleMessage(const QString &)), &progress, SLOT(setSpinLabel(const QString &))); connect(autorouter, SIGNAL(setCycleCount(int)), &progress, SLOT(setSpinValue(int))); connect(autorouter, SIGNAL(disableButtons()), &progress, SLOT(disableButtons())); ProcessEventBlocker::processEvents(); ProcessEventBlocker::block(); autorouter->start(); pcbSketchWidget->setIgnoreSelectionChangeEvents(false); delete autorouter; pcbSketchWidget->setLayerActive(ViewLayer::Copper1, copper1Active); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, copper1Active); pcbSketchWidget->setLayerActive(ViewLayer::Copper0, copper0Active); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, copper0Active); updateActiveLayerButtons(); ProcessEventBlocker::unblock(); } void MainWindow::createTrace() { m_currentGraphicsView->createTrace(retrieveWire(), true); } void MainWindow::excludeFromAutoroute() { Wire * wire = retrieveWire(); PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; pcbSketchWidget->excludeFromAutoroute(wire == NULL ? m_excludeFromAutorouteAct->isChecked() : m_excludeFromAutorouteWireAct->isChecked()); } void MainWindow::selectAllTraces() { m_currentGraphicsView->selectAllWires(m_currentGraphicsView->getTraceFlag()); } void MainWindow::updateRoutingStatus() { RoutingStatus routingStatus; routingStatus.zero(); m_currentGraphicsView->updateRoutingStatus(NULL, routingStatus, true); } void MainWindow::selectAllExcludedTraces() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; pcbSketchWidget->selectAllExcludedTraces(); } void MainWindow::selectAllIncludedTraces() { PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return; pcbSketchWidget->selectAllIncludedTraces(); } void MainWindow::selectAllJumperItems() { m_pcbGraphicsView->selectAllItemType(ModelPart::Jumper, tr("jumpers")); } void MainWindow::selectAllCopperFill() { m_pcbGraphicsView->selectAllItemType(ModelPart::CopperFill, tr("copperfill")); } void MainWindow::selectAllVias() { m_pcbGraphicsView->selectAllItemType(ModelPart::Via, tr("vias")); } void MainWindow::notClosableForAWhile() { m_dontClose = true; QTimer::singleShot(500, this, SLOT(ensureClosable())); } void MainWindow::ensureClosable() { m_dontClose = false; } void MainWindow::showPartLabels() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->showPartLabels(m_showPartLabelAct->data().toBool()); } void MainWindow::addNote() { if (m_currentGraphicsView == NULL) return; ViewGeometry vg; vg.setRect(0, 0, Note::initialMinWidth, Note::initialMinHeight); QPointF tl = m_currentGraphicsView->mapToScene(QPoint(0, 0)); QSizeF vpSize = m_currentGraphicsView->viewport()->size(); tl.setX(tl.x() + ((vpSize.width() - Note::initialMinWidth) / 2.0)); tl.setY(tl.y() + ((vpSize.height() - Note::initialMinHeight) / 2.0)); vg.setLoc(tl); QUndoCommand * parentCommand = new QUndoCommand(tr("Add Note")); m_currentGraphicsView->stackSelectionState(false, parentCommand); m_currentGraphicsView->scene()->clearSelection(); new AddItemCommand(m_currentGraphicsView, BaseCommand::SingleView, ModuleIDNames::NoteModuleIDName, m_currentGraphicsView->defaultViewLayerPlacement(NULL), vg, ItemBase::getNextID(), false, -1, parentCommand); m_undoStack->push(parentCommand); } bool MainWindow::alreadyOpen(const QString & fileName) { foreach (QWidget * widget, QApplication::topLevelWidgets()) { MainWindow * mainWindow = qobject_cast(widget); if (mainWindow == NULL) continue; // don't load two copies of the same file if (mainWindow->fileName().compare(fileName) == 0) { mainWindow->raise(); return true; } } return false; } void MainWindow::enableAddBendpointAct(QGraphicsItem * graphicsItem) { m_addBendpointAct->setEnabled(false); m_convertToViaAct->setEnabled(false); m_flattenCurveAct->setEnabled(false); Wire * wire = dynamic_cast(graphicsItem); if (wire == NULL) return; if (wire->getRatsnest()) return; m_flattenCurveAct->setEnabled(wire->isCurved()); BendpointAction * bendpointAction = qobject_cast(m_addBendpointAct); BendpointAction * convertToViaAction = qobject_cast(m_convertToViaAct); FGraphicsScene * scene = qobject_cast(graphicsItem->scene()); if (scene != NULL) { bendpointAction->setLastLocation(scene->lastContextMenuPos()); convertToViaAction->setLastLocation(scene->lastContextMenuPos()); } bool enabled = false; bool ctvEnabled = false; if (m_currentGraphicsView->lastHoverEnterConnectorItem()) { bendpointAction->setText(tr("Remove Bendpoint")); bendpointAction->setLastHoverEnterConnectorItem(m_currentGraphicsView->lastHoverEnterConnectorItem()); bendpointAction->setLastHoverEnterItem(NULL); convertToViaAction->setLastHoverEnterConnectorItem(m_currentGraphicsView->lastHoverEnterConnectorItem()); convertToViaAction->setLastHoverEnterItem(NULL); ctvEnabled = enabled = true; } else if (m_currentGraphicsView->lastHoverEnterItem()) { bendpointAction->setText(tr("Add Bendpoint")); bendpointAction->setLastHoverEnterItem(m_currentGraphicsView->lastHoverEnterItem()); bendpointAction->setLastHoverEnterConnectorItem(NULL); convertToViaAction->setLastHoverEnterItem(NULL); convertToViaAction->setLastHoverEnterConnectorItem(NULL); enabled = true; } else { bendpointAction->setLastHoverEnterItem(NULL); bendpointAction->setLastHoverEnterConnectorItem(NULL); convertToViaAction->setLastHoverEnterItem(NULL); convertToViaAction->setLastHoverEnterConnectorItem(NULL); } m_addBendpointAct->setEnabled(enabled); m_convertToViaAct->setEnabled(ctvEnabled && (m_currentGraphicsView == m_pcbGraphicsView)); } void MainWindow::addBendpoint() { BendpointAction * bendpointAction = qobject_cast(m_addBendpointAct); m_currentGraphicsView->addBendpoint(bendpointAction->lastHoverEnterItem(), bendpointAction->lastHoverEnterConnectorItem(), bendpointAction->lastLocation()); } void MainWindow::convertToVia() { BendpointAction * bendpointAction = qobject_cast(m_convertToViaAct); m_pcbGraphicsView->convertToVia(bendpointAction->lastHoverEnterConnectorItem()); } void MainWindow::convertToBendpoint() { m_pcbGraphicsView->convertToBendpoint(); } void MainWindow::flattenCurve() { BendpointAction * bendpointAction = qobject_cast(m_addBendpointAct); m_currentGraphicsView->flattenCurve(bendpointAction->lastHoverEnterItem(), bendpointAction->lastHoverEnterConnectorItem(), bendpointAction->lastLocation()); } void MainWindow::tidyWires() { m_currentGraphicsView->tidyWires(); } void MainWindow::copperFill() { groundFillAux2(false); } void MainWindow::groundFill() { groundFillAux2(true); } void MainWindow::groundFillAux2(bool fillGroundTraces) { if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0) && m_pcbGraphicsView->layerIsActive(ViewLayer::Copper1)) { groundFillAux(fillGroundTraces, ViewLayer::UnknownLayer); } else if (m_pcbGraphicsView->layerIsActive(ViewLayer::Copper0)) { groundFillAux(fillGroundTraces, ViewLayer::GroundPlane0); } else { groundFillAux(fillGroundTraces, ViewLayer::GroundPlane1); } } void MainWindow::copperFill(ViewLayer::ViewLayerID viewLayerID) { groundFillAux(false, viewLayerID); } void MainWindow::groundFill(ViewLayer::ViewLayerID viewLayerID) { groundFillAux(true, viewLayerID); } void MainWindow::groundFillAux(bool fillGroundTraces, ViewLayer::ViewLayerID viewLayerID) { // TODO: // what about leftover temp files from crashes? // clear ground plane when anything changes // some polygons can be combined // remove old ground plane modules from paletteModel and database if (m_pcbGraphicsView == NULL) return; int boardCount; ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount); if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use ground or copper fill.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please select a PCB--copper fill only works for one board at a time.")); return; } FileProgressDialog fileProgress(tr("Generating %1 fill...").arg(fillGroundTraces ? tr("ground") : tr("copper")), 0, this); fileProgress.setIndeterminate(); QUndoCommand * parentCommand = new QUndoCommand(fillGroundTraces ? tr("Ground Fill") : tr("Copper Fill")); m_pcbGraphicsView->blockUI(true); removeGroundFill(viewLayerID, parentCommand); if (m_pcbGraphicsView->groundFill(fillGroundTraces, viewLayerID, parentCommand)) { m_undoStack->push(parentCommand); } else { delete parentCommand; } m_pcbGraphicsView->blockUI(false); } void MainWindow::removeGroundFill() { removeGroundFill(ViewLayer::UnknownLayer, NULL); } void MainWindow::removeGroundFill(ViewLayer::ViewLayerID viewLayerID, QUndoCommand * parentCommand) { QSet toDelete; int boardCount; ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount); if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to remove copper fill.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please select a PCB--ground fill operations only work on a one board at a time.")); return; } foreach (QGraphicsItem * item, m_pcbGraphicsView->scene()->collidingItems(board)) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->moveLock()) continue; if (!isGroundFill(itemBase)) continue; if (viewLayerID != ViewLayer::UnknownLayer) { if (itemBase->viewLayerID() != viewLayerID) continue; } toDelete.insert(itemBase->layerKinChief()); } if (toDelete.count() == 0) return; bool push = (parentCommand == NULL); if (push) { parentCommand = new QUndoCommand(tr("Remove copper fill")); } new CleanUpWiresCommand(m_pcbGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(m_pcbGraphicsView, CleanUpWiresCommand::UndoOnly, parentCommand); m_pcbGraphicsView->deleteMiddle(toDelete, parentCommand); foreach (ItemBase * itemBase, toDelete) { itemBase->saveGeometry(); m_pcbGraphicsView->makeDeleteItemCommand(itemBase, BaseCommand::CrossView, parentCommand); } new CleanUpRatsnestsCommand(m_pcbGraphicsView, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(m_pcbGraphicsView, CleanUpWiresCommand::RedoOnly, parentCommand); if (push) { m_undoStack->push(parentCommand); } else { foreach (ItemBase * itemBase, toDelete) { // move them out of the way because they are about to be deleted anyhow itemBase->setPos(itemBase->pos() + board->sceneBoundingRect().bottomRight() + QPointF(10000, 10000)); } } } bool MainWindow::isGroundFill(ItemBase * itemBase) { return (itemBase->itemType() == ModelPart::CopperFill); } QMenu *MainWindow::breadboardItemMenu() { QMenu *menu = new QMenu(QObject::tr("Part"), this); createRotateSubmenu(menu); return viewItemMenuAux(menu); } QMenu *MainWindow::schematicItemMenu() { QMenu *menu = new QMenu(QObject::tr("Part"), this); createRotateSubmenu(menu); menu->addAction(m_flipHorizontalAct); menu->addAction(m_flipVerticalAct); return viewItemMenuAux(menu); } QMenu *MainWindow::pcbItemMenu() { QMenu *menu = new QMenu(QObject::tr("Part"), this); createRotateSubmenu(menu); menu = viewItemMenuAux(menu); menu->addAction(m_hidePartSilkscreenAct); menu->addSeparator(); menu->addAction(m_convertToBendpointAct); m_convertToBendpointSeparator = menu->addSeparator(); menu->addAction(m_setOneGroundFillSeedAct); menu->addAction(m_clearGroundFillSeedsAct); return menu; } QMenu *MainWindow::breadboardWireMenu() { QMenu *menu = new QMenu(QObject::tr("Wire"), this); // createZOrderWireSubmenu(menu); createZOrderSubmenu(menu); menu->addSeparator(); m_breadboardWireColorMenu = menu->addMenu(tr("&Wire Color")); foreach(QString colorName, Wire::colorNames) { QString colorValue = Wire::colorTrans.value(colorName); QAction * action = new QAction(colorName, this); m_breadboardWireColorMenu->addAction(action); action->setData(colorValue); action->setCheckable(true); action->setChecked(false); connect(action, SIGNAL(triggered(bool)), this, SLOT(changeWireColor(bool))); } menu->addAction(m_createWireWireAct); menu->addSeparator(); menu->addAction(m_deleteWireAct); menu->addAction(m_deleteWireMinusAct); menu->addSeparator(); menu->addAction(m_addBendpointAct); menu->addAction(m_flattenCurveAct); #ifndef QT_NO_DEBUG menu->addSeparator(); menu->addAction(m_infoViewOnHoverAction); #endif connect( menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu())); return menu; } QMenu *MainWindow::pcbWireMenu() { QMenu *menu = new QMenu(QObject::tr("Wire"), this); // createZOrderWireSubmenu(menu); createZOrderSubmenu(menu); menu->addSeparator(); menu->addAction(m_changeTraceLayerWireAct); menu->addAction(m_createTraceWireAct); menu->addAction(m_excludeFromAutorouteWireAct); menu->addSeparator(); menu->addAction(m_deleteWireAct); menu->addAction(m_deleteWireMinusAct); menu->addSeparator(); menu->addAction(m_addBendpointAct); menu->addAction(m_convertToViaAct); menu->addAction(m_flattenCurveAct); #ifndef QT_NO_DEBUG menu->addSeparator(); menu->addAction(m_infoViewOnHoverAction); #endif connect(menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu())); return menu; } QMenu *MainWindow::schematicWireMenu() { QMenu *menu = new QMenu(QObject::tr("Wire"), this); // createZOrderWireSubmenu(menu); createZOrderSubmenu(menu); menu->addSeparator(); m_schematicWireColorMenu = menu->addMenu(tr("&Wire Color")); foreach(QString colorName, Wire::colorNames) { QString colorValue = Wire::colorTrans.value(colorName); if (colorValue == "white") continue; QAction * action = new QAction(colorName, this); m_schematicWireColorMenu->addAction(action); action->setData(colorValue); action->setCheckable(true); connect(action, SIGNAL(triggered(bool)), this, SLOT(changeWireColor(bool))); } menu->addAction(m_createTraceWireAct); menu->addAction(m_excludeFromAutorouteWireAct); menu->addSeparator(); menu->addAction(m_deleteWireAct); menu->addAction(m_deleteWireMinusAct); menu->addSeparator(); menu->addAction(m_addBendpointAct); #ifndef QT_NO_DEBUG menu->addSeparator(); menu->addAction(m_infoViewOnHoverAction); #endif connect( menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu())); return menu; } QMenu *MainWindow::viewItemMenuAux(QMenu* menu) { // createZOrderWireSubmenu(menu); createZOrderSubmenu(menu); menu->addAction(m_moveLockAct); menu->addAction(m_stickyAct); menu->addSeparator(); menu->addAction(m_copyAct); menu->addAction(m_duplicateAct); menu->addAction(m_deleteAct); menu->addAction(m_deleteMinusAct); #ifndef QT_NO_DEBUG menu->addSeparator(); menu->addAction(m_disconnectAllAct); #endif menu->addSeparator(); menu->addAction(m_openInPartsEditorNewAct); createAddToBinSubmenu(menu); menu->addSeparator(); menu->addAction(m_showPartLabelAct); #ifndef QT_NO_DEBUG menu->addSeparator(); menu->addAction(m_infoViewOnHoverAction); menu->addAction(m_exportNormalizedSvgAction); menu->addAction(m_exportNormalizedFlattenedSvgAction); menu->addAction(m_testConnectorsAction); #endif connect( menu, SIGNAL(aboutToShow()), this, SLOT(updatePartMenu()) ); return menu; } void MainWindow::changeWireColor(bool checked) { if (checked == false) { // choosing the same color again (assuming this action can only apply to a single wire at a time) return; } QAction * action = qobject_cast(sender()); if (action == NULL) return; QString colorName = action->data().toString(); if (colorName.isEmpty()) return; m_currentGraphicsView->changeWireColor(colorName); } void MainWindow::startSaveInstancesSlot(const QString & fileName, ModelPart *, QXmlStreamWriter & streamWriter) { Q_UNUSED(fileName); if (m_backingUp) { streamWriter.writeTextElement("originalFileName", m_fwFilename); } if (m_pcbGraphicsView) { QList boards = m_pcbGraphicsView->findBoard(); if (boards.count()) { streamWriter.writeStartElement("boards"); foreach (ItemBase * board, boards) { QRectF r = board->sceneBoundingRect(); double w = 2.54 * r.width() / GraphicsUtils::SVGDPI; double h = 2.54 * r.height() / GraphicsUtils::SVGDPI; streamWriter.writeStartElement("board"); streamWriter.writeAttribute("moduleId", QString("%1").arg(board->moduleID())); streamWriter.writeAttribute("title", QString("%1").arg(board->title())); streamWriter.writeAttribute("instance", QString("%1").arg(board->instanceTitle())); streamWriter.writeAttribute("width", QString("%1cm").arg(w)); streamWriter.writeAttribute("height", QString("%1cm").arg(h)); streamWriter.writeEndElement(); } streamWriter.writeEndElement(); } } if (m_linkedProgramFiles.count() > 0) { streamWriter.writeStartElement("programs"); QSettings settings; streamWriter.writeAttribute("pid", settings.value("pid").toString()); foreach (LinkedFile * linkedFile, m_linkedProgramFiles) { streamWriter.writeStartElement("program"); streamWriter.writeAttribute("language", linkedFile->platform); streamWriter.writeCharacters(linkedFile->linkedFilename); streamWriter.writeEndElement(); } streamWriter.writeEndElement(); } streamWriter.writeStartElement("views"); QList views; views << m_breadboardGraphicsView << m_schematicGraphicsView << m_pcbGraphicsView; foreach (SketchWidget * sketchWidget, views) { streamWriter.writeStartElement("view"); streamWriter.writeAttribute("name", ViewLayer::viewIDXmlName(sketchWidget->viewID())); streamWriter.writeAttribute("backgroundColor", sketchWidget->background().name()); streamWriter.writeAttribute("gridSize", sketchWidget->gridSizeText()); streamWriter.writeAttribute("showGrid", sketchWidget->showingGrid() ? "1" : "0"); streamWriter.writeAttribute("alignToGrid", sketchWidget->alignedToGrid() ? "1" : "0"); streamWriter.writeAttribute("viewFromBelow", sketchWidget->viewFromBelow() ? "1" : "0"); QHash autorouterSettings = sketchWidget->getAutorouterSettings(); foreach (QString key, autorouterSettings.keys()) { streamWriter.writeAttribute(key, autorouterSettings.value(key)); } streamWriter.writeEndElement(); } streamWriter.writeEndElement(); } void MainWindow::obsoleteSMDOrientationSlot() { m_obsoleteSMDOrientation = true; } void MainWindow::oldSchematicsSlot(const QString &filename, bool & useOldSchematics) { useOldSchematics = m_convertedSchematic = m_useOldSchematic = false; if (m_noSchematicConversion) return; if (m_readOnly) { useOldSchematics = m_useOldSchematic = true; return; } QMessageBox::StandardButton answer = oldSchematicMessage(filename); if (answer == QMessageBox::No) { useOldSchematics = m_useOldSchematic = true; this->setReadOnly(true); } else { m_convertedSchematic = true; } } QMessageBox::StandardButton MainWindow::oldSchematicMessage(const QString & filename) { QFileInfo info(filename); FMessageBox messageBox(NULL); messageBox.setWindowTitle(tr("Schematic view update")); messageBox.setText(tr("There is a new graphics standard for schematic-view part images, beginning with version 0.8.6.\n\n") + tr("Would you like to convert '%1' to the new standard now or open the file read-only?\n").arg(info.fileName()) ); messageBox.setInformativeText("

  • " + tr("The conversion process will not modify '%1', until you save the file. ").arg(info.fileName()) + + "
  • " + tr("You will have to rearrange parts and connections in schematic view, as the sizes of most part images will have changed. Consider using the Autorouter to clean up traces. ") + + "
  • " + tr("Note that any custom parts will not be converted. A tool for converting 'rectangular' schematic images is available in the Parts Editor.") + + "
" ); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); messageBox.setDefaultButton(QMessageBox::Yes); messageBox.setIcon(QMessageBox::Question); messageBox.setWindowModality(Qt::WindowModal); messageBox.setButtonText(QMessageBox::Yes, tr("Convert")); messageBox.setButtonText(QMessageBox::No, tr("Read-only")); return (QMessageBox::StandardButton) messageBox.exec(); } void MainWindow::loadedRootSlot(const QString & fname, ModelBase *, QDomElement & root) { if (root.isNull()) return; QDomElement programs = root.firstChildElement("programs"); if (programs.isNull()) return; QString thatPid = programs.attribute("pid"); QSettings settings; QString thisPid = settings.value("pid").toString(); bool sameMachine = thatPid.isEmpty() || (!thisPid.isEmpty() && (thatPid.compare(thisPid) == 0)); QFileInfo fileInfo(fname); QDir dir = fileInfo.absoluteDir(); QDomElement program = programs.firstChildElement("program"); while (!program.isNull()) { bool obsolete = false; bool inBundle = false; QString text; TextUtils::findText(program, text); if (!text.isEmpty()) { QString platform = program.attribute("language"); QString path; if (thatPid.isEmpty()) { // pre 0.7.0 relative path QFileInfo newFileInfo(text); dir.cd(newFileInfo.dir().path()); path = dir.absoluteFilePath(newFileInfo.fileName()); obsolete = true; } else { path = text; } LinkedFile * linkedFile = new LinkedFile; QFileInfo info(path); if (!(sameMachine && info.exists())) { inBundle = true; path = dir.absoluteFilePath(info.fileName()); } linkedFile->linkedFilename = path; linkedFile->platform = platform; linkedFile->fileFlags = LinkedFile::NoFlag; if (sameMachine) linkedFile->fileFlags |= LinkedFile::SameMachineFlag; if (obsolete) linkedFile->fileFlags |= LinkedFile::ObsoleteFlag; if (inBundle) linkedFile->fileFlags |= LinkedFile::InBundleFlag; if (this->m_readOnly) linkedFile->fileFlags |= LinkedFile::ReadOnlyFlag; m_linkedProgramFiles.append(linkedFile); } program = program.nextSiblingElement("program"); } } void MainWindow::loadedViewsSlot(ModelBase *, QDomElement & views) { if (views.isNull()) return; QDomElement view = views.firstChildElement("view"); while (!view.isNull()) { QString name = view.attribute("name"); ViewLayer::ViewID viewID = ViewLayer::idFromXmlName(name); SketchWidget * sketchWidget = NULL; switch (viewID) { case ViewLayer::BreadboardView: sketchWidget = m_breadboardGraphicsView; break; case ViewLayer::SchematicView: sketchWidget = m_schematicGraphicsView; break; case ViewLayer::PCBView: sketchWidget = m_pcbGraphicsView; break; default: break; } if (sketchWidget) { QString colorName = view.attribute("backgroundColor", ""); QString gridSizeText = view.attribute("gridSize", ""); QString alignToGridText = view.attribute("alignToGrid", ""); QString showGridText = view.attribute("showGrid", ""); QString viewFromBelowText = view.attribute("viewFromBelow", ""); QHash autorouterSettings; QDomNamedNodeMap map = view.attributes(); for (int m = 0; m < map.count(); m++) { QDomNode node = map.item(m); autorouterSettings.insert(node.nodeName(), node.nodeValue()); } sketchWidget->setAutorouterSettings(autorouterSettings); QColor color; color.setNamedColor(colorName); bool redraw = false; if (color.isValid()) { sketchWidget->setBackground(color); redraw = true; } if (!alignToGridText.isEmpty()) { sketchWidget->alignToGrid(alignToGridText.compare("1") == 0); } if (!showGridText.isEmpty()) { sketchWidget->showGrid(showGridText.compare("1") == 0); redraw = 1; } if (!gridSizeText.isEmpty()) { sketchWidget->setGridSize(gridSizeText); redraw = true; } if (!viewFromBelowText.isEmpty()) { sketchWidget->setViewFromBelow(viewFromBelowText.compare("1") == 0); redraw = 1; } if (redraw) sketchWidget->invalidateScene(); } view = view.nextSiblingElement("view"); } } void MainWindow::disconnectAll() { m_currentGraphicsView->disconnectAll(); } bool MainWindow::externalProcess(QString & name, QString & path, QStringList & args) { emit externalProcessSignal(name, path, args); if (path.isEmpty()) return false; if (name.isEmpty()) { name = tr("Launch %1...").arg(path); } return true; } void MainWindow::launchExternalProcess() { QString name; QString path; QStringList args; if (!externalProcess(name, path, args)) return; args.append("-sketch"); args.append(fileName()); m_externalProcessOutput.clear(); QFileInfo f = QFileInfo(path); QProcess * process = new QProcess(this); process->setWorkingDirectory(f.dir().absolutePath()); process->setProcessChannelMode(QProcess::MergedChannels); process->setReadChannel(QProcess::StandardOutput); connect(process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus))); connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyRead())); connect(process, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState))); process->start(path, args); } void MainWindow::processError(QProcess::ProcessError processError) { DebugDialog::debug(QString("process error %1").arg(processError)); } void MainWindow::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { DebugDialog::debug(QString("process finished %1 %2").arg(exitCode).arg(exitStatus)); QString name, path; QStringList args; externalProcess(name, path, args); QMessageBox::information(this, name, QString(m_externalProcessOutput)); sender()->deleteLater(); } void MainWindow::processReadyRead() { QByteArray byteArray = qobject_cast(sender())->readAllStandardOutput(); m_externalProcessOutput.append(byteArray); DebugDialog::debug(byteArray.data()); } void MainWindow::processStateChanged(QProcess::ProcessState newState) { switch(newState) { case QProcess::Running: DebugDialog::debug(QString("process running")); break; case QProcess::Starting: DebugDialog::debug(QString("process starting")); break; case QProcess::NotRunning: DebugDialog::debug(QString("process not running")); break; } } void MainWindow::shareOnline() { QDesktopServices::openUrl(QString("http://fritzing.org/projects/create/")); } void MainWindow::selectAllObsolete() { selectAllObsolete(true); } QList MainWindow::selectAllObsolete(bool displayFeedback) { QList items = m_pcbGraphicsView->selectAllObsolete(); if (!displayFeedback) return items; if (items.count() <= 0) { QMessageBox::information(this, tr("Fritzing"), tr("No outdated parts found.\nAll your parts are up-to-date.") ); } else { checkSwapObsolete(items, false); } return items; } void MainWindow::checkSwapObsolete(QList & items, bool includeUpdateLaterMessage) { QString msg = includeUpdateLaterMessage ? tr("\n\nNote: if you want to update later, there are options under the 'Part' menu for dealing with outdated parts individually. ") : ""; QMessageBox::StandardButton answer = FMessageBox::question( this, tr("Outdated parts"), tr("There are %n outdated part(s) in this sketch. ", "", items.count()) + tr("We strongly recommend that you update these %n parts to the latest version. ", "", items.count()) + tr("This may result in changes to your sketch, as parts or connectors may be shifted. ") + msg + tr("\n\nDo you want to update now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ); // TODO: make button texts translatable if (answer == QMessageBox::Yes) { swapObsolete(true, items); } } ModelPart * MainWindow::findReplacedby(ModelPart * originalModelPart) { ModelPart * newModelPart = originalModelPart; while (true) { QString newModuleID = newModelPart->replacedby(); if (newModuleID.isEmpty()) { return ((newModelPart == originalModelPart) ? NULL : newModelPart); } ModelPart * tempModelPart = this->m_referenceModel->retrieveModelPart(newModuleID); if (tempModelPart == NULL) { // something's screwy return NULL; } newModelPart = tempModelPart; } } void MainWindow::swapObsolete() { QList items; swapObsolete(true, items); } void MainWindow::swapObsolete(bool displayFeedback, QList & items) { QSet itemBases; if (items.count() == 0) { foreach (QGraphicsItem * item, m_pcbGraphicsView->scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isObsolete()) continue; itemBase = itemBase->layerKinChief(); itemBases.insert(itemBase); } if (itemBases.count() <= 0) return; } else { foreach (ItemBase * itemBase, items) itemBases.insert(itemBase); } QUndoCommand* parentCommand = new QUndoCommand(); int count = 0; QMap propsMap; foreach (ItemBase * itemBase, itemBases) { ModelPart * newModelPart = findReplacedby(itemBase->modelPart()); if (newModelPart == NULL) { FMessageBox::information( this, tr("Sorry!"), tr( "unable to find replacement for %1.\n").arg(itemBase->title()) ); continue; } count++; long newID = swapSelectedAuxAux(itemBase, newModelPart->moduleID(), itemBase->viewLayerPlacement(), propsMap, parentCommand); if (itemBase->modelPart()) { // special case for swapping old resistors. QString resistance = itemBase->modelPart()->properties().value("resistance", ""); if (!resistance.isEmpty()) { QChar r = resistance.at(resistance.length() - 1); ushort ohm = r.unicode(); if (ohm == 8486) { // ends with the ohm symbol resistance.chop(1); } } QString footprint = itemBase->modelPart()->properties().value("footprint", ""); if (!resistance.isEmpty() && !footprint.isEmpty()) { new SetResistanceCommand(m_currentGraphicsView, newID, resistance, resistance, footprint, footprint, parentCommand); } // special case for swapping LEDs if (newModelPart->moduleID().contains(ModuleIDNames::ColorLEDModuleIDName)) { QString oldColor = itemBase->modelPart()->properties().value("color"); QString newColor; if (oldColor.contains("red", Qt::CaseInsensitive)) { newColor = "Red (633nm)"; } else if (oldColor.contains("blue", Qt::CaseInsensitive)) { newColor = "Blue (430nm)"; } else if (oldColor.contains("yellow", Qt::CaseInsensitive)) { newColor = "Yellow (585nm)"; } else if (oldColor.contains("green", Qt::CaseInsensitive)) { newColor = "Green (555nm)"; } else if (oldColor.contains("white", Qt::CaseInsensitive)) { newColor = "White (4500K)"; } if (newColor.length() > 0) { new SetPropCommand(m_currentGraphicsView, newID, "color", newColor, newColor, true, parentCommand); } } } } if (count == 0) { delete parentCommand; } else { parentCommand->setText(tr("Update %1 part(s)", "").arg(count)); m_undoStack->push(parentCommand); } if (displayFeedback) { QMessageBox::information(this, tr("Fritzing"), tr("Successfully updated %1 part(s).\n" "Please check all views for potential side-effects.").arg(count) ); } DebugDialog::debug(QString("updated %1 obsolete in %2").arg(count).arg(m_fwFilename)); } void MainWindow::throwFakeException() { throw "fake exception"; } void MainWindow::alignToGrid() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->alignToGrid(m_alignToGridAct->isChecked()); setWindowModified(true); } void MainWindow::showGrid() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->showGrid(m_showGridAct->isChecked()); setWindowModified(true); } void MainWindow::setGridSize() { GridSizeThing gridSizeThing(m_currentGraphicsView->viewName(), m_currentGraphicsView->getShortName(), m_currentGraphicsView->defaultGridSizeInches(), m_currentGraphicsView->gridSizeText()); GridSizeDialog dialog(&gridSizeThing); dialog.setWindowTitle(QObject::tr("Set Grid Size")); QVBoxLayout * vLayout = new QVBoxLayout(&dialog); vLayout->addWidget(createGridSizeForm(&gridSizeThing)); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); vLayout->addWidget(buttonBox); int result = dialog.exec(); if (result == QDialog::Accepted) { QString units = (gridSizeThing.inButton->isChecked() ? "in" : "mm"); m_currentGraphicsView->setGridSize(gridSizeThing.lineEdit->text() + units); setWindowModified(true); } } QWidget * MainWindow::createGridSizeForm(GridSizeThing * gridSizeThing) { this->setObjectName("gridSizeDia"); QGroupBox * over = new QGroupBox("", this); QVBoxLayout * vLayout = new QVBoxLayout(); QLabel * explain = new QLabel(tr("Set the grid size for %1.").arg(gridSizeThing->viewName)); vLayout->addWidget(explain); QGroupBox * groupBox = new QGroupBox(this); QHBoxLayout * hLayout = new QHBoxLayout(); QLabel * label = new QLabel(tr("Grid Size:")); hLayout->addWidget(label); gridSizeThing->lineEdit = new QLineEdit(); gridSizeThing->lineEdit->setFixedWidth(55); gridSizeThing->validator = new QDoubleValidator(gridSizeThing->lineEdit); gridSizeThing->validator->setRange(0.001, 1.0, 4); gridSizeThing->validator->setNotation(QDoubleValidator::StandardNotation); gridSizeThing->validator->setLocale(QLocale::C); gridSizeThing->lineEdit->setValidator(gridSizeThing->validator); hLayout->addWidget(gridSizeThing->lineEdit); gridSizeThing->inButton = new QRadioButton(tr("in"), this); hLayout->addWidget(gridSizeThing->inButton); gridSizeThing->mmButton = new QRadioButton(tr("mm"), this); hLayout->addWidget(gridSizeThing->mmButton); groupBox->setLayout(hLayout); vLayout->addWidget(groupBox); vLayout->addSpacing(5); QPushButton * pushButton = new QPushButton(this); pushButton->setText(tr("Restore Default")); pushButton->setMaximumWidth(150); vLayout->addWidget(pushButton); vLayout->addSpacing(10); over->setLayout(vLayout); if (gridSizeThing->gridSizeText.length() <= 2) { gridSizeThing->inButton->setChecked(true); gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->defaultGridSize)); } else { if (gridSizeThing->gridSizeText.endsWith("mm")) { gridSizeThing->mmButton->setChecked(true); gridSizeThing->validator->setTop(25.4); } else { gridSizeThing->inButton->setChecked(true); } QString szString = gridSizeThing->gridSizeText; szString.chop(2); gridSizeThing->lineEdit->setText(szString); } connect(gridSizeThing->inButton, SIGNAL(clicked(bool)), this, SLOT(gridUnits(bool))); connect(pushButton, SIGNAL(clicked()), this, SLOT(restoreDefaultGrid())); connect(gridSizeThing->mmButton, SIGNAL(clicked(bool)), this, SLOT(gridUnits(bool))); return over; } void MainWindow::openProgramWindow() { if (m_programWindow) { m_programWindow->setVisible(true); m_programWindow->raise(); return; } m_programWindow = new ProgramWindow(); connect(m_programWindow, SIGNAL(linkToProgramFile(const QString &, Platform *, bool, bool)), this, SLOT(linkToProgramFile(const QString &, Platform *, bool, bool))); connect(m_programWindow, SIGNAL(changeActivationSignal(bool, QWidget *)), qApp, SLOT(changeActivation(bool, QWidget *)), Qt::DirectConnection); connect(m_programWindow, SIGNAL(destroyed(QObject *)), qApp, SLOT(topLevelWidgetDestroyed(QObject *))); QFileInfo fileInfo(m_fwFilename); m_programWindow->setup(); m_programWindow->linkFiles(m_linkedProgramFiles, fileInfo.absoluteDir().absolutePath()); m_programWindow->setVisible(true); } void MainWindow::linkToProgramFile(const QString & filename, Platform * platform, bool addLink, bool strong) { #ifdef Q_OS_WIN Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; #else Qt::CaseSensitivity sensitivity = Qt::CaseSensitive; #endif if (addLink && strong) { bool gotOne = false; foreach (LinkedFile * linkedFile, m_linkedProgramFiles) { if (linkedFile->linkedFilename.compare(filename, sensitivity) == 0) { if (linkedFile->platform != platform->getName()) { linkedFile->platform = platform->getName(); this->setWindowModified(true); } gotOne = true; break; } } if (!gotOne) { LinkedFile * linkedFile = new LinkedFile; linkedFile->linkedFilename = filename; linkedFile->platform = platform->getName(); m_linkedProgramFiles.append(linkedFile); this->setWindowModified(true); } return; } else { for (int i = 0; i < m_linkedProgramFiles.count(); i++) { LinkedFile * linkedFile = m_linkedProgramFiles.at(i); if (linkedFile->linkedFilename.compare(filename, sensitivity) == 0) { if (strong) { m_linkedProgramFiles.removeAt(i); this->setWindowModified(true); } else { if (linkedFile->platform != platform->getName()) { linkedFile->platform = platform->getName(); this->setWindowModified(true); } } return; } } } } QStringList MainWindow::newDesignRulesCheck() { return newDesignRulesCheck(true); } QStringList MainWindow::newDesignRulesCheck(bool showOkMessage) { QStringList results; if (m_currentGraphicsView == NULL) return results; PCBSketchWidget * pcbSketchWidget = qobject_cast(m_currentGraphicsView); if (pcbSketchWidget == NULL) return results; ItemBase * board = NULL; if (pcbSketchWidget->autorouteTypePCB()) { int boardCount; board = pcbSketchWidget->findSelectedBoard(boardCount); if (boardCount == 0) { QString message = tr("Your sketch does not have a board yet! DRC only works with a PCB."); results << message; FMessageBox::critical(this, tr("Fritzing"), message); return results; } if (board == NULL) { QString message = tr("Please select a PCB. DRC only works on one board at a time."); results << message; FMessageBox::critical(this, tr("Fritzing"), message); return results; } } bool copper0Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper0); bool copper1Active = pcbSketchWidget->layerIsActive(ViewLayer::Copper1); AutorouteProgressDialog progress(tr("DRC Progress..."), true, false, false, false, pcbSketchWidget, this); progress.setModal(true); progress.show(); QRect pr = progress.frameGeometry(); QRect wr = pcbSketchWidget->frameGeometry(); QPoint p = pcbSketchWidget->mapTo(this, wr.topRight()); progress.move(p.x() - pr.width(), pr.top()); DRC drc(pcbSketchWidget, board); connect(&drc, SIGNAL(wantTopVisible()), this, SLOT(activeLayerTop()), Qt::DirectConnection); connect(&drc, SIGNAL(wantBottomVisible()), this, SLOT(activeLayerBottom()), Qt::DirectConnection); connect(&drc, SIGNAL(wantBothVisible()), this, SLOT(activeLayerBoth()), Qt::DirectConnection); connect(&progress, SIGNAL(cancel()), &drc, SLOT(cancel()), Qt::DirectConnection); connect(&drc, SIGNAL(setMaximumProgress(int)), &progress, SLOT(setMaximum(int)), Qt::DirectConnection); connect(&drc, SIGNAL(setProgressValue(int)), &progress, SLOT(setValue(int)), Qt::DirectConnection); connect(&drc, SIGNAL(setProgressMessage(const QString &)), &progress, SLOT(setMessage(const QString &))); connect(&drc, SIGNAL(hideProgress()), &progress, SLOT(close())); ProcessEventBlocker::processEvents(); ProcessEventBlocker::block(); results = drc.start(showOkMessage, pcbSketchWidget->getKeepout() * 1000 / GraphicsUtils::SVGDPI); // pixels to mils ProcessEventBlocker::unblock(); pcbSketchWidget->setLayerActive(ViewLayer::Copper1, copper1Active); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen1, copper1Active); pcbSketchWidget->setLayerActive(ViewLayer::Copper0, copper0Active); pcbSketchWidget->setLayerActive(ViewLayer::Silkscreen0, copper0Active); updateActiveLayerButtons(); return results; } void MainWindow::changeTraceLayer() { if (m_currentGraphicsView == NULL) return; if (m_currentGraphicsView != m_pcbGraphicsView) return; Wire * wire = retrieveWire(); m_pcbGraphicsView->changeTraceLayer(wire, false, NULL); } Wire * MainWindow::retrieveWire() { WireAction * wireAction = qobject_cast(sender()); if (wireAction == NULL) return NULL; return wireAction->wire(); } ConnectorItem * MainWindow::retrieveConnectorItem() { ConnectorItemAction * connectorItemAction = qobject_cast(sender()); if (connectorItemAction == NULL) return NULL; return connectorItemAction->connectorItem(); } void MainWindow::setSticky() { QList items = m_currentGraphicsView->scene()->selectedItems(); if (items.count() < 1) return; ItemBase * itemBase = dynamic_cast(items.at(0)); if (itemBase == NULL) return; if (!itemBase->isBaseSticky()) return; itemBase->setLocalSticky(!itemBase->isLocalSticky()); } void MainWindow::moveLock() { bool moveLock = true; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->selectedItems()) { ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item); if (itemBase == NULL) continue; if (itemBase->itemType() == ModelPart::Wire) continue; if (itemBase->moveLock()) { moveLock = false; break; } } ItemBase * viewedItem = m_infoView->currentItem(); foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->selectedItems()) { ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item); if (itemBase == NULL) continue; if (itemBase->itemType() == ModelPart::Wire) continue; itemBase->setMoveLock(moveLock); if (viewedItem && viewedItem->layerKinChief() == itemBase->layerKinChief()) { m_currentGraphicsView->viewItemInfo(itemBase); } } } void MainWindow::selectMoveLock() { m_currentGraphicsView->selectAllMoveLock(); } void MainWindow::autorouterSettings() { if (m_currentGraphicsView != m_pcbGraphicsView) return; m_pcbGraphicsView->autorouterSettings(); } void MainWindow::orderFab() { QDesktopServices::openUrl(QString("http://fab.fritzing.org/")); } void MainWindow::setGroundFillSeeds() { int boardCount; ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount); if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use copper fill operations.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please select a PCB. Copper fill operations only work on one board at a time.")); return; } m_pcbGraphicsView->setGroundFillSeeds(); } void MainWindow::clearGroundFillSeeds() { int boardCount; ItemBase * board = m_pcbGraphicsView->findSelectedBoard(boardCount); if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use copper fill operations.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please select a PCB. Copper fill operations only work on one board at a time.")); return; } m_pcbGraphicsView->clearGroundFillSeeds(); } void MainWindow::setOneGroundFillSeed() { ConnectorItemAction * action = qobject_cast(sender()); if (action == NULL) return; ConnectorItem * connectorItem = action->connectorItem(); if (connectorItem == NULL) return; GroundFillSeedCommand * command = new GroundFillSeedCommand(m_pcbGraphicsView, NULL); command->addItem(connectorItem->attachedToID(), connectorItem->connectorSharedID(), action->isChecked()); m_undoStack->push(command); } void MainWindow::gridUnits(bool checked) { QWidget * widget = qobject_cast(sender()); if (widget == NULL) return; GridSizeDialog * dialog = qobject_cast(widget->window()); if (dialog == NULL) return; GridSizeThing * gridSizeThing = dialog->gridSizeThing(); QString units; if (sender() == gridSizeThing->inButton) { units = (checked) ? "in" : "mm"; } else { units = (checked) ? "mm" : "in"; } if (units.startsWith("mm")) { gridSizeThing->validator->setTop(25.4); gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->lineEdit->text().toDouble() * 25.4)); } else { gridSizeThing->validator->setTop(1.0); gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->lineEdit->text().toDouble() / 25.4)); } } void MainWindow::restoreDefaultGrid() { QWidget * widget = qobject_cast(sender()); if (widget == NULL) return; GridSizeDialog * dialog = qobject_cast(widget->window()); if (dialog == NULL) return; GridSizeThing * gridSizeThing = dialog->gridSizeThing(); gridSizeThing->inButton->setChecked(true); gridSizeThing->mmButton->setChecked(false); gridSizeThing->lineEdit->setText(QString::number(gridSizeThing->defaultGridSize)); } void MainWindow::setBackgroundColor() { QColor cc = m_currentGraphicsView->background(); QColor scc = m_currentGraphicsView->standardBackground(); SetColorDialog setColorDialog(tr("%1 background").arg(m_currentGraphicsView->viewName()), cc, scc, true, this); int result = setColorDialog.exec(); if (result == QDialog::Rejected) return; QColor newColor = setColorDialog.selectedColor(); m_currentGraphicsView->setBackgroundColor(newColor, setColorDialog.isPrefsColor()); setWindowModified(true); } void MainWindow::checkLoadedTraces() { if (m_pcbGraphicsView) m_pcbGraphicsView->checkLoadedTraces(); } void MainWindow::showUnrouted() { m_currentGraphicsView->showUnrouted(); } void MainWindow::hidePartSilkscreen() { m_pcbGraphicsView->hidePartSilkscreen(); } void MainWindow::fabQuote() { if (m_pcbGraphicsView) m_pcbGraphicsView->fabQuote(); } void MainWindow::findPartInSketch() { static QString lastSearchText; if (m_currentGraphicsView == NULL) return; bool ok; QString text = QInputDialog::getText(this, tr("Enter Text"), tr("Text will match part label, description, title, etc. Enter text to search for:"), QLineEdit::Normal, lastSearchText, &ok); if (!ok || text.isEmpty()) return; lastSearchText = text; QSet itemBases; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemBases.insert(itemBase->layerKinChief()); } QStringList strings; strings << text; QList matched; foreach (ItemBase * itemBase, itemBases) { #ifndef QT_NO_DEBUG if (QString::number(itemBase->id()).contains(text)) { matched << itemBase; continue; } #endif if (itemBase->instanceTitle().contains(text, Qt::CaseInsensitive)) { matched << itemBase; continue; } QList modelParts; m_referenceModel->search(itemBase->modelPart(), strings, modelParts, true); if (modelParts.count() > 0) { matched << itemBase; } } if (matched.count() == 0) { QMessageBox::information(this, tr("Search"), tr("No parts matched search term '%1'.").arg(text)); return; } m_currentGraphicsView->selectItems(matched); } void MainWindow::setGroundFillKeepout() { if (m_pcbGraphicsView != NULL) m_pcbGraphicsView->setGroundFillKeepout(); } void MainWindow::setViewFromBelowToggle() { if (m_pcbGraphicsView != NULL) { m_pcbGraphicsView->setViewFromBelow(m_viewFromBelowToggleAct->isChecked()); updateActiveLayerButtons(); } } void MainWindow::setViewFromBelow() { if (m_pcbGraphicsView != NULL) { m_pcbGraphicsView->setViewFromBelow(true); updateActiveLayerButtons(); } } void MainWindow::setViewFromAbove() { if (m_pcbGraphicsView != NULL) { m_pcbGraphicsView->setViewFromBelow(false); updateActiveLayerButtons(); } } void MainWindow::updateExportMenu() { bool enabled = m_currentGraphicsView != NULL; foreach (QAction * action, m_exportMenu->actions()) { action->setEnabled(enabled); } } void MainWindow::testConnectors() { if (m_currentGraphicsView == NULL) return; m_currentGraphicsView->testConnectors(); } fritzing-app-0.9.3b/src/mainwindow/sketchareawidget.cpp000066400000000000000000000113041272363601300232660ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include "sketchareawidget.h" const QString SketchAreaWidget::RoutingStateLabelName = "routingStateLabel"; SketchAreaWidget::SketchAreaWidget(QWidget *contentView, QMainWindow *parent) : QFrame(parent) { init(contentView, parent, true, true); } SketchAreaWidget::SketchAreaWidget(QWidget *contentView, QMainWindow *parent, bool hasToolBar, bool hasStatusBar) : QFrame(parent) { init (contentView, parent, hasToolBar, hasStatusBar); } SketchAreaWidget::~SketchAreaWidget() { // TODO Auto-generated destructor stub } void SketchAreaWidget::init(QWidget *contentView, QMainWindow *parent, bool hasToolBar, bool hasStatusBar) { m_contentView = contentView; contentView->setParent(this); createLayout(); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(contentView); layout->addWidget(m_toolbar); if (!hasToolBar) m_toolbar->hide(); layout->addWidget(m_statusBarArea); if (!hasStatusBar) m_statusBarArea->hide(); m_statusBarArea->setFixedHeight(parent->statusBar()->height()*3/4); } QWidget *SketchAreaWidget::contentView() { return m_contentView; } void SketchAreaWidget::createLayout() { m_toolbar = new QFrame(this); m_toolbar->setObjectName("sketchAreaToolbar"); m_toolbar->setFixedHeight(66); QFrame *leftButtons = new QFrame(m_toolbar); m_leftButtonsContainer = new QHBoxLayout(leftButtons); m_leftButtonsContainer->setMargin(0); m_leftButtonsContainer->setSpacing(0); QFrame *middleButtons = new QFrame(m_toolbar); middleButtons->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::MinimumExpanding); m_middleButtonsContainer = new QVBoxLayout(middleButtons); m_middleButtonsContainer->setSpacing(0); m_middleButtonsContainer->setMargin(0); QFrame *rightButtons = new QFrame(m_toolbar); m_rightButtonsContainer = new QHBoxLayout(rightButtons); m_rightButtonsContainer->setMargin(0); m_rightButtonsContainer->setSpacing(0); QHBoxLayout *toolbarLayout = new QHBoxLayout(m_toolbar); toolbarLayout->setMargin(0); toolbarLayout->setSpacing(0); toolbarLayout->addWidget(leftButtons); toolbarLayout->addWidget(middleButtons); toolbarLayout->addWidget(rightButtons); m_statusBarArea = new QFrame(this); m_statusBarArea->setObjectName("statusBarContainer"); QVBoxLayout *statusbarlayout = new QVBoxLayout(m_statusBarArea); statusbarlayout->setMargin(0); statusbarlayout->setSpacing(0); } void SketchAreaWidget::setToolbarWidgets(QList widgets) { bool goLeft = true; foreach(QWidget* widget, widgets) { if(widget->objectName() != RoutingStateLabelName) { if (goLeft) m_leftButtonsContainer->addWidget(widget); else m_rightButtonsContainer->addWidget(widget); } else { m_middleButtonsContainer->addSpacerItem(new QSpacerItem(0,1,QSizePolicy::Maximum)); m_middleButtonsContainer->addWidget(widget); m_middleButtonsContainer->addSpacerItem(new QSpacerItem(0,1,QSizePolicy::Maximum)); goLeft = false; } } } void SketchAreaWidget::addStatusBar(QStatusBar *statusBar) { m_statusBarArea->layout()->addWidget(statusBar); } QWidget *SketchAreaWidget::separator(QWidget* parent) { QLabel *separator = new QLabel(parent); separator->setPixmap(QPixmap(":/resources/images/toolbar_icons/toolbar_separator.png")); separator->setStyleSheet("margin-left: 1px; margin-right: 1px;"); separator->setObjectName("ToolBarSeparator"); return separator; } void SketchAreaWidget::setRoutingStatusLabel(ExpandingLabel * rsl) { m_routingStatusLabel = rsl; } ExpandingLabel * SketchAreaWidget::routingStatusLabel() { return m_routingStatusLabel; } QFrame * SketchAreaWidget::toolbar() { return m_toolbar; } fritzing-app-0.9.3b/src/mainwindow/sketchareawidget.h000066400000000000000000000041341272363601300227360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SKETCHAREAWIDGET_H_ #define SKETCHAREAWIDGET_H_ #include #include #include #include class SketchAreaWidget : public QFrame { Q_OBJECT public: SketchAreaWidget(QWidget *contentView, QMainWindow *parent); SketchAreaWidget(QWidget *contentView, QMainWindow *parent, bool hasToolBar, bool hasStatusBar); virtual ~SketchAreaWidget(); QWidget* contentView(); void setToolbarWidgets(QList buttons); void addStatusBar(QStatusBar *); static QWidget *separator(QWidget* parent); class ExpandingLabel * routingStatusLabel(); void setRoutingStatusLabel(ExpandingLabel *); QFrame * toolbar(); protected: void init(QWidget *contentView, QMainWindow *parent, bool hasToolBar, bool hasStatusBar); void createLayout(); public: static const QString RoutingStateLabelName; protected: QWidget *m_contentView; QFrame *m_toolbar; QHBoxLayout *m_leftButtonsContainer; QVBoxLayout *m_middleButtonsContainer; QHBoxLayout *m_rightButtonsContainer; QFrame *m_statusBarArea; class ExpandingLabel * m_routingStatusLabel; }; #endif /* SKETCHAREAWIDGET_H_ */ fritzing-app-0.9.3b/src/model/000077500000000000000000000000001272363601300161715ustar00rootroot00000000000000fritzing-app-0.9.3b/src/model/modelbase.cpp000066400000000000000000000730371272363601300206420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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.old You should have received a copy of the GNU General Public License along with Fritzing. If not, see . ******************************************************************** $Revision: 6963 $: $Author: irascibl@gmail.com $: $Date: 2013-04-14 10:13:52 +0200 (So, 14. Apr 2013) $ ********************************************************************/ #include "modelbase.h" #include "../debugdialog.h" #include "../items/partfactory.h" #include "../items/moduleidnames.h" #include "../utils/textutils.h" #include "../utils/folderutils.h" #include "../utils/fmessagebox.h" #include "../version/version.h" #include "../viewgeometry.h" #include QList ModelBase::CoreList; ///////////////////////////////////////////////// ModelBase::ModelBase( bool makeRoot ) { m_checkForReversedWires = m_useOldSchematics = false; m_reportMissingModules = true; m_referenceModel = NULL; m_root = NULL; if (makeRoot) { m_root = new ModelPart(); m_root->setModelPartShared(new ModelPartSharedRoot()); } } ModelBase::~ModelBase() { if (m_root) { ModelPartShared * modelPartShared = m_root->modelPartShared(); if (modelPartShared) { m_root->setModelPartShared(NULL); delete modelPartShared; } delete m_root; } } ModelPart * ModelBase::root() { return m_root; } ModelPart * ModelBase::retrieveModelPart(const QString & /* moduleID */) { return NULL; } // loads a model from an fz file--assumes a reference model exists with all parts bool ModelBase::loadFromFile(const QString & fileName, ModelBase * referenceModel, QList & modelParts, bool checkViews) { m_referenceModel = referenceModel; QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { FMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Cannot read file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("Parse error (1) at line %1, column %2:\n%3\n%4") .arg(errorLine) .arg(errorColumn) .arg(errorStr) .arg(fileName)); return false; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (2).").arg(fileName)); return false; } emit loadedRoot(fileName, this, root); if (root.tagName() != "module") { FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (4).").arg(fileName)); return false; } bool checkForOldSchematics = true; bool checkForRats = true; bool checkForTraces = true; bool checkForMysteryParts = true; bool checkForObsoleteSMDOrientation = true; m_fritzingVersion = root.attribute("fritzingVersion"); if (m_fritzingVersion.length() > 0) { // with version 0.4.3 ratsnests in fz files are obsolete VersionThing versionThingRats; versionThingRats.majorVersion = 0; versionThingRats.minorVersion = 4; versionThingRats.minorSubVersion = 2; versionThingRats.releaseModifier = ""; VersionThing versionThingFz; Version::toVersionThing(m_fritzingVersion,versionThingFz); checkForRats = !Version::greaterThan(versionThingRats, versionThingFz); // with version 0.6.5 traces are copied to all views versionThingRats.minorVersion = 6; versionThingRats.minorSubVersion = 4; checkForTraces = !Version::greaterThan(versionThingRats, versionThingFz); // with version 0.7.6 mystery part spacing implementation changes versionThingRats.minorVersion = 7; versionThingRats.minorSubVersion = 5; checkForMysteryParts = !Version::greaterThan(versionThingRats, versionThingFz); // with version 0.8.0 flipSMD is horizontal versionThingRats.minorVersion = 7; versionThingRats.minorSubVersion = 13; checkForObsoleteSMDOrientation = !Version::greaterThan(versionThingRats, versionThingFz); // with version 0.8.6 we get a new schematic template versionThingRats.minorVersion = 8; versionThingRats.minorSubVersion = 5; checkForOldSchematics = !Version::greaterThan(versionThingRats, versionThingFz); // with version 0.9.3 we don't have to worry about reversed wires versionThingRats.minorVersion = 9; versionThingRats.minorSubVersion = 2; m_checkForReversedWires = !Version::greaterThan(versionThingRats, versionThingFz); } ModelPartSharedRoot * modelPartSharedRoot = this->rootModelPartShared(); QDomElement title = root.firstChildElement("title"); if (!title.isNull()) { if (modelPartSharedRoot) { modelPartSharedRoot->setTitle(title.text()); } } // ensures changeBinIcon() is not available // this may be a bug? QString iconFilename = root.attribute("icon"); if (iconFilename.isEmpty()) { iconFilename = title.text() + ".png"; } if (!iconFilename.isEmpty()) { if (modelPartSharedRoot) { modelPartSharedRoot->setIcon(iconFilename); } } QString searchTerm = root.attribute("search"); if (!searchTerm.isEmpty() && modelPartSharedRoot != NULL) { modelPartSharedRoot->setSearchTerm(searchTerm); } QDomElement views = root.firstChildElement("views"); emit loadedViews(this, views); QDomElement instances = root.firstChildElement("instances"); if (instances.isNull()) { FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (3).").arg(fileName)); return false; } // delete any aready-existing model parts for (int i = m_root->children().count() - 1; i >= 0; i--) { QObject* child = m_root->children()[i]; child->setParent(NULL); delete child; } emit loadingInstances(this, instances); if (checkForRats) { QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { QDomElement nextInstance = instance.nextSiblingElement("instance"); if (isRatsnest(instance)) { instances.removeChild(instance); } instance = nextInstance; } } if (checkForTraces) { QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { checkTraces(instance); instance = instance.nextSiblingElement("instance"); } } if (checkForMysteryParts) { QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { checkMystery(instance); instance = instance.nextSiblingElement("instance"); } } if (checkForObsoleteSMDOrientation) { QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { if (checkObsoleteOrientation(instance)) { emit obsoleteSMDOrientationSignal(); break; } instance = instance.nextSiblingElement("instance"); } } m_useOldSchematics = false; if (checkForOldSchematics) { QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { if (checkOldSchematics(instance)) { emit oldSchematicsSignal(fileName, m_useOldSchematics); break; } instance = instance.nextSiblingElement("instance"); } } bool result = loadInstances(domDocument, instances, modelParts, checkViews); emit loadedInstances(this, instances); return result; } ModelPart * ModelBase::fixObsoleteModuleID(QDomDocument & domDocument, QDomElement & instance, QString & moduleIDRef) { return PartFactory::fixObsoleteModuleID(domDocument, instance, moduleIDRef, m_referenceModel); } bool ModelBase::loadInstances(QDomDocument & domDocument, QDomElement & instances, QList & modelParts, bool checkViews) { QHash missingModules; QDomElement instance = instances.firstChildElement("instance"); ModelPart* modelPart = NULL; while (!instance.isNull()) { emit loadingInstance(this, instance); if (checkViews) { QDomElement views = instance.firstChildElement("views"); QDomElement view = views.firstChildElement(); if (views.isNull() || view.isNull()) { // do not load a part with no views //QString text; //QTextStream stream(&text); //instance.save(stream, 0); //DebugDialog::debug(text); instance = instance.nextSiblingElement("instance"); continue; } } // for now assume all parts are in the palette QString moduleIDRef = instance.attribute("moduleIdRef"); //DebugDialog::debug("loading " + moduleIDRef); if (moduleIDRef.compare(ModuleIDNames::SpacerModuleIDName) == 0) { ModelPart * mp = new ModelPart(ModelPart::Space); mp->setInstanceText(instance.attribute("path")); mp->setParent(m_root); mp->modelPartShared()->setModuleID(ModuleIDNames::SpacerModuleIDName); mp->modelPartShared()->setPath(instance.attribute("path")); modelParts.append(mp); instance = instance.nextSiblingElement("instance"); continue; } bool generated = false; modelPart = m_referenceModel->retrieveModelPart(moduleIDRef); if (modelPart == NULL) { DebugDialog::debug(QString("module id %1 not found in database").arg(moduleIDRef)); modelPart = fixObsoleteModuleID(domDocument, instance, moduleIDRef); if (modelPart == NULL) { modelPart = genFZP(moduleIDRef, m_referenceModel); if (modelPart != NULL) { instance.setAttribute("moduleIdRef", modelPart->moduleID()); moduleIDRef = modelPart->moduleID(); generated = true; } if (modelPart == NULL) { missingModules.insert(moduleIDRef, instance.attribute("path")); instance = instance.nextSiblingElement("instance"); continue; } } } if (modelPart->isCore() && m_useOldSchematics) { modelPart = createOldSchematicPart(modelPart, moduleIDRef); } modelPart->setInBin(true); modelPart = addModelPart(m_root, modelPart); modelPart->setInstanceDomElement(instance); modelParts.append(modelPart); // TODO Mariano: i think this is not the way QString instanceTitle = instance.firstChildElement("title").text(); if(!instanceTitle.isNull() && !instanceTitle.isEmpty()) { modelPart->setInstanceTitle(instanceTitle, false); } QDomElement localConnectors = instance.firstChildElement("localConnectors"); QDomElement localConnector = localConnectors.firstChildElement("localConnector"); while (!localConnector.isNull()) { modelPart->setConnectorLocalName(localConnector.attribute("id"), localConnector.attribute("name")); localConnector = localConnector.nextSiblingElement("localConnector"); } QString instanceText = instance.firstChildElement("text").text(); if(!instanceText.isNull() && !instanceText.isEmpty()) { modelPart->setInstanceText(instanceText); } bool ok; long index = instance.attribute("modelIndex").toLong(&ok); if (ok) { // set the index so we can find the same model part later, as we continue loading modelPart->setModelIndex(index); } // note: this QDomNamedNodeMap loop is obsolete, but leaving it here so that old sketches don't get broken (jc, 22 Oct 2009) QDomNamedNodeMap map = instance.attributes(); for (int m = 0; m < map.count(); m++) { QDomNode node = map.item(m); QString nodeName = node.nodeName(); if (nodeName.isEmpty()) continue; if (nodeName.compare("moduleIdRef") == 0) continue; if (nodeName.compare("modelIndex") == 0) continue; if (nodeName.compare("originalModelIndex") == 0) continue; if (nodeName.compare("path") == 0) continue; modelPart->setLocalProp(nodeName, node.nodeValue()); } // "property" loop replaces previous QDomNamedNodeMap loop (jc, 22 Oct 2009) QDomElement prop = instance.firstChildElement("property"); while(!prop.isNull()) { QString name = prop.attribute("name"); if (!name.isEmpty()) { QString value = prop.attribute("value"); if (!value.isEmpty()) { modelPart->setLocalProp(name, value); } } prop = prop.nextSiblingElement("property"); } instance = instance.nextSiblingElement("instance"); } if (m_reportMissingModules && missingModules.count() > 0) { QString unableToFind = QString("%1
") .arg(tr("Unable to find the following %n part(s):", "", missingModules.count())); foreach (QString key, missingModules.keys()) { unableToFind += QString("") .arg(key).arg(tr("at")).arg(missingModules.value(key, "")); } unableToFind += "
'%1'%2'%3'
"; FMessageBox::warning(NULL, QObject::tr("Fritzing"), unableToFind); } return true; } ModelPart * ModelBase::addModelPart(ModelPart * parent, ModelPart * copyChild) { //if (copyChild->moduleID() == "df9d072afa2b594ac67b60b4153ff57b_29") { // DebugDialog::debug("alive in here"); //} ModelPart * modelPart = new ModelPart(); modelPart->copyNew(copyChild); modelPart->setParent(parent); modelPart->initConnectors(); modelPart->flipSMDAnd(); return modelPart; } ModelPart * ModelBase::addPart(QString newPartPath, bool addToReference) { Q_UNUSED(newPartPath); Q_UNUSED(addToReference); throw "ModelBase::addPart should not be invoked"; return NULL; } ModelPart * ModelBase::addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists) { Q_UNUSED(updateIdAlreadyExists); Q_UNUSED(newPartPath); Q_UNUSED(addToReference); throw "ModelBase::addPart should not be invoked"; return NULL; } // TODO Mariano: this function should never get called. Make pure virtual bool ModelBase::addPart(ModelPart * modelPart, bool update) { Q_UNUSED(modelPart); Q_UNUSED(update); throw "ModelBase::addPart should not be invoked"; return false; } void ModelBase::save(const QString & fileName, bool asPart) { QFileInfo info(fileName); QDir dir = info.absoluteDir(); QString temp = dir.absoluteFilePath("temp.xml"); QFile file1(temp); if (!file1.open(QFile::WriteOnly | QFile::Text)) { FMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Cannot write file temp:\n%1\n%2\n%3.") .arg(temp) .arg(fileName) .arg(file1.errorString()) ); return; } QXmlStreamWriter streamWriter(&file1); save(fileName, streamWriter, asPart); file1.close(); QFile original(fileName); if(original.exists() && !original.remove()) { file1.remove(); FMessageBox::warning( NULL, tr("File save failed!"), tr("Couldn't overwrite file '%1'.\nReason: %2 (errcode %3)") .arg(fileName) .arg(original.errorString()) .arg(original.error()) ); return; } file1.rename(fileName); } void ModelBase::save(const QString & fileName, QXmlStreamWriter & streamWriter, bool asPart) { streamWriter.setAutoFormatting(true); if(asPart) { m_root->saveAsPart(streamWriter, true); } else { m_root->saveInstances(fileName, streamWriter, true); } } bool ModelBase::paste(ModelBase * referenceModel, QByteArray & data, QList & modelParts, QHash & boundingRects, bool preserveIndex) { m_referenceModel = referenceModel; QDomDocument domDocument; QString errorStr; int errorLine; int errorColumn; bool result = domDocument.setContent(data, &errorStr, &errorLine, &errorColumn); if (!result) return false; QDomElement module = domDocument.documentElement(); if (module.isNull()) { return false; } QDomElement boundingRectsElement = module.firstChildElement("boundingRects"); if (!boundingRectsElement.isNull()) { QDomElement boundingRect = boundingRectsElement.firstChildElement("boundingRect"); while (!boundingRect.isNull()) { QString name = boundingRect.attribute("name"); QString rect = boundingRect.attribute("rect"); QRectF br; if (!rect.isEmpty()) { QStringList s = rect.split(" "); if (s.count() == 4) { QRectF r(s[0].toDouble(), s[1].toDouble(), s[2].toDouble(), s[3].toDouble()); br = r; } } boundingRects.insert(name, br); boundingRect = boundingRect.nextSiblingElement("boundingRect"); } } QDomElement instances = module.firstChildElement("instances"); if (instances.isNull()) { return false; } if (!preserveIndex) { // need to map modelIndexes from copied parts to new modelIndexes QHash oldToNew; QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { long oldModelIndex = instance.attribute("modelIndex").toLong(); oldToNew.insert(oldModelIndex, ModelPart::nextIndex()); instance = instance.nextSiblingElement("instance"); } renewModelIndexes(instances, "instance", oldToNew); } //QFile file("test.xml"); //file.open(QFile::WriteOnly); //file.write(domDocument.toByteArray()); //file.close(); return loadInstances(domDocument, instances, modelParts, true); } void ModelBase::renewModelIndexes(QDomElement & parentElement, const QString & childName, QHash & oldToNew) { QDomElement instance = parentElement.firstChildElement(childName); while (!instance.isNull()) { long oldModelIndex = instance.attribute("modelIndex").toLong(); instance.setAttribute("modelIndex", QString::number(oldToNew.value(oldModelIndex))); QDomElement views = instance.firstChildElement("views"); if (!views.isNull()) { QDomElement view = views.firstChildElement(); while (!view.isNull()) { bool ok; int superpart = view.attribute("superpart").toLong(&ok); if (ok) { view.setAttribute("superpart", QString::number(oldToNew.value(superpart))); } QDomElement connectors = view.firstChildElement("connectors"); if (!connectors.isNull()) { QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement connects = connector.firstChildElement("connects"); if (!connects.isNull()) { QDomElement connect = connects.firstChildElement("connect"); while (!connect.isNull()) { bool ok; oldModelIndex = connect.attribute("modelIndex").toLong(&ok); if (ok) { long newModelIndex = oldToNew.value(oldModelIndex, -1); if (newModelIndex != -1) { connect.setAttribute("modelIndex", QString::number(newModelIndex)); } else { //DebugDialog::debug(QString("keep old model index %1").arg(oldModelIndex)); } } connect = connect.nextSiblingElement("connect"); } } connector = connector.nextSiblingElement("connector"); } } view = view.nextSiblingElement(); } } instance = instance.nextSiblingElement(childName); } } void ModelBase::setReportMissingModules(bool b) { m_reportMissingModules = b; } ModelPart * ModelBase::genFZP(const QString & moduleID, ModelBase * referenceModel) { QString path = PartFactory::getFzpFilename(moduleID); if (path.isEmpty()) return NULL; ModelPart* mp = referenceModel->addPart(path, true, true); if (mp) mp->setCore(true); return mp; } ModelPartSharedRoot * ModelBase::rootModelPartShared() { if (m_root == NULL) return NULL; return m_root->modelPartSharedRoot(); } bool ModelBase::isRatsnest(QDomElement & instance) { QString moduleIDRef = instance.attribute("moduleIdRef"); if (moduleIDRef.compare(ModuleIDNames::WireModuleIDName) != 0) return false; QDomElement views = instance.firstChildElement("views"); if (views.isNull()) return false; QDomElement view = views.firstChildElement(); while (!view.isNull()) { QDomElement geometry = view.firstChildElement("geometry"); if (!geometry.isNull()) { int flags = geometry.attribute("wireFlags").toInt(); if (flags & ViewGeometry::RatsnestFlag) { return true; } if (flags & ViewGeometry::ObsoleteJumperFlag) { return true; } } view = view.nextSiblingElement(); } return false; } bool ModelBase::checkOldSchematics(QDomElement & instance) { if (instance.attribute("moduleIdRef").compare(ModuleIDNames::WireModuleIDName) != 0) { return false; } QDomElement views = instance.firstChildElement("views"); QDomElement schematicView = views.firstChildElement("schematicView"); QDomElement geometry = schematicView.firstChildElement("geometry"); if (geometry.isNull()) return false; int flags = geometry.attribute("wireFlags", "0").toInt(); return (flags & ViewGeometry::SchematicTraceFlag) != 0; } bool ModelBase::checkObsoleteOrientation(QDomElement & instance) { QString flippedSMD = instance.attribute("flippedSMD", ""); if (flippedSMD != "true") return false; QDomElement views = instance.firstChildElement("views"); QDomElement pcbView = views.firstChildElement("pcbView"); return (pcbView.attribute("layer", "") == "copper0"); } void ModelBase::checkTraces(QDomElement & instance) { QString moduleIDRef = instance.attribute("moduleIdRef"); if (moduleIDRef.compare(ModuleIDNames::WireModuleIDName) != 0) return; QDomElement views = instance.firstChildElement("views"); if (views.isNull()) return; QDomElement bbView = views.firstChildElement("breadboardView"); QDomElement schView = views.firstChildElement("schematicView"); QDomElement pcbView = views.firstChildElement("pcbView"); if (!bbView.isNull() && !schView.isNull() && !pcbView.isNull()) { // if it's a breadboard wire; just make sure flag is correct QList elements; elements << bbView << schView << pcbView; foreach (QDomElement element, elements) { QDomElement geometry = element.firstChildElement("geometry"); if (!geometry.isNull()) { int flags = geometry.attribute("wireFlags").toInt(); if (flags & ViewGeometry::PCBTraceFlag) return; // not a breadboard wire, bail out if (flags & ViewGeometry::SchematicTraceFlag) return; // not a breadboard wire, bail out if ((flags & ViewGeometry::NormalFlag) == 0) { flags |= ViewGeometry::NormalFlag; geometry.setAttribute("wireFlags", QString::number(flags)); } } } return; } if (!bbView.isNull()) { QDomElement geometry = bbView.firstChildElement("geometry"); if (!geometry.isNull()) { int flags = geometry.attribute("wireFlags").toInt(); if ((flags & ViewGeometry::NormalFlag) == 0) { flags |= ViewGeometry::NormalFlag; geometry.setAttribute("wireFlags", QString::number(flags)); } } schView = bbView.cloneNode(true).toElement(); pcbView = bbView.cloneNode(true).toElement(); schView.setTagName("schematicView"); pcbView.setTagName("pcbView"); views.appendChild(pcbView); views.appendChild(schView); return; } if (!schView.isNull()) { QDomElement geometry = schView.firstChildElement("geometry"); if (!geometry.isNull()) { int flags = geometry.attribute("wireFlags").toInt(); if (flags & ViewGeometry::PCBTraceFlag) { flags ^= ViewGeometry::PCBTraceFlag; flags |= ViewGeometry::SchematicTraceFlag; geometry.setAttribute("wireFlags", QString::number(flags)); } } pcbView = schView.cloneNode(true).toElement(); bbView = schView.cloneNode(true).toElement(); pcbView.setTagName("pcbView"); bbView.setTagName("breadboardView"); views.appendChild(bbView); views.appendChild(pcbView); return; } if (!pcbView.isNull()) { schView = pcbView.cloneNode(true).toElement(); bbView = pcbView.cloneNode(true).toElement(); schView.setTagName("schematicView"); bbView.setTagName("breadboardView"); views.appendChild(bbView); views.appendChild(schView); return; } QDomElement iconView = views.firstChildElement("iconView"); if (!iconView.isNull()) return; QString string; QTextStream stream(&string); instance.save(stream, 0); stream.flush(); DebugDialog::debug(QString("no wire view elements in fz file %1").arg(string)); } const QString & ModelBase::fritzingVersion() { return m_fritzingVersion; } void ModelBase::setReferenceModel(ModelBase * modelBase) { m_referenceModel = modelBase; } void ModelBase::checkMystery(QDomElement & instance) { QString moduleIDRef = instance.attribute("moduleIdRef"); bool mystery = false; bool sip = false; bool dip = false; if (moduleIDRef.contains("mystery", Qt::CaseInsensitive)) mystery = true; else if (moduleIDRef.contains("sip", Qt::CaseInsensitive)) sip = true; else if (moduleIDRef.contains("dip", Qt::CaseInsensitive)) dip = true; else return; QString spacing; int pins = TextUtils::getPinsAndSpacing(moduleIDRef, spacing); QDomElement prop = instance.firstChildElement("property"); while (!prop.isNull()) { if (prop.attribute("name", "").compare("spacing") == 0) { QString trueSpacing = prop.attribute("value", ""); if (trueSpacing.isEmpty()) trueSpacing = "300mil"; if (moduleIDRef.contains(spacing)) { moduleIDRef.replace(spacing, trueSpacing); instance.setAttribute("moduleIdRef", moduleIDRef); return; } // if we're here, it's a single sided mystery part. moduleIDRef = QString("mystery_part_sip_%1_100mil").arg(pins); instance.setAttribute("moduleIdRef", moduleIDRef); return; } prop = prop.nextSiblingElement("property"); } } bool ModelBase::onCoreList(const QString & moduleID) { // CoreList contains db entries that are (presumably) overridden by an fzp in the parts folder return CoreList.contains(moduleID); } ModelPart * ModelBase::createOldSchematicPart(ModelPart * modelPart, QString & moduleIDRef) { QString schematicFilename = modelPart->imageFileName(ViewLayer::SchematicView, ViewLayer::Schematic); if (!schematicFilename.startsWith("schematic")) { schematicFilename = modelPart->imageFileName(ViewLayer::SchematicView, ViewLayer::SchematicFrame); if (!schematicFilename.startsWith("schematic")) { return modelPart; } } DebugDialog::debug("schematic " + schematicFilename); QString oldModuleIDRef = PartFactory::OldSchematicPrefix + moduleIDRef; ModelPart * oldModelPart = m_referenceModel->retrieveModelPart(oldModuleIDRef); // cached after the first time it's created if (oldModelPart) { moduleIDRef = oldModuleIDRef; return oldModelPart; } int ix = schematicFilename.indexOf("/"); schematicFilename.insert(ix + 1, PartFactory::OldSchematicPrefix); QString oldSvgPath = FolderUtils::getAppPartsSubFolderPath("") + "/svg/obsolete/"+ schematicFilename; oldModelPart = createOldSchematicPartAux(modelPart, oldModuleIDRef, schematicFilename, oldSvgPath); if (oldModelPart) { moduleIDRef = oldModuleIDRef; return oldModelPart; } oldSvgPath = ":resources/parts/svg/obsolete/"+ schematicFilename; oldModelPart = createOldSchematicPartAux(modelPart, oldModuleIDRef, schematicFilename, oldSvgPath); if (oldModelPart) { moduleIDRef = oldModuleIDRef; return oldModelPart; } // see whether it's a generated part oldSvgPath = PartFactory::getSvgFilename(schematicFilename); if (!oldSvgPath.isEmpty()) { oldModelPart = createOldSchematicPartAux(modelPart, oldModuleIDRef, schematicFilename, oldSvgPath); if (oldModelPart) { moduleIDRef = oldModuleIDRef; return oldModelPart; } } return modelPart; } ModelPart * ModelBase::createOldSchematicPartAux(ModelPart * modelPart, const QString & oldModuleIDRef, const QString & oldSchematicFileName, const QString & oldSvgPath) { if (!QFile::exists(oldSvgPath)) return NULL; // create oldModelPart, set up the new image file name, add it to refmodel QFile newFzp(modelPart->path()); QDomDocument oldDoc; bool ok = oldDoc.setContent(&newFzp); if (!ok) { // this shouldn't happen return NULL; } QDomElement root = oldDoc.documentElement(); root.setAttribute("moduleId", oldModuleIDRef); QDomElement views = root.firstChildElement("views"); QDomElement schematicView = views.firstChildElement("schematicView"); QDomElement layers = schematicView.firstChildElement("layers"); if (layers.isNull()) { // this shouldn't happen return NULL; } layers.setAttribute("image", oldSchematicFileName); QString oldFzpPath = PartFactory::fzpPath() + oldModuleIDRef + ".fzp"; if (!TextUtils::writeUtf8(oldFzpPath, oldDoc.toString())) { // this shouldn't happen return NULL; } ModelPart * oldModelPart = m_referenceModel->addPart(oldFzpPath, true, true); oldModelPart->setCore(modelPart->isCore()); oldModelPart->setContrib(modelPart->isContrib()); return oldModelPart; } bool ModelBase::checkForReversedWires() { return m_checkForReversedWires; } fritzing-app-0.9.3b/src/model/modelbase.h000066400000000000000000000074301272363601300203010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6565 $: $Author: irascibl@gmail.com $: $Date: 2012-10-15 12:10:48 +0200 (Mo, 15. Okt 2012) $ ********************************************************************/ #ifndef MODELBASE_H #define MODELBASE_H #include #include "modelpart.h" class ModelBase : public QObject { Q_OBJECT public: ModelBase(bool makeRoot); virtual ~ModelBase(); ModelPart * root(); ModelPartSharedRoot * rootModelPartShared(); virtual ModelPart* retrieveModelPart(const QString & moduleID); virtual ModelPart * addModelPart(ModelPart * parent, ModelPart * copyChild); bool loadFromFile(const QString & fileName, ModelBase* referenceModel, QList & modelParts, bool checkInstances); void save(const QString & fileName, bool asPart); void save(const QString & fileName, class QXmlStreamWriter &, bool asPart); virtual ModelPart * addPart(QString newPartPath, bool addToReference); virtual bool addPart(ModelPart * modelPart, bool update); virtual ModelPart * addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists); bool paste(ModelBase * referenceModel, QByteArray & data, QList & modelParts, QHash & boundingRects, bool preserveIndex); void setReportMissingModules(bool); ModelPart * genFZP(const QString & moduleID, ModelBase * referenceModel); const QString & fritzingVersion(); void setReferenceModel(ModelBase *); bool checkForReversedWires(); public: static bool onCoreList(const QString & moduleID); signals: void loadedViews(ModelBase *, QDomElement & views); void loadedRoot(const QString & fileName, ModelBase *, QDomElement & root); void loadingInstances(ModelBase *, QDomElement & instances); void loadingInstance(ModelBase *, QDomElement & instance); void loadedInstances(ModelBase *, QDomElement & instances); void obsoleteSMDOrientationSignal(); void oldSchematicsSignal(const QString & filename, bool & useOldSchematics); protected: void renewModelIndexes(QDomElement & root, const QString & childName, QHash & oldToNew); bool loadInstances(QDomDocument &, QDomElement & root, QList & modelParts, bool checkViews); ModelPart * fixObsoleteModuleID(QDomDocument & domDocument, QDomElement & instance, QString & moduleIDRef); static bool isRatsnest(QDomElement & instance); static void checkTraces(QDomElement & instance); static void checkMystery(QDomElement & instance); static bool checkObsoleteOrientation(QDomElement & instance); static bool checkOldSchematics(QDomElement & instance); ModelPart * createOldSchematicPart(ModelPart *, QString & moduleIDRef); ModelPart * createOldSchematicPartAux(ModelPart *, const QString & oldModuleIDRef, const QString & oldSchematicFileName, const QString & oldSvgPath); protected: QPointer m_root; QPointer m_referenceModel; bool m_reportMissingModules; QString m_fritzingVersion; bool m_useOldSchematics; bool m_checkForReversedWires; protected: static QList CoreList; }; #endif fritzing-app-0.9.3b/src/model/modelpart.cpp000066400000000000000000000705131272363601300206720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6956 $: $Author: irascibl@gmail.com $: $Date: 2013-04-07 12:14:50 +0200 (So, 07. Apr 2013) $ ********************************************************************/ #include "modelpart.h" #include "../debugdialog.h" #include "../connectors/connectorshared.h" #include "../connectors/busshared.h" #include "../connectors/bus.h" #include "../version/version.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../items/itembase.h" #include "../items/partfactory.h" #include #include long ModelPart::m_nextIndex = 0; const int ModelPart::indexMultiplier = 10; QStringList ModelPart::m_possibleFolders; typedef QHash InstanceTitleIncrementHash; static QHash AllInstanceTitleIncrements; InstanceTitleIncrementHash NullInstanceTitleIncrements; static const QRegExp InstanceTitleRegExp("^(.*[^\\d])(\\d+)$"); static const QList EmptyViewImages; //////////////////////////////////////// ModelPart::ModelPart(ItemType type) : QObject() { commonInit(type); m_modelPartShared = NULL; m_index = m_nextIndex++; } ModelPart::ModelPart(QDomDocument & domDocument, const QString & path, ItemType type) : QObject() { commonInit(type); m_modelPartShared = new ModelPartShared(domDocument, path); m_modelPartShared->addOwner(this); } void ModelPart::commonInit(ItemType type) { m_type = type; m_locationFlags = 0; m_indexSynched = false; } ModelPart::~ModelPart() { //DebugDialog::debug(QString("deleting modelpart %1 %2").arg((long) this, 0, 16).arg(m_index)); clearOldInstanceTitle(m_instanceTitle); InstanceTitleIncrementHash * itih = AllInstanceTitleIncrements.value(this); if (itih) { AllInstanceTitleIncrements.remove(this); foreach (ModelPartList * list, itih->values()) { delete list; } delete itih; } foreach (Connector * connector, m_connectorHash.values()) { delete connector; } m_connectorHash.clear(); clearBuses(); } const QString & ModelPart::moduleID() const { if (m_modelPartShared != NULL) return m_modelPartShared->moduleID(); return ___emptyString___; } const QString & ModelPart::label() { if (m_modelPartShared != NULL) return m_modelPartShared->label(); return ___emptyString___; } const QString & ModelPart::author() { if (m_modelPartShared != NULL) return m_modelPartShared->author(); return ___emptyString___; } const QString & ModelPart::taxonomy() { if (m_modelPartShared != NULL) return m_modelPartShared->taxonomy(); return ___emptyString___; } const QString & ModelPart::uri() { if (m_modelPartShared != NULL) return m_modelPartShared->uri(); return ___emptyString___; } const QDate & ModelPart::date() { if (m_modelPartShared != NULL) return m_modelPartShared->date(); static QDate tempDate; tempDate = QDate::currentDate(); return tempDate; } void ModelPart::setItemType(ItemType t) { m_type = t; } void ModelPart::copy(ModelPart * modelPart) { if (modelPart == NULL) return; m_type = modelPart->itemType(); m_modelPartShared = modelPart->modelPartShared(); if (m_modelPartShared) m_modelPartShared->addOwner(this); m_locationFlags = modelPart->m_locationFlags; } void ModelPart::copyNew(ModelPart * modelPart) { copy(modelPart); } void ModelPart::copyStuff(ModelPart * modelPart) { modelPartShared()->copy(modelPart->modelPartShared()); } ModelPartShared * ModelPart::modelPartShared() { if(!m_modelPartShared) { m_modelPartShared = new ModelPartShared(); m_modelPartShared->addOwner(this); } return m_modelPartShared; } ModelPartSharedRoot * ModelPart::modelPartSharedRoot() { return qobject_cast(m_modelPartShared); } void ModelPart::setModelPartShared(ModelPartShared * modelPartShared) { m_modelPartShared = modelPartShared; if (modelPartShared) m_modelPartShared->addOwner(this); } void ModelPart::addViewItem(ItemBase * item) { m_viewItems.append(item); } void ModelPart::removeViewItem(ItemBase * item) { m_viewItems.removeOne(item); } ItemBase * ModelPart::viewItem(QGraphicsScene * scene) { foreach (ItemBase * itemBase, m_viewItems) { if (itemBase->scene() == scene) return itemBase; } return NULL; } ItemBase * ModelPart::viewItem(ViewLayer::ViewID viewID) { foreach (ItemBase * itemBase, m_viewItems) { if (itemBase->viewID() == viewID) return itemBase; } return NULL; } void ModelPart::saveInstances(const QString & fileName, QXmlStreamWriter & streamWriter, bool startDocument) { if (startDocument) { streamWriter.writeStartDocument(); streamWriter.writeStartElement("module"); streamWriter.writeAttribute("fritzingVersion", Version::versionString()); ModelPartSharedRoot * root = modelPartSharedRoot(); if (root) { if (!root->icon().isEmpty()) { streamWriter.writeAttribute("icon", root->icon()); } if (!root->searchTerm().isEmpty()) { streamWriter.writeAttribute("search", root->searchTerm()); } } QString title = this->title(); if(!title.isNull() && !title.isEmpty()) { streamWriter.writeTextElement("title",title); } emit startSaveInstances(fileName, this, streamWriter); streamWriter.writeStartElement("instances"); } if (parent() != NULL) { // m_viewItems.size() > 0 saveInstance(streamWriter); } QList children = this->children(); if(m_orderedChildren.count() > 0) { children = m_orderedChildren; } QList::const_iterator i; for (i = children.constBegin(); i != children.constEnd(); ++i) { ModelPart* mp = qobject_cast(*i); if (mp == NULL) continue; mp->saveInstances(fileName, streamWriter, false); } if (startDocument) { streamWriter.writeEndElement(); // instances streamWriter.writeEndElement(); // module streamWriter.writeEndDocument(); } } void ModelPart::saveInstance(QXmlStreamWriter & streamWriter) { if (localProp("ratsnest").toBool()) { return; // don't save virtual wires } streamWriter.writeStartElement("instance"); if (m_modelPartShared != NULL) { QString moduleIdRef = m_modelPartShared->moduleID(); moduleIdRef.remove(PartFactory::OldSchematicPrefix); streamWriter.writeAttribute("moduleIdRef", moduleIdRef); streamWriter.writeAttribute("modelIndex", QString::number(m_index)); streamWriter.writeAttribute("path", m_modelPartShared->path()); if (m_modelPartShared->flippedSMD()) { streamWriter.writeAttribute("flippedSMD", "true"); } } bool writeLocal = false; foreach (Connector * connector, this->connectors()) { if (!connector->connectorLocalName().isEmpty()) { writeLocal = true; break; } } if (writeLocal) { streamWriter.writeStartElement("localConnectors"); foreach (Connector * connector, this->connectors()) { if (!connector->connectorLocalName().isEmpty()) { streamWriter.writeStartElement("localConnector"); streamWriter.writeAttribute("id", connector->connectorSharedID()); streamWriter.writeAttribute("name", TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(connector->connectorLocalName()))); streamWriter.writeEndElement(); } } streamWriter.writeEndElement(); } foreach (QByteArray byteArray, dynamicPropertyNames()) { streamWriter.writeStartElement("property"); streamWriter.writeAttribute("name", byteArray.data()); streamWriter.writeAttribute("value", property(byteArray.data()).toString()); streamWriter.writeEndElement(); } QString title = instanceTitle(); if(!title.isNull() && !title.isEmpty()) { writeTag(streamWriter,"title",title); } QString text = instanceText(); if(!text.isNull() && !text.isEmpty()) { streamWriter.writeStartElement("text"); streamWriter.writeCharacters(text); streamWriter.writeEndElement(); } // tell the views to write themselves out streamWriter.writeStartElement("views"); foreach (ItemBase * itemBase, m_viewItems) { itemBase->saveInstance(streamWriter); } streamWriter.writeEndElement(); // views streamWriter.writeEndElement(); //instance } void ModelPart::writeTag(QXmlStreamWriter & streamWriter, QString tagName, QString tagValue) { if(!tagValue.isEmpty()) { streamWriter.writeTextElement(tagName,tagValue); } } void ModelPart::writeNestedTag(QXmlStreamWriter & streamWriter, QString tagName, const QStringList &values, QString childTag) { if(values.count() > 0) { streamWriter.writeStartElement(tagName); for(int i=0; i &values, QString childTag, QString attrName) { streamWriter.writeStartElement(tagName); for(int i=0; imoduleID(); moduleID.remove(PartFactory::OldSchematicPrefix); streamWriter.writeAttribute("moduleId", moduleID); writeTag(streamWriter,"version",m_modelPartShared->version()); writeTag(streamWriter,"author",m_modelPartShared->author()); writeTag(streamWriter,"title",title()); writeTag(streamWriter,"label",m_modelPartShared->label()); writeTag(streamWriter,"date",m_modelPartShared->dateAsStr()); writeNestedTag(streamWriter,"tags",m_modelPartShared->tags(),"tag"); writeNestedTag(streamWriter,"properties",m_modelPartShared->properties(),"property","name"); writeTag(streamWriter,"taxonomy",m_modelPartShared->taxonomy()); writeTag(streamWriter,"description",m_modelPartShared->description()); QString spice = m_modelPartShared->spice(); if (!spice.isEmpty()) { streamWriter.writeStartElement("spice"); QStringList lines = spice.split("\r",QString::SkipEmptyParts); foreach (QString line, lines) { writeTag(streamWriter, "line", line); } QString spiceModel = m_modelPartShared->spiceModel(); if (!spiceModel.isEmpty()) { lines = spiceModel.split("\r",QString::SkipEmptyParts); foreach (QString line, lines) { writeTag(streamWriter, "model", line); } } streamWriter.writeEndElement(); } writeTag(streamWriter,"spice",m_modelPartShared->spice()); writeTag(streamWriter,"url",m_modelPartShared->url()); } if (m_viewItems.size() > 0) { if (startDocument) { streamWriter.writeStartElement("views"); } for (int i = 0; i < m_viewItems.size(); i++) { ItemBase * item = m_viewItems[i]; item->writeXml(streamWriter); } if(startDocument) { streamWriter.writeEndElement(); } streamWriter.writeStartElement("connectors"); const QList< QPointer > connectors = m_modelPartShared->connectorsShared(); for (int i = 0; i < connectors.count(); i++) { Connector * connector = new Connector(connectors[i], this); connector->saveAsPart(streamWriter); delete connector; } streamWriter.writeEndElement(); } QList::const_iterator i; for (i = children().constBegin(); i != children().constEnd(); ++i) { ModelPart * mp = qobject_cast(*i); if (mp == NULL) continue; mp->saveAsPart(streamWriter, false); } if (startDocument) { streamWriter.writeEndElement(); streamWriter.writeEndElement(); streamWriter.writeEndDocument(); } } void ModelPart::initConnectors(bool force) { if(m_modelPartShared == NULL) return; if(force) { foreach (Connector * connector, m_connectorHash.values()) { // due to craziness in the parts editor // m_deletedConnectors.append(connector); delete connector; } m_connectorHash.clear(); clearBuses(); } if(m_connectorHash.count() > 0) return; // already done m_modelPartShared->initConnectors(); foreach (ConnectorShared * connectorShared, m_modelPartShared->connectorsShared()) { Connector * connector = new Connector(connectorShared, this); m_connectorHash.insert(connectorShared->id(), connector); } initBuses(); } void ModelPart::clearBuses() { foreach (Bus * bus, m_busHash.values()) { delete bus; } m_busHash.clear(); } void ModelPart::initBuses() { foreach (Connector * connector, m_connectorHash.values()) { BusShared * busShared = connector->connectorShared()->bus(); if (busShared != NULL) { Bus * bus = m_busHash.value(busShared->id()); if (bus == NULL) { bus = new Bus(busShared, this); m_busHash.insert(busShared->id(), bus); } connector->setBus(bus); bus->addConnector(connector); } } } const QHash > & ModelPart::connectors() { return m_connectorHash; } long ModelPart::modelIndex() { return m_index; } void ModelPart::setModelIndex(long index) { m_index = index; updateIndex(index); } void ModelPart::setModelIndexFromMultiplied(long multiplied) { if (m_indexSynched) { // this is gross. m_index should always be itemBase->id() / ModelPart::indexMultiplier // but sometimes multiple parts reuse the same model part, so this makes sure we don't overwrite // when temporarily reusing a modelpart. Eventually always create a new model part and get rid of modelIndex if (m_index != multiplied / ModelPart::indexMultiplier) { //DebugDialog::debug("temporary model part?"); } return; } m_indexSynched = true; setModelIndex(multiplied / ModelPart::indexMultiplier); } void ModelPart::updateIndex(long index) { if (index >= m_nextIndex) { m_nextIndex = index + 1; } } long ModelPart::nextIndex() { return m_nextIndex++; } void ModelPart::setInstanceDomElement(const QDomElement & domElement) { //DebugDialog::debug(QString("model part instance %1").arg((long) this, 0, 16)); m_instanceDomElement = domElement; } const QDomElement & ModelPart::instanceDomElement() { return m_instanceDomElement; } const QString & ModelPart::fritzingVersion() { if (m_modelPartShared != NULL) return m_modelPartShared->fritzingVersion(); return ___emptyString___; } const QString & ModelPart::title() { if (!m_localTitle.isEmpty()) return m_localTitle; if (m_modelPartShared != NULL) return m_modelPartShared->title(); return m_localTitle; } void ModelPart::setLocalTitle(const QString & localTitle) { m_localTitle = localTitle; } const QString & ModelPart::version() { if (m_modelPartShared != NULL) return m_modelPartShared->version(); return ___emptyString___; } const QString & ModelPart::path() { if (m_modelPartShared != NULL) return m_modelPartShared->path(); return ___emptyString___; } const QString & ModelPart::description() { if (m_modelPartShared != NULL) return m_modelPartShared->description(); return ___emptyString___; } const QString & ModelPart::spice() { if (m_modelPartShared != NULL) return m_modelPartShared->spice(); return ___emptyString___; } const QString & ModelPart::spiceModel() { if (m_modelPartShared != NULL) return m_modelPartShared->spiceModel(); return ___emptyString___; } const QString & ModelPart::url() { if (m_modelPartShared != NULL) return m_modelPartShared->url(); return ___emptyString___; } const QStringList & ModelPart::tags() { if (m_modelPartShared != NULL) return m_modelPartShared->tags(); return ___emptyStringList___; } const QHash & ModelPart::properties() const { if (m_modelPartShared != NULL) return m_modelPartShared->properties(); return ___emptyStringHash___; } Connector * ModelPart::getConnector(const QString & id) { return m_connectorHash.value(id); } const QHash > & ModelPart::buses() { return m_busHash; } Bus * ModelPart::bus(const QString & busID) { return m_busHash.value(busID); } bool ModelPart::ignoreTerminalPoints() { if (m_modelPartShared != NULL) return m_modelPartShared->ignoreTerminalPoints(); return true; } bool ModelPart::isCore() { return (m_locationFlags & CoreFlag) != 0; } void ModelPart::setCore(bool core) { setLocationFlag(core, CoreFlag); } bool ModelPart::isContrib() { return (m_locationFlags & ContribFlag) != 0; } void ModelPart::setContrib(bool contrib) { setLocationFlag(contrib, ContribFlag); } bool ModelPart::isAlien() { return (m_locationFlags & AlienFlag) != 0;; } void ModelPart::setAlien(bool alien) { setLocationFlag(alien, AlienFlag); } bool ModelPart::isInBin() { return (m_locationFlags & InBinFlag) != 0;; } void ModelPart::setInBin(bool inBin) { setLocationFlag(inBin, InBinFlag); } bool ModelPart::isFzz() { return (m_locationFlags & FzzFlag) != 0;; } void ModelPart::setFzz(bool fzz) { setLocationFlag(fzz, FzzFlag); } void ModelPart::setLocationFlag(bool setting, LocationFlag flag) { if (setting) { m_locationFlags |= flag; } else { m_locationFlags &= ~flag; } } bool ModelPart::hasViewID(ViewLayer::ViewID viewID) { if (m_modelPartShared) return m_modelPartShared->hasViewID(viewID); return false; } bool ModelPart::canFlipVertical(ViewLayer::ViewID viewID) { if (m_modelPartShared) return m_modelPartShared->canFlipVertical(viewID); return false; } bool ModelPart::canFlipHorizontal(ViewLayer::ViewID viewID) { if (m_modelPartShared) return m_modelPartShared->canFlipHorizontal(viewID); return false; } LayerList ModelPart::viewLayers(ViewLayer::ViewID viewID) { if (m_modelPartShared) return m_modelPartShared->viewLayers(viewID); LayerList layerList; return layerList; } QString ModelPart::imageFileName(ViewLayer::ViewID viewID) { if (m_modelPartShared) return m_modelPartShared->imageFileName(viewID); return ""; } QString ModelPart::imageFileName(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { if (m_modelPartShared) return m_modelPartShared->imageFileName(viewID, viewLayerID); return ""; } bool ModelPart::anySticky(ViewLayer::ViewID viewID) { if (m_modelPartShared) return m_modelPartShared->anySticky(viewID); return false; } QList ModelPart::getAllParts() { QList retval; QList::const_iterator i; for (i = children().constBegin(); i != children().constEnd(); ++i) { ModelPart* mp = qobject_cast(*i); if (mp == NULL) continue; retval << mp; } return retval; } QList ModelPart::getAllNonCoreParts() { QList retval; QList::const_iterator i; for (i = children().constBegin(); i != children().constEnd(); ++i) { ModelPart* mp = qobject_cast(*i); if (mp == NULL) continue; if(!mp->isCore()) { retval << mp; } } return retval; } bool ModelPart::hasViewID(long id) { foreach (ItemBase * item, m_viewItems) { if (item->id() == id) return true; } return false; } const QString & ModelPart::instanceTitle() const { return m_instanceTitle; } const QString & ModelPart::instanceText() { return m_instanceText; } void ModelPart::setInstanceText(QString text) { m_instanceText = text; } void ModelPart::clearOldInstanceTitle(const QString & title) { InstanceTitleIncrementHash * itih = NULL; if (parent() == NULL) { itih = &NullInstanceTitleIncrements; } else { itih = AllInstanceTitleIncrements.value(parent(), NULL); } if (itih == NULL) return; //DebugDialog::debug(QString("clearing title:%1 ix:%2").arg(title).arg(modelPart->modelIndex())); QString prefix = title; int ix = InstanceTitleRegExp.indexIn(title); if (ix >= 0) { prefix = InstanceTitleRegExp.cap(1); } ModelPartList * modelParts = itih->value(prefix, NULL); if (modelParts) { modelParts->removeOne(this); //DebugDialog::debug(QString("\tc:%1").arg(modelParts->count())); } } ModelPartList * ModelPart::ensureInstanceTitleIncrements(const QString & prefix) { InstanceTitleIncrementHash * itih = NULL; if (parent() == NULL) { itih = &NullInstanceTitleIncrements; } else { itih = AllInstanceTitleIncrements.value(parent(), NULL); if (itih == NULL) { itih = new InstanceTitleIncrementHash; AllInstanceTitleIncrements.insert(parent(), itih); } } ModelPartList * modelParts = itih->value(prefix, NULL); if (modelParts == NULL) { modelParts = new ModelPartList; itih->insert(prefix, modelParts); } return modelParts; } void ModelPart::setInstanceTitle(QString title, bool initial) { if (initial) { if (setSubpartInstanceTitle()) return; } if (title.compare(m_instanceTitle) == 0) return; clearOldInstanceTitle(m_instanceTitle); m_instanceTitle = title; QString prefix = title; int ix = InstanceTitleRegExp.indexIn(title); if (ix >= 0) { prefix = InstanceTitleRegExp.cap(1); ModelPartList * modelParts = ensureInstanceTitleIncrements(prefix); modelParts->append(this); } //DebugDialog::debug(QString("adding title:%1 ix:%2 c:%3").arg(title).arg(modelIndex()).arg(modelParts->count())); if (m_modelPartShared && m_modelPartShared->hasSubparts()) { if (m_viewItems.count() > 0) { ItemBase * itemBase = m_viewItems.last(); if (itemBase) { foreach (ItemBase * subpart, itemBase->subparts()) { subpart->setInstanceTitle("", true); // will end up calling setSubpartInstanceTitle() } } } } } bool ModelPart::setSubpartInstanceTitle() { if (m_modelPartShared == NULL) return false; if (m_modelPartShared->superpart() == NULL) return false; if (m_viewItems.count() <= 0) return false; ItemBase * itemBase = m_viewItems.last(); if (itemBase == NULL) return false; itemBase = itemBase->superpart(); if (itemBase == NULL) return false; QString superTitle = itemBase->instanceTitle(); if (superTitle.isEmpty()) return false; QString label = m_modelPartShared->label(); if (label.isEmpty()) return false; m_instanceTitle = superTitle + "_" + label; return true; } QString ModelPart::getNextTitle(const QString & title) { QString prefix = title; int ix = InstanceTitleRegExp.indexIn(title); if (ix >= 0) { prefix = InstanceTitleRegExp.cap(1); } else { bool allDigits = true; foreach (QChar c, title) { if (!c.isDigit()) { allDigits = false; break; } } if (allDigits) { return title; } } // TODO: if this were a sorted list, ModelPartList * modelParts = ensureInstanceTitleIncrements(prefix); int highestSoFar = 0; bool gotNull = false; foreach (ModelPart * modelPart, *modelParts) { if (modelPart == NULL) { gotNull = true; continue; } QString title = modelPart->instanceTitle(); title.remove(0, prefix.length()); int count = title.toInt(); // returns zero on failure if (count > highestSoFar) { highestSoFar = count; } } if (gotNull) { modelParts->removeAll(NULL); } //DebugDialog::debug(QString("returning increment %1, %2").arg(prefix).arg(highestSoFar + 1)); return QString("%1%2").arg(prefix).arg(highestSoFar + 1); } void ModelPart::setOrderedChildren(QList children) { m_orderedChildren = children; } void ModelPart::setLocalProp(const char * name, const QVariant & value) { //DebugDialog::debug(QString("mp set prop %1 %2").arg(name).arg(value.toString())); QObject::setProperty(name, value); } QVariant ModelPart::localProp(const char * name) const { return property(name); } void ModelPart::setLocalProp(const QString & name, const QVariant & value) { QByteArray b = name.toLatin1(); setLocalProp(b.data(), value); } QVariant ModelPart::localProp(const QString & name) const { QByteArray b = name.toLatin1(); return localProp(b.data()); } const QStringList & ModelPart::possibleFolders() { if (m_possibleFolders.count() == 0) { m_possibleFolders << "core" << "obsolete" << "contrib" << "user"; } return m_possibleFolders; } const QString & ModelPart::replacedby() { if (m_modelPartShared != NULL) return m_modelPartShared->replacedby(); return ___emptyString___; } bool ModelPart::isObsolete() { if (m_modelPartShared != NULL) return !m_modelPartShared->replacedby().isEmpty(); return false; } bool ModelPart::flippedSMD() { if (m_modelPartShared != NULL) { return m_modelPartShared->flippedSMD(); } return false; } bool ModelPart::needsCopper1() { if (m_modelPartShared != NULL) { return m_modelPartShared->needsCopper1(); } return false; } bool ModelPart::hasViewFor(ViewLayer::ViewID viewID) { if (m_modelPartShared == NULL) return false; return m_modelPartShared->hasViewFor(viewID); } bool ModelPart::hasViewFor(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { if (m_modelPartShared == NULL) return false; return m_modelPartShared->hasViewFor(viewID, viewLayerID); } QString ModelPart::hasBaseNameFor(ViewLayer::ViewID viewID) { if (m_modelPartShared == NULL) return ___emptyString___; return m_modelPartShared->hasBaseNameFor(viewID); } const QStringList & ModelPart::displayKeys() { if (m_modelPartShared == NULL) return ___emptyStringList___; return m_modelPartShared->displayKeys(); } ModelPart::ItemType ModelPart::itemType() const { return m_type; }; void ModelPart::setConnectorLocalName(const QString & id, const QString & name) { if (id.isEmpty()) return; Connector * connector = m_connectorHash.value(id, NULL); if (connector) { connector->setConnectorLocalName(name); } } QString ModelPart::connectorLocalName(const QString & id) { if (id.isEmpty()) return ""; Connector * connector = m_connectorHash.value(id, NULL); if (connector) { return connector->connectorLocalName(); } return ""; } QString ModelPart::family(){ if (m_modelPartShared) return m_modelPartShared->family(); return ""; } bool ModelPart::hasViewItems() { return (m_viewItems.count() > 0); } void ModelPart::setDBID(qulonglong dbid) { if (m_modelPartShared) m_modelPartShared->setDBID(dbid); } qulonglong ModelPart::dbid() { if (m_modelPartShared) return m_modelPartShared->dbid(); return 0; } const QList ModelPart::viewImages() { if (m_modelPartShared) return m_modelPartShared->viewImages(); return EmptyViewImages; } void ModelPart::setViewImage(ViewImage * viewImage) { if (m_modelPartShared) m_modelPartShared->setViewImage(viewImage); } void ModelPart::setTag(const QString & tag) { if (m_modelPartShared) m_modelPartShared->setTag(tag); } void ModelPart::setProperty(const QString & name, const QString &value, bool showInLabel) { if (m_modelPartShared) m_modelPartShared->setProperty(name, value, showInLabel); } bool ModelPart::showInLabel(const QString & propertyName) { if (m_modelPartShared) return m_modelPartShared->showInLabel(propertyName); return false; } void ModelPart::addConnector(Connector * connector) { m_connectorHash.insert(connector->connectorSharedID(), connector); if (m_modelPartShared) m_modelPartShared->addConnector(connector->connectorShared()); } void ModelPart::flipSMDAnd() { if (m_modelPartShared) m_modelPartShared->flipSMDAnd(); } void ModelPart::setImageFileName(ViewLayer::ViewID viewID, const QString & filename) { if (m_modelPartShared) m_modelPartShared->setImageFileName(viewID, filename); } void ModelPart::lookForZeroConnector() { if (m_modelPartShared) m_modelPartShared->lookForZeroConnector(); } bool ModelPart::hasZeroConnector() { if (m_modelPartShared) return m_modelPartShared->hasZeroConnector(); return false; } void ModelPart::killViewItems() { foreach (ItemBase * itemBase, m_viewItems) { if (itemBase) delete itemBase; } m_viewItems.clear(); } bool ModelPart::hasSubparts() { if (m_modelPartShared) return m_modelPartShared->hasSubparts(); return false; } void ModelPart::setSubpartID(const QString & id) { if (m_modelPartShared) m_modelPartShared->setSubpartID(id); } fritzing-app-0.9.3b/src/model/modelpart.h000066400000000000000000000167231272363601300203420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6956 $: $Author: irascibl@gmail.com $: $Date: 2013-04-07 12:14:50 +0200 (So, 07. Apr 2013) $ ********************************************************************/ #ifndef MODELPART_H #define MODELPART_H #include #include #include #include #include #include #include #include #include #include #include #include "modelpartshared.h" #include "../connectors/connector.h" #include "../connectors/bus.h" class ModelPart : public QObject { Q_OBJECT public: enum ItemType { Part, Wire, Breadboard, Board, ResizableBoard, Note, Symbol, Jumper, CopperFill, Logo, Hole, Via, Ruler, SchematicSubpart, Space, Unknown }; enum LocationFlag { NoFlag = 0, CoreFlag = 1, ContribFlag = 2, AlienFlag = 4, FzzFlag = 8, InBinFlag = 16 }; Q_DECLARE_FLAGS(LocationFlags, LocationFlag) public: ModelPart(QDomDocument &, const QString& path, ItemType type); ModelPart(ItemType type = ModelPart::Unknown); ~ModelPart(); ModelPart::ItemType itemType() const; void setItemType(ItemType); const QString & moduleID() const; void copy(ModelPart *); void copyNew(ModelPart *); void copyStuff(ModelPart * modelPart); ModelPartShared * modelPartShared(); ModelPartSharedRoot * modelPartSharedRoot(); void setModelPartShared(ModelPartShared *modelPartShared); void saveInstances(const QString & fileName, QXmlStreamWriter & streamWriter, bool startDocument); void saveAsPart(QXmlStreamWriter & streamWriter, bool startDocument); void addViewItem(class ItemBase *); void removeViewItem(class ItemBase *); void killViewItems(); class ItemBase * viewItem(QGraphicsScene * scene); class ItemBase * viewItem(ViewLayer::ViewID); bool hasViewItems(); void initConnectors(bool force=false); const QHash > & connectors(); long modelIndex(); void setModelIndex(long index); void setModelIndexFromMultiplied(long multipliedIndex); void setInstanceDomElement(const QDomElement &); const QDomElement & instanceDomElement(); Connector * getConnector(const QString & id); const QString & fritzingVersion(); const QString & title(); const QString & description(); const QString & spice(); const QString & spiceModel(); const QString & url(); const QStringList & tags(); const QStringList & displayKeys(); const QHash & properties() const; const QHash > & buses(); const QString & taxonomy(); const QString & version(); const QString & path(); const QString & label(); const QString & author(); const QString & language(); const QString & uri(); const QDate & date(); QString family(); void setDBID(qulonglong); qulonglong dbid(); Bus * bus(const QString & busID); bool ignoreTerminalPoints(); bool isCore(); void setCore(bool core); bool isContrib(); void setContrib(bool contrib); bool isAlien(); // from "outside" void setAlien(bool alien); bool isFzz(); // from "outside" void setFzz(bool alien); void setLocationFlag(bool setting, LocationFlag flag); bool isInBin(); void setInBin(bool); bool hasViewID(ViewLayer::ViewID); bool canFlipVertical(ViewLayer::ViewID); bool canFlipHorizontal(ViewLayer::ViewID); bool anySticky(ViewLayer::ViewID); QString imageFileName(ViewLayer::ViewID); void setImageFileName(ViewLayer::ViewID, const QString & filename); QString imageFileName(ViewLayer::ViewID, ViewLayer::ViewLayerID); LayerList viewLayers(ViewLayer::ViewID); void setViewImage(struct ViewImage *); QList getAllParts(); QList getAllNonCoreParts(); bool hasViewID(long id); const QString & instanceTitle() const; const QString & instanceText(); void setInstanceTitle(QString, bool initial); void setInstanceText(QString); QString getNextTitle(const QString & candidate); void setOrderedChildren(QList children); void setLocalProp(const QString & name, const QVariant & value); QVariant localProp(const QString & name) const; void setLocalProp(const char * name, const QVariant & value); QVariant localProp(const char * name) const; void setTag(const QString &tag); void setProperty(const QString & key, const QString & value, bool showInLabel); bool showInLabel(const QString & key); const QString & replacedby(); bool isObsolete(); bool flippedSMD(); bool needsCopper1(); bool hasViewFor(ViewLayer::ViewID); bool hasViewFor(ViewLayer::ViewID, ViewLayer::ViewLayerID); QString hasBaseNameFor(ViewLayer::ViewID); void initBuses(); void clearBuses(); void setConnectorLocalName(const QString & id, const QString & name); QString connectorLocalName(const QString & id); void setLocalTitle(const QString &); const QList viewImages(); void addConnector(Connector *); void flipSMDAnd(); void lookForZeroConnector(); bool hasZeroConnector(); bool hasSubparts(); void setSubpartID(const QString &); public: static long nextIndex(); static void updateIndex(long index); static const int indexMultiplier; static const QStringList & possibleFolders(); signals: void startSaveInstances(const QString & fileName, ModelPart *, QXmlStreamWriter &); protected: void writeTag(QXmlStreamWriter & streamWriter, QString tagName, QString tagValue); void writeNestedTag(QXmlStreamWriter & streamWriter, QString tagName, const QStringList &values, QString childTag); void writeNestedTag(QXmlStreamWriter & streamWriter, QString tagName, const QHash &values, QString childTag, QString attrName); void commonInit(ItemType type); void saveInstance(QXmlStreamWriter & streamWriter); QList< QPointer > * ensureInstanceTitleIncrements(const QString & prefix); void clearOldInstanceTitle(const QString & title); bool setSubpartInstanceTitle(); protected: QList< QPointer > m_viewItems; ItemType m_type; QPointer m_modelPartShared; QHash > m_connectorHash; QHash > m_busHash; long m_index; // only used at save time to identify model parts in the xml QDomElement m_instanceDomElement; // only used at load time (so far) LocationFlags m_locationFlags; bool m_indexSynched; QString m_instanceTitle; QString m_instanceText; QString m_localTitle; QList m_orderedChildren; protected: static QHash itemTypeNames; static long m_nextIndex; static QStringList m_possibleFolders; }; Q_DECLARE_METATYPE( ModelPart* ); // so we can stash them in a QVariant typedef QList< QPointer > ModelPartList; Q_DECLARE_OPERATORS_FOR_FLAGS(ModelPart::LocationFlags) #endif fritzing-app-0.9.3b/src/model/modelpartshared.cpp000066400000000000000000000604221272363601300220570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #include "modelpartshared.h" #include "../connectors/connectorshared.h" #include "../debugdialog.h" #include "../connectors/busshared.h" #include #include #include void copyPinAttributes(QDomElement & from, QDomElement & to) { to.setAttribute("svgId", from.attribute("svgId")); QString terminalId = from.attribute("terminalId"); if (!terminalId.isEmpty()) { to.setAttribute("terminalId", terminalId); } QString hybrid = from.attribute("hybrid"); if (!hybrid.isEmpty()) { to.setAttribute("hybrid", hybrid); } QString legId = from.attribute("legId"); if (!legId.isEmpty()) { to.setAttribute("legId", legId); } } /////////////////////////////////////////////// ViewImage::ViewImage(ViewLayer::ViewID vi) { flipped = sticky = layers = 0; canFlipVertical = canFlipHorizontal = false; viewID = vi; } /////////////////////////////////////////////// const QString & ModelPartSharedRoot::icon() { return m_icon; } void ModelPartSharedRoot::setIcon(const QString & filename) { m_icon = filename; } const QString & ModelPartSharedRoot::searchTerm() { return m_searchTerm; } void ModelPartSharedRoot::setSearchTerm(const QString & searchTerm) { m_searchTerm = searchTerm; } /////////////////////////////////////////////// const QString ModelPartShared::PartNumberPropertyName = "part number"; ModelPartShared::ModelPartShared() { commonInit(); m_path = ""; } ModelPartShared::ModelPartShared(QDomDocument & domDocument, const QString & path) { commonInit(); m_path = path; setDomDocument(domDocument); } void ModelPartShared::commonInit() { m_moduleID = ""; m_dbid = 0; m_ownerCount = 0; m_hasZeroConnector = m_flippedSMD = m_connectorsInitialized = m_ignoreTerminalPoints = m_needsCopper1 = false; m_superpart = NULL; } ModelPartShared::~ModelPartShared() { foreach (ConnectorShared * connectorShared, m_connectorSharedHash.values()) { delete connectorShared; } m_connectorSharedHash.clear(); foreach (ViewImage * viewImage, m_viewImages.values()) { delete viewImage; } m_viewImages.clear(); foreach (BusShared * busShared, m_buses.values()) { delete busShared; } m_buses.clear(); } bool ModelPartShared::setDomDocument(QDomDocument & domDocument) { static qulonglong one = 1; QDomElement root = domDocument.documentElement(); if (root.isNull()) { return false; } if (root.tagName() != "module") { return false; } loadTagText(root, "title", m_title); loadTagText(root, "label", m_label); loadTagText(root, "version", m_version); loadTagText(root, "author", m_author); loadTagText(root, "description", m_description); loadTagText(root, "url", m_url); loadTagText(root, "taxonomy", m_taxonomy); loadTagText(root, "date", m_date); QDomElement version = root.firstChildElement("version"); if (!version.isNull()) { m_replacedby = version.attribute("replacedby"); } QDomElement spice = root.firstChildElement("spice"); QDomElement line = spice.firstChildElement("line"); while (!line.isNull()) { m_spice += line.text(); m_spice += "\n"; line = line.nextSiblingElement("line"); } QDomElement model = spice.firstChildElement("model"); while (!model.isNull()) { m_spiceModel += model.text(); m_spiceModel += "\n"; model = model.nextSiblingElement("model"); } populateTags(root, m_tags); populateProperties(root, m_properties, m_displayKeys); //foreach (QString key, m_displayKeys) { // DebugDialog::debug("set display " + m_moduleID + " " + key); //} ensurePartNumberProperty(); m_moduleID = root.attribute("moduleId", ""); m_fritzingVersion = root.attribute("fritzingVersion", ""); QDomElement views = root.firstChildElement("views"); if (!views.isNull()) { QDomElement view = views.firstChildElement(); while (!view.isNull()) { ViewLayer::ViewID viewID = ViewLayer::idFromXmlName(view.nodeName()); ViewImage * viewImage = new ViewImage(viewID); m_viewImages.insert(viewID, viewImage); viewImage->canFlipHorizontal = view.attribute("fliphorizontal","").compare("true") == 0; viewImage->canFlipVertical = view.attribute("flipvertical","").compare("true") == 0; QDomElement layers = view.firstChildElement("layers"); if (!layers.isNull()) { viewImage->image = layers.attribute("image", ""); QDomElement layer = layers.firstChildElement("layer"); while (!layer.isNull()) { ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(layer.attribute("layerId")); qulonglong sticky = (layer.attribute("sticky", "").compare("true") == 0) ? 1 : 0; viewImage->layers |= (one << viewLayerID); viewImage->sticky |= (sticky << viewLayerID); layer = layer.nextSiblingElement("layer"); } } addSchematicText(viewImage); view = view.nextSiblingElement(); } } return true; } void ModelPartShared::loadTagText(QDomElement parent, QString tagName, QString &field) { QDomElement tagElement = parent.firstChildElement(tagName); if (!tagElement.isNull()) { field = tagElement.text(); } } void ModelPartShared::populateTags(QDomElement parent, QStringList &list) { QDomElement tags = parent.firstChildElement("tags"); QDomElement tag = tags.firstChildElement("tag"); while (!tag.isNull()) { list << tag.text(); tag = tag.nextSiblingElement("tag"); } } void ModelPartShared::populateProperties(QDomElement parent, QHash &hash, QStringList & displayKeys) { QDomElement properties = parent.firstChildElement("properties"); QDomElement prop = properties.firstChildElement("property"); while (!prop.isNull()) { QString name = prop.attribute("name"); QString value = prop.text(); hash.insert(name.toLower().trimmed(),value); if (prop.attribute("showInLabel", "").compare("yes", Qt::CaseInsensitive) == 0) { displayKeys.append(name); } prop = prop.nextSiblingElement("property"); } } const QString & ModelPartShared::title() { return m_title; } void ModelPartShared::setTitle(QString title) { m_title = title; } const QString & ModelPartShared::label() const { return m_label; } void ModelPartShared::setLabel(QString label) { m_label = label; } const QString & ModelPartShared::uri() { return m_uri; } void ModelPartShared::setUri(QString uri) { m_uri = uri; } const QString & ModelPartShared::version() { return m_version; } void ModelPartShared::setVersion(QString version) { m_version = version; } const QString & ModelPartShared::author() { return m_author; } void ModelPartShared::setAuthor(QString author) { m_author = author; } const QString & ModelPartShared::description() { return m_description; } const QString & ModelPartShared::url() { return m_url; } const QString & ModelPartShared::spice() { return m_spice; } const QString & ModelPartShared::spiceModel() { return m_spiceModel; } void ModelPartShared::setDescription(QString description) { m_description = description; } void ModelPartShared::setSpice(QString spice) { m_spice = spice; } void ModelPartShared::setSpiceModel(QString model) { m_spiceModel = model; } void ModelPartShared::setUrl(QString url) { m_url = url; } const QDate & ModelPartShared::date() { // return *new QDate(QDate::fromString(m_date,Qt::ISODate)); // causes memory leak static QDate tempDate; tempDate = QDate::fromString(m_date,Qt::ISODate); return tempDate; } void ModelPartShared::setDate(QDate date) { m_date = date.toString(Qt::ISODate); } const QString & ModelPartShared::dateAsStr() { return m_date; } void ModelPartShared::setDate(QString date) { m_date = date; } const QStringList & ModelPartShared::tags() { return m_tags; } void ModelPartShared::setTags(const QStringList &tags) { m_tags = tags; } void ModelPartShared::setTag(const QString &tag) { m_tags.append(tag); } QString ModelPartShared::family() { return m_properties.value("family"); } void ModelPartShared::setFamily(const QString &family) { m_properties.insert("family",family); } QHash & ModelPartShared::properties() { return m_properties; } void ModelPartShared::setProperties(const QHash &properties) { m_properties = properties; ensurePartNumberProperty(); } const QString & ModelPartShared::path() { return m_path; } void ModelPartShared::setPath(QString path) { m_path = path; } const QString & ModelPartShared::taxonomy() { return m_taxonomy; } void ModelPartShared::setTaxonomy(QString taxonomy) { m_taxonomy = taxonomy; } const QString & ModelPartShared::moduleID() { return m_moduleID; } void ModelPartShared::setModuleID(QString moduleID) { m_moduleID = moduleID; } const QList< QPointer > ModelPartShared::connectorsShared() { return m_connectorSharedHash.values(); } void ModelPartShared::setConnectorsShared(QList< QPointer > connectors) { for (int i = 0; i < connectors.size(); i++) { ConnectorShared* cs = connectors[i]; m_connectorSharedHash[cs->id()] = cs; } } void ModelPartShared::setConnectorsInitialized(bool init) { m_connectorsInitialized = init; } void ModelPartShared::initConnectors() { if (m_connectorsInitialized) return; QFile file(m_path); QString errorStr; int errorLine; int errorColumn; QDomDocument doc; doc.setContent(&file, &errorStr, &errorLine, &errorColumn); m_connectorsInitialized = true; QDomElement root = doc.documentElement(); if (root.isNull()) { return; } QDomElement connectors = root.firstChildElement("connectors"); if (connectors.isNull()) return; m_ignoreTerminalPoints = (connectors.attribute("ignoreTerminalPoints").compare("true", Qt::CaseInsensitive) == 0); //DebugDialog::debug(QString("part:%1 %2").arg(m_moduleID).arg(m_title)); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { ConnectorShared * connectorShared = new ConnectorShared(connector); m_connectorSharedHash.insert(connectorShared->id(), connectorShared); connector = connector.nextSiblingElement("connector"); } lookForZeroConnector(); QDomElement buses = root.firstChildElement("buses"); if (!buses.isNull()) { QDomElement busElement = buses.firstChildElement("bus"); while (!busElement.isNull()) { BusShared * busShared = new BusShared(busElement, m_connectorSharedHash); m_buses.insert(busShared->id(), busShared); busElement = busElement.nextSiblingElement("bus"); } } //DebugDialog::debug(QString("model %1 has %2 connectors and %3 bus connectors").arg(this->title()).arg(m_connectorSharedHash.count()).arg(m_buses.count()) ); } ConnectorShared * ModelPartShared::getConnectorShared(const QString & id) { return m_connectorSharedHash.value(id); } bool ModelPartShared::ignoreTerminalPoints() { return m_ignoreTerminalPoints; } void ModelPartShared::copy(ModelPartShared* other) { setAuthor(other->author()); setConnectorsShared(other->connectorsShared()); setDate(other->date()); setLabel(other->label()); setDescription(other->description()); setSpice(other->spice()); setSpiceModel(other->spiceModel()); setUrl(other->url()); setFamily(other->family()); setProperties(other->properties()); setTags(other->tags()); setTaxonomy(other->taxonomy()); setTitle(other->title()); setUri(other->uri()); setVersion(other->version()); setSuperpart(other->superpart()); foreach (ViewLayer::ViewID viewID, other->m_viewImages.keys()) { ViewImage * otherViewImage = other->m_viewImages.value(viewID); ViewImage * viewImage = new ViewImage(viewID); viewImage->layers = otherViewImage->layers; viewImage->sticky = otherViewImage->sticky; viewImage->canFlipHorizontal = otherViewImage->canFlipHorizontal; viewImage->canFlipVertical = otherViewImage->canFlipVertical; viewImage->image = otherViewImage->image; m_viewImages.insert(viewID, viewImage); } } void ModelPartShared::setProperty(const QString & key, const QString & value, bool showInLabel) { m_properties.insert(key, value); if (showInLabel) { m_displayKeys.append(key); } } const QString & ModelPartShared::replacedby() { return m_replacedby; } void ModelPartShared::setReplacedby(const QString & replacedby) { m_replacedby = replacedby; } void ModelPartShared::setFlippedSMD(bool f) { m_flippedSMD = f; } bool ModelPartShared::flippedSMD() { return m_flippedSMD; } bool ModelPartShared::needsCopper1() { return m_needsCopper1; } void ModelPartShared::connectorIDs(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID, QStringList & connectorIDs, QStringList & terminalIDs, QStringList & legIDs) { foreach (ConnectorShared * connectorShared, m_connectorSharedHash.values()) { SvgIdLayer * svgIdLayer = connectorShared->fullPinInfo(viewID, viewLayerID); if (svgIdLayer == NULL) { continue; } else { connectorIDs.append(svgIdLayer->m_svgId); terminalIDs.append(svgIdLayer->m_terminalId); legIDs.append(svgIdLayer->m_legId); } } } void ModelPartShared::flipSMDAnd() { if (this->path().startsWith(ResourcePath)) { // assume resources are set up exactly as intended //DebugDialog::debug(QString("skip flip %1").arg(path())); return; } static qulonglong one = 1; ViewImage * viewImage = m_viewImages.value(ViewLayer::PCBView); // needs to be called after initConnectors() LayerList layerList = viewLayers(ViewLayer::PCBView); if (layerList.isEmpty()) return; if (!layerList.contains(ViewLayer::Copper0) && !layerList.contains(ViewLayer::Copper1)) { return; } if (layerList.contains(ViewLayer::Copper0)) { // THT here if (!layerList.contains(ViewLayer::Copper1)) { // fill in missing copper1 layer m_needsCopper1 = true; viewImage->layers |= (one << ViewLayer::Copper1); copyPins(ViewLayer::Copper0, ViewLayer::Copper1); } // prep for placing on the bottom if (layerList.contains(ViewLayer::Silkscreen1) && !layerList.contains(ViewLayer::Silkscreen0)) { //DebugDialog::debug(QString("silk0 %1 %2").arg(this->title()).arg(this->moduleID())); viewImage->layers |= (one << ViewLayer::Silkscreen0); layerList << ViewLayer::Silkscreen0; } if (layerList.contains(ViewLayer::Silkscreen0)) { viewImage->flipped |= (one << ViewLayer::Silkscreen0); } // swap layer for any tht part if (!m_properties.keys().contains("layer")) { // used for swapping part from copper1 to copper0 m_properties.insert("layer", ""); } return; } setFlippedSMD(true); // DebugDialog::debug("set flipped smd " + moduleID()); if (!m_properties.keys().contains("layer")) { // used for swapping part from copper1 to copper0 m_properties.insert("layer", ""); } if (!layerList.contains(ViewLayer::Copper0)) { viewImage->layers |= (one << ViewLayer::Copper0); } viewImage->flipped |= (one << ViewLayer::Copper0); if (layerList.contains(ViewLayer::Silkscreen1) && !layerList.contains(ViewLayer::Silkscreen0)) { viewImage->layers |= (one << ViewLayer::Silkscreen0); layerList << ViewLayer::Silkscreen0; } if (layerList.contains(ViewLayer::Silkscreen0)) { viewImage->flipped |= (one << ViewLayer::Silkscreen0); } //if (this->moduleID() == "df9d072afa2b594ac67b60b4153ff57b_29") { // DebugDialog::debug("alive in here"); //} copyPins(ViewLayer::Copper1, ViewLayer::Copper0); } bool ModelPartShared::hasViewFor(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID, NULL); if (viewImage == NULL) return false; return viewImage->layers != 0; } bool ModelPartShared::hasViewFor(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { ViewImage * viewImage = m_viewImages.value(viewID, NULL); if (viewImage == NULL) return false; qulonglong one = 1; return (viewImage->layers & (one << viewLayerID)) != 0; } QString ModelPartShared::hasBaseNameFor(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID, NULL); if (viewImage == NULL) return ""; return viewImage->image; } const QStringList & ModelPartShared::displayKeys() { return m_displayKeys; } void ModelPartShared::ensurePartNumberProperty() { if (!m_properties.keys().contains(PartNumberPropertyName)) { m_properties.insert(PartNumberPropertyName, ""); m_displayKeys.append(PartNumberPropertyName); } } void ModelPartShared::setDBID(qulonglong dbid) { m_dbid = dbid; } qulonglong ModelPartShared::dbid() { return m_dbid; } const QString & ModelPartShared::fritzingVersion() { return m_fritzingVersion; } void ModelPartShared::setFritzingVersion(const QString & fv) { m_fritzingVersion = fv; } void ModelPartShared::setViewImage(ViewImage * viewImage) { ViewImage * old = m_viewImages.value(viewImage->viewID); if (old) delete old; m_viewImages.insert(viewImage->viewID, viewImage); addSchematicText(viewImage); } const QList ModelPartShared::viewImages() { return m_viewImages.values(); } QString ModelPartShared::imageFileName(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return ""; qulonglong one = 1; if ((viewImage->layers & (one << viewLayerID)) == 0) return ""; // DebugDialog::debug(QString("image filename %1 %2 %3").arg(viewImage->image).arg(viewID).arg(viewLayerID)); return viewImage->image; } QString ModelPartShared::imageFileName(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return ""; return viewImage->image; } void ModelPartShared::setImageFileName(ViewLayer::ViewID viewID, const QString & filename) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return; viewImage->image = filename; } bool ModelPartShared::hasViewID(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return false; return viewImage->layers != 0; } bool ModelPartShared::hasMultipleLayers(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return false; // http://eli.thegreenplace.net/2004/07/30/a-cool-algorithm-for-counting-ones-in-a-bitstring/ qulonglong layers = viewImage->layers; return ((layers & (layers - 1)) != 0); } qulonglong layers(ViewImage * viewImage) { return viewImage->layers; } qulonglong flipped(ViewImage * viewImage) { return viewImage->flipped; } LayerList ModelPartShared::viewLayersFlipped(ViewLayer::ViewID viewID) { return viewLayersAux(viewID, flipped); } LayerList ModelPartShared::viewLayers(ViewLayer::ViewID viewID) { return viewLayersAux(viewID, layers); } LayerList ModelPartShared::viewLayersAux(ViewLayer::ViewID viewID, qulonglong (*accessor)(ViewImage *)) { static QHash ToLayerIDs; LayerList layerList; ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return layerList; // http://eli.thegreenplace.net/2004/07/30/a-cool-algorithm-for-counting-ones-in-a-bitstring/ if (ToLayerIDs.isEmpty()) { qulonglong one = 1; for (int ix = 0; ix < ViewLayer::ViewLayerCount; ix++) { ToLayerIDs.insert(one, (ViewLayer::ViewLayerID) ix); one = one << 1; } } qulonglong layers = accessor(viewImage); while (layers) { qulonglong removeLeast = layers & (layers - 1); qulonglong diff = layers - removeLeast; layerList << ToLayerIDs.value(diff); layers = removeLeast; } return layerList; } bool ModelPartShared::canFlipHorizontal(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return false; return viewImage->canFlipHorizontal; } bool ModelPartShared::canFlipVertical(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return false; return viewImage->canFlipVertical; } bool ModelPartShared::anySticky(ViewLayer::ViewID viewID) { ViewImage * viewImage = m_viewImages.value(viewID); if (viewImage == NULL) return false; return (viewImage->sticky != 0); } void ModelPartShared::addConnector(ConnectorShared * connectorShared) { m_connectorSharedHash.insert(connectorShared->id(), connectorShared); } void ModelPartShared::copyPins(ViewLayer::ViewLayerID from, ViewLayer::ViewLayerID to) { foreach (ConnectorShared * connectorShared, m_connectorSharedHash.values()) { SvgIdLayer * svgIdLayer = connectorShared->fullPinInfo(ViewLayer::PCBView, to); if (svgIdLayer) { // already there continue; } svgIdLayer = connectorShared->fullPinInfo(ViewLayer::PCBView, from); if (svgIdLayer == NULL) { DebugDialog::debug(QString("missing connector in %1").arg(moduleID())); continue; } SvgIdLayer * newSvgIdLayer = svgIdLayer->copyLayer(); newSvgIdLayer->m_svgViewLayerID = to; connectorShared->insertPin(ViewLayer::PCBView, newSvgIdLayer); } } void ModelPartShared::insertBus(BusShared * busShared) { m_buses.insert(busShared->id(), busShared); } void ModelPartShared::lookForZeroConnector() { foreach (QString key, m_connectorSharedHash.keys()) { int ix = IntegerFinder.indexIn(key); if (ix >= 0) { if (IntegerFinder.cap(0) == "0") { m_hasZeroConnector = true; return; } } } } bool ModelPartShared::hasZeroConnector() { return m_hasZeroConnector; } void ModelPartShared::addOwner(QObject * owner) { m_ownerCount++; connect(owner, SIGNAL(destroyed()), this, SLOT(removeOwner())); } void ModelPartShared::removeOwner() { if (--m_ownerCount == 0) { // DebugDialog::debug(QString("last owner %1").arg(moduleID())); // this->deleteLater(); } } void ModelPartShared::addSchematicText(ViewImage * viewImage) { if (viewImage->viewID != ViewLayer::SchematicView) return; qulonglong one = 1; if ((viewImage->layers & (one << ViewLayer::Schematic)) == 0) return; viewImage->canFlipHorizontal = viewImage->canFlipVertical = true; viewImage->layers |= (one << ViewLayer::SchematicText); } bool ModelPartShared::showInLabel(const QString & propertyName) { //foreach (QString key, m_displayKeys) { // DebugDialog::debug("check display " + m_moduleID + " " + key); //} return m_displayKeys.contains(propertyName, Qt::CaseInsensitive); } const QList< QPointer > & ModelPartShared::subparts() { return m_subparts; } void ModelPartShared::addSubpart(ModelPartShared * subpart) { if (subpart == NULL) return; m_subparts.append(subpart); subpart->setSuperpart(this); } ModelPartShared * ModelPartShared::superpart() { return m_superpart; } void ModelPartShared::setSuperpart(ModelPartShared * sup) { m_superpart = sup; } bool ModelPartShared::hasSubparts() { return m_subparts.count() > 0; } void ModelPartShared::setSubpartID(const QString & id) { m_subpartID = id; } const QString & ModelPartShared::subpartID() const { return m_subpartID; } QPointF ModelPartShared::subpartOffset() const { return m_subpartOffset; } void ModelPartShared::setSubpartOffset(QPointF p) { m_subpartOffset = p; } fritzing-app-0.9.3b/src/model/modelpartshared.h000066400000000000000000000154201272363601300215220ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #ifndef MODELPARTSHARED_H #define MODELPARTSHARED_H #include #include #include #include #include #include #include #include "../viewlayer.h" struct ViewImage { ViewLayer::ViewID viewID; qulonglong layers; qulonglong sticky; qulonglong flipped; QString image; bool canFlipHorizontal; bool canFlipVertical; ViewImage(ViewLayer::ViewID); }; class ModelPartShared : public QObject { Q_OBJECT public: ModelPartShared(); ModelPartShared(QDomDocument &, const QString & path); ~ModelPartShared(); //QDomDocument * domDocument(); void copy(ModelPartShared* other); const QString & uri(); void setUri(QString); const QString & moduleID(); void setModuleID(QString); const QString & version(); void setVersion(QString); const QString & author(); void setAuthor(QString); const QString & description(); void setDescription(QString); const QString & spice(); void setSpice(QString); const QString & spiceModel(); void setSpiceModel(QString); const QString & url(); void setUrl(QString); const QString & title(); void setTitle(QString); const QString & label() const; void setLabel(QString); const QDate & date(); void setDate(QDate); const QString & dateAsStr(); void setDate(QString); void setDBID(qulonglong); qulonglong dbid(); const QString & fritzingVersion(); void setFritzingVersion(const QString &); const QList viewImages(); QString imageFileName(ViewLayer::ViewID, ViewLayer::ViewLayerID); void setImageFileName(ViewLayer::ViewID, const QString & filename); QString imageFileName(ViewLayer::ViewID); const QList< QPointer > & subparts(); void addSubpart(ModelPartShared * subpart); bool hasSubparts(); void setSubpartID(const QString &); const QString & subpartID() const; ModelPartShared * superpart(); void setSuperpart(ModelPartShared *); bool anySticky(ViewLayer::ViewID); bool hasMultipleLayers(ViewLayer::ViewID); bool canFlipHorizontal(ViewLayer::ViewID); bool canFlipVertical(ViewLayer::ViewID); bool hasViewID(ViewLayer::ViewID viewID); LayerList viewLayers(ViewLayer::ViewID viewID); LayerList viewLayersFlipped(ViewLayer::ViewID viewID); const QString & path(); void setPath(QString path); const QString & taxonomy(); void setTaxonomy(QString taxonomy); const QList< QPointer > connectorsShared(); void setConnectorsShared(QList< QPointer > connectors); void connectorIDs(ViewLayer::ViewID viewId, ViewLayer::ViewLayerID viewLayerID, QStringList & connectorIDs, QStringList & terminalIDs, QStringList & legIDs); const QStringList &tags(); void setTags(const QStringList &tags); void setTag(const QString &tag); QString family(); void setFamily(const QString &family); QHash & properties(); void setProperties(const QHash &properties); const QStringList & displayKeys(); void initConnectors(); void setConnectorsInitialized(bool); ConnectorShared * getConnectorShared(const QString & id); bool ignoreTerminalPoints(); void setProperty(const QString & key, const QString & value, bool showInLabel); bool showInLabel(const QString & key); const QString & replacedby(); void setReplacedby(const QString & replacedby); void flipSMDAnd(); void setFlippedSMD(bool); bool flippedSMD(); bool needsCopper1(); bool hasViewFor(ViewLayer::ViewID); bool hasViewFor(ViewLayer::ViewID, ViewLayer::ViewLayerID); QString hasBaseNameFor(ViewLayer::ViewID); void setViewImage(ViewImage *); void addConnector(ConnectorShared *); void insertBus(class BusShared *); void lookForZeroConnector(); bool hasZeroConnector(); void addOwner(QObject *); void setSubpartOffset(QPointF); QPointF subpartOffset() const; protected: void loadTagText(QDomElement parent, QString tagName, QString &field); // used to populate de StringList that contains both the and the values void populateTags(QDomElement parent, QStringList &list); void populateProperties(QDomElement parent, QHash &hash, QStringList & displayKeys); void commonInit(); void ensurePartNumberProperty(); void copyPins(ViewLayer::ViewLayerID from, ViewLayer::ViewLayerID to); LayerList viewLayersAux(ViewLayer::ViewID viewID, qulonglong (*accessor)(ViewImage *)); void addSchematicText(ViewImage *); bool setDomDocument(QDomDocument &); protected slots: void removeOwner(); public: static const QString PartNumberPropertyName; protected: //QDomDocument* m_domDocument; QString m_uri; QString m_moduleID; QString m_fritzingVersion; QString m_version; QString m_author; QString m_title; QString m_label; QString m_description; QString m_spice; QString m_spiceModel; QString m_url; QString m_date; QString m_replacedby; QString m_path; QString m_taxonomy; QStringList m_tags; QStringList m_displayKeys; QHash m_properties; QHash > m_connectorSharedHash; QHash m_buses; QHash m_viewImages; bool m_connectorsInitialized; bool m_ignoreTerminalPoints; bool m_flippedSMD; bool m_needsCopper1; // for converting pre-two-layer parts qulonglong m_dbid; bool m_hasZeroConnector; int m_ownerCount; QList< QPointer > m_subparts; QPointer m_superpart; QString m_subpartID; QPointF m_subpartOffset; }; class ModelPartSharedRoot : public ModelPartShared { Q_OBJECT public: const QString & icon(); void setIcon(const QString & filename); const QString & searchTerm(); void setSearchTerm(const QString & searchTerm); protected: QString m_icon; QString m_searchTerm; }; #endif fritzing-app-0.9.3b/src/model/palettemodel.cpp000066400000000000000000000537641272363601300213730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 7004 $: $Author: irascibl@gmail.com $: $Date: 2013-04-29 13:10:59 +0200 (Mo, 29. Apr 2013) $ ********************************************************************/ #include "palettemodel.h" #include #include #include #include #include #include "../debugdialog.h" #include "modelpart.h" #include "../version/version.h" #include "../layerattributes.h" #include "../utils/folderutils.h" #include "../utils/fmessagebox.h" #include "../utils/textutils.h" #include "../items/moduleidnames.h" #include "../items/partfactory.h" static bool JustAppendAllPartsInstances = false; static bool FirstTime = true; QString PaletteModel::s_fzpOverrideFolder; const static QString InstanceTemplate( "\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\n" "\t\t\n"); void setFlip(ViewLayer::ViewID viewID, QXmlStreamReader & xml, QHash & viewImages) { bool fv = xml.attributes().value("flipvertical", "").toString().compare("true") == 0; bool fh = xml.attributes().value("fliphorizontal", "").toString().compare("true") == 0; viewImages.value(viewID)->canFlipHorizontal = fh; viewImages.value(viewID)->canFlipVertical = fv; } ///////////////////////////////////// PaletteModel::PaletteModel() : ModelBase(true) { m_loadedFromFile = false; m_loadingContrib = false; m_fullLoad = false; } PaletteModel::PaletteModel(bool makeRoot, bool doInit) : ModelBase( makeRoot ) { m_loadedFromFile = false; m_loadingContrib = false; m_fullLoad = false; if (doInit) { initParts(false); } } PaletteModel::~PaletteModel() { foreach (ModelPart * modelPart, m_partHash.values()) { delete modelPart; } } void PaletteModel::initParts(bool dbExists) { loadParts(dbExists); if (m_root == NULL) { FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("No parts found.") ); } } void PaletteModel::initNames() { } ModelPart * PaletteModel::retrieveModelPart(const QString & moduleID) { ModelPart * modelPart = m_partHash.value(moduleID, NULL); if (modelPart != NULL) return modelPart; if (m_referenceModel != NULL) { return m_referenceModel->retrieveModelPart(moduleID); } return NULL; } bool PaletteModel::containsModelPart(const QString & moduleID) { return m_partHash.contains(moduleID); } void PaletteModel::loadParts(bool dbExists) { QStringList nameFilters; nameFilters << "*" + FritzingPartExtension; JustAppendAllPartsInstances = true; /// !!!!!!!!!!!!!!!! "JustAppendAllPartsInstances = CreateAllPartsBinFile" /// !!!!!!!!!!!!!!!! is incorrect /// !!!!!!!!!!!!!!!! this flag was originally set up because sometimes we were appending a /// !!!!!!!!!!!!!!!! single instance into an already existing file, /// !!!!!!!!!!!!!!!! so simply appending new items as text gave us xml errors. /// !!!!!!!!!!!!!!!! The problem was that there was no easy way to set the flag directly on the actual /// !!!!!!!!!!!!!!!! function being used: PaletteModel::LoadPart(), though maybe this deserves another look. /// !!!!!!!!!!!!!!!! However, since we're starting from scratch in LoadParts, we can use the much faster /// !!!!!!!!!!!!!!!! file append method. Since CreateAllPartsBinFile is false in release mode, /// !!!!!!!!!!!!!!!! Fritzing was taking forever to start up. if (FirstTime) { } int totalPartCount = 0; emit loadedPart(0, totalPartCount); QDir dir1 = FolderUtils::getAppPartsSubFolder(""); QDir dir2(FolderUtils::getUserPartsPath()); QDir dir3(":/resources/parts"); QDir dir4(s_fzpOverrideFolder); if (m_fullLoad || !dbExists) { // otherwise these will already be in the database countParts(dir1, nameFilters, totalPartCount); countParts(dir3, nameFilters, totalPartCount); } if (!m_fullLoad) { // don't include local parts when doing full load countParts(dir2, nameFilters, totalPartCount); if (!s_fzpOverrideFolder.isEmpty()) { countParts(dir4, nameFilters, totalPartCount); } } emit partsToLoad(totalPartCount); int loadingPart = 0; if (m_fullLoad || !dbExists) { // otherwise these will already be in the database loadPartsAux(dir1, nameFilters, loadingPart, totalPartCount); loadPartsAux(dir3, nameFilters, loadingPart, totalPartCount); } if (!m_fullLoad) { loadPartsAux(dir2, nameFilters, loadingPart, totalPartCount); if (!s_fzpOverrideFolder.isEmpty()) { loadPartsAux(dir4, nameFilters, loadingPart, totalPartCount); } } if (FirstTime) { } JustAppendAllPartsInstances = false; /// !!!!!!!!!!!!!!!! "JustAppendAllPartsInstances = !CreateAllPartsBinFile" /// !!!!!!!!!!!!!!!! is incorrect /// !!!!!!!!!!!!!!!! See above. We simply want to restore the default, so that other functions calling /// !!!!!!!!!!!!!!!! writeInstanceInCommonBin via LoadPart() will use the slower DomDocument methods, /// !!!!!!!!!!!!!!!! since in that case we are appending to an already existing file. FirstTime = false; } void PaletteModel::countParts(QDir & dir, QStringList & nameFilters, int & partCount) { QStringList list = dir.entryList(nameFilters, QDir::Files | QDir::NoSymLinks); partCount += list.size(); QStringList dirs = dir.entryList(QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); for (int i = 0; i < dirs.size(); ++i) { QString temp2 = dirs[i]; dir.cd(temp2); countParts(dir, nameFilters, partCount); dir.cdUp(); } } void PaletteModel::loadPartsAux(QDir & dir, QStringList & nameFilters, int & loadingPart, int totalPartCount) { //QString temp = dir.absolutePath(); QFileInfoList list = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); for (int i = 0; i < list.size(); ++i) { QFileInfo fileInfo = list.at(i); QString path = fileInfo.absoluteFilePath (); //DebugDialog::debug(QString("part path:%1 core? %2").arg(path).arg(m_loadingCore? "true" : "false")); loadPart(path, false); emit loadedPart(++loadingPart, totalPartCount); //DebugDialog::debug("loadedok"); } QStringList dirs = dir.entryList(QDir::AllDirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); for (int i = 0; i < dirs.size(); ++i) { QString temp2 = dirs[i]; dir.cd(temp2); m_loadingContrib = (temp2 == "contrib"); loadPartsAux(dir, nameFilters, loadingPart, totalPartCount); dir.cdUp(); } } ModelPart * PaletteModel::loadPart(const QString & path, bool update) { QFile file(path); if (!file.open(QFile::ReadOnly | QFile::Text)) { FMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Cannot read file %1:\n%2.") .arg(path) .arg(file.errorString())); return NULL; } //DebugDialog::debug(QString("loading %2 %1").arg(path).arg(QTime::currentTime().toString("HH:mm:ss.zzz"))); ModelPart::ItemType type = ModelPart::Part; QString moduleID; QString title; QString propertiesText; QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("Parse error (2) at line %1, column %2:\n%3\n%4") .arg(errorLine) .arg(errorColumn) .arg(errorStr) .arg(path)); return NULL; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { //QMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file is not a Fritzing file (8).")); return NULL; } if (root.tagName() != "module") { //QMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file is not a Fritzing file (9).")); return NULL; } moduleID = root.attribute("moduleId"); if (moduleID.isNull() || moduleID.isEmpty()) { //QMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("The file is not a Fritzing file (10).")); return NULL; } // check if it's a wire QDomElement propertiesElement = root.firstChildElement("properties"); propertiesText = propertiesElement.text(); QDomElement t = root.firstChildElement("title"); TextUtils::findText(t, title); //DebugDialog::debug("module ID " + moduleID); // FIXME: properties is nested right now if (moduleID.compare(ModuleIDNames::WireModuleIDName) == 0) { type = ModelPart::Wire; } else if (moduleID.compare(ModuleIDNames::JumperModuleIDName) == 0) { type = ModelPart::Jumper; } else if (moduleID.endsWith(ModuleIDNames::LogoImageModuleIDName)) { type = ModelPart::Logo; } else if (moduleID.endsWith(ModuleIDNames::LogoTextModuleIDName)) { type = ModelPart::Logo; } else if (moduleID.compare(ModuleIDNames::GroundPlaneModuleIDName) == 0) { type = ModelPart::CopperFill; } else if (moduleID.compare(ModuleIDNames::NoteModuleIDName) == 0) { type = ModelPart::Note; } else if (moduleID.endsWith(ModuleIDNames::TwoPowerModuleIDName)) { type = ModelPart::Part; } else if (moduleID.endsWith(ModuleIDNames::PowerModuleIDName)) { type = ModelPart::Symbol; } else if (moduleID.compare(ModuleIDNames::GroundModuleIDName) == 0) { type = ModelPart::Symbol; } else if (moduleID.endsWith(ModuleIDNames::NetLabelModuleIDName)) { type = ModelPart::Symbol; } else if (moduleID.compare(ModuleIDNames::PowerLabelModuleIDName) == 0) { type = ModelPart::Symbol; } else if (moduleID.compare(ModuleIDNames::RulerModuleIDName) == 0) { type = ModelPart::Ruler; } else if (moduleID.compare(ModuleIDNames::ViaModuleIDName) == 0) { type = ModelPart::Via; } else if (moduleID.compare(ModuleIDNames::HoleModuleIDName) == 0) { type = ModelPart::Hole; } else if (moduleID.endsWith(ModuleIDNames::PerfboardModuleIDName)) { type = ModelPart::Breadboard; } else if (moduleID.endsWith(ModuleIDNames::StripboardModuleIDName)) { type = ModelPart::Breadboard; } else if (moduleID.endsWith(ModuleIDNames::Stripboard2ModuleIDName)) { type = ModelPart::Breadboard; } else if (propertiesText.contains("breadboard", Qt::CaseInsensitive)) { type = ModelPart::Breadboard; } else if (propertiesText.contains("plain vanilla pcb", Qt::CaseInsensitive)) { if (propertiesText.contains("shield", Qt::CaseInsensitive) || title.contains("custom", Qt::CaseInsensitive)) { type = ModelPart::Board; } else { type = ModelPart::ResizableBoard; } } ModelPart * modelPart = new ModelPart(domDocument, path, type); if (modelPart == NULL) return NULL; if (path.startsWith(ResourcePath)) { modelPart->setCore(true); } else if (onCoreList(moduleID)) { // for database entries which have existing fzp files. modelPart->setCore(true); } modelPart->setContrib(m_loadingContrib); QDomElement subparts = root.firstChildElement("schematic-subparts"); QDomElement subpart = subparts.firstChildElement("subpart"); while (!subpart.isNull()) { ModelPart * subModelPart = makeSubpart(modelPart, subpart); m_partHash.insert(subModelPart->moduleID(), subModelPart); subpart = subpart.nextSiblingElement("subpart"); } if (m_partHash.value(moduleID, NULL)) { if(!update) { FMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("The part '%1' at '%2' does not have a unique module id '%3'.") .arg(modelPart->title()) .arg(path) .arg(moduleID)); return NULL; } else { m_partHash[moduleID]->copyStuff(modelPart); } } else { m_partHash.insert(moduleID, modelPart); } if (m_root == NULL) { m_root = modelPart; } else { modelPart->setParent(m_root); } return modelPart; } bool PaletteModel::loadFromFile(const QString & fileName, ModelBase * referenceModel, bool checkViews) { QList modelParts; bool result = ModelBase::loadFromFile(fileName, referenceModel, modelParts, checkViews); if (result) { m_loadedFromFile = true; m_loadedFrom = fileName; } return result; } bool PaletteModel::loadedFromFile() { return m_loadedFromFile; } QString PaletteModel::loadedFrom() { if(m_loadedFromFile) { return m_loadedFrom; } else { return ___emptyString___; } } ModelPart * PaletteModel::addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists) { /*ModelPart * modelPart = loadPart(newPartPath, updateIdAlreadyExists);; if (m_referenceModel != NULL && addToReference) { modelPart = m_referenceModel->addPart(newPartPath, addToReference); if (modelPart != NULL) { return addModelPart( m_root, modelPart); } }*/ ModelPart * modelPart = loadPart(newPartPath, updateIdAlreadyExists); if (m_referenceModel && addToReference) { m_referenceModel->addPart(modelPart,updateIdAlreadyExists); } return modelPart; } void PaletteModel::removePart(const QString &moduleID) { ModelPart *mpToRemove = NULL; QList::const_iterator i; for (i = m_root->children().constBegin(); i != m_root->children().constEnd(); ++i) { ModelPart* mp = qobject_cast(*i); if (mp == NULL) continue; //DebugDialog::debug(QString("remove part %1").arg(mp->moduleID())); if(mp->moduleID() == moduleID) { mpToRemove = mp; break; } } if(mpToRemove) { mpToRemove->setParent(NULL); delete mpToRemove; } //DebugDialog::debug(QString("part hash count %1").arg(m_partHash.count())); m_partHash.remove(moduleID); //DebugDialog::debug(QString("part hash count %1").arg(m_partHash.count())); } void PaletteModel::removeParts() { QList modelParts; foreach (QObject * child, m_root->children()) { ModelPart * modelPart = qobject_cast(child); if (modelPart == NULL) continue; modelParts.append(modelPart); } foreach(ModelPart * modelPart, modelParts) { modelPart->setParent(NULL); m_partHash.remove(modelPart->moduleID()); delete modelPart; } } void PaletteModel::clearPartHash() { foreach (ModelPart * modelPart, m_partHash.values()) { ModelPartShared * modelPartShared = modelPart->modelPartShared(); if (modelPartShared) { modelPart->setModelPartShared(NULL); delete modelPartShared; } delete modelPart; } m_partHash.clear(); } void PaletteModel::setOrdererChildren(QList children) { m_root->setOrderedChildren(children); } QList PaletteModel::search(const QString & searchText, bool allowObsolete) { QList modelParts; QStringList strings = searchText.split(" "); search(m_root, strings, modelParts, allowObsolete); return modelParts; } void PaletteModel::search(ModelPart * modelPart, const QStringList & searchStrings, QList & modelParts, bool allowObsolete) { // TODO: eventually move all this into the database? // or use lucene // or google search api int count = 0; foreach (QString searchString, searchStrings) { bool gotOne = false; if (modelPart->title().contains(searchString, Qt::CaseInsensitive)) { gotOne = true; } else if (modelPart->description().contains(searchString, Qt::CaseInsensitive)) { gotOne = true; } else if (modelPart->url().contains(searchString, Qt::CaseInsensitive)) { gotOne = true; } else if (modelPart->author().contains(searchString, Qt::CaseInsensitive)) { gotOne = true; } else if (modelPart->moduleID().contains(searchString, Qt::CaseInsensitive)) { gotOne = true; } else { foreach (QString string, modelPart->tags()) { if (string.contains(searchString, Qt::CaseInsensitive)) { gotOne = true; break; } } } if (!gotOne) { foreach (QString string, modelPart->properties().values()) { if (string.contains(searchString, Qt::CaseInsensitive)) { gotOne = true; break; } } } if (!gotOne) { foreach (QString string, modelPart->properties().keys()) { if (string.contains(searchString, Qt::CaseInsensitive)) { gotOne = true; break; } } } if (!gotOne) break; count++; } if ((count == searchStrings.count()) && !modelParts.contains(modelPart)) { if (!allowObsolete && modelPart->isObsolete()) { } else { modelParts.append(modelPart); emit addSearchMaximum(1); } } emit addSearchMaximum(modelPart->children().count()); foreach(QObject * child, modelPart->children()) { ModelPart * mp = qobject_cast(child); if (mp == NULL) continue; search(mp, searchStrings, modelParts, allowObsolete); emit incSearch(); } } QList PaletteModel::findContribNoBin() { QList modelParts; foreach (ModelPart * modelPart, m_partHash.values()) { if (modelPart->isContrib()) { if (!modelPart->isInBin()) { modelParts << modelPart; } } } return modelParts; } ModelPart * PaletteModel::makeSubpart(ModelPart * originalModelPart, const QDomElement & originalSubpart) { QString newLabel = originalSubpart.attribute("label"); QString newID = originalSubpart.attribute("id"); QDomElement originalRoot = originalSubpart.ownerDocument().documentElement(); QString moduleID = originalRoot.attribute("moduleId") + "_" + newID; ModelPart * modelPart = retrieveModelPart(moduleID); if (modelPart) { return modelPart; } QDomDocument subdoc = originalSubpart.ownerDocument().cloneNode(true).toDocument(); QDomElement root = subdoc.documentElement(); QDomElement subparts = root.firstChildElement("schematic-subparts"); root.removeChild(subparts); root.setAttribute("moduleId", moduleID); QDomElement label = root.firstChildElement("label"); if (!label.isNull()) { TextUtils::replaceChildText(label, newLabel); } QDomElement views = root.firstChildElement("views"); QDomElement schematicView = views.firstChildElement(ViewLayer::viewIDXmlName(ViewLayer::SchematicView)); QDomElement schematicLayers = schematicView.firstChildElement("layers"); QString image = schematicLayers.attribute("image"); image.replace(".svg", "_" + newID + ".svg"); schematicLayers.setAttribute("image", image); views.removeChild(schematicView); QDomElement view = views.firstChildElement(); while (!view.isNull()) { QDomElement layers = view.firstChildElement("layers"); view.removeChild(layers); layers = schematicLayers.cloneNode(true).toElement(); view.appendChild(layers); view = view.nextSiblingElement(); } views.appendChild(schematicView); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); QHash connectorHash; while (!connector.isNull()) { QDomElement next = connector.nextSiblingElement("connector"); connectors.removeChild(connector); connectorHash.insert(connector.attribute("id"), connector); connector = next; } QDomElement originalConnectors = originalSubpart.firstChildElement("connectors"); QDomElement originalConnector = originalConnectors.firstChildElement("connector"); QString schematicLayerName = ViewLayer::viewLayerXmlNameFromID(ViewLayer::Schematic); while (!originalConnector.isNull()) { QString id = originalConnector.attribute("id"); QDomElement connector = connectorHash.value(id); connectors.appendChild(connector); QDomElement cviews = connector.firstChildElement("views"); QDomElement view = cviews.firstChildElement(); while (!view.isNull()) { QDomElement p = view.firstChildElement("p"); bool firstTime = true; while (!p.isNull()) { QDomElement next = p.nextSiblingElement("p"); if (firstTime) { p.setAttribute("layer", schematicLayerName); firstTime = false; } else { view.removeChild(p); } p = next; } view = view.nextSiblingElement(); } originalConnector = originalConnector.nextSiblingElement("connector"); } QString path = PartFactory::fzpPath() + moduleID + ".fzp"; QString fzp = subdoc.toString(4); TextUtils::writeUtf8(path, fzp); modelPart = new ModelPart(subdoc, path, ModelPart::SchematicSubpart); modelPart->setSubpartID(newID); originalModelPart->modelPartShared()->addSubpart(modelPart->modelPartShared()); m_partHash.insert(moduleID, modelPart); return modelPart; } QList PaletteModel::allParts() { QList modelParts; foreach (ModelPart * modelPart, m_partHash.values()) { if (!modelPart->isObsolete()) modelParts.append(modelPart); } return modelParts; } void PaletteModel::setFzpOverrideFolder(const QString & fzpOverrideFolder) { s_fzpOverrideFolder = fzpOverrideFolder; } fritzing-app-0.9.3b/src/model/palettemodel.h000066400000000000000000000057331272363601300210310ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #ifndef PALETTEMODEL_H #define PALETTEMODEL_H #include "modelpart.h" #include "modelbase.h" #include #include #include #include #include class PaletteModel : public ModelBase { Q_OBJECT public: PaletteModel(); PaletteModel(bool makeRoot, bool doInit); ~PaletteModel(); ModelPart * retrieveModelPart(const QString & moduleID); virtual bool containsModelPart(const QString & moduleID); virtual ModelPart * loadPart(const QString & path, bool update); void clear(); bool loadedFromFile(); QString loadedFrom(); bool loadFromFile(const QString & fileName, ModelBase* referenceModel, bool checkViews); ModelPart * addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists); void removePart(const QString &moduleID); void removeParts(); QList search(const QString & searchText, bool allowObsolete); void clearPartHash(); void setOrdererChildren(QList children); void search(ModelPart * modelPart, const QStringList & searchStrings, QList & modelParts, bool allowObsolete); QList findContribNoBin(); QList allParts(); protected: QHash m_partHash; bool m_loadedFromFile; QString m_loadedFrom; // The file this was loaded from, only if m_loadedFromFile == true bool m_loadingContrib; bool m_fullLoad; signals: void loadedPart(int i, int total); void incSearch(); void addSearchMaximum(int); void partsToLoad(int total); protected: virtual void initParts(bool dbExists); void loadParts(bool dbExists); void loadPartsAux(QDir & dir, QStringList & nameFilters, int & loadedPart, int totalParts); void countParts(QDir & dir, QStringList & nameFilters, int & partCount); ModelPart * makeSubpart(ModelPart * originalModelPart, const QDomElement & originalSubparth); public: static void initNames(); static void setFzpOverrideFolder(const QString &); protected: static QString s_fzpOverrideFolder; }; #endif fritzing-app-0.9.3b/src/model/sketchmodel.cpp000066400000000000000000000042261272363601300212030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "sketchmodel.h" #include "../debugdialog.h" #include #include #include #include #include SketchModel::SketchModel(bool makeRoot) : ModelBase(makeRoot) { } SketchModel::SketchModel(ModelPart * root) : ModelBase(false) { m_root = root; } void SketchModel::removeModelPart(ModelPart * modelPart) { modelPart->setParent(NULL); //DebugDialog::debug(QString("model count %1").arg(root()->children().size())); } ModelPart * SketchModel::findModelPart(const QString & moduleID, long id) { if (m_root == NULL) return NULL; return findModelPartAux(m_root, moduleID, id); } ModelPart * SketchModel::findModelPartAux(ModelPart * modelPart, const QString & moduleID, long id) { if (modelPart->moduleID().compare(moduleID) == 0) { if (modelPart->hasViewID(id)) { return modelPart; } if (modelPart->modelIndex() * ModelPart::indexMultiplier == id) { return modelPart; } } foreach (QObject * child, modelPart->children()) { ModelPart * mp = qobject_cast(child); if (mp == NULL) continue; mp = findModelPartAux(mp, moduleID, id); if (mp != NULL) { return mp; } } return NULL; } fritzing-app-0.9.3b/src/model/sketchmodel.h000066400000000000000000000026751272363601300206560ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SKETCHMODEL_H #define SKETCHMODEL_H #include "modelpart.h" #include "modelbase.h" #include #include class SketchModel : public ModelBase { Q_OBJECT public: SketchModel(bool makeRoot); SketchModel(ModelPart * root); void removeModelPart(ModelPart *); ModelPart * findModelPart(const QString & moduleID, long id); protected: ModelPart * findModelPartAux(ModelPart * modelPart, const QString & moduleID, long id); }; #endif fritzing-app-0.9.3b/src/partsbinpalette/000077500000000000000000000000001272363601300202725ustar00rootroot00000000000000fritzing-app-0.9.3b/src/partsbinpalette/binmanager/000077500000000000000000000000001272363601300223755ustar00rootroot00000000000000fritzing-app-0.9.3b/src/partsbinpalette/binmanager/binmanager.cpp000066400000000000000000001224361272363601300252140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6962 $: $Author: irascibl@gmail.com $: $Date: 2013-04-14 00:08:36 +0200 (So, 14. Apr 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include "binmanager.h" #include "stacktabwidget.h" #include "stacktabbar.h" #include "../../model/modelpart.h" #include "../../mainwindow/mainwindow.h" #include "../../model/palettemodel.h" #include "../../waitpushundostack.h" #include "../../debugdialog.h" #include "../../utils/folderutils.h" #include "../../utils/textutils.h" #include "../../utils/fileprogressdialog.h" #include "../../referencemodel/referencemodel.h" #include "../../items/partfactory.h" #include "../partsbinpalettewidget.h" #include "../partsbinview.h" /////////////////////////////////////////////////////////// QString BinLocation::toString(BinLocation::Location location) { switch (location) { case BinLocation::User: return "user"; case BinLocation::More: return "more"; case BinLocation::App: return "app"; case BinLocation::Outside: default: return "outside"; } } BinLocation::Location BinLocation::fromString(const QString & locationString) { if (locationString.compare("user", Qt::CaseInsensitive) == 0) return BinLocation::User; if (locationString.compare("more", Qt::CaseInsensitive) == 0) return BinLocation::More; if (locationString.compare("app", Qt::CaseInsensitive) == 0) return BinLocation::App; return BinLocation::Outside; } BinLocation::Location BinLocation::findLocation(const QString & filename) { if (filename.startsWith(FolderUtils::getUserBinsPath())) { return BinLocation::User; } else if (filename.startsWith(FolderUtils::getAppPartsSubFolderPath("bins") + "/more")) { return BinLocation::More; } else if (filename.startsWith(FolderUtils::getAppPartsSubFolderPath("bins"))) { return BinLocation::App; } return BinLocation::Outside; } /////////////////////////////////////////////////////////// QString BinManager::Title; QString BinManager::MyPartsBinLocation; QString BinManager::MyPartsBinTemplateLocation; QString BinManager::SearchBinLocation; QString BinManager::SearchBinTemplateLocation; QString BinManager::ContribPartsBinLocation; QString BinManager::TempPartsBinTemplateLocation; QString BinManager::CorePartsBinLocation; QString BinManager::StandardBinStyle = "background-color: gray;"; QString BinManager::CurrentBinStyle = "background-color: black;"; QHash BinManager::StandardBinIcons; BinManager::BinManager(class ReferenceModel *referenceModel, class HtmlInfoView *infoView, WaitPushUndoStack *undoStack, MainWindow* parent) : QFrame(parent) { BinManager::Title = tr("Parts"); m_combinedMenu = NULL; m_showListViewAction = m_showIconViewAction = NULL; m_referenceModel = referenceModel; m_infoView = infoView; m_undoStack = undoStack; m_defaultSaveFolder = FolderUtils::getUserBinsPath(); m_mainWindow = parent; m_currentBin = NULL; connect(this, SIGNAL(savePartAsBundled(const QString &)), m_mainWindow, SLOT(saveBundledPart(const QString &))); m_unsavedBinsCount = 0; QVBoxLayout *lo = new QVBoxLayout(this); m_stackTabWidget = new StackTabWidget(this); m_stackTabWidget->setTabPosition(QTabWidget::West); lo->addWidget(m_stackTabWidget); lo->setMargin(0); lo->setSpacing(0); setMaximumHeight(500); } BinManager::~BinManager() { } void BinManager::initStandardBins() { createCombinedMenu(); createContextMenus(); //DebugDialog::debug("init bin manager"); QList actualLocations; findAllBins(actualLocations); hackLocalContrib(actualLocations); restoreStateAndGeometry(actualLocations); foreach (BinLocation * location, actualLocations) { PartsBinPaletteWidget* bin = newBin(); bin->load(location->path, m_mainWindow->fileProgressDialog(), true); m_stackTabWidget->addTab(bin, bin->icon(), bin->title()); m_stackTabWidget->stackTabBar()->setTabToolTip(m_stackTabWidget->count() - 1, bin->title()); registerBin(bin); delete location; } actualLocations.clear(); //DebugDialog::debug("open core bin"); openCoreBinIn(); //DebugDialog::debug("after core bin"); currentChanged(m_stackTabWidget->currentIndex()); connectTabWidget(); } void BinManager::addBin(PartsBinPaletteWidget* bin) { m_stackTabWidget->addTab(bin, bin->icon(), bin->title()); registerBin(bin); setAsCurrentBin(bin); } void BinManager::registerBin(PartsBinPaletteWidget* bin) { if (!bin->fileName().isEmpty()) { m_openedBins[bin->fileName()] = bin; if (bin->fileName().compare(CorePartsBinLocation) == 0) { bin->setAllowsChanges(false); } else if (bin->fileName().compare(SearchBinLocation) == 0) { bin->setAllowsChanges(false); } else if (bin->fileName().compare(ContribPartsBinLocation) == 0) { bin->setAllowsChanges(false); } else if (bin->fileName().compare(m_tempPartsBinLocation) == 0) { bin->setAllowsChanges(false); } else if (bin->fileName().contains(FolderUtils::getAppPartsSubFolderPath("bins"))) { bin->setAllowsChanges(false); } } } void BinManager::connectTabWidget() { connect( m_stackTabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentChanged(int)) ); connect( m_stackTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int)) ); } void BinManager::insertBin(PartsBinPaletteWidget* bin, int index) { registerBin(bin); m_stackTabWidget->insertTab(index, bin, bin->icon(), bin->title()); m_stackTabWidget->setCurrentIndex(index); } bool BinManager::beforeClosing() { bool retval = true; for(int j = 0; j < m_stackTabWidget->count(); j++) { PartsBinPaletteWidget *bin = qobject_cast(m_stackTabWidget->widget(j)); if (bin && !bin->fastLoaded()) { setAsCurrentTab(bin); retval = retval && bin->beforeClosing(); if(!retval) break; } } if(retval) { saveStateAndGeometry(); } return retval; } void BinManager::setAsCurrentTab(PartsBinPaletteWidget* bin) { m_stackTabWidget->setCurrentWidget(bin); } bool BinManager::hasAlienParts() { return false; } void BinManager::setInfoViewOnHover(bool infoViewOnHover) { Q_UNUSED(infoViewOnHover); } void BinManager::addNewPart(ModelPart *modelPart) { PartsBinPaletteWidget* myPartsBin = getOrOpenMyPartsBin(); myPartsBin->addPart(modelPart); setDirtyTab(myPartsBin); } PartsBinPaletteWidget* BinManager::getOrOpenMyPartsBin() { return getOrOpenBin(MyPartsBinLocation, MyPartsBinTemplateLocation); } PartsBinPaletteWidget* BinManager::getOrOpenSearchBin() { PartsBinPaletteWidget * bin = getOrOpenBin(SearchBinLocation, SearchBinTemplateLocation); if (bin) { bin->setSaveQuietly(true); } return bin; } PartsBinPaletteWidget* BinManager::getOrOpenBin(const QString & binLocation, const QString & binTemplateLocation) { PartsBinPaletteWidget* partsBin = findBin(binLocation); if(!partsBin) { QString fileToOpen = QFileInfo(binLocation).exists() ? binLocation : createIfBinNotExists(binLocation, binTemplateLocation); partsBin = openBinIn(fileToOpen, false); } if (partsBin != NULL && partsBin->fastLoaded()) { partsBin->load(partsBin->fileName(), partsBin, false); } return partsBin; } PartsBinPaletteWidget* BinManager::findBin(const QString & binLocation) { for (int i = 0; i < m_stackTabWidget->count(); i++) { PartsBinPaletteWidget* bin = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i); if(bin->fileName() == binLocation) { return bin; } } return NULL; } QString BinManager::createIfMyPartsNotExists() { return createIfBinNotExists(MyPartsBinLocation, MyPartsBinTemplateLocation); } QString BinManager::createIfSearchNotExists() { return createIfBinNotExists(SearchBinLocation, SearchBinTemplateLocation); } QString BinManager::createIfBinNotExists(const QString & dest, const QString & source) { QString binPath = dest; QFile file(source); FolderUtils::slamCopy(file, binPath); return binPath; } void BinManager::addPartToBin(ModelPart *modelPart, int position) { PartsBinPaletteWidget *bin = m_currentBin? m_currentBin: getOrOpenMyPartsBin(); addPartToBinAux(bin,modelPart,position); } void BinManager::addToMyParts(ModelPart *modelPart) { PartsBinPaletteWidget *bin = getOrOpenMyPartsBin(); if (bin) { addPartToBinAux(bin,modelPart); setAsCurrentTab(bin); } } void BinManager::addToTempPartsBin(ModelPart *modelPart) { PartsBinPaletteWidget *bin = getOrOpenBin(m_tempPartsBinLocation, TempPartsBinTemplateLocation); if (bin) { addPartToBinAux(bin,modelPart); setAsCurrentTab(bin); bin->setDirty(false); } } void BinManager::hideTempPartsBin() { for (int i = 0; i < m_stackTabWidget->count(); i++) { PartsBinPaletteWidget* bin = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i); if (bin->fileName().compare(m_tempPartsBinLocation) == 0) { m_stackTabWidget->removeTab(i); break; } } } void BinManager::addPartToBinAux(PartsBinPaletteWidget *bin, ModelPart *modelPart, int position) { if(bin) { if (bin->fastLoaded()) { bin->load(bin->fileName(), bin, false); } bin->addPart(modelPart, position); setDirtyTab(bin); } } void BinManager::load(const QString& filename) { openBin(filename); } void BinManager::setDirtyTab(PartsBinPaletteWidget* w, bool dirty) { /* if (!w->windowTitle().contains(FritzingWindow::QtFunkyPlaceholder)) { // trying to deal with the warning in QWidget::setWindowModified // but setting the title here doesn't work QString t = w->windowTitle(); if (t.isEmpty()) t = " "; w->setWindowTitle(t); } */ w->setWindowModified(dirty); if(m_stackTabWidget != NULL) { int tabIdx = m_stackTabWidget->indexOf(w); m_stackTabWidget->setTabText(tabIdx, w->title()+(dirty? " *": "")); } else { qWarning() << tr("BinManager::setDirtyTab: Couldn't set the bin '%1' as dirty").arg(w->title()); } } void BinManager::updateTitle(PartsBinPaletteWidget* w, const QString& newTitle) { if(m_stackTabWidget != NULL) { m_stackTabWidget->setTabText(m_stackTabWidget->indexOf(w), newTitle+" *"); setDirtyTab(w); } else { qWarning() << tr("BinManager::updateTitle: Couldn't set the bin '%1' as dirty").arg(w->title()); } } PartsBinPaletteWidget* BinManager::newBinIn() { PartsBinPaletteWidget* bin = newBin(); bin->setPaletteModel(new PaletteModel(true, false), true); bin->setTitle(tr("New bin (%1)").arg(++m_unsavedBinsCount)); insertBin(bin, m_stackTabWidget->count()); bin->setReadOnly(false); renameBin(); return bin; } PartsBinPaletteWidget* BinManager::openBinIn(QString fileName, bool fastLoad) { if(fileName.isNull() || fileName.isEmpty()) { fileName = QFileDialog::getOpenFileName( this, tr("Select a Fritzing Parts Bin file to open"), m_defaultSaveFolder, tr("Fritzing Bin Files (*%1 *%2);;Fritzing Bin (*%1);;Fritzing Shareable Bin (*%2)") .arg(FritzingBinExtension).arg(FritzingBundledBinExtension) ); if (fileName.isNull()) return NULL; } PartsBinPaletteWidget* bin = NULL; bool createNewOne = false; if(m_openedBins.contains(fileName)) { bin = m_openedBins[fileName]; if(m_stackTabWidget) { m_stackTabWidget->setCurrentWidget(bin); } else { m_openedBins.remove(fileName); createNewOne = true; } } else { createNewOne = true; } if(createNewOne) { bin = newBin(); if(bin->open(fileName, bin, fastLoad)) { m_openedBins[fileName] = bin; insertBin(bin, m_stackTabWidget->count()); // to force the user to take a decision of what to do with the imported parts if(fileName.endsWith(FritzingBundledBinExtension)) { setDirtyTab(bin); } } } if (!fastLoad) { setAsCurrentBin(bin); } return bin; } PartsBinPaletteWidget* BinManager::openCoreBinIn() { PartsBinPaletteWidget* bin = findBin(CorePartsBinLocation); if (bin != NULL) { setAsCurrentTab(bin); } else { bin = newBin(); bin->setAllowsChanges(false); bin->load(BinManager::CorePartsBinLocation, bin, false); insertBin(bin, 0); } setAsCurrentBin(bin); return bin; } PartsBinPaletteWidget* BinManager::newBin() { PartsBinPaletteWidget* bin = new PartsBinPaletteWidget(m_referenceModel, m_infoView, m_undoStack,this); connect( bin, SIGNAL(fileNameUpdated(PartsBinPaletteWidget*, const QString&, const QString&)), this, SLOT(updateFileName(PartsBinPaletteWidget*, const QString&, const QString&)) ); connect( bin, SIGNAL(focused(PartsBinPaletteWidget*)), this, SLOT(setAsCurrentBin(PartsBinPaletteWidget*)) ); connect(bin, SIGNAL(saved(bool)), m_mainWindow, SLOT(binSaved(bool))); connect(m_mainWindow, SIGNAL(alienPartsDismissed()), bin, SLOT(removeAlienParts())); return bin; } void BinManager::currentChanged(int index) { for (int i = 0; i < m_stackTabWidget->count(); i++) { PartsBinPaletteWidget* bin = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i); if (bin == NULL) continue; if (!bin->hasMonoIcon()) continue; if (i == index) { m_stackTabWidget->setTabIcon(i, bin->icon()); } else { m_stackTabWidget->setTabIcon(i, bin->monoIcon()); } } PartsBinPaletteWidget *bin = getBin(index); if (bin) setAsCurrentBin(bin); } void BinManager::setAsCurrentBin(PartsBinPaletteWidget* bin) { if (bin == NULL) { qWarning() << tr("Cannot set a NULL bin as the current one"); return; } if (bin->fastLoaded()) { bin->load(bin->fileName(), bin, false); } if (m_currentBin == bin) return; if (bin->fileName().compare(SearchBinLocation) == 0) { bin->focusSearch(); } /* // jrc 3-july-2013 commented out this stylesheet change: // it causes the tab bar to lose its scroll position // the stylesheet change is commented out in the qss file, so visually it's a no-op QString style = m_mainWindow->styleSheet(); if(m_currentBin && m_stackTabWidget) { StackTabBar *currTabBar = m_stackTabWidget->stackTabBar(); currTabBar->setProperty("current","false"); currTabBar->setStyleSheet(""); currTabBar->setStyleSheet(style); } if(m_stackTabWidget) { m_currentBin = bin; StackTabBar *currTabBar = m_stackTabWidget->stackTabBar(); currTabBar->setProperty("current","true"); currTabBar->setStyleSheet(""); currTabBar->setStyleSheet(style); } */ } void BinManager::closeBinIn(int index) { if (m_stackTabWidget->count() == 1) return; int realIndex = index == -1? m_stackTabWidget->currentIndex(): index; PartsBinPaletteWidget *w = getBin(realIndex); if(w && w->beforeClosing()) { m_stackTabWidget->removeTab(realIndex); m_openedBins.remove(w->fileName()); } } PartsBinPaletteWidget* BinManager::getBin(int index) { return qobject_cast(m_stackTabWidget->widget(index)); } PartsBinPaletteWidget* BinManager::currentBin() { return qobject_cast(m_stackTabWidget->currentWidget()); } void BinManager::updateFileName(PartsBinPaletteWidget* bin, const QString &newFileName, const QString &oldFilename) { m_openedBins.remove(oldFilename); m_openedBins[newFileName] = bin; } void BinManager::saveStateAndGeometry() { QSettings settings; settings.remove("bins2"); // clean up previous state settings.beginGroup("bins2"); for(int j = m_stackTabWidget->count() - 1; j >= 0; j--) { PartsBinPaletteWidget *bin = qobject_cast(m_stackTabWidget->widget(j)); if (bin) { settings.beginGroup(QString::number(j)); settings.setValue("location", BinLocation::toString(bin->location())); settings.setValue("title", bin->title()); settings.setValue("path", bin->fileName()); settings.endGroup(); } } settings.endGroup(); } void BinManager::restoreStateAndGeometry(QList & actualLocations) { QList theoreticalLocations; QSettings settings; settings.beginGroup("bins2"); int size = settings.childGroups().size(); if (size == 0) { // first time readTheoreticalLocations(theoreticalLocations); } else { for (int i = 0; i < size; ++i) { settings.beginGroup(QString::number(i)); BinLocation * location = new BinLocation; location->location = BinLocation::fromString(settings.value("location").toString()); location->path = settings.value("path").toString(); location->title = settings.value("title").toString(); theoreticalLocations.append(location); settings.endGroup(); } } foreach (BinLocation * location, actualLocations) { location->marked = false; } foreach (BinLocation * tLocation, theoreticalLocations) { foreach (BinLocation * aLocation, actualLocations) { if (aLocation->title.compare(tLocation->title) == 0 && aLocation->location == tLocation->location) { aLocation->marked = true; break; } } } QList tempLocations(actualLocations); actualLocations.clear(); foreach (BinLocation * tLocation, theoreticalLocations) { tLocation->marked = false; bool gotOne = false; for (int ix = 0; ix < tempLocations.count(); ix++) { BinLocation * aLocation = tempLocations[ix]; if (aLocation->title.compare(tLocation->title) == 0 && aLocation->location == tLocation->location) { gotOne = true; actualLocations.append(aLocation); tempLocations.removeAt(ix); //DebugDialog::debug("adding loc 1 " + aLocation->path); break; } } if (gotOne) continue; if (tLocation->title == "___*___") { for (int ix = 0; ix < tempLocations.count(); ix++) { BinLocation * aLocation = tempLocations[ix]; if (!aLocation->marked && aLocation->location == tLocation->location) { gotOne = true; actualLocations.append(aLocation); tempLocations.removeAt(ix); //DebugDialog::debug("adding loc 2 " + aLocation->path); break; } } } if (gotOne) continue; if (!tLocation->path.isEmpty()) { QFileInfo info(tLocation->path); if (info.exists()) { //DebugDialog::debug("adding loc 3 " + tLocation->path); actualLocations.append(tLocation); tLocation->marked = true; } } } foreach (BinLocation * tLocation, theoreticalLocations) { if (!tLocation->marked) { delete tLocation; } } // catch the leftovers actualLocations.append(tempLocations); //foreach (BinLocation * aLocation, tempLocations) { //DebugDialog::debug("adding loc 4 " + aLocation->path); //} } void BinManager::readTheoreticalLocations(QList & theoreticalLocations) { QFile file(":/resources/bins/order.xml"); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse order.xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } QDomElement bin = domDocument.documentElement().firstChildElement("bin"); while (!bin.isNull()) { BinLocation * location = new BinLocation; location->title = bin.attribute("title", ""); location->location = BinLocation::fromString(bin.attribute("location", "")); theoreticalLocations.append(location); bin = bin.nextSiblingElement("bin"); } } void BinManager::hackLocalContrib(QList & locations) { // with release 0.7.12, there is no more local contrib bin // so clear out existing local contrib bins by copying parts to mine bin BinLocation * localContrib = NULL; BinLocation * myParts = NULL; foreach (BinLocation * location, locations) { if (location->location == BinLocation::User) { if (location->title == "Contributed Parts") { localContrib = location; } else if (location->title == "My Parts") { myParts = location; } } } if (localContrib == NULL) return; if (myParts == NULL) { createIfBinNotExists(MyPartsBinLocation, MyPartsBinTemplateLocation); myParts = new BinLocation; myParts->location = BinLocation::User; myParts->path = MyPartsBinLocation; QString icon; getBinTitle(myParts->path, myParts->title, icon); locations.append(myParts); } QString errorStr; int errorLine; int errorColumn; QFile contribFile(localContrib->path); QDomDocument contribDoc; bool result = contribDoc.setContent(&contribFile, true, &errorStr, &errorLine, &errorColumn); locations.removeOne(localContrib); contribFile.close(); bool removed = contribFile.remove(); if (!removed) { DebugDialog::debug("failed to remove contrib bin"); } if (!result) return; QFile myPartsFile(myParts->path); QDomDocument myPartsDoc; if (!myPartsDoc.setContent(&myPartsFile, true, &errorStr, &errorLine, &errorColumn)) { return; } QDomElement myPartsRoot = myPartsDoc.documentElement(); QDomElement myPartsInstances = myPartsRoot.firstChildElement("instances"); bool changed = false; QDomElement contribRoot = contribDoc.documentElement(); QDomElement contribInstances = contribRoot.firstChildElement("instances"); QDomElement contribInstance = contribInstances.firstChildElement("instance"); while (!contribInstance.isNull()) { QString moduleIDRef = contribInstance.attribute("moduleIdRef"); QDomElement myPartsInstance = myPartsInstances.firstChildElement("instance"); bool already = false; while (!myPartsInstance.isNull()) { QString midr = myPartsInstance.attribute("moduleIdRef"); if (midr == moduleIDRef) { already = true; break; } myPartsInstance = myPartsInstance.nextSiblingElement("instance"); } if (!already) { QDomNode node = contribInstance.cloneNode(true); myPartsInstances.appendChild(node); changed = true; } contribInstance = contribInstance.nextSiblingElement("instance"); } if (changed) { TextUtils::writeUtf8(myParts->path, myPartsDoc.toString(0)); } } void BinManager::findAllBins(QList & locations) { BinLocation * location = new BinLocation; location->location = BinLocation::App; location->path = CorePartsBinLocation; QString icon; getBinTitle(location->path, location->title, icon); locations.append(location); location = new BinLocation; location->location = BinLocation::App; location->path = ContribPartsBinLocation; getBinTitle(location->path, location->title, icon); locations.append(location); QDir userBinsDir(FolderUtils::getUserBinsPath()); findBins(userBinsDir, locations, BinLocation::User); QDir dir(FolderUtils::getAppPartsSubFolderPath("bins")); dir.cd("more"); findBins(dir, locations, BinLocation::More); } void BinManager::findBins(QDir & dir, QList & locations, BinLocation::Location loc) { QStringList filters; filters << "*"+FritzingBinExtension; QFileInfoList files = dir.entryInfoList(filters); foreach(QFileInfo info, files) { BinLocation * location = new BinLocation; location->path = info.absoluteFilePath(); location->location = loc; QString icon; getBinTitle(location->path, location->title, icon); locations.append(location); } } bool BinManager::getBinTitle(const QString & filename, QString & binName, QString & iconName) { QFile file(filename); file.open(QFile::ReadOnly); QXmlStreamReader xml(&file); xml.setNamespaceProcessing(false); while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name().toString().compare("module") == 0) { iconName = xml.attributes().value("icon").toString(); } else if (xml.name().toString().compare("title") == 0) { binName = xml.readElementText(); return true; } break; default: break; } } return false; } void BinManager::tabCloseRequested(int index) { closeBinIn(index); } void BinManager::addPartTo(PartsBinPaletteWidget* bin, ModelPart* mp, bool setDirty) { if(mp) { bool alreadyIn = bin->contains(mp->moduleID()); bin->addPart(mp); if(!alreadyIn && setDirty) { setDirtyTab(bin,true); } } } void BinManager::editSelectedPartNewFrom(PartsBinPaletteWidget* bin) { ItemBase * itemBase = bin->selectedItemBase(); m_mainWindow->getPartsEditorNewAnd(itemBase); } bool BinManager::isTabReorderingEvent(QDropEvent* event) { const QMimeData *m = event->mimeData(); QStringList formats = m->formats(); return formats.contains("action") && (m->data("action") == "tab-reordering"); } const QString &BinManager::getSelectedModuleIDFromSketch() { return m_mainWindow->selectedModuleID(); } QList BinManager::openedBinsActions(const QString &moduleId) { QMap titlesAndActions; // QMap sorts values by key for (int i = 0; i < m_stackTabWidget->count(); i++) { PartsBinPaletteWidget* pppw = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i); if (pppw->readOnly()) continue; QAction *act = pppw->addPartToMeAction(); act->setEnabled(!pppw->contains(moduleId)); titlesAndActions[pppw->title()] = act; } return titlesAndActions.values(); } void BinManager::openBin(const QString &filename) { openBinIn(filename, false); } MainWindow* BinManager::mainWindow() { return m_mainWindow; } void BinManager::initNames() { BinManager::MyPartsBinLocation = FolderUtils::getUserBinsPath()+"/my_parts.fzb"; BinManager::MyPartsBinTemplateLocation =":/resources/bins/my_parts.fzb"; BinManager::SearchBinLocation = FolderUtils::getUserBinsPath()+"/search.fzb"; BinManager::SearchBinTemplateLocation =":/resources/bins/search.fzb"; BinManager::ContribPartsBinLocation = FolderUtils::getAppPartsSubFolderPath("bins")+"/contribParts.fzb"; BinManager::CorePartsBinLocation = FolderUtils::getAppPartsSubFolderPath("bins")+"/core.fzb"; BinManager::TempPartsBinTemplateLocation =":/resources/bins/temp.fzb"; StandardBinIcons.insert(BinManager::MyPartsBinLocation, "Mine.png"); StandardBinIcons.insert(BinManager::SearchBinLocation, "Search.png"); StandardBinIcons.insert(BinManager::ContribPartsBinLocation, "Contrib.png"); StandardBinIcons.insert(BinManager::CorePartsBinLocation, "Core.png"); } void BinManager::search(const QString & searchText) { PartsBinPaletteWidget * searchBin = getOrOpenSearchBin(); if (searchBin == NULL) return; FileProgressDialog progress(tr("Searching..."), 0, this); progress.setIncValueMod(10); connect(m_referenceModel, SIGNAL(addSearchMaximum(int)), &progress, SLOT(addMaximum(int))); connect(m_referenceModel, SIGNAL(incSearch()), &progress, SLOT(incValue())); QList modelParts = m_referenceModel->search(searchText, false); progress.setIncValueMod(1); searchBin->removeParts(); foreach (ModelPart * modelPart, modelParts) { //DebugDialog::debug(modelPart->title()); if (modelPart->itemType() == ModelPart::SchematicSubpart) { } else if (modelPart->moduleID().contains(PartFactory::OldSchematicPrefix)) { } else { this->addPartTo(searchBin, modelPart, false); } progress.incValue(); } setDirtyTab(searchBin); } bool BinManager::currentViewIsIconView() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return true; return bin->currentViewIsIconView(); } void BinManager::toIconView() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; bin->toIconView(); } void BinManager::toListView() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; bin->toListView(); } void BinManager::updateBinCombinedMenuCurrent() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; updateBinCombinedMenu(bin); } void BinManager::updateBinCombinedMenu(PartsBinPaletteWidget * bin) { if (m_combinedMenu == NULL) return; m_saveBinAction->setEnabled(bin->allowsChanges()); m_renameBinAction->setEnabled(bin->canClose()); m_closeBinAction->setEnabled(bin->canClose()); m_deleteBinAction->setEnabled(bin->canClose()); ItemBase *itemBase = bin->selectedItemBase(); bool enabled = (itemBase != NULL); m_editPartNewAction->setEnabled(enabled && itemBase->canEditPart()); bool enableAnyway = false; #ifndef QT_NO_DEBUG enableAnyway = true; #endif m_exportPartAction->setEnabled(enabled && (!itemBase->modelPart()->isCore() || enableAnyway)); m_removePartAction->setEnabled(enabled && bin->allowsChanges()); m_findPartAction->setEnabled(enabled); } void BinManager::createCombinedMenu() { m_combinedMenu = new QMenu(tr("Bin"), this); m_openAction = new QAction(tr("Import..."), this); m_openAction->setStatusTip(tr("Load a Fritzing part (.fzpz), or a Fritzing parts bin (.fzb, .fzbz)")); connect(m_openAction, SIGNAL(triggered()), this, SLOT(mainLoad())); m_newBinAction = new QAction(tr("New Bin..."), this); m_newBinAction->setStatusTip(tr("Create a new parts bin")); connect(m_newBinAction, SIGNAL(triggered()),this, SLOT(newBinIn())); m_closeBinAction = new QAction(tr("Close Bin"), this); m_closeBinAction->setStatusTip(tr("Close parts bin")); connect(m_closeBinAction, SIGNAL(triggered()),this, SLOT(closeBin())); m_deleteBinAction = new QAction(tr("Delete Bin"), this); m_deleteBinAction->setStatusTip(tr("Delete parts bin")); connect(m_deleteBinAction, SIGNAL(triggered()),this, SLOT(deleteBin())); m_saveBinAction = new QAction(tr("Save Bin"), this); m_saveBinAction->setStatusTip(tr("Save parts bin")); connect(m_saveBinAction, SIGNAL(triggered()),this, SLOT(saveBin())); m_saveBinAsAction = new QAction(tr("Save Bin As..."), this); m_saveBinAsAction->setStatusTip(tr("Save parts bin as...")); connect(m_saveBinAsAction, SIGNAL(triggered()),this, SLOT(saveBinAs())); m_saveBinAsBundledAction = new QAction(tr("Export Bin..."), this); m_saveBinAsBundledAction->setStatusTip(tr("Save parts bin in compressed format...")); connect(m_saveBinAsBundledAction, SIGNAL(triggered()),this, SLOT(saveBundledBin())); m_renameBinAction = new QAction(tr("Rename Bin..."), this); m_renameBinAction->setStatusTip(tr("Rename parts bin...")); connect(m_renameBinAction, SIGNAL(triggered()),this, SLOT(renameBin())); m_copyToSketchAction = new QAction(tr("Copy to Sketch"), this); m_copyToSketchAction->setStatusTip(tr("Copy all the parts in the bin to a sketch")); connect(m_copyToSketchAction, SIGNAL(triggered()),this, SLOT(copyToSketch())); m_copyAllToSketchAction = new QAction(tr("Copy all to Sketch"), this); m_copyAllToSketchAction->setStatusTip(tr("Copy all loaded parts to the sketch")); connect(m_copyAllToSketchAction, SIGNAL(triggered()),this, SLOT(copyAllToSketch())); m_showListViewAction = new QAction(tr("Show Bin in List View"), this); m_showListViewAction->setCheckable(true); m_showListViewAction->setStatusTip(tr("Display parts as a list")); connect(m_showListViewAction, SIGNAL(triggered()),this, SLOT(toListView())); m_showIconViewAction = new QAction(tr("Show Bin in Icon View"), this); m_showIconViewAction->setCheckable(true); m_showIconViewAction->setStatusTip(tr("Display parts as icons")); connect(m_showIconViewAction, SIGNAL(triggered()),this, SLOT(toIconView())); m_combinedMenu->addAction(m_openAction); m_combinedMenu->addSeparator(); m_combinedMenu->addAction(m_newBinAction); m_combinedMenu->addAction(m_closeBinAction); m_combinedMenu->addAction(m_deleteBinAction); m_combinedMenu->addAction(m_saveBinAction); m_combinedMenu->addAction(m_saveBinAsAction); m_combinedMenu->addAction(m_saveBinAsBundledAction); m_combinedMenu->addAction(m_renameBinAction); #ifndef QT_NO_DEBUG m_combinedMenu->addAction(m_copyToSketchAction); m_combinedMenu->addAction(m_copyAllToSketchAction); #endif m_combinedMenu->addSeparator(); m_combinedMenu->addAction(m_showIconViewAction); m_combinedMenu->addAction(m_showListViewAction); m_editPartNewAction = new QAction(tr("Edit Part (new parts editor)..."),this); m_exportPartAction = new QAction(tr("Export Part..."),this); m_removePartAction = new QAction(tr("Remove Part"),this); m_findPartAction = new QAction(tr("Find Part in Sketch"),this); connect(m_editPartNewAction, SIGNAL(triggered()),this, SLOT(editSelectedNew())); connect(m_exportPartAction, SIGNAL(triggered()),this, SLOT(exportSelected())); connect(m_removePartAction, SIGNAL(triggered()),this, SLOT(removeSelected())); connect(m_findPartAction, SIGNAL(triggered()),this, SLOT(findSelected())); connect(m_combinedMenu, SIGNAL(aboutToShow()), this, SLOT(updateBinCombinedMenuCurrent())); m_combinedMenu->addSeparator(); m_combinedMenu->addAction(m_editPartNewAction); m_combinedMenu->addAction(m_exportPartAction); m_combinedMenu->addAction(m_removePartAction); m_combinedMenu->addAction(m_findPartAction); } void BinManager::createContextMenus() { m_binContextMenu = new QMenu(this); m_binContextMenu->addAction(m_closeBinAction); m_binContextMenu->addAction(m_deleteBinAction); m_binContextMenu->addAction(m_saveBinAction); m_binContextMenu->addAction(m_saveBinAsAction); m_binContextMenu->addAction(m_saveBinAsBundledAction); m_binContextMenu->addAction(m_renameBinAction); m_partContextMenu = new QMenu(this); m_partContextMenu->addAction(m_editPartNewAction); m_partContextMenu->addAction(m_exportPartAction); m_partContextMenu->addAction(m_removePartAction); m_partContextMenu->addAction(m_findPartAction); } void BinManager::closeBin() { closeBinIn(-1); } void BinManager::deleteBin() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; QMessageBox::StandardButton answer = QMessageBox::question( this, tr("Delete bin"), tr("Do you really want to delete bin '%1'? This action cannot be undone.").arg(bin->title()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); // TODO: make button texts translatable if (answer != QMessageBox::Yes) return; QString filename = bin->fileName(); closeBinIn(-1); QFile::remove(filename); } void BinManager::importPartToMineBin(const QString & filename) { if (!filename.isEmpty() && !filename.isNull()) { PartsBinPaletteWidget * bin = getOrOpenBin(MyPartsBinLocation, MyPartsBinTemplateLocation); if (bin == NULL) return; setAsCurrentTab(bin); importPart(filename, bin); } } void BinManager::importPartToCurrentBin(const QString & filename) { if (!filename.isEmpty() && !filename.isNull()) { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; importPart(filename, bin); } } void BinManager::importPart(const QString & filename, PartsBinPaletteWidget * bin) { ModelPart *mp = m_mainWindow->loadBundledPart(filename, !bin->allowsChanges()); if (bin->allowsChanges()) { addPartTo(bin, mp, true); } } void BinManager::editSelectedNew() { editSelectedPartNewFrom(currentBin()); } void BinManager::renameBin() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; if (!currentBin()->allowsChanges()) { // TODO: disable menu item instead QMessageBox::warning(this, tr("Read-only bin"), tr("This bin cannot be renamed.")); return; } bool ok; QString newTitle = QInputDialog::getText( this, tr("Rename bin"), tr("Please choose a name for the bin:"), QLineEdit::Normal, bin->title(), &ok ); if(ok) { bin->setTitle(newTitle); m_stackTabWidget->stackTabBar()->setTabToolTip(m_stackTabWidget->currentIndex(), newTitle); bin->addPartToMeAction()->setText(newTitle); updateTitle(bin, newTitle); } } void BinManager::saveBin() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; bool result = bin->save(); if (result) setDirtyTab(currentBin(),false); } void BinManager::saveBinAs() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; bin->saveAs(); } void BinManager::updateViewChecks(bool iconView) { if (m_showListViewAction == NULL) return; if (m_showIconViewAction == NULL) return; if (iconView) { m_showListViewAction->setChecked(false); m_showIconViewAction->setChecked(true); } else { m_showListViewAction->setChecked(true); m_showIconViewAction->setChecked(false); } } QMenu * BinManager::binContextMenu(PartsBinPaletteWidget * bin) { updateBinCombinedMenu(bin); return m_binContextMenu; } QMenu * BinManager::partContextMenu(PartsBinPaletteWidget * bin) { updateBinCombinedMenu(bin); return m_partContextMenu; } QMenu * BinManager::combinedMenu(PartsBinPaletteWidget * bin) { updateBinCombinedMenu(bin); return m_combinedMenu; } QMenu * BinManager::combinedMenu() { return m_combinedMenu; } bool BinManager::removeSelected() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return false; ModelPart * mp = bin->selectedModelPart(); if (mp == NULL) return false; if (m_mainWindow->anyUsePart(mp->moduleID())) { QMessageBox::warning(this, tr("Remove from Bin"), tr("Unable to remove part '%1'--it is in use in a sketch").arg(mp->title())); return false; } QMessageBox::StandardButton answer = QMessageBox::question( this, tr("Remove from bin"), tr("Do you really want to remove '%1' from the bin? This operation cannot be undone.").arg(mp->title()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); // TODO: make button texts translatable if(answer != QMessageBox::Yes) return false; m_undoStack->push(new QUndoCommand("Parts bin: part removed")); bin->removePart(mp->moduleID(), mp->path()); bin->setDirty(); return true; } void BinManager::findSelected() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; ModelPart * mp = bin->selectedModelPart(); if (mp == NULL) return; m_mainWindow->selectPartsWithModuleID(mp); } void BinManager::exportSelected() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; ModelPart * mp = bin->selectedModelPart(); if (mp == NULL) return; emit savePartAsBundled(mp->moduleID()); } void BinManager::saveBundledBin() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; bin->saveBundledBin(); } void BinManager::setTabIcon(PartsBinPaletteWidget* w, QIcon * icon) { if (m_stackTabWidget != NULL) { int tabIdx = m_stackTabWidget->indexOf(w); m_stackTabWidget->setTabIcon(tabIdx, *icon); } } void BinManager::copyFilesToContrib(ModelPart * mp, QWidget * originator) { PartsBinPaletteWidget * bin = qobject_cast(originator); if (bin == NULL) return; if (bin->fileName().compare(m_tempPartsBinLocation) != 0) return; // only copy from temp bin QString path = mp->path(); if (path.isEmpty()) return; QFileInfo info(path); QFile fzp(path); QString parts = FolderUtils::getUserPartsPath(); FolderUtils::slamCopy(fzp, parts + "/contrib/" + info.fileName()); QString prefix = parts + "/svg/contrib/"; QDir dir = info.absoluteDir(); dir.cdUp(); dir.cd("svg"); dir.cd("contrib"); QList viewIDs; viewIDs << ViewLayer::IconView << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView; foreach (ViewLayer::ViewID viewID, viewIDs) { QString fn = mp->hasBaseNameFor(viewID); if (!fn.isEmpty()) { QFile svg(dir.absoluteFilePath(fn)); FolderUtils::slamCopy(svg, prefix + fn); } } } bool BinManager::isTempPartsBin(PartsBinPaletteWidget * bin) { return bin->fileName().compare(m_tempPartsBinLocation) == 0; } void BinManager::setTempPartsBinLocation(const QString & name) { m_tempPartsBinLocation = name; //StandardBinIcons.insert(m_tempPartsBinLocation, "Temp.png"); } void BinManager::mainLoad() { QString path = m_defaultSaveFolder; QString fileName = FolderUtils::getOpenFileName( this, tr("Select a Fritzing File to Open"), path, tr("Fritzing Files (*%1 *%2 *%3);;Fritzing Part (*%1);;Fritzing Bin (*%2);;Fritzing Shareable Bin (*%3)") .arg(FritzingBundledPartExtension) .arg(FritzingBinExtension) .arg(FritzingBundledBinExtension) ); if (fileName.isEmpty()) return; if (fileName.endsWith(FritzingBundledPartExtension)) { importPartToCurrentBin(fileName); return; } if (fileName.endsWith(FritzingBinExtension) || fileName.endsWith(FritzingBundledBinExtension)) { openBinIn(fileName, false); } } void BinManager::hideTabBar() { m_stackTabWidget->stackTabBar()->hide(); } void BinManager::reloadPart(const QString & moduleID) { PartsBinView::removePartReference(moduleID); for(int j = 0; j < m_stackTabWidget->count(); j++) { PartsBinPaletteWidget *bin = qobject_cast(m_stackTabWidget->widget(j)); if (bin == NULL) continue; bin->reloadPart(moduleID); } } void BinManager::copyToSketch() { PartsBinPaletteWidget * bin = currentBin(); if (bin == NULL) return; QList modelParts = bin->getAllParts(); if (modelParts.count() == 0) return; if (m_mainWindow) { m_mainWindow->addToSketch(modelParts); } } void BinManager::copyAllToSketch() { QList modelParts; if (m_mainWindow) { m_mainWindow->addToSketch(modelParts); } } fritzing-app-0.9.3b/src/partsbinpalette/binmanager/binmanager.h000066400000000000000000000164111272363601300246540ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef BINMANAGER_H_ #define BINMANAGER_H_ #include #include #include #include #include class ModelPart; class PaletteModel; class MainWindow; class PartsBinPaletteWidget; struct BinLocation { enum Location { More, User, App, Outside, Any }; QString path; QString title; Location location; bool marked; static QString toString(BinLocation::Location location); static BinLocation::Location findLocation(const QString & filename); static BinLocation::Location fromString(const QString & locationString); }; class BinManager : public QFrame { Q_OBJECT public: BinManager(class ReferenceModel *referenceModel, class HtmlInfoView *infoView, class WaitPushUndoStack *undoStack, MainWindow* parent); virtual ~BinManager(); void initStandardBins(); void addBin(PartsBinPaletteWidget* bin); void insertBin(PartsBinPaletteWidget* bin, int index); void addPartToBin(ModelPart *modelPart, int position = -1); void addToMyParts(ModelPart *modelPart); void addToTempPartsBin(ModelPart *modelPart); void hideTempPartsBin(); void addNewPart(ModelPart *modelPart); bool beforeClosing(); bool hasAlienParts(); QString createIfMyPartsNotExists(); QString createIfSearchNotExists(); QString createIfBinNotExists(const QString & dest, const QString & source); void setInfoViewOnHover(bool infoViewOnHover); void load(const QString&); void setDirtyTab(PartsBinPaletteWidget* w, bool dirty=true); void updateTitle(PartsBinPaletteWidget* w, const QString& newTitle); void openBin(const QString &fileName); PartsBinPaletteWidget* openBinIn(QString fileName, bool fastLoad); PartsBinPaletteWidget* openCoreBinIn(); void closeBinIn(int index); void addPartTo(PartsBinPaletteWidget* bin, ModelPart* mp, bool setDirty); void editSelectedPartNewFrom(PartsBinPaletteWidget* bin); const QString &getSelectedModuleIDFromSketch(); QList openedBinsActions(const QString &moduleId); MainWindow* mainWindow(); void search(const QString & searchText); bool currentViewIsIconView(); void updateViewChecks(bool iconView); QMenu * binContextMenu(PartsBinPaletteWidget *); QMenu * partContextMenu(PartsBinPaletteWidget *); QMenu * combinedMenu(PartsBinPaletteWidget *); QMenu * combinedMenu(); void setTabIcon(PartsBinPaletteWidget* w, QIcon *); void copyFilesToContrib(ModelPart *, QWidget * originator); void importPartToCurrentBin(const QString & filename); void importPartToMineBin(const QString & filename); bool isTempPartsBin(PartsBinPaletteWidget * bin); void setTempPartsBinLocation(const QString & filename); void hideTabBar(); void reloadPart(const QString & moduleID); signals: void savePartAsBundled(const QString &moduleId); public slots: void updateBinCombinedMenuCurrent(); void toIconView(); void toListView(); void setAsCurrentBin(PartsBinPaletteWidget* bin); protected slots: void updateFileName(PartsBinPaletteWidget* bin, const QString &newFileName, const QString &oldFilename); void currentChanged(int); void tabCloseRequested(int); PartsBinPaletteWidget* newBinIn(); void closeBin(); void deleteBin(); void editSelectedNew(); void saveBin(); void saveBinAs(); void renameBin(); void copyToSketch(); void copyAllToSketch(); void exportSelected(); bool removeSelected(); void findSelected(); void saveBundledBin(); void mainLoad(); protected: void createMenu(); PartsBinPaletteWidget* newBin(); void registerBin(PartsBinPaletteWidget* bin); PartsBinPaletteWidget* getBin(int index); PartsBinPaletteWidget* currentBin(); void setAsCurrentTab(PartsBinPaletteWidget* bin); PartsBinPaletteWidget* getOrOpenMyPartsBin(); PartsBinPaletteWidget* getOrOpenSearchBin(); PartsBinPaletteWidget* getOrOpenBin(const QString & dest, const QString & source); void connectTabWidget(); void addPartToBinAux(PartsBinPaletteWidget *bin, ModelPart *modelPart, int position = -1); PartsBinPaletteWidget* findBin(const QString & binLocation); void createCombinedMenu(); void createContextMenus(); void saveStateAndGeometry(); void restoreStateAndGeometry(QList & actualLocations); void findAllBins(QList & actualLocations); void findBins(QDir &, QList & actualLocations, BinLocation::Location); void readTheoreticalLocations(QList & theoreticalLocations); void updateBinCombinedMenu(PartsBinPaletteWidget * bin); void importPart(const QString & filename, PartsBinPaletteWidget * bin); void hackLocalContrib(QList &); protected: ReferenceModel *m_referenceModel; HtmlInfoView *m_infoView; WaitPushUndoStack *m_undoStack; MainWindow *m_mainWindow; PartsBinPaletteWidget *m_currentBin; class StackTabWidget* m_stackTabWidget; QHash m_openedBins; int m_unsavedBinsCount; QString m_defaultSaveFolder; QMenu *m_binContextMenu; QMenu *m_combinedMenu; QAction *m_openAction; QAction *m_newBinAction; QAction *m_closeBinAction; QAction *m_deleteBinAction; QAction *m_saveBinAction; QAction *m_saveBinAsAction; QAction *m_saveBinAsBundledAction; QAction *m_renameBinAction; QAction *m_copyToSketchAction; QAction *m_copyAllToSketchAction; QAction *m_showListViewAction; QAction *m_showIconViewAction; QMenu *m_partContextMenu; QMenu *m_partMenu; QAction *m_editPartNewAction; QAction *m_exportPartAction; QAction *m_removePartAction; QAction *m_findPartAction; QString m_tempPartsBinLocation; protected: static QString StandardBinStyle; static QString CurrentBinStyle; public: static QString Title; static QString CorePartsBinLocation; static QString MyPartsBinLocation; static QString MyPartsBinTemplateLocation; static QString SearchBinLocation; static QString SearchBinTemplateLocation; static QString ContribPartsBinLocation; static QString TempPartsBinTemplateLocation; static QHash StandardBinIcons; static bool isTabReorderingEvent(QDropEvent* event); static void initNames(); static bool getBinTitle(const QString & filename, QString & binTitle, QString & binIcon); }; #endif /* BINMANAGER_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/binmanager/stacktabbar.cpp000066400000000000000000000106231272363601300253640ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6549 $: $Author: irascibl@gmail.com $: $Date: 2012-10-11 06:54:26 +0200 (Do, 11. Okt 2012) $ ********************************************************************/ #include #include #include #include #include #include #include "stacktabbar.h" #include "stacktabwidget.h" #include "../partsbinpalettewidget.h" #include "../partsbinview.h" #include "../../debugdialog.h" StackTabBar::StackTabBar(StackTabWidget *parent) : QTabBar(parent) { setAcceptDrops(true); this->setUsesScrollButtons(true); //this->setContentsMargins(0,0,0,0); //this->setTabsClosable(true); setMovable(true); m_parent = parent; setProperty("current","false"); setExpanding(false); setElideMode(Qt::ElideRight); setIconSize(QSize(32, 32)); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(showContextMenu(const QPoint &))); m_dragMoveTimer.setSingleShot(true); m_dragMoveTimer.setInterval(250); connect(&m_dragMoveTimer, SIGNAL(timeout()), this, SLOT(setIndex())); } bool StackTabBar::mimeIsAction(const QMimeData* m, const QString& action) { if(m) { QStringList formats = m->formats(); return formats.contains("action") && (m->data("action") == action); } else { return false; } } void StackTabBar::dragEnterEvent(QDragEnterEvent* event) { // Only accept if it's an part-reordering request const QMimeData *m = event->mimeData(); if (mimeIsAction(m, "part-reordering")) { event->acceptProposedAction(); } } void StackTabBar::dragLeaveEvent(QDragLeaveEvent *event) { Q_UNUSED(event); m_dragMoveTimer.stop(); } void StackTabBar::dragMoveEvent(QDragMoveEvent* event) { const QMimeData *m = event->mimeData(); int index = tabAt(event->pos()); if ((event->source() != this) && mimeIsAction(m,"part-reordering")) { PartsBinPaletteWidget* bin = qobject_cast(m_parent->widget(index)); if(bin && bin->allowsChanges()) { event->acceptProposedAction(); m_dragMoveTimer.setProperty("index", index); if (!m_dragMoveTimer.isActive()) { DebugDialog::debug("starting drag move timer"); m_dragMoveTimer.start(); } //DebugDialog::debug(QString("setting index %1").arg(index)); } } } void StackTabBar::dropEvent(QDropEvent* event) { int toIndex = tabAt(event->pos()); const QMimeData *m = event->mimeData(); if(mimeIsAction(m, "part-reordering")) { PartsBinPaletteWidget* bin = qobject_cast(m_parent->widget(toIndex)); if(bin && bin->allowsChanges()) { bin->currentView()->dropEventAux(event,true); } } event->acceptProposedAction(); } void StackTabBar::showContextMenu(const QPoint &point) { if (point.isNull()) return; int tabIndex = this->tabAt(point); PartsBinPaletteWidget* bin = qobject_cast(m_parent->widget(tabIndex)); if (bin == NULL) return; setCurrentIndex(tabIndex); QMenu * contextMenu = bin->binContextMenu(); if (contextMenu) { contextMenu->exec(this->mapToGlobal(point)); } delete contextMenu; } void StackTabBar::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QStylePainter painter(this); for(int i = 0; i < this->count(); ++i) { QStyleOptionTabV2 option; initStyleOption(&option, i); option.shape = RoundedNorth; option.text = ""; painter.drawControl(QStyle::CE_TabBarTab, option); } } void StackTabBar::setIndex() { if (sender() == NULL) return; bool ok = false; int index = sender()->property("index").toInt(&ok); if (!ok) return; setCurrentIndex(index); } fritzing-app-0.9.3b/src/partsbinpalette/binmanager/stacktabbar.h000066400000000000000000000032421272363601300250300ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef STACKTABBAR_H_ #define STACKTABBAR_H_ #include #include class StackTabBar : public QTabBar { Q_OBJECT public: StackTabBar(class StackTabWidget *parent); protected: void dragEnterEvent(QDragEnterEvent* event); void dragLeaveEvent(QDragLeaveEvent* event); void dragMoveEvent(QDragMoveEvent* event); void dropEvent(QDropEvent* event); void paintEvent(QPaintEvent *event); bool mimeIsAction(const class QMimeData* m, const QString& action); public slots: void setIndex(); protected slots: void showContextMenu(const QPoint &point); protected: class StackTabWidget* m_parent; QTimer m_dragMoveTimer; }; #endif /* STACKTABBAR_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/binmanager/stacktabwidget.cpp000066400000000000000000000032761272363601300261110ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "stacktabwidget.h" #include "stacktabbar.h" #include "../../debugdialog.h" StackTabWidget::StackTabWidget(QWidget *parent) : QTabWidget(parent) { setElideMode(Qt::ElideRight); setTabBar(new StackTabBar(this)); connect( this, SIGNAL(currentChanged(int)), this, SLOT(informCurrentChanged(int)) ); connect( this, SIGNAL(tabCloseRequested(int)), this, SLOT(informTabCloseRequested(int)) ); } StackTabBar *StackTabWidget::stackTabBar() { return qobject_cast(tabBar()); } void StackTabWidget::informCurrentChanged(int index) { emit currentChanged(this,index); } void StackTabWidget::informTabCloseRequested(int index) { emit tabCloseRequested(this, index); } fritzing-app-0.9.3b/src/partsbinpalette/binmanager/stacktabwidget.h000066400000000000000000000031121272363601300255430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef STACKTABWIDGET_H_ #define STACKTABWIDGET_H_ #include #include #include #include // originally extracted from http://wiki.qtcentre.org/index.php?title=Movable_Tabs class StackTabWidget : public QTabWidget { Q_OBJECT public: StackTabWidget(QWidget *parent); class StackTabBar *stackTabBar(); public slots: void informCurrentChanged(int index); void informTabCloseRequested(int index); signals: void currentChanged(StackTabWidget *, int index); void tabCloseRequested(StackTabWidget *, int index); }; #endif /* STACKTABWIDGET_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/graphicsflowlayout.cpp000066400000000000000000000057211272363601300247310ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6924 $: $Author: irascibl@gmail.com $: $Date: 2013-03-10 16:58:25 +0100 (So, 10. Mrz 2013) $ ********************************************************************/ #include "graphicsflowlayout.h" #include "../utils/misc.h" static const int SpaceBefore = 5; static const int SpaceAfter = 3; GraphicsFlowLayout::GraphicsFlowLayout(QGraphicsLayoutItem *parent, int spacing) : QGraphicsLinearLayout(parent) { setSpacing(spacing); } void GraphicsFlowLayout::widgetEvent(QEvent * e) { Q_UNUSED(e) } void GraphicsFlowLayout::setGeometry(const QRectF &rect) { QGraphicsLinearLayout::setGeometry(rect); doLayout(rect); } int GraphicsFlowLayout::heightForWidth(int width) { int height = doLayout(QRectF(0, 0, width, 0)); return height; } int GraphicsFlowLayout::doLayout(const QRectF &rect) { double x = rect.x(); double y = rect.y(); double lineHeight = 0; for(int i=0; i < count(); i++) { QGraphicsLayoutItem* item = itemAt(i); int nextX = x + item->preferredSize().width() + spacing(); if (item->sizePolicy().horizontalPolicy() == QSizePolicy::Expanding) { int myY = y + lineHeight + spacing() + SpaceBefore; QRectF r(QPoint(rect.x(), myY), item->preferredSize()); item->setGeometry(r); x = rect.x(); y = myY + item->preferredSize().height() + spacing() + SpaceAfter; continue; } if (nextX - spacing() > rect.right() && lineHeight > 0) { x = rect.x(); y = y + lineHeight + spacing(); nextX = x + item->preferredSize().width() + spacing(); lineHeight = 0; } item->setGeometry(QRectF(QPoint(x, y), item->preferredSize())); x = nextX; // item->preferredSize().height() returns qreal, armel compiler complains lineHeight = qMax(lineHeight, item->preferredSize().height()); } m_lastWidth = rect.width(); return y + lineHeight - rect.y(); } void GraphicsFlowLayout::clear() { QList itemsToRemove; for(int i=0; i < count(); i++) { itemsToRemove << itemAt(i); } foreach(QGraphicsLayoutItem* iToR, itemsToRemove) { removeItem(iToR); } } int GraphicsFlowLayout::indexOf(const QGraphicsLayoutItem *item) { for(int i=0; i < count(); i++) { if(itemAt(i) == item) return i; } return -1; } fritzing-app-0.9.3b/src/partsbinpalette/graphicsflowlayout.h000066400000000000000000000030541272363601300243730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef GRAPHICSFLOWLAYOUT_H_ #define GRAPHICSFLOWLAYOUT_H_ #include #include #include #include class GraphicsFlowLayout : public QGraphicsLinearLayout { public: GraphicsFlowLayout(QGraphicsLayoutItem *parent, int spacing); void setGeometry(const QRectF &rect); int heightForWidth(int width); void clear(); int indexOf(const QGraphicsLayoutItem *item); protected: void widgetEvent(QEvent * e); int doLayout(const QRectF &rect); double m_lastWidth; }; #endif /* GRAPHICSFLOWLAYOUT_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/partsbincommands.cpp000066400000000000000000000071171272363601300243500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "partsbincommands.h" #include "partsbinpalettewidget.h" ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PartsBinBaseCommand::PartsBinBaseCommand(class PartsBinPaletteWidget* bin, QUndoCommand* parent) : QUndoCommand(parent) { m_bin = bin; } class PartsBinPaletteWidget* PartsBinBaseCommand::bin() { return m_bin; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PartsBinAddRemoveArrangeCommand::PartsBinAddRemoveArrangeCommand(class PartsBinPaletteWidget* bin, QString moduleID, QUndoCommand *parent) : PartsBinBaseCommand(bin, parent) { m_moduleID = moduleID; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PartsBinAddRemoveCommand::PartsBinAddRemoveCommand(class PartsBinPaletteWidget* bin, QString moduleID, QString path, int index, QUndoCommand *parent) : PartsBinAddRemoveArrangeCommand(bin, moduleID, parent) { m_index = index; m_path = path; } void PartsBinAddRemoveCommand::add() { m_bin->addPart(m_moduleID, m_index); } void PartsBinAddRemoveCommand::remove() { m_bin->removePart(m_moduleID, m_path); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PartsBinAddCommand::PartsBinAddCommand(class PartsBinPaletteWidget* bin, QString moduleID, QString path, int index, QUndoCommand *parent) : PartsBinAddRemoveCommand(bin, moduleID, path, index, parent) {} void PartsBinAddCommand::undo() { remove(); } void PartsBinAddCommand::redo() { add(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PartsBinRemoveCommand::PartsBinRemoveCommand(class PartsBinPaletteWidget* bin, QString moduleID, QString path, int index, QUndoCommand *parent) : PartsBinAddRemoveCommand(bin, moduleID, path, index, parent) {} void PartsBinRemoveCommand::undo() { add(); } void PartsBinRemoveCommand::redo() { remove(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PartsBinArrangeCommand::PartsBinArrangeCommand(class PartsBinPaletteWidget* bin, QString moduleID, int oldIndex, int newIndex, QUndoCommand *parent) : PartsBinAddRemoveArrangeCommand(bin, moduleID, parent) { m_oldIndex = oldIndex; m_newIndex = newIndex; } void PartsBinArrangeCommand::undo() { } void PartsBinArrangeCommand::redo() { } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// fritzing-app-0.9.3b/src/partsbinpalette/partsbincommands.h000066400000000000000000000051141272363601300240100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSBINCOMMANDS_H #define PARTSBINCOMMANDS_H #include #include class PartsBinBaseCommand : public QUndoCommand { public: PartsBinBaseCommand(class PartsBinPaletteWidget*, QUndoCommand* parent = 0); class PartsBinPaletteWidget* bin(); protected: class PartsBinPaletteWidget* m_bin; }; class PartsBinAddRemoveArrangeCommand : public PartsBinBaseCommand { public: PartsBinAddRemoveArrangeCommand(class PartsBinPaletteWidget*, QString moduleID, QUndoCommand *parent = 0); protected: QString m_moduleID; }; class PartsBinAddRemoveCommand : public PartsBinAddRemoveArrangeCommand { public: PartsBinAddRemoveCommand(class PartsBinPaletteWidget*, QString moduleID, QString path, int index, QUndoCommand *parent = 0); protected: void add(); void remove(); int m_index; QString m_path; }; class PartsBinAddCommand : public PartsBinAddRemoveCommand { public: PartsBinAddCommand(class PartsBinPaletteWidget*, QString moduleID, QString path, int index = -1, QUndoCommand *parent = 0); void undo(); void redo(); }; class PartsBinRemoveCommand : public PartsBinAddRemoveCommand { public: PartsBinRemoveCommand(class PartsBinPaletteWidget*, QString moduleID, QString path, int index = -1, QUndoCommand *parent = 0); void undo(); void redo(); }; class PartsBinArrangeCommand : public PartsBinAddRemoveArrangeCommand { public: PartsBinArrangeCommand(class PartsBinPaletteWidget*, QString moduleID, int oldIndex, int newIndex, QUndoCommand *parent = 0); void undo(); void redo(); protected: int m_oldIndex; int m_newIndex; }; #endif // PARTSBINCOMMANDS_H fritzing-app-0.9.3b/src/partsbinpalette/partsbiniconview.cpp000066400000000000000000000321211272363601300243630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include #include #include #include #include "partsbiniconview.h" #include "graphicsflowlayout.h" #include "../items/paletteitem.h" #include "../debugdialog.h" #include "svgiconwidget.h" #include "../model/palettemodel.h" #include "../items/partfactory.h" #include "partsbinpalettewidget.h" #define ICON_SPACING 5 PartsBinIconView::PartsBinIconView(ReferenceModel* referenceModel, PartsBinPaletteWidget *parent) : InfoGraphicsView((QWidget*)parent), PartsBinView(referenceModel, parent) { setAcceptWheelEvents(false); setFrameStyle(QFrame::Raised | QFrame::StyledPanel); setAlignment(Qt::AlignLeft | Qt::AlignTop); setAcceptDrops(true); QGraphicsScene* scene = new QGraphicsScene(this); this->setScene(scene); m_layouter = NULL; m_layout = NULL; setupLayout(); //connect(scene, SIGNAL(selectionChanged()), this, SLOT(informNewSelection())); //connect(scene, SIGNAL(changed(const QList&)), this, SLOT(informNewSelection())); m_noSelectionChangeEmition = false; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setContextMenuPolicy(Qt::CustomContextMenu); connect( this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)) ); } void PartsBinIconView::setupLayout() { // TODO Mariano: smells like leak, but if this two lines are uncommented there's a crash // Also tried to iterate through layout children, but didn't work // Mariano: see under doClear--deleting m_layouter automatically deletes m_layout //delete m_layouter; //delete m_layout; m_layouter = new QGraphicsWidget; m_layout = new GraphicsFlowLayout(m_layouter, ICON_SPACING); scene()->addItem(m_layouter); } void PartsBinIconView::updateSize(QSize newSize) { updateSizeAux(newSize.width()); } void PartsBinIconView::updateSize() { updateSizeAux(width()); } void PartsBinIconView::updateSizeAux(int width) { setSceneRect(0, 0, width, m_layout->heightForWidth(width)); } void PartsBinIconView::resizeEvent(QResizeEvent * event) { InfoGraphicsView::resizeEvent(event); updateSize(event->size()); } void PartsBinIconView::mousePressEvent(QMouseEvent *event) { SvgIconWidget* icon = svgIconWidgetAt(event->pos()); if (icon == NULL || event->button() != Qt::LeftButton) { QGraphicsView::mousePressEvent(event); if (icon == NULL) { viewItemInfo(NULL); } } else { if (icon != NULL) { QList items = scene()->selectedItems(); for (int i = 0; i < items.count(); i++) { // not sure why clearSelection doesn't do the update, but whatever... items[i]->setSelected(false); items[i]->update(); } icon->setSelected(true); icon->update(); QPointF mts = this->mapToScene(event->pos()); QString moduleID = icon->moduleID(); QPoint hotspot = (mts.toPoint()-icon->pos().toPoint()); viewItemInfo(icon->itemBase()); mousePressOnItem(event->pos(), moduleID, icon->rect().size().toSize(), (mts - icon->pos()), hotspot ); } else { viewItemInfo(NULL); } } informNewSelection(); } void PartsBinIconView::doClear() { PartsBinView::doClear(); m_layout->clear(); delete m_layouter; // deleting layouter deletes layout delete scene(); // deleting scene deletes QGraphicsItems setScene(new QGraphicsScene(this)); setupLayout(); } void PartsBinIconView::addPart(ModelPart * model, int position) { PartsBinView::addPart(model, position); updateSize(); } void PartsBinIconView::removePart(const QString &moduleID) { SvgIconWidget *itemToRemove = NULL; int position = 0; foreach(QGraphicsItem *gIt, m_layouter->childItems()) { SvgIconWidget *it = dynamic_cast(gIt); if(it && it->moduleID() == moduleID) { itemToRemove = it; break; } else { position++; } } if(itemToRemove) { m_itemBaseHash.remove(moduleID); itemToRemove->setParentItem(NULL); m_noSelectionChangeEmition = true; m_layout->removeItem(itemToRemove); delete itemToRemove; } setSelected(position, true); updateSize(); } void PartsBinIconView::removeParts() { QList itemsToRemove; foreach(QGraphicsItem *gIt, m_layouter->childItems()) { SvgIconWidget *it = dynamic_cast(gIt); if(it) { itemsToRemove.append(it); } } m_itemBaseHash.clear(); foreach (SvgIconWidget * itemToRemove, itemsToRemove) { m_noSelectionChangeEmition = true; itemToRemove->setParentItem(NULL); m_layout->removeItem(itemToRemove); delete itemToRemove; } updateSize(); } int PartsBinIconView::setItemAux(ModelPart * modelPart, int position) { if (!modelPart || modelPart->itemType() == ModelPart::Unknown) { // don't want the empty root item to appear in the view return position; } emit settingItem(); QString moduleID = modelPart->moduleID(); if (contains(moduleID)) { return position; } SvgIconWidget* svgicon = NULL; if (modelPart->itemType() != ModelPart::Space) { ItemBase::PluralType plural; ItemBase * itemBase = loadItemBase(moduleID, plural); svgicon = new SvgIconWidget(modelPart, ViewLayer::IconView, itemBase, plural == ItemBase::Plural); } else { svgicon = new SvgIconWidget(modelPart, ViewLayer::IconView, NULL, false); } if(position > -1) { m_layout->insertItem(position, svgicon); } else { m_layout->addItem(svgicon); position = m_layout->count() - 1; } return position; } void PartsBinIconView::setPaletteModel(PaletteModel *model, bool clear) { PartsBinView::setPaletteModel(model, clear); updateSize(); } void PartsBinIconView::loadFromModel(PaletteModel * model) { ModelPart* root = model->root(); QList::const_iterator i; for (i = root->children().constBegin(); i != root->children().constEnd(); ++i) { ModelPart* mp = qobject_cast(*i); if (mp == NULL) continue; QDomElement instance = mp->instanceDomElement(); if (instance.isNull()) continue; QDomElement views = instance.firstChildElement("views"); if (views.isNull()) continue; QString name = ViewLayer::viewIDXmlName(ViewLayer::IconView); QDomElement view = views.firstChildElement(name); if (view.isNull()) continue; QDomElement geometry = view.firstChildElement("geometry"); if (geometry.isNull()) continue; setItemAux(mp); } } ModelPart *PartsBinIconView::selectedModelPart() { SvgIconWidget *icon = dynamic_cast(selectedAux()); if(icon) { return icon->modelPart(); } else { return NULL; } } ItemBase *PartsBinIconView::selectedItemBase() { SvgIconWidget *icon = dynamic_cast(selectedAux()); if(icon) { return icon->itemBase(); } else { return NULL; } } void PartsBinIconView::setSelected(int position, bool doEmit) { QGraphicsLayoutItem *glIt = m_layout->itemAt(position); if(SvgIconWidget *item = dynamic_cast(glIt)) { m_noSelectionChangeEmition = true; scene()->clearSelection(); m_noSelectionChangeEmition = !doEmit; item->setSelected(true); } } bool PartsBinIconView::swappingEnabled(ItemBase * itemBase) { Q_UNUSED(itemBase); return false; } int PartsBinIconView::selectedIndex() { int idx = 0; foreach(QGraphicsItem *it, scene()->items()) { SvgIconWidget *icon = dynamic_cast(it); if(icon) { if(icon->isSelected()) { return idx; } else { idx++; } } } return -1; } void PartsBinIconView::informNewSelection() { if(!m_noSelectionChangeEmition) { emit selectionChanged(selectedIndex()); } else { m_noSelectionChangeEmition = false; } } void PartsBinIconView::dragMoveEvent(QDragMoveEvent* event) { dragMoveEnterEventAux(event); } void PartsBinIconView::dropEvent(QDropEvent* event) { dropEventAux(event); } void PartsBinIconView::moveItem(int fromIndex, int toIndex) { itemMoved(fromIndex,toIndex); emit informItemMoved(fromIndex, toIndex); } void PartsBinIconView::itemMoved(int fromIndex, int toIndex) { QGraphicsLayoutItem *item = m_layout->itemAt(fromIndex); m_layout->removeItem(item); m_layout->insertItem(toIndex,item); updateSize(); setSelected(toIndex,/*doEmit*/false); } int PartsBinIconView::itemIndexAt(const QPoint& pos, bool &trustIt) { trustIt = true; SvgIconWidget *item = svgIconWidgetAt(pos); if(item) { int foundIdx = m_layout->indexOf(item); /*if(foundIdx != -1) { // no trouble finding it return foundIdx; } //else: something weird happened, the item exists, // but it should have and index > -1 */ if(foundIdx == -1) { // no indicator shown trustIt = false; } return foundIdx; } if(!inEmptyArea(pos)) { // let's see if moving around the point we can find a real item return m_layout->indexOf(closestItemTo(pos)); } else { // it's beyond the icons area, so -1 is OK return -1; } } bool PartsBinIconView::inEmptyArea(const QPoint& pos) { // used only by internal drag and drop if(m_layout->count() == 0) { return true; } else { QGraphicsLayoutItem *lastItem = m_layout->itemAt(m_layout->count()-1); QPointF itemBottomRightPoint = lastItem->graphicsItem()->mapToScene(lastItem->contentsRect().bottomRight()); return itemBottomRightPoint.y() < pos.y() || ( itemBottomRightPoint.y() >= pos.y() && itemBottomRightPoint.x() < pos.x() ); } } QGraphicsWidget* PartsBinIconView::closestItemTo(const QPoint& pos) { QPointF realPos = mapToScene(pos); SvgIconWidget *item = NULL; this -> setObjectName("partsIcon"); if((item = svgIconWidgetAt(realPos.x()+ICON_SPACING,realPos.y()+ICON_SPACING))) { return item; } if((item = svgIconWidgetAt(realPos.x()-ICON_SPACING,realPos.y()+ICON_SPACING))) { return item; } if((item = svgIconWidgetAt(realPos.x()+ICON_SPACING,realPos.y()-ICON_SPACING))) { return item; } if((item = svgIconWidgetAt(realPos.x()-ICON_SPACING,realPos.y()-ICON_SPACING))) { return item; } return NULL; } QList PartsBinIconView::orderedChildren() { QList result; for(int i=0; i < m_layout->count(); i++) { SvgIconWidget *it = dynamic_cast(m_layout->itemAt(i)); if(it) { result << it->modelPart(); } } return result; } void PartsBinIconView::showContextMenu(const QPoint& pos) { SvgIconWidget *it = svgIconWidgetAt(pos); QMenu *menu; if(it) { scene()->clearSelection(); it->setSelected(true); menu = m_parent->partContextMenu(); } else { menu = m_parent->combinedMenu(); } menu->exec(mapToGlobal(pos)); } SvgIconWidget * PartsBinIconView::svgIconWidgetAt(int x, int y) { return svgIconWidgetAt(QPoint(x, y)); } SvgIconWidget * PartsBinIconView::svgIconWidgetAt(const QPoint & pos) { QGraphicsItem * item = itemAt(pos); while (item != NULL) { SvgIconWidget * svgIconWidget = dynamic_cast(item); if (svgIconWidget != NULL) { return svgIconWidget; } item = item->parentItem(); } return NULL; } void PartsBinIconView::reloadPart(const QString & moduleID) { if (!contains(moduleID)) return; for (int i = 0; i < m_layout->count(); i++) { SvgIconWidget *it = dynamic_cast(m_layout->itemAt(i)); if (it == NULL) continue; if (it->itemBase()->moduleID().compare(moduleID) != 0) continue; ItemBase::PluralType plural; ItemBase * itemBase = loadItemBase(moduleID, plural); it->setItemBase(itemBase, plural); return; } } ItemBase * PartsBinIconView::loadItemBase(const QString & moduleID, ItemBase::PluralType & plural) { ItemBase * itemBase = ItemBaseHash.value(moduleID); ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID); if (itemBase == NULL) { itemBase = PartFactory::createPart(modelPart, ViewLayer::NewTop, ViewLayer::IconView, ViewGeometry(), ItemBase::getNextID(), NULL, NULL, false); ItemBaseHash.insert(moduleID, itemBase); } m_itemBaseHash.insert(moduleID, itemBase); plural = itemBase->isPlural(); if (plural == ItemBase::NotSure) { QHash properties = modelPart->properties(); QString family = properties.value("family", "").toLower(); foreach (QString key, properties.keys()) { QStringList values = m_referenceModel->propValues(family, key, true); if (values.length() > 1) { plural = ItemBase::Plural; break; } } } return itemBase; } fritzing-app-0.9.3b/src/partsbinpalette/partsbiniconview.h000066400000000000000000000060721272363601300240360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6494 $: $Author: irascibl@gmail.com $: $Date: 2012-09-29 17:40:27 +0200 (Sa, 29. Sep 2012) $ ********************************************************************/ #ifndef ICONVIEW_H_ #define ICONVIEW_H_ #include #include #include "partsbinview.h" #include "../sketch/infographicsview.h" QT_BEGIN_NAMESPACE class QDragEnterEvent; class QDropEvent; QT_END_NAMESPACE class PartsBinIconView : public InfoGraphicsView, public PartsBinView { Q_OBJECT public: PartsBinIconView(ReferenceModel* referenceModel, PartsBinPaletteWidget *parent); void loadFromModel(class PaletteModel *); void setPaletteModel(class PaletteModel *model, bool clear=false); void addPart(ModelPart * model, int position = -1); void removePart(const QString &moduleID); void removeParts(); bool swappingEnabled(ItemBase *); ModelPart *selectedModelPart(); ItemBase *selectedItemBase(); int selectedIndex(); QList orderedChildren(); void reloadPart(const QString & moduleID); protected: void doClear(); void moveItem(int fromIndex, int toIndex); int itemIndexAt(const QPoint& pos, bool &trustIt); void mousePressEvent(QMouseEvent *event); void dragMoveEvent(QDragMoveEvent* event); void dropEvent(QDropEvent* event); int setItemAux(ModelPart *, int position = -1); void resizeEvent(QResizeEvent * event); void updateSize(QSize newSize); void updateSize(); void updateSizeAux(int width); void setupLayout(); bool inEmptyArea(const QPoint& pos); QGraphicsWidget* closestItemTo(const QPoint& pos); class SvgIconWidget * svgIconWidgetAt(const QPoint & pos); class SvgIconWidget * svgIconWidgetAt(int x, int y); ItemBase * loadItemBase(const QString & moduleID, ItemBase::PluralType &); public slots: void setSelected(int position, bool doEmit=false); void informNewSelection(); void itemMoved(int fromIndex, int toIndex); protected slots: void showContextMenu(const QPoint& pos); signals: void informItemMoved(int fromIndex, int toIndex); void selectionChanged(int index); void settingItem(); protected: LayerHash m_viewLayers; QGraphicsWidget *m_layouter; class GraphicsFlowLayout *m_layout; QMenu *m_itemMenu; bool m_noSelectionChangeEmition; }; #endif /* ICONVIEW_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/partsbinlistview.cpp000066400000000000000000000247601272363601300244200ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6934 $: $Author: irascibl@gmail.com $: $Date: 2013-03-22 08:35:24 +0100 (Fr, 22. Mrz 2013) $ ********************************************************************/ #include #include #include "../debugdialog.h" #include "../infoview/htmlinfoview.h" #include "../items/itembase.h" #include "../fsvgrenderer.h" #include "../itemdrag.h" #include "../items/partfactory.h" #include "../layerattributes.h" #include "partsbinpalettewidget.h" #include "partsbinlistview.h" static const QColor SectionHeaderBackgroundColor(128, 128, 128); static const QColor SectionHeaderForegroundColor(32, 32, 32); PartsBinListView::PartsBinListView(ReferenceModel* referenceModel, PartsBinPaletteWidget *parent) : QListWidget((QWidget *) parent), PartsBinView(referenceModel, parent) { m_infoView = NULL; m_hoverItem = NULL; m_infoViewOnHover = true; setMouseTracking(true); setSpacing(2); setIconSize(QSize(16,16)); setSortingEnabled(false); setDragEnabled(true); viewport()->setAcceptDrops(true); setDropIndicatorShown(true); setDragDropMode(QAbstractItemView::DragDrop); setAcceptDrops(true); setContextMenuPolicy(Qt::CustomContextMenu); connect( this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)) ); } PartsBinListView::~PartsBinListView() { } void PartsBinListView::doClear() { m_hoverItem = NULL; PartsBinView::doClear(); clear(); } int PartsBinListView::setItemAux(ModelPart * modelPart, int position) { if (modelPart->modelPartShared() == NULL) return position; if (modelPart->itemType() == ModelPart::Unknown) { // don't want the empty root to appear in the view return position; } emit settingItem(); QString moduleID = modelPart->moduleID(); if (contains(moduleID)) { return position; } QListWidgetItem * lwi = new QListWidgetItem(modelPart->title()); if (modelPart->itemType() == ModelPart::Space) { lwi->setBackground(QBrush(SectionHeaderBackgroundColor)); lwi->setForeground(QBrush(SectionHeaderForegroundColor)); lwi->setData(Qt::UserRole, 0); lwi->setFlags(0); lwi->setText(" " + TranslatedCategoryNames.value(modelPart->instanceText(), modelPart->instanceText())); } else { loadImage(modelPart, lwi, moduleID); } if(position > -1 && position < count()) { insertItem(position, lwi); } else { addItem(lwi); position = this->count(); } return position; } void PartsBinListView::mouseMoveEvent(QMouseEvent *event) { if(m_infoViewOnHover) { QListWidgetItem * item = itemAt(event->pos()); if (item != NULL) { showInfo(item); } else { m_hoverItem = NULL; } } QListWidget::mouseMoveEvent(event); } void PartsBinListView::showInfo(QListWidgetItem * item) { if (item == m_hoverItem) { // no change return; } if (m_hoverItem != NULL && m_infoView != NULL) { ItemBase * itemBase = itemItemBase(m_hoverItem); if (itemBase != NULL) { m_infoView->hoverLeaveItem(NULL, NULL, itemBase); } } if (item == NULL) { return; } m_hoverItem = item; if (m_infoView != NULL) { ItemBase * itemBase = itemItemBase(item); if (itemBase != NULL) { m_infoView->hoverEnterItem(NULL, NULL, itemBase, swappingEnabled()); } } } void PartsBinListView::mousePressEvent(QMouseEvent *event) { m_dragStartPos = event->pos(); QListWidget::mousePressEvent(event); QListWidgetItem * current = currentItem(); if (current == NULL) { m_hoverItem = NULL; if (m_infoView != NULL) m_infoView->viewItemInfo(NULL, NULL, false); return; } showInfo(current); if (m_infoView != NULL) m_infoView->viewItemInfo(NULL, itemItemBase(current), false); } void PartsBinListView::setInfoView(HtmlInfoView * infoView) { m_infoView = infoView; } void PartsBinListView::removePart(const QString &moduleID) { m_hoverItem = NULL; int idxToRemove = position(moduleID); if(idxToRemove > -1) { m_itemBaseHash.remove(moduleID); delete takeItem(idxToRemove); } } void PartsBinListView::removeParts() { m_hoverItem = NULL; m_itemBaseHash.clear(); while (count() > 0) { delete takeItem(0); } } int PartsBinListView::position(const QString &moduleID) { for(int i=0; i < count(); i++) { if(itemModuleID(item(i)) == moduleID) { return i; } } return -1; } ItemBase * PartsBinListView::itemItemBase(const QListWidgetItem *item) const { return item->data(Qt::UserRole).value(); } ModelPart *PartsBinListView::itemModelPart(const QListWidgetItem *item) const { ItemBase * itemBase = itemItemBase(item); if (itemBase == NULL) return NULL; return itemBase->modelPart(); } const QString &PartsBinListView::itemModuleID(const QListWidgetItem *item) { ModelPart *modelPart = itemModelPart(item); if (modelPart == NULL) { // this shouldn't happen? return ___emptyString___; } return modelPart->moduleID(); } ItemBase *PartsBinListView::selectedItemBase() { if(selectedItems().size()==1) { return itemItemBase(selectedItems()[0]); } return NULL; } ModelPart *PartsBinListView::selectedModelPart() { if(selectedItems().size()==1) { return itemModelPart(selectedItems()[0]); } return NULL; } bool PartsBinListView::swappingEnabled() { return false; } void PartsBinListView::setSelected(int position, bool doEmit) { Q_UNUSED(doEmit); if(position > -1 && position < count()) { item(position)->setSelected(true); } else { setCurrentRow(position); } } /*void PartsBinListView::dragEnterEvent(QDragEnterEvent* event) { dragMoveEnterEventAux(event); }*/ void PartsBinListView::dropEvent(QDropEvent* event) { dropEventAux(event); QListWidget::dropEvent(event); } void PartsBinListView::startDrag(Qt::DropActions supportedActions) { QListWidget::startDrag(supportedActions); ItemDrag::dragIsDone(); } void PartsBinListView::moveItem(int fromIndex, int toIndex) { itemMoved(fromIndex,toIndex); emit informItemMoved(fromIndex, toIndex); } void PartsBinListView::itemMoved(int fromIndex, int toIndex) { QListWidgetItem *item = takeItem(fromIndex); insertItem(toIndex,item); //setItemSelected(item,true); setSelected(toIndex); } int PartsBinListView::itemIndexAt(const QPoint& pos, bool &trustIt) { trustIt = true; QListWidgetItem *item = itemAt(pos); if(item) { return row(item); } else if(rect().contains(pos)) { trustIt = false; return -1; } else { return -1; } } bool PartsBinListView::dropMimeData(int index, const QMimeData *data, Qt::DropAction action) { Q_UNUSED(index); Q_UNUSED(action); if(data->hasFormat("action") && (data->data("action") == "part-reordering")) { /*QByteArray itemData = data->data("application/x-dnditemdata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QString moduleID; QPointF offset; dataStream >> moduleID >> offset; ModelPart *modelPart = m_partHash[moduleID]; QModelIndex idx = model()->index(index, 0); model()->setData(idx, qVariantFromValue(modelPart), Qt::UserRole);*/ return true; } else { return false; } return true; } QMimeData * PartsBinListView::mimeData(const QList items) const { if (items.count()>1) { throw "PartsBinListView::mimeData too many items"; } if(items.count()==1) { QListWidgetItem * item = items.at(0); ModelPart * modelPart = itemModelPart(item); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); dataStream << modelPart->moduleID() << QPointF(0,0); QMimeData *mimeData = new QMimeData; mimeData->setData("application/x-dnditemdata", itemData); mimeData->setData("action", "part-reordering"); return mimeData; } else { return QListWidget::mimeData(items); } } QStringList PartsBinListView::mimeTypes() const { QStringList list; list << "application/x-dnditemdata" << "action"; return list; } QList PartsBinListView::orderedChildren() { QList result; for(int i=0; i < count(); i++) { ModelPart *mp = itemModelPart(item(i)); if(mp) { result << mp; } } return result; } void PartsBinListView::showContextMenu(const QPoint& pos) { QListWidgetItem *it = itemAt(pos); QMenu *menu; if(it) { clearSelection(); it->setSelected(true); menu = m_parent->partContextMenu(); } else { menu = m_parent->combinedMenu(); } menu->exec(mapToGlobal(pos)); } void PartsBinListView::reloadPart(const QString & moduleID) { if (!contains(moduleID)) return; for(int i = 0; i < count(); i++) { QListWidgetItem * lwi = item(i); ItemBase * itemBase = itemItemBase(lwi); if (itemBase == NULL) continue; if (itemBase->moduleID().compare(moduleID) == 0) { lwi->setText(itemBase->title()); loadImage(itemBase->modelPart(), lwi, moduleID); return; } } } void PartsBinListView::loadImage(ModelPart * modelPart, QListWidgetItem * lwi, const QString & moduleID) { ItemBase * itemBase = ItemBaseHash.value(moduleID); if (itemBase == NULL) { itemBase = PartFactory::createPart(modelPart, ViewLayer::NewTop, ViewLayer::IconView, ViewGeometry(), ItemBase::getNextID(), NULL, NULL, false); ItemBaseHash.insert(moduleID, itemBase); LayerAttributes layerAttributes; itemBase->initLayerAttributes(layerAttributes, ViewLayer::IconView, ViewLayer::Icon, itemBase->viewLayerPlacement(), false, false); FSvgRenderer * renderer = itemBase->setUpImage(modelPart, layerAttributes); if (renderer != NULL) { if (itemBase) { itemBase->setFilename(renderer->filename()); } itemBase->setSharedRendererEx(renderer); } } lwi->setData(Qt::UserRole, qVariantFromValue( itemBase ) ); QSize size(HtmlInfoView::STANDARD_ICON_IMG_WIDTH, HtmlInfoView::STANDARD_ICON_IMG_HEIGHT); QPixmap * pixmap = FSvgRenderer::getPixmap(itemBase->renderer(), size); lwi->setIcon(QIcon(*pixmap)); delete pixmap; lwi->setData(Qt::UserRole + 1, itemBase->renderer()->defaultSize()); m_itemBaseHash.insert(moduleID, itemBase); } fritzing-app-0.9.3b/src/partsbinpalette/partsbinlistview.h000066400000000000000000000055541272363601300240650ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef LISTVIEW_H_ #define LISTVIEW_H_ #include #include #include "partsbinview.h" class PartsBinListView : public QListWidget, public PartsBinView { Q_OBJECT public: PartsBinListView(ReferenceModel* referenceModel, PartsBinPaletteWidget *parent); ~PartsBinListView(); void setInfoView(class HtmlInfoView *); void removePart(const QString &moduleID); void removeParts(); ModelPart *selectedModelPart(); ItemBase *selectedItemBase(); bool swappingEnabled(); int position(const QString &moduleID); QList orderedChildren(); void reloadPart(const QString & moduleID); public slots: void setSelected(int position, bool doEmit=false); void itemMoved(int fromIndex, int toIndex); protected slots: void showContextMenu(const QPoint& pos); signals: void informItemMoved(int fromIndex, int toIndex); void settingItem(); protected: void doClear(); void moveItem(int fromIndex, int toIndex); int itemIndexAt(const QPoint& pos, bool &trustIt); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); //void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); void startDrag(Qt::DropActions supportedActions); int setItemAux(ModelPart * modelPart, int position = -1); ModelPart *itemModelPart(const QListWidgetItem *item) const; ItemBase *itemItemBase(const QListWidgetItem *item) const; const QString& itemModuleID(const QListWidgetItem *item); void showInfo(QListWidgetItem * item); bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action); QMimeData * mimeData(const QList items) const; QStringList mimeTypes() const; void loadImage(ModelPart *, QListWidgetItem * lwi, const QString & moduleID); protected: class HtmlInfoView * m_infoView; QListWidgetItem * m_hoverItem; }; #endif /* LISTVIEW_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/partsbinpalettewidget.cpp000066400000000000000000000714131272363601300254110ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "partsbinpalettewidget.h" #include "partsbincommands.h" #include "partsbiniconview.h" #include "partsbinlistview.h" #include "searchlineedit.h" #include "../utils/misc.h" #include "../debugdialog.h" #include "../infoview/htmlinfoview.h" #include "../utils/fileprogressdialog.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../mainwindow/mainwindow.h" // TODO: PartsBinPaletteWidget should not call MainWindow functions static QString CustomIconName = "Custom1.png"; static QString CustomIconTitle = "Fritzing Custom Icon"; inline bool isCustomSvg(const QString & string) { return string.startsWith(" PaletteBinModels; static QIcon EmptyIcon; ////////////////////////////////////////////// PartsBinPaletteWidget::PartsBinPaletteWidget(ReferenceModel *referenceModel, HtmlInfoView *infoView, WaitPushUndoStack *undoStack, BinManager* manager) : QFrame(manager) { m_binLabel = NULL; m_monoIcon = m_icon = NULL; m_searchLineEdit = NULL; m_saveQuietly = false; m_fastLoaded = false; m_model = NULL; m_loadingProgressDialog = NULL; setAcceptDrops(true); setAllowsChanges(true); m_manager = manager; m_referenceModel = referenceModel; m_canDeleteModel = false; m_orderHasChanged = false; Q_UNUSED(undoStack); m_undoStack = new WaitPushUndoStack(this); connect(m_undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(undoStackCleanChanged(bool)) ); m_iconView = new PartsBinIconView(m_referenceModel, this); m_iconView->setInfoView(infoView); m_listView = new PartsBinListView(m_referenceModel, this); m_listView->setInfoView(infoView); m_stackedWidget = new QStackedWidget(this); m_stackedWidget->addWidget(m_iconView); m_stackedWidget->addWidget(m_listView); QVBoxLayout * vbl = new QVBoxLayout(this); vbl->setMargin(3); vbl->setSpacing(0); m_header = NULL; setupHeader(); if (m_header) { vbl->addWidget(m_header); QFrame * separator = new QFrame(); separator->setMaximumHeight(1); separator->setObjectName("partsBinHeaderSeparator"); separator->setFrameShape(QFrame::HLine); separator->setFrameShadow(QFrame::Plain); vbl->addWidget(separator); } vbl->addWidget(m_stackedWidget); this->setLayout(vbl); setObjectName("partsBinContainer"); toIconView(); m_defaultSaveFolder = FolderUtils::getUserBinsPath(); m_untitledFileName = tr("Untitled Bin"); connect(m_listView, SIGNAL(currentRowChanged(int)), m_iconView, SLOT(setSelected(int))); connect(m_iconView, SIGNAL(selectionChanged(int)), m_listView, SLOT(setSelected(int))); connect(m_listView, SIGNAL(currentRowChanged(int)), m_manager, SLOT(updateBinCombinedMenuCurrent())); connect(m_iconView, SIGNAL(selectionChanged(int)), m_manager, SLOT(updateBinCombinedMenuCurrent())); connect(m_listView, SIGNAL(informItemMoved(int,int)), m_iconView, SLOT(itemMoved(int,int))); connect(m_iconView, SIGNAL(informItemMoved(int,int)), m_listView, SLOT(itemMoved(int,int))); connect(m_listView, SIGNAL(informItemMoved(int,int)), this, SLOT(itemMoved())); connect(m_iconView, SIGNAL(informItemMoved(int,int)), this, SLOT(itemMoved())); if (m_binLabel) m_binLabel->setText(m_title); m_addPartToMeAction = new QAction(m_title,this); connect(m_addPartToMeAction, SIGNAL(triggered()),this, SLOT(addSketchPartToMe())); installEventFilter(this); } PartsBinPaletteWidget::~PartsBinPaletteWidget() { if (m_canDeleteModel && m_model != NULL) { delete m_model; m_model = NULL; } if (m_icon) delete m_icon; if (m_monoIcon) delete m_monoIcon; } void PartsBinPaletteWidget::cleanup() { foreach (PaletteModel * paletteModel, PaletteBinModels) { delete paletteModel; } PaletteBinModels.clear(); } QSize PartsBinPaletteWidget::sizeHint() const { return QSize(DockWidthDefault, PartsBinHeightDefault); } QString PartsBinPaletteWidget::title() const { return m_title; } void PartsBinPaletteWidget::setTitle(const QString &title) { if(m_title != title) { m_title = title; if (m_binLabel) m_binLabel->setText(title); } } void PartsBinPaletteWidget::setupHeader() { QMenu * combinedMenu = m_manager->combinedMenu(); if (combinedMenu == NULL) return; m_combinedBinMenuButton = newToolButton("partsBinCombinedMenuButton", ___emptyString___, ___emptyString___); m_combinedBinMenuButton->setMenu(combinedMenu); m_binLabel = new QLabel(this); m_binLabel->setObjectName("partsBinLabel"); m_binLabel->setWordWrap(false); m_searchLineEdit = new SearchLineEdit(this); m_searchLineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); connect(m_searchLineEdit, SIGNAL(returnPressed()), this, SLOT(search())); m_searchStackedWidget = new QStackedWidget(this); m_searchStackedWidget->setObjectName("searchStackedWidget"); m_searchStackedWidget->addWidget(m_binLabel); m_searchStackedWidget->addWidget(m_searchLineEdit); m_header = new QFrame(this); m_header->setObjectName("partsBinHeader"); QHBoxLayout * hbl = new QHBoxLayout(); hbl->setSpacing(0); hbl->setMargin(0); hbl->addWidget(m_searchStackedWidget); hbl->addWidget(m_combinedBinMenuButton); m_header->setLayout(hbl); } void PartsBinPaletteWidget::setView(PartsBinView *view) { m_currentView = view; if(m_currentView == m_iconView) { m_stackedWidget->setCurrentIndex(0); m_manager->updateViewChecks(true); } else { m_stackedWidget->setCurrentIndex(1); m_manager->updateViewChecks(false); } } void PartsBinPaletteWidget::toIconView() { setView(m_iconView); } void PartsBinPaletteWidget::toListView() { disconnect(m_listView, SIGNAL(currentRowChanged(int)), m_iconView, SLOT(setSelected(int))); setView(m_listView); connect(m_listView, SIGNAL(currentRowChanged(int)), m_iconView, SLOT(setSelected(int))); } bool PartsBinPaletteWidget::saveAsAux(const QString &filename) { FileProgressDialog progress("Saving...", 0, this); QString oldFilename = m_fileName; setFilename(filename); QString title = this->title(); if(!title.isNull() && !title.isEmpty()) { ModelPartSharedRoot * root = m_model->rootModelPartShared(); if (root) root->setTitle(title); } if(m_orderHasChanged) { m_model->setOrdererChildren(m_iconView->orderedChildren()); } m_model->save(filename, false); if(m_orderHasChanged) { m_model->setOrdererChildren(QList()); m_orderHasChanged = false; } m_undoStack->setClean(); m_location = BinLocation::findLocation(filename); if(oldFilename != m_fileName) { emit fileNameUpdated(this,m_fileName,oldFilename); } emit saved(hasAlienParts()); foreach (QString path, m_removed) { bool result = QFile::remove(path); if (!result) { DebugDialog::debug("unable to delete '" + path + "' from bin"); } } m_removed.clear(); return true; } void PartsBinPaletteWidget::loadFromModel(PaletteModel *model) { m_iconView->loadFromModel(model); m_listView->setPaletteModel(model); afterModelSetted(model); m_canDeleteModel = false; // FApplication is holding this model, so don't delete it setFilename(model->loadedFrom()); } void PartsBinPaletteWidget::setPaletteModel(PaletteModel *model, bool clear) { m_iconView->setPaletteModel(model, clear); m_listView->setPaletteModel(model, clear); afterModelSetted(model); } void PartsBinPaletteWidget::afterModelSetted(PaletteModel *model) { grabTitle(model); m_model = model; m_undoStack->setClean(); setFilename(model->loadedFrom()); } void PartsBinPaletteWidget::grabTitle(PaletteModel *model) { ModelPartSharedRoot * root = model->rootModelPartShared(); if (!root) return; QString iconFilename = root->icon(); grabTitle(root->title(), iconFilename); root->setIcon(iconFilename); if (m_searchLineEdit) m_searchLineEdit->setText(root->searchTerm()); } void PartsBinPaletteWidget::grabTitle(const QString & title, QString & iconFilename) { m_title = title; m_addPartToMeAction->setText(m_title); if (m_binLabel) m_binLabel->setText(m_title); QString temp = BinManager::StandardBinIcons.value(m_fileName, ""); if (!temp.isEmpty()) { iconFilename = temp; } else if (iconFilename.isEmpty()) { iconFilename = CustomIconName; } if (isCustomSvg(iconFilename)) { // convert to image int w = TextUtils::getViewBoxCoord(iconFilename, 2); int h = TextUtils::getViewBoxCoord(iconFilename, 3); QImage image(w, h, QImage::Format_ARGB32); image.fill(0); QRectF target(0, 0, w, h); QSvgRenderer renderer(iconFilename.toUtf8()); QPainter painter; painter.begin(&image); renderer.render(&painter, target); painter.end(); //image.save(FolderUtils::getUserDataStorePath("") + "/test icon.png"); m_icon = new QIcon(QPixmap::fromImage(image)); m_monoIcon = new QIcon(":resources/bins/icons/Custom1-mono.png"); // TODO: hack svg to make a mono icon } else { QFileInfo info(m_fileName); QDir dir = info.absoluteDir(); QString path = dir.absoluteFilePath(iconFilename); QFile file1(path); if (file1.exists()) { m_icon = new QIcon(path); } else { path = ":resources/bins/icons/" + iconFilename; QFile file2(path); if (file2.exists()) { m_icon = new QIcon(path); } } if (m_icon) { int ix = path.lastIndexOf("."); path.insert(ix, "-mono"); QFile file3(path); if (file3.exists()) { m_monoIcon = new QIcon(path); } } } } void PartsBinPaletteWidget::addPart(ModelPart *modelPart, int position) { if (m_model == NULL) { return; } ModelPart *mp = m_model->addModelPart(m_model->root(),modelPart); m_iconView->addPart(mp, position); m_listView->addPart(mp, position); if(modelPart->isAlien()) { m_alienParts << mp->moduleID(); } } QToolButton* PartsBinPaletteWidget::newToolButton(const QString& btnObjName, const QString& imgPath, const QString &text) { QToolButton *toolBtn = new QToolButton(this); toolBtn->setObjectName(btnObjName); toolBtn->setToolButtonStyle(Qt::ToolButtonIconOnly); toolBtn->setArrowType(Qt::NoArrow); if (!text.isEmpty()) { toolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolBtn->setText(text); } toolBtn->setPopupMode(QToolButton::InstantPopup); if (!imgPath.isEmpty()) { toolBtn->setIcon(QIcon(imgPath)); } toolBtn->setArrowType(Qt::NoArrow); return toolBtn; } bool PartsBinPaletteWidget::save() { bool result = true; if (FolderUtils::isEmptyFileName(m_fileName,m_untitledFileName) || currentBinIsCore()) { result = saveAs(); } else { saveAsAux(m_fileName); } return result; } bool PartsBinPaletteWidget::saveAs() { QString fileExt; QString fileName = QFileDialog::getSaveFileName( this, tr("Specify a file name"), (m_fileName.isNull() || m_fileName.isEmpty() || /* it's a resource */ m_fileName.startsWith(":"))? m_defaultSaveFolder+"/"+title()+FritzingBinExtension: m_fileName, tr("Fritzing Bin (*%1)").arg(FritzingBinExtension), &fileExt ); if (fileName.isEmpty()) return false; // Cancel pressed if(!FritzingWindow::alreadyHasExtension(fileName, FritzingBinExtension)) { fileName += FritzingBinExtension; } saveAsAux(fileName); return true; } void PartsBinPaletteWidget::saveBundledBin() { bool wasModified = m_isDirty; m_manager->mainWindow()->saveBundledNonAtomicEntity( m_fileName, FritzingBundledBinExtension, this, m_model->root()->getAllNonCoreParts(), true, "", true, false ); setDirty(wasModified); } bool PartsBinPaletteWidget::loadBundledAux(QDir &unzipDir, QList mps) { QStringList namefilters; namefilters << "*"+FritzingBinExtension; this->load(unzipDir.entryInfoList(namefilters)[0].filePath(), this, false); foreach(ModelPart* mp, mps) { if(mp->isAlien()) { // double check m_alienParts << mp->moduleID(); } } setFilename(___emptyString___); return true; } bool PartsBinPaletteWidget::open(QString fileName, QWidget * progressTarget, bool fastLoad) { QFile file(fileName); if (!file.exists()) { QMessageBox::warning(NULL, tr("Fritzing"), tr("Cannot find file %1.") .arg(fileName)); return false; } if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(NULL, tr("Fritzing"), tr("Cannot read file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } file.close(); if(fileName.endsWith(FritzingBinExtension)) { load(fileName, progressTarget, fastLoad); m_isDirty = false; } else if(fileName.endsWith(FritzingBundledBinExtension)) { return m_manager->mainWindow()->loadBundledNonAtomicEntity(fileName,this,false, false); } return true; } void PartsBinPaletteWidget::load(const QString &filename, QWidget * progressTarget, bool fastLoad) { // TODO deleting this local palette reference model deletes modelPartShared held by the palette bin modelParts //PaletteModel * paletteReferenceModel = new PaletteModel(true, true); m_location = BinLocation::findLocation(filename); //DebugDialog::debug("loading bin"); if (fastLoad) { QString binName, iconName; if (BinManager::getBinTitle(filename, binName, iconName)) { m_fileName = filename; grabTitle(binName, iconName); m_fastLoaded = true; } return; } m_fastLoaded = false; PaletteModel * paletteBinModel = PaletteBinModels.value(filename); if (paletteBinModel == NULL) { paletteBinModel = new PaletteModel(true, false); //DebugDialog::debug("after palette model"); QString name = m_title; if (name.isEmpty()) name = QFileInfo(filename).completeBaseName(); bool deleteWhenDone = false; if (progressTarget != NULL) { //DebugDialog::debug("open progress " + filename); deleteWhenDone = true; progressTarget = m_loadingProgressDialog = new FileProgressDialog(tr("Loading..."), 200, progressTarget); m_loadingProgressDialog->setBinLoadingChunk(200); m_loadingProgressDialog->setBinLoadingCount(1); m_loadingProgressDialog->setMessage(tr("loading bin '%1'").arg(name)); m_loadingProgressDialog->show(); } if (progressTarget) { connect(paletteBinModel, SIGNAL(loadingInstances(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstancesSlot(ModelBase *, QDomElement &))); connect(paletteBinModel, SIGNAL(loadingInstance(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstanceSlot(ModelBase *, QDomElement &))); connect(m_iconView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot())); connect(m_listView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot())); } DebugDialog::debug(QString("loading bin '%1'").arg(name)); bool result = paletteBinModel->loadFromFile(filename, m_referenceModel, false); //DebugDialog::debug(QString("done loading bin '%1'").arg(name)); if (!result) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Fritzing cannot load the parts bin")); } else { m_fileName = filename; setPaletteModel(paletteBinModel,true); PaletteBinModels.insert(filename, paletteBinModel); } if (progressTarget) { //DebugDialog::debug("close progress " + filename); disconnect(paletteBinModel, SIGNAL(loadingInstances(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstancesSlot(ModelBase *, QDomElement &))); disconnect(paletteBinModel, SIGNAL(loadingInstance(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstanceSlot(ModelBase *, QDomElement &))); disconnect(m_iconView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot())); disconnect(m_listView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot())); if (deleteWhenDone) { m_loadingProgressDialog->close(); delete m_loadingProgressDialog; } m_loadingProgressDialog = NULL; } } else { m_fileName = filename; setPaletteModel(paletteBinModel,true); } //DebugDialog::debug("done loading bin"); //delete paletteReferenceModel; } void PartsBinPaletteWidget::undoStackCleanChanged(bool isClean) { if(!isClean && currentBinIsCore()) { setFilename(QString::null); } setWindowModified(!isClean); m_manager->setDirtyTab(this,isClean); } bool PartsBinPaletteWidget::currentBinIsCore() { return m_fileName == BinManager::CorePartsBinLocation; } bool PartsBinPaletteWidget::beforeClosing() { bool retval; if (this->isWindowModified()) { QMessageBox::StandardButton reply; if (m_saveQuietly) { reply = QMessageBox::Save; } else { QMessageBox messageBox(this); messageBox.setWindowTitle(tr("Save bin \"%1\"").arg(title())); messageBox.setText(tr("Do you want to save the changes you made in the bin \"%1\"?").arg(title())); messageBox.setInformativeText(tr("Your changes will be lost if you don't save them.")); messageBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); messageBox.setDefaultButton(QMessageBox::Save); messageBox.setIcon(QMessageBox::Warning); messageBox.setWindowModality(Qt::WindowModal); messageBox.setButtonText(QMessageBox::Save, tr("Save")); messageBox.setButtonText(QMessageBox::Discard, tr("Don't Save")); messageBox.button(QMessageBox::Discard)->setShortcut(tr("Ctrl+D")); messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel")); reply = (QMessageBox::StandardButton)messageBox.exec(); } if (reply == QMessageBox::Save) { retval = save(); } else if (reply == QMessageBox::Discard) { retval = true; } else { retval = false; } } else { retval = true; } return retval; } void PartsBinPaletteWidget::closeEvent(QCloseEvent* event) { QFrame::closeEvent(event); } void PartsBinPaletteWidget::mousePressEvent(QMouseEvent* event) { emit focused(this); QFrame::mousePressEvent(event); } ModelPart * PartsBinPaletteWidget::selectedModelPart() { return m_currentView->selectedModelPart(); } ItemBase * PartsBinPaletteWidget::selectedItemBase() { return m_currentView->selectedItemBase(); } bool PartsBinPaletteWidget::contains(const QString &moduleID) { return m_iconView->contains(moduleID); } bool PartsBinPaletteWidget::hasAlienParts() { return m_alienParts.size() > 0; } void PartsBinPaletteWidget::addPart(const QString& moduleID, int position) { ModelPart *modelPart = m_referenceModel->retrieveModelPart(moduleID); addPart(modelPart, position); } void PartsBinPaletteWidget::removePart(const QString & moduleID, const QString & path) { m_iconView->removePart(moduleID); m_listView->removePart(moduleID); // remove the model part from the model last, as this deletes it, // and the removePart calls above still need the modelpart m_model->removePart(moduleID); if (path.contains(FolderUtils::getUserPartsPath())) { m_removed << path; } } void PartsBinPaletteWidget::removeParts() { m_iconView->removeParts(); m_listView->removeParts(); // remove the model part from the model last, as this deletes it, // and the removePart calls above still need the modelpart m_model->removeParts(); } void PartsBinPaletteWidget::removeAlienParts() { foreach(QString moduleID, m_alienParts) { removePart(moduleID, ""); } m_alienParts.clear(); } void PartsBinPaletteWidget::setInfoViewOnHover(bool infoViewOnHover) { if(m_iconView) m_iconView->setInfoViewOnHover(infoViewOnHover); if(m_listView) m_listView->setInfoViewOnHover(infoViewOnHover); } void PartsBinPaletteWidget::addPartCommand(const QString& moduleID) { /*bool updating = alreadyIn(moduleID); QString partTitle = m_referenceModel->partTitle(moduleID); if(partTitle.isEmpty()) partTitle = moduleID; QString undoStackMsg; if(!updating) { undoStackMsg = tr("\"%1\" added to bin").arg(partTitle); } else { undoStackMsg = tr("\"%1\" updated in bin").arg(partTitle); } QUndoCommand *parentCmd = new QUndoCommand(undoStackMsg); int index = m_listView->position(moduleID); new PartsBinAddCommand(this, moduleID, index, parentCmd); m_undoStack->push(parentCmd);*/ QMessageBox::StandardButton answer = QMessageBox::question( this, tr("Add to bin"), tr("Do you really want to add the selected part to the bin?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ); // TODO: make button texts translatable if(answer == QMessageBox::Yes) { int index = m_listView->position(moduleID); m_undoStack->push(new QUndoCommand("Parts bin: part added")); addPart(moduleID, index); } } void PartsBinPaletteWidget::itemMoved() { m_orderHasChanged = true; m_manager->setDirtyTab(this); } void PartsBinPaletteWidget::setDirty(bool dirty) { m_manager->setDirtyTab(this, dirty); m_isDirty = dirty; } const QString &PartsBinPaletteWidget::fileName() { return m_fileName; } bool PartsBinPaletteWidget::eventFilter(QObject *obj, QEvent *event) { if (obj == this) { if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::GraphicsSceneDragMove || event->type() == QEvent::GraphicsSceneDrop || event->type() == QEvent::GraphicsSceneMousePress ) { emit focused(this); } } return QFrame::eventFilter(obj, event); } PartsBinView *PartsBinPaletteWidget::currentView() { return m_currentView; } void PartsBinPaletteWidget::dragEnterEvent(QDragEnterEvent *event) { QFrame::dragEnterEvent(event); } void PartsBinPaletteWidget::dragLeaveEvent(QDragLeaveEvent *event) { QFrame::dragLeaveEvent(event); } void PartsBinPaletteWidget::dragMoveEvent(QDragMoveEvent *event) { QFrame::dragMoveEvent(event); } void PartsBinPaletteWidget::dropEvent(QDropEvent *event) { QFrame::dropEvent(event); } QAction *PartsBinPaletteWidget::addPartToMeAction() { return m_addPartToMeAction; } void PartsBinPaletteWidget::addSketchPartToMe() { m_manager->openBinIn(this->m_fileName, false); QString moduleID = m_manager->getSelectedModuleIDFromSketch(); if (moduleID.isEmpty()) return; bool wasAlreadyIn = contains(moduleID); addPart(moduleID, -1); if(!wasAlreadyIn) { setDirty(); } } void PartsBinPaletteWidget::setFilename(const QString &filename) { m_fileName = filename; if (m_fileName.compare(BinManager::SearchBinLocation) == 0) { m_searchStackedWidget->setCurrentIndex(1); } bool acceptIt = !currentBinIsCore(); setAcceptDrops(acceptIt); m_iconView->setAcceptDrops(acceptIt); m_listView->setAcceptDrops(acceptIt); } void PartsBinPaletteWidget::search() { SearchLineEdit * edit = qobject_cast(sender()); if (edit == NULL) return; QString searchText = edit->text(); if (searchText.isEmpty()) return; ModelPartSharedRoot * root = m_model->rootModelPartShared(); if (root) { root->setSearchTerm(searchText); } m_manager->search(searchText); } bool PartsBinPaletteWidget::allowsChanges() { return m_allowsChanges; } bool PartsBinPaletteWidget::readOnly() { return !allowsChanges(); } void PartsBinPaletteWidget::setAllowsChanges(bool allowsChanges) { m_allowsChanges = allowsChanges; } void PartsBinPaletteWidget::setReadOnly(bool readOnly) { setAllowsChanges(!readOnly); } void PartsBinPaletteWidget::focusSearch() { if (m_searchLineEdit) { QTimer::singleShot(20, this, SLOT(focusSearchAfter())); } } void PartsBinPaletteWidget::focusSearchAfter() { //DebugDialog::debug("focus search after"); if (m_searchLineEdit->decoy()) { m_searchLineEdit->setDecoy(false); } m_searchLineEdit->setFocus(Qt::OtherFocusReason); } void PartsBinPaletteWidget::setSaveQuietly(bool saveQuietly) { m_saveQuietly = saveQuietly; } bool PartsBinPaletteWidget::currentViewIsIconView() { if (m_currentView == NULL) return true; return (m_currentView == m_iconView); } QIcon PartsBinPaletteWidget::icon() { if (m_icon) return *m_icon; return EmptyIcon; } bool PartsBinPaletteWidget::hasMonoIcon() { return m_monoIcon != NULL; } QIcon PartsBinPaletteWidget::monoIcon() { if (m_monoIcon) return *m_monoIcon; return EmptyIcon; } QMenu * PartsBinPaletteWidget::combinedMenu() { return m_manager->combinedMenu(this); } QMenu * PartsBinPaletteWidget::partContextMenu() { return m_manager->partContextMenu(this); } QMenu * PartsBinPaletteWidget::binContextMenu() { QMenu * menu = m_manager->binContextMenu(this); if (menu == NULL) return NULL; QMenu * newMenu = new QMenu(); foreach (QAction * action, menu->actions()) { newMenu->addAction(action); } // TODO: need to enable/disable actions based on this bin ModelPartSharedRoot * root = (m_model == NULL) ? NULL : m_model->rootModelPartShared(); if (root) { QString iconFilename = root->icon(); if (iconFilename.compare(CustomIconName) == 0 || isCustomSvg(iconFilename)) { newMenu->addSeparator(); QAction * action = new QAction(tr("Change icon color..."), newMenu); action->setToolTip(tr("Change the color of the icon for this bin.")); connect(action, SIGNAL(triggered()), this, SLOT(changeIconColor())); newMenu->addAction(action); } } return newMenu; } void PartsBinPaletteWidget::changeIconColor() { QImage image(":resources/bins/icons/" + CustomIconName); QColor initial(image.pixel(image.width() / 2, image.height() / 2)); QColor color = QColorDialog::getColor(initial, this, tr("Select a color for this icon"), 0 ); if (!color.isValid()) return; QRgb match = initial.rgba(); for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb rgb = image.pixel(x, y); if (qRed(rgb) == qRed(match) && qBlue(rgb) == qBlue(match) && qGreen(rgb) == qGreen(match)) { image.setPixel(x, y, (color.rgb() & 0xffffff) | (qAlpha(rgb) << 24)); } } } #ifndef QT_NO_DEBUG //QFileInfo info(m_fileName); //image.save(FolderUtils::getUserDataStorePath("") + "/" + info.completeBaseName() + ".png"); #endif delete m_icon; m_icon = new QIcon(QPixmap::fromImage(image)); m_manager->setTabIcon(this, m_icon); setDirty(); ModelPartSharedRoot * root = m_model->rootModelPartShared(); if (root) { QSvgGenerator svgGenerator; svgGenerator.setResolution(90); svgGenerator.setTitle(CustomIconTitle); QBuffer buffer; svgGenerator.setOutputDevice(&buffer); QSize sz = image.size(); svgGenerator.setSize(sz); svgGenerator.setViewBox(QRect(0, 0, sz.width(), sz.height())); QPainter svgPainter(&svgGenerator); svgPainter.drawImage(QPoint(0,0), image); svgPainter.end(); QString svg(buffer.buffer()); root->setIcon(svg); } } bool PartsBinPaletteWidget::fastLoaded() { return m_fastLoaded; } BinLocation::Location PartsBinPaletteWidget::location() { return m_location; } bool PartsBinPaletteWidget::canClose() { switch (m_location) { case BinLocation::User: if (m_fileName.compare(BinManager::SearchBinLocation) == 0) return false; if (m_fileName.compare(BinManager::ContribPartsBinLocation) == 0) return false; if (m_fileName.compare(BinManager::MyPartsBinLocation) == 0) return false; if (m_manager->isTempPartsBin(this)) return false; return true; case BinLocation::More: return false; case BinLocation::App: return false; case BinLocation::Outside: default: return true; } } void PartsBinPaletteWidget::copyFilesToContrib(ModelPart * mp, QWidget * originator) { m_manager->copyFilesToContrib(mp, originator); } ModelPart * PartsBinPaletteWidget::root() { if (m_model == NULL) return NULL; return m_model->root(); } bool PartsBinPaletteWidget::isTempPartsBin() { return m_manager->isTempPartsBin(this); } void PartsBinPaletteWidget::reloadPart(const QString & moduleID) { m_iconView->reloadPart(moduleID); m_listView->reloadPart(moduleID); } QList PartsBinPaletteWidget::getAllParts() { QList empty; if (m_model == NULL) return empty; if (m_model->root() == NULL) return empty; return m_model->root()->getAllParts(); } fritzing-app-0.9.3b/src/partsbinpalette/partsbinpalettewidget.h000066400000000000000000000130011272363601300250430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . *********************************************** ********************* $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSBINPALETTEWIDGET_H_ #define PARTSBINPALETTEWIDGET_H_ #include #include #include #include #include "../model/palettemodel.h" #include "../model/modelpart.h" #include "../waitpushundostack.h" #include "../utils/fileprogressdialog.h" #include "../utils/bundler.h" #include "binmanager/binmanager.h" class PartsBinPaletteWidget : public QFrame, public Bundler { Q_OBJECT public: PartsBinPaletteWidget(class ReferenceModel *referenceModel, class HtmlInfoView *infoView, WaitPushUndoStack *undoStack, class BinManager* manager); ~PartsBinPaletteWidget(); QSize sizeHint() const; QString title() const; void setTitle(const QString &title); void loadFromModel(PaletteModel *model); void setPaletteModel(PaletteModel *model, bool clear = false); void addPart(ModelPart *modelPart, int position = -1); bool currentBinIsCore(); bool beforeClosing(); bool canClose(); ModelPart * selectedModelPart(); ItemBase * selectedItemBase(); bool hasAlienParts(); QList getAllParts(); void setInfoViewOnHover(bool infoViewOnHover); void addPart(const QString& moduleID, int position); void addNewPart(ModelPart *modelPart); void removePart(const QString & moduleID, const QString & path); void removeParts(); void load(const QString& filename, QWidget * progressTarget, bool fastLoad); bool contains(const QString &moduleID); void setDirty(bool dirty=true); const QString &fileName(); class PartsBinView *currentView(); QAction *addPartToMeAction(); bool allowsChanges(); bool readOnly(); void setAllowsChanges(bool); void setReadOnly(bool); void focusSearch(); void setSaveQuietly(bool); bool open(QString fileName, QWidget * progressTarget, bool fastLoad); bool currentViewIsIconView(); QIcon icon(); QIcon monoIcon(); bool hasMonoIcon(); void saveBundledBin(); QMenu * combinedMenu(); QMenu * binContextMenu(); QMenu * partContextMenu(); bool fastLoaded(); BinLocation::Location location(); void copyFilesToContrib(ModelPart *, QWidget * originator); ModelPart * root(); bool isTempPartsBin(); void reloadPart(const QString & moduleID); public slots: void addPartCommand(const QString& moduleID); void removeAlienParts(); void itemMoved(); void toIconView(); void toListView(); bool save(); bool saveAs(); void changeIconColor(); protected slots: void undoStackCleanChanged(bool isClean); void addSketchPartToMe(); void search(); void focusSearchAfter(); signals: void saved(bool hasPartsFromBundled); void fileNameUpdated(PartsBinPaletteWidget*, const QString &newFileName, const QString &oldFilename); void focused(PartsBinPaletteWidget*); protected: void dragEnterEvent(QDragEnterEvent *event); void dragLeaveEvent(QDragLeaveEvent *event); void dragMoveEvent(QDragMoveEvent *event); void dropEvent(QDropEvent *event); void closeEvent(QCloseEvent *event); void mousePressEvent(QMouseEvent *event); bool eventFilter(QObject *obj, QEvent *event); void setupHeader(); void grabTitle(PaletteModel *model); void grabTitle(const QString & title, QString & iconFilename); void setView(class PartsBinView *view); bool saveAsAux(const QString &filename); void afterModelSetted(PaletteModel *model); QToolButton* newToolButton(const QString& btnObjName, const QString& imgPath, const QString &text); bool loadBundledAux(QDir &unzipDir, QList mps); void setFilename(const QString &filename); protected: PaletteModel *m_model; ReferenceModel *m_referenceModel; bool m_canDeleteModel; bool m_orderHasChanged; QString m_fileName; QString m_defaultSaveFolder; QString m_untitledFileName; QString m_title; bool m_isDirty; PartsBinView *m_currentView; class PartsBinIconView *m_iconView; class PartsBinListView *m_listView; QFrame *m_header; QLabel * m_binLabel; class SearchLineEdit * m_searchLineEdit; QToolButton * m_combinedBinMenuButton; WaitPushUndoStack *m_undoStack; BinManager *m_manager; QStringList m_alienParts; bool m_allowsChanges; bool m_saveQuietly; FileProgressDialog * m_loadingProgressDialog; QIcon * m_icon; QIcon * m_monoIcon; QAction *m_addPartToMeAction; QStackedWidget * m_stackedWidget; QStackedWidget * m_searchStackedWidget; bool m_fastLoaded; BinLocation::Location m_location; QStringList m_removed; public: static void cleanup(); public: static QString Title; }; #endif /* PARTSBINPALETTEWIDGET_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/partsbinview.cpp000066400000000000000000000161471272363601300235240ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6494 $: $Author: irascibl@gmail.com $: $Date: 2012-09-29 17:40:27 +0200 (Sa, 29. Sep 2012) $ ********************************************************************/ #include #include #include #include #include "partsbinview.h" #include "partsbinpalettewidget.h" #include "../itemdrag.h" #include "../utils/misc.h" QHash PartsBinView::TranslatedCategoryNames; QHash PartsBinView::ItemBaseHash; PartsBinView::PartsBinView(ReferenceModel *referenceModel, PartsBinPaletteWidget *parent) { if (TranslatedCategoryNames.count() == 0) { TranslatedCategoryNames.insert("Basic", QObject::tr("Basic")); TranslatedCategoryNames.insert("Input", QObject::tr("Input")); TranslatedCategoryNames.insert("Output", QObject::tr("Output")); TranslatedCategoryNames.insert("ICs", QObject::tr("ICs")); TranslatedCategoryNames.insert("Power", QObject::tr("Power")); TranslatedCategoryNames.insert("Connection", QObject::tr("Connection")); TranslatedCategoryNames.insert("Microcontroller", QObject::tr("Microcontroller")); TranslatedCategoryNames.insert("Breadboard View", QObject::tr("Breadboard View")); TranslatedCategoryNames.insert("Schematic View", QObject::tr("Schematic View")); TranslatedCategoryNames.insert("PCB View", QObject::tr("PCB View")); TranslatedCategoryNames.insert("Tools", QObject::tr("Tools")); TranslatedCategoryNames.insert("Shields", QObject::tr("Shields")); TranslatedCategoryNames.insert("LilyPad", QObject::tr("LilyPad")); TranslatedCategoryNames.insert("Other", QObject::tr("Other")); TranslatedCategoryNames.insert("Sensors", QObject::tr("Sensors")); } m_referenceModel = referenceModel; m_parent = parent; } PartsBinView::~PartsBinView() { } void PartsBinView::cleanup() { foreach (ItemBase * itemBase, ItemBaseHash.values()) { delete itemBase; } } void PartsBinView::setPaletteModel(PaletteModel * model, bool clear) { if (clear) { doClear(); } if (model->root() == NULL) return; setItemAux(model->root()); setItem(model->root()); } void PartsBinView::reloadParts(PaletteModel * model) { setPaletteModel(model, true); } void PartsBinView::doClear() { m_itemBaseHash.clear(); } void PartsBinView::removePartReference(const QString & moduleID) { ItemBase * itemBase = ItemBaseHash.value(moduleID); if (itemBase) { ItemBaseHash.remove(moduleID); itemBase->deleteLater(); } } void PartsBinView::setItem(ModelPart * modelPart) { QList::const_iterator i; for (i = modelPart->children().constBegin(); i != modelPart->children().constEnd(); ++i) { setItemAux(dynamic_cast(*i)); } for (i = modelPart->children().constBegin(); i != modelPart->children().constEnd(); ++i) { setItem(dynamic_cast(*i)); } } void PartsBinView::addPart(ModelPart * model, int position) { int newPosition = setItemAux(model, position); setSelected(newPosition); } void PartsBinView::mousePressOnItem(const QPoint &dragStartPos, const QString &moduleId, const QSize &size, const QPointF &dataPoint, const QPoint &hotspot) { if (moduleId.isEmpty()) return; m_dragStartPos = dragStartPos; QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); dataStream << moduleId << dataPoint; QMimeData *mimeData = new QMimeData; mimeData->setData("application/x-dnditemdata", itemData); mimeData->setData("action", "part-reordering"); ItemDrag::setOriginator(this->m_parent); ItemDrag::setOriginatorIsTempBin(m_parent->isTempPartsBin()); QDrag * drag = new QDrag(dynamic_cast(this)); drag->setMimeData(mimeData); /* #ifndef Q_OS_LINUX // transparency doesn't seem to work for linux QPixmap pixmap(size); pixmap.fill(Qt::transparent); QPainter painter; painter.begin(&pixmap); QPen pen(QColor(0,0,0,127)); pen.setStyle(Qt::DashLine); pen.setWidth(1); painter.setPen(pen); painter.drawRect(0,0,pixmap.width() - 1, pixmap.height() - 1); painter.end(); drag->setPixmap(pixmap); drag->setHotSpot(hotspot); #else Q_UNUSED(hotspot); Q_UNUSED(size); #endif */ Q_UNUSED(hotspot); Q_UNUSED(size); // can set the pixmap, but can't hide it //QPixmap * pixmap = pitem->pixmap(); //if (pixmap != NULL) { //drag.setPixmap(*pixmap); //drag.setHotSpot(mts.toPoint() - pitem->pos().toPoint()); // // setDragCursor doesn't seem to help //drag.setDragCursor(*pitem->pixmap(), Qt::MoveAction); //drag.setDragCursor(*pitem->pixmap(), Qt::CopyAction); //drag.setDragCursor(*pitem->pixmap(), Qt::LinkAction); //drag.setDragCursor(*pitem->pixmap(), Qt::IgnoreAction); drag->exec(); /*if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) { } else { }*/ ItemDrag::dragIsDone(); } void PartsBinView::dragMoveEnterEventAux(QDragMoveEvent* event) { // Only accept if it's an icon-reordering request const QMimeData* m = event->mimeData(); QStringList formats = m->formats(); if (formats.contains("action") && (m->data("action") == "part-reordering")) { event->acceptProposedAction(); } } void PartsBinView::dropEventAux(QDropEvent* event, bool justAppend) { bool trustResult; int toIndex; if(justAppend) { toIndex = -1; trustResult = true; } else { toIndex = itemIndexAt(event->pos(), trustResult); } if(!trustResult) return; if(event->source() == dynamic_cast(this)) { int fromIndex = itemIndexAt(m_dragStartPos, trustResult); if(trustResult && fromIndex != toIndex) { moveItem(fromIndex,toIndex); } } else { QByteArray itemData = event->mimeData()->data("application/x-dnditemdata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QString moduleID; QPointF offset; dataStream >> moduleID >> offset; ModelPart * mp = m_referenceModel->retrieveModelPart(moduleID); m_parent->copyFilesToContrib(mp, ItemDrag::originator()); if(mp) { if(m_parent->contains(moduleID)) { QMessageBox::information(NULL, QObject::tr("Part already in bin"), QObject::tr("The part that you have just added,\nis already there, we won't add it again, right?")); } else { m_parent->addPart(mp,toIndex); m_parent->setDirty(); } } } event->acceptProposedAction(); } bool PartsBinView::contains(const QString &moduleID) { return m_itemBaseHash.contains(moduleID); } void PartsBinView::setInfoViewOnHover(bool infoViewOnHover) { m_infoViewOnHover = infoViewOnHover; } fritzing-app-0.9.3b/src/partsbinpalette/partsbinview.h000066400000000000000000000057341272363601300231710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSBINVIEW_H_ #define PARTSBINVIEW_H_ #include "../model/palettemodel.h" #include "../items/paletteitem.h" #include "../referencemodel/referencemodel.h" class PartsBinPaletteWidget; class PartsBinView { public: PartsBinView(ReferenceModel *referenceModel, PartsBinPaletteWidget *parent); virtual ~PartsBinView(); // removes compiler warnings virtual void setPaletteModel(PaletteModel * model, bool clear = false); void reloadParts(PaletteModel * model); void addPart(ModelPart * model, int position = -1); virtual void removePart(const QString &moduleID) = 0; virtual void removeParts() = 0; virtual ModelPart *selectedModelPart() = 0; virtual ItemBase *selectedItemBase() = 0; bool contains(const QString &moduleID); void setInfoViewOnHover(bool infoViewOnHover); virtual QList orderedChildren() = 0; virtual void reloadPart(const QString & moduleID) = 0; void dropEventAux(QDropEvent* event, bool justAppend = false); protected: virtual void doClear(); virtual void moveItem(int fromIndex, int toIndex) = 0; virtual int itemIndexAt(const QPoint& pos, bool &trustIt) = 0; virtual void setSelected(int position, bool doEmit=false) = 0; void setItem(ModelPart * modelPart); void mousePressOnItem( const QPoint &dragStartPos, const QString &moduleId, const QSize &size, const QPointF &dataPoint = QPointF(0,0), const QPoint &hotspot = QPoint(0,0)); void dragMoveEnterEventAux(QDragMoveEvent* event); virtual int setItemAux(ModelPart * modelPart, int position = -1) = 0; public: static void cleanup(); static void removePartReference(const QString & moduleID); public: static QHash TranslatedCategoryNames; protected: ReferenceModel *m_referenceModel; PartsBinPaletteWidget *m_parent; bool m_infoViewOnHover; QPoint m_dragStartPos; QHash m_itemBaseHash; static QHash ItemBaseHash; }; #endif /* PARTSBINVIEW_H_ */ fritzing-app-0.9.3b/src/partsbinpalette/searchlineedit.cpp000066400000000000000000000060201272363601300237570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "searchlineedit.h" #include "../debugdialog.h" #include #include #include static QPixmap * SearchFieldPixmap; SearchLineEdit::SearchLineEdit(QWidget * parent) : QLineEdit(parent) { SearchFieldPixmap = NULL; setDecoy(true); } SearchLineEdit::~SearchLineEdit() { } void SearchLineEdit::cleanup() { if (SearchFieldPixmap != NULL) { delete SearchFieldPixmap; SearchFieldPixmap = NULL; } } void SearchLineEdit::mousePressEvent(QMouseEvent * event) { QLineEdit::mousePressEvent(event); emit clicked(); } void SearchLineEdit::paintEvent(QPaintEvent * event) { QLineEdit::paintEvent(event); if (SearchFieldPixmap == NULL) { SearchFieldPixmap = new QPixmap(":/resources/images/icons/searchField.png"); } if (SearchFieldPixmap == NULL) return; if (SearchFieldPixmap->isNull()) return; QPainter painter(this); QSize sz = size(); int x = sz.width() - SearchFieldPixmap->width() - 2; int y = (sz.height() - SearchFieldPixmap->height()) / 2; painter.drawPixmap(x, y, SearchFieldPixmap->width(), SearchFieldPixmap->height(), *SearchFieldPixmap); } void SearchLineEdit::enterEvent(QEvent * event) { QLineEdit::enterEvent(event); if (m_decoy) { setColors(QColor(0xc8, 0xc8, 0xc8), QColor(0x57, 0x57, 0x57)); } } void SearchLineEdit::leaveEvent(QEvent * event) { QLineEdit::leaveEvent(event); if (m_decoy) { setColors(QColor(0xb3, 0xb3, 0xb3), QColor(0x57, 0x57, 0x57)); } } void SearchLineEdit::setColors(const QColor & base, const QColor & text) { setStyleSheet(QString("background: rgb(%1,%2,%3); color: rgb(%4,%5,%6);") .arg(base.red()).arg(base.green()).arg(base.blue()) .arg(text.red()).arg(text.green()).arg(text.blue()) ); } void SearchLineEdit::setDecoy(bool d) { m_decoy = d; if (m_decoy) { setReadOnly(true); setColors(QColor(0xb3, 0xb3, 0xb3), QColor(0x57, 0x57, 0x57)); } else { setReadOnly(false); setColors(QColor(0xfc, 0xfc, 0xfc), QColor(0x00, 0x00, 0x00)); } setCursor(Qt::IBeamCursor); } bool SearchLineEdit::decoy() { return m_decoy; } fritzing-app-0.9.3b/src/partsbinpalette/searchlineedit.h000066400000000000000000000030331272363601300234250ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SEARCHLINEEDIT_H #define SEARCHLINEEDIT_H #include #include class SearchLineEdit : public QLineEdit { Q_OBJECT public: SearchLineEdit(QWidget * parent = NULL); ~SearchLineEdit(); void setDecoy(bool); bool decoy(); public: static void cleanup(); signals: void clicked(); protected: void paintEvent(QPaintEvent *); void mousePressEvent(QMouseEvent *); void enterEvent( QEvent *); void leaveEvent( QEvent *); void setColors(const QColor & base, const QColor & text); protected: bool m_decoy; }; #endif fritzing-app-0.9.3b/src/partsbinpalette/svgiconwidget.cpp000066400000000000000000000155071272363601300236620ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6934 $: $Author: irascibl@gmail.com $: $Date: 2013-03-22 08:35:24 +0100 (Fr, 22. Mrz 2013) $ ********************************************************************/ #include #include #include "svgiconwidget.h" #include "../sketch/infographicsview.h" #include "../debugdialog.h" #include "../utils/misc.h" #include "../fsvgrenderer.h" #include "../items/moduleidnames.h" #include "../layerattributes.h" #include "partsbinview.h" #define SELECTED_STYLE "background-color: white;" #define NON_SELECTED_STYLE "background-color: #C2C2C2;" const QColor SectionHeaderColor(80, 80, 80); #define SELECTION_THICKNESS 2 #define HALF_SELECTION_THICKNESS 1 #define ICON_SIZE 32 #define SINGULAR_OFFSET 3 #define PLURAL_OFFSET 2 static QPixmap * PluralImage = NULL; static QPixmap * SingularImage = NULL; //////////////////////////////////////////////////////////// SvgIconPixmapItem::SvgIconPixmapItem(const QPixmap & pixmap, QGraphicsItem * parent) : QGraphicsPixmapItem(pixmap, parent) { } void SvgIconPixmapItem::setPlural(bool plural) { m_plural = plural; } void SvgIconPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QGraphicsPixmapItem::paint(painter, option, widget); if (this->parentItem()->isSelected()) { painter->save(); QPen pen = painter->pen(); pen.setColor(QColor(122, 15, 49)); pen.setWidth(SELECTION_THICKNESS); painter->setPen(pen); painter->drawRect(m_plural ? HALF_SELECTION_THICKNESS : HALF_SELECTION_THICKNESS + 1, m_plural ? HALF_SELECTION_THICKNESS : HALF_SELECTION_THICKNESS + 1, ICON_SIZE + SELECTION_THICKNESS, ICON_SIZE + SELECTION_THICKNESS); painter->restore(); } } //////////////////////////////////////////////////////////// SvgIconWidget::SvgIconWidget(ModelPart * modelPart, ViewLayer::ViewID viewID, ItemBase * itemBase, bool plural) : QGraphicsWidget() { m_moduleId = modelPart->moduleID(); m_itemBase = itemBase; if (modelPart->itemType() == ModelPart::Space) { m_moduleId = ModuleIDNames::SpacerModuleIDName; QString text = PartsBinView::TranslatedCategoryNames.value(modelPart->instanceText(), modelPart->instanceText()); this->setData(Qt::UserRole, text); if (text.isEmpty()) { this->setMaximumSize(PluralImage->size().width(), 1); } else { this->setMaximumSize(PluralImage->size().width(), 8); } setAcceptHoverEvents(false); setFlags(0); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); } else { this->setMaximumSize(PluralImage->size()); setAcceptHoverEvents(true); setFlags(QGraphicsItem::ItemIsSelectable); setupImage(plural, viewID); } } void SvgIconWidget::initNames() { if (PluralImage == NULL) { PluralImage = new QPixmap(":/resources/images/icons/parts_plural_v3_plur.png"); } if (SingularImage == NULL) { SingularImage = new QPixmap(":/resources/images/icons/parts_plural_v3_sing.png"); } } SvgIconWidget::~SvgIconWidget() { //delete m_itemBase; // these are now shared } void SvgIconWidget::cleanup() { if (PluralImage) { delete PluralImage; PluralImage = NULL; } if (SingularImage) { delete SingularImage; SingularImage = NULL; } } ItemBase *SvgIconWidget::itemBase() const { return m_itemBase; } ModelPart *SvgIconWidget::modelPart() const { return m_itemBase->modelPart(); } const QString &SvgIconWidget::moduleID() const { return m_moduleId; } void SvgIconWidget::hoverEnterEvent ( QGraphicsSceneHoverEvent * event ){ QGraphicsWidget::hoverEnterEvent(event); InfoGraphicsView * igv = InfoGraphicsView::getInfoGraphicsView(this); if (igv) { igv->hoverEnterItem(event, m_itemBase); } } void SvgIconWidget::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) { QGraphicsWidget::hoverLeaveEvent(event); InfoGraphicsView * igv = InfoGraphicsView::getInfoGraphicsView(this); if (igv) { igv->hoverLeaveItem(event, m_itemBase); } } void SvgIconWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (m_moduleId.compare(ModuleIDNames::SpacerModuleIDName) == 0) { QString text = data(Qt::UserRole).toString(); if (!text.isEmpty()) { painter->save(); QRectF r = this->boundingRect(); QPen pen = painter->pen(); pen.setColor(SectionHeaderColor); pen.setWidth(1); painter->setPen(pen); QFont font = painter->font(); font.setPointSize(8); painter->setFont(font); painter->drawText(r.left(), r.bottom(), text); //QFontMetrics fm(font); //r.setLeft(r.left() + fm.width(text)); //r.setBottom(r.bottom() + 2); //painter->drawLine(r.left(), r.bottom(), scene()->width(), r.bottom()); painter->restore(); } return; } QGraphicsWidget::paint(painter, option, widget); } void SvgIconWidget::setItemBase(ItemBase * itemBase, bool plural) { m_itemBase = itemBase; setupImage(plural, itemBase->viewID()); } void SvgIconWidget::setupImage(bool plural, ViewLayer::ViewID viewID) { LayerAttributes layerAttributes; m_itemBase->initLayerAttributes(layerAttributes, viewID, ViewLayer::Icon, ViewLayer::NewTop, false, false); ModelPart * modelPart = m_itemBase->modelPart(); FSvgRenderer * renderer = m_itemBase->setUpImage(modelPart, layerAttributes); if (renderer == NULL) { DebugDialog::debug(QString("missing renderer for icon %1").arg(modelPart->moduleID())); } if (renderer && m_itemBase) { m_itemBase->setFilename(renderer->filename()); } QPixmap pixmap(plural ? *PluralImage : *SingularImage); QPixmap * icon = (renderer == NULL) ? NULL : FSvgRenderer::getPixmap(renderer, QSize(ICON_SIZE, ICON_SIZE)); if (icon) { QPainter painter; painter.begin(&pixmap); if (plural) { painter.drawPixmap(PLURAL_OFFSET, PLURAL_OFFSET, *icon); } else { painter.drawPixmap(SINGULAR_OFFSET, SINGULAR_OFFSET, *icon); } painter.end(); delete icon; } m_pixmapItem = new SvgIconPixmapItem(pixmap, this); m_pixmapItem->setPlural(plural); m_pixmapItem->setFlags(0); m_pixmapItem->setPos(0, 0); if (m_itemBase) { m_itemBase->setTooltip(); setToolTip(m_itemBase->toolTip()); } if (renderer) { m_itemBase->setSharedRendererEx(renderer); } } fritzing-app-0.9.3b/src/partsbinpalette/svgiconwidget.h000066400000000000000000000043461272363601300233260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef SVGICONWIDGET_H_ #define SVGICONWIDGET_H_ #include #include #include #include #include #include "../model/modelpart.h" #include "../items/itembase.h" class SvgIconPixmapItem : public QGraphicsPixmapItem { public: SvgIconPixmapItem(const QPixmap & pixmap, QGraphicsItem * parent = 0); void setPlural(bool); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); protected: bool m_plural; }; class SvgIconWidget : public QGraphicsWidget { Q_OBJECT public: SvgIconWidget(ModelPart *, ViewLayer::ViewID, ItemBase *, bool plural); ~SvgIconWidget(); ItemBase * itemBase() const; ModelPart * modelPart() const; const QString &moduleID() const; void setItemBase(ItemBase *, bool plural); static void initNames(); static void cleanup(); protected: void hoverEnterEvent ( QGraphicsSceneHoverEvent * event ); void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void setupImage(bool plural, ViewLayer::ViewID viewID); protected: QPointer m_itemBase; SvgIconPixmapItem * m_pixmapItem; QString m_moduleId; }; #endif /* SVGICONWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/000077500000000000000000000000001272363601300174315ustar00rootroot00000000000000fritzing-app-0.9.3b/src/partseditor/baseremovebutton.h000066400000000000000000000032621272363601300231710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision$: $Author$: $Date$ ********************************************************************/ #ifndef BASEREMOVEBUTTON_H_ #define BASEREMOVEBUTTON_H_ #include class BaseRemoveButton : public QLabel { public: BaseRemoveButton(QWidget *parent) : QLabel(parent) { m_enterIcon = QPixmap(":/resources/images/remove_prop_enter.png"); m_leaveIcon = QPixmap(":/resources/images/remove_prop_leave.png"); setPixmap(m_leaveIcon); } protected: virtual void clicked() = 0; void mousePressEvent(QMouseEvent * event) { clicked(); QLabel::mousePressEvent(event); } void enterEvent(QEvent * event) { setPixmap(m_enterIcon); QLabel::enterEvent(event); } void leaveEvent(QEvent * event) { setPixmap(m_leaveIcon); QLabel::leaveEvent(event); } protected: QPixmap m_enterIcon; QPixmap m_leaveIcon; }; #endif /* BASEREMOVEBUTTON_H_ */ fritzing-app-0.9.3b/src/partseditor/hashpopulatewidget.cpp000066400000000000000000000143201272363601300240360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "hashpopulatewidget.h" #include "../debugdialog.h" #include "../utils/misc.h" HashLineEdit::HashLineEdit(const QString &text, bool defaultValue, QWidget *parent) : QLineEdit(text, parent) { connect(this, SIGNAL(editingFinished()), parent, SLOT(lineEditChanged())); m_firstText = text; m_isDefaultValue = defaultValue; if(defaultValue) { setStyleSheet("font-style: italic;"); connect(this,SIGNAL(textChanged(QString)),this,SLOT(updateObjectName())); } else { setStyleSheet("font-style: normal;"); } } QString HashLineEdit::textIfSetted() { if(m_isDefaultValue && !hasChanged()) { return ""; } else { return text(); } } void HashLineEdit::updateObjectName() { if(m_isDefaultValue) { if(!isModified() && !hasChanged()) { setStyleSheet("font-style: italic;"); } else { setStyleSheet("font-style: normal;"); } } } void HashLineEdit::mousePressEvent(QMouseEvent * event) { if(m_isDefaultValue && !isModified() && !hasChanged()) { setText(""); } QLineEdit::mousePressEvent(event); } bool HashLineEdit::hasChanged() { return m_firstText != text(); } void HashLineEdit::focusOutEvent(QFocusEvent * event) { if(text().isEmpty()) { setText(m_firstText); } QLineEdit::focusOutEvent(event); } HashPopulateWidget::HashPopulateWidget(const QString & title, const QHash &initValues, const QStringList &readOnlyKeys, bool keysOnly, QWidget *parent) : QFrame(parent) { m_keysOnly = keysOnly; m_lastLabel = NULL; m_lastValue = NULL; QGridLayout *layout = new QGridLayout(); layout->setColumnStretch(0,0); layout->setColumnStretch(1,1); layout->setColumnStretch(2,0); if (!title.isEmpty()) { layout->addWidget(new QLabel(title),0,0,0); } QList keys = initValues.keys(); qSort(keys); for(int i=0; i < keys.count(); i++) { HashLineEdit *name = new HashLineEdit(keys[i],false,this); HashLineEdit *value = new HashLineEdit(initValues[keys[i]],false,this); if (m_keysOnly) value->hide(); int ix = layout->rowCount(); if(readOnlyKeys.contains(keys[i])) { name->setEnabled(false); } else { HashRemoveButton *button = createRemoveButton(name, value); layout->addWidget(button,ix,3); } layout->addWidget(name,ix,0); layout->addWidget(value,ix,1,1,2); } addRow(layout); this->setLayout(layout); } HashRemoveButton *HashPopulateWidget::createRemoveButton(HashLineEdit* label, HashLineEdit* value) { HashRemoveButton *button = new HashRemoveButton(label, value, this); connect(button, SIGNAL(clicked(HashRemoveButton*)), this, SLOT(removeRow(HashRemoveButton*))); return button; } const QHash &HashPopulateWidget::hash() { static QHash theHash; theHash.clear(); for(int i=1 /*i==0 is title*/; i < gridLayout()->rowCount() /*last one is always an empty one*/; i++) { QString label; HashLineEdit *labelEdit = lineEditAt(i,0); if(labelEdit) { label = labelEdit->textIfSetted(); } QString value; HashLineEdit *valueEdit = lineEditAt(i,1); if(valueEdit) { value = valueEdit->textIfSetted(); } if(!label.isEmpty() /*&& !value.isEmpty()*/) { theHash.insert(label, value); } } return theHash; } HashLineEdit* HashPopulateWidget::lineEditAt(int row, int col) { QLayoutItem *litem = gridLayout()->itemAtPosition(row,col); return litem ? qobject_cast(litem->widget()) : NULL; } void HashPopulateWidget::addRow(QGridLayout *layout) { if(layout == NULL) { layout = gridLayout(); } if(m_lastLabel) { disconnect(m_lastLabel,SIGNAL(editingFinished()),this,SLOT(lastRowEditionCompleted())); } if(m_lastValue) { disconnect(m_lastValue,SIGNAL(editingFinished()),this,SLOT(lastRowEditionCompleted())); } int ix = layout->rowCount(); m_lastLabel = new HashLineEdit(QObject::tr("a label"),true,this); layout->addWidget(m_lastLabel,ix,0); connect(m_lastLabel,SIGNAL(editingFinished()),this,SLOT(lastRowEditionCompleted())); m_lastValue = new HashLineEdit(QObject::tr("a value"),true,this); layout->addWidget(m_lastValue,ix,1,1,2); connect(m_lastValue,SIGNAL(editingFinished()),this,SLOT(lastRowEditionCompleted())); if (m_keysOnly) m_lastValue->hide(); emit editionStarted(); } QGridLayout * HashPopulateWidget::gridLayout() { return qobject_cast(this->layout()); } void HashPopulateWidget::lastRowEditionCompleted() { if( m_lastLabel && !m_lastLabel->text().isEmpty() && m_lastLabel->hasChanged()) { if(m_lastLabel->text().isEmpty() && m_lastValue->text().isEmpty()) { // removeRow() ?; } else { HashRemoveButton *button = createRemoveButton(m_lastLabel, m_lastValue); for (int i = 0; i < gridLayout()->rowCount(); i++) { HashLineEdit * label = lineEditAt(i, 0); if (m_lastLabel == label) { gridLayout()->addWidget(button,i,3); break; } } addRow(); } } } void HashPopulateWidget::removeRow(HashRemoveButton* button) { QLayout *lo = layout(); QList widgs; widgs << button->label() << button->value() << button; foreach(QWidget* w, widgs) { lo->removeWidget(w); //w->hide(); //delete w; } lo->update(); emit changed(); } void HashPopulateWidget::lineEditChanged() { emit changed(); } fritzing-app-0.9.3b/src/partseditor/hashpopulatewidget.h000066400000000000000000000053321272363601300235060ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef HASHPOPULATEWIDGET_H_ #define HASHPOPULATEWIDGET_H_ #include #include #include #include #include "baseremovebutton.h" class HashLineEdit : public QLineEdit { Q_OBJECT public: HashLineEdit(const QString &text, bool defaultValue = false, QWidget *parent = 0); bool hasChanged(); QString textIfSetted(); protected slots: void updateObjectName(); protected: void mousePressEvent(QMouseEvent * event); void focusOutEvent(QFocusEvent * event); QString m_firstText; bool m_isDefaultValue; }; class HashRemoveButton : public BaseRemoveButton { Q_OBJECT public: HashRemoveButton(QWidget* label, QWidget* value, QWidget *parent) : BaseRemoveButton(parent) { m_label = label; m_value = value; } QWidget *label() {return m_label;} QWidget *value() {return m_value;} signals: void clicked(HashRemoveButton*); protected: void clicked() { emit clicked(this); } QWidget *m_label; QWidget *m_value; }; class HashPopulateWidget : public QFrame { Q_OBJECT public: HashPopulateWidget(const QString & title, const QHash &initValues, const QStringList &readOnlyKeys, bool keysOnly, QWidget *parent); const QHash & hash(); HashLineEdit* lineEditAt(int row, int col); protected slots: void lastRowEditionCompleted(); void removeRow(HashRemoveButton*); void lineEditChanged(); signals: void editionStarted(); void changed(); protected: void addRow(QGridLayout *layout = 0); QGridLayout* gridLayout(); HashRemoveButton *createRemoveButton(HashLineEdit* label, HashLineEdit* value); HashLineEdit *m_lastLabel; HashLineEdit *m_lastValue; bool m_keysOnly; }; #endif /* HASHPOPULATEWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/kicadmoduledialog.cpp000066400000000000000000000050631272363601300236020ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6385 $: $Author: cohen@irascible.com $: $Date: 2012-09-08 21:21:20 +0200 (Sa, 08. Sep 2012) $ ********************************************************************/ #include "kicadmoduledialog.h" #include "../debugdialog.h" #include "../connectors/connectoritem.h" #include "../sketch/pcbsketchwidget.h" #include #include #include #include #include ///////////////////////////////////////////////////////// KicadModuleDialog::KicadModuleDialog(const QString & partType, const QString & filename, const QStringList & modules, QWidget *parent) : QDialog(parent) { this->setWindowTitle(QObject::tr("Select %1").arg(partType)); QVBoxLayout * vLayout = new QVBoxLayout(this); QFrame * frame = new QFrame(this); QFormLayout * formLayout = new QFormLayout(); m_comboBox = new QComboBox(this); m_comboBox->addItems(modules); formLayout->addRow(QString("%1:").arg(partType), m_comboBox ); frame->setLayout(formLayout); QLabel * label = new QLabel(QString("There are %1 %3 descriptions in '%2'. Please select one.").arg(modules.count()).arg(filename).arg(partType)); vLayout->addWidget(label); vLayout->addWidget(frame); QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vLayout->addWidget(buttonBox); this->setLayout(vLayout); } KicadModuleDialog::~KicadModuleDialog() { } const QString KicadModuleDialog::selectedModule() { return m_comboBox->currentText(); } fritzing-app-0.9.3b/src/partseditor/kicadmoduledialog.h000066400000000000000000000025631272363601300232510ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6385 $: $Author: cohen@irascible.com $: $Date: 2012-09-08 21:21:20 +0200 (Sa, 08. Sep 2012) $ ********************************************************************/ #ifndef KICADMODULEDIALOG_H #define KICADMODULEDIALOG_H #include #include class KicadModuleDialog : public QDialog { Q_OBJECT public: KicadModuleDialog(const QString & partType, const QString & filename, const QStringList & moduleNames, QWidget *parent = 0); ~KicadModuleDialog(); const QString selectedModule(); protected: QComboBox * m_comboBox; }; #endif fritzing-app-0.9.3b/src/partseditor/obsolete/000077500000000000000000000000001272363601300212455ustar00rootroot00000000000000fritzing-app-0.9.3b/src/partseditor/obsolete/abstractconnectorinfowidget.cpp000066400000000000000000000045201272363601300275500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "abstractconnectorinfowidget.h" #include "connectorinforemovebutton.h" #include "connectorsinfowidget.h" #include "../utils/misc.h" #include int AbstractConnectorInfoWidget::SingleConnectorHeight = 40; AbstractConnectorInfoWidget::AbstractConnectorInfoWidget(ConnectorsInfoWidget *topLevelContainer, QWidget *parent) : QFrame(parent) { m_topLevelContainer = topLevelContainer; m_removeButton = new ConnectorInfoRemoveButton(this); connect( m_removeButton, SIGNAL(clicked(AbstractConnectorInfoWidget*)), topLevelContainer, SLOT(removeConnector(AbstractConnectorInfoWidget*)) ); setMinimumWidth(100); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); setMinimumHeight(SingleConnectorHeight); } void AbstractConnectorInfoWidget::setSelected(bool selected, bool doEmitChange) { m_isSelected = selected; setProperty("selected",m_isSelected); reapplyStyle(); if(selected) { setFocus(); if(doEmitChange) { emit tellSistersImNewSelected(this); } } } void AbstractConnectorInfoWidget::reapplyStyle() { QString path = ":/resources/styles/partseditor.qss"; QFile styleSheet(path); if (!styleSheet.open(QIODevice::ReadOnly)) { qWarning("Unable to open :/resources/styles/partseditor.qss"); } else { setStyleSheet(styleSheet.readAll()); } } bool AbstractConnectorInfoWidget::isSelected() { return m_isSelected; } fritzing-app-0.9.3b/src/partseditor/obsolete/abstractconnectorinfowidget.h000066400000000000000000000035411272363601300272170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ABSTRACTCONNECTORINFOWIDGET_H_ #define ABSTRACTCONNECTORINFOWIDGET_H_ #include #include class ConnectorInfoRemoveButton; class AbstractConnectorInfoWidget : public QFrame { Q_OBJECT public: AbstractConnectorInfoWidget(class ConnectorsInfoWidget *topLevelContainer, QWidget *parent=0); virtual void setSelected(bool selected, bool doEmitChange=true); bool isSelected(); signals: void tellSistersImNewSelected(AbstractConnectorInfoWidget*); // Meant to be used in the info context void tellViewsMyConnectorIsNewSelected(const QString&); // Meant to be used in the info context void repaintNeeded(); protected: void reapplyStyle(); ConnectorsInfoWidget *m_topLevelContainer; ConnectorInfoRemoveButton *m_removeButton; volatile bool m_isSelected; static int SingleConnectorHeight; }; #endif /* ABSTRACTCONNECTORINFOWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/abstracteditablelabelwidget.cpp000066400000000000000000000073361272363601300274630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "abstracteditablelabelwidget.h" #include "../utils/misc.h" AbstractEditableLabelWidget::AbstractEditableLabelWidget(QString text, WaitPushUndoStack *undoStack, QWidget *parent, QString title, bool edited, bool noSpacing) : QFrame(parent) { m_noSpacing = noSpacing; m_edited = edited; m_isInEditionMode = false; m_undoStack = undoStack; QGridLayout *layout = new QGridLayout; if(!title.isNull() && !title.isEmpty()) { m_title = new QLabel(title, this); m_title->setObjectName("title"); layout->addWidget(m_title,0,0); } else { m_title = NULL; } setLayout(layout); m_label = new EditableLabel(text, this); connect(m_label,SIGNAL(editionStarted(QString)),this,SLOT(editionStarted(QString))); connect(this,SIGNAL(editionCompleted(QString)),m_label,SLOT(editionCompleted(QString))); m_acceptButton = new QPushButton(tr("Accept"), this); connect(m_acceptButton,SIGNAL(clicked()),this,SLOT(informEditionCompleted())); m_cancelButton = new QPushButton(tr("Cancel"), this); connect(m_cancelButton,SIGNAL(clicked()),this,SLOT(editionCanceled())); } QString AbstractEditableLabelWidget::text() { if(m_edited) { return m_label->text(); } else { return ___emptyString___; } } void AbstractEditableLabelWidget::editionStarted(QString text) { setEditionText(text); toEditionMode(); } void AbstractEditableLabelWidget::informEditionCompleted() { if(m_isInEditionMode) { m_undoStack->push(new QUndoCommand("Dummy parts editor command")); m_edited = true; emit editionCompleted(editionText()); toStandardMode(); } } void AbstractEditableLabelWidget::editionCanceled() { toStandardMode(); } void AbstractEditableLabelWidget::toStandardMode() { m_isInEditionMode = false; hide(); QGridLayout *layout = (QGridLayout*)this->layout(); myEditionWidget()->hide(); layout->removeWidget(myEditionWidget()); m_acceptButton->hide(); layout->removeWidget(m_acceptButton); m_cancelButton->hide(); layout->removeWidget(m_cancelButton); m_label->show(); layout->addWidget(m_label,1,0); setNoSpacing(layout); updateGeometry(); show(); emit editionFinished(); } void AbstractEditableLabelWidget::toEditionMode() { m_isInEditionMode = true; hide(); QGridLayout *layout = (QGridLayout*)this->layout(); m_label->hide(); layout->removeWidget(m_label); if(!m_edited) { setEmptyTextToEdit(); } myEditionWidget()->show(); layout->addWidget(myEditionWidget(),1,0,1,5); m_acceptButton->show(); layout->addWidget(m_acceptButton,2,3); m_cancelButton->show(); layout->addWidget(m_cancelButton,2,4); setNoSpacing(layout); updateGeometry(); show(); emit editionStarted(); } void AbstractEditableLabelWidget::setNoSpacing(QLayout *layout) { if(m_noSpacing) { layout->setMargin(0); layout->setSpacing(0); } } fritzing-app-0.9.3b/src/partseditor/obsolete/abstracteditablelabelwidget.h000066400000000000000000000043341272363601300271230ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not,AbstractEditableLabelWidget see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include "editablelabel.h" #include "../waitpushundostack.h" #ifndef ABSTRACTEDITABLELABELWIDGET_H_ #define ABSTRACTEDITABLELABELWIDGET_H_ class AbstractEditableLabelWidget : public QFrame { Q_OBJECT public: AbstractEditableLabelWidget(QString text, WaitPushUndoStack *undoStack, QWidget *parent=0, QString title="", bool edited=false, bool noSpacing=false); QString text(); protected slots: void editionStarted(QString text); void informEditionCompleted(); void editionCanceled(); signals: void editionCompleted(QString text); void editionStarted(); void editionFinished(); protected: void toStandardMode(); void toEditionMode(); void setNoSpacing(QLayout *layout); virtual QString editionText()=0; virtual void setEditionText(QString text)=0; virtual QWidget* myEditionWidget()=0; virtual void setEmptyTextToEdit()=0; QLabel *m_title; EditableLabel *m_label; QPushButton *m_acceptButton; QPushButton *m_cancelButton; class WaitPushUndoStack * m_undoStack; bool m_noSpacing; bool m_edited; volatile bool m_isInEditionMode; }; #endif /* ABSTRACTEDITABLELABELWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/abstractimagebutton.h000066400000000000000000000033521272363601300254630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ABSTRACTIMAGEBUTTON_H_ #define ABSTRACTIMAGEBUTTON_H_ #include #include "abstractstatesbutton.h" class AbstractImageButton : public QLabel, public AbstractStatesButton { Q_OBJECT public: AbstractImageButton(QWidget *parent=0) : QLabel(parent) { }; virtual ~AbstractImageButton() {} signals: void clicked(); protected: virtual QString imagePrefix() = 0; void setImage(const QPixmap & pixmap) { setPixmap(pixmap); } void mousePressEvent(QMouseEvent * event) { setPressedIcon(); QLabel::mousePressEvent(event); } void mouseReleaseEvent(QMouseEvent * event) { if(isEnabled()) { emit clicked(); setEnabledIcon(); } QLabel::mouseReleaseEvent(event); } }; #endif /* ABSTRACTIMAGEBUTTON_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/addremoveconnectorbutton.h000066400000000000000000000027171272363601300265420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ADDREMOVECONNECTORBUTTON_H_ #define ADDREMOVECONNECTORBUTTON_H_ #include "../utils/abstractimagebutton.h" class AddRemoveConnectorButton : public AbstractImageButton { public: AddRemoveConnectorButton(const QString &imageName, QWidget *parent=0) : AbstractImageButton(parent) { setupIcons(imageName); setEnabledIcon(); }; protected: QString imagePrefix() { return ":/resources/images/icons/partsEditorConnector"; } }; #endif /* ADDREMOVECONNECTORBUTTON_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/addremovelistwidget.cpp000066400000000000000000000063311272363601300260220ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "addremovelistwidget.h" #include "../utils/misc.h" #define BUTTON_SIZE 25 AddRemoveListWidget::AddRemoveListWidget(QString title, QWidget *parent) : QGroupBox(parent) { m_label = new QLabel(title+":"); //setFixedWidth(parentWidget()->width()); m_addButton = new QPushButton(QIcon(":/resources/images/add.png"),"",this); m_addButton->setFixedSize(BUTTON_SIZE,BUTTON_SIZE); connect(m_addButton,SIGNAL(clicked()),this,SLOT(addItem())); m_removeButton = new QPushButton(QIcon(":/resources/images/remove.png"),"",this); m_removeButton->setFixedSize(BUTTON_SIZE,BUTTON_SIZE); connect(m_removeButton,SIGNAL(clicked()),this,SLOT(removeSelectedItems())); m_list = new QListWidget(this); m_list->setSelectionMode(QAbstractItemView::ContiguousSelection); m_list->setFixedHeight(BUTTON_SIZE*3); m_list->setSortingEnabled(true); int row=0; QGridLayout *layout = new QGridLayout; layout->setSpacing(1); layout->setMargin(3); layout->addWidget(m_label,row,0); layout->addWidget(m_addButton,row,1); layout->addWidget(m_removeButton,row++,2); layout->addWidget(m_list,row,0,row,3); row++; setLayout(layout); } void AddRemoveListWidget::addItem() { addItem(m_label->text().toLower().remove("s:")); } void AddRemoveListWidget::addItem(QString itemText) { QListWidgetItem *item = new QListWidgetItem(); item->setFlags(item->flags() | Qt::ItemIsEditable); item->setText(itemText); m_list->addItem(item); m_list->editItem(item); } void AddRemoveListWidget::removeSelectedItems() { QList selitems = m_list->selectedItems(); for(int i=0; i < selitems.size(); i++) { m_list->takeItem(m_list->row(selitems[i])); } } int AddRemoveListWidget::count() { if(m_list != NULL) { return m_list->count(); } else { return 0; } } QListWidgetItem* AddRemoveListWidget::itemAt(int rowIdx) { if(m_list != NULL) { return m_list->item(rowIdx); } else { return NULL; } } QStringList& AddRemoveListWidget::getItemsText() { QStringList *retval = new QStringList(); for(int i=0; i < count(); i++) { *retval << itemAt(i)->text(); } return *retval; } void AddRemoveListWidget::setItemsText(const QStringList& texts) { for(int i=0; i < texts.count(); i++) { addItem(texts[i]); } m_list->sortItems(); } fritzing-app-0.9.3b/src/partseditor/obsolete/addremovelistwidget.h000066400000000000000000000032021272363601300254610ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ADDREMOVELISTWIDGET_H_ #define ADDREMOVELISTWIDGET_H_ #include #include #include #include class AddRemoveListWidget : public QGroupBox { Q_OBJECT public: AddRemoveListWidget(QString title, QWidget *parent=0); int count(); QListWidgetItem* itemAt(int rowIdx); QStringList& getItemsText(); void setItemsText(const QStringList& texts); protected slots: void addItem(); void addItem(QString itemText); void removeSelectedItems(); protected: QLabel *m_label; QPushButton *m_addButton; QPushButton *m_removeButton; QListWidget *m_list; }; #endif /* ADDREMOVELISTWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/connectorinforemovebutton.h000066400000000000000000000032301272363601300267340ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef CONNECTORINFOREMOVEBUTTON_H_ #define CONNECTORINFOREMOVEBUTTON_H_ #include "baseremovebutton.h" #include "abstractconnectorinfowidget.h" class ConnectorInfoRemoveButton : public BaseRemoveButton { Q_OBJECT public: ConnectorInfoRemoveButton(AbstractConnectorInfoWidget* parent) : BaseRemoveButton(parent) { m_connInfo = parent; } signals: void clicked(AbstractConnectorInfoWidget*); protected: void mousePressEvent(QMouseEvent * event) { Q_UNUSED(event) clicked(); //QLabel::mousePressEvent(event); } void clicked() { emit clicked(m_connInfo); } AbstractConnectorInfoWidget *m_connInfo; }; #endif /* CONNECTORINFOREMOVEBUTTON_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/connectorrectangle.cpp000066400000000000000000000117411272363601300256340ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include "connectorrectangle.h" #include "../sketch/zoomablegraphicsview.h" #include "../debugdialog.h" double ConnectorRectangle::ErrorIconSize = 6; ConnectorRectangle::ConnectorRectangle(QGraphicsItem* owner, bool withHandlers) : QObject() { m_minWidth = m_minHeight = 0; m_owner = owner; m_firstPaint = true; if(withHandlers) { m_topLeftHandler = new CornerHandler(this, owner, Qt::TopLeftCorner); m_topRightHandler = new CornerHandler(this, owner, Qt::TopRightCorner); m_bottomRightHandler = new CornerHandler(this, owner, Qt::BottomRightCorner); m_bottomLeftHandler = new CornerHandler(this, owner, Qt::BottomLeftCorner); m_cornerHandlers << m_topLeftHandler << m_topRightHandler << m_bottomRightHandler << m_bottomLeftHandler; setHandlersVisible(false); } else { m_topLeftHandler = m_topRightHandler = m_bottomRightHandler = m_bottomLeftHandler = NULL; } } ConnectorRectangle::~ConnectorRectangle() { } void ConnectorRectangle::setHandlersVisible(bool visible) { foreach(CornerHandler* handler, m_cornerHandlers) { handler->doSetVisible(visible); } } void ConnectorRectangle::resizeRect(double x1, double y1, double x2, double y2) { double width = x2-x1 < m_minWidth ? m_minWidth : x2-x1; double height = y2-y1 < m_minHeight ? m_minHeight : y2-y1; QRectF rect = owner()->boundingRect(); if(width != rect.width() && height != rect.height()) { emit resizeSignal(x1,y1,width,height); } } bool ConnectorRectangle::isResizable() { bool resizable; emit isResizableSignal(resizable); // must be connected via Qt::DirectConnection return resizable; } void ConnectorRectangle::paint(QPainter *painter) { QRectF rect = m_owner->boundingRect(); if(m_firstPaint && rect.width() > 0 && rect.height() > 0) { placeHandlers(); m_firstPaint = false; } bool beingResized = false; foreach(CornerHandler* handler, m_cornerHandlers) { if(handler->isBeingDragged()) { beingResized = true; break; } } if(beingResized) { foreach(CornerHandler* handler, m_cornerHandlers) { handler->doPaint(painter); } } } void ConnectorRectangle::resizingStarted() { foreach(CornerHandler* handler, m_cornerHandlers) { handler->doSetVisible(false); } } void ConnectorRectangle::resizingFinished() { foreach(CornerHandler* handler, m_cornerHandlers) { handler->doSetVisible(true); setHandlerRect(handler); } } void ConnectorRectangle::placeHandlers() { foreach(CornerHandler* handler, m_cornerHandlers) { setHandlerRect(handler); } } void ConnectorRectangle::setHandlerRect(CornerHandler* handler) { handler->doSetRect(handlerRect(handler->corner())); } QRectF ConnectorRectangle::handlerRect(Qt::Corner corner) { QRectF rect = m_owner->boundingRect(); double scale = currentScale(); //DebugDialog::debug(QString("scale %1").arg(scale)); QPointF offset(CornerHandler::Size/scale,CornerHandler::Size/scale); QPointF cornerPoint; switch(corner) { case Qt::TopLeftCorner: cornerPoint=rect.topLeft(); break; case Qt::TopRightCorner: cornerPoint=rect.topRight(); break; case Qt::BottomRightCorner: cornerPoint=rect.bottomRight(); break; case Qt::BottomLeftCorner: cornerPoint=rect.bottomLeft(); break; default: throw "ConnectorRectangle::handlerRect: unknown corner"; } return QRectF(cornerPoint-offset,cornerPoint+offset); } QRectF ConnectorRectangle::errorIconRect() { QRectF rect = m_owner->boundingRect(); double scale = currentScale(); QPointF offset(ErrorIconSize/scale,ErrorIconSize/scale); QPointF cornerPoint = rect.topLeft()-offset; return QRectF(cornerPoint-offset,cornerPoint+offset); } double ConnectorRectangle::currentScale() { if(m_owner->scene()) { ZoomableGraphicsView *sw = dynamic_cast(m_owner->scene()->parent()); if(sw) { return sw->currentZoom()/100; } } return 1; } QGraphicsItem *ConnectorRectangle::owner() { return m_owner; } void ConnectorRectangle::setMinSize(double minWidth, double minHeight) { m_minWidth = minWidth; m_minHeight = minHeight; } fritzing-app-0.9.3b/src/partseditor/obsolete/connectorrectangle.h000066400000000000000000000043461272363601300253040ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef CONNECTORRECTANGLE_H_ #define CONNECTORRECTANGLE_H_ #include "cornerhandler.h" class ConnectorRectangle : public QObject { Q_OBJECT public: enum State { Normal = 0x00000, Highlighted = 0x00001, Hover = 0x00002, Selected = 0x00003 }; ConnectorRectangle(QGraphicsItem* owner, bool withHandlers = true); ~ConnectorRectangle(); QGraphicsItem *owner(); void resizeRect(double x1, double y1, double x2, double y2); bool isResizable(); void resizingStarted(); void resizingFinished(); double currentScale(); void setMinSize(double minWidth, double minHeight); void setHandlersVisible(bool visible); QRectF handlerRect(Qt::Corner corner); QRectF errorIconRect(); void paint(QPainter *painter); signals: void resizeSignal(double x1, double y1, double x2, double y2); void isResizableSignal(bool & resizable); protected: void setHandlerRect(CornerHandler* handler); void placeHandlers(); QGraphicsItem *m_owner; CornerHandler *m_topLeftHandler; CornerHandler *m_topRightHandler; CornerHandler *m_bottomRightHandler; CornerHandler *m_bottomLeftHandler; QList m_cornerHandlers; double m_minWidth; double m_minHeight; bool m_firstPaint; public: static double ErrorIconSize; }; #endif /* CONNECTORRECTANGLE_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/connectorsinfowidget.cpp000066400000000000000000000511501272363601300262100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include "connectorsinfowidget.h" #include "addremoveconnectorbutton.h" #include "partseditorviewswidget.h" #include "../debugdialog.h" #include "../connectors/connectorshared.h" #include "../processeventblocker.h" ConnectorsInfoWidget::ConnectorsInfoWidget(WaitPushUndoStack *undoStack, QWidget *parent) : QFrame(parent) { m_objToDelete = NULL; m_selected = NULL; m_undoStack = undoStack; m_views = NULL; createScrollArea(); createToolsArea(); QGridLayout *layout = new QGridLayout(this); layout->addWidget(m_title,0,0); layout->addWidget(m_scrollArea,1,0); layout->addWidget(m_toolsContainter,2,0); layout->setContentsMargins(3, 10, 3, 10); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); setFocusPolicy(Qt::StrongFocus); installEventFilter(this); m_connRemoved = false; m_connAdded = false; } void ConnectorsInfoWidget::emitPaintNeeded() { emit repaintNeeded(); } void ConnectorsInfoWidget::createToolsArea() { m_toolsContainter = new QFrame(this); QHBoxLayout *lo = new QHBoxLayout(m_toolsContainter); QPushButton *addBtn = new QPushButton(QObject::tr("Add connector"),this); connect(addBtn, SIGNAL(clicked()), this, SLOT(addConnector())); lo->setMargin(2); lo->setSpacing(2); lo->addWidget(addBtn); lo->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding)); } void ConnectorsInfoWidget::createScrollArea() { m_scrollContent = new QFrame(); m_scrollContent->setObjectName("connInfoContent"); m_scrollContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_mismatchersFrameParent = new QFrame(this); m_mismatchersFrameParent->setObjectName("mismatchConns"); m_mismatchersFrameParent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); QVBoxLayout *parentLo = new QVBoxLayout(m_mismatchersFrameParent); parentLo->setMargin(1); m_mismatchersFrame = new QFrame(m_mismatchersFrameParent); //m_mismatchersFrame->resize(this->width(),m_mismatchersFrame->height()); QVBoxLayout *mismatchLayout = new QVBoxLayout(m_mismatchersFrame); mismatchLayout->setMargin(0); mismatchLayout->setSpacing(0); m_mismatchersFrame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); QLabel *mismatchConnHeader = new QLabel(tr("Mismatching Connector IDs")); mismatchConnHeader->setObjectName("mismatchConnsHeader"); parentLo->addWidget(mismatchConnHeader); parentLo->addWidget(m_mismatchersFrame); /*QLabel *mismatchConnFooter = new QLabel(tr("These problems need to be fixed in the svg-files directly")); mismatchConnFooter->setObjectName("mismatchConnsFooter"); parentLo->addWidget(mismatchConnFooter);*/ m_mismatchersFrameParent->hide(); QVBoxLayout *scrollLayout = new QVBoxLayout(m_scrollContent); scrollLayout->setMargin(0); scrollLayout->setSpacing(0); scrollContentLayout()->addWidget(m_mismatchersFrameParent); scrollContentLayout()->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::MinimumExpanding)); m_scrollArea = new QScrollArea(this); m_scrollArea->setWidget(m_scrollContent); m_scrollArea->setWidgetResizable(true); m_title = new QLabel(" "+tr("List of Connectors")); m_title->setObjectName("title"); } void ConnectorsInfoWidget::selectionChanged(AbstractConnectorInfoWidget* selected) { if(m_selected) { m_selected->setSelected(false); } m_selected = selected; QTimer::singleShot(20, this, SLOT(emitPaintNeeded())); } void ConnectorsInfoWidget::setSelected(AbstractConnectorInfoWidget * newSelected) { if(newSelected != m_selected) { newSelected->setSelected(true,false); selectionChanged(newSelected); } } bool ConnectorsInfoWidget::eventFilter(QObject *obj, QEvent *event) { if(obj == this) { if(event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride) { QKeyEvent *keyEvent = (QKeyEvent*)event; if(keyEvent->key() == Qt::Key_Up) { selectPrev(); return true; } else if(keyEvent->key() == Qt::Key_Down) { selectNext(); return true; } } } return false; } /*void ConnectorsInfoWidget::ensureSelection(int i) { // TODO Mariano: Strange bug (if this is set, each up or dow key pressed event triggers twice) if(i==1) { // 1 == the fixed idx of the tab bool found = false; if(!m_selected) { if(m_connsInfo.size() > 0) { setSelected(m_connsInfo[0]); found = true; } else if(m_mismatchConnsInfo.size() > 0) { setSelected(m_mismatchConnsInfo[0]); found = true; } if(found) { this->setFocus(); } } } }*/ void ConnectorsInfoWidget::selectNext() { if(m_selected) { int selIdx = m_connsInfo.indexOf((SingleConnectorInfoWidget*)m_selected); if(selIdx > -1 && selIdx < m_connsInfo.size()-1) { // It's a single connector setSelected(m_connsInfo[selIdx+1]); } else { selIdx = m_mismatchConnsInfo.indexOf((MismatchingConnectorWidget*)m_selected); if(selIdx > -1) { if(selIdx < m_mismatchConnsInfo.size()-1) { setSelected(m_mismatchConnsInfo[selIdx+1]); } else if(m_connsInfo.size() > 0) { setSelected(m_connsInfo[0]); } } } } } void ConnectorsInfoWidget::selectPrev() { if(m_selected) { int selIdx = m_connsInfo.indexOf((SingleConnectorInfoWidget*)m_selected); if(selIdx > -1 ) { // It's a single connector and not the first if(selIdx > 0) { setSelected(m_connsInfo[selIdx-1]); } else if(m_mismatchConnsInfo.size()>0) { setSelected(m_mismatchConnsInfo[m_mismatchConnsInfo.size()-1]); } } else { selIdx = m_mismatchConnsInfo.indexOf((MismatchingConnectorWidget*)m_selected); if(selIdx > 0) { // it's a mismatch setSelected(m_mismatchConnsInfo[selIdx-1]); } } } } void ConnectorsInfoWidget::updateLayout() { m_mismatchersFrame->updateGeometry(); m_mismatchersFrame->adjustSize(); m_mismatchersFrameParent->updateGeometry(); m_mismatchersFrameParent->adjustSize(); m_scrollContent->adjustSize(); } void ConnectorsInfoWidget::addConnectorInfo(MismatchingConnectorWidget* mcw) { if(mcw->prevConn()) { addConnectorInfo(mcw->prevConn()); } else { addConnectorInfo(mcw->connId()); } } Connector* ConnectorsInfoWidget::addConnectorInfo(QString id) { ConnectorShared *connShared = new ConnectorShared(); connShared->setId(id); connShared->addPin( ViewLayer::BreadboardView, m_views->breadboardView()->svgIdForConnector(id), m_views->breadboardView()->connectorsLayerId(), m_views->breadboardView()->terminalIdForConnector(id), "", false ); connShared->addPin( ViewLayer::SchematicView, m_views->schematicView()->svgIdForConnector(id), m_views->schematicView()->connectorsLayerId(), m_views->schematicView()->terminalIdForConnector(id), "", false ); connShared->addPin( ViewLayer::PCBView, m_views->pcbView()->svgIdForConnector(id), m_views->pcbView()->connectorsLayerId(), m_views->pcbView()->terminalIdForConnector(id), "", false ); Connector *conn = new Connector(connShared,0); // modelPart =? null addConnectorInfo(conn); return conn; } void ConnectorsInfoWidget::addConnectorInfo(Connector *conn) { QString connId = conn->connectorSharedID(); m_connIds << connId; int connCount = m_connsInfo.size(); SingleConnectorInfoWidget *sci = new SingleConnectorInfoWidget(this, m_undoStack, conn, m_scrollContent); scrollContentLayout()->insertWidget(connCount+1,sci); m_connsInfo << sci; m_allConnsInfo[connId] = sci; connect(sci,SIGNAL(editionStarted()),this,SLOT(updateLayout())); connect(sci,SIGNAL(editionFinished()),this,SLOT(updateLayout())); connect(sci,SIGNAL(tellSistersImNewSelected(AbstractConnectorInfoWidget*)),this,SLOT(selectionChanged(AbstractConnectorInfoWidget*))); connect(sci,SIGNAL(tellViewsMyConnectorIsNewSelected(const QString&)),this,SLOT(informConnectorSelection(const QString &))); connect(this,SIGNAL(editionCompleted()),sci,SLOT(editionCompleted())); m_scrollContent->updateGeometry(); } void ConnectorsInfoWidget::addMismatchingConnectorInfo(ViewLayer::ViewIdentifier viewId, QString connId) { m_connIds << connId; MismatchingConnectorWidget *mcw = new MismatchingConnectorWidget(this,viewId,connId,m_mismatchersFrame); addMismatchingConnectorInfo(mcw); connect( mcw, SIGNAL(completeConn(MismatchingConnectorWidget*)), this, SLOT(completeConn(MismatchingConnectorWidget*)) ); } void ConnectorsInfoWidget::addMismatchingConnectorInfo(MismatchingConnectorWidget *mcw) { int connCount = m_mismatchConnsInfo.size(); ((QVBoxLayout*)m_mismatchersFrame->layout())->insertWidget(connCount,mcw); m_mismatchConnsInfo << mcw; m_allConnsInfo[mcw->connId()] = mcw; connect(mcw,SIGNAL(tellSistersImNewSelected(AbstractConnectorInfoWidget*)),this,SLOT(selectionChanged(AbstractConnectorInfoWidget*))); connect(mcw,SIGNAL(tellViewsMyConnectorIsNewSelected(const QString &)),this,SLOT(informConnectorSelection(const QString &))); if(m_mismatchConnsInfo.size()==1) { m_mismatchersFrameParent->show(); } /*if(!m_selected && connCount == 0) { setSelected(mcw); }*/ foreach(ViewLayer::ViewIdentifier viewId, mcw->views()) { emit setMismatching(viewId, mcw->connId(), true); } } QVBoxLayout *ConnectorsInfoWidget::scrollContentLayout() { return (QVBoxLayout*)m_scrollContent->layout(); } void ConnectorsInfoWidget::connectorsFound(QList< QPointer > conns) { qSort(conns); QProgressDialog progress(tr("Loading connectors..."), 0, 0, conns.size(), this); progress.show(); for(int i=0; i < conns.size(); i++) { progress.setValue(i); ProcessEventBlocker::processEvents(); // to keep the app away from freezing addConnectorInfo(conns[i]); } progress.setValue(conns.size()); updateLayout(); } void ConnectorsInfoWidget::informConnectorSelection(const QString &connId) { emit connectorSelected(connId); } void ConnectorsInfoWidget::informEditionCompleted() { emit editionCompleted(); } const QList< QPointer > ConnectorsInfoWidget::connectorsShared() { QList< QPointer > connectorsShared; for(int i=0; iid(); Connector *conn = sci->connector(); //foreach (ConnectorItem * connectorItem, conn->viewItems()) { //connectorItem->debugInfo("what is this connector"); //} ConnectorShared* cs = conn->connectorShared(); //foreach (SvgIdLayer * sil, cs->pins()) { //DebugDialog::debug(QString("what is this pin %1").arg(sil->m_svgViewLayerID)); //} cs->setId(id); cs->setSharedName(sci->name()); cs->setDescription(sci->description()); cs->setConnectorType(sci->type()); connectorsShared << cs; } return connectorsShared; } void ConnectorsInfoWidget::removeTerminalPoint(const QString &connId, ViewLayer::ViewIdentifier vid) { for(int i=0; iconnector(); foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(vid)) { if(conn->connectorSharedID() == connId) { sil->m_terminalId = ___emptyString___; } } } } // If we're reloading an image, clear mismatching connectors related exclusively to that view void ConnectorsInfoWidget::clearMismatchingForView(ViewLayer::ViewIdentifier viewId) { foreach(MismatchingConnectorWidget* mcw, m_mismatchConnsInfo) { if(mcw->views().size()==1 && mcw->views()[0] == viewId) { removeMismatchingConnectorInfo(mcw, false); } } } // Updates previous connector to mismatching if they are not in the list void ConnectorsInfoWidget::singleToMismatchingNotInView(ViewLayer::ViewIdentifier viewId, const QStringList &connIds) { foreach(SingleConnectorInfoWidget* sci, m_connsInfo) { if(connIds.indexOf(sci->id()) == -1) { MismatchingConnectorWidget *mcw = sci->toMismatching(viewId); connect( mcw, SIGNAL(completeConn(MismatchingConnectorWidget*)), this, SLOT(completeConn(MismatchingConnectorWidget*)) ); removeConnectorInfo(sci,false); addMismatchingConnectorInfo(mcw); } } m_mismatchersFrame->adjustSize(); scrollContentLayout()->update(); m_mismatchersFrameParent->adjustSize(); m_mismatchersFrameParent->layout()->update(); updateLayout(); } void ConnectorsInfoWidget::syncNewConnectors(ViewLayer::ViewIdentifier viewId, const QList< QPointer > &conns) { clearMismatchingForView(viewId); // clean the old pins for this view /*foreach(QString oldId, m_connectorsPins.keys()) { m_connectorsPins[oldId].remove(viewId); }*/ QStringList connIds; foreach(Connector *conn, conns) { QString connId = conn->connectorSharedID(); connIds << connId; if(existingConnId(connId)) { SingleConnectorInfoWidget * sci = findSCI(connId); resetType(viewId, sci, conn); resetName(viewId, sci, conn); emit existingConnectorSignal(viewId, connId, sci ? sci->connector() : NULL, conn); } else { MismatchingConnectorWidget *mcw = NULL; if(( mcw = existingMismatchingConnector(connId) )) { if(mcw->onlyMissingThisView(viewId)) { Connector * prevConn = mcw->prevConn(); removeMismatchingConnectorInfo(mcw, false); // mcw was just deleted in removeMismatchingConnectorInfo so we can't use it directly if(prevConn) { addConnectorInfo(prevConn); } else { addConnectorInfo(connId); } SingleConnectorInfoWidget * sci = findSCI(connId); resetType(viewId, sci, conn); resetName(viewId, sci, conn); emit existingConnectorSignal(viewId, connId, sci ? sci->connector() : NULL, conn); } else { mcw->addViewPresence(viewId); emit setMismatching(viewId, mcw->connId(), true); } } else { addMismatchingConnectorInfo(viewId, connId); } } } //clearMismatchingForView(viewId); singleToMismatchingNotInView(viewId,connIds); } bool ConnectorsInfoWidget::existingConnId(const QString &id) { return findConnector(id) != NULL; } MismatchingConnectorWidget* ConnectorsInfoWidget::existingMismatchingConnector(const QString &id) { foreach(MismatchingConnectorWidget *mci, m_mismatchConnsInfo) { if(mci->connId() == id) { return mci; } } return NULL; } void ConnectorsInfoWidget::removeMismatchingConnectorInfo(MismatchingConnectorWidget* mcw, bool singleShot, bool alsoDeleteFromView) { m_mismatchersFrame->layout()->removeWidget(mcw); m_mismatchConnsInfo.removeOne(mcw); m_allConnsInfo.remove(mcw->connId()); if(m_mismatchConnsInfo.size()==0) { m_mismatchersFrameParent->hide(); updateLayout(); } if(alsoDeleteFromView) { emit removeConnectorFrom(mcw->connId(),ViewLayer::AllViews); } else { foreach(ViewLayer::ViewIdentifier viewId, mcw->views()) { emit setMismatching(viewId, mcw->connId(), false); } } if(m_selected == mcw) { m_selected = NULL; } m_objToDelete = mcw; if(singleShot) { QTimer::singleShot(100, this, SLOT(deleteAux())); } else { deleteAux(); } } void ConnectorsInfoWidget::removeConnectorInfo(SingleConnectorInfoWidget *sci, bool singleShot, bool alsoDeleteFromView) { m_connRemoved = true; scrollContentLayout()->removeWidget(sci); m_connsInfo.removeOne(sci); m_allConnsInfo.remove(sci->id()); if(m_selected == sci) { m_selected = NULL; } if(alsoDeleteFromView) { emit removeConnectorFrom(sci->id(), ViewLayer::AllViews); } m_objToDelete = sci; if(singleShot) { QTimer::singleShot(100, this, SLOT(deleteAux())); } else { deleteAux(); } } Connector* ConnectorsInfoWidget::findConnector(const QString &id) { foreach(SingleConnectorInfoWidget *sci, m_connsInfo) { if(sci->id() == id) { return sci->connector(); } } return NULL; } SingleConnectorInfoWidget* ConnectorsInfoWidget::findSCI(const QString &id) { foreach(SingleConnectorInfoWidget *sci, m_connsInfo) { if(sci->id() == id) { return sci; } } return NULL; } void ConnectorsInfoWidget::addConnector() { m_connAdded = true; if (m_views->breadboardView()->myItem() == NULL || m_views->schematicView()->myItem() == NULL || m_views->pcbView()->myItem() == NULL) { QMessageBox::warning( parentWidget(), tr("Couldn't add connector"), tr("Please, first load an image in each view,\nbefore adding any connectors") ); return; } QString connId = QString("connector%1").arg(nextConnId()); emit drawConnector(addConnectorInfo(connId)); if(m_allConnsInfo[connId]) { setSelected(m_allConnsInfo[connId]); } } void ConnectorsInfoWidget::removeSelectedConnector() { if(!m_selected) return; removeConnector(m_selected); } void ConnectorsInfoWidget::removeConnector(AbstractConnectorInfoWidget* connInfo) { MismatchingConnectorWidget* mismatch = dynamic_cast(connInfo); if(mismatch) { removeMismatchingConnectorInfo(mismatch, true, true); } else { SingleConnectorInfoWidget *single = dynamic_cast(connInfo); if(single) { removeConnectorInfo(single, true, true); } } } int ConnectorsInfoWidget::nextConnId() { int max = 0; foreach(QString connId, m_connIds) { QString currId = connId; if(currId.startsWith("connector")) { QString helpStr = currId.remove("connector"); if(!helpStr.isEmpty()) { bool isInt; int helpInt = helpStr.toInt(&isInt); if(isInt && max <= helpInt) { max = ++helpInt; } } } } return max; } void ConnectorsInfoWidget::deleteAux() { if(m_objToDelete) { if(m_selected == m_objToDelete) { m_selected = NULL; } delete m_objToDelete; m_objToDelete = NULL; } } int ConnectorsInfoWidget::scrollBarWidth() { return m_scrollArea->verticalScrollBar()->width(); } void ConnectorsInfoWidget::connectorSelectedInView(const QString &connId) { setSelected(m_allConnsInfo[connId]); } void ConnectorsInfoWidget::setViews(PartsEditorViewsWidget* connsView) { m_views = connsView; } void ConnectorsInfoWidget::completeConn(MismatchingConnectorWidget* mcw) { if(m_views->imagesLoadedInAllViews()) { QList missingViews = mcw->missingViews(); QList availViews = mcw->views(); QString connId = mcw->connId(); removeMismatchingConnectorInfo(mcw); if(mcw->prevConn()) { addConnectorInfo(mcw->prevConn()); } else { addConnectorInfo(connId); } Connector *connector = findConnector(connId); foreach(ViewLayer::ViewIdentifier viewId, missingViews) { emit drawConnector(viewId, connector); } foreach(ViewLayer::ViewIdentifier viewId, availViews) { emit setMismatching(viewId,connId,false); } } else { QMessageBox::information( parentWidget(), tr("Couldn't fix connector"), tr("Please, first load an image in each view,\nin order to fix this connector") ); } } bool ConnectorsInfoWidget::connectorsRemoved() { return m_connRemoved; } bool ConnectorsInfoWidget::connectorAdded() { return m_connAdded; } bool ConnectorsInfoWidget::connectorsCountChanged() { return connectorsRemoved() || connectorAdded(); } bool ConnectorsInfoWidget::hasMismatchingConnectors() { return m_mismatchConnsInfo.size() > 0; } void ConnectorsInfoWidget::resetType(ViewLayer::ViewIdentifier viewId, SingleConnectorInfoWidget * sci, Connector * conn) { if (viewId != ViewLayer::PCBView) return; if (conn->connectorType() == sci->connectorType()) return; if (conn->connectorType() == Connector::Pad) { // was male or female sci->setConnectorType(conn->connectorType()); return; } if (sci->connectorType() != Connector::Pad) return; // was pad, should it be male or female? // TODO: figure out which gender or ask the user to fix it. sci->setConnectorType(Connector::Male); } void ConnectorsInfoWidget::resetName(ViewLayer::ViewIdentifier viewId, SingleConnectorInfoWidget * sci, Connector * conn) { Q_UNUSED(viewId); if (!sci->name().startsWith("pin", Qt::CaseInsensitive)) return; // probably already changed by the user QString name = conn->connectorSharedName(); if (name.isEmpty()) return; if (name.startsWith("pin", Qt::CaseInsensitive)) return; bool ok; int num = name.toInt(&ok); if (ok) return; // just a number, don't bother Q_UNUSED(num); sci->setName(name); } fritzing-app-0.9.3b/src/partseditor/obsolete/connectorsinfowidget.h000066400000000000000000000120161272363601300256530ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef CONNECTORSINFOWIDGET_H_ #define CONNECTORSINFOWIDGET_H_ #include #include #include #include #include "singleconnectorinfowidget.h" #include "mismatchingconnectorwidget.h" class PartsEditorViewsWidget; class ConnectorsInfoWidget : public QFrame { Q_OBJECT public: ConnectorsInfoWidget(WaitPushUndoStack *undoStack, QWidget *parent=0); const QList< QPointer > connectorsShared(); QCheckBox *showTerminalPointsCheckBox(); int scrollBarWidth(); void setViews(PartsEditorViewsWidget* connsView); bool connectorsCountChanged(); bool connectorsRemoved(); bool connectorAdded(); bool hasMismatchingConnectors(); public slots: void connectorsFound(QList< QPointer >); void informConnectorSelection(const QString &); void informEditionCompleted(); void syncNewConnectors(ViewLayer::ViewIdentifier viewId, const QList< QPointer > &conns); void emitPaintNeeded(); void addConnector(); void removeSelectedConnector(); void removeConnector(AbstractConnectorInfoWidget* connInfo); void removeTerminalPoint(const QString &connId, ViewLayer::ViewIdentifier vid); signals: void connectorSelected(const QString &); void editionCompleted(); void existingConnectorSignal(ViewLayer::ViewIdentifier viewId, const QString &id, Connector* existingConnector, Connector * newConnector); void setMismatching(ViewLayer::ViewIdentifier viewId, const QString &connId, bool mismatching); void repaintNeeded(); void showTerminalPoints(bool show); void drawConnector(Connector*); void drawConnector(ViewLayer::ViewIdentifier, Connector*); void removeConnectorFrom(const QString &connId, ViewLayer::ViewIdentifier view); protected slots: void updateLayout(); void selectionChanged(AbstractConnectorInfoWidget* selected); void deleteAux(); void connectorSelectedInView(const QString &connId); void completeConn(MismatchingConnectorWidget* mcw); protected: void createScrollArea(); void createToolsArea(); void addConnectorInfo(MismatchingConnectorWidget* mcw); Connector* addConnectorInfo(QString id); void addConnectorInfo(Connector *conn); void addMismatchingConnectorInfo(MismatchingConnectorWidget *mcw); void addMismatchingConnectorInfo(ViewLayer::ViewIdentifier viewID, QString connId); QVBoxLayout *scrollContentLayout(); bool eventFilter(QObject *obj, QEvent *event); void setSelected(AbstractConnectorInfoWidget * newSelected); void selectNext(); void selectPrev(); void clearMismatchingForView(ViewLayer::ViewIdentifier viewId); void singleToMismatchingNotInView(ViewLayer::ViewIdentifier viewId, const QStringList &connIds); bool existingConnId(const QString &id); MismatchingConnectorWidget* existingMismatchingConnector(const QString &id); void removeMismatchingConnectorInfo(MismatchingConnectorWidget* mcw, bool singleShot = true, bool alsoDeleteFromView = false); void removeConnectorInfo(SingleConnectorInfoWidget *sci, bool singleShot = true, bool alsoDeleteFromView = false); Connector* findConnector(const QString &id); SingleConnectorInfoWidget * findSCI(const QString &id); int nextConnId(); void resetType(ViewLayer::ViewIdentifier viewId, SingleConnectorInfoWidget * sci, Connector * conn); void resetName(ViewLayer::ViewIdentifier viewId, SingleConnectorInfoWidget * sci, Connector * conn); protected: //QHash > m_connectorsPins; QLabel *m_title; QScrollArea *m_scrollArea; QFrame *m_scrollContent; QFrame *m_mismatchersFrame; QFrame *m_mismatchersFrameParent; QFrame *m_toolsContainter; PartsEditorViewsWidget* m_views; AbstractConnectorInfoWidget *m_selected; QList m_connsInfo; QList< QPointer > m_mismatchConnsInfo; QHash m_allConnsInfo; QStringList m_connIds; WaitPushUndoStack *m_undoStack; QObject *m_objToDelete; bool m_connRemoved; bool m_connAdded; }; #endif /* CONNECTORSINFOWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/cornerhandler.cpp000066400000000000000000000127671272363601300246140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include "cornerhandler.h" #include "connectorrectangle.h" #include "../debugdialog.h" QHash CornerHandler::pixmapHash; double CornerHandler::Size = 4; CornerHandler::CornerHandler(ConnectorRectangle *parent, QGraphicsItem* parentItem, Qt::Corner corner) : QGraphicsRectItem(parentItem) { m_parent = parent; m_corner = corner; m_resizing = false; initPixmapHash(); setFlag(QGraphicsItem::ItemIgnoresTransformations); setFlag(QGraphicsItem::ItemClipsChildrenToShape); setAcceptHoverEvents(true); doSetVisible(false); } void CornerHandler::doSetVisible(bool visible) { if(visible) { setBrush(QBrush(QColor::fromRgb(0,250,0))); } else { setBrush(QBrush()); } m_isVisible = visible; } void CornerHandler::initPixmapHash() { if(pixmapHash.isEmpty()) { pixmapHash[Qt::TopLeftCorner] = QPixmap(":/resources/images/itemselection/cornerHandlerActiveTopLeft.png"); pixmapHash[Qt::TopRightCorner] = QPixmap(":/resources/images/itemselection/cornerHandlerActiveTopRight.png"); pixmapHash[Qt::BottomRightCorner] = QPixmap(":/resources/images/itemselection/cornerHandlerActiveBottomRight.png"); pixmapHash[Qt::BottomLeftCorner] = QPixmap(":/resources/images/itemselection/cornerHandlerActiveBottomLeft.png"); } } void CornerHandler::resize(const QPointF &mousePos) { QRectF rect = m_parent->owner()->boundingRect(); double oldX1 = rect.x(); double oldY1 = rect.y(); double oldX2 = oldX1+rect.width(); double oldY2 = oldY1+rect.height(); double newX = mapToItem(m_parent->owner(),mousePos).x(); double newY = mapToItem(m_parent->owner(),mousePos).y(); switch(m_corner) { case Qt::TopLeftCorner: m_parent->resizeRect(newX,newY,oldX2,oldY2); break; case Qt::TopRightCorner: m_parent->resizeRect(oldX1,newY,newX,oldY2); break; case Qt::BottomRightCorner: m_parent->resizeRect(oldX1,oldY1,newX,newY); break; case Qt::BottomLeftCorner: m_parent->resizeRect(newX,oldY1,oldX2,newY); break; default: break; } } Qt::Corner CornerHandler::corner() { return m_corner; } void CornerHandler::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if(m_parent->isResizable() && m_resizing) { resize(event->pos()); scene()->update(); } else { QGraphicsRectItem::mouseMoveEvent(event); } } void CornerHandler::mousePressEvent(QGraphicsSceneMouseEvent *event) { if(m_isVisible) { if(m_parent->isResizable()) { m_parent->resizingStarted(); m_resizing = true; setFlag(QGraphicsItem::ItemIgnoresTransformations,false); } else { QGraphicsRectItem::mousePressEvent(event); } } else { QGraphicsRectItem::mousePressEvent(event); } } void CornerHandler::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if(m_parent->isResizable() && m_resizing) { setFlag(QGraphicsItem::ItemIgnoresTransformations,true); m_resizing = false; m_parent->resizingFinished(); } QGraphicsRectItem::mouseReleaseEvent(event); } void CornerHandler::hoverEnterEvent(QGraphicsSceneHoverEvent * event) { QGraphicsRectItem::hoverEnterEvent(event); if(m_isVisible && m_parent->isResizable()) { setCursor(QCursor(cursorForCorner(m_corner))); } } void CornerHandler::hoverLeaveEvent(QGraphicsSceneHoverEvent * event) { if(m_isVisible && m_parent->isResizable()) { unsetCursor(); } QGraphicsRectItem::hoverLeaveEvent(event); } Qt::CursorShape CornerHandler::cursorForCorner(Qt::Corner corner) { Qt::CursorShape cursorShape = Qt::SizeFDiagCursor; switch(corner) { case Qt::TopLeftCorner: case Qt::BottomRightCorner: cursorShape = Qt::SizeFDiagCursor; break; case Qt::TopRightCorner: case Qt::BottomLeftCorner: cursorShape = Qt::SizeBDiagCursor; break; default: throw "CornerHandler::cursorForCorner unknown corner"; } return cursorShape; } bool CornerHandler::isBeingDragged() { return m_resizing; } void CornerHandler::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if(m_isVisible && m_parent->isResizable()) { doPaint(painter); } } void CornerHandler::doPaint(QPainter *painter) { if(m_isVisible) { QRectF trgRect = m_parent->handlerRect(m_corner); //DebugDialog::debug("handler rect ", trgRect); QPixmap pm = pixmapHash[m_corner]; QRectF srcRect = QRectF(pm.rect()); //painter->drawRect(rect); painter->drawPixmap(trgRect,pm,srcRect); } } void CornerHandler::doSetRect(const QRectF &newRect) { setFlag(QGraphicsItem::ItemIgnoresTransformations,false); setRect(mapFromParent(newRect).boundingRect()); } fritzing-app-0.9.3b/src/partseditor/obsolete/cornerhandler.h000066400000000000000000000041321272363601300242440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef CORNERHANDLER_H_ #define CORNERHANDLER_H_ #include #include class ConnectorRectangle; class CornerHandler : public QGraphicsRectItem { public: CornerHandler(ConnectorRectangle *parent, QGraphicsItem* parentItem, Qt::Corner corner); void resize(const QPointF &mousePos); Qt::Corner corner(); bool isBeingDragged(); void doSetVisible(bool visible); void doSetRect(const QRectF &newRect); void doPaint(QPainter *painter); protected: void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent * event); void hoverLeaveEvent(QGraphicsSceneHoverEvent * event); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); Qt::CursorShape cursorForCorner(Qt::Corner); void initPixmapHash(); ConnectorRectangle *m_parent; Qt::Corner m_corner; bool m_isVisible; volatile bool m_resizing; public: static QHash pixmapHash; static double Size; }; #endif /* CORNERHANDLER_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/editabledatewidget.cpp000066400000000000000000000033071272363601300255670ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "editabledatewidget.h" #include "../utils/misc.h" EditableDateWidget::EditableDateWidget(QDate date, WaitPushUndoStack *undoStack, QWidget *parent, QString title, bool edited, bool noSpacing) : AbstractEditableLabelWidget(date.toString(Qt::ISODate), undoStack, parent, title, edited, noSpacing) { m_dateEdit = new QDateEdit(this); toStandardMode(); } QString EditableDateWidget::editionText() { return m_dateEdit->date().toString(Qt::ISODate); } void EditableDateWidget::setEditionText(QString text) { m_dateEdit->setDate(QDate::fromString(text, Qt::ISODate)); } QWidget* EditableDateWidget::myEditionWidget() { return m_dateEdit; } void EditableDateWidget::setEmptyTextToEdit() { m_dateEdit->setDate(QDate()); } fritzing-app-0.9.3b/src/partseditor/obsolete/editabledatewidget.h000066400000000000000000000030201272363601300252240ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef EDITABLEDATEWIDGET_H_ #define EDITABLEDATEWIDGET_H_ #include "abstracteditablelabelwidget.h" #include class EditableDateWidget : public AbstractEditableLabelWidget { Q_OBJECT public: EditableDateWidget(QDate date, WaitPushUndoStack *undoStack, QWidget *parent=0, QString title="", bool noSpacing=false, bool edited=false); protected: QString editionText(); void setEditionText(QString text); QWidget* myEditionWidget(); void setEmptyTextToEdit(); QDateEdit *m_dateEdit; }; #endif /* EDITABLEDATEWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/editablelabel.cpp000066400000000000000000000026711272363601300245300ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "editablelabel.h" #include "../utils/misc.h" EditableLabel::EditableLabel(QWidget *parent) : QLabel(parent) { setWordWrap(true); } EditableLabel::EditableLabel(const QString &text, QWidget *parent) : QLabel(text,parent) { setWordWrap(true); } void EditableLabel::editionCompleted(QString newText) { setText(newText); } void EditableLabel::mousePressEvent (QMouseEvent *event) { emit editionStarted(text()); QWidget::mousePressEvent(event); } fritzing-app-0.9.3b/src/partseditor/obsolete/editablelabel.h000066400000000000000000000026351272363601300241750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef EDITABLELABEL_H_ #define EDITABLELABEL_H_ #include #include class EditableLabel : public QLabel { Q_OBJECT public: EditableLabel(QWidget *parent=0); EditableLabel(const QString & text, QWidget *parent=0); public slots: void editionCompleted(QString newText); signals: void editionStarted(QString); protected: void mousePressEvent(QMouseEvent *event); }; #endif /* EDITABLELABEL_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/editablelinewidget.cpp000066400000000000000000000035101272363601300255750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "editablelinewidget.h" #include "../utils/misc.h" EditableLineWidget::EditableLineWidget(QString text, WaitPushUndoStack *undoStack, QWidget *parent, QString title, bool edited, bool noSpacing) : AbstractEditableLabelWidget(text, undoStack, parent, title, edited, noSpacing) { m_lineEdit = new QLineEdit(this); toStandardMode(); } void EditableLineWidget::setValidator(const QValidator * v ) { m_lineEdit->setValidator(v); } void EditableLineWidget::setText(const QString &text) { m_lineEdit->setText(text); m_label->setText(text); } QString EditableLineWidget::editionText() { return m_lineEdit->text(); } void EditableLineWidget::setEditionText(QString text) { m_lineEdit->setText(text); } QWidget* EditableLineWidget::myEditionWidget() { return m_lineEdit; } void EditableLineWidget::setEmptyTextToEdit() { m_lineEdit->setText(""); } fritzing-app-0.9.3b/src/partseditor/obsolete/editablelinewidget.h000066400000000000000000000031431272363601300252440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef EDITABLELINEWIDGET_H_ #define EDITABLELINEWIDGET_H_ #include #include "abstracteditablelabelwidget.h" class EditableLineWidget : public AbstractEditableLabelWidget { Q_OBJECT public: EditableLineWidget(QString text, WaitPushUndoStack *undoStack, QWidget *parent=0, QString title="", bool edited=false, bool noSpacing=false); void setValidator(const QValidator * v ); void setText(const QString &text); protected: QString editionText(); void setEditionText(QString text); QWidget* myEditionWidget(); void setEmptyTextToEdit(); QLineEdit *m_lineEdit; }; #endif /* EDITABLELINEWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/editabletextwidget.cpp000066400000000000000000000032401272363601300256320ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "editabletextwidget.h" #include "../utils/misc.h" EditableTextWidget::EditableTextWidget(QString text, WaitPushUndoStack *undoStack, QWidget *parent, QString title, bool edited, bool noSpacing) : AbstractEditableLabelWidget(text, undoStack, parent, title, edited, noSpacing) { m_textEdit = new QTextEdit(this); m_textEdit->setFixedHeight(70); toStandardMode(); } QString EditableTextWidget::editionText() { return m_textEdit->toPlainText(); } void EditableTextWidget::setEditionText(QString text) { m_textEdit->setText(text); } QWidget* EditableTextWidget::myEditionWidget() { return m_textEdit; } void EditableTextWidget::setEmptyTextToEdit() { m_textEdit->setText(""); } fritzing-app-0.9.3b/src/partseditor/obsolete/editabletextwidget.h000066400000000000000000000030221272363601300252750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef EDITABLETEXTWIDGET_H_ #define EDITABLETEXTWIDGET_H_ #include #include "abstracteditablelabelwidget.h" class EditableTextWidget : public AbstractEditableLabelWidget { Q_OBJECT public: EditableTextWidget(QString text, WaitPushUndoStack *undoStack, QWidget *parent=0, QString title="", bool noSpacing=false, bool edited=false); protected: QString editionText(); void setEditionText(QString text); QWidget* myEditionWidget(); void setEmptyTextToEdit(); QTextEdit *m_textEdit; }; #endif /* EDITABLETEXTWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/fixfontsdialog.cpp000066400000000000000000000136501272363601300247760ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include "fixfontsdialog.h" #include "../installedfonts.h" #include "../debugdialog.h" ////////////////////////////////////////////////////////////////////////////// class FixedFontComboBox : public QComboBox { public: FixedFontComboBox(QWidget *parent, const QString &brokenFont) : QComboBox(parent) { m_brokenFont = brokenFont; } const QString &brokenFont() { return m_brokenFont; } void setBrokenFont(const QString &brokenFont) { m_brokenFont = brokenFont; } protected: QString m_brokenFont; }; ////////////////////////////////////////////////////////////////////////////// FixFontsDialog::FixFontsDialog(QWidget *parent, const QSet fontsTofix) : QDialog(parent) { setWindowTitle(tr("Unavailable fonts")); QFrame *container = new QFrame(this); container->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Expanding); QVBoxLayout *layout = new QVBoxLayout(container); layout->setSpacing(1); layout->setMargin(1); foreach (QString f, fontsTofix) { DebugDialog::debug("incoming font " + f); } m_fontsToFix = fontsTofix - InstalledFonts::InstalledFontsList; foreach (QString fontFileName, InstalledFonts::InstalledFontsNameMapper.values()) { // SVG uses filename which may not match family name (e.g. "DroidSans" and "Droid Sans") m_fontsToFix.remove(fontFileName); } QStringList availFonts = InstalledFonts::InstalledFontsList.toList(); qSort(availFonts); availFonts.insert(0,tr("-- ignore --")); int defaultIdx = availFonts.indexOf("Droid Sans"); QStringList fontsToFixList = m_fontsToFix.toList(); qSort(fontsToFixList); foreach(QString ftf, fontsToFixList) { DebugDialog::debug("font not found: "+ftf); createLine(layout,ftf,availFonts,defaultIdx); } layout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding)); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QVBoxLayout *mainLO = new QVBoxLayout(this); mainLO->setMargin(8); mainLO->setSpacing(2); QLabel *msgLabel = new QLabel(tr( "One or more fonts used in this SVG file are not available in Fritzing.\n" "Please select one of the Fritzing fonts to replace them:" ),this); msgLabel->setWordWrap(true); mainLO->addWidget(msgLabel); mainLO->addWidget(container); mainLO->addSpacerItem(new QSpacerItem(0,5)); QLabel *infoLabel = new QLabel( "For more information, please refer to the " "part creation guidelines." , this); infoLabel->setOpenExternalLinks(true); infoLabel->setWordWrap(true); mainLO->addWidget(infoLabel); mainLO->addSpacerItem(new QSpacerItem(0,5)); QHBoxLayout *btnLayout = new QHBoxLayout(); btnLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Minimum)); btnLayout->addWidget(buttonBox); mainLO->addLayout(btnLayout); setMinimumWidth(100); } FixFontsDialog::~FixFontsDialog() { } // assumes that the first item in items, is a default value void FixFontsDialog::createLine(QLayout* layout, const QString &brokenFont, const QStringList &items, int defaultIdx) { FixedFontComboBox *cb = new FixedFontComboBox(this,brokenFont); int fontValue = -1; foreach(QString font, items) { cb->addItem(font, fontValue); fontValue++; } cb->setCurrentIndex(defaultIdx); m_fixedFonts << cb; QFrame *line = new QFrame(this); line->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); QHBoxLayout *lineLO = new QHBoxLayout(line); lineLO->setSpacing(0); lineLO->setMargin(1); lineLO->addWidget(new QLabel(tr("Replace "),line)); lineLO->addWidget(new QLabel(QString("%1 ").arg(brokenFont),line)); lineLO->addWidget(new QLabel(tr("with "),line)); lineLO->addWidget(cb); layout->addWidget(line); } QSet FixFontsDialog::fontsToFix() { return m_fontsToFix; } FixedFontsHash FixFontsDialog::getFixedFontsHash() { FixedFontsHash retval; foreach(FixedFontComboBox* cb, m_fixedFonts) { int idx = cb->currentIndex(); if( idx != -1) { int value = cb->itemData(idx).toInt(); if(value > -1) { QString fixedFont = cb->itemText(idx); // at least for Droid Sans, family name is "Droid Sans" but SVGs seem to need filename ("DroidSans"). fixedFont = InstalledFonts::InstalledFontsNameMapper.value(fixedFont); retval[cb->brokenFont()] = fixedFont; } } } return retval; } FixedFontsHash FixFontsDialog::fixFonts(QWidget *parent, const QSet fontsTofix, bool &cancelPressed) { FixedFontsHash retval; cancelPressed = false; FixFontsDialog *dlg = new FixFontsDialog(parent,fontsTofix); if(dlg->fontsToFix().size() > 0) { if(dlg->exec() == QDialog::Accepted) { retval = dlg->getFixedFontsHash(); } else { cancelPressed = true; } } delete dlg; return retval; } fritzing-app-0.9.3b/src/partseditor/obsolete/fixfontsdialog.h000066400000000000000000000032751272363601300244450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FIXFONTSDIALOG_H_ #define FIXFONTSDIALOG_H_ #include #include #define FixedFontsHash QHash class FixFontsDialog : public QDialog { Q_OBJECT public: FixFontsDialog(QWidget *parent, const QSet fontsTofix); virtual ~FixFontsDialog(); FixedFontsHash getFixedFontsHash(); QSet fontsToFix(); static FixedFontsHash fixFonts(QWidget *parent, const QSet fontsTofix, bool &cancelPressed); protected: void createLine(QLayout* layout, const QString &brokenFont, const QStringList &items, int defaultIdx); QSet m_fontsToFix; QList m_fixedFonts; }; #endif /* FIXFONTSDIALOG_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/mismatchingconnectorwidget.cpp000066400000000000000000000114731272363601300274010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include "mismatchingconnectorwidget.h" #include "connectorinforemovebutton.h" #include "../debugdialog.h" QList MismatchingConnectorWidget::AllViews; //TODO Mariano: looks like an abstracteditable, perhaps can be one MismatchingConnectorWidget::MismatchingConnectorWidget(ConnectorsInfoWidget *topLevelContainer, ViewLayer::ViewIdentifier viewId, const QString &connId, QWidget *parent, bool isInView, Connector* conn) : AbstractConnectorInfoWidget(topLevelContainer, parent) { if(AllViews.size() == 0) { AllViews << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView; } m_prevConn = conn; m_connId = connId; m_connIdLabel = new QLabel(m_connId+":", this); m_connIdLabel->setObjectName("mismatchConnId"); m_connMsgLabel = new QLabel(viewsString(),this); m_connMsgLabel->setObjectName("mismatchConnMsg"); QLabel *errorImg = new QLabel(this); errorImg->setPixmap(QPixmap(":/resources/images/error_x_small.png")); if(isInView) { m_missingViews << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView; addViewPresence(viewId); } else { removeViewPresence(viewId); } QPushButton *completeConnBtn = new QPushButton(tr("fix this!"),this); connect(completeConnBtn,SIGNAL(clicked()),this,SLOT(emitCompletion())); QHBoxLayout *lo = new QHBoxLayout(); lo->addWidget(errorImg); lo->addSpacerItem(new QSpacerItem(5,0)); lo->addWidget(m_connIdLabel); lo->addWidget(m_connMsgLabel); lo->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Expanding)); lo->addWidget(completeConnBtn); lo->addSpacerItem(new QSpacerItem(10,0,QSizePolicy::Minimum,QSizePolicy::Expanding)); lo->addWidget(m_removeButton); lo->setMargin(3); lo->setSpacing(3); this->setLayout(lo); //updateGeometry(); setMaximumHeight(SingleConnectorHeight); } MismatchingConnectorWidget::~MismatchingConnectorWidget() { } void MismatchingConnectorWidget::setSelected(bool selected, bool doEmitChange) { AbstractConnectorInfoWidget::setSelected(selected, doEmitChange); if(selected && !m_connId.isNull()) { emit tellViewsMyConnectorIsNewSelected(m_connId); } } bool MismatchingConnectorWidget::onlyMissingThisView(ViewLayer::ViewIdentifier viewId) { return m_missingViews.size() == 1 && m_missingViews[0] == viewId; } void MismatchingConnectorWidget::addViewPresence(ViewLayer::ViewIdentifier viewId) { m_missingViews.removeAll(viewId); m_connMsgLabel->setText(viewsString()); } void MismatchingConnectorWidget::removeViewPresence(ViewLayer::ViewIdentifier viewId) { m_missingViews << viewId; m_connMsgLabel->setText(viewsString()); } bool MismatchingConnectorWidget::presentInAllViews() { return m_missingViews.size() == 0; } const QString &MismatchingConnectorWidget::connId() { return m_connId; } Connector *MismatchingConnectorWidget::prevConn() { return m_prevConn; } QList MismatchingConnectorWidget::views() { QList list = AllViews; for(int i=0; i < m_missingViews.size(); i++) { list.removeAll(m_missingViews[i]); } return list; } QList MismatchingConnectorWidget::missingViews() { return m_missingViews; } QString MismatchingConnectorWidget::viewsString() { QString retval = tr("In "); bool notFirst = false; for(int i=0; i < AllViews.size(); i++) { ViewLayer::ViewIdentifier viewId = AllViews[i]; if(!m_missingViews.contains(viewId)) { if(notFirst) { retval += tr("and "); } retval += ViewLayer::viewIdentifierNaturalName(viewId)+" "; notFirst = true; } } retval += tr("view only"); return retval; } void MismatchingConnectorWidget::mousePressEvent(QMouseEvent * event) { if(!isSelected()) { setSelected(true); } QFrame::mousePressEvent(event); } void MismatchingConnectorWidget::emitCompletion() { emit completeConn(this); } fritzing-app-0.9.3b/src/partseditor/obsolete/mismatchingconnectorwidget.h000066400000000000000000000046161272363601300270470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef MISMATCHINGCONNECTORWIDGET_H_ #define MISMATCHINGCONNECTORWIDGET_H_ #include #include "abstractconnectorinfowidget.h" #include "../connectors/connector.h" class MismatchingConnectorWidget : public AbstractConnectorInfoWidget { Q_OBJECT public: MismatchingConnectorWidget(class ConnectorsInfoWidget *topLevelContainer, ViewLayer::ViewIdentifier viewId, const QString &connId, QWidget *parent, bool isInView = true, Connector* conn = NULL); ~MismatchingConnectorWidget(); void setSelected(bool selected, bool doEmitChange=true); bool onlyMissingThisView(ViewLayer::ViewIdentifier viewId); void addViewPresence(ViewLayer::ViewIdentifier viewId); void removeViewPresence(ViewLayer::ViewIdentifier viewId); const QString &connId(); Connector *prevConn(); QList views(); QList missingViews(); bool presentInAllViews(); signals: void completeConn(MismatchingConnectorWidget*); protected slots: void emitCompletion(); protected: void mousePressEvent(QMouseEvent * event); QString viewsString(); QList m_missingViews; QString m_connId; QPointer m_prevConn; // If this connector info used to be a not mismatching one, we save that info here QLabel *m_connIdLabel; QLabel *m_connMsgLabel; static QList AllViews; }; #endif /* MISMATCHINGCONNECTORWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/partconnectorswidget.cpp000066400000000000000000000025011272363601300262170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include "partconnectorswidget.h" #include "../utils/misc.h" PartConnectorsWidget::PartConnectorsWidget(QWidget* info, QWidget *parent) : QFrame(parent) { QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(info); layout->setMargin(4); layout->setSpacing(10); } fritzing-app-0.9.3b/src/partseditor/obsolete/partconnectorswidget.h000066400000000000000000000023431272363601300256700ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTCONNECTORSWIDGET_H_ #define PARTCONNECTORSWIDGET_H_ #include class PartConnectorsWidget : public QFrame { Q_OBJECT public: PartConnectorsWidget(QWidget* info, QWidget *parent=0); }; #endif /* PARTCONNECTORSWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/partinfowidget.cpp000066400000000000000000000067031272363601300250050ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "partinfowidget.h" #include "../debugdialog.h" PartInfoWidget::PartInfoWidget(QWidget *parent) : QWidget(parent) { m_version = new QLineEdit(this); m_author = new QLineEdit(this); m_title = new QLineEdit(this); //m_taxonomy = new QLineEdit(this); m_label = new QLineEdit(this); m_description = new QTextEdit(this); m_description->setFixedHeight(50); m_date = new QDateEdit(QDate::currentDate(), this); m_date->setDisplayFormat("yyyy-MM-dd"); m_tagsList = new AddRemoveListWidget(tr("Tags"),this); m_propertyList = new AddRemoveListWidget(tr("Properties"),this); int row = 0; QGridLayout *layout = new QGridLayout(); layout->setSpacing(4); layout->setMargin(3); layout->addWidget(new QLabel(tr("Version")), row, 0); layout->addWidget(m_version, row, 1); layout->addWidget(new QLabel(tr("Author")), row, 2); layout->addWidget(m_author, row++, 3); layout->addWidget(new QLabel(tr("Title")), row, 0); layout->addWidget(m_title, row++, 1, 1, 3); layout->addWidget(new QLabel(tr("Label")), row, 0); layout->addWidget(m_label, row, 1); layout->addWidget(new QLabel(tr("Description")), row, 0); layout->addWidget(m_description, row++, 1, 1, 3); /*layout->addWidget(new QLabel(tr("Taxonomy")), row, 0); layout->addWidget(m_taxonomy, row, 1);*/ layout->addWidget(new QLabel(tr("Date")), row, 2); layout->addWidget(m_date, row++, 3); layout->addWidget(m_tagsList, row, 0, 1, 2); layout->addWidget(m_propertyList, row++, 2, 1, 2); setLayout(layout); } ModelPartShared* PartInfoWidget::modelPartShared() { ModelPartShared* mps = new ModelPartShared(); mps->setVersion(m_version->text()); mps->setAuthor(m_author->text()); mps->setTitle(m_title->text()); mps->setDate(m_date->date()); //mps->setTaxonomy(m_taxonomy->text()); mps->setLabel(m_label->text()); mps->setDescription(m_description->toPlainText()); mps->setTags(m_tagsList->getItemsText()); //mps->setProperties(m_propertiesList->getItemsText()); return mps; } void PartInfoWidget::updateInfo(ModelPart * modelPart) { ModelPartShared* mps = modelPart->modelPartShared(); m_version->setText(mps->version()); m_author->setText(mps->author()); m_title->setText(mps->title()); m_date->setDate(mps->date()); //m_taxonomy->setText(mps->taxonomy()); m_label->setText(mps->label()); DebugDialog::debug("setting text: "+mps->description()); m_description->setText(mps->description()); m_tagsList->setItemsText(mps->tags()); //m_propertiesList->setItemsText(mps->properties()); } fritzing-app-0.9.3b/src/partseditor/obsolete/partinfowidget.h000066400000000000000000000032711272363601300244470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTINFOWIDGET_H_ #define PARTINFOWIDGET_H_ #include #include #include #include #include "addremovelistwidget.h" #include "../model/modelpart.h" #include "../model/modelpartshared.h" class PartInfoWidget : public QWidget { Q_OBJECT public: PartInfoWidget(QWidget *parent = 0); ModelPartShared* modelPartShared(); public slots: void updateInfo(ModelPart *); protected: QLineEdit *m_version; QLineEdit *m_author; QLineEdit *m_title; //QLineEdit *m_taxonomy; QLineEdit *m_label; QTextEdit *m_description; QDateEdit *m_date; AddRemoveListWidget * m_tagsList; AddRemoveListWidget * m_propertyList; }; #endif /* PARTINFOWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/partseditormainwindow.cpp000066400000000000000000000652151272363601300264170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6417 $: $Author: cohen@irascible.com $: $Date: 2012-09-14 23:34:09 +0200 (Fr, 14. Sep 2012) $ ********************************************************************/ #include "partseditormainwindow.h" #include "pcbxml.h" #include "../debugdialog.h" #include "../mainwindow/fdockwidget.h" #include "../waitpushundostack.h" #include "editabletextwidget.h" #include "partseditorview.h" #include "partseditorviewswidget.h" #include "editabledatewidget.h" #include "hashpopulatewidget.h" #include "partspecificationswidget.h" #include "partconnectorswidget.h" #include "../model/palettemodel.h" #include "../model/sketchmodel.h" #include "../utils/folderutils.h" #include "../processeventblocker.h" #include #include #include #include #include QString PartsEditorMainWindow::TitleFreshStartText; QString PartsEditorMainWindow::LabelFreshStartText; QString PartsEditorMainWindow::DescriptionFreshStartText; QString PartsEditorMainWindow::TaxonomyFreshStartText; QString PartsEditorMainWindow::TagsFreshStartText; QString PartsEditorMainWindow::FooterText; QString PartsEditorMainWindow::UntitledPartName; QString PartsEditorMainWindow::___partsEditorName___; int PartsEditorMainWindow::UntitledPartIndex = 1; QPointer PartsEditorMainWindow::m_lastOpened = NULL; int PartsEditorMainWindow::m_closedBeforeCount = 0; bool PartsEditorMainWindow::m_closeAfterSaving = true; #ifdef QT_NO_DEBUG #define CORE_EDITION_ENABLED false #else #define CORE_EDITION_ENABLED false #endif PartsEditorMainWindow::PartsEditorMainWindow(QWidget *parent) : FritzingWindow(untitledFileName(), untitledFileCount(), fileExtension(), parent) { m_iconItem = m_breadboardItem = m_schematicItem = m_pcbItem = NULL; m_editingAlien = false; } PartsEditorMainWindow::~PartsEditorMainWindow() { if (m_iconItem) { // m_iconItem was custom-created for the PartsEditor; the other items exist in the sketch so shouldn't be deleted delete m_iconItem; } if (m_sketchModel) { // memory leak here, but delete or deleteLater causes a crash if you're editing an already existing part; a new part seems ok delete m_sketchModel; } if (m_paletteModel) { delete m_paletteModel; } } void PartsEditorMainWindow::initText() { UntitledPartName = tr("Untitled Part"); TitleFreshStartText = tr("Please find a name for me!"); LabelFreshStartText = tr("Please provide a label"); DescriptionFreshStartText = tr("You could tell a little bit about this part"); TaxonomyFreshStartText = tr("Please classify this part"); TagsFreshStartText = tr("You can add your tags to make searching easier"); FooterText = tr("created by %1 on %2"); ___partsEditorName___ = tr("Parts Editor"); } void PartsEditorMainWindow::setup(long id, ModelPart *modelPart, bool fromTemplate, ItemBase * fromItem) { ModelPart * originalModelPart = NULL; QFile styleSheet(":/resources/styles/partseditor.qss"); m_mainFrame = new QFrame(this); m_mainFrame->setObjectName("partsEditor"); m_editingAlien = modelPart? modelPart->isAlien(): false; if (!styleSheet.open(QIODevice::ReadOnly)) { qWarning("Unable to open :/resources/styles/partseditor.qss"); } else { m_mainFrame->setStyleSheet(styleSheet.readAll()); } resize(500,700); m_id = id; m_partUpdated = false; m_savedAsNewPart = false; setAttribute(Qt::WA_DeleteOnClose, true); m_paletteModel = new PaletteModel(false, false); if(modelPart == NULL){ m_lastOpened = this; m_updateEnabled = true; } else { // user only allowed to save parts, once he has saved it as a new one m_updateEnabled = modelPart->isCore()? CORE_EDITION_ENABLED: (modelPart->isContrib() || modelPart->isAlien()? false: true); m_fwFilename = modelPart->path(); setTitle(); UntitledPartIndex--; // TODO Mariano: not good enough } if(!fromTemplate) { m_sketchModel = new SketchModel(true); } else { ModelPart * mp = m_paletteModel->loadPart(m_fwFilename, false); // this seems hacky but maybe it's ok if (mp == NULL || mp->modelPartShared() == NULL) { QMessageBox::critical(this, tr("Parts Editor"), tr("Error! Cannot load part.\n"), QMessageBox::Close); QTimer::singleShot(60, this, SLOT(close())); return; } QHash properties = mp->modelPartShared()->properties(); foreach (QString key, properties.keys()) { QVariant prop = modelPart->localProp(key); if (!prop.isNull() && prop.isValid()) { QString p = prop.toString(); if (!p.isEmpty()) { mp->modelPartShared()->properties()[key] = p; if (key.compare("chip label") == 0) { mp->modelPartShared()->properties()["family"] = p; } } } } originalModelPart = modelPart; modelPart = mp; m_sketchModel = new SketchModel(modelPart); } ModelPart *mp = fromTemplate ? modelPart : NULL; createHeader(mp); createCenter(mp, fromItem); createFooter(); // copy connector local names here if any if (originalModelPart) { foreach (Connector * fromConnector, originalModelPart->connectors()) { foreach (Connector * toConnector, mp->connectors()) { if (toConnector->connectorSharedID().compare(fromConnector->connectorSharedID()) == 0) { toConnector->setConnectorLocalName(fromConnector->connectorLocalName()); } } } } layout()->setMargin(0); layout()->setSpacing(0); QGridLayout *layout = new QGridLayout(m_mainFrame); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(m_headerFrame,0,0); layout->addWidget(m_centerFrame,1,0); layout->addWidget(m_footerFrame,2,0); setCentralWidget(m_mainFrame); if(fromTemplate) { m_views->setViewItems(m_breadboardItem, m_schematicItem, m_pcbItem); m_views->loadViewsImagesFromModel(m_paletteModel, m_sketchModel->root()); } setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); QSettings settings; if (!settings.value("peditor/state").isNull()) { restoreState(settings.value("peditor/state").toByteArray()); } if (!settings.value("peditor/geometry").isNull()) { restoreGeometry(settings.value("peditor/geometry").toByteArray()); } installEventFilter(this); } void PartsEditorMainWindow::createHeader(ModelPart *modelPart) { m_headerFrame = new QFrame(); m_headerFrame->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed)); m_headerFrame->setObjectName("header"); m_headerFrame->setStyleSheet("padding: 2px; padding-bottom: 0;"); int iconViewSize = 50; QGraphicsProxyWidget *startItem = modelPart? NULL: PartsEditorMainWindow::emptyViewItem("icon_icon.png",___emptyString___); m_iconViewImage = new PartsEditorView( ViewLayer::IconView, createTempFolderIfNecessary(), false, startItem, m_headerFrame, iconViewSize ); m_iconViewImage->setViewItem(m_iconItem); m_iconViewImage->setFixedSize(iconViewSize,iconViewSize); m_iconViewImage->setObjectName("iconImage"); m_iconViewImage->setSketchModel(m_sketchModel); m_iconViewImage->setUndoStack(m_undoStack); m_iconViewImage->addViewLayer(new ViewLayer(ViewLayer::Icon, true, 2.5)); m_iconViewImage->setViewLayerIDs(ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon); if(modelPart) { m_iconViewImage->loadFromModel(m_paletteModel, modelPart); } QString linkStyle = "font-size: 10px; color: #7070ff;"; QLabel *button = new QLabel( QString("%1") .arg(tr("Load icon..")) .arg(linkStyle), this); button->setObjectName("iconBrowseButton"); //button->setFixedWidth(iconViewSize); button->setFixedHeight(20); //m_iconViewImage->addFixedToBottomRight(button); connect(button, SIGNAL(linkActivated(const QString&)), m_iconViewImage, SLOT(loadFile())); QString title = modelPart ? modelPart->title() : TitleFreshStartText; m_title = new EditableLineWidget(title,m_undoStack,m_headerFrame,"",modelPart,true); m_title->setObjectName("partTitle"); m_title->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::MinimumExpanding)); QGridLayout *headerLayout = new QGridLayout(); headerLayout->addWidget(m_iconViewImage,0,0); headerLayout->addWidget(m_title,0,1); headerLayout->addWidget(button,1,0,1,2); headerLayout->setVerticalSpacing(0); headerLayout->setMargin(0); m_headerFrame->setLayout(headerLayout); } void PartsEditorMainWindow::createCenter(ModelPart *modelPart, ItemBase * fromItem) { m_moduleId = modelPart ? modelPart->moduleID() : ""; m_version = modelPart ? modelPart->version() : ""; m_uri = modelPart ? modelPart->uri() : ""; m_centerFrame = new QFrame(); m_centerFrame->setObjectName("center"); QList specWidgets; m_connsInfo = new ConnectorsInfoWidget(m_undoStack,this); m_views = new PartsEditorViewsWidget(m_sketchModel, m_undoStack, m_connsInfo, this, fromItem); QString label = modelPart ? modelPart->label() : LabelFreshStartText; m_label = new EditableLineWidget(label,m_undoStack,this,tr("Label"),modelPart); QString description = modelPart ? modelPart->description() : DescriptionFreshStartText; m_description = new EditableTextWidget(description,m_undoStack,this,tr("Description"),modelPart); /*QString taxonomy = modelPart ? modelPart->modelPartShared()->taxonomy() : TAXONOMY_FRESH_START_TEXT; m_taxonomy = new EditableLineWidget(taxonomy,m_undoStack,this,tr("Taxonomy"),modelPart); QRegExp regexp("[\\w+\\.]+\\w$"); m_taxonomy->setValidator(new QRegExpValidator(regexp,this));*/ QStringList readOnlyKeys; readOnlyKeys << "family" << "voltage" << "type"; QHash initValues; if(modelPart) { initValues = modelPart->properties(); } else { initValues["family"] = ""; //initValues["voltage"] = ""; //initValues["type"] = "Through-Hole"; } m_properties = new HashPopulateWidget(tr("Properties"),initValues,readOnlyKeys,false, this); connect(m_properties, SIGNAL(changed()), this, SLOT(propertiesChanged())); QString tags = modelPart ? modelPart->tags().join(", ") : TagsFreshStartText; m_tags = new EditableLineWidget(tags,m_undoStack,this,tr("Tags"),modelPart); m_author = new EditableLineWidget( modelPart ? modelPart->author() : QString(getenvUser()), m_undoStack, this, tr("Author"),true); connect( m_author,SIGNAL(editionCompleted(QString)), this,SLOT(updateDateAndAuthor())); m_createdOn = new EditableDateWidget( modelPart ? modelPart->date() : QDate::currentDate(), m_undoStack,this, tr("Created/Updated on"),true); connect( m_createdOn,SIGNAL(editionCompleted(QString)), this,SLOT(updateDateAndAuthor())); m_createdByText = new QLabel(FooterText.arg(m_author->text()).arg(m_createdOn->text())); m_createdByText->setObjectName("createdBy"); specWidgets << m_label << m_description /*<< m_taxonomy*/ << m_properties << m_tags << m_author << m_createdOn << m_createdByText; m_connsInfo->setViews(m_views); connect(m_connsInfo, SIGNAL(repaintNeeded()), m_views, SLOT(repaint())); connect(m_connsInfo, SIGNAL(drawConnector(Connector*)), m_views, SLOT(drawConnector(Connector*))); connect( m_connsInfo, SIGNAL(drawConnector(ViewLayer::ViewIdentifier, Connector*)), m_views, SLOT(drawConnector(ViewLayer::ViewIdentifier, Connector*)) ); connect( m_connsInfo, SIGNAL(setMismatching(ViewLayer::ViewIdentifier, const QString&, bool)), m_views, SLOT(setMismatching(ViewLayer::ViewIdentifier, const QString&, bool)) ); connect(m_connsInfo, SIGNAL(removeConnectorFrom(const QString&,ViewLayer::ViewIdentifier)), m_views, SLOT(removeConnectorFrom(const QString&,ViewLayer::ViewIdentifier))); connect(m_views, SIGNAL(connectorSelectedInView(const QString&)), m_connsInfo, SLOT(connectorSelectedInView(const QString&))); m_views->showTerminalPointsCheckBox()->setChecked(false); m_views->connectTerminalRemoval(m_connsInfo); connect( m_views, SIGNAL(connectorsFoundSignal(QList< QPointer >)), m_connsInfo, SLOT(connectorsFound(QList< QPointer >)) ); m_tabWidget = new QTabWidget(m_centerFrame); m_tabWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); m_tabWidget->addTab(new PartSpecificationsWidget(specWidgets,this),tr("Specifications")); m_tabWidget->addTab(new PartConnectorsWidget(m_connsInfo,this),tr("Connectors")); QGridLayout *tabLayout = new QGridLayout(m_tabWidget); tabLayout->setMargin(0); tabLayout->setSpacing(0); QSplitter *splitter = new QSplitter(Qt::Vertical,this); splitter->addWidget(m_views); splitter->addWidget(m_tabWidget); QGridLayout *mainLayout = new QGridLayout(m_centerFrame); mainLayout->setMargin(0); mainLayout->setSpacing(0); mainLayout->addWidget(splitter,0,0,1,1); } void PartsEditorMainWindow::connectWidgetsToSave(const QList &widgets) { for(int i=0; i < widgets.size(); i++) { connect(m_saveAsNewPartButton,SIGNAL(clicked()),widgets[i],SLOT(informEditionCompleted())); connect(this,SIGNAL(saveButtonClicked()),widgets[i],SLOT(informEditionCompleted())); } } void PartsEditorMainWindow::createFooter() { m_footerFrame = new QFrame(); m_footerFrame->setObjectName("footer"); m_footerFrame->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); m_saveAsNewPartButton = new QPushButton(tr("save as new part")); m_saveAsNewPartButton->setObjectName("saveAsPartButton"); m_saveAsNewPartButton->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); m_saveButton = new QPushButton(tr("save")); m_saveButton->setObjectName("saveAsButton"); m_saveButton->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); updateSaveButton(); QList editableLabelWidgets; editableLabelWidgets << m_title << m_label << m_description /*<< m_taxonomy*/ << m_tags << m_author << m_createdOn << m_connsInfo ; connectWidgetsToSave(editableLabelWidgets); connect(m_saveAsNewPartButton, SIGNAL(clicked()), this, SLOT(saveAs())); connect(m_saveButton, SIGNAL(clicked()), this, SLOT(save())); m_cancelCloseButton = new QPushButton(tr("cancel")); m_cancelCloseButton->setObjectName("cancelButton"); m_cancelCloseButton->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); connect(m_cancelCloseButton, SIGNAL(clicked()), this, SLOT(close())); QHBoxLayout *footerLayout = new QHBoxLayout; footerLayout->setMargin(0); footerLayout->setSpacing(0); footerLayout->addSpacerItem(new QSpacerItem(40,0,QSizePolicy::Minimum,QSizePolicy::Minimum)); footerLayout->addWidget(m_saveAsNewPartButton); footerLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Minimum)); footerLayout->addWidget(m_saveButton); footerLayout->addSpacerItem(new QSpacerItem(15,0,QSizePolicy::Minimum,QSizePolicy::Minimum)); footerLayout->addWidget(m_cancelCloseButton); footerLayout->addSpacerItem(new QSpacerItem(40,0,QSizePolicy::Minimum,QSizePolicy::Minimum)); m_footerFrame->setLayout(footerLayout); } QGraphicsProxyWidget *PartsEditorMainWindow::emptyViewItem(QString iconFile, QString text) { QLabel *icon = new QLabel(); icon->setPixmap(QPixmap(":/resources/images/"+iconFile)); icon->setAlignment(Qt::AlignHCenter); QLabel *label = NULL; if(!text.isNull() && !text.isEmpty()) { label = new QLabel(text); label->setAlignment(Qt::AlignHCenter); } QFrame *container = new QFrame(); container->setStyleSheet("background-color: transparent;"); QVBoxLayout *mainLo = new QVBoxLayout(container); mainLo->setMargin(0); mainLo->setSpacing(0); QHBoxLayout *lo1 = new QHBoxLayout(); lo1->addWidget(icon); lo1->setMargin(0); lo1->setSpacing(0); mainLo->addLayout(lo1); if(label) { QHBoxLayout *lo2 = new QHBoxLayout(); lo2->addWidget(label); lo2->setMargin(0); lo2->setSpacing(0); mainLo->addLayout(lo2); } QGraphicsProxyWidget *item = new QGraphicsProxyWidget(); item->setWidget(container); return item; } const QDir& PartsEditorMainWindow::createTempFolderIfNecessary() { if(m_tempDir.path() == ".") { QString randext = FolderUtils::getRandText(); m_tempDir = QDir(QDir::tempPath()); bool dirCreation = m_tempDir.mkdir(randext); if (!dirCreation) { throw "PartsEditorMainWindow::createTempFolderIfNecessary creation failure"; } if(dirCreation) { bool cdResult = m_tempDir.cd(randext); if (!cdResult) { throw "PartsEditorMainWindow::createTempFolderIfNecessary cd failure"; } } else { // will use the standard location of the temp folder // instead of a subfolder inside of it } } return m_tempDir; } bool PartsEditorMainWindow::save() { if(validateMinRequirements() && wannaSaveAfterWarning(false)) { bool result = (m_saveButton->isEnabled()) ? FritzingWindow::save() : saveAs(); if(result) { m_cancelCloseButton->setText(tr("close")); if(m_closeAfterSaving) close(); } return result; } else { return false; } } bool PartsEditorMainWindow::saveAs() { if(!validateMinRequirements()) return false; QString fileNameBU = m_fwFilename; QString moduleIdBU = m_moduleId; m_moduleId = ___emptyString___; QString title = m_title->text(); m_fwFilename = !title.isEmpty() ? title+FritzingPartExtension : QFileInfo(m_fwFilename).baseName(); // TODO Mariano: This folder should be defined in the preferences... some day QString partsFolderPath = FolderUtils::getUserDataStorePath("parts")+"/"; QString userPartsFolderPath = partsFolderPath+"user/"; bool firstTime = true; // Perhaps the user wants to use the default file name, confirm first while(m_fwFilename.isEmpty() || QFileInfo(userPartsFolderPath+m_fwFilename).exists() || (FolderUtils::isEmptyFileName(m_fwFilename,untitledFileName()) && firstTime) ) { bool ok; m_fwFilename = QInputDialog::getText( this, tr("Save as new part"), tr("There's already a file with this name.\nPlease, specify a new filename"), QLineEdit::Normal, m_fwFilename, &ok ); firstTime = false; if (!ok) { m_moduleId = moduleIdBU; m_fwFilename = fileNameBU; return false; } } m_fwFilename.replace(QRegExp("[^A-Za-z0-9\\.]"), "_"); Qt::CaseSensitivity cs = Qt::CaseSensitive; #ifdef Q_WS_WIN // seems to be necessary for Windows: getUserDataStorePath() returns a string starting with "c:" // but the file dialog returns a string beginning with "C:" cs = Qt::CaseInsensitive; #endif QString filename = !m_fwFilename.startsWith(userPartsFolderPath, cs) && !m_fwFilename.startsWith(partsFolderPath, cs) ? userPartsFolderPath+m_fwFilename : m_fwFilename; QString guid = "__"+FolderUtils::getRandText()+FritzingPartExtension; if(!alreadyHasExtension(filename, FritzingPartExtension)) { filename += guid; } else { filename.replace(FritzingPartExtension,guid); } makeNonCore(); if (!saveAsAux(filename)) return false; if(wannaSaveAfterWarning(true)) { m_savedAsNewPart = true; m_updateEnabled = true; updateButtons(); if(m_closeAfterSaving) close(); return true; } return false; } void PartsEditorMainWindow::makeNonCore() { QString oldTags = m_tags->text(); QString newTags = oldTags.replace("fritzing core","user part",Qt::CaseInsensitive); m_tags->setText(newTags); } bool PartsEditorMainWindow::saveAsAux(const QString & fileName) { QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("Fritzing"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } file.close(); updateDateAndAuthor(); QApplication::setOverrideCursor(Qt::WaitCursor); m_sketchModel->root()->setModelPartShared(createModelPartShared()); QString fileNameAux = QFileInfo(fileName).fileName(); m_views->copySvgFilesToDestiny(fileNameAux); m_iconViewImage->copySvgFileToDestiny(fileNameAux); m_sketchModel->save(fileName, true); QApplication::restoreOverrideCursor(); statusBar()->showMessage(tr("Saved '%1'").arg(fileName), 2000); m_partUpdated = true; setFileName(fileName); //setCurrentFile(fileName); // mark the stack clean so we update the window dirty flag m_undoStack->setClean(); setTitle(); if(m_editingAlien) { // FIXME: this will keep ALL the external files, not just the ones that this part uses emit alienPartUsed(); m_editingAlien = false; } return true; } void PartsEditorMainWindow::updateDateAndAuthor() { m_createdByText->setText(FooterText.arg(m_author->text()).arg(m_createdOn->text())); } ModelPartShared* PartsEditorMainWindow::createModelPartShared() { ModelPartShared* shared = new ModelPartShared(); if(m_moduleId.isNull() || m_moduleId.isEmpty()) { m_moduleId = FolderUtils::getRandText(); } shared->setModuleID(m_moduleId); shared->setUri(m_uri); shared->setVersion(m_version); shared->setAuthor(m_author->text()); shared->setTitle(m_title->text()); shared->setDate(m_createdOn->text()); shared->setLabel(m_label->text()); //stuff->setTaxonomy(m_taxonomy->text()); shared->setDescription(m_description->text()); QStringList tags = m_tags->text().split(", "); shared->setTags(tags); shared->setProperties(m_properties->hash()); m_iconViewImage->aboutToSave(true); m_views->aboutToSave(); // the deal seems to be that an original modelpart is created and becomes the official sketch model (root). // the ConnectorsShared from that model are used to seed the PartsEditorConnectorsConnectorItems. // When a new image is loaded into a view, the sketch model's ConnectorsShared are replaced with newly constructed ones. // So now the sketch model points to one set of ConnectorsShared, but the original ones are still sitting // back in the PartsEditorConnectorsConnectorItems. So now we replace the new ones with the old ones. // I think. QList< QPointer > connsShared = m_connsInfo->connectorsShared(); m_views->updatePinsInfo(connsShared); shared->setConnectorsShared(m_connsInfo->connectorsShared()); return shared; } void PartsEditorMainWindow::cleanUp() { bool decUntitled = m_fwFilename.startsWith(untitledFileName()); if(decUntitled) { if(m_lastOpened == this) { UntitledPartIndex -= m_closedBeforeCount; UntitledPartIndex--; m_closedBeforeCount = 0; } else { m_closedBeforeCount++; } } if(m_tempDir.path() != ".") { FolderUtils::rmdir(m_tempDir); m_tempDir = QDir(); } } void PartsEditorMainWindow::parentAboutToClose() { if(beforeClosing(false)) { cleanUp(); } } void PartsEditorMainWindow::closeEvent(QCloseEvent *event) { if(beforeClosing()) { cleanUp(); QMainWindow::closeEvent(event); if(m_partUpdated) { emit partUpdated(m_fwFilename, m_id, !m_savedAsNewPart && (m_connsInfo->connectorsCountChanged() || m_views->connectorsPosOrSizeChanged()) ); } emit closed(m_id); } else { event->ignore(); } QSettings settings; settings.setValue("peditor/state",saveState()); settings.setValue("peditor/geometry",saveGeometry()); } const QDir& PartsEditorMainWindow::tempDir() { return createTempFolderIfNecessary(); } const QString PartsEditorMainWindow::untitledFileName() { return UntitledPartName; } int &PartsEditorMainWindow::untitledFileCount() { return UntitledPartIndex; } const QString PartsEditorMainWindow::fileExtension() { return FritzingPartExtension; } const QString PartsEditorMainWindow::defaultSaveFolder() { return QDir::currentPath()+"/parts/user/"; } void PartsEditorMainWindow::updateSaveButton() { if(m_saveButton) m_saveButton->setEnabled(m_updateEnabled); } bool PartsEditorMainWindow::wannaSaveAfterWarning(bool savingAsNew) { bool doEmit = true; bool errorFound = false; QString msg = "Some errors found:\n"; if(!m_savedAsNewPart && !savingAsNew && m_connsInfo->connectorsRemoved()) { errorFound = true; msg += "- The connectors defined in this part have changed.\n" "If you save it, you may break other sketches that already use it.\n"; } if(m_connsInfo->hasMismatchingConnectors()) { errorFound = true; msg += "- Some connectors are not present in all views." "If you save now, they will be removed.\n"; } if(errorFound) { msg += "\nDo you want to save anyway?"; QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Updating existing part"), msg, QMessageBox::Yes|QMessageBox::No ); doEmit = (btn == QMessageBox::Yes); } if(doEmit) emit saveButtonClicked(); return doEmit; } void PartsEditorMainWindow::updateButtons() { m_saveAsNewPartButton->setEnabled(false); m_cancelCloseButton->setText(tr("close")); } bool PartsEditorMainWindow::eventFilter(QObject *object, QEvent *event) { if (object == this && event->type() == QEvent::ShortcutOverride) { QKeyEvent *keyEvent = dynamic_cast(event); if(keyEvent && keyEvent->matches(QKeySequence::Close)) { this->close(); event->ignore(); ProcessEventBlocker::processEvents(); #ifdef Q_WS_MAC FritzingWindow *parent = qobject_cast(parentWidget()); if(parent) { parent->notClosableForAWhile(); } #endif return true; } } return QMainWindow::eventFilter(object, event); } const QString PartsEditorMainWindow::fritzingTitle() { QString fritzing = FritzingWindow::fritzingTitle(); return tr("%1 %2").arg(fritzing).arg(___partsEditorName___); } bool PartsEditorMainWindow::event(QEvent * e) { switch (e->type()) { case QEvent::WindowActivate: emit changeActivationSignal(true, this); break; case QEvent::WindowDeactivate: emit changeActivationSignal(false, this); break; default: break; } return FritzingWindow::event(e); } bool PartsEditorMainWindow::validateMinRequirements() { if(!m_iconViewImage->isEmpty()) { return true; } else { QMessageBox::information(this, tr("Icon needed"), tr("Please, provide an icon image for this part")); return false; } } void PartsEditorMainWindow::setViewItems(ItemBase * iiItem, ItemBase* bbItem, ItemBase* schemItem, ItemBase* pcbItem) { m_iconItem = iiItem; m_breadboardItem = bbItem; m_schematicItem = schemItem; m_pcbItem = pcbItem; } QString PartsEditorMainWindow::getExtensionString() { return tr("Fritzing Parts (*%1)").arg(fileExtension()); } QStringList PartsEditorMainWindow::getExtensions() { QStringList extensions; extensions.append(fileExtension()); return extensions; } void PartsEditorMainWindow::propertiesChanged() { if (m_undoStack) { m_undoStack->push(new QUndoCommand("dummy parts editor command")); } } fritzing-app-0.9.3b/src/partseditor/obsolete/partseditormainwindow.h000066400000000000000000000115411272363601300260550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSEDITORMAINWINDOW_H_ #define PARTSEDITORMAINWINDOW_H_ #include #include #include #include #include "../mainwindow/fritzingwindow.h" #include "../model/modelpartshared.h" #include "connectorsinfowidget.h" #include "editablelinewidget.h" QT_FORWARD_DECLARE_CLASS(QGraphicsScene) QT_FORWARD_DECLARE_CLASS(QGraphicsView) QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QSplitter) class PartsEditorMainWindow : public FritzingWindow { Q_OBJECT public: PartsEditorMainWindow(QWidget *parent=0); ~PartsEditorMainWindow(); void setup(long id, ModelPart *modelPart, bool fromTemplate, class ItemBase * fromItem); void setViewItems(class ItemBase *, class ItemBase *, class ItemBase *, class ItemBase *); static const QString templatePath; const QDir& tempDir(); bool validateMinRequirements(); bool save(); signals: void partUpdated(const QString &filename, long myId, bool connectorsChanged); void closed(long id); void changeActivationSignal(bool activate, QWidget * originator); void saveButtonClicked(); void alienPartUsed(); public slots: void parentAboutToClose(); protected slots: void updateDateAndAuthor(); void propertiesChanged(); protected: bool saveAs(); bool saveAsAux(const QString & fileName); const QDir& createTempFolderIfNecessary(); void closeEvent(QCloseEvent *event); bool eventFilter(QObject *object, QEvent *event); void createHeader(ModelPart * = 0); void createCenter(ModelPart * = 0, class ItemBase * fromItem = 0); void connectWidgetsToSave(const QList &widgets); void createFooter(); ModelPartShared* createModelPartShared(); const QString untitledFileName(); int &untitledFileCount(); const QString fileExtension(); QString getExtensionString(); QStringList getExtensions(); const QString defaultSaveFolder(); void updateSaveButton(); bool wannaSaveAfterWarning(bool savingAsNew); void updateButtons(); const QString fritzingTitle(); void cleanUp(); bool event(QEvent *); void makeNonCore(); protected: long m_id; QPointer m_paletteModel; QPointer m_sketchModel; QPointer m_iconViewImage; QPointer m_title; QPointer m_views; QPointer m_iconItem; QPointer m_breadboardItem; QPointer m_schematicItem; QPointer m_pcbItem; QPointer m_label; QPointer m_description; //QPointer m_taxonomy; QPointer m_tags; QPointer m_properties; QPointer m_author; QPointer m_createdOn; QPointer m_createdByText; QPointer m_connsInfo; QString m_version; QString m_moduleId; QString m_uri; QPointer m_saveAsNewPartButton; QPointer m_saveButton; QPointer m_cancelCloseButton; QPointer m_tabWidget; QPointer m_mainFrame; QPointer m_headerFrame; QPointer m_centerFrame; QPointer m_footerFrame; bool m_updateEnabled; bool m_partUpdated; bool m_savedAsNewPart; bool m_editingAlien; static QPointer m_lastOpened; static int m_closedBeforeCount; static QString ___partsEditorName___; static bool m_closeAfterSaving; public: static QString TitleFreshStartText; static QString LabelFreshStartText; static QString DescriptionFreshStartText; static QString TaxonomyFreshStartText; static QString TagsFreshStartText; static QString FooterText; static QString UntitledPartName; static int UntitledPartIndex; static QGraphicsProxyWidget *emptyViewItem(QString iconFile, QString text=""); static void initText(); }; #endif /* PARTSEDITORMAINWINDOW_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/partseditorview.cpp000066400000000000000000001702721272363601300252150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6417 $: $Author: cohen@irascible.com $: $Date: 2012-09-14 23:34:09 +0200 (Fr, 14. Sep 2012) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "partseditorview.h" #include "partseditorconnectoritem.h" #include "fixfontsdialog.h" #include "zoomcontrols.h" #include "kicadmoduledialog.h" #include "../items/layerkinpaletteitem.h" #include "../items/partfactory.h" #include "../layerattributes.h" #include "../mainwindow/fritzingwindow.h" #include "../fsvgrenderer.h" #include "../debugdialog.h" #include "../utils/folderutils.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../utils/ratsnestcolors.h" #include "../svg/gedaelement2svg.h" #include "../svg/kicadmodule2svg.h" #include "../svg/kicadschematic2svg.h" #include "../connectors/connectorshared.h" int PartsEditorView::ConnDefaultWidth = 5; int PartsEditorView::ConnDefaultHeight = ConnDefaultWidth; PartsEditorView::PartsEditorView( ViewLayer::ViewIdentifier viewId, QDir tempDir, bool showingTerminalPoints, QGraphicsProxyWidget *startItem, QWidget *parent, int size, bool deleteModelPartOnClearScene, ItemBase * fromItem) : SketchWidget(viewId, parent, size, size) { m_alignToGrid = m_showGrid = false; m_viewItem = NULL; m_item = NULL; m_connsLayerID = ViewLayer::UnknownLayer; m_svgLoaded = false; m_deleteModelPartOnSceneClear = deleteModelPartOnClearScene; m_tempFolder = tempDir; setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setDefaultBackground(); //spec m_svgFilePath = new SvgAndPartFilePath; m_startItem = startItem; if(m_startItem) { addFixedToCenterItem(startItem); ensureFixedToCenterItems(); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(ensureFixedItemsPositions())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(ensureFixedItemsPositions())); connect(this, SIGNAL(resizeSignal()), this, SLOT(ensureFixedItemsPositions())); connect(this, SIGNAL(wheelSignal()), this, SLOT(ensureFixedItemsPositions())); // TODO: do we still need this? QTimer::singleShot(400, this, SLOT(ensureFixedItemsPositions())); } addDefaultLayers(fromItem); // conns m_showingTerminalPoints = showingTerminalPoints; m_lastSelectedConnId = ""; setDragMode(QGraphicsView::ScrollHandDrag); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); m_terminalPointsTimer = new QTimer(this); connect( m_terminalPointsTimer,SIGNAL(timeout()), this,SLOT(recoverTerminalPointsState()) ); m_showingTerminalPointsBackup = m_showingTerminalPoints; m_fitItemInViewTimer = new QTimer(this); m_fitItemInViewTimer->setSingleShot(true); m_fitItemInViewTimer->setInterval(200); connect(m_fitItemInViewTimer,SIGNAL(timeout()),this,SLOT(fitCenterAndDeselect())); } PartsEditorView::~PartsEditorView() { if (m_startItem) delete m_startItem; delete m_svgFilePath; clearScene(); } void PartsEditorView::addDefaultLayers(ItemBase * fromItem) { switch( m_viewIdentifier ) { case ViewLayer::BreadboardView: addBreadboardViewLayers(); break; case ViewLayer::SchematicView: addSchematicViewLayers(); break; case ViewLayer::PCBView: addPcbViewLayers(); if (fromItem && fromItem->modelPart()->flippedSMD()) { DebugDialog::debug("editing an SMD part"); setViewLayerIDs(ViewLayer::Silkscreen1, ViewLayer::Copper1Trace, ViewLayer::Copper1, ViewLayer::PcbRuler, ViewLayer::PcbNote); this->m_viewLayers.remove(ViewLayer::Copper0); this->m_viewLayers.remove(ViewLayer::Silkscreen0); this->m_viewLayers.remove(ViewLayer::Copper0Trace); } break; default: break; } } void PartsEditorView::addItemInPartsEditor(ModelPart * modelPart, SvgAndPartFilePath * svgFilePath) { if (modelPart == NULL) { throw "PartsEditorView::addItemInPartsEditor no model part"; } clearScene(); m_item = newPartsEditorPaletteItem(modelPart, svgFilePath); this->addItem(modelPart, defaultViewLayerSpec(), BaseCommand::CrossView, m_item->getViewGeometry(), m_item->id(), -1, NULL, m_item); fitCenterAndDeselect(); setItemProperties(); /*foreach(QWidget* w, m_fixedWidgets) { QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); proxy->setWidget(w); addFixedToBottomRightItem(proxy); }*/ emit connectorsFoundSignal(this->m_viewIdentifier,m_item->connectors()); } ItemBase * PartsEditorView::addItemAux(ModelPart * modelPart, ViewLayer::ViewLayerSpec viewLayerSpec, const ViewGeometry &, long id, PaletteItem * paletteItemAux, bool doConnectors, ViewLayer::ViewIdentifier, bool temporary) { Q_UNUSED(id); Q_UNUSED(temporary); if(paletteItemAux == NULL) { paletteItemAux = newPartsEditorPaletteItem(modelPart); } PartsEditorPaletteItem *paletteItem = dynamic_cast(paletteItemAux); if (paletteItem == NULL) { throw "PartsEditorView::addItemAux paletteItem not found"; } if(paletteItem) { modelPart->initConnectors(); // is a no-op if connectors already in place QString layerFileName = getLayerFileName(modelPart); if(!layerFileName.isEmpty()) { if(paletteItem->createSvgPath(modelPart->path(), layerFileName)) { paletteItem->createSvgFile(paletteItem->svgFilePath()->absolutePath()); ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString( findConnectorsLayerId(paletteItem->svgDom()) ); if(viewLayerID == ViewLayer::UnknownLayer) { viewLayerID = getViewLayerID(modelPart, m_viewIdentifier, viewLayerSpec); } addDefaultLayers(NULL); if (m_viewItem != NULL) { QHash svgHash; QString svg = ""; foreach (ViewLayer * vl, m_viewLayers.values()) { svg += m_viewItem->retrieveSvg(vl->viewLayerID(), svgHash, false, GraphicsUtils::StandardFritzingDPI); } if (!svg.isEmpty()) { QSizeF size = m_viewItem->size(); svg = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, size.width(), size.height()) + svg + ""; paletteItem->setItemSVG(svg); } } QString error; if (paletteItem->renderImage(modelPart, m_viewIdentifier, m_viewLayers, viewLayerID, doConnectors, error)) { addToScene(paletteItemAux, paletteItemAux->viewLayerID()); // layers are not needed on the parts editor (so far) return paletteItemAux; } } } } return NULL; } void PartsEditorView::fitCenterAndDeselect() { if(m_item) { m_item->setSelected(false); m_item->setHidden(false); m_item->setInactive(false); fitInView(m_item, Qt::KeepAspectRatio); QRectF viewRect = rect(); QRectF itemsRect = scene()->itemsBoundingRect(); double wRelation = viewRect.width() / itemsRect.width(); double hRelation = viewRect.height() / itemsRect.height(); if(wRelation < hRelation) { m_scaleValue = (wRelation * 100); } else { m_scaleValue = (hRelation * 100); } emit zoomChanged(m_scaleValue); } } void PartsEditorView::setDefaultBackground() { QString bgColor = " PartsEditorView {background-color: rgb(%1,%2,%3);} "; QColor c = standardBackground(); if (c.isValid()) { setStyleSheet(styleSheet()+bgColor.arg(c.red()).arg(c.green()).arg(c.blue())); } } void PartsEditorView::clearScene() { if(m_item) { deleteItem(m_item, m_deleteModelPartOnSceneClear, true, false); scene()->clear(); m_item = NULL; } } void PartsEditorView::removeConnectors() { QList list; for (int i = m_item->childItems().count()-1; i >= 0; i--) { PartsEditorConnectorItem * connectorItem = dynamic_cast(m_item->childItems()[i]); if (connectorItem == NULL) continue; list << connectorItem; } for(int i=0; i < list.size(); i++) { list[i]->removeFromModel(); delete list[i]; } } ModelPart *PartsEditorView::createFakeModelPart(SvgAndPartFilePath *svgpath) { const QHash connIds = getConnectorsSvgIds(svgpath->absolutePath()); const QStringList layers = getLayers(svgpath->absolutePath()); QString path = svgpath->relativePath().isEmpty() ? svgpath->absolutePath() : svgpath->relativePath(); ModelPart * mp = createFakeModelPart(connIds, layers, path); return mp; } ModelPart *PartsEditorView::createFakeModelPart(const QHash &conns, const QStringList &layers, const QString &svgFilePath) { QDomDocument *domDoc = new QDomDocument(); QString errorStr; int errorLine; int errorColumn; QString fakeFzFile = QString("\n")+ QString("<%1>\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier)).arg(svgFilePath); foreach(QString layer, layers) { fakeFzFile += QString(" \n").arg(layer); } fakeFzFile += QString("\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier))+ QString("\n"); QStringList defaultLayers = defaultLayerAsStringlist(); foreach(QString id, conns.keys()) { QString terminalAttr = conns[id].terminalId.isEmpty() ? "" : QString("terminalId='%1'").arg(conns[id].terminalId); QString name = conns[id].connectorName.isEmpty() ? "" : QString("name='%1'").arg(conns[id].connectorName); fakeFzFile += QString("\n").arg(id).arg(name) + QString("<%1>\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier)); foreach (QString layer, defaultLayers) { if (layers.contains(layer)) { fakeFzFile += QString("

\n") .arg(layer) .arg(conns[id].connectorId) .arg(terminalAttr); } } fakeFzFile += QString("\n").arg(ViewLayer::viewIdentifierXmlName(m_viewIdentifier))+ QString("\n"); } fakeFzFile += QString("\n"); QString path = m_tempFolder.absolutePath()+"/"+FolderUtils::getRandText()+".fz"; TextUtils::writeUtf8(path, fakeFzFile); domDoc->setContent(fakeFzFile, &errorStr, &errorLine, &errorColumn); ModelPart *retval = m_sketchModel->root(); retval->modelPartShared()->setDomDocument(domDoc); retval->modelPartShared()->resetConnectorsInitialization(); retval->modelPartShared()->setPath(path); retval->initConnectors(true /*redo connectors*/); return retval; } const QHash PartsEditorView::getConnectorsSvgIds(const QString &path) { QDomDocument dom ; QFile file(path); dom.setContent(&file); QDomElement docElem = dom.documentElement(); getConnectorsSvgIdsAux(docElem); return m_svgIds; } void PartsEditorView::getConnectorsSvgIdsAux(QDomElement &docElem) { QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { QString id = e.attribute("id"); if(id.startsWith("connector") && id.endsWith("terminal")) { QString conn = id.left(id.lastIndexOf(QRegExp("\\d"))+1); ConnectorTerminalSvgIdPair pair = m_svgIds.contains(conn) ? m_svgIds[conn] : ConnectorTerminalSvgIdPair(); pair.terminalId = id; m_svgIds[conn] = pair; } else if(id.startsWith("connector") /*&& id.endsWith("pin") */ ) { QString conn = id.left(id.lastIndexOf(QRegExp("\\d"))+1); ConnectorTerminalSvgIdPair pair = m_svgIds.contains(conn) ? m_svgIds[conn] : ConnectorTerminalSvgIdPair(); pair.connectorId = id; pair.connectorName = e.attribute("connectorname"); m_svgIds[conn] = pair; } if(e.hasChildNodes()) { getConnectorsSvgIdsAux(e); } e = e.nextSiblingElement(); } } const QStringList PartsEditorView::getLayers(const QString &path) { if(m_viewIdentifier == ViewLayer::IconView) { // defaulting layer to icon for iconview return defaultLayerAsStringlist(); } else { QDomDocument dom; QFile file(path); dom.setContent(&file); return getLayers(&dom); } } const QStringList PartsEditorView::getLayers(const QDomDocument *dom, bool fakeDefaultIfNone) { QStringList retval; QDomNodeList nodeList = dom->elementsByTagName("g"); for (uint i = 0; i < nodeList.length(); i++) { QDomElement e = nodeList.item(i).toElement(); QString id = e.attribute("id"); if (id.isEmpty()) continue; if (ViewLayer::viewLayerIDFromXmlString(id) == ViewLayer::UnknownLayer) continue; retval << id; } if(fakeDefaultIfNone && retval.isEmpty()) { retval << ViewLayer::viewIdentifierNaturalName(m_viewIdentifier); } return retval; } PartsEditorPaletteItem *PartsEditorView::newPartsEditorPaletteItem(ModelPart *modelPart) { return new PartsEditorConnectorsPaletteItem(this, modelPart, m_viewIdentifier); } PartsEditorPaletteItem *PartsEditorView::newPartsEditorPaletteItem(ModelPart * modelPart, SvgAndPartFilePath *path) { return new PartsEditorConnectorsPaletteItem(this, modelPart, m_viewIdentifier, path); } QDir PartsEditorView::tempFolder() { return m_tempFolder; } QString PartsEditorView::getOrCreateViewFolderInTemp() { QString viewFolder = ViewLayer::viewIdentifierNaturalName(m_viewIdentifier); if(!QFileInfo(m_tempFolder.absolutePath()+"/"+viewFolder).exists()) { bool mkResult = m_tempFolder.mkpath(m_tempFolder.absolutePath()+"/"+viewFolder); if (!mkResult) { throw "PartsEditorView::getOrCreateViewFolderInTemp failed"; } } return viewFolder; } bool PartsEditorView::isEmpty() { return m_item == NULL; } bool PartsEditorView::ensureFilePath(const QString &filePath) { QString svgFolder = FolderUtils::getUserDataStorePath("parts")+"/svg"; Qt::CaseSensitivity cs = Qt::CaseSensitive; #ifdef Q_WS_WIN cs = Qt::CaseInsensitive; #endif if(!filePath.contains(svgFolder, cs)) { // This has to be here in order of all this, to work in release mode m_tempFolder.mkpath(QFileInfo(filePath).absoluteDir().path()); } return true; } ViewLayer::ViewLayerID PartsEditorView::connectorsLayerId() { //Q_ASSERT(m_item); findConnectorsLayerId(); return m_connsLayerID; } QString PartsEditorView::terminalIdForConnector(const QString &connId) { //Q_ASSERT(m_item) if (m_item == NULL) return ""; QString result = ""; QDomElement elem = m_item->svgDom()->documentElement(); if(terminalIdForConnectorIdAux(result, connId, elem, true)) { return result; } else { return ""; } } bool PartsEditorView::terminalIdForConnectorIdAux(QString &result, const QString &connId, QDomElement &docElem, bool wantTerminal) { QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { QString id = e.attribute("id"); if(id.startsWith(connId) && ((wantTerminal && id.endsWith("terminal")) || (!wantTerminal && !id.endsWith("terminal")))) { // the id is the one from the previous iteration result = id; return true; } else if(e.hasChildNodes()) { // potencial solution, if the next iteration returns true if(terminalIdForConnectorIdAux(result, connId, e, wantTerminal)) { return true; } } e = e.nextSiblingElement(); } return false; } void PartsEditorView::findConnectorsLayerId() { if(m_connsLayerID == ViewLayer::UnknownLayer) { if (m_item != NULL) { m_connsLayerID = ViewLayer::viewLayerIDFromXmlString( findConnectorsLayerId(m_item->svgDom()) ); } if(m_connsLayerID == ViewLayer::UnknownLayer) { m_connsLayerID = SketchWidget::defaultConnectorLayer(m_viewIdentifier); } } } QStringList PartsEditorView::findConnectorsLayerIds(QDomDocument *svgDom) { QStringList result; QDomElement docElem = svgDom->documentElement(); findConnectorsLayerIdsAux(result, docElem); if (result.count() > 0) return result; return defaultLayerAsStringlist(); } void PartsEditorView::findConnectorsLayerIdsAux(QStringList &result, QDomElement &docElem) { QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { QString id = e.attribute("id"); if (id.startsWith("connector")) { QDomElement parent = e.parentNode().toElement(); QString id = parent.attribute("id"); if (!id.isEmpty() && (ViewLayer::viewLayerIDFromXmlString(id) != ViewLayer::UnknownLayer)) { result << id; } } else if(e.hasChildNodes()) { findConnectorsLayerIdsAux(result, e); } e = e.nextSiblingElement(); } } QString PartsEditorView::findConnectorsLayerId(QDomDocument *svgDom) { QString result; QStringList layers; QDomElement docElem = svgDom->documentElement(); if(findConnectorsLayerIdAux(result, docElem, layers)) { if(ViewLayer::viewLayerIDFromXmlString(result) == ViewLayer::UnknownLayer) { foreach(QString layer, layers) { ViewLayer::ViewLayerID vlid = ViewLayer::viewLayerIDFromXmlString(layer); if(m_viewLayers.keys().contains(vlid)) { result = layer; } } } return result; } else { return defaultLayerAsStringlist().at(0); } } bool PartsEditorView::findConnectorsLayerIdAux(QString &result, QDomElement &docElem, QStringList &prevLayers) { QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { QString id = e.attribute("id"); if(id.startsWith("connector")) { // the id is the one from the previous iteration return true; } else if(e.hasChildNodes()) { // potencial solution, if the next iteration returns true result = id; prevLayers << id; if(findConnectorsLayerIdAux(result, e, prevLayers)) { return true; } } e = e.nextSiblingElement(); } return false; } QString PartsEditorView::getLayerFileName(ModelPart * modelPart) { return modelPart->imageFileName(m_viewIdentifier); } // specs void PartsEditorView::copySvgFileToDestiny(const QString &partFileName) { Qt::CaseSensitivity cs = Qt::CaseSensitive; #ifdef Q_WS_WIN cs = Qt::CaseInsensitive; #endif // if the svg file is in the temp folder, then copy it to destiny if(m_svgFilePath->absolutePath().startsWith(m_tempFolder.absolutePath(),cs)) { QString origFile = svgFilePath(); setFriendlierSvgFileName(partFileName); QString destFile = FolderUtils::getUserDataStorePath("parts")+"/svg/user/"+m_svgFilePath->relativePath(); ensureFilePath(origFile); QFile tempFile(origFile); DebugDialog::debug(QString("copying from %1 to %2") .arg(origFile) .arg(destFile)); tempFile.copy(destFile); tempFile.close(); // update the item info, to point to this file m_svgFilePath->setAbsolutePath(destFile); } } void PartsEditorView::loadFile() { QStringList extras; extras.append(""); extras.append(""); QString imageFiles; if (m_viewIdentifier == ViewLayer::PCBView) { imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4 %5);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;gEDA Footprint Files (%4);;Kicad Module Files (%5)"); // extras[0] = "*.fp"; extras[1] = "*.mod"; } else { imageFiles = tr("Image Files (%1 %2 %3);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3)%4%5"); } if (m_viewIdentifier == ViewLayer::SchematicView) { extras[0] = "*.lib"; imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;Kicad Schematic Files (%4)%5"); // } QString origPath = FolderUtils::getOpenFileName(this, tr("Open Image"), m_originalSvgFilePath.isEmpty() ? FolderUtils::openSaveFolder() /* FolderUtils::getUserDataStorePath("parts")+"/parts/svg/" */ : m_originalSvgFilePath, imageFiles.arg("*.svg").arg("*.jpg *.jpeg").arg("*.png").arg(extras[0]).arg(extras[1]) ); if(origPath.isEmpty()) { return; // Cancel pressed } if(!origPath.endsWith(".svg")) { try { origPath = createSvgFromImage(origPath); } catch (const QString & msg) { QMessageBox::warning( NULL, tr("Conversion problem"), tr("Unable to load image file: \n%1").arg(msg) ); return; } } if(!origPath.isEmpty()) { if(m_startItem) { m_fixedToCenterItems.removeAll(m_startItem); delete m_startItem; m_startItem = NULL; } m_viewItem = NULL; // loading a new file, so m_viewItem is obsolete loadSvgFile(origPath); } } void PartsEditorView::updateModelPart(const QString& origPath) { m_undoStack->push(new QUndoCommand("Dummy parts editor command")); setSvgFilePath(origPath); copyToTempAndRenameIfNecessary(m_svgFilePath); m_item->setSvgFilePath(m_svgFilePath); ModelPart *mp = createFakeModelPart(m_svgFilePath); m_item->setModelPart(mp); } void PartsEditorView::loadSvgFile(const QString& origPath) { // back to an empty state m_drawnConns.clear(); m_removedConnIds.clear(); m_connsLayerID = ViewLayer::UnknownLayer; m_svgIds.clear(); m_svgLoaded = true; bool canceled = false; beforeSVGLoading(origPath, canceled); if(!canceled) { m_undoStack->push(new QUndoCommand("Dummy parts editor command")); setSvgFilePath(origPath); // TODO: this code reuses the current modelpart and replaces its connectors, // it would be better to delete the modelpart and create a new one // however, one would have to tidy up the various objects that rely on pointers // to the original modelpart and its connectors ModelPart * mp = createFakeModelPart(m_svgFilePath); loadSvgFile(mp); } } void PartsEditorView::beforeSVGLoading(const QString &filename, bool &canceled) { QFile file(filename); if(!file.open(QIODevice::ReadOnly )) { QMessageBox::warning( NULL, tr("Couldn't open svg file"), tr( "The file couldn't be opened. If this file defines its dimensions \n" "in non-real-world units (e.g. pixels), then they won't be translated \n" "into real life ones.\n" "Malformed font-family definitions won't be fixed either.") ); return; } QString fileContent(file.readAll()); bool fileHasChanged = (m_viewIdentifier == ViewLayer::IconView) ? false : TextUtils::fixPixelDimensionsIn(fileContent); fileHasChanged |= TextUtils::cleanSodipodi(fileContent); fileHasChanged |= TextUtils::fixViewboxOrigin(fileContent); fileHasChanged |= TextUtils::tspanRemove(fileContent); fileHasChanged |= fixFonts(fileContent,filename,canceled); if(fileHasChanged) { file.close(); if(!TextUtils::writeUtf8(filename, fileContent)) { QMessageBox::warning( NULL, tr("Couldn't write into file"), tr( "This file needs to be fixed to fit fritzing needs, but it couldn't\n" "be written.\n" "Fritzing is not compatible with this kind of svg files. Please \n" "check your permissions, and try again.\n\n" "More information at http://fritzing.org/using-svg-images-new-parts/" ) ); } } } bool PartsEditorView::fixFonts(QString &fileContent, const QString &filename, bool &canceled) { bool changed = removeFontFamilySingleQuotes(fileContent, filename); changed |= fixUnavailableFontFamilies(fileContent, filename, canceled); return changed; } bool PartsEditorView::removeFontFamilySingleQuotes(QString &fileContent, const QString &filename) { QString pattern = "font-family=\"('.*')\""; QSet wrongFontFamilies = TextUtils::getRegexpCaptures(pattern,fileContent); foreach(QString ff, wrongFontFamilies) { QString wrongFF = ff; QString fixedFF = ff.remove('\''); fileContent.replace(wrongFF,fixedFF); DebugDialog::debug( QString("removing font-family single quotes: \"%1\" to \"%2\" in file '%3'") .arg(wrongFF).arg(fixedFF).arg(filename) ); } return wrongFontFamilies.size() > 0; } bool PartsEditorView::fixUnavailableFontFamilies(QString &fileContent, const QString &filename, bool &canceled) { QSet definedFFs; definedFFs.unite(getAttrFontFamilies(fileContent)); definedFFs.unite(getFontFamiliesInsideStyleTag(fileContent)); FixedFontsHash fixedFonts = FixFontsDialog::fixFonts(this,definedFFs,canceled); if(!canceled) { foreach(QString oldF, fixedFonts.keys()) { QString newF = fixedFonts[oldF]; fileContent.replace(oldF,newF); DebugDialog::debug( QString("replacing font-family: \"%1\" to \"%2\" in file '%3'") .arg(oldF).arg(newF).arg(filename) ); } } return !canceled && fixedFonts.size() > 0; } QSet PartsEditorView::getAttrFontFamilies(const QString &fileContent) { /* * font-family defined as attr example: A0 */ QString pattern = "font-family\\s*=\\s*\"(.|[^\"]*)\\s*\""; return TextUtils::getRegexpCaptures(pattern,fileContent); } QSet PartsEditorView::getFontFamiliesInsideStyleTag(const QString &fileContent) { /* * regexp: font-family\s*:\s*(.|[^;"]*).*" * font-family defined in a style attr example: style="font-size:9;-inkscape-font-specification:Droid Sans;font-family:Droid Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal" style="font-size:144px;font-style:normal;font-weight:normal;line-height:100%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans" x="18.000002" */ QString pattern = "font-family\\s*:\\s*(.|[^;\"]*).*\""; return TextUtils::getRegexpCaptures(pattern,fileContent); } void PartsEditorView::loadSvgFile(ModelPart * modelPart) { addItemInPartsEditor(modelPart, m_svgFilePath); copyToTempAndRenameIfNecessary(m_svgFilePath); m_item->setSvgFilePath(m_svgFilePath); } void PartsEditorView::loadFromModel(PaletteModel *paletteModel, ModelPart * modelPart) { clearScene(); ViewGeometry viewGeometry; this->setPaletteModel(paletteModel); m_item = (PartsEditorPaletteItem*) addItemAux(modelPart, defaultViewLayerSpec(), viewGeometry, ItemBase::getNextID(), NULL, true, m_viewIdentifier, true); fitCenterAndDeselect(); setItemProperties(); if(m_item) { if(m_startItem) { m_fixedToCenterItems.removeAll(m_startItem); delete m_startItem; m_startItem = NULL; } SvgAndPartFilePath *sp = m_item->svgFilePath(); copyToTempAndRenameIfNecessary(sp); m_item->setSvgFilePath(m_svgFilePath); } } void PartsEditorView::copyToTempAndRenameIfNecessary(SvgAndPartFilePath *filePathOrig) { m_originalSvgFilePath = filePathOrig->absolutePath(); QString userSvgFolderPath = FolderUtils::getUserDataStorePath("parts")+"/svg"; QString coreSvgFolderPath = FolderUtils::getApplicationSubFolderPath("parts")+"/svg"; QString pfSvgFolderPath = PartFactory::folderPath()+"/svg"; if(!(filePathOrig->absolutePath().startsWith(userSvgFolderPath) || filePathOrig->absolutePath().startsWith(coreSvgFolderPath) || filePathOrig->absolutePath().startsWith(pfSvgFolderPath)) ) { // it's outside the parts folder DebugDialog::debug(QString("copying from %1").arg(m_originalSvgFilePath)); QString viewFolder = ViewLayer::viewIdentifierNaturalName(m_viewIdentifier); if(!QFileInfo(m_tempFolder.path()+"/"+viewFolder).exists() && !m_tempFolder.mkdir(viewFolder)) return; if(!m_tempFolder.cd(viewFolder)) return; QString destFilePath = FolderUtils::getRandText()+".svg"; DebugDialog::debug(QString("dest file: %1").arg(m_tempFolder.absolutePath()+"/"+destFilePath)); ensureFilePath(m_tempFolder.absolutePath()+"/"+destFilePath); QFile tempFile(m_originalSvgFilePath); tempFile.copy(m_tempFolder.absolutePath()+"/"+destFilePath); tempFile.close(); if(!m_tempFolder.cd("..")) return; // out of view folder m_svgFilePath->setRelativePath(viewFolder+"/"+destFilePath); m_svgFilePath->setAbsolutePath(m_tempFolder.absolutePath()+"/"+m_svgFilePath->relativePath()); } else { QString relPathAux = filePathOrig->relativePath(); m_svgFilePath->setAbsolutePath(m_originalSvgFilePath); if (relPathAux.count("/") > 2) { throw "PartsEditorView::copyToTempAndRenameIfNecessary bad path"; } if(relPathAux.count("/") == 2) { // this means that core/user/contrib is still in the file name m_svgFilePath->setRelativePath( relPathAux.right(// remove user/core/contrib relPathAux.size() - relPathAux.indexOf("/") - 1 ) ); } else { //otherwise, just leave it as it is m_svgFilePath->setRelativePath(relPathAux); } } } void PartsEditorView::setSvgFilePath(const QString &filePath) { ensureFilePath(filePath); m_originalSvgFilePath = filePath; QString userSvgFolder = FolderUtils::getUserDataStorePath("parts")+"/svg"; QString coreSvgFolder = FolderUtils::getApplicationSubFolderPath("parts")+"/svg"; QString pfSvgFolder = PartFactory::folderPath()+"/svg"; QString tempFolder = m_tempFolder.absolutePath(); QString relative; Qt::CaseSensitivity cs = Qt::CaseSensitive; QString filePathAux = filePath; #ifdef Q_WS_WIN // seems to be necessary for Windows: getUserDataStorePath() returns a string starting with "c:" // but the file dialog returns a string beginning with "C:" cs = Qt::CaseInsensitive; #endif if(filePath.contains(userSvgFolder, cs) || filePath.contains(coreSvgFolder, cs) || filePath.contains(pfSvgFolder, cs)) { int ix = filePath.indexOf("svg"); // is core/user file relative = filePathAux.remove(0, ix + 4); //Mariano: I don't like this folder thing anymore relative = relative.mid(filePathAux.indexOf("/")+1); // remove core/user/contrib } else { // generated jpeg/png or file outside fritzing folder relative = ""; } if (m_svgFilePath) delete m_svgFilePath; m_svgFilePath = new SvgAndPartFilePath(filePath,relative); } const QString PartsEditorView::svgFilePath() { return m_svgFilePath->absolutePath(); } const SvgAndPartFilePath& PartsEditorView::svgFileSplit() { return *m_svgFilePath; } QString PartsEditorView::createSvgFromImage(const QString &origFilePath) { QString viewFolder = getOrCreateViewFolderInTemp(); QString newFilePath = m_tempFolder.absolutePath()+"/"+viewFolder+"/"+FolderUtils::getRandText()+".svg"; ensureFilePath(newFilePath); if (origFilePath.endsWith(".fp")) { // this is a geda footprint file GedaElement2Svg geda; QString svg = geda.convert(origFilePath, false); return saveSvg(svg, newFilePath); } if (origFilePath.endsWith(".lib")) { // Kicad schematic library file QStringList defs = KicadSchematic2Svg::listDefs(origFilePath); if (defs.count() == 0) { throw tr("no schematics found in %1").arg(origFilePath); } QString def; if (defs.count() > 1) { KicadModuleDialog kmd(tr("schematic part"), origFilePath, defs, this); int result = kmd.exec(); if (result != QDialog::Accepted) { return ""; } def = kmd.selectedModule(); } else { def = defs.at(0); } KicadSchematic2Svg kicad; QString svg = kicad.convert(origFilePath, def); return saveSvg(svg, newFilePath); } if (origFilePath.endsWith(".mod")) { // Kicad footprint (Module) library file QStringList modules = KicadModule2Svg::listModules(origFilePath); if (modules.count() == 0) { throw tr("no footprints found in %1").arg(origFilePath); } QString module; if (modules.count() > 1) { KicadModuleDialog kmd("footprint", origFilePath, modules, this); int result = kmd.exec(); if (result != QDialog::Accepted) { return ""; } module = kmd.selectedModule(); } else { module = modules.at(0); } KicadModule2Svg kicad; QString svg = kicad.convert(origFilePath, module, false); return saveSvg(svg, newFilePath); } // deal with png, jpg, etc.: /* %1=witdh in mm * %2=height in mm * %3=width in local coords * %4=height in local coords * %5=binary data */ /* QString svgTemplate = "\n" " \n" " \n" " \n" " \n" " "; QPixmap pixmap(origFilePath); QByteArray bytes; QBuffer buffer(&bytes); buffer.open(QIODevice::WriteOnly); pixmap.save(&buffer,"png"); // writes pixmap into bytes in PNG format QString svgDom = svgTemplate .arg(pixmap.widthMM()).arg(pixmap.heightMM()) .arg(pixmap.width()).arg(pixmap.height()) .arg(QString("data:image/png;base64,%2").arg(QString(bytes.toBase64()))); QFile destFile(newFilePath); if(!destFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::information(NULL, "", "file not created"); if(!destFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::information(NULL, "", "file not created 2"); } } QTextStream out(&destFile); out << svgDom; destFile.close(); qDebug() << newFilePath; bool existsResult = QFileInfo(newFilePath).exists(); Q_ASSERT(existsResult); */ QImage img(origFilePath); QSvgGenerator svgGenerator; svgGenerator.setResolution(90); svgGenerator.setFileName(newFilePath); QSize sz = img.size(); svgGenerator.setSize(sz); svgGenerator.setViewBox(QRect(0, 0, sz.width(), sz.height())); QPainter svgPainter(&svgGenerator); svgPainter.drawImage(QPoint(0,0), img); svgPainter.end(); return newFilePath; } QString PartsEditorView::setFriendlierSvgFileName(const QString &partFileName) { QString aux = partFileName; aux = aux .remove(FritzingPartExtension) .replace(" ","_"); if(aux.length()>40) aux.truncate(40); aux+=QString("__%1__%2.svg") .arg(ViewLayer::viewIdentifierNaturalName(m_viewIdentifier)) .arg(FolderUtils::getRandText()); int slashIdx = m_svgFilePath->relativePath().indexOf("/"); QString relpath = m_svgFilePath->relativePath(); QString relpath2 = relpath; QString abspath = m_svgFilePath->absolutePath(); QString viewFolder = relpath.remove(slashIdx,relpath.size()-slashIdx+1); m_svgFilePath->setAbsolutePath(abspath.remove(relpath2)+viewFolder+"/"+aux); m_svgFilePath->setRelativePath(viewFolder+"/"+aux); return aux; } // conns void PartsEditorView::wheelEvent(QWheelEvent* event) { if(m_showingTerminalPoints) { if(!m_terminalPointsTimer->isActive()) { m_showingTerminalPointsBackup = m_showingTerminalPoints; showTerminalPoints(false); m_terminalPointsTimer->start(50); } } else if(m_terminalPointsTimer->isActive()) { m_terminalPointsTimer->stop(); m_terminalPointsTimer->start(50); } SketchWidget::wheelEvent(event); } void PartsEditorView::mousePressEvent(QMouseEvent *event) { SketchWidget::mousePressEvent(event); } void PartsEditorView::mouseMoveEvent(QMouseEvent *event) { QGraphicsView::mouseMoveEvent(event); } void PartsEditorView::mouseReleaseEvent(QMouseEvent *event) { SketchWidget::mouseReleaseEvent(event); } void PartsEditorView::resizeEvent(QResizeEvent * event) { SketchWidget::resizeEvent(event); if(m_fitItemInViewTimer->isActive()) { m_fitItemInViewTimer->stop(); } m_fitItemInViewTimer->start(); } void PartsEditorView::drawConector(Connector *conn, bool showTerminalPoint) { QSize size(ConnDefaultWidth,ConnDefaultHeight); createConnector(conn,size,showTerminalPoint); } void PartsEditorView::createConnector(Connector *conn, const QSize &connSize, bool showTerminalPoint) { QString connId = conn->connectorSharedID(); QRectF bounds = m_item ? QRectF(m_item->boundingRect().center(),connSize) : QRectF(scene()->itemsBoundingRect().center(),connSize); PartsEditorConnectorsConnectorItem *connItem = new PartsEditorConnectorsConnectorItem(conn, m_item, m_showingTerminalPoints, bounds); m_drawnConns[connId] = connItem; connItem->setShowTerminalPoint(showTerminalPoint); m_undoStack->push(new QUndoCommand( QString("connector '%1' added to %2 view") .arg(connId).arg(ViewLayer::viewIdentifierName(m_viewIdentifier)) )); } void PartsEditorView::removeConnector(const QString &connId) { ConnectorItem *connToRemove = NULL; foreach(QGraphicsItem *item, items()) { ConnectorItem *connItem = dynamic_cast(item); if(connItem && connItem->connector()->connectorSharedID() == connId) { connToRemove = connItem; break; } } if(connToRemove) { scene()->removeItem(connToRemove); scene()->update(); m_undoStack->push(new QUndoCommand( QString("connector '%1' removed from %2 view") .arg(connId).arg(ViewLayer::viewIdentifierName(m_viewIdentifier)) )); PartsEditorConnectorsConnectorItem *connToRemoveAux = dynamic_cast(connToRemove); m_drawnConns.remove(connToRemoveAux->connectorSharedID()); m_removedConnIds << connId; } } void PartsEditorView::setItemProperties() { if(m_item) { m_item->setFlag(QGraphicsItem::ItemIsSelectable, false); m_item->setFlag(QGraphicsItem::ItemIsMovable, false); m_item->setFlag(QGraphicsItem::ItemClipsToShape, true); //m_item->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); myItem()->highlightConnectors(m_lastSelectedConnId); double size = 500; // just make sure the user get enough space to play setSceneRect(0,0,size,size); m_item->setPos((size-m_item->size().width())/2,(size-m_item->size().height())/2); centerOn(m_item); } //ensureFixedToBottomRight(m_zoomControls); } void PartsEditorView::informConnectorSelection(const QString &connId) { if(m_item) { m_lastSelectedConnId = connId; myItem()->highlightConnectors(connId); } } void PartsEditorView::informConnectorSelectionFromView(const QString &connId) { informConnectorSelection(connId); emit connectorSelected(connId); } void PartsEditorView::setMismatching(ViewLayer::ViewIdentifier viewId, const QString &id, bool mismatching) { if(m_item && viewId == m_viewIdentifier) { for (int i = 0; i < m_item->childItems().count(); i++) { PartsEditorConnectorsConnectorItem * connectorItem = dynamic_cast(m_item->childItems()[i]); if(connectorItem == NULL) continue; if(connectorItem->connector()->connectorSharedID() == id) { connectorItem->setMismatching(mismatching); } } } } void PartsEditorView::aboutToSave(bool fakeDefaultIfNone) { if(m_item) { FSvgRenderer renderer; QByteArray bytes = renderer.loadSvg(m_item->flatSvgFilePath()); if (!bytes.isEmpty()) { QRectF svgViewBox = renderer.viewBoxF(); QSizeF sceneViewBox = renderer.defaultSizeF(); QDomDocument *svgDom = m_item->svgDom(); // this may change the layers defined in the file, so // let's get the connectorsLayer after it bool somethingChanged = addDefaultLayerIfNotInSvg(svgDom, fakeDefaultIfNone); QDomElement elem = svgDom->documentElement(); somethingChanged |= removeConnectorsIfNeeded(elem); QStringList connectorsLayerIds = findConnectorsLayerIds(svgDom); foreach (QString connectorsLayerId, connectorsLayerIds) { somethingChanged |= updateTerminalPoints(svgDom, sceneViewBox, svgViewBox, connectorsLayerId); somethingChanged |= addConnectorsIfNeeded(svgDom, sceneViewBox, svgViewBox, connectorsLayerId); } somethingChanged |= (m_viewItem != NULL); if(somethingChanged) { QString viewFolder = getOrCreateViewFolderInTemp(); QString tempFile = m_tempFolder.absolutePath()+"/"+viewFolder+"/"+FolderUtils::getRandText()+".svg"; ensureFilePath(tempFile); if(!TextUtils::writeUtf8(tempFile, TextUtils::removeXMLEntities(svgDom->toString()))) { /*QMessageBox::information(NULL,"", QString("Couldn't open file for update, after drawing connectors: '%1'") .arg(tempFile) );*/ DebugDialog::debug(QString("Couldn't open file for update, after drawing connectors: '%1'").arg(tempFile)); } } } else { DebugDialog::debug("updating part view svg file: could not load file "+m_item->flatSvgFilePath()); } } } bool PartsEditorView::addConnectorsIfNeeded(QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId) { bool changed = false; if(!m_drawnConns.isEmpty()) { QRectF bounds; QString connId; foreach(PartsEditorConnectorsConnectorItem* drawnConn, m_drawnConns.values()) { bounds = drawnConn->mappedRect(); connId = drawnConn->connector()->connectorSharedID(); QRectF svgRect = mapFromSceneToSvg(bounds,sceneViewBox,svgViewBox); QString svgId = svgIdForConnector(drawnConn->connector(), connId); addRectToSvg(svgDom,svgId,svgRect, connectorsLayerId); } changed = true; } return changed; } bool PartsEditorView::addDefaultLayerIfNotInSvg(QDomDocument *svgDom, bool fakeDefaultIfNone) { QStringList defaultLayers = defaultLayerAsStringlist(); QStringList layers = getLayers(svgDom, fakeDefaultIfNone); foreach (QString defaultLayer, defaultLayers) { if (layers.contains(defaultLayer)) return false; } // jrc 4/7/2010: not sure if making a new top level layer is correct // since it could swallow other layers QDomElement docElem = svgDom->documentElement(); QDomElement newTopLevel = svgDom->createElement("g"); newTopLevel.setAttribute("id", defaultLayers.at(0)); // place the child in a aux list, cause the // qdomnodelist takes care of the references QList children; for(QDomNode child=docElem.firstChild(); !child.isNull(); child=child.nextSibling()) { children << child; } foreach(QDomNode child, children) { newTopLevel.appendChild(child); } docElem.appendChild(newTopLevel); return true; } LayerList PartsEditorView::defaultLayers() { LayerList layers; switch( m_viewIdentifier ) { case ViewLayer::IconView: layers << ViewLayer::Icon; break; case ViewLayer::BreadboardView: layers << ViewLayer::Breadboard; break; case ViewLayer::SchematicView: layers << ViewLayer::Schematic; break; case ViewLayer::PCBView: layers << ViewLayer::Copper0 << ViewLayer::Copper1; break; default: layers << ViewLayer::UnknownLayer; break; } return layers; } QStringList PartsEditorView::defaultLayerAsStringlist() { QStringList layers; foreach (ViewLayer::ViewLayerID viewLayerID, defaultLayers()) { layers << ViewLayer::viewLayerXmlNameFromID(viewLayerID); } return layers; } QString PartsEditorView::svgIdForConnector(const QString &connId) { //Q_ASSERT(m_item) if (m_item == NULL) return connId; QString result = ""; QDomElement elem = m_item->svgDom()->documentElement(); if(terminalIdForConnectorIdAux(result, connId, elem, false)) { return result; } /* foreach(Connector* conn, m_item->connectors()) { QString svgId = svgIdForConnector(conn, connId); if(connId != svgId) { return svgId; } } */ return connId; } QString PartsEditorView::svgIdForConnector(Connector* conn, const QString &connId) { if (conn->connectorShared() && conn->connectorSharedID() == connId) { foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(m_viewIdentifier)) { return sil->m_svgId; } } return connId; } bool PartsEditorView::updateTerminalPoints(QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId) { QList connsWithNewTPs; QStringList tpIdsToRemove; foreach(QGraphicsItem *item, items()) { PartsEditorConnectorsConnectorItem *citem = dynamic_cast(item); if(citem) { TerminalPointItem *tp = citem->terminalPointItem(); if (!tp) { citem->setShowTerminalPoint(citem->isShowingTerminalPoint()); tp = citem->terminalPointItem(); } QString connId = citem->connector()->connectorSharedID(); QString terminalId = connId+"terminal"; if(tp && !tp->isInTheCenter()) { if(tp->hasBeenMoved() || citem->hasBeenMoved()) { connsWithNewTPs << citem; tpIdsToRemove << terminalId; //DebugDialog::debug("<<<< MOVED! removing terminal "+terminalId+" in view: "+ViewLayer::viewIdentifierName(m_viewIdentifier)); updateSvgIdLayer(connId, terminalId, connectorsLayerId); } } else { //DebugDialog::debug("<<<< removing terminal "+terminalId+" in view: "+ViewLayer::viewIdentifierName(m_viewIdentifier)); tpIdsToRemove << terminalId; emit removeTerminalPoint(connId, m_viewIdentifier); } } } QDomElement elem = svgDom->documentElement(); removeTerminalPoints(tpIdsToRemove,elem); addNewTerminalPoints(connsWithNewTPs, svgDom, sceneViewBox, svgViewBox, connectorsLayerId); return !tpIdsToRemove.isEmpty(); } void PartsEditorView::updateSvgIdLayer(const QString &connId, const QString &terminalId, const QString &connectorsLayerId) { ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(connectorsLayerId); foreach(Connector *conn, m_item->connectors()) { foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(m_viewIdentifier)) { if(conn->connectorSharedID() == connId) { if (sil->m_svgViewLayerID == viewLayerID) { sil->m_terminalId = terminalId; return; } } } foreach(SvgIdLayer *sil, conn->connectorShared()->pins().values(m_viewIdentifier)) { if(conn->connectorSharedID() == connId) { sil->m_terminalId = terminalId; if(viewLayerID != ViewLayer::UnknownLayer) { sil->m_svgViewLayerID = viewLayerID; } } } } } void PartsEditorView::removeTerminalPoints(const QStringList &tpIdsToRemove, QDomElement &docElem) { QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { bool doRemove = false; QString id = e.attribute("id"); if(tpIdsToRemove.contains(id)) { doRemove = true; } else if(e.hasChildNodes()) { removeTerminalPoints(tpIdsToRemove,e); } QDomElement e2; if(doRemove) { e2 = e; } e = e.nextSiblingElement(); if(doRemove) { e2.removeAttribute("id"); } } } void PartsEditorView::addNewTerminalPoints( const QList &connsWithNewTPs, QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId ) { foreach(PartsEditorConnectorsConnectorItem* citem, connsWithNewTPs) { QString connId = citem->connector()->connectorSharedID(); TerminalPointItem *tp = citem->terminalPointItem(); if (tp == NULL) { throw "PartsEditorView::addNewTerminalPoints tp missing"; } if(tp) { QRectF tpointRect(tp->mappedPoint(), QPointF(0,0)); QRectF svgTpRect = mapFromSceneToSvg(tpointRect,sceneViewBox,svgViewBox); double halfTPSize = 0.001; // a tiny rectangle svgTpRect.setSize(QSizeF(halfTPSize*2,halfTPSize*2)); addRectToSvg(svgDom,connId+"terminal",svgTpRect, connectorsLayerId); } else { qWarning() << tr( "Parts Editor: couldn't save terminal " "point for connector %1 in %2 view") .arg(citem->connector()->connectorSharedID()) .arg(ViewLayer::viewIdentifierNaturalName(m_viewIdentifier)); } } } bool PartsEditorView::removeConnectorsIfNeeded(QDomElement &docElem) { if(!m_removedConnIds.isEmpty()) { //Q_ASSERT(docElem.tagName() == "svg"); QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { QString id = e.attribute("id"); if(isSupposedToBeRemoved(id)) { e.removeAttribute("id"); } else if(e.hasChildNodes()) { removeConnectorsIfNeeded(e); } e = e.nextSiblingElement(); } return true; } return false; } QRectF PartsEditorView::mapFromSceneToSvg(const QRectF &itemRect, const QSizeF &sceneViewBox, const QRectF &svgViewBox) { double relationW = svgViewBox.width() / sceneViewBox.width(); double relationH = svgViewBox.height() / sceneViewBox.height(); double x = itemRect.x() * relationW; double y = itemRect.y() * relationH; double width = itemRect.width() * relationW; double height = itemRect.height() * relationH; return QRectF(x,y,width,height); } bool PartsEditorView::addRectToSvg(QDomDocument* svgDom, const QString &id, const QRectF &rect, const QString &connectorsLayerId) { QDomElement connElem = svgDom->createElement("rect"); connElem.setAttribute("id",id); connElem.setAttribute("x",rect.x()); connElem.setAttribute("y",rect.y()); connElem.setAttribute("width",rect.width()); connElem.setAttribute("height",rect.height()); connElem.setAttribute("fill","none"); if(connectorsLayerId.isEmpty()) { svgDom->firstChildElement("svg").appendChild(connElem); return true; } else { QDomElement docElem = svgDom->documentElement(); return addRectToSvgAux(docElem, connectorsLayerId, connElem); } } bool PartsEditorView::addRectToSvgAux(QDomElement &docElem, const QString &connectorsLayerId, QDomElement &rectElem) { QDomElement e = docElem.firstChildElement(); while(!e.isNull()) { QString id = e.attribute("id"); if(id == connectorsLayerId) { e.appendChild(rectElem); return true; } else if(e.hasChildNodes()) { if(addRectToSvgAux(e, connectorsLayerId, rectElem)) { return true; } } e = e.nextSiblingElement(); } return false; } bool PartsEditorView::isSupposedToBeRemoved(const QString& id) { if (id.isEmpty()) return false; // TODO: m_removedConnIds should be svg ids and not connectorSharedIDs (from fzp) foreach(QString toBeRemoved, m_removedConnIds) { if(id.startsWith(toBeRemoved)) { QString temp = id; temp = temp.remove(0, toBeRemoved.length()); if (temp.length() == 0) return true; // assumes svg id is always prefixDsuffix where D is some string of decimal digits // and prefixD matches toBeRemoved if (!temp.at(0).isDigit()) { return true; } } } return false; } PartsEditorConnectorsPaletteItem *PartsEditorView::myItem() { return dynamic_cast(m_item.data()); } void PartsEditorView::showTerminalPoints(bool show) { m_showingTerminalPoints = show; foreach(QGraphicsItem *item, items()) { PartsEditorConnectorsConnectorItem *connItem = dynamic_cast(item); if(connItem) { connItem->setShowTerminalPoint(show); } } scene()->update(); /*if(!m_showingTerminalPoints) { m_terminalPointsTimer->stop(); }*/ } bool PartsEditorView::showingTerminalPoints() { return m_showingTerminalPoints; } void PartsEditorView::inFileDefinedConnectorChanged(PartsEditorConnectorsConnectorItem *connItem) { QString connId = connItem->connectorSharedID(); m_drawnConns[connId] = connItem; if(!m_removedConnIds.contains(connId)) { m_removedConnIds << connId; } } void PartsEditorView::addFixedToBottomRight(QWidget *widget) { m_fixedWidgets << widget; QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); proxy->setWidget(widget); addFixedToBottomRightItem(proxy); } bool PartsEditorView::imageLoaded() { return m_item != NULL; } void PartsEditorView::drawBackground(QPainter *painter, const QRectF &rect) { SketchWidget::drawBackground(painter,rect); // 10mm spacing grid /*const int gridSize = 10*width()/widthMM(); QRectF itemRect = m_item->mapToScene(m_item->boundingRect()).boundingRect(); painter->drawRect(itemRect); double itemTop = itemRect.top(); double itemLeft = itemRect.left(); QVarLengthArray lines; for (double x = itemLeft; x < rect.right(); x += gridSize) { lines.append(QLineF(x, rect.top(), x, rect.bottom())); } for (double x = itemLeft-gridSize; x > rect.left(); x -= gridSize) { lines.append(QLineF(x, rect.top(), x, rect.bottom())); } for (double y = itemTop; y < rect.bottom(); y += gridSize) { lines.append(QLineF(rect.left(), y, rect.right(), y)); } for (double y = itemTop-gridSize; y > rect.top(); y -= gridSize) { lines.append(QLineF(rect.left(), y, rect.right(), y)); } painter->drawLines(lines.data(), lines.size());*/ } void PartsEditorView::recoverTerminalPointsState() { showTerminalPoints(m_showingTerminalPointsBackup); m_terminalPointsTimer->stop(); } bool PartsEditorView::connsPosOrSizeChanged() { foreach(QGraphicsItem *item, items()) { PartsEditorConnectorsConnectorItem *citem = dynamic_cast(item); if(citem) { TerminalPointItem *tp = citem->terminalPointItem(); if((tp && tp->hasBeenMoved()) || citem->hasBeenMoved() || citem->hasBeenResized()) { return true; } } } return false; } void PartsEditorView::setViewItem(ItemBase * item) { m_viewItem = item; } void PartsEditorView::checkConnectorLayers(ViewLayer::ViewIdentifier viewIdentifier, const QString & connId, Connector* existingConnector, Connector * newConnector) { if (m_viewIdentifier != viewIdentifier) return; Q_UNUSED(connId); QList newpins = newConnector->connectorShared()->pins().values(viewIdentifier); QList oldpins = existingConnector->connectorShared()->pins().values(viewIdentifier); LayerList layerList; QList changes; foreach (SvgIdLayer * newSvgIdLayer, newpins) { bool gotOne = false; layerList << newSvgIdLayer->m_svgViewLayerID; foreach (SvgIdLayer * oldSvgIdLayer, oldpins) { if (newSvgIdLayer->m_svgViewLayerID == oldSvgIdLayer->m_svgViewLayerID) { gotOne = true; break; } } if (!gotOne) { changes << newSvgIdLayer; } } foreach (SvgIdLayer * svgIdLayer, changes) { SvgIdLayer * cpy = svgIdLayer->copyLayer(); existingConnector->connectorShared()->insertPin(viewIdentifier, cpy); } changes.clear(); foreach (SvgIdLayer * oldSvgIdLayer, oldpins) { bool gotOne = false; foreach (SvgIdLayer * newSvgIdLayer, newpins) { if (newSvgIdLayer->m_svgViewLayerID == oldSvgIdLayer->m_svgViewLayerID) { gotOne = true; break; } } if (!gotOne) { changes << oldSvgIdLayer; } } foreach (SvgIdLayer * svgIdLayer, changes) { existingConnector->connectorShared()->removePin(viewIdentifier, svgIdLayer); } } void PartsEditorView::updatePinsInfo(QList< QPointer > connsShared) { if(!m_svgLoaded) return; // if the user has not changed the svg file, there's nothing to update ViewLayer::ViewLayerID layerID = connectorsLayerId(); QList notFound; foreach(ConnectorShared* cs, connsShared) { QString connId = cs->id(); SvgIdLayer* pinInfo = cs->fullPinInfo(m_viewIdentifier, layerID); if (pinInfo == NULL) { notFound << cs; } else if(!m_svgIds[connId].connectorId.isEmpty()) { pinInfo->m_svgId = m_svgIds[connId].connectorId; // terminal points are already updated (by the function updateTerminalPoints) // pinInfo->m_terminalId = m_svgIds[connId].terminalId; } } if (notFound.length() == 0) return; // not sure this is the right place to handle the change of connector layers... LayerList alts = ViewLayer::findAlternativeLayers(layerID); if (alts.length() == 0) return; foreach (ViewLayer::ViewLayerID vlid, alts) { QList found; foreach(ConnectorShared* cs, notFound) { QString connId = cs->id(); SvgIdLayer* pinInfo = cs->fullPinInfo(m_viewIdentifier, vlid); if (pinInfo != NULL && !m_svgIds[connId].connectorId.isEmpty()) { pinInfo->m_svgId = m_svgIds[connId].connectorId; pinInfo->m_svgViewLayerID = layerID; found << cs; } } foreach (ConnectorShared * cs, found) { notFound.removeOne(cs); } } } QString PartsEditorView::saveSvg(const QString & svg, const QString & newFilePath) { if (!TextUtils::writeUtf8(newFilePath, svg)) { throw tr("unable to open temp file %1").arg(newFilePath); } return newFilePath; } void PartsEditorView::clearFixedItems() { m_fixedToBottomLeftItems.clear(); m_fixedToBottomRightItems.clear(); m_fixedToCenterItems.clear(); m_fixedToTopLeftItems.clear(); m_fixedToTopRightItems.clear(); } void PartsEditorView::ensureFixedItemsPositions() { //DebugDialog::debug("ensure fixed items positions"); ensureFixedToBottomLeftItems(); ensureFixedToCenterItems(); ensureFixedToTopLeftItems(); ensureFixedToTopRightItems(); ensureFixedToBottomRightItems(); scene()->update(sceneRect()); } void PartsEditorView::addFixedToTopLeftItem(QGraphicsItem *item) { item->setFlag(QGraphicsItem::ItemIgnoresTransformations); if(!scene()->items().contains(item)) { scene()->addItem(item); } m_fixedToTopLeftItems << item; ensureFixedToTopLeft(item); } void PartsEditorView::addFixedToTopRightItem(QGraphicsItem *item) { item->setFlag(QGraphicsItem::ItemIgnoresTransformations); if(!scene()->items().contains(item)) { scene()->addItem(item); } m_fixedToTopRightItems << item; ensureFixedToTopRight(item); } void PartsEditorView::addFixedToBottomLeftItem(QGraphicsItem *item) { item->setFlag(QGraphicsItem::ItemIgnoresTransformations); if(!scene()->items().contains(item)) { scene()->addItem(item); } m_fixedToBottomLeftItems << item; ensureFixedToBottomLeft(item); } void PartsEditorView::addFixedToBottomRightItem(QGraphicsItem *item) { item->setFlag(QGraphicsItem::ItemIgnoresTransformations); if(!scene()->items().contains(item)) { scene()->addItem(item); } m_fixedToBottomRightItems << item; ensureFixedToBottomRight(item); } void PartsEditorView::addFixedToCenterItem(QGraphicsItem *item) { item->setFlag(QGraphicsItem::ItemIgnoresTransformations); if(!scene()->items().contains(item)) { scene()->addItem(item); } m_fixedToCenterItems << item; ensureFixedToCenter(item); } void PartsEditorView::ensureFixedToTopLeftItems() { if(isVisible()) { QList toRemove; foreach(QGraphicsItem* item, m_fixedToTopLeftItems) { if(scene()->items().contains(item)) { ensureFixedToTopLeft(item); } else { toRemove << item; } } foreach(QGraphicsItem* item, toRemove) { m_fixedToTopLeftItems.removeAll(item); } } } void PartsEditorView::ensureFixedToTopLeft(QGraphicsItem* item) { item->setPos(mapToScene(0,0)); } void PartsEditorView::ensureFixedToTopRightItems() { if(isVisible()) { QList toRemove; foreach(QGraphicsItem* item, m_fixedToTopRightItems) { if(scene()->items().contains(item)) { ensureFixedToTopRight(item); } else { toRemove << item; } } foreach(QGraphicsItem* item, toRemove) { m_fixedToTopRightItems.removeAll(item); } } } void PartsEditorView::ensureFixedToTopRight(QGraphicsItem* item) { int x = (int) (width()-fixedItemWidth(item)); int y = 0; item->setPos(mapToScene(x,y)); } void PartsEditorView::ensureFixedToBottomLeftItems() { if(isVisible()) { QList toRemove; foreach(QGraphicsItem* item, m_fixedToBottomLeftItems) { if(scene()->items().contains(item)) { ensureFixedToBottomLeft(item); } else { toRemove << item; } } foreach(QGraphicsItem* item, toRemove) { m_fixedToBottomLeftItems.removeAll(item); } } } void PartsEditorView::ensureFixedToBottomLeft(QGraphicsItem* item) { int x = 0; int y = (int) (height()-fixedItemHeight(item)); item->setPos(mapToScene(x,y)); } void PartsEditorView::ensureFixedToBottomRightItems() { if(isVisible()) { QList toRemove; foreach(QGraphicsItem* item, m_fixedToBottomRightItems) { if(scene()->items().contains(item)) { ensureFixedToBottomRight(item); } else { toRemove << item; } } foreach(QGraphicsItem* item, toRemove) { m_fixedToBottomRightItems.removeAll(item); } } } void PartsEditorView::ensureFixedToBottomRight(QGraphicsItem* item) { int x = (int) (width()-fixedItemWidth(item)); int y = (int) (height()-fixedItemHeight(item)); item->setPos(mapToScene(x,y)); } void PartsEditorView::ensureFixedToCenterItems() { if(isVisible()) { QList toRemove; foreach(QGraphicsItem* item, m_fixedToCenterItems) { if(scene()->items().contains(item)) { ensureFixedToCenter(item); } else { toRemove << item; } } foreach(QGraphicsItem* item, toRemove) { m_fixedToCenterItems.removeAll(item); } } } void PartsEditorView::ensureFixedToCenter(QGraphicsItem* item) { double x = (width()-fixedItemWidth(item))/2; double y = (height()-fixedItemHeight(item))/2; QPointF pos = mapToScene(x,y); if(pos.x() < scene()->width() && pos.y() < scene()->height()) { item->setPos(pos); } } void PartsEditorView::removeIfFixedPos(QGraphicsItem *item) { m_fixedToBottomLeftItems.removeAll(item); m_fixedToBottomRightItems.removeAll(item); m_fixedToCenterItems.removeAll(item); m_fixedToTopLeftItems.removeAll(item); m_fixedToTopRightItems.removeAll(item); } double PartsEditorView::fixedItemWidth(QGraphicsItem* item) { QGraphicsProxyWidget* gWidget = dynamic_cast(item); if(gWidget) { return gWidget->widget()->width(); } else { return item->boundingRect().width(); } } double PartsEditorView::fixedItemHeight(QGraphicsItem* item) { QGraphicsProxyWidget* gWidget = dynamic_cast(item); if(gWidget) { return gWidget->widget()->height(); } else { return item->boundingRect().height(); } } void PartsEditorView::deleteItem(ItemBase * itemBase, bool deleteModelPart, bool doEmit, bool later) { removeIfFixedPos(itemBase); SketchWidget::deleteItem(itemBase, deleteModelPart, doEmit, later); } void PartsEditorView::setPaletteModel(PaletteModel * paletteModel) { m_paletteModel = paletteModel; } fritzing-app-0.9.3b/src/partseditor/obsolete/partseditorview.h000066400000000000000000000240461272363601300246570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSEDITORVIEW_H_ #define PARTSEDITORVIEW_H_ #include "../sketch/sketchwidget.h" #include "../model/palettemodel.h" #include "partseditorpaletteitem.h" #include "partseditorconnectorspaletteitem.h" #include "partseditorconnectorsconnectoritem.h" #include #include #include struct ConnectorTerminalSvgIdPair { ConnectorTerminalSvgIdPair() { } QString connectorId; QString terminalId; QString connectorName; }; class PartsEditorView : public SketchWidget { Q_OBJECT public: PartsEditorView( ViewLayer::ViewIdentifier, QDir tempDir, bool showingTerminalPoints, QGraphicsProxyWidget *startItem=0, QWidget *parent=0, int size=150, bool deleteModelPartOnClearScene=false, class ItemBase * fromItem = NULL); ~PartsEditorView(); // general QDir tempFolder(); bool isEmpty(); ViewLayer::ViewLayerID connectorsLayerId(); QString terminalIdForConnector(const QString &connId); void addFixedToBottomRight(QWidget *widget); bool imageLoaded(); // specs void loadSvgFile(ModelPart * modelPart); void copySvgFileToDestiny(const QString &partFileName); const QString svgFilePath(); const SvgAndPartFilePath& svgFileSplit(); // conns void drawConector(Connector *conn, bool showTerminalPoint); void removeConnector(const QString &connId); void inFileDefinedConnectorChanged(PartsEditorConnectorsConnectorItem *connItem); void aboutToSave(bool fakeDefaultIfNotIn); void updatePinsInfo(QList< QPointer > conns); void showTerminalPoints(bool show); bool showingTerminalPoints(); QString svgIdForConnector(const QString &connId); PartsEditorConnectorsPaletteItem *myItem(); bool connsPosOrSizeChanged(); void setViewItem(ItemBase *); void setPaletteModel(PaletteModel *); public slots: // general void loadFromModel(PaletteModel *paletteModel, ModelPart * modelPart); void addItemInPartsEditor(ModelPart * modelPart, SvgAndPartFilePath * svgFilePath); // specs void loadFile(); void loadSvgFile(const QString& origPath); void updateModelPart(const QString& origPath); // conns void informConnectorSelection(const QString& connId); void informConnectorSelectionFromView(const QString& connId); void setMismatching(ViewLayer::ViewIdentifier viewId, const QString &id, bool mismatching); void checkConnectorLayers(ViewLayer::ViewIdentifier, const QString & connId, Connector* existingConnector, Connector * newConnector); protected slots: void recoverTerminalPointsState(); void fitCenterAndDeselect(); void ensureFixedItemsPositions(); signals: // conns void connectorsFoundSignal(ViewLayer::ViewIdentifier viewId, const QList< QPointer > &conns); void svgFileLoadNeeded(const QString &filepath); void connectorSelected(const QString& connId); void removeTerminalPoint(const QString &connId, ViewLayer::ViewIdentifier vid); protected: // general PartsEditorPaletteItem *newPartsEditorPaletteItem(ModelPart * modelPart); PartsEditorPaletteItem *newPartsEditorPaletteItem(ModelPart * modelPart, SvgAndPartFilePath *path); void setDefaultBackground(); void clearScene(); void removeConnectors(); void addDefaultLayers(class ItemBase * fromItem); void wheelEvent(QWheelEvent* event); void drawBackground(QPainter *painter, const QRectF &rect); ItemBase * addItemAux(ModelPart * modelPart, ViewLayer::ViewLayerSpec, const ViewGeometry & viewGeometry, long id, PaletteItem* paletteItem, bool doConnectors, ViewLayer::ViewIdentifier, bool temporary); ModelPart *createFakeModelPart(SvgAndPartFilePath *svgpath); ModelPart *createFakeModelPart(const QHash &connIds, const QStringList &layers, const QString &svgFilePath); const QHash getConnectorsSvgIds(const QString &path); void getConnectorsSvgIdsAux(QDomElement &docElem); const QStringList getLayers(const QString &path); const QStringList getLayers(const QDomDocument *dom, bool fakeDefaultIfNone=true); QString getOrCreateViewFolderInTemp(); bool ensureFilePath(const QString &filePath); // TODO: retire all singular findConnectorsLayerId() for plural findConnectorsLayerIds() QStringList findConnectorsLayerIds(QDomDocument *svgDom); void findConnectorsLayerIdsAux(QStringList &result, QDomElement &docElem); void findConnectorsLayerId(); QString findConnectorsLayerId(QDomDocument *svgDom); bool findConnectorsLayerIdAux(QString &result, QDomElement &docElem, QStringList &prevLayers); bool terminalIdForConnectorIdAux(QString &result, const QString &connId, QDomElement &docElem, bool wantTerminal); QString getLayerFileName(ModelPart * modelPart); LayerList defaultLayers(); QStringList defaultLayerAsStringlist(); // SVG fixing void beforeSVGLoading(const QString &filename, bool &canceled); bool fixFonts(QString &fileContent, const QString &filename, bool &canceled); bool removeFontFamilySingleQuotes(QString &fileContent, const QString &filename); bool fixUnavailableFontFamilies(QString &fileContent, const QString &filename, bool &canceled); QSet getAttrFontFamilies(const QString &fileContent); QSet getFontFamiliesInsideStyleTag(const QString &fileContent); // specs void setSvgFilePath(const QString &filePath); void copyToTempAndRenameIfNecessary(SvgAndPartFilePath *filePathOrig); QString createSvgFromImage(const QString &filePath); QString setFriendlierSvgFileName(const QString &partFileName); // conns void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void resizeEvent(QResizeEvent * event); void connectItem(); void createConnector(Connector *conn, const QSize &connSize, bool showTerminalPoint); void setItemProperties(); bool isSupposedToBeRemoved(const QString& id); bool addConnectorsIfNeeded(QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId); bool removeConnectorsIfNeeded(QDomElement &docEle); bool updateTerminalPoints(QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId); bool addDefaultLayerIfNotInSvg(QDomDocument *svgDom, bool fakeDefaultIfNone); QString svgIdForConnector(Connector* conn, const QString &connId); void updateSvgIdLayer(const QString &connId, const QString &terminalId, const QString &connectorsLayerId); void removeTerminalPoints(const QStringList &tpIdsToRemove, QDomElement &docElem); void addNewTerminalPoints( const QList &connsWithNewTPs, QDomDocument *svgDom, const QSizeF &sceneViewBox, const QRectF &svgViewBox, const QString &connectorsLayerId ); QRectF mapFromSceneToSvg(const QRectF &sceneRect, const QSizeF &defaultSize, const QRectF &viewBox); bool addRectToSvg(QDomDocument* svgDom, const QString &id, const QRectF &rect, const QString &connectorsLayerId); bool addRectToSvgAux(QDomElement &docElem, const QString &connectorsLayerId, QDomElement &rectElem); QString saveSvg(const QString & svg, const QString & newFilePath); void addFixedToTopLeftItem(QGraphicsItem *item); void addFixedToTopRightItem(QGraphicsItem *item); void addFixedToBottomLeftItem(QGraphicsItem *item); void addFixedToCenterItem(QGraphicsItem *item); void addFixedToBottomRightItem(QGraphicsItem *item); void ensureFixedToTopLeftItems(); void ensureFixedToTopRightItems(); void ensureFixedToBottomLeftItems(); void ensureFixedToBottomRightItems(); void ensureFixedToCenterItems(); void ensureFixedToTopLeft(QGraphicsItem* item); void ensureFixedToTopRight(QGraphicsItem* item); void ensureFixedToBottomLeft(QGraphicsItem* item); void ensureFixedToBottomRight(QGraphicsItem* item); void ensureFixedToCenter(QGraphicsItem* item); void clearFixedItems(); void removeIfFixedPos(QGraphicsItem *item); double fixedItemWidth(QGraphicsItem* item); double fixedItemHeight(QGraphicsItem* item); void deleteItem(ItemBase *, bool deleteModelPart, bool doEmit, bool later); protected: QPointer m_item; // just one item per view QDir m_tempFolder; bool m_deleteModelPartOnSceneClear; QPointer m_startItem; SvgAndPartFilePath *m_svgFilePath; QString m_originalSvgFilePath; QHash m_drawnConns; QStringList m_removedConnIds; QHash m_svgIds; ViewLayer::ViewLayerID m_connsLayerID; bool m_svgLoaded; QString m_lastSelectedConnId; bool m_showingTerminalPoints; bool m_showingTerminalPointsBackup; QTimer *m_terminalPointsTimer; QList m_fixedWidgets; ItemBase * m_viewItem; QTimer *m_fitItemInViewTimer; QList m_fixedToTopLeftItems; QList m_fixedToTopRightItems; QList m_fixedToBottomLeftItems; QList m_fixedToBottomRightItems; QList m_fixedToCenterItems; PaletteModel * m_paletteModel; protected: static int ConnDefaultWidth; static int ConnDefaultHeight; }; #endif /* PARTSEDITORVIEW_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/partseditorviewswidget.cpp000066400000000000000000000302741272363601300266010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include "partseditorviewswidget.h" #include "partseditormainwindow.h" #include "zoomcontrols.h" #include "../utils/misc.h" #include "../waitpushundostack.h" #include "../debugdialog.h" QString PartsEditorViewsWidget::EmptyBreadViewText; QString PartsEditorViewsWidget::EmptySchemViewText; QString PartsEditorViewsWidget::EmptyPcbViewText; PartsEditorViewsWidget::PartsEditorViewsWidget(SketchModel *sketchModel, WaitPushUndoStack *undoStack, ConnectorsInfoWidget* info, QWidget *parent, ItemBase * fromItem) : QFrame(parent) { init(); m_showTerminalPointsCheckBox = new QCheckBox(this); m_showTerminalPointsCheckBox->setText(tr("Show Anchor Points")); connect(m_showTerminalPointsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(showHideTerminalPoints(int))); m_breadView = createViewImageWidget(sketchModel, undoStack, ViewLayer::BreadboardView, "breadboard_icon.png", EmptyBreadViewText, info, ViewLayer::Breadboard, fromItem); m_schemView = createViewImageWidget(sketchModel, undoStack, ViewLayer::SchematicView, "schematic_icon.png", EmptySchemViewText, info, ViewLayer::Schematic, fromItem); m_pcbView = createViewImageWidget(sketchModel, undoStack, ViewLayer::PCBView, "pcb_icon.png", EmptyPcbViewText, info, ViewLayer::Copper0, fromItem); m_breadView->setViewLayerIDs(ViewLayer::Breadboard, ViewLayer::BreadboardWire, ViewLayer::Breadboard, ViewLayer::BreadboardRuler, ViewLayer::BreadboardNote); m_schemView->setViewLayerIDs(ViewLayer::Schematic, ViewLayer::SchematicWire, ViewLayer::Schematic, ViewLayer::SchematicRuler, ViewLayer::SchematicNote); m_pcbView->setViewLayerIDs(ViewLayer::Schematic, ViewLayer::SchematicWire, ViewLayer::Schematic, ViewLayer::SchematicRuler, ViewLayer::PcbNote); connectPair(m_breadView,m_schemView); connectPair(m_schemView,m_pcbView); connectPair(m_pcbView,m_breadView); connectToThis(m_breadView); connectToThis(m_schemView); connectToThis(m_pcbView); m_viewsContainter = new QSplitter(this); m_viewsContainter->addWidget(addZoomControlsAndBrowseButton(m_breadView)); m_viewsContainter->addWidget(addZoomControlsAndBrowseButton(m_schemView)); m_viewsContainter->addWidget(addZoomControlsAndBrowseButton(m_pcbView)); m_viewsContainter->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); m_guidelines = new QLabel(tr("Please refer to the guidelines before modifying or creating parts"), this); m_guidelines->setOpenExternalLinks(true); m_guidelines->setObjectName("guidelinesLabel"); QHBoxLayout *labelLayout = new QHBoxLayout(); labelLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::MinimumExpanding,QSizePolicy::Fixed)); labelLayout->addWidget(m_guidelines); labelLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::MinimumExpanding,QSizePolicy::Fixed)); QFrame *toolsAndInfoContainer = new QFrame(this); QHBoxLayout *layout2 = new QHBoxLayout(toolsAndInfoContainer); layout2->addLayout(labelLayout); layout2->addWidget(m_showTerminalPointsCheckBox); layout2->setMargin(1); layout2->setSpacing(1); QVBoxLayout *lo = new QVBoxLayout(this); lo->addWidget(m_viewsContainter); lo->addWidget(toolsAndInfoContainer); lo->setMargin(3); lo->setSpacing(1); this->resize(width(),220); //this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); } PartsEditorViewsWidget::~PartsEditorViewsWidget() { if (m_breadView) delete m_breadView; if (m_schemView) delete m_schemView; if (m_pcbView) delete m_pcbView; } void PartsEditorViewsWidget::init() { m_connsPosChanged = false; if(EmptyBreadViewText.isEmpty()) { EmptyBreadViewText = tr("What does this\npart look like on\nthe breadboard?"); } if(EmptySchemViewText.isEmpty()) { EmptySchemViewText = tr("What does this\npart look like in\na schematic view?"); } if(EmptyPcbViewText.isEmpty()) { EmptyPcbViewText = tr("What does this\npart look like in\nthe PCB view?"); } } PartsEditorView * PartsEditorViewsWidget::createViewImageWidget( SketchModel* sketchModel, WaitPushUndoStack *undoStack, ViewLayer::ViewIdentifier viewId, QString iconFileName, QString startText, ConnectorsInfoWidget* info, ViewLayer::ViewLayerID viewLayerId, ItemBase * fromItem) { PartsEditorView * viw = new PartsEditorView(viewId,tempDir(),showingTerminalPoints(),PartsEditorMainWindow::emptyViewItem(iconFileName,startText),this, 150, false, fromItem); viw->setSketchModel(sketchModel); viw->setUndoStack(undoStack); viw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); viw->addViewLayer(new ViewLayer(viewLayerId, true, 2.5)); m_views[viewId] = viw; connect( info, SIGNAL(connectorSelected(const QString&)), viw, SLOT(informConnectorSelection(const QString&)) ); connect( viw, SIGNAL(connectorsFoundSignal(ViewLayer::ViewIdentifier, const QList< QPointer > &)), info, SLOT(syncNewConnectors(ViewLayer::ViewIdentifier, const QList< QPointer > &)) ); connect( info, SIGNAL(existingConnectorSignal(ViewLayer::ViewIdentifier, const QString &, Connector*, Connector*)), viw, SLOT(checkConnectorLayers(ViewLayer::ViewIdentifier, const QString &, Connector*, Connector*)) ); connect( info, SIGNAL(setMismatching(ViewLayer::ViewIdentifier, const QString &, bool)), viw, SLOT(setMismatching(ViewLayer::ViewIdentifier, const QString &, bool)) ); return viw; } void PartsEditorViewsWidget::copySvgFilesToDestiny(const QString &partFileName) { m_breadView->copySvgFileToDestiny(partFileName); m_schemView->copySvgFileToDestiny(partFileName); m_pcbView->copySvgFileToDestiny(partFileName); } void PartsEditorViewsWidget::loadViewsImagesFromModel(PaletteModel *paletteModel, ModelPart *modelPart) { m_breadView->scene()->clear(); m_breadView->setPaletteModel(paletteModel); m_breadView->loadFromModel(paletteModel, modelPart); m_schemView->scene()->clear(); m_schemView->setPaletteModel(paletteModel); m_schemView->loadFromModel(paletteModel, modelPart); m_pcbView->scene()->clear(); m_pcbView->setPaletteModel(paletteModel); m_pcbView->loadFromModel(paletteModel, modelPart); if(modelPart->connectors().size() > 0) { emit connectorsFoundSignal(modelPart->connectors().values()); } } const QDir& PartsEditorViewsWidget::tempDir() { return ((PartsEditorMainWindow*)parentWidget())->tempDir(); } void PartsEditorViewsWidget::connectPair(PartsEditorView *v1, PartsEditorView *v2) { connect( v1, SIGNAL(connectorSelected(const QString &)), v2, SLOT(informConnectorSelection(const QString &)) ); connect( v2, SIGNAL(connectorSelected(const QString &)), v1, SLOT(informConnectorSelection(const QString &)) ); } void PartsEditorViewsWidget::connectToThis(PartsEditorView *v) { connect( v, SIGNAL(connectorSelected(const QString &)), this, SLOT(informConnectorSelection(const QString &)) ); } void PartsEditorViewsWidget::repaint() { m_breadView->scene()->update(); m_schemView->scene()->update(); m_pcbView->scene()->update(); } void PartsEditorViewsWidget::drawConnector(Connector* conn) { bool showing = showingTerminalPoints(); m_breadView->drawConector(conn,showing); m_schemView->drawConector(conn,showing); m_pcbView->drawConector(conn,showing); } void PartsEditorViewsWidget::drawConnector(ViewLayer::ViewIdentifier viewId, Connector* conn) { bool showing = showingTerminalPoints(); m_views[viewId]->drawConector(conn,showing); } void PartsEditorViewsWidget::setMismatching(ViewLayer::ViewIdentifier viewId, const QString &connId, bool mismatching) { m_views[viewId]->setMismatching(viewId, connId, mismatching); m_views[viewId]->scene()->update(); } void PartsEditorViewsWidget::aboutToSave() { m_breadView->aboutToSave(false); m_schemView->aboutToSave(false); m_pcbView->aboutToSave(false); } void PartsEditorViewsWidget::removeConnectorFrom(const QString &connId, ViewLayer::ViewIdentifier viewId) { if(viewId == ViewLayer::AllViews) { m_breadView->removeConnector(connId); m_schemView->removeConnector(connId); m_pcbView->removeConnector(connId); } else { m_views[viewId]->removeConnector(connId); } } void PartsEditorViewsWidget::showHideTerminalPoints(int checkState) { bool show = checkStateToBool(checkState); m_breadView->showTerminalPoints(show); m_schemView->showTerminalPoints(show); m_pcbView->showTerminalPoints(show); } bool PartsEditorViewsWidget::showingTerminalPoints() { return checkStateToBool(m_showTerminalPointsCheckBox->checkState()); } bool PartsEditorViewsWidget::checkStateToBool(int checkState) { if(checkState == Qt::Checked) { return true; } else if(checkState == Qt::Unchecked) { return false; } return false; } QCheckBox *PartsEditorViewsWidget::showTerminalPointsCheckBox() { return m_showTerminalPointsCheckBox; } void PartsEditorViewsWidget::informConnectorSelection(const QString &connId) { emit connectorSelectedInView(connId); } QWidget *PartsEditorViewsWidget::addZoomControlsAndBrowseButton(PartsEditorView *view) { QFrame *container1 = new QFrame(this); QVBoxLayout *lo1 = new QVBoxLayout(container1); lo1->setSpacing(1); lo1->setMargin(0); QLabel *button = new QLabel(QString("%1").arg(tr("Load image..")), this); button->setObjectName("browseButton"); button->setMinimumWidth(85); button->setMaximumWidth(85); button->setFixedHeight(20); button->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); connect(button, SIGNAL(linkActivated(const QString&)), view, SLOT(loadFile())); QHBoxLayout *lo2 = new QHBoxLayout(); lo2->setSpacing(1); lo2->setMargin(0); lo2->addWidget(button); lo2->addWidget(new ZoomControls(view,container1)); lo1->addWidget(view); lo1->addLayout(lo2); return container1; } PartsEditorView *PartsEditorViewsWidget::breadboardView() { return m_breadView; } PartsEditorView *PartsEditorViewsWidget::schematicView() { return m_schemView; } PartsEditorView *PartsEditorViewsWidget::pcbView() { return m_pcbView; } bool PartsEditorViewsWidget::imagesLoadedInAllViews() { return m_breadView->imageLoaded() && m_schemView->imageLoaded() && m_pcbView->imageLoaded(); } void PartsEditorViewsWidget::connectTerminalRemoval(const ConnectorsInfoWidget* connsInfo) { connect( m_breadView, SIGNAL(removeTerminalPoint(const QString&, ViewLayer::ViewIdentifier)), connsInfo, SLOT(removeTerminalPoint(const QString&, ViewLayer::ViewIdentifier)) ); connect( m_schemView, SIGNAL(removeTerminalPoint(const QString&, ViewLayer::ViewIdentifier)), connsInfo, SLOT(removeTerminalPoint(const QString&, ViewLayer::ViewIdentifier)) ); connect( m_pcbView, SIGNAL(removeTerminalPoint(const QString&, ViewLayer::ViewIdentifier)), connsInfo, SLOT(removeTerminalPoint(const QString&, ViewLayer::ViewIdentifier)) ); } bool PartsEditorViewsWidget::connectorsPosOrSizeChanged() { return m_breadView->connsPosOrSizeChanged() || m_schemView->connsPosOrSizeChanged() || m_pcbView->connsPosOrSizeChanged(); } void PartsEditorViewsWidget::setViewItems(ItemBase* bbItem, ItemBase* schemItem, ItemBase* pcbItem) { m_breadView->setViewItem(bbItem); m_schemView->setViewItem(schemItem); m_pcbView->setViewItem(pcbItem); } void PartsEditorViewsWidget::updatePinsInfo(QList< QPointer > connsShared) { m_breadView->updatePinsInfo(connsShared); m_schemView->updatePinsInfo(connsShared); m_pcbView->updatePinsInfo(connsShared); } fritzing-app-0.9.3b/src/partseditor/obsolete/partseditorviewswidget.h000066400000000000000000000070551272363601300262470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSEDITORVIEWSWIDGET_H_ #define PARTSEDITORVIEWSWIDGET_H_ #include #include #include "partseditorview.h" #include "connectorsinfowidget.h" class PartsEditorViewsWidget : public QFrame { Q_OBJECT public: PartsEditorViewsWidget(SketchModel *sketchModel, class WaitPushUndoStack *undoStack, ConnectorsInfoWidget* info, QWidget *parent, class ItemBase * fromItem); ~PartsEditorViewsWidget(); void copySvgFilesToDestiny(const QString &partFileName); void loadViewsImagesFromModel(PaletteModel *paletteModel, ModelPart *modelPart); const QDir& tempDir(); void aboutToSave(); void updatePinsInfo(QList< QPointer > connsShared); QCheckBox *showTerminalPointsCheckBox(); bool imagesLoadedInAllViews(); void setViewItems(class ItemBase *, class ItemBase *, class ItemBase *); PartsEditorView *breadboardView(); PartsEditorView *schematicView(); PartsEditorView *pcbView(); void connectTerminalRemoval(const ConnectorsInfoWidget* connsInfo); bool connectorsPosOrSizeChanged(); public slots: void repaint(); void drawConnector(Connector*); void drawConnector(ViewLayer::ViewIdentifier, Connector*); void removeConnectorFrom(const QString&,ViewLayer::ViewIdentifier); void showHideTerminalPoints(int checkState); void informConnectorSelection(const QString &connId); void setMismatching(ViewLayer::ViewIdentifier viewId, const QString &id, bool mismatching); signals: void connectorsFoundSignal(QList< QPointer >); void connectorSelectedInView(const QString& connId); protected: PartsEditorView * createViewImageWidget( SketchModel* sketchModel, class WaitPushUndoStack *undoStack, ViewLayer::ViewIdentifier viewId, QString iconFileName, QString startText, ConnectorsInfoWidget* info, ViewLayer::ViewLayerID viewLayerId, class ItemBase * fromItem ); void init(); QWidget *addZoomControlsAndBrowseButton(PartsEditorView *view); bool showingTerminalPoints(); bool checkStateToBool(int checkState); void connectPair(PartsEditorView *v1, PartsEditorView *v2); void connectToThis(PartsEditorView *v); QPointer m_breadView; QPointer m_schemView; QPointer m_pcbView; QHash m_views; QSplitter *m_viewsContainter; QPointer m_showTerminalPointsCheckBox; QPointer m_guidelines; bool m_connsPosChanged; protected: static QString EmptyBreadViewText; static QString EmptySchemViewText; static QString EmptyPcbViewText; }; #endif /* PARTSEDITORVIEWSWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/partspecificationswidget.cpp000066400000000000000000000047661272363601300270640ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include "partspecificationswidget.h" #include "../debugdialog.h" PartSpecificationsWidget::PartSpecificationsWidget(QList widgets, QWidget *parent) : QScrollArea(parent) { m_scrollContent = new QFrame(this); m_scrollContent->setObjectName("scroll"); //m_scrollContent->setSizePolicy(QSizePolicy::M,QSizePolicy::Expanding); QGridLayout *layout = new QGridLayout(m_scrollContent); for(int i=0; i < widgets.size(); i++) { //widgets[i]->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); widgets[i]->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); layout->addWidget(widgets[i],i,0); if(widgets[i]->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("editionStarted()")) > -1) { connect(widgets[i],SIGNAL(editionStarted()),this,SLOT(updateLayout())); } if(widgets[i]->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("editionFinished()")) > -1) { connect(widgets[i],SIGNAL(editionFinished()),this,SLOT(updateLayout())); } } layout->setMargin(0); layout->setSpacing(10); setWidget(m_scrollContent); setMinimumWidth(m_scrollContent->width()+15); //scrollbar setWidgetResizable(true); QGridLayout *mylayout = new QGridLayout(this); mylayout->setMargin(0); mylayout->setSpacing(0); //resize(sizeHint()); } void PartSpecificationsWidget::updateLayout() { m_scrollContent->adjustSize(); } QSize PartSpecificationsWidget::sizeHint() { //return QSize(width(),600); return QScrollArea::sizeHint(); } fritzing-app-0.9.3b/src/partseditor/obsolete/partspecificationswidget.h000066400000000000000000000025601272363601300265170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSPECIFICATIONSWIDGET_H_ #define PARTSPECIFICATIONSWIDGET_H_ #include class PartSpecificationsWidget : public QScrollArea { Q_OBJECT public: PartSpecificationsWidget(QList widgets, QWidget *parent=0); QSize sizeHint(); protected slots: void updateLayout(); protected: QFrame *m_scrollContent; }; #endif /* PARTSPECIFICATIONSWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/pcbxml.cpp000066400000000000000000000227131272363601300232430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "pcbxml.h" #include #include #include #include "../debugdialog.h" // this module converts fritzing footprint xml files to svg // TODO: mask and keepout layers // TODO: elementarc // TODO: square pins and round pads? PcbXML::PcbXML( const QDomElement & pcbDocument ) { // TODO: need some execption handling for bad elements missing attrs, etc. m_svg = new SVGDomDocument(); m_svgroot = m_svg->documentElement(); // FIXME: what happens when there is no mark - should be 0 m_markx = pcbDocument.attribute("MX").toInt(); m_marky = pcbDocument.attribute("MY").toInt(); m_units = pcbDocument.attribute("units"); m_minx = m_markx; m_miny = m_marky; m_maxx = m_markx; m_maxy = m_marky; m_pinCount = 0; m_padCount = 0; m_silkscreen = m_svg->createGroup("silkscreen"); m_copper = m_svg->createGroup("copper0"); m_keepout = m_svg->createGroup("keepout"); m_mask = m_svg->createGroup("soldermask"); m_outline = m_svg->createGroup("outline"); QDomNodeList tagList = pcbDocument.childNodes(); //TODO: eventually need to support recursing into the tree here //TODO: support footprints with multiple root elements (module style) for(uint i = 0; i < tagList.length(); i++){ drawNode(tagList.item(i)); } m_svg->setHeight(m_maxy-m_miny, m_units); m_svg->setWidth(m_maxx-m_minx, m_units); m_svg->setViewBox(0,0,m_maxx-m_minx,m_maxy-m_miny); shiftCoordinates(); m_svgFile = QDir::tempPath () + "/footprint.svg"; DebugDialog::debug(QString("temp path %1").arg(QDir::tempPath () + "/test.svg" ) ); m_svg->save(m_svgFile); } QString PcbXML::getSvgFile(){ return m_svgFile; } void PcbXML::drawNode(QDomNode node){ DebugDialog::debug("drawing node:"); QString tag = node.nodeName().toLower(); if(tag=="pin"){ DebugDialog::debug("\tPin"); drawPin(node); } else if(tag=="pad"){ DebugDialog::debug("\tPad"); drawPad(node); } else if(tag=="elementline"){ DebugDialog::debug("\tElementLine"); drawElementLine(node); } else if(tag=="elementarc"){ DebugDialog::debug("\tElementArc"); drawElementArc(node); } else if(tag=="mark"){ DebugDialog::debug("\tMark"); drawMark(node); } else { DebugDialog::debug("cannot draw - unrecognized tag"); } } void PcbXML::drawPin(QDomNode node){ QDomElement element = node.toElement(); QDomElement pin = m_svg->createElement("circle"); int x; int y; if( element.hasAttribute("aX") ){ x = element.attribute("aX").toInt(); y = element.attribute("aY").toInt(); } else { x = element.attribute("rX").toInt() + m_markx; y = element.attribute("rY").toInt() + m_marky; } int drill = element.attribute("Drill").toInt()/2; int thickness = element.attribute("Thickness").toInt()/2 - drill; int radius = drill + thickness/2; QString id = QString("connector%1pin").arg(m_pinCount); // BUG: what's up with str -> hex conversion??? bool verify; bool square=false; if(element.hasAttribute("NFlags")) { square = element.attribute("NFlags").toInt(&verify,16) & 0x0100; } else if(element.hasAttribute("SFlags")) { square = element.attribute("SFlags").toInt(&verify,16) & 0x0100; } //DebugDialog::debug(QString("NFlags: %1 Value:%2 Square:%3").arg(element.attribute("NFlags")).arg(element.attribute("NFlags").toInt(&verify,16)).arg(square)); m_pinCount++; if(square){ QDomElement sq = m_svg->createElement("rect"); sq.setAttribute("x",x-radius); sq.setAttribute("y",y-radius); sq.setAttribute("fill", "none"); sq.setAttribute("width",radius*2); sq.setAttribute("height",radius*2); sq.setAttribute("stroke", "rgb(255, 191, 0)"); sq.setAttribute("stroke-width", thickness); QDomNode tempsq = m_copper.appendChild(sq); } pin.setAttribute("cx", x); pin.setAttribute("cy", y); pin.setAttribute("r", radius); pin.setAttribute("stroke", "rgb(255, 191, 0)"); pin.setAttribute("stroke-width", thickness); pin.setAttribute("id", id); pin.setAttribute("fill", "none"); QDomNode temp = m_copper.appendChild(pin); minMax(x,y,radius+(thickness/2)); } void PcbXML::drawPad(QDomNode node){ QDomElement element = node.toElement(); QDomElement pad = m_svg->createElement("rect"); // TODO: mask and keepout layers int x1 = qMin(element.attribute("rX1").toInt(),element.attribute("rX2").toInt()) + m_markx; int x2 = qMax(element.attribute("rX1").toInt(),element.attribute("rX2").toInt()) + m_markx; int y1 = qMin(element.attribute("rY1").toInt(),element.attribute("rY2").toInt()) + m_marky; int y2 = qMax(element.attribute("rY1").toInt(),element.attribute("rY2").toInt()) + m_marky; int thickness = element.attribute("Thickness").toInt(); QString id = "connector" + QString::number(m_padCount) + "pad"; m_padCount++; int x = x1 - (thickness/2); int y = y1 - (thickness/2); int width = x2 - x1 + thickness; int height = y2 -y1 + thickness; pad.setAttribute("x", x); pad.setAttribute("y", y); pad.setAttribute("width", width); pad.setAttribute("height", height); pad.setAttribute("fill", "rgb(255, 191, 0)"); pad.setAttribute("id", id); QDomNode temp = m_copper.appendChild(pad); minMax(x,y,0); minMax(x+width,y+height,0); } void PcbXML::drawElementLine(QDomNode node){ QDomElement element = node.toElement(); QDomElement line = m_svg->createElement("line"); int x1 = element.attribute("X1").toInt() + m_markx; int y1 = element.attribute("Y1").toInt() + m_marky; int x2 = element.attribute("X2").toInt() + m_markx; int y2 = element.attribute("Y2").toInt() + m_marky; int thickness = element.attribute("Thickness").toInt(); line.setAttribute("x1", x1); line.setAttribute("y1", y1); line.setAttribute("x2", x2); line.setAttribute("y2", y2); line.setAttribute("stroke", "white"); line.setAttribute("stroke-width", thickness); QDomNode temp = m_silkscreen.appendChild(line); minMax(x1,y1,thickness); minMax(x2,y2,thickness); } void PcbXML::drawElementArc(QDomNode node){ QDomElement element = node.toElement(); // TODO: implement this with cubic bezier in svg int x = element.attribute("X").toInt() + m_markx; int y = element.attribute("Y").toInt() + m_marky; //int width = element.attribute("width").toInt()/2; //int height = element.attribute("height").toInt()/2; //int startangle = element.attribute("StartAngle").toInt(); //int endangle = element.attribute("Delta").toInt() + startangle; //int thickness = element.attribute("Thickness").toInt(); QString path = "M" + QString::number(x) + "," + QString::number(y) + " "; } // ignore for now void PcbXML::drawMark(QDomNode /*node*/){ //QDomElement element = node.toElement();QDomElement element = node.toElement(); // //int ax = element.attribute("aX").toInt(); int ax = element.attribute("aX").toInt(); //int ay = element.attribute("aY").toInt(); int ay = element.attribute("aY").toInt(); //int radius = element.attribute("Drill").toInt()/2; int radius = element.attribute("Drill").toInt()/2; //int thickness = element.attribute("Thickness").toInt(); int thickness = element.attribute("Thickness").toInt(); return; } // checks to see if input is greater or less than current min max // adjust viewbox accordingly void PcbXML::minMax(int x, int y, int width){ m_minx = qMin(x-width, m_minx); m_miny = qMin(y-width, m_miny); m_maxx = qMax(x+width, m_maxx); m_maxy = qMax(y+width, m_maxy); } // given minimum x and y coordinates shift all elements to upper left corner void PcbXML::shiftCoordinates(){ // shift lines QDomNodeList lineList = m_svgroot.elementsByTagName("line"); for(uint i = 0; i < lineList.length(); i++){ QDomElement line = lineList.item(i).toElement(); float x1 = line.attribute("x1").toFloat() - m_minx; line.setAttribute("x1", x1); float x2 = line.attribute("x2").toFloat() - m_minx; line.setAttribute("x2", x2); float y1 = line.attribute("y1").toFloat() - m_miny; line.setAttribute("y1", y1); float y2 = line.attribute("y2").toFloat() - m_miny; line.setAttribute("y2", y2); } // circles QDomNodeList circleList = m_svgroot.elementsByTagName("circle"); for(uint i = 0; i < circleList.length(); i++){ QDomElement circle = circleList.item(i).toElement(); float cx = circle.attribute("cx").toFloat() - m_minx; circle.setAttribute("cx",cx); float cy = circle.attribute("cy").toFloat() - m_miny; circle.setAttribute("cy",cy); } // rects QDomNodeList rectList = m_svgroot.elementsByTagName("rect"); for(uint i = 0; i < rectList.length(); i++){ QDomElement rect = rectList.item(i).toElement(); float x = rect.attribute("x").toFloat() - m_minx; rect.setAttribute("x", x); float y = rect.attribute("y").toFloat() - m_miny; rect.setAttribute("y", y); } //TODO: arcs return; } fritzing-app-0.9.3b/src/partseditor/obsolete/pcbxml.h000066400000000000000000000034771272363601300227160ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PCBXML_H #define PCBXML_H // #include #include #include "svgdomdocument.h" // class PcbXML { public: PcbXML(const QDomElement & pcbDocument); QString getSvgFile(); private: SVGDomDocument * m_svg; QString m_svgFile; QDomElement m_svgroot; QDomElement m_silkscreen; QDomElement m_copper; QDomElement m_keepout; QDomElement m_mask; QDomElement m_outline; int m_markx; int m_marky; int m_minx; int m_miny; int m_maxx; int m_maxy; int m_pinCount; int m_padCount; QString m_units; // length units for the root element coordinates void drawNode(QDomNode node); void drawPin(QDomNode node); void drawPad(QDomNode node); void drawElementLine(QDomNode node); void drawElementArc(QDomNode node); void drawMark(QDomNode node); void minMax(int, int, int); void shiftCoordinates(); }; #endif fritzing-app-0.9.3b/src/partseditor/obsolete/resizablerectitem.cpp000066400000000000000000000024171272363601300254720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "resizablerectitem.h" ResizableRectItem::ResizableRectItem() { m_resizable = false; // not resizable by default m_handlers = NULL; } void ResizableRectItem::setResizable(bool resizable) { m_resizable = resizable; } bool ResizableRectItem::isResizable() { return m_resizable; } fritzing-app-0.9.3b/src/partseditor/obsolete/resizablerectitem.h000066400000000000000000000030601272363601300251320ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef RESIZABLERECTITEM_H_ #define RESIZABLERECTITEM_H_ #include class ConnectorRectangle; class ResizableRectItem { friend class ConnectorRectangle; public: ResizableRectItem(); virtual ~ResizableRectItem() {} virtual qreal minWidth() = 0; virtual qreal minHeight() = 0; virtual void resizeRect(qreal x, qreal y, qreal width, qreal height) = 0; virtual void doPrepareGeometryChange() = 0; void setResizable(bool resizable); bool isResizable(); protected: ConnectorRectangle *m_handlers; bool m_resizable; }; #endif /* RESIZABLERECTITEM_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/simpleeditablelabelwidget.cpp000066400000000000000000000064561272363601300271530ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "simpleeditablelabelwidget.h" #include "../debugdialog.h" #include #include SimpleEditableLabelWidget::SimpleEditableLabelWidget(QUndoStack *undoStack, QWidget *parent, const QString &text, bool edited) : QFrame(parent) { setObjectName("partsBinTitle"); m_label = new QLabel(this); m_lineEdit = new QLineEdit(this); connect(m_lineEdit,SIGNAL(editingFinished()),this,SLOT(toStandardMode())); QHBoxLayout *lo = new QHBoxLayout(this); lo->setMargin(3); lo->setSpacing(0); m_hasBeenEdited = edited; m_isInEditionMode = false; m_label->setText(text); m_undoStack = undoStack; updateUndoStackIfNecessary(); toStandardMode(edited); } QString SimpleEditableLabelWidget::text() { return m_label->text(); } void SimpleEditableLabelWidget::setText(const QString &text, bool markAsEdited) { if(m_label->text() != text) { m_label->setText(text); m_hasBeenEdited = markAsEdited; updateUndoStackIfNecessary(); emit textChanged(text); } } void SimpleEditableLabelWidget::toStandardMode(bool markAsEdited) { setText(m_lineEdit->text(), markAsEdited); swapWidgets(m_label, m_lineEdit); } void SimpleEditableLabelWidget::toEditionMode() { //if(m_hasBeenEdited) { m_lineEdit->setText(m_label->text()); //} else { // Remove this part of the branch if we want the lineedit to remember what was typed the last time //m_lineEdit->setText(""); //} swapWidgets(m_lineEdit, m_label); m_lineEdit->setFocus(); } void SimpleEditableLabelWidget::swapWidgets(QWidget *toShow, QWidget *toHide) { layout()->removeWidget(toHide); toHide->hide(); layout()->addWidget(toShow); toShow->show(); m_isInEditionMode = (toShow == m_lineEdit); } void SimpleEditableLabelWidget::swapMode() { if(m_isInEditionMode) { toStandardMode(); } else { toEditionMode(); } } void SimpleEditableLabelWidget::mousePressEvent(QMouseEvent *event) { if(!m_isInEditionMode) { swapMode(); } QFrame::mousePressEvent(event); } void SimpleEditableLabelWidget::keyPressEvent(QKeyEvent *event) { if(m_isInEditionMode && event->key() == Qt::Key_Escape) { QString prevText = m_label->text(); toStandardMode(); setText(prevText); } QFrame::keyPressEvent(event); } void SimpleEditableLabelWidget::updateUndoStackIfNecessary() { if(m_hasBeenEdited) { m_undoStack->push(new QUndoCommand("Palette Widget title modified")); } } fritzing-app-0.9.3b/src/partseditor/obsolete/simpleeditablelabelwidget.h000066400000000000000000000036601272363601300266120ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SIMPLEEDITABLELABELWIDGET_H_ #define SIMPLEEDITABLELABELWIDGET_H_ #include #include #include #include class SimpleEditableLabelWidget : public QFrame { Q_OBJECT public: SimpleEditableLabelWidget(QUndoStack *undoStack, QWidget *parent=0, const QString &text = "", bool edited=false); void setText(const QString &text, bool markAsEdited = true); QString text(); protected slots: void toStandardMode(bool markAsEdited = true); void toEditionMode(); signals: void textChanged(const QString& text); protected: void swapWidgets(QWidget *toShow, QWidget *toHide); void swapMode(); void mousePressEvent(QMouseEvent *event); void keyPressEvent(QKeyEvent *event); void updateUndoStackIfNecessary(); protected: QLabel *m_label; QLineEdit *m_lineEdit; QUndoStack *m_undoStack; bool m_hasBeenEdited; volatile bool m_isInEditionMode; }; #endif /* SIMPLEEDITABLELABELWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/singleconnectorinfowidget.cpp000066400000000000000000000257011272363601300272320ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "connectorsinfowidget.h" #include "singleconnectorinfowidget.h" #include "connectorinforemovebutton.h" #include "../connectors/connectorshared.h" #include "../debugdialog.h" static const QString PadString = "Pad"; ConnectorTypeWidget::ConnectorTypeWidget(Connector::ConnectorType type, QWidget *parent) : QFrame(parent) { m_isSelected = false; m_noEditionModeWidget = new QLabel(this); m_editionModeWidget = new QPushButton(this); connect( m_editionModeWidget, SIGNAL(clicked()), this, SLOT(toggleValue()) ); m_noEditionModeWidget->setFixedWidth(30); m_editionModeWidget->setFixedWidth(30); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(2); layout->setSpacing(0); layout->addWidget(m_noEditionModeWidget); layout->addWidget(m_editionModeWidget); setType(type); } Connector::ConnectorType ConnectorTypeWidget::type() { if(text()==FemaleSymbolString) { return Connector::connectorTypeFromName("female"); } else if(text()==MaleSymbolString) { return Connector::connectorTypeFromName("male"); } else if (text()==PadString) { return Connector::connectorTypeFromName("pad"); } return Connector::Unknown; } const QString &ConnectorTypeWidget::typeAsStr() { return Connector::connectorNameFromType(type()); } void ConnectorTypeWidget::setType(Connector::ConnectorType type) { if(m_isInEditionMode) { m_typeBackUp = this->type(); } if(type == Connector::Female) { setText(FemaleSymbolString); } else if(type == Connector::Male) { setText(MaleSymbolString); } else if(type == Connector::Pad) { setText(PadString); } setToolTip(Connector::connectorNameFromType(type)); } void ConnectorTypeWidget::setSelected(bool selected) { m_isSelected = selected; } void ConnectorTypeWidget::setText(const QString &text) { m_text = text; m_noEditionModeWidget->setText(text); m_editionModeWidget->setText(text); } const QString &ConnectorTypeWidget::text() { return m_text; } void ConnectorTypeWidget::setInEditionMode(bool edition) { m_isInEditionMode = edition; m_noEditionModeWidget->setVisible(!edition); m_editionModeWidget->setVisible(edition); } void ConnectorTypeWidget::cancel() { setType(m_typeBackUp); } void ConnectorTypeWidget::toggleValue() { if(m_isSelected && m_isInEditionMode) { if(type() == Connector::Female) { setType(Connector::Pad); } else if(type() == Connector::Male) { setType(Connector::Female); } else if (type() == Connector::Pad) { setType(Connector::Male); } } } SingleConnectorInfoWidget::SingleConnectorInfoWidget(ConnectorsInfoWidget *topLevelContainer, WaitPushUndoStack *undoStack, Connector* connector, QWidget *parent) : AbstractConnectorInfoWidget(topLevelContainer,parent) { static QString EMPTY_CONN_NAME = QObject::tr("no name yet"); static QString EMPTY_CONN_DESC = QObject::tr("no description yet"); static Connector::ConnectorType EMPTY_CONN_TYPE = Connector::Male; QString name; QString description; Connector::ConnectorType type; m_undoStack = undoStack; m_connector = connector; if(connector && connector->connectorShared()) { m_id = connector->connectorSharedID(); name = connector->connectorSharedName(); if (name.isEmpty()) name = EMPTY_CONN_NAME; description = connector->connectorSharedDescription(); if (description.isEmpty()) description = EMPTY_CONN_DESC; type = connector->connectorType(); if (type == Connector::Unknown) type = EMPTY_CONN_TYPE; } else { name = EMPTY_CONN_NAME; description = EMPTY_CONN_DESC; type = EMPTY_CONN_TYPE; } m_nameLabel = new QLabel(name,this); m_nameDescSeparator = new QLabel(" - ",this); m_descLabel = new QLabel(description,this); m_descLabel->setObjectName("description"); m_type = new ConnectorTypeWidget(type, this); m_nameEdit = NULL; m_descEdit = NULL; m_nameEditContainer = new QFrame(this); QHBoxLayout *nameLO = new QHBoxLayout(m_nameEditContainer); nameLO->setSpacing(0); nameLO->setMargin(0); m_nameEditContainer->hide(); m_descEditContainer = new QFrame(this); QVBoxLayout *descLO = new QVBoxLayout(m_descEditContainer); descLO->setSpacing(0); descLO->setMargin(0); m_descEditContainer->hide(); m_acceptButton = NULL; m_cancelButton = NULL; setSelected(false); QLayout *layout = new QVBoxLayout(this); layout->setMargin(1); toStandardMode(); } QString SingleConnectorInfoWidget::id() { if (m_connector) { return m_connector->connectorSharedID(); } return m_id; } QString SingleConnectorInfoWidget::name() { return m_nameLabel->text(); } QString SingleConnectorInfoWidget::description() { return m_descLabel->text(); } QString SingleConnectorInfoWidget::type() { return m_type->typeAsStr(); } void SingleConnectorInfoWidget::startEdition() { createInputs(); m_nameEdit->setText(m_nameLabel->text()); m_descEdit->setText(m_descLabel->text()); toEditionMode(); emit editionStarted(); } void SingleConnectorInfoWidget::createInputs() { if(!m_nameEdit) { m_nameEdit = new QLineEdit(this); m_nameEditContainer->layout()->addWidget(new QLabel(tr("Name: "),this)); m_nameEditContainer->layout()->addWidget(m_nameEdit); } if(!m_descEdit) { m_descEdit = new QTextEdit(this); m_descEditContainer->layout()->addWidget(new QLabel(tr("Description:"),this)); m_descEditContainer->layout()->addWidget(m_descEdit); } } void SingleConnectorInfoWidget::editionCompleted() { if(m_isInEditionMode) { m_undoStack->push(new QUndoCommand("Dummy parts editor command")); m_nameLabel->setText(m_nameEdit->text()); m_descLabel->setText(m_descEdit->toPlainText()); toStandardMode(); emit editionFinished(); } } void SingleConnectorInfoWidget::editionCanceled() { m_type->cancel(); toStandardMode(); emit editionFinished(); } void SingleConnectorInfoWidget::toStandardMode() { hide(); setInEditionMode(false); hideIfNeeded(m_nameEditContainer); hideIfNeeded(m_descEditContainer); hideIfNeeded(m_acceptButton); hideIfNeeded(m_cancelButton); QHBoxLayout *hbLayout = new QHBoxLayout(); hbLayout->addWidget(m_type); hbLayout->addSpacerItem(new QSpacerItem(10,0)); hbLayout->addWidget(m_nameLabel); m_nameLabel->show(); hbLayout->addWidget(m_nameDescSeparator); m_nameDescSeparator->show(); hbLayout->addWidget(m_descLabel); m_descLabel->show(); hbLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding)); hbLayout->addWidget(m_removeButton); QVBoxLayout *layout = (QVBoxLayout*)this->layout(); layout->addLayout(hbLayout); setFixedHeight(SingleConnectorHeight); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); updateGeometry(); show(); setFocus(); } void SingleConnectorInfoWidget::toEditionMode() { hide(); setInEditionMode(true); hideIfNeeded(m_nameLabel); hideIfNeeded(m_nameDescSeparator); hideIfNeeded(m_descLabel); createInputs(); // first row QHBoxLayout *firstRowLayout = new QHBoxLayout(); firstRowLayout->addWidget(m_type); firstRowLayout->addSpacerItem(new QSpacerItem(10,0)); firstRowLayout->addWidget(m_nameEditContainer); firstRowLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding)); firstRowLayout->addWidget(m_removeButton); m_nameEditContainer->show(); // second row m_descEditContainer->show(); // third row if(!m_acceptButton) { m_acceptButton = new QPushButton(QObject::tr("Accept"),this); connect(m_acceptButton,SIGNAL(clicked()),this,SLOT(editionCompleted())); } m_acceptButton->show(); if(!m_cancelButton) { m_cancelButton = new QPushButton(QObject::tr("Cancel"),this); connect(m_cancelButton,SIGNAL(clicked()),this,SLOT(editionCanceled())); } m_cancelButton->show(); QHBoxLayout *thirdRowLayout = new QHBoxLayout(); thirdRowLayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding)); thirdRowLayout->addWidget(m_acceptButton); thirdRowLayout->addWidget(m_cancelButton); QVBoxLayout *layout = (QVBoxLayout*)this->layout(); layout->addLayout(firstRowLayout); layout->addWidget(m_descEditContainer); layout->addLayout(thirdRowLayout); setFixedHeight(SingleConnectorHeight*4); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); updateGeometry(); show(); setFocus(); emit editionStarted(); } void SingleConnectorInfoWidget::hideIfNeeded(QWidget* w) { if(w) { w->hide(); layout()->removeWidget(w); } } QSize SingleConnectorInfoWidget::maximumSizeHint() const { return sizeHint(); } QSize SingleConnectorInfoWidget::minimumSizeHint() const { return sizeHint(); } QSize SingleConnectorInfoWidget::sizeHint() const { QSize retval; if(m_isInEditionMode) { retval = QSize(width(),SingleConnectorHeight*4); } else { retval = QSize(width(),SingleConnectorHeight); } return retval; } void SingleConnectorInfoWidget::setSelected(bool selected, bool doEmitChange) { AbstractConnectorInfoWidget::setSelected(selected, doEmitChange); m_type->setSelected(selected); if(selected && m_connector) { emit tellViewsMyConnectorIsNewSelected(m_connector->connectorSharedID()); } } void SingleConnectorInfoWidget::setInEditionMode(bool inEditionMode) { m_isInEditionMode = inEditionMode; m_type->setInEditionMode(inEditionMode); } bool SingleConnectorInfoWidget::isInEditionMode() { return m_isInEditionMode; } void SingleConnectorInfoWidget::mousePressEvent(QMouseEvent * event) { if(isSelected() && !isInEditionMode()) { startEdition(); } else if(!isSelected()) { setSelected(true); } QFrame::mousePressEvent(event); } Connector *SingleConnectorInfoWidget::connector() { return m_connector; } MismatchingConnectorWidget *SingleConnectorInfoWidget::toMismatching(ViewLayer::ViewIdentifier missingViewId) { MismatchingConnectorWidget *mcw = new MismatchingConnectorWidget(m_topLevelContainer,missingViewId, m_connector->connectorSharedID(), (QWidget*)parent(), false, m_connector); return mcw; } Connector::ConnectorType SingleConnectorInfoWidget::connectorType() { if (m_type == NULL) return Connector::Unknown; return m_type->type(); } void SingleConnectorInfoWidget::setConnectorType(Connector::ConnectorType type) { if (m_type) { m_type->setType(type); } } void SingleConnectorInfoWidget::setName(const QString & name) { m_nameLabel->setText(name); if (m_nameEdit) { m_nameEdit->setText(name); } } fritzing-app-0.9.3b/src/partseditor/obsolete/singleconnectorinfowidget.h000066400000000000000000000067251272363601300267040ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SINGLECONNECTORINFOWIDGET_H_ #define SINGLECONNECTORINFOWIDGET_H_ #include "../connectors/connector.h" #include "abstractconnectorinfowidget.h" #include "editablelinewidget.h" #include "editabletextwidget.h" #include "mismatchingconnectorwidget.h" class ConnectorTypeWidget : public QFrame { Q_OBJECT public: ConnectorTypeWidget(Connector::ConnectorType type = Connector::Female, QWidget *parent=0); Connector::ConnectorType type(); const QString &typeAsStr(); void setType(Connector::ConnectorType type); void setSelected(bool selected); void setInEditionMode(bool edition); void cancel(); protected slots: void toggleValue(); protected: void setText(const QString &text); const QString &text(); QLabel *m_noEditionModeWidget; QPushButton *m_editionModeWidget; QString m_text; bool m_isSelected; volatile bool m_isInEditionMode; Connector::ConnectorType m_typeBackUp; }; class SingleConnectorInfoWidget : public AbstractConnectorInfoWidget { Q_OBJECT public: SingleConnectorInfoWidget(ConnectorsInfoWidget *topLevelContainer, WaitPushUndoStack *undoStack, Connector* connector=0, QWidget *parent=0); void setSelected(bool selected, bool doEmitChange=true); void setInEditionMode(bool inEditionMode); bool isInEditionMode(); QSize sizeHint() const; QSize minimumSizeHint() const; QSize maximumSizeHint() const; Connector * connector(); QString id(); QString name(); void setName(const QString &); QString description(); QString type(); Connector::ConnectorType connectorType(); void setConnectorType(Connector::ConnectorType); MismatchingConnectorWidget *toMismatching(ViewLayer::ViewIdentifier viewId); protected slots: void editionCompleted(); void editionCanceled(); signals: void editionStarted(); void editionFinished(); //void connectorSelected(const QString&); protected: void toStandardMode(); void toEditionMode(); void startEdition(); void createInputs(); void mousePressEvent(QMouseEvent * event); void hideIfNeeded(QWidget* w); //QFrame *noEditFrame; QLabel *m_nameLabel; QLabel *m_nameDescSeparator; QLabel *m_descLabel; ConnectorTypeWidget *m_type; QFrame *m_nameEditContainer; QLineEdit *m_nameEdit; QFrame *m_descEditContainer; QTextEdit *m_descEdit; QPushButton *m_acceptButton; QPushButton *m_cancelButton; WaitPushUndoStack *m_undoStack; QPointer m_connector; QString m_id; volatile bool m_isInEditionMode; }; #endif /* SINGLECONNECTORINFOWIDGET_H_ */ fritzing-app-0.9.3b/src/partseditor/obsolete/svgdomdocument.cpp000066400000000000000000000064271272363601300250200ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "svgdomdocument.h" SVGDomDocument::SVGDomDocument( ) : QDomDocument() { QDomElement svgroot = createElement("svg"); svgroot.setAttribute("xmlns","http://www.w3.org/2000/svg"); svgroot.setAttribute("version","1.2"); svgroot.setAttribute("baseProfile","tiny"); //svgroot.setAttribute("viewBox","0 0 400 400"); //TODO: set dynamically appendChild(svgroot); QDomElement desc = createElement("desc"); QDomText descText = createTextNode("Fritzing footprint SVG"); desc.appendChild(descText); svgroot.appendChild(desc); } void SVGDomDocument::setWidth(int width, QString units){ QDomElement svgroot = documentElement(); float scaled = 1; if(units == "mils"){ scaled = qreal(width)/1000.0; units = "in"; } if(units == "cmil"){ scaled = qreal(width)/100000.0; units = "in"; } QString strwidth = QString::number(scaled) + units; svgroot.setAttribute("width",strwidth); } void SVGDomDocument::setHeight(int height, QString units){ QDomElement svgroot = documentElement(); float scaled = 1; if(units == "mils"){ scaled = qreal(height)/1000.0; units = "in"; } if(units == "cmil"){ scaled = qreal(height)/100000.0; units = "in"; } QString strheight = QString::number(scaled) + units; svgroot.setAttribute("height",strheight); } void SVGDomDocument::setViewBox(int minx, int miny, int maxx, int maxy){ QDomElement svgroot = documentElement(); QString viewBox = QString::number(minx) + " " + QString::number(miny) + " " + QString::number(maxx) + " " + QString::number(maxy); svgroot.setAttribute("viewBox", viewBox); } void SVGDomDocument::save(QString fileName){ QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(NULL, QObject::tr("Parts Editor"), QObject::tr("Cannot write to file %1:\n%2.") .arg(fileName) .arg(file.errorString())); } QTextStream out(&file); // This is kinda naughty but QDom seem to have no other way set the header! QString xmlDeclaration = "\n"; out << xmlDeclaration << toString(); file.close(); } QDomElement SVGDomDocument::createGroup(QString id){ QDomElement group = createElement("g"); group.setAttribute("id", id); documentElement().appendChild(group); return group; } fritzing-app-0.9.3b/src/partseditor/obsolete/svgdomdocument.h000066400000000000000000000025011272363601300244520ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SVGDOMDOCUMENT_H #define SVGDOMDOCUMENT_H // #include #include // class SVGDomDocument : public QDomDocument { public: SVGDomDocument(); QDomElement createGroup(QString id); void setHeight(int, QString); void setWidth(int, QString); void setViewBox(int, int, int, int); void save(QString fileName); }; #endif fritzing-app-0.9.3b/src/partseditor/obsolete/svgview.cpp000066400000000000000000000142431272363601300234470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "svgview.h" #include // SVGView::SVGView(const QString &name, QWidget *parent) : QFrame(parent) { setFrameStyle(Sunken | StyledPanel); m_graphicsView = new QGraphicsView; m_graphicsView->setRenderHint(QPainter::Antialiasing, true); m_graphicsView->setDragMode(QGraphicsView::RubberBandDrag); m_graphicsView->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate); int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize); QSize iconSize(size, size); m_domDocument = new QDomDocument; m_zoom = 1; m_rotation = 0; QToolButton *zoomInIcon = new QToolButton; zoomInIcon->setAutoRepeat(true); zoomInIcon->setAutoRepeatInterval(33); zoomInIcon->setAutoRepeatDelay(0); zoomInIcon->setIcon(QPixmap(":/zoomin.png")); zoomInIcon->setIconSize(iconSize); QToolButton *zoomOutIcon = new QToolButton; zoomOutIcon->setAutoRepeat(true); zoomOutIcon->setAutoRepeatInterval(33); zoomOutIcon->setAutoRepeatDelay(0); zoomOutIcon->setIcon(QPixmap(":/zoomout.png")); zoomOutIcon->setIconSize(iconSize); QToolButton *rotateLeftIcon = new QToolButton; rotateLeftIcon->setIcon(QPixmap(":/rotateleft.png")); rotateLeftIcon->setIconSize(iconSize); QToolButton *rotateRightIcon = new QToolButton; rotateRightIcon->setIcon(QPixmap(":/rotateright.png")); rotateRightIcon->setIconSize(iconSize); // Label layout QHBoxLayout *labelLayout = new QHBoxLayout; m_label = new QLabel(name); //TODO: put proper icons in here m_printButton = new QToolButton; m_printButton->setIcon(QIcon(QPixmap(":/resources/images/document-print.png"))); m_printButton->setText(tr("Print")); m_printButton->setToolTip(tr("Print")); m_loadPCBXMLButton = new QToolButton; m_loadPCBXMLButton->setIcon(QIcon(QPixmap(":/resources/images/applications-accessories.png"))); m_loadPCBXMLButton->setText(tr("Import XML")); m_loadPCBXMLButton->setToolTip(tr("Import XML")); labelLayout->addWidget(m_label); labelLayout->addStretch(); labelLayout->addWidget(m_printButton); labelLayout->addWidget(m_loadPCBXMLButton); QGridLayout *topLayout = new QGridLayout; topLayout->addLayout(labelLayout, 0, 0); topLayout->addWidget(m_graphicsView, 1, 0); setLayout(topLayout); connect(rotateLeftIcon, SIGNAL(clicked()), this, SLOT(rotateLeft())); connect(rotateRightIcon, SIGNAL(clicked()), this, SLOT(rotateRight())); connect(zoomInIcon, SIGNAL(clicked()), this, SLOT(zoomIn())); connect(zoomOutIcon, SIGNAL(clicked()), this, SLOT(zoomOut())); connect(m_printButton, SIGNAL(clicked()), this, SLOT(print())); connect(m_loadPCBXMLButton, SIGNAL(clicked()), this, SLOT(importPCBXML())); setupMatrix(); } // void SVGView::importPCBXML(){ QString path = QFileDialog::getOpenFileName(this, tr("Select Footprint XML File"), "", tr("Fritzing Footprint XML Files (*.fzfp);;All Files (*)")); QFile file(path); if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Cannot read file %1:\n%2.") .arg(path) .arg(file.errorString())); } QString errorStr; int errorLine; int errorColumn; if (!m_domDocument->setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { QMessageBox::critical(NULL, QObject::tr("Fritzing"), QObject::tr("Parse error (3) at line %1, column %2:\n%3\n%4") .arg(errorLine) .arg(errorColumn) .arg(errorStr) .arg(path)); return; } QDomElement root = m_domDocument->documentElement(); if (root.isNull()) { QMessageBox::critical(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing file (12).").arg(path)); return; } if (root.tagName().toLower() != "element") { QMessageBox::critical(NULL, QObject::tr("Fritzing"), QObject::tr("The file %1 is not a Fritzing Footprint XML file.").arg(path)); return; } drawPCBXML(&root); } void SVGView::drawPCBXML(QDomElement * rootElement) { m_pcbXML = new PcbXML(*rootElement); m_pcbWidget = new QSvgWidget(m_pcbXML->getSvgFile()); m_scene.addWidget(m_pcbWidget); m_graphicsView->setScene(&m_scene); m_graphicsView->show(); //connect(pcbWidget, SIGNAL(repaintNeeded()), this, SLOT(pcbWidget->update())); } void SVGView::setupMatrix(){ // TODO: add support for scaling and rotation QMatrix matrix; matrix.scale(qreal(1), qreal(1)); matrix.rotate(m_rotation); m_graphicsView->setMatrix(matrix); } QGraphicsView *SVGView::view() const { return m_graphicsView; } void SVGView::print() { #ifndef QT_NO_PRINTER QPrinter printer; QPrintDialog dialog(&printer, this); if (dialog.exec() == QDialog::Accepted) { QPainter painter(&printer); m_graphicsView->render(&painter); } #endif } void SVGView::zoomIn() { m_zoom++; //zoomSlider->setValue(zoomSlider->value() + 1); } void SVGView::zoomOut() { m_zoom--; //zoomSlider->setValue(zoomSlider->value() - 1); } void SVGView::rotateRight(){ } void SVGView::rotateLeft(){ } fritzing-app-0.9.3b/src/partseditor/obsolete/svgview.h000066400000000000000000000040131272363601300231060ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SVGVIEW_H #define SVGVIEW_H // #include #include #include #include #include #include "pcbxml.h" QT_FORWARD_DECLARE_CLASS(QGraphicsView) QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QSlider) QT_FORWARD_DECLARE_CLASS(QToolButton) class SVGView : public QFrame { Q_OBJECT public: SVGView(const QString &name, QWidget *parent = 0); QGraphicsView *view() const; private slots: void print(); void importPCBXML(); void zoomIn(); void zoomOut(); void rotateLeft(); void rotateRight(); private: QGraphicsView *m_graphicsView; QLabel *m_label; QToolButton *m_printButton; QToolButton *m_loadPCBXMLButton; QSvgRenderer *m_renderer; QSvgWidget *m_pcbWidget; PcbXML *m_pcbXML; QGraphicsScene m_scene; QDomDocument *m_domDocument; // footprint xml file // graphics layers (svg groups really) qreal m_zoom; qreal m_rotation; void setupMatrix(); void drawPCBXML(QDomElement * pcbDocument); }; #endif fritzing-app-0.9.3b/src/partseditor/pecommands.cpp000066400000000000000000000261631272363601300222730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "pecommands.h" #include "pemainwindow.h" #include "../debugdialog.h" ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// PEBaseCommand::PEBaseCommand(PEMainWindow * peMainWindow, QUndoCommand *parent) : BaseCommand(BaseCommand::CrossView, NULL, parent) { m_peMainWindow = peMainWindow; } PEBaseCommand::~PEBaseCommand() { } QString PEBaseCommand::getParamString() const { return ""; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeMetadataCommand::ChangeMetadataCommand(PEMainWindow * peMainWindow, const QString & name, const QString & oldValue, const QString & newValue, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_name = name; m_oldValue = oldValue; m_newValue = newValue; } void ChangeMetadataCommand::undo() { m_peMainWindow->changeMetadata(m_name, m_oldValue, true); } void ChangeMetadataCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else { m_peMainWindow->changeMetadata(m_name, m_newValue, true); } } QString ChangeMetadataCommand::getParamString() const { return "ChangeMetadataCommand " + QString(" name:%1, old:%2, new:%3") .arg(m_name) .arg(m_oldValue) .arg(m_newValue) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeTagsCommand::ChangeTagsCommand(PEMainWindow * peMainWindow, const QStringList & oldTags, const QStringList & newTags, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_old = oldTags; m_new = newTags; } void ChangeTagsCommand::undo() { m_peMainWindow->changeTags(m_old, true); } void ChangeTagsCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else { m_peMainWindow->changeTags(m_new, true); } } QString ChangeTagsCommand::getParamString() const { return "ChangeTagsCommand " + QString(" old:%1, new:%2") .arg(m_old.count()) .arg(m_new.count()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangePropertiesCommand::ChangePropertiesCommand(PEMainWindow * peMainWindow, const QHash & oldProps, const QHash & newProps, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_old = oldProps; m_new = newProps; } void ChangePropertiesCommand::undo() { m_peMainWindow->changeProperties(m_old, true); } void ChangePropertiesCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else { m_peMainWindow->changeProperties(m_new, true); } } QString ChangePropertiesCommand::getParamString() const { return "ChangePropertiesCommand " + QString(" old:%1, new:%2") .arg(m_old.count()) .arg(m_new.count()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeConnectorMetadataCommand::ChangeConnectorMetadataCommand(PEMainWindow * peMainWindow, ConnectorMetadata * oldValue, ConnectorMetadata * newValue, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_oldcm = *oldValue; m_newcm = *newValue; } void ChangeConnectorMetadataCommand::undo() { m_peMainWindow->changeConnectorMetadata(&m_oldcm, true); } void ChangeConnectorMetadataCommand::redo() { if (m_skipFirstRedo) { m_skipFirstRedo = false; } else { m_peMainWindow->changeConnectorMetadata(&m_newcm, true); } } QString ChangeConnectorMetadataCommand::getParamString() const { return "ChangeConnectorMetadataCommand " + QString(" name:%1, old:%2, new:%3") .arg(m_oldcm.connectorName) .arg(m_newcm.connectorName) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeFzpCommand::ChangeFzpCommand(PEMainWindow * peMainWindow, const QString & oldFzpFile, const QString & newFzpFile, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_oldFzpFile = oldFzpFile; m_newFzpFile = newFzpFile; } void ChangeFzpCommand::undo() { if (!m_redoOnly) { m_peMainWindow->restoreFzp(m_oldFzpFile); } } void ChangeFzpCommand::redo() { if (!m_undoOnly) { m_peMainWindow->restoreFzp(m_newFzpFile); } } QString ChangeFzpCommand::getParamString() const { return "ChangeFzpCommand " + QString(" old:%1 new:%2") .arg(m_oldFzpFile) .arg(m_newFzpFile) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeSvgCommand::ChangeSvgCommand(PEMainWindow * peMainWindow, SketchWidget * sketchWidget, const QString & oldFilename, const QString & newFilename, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_sketchWidget = sketchWidget; m_oldFilename = oldFilename; m_newFilename = newFilename; } void ChangeSvgCommand::undo() { m_peMainWindow->changeSvg(m_sketchWidget, m_oldFilename, -1); } void ChangeSvgCommand::redo() { m_peMainWindow->changeSvg(m_sketchWidget, m_newFilename, 1); } QString ChangeSvgCommand::getParamString() const { return "ChangeSvgCommand " + QString(" id:%1, old:%2, new:%3") .arg(m_sketchWidget->viewID()) .arg(m_oldFilename) .arg(m_newFilename) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// RelocateConnectorSvgCommand::RelocateConnectorSvgCommand(PEMainWindow * peMainWindow, SketchWidget * sketchWidget, const QString & id, const QString & terminalID, const QString & oldGorn, const QString & oldGornTerminal, const QString & newGorn, const QString & newGornTerminal, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_sketchWidget = sketchWidget; m_id = id; m_terminalID = terminalID; m_oldGorn = oldGorn; m_oldGornTerminal = oldGornTerminal; m_newGorn = newGorn; m_newGornTerminal = newGornTerminal; } void RelocateConnectorSvgCommand::undo() { m_peMainWindow->relocateConnectorSvg(m_sketchWidget, m_id, m_terminalID, m_newGorn, m_newGornTerminal, m_oldGorn, m_oldGornTerminal, -1); } void RelocateConnectorSvgCommand::redo() { m_peMainWindow->relocateConnectorSvg(m_sketchWidget, m_id, m_terminalID, m_oldGorn, m_oldGornTerminal, m_newGorn, m_newGornTerminal, 1); } QString RelocateConnectorSvgCommand::getParamString() const { return "RelocateConnectorSvgCommand " + QString(" vid:%1 id:%2, terminalid:%3, oldgorn:%4, oldgornterminal:%5, newgorn:%6, newgornterminal:%7") .arg(m_sketchWidget->viewID()) .arg(m_id) .arg(m_terminalID) .arg(m_oldGorn) .arg(m_oldGornTerminal) .arg(m_newGorn) .arg(m_newGornTerminal) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// MoveTerminalPointCommand::MoveTerminalPointCommand(PEMainWindow * peMainWindow, SketchWidget * sketchWidget, const QString & id, QSizeF size, QPointF oldLocation, QPointF newLocation, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_sketchWidget = sketchWidget; m_id = id; m_size = size; m_oldLocation = oldLocation; m_newLocation = newLocation; } void MoveTerminalPointCommand::undo() { m_peMainWindow->moveTerminalPoint(m_sketchWidget, m_id, m_size, m_oldLocation, -1); } void MoveTerminalPointCommand::redo() { m_peMainWindow->moveTerminalPoint(m_sketchWidget, m_id, m_size, m_newLocation, 1); } QString MoveTerminalPointCommand::getParamString() const { return "RelocateConnectorSvgCommand " + QString(" vid:%1, id:%2, old:%3,%4, new:%5,%6") .arg(m_sketchWidget->viewID()) .arg(m_id) .arg(m_oldLocation.x()) .arg(m_oldLocation.y()) .arg(m_newLocation.x()) .arg(m_newLocation.y()) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// RemoveBusConnectorCommand::RemoveBusConnectorCommand(PEMainWindow * peMainWindow, const QString & busID, const QString & connectorID, bool inverted, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_busID = busID; m_connectorID = connectorID; m_inverted = inverted; } void RemoveBusConnectorCommand::undo() { if (m_inverted) m_peMainWindow->removeBusConnector(m_busID, m_connectorID, true); else m_peMainWindow->addBusConnector(m_busID, m_connectorID); } void RemoveBusConnectorCommand::redo() { if (m_inverted) m_peMainWindow->addBusConnector(m_busID, m_connectorID); else m_peMainWindow->removeBusConnector(m_busID, m_connectorID, true); } QString RemoveBusConnectorCommand::getParamString() const { return "RemoveBusConnectorCommand " + QString(" busID:%1, connectorID:%2 inv:%3") .arg(m_busID) .arg(m_connectorID) .arg(m_inverted); ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// ChangeSMDCommand::ChangeSMDCommand(PEMainWindow * peMainWindow, const QString & before, const QString & after, const QString & oldFilename, const QString & newFilename, QUndoCommand *parent) : PEBaseCommand(peMainWindow, parent) { m_before = before; m_after = after; m_oldFilename = oldFilename; m_newFilename = newFilename; } void ChangeSMDCommand::undo() { m_peMainWindow->changeSMD(m_before, m_oldFilename, -1); } void ChangeSMDCommand::redo() { m_peMainWindow->changeSMD(m_after, m_newFilename, 1); } QString ChangeSMDCommand::getParamString() const { return "ChangeSMDCommand " + QString(" before:%1, after:%2 oldpath:%3, newPath:%4, oldorig:%5, neworig:%6") .arg(m_before) .arg(m_after) .arg(m_oldFilename) .arg(m_newFilename) ; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// fritzing-app-0.9.3b/src/partseditor/pecommands.h000066400000000000000000000130061272363601300217300ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify\ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6570 $: $Author: irascibl@gmail.com $: $Date: 2012-10-16 06:25:29 +0200 (Di, 16. Okt 2012) $ ********************************************************************/ #ifndef PECOMMANDS_H #define PECOMMANDS_H #include "../commands.h" #include "peutils.h" ///////////////////////////////////////////// class PEBaseCommand : public BaseCommand { public: PEBaseCommand(class PEMainWindow *, QUndoCommand* parent); ~PEBaseCommand(); QString getParamString() const; protected: class PEMainWindow * m_peMainWindow; }; ///////////////////////////////////////////// class ChangeMetadataCommand : public PEBaseCommand { public: ChangeMetadataCommand(class PEMainWindow *, const QString & name, const QString & oldValue, const QString & newValue, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_name; QString m_oldValue; QString m_newValue; }; ///////////////////////////////////////////// class ChangeConnectorMetadataCommand : public PEBaseCommand { public: ChangeConnectorMetadataCommand(class PEMainWindow *, ConnectorMetadata * oldcm, ConnectorMetadata * newcm, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: ConnectorMetadata m_oldcm; ConnectorMetadata m_newcm; }; ///////////////////////////////////////////// class ChangeFzpCommand : public PEBaseCommand { public: ChangeFzpCommand(class PEMainWindow *, const QString & oldFzpFile, const QString & newFzpFile, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_oldFzpFile; QString m_newFzpFile; }; ///////////////////////////////////////////// class ChangeTagsCommand : public PEBaseCommand { public: ChangeTagsCommand(class PEMainWindow *, const QStringList & oldTabs, const QStringList & newTags, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QStringList m_old; QStringList m_new; }; ///////////////////////////////////////////// class ChangePropertiesCommand : public PEBaseCommand { public: ChangePropertiesCommand(class PEMainWindow *, const QHash & oldProps, const QHash & newProps, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QHash m_old; QHash m_new; }; ///////////////////////////////////////////// class ChangeSvgCommand : public PEBaseCommand { public: ChangeSvgCommand(class PEMainWindow *, SketchWidget *, const QString & oldFilename, const QString & newFilename, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_oldFilename; QString m_newFilename; }; ///////////////////////////////////////////// class RelocateConnectorSvgCommand : public PEBaseCommand { public: RelocateConnectorSvgCommand(class PEMainWindow *, SketchWidget *, const QString & id, const QString & terminalID, const QString & oldGorn, const QString & oldGornTerminal, const QString & newGorn, const QString & newGornTerminal, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_id; QString m_terminalID; QString m_oldGorn; QString m_oldGornTerminal; QString m_newGorn; QString m_newGornTerminal; }; ///////////////////////////////////////////// class MoveTerminalPointCommand : public PEBaseCommand { public: MoveTerminalPointCommand(class PEMainWindow *, SketchWidget *, const QString & id, QSizeF size, QPointF oldLocation, QPointF newLocation, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_id; QSizeF m_size; QPointF m_oldLocation; QPointF m_newLocation; }; ///////////////////////////////////////////// class RemoveBusConnectorCommand : public PEBaseCommand { public: RemoveBusConnectorCommand(class PEMainWindow *, const QString & busID, const QString & connectorID, bool inverted, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_busID; QString m_connectorID; bool m_inverted; }; ///////////////////////////////////////////// class ChangeSMDCommand : public PEBaseCommand { public: ChangeSMDCommand(class PEMainWindow *, const QString & before, const QString & after, const QString & oldFilename, const QString & newFilename, QUndoCommand *parent); void undo(); void redo(); protected: QString getParamString() const; protected: QString m_before; QString m_after; QString m_oldFilename; QString m_newFilename; }; ///////////////////////////////////////////// #endif // PECOMMANDS_H fritzing-app-0.9.3b/src/partseditor/peconnectorsview.cpp000066400000000000000000000206241272363601300235360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include "peconnectorsview.h" #include "peutils.h" #include "hashpopulatewidget.h" #include "../debugdialog.h" ////////////////////////////////////// PEConnectorsView::PEConnectorsView(QWidget * parent) : QFrame(parent) { m_connectorCount = 0; this -> setObjectName("peConnectors"); /* QFile styleSheet(":/resources/styles/newpartseditor.qss"); if (!styleSheet.open(QIODevice::ReadOnly)) { DebugDialog::debug("Unable to open :/resources/styles/newpartseditor.qss"); } else { this->setStyleSheet(styleSheet.readAll()); } */ QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setSizeConstraint( QLayout::SetMinAndMaxSize ); QLabel *explanation = new QLabel(tr("This is where you edit the connector metadata for the part")); mainLayout->addWidget(explanation); QFrame * numberFrame = new QFrame(); QHBoxLayout * numberLayout = new QHBoxLayout(); QLabel * label = new QLabel(tr("number of connectors:")); numberLayout->addWidget(label); m_numberEdit = new QLineEdit(); QValidator *validator = new QIntValidator(1, 999, this); m_numberEdit->setValidator(validator); numberLayout->addWidget(m_numberEdit); connect(m_numberEdit, SIGNAL(editingFinished()), this, SLOT(connectorCountEntry())); numberLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); numberFrame->setLayout(numberLayout); mainLayout->addWidget(numberFrame); QFrame * typeFrame = new QFrame(); QHBoxLayout * typeLayout = new QHBoxLayout(); label = new QLabel(QObject::tr("Set all to:")); label->setObjectName("NewPartsEditorLabel"); typeLayout->addWidget(label); m_radios.clear(); QRadioButton * radioButton = new QRadioButton(MaleSymbolString); QObject::connect(radioButton, SIGNAL(clicked()), this, SLOT(allTypeEntry())); radioButton->setObjectName("NewPartsEditorRadio"); radioButton->setProperty("value", Connector::Male); typeLayout->addWidget(radioButton); m_radios.append(radioButton); radioButton = new QRadioButton(FemaleSymbolString); QObject::connect(radioButton, SIGNAL(clicked()), this, SLOT(allTypeEntry())); radioButton->setObjectName("NewPartsEditorRadio"); radioButton->setProperty("value", Connector::Female); typeLayout->addWidget(radioButton); m_radios.append(radioButton); radioButton = new QRadioButton(QObject::tr("Pad")); QObject::connect(radioButton, SIGNAL(clicked()), this, SLOT(allTypeEntry())); radioButton->setObjectName("NewPartsEditorRadio"); radioButton->setProperty("value", Connector::Pad); typeLayout->addWidget(radioButton); m_radios.append(radioButton); typeLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); typeFrame->setLayout(typeLayout); mainLayout->addWidget(typeFrame); QFrame * smdFrame = new QFrame(); QHBoxLayout * smdLayout = new QHBoxLayout(); m_tht = new QRadioButton(tr("Through-hole")); QObject::connect(m_tht, SIGNAL(clicked()), this, SLOT(smdEntry())); m_tht->setObjectName("NewPartsEditorRadio"); smdLayout->addWidget(m_tht); m_smd = new QRadioButton(tr("SMD")); QObject::connect(m_smd, SIGNAL(clicked()), this, SLOT(smdEntry())); m_smd->setObjectName("NewPartsEditorRadio"); smdLayout->addWidget(m_smd); smdLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); smdFrame->setLayout(smdLayout); mainLayout->addWidget(smdFrame); m_scrollArea = new QScrollArea; m_scrollArea->setWidgetResizable(true); m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scrollFrame = new QFrame; m_scrollArea->setWidget(m_scrollFrame); mainLayout->addWidget(m_scrollArea); this->setLayout(mainLayout); } PEConnectorsView::~PEConnectorsView() { } void PEConnectorsView::initConnectors(QList * connectorList) { QWidget * widget = QApplication::focusWidget(); if (widget) { QList children = m_scrollFrame->findChildren(); if (children.contains(widget)) { widget->blockSignals(true); } } if (m_scrollFrame) { m_scrollArea->setWidget(NULL); delete m_scrollFrame; m_scrollFrame = NULL; } m_connectorCount = connectorList->size(); m_numberEdit->setText(QString::number(m_connectorCount)); m_scrollFrame = new QFrame(this); m_scrollFrame->setObjectName("NewPartsEditorConnectors"); QVBoxLayout *scrollLayout = new QVBoxLayout(); int ix = 0; foreach (QDomElement connector, *connectorList) { QWidget * widget = PEUtils::makeConnectorForm(connector, ix++, this, true); scrollLayout->addWidget(widget); } scrollLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); m_scrollFrame->setLayout(scrollLayout); m_scrollArea->setWidget(m_scrollFrame); } void PEConnectorsView::nameEntry() { QLineEdit * lineEdit = qobject_cast(sender()); if (lineEdit != NULL && lineEdit->isModified()) { changeConnector(); lineEdit->setModified(false); } } void PEConnectorsView::typeEntry() { changeConnector(); } void PEConnectorsView::descriptionEntry() { QLineEdit * lineEdit = qobject_cast(sender()); if (lineEdit != NULL && lineEdit->isModified()) { changeConnector(); lineEdit->setModified(false); } } void PEConnectorsView::connectorCountEntry() { if (!m_mutex.tryLock(1)) return; // need the mutex because multiple editingFinished() signals can be triggered more-or-less at once QLineEdit * lineEdit = qobject_cast(sender()); if (lineEdit != NULL && lineEdit->isModified()) { int newCount = lineEdit->text().toInt(); if (newCount != m_connectorCount) { m_connectorCount = newCount; emit connectorCountChanged(newCount); lineEdit->setModified(false); } } m_mutex.unlock(); } void PEConnectorsView::removeConnector() { bool ok; int senderIndex = sender()->property("index").toInt(&ok); if (!ok) return; ConnectorMetadata cmd; if (!PEUtils::fillInMetadata(senderIndex, m_scrollFrame, cmd)) return; QList cmdList; cmdList.append(&cmd); emit removedConnectors(cmdList); } void PEConnectorsView::changeConnector() { bool ok; int senderIndex = sender()->property("index").toInt(&ok); if (!ok) return; ConnectorMetadata cmd; if (!PEUtils::fillInMetadata(senderIndex, m_scrollFrame, cmd)) return; emit connectorMetadataChanged(&cmd); } void PEConnectorsView::allTypeEntry() { QRadioButton * radio = qobject_cast(sender()); if (radio == NULL) return; bool ok; Connector::ConnectorType ct = (Connector::ConnectorType) radio->property("value").toInt(&ok); if (!ok) return; emit connectorsTypeChanged(ct); QTimer::singleShot(10, this, SLOT(uncheckRadios())); } void PEConnectorsView::smdEntry() { QRadioButton * radio = qobject_cast(sender()); if (radio == NULL) return; emit smdChanged(radio == m_smd ? "smd" : "tht"); } void PEConnectorsView::uncheckRadios() { // this doesn't work because the buttons are "autoexclusive" foreach (QRadioButton * radio, m_radios) { radio->setChecked(false); } } void PEConnectorsView::setSMD(bool smd) { if (smd) m_smd->setChecked(true); else m_tht->setChecked(true); } fritzing-app-0.9.3b/src/partseditor/peconnectorsview.h000066400000000000000000000046171272363601300232070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6499 $: $Author: irascibl@gmail.com $: $Date: 2012-10-02 07:21:40 +0200 (Di, 02. Okt 2012) $ ********************************************************************/ #ifndef CONNECTORSVIEW_H #define CONNECTORSVIEW_H #include #include #include #include #include #include #include #include #include #include #include #include "../items/itembase.h" #include "../items/wire.h" #include "../connectors/connectoritem.h" #include "../connectors/connector.h" #include "../referencemodel/referencemodel.h" class PEConnectorsView : public QFrame { Q_OBJECT public: PEConnectorsView(QWidget * parent = 0); ~PEConnectorsView(); void initConnectors(QList * connectorList); bool anyModified(); void setSMD(bool); signals: void connectorMetadataChanged(struct ConnectorMetadata *); void removedConnectors(QList &); void connectorCountChanged(int); void connectorsTypeChanged(Connector::ConnectorType); void smdChanged(const QString &); protected slots: void nameEntry(); void descriptionEntry(); void typeEntry(); void connectorCountEntry(); void removeConnector(); void allTypeEntry(); void smdEntry(); void uncheckRadios(); protected: void changeConnector(); protected: QFrame * m_scrollFrame; QScrollArea * m_scrollArea; QLineEdit * m_numberEdit; int m_connectorCount; QMutex m_mutex; QList m_radios; QRadioButton * m_smd; QRadioButton * m_tht; }; #endif fritzing-app-0.9.3b/src/partseditor/pegraphicsitem.cpp000066400000000000000000000252731272363601300231520ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "pegraphicsitem.h" #include "../debugdialog.h" #include "../sketch/infographicsview.h" #include "../items/itembase.h" #include #include #include #include static QVector Dashes; static const int DashLength = 3; static bool ShiftDown = false; static QPointF OriginalShiftPos; static bool ShiftX = false; static bool ShiftY = false; static bool SpaceBarWasPressed = false; static const double MinMouseMove = 2; static const QColor NormalColor(0, 0, 255); static const QColor PickColor(255, 0, 255); //////////////////////////////////////////////// PEGraphicsItem::PEGraphicsItem(double x, double y, double w, double h, ItemBase * itemBase) : QGraphicsRectItem(x, y, w, h) { if (Dashes.isEmpty()) { Dashes << DashLength << DashLength; } m_itemBase = itemBase; m_pick = m_flash = false; m_terminalPoint = QPointF(w / 2, h / 2); m_dragTerminalPoint = m_showMarquee = m_showTerminalPoint = false; setAcceptedMouseButtons(Qt::LeftButton); setAcceptHoverEvents(true); //setFlag(QGraphicsItem::ItemIsSelectable, true ); setHighlighted(false); setBrush(QBrush(NormalColor)); } PEGraphicsItem::~PEGraphicsItem() { m_element.clear(); } void PEGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *) { m_wheelAccum = 0; setHighlighted(true); } void PEGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *) { setHighlighted(false); } void PEGraphicsItem::wheelEvent(QGraphicsSceneWheelEvent * event) { //DebugDialog::debug(QString("wheel %1 %2").arg(event->delta()).arg(event->orientation())); #ifndef Q_OS_MAC // qt 4.8.3: mac: event orientation is messed up at this point if (event->orientation() == Qt::Horizontal) return; #endif if (event->delta() == 0) return; if ((event->modifiers() & Qt::ShiftModifier) == 0) return; // delta one click forward = 120; delta one click backward = -120 int magDelta = qAbs(event->delta()); int sign = event->delta() / magDelta; int delta = sign * qMin(magDelta, 120); m_wheelAccum += delta; if (qAbs(m_wheelAccum) < 120) return; m_wheelAccum = 0; QList items; foreach (QGraphicsItem * item, scene()->items(event->scenePos())) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi) items.append(pegi); } if (items.count() < 2) return; int ix = -1; int mix = -1; for (int i = 0; i < items.count(); i++) { if (items.at(i)->highlighted()) { ix = i; break; } if (items.at(i) == this) { mix = i; } } if (ix == -1) ix = mix; if (ix == -1) { // shouldn't happen return; } ix += sign; // wrap //while (ix < 0) { // ix += items.count(); //} //ix = ix % items.count(); // don't wrap if (ix < 0) ix = 0; else if (ix >= items.count()) ix = items.count() - 1; PEGraphicsItem * theItem = items.at(ix); if (theItem->highlighted()) { theItem->flash(); } else { theItem->setHighlighted(true); } } void PEGraphicsItem::setHighlighted(bool highlighted) { if (highlighted) { m_highlighted = true; setOpacity(0.4); foreach (QGraphicsItem * item, scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi == NULL) continue; if (pegi == this) continue; if (!pegi->highlighted()) continue; pegi->setHighlighted(false); } emit highlightSignal(this); } else { m_highlighted = false; setOpacity(0.001); } update(); } bool PEGraphicsItem::highlighted() { return m_highlighted; } void PEGraphicsItem::setElement(QDomElement & el) { m_element = el; } QDomElement & PEGraphicsItem::element() { return m_element; } void PEGraphicsItem::setOffset(QPointF p) { m_offset = p; } QPointF PEGraphicsItem::offset() { return m_offset; } void PEGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QGraphicsRectItem::paint(painter, option, widget); if (m_flash) { m_flash = false; QTimer::singleShot(40, this, SLOT(restoreColor())); } bool save = m_showTerminalPoint || m_showMarquee; if (save) painter->save(); if (m_showTerminalPoint) { QRectF r = rect(); QLineF l1(0, m_terminalPoint.y(), r.width(), m_terminalPoint.y()); QLineF l2(m_terminalPoint.x(), 0, m_terminalPoint.x(), r.height()); painter->setOpacity(1.0); painter->setPen(QPen(QColor(0, 0, 0), 0, Qt::SolidLine)); painter->setBrush(Qt::NoBrush); painter->drawLine(l1); painter->drawLine(l2); painter->setPen(QPen(QColor(255, 255, 255), 0, Qt::DashLine)); painter->setBrush(Qt::NoBrush); painter->drawLine(l1); painter->drawLine(l2); } if (m_showMarquee) { QRectF r = rect(); double d = qMin(r.width(), r.height()) / 16; r.adjust(d, d, -d, -d); painter->setOpacity(1.0); painter->setPen(QPen(QColor(0, 0, 0), 0, Qt::SolidLine)); painter->setBrush(Qt::NoBrush); painter->drawRect(r); painter->setPen(QPen(QColor(255, 255, 255), 0, Qt::DashLine)); painter->setBrush(Qt::NoBrush); painter->drawRect(r); } if (save) painter->restore(); } void PEGraphicsItem::showTerminalPoint(bool show) { m_showTerminalPoint = show; update(); } bool PEGraphicsItem::showingTerminalPoint() { return m_showTerminalPoint; } void PEGraphicsItem::showMarquee(bool show) { if (show) { m_showMarquee = true; foreach (QGraphicsItem * item, scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi == NULL) continue; if (pegi == this) continue; if (!pegi->showingMarquee()) continue; pegi->showMarquee(false); } } else { m_showMarquee = false; } update(); } bool PEGraphicsItem::showingMarquee() { return m_showMarquee; } void PEGraphicsItem::setTerminalPoint(QPointF p) { m_pendingTerminalPoint = m_terminalPoint = p; } QPointF PEGraphicsItem::terminalPoint() { return m_terminalPoint; } void PEGraphicsItem::setPendingTerminalPoint(QPointF p) { m_pendingTerminalPoint = p; } QPointF PEGraphicsItem::pendingTerminalPoint() { return m_pendingTerminalPoint; } void PEGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent * event) { m_dragTerminalPoint = false; if (!m_highlighted) { // allows to click through to next layer event->ignore(); return; } if (!event->buttons() && Qt::LeftButton) { event->ignore(); return; } InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this); if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) { event->ignore(); return; } bool ignore; emit mousePressedSignal(this, ignore); if (ignore) { event->ignore(); return; } if (m_showMarquee) { QPointF p = event->pos(); if (event->modifiers() & Qt::ShiftModifier) { ShiftDown = true; ShiftX = ShiftY = false; OriginalShiftPos = p; } if (qAbs(p.x() - m_terminalPoint.x()) <= MinMouseMove && qAbs(p.y() - m_terminalPoint.y()) <= MinMouseMove) { m_dragTerminalPoint = true; m_terminalPointOrigin = m_terminalPoint; m_dragTerminalOrigin = event->pos(); } return; } event->ignore(); } void PEGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { if (!m_dragTerminalPoint) return; if (ShiftDown && !(event->modifiers() & Qt::ShiftModifier)) { ShiftDown = false; } QPointF p = event->pos(); if (ShiftDown) { if (ShiftX) { // moving along x, constrain y p.setY(OriginalShiftPos.y()); } else if (ShiftY) { // moving along y, constrain x p.setX(OriginalShiftPos.x()); } else { double dx = qAbs(p.x() - OriginalShiftPos.x()); double dy = qAbs(p.y() - OriginalShiftPos.y()); if (dx - dy > MinMouseMove) { ShiftX = true; } else if (dy - dx > MinMouseMove) { ShiftY = true; } } } else if (event->modifiers() & Qt::ShiftModifier) { ShiftDown = true; ShiftX = ShiftY = false; OriginalShiftPos = event->pos(); } QPointF newTerminalPoint = m_terminalPointOrigin + p - m_dragTerminalOrigin; if (newTerminalPoint.x() < 0) newTerminalPoint.setX(0); else if (newTerminalPoint.x() > rect().width()) newTerminalPoint.setX(rect().width()); if (newTerminalPoint.y() < 0) newTerminalPoint.setY(0); else if (newTerminalPoint.y() > rect().height()) newTerminalPoint.setY(rect().height()); m_terminalPoint = newTerminalPoint; emit terminalPointMoved(this, newTerminalPoint); update(); } void PEGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *) { if (m_dragTerminalPoint) { m_dragTerminalPoint = false; if (m_terminalPointOrigin != m_terminalPoint) { emit terminalPointChanged(this, m_terminalPointOrigin, m_terminalPoint); } } else { // relocate the connector emit mouseReleasedSignal(this); } } void PEGraphicsItem::setPickAppearance(bool pick) { m_pick = pick; setBrush(pick ? PickColor : NormalColor); } void PEGraphicsItem::flash() { m_savedOpacity = opacity(); m_flash = true; setBrush(QColor(255, 255, 255)); update(); } void PEGraphicsItem::restoreColor() { setBrush(m_pick ? PickColor : NormalColor); setOpacity(m_savedOpacity); update(); } ItemBase * PEGraphicsItem::itemBase() { return m_itemBase; } fritzing-app-0.9.3b/src/partseditor/pegraphicsitem.h000066400000000000000000000061121272363601300226060ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PEGRAPHICSITEM_H_ #define PEGRAPHICSITEM_H_ #include #include #include #include #include #include #include class PEGraphicsItem : public QObject, public QGraphicsRectItem { Q_OBJECT public: PEGraphicsItem(double x, double y, double width, double height, class ItemBase *); ~PEGraphicsItem(); void hoverEnterEvent(QGraphicsSceneHoverEvent *); void hoverLeaveEvent(QGraphicsSceneHoverEvent *); void wheelEvent(QGraphicsSceneWheelEvent *); void mousePressEvent(QGraphicsSceneMouseEvent *); void mouseMoveEvent(QGraphicsSceneMouseEvent *); void mouseReleaseEvent(QGraphicsSceneMouseEvent *); void setHighlighted(bool); bool highlighted(); void setElement(QDomElement &); QDomElement & element(); void setOffset(QPointF); QPointF offset(); void showTerminalPoint(bool); bool showingTerminalPoint(); void showMarquee(bool); bool showingMarquee(); void setTerminalPoint(QPointF); QPointF terminalPoint(); void setPendingTerminalPoint(QPointF); QPointF pendingTerminalPoint(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void setPickAppearance(bool); void flash(); class ItemBase * itemBase(); signals: void highlightSignal(PEGraphicsItem *); void mousePressedSignal(PEGraphicsItem *, bool & ignore); void mouseReleasedSignal(PEGraphicsItem *); void terminalPointMoved(PEGraphicsItem *, QPointF); void terminalPointChanged(PEGraphicsItem *, QPointF before, QPointF after); protected slots: void restoreColor(); protected: bool m_highlighted; bool m_flash; QDomElement m_element; QPointF m_offset; bool m_showTerminalPoint; bool m_showMarquee; QPointF m_terminalPoint; QPointF m_pendingTerminalPoint; bool m_dragTerminalPoint; QPointF m_dragTerminalOrigin; QPointF m_terminalPointOrigin; bool m_drawHighlight; int m_wheelAccum; qreal m_savedOpacity; bool m_pick; class ItemBase * m_itemBase; }; #endif /* PEGRAPHICSITEM_H_ */ fritzing-app-0.9.3b/src/partseditor/pemainwindow.cpp000066400000000000000000004155511272363601300226510ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ /* TO DO ****************************************** ///////////////////////////////// first release /////////////////////////////// clicking set bus mode fucks up anchor points terminal point area is not highlighting properly ////////////////////// second release ///////////////////////////////// icon editor? need simplified infoview change color of breadboard text item for svg import check for flaws: multiple connector or terminal ids trash any other matching id loading tht into smd or v.v. can't seem to assign connectors after adding connectors why isn't swapping available when a family has new parts with multiple variant values? test button with export etchable to make sure the part is right? export etchable is disabled without a board align to grid when moving? restore editable pin labels functionality requires storing labels in the part rather than in the sketch hidden connectors allow multiple hits to the same place and add copies of the svg element if necessary restore parts bin buses secondary representation (list view) display during bus mode instead of connector list allow to delete bus, delete nodemember, add nodemember using right-click for now use the actual svg shape instead of rectangles construct a new svg with the width and height of the bounding box of the element translate the element by the current top-left of the element set the fill color and kill the stroke width properties and tags entries allow duplicates delete temp files after crash show in OS button only shows folder and not file in linux smd vs. tht after it's all over remove copper0 if part is all smd split copper0 and copper1 allow mixed tht/smd parts in fritzing but for now disable flipsmd allow smd with holes to flip? import kicad sch files? kicad schematic does not match our schematic on svg import detect all connector IDs if any are invisible, tell user this is obsolete sort connector list alphabetically or numerically? full svg outline view bendable legs same as terminalID, but must be a line use a radio buttons to distinguish? set flippable connector duplicate op add layers: put everything in silkscreen, then give copper1, copper0 checkbox what about breadboardbreadboard or other odd layers? if you click something as a connector, automatically move it into copper how to distinguish between both and top--default to both, let user set "pad" setting layer for top level group sets all children? swap connector metadata op delete op move connectors with arrow keys, or typed coordinates drag and drop later import eagle lbr eagle brd for schematic view offer lines-or-pins, rects, and a selection of standard schematic icons in the parts bin for breadboard view import generate ICs, dips, sips, breakouts for pcb view pads, pins (circle, rect, oblong), holes lines and curves? import silkscreen hybrids flip and rotate images w/in the view? taxonomy entry like tag entry? new schematic layout specs give users a family popup with all family names ditto for other properties matrix problem with move and duplicate (i.e. if element inherits a matrix from far above) even a problem when inserting hole, pad, or pin eliminate internal transforms, then insert inverted matrix, then use untransformed coords based on viewbox ***************************************************/ #include "pemainwindow.h" #include "pemetadataview.h" #include "peconnectorsview.h" #include "pecommands.h" #include "petoolview.h" #include "pesvgview.h" #include "pegraphicsitem.h" #include "kicadmoduledialog.h" #include "../debugdialog.h" #include "../sketch/breadboardsketchwidget.h" #include "../sketch/schematicsketchwidget.h" #include "../sketch/pcbsketchwidget.h" #include "../mainwindow/sketchareawidget.h" #include "../referencemodel/referencemodel.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/folderutils.h" #include "../utils/s2s.h" #include "../mainwindow/fdockwidget.h" #include "../fsvgrenderer.h" #include "../partsbinpalette/binmanager/binmanager.h" #include "../svg/gedaelement2svg.h" #include "../svg/kicadmodule2svg.h" #include "../svg/kicadschematic2svg.h" #include "../sketchtoolbutton.h" #include "../items/virtualwire.h" #include "../connectors/connectoritem.h" #include "../connectors/bus.h" #include "../installedfonts.h" #include "../dock/layerpalette.h" #include "../utils/cursormaster.h" #include "../infoview/htmlinfoview.h" #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////// static bool RubberBandLegWarning = false; static bool GotZeroConnector = false; static const QString ReferenceFileString("referenceFile"); static const int IconViewIndex = 3; static const int MetadataViewIndex = 4; static const int ConnectorsViewIndex = 5; const static int PegiZ = 5000; const static int RatZ = 6000; static long FakeGornSiblingNumber = 0; void removeGornAux(QDomElement & element) { bool hasGorn = element.hasAttribute("gorn"); bool hasOld = element.hasAttribute("oldid"); if (hasGorn && hasOld) { element.removeAttribute("gorn"); QString oldid = element.attribute("oldid"); if (oldid.isEmpty()) element.removeAttribute("id"); else element.setAttribute("id", oldid); element.removeAttribute("oldid"); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { removeGornAux(child); child = child.nextSiblingElement(); } } QString removeGorn(QString & svg) { QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { // shouldn't happen DebugDialog::debug(QString("remove gorn failure: %1 %2 %3 %4").arg(errorStr).arg(errorLine).arg(errorColumn).arg(svg)); return svg; } QDomElement root = doc.documentElement(); removeGornAux(root); return doc.toString(4); } bool byID(QDomElement & c1, QDomElement & c2) { int c1id = -1; int c2id = -1; int ix = IntegerFinder.indexIn(c1.attribute("id")); if (ix >= 0) c1id = IntegerFinder.cap(0).toInt(); ix = IntegerFinder.indexIn(c2.attribute("id")); if (ix >= 0) c2id = IntegerFinder.cap(0).toInt(); if (c1id == 0 || c2id == 0) GotZeroConnector = true; return c1id <= c2id; } void removeID(QDomElement & root, const QString & value) { if (root.attribute("id") == value) { root.removeAttribute("id"); } QDomNodeList nodeList = root.childNodes(); for (int i = 0; i < nodeList.count(); i++) { QDomNode node = nodeList.item(i); if (node.nodeType() == QDomNode::ElementNode) { QDomElement element = node.toElement(); removeID(element, value); } } } //////////////////////////////////////////////////// IconSketchWidget::IconSketchWidget(ViewLayer::ViewID viewID, QWidget *parent) : SketchWidget(viewID, parent) { m_shortName = QObject::tr("ii"); m_viewName = QObject::tr("Icon View"); initBackgroundColor(); } void IconSketchWidget::addViewLayers() { setViewLayerIDs(ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon, ViewLayer::Icon); addViewLayersAux(ViewLayer::layersForView(ViewLayer::IconView), ViewLayer::layersForViewFromBelow(ViewLayer::IconView)); } //////////////////////////////////////////////////// ViewThing::ViewThing() { itemBase = NULL; document = NULL; svgChangeCount = 0; everZoomed = false; sketchWidget = NULL; firstTime = true; busMode = false; } ///////////////////////////////////////////////////// PEMainWindow::PEMainWindow(ReferenceModel * referenceModel, QWidget * parent) : MainWindow(referenceModel, parent) { m_viewThings.insert(ViewLayer::BreadboardView, new ViewThing); m_viewThings.insert(ViewLayer::SchematicView, new ViewThing); m_viewThings.insert(ViewLayer::PCBView, new ViewThing); m_viewThings.insert(ViewLayer::IconView, new ViewThing); m_useNextPick = m_inPickMode = false; m_autosaveTimer.stop(); disconnect(&m_autosaveTimer, SIGNAL(timeout()), this, SLOT(backupSketch())); m_gaveSaveWarning = m_canSave = false; m_settingsPrefix = "pe/"; m_guid = TextUtils::getRandText(); m_prefix = "prefix0000"; m_fileIndex = 0; m_userPartsFolderPath = FolderUtils::getUserPartsPath()+"/user/"; m_userPartsFolderSvgPath = FolderUtils::getUserPartsPath()+"/svg/user/"; m_peToolView = NULL; m_peSvgView = NULL; m_connectorsView = NULL; } PEMainWindow::~PEMainWindow() { // PEGraphicsItems are still holding QDomElement so delete them before m_fzpDocument is deleted killPegi(); // kill temp files foreach (QString string, m_filesToDelete) { QFile::remove(string); } QDir dir = QDir::temp(); dir.rmdir(makeDirName()); } void PEMainWindow::closeEvent(QCloseEvent *event) { qDebug() << "close event"; QStringList messages; if (m_inFocusWidgets.count() > 0) { bool gotOne = false; // should only be one in-focus widget foreach (QWidget * widget, m_inFocusWidgets) { QLineEdit * lineEdit = qobject_cast(widget); if (lineEdit) { if (lineEdit->isModified()) { lineEdit->clearFocus(); lineEdit->setModified(false); gotOne = true; } } else { QTextEdit * textEdit = qobject_cast(widget); if (textEdit) { if (textEdit->document()->isModified()) { textEdit->clearFocus(); textEdit->document()->setModified(false); gotOne = true; } } } } if (gotOne) { messages << tr("There is one last edit still pending."); } } QString family = m_metadataView->family(); if (family.isEmpty()) { messages << tr("The 'family' property can not be blank."); } QStringList keys = m_metadataView->properties().keys(); if (keys.contains("family", Qt::CaseInsensitive)) { messages << tr("A duplicate 'family' property is not allowed"); } if (keys.contains("variant", Qt::CaseInsensitive)) { messages << tr("A duplicate 'variant' property is not allowed"); } bool discard = true; if (messages.count() > 0) { QMessageBox messageBox(this); messageBox.setWindowTitle(tr("Close without saving?")); QString message = tr("This part can not be saved as-is:\n\n"); foreach (QString string, messages) { message.append('\t'); message.append(string); messages.append("\n\n"); } message += tr("Do you want to keep working or close without saving?"); messageBox.setText(message); messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); messageBox.setDefaultButton(QMessageBox::Cancel); messageBox.setIcon(QMessageBox::Warning); messageBox.setWindowModality(Qt::WindowModal); messageBox.setButtonText(QMessageBox::Ok, tr("Close without saving")); messageBox.setButtonText(QMessageBox::Cancel, tr("Keep working")); QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec(); if (answer != QMessageBox::Ok) { event->ignore(); return; } } else if (!beforeClosing(true, discard)) { event->ignore(); return; } if (!discard) { // if all connectors not found, put up a message connectorWarning(); } QSettings settings; settings.setValue(m_settingsPrefix + "state", saveState()); settings.setValue(m_settingsPrefix + "geometry", saveGeometry()); QMainWindow::closeEvent(event); } void PEMainWindow::initLockedFiles(bool) { } void PEMainWindow::initSketchWidgets(bool whatever) { Q_UNUSED(whatever); MainWindow::initSketchWidgets(false); m_iconGraphicsView = new IconSketchWidget(ViewLayer::IconView, this); initSketchWidget(m_iconGraphicsView); m_iconWidget = new SketchAreaWidget(m_iconGraphicsView,this); addTab(m_iconWidget, tr("Icon")); initSketchWidget(m_iconGraphicsView); ViewThing * viewThing = m_viewThings.value(m_breadboardGraphicsView->viewID()); viewThing->sketchWidget = m_breadboardGraphicsView; viewThing->document = &m_breadboardDocument; viewThing = m_viewThings.value(m_schematicGraphicsView->viewID()); viewThing->sketchWidget = m_schematicGraphicsView; viewThing->document = &m_schematicDocument; viewThing = m_viewThings.value(m_pcbGraphicsView->viewID()); viewThing->sketchWidget = m_pcbGraphicsView; viewThing->document = &m_pcbDocument; viewThing = m_viewThings.value(m_iconGraphicsView->viewID()); viewThing->sketchWidget = m_iconGraphicsView; viewThing->document = &m_iconDocument; foreach (ViewThing * viewThing, m_viewThings.values()) { viewThing->sketchWidget->setAcceptWheelEvents(true); viewThing->sketchWidget->setChainDrag(false); // no bendpoints viewThing->firstTime = true; viewThing->everZoomed = true; connect(viewThing->sketchWidget, SIGNAL(newWireSignal(Wire *)), this, SLOT(newWireSlot(Wire *))); connect(viewThing->sketchWidget, SIGNAL(showing(SketchWidget *)), this, SLOT(showing(SketchWidget *))); connect(viewThing->sketchWidget, SIGNAL(itemMovedSignal(ItemBase *)), this, SLOT(itemMovedSlot(ItemBase *))); connect(viewThing->sketchWidget, SIGNAL(resizedSignal(ItemBase *)), this, SLOT(resizedSlot(ItemBase *))); connect(viewThing->sketchWidget, SIGNAL(clickedItemCandidateSignal(QGraphicsItem *, bool &)), this, SLOT(clickedItemCandidateSlot(QGraphicsItem *, bool &)), Qt::DirectConnection); connect(viewThing->sketchWidget, SIGNAL(itemAddedSignal(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *)), this, SLOT(itemAddedSlot(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long, SketchWidget *))); } m_metadataView = new PEMetadataView(this); SketchAreaWidget * sketchAreaWidget = new SketchAreaWidget(m_metadataView, this, false, false); addTab(sketchAreaWidget, tr("Metadata")); connect(m_metadataView, SIGNAL(metadataChanged(const QString &, const QString &)), this, SLOT(metadataChanged(const QString &, const QString &)), Qt::DirectConnection); connect(m_metadataView, SIGNAL(tagsChanged(const QStringList &)), this, SLOT(tagsChanged(const QStringList &)), Qt::DirectConnection); connect(m_metadataView, SIGNAL(propertiesChanged(const QHash &)), this, SLOT(propertiesChanged(const QHash &)), Qt::DirectConnection); m_connectorsView = new PEConnectorsView(this); sketchAreaWidget = new SketchAreaWidget(m_connectorsView, this, false, false); addTab(sketchAreaWidget, tr("Connectors")); connect(m_connectorsView, SIGNAL(connectorMetadataChanged(ConnectorMetadata *)), this, SLOT(connectorMetadataChanged(ConnectorMetadata *)), Qt::DirectConnection); connect(m_connectorsView, SIGNAL(connectorsTypeChanged(Connector::ConnectorType)), this, SLOT(connectorsTypeChanged(Connector::ConnectorType))); connect(m_connectorsView, SIGNAL(removedConnectors(QList &)), this, SLOT(removedConnectors(QList &)), Qt::DirectConnection); connect(m_connectorsView, SIGNAL(connectorCountChanged(int)), this, SLOT(connectorCountChanged(int)), Qt::DirectConnection); connect(m_connectorsView, SIGNAL(smdChanged(const QString &)), this, SLOT(smdChanged(const QString &))); } void PEMainWindow::initDock() { m_layerPalette = new LayerPalette(this); /* m_infoView = new HtmlInfoView(); m_infoView->init(true); m_binManager = new BinManager(m_referenceModel, m_infoView, m_undoStack, this); m_binManager->openBin(":/resources/bins/pe.fzb"); m_binManager->hideTabBar(); */ } void PEMainWindow::moreInitDock() { static int BinMinHeight = 75; static int ToolDefaultHeight = 150; static int SvgDefaultHeight = 50; m_peToolView = new PEToolView(); connect(m_peToolView, SIGNAL(getSpinAmount(double &)), this, SLOT(getSpinAmount(double &)), Qt::DirectConnection); connect(m_peToolView, SIGNAL(terminalPointChanged(const QString &)), this, SLOT(terminalPointChanged(const QString &))); connect(m_peToolView, SIGNAL(terminalPointChanged(const QString &, double)), this, SLOT(terminalPointChanged(const QString &, double))); connect(m_peToolView, SIGNAL(switchedConnector(int)), this, SLOT(switchedConnector(int))); connect(m_peToolView, SIGNAL(removedConnector(const QDomElement &)), this, SLOT(removedConnector(const QDomElement &))); connect(m_peToolView, SIGNAL(pickModeChanged(bool)), this, SLOT(pickModeChanged(bool))); connect(m_peToolView, SIGNAL(busModeChanged(bool)), this, SLOT(busModeChanged(bool))); connect(m_peToolView, SIGNAL(connectorMetadataChanged(ConnectorMetadata *)), this, SLOT(connectorMetadataChanged(ConnectorMetadata *)), Qt::DirectConnection); makeDock(tr("Connectors"), m_peToolView, DockMinWidth, ToolDefaultHeight); m_peToolView->setMinimumSize(DockMinWidth, ToolDefaultHeight); m_peSvgView = new PESvgView(); makeDock(tr("SVG"), m_peSvgView, DockMinWidth, SvgDefaultHeight); m_peSvgView->setMinimumSize(DockMinWidth, SvgDefaultHeight); if (m_binManager) { QDockWidget * dockWidget = makeDock(BinManager::Title, m_binManager, DockMinWidth, BinMinHeight); dockWidget->resize(0, 0); } if (m_infoView) { makeDock(tr("Inspector"), m_infoView, InfoViewMinHeight, InfoViewHeightDefault); this -> setObjectName("PEInspector"); } makeDock(tr("Layers"), m_layerPalette, DockMinWidth, DockMinHeight)->hide(); m_layerPalette->setMinimumSize(DockMinWidth, DockMinHeight); m_layerPalette->setShowAllLayersAction(m_showAllLayersAct); m_layerPalette->setHideAllLayersAction(m_hideAllLayersAct); } void PEMainWindow::createFileMenuActions() { MainWindow::createFileMenuActions(); m_reuseBreadboardAct = new QAction(tr("Reuse breadboard image"), this); m_reuseBreadboardAct->setStatusTip(tr("Reuse the breadboard image in this view")); connect(m_reuseBreadboardAct, SIGNAL(triggered()), this, SLOT(reuseBreadboard())); m_reuseSchematicAct = new QAction(tr("Reuse schematic image"), this); m_reuseSchematicAct->setStatusTip(tr("Reuse the schematic image in this view")); connect(m_reuseSchematicAct, SIGNAL(triggered()), this, SLOT(reuseSchematic())); m_reusePCBAct = new QAction(tr("Reuse PCB image"), this); m_reusePCBAct->setStatusTip(tr("Reuse the PCB image in this view")); connect(m_reusePCBAct, SIGNAL(triggered()), this, SLOT(reusePCB())); } void PEMainWindow::createActions() { createFileMenuActions(); m_openAct->setText(tr("Load image for view...")); m_openAct->setStatusTip(tr("Open a file to use as the image for this view of the part.")); disconnect(m_openAct, SIGNAL(triggered()), this, SLOT(mainLoad())); connect(m_openAct, SIGNAL(triggered()), this, SLOT(loadImage())); m_showInOSAct = new QAction(tr("Show in Folder"), this); m_showInOSAct->setStatusTip(tr("On the desktop, open the folder containing the current svg file.")); connect(m_showInOSAct, SIGNAL(triggered()), this, SLOT(showInOS())); createEditMenuActions(); m_convertToTenthAct = new QAction(tr("Convert schematic to 0.1 inch standard"), this); m_convertToTenthAct->setStatusTip(tr("Convert pre-0.8.6 schematic image to new 0.1 inch standard")); connect(m_convertToTenthAct, SIGNAL(triggered()), this, SLOT(convertToTenth())); m_deleteBusConnectionAct = new WireAction(tr("Remove Internal Connection"), this); connect(m_deleteBusConnectionAct, SIGNAL(triggered()), this, SLOT(deleteBusConnection())); createViewMenuActions(false); createHelpMenuActions(); createWindowMenuActions(); createActiveLayerActions(); } void PEMainWindow::createMenus() { createFileMenu(); createEditMenu(); createViewMenu(); createWindowMenu(); createHelpMenu(); } void PEMainWindow::createFileMenu() { m_fileMenu = menuBar()->addMenu(tr("&File")); m_fileMenu->addAction(m_openAct); m_fileMenu->addAction(m_reuseBreadboardAct); m_fileMenu->addAction(m_reuseSchematicAct); m_fileMenu->addAction(m_reusePCBAct); //m_fileMenu->addAction(m_revertAct); m_fileMenu->addSeparator(); m_fileMenu->addAction(m_closeAct); m_fileMenu->addAction(m_saveAct); m_fileMenu->addAction(m_saveAsAct); m_saveAsAct->setText(tr("Save as new part")); m_saveAsAct->setStatusTip(tr("Make a copy of the part and save it in the 'My Parts' Bin")); m_fileMenu->addSeparator(); m_exportMenu = m_fileMenu->addMenu(tr("&Export")); //m_fileMenu->addAction(m_pageSetupAct); m_fileMenu->addAction(m_printAct); m_fileMenu->addAction(m_showInOSAct); m_fileMenu->addSeparator(); m_fileMenu->addAction(m_quitAct); populateExportMenu(); connect(m_fileMenu, SIGNAL(aboutToShow()), this, SLOT(updateFileMenu())); } void PEMainWindow::createEditMenu() { m_editMenu = menuBar()->addMenu(tr("&Edit")); m_editMenu->addAction(m_undoAct); m_editMenu->addAction(m_redoAct); m_editMenu->addSeparator(); m_editMenu->addAction(m_convertToTenthAct); updateEditMenu(); connect(m_editMenu, SIGNAL(aboutToShow()), this, SLOT(updateEditMenu())); } QList PEMainWindow::getButtonsForView(ViewLayer::ViewID viewID) { QList retval; SketchAreaWidget *parent; switch(viewID) { case ViewLayer::BreadboardView: parent = m_breadboardWidget; break; case ViewLayer::SchematicView: parent = m_schematicWidget; break; case ViewLayer::PCBView: parent = m_pcbWidget; break; default: return retval; } //retval << createExportEtchableButton(parent); switch (viewID) { case ViewLayer::BreadboardView: break; case ViewLayer::SchematicView: break; case ViewLayer::PCBView: // retval << createActiveLayerButton(parent); break; default: break; } return retval; } bool PEMainWindow::activeLayerWidgetAlwaysOn() { return true; } void PEMainWindow::connectPairs() { bool succeeded = true; succeeded = succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_breadboardGraphicsView, SLOT(spaceBarIsPressedSlot(bool))); succeeded = succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_schematicGraphicsView, SLOT(spaceBarIsPressedSlot(bool))); succeeded = succeeded && connect(qApp, SIGNAL(spaceBarIsPressedSignal(bool)), m_pcbGraphicsView, SLOT(spaceBarIsPressedSlot(bool))); succeeded = succeeded && connect(m_pcbGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double))); succeeded = succeeded && connect(m_breadboardGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double))); succeeded = succeeded && connect(m_schematicGraphicsView, SIGNAL(cursorLocationSignal(double, double, double, double)), this, SLOT(cursorLocationSlot(double, double, double, double))); connect(m_breadboardGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *))); connect(m_schematicGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *))); connect(m_pcbGraphicsView, SIGNAL(setActiveWireSignal(Wire *)), this, SLOT(setActiveWire(Wire *))); } QMenu *PEMainWindow::breadboardWireMenu() { QMenu *menu = new QMenu(QObject::tr("Internal Connections"), this); menu->addAction(m_deleteBusConnectionAct); connect( menu, SIGNAL(aboutToShow()), this, SLOT(updateWireMenu())); return menu; } QMenu *PEMainWindow::breadboardItemMenu() { return NULL; } QMenu *PEMainWindow::schematicWireMenu() { return breadboardWireMenu(); } QMenu *PEMainWindow::schematicItemMenu() { return NULL; } QMenu *PEMainWindow::pcbWireMenu() { return breadboardWireMenu(); } QMenu *PEMainWindow::pcbItemMenu() { return NULL; } bool PEMainWindow::setInitialItem(PaletteItem * paletteItem) { m_pcbGraphicsView->setBoardLayers(2, true); m_pcbGraphicsView->setLayerActive(ViewLayer::Copper1, true); m_pcbGraphicsView->setLayerActive(ViewLayer::Copper0, true); m_pcbGraphicsView->setLayerActive(ViewLayer::Silkscreen1, true); m_pcbGraphicsView->setLayerActive(ViewLayer::Silkscreen0, true); ModelPart * originalModelPart = NULL; if (paletteItem == NULL) { // this shouldn't happen originalModelPart = m_referenceModel->retrieveModelPart("generic_ic_dip_8_300mil"); } else { originalModelPart = paletteItem->modelPart(); } m_originalFzpPath = originalModelPart->path(); m_originalModuleID = originalModelPart->moduleID(); QFileInfo info(originalModelPart->path()); QString basename = info.completeBaseName(); int ix = GuidMatcher.indexIn(basename); if (ix > 1 && basename.at(ix - 1) == '_') { int dix = ix + 32 + 1; if (basename.count() > dix) { bool gotPrefix = true; for (int i = dix; i < basename.count(); i++) { if (!basename.at(i).isDigit()) { gotPrefix = false; break; } } if (gotPrefix) { m_prefix = basename.left(ix - 1); } } } //DebugDialog::debug(QString("%1, %2").arg(info.absoluteFilePath()).arg(m_userPartsFolderPath)); m_canSave = info.absoluteFilePath().contains(m_userPartsFolderPath); if (!loadFzp(originalModelPart->path())) return false; QDomElement fzpRoot = m_fzpDocument.documentElement(); QString referenceFile = fzpRoot.attribute(ReferenceFileString); if (referenceFile.isEmpty()) { fzpRoot.setAttribute(ReferenceFileString, info.fileName()); } QDomElement views = fzpRoot.firstChildElement("views"); QDomElement author = fzpRoot.firstChildElement("author"); if (author.isNull()) { author = m_fzpDocument.createElement("author"); fzpRoot.appendChild(author); } if (author.text().isEmpty()) { TextUtils::replaceChildText(author, QString(getenvUser())); } QDomElement date = fzpRoot.firstChildElement("date"); if (date.isNull()) { date = m_fzpDocument.createElement("date"); fzpRoot.appendChild(date); } TextUtils::replaceChildText(date, QDate::currentDate().toString()); QDomElement properties = fzpRoot.firstChildElement("properties"); if (properties.isNull()) { properties = m_fzpDocument.createElement("properties"); fzpRoot.appendChild(properties); } QHash props = originalModelPart->properties(); foreach (QString key, props.keys()) { replaceProperty(key, props.value(key), properties); } // record "local" properties foreach (QByteArray byteArray, originalModelPart->dynamicPropertyNames()) { replaceProperty(byteArray, originalModelPart->property(byteArray).toString(), properties); } bool hasLegID = false; QDomElement connectors = fzpRoot.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QString localName = originalModelPart->connectorLocalName(connector.attribute("id")); if (!localName.isEmpty()) { connector.setAttribute("name", localName); } if (!hasLegID) { QDomNodeList plist = connector.elementsByTagName("p"); for (int i = 0; i < plist.count(); i++) { QDomElement p = plist.at(i).toElement(); if (!p.attribute("legId").isEmpty()) { hasLegID = true; break; } } } connector = connector.nextSiblingElement("connector"); } if (hasLegID && !RubberBandLegWarning) { RubberBandLegWarning = true; QMessageBox::warning(NULL, tr("Parts Editor"), tr("This part has bendable legs. ") + tr("This version of the Parts Editor does not yet support editing bendable legs, and the legs may not be displayed correctly in breadboard view . ") + tr("If you make changes to breadboard view, or change connector metadata, the legs may no longer work. ") + tr("You can safely make changes to Schematic or PCB view.\n\n") + tr("This warning will not be repeated in this session of Fritzing") ); } // for now kill the editable pin labels property, otherwise the saved part will try to use the labels that are only found in the sketch QDomElement epl = TextUtils::findElementWithAttribute(properties, "name", "editable pin labels"); if (!epl.isNull()) { TextUtils::replaceChildText(epl, "false"); } QDomElement family = TextUtils::findElementWithAttribute(properties, "name", "family"); if (family.isNull()) { replaceProperty("family", m_guid, properties); } QDomElement variant = TextUtils::findElementWithAttribute(properties, "name", "variant"); if (variant.isNull()) { QString newVariant = makeNewVariant(family.text()); replaceProperty("variant", newVariant, properties); } foreach (ViewThing * viewThing, m_viewThings.values()) { if (viewThing->sketchWidget == NULL) continue; ItemBase * itemBase = originalModelPart->viewItem(viewThing->sketchWidget->viewID()); if (itemBase == NULL) continue; viewThing->referenceFile = getSvgReferenceFile(itemBase->filename()); if (!itemBase->hasCustomSVG()) { QFile file(itemBase->filename()); if (!file.open(QFile::ReadOnly)) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to load '%1'. Please close the parts editor without saving and try again.").arg(itemBase->filename())); continue; } QString svg = file.readAll(); insertDesc(viewThing->referenceFile, svg); TextUtils::fixMuch(svg, true); QString svgPath = makeSvgPath2(viewThing->sketchWidget); bool result = writeXml(m_userPartsFolderSvgPath + svgPath, removeGorn(svg), true); if (!result) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to write svg to %1").arg(svgPath)); return false; } continue; } QHash svgHash; QStringList svgList; double factor; foreach (ViewLayer * vl, viewThing->sketchWidget->viewLayers().values()) { QString string = itemBase->retrieveSvg(vl->viewLayerID(), svgHash, false, GraphicsUtils::StandardFritzingDPI, factor); if (!string.isEmpty()) { svgList.append(string); } } if (svgList.count() == 0) { DebugDialog::debug(QString("pe: missing custom svg %1").arg(originalModelPart->moduleID())); continue; } QString svg; if (svgList.count() == 1) { svg = svgList.at(0); } else { // deal with copper0 and copper1 layers as parent/child // have to remove whitespace in order to compare the two svgs QRegExp white("\\s"); QStringList whiteList; foreach (QString string, svgList) { string.remove(white); whiteList << string; } bool keepGoing = true; while (keepGoing) { keepGoing = false; for (int i = 0; i < whiteList.count() - 1; i++) { for (int j = i + 1; j < whiteList.count(); j++) { if (whiteList.at(i).contains(whiteList.at(j))) { keepGoing = true; whiteList.removeAt(j); svgList.removeAt(j); break; } if (whiteList.at(j).contains(whiteList.at(i))) { keepGoing = true; whiteList.removeAt(i); svgList.removeAt(i); break; } } if (keepGoing) break; } } svg = svgList.join("\n"); } QSizeF size = itemBase->size(); QString header = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, size.width(), size.height()); header += makeDesc(viewThing->referenceFile); svg = header + svg + ""; QString svgPath = makeSvgPath2(viewThing->sketchWidget); bool result = writeXml(m_userPartsFolderSvgPath + svgPath, removeGorn(svg), true); if (!result) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to write svg to %1").arg(svgPath)); return false; } QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewThing->sketchWidget->viewID())); QDomElement layers = view.firstChildElement("layers"); if (layers.isNull()) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to parse fzp file %1").arg(originalModelPart->path())); return false; } setImageAttribute(layers, svgPath); } reload(true); foreach (ViewThing * viewThing, m_viewThings.values()) { viewThing->originalSvgPath = viewThing->itemBase->filename(); viewThing->svgChangeCount = 0; } setTitle(); createRaiseWindowActions(); return true; } void PEMainWindow::initZoom() { if (m_peToolView == NULL) return; if (m_currentGraphicsView == NULL) return; ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID()); if (viewThing->itemBase == NULL) return; if (!viewThing->everZoomed) { viewThing->everZoomed = true; m_currentGraphicsView->fitInWindow(); } m_peSvgView->setFilename(viewThing->referenceFile); } void PEMainWindow::setTitle() { QString title = tr("Fritzing (New) Parts Editor"); QString partTitle = getPartTitle(); QString viewName; if (m_currentGraphicsView) viewName = m_currentGraphicsView->viewName(); else if (currentTabIndex() == IconViewIndex) viewName = tr("Icon View"); else if (currentTabIndex() == MetadataViewIndex) viewName = tr("Metadata View"); else if (currentTabIndex() == ConnectorsViewIndex) viewName = tr("Connectors View"); setWindowTitle(QString("%1: %2 [%3]%4").arg(title).arg(partTitle).arg(viewName).arg(QtFunkyPlaceholder)); } void PEMainWindow::createViewMenuActions(bool showWelcome) { MainWindow::createViewMenuActions(showWelcome); m_showIconAct = new QAction(tr("Show Icon"), this); m_showIconAct->setShortcut(tr("Ctrl+4")); m_showIconAct->setStatusTip(tr("Show the icon view")); connect(m_showIconAct, SIGNAL(triggered()), this, SLOT(showIconView())); m_showMetadataViewAct = new QAction(tr("Show Metadata"), this); m_showMetadataViewAct->setShortcut(tr("Ctrl+5")); m_showMetadataViewAct->setStatusTip(tr("Show the metadata view")); connect(m_showMetadataViewAct, SIGNAL(triggered()), this, SLOT(showMetadataView())); m_showConnectorsViewAct = new QAction(tr("Show Connectors"), this); m_showConnectorsViewAct->setShortcut(tr("Ctrl+6")); m_showConnectorsViewAct->setStatusTip(tr("Show the connector metadata in a list view")); connect(m_showConnectorsViewAct, SIGNAL(triggered()), this, SLOT(showConnectorsView())); m_hideOtherViewsAct = new QAction(tr("Make only this view visible"), this); m_hideOtherViewsAct->setStatusTip(tr("The part will only be visible in this view and icon view")); connect(m_hideOtherViewsAct, SIGNAL(triggered()), this, SLOT(hideOtherViews())); } void PEMainWindow::createViewMenu() { MainWindow::createViewMenu(); bool afterNext = false; foreach (QAction * action, m_viewMenu->actions()) { if (action == m_setBackgroundColorAct) { afterNext = true; } else if (afterNext) { m_viewMenu->insertSeparator(action); m_viewMenu->insertAction(action, m_hideOtherViewsAct); break; } } afterNext = false; foreach (QAction * action, m_viewMenu->actions()) { if (action == m_showPCBAct) { afterNext = true; } else if (afterNext) { m_viewMenu->insertAction(action, m_showIconAct); m_viewMenu->insertAction(action, m_showMetadataViewAct); m_viewMenu->insertAction(action, m_showConnectorsViewAct); break; } } m_numFixedActionsInViewMenu = m_viewMenu->actions().size(); } void PEMainWindow::showMetadataView() { setCurrentTabIndex(MetadataViewIndex); } void PEMainWindow::showConnectorsView() { setCurrentTabIndex(ConnectorsViewIndex); } void PEMainWindow::showIconView() { setCurrentTabIndex(IconViewIndex); } void PEMainWindow::changeSpecialProperty(const QString & name, const QString & value) { QHash oldProperties = getOldProperties(); if (value.isEmpty()) { QMessageBox::warning(NULL, tr("Blank not allowed"), tr("The value of '%1' can not be blank.").arg(name)); m_metadataView->resetProperty(name, value); return; } if (oldProperties.value(name) == value) { return; } QHash newProperties(oldProperties); newProperties.insert(name, value); ChangePropertiesCommand * cpc = new ChangePropertiesCommand(this, oldProperties, newProperties, NULL); cpc->setText(tr("Change %1 to %2").arg(name).arg(value)); cpc->setSkipFirstRedo(); changeProperties(newProperties, false); m_undoStack->waitPush(cpc, SketchWidget::PropChangeDelay); } void PEMainWindow::metadataChanged(const QString & name, const QString & value) { qDebug() << "metadata changed"; if (name.compare("family") == 0) { changeSpecialProperty(name, value); return; } if (name.compare("variant") == 0) { QString family = m_metadataView->family(); QHash variants = m_referenceModel->allPropValues(family, "variant"); QStringList values = variants.values(value); if (m_canSave) { QString moduleID = m_fzpDocument.documentElement().attribute("moduleId"); values.removeOne(moduleID); } if (values.count() > 0) { QMessageBox::warning(NULL, tr("Must be unique"), tr("Variant '%1' is in use. The variant name must be unique.").arg(value)); return; } changeSpecialProperty(name, value); return; } QString menuText = (name.compare("description") == 0) ? tr("Change description") : tr("Change %1 to '%2'").arg(name).arg(value); // called from metadataView QDomElement root = m_fzpDocument.documentElement(); QDomElement element = root.firstChildElement(name); QString oldValue = element.text(); if (oldValue == value) return; ChangeMetadataCommand * cmc = new ChangeMetadataCommand(this, name, oldValue, value, NULL); cmc->setText(menuText); cmc->setSkipFirstRedo(); changeMetadata(name, value, false); m_undoStack->waitPush(cmc, SketchWidget::PropChangeDelay); } void PEMainWindow::changeMetadata(const QString & name, const QString & value, bool updateDisplay) { // called from command object QDomElement root = m_fzpDocument.documentElement(); TextUtils::replaceElementChildText(root, name, value); //QString test = m_fzpDocument.toString(); if (updateDisplay) { m_metadataView->initMetadata(m_fzpDocument); } } void PEMainWindow::tagsChanged(const QStringList & newTags) { // called from metadataView QDomElement root = m_fzpDocument.documentElement(); QDomElement tags = root.firstChildElement("tags"); QDomElement tag = tags.firstChildElement("tag"); QStringList oldTags; while (!tag.isNull()) { oldTags << tag.text(); tag = tag.nextSiblingElement("tag"); } ChangeTagsCommand * ctc = new ChangeTagsCommand(this, oldTags, newTags, NULL); ctc->setText(tr("Change tags")); ctc->setSkipFirstRedo(); changeTags(newTags, false); m_undoStack->waitPush(ctc, SketchWidget::PropChangeDelay); } void PEMainWindow::changeTags(const QStringList & newTags, bool updateDisplay) { QDomElement root = m_fzpDocument.documentElement(); QDomElement tags = root.firstChildElement("tags"); QDomElement tag = tags.firstChildElement("tag"); while (!tag.isNull()) { tags.removeChild(tag); tag = tags.firstChildElement("tag"); } foreach (QString newTag, newTags) { QDomElement tag = m_fzpDocument.createElement("tag"); tags.appendChild(tag); TextUtils::replaceChildText(tag, newTag); } if (updateDisplay) { m_metadataView->initMetadata(m_fzpDocument); } } void PEMainWindow::propertiesChanged(const QHash & newProperties) { qDebug() << "properties changed"; QStringList keys = newProperties.keys(); if (keys.contains("family", Qt::CaseInsensitive)) { QMessageBox::warning(NULL, tr("Duplicate problem"), tr("Duplicate 'family' property not allowed")); return; } if (keys.contains("variant", Qt::CaseInsensitive)) { QMessageBox::warning(NULL, tr("Duplicate problem"), tr("Duplicate 'variant' property not allowed")); return; } // called from metadataView QHash oldProperties = getOldProperties(); ChangePropertiesCommand * cpc = new ChangePropertiesCommand(this, oldProperties, newProperties, NULL); cpc->setText(tr("Change properties")); cpc->setSkipFirstRedo(); changeProperties(newProperties, false); m_undoStack->waitPush(cpc, SketchWidget::PropChangeDelay); } void PEMainWindow::changeProperties(const QHash & newProperties, bool updateDisplay) { bool incomingFamily = newProperties.keys().contains("family"); bool incomingVariant = newProperties.keys().contains("variant"); QDomElement root = m_fzpDocument.documentElement(); QDomElement properties = root.firstChildElement("properties"); QDomElement prop = properties.firstChildElement("property"); while (!prop.isNull()) { QDomElement next = prop.nextSiblingElement("property"); bool doRemove = true; if (prop.attribute("name") == "family" && !incomingFamily) doRemove = false; if (prop.attribute("name") == "variant" && !incomingVariant) doRemove = false; if (doRemove) { properties.removeChild(prop); } prop = next; } foreach (QString name, newProperties.keys()) { QDomElement prop = m_fzpDocument.createElement("property"); properties.appendChild(prop); prop.setAttribute("name", name); TextUtils::replaceChildText(prop, newProperties.value(name)); } if (updateDisplay) { m_metadataView->initMetadata(m_fzpDocument); } } QHash PEMainWindow::getOldProperties() { QDomElement root = m_fzpDocument.documentElement(); QDomElement properties = root.firstChildElement("properties"); QDomElement prop = properties.firstChildElement("property"); QHash oldProperties; while (!prop.isNull()) { QString name = prop.attribute("name"); QString value = prop.text(); oldProperties.insert(name, value); prop = prop.nextSiblingElement("property"); } return oldProperties; } void PEMainWindow::connectorMetadataChanged(ConnectorMetadata * cmd) { int index; QDomElement connector = findConnector(cmd->connectorID, index); if (connector.isNull()) return; ConnectorMetadata oldcmd; fillInMetadata(connector, oldcmd); ChangeConnectorMetadataCommand * ccmc = new ChangeConnectorMetadataCommand(this, &oldcmd, cmd, NULL); ccmc->setText(tr("Change connector %1").arg(cmd->connectorName)); bool skipFirstRedo = (sender() == m_connectorsView); if (skipFirstRedo) { ccmc->setSkipFirstRedo(); changeConnectorElement(connector, cmd); initConnectors(false); } m_undoStack->waitPush(ccmc, SketchWidget::PropChangeDelay); } QDomElement PEMainWindow::findConnector(const QString & id, int & index) { QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); index = 0; while (!connector.isNull()) { if (id.compare(connector.attribute("id")) == 0) { return connector; } connector = connector.nextSiblingElement("connector"); index++; } return QDomElement(); } void PEMainWindow::changeConnectorMetadata(ConnectorMetadata * cmd, bool updateDisplay) { int index; QDomElement connector = findConnector(cmd->connectorID, index); if (connector.isNull()) return; changeConnectorElement(connector, cmd); if (updateDisplay) { initConnectors(true); m_peToolView->setCurrentConnector(index); } } void PEMainWindow::changeConnectorElement(QDomElement & connector, ConnectorMetadata * cmd) { connector.setAttribute("name", cmd->connectorName); connector.setAttribute("id", cmd->connectorID); connector.setAttribute("type", Connector::connectorNameFromType(cmd->connectorType)); TextUtils::replaceElementChildText(connector, "description", cmd->connectorDescription); #ifndef QT_NO_DEBUG QDomElement description = connector.firstChildElement("description"); QString text = description.text(); DebugDialog::debug(QString("description %1").arg(text)); #endif } void PEMainWindow::initSvgTree(SketchWidget * sketchWidget, ItemBase * itemBase, QDomDocument & svgDocument) { QString errorStr; int errorLine; int errorColumn; QDomDocument tempSvgDoc; QFile file(itemBase->filename()); if (!tempSvgDoc.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse svg: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } if (itemBase->viewID() == ViewLayer::PCBView) { QDomElement root = tempSvgDoc.documentElement(); QDomElement copper0 = TextUtils::findElementWithAttribute(root, "id", "copper0"); QDomElement copper1 = TextUtils::findElementWithAttribute(root, "id", "copper1"); if (!copper0.isNull() && !copper1.isNull()) { QDomElement parent0 = copper0.parentNode().toElement(); QDomElement parent1 = copper1.parentNode().toElement(); if (parent0.attribute("id") == "copper1") ; else if (parent1.attribute("id") == "copper0") ; else { QMessageBox::warning(NULL, tr("SVG problem"), tr("This version of the new Parts Editor can not deal with separate copper0 and copper1 layers in '%1'. ").arg(itemBase->filename()) + tr("So editing may produce an invalid PCB view image")); } } } int z = PegiZ; FSvgRenderer tempRenderer; QByteArray rendered = tempRenderer.loadSvg(tempSvgDoc.toByteArray(), "", false); // cleans up the svg if (!svgDocument.setContent(rendered, true, &errorStr, &errorLine, &errorColumn)) { DebugDialog::debug(QString("unable to parse svg (2): %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn)); return; } TextUtils::gornTree(svgDocument); FSvgRenderer renderer; renderer.loadSvg(svgDocument.toByteArray(), "", false); QHash pegiHash; QList traverse; traverse << svgDocument.documentElement(); while (traverse.count() > 0) { QDomElement element = traverse.takeFirst(); // depth first QList next; QDomElement child = element.firstChildElement(); while (!child.isNull()) { next << child; child = child.nextSiblingElement(); } while (next.count() > 0) { traverse.push_front(next.takeLast()); } bool isG = false; bool isSvg = false; QString tagName = element.tagName(); if (tagName.compare("rect") == 0); else if (tagName.compare("g") == 0) { isG = true; } else if (tagName.compare("svg") == 0) { isSvg = true; } else if (tagName.compare("circle") == 0); else if (tagName.compare("ellipse") == 0); else if (tagName.compare("path") == 0); else if (tagName.compare("line") == 0); else if (tagName.compare("polyline") == 0); else if (tagName.compare("polygon") == 0); else if (tagName.compare("text") == 0); else continue; QRectF bounds = getPixelBounds(renderer, element); // known Qt bug: boundsOnElement returns zero width and height for text elements. if (bounds.width() > 0 && bounds.height() > 0) { PEGraphicsItem * pegi = makePegi(bounds.size(), bounds.topLeft(), itemBase, element, z++); pegiHash.insert(element.attribute("id"), pegi); /* QString string; QTextStream stream(&string); element.save(stream, 0); DebugDialog::debug("........"); DebugDialog::debug(string); */ } } QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QString svgID, terminalID; bool ok = ViewLayer::getConnectorSvgIDs(connector, sketchWidget->viewID(), svgID, terminalID); if (ok) { PEGraphicsItem * terminalItem = pegiHash.value(terminalID, NULL); PEGraphicsItem * pegi = pegiHash.value(svgID); if (pegi == NULL) { DebugDialog::debug(QString("missing pegi for svg id %1").arg(svgID)); } else { QPointF terminalPoint = pegi->rect().center(); if (terminalItem) { terminalPoint = terminalItem->pos() - pegi->pos() + terminalItem->rect().center(); } pegi->setTerminalPoint(terminalPoint); pegi->showTerminalPoint(true); } } connector = connector.nextSiblingElement("connector"); } } void PEMainWindow::highlightSlot(PEGraphicsItem * pegi) { if (m_peToolView) { bool enableTerminalPointControls = anyMarquee(); bool vis = anyVisible(); m_peToolView->enableConnectorChanges(vis && pegi->showingMarquee(), vis && enableTerminalPointControls, m_connectorList.count() > 0, vis); } if (m_peSvgView) { m_peSvgView->highlightElement(pegi); } } void PEMainWindow::initConnectors(bool updateConnectorsView) { QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); m_connectorList.clear(); while (!connector.isNull()) { m_connectorList.append(connector); connector = connector.nextSiblingElement("connector"); } qSort(m_connectorList.begin(), m_connectorList.end(), byID); if (updateConnectorsView) { m_connectorsView->initConnectors(&m_connectorList); } m_peToolView->initConnectors(&m_connectorList); updateAssignedConnectors(); } void PEMainWindow::switchedConnector(int ix) { if (m_currentGraphicsView == NULL) return; switchedConnector(ix, m_currentGraphicsView); } void PEMainWindow::switchedConnector(int ix, SketchWidget * sketchWidget) { if (m_connectorList.count() == 0) return; QDomElement element = m_connectorList.at(ix); if (element.isNull()) return; QString svgID, terminalID; if (!ViewLayer::getConnectorSvgIDs(element, sketchWidget->viewID(), svgID, terminalID)) return; QList pegiList = getPegiList(sketchWidget); bool gotOne = false; foreach (PEGraphicsItem * pegi, pegiList) { QDomElement pegiElement = pegi->element(); if (pegiElement.attribute("id").compare(svgID) == 0) { gotOne = true; pegi->showMarquee(true); pegi->setHighlighted(true); m_peToolView->setTerminalPointCoords(pegi->terminalPoint()); m_peToolView->setTerminalPointLimits(pegi->rect().size()); break; } } if (!gotOne) { foreach (PEGraphicsItem * pegi, pegiList) { pegi->showMarquee(false); pegi->setHighlighted(false); } } } void PEMainWindow::loadImage() { if (m_currentGraphicsView == NULL) return; QStringList extras; extras.append(""); extras.append(""); QString imageFiles; if (m_currentGraphicsView->viewID() == ViewLayer::PCBView) { imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4 %5);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;gEDA Footprint Files (%4);;Kicad Module Files (%5)"); // extras[0] = "*.fp"; extras[1] = "*.mod"; } else { imageFiles = tr("Image Files (%1 %2 %3);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3)%4%5"); } /* if (m_currentGraphicsView->viewID() == ViewLayer::SchematicView) { extras[0] = "*.lib"; imageFiles = tr("Image & Footprint Files (%1 %2 %3 %4);;SVG Files (%1);;JPEG Files (%2);;PNG Files (%3);;Kicad Schematic Files (%4)%5"); // } */ QString initialPath = FolderUtils::openSaveFolder(); ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID()); ItemBase * itemBase = viewThing->itemBase; if (itemBase) { initialPath = itemBase->filename(); } QString origPath = FolderUtils::getOpenFileName(this, tr("Open Image"), initialPath, imageFiles.arg("*.svg").arg("*.jpg *.jpeg").arg("*.png").arg(extras[0]).arg(extras[1]) ); if (origPath.isEmpty()) { return; // Cancel pressed } QString newReferenceFile; QString svg; if (origPath.endsWith(".svg")) { newReferenceFile = getSvgReferenceFile(origPath); QFile origFile(origPath); if (!origFile.open(QFile::ReadOnly)) { QMessageBox::warning(NULL, tr("Conversion problem"), tr("Unable to load '%1'").arg(origPath)); return; } svg = origFile.readAll(); origFile.close(); if (svg.contains("coreldraw", Qt::CaseInsensitive) && svg.contains("cdata", Qt::CaseInsensitive)) { QMessageBox::warning(NULL, tr("Conversion problem"), tr("The SVG file '%1' appears to have been exported from CorelDRAW without the 'presentation attributes' setting. ").arg(origPath) + tr("Please re-export the SVG file using that setting, and try loading again.") ); return; } QStringList availFonts = InstalledFonts::InstalledFontsList.toList(); if (availFonts.count() > 0) { QString destFont = availFonts.at(0); foreach (QString f, availFonts) { if (f.contains("droid", Qt::CaseInsensitive)) { destFont = f; break; } } bool reallyFixed = false; TextUtils::fixFonts(svg, destFont, reallyFixed); if (reallyFixed) { QMessageBox::information(NULL, tr("Fonts"), tr("Fritzing currently only supports OCRA and Droid fonts--these have been substituted in for the fonts in '%1'").arg(origPath)); } } } else { newReferenceFile = origPath; if (origPath.endsWith("png") || origPath.endsWith("jpg") || origPath.endsWith("jpeg")) { QString message = tr("You may use a PNG or JPG image to construct your part, but it is better to use an SVG. ") + tr("PNG and JPG images retain their nature as bitmaps and do not look good when scaled--") + tr("so for Fritzing parts it is best to use PNG and JPG only as placeholders.") ; QMessageBox::information(NULL, tr("Use of PNG and JPG discouraged"), message); } try { svg = createSvgFromImage(origPath); } catch (const QString & msg) { QMessageBox::warning(NULL, tr("Conversion problem"), tr("Unable to load image file '%1': \n\n%2").arg(origPath).arg(msg)); return; } } if (svg.isEmpty()) { QMessageBox::warning(NULL, tr("Conversion problem"), tr("Unable to load image file '%1'").arg(origPath)); return; } QString errorStr; int errorLine; int errorColumn; QDomDocument doc; bool result = doc.setContent(svg.toUtf8(), &errorStr, &errorLine, &errorColumn); if (!result) { QMessageBox::warning(NULL, tr("SVG problem"), tr("Unable to parse '%1': %2 line:%3 column:%4").arg(origPath).arg(errorStr).arg(errorLine).arg(errorColumn)); return; } if (m_currentGraphicsView == m_pcbGraphicsView) { QDomElement root = doc.documentElement(); QDomElement check = TextUtils::findElementWithAttribute(root, "id", "copper1"); if (check.isNull()) { check = TextUtils::findElementWithAttribute(root, "id", "copper0"); } if (check.isNull()) { QString message = tr("There are no copper layers defined in: %1. ").arg(origPath) + tr("See this explanation.") + tr("

This will not be a problem in the next release of the Parts Editor, ") + tr("but for now please modify the file according to the instructions in the link.") ; QMessageBox::warning(NULL, tr("SVG problem"), message); return; } } TextUtils::fixMuch(svg, true); insertDesc(newReferenceFile, svg); QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(m_currentGraphicsView); bool success = writeXml(newPath, removeGorn(svg), true); if (!success) { QMessageBox::warning(NULL, tr("Copy problem"), tr("Unable to make a local copy of: '%1'").arg(origPath)); return; } QFileInfo info(origPath); QUndoCommand * parentCommand = new QUndoCommand(QString("Load '%1'").arg(info.fileName())); new ChangeSvgCommand(this, m_currentGraphicsView, itemBase->filename(), newPath, parentCommand); m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } QString PEMainWindow::createSvgFromImage(const QString &origFilePath) { if (origFilePath.endsWith(".fp")) { // this is a geda footprint file GedaElement2Svg geda; QString svg = geda.convert(origFilePath, false); return svg; } if (origFilePath.endsWith(".lib")) { // Kicad schematic library file QStringList defs = KicadSchematic2Svg::listDefs(origFilePath); if (defs.count() == 0) { throw tr("no schematics found in %1").arg(origFilePath); } QString def; if (defs.count() > 1) { KicadModuleDialog kmd(tr("schematic part"), origFilePath, defs, this); int result = kmd.exec(); if (result != QDialog::Accepted) { return ""; } def = kmd.selectedModule(); } else { def = defs.at(0); } KicadSchematic2Svg kicad; QString svg = kicad.convert(origFilePath, def); return svg; } if (origFilePath.endsWith(".mod")) { // Kicad footprint (Module) library file QStringList modules = KicadModule2Svg::listModules(origFilePath); if (modules.count() == 0) { throw tr("no footprints found in %1").arg(origFilePath); } QString module; if (modules.count() > 1) { KicadModuleDialog kmd("footprint", origFilePath, modules, this); int result = kmd.exec(); if (result != QDialog::Accepted) { return ""; } module = kmd.selectedModule(); } else { module = modules.at(0); } KicadModule2Svg kicad; QString svg = kicad.convert(origFilePath, module, false); return svg; } // deal with png, jpg: QBuffer buffer; QImage img(origFilePath); QSvgGenerator svgGenerator; svgGenerator.setResolution(90); svgGenerator.setOutputDevice(&buffer); QSize sz = img.size(); svgGenerator.setSize(sz); svgGenerator.setViewBox(QRect(0, 0, sz.width(), sz.height())); QPainter svgPainter(&svgGenerator); svgPainter.drawImage(QPoint(0,0), img); svgPainter.end(); return QString(buffer.buffer()); } /* QString PEMainWindow::makeSvgPath(const QString & referenceFile, SketchWidget * sketchWidget, bool useIndex) { QString rf = referenceFile; if (!rf.isEmpty()) rf += "_"; QString viewName = ViewLayer::viewIDNaturalName(sketchWidget->viewID()); QString indexString; if (useIndex) indexString = QString("_%3").arg(m_fileIndex++); return QString("%1/%2%3_%1%4.svg").arg(viewName).arg(rf).arg(m_guid).arg(indexString); } */ QString PEMainWindow::makeSvgPath2(SketchWidget * sketchWidget) { QString viewName = ViewLayer::viewIDNaturalName(sketchWidget->viewID()); return QString("%1/%2_%3_%4_%1.svg").arg(viewName).arg(m_prefix).arg(m_guid).arg(m_fileIndex); } void PEMainWindow::changeSvg(SketchWidget * sketchWidget, const QString & filename, int changeDirection) { QDomElement fzpRoot = m_fzpDocument.documentElement(); setImageAttribute(fzpRoot, filename, sketchWidget->viewID()); foreach (ViewThing * viewThing, m_viewThings.values()) { foreach(ItemBase * lk, viewThing->itemBase->layerKin()) { delete lk; } delete viewThing->itemBase; viewThing->itemBase = NULL; } updateChangeCount(sketchWidget, changeDirection); reload(false); } QString PEMainWindow::makeDirName() { return "fritzing_pe_" + m_guid; } QString PEMainWindow::saveFzp() { QDir dir = QDir::temp(); QString dirName = makeDirName(); dir.mkdir(dirName); dir.cd(dirName); QString fzpPath = dir.absoluteFilePath(QString("%1_%2_%3.fzp").arg(m_prefix).arg(m_guid).arg(m_fileIndex++)); DebugDialog::debug("temp path " + fzpPath); writeXml(fzpPath, m_fzpDocument.toString(), true); return fzpPath; } void PEMainWindow::reload(bool firstTime) { Q_UNUSED(firstTime); CursorMaster::instance()->addCursor(this, Qt::WaitCursor); QList toDelete; foreach (ViewThing * viewThing, m_viewThings.values()) { if (viewThing->sketchWidget == NULL) continue; foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase) toDelete << itemBase; } } foreach (ItemBase * itemBase, toDelete) { delete itemBase; } killPegi(); QString fzpPath = saveFzp(); // needs a document somewhere to set up connectors--not part of the undo stack ModelPart * modelPart = new ModelPart(m_fzpDocument, fzpPath, ModelPart::Part); long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(QPointF(0, 0)); QList itemBases; itemBases << m_iconGraphicsView->addItem(modelPart, m_iconGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL); itemBases << m_breadboardGraphicsView->addItem(modelPart, m_breadboardGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL); itemBases << m_schematicGraphicsView->addItem(modelPart, m_schematicGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL); itemBases << m_pcbGraphicsView->addItem(modelPart, m_pcbGraphicsView->defaultViewLayerPlacement(modelPart), BaseCommand::SingleView, viewGeometry, newID, -1, NULL); foreach (ItemBase * itemBase, itemBases) { ViewThing * viewThing = m_viewThings.value(itemBase->viewID()); viewThing->itemBase = itemBase; viewThing->referenceFile = getSvgReferenceFile(itemBase->filename()); } m_metadataView->initMetadata(m_fzpDocument); QList widgets; widgets << m_metadataView << m_peToolView << m_connectorsView; foreach (QWidget * widget, widgets) { QList lineEdits = widget->findChildren(); foreach (QLineEdit * lineEdit, lineEdits) { lineEdit->installEventFilter(this); } QList textEdits = widget->findChildren(); foreach (QTextEdit * textEdit, textEdits) { textEdit->installEventFilter(this); } } if (m_currentGraphicsView) { showing(m_currentGraphicsView); } foreach (ViewThing * viewThing, m_viewThings.values()) { initSvgTree(viewThing->sketchWidget, viewThing->itemBase, *viewThing->document); } initConnectors(true); m_connectorsView->setSMD(modelPart->flippedSMD()); foreach (ViewThing * viewThing, m_viewThings.values()) { // TODO: may have to revisit this and move all pegi items //viewThing->itemBase->setMoveLock(true); //viewThing->itemBase->setItemIsSelectable(false); viewThing->itemBase->setAcceptsMousePressLegEvent(false); viewThing->itemBase->setSwappable(false); viewThing->sketchWidget->hideConnectors(true); viewThing->everZoomed = false; } switchedConnector(m_peToolView->currentConnectorIndex()); // processEventBlocker might be enough? QTimer::singleShot(10, this, SLOT(initZoom())); CursorMaster::instance()->removeCursor(this); } void PEMainWindow::busModeChanged(bool state) { if (m_currentGraphicsView == NULL) return; if (!state) { foreach (ViewThing * viewThing, m_viewThings.values()) { viewThing->busMode = false; viewThing->sketchWidget->hideConnectors(true); } deleteBuses(); reload(false); return; } QList pegiList = getPegiList(m_currentGraphicsView); reload(false); QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); foreach (ViewThing * viewThing, m_viewThings.values()) { // items on pegiList no longer exist after reload so get them now pegiList = getPegiList(viewThing->sketchWidget); viewThing->sketchWidget->hideConnectors(true); foreach (PEGraphicsItem * pegi, pegiList) { pegi->setVisible(false); } // show connectorItems that have connectors assigned QStringList connectorIDs; QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement p = ViewLayer::getConnectorPElement(connector, viewThing->sketchWidget->viewID()); QString id = p.attribute("svgId"); foreach (PEGraphicsItem * pegi, pegiList) { QDomElement pegiElement = pegi->element(); if (pegiElement.attribute("id").compare(id) == 0) { connectorIDs.append(connector.attribute("id")); break; } } connector = connector.nextSiblingElement("connector"); } if (connectorIDs.count() > 0) { viewThing->itemBase->showConnectors(connectorIDs); } } displayBuses(); m_peToolView->enableConnectorChanges(false, false, m_connectorList.count() > 0, false); } void PEMainWindow::pickModeChanged(bool state) { if (m_currentGraphicsView == NULL) return; // never actually get state == false; m_inPickMode = state; if (m_inPickMode) { QApplication::setOverrideCursor(Qt::PointingHandCursor); foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi) pegi->setPickAppearance(true); } qApp->installEventFilter(this); } } void PEMainWindow::pegiTerminalPointMoved(PEGraphicsItem * pegi, QPointF p) { // called while terminal point is being dragged, no need for an undo operation Q_UNUSED(pegi); m_peToolView->setTerminalPointCoords(p); } void PEMainWindow::pegiTerminalPointChanged(PEGraphicsItem * pegi, QPointF before, QPointF after) { // called at the end of terminal point dragging terminalPointChangedAux(pegi, before, after); } void PEMainWindow::pegiMousePressed(PEGraphicsItem * pegi, bool & ignore) { ignore = false; if (m_useNextPick) { m_useNextPick = false; relocateConnector(pegi); return; } ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID()); if (pegi->itemBase() != viewThing->itemBase) { ignore = true; return; } QString id = pegi->element().attribute("id"); if (id.isEmpty()) return; QDomElement current = m_connectorList.at(m_peToolView->currentConnectorIndex()); if (current.attribute("id").compare(id) == 0) { // already there return; } // if a connector has been clicked, make it the current connector QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement p = ViewLayer::getConnectorPElement(connector, m_currentGraphicsView->viewID()); if (p.attribute("svgId") == id || p.attribute("terminalId") == id) { m_peToolView->setCurrentConnector(connector); return; } connector = connector.nextSiblingElement("connector"); } } void PEMainWindow::pegiMouseReleased(PEGraphicsItem *) { } void PEMainWindow::relocateConnector(PEGraphicsItem * pegi) { QString newGorn = pegi->element().attribute("gorn"); QDomDocument * svgDoc = m_viewThings.value(m_currentGraphicsView->viewID())->document; QDomElement svgRoot = svgDoc->documentElement(); QDomElement newGornElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", newGorn); if (newGornElement.isNull()) { return; } QDomElement fzpRoot = m_fzpDocument.documentElement(); QDomElement connectors = fzpRoot.firstChildElement("connectors"); QDomElement currentConnectorElement = m_connectorList.at(m_peToolView->currentConnectorIndex()); QString svgID, terminalID; if (!ViewLayer::getConnectorSvgIDs(currentConnectorElement, m_currentGraphicsView->viewID(), svgID, terminalID)) { return; } QDomElement oldGornElement = TextUtils::findElementWithAttribute(svgRoot, "id", svgID); QString oldGorn = oldGornElement.attribute("gorn"); QString oldGornTerminal; if (!terminalID.isEmpty()) { QDomElement element = TextUtils::findElementWithAttribute(svgRoot, "id", terminalID); oldGornTerminal = element.attribute("gorn"); } if (newGornElement.attribute("gorn").compare(oldGornElement.attribute("gorn")) == 0) { // no change return; } RelocateConnectorSvgCommand * rcsc = new RelocateConnectorSvgCommand(this, m_currentGraphicsView, svgID, terminalID, oldGorn, oldGornTerminal, newGorn, "", NULL); rcsc->setText(tr("Relocate connector %1").arg(currentConnectorElement.attribute("name"))); m_undoStack->waitPush(rcsc, SketchWidget::PropChangeDelay); } void PEMainWindow::relocateConnectorSvg(SketchWidget * sketchWidget, const QString & svgID, const QString & terminalID, const QString & oldGorn, const QString & oldGornTerminal, const QString & newGorn, const QString & newGornTerminal, int changeDirection) { ViewLayer::ViewID viewID = sketchWidget->viewID(); ViewThing * viewThing = m_viewThings.value(viewID); QDomDocument * svgDoc = viewThing->document; QDomElement svgRoot = svgDoc->documentElement(); QDomElement oldGornElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", oldGorn); QDomElement oldGornTerminalElement; if (!oldGornTerminal.isEmpty()) { oldGornTerminalElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", oldGornTerminal); } QDomElement newGornElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", newGorn); QDomElement newGornTerminalElement; if (!newGornTerminal.isEmpty()) { newGornTerminalElement = TextUtils::findElementWithAttribute(svgRoot, "gorn", newGornTerminal); } if (!oldGornElement.isNull()) { oldGornElement.removeAttribute("id"); oldGornElement.removeAttribute("oldid"); // remove oldid so removeGorn() call later doesn't restore anything by accident } if (!oldGornTerminalElement.isNull()) { oldGornTerminalElement.removeAttribute("id"); oldGornTerminalElement.removeAttribute("oldid"); } if (!newGornElement.isNull()) { newGornElement.setAttribute("id", svgID); newGornElement.removeAttribute("oldid"); } if (!newGornTerminalElement.isNull()) { newGornTerminalElement.setAttribute("id", terminalID); newGornTerminalElement.removeAttribute("oldid"); } QDomElement fzpRoot = m_fzpDocument.documentElement(); QDomElement connectors = fzpRoot.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QString cSvgID, cTerminalID; if (ViewLayer::getConnectorSvgIDs(connector, viewID, cSvgID, cTerminalID)) { if (cSvgID == svgID) break; } connector = connector.nextSiblingElement("connector"); } if (connector.isNull()) return; QDomElement p = ViewLayer::getConnectorPElement(connector, viewID); if (terminalID.isEmpty()) { p.removeAttribute("terminalId"); } else { p.setAttribute("terminalId", terminalID); } // update svg in case there is a subsequent call to reload QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(sketchWidget); QString svg = TextUtils::svgNSOnly(svgDoc->toString()); writeXml(newPath, removeGorn(svg), true); setImageAttribute(fzpRoot, newPath, viewID); foreach (QGraphicsItem * item, sketchWidget->scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi == NULL) continue; QDomElement element = pegi->element(); if (element.attribute("gorn").compare(newGorn) == 0) { pegi->setHighlighted(true); pegi->showMarquee(true); pegi->showTerminalPoint(true); switchedConnector(m_peToolView->currentConnectorIndex(), sketchWidget); } else if (element.attribute("gorn").compare(oldGorn) == 0) { pegi->showTerminalPoint(false); pegi->showMarquee(false); } } updateAssignedConnectors(); updateChangeCount(sketchWidget, changeDirection); } bool PEMainWindow::save() { if (!canSave()) { return saveAs(false); } return saveAs(true); } bool PEMainWindow::saveAs() { return saveAs(false); } bool PEMainWindow::saveAs(bool overWrite) { bool ok = false; QString prefix = QInputDialog::getText( this, tr("Filename prefix"), tr("

Please enter a prefix to help you identify the part files.
" "The file names will have the form 'PREFIX_%1'.
" "(It is not necessary to change the proposed prefix, since a unique suffix is always added.)

").arg(m_guid), QLineEdit::Normal, m_prefix, &ok ); if (!ok || prefix.isEmpty()) return false; if (prefix != m_prefix) overWrite = false; m_prefix = prefix; QDomElement fzpRoot = m_fzpDocument.documentElement(); QList affectedWindows; QList allWindows; if (overWrite) { foreach (QWidget *widget, QApplication::topLevelWidgets()) { MainWindow *mainWindow = qobject_cast(widget); if (mainWindow == NULL) continue; if (qobject_cast(mainWindow) != NULL) continue; allWindows.append(mainWindow); if (mainWindow->usesPart(m_originalModuleID)) { affectedWindows.append(mainWindow); } } if (affectedWindows.count() > 0 && !m_gaveSaveWarning) { QMessageBox messageBox(this); messageBox.setWindowTitle(tr("Sketch Change Warning")); QString message; if (affectedWindows.count() == 1) { message = tr("The open sketch '%1' uses the part you are editing. ").arg(affectedWindows.first()->windowTitle()); message += tr("Saving this part will make a change to the sketch that cannot be undone."); } else { message = tr("The open sketches "); for (int i = 0; i < affectedWindows.count() - 1; i++) { message += tr("'%1', ").arg(affectedWindows.at(i)->windowTitle()); } message += tr("and '%1' ").arg(affectedWindows.last()->windowTitle()); message += tr("Saving this part will make a change to these sketches that cannot be undone."); } message += tr("\n\nGo ahead and save?"); messageBox.setText(message); messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); messageBox.setDefaultButton(QMessageBox::Cancel); messageBox.setIcon(QMessageBox::Warning); messageBox.setWindowModality(Qt::WindowModal); messageBox.setButtonText(QMessageBox::Ok, tr("Save")); messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel")); QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec(); if (answer != QMessageBox::Ok) { return false; } m_gaveSaveWarning = true; } } QDomElement views = fzpRoot.firstChildElement("views"); QHash svgPaths; foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) { ViewThing * viewThing = m_viewThings.value(viewID); QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewID)); QDomElement layers = view.firstChildElement("layers"); QString currentSvgPath = layers.attribute("image"); svgPaths.insert(viewID, currentSvgPath); QMultiHash extraSvg; QDomDocument writeDoc = viewThing->document->cloneNode(true).toDocument(); QDomElement svgRoot = writeDoc.documentElement(); double svgWidth, svgHeight, vbWidth, vbHeight; bool svgOK = TextUtils::getSvgSizes(writeDoc, svgWidth, svgHeight, vbWidth, vbHeight); if (svgOK) { QHash svgHash; foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->layerKinChief() == viewThing->itemBase) continue; double factor; QString string = itemBase->layerKinChief()->retrieveSvg(itemBase->viewLayerID(), svgHash, false, vbWidth / svgWidth, factor); if (!string.isEmpty()) { QPointF d = itemBase->pos() - viewThing->itemBase->pos(); double dx = d.x() * (vbWidth / svgWidth) / GraphicsUtils::SVGDPI; double dy = d.y() * (vbWidth / svgWidth) / GraphicsUtils::SVGDPI; string = QString("\n%3\n\n").arg(dx).arg(dy).arg(string); extraSvg.insert(itemBase->viewLayerID(), string); } } } // deal with copper1 and copper0: // find innermost // don't save twice QDomElement copperChild; LayerList sketchLayers = viewThing->sketchWidget->viewLayers().keys(); if (sketchLayers.contains(ViewLayer::Copper0) && sketchLayers.contains(ViewLayer::Copper1)) { QDomElement copper0 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper0"); QDomElement copper1 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper1"); if (!copper0.isNull() && !copper1.isNull()) { QDomElement parent0 = copper0.parentNode().toElement(); QDomElement parent1 = copper1.parentNode().toElement(); if (parent0.attribute("id") == "copper1") { copperChild = copper0; } else if (parent1.attribute("id") == "copper0") { copperChild = copper1; } } if (!copperChild.isNull()) { if (extraSvg.uniqueKeys().contains(ViewLayer::Copper0)) { extraSvg.remove(ViewLayer::Copper1); } } } foreach (ViewLayer::ViewLayerID viewLayerID, extraSvg.uniqueKeys()) { QStringList svgList = extraSvg.values(viewLayerID); QDomElement svgViewElement = (viewLayerID == ViewLayer::Copper0 && !copperChild.isNull()) ? copperChild : TextUtils::findElementWithAttribute(svgRoot, "id", ViewLayer::viewLayerXmlNameFromID(viewLayerID)); if (svgViewElement.isNull()) { svgViewElement = svgRoot; } foreach (QString svg, svgList) { QDomDocument doc; doc.setContent(svg, true); QDomElement root = doc.documentElement(); foreach (ViewLayer::ViewLayerID vlid, sketchLayers) { removeID(root, ViewLayer::viewLayerXmlNameFromID(vlid)); } svgViewElement.appendChild(doc.documentElement()); } } QString svg = writeDoc.toString(); insertDesc(viewThing->referenceFile, svg); QString svgPath = makeSvgPath2(viewThing->sketchWidget); setImageAttribute(layers, svgPath); QString actualPath = m_userPartsFolderSvgPath + svgPath; bool result = writeXml(actualPath, removeGorn(svg), false); if (!result) { // TODO: warn user } } QDir dir(m_userPartsFolderPath); QString suffix = QString("%1_%2_%3").arg(m_prefix).arg(m_guid).arg(m_fileIndex++); QString fzpPath = dir.absoluteFilePath(QString("%1.fzp").arg(suffix)); if (overWrite) { fzpPath = m_originalFzpPath; } else { fzpRoot.setAttribute("moduleId", suffix); QString family = m_metadataView->family(); QString variant = m_metadataView->variant(); QHash variants = m_referenceModel->allPropValues(family, "variant"); QStringList values = variants.values(variant); if (values.count() > 0) { QString newVariant = makeNewVariant(family); QDomElement properties = fzpRoot.firstChildElement("properties"); replaceProperty("variant", newVariant, properties); m_metadataView->resetProperty("variant", newVariant); } } bool result = writeXml(fzpPath, m_fzpDocument.toString(), false); if (!overWrite) { m_originalFzpPath = fzpPath; m_canSave = true; m_originalModuleID = fzpRoot.attribute("moduleId"); } // restore the set of working svg files foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) { QString svgPath = svgPaths.value(viewID); if (svgPath.isEmpty()) continue; QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewID)); QDomElement layers = view.firstChildElement("layers"); setImageAttribute(layers, svgPath); } ModelPart * modelPart = m_referenceModel->retrieveModelPart(m_originalModuleID); if (modelPart == NULL) { modelPart = m_referenceModel->loadPart(fzpPath, true); emit addToMyPartsSignal(modelPart); } else { m_referenceModel->reloadPart(fzpPath, m_originalModuleID); WaitPushUndoStack undoStack; QUndoCommand * parentCommand = new QUndoCommand; foreach (MainWindow * mainWindow, affectedWindows) { mainWindow->updateParts(m_originalModuleID, parentCommand); } foreach (MainWindow * mainWindow, allWindows) { mainWindow->updatePartsBin(m_originalModuleID); } undoStack.push(parentCommand); } m_autosaveNeeded = false; m_undoStack->setClean(); return result; } void PEMainWindow::updateChangeCount(SketchWidget * sketchWidget, int changeDirection) { ViewThing * viewThing = m_viewThings.value(sketchWidget->viewID()); viewThing->svgChangeCount += changeDirection; } PEGraphicsItem * PEMainWindow::findConnectorItem() { if (m_currentGraphicsView == NULL) return NULL; QDomElement connector = m_connectorList.at(m_peToolView->currentConnectorIndex()); if (connector.isNull()) return NULL; QString svgID, terminalID; bool ok = ViewLayer::getConnectorSvgIDs(connector, m_currentGraphicsView->viewID(), svgID, terminalID); if (!ok) return NULL; QList pegiList = getPegiList(m_currentGraphicsView); foreach (PEGraphicsItem * pegi, pegiList) { if (pegi->element().attribute("id") == svgID) return pegi; } return NULL; } void PEMainWindow::terminalPointChanged(const QString & how) { PEGraphicsItem * pegi = findConnectorItem(); if (pegi == NULL) return; QRectF r = pegi->rect(); QPointF p = r.center(); if (how == "center") { } else if (how == "N") { p.setY(0); } else if (how == "E") { p.setX(r.width()); } else if (how == "S") { p.setY(r.height()); } else if (how == "W") { p.setX(0); } terminalPointChangedAux(pegi, pegi->terminalPoint(), p); // TODO: UndoCommand which changes fzp xml and svg xml } void PEMainWindow::terminalPointChanged(const QString & coord, double value) { PEGraphicsItem * pegi = findConnectorItem(); if (pegi == NULL) return; QPointF p = pegi->terminalPoint(); if (coord == "x") { p.setX(qMax(0.0, qMin(value, pegi->rect().width()))); } else { p.setY(qMax(0.0, qMin(value, pegi->rect().height()))); } terminalPointChangedAux(pegi, pegi->terminalPoint(), p); } void PEMainWindow::terminalPointChangedAux(PEGraphicsItem * pegi, QPointF before, QPointF after) { if (pegi->pendingTerminalPoint() == after) { return; } pegi->setPendingTerminalPoint(after); QDomElement currentConnectorElement = m_connectorList.at(m_peToolView->currentConnectorIndex()); MoveTerminalPointCommand * mtpc = new MoveTerminalPointCommand(this, this->m_currentGraphicsView, currentConnectorElement.attribute("id"), pegi->rect().size(), before, after, NULL); mtpc->setText(tr("Move terminal point")); m_undoStack->waitPush(mtpc, SketchWidget::PropChangeDelay); } void PEMainWindow::getSpinAmount(double & amount) { double zoom = m_currentGraphicsView->currentZoom() / 100; if (zoom == 0) { amount = 1; return; } amount = qMin(1.0, 1.0 / zoom); if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { amount *= 10; } } void PEMainWindow::moveTerminalPoint(SketchWidget * sketchWidget, const QString & connectorID, QSizeF size, QPointF p, int changeDirection) { // TODO: assumes these IDs are unique--need to check if they are not QPointF center(size.width() / 2, size.height() / 2); bool centered = qAbs(center.x() - p.x()) < .05 && qAbs(center.y() - p.y()) < .05; QDomElement fzpRoot = m_fzpDocument.documentElement(); QDomElement connectorElement = TextUtils::findElementWithAttribute(fzpRoot, "id", connectorID); if (connectorElement.isNull()) { DebugDialog::debug(QString("missing connector %1").arg(connectorID)); return; } QDomElement pElement = ViewLayer::getConnectorPElement(connectorElement, sketchWidget->viewID()); QString svgID = pElement.attribute("svgId"); if (svgID.isEmpty()) { DebugDialog::debug(QString("Can't find svgId for connector %1").arg(connectorID)); return; } PEGraphicsItem * connectorPegi = NULL; QList pegiList = getPegiList(sketchWidget); foreach (PEGraphicsItem * pegi, pegiList) { QDomElement pegiElement = pegi->element(); if (pegiElement.attribute("id").compare(svgID) == 0) { connectorPegi = pegi; } } if (connectorPegi == NULL) return; if (centered) { pElement.removeAttribute("terminalId"); // no need to change SVG in this case } else { ViewThing * viewThing = m_viewThings.value(sketchWidget->viewID()); QDomDocument * svgDoc = viewThing->document; QDomElement svgRoot = svgDoc->documentElement(); QDomElement svgConnectorElement = TextUtils::findElementWithAttribute(svgRoot, "id", svgID); if (svgConnectorElement.isNull()) { DebugDialog::debug(QString("Unable to find svg connector element %1").arg(svgID)); return; } QString terminalID = pElement.attribute("terminalId"); if (terminalID.isEmpty()) { terminalID = svgID; if (terminalID.endsWith("pin") || terminalID.endsWith("pad")) { terminalID.chop(3); } terminalID += "terminal"; pElement.setAttribute("terminalId", terminalID); } FSvgRenderer renderer; renderer.loadSvg(svgDoc->toByteArray(), "", false); QRectF svgBounds = renderer.boundsOnElement(svgID); double cx = p.x () * svgBounds.width() / size.width(); double cy = p.y() * svgBounds.height() / size.height(); double dx = svgBounds.width() / 1000; double dy = svgBounds.height() / 1000; QDomElement terminalElement = TextUtils::findElementWithAttribute(svgRoot, "id", terminalID); if (terminalElement.isNull()) { terminalElement = svgDoc->createElement("rect"); } else if (terminalElement.tagName() != "rect" || terminalElement.attribute("fill") != "none" || terminalElement.attribute("stroke") != "none") { terminalElement.setAttribute("id", ""); terminalElement = svgDoc->createElement("rect"); } terminalElement.setAttribute("id", terminalID); terminalElement.setAttribute("oldid", terminalID); terminalElement.setAttribute("stroke", "none"); terminalElement.setAttribute("fill", "none"); terminalElement.setAttribute("stroke-width", "0"); terminalElement.setAttribute("x", QString::number(svgBounds.left() + cx - dx)); terminalElement.setAttribute("y", QString::number(svgBounds.top() + cy - dy)); terminalElement.setAttribute("width", QString::number(dx * 2)); terminalElement.setAttribute("height", QString::number(dy * 2)); if (terminalElement.attribute("gorn").isEmpty()) { QString gorn = svgConnectorElement.attribute("gorn"); int ix = gorn.lastIndexOf("."); if (ix > 0) { gorn.truncate(ix); } gorn = QString("%1.gen%2").arg(gorn).arg(FakeGornSiblingNumber++); terminalElement.setAttribute("gorn", gorn); } svgConnectorElement.parentNode().insertAfter(terminalElement, svgConnectorElement); double oldZ = connectorPegi->zValue() + 1; foreach (PEGraphicsItem * pegi, pegiList) { QDomElement pegiElement = pegi->element(); if (pegiElement.attribute("id").compare(terminalID) == 0) { DebugDialog::debug("old pegi location", pegi->pos()); oldZ = pegi->zValue(); pegiList.removeOne(pegi); delete pegi; break; } } // update svg in case there is a subsequent call to reload QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(sketchWidget); QString svg = TextUtils::svgNSOnly(svgDoc->toString()); writeXml(newPath, removeGorn(svg), true); setImageAttribute(fzpRoot, newPath, sketchWidget->viewID()); double invdx = dx * size.width() / svgBounds.width(); double invdy = dy * size.height() / svgBounds.height(); QPointF topLeft = connectorPegi->offset() + p - QPointF(invdx, invdy); PEGraphicsItem * pegi = makePegi(QSizeF(invdx * 2, invdy * 2), topLeft, viewThing->itemBase, terminalElement, oldZ); DebugDialog::debug("new pegi location", pegi->pos()); updateChangeCount(sketchWidget, changeDirection); } connectorPegi->setTerminalPoint(p); connectorPegi->update(); m_peToolView->setTerminalPointCoords(p); } void PEMainWindow::showInOS(QWidget *parent, const QString &pathIn) { Q_UNUSED(parent); FolderUtils::showInFolder(pathIn); QClipboard *clipboard = QApplication::clipboard(); if (clipboard != NULL) { clipboard->setText(pathIn); } } void PEMainWindow::showInOS() { if (m_currentGraphicsView == NULL) return; ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID()); showInOS(this, viewThing->itemBase->filename()); } PEGraphicsItem * PEMainWindow::makePegi(QSizeF size, QPointF topLeft, ItemBase * itemBase, QDomElement & element, double z) { PEGraphicsItem * pegiItem = new PEGraphicsItem(0, 0, size.width(), size.height(), itemBase); pegiItem->showTerminalPoint(false); pegiItem->setPos(itemBase->pos() + topLeft); pegiItem->setZValue(z); itemBase->scene()->addItem(pegiItem); pegiItem->setElement(element); pegiItem->setOffset(topLeft); connect(pegiItem, SIGNAL(highlightSignal(PEGraphicsItem *)), this, SLOT(highlightSlot(PEGraphicsItem *))); connect(pegiItem, SIGNAL(mouseReleasedSignal(PEGraphicsItem *)), this, SLOT(pegiMouseReleased(PEGraphicsItem *))); connect(pegiItem, SIGNAL(mousePressedSignal(PEGraphicsItem *, bool &)), this, SLOT(pegiMousePressed(PEGraphicsItem *, bool &)), Qt::DirectConnection); connect(pegiItem, SIGNAL(terminalPointMoved(PEGraphicsItem *, QPointF)), this, SLOT(pegiTerminalPointMoved(PEGraphicsItem *, QPointF))); connect(pegiItem, SIGNAL(terminalPointChanged(PEGraphicsItem *, QPointF, QPointF)), this, SLOT(pegiTerminalPointChanged(PEGraphicsItem *, QPointF, QPointF))); return pegiItem; } QRectF PEMainWindow::getPixelBounds(FSvgRenderer & renderer, QDomElement & element) { QSizeF defaultSizeF = renderer.defaultSizeF(); QRectF viewBox = renderer.viewBoxF(); QString id = element.attribute("id"); QRectF r = renderer.boundsOnElement(id); QMatrix matrix = renderer.matrixForElement(id); QString oldid = element.attribute("oldid"); if (!oldid.isEmpty()) { element.setAttribute("id", oldid); element.removeAttribute("oldid"); } QRectF bounds = matrix.mapRect(r); bounds.setRect(bounds.x() * defaultSizeF.width() / viewBox.width(), bounds.y() * defaultSizeF.height() / viewBox.height(), bounds.width() * defaultSizeF.width() / viewBox.width(), bounds.height() * defaultSizeF.height() / viewBox.height()); return bounds; } bool PEMainWindow::canSave() { return m_canSave; } void PEMainWindow::tabWidget_currentChanged(int index) { MainWindow::tabWidget_currentChanged(index); if (m_peToolView == NULL) return; switchedConnector(m_peToolView->currentConnectorIndex()); bool enabled = index < IconViewIndex; m_peSvgView->setChildrenVisible(enabled); m_peToolView->setChildrenVisible(enabled); if (m_currentGraphicsView == NULL) { // update title when switching to connector and metadata view setTitle(); } else { } updateAssignedConnectors(); updateActiveLayerButtons(); } void PEMainWindow::backupSketch() { } void PEMainWindow::removedConnector(const QDomElement & element) { QList connectors; connectors.append(element); removedConnectorsAux(connectors); } void PEMainWindow::removedConnectors(QList & cmdList) { QList connectors; foreach (ConnectorMetadata * cmd, cmdList) { int index; QDomElement connector = findConnector(cmd->connectorID, index); if (connector.isNull()) return; cmd->index = index; connectors.append(connector); } removedConnectorsAux(connectors); } void PEMainWindow::removedConnectorsAux(QList & connectors) { QString originalPath = saveFzp(); foreach (QDomElement connector, connectors) { if (m_removedConnector.isEmpty()) { QTextStream stream(&m_removedConnector); connector.save(stream, 0); } connector.parentNode().removeChild(connector); } QString newPath = saveFzp(); ChangeFzpCommand * cfc = new ChangeFzpCommand(this, originalPath, newPath, NULL); QString message; if (connectors.count() == 1) { message = tr("Remove connector"); } else { message = tr("Remove %1 connectors").arg(connectors.count()); } cfc->setText(message); m_undoStack->waitPush(cfc, SketchWidget::PropChangeDelay); } void PEMainWindow::restoreFzp(const QString & fzpPath) { if (!loadFzp(fzpPath)) return; reload(false); } void PEMainWindow::setBeforeClosingText(const QString & filename, QMessageBox & messageBox) { Q_UNUSED(filename); QString partTitle = getPartTitle(); messageBox.setWindowTitle(tr("Save \"%1\"").arg(partTitle)); messageBox.setText(tr("Do you want to save the changes you made in the part \"%1\"?").arg(partTitle)); messageBox.setInformativeText(tr("Your changes will be lost if you don't save them.")); } QString PEMainWindow::getPartTitle() { QString partTitle = tr("untitled part"); QDomElement root = m_fzpDocument.documentElement(); QDomElement title = root.firstChildElement("title"); QString candidate = title.text(); if (!candidate.isEmpty()) return candidate; if (m_viewThings.count() > 0) { ViewThing * viewThing = m_viewThings.values().at(0); if (viewThing->itemBase) { candidate = viewThing->itemBase->title(); if (!candidate.isEmpty()) return candidate; } } return partTitle; } void PEMainWindow::killPegi() { foreach (ViewThing * viewThing, m_viewThings.values()) { if (viewThing->sketchWidget == NULL) continue; foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi) delete pegi; } } } bool PEMainWindow::loadFzp(const QString & path) { QFile file(path); QString errorStr; int errorLine; int errorColumn; bool result = m_fzpDocument.setContent(&file, &errorStr, &errorLine, &errorColumn); if (!result) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to load fzp from %1").arg(path)); return false; } return true; } void PEMainWindow::connectorCountChanged(int newCount) { QList connectorList; QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { connectorList.append(connector); connector = connector.nextSiblingElement(); } if (newCount == connectorList.count()) return; if (newCount < connectorList.count()) { qSort(connectorList.begin(), connectorList.end(), byID); QList toDelete; for (int i = newCount; i < connectorList.count(); i++) { toDelete.append(connectorList.at(i)); } removedConnectorsAux(toDelete); return; } // add connectors int id = 0; foreach (QDomElement connector, connectorList) { int ix = IntegerFinder.indexIn(connector.attribute("id")); if (ix >= 0) { int candidate = IntegerFinder.cap(0).toInt(); if (candidate > id) id = candidate; } // sometimes id = 0 but name = 1, and we are now using name = id ix = IntegerFinder.indexIn(connector.attribute("name")); if (ix >= 0) { int candidate = IntegerFinder.cap(0).toInt(); if (candidate > id) id = candidate; } } QString originalPath = saveFzp(); QDomElement connectorModel; QDomDocument tempDoc; if (connectorList.count() > 0) connectorModel = connectorList.at(0); else { tempDoc.setContent(m_removedConnector); connectorModel = tempDoc.documentElement(); if (connectorModel.isNull()) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to create new connector--you may have to start over.")); return; } } for (int i = connectorList.count(); i < newCount; i++) { id++; QDomElement element = connectorModel.cloneNode(true).toElement(); connectors.appendChild(element); QString newName = QString("pin %1").arg(id); element.setAttribute("name", newName); QString cid = QString("connector%1").arg(id); element.setAttribute("id", cid); QString svgid = cid + "pin"; QDomNodeList nodeList = element.elementsByTagName("p"); for (int n = 0; n < nodeList.count(); n++) { QDomElement p = nodeList.at(n).toElement(); p.removeAttribute("terminalId"); p.removeAttribute("legId"); p.setAttribute("svgId", svgid); } QDomElement description = element.firstChildElement("description"); if (description.isNull()) { description = m_fzpDocument.createElement("description"); element.appendChild(description); } TextUtils::replaceChildText(description, newName); } QString newPath = saveFzp(); ChangeFzpCommand * cfc = new ChangeFzpCommand(this, originalPath, newPath, NULL); QString message; if (newCount - connectorList.count() == 1) { message = tr("Add connector"); } else { message = tr("Add %1 connectors").arg(newCount - connectorList.count()); } cfc->setText(message); m_undoStack->waitPush(cfc, SketchWidget::PropChangeDelay); } bool PEMainWindow::editsModuleID(const QString & moduleID) { // only to detect whether a user tries to open the parts editor on the same part twice return (m_originalModuleID.compare(moduleID) == 0); } /* QString PEMainWindow::getFzpReferenceFile() { QString referenceFile = m_fzpDocument.documentElement().attribute(ReferenceFileString); if (!referenceFile.isEmpty()) referenceFile += "_"; return referenceFile; } */ QString PEMainWindow::getSvgReferenceFile(const QString & filename) { QFileInfo info(filename); QString referenceFile = info.fileName(); QFile file(filename); if (!file.open(QFile::ReadOnly)) return referenceFile; QString svg = file.readAll(); if (!svg.contains(ReferenceFileString)) return referenceFile; QXmlStreamReader xml(svg.toUtf8()); xml.setNamespaceProcessing(false); bool inDesc = false; while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: { QString name = xml.name().toString(); if (inDesc && name.compare(ReferenceFileString) == 0) { QString candidate = xml.readElementText().trimmed(); if (candidate.isEmpty()) return referenceFile; return candidate; } if (name.compare("desc") == 0) { inDesc = true; } } break; case QXmlStreamReader::EndElement: { QString name = xml.name().toString(); if (name.compare("desc") == 0) { inDesc = false; } } break; default: break; } } return referenceFile; } QString PEMainWindow::makeDesc(const QString & referenceFile) { return QString("\n<%2>%1\n").arg(referenceFile).arg(ReferenceFileString); } void PEMainWindow::updateWindowMenu() { } void PEMainWindow::updateRaiseWindowAction() { QString title = tr("Fritzing (New) Parts Editor"); QString partTitle = getPartTitle(); QString actionText = QString("%1: %2").arg(title).arg(partTitle); m_raiseWindowAct->setText(actionText); m_raiseWindowAct->setToolTip(actionText); m_raiseWindowAct->setStatusTip("raise \""+actionText+"\" window"); } bool PEMainWindow::writeXml(const QString & path, const QString & xml, bool temp) { bool result = TextUtils::writeUtf8(path, TextUtils::svgNSOnly(xml)); if (result) { if (temp) m_filesToDelete.append(path); else m_filesToDelete.removeAll(path); } return result; } void PEMainWindow::displayBuses() { deleteBuses(); QDomElement root = m_fzpDocument.documentElement(); QDomElement buses = root.firstChildElement("buses"); QDomElement bus = buses.firstChildElement("bus"); while (!bus.isNull()) { QDomElement nodeMember = bus.firstChildElement("nodeMember"); QSet connectorIDs; while (!nodeMember.isNull()) { QString connectorID = nodeMember.attribute("connectorId"); if (!connectorID.isEmpty()) { connectorIDs.insert(connectorID); } nodeMember = nodeMember.nextSiblingElement("nodeMember"); } foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) { ViewThing * viewThing = m_viewThings.value(viewID); QList connectorItems; foreach (QString connectorID, connectorIDs) { ConnectorItem * connectorItem = viewThing->itemBase->findConnectorItemWithSharedID(connectorID, viewThing->itemBase->viewLayerPlacement()); if (connectorItem) connectorItems.append(connectorItem); } for (int i = 0; i < connectorItems.count() - 1; i++) { ConnectorItem * c1 = connectorItems.at(i); ConnectorItem * c2 = connectorItems.at(i + 1); Wire * wire = viewThing->sketchWidget->makeOneRatsnestWire(c1, c2, false, QColor(0, 255, 0), true); wire->setZValue(RatZ); } } bus = bus.nextSiblingElement("bus"); } } void PEMainWindow::updateWireMenu() { // assumes update wire menu is only called when right-clicking a wire // and that wire is cached by the menu in Wire::mousePressEvent Wire * wire = m_activeWire; m_activeWire = NULL; m_deleteBusConnectionAct->setWire(wire); m_deleteBusConnectionAct->setEnabled(true); } void PEMainWindow::deleteBusConnection() { WireAction * wireAction = qobject_cast(sender()); if (wireAction == NULL) return; Wire * wire = wireAction->wire(); if (wire == NULL) return; QList wires; QList ends; wire->collectChained(wires, ends); if (ends.count() != 2) return; QString id0 = ends.at(0)->connectorSharedID(); QString id1 = ends.at(1)->connectorSharedID(); QDomElement root = m_fzpDocument.documentElement(); QDomElement buses = root.firstChildElement("buses"); QDomElement bus = buses.firstChildElement("bus"); QDomElement nodeMember0; QDomElement nodeMember1; while (!bus.isNull()) { nodeMember0 = TextUtils::findElementWithAttribute(bus, "connectorId", id0); nodeMember1 = TextUtils::findElementWithAttribute(bus, "connectorId", id1); if (!nodeMember0.isNull() && !nodeMember1.isNull()) break; bus = bus.nextSiblingElement("bus"); } QString busID = bus.attribute("id"); if (nodeMember0.isNull() || nodeMember1.isNull()) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Internal connections are very messed up.")); return; } QUndoCommand * parentCommand = new QUndoCommand(); QStringList names; names << ends.at(0)->connectorSharedName() << ends.at(1)->connectorSharedName() ; new RemoveBusConnectorCommand(this, busID, id0, false, parentCommand); new RemoveBusConnectorCommand(this, busID, id1, false, parentCommand); if (ends.at(0)->connectedToItems().count() > 1) { // restore it names.removeAt(0); new RemoveBusConnectorCommand(this, busID, id0, true, parentCommand); } if (ends.at(1)->connectedToItems().count() > 1) { // restore it new RemoveBusConnectorCommand(this, busID, id1, true, parentCommand); } parentCommand->setText(tr("Remove internal connection from '%1'").arg(names.at(0))); m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } void PEMainWindow::newWireSlot(Wire * wire) { wire->setDisplayBendpointCursor(false); disconnect(wire, 0, m_viewThings.value(wire->viewID())->sketchWidget, 0); connect(wire, SIGNAL(wireChangedSignal(Wire*, const QLineF & , const QLineF & , QPointF, QPointF, ConnectorItem *, ConnectorItem *) ), this, SLOT(wireChangedSlot(Wire*, const QLineF & , const QLineF & , QPointF, QPointF, ConnectorItem *, ConnectorItem *)), Qt::DirectConnection); // DirectConnection means call the slot directly like a subroutine, without waiting for a thread or queue } void PEMainWindow::wireChangedSlot(Wire* wire, const QLineF &, const QLineF &, QPointF, QPointF, ConnectorItem * fromOnWire, ConnectorItem * to) { wire->deleteLater(); if (to == NULL) return; ConnectorItem * from = wire->otherConnector(fromOnWire)->firstConnectedToIsh(); if (from == NULL) return; QDomElement root = m_fzpDocument.documentElement(); QDomElement buses = root.firstChildElement("buses"); if (buses.isNull()) { buses = m_fzpDocument.createElement("buses"); root.appendChild(buses); } QDomElement fromBus = findNodeMemberBus(buses, from->connectorSharedID()); QString fromBusID = fromBus.attribute("id"); QDomElement toBus = findNodeMemberBus(buses, to->connectorSharedID()); QString toBusID = toBus.attribute("id"); QString busID = fromBusID.isEmpty() ? toBusID : fromBusID; if (busID.isEmpty()) { int theMax = std::numeric_limits::max(); for (int ix = 1; ix < theMax; ix++) { QString candidate = QString("internal%1").arg(ix); QDomElement busElement = findBus(buses, candidate); if (busElement.isNull()) { busID = candidate; break; } } } QUndoCommand * parentCommand = new QUndoCommand(tr("Add internal connection from '%1' to '%2'").arg(from->connectorSharedName()).arg(to->connectorSharedName())); if (!fromBusID.isEmpty()) { // changing the bus for this nodeMember new RemoveBusConnectorCommand(this, fromBusID, from->connectorSharedID(), false, parentCommand); } if (!toBusID.isEmpty()) { // changing the bus for this nodeMember new RemoveBusConnectorCommand(this, toBusID, to->connectorSharedID(), false, parentCommand); } new RemoveBusConnectorCommand(this, busID, from->connectorSharedID(), true, parentCommand); new RemoveBusConnectorCommand(this, busID, to->connectorSharedID(), true, parentCommand); m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } QDomElement PEMainWindow::findBus(const QDomElement & buses, const QString & id) { QDomElement busElement = buses.firstChildElement("bus"); while (!busElement.isNull()) { if (busElement.attribute("id").compare(id) == 0) { return busElement; } busElement = busElement.nextSiblingElement("bus"); } return QDomElement(); } QDomElement PEMainWindow::findNodeMemberBus(const QDomElement & buses, const QString & connectorID) { QDomElement bus = buses.firstChildElement("bus"); while (!bus.isNull()) { QDomElement nodeMember = bus.firstChildElement("nodeMember"); while (!nodeMember.isNull()) { if (nodeMember.attribute("connectorId").compare(connectorID) == 0) { return bus; } nodeMember = nodeMember.nextSiblingElement("nodeMember"); } bus = bus.nextSiblingElement("bus"); } return QDomElement(); } void PEMainWindow::addBusConnector(const QString & busID, const QString & connectorID) { // called from command object removeBusConnector(busID, connectorID, false); // keep the dom very clean QDomElement root = m_fzpDocument.documentElement(); QDomElement buses = root.firstChildElement("buses"); if (buses.isNull()) { m_fzpDocument.createElement("buses"); root.appendChild(buses); } QDomElement theBusElement = findBus(buses, busID); if (theBusElement.isNull()) { theBusElement = m_fzpDocument.createElement("bus"); theBusElement.setAttribute("id", busID); buses.appendChild(theBusElement); } QDomElement nodeMember = m_fzpDocument.createElement("nodeMember"); nodeMember.setAttribute("connectorId", connectorID); theBusElement.appendChild(nodeMember); displayBuses(); } void PEMainWindow::removeBusConnector(const QString & busID, const QString & connectorID, bool display) { // called from command object // for the sake of cleaning, deletes all matching nodeMembers so be careful about the order of deletion and addition within the same parentCommand Q_UNUSED(busID); QDomElement root = m_fzpDocument.documentElement(); QDomElement buses = root.firstChildElement("buses"); QDomElement bus = buses.firstChildElement("bus"); QList toDelete; while (!bus.isNull()) { QDomElement nodeMember = bus.firstChildElement("nodeMember"); while (!nodeMember.isNull()) { if (nodeMember.attribute("connectorId").compare(connectorID) == 0) { toDelete.append(nodeMember); } nodeMember = nodeMember.nextSiblingElement("nodeMember"); } bus = bus.nextSiblingElement("bus"); } foreach (QDomElement element, toDelete) { element.parentNode().removeChild(element); } if (display) displayBuses(); } void PEMainWindow::replaceProperty(const QString & key, const QString & value, QDomElement & properties) { QDomElement prop = properties.firstChildElement("property"); while (!prop.isNull()) { QString name = prop.attribute("name"); if (name.compare(key, Qt::CaseInsensitive) == 0) { TextUtils::replaceChildText(prop, value); return; } prop = prop.nextSiblingElement("property"); } prop = m_fzpDocument.createElement("property"); properties.appendChild(prop); prop.setAttribute("name", key); TextUtils::replaceChildText(prop, value); } QWidget * PEMainWindow::createTabWidget() { QTabWidget * tabWidget = new QTabWidget(this); tabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); tabWidget->setObjectName("pe_tabs"); return tabWidget; } void PEMainWindow::addTab(QWidget * widget, const QString & label) { qobject_cast(m_tabWidget)->addTab(widget, label); this -> setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } int PEMainWindow::currentTabIndex() { return qobject_cast(m_tabWidget)->currentIndex(); } void PEMainWindow::setCurrentTabIndex(int index) { qobject_cast(m_tabWidget)->setCurrentIndex(index); } QWidget * PEMainWindow::currentTabWidget() { return qobject_cast(m_tabWidget)->currentWidget(); } bool PEMainWindow::event(QEvent * e) { if (e->type() == QEvent::Close) { //qDebug() << "event close"; //if (m_inFocusWidgets.count() > 0) { // e->ignore(); // qDebug() << "bail in focus"; // return true; //} } return MainWindow::event(e); } bool PEMainWindow::eventFilter(QObject *object, QEvent *event) { if (m_inPickMode) { switch (event->type()) { case QEvent::MouseButtonPress: clearPickMode(); { QMouseEvent * mouseEvent = static_cast(event); m_useNextPick = (mouseEvent->button() == Qt::LeftButton); } QTimer::singleShot(1, this, SLOT(resetNextPick())); break; case QEvent::ApplicationActivate: case QEvent::ApplicationDeactivate: case QEvent::WindowActivate: case QEvent::WindowDeactivate: case QEvent::NonClientAreaMouseButtonPress: clearPickMode(); break; case QEvent::KeyPress: { QKeyEvent * kevent = static_cast(event); if (kevent->key() == Qt::Key_Escape) { clearPickMode(); return true; } } default: break; } return false; } //qDebug() << "event" << event->type(); if (event->type() == QEvent::FocusIn) { QLineEdit * lineEdit = qobject_cast(object); if (lineEdit != NULL) { if (lineEdit->window() == this) { qDebug() << "inc focus"; m_inFocusWidgets << lineEdit; } } else { QTextEdit * textEdit = qobject_cast(object); if (textEdit != NULL && textEdit->window() == this) { qDebug() << "inc focus"; m_inFocusWidgets << textEdit; } } } if (event->type() == QEvent::FocusOut) { QLineEdit * lineEdit = qobject_cast(object); if (lineEdit != NULL) { if (lineEdit->window() == this) { qDebug() << "dec focus"; m_inFocusWidgets.removeOne(lineEdit); } } else { QTextEdit * textEdit = qobject_cast(object); if (textEdit != NULL && textEdit->window() == this) { qDebug() << "inc focus"; m_inFocusWidgets.removeOne(textEdit); } } } return false; } void PEMainWindow::closeLater() { close(); } void PEMainWindow::resetNextPick() { m_useNextPick = false; } void PEMainWindow::clearPickMode() { qApp->removeEventFilter(this); m_useNextPick = m_inPickMode = false; QApplication::restoreOverrideCursor(); if (m_currentGraphicsView) { foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi) pegi->setPickAppearance(false); } } } QList PEMainWindow::getPegiList(SketchWidget * sketchWidget) { // DebugDialog::debug("-----------------------------"); QList pegiList; foreach (QGraphicsItem * item, sketchWidget->scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi == NULL) continue; pegiList.append(pegi); /* if (pegi->showingTerminalPoint() || pegi->showingMarquee() || !pegi->element().attribute("id").isEmpty()) { DebugDialog::debug(QString("pegi m:%1 t:%2 %3") .arg(pegi->showingMarquee()) .arg(pegi->showingTerminalPoint()) .arg(pegi->element().attribute("id"))); } */ } //DebugDialog::debug("-----------------------------"); return pegiList; } void PEMainWindow::deleteBuses() { QList toDelete; foreach (ViewThing * viewThing, m_viewThings.values()) { foreach (QGraphicsItem * item, viewThing->sketchWidget->scene()->items()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; toDelete << wire; } } foreach (Wire * wire, toDelete) { delete wire; } } void PEMainWindow::connectorsTypeChanged(Connector::ConnectorType ct) { QUndoCommand * parentCommand = NULL; QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { ConnectorMetadata oldCmd; fillInMetadata(connector, oldCmd); if (oldCmd.connectorType != ct) { if (parentCommand == NULL) { parentCommand = new QUndoCommand(tr("Change all connectors to %1").arg(Connector::connectorNameFromType(ct))); } ConnectorMetadata cmd = oldCmd; cmd.connectorType = ct; new ChangeConnectorMetadataCommand(this, &oldCmd, &cmd, parentCommand); } connector = connector.nextSiblingElement("connector"); } if (parentCommand) { m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } } void PEMainWindow::fillInMetadata(const QDomElement & connector, ConnectorMetadata & cmd) { cmd.connectorID = connector.attribute("id"); cmd.connectorType = Connector::connectorTypeFromName(connector.attribute("type")); cmd.connectorName = connector.attribute("name"); QDomElement description = connector.firstChildElement("description"); cmd.connectorDescription = description.text(); } void PEMainWindow::smdChanged(const QString & after) { QString before; bool toSMD = true; if (after.compare("smd", Qt::CaseInsensitive) != 0) { before = "smd"; toSMD = false; } ViewThing * viewThing = m_viewThings.value(ViewLayer::PCBView); ItemBase * itemBase = viewThing->itemBase; if (itemBase == NULL) return; QFile file(itemBase->filename()); QDomDocument svgDoc; svgDoc.setContent(&file); QDomElement svgRoot = svgDoc.documentElement(); QDomElement svgCopper0 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper0"); QDomElement svgCopper1 = TextUtils::findElementWithAttribute(svgRoot, "id", "copper1"); if (svgCopper0.isNull() && svgCopper1.isNull()) { QMessageBox::critical(NULL, tr("Parts Editor"), tr("Unable to parse '%1'").arg(itemBase->filename())); return; } if (toSMD) { if (svgCopper0.isNull() && !svgCopper1.isNull()) { // everything is fine } else if (svgCopper1.isNull()) { // just needs a swap svgCopper0.setAttribute("id", "copper1"); } else { svgCopper0.removeAttribute("id"); } } else { if (!svgCopper0.isNull() && !svgCopper1.isNull()) { // everything is fine } else if (svgCopper1.isNull()) { svgCopper1 = m_pcbDocument.createElement("g"); svgCopper1.setAttribute("id", "copper1"); svgRoot.appendChild(svgCopper1); QDomElement child = svgCopper0.firstChildElement(); while (!child.isNull()) { QDomElement next = child.nextSiblingElement(); svgCopper1.appendChild(child); child = next; } svgCopper0.appendChild(svgCopper1); } else { svgCopper0 = m_pcbDocument.createElement("g"); svgCopper0.setAttribute("id", "copper0"); svgRoot.appendChild(svgCopper0); QDomElement child = svgCopper1.firstChildElement(); while (!child.isNull()) { QDomElement next = child.nextSiblingElement(); svgCopper0.appendChild(child); child = next; } svgCopper1.appendChild(svgCopper0); } } QString newPath = m_userPartsFolderSvgPath + makeSvgPath2(m_pcbGraphicsView); QString svg = TextUtils::svgNSOnly(svgDoc.toString()); writeXml(newPath, removeGorn(svg), true); ChangeSMDCommand * csc = new ChangeSMDCommand(this, before, after, itemBase->filename(), newPath, NULL); csc->setText(tr("Change to %1").arg(after)); m_undoStack->waitPush(csc, SketchWidget::PropChangeDelay); } void PEMainWindow::changeSMD(const QString & after, const QString & filename, int changeDirection) { QDomElement root = m_fzpDocument.documentElement(); QDomElement views = root.firstChildElement("views"); QDomElement pcbView = views.firstChildElement("pcbView"); QDomElement layers = pcbView.firstChildElement("layers"); QDomElement copper0 = TextUtils::findElementWithAttribute(layers, "layerId", "copper0"); QDomElement copper1 = TextUtils::findElementWithAttribute(layers, "layerId", "copper1"); bool toSMD = true; if (after.compare("smd", Qt::CaseInsensitive) == 0) { if (!copper0.isNull()) { copper0.parentNode().removeChild(copper0); } } else { toSMD = false; if (copper0.isNull()) { copper0 = m_fzpDocument.createElement("layer"); copper0.setAttribute("layerId", "copper0"); layers.appendChild(copper0); } } if (copper1.isNull()) { copper1 = m_fzpDocument.createElement("layer"); copper1.setAttribute("layerId", "copper1"); layers.appendChild(copper1); } QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { setConnectorSMD(toSMD, connector); connector = connector.nextSiblingElement("connector"); } changeSvg(m_pcbGraphicsView, filename, changeDirection); } void PEMainWindow::setConnectorSMD(bool toSMD, QDomElement & connector) { QDomElement views = connector.firstChildElement("views"); QDomElement pcbView = views.firstChildElement("pcbView"); QDomElement copper0 = TextUtils::findElementWithAttribute(pcbView, "layer", "copper0"); QDomElement copper1 = TextUtils::findElementWithAttribute(pcbView, "layer", "copper1"); if (copper0.isNull() && copper1.isNull()) { // SVG is seriously messed up DebugDialog::debug("setting SMD is very broken"); return; } if (toSMD) { if (copper0.isNull() && !copper1.isNull()) { // already correct return; } if (copper1.isNull()) { // swap it to copper1 copper0.setAttribute("layer", "copper1"); return; } // remove the extra layer copper0.parentNode().removeChild(copper0); return; } if (!copper0.isNull() && !copper1.isNull()) { // already correct return; } if (copper1.isNull()) { copper1 = copper0.cloneNode(true).toElement(); copper0.parentNode().appendChild(copper1); copper1.setAttribute("layer", "copper1"); return; } copper0 = copper1.cloneNode(true).toElement(); copper1.parentNode().appendChild(copper0); copper0.setAttribute("layer", "copper0"); } void PEMainWindow::reuseBreadboard() { reuseImage(ViewLayer::BreadboardView); } void PEMainWindow::reuseSchematic() { reuseImage(ViewLayer::SchematicView); } void PEMainWindow::reusePCB() { reuseImage(ViewLayer::PCBView); } void PEMainWindow::reuseImage(ViewLayer::ViewID viewID) { if (m_currentGraphicsView == NULL) return; ViewThing * afterViewThing = m_viewThings.value(viewID); if (afterViewThing->itemBase == NULL) return; QString afterFilename = afterViewThing->itemBase->filename(); ViewThing * beforeViewThing = m_viewThings.value(m_currentGraphicsView->viewID()); ChangeSvgCommand * csc = new ChangeSvgCommand(this, m_currentGraphicsView, beforeViewThing->itemBase->filename(), afterFilename, NULL); QFileInfo info(afterFilename); csc->setText(QString("Load '%1'").arg(info.fileName())); m_undoStack->waitPush(csc, SketchWidget::PropChangeDelay); } void PEMainWindow::updateFileMenu() { MainWindow::updateFileMenu(); m_saveAct->setEnabled(canSave()); /* QHash enabled; enabled.insert(ViewLayer::BreadboardView, true); enabled.insert(ViewLayer::SchematicView, true); enabled.insert(ViewLayer::PCBView, true); bool enableAll = true; if (m_currentGraphicsView == NULL) { enableAll = false; } else { ViewLayer::ViewID viewID = m_currentGraphicsView->viewID(); enabled.insert(viewID, false); } m_reuseBreadboardAct->setEnabled(enableAll && enabled.value(ViewLayer::BreadboardView)); m_reuseSchematicAct->setEnabled(enableAll && enabled.value(ViewLayer::SchematicView)); m_reusePCBAct->setEnabled(enableAll && enabled.value(ViewLayer::PCBView)); */ bool enabled = m_currentGraphicsView != NULL && m_currentGraphicsView->viewID() == ViewLayer::IconView; m_reuseBreadboardAct->setEnabled(enabled); m_reuseSchematicAct->setEnabled(enabled); m_reusePCBAct->setEnabled(enabled); } void PEMainWindow::setImageAttribute(QDomElement & fzpRoot, const QString & svgPath, ViewLayer::ViewID viewID) { QDomElement views = fzpRoot.firstChildElement("views"); QDomElement view = views.firstChildElement(ViewLayer::viewIDXmlName(viewID)); QDomElement layers = view.firstChildElement("layers"); QFileInfo info(svgPath); QDir dir = info.absoluteDir(); QString shortName = dir.dirName() + "/" + info.fileName(); setImageAttribute(layers, shortName); } void PEMainWindow::setImageAttribute(QDomElement & layers, const QString & svgPath) { layers.setAttribute("image", svgPath); QDomElement layer = layers.firstChildElement("layer"); if (!layer.isNull()) return; layer = m_fzpDocument.createElement("layer"); layers.appendChild(layer); layer.setAttribute("layerId", ViewLayer::viewLayerXmlNameFromID(ViewLayer::UnknownLayer)); } void PEMainWindow::updateLayerMenu(bool resetLayout) { MainWindow::updateLayerMenu(resetLayout); bool enabled = false; if (m_currentGraphicsView != NULL) { switch (m_currentGraphicsView->viewID()) { case ViewLayer::BreadboardView: case ViewLayer::SchematicView: case ViewLayer::PCBView: enabled = true; default: break; } } m_hideOtherViewsAct->setEnabled(enabled); } void PEMainWindow::hideOtherViews() { if (m_currentGraphicsView == NULL) return; ViewLayer::ViewID afterViewID = m_currentGraphicsView->viewID(); ItemBase * afterItemBase = m_viewThings.value(afterViewID)->itemBase; if (afterItemBase == NULL) return; QString afterFilename = afterItemBase->filename(); QList viewIDList; viewIDList << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView; viewIDList.removeOne(afterViewID); QString originalPath = saveFzp(); QString afterViewName = ViewLayer::viewIDXmlName(afterViewID); QStringList beforeViewNames; foreach (ViewLayer::ViewID viewID, viewIDList) { beforeViewNames << ViewLayer::viewIDXmlName(viewID); } QDomElement root = m_fzpDocument.documentElement(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement views = connector.firstChildElement("views"); QDomElement afterView = views.firstChildElement(afterViewName); foreach (QString name, beforeViewNames) { QDomElement toRemove = views.firstChildElement(name); if (!toRemove.isNull()) { toRemove.parentNode().removeChild(toRemove); } QDomElement toReplace = afterView.cloneNode(true).toElement(); toReplace.setTagName(name); views.appendChild(toReplace); } connector = connector.nextSiblingElement("connector"); } QDomElement views = root.firstChildElement("views"); QDomElement afterView = views.firstChildElement(afterViewName); foreach (QString name, beforeViewNames) { QDomElement toRemove = views.firstChildElement(name); if (!toRemove.isNull()) { toRemove.parentNode().removeChild(toRemove); } QDomElement toReplace = afterView.cloneNode(true).toElement(); toReplace.setTagName(name); views.appendChild(toReplace); } QString newPath = saveFzp(); ChangeFzpCommand * cfc = new ChangeFzpCommand(this, originalPath, newPath, NULL); cfc->setText(tr("Make only %1 view visible").arg(m_currentGraphicsView->viewName())); m_undoStack->waitPush(cfc, SketchWidget::PropChangeDelay); } QString PEMainWindow::makeNewVariant(const QString & family) { QStringList variants = m_referenceModel->propValues(family, "variant", true); int theMax = std::numeric_limits::max(); QString candidate; for (int i = 1; i < theMax; i++) { candidate = QString("variant %1").arg(i); if (!variants.contains(candidate, Qt::CaseInsensitive)) break; } return candidate; } void PEMainWindow::updateAssignedConnectors() { if (m_currentGraphicsView == NULL) return; QDomDocument * doc = m_viewThings.value(m_currentGraphicsView->viewID())->document; if (doc) m_peToolView->showAssignedConnectors(doc, m_currentGraphicsView->viewID()); } void PEMainWindow::connectorWarning() { QHash unassigned; foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) { unassigned.insert(viewID, 0); } int unassignedTotal = 0; QDomElement fzpRoot = m_fzpDocument.documentElement(); QDomElement connectors = fzpRoot.firstChildElement("connectors"); foreach (ViewLayer::ViewID viewID, m_viewThings.keys()) { if (viewID == ViewLayer::IconView) continue; QDomDocument * svgDoc = m_viewThings.value(viewID)->document; QDomElement svgRoot = svgDoc->documentElement(); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QString svgID, terminalID; if (ViewLayer::getConnectorSvgIDs(connector, viewID, svgID, terminalID)) { QDomElement element = TextUtils::findElementWithAttribute(svgRoot, "id", svgID); if (element.isNull()) { unassigned.insert(viewID, 1 + unassigned.value(viewID)); unassignedTotal++; } } else { unassigned.insert(viewID, 1 + unassigned.value(viewID)); unassignedTotal++; } connector = connector.nextSiblingElement("connector"); } } if (unassignedTotal > 0) { int viewCount = 0; foreach (ViewLayer::ViewID viewID, unassigned.keys()) { if (unassigned.value(viewID) > 0) viewCount++; } QMessageBox::warning(NULL, tr("Parts Editor"), tr("This part has %n unassigned connectors ", "", unassignedTotal) + tr("across %n views. ", "", viewCount) + tr("Until all connectors are assigned to SVG elements, the part will not work correctly. ") + tr("Exiting the Parts Editor now is fine, as long as you remember to finish the assignments later.") ); } } void PEMainWindow::showing(SketchWidget * sketchWidget) { ViewThing * viewThing = m_viewThings.value(sketchWidget->viewID()); if (viewThing->firstTime) { viewThing->firstTime = false; QPointF offset = viewThing->sketchWidget->alignOneToGrid(viewThing->itemBase); if (offset.x() != 0 || offset.y() != 0) { QList pegiList = getPegiList(sketchWidget); foreach (PEGraphicsItem * pegi, pegiList) { pegi->setPos(pegi->pos() + offset); pegi->setOffset(pegi->offset() + offset); } } } } bool PEMainWindow::anyMarquee() { if (m_currentGraphicsView == NULL) return false; QList pegiList = getPegiList(m_currentGraphicsView); foreach (PEGraphicsItem * pegi, pegiList) { if (pegi->showingMarquee()) { return true; } } return false; } bool PEMainWindow::anyVisible() { if (m_currentGraphicsView == NULL) return false; foreach (QGraphicsItem * item, m_currentGraphicsView->scene()->items()) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi == NULL) continue; return pegi->isVisible(); } return false; } void PEMainWindow::changeReferenceFile(ViewLayer::ViewID viewID, const QString referenceFile) { ViewThing * viewThing = m_viewThings.value(viewID); if (viewThing == NULL) { // shouldn't happen DebugDialog::debug(QString("missing view thing for %1").arg(viewID)); return; } viewThing->referenceFile = referenceFile; } void PEMainWindow::insertDesc(const QString & referenceFile, QString & svg) { if (svg.contains(ReferenceFileString)) return; int ix = svg.indexOf("= 0) { int jx = svg.indexOf(">", ix); if (jx > ix) { svg.insert(jx + 1, makeDesc(referenceFile)); } } } void PEMainWindow::itemAddedSlot(ModelPart *, ItemBase * itemBase, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, SketchWidget *) { Q_UNUSED(id); if (itemBase == NULL) return; if (itemBase->viewID() != m_currentGraphicsView->viewID()) return; QDomElement element; double z = 0; foreach (PEGraphicsItem * pegi, getPegiList(m_currentGraphicsView)) { if (pegi->zValue() > z) z = pegi->zValue(); } QRectF bounds = itemBase->boundingRect(); makePegi(bounds.size(), QPointF(0, 0), itemBase, element, z + 1); } void PEMainWindow::itemMovedSlot(ItemBase * itemBase) { if (itemBase == NULL) return; if (itemBase->viewID() != m_currentGraphicsView->viewID()) return; foreach (PEGraphicsItem * pegi, getPegiList(m_currentGraphicsView)) { if (pegi->itemBase() == itemBase) { pegi->setPos(itemBase->pos() + pegi->offset()); } } } void PEMainWindow::resizedSlot(ItemBase * itemBase) { if (itemBase == NULL) return; if (itemBase->viewID() != m_currentGraphicsView->viewID()) return; foreach (PEGraphicsItem * pegi, getPegiList(m_currentGraphicsView)) { if (pegi->itemBase() == itemBase) { pegi->setPos(itemBase->pos() + pegi->offset()); QRectF bounds = itemBase->boundingRect(); pegi->setRect(bounds); } } } void PEMainWindow::clickedItemCandidateSlot(QGraphicsItem * item, bool & ok) { PEGraphicsItem * pegi = dynamic_cast(item); if (pegi == NULL) { ok = true; return; } ok = pegi->showingMarquee(); } void PEMainWindow::initProgrammingWidget() { } void PEMainWindow::initWelcomeView() { } void PEMainWindow::setInitialView() { // do this the first time, since the current_changed signal wasn't sent int tab = 0; tabWidget_currentChanged(tab+1); tabWidget_currentChanged(tab); } void PEMainWindow::updateExportMenu() { foreach (QAction * action, m_exportMenu->actions()) { action->setEnabled(false); } } void PEMainWindow::convertToTenth() { if (m_currentGraphicsView == NULL) return; if (m_currentGraphicsView->viewID() != ViewLayer::SchematicView) return; QString originalFzpPath = saveFzp(); QString newFzpPath = saveFzp(); ViewThing * viewThing = m_viewThings.value(m_currentGraphicsView->viewID()); QString originalSvgPath = viewThing->itemBase->filename(); QString newSvgPath = m_userPartsFolderSvgPath + makeSvgPath2(m_currentGraphicsView); QFile::copy(originalSvgPath, newSvgPath); S2S s2s(false); connect(&s2s, SIGNAL(messageSignal(const QString &)), this, SLOT(s2sMessageSlot(const QString &))); bool result = s2s.onefzp(newFzpPath, newSvgPath); if (!result) return; // if conversion fails QUndoCommand * parentCommand = new QUndoCommand("Convert Schematic"); new ChangeFzpCommand(this, originalFzpPath, newFzpPath, parentCommand); new ChangeSvgCommand(this, m_currentGraphicsView, originalSvgPath, newSvgPath, parentCommand); m_undoStack->waitPush(parentCommand, SketchWidget::PropChangeDelay); } void PEMainWindow::s2sMessageSlot(const QString & message) { QMessageBox::information(this, "Schematic Conversion", message); } void PEMainWindow::updateEditMenu() { MainWindow::updateEditMenu(); m_convertToTenthAct->setEnabled(m_currentGraphicsView != NULL && m_currentGraphicsView->viewID() == ViewLayer::SchematicView); } fritzing-app-0.9.3b/src/partseditor/pemainwindow.h000066400000000000000000000242511272363601300223070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6943 $: $Author: irascibl@gmail.com $: $Date: 2013-03-30 14:06:38 +0100 (Sa, 30. Mrz 2013) $ ********************************************************************/ #ifndef PEMAINWINDOW_H_ #define PEMAINWINDOW_H_ #include "../mainwindow/mainwindow.h" #include "../model/modelpartshared.h" #include "../sketch/sketchwidget.h" #include "peconnectorsview.h" class IconSketchWidget : public SketchWidget { Q_OBJECT public: IconSketchWidget(ViewLayer::ViewID, QWidget *parent=0); void addViewLayers(); }; struct ViewThing { ItemBase * itemBase; QDomDocument * document; int svgChangeCount; bool everZoomed; SketchWidget * sketchWidget; QString referenceFile; QString originalSvgPath; bool firstTime; bool busMode; ViewThing(); }; class PEMainWindow : public MainWindow { Q_OBJECT public: PEMainWindow(class ReferenceModel * referenceModel, QWidget * parent); ~PEMainWindow(); bool setInitialItem(class PaletteItem *); void changeTags(const QStringList &, bool updateDisplay); void changeProperties(const QHash &, bool updateDisplay); void changeMetadata(const QString & name, const QString & value, bool updateDisplay); void changeConnectorMetadata(ConnectorMetadata *, bool updateDisplay); void changeSvg(SketchWidget *, const QString & filename, int changeDirection); void relocateConnectorSvg(SketchWidget *, const QString & id, const QString & terminalID, const QString & oldGorn, const QString & oldGornTerminal, const QString & newGorn, const QString & newGornTerminal, int changeDirection); void moveTerminalPoint(SketchWidget *, const QString & id, QSizeF, QPointF, int changeDirection); void restoreFzp(const QString & filename); bool editsModuleID(const QString &); void addBusConnector(const QString & busID, const QString & connectorID); void removeBusConnector(const QString & busID, const QString & connectorID, bool display); void changeSMD(const QString & smd, const QString & filename, int changeDirection); void changeReferenceFile(ViewLayer::ViewID viewID, const QString referenceFile); void changeOriginalFile(ViewLayer::ViewID viewID, const QString originalFile, int changeCount); signals: void addToMyPartsSignal(ModelPart *); public slots: void metadataChanged(const QString & name, const QString & value); void propertiesChanged(const QHash &); void tagsChanged(const QStringList &); void connectorMetadataChanged(struct ConnectorMetadata *); void removedConnectors(QList &); void highlightSlot(class PEGraphicsItem *); void pegiMousePressed(class PEGraphicsItem *, bool & ignore); void pegiMouseReleased(class PEGraphicsItem *); void pegiTerminalPointMoved(class PEGraphicsItem *, QPointF); void pegiTerminalPointChanged(class PEGraphicsItem *, QPointF before, QPointF after); void switchedConnector(int); void removedConnector(const QDomElement &); void terminalPointChanged(const QString & how); void terminalPointChanged(const QString & coord, double value); void getSpinAmount(double & amount); void pickModeChanged(bool); void busModeChanged(bool); void connectorCountChanged(int); void deleteBusConnection(); void newWireSlot(Wire *); void wireChangedSlot(class Wire*, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, ConnectorItem * from, ConnectorItem * to); void connectorsTypeChanged(Connector::ConnectorType); void smdChanged(const QString &); void showing(SketchWidget *); void updateExportMenu(); void updateEditMenu(); void s2sMessageSlot(const QString & message); protected: void closeEvent(QCloseEvent * event); bool event(QEvent *); void initLockedFiles(bool lockFiles); void initSketchWidgets(bool withIcons); void initProgrammingWidget(); void initDock(); void moreInitDock(); void setInitialView(); void createActions(); void createMenus(); QList getButtonsForView(ViewLayer::ViewID); void connectPairs(); QMenu *breadboardItemMenu(); QMenu *schematicItemMenu(); QMenu *pcbItemMenu(); QMenu *pcbWireMenu(); QMenu *schematicWireMenu(); QMenu *breadboardWireMenu(); void setTitle(); void createViewMenuActions(bool showWelcome); void createFileMenuActions(); void createViewMenu(); void createEditMenu(); QHash getOldProperties(); QDomElement findConnector(const QString & id, int & index); void changeConnectorElement(QDomElement & connector, ConnectorMetadata *); void initSvgTree(SketchWidget *, ItemBase *, QDomDocument &); void initConnectors(bool updateConnectorsView); QString createSvgFromImage(const QString &origFilePath); // QString makeSvgPath(const QString & referenceFile, SketchWidget * sketchWidget, bool useIndex); QString makeSvgPath2(SketchWidget * sketchWidget); QString saveFzp(); void reload(bool firstTime); void createFileMenu(); void updateChangeCount(SketchWidget * sketchWidget, int changeDirection); class PEGraphicsItem * findConnectorItem(); void terminalPointChangedAux(PEGraphicsItem * pegi, QPointF before, QPointF after); void showInOS(QWidget *parent, const QString &pathIn); void switchedConnector(int, SketchWidget *); PEGraphicsItem * makePegi(QSizeF size, QPointF topLeft, ItemBase * itemBase, QDomElement & element, double z); QRectF getPixelBounds(FSvgRenderer & renderer, QDomElement & element); bool canSave(); bool saveAs(bool overWrite); void setBeforeClosingText(const QString & filename, QMessageBox & messageBox); QString getPartTitle(); void killPegi(); bool loadFzp(const QString & path); void removedConnectorsAux(QList & connectors); //QString getFzpReferenceFile(); QString getSvgReferenceFile(const QString & filename); QString makeDesc(const QString & referenceFile); void insertDesc(const QString & referenceFile, QString & svg); void updateRaiseWindowAction(); bool writeXml(const QString & path, const QString & text, bool temp); void displayBuses(); QDomElement findBus(const QDomElement &, const QString &); QDomElement findNodeMemberBus(const QDomElement & buses, const QString & connectorID); void replaceProperty(const QString & key, const QString & value, QDomElement & properties); QWidget * createTabWidget(); void addTab(QWidget * widget, const QString & label); int currentTabIndex(); void setCurrentTabIndex(int); QWidget * currentTabWidget(); void changeSpecialProperty(const QString & name, const QString & value); bool eventFilter(QObject *, QEvent *); void relocateConnector(PEGraphicsItem * pegi); void clearPickMode(); void deleteBuses(); QList getPegiList(SketchWidget *); void fillInMetadata(const QDomElement & connector, ConnectorMetadata & cmd); void setConnectorSMD(bool toSMD, QDomElement & connector); bool activeLayerWidgetAlwaysOn(); void updateFileMenu(); void reuseImage(ViewLayer::ViewID); void setImageAttribute(QDomElement & layers, const QString & image); void setImageAttribute(QDomElement & fzpRoot, const QString & svgPath, ViewLayer::ViewID viewID); QString makeNewVariant(const QString & family); void connectorWarning(); bool anyMarquee(); bool anyVisible(); QString makeDirName(); void initWelcomeView(); protected slots: void initZoom(); void showMetadataView(); void showConnectorsView(); void showIconView(); void loadImage(); bool save(); bool saveAs(); void showInOS(); void tabWidget_currentChanged(int index); void backupSketch(); void updateWindowMenu(); void updateWireMenu(); void closeLater(); void resetNextPick(); void reuseBreadboard(); void reuseSchematic(); void reusePCB(); void convertToTenth(); void hideOtherViews(); void updateLayerMenu(bool resetLayout = false); void updateAssignedConnectors(); void itemAddedSlot(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, SketchWidget * dropOrigin); void itemMovedSlot(ItemBase *); void clickedItemCandidateSlot(QGraphicsItem *, bool & ok); void resizedSlot(ItemBase *); protected: QDomDocument m_fzpDocument; QDomDocument m_iconDocument; QDomDocument m_breadboardDocument; QDomDocument m_schematicDocument; QDomDocument m_pcbDocument; QAction * m_showMetadataViewAct; QAction * m_showConnectorsViewAct; QAction * m_showIconAct; QAction * m_showInOSAct; class WireAction * m_deleteBusConnectionAct; QAction * m_reuseBreadboardAct; QAction * m_reuseSchematicAct; QAction * m_reusePCBAct; QAction * m_hideOtherViewsAct; QAction * m_convertToTenthAct; QPointer m_iconWidget; QPointer m_iconGraphicsView; class PEMetadataView * m_metadataView; class PEConnectorsView * m_connectorsView; class PEToolView * m_peToolView; class PESvgView * m_peSvgView; QString m_guid; QString m_prefix; int m_fileIndex; QHash m_viewThings; QString m_userPartsFolderPath; QString m_userPartsFolderSvgPath; bool m_canSave; QString m_originalFzpPath; QString m_originalModuleID; bool m_gaveSaveWarning; QStringList m_filesToDelete; QList< QPointer > m_inFocusWidgets; bool m_inPickMode; bool m_useNextPick; QList m_connectorList; QString m_removedConnector; }; #endif /* PEMAINWINDOW_H_ */ fritzing-app-0.9.3b/src/partseditor/pemetadataview.cpp000066400000000000000000000230211272363601300231330ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include "pemetadataview.h" #include "hashpopulatewidget.h" ////////////////////////////////////// FocusOutTextEdit::FocusOutTextEdit(QWidget * parent) : QTextEdit(parent) { } FocusOutTextEdit::~FocusOutTextEdit() { } void FocusOutTextEdit::focusOutEvent(QFocusEvent * e) { QTextEdit::focusOutEvent(e); if (document()->isModified()) { emit focusOut(); document()->setModified(false); } } ////////////////////////////////////// PEMetadataView::PEMetadataView(QWidget * parent) : QScrollArea(parent) { m_mainFrame = NULL; this->setWidgetResizable(true); this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } PEMetadataView::~PEMetadataView() { } void PEMetadataView::titleEntry() { if (m_titleEdit->isModified()) { emit metadataChanged("title", m_titleEdit->text()); m_titleEdit->setModified(false); } } void PEMetadataView::authorEntry() { if (m_authorEdit->isModified()) { emit metadataChanged("author", m_authorEdit->text()); m_authorEdit->setModified(false); } } void PEMetadataView::descriptionEntry() { if (m_descriptionEdit->document()->isModified()) { emit metadataChanged("description", m_descriptionEdit->toHtml()); m_descriptionEdit->document()->setModified(false); } } void PEMetadataView::urlEntry() { if (m_urlEdit->isModified()) { emit metadataChanged("url", m_urlEdit->text()); m_urlEdit->setModified(false); } } void PEMetadataView::labelEntry() { if (m_labelEdit->isModified()) { emit metadataChanged("label", m_labelEdit->text()); m_labelEdit->setModified(false); } } void PEMetadataView::familyEntry() { if (m_familyEdit->isModified()) { emit metadataChanged("family", m_familyEdit->text()); m_familyEdit->setModified(false); } } void PEMetadataView::variantEntry() { if (m_variantEdit->isModified()) { emit metadataChanged("variant", m_variantEdit->text()); m_variantEdit->setModified(false); } } void PEMetadataView::dateEntry() { } void PEMetadataView::propertiesEntry() { emit propertiesChanged(m_propertiesEdit->hash()); } const QHash & PEMetadataView::properties() { return m_propertiesEdit->hash(); } void PEMetadataView::tagsEntry() { static QStringList keys; keys = m_tagsEdit->hash().keys(); emit tagsChanged(keys); } void PEMetadataView::initMetadata(const QDomDocument & doc) { QWidget * widget = QApplication::focusWidget(); if (widget) { QList children = m_mainFrame->findChildren(); if (children.contains(widget)) { widget->blockSignals(true); } } if (m_mainFrame) { this->setWidget(NULL); delete m_mainFrame; m_mainFrame = NULL; } QDomElement root = doc.documentElement(); QDomElement label = root.firstChildElement("label"); QDomElement author = root.firstChildElement("author"); QDomElement descr = root.firstChildElement("description"); QDomElement title = root.firstChildElement("title"); QDomElement date = root.firstChildElement("date"); QDomElement url = root.firstChildElement("url"); QStringList readOnlyKeys; QHash tagHash; QDomElement tags = root.firstChildElement("tags"); QDomElement tag = tags.firstChildElement("tag"); while (!tag.isNull()) { tagHash.insert(tag.text(), ""); tag = tag.nextSiblingElement("tag"); } QString family; QString variant; QHash propertyHash; QDomElement properties = root.firstChildElement("properties"); QDomElement prop = properties.firstChildElement("property"); while (!prop.isNull()) { QString name = prop.attribute("name"); QString value = prop.text(); if (name.compare("family", Qt::CaseInsensitive) == 0) { family = value; } else if (name.compare("variant", Qt::CaseInsensitive) == 0) { variant = value; } else { propertyHash.insert(name, value); } prop = prop.nextSiblingElement("property"); } m_mainFrame = new QFrame(this); m_mainFrame->setObjectName("metadataMainFrame"); QVBoxLayout *mainLayout = new QVBoxLayout(m_mainFrame); mainLayout->setSizeConstraint( QLayout::SetMinAndMaxSize ); QLabel *explanation = new QLabel(tr("This is where you edit the metadata for the part ...")); mainLayout->addWidget(explanation); QFormLayout * formLayout = new QFormLayout(); QFrame * formFrame = new QFrame; mainLayout->addWidget(formFrame); m_titleEdit = new QLineEdit(); m_titleEdit->setText(title.text()); connect(m_titleEdit, SIGNAL(editingFinished()), this, SLOT(titleEntry())); m_titleEdit->setObjectName("PartsEditorLineEdit"); m_titleEdit->setStatusTip(tr("Set the part's title")); formLayout->addRow(tr("Title"), m_titleEdit); m_dateEdit = new QLineEdit(); m_dateEdit->setText(date.text()); connect(m_dateEdit, SIGNAL(editingFinished()), this, SLOT(dateEntry())); m_dateEdit->setObjectName("PartsEditorLineEdit"); m_dateEdit->setStatusTip(tr("Set the part's date")); m_dateEdit->setEnabled(false); formLayout->addRow(tr("Date"), m_dateEdit); m_authorEdit = new QLineEdit(); m_authorEdit->setText(author.text()); connect(m_authorEdit, SIGNAL(editingFinished()), this, SLOT(authorEntry())); m_authorEdit->setObjectName("PartsEditorLineEdit"); m_authorEdit->setStatusTip(tr("Set the part's author")); formLayout->addRow(tr("Author"), m_authorEdit); m_descriptionEdit = new FocusOutTextEdit(); m_descriptionEdit->setText(descr.text()); m_descriptionEdit->document()->setModified(false); connect(m_descriptionEdit, SIGNAL(focusOut()), this, SLOT(descriptionEntry())); m_descriptionEdit->setObjectName("PartsEditorTextEdit"); m_descriptionEdit->setStatusTip(tr("Set the part's description--you can use simple html (as defined by Qt's Rich Text)")); formLayout->addRow(tr("Description"), m_descriptionEdit); m_labelEdit = new QLineEdit(); m_labelEdit->setText(label.text()); connect(m_labelEdit, SIGNAL(editingFinished()), this, SLOT(labelEntry())); m_labelEdit->setObjectName("PartsEditorLineEdit"); m_labelEdit->setStatusTip(tr("Set the default part label prefix")); formLayout->addRow(tr("Label"), m_labelEdit); m_urlEdit = new QLineEdit(); m_urlEdit->setText(url.text()); connect(m_urlEdit, SIGNAL(editingFinished()), this, SLOT(urlEntry())); m_urlEdit->setObjectName("PartsEditorLineEdit"); m_urlEdit->setStatusTip(tr("Set the part's url if it is described on a web page")); formLayout->addRow(tr("URL"), m_urlEdit); m_familyEdit = new QLineEdit(); m_familyEdit->setText(family); connect(m_familyEdit, SIGNAL(editingFinished()), this, SLOT(familyEntry())); m_familyEdit->setObjectName("PartsEditorLineEdit"); m_familyEdit->setStatusTip(tr("Set the part's family--what other parts is this part related to")); formLayout->addRow(tr("Family"), m_familyEdit); m_variantEdit = new QLineEdit(); m_variantEdit->setText(variant); connect(m_variantEdit, SIGNAL(editingFinished()), this, SLOT(variantEntry())); m_variantEdit->setObjectName("PartsEditorLineEdit"); m_variantEdit->setStatusTip(tr("Set the part's variant--this makes it unique from all other parts in the same family")); formLayout->addRow(tr("Variant"), m_variantEdit); m_propertiesEdit = new HashPopulateWidget("", propertyHash, readOnlyKeys, false, this); m_propertiesEdit->setObjectName("PartsEditorPropertiesEdit"); m_propertiesEdit->setStatusTip(tr("Set the part's properties")); connect(m_propertiesEdit, SIGNAL(changed()), this, SLOT(propertiesEntry())); formLayout->addRow(tr("Properties"), m_propertiesEdit); m_tagsEdit = new HashPopulateWidget("", tagHash, readOnlyKeys, true, this); m_tagsEdit->setObjectName("PartsEditorPropertiesEdit"); m_tagsEdit->setStatusTip(tr("Set the part's tags")); connect(m_tagsEdit, SIGNAL(changed()), this, SLOT(tagsEntry())); formLayout->addRow(tr("Tags"), m_tagsEdit); formFrame->setLayout(formLayout); m_mainFrame->setLayout(mainLayout); this->setWidget(m_mainFrame); } void PEMetadataView::resetProperty(const QString & name, const QString & value) { if (name == "family") m_familyEdit->setText(value); else if (name == "variant") m_variantEdit->setText(value); } QString PEMetadataView::family() { return m_familyEdit->text(); } QString PEMetadataView::variant() { return m_variantEdit->text(); } fritzing-app-0.9.3b/src/partseditor/pemetadataview.h000066400000000000000000000054451272363601300226120ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6483 $: $Author: irascibl@gmail.com $: $Date: 2012-09-26 15:45:37 +0200 (Mi, 26. Sep 2012) $ ********************************************************************/ #ifndef METADATAVIEW_H #define METADATAVIEW_H #include #include #include #include #include #include #include #include #include #include #include "../items/itembase.h" #include "../items/wire.h" #include "../connectors/connectoritem.h" #include "../referencemodel/referencemodel.h" class PEMetadataView : public QScrollArea { Q_OBJECT public: PEMetadataView(QWidget * parent = 0); ~PEMetadataView(); void initMetadata(const QDomDocument &); void resetProperty(const QString & name, const QString & value); QString family(); QString variant(); const QHash & properties(); signals: void metadataChanged(const QString & name, const QString & value); void propertiesChanged(const QHash &); void tagsChanged(const QStringList &); protected slots: void titleEntry(); void authorEntry(); void descriptionEntry(); void labelEntry(); void familyEntry(); void variantEntry(); void dateEntry(); void urlEntry(); void propertiesEntry(); void tagsEntry(); protected: QPointer m_titleEdit; QPointer m_dateEdit; QPointer m_authorEdit; QPointer m_familyEdit; QPointer m_variantEdit; QPointer m_labelEdit; QPointer m_urlEdit; QPointer m_descriptionEdit; QPointer m_propertiesEdit; QPointer m_tagsEdit; QPointer m_mainFrame; }; class FocusOutTextEdit :public QTextEdit { Q_OBJECT public: FocusOutTextEdit(QWidget * parent = 0); ~FocusOutTextEdit(); signals: void focusOut(); protected: void focusOutEvent(QFocusEvent *); }; #endif fritzing-app-0.9.3b/src/partseditor/pesvgview.cpp000066400000000000000000000103401272363601300221520ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "pesvgview.h" #include "pegraphicsitem.h" #include "peutils.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../debugdialog.h" #include #include #include #include #include #include ////////////////////////////////////// PESvgView::PESvgView(QWidget * parent) : QFrame(parent) { this->setObjectName("peSVG"); m_pegi = NULL; QVBoxLayout * mainLayout = new QVBoxLayout; m_filename = new QLabel(); mainLayout->addWidget(m_filename); QFrame * boundsFrame = new QFrame; QHBoxLayout * boundsLayout = new QHBoxLayout; QLabel * label = new QLabel("x:"); boundsLayout->addWidget(label); m_x = new QLabel; boundsLayout->addWidget(m_x); boundsLayout->addSpacing(PEUtils::Spacing); label = new QLabel("y:"); boundsLayout->addWidget(label); m_y = new QLabel; boundsLayout->addWidget(m_y); boundsLayout->addSpacing(PEUtils::Spacing); label = new QLabel(tr("width:")); boundsLayout->addWidget(label); m_width = new QLabel; boundsLayout->addWidget(m_width); boundsLayout->addSpacing(PEUtils::Spacing); label = new QLabel(tr("height:")); boundsLayout->addWidget(label); m_height = new QLabel; boundsLayout->addWidget(m_height); boundsLayout->addSpacing(PEUtils::Spacing); m_units = new QLabel(); boundsLayout->addWidget(m_units); boundsLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); boundsFrame->setLayout(boundsLayout); mainLayout->addWidget(boundsFrame); m_svgElement = new QLabel; m_svgElement->setWordWrap(false); m_svgElement->setTextFormat(Qt::PlainText); mainLayout->addWidget(m_svgElement); mainLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); //this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); this->setLayout(mainLayout); } PESvgView::~PESvgView() { } void PESvgView::highlightElement(PEGraphicsItem * pegi) { m_pegi = pegi; if (pegi == NULL) { m_svgElement->setText(""); m_x->setText(""); m_y->setText(""); m_width->setText(""); m_height->setText(""); return; } QString string; QTextStream stream(&string); pegi->element().save(stream, 0); string = TextUtils::killXMLNS(string); int ix = string.indexOf("\n"); if (ix > 0) { int jx = string.indexOf("\n", ix + 1); if (jx >= 0) { string.truncate(jx - 1); } else { string.truncate(ix + 200); } } else { string.truncate(200); } m_svgElement->setText(string); QPointF p = pegi->offset(); m_x->setText(PEUtils::convertUnitsStr(p.x())); m_y->setText(PEUtils::convertUnitsStr(p.y())); QRectF r = pegi->rect(); m_width->setText(PEUtils::convertUnitsStr(r.width())); m_height->setText(PEUtils::convertUnitsStr(r.height())); m_units->setText(QString("(%1)").arg(PEUtils::Units)); } void PESvgView::setChildrenVisible(bool vis) { foreach (QWidget * widget, findChildren()) { widget->setVisible(vis); } } void PESvgView::setFilename(const QString & filename) { m_filename->setText(filename); } fritzing-app-0.9.3b/src/partseditor/pesvgview.h000066400000000000000000000032551272363601300216260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PESVGVIEW_H #define PESVGVIEW_H #include #include #include #include #include #include #include #include #include #include class PESvgView : public QFrame { Q_OBJECT public: PESvgView(QWidget * parent = NULL); ~PESvgView(); void highlightElement(class PEGraphicsItem *); void setChildrenVisible(bool vis); void setFilename(const QString &); protected: QLabel * m_svgElement; QLabel * m_height; QLabel * m_width; QLabel * m_x; QLabel * m_y; QLabel * m_filename; QLabel * m_units; class PEGraphicsItem * m_pegi; }; #endif fritzing-app-0.9.3b/src/partseditor/petoolview.cpp000066400000000000000000000365311272363601300223420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "peutils.h" #include "petoolview.h" #include "pegraphicsitem.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../debugdialog.h" #include #include #include #include #include #include #include ////////////////////////////////////// static QPixmap * CheckImage = NULL; static QPixmap * NoCheckImage = NULL; ////////////////////////////////////// PEDoubleSpinBox::PEDoubleSpinBox(QWidget * parent) : QDoubleSpinBox(parent) { } void PEDoubleSpinBox::stepBy(int steps) { double amount; emit getSpinAmount(amount); setSingleStep(amount); QDoubleSpinBox::stepBy(steps); } ////////////////////////////////////// PEToolView::PEToolView(QWidget * parent) : QFrame (parent) { m_assignButton = NULL; if (CheckImage == NULL) CheckImage = new QPixmap(":/resources/images/icons/check.png"); if (NoCheckImage == NULL) NoCheckImage = new QPixmap(":/resources/images/icons/nocheck.png"); this->setObjectName("PEToolView"); /* QFile styleSheet(":/resources/styles/newpartseditor.qss"); if (!styleSheet.open(QIODevice::ReadOnly)) { DebugDialog::debug("Unable to open :/resources/styles/newpartseditor.qss"); } else { this->setStyleSheet(styleSheet.readAll()); } */ m_pegi = NULL; QVBoxLayout * mainLayout = new QVBoxLayout; mainLayout -> setObjectName("connectorFrame"); QSplitter * splitter = new QSplitter(Qt::Vertical); mainLayout->addWidget(splitter); QFrame * connectorsFrame = new QFrame; QVBoxLayout * connectorsLayout = new QVBoxLayout; QLabel * label = new QLabel(tr("Connector List (a checkmark means the graphic was selected)")); connectorsLayout->addWidget(label); m_connectorListWidget = new QTreeWidget(); m_connectorListWidget->setColumnCount(2); connect(m_connectorListWidget, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(switchConnector(QTreeWidgetItem *, QTreeWidgetItem *))); connectorsLayout->addWidget(m_connectorListWidget); m_busModeBox = new QCheckBox(tr("Set Internal Connections")); m_busModeBox->setChecked(false); m_busModeBox->setToolTip(tr("Set this checkbox to edit internal connections by drawing wires")); connect(m_busModeBox, SIGNAL(clicked(bool)), this, SLOT(busModeChangedSlot(bool))); connectorsLayout->addWidget(m_busModeBox); connectorsFrame->setLayout(connectorsLayout); splitter->addWidget(connectorsFrame); m_connectorInfoGroupBox = new QGroupBox; m_connectorInfoLayout = new QVBoxLayout; m_connectorInfoWidget = new QFrame; // a placeholder for PEUtils::connectorForm m_connectorInfoLayout->addWidget(m_connectorInfoWidget); m_terminalPointGroupBox = new QGroupBox("Terminal point"); m_terminalPointGroupBox->setToolTip(tr("Controls for setting the terminal point for a connector. The terminal point is where a wire will attach to the connector. You can also drag the crosshair of the current connector")); QVBoxLayout * anchorGroupLayout = new QVBoxLayout; QFrame * posRadioFrame = new QFrame; QHBoxLayout * posRadioLayout = new QHBoxLayout; QList positionNames; positionNames << "Center" << "W" << "N" << "S" << "E"; QList trPositionNames; trPositionNames << tr("Center") << tr("W") << tr("N") << tr("S") << tr("E"); QList trLongNames; trLongNames << tr("center") << tr("west") << tr("north") << tr("south") << tr("east"); for (int i = 0; i < positionNames.count(); i++) { QPushButton * button = new QPushButton(trPositionNames.at(i)); button->setProperty("how", positionNames.at(i)); button->setToolTip(tr("Sets the connector's terminal point to %1.").arg(trLongNames.at(i))); connect(button, SIGNAL(clicked()), this, SLOT(buttonChangeTerminalPoint())); posRadioLayout->addWidget(button); m_buttons.append(button); } posRadioLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); posRadioFrame->setLayout(posRadioLayout); anchorGroupLayout->addWidget(posRadioFrame); QFrame * posNumberFrame = new QFrame; QHBoxLayout * posNumberLayout = new QHBoxLayout; label = new QLabel("x"); posNumberLayout->addWidget(label); m_terminalPointX = new PEDoubleSpinBox; m_terminalPointX->setDecimals(4); m_terminalPointX->setToolTip(tr("Modifies the x-coordinate of the terminal point")); posNumberLayout->addWidget(m_terminalPointX); connect(m_terminalPointX, SIGNAL(getSpinAmount(double &)), this, SLOT(getSpinAmountSlot(double &)), Qt::DirectConnection); connect(m_terminalPointX, SIGNAL(valueChanged(double)), this, SLOT(terminalPointEntry())); posNumberLayout->addSpacing(PEUtils::Spacing); label = new QLabel("y"); posNumberLayout->addWidget(label); m_terminalPointY = new PEDoubleSpinBox; m_terminalPointY->setDecimals(4); m_terminalPointY->setToolTip(tr("Modifies the y-coordinate of the terminal point")); posNumberLayout->addWidget(m_terminalPointY); connect(m_terminalPointY, SIGNAL(getSpinAmount(double &)), this, SLOT(getSpinAmountSlot(double &)), Qt::DirectConnection); connect(m_terminalPointY, SIGNAL(valueChanged(double)), this, SLOT(terminalPointEntry())); posNumberLayout->addSpacing(PEUtils::Spacing); m_units = new QLabel(); posNumberLayout->addWidget(m_units); posNumberLayout->addSpacing(PEUtils::Spacing); m_terminalPointDragState = new QLabel(tr("Dragging disabled")); posNumberLayout->addWidget(m_terminalPointDragState); posNumberLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); posNumberFrame->setLayout(posNumberLayout); anchorGroupLayout->addWidget(posNumberFrame); m_terminalPointGroupBox->setLayout(anchorGroupLayout); m_connectorInfoLayout->addWidget(m_terminalPointGroupBox); m_connectorInfoLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); m_connectorInfoGroupBox->setLayout(m_connectorInfoLayout); splitter->addWidget(m_connectorInfoGroupBox); //this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //this->setWidget(splitter); this->setLayout(mainLayout); m_connectorListWidget->resize(m_connectorListWidget->width(), 0); enableConnectorChanges(false, false, false, false); } PEToolView::~PEToolView() { } void PEToolView::enableConnectorChanges(bool enableTerminalPointDrag, bool enableTerminalPointControls, bool enableInfo, bool enableAssign) { if (m_assignButton) { m_assignButton->setEnabled(enableAssign); } if (m_terminalPointGroupBox) { m_terminalPointGroupBox->setEnabled(enableTerminalPointControls); } if (m_connectorInfoWidget) { m_connectorInfoWidget->setEnabled(enableInfo); } if (m_terminalPointDragState) { if (enableTerminalPointDrag) { m_terminalPointDragState->setText(tr("Dragging enabled")); m_terminalPointDragState->setEnabled(true); } else { m_terminalPointDragState->setText(tr("Dragging disabled")); m_terminalPointDragState->setEnabled(false); } } } void PEToolView::initConnectors(QList * connectorList) { m_connectorListWidget->blockSignals(true); m_connectorListWidget->clear(); // deletes QTreeWidgetItems m_connectorList = connectorList; for (int ix = 0; ix < connectorList->count(); ix++) { QDomElement connector = connectorList->at(ix); QTreeWidgetItem *item = new QTreeWidgetItem; item->setData(0, Qt::DisplayRole, connector.attribute("name")); item->setData(0, Qt::UserRole, ix); item->setData(1, Qt::UserRole, ix); item->setData(0, Qt::DecorationRole, *NoCheckImage); m_connectorListWidget->addTopLevelItem(item); QLabel * label = new QLabel(""); //label->setPixmap(*NoCheckImage); m_connectorListWidget->setItemWidget(item, 1, label); } if (connectorList->count() > 0) { m_connectorListWidget->setCurrentItem(m_connectorListWidget->topLevelItem(0)); switchConnector(m_connectorListWidget->currentItem(), NULL); } m_connectorListWidget->blockSignals(false); } void PEToolView::showAssignedConnectors(const QDomDocument * svgDoc, ViewLayer::ViewID viewID) { QDomElement svgRoot = svgDoc->documentElement(); for (int i = 0; i < m_connectorListWidget->topLevelItemCount(); i++) { QTreeWidgetItem * item = m_connectorListWidget->topLevelItem(i); int index = item->data(0, Qt::UserRole).toInt(); QDomElement connector = m_connectorList->at(index); QString svgID, terminalID; bool ok = ViewLayer::getConnectorSvgIDs(connector, viewID, svgID, terminalID); if (!ok) { continue; } QDomElement element = TextUtils::findElementWithAttribute(svgRoot, "id", svgID); if (element.isNull()) { item->setData(0, Qt::DecorationRole, *NoCheckImage); } else { item->setData(0, Qt::DecorationRole, *CheckImage); } } hideConnectorListStuff(); m_connectorListWidget->repaint(); } void PEToolView::switchConnector(QTreeWidgetItem * current, QTreeWidgetItem * previous) { Q_UNUSED(previous); QWidget * widget = QApplication::focusWidget(); if (widget) { QList children = m_connectorInfoWidget->findChildren(); if (children.contains(widget)) { widget->blockSignals(true); } } if (m_connectorInfoWidget) { delete m_connectorInfoWidget; m_connectorInfoWidget = NULL; } if (current == NULL) return; int index = current->data(0, Qt::UserRole).toInt(); QDomElement element = m_connectorList->at(index); int pos = 99999; for (int ix = 0; ix < m_connectorInfoLayout->count(); ix++) { QLayoutItem * item = m_connectorInfoLayout->itemAt(ix); if (item->widget() == m_terminalPointGroupBox) { pos = ix; break; } } m_connectorInfoWidget = PEUtils::makeConnectorForm(element, index, this, false); m_connectorInfoLayout->insertWidget(pos, m_connectorInfoWidget); m_connectorInfoGroupBox->setTitle(tr("Connector %1").arg(element.attribute("name"))); m_units->setText(QString("(%1)").arg(PEUtils::Units)); hideConnectorListStuff(); emit switchedConnector(index); } void PEToolView::busModeChangedSlot(bool state) { emit busModeChanged(state); } void PEToolView::nameEntry() { changeConnector(); } void PEToolView::typeEntry() { changeConnector(); } void PEToolView::descriptionEntry() { changeConnector(); } void PEToolView::changeConnector() { QTreeWidgetItem * item = m_connectorListWidget->currentItem(); if (item == NULL) return; int index = item->data(0, Qt::UserRole).toInt(); ConnectorMetadata cmd; if (!PEUtils::fillInMetadata(index, this, cmd)) return; emit connectorMetadataChanged(&cmd); } void PEToolView::setCurrentConnector(const QDomElement & newConnector) { for (int ix = 0; ix < m_connectorListWidget->topLevelItemCount(); ix++) { QTreeWidgetItem * item = m_connectorListWidget->topLevelItem(ix); int index = item->data(0, Qt::UserRole).toInt(); QDomElement connector = m_connectorList->at(index); if (connector.attribute("id") == newConnector.attribute("id")) { m_connectorListWidget->setCurrentItem(item); hideConnectorListStuff(); return; } } } int PEToolView::currentConnectorIndex() { QTreeWidgetItem * item = m_connectorListWidget->currentItem(); if (item == NULL) return -1; int index = item->data(0, Qt::UserRole).toInt(); return index; } void PEToolView::setTerminalPointCoords(QPointF p) { m_terminalPointX->blockSignals(true); m_terminalPointY->blockSignals(true); m_terminalPointX->setValue(PEUtils::convertUnits(p.x())); m_terminalPointY->setValue(PEUtils::convertUnits(p.y())); m_terminalPointX->blockSignals(false); m_terminalPointY->blockSignals(false); } void PEToolView::setTerminalPointLimits(QSizeF sz) { m_terminalPointX->setRange(0, sz.width()); m_terminalPointY->setRange(0, sz.height()); } void PEToolView::buttonChangeTerminalPoint() { QString how = sender()->property("how").toString(); emit terminalPointChanged(how); } void PEToolView::terminalPointEntry() { if (sender() == m_terminalPointX) { emit terminalPointChanged("x", PEUtils::unconvertUnits(m_terminalPointX->value())); } else if (sender() == m_terminalPointY) { emit terminalPointChanged("y", PEUtils::unconvertUnits(m_terminalPointY->value())); } } void PEToolView::getSpinAmountSlot(double & d) { emit getSpinAmount(d); } void PEToolView::removeConnector() { QTreeWidgetItem * item = m_connectorListWidget->currentItem(); if (item == NULL) return; int index = item->data(0, Qt::UserRole).toInt(); QDomElement element = m_connectorList->at(index); emit removedConnector(element); } void PEToolView::setChildrenVisible(bool vis) { foreach (QWidget * widget, findChildren()) { widget->setVisible(vis); } } void PEToolView::pickModeChangedSlot() { emit pickModeChanged(true); } void PEToolView::hideConnectorListStuff() { m_connectorListWidget->setHeaderHidden(true); QTreeWidgetItem * current = m_connectorListWidget->currentItem(); for (int i = 0; i < m_connectorListWidget->topLevelItemCount(); i++) { QTreeWidgetItem * item = m_connectorListWidget->topLevelItem(i); QWidget * widget = m_connectorListWidget->itemWidget(item, 1); if (qobject_cast(widget)) { if (item == current) ; // button is already there else { // remove the button and add the label m_connectorListWidget->removeItemWidget(item, 1); QLabel * label = new QLabel(); //label->setPixmap(*NoCheckImage); m_connectorListWidget->setItemWidget(item, 1, label); } } else { if (item != current) ; // label is already there else { m_assignButton = new QPushButton(tr("Select graphic")); m_assignButton->setMaximumWidth(150); connect(m_assignButton, SIGNAL(clicked()), this, SLOT(pickModeChangedSlot()), Qt::DirectConnection); m_assignButton->setToolTip(tr("Use the cursor location and mouse wheel to navigate to the SVG element which you want to assign to the current connector, then mouse down to select it.")); m_connectorListWidget->setItemWidget(item, 1, m_assignButton); } } } } void PEToolView::setCurrentConnector(int ix) { m_connectorListWidget->setCurrentItem(m_connectorListWidget->topLevelItem(ix)); switchConnector(m_connectorListWidget->currentItem(), NULL); } fritzing-app-0.9.3b/src/partseditor/petoolview.h000066400000000000000000000067511272363601300220100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef PETOOLVIEW_H #define PETOOLVIEW_H #include #include #include #include #include #include #include #include #include #include #include #include "../viewlayer.h" class PEDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT public: PEDoubleSpinBox(QWidget * parent = 0); void stepBy (int steps); signals: void getSpinAmount(double &); }; class PEToolView : public QFrame { Q_OBJECT public: PEToolView(QWidget * parent = NULL); ~PEToolView(); void initConnectors(QList * connectorList); int currentConnectorIndex(); void setCurrentConnector(const QDomElement &); void setCurrentConnector(int index); void setTerminalPointCoords(QPointF); void setTerminalPointLimits(QSizeF); void setChildrenVisible(bool vis); void enableConnectorChanges(bool enableTerminalPointDrag, bool enableTerminalPointControls, bool enableInfo, bool enableAssign); void showAssignedConnectors(const QDomDocument * svgDoc, ViewLayer::ViewID); signals: void switchedConnector(int); void removedConnector(const QDomElement &); void pickModeChanged(bool); void busModeChanged(bool); void terminalPointChanged(const QString & how); void terminalPointChanged(const QString & coord, double value); void getSpinAmount(double &); void connectorMetadataChanged(struct ConnectorMetadata *); protected slots: void switchConnector(QTreeWidgetItem * current, QTreeWidgetItem * previous); void pickModeChangedSlot(); void busModeChangedSlot(bool); void descriptionEntry(); void typeEntry(); void nameEntry(); void buttonChangeTerminalPoint(); void terminalPointEntry(); void getSpinAmountSlot(double &); void removeConnector(); protected: void changeConnector(); void hideConnectorListStuff(); protected: QPointer m_connectorListWidget; QList m_buttons; QPointer m_pegi; QList * m_connectorList; QPointer m_connectorInfoGroupBox; QPointer m_connectorInfoLayout; QPointer m_connectorInfoWidget; QPointer m_busModeBox; QPointer m_terminalPointX; QPointer m_terminalPointY; QPointer m_units; QPointer m_terminalPointGroupBox; QPointer m_terminalPointDragState; QPointer m_assignButton; }; #endif fritzing-app-0.9.3b/src/partseditor/peutils.cpp000066400000000000000000000201401272363601300216170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "peutils.h" #include "hashpopulatewidget.h" #include "../debugdialog.h" #include "../utils/graphicsutils.h" #include #include #include #include #include #include #include QString PEUtils::Units = "mm"; const int PEUtils::Spacing = 10; ///////////////////////////////////////////////////////// QString PEUtils::convertUnitsStr(double val) { return QString::number(PEUtils::convertUnits(val)); } double PEUtils::convertUnits(double val) { if (Units.compare("in") == 0) { return val / GraphicsUtils::SVGDPI; } else if (Units.compare("mm") == 0) { return val * 25.4 / GraphicsUtils::SVGDPI; } return val; } double PEUtils::unconvertUnits(double val) { if (Units.compare("in") == 0) { return val * GraphicsUtils::SVGDPI; } else if (Units.compare("mm") == 0) { return val * GraphicsUtils::SVGDPI / 25.4; } return val; } QWidget * PEUtils::makeConnectorForm(const QDomElement & connector, int index, QObject * slotHolder, bool alternating) { QFrame * frame = new QFrame(); if (alternating) { frame->setObjectName(index % 2 == 0 ? "NewPartsEditorConnector0Frame" : "NewPartsEditorConnector1Frame"); } else { frame->setObjectName("NewPartsEditorConnectorFrame"); } QVBoxLayout * mainLayout = new QVBoxLayout(); mainLayout->setMargin(0); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); QFrame * nameFrame = new QFrame(); QHBoxLayout * nameLayout = new QHBoxLayout(); QLabel * justLabel = new QLabel(QObject::tr("Name:")); justLabel->setObjectName("NewPartsEditorLabel"); nameLayout->addWidget(justLabel); QLineEdit * nameEdit = new QLineEdit(); nameEdit->setText(connector.attribute("name")); QObject::connect(nameEdit, SIGNAL(editingFinished()), slotHolder, SLOT(nameEntry())); nameEdit->setObjectName("NewPartsEditorLineEdit"); nameEdit->setStatusTip(QObject::tr("Set the connectors's title")); nameEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); nameEdit->setProperty("index", index); nameEdit->setProperty("type", "name"); nameEdit->setProperty("id", connector.attribute("id")); nameLayout->addWidget(nameEdit); nameLayout->addSpacing(Spacing); HashRemoveButton * hashRemoveButton = new HashRemoveButton(NULL, NULL, NULL); hashRemoveButton->setProperty("index", index); QObject::connect(hashRemoveButton, SIGNAL(clicked(HashRemoveButton *)), slotHolder, SLOT(removeConnector())); nameLayout->addWidget(hashRemoveButton); nameFrame->setLayout(nameLayout); mainLayout->addWidget(nameFrame); QFrame * descriptionFrame = new QFrame(); QHBoxLayout * descriptionLayout = new QHBoxLayout(); justLabel = new QLabel(QObject::tr("Description:")); justLabel->setObjectName("NewPartsEditorLabel"); descriptionLayout->addWidget(justLabel); QLineEdit * descriptionEdit = new QLineEdit(); QDomElement description = connector.firstChildElement("description"); descriptionEdit->setText(description.text()); QObject::connect(descriptionEdit, SIGNAL(editingFinished()), slotHolder, SLOT(descriptionEntry())); descriptionEdit->setObjectName("NewPartsEditorLineEdit"); descriptionEdit->setStatusTip(QObject::tr("Set the connectors's description")); descriptionEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); descriptionEdit->setProperty("index", index); descriptionEdit->setProperty("type", "description"); descriptionLayout->addWidget(descriptionEdit); descriptionFrame->setLayout(descriptionLayout); mainLayout->addWidget(descriptionFrame); QFrame * idFrame = new QFrame(); QHBoxLayout * idLayout = new QHBoxLayout(); justLabel = new QLabel(QObject::tr("id:")); justLabel->setObjectName("NewPartsEditorLabel"); idLayout->addWidget(justLabel); justLabel = new QLabel(connector.attribute("id")); justLabel->setObjectName("NewPartsEditorLabel"); idLayout->addWidget(justLabel); idLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); Connector::ConnectorType ctype = Connector::connectorTypeFromName(connector.attribute("type")); justLabel = new QLabel(QObject::tr("type:")); justLabel->setObjectName("NewPartsEditorLabel"); idLayout->addWidget(justLabel); QRadioButton * radioButton = new QRadioButton(MaleSymbolString); QObject::connect(radioButton, SIGNAL(clicked()), slotHolder, SLOT(typeEntry())); radioButton->setObjectName("NewPartsEditorRadio"); if (ctype == Connector::Male) radioButton->setChecked(true); radioButton->setProperty("value", Connector::Male); radioButton->setProperty("index", index); radioButton->setProperty("type", "radio"); idLayout->addWidget(radioButton); radioButton = new QRadioButton(FemaleSymbolString); QObject::connect(radioButton, SIGNAL(clicked()), slotHolder, SLOT(typeEntry())); radioButton->setObjectName("NewPartsEditorRadio"); if (ctype == Connector::Female) radioButton->setChecked(true); radioButton->setProperty("value", Connector::Female); radioButton->setProperty("index", index); radioButton->setProperty("type", "radio"); idLayout->addWidget(radioButton); radioButton = new QRadioButton(QObject::tr("Pad")); QObject::connect(radioButton, SIGNAL(clicked()), slotHolder, SLOT(typeEntry())); radioButton->setObjectName("NewPartsEditorRadio"); if (ctype == Connector::Pad) radioButton->setChecked(true); radioButton->setProperty("value", Connector::Pad); idLayout->addWidget(radioButton); radioButton->setProperty("index", index); radioButton->setProperty("type", "radio"); idLayout->addSpacing(Spacing); idFrame->setLayout(idLayout); mainLayout->addWidget(idFrame); frame->setLayout(mainLayout); return frame; } bool PEUtils::fillInMetadata(int senderIndex, QWidget * parent, ConnectorMetadata & cmd) { bool result = false; QList widgets = parent->findChildren(); foreach (QWidget * widget, widgets) { bool ok; int index = widget->property("index").toInt(&ok); if (!ok) continue; if (index != senderIndex) continue; QString type = widget->property("type").toString(); if (type == "name") { QLineEdit * lineEdit = qobject_cast(widget); if (lineEdit == NULL) continue; cmd.connectorName = lineEdit->text(); cmd.connectorID = widget->property("id").toString(); result = true; } else if (type == "radio") { QRadioButton * radioButton = qobject_cast(widget); if (radioButton == NULL) continue; if (!radioButton->isChecked()) continue; cmd.connectorType = (Connector::ConnectorType) radioButton->property("value").toInt(); } else if (type == "description") { QLineEdit * lineEdit = qobject_cast(widget); if (lineEdit == NULL) continue; cmd.connectorDescription = lineEdit->text(); } } return result; } fritzing-app-0.9.3b/src/partseditor/peutils.h000066400000000000000000000033171272363601300212730ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PEUTILS_H #define PEUTILS_H #include "../connectors/connector.h" #include #include struct ConnectorMetadata { Connector::ConnectorType connectorType; QString connectorName; QString connectorDescription; QString connectorID; int index; }; class PEUtils { public: static QString convertUnitsStr(double val); static double convertUnits(double val); static double unconvertUnits(double val); static QWidget * makeConnectorForm(const QDomElement & connector, int index, QObject * slotHolder, bool alternating); static bool fillInMetadata(int senderIndex, QWidget * parentWidget, ConnectorMetadata & cmd); public: static QString Units; const static int Spacing; }; #endif fritzing-app-0.9.3b/src/processeventblocker.cpp000066400000000000000000000043061272363601300216620ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "processeventblocker.h" #include #include ProcessEventBlocker * ProcessEventBlocker::m_singleton = new ProcessEventBlocker(); ProcessEventBlocker::ProcessEventBlocker() { m_count = 0; } void ProcessEventBlocker::processEvents() { m_singleton->_processEvents(); } void ProcessEventBlocker::processEvents(int maxTime) { m_singleton->_processEvents(maxTime); } bool ProcessEventBlocker::isProcessing() { return m_singleton->_isProcessing(); } void ProcessEventBlocker::block() { return m_singleton->_inc(1); } void ProcessEventBlocker::unblock() { return m_singleton->_inc(-1); } void ProcessEventBlocker::_processEvents() { m_mutex.lock(); m_count++; m_mutex.unlock(); QApplication::processEvents(); m_mutex.lock(); m_count--; m_mutex.unlock(); } void ProcessEventBlocker::_processEvents(int maxTime) { m_mutex.lock(); m_count++; m_mutex.unlock(); QApplication::processEvents(QEventLoop::AllEvents, maxTime); m_mutex.lock(); m_count--; m_mutex.unlock(); } bool ProcessEventBlocker::_isProcessing() { m_mutex.lock(); bool result = m_count > 0; m_mutex.unlock(); return result; } void ProcessEventBlocker::_inc(int i) { m_mutex.lock(); m_count += i; m_mutex.unlock(); } fritzing-app-0.9.3b/src/processeventblocker.h000066400000000000000000000030411272363601300213220ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PROCESSEVENTBLOCKER_H_ #define PROCESSEVENTBLOCKER_H_ #include class ProcessEventBlocker { protected: ProcessEventBlocker(); ~ProcessEventBlocker(); bool _isProcessing(); void _processEvents(); void _processEvents(int maxTime); void _inc(int i); public: static void processEvents(); static void processEvents(int maxTime); static bool isProcessing(); static void block(); static void unblock(); protected: int m_count; QMutex m_mutex; protected: static ProcessEventBlocker * m_singleton; }; #endif /* PROCESSEVENTBLOCKER_H_ */ fritzing-app-0.9.3b/src/program/000077500000000000000000000000001272363601300165405ustar00rootroot00000000000000fritzing-app-0.9.3b/src/program/console.cpp000066400000000000000000000062141272363601300207110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Denis Shienkov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSerialPort module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "console.h" #include #include Console::Console(QWidget *parent) : QPlainTextEdit(parent) , localEchoEnabled(false) { document()->setMaximumBlockCount(1000); QFont font = document()->defaultFont(); font.setFamily("Droid Sans Mono"); document()->setDefaultFont(font); //setCenterOnScroll(true); } void Console::putData(const QByteArray &data) { insertPlainText(QString(data)); QScrollBar *bar = verticalScrollBar(); bar->setValue(bar->maximum()); } void Console::setLocalEchoEnabled(bool set) { localEchoEnabled = set; } void Console::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Backspace: case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: break; default: if (localEchoEnabled) QPlainTextEdit::keyPressEvent(e); emit getData(e->text().toLocal8Bit()); } } void Console::mousePressEvent(QMouseEvent *e) { Q_UNUSED(e) setFocus(); } void Console::mouseDoubleClickEvent(QMouseEvent *e) { Q_UNUSED(e) } void Console::contextMenuEvent(QContextMenuEvent *e) { Q_UNUSED(e) } fritzing-app-0.9.3b/src/program/console.h000066400000000000000000000050701272363601300203550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Denis Shienkov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSerialPort module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef CONSOLE_H #define CONSOLE_H #include class Console : public QPlainTextEdit { Q_OBJECT signals: void getData(const QByteArray &data); public: explicit Console(QWidget *parent = 0); void putData(const QByteArray &data); void setLocalEchoEnabled(bool set); protected: virtual void keyPressEvent(QKeyEvent *e); virtual void mousePressEvent(QMouseEvent *e); virtual void mouseDoubleClickEvent(QMouseEvent *e); virtual void contextMenuEvent(QContextMenuEvent *e); private: bool localEchoEnabled; }; #endif // CONSOLE_H fritzing-app-0.9.3b/src/program/consolesettings.cpp000066400000000000000000000204051272363601300224700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Denis Shienkov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSerialPort module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "consolesettings.h" #include "ui_consolesettings.h" #include #include #include QT_USE_NAMESPACE ConsoleSettings::ConsoleSettings(QWidget *parent) : QDialog(parent), ui(new Ui::ConsoleSettings) { ui->setupUi(this); intValidator = new QIntValidator(0, 4000000, this); ui->baudRateBox->setInsertPolicy(QComboBox::NoInsert); connect(ui->applyButton, SIGNAL(clicked()), this, SLOT(apply())); connect(ui->serialPortInfoListBox, SIGNAL(currentIndexChanged(int)), this, SLOT(showPortInfo(int))); connect(ui->baudRateBox, SIGNAL(currentIndexChanged(int)), this, SLOT(checkCustomBaudRatePolicy(int))); fillPortsParameters(); fillPortsInfo(); updateSettings(); } ConsoleSettings::~ConsoleSettings() { delete ui; } ConsoleSettings::Settings ConsoleSettings::settings() const { return currentSettings; } void ConsoleSettings::showPortInfo(int idx) { if (idx != -1) { QStringList list = ui->serialPortInfoListBox->itemData(idx).toStringList(); ui->descriptionLabel->setText(tr("Description: %1").arg(list.at(1))); ui->manufacturerLabel->setText(tr("Manufacturer: %1").arg(list.at(2))); ui->serialNumberLabel->setText(tr("Serial number: %1").arg(list.at(3))); ui->locationLabel->setText(tr("Location: %1").arg(list.at(4))); ui->vidLabel->setText(tr("Vendor Identifier: %1").arg(list.at(5))); ui->pidLabel->setText(tr("Product Identifier: %1").arg(list.at(6))); } } void ConsoleSettings::apply() { updateSettings(); hide(); } void ConsoleSettings::checkCustomBaudRatePolicy(int idx) { bool isCustomBaudRate = !ui->baudRateBox->itemData(idx).isValid(); ui->baudRateBox->setEditable(isCustomBaudRate); if (isCustomBaudRate) { ui->baudRateBox->clearEditText(); QLineEdit *edit = ui->baudRateBox->lineEdit(); edit->setValidator(intValidator); } } void ConsoleSettings::fillPortsParameters() { ui->baudRateBox->addItem(QStringLiteral("9600"), QSerialPort::Baud9600); ui->baudRateBox->addItem(QStringLiteral("19200"), QSerialPort::Baud19200); ui->baudRateBox->addItem(QStringLiteral("38400"), QSerialPort::Baud38400); ui->baudRateBox->addItem(QStringLiteral("115200"), QSerialPort::Baud115200); ui->baudRateBox->addItem(QStringLiteral("Custom")); ui->dataBitsBox->addItem(QStringLiteral("5"), QSerialPort::Data5); ui->dataBitsBox->addItem(QStringLiteral("6"), QSerialPort::Data6); ui->dataBitsBox->addItem(QStringLiteral("7"), QSerialPort::Data7); ui->dataBitsBox->addItem(QStringLiteral("8"), QSerialPort::Data8); ui->dataBitsBox->setCurrentIndex(3); ui->parityBox->addItem(QStringLiteral("None"), QSerialPort::NoParity); ui->parityBox->addItem(QStringLiteral("Even"), QSerialPort::EvenParity); ui->parityBox->addItem(QStringLiteral("Odd"), QSerialPort::OddParity); ui->parityBox->addItem(QStringLiteral("Mark"), QSerialPort::MarkParity); ui->parityBox->addItem(QStringLiteral("Space"), QSerialPort::SpaceParity); ui->stopBitsBox->addItem(QStringLiteral("1"), QSerialPort::OneStop); #ifdef Q_OS_WIN ui->stopBitsBox->addItem(QStringLiteral("1.5"), QSerialPort::OneAndHalfStop); #endif ui->stopBitsBox->addItem(QStringLiteral("2"), QSerialPort::TwoStop); ui->flowControlBox->addItem(QStringLiteral("None"), QSerialPort::NoFlowControl); ui->flowControlBox->addItem(QStringLiteral("RTS/CTS"), QSerialPort::HardwareControl); ui->flowControlBox->addItem(QStringLiteral("XON/XOFF"), QSerialPort::SoftwareControl); } void ConsoleSettings::fillPortsInfo() { ui->serialPortInfoListBox->clear(); static const QString blankString = QObject::tr("N/A"); QString description; QString manufacturer; QString serialNumber; foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { QStringList list; description = info.description(); manufacturer = info.manufacturer(); //serialNumber = info.serialNumber(); list << info.portName() << (!description.isEmpty() ? description : blankString) << (!manufacturer.isEmpty() ? manufacturer : blankString) << (!serialNumber.isEmpty() ? serialNumber : blankString) << info.systemLocation() << (info.vendorIdentifier() ? QString::number(info.vendorIdentifier(), 16) : blankString) << (info.productIdentifier() ? QString::number(info.productIdentifier(), 16) : blankString); ui->serialPortInfoListBox->addItem(list.first(), list); } } void ConsoleSettings::selectPortName(const QString portName) { if (portName.isEmpty()) return; int ix = ui->serialPortInfoListBox->findText(portName); if (ix >= 0) { currentSettings.name = portName; ui->serialPortInfoListBox->setCurrentIndex(ix); } } void ConsoleSettings::updateSettings() { currentSettings.name = ui->serialPortInfoListBox->currentText(); if (ui->baudRateBox->currentIndex() == 4) { currentSettings.baudRate = ui->baudRateBox->currentText().toInt(); } else { currentSettings.baudRate = static_cast( ui->baudRateBox->itemData(ui->baudRateBox->currentIndex()).toInt()); } currentSettings.stringBaudRate = QString::number(currentSettings.baudRate); currentSettings.dataBits = static_cast( ui->dataBitsBox->itemData(ui->dataBitsBox->currentIndex()).toInt()); currentSettings.stringDataBits = ui->dataBitsBox->currentText(); currentSettings.parity = static_cast( ui->parityBox->itemData(ui->parityBox->currentIndex()).toInt()); currentSettings.stringParity = ui->parityBox->currentText(); currentSettings.stopBits = static_cast( ui->stopBitsBox->itemData(ui->stopBitsBox->currentIndex()).toInt()); currentSettings.stringStopBits = ui->stopBitsBox->currentText(); currentSettings.flowControl = static_cast( ui->flowControlBox->itemData(ui->flowControlBox->currentIndex()).toInt()); currentSettings.stringFlowControl = ui->flowControlBox->currentText(); currentSettings.localEchoEnabled = ui->localEchoCheckBox->isChecked(); } fritzing-app-0.9.3b/src/program/consolesettings.h000066400000000000000000000062561272363601300221450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Denis Shienkov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSerialPort module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef CONSOLESETTINGS_H #define CONSOLESETTINGS_H #include #include QT_USE_NAMESPACE QT_BEGIN_NAMESPACE namespace Ui { class ConsoleSettings; } class QIntValidator; QT_END_NAMESPACE class ConsoleSettings : public QDialog { Q_OBJECT public: struct Settings { QString name; qint32 baudRate; QString stringBaudRate; QSerialPort::DataBits dataBits; QString stringDataBits; QSerialPort::Parity parity; QString stringParity; QSerialPort::StopBits stopBits; QString stringStopBits; QSerialPort::FlowControl flowControl; QString stringFlowControl; bool localEchoEnabled; }; explicit ConsoleSettings(QWidget *parent = 0); ~ConsoleSettings(); Settings settings() const; void selectPortName(const QString portName); private slots: void showPortInfo(int idx); void apply(); void checkCustomBaudRatePolicy(int idx); private: void fillPortsParameters(); void fillPortsInfo(); void updateSettings(); private: Ui::ConsoleSettings *ui; Settings currentSettings; QIntValidator *intValidator; }; #endif // CONSOLESETTINGS_H fritzing-app-0.9.3b/src/program/consolesettings.ui000066400000000000000000000116311272363601300223240ustar00rootroot00000000000000 ConsoleSettings 0 0 400 336 Settings Select Parameters BaudRate: Data bits: Parity: Stop bits: Flow control: Select Serial Port Description: Manufacturer: Serial number: Location: Vendor ID: Product ID: Qt::Horizontal 96 20 Apply Additional options Local echo true fritzing-app-0.9.3b/src/program/consolewindow.cpp000066400000000000000000000164421272363601300221450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Denis Shienkov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSerialPort module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "consolewindow.h" #include "ui_consolewindow.h" #include "console.h" #include "consolesettings.h" #include #include #include #include ConsoleWindow::ConsoleWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::ConsoleWindow) { QFile styleSheet(":/resources/styles/programwindow.qss"); this->setObjectName("consoleWindow"); if (!styleSheet.open(QIODevice::ReadOnly)) { qWarning("Unable to open :/resources/styles/programwindow.qss"); } else { QString ss = styleSheet.readAll(); #ifdef Q_OS_MAC int paneLoc = 4; int tabBarLoc = 0; #else int paneLoc = -1; int tabBarLoc = 5; #endif ss = ss.arg(paneLoc).arg(tabBarLoc); this->setStyleSheet(ss); } ui->setupUi(this); console = new Console; console->setEnabled(false); setCentralWidget(console); serial = new QSerialPort(this); settings = new ConsoleSettings; QSettings settings; if (!settings.value("consolewindow/state").isNull()) { restoreState(settings.value("consolewindow/state").toByteArray()); } if (!settings.value("consolewindow/geometry").isNull()) { restoreGeometry(settings.value("consolewindow/geometry").toByteArray()); } ui->actionConnect->setEnabled(true); ui->actionDisconnect->setEnabled(false); ui->actionQuit->setEnabled(true); ui->actionConfigure->setEnabled(true); initActionsConnections(); connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError))); connect(serial, SIGNAL(readyRead()), this, SLOT(readData())); connect(console, SIGNAL(getData(QByteArray)), this, SLOT(writeData(QByteArray))); } ConsoleWindow::~ConsoleWindow() { delete settings; delete ui; } void ConsoleWindow::closeEvent(QCloseEvent *event) { closeSerialPort(); QSettings settings; settings.setValue("consolewindow/geometry", saveGeometry()); settings.setValue("consolewindow/tate", saveState()); QMainWindow::closeEvent(event); } void ConsoleWindow::openSerialPort(const QString portName) { if (portName.isEmpty()) return; settings->selectPortName(portName); if (serial->isOpen()) { if (serial->portName().compare(portName) != 0) { closeSerialPort(); openSerialPort(); } } else { openSerialPort(); } } void ConsoleWindow::openSerialPort() { ConsoleSettings::Settings p = settings->settings(); serial->setPortName(p.name); if (serial->open(QIODevice::ReadWrite)) { serial->setBaudRate(p.baudRate); serial->setDataBits(p.dataBits); serial->setParity(p.parity); serial->setStopBits(p.stopBits); serial->setFlowControl(p.flowControl); console->setEnabled(true); console->setLocalEchoEnabled(p.localEchoEnabled); ui->actionConnect->setEnabled(false); ui->actionDisconnect->setEnabled(true); ui->actionConfigure->setEnabled(false); ui->statusBar->showMessage(tr("Connected to %1 : %2, %3, %4, %5, %6") .arg(p.name).arg(p.stringBaudRate).arg(p.stringDataBits) .arg(p.stringParity).arg(p.stringStopBits).arg(p.stringFlowControl)); } else { QMessageBox::critical(this, tr("Error"), serial->errorString()); ui->statusBar->showMessage(tr("Serial port open error")); } } void ConsoleWindow::closeSerialPort(const QString portName) { if (portName.isEmpty()) return; if (portName.compare(serial->portName()) == 0) { closeSerialPort(); } } void ConsoleWindow::closeSerialPort() { if (serial->isOpen()) { serial->close(); console->setEnabled(false); ui->actionConnect->setEnabled(true); ui->actionDisconnect->setEnabled(false); ui->actionConfigure->setEnabled(true); ui->statusBar->showMessage(tr("Disconnected")); } } void ConsoleWindow::about() { QMessageBox::about(this, tr("About Serial Monitor"), tr("This terminal displays the serial communication on the " "selected port, usually between your computer and the " "connected microcontroller.")); } void ConsoleWindow::writeData(const QByteArray &data) { serial->write(data); } void ConsoleWindow::readData() { QByteArray data = serial->readAll(); console->putData(data); } void ConsoleWindow::handleError(QSerialPort::SerialPortError error) { if (error == QSerialPort::ResourceError) { QMessageBox::critical(this, tr("Critical Error"), serial->errorString()); closeSerialPort(); } } void ConsoleWindow::initActionsConnections() { connect(ui->actionConnect, SIGNAL(triggered()), this, SLOT(openSerialPort())); connect(ui->actionDisconnect, SIGNAL(triggered()), this, SLOT(closeSerialPort())); connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close())); connect(ui->actionConfigure, SIGNAL(triggered()), settings, SLOT(show())); connect(ui->actionClear, SIGNAL(triggered()), console, SLOT(clear())); connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about())); connect(ui->actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } fritzing-app-0.9.3b/src/program/consolewindow.h000066400000000000000000000056341272363601300216130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Denis Shienkov ** Copyright (C) 2012 Laszlo Papp ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSerialPort module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef CONSOLEWINDOW_H #define CONSOLEWINDOW_H #include #include #include QT_BEGIN_NAMESPACE namespace Ui { class ConsoleWindow; } QT_END_NAMESPACE class Console; class ConsoleSettings; class ConsoleWindow : public QMainWindow { Q_OBJECT public: explicit ConsoleWindow(QWidget *parent = 0); ~ConsoleWindow(); void openSerialPort(const QString portName); void closeSerialPort(const QString portName); protected: void closeEvent(QCloseEvent *event); private slots: void openSerialPort(); void closeSerialPort(); void about(); void writeData(const QByteArray &data); void readData(); void handleError(QSerialPort::SerialPortError error); private: void initActionsConnections(); private: Ui::ConsoleWindow *ui; Console *console; ConsoleSettings *settings; QSerialPort *serial; }; #endif // CONSOLEWINDOW_H fritzing-app-0.9.3b/src/program/consolewindow.ui000066400000000000000000000067471272363601300220070ustar00rootroot00000000000000 ConsoleWindow 0 0 400 300 Serial Monitor 0 0 400 22 Monitor Tools TopToolBarArea false &About About program Alt+A About Qt C&onnect Connect to serial port Ctrl+O &Disconnect Disconnect from serial port Ctrl+D &Configure Configure serial port Alt+C C&lear Clear data Alt+L :/images/application-exit.png:/images/application-exit.png &Quit Ctrl+Q fritzing-app-0.9.3b/src/program/highlighter.cpp000066400000000000000000000143131272363601300215440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "highlighter.h" #include "syntaxer.h" #include "../debugdialog.h" #include #include #define STRINGOFFSET 10 #define COMMENTOFFSET 100 static const QChar CEscapeChar('\\'); QHash Highlighter::m_styleFormats; Highlighter::Highlighter(QTextEdit * textEdit) : QSyntaxHighlighter(textEdit) { m_syntaxer = NULL; } Highlighter::~Highlighter() { } void Highlighter::loadStyles(const QString & filename) { QFile file(filename); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) return; if (root.tagName() != "styles") return; QDomElement style = root.firstChildElement("style"); while (!style.isNull()) { QTextCharFormat * tcf = new QTextCharFormat(); QColor color(Qt::black); QString colorString = style.attribute("color"); if (!colorString.isEmpty()) { color.setNamedColor(colorString); tcf->setForeground(QBrush(color)); } QString italicString = style.attribute("italic"); if (italicString.compare("1") == 0) { tcf->setFontItalic(true); } QString boldString = style.attribute("bold"); if (boldString.compare("1") == 0) { tcf->setFontWeight(QFont::Bold); } QString underlineString = style.attribute("underline"); if (underlineString.compare("1") == 0) { tcf->setFontUnderline(true); } m_styleFormats.insert(style.attribute("name"), tcf); style = style.nextSiblingElement("style"); } } void Highlighter::setSyntaxer(Syntaxer * syntaxer) { m_syntaxer = syntaxer; } Syntaxer * Highlighter::syntaxer() { return m_syntaxer; } void Highlighter::highlightBlock(const QString &text) { if (!m_syntaxer) return; if (text.isEmpty()) { setCurrentBlockState(previousBlockState()); return; } setCurrentBlockState(0); int startCommentIndex = -1; int startStringIndex = -1; const CommentInfo * currentCommentInfo = NULL; int pbs = previousBlockState(); if (pbs <= 0) { m_syntaxer->matchCommentStart(text, 0, startCommentIndex, currentCommentInfo); } else if (pbs >= COMMENTOFFSET) { currentCommentInfo = m_syntaxer->getCommentInfo(previousBlockState() - COMMENTOFFSET); startCommentIndex = 0; } else if (pbs == STRINGOFFSET) { startStringIndex = 0; } QString noComment = text; while (startCommentIndex >= 0) { int endIndex = currentCommentInfo->m_multiLine ? text.indexOf(currentCommentInfo->m_end, startCommentIndex, currentCommentInfo->m_caseSensitive) : text.length(); int commentLength; if (endIndex == -1) { setCurrentBlockState(currentCommentInfo->m_index + COMMENTOFFSET); commentLength = text.length() - startCommentIndex; } else { commentLength = endIndex - startCommentIndex + currentCommentInfo->m_end.length(); } noComment.replace(startCommentIndex, commentLength, QString(commentLength, ' ')); QTextCharFormat * cf = m_styleFormats.value("Comment", NULL); if (cf != NULL) { setFormat(startCommentIndex, commentLength, *cf); } m_syntaxer->matchCommentStart(text, startCommentIndex + commentLength, startCommentIndex, currentCommentInfo); } highlightStrings(startStringIndex, noComment); highlightTerms(noComment); } void Highlighter::highlightStrings(int startStringIndex, QString & text) { if (startStringIndex < 0) { startStringIndex = m_syntaxer->matchStringStart(text, 0); } // TODO: not handling "" as a way to escape-quote while (startStringIndex >= 0) { int endIndex = -1; int ssi = startStringIndex; while (true) { endIndex = m_syntaxer->matchStringEnd(text, ssi + 1); if (!m_syntaxer->hlCStringChar()) { // only some languages use \ to escape break; } if (endIndex == -1) { break; } // TODO: escape char is backslash only; are there others in other compilers? if (text.at(endIndex - 1) != CEscapeChar) { break; } ssi = endIndex; } int stringLength; if (endIndex == -1) { setCurrentBlockState(STRINGOFFSET); stringLength = text.length() - startStringIndex; } else { stringLength = endIndex - startStringIndex + 1; } text.replace(startStringIndex, stringLength, QString(stringLength, ' ')); QTextCharFormat * sf = m_styleFormats.value("String", NULL); if (sf != NULL) { setFormat(startStringIndex, stringLength, *sf); } startStringIndex = m_syntaxer->matchStringStart(text, startStringIndex + stringLength); } } void Highlighter::highlightTerms(const QString & text) { int lastWordBreak = 0; int textLength = text.length(); int b; while (lastWordBreak < textLength) { for (b = lastWordBreak; b < textLength; b++) { if (!isWordChar(text.at(b))) break; } if (b > lastWordBreak) { TrieLeaf * leaf = NULL; if (m_syntaxer->matches(text.mid(lastWordBreak, b - lastWordBreak), leaf)) { SyntaxerTrieLeaf * stl = dynamic_cast(leaf); if (stl != NULL) { QString format = Syntaxer::formatFromList(stl->name()); QTextCharFormat * tcf = m_styleFormats.value(format, NULL); if (tcf != NULL) { setFormat(lastWordBreak, b - lastWordBreak, *tcf); } } } } lastWordBreak = b + 1; } } bool Highlighter::isWordChar(QChar c) { return c.isLetterOrNumber() || c == '#' || c == '_'; } fritzing-app-0.9.3b/src/program/highlighter.h000066400000000000000000000033051272363601300212100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef HIGHLIGHTER_H_ #define HIGHLIGHTER_H_ #include #include #include #include #include class Highlighter : public QSyntaxHighlighter { Q_OBJECT public: Highlighter(QTextEdit * parent); ~Highlighter(); void setSyntaxer(class Syntaxer *); class Syntaxer * syntaxer(); public: static void loadStyles(const QString & filename); protected: void highlightBlock(const QString & text); bool isWordChar(QChar c); void highlightTerms(const QString & text); void highlightStrings(int startStringIndex, QString & text); protected: QPointer m_syntaxer; static QHash m_styleFormats; }; #endif /* HIGHLIGHTER_H_ */ fritzing-app-0.9.3b/src/program/platform.cpp000066400000000000000000000072721272363601300211000ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ********************************************************************/ #include "platform.h" #include "syntaxer.h" #include "../utils/folderutils.h" #include #include Platform::Platform(const QString &name) : QObject() { m_name = name; initSyntaxer(); initCommandLocation(); } Platform::~Platform() { // nothing to do } QString Platform::getName() const { return m_name; } void Platform::upload(QWidget * source, const QString &port, const QString &board, const QString &fileLocation) { // stub } Syntaxer * Platform::getSyntaxer() { return m_syntaxer; } void Platform::initSyntaxer() { Syntaxer * syntaxer = new Syntaxer(); QDir dir(FolderUtils::getApplicationSubFolderPath("translations")); dir.cd("syntax"); QStringList nameFilters; nameFilters << "*.xml"; QFileInfoList list = dir.entryInfoList(nameFilters, QDir::Files | QDir::NoSymLinks); foreach (QFileInfo fileInfo, list) { if (fileInfo.completeBaseName().compare(getName(), Qt::CaseInsensitive) == 0) { syntaxer->loadSyntax(fileInfo.absoluteFilePath()); break; } } m_syntaxer = syntaxer; } void Platform::initCommandLocation() { QSettings settings; m_commandLocation = settings.value(QString("programwindow/programmer.%1").arg(getName())).toString(); } QString Platform::getCommandLocation() const { return m_commandLocation; } void Platform::setCommandLocation(const QString &commandLocation) { m_commandLocation = commandLocation; QSettings settings; settings.setValue(QString("programwindow/programmer.%1").arg(getName()), commandLocation); emit commandLocationChanged(); } QStringList Platform::getExtensions() const { return m_extensions; } void Platform::setExtensions(const QStringList &suffixes) { m_extensions = suffixes; } QMap Platform::getBoards() const { return m_boards; } void Platform::setBoards(const QMap &boards) { m_boards = boards; } QUrl Platform::getReferenceUrl() const { return m_referenceUrl; } void Platform::setReferenceUrl(const QUrl &referenceUrl) { m_referenceUrl = referenceUrl; } bool Platform::canProgram() const { return m_canProgram; } void Platform::setCanProgram(bool canProgram) { m_canProgram = canProgram; } QUrl Platform::getDownloadUrl() const { return m_downloadUrl; } void Platform::setDownloadUrl(const QUrl &downloadUrl) { m_downloadUrl = downloadUrl; } QString Platform::getMinVersion() const { return m_minVersion; } void Platform::setMinVersion(const QString &minVersion) { m_minVersion = minVersion; } QString Platform::getDefaultBoardName() const { return m_defaultBoardName; } void Platform::setDefaultBoardName(const QString &defaultBoardName) { m_defaultBoardName = defaultBoardName; } QString Platform::getIdeName() const { return m_ideName; } void Platform::setIdeName(const QString &ideName) { m_ideName = ideName; } fritzing-app-0.9.3b/src/program/platform.h000066400000000000000000000050351272363601300205400ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ********************************************************************/ #include "syntaxer.h" #include #include #include #include #include #include #include #include #ifndef PLATFORM_H #define PLATFORM_H class Platform : public QObject { Q_OBJECT public: Platform(const QString &name); ~Platform(); virtual void upload(QWidget *source, const QString &port, const QString &board, const QString &fileLocation); Syntaxer *getSyntaxer(); QString getName() const; QString getCommandLocation() const; void setCommandLocation(const QString &commandLocation); QStringList getExtensions() const; void setExtensions(const QStringList &suffixes); QMap getBoards() const; void setBoards(const QMap &boards); QUrl getReferenceUrl() const; void setReferenceUrl(const QUrl &referenceUrl); bool canProgram() const; void setCanProgram(bool canProgram); QUrl getDownloadUrl() const; void setDownloadUrl(const QUrl &downloadUrl); QString getMinVersion() const; void setMinVersion(const QString &minVersion); QString getDefaultBoardName() const; void setDefaultBoardName(const QString &defaultBoardName); QString getIdeName() const; void setIdeName(const QString &ideName); signals: void commandLocationChanged(); protected: QString m_name; QString m_commandLocation; bool m_canProgram; QStringList m_extensions; QMap m_boards; QString m_defaultBoardName; QUrl m_referenceUrl; QString m_ideName; QUrl m_downloadUrl; QString m_minVersion; private: void initSyntaxer(); void initCommandLocation(); private: QPointer m_syntaxer; }; #endif // PLATFORM_H fritzing-app-0.9.3b/src/program/platformarduino.cpp000066400000000000000000000101421272363601300224500ustar00rootroot00000000000000#include "platformarduino.h" #include "programtab.h" #include #include #include PlatformArduino::PlatformArduino() : Platform(QString("Arduino")) { setReferenceUrl(QUrl(QString("http://arduino.cc/en/Reference/"))); setIdeName("Arduino IDE"); setDownloadUrl(QUrl("http://arduino.cc/en/Main/Software")); setMinVersion("1.5.2"); setCanProgram(true); setExtensions(QStringList() << ".ino" << ".pde"); QMap boards; // https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/boards.txt boards.insert("Arduino UNO", "arduino:avr:uno"); boards.insert("Arduino Yún", "arduino:avr:yun"); boards.insert("Arduino Mega/2560", "arduino:avr:mega"); boards.insert("Arduino Duemilanove/Diecemila", "arduino:avr:diecimila"); boards.insert("Arduino Nano", "arduino:avr:nano"); boards.insert("Arduino Mega ADK", "arduino:avr:megaADK"); boards.insert("Arduino Leonardo", "arduino:avr:leonardo"); boards.insert("Arduino Micro", "arduino:avr:micro"); boards.insert("Arduino Esplora", "arduino:avr:Esplora"); boards.insert("Arduino Mini", "arduino:avr:mini"); boards.insert("Arduino Ethernet", "arduino:avr:ethernet"); boards.insert("Arduino Fio", "arduino:avr:fio"); boards.insert("Arduino BT", "arduino:avr:bt"); boards.insert("Lilypad Arduino USB", "arduino:avr:LilyPadUSB"); boards.insert("LilyPad Arduino ", "arduino:avr:lilypad"); boards.insert("Arduino Pro/Pro Mini", "arduino:avr:pro"); boards.insert("Arduino NG or older", "arduino:avr:atmegang"); boards.insert("Arduino Robot Control", "arduino:avr:robotControl"); boards.insert("Arduino Robot Motor", "arduino:avr:robotMotor"); boards.insert("Arduino Gemma", "arduino:avr:gemma"); // https://github.com/arduino/Arduino/blob/master/hardware/arduino/sam/boards.txt boards.insert("Arduino Due (Programming Port)", "arduino:sam:arduino_due_x_dbg"); boards.insert("Arduino Due (Native USB Port)", "arduino:sam:arduino_due_x"); // Note: Edison, Galileo, 101 are programmed via Arduino IDE Add-ons, // not sure if the command line is supported for them setBoards(boards); setDefaultBoardName("Arduino UNO"); } void PlatformArduino::upload(QWidget *source, const QString &port, const QString &board, const QString &fileLocation) { QProcess * process = new QProcess(this); process->setProcessChannelMode(QProcess::MergedChannels); process->setReadChannel(QProcess::StandardOutput); connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), source, SLOT(programProcessFinished(int, QProcess::ExitStatus))); connect(process, SIGNAL(readyReadStandardOutput()), source, SLOT(programProcessReadyRead())); // Make sure .ino is in its own folder with same name (as required by Arduino compiler), // otherwise create a subfolder and copy the file there. QFileInfo fileInfo(fileLocation); QString tmpFilePath = fileInfo.absoluteFilePath(); QString dirName = fileInfo.dir().dirName(); QString sketchName = fileInfo.baseName(); if (dirName.compare(sketchName, Qt::CaseInsensitive) != 0) { QString tmpSketchName(sketchName.append("_TMP")); fileInfo.dir().mkdir(tmpSketchName); tmpFilePath = fileInfo.absolutePath().append("/").append(tmpSketchName).append("/") .append(fileInfo.baseName().append("_TMP.").append(fileInfo.suffix())); if (QFile::exists(tmpFilePath)) QFile::remove(tmpFilePath); QFile::copy(fileInfo.absoluteFilePath(), tmpFilePath); } QStringList args; // see https://github.com/arduino/Arduino/blob/ide-1.5.x/build/shared/manpage.adoc //args.append(QString("--verbose")); args.append(QString("--board")); args.append(getBoards().value(board)); args.append(QString("--port")); args.append(port); args.append(QString("--upload")); args.append(QDir::toNativeSeparators(tmpFilePath)); ProgramTab *tab = qobject_cast(source); if (tab) tab->appendToConsole(tr("Running %1 %2").arg(getCommandLocation()).arg(args.join(" "))); process->start(getCommandLocation(), args); } fritzing-app-0.9.3b/src/program/platformarduino.h000066400000000000000000000004641272363601300221230ustar00rootroot00000000000000#include "platform.h" #include #ifndef PLATFORMARDUINO_H #define PLATFORMARDUINO_H class PlatformArduino : public Platform { public: PlatformArduino(); void upload(QWidget *source, const QString &port, const QString &board, const QString &fileLocation); }; #endif // PLATFORMARDUINO_H fritzing-app-0.9.3b/src/program/platformlaunchpad.cpp000066400000000000000000000012371272363601300227530ustar00rootroot00000000000000#include "platformlaunchpad.h" PlatformLaunchpad::PlatformLaunchpad() : Platform(QString("Launchpad")) { setReferenceUrl(QUrl("http://energia.nu/reference/")); setDownloadUrl(QUrl("http://www.ti.com/tool/msp430-flasher")); // http://energia.nu/download/ doesn't seem to have command line support setMinVersion("1.0"); setCanProgram(true); setExtensions(QStringList() << ".txt"); } void PlatformLaunchpad::upload(const QString &port, const QString &board, const QString &fileLocation, QTextEdit *console) { // see http://www.ti.com/tool/msp430-flasher // http://processors.wiki.ti.com/index.php/MSP430_Flasher_-_Command_Line_Programmer } fritzing-app-0.9.3b/src/program/platformlaunchpad.h000066400000000000000000000004551272363601300224210ustar00rootroot00000000000000#include "platform.h" #ifndef PLATFORMLAUNCHPAD_H #define PLATFORMLAUNCHPAD_H class PlatformLaunchpad : public Platform { public: PlatformLaunchpad(); void upload(const QString &port, const QString &board, const QString &fileLocation, QTextEdit *console); }; #endif // PLATFORMLAUNCHPAD_H fritzing-app-0.9.3b/src/program/platformpicaxe.cpp000066400000000000000000000050631272363601300222660ustar00rootroot00000000000000#include "platformpicaxe.h" #include #include #include #include "programtab.h" PlatformPicaxe::PlatformPicaxe() : Platform(QString("PICAXE")) { setReferenceUrl(QUrl("http://www.picaxe.com/BASIC-Commands")); setCanProgram(true); setIdeName("PICAXE Compilers"); setDownloadUrl(QUrl("http://www.picaxe.com/Software/Drivers/PICAXE-Compilers/")); setMinVersion("2.0"); setExtensions(QStringList() << ".bas" << ".BAS"); QMap boards; boards.insert("PICAXE-08", "picaxe08"); boards.insert("PICAXE-08M", "picaxe08m"); boards.insert("PICAXE-08M2", "picaxe08m2"); boards.insert("PICAXE-08M2LE", "picaxe08m2le"); boards.insert("PICAXE-14M", "picaxe14m"); boards.insert("PICAXE-14M2", "picaxe14m2"); boards.insert("PICAXE-18", "picaxe18"); boards.insert("PICAXE-18A", "picaxe18a"); boards.insert("PICAXE-18M", "picaxe18m"); boards.insert("PICAXE-18M2", "picaxe18m2"); boards.insert("PICAXE-18X", "picaxe18x"); boards.insert("PICAXE-20", "picaxe20"); boards.insert("PICAXE-20M2", "picaxe20m2"); boards.insert("PICAXE-20X2", "picaxe20x2"); boards.insert("PICAXE-28", "picaxe28"); boards.insert("PICAXE-28A", "picaxe28a"); boards.insert("PICAXE-28X", "picaxe28x"); boards.insert("PICAXE-28X1", "picaxe28x1"); boards.insert("PICAXE-28X2", "picaxe28x2"); boards.insert("PICAXE-40X", "picaxe28x"); boards.insert("PICAXE-40X1", "picaxe28x1"); boards.insert("PICAXE-40X2", "picaxe28x2"); setBoards(boards); setDefaultBoardName("PICAXE-08M"); } void PlatformPicaxe::upload(QWidget *source, const QString &port, const QString &board, const QString &fileLocation) { // see http://www.picaxe.com/docs/beta_compiler.pdf QProcess * process = new QProcess(this); process->setProcessChannelMode(QProcess::MergedChannels); process->setReadChannel(QProcess::StandardOutput); connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), source, SLOT(programProcessFinished(int, QProcess::ExitStatus))); connect(process, SIGNAL(readyReadStandardOutput()), source, SLOT(programProcessReadyRead())); QFileInfo cmdFileInfo(getCommandLocation()); QString cmd(cmdFileInfo.absoluteDir().absolutePath().append("/").append(getBoards().value(board))); QStringList args; args.append(QString("-c%1").arg(port)); args.append(fileLocation); ProgramTab *tab = qobject_cast(source); if (tab) tab->appendToConsole(tr("Running %1 %2").arg(cmd).arg(args.join(" "))); process->start(cmd, args); } fritzing-app-0.9.3b/src/program/platformpicaxe.h000066400000000000000000000004571272363601300217350ustar00rootroot00000000000000#include "platform.h" #include #ifndef PLATFORMPICAXE_H #define PLATFORMPICAXE_H class PlatformPicaxe : public Platform { public: PlatformPicaxe(); void upload(QWidget *source, const QString &port, const QString &board, const QString &fileLocation); }; #endif // PLATFORMPICAXE_H fritzing-app-0.9.3b/src/program/programtab.cpp000066400000000000000000000676201272363601300214150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "programtab.h" #include "highlighter.h" #include "syntaxer.h" #include "consolewindow.h" #include "../debugdialog.h" #include "../utils/folderutils.h" #include "../sketchtoolbutton.h" #include #include #include #include #include #include #include #include #include #include static const QChar Quote91Char(0x91); static QString UnableToProgramMessage; ///////////////////////////////////////// SerialPortComboBox::SerialPortComboBox() : QComboBox() { } void SerialPortComboBox::showPopup() { emit aboutToShow(); QComboBox::showPopup(); } ///////////////////////////////////////// DeleteDialog::DeleteDialog(const QString & title, const QString & text, bool deleteFileCheckBox, QWidget *parent, Qt::WindowFlags flags) : QDialog(parent, flags) { // we use this dialog instead of the normal QMessageBox because it's currently hard if not impossible to add a checkbox to QMessageBox. // supposedly this will be fixed in a future release of Qt. this->setWindowTitle(title); this->setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); QVBoxLayout * vlayout = new QVBoxLayout(this); QFrame * frame = new QFrame(this); QHBoxLayout * hlayout = new QHBoxLayout(frame); QLabel * iconLabel = new QLabel; iconLabel->setPixmap(QMessageBox::standardIcon(QMessageBox::Warning)); iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QLabel * label = new QLabel; label->setWordWrap(true); label->setText(text); hlayout->addWidget(iconLabel); hlayout->addWidget(label); vlayout->addWidget(frame); m_checkBox = NULL; if (deleteFileCheckBox) { m_checkBox = new QCheckBox(tr("Also delete the file")); vlayout->addSpacing(7); vlayout->addWidget(m_checkBox); vlayout->addSpacing(15); } m_buttonBox = new QDialogButtonBox; m_buttonBox->addButton(QDialogButtonBox::Yes); m_buttonBox->addButton(QDialogButtonBox::No); m_buttonBox->button(QDialogButtonBox::Yes)->setText(tr("Remove")); m_buttonBox->button(QDialogButtonBox::No)->setText(tr("Don't remove")); QObject::connect(m_buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); vlayout->addWidget(m_buttonBox); this->setModal(true); } void DeleteDialog::buttonClicked(QAbstractButton * button) { this->done(m_buttonBox->standardButton(button)); } bool DeleteDialog::deleteFileChecked() { if (m_checkBox == NULL) return false; return m_checkBox->isChecked(); } ///////////////////////////////////////////// QIcon * AsteriskIcon = NULL; ProgramTab::ProgramTab(QString & filename, QWidget *parent) : QFrame(parent) { if (UnableToProgramMessage.isEmpty()) { UnableToProgramMessage = tr("While it is possible to read and edit %1 programming files, it is not yet possible to use Fritzing to compile or upload these programs to a microcontroller."); } m_tabWidget = NULL; while (parent != NULL) { QTabWidget * tabWidget = qobject_cast(parent); if (tabWidget) { m_tabWidget = tabWidget; break; } parent = parent->parentWidget(); } if (AsteriskIcon == NULL) { AsteriskIcon = new QIcon(":/resources/images/icons/asterisk.png"); } m_canCopy = false; m_canCut = false; m_canUndo = false; m_canRedo = false; m_canPaste = false; //m_platform = NULL; m_port = ""; m_board = ""; m_filename = filename; m_updateEnabled = false; QGridLayout *editLayout = new QGridLayout(this); editLayout->setMargin(0); editLayout->setSpacing(0); while (m_programWindow == NULL) { m_programWindow = qobject_cast(parent); parent = parent->parentWidget(); } // m_textEdit needs to be initialized before createFooter so // some signals get connected properly. m_textEdit = new QTextEdit; m_textEdit->setObjectName("code"); m_textEdit->setFontFamily("Droid Sans Mono"); m_textEdit->setLineWrapMode(QTextEdit::NoWrap); QFontMetrics fm(m_textEdit->currentFont()); m_textEdit->setTabStopWidth(fm.averageCharWidth() * 2); m_textEdit->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); m_textEdit->setUndoRedoEnabled(true); connect(m_textEdit, SIGNAL(textChanged()), this, SLOT(textChanged())); connect(m_textEdit, SIGNAL(undoAvailable(bool)), this, SLOT(enableUndo(bool))); connect(m_textEdit, SIGNAL(redoAvailable(bool)), this, SLOT(enableRedo(bool))); connect(m_textEdit, SIGNAL(copyAvailable(bool)), this, SLOT(enableCopy(bool))); connect(m_textEdit, SIGNAL(copyAvailable(bool)), this, SLOT(enableCut(bool))); // Reuse copy signal for cut m_highlighter = new Highlighter(m_textEdit); QSplitter * splitter = new QSplitter; splitter->setObjectName("splitter"); splitter->setOrientation(Qt::Vertical); editLayout->addWidget(splitter, 1, 0); splitter->addWidget(m_textEdit); m_console = new QPlainTextEdit(); m_console->setObjectName("console"); m_console->setReadOnly(true); QFont font = m_console->document()->defaultFont(); font.setFamily("Droid Sans Mono"); m_console->document()->setDefaultFont(font); splitter->addWidget(m_console); splitter->setStretchFactor(0, 8); splitter->setStretchFactor(1, 2); m_toolbar = new QFrame(this); m_toolbar->setObjectName("sketchAreaToolbar"); m_toolbar->setFixedHeight(66); QFrame *leftButtons = new QFrame(m_toolbar); m_leftButtonsContainer = new QHBoxLayout(leftButtons); m_leftButtonsContainer->setMargin(0); m_leftButtonsContainer->setSpacing(0); QFrame *middleButtons = new QFrame(m_toolbar); middleButtons->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::MinimumExpanding); m_middleButtonsContainer = new QHBoxLayout(middleButtons); m_middleButtonsContainer->setSpacing(0); m_middleButtonsContainer->setMargin(0); QFrame *rightButtons = new QFrame(m_toolbar); m_rightButtonsContainer = new QHBoxLayout(rightButtons); m_rightButtonsContainer->setMargin(0); m_rightButtonsContainer->setSpacing(0); QHBoxLayout *toolbarLayout = new QHBoxLayout(m_toolbar); toolbarLayout->setMargin(0); toolbarLayout->setSpacing(0); toolbarLayout->addWidget(leftButtons); toolbarLayout->addWidget(middleButtons); toolbarLayout->addWidget(rightButtons); editLayout->addWidget(m_toolbar,2,0); //createToolBarMenu(); } ProgramTab::~ProgramTab() { } /** * We override the showEvent for the tab to trigger a menu update */ void ProgramTab::showEvent(QShowEvent *event) { QFrame::showEvent(event); m_textEdit->setFocus(); updateMenu(); } void ProgramTab::initMenus() { m_newButton = new SketchToolButton("NewCode", this, m_programWindow->m_newAction); m_newButton->setText(tr("New")); m_newButton->setObjectName("newCodeButton"); m_newButton->setEnabledIcon(); // seems to need this to display button icon first time m_leftButtonsContainer->addWidget(m_newButton); m_openButton = new SketchToolButton("OpenCode", this, m_programWindow->m_openAction); m_openButton->setText(tr("Open")); m_openButton->setObjectName("openCodeButton"); m_openButton->setEnabledIcon(); // seems to need this to display button icon first time m_leftButtonsContainer->addWidget(m_openButton); m_saveButton = new SketchToolButton("SaveCode", this, m_programWindow->m_saveAction); m_saveButton->setText(tr("Save")); m_saveButton->setObjectName("saveCodeButton"); m_saveButton->setEnabledIcon(); // seems to need this to display button icon first time m_leftButtonsContainer->addWidget(m_saveButton); // Platform selection QFrame *platformSelector = new QFrame(m_middleButtonsContainer->parentWidget()); platformSelector->setObjectName("toolbarSelector"); QVBoxLayout *platformSelectionContainer = new QVBoxLayout(platformSelector); platformSelectionContainer->setSpacing(0); platformSelectionContainer->setMargin(0); QLabel * platformLabel = new QLabel(tr("Platform"), this); m_platformComboBox = new QComboBox(); m_platformComboBox->setObjectName("toolBarComboBox"); m_platformComboBox->setEditable(false); m_platformComboBox->setEnabled(true); foreach (Platform * platform, m_programWindow->getAvailablePlatforms()) { m_platformComboBox->addItem(platform->getName()); } QSettings settings; QString currentPlatform = settings.value("programwindow/platform", "").toString(); if (currentPlatform.isEmpty()) { currentPlatform = m_platformComboBox->currentText(); // TODO } platformSelectionContainer->addWidget(m_platformComboBox); platformSelectionContainer->addWidget(platformLabel); m_rightButtonsContainer->addWidget(platformSelector); connect(this, SIGNAL(platformChanged(Platform *)), m_programWindow, SLOT(updateBoards())); connect(this, SIGNAL(platformChanged(Platform *)), this, SLOT(updateBoards())); setPlatform(currentPlatform, false); // Board selection QFrame *boardSelector = new QFrame(m_middleButtonsContainer->parentWidget()); boardSelector->setObjectName("toolbarSelector"); QVBoxLayout *boardSelectionContainer = new QVBoxLayout(boardSelector); boardSelectionContainer->setSpacing(0); boardSelectionContainer->setMargin(0); QLabel * boardLabel = new QLabel(tr("Board"), this); m_boardComboBox = new QComboBox(); m_boardComboBox->setObjectName("toolBarComboBox"); m_boardComboBox->setEditable(false); m_boardComboBox->setEnabled(true); updateBoards(); QString currentBoard = settings.value("programwindow/board", "").toString(); if (currentBoard.isEmpty()) { currentBoard = m_boardComboBox->currentText(); } else if (!m_programWindow->getBoards().contains(currentBoard)) { currentBoard = m_boardComboBox->currentText(); } setBoard(currentBoard); boardSelectionContainer->addWidget(m_boardComboBox); boardSelectionContainer->addWidget(boardLabel); m_rightButtonsContainer->addWidget(boardSelector); // Port selection QFrame *portSelector = new QFrame(m_middleButtonsContainer->parentWidget()); portSelector->setObjectName("toolbarSelector"); QVBoxLayout *portSelectionContainer = new QVBoxLayout(portSelector); portSelectionContainer->setSpacing(0); portSelectionContainer->setMargin(0); QLabel * portLabel = new QLabel(tr("Port"), this); m_portComboBox = new SerialPortComboBox(); m_portComboBox->setObjectName("toolBarComboBox"); m_portComboBox->setEditable(false); m_portComboBox->setEnabled(true); QList ports = m_programWindow->getSerialPorts(); foreach (const QSerialPortInfo port, ports) m_portComboBox->addItem(port.portName(), port.systemLocation()); QString currentPort = settings.value("programwindow/port", "").toString(); if (currentPort.isEmpty()) { currentPort = m_portComboBox->currentText(); } else if (!m_programWindow->hasPort(currentPort)) { currentPort = m_portComboBox->currentText(); } setPort(currentPort); portSelectionContainer->addWidget(m_portComboBox); portSelectionContainer->addWidget(portLabel); m_rightButtonsContainer->addWidget(portSelector); // connect last so these signals aren't triggered during initialization connect(m_platformComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(setPlatform(const QString &))); connect(m_portComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(setPort(const QString &))); connect(m_portComboBox, SIGNAL(aboutToShow()), this, SLOT(updateSerialPorts()), Qt::DirectConnection); connect(m_boardComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(setBoard(const QString &))); m_monitorButton = new SketchToolButton("MonitorCode", this, m_programWindow->m_monitorAction); m_monitorButton->setText(tr("Serial Monitor")); m_monitorButton->setObjectName("monitorCodeButton"); m_monitorButton->setEnabledIcon(); // seems to need this to display button icon first time m_rightButtonsContainer->addWidget(m_monitorButton); m_programButton = new SketchToolButton("ProgramCode", this, m_programWindow->m_programAction); m_programButton->setText(tr("Upload")); m_programButton->setObjectName("programCodeButton"); m_programButton->setEnabledIcon(); // seems to need this to display button icon first time m_rightButtonsContainer->addWidget(m_programButton); // QFrame * superFrame = new QFrame(); // QVBoxLayout * superLayout = new QVBoxLayout(); // superLayout->setMargin(0); // superLayout->setSpacing(0); // m_unableToProgramLabel = new QLabel(UnableToProgramMessage.arg("")); // m_unableToProgramLabel->setObjectName("unableToProgramLabel"); // superLayout->addWidget(footerFrame); // superLayout->addWidget(m_unableToProgramLabel); // superFrame->setLayout(superLayout); setPlatform(currentPlatform, false); } void ProgramTab::setPlatform(const QString & newPlatformName) { setPlatform(m_programWindow->getPlatformByName(newPlatformName), true); } void ProgramTab::setPlatform(const QString & newPlatformName, bool updateLink) { setPlatform(m_programWindow->getPlatformByName(newPlatformName), updateLink); } void ProgramTab::setPlatform(Platform * newPlatform) { setPlatform(newPlatform, true); } void ProgramTab::setPlatform(Platform * newPlatform, bool updateLink) { DebugDialog::debug(QString("Setting platform to %1").arg(newPlatform->getName())); bool isPlatformChanged = newPlatform != m_platform; if (updateLink && isPlatformChanged) { m_programWindow->updateLink(m_filename, newPlatform, false, false); } if (m_platform != NULL) m_platform->disconnect(SIGNAL(commandLocationChanged())); connect(newPlatform, SIGNAL(commandLocationChanged()), this, SLOT(enableProgramButton())); m_platform = newPlatform; m_platformComboBox->setCurrentIndex(m_platformComboBox->findText(newPlatform->getName())); Syntaxer * syntaxer = m_platform->getSyntaxer(); m_highlighter->setSyntaxer(syntaxer); m_highlighter->rehighlight(); updateMenu(); QSettings settings; settings.setValue("programwindow/platform", newPlatform->getName()); //bool canProgram = (syntaxer != NULL && syntaxer->canProgram()); //m_unableToProgramLabel->setVisible(!canProgram); //m_unableToProgramLabel->setText(UnableToProgramMessage.arg(newPlatform.getName())); if (updateLink && isPlatformChanged) emit platformChanged(newPlatform); } void ProgramTab::setPort(const QString & newPort) { DebugDialog::debug(QString("Setting port to %1").arg(newPort)); int ix = m_portComboBox->findText(newPort); if (ix >= 0) { m_port = newPort; m_portComboBox->setCurrentIndex(ix); m_portComboBox->setToolTip(newPort); updateMenu(); QSettings settings; settings.setValue("programwindow/port", newPort); } } void ProgramTab::setBoard(const QString & newBoard) { DebugDialog::debug(QString("Setting board to %1").arg(newBoard)); int ix = m_boardComboBox->findText(newBoard); if (ix >= 0) { m_board = newBoard; m_boardComboBox->setCurrentIndex(ix); m_boardComboBox->setToolTip(newBoard); updateMenu(); QSettings settings; settings.setValue("programwindow/board", newBoard); } } bool ProgramTab::loadProgramFile() { if (isModified() || !m_textEdit->document()->isEmpty()) { m_programWindow->loadProgramFileNew(); return false; } QString fileName = FolderUtils::getOpenFileName( this, tr("Select a program file to load"), FolderUtils::openSaveFolder(), m_highlighter->syntaxer()->extensionString() ); if (fileName.isEmpty()) return false; return loadProgramFile(fileName, "", true); } bool ProgramTab::loadProgramFile(const QString & fileName, const QString & altFileName, bool updateLink) { DebugDialog::debug("program tab load program file"); if (m_programWindow->alreadyHasProgram(fileName)) { return false; } DebugDialog::debug("checking file"); QFile file(fileName); if (!file.open(QFile::ReadOnly)) { m_programWindow->updateLink(fileName, m_platform, false, true); if (!altFileName.isEmpty()) { file.setFileName(altFileName); if (!file.open(QFile::ReadOnly)) { QFileInfo fileInfo(fileName); QString fn = FolderUtils::getOpenFileName( NULL, tr("Fritzing is unable to find '%1', please locate it").arg(fileInfo.fileName()), FolderUtils::openSaveFolder(), tr("Code (*.%1)").arg(fileInfo.suffix()) ); if (fn.isEmpty()) return false; file.setFileName(fn); if (!file.open(QFile::ReadOnly)) { return false; } } } } DebugDialog::debug("about to read"); m_filename = file.fileName(); QString text = file.readAll(); // clean out 0x91, mostly due to picaxe files for (int i = 0; i < text.count(); i++) { if (text[i] == Quote91Char) { text[i] = '\''; } } DebugDialog::debug("about to set text"); m_textEdit->setText(text); setClean(); QFileInfo fileInfo(m_filename); m_tabWidget->setTabText(m_tabWidget->currentIndex(), fileInfo.fileName()); m_tabWidget->setTabToolTip(m_tabWidget->currentIndex(), m_filename); DebugDialog::debug("about to update link"); if (updateLink) { m_programWindow->updateLink(m_filename, m_platform, true, true); } return true; } void ProgramTab::textChanged() { QIcon tabIcon = m_tabWidget->tabIcon(m_tabWidget->currentIndex()); bool modified = isModified(); if (m_saveButton) { m_saveButton->setEnabled(modified); } updateMenu(); // calls enableProgramButton if (tabIcon.isNull()) { if (modified) { m_tabWidget->setTabIcon(m_tabWidget->currentIndex(), *AsteriskIcon); } } else { if (!modified) { m_tabWidget->setTabIcon(m_tabWidget->currentIndex(), QIcon()); } } } void ProgramTab::undo() { m_textEdit->undo(); } void ProgramTab::enableUndo(bool enable) { m_canUndo = enable; m_saveButton->setEnabled(m_canUndo); updateMenu(); } void ProgramTab::redo() { m_textEdit->redo(); } void ProgramTab::enableRedo(bool enable) { m_canRedo = enable; updateMenu(); } void ProgramTab::cut() { m_textEdit->cut(); } void ProgramTab::enableCut(bool enable) { m_canCut = enable; updateMenu(); } void ProgramTab::copy() { m_textEdit->copy(); } void ProgramTab::enableCopy(bool enable) { m_canCopy = enable; updateMenu(); } void ProgramTab::paste() { m_textEdit->paste(); } void ProgramTab::enablePaste(bool enable) { m_canPaste = enable; updateMenu(); } void ProgramTab::selectAll() { m_textEdit->selectAll(); } void ProgramTab::deleteTab() { bool deleteFile = false; if (!m_textEdit->document()->isEmpty()) { QString name = QFileInfo(m_filename).fileName(); if (name.isEmpty()) { name = m_tabWidget->tabText(m_tabWidget->currentIndex()); } DeleteDialog deleteDialog(tr("Remove \"%1\"?").arg(name), tr("Are you sure you want to remove \"%1\" from the sketch?").arg(name), !FolderUtils::isEmptyFileName(m_filename, "Untitled"), NULL, 0); int reply = deleteDialog.exec(); if (reply != QMessageBox::Yes) { return; } deleteFile = deleteDialog.deleteFileChecked(); } if (m_tabWidget) { emit wantToDelete(m_tabWidget->currentIndex(), deleteFile); this->deleteLater(); } } bool ProgramTab::isModified() { return m_textEdit->document()->isModified(); } const QString & ProgramTab::filename() { return m_filename; } void ProgramTab::setFilename(const QString & name) { m_filename = name; updateMenu(); // Here we update the widget's tab label. We need // to check that it both has the PTabWidget as its // parent and it's already been added to the PTabWidget int currentIndex = m_tabWidget->indexOf(this); if (currentIndex >= 0) { m_tabWidget->setTabText(currentIndex, name.section("/",-1)); } else { DebugDialog::debug("Negative index"); } } const QStringList & ProgramTab::extensions() { if (m_highlighter == NULL) return ___emptyStringList___; Syntaxer * syntaxer = m_highlighter->syntaxer(); if (syntaxer == NULL) return ___emptyStringList___; return syntaxer->extensions(); } const QString & ProgramTab::extensionString() { if (m_highlighter == NULL) return ___emptyString___; Syntaxer * syntaxer = m_highlighter->syntaxer(); if (syntaxer == NULL) return ___emptyString___; return syntaxer->extensionString(); } void ProgramTab::setClean() { m_textEdit->document()->setModified(false); textChanged(); } void ProgramTab::setDirty() { m_textEdit->document()->setModified(true); textChanged(); } void ProgramTab::save() { emit wantToSave(m_tabWidget->currentIndex()); } void ProgramTab::saveAs() { emit wantToSaveAs(m_tabWidget->currentIndex()); } void ProgramTab::rename() { emit wantToRename(m_tabWidget->currentIndex()); } void ProgramTab::print(QPrinter &printer) { m_textEdit->print(&printer); } void ProgramTab::setText(QString text) { m_textEdit->setPlainText(text); } QString ProgramTab::text() { return m_textEdit->toPlainText(); } bool ProgramTab::readOnly() { // TODO: return true if it's a sample file return false; } bool ProgramTab::save(const QString & filename) { QFile file(filename); if (!file.open(QFile::WriteOnly)) { return false; } QByteArray b = m_textEdit->toPlainText().toLatin1(); qint64 written = file.write(b); file.close(); bool result = (b.length() == written); if (result) { setFilename(filename); } return result; } void ProgramTab::serialMonitor() { if (m_monitorWindow == NULL) { m_monitorWindow = new ConsoleWindow(this); } m_monitorWindow->show(); m_monitorWindow->activateWindow(); m_monitorWindow->raise(); m_monitorWindow->openSerialPort(m_port); } void ProgramTab::sendProgram() { const QString commandLoc = m_platform->getCommandLocation(); if (commandLoc.isEmpty()) { m_console->setPlainText(tr("No uploader for %1 specified. Go to Preferences > Code View to configure it.").arg(m_platform->getName())); return; } if (! QFile::exists(commandLoc)) { m_console->setPlainText(tr("Uploader configured, but not found at %1").arg(commandLoc)); return; } if (isModified()) { //QMessageBox::information(this, QObject::tr("Fritzing"), tr("The file '%1' must be saved before it can be sent to the programmer.").arg(m_filename)); //return; save(); } if (m_monitorWindow != NULL) { m_monitorWindow->closeSerialPort(m_portComboBox->currentText()); } m_programButton->setEnabled(false); m_console->setPlainText(""); m_platform->upload(this, m_portComboBox->currentData().toString(), m_boardComboBox->currentText(), m_filename); } void ProgramTab::programProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { DebugDialog::debug(QString("program process finished %1 %2").arg(exitCode).arg(exitStatus)); m_programButton->setEnabled(true); sender()->deleteLater(); if (exitCode == 0) { m_console->appendPlainText(tr("Upload finished.")); } else { m_console->appendPlainText(tr("Upload failed with exit code %1, %2").arg(exitCode).arg(exitStatus)); } } void ProgramTab::programProcessReadyRead() { QByteArray byteArray = qobject_cast(sender())->readAllStandardOutput(); m_console->appendPlainText(byteArray); } /** * This function emits all the required signals to update the menu of its owning ProgramWindow. * It is currently called on the QTabWidget's currentChanged() signal. */ void ProgramTab::updateMenu() { // DebugDialog::debug(QString("Updating the menu for tab %1").arg(m_tabWidget->currentIndex())); // DebugDialog::debug(QString("Undo: %1").arg(m_canUndo)); // DebugDialog::debug(QString("Redo: %1").arg(m_canRedo)); // DebugDialog::debug(QString("Cut: %1").arg(m_canCut)); // DebugDialog::debug(QString("Copy: %1").arg(m_canCopy)); // DebugDialog::debug(QString("Paste: %1").arg(m_canPaste)); // DebugDialog::debug(QString("Platform: %1").arg(m_platform.getName())); // DebugDialog::debug(QString("Port: %1").arg(m_port)); enableProgramButton(); // Emit a signal so that the ProgramWindow can update its own UI. emit programWindowUpdateRequest(m_programButton ? m_programButton->isEnabled() : false, m_canUndo, m_canRedo, m_canCut, m_canCopy, m_canPaste, m_platform, m_port, m_board, m_filename); } void ProgramTab::updateBoards() { QString currentBoard; int index = m_boardComboBox->currentIndex(); if (index >= 0) { currentBoard = m_boardComboBox->itemText(index); } while (m_boardComboBox->count() > 0) { m_boardComboBox->removeItem(0); } QMap boardNames = m_programWindow->getBoards(); foreach (QString name, boardNames.keys()) { m_boardComboBox->addItem(name, boardNames.value(name)); } int foundIt = m_boardComboBox->findText(currentBoard); if (foundIt >= 0) m_boardComboBox->setCurrentIndex(foundIt); else setBoard(m_platform->getDefaultBoardName()); } void ProgramTab::updateSerialPorts() { QList ports = m_programWindow->getSerialPorts(); QList newPorts; foreach (QSerialPortInfo port, ports) { if (m_portComboBox->findText(port.portName()) < 0) { newPorts.append(port); } } QList obsoletePorts; for (int i = 0; i < m_portComboBox->count(); i++) { QString portName = m_portComboBox->itemText(i); if (!m_programWindow->hasPort(portName)) { obsoletePorts.append(i); } if (i == m_portComboBox->currentIndex()) { // TODO: what? } } for (int i = obsoletePorts.count() - 1; i >= 0; i--) { m_portComboBox->removeItem(obsoletePorts.at(i)); } foreach (QSerialPortInfo port, newPorts) { m_portComboBox->addItem(port.portName(), port.systemLocation()); } enableProgramButton(); enableMonitorButton(); } Platform * ProgramTab::platform() { return m_platform; } void ProgramTab::enableMonitorButton() { if (m_monitorButton == NULL) return; bool enabled = true; if (m_portComboBox->count() == 0) { enabled = false; } m_monitorButton->setEnabled(enabled); } void ProgramTab::enableProgramButton() { if (m_programButton == NULL) return; bool enabled = true; // always enable, to show helpful error message if no programmer is set up /* if (m_platform->getCommandLocation().isEmpty()) { enabled = false; } if (enabled && m_portComboBox->count() == 0) { enabled = false; } if (enabled && m_textEdit->document()->isEmpty()) { enabled = false; } */ m_programButton->setEnabled(enabled); } void ProgramTab::appendToConsole(const QString & text) { m_console->appendPlainText(text); } fritzing-app-0.9.3b/src/program/programtab.h000066400000000000000000000130171272363601300210510ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #ifndef PROGRAMTAB_H_ #define PROGRAMTAB_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "programwindow.h" #include "consolewindow.h" #include "../sketchtoolbutton.h" class SerialPortComboBox : public QComboBox { Q_OBJECT public: SerialPortComboBox(); protected: void showPopup(); signals: void aboutToShow(); }; class ProgramTab : public QFrame { Q_OBJECT public: ProgramTab(QString & filename, QWidget * parent); ~ProgramTab(); void showEvent(QShowEvent *event); bool isModified(); const QString & filename(); void setFilename(const QString &); const QStringList & extensions(); const QString & extensionString(); bool readOnly(); void setClean(); void setDirty(); bool save(const QString & filename); bool loadProgramFile(const QString & filename, const QString & altFilename, bool noUpdate); void print(QPrinter & printer); void setText(QString text); QString text(); Platform *platform(); void setPlatform(const QString & newPlatformName, bool updateLink); void setPlatform(Platform * newPlatform, bool updateLink); void appendToConsole(const QString &); void initMenus(); public slots: void setPlatform(const QString & newPlatformName); void setPlatform(Platform * newPlatform); void setPort(const QString &); void setBoard(const QString &); bool loadProgramFile(); void textChanged(); void undo(); void enableUndo(bool enable); void redo(); void enableRedo(bool enable); void cut(); void enableCut(bool enable); void copy(); void enableCopy(bool enable); void paste(); void enablePaste(bool enable); void selectAll(); void deleteTab(); void save(); void saveAs(); void rename(); void serialMonitor(); void sendProgram(); void programProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void programProcessReadyRead(); void updateMenu(); void updateSerialPorts(); void updateBoards(); signals: // TODO: since ProgramTab has m_programWindow most/all of these signals could be replaced by direct // calls to ProgramWindow public functions void wantToSave(int); void wantToSaveAs(int); void wantToRename(int); void wantToDelete(int, bool deleteFile); void platformChanged(Platform * newPlatform); void programWindowUpdateRequest(bool programEnable, bool undoEnable, bool redoEnable, bool cutEnable, bool copyEnable, bool pasteEnable, Platform * platform, const QString & port, const QString & board, const QString & filename); protected: QFrame * createFooter(); protected slots: void enableProgramButton(); void enableMonitorButton(); protected: QPointer m_newButton; QPointer m_openButton; QPointer m_saveButton; QPointer m_cancelCloseButton; QPointer m_monitorButton; QPointer m_programButton; QPointer m_portComboBox; QPointer m_platformComboBox; QPointer m_boardComboBox; QPointer m_textEdit; QPointer m_console; QPointer m_tabWidget; QPointer m_unableToProgramLabel; QPointer m_toolbar; QHBoxLayout *m_leftButtonsContainer; QHBoxLayout *m_middleButtonsContainer; QHBoxLayout *m_rightButtonsContainer; bool m_updateEnabled; QPointer m_monitorWindow; QPointer m_programWindow; // Store the status of selected text, undo, and redo actions // so this tab can emit the proper status of these actions // on activation bool m_canUndo; bool m_canRedo; bool m_canCopy; bool m_canCut; bool m_canPaste; QPointer m_platform; QString m_port; QString m_board; QPointer m_highlighter; QString m_filename; }; class DeleteDialog : public QDialog { Q_OBJECT public: DeleteDialog(const QString & title, const QString & text, bool deleteFileCheckBox, QWidget * parent = 0, Qt::WindowFlags f = 0); bool deleteFileChecked(); protected slots: void buttonClicked(QAbstractButton * button); protected: QDialogButtonBox * m_buttonBox; QCheckBox * m_checkBox; }; #endif /* PROGRAMTAB_H_ */ fritzing-app-0.9.3b/src/program/programwindow.cpp000066400000000000000000000704231272363601300221510ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ ///////////////////////////////////////////// // // TODO // // integrate dirty // remove old program window // enable all buttons, and give error messages (i.e. where is IDE) #include "programwindow.h" #include "highlighter.h" #include "syntaxer.h" #include "programtab.h" #include "platformarduino.h" #include "platformpicaxe.h" #include "../debugdialog.h" #include "../waitpushundostack.h" #include "../utils/folderutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////// PTabWidget::PTabWidget(QWidget * parent) : QTabWidget(parent) { m_lastTabIndex = -1; connect(tabBar(), SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); } QTabBar * PTabWidget::tabBar() { return QTabWidget::tabBar(); } void PTabWidget::tabChanged(int index) { // Hide the close button on the old tab if (m_lastTabIndex >= 0) { QAbstractButton *tabButton = qobject_cast(tabBar()->tabButton(m_lastTabIndex, QTabBar::LeftSide)); if (!tabButton) { tabButton = qobject_cast(tabBar()->tabButton(m_lastTabIndex, QTabBar::RightSide)); } if (tabButton) { tabButton->hide(); } } m_lastTabIndex = index; // Show the close button on the new tab if (m_lastTabIndex >= 0) { QAbstractButton *tabButton = qobject_cast(tabBar()->tabButton(m_lastTabIndex, QTabBar::LeftSide)); if (!tabButton) { tabButton = qobject_cast(tabBar()->tabButton(m_lastTabIndex, QTabBar::RightSide)); } if (tabButton) { tabButton->show(); } } } /////////////////////////////////////////////// static int UntitledIndex = 1; QList ProgramWindow::m_platforms; QString ProgramWindow::NoBoardName; ProgramWindow::ProgramWindow(QWidget *parent) : FritzingWindow("", untitledFileCount(), "", parent) { QFile styleSheet(":/resources/styles/programwindow.qss"); this->setObjectName("programmingWindow"); if (!styleSheet.open(QIODevice::ReadOnly)) { qWarning("Unable to open :/resources/styles/programwindow.qss"); } else { QString ss = styleSheet.readAll(); #ifdef Q_OS_MAC int paneLoc = 4; int tabBarLoc = 0; #else int paneLoc = -1; int tabBarLoc = 5; #endif ss = ss.arg(paneLoc).arg(tabBarLoc); this->setStyleSheet(ss); } if (m_platforms.count() == 0) { initPlatforms(); } if (NoBoardName.isEmpty()) { NoBoardName = tr("No boards available"); } m_savingProgramTab = NULL; UntitledIndex--; // incremented by FritzingWindow ProgramWindow::setTitle(); // set to something weird by FritzingWindow } ProgramWindow::~ProgramWindow() { } void ProgramWindow::setup() { if (parentWidget() == NULL) { resize(500,700); setAttribute(Qt::WA_DeleteOnClose, true); } QFrame * mainFrame = new QFrame(this); QFrame * headerFrame = createHeader(); QFrame * centerFrame = createCenter(); layout()->setMargin(0); layout()->setSpacing(0); QGridLayout *layout = new QGridLayout(mainFrame); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(headerFrame,0,0); layout->addWidget(centerFrame,1,0); setCentralWidget(mainFrame); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); QSettings settings; if (!settings.value("programwindow/state").isNull()) { restoreState(settings.value("programwindow/state").toByteArray()); } if (!settings.value("programwindow/geometry").isNull()) { restoreGeometry(settings.value("programwindow/geometry").toByteArray()); } installEventFilter(this); } void ProgramWindow::initMenus(QMenuBar * menubar) { QAction *currentAction; m_editMenu = menubar->addMenu(tr("&Edit")); m_undoAction = new QAction(tr("Undo"), this); m_undoAction->setShortcuts(QKeySequence::Undo); m_undoAction->setEnabled(false); connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo())); m_editMenu->addAction(m_undoAction); m_redoAction = new QAction(tr("Redo"), this); m_redoAction->setShortcuts(QKeySequence::Redo); m_redoAction->setEnabled(false); connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); m_editMenu->addAction(m_redoAction); m_editMenu->addSeparator(); m_cutAction = new QAction(tr("&Cut"), this); m_cutAction->setShortcut(QKeySequence::Cut); m_cutAction->setStatusTip(tr("Cut selection")); m_cutAction->setEnabled(false); connect(m_cutAction, SIGNAL(triggered()), this, SLOT(cut())); m_editMenu->addAction(m_cutAction); m_copyAction = new QAction(tr("&Copy"), this); m_copyAction->setShortcut(QKeySequence::Copy); m_copyAction->setStatusTip(tr("Copy selection")); m_copyAction->setEnabled(false); connect(m_copyAction, SIGNAL(triggered()), this, SLOT(copy())); m_editMenu->addAction(m_copyAction); m_pasteAction = new QAction(tr("&Paste"), this); m_pasteAction->setShortcut(QKeySequence::Paste); m_pasteAction->setStatusTip(tr("Paste clipboard contents")); // TODO: Check clipboard status and disable appropriately here connect(m_pasteAction, SIGNAL(triggered()), this, SLOT(paste())); m_editMenu->addAction(m_pasteAction); m_editMenu->addSeparator(); m_selectAction = new QAction(tr("&Select All"), this); m_selectAction->setShortcut(QKeySequence::SelectAll); m_selectAction->setStatusTip(tr("Select all text")); connect(m_selectAction, SIGNAL(triggered()), this, SLOT(selectAll())); m_editMenu->addAction(m_selectAction); m_editMenu->addSeparator(); m_preferencesAction = new QAction(tr("&Preferences..."), this); m_preferencesAction->setStatusTip(tr("Show the application's about box")); connect(m_preferencesAction, SIGNAL(triggered()), QApplication::instance(), SLOT(preferences())); m_editMenu->addAction(m_preferencesAction); m_programMenu = menubar->addMenu(tr("&Code")); m_newAction = new QAction(tr("&New Tab"), this); m_newAction->setShortcut(QKeySequence::AddTab); m_newAction->setStatusTip(tr("Create a new program tab")); connect(m_newAction, SIGNAL(triggered()), this, SLOT(addTab())); m_programMenu->addAction(m_newAction); m_openAction = new QAction(tr("&Import Code..."), this); m_openAction->setShortcut(tr("Alt+Ctrl+I")); m_openAction->setStatusTip(tr("Import a program from a file")); connect(m_openAction, SIGNAL(triggered()), this, SLOT(loadProgramFile())); m_programMenu->addAction(m_openAction); m_saveAction = new QAction(tr("&Save Tab"), this); m_saveAction->setShortcut(tr("Alt+Ctrl+S")); m_saveAction->setStatusTip(tr("Save the current program tab")); connect(m_saveAction, SIGNAL(triggered()), this, SLOT(saveCurrentTab())); m_programMenu->addAction(m_saveAction); currentAction = new QAction(tr("&Rename Tab"), this); currentAction->setShortcut(tr("Alt+Ctrl+R")); currentAction->setStatusTip(tr("Rename the current program tab")); connect(currentAction, SIGNAL(triggered()), this, SLOT(rename())); m_programMenu->addAction(currentAction); currentAction = new QAction(tr("Close Tab"), this); currentAction->setShortcut(tr("Alt+Ctrl+W")); currentAction->setStatusTip(tr("Remove the current program tab from the sketch")); connect(currentAction, SIGNAL(triggered()), this, SLOT(closeCurrentTab())); m_programMenu->addAction(currentAction); m_programMenu->addSeparator(); m_platformMenu = new QMenu(tr("Platform"), this); m_programMenu->addMenu(m_platformMenu); QSettings settings; QString currentPlatform = settings.value("programwindow/platform", "").toString(); QList platforms = getAvailablePlatforms(); m_platformActionGroup = new QActionGroup(this); foreach (Platform * platform, platforms) { currentAction = new QAction(platform->getName(), this); currentAction->setCheckable(true); m_platformActions.insert(platform, currentAction); m_platformActionGroup->addAction(currentAction); m_platformMenu->addAction(currentAction); if (!currentPlatform.isEmpty()) { if (platform->getName().compare(currentPlatform) == 0) { currentAction->setChecked(true); } } } connect(m_platformMenu, SIGNAL(triggered(QAction*)), this, SLOT(setPlatform(QAction*))); m_boardMenu = new QMenu(tr("Board"), this); m_programMenu->addMenu(m_boardMenu); m_boardActionGroup = new QActionGroup(this); updateBoards(); connect(m_boardMenu, SIGNAL(triggered(QAction*)), this, SLOT(setBoard(QAction*))); m_serialPortMenu = new QMenu(tr("Port"), this); m_programMenu->addMenu(m_serialPortMenu); m_serialPortActionGroup = new QActionGroup(this); updateSerialPorts(); connect(m_serialPortMenu, SIGNAL(triggered(QAction*)), this, SLOT(setPort(QAction*))); connect(m_serialPortMenu, SIGNAL(aboutToShow()), this, SLOT(updateSerialPorts()), Qt::DirectConnection); m_programMenu->addSeparator(); m_monitorAction = new QAction(tr("Serial Monitor"), this); m_monitorAction->setShortcut(tr("Ctrl+M")); m_monitorAction->setStatusTip(tr("Monitor the serial port communication")); m_monitorAction->setEnabled(false); connect(m_monitorAction, SIGNAL(triggered()), this, SLOT(serialMonitor())); m_programMenu->addAction(m_monitorAction); m_programAction = new QAction(tr("Upload"), this); m_programAction->setShortcut(tr("Ctrl+U")); m_programAction->setStatusTip(tr("Upload the current program onto a microcontroller")); m_programAction->setEnabled(false); connect(m_programAction, SIGNAL(triggered()), this, SLOT(sendProgram())); m_programMenu->addAction(m_programAction); m_viewMenu = menubar->addMenu(tr("&View")); foreach (QAction * action, m_viewMenuActions) { m_viewMenu->addAction(action); } addTab(); // the initial ProgramTab must be created after all actions are set up } void ProgramWindow::showMenus(bool show) { if (m_editMenu) { m_editMenu->menuAction()->setVisible(show); m_editMenu->setEnabled(show); m_undoAction->setEnabled(show); m_redoAction->setEnabled(show); m_cutAction->setEnabled(show); m_copyAction->setEnabled(show); m_pasteAction->setEnabled(show); m_selectAction->setEnabled(show); } if (m_programMenu) { m_programMenu->menuAction()->setVisible(show); m_programMenu->setEnabled(show); } if (m_viewMenu) { m_viewMenu->menuAction()->setVisible(show); m_viewMenu->setEnabled(show); } } void ProgramWindow::createViewMenuActions(QList & actions) { m_viewMenuActions = actions; } void ProgramWindow::linkFiles(const QList & linkedFiles, const QString & alternativePath) { if (linkedFiles.isEmpty()) return; bool firstTime = true; foreach (LinkedFile * linkedFile, linkedFiles) { ProgramTab * programTab = NULL; if (firstTime) { firstTime = false; programTab = indexWidget(0); } else { programTab = addTab(); } QDir dir(alternativePath); QFileInfo fileInfo(linkedFile->linkedFilename); programTab->loadProgramFile(linkedFile->linkedFilename, dir.absoluteFilePath(fileInfo.fileName()), false); if ((linkedFile->fileFlags & LinkedFile::InBundleFlag) && ((linkedFile->fileFlags & LinkedFile::ReadOnlyFlag) == 0)) { if (linkedFile->fileFlags & LinkedFile::SameMachineFlag) { programTab->appendToConsole(tr("File '%1' was restored from the .fzz file; the local copy was not found.").arg(fileInfo.fileName())); } else { programTab->appendToConsole(tr("File '%1' was restored from the .fzz file; save a local copy to work with an external editor.").arg(fileInfo.fileName())); } } if (hasPlatform(linkedFile->platform)) { programTab->setPlatform(linkedFile->platform, false); } else { linkedFile->platform.clear(); } } } QFrame * ProgramWindow::createHeader() { QFrame * headerFrame = new QFrame(); headerFrame->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed)); headerFrame->setObjectName("header"); return headerFrame; } QFrame * ProgramWindow::createCenter() { QFrame * centerFrame = new QFrame(this); centerFrame->setObjectName("center"); m_tabWidget = new PTabWidget(centerFrame); m_tabWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); m_tabWidget->setMovable(true); m_tabWidget->setTabsClosable(true); m_tabWidget->setUsesScrollButtons(false); m_tabWidget->setElideMode(Qt::ElideLeft); connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); //addTab(); QGridLayout *tabLayout = new QGridLayout(m_tabWidget); tabLayout->setMargin(0); tabLayout->setSpacing(0); QGridLayout *mainLayout = new QGridLayout(centerFrame); mainLayout->setMargin(0); mainLayout->setSpacing(0); mainLayout->addWidget(m_tabWidget,0,0,1,1); return centerFrame; } void ProgramWindow::cleanUp() { } /** * eventFilter here is used to catch the keyboard shortcuts that trigger the close event * for ProgramWindow. If there are more than one tab widget the standard close shortcut * should only close the current tab instead of closing the whole window. Every other * case is ignored and handled by closeEvent() like normal. */ bool ProgramWindow::eventFilter(QObject * object, QEvent * event) { if (object == this && event->type() == QEvent::ShortcutOverride) { QKeyEvent *keyEvent = dynamic_cast(event); if(keyEvent && keyEvent->matches(QKeySequence::Close) && m_tabWidget->count() > 1 ) { return true; } } return QMainWindow::eventFilter(object, event); } /** * Reimplement closeEvent to save any modified documents before closing. */ void ProgramWindow::closeEvent(QCloseEvent *event) { bool discard; if(beforeClosing(true, discard)) { cleanUp(); QMainWindow::closeEvent(event); emit closed(); } else { event->ignore(); } QSettings settings; settings.setValue("programwindow/state",saveState()); settings.setValue("programwindow/geometry",saveGeometry()); } const QString ProgramWindow::untitledFileName() { return "Untitled"; } const QString ProgramWindow::fileExtension() { return ""; } const QString ProgramWindow::defaultSaveFolder() { return FolderUtils::openSaveFolder(); } bool ProgramWindow::event(QEvent * e) { switch (e->type()) { case QEvent::WindowActivate: emit changeActivationSignal(true, this); break; case QEvent::WindowDeactivate: emit changeActivationSignal(false, this); break; default: break; } return FritzingWindow::event(e); } int & ProgramWindow::untitledFileCount() { return UntitledIndex; } void ProgramWindow::setTitle() { setWindowTitle(tr("Code Window")); } void ProgramWindow::setTitle(const QString & filename) { setWindowTitle(tr("Code Window - %1").arg(filename)); } /** * Create and open a new tab within the PTabWidget child. * This function handled connecting all the appropriate signals * and setting an appropriate filename. */ ProgramTab * ProgramWindow::addTab() { QString name = (UntitledIndex == 1) ? untitledFileName() : tr("%1%2").arg(untitledFileName()).arg(UntitledIndex); ProgramTab * programTab = new ProgramTab(name, m_tabWidget); connect(programTab, SIGNAL(wantToSave(int)), this, SLOT(tabSave(int))); connect(programTab, SIGNAL(wantToSaveAs(int)), this, SLOT(tabSaveAs(int))); connect(programTab, SIGNAL(wantToRename(int)), this, SLOT(tabRename(int))); connect(programTab, SIGNAL(wantToDelete(int, bool)), this, SLOT(tabDelete(int, bool)), Qt::DirectConnection); connect(programTab, SIGNAL(programWindowUpdateRequest(bool, bool, bool, bool, bool, bool, Platform *, const QString &, const QString &, const QString &)), this, SLOT(updateMenu(bool, bool, bool, bool, bool, bool, Platform *, const QString &, const QString &, const QString &))); int ix = m_tabWidget->addTab(programTab, name); m_tabWidget->setCurrentIndex(ix); programTab->initMenus(); UntitledIndex++; return programTab; } /** * A function for closing the current displayed tab. * I'm using a ProgramWindow method instead of calling deleteTab() * directly as I believe it's more apropos. */ void ProgramWindow::closeCurrentTab() { closeTab(m_tabWidget->currentIndex()); } void ProgramWindow::closeTab(int index) { ProgramTab * pTab = indexWidget(index); if (pTab) { emit linkToProgramFile(pTab->filename(), NULL, false, true); pTab->deleteTab(); } } /** * This slot is for updating the tab-dependent menu items. * - program * - undo/redo * - cut/copy */ void ProgramWindow::updateMenu(bool programEnable, bool undoEnable, bool redoEnable, bool cutEnable, bool copyEnable, bool pasteEnable, Platform* platform, const QString & port, const QString & board, const QString & filename) { ProgramTab * programTab = currentWidget(); m_saveAction->setEnabled(programTab->isModified()); m_monitorAction->setEnabled(!port.isEmpty()); m_programAction->setEnabled(programEnable); m_undoAction->setEnabled(undoEnable); m_redoAction->setEnabled(redoEnable); m_cutAction->setEnabled(cutEnable); m_copyAction->setEnabled(copyEnable); m_pasteAction->setEnabled(pasteEnable); QAction *lang = m_platformActions.value(platform); if (lang) { lang->setChecked(true); } QAction *portAction = m_portActions.value(port); if (portAction) { portAction->setChecked(true); } QAction *boardAction = m_boardActions.value(board); if (boardAction) { boardAction->setChecked(true); } setTitle(filename); } void ProgramWindow::setPlatform(QAction* action) { currentWidget()->setPlatform(action->text()); } void ProgramWindow::setPort(QAction* action) { currentWidget()->setPort(action->text()); } void ProgramWindow::setBoard(QAction* action) { currentWidget()->setBoard(action->text()); } bool ProgramWindow::beforeClosing(bool showCancel, bool & discard) { discard = false; for (int i = 0; i < m_tabWidget->count(); i++) { if (!beforeClosingTab(i, showCancel)) { return false; } } return true; } bool ProgramWindow::beforeClosingTab(int index, bool showCancel) { ProgramTab * programTab = indexWidget(index); if (programTab == NULL) return true; if (!programTab->isModified()) return true; QMessageBox::StandardButton reply = beforeClosingMessage(programTab->filename(), showCancel); if (reply == QMessageBox::Save) { return prepSave(programTab, false); } if (reply == QMessageBox::Discard) { return true; } return false; } void ProgramWindow::print() { #ifndef QT_NO_PRINTER QPrinter printer(QPrinter::HighResolution); QPrintDialog printDialog(&printer, this); if (printDialog.exec() == QDialog::Accepted) { currentWidget()->print(printer); } #endif } // overrides MainWindow::saveAsAux bool ProgramWindow::saveAsAux(const QString & fileName) { if (!m_savingProgramTab) return false; bool result = m_savingProgramTab->save(fileName); m_savingProgramTab = NULL; return result; } void ProgramWindow::tabDelete(int index, bool deleteFile) { ProgramTab * programTab = indexWidget(index); QString fname = programTab->filename(); m_tabWidget->removeTab(index); if (m_tabWidget->count() == 0) { addTab(); } if (deleteFile) { QFile file(fname); file.remove(); } } void ProgramWindow::saveAll() { for (int i= 0; i < m_tabWidget->count(); i++) tabSave(i); } void ProgramWindow::saveCurrentTab() { tabSave(m_tabWidget->currentIndex()); } void ProgramWindow::tabSave(int index) { ProgramTab * programTab =indexWidget(index); if (programTab == NULL) return; prepSave(programTab, false); } void ProgramWindow::tabSaveAs(int index) { ProgramTab * programTab = indexWidget(index); if (programTab == NULL) return; prepSave(programTab, true); } void ProgramWindow::tabRename(int index) { ProgramTab * programTab = indexWidget(index); if (programTab == NULL) return; QString oldFileName = programTab->filename(); if (prepSave(programTab, true)) { if (programTab->filename() != oldFileName) { QFile oldFile(oldFileName); if (oldFile.exists()) { oldFile.remove(); emit linkToProgramFile(oldFileName, NULL, false, true); } } } } void ProgramWindow::duplicateTab() { ProgramTab * oldTab = currentWidget(); if (oldTab == NULL) return; ProgramTab * newTab = addTab(); newTab->setText(oldTab->text()); } void ProgramWindow::tabBeforeClosing(int index, bool & ok) { ok = beforeClosingTab(index, true); } bool ProgramWindow::prepSave(ProgramTab * programTab, bool saveAsFlag) { m_savingProgramTab = programTab; // need this for the saveAsAux call if (!programTab->isModified()) return false; bool result = (saveAsFlag) ? saveAs(programTab->filename(), programTab->readOnly()) : save(programTab->filename(), programTab->readOnly()); if (result) { programTab->setClean(); emit linkToProgramFile(programTab->filename(), programTab->platform(), true, true); } return result; } void ProgramWindow::initPlatforms() { QDir dir(FolderUtils::getApplicationSubFolderPath("translations")); Highlighter::loadStyles(dir.absolutePath().append("/syntax/styles.xml")); m_platforms << new PlatformArduino() << new PlatformPicaxe(); } QList ProgramWindow::getAvailablePlatforms() { return m_platforms; } Platform * ProgramWindow::getPlatformByName(const QString & platformName) { foreach (Platform * platform, getAvailablePlatforms()) { if (platform->getName().compare(platformName, Qt::CaseInsensitive) == 0) return platform; } return NULL; } bool ProgramWindow::hasPlatform(const QString & platformName) { return getPlatformByName(platformName) != NULL; } const QMap ProgramWindow::getBoards() { if (currentWidget() && currentWidget()->platform()) return currentWidget()->platform()->getBoards(); QMap boards; boards.insert(NoBoardName, NoBoardName); return boards; } QAction * ProgramWindow::addBoard(const QString & name, const QString & definition) { QAction * currentAction = new QAction(name, this); currentAction->setCheckable(true); currentAction->setData(definition); m_boardActions.insert(name, currentAction); m_boardMenu->addAction(currentAction); m_boardActionGroup->addAction(currentAction); return currentAction; } void ProgramWindow::updateBoards() { QMap boards = getBoards(); m_boardActions.clear(); foreach (QAction * action, m_boardActionGroup->actions()) m_boardActionGroup->removeAction(action); m_boardMenu->clear(); QMapIterator i(boards); while (i.hasNext()) { i.next(); addBoard(i.key(), i.value()); } } void ProgramWindow::loadProgramFile() { DebugDialog::debug("loading program file"); currentWidget()->loadProgramFile(); } void ProgramWindow::loadProgramFileNew() { ProgramTab * programTab = addTab(); if (programTab) { if (!programTab->loadProgramFile()) { delete programTab; } } } void ProgramWindow::rename() { currentWidget()->rename(); } void ProgramWindow::undo() { currentWidget()->undo(); } void ProgramWindow::redo() { currentWidget()->redo(); } void ProgramWindow::cut() { currentWidget()->cut(); } void ProgramWindow::copy() { currentWidget()->copy(); } void ProgramWindow::paste() { currentWidget()->paste(); } void ProgramWindow::selectAll() { currentWidget()->selectAll(); } void ProgramWindow::serialMonitor() { currentWidget()->serialMonitor(); } void ProgramWindow::sendProgram() { currentWidget()->sendProgram(); } ProgramTab * ProgramWindow::currentWidget() { return qobject_cast(m_tabWidget->currentWidget()); } ProgramTab * ProgramWindow::indexWidget(int index) { return qobject_cast(m_tabWidget->widget(index)); } bool ProgramWindow::alreadyHasProgram(const QString & filename) { DebugDialog::debug("already has program"); for (int i = 0; i < m_tabWidget->count(); i++) { ProgramTab * tab = indexWidget(i); if (tab->filename() == filename) { m_tabWidget->setCurrentIndex(i); return true; } } return false; } QString ProgramWindow::getExtensionString() { ProgramTab * pt = currentWidget(); if (pt == NULL) return ""; return pt->extensionString(); } QStringList ProgramWindow::getExtensions() { ProgramTab * pt = currentWidget(); if (pt == NULL) return QStringList(); return pt->extensions(); } QList ProgramWindow::getSerialPorts() { QList ports; ports = QSerialPortInfo::availablePorts(); /* // on the pc, handy for testing the UI when there are no serial ports ports.removeOne("COM0"); ports.removeOne("COM1"); ports.removeOne("COM2"); ports.removeOne("COM3"); */ return ports; } void ProgramWindow::updateSerialPorts() { QList ports = getSerialPorts(); m_portActions.clear(); foreach (QAction * action, m_serialPortActionGroup->actions()) m_serialPortActionGroup->removeAction(action); m_serialPortMenu->clear(); foreach (QSerialPortInfo port, ports) { addPort(port); } } QAction * ProgramWindow::addPort(QSerialPortInfo port) { QAction * currentAction = new QAction(port.portName(), this); currentAction->setCheckable(true); currentAction->setData(port.systemLocation()); m_portActions.insert(port.portName(), currentAction); m_serialPortMenu->addAction(currentAction); m_serialPortActionGroup->addAction(currentAction); return currentAction; } bool ProgramWindow::hasPort(const QString & portName) { foreach (QSerialPortInfo port, getSerialPorts()) { if (port.portName().compare(portName) == 0) return true; } return false; } void ProgramWindow::updateLink(const QString & filename, Platform * platform, bool addlink, bool strong) { DebugDialog::debug("updating link"); emit linkToProgramFile(filename, platform, addlink, strong); } void ProgramWindow::portProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { DebugDialog::debug(QString("process finished %1 %2").arg(exitCode).arg(exitStatus)); // parse the text and update the combo box sender()->deleteLater(); } void ProgramWindow::portProcessReadyRead() { m_ports.clear(); QByteArray byteArray = qobject_cast(sender())->readAllStandardOutput(); QTextStream textStream(byteArray, QIODevice::ReadOnly); while (true) { QString line = textStream.readLine(); if (line.isNull()) break; if (!line.contains("tty")) continue; if (!line.contains("serial", Qt::CaseInsensitive)) continue; QStringList candidates = line.split(" "); foreach (QString candidate, candidates) { if (candidate.contains("tty")) { m_ports.append(candidate); break; } } } } fritzing-app-0.9.3b/src/program/programwindow.h000066400000000000000000000140551272363601300216150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by\ the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PROGRAMWINDOW_H_ #define PROGRAMWINDOW_H_ #include #include #include #include #include #include #include #include #include // #include #include #include #include "platform.h" #include "syntaxer.h" #include "../mainwindow/fritzingwindow.h" struct LinkedFile { enum FileFlag { NoFlag = 0, SameMachineFlag = 1, ObsoleteFlag = 2, InBundleFlag = 4, ReadOnlyFlag = 8 }; Q_DECLARE_FLAGS(FileFlags, FileFlag) QString linkedFilename; QString platform; FileFlags fileFlags; }; class PTabWidget : public QTabWidget { Q_OBJECT protected slots: void tabChanged(int index); protected: int m_lastTabIndex; public: PTabWidget(QWidget * parent); QTabBar * tabBar(); }; class ProgramWindow : public FritzingWindow { Q_OBJECT public: ProgramWindow(QWidget *parent=0); ~ProgramWindow(); void setup(); void initMenus(QMenuBar * menubar); void linkFiles(const QList &, const QString & alternativePath); const QString defaultSaveFolder(); QList getSerialPorts(); QList getAvailablePlatforms(); bool hasPlatform(const QString &platformName); Platform *getPlatformByName(const QString &platformName); const QMap getBoards(); bool hasPort(const QString &portName); void loadProgramFileNew(); bool alreadyHasProgram(const QString &); void updateLink(const QString & filename, Platform *platform, bool addlink, bool strong); void showMenus(bool); void createViewMenuActions(QList &); void print(); public slots: void saveAll(); signals: void closed(); void changeActivationSignal(bool activate, QWidget * originator); void linkToProgramFile(const QString & filename, Platform * platform, bool addlink, bool strong); protected slots: void loadProgramFile(); class ProgramTab * addTab(); void closeCurrentTab(); void closeTab(int index); void saveCurrentTab(); void tabSave(int); void tabSaveAs(int); void tabRename(int); void duplicateTab(); void tabBeforeClosing(int, bool & ok); void tabDelete(int index, bool deleteFile); void updateMenu(bool programEnable, bool undoEnable, bool redoEnable, bool cutEnable, bool copyEnable, bool pasteEnable, Platform * platform, const QString & port, const QString & board, const QString & filename); void updateSerialPorts(); void portProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); void portProcessReadyRead(); void updateBoards(); // The following methods just forward events on to the current tab void setPlatform(QAction*); void setPort(QAction*); void setBoard(QAction*); void rename(); void undo(); void redo(); void cut(); void copy(); void paste(); void selectAll(); void serialMonitor(); void sendProgram(); protected: bool event(QEvent * event); void closeEvent(QCloseEvent *event); bool eventFilter(QObject *object, QEvent *event); QFrame * createHeader(); QFrame * createCenter(); const QString untitledFileName(); const QString fileExtension(); void cleanUp(); int &untitledFileCount(); void setTitle(); void setTitle(const QString & filename); bool saveAsAux(const QString & fileName); bool prepSave(class ProgramTab *, bool saveAsFlag); bool beforeClosingTab(int index, bool showCancel); QAction * addBoard(const QString & name, const QString & definition); QAction * addPort(QSerialPortInfo port); inline ProgramTab * currentWidget(); inline ProgramTab * indexWidget(int index); QString getExtensionString(); QStringList getExtensions(); bool beforeClosing(bool showCancel, bool & discard); // returns true if close, false if cancel QStringList getSerialPortsAux(); protected: static void initPlatforms(); public: static const QString LocateName; static QString NoBoardName; QAction *m_newAction; QAction *m_openAction; QAction *m_saveAction; QAction *m_monitorAction; QAction *m_programAction; protected: static QList m_platforms; protected: QPointer m_tabWidget; QPointer m_addButton; QPointer m_savingProgramTab; QAction *m_undoAction; QAction *m_redoAction; QAction *m_cutAction; QAction *m_copyAction; QAction *m_pasteAction; QAction *m_selectAction; QAction *m_printAction; QAction *m_preferencesAction; QMap m_platformActions; QMap m_portActions; QMap m_boardActions; QActionGroup * m_platformActionGroup; QActionGroup * m_boardActionGroup; QActionGroup * m_serialPortActionGroup; QMenu * m_platformMenu; QMenu * m_boardMenu; QMenu * m_serialPortMenu; QStringList m_ports; // temporary storage for linux QMenu* m_editMenu; QMenu* m_viewMenu; QList m_viewMenuActions; QMenu * m_programMenu; }; #endif /* ProgramWindow_H_ */ fritzing-app-0.9.3b/src/program/syntaxer.cpp000066400000000000000000000163761272363601300211360ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include "syntaxer.h" #include "../debugdialog.h" #include "../utils/textutils.h" #include #include QHash Syntaxer::m_listsToFormats; Syntaxer::Syntaxer() : QObject() { m_trieRoot = NULL; } Syntaxer::~Syntaxer() { if (m_trieRoot) { delete m_trieRoot; } } bool Syntaxer::loadSyntax(const QString &filename) { QFile file(filename); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { return false; } QDomElement root = domDocument.documentElement(); if (root.isNull()) return false; if (root.tagName() != "language") return false; QDomElement highlighting = root.firstChildElement("highlighting"); if (highlighting.isNull()) return false; QDomElement general = root.firstChildElement("general"); if (general.isNull()) return false; QDomElement contexts = highlighting.firstChildElement("contexts"); if (contexts.isNull()) return false; m_hlCStringChar = false; QDomElement context = contexts.firstChildElement("context"); while (!context.isNull()) { if (context.attribute("attribute").compare("Normal Text") == 0) { m_stringDelimiter = getStringDelimiter(context); initListsToFormats(context); } else if (context.attribute("attribute").compare("String") == 0) { QDomElement HlCStringChar = context.firstChildElement("HlCStringChar"); if (!HlCStringChar.isNull()) { m_hlCStringChar = true; } } context = context.nextSiblingElement("context"); } m_canProgram = root.attribute("canProgram", "").compare("true", Qt::CaseInsensitive) == 0; m_name = root.attribute("name"); QStringList extensions = root.attribute("extensions").split(";", QString::SkipEmptyParts); if (extensions.count() > 0) { m_extensionString = m_name + " " + QObject::tr("files") + " ("; foreach (QString ext, extensions) { m_extensionString += ext + " "; int ix = ext.indexOf("."); if (ix > 0) { ext.remove(0, ix); } m_extensions.append(ext); } m_extensionString.chop(1); m_extensionString += ")"; } m_trieRoot = new TrieNode('\0'); QDomElement list = highlighting.firstChildElement("list"); while (!list.isNull()) { loadList(list); list = list.nextSiblingElement("list"); } QDomElement comments = general.firstChildElement("comments"); if (!comments.isNull()) { Qt::CaseSensitivity caseSensitivity = comments.attribute("casesensitive").compare("1") == 0 ? Qt::CaseSensitive : Qt::CaseInsensitive; QDomElement comment = comments.firstChildElement("comment"); while (!comment.isNull()) { CommentInfo * commentInfo = new CommentInfo(comment.attribute("start"), comment.attribute("end"), caseSensitivity); commentInfo->m_index = m_commentInfo.count(); m_commentInfo.append(commentInfo); comment = comment.nextSiblingElement("comment"); } } return true; } QString Syntaxer::parseForName(const QString & filename) { QFile file(filename); file.open(QFile::ReadOnly); QXmlStreamReader xml(&file); xml.setNamespaceProcessing(false); while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name().toString().compare("language") == 0) { return xml.attributes().value("name").toString(); } break; default: break; } } return ""; } void Syntaxer::loadList(QDomElement & list) { QString name = list.attribute("name"); SyntaxerTrieLeaf * stf = new SyntaxerTrieLeaf(name); QDomElement item = list.firstChildElement("item"); while (!item.isNull()) { QString text; if (TextUtils::findText(item, text)) { QString s = text.trimmed(); m_trieRoot->addString(s, false, stf); } item = item.nextSiblingElement("item"); } } bool Syntaxer::matches(const QString & string, TrieLeaf * & leaf) { if (m_trieRoot == NULL) return false; QString temp = string; return m_trieRoot->matches(temp, leaf); } const CommentInfo * Syntaxer::getCommentInfo(int ix) { return m_commentInfo.at(ix); } bool Syntaxer::matchCommentStart(const QString & text, int offset, int & result, const CommentInfo * & resultCommentInfo) { result = -1; foreach (CommentInfo * commentInfo, m_commentInfo) { int si = text.indexOf(commentInfo->m_start, offset, commentInfo->m_caseSensitive); if (si >= 0 && (result < 0 || si < result)) { result = si; resultCommentInfo = commentInfo; } } return (result >= offset); } int Syntaxer::matchStringStart(const QString & text, int offset) { if (m_stringDelimiter.isNull()) return -1; return text.indexOf(m_stringDelimiter, offset); } int Syntaxer::matchStringEnd(const QString & text, int offset) { return matchStringStart(text, offset); } const QStringList & Syntaxer::extensions() { return m_extensions; } const QString & Syntaxer::extensionString() { return m_extensionString; } QChar Syntaxer::getStringDelimiter(QDomElement & context) { QDomElement detectChar = context.firstChildElement("DetectChar"); while (!detectChar.isNull()) { if (detectChar.attribute("attribute").compare("String") == 0) { QString c = detectChar.attribute("char"); if (c.length() > 0) { return c.at(0); } return QChar(); } detectChar = detectChar.nextSiblingElement("DetectChar"); } return QChar(); } void Syntaxer::initListsToFormats(QDomElement & context) { QDomElement keyword = context.firstChildElement("keyword"); while (!keyword.isNull()) { QString format = keyword.attribute("attribute"); QString list = keyword.attribute("String"); if (!format.isEmpty() && !list.isEmpty()) { m_listsToFormats.insert(list, format); } keyword = keyword.nextSiblingElement("keyword"); } } QString Syntaxer::formatFromList(const QString & list) { return m_listsToFormats.value(list, ___emptyString___); } bool Syntaxer::hlCStringChar() { return m_hlCStringChar; } bool Syntaxer::canProgram() { return m_canProgram; } ////////////////////////////////////////////// SyntaxerTrieLeaf::SyntaxerTrieLeaf(QString name) { m_name = name; } SyntaxerTrieLeaf::~SyntaxerTrieLeaf() { } const QString & SyntaxerTrieLeaf::name() { return m_name; } ////////////////////////////////////////////// CommentInfo::CommentInfo(const QString & start, const QString & end, Qt::CaseSensitivity caseSensitive) { m_start = start; m_end = end; m_multiLine = !end.isEmpty(); m_caseSensitive = caseSensitive; } fritzing-app-0.9.3b/src/program/syntaxer.h000066400000000000000000000052101272363601300205640ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SYNTAXER_H_ #define SYNTAXER_H_ #include #include #include #include #include "trienode.h" class CommentInfo { public: CommentInfo(const QString & start, const QString & end, Qt::CaseSensitivity); public: bool m_multiLine; QString m_start; QString m_end; int m_index; Qt::CaseSensitivity m_caseSensitive; }; class Syntaxer : public QObject { Q_OBJECT public: Syntaxer(); virtual ~Syntaxer(); bool loadSyntax(const QString & filename); bool matches(const QString & string, TrieLeaf * & leaf); const CommentInfo * getCommentInfo(int ix); bool matchCommentStart(const QString & text, int offset, int & result, const CommentInfo * & resultCommentInfo); int matchStringStart(const QString & text, int offset); int matchStringEnd(const QString & text, int offset); const QString & extensionString(); const QStringList & extensions(); bool hlCStringChar(); bool canProgram(); public: static QString parseForName(const QString & filename); static QString formatFromList(const QString & list); protected: void loadList(QDomElement & list); QChar getStringDelimiter(QDomElement & context); void initListsToFormats(QDomElement & context); protected: static QHash m_listsToFormats; protected: TrieNode * m_trieRoot; QString m_name; QString m_extensionString; QStringList m_extensions; QList m_commentInfo; QChar m_stringDelimiter; bool m_hlCStringChar; bool m_canProgram; }; class SyntaxerTrieLeaf : public TrieLeaf { public: SyntaxerTrieLeaf(QString name); ~SyntaxerTrieLeaf(); const QString & name(); protected: QString m_name; }; #endif /* SYNTAXER_H_ */ fritzing-app-0.9.3b/src/program/trienode.cpp000066400000000000000000000051151272363601300210570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "trienode.h" #include "../debugdialog.h" #include #include TrieLeaf::TrieLeaf() { } TrieLeaf::~TrieLeaf() { } TrieNode::TrieNode(QChar c) { m_char = c; m_leafData = NULL; m_isLeaf = false; m_caseInsensitive = false; } TrieNode::~TrieNode() { foreach (TrieNode * node, m_children) { delete node; } m_children.clear(); } void TrieNode::addString(QString & string, bool caseInsensitive, TrieLeaf * leaf) { if (string.isEmpty()) { m_leafData = leaf; m_isLeaf = true; return; } QChar in(string.at(0)); QString next = string.remove(0, 1); QChar c0, c1; if (caseInsensitive) { c0 = in.toLower(); c1 = in.toUpper(); } else { c0 = c1 = in; } addStringAux(c0, next, caseInsensitive, leaf); if (c0 != c1) { addStringAux(c1, next, caseInsensitive, leaf); } } void TrieNode::addStringAux(QChar c, QString & next, bool caseInsensitive, TrieLeaf * leaf) { bool gotc = false; foreach (TrieNode * node, m_children) { if (node->matchesChar(c)) { node->addString(next, caseInsensitive, leaf); gotc = true; } } if (!gotc) { TrieNode * child = new TrieNode(c); m_children.append(child); child->addString(next, caseInsensitive, leaf); } } bool TrieNode::matchesChar(QChar c) { return (c == m_char); } bool TrieNode::matches(QString & string, TrieLeaf * & leaf) { if (string.isEmpty()) { if (m_isLeaf) { leaf = m_leafData; return true; } return false; } QChar in(string.at(0)); foreach (TrieNode * node, m_children) { if (node->matchesChar(in)) { return node->matches(string.remove(0, 1), leaf); } } return false; } fritzing-app-0.9.3b/src/program/trienode.h000066400000000000000000000031461272363601300205260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef TRIENODE_H_ #define TRIENODE_H_ #include #include class TrieLeaf { public: TrieLeaf(); virtual ~TrieLeaf(); }; class TrieNode { public: TrieNode(QChar); virtual ~TrieNode(); virtual void addString(QString & s, bool caseInsensitive, TrieLeaf * leaf); virtual bool matches(QString & string, TrieLeaf * & leaf); protected: virtual void addStringAux(QChar c, QString & next, bool caseInsensitive, TrieLeaf * leaf); virtual bool matchesChar(QChar c); protected: QChar m_char; bool m_caseInsensitive; QList m_children; TrieLeaf * m_leafData; bool m_isLeaf; }; #endif /* TRIENODE_H_ */ fritzing-app-0.9.3b/src/referencemodel/000077500000000000000000000000001272363601300200505ustar00rootroot00000000000000fritzing-app-0.9.3b/src/referencemodel/referencemodel.h000066400000000000000000000050671272363601300232100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef REFERENCEMODEL_H_ #define REFERENCEMODEL_H_ #include "../model/palettemodel.h" class ReferenceModel : public PaletteModel { Q_OBJECT public: virtual bool loadAll(const QString & databaseName, bool fullLoad, bool dbExists) = 0; virtual bool loadFromDB(const QString & databaseName) = 0; virtual ModelPart *loadPart(const QString & path, bool update) = 0; virtual ModelPart *reloadPart(const QString & path, const QString & moduleID) = 0; virtual ModelPart *retrieveModelPart(const QString &moduleID) = 0; virtual bool addPart(ModelPart * newModel, bool update) = 0; virtual ModelPart * addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists) = 0; virtual bool updatePart(ModelPart * newModel) = 0; virtual bool swapEnabled() const = 0; virtual QString partTitle(const QString & moduleID) = 0; virtual void recordProperty(const QString &name, const QString &value) = 0; virtual QString retrieveModuleIdWith(const QString &family, const QString &propertyName, bool closestMatch) = 0; virtual QString retrieveModuleId(const QString &family, const QMultiHash &properties, const QString &propertyName, bool closestMatch) = 0; virtual QStringList propValues(const QString &family, const QString &propName, bool distinct) = 0; virtual QMultiHash allPropValues(const QString &family, const QString &propName) = 0; virtual bool lastWasExactMatch() = 0; virtual void setSha(const QString & sha) = 0; virtual const QString & sha() const = 0; }; #endif /* REFERENCEMODEL_H_ */ fritzing-app-0.9.3b/src/referencemodel/sqlitereferencemodel.cpp000066400000000000000000001355421272363601300247670ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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.floadfrom You should have received a copy of the GNU General Public License along with Fritzing. If not, see . ******************************************************************** $Revision: 6956 $: $Author: irascibl@gmail.com $: $Date: 2013-04-07 12:14:50 +0200 (So, 07. Apr 2013) $ ********************************************************************/ // copying a table from one SQWL database to another http://sqlite.phxsoftware.com/forums/t/285.aspx #include #include #include #include #include #include #include #include #include "sqlitereferencemodel.h" #include "../debugdialog.h" #include "../connectors/svgidlayer.h" #include "../connectors/connector.h" #include "../connectors/connectorshared.h" #include "../connectors/busshared.h" #include "../utils/folderutils.h" #include "../utils/fmessagebox.h" #include "../utils/textutils.h" #define MAX_CONN_TRIES 3 static const qulonglong NO_ID = std::numeric_limits::max(); void debugError(bool result, QSqlQuery & query) { if (result) return; QSqlError error = query.lastError(); DebugDialog::debug(QString("%1 %2 %3").arg(error.text()).arg(error.number()).arg(error.type())); } static ModelPart * DebugModelPart = NULL; void debugExec(const QString & msg, QSqlQuery & query) { DebugDialog::debug( "SQLITE: " + msg + "\n" "\t "+ query.lastQuery() + "\n" "\t ERROR DRIVER: "+ query.lastError().driverText() + "\n" "\t ERROR DB: " + query.lastError().databaseText() + "\n" "\t moduleid:" + (DebugModelPart == NULL ? "" : DebugModelPart->moduleID()) + "" ); QMap map = query.boundValues(); foreach (QString name, map.keys()) { DebugDialog::debug(QString("\t%1:%2").arg(name).arg(map.value(name).toString())); } } void killConnectors(QVector & connectors) { foreach (Connector * connector, connectors) { delete connector->connectorShared(); delete connector; } connectors.clear(); } void killBuses(QVector & buses) { foreach (BusShared * bus, buses) { delete bus; } buses.clear(); } QStringList FailurePartMessages; QStringList FailurePropertyMessages; void noSwappingMessage() { FMessageBox::warning(NULL, QObject::tr("Oops!"), QObject::tr("Sorry, we have a problem with the swapping mechanism.\nFritzing still works, but you won't be able to change parts properties."), QMessageBox::Ok); } /////////////////////////////////////////////////// SqliteReferenceModel::SqliteReferenceModel() { m_swappingEnabled = false; m_lastWasExactMatch = true; } bool SqliteReferenceModel::loadAll(const QString & databaseName, bool fullLoad, bool dbExists) { FailurePartMessages.clear(); FailurePropertyMessages.clear(); m_fullLoad = fullLoad; initParts(dbExists); int tries = 0; m_keepGoing = true; while(!m_swappingEnabled && tries < MAX_CONN_TRIES && m_keepGoing) { createDatabase(databaseName, fullLoad); if(!m_swappingEnabled) { deleteConnection(); } tries++; } /* TODO Mariano: perhaps we should check that there are no parts with * the same family and providing exactly the same properties set */ DebugDialog::debug("referenceModel::loadAll completed"); if (!m_swappingEnabled) { noSwappingMessage(); } else if (FailurePartMessages.count() > 0) { QString message = tr("The swapping mechanism is disabled for:\n\n"); if (FailurePartMessages.count() == 1) { message += FailurePartMessages.at(0); } else if (FailurePartMessages.count() < 6) { message += FailurePartMessages.join("\n"); } else { for (int i = 0; i < 4; i++) { message += FailurePartMessages.at(i); message += ("\n"); } message += "\n" + tr("and %1 other parts").arg(FailurePartMessages.count() - 4); } FMessageBox::warning(QApplication::activeWindow(), QObject::tr("Oops!"), message, QMessageBox::Ok); } else if (FailurePropertyMessages.count() > 0) { QString message = tr("The swapping mechanism is disabled for:\n\n"); if (FailurePropertyMessages.count() == 1) { message += FailurePropertyMessages.at(0); } else if (FailurePropertyMessages.count() < 6) { message += FailurePropertyMessages.join("\n"); } else { for (int i = 0; i < 4; i++) { message += FailurePropertyMessages.at(i); message += ("\n"); } message += "\n" + tr("and %1 other properties").arg(FailurePropertyMessages.count() - 4); } FMessageBox::warning(QApplication::activeWindow(), QObject::tr("Oops!"), message, QMessageBox::Ok); } return m_swappingEnabled; } bool SqliteReferenceModel::loadFromDB(const QString & databaseName) { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "temporary"); db.setDatabaseName(databaseName); /* QVariant v = m_database.driver()->handle(); if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0) { // v.data() returns a pointer to the handle sqlite3 *handle = *static_cast(v.data()); if (handle != 0) { // check that it is not NULL const char * message = sqlite3_errmsg(handle); qDebug() << "testing sqlite3" << message; } } */ m_swappingEnabled = loadFromDB(m_database, db); if (db.isOpen()) db.close(); if (!m_swappingEnabled) { killParts(); noSwappingMessage(); } return m_swappingEnabled; } bool SqliteReferenceModel::loadFromDB(QSqlDatabase & keep_db, QSqlDatabase & db) { bool opened = false; for (int i = 0; i < MAX_CONN_TRIES; i++) { if (!db.open()) continue; opened = true; break; } if (!opened) { return false; } m_sha = ""; QSqlQuery query = db.exec("SELECT sha FROM lastcommit where id=0"); debugError(query.isActive(), query); if (query.isActive()) { if (query.next()) { m_sha = query.value(0).toString(); } } query = db.exec("SELECT COUNT(*) FROM parts"); debugError(query.isActive(), query); if (!query.isActive() || !query.next()) return false; int count = query.value(0).toInt(); if (count == 0) { return false; } DebugDialog::debug(QString("parts count %1").arg(count)); QVector parts(count + 1, NULL); QVector oldToNew(count + 1, 0); query = db.exec("SELECT path, moduleID, id, family, version, replacedby, fritzingversion, author, title, label, date, description, spice, spicemodel, taxonomy, itemtype FROM parts"); debugError(query.isActive(), query); if (!query.isActive()) return false; QSqlQuery q2(keep_db); bool result = q2.prepare("INSERT INTO parts(moduleID, family, core) VALUES (:moduleID, :family, :core)"); debugError(result, q2); QFileInfo info(db.databaseName()); QDir partsDir = info.absoluteDir(); // parts folder while (query.next()) { int ix = 0; QString path = query.value(ix++).toString(); QString moduleID = query.value(ix++).toString(); qulonglong dbid = query.value(ix++).toULongLong(); if (m_partHash.value(moduleID, NULL) != NULL) { // a part with this moduleID was already loaded--the file version overrides the db version continue; } if (!path.startsWith(ResourcePath)) { // not the resources path path = partsDir.absoluteFilePath(path); if (QFileInfo(path).exists()) { CoreList << moduleID; } } ModelPart * modelPart = new ModelPart(); ModelPartShared * modelPartShared = new ModelPartShared(); modelPart->setModelPartShared(modelPartShared); modelPartShared->setModuleID(moduleID); modelPartShared->setDBID(dbid); QString family = query.value(ix++).toString(); modelPartShared->setFamily(family); modelPartShared->setVersion(query.value(ix++).toString()); modelPartShared->setReplacedby(query.value(ix++).toString()); modelPartShared->setFritzingVersion(query.value(ix++).toString()); modelPartShared->setAuthor(query.value(ix++).toString()); modelPartShared->setTitle(query.value(ix++).toString()); modelPartShared->setLabel(query.value(ix++).toString()); modelPartShared->setDate(query.value(ix++).toString()); modelPartShared->setDescription(query.value(ix++).toString()); modelPartShared->setSpice(query.value(ix++).toString()); modelPartShared->setSpiceModel(query.value(ix++).toString()); modelPartShared->setTaxonomy(query.value(ix++).toString()); modelPart->setItemType((ModelPart::ItemType) query.value(ix++).toInt()); modelPartShared->setPath(path); modelPart->setCore(true); modelPartShared->setConnectorsInitialized(true); m_partHash.insert(modelPartShared->moduleID(), modelPart); parts[dbid] = modelPart; q2.bindValue(":moduleID", modelPartShared->moduleID()); q2.bindValue(":family", family); q2.bindValue(":core", "1"); bool result = q2.exec(); if (!result) debugExec("unable to add part to memory", q2); qulonglong newid = q2.lastInsertId().toULongLong(); oldToNew[dbid] = newid; } query = db.exec("SELECT viewid, image, layers, sticky, flipvertical, fliphorizontal, part_id FROM viewimages"); debugError(query.isActive(), query); if (!query.isActive()) return false; while (query.next()) { int ix = 0; ViewImage * viewImage = new ViewImage(ViewLayer::BreadboardView); viewImage->viewID = (ViewLayer::ViewID) query.value(ix++).toInt(); viewImage->image = query.value(ix++).toString(); viewImage->layers = query.value(ix++).toULongLong(); viewImage->sticky = query.value(ix++).toULongLong(); viewImage->canFlipVertical = query.value(ix++).toInt() == 0 ? false : true; viewImage->canFlipHorizontal = query.value(ix++).toInt() == 0 ? false : true; qulonglong dbid = query.value(ix++).toULongLong(); ModelPart * modelPart = parts.at(dbid); if (modelPart) { parts.at(dbid)->setViewImage(viewImage); } } query = db.exec("SELECT tag, part_id FROM tags"); debugError(query.isActive(), query); if (!query.isActive()) return false; while (query.next()) { int ix = 0; QString tag = query.value(ix++).toString(); qulonglong dbid = query.value(ix++).toULongLong(); ModelPart * modelPart = parts.at(dbid); if (modelPart) { parts.at(dbid)->setTag(tag); } } query = db.exec("SELECT name, value, part_id, show_in_label FROM properties"); debugError(query.isActive(), query); if (!query.isActive()) return false; QSqlQuery q3(keep_db); result = q3.prepare("INSERT INTO properties(name, value, part_id, show_in_label) VALUES (:name, :value, :part_id, :show_in_label)"); debugError(result, q3); while (query.next()) { int ix = 0; QString name = query.value(ix++).toString(); QString value = query.value(ix++).toString(); qulonglong dbid = query.value(ix++).toULongLong(); int showInLabel = query.value(ix++).toInt(); ModelPart * modelPart = parts.at(dbid); if (modelPart) { parts.at(dbid)->setProperty(name, value, showInLabel); q3.bindValue(":name", name.toLower().trimmed()); q3.bindValue(":value", value); q3.bindValue(":part_id", oldToNew[dbid]); q3.bindValue(":show_in_label", showInLabel); bool result = q3.exec(); if (!result) debugExec("unable to add property to memory", q3); } } query = db.exec("SELECT COUNT(*) FROM connectors"); debugError(query.isActive(), query); if (!query.isActive() || !query.next()) return false; int connectorCount = query.value(0).toInt(); if (connectorCount == 0) return false; QVector connectors(connectorCount + 1, NULL); query = db.exec("SELECT id, connectorid, type, name, description, replacedby, part_id FROM connectors"); debugError(query.isActive(), query); if (!query.isActive()) { killConnectors(connectors); return false; } while (query.next()) { int ix = 0; qulonglong cid = query.value(ix++).toULongLong(); QString connectorid = query.value(ix++).toString(); Connector::ConnectorType type = (Connector::ConnectorType) query.value(ix++).toInt(); QString name = query.value(ix++).toString(); QString description = query.value(ix++).toString(); QString replacedby = query.value(ix++).toString(); qulonglong dbid = query.value(ix++).toULongLong(); ModelPart * modelPart = parts.at(dbid); if (modelPart) { ConnectorShared * connectorShared = new ConnectorShared(); connectorShared->setConnectorType(type); connectorShared->setDescription(description); connectorShared->setReplacedby(replacedby); connectorShared->setSharedName(name); connectorShared->setId(connectorid); Connector * connector = new Connector(connectorShared, modelPart); modelPart->addConnector(connector); connectors[cid] = connector; } } query = db.exec("SELECT view, layer, svgid, hybrid, terminalid, legid, connector_id FROM connectorlayers"); debugError(query.isActive(), query); if (!query.isActive()) { killConnectors(connectors); return false; } while (query.next()) { int ix = 0; ViewLayer::ViewID viewID = (ViewLayer::ViewID) query.value(ix++).toInt(); ViewLayer::ViewLayerID viewLayerID = (ViewLayer::ViewLayerID) query.value(ix++).toInt(); QString svgID = query.value(ix++).toString(); bool hybrid = query.value(ix++).toInt() == 0 ? false : true; QString terminalID = query.value(ix++).toString(); QString legID = query.value(ix++).toString(); qulonglong cid = query.value(ix++).toULongLong(); Connector * connector = connectors.at(cid); if (connector) { connectors[cid]->addPin(viewID, svgID, viewLayerID, terminalID, legID, hybrid); } } query = db.exec("SELECT COUNT(*) FROM buses"); debugError(query.isActive(), query); if (!query.isActive() || !query.next()) return false; int busCount = query.value(0).toInt(); if (busCount == 0) return false; QVector buses(busCount + 1, NULL); QHash busids; query = db.exec("SELECT id, name, part_id FROM buses"); debugError(query.isActive(), query); if (!query.isActive()) { killConnectors(connectors); killBuses(buses); return false; } while (query.next()) { int ix = 0; qulonglong bid = query.value(ix++).toULongLong(); QString name = query.value(ix++).toString(); qulonglong dbid = query.value(ix++).toULongLong(); ModelPart * modelPart = parts.at(dbid); if (modelPart) { BusShared * busShared = new BusShared(name); ModelPart * modelPart = parts.at(dbid); modelPart->modelPartShared()->insertBus(busShared); buses[bid] = busShared; busids.insert(busShared, dbid); } } query = db.exec("SELECT connectorid, bus_id FROM busmembers"); debugError(query.isActive(), query); if (!query.isActive()) { killConnectors(connectors); killBuses(buses); return false; } while (query.next()) { int ix = 0; QString connectorid = query.value(ix++).toString(); qulonglong bid = query.value(ix++).toULongLong(); BusShared * busShared = buses[bid]; if (busShared) { qulonglong dbid = busids.value(busShared); ModelPart * modelPart = parts.at(dbid); if (modelPart) { ConnectorShared * connectorShared = modelPart->modelPartShared()->getConnectorShared(connectorid); busShared->addConnectorShared(connectorShared); } } } query = db.exec("SELECT subpart_id, part_id FROM schematic_subparts"); debugError(query.isActive(), query); if (query.isActive()) { while (query.next()) { int ix = 0; QString subpartID = query.value(ix++).toString(); qulonglong dbid = query.value(ix++).toULongLong(); ModelPart * modelPart = parts.at(dbid); if (modelPart) { QString subModuleID = modelPart->moduleID() + "_" + subpartID; ModelPart * subModelPart = m_partHash.value(subModuleID); if (subModelPart) { subModelPart->setSubpartID(subpartID); modelPart->modelPartShared()->addSubpart(subModelPart->modelPartShared()); } } } } if (m_root == NULL) { m_root = new ModelPart(); } foreach (ModelPart * modelPart, m_partHash.values()) { if (modelPart->dbid() != 0) { // initConnectors is not redundant here // there may be parts in m_partHash loaded from a file rather from the database // modelPart->initConnectors(); modelPart->flipSMDAnd(); modelPart->initBuses(); modelPart->setParent(m_root); modelPart->lookForZeroConnector(); } } return true; } SqliteReferenceModel::~SqliteReferenceModel() { deleteConnection(); } void SqliteReferenceModel::initParts(bool dbExists) { m_init = true; PaletteModel::initParts(dbExists); m_init = false; } bool SqliteReferenceModel::createDatabase(const QString & databaseName, bool fullLoad) { m_swappingEnabled = true; m_database = QSqlDatabase::addDatabase("QSQLITE"); m_database.setDatabaseName(databaseName.isEmpty() ? ":memory:" : databaseName); if (!m_database.open()) { m_swappingEnabled = false; } else { m_keepGoing = false; bool gotTransaction = m_database.transaction(); DebugDialog::debug(gotTransaction ? "got transaction" : "no transaction"); bool result = createParts(m_database, fullLoad); QSqlQuery query; result = query.exec("CREATE TABLE lastcommit (\n" "id INTEGER PRIMARY KEY NOT NULL,\n" "sha TEXT NOT NULL" ")"); debugError(result, query); result = query.exec("CREATE TABLE viewimages (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "viewid INTEGER NOT NULL,\n" // ViewLayer::ViewID "image TEXT NOT NULL,\n" "layers INTEGER NOT NULL,\n" // bit flag (max 8 bytes = 64 layers) "sticky INTEGER NOT NULL,\n" // bit flag (max 8 bytes = 64 layers) "flipvertical INTEGER NOT NULL,\n" // boolean "fliphorizontal INTEGER NOT NULL,\n" // boolean "part_id INTEGER NOT NULL" ")"); debugError(result, query); result = query.exec("CREATE TABLE connectors (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "connectorid TEXT NOT NULL,\n" "type INTEGER NOT NULL,\n" "name TEXT NOT NULL,\n" "description TEXT,\n" "replacedby TEXT,\n" "part_id INTEGER NOT NULL" ")"); debugError(result, query); result = query.exec("CREATE TABLE connectorlayers (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "view INTEGER NOT NULL,\n" // ViewLayer::ViewID "layer INTEGER NOT NULL,\n" // ViewLayer::ViewLayerID "svgid TEXT NOT NULL,\n" "hybrid INTEGER NOT NULL,\n" "terminalid TEXT,\n" "legid TEXT,\n" "connector_id INTEGER NOT NULL" ")"); debugError(result, query); result = query.exec("CREATE TABLE buses (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "name TEXT NOT NULL,\n" "part_id INTEGER NOT NULL" ")"); debugError(result, query); result = query.exec("CREATE TABLE busmembers (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "connectorid TEXT NOT NULL,\n" "bus_id INTEGER NOT NULL" ")"); debugError(result, query); result = query.exec("CREATE TABLE schematic_subparts (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "label TEXT NOT NULL,\n" "subpart_id TEXT NOT NULL,\n" "part_id INTEGER NOT NULL" ")"); debugError(result, query); // TODO: create a dictionary table so redundant tags, property names, and property values aren't stored multiple times result = query.exec("CREATE TABLE tags (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,\n" "tag TEXT NOT NULL,\n" "part_id INTEGER NOT NULL" ")"); debugError(result, query); result = createProperties(m_database); result = query.exec("CREATE TRIGGER unique_part__moduleID \n" "BEFORE INSERT ON parts \n" "FOR EACH ROW BEGIN \n" "SELECT RAISE(ROLLBACK, 'insert on table \"parts\" violates unique constraint \"unique_part__moduleID\"') \n" "WHERE (SELECT count(*) FROM parts WHERE moduleID = NEW.moduleID) > 0; \n" "END; " ); debugError(result, query); if (fullLoad) { QSqlQuery query; query.prepare("INSERT INTO lastcommit(id, sha) VALUES (:id, :sha)"); query.bindValue(":id", 0); query.bindValue(":sha", m_sha); result = query.exec(); debugError(result, query); foreach(ModelPart* mp, m_partHash.values()) { mp->initConnectors(false); } } foreach(ModelPart* mp, m_partHash.values()) { addPartAux(mp, fullLoad); } createIndexes(); createMoreIndexes(m_database); m_database.commit(); } return m_swappingEnabled; } void SqliteReferenceModel::deleteConnection() { QSqlDatabase::removeDatabase("SQLITE"); } ModelPart *SqliteReferenceModel::loadPart(const QString & path, bool update) { ModelPart *modelPart = PaletteModel::loadPart(path, update); if (modelPart == NULL) return modelPart; if (!m_init) addPart(modelPart, update); return modelPart; } ModelPart *SqliteReferenceModel::retrieveModelPart(const QString &moduleID) { if (moduleID.isEmpty()) { return NULL; } return m_partHash.value(moduleID, NULL); } QString SqliteReferenceModel::retrieveModuleIdWith(const QString &family, const QString &propertyName, bool closestMatch) { QString moduleID = retrieveModuleId(family,m_recordedProperties,propertyName, closestMatch); m_recordedProperties.clear(); return moduleID; } QString SqliteReferenceModel::retrieveModuleId(const QString &family, const QMultiHash &properties, const QString &propertyName, bool closestMatch) { QString propertyValue; if(properties.size() > 0) { QString queryStr = "SELECT moduleID FROM parts part \n" "WHERE part.family = :family AND (1=1 "; QHash params; int ix = 0; foreach (QString name, properties.uniqueKeys()) { foreach (QString value, properties.values(name)) { QString propParam = QString(":prop%1").arg(ix++); queryStr += QString(" AND EXISTS (SELECT * FROM properties prop WHERE prop.part_id = part.id AND prop.name = %1_name AND prop.value = %1_value)").arg(propParam); params[propParam+"_name"] = name; params[propParam+"_value"] = value; if(name == propertyName) { propertyValue = value; } } } queryStr += ") order by part.core desc"; QSqlQuery query; query.prepare(queryStr); query.bindValue(":family",family.toLower().trimmed()); foreach(QString name, params.uniqueKeys()) { query.bindValue(name,params[name].toLower().trimmed()); } QString moduleId; if(query.exec()) { if(query.next()) { moduleId = query.value(0).toString(); //grab the first } //DebugDialog::debug("SQLITE: found: "+moduleId); } else { debugExec("couldn't retrieve part", query); } if(!moduleId.isEmpty()) { m_lastWasExactMatch = true; return moduleId; } else if(closestMatch || !propertyName.isEmpty()) { m_lastWasExactMatch = false; return closestMatchId(family, properties, propertyName, propertyValue); } else { return ___emptyString___; } } else { return ___emptyString___; } } QString SqliteReferenceModel::closestMatchId(const QString &family, const QMultiHash &properties, const QString &propertyName, const QString &propertyValue) { QStringList possibleMatches = getPossibleMatches(family, properties, propertyName, propertyValue); return getClosestMatch(family, properties, possibleMatches); } QStringList SqliteReferenceModel::getPossibleMatches(const QString &family, const QMultiHash &properties, const QString &propertyName, const QString &propertyValue) { Q_UNUSED(properties); QStringList result; QString queryStr = "SELECT moduleID FROM parts part \n" "WHERE part.family = :family AND EXISTS ( \n" "SELECT * FROM properties prop \n" "WHERE prop.part_id = part.id %1 \n" ") "; queryStr = queryStr.arg((propertyName.isEmpty()) ? "" : "AND prop.name = :prop_name AND prop.value = :prop_value "); QSqlQuery query; query.prepare(queryStr); query.bindValue(":family", family.toLower().trimmed()); if (!propertyName.isEmpty()) { query.bindValue(":prop_name", propertyName.toLower().trimmed()); query.bindValue(":prop_value", propertyValue); } if(query.exec()) { while(query.next()) { result << query.value(0).toString(); } //DebugDialog::debug(QString("SQLITE: %1 possible matches found").arg(result.size())); } else { debugExec("couldn't get possible match", query); } return result; } QString SqliteReferenceModel::getClosestMatch(const QString &family, const QMultiHash &properties, QStringList possibleMatches) { int propsInCommonCount = 0; int propsInCommonCountAux = 0; QString result; foreach(QString modId, possibleMatches) { propsInCommonCountAux = countPropsInCommon(family, properties, retrieveModelPart(modId)); if(propsInCommonCountAux > propsInCommonCount) { result = modId; propsInCommonCount = propsInCommonCountAux; } } return result; } int SqliteReferenceModel::countPropsInCommon(const QString &family, const QMultiHash &properties, const ModelPart *part2) { Q_UNUSED(family) if (part2 == NULL) { DebugDialog::debug("countPropsInCommon failure"); return 0; } int result = 0; QMultiHash props2 = part2->properties(); foreach(QString prop, properties.uniqueKeys()) { QStringList values1 = properties.values(prop); QStringList values2 = props2.values(prop); foreach (QString value1, values1) { if (values2.contains(value1)) { result++; } } } return result; } bool SqliteReferenceModel::lastWasExactMatch() { return m_lastWasExactMatch; } bool SqliteReferenceModel::addPartAux(ModelPart * newModel, bool fullLoad) { try { bool result = insertPart(newModel, fullLoad); return result; } catch (const char * msg) { DebugDialog::debug(msg); } catch (const QString & msg) { DebugDialog::debug(msg); } catch (...) { DebugDialog::debug("SqliteReferenceModel::addPartAux failure"); } return false; } bool SqliteReferenceModel::addPart(ModelPart * newModel, bool update) { bool result; if(update && containsModelPart(newModel->moduleID())) { result = updatePart(newModel); } else { result = addPartAux(newModel, false); } return result; } ModelPart * SqliteReferenceModel::addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists) { return PaletteModel::addPart(newPartPath, addToReference, updateIdAlreadyExists); } bool SqliteReferenceModel::removePart(const QString &moduleId) { m_partHash.remove(moduleId); return removePartFromDataBase(moduleId); } bool SqliteReferenceModel::removePartFromDataBase(const QString & moduleId) { qulonglong partId = this->partId(moduleId); if(partId == NO_ID) return false; removePart(partId); removeProperties(partId); removeViewImages(partId); removeConnectors(partId); removeBuses(partId); removeSchematicSubparts(partId); removeTags(partId); return true; } ModelPart * SqliteReferenceModel::reloadPart(const QString & path, const QString & moduleID) { m_partHash.remove(moduleID); ModelPart *modelPart = PaletteModel::loadPart(path, false); if (modelPart == NULL) return modelPart; updatePart(modelPart); return modelPart; } bool SqliteReferenceModel::updatePart(ModelPart * newModel) { if(m_swappingEnabled) { if (removePartFromDataBase(newModel->moduleID())) { return addPartAux(newModel, false); } else { return false; } } else { return false; } } bool SqliteReferenceModel::insertPart(ModelPart * modelPart, bool fullLoad) { DebugModelPart = modelPart; QHash properties = modelPart->properties(); QSqlQuery query; QString fields; QString values; if (fullLoad) { fields = " version, replacedby, fritzingversion, author, title, label, date, description, spice, spicemodel, taxonomy, itemtype, path"; values = " :version, :replacedby, :fritzingversion, :author, :title, :label, :date, :description, :spice, :spicemodel, :taxonomy, :itemtype, :path"; } else { fields = " core"; values = " :core"; } query.prepare(QString("INSERT INTO parts(moduleID, family, %1) VALUES (:moduleID, :family, %2)").arg(fields).arg(values)); query.bindValue(":moduleID", modelPart->moduleID()); query.bindValue(":family", properties.value("family").toLower().trimmed()); if (fullLoad) { QString path = modelPart->path(); QString prefix = FolderUtils::getAppPartsSubFolderPath(""); if (path.startsWith(ResourcePath)) { } else if (path.startsWith(prefix)) { path = path.mid(prefix.count() + 1); // + 1 to remove the beginning "/" } else { bool bail = true; if (modelPart->itemType() == ModelPart::SchematicSubpart) { ModelPartShared * mps = modelPart->modelPartShared(); if (mps && mps->superpart() && mps->superpart()->path().startsWith(prefix)) { bail = false; } } if (bail) { DebugDialog::debug(QString("part path not in parts:%1 %2 %3").arg(path).arg(prefix).arg(fullLoad)); return true; } } query.bindValue(":version", modelPart->version()); query.bindValue(":replacedby", modelPart->replacedby()); query.bindValue(":fritzingversion", modelPart->fritzingVersion()); query.bindValue(":author", modelPart->author()); query.bindValue(":title", modelPart->title()); query.bindValue(":label", modelPart->label()); query.bindValue(":date", modelPart->date()); query.bindValue(":description", modelPart->description()); query.bindValue(":spice", modelPart->spice()); query.bindValue(":spicemodel", modelPart->spiceModel()); query.bindValue(":taxonomy", modelPart->taxonomy()); query.bindValue(":itemtype", (int) modelPart->itemType()); query.bindValue(":path", path); } else { query.bindValue(":core", modelPart->isCore() ? "1" : "0"); } if (query.exec()) { qulonglong id = query.lastInsertId().toULongLong(); modelPart->setDBID(id); foreach (QString prop, properties.keys()) { if (prop == "family") continue; bool result = insertProperty(prop, properties.value(prop), id, modelPart->showInLabel(prop)); if (fullLoad && !result) { m_swappingEnabled = false; } if (!result) { FailurePropertyMessages << tr("property '%1' in part '%2' with id '%3'.") .arg(prop).arg(modelPart->path()).arg(modelPart->moduleID()); } } if (fullLoad) { foreach (QString tag, modelPart->tags()) { insertTag(tag, id); } foreach (ViewImage * viewImage, modelPart->viewImages()) { insertViewImage(viewImage, id); } foreach (Connector * connector, modelPart->connectors().values()) { insertConnector(connector, id); } foreach (Bus * bus, modelPart->buses().values()) { insertBus(bus, id); } ModelPartShared * mps = modelPart->modelPartShared(); if (mps) { foreach (ModelPartShared * sub, mps->subparts()) { insertSubpart(sub, id); } } } } else { debugExec("couldn't insert part", query); FailurePartMessages << tr("part '%1' with id '%2'; possibly because it has no 'family' property.") .arg(modelPart->path()).arg(modelPart->moduleID()); } DebugModelPart = NULL; return true; } bool SqliteReferenceModel::insertProperty(const QString & name, const QString & value, qulonglong id, bool showInLabel) { QSqlQuery query; query.prepare("INSERT INTO properties(name, value, part_id, show_in_label) VALUES (:name, :value, :part_id, :show_in_label)"); query.bindValue(":name", name.toLower().trimmed()); query.bindValue(":value", value); query.bindValue(":part_id", id); query.bindValue(":show_in_label", showInLabel ? 1 : 0); if(!query.exec()) { debugExec("couldn't insert property", query); return false; } else { // qulonglong id = query.lastInsertId().toULongLong(); } return true; } bool SqliteReferenceModel::insertTag(const QString & tag, qulonglong id) { QSqlQuery query; query.prepare("INSERT INTO tags(tag, part_id) VALUES (:tag, :part_id)"); query.bindValue(":tag", tag.toLower().trimmed()); query.bindValue(":part_id", id); if(!query.exec()) { debugExec("couldn't insert tag", query); m_swappingEnabled = false; } else { // qulonglong id = query.lastInsertId().toULongLong(); } return true; } bool SqliteReferenceModel::insertViewImage(const ViewImage * viewImage, qulonglong id) { if (viewImage->image.isEmpty() && viewImage->layers == 0) return true; QSqlQuery query; query.prepare("INSERT INTO viewimages(viewid, image, layers, sticky, flipvertical, fliphorizontal, part_id) " "VALUES (:viewid, :image, :layers, :sticky, :flipvertical, :fliphorizontal, :part_id)"); query.bindValue(":viewid", viewImage->viewID); query.bindValue(":image", viewImage->image); query.bindValue(":layers", viewImage->layers); query.bindValue(":sticky", viewImage->sticky); query.bindValue(":flipvertical", viewImage->canFlipVertical ? 1 : 0); query.bindValue(":fliphorizontal", viewImage->canFlipHorizontal ? 1 : 0); query.bindValue(":part_id", id); if(!query.exec()) { debugExec("couldn't insert viewimage", query); m_swappingEnabled = false; } else { // qulonglong id = query.lastInsertId().toULongLong(); } return true; } bool SqliteReferenceModel::insertBus(const Bus * bus, qulonglong id) { QSqlQuery query; query.prepare("INSERT INTO buses(name, part_id) VALUES (:name, :part_id)"); query.bindValue(":name", bus->id()); query.bindValue(":part_id", id); if(!query.exec()) { debugExec("couldn't insert bus", query); m_swappingEnabled = false; } else { qulonglong bid = query.lastInsertId().toULongLong(); foreach (Connector * connector, bus->connectors()) { insertBusMember(connector, bid); } } return true; } bool SqliteReferenceModel::insertBusMember(const Connector * connector, qulonglong id) { QSqlQuery query; query.prepare("INSERT INTO busmembers(connectorid, bus_id) VALUES (:connectorid, :bus_id)"); query.bindValue(":connectorid", connector->connectorSharedID()); query.bindValue(":bus_id", id); if(!query.exec()) { debugExec("couldn't insert busmember", query); m_swappingEnabled = false; } else { //qulonglong id = query.lastInsertId().toULongLong(); } return true; } bool SqliteReferenceModel::insertConnector(const Connector * connector, qulonglong id) { QSqlQuery query; query.prepare("INSERT INTO connectors(connectorid, type, name, description, replacedby, part_id) VALUES (:connectorid, :type, :name, :description, :replacedby, :part_id)"); query.bindValue(":connectorid", connector->connectorSharedID()); query.bindValue(":type", (int) connector->connectorType()); query.bindValue(":name", connector->connectorSharedName()); query.bindValue(":description", connector->connectorSharedDescription()); query.bindValue(":replacedby", connector->connectorSharedReplacedby()); query.bindValue(":part_id", id); if(!query.exec()) { debugExec("couldn't insert connector", query); m_swappingEnabled = false; } else { qulonglong id = query.lastInsertId().toULongLong(); foreach (SvgIdLayer * layer, connector->svgIdLayers()) { insertConnectorLayer(layer, id); } } return true; } bool SqliteReferenceModel::insertConnectorLayer(const SvgIdLayer * svgIdLayer, qulonglong id) { QSqlQuery query; query.prepare("INSERT INTO connectorLayers(view, layer, svgid, hybrid, terminalid, legid, connector_id) VALUES " "(:view, :layer, :svgid, :hybrid, :terminalid, :legid, :connector_id)"); query.bindValue(":view", svgIdLayer->m_viewID); query.bindValue(":layer", svgIdLayer->m_svgViewLayerID); query.bindValue(":svgid", svgIdLayer->m_svgId); query.bindValue(":hybrid", svgIdLayer->m_hybrid ? 1 : 0); query.bindValue(":terminalid", svgIdLayer->m_terminalId); query.bindValue(":legid", svgIdLayer->m_legId); query.bindValue(":connector_id", id); if(!query.exec()) { debugExec("couldn't insert viewimage", query); m_swappingEnabled = false; } else { // qulonglong id = query.lastInsertId().toULongLong(); } return true; } QStringList SqliteReferenceModel::propValues(const QString &family, const QString &propName, bool distinct) { QStringList retval; QSqlQuery query; query.prepare(QString( "SELECT %1 prop.value FROM properties prop JOIN parts PART ON part.id = prop.part_id \n" "WHERE part.family = :family AND prop.name = :propName ORDER BY prop.value \n" ).arg(distinct ? " DISTINCT ":"") ); query.bindValue(":family",family.toLower().trimmed()); query.bindValue(":propName",propName.toLower().trimmed()); if(query.exec()) { while(query.next()) { QString value = query.value(0).toString(); if (!value.isEmpty()) retval << value; } } else { debugExec("couldn't retrieve values", query); m_swappingEnabled = false; } return retval; } QMultiHash SqliteReferenceModel::allPropValues(const QString &family, const QString &propName) { QMultiHash retval; QSqlQuery query; query.prepare(QString( "SELECT value, moduleID FROM properties prop JOIN parts part ON part.id = prop.part_id \n" "WHERE part.family = :family AND prop.name = :propName\n" ) ); query.bindValue(":family",family.toLower().trimmed()); query.bindValue(":propName",propName.toLower().trimmed()); if(query.exec()) { while(query.next()) { //QSqlRecord record = query.record(); //for (int i = 0; i < record.count(); i++) { // DebugDialog::debug("result " + record.fieldName(i) + " " + record.value(i).toString()); //} QString prop = query.value(0).toString(); if (!prop.isEmpty()) retval.insert(prop, query.value(1).toString()); } } else { debugExec("couldn't retrieve values", query); m_swappingEnabled = false; } return retval; } void SqliteReferenceModel::recordProperty(const QString &name, const QString &value) { DebugDialog::debug(QString("RECORDING PROPERTY %1:%2").arg(name).arg(value)); m_recordedProperties.insert(name,value); } bool SqliteReferenceModel::swapEnabled() const { return m_swappingEnabled; } bool SqliteReferenceModel::containsModelPart(const QString & moduleID) { return partId(moduleID) != NO_ID; } qulonglong SqliteReferenceModel::partId(QString moduleID) { qulonglong partId = NO_ID; QSqlQuery query; query.prepare( "SELECT id FROM parts \n" "WHERE moduleID = :moduleID " ); query.bindValue(":moduleID",moduleID); if(query.exec()) { //DebugDialog::debug("SQLITE: retrieving id: "+moduleID); if(query.next()) { partId = query.value(0).toULongLong(); //grab the first } //DebugDialog::debug(QString("SQLITE: found: %1").arg(partId)); } else { debugExec("couldn't retrieve part", query); } return partId; } bool SqliteReferenceModel::removePart(qulonglong partId) { return removex(partId, "parts", "id"); } bool SqliteReferenceModel::removeProperties(qulonglong partId) { return removex(partId, "properties", "part_id"); } QString SqliteReferenceModel::partTitle(const QString & moduleID) { ModelPart *mp = retrieveModelPart(moduleID); if(mp) { return mp->modelPartShared()->title(); } else { return ___emptyString___; } } void SqliteReferenceModel::killParts() { foreach (ModelPart * modelPart, m_partHash.values()) { delete modelPart; } m_partHash.clear(); } bool SqliteReferenceModel::createProperties(QSqlDatabase & db) { QSqlQuery query = db.exec("CREATE TABLE properties (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,\n" "name TEXT NOT NULL,\n" "value TEXT NOT NULL,\n" "show_in_label INTEGER NOT NULL,\n" "part_id INTEGER NOT NULL" ")"); debugError(query.isActive(), query); return query.isActive(); } bool SqliteReferenceModel::createParts(QSqlDatabase & db, bool fullLoad) { QString extra; if (fullLoad) { extra = "version TEXT,\n" "replacedby TEXT,\n" "fritzingversion TEXT,\n" "author TEXT,\n" "title TEXT,\n" "label TEXT,\n" "date TEXT,\n" "description TEXT,\n" "spice TEXT,\n" "spicemodel TEXT,\n" "taxonomy TEXT,\n" "itemtype INTEGER NOT NULL,\n" "path TEXT\n" ; } else { extra = "core TEXT NOT NULL\n" ; } QString string = QString("CREATE TABLE parts (\n" "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" "moduleID TEXT NOT NULL,\n" "family TEXT NOT NULL,\n" "%1" ")") .arg(extra); QSqlQuery query = db.exec(string); debugError(query.isActive(), query); return query.isActive(); } bool SqliteReferenceModel::insertSubpart(ModelPartShared * mps, qulonglong id) { QSqlQuery query; query.prepare("INSERT INTO schematic_subparts(label, subpart_id, part_id) VALUES (:label, :subpart_id, :part_id)"); query.bindValue(":label", mps->label()); query.bindValue(":subpart_id", mps->subpartID()); query.bindValue(":part_id", id); if(!query.exec()) { debugExec("couldn't insert bus", query); m_swappingEnabled = false; } //else { // qulonglong sid = query.lastInsertId().toULongLong(); //} return true; } void SqliteReferenceModel::createIndexes() { // supposedly faster to add these after the data is inserted QSqlQuery query; bool result = query.exec("CREATE INDEX idx_viewimage_part_id ON viewimages (part_id ASC)"); debugError(result, query); result = query.exec("CREATE INDEX idx_connector_part_id ON connectors (part_id ASC)"); debugError(result, query); result = query.exec("CREATE INDEX idx_connectorlayer_connector_id ON connectorlayers (connector_id ASC)"); debugError(result, query); result = query.exec("CREATE INDEX idx_bus_part_id ON buses (part_id ASC)"); debugError(result, query); result = query.exec("CREATE INDEX idx_busmember_bus_id ON busmembers (bus_id ASC)"); debugError(result, query); result = query.exec("CREATE INDEX idx_schematic_subpart_part_id ON schematic_subparts (part_id ASC)"); debugError(result, query); result = query.exec("CREATE INDEX idx_tag_part_id ON tags (part_id ASC)"); debugError(result, query); } void SqliteReferenceModel::createMoreIndexes(QSqlDatabase & db) { // supposedly faster to add these after the data is inserted QSqlQuery query = db.exec("CREATE INDEX idx_property_name ON properties (name ASC)"); debugError(query.isActive(), query); query = db.exec("CREATE INDEX idx_part_id ON parts (id ASC)"); debugError(query.isActive(), query); query = db.exec("CREATE INDEX idx_part_moduleID ON parts (moduleID ASC)"); debugError(query.isActive(), query); query = db.exec("CREATE INDEX idx_part_family ON parts (family ASC)"); debugError(query.isActive(), query); } void SqliteReferenceModel::setSha(const QString & sha) { m_sha = sha; } const QString & SqliteReferenceModel::sha() const { return m_sha; } bool SqliteReferenceModel::removeViewImages(qulonglong partId) { return removex(partId, "viewimages", "part_id"); } bool SqliteReferenceModel::removeConnectors(qulonglong partId) { QSqlQuery query; query.prepare("SELECT id from connectors WHERE part_id = :id"); query.bindValue(":id",partId); if (query.exec()) { while (query.next()) { qulonglong cid = query.value(0).toULongLong(); removex(cid, "connectorlayers", "connector_id"); } } return removex(partId, "connectors", "part_id"); } bool SqliteReferenceModel::removeBuses(qulonglong partId) { QSqlQuery query; query.prepare("SELECT id from buses WHERE part_id = :id"); query.bindValue(":id",partId); if (query.exec()) { while (query.next()) { qulonglong bid = query.value(0).toULongLong(); removex(bid, "busmembers", "bus_id"); } } return removex(partId, "buses", "part_id"); } bool SqliteReferenceModel::removeSchematicSubparts(qulonglong partId) { return removex(partId, "schematic_subparts", "part_id"); } bool SqliteReferenceModel::removeTags(qulonglong partId) { return removex(partId, "tags", "part_id"); } bool SqliteReferenceModel::removex(qulonglong id, const QString & tableName, const QString & idName) { bool result = true; QSqlQuery query; query.prepare(QString("DELETE FROM %1 WHERE %2 = :id").arg(tableName).arg(idName)); query.bindValue(":id", id); if(query.exec()) { result = true; } else { DebugDialog::debug( "SQLITE: couldn't delete from " + tableName + "\n" "\t "+query.lastQuery()+"\n" "\t ERROR DRIVER: "+query.lastError().driverText()+"\n" "\t ERROR DB: "+query.lastError().databaseText()+"\n" ); result = false; } return result; } fritzing-app-0.9.3b/src/referencemodel/sqlitereferencemodel.h000066400000000000000000000120571272363601300244270ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #ifndef SQLITEREFERENCEMODEL_H_ #define SQLITEREFERENCEMODEL_H_ #include #include #include #include "referencemodel.h" class SqliteReferenceModel : public ReferenceModel { Q_OBJECT public: SqliteReferenceModel(); ~SqliteReferenceModel(); bool loadAll(const QString & databaseName, bool fullLoad, bool dbExists); bool loadFromDB(const QString & databaseName); ModelPart *loadPart(const QString & path, bool update); ModelPart *reloadPart(const QString & path, const QString & moduleID); ModelPart *retrieveModelPart(const QString &moduleID); bool addPart(ModelPart * newModel, bool update); bool updatePart(ModelPart * newModel); ModelPart * addPart(QString newPartPath, bool addToReference, bool updateIdAlreadyExists); bool swapEnabled() const; bool containsModelPart(const QString & moduleID); QString partTitle(const QString & moduleID); QStringList propValues(const QString &family, const QString &propName, bool distinct); QMultiHash allPropValues(const QString &family, const QString &propName); void recordProperty(const QString &name, const QString &value); QString retrieveModuleIdWith(const QString &family, const QString &propertyName, bool closestMatch); QString retrieveModuleId(const QString &family, const QMultiHash &properties, const QString &propertyName, bool closestMatch); bool lastWasExactMatch(); void setSha(const QString & sha); const QString & sha() const; protected: void initParts(bool dbExists); void killParts(); bool addPartAux(ModelPart * newModel, bool fullLoad); QString closestMatchId(const QString &family, const QMultiHash &properties, const QString &propertyName, const QString &propertyValue); QStringList getPossibleMatches(const QString &family, const QMultiHash &properties, const QString &propertyName, const QString &propertyValue); QString getClosestMatch(const QString &family, const QMultiHash &properties, QStringList possibleMatches); int countPropsInCommon(const QString &family, const QMultiHash &properties, const ModelPart *part2); bool createDatabase(const QString & databaseName, bool fullLoad); void deleteConnection(); bool insertPart(ModelPart *, bool fullLoad); bool insertProperty(const QString & name, const QString & value, qulonglong id, bool showInLabel); bool insertTag(const QString & tag, qulonglong id); bool insertViewImage(const struct ViewImage *, qulonglong id); bool insertConnector(const class Connector *, qulonglong id); bool insertConnectorLayer(const class SvgIdLayer *, qulonglong id); // connector db id bool insertBus(const Bus *, qulonglong id); bool insertBusMember(const Connector *, qulonglong id); qulonglong partId(QString moduleID); bool removePart(qulonglong partId); bool removeProperties(qulonglong partId); bool loadFromDB(QSqlDatabase & keep_db, QSqlDatabase & db); bool createProperties(QSqlDatabase &); bool createParts(QSqlDatabase &, bool fullLoad); bool insertSubpart(ModelPartShared *, qulonglong id); bool insertSubpartConnector(const ConnectorShared * cs, qulonglong id); void createIndexes(); void createMoreIndexes(QSqlDatabase &); bool removeViewImages(qulonglong partId); bool removeConnectors(qulonglong partId); bool removeBuses(qulonglong partId); bool removeSchematicSubparts(qulonglong partId); bool removeTags(qulonglong partId); bool removex(qulonglong id, const QString & tableName, const QString & idName); bool removePart(const QString & moduleId); bool removePartFromDataBase(const QString & moduleId); protected: volatile bool m_swappingEnabled; volatile bool m_lastWasExactMatch; volatile bool m_keepGoing; bool m_init; QSqlDatabase m_database; QMultiHash m_recordedProperties; QString m_sha; }; #endif /* SQLITEREFERENCEMODEL_H_ */ fritzing-app-0.9.3b/src/routingstatus.h000066400000000000000000000031141272363601300201740ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify\ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ROUTINGSTATUS_H #define ROUTINGSTATUS_H #include "items/itembase.h" struct RoutingStatus { int m_netCount; int m_netRoutedCount; int m_connectorsLeftToRoute; int m_jumperItemCount; public: void zero() { m_netCount = m_netRoutedCount = m_connectorsLeftToRoute = m_jumperItemCount = 0; } bool operator!=(const RoutingStatus &other) const { return (m_netCount != other.m_netCount) || (m_netRoutedCount != other.m_netRoutedCount) || (m_connectorsLeftToRoute != other.m_connectorsLeftToRoute) || (m_jumperItemCount != other.m_jumperItemCount); } }; #endif // ROUTINGSTATUS_H fritzing-app-0.9.3b/src/sketch/000077500000000000000000000000001272363601300163525ustar00rootroot00000000000000fritzing-app-0.9.3b/src/sketch/breadboardsketchwidget.cpp000066400000000000000000000202661272363601300235570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #include "breadboardsketchwidget.h" #include "../debugdialog.h" #include "../items/virtualwire.h" #include "../items/resizableboard.h" #include "../connectors/connectoritem.h" #include "../items/moduleidnames.h" #include "../waitpushundostack.h" #include static const double WireHoverStrokeFactor = 4.0; BreadboardSketchWidget::BreadboardSketchWidget(ViewLayer::ViewID viewID, QWidget *parent) : SketchWidget(viewID, parent) { m_shortName = QObject::tr("bb"); m_viewName = QObject::tr("Breadboard View"); initBackgroundColor(); } void BreadboardSketchWidget::setWireVisible(Wire * wire) { bool visible = !(wire->getTrace()); wire->setVisible(visible); wire->setEverVisible(visible); //wire->setVisible(true); // for debugging } bool BreadboardSketchWidget::collectFemaleConnectees(ItemBase * itemBase, QSet & items) { return itemBase->collectFemaleConnectees(items); } bool BreadboardSketchWidget::checkUnder() { return true; }; void BreadboardSketchWidget::findConnectorsUnder(ItemBase * item) { item->findConnectorsUnder(); } void BreadboardSketchWidget::addViewLayers() { setViewLayerIDs(ViewLayer::Breadboard, ViewLayer::BreadboardWire, ViewLayer::Breadboard, ViewLayer::BreadboardRuler, ViewLayer::BreadboardNote); addViewLayersAux(ViewLayer::layersForView(ViewLayer::BreadboardView), ViewLayer::layersForViewFromBelow(ViewLayer::BreadboardView)); } bool BreadboardSketchWidget::disconnectFromFemale(ItemBase * item, QHash & savedItems, ConnectorPairHash & connectorHash, bool doCommand, bool rubberBandLegEnabled, QUndoCommand * parentCommand) { // if item is attached to a virtual wire or a female connector in breadboard view // then disconnect it // at the moment, I think this doesn't apply to other views bool result = false; foreach (ConnectorItem * fromConnectorItem, item->cachedConnectorItems()) { if (rubberBandLegEnabled && fromConnectorItem->hasRubberBandLeg()) continue; foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { if (rubberBandLegEnabled && toConnectorItem->hasRubberBandLeg()) continue; if (toConnectorItem->connectorType() == Connector::Female) { if (savedItems.keys().contains(toConnectorItem->attachedTo()->layerKinChief()->id())) { // the thing we're connected to is also moving, so don't disconnect continue; } result = true; fromConnectorItem->tempRemove(toConnectorItem, true); toConnectorItem->tempRemove(fromConnectorItem, true); if (doCommand) { extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::NewBottom, false, parentCommand); } connectorHash.insert(fromConnectorItem, toConnectorItem); } } } return result; } BaseCommand::CrossViewType BreadboardSketchWidget::wireSplitCrossView() { return BaseCommand::CrossView; } bool BreadboardSketchWidget::canDropModelPart(ModelPart * modelPart) { if (!SketchWidget::canDropModelPart(modelPart)) return false; if (Board::isBoard(modelPart)) { return matchesLayer(modelPart); } switch (modelPart->itemType()) { case ModelPart::Logo: if (modelPart->moduleID().contains("breadboard", Qt::CaseInsensitive)) return true; case ModelPart::Symbol: case ModelPart::SchematicSubpart: case ModelPart::Jumper: case ModelPart::CopperFill: case ModelPart::Hole: case ModelPart::Via: return false; default: if (modelPart->moduleID().endsWith(ModuleIDNames::SchematicFrameModuleIDName)) return false; if (modelPart->moduleID().endsWith(ModuleIDNames::PadModuleIDName)) return false; if (modelPart->moduleID().endsWith(ModuleIDNames::CopperBlockerModuleIDName)) return false; return true; } } void BreadboardSketchWidget::initWire(Wire * wire, int penWidth) { if (wire->getRatsnest()) { // handle elsewhere return; } wire->setPenWidth(penWidth - 2, this, (penWidth - 2) * WireHoverStrokeFactor); wire->setColorString("blue", 1.0, false); } const QString & BreadboardSketchWidget::traceColor(ViewLayer::ViewLayerPlacement) { if (!m_lastColorSelected.isEmpty()) return m_lastColorSelected; static QString color = "blue"; return color; } double BreadboardSketchWidget::getTraceWidth() { // TODO: dig this constant out of wire.svg or somewhere else... return 2.0; } void BreadboardSketchWidget::getLabelFont(QFont & font, QColor & color, ItemBase *) { font.setFamily("Droid Sans"); font.setPointSize(9); font.setBold(false); font.setItalic(false); color.setAlpha(255); color.setRgb(0); } void BreadboardSketchWidget::setNewPartVisible(ItemBase * itemBase) { switch (itemBase->itemType()) { case ModelPart::Symbol: case ModelPart::SchematicSubpart: case ModelPart::Jumper: case ModelPart::CopperFill: case ModelPart::Logo: if (itemBase->moduleID().contains("breadboard", Qt::CaseInsensitive)) return; case ModelPart::Hole: case ModelPart::Via: itemBase->setVisible(false); itemBase->setEverVisible(false); return; default: if (itemBase->moduleID().endsWith(ModuleIDNames::SchematicFrameModuleIDName) || itemBase->moduleID().endsWith(ModuleIDNames::PadModuleIDName) || itemBase->moduleID().endsWith(ModuleIDNames::PowerModuleIDName)) { itemBase->setVisible(false); itemBase->setEverVisible(false); return; } break; } } bool BreadboardSketchWidget::canDisconnectAll() { return false; } bool BreadboardSketchWidget::ignoreFemale() { return false; } double BreadboardSketchWidget::defaultGridSizeInches() { return 0.1; } ViewLayer::ViewLayerID BreadboardSketchWidget::getLabelViewLayerID(ItemBase *) { return ViewLayer::BreadboardLabel; } void BreadboardSketchWidget::addDefaultParts() { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(QPointF(0, 0)); ModelPart * modelPart = referenceModel()->retrieveModelPart(ModuleIDNames::FullPlusBreadboardModuleIDName); m_addedDefaultPart = addItem(modelPart, defaultViewLayerPlacement(modelPart), BaseCommand::CrossView, viewGeometry, newID, -1, NULL); m_addDefaultParts = true; // have to put this off until later, because positioning the item doesn't work correctly until the view is visible } void BreadboardSketchWidget::showEvent(QShowEvent * event) { SketchWidget::showEvent(event); if (m_addDefaultParts && (m_addedDefaultPart != NULL)) { m_addDefaultParts = false; QSizeF partSize = m_addedDefaultPart->size(); QSizeF vpSize = this->viewport()->size(); //if (vpSize.height() < helpSize.height() + 50 + partSize.height()) { //vpSize.setWidth(vpSize.width() - verticalScrollBar()->width()); //} QPointF p; p.setX((int) ((vpSize.width() - partSize.width()) / 2.0)); p.setY((int) ((vpSize.height() - partSize.height()) / 2.0)); QPointF q = mapToScene(p.toPoint()); m_addedDefaultPart->setPos(q); alignOneToGrid(m_addedDefaultPart); QTimer::singleShot(10, this, SLOT(vScrollToZero())); } } double BreadboardSketchWidget::getWireStrokeWidth(Wire *, double wireWidth) { return wireWidth * WireHoverStrokeFactor; } void BreadboardSketchWidget::getBendpointWidths(Wire * wire, double width, double & bendpointWidth, double & bendpoint2Width, bool & negativeOffsetRect) { Q_UNUSED(wire); Q_UNUSED(width); bendpoint2Width = bendpointWidth = -1; negativeOffsetRect = true; } fritzing-app-0.9.3b/src/sketch/breadboardsketchwidget.h000066400000000000000000000043601272363601300232210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef BREADBOARDSKETCHWIDGET_H #define BREADBOARDSKETCHWIDGET_H #include "sketchwidget.h" class BreadboardSketchWidget : public SketchWidget { Q_OBJECT public: BreadboardSketchWidget(ViewLayer::ViewID, QWidget *parent=0); void addViewLayers(); void initWire(Wire *, int penWidth); bool canDisconnectAll(); bool ignoreFemale(); void addDefaultParts(); void showEvent(QShowEvent * event); double getWireStrokeWidth(Wire *, double wireWidth); void getBendpointWidths(class Wire *, double w, double & w1, double & w2, bool & negativeOffsetRect); protected: void setWireVisible(Wire * wire); bool collectFemaleConnectees(ItemBase *, QSet &); void findConnectorsUnder(ItemBase * item); bool checkUnder(); bool disconnectFromFemale(ItemBase * item, QHash & savedItems, ConnectorPairHash &, bool doCommand, bool rubberBandLegEnabled, QUndoCommand * parentCommand); BaseCommand::CrossViewType wireSplitCrossView(); bool canDropModelPart(ModelPart * modelPart); void getLabelFont(QFont &, QColor &, ItemBase *); void setNewPartVisible(ItemBase *); double defaultGridSizeInches(); ViewLayer::ViewLayerID getLabelViewLayerID(ItemBase *); double getTraceWidth(); const QString & traceColor(ViewLayer::ViewLayerPlacement); }; #endif fritzing-app-0.9.3b/src/sketch/fgraphicsscene.cpp000066400000000000000000000054501272363601300220460ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "fgraphicsscene.h" #include "../items/paletteitembase.h" #include "../items/wire.h" #include "../connectors/connectoritem.h" #include "../sketch/infographicsview.h" #include FGraphicsScene::FGraphicsScene( QObject * parent) : QGraphicsScene(parent) { m_displayHandles = true; //setItemIndexMethod(QGraphicsScene::NoIndex); } void FGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent) { // TODO: how do we get a QTransform? QGraphicsItem * item = this->itemAt(helpEvent->scenePos(), QTransform()); if (item == NULL) return; QString text; QPoint point; ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem) { connectorItem->updateTooltip(); } } else { itemBase->updateTooltip(); } if (!item->toolTip().isEmpty()) { text = item->toolTip(); point = helpEvent->screenPos(); } // Show or hide the tooltip QToolTip::showText(point, text); } void FGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent) { m_lastContextMenuPos = contextMenuEvent->scenePos(); QGraphicsScene::contextMenuEvent(contextMenuEvent); } QPointF FGraphicsScene::lastContextMenuPos() { return m_lastContextMenuPos; } void FGraphicsScene::setDisplayHandles(bool displayHandles) { m_displayHandles = displayHandles; } bool FGraphicsScene::displayHandles() { return m_displayHandles; } QList FGraphicsScene::lockedSelectedItems() { QList items; foreach (QGraphicsItem * gitem, this->selectedItems()) { ItemBase *itemBase = dynamic_cast(gitem); if (itemBase == NULL) continue; if (itemBase->moveLock()) { items.append(itemBase); } } return items; } fritzing-app-0.9.3b/src/sketch/fgraphicsscene.h000066400000000000000000000030701272363601300215070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FGRAPHICSSCENE_H #define FGRAPHICSSCENE_H #include #include #include #include "../items/itembase.h" class FGraphicsScene : public QGraphicsScene { Q_OBJECT public: FGraphicsScene(QObject * parent = 0); void helpEvent(QGraphicsSceneHelpEvent *); void contextMenuEvent(QGraphicsSceneContextMenuEvent *); QPointF lastContextMenuPos(); void setDisplayHandles(bool); bool displayHandles(); QList lockedSelectedItems(); protected: QPointF m_lastContextMenuPos; bool m_displayHandles; }; #endif fritzing-app-0.9.3b/src/sketch/infographicsview.cpp000066400000000000000000000276051272363601300224370ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "infographicsview.h" #include "../debugdialog.h" #include "../infoview/htmlinfoview.h" #include static LayerHash ViewLayers; InfoGraphicsView::InfoGraphicsView( QWidget * parent ) : ZoomableGraphicsView(parent) { m_infoView = NULL; m_boardLayers = 1; m_hoverEnterMode = m_hoverEnterConnectorMode = false; m_smdOrientation = Qt::Vertical; } void InfoGraphicsView::viewItemInfo(ItemBase * item) { if (m_infoView == NULL) return; m_infoView->viewItemInfo(this, item ? item->layerKinChief() : item, swappingEnabled(item)); } void InfoGraphicsView::hoverEnterItem(QGraphicsSceneHoverEvent * event, ItemBase * itemBase) { if (m_infoView == NULL) return; if (event->modifiers() & Qt::ShiftModifier || itemBase->viewID() == ViewLayer::IconView) { m_hoverEnterMode = true; m_infoView->hoverEnterItem(this, event, itemBase ? itemBase->layerKinChief() : itemBase, swappingEnabled(itemBase)); } } void InfoGraphicsView::hoverLeaveItem(QGraphicsSceneHoverEvent * event, ItemBase * itemBase) { if (m_infoView == NULL) return; if (m_hoverEnterMode) { m_hoverEnterMode = false; m_infoView->hoverLeaveItem(this, event, itemBase ? itemBase->layerKinChief() : itemBase); } } void InfoGraphicsView::hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { if (m_infoView == NULL) return; if (event->modifiers() & Qt::ShiftModifier) { m_hoverEnterConnectorMode = true; m_infoView->hoverEnterConnectorItem(this, event, item, swappingEnabled(item->attachedTo())); } } void InfoGraphicsView::hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item){ if (m_infoView == NULL) return; if (m_hoverEnterConnectorMode) { m_hoverEnterConnectorMode = false; m_infoView->hoverLeaveConnectorItem(this, event, item); } } void InfoGraphicsView::setInfoView(HtmlInfoView * infoView) { m_infoView = infoView; } HtmlInfoView * InfoGraphicsView::infoView() { return m_infoView; } void InfoGraphicsView::mousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *) { } void InfoGraphicsView::hidePartLabel(ItemBase * item) { Q_UNUSED(item); } void InfoGraphicsView::partLabelChanged(ItemBase * item, const QString &oldText, const QString & newText) { Q_UNUSED(item); Q_UNUSED(oldText); Q_UNUSED(newText); } void InfoGraphicsView::noteChanged(ItemBase * item, const QString &oldText, const QString & newText, QSizeF oldSize, QSizeF newSize) { Q_UNUSED(item); Q_UNUSED(oldText); Q_UNUSED(newText); Q_UNUSED(oldSize); Q_UNUSED(newSize); } QGraphicsItem *InfoGraphicsView::selectedAux() { QList selItems = scene()->selectedItems(); if(selItems.size() != 1) { return NULL; } else { return selItems[0]; } } void InfoGraphicsView::partLabelMoved(ItemBase * itemBase, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset) { Q_UNUSED(itemBase); Q_UNUSED(oldPos); Q_UNUSED(oldOffset); Q_UNUSED(newPos); Q_UNUSED(newOffset); } void InfoGraphicsView::rotateFlipPartLabel(ItemBase * itemBase, double degrees, Qt::Orientations flipDirection) { Q_UNUSED(itemBase); Q_UNUSED(degrees); Q_UNUSED(flipDirection); } void InfoGraphicsView::noteSizeChanged(ItemBase * itemBase, const QSizeF & oldSize, const QSizeF & newSize) { Q_UNUSED(itemBase); Q_UNUSED(oldSize); Q_UNUSED(newSize); } bool InfoGraphicsView::spaceBarIsPressed() { return false; } InfoGraphicsView * InfoGraphicsView::getInfoGraphicsView(QGraphicsItem * item) { if (item == NULL) return NULL; QGraphicsScene * scene = item->scene(); if (scene == NULL) return NULL; return dynamic_cast(scene->parent()); } void InfoGraphicsView::initWire(Wire *, int penWidth) { Q_UNUSED(penWidth); } void InfoGraphicsView::getBendpointWidths(class Wire *, double w, double & w1, double & w2, bool & negativeOffsetRect) { Q_UNUSED(w); Q_UNUSED(w1); Q_UNUSED(w2); Q_UNUSED(negativeOffsetRect); } void InfoGraphicsView::getLabelFont(QFont &, QColor &, ItemBase *) { } double InfoGraphicsView::getLabelFontSizeTiny() { return 5; } double InfoGraphicsView::getLabelFontSizeSmall() { return 7; } double InfoGraphicsView::getLabelFontSizeMedium() { return 9; } double InfoGraphicsView::getLabelFontSizeLarge() { return 14; } bool InfoGraphicsView::hasBigDots() { return false; } void InfoGraphicsView::setVoltage(double v, bool doEmit) { if (doEmit) { emit setVoltageSignal(v, false); } } void InfoGraphicsView::resizeBoard(double w, double h, bool doEmit) { Q_UNUSED(w); Q_UNUSED(h); Q_UNUSED(doEmit); } void InfoGraphicsView::setResistance(QString resistance, QString pinSpacing) { Q_UNUSED(resistance); Q_UNUSED(pinSpacing); } void InfoGraphicsView::setProp(ItemBase * item, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, bool redraw) { Q_UNUSED(item); Q_UNUSED(prop); Q_UNUSED(trProp); Q_UNUSED(oldValue); Q_UNUSED(newValue); Q_UNUSED(redraw); } void InfoGraphicsView::setHoleSize(ItemBase * item, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, QRectF & oldRect, QRectF & newRect, bool redraw) { Q_UNUSED(item); Q_UNUSED(prop); Q_UNUSED(trProp); Q_UNUSED(oldValue); Q_UNUSED(newValue); Q_UNUSED(oldRect); Q_UNUSED(newRect); Q_UNUSED(redraw); } void InfoGraphicsView::changeWireWidthMils(const QString newWidth) { Q_UNUSED(newWidth); } void InfoGraphicsView::changeWireColor(const QString newColor) { Q_UNUSED(newColor); } void InfoGraphicsView::swap(const QString & family, const QString & prop, QMap & propsMap, ItemBase * itemBase) { emit swapSignal(family, prop, propsMap, itemBase); } void InfoGraphicsView::setInstanceTitle(long id, const QString & oldTitle, const QString & newTitle, bool isUndoable, bool doEmit) { Q_UNUSED(id); Q_UNUSED(newTitle); Q_UNUSED(oldTitle); Q_UNUSED(isUndoable); Q_UNUSED(doEmit); } LayerHash & InfoGraphicsView::viewLayers() { return ViewLayers; } void InfoGraphicsView::loadLogoImage(ItemBase *, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName) { Q_UNUSED(oldSvg); Q_UNUSED(oldAspectRatio); Q_UNUSED(oldFilename); Q_UNUSED(newFilename); Q_UNUSED(addName); } void InfoGraphicsView::setNoteFocus(QGraphicsItem * item, bool inFocus) { Q_UNUSED(item); Q_UNUSED(inFocus); } void InfoGraphicsView::setBoardLayers(int layers, bool redraw) { Q_UNUSED(redraw); m_boardLayers = layers; } int InfoGraphicsView::boardLayers() { return m_boardLayers; } VirtualWire * InfoGraphicsView::makeOneRatsnestWire(ConnectorItem * source, ConnectorItem * dest, bool routed, QColor color, bool force) { Q_UNUSED(source); Q_UNUSED(dest); Q_UNUSED(routed); Q_UNUSED(color); Q_UNUSED(force); return NULL; } void InfoGraphicsView::getRatsnestColor(QColor & color) { Q_UNUSED(color); } void InfoGraphicsView::changeBus(ItemBase *, bool connect, const QString & oldBus, const QString & newBus, QList &, const QString & message, const QString & oldLayout, const QString & newLayout) { Q_UNUSED(connect); Q_UNUSED(oldBus); Q_UNUSED(newBus); Q_UNUSED(message); Q_UNUSED(oldLayout); Q_UNUSED(newLayout); } const QString & InfoGraphicsView::filenameIf() { return ___emptyString___; } QString InfoGraphicsView::generateCopperFillUnit(ItemBase * itemBase, QPointF whereToStart) { Q_UNUSED(itemBase); Q_UNUSED(whereToStart); return ""; } void InfoGraphicsView::prepLegSelection(ItemBase *) { } void InfoGraphicsView::prepLegBendpointMove(ConnectorItem * from, int index, QPointF oldPos, QPointF newPos, ConnectorItem * to, bool changeConnections) { Q_UNUSED(from); Q_UNUSED(index); Q_UNUSED(oldPos); Q_UNUSED(newPos); Q_UNUSED(to); Q_UNUSED(changeConnections); } void InfoGraphicsView::prepLegCurveChange(ConnectorItem * from, int index, const class Bezier *, const class Bezier *, bool triggerFirstTime) { Q_UNUSED(from); Q_UNUSED(index); Q_UNUSED(triggerFirstTime); } void InfoGraphicsView::prepLegBendpointChange(ConnectorItem *, int oldCount, int newCount, int index, QPointF, const class Bezier *, const class Bezier *, const class Bezier *, bool triggerFirstTime) { Q_UNUSED(triggerFirstTime); Q_UNUSED(index); Q_UNUSED(oldCount); Q_UNUSED(newCount); } double InfoGraphicsView::getWireStrokeWidth(Wire *, double wireWidth) { return wireWidth; } bool InfoGraphicsView::curvyWiresIndicated(Qt::KeyboardModifiers) { return true; } void InfoGraphicsView::triggerRotate(ItemBase *, double degrees) { Q_UNUSED(degrees); } void InfoGraphicsView::changePinLabels(ItemBase * itemBase, bool singleRow) { Q_UNUSED(itemBase); Q_UNUSED(singleRow); } void InfoGraphicsView::renamePins(ItemBase *, const QStringList & oldLabels, const QStringList & newLabels, bool singleRow) { Q_UNUSED(oldLabels); Q_UNUSED(newLabels); Q_UNUSED(singleRow); } ViewGeometry::WireFlag InfoGraphicsView::getTraceFlag() { return ViewGeometry::NoFlag; } void InfoGraphicsView::setAnyInRotation() { } void InfoGraphicsView::setActiveWire(Wire * wire) { emit setActiveWireSignal(wire); } void InfoGraphicsView::setActiveConnectorItem(ConnectorItem * connectorItem) { emit setActiveConnectorItemSignal(connectorItem); } void InfoGraphicsView::resolveTemporary(bool, ItemBase *) { } void InfoGraphicsView::newWire(Wire * wire) { bool succeeded = connect(wire, SIGNAL(wireChangedSignal(Wire*, const QLineF & , const QLineF & , QPointF, QPointF, ConnectorItem *, ConnectorItem *) ), this, SLOT(wireChangedSlot(Wire*, const QLineF & , const QLineF & , QPointF, QPointF, ConnectorItem *, ConnectorItem *)), Qt::DirectConnection); // DirectConnection means call the slot directly like a subroutine, without waiting for a thread or queue succeeded = connect(wire, SIGNAL(wireChangedCurveSignal(Wire*, const Bezier *, const Bezier *, bool)), this, SLOT(wireChangedCurveSlot(Wire*, const Bezier *, const Bezier *, bool)), Qt::DirectConnection); // DirectConnection means call the slot directly like a subroutine, without waiting for a thread or queue succeeded = succeeded && connect(wire, SIGNAL(wireSplitSignal(Wire*, QPointF, QPointF, const QLineF & )), this, SLOT(wireSplitSlot(Wire*, QPointF, QPointF, const QLineF & ))); succeeded = succeeded && connect(wire, SIGNAL(wireJoinSignal(Wire*, ConnectorItem *)), this, SLOT(wireJoinSlot(Wire*, ConnectorItem*))); if (!succeeded) { DebugDialog::debug("wire signal connect failed"); } emit newWireSignal(wire); } void InfoGraphicsView::setSMDOrientation(Qt::Orientations orientation) { m_smdOrientation = orientation; } Qt::Orientations InfoGraphicsView::smdOrientation() { return m_smdOrientation; } void InfoGraphicsView::moveItem(ItemBase *, double x, double y) { Q_UNUSED(x); Q_UNUSED(y); } void InfoGraphicsView::updateRotation(ItemBase * itemBase) { if (m_infoView) m_infoView->updateRotation(itemBase); } void InfoGraphicsView::rotateX(double degrees, bool rubberBandLegEnabled, ItemBase * originatingItem) { Q_UNUSED(degrees); Q_UNUSED(rubberBandLegEnabled); Q_UNUSED(originatingItem); } fritzing-app-0.9.3b/src/sketch/infographicsview.h000066400000000000000000000151201272363601300220710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6715 $: $Author: irascibl@gmail.com $: $Date: 2012-12-17 11:27:32 +0100 (Mo, 17. Dez 2012) $ ********************************************************************/ #ifndef INFOGRAPHICSVIEW_H #define INFOGRAPHICSVIEW_H #include #include #include #include #include "../items/itembase.h" #include "zoomablegraphicsview.h" class InfoGraphicsView : public ZoomableGraphicsView { Q_OBJECT public: InfoGraphicsView(QWidget* parent = 0); virtual void viewItemInfo(ItemBase *); virtual void hoverEnterItem(QGraphicsSceneHoverEvent * event, ItemBase *); virtual void hoverLeaveItem(QGraphicsSceneHoverEvent * event, ItemBase *); void updateRotation(ItemBase *); virtual bool swappingEnabled(ItemBase *) = 0; virtual void hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem *); virtual void hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem *); void setInfoView(class HtmlInfoView *); class HtmlInfoView * infoView(); virtual void mousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); virtual void hidePartLabel(ItemBase * item); virtual void partLabelMoved(ItemBase *, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset); virtual void rotateFlipPartLabel(ItemBase *, double degrees, Qt::Orientations flipDirection); virtual void noteSizeChanged(ItemBase * itemBase, const QSizeF & oldSize, const QSizeF & newSize); virtual bool spaceBarIsPressed(); virtual void initWire(class Wire *, int penWidth); virtual void setIgnoreSelectionChangeEvents(bool) {} virtual void getBendpointWidths(class Wire *, double w, double & w1, double & w2, bool & negativeOffsetRect); virtual void getLabelFont(QFont &, QColor &, ItemBase *); virtual double getLabelFontSizeTiny(); virtual double getLabelFontSizeSmall(); virtual double getLabelFontSizeMedium(); virtual double getLabelFontSizeLarge(); virtual bool hasBigDots(); virtual LayerHash & viewLayers(); virtual void loadLogoImage(ItemBase *, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName); virtual void setNoteFocus(QGraphicsItem *, bool inFocus); int boardLayers(); virtual void setBoardLayers(int, bool redraw); virtual class VirtualWire * makeOneRatsnestWire(ConnectorItem * source, ConnectorItem * dest, bool routed, QColor, bool force); virtual void getRatsnestColor(QColor &); virtual void changeBus(ItemBase *, bool connect, const QString & oldBus, const QString & newBus, QList &, const QString & message, const QString & oldLayout, const QString & newLayout); virtual const QString & filenameIf(); virtual QString generateCopperFillUnit(ItemBase * itemBase, QPointF whereToStart); virtual void prepLegBendpointMove(ConnectorItem * from, int index, QPointF oldPos, QPointF newPos, ConnectorItem * to, bool changeConnections); virtual void prepLegCurveChange(ConnectorItem * from, int index, const class Bezier * oldB, const class Bezier * newB, bool triggerFirstTime); virtual void prepLegBendpointChange(ConnectorItem * from, int oldCount, int newCount, int index, QPointF pos, const class Bezier *, const class Bezier *, const class Bezier *, bool triggerFirstTime); virtual void prepLegSelection(ItemBase *); virtual double getWireStrokeWidth(Wire *, double wireWidth); virtual bool curvyWiresIndicated(Qt::KeyboardModifiers); virtual void triggerRotate(ItemBase *, double degrees); virtual void changePinLabels(ItemBase *, bool singleRow); virtual void renamePins(ItemBase *, const QStringList & oldLabels, const QStringList & newLabels, bool singleRow); virtual ViewGeometry::WireFlag getTraceFlag(); virtual void setAnyInRotation(); virtual void partLabelChanged(ItemBase *, const QString &oldText, const QString & newText); virtual void noteChanged(ItemBase *, const QString &oldText, const QString & newText, QSizeF oldSize, QSizeF newSize); virtual void setResistance(QString resistance, QString pinSpacing); virtual void setProp(ItemBase *, const QString & propName, const QString & translatedPropName, const QString & oldValue, const QString & newValue, bool redraw); virtual void setHoleSize(ItemBase *, const QString & propName, const QString & translatedPropName, const QString & oldValue, const QString & newValue, QRectF & oldRect, QRectF & newRect, bool redraw); virtual void changeWireWidthMils(const QString newWidth); virtual void changeWireColor(const QString newColor); virtual void swap(const QString & family, const QString & prop, QMap & propsMap, ItemBase *); virtual void resolveTemporary(bool, ItemBase *); virtual void newWire(Wire *); void setActiveWire(Wire *); void setActiveConnectorItem(ConnectorItem *); void setSMDOrientation(Qt::Orientations); Qt::Orientations smdOrientation(); virtual void moveItem(ItemBase *, double x, double y); virtual void rotateX(double degrees, bool rubberBandLegEnabled, ItemBase * originatingItem); public slots: virtual void setVoltage(double, bool doEmit); virtual void resizeBoard(double w, double h, bool doEmit); virtual void setInstanceTitle(long id, const QString & oldTitle, const QString & newTitle, bool isUndoable, bool doEmit); signals: void setVoltageSignal(double, bool doEmit); void swapSignal(const QString & family, const QString & prop, QMap & propsMap, ItemBase *); void changePinLabelsSignal(ItemBase *, bool singleRow); void setActiveWireSignal(Wire *); void setActiveConnectorItemSignal(ConnectorItem *); void newWireSignal(Wire *); public: static InfoGraphicsView * getInfoGraphicsView(QGraphicsItem *); protected: QGraphicsItem *selectedAux(); class HtmlInfoView *m_infoView; int m_boardLayers; bool m_hoverEnterMode; bool m_hoverEnterConnectorMode; Qt::Orientations m_smdOrientation; }; #endif fritzing-app-0.9.3b/src/sketch/pcbsketchwidget.cpp000066400000000000000000003005541272363601300222370ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6998 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #include #include "pcbsketchwidget.h" #include "../debugdialog.h" #include "../items/tracewire.h" #include "../items/virtualwire.h" #include "../items/resizableboard.h" #include "../items/pad.h" #include "../waitpushundostack.h" #include "../connectors/connectoritem.h" #include "../items/moduleidnames.h" #include "../items/partlabel.h" #include "../fsvgrenderer.h" #include "../autoroute/autorouteprogressdialog.h" #include "../autoroute/drc.h" #include "../items/groundplane.h" #include "../items/jumperitem.h" #include "../utils/autoclosemessagebox.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/folderutils.h" #include "../processeventblocker.h" #include "../autoroute/cmrouter/tileutils.h" #include "../autoroute/cmrouter/cmrouter.h" #include "../autoroute/panelizer.h" #include "../autoroute/autoroutersettingsdialog.h" #include "../svg/groundplanegenerator.h" #include "../items/logoitem.h" #include "../dialogs/groundfillseeddialog.h" #include "../version/version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include ///////////////////////////////////////////////////// static const int MAX_INT = std::numeric_limits::max(); static const double StrokeWidthIncrement = 50; static QString PCBTraceColor1 = "trace1"; static QString PCBTraceColor = "trace"; QSizeF PCBSketchWidget::m_jumperItemSize = QSizeF(0, 0); struct DistanceThing { int distance; bool fromConnector0; }; QHash distances; bool bySize(QList * l1, QList * l2) { return l1->count() >= l2->count(); } bool distanceLessThan(ConnectorItem * end0, ConnectorItem * end1) { if (end0->connectorType() == Connector::Male && end1->connectorType() == Connector::Female) { return true; } if (end1->connectorType() == Connector::Male && end0->connectorType() == Connector::Female) { return false; } DistanceThing * dt0 = distances.value(end0, NULL); DistanceThing * dt1 = distances.value(end1, NULL); if (dt0 && dt1) { return dt0->distance <= dt1->distance; } if (dt0) { return true; } if (dt1) { return false; } return true; } ////////////////////////////////////////////////////// const char * PCBSketchWidget::FakeTraceProperty = "FakeTrace"; PCBSketchWidget::PCBSketchWidget(ViewLayer::ViewID viewID, QWidget *parent) : SketchWidget(viewID, parent) { m_rolloverQuoteDialog = NULL; m_requestQuoteTimer.setSingleShot(true); m_requestQuoteTimer.setInterval(100); connect(&m_requestQuoteTimer, SIGNAL(timeout()), this, SLOT(requestQuoteNow())); m_resizingBoard = NULL; m_resizingJumperItem = NULL; m_viewName = QObject::tr("PCB View"); m_shortName = QObject::tr("pcb"); initBackgroundColor(); m_routingStatus.zero(); m_cleanType = noClean; } void PCBSketchWidget::setWireVisible(Wire * wire) { bool visible = wire->getRatsnest() || (wire->isTraceType(this->getTraceFlag())); wire->setVisible(visible); wire->setEverVisible(visible); } void PCBSketchWidget::addViewLayers() { setViewLayerIDs(ViewLayer::Silkscreen1, ViewLayer::Copper0Trace, ViewLayer::Copper0, ViewLayer::PcbRuler, ViewLayer::PcbNote); addViewLayersAux(ViewLayer::layersForView(ViewLayer::PCBView), ViewLayer::layersForViewFromBelow(ViewLayer::PCBView)); ViewLayer * silkscreen1 = m_viewLayers.value(ViewLayer::Silkscreen1); ViewLayer * silkscreen1Label = m_viewLayers.value(ViewLayer::Silkscreen1Label); if (silkscreen1 && silkscreen1Label) { silkscreen1Label->setParentLayer(silkscreen1); } ViewLayer * silkscreen0 = m_viewLayers.value(ViewLayer::Silkscreen0); ViewLayer * silkscreen0Label = m_viewLayers.value(ViewLayer::Silkscreen0Label); if (silkscreen0 && silkscreen0Label) { silkscreen0Label->setParentLayer(silkscreen0); } ViewLayer * copper0 = m_viewLayers.value(ViewLayer::Copper0); ViewLayer * copper0Trace = m_viewLayers.value(ViewLayer::Copper0Trace); ViewLayer * copper1 = m_viewLayers.value(ViewLayer::Copper1); ViewLayer * copper1Trace = m_viewLayers.value(ViewLayer::Copper1Trace); if (copper0 && copper0Trace) { copper0Trace->setParentLayer(copper0); } ViewLayer * groundPlane0 = m_viewLayers.value(ViewLayer::GroundPlane0); if (copper0 && groundPlane0) { groundPlane0->setParentLayer(copper0); } if (copper1 && copper1Trace) { copper1Trace->setParentLayer(copper1); } ViewLayer * groundPlane1 = m_viewLayers.value(ViewLayer::GroundPlane1); if (copper1 && groundPlane1) { groundPlane1->setParentLayer(copper1); } // disable these for now //viewLayer = m_viewLayers.value(ViewLayer::Keepout); //viewLayer->action()->setEnabled(false); setBoardLayers(1, false); } ViewLayer::ViewLayerID PCBSketchWidget::multiLayerGetViewLayerID(ModelPart * modelPart, ViewLayer::ViewID viewID, ViewLayer::ViewLayerPlacement viewLayerPlacement, LayerList & layerList) { if (modelPart->itemType() == ModelPart::CopperFill) { if (viewLayerPlacement == ViewLayer::NewBottom) return ViewLayer::GroundPlane0; else if (viewLayerPlacement == ViewLayer::NewTop) return ViewLayer::GroundPlane1; } // privilege Copper if it's available ViewLayer::ViewLayerID wantLayer = (modelPart->flippedSMD() && viewLayerPlacement == ViewLayer::NewTop) ? ViewLayer::Copper1 : ViewLayer::Copper0; if (layerList.contains(wantLayer)) return wantLayer; return SketchWidget::multiLayerGetViewLayerID(modelPart, viewID, viewLayerPlacement, layerList); } bool PCBSketchWidget::canDeleteItem(QGraphicsItem * item, int count) { VirtualWire * wire = dynamic_cast(item); if (wire != NULL && count > 1) return false; return SketchWidget::canDeleteItem(item, count); } bool PCBSketchWidget::canCopyItem(QGraphicsItem * item, int count) { VirtualWire * wire = dynamic_cast(item); if (wire != NULL) { if (wire->getRatsnest()) return false; } return SketchWidget::canCopyItem(item, count); } bool PCBSketchWidget::canChainWire(Wire * wire) { bool result = SketchWidget::canChainWire(wire); if (!result) return result; if (wire->getRatsnest()) { ConnectorItem * c0 = wire->connector0()->firstConnectedToIsh(); if (c0 == NULL) return false; ConnectorItem * c1 = wire->connector1()->firstConnectedToIsh(); if (c1 == NULL) return false; return !c0->wiredTo(c1, (ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::RatsnestFlag | ViewGeometry::SchematicTraceFlag) ^ getTraceFlag()); } return result; } void PCBSketchWidget::createTrace(Wire * wire, bool useLastWireColor) { QString commandString = tr("Create Trace from Ratsnest"); SketchWidget::createTrace(wire, commandString, getTraceFlag(), useLastWireColor); ensureTraceLayerVisible(); } void PCBSketchWidget::excludeFromAutoroute(bool exclude) { foreach (QGraphicsItem * item, scene()->selectedItems()) { TraceWire * wire = dynamic_cast(item); if (wire) { if (!wire->isTraceType(getTraceFlag())) continue; QList wires; QList ends; wire->collectChained(wires, ends); foreach (Wire * w, wires) { w->setAutoroutable(!exclude); } continue; } JumperItem * jumperItem = dynamic_cast(item); if (jumperItem) { jumperItem->setAutoroutable(!exclude); continue; } Via * via = dynamic_cast(item); if (via) { via->setAutoroutable(!exclude); continue; } } } void PCBSketchWidget::selectAllExcludedTraces() { selectAllXTraces(false, QObject::tr("Select all 'Don't autoroute' traces"), autorouteTypePCB()); } void PCBSketchWidget::selectAllIncludedTraces() { selectAllXTraces(true, QObject::tr("Select all autorouteable traces"), autorouteTypePCB()); } void PCBSketchWidget::selectAllXTraces(bool autoroutable, const QString & cmdText, bool forPCB) { QList wires; QList items; if (forPCB) { int boardCount; ItemBase * board = findSelectedBoard(boardCount); if (boardCount == 0 && autorouteTypePCB()) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use this selection operation.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please click on a PCB first--this selection operation only works for one board at a time.")); return; } items = scene()->collidingItems(board); } else { items = scene()->items(); } foreach (QGraphicsItem * item, items) { TraceWire * wire = dynamic_cast(item); if (wire == NULL) continue; if (!wire->isTraceType(getTraceFlag())) continue; if (wire->getAutoroutable() == autoroutable) { wires.append(wire); } } QUndoCommand * parentCommand = new QUndoCommand(cmdText); stackSelectionState(false, parentCommand); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); foreach (Wire * wire, wires) { selectItemCommand->addRedo(wire->id()); } scene()->clearSelection(); m_undoStack->push(parentCommand); } const QString & PCBSketchWidget::hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { Q_UNUSED(event); Q_UNUSED(item); static QString message = tr("Click this connector to drag out a new trace."); return message; } void PCBSketchWidget::addDefaultParts() { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(QPointF(0, 0)); // have to put this off until later, because positioning the item doesn't work correctly until the view is visible m_addedDefaultPart = addItem(referenceModel()->retrieveModelPart(ModuleIDNames::TwoSidedRectanglePCBModuleIDName), ViewLayer::NewTop, BaseCommand::CrossView, viewGeometry, newID, -1, NULL); m_addDefaultParts = true; changeBoardLayers(2, true); } void PCBSketchWidget::showEvent(QShowEvent * event) { SketchWidget::showEvent(event); dealWithDefaultParts(); } void PCBSketchWidget::dealWithDefaultParts() { if (!m_addDefaultParts) return; if (m_addedDefaultPart == NULL) return; m_addDefaultParts = false; QSizeF vpSize = this->viewport()->size(); QSizeF partSize(300, 200); //if (vpSize.height() < helpSize.height() + 50 + partSize.height()) { //vpSize.setWidth(vpSize.width() - verticalScrollBar()->width()); //} QPointF p; p.setX((int) ((vpSize.width() - partSize.width()) / 2.0)); p.setY((int) ((vpSize.height() - partSize.height()) / 2.0)); // place it QPointF q = mapToScene(p.toPoint()); m_addedDefaultPart->setPos(q); alignOneToGrid(m_addedDefaultPart); ResizableBoard * rb = qobject_cast(m_addedDefaultPart); if (rb) rb->resizePixels(partSize.width(), partSize.height(), m_viewLayers); QTimer::singleShot(10, this, SLOT(vScrollToZero())); // set both layers active by default setLayerActive(ViewLayer::Copper1, true); setLayerActive(ViewLayer::Copper0, true); setLayerActive(ViewLayer::Silkscreen1, true); setLayerActive(ViewLayer::Silkscreen0, true); } void PCBSketchWidget::setClipEnds(ClipableWire * vw, bool clipEnds) { vw->setClipEnds(clipEnds); } ViewLayer::ViewLayerID PCBSketchWidget::getDragWireViewLayerID(ConnectorItem * connectorItem) { switch (connectorItem->attachedToViewLayerID()) { case ViewLayer::Copper1: case ViewLayer::Copper1Trace: case ViewLayer::GroundPlane1: return ViewLayer::Copper1Trace; default: return ViewLayer::Copper0Trace; } } ViewLayer::ViewLayerID PCBSketchWidget::getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewGeometry.getRatsnest()) { return ViewLayer::PcbRatsnest; } if (viewGeometry.getAnyTrace()) { switch (viewLayerPlacement) { case ViewLayer::NewTop: return ViewLayer::Copper1Trace; default: return ViewLayer::Copper0Trace; } } switch (viewLayerPlacement) { case ViewLayer::NewTop: return ViewLayer::Copper1Trace; default: return m_wireViewLayerID; } } void PCBSketchWidget::initWire(Wire * wire, int penWidth) { Q_UNUSED(penWidth); if (wire->getRatsnest()) return; wire->setColorString(traceColor(wire->connector0()), 1.0, false); wire->setPenWidth(1, this, 2); } bool PCBSketchWidget::autorouteTypePCB() { return true; } const QString & PCBSketchWidget::traceColor(ConnectorItem * forColor) { switch(forColor->attachedToViewLayerID()) { case ViewLayer::Copper1: case ViewLayer::Copper1Trace: case ViewLayer::GroundPlane1: return PCBTraceColor1; default: return PCBTraceColor; } } const QString & PCBSketchWidget::traceColor(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewTop) { return PCBTraceColor1; } return PCBTraceColor; } PCBSketchWidget::CleanType PCBSketchWidget::cleanType() { return m_cleanType; } void PCBSketchWidget::ensureTraceLayersVisible() { ensureLayerVisible(ViewLayer::Copper0); ensureLayerVisible(ViewLayer::Copper0Trace); ensureLayerVisible(ViewLayer::GroundPlane0); if (m_boardLayers == 2) { ensureLayerVisible(ViewLayer::Copper1); ensureLayerVisible(ViewLayer::Copper1Trace); ensureLayerVisible(ViewLayer::GroundPlane1); } } void PCBSketchWidget::ensureTraceLayerVisible() { ensureLayerVisible(ViewLayer::Copper0); ensureLayerVisible(ViewLayer::Copper0Trace); } bool PCBSketchWidget::canChainMultiple() { return false; } void PCBSketchWidget::setNewPartVisible(ItemBase * itemBase) { if (itemBase->itemType() == ModelPart::Breadboard || itemBase->itemType() == ModelPart::Symbol || itemBase->itemType() == ModelPart::SchematicSubpart || itemBase->moduleID().endsWith(ModuleIDNames::SchematicFrameModuleIDName) || itemBase->moduleID().endsWith(ModuleIDNames::PowerModuleIDName)) { // don't need to see the breadboard in the other views // but it's there so connections can be more easily synched between views itemBase->setVisible(false); itemBase->setEverVisible(false); } } bool PCBSketchWidget::canDropModelPart(ModelPart * modelPart) { if (!SketchWidget::canDropModelPart(modelPart)) return false; if (Board::isBoard(modelPart)) { return matchesLayer(modelPart); } switch (modelPart->itemType()) { case ModelPart::Logo: if (modelPart->moduleID().contains("schematic", Qt::CaseInsensitive)) return false; if (modelPart->moduleID().contains("breadboard", Qt::CaseInsensitive)) return false; case ModelPart::Jumper: case ModelPart::Ruler: case ModelPart::CopperFill: return true; case ModelPart::Wire: case ModelPart::Breadboard: case ModelPart::Symbol: case ModelPart::SchematicSubpart: // can't drag and drop these parts in this view return false; default: return !modelPart->moduleID().endsWith(ModuleIDNames::SchematicFrameModuleIDName); } return true; } bool PCBSketchWidget::bothEndsConnected(Wire * wire, ViewGeometry::WireFlags flag, ConnectorItem * oneEnd, QList & wires, QList & partConnectorItems) { QList visited; return bothEndsConnectedAux(wire, flag, oneEnd, wires, partConnectorItems, visited); } bool PCBSketchWidget::bothEndsConnectedAux(Wire * wire, ViewGeometry::WireFlags flag, ConnectorItem * oneEnd, QList & wires, QList & partConnectorItems, QList & visited) { if (visited.contains(wire)) return false; visited.append(wire); bool result = false; ConnectorItem * otherEnd = wire->otherConnector(oneEnd); foreach (ConnectorItem * toConnectorItem, otherEnd->connectedToItems()) { if (partConnectorItems.contains(toConnectorItem)) { result = true; continue; } if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue; Wire * w = qobject_cast(toConnectorItem->attachedTo()); ViewGeometry::WireFlags wflag = w->wireFlags() & (ViewGeometry::RatsnestFlag | getTraceFlag()); if (wflag != flag) continue; result = bothEndsConnectedAux(w, flag, toConnectorItem, wires, partConnectorItems, visited) || result; // let it recurse } if (result) { wires.removeOne(wire); } return result; } bool PCBSketchWidget::canCreateWire(Wire * dragWire, ConnectorItem * from, ConnectorItem * to) { Q_UNUSED(dragWire); return ((from != NULL) && (to != NULL)); } ConnectorItem * PCBSketchWidget::findNearestPartConnectorItem(ConnectorItem * fromConnectorItem) { // find the nearest part to fromConnectorItem Wire * wire = qobject_cast(fromConnectorItem->attachedTo()); if (wire == NULL) return NULL; QList ends; calcDistances(wire, ends); clearDistances(); if (ends.count() < 1) return NULL; return ends[0]; } void PCBSketchWidget::calcDistances(Wire * wire, QList & ends) { QList chained; wire->collectChained(chained, ends); if (ends.count() < 2) return; clearDistances(); foreach (ConnectorItem * end, ends) { bool fromConnector0; QList distanceWires; int distance = calcDistance(wire, end, 0, distanceWires, fromConnector0); DistanceThing * dt = new DistanceThing; dt->distance = distance; dt->fromConnector0 = fromConnector0; DebugDialog::debug(QString("distance %1 %2 %3, %4 %5") .arg(end->attachedToID()).arg(end->attachedToTitle()).arg(end->connectorSharedID()) .arg(distance).arg(fromConnector0 ? "connector0" : "connector1")); distances.insert(end, dt); } qSort(ends.begin(), ends.end(), distanceLessThan); } void PCBSketchWidget::clearDistances() { foreach (ConnectorItem * c, distances.keys()) { DistanceThing * dt = distances.value(c, NULL); if (dt) delete dt; } distances.clear(); } int PCBSketchWidget::calcDistanceAux(ConnectorItem * from, ConnectorItem * to, int distance, QList & distanceWires) { //DebugDialog::debug(QString("calc distance aux: %1 %2, %3 %4, %5").arg(from->attachedToID()).arg(from->connectorSharedID()) //.arg(to->attachedToTitle()).arg(to->connectorSharedID()).arg(distance)); foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) { if (toConnectorItem == to) { return distance; } } int result = MAX_INT; foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue; Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (distanceWires.contains(w)) continue; bool fromConnector0; int temp = calcDistance(w, to, distance + 1, distanceWires, fromConnector0); if (temp < result) { result = temp; } } return result; } int PCBSketchWidget::calcDistance(Wire * wire, ConnectorItem * end, int distance, QList & distanceWires, bool & fromConnector0) { //DebugDialog::debug(QString("calc distance wire: %1 rat:%2 to %3 %4, %5").arg(wire->id()).arg(wire->getRatsnest()) //.arg(end->attachedToTitle()).arg(end->connectorSharedID()).arg(distance)); distanceWires.append(wire); int d0 = calcDistanceAux(wire->connector0(), end, distance, distanceWires); if (d0 == distance) { fromConnector0 = true; return d0; } int d1 = calcDistanceAux(wire->connector1(), end, distance, distanceWires); if (d0 <= d1) { fromConnector0 = true; return d0; } fromConnector0 = false; return d1; } void PCBSketchWidget::showGroundTraces(QList & connectorItems, bool show) { foreach (ConnectorItem * connectorItem, connectorItems) { TraceWire * trace = dynamic_cast(connectorItem->attachedTo()); if (trace == NULL) continue; if (!trace->isTraceType(getTraceFlag())) continue; trace->setVisible(show); } } void PCBSketchWidget::getLabelFont(QFont & font, QColor & color, ItemBase * itemBase) { font.setFamily(OCRAFontName); font.setPointSize(getLabelFontSizeSmall()); font.setBold(false); font.setItalic(false); color.setAlpha(255); QString name = ViewLayer::Silkscreen1Color; if (boardLayers() == 2) { if (itemBase->viewLayerPlacement() == ViewLayer::NewBottom) name = ViewLayer::Silkscreen0Color; } color.setNamedColor(name); } ViewLayer::ViewLayerID PCBSketchWidget::getLabelViewLayerID(ItemBase * itemBase) { if (boardLayers() == 2) { if (itemBase->viewLayerPlacement() == ViewLayer::NewBottom) return ViewLayer::Silkscreen0Label; } return ViewLayer::Silkscreen1Label; } double PCBSketchWidget::getLabelFontSizeTiny() { return 3; } double PCBSketchWidget::getLabelFontSizeSmall() { return 5; } double PCBSketchWidget::getLabelFontSizeMedium() { return 7; } double PCBSketchWidget::getLabelFontSizeLarge() { return 12; } void PCBSketchWidget::resizeBoard(double mmW, double mmH, bool doEmit) { Q_UNUSED(doEmit); PaletteItem * item = getSelectedPart(); if (item == NULL) return; bool handle = false; switch (item->itemType()) { case ModelPart::ResizableBoard: case ModelPart::Logo: handle = true; break; case ModelPart::Part: handle = item->moduleID().endsWith(ModuleIDNames::PadModuleIDName) || item->moduleID().endsWith(ModuleIDNames::CopperBlockerModuleIDName) || item->moduleID().endsWith(ModuleIDNames::SchematicFrameModuleIDName); break; default: break; } if (!handle) return SketchWidget::resizeBoard(mmW, mmH, doEmit); resizeWithHandle(item, mmW, mmH); } void PCBSketchWidget::showLabelFirstTime(long itemID, bool show, bool doEmit) { // called when new item is added, to decide whether to show part label SketchWidget::showLabelFirstTime(itemID, show, doEmit); ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; if (!canDropModelPart(itemBase->modelPart())) return; switch (itemBase->itemType()) { case ModelPart::Part: case ModelPart::Jumper: { if (itemBase->hasPartLabel()) { ViewLayer * viewLayer = m_viewLayers.value(getLabelViewLayerID(itemBase)); itemBase->showPartLabel(itemBase->isVisible(), viewLayer); itemBase->partLabelSetHidden(!viewLayer->visible()); } } break; default: break; } } ItemBase * PCBSketchWidget::findBoardBeneath(ItemBase * itemBase) { foreach (QGraphicsItem * item, scene()->collidingItems(itemBase)) { Board * board = dynamic_cast(item); if (board == NULL) continue; if (Board::isBoard(board)) return board; } return NULL; } ItemBase * PCBSketchWidget::findSelectedBoard(int & boardCount) { QList boards = findBoard(); boardCount = boards.count(); if (boards.count() == 0) return NULL; if (boards.count() == 1) return boards.at(0); int selectedCount = 0; ItemBase * selectedBoard = NULL; foreach (ItemBase * board, boards) { if (board->isSelected()) { selectedCount++; selectedBoard = board; } } if (selectedCount == 1) return selectedBoard; return NULL; } QList PCBSketchWidget::findBoard() { QSet boards; foreach (QGraphicsItem * childItem, items()) { Board * board = dynamic_cast(childItem); if (board == NULL) continue; if (Board::isBoard(board)) { boards.insert(board->layerKinChief()); } } return boards.toList(); } void PCBSketchWidget::forwardRoutingStatus(const RoutingStatus & routingStatus) { m_routingStatus = routingStatus; SketchWidget::forwardRoutingStatus(routingStatus); } double PCBSketchWidget::defaultGridSizeInches() { return 0.1; } ViewLayer::ViewLayerPlacement PCBSketchWidget::wireViewLayerPlacement(ConnectorItem * connectorItem) { switch (connectorItem->attachedToViewLayerID()) { case ViewLayer::Copper1: case ViewLayer::Copper1Trace: case ViewLayer::GroundPlane1: return ViewLayer::NewTop; default: return ViewLayer::NewBottom; } } void PCBSketchWidget::setBoardLayers(int layers, bool redraw) { SketchWidget::setBoardLayers(layers, redraw); QList viewLayerIDs; viewLayerIDs << ViewLayer::Copper1 << ViewLayer::Copper1Trace; foreach (ViewLayer::ViewLayerID viewLayerID, viewLayerIDs) { ViewLayer * layer = m_viewLayers.value(viewLayerID, NULL); if (layer) { layer->action()->setEnabled(layers == 2); layer->setVisible(layers == 2); if (redraw) { setLayerVisible(layer, layers == 2, true); if (layers == 2) { layer->action()->setChecked(true); } } } } } void PCBSketchWidget::swapLayers(ItemBase *, int newLayers, QUndoCommand * parentCommand) { QList smds; QList pads; QList already; ChangeBoardLayersCommand * changeBoardCommand = new ChangeBoardLayersCommand(this, m_boardLayers, newLayers, parentCommand); QList boards = findBoard(); foreach (ItemBase * board, boards) { new SetPropCommand(this, board->id(), "layers", QString::number(m_boardLayers), QString::number(newLayers), true, parentCommand); } if (newLayers == 2) { new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); return; } // disconnect and flip smds foreach (QGraphicsItem * item, scene()->items()) { ItemBase * smd = dynamic_cast(item); if (smd == NULL) continue; if (smd->moduleID().endsWith(ModuleIDNames::PadModuleIDName)) { pads << smd; continue; } if (!smd->modelPart()->flippedSMD()) continue; smd = smd->layerKinChief(); if (smds.contains(smd)) continue; smds.append(smd); } changeTraceLayer(NULL, true, changeBoardCommand); foreach (ItemBase * smd, smds) { long newID; emit subSwapSignal(this, smd, smd->moduleID(), (newLayers == 1) ? ViewLayer::NewBottom : ViewLayer::NewTop, newID, changeBoardCommand); } foreach (ItemBase * itemBase, pads) { Pad * pad = qobject_cast(itemBase); if (pad == NULL) continue; long newID; emit subSwapSignal(this, pad, (newLayers == 1) ? ModuleIDNames::Copper0PadModuleIDName : ModuleIDNames::PadModuleIDName, (newLayers == 1) ? ViewLayer::NewBottom : ViewLayer::NewTop, newID, changeBoardCommand); double w = pad->modelPart()->localProp("width").toDouble(); double h = pad->modelPart()->localProp("height").toDouble(); new ResizeBoardCommand(this, newID, w, h, w, h, parentCommand); } } bool PCBSketchWidget::isBoardLayerChange(ItemBase * itemBase, const QString & newModuleID, int & newLayers) { newLayers = m_boardLayers; if (!Board::isBoard(itemBase)) { // no change return false; } ModelPart * modelPart = referenceModel()->retrieveModelPart(newModuleID); if (modelPart == NULL) { // shouldn't happen return false; } QString slayers = modelPart->properties().value("layers", ""); if (slayers.isEmpty()) { // shouldn't happen return false; } bool ok; int layers = slayers.toInt(&ok); if (!ok) { // shouldn't happen return false; } newLayers = layers; return (m_boardLayers != layers); } void PCBSketchWidget::changeBoardLayers(int layers, bool doEmit) { setBoardLayers(layers, true); SketchWidget::changeBoardLayers(layers, doEmit); if (layers == 1) { this->setLayerActive(ViewLayer::Copper0, true); this->setLayerActive(ViewLayer::Silkscreen0, true); } emit updateLayerMenuSignal(); } void PCBSketchWidget::loadFromModelParts(QList & modelParts, BaseCommand::CrossViewType crossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList & newIDs) { int layers = 1; if (parentCommand == NULL) { foreach (ModelPart * modelPart, modelParts) { if (Board::isBoard(modelPart)) { QString slayers = modelPart->localProp("layers").toString(); if (slayers.isEmpty()) { slayers = modelPart->properties().value("layers", ""); } if (slayers.isEmpty()) { // shouldn't happen continue; } bool ok; int localLayers = slayers.toInt(&ok); if (!ok) { // shouldn't happen continue; } if (localLayers == 2) { layers = 2; break; } } else if (modelPart->itemType() == ModelPart::Wire) { QDomElement instance = modelPart->instanceDomElement(); QDomElement views = instance.firstChildElement("views"); QDomElement view = views.firstChildElement("pcbView"); if (view.attribute("layer").compare("copper1trace") == 0) { layers = 2; break; } } else if (modelPart->itemType() == ModelPart::CopperFill) { QDomElement instance = modelPart->instanceDomElement(); QDomElement views = instance.firstChildElement("views"); QDomElement view = views.firstChildElement("pcbView"); if (view.attribute("layer").compare("groundplane1") == 0) { layers = 2; break; } } } changeBoardLayers(layers, true); } SketchWidget::loadFromModelParts(modelParts, crossViewType, parentCommand, offsetPaste, boundingRect, seekOutsideConnections, newIDs); if (parentCommand == NULL) { changeBoardLayers(layers, true); shiftHoles(); } } bool PCBSketchWidget::isInLayers(ConnectorItem * connectorItem, ViewLayer::ViewLayerPlacement viewLayerPlacement) { return connectorItem->isInLayers(viewLayerPlacement); } bool PCBSketchWidget::routeBothSides() { return m_boardLayers > 1; } bool PCBSketchWidget::sameElectricalLayer2(ViewLayer::ViewLayerID id1, ViewLayer::ViewLayerID id2) { switch (id1) { case ViewLayer::Copper0Trace: if (id1 == id2) return true; return (id2 == ViewLayer::Copper0 || id2 == ViewLayer::GroundPlane0); case ViewLayer::Copper0: case ViewLayer::GroundPlane0: if (id1 == id2) return true; return (id2 == ViewLayer::Copper0Trace); case ViewLayer::Copper1Trace: if (id1 == id2) return true; return (id2 == ViewLayer::Copper1 || id2 == ViewLayer::GroundPlane1); case ViewLayer::Copper1: case ViewLayer::GroundPlane1: if (id1 == id2) return true; return (id2 == ViewLayer::Copper1Trace); default: break; } return false; } void PCBSketchWidget::changeTraceLayer(ItemBase * itemBase, bool force, QUndoCommand * parentCommand) { QList visitedWires; QSet changeWires; TraceWire * sample = NULL; QList items; if (itemBase != NULL) items << itemBase; else if (force) items = scene()->items(); else items = scene()->selectedItems(); foreach (QGraphicsItem * item, items) { TraceWire * tw = dynamic_cast(item); if (tw == NULL) continue; if (!tw->isTraceType(getTraceFlag())) continue; if (visitedWires.contains(tw)) continue; sample = tw; QList wires; QList ends; tw->collectChained(wires, ends); visitedWires.append(wires); if (!force) { bool canChange = true; foreach(ConnectorItem * end, ends) { if (end->getCrossLayerConnectorItem() == NULL) { canChange = false; break; } } if (!canChange) continue; } changeWires.insert(tw); } if (changeWires.count() == 0 || sample == NULL) return; bool createNew = false; if (parentCommand == NULL) { parentCommand = new QUndoCommand(tr("Change trace layer")); createNew = true; } ViewLayer::ViewLayerID newViewLayerID = (sample->viewLayerID() == ViewLayer::Copper0Trace) ? ViewLayer::Copper1Trace : ViewLayer::Copper0Trace; if (force) { // move all traces to bottom layer (force == true when switching from 2 layers to 1) newViewLayerID = ViewLayer::Copper0Trace; } foreach (Wire * wire, changeWires) { QList wires; QList ends; wire->collectChained(wires, ends); // probably safest to disconnect change the layers and reconnect, so that's why the redundant looping foreach (ConnectorItem * end, ends) { ConnectorItem * targetConnectorItem = NULL; foreach (ConnectorItem * toConnectorItem, end->connectedToItems()) { Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (w == NULL) continue; if (wires.contains(w)) { targetConnectorItem = toConnectorItem; break; } } extendChangeConnectionCommand(BaseCommand::SingleView, targetConnectorItem, end, ViewLayer::specFromID(end->attachedToViewLayerID()), false, parentCommand); } foreach (Wire * w, wires) { new ChangeLayerCommand(this, w->id(), w->zValue(), m_viewLayers.value(newViewLayerID)->nextZ(), w->viewLayerID(), newViewLayerID, parentCommand); } foreach (ConnectorItem * end, ends) { ConnectorItem * targetConnectorItem = NULL; foreach (ConnectorItem * toConnectorItem, end->connectedToItems()) { Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (w == NULL) continue; if (wires.contains(w)) { targetConnectorItem = toConnectorItem; break; } } new ChangeConnectionCommand(this, BaseCommand::SingleView, targetConnectorItem->attachedToID(), targetConnectorItem->connectorSharedID(), end->attachedToID(), end->connectorSharedID(), ViewLayer::specFromID(newViewLayerID), true, parentCommand); } } if (createNew) { m_undoStack->waitPush(parentCommand, PropChangeDelay); } } void PCBSketchWidget::changeLayer(long id, double z, ViewLayer::ViewLayerID viewLayerID) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; itemBase->setViewLayerID(viewLayerID, m_viewLayers); itemBase->setZValue(z); itemBase->saveGeometry(); TraceWire * tw = qobject_cast(itemBase); if (tw != NULL) { ViewLayer::ViewLayerPlacement viewLayerPlacement = ViewLayer::specFromID(viewLayerID); tw->setViewLayerPlacement(viewLayerPlacement); tw->setColorString(traceColor(viewLayerPlacement), 1.0, true); ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); tw->setInactive(!viewLayer->isActive()); tw->setHidden(!viewLayer->visible()); tw->update(); } updateInfoView(); } bool PCBSketchWidget::resizingJumperItemPress(ItemBase * itemBase) { if (itemBase == NULL) return false; JumperItem * jumperItem = qobject_cast(itemBase->layerKinChief()); if (jumperItem == NULL) return false; if (!jumperItem->inDrag()) return false; m_resizingJumperItem = jumperItem; m_resizingJumperItem->saveParams(); if (m_alignToGrid) { m_alignmentStartPoint = QPointF(0,0); ItemBase * board = findBoardBeneath(m_resizingJumperItem); QHash savedItems; QHash savedWires; if (board == NULL) { foreach (QGraphicsItem * item, scene()->items()) { PaletteItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->itemType() == ModelPart::Jumper) continue; savedItems.insert(itemBase->layerKinChief()->id(), itemBase); } } findAlignmentAnchor(board, savedItems, savedWires); m_jumperDragOffset = jumperItem->dragOffset(); connect(m_resizingJumperItem, SIGNAL(alignMe(JumperItem *, QPointF &)), this, SLOT(alignJumperItem(JumperItem *, QPointF &)), Qt::DirectConnection); } return true; } void PCBSketchWidget::alignJumperItem(JumperItem * jumperItem, QPointF & loc) { Q_UNUSED(jumperItem); if (!m_alignToGrid) return; QPointF newPos = loc - m_jumperDragOffset - m_alignmentStartPoint; double ny = GraphicsUtils::getNearestOrdinate(newPos.y(), gridSizeInches() * GraphicsUtils::SVGDPI); double nx = GraphicsUtils::getNearestOrdinate(newPos.x(), gridSizeInches() * GraphicsUtils::SVGDPI); loc.setX(loc.x() + nx - newPos.x()); loc.setY(loc.y() + ny - newPos.y()); } bool PCBSketchWidget::resizingJumperItemRelease() { if (m_resizingJumperItem == NULL) return false; if (m_alignToGrid) { disconnect(m_resizingJumperItem, SIGNAL(alignMe(JumperItem *, QPointF &)), this, SLOT(alignJumperItem(JumperItem *, QPointF &))); } resizeJumperItem(); return true; } void PCBSketchWidget::resizeJumperItem() { QPointF oldC0, oldC1; QPointF oldPos; m_resizingJumperItem->getParams(oldPos, oldC0, oldC1); QPointF newC0, newC1; QPointF newPos; m_resizingJumperItem->saveParams(); m_resizingJumperItem->getParams(newPos, newC0, newC1); QUndoCommand * cmd = new ResizeJumperItemCommand(this, m_resizingJumperItem->id(), oldPos, oldC0, oldC1, newPos, newC0, newC1, NULL); cmd->setText("Resize Jumper"); m_undoStack->waitPush(cmd, 10); m_resizingJumperItem = NULL; } bool PCBSketchWidget::canDragWire(Wire * wire) { if (wire == NULL) return false; if (wire->getRatsnest()) return false; return true; } void PCBSketchWidget::wireSplitSlot(Wire* wire, QPointF newPos, QPointF oldPos, const QLineF & oldLine) { if (!wire->getRatsnest()) { SketchWidget::wireSplitSlot(wire, newPos, oldPos, oldLine); } createTrace(wire, false); } ItemBase * PCBSketchWidget::addCopperLogoItem(ViewLayer::ViewLayerPlacement viewLayerPlacement) { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(QPointF(0, 0)); QString moduleID = (viewLayerPlacement == ViewLayer::NewBottom) ? ModuleIDNames::Copper0LogoTextModuleIDName : ModuleIDNames::Copper1LogoTextModuleIDName; return addItem(referenceModel()->retrieveModelPart(moduleID), viewLayerPlacement, BaseCommand::SingleView, viewGeometry, newID, -1, NULL); } bool PCBSketchWidget::hasAnyNets() { return m_routingStatus.m_netCount > 0; } QSizeF PCBSketchWidget::jumperItemSize() { if (m_jumperItemSize.width() == 0) { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(QPointF(0, 0)); ItemBase * itemBase = addItem(referenceModel()->retrieveModelPart(ModuleIDNames::JumperModuleIDName), ViewLayer::NewTop, BaseCommand::SingleView, viewGeometry, newID, -1, NULL); if (itemBase) { JumperItem * jumperItem = qobject_cast(itemBase); m_jumperItemSize = jumperItem->connector0()->rect().size(); deleteItem(itemBase, true, false, false); } } return m_jumperItemSize; } double PCBSketchWidget::getKeepout() { QString keepoutString = m_autorouterSettings.value(DRC::KeepoutSettingName); if (keepoutString.isEmpty()) { QSettings settings; keepoutString = settings.value(DRC::KeepoutSettingName, "").toString(); } bool ok; double inches = TextUtils::convertToInches(keepoutString, &ok, false); if (!ok) { keepoutString = QString("%1in").arg(DRC::KeepoutDefaultMils / 1000); inches = DRC::KeepoutDefaultMils / 1000; } m_autorouterSettings.insert(DRC::KeepoutSettingName, keepoutString); return inches * GraphicsUtils::SVGDPI; // inches converted to pixels } void PCBSketchWidget::setKeepout(double mils) { QString keepoutString = QString("%1in").arg(mils / 1000); m_autorouterSettings.insert(DRC::KeepoutSettingName, keepoutString); } void PCBSketchWidget::resetKeepout() { setKeepout(DRC::KeepoutDefaultMils); } bool PCBSketchWidget::acceptsTrace(const ViewGeometry & viewGeometry) { return !viewGeometry.getSchematicTrace(); } ItemBase * PCBSketchWidget::placePartDroppedInOtherView(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin) { ItemBase * newItem = SketchWidget::placePartDroppedInOtherView(modelPart, viewLayerPlacement, viewGeometry, id, dropOrigin); if (newItem == NULL) return newItem; if (!newItem->isEverVisible()) return newItem; dealWithDefaultParts(); QList boards; if (autorouteTypePCB()) { boards = findBoard(); } else { boards << NULL; } foreach (ItemBase * board, boards) { // This is a 2d bin-packing problem. We can use our tile datastructure for this. // Use a simple best-fit approach for now. No idea how optimal a solution it is. CMRouter router(this, board, false); int keepout = 10; router.setKeepout(keepout); Plane * plane = router.initPlane(false); QList alreadyTiled; foreach (QGraphicsItem * item, (board) ? scene()->collidingItems(board) : scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isEverVisible()) continue; if (itemBase->layerKinChief() != itemBase) continue; if (itemBase->layerKinChief() == board) continue; if (itemBase->layerKinChief() == newItem) continue; Wire * wire = qobject_cast(itemBase); if (wire != NULL) { if (!wire->getTrace()) continue; if (!wire->isTraceType(getTraceFlag())) continue; } else if (ResizableBoard::isBoard(itemBase)) continue; // itemBase->debugInfo("tiling"); QRectF r = itemBase->sceneBoundingRect().adjusted(-keepout, -keepout, keepout, keepout); router.insertTile(plane, r, alreadyTiled, NULL, Tile::OBSTACLE, CMRouter::IgnoreAllOverlaps); } BestPlace bestPlace; bestPlace.maxRect = router.boardRect(); bestPlace.rotate90 = false; bestPlace.width = realToTile(newItem->boundingRect().width()); bestPlace.height = realToTile(newItem->boundingRect().height()); bestPlace.plane = plane; TiSrArea(NULL, plane, &bestPlace.maxRect, Panelizer::placeBestFit, &bestPlace); if (bestPlace.bestTile != NULL) { QRectF r; tileToQRect(bestPlace.bestTile, r); ItemBase * chief = newItem->layerKinChief(); chief->setPos(r.topLeft()); DebugDialog::debug(QString("placing part with rotation:%1").arg(bestPlace.rotate90), r); if (bestPlace.rotate90) { chief->rotateItem(90, false); } alignOneToGrid(newItem); } router.drcClean(); if (bestPlace.bestTile != NULL) { break; } } return newItem; } void PCBSketchWidget::autorouterSettings() { // initialize settings values if they haven't already been initialized getKeepout(); QString ringThickness, holeSize; getDefaultViaSize(ringThickness, holeSize); getAutorouterTraceWidth(); AutorouterSettingsDialog dialog(m_autorouterSettings); if (QDialog::Accepted == dialog.exec()) { m_autorouterSettings = dialog.getSettings(); QSettings settings; foreach (QString key, m_autorouterSettings.keys()) { settings.setValue(key, m_autorouterSettings.value(key)); } } } void PCBSketchWidget::getViaSize(double & ringThickness, double & holeSize) { QString ringThicknessStr, holeSizeStr; getDefaultViaSize(ringThicknessStr, holeSizeStr); double rt = TextUtils::convertToInches(ringThicknessStr); double hs = TextUtils::convertToInches(holeSizeStr); ringThickness = rt * GraphicsUtils::SVGDPI; holeSize = hs * GraphicsUtils::SVGDPI; } void PCBSketchWidget::getDefaultViaSize(QString & ringThickness, QString & holeSize) { // these settings are initialized in via.cpp ringThickness = m_autorouterSettings.value(Via::AutorouteViaRingThickness, ""); holeSize = m_autorouterSettings.value(Via::AutorouteViaHoleSize, ""); QSettings settings; if (ringThickness.isEmpty()) { ringThickness = settings.value(Via::AutorouteViaRingThickness, Via::DefaultAutorouteViaRingThickness).toString(); } if (holeSize.isEmpty()) { holeSize = settings.value(Via::AutorouteViaHoleSize, Via::DefaultAutorouteViaHoleSize).toString(); } m_autorouterSettings.insert(Via::AutorouteViaRingThickness, ringThickness); m_autorouterSettings.insert(Via::AutorouteViaHoleSize, holeSize); } void PCBSketchWidget::deleteItem(ItemBase * itemBase, bool deleteModelPart, bool doEmit, bool later) { bool boardDeleted = Board::isBoard(itemBase); SketchWidget::deleteItem(itemBase, deleteModelPart, doEmit, later); if (boardDeleted) { if (findBoard().count() == 0) { emit boardDeletedSignal(); } requestQuoteSoon(); } } double PCBSketchWidget::getTraceWidth() { return Wire::STANDARD_TRACE_WIDTH; } double PCBSketchWidget::getAutorouterTraceWidth() { QString traceWidthString = m_autorouterSettings.value(AutorouterSettingsDialog::AutorouteTraceWidth, ""); if (traceWidthString.isEmpty()) { QSettings settings; QString def = QString::number(GraphicsUtils::pixels2mils(getTraceWidth(), GraphicsUtils::SVGDPI)); traceWidthString = settings.value(AutorouterSettingsDialog::AutorouteTraceWidth, def).toString(); } m_autorouterSettings.insert(AutorouterSettingsDialog::AutorouteTraceWidth, traceWidthString); return GraphicsUtils::SVGDPI * traceWidthString.toInt() / 1000.0; // traceWidthString is in mils } void PCBSketchWidget::getBendpointWidths(Wire * wire, double width, double & bendpointWidth, double & bendpoint2Width, bool & negativeOffsetRect) { Q_UNUSED(wire); bendpointWidth = bendpoint2Width = (width / -2); negativeOffsetRect = false; } double PCBSketchWidget::getSmallerTraceWidth(double minDim) { int mils = qMax((int) GraphicsUtils::pixels2mils(minDim, GraphicsUtils::SVGDPI) - 1, TraceWire::MinTraceWidthMils); return GraphicsUtils::mils2pixels(mils, GraphicsUtils::SVGDPI); } bool PCBSketchWidget::groundFill(bool fillGroundTraces, ViewLayer::ViewLayerID viewLayerID, QUndoCommand * parentCommand) { m_groundFillSeeds = NULL; int boardCount; ItemBase * board = findSelectedBoard(boardCount); // barf an error if there's no board if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use copper fill.")); return false; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("%1 Fill: please select the board you want to apply fill to.").arg(fillGroundTraces ? tr("Ground") : tr("Copper"))); return false; } QList seeds; if (fillGroundTraces) { bool gotTrueSeeds = collectGroundFillSeeds(seeds, false); if (!gotTrueSeeds && (seeds.count() != 1)) { QString message = tr("Please designate one or more ground fill seeds before doing a ground fill.\n\n"); setGroundFillSeeds(message); return false; } ConnectorItem::collectEqualPotential(seeds, true, ViewGeometry::NoFlag); //foreach (ConnectorItem * seed, seeds) { // seed->debugInfo("seed"); //} m_groundFillSeeds = &seeds; } LayerList viewLayerIDs; viewLayerIDs << ViewLayer::Board; QRectF boardImageRect, copperImageRect; RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString boardSvg = renderToSVG(renderThing, board, viewLayerIDs); if (boardSvg.isEmpty()) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to render board svg (1).")); return false; } boardImageRect = renderThing.imageRect; renderThing.renderBlocker = true; QString svg0; if (viewLayerID == ViewLayer::UnknownLayer || viewLayerID == ViewLayer::GroundPlane0) { viewLayerIDs.clear(); viewLayerIDs << ViewLayer::Copper0 << ViewLayer::Copper0Trace << ViewLayer::GroundPlane0; // hide ground traces so the ground plane will intersect them if (fillGroundTraces) showGroundTraces(seeds, false); svg0 = renderToSVG(renderThing, board, viewLayerIDs); if (fillGroundTraces) showGroundTraces(seeds, true); if (svg0.isEmpty()) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to render copper svg (1).")); return false; } copperImageRect = renderThing.imageRect; } QString svg1; if (boardLayers() > 1 && (viewLayerID == ViewLayer::UnknownLayer || viewLayerID == ViewLayer::GroundPlane1)) { viewLayerIDs.clear(); viewLayerIDs << ViewLayer::Copper1 << ViewLayer::Copper1Trace << ViewLayer::GroundPlane1; if (fillGroundTraces) showGroundTraces(seeds, false); svg1 = renderToSVG(renderThing, board, viewLayerIDs); if (fillGroundTraces) showGroundTraces(seeds, true); if (svg1.isEmpty()) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to render copper svg (1).")); return false; } copperImageRect = renderThing.imageRect; } QStringList exceptions; exceptions << "none" << "" << background().name(); // the color of holes in the board GroundPlaneGenerator gpg0; if (!svg0.isEmpty()) { gpg0.setLayerName("groundplane"); gpg0.setStrokeWidthIncrement(StrokeWidthIncrement); gpg0.setMinRunSize(10, 10); if (fillGroundTraces) { connect(&gpg0, SIGNAL(postImageSignal(GroundPlaneGenerator *, QImage *, QImage *, QGraphicsItem *, QList *)), this, SLOT(postImageSlot(GroundPlaneGenerator *, QImage *, QImage *, QGraphicsItem *, QList *)), Qt::DirectConnection); } bool result = gpg0.generateGroundPlane(boardSvg, boardImageRect.size(), svg0, copperImageRect.size(), exceptions, board, GraphicsUtils::StandardFritzingDPI / 2.0 /* 2 MIL */, ViewLayer::Copper0Color, getKeepoutMils()); if (result == false) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to write copper fill (1).")); return false; } } GroundPlaneGenerator gpg1; if (boardLayers() > 1 && !svg1.isEmpty()) { gpg1.setLayerName("groundplane1"); gpg1.setStrokeWidthIncrement(StrokeWidthIncrement); gpg1.setMinRunSize(10, 10); if (fillGroundTraces) { connect(&gpg1, SIGNAL(postImageSignal(GroundPlaneGenerator *, QImage *, QImage *, QGraphicsItem *, QList *)), this, SLOT(postImageSlot(GroundPlaneGenerator *, QImage *, QImage *, QGraphicsItem *, QList *)), Qt::DirectConnection); } bool result = gpg1.generateGroundPlane(boardSvg, boardImageRect.size(), svg1, copperImageRect.size(), exceptions, board, GraphicsUtils::StandardFritzingDPI / 2.0 /* 2 MIL */, ViewLayer::Copper1Color, getKeepoutMils()); if (result == false) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to write copper fill (2).")); return false; } } QString fillType = (fillGroundTraces) ? GroundPlane::fillTypeGround : GroundPlane::fillTypePlain; QRectF bsbr = board->sceneBoundingRect(); int ix = 0; foreach (QString svg, gpg0.newSVGs()) { ViewGeometry vg; vg.setLoc(bsbr.topLeft() + gpg0.newOffsets()[ix++]); long newID = ItemBase::getNextID(); new AddItemCommand(this, BaseCommand::CrossView, ModuleIDNames::GroundPlaneModuleIDName, ViewLayer::NewBottom, vg, newID, false, -1, parentCommand); new SetPropCommand(this, newID, "svg", svg, svg, true, parentCommand); new SetPropCommand(this, newID, "fillType", fillType, fillType, false, parentCommand); } ix = 0; foreach (QString svg, gpg1.newSVGs()) { ViewGeometry vg; vg.setLoc(bsbr.topLeft() + gpg1.newOffsets()[ix++]); long newID = ItemBase::getNextID(); new AddItemCommand(this, BaseCommand::CrossView, ModuleIDNames::GroundPlaneModuleIDName, ViewLayer::NewTop, vg, newID, false, -1, parentCommand); new SetPropCommand(this, newID, "svg", svg, svg, true, parentCommand); new SetPropCommand(this, newID, "fillType", fillType, fillType, false, parentCommand); } return true; } QString PCBSketchWidget::generateCopperFillUnit(ItemBase * itemBase, QPointF whereToStart) { int boardCount; ItemBase * board = findSelectedBoard(boardCount); // barf an error if there's no board if (boardCount == 0) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use copper fill.")); return ""; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Copper fill: please select only the board you want to fill.")); return ""; } QRectF bsbr = board->sceneBoundingRect(); if (!bsbr.contains(whereToStart)) { QMessageBox::critical(this, tr("Fritzing"), tr("Unable to create copper fill--probably the part wasn't dropped onto the PCB.")); return ""; } LayerList viewLayerIDs; viewLayerIDs << ViewLayer::Board; QRectF boardImageRect, copperImageRect; RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString boardSvg = renderToSVG(renderThing, board, viewLayerIDs); if (boardSvg.isEmpty()) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to render board svg (1).")); return ""; } boardImageRect = renderThing.imageRect; ViewLayer::ViewLayerPlacement viewLayerPlacement = ViewLayer::NewBottom; QString color = ViewLayer::Copper0Color; QString gpLayerName = "groundplane"; if (m_boardLayers == 2 && !dropOnBottom()) { gpLayerName += "1"; color = ViewLayer::Copper1Color; viewLayerPlacement = ViewLayer::NewTop; } viewLayerIDs = ViewLayer::copperLayers(viewLayerPlacement); bool vis = itemBase->isVisible(); itemBase->setVisible(false); renderThing.renderBlocker = true; QString svg = renderToSVG(renderThing, board, viewLayerIDs); itemBase->setVisible(vis); if (svg.isEmpty()) { QMessageBox::critical(this, tr("Fritzing"), tr("Fritzing error: unable to render copper svg (1).")); return ""; } copperImageRect = renderThing.imageRect; QStringList exceptions; exceptions << "none" << "" << background().name(); // the color of holes in the board GroundPlaneGenerator gpg; gpg.setStrokeWidthIncrement(StrokeWidthIncrement); gpg.setLayerName(gpLayerName); gpg.setMinRunSize(10, 10); bool result = gpg.generateGroundPlaneUnit(boardSvg, boardImageRect.size(), svg, copperImageRect.size(), exceptions, board, GraphicsUtils::StandardFritzingDPI / 2.0 /* 2 MIL */, color, whereToStart, getKeepoutMils()); if (result == false || gpg.newSVGs().count() < 1) { QMessageBox::critical(this, tr("Fritzing"), tr("Unable to create copper fill--possibly the part was dropped onto another part or wire rather than the actual PCB.")); return ""; } itemBase->setPos(bsbr.topLeft() + gpg.newOffsets()[0]); itemBase->setViewLayerID(gpLayerName, m_viewLayers); return gpg.newSVGs()[0]; } bool PCBSketchWidget::connectorItemHasSpec(ConnectorItem * connectorItem, ViewLayer::ViewLayerPlacement spec) { if (ViewLayer::specFromID(connectorItem->attachedToViewLayerID()) == spec) return true; connectorItem = connectorItem->getCrossLayerConnectorItem(); if (connectorItem == NULL) return false; return (ViewLayer::specFromID(connectorItem->attachedToViewLayerID()) == spec); } ViewLayer::ViewLayerPlacement PCBSketchWidget::createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to) { QList guesses; guesses.append(layerIsActive(ViewLayer::Copper0) ? ViewLayer::NewBottom : ViewLayer::NewTop); guesses.append(layerIsActive(ViewLayer::Copper0) ? ViewLayer::NewTop : ViewLayer::NewBottom); foreach (ViewLayer::ViewLayerPlacement guess, guesses) { if (connectorItemHasSpec(from, guess) && connectorItemHasSpec(to, guess)) { return guess; } } return ViewLayer::UnknownPlacement; } double PCBSketchWidget::getWireStrokeWidth(Wire * wire, double wireWidth) { double w, h; wire->originalConnectorDimensions(w, h); if (wireWidth < Wire::THIN_TRACE_WIDTH) { wire->setConnectorDimensions(qMin(w, wireWidth + 1), qMin(w, wireWidth + 1)); } if (wireWidth < Wire::STANDARD_TRACE_WIDTH) { wire->setConnectorDimensions(qMin(w, wireWidth + 1.5), qMin(w, wireWidth + 1.5)); } else { wire->setConnectorDimensions(w, h); } return wireWidth + 6; } Wire * PCBSketchWidget::createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement spec) { if (spec == ViewLayer::UnknownPlacement) { spec = wireViewLayerPlacement(connectorItem); } viewGeometry.setPCBTrace(true); Wire * wire = SketchWidget::createTempWireForDragging(fromWire, wireModel, connectorItem, viewGeometry, spec); if (fromWire == NULL) { wire->setColorString(traceColor(connectorItem), 1.0, false); double traceWidth = getTraceWidth(); double minDim = connectorItem->minDimension(); if (minDim < traceWidth) { traceWidth = getSmallerTraceWidth(minDim); } wire->setWireWidth(traceWidth, this, getWireStrokeWidth(wire, traceWidth)); wire->setProperty(FakeTraceProperty, true); } else { wire->setColorString(fromWire->colorString(), fromWire->opacity(), false); wire->setWireWidth(fromWire->width(), this, fromWire->hoverStrokeWidth()); } return wire; } void PCBSketchWidget::prereleaseTempWireForDragging(Wire* wire) { if (wire->property(PCBSketchWidget::FakeTraceProperty).toBool()) { // make it not look like a trace, or modifyNewWireConnections will create the wrong kind of wire wire->setWireFlags(0); } } void PCBSketchWidget::rotatePartLabels(double degrees, QTransform & transform, QPointF center, QUndoCommand * parentCommand) { QList savedValues = m_savedItems.values(); /* QSet boards; foreach (ItemBase * itemBase, savedValues) { if (Board::isBoard(itemBase)) { boards.insert(itemBase); } } if (boards.count() == 0) return; QRectF bbr; foreach (ItemBase * board, boards.values()) { bbr |= board->sceneBoundingRect(); } */ foreach (QGraphicsItem * item, scene()->items()) { PartLabel * partLabel = dynamic_cast(item); if (partLabel == NULL) continue; if (!partLabel->isVisible()) continue; //if (!bbr.intersects(partLabel->sceneBoundingRect())) continue; // if the part is on the board and the label is off the board, this does not rotate if (!savedValues.contains(partLabel->owner()->layerKinChief())) continue; QPointF offset = partLabel->pos() - partLabel->owner()->pos(); new MoveLabelCommand(this, partLabel->owner()->id(), partLabel->pos(), offset, partLabel->pos(), offset, parentCommand); new RotateFlipLabelCommand(this, partLabel->owner()->id(), degrees, 0, parentCommand); QPointF p = GraphicsUtils::calcRotation(transform, center, partLabel->pos(), partLabel->boundingRect().center()); ViewGeometry vg; partLabel->owner()->calcRotation(transform, center, vg); new MoveLabelCommand(this, partLabel->owner()->id(), p, p - vg.loc(), p, p - vg.loc(), parentCommand); } } QString PCBSketchWidget::characterizeGroundFill(ViewLayer::ViewLayerID whichGroundPlane) { QString result = GroundPlane::fillTypeNone; bool gotOne = false; int boardCount; ItemBase * board = findSelectedBoard(boardCount); foreach (QGraphicsItem * item, scene()->collidingItems(board)) { GroundPlane * gp = dynamic_cast(item); if (gp == NULL) continue; if (gp->viewLayerID() == whichGroundPlane) { gotOne = true; break; } } if (!gotOne) return result; foreach (QGraphicsItem * item, scene()->items()) { GroundPlane * gp = dynamic_cast(item); if (gp == NULL) continue; if (gp->viewLayerID() != whichGroundPlane) continue; QString fillType = gp->prop("fillType"); if (fillType.isEmpty()) { // old style fill with no property return GroundPlane::fillTypeGround; } if (fillType == GroundPlane::fillTypeGround) { // assumes multiple fill types are not possible return fillType; } if (fillType == GroundPlane::fillTypePlain) { // assumes multiple fill types are not possible return fillType; } result = fillType; } return result; } void PCBSketchWidget::setUpColor(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Wire * wire, QUndoCommand * parentCommand) { QString tc = traceColor(fromConnectorItem); new WireColorChangeCommand(this, wire->id(), tc, tc, 1.0, 1.0, parentCommand); double traceWidth = getTraceWidth(); if (autorouteTypePCB()) { double minDim = qMin(fromConnectorItem->minDimension(), toConnectorItem->minDimension()); if (minDim < traceWidth) { traceWidth = getSmallerTraceWidth(minDim); } } new WireWidthChangeCommand(this, wire->id(), traceWidth, traceWidth, parentCommand); } ViewGeometry::WireFlag PCBSketchWidget::getTraceFlag() { return ViewGeometry::PCBTraceFlag; } void PCBSketchWidget::postImageSlot(GroundPlaneGenerator * gpg, QImage * copperImage, QImage * boardImage, QGraphicsItem * board, QList * rects) { if (m_groundFillSeeds == NULL) return; ViewLayer::ViewLayerID viewLayerID = (gpg->layerName() == "groundplane") ? ViewLayer::Copper0 : ViewLayer::Copper1; QRectF boardRect = board->sceneBoundingRect(); foreach (ConnectorItem * connectorItem, *m_groundFillSeeds) { if (connectorItem->attachedToViewLayerID() != viewLayerID) continue; if (connectorItem->attachedToItemType() == ModelPart::Wire) continue; if (!connectorItem->attachedTo()->isEverVisible()) continue; //connectorItem->debugInfo("post image b"); QRectF r = connectorItem->sceneBoundingRect(); //DebugDialog::debug("pb", r); QRectF check = r; check.setLeft(r.right()); check.setRight(r.right() + r.width()); bool checkRight = !hasNeighbor(connectorItem, viewLayerID, check); check = r; check.setLeft(r.left() - r.width()); check.setRight(r.left()); bool checkLeft = !hasNeighbor(connectorItem, viewLayerID, check); check = r; check.setTop(r.bottom()); check.setBottom(r.bottom() + r.height()); bool checkDown = !hasNeighbor(connectorItem, viewLayerID, check); check = r; check.setTop(r.top() - r.width()); check.setBottom(r.top()); bool checkUp = !hasNeighbor(connectorItem, viewLayerID, check); double x1 = (r.left() - boardRect.left()) * copperImage->width() / boardRect.width(); double x2 = (r.right() - boardRect.left()) * copperImage->width() / boardRect.width(); double y1 = (r.top() - boardRect.top()) * copperImage->height() / boardRect.height(); double y2 = (r.bottom() - boardRect.top()) * copperImage->height() / boardRect.height(); double w = x2 - x1; double h = y2 - y1; double cw = w / 4; double ch = h / 4; int cx = (x1 + x2) /2; int cy = (y1 + y2) /2; int rad = qFloor(connectorItem->calcClipRadius() * copperImage->width() / boardRect.width()); double borderl = qMax(0.0, x1 - w); double borderr = qMin(x2 + w, copperImage->width()); double bordert = qMax(0.0, y1 - h); double borderb = qMin(y2 + h, copperImage->height()); // check left, up, right, down for groundplane, and if it's there draw to it from the connector if (checkUp){ for (int y = y1; y > bordert; y--) { if ((copperImage->pixel(cx, y) & 0xffffff) || (boardImage->pixel(cx, y) == 0xff000000)) { QRectF s(cx - cw, y - 1, cw + cw, cy - y - rad); rects->append(s); break; } } } if (checkDown) { for (int y = y2; y < borderb; y++) { if ((copperImage->pixel(cx, y) & 0xffffff) || (boardImage->pixel(cx, y) == 0xff000000)) { QRectF s(cx - cw, cy + rad, cw + cw, y - cy - rad); rects->append(s); break; } } } if (checkLeft) { for (int x = x1; x > borderl; x--) { if ((copperImage->pixel(x, cy) & 0xffffff) || (boardImage->pixel(x, cy) == 0xff000000)) { QRectF s(x - 1, cy - ch, cx - x - rad, ch + ch); rects->append(s); break; } } } if (checkRight) { for (int x = x2; x < borderr; x++) { if ((copperImage->pixel(x, cy) & 0xffffff) || (boardImage->pixel(x, cy) == 0xff000000)) { QRectF s(cx + rad, cy - ch, x - cx - rad, ch + ch); rects->append(s); break; } } } DebugDialog::debug(QString("x1:%1 y1:%2 x2:%3 y2:%4").arg(x1).arg(y1).arg(x2).arg(y2)); } } bool PCBSketchWidget::hasNeighbor(ConnectorItem * connectorItem, ViewLayer::ViewLayerID viewLayerID, const QRectF & r) { foreach (QGraphicsItem * item, scene()->items(r)) { ConnectorItem * ci = dynamic_cast(item); if (ci != NULL) { if (ci->attachedToViewLayerID() != viewLayerID) continue; if (!ci->attachedTo()->isEverVisible()) continue; if (ci == connectorItem) continue; return true; } TraceWire * traceWire = dynamic_cast(item); if (traceWire != NULL) { if (!sameElectricalLayer2(traceWire->viewLayerID(), viewLayerID)) continue; if (!traceWire->isTraceType(getTraceFlag())) continue; return true; } } return false; } void PCBSketchWidget::collectThroughHole(QList & th, QList & pads, const LayerList & layerList) { foreach (QGraphicsItem * item, scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!connectorItem->attachedTo()->isVisible()) continue; if (!layerList.contains(connectorItem->attachedToViewLayerID())) continue; if (connectorItem->attachedTo()->moduleID().endsWith(ModuleIDNames::PadModuleIDName)) { pads.append(connectorItem); continue; } if (connectorItem->attachedTo()->modelPart()->flippedSMD()) { pads.append(connectorItem); continue; } th << connectorItem; } } void PCBSketchWidget::hideCopperLogoItems(QList & copperLogoItems) { foreach (QGraphicsItem * item, this->items()) { CopperLogoItem * logoItem = dynamic_cast(item); if (logoItem && logoItem->isVisible()) { copperLogoItems.append(logoItem); logoItem->setVisible(false); } } } void PCBSketchWidget::hideHoles(QList & holes) { foreach (QGraphicsItem * item, this->items()) { ItemBase * itemBase = dynamic_cast(item); // for some reason the layerkin of the hole doesn't have a modelPart->itemType() == ModelPart::Hole if (itemBase && itemBase->isVisible() && itemBase->layerKinChief()->modelPart()->itemType() == ModelPart::Hole) { holes.append(itemBase); itemBase->setVisible(false); } } } void PCBSketchWidget::restoreCopperLogoItems(QList & copperLogoItems) { foreach (ItemBase * logoItem, copperLogoItems) { logoItem->setVisible(true); } } void PCBSketchWidget::clearGroundFillSeeds() { QList trueSeeds; int boardCount; ItemBase * board = findSelectedBoard(boardCount); if (board == NULL) return; foreach (QGraphicsItem * item, scene()->collidingItems(board)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (connectorItem->attachedToItemType() == ModelPart::CopperFill) continue; if (connectorItem->isGroundFillSeed()) { trueSeeds.append(connectorItem); continue; } } if (trueSeeds.count() == 0) return; GroundFillSeedCommand * command = new GroundFillSeedCommand(this, NULL); command->setText(tr("Clear ground fill seeds")); foreach (ConnectorItem * connectorItem, trueSeeds) { command->addItem(connectorItem->attachedToID(), connectorItem->connectorSharedID(), false); } m_undoStack->waitPush(command, PropChangeDelay); } void PCBSketchWidget::setGroundFillSeeds() { setGroundFillSeeds(""); } void PCBSketchWidget::setGroundFillSeeds(const QString & intro) { QList seeds; collectGroundFillSeeds(seeds, true); GroundFillSeedDialog gfsd(this, seeds, intro, NULL); int result = gfsd.exec(); if (result == QDialog::Accepted) { GroundFillSeedCommand * command = NULL; QList results; gfsd.getResults(results); bool checked = false; for (int i = 0; i < seeds.count(); i++) { ConnectorItem * ci = seeds.at(i); bool isSeed = results.at(i); checked |= isSeed; if (isSeed != ci->isGroundFillSeed()) { if (command == NULL) { command = new GroundFillSeedCommand(this, NULL); } command->addItem(ci->attachedToID(), ci->connectorSharedID(), isSeed); } } if (command) { m_undoStack->push(command); } if (gfsd.getFill()) { if (checked) emit groundFillSignal(); else emit copperFillSignal(); } } } bool PCBSketchWidget::collectGroundFillSeeds(QList & seeds, bool includePotential) { QList trueSeeds; QList potentialSeeds; int boardCount; ItemBase * board = findSelectedBoard(boardCount); if (board == NULL) return false; foreach (QGraphicsItem * item, scene()->collidingItems(board)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (connectorItem->attachedToItemType() == ModelPart::CopperFill) continue; if (connectorItem->isGroundFillSeed()) { trueSeeds.append(connectorItem); continue; } if (connectorItem->isGrounded()) { potentialSeeds.append(connectorItem); } } for (int ix = 0; ix < trueSeeds.count(); ix++) { ConnectorItem * ci = trueSeeds.at(ix); QList cis; cis.append(ci); ConnectorItem::collectEqualPotential(cis, true, ViewGeometry::NoFlag); foreach (ConnectorItem * eq, cis) { if (eq != ci) trueSeeds.removeAll(eq); potentialSeeds.removeAll(eq); } } for (int ix = 0; ix < potentialSeeds.count(); ix++) { ConnectorItem * ci = potentialSeeds.at(ix); QList cis; cis.append(ci); ConnectorItem::collectEqualPotential(cis, true, ViewGeometry::NoFlag); foreach (ConnectorItem * eq, cis) { if (eq != ci) potentialSeeds.removeAll(eq); } } seeds.append(trueSeeds); if (trueSeeds.count() == 0 || includePotential) { seeds.append(potentialSeeds); } return trueSeeds.count() > 0; } void PCBSketchWidget::shiftHoles() { // vias and holes before version 0.7.3 did not have offset VersionThing versionThingOffset; versionThingOffset.majorVersion = 0; versionThingOffset.minorVersion = 7; versionThingOffset.minorSubVersion = 2; versionThingOffset.releaseModifier = "b"; VersionThing versionThingFz; Version::toVersionThing(m_sketchModel->fritzingVersion(), versionThingFz); bool doShift = !Version::greaterThan(versionThingOffset, versionThingFz); if (!doShift) return; foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; switch (itemBase->itemType()) { case ModelPart::Via: case ModelPart::Hole: itemBase->setPos(itemBase->pos().x() - (Hole::OffsetPixels / 2), itemBase->pos().y() - (Hole::OffsetPixels / 2)); break; default: continue; } } } bool PCBSketchWidget::canAlignToCenter(ItemBase * itemBase) { return qobject_cast(itemBase) != NULL; } int PCBSketchWidget::selectAllItemType(ModelPart::ItemType itemType, const QString & typeName) { int boardCount; ItemBase * board = findSelectedBoard(boardCount); if (boardCount == 0 && autorouteTypePCB()) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use this selection operation.")); return 0; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please click on a PCB first--this selection operation only works for one board at a time.")); return 0; } QSet itemBases; foreach (QGraphicsItem * item, (board == NULL ? scene()->items() : scene()->collidingItems(board))) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->itemType() != itemType) continue; itemBases.insert(itemBase->layerKinChief()); } return selectAllItems(itemBases, QObject::tr("Select all %1").arg(typeName)); } void PCBSketchWidget::selectAllWires(ViewGeometry::WireFlag flag) { int boardCount; ItemBase * board = findSelectedBoard(boardCount); if (boardCount == 0 && autorouteTypePCB()) { QMessageBox::critical(this, tr("Fritzing"), tr("Your sketch does not have a board yet! Please add a PCB in order to use this selection operation.")); return; } if (board == NULL) { QMessageBox::critical(this, tr("Fritzing"), tr("Please click on a PCB first--this selection operation only works for one board at a time.")); return; } QList items = scene()->collidingItems(board); selectAllWiresFrom(flag, items); } ViewLayer::ViewLayerPlacement PCBSketchWidget::defaultViewLayerPlacement(ModelPart * modelPart) { if (modelPart == NULL || boardLayers() == 2) return SketchWidget::defaultViewLayerPlacement(modelPart); if (modelPart->flippedSMD()) return ViewLayer::NewBottom; if (modelPart->moduleID() == ModuleIDNames::GroundPlaneModuleIDName) return ViewLayer::NewBottom; return SketchWidget::defaultViewLayerPlacement(modelPart); } QString PCBSketchWidget::checkDroppedModuleID(const QString & moduleID) { if (moduleID.endsWith(ModuleIDNames::CopperBlockerModuleIDName)) { if (dropOnBottom()) return ModuleIDNames::Copper0BlockerModuleIDName; return ModuleIDNames::Copper1BlockerModuleIDName; } if (moduleID.endsWith(ModuleIDNames::PadModuleIDName)) { if (dropOnBottom()) return ModuleIDNames::Copper0PadModuleIDName; return ModuleIDNames::PadModuleIDName; } if (moduleID.endsWith(ModuleIDNames::RectanglePCBModuleIDName)) { if (boardLayers() == 2) return ModuleIDNames::TwoSidedRectanglePCBModuleIDName; return ModuleIDNames::RectanglePCBModuleIDName; } if (moduleID.endsWith(ModuleIDNames::EllipsePCBModuleIDName)) { if (boardLayers() == 2) return ModuleIDNames::TwoSidedEllipsePCBModuleIDName; return ModuleIDNames::EllipsePCBModuleIDName; } return moduleID; } void PCBSketchWidget::convertToVia(ConnectorItem * lastHoverEnterConnectorItem) { Wire * wire = qobject_cast(lastHoverEnterConnectorItem->attachedTo()); if (wire == NULL) return; this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(QObject::tr("Convert to Via")); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); double ringThickness, holeSize; getViaSize(ringThickness, holeSize); QPointF p = lastHoverEnterConnectorItem->sceneAdjustedTerminalPoint(NULL); double d = ringThickness + (holeSize / 2) + Via::OffsetPixels; QPointF loc(p.x() - d, p.y() - d); long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(loc); new AddItemCommand(this, BaseCommand::CrossView, ModuleIDNames::ViaModuleIDName, wire->viewLayerPlacement(), viewGeometry, newID, true, -1, parentCommand); QList connectorItems; connectorItems.append(lastHoverEnterConnectorItem); for (int i = 0; i < connectorItems.count(); i++) { ConnectorItem * from = connectorItems.at(i); foreach (ConnectorItem * to, from->connectedToItems()) { Wire * w = qobject_cast(to->attachedTo()); if (w != NULL && w->isTraceType(getTraceFlag())) { if (!connectorItems.contains(to)) { connectorItems.append(to); } } } } foreach (ConnectorItem * from, connectorItems) { foreach (ConnectorItem * to, from->connectedToItems()) { Wire * w = qobject_cast(to->attachedTo()); if (w != NULL && w->isTraceType(getTraceFlag())) { new ChangeConnectionCommand(this, BaseCommand::CrossView, from->attachedToID(), from->connectorSharedID(), to->attachedToID(), to->connectorSharedID(), ViewLayer::specFromID(w->viewLayerID()), false, parentCommand); } } } foreach (ConnectorItem * from, connectorItems) { new ChangeConnectionCommand(this, BaseCommand::CrossView, from->attachedToID(), from->connectorSharedID(), newID, "connector0", ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); } SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->addRedo(newID); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } void PCBSketchWidget::convertToBendpoint() { ItemBase * itemBase = NULL; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * candidate = dynamic_cast(item); if (candidate == NULL) continue; if (itemBase == NULL) itemBase = candidate->layerKinChief(); else if (candidate->layerKinChief() != itemBase) return; } Via * via = dynamic_cast(itemBase); if (via == NULL) return; QList viaConnectorItems; viaConnectorItems << via->connectorItem(); if (via->connectorItem()->getCrossLayerConnectorItem()) { viaConnectorItems << via->connectorItem()->getCrossLayerConnectorItem(); } QList targets; int copper0 = 0; int copper1 = 0; bool copper0Only = false; bool copper1Only = false; foreach (ConnectorItem * viaConnectorItem, viaConnectorItems) { foreach (ConnectorItem * connectorItem, viaConnectorItem->connectedToItems()) { Wire * wire = qobject_cast(connectorItem->attachedTo()); if (wire == NULL) continue; if (wire->getRatsnest()) continue; if (!wire->isTraceType(getTraceFlag())) continue; bool gotOne = false;; if (wire->viewLayerID() == ViewLayer::Copper0Trace) { copper0++; gotOne = true; } else if (wire->viewLayerID() == ViewLayer::Copper1Trace) { copper1++; gotOne = true; } if (!gotOne) continue; targets.append(connectorItem); QList wires; QList ends; wire->collectChained(wires, ends); foreach (ConnectorItem * end, ends) { if (end->getCrossLayerConnectorItem() == NULL) { if (ViewLayer::copperLayers(ViewLayer::NewTop).contains(end->attachedToViewLayerID())) { copper1Only = true; } else { copper0Only = true; } } } } } if (copper0Only && copper1Only) { QMessageBox::warning(this, tr("Fritzing"), tr("Unable to convert this via to a bendpoint because it is connected to a part that is only on the bottom layer and another part that is only on the top layer.")); return; } this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(QObject::tr("Convert Via to Bendpoint")); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); foreach (ConnectorItem * target, targets) { new ChangeConnectionCommand(this, BaseCommand::CrossView, target->attachedToID(), target->connectorSharedID(), via->id(), via->connectorItem()->connectorSharedID(), ViewLayer::specFromID(target->attachedToViewLayerID()), false, parentCommand); } ViewLayer::ViewLayerID dest = ViewLayer::Copper0Trace; if (copper1Only) { dest = ViewLayer::Copper1Trace; } else if (!copper0Only) { if (copper1 > copper0) { dest = ViewLayer::Copper1Trace; } } if (copper0 > 0 && copper1 > 0) { foreach (ConnectorItem * target, targets) { if (target->attachedToViewLayerID() == dest) continue; Wire * wire = qobject_cast(target->attachedTo()); QList wires; QList ends; wire->collectChained(wires, ends); foreach (Wire * w, wires) { new ChangeLayerCommand(this, w->id(), w->zValue(), m_viewLayers.value(dest)->nextZ(), w->viewLayerID(), dest, parentCommand); } } } ConnectorItem * from = targets.at(0); for (int j = 1; j < targets.count(); j++) { ConnectorItem * to = targets.at(j); new ChangeConnectionCommand(this, BaseCommand::CrossView, from->attachedToID(), from->connectorSharedID(), to->attachedToID(), to->connectorSharedID(), ViewLayer::specFromID(dest), true, parentCommand); } makeDeleteItemCommand(via, BaseCommand::CrossView, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } bool PCBSketchWidget::canConnect(Wire * from, ItemBase * to) { to = to->layerKinChief(); QList kin; kin.append(to); kin.append(to->layerKin()); foreach (ItemBase * itemBase, kin) { if (!ViewLayer::isCopperLayer(itemBase->viewLayerID())) continue; if (ViewLayer::canConnect(from->viewLayerID(), itemBase->viewLayerID())) return true; } return false; } QString PCBSketchWidget::makePasteMask(const QString & svgMask, ItemBase * board, double dpi, const LayerList & maskLayerIDs) { QList throughHoles; QList pads; collectThroughHole(throughHoles, pads, maskLayerIDs); if (pads.count() == 0) return ""; QRectF boardRect = board->sceneBoundingRect(); QList connectorRects; foreach (ConnectorItem * connectorItem, throughHoles) { QRectF r = connectorItem->sceneBoundingRect(); QRectF s((r.left() - boardRect.left()) * dpi / GraphicsUtils::SVGDPI, (r.top() - boardRect.top()) * dpi / GraphicsUtils::SVGDPI, r.width() * dpi / GraphicsUtils::SVGDPI, r.height() * dpi / GraphicsUtils::SVGDPI); connectorRects << s; } QDomDocument doc; doc.setContent(svgMask); QList leaves; QDomElement root = doc.documentElement(); TextUtils::collectLeaves(root, leaves); int ix = 0; foreach (QDomElement element, leaves) { element.setAttribute("id", ix++); } QSvgRenderer renderer; renderer.load(doc.toByteArray()); foreach (QDomElement element, leaves) { QString id = element.attribute("id"); QRectF bounds = renderer.boundsOnElement(id); QRectF leafRect = renderer.matrixForElement(id).mapRect(bounds); QPointF leafCenter = leafRect.center(); foreach (QRectF r, connectorRects) { if (!leafRect.intersects(r)) continue; if (!r.contains(leafCenter)) continue; QPointF rCenter = r.center(); if (!leafRect.contains(rCenter)) continue; element.setTagName("g"); break; } } return doc.toString(); } int PCBSketchWidget::checkLoadedTraces() { ProcessEventBlocker::processEvents(); scene()->clearSelection(); QList wires; QHash lines; foreach (QGraphicsItem * item, scene()->items()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (!wire->isTraceType(getTraceFlag())) continue; ConnectorItem * c0 = wire->connector0(); ConnectorItem * c1 = wire->connector1(); QPointF p0 = c0->sceneBoundingRect().center(); QPointF p1 = c1->sceneBoundingRect().center(); QLineF line(p0, p1); lines.insert(wire, line); } foreach (Wire * wire, lines.keys()) { QList already; wire->updateConnections(wire->connector0(), false, already); wire->updateConnections(wire->connector1(), false, already); } foreach (Wire * wire, lines.keys()) { QLineF line = wire->line(); QPointF l0 = wire->pos() + line.p1(); QPointF l1 = wire->pos() + line.p2(); QLineF oldLine = lines.value(wire); double d = 0.1; if (qAbs(oldLine.p1().x() - l0.x()) > d || qAbs(oldLine.p1().y() - l0.y()) > d || qAbs(oldLine.p2().x() - l1.x()) > d || qAbs(oldLine.p2().y() - l1.y()) > d) { wires.append(wire); wire->debugInfo2(QString("wire moved from:\n%1,%2 %3,%4\n%5,%6 %7,%8") .arg(oldLine.p1().x()).arg(oldLine.p1().y()).arg(oldLine.p2().x()).arg(oldLine.p2().y()) .arg(l0.x()).arg(l0.y()).arg(l1.x()).arg(l1.y()) ); } } foreach (Wire * wire, wires) { wire->setSelected(true); } return wires.count(); } bool PCBSketchWidget::hasCustomBoardShape() { QList boards = findBoard(); foreach (ItemBase * board, boards) { if (qobject_cast(board)) return true; } return false; } ViewLayer::ViewLayerPlacement PCBSketchWidget::getViewLayerPlacement(ModelPart * modelPart, QDomElement & instance, QDomElement & view, ViewGeometry & viewGeometry) { if (modelPart->flippedSMD()) { ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(view.attribute("layer")); if (ViewLayer::bottomLayers().contains(viewLayerID)) return ViewLayer::NewBottom; return ViewLayer::NewTop; } if (modelPart->itemType() == ModelPart::Part) { QDomElement views = view.parentNode().toElement(); QDomElement pcbview = views.firstChildElement("pcbView"); bool bottom = pcbview.attribute("bottom", "").compare("true") == 0; if (bottom) return ViewLayer::NewBottom; } return SketchWidget::getViewLayerPlacement(modelPart, instance, view, viewGeometry); } LayerList PCBSketchWidget::routingLayers(ViewLayer::ViewLayerPlacement spec) { LayerList layerList = ViewLayer::copperLayers(spec); layerList.removeOne(ViewLayer::GroundPlane0); layerList.removeOne(ViewLayer::GroundPlane1); return layerList; } bool PCBSketchWidget::attachedToBottomLayer(ConnectorItem * connectorItem) { return (connectorItem->attachedToViewLayerID() == ViewLayer::Copper0) || (connectorItem->attachedToViewLayerID() == ViewLayer::Copper0Trace); } bool PCBSketchWidget::attachedToTopLayer(ConnectorItem * connectorItem) { return (connectorItem->attachedToViewLayerID() == ViewLayer::Copper1) || (connectorItem->attachedToViewLayerID() == ViewLayer::Copper1Trace); } QHash PCBSketchWidget::getAutorouterSettings() { return m_autorouterSettings; } void PCBSketchWidget::setAutorouterSettings(QHash & autorouterSettings) { QList keys; keys << DRC::KeepoutSettingName << AutorouterSettingsDialog::AutorouteTraceWidth << Via::AutorouteViaHoleSize << Via::AutorouteViaRingThickness << GroundPlaneGenerator::KeepoutSettingName; foreach (QString key, keys) { m_autorouterSettings.insert(key, autorouterSettings.value(key, "")); } } void PCBSketchWidget::hidePartSilkscreen() { ItemBase * itemBase = NULL; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * candidate = dynamic_cast(item); if (candidate == NULL) continue; itemBase = candidate->layerKinChief(); break; } if (itemBase == NULL) return; QList itemBases; itemBases.append(itemBase); itemBases.append(itemBase->layerKin()); foreach (ItemBase * lkpi, itemBases) { if (lkpi->viewLayerID() == ViewLayer::Silkscreen1 || lkpi->viewLayerID() == ViewLayer::Silkscreen0) { bool layerHidden = lkpi->layerHidden(); QUndoCommand * parentCommand = new QUndoCommand(layerHidden ? tr("Show part silkscreen") : tr("Hide part silkscreen")); new HidePartLayerCommand(this, itemBase->id(), ViewLayer::Silkscreen0, layerHidden, !layerHidden, parentCommand); new HidePartLayerCommand(this, itemBase->id(), ViewLayer::Silkscreen1, layerHidden, !layerHidden, parentCommand); m_undoStack->push(parentCommand); break; } } } void PCBSketchWidget::fabQuote() { int boardCount = 0; double area = calcBoardArea(boardCount); QuoteDialog::setArea(area, boardCount); if (boardCount == 0) { QMessageBox::information(this, tr("Fritzing Fab Quote"), tr("Your sketch does not have a board yet. You cannot fabricate this sketch without a PCB part.")); return; } if (!QuoteDialog::quoteSucceeded()) { QMessageBox::information(this, tr("Fritzing Fab Quote"), tr("Sorry, http://fab.fritzing.org is not responding to the quote request. Please check your network connection and/or try again later.")); requestQuote(true); return; } m_quoteDialog = new QuoteDialog(true, this); requestQuote(true); m_quoteDialog->exec(); delete m_quoteDialog; m_quoteDialog = NULL; } void PCBSketchWidget::gotFabQuote(QNetworkReply * networkReply) { QNetworkAccessManager * manager = networkReply->manager(); QString count = manager->property("count").toString(); QStringList countArgs = count.split(","); int responseCode = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (responseCode == 200) { QString data(networkReply->readAll()); QStringList values = data.split(","); if (values.count() == countArgs.count()) { for (int ix = 0; ix < countArgs.count(); ix++) { QString value = values.at(ix); QString c = countArgs.at(ix); bool ok; int count = c.toInt(&ok); if (!ok) continue; double cost = value.toDouble(&ok); if (!ok) continue; QuoteDialog::setCountCost(ix, count, cost); } QuoteDialog::setQuoteSucceeded(true); } if (m_quoteDialog) m_quoteDialog->setText(); if (m_rolloverQuoteDialog) m_rolloverQuoteDialog->setText(); } else { } manager->deleteLater(); networkReply->deleteLater(); } void PCBSketchWidget::requestQuote(bool byUser) { int boardCount; double area = calcBoardArea(boardCount); QuoteDialog::setArea(area, boardCount); QString paramString = Version::makeRequestParamsString(false); QNetworkAccessManager * manager = new QNetworkAccessManager(this); QString countArgs = QuoteDialog::countArgs(); manager->setProperty("count", countArgs); QString filename = QUrl::toPercentEncoding(filenameIf()); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(gotFabQuote(QNetworkReply *))); QString string = QString("http://fab.fritzing.org/fritzing-fab/quote%1&area=%2&count=%3&filename=%4&byuser=%5") .arg(paramString) .arg(area) .arg(countArgs) .arg(filename) .arg(byUser) ; QuoteDialog::setQuoteSucceeded(false); manager->get(QNetworkRequest(QUrl(string))); } double PCBSketchWidget::calcBoardArea(int & boardCount) { QList boards = findBoard(); boardCount = boards.count(); if (boardCount == 0) { return 0; } double area = 0; foreach (ItemBase * board, boards) { area += GraphicsUtils::pixels2mm(board->boundingRect().width(), GraphicsUtils::SVGDPI) * GraphicsUtils::pixels2mm(board->boundingRect().height(), GraphicsUtils::SVGDPI) / 100; } return area; } PaletteItem* PCBSketchWidget::addPartItem(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, PaletteItem * paletteItem, bool doConnectors, bool & ok, ViewLayer::ViewID viewID, bool temporary) { if (viewID == ViewLayer::PCBView && Board::isBoard(modelPart)) { requestQuoteSoon(); } return SketchWidget::addPartItem(modelPart, viewLayerPlacement, paletteItem, doConnectors, ok, viewID, temporary); } void PCBSketchWidget::requestQuoteSoon() { m_requestQuoteTimer.stop(); m_requestQuoteTimer.start(); } void PCBSketchWidget::requestQuoteNow() { m_requestQuoteTimer.stop(); requestQuote(false); } ItemBase * PCBSketchWidget::resizeBoard(long itemID, double mmW, double mmH) { ItemBase * itemBase = SketchWidget::resizeBoard(itemID, mmW, mmH); if (itemBase != NULL && Board::isBoard(itemBase)) requestQuoteSoon(); return itemBase; } QDialog * PCBSketchWidget::quoteDialog(QWidget * parent) { if (m_rolloverQuoteDialog == NULL) { m_rolloverQuoteDialog = new QuoteDialog(false, parent); requestQuote(false); } m_rolloverQuoteDialog->setText(); return m_rolloverQuoteDialog; } double PCBSketchWidget::getKeepoutMils() { QSettings settings; QString keepoutString = m_autorouterSettings.value(GroundPlaneGenerator::KeepoutSettingName); if (keepoutString.isEmpty()) { keepoutString = settings.value(GroundPlaneGenerator::KeepoutSettingName, "").toString(); } bool ok; double mils = TextUtils::convertToInches(keepoutString, &ok, false); if (ok) { mils *= 1000; // convert from inches } else { mils = GroundPlaneGenerator::KeepoutDefaultMils; } return mils; } void PCBSketchWidget::setGroundFillKeepout() { bool ok; double mils = QInputDialog::getInt(this, tr("Enter Keepout"), tr("Keepout is in mils (.001 inches).\n\n") + tr("Note that due to aliasing, distances may be too short by up to 2 mils\n") + tr("so you may want to increase the keepout value by that much.\n\n") + tr("10 mils is a good default choice.\n\n") + tr("Enter keepout value:"), qRound(getKeepoutMils()), 0, 10 * 1000, 1, &ok); if (!ok) return; QString keepoutString = QString("%1in").arg(mils / 1000); m_autorouterSettings.insert(GroundPlaneGenerator::KeepoutSettingName, keepoutString); QSettings settings; settings.setValue(GroundPlaneGenerator::KeepoutSettingName, keepoutString); } void PCBSketchWidget::setViewFromBelow(bool viewFromBelow) { if (m_viewFromBelow == viewFromBelow) return; QSet chiefs; foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; ViewLayer * viewLayer = m_viewLayers.value(itemBase->viewLayerID(), NULL); if (viewLayer == NULL) continue; double newZ = viewLayer->getZFromBelow(itemBase->z(), viewFromBelow); itemBase->setZValue(newZ); itemBase->getViewGeometry().setZ(newZ); chiefs.insert(itemBase->layerKinChief()); } foreach (ItemBase * chief, chiefs) { chief->figureHover(); } foreach (ViewLayer * viewLayer, m_viewLayers.values()) { viewLayer->setFromBelow(viewFromBelow); } // enable only with multiple layers? // active layer should be secondary mechanism? SketchWidget::setViewFromBelow(viewFromBelow); } void PCBSketchWidget::getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement & viewLayerPlacement) { if (ResizableBoard::isBoard(modelPart)) { viewLayerPlacement = ViewLayer::NewTop; return; } viewLayerPlacement = defaultViewLayerPlacement(modelPart); // top for a two layer board if (boardLayers() == 2) { if (dropOnBottom()) { viewLayerPlacement = ViewLayer::NewBottom; } return; } } bool PCBSketchWidget::dropOnBottom() { if (boardLayers() == 1) return true; if (!layerIsActive(ViewLayer::Copper0)) return false; if (!layerIsActive(ViewLayer::Copper1)) return true; return viewFromBelow(); } bool PCBSketchWidget::updateOK(ConnectorItem * c1, ConnectorItem * c2) { // don't update if both connectors belong to parts--this isn't legit in schematic or pcb view if (c1->attachedTo()->wireFlags()) return true; return c2->attachedTo()->wireFlags() != 0; } fritzing-app-0.9.3b/src/sketch/pcbsketchwidget.h000066400000000000000000000250161272363601300217010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6979 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 23:20:35 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #ifndef PCBSKETCHWIDGET_H #define PCBSKETCHWIDGET_H #include "sketchwidget.h" #include "../dialogs/quotedialog.h" #include #include #include /////////////////////////////////////////////// class PCBSketchWidget : public SketchWidget { Q_OBJECT public: PCBSketchWidget(ViewLayer::ViewID, QWidget *parent=0); void addViewLayers(); bool canDeleteItem(QGraphicsItem * item, int count); bool canCopyItem(QGraphicsItem * item, int count); void createTrace(Wire *, bool useLastWireColor); void excludeFromAutoroute(bool exclude); void selectAllExcludedTraces(); void selectAllIncludedTraces(); bool hasAnyNets(); void forwardRoutingStatus(const RoutingStatus &); void addDefaultParts(); void showEvent(QShowEvent * event); void initWire(Wire *, int penWidth); virtual bool autorouteTypePCB(); virtual double getKeepout(); void setKeepout(double mils); void resetKeepout(); virtual const QString & traceColor(ConnectorItem *); const QString & traceColor(ViewLayer::ViewLayerPlacement); virtual void ensureTraceLayersVisible(); virtual void ensureTraceLayerVisible(); bool canChainMultiple(); void setNewPartVisible(ItemBase *); void setClipEnds(class ClipableWire *, bool); void showGroundTraces(QList & seeds, bool show); virtual double getLabelFontSizeTiny(); virtual double getLabelFontSizeSmall(); virtual double getLabelFontSizeMedium(); virtual double getLabelFontSizeLarge(); ViewLayer::ViewLayerID getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement); QList findBoard(); ItemBase * findSelectedBoard(int & boardCount); ItemBase * findBoardBeneath(ItemBase *); void setBoardLayers(int, bool redraw); void swapLayers(ItemBase * itemBase, int newLayers, QUndoCommand * parentCommand); void loadFromModelParts(QList & modelParts, BaseCommand::CrossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList & newIDs); virtual bool isInLayers(ConnectorItem *, ViewLayer::ViewLayerPlacement); bool routeBothSides(); bool sameElectricalLayer2(ViewLayer::ViewLayerID, ViewLayer::ViewLayerID); void changeTraceLayer(ItemBase *, bool force, QUndoCommand * parentCommand); void changeLayer(long id, double z, ViewLayer::ViewLayerID viewLayerID); bool acceptsTrace(const ViewGeometry & viewGeometry); ItemBase * placePartDroppedInOtherView(ModelPart *, ViewLayer::ViewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin); void autorouterSettings(); void getViaSize(double & ringThickness, double & holeSize); void deleteItem(ItemBase *, bool deleteModelPart, bool doEmit, bool later); double getTraceWidth(); virtual double getAutorouterTraceWidth(); void getBendpointWidths(class Wire *, double w, double & w1, double & w2, bool & negativeOffsetRect); double getSmallerTraceWidth(double minDim); bool groundFill(bool fillGroundTraces, ViewLayer::ViewLayerID, QUndoCommand * parentCommand); void setGroundFillSeeds(); void clearGroundFillSeeds(); QString generateCopperFillUnit(ItemBase * itemBase, QPointF whereToStart); double getWireStrokeWidth(Wire *, double wireWidth); ItemBase * addCopperLogoItem(ViewLayer::ViewLayerPlacement viewLayerPlacement); QString characterizeGroundFill(ViewLayer::ViewLayerID); ViewGeometry::WireFlag getTraceFlag(); void hideCopperLogoItems(QList &); void restoreCopperLogoItems(QList &); void hideHoles(QList &); QString makePasteMask(const QString & svgMask, ItemBase * board, double dpi, const LayerList & maskLayerIDs); int selectAllItemType(ModelPart::ItemType, const QString & typeName); bool isBoardLayerChange(ItemBase * itemBase, const QString & newModuleID, int & newLayers); void convertToVia(ConnectorItem * lastHoverEnterConnectorItem); void convertToBendpoint(); int checkLoadedTraces(); bool hasCustomBoardShape(); virtual QSizeF jumperItemSize(); virtual LayerList routingLayers(ViewLayer::ViewLayerPlacement); virtual bool attachedToTopLayer(ConnectorItem *); virtual bool attachedToBottomLayer(ConnectorItem *); QHash getAutorouterSettings(); void setAutorouterSettings(QHash &); void getDefaultViaSize(QString & ringThickness, QString & holeSize); void hidePartSilkscreen(); void fabQuote(); QDialog * quoteDialog(QWidget * parent); void setGroundFillKeepout(); void setViewFromBelow(bool); bool dropOnBottom(); ViewLayer::ViewLayerPlacement defaultViewLayerPlacement(ModelPart *); public slots: void resizeBoard(double w, double h, bool doEmit); void showLabelFirstTime(long itemID, bool show, bool doEmit); void changeBoardLayers(int layers, bool doEmit); ItemBase * resizeBoard(long id, double w, double h); public: enum CleanType { noClean, ninetyClean }; CleanType cleanType(); protected: void setWireVisible(Wire * wire); // void checkAutorouted(); ViewLayer::ViewLayerID multiLayerGetViewLayerID(ModelPart * modelPart, ViewLayer::ViewID, ViewLayer::ViewLayerPlacement, LayerList &); bool canChainWire(Wire *); bool canDragWire(Wire * wire); const QString & hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item); //bool modifyNewWireConnections(Wire * dragWire, ConnectorItem * fromOnWire, ConnectorItem * from, ConnectorItem * to, QUndoCommand * parentCommand); ViewLayer::ViewLayerID getDragWireViewLayerID(ConnectorItem *); bool canDropModelPart(ModelPart * modelPart); bool canCreateWire(Wire * dragWire, ConnectorItem * from, ConnectorItem * to); bool bothEndsConnected(Wire * wire, ViewGeometry::WireFlags, ConnectorItem * oneEnd, QList & wires, QList & partConnectorItems); void setUpColor(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Wire * wire, QUndoCommand * parentCommand); ConnectorItem * findNearestPartConnectorItem(ConnectorItem * fromConnectorItem); bool bothEndsConnectedAux(Wire * wire, ViewGeometry::WireFlags flag, ConnectorItem * oneEnd, QList & wires, QList & partConnectorItems, QList & visited); void getLabelFont(QFont &, QColor &, ItemBase *); double defaultGridSizeInches(); ViewLayer::ViewLayerID getLabelViewLayerID(ItemBase *); ViewLayer::ViewLayerPlacement wireViewLayerPlacement(ConnectorItem *); bool resizingJumperItemPress(ItemBase *); bool resizingJumperItemRelease(); void resizeJumperItem(); void dealWithDefaultParts(); bool connectorItemHasSpec(ConnectorItem * connectorItem, ViewLayer::ViewLayerPlacement spec); ViewLayer::ViewLayerPlacement createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to); Wire * createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement); void prereleaseTempWireForDragging(Wire*); void rotatePartLabels(double degrees, QTransform &, QPointF center, QUndoCommand * parentCommand); bool hasNeighbor(ConnectorItem * connectorItem, ViewLayer::ViewLayerID viewLayerID, const QRectF & r); void setGroundFillSeeds(const QString & intro); bool collectGroundFillSeeds(QList & seeds, bool includePotential); void shiftHoles(); void selectAllXTraces(bool autoroutable, const QString & cmdText, bool forPCB); bool canAlignToCenter(ItemBase *); void selectAllWires(ViewGeometry::WireFlag flag); QString checkDroppedModuleID(const QString & moduleID); bool canConnect(Wire * from, ItemBase * to); void collectThroughHole(QList & th, QList & pads, const LayerList &); ViewLayer::ViewLayerPlacement getViewLayerPlacement(ModelPart *, QDomElement & instance, QDomElement & view, ViewGeometry &); void requestQuote(bool byUser); double calcBoardArea(int & boardCount); PaletteItem* addPartItem(ModelPart * modelPart, ViewLayer::ViewLayerPlacement, PaletteItem * paletteItem, bool doConnectors, bool & ok, ViewLayer::ViewID, bool temporary); void requestQuoteSoon(); double getKeepoutMils(); bool updateOK(ConnectorItem *, ConnectorItem *); signals: void subSwapSignal(SketchWidget *, ItemBase *, const QString & newModuleID, ViewLayer::ViewLayerPlacement, long & newID, QUndoCommand * parentCommand); void boardDeletedSignal(); void groundFillSignal(); void copperFillSignal(); protected: static void calcDistances(Wire * wire, QList & ends); static void clearDistances(); static int calcDistance(Wire * wire, ConnectorItem * end, int distance, QList & distanceWires, bool & fromConnector0); static int calcDistanceAux(ConnectorItem * from, ConnectorItem * to, int distance, QList & distanceWires); protected slots: void alignJumperItem(class JumperItem *, QPointF &); void wireSplitSlot(class Wire*, QPointF newPos, QPointF oldPos, const QLineF & oldLine); void postImageSlot(class GroundPlaneGenerator *, QImage * copperImage, QImage * boardImage, QGraphicsItem * board, QList *); void gotFabQuote(QNetworkReply *); void requestQuoteNow(); void getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement &); protected: CleanType m_cleanType; QPointF m_jumperDragOffset; QPointer m_resizingJumperItem; QList * m_groundFillSeeds; QHash m_autorouterSettings; QPointer m_quoteDialog; QPointer m_rolloverQuoteDialog; QTimer m_requestQuoteTimer; protected: static QSizeF m_jumperItemSize; static const char * FakeTraceProperty; }; #endif fritzing-app-0.9.3b/src/sketch/schematicsketchwidget.cpp000066400000000000000000000470621272363601300234350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #include "schematicsketchwidget.h" #include "../debugdialog.h" #include "../items/virtualwire.h" #include "../items/symbolpaletteitem.h" #include "../items/tracewire.h" #include "../items/partlabel.h" #include "../connectors/connectoritem.h" #include "../waitpushundostack.h" #include "../items/moduleidnames.h" #include "../fsvgrenderer.h" #include "../utils/graphicsutils.h" #include "../version/version.h" #include QSizeF SchematicSketchWidget::m_jumperItemSize = QSizeF(0, 0); static QString SchematicTraceColor = "black"; static const double TraceHoverStrokeFactor = 3; static const double TraceWidthMils = 9.7222; static const double TraceWidthMilsOld = 33.3333; bool sameGround(ConnectorItem * c1, ConnectorItem * c2) { bool c1Grounded = c1->isGrounded(); bool c2Grounded = c2->isGrounded(); return (c1Grounded == c2Grounded); } /////////////////////////////////////////////////// SchematicSketchWidget::SchematicSketchWidget(ViewLayer::ViewID viewID, QWidget *parent) : PCBSketchWidget(viewID, parent) { m_oldSchematic = m_convertSchematic = false; m_shortName = QObject::tr("schem"); m_viewName = QObject::tr("Schematic View"); initBackgroundColor(); m_cleanType = ninetyClean; m_updateDotsTimer.setInterval(20); m_updateDotsTimer.setSingleShot(true); connect(&m_updateDotsTimer, SIGNAL(timeout()), this, SLOT(updateBigDots())); } void SchematicSketchWidget::addViewLayers() { setViewLayerIDs(ViewLayer::Schematic, ViewLayer::SchematicTrace, ViewLayer::Schematic, ViewLayer::SchematicRuler, ViewLayer::SchematicNote); addViewLayersAux(ViewLayer::layersForView(ViewLayer::SchematicView), ViewLayer::layersForViewFromBelow(ViewLayer::SchematicView)); } ViewLayer::ViewLayerID SchematicSketchWidget::getDragWireViewLayerID(ConnectorItem *) { return ViewLayer::SchematicTrace; } ViewLayer::ViewLayerID SchematicSketchWidget::getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewGeometry.getAnyTrace()) { return ViewLayer::SchematicTrace; } if (viewGeometry.getRatsnest()) { return ViewLayer::SchematicWire; } return SketchWidget::getWireViewLayerID(viewGeometry, viewLayerPlacement); } void SchematicSketchWidget::initWire(Wire * wire, int penWidth) { Q_UNUSED(penWidth); if (wire->getRatsnest()) { return; } wire->setPenWidth(getTraceWidth(), this, getTraceWidth() * TraceHoverStrokeFactor); wire->setColorString("black", 1.0, false); } bool SchematicSketchWidget::autorouteTypePCB() { return false; } void SchematicSketchWidget::tidyWires() { QList wires; QList visited; foreach (QGraphicsItem * item, scene()->selectedItems()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if ((wire->getViewGeometry().wireFlags() & ViewGeometry::SchematicTraceFlag) == 0) continue; if (visited.contains(wire)) continue; } } void SchematicSketchWidget::ensureTraceLayersVisible() { ensureLayerVisible(ViewLayer::SchematicTrace); } void SchematicSketchWidget::ensureTraceLayerVisible() { ensureLayerVisible(ViewLayer::SchematicTrace); } void SchematicSketchWidget::setClipEnds(ClipableWire * vw, bool) { vw->setClipEnds(false); } void SchematicSketchWidget::getBendpointWidths(Wire * wire, double width, double & bendpointWidth, double & bendpoint2Width, bool & negativeOffsetRect) { Q_UNUSED(wire); bendpointWidth = -width - 1; bendpoint2Width = width + ((m_oldSchematic) ? 3 : 1); negativeOffsetRect = true; } void SchematicSketchWidget::getLabelFont(QFont & font, QColor & color, ItemBase *) { font.setFamily("Droid Sans"); font.setPointSize(getLabelFontSizeSmall()); font.setBold(false); font.setItalic(false); color.setAlpha(255); color.setRgb(0); } void SchematicSketchWidget::setNewPartVisible(ItemBase * itemBase) { switch (itemBase->itemType()) { case ModelPart::Logo: if (itemBase->moduleID().contains("schematic", Qt::CaseInsensitive)) break; case ModelPart::Breadboard: case ModelPart::Jumper: case ModelPart::CopperFill: case ModelPart::Via: case ModelPart::Hole: // don't need to see the breadboard in the other views // but it's there so connections can be more easily synched between views itemBase->setVisible(false); itemBase->setEverVisible(false); return; default: if (itemBase->moduleID().endsWith(ModuleIDNames::PadModuleIDName)) { itemBase->setVisible(false); itemBase->setEverVisible(false); return; } break; } } bool SchematicSketchWidget::canDropModelPart(ModelPart * modelPart) { if (!SketchWidget::canDropModelPart(modelPart)) return false; switch (modelPart->itemType()) { case ModelPart::Logo: if (modelPart->moduleID().contains("schematic", Qt::CaseInsensitive)) return true; case ModelPart::Jumper: case ModelPart::CopperFill: case ModelPart::Board: case ModelPart::ResizableBoard: case ModelPart::Breadboard: case ModelPart::Via: case ModelPart::Hole: return false; case ModelPart::Symbol: case ModelPart::SchematicSubpart: return true; default: break; } if (modelPart->moduleID().endsWith(ModuleIDNames::SchematicFrameModuleIDName)) return true; if (modelPart->moduleID().endsWith(ModuleIDNames::PadModuleIDName)) return false; if (modelPart->moduleID().endsWith(ModuleIDNames::CopperBlockerModuleIDName)) return false; return PCBSketchWidget::canDropModelPart(modelPart); } bool SchematicSketchWidget::includeSymbols() { return true; } bool SchematicSketchWidget::hasBigDots() { return true; } void SchematicSketchWidget::updateBigDots() { QList connectorItems; foreach (QGraphicsItem * item, scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (connectorItem->attachedToItemType() != ModelPart::Wire) continue; TraceWire * traceWire = qobject_cast(connectorItem->attachedTo()); if (traceWire == NULL) continue; //DebugDialog::debug(QString("update big dot %1 %2").arg(traceWire->id()).arg(connectorItem->connectorSharedID())); connectorItems.append(connectorItem); } QList visited; foreach (ConnectorItem * connectorItem, connectorItems) { connectorItem->restoreColor(visited); } } void SchematicSketchWidget::changeConnection(long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, bool doEmit, bool updateConnections) { m_updateDotsTimer.stop(); SketchWidget::changeConnection(fromID, fromConnectorID, toID, toConnectorID, viewLayerPlacement, connect, doEmit, updateConnections); m_updateDotsTimer.start(); } void SchematicSketchWidget::setInstanceTitle(long itemID, const QString & oldText, const QString & newText, bool isUndoable, bool doEmit) { // isUndoable is true when setInstanceTitle is called from the infoview if (isUndoable) { SymbolPaletteItem * sitem = qobject_cast(findItem(itemID)); if (sitem && sitem->isOnlyNetLabel()) { setProp(sitem, "label", ItemBase::TranslatedPropertyNames.value("label"), oldText, newText, true); return; } } SketchWidget::setInstanceTitle(itemID, oldText, newText, isUndoable, doEmit); } void SchematicSketchWidget::setProp(ItemBase * itemBase, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, bool redraw) { if (prop =="label") { SymbolPaletteItem * sitem = qobject_cast(itemBase); if (sitem != NULL && sitem->isOnlyNetLabel()) { if (sitem->getLabel() == newValue) { return; } QUndoCommand * parentCommand = new QUndoCommand(); parentCommand->setText(tr("Change label from %1 to %2").arg(oldValue).arg(newValue)); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); QList done; foreach (ConnectorItem * toConnectorItem, sitem->connector0()->connectedToItems()) { Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (w == NULL) continue; if (done.contains(w)) continue; QList ends; removeWire(w, ends, done, parentCommand); } new SetPropCommand(this, itemBase->id(), "label", oldValue, newValue, true, parentCommand); new ChangeLabelTextCommand(this, itemBase->id(), oldValue, newValue, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); return; } } SketchWidget::setProp(itemBase, prop, trProp, oldValue, newValue, redraw); } void SchematicSketchWidget::setVoltage(double v, bool doEmit) { Q_UNUSED(doEmit); PaletteItem * item = getSelectedPart(); if (item == NULL) return; if (item->itemType() != ModelPart::Symbol) return; SymbolPaletteItem * sitem = qobject_cast(item); if (sitem == NULL) return; if (sitem->moduleID().compare("ground symbol", Qt::CaseInsensitive) == 0) return; if (v == sitem->voltage()) return; QUndoCommand * parentCommand = new QUndoCommand(); parentCommand->setText(tr("Change voltage from %1 to %2").arg(sitem->voltage()).arg(v)); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); QList done; foreach (ConnectorItem * toConnectorItem, sitem->connector0()->connectedToItems()) { Wire * w = qobject_cast(toConnectorItem->attachedTo()); if (w == NULL) continue; if (done.contains(w)) continue; QList ends; removeWire(w, ends, done, parentCommand); } new SetPropCommand(this, item->id(), "voltage", QString::number(sitem->voltage()), QString::number(v), true, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); } double SchematicSketchWidget::defaultGridSizeInches() { return GraphicsUtils::StandardSchematicSeparation10thinMils / 1000; } ViewLayer::ViewLayerID SchematicSketchWidget::getLabelViewLayerID(ItemBase *) { return ViewLayer::SchematicLabel; } const QString & SchematicSketchWidget::traceColor(ConnectorItem *) { if (m_lastColorSelected.isEmpty()) return SchematicTraceColor; else return m_lastColorSelected; } const QString & SchematicSketchWidget::traceColor(ViewLayer::ViewLayerPlacement) { return SchematicTraceColor; } bool SchematicSketchWidget::isInLayers(ConnectorItem * connectorItem, ViewLayer::ViewLayerPlacement viewLayerPlacement) { Q_UNUSED(connectorItem); Q_UNUSED(viewLayerPlacement); return true; } bool SchematicSketchWidget::routeBothSides() { return false; } void SchematicSketchWidget::addDefaultParts() { SketchWidget::addDefaultParts(); } bool SchematicSketchWidget::sameElectricalLayer2(ViewLayer::ViewLayerID, ViewLayer::ViewLayerID) { // schematic is always one layer return true; } double SchematicSketchWidget::getKeepout() { return 0.1 * GraphicsUtils::SVGDPI; // in pixels } bool SchematicSketchWidget::acceptsTrace(const ViewGeometry & viewGeometry) { return viewGeometry.getSchematicTrace(); } ViewGeometry::WireFlag SchematicSketchWidget::getTraceFlag() { return ViewGeometry::SchematicTraceFlag; } double SchematicSketchWidget::getTraceWidth() { return GraphicsUtils::SVGDPI * ((m_oldSchematic ) ? TraceWidthMilsOld : TraceWidthMils) / 1000; } double SchematicSketchWidget::getAutorouterTraceWidth() { return getTraceWidth(); } void SchematicSketchWidget::extraRenderSvgStep(ItemBase * itemBase, QPointF offset, double dpi, double printerScale, QString & outputSvg) { TraceWire * traceWire = qobject_cast(itemBase); if (traceWire == NULL) return; if (traceWire->connector0()->isBigDot()) { double r = traceWire->connector0()->rect().width(); outputSvg += makeCircleSVG(traceWire->connector0()->sceneAdjustedTerminalPoint(NULL), r, offset, dpi, printerScale); } if (traceWire->connector1()->isBigDot()) { double r = traceWire->connector0()->rect().width(); outputSvg += makeCircleSVG(traceWire->connector1()->sceneAdjustedTerminalPoint(NULL), r, offset, dpi, printerScale); } } QString SchematicSketchWidget::makeCircleSVG(QPointF p, double r, QPointF offset, double dpi, double printerScale) { double cx = (p.x() - offset.x()) * dpi / printerScale; double cy = (p.y() - offset.y()) * dpi / printerScale; double rr = r * dpi / printerScale; QString stroke = "black"; return QString("") .arg(cx) .arg(cy) .arg(rr); } QString SchematicSketchWidget::generateCopperFillUnit(ItemBase * itemBase, QPointF whereToStart) { Q_UNUSED(itemBase); Q_UNUSED(whereToStart); return ""; } ViewLayer::ViewLayerPlacement SchematicSketchWidget::createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to) { return SketchWidget::createWireViewLayerPlacement(from, to); } double SchematicSketchWidget::getWireStrokeWidth(Wire *, double wireWidth) { return wireWidth * TraceHoverStrokeFactor; } Wire * SchematicSketchWidget::createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement spec) { viewGeometry.setSchematicTrace(true); Wire * wire = SketchWidget::createTempWireForDragging(fromWire, wireModel, connectorItem, viewGeometry, spec); if (fromWire) { wire->setColorString(fromWire->colorString(), fromWire->opacity(), false); } else { wire->setProperty(PCBSketchWidget::FakeTraceProperty, true); wire->setColorString(traceColor(connectorItem), 1.0, false); } return wire; } void SchematicSketchWidget::rotatePartLabels(double degrees, QTransform & transform, QPointF center, QUndoCommand * parentCommand) { PCBSketchWidget::rotatePartLabels(degrees, transform, center, parentCommand); } void SchematicSketchWidget::loadFromModelParts(QList & modelParts, BaseCommand::CrossViewType crossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList & newIDs) { SketchWidget::loadFromModelParts(modelParts, crossViewType, parentCommand, offsetPaste, boundingRect, seekOutsideConnections, newIDs); } void SchematicSketchWidget::selectAllWires(ViewGeometry::WireFlag flag) { SketchWidget::selectAllWires(flag); } bool SchematicSketchWidget::canConnect(Wire *, ItemBase *) { return true; } QString SchematicSketchWidget::checkDroppedModuleID(const QString & moduleID) { return moduleID; } LayerList SchematicSketchWidget::routingLayers(ViewLayer::ViewLayerPlacement) { LayerList layerList; layerList << ViewLayer::Schematic; return layerList; } bool SchematicSketchWidget::attachedToBottomLayer(ConnectorItem * connectorItem) { return (connectorItem->attachedToViewLayerID() == ViewLayer::Schematic) || (connectorItem->attachedToViewLayerID() == ViewLayer::SchematicTrace); } bool SchematicSketchWidget::attachedToTopLayer(ConnectorItem *) { return false; } QSizeF SchematicSketchWidget::jumperItemSize() { if (SchematicSketchWidget::m_jumperItemSize.width() == 0) { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; viewGeometry.setLoc(QPointF(0, 0)); ItemBase * itemBase = addItem(referenceModel()->retrieveModelPart(ModuleIDNames::NetLabelModuleIDName), defaultViewLayerPlacement(NULL), BaseCommand::SingleView, viewGeometry, newID, -1, NULL); if (itemBase) { SymbolPaletteItem * netLabel = qobject_cast(itemBase); netLabel->setLabel("00"); SchematicSketchWidget::m_jumperItemSize = netLabel->boundingRect().size(); deleteItem(itemBase, true, false, false); } } return SchematicSketchWidget::m_jumperItemSize; } QHash SchematicSketchWidget::getAutorouterSettings() { return SketchWidget::getAutorouterSettings(); } void SchematicSketchWidget::setAutorouterSettings(QHash & autorouterSettings) { SketchWidget::setAutorouterSettings(autorouterSettings); } void SchematicSketchWidget::getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement & viewLayerPlacement) { SketchWidget::getDroppedItemViewLayerPlacement(modelPart, viewLayerPlacement); } ViewLayer::ViewLayerPlacement SchematicSketchWidget::getViewLayerPlacement(ModelPart * modelPart, QDomElement & instance, QDomElement & view, ViewGeometry & viewGeometry) { return SketchWidget::getViewLayerPlacement(modelPart, instance, view, viewGeometry); } void SchematicSketchWidget::viewGeometryConversionHack(ViewGeometry & viewGeometry, ModelPart * modelPart) { if (!m_convertSchematic) return; if (modelPart->itemType() == ModelPart::Wire) return; if (viewGeometry.transform().isIdentity()) return; ViewGeometry vg; ItemBase * itemBase = addItemAuxTemp(modelPart, ViewLayer::NewTop, vg, 0, true, viewID(), true); double rotation; if (GraphicsUtils::isFlipped(viewGeometry.transform().toAffine(), rotation)) { itemBase->flipItem(Qt::Horizontal); } itemBase->rotateItem(rotation, false); itemBase->saveGeometry(); viewGeometry.setTransform(itemBase->getViewGeometry().transform()); foreach (ItemBase * kin, itemBase->layerKin()) delete kin; delete itemBase; } void SchematicSketchWidget::setOldSchematic(bool old) { m_oldSchematic = old; } bool SchematicSketchWidget::isOldSchematic() { return m_oldSchematic; } void SchematicSketchWidget::setConvertSchematic(bool convert) { m_convertSchematic = convert; } void SchematicSketchWidget::resizeWires() { double tw = getTraceWidth(); double sw = getWireStrokeWidth(NULL, tw); foreach (QGraphicsItem * item, scene()->items()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (!wire->isTraceType(getTraceFlag())) continue; wire->setWireWidth(tw, this, sw); } } void SchematicSketchWidget::resizeLabels() { double fontSize = getLabelFontSizeSmall(); foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->hasPartLabel() && itemBase->partLabel() != NULL) { itemBase->partLabel()->setFontPointSize(fontSize); } } } fritzing-app-0.9.3b/src/sketch/schematicsketchwidget.h000066400000000000000000000117731272363601300231020ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6979 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 23:20:35 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #ifndef SCHEMATICSKETCHWIDGET_H #define SCHEMATICSKETCHWIDGET_H #include "pcbsketchwidget.h" class SchematicSketchWidget : public PCBSketchWidget { Q_OBJECT public: SchematicSketchWidget(ViewLayer::ViewID, QWidget *parent=0); void addViewLayers(); ViewLayer::ViewLayerID getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement); ViewLayer::ViewLayerID getDragWireViewLayerID(ConnectorItem *); void initWire(Wire *, int penWidth); bool autorouteTypePCB(); double getKeepout(); void tidyWires(); void ensureTraceLayersVisible(); void ensureTraceLayerVisible(); void setClipEnds(ClipableWire * vw, bool); void getBendpointWidths(class Wire *, double w, double & w1, double & w2, bool & negativeOffsetRect); void getLabelFont(QFont &, QColor &, ItemBase *); void setNewPartVisible(ItemBase *); bool canDropModelPart(ModelPart * modelPart); bool includeSymbols(); bool hasBigDots(); void changeConnection(long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, bool doEmit, bool updateConnections); double defaultGridSizeInches(); const QString & traceColor(ConnectorItem * forColor); const QString & traceColor(ViewLayer::ViewLayerPlacement); bool isInLayers(ConnectorItem *, ViewLayer::ViewLayerPlacement); bool routeBothSides(); void addDefaultParts(); bool sameElectricalLayer2(ViewLayer::ViewLayerID, ViewLayer::ViewLayerID); bool acceptsTrace(const ViewGeometry &); ViewGeometry::WireFlag getTraceFlag(); double getTraceWidth(); double getAutorouterTraceWidth(); QString generateCopperFillUnit(ItemBase * itemBase, QPointF whereToStart); double getWireStrokeWidth(Wire *, double wireWidth); Wire * createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement); void rotatePartLabels(double degrees, QTransform &, QPointF center, QUndoCommand * parentCommand); void loadFromModelParts(QList & modelParts, BaseCommand::CrossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList & newIDs); LayerList routingLayers(ViewLayer::ViewLayerPlacement); bool attachedToTopLayer(ConnectorItem *); bool attachedToBottomLayer(ConnectorItem *); QSizeF jumperItemSize(); QHash getAutorouterSettings(); void setAutorouterSettings(QHash &); ViewLayer::ViewLayerPlacement getViewLayerPlacement(ModelPart *, QDomElement & instance, QDomElement & view, ViewGeometry &); void setConvertSchematic(bool); void setOldSchematic(bool); bool isOldSchematic(); void resizeWires(); void resizeLabels(); public slots: void setVoltage(double voltage, bool doEmit); void setProp(ItemBase *, const QString & propName, const QString & translatedPropName, const QString & oldValue, const QString & newValue, bool redraw); void setInstanceTitle(long id, const QString & oldTitle, const QString & newTitle, bool isUndoable, bool doEmit); protected slots: void updateBigDots(); void getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement &); protected: double getRatsnestOpacity(); double getRatsnestWidth(); ViewLayer::ViewLayerID getLabelViewLayerID(ItemBase *); void extraRenderSvgStep(ItemBase *, QPointF offset, double dpi, double printerScale, QString & outputSvg); QString makeCircleSVG(QPointF p, double r, QPointF offset, double dpi, double printerScale); ViewLayer::ViewLayerPlacement createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to); void selectAllWires(ViewGeometry::WireFlag flag); bool canConnect(Wire * from, ItemBase * to); QString checkDroppedModuleID(const QString & moduleID); void viewGeometryConversionHack(ViewGeometry &, ModelPart *); protected: QTimer m_updateDotsTimer; bool m_convertSchematic; bool m_oldSchematic; static QSizeF m_jumperItemSize; }; #endif fritzing-app-0.9.3b/src/sketch/sketchwidget.cpp000066400000000000000000012202371272363601300215520ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty ofro 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 Fritzing. If not, see . ******************************************************************** $Revision: 7000 $: $Author: irascibl@gmail.com $: $Date: 2013-04-29 07:24:08 +0200 (Mon, 29 Apr 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../items/partfactory.h" #include "../items/paletteitem.h" #include "../items/logoitem.h" #include "../items/pad.h" #include "../items/ruler.h" #include "../items/symbolpaletteitem.h" #include "../items/wire.h" #include "../commands.h" #include "../model/modelpart.h" #include "../debugdialog.h" #include "../items/layerkinpaletteitem.h" #include "sketchwidget.h" #include "../connectors/connectoritem.h" #include "../connectors/svgidlayer.h" #include "../items/jumperitem.h" #include "../items/stripboard.h" #include "../items/virtualwire.h" #include "../items/tracewire.h" #include "../itemdrag.h" #include "../layerattributes.h" #include "../waitpushundostack.h" #include "fgraphicsscene.h" #include "../version/version.h" #include "../items/partlabel.h" #include "../items/note.h" #include "../svg/svgfilesplitter.h" #include "../svg/svgflattener.h" #include "../infoview/htmlinfoview.h" #include "../items/resizableboard.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/bezier.h" #include "../utils/cursormaster.h" #include "../utils/fmessagebox.h" #include "../fsvgrenderer.h" #include "../items/resistor.h" #include "../items/mysterypart.h" #include "../items/pinheader.h" #include "../items/dip.h" #include "../items/groundplane.h" #include "../items/moduleidnames.h" #include "../items/hole.h" #include "../items/capacitor.h" #include "../items/schematicframe.h" #include "../utils/graphutils.h" #include "../utils/ratsnestcolors.h" #include "../utils/cursormaster.h" ///////////////////////////////////////////////////////////////////// bool hideTerminalID(QDomDocument & doc, const QString & terminalID) { QDomElement root = doc.documentElement(); QDomElement terminal = TextUtils::findElementWithAttribute(root, "id", terminalID); if (terminal.isNull()) return false; terminal.setTagName("g"); return true; } bool ensureStrokeWidth(QDomDocument & doc, const QString & connectorID, double factor) { QDomElement root = doc.documentElement(); QDomElement connector = TextUtils::findElementWithAttribute(root, "id", connectorID); if (connector.isNull()) return false; QString stroke = connector.attribute("stroke"); if (stroke.isEmpty()) return false; QString strokeWidth = connector.attribute("stroke-width"); if (!strokeWidth.isEmpty()) return false; TextUtils::getStrokeWidth(connector, factor); // default stroke width is 1, multipled by factor return true; } ///////////////////////////////////////////////////////////////////// SizeItem::SizeItem() { } SizeItem::~SizeItem() { } ///////////////////////////////////////////////////////////////////// enum ConnectionStatus { IN_, OUT_, FREE_, UNDETERMINED_ }; static const double CloseEnough = 0.5; // in pixels, for swapping into the breadboard const int SketchWidget::MoveAutoScrollThreshold = 5; const int SketchWidget::DragAutoScrollThreshold = 10; static const int AutoRepeatDelay = 750; const int SketchWidget::PropChangeDelay = 100; bool SketchWidget::m_blockUI = false; ///////////////////////////////////////////////////////////////////// bool zLessThan(QGraphicsItem * & p1, QGraphicsItem * & p2) { return p1->zValue() < p2->zValue(); } ///////////////////////////////////////////////////////////////////// SketchWidget::SketchWidget(ViewLayer::ViewID viewID, QWidget *parent, int size, int minSize) : InfoGraphicsView(parent) { m_everZoomed = false; m_itemMenu = NULL; m_pasting = false; m_rubberBandLegWasEnabled = m_curvyWires = false; m_middleMouseIsPressed = false; m_arrowTimer.setParent(this); m_arrowTimer.setInterval(AutoRepeatDelay); m_arrowTimer.setSingleShot(true); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) m_arrowTimer.setTimerType(Qt::PreciseTimer); m_autoScrollTimer.setTimerType(Qt::PreciseTimer); #endif connect(&m_arrowTimer, SIGNAL(timeout()), this, SLOT(arrowTimerTimeout())); m_addDefaultParts = false; m_addedDefaultPart = NULL; m_movingItem = NULL; m_movingSVGRenderer = NULL; m_clearSceneRect = false; m_draggingBendpoint = false; m_zoom = 100; m_showGrid = m_alignToGrid = true; m_movingByMouse = m_movingByArrow = false; m_statusConnectState = StatusConnectNotTried; m_dragBendpointWire = NULL; m_lastHoverEnterItem = NULL; m_lastHoverEnterConnectorItem = NULL; m_spaceBarWasPressed = m_spaceBarIsPressed = false; m_current = false; m_ignoreSelectionChangeEvents = 0; m_droppingItem = NULL; m_chainDrag = false; m_bendpointWire = m_connectorDragWire = NULL; m_tempDragWireCommand = m_holdingSelectItemCommand = NULL; m_viewID = viewID; //setAlignment(Qt::AlignLeft | Qt::AlignTop); setDragMode(QGraphicsView::RubberBandDrag); setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); setAcceptDrops(true); setRenderHint(QPainter::Antialiasing, true); //setCacheMode(QGraphicsView::CacheBackground); //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); //setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setTransformationAnchor(QGraphicsView::AnchorViewCenter); //setTransformationAnchor(QGraphicsView::AnchorUnderMouse); //setTransformationAnchor(QGraphicsView::NoAnchor); FGraphicsScene* scene = new FGraphicsScene(this); this->setScene(scene); //this->scene()->setSceneRect(0,0, rect().width(), rect().height()); // Setting the scene rect here seems to mean it never resizes when the user drags an object // outside the sceneRect bounds. So catch some signal and do the resize manually? // this->scene()->setSceneRect(0, 0, 500, 500); // if the sceneRect isn't set, the view seems to grow and scroll gracefully as new items are added // however, it doesn't shrink if items are removed. // a bit of a hack so that, when there is no scenerect set, // the first item dropped into the scene doesn't leap to the top left corner // as the scene resizes to fit the new item m_sizeItem = new SizeItem(); m_sizeItem->setLine(0, 0, rect().width(), rect().height()); //DebugDialog::debug(QString("initial rect %1 %2").arg(rect().width()).arg(rect().height())); this->scene()->addItem(m_sizeItem); m_sizeItem->setVisible(false); connect(this->scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChangedSlot())); connect(QApplication::clipboard(),SIGNAL(changed(QClipboard::Mode)),this,SLOT(restartPasteCount())); restartPasteCount(); // the first time setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); resize(size, size); setMinimumSize(minSize, minSize); setLastPaletteItemSelected(NULL); m_infoViewOnHover = true; setMouseTracking(true); } SketchWidget::~SketchWidget() { foreach (ViewLayer * viewLayer, m_viewLayers.values()) { if (viewLayer == NULL) continue; delete viewLayer; } m_viewLayers.clear(); } void SketchWidget::restartPasteCount() { m_pasteCount = 0; } WaitPushUndoStack* SketchWidget::undoStack() { return m_undoStack; } void SketchWidget::setUndoStack(WaitPushUndoStack * undoStack) { m_undoStack = undoStack; } void SketchWidget::loadFromModelParts(QList & modelParts, BaseCommand::CrossViewType crossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList & newIDs) { clearHoldingSelectItem(); if (parentCommand) { SelectItemCommand * selectItemCommand = stackSelectionState(false, parentCommand); selectItemCommand->setSelectItemType(SelectItemCommand::DeselectAll); selectItemCommand->setCrossViewType(crossViewType); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); } QHash newItems; setIgnoreSelectionChangeEvents(true); QString viewName = ViewLayer::viewIDXmlName(m_viewID); QMultiMap zmap; QPointF sceneCenter = mapToScene(viewport()->rect().center()); QPointF sceneCorner; if (boundingRect) { sceneCorner.setX(sceneCenter.x() - (boundingRect->width() / 2)); sceneCorner.setY(sceneCenter.y() - (boundingRect->height() / 2)); } QHash superparts; QHash superparts2; QList zeroLength; // make parts foreach (ModelPart * mp, modelParts) { QDomElement instance = mp->instanceDomElement(); if (instance.isNull()) continue; QDomElement views = instance.firstChildElement("views"); if (views.isNull()) continue; QDomElement view = views.firstChildElement(viewName); if (view.isNull()) continue; bool locked = view.attribute("locked", "").compare("true") == 0; bool superpartOK; long superpartID = view.attribute("superpart", "").toLong(&superpartOK); if (superpartOK) { superpartID = ItemBase::getNextID(superpartID); } QDomElement geometry = view.firstChildElement("geometry"); if (geometry.isNull()) continue; ViewGeometry viewGeometry(geometry); if (mp->itemType() == ModelPart::Wire) { if (viewGeometry.hasFlag(getTraceFlag())) { QLineF l = viewGeometry.line(); if (l.p1().x() == 0 && l.p1().y() == 0 && l.p2().x() == 0 && l.p2().y() == 0) { if (view.firstChildElement("connectors").isNull() && view.nextSiblingElement().isNull()) { DebugDialog::debug(QString("wire has zero length %1 in %2").arg(mp->moduleID()).arg(m_viewID)); zeroLength.append(mp); continue; } } } } QDomElement labelGeometry = view.firstChildElement("titleGeometry"); QDomElement layerHidden = view.firstChildElement("layerHidden"); ViewLayer::ViewLayerPlacement viewLayerPlacement = getViewLayerPlacement(mp, instance, view, viewGeometry); // use a function of the model index to ensure the same parts have the same ID across views long newID = ItemBase::getNextID(mp->modelIndex()); if (parentCommand == NULL) { viewGeometryConversionHack(viewGeometry, mp); ItemBase * itemBase = addItemAux(mp, viewLayerPlacement, viewGeometry, newID, true, m_viewID, false); if (itemBase != NULL) { if (locked) { itemBase->setMoveLock(true); } if (superpartOK) { superparts.insert(itemBase, superpartID); } while (!layerHidden.isNull()) { hidePartLayer(itemBase, ViewLayer::viewLayerIDFromXmlString(layerHidden.attribute("layer")), true); layerHidden = layerHidden.nextSiblingElement("layerHidden"); } //if (itemBase->itemType() == ModelPart::ResizableBoard) { //DebugDialog::debug("sticky"); //} if (itemBase->isBaseSticky() && itemBase->isLocalSticky()) { // make sure the icon is displayed itemBase->setLocalSticky(true); } zmap.insert(viewGeometry.z() - qFloor(viewGeometry.z()), itemBase); bool gotOne = false; if (!gotOne) { PaletteItem * paletteItem = qobject_cast(itemBase); if (paletteItem != NULL) { // wires don't have transforms //paletteItem->setTransforms(); // jrc 14 july 2013: this call seems redundant--transforms have already been set up by now gotOne = true; } } if (!gotOne) { Wire * wire = qobject_cast(itemBase); if (wire != NULL) { QDomElement extras = view.firstChildElement("wireExtras"); wire->setExtras(extras, this); gotOne = true; } } if (!gotOne) { Note * note = qobject_cast(itemBase); if (note != NULL) { note->setText(mp->instanceText(), true); gotOne = true; } } // use the modelIndex from mp, not from the newly created item, because we're mapping from the modelIndex in the xml file newItems.insert(mp->modelIndex(), itemBase); if (itemBase->itemType() != ModelPart::Wire) { itemBase->restorePartLabel(labelGeometry, getLabelViewLayerID(itemBase)); } } } else { // offset pasted items so we can differentiate them from the originals if (offsetPaste) { if (m_pasteOffset.x() != 0 || m_pasteOffset.y() != 0) { viewGeometry.offset((20 * m_pasteCount) + m_pasteOffset.x(), (20 * m_pasteCount) + m_pasteOffset.y()); } else if (boundingRect && !boundingRect->isNull()) { double dx = viewGeometry.loc().x() - boundingRect->left() + sceneCorner.x() + (20 * m_pasteCount); double dy = viewGeometry.loc().y() - boundingRect->top() + sceneCorner.y() + (20 * m_pasteCount); viewGeometry.setLoc(QPointF(dx, dy)); } } newAddItemCommand(crossViewType, mp, mp->moduleID(), viewLayerPlacement, viewGeometry, newID, false, mp->modelIndex(), false, parentCommand); if (superpartOK) { superparts2.insert(newID, superpartID); } // TODO: all this part specific stuff should be in the PartFactory if (Board::isBoard(mp) || mp->itemType() == ModelPart::Logo) { bool ok; double w = mp->localProp("width").toDouble(&ok); if (ok) { double h = mp->localProp("height").toDouble(&ok); if (ok) { new ResizeBoardCommand(this, newID, w, h, w, h, parentCommand); } } } else if (mp->itemType() == ModelPart::Note) { new ChangeNoteTextCommand(this, newID, mp->instanceText(), mp->instanceText(), viewGeometry.rect().size(), viewGeometry.rect().size(), parentCommand); } else if (mp->itemType() == ModelPart::Ruler) { QString w = mp->localProp("width").toString(); QString w2 = w; w.chop(2); int units = w2.endsWith("cm") ? 0 : 1; new ResizeBoardCommand(this, newID, w.toDouble(), units, w.toDouble(), units, parentCommand); mp->setLocalProp("width", ""); // ResizeBoardCommand won't execute if the width property is already set } if (locked) { new MoveLockCommand(this, newID, true, true, parentCommand); } while (!layerHidden.isNull()) { new HidePartLayerCommand(this, newID, ViewLayer::viewLayerIDFromXmlString(layerHidden.attribute("layer")), true, true, parentCommand); layerHidden = layerHidden.nextSiblingElement("layerHidden"); } if (!labelGeometry.isNull()) { QDomElement clone = labelGeometry.cloneNode(true).toElement(); bool ok; double x = clone.attribute("x").toDouble(&ok); if (ok) { if (m_pasteOffset.x() == 0 && m_pasteOffset.y() == 0) { int dx = (boundingRect) ? boundingRect->left() : 0; x = x - dx + sceneCorner.x() + (20 * m_pasteCount); } else { x += (20 * m_pasteCount) + m_pasteOffset.x(); } clone.setAttribute("x", QString::number(x)); } double y = clone.attribute("y").toDouble(&ok); if (ok) { if (m_pasteOffset.x() == 0 && m_pasteOffset.y() == 0) { int dy = boundingRect ? boundingRect->top() : 0; y = y - dy + sceneCorner.y() + (20 * m_pasteCount); } else { y += (20 * m_pasteCount) + m_pasteOffset.y(); } clone.setAttribute("y", QString::number(y)); } new RestoreLabelCommand(this, newID, clone, parentCommand); } newIDs << newID; if (mp->moduleID() == ModuleIDNames::WireModuleIDName) { addWireExtras(newID, view, parentCommand); } } } foreach (ModelPart * mp, zeroLength) { modelParts.removeOne(mp); mp->killViewItems(); m_sketchModel->removeModelPart(mp); delete mp; } foreach (ItemBase * sub, superparts.keys()) { ItemBase * super = findItem(superparts.value(sub)); if (super) { super->addSubpart(sub); } } foreach (long newID, superparts2.keys()) { AddSubpartCommand * asc = new AddSubpartCommand(this, crossViewType, superparts2.value(newID), newID, parentCommand); asc->setRedoOnly(); } if (parentCommand) { foreach (long id, newIDs) { new CheckStickyCommand(this, crossViewType, id, false, CheckStickyCommand::RemoveOnly, parentCommand); } } if (zmap.count() > 0) { double z = 0.5; foreach (ItemBase * itemBase, zmap.values()) { itemBase->slamZ(z); z += ViewLayer::getZIncrement(); } foreach (ViewLayer * viewLayer, m_viewLayers) { if (viewLayer != NULL) viewLayer->resetNextZ(z); } } QStringList alreadyConnected; QHash legs; // now restore connections foreach (ModelPart * mp, modelParts) { QDomElement instance = mp->instanceDomElement(); if (instance.isNull()) continue; QDomElement views = instance.firstChildElement("views"); if (views.isNull()) continue; QDomElement view = views.firstChildElement(viewName); if (view.isNull()) continue; QDomElement connectors = view.firstChildElement("connectors"); if (connectors.isNull()) { continue; } QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QString fromConnectorID = connector.attribute("connectorId"); ViewLayer::ViewLayerID connectorViewLayerID = ViewLayer::viewLayerIDFromXmlString(connector.attribute("layer")); bool gfs = connector.attribute("groundFillSeed").compare("true") == 0; if (gfs) { ItemBase * fromBase = newItems.value(mp->modelIndex(), NULL); if (fromBase) { ConnectorItem * fromConnectorItem = fromBase->findConnectorItemWithSharedID(fromConnectorID, ViewLayer::specFromID(connectorViewLayerID)); if (fromConnectorItem) { fromConnectorItem->setGroundFillSeed(true); } } } QDomElement connects = connector.firstChildElement("connects"); if (!connects.isNull()) { QDomElement connect = connects.firstChildElement("connect"); while (!connect.isNull()) { handleConnect(connect, mp, fromConnectorID, connectorViewLayerID, alreadyConnected, newItems, parentCommand, seekOutsideConnections); connect = connect.nextSiblingElement("connect"); } } QDomElement leg = connector.firstChildElement("leg"); if (!leg.isNull() && !leg.firstChildElement("point").isNull()) { if (parentCommand) { legs.insert(QString::number(ItemBase::getNextID(mp->modelIndex())) + "." + fromConnectorID, leg); } else { ItemBase * fromBase = newItems.value(mp->modelIndex(), NULL); if (fromBase) { legs.insert(QString::number(fromBase->id()) + "." + fromConnectorID, leg); } } } connector = connector.nextSiblingElement("connector"); } } // must do legs after all connections are set up foreach (QString key, legs.keys()) { int ix = key.indexOf("."); if (ix <= 0) continue; QDomElement leg = legs.value(key); long id = key.left(ix).toInt(); QString fromConnectorID = key.remove(0, ix + 1); QPolygonF poly = TextUtils::polygonFromElement(leg); if (poly.count() < 2) continue; if (parentCommand) { ChangeLegCommand * clc = new ChangeLegCommand(this, id, fromConnectorID, poly, poly, true, true, "copy", parentCommand); clc->setSimple(); } else { changeLeg(id, fromConnectorID, poly, true, "load"); } QDomElement bElement = leg.firstChildElement("bezier"); int bIndex = 0; while (!bElement.isNull()) { Bezier bezier = Bezier::fromElement(bElement); if (!bezier.isEmpty()) { if (parentCommand) { new ChangeLegCurveCommand(this, id, fromConnectorID, bIndex, &bezier, &bezier, parentCommand); } else { changeLegCurve(id, fromConnectorID, bIndex, &bezier); } } bElement = bElement.nextSiblingElement("bezier"); bIndex++; } } if (parentCommand == NULL) { foreach (ItemBase * item, newItems) { item->doneLoading(); if (item->isBaseSticky()) { stickyScoop(item, false, NULL); } } m_pasteCount = 0; this->scene()->clearSelection(); cleanUpWires(false, NULL); } else { if (offsetPaste) { // m_pasteCount used for offsetting paste items, not a count of how many items are pasted m_pasteCount++; } new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); } setIgnoreSelectionChangeEvents(false); m_pasteOffset = QPointF(0,0); } void SketchWidget::handleConnect(QDomElement & connect, ModelPart * mp, const QString & fromConnectorID, ViewLayer::ViewLayerID fromViewLayerID, QStringList & alreadyConnected, QHash & newItems, QUndoCommand * parentCommand, bool seekOutsideConnections) { bool ok; QHash otherNewItems; long modelIndex = connect.attribute("modelIndex").toLong(&ok); QString toConnectorID = connect.attribute("connectorId"); ViewLayer::ViewLayerID toViewLayerID = ViewLayer::viewLayerIDFromXmlString(connect.attribute("layer")); QString already = ((mp->modelIndex() <= modelIndex) ? QString("%1.%2.%3.%4.%5.%6") : QString("%4.%5.%6.%1.%2.%3")) .arg(mp->modelIndex()).arg(fromConnectorID).arg(fromViewLayerID) .arg(modelIndex).arg(toConnectorID).arg(toViewLayerID); if (alreadyConnected.contains(already)) return; alreadyConnected.append(already); if (parentCommand == NULL) { ItemBase * fromBase = newItems.value(mp->modelIndex(), NULL); ItemBase * toBase = newItems.value(modelIndex, NULL); if (toBase == NULL) { toBase = otherNewItems.value(modelIndex, NULL); } if (fromBase == NULL || toBase == NULL) { if (!seekOutsideConnections) return; if (fromBase == NULL) { fromBase = findItem(mp->modelIndex() * ModelPart::indexMultiplier); if (fromBase == NULL) return; } if (toBase == NULL) { toBase = findItem(modelIndex * ModelPart::indexMultiplier); if (toBase == NULL) return; } } ConnectorItem * fromConnectorItem = fromBase->findConnectorItemWithSharedID(fromConnectorID, ViewLayer::specFromID(fromViewLayerID)); ConnectorItem * toConnectorItem = toBase->findConnectorItemWithSharedID(toConnectorID, ViewLayer::specFromID(toViewLayerID)); if (fromConnectorItem == NULL || toConnectorItem == NULL) { return; } fromConnectorItem->connectTo(toConnectorItem); toConnectorItem->connectTo(fromConnectorItem); fromConnectorItem->connector()->connectTo(toConnectorItem->connector()); if (fromConnectorItem->attachedToItemType() == ModelPart::Wire && toConnectorItem->attachedToItemType() == ModelPart::Wire) { fromConnectorItem->setHidden(false); toConnectorItem->setHidden(false); } ratsnestConnect(fromConnectorItem, true); ratsnestConnect(toConnectorItem, true); return; } // single view because handle connect is called from loadModelPart which gets called for each view new ChangeConnectionCommand(this, BaseCommand::SingleView, ItemBase::getNextID(mp->modelIndex()), fromConnectorID, ItemBase::getNextID(modelIndex), toConnectorID, ViewLayer::specFromID(fromViewLayerID), true, parentCommand); } void SketchWidget::addWireExtras(long newID, QDomElement & view, QUndoCommand * parentCommand) { QDomElement extras = view.firstChildElement("wireExtras"); if (extras.isNull()) return; QDomElement copy(extras); new WireExtrasCommand(this, newID, copy, copy, parentCommand); } void SketchWidget::setWireExtras(long newID, QDomElement & extras) { Wire * wire = qobject_cast(findItem(newID)); if (wire == NULL) return; wire->setExtras(extras, this); } ItemBase * SketchWidget::addItem(const QString & moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, BaseCommand::CrossViewType crossViewType, const ViewGeometry & viewGeometry, long id, long modelIndex, AddDeleteItemCommand * originatingCommand) { if (m_referenceModel == NULL) return NULL; ItemBase * itemBase = NULL; ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID); if (modelPart != NULL) { if (!m_blockUI) { QApplication::setOverrideCursor(Qt::WaitCursor); statusMessage(tr("loading part")); } itemBase = addItem(modelPart, viewLayerPlacement, crossViewType, viewGeometry, id, modelIndex, originatingCommand); if (!m_blockUI) { statusMessage(tr("done loading"), 2000); QApplication::restoreOverrideCursor(); } } return itemBase; } ItemBase * SketchWidget::addItem(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, BaseCommand::CrossViewType crossViewType, const ViewGeometry & viewGeometry, long id, long modelIndex, AddDeleteItemCommand * originatingCommand) { ItemBase * newItem = NULL; //if (checkAlreadyExists) { // newItem = findItem(id); // if (newItem != NULL) { // newItem->debugInfo("already exists"); // } //} if (newItem == NULL) { ModelPart * mp = NULL; if (modelIndex >= 0) { // used only with Paste, so far--this assures that parts created across views will share the same ModelPart mp = m_sketchModel->findModelPart(modelPart->moduleID(), id); } if (mp == NULL) { modelPart = m_sketchModel->addModelPart(m_sketchModel->root(), modelPart); } else { modelPart = mp; } if (modelPart == NULL) return NULL; newItem = addItemAux(modelPart, viewLayerPlacement, viewGeometry, id, true, m_viewID, false); } if (crossViewType == BaseCommand::CrossView) { //DebugDialog::debug(QString("emit item added")); emit itemAddedSignal(modelPart, newItem, viewLayerPlacement, viewGeometry, id, originatingCommand ? originatingCommand->dropOrigin() : NULL); //DebugDialog::debug(QString("after emit item added")); } return newItem; } ItemBase * SketchWidget::addItemAuxTemp(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, bool doConnectors, ViewLayer::ViewID viewID, bool temporary) { modelPart = m_sketchModel->addModelPart(m_sketchModel->root(), modelPart); if (modelPart == NULL) return NULL; // this is very fucked up return addItemAux(modelPart, viewLayerPlacement, viewGeometry, id, doConnectors, viewID, temporary); } ItemBase * SketchWidget::addItemAux(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, bool doConnectors, ViewLayer::ViewID viewID, bool temporary) { if (viewID == ViewLayer::UnknownView) { viewID = m_viewID; } if (doConnectors) { modelPart->initConnectors(); // is a no-op if connectors already in place } ItemBase * newItem = PartFactory::createPart(modelPart, viewLayerPlacement, viewID, viewGeometry, id, m_itemMenu, m_wireMenu, true); Wire * wire = qobject_cast(newItem); if (wire) { QString descr; bool ratsnest = viewGeometry.getRatsnest(); if (ratsnest) { setClipEnds((ClipableWire *) wire, true); descr = "ratsnest"; } else if (viewGeometry.getAnyTrace() ) { setClipEnds((ClipableWire *) wire, true); descr = "trace"; } else { wire->setNormal(true); descr = "wire"; } wire->setUp(getWireViewLayerID(viewGeometry, wire->viewLayerPlacement()), m_viewLayers, this); setWireVisible(wire); wire->updateConnectors(); addToScene(wire, wire->viewLayerID()); wire->addedToScene(temporary); wire->debugInfo("add " + descr); return wire; } if (modelPart->itemType() == ModelPart::Note) { newItem->setViewLayerID(getNoteViewLayerID(), m_viewLayers); newItem->setZValue(newItem->z()); newItem->setVisible(true); addToScene(newItem, getNoteViewLayerID()); newItem->addedToScene(temporary); return newItem; } bool ok; addPartItem(modelPart, viewLayerPlacement, (PaletteItem *) newItem, doConnectors, ok, viewID, temporary); newItem->debugInfo("add part"); setNewPartVisible(newItem); newItem->updateConnectors(); return newItem; } void SketchWidget::setNewPartVisible(ItemBase * itemBase) { Q_UNUSED(itemBase); // defaults to visible, so do nothing } void SketchWidget::checkSticky(long id, bool doEmit, bool checkCurrent, CheckStickyCommand * checkStickyCommand) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; if (itemBase->hidden() || itemBase->layerHidden()) { } else if (itemBase->isBaseSticky()) { stickyScoop(itemBase, checkCurrent, checkStickyCommand); } else { ItemBase * stickyOne = overSticky(itemBase); ItemBase * wasStickyOne = itemBase->stickingTo(); if (stickyOne != wasStickyOne) { if (wasStickyOne != NULL) { wasStickyOne->addSticky(itemBase, false); itemBase->addSticky(wasStickyOne, false); if (checkStickyCommand) { checkStickyCommand->stick(this, wasStickyOne->id(), itemBase->id(), false); } } if (stickyOne != NULL) { stickyOne->addSticky(itemBase, true); itemBase->addSticky(stickyOne, true); if (checkStickyCommand) { checkStickyCommand->stick(this, stickyOne->id(), itemBase->id(), true); } } } } if (doEmit) { checkStickySignal(id, false, false, checkStickyCommand); } } PaletteItem* SketchWidget::addPartItem(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, PaletteItem * paletteItem, bool doConnectors, bool & ok, ViewLayer::ViewID viewID, bool temporary) { ok = false; ViewLayer::ViewLayerID viewLayerID = getViewLayerID(modelPart, viewID, viewLayerPlacement); if (viewLayerID == ViewLayer::UnknownLayer) { // render it only if the layer is defined in the fzp file // if the view is not defined in the part file, without this condition // fritzing crashes return paletteItem; } QString error; bool result = paletteItem->renderImage(modelPart, viewID, m_viewLayers, viewLayerID, doConnectors, error); if (!result) { bool retry = false; switch (viewLayerID) { case ViewLayer::Copper0: viewLayerID = ViewLayer::Copper1; retry = true; break; case ViewLayer::Copper1: viewLayerID = ViewLayer::Copper0; retry = true; break; default: break; } if (retry) { result = paletteItem->renderImage(modelPart, viewID, m_viewLayers, viewLayerID, doConnectors, error); } } bool hideSuper = modelPart->hasSubparts() && !temporary && viewID == ViewLayer::SchematicView; if (result) { //DebugDialog::debug(QString("addPartItem %1").arg(viewID)); addToScene(paletteItem, paletteItem->viewLayerID()); if (hideSuper) { paletteItem->setEverVisible(false); paletteItem->setVisible(false); } paletteItem->loadLayerKin(m_viewLayers, viewLayerPlacement); foreach (ItemBase * lkpi, paletteItem->layerKin()) { this->scene()->addItem(lkpi); lkpi->setHidden(!layerIsVisible(lkpi->viewLayerID())); lkpi->setInactive(!layerIsActive(lkpi->viewLayerID())); if (hideSuper) { lkpi->setEverVisible(false); lkpi->setVisible(false); } } //DebugDialog::debug(QString("after layerkin %1").arg(viewID)); ok = true; } else { // nobody falls through to here now? FMessageBox::information(NULL, QObject::tr("Fritzing"), QObject::tr("Error reading file %1: %2.").arg(modelPart->path()).arg(error) ); DebugDialog::debug(QString("addPartItem renderImage failed %1 %2").arg(modelPart->moduleID()).arg(error)); //paletteItem->modelPart()->removeViewItem(paletteItem); //delete paletteItem; //return NULL; scene()->addItem(paletteItem); //paletteItem->setVisible(false); } paletteItem->addedToScene(temporary); return paletteItem; } void SketchWidget::addToScene(ItemBase * item, ViewLayer::ViewLayerID viewLayerID) { scene()->addItem(item); item->setSelected(true); item->setHidden(!layerIsVisible(viewLayerID)); item->setInactive(!layerIsActive(viewLayerID)); } ItemBase * SketchWidget::findItem(long id) { // TODO: this needs to be optimized: could make a hash table long baseid = id / ModelPart::indexMultiplier; foreach (QGraphicsItem * item, this->scene()->items()) { ItemBase* base = dynamic_cast(item); if (base == NULL) continue; if (base->id() == id) { return base; } if (base->id() / ModelPart::indexMultiplier == baseid) { // found chief or layerkin ItemBase * chief = base->layerKinChief(); if (chief->id() == id) return chief; foreach (ItemBase * lk, chief->layerKin()) { if (lk->id() == id) return lk; } return chief; } } return NULL; } void SketchWidget::deleteItem(long id, bool deleteModelPart, bool doEmit, bool later) { ItemBase * pitem = findItem(id); DebugDialog::debug(QString("delete item (1) %1 %2 %3 %4").arg(id).arg(doEmit).arg(m_viewID).arg((long) pitem, 0, 16) ); if (pitem != NULL) { deleteItem(pitem, deleteModelPart, doEmit, later); } else { if (doEmit) { emit itemDeletedSignal(id); } } } void SketchWidget::deleteItem(ItemBase * itemBase, bool deleteModelPart, bool doEmit, bool later) { long id = itemBase->id(); DebugDialog::debug(QString("delete item (2) %1 %2 %3 %4").arg(id).arg(itemBase->title()).arg(m_viewID).arg((long) itemBase, 0, 16) ); // this is a hack to try to workaround a Qt 4.7 crash in QGraphicsSceneFindItemBspTreeVisitor::visit // when using a custom boundingRect, after deleting an item, it still appears on the visit list. // // the problem arises because the legItems are used to calculate the boundingRect() of the item. // But in the destructor, the childItems are deleted first, then the BSP tree is updated // at that point, the boundingRect() will return a different value than what's in the BSP tree, // which is the old value of the boundingRect before the legs were deleted. if (itemBase->hasRubberBandLeg()) { DebugDialog::debug("kill rubberBand"); itemBase->killRubberBandLeg(); } if (m_infoView != NULL) { m_infoView->unregisterCurrentItemIf(itemBase->id()); } if (itemBase == this->m_lastPaletteItemSelected) { setLastPaletteItemSelected(NULL); } // m_lastSelected.removeOne(itemBase); hack for 4.5.something if (deleteModelPart) { ModelPart * modelPart = itemBase->modelPart(); if (modelPart != NULL) { m_sketchModel->removeModelPart(modelPart); delete modelPart; } } itemBase->removeLayerKin(); this->scene()->removeItem(itemBase); if (later) { itemBase->deleteLater(); } else { delete itemBase; } if (doEmit) { emit itemDeletedSignal(id); } } void SketchWidget::deleteSelected(Wire * wire, bool minus) { QSet itemBases; if (wire) { itemBases << wire; } else { foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->moveLock()) continue; itemBase = itemBase->layerKinChief(); if (itemBase->moveLock()) continue; itemBases.insert(itemBase); } } if (itemBases.count() == 0) return; // assumes ratsnest is not mixed with other itembases bool rats = true; foreach (ItemBase * itemBase, itemBases) { Wire * wire = qobject_cast(itemBase); if (wire == NULL) { rats = false; break; } if (!wire->getRatsnest()) { rats = false; break; } } if (!rats) { cutDeleteAux("Delete", minus, wire); // wire is selected in this case, so don't bother sending it along return; } QUndoCommand * parentCommand = new QUndoCommand(tr("Delete ratsnest")); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); deleteRatsnest(wire, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::cutDeleteAux(QString undoStackMessage, bool minus, Wire * wire) { //DebugDialog::debug("before delete"); // get sitems first, before calling stackSelectionState // because selectedItems will return an empty list const QList sitems = scene()->selectedItems(); QSet deletedItems; if (minus && wire != NULL) { // called from wire context menu "delete to bendpoint" deletedItems.insert(wire); } else { foreach (QGraphicsItem * sitem, sitems) { if (!canDeleteItem(sitem, sitems.count())) continue; // canDeleteItem insures dynamic_cast(sitem)->layerKinChief() won't break ItemBase * itemBase = dynamic_cast(sitem)->layerKinChief(); if (itemBase->superpart()) { deletedItems.insert(itemBase->superpart()); foreach (ItemBase * sub, itemBase->superpart()->subparts()) deletedItems.insert(sub); } else if (itemBase->subparts().count() > 0) { deletedItems.insert(itemBase); foreach (ItemBase * sub, itemBase->subparts()) deletedItems.insert(sub); } else { deletedItems.insert(itemBase); } } if (!minus) { QSet allWires; foreach (ItemBase * itemBase, deletedItems) { QList connectorItems; foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { connectorItems.append(connectorItem); ConnectorItem * cross = connectorItem->getCrossLayerConnectorItem(); if (cross) connectorItems.append(cross); } foreach (ConnectorItem * connectorItem, connectorItems) { foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue; Wire * wire = qobject_cast(toConnectorItem->attachedTo()); if (wire->getRatsnest()) continue; //if (!wire->isTraceType(getTraceFlag())) continue; // removes connected wires across views when commented out QList wires; wire->collectDirectWires(wires); foreach (Wire * w, wires) { allWires.insert(w); } } } } if (allWires.count() > 0) { QList wires = allWires.toList(); wires.at(0)->collectDirectWires(wires); foreach (Wire * w, wires) { deletedItems.insert(w); } } } } if (deletedItems.count() <= 0) { return; } QString string; if (deletedItems.count() == 1) { ItemBase * firstItem = *(deletedItems.begin()); string = tr("%1 %2").arg(undoStackMessage).arg(firstItem->title()); } else { string = tr("%1 %2 items").arg(undoStackMessage).arg(QString::number(deletedItems.count())); } QUndoCommand * parentCommand = new QUndoCommand(string); parentCommand->setText(string); deleteAux(deletedItems, parentCommand, true); } void SketchWidget::deleteAux(QSet & deletedItems, QUndoCommand * parentCommand, bool doPush) { stackSelectionState(false, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); deleteMiddle(deletedItems, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); foreach (ItemBase * itemBase, deletedItems) { if (itemBase->superpart() != NULL) { AddSubpartCommand * asc = new AddSubpartCommand(this, BaseCommand::CrossView, itemBase->superpart()->id(), itemBase->id(), parentCommand); asc->setUndoOnly(); } } // actual delete commands must come last for undo to work properly foreach (ItemBase * itemBase, deletedItems) { this->makeDeleteItemCommand(itemBase, BaseCommand::CrossView, parentCommand); } if (doPush) { m_undoStack->waitPush(parentCommand, PropChangeDelay); } } bool isVirtualWireConnector(ConnectorItem * toConnectorItem) { return (qobject_cast(toConnectorItem->attachedTo()) != NULL); } void SketchWidget::deleteMiddle(QSet & deletedItems, QUndoCommand * parentCommand) { foreach (ItemBase * itemBase, deletedItems) { foreach (ConnectorItem * fromConnectorItem, itemBase->cachedConnectorItems()) { foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(fromConnectorItem->attachedToViewLayerID()), false, parentCommand); fromConnectorItem->tempRemove(toConnectorItem, false); toConnectorItem->tempRemove(fromConnectorItem, false); } fromConnectorItem = fromConnectorItem->getCrossLayerConnectorItem(); if (fromConnectorItem) { foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(fromConnectorItem->attachedToViewLayerID()), false, parentCommand); fromConnectorItem->tempRemove(toConnectorItem, false); toConnectorItem->tempRemove(fromConnectorItem, false); } } } } } void SketchWidget::deleteTracesSlot(QSet & deletedItems, QHash & otherDeletedItems, QList & deletedIDs, bool isForeign, QUndoCommand * parentCommand) { Q_UNUSED(parentCommand); foreach (ItemBase * itemBase, deletedItems) { if (itemBase->itemType() == ModelPart::Wire) continue; if (isForeign) { itemBase = findItem(itemBase->id()); if (itemBase == NULL) continue; // only foreign items need move/transform; the current view carries its own viewgeometry itemBase->saveGeometry(); } bool isJumper = (itemBase->itemType() == ModelPart::Jumper); foreach (ConnectorItem * fromConnectorItem, itemBase->cachedConnectorItems()) { QList connectorItems; foreach (ConnectorItem * ci, fromConnectorItem->connectedToItems()) connectorItems << ci; ConnectorItem * crossConnectorItem = fromConnectorItem->getCrossLayerConnectorItem(); if (crossConnectorItem) { foreach (ConnectorItem * ci, crossConnectorItem->connectedToItems()) connectorItems << ci; } foreach (ConnectorItem * toConnectorItem, connectorItems) { Wire * wire = qobject_cast(toConnectorItem->attachedTo()); if (wire == NULL) continue; if (isJumper || (wire->isTraceType(getTraceFlag()))) { QList wires; QList ends; wire->collectChained(wires, ends); foreach (Wire * w, wires) { if (!deletedIDs.contains(w->id())) { otherDeletedItems.insert(w, this); deletedIDs.append(w->id()); } } } } } } } ChangeConnectionCommand * SketchWidget::extendChangeConnectionCommand(BaseCommand::CrossViewType crossView, long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, QUndoCommand * parentCommand) { ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) { return NULL; // for now } ItemBase * toItem = findItem(toID); if (toItem == NULL) { return NULL; // for now } ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, viewLayerPlacement); if (fromConnectorItem == NULL) return NULL; // for now ConnectorItem * toConnectorItem = findConnectorItem(toItem, toConnectorID, viewLayerPlacement); if (toConnectorItem == NULL) return NULL; // for now return extendChangeConnectionCommand(crossView, fromConnectorItem, toConnectorItem, viewLayerPlacement, connect, parentCommand); } ChangeConnectionCommand * SketchWidget::extendChangeConnectionCommand(BaseCommand::CrossViewType crossView, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, QUndoCommand * parentCommand) { // cases: // delete // paste // drop (wire) // drop (part) // move (part) // move (wire) // drag wire end // drag out new wire ItemBase * fromItem = fromConnectorItem->attachedTo(); if (fromItem == NULL) { return NULL; // for now } ItemBase * toItem = toConnectorItem->attachedTo(); if (toItem == NULL) { return NULL; // for now } return new ChangeConnectionCommand(this, crossView, fromItem->id(), fromConnectorItem->connectorSharedID(), toItem->id(), toConnectorItem->connectorSharedID(), viewLayerPlacement, connect, parentCommand); } long SketchWidget::createWire(ConnectorItem * from, ConnectorItem * to, ViewGeometry::WireFlags wireFlags, bool dontUpdate, BaseCommand::CrossViewType crossViewType, QUndoCommand * parentCommand) { if (from == NULL || to == NULL) { return -1; } long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; QPointF fromPos = from->sceneAdjustedTerminalPoint(NULL); viewGeometry.setLoc(fromPos); QPointF toPos = to->sceneAdjustedTerminalPoint(NULL); QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y()); viewGeometry.setLine(line); viewGeometry.setWireFlags(wireFlags); DebugDialog::debug(QString("creating wire %11: %1, flags: %6, from %7 %8, to %9 %10, frompos: %2 %3, topos: %4 %5") .arg(newID) .arg(fromPos.x()).arg(fromPos.y()) .arg(toPos.x()).arg(toPos.y()) .arg(wireFlags) .arg(from->attachedToTitle()).arg(from->connectorSharedID()) .arg(to->attachedToTitle()).arg(to->connectorSharedID()) .arg(m_viewID) ); ViewLayer::ViewLayerPlacement viewLayerPlacement = createWireViewLayerPlacement(from, to); new AddItemCommand(this, crossViewType, ModuleIDNames::WireModuleIDName, viewLayerPlacement, viewGeometry, newID, false, -1, parentCommand); new CheckStickyCommand(this, crossViewType, newID, false, CheckStickyCommand::RemoveOnly, parentCommand); ChangeConnectionCommand * ccc = new ChangeConnectionCommand(this, crossViewType, from->attachedToID(), from->connectorSharedID(), newID, "connector0", viewLayerPlacement, // ViewLayer::specFromID(from->attachedToViewLayerID()) true, parentCommand); ccc->setUpdateConnections(!dontUpdate); ccc = new ChangeConnectionCommand(this, crossViewType, to->attachedToID(), to->connectorSharedID(), newID, "connector1", viewLayerPlacement, // ViewLayer::specFromID(to->attachedToViewLayerID()) true, parentCommand); ccc->setUpdateConnections(!dontUpdate); return newID; } ViewLayer::ViewLayerPlacement SketchWidget::createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to) { Q_UNUSED(to); return from->attachedToViewLayerPlacement(); } void SketchWidget::moveItem(long id, ViewGeometry & viewGeometry, bool updateRatsnest) { ItemBase * itemBase = findItem(id); if (itemBase != NULL) { if (updateRatsnest) { ratsnestConnect(itemBase, true); } itemBase->moveItem(viewGeometry); if (m_infoView) m_infoView->updateLocation(itemBase); } } void SketchWidget::simpleMoveItem(long id, QPointF p) { ItemBase * itemBase = findItem(id); if (itemBase != NULL) { itemBase->setItemPos(p); if (m_infoView) m_infoView->updateLocation(itemBase); } } void SketchWidget::moveItem(long id, const QPointF & p, bool updateRatsnest) { ItemBase * itemBase = findItem(id); if (itemBase != NULL) { itemBase->setPos(p); if (updateRatsnest) { ratsnestConnect(itemBase, true); } emit itemMovedSignal(itemBase); if (m_infoView) m_infoView->updateLocation(itemBase); } } void SketchWidget::updateWire(long id, const QString & connectorID, bool updateRatsnest) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; Wire * wire = qobject_cast(itemBase); if (wire == NULL) return; ConnectorItem * connectorItem = findConnectorItem(wire, connectorID, ViewLayer::specFromID(wire->viewLayerID())); if (connectorItem == NULL) return; if (updateRatsnest) { ratsnestConnect(connectorItem, true); } wire->simpleConnectedMoved(connectorItem); } void SketchWidget::rotateItem(long id, double degrees) { //DebugDialog::debug(QString("rotating %1 %2").arg(id).arg(degrees) ); if (!isVisible()) return; ItemBase * itemBase = findItem(id); if (itemBase != NULL) { itemBase->rotateItem(degrees, false); if (m_infoView) m_infoView->updateRotation(itemBase); } } void SketchWidget::transformItem(long id, const QMatrix & matrix) { ItemBase * itemBase = findItem(id); if (itemBase != NULL) { itemBase->transformItem2(matrix); if (m_infoView) m_infoView->updateRotation(itemBase); } } void SketchWidget::flipItem(long id, Qt::Orientations orientation) { //DebugDialog::debug(QString("flipping %1 %2").arg(id).arg(orientation) ); if (!isVisible()) return; ItemBase * itemBase = findItem(id); if (itemBase != NULL) { itemBase->flipItem(orientation); if (m_infoView) m_infoView->updateRotation(itemBase); ratsnestConnect(itemBase, true); } } void SketchWidget::changeWire(long fromID, QLineF line, QPointF pos, bool updateConnections, bool updateRatsnest) { /* DebugDialog::debug(QString("change wire %1; %2,%3,%4,%5; %6,%7; %8") .arg(fromID) .arg(line.x1()) .arg(line.y1()) .arg(line.x2()) .arg(line.y2()) .arg(pos.x()) .arg(pos.y()) .arg(updateConnections) ); */ ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) return; Wire* wire = dynamic_cast(fromItem); if (wire == NULL) return; wire->setLineAnd(line, pos, true); if (updateConnections) { QList already; wire->updateConnections(wire->connector0(), false, already); wire->updateConnections(wire->connector1(), false, already); } if (updateRatsnest) { ratsnestConnect(wire->connector0(), true); ratsnestConnect(wire->connector1(), true); } } void SketchWidget::rotateLeg(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool active) { ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) { DebugDialog::debug("rotate leg exit 1"); return; } ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(fromItem->viewLayerID())); if (fromConnectorItem == NULL) { DebugDialog::debug("rotate leg exit 2"); return; } fromConnectorItem->rotateLeg(leg, active); } void SketchWidget::changeLeg(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool relative, const QString & why) { changeLegAux(fromID, fromConnectorID, leg, false, relative, true, why); } void SketchWidget::recalcLeg(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool relative, bool active, const QString & why) { changeLegAux(fromID, fromConnectorID, leg, true, relative, active, why); } void SketchWidget::changeLegAux(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool reset, bool relative, bool active, const QString & why) { ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) { DebugDialog::debug("change leg exit 1"); return; } ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(fromItem->viewLayerID())); if (fromConnectorItem == NULL) { DebugDialog::debug("change leg exit 2"); return; } if (reset) { fromConnectorItem->resetLeg(leg, relative, active, why); } else { fromConnectorItem->setLeg(leg, relative, why); } QList already; fromItem->updateConnections(fromConnectorItem, false, already); } void SketchWidget::selectItem(long id, bool state, bool updateInfoView, bool doEmit) { this->clearHoldingSelectItem(); ItemBase * item = findItem(id); if (item != NULL) { item->setSelected(state); if(updateInfoView) { // show something in the info view, even if it's not selected viewItemInfo(item); } if (doEmit) { emit itemSelectedSignal(id, state); } } PaletteItem *pitem = dynamic_cast(item); if(pitem) { setLastPaletteItemSelected(pitem); } } void SketchWidget::selectDeselectAllCommand(bool state) { this->clearHoldingSelectItem(); SelectItemCommand * cmd = stackSelectionState(false, NULL); cmd->setText(state ? tr("Select All") : tr("Deselect")); cmd->setSelectItemType( state ? SelectItemCommand::SelectAll : SelectItemCommand::DeselectAll ); m_undoStack->push(cmd); } void SketchWidget::selectAllItems(bool state, bool doEmit) { foreach (QGraphicsItem * item, this->scene()->items()) { item->setSelected(state); } if (doEmit) { emit selectAllItemsSignal(state, false); } } void SketchWidget::cut() { copy(); cutDeleteAux("Cut", false, NULL); } void SketchWidget::copy() { QList tlBases; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item); if (itemBase == NULL) continue; if (itemBase->getRatsnest()) continue; if (tlBases.contains(itemBase)) continue; QList superSubs = collectSuperSubs(itemBase); if (superSubs.count() > 0) { foreach (ItemBase * supersub, superSubs) { if (!tlBases.contains(supersub)) tlBases.append(supersub); } continue; } tlBases.append(itemBase); } QList bases; sortAnyByZ(tlBases, bases); // sort them in z-order so the copies also appear in the same order copyAux(bases, true); } void SketchWidget::copyAux(QList & bases, bool saveBoundingRects) { for (int i = bases.count() - 1; i >= 0; i--) { Wire * wire = qobject_cast(bases.at(i)); if (wire == NULL) continue; if (!wire->getTrace()) continue; QList ends; QList wires; wire->collectChained(wires, ends); if (ends.count() < 2) { // trace is dangling bases.removeAt(i); continue; } foreach (ConnectorItem * end, ends) { if (bases.contains(end->attachedTo())) continue; if (bases.contains(end->attachedTo()->layerKinChief())) continue; // trace is dangling bases.removeAt(i); break; } } if (bases.count() == 0) { return; } QByteArray itemData; QList modelIndexes; copyHeart(bases, saveBoundingRects, itemData, modelIndexes); // only preserve connections for copied items that connect to each other QByteArray newItemData = removeOutsideConnections(itemData, modelIndexes); QMimeData *mimeData = new QMimeData; mimeData->setData("application/x-dnditemsdata", newItemData); mimeData->setData("text/plain", newItemData); QClipboard *clipboard = QApplication::clipboard(); if (clipboard == NULL) { // shouldn't happen delete mimeData; return; } clipboard->setMimeData(mimeData, QClipboard::Clipboard); } void SketchWidget::pasteHeart(QByteArray & itemData, bool seekOutsideConnections) { QList modelParts; QHash boundingRects; if (m_sketchModel->paste(m_referenceModel, itemData, modelParts, boundingRects, true)) { QRectF r; QRectF boundingRect = boundingRects.value(this->viewName(), r); QList newIDs; this->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, true, &r, seekOutsideConnections, newIDs); } } void SketchWidget::copyHeart(QList & bases, bool saveBoundingRects, QByteArray & itemData, QList & modelIndexes) { QXmlStreamWriter streamWriter(&itemData); streamWriter.writeStartElement("module"); streamWriter.writeAttribute("fritzingVersion", Version::versionString()); if (saveBoundingRects) { QRectF itemsBoundingRect; foreach (ItemBase * itemBase, bases) { if (itemBase->getRatsnest()) continue; itemsBoundingRect |= itemBase->sceneBoundingRect(); } QHash boundingRects; boundingRects.insert(m_viewName, itemsBoundingRect); emit copyBoundingRectsSignal(boundingRects); streamWriter.writeStartElement("boundingRects"); foreach (QString key, boundingRects.keys()) { streamWriter.writeStartElement("boundingRect"); streamWriter.writeAttribute("name", key); QRectF r = boundingRects.value(key); streamWriter.writeAttribute("rect", QString("%1 %2 %3 %4") .arg(r.left()) .arg(r.top()) .arg(r.width()) .arg(r.height())); streamWriter.writeEndElement(); } streamWriter.writeEndElement(); } streamWriter.writeStartElement("instances"); foreach (ItemBase * base, bases) { if (base->getRatsnest()) continue; base->modelPart()->saveInstances("", streamWriter, false); modelIndexes.append(base->modelPart()->modelIndex()); } streamWriter.writeEndElement(); streamWriter.writeEndElement(); } QByteArray SketchWidget::removeOutsideConnections(const QByteArray & itemData, QList & modelIndexes) { // now have to remove each connection that points to a part outside of the set of parts being copied QDomDocument domDocument; QString errorStr; int errorLine; int errorColumn; bool result = domDocument.setContent(itemData, &errorStr, &errorLine, &errorColumn); if (!result) return ___emptyByteArray___; QDomElement root = domDocument.documentElement(); if (root.isNull()) { return ___emptyByteArray___; } QDomElement instances = root.firstChildElement("instances"); if (instances.isNull()) return ___emptyByteArray___; QDomElement instance = instances.firstChildElement("instance"); while (!instance.isNull()) { QDomElement views = instance.firstChildElement("views"); if (!views.isNull()) { QDomElement view = views.firstChildElement(); while (!view.isNull()) { QDomElement connectors = view.firstChildElement("connectors"); if (!connectors.isNull()) { QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement connects = connector.firstChildElement("connects"); if (!connects.isNull()) { QDomElement connect = connects.firstChildElement("connect"); QList toDelete; while (!connect.isNull()) { long modelIndex = connect.attribute("modelIndex").toLong(); if (!modelIndexes.contains(modelIndex)) { toDelete.append(connect); } connect = connect.nextSiblingElement("connect"); } foreach (QDomElement connect, toDelete) { QDomNode removed = connects.removeChild(connect); if (removed.isNull()) { DebugDialog::debug("removed is null"); } } } connector = connector.nextSiblingElement("connector"); } } view = view.nextSiblingElement(); } } instance = instance.nextSiblingElement("instance"); } return domDocument.toByteArray(); } void SketchWidget::dragEnterEvent(QDragEnterEvent *event) { if (dragEnterEventAux(event)) { setupAutoscroll(false); event->acceptProposedAction(); } else if (event->mimeData()->hasFormat("application/x-dndsketchdata")) { if (event->source() != this) { m_movingItem = NULL; SketchWidget * other = dynamic_cast(event->source()); if (other == NULL) { throw "drag enter event from unknown source"; } // TODO: this checkunder will probably never work m_checkUnder = other->m_checkUnder; m_movingItem = new QGraphicsSvgItem(); m_movingItem->setSharedRenderer(other->m_movingSVGRenderer); this->scene()->addItem(m_movingItem); m_movingItem->setPos(mapToScene(event->pos()) - other->m_movingSVGOffset); } event->acceptProposedAction(); } else { // subclass seems to call acceptProposedAction so don't invoke it // QGraphicsView::dragEnterEvent(event); event->ignore(); } } bool SketchWidget::dragEnterEventAux(QDragEnterEvent *event) { if (!event->mimeData()->hasFormat("application/x-dnditemdata")) return false; scene()->setSceneRect(scene()->sceneRect()); // prevents inadvertent scrolling when dragging in items from the parts bin m_clearSceneRect = true; m_droppingWire = false; QByteArray itemData = event->mimeData()->data("application/x-dnditemdata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QString moduleID; QPointF offset; dataStream >> moduleID >> offset; moduleID = checkDroppedModuleID(moduleID); ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID); if (modelPart == NULL) return false; if (!canDropModelPart(modelPart)) return false; m_droppingWire = (modelPart->itemType() == ModelPart::Wire); m_droppingOffset = offset; if (ItemDrag::cache().contains(this)) { m_droppingItem->setVisible(true); } else { ViewGeometry viewGeometry; QPointF p = QPointF(this->mapToScene(event->pos())) - offset; viewGeometry.setLoc(p); long fromID = ItemBase::getNextID(); bool doConnectors = true; // create temporary item for dragging m_droppingItem = addItemAuxTemp(modelPart, defaultViewLayerPlacement(modelPart), viewGeometry, fromID, doConnectors, m_viewID, true); QSizeF size = m_droppingItem->sceneBoundingRect().size(); if (size.width() < offset.x() || size.height() < offset.y()) { offset = m_droppingOffset = QPointF(size.width() / 2, size.height() / 2); } QHash savedItems; QHash savedWires; findAlignmentAnchor(m_droppingItem, savedItems, savedWires); ItemDrag::cache().insert(this, m_droppingItem); //m_droppingItem->setCacheMode(QGraphicsItem::ItemCoordinateCache); connect(ItemDrag::singleton(), SIGNAL(dragIsDoneSignal(ItemDrag *)), this, SLOT(dragIsDoneSlot(ItemDrag *))); } //ItemDrag::_setPixmapVisible(false); m_checkUnder.clear(); if (checkUnder()) { m_checkUnder.append(m_droppingItem); } // make sure relevant layer is visible ViewLayer::ViewLayerID viewLayerID; if (m_droppingWire) { viewLayerID = getWireViewLayerID(m_droppingItem->getViewGeometry(), m_droppingItem->viewLayerPlacement()); } else if(modelPart->moduleID().compare(ModuleIDNames::RulerModuleIDName) == 0) { viewLayerID = getRulerViewLayerID(); } else if(modelPart->moduleID().compare(ModuleIDNames::NoteModuleIDName) == 0) { viewLayerID = getNoteViewLayerID(); } else { viewLayerID = getPartViewLayerID(); } ensureLayerVisible(viewLayerID); // TODO: if any layer in the dragged part is visible, then don't bother calling ensureLayerVisible return true; } bool SketchWidget::canDropModelPart(ModelPart * modelPart) { LayerList layerList = modelPart->viewLayers(viewID()); foreach (ViewLayer::ViewLayerID viewLayerID, m_viewLayers.keys()) { if (layerList.contains(viewLayerID)) return true; } return false; } void SketchWidget::dragLeaveEvent(QDragLeaveEvent * event) { Q_UNUSED(event); turnOffAutoscroll(); if (m_droppingItem != NULL) { if (m_clearSceneRect) { m_clearSceneRect = false; scene()->setSceneRect(QRectF()); } m_droppingItem->setVisible(false); //ItemDrag::_setPixmapVisible(true); } else { // dragging sketch items if (m_movingItem) { delete m_movingItem; m_movingItem = NULL; } } //QGraphicsView::dragLeaveEvent(event); // we override QGraphicsView::dragEnterEvent so don't call the subclass dragLeaveEvent here } void SketchWidget::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("application/x-dnditemdata")) { dragMoveHighlightConnector(event->pos()); event->acceptProposedAction(); return; } if (event->mimeData()->hasFormat("application/x-dndsketchdata")) { if (event->source() == this) { m_globalPos = this->mapToGlobal(event->pos()); if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) { QPointF p = GraphicsUtils::calcConstraint(m_mousePressGlobalPos, m_globalPos); m_globalPos.setX(p.x()); m_globalPos.setY(p.y()); } moveItems(m_globalPos, true, m_rubberBandLegWasEnabled); m_moveEventCount++; } else { SketchWidget * other = dynamic_cast(event->source()); if (other == NULL) { throw "drag move event from unknown source"; } m_movingItem->setPos(mapToScene(event->pos()) - other->m_movingSVGOffset); } event->acceptProposedAction(); return; } //QGraphicsView::dragMoveEvent(event); // we override QGraphicsView::dragEnterEvent so don't call the subclass dragMoveEvent here } void SketchWidget::dragMoveHighlightConnector(QPoint eventPos) { if (m_droppingItem == NULL) return; m_globalPos = this->mapToGlobal(eventPos); checkAutoscroll(m_globalPos); QPointF loc = this->mapToScene(eventPos) - m_droppingOffset; if (m_alignToGrid && (m_alignmentItem != NULL)) { QPointF l = m_alignmentItem->getViewGeometry().loc(); alignLoc(loc, m_alignmentStartPoint, loc, l); //QPointF q = m_alignmentItem->pos(); //if (q != l) { // DebugDialog::debug(QString("m alignment %1 %2, %3 %4").arg(q.x()).arg(q.y()).arg(l.x()).arg(l.y())); //} } m_droppingItem->setItemPos(loc); if (m_checkUnder.contains(m_droppingItem)) { m_droppingItem->findConnectorsUnder(); } } void SketchWidget::dropEvent(QDropEvent *event) { m_alignmentItem = NULL; turnOffAutoscroll(); clearHoldingSelectItem(); if (event->mimeData()->hasFormat("application/x-dnditemdata")) { dropItemEvent(event); } else if (event->mimeData()->hasFormat("application/x-dndsketchdata")) { if (m_movingItem) { delete m_movingItem; m_movingItem = NULL; } ConnectorItem::clearEqualPotentialDisplay(); if (event->source() == this) { checkMoved(false); m_savedItems.clear(); m_savedWires.clear(); } else { SketchWidget * other = dynamic_cast(event->source()); if (other == NULL) { throw "drag and drop from unknown source"; } ItemBase * ref = other->m_moveReferenceItem; QPointF originalPos = ref->getViewGeometry().loc(); other->copyDrop(); QPointF startLocal = other->mapFromGlobal(QPoint(other->m_mousePressGlobalPos.x(), other->m_mousePressGlobalPos.y())); QPointF sceneLocal = other->mapToScene(startLocal.x(), startLocal.y()); QPointF offset = sceneLocal - originalPos; m_pasteOffset = this->mapToScene(event->pos()) - offset - originalPos; DebugDialog::debug(QString("other %1 %2, event %3 %4") .arg(startLocal.x()).arg(startLocal.y()) .arg(event->pos().x()).arg(event->pos().y()) ); m_pasteCount = 0; emit dropPasteSignal(this); } event->acceptProposedAction(); } else { QGraphicsView::dropEvent(event); } DebugDialog::debug("after drop event"); } void SketchWidget::dropItemEvent(QDropEvent *event) { if (m_droppingItem == NULL) return; if (m_clearSceneRect) { m_clearSceneRect = false; scene()->setSceneRect(QRectF()); } ModelPart * modelPart = m_droppingItem->modelPart(); if (modelPart == NULL) return; if (modelPart->modelPartShared() == NULL) return; ModelPart::ItemType itemType = modelPart->itemType(); QUndoCommand* parentCommand = new TemporaryCommand(tr("Add %1").arg(m_droppingItem->title())); stackSelectionState(false, parentCommand); CleanUpWiresCommand * cuw = new CleanUpWiresCommand(this, CleanUpWiresCommand::Noop, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); m_droppingItem->saveGeometry(); ViewGeometry viewGeometry = m_droppingItem->getViewGeometry(); long fromID = m_droppingItem->id(); BaseCommand::CrossViewType crossViewType = BaseCommand::CrossView; switch (modelPart->itemType()) { case ModelPart::Ruler: case ModelPart::Note: // rulers and notes are local to a particular view crossViewType = BaseCommand::SingleView; break; default: break; } ViewLayer::ViewLayerPlacement viewLayerPlacement; getDroppedItemViewLayerPlacement(modelPart, viewLayerPlacement); AddItemCommand * addItemCommand = newAddItemCommand(crossViewType, modelPart, modelPart->moduleID(), viewLayerPlacement, viewGeometry, fromID, true, -1, true, parentCommand); addItemCommand->setDropOrigin(this); new SetDropOffsetCommand(this, fromID, m_droppingOffset, parentCommand); new CheckStickyCommand(this, crossViewType, fromID, false, CheckStickyCommand::RemoveOnly, parentCommand); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->addRedo(fromID); new ShowLabelFirstTimeCommand(this, crossViewType, fromID, true, true, parentCommand); if (modelPart->itemType() == ModelPart::Wire && !m_lastColorSelected.isEmpty()) { new WireColorChangeCommand(this, fromID, m_lastColorSelected, m_lastColorSelected, 1.0, 1.0, parentCommand); } bool gotConnector = false; // jrc: 24 aug 2010: don't see why restoring color on dropped item is necessary //QList connectorItems; foreach (ConnectorItem * connectorItem, m_droppingItem->cachedConnectorItems()) { //connectorItem->setMarked(false); //connectorItems.append(connectorItem); ConnectorItem * to = connectorItem->overConnectorItem(); if (to != NULL) { to->connectorHover(to->attachedTo(), false); connectorItem->setOverConnectorItem(NULL); // clean up extendChangeConnectionCommand(BaseCommand::CrossView, connectorItem, to, ViewLayer::specFromID(connectorItem->attachedToViewLayerID()), true, parentCommand); gotConnector = true; } //connectorItem->clearConnectorHover(); } //foreach (ConnectorItem * connectorItem, connectorItems) { //if (!connectorItem->marked()) { //connectorItem->restoreColor(false, 0, true); //} //} //m_droppingItem->clearConnectorHover(); clearTemporaries(); killDroppingItem(); if (gotConnector) { new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); cuw->setDirection(CleanUpWiresCommand::UndoOnly); } if (itemType == ModelPart::CopperFill) { m_undoStack->waitPushTemporary(parentCommand, 10); } else { m_undoStack->waitPush(parentCommand, 10); } event->acceptProposedAction(); emit dropSignal(event->pos()); } SelectItemCommand* SketchWidget::stackSelectionState(bool pushIt, QUndoCommand * parentCommand) { // if pushIt assumes m_undoStack->beginMacro has previously been called //DebugDialog::debug(QString("stacking")); // DebugDialog::debug(QString("stack selection state %1 %2").arg(pushIt).arg((long) parentCommand)); SelectItemCommand* selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); const QList sitems = scene()->selectedItems(); for (int i = 0; i < sitems.size(); ++i) { ItemBase * base = ItemBase::extractTopLevelItemBase(sitems.at(i)); if (base == NULL) continue; selectItemCommand->addUndo(base->id()); //DebugDialog::debug(QString("\tstacking %1").arg(base->id())); } selectItemCommand->setText(tr("Selection")); if (pushIt) { m_undoStack->push(selectItemCommand); } return selectItemCommand; } bool SketchWidget::moveByArrow(double dx, double dy, QKeyEvent * event) { bool rubberBandLegEnabled = false; DebugDialog::debug(QString("move by arrow %1").arg(event == NULL ? false : event->isAutoRepeat())); if (event == NULL || !event->isAutoRepeat()) { m_dragBendpointWire = NULL; clearHoldingSelectItem(); m_savedItems.clear(); m_savedWires.clear(); m_moveEventCount = 0; m_arrowTotalX = m_arrowTotalY = 0; QPoint cp = QCursor::pos(); QPoint wp = this->mapFromGlobal(cp); QPointF sp = this->mapToScene(wp); Wire * wire = dynamic_cast(scene()->itemAt(sp, QTransform())); bool draggingWire = false; if (wire != NULL) { if (canChainWire(wire) && wire->hasConnections()) { if (canDragWire(wire) && ((event->modifiers() & altOrMetaModifier()) != 0)) { prepDragWire(wire); draggingWire = true; } } } if (!draggingWire) { rubberBandLegEnabled = (event != NULL) && ((event->modifiers() & altOrMetaModifier()) != 0); prepMove(NULL, rubberBandLegEnabled, true); } if (m_savedItems.count() == 0) return false; m_mousePressScenePos = this->mapToScene(this->rect().center()); m_movingByArrow = true; } else { //DebugDialog::debug("autorepeat"); } if (event != NULL && (event->modifiers() & Qt::ShiftModifier)) { dx *= 10; dy *= 10; } if (m_alignToGrid) { dx *= gridSizeInches() * GraphicsUtils::SVGDPI; dy *= gridSizeInches() * GraphicsUtils::SVGDPI; } m_arrowTotalX += dx; m_arrowTotalY += dy; QPoint globalPos = mapFromScene(m_mousePressScenePos + QPointF(m_arrowTotalX, m_arrowTotalY)); globalPos = mapToGlobal(globalPos); moveItems(globalPos, false, rubberBandLegEnabled); m_moveEventCount++; return true; } void SketchWidget::mousePressEvent(QMouseEvent *event) { m_originatingItem = NULL; m_draggingBendpoint = false; if (m_movingByArrow) return; m_movingByMouse = true; QMouseEvent * hackEvent = NULL; if (event->button() == Qt::MidButton && !spaceBarIsPressed()) { m_middleMouseIsPressed = true; setDragMode(QGraphicsView::ScrollHandDrag); setCursor(Qt::OpenHandCursor); // make the event look like a left button press to fool the underlying drag mode implementation event = hackEvent = new QMouseEvent(event->type(), event->pos(), event->globalPos(), Qt::LeftButton, event->buttons() | Qt::LeftButton, event->modifiers()); } m_dragBendpointWire = NULL; m_spaceBarWasPressed = spaceBarIsPressed(); if (m_spaceBarWasPressed) { InfoGraphicsView::mousePressEvent(event); if (hackEvent) delete hackEvent; return; } //setRenderHint(QPainter::Antialiasing, false); clearHoldingSelectItem(); m_savedItems.clear(); m_savedWires.clear(); m_moveEventCount = 0; m_holdingSelectItemCommand = stackSelectionState(false, NULL); m_mousePressScenePos = mapToScene(event->pos()); m_mousePressGlobalPos = event->globalPos(); squashShapes(m_mousePressScenePos); QList items = this->items(event->pos()); QGraphicsItem* wasItem = getClickedItem(items); m_anyInRotation = false; // mouse event gets passed through to individual QGraphicsItems QGraphicsView::mousePressEvent(event); if (m_anyInRotation) { m_anyInRotation = false; unsquashShapes(); return; } items = this->items(event->pos()); QGraphicsItem* item = getClickedItem(items); if (item != wasItem) { // if the item was deleted during mousePressEvent // for example, by shift-clicking a connectorItem return; } unsquashShapes(); if (item == NULL) { if (items.length() == 1) { // if we unambiguously click on a partlabel whose owner is unselected, go ahead and activate it PartLabel * partLabel = dynamic_cast(items[0]); if (partLabel != NULL) { partLabel->owner()->setSelected(true); return; } } clickBackground(event); if (m_infoView != NULL) { m_infoView->viewItemInfo(this, NULL, true); } return; } PartLabel * partLabel = dynamic_cast(item); if (partLabel != NULL) { viewItemInfo(partLabel->owner()); setLastPaletteItemSelectedIf(partLabel->owner()); return; } // Note's child items (at the moment) are the resize grip and the text editor Note * note = dynamic_cast(item->parentItem()); if (note != NULL) { return; } Stripbit * stripbit = dynamic_cast(item); if (stripbit) return; ItemBase * itemBase = dynamic_cast(item); if (itemBase) { viewItemInfo(itemBase); setLastPaletteItemSelectedIf(itemBase); } if (resizingBoardPress(itemBase)) { return; } ConnectorItem * connectorItem = dynamic_cast(wasItem); if (connectorItem != NULL && connectorItem->isDraggingLeg()) { return; } Wire * wire = dynamic_cast(item); if ((event->button() == Qt::LeftButton) && (wire != NULL)) { if (canChainWire(wire) && wire->hasConnections()) { if (canDragWire(wire) && ((event->modifiers() & altOrMetaModifier()) != 0)) { prepDragWire(wire); return; } else { m_dragCurve = curvyWiresIndicated(event->modifiers()) && wire->canHaveCurve(); m_dragBendpointWire = wire; m_dragBendpointPos = event->pos(); if (m_connectorDragWire) { // if you happen to drag on a wire which is on top of a connector // drag the bendpoint on the wire rather than the new wire from the connector // maybe a better approach to block mousePressConnectorEvent? delete m_connectorDragWire; m_connectorDragWire = NULL; } return; } } } if (resizingJumperItemPress(itemBase)) { return; } if (event->button() == Qt::LeftButton) { prepMove(itemBase ? itemBase : dynamic_cast(item->parentItem()), (event->modifiers() & altOrMetaModifier()) != 0, true); if (m_alignToGrid && (itemBase == NULL) && (event->modifiers() == Qt::NoModifier)) { Wire * wire = dynamic_cast(item->parentItem()); if (wire != NULL && wire->draggingEnd()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem != NULL) { m_draggingBendpoint = (connectorItem->connectionsCount() > 0); this->m_alignmentStartPoint = mapToScene(event->pos()) - connectorItem->sceneAdjustedTerminalPoint(NULL); } } } m_moveReferenceItem = m_savedItems.count() > 0 ? m_savedItems.values().at(0) : NULL; setupAutoscroll(true); } } void SketchWidget::prepMove(ItemBase * originatingItem, bool rubberBandLegEnabled, bool includeRatsnest) { m_originatingItem = originatingItem; m_rubberBandLegWasEnabled = rubberBandLegEnabled; m_checkUnder.clear(); //DebugDialog::debug("prep move check under = false"); QSet wires; QList items; foreach (QGraphicsItem * gitem, this->scene()->selectedItems()) { ItemBase *itemBase = dynamic_cast(gitem); if (itemBase == NULL) continue; if (itemBase->moveLock()) continue; items.append(itemBase); } //DebugDialog::debug(QString("prep move items %1").arg(items.count())); int originalItemsCount = items.count(); for (int i = 0; i < items.count(); i++) { ItemBase * itemBase = items[i]; if (itemBase->itemType() == ModelPart::Wire) { Wire * wire = qobject_cast(itemBase); if (wire->isTraceType(getTraceFlag())) { wires.insert(wire); // wire->debugInfo("adding wire"); } else if (includeRatsnest && wire->getRatsnest()) { wires.insert(wire); } continue; } ItemBase * chief = itemBase->layerKinChief(); if (chief->moveLock()) continue; m_savedItems.insert(chief->id(), chief); //chief->debugInfo("adding saved"); if (chief->isSticky()) { foreach(ItemBase * sitemBase, chief->stickyList()) { if (sitemBase->isVisible()) { if (sitemBase->itemType() == ModelPart::Wire) { Wire * wire = qobject_cast(sitemBase); if (wire->isTraceType(getTraceFlag())) { wires.insert(wire); //wire->debugInfo("adding wire"); } else if (includeRatsnest && wire->getRatsnest()) { wires.insert(wire); } } else { m_savedItems.insert(sitemBase->layerKinChief()->id(), sitemBase); //sitemBase->debugInfo("adding sitem saved"); if (!items.contains(sitemBase)) { items.append(sitemBase); } } } } } QSet set; if (collectFemaleConnectees(chief, set)) { // for historical reasons returns true if chief has male connectors if (i < originalItemsCount) { m_checkUnder.append(chief); } } foreach (ItemBase * sitemBase, set) { if (!items.contains(sitemBase)) { items.append(sitemBase); } } QSet tempWires; chief->collectWireConnectees(tempWires); foreach (Wire * wire, tempWires) { if (wire->isTraceType(getTraceFlag())) { wires.insert(wire); } else if (includeRatsnest && wire->getRatsnest()) { wires.insert(wire); } } } for (int i = m_checkUnder.count() - 1; i >= 0; i--) { ItemBase * itemBase = m_checkUnder.at(i); foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { bool gotOne = false; foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (m_savedItems.value(toConnectorItem->attachedToID(), NULL) != NULL) { m_checkUnder.removeAt(i); gotOne = true; break; } } if (gotOne) break; } } if (wires.count() > 0) { QList freeWires; categorizeDragWires(wires, freeWires); m_checkUnder.append(freeWires); } categorizeDragLegs(rubberBandLegEnabled); foreach (ItemBase * itemBase, m_savedItems.values()) { itemBase->saveGeometry(); } foreach (Wire * w, m_savedWires.keys()) { w->saveGeometry(); } findAlignmentAnchor(originatingItem, m_savedItems, m_savedWires); } void SketchWidget::alignLoc(QPointF & loc, const QPointF startPoint, const QPointF newLoc, const QPointF originalLoc) { // in the standard case, startpoint is the center of the connectorItem, newLoc is the current mouse position, // originalLoc is the original mouse position. newpos is therefore the new position of the center of the connectorItem // and ny and ny make up the nearest grid point. nx, ny - newloc give just the offset from the grid, which is then // applied to loc, which is the location of the item being dragged QPointF newPos = startPoint + newLoc - originalLoc; double ny = GraphicsUtils::getNearestOrdinate(newPos.y(), gridSizeInches() * GraphicsUtils::SVGDPI); double nx = GraphicsUtils::getNearestOrdinate(newPos.x(), gridSizeInches() * GraphicsUtils::SVGDPI); loc.setX(loc.x() + nx - newPos.x()); loc.setY(loc.y() + ny - newPos.y()); } void SketchWidget::findAlignmentAnchor(ItemBase * originatingItem, QHash & savedItems, QHash & savedWires) { m_alignmentItem = NULL; if (!m_alignToGrid) return; if (originatingItem) { foreach (ConnectorItem * connectorItem, originatingItem->cachedConnectorItems()) { m_alignmentStartPoint = connectorItem->sceneAdjustedTerminalPoint(NULL); m_alignmentItem = originatingItem; return; } if (canAlignToTopLeft(originatingItem)) { m_alignmentStartPoint = originatingItem->pos(); m_alignmentItem = originatingItem; return; } if (canAlignToCenter(originatingItem)) { m_alignmentStartPoint = originatingItem->sceneBoundingRect().center(); m_alignmentItem = originatingItem; return; } } foreach (ItemBase * itemBase, savedItems) { foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { m_alignmentStartPoint = connectorItem->sceneAdjustedTerminalPoint(NULL); m_alignmentItem = itemBase; return; } } foreach (Wire * w, savedWires.keys()) { m_alignmentItem = w; m_alignmentStartPoint = w->connector0()->sceneAdjustedTerminalPoint(NULL); return; } foreach (ItemBase * itemBase, savedItems) { if (canAlignToTopLeft(itemBase)) { m_alignmentStartPoint = itemBase->pos(); m_alignmentItem = itemBase; return; } if (canAlignToCenter(itemBase)) { m_alignmentStartPoint = itemBase->sceneBoundingRect().center(); m_alignmentItem = itemBase; return; } } } struct ConnectionThing { Wire * wire; ConnectionStatus status[2]; }; void SketchWidget::categorizeDragLegs(bool rubberBandLegEnabled) { m_stretchingLegs.clear(); if (!rubberBandLegEnabled) return; QSet passives; foreach (ItemBase * itemBase, m_savedItems.values()) { if (itemBase->itemType() == ModelPart::Wire) continue; if (!itemBase->hasRubberBandLeg()) continue; // 1. we are dragging a part with rubberBand legs which are attached to some part not being dragged along (i.e. a breadboard) // so we stretch those attached legs // 2. a part has rubberBand legs attached to multiple parts, and we are only dragging some of the parts foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { if (!connectorItem->hasRubberBandLeg()) continue; if (connectorItem->connectionsCount() == 0) continue; bool treatAsNormal = true; foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) continue; if (m_savedItems.value(toConnectorItem->attachedTo()->layerKinChief()->id(), NULL)) continue; treatAsNormal = false; break; } if (treatAsNormal) continue; if (itemBase->isSelected()) { // itemBase is being dragged, but the connector doesn't come along connectorItem->prepareToStretch(true); m_stretchingLegs.insert(itemBase, connectorItem); continue; } // itemBase has connectors stuck into multiple parts, not all of which are being dragged // but we're in a loop of savedItems, so we have to treat it later passives.insert(itemBase); } } foreach (ItemBase * itemBase, passives) { // we're not actually dragging the itemBase // one of its connectors is coming along for the ride m_savedItems.remove(itemBase->id()); foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { if (!connectorItem->hasRubberBandLeg()) continue; if (connectorItem->connectionsCount() == 0) continue; foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) continue; ItemBase * chief = toConnectorItem->attachedTo()->layerKinChief(); if (m_savedItems.value(chief->id(), NULL) == NULL) { // connected to another part // treat it as passive as well } // the connector is passively dragged along with the part it is connected to // but the part it is attached to stays put connectorItem->prepareToStretch(false); m_stretchingLegs.insert(chief, connectorItem); break; } } } } void SketchWidget::categorizeDragWires(QSet & wires, QList & freeWires) { foreach (Wire * w, wires) { QList chainedWires; QList ends; w->collectChained(chainedWires, ends); foreach (Wire * ww, chainedWires) { wires.insert(ww); } } QList connectionThings; QHash ctHash; foreach (Wire * w, wires) { ConnectionThing * ct = new ConnectionThing; ct->wire = w; ct->status[0] = ct->status[1] = UNDETERMINED_; connectionThings.append(ct); ctHash.insert(w, ct); } // handle free first foreach (Wire * w, wires) { if (w->getTrace()) continue; QList pairs; pairs << w->connector0() << w->connector1(); for (int i = 0; i < pairs.count(); i++) { ConnectorItem * ci = pairs.at(i); if (ci->connectionsCount() == 0) { ConnectionThing * ct = ctHash.value(ci->attachedTo()); if (ct->status[i] != UNDETERMINED_) continue; // if one end is FREE, treat all connected wires as free (except possibly if the other end connector is attached to something) QList chainedWires; QList ends; ct->wire->collectChained(chainedWires, ends); ends.clear(); foreach (Wire * ww, chainedWires) { ends.append(ww->connector0()); ends.append(ww->connector1()); } foreach (ConnectorItem * end, ends) { if (end->connectionsCount() == 0) { ctHash.value(end->attachedTo())->status[i] = FREE_; } else { bool onlyWires = true; foreach (ConnectorItem * to, end->connectedToItems()) { if (to->attachedToItemType() != ModelPart::Wire) { onlyWires = false; break; } } if (onlyWires) { ctHash.value(end->attachedTo())->status[i] = FREE_; } } } } } } int noChangeCount = 0; QList outWires; while (connectionThings.count() > 0) { ConnectionThing * ct = connectionThings.takeFirst(); bool changed = false; QList from; from.append(ct->wire->connector0()); from.append(ct->wire->connector1()); for (int i = 0; i < 2; i++) { if (ct->status[i] != UNDETERMINED_) continue; foreach (ConnectorItem * toConnectorItem, from.at(i)->connectedToItems()) { if (m_savedItems.keys().contains(toConnectorItem->attachedTo()->layerKinChief()->id())) { changed = true; ct->status[i] = IN_; break; } bool notWire = toConnectorItem->attachedToItemType() != ModelPart::Wire; if (notWire || outWires.contains(toConnectorItem->attachedTo())) { changed = true; ct->status[i] = OUT_; break; } } if (ct->status[i] != UNDETERMINED_) continue; ItemBase * stickingTo = ct->wire->stickingTo(); if (stickingTo != NULL) { QPointF p = from.at(i)->sceneAdjustedTerminalPoint(NULL); if (stickingTo->contains(stickingTo->mapFromScene(p))) { if (m_savedItems.keys().contains(stickingTo->layerKinChief()->id())) { ct->status[i] = IN_; changed = true; } } } if (ct->status[i] != UNDETERMINED_) continue; // it's not connected and not stuck if (ct->wire->getTrace() && from.at(i)->connectionsCount() == 0) { QPointF p = from.at(i)->sceneAdjustedTerminalPoint(NULL); foreach (QGraphicsItem * item, scene()->items(p)) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; ct->status[i] = m_savedItems.keys().contains(itemBase->layerKinChief()->id()) ? IN_ : OUT_; changed = true; break; } } if (ct->status[i] != UNDETERMINED_) continue; if (from.at(i)->connectionsCount() == 0) { DebugDialog::debug("FREE_ wire should not be found at this point"); } } if (ct->status[0] != UNDETERMINED_ && ct->status[1] != UNDETERMINED_) { if (ct->status[0] == IN_) { if (ct->status[1] == IN_ || ct->status[1] == FREE_) { m_savedItems.insert(ct->wire->id(), ct->wire); //ct->wire->debugInfo("adding saved free"); if (ct->status[1] == FREE_) freeWires.append(ct->wire); } else { // attach the connector that stays IN m_savedWires.insert(ct->wire, ct->wire->connector0()); //ct->wire->debugInfo("adding saved in"); } } else if (ct->status[0] == OUT_) { if (ct->status[1] == IN_) { // attach the connector that stays in m_savedWires.insert(ct->wire, ct->wire->connector1()); } else { // don't drag this; both ends are connected OUT outWires.append(ct->wire); } } else /* ct->status[0] == FREE_ */ { if (ct->status[1] == IN_) { // attach wire that stays IN m_savedItems.insert(ct->wire->id(), ct->wire); freeWires.append(ct->wire); } else if (ct->status[1] == FREE_) { // both sides are free, so if the wire is selected, drag it if (ct->wire->isSelected()) { m_savedItems.insert(ct->wire->id(), ct->wire); freeWires.append(ct->wire); //ct->wire->debugInfo("adding saved free 2"); } else { outWires.append(ct->wire); } } else { // don't drag this; both ends are connected OUT outWires.append(ct->wire); } } delete ct; noChangeCount = 0; } else { connectionThings.append(ct); if (changed) { noChangeCount = 0; } else { if (++noChangeCount > connectionThings.count()) { QList cts; foreach (ConnectionThing * ct, connectionThings) { // if one end is OUT and the other end is unaccounted for at this pass, then both ends are OUT if ((ct->status[0] == FREE_ || ct->status[0] == OUT_) || (ct->status[1] == FREE_ || ct->status[1] == OUT_)) { noChangeCount = 0; outWires.append(ct->wire); delete ct; } else { cts.append(ct); } } if (noChangeCount == 0) { // get ready for another pass, we got rid of some connectionThings.clear(); foreach (ConnectionThing * ct, cts) { connectionThings.append(ct); } } else { // we've elimated all OUT items so mark everybody IN foreach (ConnectionThing * ct, connectionThings) { m_savedItems.insert(ct->wire->id(), ct->wire); //ct->wire->debugInfo("adding saved in 2"); delete ct; } connectionThings.clear(); } } } } } } void SketchWidget::clickBackground(QMouseEvent *) { // in here if you clicked on the sketch itself, } void SketchWidget::prepDragWire(Wire * wire) { bool drag = true; foreach (ConnectorItem * toConnectorItem, wire->connector0()->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { m_savedWires.insert(qobject_cast(toConnectorItem->attachedTo()), toConnectorItem); } else { drag = false; break; } } if (drag) { foreach (ConnectorItem * toConnectorItem, wire->connector1()->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { m_savedWires.insert(qobject_cast(toConnectorItem->attachedTo()), toConnectorItem); } else { drag = false; break; } } } if (!drag) { m_savedWires.clear(); return; } m_savedItems.clear(); m_savedItems.insert(wire->id(), wire); //wire->debugInfo("adding saved wire"); wire->saveGeometry(); foreach (Wire * w, m_savedWires.keys()) { w->saveGeometry(); } setupAutoscroll(true); } void SketchWidget::prepDragBendpoint(Wire * wire, QPoint eventPos, bool dragCurve) { m_bendpointWire = wire; wire->saveGeometry(); ViewGeometry vg = m_bendpointVG = wire->getViewGeometry(); QPointF newPos = mapToScene(eventPos); if (dragCurve) { setupAutoscroll(true); wire->initDragCurve(newPos); wire->grabMouse(); unsquashShapes(); return; } QPointF oldPos = wire->pos(); QLineF oldLine = wire->line(); Bezier left, right; bool curved = wire->initNewBendpoint(newPos, left, right); //DebugDialog::debug(QString("oldpos"), oldPos); //DebugDialog::debug(QString("oldline p1"), oldLine.p1()); //DebugDialog::debug(QString("oldline p2"), oldLine.p2()); QLineF newLine(oldLine.p1(), newPos - oldPos); wire->setLine(newLine); if (curved) wire->changeCurve(&left); vg.setLoc(newPos); QLineF newLine2(QPointF(0,0), oldLine.p2() + oldPos - newPos); vg.setLine(newLine2); ConnectorItem * oldConnector1 = wire->connector1(); m_connectorDragWire = this->createTempWireForDragging(wire, wire->modelPart(), oldConnector1, vg, wire->viewLayerPlacement()); if (curved) { right.translateToZero(); m_connectorDragWire->changeCurve(&right); } ConnectorItem * newConnector1 = m_connectorDragWire->connector1(); foreach (ConnectorItem * toConnectorItem, oldConnector1->connectedToItems()) { oldConnector1->tempRemove(toConnectorItem, false); toConnectorItem->tempRemove(oldConnector1, false); newConnector1->tempConnectTo(toConnectorItem, false); toConnectorItem->tempConnectTo(newConnector1, false); } oldConnector1->tempConnectTo(m_connectorDragWire->connector0(), false); m_connectorDragWire->connector0()->tempConnectTo(oldConnector1, false); m_connectorDragConnector = oldConnector1; setupAutoscroll(true); m_connectorDragWire->initDragEnd(m_connectorDragWire->connector0(), newPos); m_connectorDragWire->grabMouse(); unsquashShapes(); //m_connectorDragWire->debugInfo("grabbing mouse"); } bool SketchWidget::collectFemaleConnectees(ItemBase * itemBase, QSet & items) { Q_UNUSED(itemBase); Q_UNUSED(items); return false; } bool SketchWidget::checkUnder() { return false; }; bool SketchWidget::draggingWireEnd() { if (m_connectorDragWire != NULL) return true; QGraphicsItem * mouseGrabberItem = scene()->mouseGrabberItem(); Wire * wire = dynamic_cast(mouseGrabberItem); if (wire == NULL) { ConnectorItem * connectorItem = dynamic_cast(mouseGrabberItem); if (connectorItem == NULL) return false; if (connectorItem->attachedToItemType() != ModelPart::Wire) return false; wire = qobject_cast(connectorItem->attachedTo()); } // wire->debugInfo("mouse grabber"); return wire->draggingEnd(); } void SketchWidget::mouseMoveEvent(QMouseEvent *event) { // if its just dragging a wire end do default // otherwise handle all move action here if (m_movingByArrow) return; QPointF scenePos = mapToScene(event->pos()); double posx = scenePos.x() / GraphicsUtils::SVGDPI; double posy = scenePos.y() / GraphicsUtils::SVGDPI; if ( event->buttons() & (Qt::LeftButton | Qt::RightButton ) ) { QRectF selectionRect = this->scene()->selectionArea().boundingRect(); double width = selectionRect.width() / GraphicsUtils::SVGDPI; double height = selectionRect.height() / GraphicsUtils::SVGDPI; emit cursorLocationSignal(posx, posy, width, height); //DebugDialog::debug(QString("pos= %1,%2 size= %3 %4").arg(posx).arg(posy).arg(width).arg(height)); } else { emit cursorLocationSignal(posx, posy); //DebugDialog::debug(QString("pos= %1,%2").arg(posx).arg(posy)); } if (m_dragBendpointWire != NULL) { Wire * tempWire = m_dragBendpointWire; m_dragBendpointWire = NULL; prepDragBendpoint(tempWire, m_dragBendpointPos, m_dragCurve); m_draggingBendpoint = true; DebugDialog::debug("dragging bendpoint"); this->m_alignmentStartPoint = mapToScene(m_dragBendpointPos); // not sure this will be correct... return; } if (m_spaceBarWasPressed) { InfoGraphicsView::mouseMoveEvent(event); return; } if (m_savedItems.count() > 0) { if ((event->buttons() & Qt::LeftButton) && !draggingWireEnd()) { m_globalPos = event->globalPos(); if ((m_globalPos - m_mousePressGlobalPos).manhattanLength() >= QApplication::startDragDistance()) { QMimeData *mimeData = new QMimeData; mimeData->setData("application/x-dndsketchdata", NULL); QDrag * drag = new QDrag(this); drag->setMimeData(mimeData); //QBitmap bitmap = *CursorMaster::MoveCursor->bitmap(); //drag->setDragCursor(bitmap, Qt::MoveAction); QPointF offset; QString svg = makeMoveSVG(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, offset); m_movingSVGRenderer = new QSvgRenderer(new QXmlStreamReader(svg)); m_movingSVGOffset = m_mousePressScenePos - offset; m_moveEventCount = 0; // reset m_moveEventCount to make sure that equal potential highlights are cleared m_movingByMouse = false; drag->exec(); delete m_movingSVGRenderer; m_movingSVGRenderer = NULL; return; } } } m_moveEventCount++; if (m_alignToGrid && m_draggingBendpoint) { QPointF sp(scenePos); alignLoc(sp, sp, QPointF(0,0), QPointF(0,0)); QPointF p = mapFromScene(sp); QPoint pp(qRound(p.x()), qRound(p.y())); QPointF q = mapToGlobal(pp); QMouseEvent alignedEvent(event->type(), pp, QPoint(q.x(), q.y()), event->button(), event->buttons(), event->modifiers()); //DebugDialog::debug(QString("sketch move event %1,%2").arg(sp.x()).arg(sp.y())); QGraphicsView::mouseMoveEvent(&alignedEvent); return; } if (draggingWireEnd()) { // DebugDialog::debug("dragging wire end"); checkAutoscroll(event->globalPos()); } QList squashed; if (event->buttons() == Qt::NoButton) { squashShapes(scenePos); } QGraphicsView::mouseMoveEvent(event); unsquashShapes(); } QString SketchWidget::makeMoveSVG(double printerScale, double dpi, QPointF & offset) { QRectF itemsBoundingRect; foreach (ItemBase * itemBase, m_savedItems.values()) { itemsBoundingRect |= itemBase->sceneBoundingRect(); } double width = itemsBoundingRect.width(); double height = itemsBoundingRect.height(); offset = itemsBoundingRect.topLeft(); QString outputSVG = TextUtils::makeSVGHeader(printerScale, dpi, width, height); foreach (ItemBase * itemBase, m_savedItems.values()) { Wire * wire = qobject_cast(itemBase); if (wire != NULL) { outputSVG.append(makeWireSVG(wire, offset, dpi, printerScale, true)); } else { outputSVG.append(TextUtils::makeRectSVG(itemBase->sceneBoundingRect(), offset, dpi, printerScale)); } } //outputSVG.append(makeRectSVG(itemsBoundingRect, offset, dpi, printerScale)); outputSVG += ""; return outputSVG; } void SketchWidget::moveItems(QPoint globalPos, bool checkAutoScrollFlag, bool rubberBandLegEnabled) { if (checkAutoScrollFlag) { bool result = checkAutoscroll(globalPos); if (!result) return; } QPoint q = mapFromGlobal(globalPos); QPointF scenePos = mapToScene(q); if (m_alignToGrid && (m_alignmentItem != NULL)) { QPointF currentParentPos = m_alignmentItem->mapToParent(m_alignmentItem->mapFromScene(scenePos)); QPointF buttonDownParentPos = m_alignmentItem->mapToParent(m_alignmentItem->mapFromScene(m_mousePressScenePos)); alignLoc(scenePos, m_alignmentStartPoint, currentParentPos, buttonDownParentPos); } /* DebugDialog::debug(QString("scroll 1 sx:%1 sy:%2 sbx:%3 sby:%4 qx:%5 qy:%6") .arg(scenePos.x()).arg(scenePos.y()) .arg(m_mousePressScenePos.x()).arg(m_mousePressScenePos.y()) .arg(q.x()).arg(q.y()) ); */ if (m_moveEventCount == 0) { // first time m_moveDisconnectedFromFemale.clear(); foreach (ItemBase * item, m_savedItems) { if (item->itemType() == ModelPart::Wire) continue; //DebugDialog::debug(QString("disconnecting from female %1").arg(item->instanceTitle())); disconnectFromFemale(item, m_savedItems, m_moveDisconnectedFromFemale, false, rubberBandLegEnabled, NULL); } } foreach (ItemBase * itemBase, m_savedItems) { QPointF currentParentPos = itemBase->mapToParent(itemBase->mapFromScene(scenePos)); QPointF buttonDownParentPos = itemBase->mapToParent(itemBase->mapFromScene(m_mousePressScenePos)); itemBase->setPos(itemBase->getViewGeometry().loc() + currentParentPos - buttonDownParentPos); foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) { connectorItem->stretchBy(currentParentPos - buttonDownParentPos); } if (m_checkUnder.contains(itemBase)) { findConnectorsUnder(itemBase); } /* DebugDialog::debug(QString("scroll 2 lx:%1 ly:%2 cpx:%3 cpy:%4 qx:%5 qy:%6 px:%7 py:%8") .arg(item->getViewGeometry().loc().x()).arg(item->getViewGeometry().loc().y()) .arg(currentParentPos.x()).arg(currentParentPos.y()) .arg(buttonDownParentPos.x()).arg(buttonDownParentPos.y()) .arg(item->pos().x()).arg(item->pos().y()) ); */ } foreach (Wire * wire, m_savedWires.keys()) { wire->simpleConnectedMoved(m_savedWires.value(wire)); } //DebugDialog::debug(QString("done move items %1").arg(QTime::currentTime().msec()) ); if (m_infoView) { if (m_originatingItem) { m_infoView->updateLocation(m_originatingItem->layerKinChief()); } else { foreach (ItemBase * itemBase, m_savedItems) { m_infoView->updateLocation(itemBase->layerKinChief()); } } } } void SketchWidget::findConnectorsUnder(ItemBase * item) { Q_UNUSED(item); } void SketchWidget::mouseReleaseEvent(QMouseEvent *event) { //setRenderHint(QPainter::Antialiasing, true); //DebugDialog::debug("sketch mouse release event"); m_draggingBendpoint = false; if (m_movingByArrow) return; m_alignmentItem = NULL; m_movingByMouse = false; m_dragBendpointWire = NULL; ConnectorItem::clearEqualPotentialDisplay(); if (m_spaceBarWasPressed) { QMouseEvent * hackEvent = NULL; if (m_middleMouseIsPressed) { // make the event look like a left button press to fool the underlying drag mode implementation event = hackEvent = new QMouseEvent(event->type(), event->pos(), event->globalPos(), Qt::LeftButton, event->buttons() | Qt::LeftButton, event->modifiers()); } InfoGraphicsView::mouseReleaseEvent(event); m_spaceBarWasPressed = false; if (m_middleMouseIsPressed) { m_middleMouseIsPressed = false; setDragMode(QGraphicsView::RubberBandDrag); setCursor(Qt::ArrowCursor); } //DebugDialog::debug("turning off spacebar was"); if (hackEvent) delete hackEvent; return; } if (resizingBoardRelease()) { InfoGraphicsView::mouseReleaseEvent(event); return; } if (resizingJumperItemRelease()) { InfoGraphicsView::mouseReleaseEvent(event); return; } turnOffAutoscroll(); QGraphicsView::mouseReleaseEvent(event); if (m_connectorDragWire != NULL) { // remove again (may not have been removed earlier) if (m_connectorDragWire->scene() != NULL) { removeDragWire(); //m_infoView->unregisterCurrentItem(); updateInfoView(); } if (m_bendpointWire) { // click on wire but no drag: restore original state of wire foreach (ConnectorItem * toConnectorItem, m_connectorDragWire->connector1()->connectedToItems()) { m_connectorDragWire->connector1()->tempRemove(toConnectorItem, false); toConnectorItem->tempRemove(m_connectorDragWire->connector1(), false); m_bendpointWire->connector1()->tempConnectTo(toConnectorItem, false); toConnectorItem->tempConnectTo(m_bendpointWire->connector1(), false); } m_bendpointWire->connector1()->tempRemove(m_connectorDragWire->connector0(), false); m_connectorDragWire->connector0()->tempRemove(m_bendpointWire->connector1(), false); m_bendpointWire->setLine(m_bendpointVG.line()); } DebugDialog::debug("deleting connector drag wire"); delete m_connectorDragWire; m_bendpointWire = m_connectorDragWire = NULL; m_savedItems.clear(); m_savedWires.clear(); m_connectorDragConnector = NULL; return; } // make sure this is cleared m_bendpointWire = NULL; if (m_moveEventCount == 0) { if (this->m_holdingSelectItemCommand != NULL) { if (m_holdingSelectItemCommand->updated()) { SelectItemCommand* tempCommand = m_holdingSelectItemCommand; m_holdingSelectItemCommand = NULL; //DebugDialog::debug(QString("scene changed push select %1").arg(scene()->selectedItems().count())); m_undoStack->push(tempCommand); } else { clearHoldingSelectItem(); } } } m_savedItems.clear(); m_savedWires.clear(); } bool SketchWidget::checkMoved(bool wait) { if (m_moveEventCount == 0) { return false; } int moveCount = m_savedItems.count(); if (moveCount <= 0) { return false; } ItemBase * saveBase = NULL; foreach (ItemBase * item, m_savedItems) { saveBase = item; break; } clearHoldingSelectItem(); QString moveString; QString viewName = ViewLayer::viewIDName(m_viewID); if (moveCount == 1) { moveString = tr("Move %2 (%1)").arg(viewName).arg(saveBase->title()); } else { moveString = tr("Move %2 items (%1)").arg(viewName).arg(QString::number(moveCount)); } QUndoCommand * parentCommand = new QUndoCommand(moveString); bool hasBoard = false; foreach (ItemBase * item, m_savedItems) { rememberSticky(item, parentCommand); } CleanUpWiresCommand * cuw = new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); moveLegBendpoints(true, parentCommand); bool gotConnection = true; MoveItemsCommand * moveItemsCommand = new MoveItemsCommand(this, true, parentCommand); foreach (ItemBase * item, m_savedItems) { if (item == NULL) continue; ViewGeometry viewGeometry(item->getViewGeometry()); item->saveGeometry(); moveItemsCommand->addItem(item->id(), viewGeometry.loc(), item->getViewGeometry().loc()); if (item->itemType() == ModelPart::Breadboard) { hasBoard = true; continue; } // TODO: boardtypes and breadboard types are always sticky if (Board::isBoard(item)) { hasBoard = true; } } foreach (ItemBase * item, m_savedItems) { new CheckStickyCommand(this, BaseCommand::SingleView, item->id(), false, CheckStickyCommand::RedoOnly, parentCommand); } foreach (ItemBase * item, m_savedWires.keys()) { rememberSticky(item, parentCommand); } foreach (Wire * wire, m_savedWires.keys()) { if (wire == NULL) continue; moveItemsCommand->addWire(wire->id(), m_savedWires.value(wire)->connectorSharedID()); } foreach (ItemBase * item, m_savedWires.keys()) { new CheckStickyCommand(this, BaseCommand::SingleView, item->id(), false, CheckStickyCommand::RedoOnly, parentCommand); } foreach (ConnectorItem * fromConnectorItem, m_moveDisconnectedFromFemale.uniqueKeys()) { foreach (ConnectorItem * toConnectorItem, m_moveDisconnectedFromFemale.values(fromConnectorItem)) { extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(fromConnectorItem->attachedToViewLayerID()), false, parentCommand); gotConnection = true; } } QList restoreConnectorItems; foreach (ItemBase * item, m_savedItems) { foreach (ConnectorItem * fromConnectorItem, item->cachedConnectorItems()) { if (item->itemType() == ModelPart::Wire) { if (fromConnectorItem->connectionsCount() > 0) { continue; } } ConnectorItem * toConnectorItem = fromConnectorItem->overConnectorItem(); if (toConnectorItem != NULL) { toConnectorItem->connectorHover(item, false); fromConnectorItem->setOverConnectorItem(NULL); // clean up gotConnection = true; extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), true, parentCommand); } restoreConnectorItems.append(fromConnectorItem); fromConnectorItem->clearConnectorHover(); } item->clearConnectorHover(); } QList visited; foreach (ConnectorItem * connectorItem, restoreConnectorItems) { connectorItem->restoreColor(visited); } // must restore legs after connections are restored (redo direction) moveLegBendpoints(false, parentCommand); clearTemporaries(); if (gotConnection) { new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); cuw->setDirection(CleanUpWiresCommand::UndoOnly); } if (wait) { m_undoStack->waitPush(parentCommand, PropChangeDelay); } else { m_undoStack->push(parentCommand); } return true; } void SketchWidget::setReferenceModel(ReferenceModel *referenceModel) { m_referenceModel = referenceModel; } ReferenceModel * SketchWidget::referenceModel() { return m_referenceModel; } void SketchWidget::setSketchModel(SketchModel * sketchModel) { m_sketchModel = sketchModel; } void SketchWidget::itemAddedSlot(ModelPart * modelPart, ItemBase *, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin) { if (dropOrigin != NULL && dropOrigin != this) { placePartDroppedInOtherView(modelPart, viewLayerPlacement, viewGeometry, id, dropOrigin); } else { addItemAux(modelPart, viewLayerPlacement, viewGeometry, id, true, m_viewID, false); } } ItemBase * SketchWidget::placePartDroppedInOtherView(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin) { // offset the part QPointF from = dropOrigin->mapToScene(QPoint(0, 0)); QPointF to = this->mapToScene(QPoint(0, 0)); QPointF dp = viewGeometry.loc() - from; ViewGeometry vg(viewGeometry); vg.setLoc(to + dp); ItemBase * itemBase = addItemAux(modelPart, viewLayerPlacement, vg, id, true, m_viewID, false); if (m_alignToGrid && (itemBase != NULL)) { alignOneToGrid(itemBase); } return itemBase; } void SketchWidget::itemDeletedSlot(long id) { ItemBase * pitem = findItem(id); if (pitem != NULL) { deleteItem(pitem, false, false, false); } } void SketchWidget::selectionChangedSlot() { if (m_ignoreSelectionChangeEvents > 0) { return; } emit selectionChangedSignal(); if (m_holdingSelectItemCommand != NULL) { //DebugDialog::debug("update holding command"); int selCount = 0; ItemBase* saveBase = NULL; QString selString; m_holdingSelectItemCommand->clearRedo(); const QList sitems = scene()->selectedItems(); foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * base = dynamic_cast(item); if (base == NULL) continue; saveBase = base; m_holdingSelectItemCommand->addRedo(base->layerKinChief()->id()); selCount++; } if (selCount == 1) { selString = tr("Select %1").arg(saveBase->title()); } else { selString = tr("Select %1 items").arg(QString::number(selCount)); } m_holdingSelectItemCommand->setText(selString); m_holdingSelectItemCommand->setUpdated(true); } } void SketchWidget::clearHoldingSelectItem() { // DebugDialog::debug("clear holding"); if (m_holdingSelectItemCommand != NULL) { delete m_holdingSelectItemCommand; m_holdingSelectItemCommand = NULL; } } void SketchWidget::clearSelection() { this->scene()->clearSelection(); emit clearSelectionSignal(); } void SketchWidget::clearSelectionSlot() { this->scene()->clearSelection(); } void SketchWidget::itemSelectedSlot(long id, bool state) { ItemBase * item = findItem(id); //DebugDialog::debug(QString("got item selected signal %1 %2 %3 %4").arg(id).arg(state).arg(item != NULL).arg(m_viewID)); if (item != NULL) { item->setSelected(state); } PaletteItem *pitem = dynamic_cast(item); if(pitem) { setLastPaletteItemSelected(pitem); } } void SketchWidget::prepLegSelection(ItemBase * itemBase) { this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted if (itemBase->isSelected()) return; m_holdingSelectItemCommand = stackSelectionState(false, NULL); itemBase->setSelected(true); } void SketchWidget::prepLegBendpointMove(ConnectorItem * from, int index, QPointF oldPos, QPointF newPos, ConnectorItem * to, bool changeConnections) { this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(); if (m_holdingSelectItemCommand) { SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->copyUndo(m_holdingSelectItemCommand); selectItemCommand->copyRedo(m_holdingSelectItemCommand); clearHoldingSelectItem(); } long fromID = from->attachedToID(); QString fromConnectorID = from->connectorSharedID(); long toID = -1; QString toConnectorID; if (changeConnections && (to != NULL)) { toID = to->attachedToID(); toConnectorID = to->connectorSharedID(); } if (changeConnections) { new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); } MoveLegBendpointCommand * mlbc = new MoveLegBendpointCommand(this, fromID, fromConnectorID, index, oldPos, newPos, parentCommand); mlbc->setUndoOnly(); if (changeConnections) { QList< QPointer > former = from->connectedToItems(); QString prefix; QString suffix; if (to == NULL) { if (former.count() > 0) { prefix = tr("Disconnect"); suffix = tr("from %1").arg(former.at(0)->attachedToInstanceTitle()); } else { prefix = tr("Move leg of"); } } else { prefix = tr("Connect"); suffix = tr("to %1").arg(to->attachedToInstanceTitle()); } parentCommand->setText(QObject::tr("%1 %2,%3 %4") .arg(prefix) .arg(from->attachedTo()->instanceTitle()) .arg(from->connectorSharedName()) .arg(suffix) ); if (former.count() > 0) { QList connectorItems; connectorItems.append(from); ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag); foreach (ConnectorItem * formerConnectorItem, former) { ChangeConnectionCommand * ccc = extendChangeConnectionCommand(BaseCommand::CrossView, from, formerConnectorItem, ViewLayer::specFromID(from->attachedToViewLayerID()), false, parentCommand); ccc->setUpdateConnections(false); from->tempRemove(formerConnectorItem, false); formerConnectorItem->tempRemove(from, false); } } if (to != NULL) { ChangeConnectionCommand * ccc = extendChangeConnectionCommand(BaseCommand::CrossView, from, to, ViewLayer::specFromID(from->attachedToViewLayerID()), true, parentCommand); ccc->setUpdateConnections(false); } } else { parentCommand->setText(QObject::tr("Change leg of %1,%2") .arg(from->attachedTo()->instanceTitle()) .arg(from->connectorSharedName()) ); } // change leg after connections have been restored mlbc = new MoveLegBendpointCommand(this, fromID, fromConnectorID, index, oldPos, newPos, parentCommand); mlbc->setRedoOnly(); if (changeConnections) { new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); } m_undoStack->push(parentCommand); } void SketchWidget::prepLegCurveChange(ConnectorItem * from, int index, const class Bezier * oldB, const class Bezier * newB, bool triggerFirstTime) { this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(tr("Change leg curvature for %1.").arg(from->attachedToInstanceTitle())); if (m_holdingSelectItemCommand) { SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->copyUndo(m_holdingSelectItemCommand); selectItemCommand->copyRedo(m_holdingSelectItemCommand); clearHoldingSelectItem(); } long fromID = from->attachedToID(); QString fromConnectorID = from->connectorSharedID(); ChangeLegCurveCommand * clcc = new ChangeLegCurveCommand(this, fromID, fromConnectorID, index, oldB, newB, parentCommand); if (!triggerFirstTime) { clcc->setSkipFirstRedo(); } m_undoStack->push(parentCommand); } void SketchWidget::prepLegBendpointChange(ConnectorItem * from, int oldCount, int newCount, int index, QPointF p, const class Bezier * bezier0, const class Bezier * bezier1, const class Bezier * bezier2, bool triggerFirstTime) { this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(tr("Change leg bendpoint for %1.").arg(from->attachedToInstanceTitle())); if (m_holdingSelectItemCommand) { SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->copyUndo(m_holdingSelectItemCommand); selectItemCommand->copyRedo(m_holdingSelectItemCommand); clearHoldingSelectItem(); } long fromID = from->attachedToID(); QString fromConnectorID = from->connectorSharedID(); ChangeLegBendpointCommand * clbc = new ChangeLegBendpointCommand(this, fromID, fromConnectorID, oldCount, newCount, index, p, bezier0, bezier1, bezier2, parentCommand); if (!triggerFirstTime) { clbc->setSkipFirstRedo(); } m_undoStack->push(parentCommand); } void SketchWidget::wireChangedSlot(Wire* wire, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, ConnectorItem * from, ConnectorItem * to) { this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted // TODO: make sure all these pointers to pointers to pointers aren't null... if (wire == this->m_connectorDragWire) { dragWireChanged(wire, from, to); return; } clearDragWireTempCommand(); if ((to != NULL) && from->connectedToItems().contains(to)) { // there's no change: the wire was dragged back to its original connection QList already; from->attachedTo()->updateConnections(to, false, already); return; } QUndoCommand * parentCommand = new QUndoCommand(); long fromID = wire->id(); QString fromConnectorID; if (from != NULL) { fromConnectorID = from->connectorSharedID(); } long toID = -1; QString toConnectorID; if (to != NULL) { toID = to->attachedToID(); toConnectorID = to->connectorSharedID(); } new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); rememberSticky(wire, parentCommand); bool chained = false; foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) { Wire * toWire = qobject_cast(toConnectorItem->attachedTo()); if (toWire) { chained = true; break; } } new ChangeWireCommand(this, fromID, oldLine, newLine, oldPos, newPos, true, true, parentCommand); new CheckStickyCommand(this, BaseCommand::SingleView, fromID, false, CheckStickyCommand::RedoOnly, parentCommand); foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) { Wire * toWire = qobject_cast(toConnectorItem->attachedTo()); if (toWire == NULL) continue; rememberSticky(toWire, parentCommand); ViewGeometry vg = toWire->getViewGeometry(); QLineF nl = toWire->line(); QPointF np = toWire->pos(); new ChangeWireCommand(this, toWire->id(), vg.line(), nl, vg.loc(), np, true, true, parentCommand); new CheckStickyCommand(this, BaseCommand::SingleView, toWire->id(), false, CheckStickyCommand::RedoOnly, parentCommand); } QList< QPointer > former = from->connectedToItems(); QString prefix; QString suffix; if (to == NULL) { if (former.count() > 0 && !chained) { prefix = tr("Disconnect"); // the suffix is a little tricky to determine // it might be multiple disconnects, or might be disconnecting a virtual wire, in which case, the // title needs to come from the virtual wire's other connection's attachedTo() // suffix = tr("from %1").arg(former->attachedToTitle()); } else { prefix = tr("Change"); } } else { prefix = tr("Connect"); suffix = tr("to %1").arg(to->attachedToInstanceTitle()); } parentCommand->setText(QObject::tr("%1 %2 %3").arg(prefix).arg(wire->title()).arg(suffix) ); if (!chained) { if (former.count() > 0) { foreach (ConnectorItem * formerConnectorItem, former) { extendChangeConnectionCommand(BaseCommand::CrossView, from, formerConnectorItem, ViewLayer::specFromID(wire->viewLayerID()), false, parentCommand); from->tempRemove(formerConnectorItem, false); formerConnectorItem->tempRemove(from, false); } } if (to != NULL) { extendChangeConnectionCommand(BaseCommand::CrossView, from, to, ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); } } clearTemporaries(); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::dragWireChanged(Wire* wire, ConnectorItem * fromOnWire, ConnectorItem * to) { if (m_bendpointWire != NULL && wire->getRatsnest()) { dragRatsnestChanged(); return; } prereleaseTempWireForDragging(m_connectorDragWire); BaseCommand::CrossViewType crossViewType = BaseCommand::CrossView; if (m_bendpointWire) { } else { m_connectorDragConnector->tempRemove(m_connectorDragWire->connector1(), false); m_connectorDragWire->connector1()->tempRemove(m_connectorDragConnector, false); // if to and from are the same connector, you can't draw a wire to yourself // or to == NULL and it's pcb or schematic view, bail out if ((m_connectorDragConnector == to) || !canCreateWire(wire, fromOnWire, to)) { clearDragWireTempCommand(); removeDragWire(); return; } } QUndoCommand * parentCommand = new QUndoCommand(); parentCommand->setText(tr("Create and connect wire")); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); if (m_tempDragWireCommand != NULL) { selectItemCommand->copyUndo(m_tempDragWireCommand); clearDragWireTempCommand(); } new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); m_connectorDragWire->saveGeometry(); bool doEmit = false; long fromID = wire->id(); DebugDialog::debug(QString("m_connectorDragConnector:%1 %4 from:%2 to:%3") .arg(m_connectorDragConnector->connectorSharedID()) .arg(fromOnWire->connectorSharedID()) .arg((to == NULL) ? "null" : to->connectorSharedID()) .arg(m_connectorDragConnector->attachedTo()->title()) ); // create a new wire with the same id as the temporary wire ViewGeometry vg = m_connectorDragWire->getViewGeometry(); vg.setWireFlags(getTraceFlag()); new AddItemCommand(this, crossViewType, m_connectorDragWire->moduleID(), m_connectorDragWire->viewLayerPlacement(), vg, fromID, true, -1, parentCommand); new CheckStickyCommand(this, crossViewType, fromID, false, CheckStickyCommand::RemoveOnly, parentCommand); selectItemCommand->addRedo(fromID); if (m_bendpointWire == NULL) { ConnectorItem * anchor = wire->otherConnector(fromOnWire); if (anchor != NULL) { extendChangeConnectionCommand(BaseCommand::CrossView, anchor, m_connectorDragConnector, ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); doEmit = true; } if (to != NULL) { extendChangeConnectionCommand(BaseCommand::CrossView, fromOnWire, to, ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); doEmit = true; } setUpColor(m_connectorDragConnector, to, wire, parentCommand); } else { new WireColorChangeCommand(this, wire->id(), m_bendpointWire->colorString(), m_bendpointWire->colorString(), m_bendpointWire->opacity(), m_bendpointWire->opacity(), parentCommand); new WireWidthChangeCommand(this, wire->id(), m_bendpointWire->width(), m_bendpointWire->width(), parentCommand); if (m_bendpointWire->banded()) { new SetPropCommand(this, wire->id(), "banded", "Yes", "Yes", true, parentCommand); } } if (m_bendpointWire) { ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, m_bendpointWire->id(), m_bendpointWire->undoCurve(), m_bendpointWire->curve(), m_bendpointWire->getAutoroutable(), parentCommand); cwcc->setUndoOnly(); // puts the wire in position at redo time ChangeWireCommand * cwc = new ChangeWireCommand(this, m_bendpointWire->id(), m_bendpointVG.line(), m_bendpointWire->line(), m_bendpointVG.loc(), m_bendpointWire->pos(), true, false, parentCommand); cwc->setRedoOnly(); foreach (ConnectorItem * toConnectorItem, wire->connector1()->connectedToItems()) { toConnectorItem->tempRemove(wire->connector1(), false); wire->connector1()->tempRemove(toConnectorItem, false); new ChangeConnectionCommand(this, BaseCommand::CrossView, m_bendpointWire->id(), m_connectorDragConnector->connectorSharedID(), toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), false, parentCommand); new ChangeConnectionCommand(this, BaseCommand::CrossView, wire->id(), wire->connector1()->connectorSharedID(), toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), true, parentCommand); } m_connectorDragConnector->tempRemove(wire->connector0(), false); wire->connector0()->tempRemove(m_connectorDragConnector, false); new ChangeConnectionCommand(this, BaseCommand::CrossView, m_connectorDragConnector->attachedToID(), m_connectorDragConnector->connectorSharedID(), wire->id(), wire->connector0()->connectorSharedID(), ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); new ChangeWireCurveCommand(this, wire->id(), NULL, wire->curve(), wire->getAutoroutable(), parentCommand); cwcc = new ChangeWireCurveCommand(this, m_bendpointWire->id(), m_bendpointWire->undoCurve(), m_bendpointWire->curve(), m_bendpointWire->getAutoroutable(), parentCommand); cwcc->setRedoOnly(); // puts the wire in position at undo time cwc = new ChangeWireCommand(this, m_bendpointWire->id(), m_bendpointVG.line(), m_bendpointWire->line(), m_bendpointVG.loc(), m_bendpointWire->pos(), true, false, parentCommand); cwc->setUndoOnly(); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->addRedo(m_bendpointWire->id()); m_bendpointWire = NULL; // signal that we're done } clearTemporaries(); // remove the temporary wire removeDragWire(); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } void SketchWidget::dragRatsnestChanged() { // m_bendpointWire is the original wire // m_connectorDragWire is temporary // wire == m_connectorDragWire // m_connectorDragConnector is from original wire QList wires; QList ends; m_bendpointWire->collectChained(wires, ends); if (ends.count() != 2) { // ratsnest wires should always and only have two ends: we're screwed return; } ViewLayer::ViewLayerPlacement viewLayerPlacement = createWireViewLayerPlacement(ends[0], ends[1]); if (viewLayerPlacement == ViewLayer::UnknownPlacement) { // for now this should not be possible QMessageBox::critical(this, tr("Fritzing"), tr("This seems like an attempt to create a trace across layers. This circumstance should not arise: please contact the developers.")); return; } BaseCommand::CrossViewType crossViewType = BaseCommand::CrossView; QUndoCommand * parentCommand = new QUndoCommand(); parentCommand->setText(tr("Create and connect %1").arg(m_viewID == ViewLayer::BreadboardView ? tr("wire") : tr("trace"))); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); //SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); m_connectorDragWire->saveGeometry(); m_bendpointWire->saveGeometry(); double traceWidth = getTraceWidth(); QString tColor = traceColor(viewLayerPlacement); if (!this->m_lastColorSelected.isEmpty()) { tColor = this->m_lastColorSelected; } long newID1 = ItemBase::getNextID(); ViewGeometry vg1 = m_connectorDragWire->getViewGeometry(); vg1.setWireFlags(getTraceFlag()); new AddItemCommand(this, crossViewType, m_connectorDragWire->moduleID(), viewLayerPlacement, vg1, newID1, true, -1, parentCommand); new CheckStickyCommand(this, crossViewType, newID1, false, CheckStickyCommand::RemoveOnly, parentCommand); new WireColorChangeCommand(this, newID1, tColor, tColor, 1.0, 1.0, parentCommand); new WireWidthChangeCommand(this, newID1, traceWidth, traceWidth, parentCommand); long newID2 = ItemBase::getNextID(); ViewGeometry vg2 = m_bendpointWire->getViewGeometry(); vg2.setWireFlags(getTraceFlag()); new AddItemCommand(this, crossViewType, m_bendpointWire->moduleID(), viewLayerPlacement, vg2, newID2, true, -1, parentCommand); new CheckStickyCommand(this, crossViewType, newID2, false, CheckStickyCommand::RemoveOnly, parentCommand); new WireColorChangeCommand(this, newID2, tColor, tColor, 1.0, 1.0, parentCommand); new WireWidthChangeCommand(this, newID2, traceWidth, traceWidth, parentCommand); new ChangeConnectionCommand(this, BaseCommand::CrossView, newID2, m_connectorDragConnector->connectorSharedID(), newID1, m_connectorDragWire->connector0()->connectorSharedID(), viewLayerPlacement, // ViewLayer::specFromID(wire->viewLayerID()) true, parentCommand); foreach (ConnectorItem * toConnectorItem, m_bendpointWire->connector0()->connectedToItems()) { new ChangeConnectionCommand(this, BaseCommand::CrossView, newID2, m_bendpointWire->connector0()->connectorSharedID(), toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), viewLayerPlacement, // ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()) true, parentCommand); } foreach (ConnectorItem * toConnectorItem, m_connectorDragWire->connector1()->connectedToItems()) { new ChangeConnectionCommand(this, BaseCommand::CrossView, newID1, m_connectorDragWire->connector1()->connectorSharedID(), toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), viewLayerPlacement, // ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()) true, parentCommand); m_connectorDragWire->connector1()->tempRemove(toConnectorItem, false); toConnectorItem->tempRemove(m_connectorDragWire->connector1(), false); m_bendpointWire->connector1()->tempConnectTo(toConnectorItem, false); toConnectorItem->tempConnectTo(m_bendpointWire->connector1(), false); } m_bendpointWire->setPos(m_bendpointVG.loc()); m_bendpointWire->setLine(m_bendpointVG.line()); m_connectorDragConnector->tempRemove(m_connectorDragWire->connector0(), false); m_connectorDragWire->connector0()->tempRemove(m_connectorDragConnector, false); m_bendpointWire = NULL; // signal that we're done // remove the temporary wire removeDragWire(); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } void SketchWidget::setUpColor(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Wire * wire, QUndoCommand * parentCommand) { Q_UNUSED(fromConnectorItem); Q_UNUSED(toConnectorItem); if (!this->m_lastColorSelected.isEmpty()) { new WireColorChangeCommand(this, wire->id(), m_lastColorSelected, m_lastColorSelected, wire->opacity(), wire->opacity(), parentCommand); } } void SketchWidget::addViewLayer(ViewLayer * viewLayer) { ViewLayer * oldViewLayer = m_viewLayers.value(viewLayer->viewLayerID(), NULL); if (oldViewLayer) { delete oldViewLayer; } m_viewLayers.insert(viewLayer->viewLayerID(), viewLayer); QAction* action = new QAction(QObject::tr("%1 Layer").arg(viewLayer->displayName()), this); action->setData(QVariant::fromValue(viewLayer)); action->setCheckable(true); action->setChecked(viewLayer->visible()); action->setEnabled(true); connect(action, SIGNAL(triggered()), this, SLOT(toggleLayerVisibility())); viewLayer->setAction(action); } void SketchWidget::setAllLayersVisible(bool visible) { LayerList keys = m_viewLayers.keys(); for (int i = 0; i < keys.count(); i++) { ViewLayer * viewLayer = m_viewLayers.value(keys[i]); if (viewLayer != NULL && viewLayer->action()->isEnabled()) { setLayerVisible(viewLayer, visible, true); } } } ItemCount SketchWidget::calcItemCount() { ItemCount itemCount; // TODO: replace scene()->items() QList items = scene()->items(); QList selItems = scene()->selectedItems(); itemCount.visLabelCount = itemCount.hasLabelCount = 0; itemCount.selCount = 0; itemCount.selHFlipable = itemCount.selVFlipable = itemCount.selRotatable = itemCount.sel45Rotatable = 0; itemCount.itemsCount = 0; itemCount.obsoleteCount = 0; itemCount.moveLockCount = 0; itemCount.wireCount = 0; for (int i = 0; i < selItems.count(); i++) { ItemBase * itemBase = ItemBase::extractTopLevelItemBase(selItems[i]); if (itemBase != NULL) { itemCount.selCount++; if (itemBase->moveLock()) { itemCount.moveLockCount++; } if (itemBase->hasPartLabel()) { itemCount.hasLabelCount++; if (itemBase->isPartLabelVisible()) { itemCount.visLabelCount++; } } if (itemBase->isObsolete()) { itemCount.obsoleteCount++; } if (itemBase->itemType() == ModelPart::Wire) { itemCount.wireCount++; } else { bool rotatable = itemBase->rotationAllowed(); if (rotatable) { itemCount.selRotatable++; } rotatable = itemBase->rotation45Allowed(); if (rotatable) { itemCount.sel45Rotatable++; } if (itemBase->canFlipHorizontal()) { itemCount.selHFlipable++; } if (itemBase->canFlipVertical()) { itemCount.selVFlipable++; } } } } /* DebugDialog::debug(QString("sc:%1 wc:%2 sr:%3 s45r:%4 sv:%5 sh:%6") .arg(itemCount.selCount) .arg(itemCount.wireCount) .arg(itemCount.selRotatable) .arg(itemCount.sel45Rotatable) .arg(itemCount.selVFlipable) .arg(itemCount.selHFlipable) ); */ if (itemCount.selCount - itemCount.wireCount != itemCount.selRotatable) { // if you can't rotate them all, then you can't rotate any itemCount.selRotatable = 0; } if (itemCount.selCount - itemCount.wireCount != itemCount.sel45Rotatable) { // if you can't rotate them all, then you can't rotate any itemCount.sel45Rotatable = 0; } if (itemCount.selCount - itemCount.wireCount != itemCount.selVFlipable) { itemCount.selVFlipable = 0; } if (itemCount.selCount - itemCount.wireCount != itemCount.selHFlipable) { itemCount.selHFlipable = 0; } if (itemCount.selCount > 0) { for (int i = 0; i < items.count(); i++) { if (ItemBase::extractTopLevelItemBase(items[i]) != NULL) { itemCount.itemsCount++; } } } return itemCount; } bool SketchWidget::layerIsVisible(ViewLayer::ViewLayerID viewLayerID) { ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); if (viewLayer == NULL) return false; return viewLayer->visible(); } bool SketchWidget::layerIsActive(ViewLayer::ViewLayerID viewLayerID) { ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); if (viewLayer == NULL) return false; return viewLayer->isActive(); } void SketchWidget::setLayerVisible(ViewLayer::ViewLayerID viewLayerID, bool vis, bool doChildLayers) { ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); if (viewLayer) { setLayerVisible(viewLayer, vis, doChildLayers); } } void SketchWidget::toggleLayerVisibility() { QAction * action = qobject_cast(sender()); if (action == NULL) return; ViewLayer * viewLayer = action->data().value(); if (viewLayer == NULL) return; setLayerVisible(viewLayer, !viewLayer->visible(), viewLayer->includeChildLayers()); } void SketchWidget::setLayerVisible(ViewLayer * viewLayer, bool visible, bool doChildLayers) { LayerList viewLayerIDs; viewLayerIDs.append(viewLayer->viewLayerID()); viewLayer->setVisible(visible); if (doChildLayers) { foreach (ViewLayer * childLayer, viewLayer->childLayers()) { childLayer->setVisible(visible); viewLayerIDs.append(childLayer->viewLayerID()); } } // TODO: replace scene()->items() foreach (QGraphicsItem * item, scene()->items()) { // want all items, not just topLevel ItemBase * itemBase = dynamic_cast(item); if (itemBase) { if (viewLayerIDs.contains(itemBase->viewLayerID())) { itemBase->setHidden(!visible); //DebugDialog::debug(QString("setting visible %1").arg(viewLayer->visible())); } continue; } PartLabel * partLabel = dynamic_cast(item); if (partLabel && (viewLayerIDs.contains(partLabel->viewLayerID()))) { partLabel->setHidden(!visible); } } } void SketchWidget::setLayerActive(ViewLayer::ViewLayerID viewLayerID, bool active) { ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); if (viewLayer) { setLayerActive(viewLayer, active); } } void SketchWidget::setLayerActive(ViewLayer * viewLayer, bool active) { LayerList viewLayerIDs; viewLayerIDs.append(viewLayer->viewLayerID()); viewLayer->setActive(active); foreach (ViewLayer * childLayer, viewLayer->childLayers()) { childLayer->setActive(active); viewLayerIDs.append(childLayer->viewLayerID()); } // TODO: replace scene()->items() foreach (QGraphicsItem * item, scene()->items()) { // want all items, not just topLevel ItemBase * itemBase = dynamic_cast(item); if (itemBase != NULL) { //itemBase->debugInfo("setActive"); if (viewLayerIDs.contains(itemBase->viewLayerID())) { itemBase->setInactive(!active); //DebugDialog::debug(QString("setting visible %1").arg(viewLayer->visible())); } continue; } PartLabel * partLabel = dynamic_cast(item); if (partLabel != NULL) { if (viewLayerIDs.contains(partLabel->viewLayerID())) { partLabel->setInactive(!active); } continue; } } } void SketchWidget::sendToBack() { QList bases; if (!startZChange(bases)) return; QString text = QObject::tr("Bring forward"); continueZChangeMax(bases, bases.size() - 1, -1, greaterThan, -1, text); } void SketchWidget::sendBackward() { QList bases; if (!startZChange(bases)) return; QString text = QObject::tr("Send backward"); continueZChange(bases, 0, bases.size(), lessThan, 1, text); } void SketchWidget::bringForward() { QList bases; if (!startZChange(bases)) return; QString text = QObject::tr("Bring forward"); continueZChange(bases, bases.size() - 1, -1, greaterThan, -1, text); } void SketchWidget::bringToFront() { QList bases; if (!startZChange(bases)) return; QString text = QObject::tr("Bring to front"); continueZChangeMax(bases, 0, bases.size(), lessThan, 1, text); } double SketchWidget::fitInWindow() { QRectF itemsRect; foreach(QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isEverVisible()) continue; itemsRect |= itemBase->sceneBoundingRect(); } static const double borderFactor = 0.03; itemsRect.adjust(-itemsRect.width() * borderFactor, -itemsRect.height() * borderFactor, itemsRect.width() * borderFactor, itemsRect.height() * borderFactor); QRectF viewRect = rect(); //fitInView(itemsRect.x(), itemsRect.y(), itemsRect.width(), itemsRect.height(), Qt::KeepAspectRatio); double wRelation = (viewRect.width() - this->verticalScrollBar()->width() - 5) / itemsRect.width(); double hRelation = (viewRect.height() - this->horizontalScrollBar()->height() - 5) / itemsRect.height(); //DebugDialog::debug(QString("scen rect: w%1 h%2").arg(itemsRect.width()).arg(itemsRect.height())); //DebugDialog::debug(QString("view rect: w%1 h%2").arg(viewRect.width()).arg(viewRect.height())); //DebugDialog::debug(QString("relations (v/s): w%1 h%2").arg(wRelation).arg(hRelation)); if(wRelation < hRelation) { m_scaleValue = (wRelation * 100); } else { m_scaleValue = (hRelation * 100); } this->centerOn(itemsRect.center()); this->absoluteZoom(m_scaleValue); return m_scaleValue; } bool SketchWidget::startZChange(QList & bases) { int selCount = bases.count(); if (selCount == 0) { selCount = scene()->selectedItems().count(); if (selCount <= 0) return false; } const QList items = scene()->items(); if (items.count() <= selCount) return false; sortAnyByZ(items, bases); return true; } void SketchWidget::continueZChange(QList & bases, int start, int end, bool (*test)(int current, int start), int inc, const QString & text) { bool moved = false; int last = bases.size(); for (int i = start; test(i, end); i += inc) { ItemBase * base = bases[i]; if (!base->getViewGeometry().selected()) continue; int j = i - inc; if (j >= 0 && j < last && bases[j]->viewLayerID() == base->viewLayerID()) { bases.move(i, j); moved = true; } } if (!moved) { return; } continueZChangeAux(bases, text); } void SketchWidget::continueZChangeMax(QList & bases, int start, int end, bool (*test)(int current, int start), int inc, const QString & text) { QHash marked; bool moved = false; int last = bases.size(); for (int i = start; test(i, end); i += inc) { ItemBase * base = bases[i]; if (!base->getViewGeometry().selected()) continue; if (marked[base] != NULL) continue; marked.insert(base, base); int dest = -1; for (int j = i + inc; j >= 0 && j < last && bases[j]->viewLayerID() == base->viewLayerID(); j += inc) { dest = j; } if (dest >= 0) { moved = true; bases.move(i, dest); DebugDialog::debug(QString("moving %1 to %2").arg(i).arg(dest)); i -= inc; // because we just modified the list and would miss the next item } } if (!moved) { return; } continueZChangeAux(bases, text); } void SketchWidget::continueZChangeAux(QList & bases, const QString & text) { ChangeZCommand * changeZCommand = new ChangeZCommand(this, NULL); ViewLayer::ViewLayerID lastViewLayerID = ViewLayer::UnknownLayer; double z = 0; for (int i = 0; i < bases.size(); i++) { double oldZ = bases[i]->getViewGeometry().z(); if (bases[i]->viewLayerID() != lastViewLayerID) { lastViewLayerID = bases[i]->viewLayerID(); z = qFloor(oldZ); } else { z += ViewLayer::getZIncrement(); } if (oldZ == z) continue; // optimize this by only adding z's that must change // rather than changing all of them changeZCommand->addTriplet(bases[i]->id(), oldZ, z); } changeZCommand->setText(text); m_undoStack->push(changeZCommand); } void SketchWidget::sortAnyByZ(const QList & items, QList & bases) { for (int i = 0; i < items.size(); i++) { ItemBase * base = dynamic_cast(items[i]); if (base != NULL) { bases.append(base); base->saveGeometry(); } } // order by z qSort(bases.begin(), bases.end(), ItemBase::zLessThan); } bool SketchWidget::lessThan(int a, int b) { return a < b; } bool SketchWidget::greaterThan(int a, int b) { return a > b; } void SketchWidget::changeZ(QHash triplets, double (*pairAccessor)(RealPair *) ) { // TODO: replace scene->items const QList items = scene()->items(); for (int i = 0; i < items.size(); i++) { // want all items, not just topLevel ItemBase * itemBase = dynamic_cast(items[i]); if (itemBase == NULL) continue; RealPair * pair = triplets[itemBase->id()]; if (pair == NULL) continue; double newZ = pairAccessor(pair); ViewLayer * viewLayer = m_viewLayers.value(itemBase->viewLayerID()); if (viewLayer) { newZ = viewLayer->getZFromBelow(newZ, this->viewFromBelow()); } //DebugDialog::debug(QString("change z %1 %2").arg(itemBase->id()).arg(newZ)); items[i]->setZValue(newZ); } } ViewLayer::ViewLayerID SketchWidget::getDragWireViewLayerID(ConnectorItem *) { return m_wireViewLayerID; } ViewLayer::ViewLayerID SketchWidget::getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement) { if (viewGeometry.getRatsnest()) { return ViewLayer::BreadboardRatsnest; } return m_wireViewLayerID; } ViewLayer::ViewLayerID SketchWidget::getRulerViewLayerID() { return m_rulerViewLayerID; } ViewLayer::ViewLayerID SketchWidget::getPartViewLayerID() { return m_partViewLayerID; } ViewLayer::ViewLayerID SketchWidget::getConnectorViewLayerID() { return m_connectorViewLayerID; } ViewLayer::ViewLayerID SketchWidget::getLabelViewLayerID(ItemBase *) { return ViewLayer::UnknownLayer; } ViewLayer::ViewLayerID SketchWidget::getNoteViewLayerID() { return m_noteViewLayerID; } void SketchWidget::mousePressConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) { ModelPart * wireModel = m_referenceModel->retrieveModelPart(ModuleIDNames::WireModuleIDName); if (wireModel == NULL) return; m_tempDragWireCommand = m_holdingSelectItemCommand; m_holdingSelectItemCommand = NULL; clearHoldingSelectItem(); // make sure wire layer is visible ViewLayer::ViewLayerID viewLayerID = getDragWireViewLayerID(connectorItem); ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); if (viewLayer != NULL && !viewLayer->visible()) { setLayerVisible(viewLayer, true, true); emit updateLayerMenuSignal(); } ViewGeometry viewGeometry; QPointF p = QPointF(connectorItem->mapToScene(event->pos())); viewGeometry.setLoc(p); viewGeometry.setLine(QLineF(0,0,0,0)); m_connectorDragConnector = connectorItem; m_connectorDragWire = createTempWireForDragging(NULL, wireModel, connectorItem, viewGeometry, ViewLayer::UnknownPlacement); if (m_connectorDragWire == NULL) { clearDragWireTempCommand(); return; } m_connectorDragWire->debugInfo("creating connector drag wire"); setupAutoscroll(true); // give connector item the mouse, so wire doesn't get mouse moved events m_connectorDragWire->setVisible(true); m_connectorDragWire->grabMouse(); unsquashShapes(); //m_connectorDragWire->debugInfo("grabbing mouse 2"); m_connectorDragWire->initDragEnd(m_connectorDragWire->connector0(), event->scenePos()); m_connectorDragConnector->tempConnectTo(m_connectorDragWire->connector1(), false); m_connectorDragWire->connector1()->tempConnectTo(m_connectorDragConnector, false); if (!m_lastColorSelected.isEmpty()) { m_connectorDragWire->setColorString(m_lastColorSelected, m_connectorDragWire->opacity(), false); } } void SketchWidget::rotateX(double degrees, bool rubberBandLegEnabled, ItemBase * originatingItem) { if (qAbs(degrees) < 0.01) return; clearHoldingSelectItem(); m_savedItems.clear(); m_savedWires.clear(); prepMove(originatingItem, rubberBandLegEnabled, false); QRectF itemsBoundingRect; // want the bounding rect of the original selected items, not all the items that are secondarily being rotated foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemsBoundingRect |= (item->transform() * QTransform().translate(item->x(), item->y())) .mapRect(itemBase->boundingRectWithoutLegs() /* | item->childrenBoundingRect() */); } QPointF center = itemsBoundingRect.center(); QTransform rotation; rotation.rotate(degrees); QString string = tr("Rotate %2 (%1)") .arg(ViewLayer::viewIDName(m_viewID)) .arg((m_savedItems.count() == 1) ? m_savedItems.values().at(0)->title() : QString::number(m_savedItems.count() + m_savedWires.count()) + " items" ); QUndoCommand * parentCommand = new QUndoCommand(string); //foreach (long id, m_savedItems.keys()) { //m_savedItems.value(id)->debugInfo(QString("save item %1").arg(id)); //} new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); // change legs after connections have been updated (undo direction) moveLegBendpoints(true, parentCommand); rotatePartLabels(degrees, rotation, center, parentCommand); QList wires; foreach (ItemBase * itemBase, m_savedItems.values()) { if (itemBase->itemType() == ModelPart::Wire) { wires << qobject_cast(itemBase); } } foreach (Wire * wire, wires) { rotateWire(wire, rotation, center, true, parentCommand); } foreach (ItemBase * itemBase, m_savedItems.values()) { switch (itemBase->itemType()) { case ModelPart::Via: case ModelPart::Hole: { QPointF p = itemBase->sceneBoundingRect().center(); QPointF d = p - center; QPointF dt = rotation.map(d) + center; ViewGeometry vg1 = itemBase->getViewGeometry(); ViewGeometry vg2(vg1); vg2.setLoc(vg1.loc() + dt - p); new MoveItemCommand(this, itemBase->id(), vg1, vg2, true, parentCommand); } break; case ModelPart::Wire: break; default: { ViewGeometry vg1 = itemBase->getViewGeometry(); ViewGeometry vg2(vg1); itemBase->calcRotation(rotation, center, vg2); ConnectorPairHash connectorHash; disconnectFromFemale(itemBase, m_savedItems, connectorHash, true, rubberBandLegEnabled, parentCommand); new MoveItemCommand(this, itemBase->id(), vg1, vg1, true, parentCommand); new RotateItemCommand(this, itemBase->id(), degrees, parentCommand); new MoveItemCommand(this, itemBase->id(), vg2, vg2, true, parentCommand); } break; } } foreach (Wire * wire, wires) { rotateWire(wire, rotation, center, false, parentCommand); } // change legs after connections have been updated (redo direction) QList connectorItems; foreach (ItemBase * itemBase, m_stretchingLegs.uniqueKeys()) { foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) { connectorItems.append(connectorItem); QPolygonF oldLeg, newLeg; bool active; connectorItem->stretchDone(oldLeg, newLeg, active); new RotateLegCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), oldLeg, active, parentCommand); } } foreach (Wire * wire, m_savedWires.keys()) { ViewGeometry vg1 = wire->getViewGeometry(); ViewGeometry vg2(vg1); ConnectorItem * rotater = m_savedWires.value(wire); QPointF p0 = rotater->sceneAdjustedTerminalPoint(NULL); QPointF d0 = p0 - center; QPointF d0t = rotation.map(d0); QPointF p1 = wire->otherConnector(rotater)->sceneAdjustedTerminalPoint(NULL); if (rotater == wire->connector0()) { new ChangeWireCommand(this, wire->id(), vg1.line(), QLineF(QPointF(0,0), p1 - (d0t + center)), vg1.loc(), d0t + center, true, true, parentCommand); } else { new ChangeWireCommand(this, wire->id(), vg1.line(), QLineF(QPointF(0,0), d0t + center - p1), vg1.loc(), vg1.loc(), true, true, parentCommand); } } new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } void SketchWidget::rotateWire(Wire * wire, QTransform & rotation, QPointF center, bool undoOnly, QUndoCommand * parentCommand) { //wire->debugInfo("rotating wire"); QPointF p0 = wire->connector0()->sceneAdjustedTerminalPoint(NULL); QPointF d0 = p0 - center; QPointF d0t = rotation.map(d0); QPointF p1 = wire->connector1()->sceneAdjustedTerminalPoint(NULL); QPointF d1 = p1 - center; QPointF d1t = rotation.map(d1); ViewGeometry vg1 = wire->getViewGeometry(); ChangeWireCommand * cwc = new ChangeWireCommand(this, wire->id(), vg1.line(), QLineF(QPointF(0,0), d1t - d0t), vg1.loc(), d0t + center, true, true, parentCommand); if (undoOnly) cwc->setUndoOnly(); else cwc->setRedoOnly(); const Bezier * bezier = wire->curve(); if (bezier) { Bezier * newBezier = new Bezier(); newBezier->set_endpoints(QPointF(0,0), d1t - d0t); QPointF c0 = p0 + bezier->cp0(); QPointF dc0 = c0 - center; QPointF dc0t = rotation.map(dc0); newBezier->set_cp0(dc0t - d0t); QPointF c1 = p0 + bezier->cp1(); QPointF dc1 = c1 - center; QPointF dc1t = rotation.map(dc1); newBezier->set_cp1(dc1t - d0t); ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), bezier, newBezier, wire->getAutoroutable(), parentCommand); if (undoOnly) cwcc->setUndoOnly(); else cwcc->setRedoOnly(); } } void SketchWidget::rotatePartLabels(double degrees, QTransform &, QPointF center, QUndoCommand * parentCommand) { Q_UNUSED(center); Q_UNUSED(degrees); Q_UNUSED(parentCommand); } void SketchWidget::flipX(Qt::Orientations orientation, bool rubberBandLegEnabled) { if (!this->isVisible()) return; clearHoldingSelectItem(); m_savedItems.clear(); m_savedWires.clear(); prepMove(NULL, rubberBandLegEnabled, false); QList items = scene()->selectedItems(); QList targets; for (int i = 0; i < items.size(); i++) { // can't flip layerkin (layerkin flipped indirectly) ItemBase *itemBase = ItemBase::extractTopLevelItemBase(items[i]); if (itemBase == NULL) continue; if (Board::isBoard(itemBase)) continue; switch (itemBase->itemType()) { case ModelPart::Wire: case ModelPart::Note: case ModelPart::CopperFill: case ModelPart::Unknown: case ModelPart::Via: case ModelPart::Hole: case ModelPart::Breadboard: continue; default: if (!itemBase->canFlip(orientation)) { continue; } break; } targets.append(itemBase); } if (targets.count() <= 0) { return; } QString string = tr("Flip %2 (%1)") .arg(ViewLayer::viewIDName(m_viewID)) .arg((targets.count() == 1) ? targets[0]->title() : QString::number(targets.count()) + " items" ); QUndoCommand * parentCommand = new QUndoCommand(string); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); // change legs after connections have been updated (undo direction) moveLegBendpoints(true, parentCommand); QHash emptyList; // emptylist is only used for a move command ConnectorPairHash connectorHash; foreach (ItemBase * item, targets) { disconnectFromFemale(item, emptyList, connectorHash, true, rubberBandLegEnabled, parentCommand); if (item->isSticky()) { //TODO: apply transformation to stuck items } // TODO: if item has female connectors, then apply transform to connected items new FlipItemCommand(this, item->id(), orientation, parentCommand); } // change legs after connections have been updated (redo direction) foreach (ItemBase * itemBase, m_stretchingLegs.uniqueKeys()) { foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) { QPolygonF oldLeg, newLeg; bool active; connectorItem->stretchDone(oldLeg, newLeg, active); new RotateLegCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), oldLeg, active, parentCommand); } } clearTemporaries(); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } ConnectorItem * SketchWidget::findConnectorItem(ConnectorItem * foreignConnectorItem) { ItemBase * itemBase = findItem(foreignConnectorItem->attachedTo()->layerKinChief()->id()); if (itemBase == NULL) { return NULL; } ConnectorItem * result = findConnectorItem(itemBase, foreignConnectorItem->connectorSharedID(), ViewLayer::NewBottom); if (result) return result; return findConnectorItem(itemBase, foreignConnectorItem->connectorSharedID(), ViewLayer::NewTop); } ConnectorItem * SketchWidget::findConnectorItem(ItemBase * itemBase, const QString & connectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement) { ConnectorItem * connectorItem = itemBase->findConnectorItemWithSharedID(connectorID, viewLayerPlacement); if (connectorItem != NULL) return connectorItem; DebugDialog::debug("used to seek layer kin"); /* if (seekLayerKin) { PaletteItem * pitem = qobject_cast(itemBase); if (pitem == NULL) return NULL; foreach (ItemBase * lkpi, pitem->layerKin()) { connectorItem = lkpi->findConnectorItemWithSharedID(connectorID); if (connectorItem != NULL) return connectorItem; } } */ return NULL; } PaletteItem * SketchWidget::getSelectedPart(){ QList items= scene()->selectedItems(); PaletteItem *item = NULL; // dynamic cast returns null in cases where non-PaletteItems (i.e. wires and layerKin palette items) are selected for(int i=0; i < items.count(); i++){ PaletteItem *temp = dynamic_cast(items[i]); if (temp == NULL) continue; if (item != NULL) return NULL; // there are multiple items selected item = temp; } return item; } void SketchWidget::setBackground(QColor color) { /*QBrush brush(color); brush.setTexture(QPixmap("/home/merun/workspace/fritzing_trunk/phoenix/resources/images/schematic_grid_tile.png")); scene()->setBackgroundBrush(brush);*/ scene()->setBackgroundBrush(QBrush(color)); } void SketchWidget::setBackgroundColor(QColor color, bool setPrefs) { if (setPrefs) { QSettings settings; settings.setValue(QString("%1BackgroundColor").arg(getShortName()), color.name()); } setBackground(color); } const QColor& SketchWidget::background() { return scene()->backgroundBrush().color(); } void SketchWidget::setItemMenu(QMenu* itemMenu){ m_itemMenu = itemMenu; } void SketchWidget::setWireMenu(QMenu* wireMenu){ m_wireMenu = wireMenu; } void SketchWidget::wireConnectedSlot(long fromID, QString fromConnectorID, long toID, QString toConnectorID) { ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) return; Wire* wire = qobject_cast(fromItem); if (wire == NULL) return; ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(wire->viewLayerID())); if (fromConnectorItem == NULL) { // shouldn't be here return; } ItemBase * toItem = findItem(toID); if (toItem == NULL) { // this was a disconnect return; } ConnectorItem * toConnectorItem = findConnectorItem(toItem, toConnectorID, ViewLayer::specFromID(wire->viewLayerID())); if (toConnectorItem == NULL) { // shouldn't really be here return; } QPointF p1(0,0), p2, pos; ConnectorItem * other = wire->otherConnector(fromConnectorItem); if (fromConnectorItem == wire->connector0()) { pos = toConnectorItem->sceneAdjustedTerminalPoint(fromConnectorItem); ConnectorItem * toConnector1 = other->firstConnectedToIsh(); if (toConnector1 == NULL) { p2 = other->mapToScene(other->pos()) - pos; } else { p2 = toConnector1->sceneAdjustedTerminalPoint(other); } } else { pos = wire->pos(); ConnectorItem * toConnector0 = other->firstConnectedToIsh(); if (toConnector0 == NULL) { pos = wire->pos(); } else { pos = toConnector0->sceneAdjustedTerminalPoint(other); } p2 = toConnectorItem->sceneAdjustedTerminalPoint(fromConnectorItem) - pos; } wire->setLineAnd(QLineF(p1, p2), pos, true); // here's the connect (model has been previously updated) fromConnectorItem->connectTo(toConnectorItem); toConnectorItem->connectTo(fromConnectorItem); this->update(); } void SketchWidget::wireDisconnectedSlot(long fromID, QString fromConnectorID) { DebugDialog::debug(QString("got wire disconnected")); ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) return; Wire* wire = qobject_cast(fromItem); if (wire == NULL) return; ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(wire->viewLayerID())); if (fromConnectorItem == NULL) { // shouldn't be here return; } ConnectorItem * toConnectorItem = fromConnectorItem->firstConnectedToIsh(); if (toConnectorItem != NULL) { fromConnectorItem->removeConnection(toConnectorItem, true); toConnectorItem->removeConnection(fromConnectorItem, true); } } void SketchWidget::changeConnection(long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, bool doEmit, bool updateConnections) { changeConnectionAux(fromID, fromConnectorID, toID, toConnectorID, viewLayerPlacement, connect, updateConnections); if (doEmit) { //TODO: findPartOrWire not necessary for harmonize? //fromID = findPartOrWire(fromID); //toID = findPartOrWire(toID); emit changeConnectionSignal(fromID, fromConnectorID, toID, toConnectorID, viewLayerPlacement, connect, updateConnections); } } void SketchWidget::changeConnectionAux(long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, bool updateConnections) { // only called from the above changeConnection() which is invoked only from a command object DebugDialog::debug(QString("changeConnection: from %1 %2; to %3 %4 con:%5 v:%6") .arg(fromID).arg(fromConnectorID) .arg(toID).arg(toConnectorID) .arg(connect).arg(m_viewID) ); ItemBase * fromItem = findItem(fromID); if (fromItem == NULL) { DebugDialog::debug(QString("change connection exit 1 %1").arg(fromID)); return; } ItemBase * toItem = findItem(toID); if (toItem == NULL) { DebugDialog::debug(QString("change connection exit 2 %1").arg(toID)); return; } ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, viewLayerPlacement); if (fromConnectorItem == NULL) { // shouldn't be here DebugDialog::debug(QString("change connection exit 3 %1 %2").arg(fromItem->id()).arg(fromConnectorID)); return; } ConnectorItem * toConnectorItem = findConnectorItem(toItem, toConnectorID, viewLayerPlacement); if (toConnectorItem == NULL) { // shouldn't be here DebugDialog::debug(QString("change connection exit 4 %1 %2").arg(toItem->id()).arg(toConnectorID)); return; } //fromConnectorItem->debugInfo(" from"); //toConnectorItem->debugInfo(" to"); ratsnestConnect(fromConnectorItem, toConnectorItem, connect, true); if (connect) { // canConnect checks for when a THT part has been swapped for an SMD part, and the connections are now on different layers // it seems very difficult to test this condition before the part has actually been created if (canConnect(fromItem, toItem)) { fromConnectorItem->connector()->connectTo(toConnectorItem->connector()); fromConnectorItem->connectTo(toConnectorItem); toConnectorItem->connectTo(fromConnectorItem); } } else { fromConnectorItem->connector()->disconnectFrom(toConnectorItem->connector()); fromConnectorItem->removeConnection(toConnectorItem, true); toConnectorItem->removeConnection(fromConnectorItem, true); } if (updateConnections) { if (updateOK(fromConnectorItem, toConnectorItem)) { QList already; fromConnectorItem->attachedTo()->updateConnections(fromConnectorItem, false, already); toConnectorItem->attachedTo()->updateConnections(toConnectorItem, false, already); } } } void SketchWidget::changeConnectionSlot(long fromID, QString fromConnectorID, long toID, QString toConnectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement, bool connect, bool updateConnections) { changeConnection(fromID, fromConnectorID, toID, toConnectorID, viewLayerPlacement, connect, false, updateConnections); } void SketchWidget::keyReleaseEvent(QKeyEvent * event) { //DebugDialog::debug(QString("key release event %1").arg(event->isAutoRepeat())); if (m_movingByArrow) { m_autoScrollTimer.stop(); m_arrowTimer.start(); //DebugDialog::debug("key release event"); } else { QGraphicsView::keyReleaseEvent(event); } } void SketchWidget::arrowTimerTimeout() { m_movingByArrow = false; if (checkMoved(false)) { m_savedItems.clear(); m_savedWires.clear(); } } void SketchWidget::keyPressEvent ( QKeyEvent * event ) { //DebugDialog::debug("key press event"); if ((m_inFocus.length() == 0) && !m_movingByMouse) { int dx = 0, dy = 0; switch (event->key()) { case Qt::Key_Up: dy = -1; break; case Qt::Key_Down: dy = 1; break; case Qt::Key_Left: dx = -1; break; case Qt::Key_Right: dx = 1; break; default: break; } if (dx != 0 || dy != 0) { m_arrowTimer.stop(); DebugDialog::debug("arrow press event"); ConnectorItem::clearEqualPotentialDisplay(); moveByArrow(dx, dy, event); m_arrowTimer.start(); return; } } QGraphicsView::keyPressEvent(event); } void SketchWidget::makeDeleteItemCommand(ItemBase * itemBase, BaseCommand::CrossViewType crossView, QUndoCommand * parentCommand) { if (crossView == BaseCommand::CrossView) { emit makeDeleteItemCommandPrepSignal(itemBase, true, parentCommand); } makeDeleteItemCommandPrepSlot(itemBase, false, parentCommand); if (crossView == BaseCommand::CrossView) { emit makeDeleteItemCommandFinalSignal(itemBase, true, parentCommand); } makeDeleteItemCommandFinalSlot(itemBase, false, parentCommand); } void SketchWidget::makeDeleteItemCommandPrepSlot(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand) { if (foreign) { itemBase = findItem(itemBase->id()); if (itemBase == NULL) return; } if (itemBase->isPartLabelVisible()) { ShowLabelCommand * slc = new ShowLabelCommand(this, parentCommand); slc->add(itemBase->id(), true, true); } Note * note = qobject_cast(itemBase); if (note != NULL) { ChangeNoteTextCommand * cntc = new ChangeNoteTextCommand(this, note->id(), note->text(), note->text(), QSizeF(), QSizeF(), parentCommand); cntc->setSkipFirstRedo(); } else { new ChangeLabelTextCommand(this, itemBase->id(), itemBase->instanceTitle(), itemBase->instanceTitle(), parentCommand); } if (!foreign) { QMap propsMap; prepDeleteProps(itemBase, itemBase->id(), "", propsMap, parentCommand); } rememberSticky(itemBase, parentCommand); Wire * wire = qobject_cast(itemBase); if (wire) { const Bezier * bezier = wire->curve(); if (bezier && !bezier->isEmpty()) { ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, itemBase->id(), bezier, NULL, wire->getAutoroutable(), parentCommand); cwcc->setUndoOnly(); } } if (itemBase->hasRubberBandLeg()) { foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { if (!connectorItem->hasRubberBandLeg()) continue; // backwards order: curves then polys, since these will be trigged by undo QVector beziers = connectorItem->beziers(); for (int i = 0; i < beziers.count() - 1; i++) { Bezier * bezier = beziers.at(i); if (bezier == NULL) continue; if (bezier->isEmpty()) continue; ChangeLegCurveCommand * clcc = new ChangeLegCurveCommand(this, itemBase->id(), connectorItem->connectorSharedID(), i, bezier, bezier, parentCommand); clcc->setUndoOnly(); } QPolygonF poly = connectorItem->leg(); ChangeLegCommand * clc = new ChangeLegCommand(this, itemBase->id(), connectorItem->connectorSharedID(), poly, poly, true, true, "delete", parentCommand); clc->setUndoOnly(); // TODO: beziers here } } } void SketchWidget::makeDeleteItemCommandFinalSlot(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand) { if (foreign) { itemBase = findItem(itemBase->id()); if (itemBase == NULL) return; } ModelPart * mp = itemBase->modelPart(); // single view because this is called for each view new DeleteItemCommand(this, BaseCommand::SingleView, mp->moduleID(), itemBase->viewLayerPlacement(), itemBase->getViewGeometry(), itemBase->id(), mp->modelIndex(), parentCommand); } void SketchWidget::prepDeleteProps(ItemBase * itemBase, long id, const QString & newModuleID, QMap & propsMap, QUndoCommand * parentCommand) { // TODO: does this need to be generalized to the whole set of modelpart props? // TODO: Ruler? // TODO: this all belongs in PartFactory or in a call to the part itself // NOTE: prepDeleteProps is called after a swap and assumes that the new part is closely related to the old part // meaning that the properties of itemBase (which is the old part) apply to the new part (which has not yet been created) // this works most of the time, but does not, for example, when a ResizableBoard is swapped for a custom board shape bool boardToCustomBoard = false; ModelPart * mp = (newModuleID.isEmpty()) ? itemBase->modelPart() : referenceModel()->retrieveModelPart(newModuleID); if (mp->itemType() == ModelPart::Logo && qobject_cast(itemBase) != NULL) { boardToCustomBoard = true; mp = itemBase->modelPart(); } switch (mp->itemType()) { case ModelPart::Wire: { Wire * wire = qobject_cast(itemBase); new WireWidthChangeCommand(this, id, wire->width(), wire->width(), parentCommand); new WireColorChangeCommand(this, id, wire->colorString(), wire->colorString(), wire->opacity(), wire->opacity(), parentCommand); } return; case ModelPart::Board: new ChangeBoardLayersCommand(this, m_boardLayers, m_boardLayers, parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); return; case ModelPart::ResizableBoard: { new ChangeBoardLayersCommand(this, m_boardLayers, m_boardLayers, parentCommand); ResizableBoard * brd = qobject_cast(itemBase); if (brd) { brd->saveParams(); QPointF p; QSizeF sz; brd->getParams(p, sz); ResizeBoardCommand * rbc = new ResizeBoardCommand(this, id, sz.width(), sz.height(), sz.width(), sz.height(), parentCommand); if (boardToCustomBoard) { rbc->setUndoOnly(); } } prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); } return; case ModelPart::Logo: { LogoItem * logo = qobject_cast(itemBase); logo->saveParams(); QPointF p; QSizeF sz; logo->getParams(p, sz); new ResizeBoardCommand(this, id, sz.width(), sz.height(), sz.width(), sz.height(), parentCommand); QString logoProp = logo->prop("logo"); QString shapeProp = logo->prop("shape"); if (!logoProp.isEmpty()) { new SetPropCommand(this, id, "logo", logoProp, logoProp, true, parentCommand); } else if (!shapeProp.isEmpty()) { QString newName = logo->getNewLayerFileName(propsMap.value("layer")); new LoadLogoImageCommand(this, id, shapeProp, logo->modelPart()->localProp("aspectratio").toSizeF(), logo->prop("lastfilename"), newName, false, parentCommand); } prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); } return; case ModelPart::Jumper: { JumperItem * jumper = qobject_cast(itemBase); jumper->saveParams(); QPointF p; QPointF c0, c1; jumper->getParams(p, c0, c1); new ResizeJumperItemCommand(this, id, p, c0, c1, p, c0, c1, parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); } return; case ModelPart::CopperFill: { GroundPlane * groundPlane = dynamic_cast(itemBase); new SetPropCommand(this, id, "svg", groundPlane->svg(), groundPlane->svg(), true, parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); } return; case ModelPart::Symbol: { SymbolPaletteItem * sitem = dynamic_cast(itemBase); QString label = sitem->getLabel(); if (!label.isEmpty()) { new SetPropCommand(this, id, "label", label, label, true, parentCommand); } prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); } return; default: break; } Pad* pad = qobject_cast(itemBase); if (pad != NULL) { pad->saveParams(); QPointF p; QSizeF sz; pad->getParams(p, sz); new ResizeBoardCommand(this, id, sz.width(), sz.height(), sz.width(), sz.height(), parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); return; } Resistor * resistor = qobject_cast(itemBase); if (resistor != NULL) { new SetResistanceCommand(this, id, resistor->resistance(), resistor->resistance(), resistor->pinSpacing(), resistor->pinSpacing(), parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); return; } MysteryPart * mysteryPart = qobject_cast(itemBase); if (mysteryPart != NULL) { new SetPropCommand(this, id, "chip label", mysteryPart->chipLabel(), mysteryPart->chipLabel(), true, parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); return; } /* PinHeader * pinHeader = qobject_cast(itemBase); if (pinHeader != NULL) { // deal with old-style pin headers (pre 0.6.4) new SetPropCommand(this, id, "form", pinHeader->form(), PinHeader::findForm(newModuleID), true, parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, parentCommand); return; } */ Hole * hole = qobject_cast(itemBase); if (hole != NULL) { new SetPropCommand(this, id, "hole size", hole->holeSize(), hole->holeSize(), true, parentCommand); prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); return; } prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand); } void SketchWidget::prepDeleteOtherProps(ItemBase * itemBase, long id, const QString & newModuleID, QMap & propsMap, QUndoCommand * parentCommand) { Capacitor * capacitor = qobject_cast(itemBase); if (capacitor) { QHash properties; capacitor->getProperties(properties); foreach(QString prop, properties.keys()) { new SetPropCommand(this, id, prop, properties.value(prop), properties.value(prop), true, parentCommand); } } if (itemBase->moduleID().endsWith(ModuleIDNames::StripboardModuleIDName) || itemBase->moduleID().endsWith(ModuleIDNames::Stripboard2ModuleIDName)) { QString buses = itemBase->prop("buses"); QString newBuses = propsMap.value("buses"); if (newBuses.isEmpty()) newBuses = buses; if (!buses.isEmpty()) { new SetPropCommand(this, id, "buses", buses, newBuses, true, parentCommand); } QString layout = itemBase->prop("layout"); QString newLayout = propsMap.value("layout"); if (newLayout.isEmpty()) newLayout = layout; if (!layout.isEmpty()) { new SetPropCommand(this, id, "layout", layout, newLayout, true, parentCommand); } } QString value = itemBase->modelPart()->localProp(ModelPartShared::PartNumberPropertyName).toString(); if (!value.isEmpty()) { QString newValue = value; if (!newModuleID.isEmpty()) { newValue = ""; ModelPart * newModelPart = m_referenceModel->retrieveModelPart(newModuleID); if (newModelPart != NULL) { newValue = newModelPart->properties().value(ModelPartShared::PartNumberPropertyName, ""); } } new SetPropCommand(this, id, ModelPartShared::PartNumberPropertyName, value, newValue, true, parentCommand); } } void SketchWidget::rememberSticky(long id, QUndoCommand * parentCommand) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; rememberSticky(itemBase, parentCommand); } void SketchWidget::rememberSticky(ItemBase * itemBase, QUndoCommand * parentCommand) { QList< QPointer > stickyList = itemBase->stickyList(); if (stickyList.count() <= 0) return; CheckStickyCommand * checkStickyCommand = new CheckStickyCommand(this, BaseCommand::SingleView, itemBase->id(), false, CheckStickyCommand::UndoOnly, parentCommand); if (itemBase->isBaseSticky()) { foreach (ItemBase * sticking, stickyList) { checkStickyCommand->stick(this, itemBase->id(), sticking->id(), true); } } else if (itemBase->stickingTo() != NULL) { checkStickyCommand->stick(this, itemBase->stickingTo()->id(), itemBase->id(), true); } } ViewLayer::ViewID SketchWidget::viewID() { return m_viewID; } void SketchWidget::setViewLayerIDs(ViewLayer::ViewLayerID part, ViewLayer::ViewLayerID wire, ViewLayer::ViewLayerID connector, ViewLayer::ViewLayerID ruler, ViewLayer::ViewLayerID note) { m_partViewLayerID = part; m_wireViewLayerID = wire; m_connectorViewLayerID = connector; m_rulerViewLayerID = ruler; m_noteViewLayerID = note; } void SketchWidget::dragIsDoneSlot(ItemDrag * itemDrag) { disconnect(itemDrag, SIGNAL(dragIsDoneSignal(ItemDrag *)), this, SLOT(dragIsDoneSlot(ItemDrag *))); killDroppingItem(); // drag is done, but nothing dropped here: remove the temporary item } void SketchWidget::clearTemporaries() { for (int i = 0; i < m_temporaries.count(); i++) { QGraphicsItem * item = m_temporaries[i]; scene()->removeItem(item); delete item; } m_temporaries.clear(); } void SketchWidget::killDroppingItem() { m_alignmentItem = NULL; if (m_droppingItem != NULL) { m_droppingItem->removeLayerKin(); this->scene()->removeItem(m_droppingItem); if (m_droppingItem->modelPart()) { delete m_droppingItem->modelPart(); } delete m_droppingItem; m_droppingItem = NULL; } } ViewLayer::ViewLayerID SketchWidget::getViewLayerID(ModelPart * modelPart, ViewLayer::ViewID viewID, ViewLayer::ViewLayerPlacement viewLayerPlacement) { LayerList viewLayers = modelPart->viewLayers(viewID); if (viewLayers.count() == 1) { return viewLayers.at(0); } return multiLayerGetViewLayerID(modelPart, viewID, viewLayerPlacement, viewLayers); } ViewLayer::ViewLayerID SketchWidget::multiLayerGetViewLayerID(ModelPart * modelPart, ViewLayer::ViewID viewID, ViewLayer::ViewLayerPlacement viewLayerPlacement, LayerList & layerList) { Q_UNUSED(modelPart); Q_UNUSED(viewID); Q_UNUSED(viewLayerPlacement); if (layerList.count() == 0) return ViewLayer::UnknownLayer; return layerList.at(0); } ItemBase * SketchWidget::overSticky(ItemBase * itemBase) { if (!itemBase->stickyEnabled()) return NULL; foreach (QGraphicsItem * item, scene()->collidingItems(itemBase)) { ItemBase * s = dynamic_cast(item); if (s == NULL) continue; if (s == itemBase) continue; if (!s->isBaseSticky()) continue; return s->layerKinChief(); } return NULL; } void SketchWidget::stickem(long stickTargetID, long stickSourceID, bool stick) { ItemBase * stickTarget = findItem(stickTargetID); if (stickTarget == NULL) return; ItemBase * stickSource = findItem(stickSourceID); if (stickSource == NULL) return; stickTarget->addSticky(stickSource, stick); stickSource->addSticky(stickTarget, stick); } void SketchWidget::setChainDrag(bool chainDrag) { m_chainDrag = chainDrag; } void SketchWidget::stickyScoop(ItemBase * stickyOne, bool checkCurrent, CheckStickyCommand * checkStickyCommand) { // TODO: use the shape rather than the rect // need to find the best layerkin to use in that case //foreach (QGraphicsItem * item, scene()->collidingItems(stickyOne)) { QList added; QList already; QPolygonF poly = stickyOne->mapToScene(stickyOne->boundingRect()); foreach (QGraphicsItem * item, scene()->items(poly)) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemBase = itemBase->layerKinChief(); if (!itemBase->stickyEnabled()) continue; if (added.contains(itemBase)) continue; if (itemBase->isBaseSticky()) continue; if (stickyOne->alreadySticking(itemBase)) { already.append(itemBase); continue; } stickyOne->addSticky(itemBase, true); itemBase->addSticky(stickyOne, true); if (checkStickyCommand) { checkStickyCommand->stick(this, stickyOne->id(), itemBase->id(), true); } added.append(itemBase); } if (checkCurrent) { foreach (ItemBase * itemBase, stickyOne->stickyList()) { if (added.contains(itemBase)) continue; if (already.contains(itemBase)) continue; stickyOne->addSticky(itemBase, false); itemBase->addSticky(stickyOne, false); if (checkStickyCommand) { checkStickyCommand->stick(this, stickyOne->id(), itemBase->id(), false); } } } } void SketchWidget::wireSplitSlot(Wire* wire, QPointF newPos, QPointF oldPos, const QLineF & oldLine) { if (!canChainWire(wire)) return; this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(); parentCommand->setText(QObject::tr("Split Wire") ); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); long fromID = wire->id(); QLineF newLine(oldLine.p1(), newPos - oldPos); long newID = ItemBase::getNextID(); ViewGeometry vg(wire->getViewGeometry()); vg.setLoc(newPos); QLineF newLine2(QPointF(0,0), oldLine.p2() + oldPos - newPos); vg.setLine(newLine2); BaseCommand::CrossViewType crossView = wireSplitCrossView(); new AddItemCommand(this, crossView, ModuleIDNames::WireModuleIDName, wire->viewLayerPlacement(), vg, newID, true, -1, parentCommand); new CheckStickyCommand(this, crossView, newID, false, CheckStickyCommand::RemoveOnly, parentCommand); new WireColorChangeCommand(this, newID, wire->colorString(), wire->colorString(), wire->opacity(), wire->opacity(), parentCommand); new WireWidthChangeCommand(this, newID, wire->width(), wire->width(), parentCommand); if (wire->banded()) { new SetPropCommand(this, newID, "banded", "Yes", "Yes", true, parentCommand); } // disconnect from original wire and reconnect to new wire ConnectorItem * connector1 = wire->connector1(); foreach (ConnectorItem * toConnectorItem, connector1->connectedToItems()) { new ChangeConnectionCommand(this, crossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), wire->id(), connector1->connectorSharedID(), ViewLayer::specFromID(wire->viewLayerID()), false, parentCommand); new ChangeConnectionCommand(this, crossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), newID, connector1->connectorSharedID(), ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); } new ChangeWireCommand(this, fromID, oldLine, newLine, oldPos, oldPos, true, false, parentCommand); // connect the two wires new ChangeConnectionCommand(this, crossView, wire->id(), connector1->connectorSharedID(), newID, "connector0", ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); selectItemCommand->addRedo(newID); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } void SketchWidget::wireJoinSlot(Wire* wire, ConnectorItem * clickedConnectorItem) { // if (!canChainWire(wire)) return; // can't join a wire in some views (for now) this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted QUndoCommand * parentCommand = new QUndoCommand(); parentCommand->setText(QObject::tr("Join Wire") ); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); // assumes there is one and only one item connected ConnectorItem * toConnectorItem = clickedConnectorItem->connectedToItems()[0]; if (toConnectorItem == NULL) return; Wire * toWire = qobject_cast(toConnectorItem->attachedTo()); if (toWire == NULL) return; if (wire->id() > toWire->id()) { // delete the wire with the higher id // so we can keep the three views in sync // i.e. the original wire has the lowest id in the chain Wire * wtemp = toWire; toWire = wire; wire = wtemp; ConnectorItem * ctemp = toConnectorItem; toConnectorItem = clickedConnectorItem; clickedConnectorItem = ctemp; } ConnectorItem * otherConnector = toWire->otherConnector(toConnectorItem); BaseCommand::CrossViewType crossView = BaseCommand::CrossView; // wireSplitCrossView(); ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), wire->curve(), NULL, wire->getAutoroutable(), parentCommand); cwcc->setUndoOnly(); cwcc = new ChangeWireCurveCommand(this, toWire->id(), toWire->curve(), NULL, toWire->getAutoroutable(),parentCommand); cwcc->setUndoOnly(); // disconnect the wires new ChangeConnectionCommand(this, crossView, wire->id(), clickedConnectorItem->connectorSharedID(), toWire->id(), toConnectorItem->connectorSharedID(), ViewLayer::specFromID(wire->viewLayerID()), false, parentCommand); // disconnect everyone from the other end of the wire being deleted, and reconnect to the remaining wire foreach (ConnectorItem * otherToConnectorItem, otherConnector->connectedToItems()) { new ChangeConnectionCommand(this, crossView, otherToConnectorItem->attachedToID(), otherToConnectorItem->connectorSharedID(), toWire->id(), otherConnector->connectorSharedID(), ViewLayer::specFromID(toWire->viewLayerID()), false, parentCommand); new ChangeConnectionCommand(this, crossView, otherToConnectorItem->attachedToID(), otherToConnectorItem->connectorSharedID(), wire->id(), clickedConnectorItem->connectorSharedID(), ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand); } toWire->saveGeometry(); makeDeleteItemCommand(toWire, crossView, parentCommand); Bezier b0, b1; QLineF newLine; QPointF newPos; if (otherConnector == toWire->connector1()) { newPos = wire->pos(); newLine = QLineF(QPointF(0,0), toWire->pos() - wire->pos() + toWire->line().p2()); b0.copy(wire->curve()); b1.copy(toWire->curve()); b0.set_endpoints(wire->line().p1(), wire->line().p2()); b1.set_endpoints(toWire->line().p1(), toWire->line().p2()); b1.translate(toWire->pos() - wire->pos()); } else { newPos = toWire->pos(); newLine = QLineF(QPointF(0,0), wire->pos() - toWire->pos() + wire->line().p2()); b0.copy(toWire->curve()); b1.copy(wire->curve()); b0.set_endpoints(toWire->line().p1(), toWire->line().p2()); b1.set_endpoints(wire->line().p1(), wire->line().p2()); b1.translate(wire->pos() - toWire->pos()); } new ChangeWireCommand(this, wire->id(), wire->line(), newLine, wire->pos(), newPos, true, false, parentCommand); Bezier joinBezier = b0.join(&b1); if (!joinBezier.isEmpty()) { ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), wire->curve(), &joinBezier, wire->getAutoroutable(), parentCommand); cwcc->setRedoOnly(); } new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } void SketchWidget::hoverEnterItem(QGraphicsSceneHoverEvent * event, ItemBase * item) { if(m_infoViewOnHover || currentlyInfoviewed(item)) { InfoGraphicsView::hoverEnterItem(event, item); } Wire * wire = dynamic_cast(item); if (wire != NULL) { if (canChainWire(wire)) { bool segment = wire->connector0()->chained() && wire->connector1()->chained(); bool disconnected = wire->connector0()->connectionsCount() == 0 && wire->connector1()->connectionsCount() == 0; statusMessage(QString("%1 to add a bendpoint %2") .arg(disconnected ? tr("Double-click") : tr("Drag or double-click")) .arg(segment ? tr("or alt-drag to move the segment") : tr(""))); m_lastHoverEnterItem = item; } } } void SketchWidget::statusMessage(QString message, int timeout) { // TODO: eventually do the connecting from the window not from the sketch QMainWindow * mainWindow = qobject_cast(window()); if (mainWindow == NULL) return; if (m_statusConnectState == StatusConnectNotTried) { bool result = connect(this, SIGNAL(statusMessageSignal(QString, int)), mainWindow, SLOT(statusMessage(QString, int))); m_statusConnectState = (result) ? StatusConnectSucceeded : StatusConnectFailed; } if (m_statusConnectState == StatusConnectFailed) { QStatusBar * sb = mainWindow->statusBar(); if (sb != NULL) { sb->showMessage(message, timeout); } } else { emit statusMessageSignal(message, timeout); } } void SketchWidget::hoverLeaveItem(QGraphicsSceneHoverEvent * event, ItemBase * item){ m_lastHoverEnterItem = NULL; if(m_infoViewOnHover) { InfoGraphicsView::hoverLeaveItem(event, item); } if (canChainWire(qobject_cast(item))) { statusMessage(QString()); } } void SketchWidget::hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { if(m_infoViewOnHover || currentlyInfoviewed(item->attachedTo())) { InfoGraphicsView::hoverEnterConnectorItem(event, item); } if (item->attachedToItemType() == ModelPart::Wire) { if (!this->m_chainDrag) return; if (!item->chained()) return; m_lastHoverEnterConnectorItem = item; QString msg = hoverEnterWireConnectorMessage(event, item); statusMessage(msg); } else { QString msg = hoverEnterPartConnectorMessage(event, item); statusMessage(msg); } } const QString & SketchWidget::hoverEnterWireConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { Q_UNUSED(event); Q_UNUSED(item); static QString message = tr("Double-click to delete this bend point"); return message; } const QString & SketchWidget::hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { Q_UNUSED(event); Q_UNUSED(item); return ___emptyString___; } void SketchWidget::hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) { m_lastHoverEnterConnectorItem = NULL; ItemBase* attachedTo = item->attachedTo(); if(attachedTo) { if(m_infoViewOnHover || currentlyInfoviewed(attachedTo)) { InfoGraphicsView::hoverLeaveConnectorItem(event, item); if(attachedTo->collidesWithItem(item)) { hoverEnterItem(event,attachedTo); } } attachedTo->hoverLeaveConnectorItem(event, item); } if (attachedTo->itemType() == ModelPart::Wire) { if (!this->m_chainDrag) return; if (!item->chained()) return; statusMessage(QString()); } else { statusMessage(QString()); } } bool SketchWidget::currentlyInfoviewed(ItemBase *item) { if(m_infoView) { ItemBase * currInfoView = m_infoView->currentItem(); return !currInfoView || item == currInfoView;//->cu selItems.size()==1 && selItems[0] == item; } return false; } void SketchWidget::cleanUpWires(bool doEmit, CleanUpWiresCommand * command) { RoutingStatus routingStatus; updateRoutingStatus(command, routingStatus, false); if (doEmit) { emit cleanUpWiresSignal(command); } } void SketchWidget::cleanUpWiresSlot(CleanUpWiresCommand * command) { RoutingStatus routingStatus; updateRoutingStatus(command, routingStatus, false); } void SketchWidget::noteChanged(ItemBase * item, const QString &oldText, const QString & newText, QSizeF oldSize, QSizeF newSize) { ChangeNoteTextCommand * command = new ChangeNoteTextCommand(this, item->id(), oldText, newText, oldSize, newSize, NULL); command->setText(tr("Note text change")); command->setSkipFirstRedo(); m_undoStack->waitPush(command, PropChangeDelay); } void SketchWidget::partLabelChanged(ItemBase * pitem,const QString & oldText, const QString &newText) { // partLabelChanged triggered from inline editing the label if (!m_current) { // all three views get the partLabelChanged call, but only need to act on this once return; } //if (currentlyInfoviewed(pitem)) { // TODO: just change the affected item in the info view //viewItemInfo(pitem); //} partLabelChangedAux(pitem, oldText, newText); } void SketchWidget::partLabelChangedAux(ItemBase * pitem,const QString & oldText, const QString &newText) { if (pitem == NULL) return; ChangeLabelTextCommand * command = new ChangeLabelTextCommand(this, pitem->id(), oldText, newText, NULL); command->setText(tr("Change %1 label to '%2'").arg(pitem->title()).arg(newText)); m_undoStack->waitPush(command, PropChangeDelay); } void SketchWidget::setInfoViewOnHover(bool infoViewOnHover) { m_infoViewOnHover = infoViewOnHover; } void SketchWidget::updateInfoView() { if (m_blockUI) return; QTimer::singleShot(50, this, SLOT(updateInfoViewSlot())); } void SketchWidget::updateInfoViewSlot() { foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; ItemBase * chief = itemBase->layerKinChief(); viewItemInfo(chief); return; } viewItemInfo(m_lastPaletteItemSelected); } long SketchWidget::setUpSwap(SwapThing & swapThing, bool master) { if (swapThing.firstTime) { swapThing.firstTime = false; swapThing.newID = swapStart(swapThing, master); // need to ensure the parts are all created first thing emit swapStartSignal(swapThing, master); } ItemBase * itemBase = swapThing.itemBase; if (itemBase->viewID() != m_viewID) { itemBase = findItem(itemBase->id()); if (itemBase == NULL) return swapThing.newID; } setUpSwapReconnect(swapThing, itemBase, swapThing.newID, master); new CheckStickyCommand(this, BaseCommand::SingleView, swapThing.newID, false, CheckStickyCommand::RemoveOnly, swapThing.parentCommand); if (itemBase->isPartLabelVisible()) { ShowLabelCommand * slc = new ShowLabelCommand(this, swapThing.parentCommand); slc->add(swapThing.newID, true, true); } if (master) { SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, swapThing.parentCommand); selectItemCommand->addRedo(swapThing.newID); selectItemCommand->addUndo(itemBase->id()); new ChangeLabelTextCommand(this, itemBase->id(), itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand); new ChangeLabelTextCommand(this, swapThing.newID, itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand); /* foreach (Wire * wire, swapThing.wiresToDelete) { QList chained; QList ends; wire->collectChained(chained, ends); foreach (Wire * w, chained) { if (!swapThing.wiresToDelete.contains(w)) swapThing.wiresToDelete.append(w); } } makeWiresChangeConnectionCommands(swapThing.wiresToDelete, swapThing.parentCommand); foreach (Wire * wire, swapThing.wiresToDelete) { makeDeleteItemCommand(wire, BaseCommand::CrossView, swapThing.parentCommand); } */ makeDeleteItemCommand(itemBase, BaseCommand::CrossView, swapThing.parentCommand); selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, swapThing.parentCommand); selectItemCommand->addRedo(swapThing.newID); // to make sure new item is selected so it appears in the info view prepDeleteProps(itemBase, swapThing.newID, swapThing.newModuleID, swapThing.propsMap, swapThing.parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand); } return swapThing.newID; } void SketchWidget::setUpSwapReconnect(SwapThing & swapThing, ItemBase * itemBase, long newID, bool master) { ModelPart * newModelPart = m_referenceModel->retrieveModelPart(swapThing.newModuleID); if (newModelPart == NULL) return; QList fromConnectorItems(itemBase->cachedConnectorItems()); newModelPart->initConnectors(); // make sure the connectors are set up QList< QPointer > newConnectors = newModelPart->connectors().values(); bool checkReplacedby = (!itemBase->modelPart()->replacedby().isEmpty() && itemBase->modelPart()->replacedby() == swapThing.newModuleID); QList notFound; QList other; QHash found; QHash m2f; foreach (ConnectorItem * fromConnectorItem, fromConnectorItems) { QList candidates; Connector * newConnector = NULL; if (fromConnectorItem->connectorType() == Connector::Male) { foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { if (toConnectorItem->connectorType() == Connector::Female) { m2f.insert(fromConnectorItem, toConnectorItem); break; } } } // matching by connectorid can lead to weird results because these all just usually count up from zero // so only match by name and description (the latter is a bit of a hail mary) QString fromName = fromConnectorItem->connectorSharedName(); QString fromDescription = fromConnectorItem->connectorSharedDescription(); QString fromReplacedby = fromConnectorItem->connectorSharedReplacedby(); //itemBase->debugInfo(QString("%1 %2").arg(fromName).arg(fromReplacedby)); foreach (Connector * connector, newConnectors) { QString toName = connector->connectorSharedName(); QString toID = connector->connectorSharedID(); if (checkReplacedby) { if (fromReplacedby.compare(toName, Qt::CaseInsensitive) == 0 || fromReplacedby.compare(toID) == 0) { candidates.clear(); candidates.append(connector); //fromConnectorItem->debugInfo(QString("matched %1 %2").arg(toName).arg(toID)); break; } } bool gotOne = false; QString toDescription = connector->connectorSharedDescription(); if (fromName.compare(toName, Qt::CaseInsensitive) == 0) { gotOne = true; } else if (fromDescription.compare(toDescription, Qt::CaseInsensitive) == 0) { gotOne = true; } else if (fromDescription.compare(toName, Qt::CaseInsensitive) == 0) { gotOne = true; } else if (fromName.compare(toDescription, Qt::CaseInsensitive) == 0) { gotOne = true; } if (gotOne) { candidates.append(connector); } } if (candidates.count() > 0) { newConnector = candidates[0]; if (candidates.count() > 1) { foreach (Connector * connector, candidates) { // this gets an exact match, if there is one if (fromConnectorItem->connectorSharedID().compare(connector->connectorSharedID(), Qt::CaseInsensitive) == 0) { newConnector = connector; break; } } } newConnectors.removeOne(newConnector); found.insert(fromConnectorItem, newConnector); fromConnectorItem = fromConnectorItem->getCrossLayerConnectorItem(); if (fromConnectorItem) { other.append(fromConnectorItem); found.insert(fromConnectorItem, newConnector); } } else { notFound.append(fromConnectorItem); } } QHash legs; QHash formerLegs; if (m2f.count() > 0 && (m_viewID == ViewLayer::BreadboardView)) { checkFit(newModelPart, itemBase, newID, found, notFound, m2f, swapThing.byWire, legs, formerLegs, swapThing.parentCommand); } fromConnectorItems.append(other); foreach (ConnectorItem * fromConnectorItem, fromConnectorItems) { //fromConnectorItem->debugInfo("from"); Connector * newConnector = found.value(fromConnectorItem, NULL); foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { // delete connection to part being swapped out Wire * wire = qobject_cast(toConnectorItem->attachedTo()); if (wire != NULL) { if (wire->getRatsnest()) continue; //if (newConnector == NULL && wire->getTrace() && wire->isTraceType(getTraceFlag())) { // swapThing.wiresToDelete.append(wire); // continue; //} } // disconnect command created for each view individually extendChangeConnectionCommand(BaseCommand::SingleView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), false, swapThing.parentCommand); bool cleanup = false; if (newConnector) { bool byWireFlag = (swapThing.byWire.value(fromConnectorItem, NULL) == newConnector); bool swapped = false; if (byWireFlag) { swapThing.toConnectorItems.insert(fromConnectorItem, toConnectorItem); } else { swapped = swappedGender(fromConnectorItem, newConnector); if (swapped && m_viewID == ViewLayer::BreadboardView) { swapThing.swappedGender.insert(fromConnectorItem, newConnector); swapThing.toConnectorItems.insert(fromConnectorItem, toConnectorItem); } } cleanup = byWireFlag || swapped; } if ((newConnector == NULL) || cleanup) { // clean up after disconnect } else { // reconnect directly without cleaning up; command created for each view // if it's an smd swap, deal with reconnecting the wire at reconnect time ChangeConnectionCommand * ccc = new ChangeConnectionCommand(this, BaseCommand::SingleView, newID, newConnector->connectorSharedID(), toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), true, swapThing.parentCommand); swapThing.reconnections.insert(fromConnectorItem, ccc); } } } foreach (ConnectorItem * fromConnectorItem, swapThing.swappedGender.keys()) { makeSwapWire(swapThing.bbView, itemBase, newID, fromConnectorItem, swapThing.toConnectorItems.value(fromConnectorItem), swapThing.swappedGender.value(fromConnectorItem), swapThing.parentCommand); } if (master && swapThing.byWire.count() > 0) { foreach (ConnectorItem * fromConnectorItem, swapThing.byWire.keys()) { makeSwapWire(swapThing.bbView, itemBase, newID, fromConnectorItem, swapThing.toConnectorItems.value(fromConnectorItem), swapThing.byWire.value(fromConnectorItem), swapThing.parentCommand); } foreach (ConnectorItem * fromConnectorItem, swapThing.byWire.keys()) { // if a part, for example a generic DIP, is reconnected by wires after swapping, // then remove any pcb and schematic direct reconnections QList toRemove; foreach (ConnectorItem * foreign, swapThing.reconnections.keys()) { ConnectorItem * candidate = swapThing.bbView->findConnectorItem(foreign); if (candidate == fromConnectorItem) { toRemove << foreign; swapThing.reconnections.value(foreign)->disable(); } } foreach (ConnectorItem * foreign, toRemove) { swapThing.reconnections.remove(foreign); } } } // changeConnection calls PaletteItemBase::connectedMoved which repositions the new part // so slam in the desired position QPointF p = itemBase->getViewGeometry().loc(); new SimpleMoveItemCommand(this, newID, p, p, swapThing.parentCommand); foreach (QString connectorID, legs.keys()) { // must be invoked after all the connections have been dealt with QPolygonF poly = legs.value(connectorID); ConnectorItem * connectorItem = formerLegs.value(connectorID, NULL); if (connectorItem && connectorItem->hasRubberBandLeg()) { poly = connectorItem->leg(); } ChangeLegCommand * clc = new ChangeLegCommand(this, newID, connectorID, poly, poly, true, true, "swap", swapThing.parentCommand); clc->setRedoOnly(); if (connectorItem && connectorItem->hasRubberBandLeg()) { QVector beziers = connectorItem->beziers(); for (int i = 0; i < beziers.count() - 1; i++) { Bezier * bezier = beziers.at(i); if (bezier == NULL) continue; if (bezier->isEmpty()) continue; ChangeLegCurveCommand * clcc = new ChangeLegCurveCommand(this, newID, connectorID, i, bezier, bezier, swapThing.parentCommand); clcc->setRedoOnly(); } } } } void SketchWidget::makeSwapWire(SketchWidget * bbView, ItemBase * itemBase, long newID, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Connector * newConnector, QUndoCommand * parentCommand) { Q_UNUSED(fromConnectorItem); long wireID = ItemBase::getNextID(); ViewGeometry vg; new AddItemCommand(bbView, BaseCommand::CrossView, ModuleIDNames::WireModuleIDName, itemBase->viewLayerPlacement(), vg, wireID, false, -1, parentCommand); new CheckStickyCommand(bbView, BaseCommand::CrossView, wireID, false, CheckStickyCommand::RemoveOnly, parentCommand); new ChangeConnectionCommand(bbView, BaseCommand::CrossView, newID, newConnector->connectorSharedID(), wireID, "connector0", ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), true, parentCommand); new ChangeConnectionCommand(bbView, BaseCommand::CrossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), wireID, "connector1", ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), bbView, parentCommand); } void SketchWidget::checkFit(ModelPart * newModelPart, ItemBase * itemBase, long newID, QHash & found, QList & notFound, QHash & m2f, QHash & byWire, QHash & legs, QHash & formerLegs, QUndoCommand * parentCommand) { if (found.count() == 0) return; ItemBase * tempItemBase = addItemAuxTemp(newModelPart, itemBase->viewLayerPlacement(), itemBase->getViewGeometry(), newID, true, m_viewID, true); if (tempItemBase == NULL) return; // we're really screwed checkFitAux(tempItemBase, itemBase, newID, found, notFound, m2f, byWire, legs, formerLegs, parentCommand); delete tempItemBase; } void SketchWidget::checkFitAux(ItemBase * tempItemBase, ItemBase * itemBase, long newID, QHash & found, QList & notFound, QHash & m2f, QHash & byWire, QHash & legs, QHash & formerLegs, QUndoCommand * parentCommand) { QPointF foundAnchor(std::numeric_limits::max(), std::numeric_limits::max()); QPointF newAnchor(std::numeric_limits::max(), std::numeric_limits::max()); QHash foundPoints; QHash newPoints; QHash foundNews; QHash newFounds; QList removeFromFound; foreach (ConnectorItem * foundConnectorItem, found.keys()) { if (m2f.value(foundConnectorItem, NULL) == NULL) { // we only care about replacing the female connectors here continue; } Connector * connector = found.value(foundConnectorItem); ConnectorItem * newConnectorItem = NULL; foreach (ConnectorItem * nci, tempItemBase->cachedConnectorItems()) { if (nci->connector()->connectorShared() == connector->connectorShared()) { newConnectorItem = nci; break; } } if (newConnectorItem == NULL) { removeFromFound.append(foundConnectorItem); } else { foundNews.insert(foundConnectorItem, newConnectorItem); newFounds.insert(newConnectorItem, foundConnectorItem); QPointF lastNew = newConnectorItem->sceneAdjustedTerminalPoint(NULL); if (lastNew.x() < newAnchor.x()) newAnchor.setX(lastNew.x()); if (lastNew.y() < newAnchor.y()) newAnchor.setY(lastNew.y()); newPoints.insert(newConnectorItem, lastNew); QPointF lastFound = foundConnectorItem->sceneAdjustedTerminalPoint(NULL); if (lastFound.x() < foundAnchor.x()) foundAnchor.setX(lastFound.x()); if (lastFound.y() < foundAnchor.y()) foundAnchor.setY(lastFound.y()); foundPoints.insert(foundConnectorItem, lastFound); } } foreach (ConnectorItem * connectorItem, removeFromFound) { found.remove(connectorItem); notFound.append(connectorItem); } if (found.count() == 0) { return; } bool allCorrespond = true; foreach (ConnectorItem * foundConnectorItem, foundNews.keys()) { QPointF fp = foundPoints.value(foundConnectorItem) - foundAnchor; ConnectorItem * newConnectorItem = foundNews.value(foundConnectorItem); QPointF np = newPoints.value(newConnectorItem) - newAnchor; if (!newConnectorItem->hasRubberBandLeg() && (qAbs(fp.x() - np.x()) >= CloseEnough || qAbs(fp.y() - np.y()) >= CloseEnough)) { // pins can be off by a little // but if even one connector is out of place, hook everything up by wires allCorrespond = false; break; } } if (allCorrespond) { if (tempItemBase->cachedConnectorItems().count() == found.count()) { if (tempItemBase->hasRubberBandLeg()) { foreach (ConnectorItem * connectorItem, tempItemBase->cachedConnectorItems()) { legs.insert(connectorItem->connectorSharedID(), connectorItem->leg()); formerLegs.insert(connectorItem->connectorSharedID(), newFounds.value(connectorItem, NULL)); } } // it's a clean swap: all connectors line up return; } } // there's a mismatch in terms of connector count between the swaps, // so make sure all target connections are open or to be swapped out // establish the location of the new item's connectors QHash newConnections; if (allCorrespond) { QList alreadyFits = foundNews.values(); foreach (ConnectorItem * nci, tempItemBase->cachedConnectorItems()) { if (alreadyFits.contains(nci)) continue; if (nci->connectorType() != Connector::Male) continue; // TODO: this doesn't handle all scenarios. For example, if another part with a female connector is // on top of the breadboard, and would be in the way QPointF p = nci->sceneAdjustedTerminalPoint(NULL) - newAnchor + foundAnchor; // eventual position of this new connector ConnectorItem * connectorUnder = NULL; foreach (QGraphicsItem * item, scene()->items(p)) { ConnectorItem * cu = dynamic_cast(item); if (cu == NULL || cu == nci || cu->attachedTo() == itemBase || cu->connectorType() != Connector::Female) { continue; } connectorUnder = cu; break; } if (connectorUnder == NULL) { // safe? continue; } foreach (ConnectorItem * uct, connectorUnder->connectedToItems()) { if (uct->attachedTo() == itemBase) { // we're safe, itemBase is swapping out continue; } // some other part is in the way allCorrespond = false; break; } if (!allCorrespond) break; newConnections.insert(nci, connectorUnder); // add a new direct connection } } if (allCorrespond) { // the extra new connectors will also fit foreach (ConnectorItem * nci, newConnections.keys()) { ConnectorItem * toConnectorItem = newConnections.value(nci); new ChangeConnectionCommand(this, BaseCommand::CrossView, newID, nci->connectorSharedID(), toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(), ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), true, parentCommand); } if (tempItemBase->hasRubberBandLeg()) { foreach (ConnectorItem * connectorItem, newConnections) { legs.insert(connectorItem->connectorSharedID(), connectorItem->leg()); } foreach (ConnectorItem * newConnectorItem, newFounds.keys()) { legs.insert(newConnectorItem->connectorSharedID(), newConnectorItem->leg()); formerLegs.insert(newConnectorItem->connectorSharedID(), newFounds.value(newConnectorItem, NULL)); } } return; } // have to replace each found value with a wire foreach (ConnectorItem * fci, found.keys()) { byWire.insert(fci, found.value(fci)); } } void SketchWidget::changeWireColor(const QString newColor) { m_lastColorSelected = newColor; QList wires; foreach (QGraphicsItem * item, scene()->selectedItems()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; wires.append(wire); } if (wires.count() <= 0) return; QString commandString; if (wires.count() == 1) { commandString = tr("Change %1 color from %2 to %3") .arg(wires[0]->instanceTitle()) .arg(wires[0]->colorString()) .arg(newColor); } else { commandString = tr("Change color of %1 wires to %2") .arg(wires.count()) .arg(newColor); } QUndoCommand* parentCommand = new QUndoCommand(commandString); foreach (Wire * wire, wires) { QList subWires; wire->collectWires(subWires); foreach (Wire * subWire, subWires) { new WireColorChangeCommand( this, subWire->id(), subWire->colorString(), newColor, subWire->opacity(), subWire->opacity(), parentCommand); } } m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::changeWireWidthMils(const QString newWidthStr) { bool ok = false; double newWidth = newWidthStr.toDouble(&ok); if (!ok) return; double trueWidth = GraphicsUtils::SVGDPI * newWidth / 1000.0; QList wires; foreach (QGraphicsItem * item, scene()->selectedItems()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (!wire->getTrace()) continue; wires.append(wire); } if (wires.count() <= 0) return; QString commandString; if (wires.count() == 1) { commandString = tr("Change %1 width from %2 to %3") .arg(wires[0]->instanceTitle()) .arg((int) wires[0]->mils()) .arg(newWidth); } else { commandString = tr("Change width of %1 wires to %2") .arg(wires.count()) .arg(newWidth); } QUndoCommand* parentCommand = new QUndoCommand(commandString); foreach (Wire * wire, wires) { QList subWires; wire->collectWires(subWires); foreach (Wire * subWire, subWires) { new WireWidthChangeCommand( this, subWire->id(), subWire->width(), trueWidth, parentCommand); } } m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::changeWireColor(long wireId, const QString& color, double opacity) { ItemBase *item = findItem(wireId); Wire* wire = qobject_cast(item); if (wire) { wire->setColorString(color, opacity, true); updateInfoView(); } } void SketchWidget::changeWireWidth(long wireId, double width) { ItemBase *item = findItem(wireId); Wire* wire = qobject_cast(item); if (wire) { wire->setWireWidth(width, this, getWireStrokeWidth(wire, width)); updateInfoView(); } } bool SketchWidget::swappingEnabled(ItemBase * itemBase) { if (itemBase == NULL) { return m_referenceModel->swapEnabled(); } return (m_referenceModel->swapEnabled() && itemBase->isSwappable()); } void SketchWidget::resizeEvent(QResizeEvent * event) { InfoGraphicsView::resizeEvent(event); QPoint s(event->size().width(), event->size().height()); QPointF p = this->mapToScene(s); QPointF z = this->mapToScene(QPoint(0,0)); // DebugDialog::debug(QString("resize event %1 %2, %3 %4, %5 %6") /* , %3 %4 %5 %6, %7 %8 %9 %10 */ // .arg(event->size().width()).arg(event->size().height()) // .arg(p.x()).arg(p.y()) // .arg(z.x()).arg(z.y()) // .arg(sr.left()).arg(sr.top()).arg(sr.width()).arg(sr.height()) // .arg(sr.left()).arg(sr.top()).arg(ir.width()).arg(ir.height()) // // ); if (m_sizeItem != NULL) { m_sizeItem->setLine(z.x(), z.y(), p.x(), p.y()); } emit resizeSignal(); } void SketchWidget::addViewLayers() { } void SketchWidget::addViewLayersAux(const LayerList & layers, const LayerList & layersFromBelow, float startZ) { m_z = startZ; foreach(ViewLayer::ViewLayerID vlId, layers) { addViewLayer(new ViewLayer(vlId, true, m_z)); m_z += 1; } double z = startZ; foreach(ViewLayer::ViewLayerID vlId, layersFromBelow) { ViewLayer * viewLayer = m_viewLayers.value(vlId, NULL); if (viewLayer) viewLayer->setInitialZFromBelow(z); z += 1; } } void SketchWidget::setIgnoreSelectionChangeEvents(bool ignore) { if (ignore) { m_ignoreSelectionChangeEvents++; } else { m_ignoreSelectionChangeEvents--; } } void SketchWidget::hideConnectors(bool hide) { foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isVisible()) continue; foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { connectorItem->setVisible(!hide); } } } void SketchWidget::saveLayerVisibility() { m_viewLayerVisibility.clear(); foreach (ViewLayer::ViewLayerID viewLayerID, m_viewLayers.keys()) { ViewLayer * viewLayer = m_viewLayers.value(viewLayerID); if (viewLayer == NULL) continue; m_viewLayerVisibility.insert(viewLayerID, viewLayer->visible()); } } void SketchWidget::restoreLayerVisibility() { foreach (ViewLayer::ViewLayerID viewLayerID, m_viewLayerVisibility.keys()) { setLayerVisible(m_viewLayers.value(viewLayerID), m_viewLayerVisibility.value(viewLayerID), false); } } void SketchWidget::changeWireFlags(long wireId, ViewGeometry::WireFlags wireFlags) { ItemBase *item = findItem(wireId); if(Wire* wire = qobject_cast(item)) { wire->setWireFlags(wireFlags); } } bool SketchWidget::disconnectFromFemale(ItemBase * item, QHash & savedItems, ConnectorPairHash & connectorHash, bool doCommand, bool rubberBandLegEnabled, QUndoCommand * parentCommand) { // schematic and pcb view connections are always via wires so this is a no-op. breadboard view has its own version. Q_UNUSED(item); Q_UNUSED(savedItems); Q_UNUSED(parentCommand); Q_UNUSED(connectorHash); Q_UNUSED(doCommand); Q_UNUSED(rubberBandLegEnabled); return false; } void SketchWidget::spaceBarIsPressedSlot(bool isPressed) { if (m_middleMouseIsPressed) return; m_spaceBarIsPressed = isPressed; if (isPressed) { setDragMode(QGraphicsView::ScrollHandDrag); //setInteractive(false); //CursorMaster::instance()->addCursor(this, Qt::OpenHandCursor); //setCursor(Qt::OpenHandCursor); //DebugDialog::debug("setting open hand cursor"); } else { //CursorMaster::instance()->removeCursor(this); setDragMode(QGraphicsView::RubberBandDrag); //setInteractive(true); //setCursor(Qt::ArrowCursor); } } void SketchWidget::updateRoutingStatus(CleanUpWiresCommand* command, RoutingStatus & routingStatus, bool manual) { //DebugDialog::debug("update ratsnest status"); routingStatus.zero(); updateRoutingStatus(routingStatus, manual); if (routingStatus != m_routingStatus) { if (command) { // changing state after the command has already been executed command->addRoutingStatus(this, m_routingStatus, routingStatus); } emit routingStatusSignal(this, routingStatus); m_routingStatus = routingStatus; } } void SketchWidget::updateRoutingStatus(RoutingStatus & routingStatus, bool manual) { //DebugDialog::debug(QString("update routing status %1 %2 %3") // .arg(m_viewID) // .arg(m_ratsnestUpdateConnect.count()) // .arg(m_ratsnestUpdateDisconnect.count()) // ); // TODO: think about ways to optimize this... QList< QPointer > ratsToDelete; QList< QList > ratnestsToUpdate; QList visited; foreach (QGraphicsItem * item, scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (visited.contains(connectorItem)) continue; //if (this->viewID() == ViewLayer::SchematicView) { // connectorItem->debugInfo("testing urs"); //} VirtualWire * vw = qobject_cast(connectorItem->attachedTo()); if (vw != NULL) { if (vw->connector0()->connectionsCount() == 0 || vw->connector1()->connectionsCount() == 0) { ratsToDelete.append(vw); } visited << vw->connector0() << vw->connector1(); continue; } QList connectorItems; connectorItems.append(connectorItem); ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag); visited.append(connectorItems); //if (this->viewID() == ViewLayer::SchematicView) { // DebugDialog::debug("________________________"); // foreach (ConnectorItem * ci, connectorItems) ci->debugInfo("cep"); //} bool doRatsnest = manual || checkUpdateRatsnest(connectorItems); if (!doRatsnest && connectorItems.count() <= 1) continue; QList partConnectorItems; ConnectorItem::collectParts(connectorItems, partConnectorItems, includeSymbols(), ViewLayer::NewTopAndBottom); if (partConnectorItems.count() < 1) continue; if (!doRatsnest && partConnectorItems.count() <= 1) continue; //if (this->viewID() == ViewLayer::SchematicView) { // DebugDialog::debug("________________________"); // foreach (ConnectorItem * pci, partConnectorItems) { // pci->debugInfo("pc 1"); // } //} for (int i = partConnectorItems.count() - 1; i >= 0; i--) { ConnectorItem * ci = partConnectorItems[i]; if (!ci->attachedTo()->isEverVisible()) { partConnectorItems.removeAt(i); } } if (partConnectorItems.count() < 1) continue; if (doRatsnest) { ratnestsToUpdate.append(partConnectorItems); } if (partConnectorItems.count() <= 1) continue; //if (this->viewID() == ViewLayer::SchematicView) { // DebugDialog::debug("________________________"); // foreach (ConnectorItem * pci, partConnectorItems) { // pci->debugInfo("pc 2"); // } //} GraphUtils::scoreOneNet(partConnectorItems, this->getTraceFlag(), routingStatus); } routingStatus.m_jumperItemCount /= 4; // since we counted each connector twice on two layers (4 connectors per jumper item) // can't do this in the above loop since VirtualWires and ConnectorItems are added and deleted foreach (QList partConnectorItems, ratnestsToUpdate) { //partConnectorItems.at(0)->debugInfo("display ratsnest"); //if (this->viewID() == ViewLayer::SchematicView) { // DebugDialog::debug("________________________"); // foreach (ConnectorItem * pci, partConnectorItems) { // pci->debugInfo("pc 3"); // } //} partConnectorItems.at(0)->displayRatsnest(partConnectorItems, this->getTraceFlag()); } foreach(QPointer vw, ratsToDelete) { if (vw != NULL) { //vw->debugInfo("removing rat 2"); vw->scene()->removeItem(vw); delete vw; } } m_ratsnestUpdateConnect.clear(); m_ratsnestUpdateDisconnect.clear(); /* // uncomment for live drc CMRouter cmRouter(this); QString message; bool result = cmRouter.drc(message); */ } void SketchWidget::ensureLayerVisible(ViewLayer::ViewLayerID viewLayerID) { ViewLayer * viewLayer = m_viewLayers.value(viewLayerID, NULL); if (viewLayer == NULL) return; if (!viewLayer->visible()) { setLayerVisible(viewLayer, true, true); } } void SketchWidget::clearDragWireTempCommand() { if (m_tempDragWireCommand) { delete m_tempDragWireCommand; m_tempDragWireCommand = NULL; } } void SketchWidget::autoScrollTimeout() { //DebugDialog::debug(QString("scrolling dx:%1 dy:%2").arg(m_autoScrollX).arg(m_autoScrollY) ); if (m_autoScrollX == 0 && m_autoScrollY == 0 ) return; if (m_autoScrollX != 0) { QScrollBar * h = horizontalScrollBar(); h->setValue(m_autoScrollX + h->value()); } if (m_autoScrollY != 0) { QScrollBar * v = verticalScrollBar(); v->setValue(m_autoScrollY + v->value()); } //DebugDialog::debug(QString("autoscrolling %1 %2").arg(m_autoScrollX).arg(m_autoScrollX)); } void SketchWidget::dragAutoScrollTimeout() { autoScrollTimeout(); dragMoveHighlightConnector(mapFromGlobal(m_globalPos)); } void SketchWidget::moveAutoScrollTimeout() { autoScrollTimeout(); moveItems(m_globalPos, true, m_rubberBandLegWasEnabled); } const QString &SketchWidget::selectedModuleID() { if(m_lastPaletteItemSelected) { return m_lastPaletteItemSelected->moduleID(); } foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase) return itemBase->moduleID(); } return ___emptyString___; } BaseCommand::CrossViewType SketchWidget::wireSplitCrossView() { return BaseCommand::CrossView; } bool SketchWidget::canDeleteItem(QGraphicsItem * item, int count) { Q_UNUSED(count); ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) return false; if (itemBase->moveLock()) return false; ItemBase * chief = itemBase->layerKinChief(); if (chief == NULL) return false; if (chief->moveLock()) return false; return true; } bool SketchWidget::canCopyItem(QGraphicsItem * item, int count) { Q_UNUSED(count); ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) return false; ItemBase * chief = itemBase->layerKinChief(); if (chief == NULL) return false; return true; } bool SketchWidget::canChainMultiple() { return false; } bool SketchWidget::canChainWire(Wire * wire) { if (!this->m_chainDrag) return false; if (wire == NULL) return false; return true; } bool SketchWidget::canDragWire(Wire * wire) { if (wire == NULL) return false; return true; } bool SketchWidget::canCreateWire(Wire * dragWire, ConnectorItem * from, ConnectorItem * to) { Q_UNUSED(dragWire); Q_UNUSED(from); Q_UNUSED(to); return true; } /* bool SketchWidget::modifyNewWireConnections(Wire * dragWire, ConnectorItem * fromOnWire, ConnectorItem * from, ConnectorItem * to, QUndoCommand * parentCommand) { Q_UNUSED(dragWire); Q_UNUSED(fromOnWire); Q_UNUSED(from); Q_UNUSED(to); Q_UNUSED(parentCommand); return false; } */ void SketchWidget::setupAutoscroll(bool moving) { m_autoScrollX = m_autoScrollY = 0; m_autoScrollThreshold = (moving) ? MoveAutoScrollThreshold : DragAutoScrollThreshold; m_autoScrollCount = 0; connect(&m_autoScrollTimer, SIGNAL(timeout()), this, moving ? SLOT(moveAutoScrollTimeout()) : SLOT(dragAutoScrollTimeout())); //DebugDialog::debug("set up autoscroll"); } void SketchWidget::turnOffAutoscroll() { m_autoScrollTimer.stop(); disconnect(&m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(moveAutoScrollTimeout())); disconnect(&m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(dragAutoScrollTimeout())); //DebugDialog::debug("turn off autoscroll"); } bool SketchWidget::checkAutoscroll(QPoint globalPos) { QRect r = rect(); QPoint q = mapFromGlobal(globalPos); if (verticalScrollBar()->isVisible()) { r.setWidth(width() - verticalScrollBar()->width()); } if (horizontalScrollBar()->isVisible()) { r.setHeight(height() - horizontalScrollBar()->height()); } if (!r.contains(q)) { m_autoScrollX = m_autoScrollY = 0; if (m_autoScrollCount < m_autoScrollThreshold) { m_autoScrollCount = 0; } return false; } //DebugDialog::debug(QString("check autoscroll %1, %2 %3").arg(QTime::currentTime().msec()).arg(q.x()).arg(q.y()) ); r.adjust(16,16,-16,-16); // these should be set someplace bool autoScroll = !r.contains(q); if (autoScroll) { if (++m_autoScrollCount < m_autoScrollThreshold) { m_autoScrollX = m_autoScrollY = 0; //DebugDialog::debug("in autoscrollThreshold"); return true; } if (m_clearSceneRect) { scene()->setSceneRect(QRectF()); m_clearSceneRect = true; } int dx = 0, dy = 0; if (q.x() > r.right()) { dx = q.x() - r.right(); } else if (q.x() < r.left()) { dx = q.x() - r.left(); } if (q.y() > r.bottom()) { dy = q.y() - r.bottom(); } else if (q.y() < r.top()) { dy = q.y() - r.top(); } int div = 3; if (dx != 0) { m_autoScrollX = (dx + ((dx > 0) ? div : -div)) / (div + 1); // ((m_autoScrollX > 0) ? 1 : -1) } if (dy != 0) { m_autoScrollY = (dy + ((dy > 0) ? div : -div)) / (div + 1); // ((m_autoScrollY > 0) ? 1 : -1) } if (!m_autoScrollTimer.isActive()) { //DebugDialog::debug("starting autoscroll timer"); m_autoScrollTimer.start(10); } } else { m_autoScrollX = m_autoScrollY = 0; if (m_autoScrollCount < m_autoScrollThreshold) { m_autoScrollCount = 0; } } //DebugDialog::debug(QString("autoscroll %1 %2").arg(m_autoScrollX).arg(m_autoScrollY) ); return true; } void SketchWidget::setWireVisible(Wire * wire) { Q_UNUSED(wire); } void SketchWidget::forwardRoutingStatus(const RoutingStatus & routingStatus) { emit routingStatusSignal(this, routingStatus); } bool SketchWidget::matchesLayer(ModelPart * modelPart) { LayerList viewLayers = modelPart->viewLayers(m_viewID); foreach (ViewLayer* viewLayer, m_viewLayers) { if (viewLayers.contains(viewLayer->viewLayerID())) return true; } return false; } const QString & SketchWidget::viewName() { return m_viewName; } void SketchWidget::setNoteText(long itemID, const QString & newText) { ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; Note * note = qobject_cast(itemBase); if (note == NULL) return; note->setText(newText, false); } void SketchWidget::incInstanceTitle(long itemID) { ItemBase * itemBase = findItem(itemID); if (itemBase) { itemBase->ensureUniqueTitle(itemBase->instanceTitle(), true); } emit updatePartLabelInstanceTitleSignal(itemID); } void SketchWidget::updatePartLabelInstanceTitleSlot(long itemID) { ItemBase * itemBase = findItem(itemID); if (itemBase) { itemBase->updatePartLabelInstanceTitle(); } } void SketchWidget::setInstanceTitle(long itemID, const QString & oldText, const QString & newText, bool isUndoable, bool doEmit) { // isUndoable is true when setInstanceTitle is called from the infoview ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; if (!isUndoable) { itemBase->setInstanceTitle(newText, false); if (doEmit && currentlyInfoviewed(itemBase)) { // TODO: just change the affected item in the info view viewItemInfo(itemBase); } if (doEmit) { emit setInstanceTitleSignal(itemID, oldText, newText, isUndoable, false); } } else { if (oldText.compare(newText) == 0) return; /* // this doesn't work correctly--undo somehow gets messed up LogoItem * logoItem = qobject_cast(itemBase); if (logoItem && logoItem->hasLogo()) { // instead of changing part label, change logo if (logoItem->logo().compare(newText) == 0) return; setProp(logoItem, "logo", tr("logo"), logoItem->logo(), newText, true); return; } */ partLabelChangedAux(itemBase, oldText, newText); } } void SketchWidget::showPartLabel(long itemID, bool showIt) { ItemBase * itemBase = findItem(itemID); if (itemBase != NULL) { itemBase->showPartLabel(showIt, m_viewLayers.value(getLabelViewLayerID(itemBase))); } } void SketchWidget::hidePartLabel(ItemBase * item) { QList itemBases; itemBases.append(item); showPartLabelsAux(false, itemBases); } void SketchWidget::collectParts(QList & partList) { // using PaletteItem instead of ItemBase ensures layerKinChiefs only foreach (QGraphicsItem * item, scene()->items()) { PaletteItem * pitem = dynamic_cast(item); if (pitem == NULL) continue; if (pitem->itemType() == ModelPart::Symbol) continue; partList.append(pitem); } } void SketchWidget::movePartLabel(long itemID, QPointF newPos, QPointF newOffset) { ItemBase * item = findItem(itemID); if (item == NULL) return; item->movePartLabel(newPos, newOffset); } void SketchWidget::setCurrent(bool current) { m_current = current; } void SketchWidget::partLabelMoved(ItemBase * itemBase, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset) { MoveLabelCommand * command = new MoveLabelCommand(this, itemBase->id(), oldPos, oldOffset, newPos, newOffset, NULL); command->setText(tr("Move label '%1'").arg(itemBase->title())); m_undoStack->push(command); } void SketchWidget::rotateFlipPartLabel(ItemBase * itemBase, double degrees, Qt::Orientations flipDirection) { RotateFlipLabelCommand * command = new RotateFlipLabelCommand(this, itemBase->id(), degrees, flipDirection, NULL); command->setText(tr("%1 label '%2'").arg((degrees != 0) ? tr("Rotate") : tr("Flip")).arg(itemBase->title())); m_undoStack->push(command); } void SketchWidget::rotateFlipPartLabel(long itemID, double degrees, Qt::Orientations flipDirection) { ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; itemBase->doRotateFlipPartLabel(degrees, flipDirection); } void SketchWidget::showPartLabels(bool show) { QList itemBases; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item); if (itemBase == NULL) continue; if (itemBase->hasPartLabel()) { itemBases.append(itemBase); } } if (itemBases.count() <= 0) return; showPartLabelsAux(show, itemBases); } void SketchWidget::showPartLabelsAux(bool show, QList & itemBases) { ShowLabelCommand * showLabelCommand = new ShowLabelCommand(this, NULL); QString text; if (show) { text = tr("show %n part label(s)", "", itemBases.count()); } else { text = tr("hide %n part label(s)", "", itemBases.count()); } showLabelCommand->setText(text); foreach (ItemBase * itemBase, itemBases) { showLabelCommand->add(itemBase->id(), itemBase->isPartLabelVisible(), show); } m_undoStack->push(showLabelCommand); } void SketchWidget::noteSizeChanged(ItemBase * itemBase, const QSizeF & oldSize, const QSizeF & newSize) { ResizeNoteCommand * command = new ResizeNoteCommand(this, itemBase->id(), oldSize, newSize, NULL); command->setText(tr("Resize Note")); m_undoStack->push(command); clearHoldingSelectItem(); } void SketchWidget::resizeNote(long itemID, const QSizeF & size) { Note * note = qobject_cast(findItem(itemID)); if (note == NULL) return; note->setSize(size); } QString SketchWidget::renderToSVG(RenderThing & renderThing, QGraphicsItem * board, const LayerList & layers) { renderThing.board = board; if (board) { renderThing.offsetRect = board->sceneBoundingRect(); } return renderToSVG(renderThing, layers); } QString SketchWidget::renderToSVG(RenderThing & renderThing, const LayerList & layers) { QList itemsAndLabels; QRectF itemsBoundingRect; QList items; if (renderThing.selectedItems) { items = scene()->selectedItems(); } else if (renderThing.board == NULL) { items = scene()->items(); } else { items = scene()->collidingItems(renderThing.board); items << renderThing.board; } foreach (QGraphicsItem * item, items) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->hidden() || itemBase->layerHidden()) continue; if (!renderThing.renderBlocker) { Pad * pad = qobject_cast(itemBase); if (pad != NULL && pad->copperBlocker()) { continue; } } if (itemBase == itemBase->layerKinChief() && itemBase->isPartLabelVisible()) { if (layers.contains(itemBase->partLabelViewLayerID())) { itemsAndLabels.append(itemBase->partLabel()); itemsBoundingRect |= itemBase->partLabelSceneBoundingRect(); } } if (!itemBase->isVisible()) continue; if (!layers.contains(itemBase->viewLayerID())) continue; itemsAndLabels.append(itemBase); itemsBoundingRect |= item->sceneBoundingRect(); } renderThing.itemsBoundingRect = itemsBoundingRect; return renderToSVG(renderThing, itemsAndLabels); } QString translateSVG(QString & svg, QPointF loc, double dpi, double printerScale) { loc.setX(loc.x() * dpi / printerScale); loc.setY(loc.y() * dpi / printerScale); if (loc.x() != 0 || loc.y() != 0) { svg = QString("%3") .arg(loc.x()) .arg(loc.y()) .arg(svg); } return svg; } QString SketchWidget::renderToSVG(RenderThing & renderThing, QList & itemsAndLabels) { renderThing.empty = true; double width = renderThing.itemsBoundingRect.width(); double height = renderThing.itemsBoundingRect.height(); QPointF offset = renderThing.itemsBoundingRect.topLeft(); if (!renderThing.offsetRect.isEmpty()) { offset = renderThing.offsetRect.topLeft(); width = renderThing.offsetRect.width(); height = renderThing.offsetRect.height(); } renderThing.imageRect.setRect(offset.x(), offset.y(), width, height); QString outputSVG = TextUtils::makeSVGHeader(renderThing.printerScale, renderThing.dpi, width, height); QHash svgHash; // put them in z order qSort(itemsAndLabels.begin(), itemsAndLabels.end(), zLessThan); QList gotLabel; foreach (QGraphicsItem * item, itemsAndLabels) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) { PartLabel * partLabel = dynamic_cast(item); if (partLabel == NULL) continue; QString labelSvg = partLabel->owner()->makePartLabelSvg(renderThing.blackOnly, renderThing.dpi, renderThing.printerScale); if (labelSvg.isEmpty()) continue; labelSvg = translateSVG(labelSvg, partLabel->owner()->partLabelScenePos() - offset, renderThing.dpi, renderThing.printerScale); labelSvg = QString("%2").arg(partLabel->owner()->id()).arg(labelSvg); renderThing.empty = false; outputSVG.append(labelSvg); continue; } if (itemBase->itemType() != ModelPart::Wire) { double factor; QString itemSvg = itemBase->retrieveSvg(itemBase->viewLayerID(), svgHash, renderThing.blackOnly, renderThing.dpi, factor); if (itemSvg.isEmpty()) continue; TextUtils::fixMuch(itemSvg, false); QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (doc.setContent(itemSvg, &errorStr, &errorLine, &errorColumn)) { bool changed = false; if (renderThing.renderBlocker) { Pad * pad = qobject_cast(itemBase); if (pad && pad->copperBlocker()) { QDomNodeList nodeList = doc.documentElement().elementsByTagName("rect"); for (int n = 0; n < nodeList.count(); n++) { QDomElement element = nodeList.at(n).toElement(); element.setAttribute("fill-opacity", 1); changed = true; } } } foreach (ConnectorItem * ci, itemBase->cachedConnectorItems()) { SvgIdLayer * svgIdLayer = ci->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID()); if (renderThing.hideTerminalPoints && !svgIdLayer->m_terminalId.isEmpty()) { // these tend to be degenerate shapes and can cause trouble at gerber export time if (hideTerminalID(doc, svgIdLayer->m_terminalId)) changed = true; } if (ensureStrokeWidth(doc, svgIdLayer->m_svgId, factor)) changed = true; if (!ci->hasRubberBandLeg()) continue; // at the moment, the legs don't get a partID, but since there are no legs in PCB view, we don't care outputSVG.append(ci->makeLegSvg(offset, renderThing.dpi, renderThing.printerScale, renderThing.blackOnly)); } if (changed) { itemSvg = doc.toString(0); } } QTransform t = itemBase->transform(); itemSvg = TextUtils::svgTransform(itemSvg, t, false, QString()); itemSvg = translateSVG(itemSvg, itemBase->scenePos() - offset, renderThing.dpi, renderThing.printerScale); itemSvg = QString("%2").arg(itemBase->id()).arg(itemSvg); outputSVG.append(itemSvg); renderThing.empty = false; /* // TODO: deal with rotations and flips QString shifted = splitter->shift(loc.x(), loc.y(), xmlName); outputSVG.append(shifted); empty = false; splitter->shift(-loc.x(), -loc.y(), xmlName); */ } else { Wire * wire = qobject_cast(itemBase); if (wire == NULL) continue; //if (wire->getTrace()) { // DebugDialog::debug(QString("trace %1 %2,%3 %4,%5") // .arg(wire->id()) // .arg(wire->line().p1().x()) // .arg(wire->line().p1().y()) // .arg(wire->line().p2().x()) // .arg(wire->line().p2().y()) // ); //} QString wireSvg = makeWireSVG(wire, offset, renderThing.dpi, renderThing.printerScale, renderThing.blackOnly); wireSvg = QString("%2").arg(wire->id()).arg(wireSvg); outputSVG.append(wireSvg); renderThing.empty = false; } extraRenderSvgStep(itemBase, offset, renderThing.dpi, renderThing.printerScale, outputSVG); } outputSVG += ""; return outputSVG; } void SketchWidget::extraRenderSvgStep(ItemBase * itemBase, QPointF offset, double dpi, double printerScale, QString & outputSvg) { Q_UNUSED(itemBase); Q_UNUSED(offset); Q_UNUSED(dpi); Q_UNUSED(printerScale); Q_UNUSED(outputSvg); } QString SketchWidget::makeWireSVG(Wire * wire, QPointF offset, double dpi, double printerScale, bool blackOnly) { QString shadow; bool dashed = false; if (wire->hasShadow()) { shadow = makeWireSVGAux(wire, wire->shadowWidth(), wire->shadowHexString(), offset, dpi, printerScale, blackOnly, false); if (wire->banded()) { dashed = true; shadow += makeWireSVGAux(wire, wire->width(), "white", offset, dpi, printerScale, blackOnly, false); } } return shadow + makeWireSVGAux(wire, wire->width(), wire->hexString(), offset, dpi, printerScale, blackOnly, dashed); } QString SketchWidget::makeWireSVGAux(Wire * wire, double width, const QString & color, QPointF offset, double dpi, double printerScale, bool blackOnly, bool dashed) { if (wire->isCurved()) { QPolygonF poly = wire->sceneCurve(offset); return TextUtils::makeCubicBezierSVG(poly, width, color, dpi, printerScale, blackOnly, dashed, Wire::TheDash); } else { QLineF line = wire->getPaintLine(); QPointF p1 = wire->scenePos() + line.p1() - offset; QPointF p2 = wire->scenePos() + line.p2() - offset; return TextUtils::makeLineSVG(p1, p2, width, color, dpi, printerScale, blackOnly, dashed, Wire::TheDash); } } void SketchWidget::drawBackground( QPainter * painter, const QRectF & rect ) { if (!rect.isValid()) { return; } //DebugDialog::debug(QString("draw background %1").arg(viewName())); InfoGraphicsView::drawBackground(painter, rect); // from: http://www.qtcentre.org/threads/27364-Trying-to-draw-a-grid-on-a-QGraphicsScene-View InfoGraphicsView::drawForeground(painter, rect); // always draw the logo in the same place in the window // no matter how the view is zoomed or scrolled static QPixmap * bgPixmap = NULL; if (bgPixmap == NULL) { bgPixmap = new QPixmap(":resources/images/fritzing_logo_background.png"); } if (bgPixmap) { QPointF p = painter->viewport().bottomLeft(); int hOffset = 0; if (horizontalScrollBar()->isVisible()) { hOffset = horizontalScrollBar()->height(); } p += QPointF(25, hOffset - 25 - bgPixmap->height()); painter->save(); painter->setWindow(painter->viewport()); painter->setTransform(QTransform()); painter->drawPixmap(p,*bgPixmap); painter->restore(); } if (m_showGrid) { double gridSize = m_gridSizeInches * GraphicsUtils::SVGDPI; int intGridSize = int(gridSize * 10000); if (intGridSize > 0) { double left = int(rect.left() * 10000) - (int(rect.left() * 10000) % intGridSize); left /= 10000; double top = int(rect.top() * 10000) - (int(rect.top() * 10000) % intGridSize); top /= 10000; QVarLengthArray linesX; for (double x = left; x < rect.right(); x += gridSize) { linesX.append(QLineF(x, rect.top(), x, rect.bottom())); } QVarLengthArray linesY; for (double y = top; y < rect.bottom(); y += gridSize) { linesY.append(QLineF(rect.left(), y, rect.right(), y)); } //DebugDialog::debug(QString("lines %1 %2").arg(linesX.count()).arg(linesY.count())); QPen pen; pen.setColor(m_gridColor); pen.setWidth(0); pen.setCosmetic(true); //pen.setStyle(Qt::DotLine); //QVector dashes; // removed dash pattern at forum suggestion: http://fritzing.org/forum/thread/855 //dashes << 1 << 1; //pen.setDashPattern(dashes); painter->save(); painter->setPen(pen); painter->drawLines(linesX.data(), linesX.size()); painter->drawLines(linesY.data(), linesY.size()); painter->restore(); } } } /* QPoint SketchWidget::calcFixedToCenterItemOffset(const QRect & viewPortRect, const QSizeF & helpSize) { QPoint p((int) ((viewPortRect.width() - helpSize.width()) / 2.0), (int) ((viewPortRect.height() - helpSize.height()) / 2.0)); return p; } */ void SketchWidget::pushCommand(QUndoCommand * command, QObject * thing) { if (m_undoStack) { CommandProgress * commandProgress = BaseCommand::initProgress(); connect(commandProgress, SIGNAL(incRedo()), thing, SLOT(incCommandProgress())); m_undoStack->push(command); BaseCommand::clearProgress(); } } bool SketchWidget::spaceBarIsPressed() { return m_spaceBarIsPressed || m_middleMouseIsPressed; } ViewLayer::ViewLayerID SketchWidget::defaultConnectorLayer(ViewLayer::ViewID viewId) { switch(viewId) { case ViewLayer::IconView: return ViewLayer::Icon; case ViewLayer::BreadboardView: return ViewLayer::Breadboard; case ViewLayer::SchematicView: return ViewLayer::Schematic; case ViewLayer::PCBView: return ViewLayer::Copper0; default: return ViewLayer::UnknownLayer; } } bool SketchWidget::swappedGender(ConnectorItem * connectorItem, Connector * newConnector) { return (connectorItem->connectorType() != newConnector->connectorType()); } void SketchWidget::setLastPaletteItemSelected(PaletteItem * paletteItem) { m_lastPaletteItemSelected = paletteItem; //DebugDialog::debug(QString("m_lastPaletteItemSelected:%1 %2").arg(paletteItem == NULL ? "NULL" : paletteItem->instanceTitle()).arg(m_viewID)); } void SketchWidget::setLastPaletteItemSelectedIf(ItemBase * itemBase) { PaletteItem * paletteItem = qobject_cast(itemBase); if (paletteItem == NULL) return; setLastPaletteItemSelected(paletteItem); } void SketchWidget::setResistance(QString resistance, QString pinSpacing) { PaletteItem * item = getSelectedPart(); if (item == NULL) return; ModelPart * modelPart = item->modelPart(); if (!modelPart->moduleID().endsWith(ModuleIDNames::ResistorModuleIDName)) return; Resistor * resistor = qobject_cast(item); if (resistor == NULL) return; if (resistance.isEmpty()) { resistance = resistor->resistance(); } if (pinSpacing.isEmpty()) { pinSpacing = resistor->pinSpacing(); } SetResistanceCommand * cmd = new SetResistanceCommand(this, item->id(), resistor->resistance(), resistance, resistor->pinSpacing(), pinSpacing, NULL); cmd->setText(tr("Change Resistance from %1 to %2").arg(resistor->resistance()).arg(resistance)); m_undoStack->waitPush(cmd, PropChangeDelay); } void SketchWidget::setResistance(long itemID, QString resistance, QString pinSpacing, bool doEmit) { ItemBase * item = findItem(itemID); if (item == NULL) return; Resistor * ritem = qobject_cast(item); if (ritem == NULL) return; ritem->setResistance(resistance, pinSpacing, false); viewItemInfo(item); if (doEmit) { emit setResistanceSignal(itemID, resistance, pinSpacing, false); } } void SketchWidget::setProp(ItemBase * item, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, bool redraw) { if (oldValue.isEmpty() && newValue.isEmpty()) return; SetPropCommand * cmd = new SetPropCommand(this, item->id(), prop, oldValue, newValue, redraw, NULL); cmd->setText(tr("Change %1 from %2 to %3").arg(trProp).arg(oldValue).arg(newValue)); // unhook triggered action from originating widget event m_undoStack->waitPush(cmd, PropChangeDelay); } void SketchWidget::setHoleSize(ItemBase * item, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, QRectF & oldRect, QRectF & newRect, bool redraw) { if (oldValue.isEmpty() && newValue.isEmpty()) return; QUndoCommand * parentCommand = new QUndoCommand(tr("Change %1 from %2 to %3").arg(trProp).arg(oldValue).arg(newValue)); new SetPropCommand(this, item->id(), prop, oldValue, newValue, redraw, parentCommand); item->saveGeometry(); ViewGeometry vg(item->getViewGeometry()); QPointF p(vg.loc().x() + (oldRect.width() / 2) - (newRect.width() / 2), vg.loc().y() + (oldRect.height() / 2) - (newRect.height() / 2)); vg.setLoc(p); new MoveItemCommand(this, item->id(), item->getViewGeometry(), vg, false, parentCommand); //DebugDialog::debug("set hole", oldRect); //DebugDialog::debug(" ", newRect); //DebugDialog::debug(" ", item->getViewGeometry().loc()); //DebugDialog::debug(" ", p); m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::setProp(long itemID, const QString & prop, const QString & value, bool redraw, bool doEmit) { ItemBase * item = findItem(itemID); if (item == NULL) return; item->setProp(prop, value); if (redraw) { viewItemInfo(item); } if (doEmit) { emit setPropSignal(itemID, prop, value, false, false); } } // called from ResizeBoardCommand ItemBase * SketchWidget::resizeBoard(long itemID, double mmW, double mmH) { ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return NULL; bool resized = false; switch (itemBase->itemType()) { case ModelPart::ResizableBoard: qobject_cast(itemBase)->resizeMM(mmW, mmH, m_viewLayers); resized = true; break; case ModelPart::Logo: qobject_cast(itemBase)->resizeMM(mmW, mmH, m_viewLayers); resized = true; break; case ModelPart::Ruler: qobject_cast(itemBase)->resizeMM(mmW, mmH, m_viewLayers); resized = true; break; } if (!resized) { Pad * pad = qobject_cast(itemBase); if (pad) { pad->resizeMM(mmW, mmH, m_viewLayers); resized = true; } } if (!resized) { SchematicFrame * schematicFrame = qobject_cast(itemBase); if (schematicFrame) { schematicFrame->resizeMM(mmW, mmH, m_viewLayers); resized = true; } } if (resized) { emit resizedSignal(itemBase); } return itemBase; } void SketchWidget::resizeBoard(double mmW, double mmH, bool doEmit) { Q_UNUSED(doEmit); PaletteItem * item = getSelectedPart(); if (item == NULL) { return InfoGraphicsView::resizeBoard(mmW, mmH, doEmit); } switch (item->itemType()) { case ModelPart::Ruler: break; case ModelPart::Logo: resizeWithHandle(item, mmW, mmH); default: return; } QString orig = item->prop("width"); QString temp = orig; temp.chop(2); double origw = temp.toDouble(); double origh = orig.endsWith("cm") ? 0 : 1; QUndoCommand * parentCommand = new QUndoCommand(tr("Resize ruler to %1 %2").arg(mmW).arg((mmH == 0) ? "cm" : "in")); new ResizeBoardCommand(this, item->id(), origw, origh, mmW, mmH, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::resizeWithHandle(ItemBase * itemBase, double mmW, double mmH) { double origw = itemBase->modelPart()->localProp("width").toDouble(); double origh = itemBase->modelPart()->localProp("height").toDouble(); if (mmH == 0 || mmW == 0) { dynamic_cast(itemBase)->setInitialSize(); double w = itemBase->modelPart()->localProp("width").toDouble(); double h = itemBase->modelPart()->localProp("height").toDouble(); if (origw == w && origh == h) { // no change return; } viewItemInfo(itemBase); mmW = w; mmH = h; } QUndoCommand * parentCommand = new QUndoCommand(tr("Resize board to %1 %2").arg(mmW).arg(mmH)); rememberSticky(itemBase, parentCommand); new ResizeBoardCommand(this, itemBase->id(), origw, origh, mmW, mmH, parentCommand); new CheckStickyCommand(this, BaseCommand::SingleView, itemBase->id(), true, CheckStickyCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::addBendpoint(ItemBase * lastHoverEnterItem, ConnectorItem * lastHoverEnterConnectorItem, QPointF lastLocation) { if (lastHoverEnterConnectorItem) { Wire * wire = qobject_cast(lastHoverEnterConnectorItem->attachedTo()); if (wire != NULL) { wireJoinSlot(wire, lastHoverEnterConnectorItem); } } else if (lastHoverEnterItem) { Wire * wire = qobject_cast(lastHoverEnterItem); if (wire != NULL) { wireSplitSlot(wire, lastLocation, wire->pos(), wire->line()); } } } void SketchWidget::flattenCurve(ItemBase * lastHoverEnterItem, ConnectorItem * lastHoverEnterConnectorItem, QPointF lastLocation) { Q_UNUSED(lastLocation); Wire * wire = NULL; if (lastHoverEnterConnectorItem) { wire = qobject_cast(lastHoverEnterConnectorItem->attachedTo()); } if (wire == NULL && lastHoverEnterItem) { wire = qobject_cast(lastHoverEnterItem); } if (wire != NULL) { wireChangedCurveSlot(wire, wire->curve(), NULL, true); } } ConnectorItem * SketchWidget::lastHoverEnterConnectorItem() { return m_lastHoverEnterConnectorItem; } ItemBase * SketchWidget::lastHoverEnterItem() { return m_lastHoverEnterItem; } LayerHash & SketchWidget::viewLayers() { return m_viewLayers; } void SketchWidget::setClipEnds(ClipableWire * vw, bool) { vw->setClipEnds(false); } void SketchWidget::createTrace(Wire * wire, bool useLastWireColor) { QString commandString = tr("Create wire from Ratsnest"); createTrace(wire, commandString, getTraceFlag(), useLastWireColor); } void SketchWidget::createTrace(Wire * fromWire, const QString & commandString, ViewGeometry::WireFlag flag, bool useLastWireColor) { QList done; QUndoCommand * parentCommand = new QUndoCommand(commandString); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); bool gotOne = false; if (fromWire == NULL) { foreach (QGraphicsItem * item, scene()->selectedItems()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (done.contains(wire)) continue; gotOne = createOneTrace(wire, flag, false, done, useLastWireColor, parentCommand); } } else { gotOne = createOneTrace(fromWire, flag, false, done, useLastWireColor, parentCommand); } if (!gotOne) { delete parentCommand; return; } new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->push(parentCommand); } bool SketchWidget::createOneTrace(Wire * wire, ViewGeometry::WireFlag flag, bool allowAny, QList & done, bool useLastWireColor, QUndoCommand * parentCommand) { QList ends; Wire * trace = NULL; if (wire->getRatsnest()) { trace = wire->findTraced(getTraceFlag(), ends); } else if (wire->isTraceType(getTraceFlag())) { trace = wire; } else if (!allowAny) { // not eligible return false; } else { trace = wire->findTraced(getTraceFlag(), ends); } if (trace && trace->hasFlag(flag)) { return false; } if (trace != NULL) { removeWire(trace, ends, done, parentCommand); } QString colorString = traceColor(createWireViewLayerPlacement(ends[0], ends[1])); if (useLastWireColor && !this->m_lastColorSelected.isEmpty()) { colorString = m_lastColorSelected; } long newID = createWire(ends[0], ends[1], flag, false, BaseCommand::CrossView, parentCommand); // TODO: is this opacity constant stored someplace new WireColorChangeCommand(this, newID, colorString, colorString, 1.0, 1.0, parentCommand); new WireWidthChangeCommand(this, newID, getTraceWidth(), getTraceWidth(), parentCommand); return true; } void SketchWidget::selectAllWires(ViewGeometry::WireFlag flag) { QList items = scene()->items(); selectAllWiresFrom(flag, items); } void SketchWidget::selectAllWiresFrom(ViewGeometry::WireFlag flag, QList & items) { QList wires; foreach (QGraphicsItem * item, items) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (wire->hasFlag(flag)) { if (wire->parentItem() != NULL) { // skip module wires continue; } wires.append(wire); } } if (wires.count() <= 0) { // TODO: tell user? } QString wireName; if (flag == getTraceFlag()) { wireName = QObject::tr("Trace wires"); } else if (flag == ViewGeometry::RatsnestFlag) { wireName = QObject::tr("Ratsnest wires"); } QUndoCommand * parentCommand = new QUndoCommand(QObject::tr("Select all %1").arg(wireName)); stackSelectionState(false, parentCommand); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); foreach (Wire * wire, wires) { selectItemCommand->addRedo(wire->id()); } scene()->clearSelection(); m_undoStack->push(parentCommand); } void SketchWidget::tidyWires() { } void SketchWidget::updateConnectors() { // update issue with 4.5.0? QList visited; foreach (QGraphicsItem* item, scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; connectorItem->restoreColor(visited); } } const QString & SketchWidget::getShortName() { return m_shortName; } void SketchWidget::getBendpointWidths(Wire * wire, double width, double & bendpointWidth, double & bendpoint2Width, bool & negativeOffsetRect) { Q_UNUSED(wire); Q_UNUSED(width); bendpoint2Width = bendpointWidth = -1; negativeOffsetRect = true; } QColor SketchWidget::standardBackground() { return RatsnestColors::backgroundColor(m_viewID); } void SketchWidget::initBackgroundColor() { setBackground(standardBackground()); QSettings settings; QString colorName = settings.value(QString("%1BackgroundColor").arg(getShortName())).toString(); if (!colorName.isEmpty()) { QColor color; color.setNamedColor(colorName); setBackground(color); } m_curvyWires = false; QString curvy = settings.value(QString("%1CurvyWires").arg(getShortName())).toString(); if (!curvy.isEmpty()) { m_curvyWires = (curvy.compare("1") == 0); } } bool SketchWidget::includeSymbols() { return false; } void SketchWidget::disconnectAll() { QSet itemBases; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; itemBases.insert(itemBase); } QList connectorItems; foreach (ItemBase * itemBase, itemBases) { ConnectorItem * fromConnectorItem = itemBase->rightClickedConnector(); if (fromConnectorItem == NULL) continue; if (fromConnectorItem->connectedToWires()) { connectorItems.append(fromConnectorItem); } } if (connectorItems.count() <= 0) return; QString string; if (itemBases.count() == 1) { ItemBase * firstItem = *(itemBases.begin()); string = tr("Disconnect all wires from %1").arg(firstItem->title()); } else { string = tr("Disconnect all wires from %1 items").arg(QString::number(itemBases.count())); } QUndoCommand * parentCommand = new QUndoCommand(string); stackSelectionState(false, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); QHash itemsToDelete; disconnectAllSlot(connectorItems, itemsToDelete, parentCommand); emit disconnectAllSignal(connectorItems, itemsToDelete, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); foreach (ItemBase * item, itemsToDelete.keys()) { itemsToDelete.value(item)->makeDeleteItemCommand(item, BaseCommand::CrossView, parentCommand); } m_undoStack->push(parentCommand); } void SketchWidget::disconnectAllSlot(QList connectorItems, QHash & itemsToDelete, QUndoCommand * parentCommand) { // (jc 2011 Aug 16): this code is not hooked up and my last recollection is that it wasn't working return; QList myConnectorItems; foreach (ConnectorItem * ci, connectorItems) { ItemBase * itemBase = findItem(ci->attachedToID()); if (itemBase == NULL) continue; ConnectorItem * fromConnectorItem = findConnectorItem(itemBase, ci->connectorSharedID(), ViewLayer::NewTop); if (fromConnectorItem != NULL) { myConnectorItems.append(fromConnectorItem); } fromConnectorItem = findConnectorItem(itemBase, ci->connectorSharedID(), ViewLayer::NewBottom); if (fromConnectorItem != NULL) { myConnectorItems.append(fromConnectorItem); } } QSet deletedItems; foreach (ConnectorItem * fromConnectorItem, myConnectorItems) { foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { Wire * wire = qobject_cast(toConnectorItem->attachedTo()); if (!wire->getRatsnest()) { QList chained; QList ends; wire->collectChained(chained, ends); foreach (Wire * w, chained) { itemsToDelete.insert(w, this); deletedItems.insert(w); } } } else if (toConnectorItem->connectorType() == Connector::Female) { if (ignoreFemale()) { //fromConnectorItem->tempRemove(toConnectorItem, false); //toConnectorItem->tempRemove(fromConnectorItem, false); //extendChangeConnectionCommand(fromConnectorItem, toConnectorItem, false, true, parentCommand); } else { ItemBase * detachee = fromConnectorItem->attachedTo(); QPointF newPos = calcNewLoc(detachee, toConnectorItem->attachedTo()); // delete connections // add wires and connections for undisconnected connectors detachee->saveGeometry(); ViewGeometry vg = detachee->getViewGeometry(); vg.setLoc(newPos); new MoveItemCommand(this, detachee->id(), detachee->getViewGeometry(), vg, false, parentCommand); QHash emptyList; ConnectorPairHash connectorHash; disconnectFromFemale(detachee, emptyList, connectorHash, true, false, parentCommand); foreach (ConnectorItem * fConnectorItem, connectorHash.uniqueKeys()) { if (myConnectorItems.contains(fConnectorItem)) { // don't need to reconnect continue; } foreach (ConnectorItem * tConnectorItem, connectorHash.values(fConnectorItem)) { createWire(fConnectorItem, tConnectorItem, ViewGeometry::NoFlag, false, BaseCommand::CrossView, parentCommand); } } } } } } deleteMiddle(deletedItems, parentCommand); } bool SketchWidget::canDisconnectAll() { return true; } bool SketchWidget::ignoreFemale() { return true; } QPointF SketchWidget::calcNewLoc(ItemBase * moveBase, ItemBase * detachFrom) { QRectF dr = detachFrom->boundingRect(); dr.moveTopLeft(detachFrom->pos()); QPointF pos = moveBase->pos(); QRectF r = moveBase->boundingRect(); pos.setX(pos.x() + (r.width() / 2.0)); pos.setY(pos.y() + (r.height() / 2.0)); double d[4]; d[0] = qAbs(pos.y() - dr.top()); d[1] = qAbs(pos.y() - dr.bottom()); d[2] = qAbs(pos.x() - dr.left()); d[3] = qAbs(pos.x() - dr.right()); int ix = 0; for (int i = 1; i < 4; i++) { if (d[i] < d[ix]) { ix = i; } } QPointF newPos = moveBase->pos(); switch (ix) { case 0: newPos.setY(dr.top() - r.height()); break; case 1: newPos.setY(dr.bottom()); break; case 2: newPos.setX(dr.left() - r.width()); break; case 3: newPos.setX(dr.right()); break; } return newPos; } long SketchWidget::findPartOrWire(long itemID) { ItemBase * item = findItem(itemID); if (item == NULL) return itemID; if (item->itemType() != ModelPart::Wire) return itemID; QList chained; QList ends; qobject_cast(item)->collectChained(chained, ends); if (chained.length() <= 1) return itemID; foreach (Wire * w, chained) { if (w->id() < itemID) { itemID = w->id(); } } return itemID; } void SketchWidget::resizeJumperItem(long itemID, QPointF pos, QPointF c0, QPointF c1) { ItemBase * item = findItem(itemID); if (item == NULL) return; if (item->itemType() != ModelPart::Jumper) return; qobject_cast(item)->resize(pos, c0, c1); } QList SketchWidget::selectAllObsolete() { QSet itemBases; foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->isObsolete()) continue; itemBases.insert(itemBase->layerKinChief()); } selectAllItems(itemBases, QObject::tr("Select outdated parts")); return itemBases.toList(); } int SketchWidget::selectAllMoveLock() { QSet itemBases; foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (!itemBase->moveLock()) continue; itemBases.insert(itemBase->layerKinChief()); } return selectAllItems(itemBases, QObject::tr("Select locked parts")); } int SketchWidget::selectAllItems(QSet & itemBases, const QString & msg) { if (itemBases.count() <= 0) { // TODO: tell user? return 0; } QUndoCommand * parentCommand = new QUndoCommand(msg); stackSelectionState(false, parentCommand); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); foreach (ItemBase * itemBase, itemBases) { selectItemCommand->addRedo(itemBase->id()); } scene()->clearSelection(); m_undoStack->push(parentCommand); return itemBases.count(); } AddItemCommand * SketchWidget::newAddItemCommand(BaseCommand::CrossViewType crossViewType, ModelPart * newModelPart, QString moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, qint64 id, bool updateInfoView, long modelIndex, bool addSubparts, QUndoCommand *parent) { AddItemCommand * aic = new AddItemCommand(this, crossViewType, moduleID, viewLayerPlacement, viewGeometry, id, updateInfoView, modelIndex, parent); if (newModelPart == NULL) { newModelPart = m_referenceModel->retrieveModelPart(moduleID); } if (!newModelPart->hasSubparts() || !addSubparts) return aic; ModelPartShared * modelPartShared = newModelPart->modelPartShared(); if (modelPartShared == NULL) return aic; foreach (ModelPartShared * mps, modelPartShared->subparts()) { long subID = ItemBase::getNextID(); ViewGeometry vg = viewGeometry; vg.setLoc(vg.loc() + (mps->subpartOffset() * GraphicsUtils::SVGDPI)); new AddItemCommand(this, crossViewType, mps->moduleID(), viewLayerPlacement, vg, subID, updateInfoView, -1, parent); AddSubpartCommand * asc = new AddSubpartCommand(this, crossViewType, id, subID, parent); asc->setRedoOnly(); } return aic; } bool SketchWidget::partLabelsVisible() { foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase->isPartLabelVisible()) return true; } return false; } void SketchWidget::showLabelFirstTime(long itemID, bool show, bool doEmit) { if (doEmit) { emit showLabelFirstTimeSignal(itemID, show, false); } } void SketchWidget::restorePartLabel(long itemID, QDomElement & element) { ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; itemBase->restorePartLabel(element, getLabelViewLayerID(itemBase)); } void SketchWidget::loadLogoImage(ItemBase * itemBase, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName) { if (oldSvg.isEmpty() && oldFilename.isEmpty()) { // have to swap in a custom shape then load the file emit swapBoardImageSignal(this, itemBase, newFilename, m_boardLayers == 1 ? ModuleIDNames::OneLayerBoardLogoImageModuleIDName : ModuleIDNames::BoardLogoImageModuleIDName, addName); return; } QUndoCommand * cmd = new LoadLogoImageCommand(this, itemBase->id(), oldSvg, oldAspectRatio, oldFilename, newFilename, addName, NULL); cmd->setText(tr("Change image from %1 to %2").arg(oldFilename).arg(newFilename)); m_undoStack->waitPush(cmd, PropChangeDelay); } void SketchWidget::loadLogoImage(long itemID, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename) { ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; LogoItem * logoItem = qobject_cast(itemBase); if (logoItem == NULL) return; logoItem->reloadImage(oldSvg, oldAspectRatio, oldFilename, false); } void SketchWidget::loadLogoImage(long itemID, const QString & newFilename, bool addName) { ItemBase * itemBase = findItem(itemID); if (itemBase == NULL) return; LogoItem * logoItem = qobject_cast(itemBase); if (logoItem == NULL) return; logoItem->loadImage(newFilename, addName); } void SketchWidget::paintEvent ( QPaintEvent * event ) { //DebugDialog::debug("sketch widget paint event"); if (scene()) { ((FGraphicsScene *) scene())->setDisplayHandles(true); } QGraphicsView::paintEvent(event); } void SketchWidget::setNoteFocus(QGraphicsItem * item, bool inFocus) { if (inFocus) { m_inFocus.append(item); } else { m_inFocus.removeOne(item); } } double SketchWidget::defaultGridSizeInches() { return 0.0; // should never get here } double SketchWidget::gridSizeInches() { return m_gridSizeInches; } void SketchWidget::alignToGrid(bool align) { m_alignToGrid = align; QSettings settings; settings.setValue(QString("%1AlignToGrid").arg(viewName()), align); } void SketchWidget::showGrid(bool show) { m_showGrid = show; QSettings settings; settings.setValue(QString("%1ShowGrid").arg(viewName()), show); update(); } bool SketchWidget::alignedToGrid() { return m_alignToGrid; } bool SketchWidget::showingGrid() { return m_showGrid; } bool SketchWidget::canAlignToTopLeft(ItemBase *) { return false; } bool SketchWidget::canAlignToCenter(ItemBase *) { return false; } void SketchWidget::saveZoom(double zoom) { m_zoom = zoom; } double SketchWidget::retrieveZoom() { return m_zoom; } void SketchWidget::setGridSize(const QString & newSize) { QSettings settings; settings.setValue(QString("%1GridSize").arg(viewName()), newSize); m_gridSizeInches = TextUtils::convertToInches(newSize); m_gridSizeText = newSize; if (m_showGrid) { invalidateScene(); } } QString SketchWidget::gridSizeText() { if (!m_gridSizeText.isEmpty()) return m_gridSizeText; return QString("%1in").arg(m_gridSizeInches); } void SketchWidget::initGrid() { m_showGrid = m_alignToGrid = true; m_gridSizeInches = defaultGridSizeInches(); QSettings settings; QString szString = settings.value(QString("%1GridSize").arg(viewName()), "").toString(); if (!szString.isEmpty()) { bool ok; double temp = TextUtils::convertToInches(szString, &ok, false); if (ok) { m_gridSizeInches = temp; m_gridSizeText = szString; } } m_alignToGrid = settings.value(QString("%1AlignToGrid").arg(viewName()), true).toBool(); m_showGrid = settings.value(QString("%1ShowGrid").arg(viewName()), true).toBool(); } void SketchWidget::copyDrop() { QList itemBases; foreach (ItemBase * itemBase, m_savedItems.values()) { QList superSubs = collectSuperSubs(itemBase); if (superSubs.count() > 0) { foreach (ItemBase * supersub, superSubs) { if (!itemBases.contains(supersub)) itemBases.append(supersub); } continue; } itemBases.append(itemBase); } qSort(itemBases.begin(), itemBases.end(), ItemBase::zLessThan); foreach (ItemBase * itemBase, itemBases) { QPointF loc = itemBase->getViewGeometry().loc(); itemBase->setItemPos(loc); } copyAux(itemBases, false); m_savedItems.clear(); m_savedWires.clear(); } ViewLayer::ViewLayerPlacement SketchWidget::defaultViewLayerPlacement(ModelPart * modelPart) { Q_UNUSED(modelPart); //return (m_boardLayers == 1) ? ViewLayer::NewBottom : ViewLayer::NewTop; return ViewLayer::NewTop; } ViewLayer::ViewLayerPlacement SketchWidget::wireViewLayerPlacement(ConnectorItem *) { return (m_boardLayers == 1) ? ViewLayer::NewBottom : ViewLayer::NewTop; } void SketchWidget::changeBoardLayers(int layers, bool doEmit) { m_boardLayers = layers; if (doEmit) { emit changeBoardLayersSignal(layers, false); } } void SketchWidget::collectAllNets(QHash & indexer, QList< QList* > & allPartConnectorItems, bool includeSingletons, bool bothSides) { // get the set of all connectors in the sketch QList allConnectors; foreach (QGraphicsItem * item, scene()->items()) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!bothSides && connectorItem->attachedToViewLayerID() == ViewLayer::Copper1) continue; allConnectors.append(connectorItem); } // find all the nets and make a list of nodes (i.e. part ConnectorItems) for each net while (allConnectors.count() > 0) { ConnectorItem * connectorItem = allConnectors.takeFirst(); QList connectorItems; connectorItems.append(connectorItem); ConnectorItem::collectEqualPotential(connectorItems, bothSides, ViewGeometry::NoFlag); if (connectorItems.count() <= 0) { continue; } foreach (ConnectorItem * ci, connectorItems) { //if (connectorItems.count(ci) > 1) { //DebugDialog::debug("collect equal potential bug"); //} //DebugDialog::debug(QString("from in equal potential %1 %2").arg(ci->connectorSharedName()).arg(ci->attachedToInstanceTitle())); allConnectors.removeOne(ci); } if (!includeSingletons && (connectorItems.count() <= 1)) { continue; } QList * partConnectorItems = new QList; ConnectorItem::collectParts(connectorItems, *partConnectorItems, includeSymbols(), ViewLayer::NewTopAndBottom); for (int i = partConnectorItems->count() - 1; i >= 0; i--) { if (!partConnectorItems->at(i)->attachedTo()->isEverVisible()) { partConnectorItems->removeAt(i); } } if ((partConnectorItems->count() <= 0) || (!includeSingletons && (partConnectorItems->count() <= 1))) { delete partConnectorItems; continue; } foreach (ConnectorItem * ci, *partConnectorItems) { //if (partConnectorItems->count(ci) > 1) { //DebugDialog::debug("collect Parts bug"); //} if (!connectorItems.contains(ci)) { // crossed layer: toss it //DebugDialog::debug(QString("not in equal potential '%1' '%2' %3") // .arg(ci->connectorSharedName()) // .arg(ci->attachedToInstanceTitle()) // .arg(ci->attachedToViewLayerID())); continue; } //if (indexer.keys().contains(ci)) { //DebugDialog::debug(QString("connector item already indexed %1 %2").arg(ci->connectorSharedName()).arg(ci->attachedToInstanceTitle())); //} //int c = indexer.count(); //DebugDialog::debug(QString("insert indexer %1 '%2' '%3' %4") //.arg(c) //.arg(ci->connectorSharedName()) //.arg(ci->attachedToInstanceTitle()) //.arg(ci->attachedToViewLayerID())); indexer.insert(ci, indexer.count()); } //DebugDialog::debug("________________"); allPartConnectorItems.append(partConnectorItems); } } ViewLayer::ViewLayerPlacement SketchWidget::getViewLayerPlacement(ModelPart * modelPart, QDomElement & instance, QDomElement & view, ViewGeometry & viewGeometry) { Q_UNUSED(instance); ViewLayer::ViewLayerPlacement viewLayerPlacement = defaultViewLayerPlacement(modelPart); if (modelPart->moduleID().compare(ModuleIDNames::GroundPlaneModuleIDName) == 0) { QString layer = view.attribute("layer"); if (layer.isEmpty()) return viewLayerPlacement; ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(layer); if (viewLayerID == ViewLayer::GroundPlane1) { return ViewLayer::NewTop; } return ViewLayer::NewBottom; } if (viewGeometry.getAnyTrace()) { QString layer = view.attribute("layer"); if (layer.isEmpty()) return viewLayerPlacement; ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(layer); switch (viewLayerID) { case ViewLayer::Copper1Trace: case ViewLayer::GroundPlane1: case ViewLayer::Copper1: return ViewLayer::NewTop; case ViewLayer::Copper0Trace: case ViewLayer::GroundPlane0: case ViewLayer::Copper0: return ViewLayer::NewBottom; default: break; } } return viewLayerPlacement; } bool SketchWidget::routeBothSides() { return false; } void SketchWidget::copyBoundingRectsSlot(QHash & boundingRects) { QRectF itemsBoundingRect; QList tlBases; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item); if (itemBase == NULL) continue; if (itemBase->getRatsnest()) continue; itemsBoundingRect |= itemBase->sceneBoundingRect(); } boundingRects.insert(m_viewName, itemsBoundingRect); } void SketchWidget::changeLayer(long id, double z, ViewLayer::ViewLayerID viewLayerID) { Q_UNUSED(id); Q_UNUSED(z); Q_UNUSED(viewLayerID); } bool SketchWidget::resizingJumperItemRelease() { return false; } bool SketchWidget::resizingJumperItemPress(ItemBase *) { return false; } bool SketchWidget::resizingBoardPress(ItemBase * itemBase) { if (itemBase == NULL) return false; // board's child items (at the moment) are the resize grips ResizableBoard * rb = qobject_cast(itemBase->layerKinChief()); if (rb == NULL) return false; if (!rb->inResize()) return false; m_resizingBoard = rb; m_resizingBoard->saveParams(); return true; } bool SketchWidget::resizingBoardRelease() { if (m_resizingBoard == NULL) return false; resizeBoard(); return true; } void SketchWidget::resizeBoard() { QSizeF oldSize; QPointF oldPos; m_resizingBoard->getParams(oldPos, oldSize); QSizeF newSize; QPointF newPos; m_resizingBoard->saveParams(); m_resizingBoard->getParams(newPos, newSize); QUndoCommand * parentCommand = new QUndoCommand(tr("Resize board to %1 %2").arg(newSize.width()).arg(newSize.height())); rememberSticky(m_resizingBoard, parentCommand); new ResizeBoardCommand(this, m_resizingBoard->id(), oldSize.width(), oldSize.height(), newSize.width(), newSize.height(), parentCommand); if (oldPos != newPos) { m_resizingBoard->saveGeometry(); ViewGeometry vg1 = m_resizingBoard->getViewGeometry(); ViewGeometry vg2 = vg1; vg1.setLoc(oldPos); vg2.setLoc(newPos); new MoveItemCommand(this, m_resizingBoard->id(), vg1, vg2, false, parentCommand); } new CheckStickyCommand(this, BaseCommand::SingleView, m_resizingBoard->id(), true, CheckStickyCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, 10); m_resizingBoard = NULL; } bool SketchWidget::hasAnyNets() { return false; } void SketchWidget::ratsnestConnect(ItemBase * itemBase, bool connect) { foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { ratsnestConnect(connectorItem, connect); } } void SketchWidget::ratsnestConnect(long id, const QString & connectorID, bool connect, bool doEmit) { if (doEmit) { emit ratsnestConnectSignal(id, connectorID, connect, false); } ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, itemBase->viewLayerPlacement()); if (connectorItem == NULL) return; ratsnestConnect(connectorItem, connect); } void SketchWidget::ratsnestConnect(ConnectorItem * c1, ConnectorItem * c2, bool connect, bool wait) { if (wait) { if (connect) { m_ratsnestCacheConnect << c1 << c2; } else { m_ratsnestCacheDisconnect << c1 << c2; } return; } QList connectorItems; connectorItems.append(c1); connectorItems.append(c2); ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag); foreach (ConnectorItem * connectorItem, connectorItems) { ratsnestConnect(connectorItem, connect); } } void SketchWidget::ratsnestConnect(ConnectorItem * connectorItem, bool connect) { if (connect) { m_ratsnestUpdateConnect << connectorItem; } else { m_ratsnestUpdateDisconnect << connectorItem; } //connectorItem->debugInfo(QString("rat connect %1").arg(connect)); } void SketchWidget::deleteRatsnest(Wire * ratsnest, QUndoCommand * parentCommand) { // deleting a ratsnest really means deleting underlying connections // assume ratsnest has only one connection at each end ConnectorItem * source = ratsnest->connector0()->firstConnectedToIsh(); ConnectorItem * sink = ratsnest->connector1()->firstConnectedToIsh(); QList connectorItems; connectorItems.append(source); connectorItems.append(sink); ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag); QList foreignSketchWidgets; emit collectRatsnestSignal(foreignSketchWidgets); // there are multiple possibilities for each pair of connectors: // they are directly connected because they're each inserted into female connectors on the same bus // they are directly connected with a wire // they are "directly" connected through some combination of female connectors and wires (i.e. one part is connected to a wire which is inserted into a female connector) // they are indirectly connected via other parts // what if there are multiple direct connections--treat it as a single connection and delete them all QList cutSet; GraphUtils::minCut(connectorItems, foreignSketchWidgets, source, sink, cutSet); emit removeRatsnestSignal(cutSet, parentCommand); foreach (ConnectorEdge * ce, cutSet) { delete ce; } } void SketchWidget::removeRatsnestSlot(QList & cutSet, QUndoCommand * parentCommand) { QHash detachItems; // key is part to be detached, value is part to detach from QSet deletedItems; QList deletedIDs; foreach (ConnectorEdge * ce, cutSet) { if (ce->c0->attachedToViewID() != viewID()) continue; if (ce->c1->attachedToViewID() != viewID()) continue; if (ce->wire) { QList ends; QList wires; ce->wire->collectChained(wires, ends); foreach (Wire * w, wires) { if (!deletedIDs.contains(w->id())) { deletedItems.insert(w); deletedIDs.append(w->id()); } } } else { // we have to detach the source or sink from a female connector if (ce->c0->connectorType() == Connector::Female) { detachItems.insert(ce->c1, ce->c0); } else { detachItems.insert(ce->c0, ce->c1); } } } foreach (ConnectorItem * detacheeConnector, detachItems.keys()) { ItemBase * detachee = detacheeConnector->attachedTo(); ConnectorItem * detachFromConnector = detachItems.value(detacheeConnector); ItemBase * detachFrom = detachFromConnector->attachedTo(); QPointF newPos = calcNewLoc(detachee, detachFrom); // delete connections // add wires and connections for undisconnected connectors detachee->saveGeometry(); ViewGeometry vg = detachee->getViewGeometry(); vg.setLoc(newPos); new MoveItemCommand(this, detachee->id(), detachee->getViewGeometry(), vg, false, parentCommand); QHash emptyList; ConnectorPairHash connectorHash; disconnectFromFemale(detachee, emptyList, connectorHash, true, false, parentCommand); foreach (ConnectorItem * fromConnectorItem, connectorHash.uniqueKeys()) { if (detachItems.keys().contains(fromConnectorItem)) { // don't need to reconnect continue; } if (detachItems.values().contains(fromConnectorItem)) { // don't need to reconnect continue; } foreach (ConnectorItem * toConnectorItem, connectorHash.values(fromConnectorItem)) { createWire(fromConnectorItem, toConnectorItem, ViewGeometry::NoFlag, false, BaseCommand::CrossView, parentCommand); } } } deleteAux(deletedItems, parentCommand, false); } void SketchWidget::addDefaultParts() { } void SketchWidget::vScrollToZero() { verticalScrollBar()->setValue(verticalScrollBar()->minimum()); } float SketchWidget::getTopZ() { return m_z; } QGraphicsItem * SketchWidget::addWatermark(const QString &filename) { QGraphicsSvgItem * item = new QGraphicsSvgItem(filename); if (item == NULL) return NULL; this->scene()->addItem(item); return item; } bool SketchWidget::acceptsTrace(const ViewGeometry &) { return false; } QPointF SketchWidget::alignOneToGrid(ItemBase * itemBase) { if (m_alignToGrid) { QHash savedItems; QHash savedWires; findAlignmentAnchor(itemBase, savedItems, savedWires); if (m_alignmentItem) { m_alignmentItem = NULL; QPointF loc = itemBase->pos(); alignLoc(loc, m_alignmentStartPoint, QPointF(0,0), QPointF(0, 0)); QPointF result = loc - itemBase->pos(); itemBase->setPos(loc); return result; } } return QPointF(0, 0); } ViewGeometry::WireFlag SketchWidget::getTraceFlag() { return ViewGeometry::NormalFlag; } void SketchWidget::changeBus(ItemBase * itemBase, bool connect, const QString & oldBus, const QString & newBus, QList & connectorItems, const QString & message, const QString & oldLayout, const QString & newLayout) { QUndoCommand * parentCommand = new QUndoCommand(message); CleanUpWiresCommand * cuwc = new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); foreach(ConnectorItem * connectorItem, connectorItems) { cuwc->addRatsnestConnect(connectorItem->attachedToID(), connectorItem->connectorSharedID(), connect); } new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new SetPropCommand(this, itemBase->id(), "buses", oldBus, newBus, true, parentCommand); new SetPropCommand(this, itemBase->id(), "layout", oldLayout, newLayout, true, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); cuwc = new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); foreach(ConnectorItem * connectorItem, connectorItems) { cuwc->addRatsnestConnect(connectorItem->attachedToID(), connectorItem->connectorSharedID(), connect); } m_undoStack->waitPush(parentCommand, PropChangeDelay); } const QString & SketchWidget::filenameIf() { static QString filename; emit filenameIfSignal(filename); return filename; } void SketchWidget::setItemDropOffset(long id, QPointF offset) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; itemBase->setDropOffset(offset); } Wire * SketchWidget::createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement spec) { Q_UNUSED(fromWire); if (spec == ViewLayer::UnknownPlacement) { spec = wireViewLayerPlacement(connectorItem); } return qobject_cast(addItemAuxTemp(wireModel, spec, viewGeometry, ItemBase::getNextID(), true, m_viewID, true)); } void SketchWidget::prereleaseTempWireForDragging(Wire*) { } void SketchWidget::wireChangedCurveSlot(Wire* wire, const Bezier * oldB, const Bezier * newB, bool triggerFirstTime) { this->clearHoldingSelectItem(); this->m_moveEventCount = 0; // clear this so an extra MoveItemCommand isn't posted ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), oldB, newB, wire->getAutoroutable(), NULL); cwcc->setText("Change wire curvature"); if (!triggerFirstTime) { cwcc->setSkipFirstRedo(); } m_undoStack->push(cwcc); } void SketchWidget::changeWireCurve(long id, const Bezier * bezier, bool autoroutable) { Wire * wire = qobject_cast(findItem(id)); if (wire == NULL) return; wire->changeCurve(bezier); wire->setAutoroutable(autoroutable); } void SketchWidget::changeLegCurve(long id, const QString & connectorID, int index, const Bezier * bezier) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID())); if (connectorItem == NULL) return; connectorItem->changeLegCurve(index, bezier); } void SketchWidget::addLegBendpoint(long id, const QString & connectorID, int index, QPointF p, const class Bezier * bezierLeft, const class Bezier * bezierRight) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID())); if (connectorItem == NULL) return; connectorItem->addLegBendpoint(index, p, bezierLeft, bezierRight); } void SketchWidget::removeLegBendpoint(long id, const QString & connectorID, int index, const class Bezier * bezierCombined) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID())); if (connectorItem == NULL) return; connectorItem->removeLegBendpoint(index, bezierCombined); } void SketchWidget::moveLegBendpoint(long id, const QString & connectorID, int index, QPointF p) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID())); if (connectorItem == NULL) return; connectorItem->moveLegBendpoint(index, p); } void SketchWidget::moveLegBendpoints(bool undoOnly, QUndoCommand * parentCommand) { foreach (ItemBase * itemBase, m_stretchingLegs.uniqueKeys()) { foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) { moveLegBendpointsAux(connectorItem, undoOnly, parentCommand); } } } void SketchWidget::moveLegBendpointsAux(ConnectorItem * connectorItem, bool undoOnly, QUndoCommand * parentCommand) { int index0, index1; QPointF oldPos0, newPos0, oldPos1, newPos1; connectorItem->moveDone(index0, oldPos0, newPos0, index1, oldPos1, newPos1); MoveLegBendpointCommand * mlbc = new MoveLegBendpointCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), index0, oldPos0, newPos0, parentCommand); if (undoOnly) mlbc->setUndoOnly(); else mlbc->setRedoOnly(); if (index0 != index1) { mlbc = new MoveLegBendpointCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), index1, oldPos1, newPos1, parentCommand); if (undoOnly) mlbc->setUndoOnly(); else mlbc->setRedoOnly(); } } bool SketchWidget::curvyWires() { return m_curvyWires; } void SketchWidget::setCurvyWires(bool curvyWires) { m_curvyWires = curvyWires; } bool SketchWidget::curvyWiresIndicated(Qt::KeyboardModifiers modifiers) { if (m_curvyWires) { return ((modifiers & Qt::ControlModifier) == 0); } return ((modifiers & Qt::ControlModifier) != 0); } void SketchWidget::setMoveLock(long id, bool lock) { ItemBase * itemBase = findItem(id); if (itemBase) itemBase->setMoveLock(lock); } void SketchWidget::triggerRotate(ItemBase * itemBase, double degrees) { QList selectedItems = scene()->selectedItems(); setIgnoreSelectionChangeEvents(true); this->clearSelection(); itemBase->setSelected(true); rotateX(degrees, false, itemBase); foreach (QGraphicsItem * item, selectedItems) { item->setSelected(true); } setIgnoreSelectionChangeEvents(false); } void SketchWidget::makeWiresChangeConnectionCommands(const QList & wires, QUndoCommand * parentCommand) { QStringList alreadyList; foreach (Wire * wire, wires) { QList wireConnectorItems; wireConnectorItems << wire->connector0() << wire->connector1(); foreach (ConnectorItem * fromConnectorItem, wireConnectorItems) { foreach(ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { QString already = ((fromConnectorItem->attachedToID() <= toConnectorItem->attachedToID()) ? QString("%1.%2.%3.%4") : QString("%3.%4.%1.%2")) .arg(fromConnectorItem->attachedToID()).arg(fromConnectorItem->connectorSharedID()) .arg(toConnectorItem->attachedToID()).arg(toConnectorItem->connectorSharedID()); if (alreadyList.contains(already)) continue; alreadyList.append(already); extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()), false, parentCommand); } } } } void SketchWidget::changePinLabelsSlot(ItemBase * itemBase, bool singleRow) { itemBase = this->findItem(itemBase->id()); if (itemBase == NULL) return; if (itemBase->viewID() != ViewLayer::SchematicView) return; PaletteItem * paletteItem = qobject_cast(itemBase->layerKinChief()); if (paletteItem == NULL) return; if (qobject_cast(itemBase)) { paletteItem->changePinLabels(singleRow, true); } else if (qobject_cast(itemBase)) { paletteItem->changePinLabels(singleRow, false); } else { bool hasLayout, sip; QStringList labels = paletteItem->sipOrDipOrLabels(hasLayout, sip); if (labels.count() == 0) return; QTransform transform = paletteItem->untransform(); QString svg = PartFactory::makeSchematicSipOrDipOr(labels, hasLayout, sip); paletteItem->resetLayerKin(svg); if (!hasLayout && !sip) { paletteItem->resetConnectors(); } paletteItem->retransform(transform); } } void SketchWidget::changePinLabels(ItemBase * itemBase, bool singleRow) { emit changePinLabelsSignal(itemBase, singleRow); changePinLabelsSlot(itemBase, singleRow); } void SketchWidget::renamePins(ItemBase * itemBase, const QStringList & oldLabels, const QStringList & newLabels, bool singleRow) { QUndoCommand * command = new RenamePinsCommand(this, itemBase->id(), oldLabels, newLabels, singleRow, NULL); command->setText(tr("change pin labels")); m_undoStack->waitPush(command, 10); } void SketchWidget::renamePins(long id, const QStringList & labels, bool singleRow) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; PaletteItem * paletteItem = qobject_cast(itemBase->layerKinChief()); if (paletteItem == NULL) return; paletteItem->renamePins(labels, singleRow); } bool SketchWidget::checkUpdateRatsnest(QList & connectorItems) { foreach (ConnectorItem * ci, m_ratsnestUpdateConnect) { if (ci == NULL) continue; if (connectorItems.contains(ci)) return true; } foreach (ConnectorItem * ci, m_ratsnestUpdateDisconnect) { if (ci == NULL) continue; if (connectorItems.contains(ci)) return true; } return false; } void SketchWidget::getRatsnestColor(QColor & color) { //RatsnestColors::reset(m_viewID); color = RatsnestColors::netColor(m_viewID); } VirtualWire * SketchWidget::makeOneRatsnestWire(ConnectorItem * source, ConnectorItem * dest, bool routed, QColor color, bool force) { if (source->attachedTo() == dest->attachedTo()) { if (source == dest) return NULL; if (source->bus() == dest->bus() && dest->bus() != NULL) { if (!force) return NULL; // don't draw a wire within the same part on the same bus } } long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; makeRatsnestViewGeometry(viewGeometry, source, dest); viewGeometry.setRouted(routed); //if (viewID() == ViewLayer::PCBView) { // source->debugInfo("making rat src"); // dest->debugInfo("making rat dst"); //} // ratsnest only added to one view ItemBase * newItemBase = addItem(m_referenceModel->retrieveModelPart(ModuleIDNames::WireModuleIDName), source->attachedTo()->viewLayerPlacement(), BaseCommand::SingleView, viewGeometry, newID, -1, NULL); VirtualWire * wire = qobject_cast(newItemBase); ConnectorItem * connector0 = wire->connector0(); source->tempConnectTo(connector0, false); connector0->tempConnectTo(source, false); ConnectorItem * connector1 = wire->connector1(); dest->tempConnectTo(connector1, false); connector1->tempConnectTo(dest, false); if (!source->attachedTo()->isVisible() || !dest->attachedTo()->isVisible()) { wire->setVisible(false); } wire->setColor(color, ratsnestOpacity()); wire->setWireWidth(ratsnestWidth(), this, VirtualWire::ShapeWidthExtra + ratsnestWidth()); return wire; } double SketchWidget::ratsnestOpacity() { return m_ratsnestOpacity; } void SketchWidget::setRatsnestOpacity(double opacity) { m_ratsnestOpacity = opacity; } double SketchWidget::ratsnestWidth() { return m_ratsnestWidth; } void SketchWidget::setRatsnestWidth(double width) { m_ratsnestWidth = width; } void SketchWidget::makeRatsnestViewGeometry(ViewGeometry & viewGeometry, ConnectorItem * source, ConnectorItem * dest) { QPointF fromPos = source->sceneAdjustedTerminalPoint(NULL); viewGeometry.setLoc(fromPos); QPointF toPos = dest->sceneAdjustedTerminalPoint(NULL); QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y()); viewGeometry.setLine(line); viewGeometry.setWireFlags(ViewGeometry::RatsnestFlag); } const QString & SketchWidget::traceColor(ViewLayer::ViewLayerPlacement) { return ___emptyString___; } double SketchWidget::getTraceWidth() { return 1; } void SketchWidget::setAnyInRotation() { m_anyInRotation = true; } void SketchWidget::removeWire(Wire * w, QList & ends, QList & done, QUndoCommand * parentCommand) { QList chained; w->collectChained(chained, ends); makeWiresChangeConnectionCommands(chained, parentCommand); foreach (Wire * c, chained) { makeDeleteItemCommand(c, BaseCommand::CrossView, parentCommand); done.append(c); } } void SketchWidget::collectRatsnestSlot(QList & foreignSketchWidgets) { foreignSketchWidgets << this; } void SketchWidget::setGroundFillSeed(long id, const QString & connectorID, bool seed) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID())); if (connectorItem == NULL) return; connectorItem->setGroundFillSeed(seed); } void SketchWidget::resolveTemporary(bool resolve, ItemBase * itemBase) { if (resolve) { m_undoStack->resolveTemporary(); return; } QTimer * timer = new QTimer(); timer->setProperty("temporary", QVariant::fromValue(itemBase)); timer->setSingleShot(true); timer->setInterval(10); connect(timer, SIGNAL(timeout()), this, SLOT(deleteTemporary())); // resolveTemporary is invoked indirectly from the temporary stack item via the itemBase, so defer deletion timer->start(); } void SketchWidget::deleteTemporary() { QObject * s = sender(); if (s == NULL) return; ItemBase * itemBase = s->property("temporary").value(); if (itemBase) { deleteItem(itemBase->id(), true, true, false); } m_undoStack->deleteTemporary(); s->deleteLater(); } QString SketchWidget::checkDroppedModuleID(const QString & moduleID) { return moduleID; } bool SketchWidget::sameElectricalLayer2(ViewLayer::ViewLayerID, ViewLayer::ViewLayerID) { return true; } bool SketchWidget::canConnect(ItemBase * from, ItemBase * to) { if (m_pasting) return true; // no need to check in this case Wire * fromWire = qobject_cast(from); Wire * toWire = qobject_cast(to); if (fromWire != NULL && fromWire->getTrace()) { if (fromWire->isTraceType(getTraceFlag())) { return canConnect(fromWire, to); } else { bool can; emit canConnectSignal(fromWire, to, can); return can; } } if (toWire != NULL && toWire->getTrace()) { if (toWire->isTraceType(getTraceFlag())) { return canConnect(toWire, from); } else { bool can; emit canConnectSignal(toWire, from, can); return can; } } return true; } bool SketchWidget::canConnect(Wire *, ItemBase *) { return true; } void SketchWidget::canConnect(Wire * from, ItemBase * to, bool & connect) { if (!from->isTraceType(getTraceFlag())) return; //from->debugInfo("can connect"); //to->debugInfo("\t"); from = qobject_cast(findItem(from->id())); to = findItem(to->id()); connect = canConnect(from, to); } long SketchWidget::swapStart(SwapThing & swapThing, bool master) { Q_UNUSED(master); long newID = ItemBase::getNextID(swapThing.newModelIndex); ItemBase * itemBase = swapThing.itemBase; if (itemBase->viewID() != m_viewID) { itemBase = findItem(itemBase->id()); if (itemBase == NULL) return newID; } ViewGeometry vg = itemBase->getViewGeometry(); QTransform oldTransform = vg.transform(); bool needsTransform = false; if (!oldTransform.isIdentity()) { // restore identity transform vg.setTransform(QTransform()); needsTransform = true; } new MoveItemCommand(this, itemBase->id(), vg, vg, false, swapThing.parentCommand); // command created for each view newAddItemCommand(BaseCommand::SingleView, NULL, swapThing.newModuleID, swapThing.viewLayerPlacement, vg, newID, true, swapThing.newModelIndex, true, swapThing.parentCommand); if (needsTransform) { QMatrix m; m.setMatrix(oldTransform.m11(), oldTransform.m12(), oldTransform.m21(), oldTransform.m22(), 0, 0); new TransformItemCommand(this, newID, m, m, swapThing.parentCommand); } return newID; } void SketchWidget::setPasting(bool pasting) { m_pasting = pasting; } void SketchWidget::showUnrouted() { // MainWindow::routingStatusLabelMouse uses a different technique for collecting unrouted... // what about multiple boards QList< QList< ConnectorItem *>* > allPartConnectorItems; QHash indexer; collectAllNets(indexer, allPartConnectorItems, false, routeBothSides()); QSet toShow; foreach (QList * connectorItems, allPartConnectorItems) { ConnectorPairHash result; GraphUtils::chooseRatsnestGraph(connectorItems, (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ getTraceFlag(), result); foreach (ConnectorItem * ck, result.uniqueKeys()) { toShow.insert(ck); foreach (ConnectorItem * cv, result.values(ck)) { toShow.insert(cv); } } } QList visited; foreach (ConnectorItem * connectorItem, toShow) { if (connectorItem->isActive() && connectorItem->isVisible() && !connectorItem->hidden() && !connectorItem->layerHidden()) { connectorItem->showEqualPotential(true, visited); } else { connectorItem = connectorItem->getCrossLayerConnectorItem(); if (connectorItem) connectorItem->showEqualPotential(true, visited); } } QString message = tr("Unrouted connections are highlighted in yellow."); if (toShow.count() == 0) message = tr("There are no unrouted connections"); QMessageBox::information(this, tr("Unrouted connections"), tr("%1\n\n" "Note: you can also trigger this display by mousing down on the routing status text in the status bar.").arg(message)); visited.clear(); foreach (ConnectorItem * connectorItem, toShow) { if (connectorItem->isActive() && connectorItem->isVisible() && !connectorItem->hidden() && !connectorItem->layerHidden()) { connectorItem->showEqualPotential(false, visited); } else { connectorItem = connectorItem->getCrossLayerConnectorItem(); if (connectorItem) connectorItem->showEqualPotential(false, visited); } } } void SketchWidget::showEvent(QShowEvent * event) { InfoGraphicsView::showEvent(event); emit showing(this); } void SketchWidget::removeDragWire() { if (scene()->mouseGrabberItem() == m_connectorDragWire) { // probably already ungrabbed by the wire, but just in case m_connectorDragWire->ungrabMouse(); //m_connectorDragWire->debugInfo("ungrabbing mouse 2"); } this->scene()->removeItem(m_connectorDragWire); } void SketchWidget::selectItem(ItemBase * itemBase) { QList itemBases; itemBases << itemBase; selectItems(itemBases); } void SketchWidget::selectItemsWithModuleID(ModelPart * modelPart) { QSet itemBases; foreach (QGraphicsItem * item, scene()->items()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase && itemBase->moduleID() == modelPart->moduleID()) { itemBases.insert(itemBase->layerKinChief()); } } if (itemBases.count() == 0) { QMessageBox::information(NULL, "Not found", tr("Part '%1' not found in sketch").arg(modelPart->title())); return; } selectItems(itemBases.values()); } void SketchWidget::addToSketch(QList & modelParts) { if (modelParts.count() == 0) { modelParts = this->m_referenceModel->allParts(); } QUndoCommand* parentCommand = new QUndoCommand(tr("Add %1 parts").arg(modelParts.count())); stackSelectionState(false, parentCommand); new CleanUpWiresCommand(this, CleanUpWiresCommand::Noop, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); int ix = 0; QList ids; int columns = 50; foreach (ModelPart * modelPart, modelParts) { ViewGeometry viewGeometry; int x = (ix % columns) * 100; int y = (ix / columns) * 100; ix++; viewGeometry.setLoc(QPointF(x, y)); ViewLayer::ViewLayerPlacement viewLayerPlacement; getDroppedItemViewLayerPlacement(modelPart, viewLayerPlacement); long id = ItemBase::getNextID(); newAddItemCommand(BaseCommand::CrossView, modelPart, modelPart->moduleID(), viewLayerPlacement, viewGeometry, id, true, -1, true, parentCommand); ids.append(id); } new PackItemsCommand(this, columns, ids, parentCommand); m_undoStack->waitPush(parentCommand, 10); } void SketchWidget::selectItems(QList startingItemBases) { QSet itemBases; foreach (ItemBase * itemBase, startingItemBases) { if (itemBase) itemBases.insert(itemBase->layerKinChief()); } QSet already; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase) already.insert(itemBase->layerKinChief()); } bool theSame = (itemBases.count() == already.count()); if (theSame) { foreach(ItemBase * itemBase, itemBases) { if (!already.contains(itemBase)) { theSame = false; break; } } } if (theSame) return; int count = 0; ItemBase * theItemBase = NULL; foreach(ItemBase * itemBase, itemBases) { if (itemBase) { theItemBase = itemBase; count++; } } QString message; if (count == 0) { message = tr("Deselect all"); } else if (count == 1) { message = tr("Select %1").arg(theItemBase->instanceTitle()); } else { message = tr("Select %1 items").arg(count); } QUndoCommand * parentCommand = new QUndoCommand(message); stackSelectionState(false, parentCommand); SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand); foreach(ItemBase * itemBase, itemBases) { if (itemBase) { selectItemCommand->addRedo(itemBase->id()); } } scene()->clearSelection(); m_undoStack->push(parentCommand); } QGraphicsItem * SketchWidget::getClickedItem(QList & items) { foreach (QGraphicsItem * gitem, items) { if (gitem->acceptedMouseButtons() != Qt::NoButton) { bool ok = true; emit clickedItemCandidateSignal(gitem, ok); if (ok) { return gitem; } } } return NULL; } void SketchWidget::blockUI(bool block) { m_blockUI = block; } void SketchWidget::viewItemInfo(ItemBase * item) { if (m_blockUI) return; InfoGraphicsView::viewItemInfo(item); } QHash SketchWidget::getAutorouterSettings() { return QHash(); } void SketchWidget::setAutorouterSettings(QHash &) { } void SketchWidget::hidePartLayer(long id, ViewLayer::ViewLayerID viewLayerID, bool hide) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; hidePartLayer(itemBase, viewLayerID, hide); } void SketchWidget::hidePartLayer(ItemBase * itemBase, ViewLayer::ViewLayerID viewLayerID, bool hide) { if (itemBase->viewLayerID() == viewLayerID) { itemBase->setLayerHidden(hide); } else { foreach (ItemBase * lkpi, itemBase->layerKinChief()->layerKin()) { if (lkpi->viewLayerID() == viewLayerID) { lkpi->setLayerHidden(hide); break; } } } } void SketchWidget::cleanupRatsnests(bool doEmit) { cleanupRatsnests(m_ratsnestCacheConnect, true); cleanupRatsnests(m_ratsnestCacheDisconnect, false); if (doEmit) emit cleanupRatsnestsSignal(false); } void SketchWidget::cleanupRatsnests(QList< QPointer > & connectorItems, bool connect) { QList cis; foreach (ConnectorItem * connectorItem, connectorItems) { if (connectorItem) cis << connectorItem; } connectorItems.clear(); QSet set = cis.toSet(); cis.clear(); QList cis2 = set.toList(); ConnectorItem::collectEqualPotential(cis2, true, ViewGeometry::RatsnestFlag); foreach (ConnectorItem * connectorItem, cis2) { ratsnestConnect(connectorItem, connect); } } void SketchWidget::addSubpart(long id, long subpartID, bool doEmit) { ItemBase * super = findItem(id); if (super == NULL) return; ItemBase * sub = findItem(subpartID); if (sub == NULL) return; super->addSubpart(sub); sub->setInstanceTitle("", true); if (doEmit) { emit addSubpartSignal(id, subpartID, false); } } void SketchWidget::getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement & viewLayerPlacement) { emit getDroppedItemViewLayerPlacementSignal(modelPart, viewLayerPlacement); } QList SketchWidget::collectSuperSubs(ItemBase * itemBase) { QList itemBases; if (itemBase->superpart()) { itemBases.append(itemBase->superpart()->layerKinChief()); foreach (ItemBase * subpart, itemBase->superpart()->subparts()) { if (!itemBases.contains(subpart->layerKinChief())) { itemBases.append(subpart->layerKinChief()); } } } else if (itemBase->subparts().count() > 0) { itemBases.append(itemBase->layerKinChief()); foreach (ItemBase * subpart, itemBase->subparts()) { if (!itemBases.contains(subpart->layerKinChief())) { itemBases.append(subpart->layerKinChief()); } } } return itemBases; } void SketchWidget::moveItem(ItemBase * itemBase, double x, double y) { if (itemBase == NULL) return; QPointF p = itemBase->pos(); if (qAbs(x - p.x()) < 0.01 && qAbs(y - p.y()) < 0.01) return; bool alignToGrid = m_alignToGrid; m_alignToGrid = false; moveByArrow(x - p.x(), y - p.y(), NULL); m_movingByArrow = false; checkMoved(false); m_savedItems.clear(); m_savedWires.clear(); m_alignToGrid = alignToGrid; } void SketchWidget::alignItems(Qt::Alignment alignment) { bool rubberBandLegEnabled = false; m_dragBendpointWire = NULL; clearHoldingSelectItem(); m_savedItems.clear(); m_savedWires.clear(); m_moveEventCount = 0; prepMove(NULL, rubberBandLegEnabled, true); QMultiHash unsaved; QSet unsavedSet; foreach (ItemBase * itemBase, m_savedItems) { if (itemBase->itemType() == ModelPart::Wire) { Wire * wire = qobject_cast(itemBase); QList wires; QList ends; wire->collectChained(wires, ends); foreach (Wire * w, wires) { if (!unsavedSet.contains(w)) { QList< QPointer > toList = w->connector0()->connectedToItems(); foreach (ConnectorItem * end, ends) { if (toList.contains(end)) { unsaved.insert(w, end); break; } } unsavedSet.insert(w); } } } } foreach (Wire * w, unsavedSet) m_savedItems.remove(w->id()); // If locked items are included, use them as the basis for finding // the bounding box wherein remaining (unlocked) items will be aligned QList lockedItems = qobject_cast(this->scene())->lockedSelectedItems(); QList boundingItems = lockedItems.count() > 0 ? lockedItems : m_savedItems.values(); if (boundingItems.count() < 1 || m_savedItems.count() < 2) return; int count = 0; double left = std::numeric_limits::max(); double top = std::numeric_limits::max(); double right = std::numeric_limits::min(); double bottom = std::numeric_limits::min(); double hcTotal = 0; double vcTotal = 0; foreach (ItemBase * itemBase, boundingItems) { QRectF r = itemBase->sceneBoundingRect(); if (r.left() < left) left = r.left(); if (r.right() > right) right = r.right(); if (r.top() < top) top = r.top(); if (r.bottom() > bottom) bottom = r.bottom(); count++; QPointF center = r.center(); hcTotal += center.x(); vcTotal += center.y(); } if (count < 1) { DebugDialog::debug("no items for alignment bounding box"); return; } hcTotal /= count; vcTotal /= count; if (m_moveEventCount == 0) { // first time m_moveDisconnectedFromFemale.clear(); foreach (ItemBase * item, m_savedItems) { if (item->itemType() == ModelPart::Wire) continue; //DebugDialog::debug(QString("disconnecting from female %1").arg(item->instanceTitle())); disconnectFromFemale(item, m_savedItems, m_moveDisconnectedFromFemale, false, rubberBandLegEnabled, NULL); } } foreach (ItemBase * itemBase, m_savedItems) { if (itemBase->itemType() == ModelPart::Wire) continue; QRectF r = itemBase->sceneBoundingRect(); QPointF dp; switch (alignment) { case Qt::AlignLeft: dp.setY(0); dp.setX(left - r.left()); break; case Qt::AlignHCenter: dp.setY(0); dp.setX(hcTotal - r.center().x()); break; case Qt::AlignRight: dp.setY(0); dp.setX(right - r.right()); break; case Qt::AlignTop: dp.setX(0); dp.setY(top - r.top()); break; case Qt::AlignVCenter: dp.setX(0); dp.setY(vcTotal - r.center().y()); break; case Qt::AlignBottom: dp.setX(0); dp.setY(bottom - r.bottom()); break; default: dp.setY(0); dp.setX(left - r.left()); break; } itemBase->setPos(itemBase->getViewGeometry().loc() + dp); foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) { connectorItem->stretchBy(dp); } if (m_checkUnder.contains(itemBase)) { findConnectorsUnder(itemBase); } } foreach (Wire * wire, m_savedWires.keys()) { ConnectorItem * ci = m_savedWires.value(wire); wire->simpleConnectedMoved(ci); } foreach (Wire * w, unsaved.keys()) { foreach (ConnectorItem * end, unsaved.values(w)) { w->simpleConnectedMoved(end); m_savedWires.insert(w, end); } } //DebugDialog::debug(QString("done move items %1").arg(QTime::currentTime().msec()) ); if (m_infoView) { foreach (ItemBase * itemBase, m_savedItems) { m_infoView->updateLocation(itemBase->layerKinChief()); } } m_movingByArrow = false; m_moveEventCount++; checkMoved(false); m_savedItems.clear(); m_savedWires.clear(); } void SketchWidget::squashShapes(QPointF scenePos) { if (viewID() == ViewLayer::BreadboardView) return; unsquashShapes(); // topmost connectoritem // topmost wire // topmost shape // topmost ConnectorItem * connectorItem = NULL; Wire * wire = NULL; QList itms = scene()->items(scenePos); if (itms.count() <= 1) return; int ix = 0; for (; ix < itms.count(); ix++) { QGraphicsItem * item = itms.at(ix); connectorItem = dynamic_cast(item); if (connectorItem && connectorItem->acceptHoverEvents()) { ItemBase * itemBase = connectorItem->attachedTo(); if (itemBase->inactive()) continue; if (itemBase->hidden()) continue; if (itemBase->layerHidden()) continue; break; } wire = dynamic_cast(item); if (wire != NULL && wire->acceptHoverEvents() && !wire->inactive() && !wire->hidden() && !wire->layerHidden()) break; } if (ix == 0) { return; } if (wire == NULL && connectorItem == NULL) { int smallest = 0; double smallestArea = std::numeric_limits::max(); for (ix = 0; ix < itms.count(); ix++) { QGraphicsItem * item = itms.at(ix); ItemBase * itemBase = dynamic_cast(item); if (itemBase != NULL) { if (itemBase->hidden() || itemBase->layerHidden() || itemBase->inactive()) continue; QPainterPath painterPath = itemBase->mapToScene(itemBase->selectionShape()); if (painterPath.contains(scenePos)) { break; } double a = itemBase->boundingRect().width() * itemBase->boundingRect().height(); if (a < smallestArea) { smallest = ix; smallestArea = a; } } } if (ix == 0) return; // use the topmost if (ix >= itms.count()) { ix = smallest; } } if (ix == 0) { return; } bool firstTime = true; for (int i = 0; i < ix; i++) { ItemBase * itemBase = dynamic_cast(itms.at(i)); if (itemBase == NULL) continue; if (connectorItem != NULL && connectorItem->parentItem() == itemBase) { if (firstTime) { // topmost contains connectorItem return; } // itembase lower in the list owns the connectorItem break; } firstTime = false; itemBase->setSquashShape(true); m_squashShapes << itemBase; } } void SketchWidget::unsquashShapes() { foreach (ItemBase * itemBase, m_squashShapes) { if (itemBase) itemBase->setSquashShape(false); } m_squashShapes.clear(); } void SketchWidget::contextMenuEvent(QContextMenuEvent *event) { // this event does not occur within mousePressEvent() so squashshapes is not called twice squashShapes(mapToScene(event->pos())); InfoGraphicsView::contextMenuEvent(event); unsquashShapes(); } void SketchWidget::mouseDoubleClickEvent(QMouseEvent *event) { squashShapes(mapToScene(event->pos())); InfoGraphicsView::mouseDoubleClickEvent(event); unsquashShapes(); } bool SketchWidget::viewportEvent(QEvent *event) { if (event->type() == QEvent::ToolTip) { QHelpEvent * helpEvent = static_cast(event); squashShapes(mapToScene(helpEvent->pos())); } bool result = InfoGraphicsView::viewportEvent(event); if (event->type() == QEvent::ToolTip) { unsquashShapes(); } return result; } void SketchWidget::packItems(int columns, const QList & ids, QUndoCommand *parent, bool doEmit) { if (ids.count() < 2) return; QList itemBases; foreach (long id, ids) { ItemBase * itemBase = findItem(id); if (itemBase == NULL) return; itemBases << itemBase; itemBase->saveGeometry(); } QPointF initial = itemBases.at(0)->pos(); double top = initial.y(); QVector heights((ids.count() + columns - 1) / columns, 0); for (int i = 0; i < itemBases.count(); i++) { ItemBase * itemBase = itemBases.at(i); QRectF r = itemBase->sceneBoundingRect(); if (r.height() > heights.at(i / columns)) { heights[i / columns] = r.height(); } } double offset = 10; double left = 0; for (int i = 0; i < itemBases.count(); i++) { if (i % columns == 0) { left = initial.x(); } ItemBase * itemBase = itemBases.at(i); ViewGeometry vg1 = itemBase->getViewGeometry(); ViewGeometry vg2 = vg1; vg2.setLoc(QPointF(left, top)); new MoveItemCommand(this, itemBase->id(), vg1, vg2, true, parent); itemBase->setPos(left, top); left += itemBase->sceneBoundingRect().width() + offset; if (i % columns == columns - 1) { top += heights.at(i / columns) + offset; } } if (doEmit) { emit packItemsSignal(columns, ids, parent, false); } } QColor SketchWidget::gridColor() const { return m_gridColor; } void SketchWidget::setGridColor(QColor color) { m_gridColor = color; } bool SketchWidget::everZoomed() const { return m_everZoomed; } void SketchWidget::setEverZoomed(bool everZoomed) { m_everZoomed = everZoomed; } bool SketchWidget::updateOK(ConnectorItem *, ConnectorItem *) { return true; } void SketchWidget::testConnectors() { static const QString templateModuleID("generic_female_pin_header_1_100mil"); static QRectF templateBoundingRect; static QPointF templateOffset; QSet already; foreach (QGraphicsItem * item, scene()->selectedItems()) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; already.insert(itemBase->layerKinChief()); } if (already.count() == 0) return; ModelPart * templateModelPart = referenceModel()->genFZP(templateModuleID, referenceModel()); if (templateBoundingRect.isNull()) { long newID = ItemBase::getNextID(); ViewGeometry viewGeometry; ItemBase * templateItem = addItemAuxTemp(templateModelPart, ViewLayer::NewTop, viewGeometry, newID, true, viewID(), true); templateBoundingRect = templateItem->sceneBoundingRect(); QPointF templatePos = templateItem->cachedConnectorItems()[0]->sceneAdjustedTerminalPoint(NULL); templateOffset = templatePos - templateBoundingRect.topLeft(); foreach (ItemBase * kin, templateItem->layerKin()) delete kin; delete templateItem; } QUndoCommand * parentCommand = new QUndoCommand(tr("test connectors")); new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand); foreach (ItemBase * itemBase, already) { QRectF sceneBoundingRect = itemBase->sceneBoundingRect(); QPointF center = sceneBoundingRect.center(); foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) { QPointF fromPos = connectorItem->sceneAdjustedTerminalPoint(NULL); QPointF toPos = fromPos; double d[4]; d[0] = qAbs(fromPos.x() - sceneBoundingRect.left()); d[1] = qAbs(fromPos.x() - sceneBoundingRect.right()); d[2] = qAbs(fromPos.y() - sceneBoundingRect.top()); d[3] = qAbs(fromPos.y() - sceneBoundingRect.bottom()); int best = 0; for (int i = 1; i < 4; i++) { if (d[i] < d[best]) best = i; } switch (best) { case 0: // left toPos.setX(sceneBoundingRect.left() - (3 * templateBoundingRect.width())); break; case 1: // right toPos.setX(sceneBoundingRect.right() + (3 * templateBoundingRect.width())); break; case 2: // top toPos.setY(sceneBoundingRect.top() - (3 * templateBoundingRect.height())); break; case 3: // bottom toPos.setY(sceneBoundingRect.bottom() + (3 * templateBoundingRect.height())); break; } long newID = ItemBase::getNextID(); ViewGeometry vg; vg.setLoc(toPos - templateOffset); newAddItemCommand(BaseCommand::CrossView, templateModelPart, templateModelPart->moduleID(), itemBase->viewLayerPlacement(), vg, newID, true, -1, true, parentCommand); long wireID = ItemBase::getNextID(); ViewGeometry vgw; vgw.setLoc(fromPos); QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y()); vgw.setLine(line); vgw.setWireFlags(getTraceFlag()); new AddItemCommand(this, BaseCommand::CrossView, ModuleIDNames::WireModuleIDName, itemBase->viewLayerPlacement(), vgw, wireID, false, -1, parentCommand); new ChangeConnectionCommand(this, BaseCommand::CrossView, itemBase->id(), connectorItem->connectorSharedID(), wireID, "connector0", itemBase->viewLayerPlacement(), true, parentCommand); new ChangeConnectionCommand(this, BaseCommand::CrossView, newID, "connector0", wireID, "connector1", itemBase->viewLayerPlacement(), true, parentCommand); } } new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand); m_undoStack->waitPush(parentCommand, PropChangeDelay); } void SketchWidget::updateWires() { QList already; ViewGeometry::WireFlag traceFlag = getTraceFlag(); foreach (QGraphicsItem * item, scene()->items()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (!wire->isTraceType(traceFlag)) continue; ConnectorItem * from = wire->connector0()->firstConnectedToIsh(); if (from != NULL) wire->connectedMoved(from, wire->connector0(), already); ConnectorItem * to = wire->connector1()->firstConnectedToIsh(); if (to != NULL) wire->connectedMoved(to, wire->connector1(), already); } } void SketchWidget::viewGeometryConversionHack(ViewGeometry &, ModelPart *) { } void SketchWidget::checkForReversedWires() { ViewGeometry::WireFlag traceFlag = getTraceFlag(); QList toReverse; foreach (QGraphicsItem * item, scene()->items()) { Wire * wire = dynamic_cast(item); if (wire == NULL) continue; if (!wire->isTraceType(traceFlag)) continue; ConnectorItem * w0 = wire->connector0(); ConnectorItem* to0 = w0->firstConnectedToIsh(); ConnectorItem * w1 = wire->connector1(); ConnectorItem* to1 = w1->firstConnectedToIsh(); QPointF p = wire->pos(); QLineF line = wire->line(); QPointF p2 = line.p2() + p; QPointF p1 = line.p1() + p; QLineF newLine(p2 - p2, p1 - p2); double totalDistance = 0; double totalReverseDistance = 0; if (to0) { QPointF to0pos = to0->sceneAdjustedTerminalPoint(NULL); QPointF w0pos = w0->sceneAdjustedTerminalPoint(NULL); totalDistance += (to0pos - w0pos).manhattanLength(); QPointF w0revPos = p2 + wire->connector0Rect(newLine).center(); totalReverseDistance += (to0pos - w0revPos).manhattanLength(); } if (to1) { QPointF to1pos = to1->sceneAdjustedTerminalPoint(NULL); QPointF w1pos = w1->sceneAdjustedTerminalPoint(NULL); totalDistance += (to1pos - w1pos).manhattanLength(); QPointF w1revPos = p2 + wire->connector1Rect(newLine).center(); totalReverseDistance += (to1pos - w1revPos).manhattanLength(); } if (totalDistance > totalReverseDistance) { toReverse << wire; wire->debugInfo(QString("reversing d:%1 %2,").arg(totalDistance).arg(totalReverseDistance)); continue; } } Bezier newBezier; foreach (Wire * wire, toReverse) { QPointF p = wire->pos(); QLineF line = wire->line(); QPointF p2 = line.p2() + p; QPointF p1 = line.p1() + p; bool isCurved = wire->isCurved(); if (isCurved) { const Bezier * bezier = wire->curve(); QPointF cp0 = bezier->cp0() + p; QPointF cp1 = bezier->cp1() + p; QPointF ep0 = bezier->endpoint0() + p; QPointF ep1 = bezier->endpoint1() + p; newBezier.set_endpoints(ep1 - p2, ep0 - p2); newBezier.set_cp0(cp1 - p2); newBezier.set_cp1(cp0 - p2); } wire->setLineAnd(QLineF(p2 - p2, p1 - p2), p2, true); wire->setConnector0Rect(); if (isCurved) { wire->changeCurve(&newBezier); } else { wire->update(); } } } fritzing-app-0.9.3b/src/sketch/sketchwidget.h000066400000000000000000001156601272363601300212210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6999 $: $Author: irascibl@gmail.com $: $Date: 2013-04-28 14:14:07 +0200 (So, 28. Apr 2013) $ ********************************************************************/ #ifndef SKETCHWIDGET_H #define SKETCHWIDGET_H #include #include #include #include #include #include #include #include #include #include "../items/paletteitem.h" #include "../referencemodel/referencemodel.h" #include "../model/sketchmodel.h" #include "../viewgeometry.h" #include "infographicsview.h" #include "../viewlayer.h" #include "../utils/misc.h" #include "../commands.h" struct ItemCount { int selCount; int hasLabelCount; int visLabelCount; int itemsCount; int selRotatable; int sel45Rotatable; int selHFlipable; int selVFlipable; int obsoleteCount; int moveLockCount; int wireCount; }; struct SwapThing { bool firstTime; long newID; ItemBase * itemBase; long newModelIndex; QString newModuleID; ViewLayer::ViewLayerPlacement viewLayerPlacement; QList wiresToDelete; QUndoCommand * parentCommand; QHash reconnections; QHash byWire; QHash toConnectorItems; QHash swappedGender; SketchWidget * bbView; QMap propsMap; }; struct RenderThing { bool selectedItems; double printerScale; bool blackOnly; QRectF imageRect; QRectF offsetRect; double dpi; bool renderBlocker; QRectF itemsBoundingRect; QGraphicsItem * board; bool empty; bool hideTerminalPoints; }; class SizeItem : public QObject, public QGraphicsLineItem { Q_OBJECT public: SizeItem(); ~SizeItem(); }; class SketchWidget : public InfoGraphicsView { Q_OBJECT Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor DESIGNABLE true) Q_PROPERTY(double ratsnestWidth READ ratsnestWidth WRITE setRatsnestWidth DESIGNABLE true) Q_PROPERTY(double ratsnestOpacity READ ratsnestOpacity WRITE setRatsnestOpacity DESIGNABLE true) public: SketchWidget(ViewLayer::ViewID, QWidget *parent=0, int size=400, int minSize=300); ~SketchWidget(); void pushCommand(QUndoCommand *, QObject * signalTarget); class WaitPushUndoStack * undoStack(); ItemBase * addItem(ModelPart *, ViewLayer::ViewLayerPlacement, BaseCommand::CrossViewType, const ViewGeometry &, long id, long modelIndex, AddDeleteItemCommand * originatingCommand); ItemBase * addItem(const QString & moduleID, ViewLayer::ViewLayerPlacement, BaseCommand::CrossViewType, const ViewGeometry &, long id, long modelIndex, AddDeleteItemCommand * originatingCommand); void deleteItem(long id, bool deleteModelPart, bool doEmit, bool later); virtual void deleteItem(ItemBase *, bool deleteModelPart, bool doEmit, bool later); void simpleMoveItem(long id, QPointF); void moveItem(long id, ViewGeometry &, bool updateRatsnest); void moveItem(long id, const QPointF & p, bool updateRatsnest); void updateWire(long id, const QString & connectorID, bool updateRatsnest); void rotateItem(long id, double degrees); void transformItem(long id, const QMatrix &); void flipItem(long id, Qt::Orientations orientation); void selectItem(long id, bool state, bool updateInfoView, bool doEmit); void selectItem(ItemBase * itemBase); void selectItems(QList); void selectItemsWithModuleID(ModelPart *); void addToSketch(QList &); void selectDeselectAllCommand(bool state); void changeWire(long fromID, QLineF line, QPointF pos, bool updateConnections, bool updateRatsnest); void changeLeg(long fromID, const QString & connectorID, const QPolygonF &, bool relative, const QString & why); void recalcLeg(long fromID, const QString & connectorID, const QPolygonF &, bool relative, bool active, const QString & why); void rotateLeg(long fromID, const QString & connectorID, const QPolygonF &, bool active); void cut(); void copy(); void setReferenceModel(class ReferenceModel *referenceModel); class ReferenceModel * referenceModel(); void setSketchModel(SketchModel *); void setUndoStack(class WaitPushUndoStack *); void clearSelection(); virtual void loadFromModelParts(QList & modelParts, BaseCommand::CrossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList & newIDs); void changeZ(QHash, double (*pairAccessor)(RealPair *) ); void sendToBack(); void sendBackward(); void bringForward(); void bringToFront(); void alignItems(Qt::Alignment); double fitInWindow(); void rotateX(double degrees, bool rubberBandLegEnabled, ItemBase * originatingItem); void flipX(Qt::Orientations orientation, bool rubberBandLegEnabled); void addBendpoint(ItemBase * lastHoverEnterItem, ConnectorItem * lastHoverEnterConnectorItem, QPointF lastLocation); void flattenCurve(ItemBase * lastHoverEnterItem, ConnectorItem * lastHoverEnterConnectorItem, QPointF lastLocation); void deleteSelected(Wire *, bool minus); PaletteItem *getSelectedPart(); void addViewLayer(ViewLayer *); void setAllLayersVisible(bool visible); void setLayerVisible(ViewLayer * viewLayer, bool visible, bool doChildLayers); void setLayerVisible(ViewLayer::ViewLayerID viewLayerID, bool visible, bool doChildLayers); void setLayerActive(ViewLayer * viewLayer, bool active); void setLayerActive(ViewLayer::ViewLayerID viewLayerID, bool active); bool layerIsVisible(ViewLayer::ViewLayerID); bool layerIsActive(ViewLayer::ViewLayerID); void sortAnyByZ(const QList & items, QList & bases); void mousePressConnectorEvent(ConnectorItem *, QGraphicsSceneMouseEvent *); void setBackground(QColor); void setBackgroundColor(QColor, bool setPref); const QColor& background(); QColor standardBackground(); void setItemMenu(QMenu*); void setWireMenu(QMenu*); virtual void changeConnection(long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, bool doEmit, bool updateConnections); ItemCount calcItemCount(); ViewLayer::ViewID viewID(); void setViewLayerIDs(ViewLayer::ViewLayerID part, ViewLayer::ViewLayerID wire, ViewLayer::ViewLayerID connector, ViewLayer::ViewLayerID ruler, ViewLayer::ViewLayerID note); void stickem(long stickTargetID, long stickSourceID, bool stick); void stickyScoop(ItemBase * stickyOne, bool checkCurrent, CheckStickyCommand *); void setChainDrag(bool); void hoverEnterItem(QGraphicsSceneHoverEvent * event, ItemBase * item); void hoverLeaveItem(QGraphicsSceneHoverEvent * event, ItemBase * item); void hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item); void hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item); void cleanUpWires(bool doEmit, class CleanUpWiresCommand *); void partLabelChanged(ItemBase *, const QString & oldText, const QString &newtext); void noteChanged(ItemBase *, const QString & oldText, const QString &newtext, QSizeF oldSize, QSizeF newSize); void setInfoViewOnHover(bool infoViewOnHover); virtual ItemBase * addItemAux(ModelPart *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, bool doConnectors, ViewLayer::ViewID, bool temporary); ItemBase * addItemAuxTemp(ModelPart *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, bool doConnectors, ViewLayer::ViewID, bool temporary); bool swappingEnabled(ItemBase *); virtual void addViewLayers(); void changeWireColor(long wireId, const QString& color, double opacity); void changeWireWidth(long wireId, double width); void changeWireFlags(long wireId, ViewGeometry::WireFlags wireFlags); void setIgnoreSelectionChangeEvents(bool); void hideConnectors(bool hide); void saveLayerVisibility(); void restoreLayerVisibility(); void updateRoutingStatus(CleanUpWiresCommand*, RoutingStatus &, bool manual); void updateRoutingStatus(RoutingStatus &, bool manual); virtual bool hasAnyNets(); void ensureLayerVisible(ViewLayer::ViewLayerID); const QString &selectedModuleID(); virtual bool canDeleteItem(QGraphicsItem * item, int count); virtual bool canCopyItem(QGraphicsItem * item, int count); const QString & viewName(); void makeDeleteItemCommand(ItemBase * itemBase, BaseCommand::CrossViewType, QUndoCommand * parentCommand); virtual void forwardRoutingStatus(const RoutingStatus &); void collectParts(QList & partList); void movePartLabel(long itemID, QPointF newPos, QPointF newOffset); void updateInfoView(); virtual void setCurrent(bool current); void partLabelMoved(ItemBase *, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset); void rotateFlipPartLabel(ItemBase *, double degrees, Qt::Orientations flipDirection); void rotateFlipPartLabel(long itemID, double degrees, Qt::Orientations flipDirection); void showPartLabels(bool show); void hidePartLabel(ItemBase * item); void noteSizeChanged(ItemBase * itemBase, const QSizeF & oldSize, const QSizeF & newSize); void resizeNote(long itemID, const QSizeF & ); class SelectItemCommand* stackSelectionState(bool pushIt, QUndoCommand * parentCommand); QString renderToSVG(RenderThing &, QGraphicsItem * board, const LayerList &); bool spaceBarIsPressed(); virtual long setUpSwap(SwapThing &, bool master); ConnectorItem * lastHoverEnterConnectorItem(); ItemBase * lastHoverEnterItem(); LayerHash & viewLayers(); virtual void createTrace(Wire*, bool useLastWireColor); virtual void selectAllWires(ViewGeometry::WireFlag); virtual void tidyWires(); const QString & getShortName(); virtual void setClipEnds(class ClipableWire *, bool); void getBendpointWidths(class Wire *, double w, double & w1, double & w2, bool & negativeOffsetRect); virtual bool includeSymbols(); void disconnectAll(); virtual bool canDisconnectAll(); virtual bool ignoreFemale(); virtual ViewLayer::ViewLayerID getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement); ItemBase * findItem(long id); long createWire(ConnectorItem * from, ConnectorItem * to, ViewGeometry::WireFlags, bool dontUpdate, BaseCommand::CrossViewType, QUndoCommand * parentCommand); QList selectAllObsolete(); int selectAllMoveLock(); void setMoveLock(long id, bool lock); bool partLabelsVisible(); void restorePartLabel(long itemID, QDomElement & element); void loadLogoImage(ItemBase *, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName); void loadLogoImage(long itemID, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename); void loadLogoImage(long itemID, const QString & newFilename, bool addName); void setNoteFocus(QGraphicsItem *, bool inFocus); void alignToGrid(bool); bool alignedToGrid(); void showGrid(bool); bool showingGrid(); void setGridSize(const QString &); QString gridSizeText(); void saveZoom(double); double retrieveZoom(); void initGrid(); virtual double defaultGridSizeInches(); void clearPasteOffset(); virtual ViewLayer::ViewLayerPlacement defaultViewLayerPlacement(ModelPart *); void collectAllNets(QHash & indexer, QList< QList* > & allPartConnectorItems, bool includeSingletons, bool bothSides); virtual bool routeBothSides(); virtual void changeLayer(long id, double z, ViewLayer::ViewLayerID viewLayerID); void ratsnestConnect(ConnectorItem * connectorItem, bool connect); void ratsnestConnect(ItemBase *, bool connect); void ratsnestConnect(ConnectorItem * c1, ConnectorItem * c2, bool connect, bool wait); virtual void addDefaultParts(); float getTopZ(); QGraphicsItem * addWatermark(const QString & filename); void copyHeart(QList & bases, bool saveBoundingRects, QByteArray & itemData, QList & modelIndexes); void pasteHeart(QByteArray & itemData, bool seekOutsideConnections); ViewGeometry::WireFlag getTraceFlag(); void changeBus(ItemBase *, bool connec, const QString & oldBus, const QString & newBus, QList &, const QString & message, const QString & oldLayout, const QString & newLayout); const QString & filenameIf(); void setItemDropOffset(long id, QPointF offset); void prepLegBendpointMove(ConnectorItem * from, int index, QPointF oldPos, QPointF newPos, ConnectorItem * to, bool changeConnections); void prepLegCurveChange(ConnectorItem * from, int index, const class Bezier * oldB, const class Bezier * newB, bool triggerFirstTime); void prepLegBendpointChange(ConnectorItem * from, int oldCount, int newCount, int index, QPointF pos, const class Bezier *, const class Bezier *, const class Bezier *, bool triggerFirstTime); void prepLegSelection(ItemBase *); void changeWireCurve(long id, const Bezier *, bool autoroutable); void changeLegCurve(long id, const QString & connectorID, int index, const Bezier *); void addLegBendpoint(long id, const QString & connectorID, int index, QPointF, const class Bezier *, const class Bezier *); void removeLegBendpoint(long id, const QString & connectorID, int index, const class Bezier *); void moveLegBendpoint(long id, const QString & connectorID, int index, QPointF); bool curvyWires(); void setCurvyWires(bool); bool curvyWiresIndicated(Qt::KeyboardModifiers); void triggerRotate(ItemBase *, double degrees); void makeWiresChangeConnectionCommands(const QList & wires, QUndoCommand * parentCommand); void renamePins(ItemBase *, const QStringList & oldLabels, const QStringList & newLabels, bool singleRow); void renamePins(long itemID, const QStringList & labels, bool singleRow); void getRatsnestColor(QColor &); VirtualWire * makeOneRatsnestWire(ConnectorItem * source, ConnectorItem * dest, bool routed, QColor color, bool force); double ratsnestOpacity(); void setRatsnestOpacity(double); double ratsnestWidth(); void setRatsnestWidth(double); void setAnyInRotation(); ConnectorItem * findConnectorItem(ConnectorItem * foreignConnectorItem); void setGroundFillSeed(long id, const QString & connectorID, bool seed); void setWireExtras(long id, QDomElement &); void resolveTemporary(bool, ItemBase *); virtual bool sameElectricalLayer2(ViewLayer::ViewLayerID, ViewLayer::ViewLayerID); void deleteMiddle(QSet & deletedItems, QUndoCommand * parentCommand); void setPasting(bool); void showUnrouted(); QPointF alignOneToGrid(ItemBase * itemBase); void showEvent(QShowEvent * event); void blockUI(bool); void viewItemInfo(ItemBase * item); virtual QHash getAutorouterSettings(); virtual void setAutorouterSettings(QHash &); void hidePartLayer(long id, ViewLayer::ViewLayerID, bool hide); void hidePartLayer(ItemBase *, ViewLayer::ViewLayerID, bool hide); void moveItem(ItemBase *, double x, double y); QColor gridColor() const; void setGridColor(QColor); bool everZoomed() const; void setEverZoomed(bool); void testConnectors(); void updateWires(); void checkForReversedWires(); protected: void dragEnterEvent(QDragEnterEvent *); bool dragEnterEventAux(QDragEnterEvent *); virtual bool canDropModelPart(ModelPart *); void dragLeaveEvent(QDragLeaveEvent *); void dragMoveEvent(QDragMoveEvent *); void dropEvent(QDropEvent *); virtual void mousePressEvent(QMouseEvent *); void mouseMoveEvent(QMouseEvent *); void mouseReleaseEvent(QMouseEvent *); void mouseDoubleClickEvent (QMouseEvent *); void contextMenuEvent(QContextMenuEvent *); bool viewportEvent(QEvent *); void paintEvent(QPaintEvent *); virtual PaletteItem* addPartItem(ModelPart * , ViewLayer::ViewLayerPlacement, PaletteItem *, bool doConnectors, bool & ok, ViewLayer::ViewID, bool temporary); void clearHoldingSelectItem(); bool startZChange(QList & bases); void continueZChange(QList & bases, int start, int end, bool (*test)(int current, int start), int inc, const QString & text); void continueZChangeMax(QList & bases, int start, int end, bool (*test)(int current, int start), int inc, const QString & text); void continueZChangeAux(QList & bases, const QString & text); virtual ViewLayer::ViewLayerID getDragWireViewLayerID(ConnectorItem *); ViewLayer::ViewLayerID getPartViewLayerID(); ViewLayer::ViewLayerID getRulerViewLayerID(); ViewLayer::ViewLayerID getConnectorViewLayerID(); virtual ViewLayer::ViewLayerID getLabelViewLayerID(ItemBase *); ViewLayer::ViewLayerID getNoteViewLayerID(); void dragMoveHighlightConnector(QPoint eventPos); void addToScene(ItemBase * item, ViewLayer::ViewLayerID viewLayerID); ConnectorItem * findConnectorItem(ItemBase * item, const QString & connectorID, ViewLayer::ViewLayerPlacement); bool checkMoved(bool wait); void changeConnectionAux(long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, bool updateConnections); void cutDeleteAux(QString undoStackMessage, bool minus, Wire * wire); void deleteAux(QSet & deletedItems, QUndoCommand * parentCommand, bool doPush); ChangeConnectionCommand * extendChangeConnectionCommand(BaseCommand::CrossViewType, long fromID, const QString & fromConnectorID, long toID, const QString & toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, QUndoCommand * parent); ChangeConnectionCommand * extendChangeConnectionCommand(BaseCommand::CrossViewType, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, ViewLayer::ViewLayerPlacement, bool connect, QUndoCommand * parentCommand); void keyPressEvent(QKeyEvent *); void keyReleaseEvent(QKeyEvent *); void clearTemporaries(); void dragWireChanged(class Wire* wire, ConnectorItem * from, ConnectorItem * to); void dragRatsnestChanged(); void killDroppingItem(); ViewLayer::ViewLayerID getViewLayerID(ModelPart *, ViewLayer::ViewID, ViewLayer::ViewLayerPlacement); ItemBase * overSticky(ItemBase *); virtual void setNewPartVisible(ItemBase *); virtual bool collectFemaleConnectees(ItemBase *, QSet &); virtual bool checkUnder(); virtual void findConnectorsUnder(ItemBase * item); bool currentlyInfoviewed(ItemBase *item); void resizeEvent(QResizeEvent *); void addViewLayersAux(const LayerList &, const LayerList & layersFromBelow, float startZ = 1.5); virtual bool disconnectFromFemale(ItemBase * item, QHash & savedItems, ConnectorPairHash &, bool doCommand, bool rubberBandLegEnabled, QUndoCommand * parentCommand); void clearDragWireTempCommand(); bool draggingWireEnd(); void moveItems(QPoint globalPos, bool checkAutoScroll, bool rubberBandLegEnabled); virtual ViewLayer::ViewLayerID multiLayerGetViewLayerID(ModelPart * modelPart, ViewLayer::ViewID, ViewLayer::ViewLayerPlacement, LayerList &); virtual BaseCommand::CrossViewType wireSplitCrossView(); virtual bool canChainMultiple(); virtual bool canChainWire(Wire *); virtual bool canDragWire(Wire *); virtual bool canCreateWire(Wire * dragWire, ConnectorItem * from, ConnectorItem * to); //virtual bool modifyNewWireConnections(Wire * dragWire, ConnectorItem * fromOnWire, ConnectorItem * from, ConnectorItem * to, QUndoCommand * parentCommand); virtual void setUpColor(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Wire * wire, QUndoCommand * parentCommand); void setupAutoscroll(bool moving); void turnOffAutoscroll(); bool checkAutoscroll(QPoint globalPos); virtual void setWireVisible(Wire *); bool matchesLayer(ModelPart * modelPart); QByteArray removeOutsideConnections(const QByteArray & itemData, QList & modelIndexes); void addWireExtras(long newID, QDomElement & view, QUndoCommand * parentCommand); virtual const QString & hoverEnterWireConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item); virtual const QString & hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item); void partLabelChangedAux(ItemBase * pitem,const QString & oldText, const QString &newText); void drawBackground( QPainter * painter, const QRectF & rect ); void handleConnect(QDomElement & connect, ModelPart *, const QString & fromConnectorID, ViewLayer::ViewLayerID, QStringList & alreadyConnected, QHash & newItems, QUndoCommand * parentCommand, bool seekOutsideConnections); void setUpSwapReconnect(SwapThing &, ItemBase * itemBase, long newID, bool master); void makeSwapWire(SketchWidget *, ItemBase *, long newID, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Connector * newConnector, QUndoCommand * parentCommand); bool swappedGender(ConnectorItem * originalConnectorItem, Connector * newConnector); void setLastPaletteItemSelected(PaletteItem * paletteItem); void setLastPaletteItemSelectedIf(ItemBase * itemBase); void prepDragBendpoint(Wire *, QPoint eventPos, bool dragCurve); void prepDragWire(Wire *); void clickBackground(QMouseEvent *); void categorizeDragWires(QSet & wires, QList & freeWires); void categorizeDragLegs(bool rubberBandLegEnabled); void prepMove(ItemBase * originatingItem, bool rubberBandLegEnabled, bool includeRatsnest); void initBackgroundColor(); QPointF calcNewLoc(ItemBase * moveBase, ItemBase * detachFrom); long findPartOrWire(long itemID); AddItemCommand * newAddItemCommand(BaseCommand::CrossViewType crossViewType, ModelPart *, QString moduleID, ViewLayer::ViewLayerPlacement, ViewGeometry & viewGeometry, qint64 id, bool updateInfoView, long modelIndex, bool addSubparts, QUndoCommand *parent); int selectAllItems(QSet & itemBases, const QString & msg); bool moveByArrow(double dx, double dy, QKeyEvent * ); double gridSizeInches(); virtual bool canAlignToTopLeft(ItemBase *); virtual bool canAlignToCenter(ItemBase *); virtual void findAlignmentAnchor(ItemBase * originatingItem, QHash & savedItems, QHash & savedWires); void alignLoc(QPointF & loc, const QPointF startPoint, const QPointF newLoc, const QPointF originalLoc); void copyAux(QList & bases, bool saveBoundingRects); void copyDrop(); void dropItemEvent(QDropEvent *event); virtual QString checkDroppedModuleID(const QString & moduleID); QString makeWireSVG(Wire * wire, QPointF offset, double dpi, double printerscale, bool blackOnly); QString makeWireSVGAux(Wire * wire, double width, const QString & color, QPointF offset, double dpi, double printerScale, bool blackOnly, bool dashed); QString makeMoveSVG(double printerScale, double dpi, QPointF & offset); void prepDeleteProps(ItemBase * itemBase, long id, const QString & newModuleID, QMap & propsMap, QUndoCommand * parentCommand); void prepDeleteOtherProps(ItemBase * itemBase, long id, const QString & newModuleID, QMap & propsMap, QUndoCommand * parentCommand); virtual ViewLayer::ViewLayerPlacement getViewLayerPlacement(ModelPart *, QDomElement & instance, QDomElement & view, ViewGeometry &); virtual ViewLayer::ViewLayerPlacement wireViewLayerPlacement(ConnectorItem *); virtual bool resizingJumperItemPress(ItemBase *); virtual bool resizingJumperItemRelease(); bool resizingBoardPress(ItemBase *); bool resizingBoardRelease(); void resizeBoard(); void resizeWithHandle(ItemBase * itemBase, double mmW, double mmH); virtual bool acceptsTrace(const ViewGeometry &); virtual ItemBase * placePartDroppedInOtherView(ModelPart *, ViewLayer::ViewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin); void showPartLabelsAux(bool show, QList & itemBases); virtual void extraRenderSvgStep(ItemBase *, QPointF offset, double dpi, double printerScale, QString & outputSvg); virtual ViewLayer::ViewLayerPlacement createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to); virtual Wire * createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement); virtual void prereleaseTempWireForDragging(Wire*); void checkFit(ModelPart * newModelPart, ItemBase * itemBase, long newID, QHash & found, QList & notFound, QHash & m2f, QHash & byWire, QHash & legs, QHash & formerLegs, QUndoCommand * parentCommand); void checkFitAux(ItemBase * tempItemBase, ItemBase * itemBase, long newID, QHash & found, QList & notFound, QHash & m2f, QHash & byWire, QHash & legs, QHash & formerLegs, QUndoCommand * parentCommand); void changeLegAux(long fromID, const QString & fromConnectorID, const QPolygonF &, bool reset, bool relative, bool active, const QString & why); void moveLegBendpoints(bool undoOnly, QUndoCommand * parentCommand); void moveLegBendpointsAux(ConnectorItem * connectorItem, bool undoOnly, QUndoCommand * parentCommand); virtual void rotatePartLabels(double degrees, QTransform &, QPointF center, QUndoCommand * parentCommand); bool checkUpdateRatsnest(QList & connectorItems); void makeRatsnestViewGeometry(ViewGeometry & viewGeometry, ConnectorItem * source, ConnectorItem * dest); virtual double getTraceWidth(); virtual const QString & traceColor(ViewLayer::ViewLayerPlacement); void createTrace(Wire * fromWire, const QString & commandString, ViewGeometry::WireFlag, bool useLastWireColor); bool createOneTrace(Wire * wire, ViewGeometry::WireFlag flag, bool allowAny, QList & done, bool useLastWireColor, QUndoCommand * parentCommand); void removeWire(Wire * w, QList & ends, QList & done, QUndoCommand * parentCommand); void selectAllWiresFrom(ViewGeometry::WireFlag flag, QList & items); bool canConnect(ItemBase * from, ItemBase * to); virtual bool canConnect(Wire * from, ItemBase * to); void removeDragWire(); QGraphicsItem * getClickedItem(QList & items); void cleanupRatsnests(QList< QPointer > & connectorItems, bool connect); void rotateWire(Wire *, QTransform & rotation, QPointF center, bool undoOnly, QUndoCommand * parentCommand); QString renderToSVG(RenderThing &, const LayerList &); QString renderToSVG(RenderThing &, QList & itemsAndLabels); QList collectSuperSubs(ItemBase *); void squashShapes(QPointF scenePos); void unsquashShapes(); virtual bool updateOK(ConnectorItem *, ConnectorItem *); virtual void viewGeometryConversionHack(ViewGeometry &, ModelPart *); protected: static bool lessThan(int a, int b); static bool greaterThan(int a, int b); signals: void itemAddedSignal(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, SketchWidget * dropOrigin); void itemDeletedSignal(long id); void clearSelectionSignal(); void itemSelectedSignal(long id, bool state); void itemMovedSignal(ItemBase *); void wireDisconnectedSignal(long fromID, QString fromConnectorID); void wireConnectedSignal(long fromID, QString fromConnectorID, long toID, QString toConnectorID); void changeConnectionSignal(long fromID, QString fromConnectorID, long toID, QString toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, bool updateConnections); void copyBoundingRectsSignal(QHash &); void cleanUpWiresSignal(CleanUpWiresCommand *); void selectionChangedSignal(); void resizeSignal(); void dropSignal(const QPoint &pos); void routingStatusSignal(SketchWidget *, const RoutingStatus &); void movingSignal(SketchWidget *, QUndoCommand * parentCommand); void selectAllItemsSignal(bool state, bool doEmit); void checkStickySignal(long id, bool doEmit, bool checkCurrent, CheckStickyCommand *); void disconnectAllSignal(QList, QHash & itemsToDelete, QUndoCommand * parentCommand); void setResistanceSignal(long itemID, QString resistance, QString pinSpacing, bool doEmit); void setPropSignal(long itemID, const QString & prop, const QString & value, bool doRedraw, bool doEmit); void setInstanceTitleSignal(long id, const QString & oldTitle, const QString & newTitle, bool isUndoable, bool doEmit); void statusMessageSignal(QString, int timeout); void showLabelFirstTimeSignal(long itemID, bool show, bool doEmit); void dropPasteSignal(SketchWidget *); void changeBoardLayersSignal(int, bool doEmit); void deleteTracesSignal(QSet & deletedItems, QHash & otherDeletedItems, QList & deletedIDs, bool isForeign, QUndoCommand * parentCommand); void makeDeleteItemCommandPrepSignal(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand); void makeDeleteItemCommandFinalSignal(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand); void cursorLocationSignal(double xinches, double yinches, double width=0.0, double height=0.0); void ratsnestConnectSignal(long id, const QString & connectorID, bool connect, bool doEmit); void updatePartLabelInstanceTitleSignal(long itemID); void filenameIfSignal(QString & filename); void collectRatsnestSignal(QList & foreignSketchWidgets); void removeRatsnestSignal(QList & cutSet, QUndoCommand * parentCommand); void updateLayerMenuSignal(); void swapBoardImageSignal(SketchWidget * sketchWidget, ItemBase * itemBase, const QString & filename, const QString & moduleID, bool addName); void canConnectSignal(Wire * from, ItemBase * to, bool & connect); void swapStartSignal(SwapThing & swapThing, bool master); void showing(SketchWidget *); void clickedItemCandidateSignal(QGraphicsItem *, bool & ok); void resizedSignal(ItemBase *); void cleanupRatsnestsSignal(bool doEmit); void addSubpartSignal(long id, long subpartID, bool doEmit); void getDroppedItemViewLayerPlacementSignal(ModelPart * modelPart, ViewLayer::ViewLayerPlacement &); void packItemsSignal(int columns, const QList & ids, QUndoCommand *parent, bool doEmit); protected slots: void itemAddedSlot(ModelPart *, ItemBase *, ViewLayer::ViewLayerPlacement, const ViewGeometry &, long id, SketchWidget * dropOrigin); void itemDeletedSlot(long id); void clearSelectionSlot(); void itemSelectedSlot(long id, bool state); void selectionChangedSlot(); void wireChangedSlot(class Wire*, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, ConnectorItem * from, ConnectorItem * to); void wireChangedCurveSlot(class Wire*, const Bezier * oldB, const Bezier * newB, bool triggerFirstTime); virtual void wireSplitSlot(class Wire*, QPointF newPos, QPointF oldPos, const QLineF & oldLine); void wireJoinSlot(class Wire*, ConnectorItem * clickedConnectorItem); void toggleLayerVisibility(); void wireConnectedSlot(long fromID, QString fromConnectorID, long toID, QString toConnectorID); void wireDisconnectedSlot(long fromID, QString fromConnectorID); void changeConnectionSlot(long fromID, QString fromConnectorID, long toID, QString toConnectorID, ViewLayer::ViewLayerPlacement, bool connect, bool updateConnections); void restartPasteCount(); void dragIsDoneSlot(class ItemDrag *); void statusMessage(QString message, int timeout = 0); void cleanUpWiresSlot(CleanUpWiresCommand *); void updateInfoViewSlot(); void spaceBarIsPressedSlot(bool); void autoScrollTimeout(); void dragAutoScrollTimeout(); void moveAutoScrollTimeout(); void rememberSticky(long id, QUndoCommand * parentCommand); void rememberSticky(ItemBase *, QUndoCommand * parentCommand); void copyBoundingRectsSlot(QHash &); void deleteRatsnest(Wire *, QUndoCommand * parentCommand); void deleteTracesSlot(QSet & deletedItems, QHash & otherDeletedItems, QList & deletedIDs, bool isForeign, QUndoCommand * parentCommand); void vScrollToZero(); void arrowTimerTimeout(); void makeDeleteItemCommandPrepSlot(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand); void makeDeleteItemCommandFinalSlot(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand); void updatePartLabelInstanceTitleSlot(long itemID); void changePinLabelsSlot(ItemBase * itemBase, bool singleRow); void changePinLabels(ItemBase *, bool singleRow); void collectRatsnestSlot(QList & foreignSketchWidgets); void removeRatsnestSlot(QList & cutSet, QUndoCommand * parentCommand); void deleteTemporary(); void canConnect(Wire * from, ItemBase * to, bool & connect); long swapStart(SwapThing & swapThing, bool master); virtual void getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement &); public slots: void changeWireColor(const QString newColor); void changeWireWidthMils(const QString newWidth); void selectAllItems(bool state, bool doEmit); void setNoteText(long itemID, const QString & newText); void setInstanceTitle(long id, const QString & oldTitle, const QString & newTitle, bool isUndoable, bool doEmit); void incInstanceTitle(long id); void showPartLabel(long id, bool showIt); void checkSticky(long id, bool doEmit, bool checkCurrent, CheckStickyCommand *); virtual ItemBase * resizeBoard(long id, double w, double h); void resizeJumperItem(long id, QPointF pos, QPointF c0, QPointF c1); void disconnectAllSlot(QList, QHash & itemsToDelete, QUndoCommand * parentCommand); void setResistance(long itemID, QString resistance, QString pinSpacing, bool doEmit); void setResistance(QString resistance, QString pinSpacing); void setProp(long itemID, const QString & prop, const QString & value, bool redraw, bool doEmit); virtual void setProp(ItemBase *, const QString & propName, const QString & translatedPropName, const QString & oldValue, const QString & newValue, bool redraw); void setHoleSize(ItemBase *, const QString & propName, const QString & translatedPropName, const QString & oldValue, const QString & newValue, QRectF & oldRect, QRectF & newRect, bool redraw); virtual void showLabelFirstTime(long itemID, bool show, bool doEmit); void resizeBoard(double w, double h, bool doEmit); virtual void changeBoardLayers(int layers, bool doEmit); void updateConnectors(); void ratsnestConnect(long id, const QString & connectorID, bool connect, bool doEmit); void cleanupRatsnests(bool doEmit); void addSubpart(long id, long subpartid, bool doEmit); void packItems(int columns, const QList & ids, QUndoCommand *parent, bool doEmit); protected: enum StatusConnectStatus { StatusConnectNotTried, StatusConnectSucceeded, StatusConnectFailed }; protected: QPointer m_referenceModel; QPointer m_sketchModel; ViewLayer::ViewID m_viewID; class WaitPushUndoStack * m_undoStack; class SelectItemCommand * m_holdingSelectItemCommand; class SelectItemCommand * m_tempDragWireCommand; LayerHash m_viewLayers; QHash m_viewLayerVisibility; QPointer m_connectorDragWire; QPointer m_bendpointWire; ViewGeometry m_bendpointVG; QPointer m_connectorDragConnector; bool m_droppingWire; QPointF m_droppingOffset; QPointer m_droppingItem; int m_moveEventCount; //QList m_lastSelected; hack for 4.5.something ViewLayer::ViewLayerID m_wireViewLayerID; ViewLayer::ViewLayerID m_partViewLayerID; ViewLayer::ViewLayerID m_rulerViewLayerID; ViewLayer::ViewLayerID m_connectorViewLayerID; ViewLayer::ViewLayerID m_noteViewLayerID; QList m_temporaries; bool m_chainDrag; QPointF m_mousePressScenePos; QPointF m_mousePressGlobalPos; QTimer m_autoScrollTimer; volatile int m_autoScrollX; volatile int m_autoScrollY; volatile int m_autoScrollCount; QPoint m_globalPos; QPointer m_lastPaletteItemSelected; int m_pasteCount; QPointF m_pasteOffset; // Part Menu QMenu *m_itemMenu; QMenu *m_wireMenu; bool m_infoViewOnHover; QHash m_savedItems; QHash m_savedWires; QList m_additionalSavedItems; int m_ignoreSelectionChangeEvents; bool m_current; QString m_lastColorSelected; ConnectorPairHash m_moveDisconnectedFromFemale; bool m_spaceBarIsPressed; bool m_spaceBarWasPressed; QPointer m_lastHoverEnterConnectorItem; QPointer m_lastHoverEnterItem; QString m_shortName; QPointer m_dragBendpointWire; bool m_dragCurve; QPoint m_dragBendpointPos; StatusConnectStatus m_statusConnectState; QList m_inFocus; QString m_viewName; bool m_movingByArrow; double m_arrowTotalX; double m_arrowTotalY; bool m_movingByMouse; bool m_alignToGrid; bool m_showGrid; double m_gridSizeInches; QString m_gridSizeText; QPointer m_alignmentItem; QPointer m_originatingItem; QPointF m_alignmentStartPoint; double m_zoom; bool m_draggingBendpoint; QPointer m_sizeItem; int m_autoScrollThreshold; bool m_clearSceneRect; QPointer m_moveReferenceItem; QPointer m_movingSVGRenderer; QPointF m_movingSVGOffset; QPointer m_movingItem; QList< QPointer > m_ratsnestUpdateDisconnect; QList< QPointer > m_ratsnestUpdateConnect; QList< QPointer > m_ratsnestCacheDisconnect; QList< QPointer > m_ratsnestCacheConnect; QList m_checkUnder; bool m_addDefaultParts; QPointer m_addedDefaultPart; float m_z; QTimer m_arrowTimer; bool m_middleMouseIsPressed; QMultiHash m_stretchingLegs; bool m_curvyWires; bool m_rubberBandLegWasEnabled; RoutingStatus m_routingStatus; bool m_anyInRotation; bool m_pasting; QPointer m_resizingBoard; QList< QPointer > m_squashShapes; QColor m_gridColor; bool m_everZoomed; double m_ratsnestOpacity; double m_ratsnestWidth; public: static ViewLayer::ViewLayerID defaultConnectorLayer(ViewLayer::ViewID viewId); static const int PropChangeDelay; static bool m_blockUI; protected: static const int MoveAutoScrollThreshold; static const int DragAutoScrollThreshold; }; #endif fritzing-app-0.9.3b/src/sketch/welcomeview.cpp000066400000000000000000001115121272363601300214050ustar00rootroot00000000000000/********************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "welcomeview.h" #include "../debugdialog.h" #include "../help/tipsandtricks.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////// static const int TitleRole = Qt::UserRole; static const int IntroRole = Qt::UserRole + 1; static const int DateRole = Qt::UserRole + 2; static const int AuthorRole = Qt::UserRole + 3; static const int IconRole = Qt::UserRole + 4; static const int RefRole = Qt::UserRole + 5; static const int ImageSpace = 65; static const int TopSpace = 5; QString WelcomeView::m_activeHeaderLabelColor = "#333"; QString WelcomeView::m_inactiveHeaderLabelColor = "#b1b1b1"; /////////////////////////////////////////////////////////////////////////////// void zeroMargin(QLayout * layout) { layout->setMargin(0); layout->setSpacing(0); } QString makeUrlText(const QString & url, const QString & urlText, const QString & color) { return QString("%2").arg(url).arg(urlText).arg(color); } QString hackColor(QString oldText, const QString & color) { QRegExp colorFinder("color:(#[^;]*);"); int ix = oldText.indexOf(colorFinder); if (ix >= 0) { oldText.replace(colorFinder.cap(1), color); } return oldText; } int pixelSize(const QString & sizeString) { // assume all sizes are of the form Npx otherwise return -1 QString temp = sizeString; temp.remove(" "); if (temp.contains("px")) { temp.remove("px"); bool ok; int ps = temp.toInt(&ok); if (ok) return ps; } return -1; } QString cleanData(const QString & data) { static QRegExp ListItemMatcher("
  • .*
  • "); ListItemMatcher.setMinimal(true); // equivalent of lazy matcher QDomDocument doc; QStringList listItems; int pos = 0; while (pos < data.count()) { int ix = data.indexOf(ListItemMatcher, pos); if (ix < 0) break; QString listItem = ListItemMatcher.cap(0); //DebugDialog::debug("ListItem " + listItem); if (doc.setContent(listItem)) { listItems << listItem; } pos += listItem.count(); } return listItems.join(""); } //////////////////////////////////////////////////////////////////////////////// BlogListWidget::BlogListWidget(QWidget * parent) : QListWidget(parent) { connect(this, SIGNAL(itemEntered(QListWidgetItem *)), this, SLOT(itemEnteredSlot(QListWidgetItem *))); } BlogListWidget::~BlogListWidget() { } QStringList & BlogListWidget::imageRequestList() { return m_imageRequestList; } /* blogEntry Title text properties color, fontfamily, fontsize*/ QColor BlogListWidget::titleTextColor() const { return m_titleTextColor; } void BlogListWidget::setTitleTextColor(QColor color) { m_titleTextColor = color; } QString BlogListWidget::titleTextFontFamily() const { return m_titleTextFontFamily; } void BlogListWidget::setTitleTextFontFamily(QString family) { m_titleTextFontFamily = family; } QString BlogListWidget::titleTextFontSize() const { return m_titleTextFontSize; } void BlogListWidget::setTitleTextFontSize(QString size) { m_titleTextFontSize = size; } QString BlogListWidget::titleTextExtraLeading() const { return m_titleTextExtraLeading; } void BlogListWidget::setTitleTextExtraLeading(QString leading) { m_titleTextExtraLeading = leading; } /* blogEntry intro text properties color, fontfamily, fontsize*/ QColor BlogListWidget::introTextColor() const { return m_introTextColor; } void BlogListWidget::setIntroTextColor(QColor color) { m_introTextColor = color; } QString BlogListWidget::introTextFontFamily() const { return m_introTextFontFamily; } void BlogListWidget::setIntroTextFontFamily(QString family) { m_introTextFontFamily = family; } QString BlogListWidget::introTextFontSize() const { return m_introTextFontSize; } void BlogListWidget::setIntroTextFontSize(QString size) { m_introTextFontSize = size; } QString BlogListWidget::introTextExtraLeading() const { return m_introTextExtraLeading; } void BlogListWidget::setIntroTextExtraLeading(QString leading) { m_introTextExtraLeading = leading; } /* blogEntry Date text properties color, fontfamily, fontsize*/ QColor BlogListWidget::dateTextColor() const { return m_dateTextColor; } void BlogListWidget::setDateTextColor(QColor color) { m_dateTextColor = color; } QString BlogListWidget::dateTextFontFamily() const { return m_dateTextFontFamily; } void BlogListWidget::setDateTextFontFamily(QString family) { m_dateTextFontFamily = family; } QString BlogListWidget::dateTextFontSize() const { return m_dateTextFontSize; } void BlogListWidget::setDateTextFontSize(QString size) { m_dateTextFontSize = size; } void BlogListWidget::itemEnteredSlot(QListWidgetItem * item) { QString url = item->data(RefRole).toString(); bool arrow = (url.isEmpty()) || (url == "nop"); setCursor(arrow ? Qt::ArrowCursor : Qt::PointingHandCursor); } //////////////////////////////////////////////////////////////////////////////// BlogListDelegate::BlogListDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } BlogListDelegate::~BlogListDelegate() { } void BlogListDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { BlogListWidget * listWidget = qobject_cast(this->parent()); if (listWidget == NULL) return; QStyle * style = listWidget->style(); if (style == NULL) return; painter->save(); QFont itemFont(painter->font()); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, listWidget); QPixmap pixmap = qvariant_cast(index.data(IconRole)); QString title = index.data(TitleRole).toString(); QString date = index.data(DateRole).toString(); QString author = index.data(AuthorRole).toString(); QString intro = index.data(IntroRole).toString(); // QRect rect; int imageSpace = ImageSpace + 10; // TITLE painter->setPen(listWidget->titleTextColor()); QFont titleFont(listWidget->titleTextFontFamily()); titleFont.setPixelSize(pixelSize(listWidget->titleTextFontSize())); painter->setFont(titleFont); QRect rect = option.rect.adjusted(imageSpace, TopSpace, 0, 0); style->drawItemText(painter, rect, Qt::AlignLeft, option.palette, true, title); QFontMetrics titleFontMetrics(titleFont); // INTRO painter->setPen(listWidget->introTextColor()); QFont introFont(listWidget->introTextFontFamily()); introFont.setPixelSize(pixelSize(listWidget->introTextFontSize())); painter->setFont(introFont); rect = option.rect.adjusted(imageSpace, TopSpace + titleFontMetrics.lineSpacing() + pixelSize(listWidget->titleTextExtraLeading()) , 0, 0); style->drawItemText(painter, rect, Qt::AlignLeft, option.palette, true, intro); QFontMetrics introFontMetrics(introFont); // DATE painter->setPen(listWidget->dateTextColor()); QFont font(listWidget->dateTextFontFamily()); font.setPixelSize(pixelSize(listWidget->dateTextFontSize())); painter->setFont(font); rect = option.rect.adjusted(imageSpace, TopSpace + titleFontMetrics.lineSpacing() + introFontMetrics.lineSpacing() + pixelSize(listWidget->introTextExtraLeading()), 0, 0); style->drawItemText(painter, rect, Qt::AlignLeft, option.palette, true, date); QFontMetrics dateTextFontMetrics(font); // AUTHOR QRect textRect = style->itemTextRect(dateTextFontMetrics, option.rect, Qt::AlignLeft, true, date); rect = option.rect.adjusted(imageSpace + textRect.width() + 7, TopSpace + titleFontMetrics.lineSpacing() + introFontMetrics.lineSpacing() + pixelSize(listWidget->introTextExtraLeading()), 0, 0); style->drawItemText(painter, rect, Qt::AlignLeft, option.palette, true, author); if (!pixmap.isNull()) { //ic.paint(painter, option.rect, Qt::AlignVCenter|Qt::AlignLeft); style->drawItemPixmap(painter, option.rect.adjusted(0, TopSpace, 0, -TopSpace), Qt::AlignLeft, pixmap); } painter->restore(); } QSize BlogListDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const { return QSize(100, ImageSpace); // very dumb value } ////////////////////////////////////// WelcomeView::WelcomeView(QWidget * parent) : QFrame(parent) { this->setObjectName("welcomeView"); m_tip = NULL; setAcceptDrops(true); initLayout(); connect(this, SIGNAL(newSketch()), this->window(), SLOT(createNewSketch())); connect(this, SIGNAL(openSketch()), this->window(), SLOT(mainLoad())); connect(this, SIGNAL(recentSketch(const QString &, const QString &)), this->window(), SLOT(openRecentOrExampleFile(const QString &, const QString &))); // TODO: blog network calls should only happen once, not for each window? QNetworkAccessManager * manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(gotBlogSnippet(QNetworkReply *))); manager->get(QNetworkRequest(QUrl("http://blog.fritzing.org/recent-posts-app/"))); manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(gotBlogSnippet(QNetworkReply *))); manager->get(QNetworkRequest(QUrl("http://fritzing.org/projects/snippet/"))); TipsAndTricks::initTipSets(); nextTip(); } WelcomeView::~WelcomeView() { } void WelcomeView::initLayout() { QGridLayout * mainLayout = new QGridLayout(); //mainLayout->setSpacing (0); //mainLayout->setContentsMargins (0, 0, 0, 0); mainLayout->setSizeConstraint (QLayout::SetMaximumSize); QWidget * recent = initRecent(); mainLayout->addWidget(recent, 0, 0); QWidget * widget = initBlog(); mainLayout->addWidget(widget, 0, 1); widget = initShop(); mainLayout->addWidget(widget, 1, 1); widget = initTip(); mainLayout->addWidget(widget, 1, 0); this->setLayout(mainLayout); } QWidget * WelcomeView::initRecent() { QFrame * frame = new QFrame; frame->setObjectName("recentFrame"); QVBoxLayout * frameLayout = new QVBoxLayout; zeroMargin(frameLayout); QFrame * titleFrame = new QFrame; titleFrame-> setObjectName("recentTitleFrame"); QHBoxLayout * titleFrameLayout = new QHBoxLayout; zeroMargin(titleFrameLayout); QLabel * label = new QLabel(tr("Recent Sketches")); label->setObjectName("recentTitle"); titleFrameLayout->addWidget(label); titleFrame ->setLayout(titleFrameLayout); frameLayout->addWidget(titleFrame); m_recentListWidget = new QListWidget(); m_recentListWidget->setObjectName("recentList"); m_recentListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); connect(m_recentListWidget, SIGNAL(itemClicked (QListWidgetItem *)), this, SLOT(recentItemClicked(QListWidgetItem *))); frameLayout->addWidget(m_recentListWidget); QStringList names; names << "recentSpace" << "recentNewSketch" << "recentOpenSketch"; foreach (QString name, names) { QWidget * widget = NULL; QLayout * whichLayout = frameLayout; QLabel * icon = NULL; QLabel * text = NULL; if (name == "recentSpace") { widget = new QLabel(); } else if (name == "recentTitleSpace") { widget = new QLabel(); } else if (name == "recentNewSketch") { widget = makeRecentItem(name, QString(""), QString("%1").arg(tr("New Sketch")), icon, text); } else if (name == "recentOpenSketch") { widget = makeRecentItem(name, QString(""), QString("%1").arg(tr("Open Sketch")), icon, text); } if (widget) { widget->setObjectName(name); whichLayout->addWidget(widget); } } // frameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); frame->setLayout(frameLayout); return frame; } QWidget * WelcomeView::makeRecentItem(const QString & objectName, const QString & iconText, const QString & textText, QLabel * & icon, QLabel * & text) { QFrame * rFrame = new QFrame; QHBoxLayout * rFrameLayout = new QHBoxLayout; zeroMargin(rFrameLayout); icon = new QLabel(iconText); icon->setObjectName("recentIcon"); connect(icon, SIGNAL(linkActivated(const QString &)), this, SLOT(clickRecent(const QString &))); rFrameLayout->addWidget(icon); text = new QLabel(textText); text->setObjectName(objectName); text->setObjectName("recentText"); rFrameLayout->addWidget(text); connect(text, SIGNAL(linkActivated(const QString &)), this, SLOT(clickRecent(const QString &))); rFrame->setLayout(rFrameLayout); return rFrame; } QWidget * WelcomeView::initShop() { QFrame * frame = new QFrame(); frame->setObjectName("shopFrame"); QVBoxLayout * frameLayout = new QVBoxLayout; zeroMargin(frameLayout); QWidget * headerFrame = createHeaderFrame( tr("Shop"), "Shop", tr("Fab"), "Fab", m_inactiveHeaderLabelColor, m_activeHeaderLabelColor, m_shopLabel, m_fabLabel); frameLayout->addWidget(headerFrame); m_shopUberFrame = createShopContentFrame(":/resources/images/welcome_kit.png", tr("Fritzing CreatorKit"), tr("The Fritzing Creator Kit is out of Stock. For Updates please visit the fritzing.blog"), "http://creatorkit.fritzing.org/", tr(""), tr(""), ":/resources/images/icons/WS-shopLogo.png", "#f5a400" ); frameLayout->addWidget(m_shopUberFrame); m_fabUberFrame = createShopContentFrame(":/resources/images/pcbs_2013.png", tr("Fritzing Fab"), tr("Fritzing Fab is an easy and affordable service for producing professional PCBs from your Fritzing sketches."), "http://fab.fritzing.org/", tr("produce your first pcb now >>"), tr("Order your PCB now."), ":/resources/images/icons/WS-fabLogo.png", "#5f4d4a" ); frameLayout->addWidget(m_fabUberFrame); frame->setLayout(frameLayout); clickBlog("fab"); return frame; } QWidget * WelcomeView::createShopContentFrame(const QString & imagePath, const QString & headline, const QString & description, const QString & url, const QString & urlText, const QString & urlText2, const QString & logoPath, const QString & footerLabelColor ) { QFrame * uberFrame = new QFrame(); uberFrame->setObjectName("shopUberFrame"); QVBoxLayout * shopUberFrameLayout = new QVBoxLayout; zeroMargin(shopUberFrameLayout); QFrame* shopContentFrame = new QFrame(); shopContentFrame->setObjectName("shopContentFrame"); QHBoxLayout * contentFrameLayout = new QHBoxLayout; zeroMargin(contentFrameLayout); QLabel * label = new QLabel(QString("").arg(imagePath)); label->setObjectName("shopContentImage"); contentFrameLayout->addWidget(label); QFrame * contentTextFrame = new QFrame(); contentTextFrame->setObjectName("shopContentTextFrame"); QVBoxLayout * contentTextFrameLayout = new QVBoxLayout; zeroMargin(contentTextFrameLayout); contentTextFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding)); label = new QLabel(headline); label->setObjectName("shopContentTextHeadline"); //label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); contentTextFrameLayout->addWidget(label); label = new QLabel(description); label->setObjectName("shopContentTextDescription"); //label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); contentTextFrameLayout->addWidget(label); label = new QLabel(QString("%2").arg(url).arg(urlText)); label->setObjectName("shopContentTextCaption"); contentTextFrameLayout->addWidget(label); //label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(label, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); contentTextFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding)); contentTextFrame->setLayout(contentTextFrameLayout); contentFrameLayout->addWidget(contentTextFrame); shopContentFrame->setLayout(contentFrameLayout); shopUberFrameLayout->addWidget(shopContentFrame); shopUberFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); QFrame * shopFooterFrame = new QFrame(); shopFooterFrame->setObjectName("shopFooterFrame"); QHBoxLayout * footerFrameLayout = new QHBoxLayout; zeroMargin(footerFrameLayout); footerFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); QLabel * footerLabel = new QLabel(QString("%2").arg(url).arg(urlText2).arg(footerLabelColor)); footerLabel->setObjectName("shopLogoText"); footerFrameLayout->addWidget(footerLabel); connect(footerLabel, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); QLabel * footerLogoLabel = new QLabel(tr("").arg(url).arg(logoPath)); footerLogoLabel->setObjectName("shopLogo"); footerFrameLayout->addWidget(footerLogoLabel); connect(footerLogoLabel, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); shopFooterFrame->setLayout(footerFrameLayout); shopUberFrameLayout->addWidget(shopFooterFrame); uberFrame->setLayout(shopUberFrameLayout); return uberFrame; } QWidget * WelcomeView::initBlog() { QFrame * frame = new QFrame(); frame->setObjectName("blogFrame"); QVBoxLayout * frameLayout = new QVBoxLayout; zeroMargin(frameLayout); QWidget * headerFrame = createHeaderFrame(tr("Projects"), "Projects", tr("Blog"), "Blog", m_inactiveHeaderLabelColor, m_activeHeaderLabelColor, m_projectsLabel, m_blogLabel); frameLayout->addWidget(headerFrame); m_blogListWidget = createBlogContentFrame("http://blog.fritzing.org", tr("Fritzing News."), ":/resources/images/icons/WS-blogLogo.png", "#802742"); m_blogUberFrame = m_blogListWidget; while (m_blogUberFrame->parentWidget()) m_blogUberFrame = m_blogUberFrame->parentWidget(); frameLayout->addWidget(m_blogUberFrame); m_projectListWidget = createBlogContentFrame("http://fritzing.org/projects/", tr("Fritzing Projects."), ":/resources/images/icons/WS-galleryLogo.png", "#00a55b"); m_projectsUberFrame = m_projectListWidget; while (m_projectsUberFrame->parentWidget()) m_projectsUberFrame = m_projectsUberFrame->parentWidget(); frameLayout->addWidget(m_projectsUberFrame); frame->setLayout(frameLayout); //DebugDialog::debug("first click blog"); clickBlog("Blog"); return frame; } QFrame * WelcomeView::createHeaderFrame (const QString & url1, const QString & urlText1, const QString & url2, const QString & urlText2, const QString & inactiveColor, const QString & activeColor, QLabel * & label1, QLabel * & label2){ QFrame * titleFrame = new QFrame(); titleFrame->setObjectName("wsSwitchableFrameHeader"); QHBoxLayout * titleFrameLayout = new QHBoxLayout; zeroMargin(titleFrameLayout); label1 = new QLabel(makeUrlText(url1, urlText1, inactiveColor)); label1->setObjectName("headerTitle1"); titleFrameLayout->addWidget(label1); connect(label1, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); QLabel * titleSpace = new QLabel("|"); titleSpace->setObjectName("headerTitleSpace"); titleFrameLayout->addWidget(titleSpace); label2 = new QLabel(makeUrlText(url2, urlText2, activeColor)); label2->setObjectName("headerTitle2"); titleFrameLayout->addWidget(label2); connect(label2, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); titleFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); titleFrame->setLayout(titleFrameLayout); return titleFrame; } BlogListWidget * WelcomeView::createBlogContentFrame(const QString & url, const QString & urlText, const QString & logoPath, const QString & footerLabelColor) { QFrame * uberFrame = new QFrame; QVBoxLayout * uberFrameLayout = new QVBoxLayout; zeroMargin(uberFrameLayout); BlogListWidget * listWidget = new BlogListWidget; listWidget->setObjectName("blogList"); listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); listWidget->setItemDelegate(new BlogListDelegate(listWidget)); connect(listWidget, SIGNAL(itemClicked (QListWidgetItem *)), this, SLOT(blogItemClicked(QListWidgetItem *))); uberFrameLayout->addWidget(listWidget); QFrame * footerFrame = new QFrame(); footerFrame->setObjectName("blogFooterFrame"); QHBoxLayout * footerFrameLayout = new QHBoxLayout; zeroMargin(footerFrameLayout); footerFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); QLabel * footerLabel = new QLabel(QString("%2").arg(url).arg(urlText).arg(footerLabelColor)); footerLabel->setObjectName("blogLogoText"); footerFrameLayout->addWidget(footerLabel); connect(footerLabel, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); footerLabel = new QLabel(tr("").arg(url).arg(logoPath)); footerLabel->setObjectName("blogLogo"); footerFrameLayout->addWidget(footerLabel); connect(footerLabel, SIGNAL(linkActivated(const QString &)), this, SLOT(clickBlog(const QString &))); footerFrame->setLayout(footerFrameLayout); uberFrameLayout->addWidget(footerFrame); uberFrame->setLayout(uberFrameLayout); return listWidget; } void WelcomeView::showEvent(QShowEvent * event) { QFrame::showEvent(event); updateRecent(); } void WelcomeView::updateRecent() { if (m_recentListWidget == NULL) return; QSettings settings; QStringList files = settings.value("recentFileList").toStringList(); m_recentListWidget->clear(); bool gotOne = false; QIcon icon(":/resources/images/icons/WS-fzz-icon.png"); for (int i = 0; i < files.size(); ++i) { QFileInfo finfo(files[i]); if (!finfo.exists()) continue; gotOne = true; QListWidgetItem * item = new QListWidgetItem(icon, finfo.fileName()); item->setData(Qt::UserRole, finfo.absoluteFilePath()); item->setToolTip(finfo.absoluteFilePath()); m_recentListWidget->addItem(item); } if (!gotOne) { // put in a placeholder if there are no recent files QListWidgetItem * item = new QListWidgetItem(icon, tr("No recent sketches found")); item->setData(Qt::UserRole, ""); m_recentListWidget->addItem(item); } } void WelcomeView::clickRecent(const QString & url) { if (url == "open") { emit openSketch(); return; } if (url == "new") { emit newSketch(); return; } } void WelcomeView::gotBlogSnippet(QNetworkReply * networkReply) { bool blog = networkReply->url().toString().contains("blog"); QString prefix = networkReply->url().scheme() + "://" + networkReply->url().authority(); QNetworkAccessManager * manager = networkReply->manager(); int responseCode = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); bool goodBlog = false; QDomDocument doc; QString errorStr; int errorLine; int errorColumn; if (responseCode == 200) { QString data(networkReply->readAll()); //DebugDialog::debug("response data " + data); data = "" + cleanData(data) + ""; // make it one tree for xml parsing if (doc.setContent(data, &errorStr, &errorLine, &errorColumn)) { readBlog(doc, true, blog, prefix); goodBlog = true; } } if (!goodBlog) { QString message = (blog) ? tr("Unable to reach blog.fritzing.org") : tr("Unable to reach friting.org/projects") ; QString placeHolder = QString("
  • ").arg(message); if (doc.setContent(placeHolder, &errorStr, &errorLine, &errorColumn)) { readBlog(doc, true, blog, ""); } } manager->deleteLater(); networkReply->deleteLater(); } void WelcomeView::clickBlog(const QString & url) { if (url.toLower() == "fab") { m_shopUberFrame->setVisible(false); m_fabUberFrame->setVisible(true); m_fabLabel->setText(hackColor(m_fabLabel->text(), m_activeHeaderLabelColor)); m_shopLabel->setText(hackColor(m_shopLabel->text(), m_inactiveHeaderLabelColor)); return; } if (url.toLower() == "shop") { m_shopUberFrame->setVisible(true); m_fabUberFrame->setVisible(false); m_fabLabel->setText(hackColor(m_fabLabel->text(), m_inactiveHeaderLabelColor)); m_shopLabel->setText(hackColor(m_shopLabel->text(), m_activeHeaderLabelColor)); return; } if (url.toLower() == "nexttip") { nextTip(); return; } if (url.toLower() == "projects") { m_projectsUberFrame->setVisible(true); m_blogUberFrame->setVisible(false); m_projectsLabel->setText(hackColor(m_projectsLabel->text(), m_activeHeaderLabelColor)); m_blogLabel->setText(hackColor(m_blogLabel->text(), m_inactiveHeaderLabelColor)); return; } if (url.toLower() == "blog") { m_projectsUberFrame->setVisible(false); m_blogUberFrame->setVisible(true); m_projectsLabel->setText(hackColor(m_projectsLabel->text(), m_inactiveHeaderLabelColor)); m_blogLabel->setText(hackColor(m_blogLabel->text(), m_activeHeaderLabelColor)); return; } QDesktopServices::openUrl(url); } /* // sample output from http://blog.fritzing.org/recent-posts-app/ // sample output from http://fritzing.org/projects/snippet/ */ void WelcomeView::readBlog(const QDomDocument & doc, bool doEmit, bool blog, const QString & prefix) { BlogListWidget * listWidget = (blog) ? m_blogListWidget : m_projectListWidget; listWidget->clear(); listWidget->imageRequestList().clear(); QDomNodeList nodeList = doc.elementsByTagName("li"); for (int i = 0; i < nodeList.count(); i++) { QDomElement element = nodeList.at(i).toElement(); QDomElement child = element.firstChildElement(); QHash stuff; while (!child.isNull()) { if (child.tagName() == "img") { stuff.insert("img", child.attribute("src")); } else { QString clss = child.attribute("class"); if (clss == "title") { QString title = child.attribute("title"); QString href = child.attribute("href"); if (!blog) { href.insert(0, prefix); } stuff.insert("title", title); stuff.insert("href", href); } else if (clss == "image") { QDomElement img = child.firstChildElement("img"); QString src = img.attribute("src"); if (!src.isEmpty()) src.insert(0, prefix); stuff.insert("img", src); } else { stuff.insert(clss, child.text()); } } child = child.nextSiblingElement(); } if (stuff.value("title", "").isEmpty()) continue; if (stuff.value("href", "").isEmpty()) continue; QListWidgetItem * item = new QListWidgetItem(); item->setData(TitleRole, stuff.value("title")); item->setData(RefRole, stuff.value("href")); QString text = stuff.value("intro", ""); text.replace("\r", " "); text.replace("\n", " "); text.replace("\t", " "); item->setData(IntroRole, text); listWidget->addItem(item); listWidget->imageRequestList() << stuff.value("img", ""); if (!stuff.value("date", "").isEmpty()) { item->setData(DateRole, stuff.value("date")); } if (!stuff.value("author", "").isEmpty()) { item->setData(AuthorRole, stuff.value("author")); } } if (listWidget->count() > 0) { listWidget->itemEnteredSlot(listWidget->item(0)); } if (doEmit) { getNextBlogImage(0, blog); foreach (QWidget *widget, QApplication::topLevelWidgets()) { WelcomeView * other = widget->findChild(); if (other == NULL) continue; if (other == this) continue; other->readBlog(doc, false, blog, prefix); } } } void WelcomeView::getNextBlogImage(int ix, bool blog) { BlogListWidget * listWidget = (blog) ? m_blogListWidget : m_projectListWidget; for (int i = ix; i < listWidget->imageRequestList().count(); i++) { QString image = listWidget->imageRequestList().at(i); if (image.isEmpty()) continue; QNetworkAccessManager * manager = new QNetworkAccessManager(this); manager->setProperty("index", i); manager->setProperty("blog", blog); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(gotBlogImage(QNetworkReply *))); manager->get(QNetworkRequest(QUrl(image))); } } void WelcomeView::gotBlogImage(QNetworkReply * networkReply) { QNetworkAccessManager * manager = networkReply->manager(); if (manager == NULL) return; int index = manager->property("index").toInt(); bool blog = manager->property("blog").toBool(); int responseCode = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (responseCode == 200) { QByteArray data(networkReply->readAll()); QPixmap pixmap; if (pixmap.loadFromData(data)) { QPixmap scaled = pixmap.scaled(QSize(ImageSpace, ImageSpace), Qt::KeepAspectRatio); setBlogItemImage(scaled, index, blog); foreach (QWidget *widget, QApplication::topLevelWidgets()) { WelcomeView * other = widget->findChild(); if (other == NULL) continue; if (other == this) continue; other->setBlogItemImage(scaled, index, blog); } } } manager->deleteLater(); networkReply->deleteLater(); getNextBlogImage(index + 1, blog); } QWidget * WelcomeView::initTip() { QFrame * tipFrame = new QFrame(); tipFrame->setObjectName("tipFrame"); QVBoxLayout * tipLayout = new QVBoxLayout(); zeroMargin(tipLayout); QLabel * tipTitle = new QLabel(tr("Tip of the Day:")); tipTitle->setObjectName("tipTitle"); tipLayout->addWidget(tipTitle); QScrollArea * scrollArea = new QScrollArea; scrollArea->setObjectName("tipScrollArea"); scrollArea->setWidgetResizable(true); // scrollArea->setAlignment(Qt::AlignTop | Qt::AlignLeft); // scrollArea->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tip = new QLabel(); m_tip->setObjectName("tip"); m_tip->setAlignment(Qt::AlignTop | Qt::AlignLeft); //connect(m_tip, SIGNAL(linkActivated(const QString &)), this->window(), SLOT(tipsAndTricks())); scrollArea->setWidget(m_tip); tipLayout->addWidget(scrollArea); tipLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Fixed)); QFrame * footerFrame = new QFrame(); footerFrame->setObjectName("tipFooterFrame"); QHBoxLayout * footerFrameLayout = new QHBoxLayout; zeroMargin(footerFrameLayout); QLabel * footerLabel = new QLabel(QString("%1").arg(tr("All Tips"))); footerLabel->setObjectName("allTips"); footerFrameLayout->addWidget(footerLabel); connect(footerLabel, SIGNAL(linkActivated(const QString &)), this->window(), SLOT(tipsAndTricks())); footerFrameLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding)); footerLabel = new QLabel(QString("%1").arg(tr("Next Tip"))); footerLabel->setObjectName("nextTip"); footerFrameLayout->addWidget(footerLabel); connect(footerLabel, SIGNAL(linkActivated(const QString &)), this, SLOT(nextTip())); footerFrame->setLayout(footerFrameLayout); tipLayout->addWidget(footerFrame); tipFrame->setLayout(tipLayout); return tipFrame; } void WelcomeView::dragEnterEvent(QDragEnterEvent *event) { DebugDialog::debug("ignoring drag enter"); event->ignore(); } void WelcomeView::nextTip() { if (m_tip == NULL) return; m_tip->setText(QString("%1").arg(TipsAndTricks::randomTip())); } void WelcomeView::recentItemClicked(QListWidgetItem * item) { QString data = item->data(Qt::UserRole).toString(); if (data.isEmpty()) return; emit recentSketch(data, data); } void WelcomeView::blogItemClicked(QListWidgetItem * item) { QString url = item->data(RefRole).toString(); if (url.isEmpty()) return; if (url == "nop") return; QDesktopServices::openUrl(url); } void WelcomeView::setBlogItemImage(QPixmap & pixmap, int index, bool blog) { // TODO: this is not totally thread-safe if there are multiple sketch widgets opened within a very short time BlogListWidget * listWidget = (blog) ? m_blogListWidget : m_projectListWidget; QListWidgetItem * item = listWidget->item(index); if (item) { item->setData(IconRole, pixmap); } } fritzing-app-0.9.3b/src/sketch/welcomeview.h000066400000000000000000000146511272363601300210600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6483 $: $Author: irascibl@gmail.com $: $Date: 2012-09-26 15:45:37 +0200 (Mi, 26. Sep 2012) $ ********************************************************************/ #ifndef WELCOMEVIEW_H #define WELCOMEVIEW_H #include #include #include #include #include #include #include #include #include #include class BlogListWidget : public QListWidget { Q_OBJECT Q_PROPERTY(QColor titleTextColor READ titleTextColor WRITE setTitleTextColor DESIGNABLE true) Q_PROPERTY(QString titleTextFontFamily READ titleTextFontFamily WRITE setTitleTextFontFamily DESIGNABLE true) Q_PROPERTY(QString titleTextFontSize READ titleTextFontSize WRITE setTitleTextFontSize DESIGNABLE true) Q_PROPERTY(QString titleTextExtraLeading READ titleTextExtraLeading WRITE setTitleTextExtraLeading DESIGNABLE true) Q_PROPERTY(QColor introTextColor READ introTextColor WRITE setIntroTextColor DESIGNABLE true) Q_PROPERTY(QString introTextFontFamily READ introTextFontFamily WRITE setIntroTextFontFamily DESIGNABLE true) Q_PROPERTY(QString introTextFontSize READ introTextFontSize WRITE setIntroTextFontSize DESIGNABLE true) Q_PROPERTY(QString introTextExtraLeading READ introTextExtraLeading WRITE setIntroTextExtraLeading DESIGNABLE true) Q_PROPERTY(QColor dateTextColor READ dateTextColor WRITE setDateTextColor DESIGNABLE true) Q_PROPERTY(QString dateTextFontFamily READ dateTextFontFamily WRITE setDateTextFontFamily DESIGNABLE true) Q_PROPERTY(QString dateTextFontSize READ dateTextFontSize WRITE setDateTextFontSize DESIGNABLE true) public: BlogListWidget(QWidget * parent = 0); ~BlogListWidget(); QColor titleTextColor() const; void setTitleTextColor(QColor); QString titleTextFontFamily() const; void setTitleTextFontFamily(QString); QString titleTextFontSize() const; void setTitleTextFontSize(QString); QString titleTextExtraLeading() const; void setTitleTextExtraLeading(QString); QColor introTextColor() const; void setIntroTextColor(QColor); QString introTextFontFamily() const; void setIntroTextFontFamily(QString); QString introTextFontSize() const; void setIntroTextFontSize(QString); QString introTextExtraLeading() const; void setIntroTextExtraLeading(QString); QColor dateTextColor() const; void setDateTextColor(QColor); QString dateTextFontFamily() const; void setDateTextFontFamily(QString); QString dateTextFontSize() const; void setDateTextFontSize(QString); QStringList & imageRequestList(); public slots: void itemEnteredSlot(QListWidgetItem *); protected: QColor m_titleTextColor; QString m_titleTextFontFamily; QString m_titleTextFontSize; QString m_titleTextExtraLeading; QColor m_introTextColor; QString m_introTextFontFamily; QString m_introTextFontSize; QString m_introTextExtraLeading; QColor m_dateTextColor; QString m_dateTextFontFamily; QString m_dateTextFontSize; QStringList m_imageRequestList; }; class WelcomeView : public QFrame { Q_OBJECT public: WelcomeView(QWidget * parent = 0); ~WelcomeView(); void showEvent(QShowEvent * event); void dragEnterEvent(QDragEnterEvent *event); void updateRecent(); protected: void initLayout(); QWidget * initRecent(); QWidget * initBlog(); QWidget * initShop(); QWidget * initTip(); void readBlog(const QDomDocument &, bool doEmit, bool blog, const QString & prefix); QWidget * makeRecentItem(const QString & objectName, const QString & iconText, const QString & textText, QLabel * & icon, QLabel * & text); void getNextBlogImage(int ix, bool blog); void setBlogItemImage(QPixmap &, int index, bool blog) ; QWidget * createShopContentFrame(const QString & imagePath, const QString & headline, const QString & description, const QString & url, const QString & urlText, const QString & urlText2, const QString & logoPath, const QString & footerLabelColor); BlogListWidget * createBlogContentFrame(const QString & url, const QString & urlText, const QString & logoPath, const QString & footerLabelColor); QFrame * createHeaderFrame(const QString & url1, const QString & urlText1, const QString & url2, const QString & urlText2, const QString & inactiveColor, const QString & activeColor, QLabel * & label1, QLabel * & label2); signals: void newSketch(); void openSketch(); void recentSketch(const QString & filename, const QString & actionText); protected slots: void clickRecent(const QString &); void gotBlogSnippet(QNetworkReply *); void gotBlogImage(QNetworkReply *); void clickBlog(const QString &); void recentItemClicked(QListWidgetItem *); void blogItemClicked(QListWidgetItem *); void nextTip(); protected: BlogListWidget * m_blogListWidget; BlogListWidget * m_projectListWidget; QWidget * m_blogUberFrame; QWidget * m_projectsUberFrame; QLabel * m_tip; QListWidget * m_recentListWidget; QWidget * m_fabUberFrame; QWidget * m_shopUberFrame; QLabel * m_projectsLabel; QLabel * m_blogLabel; QLabel * m_fabLabel; QLabel * m_shopLabel; static QString m_activeHeaderLabelColor; static QString m_inactiveHeaderLabelColor; }; class BlogListDelegate : public QAbstractItemDelegate { public: BlogListDelegate(QObject *parent = 0); virtual ~BlogListDelegate(); void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; }; #endif fritzing-app-0.9.3b/src/sketch/zoomablegraphicsview.cpp000066400000000000000000000124311272363601300233030ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #include #include #include //#include #include "zoomablegraphicsview.h" #include "../utils/zoomslider.h" #include "../utils/misc.h" const int ZoomableGraphicsView::MaxScaleValue = 3000; ZoomableGraphicsView::WheelMapping ZoomableGraphicsView::m_wheelMapping = #ifdef Q_OS_WIN ZoomPrimary; #else ScrollPrimary; #endif bool FirstTime = true; ZoomableGraphicsView::ZoomableGraphicsView( QWidget * parent ) : QGraphicsView(parent) { m_viewFromBelow = false; m_scaleValue = 100; m_maxScaleValue = MaxScaleValue; m_minScaleValue = 1; m_acceptWheelEvents = true; if (FirstTime) { FirstTime = false; QSettings settings; m_wheelMapping = (WheelMapping) settings.value("wheelMapping", m_wheelMapping).toInt(); if (m_wheelMapping >= WheelMappingCount) { m_wheelMapping = ScrollPrimary; } } //setViewport(new QGLWidget); } void ZoomableGraphicsView::wheelEvent(QWheelEvent* event) { if (!m_acceptWheelEvents) { QGraphicsView::wheelEvent(event); return; } if ((event->modifiers() & Qt::ShiftModifier) != 0) { QGraphicsView::wheelEvent(event); return; } bool doZoom = false; bool doHorizontal = false; bool doVertical = false; bool control = event->modifiers() & Qt::ControlModifier; bool alt = event->modifiers() & altOrMetaModifier(); bool shift = event->modifiers() & Qt::ShiftModifier; switch (m_wheelMapping) { case ScrollPrimary: if (control || alt) doZoom = true; else { if (event->orientation() == Qt::Horizontal) { doHorizontal = true; } else { doVertical = true; } } break; case ZoomPrimary: if (control || alt) { if (event->orientation() == Qt::Horizontal) { doHorizontal = true; } else { doVertical = true; } } else doZoom = true; break; default: // shouldn't happen return; } if (shift && (doVertical || doHorizontal)) { if (doVertical) { doVertical = false; doHorizontal = true; } else { doVertical = true; doHorizontal = false; } } int numSteps = event->delta() / 8; if (doZoom) { double delta = ((double) event->delta() / 120) * ZoomSlider::ZoomStep; if (delta == 0) return; // Scroll zooming relative to the current size relativeZoom(2*delta, true); emit wheelSignal(); } else if (doVertical) { verticalScrollBar()->setValue( verticalScrollBar()->value() - numSteps); } else if (doHorizontal) { horizontalScrollBar()->setValue( horizontalScrollBar()->value() - numSteps); } } void ZoomableGraphicsView::relativeZoom(double step, bool centerOnCursor) { double tempSize = m_scaleValue + step; if (tempSize < m_minScaleValue) { m_scaleValue = m_minScaleValue; emit zoomOutOfRange(m_scaleValue); return; } if (tempSize > m_maxScaleValue) { m_scaleValue = m_maxScaleValue; emit zoomOutOfRange(m_scaleValue); return; } double tempScaleValue = tempSize/100; m_scaleValue = tempSize; //QPoint p = QCursor::pos(); //QPoint q = this->mapFromGlobal(p); //QPointF r = this->mapToScene(q); QMatrix matrix; double multiplier = 1; if (m_viewFromBelow) multiplier = -1; matrix.scale(multiplier * tempScaleValue, tempScaleValue); if (centerOnCursor) { //this->setMatrix(QMatrix().translate(-r.x(), -r.y()) * matrix * QMatrix().translate(r.x(), r.y())); this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); } else { this->setTransformationAnchor(QGraphicsView::AnchorViewCenter); } this->setMatrix(matrix); emit zoomChanged(m_scaleValue); } void ZoomableGraphicsView::absoluteZoom(double percent) { relativeZoom(percent-m_scaleValue, false); } double ZoomableGraphicsView::currentZoom() { return m_scaleValue; } void ZoomableGraphicsView::setAcceptWheelEvents(bool accept) { m_acceptWheelEvents = accept; } void ZoomableGraphicsView::setWheelMapping(WheelMapping wm) { m_wheelMapping = wm; } ZoomableGraphicsView::WheelMapping ZoomableGraphicsView::wheelMapping() { return m_wheelMapping; } bool ZoomableGraphicsView::viewFromBelow() { return m_viewFromBelow; } void ZoomableGraphicsView::setViewFromBelow(bool viewFromBelow) { if (m_viewFromBelow == viewFromBelow) return; m_viewFromBelow = viewFromBelow; this->setTransformationAnchor(QGraphicsView::AnchorViewCenter); QTransform transform; transform.scale(-1, 1); this->setTransform(transform, true); } fritzing-app-0.9.3b/src/sketch/zoomablegraphicsview.h000066400000000000000000000040431272363601300227500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #ifndef ZOOMABLEGRAPHICSVIEW_H #define ZOOMABLEGRAPHICSVIEW_H #include #include #include #include class ZoomableGraphicsView : public QGraphicsView { Q_OBJECT public: ZoomableGraphicsView(QWidget* parent = 0); void relativeZoom(double step, bool centerToCursor); void absoluteZoom(double percent); double currentZoom(); void setAcceptWheelEvents(bool); virtual void ensureFixedToBottomRightItems() {} bool viewFromBelow(); virtual void setViewFromBelow(bool); static const int MaxScaleValue; public: enum WheelMapping { ScrollPrimary, ZoomPrimary, WheelMappingCount }; static WheelMapping wheelMapping(); static void setWheelMapping(WheelMapping); signals: void zoomChanged(double zoom); void zoomOutOfRange(double zoom); void wheelSignal(); protected: virtual void wheelEvent(QWheelEvent* event); protected: double m_scaleValue; int m_maxScaleValue; int m_minScaleValue; bool m_acceptWheelEvents; bool m_viewFromBelow; protected: static WheelMapping m_wheelMapping; }; #endif fritzing-app-0.9.3b/src/sketchtoolbutton.cpp000066400000000000000000000074471272363601300212240ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "sketchtoolbutton.h" #include "debugdialog.h" #include #include #include #include SketchToolButton::SketchToolButton(const QString &imageName, QWidget *parent, QAction* defaultAction) : QToolButton(parent) { m_imageName = imageName; // nice to have for debugging setupIcons(imageName); //DebugDialog::debug(QString("%1 %2 %3 %4 %5 %6 %7").arg(imageName) //.arg(m_enabledImage.width()).arg(m_enabledImage.height()) //.arg(m_disabledImage.width()).arg(m_disabledImage.height()) //.arg(m_pressedImage.width()).arg(m_pressedImage.height())); if(defaultAction) { setDefaultAction(defaultAction); setText(defaultAction->text()); } } SketchToolButton::SketchToolButton(const QString &imageName, QWidget *parent, QList menuActions) : QToolButton(parent) { m_imageName = imageName; // nice to have for debugging setupIcons(imageName); QMenu *menu = new QMenu(this); for(int i=0; i < menuActions.size(); i++) { QAction* act = menuActions[i]; menu->addAction(act); if(i==0) { setDefaultAction(act); } } setMenu(menu); connect(menu,SIGNAL(aboutToHide()),this,SLOT(setEnabledIconAux())); setPopupMode(QToolButton::MenuButtonPopup); } void SketchToolButton::setEnabledIconAux() { setEnabledIcon(); } QString SketchToolButton::imagePrefix() { return ":/resources/images/icons/toolbar"; } void SketchToolButton::setImage(const QPixmap & pixmap) { setIcon(QIcon(pixmap)); } void SketchToolButton::setupIcons(const QString &imageName, bool hasStates) { setIconSize(QSize(37,24)); setToolButtonStyle(Qt::ToolButtonTextUnderIcon); AbstractStatesButton::setupIcons(imageName, hasStates); } void SketchToolButton::updateEnabledState() { bool enabled = false; foreach(QAction *act, actions()) { if(act->isEnabled()) { enabled = true; break; } } setEnabled(enabled); } void SketchToolButton::actionEvent(QActionEvent *event) { switch (event->type()) { case QEvent::ActionChanged: if (event->action() == defaultAction()) { setEnabled(defaultAction()->isEnabled()); // update button state } break; default: QToolButton::actionEvent(event); } } void SketchToolButton::mousePressEvent(QMouseEvent *event) { setPressedIcon(); QToolButton::mousePressEvent(event); } void SketchToolButton::mouseReleaseEvent(QMouseEvent *event) { setEnabledIcon(); QToolButton::mouseReleaseEvent(event); } void SketchToolButton::changeEvent(QEvent *event) { if(event->type() == QEvent::EnabledChange) { if(this->isEnabled()) { setEnabledIcon(); } else { setDisabledIcon(); } } QToolButton::changeEvent(event); } void SketchToolButton::enterEvent(QEvent *event) { QToolButton::enterEvent(event); emit entered(); } void SketchToolButton::leaveEvent(QEvent *event) { QToolButton::leaveEvent(event); emit left(); } fritzing-app-0.9.3b/src/sketchtoolbutton.h000066400000000000000000000036561272363601300206670ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SKETCHTOOLBUTTON_H_ #define SKETCHTOOLBUTTON_H_ #include #include "utils/abstractstatesbutton.h" class SketchToolButton : public QToolButton, public AbstractStatesButton { Q_OBJECT public: SketchToolButton(const QString &imageName, QWidget *parent, QAction* defaultAction); SketchToolButton(const QString &imageName, QWidget *parent, QList menuActions); void updateEnabledState(); protected slots: void setEnabledIconAux(); signals: void entered(); void left(); protected: QString imagePrefix(); void setImage(const QPixmap & pixmap); void setupIcons(const QString &imageName, bool hasStates=true); void actionEvent(QActionEvent *); void mousePressEvent(QMouseEvent *); void mouseReleaseEvent(QMouseEvent *); void enterEvent(QEvent *); void leaveEvent(QEvent *); void changeEvent(QEvent *); protected: QString m_imageName; }; #endif /* SKETCHTOOLBUTTON_H_ */ fritzing-app-0.9.3b/src/svg/000077500000000000000000000000001272363601300156705ustar00rootroot00000000000000fritzing-app-0.9.3b/src/svg/gedaelement.g000066400000000000000000000401671272363601300203220ustar00rootroot00000000000000%parser GedaElementGrammar %decl gedaelementparser.h %impl gedaelementparser.cpp %token ELEMENT %token PAD %token PIN %token MARK %token ELEMENTLINE %token ELEMENTARC %token ATTRIBUTE %token LEFTPAREN %token RIGHTPAREN %token LEFTBRACKET %token RIGHTBRACKET %token NUMBER %token STRING %token HEXNUMBER %start geda_element /: /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fachhochschule Potsdam - http://fh-potsdam.de Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 1671 $: $Author: cohen@irascible.com $: $Date: 2008-11-28 12:20:31 +0100 (Fri, 28 Nov 2008) $ ********************************************************************/ #ifndef GEDAELEMENTPARSER_H #define GEDAELEMENTPARSER_H #include #include #include "gedaelementgrammar_p.h" class GedaElementLexer; class GedaElementParser: public $table { public: GedaElementParser(); ~GedaElementParser(); bool parse(GedaElementLexer *lexer); QVector & symStack(); QString errorMessage() const; QVariant result() const; private: void reallocateStack(); int m_tos; QVector m_stateStack; QVector m_symStack; QString m_errorMessage; QVariant m_result; }; #endif // GEDAELEMENTPARSER_H :/ /. /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fachhochschule Potsdam - http://fh-potsdam.de Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 1671 $: $Author: cohen@irascible.com $: $Date: 2008-11-28 12:20:31 +0100 (Fri, 28 Nov 2008) $ ********************************************************************/ #include #include "gedaelementparser.h" #include "gedaelementlexer.h" GedaElementParser::GedaElementParser() { } GedaElementParser::~GedaElementParser() { } QVector & GedaElementParser::symStack() { return m_symStack; } void GedaElementParser::reallocateStack() { int size = m_stateStack.size(); if (size == 0) size = 128; else size <<= 1; m_stateStack.resize(size); } QString GedaElementParser::errorMessage() const { return m_errorMessage; } QVariant GedaElementParser::result() const { return m_result; } bool GedaElementParser::parse(GedaElementLexer *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.at(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; yytoken = -1; } else if (act < 0) { int r = - act - 1; m_tos -= rhs [r]; act = m_stateStack.at(m_tos++); switch (r) { ./ geda_element ::= element_command element_command_sequence sub_element_sequence ; /. case $rule_number: { qDebug() << "got geda_element "; } break; ./ sub_element_sequence ::= sub_element_sequence_paren | sub_element_sequence_bracket ; /. case $rule_number: { } break; ./ sub_element_sequence_paren ::= LEFTPAREN sub_element_groups RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); } break; ./ sub_element_sequence_bracket ::= LEFTBRACKET sub_element_groups RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); } break; ./ element_command_sequence ::= element_command_sequence_paren | element_command_sequence_bracket ; /. case $rule_number: { qDebug() << " got element_command sequence "; } break; ./ element_command_sequence_paren ::= LEFTPAREN element_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got element_command sequence "; } break; ./ element_command_sequence_bracket ::= LEFTBRACKET element_arguments RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); qDebug() << " got element_command sequence "; } break; ./ element_arguments ::= SFlags description pcb_name value mark_x mark_y text_x text_y text_direction text_scale SFlags ; /. case $rule_number: { qDebug() << " got element_arguments "; } break; ./ sub_element_groups ::= sub_element_group | sub_element_group sub_element_groups ; /. case $rule_number: { qDebug() << " got sub_element_groups "; } break; ./ sub_element_group ::= pin_element | pad_element | element_arc_element | element_line_element | mark_element | attribute_element; /. case $rule_number: { qDebug() << " got sub_element_group "; } break; ./ mark_element ::= mark_command mark_sequence ; /. case $rule_number: { qDebug() << "got mark_element "; } break; ./ mark_sequence ::= mark_paren_sequence | mark_bracket_sequence ; /. case $rule_number: { qDebug() << " got mark_sequence "; } break; ./ mark_paren_sequence ::= LEFTPAREN mark_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got mark_paren_sequence "; } break; ./ mark_bracket_sequence ::= LEFTBRACKET mark_arguments RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); qDebug() << " got mark_bracket_sequence "; } break; ./ mark_arguments ::= x y ; /. case $rule_number: { qDebug() << " got mark_arguments"; } break; ./ pin_element ::= pin_command pin_sequence ; /. case $rule_number: { qDebug() << "got pin_element "; } break; ./ pin_sequence ::= pin_paren_sequence | pin_bracket_sequence ; /. case $rule_number: { qDebug() << " got pin_sequence "; } break; ./ pin_paren_sequence ::= LEFTPAREN pin_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got pin_paren_sequence "; } break; ./ pin_bracket_sequence ::= LEFTBRACKET pin_arguments RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); qDebug() << " got pin_bracket_sequence "; } break; ./ pin_arguments ::= pin_arguments_1 | pin_arguments_2 | pin_arguments_3 | pin_arguments_4; /. case $rule_number: { qDebug() << " got pin_arguments "; } break; ./ pin_arguments_1 ::= x y Thickness Clearance Mask DrillHole Name pin_number SFlags ; /. case $rule_number: { qDebug() << " got pin_arguments 1"; } break; ./ pin_arguments_2 ::= x y Thickness DrillHole Name pin_number NFlags ; /. case $rule_number: { qDebug() << " got pin_arguments 2"; } break; ./ pin_arguments_3 ::= x y Thickness DrillHole Name NFlags ; /. case $rule_number: { qDebug() << " got pin_arguments 3"; } break; ./ pin_arguments_4 ::= x y Thickness Name NFlags ; /. case $rule_number: { qDebug() << " got pin_arguments 4 "; } break; ./ pad_element ::= pad_command pad_sequence ; /. case $rule_number: { qDebug() << "got pad_element "; } break; ./ pad_sequence ::= pad_paren_sequence | pad_bracket_sequence ; /. case $rule_number: { qDebug() << " got pad_sequence "; } break; ./ pad_paren_sequence ::= LEFTPAREN pad_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got pad_paren_sequence "; } break; ./ pad_bracket_sequence ::= LEFTBRACKET pad_arguments RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); qDebug() << " got pad_bracket_sequence "; } break; ./ pad_arguments ::= pad_arguments_1 | pad_arguments_2 | pad_arguments_3 ; /. case $rule_number: { qDebug() << " got pad_arguments "; } break; ./ pad_arguments_1 ::= x1 y1 x2 y2 Thickness Clearance Mask Name pad_number SFlags ; /. case $rule_number: { qDebug() << " got pad_arguments 1"; } break; ./ pad_arguments_2 ::= x1 y1 x2 y2 Thickness Name pad_number NFlags ; /. case $rule_number: { qDebug() << " got pad_arguments 2"; } break; ./ pad_arguments_3 ::= x1 y1 x2 y2 Thickness Name NFlags ; /. case $rule_number: { qDebug() << " got pad_arguments 3"; } break; ./ element_line_element ::= element_line_command element_line_sequence ; /. case $rule_number: { qDebug() << "got element_line_element "; } break; ./ element_line_sequence ::= element_line_paren_sequence | element_line_bracket_sequence ; /. case $rule_number: { qDebug() << " got element_line_sequence "; } break; ./ element_line_paren_sequence ::= LEFTPAREN element_line_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got element_line_paren_sequence "; } break; ./ element_line_bracket_sequence ::= LEFTBRACKET element_line_arguments RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); qDebug() << " got element_line_bracket_sequence "; } break; ./ element_line_arguments ::= x1 y1 x2 y2 Thickness ; /. case $rule_number: { qDebug() << " got element_line_arguments "; } break; ./ element_arc_element ::= element_arc_command element_arc_sequence ; /. case $rule_number: { qDebug() << "got element_arc_element "; } break; ./ element_arc_sequence ::= element_arc_paren_sequence | element_arc_bracket_sequence ; /. case $rule_number: { qDebug() << " got element_arc_sequence "; } break; ./ element_arc_paren_sequence ::= LEFTPAREN element_arc_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got element_arc_paren_sequence "; } break; ./ element_arc_bracket_sequence ::= LEFTBRACKET element_arc_arguments RIGHTBRACKET ; /. case $rule_number: { m_symStack.append(QChar(']')); qDebug() << " got element_arc_bracket_sequence "; } break; ./ element_arc_arguments ::= x y Width Height StartAngle Delta Thickness ; /. case $rule_number: { qDebug() << " got element_arc_arguments "; } break; ./ attribute_element ::= attribute_command attribute_sequence ; /. case $rule_number: { qDebug() << "got attribute_element "; } break; ./ attribute_sequence ::= attribute_paren_sequence ; /. case $rule_number: { qDebug() << " got attribute_sequence "; } break; ./ attribute_paren_sequence ::= LEFTPAREN attribute_arguments RIGHTPAREN ; /. case $rule_number: { m_symStack.append(QChar(')')); qDebug() << " got attribute_paren_sequence "; } break; ./ attribute_arguments ::= Name value ; /. case $rule_number: { qDebug() << " got attribute_arguments "; } break; ./ pad_number ::= string_value ; /. case $rule_number: { } break; ./ x ::= number_value ; /. case $rule_number: { } break; ./ x1 ::= number_value ; /. case $rule_number: { } break; ./ x2 ::= number_value ; /. case $rule_number: { } break; ./ y ::= number_value ; /. case $rule_number: { } break; ./ y1 ::= number_value ; /. case $rule_number: { } break; ./ y2 ::= number_value ; /. case $rule_number: { } break; ./ Thickness ::= number_value ; /. case $rule_number: { } break; ./ Clearance ::= number_value ; /. case $rule_number: { } break; ./ Mask ::= number_value ; /. case $rule_number: { } break; ./ DrillHole ::= number_value ; /. case $rule_number: { } break; ./ Name ::= string_value ; /. case $rule_number: { } break; ./ pin_number ::= string_value ; /. case $rule_number: { } break; ./ Width ::= number_value ; /. case $rule_number: { } break; ./ Height ::= number_value ; /. case $rule_number: { } break; ./ StartAngle ::= number_value ; /. case $rule_number: { } break; ./ Delta ::= number_value ; /. case $rule_number: { } break; ./ SFlags ::= string_value | hex_number_value ; /. case $rule_number: { } break; ./ NFlags ::= hex_number_value ; /. case $rule_number: { } break; ./ description ::= string_value ; /. case $rule_number: { } break; ./ pcb_name ::= string_value ; /. case $rule_number: { } break; ./ value ::= string_value ; /. case $rule_number: { } break; ./ mark_x ::= number_value ; /. case $rule_number: { } break; ./ mark_y ::= number_value ; /. case $rule_number: { } break; ./ text_x ::= number_value ; /. case $rule_number: { } break; ./ text_y ::= number_value ; /. case $rule_number: { } break; ./ text_direction ::= number_value ; /. case $rule_number: { } break; ./ text_scale ::= number_value ; /. case $rule_number: { } break; ./ number_value ::= NUMBER ; /. case $rule_number: { qDebug() << " got NUMBER " << lexer->currentNumber(); m_symStack.append(lexer->currentNumber()); } break; ./ hex_number_value ::= HEXNUMBER ; /. case $rule_number: { qDebug() << " got HEXNUMBER " << lexer->currentNumber(); m_symStack.append(lexer->currentNumber()); } break; ./ string_value ::= STRING ; /. case $rule_number: { qDebug() << " got STRING " << lexer->currentString(); m_symStack.append(lexer->currentString()); } break; ./ element_command ::= ELEMENT ; /. case $rule_number: { qDebug() << "got ELEMENT command "; m_symStack.append(lexer->currentCommand()); } break; ./ pin_command ::= PIN ; /. case $rule_number: { qDebug() << "got PIN command "; m_symStack.append(lexer->currentCommand()); } break; ./ pad_command ::= PAD ; /. case $rule_number: { qDebug() << "got PAD command "; m_symStack.append(lexer->currentCommand()); } break; ./ mark_command::= MARK ; /. case $rule_number: { qDebug() << "got MARK command "; m_symStack.append(lexer->currentCommand()); } break; ./ element_line_command ::= ELEMENTLINE ; /. case $rule_number: { qDebug() << "got ELEMENTLINE command "; m_symStack.append(lexer->currentCommand()); } break; ./ element_arc_command ::= ELEMENTARC ; /. case $rule_number: { qDebug() << "got ELEMENTARC command "; m_symStack.append(lexer->currentCommand()); } break; ./ attribute_command ::= ATTRIBUTE ; /. case $rule_number: { qDebug() << "got ATTRIBUTE command "; m_symStack.append(lexer->currentCommand()); } break; ./ /. } // 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_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; } ./ fritzing-app-0.9.3b/src/svg/gedaelement2svg.cpp000066400000000000000000000347521272363601300214630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "gedaelement2svg.h" #include "gedaelementparser.h" #include "gedaelementlexer.h" #include "../utils/textutils.h" #include "../version/version.h" #include "../items/wire.h" #include "../debugdialog.h" #include "../fsvgrenderer.h" #include #include #include #include #include #include #include #include #include #include GedaElement2Svg::GedaElement2Svg() : X2Svg() { } QString GedaElement2Svg::convert(const QString & filename, bool allowPadsAndPins) { m_nonConnectorNumber = 0; initLimits(); QFile file(filename); if (!file.open(QFile::ReadOnly)) { throw QObject::tr("unable to open %1").arg(filename); } QString text; QTextStream textStream(&file); text = textStream.readAll(); file.close(); GedaElementLexer lexer(text); GedaElementParser parser; if (!parser.parse(&lexer)) { throw QObject::tr("unable to parse %1").arg(filename); } QFileInfo fileInfo(filename); QDateTime now = QDateTime::currentDateTime(); QString dt = now.toString("dd/MM/yyyy hh:mm:ss"); QString title = QString("%1").arg(fileInfo.fileName()); QString description = QString("Geda footprint file '%1' converted by Fritzing") .arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(fileInfo.fileName()))); QString metadata(""); metadata += ""; metadata += ""; metadata += m_attribute.arg("geda filename").arg(fileInfo.fileName()); metadata += m_attribute.arg("fritzing version").arg(Version::versionString()); metadata += m_attribute.arg("conversion date").arg(dt); // TODO: other layers QString silkscreen; QVector stack = parser.symStack(); bool hasAuthor = false; QMultiHash pads; QMultiHash pins; QStringList pinIDs; QStringList padIDs; for (int ix = 0; ix < stack.size(); ) { QVariant var = stack[ix]; if (var.type() == QVariant::String) { QString thing = var.toString(); int argCount = countArgs(stack, ix); bool mils = stack[ix + argCount + 1].toChar() == ')'; if (thing.compare("element", Qt::CaseInsensitive) == 0) { } else if (thing.compare("pad", Qt::CaseInsensitive) == 0) { QString pid; QString s = convertPad(stack, ix, argCount, mils, pid); pads.insert(pid, s); if (!padIDs.contains(pid)) { padIDs.append(pid); } } else if (thing.compare("pin", Qt::CaseInsensitive) == 0) { QString pid; QString s = convertPin(stack, ix, argCount, mils, pid); pins.insert(pid, s); if (!pinIDs.contains(pid)) { pinIDs.append(pid); } } else if (thing.compare("elementline", Qt::CaseInsensitive) == 0) { QString unused; silkscreen += convertPad(stack, ix, argCount, mils, unused); } else if (thing.compare("elementarc", Qt::CaseInsensitive) == 0) { silkscreen += convertArc(stack, ix, argCount, mils); } else if (thing.compare("mark", Qt::CaseInsensitive) == 0) { } else if (thing.compare("attribute", Qt::CaseInsensitive) == 0) { QString aname = TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(unquote(stack[ix + 1].toString()))); metadata += m_attribute.arg(aname, TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(unquote(stack[ix + 2].toString())))); if (aname.compare("author", Qt::CaseInsensitive) == 0) { hasAuthor = true; } } ix += argCount + 2; } else if (var.type() == QVariant::Char) { // will arrive here at the end of the element // TODO: shouldn't happen otherwise ix++; } else { throw QObject::tr("parse failure in %1").arg(filename); } } if (!allowPadsAndPins && pins.count() > 0 && pads.count() > 0) { throw QObject::tr("Sorry, Fritzing can't yet handle both pins and pads together (in %1)").arg(filename); } foreach (QString c, lexer.comments()) { metadata += m_comment.arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(c))); } if (!hasAuthor) { metadata += m_attribute.arg("dist-license").arg("GPL"); metadata += m_attribute.arg("use-license").arg("unlimited"); metadata += m_attribute.arg("author").arg("gEDA project"); metadata += m_attribute.arg("license-url").arg("http://www.gnu.org/licenses/gpl.html"); } metadata += ""; metadata += ""; metadata += ""; QString copper0 = makeCopper(pinIDs, pins, filename); QString copper1 = makeCopper(padIDs, pads, filename); if (!copper0.isEmpty()) { copper0 = offsetMin("\n" + copper0 + "\n"); } if (!copper1.isEmpty()) { copper1 = offsetMin("\n" + copper1 + "\n"); } if (!silkscreen.isEmpty()) { silkscreen = offsetMin("\n" + silkscreen + "\n"); } QString svg = TextUtils::makeSVGHeader(100000, 100000, m_maxX - m_minX, m_maxY - m_minY) + title + description + metadata + copper0 + copper1 + silkscreen + ""; return svg; } int GedaElement2Svg::countArgs(QVector & stack, int ix) { int argCount = 0; for (int i = ix + 1; i < stack.size(); i++) { QVariant var = stack[i]; if (var.type() == QVariant::Char) { QChar ch = var.toChar(); if (ch == ']' || ch == ')') { break; } } argCount++; } return argCount; } QString GedaElement2Svg::convertPin(QVector & stack, int ix, int argCount, bool mils, QString & pinID) { double drill = 0; QString name; QString number; //int flags = stack[ix + argCount].toInt(); //bool useNumber = (flags & 1) != 0; if (argCount == 9) { drill = stack[ix + 6].toInt(); name = stack[ix + 7].toString(); number = stack[ix + 8].toString(); } else if (argCount == 7) { drill = stack[ix + 4].toInt(); name = stack[ix + 5].toString(); number = stack[ix + 6].toString(); } else if (argCount == 6) { drill = stack[ix + 4].toInt(); name = stack[ix + 5].toString(); } else if (argCount == 5) { name = stack[ix + 4].toString(); } else { throw QObject::tr("bad pin argument count"); } pinID = getPinID(number, name, false); int cx = stack[ix + 1].toInt(); int cy = stack[ix + 2].toInt(); double r = stack[ix + 3].toInt() / 2.0; drill /= 2.0; if (mils) { // lo res cx *= 100; cy *= 100; r *= 100; drill *= 100; } checkXLimit(cx - r); checkXLimit(cx + r); checkYLimit(cy - r); checkYLimit(cy + r); double w = r - drill; // TODO: what if multiple pins have the same id--need to clear or increment the other ids. also put the pins on a bus? // TODO: if the pin has a name, post it up to the fz as the connector name QString circle = QString("") .arg(cx) .arg(cy) .arg(r - (w / 2)) .arg(pinID) .arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(name))) .arg(w) .arg(ViewLayer::Copper0Color); return circle; } QString GedaElement2Svg::convertPad(QVector & stack, int ix, int argCount, bool mils, QString & pinID) { QString name; QString number; int flags = (argCount > 5) ? stack[ix + argCount].toInt() : 0; bool square = (flags & 0x0100) != 0; int x1 = stack[ix + 1].toInt(); int y1 = stack[ix + 2].toInt(); int x2 = stack[ix + 3].toInt(); int y2 = stack[ix + 4].toInt(); int thickness = stack[ix + 5].toInt(); bool isPad = true; if (argCount == 10) { name = stack[ix + 8].toString(); number = stack[ix + 9].toString(); QString sflags = stack[ix + argCount].toString(); if (sflags.contains("square", Qt::CaseInsensitive)) { square = true; } } else if (argCount == 8) { name = stack[ix + 6].toString(); number = stack[ix + 7].toString(); } else if (argCount == 7) { name = stack[ix + 6].toString(); } else if (argCount == 5) { // this is an elementline isPad = false; } else { throw QObject::tr("bad pad argument count"); } if (isPad) { pinID = getPinID(number, name, true); } if (mils) { // lo res x1 *= 100; y1 *= 100; x2 *= 100; y2 *= 100; thickness *= 100; } double halft = thickness / 2.0; // don't know which of the coordinates is larger so check them all checkXLimit(x1 - halft); checkXLimit(x2 - halft); checkXLimit(x1 + halft); checkXLimit(x2 + halft); checkYLimit(y1 - halft); checkYLimit(y2 - halft); checkYLimit(y1 + halft); checkYLimit(y2 + halft); QString line = QString(" & stack, int ix, int argCount, bool mils) { Q_UNUSED(argCount); int x = stack[ix + 1].toInt(); int y = stack[ix + 2].toInt(); double w = stack[ix + 3].toInt(); double h = stack[ix + 4].toInt(); // In PCB, an angle of zero points left (negative X direction), and 90 degrees points down (positive Y direction) int startAngle = (stack[ix + 5].toInt()) + 180; // Positive angles sweep counterclockwise int deltaAngle = stack[ix + 6].toInt(); int thickness = stack[ix + 7].toInt(); if (mils) { // lo res x *= 100; y *= 100; w *= 100; h *= 100; thickness *= 100; } double halft = thickness / 2.0; checkXLimit(x - w - halft); checkXLimit(x + w + halft); checkYLimit(y - h - halft); checkYLimit(y + h + halft); if (deltaAngle == 360) { if (w == h) { QString circle = QString("") .arg(x) .arg(y) .arg(w) .arg(thickness); return circle; } QString ellipse = QString("") .arg(x) .arg(y) .arg(w) .arg(h) .arg(thickness); return ellipse; } int quad = 0; int startAngleQ1 = reflectQuad(startAngle, quad); double q = atan(w * tan(2 * M_PI * startAngleQ1 / 360.0) / h); double px = w * cos(q); double py = -h * sin(q); fixQuad(quad, px, py); int endAngleQ1 = reflectQuad(startAngle + deltaAngle, quad); q = atan(w * tan(2 * M_PI * endAngleQ1 / 360.0) / h); double qx = w * cos(q); double qy = -h * sin(q); fixQuad(quad, qx, qy); QString arc = QString("") .arg(thickness) .arg(px + x) .arg(py + y) .arg(w) .arg(h) .arg(qAbs(deltaAngle) >= 180 ? 1 : 0) .arg(deltaAngle > 0 ? 0 : 1) .arg(qx - px) .arg(qy - py); return arc; } void GedaElement2Svg::fixQuad(int quad, double & px, double & py) { switch (quad) { case 0: break; case 1: px = -px; break; case 2: px = -px; py = -py; break; case 3: py = -py; break; } } int GedaElement2Svg::reflectQuad(int angle, int & quad) { angle = angle %360; if (angle < 0) angle += 360; quad = angle / 90; switch (quad) { case 0: return angle; case 1: return 180 - angle; case 2: return angle - 180; case 3: return 360 - angle; } // never gets here, but keeps compiler happy return angle; } QString GedaElement2Svg::getPinID(QString & number, QString & name, bool isPad) { if (!number.isEmpty()) { number = unquote(number); } if (!name.isEmpty()) { name = unquote(name); } QString suffix = isPad ? "pad" : "pin"; if (!number.isEmpty()) { bool ok; int n = number.toInt(&ok); return ok ? QString("connector%1%2").arg(n - 1).arg(suffix) : QString("connector%1%2").arg(number).arg(suffix); } if (!name.isEmpty()) { if (number.isEmpty()) { bool ok; int n = name.toInt(&ok); if (ok) { return QString("connector%1%2").arg(n - 1).arg(suffix); } } } return QString("%1%2").arg(FSvgRenderer::NonConnectorName).arg(m_nonConnectorNumber++); } QString GedaElement2Svg::makeCopper(QStringList ids, QHash & strings, const QString & filename) { QString copper; foreach (QString id, ids) { QStringList values = strings.values(id); if (id.isEmpty()) { DebugDialog::debug(QString("geda empty id %1").arg(filename)); foreach(QString string, values) { copper.append(string); } continue; } if (values.count() == 1) { copper.append(values.at(0)); continue; } if (values.count() == 0) { // shouldn't happen continue; } DebugDialog::debug(QString("geda multiple id %1").arg(filename)); QString xml = ""; foreach (QString string, values) { xml.append(string); } xml.append(""); QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(xml, &errorStr, &errorLine, &errorColumn)) { throw QObject::tr("Unable to parse copper: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn); } QDomElement root = doc.documentElement(); QDomElement child = root.firstChildElement(); while (!child.isNull()) { QString id = child.attribute("id"); root.setAttribute("id", id); child.removeAttribute("id"); QString name = child.attribute("connectorname"); child.removeAttribute("connectorname"); if (!name.isEmpty()) { root.setAttribute("connectorname", name); } child = child.nextSiblingElement(); } copper += doc.toString(); } return copper; } fritzing-app-0.9.3b/src/svg/gedaelement2svg.h000066400000000000000000000036151272363601300211220ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef GEDAELEMENT2SVG_H #define GEDAELEMENT2SVG_H #include #include #include #include "x2svg.h" class GedaElement2Svg : public X2Svg { public: GedaElement2Svg(); QString convert(const QString & filename, bool allowPadsAndPins); protected: int countArgs(QVector & stack, int ix); QString convertPin(QVector & stack, int ix, int argCount, bool mils, QString & pinID); QString convertPad(QVector & stack, int ix, int argCount, bool mils, QString & pinID); QString convertArc(QVector & stack, int ix, int argCount, bool mils); void fixQuad(int quad, double & px, double & py); int reflectQuad(int angle, int & quad); QString getPinID(QString & number, QString & name, bool isPad); QString makeCopper(QStringList ids, QHash &, const QString & filename); protected: int m_nonConnectorNumber; }; #endif // GEDAELEMENT2SVG_H fritzing-app-0.9.3b/src/svg/gedaelementgrammar.cpp000066400000000000000000000237251272363601300222260ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! #include "gedaelementgrammar_p.h" const char *const GedaElementGrammar::spell [] = { "end of file", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #ifndef QLALR_NO_GEDAELEMENTGRAMMAR_DEBUG_INFO "geda_element", "element_command", "element_command_sequence", "sub_element_sequence", "sub_element_sequence_paren", "sub_element_sequence_bracket", "sub_element_groups", "element_command_sequence_paren", "element_command_sequence_bracket", "element_arguments", "SFlags", "description", "pcb_name", "value", "mark_x", "mark_y", "text_x", "text_y", "text_direction", "text_scale", "sub_element_group", "pin_element", "pad_element", "element_arc_element", "element_line_element", "mark_element", "attribute_element", "mark_command", "mark_sequence", "mark_paren_sequence", "mark_bracket_sequence", "mark_arguments", "x", "y", "pin_command", "pin_sequence", "pin_paren_sequence", "pin_bracket_sequence", "pin_arguments", "pin_arguments_1", "pin_arguments_2", "pin_arguments_3", "pin_arguments_4", "Thickness", "Clearance", "Mask", "DrillHole", "Name", "pin_number", "NFlags", "pad_command", "pad_sequence", "pad_paren_sequence", "pad_bracket_sequence", "pad_arguments", "pad_arguments_1", "pad_arguments_2", "pad_arguments_3", "x1", "y1", "x2", "y2", "pad_number", "element_line_command", "element_line_sequence", "element_line_paren_sequence", "element_line_bracket_sequence", "element_line_arguments", "element_arc_command", "element_arc_sequence", "element_arc_paren_sequence", "element_arc_bracket_sequence", "element_arc_arguments", "Width", "Height", "StartAngle", "Delta", "attribute_command", "attribute_sequence", "attribute_paren_sequence", "attribute_arguments", "string_value", "number_value", "hex_number_value", "$accept" #endif // QLALR_NO_GEDAELEMENTGRAMMAR_DEBUG_INFO }; const int GedaElementGrammar::lhs [] = { 15, 18, 18, 19, 20, 17, 17, 22, 23, 24, 21, 21, 35, 35, 35, 35, 35, 35, 40, 43, 43, 44, 45, 46, 36, 50, 50, 51, 52, 53, 53, 53, 53, 54, 55, 56, 57, 37, 66, 66, 67, 68, 69, 69, 69, 70, 71, 72, 39, 79, 79, 80, 81, 82, 38, 84, 84, 85, 86, 87, 41, 93, 94, 95, 77, 47, 73, 75, 48, 74, 76, 58, 59, 60, 61, 62, 63, 88, 89, 90, 91, 25, 25, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 97, 98, 96, 16, 49, 65, 42, 78, 83, 92, 99}; const int GedaElementGrammar:: rhs[] = { 3, 1, 1, 3, 3, 1, 1, 3, 3, 11, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 1, 1, 1, 1, 9, 7, 6, 5, 2, 1, 1, 3, 3, 1, 1, 1, 10, 8, 7, 2, 1, 1, 3, 3, 5, 2, 1, 1, 3, 3, 7, 2, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}; #ifndef QLALR_NO_GEDAELEMENTGRAMMAR_DEBUG_INFO const int GedaElementGrammar::rule_info [] = { 15, 16, 17, 18 , 18, 19 , 18, 20 , 19, 8, 21, 9 , 20, 10, 21, 11 , 17, 22 , 17, 23 , 22, 8, 24, 9 , 23, 10, 24, 11 , 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 25 , 21, 35 , 21, 35, 21 , 35, 36 , 35, 37 , 35, 38 , 35, 39 , 35, 40 , 35, 41 , 40, 42, 43 , 43, 44 , 43, 45 , 44, 8, 46, 9 , 45, 10, 46, 11 , 46, 47, 48 , 36, 49, 50 , 50, 51 , 50, 52 , 51, 8, 53, 9 , 52, 10, 53, 11 , 53, 54 , 53, 55 , 53, 56 , 53, 57 , 54, 47, 48, 58, 59, 60, 61, 62, 63, 25 , 55, 47, 48, 58, 61, 62, 63, 64 , 56, 47, 48, 58, 61, 62, 64 , 57, 47, 48, 58, 62, 64 , 37, 65, 66 , 66, 67 , 66, 68 , 67, 8, 69, 9 , 68, 10, 69, 11 , 69, 70 , 69, 71 , 69, 72 , 70, 73, 74, 75, 76, 58, 59, 60, 62, 77, 25 , 71, 73, 74, 75, 76, 58, 62, 77, 64 , 72, 73, 74, 75, 76, 58, 62, 64 , 39, 78, 79 , 79, 80 , 79, 81 , 80, 8, 82, 9 , 81, 10, 82, 11 , 82, 73, 74, 75, 76, 58 , 38, 83, 84 , 84, 85 , 84, 86 , 85, 8, 87, 9 , 86, 10, 87, 11 , 87, 47, 48, 88, 89, 90, 91, 58 , 41, 92, 93 , 93, 94 , 94, 8, 95, 9 , 95, 62, 28 , 77, 96 , 47, 97 , 73, 97 , 75, 97 , 48, 97 , 74, 97 , 76, 97 , 58, 97 , 59, 97 , 60, 97 , 61, 97 , 62, 96 , 63, 96 , 88, 97 , 89, 97 , 90, 97 , 91, 97 , 25, 96 , 25, 98 , 64, 98 , 26, 96 , 27, 96 , 28, 96 , 29, 97 , 30, 97 , 31, 97 , 32, 97 , 33, 97 , 34, 97 , 97, 12 , 98, 14 , 96, 13 , 16, 1 , 49, 3 , 65, 2 , 42, 4 , 78, 5 , 83, 6 , 92, 7 , 99, 15, 0}; const int GedaElementGrammar::rule_index [] = { 0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 40, 42, 45, 47, 49, 51, 53, 55, 57, 60, 62, 64, 68, 72, 75, 78, 80, 82, 86, 90, 92, 94, 96, 98, 108, 116, 123, 129, 132, 134, 136, 140, 144, 146, 148, 150, 161, 170, 178, 181, 183, 185, 189, 193, 199, 202, 204, 206, 210, 214, 222, 225, 227, 231, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312}; #endif // QLALR_NO_GEDAELEMENTGRAMMAR_DEBUG_INFO const int GedaElementGrammar::action_default [] = { 0, 97, 0, 0, 0, 0, 0, 7, 6, 95, 0, 96, 0, 83, 82, 0, 85, 0, 86, 87, 0, 94, 0, 88, 0, 89, 90, 0, 91, 0, 92, 0, 93, 0, 10, 9, 0, 8, 0, 0, 1, 3, 2, 103, 102, 101, 100, 99, 98, 0, 18, 0, 15, 0, 16, 0, 17, 0, 14, 0, 13, 11, 0, 0, 62, 61, 0, 0, 76, 64, 63, 0, 0, 57, 56, 55, 0, 66, 0, 59, 69, 0, 0, 78, 0, 79, 0, 80, 0, 81, 60, 72, 0, 58, 0, 0, 51, 50, 49, 0, 67, 0, 53, 70, 0, 68, 0, 71, 0, 54, 0, 52, 0, 0, 21, 20, 19, 0, 0, 23, 24, 0, 22, 0, 0, 40, 39, 38, 0, 43, 44, 45, 0, 42, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 0, 65, 46, 48, 84, 0, 47, 0, 41, 0, 0, 27, 26, 25, 0, 30, 31, 32, 33, 0, 29, 0, 0, 0, 0, 0, 75, 0, 0, 75, 0, 0, 77, 34, 0, 36, 0, 35, 37, 0, 28, 12, 5, 0, 4, 104}; const int GedaElementGrammar::goto_default [] = { 3, 2, 6, 40, 42, 41, 62, 8, 7, 12, 10, 15, 17, 20, 22, 24, 27, 29, 31, 33, 61, 60, 58, 52, 54, 56, 50, 55, 116, 115, 114, 117, 78, 81, 59, 157, 156, 155, 158, 159, 160, 161, 162, 90, 138, 141, 168, 66, 175, 147, 57, 127, 126, 125, 128, 129, 130, 131, 101, 104, 106, 108, 149, 53, 98, 97, 96, 99, 51, 75, 74, 73, 76, 82, 84, 86, 88, 49, 65, 64, 67, 68, 77, 13, 0}; const int GedaElementGrammar::action_index [] = { 38, -15, 27, 24, 20, 20, 22, -15, -15, -15, 3, -15, 4, -15, -15, 3, -15, 3, -15, -15, 7, -15, 7, -15, 7, -15, -15, 7, -15, 7, -15, 0, -15, 20, -15, -15, 5, -15, 58, 58, -15, -15, -15, -15, -15, -15, -15, -15, -15, 2, -15, 36, -15, 28, -15, 45, -15, 33, -15, 32, -15, 58, 6, 3, -15, -15, 3, 9, -15, -15, -15, 1, 1, -15, -15, -15, -4, -15, 1, -15, -15, 1, 16, -15, 19, -15, 19, -15, 19, -15, -15, -15, 14, -15, 19, 1, -15, -15, -15, 18, -15, 10, -15, -15, 13, -15, 13, -15, 13, -15, 11, -15, 13, 13, -15, -15, -15, 15, 16, -15, -15, 12, -15, 13, 1, -15, -15, -15, -2, -15, -15, -15, -8, -15, -12, 1, 1, 35, 1, 20, -15, 3, -15, -5, 20, -15, -15, -15, -15, -9, -15, -3, -15, 1, 1, -15, -15, -15, -10, -15, -15, -15, -15, 1, -15, -8, 35, -12, 3, -9, -1, 1, 3, -15, 3, 20, -15, -15, 20, -15, -9, -15, -15, -6, -15, -15, -15, -7, -15, -15, -85, -85, -85, -85, -74, 22, -85, -85, -85, -85, -75, -85, -85, -85, -85, -80, -85, -71, -85, -85, -73, -85, -77, -85, -82, -85, -85, -78, -85, -59, -85, -68, -85, 44, -85, -85, -85, -85, -85, 10, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, 73, -85, -85, -85, -85, 19, -85, -85, -85, -85, -85, -61, -85, -85, -85, -85, -85, -60, -85, -85, -58, -57, -85, -56, -85, -70, -85, -69, -85, -85, -85, -85, -85, -67, -34, -85, -85, -85, -85, -85, -64, -85, -85, -63, -85, -65, -85, -14, -85, -85, -85, -12, 21, -85, -85, -85, -85, 25, -85, -85, -85, -85, 2, 8, -85, -85, -85, -85, -85, -85, -85, -8, -85, -25, -23, -1, -7, -55, -36, -85, -26, -85, -16, 39, -85, -85, -85, -85, -5, -85, -85, -85, -24, 5, -85, -85, -85, -85, -85, -85, -85, -85, 23, -85, 7, 33, -11, -6, -10, -85, -18, -44, -85, -79, 20, -85, -85, 38, -85, -13, -85, -85, -85, -85, -85, -85, -85, -85, -85}; const int GedaElementGrammar::action_info [] = { 21, 164, 188, 184, 21, 9, 152, 79, 11, 133, 63, -73, 21, 21, 37, 35, 11, 186, 70, 21, 111, 122, 21, 93, 189, 21, 119, 0, 21, 102, 39, 21, 38, 11, 9, 5, 95, 4, 94, 1, 154, 124, 153, 123, 72, 0, 71, 21, 11, 0, 0, 0, 0, 113, 0, 112, 0, 0, 0, 0, 47, 48, 46, 45, 44, 43, 0, 0, 0, 0, 0, 0, 0, 26, 18, 176, 174, 28, 25, 16, 14, 163, 23, 19, 92, 89, 91, 32, 100, 187, 107, 103, 105, 118, 143, 80, 30, 83, 85, 87, 142, 172, 109, 177, 36, 69, 110, 171, 135, 181, 163, 136, 182, 139, 178, 137, 183, 150, 145, 144, 148, 100, 146, 166, 134, 121, 118, 34, 0, 165, 105, 120, 107, 132, 0, 151, 0, 173, 145, 132, 0, 91, 0, 148, 142, 0, 148, 103, 140, 0, 167, 148, 185, 169, 91, 0, 0, 100, 0, 180, 179, 0, 91, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 14, 0, 14, 0, 80, 0, 80, 0, 0, 0, 0, 0, 0, 0, 170, 0, 0, 0, 176, 14, 148, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const int GedaElementGrammar::action_check [] = { 12, 11, 9, 9, 12, 14, 9, 11, 13, 11, 8, 12, 12, 12, 9, 11, 13, 11, 9, 12, 9, 9, 12, 9, 0, 12, 11, -1, 12, 11, 8, 12, 10, 13, 14, 8, 8, 10, 10, 1, 8, 8, 10, 10, 8, -1, 10, 12, 13, -1, -1, -1, -1, 8, -1, 10, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, -1, -1, -1, -1, -1, -1, -1, 82, 81, 81, 47, 82, 82, 81, 81, 32, 82, 81, 72, 82, 82, 82, 82, 6, 82, 82, 82, 32, 47, 82, 82, 82, 82, 82, 82, 46, 43, 10, 9, 13, 67, 45, 60, 49, 32, 61, 49, 47, 47, 43, 38, 49, 81, 62, 83, 82, 10, 43, 59, 31, 32, 10, -1, 33, 82, 33, 82, 58, -1, 54, -1, 82, 81, 58, -1, 82, -1, 83, 82, -1, 83, 82, 82, -1, 44, 83, 6, 47, 82, -1, -1, 82, -1, 48, 49, -1, 82, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, 81, -1, 81, -1, 82, -1, 82, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 81, 81, 83, -1, -1, -1, 81, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; fritzing-app-0.9.3b/src/svg/gedaelementgrammar_p.h000066400000000000000000000034201272363601300222000ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! #ifndef GEDAELEMENTGRAMMAR_P_H #define GEDAELEMENTGRAMMAR_P_H class GedaElementGrammar { public: enum { EOF_SYMBOL = 0, ATTRIBUTE = 7, ELEMENT = 1, ELEMENTARC = 6, ELEMENTLINE = 5, HEXNUMBER = 14, LEFTBRACKET = 10, LEFTPAREN = 8, MARK = 4, NUMBER = 12, PAD = 2, PIN = 3, RIGHTBRACKET = 11, RIGHTPAREN = 9, STRING = 13, ACCEPT_STATE = 189, RULE_COUNT = 104, STATE_COUNT = 190, TERMINAL_COUNT = 15, NON_TERMINAL_COUNT = 85, GOTO_INDEX_OFFSET = 190, GOTO_INFO_OFFSET = 73, GOTO_CHECK_OFFSET = 73 }; static const char *const spell []; static const int lhs []; static const int rhs []; #ifndef QLALR_NO_GEDAELEMENTGRAMMAR_DEBUG_INFO static const int rule_index []; static const int rule_info []; #endif // QLALR_NO_GEDAELEMENTGRAMMAR_DEBUG_INFO static const int goto_default []; static const int action_default []; static const int action_index []; static const int action_info []; static const int action_check []; static inline int nt_action (int state, int nt) { const int *const goto_index = &action_index [GOTO_INDEX_OFFSET]; const int *const goto_check = &action_check [GOTO_CHECK_OFFSET]; const int yyn = goto_index [state] + nt; if (yyn < 0 || goto_check [yyn] != nt) return goto_default [nt]; const int *const goto_info = &action_info [GOTO_INFO_OFFSET]; return goto_info [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]; } }; #endif // GEDAELEMENTGRAMMAR_P_H fritzing-app-0.9.3b/src/svg/gedaelementlexer.cpp000066400000000000000000000162521272363601300217140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 4067 $: $Author: cohen@irascible.com $: $Date: 2010-03-27 00:21:20 +0100 (Sa, 27. Mrz 2010) $ ********************************************************************/ #include "gedaelementlexer.h" #include "gedaelementgrammar_p.h" #include #include static QRegExp findWhitespace("[\\s]+"); GedaElementLexer::GedaElementLexer(const QString &source) { m_nonWhitespaceMatcher.setPattern("[^\\s]"); m_commentMatcher.setPattern("(^\\s*\\#)"); m_elementMatcher.setPattern("Element\\s*([\\(\\[])"); //m_stringMatcher.setPattern("\"([^\"]*)\""); m_stringMatcher.setPattern("\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""); m_integerMatcher.setPattern("[-+]?\\d+"); m_hexMatcher.setPattern("0[xX][0-9a-fA-F]+"); m_source = clean(source); m_chars = m_source.unicode(); m_size = m_source.size(); //qDebug() << m_source; m_pos = 0; m_current = next(); } GedaElementLexer::~GedaElementLexer() { } QString GedaElementLexer::clean(const QString & source) { // clean it up to make it easier to parse QStringList s = source.split("\n"); for (int i = s.length() - 1; i >= 0; i--) { QString str = s[i]; if (m_commentMatcher.indexIn(str) == 0) { s.removeAt(i); str = str.remove(0, m_commentMatcher.matchedLength()); if (m_nonWhitespaceMatcher.indexIn(str) >= 0) { m_comments.push_front(str.trimmed()); } } } QString s1 = s.join("\n"); QString s2 = s1.replace(findWhitespace, " "); int ix = m_elementMatcher.indexIn(s2); if (ix < 0) { return s2; } // now figure out how many params the Element has and fill in the missing ones // this is because Element grammar requires a longer lookahead than provided by an LALR(1) parser QStringList params; GedaElementLexer lexer(s2.right(s2.length() - m_elementMatcher.matchedLength() - ix)); int nix = ix; bool done = false; while (!done) { int result = lexer.lex(); if (result <= 0) break; switch (result) { case GedaElementGrammar::HEXNUMBER: case GedaElementGrammar::NUMBER: case GedaElementGrammar::STRING: params.append(lexer.m_currentString); break; case GedaElementGrammar::RIGHTBRACKET: nix = lexer.m_pos - 2; done = true; break; case GedaElementGrammar::RIGHTPAREN: nix = lexer.m_pos - 2; done = true; break; default: return s2; } } if (params.count() == 11) { // no changes required return s2; } bool swap = false; if (params.count() == 9) { // insert MX and MY params.insert(4, "0"); params.insert(4, "0"); swap = true; } if (params.count() == 8) { // insert value params.insert(3, "\"\""); // insert MX and MY params.insert(4, "0"); params.insert(4, "0"); swap = true; } if (params.count() == 7) { // insert SFlags params.insert(0, "\"\""); // insert value params.insert(3, "\"\""); // insert MX and MY params.insert(4, "0"); params.insert(4, "0"); swap = true; } if (!swap) { return s2; } QString j = params.join(" "); return s2.replace(ix + m_elementMatcher.matchedLength(), nix, j); } int GedaElementLexer::lex() { while (true) { if (m_hexMatcher.indexIn(m_source, m_pos - 1) == m_pos - 1) { bool ok; m_currentString = m_hexMatcher.cap(0); m_currentNumber = m_currentString.toLong(&ok, 16); m_pos += m_hexMatcher.matchedLength() - 1; if (m_source.at(m_pos).isSpace()) { m_pos++; } next(); return GedaElementGrammar::HEXNUMBER; } else if (m_integerMatcher.indexIn(m_source, m_pos - 1) == m_pos - 1) { m_currentString = m_integerMatcher.cap(0); m_currentNumber = m_currentString.toLong(); m_pos += m_integerMatcher.matchedLength() - 1; if (m_source.at(m_pos).isSpace()) { m_pos++; } next(); return GedaElementGrammar::NUMBER; } else if (m_stringMatcher.indexIn(m_source, m_pos - 1) == m_pos - 1) { m_currentString = m_stringMatcher.cap(0); m_pos += m_stringMatcher.matchedLength() - 1; next(); return GedaElementGrammar::STRING; } else if (m_current.isSpace()) { next(); continue; } else if (m_current.isNull()) { return GedaElementGrammar::EOF_SYMBOL; } else if (m_current == QLatin1Char('(')) { next(); return GedaElementGrammar::LEFTPAREN; } else if (m_current == QLatin1Char(')')) { next(); return GedaElementGrammar::RIGHTPAREN; } else if (m_current == QLatin1Char('[')) { next(); return GedaElementGrammar::LEFTBRACKET; } else if (m_current == QLatin1Char(']')) { next(); return GedaElementGrammar::RIGHTBRACKET; } else if (m_source.indexOf("elementline", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "elementline"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::ELEMENTLINE; } else if (m_source.indexOf("elementarc", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "elementarc"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::ELEMENTARC; } else if (m_source.indexOf("attribute", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "attribute"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::ATTRIBUTE; } else if (m_source.indexOf("element", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "element"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::ELEMENT; } else if (m_source.indexOf("pad", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "pad"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::PAD; } else if (m_source.indexOf("pin", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "pin"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::PIN; } else if (m_source.indexOf("mark", m_pos - 1, Qt::CaseInsensitive) == m_pos - 1) { m_currentCommand = "mark"; m_pos += m_currentCommand.length() - 1; next(); return GedaElementGrammar::MARK; } return -1; } } QChar GedaElementLexer::next() { if (m_pos < m_size) m_current = m_chars[m_pos++]; else m_current = QChar(); return m_current; } QString GedaElementLexer::currentCommand() { return m_currentCommand; } double GedaElementLexer::currentNumber() { return m_currentNumber; } QString GedaElementLexer::currentString() { return m_currentString; } const QStringList & GedaElementLexer::comments() { return m_comments; } fritzing-app-0.9.3b/src/svg/gedaelementlexer.h000066400000000000000000000034671272363601300213650ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 4067 $: $Author: cohen@irascible.com $: $Date: 2010-03-27 00:21:20 +0100 (Sa, 27. Mrz 2010) $ ********************************************************************/ #ifndef GEDAELEMENTLEXER_H #define GEDAELEMENTLEXER_H #include #include #include #include class GedaElementLexer { public: GedaElementLexer(const QString &source); ~GedaElementLexer(); int lex(); QString currentCommand(); double currentNumber(); QString currentString(); const QStringList & comments(); protected: QChar next(); QString clean(const QString & source); protected: QString m_source; const QChar *m_chars; int m_size; int m_pos; QChar m_current; QString m_currentCommand; long m_currentNumber; long m_currentHexString; QString m_currentString; QRegExp m_integerMatcher; QRegExp m_hexMatcher; QRegExp m_stringMatcher; QRegExp m_elementMatcher; QRegExp m_commentMatcher; QRegExp m_nonWhitespaceMatcher; QStringList m_comments; }; #endif fritzing-app-0.9.3b/src/svg/gedaelementparser.cpp000066400000000000000000000213751272363601300220730ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 4224 $: $Author: cohen@irascible.com $: $Date: 2010-06-07 14:51:32 +0200 (Mo, 07. Jun 2010) $ ********************************************************************/ #include #include "gedaelementparser.h" #include "gedaelementlexer.h" GedaElementParser::GedaElementParser() { } GedaElementParser::~GedaElementParser() { } QVector & GedaElementParser::symStack() { return m_symStack; } void GedaElementParser::reallocateStack() { int size = m_stateStack.size(); if (size == 0) size = 128; else size <<= 1; m_stateStack.resize(size); } QString GedaElementParser::errorMessage() const { return m_errorMessage; } QVariant GedaElementParser::result() const { return m_result; } bool GedaElementParser::parse(GedaElementLexer *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.at(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; yytoken = -1; } else if (act < 0) { int r = - act - 1; m_tos -= rhs [r]; act = m_stateStack.at(m_tos++); switch (r) { case 0: { //qDebug() << "got geda_element "; } break; case 2: { } break; case 3: { m_symStack.append(QChar(')')); } break; case 4: { m_symStack.append(QChar(']')); } break; case 6: { //qDebug() << " got element_command sequence "; } break; case 7: { m_symStack.append(QChar(')')); //qDebug() << " got element_command sequence "; } break; case 8: { m_symStack.append(QChar(']')); //qDebug() << " got element_command sequence "; } break; case 9: { //qDebug() << " got element_arguments "; } break; case 11: { //qDebug() << " got sub_element_groups "; } break; case 17: { //qDebug() << " got sub_element_group "; } break; case 18: { //qDebug() << "got mark_element "; } break; case 20: { //qDebug() << " got mark_sequence "; } break; case 21: { m_symStack.append(QChar(')')); //qDebug() << " got mark_paren_sequence "; } break; case 22: { m_symStack.append(QChar(']')); //qDebug() << " got mark_bracket_sequence "; } break; case 23: { //qDebug() << " got mark_arguments"; } break; case 24: { //qDebug() << "got pin_element "; } break; case 26: { //qDebug() << " got pin_sequence "; } break; case 27: { m_symStack.append(QChar(')')); //qDebug() << " got pin_paren_sequence "; } break; case 28: { m_symStack.append(QChar(']')); //qDebug() << " got pin_bracket_sequence "; } break; case 32: { //qDebug() << " got pin_arguments "; } break; case 33: { //qDebug() << " got pin_arguments 1"; } break; case 34: { //qDebug() << " got pin_arguments 2"; } break; case 35: { //qDebug() << " got pin_arguments 3"; } break; case 36: { //qDebug() << " got pin_arguments 4 "; } break; case 37: { //qDebug() << "got pad_element "; } break; case 39: { //qDebug() << " got pad_sequence "; } break; case 40: { m_symStack.append(QChar(')')); //qDebug() << " got pad_paren_sequence "; } break; case 41: { m_symStack.append(QChar(']')); //qDebug() << " got pad_bracket_sequence "; } break; case 44: { //qDebug() << " got pad_arguments "; } break; case 45: { //qDebug() << " got pad_arguments 1"; } break; case 46: { //qDebug() << " got pad_arguments 2"; } break; case 47: { //qDebug() << " got pad_arguments 3"; } break; case 48: { //qDebug() << "got element_line_element "; } break; case 50: { //qDebug() << " got element_line_sequence "; } break; case 51: { m_symStack.append(QChar(')')); //qDebug() << " got element_line_paren_sequence "; } break; case 52: { m_symStack.append(QChar(']')); //qDebug() << " got element_line_bracket_sequence "; } break; case 53: { //qDebug() << " got element_line_arguments "; } break; case 54: { //qDebug() << "got element_arc_element "; } break; case 56: { //qDebug() << " got element_arc_sequence "; } break; case 57: { m_symStack.append(QChar(')')); //qDebug() << " got element_arc_paren_sequence "; } break; case 58: { m_symStack.append(QChar(']')); //qDebug() << " got element_arc_bracket_sequence "; } break; case 59: { //qDebug() << " got element_arc_arguments "; } break; case 60: { //qDebug() << "got attribute_element "; } break; case 61: { //qDebug() << " got attribute_sequence "; } break; case 62: { m_symStack.append(QChar(')')); //qDebug() << " got attribute_paren_sequence "; } break; case 63: { //qDebug() << " got attribute_arguments "; } break; case 64: { } break; case 65: { } break; case 66: { } break; case 67: { } break; case 68: { } break; case 69: { } break; case 70: { } break; case 71: { } break; case 72: { } break; case 73: { } break; case 74: { } break; case 75: { } break; case 76: { } break; case 77: { } break; case 78: { } break; case 79: { } break; case 80: { } break; case 82: { } break; case 83: { } break; case 84: { } break; case 85: { } break; case 86: { } break; case 87: { } break; case 88: { } break; case 89: { } break; case 90: { } break; case 91: { } break; case 92: { } break; case 93: { //qDebug() << " got NUMBER " << lexer->currentNumber(); m_symStack.append(lexer->currentNumber()); } break; case 94: { //qDebug() << " got HEXNUMBER " << lexer->currentNumber(); m_symStack.append(lexer->currentNumber()); } break; case 95: { //qDebug() << " got STRING " << lexer->currentString(); m_symStack.append(lexer->currentString()); } break; case 96: { //qDebug() << "got ELEMENT command "; m_symStack.append(lexer->currentCommand()); } break; case 97: { //qDebug() << "got PIN command "; m_symStack.append(lexer->currentCommand()); } break; case 98: { //qDebug() << "got PAD command "; m_symStack.append(lexer->currentCommand()); } break; case 99: { //qDebug() << "got MARK command "; m_symStack.append(lexer->currentCommand()); } break; case 100: { //qDebug() << "got ELEMENTLINE command "; m_symStack.append(lexer->currentCommand()); } break; case 101: { //qDebug() << "got ELEMENTARC command "; m_symStack.append(lexer->currentCommand()); } break; case 102: { //qDebug() << "got ATTRIBUTE command "; m_symStack.append(lexer->currentCommand()); } break; } // 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_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; } fritzing-app-0.9.3b/src/svg/gedaelementparser.h000066400000000000000000000032011272363601300215240ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 3848 $: $Author: cohen@irascible.com $: $Date: 2009-12-10 12:48:23 +0100 (Do, 10. Dez 2009) $ ********************************************************************/ #ifndef GEDAELEMENTPARSER_H #define GEDAELEMENTPARSER_H #include #include #include "gedaelementgrammar_p.h" class GedaElementLexer; class GedaElementParser: public GedaElementGrammar { public: GedaElementParser(); ~GedaElementParser(); bool parse(GedaElementLexer *lexer); QVector & symStack(); QString errorMessage() const; QVariant result() const; private: void reallocateStack(); int m_tos; QVector m_stateStack; QVector m_symStack; QString m_errorMessage; QVariant m_result; }; #endif // GEDAELEMENTPARSER_H fritzing-app-0.9.3b/src/svg/gerbergenerator.cpp000066400000000000000000001215511272363601300215560ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #include #include #include #include #include "gerbergenerator.h" #include "../debugdialog.h" #include "../fsvgrenderer.h" #include "../sketch/pcbsketchwidget.h" #include "../connectors/connectoritem.h" #include "../connectors/svgidlayer.h" #include "svgfilesplitter.h" #include "groundplanegenerator.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../utils/folderutils.h" #include "../version/version.h" static const QRegExp AaCc("[aAcCqQtTsS]"); static const QRegExp MFinder("([mM])\\s*([0-9.]*)[\\s,]*([0-9.]*)"); const QRegExp GerberGenerator::MultipleZs("z\\s*[^\\s]"); const QString GerberGenerator::SilkTopSuffix = "_silkTop.gto"; const QString GerberGenerator::SilkBottomSuffix = "_silkBottom.gbo"; const QString GerberGenerator::CopperTopSuffix = "_copperTop.gtl"; const QString GerberGenerator::CopperBottomSuffix = "_copperBottom.gbl"; const QString GerberGenerator::MaskTopSuffix = "_maskTop.gts"; const QString GerberGenerator::MaskBottomSuffix = "_maskBottom.gbs"; const QString GerberGenerator::PasteMaskTopSuffix = "_pasteMaskTop.gtp"; const QString GerberGenerator::PasteMaskBottomSuffix = "_pasteMaskBottom.gbp"; const QString GerberGenerator::DrillSuffix = "_drill.txt"; const QString GerberGenerator::OutlineSuffix = "_contour.gm1"; const QString GerberGenerator::MagicBoardOutlineID = "boardoutline"; const double GerberGenerator::MaskClearanceMils = 5; //////////////////////////////////////////// bool pixelsCollide(QImage * image1, QImage * image2, int x1, int y1, int x2, int y2) { for (int y = y1; y < y2; y++) { for (int x = x1; x < x2; x++) { QRgb p1 = image1->pixel(x, y); if (p1 == 0xffffffff) continue; QRgb p2 = image2->pixel(x, y); if (p2 == 0xffffffff) continue; //DebugDialog::debug(QString("p1:%1 p2:%2").arg(p1, 0, 16).arg(p2, 0, 16)); return true; } } return false; } //////////////////////////////////////////// void GerberGenerator::exportToGerber(const QString & prefix, const QString & exportDir, ItemBase * board, PCBSketchWidget * sketchWidget, bool displayMessageBoxes) { if (board == NULL) { int boardCount; board = sketchWidget->findSelectedBoard(boardCount); if (boardCount == 0) { DebugDialog::debug("board not found"); return; } if (board == NULL) { DebugDialog::debug("multiple boards found"); return; } } exportPickAndPlace(prefix, exportDir, board, sketchWidget, displayMessageBoxes); LayerList viewLayerIDs = ViewLayer::copperLayers(ViewLayer::NewBottom); int copperInvalidCount = doCopper(board, sketchWidget, viewLayerIDs, "Copper0", CopperBottomSuffix, prefix, exportDir, displayMessageBoxes); if (sketchWidget->boardLayers() == 2) { viewLayerIDs = ViewLayer::copperLayers(ViewLayer::NewTop); copperInvalidCount += doCopper(board, sketchWidget, viewLayerIDs, "Copper1", CopperTopSuffix, prefix, exportDir, displayMessageBoxes); } LayerList maskLayerIDs = ViewLayer::maskLayers(ViewLayer::NewBottom); QString maskBottom, maskTop; int maskInvalidCount = doMask(maskLayerIDs, "Mask0", MaskBottomSuffix, board, sketchWidget, prefix, exportDir, displayMessageBoxes, maskBottom); if (sketchWidget->boardLayers() == 2) { maskLayerIDs = ViewLayer::maskLayers(ViewLayer::NewTop); maskInvalidCount += doMask(maskLayerIDs, "Mask1", MaskTopSuffix, board, sketchWidget, prefix, exportDir, displayMessageBoxes, maskTop); } maskLayerIDs = ViewLayer::maskLayers(ViewLayer::NewBottom); int pasteMaskInvalidCount = doPasteMask(maskLayerIDs, "PasteMask0", PasteMaskBottomSuffix, board, sketchWidget, prefix, exportDir, displayMessageBoxes); if (sketchWidget->boardLayers() == 2) { maskLayerIDs = ViewLayer::maskLayers(ViewLayer::NewTop); pasteMaskInvalidCount += doPasteMask(maskLayerIDs, "PasteMask1", PasteMaskTopSuffix, board, sketchWidget, prefix, exportDir, displayMessageBoxes); } LayerList silkLayerIDs = ViewLayer::silkLayers(ViewLayer::NewTop); int silkInvalidCount = doSilk(silkLayerIDs, "Silk1", SilkTopSuffix, board, sketchWidget, prefix, exportDir, displayMessageBoxes, maskTop); silkLayerIDs = ViewLayer::silkLayers(ViewLayer::NewBottom); silkInvalidCount += doSilk(silkLayerIDs, "Silk0", SilkBottomSuffix, board, sketchWidget, prefix, exportDir, displayMessageBoxes, maskBottom); // now do it for the outline/contour LayerList outlineLayerIDs = ViewLayer::outlineLayers(); bool empty; QString svgOutline = renderTo(outlineLayerIDs, board, sketchWidget, empty); if (empty || svgOutline.isEmpty()) { displayMessage(QObject::tr("outline is empty"), displayMessageBoxes); return; } svgOutline = cleanOutline(svgOutline); // at this point svgOutline must be a single element; a path element may contain cutouts QMultiHash treatAsCircle; svgOutline = clipToBoard(svgOutline, board, "board", SVG2gerber::ForOutline, "", displayMessageBoxes, treatAsCircle); QSizeF svgSize = TextUtils::parseForWidthAndHeight(svgOutline); // create outline gerber from svg SVG2gerber outlineGerber; int outlineInvalidCount = outlineGerber.convert(svgOutline, sketchWidget->boardLayers() == 2, "contour", SVG2gerber::ForOutline, svgSize * GraphicsUtils::StandardFritzingDPI); //DebugDialog::debug(QString("outline output: %1").arg(outlineGerber.getGerber())); saveEnd("contour", exportDir, prefix, OutlineSuffix, displayMessageBoxes, outlineGerber); doDrill(board, sketchWidget, prefix, exportDir, displayMessageBoxes); if (outlineInvalidCount > 0 || silkInvalidCount > 0 || copperInvalidCount > 0 || maskInvalidCount || pasteMaskInvalidCount) { QString s; if (outlineInvalidCount > 0) s += QObject::tr("the board outline layer, "); if (silkInvalidCount > 0) s += QObject::tr("silkscreen layer(s), "); if (copperInvalidCount > 0) s += QObject::tr("copper layer(s), "); if (maskInvalidCount > 0) s += QObject::tr("mask layer(s), "); if (pasteMaskInvalidCount > 0) s += QObject::tr("paste mask layer(s), "); s.chop(2); displayMessage(QObject::tr("Unable to translate svg curves in %1").arg(s), displayMessageBoxes); } } int GerberGenerator::doCopper(ItemBase * board, PCBSketchWidget * sketchWidget, LayerList & viewLayerIDs, const QString & copperName, const QString & copperSuffix, const QString & filename, const QString & exportDir, bool displayMessageBoxes) { bool empty; QString svg = renderTo(viewLayerIDs, board, sketchWidget, empty); if (empty || svg.isEmpty()) { displayMessage(QObject::tr("%1 layer export is empty.").arg(copperName), displayMessageBoxes); return 0; } QMultiHash treatAsCircle; foreach (QGraphicsItem * item, sketchWidget->scene()->collidingItems(board)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!connectorItem->isPath()) continue; if (connectorItem->radius() == 0) continue; treatAsCircle.insert(connectorItem->attachedToID(), connectorItem); } QSizeF svgSize = TextUtils::parseForWidthAndHeight(svg); svg = clipToBoard(svg, board, copperName, SVG2gerber::ForCopper, "", displayMessageBoxes, treatAsCircle); if (svg.isEmpty()) { displayMessage(QObject::tr("%1 layer export is empty (case 2).").arg(copperName), displayMessageBoxes); return 0; } return doEnd(svg, sketchWidget->boardLayers(), copperName, SVG2gerber::ForCopper, svgSize * GraphicsUtils::StandardFritzingDPI, exportDir, filename, copperSuffix, displayMessageBoxes); } int GerberGenerator::doSilk(LayerList silkLayerIDs, const QString & silkName, const QString & gerberSuffix, ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes, const QString & clipString) { bool empty; QString svgSilk = renderTo(silkLayerIDs, board, sketchWidget, empty); if (empty || svgSilk.isEmpty()) { if (silkLayerIDs.contains(ViewLayer::Silkscreen1)) { displayMessage(QObject::tr("silk layer %1 export is empty").arg(silkName), displayMessageBoxes); } return 0; } //QFile f(silkName + "original.svg"); //f.open(QFile::WriteOnly); //QTextStream fs(&f); //fs << svgSilk; //f.close(); QSizeF svgSize = TextUtils::parseForWidthAndHeight(svgSilk); QMultiHash treatAsCircle; svgSilk = clipToBoard(svgSilk, board, silkName, SVG2gerber::ForSilk, clipString, displayMessageBoxes, treatAsCircle); if (svgSilk.isEmpty()) { displayMessage(QObject::tr("silk export failure"), displayMessageBoxes); return 0; } //QFile f2(silkName + "clipped.svg"); //f2.open(QFile::WriteOnly); //QTextStream fs2(&f2); //fs2 << svgSilk; //f2.close(); return doEnd(svgSilk, sketchWidget->boardLayers(), silkName, SVG2gerber::ForSilk, svgSize * GraphicsUtils::StandardFritzingDPI, exportDir, filename, gerberSuffix, displayMessageBoxes); } int GerberGenerator::doDrill(ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes) { LayerList drillLayerIDs; drillLayerIDs << ViewLayer::drillLayers(); bool empty; QString svgDrill = renderTo(drillLayerIDs, board, sketchWidget, empty); if (empty || svgDrill.isEmpty()) { displayMessage(QObject::tr("exported drill file is empty"), displayMessageBoxes); return 0; } QSizeF svgSize = TextUtils::parseForWidthAndHeight(svgDrill); QMultiHash treatAsCircle; foreach (QGraphicsItem * item, sketchWidget->scene()->collidingItems(board)) { ConnectorItem * connectorItem = dynamic_cast(item); if (connectorItem == NULL) continue; if (!connectorItem->isPath()) continue; if (connectorItem->radius() == 0) continue; treatAsCircle.insert(connectorItem->attachedToID(), connectorItem); } svgDrill = clipToBoard(svgDrill, board, "Copper0", SVG2gerber::ForDrill, "", displayMessageBoxes, treatAsCircle); if (svgDrill.isEmpty()) { displayMessage(QObject::tr("drill export failure"), displayMessageBoxes); return 0; } return doEnd(svgDrill, sketchWidget->boardLayers(), "drill", SVG2gerber::ForDrill, svgSize * GraphicsUtils::StandardFritzingDPI, exportDir, filename, DrillSuffix, displayMessageBoxes); } int GerberGenerator::doMask(LayerList maskLayerIDs, const QString &maskName, const QString & gerberSuffix, ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes, QString & clipString) { // don't want these in the mask laqyer QList copperLogoItems; sketchWidget->hideCopperLogoItems(copperLogoItems); bool empty; QString svgMask = renderTo(maskLayerIDs, board, sketchWidget, empty); sketchWidget->restoreCopperLogoItems(copperLogoItems); if (empty || svgMask.isEmpty()) { displayMessage(QObject::tr("exported mask layer %1 is empty").arg(maskName), displayMessageBoxes); return 0; } svgMask = TextUtils::expandAndFill(svgMask, "black", MaskClearanceMils * 2); if (svgMask.isEmpty()) { displayMessage(QObject::tr("%1 mask export failure (2)").arg(maskName), displayMessageBoxes); return 0; } QSizeF svgSize = TextUtils::parseForWidthAndHeight(svgMask); QMultiHash treatAsCircle; svgMask = clipToBoard(svgMask, board, maskName, SVG2gerber::ForCopper, "", displayMessageBoxes, treatAsCircle); if (svgMask.isEmpty()) { displayMessage(QObject::tr("mask export failure"), displayMessageBoxes); return 0; } clipString = svgMask; return doEnd(svgMask, sketchWidget->boardLayers(), maskName, SVG2gerber::ForCopper, svgSize * GraphicsUtils::StandardFritzingDPI, exportDir, filename, gerberSuffix, displayMessageBoxes); } int GerberGenerator::doPasteMask(LayerList maskLayerIDs, const QString &maskName, const QString & gerberSuffix, ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes) { // don't want these in the mask laqyer QList copperLogoItems; sketchWidget->hideCopperLogoItems(copperLogoItems); QList holes; sketchWidget->hideHoles(holes); bool empty; QString svgMask = renderTo(maskLayerIDs, board, sketchWidget, empty); sketchWidget->restoreCopperLogoItems(copperLogoItems); sketchWidget->restoreCopperLogoItems(holes); if (empty || svgMask.isEmpty()) { displayMessage(QObject::tr("exported paste mask layer is empty"), displayMessageBoxes); return 0; } svgMask = sketchWidget->makePasteMask(svgMask, board, GraphicsUtils::StandardFritzingDPI, maskLayerIDs); if (svgMask.isEmpty()) return 0; QSizeF svgSize = TextUtils::parseForWidthAndHeight(svgMask); QMultiHash treatAsCircle; svgMask = clipToBoard(svgMask, board, maskName, SVG2gerber::ForCopper, "", displayMessageBoxes, treatAsCircle); if (svgMask.isEmpty()) { displayMessage(QObject::tr("mask export failure"), displayMessageBoxes); return 0; } return doEnd(svgMask, sketchWidget->boardLayers(), maskName, SVG2gerber::ForCopper, svgSize * GraphicsUtils::StandardFritzingDPI, exportDir, filename, gerberSuffix, displayMessageBoxes); } int GerberGenerator::doEnd(const QString & svg, int boardLayers, const QString & layerName, SVG2gerber::ForWhy forWhy, QSizeF svgSize, const QString & exportDir, const QString & prefix, const QString & suffix, bool displayMessageBoxes) { // create mask gerber from svg SVG2gerber gerber; int invalidCount = gerber.convert(svg, boardLayers == 2, layerName, forWhy, svgSize); saveEnd(layerName, exportDir, prefix, suffix, displayMessageBoxes, gerber); return invalidCount; } bool GerberGenerator::saveEnd(const QString & layerName, const QString & exportDir, const QString & prefix, const QString & suffix, bool displayMessageBoxes, SVG2gerber & gerber) { QString outname = exportDir + "/" + prefix + suffix; QFile out(outname); if (!out.open(QIODevice::WriteOnly | QIODevice::Text)) { displayMessage(QObject::tr("%1 layer: unable to save to '%2'").arg(layerName).arg(outname), displayMessageBoxes); return false; } QTextStream stream(&out); stream << gerber.getGerber(); stream.flush(); out.close(); return true; } void GerberGenerator::displayMessage(const QString & message, bool displayMessageBoxes) { // don't use QMessageBox if running conversion as a service if (displayMessageBoxes) { QMessageBox::warning(NULL, QObject::tr("Fritzing"), message); return; } DebugDialog::debug(message); } QString GerberGenerator::clipToBoard(QString svgString, ItemBase * board, const QString & layerName, SVG2gerber::ForWhy forWhy, const QString & clipString, bool displayMessageBoxes, QMultiHash & treatAsCircle) { QRectF source = board->sceneBoundingRect(); source.moveTo(0, 0); return clipToBoard(svgString, source, layerName, forWhy, clipString, displayMessageBoxes, treatAsCircle); } QString GerberGenerator::clipToBoard(QString svgString, QRectF & boardRect, const QString & layerName, SVG2gerber::ForWhy forWhy, const QString & clipString, bool displayMessageBoxes, QMultiHash & treatAsCircle) { // document 1 will contain svg that is easy to convert to gerber QDomDocument domDocument1; QString errorStr; int errorLine; int errorColumn; bool result = domDocument1.setContent(svgString, &errorStr, &errorLine, &errorColumn); if (!result) { return ""; } QDomElement root1 = domDocument1.documentElement(); if (root1.firstChildElement().isNull()) { return ""; } if (forWhy != SVG2gerber::ForDrill) { QDomNodeList nodeList = root1.elementsByTagName("circle"); QList justHoles; for (int i = 0; i < nodeList.count(); i++) { QDomElement circle = nodeList.at(i).toElement(); if (circle.attribute("id").contains(FSvgRenderer::NonConnectorName)) { double sw = circle.attribute("stroke-width").toDouble(); if (sw == 0) { justHoles << circle; } } } foreach (QDomElement circle, justHoles) { circle.setTagName("g"); } } handleDonuts(root1, treatAsCircle); bool multipleContours = false; if (forWhy == SVG2gerber::ForOutline) { multipleContours = dealWithMultipleContours(root1, displayMessageBoxes); } // document 2 will contain svg that must be rasterized for gerber conversion QDomDocument domDocument2 = domDocument1.cloneNode(true).toDocument(); bool anyConverted = false; if (TextUtils::squashElement(domDocument1, "text", "", QRegExp())) { anyConverted = true; } // gerber can't handle ellipses that are rotated, so cull them all if (TextUtils::squashElement(domDocument1, "ellipse", "", QRegExp())) { anyConverted = true; } if (TextUtils::squashElement(domDocument1, "rect", "rx", QRegExp())) { anyConverted = true; } if (TextUtils::squashElement(domDocument1, "rect", "ry", QRegExp())) { anyConverted = true; } // gerber can't handle paths with curves if (TextUtils::squashElement(domDocument1, "path", "d", AaCc)) { anyConverted = true; } // gerber can't handle multiple subpaths if there are intersections if (TextUtils::squashElement(domDocument1, "path", "d", MultipleZs)) { anyConverted = true; } if (TextUtils::squashElement(domDocument1, "image", "", QRegExp())) { anyConverted = true; } // can't handle scaled paths very well. There is probably a deeper bug that needs to be chased down. // is this only necessary for contour view? QDomNodeList nodeList = root1.elementsByTagName("path"); for (int i = 0; i < nodeList.count(); i++) { QDomNode parent = nodeList.at(i); while (!parent.isNull()) { QString transformString = parent.toElement().attribute("transform"); if (!transformString.isNull()) { QMatrix matrix = TextUtils::transformStringToMatrix(transformString); QTransform transform(matrix); if (transform.isScaling()) { nodeList.at(i).toElement().setTagName("g"); anyConverted = true; break; } } parent = parent.parentNode(); } } QVector leaves1; int transformCount1 = 0; QDomElement e1 = domDocument1.documentElement(); TextUtils::collectLeaves(e1, transformCount1, leaves1); QVector leaves2; int transformCount2 = 0; QDomElement e2 = domDocument2.documentElement(); TextUtils::collectLeaves(e2, transformCount2, leaves2); double res = GraphicsUtils::StandardFritzingDPI; // convert from pixel dpi to StandardFritzingDPI QRectF sourceRes(boardRect.left() * res / GraphicsUtils::SVGDPI, boardRect.top() * res / GraphicsUtils::SVGDPI, boardRect.width() * res / GraphicsUtils::SVGDPI, boardRect.height() * res / GraphicsUtils::SVGDPI); int twidth = sourceRes.width(); int theight = sourceRes.height(); QSize imgSize(twidth + 2, theight + 2); QRectF target(0, 0, twidth, theight); QImage * clipImage = NULL; if (!clipString.isEmpty()) { clipImage = new QImage(imgSize, QImage::Format_Mono); clipImage->fill(0xffffffff); clipImage->setDotsPerMeterX(res * GraphicsUtils::InchesPerMeter); clipImage->setDotsPerMeterY(res * GraphicsUtils::InchesPerMeter); QXmlStreamReader reader(clipString); QSvgRenderer renderer(&reader); QPainter painter; painter.begin(clipImage); renderer.render(&painter, target); painter.end(); #ifndef QT_NO_DEBUG clipImage->save(FolderUtils::getTopLevelUserDataStorePath() + "/clip.png"); #endif } svgString = TextUtils::removeXMLEntities(domDocument1.toString()); QList possibleHoles; QXmlStreamReader reader(svgString); QSvgRenderer renderer(&reader); bool anyClipped = false; if (forWhy != SVG2gerber::ForOutline) { for (int i = 0; i < transformCount1; i++) { QString n = QString::number(i); QRectF bounds = renderer.boundsOnElement(n); QMatrix m = renderer.matrixForElement(n); QDomElement element = leaves1.at(i); QRectF mBounds = m.mapRect(bounds); if (mBounds.left() < sourceRes.left() - 0.1|| mBounds.top() < sourceRes.top() - 0.1 || mBounds.right() > sourceRes.right() + 0.1 || mBounds.bottom() > sourceRes.bottom() + 0.1) { if (element.tagName() == "circle") { possibleHoles.append(element); } // element is outside of bounds--squash it so it will be clipped // we don't care if the board shape is irregular // since anything printed between the shape and the bounding rectangle // will be physically clipped when the board is cut out element.setTagName("g"); anyClipped = anyConverted = true; } } } if (possibleHoles.count() > 0) { QList newHoles; int ix = 0; foreach (QDomElement element, possibleHoles) { QDomElement newElement = element.cloneNode(false).toElement(); double radius = element.attribute("r").toDouble(); double sw = element.attribute("stroke-width").toDouble(); element.parentNode().insertAfter(newElement, element); newElement.setAttribute("id", QString("__%1__").arg(ix++)); newElement.setAttribute("stroke-width", 0); newElement.setAttribute("r", QString::number(radius - (sw / 2))); newElement.setTagName("circle"); newHoles.append(newElement); } QSvgRenderer renderer(domDocument1.toByteArray()); for (int i = newHoles.count() - 1; i >= 0; i--) { QString id = QString("__%1__").arg(i); QRectF bounds = renderer.boundsOnElement(id); QMatrix m = renderer.matrixForElement(id); QDomElement newElement = newHoles.at(i); QRectF mBounds = m.mapRect(bounds); if (mBounds.left() < sourceRes.left() - 0.1 || mBounds.top() < sourceRes.top() - 0.1 || mBounds.right() > sourceRes.right() + 0.1 || mBounds.bottom() > sourceRes.bottom() + 0.1) { // hole is still clipped newHoles.removeAt(i); newElement.parentNode().removeChild(newElement); } else { // enlarge it a little due to aliasing when the clipped portion is converted to raster and back double radius = newElement.attribute("r").toDouble(); radius += 4; newElement.setAttribute("r", QString::number(radius)); newElement.setAttribute("stroke-width", 2); } } } if (clipImage) { QImage another(imgSize, QImage::Format_Mono); another.fill(0xffffffff); another.setDotsPerMeterX(res * GraphicsUtils::InchesPerMeter); another.setDotsPerMeterY(res * GraphicsUtils::InchesPerMeter); svgString = TextUtils::removeXMLEntities(domDocument1.toString()); QXmlStreamReader reader(svgString); QSvgRenderer renderer(&reader); QPainter painter; painter.begin(&another); renderer.render(&painter, target); painter.end(); for (int i = 0; i < transformCount1; i++) { QDomElement element = leaves1.at(i); if (element.tagName().compare("g") == 0) { // element is already converted to raster space, we'll clip it later continue; } QString n = QString::number(i); QRectF bounds = renderer.boundsOnElement(n); QMatrix m = renderer.matrixForElement(n); QRectF mBounds = m.mapRect(bounds); int x1 = qFloor(qMax(0.0, mBounds.left() - sourceRes.left())); // atmel compiler fails without cast int x2 = qCeil(qMin(sourceRes.width(), mBounds.right() - sourceRes.left())); int y1 = qFloor(qMax(0.0, mBounds.top() - sourceRes.top())); // atmel compiler fails without cast int y2 = qCeil(qMin(sourceRes.height(), mBounds.bottom() - sourceRes.top())); if (pixelsCollide(&another, clipImage, x1, y1, x2, y2)) { element.setTagName("g"); anyClipped = anyConverted = true; } } } if (anyClipped) { // svg has been changed by clipping process so get the string again svgString = TextUtils::removeXMLEntities(domDocument1.toString()); } if (anyConverted) { for (int i = 0; i < transformCount1; i++) { QDomElement element1 = leaves1.at(i); if (element1.tagName().compare("g") != 0) { // document 1 element svg can be directly converted to gerber // so remove it from document 2 QDomElement element2 = leaves2.at(i); element2.setTagName("g"); } } // expand the svg to fill the space of the image QDomElement root2 = domDocument2.documentElement(); root2.setAttribute("width", QString("%1px").arg(twidth)); root2.setAttribute("height", QString("%1px").arg(theight)); if (boardRect.x() != 0 || boardRect.y() != 0) { QString viewBox = root2.attribute("viewBox"); QStringList coords = viewBox.split(" ", QString::SkipEmptyParts); coords[0] = QString::number(sourceRes.left()); coords[1] = QString::number(sourceRes.top()); root2.setAttribute("viewBox", coords.join(" ")); } QStringList exceptions; exceptions << "none" << ""; QString toColor("#000000"); SvgFileSplitter::changeColors(root2, toColor, exceptions); QImage image(imgSize, QImage::Format_Mono); image.setDotsPerMeterX(res * GraphicsUtils::InchesPerMeter); image.setDotsPerMeterY(res * GraphicsUtils::InchesPerMeter); if (forWhy == SVG2gerber::ForOutline) { QDomNodeList paths = root2.elementsByTagName("path"); if (paths.count() == 0) { // some non-path element makes up the outline mergeOutlineElement(image, target, res, domDocument2, svgString, 0, layerName); } else { for (int p = 0; p < paths.count(); p++) { QDomElement path = paths.at(p).toElement(); path.setTagName("g"); } for (int p = 0; p < paths.count(); p++) { QDomElement path = paths.at(p).toElement(); path.setTagName("path"); if (p > 0) { paths.at(p - 1).toElement().setTagName("g"); } mergeOutlineElement(image, target, res, domDocument2, svgString, p, layerName); } } } else { image.fill(0xffffffff); QByteArray svg = TextUtils::removeXMLEntities(domDocument2.toString()).toUtf8(); QSvgRenderer renderer(svg); QPainter painter; painter.begin(&image); renderer.render(&painter, target); painter.end(); image.invertPixels(); // need white pixels on a black background for GroundPlaneGenerator #ifndef QT_NO_DEBUG image.save(FolderUtils::getTopLevelUserDataStorePath() + "/preclip_output.png"); #endif if (clipImage != NULL) { // can this be done with a single blt using composition mode // if not, grab a scanline instead of testing every pixel for (int y = 0; y < theight; y++) { for (int x = 0; x < twidth; x++) { if (clipImage->pixel(x, y) != 0xffffffff) { image.setPixel(x, y, 0); } } } } #ifndef QT_NO_DEBUG image.save(FolderUtils::getTopLevelUserDataStorePath() + "/output.png"); #endif QString path = makePath(image, res / GraphicsUtils::StandardFritzingDPI, "#000000"); svgString.replace("", path + ""); /* GroundPlaneGenerator gpg; gpg.setLayerName(layerName); gpg.setMinRunSize(1, 1); gpg.scanImage(image, image.width(), image.height(), GraphicsUtils::StandardFritzingDPI / res, GraphicsUtils::StandardFritzingDPI, "#000000", false, false, QSizeF(0, 0), 0, sourceRes.topLeft()); if (gpg.newSVGs().count() > 0) { svgString = gpg.mergeSVGs(svgString, ""); } */ } } if (clipImage) delete clipImage; return QString(svgString); } QString GerberGenerator::cleanOutline(const QString & outlineSvg) { QDomDocument doc; doc.setContent(outlineSvg); QList leaves; QDomElement root = doc.documentElement(); TextUtils::collectLeaves(root, leaves); QDomNodeList textNodes = root.elementsByTagName("text"); for (int t = 0; t < textNodes.count(); t++) { leaves << textNodes.at(t).toElement(); } if (leaves.count() == 0) return ""; if (leaves.count() == 1) return outlineSvg; if (leaves.count() > 1) { for (int i = 0; i < leaves.count(); i++) { QDomElement leaf = leaves.at(i); if (leaf.attribute("id", "").compare(MagicBoardOutlineID) == 0) { for (int j = 0; j < leaves.count(); j++) { if (i != j) { QDomElement jleaf = leaves.at(j); jleaf.parentNode().removeChild(jleaf); } } return doc.toString(); } } } if (leaves.count() == 0) return ""; return outlineSvg; } void GerberGenerator::mergeOutlineElement(QImage & image, QRectF & target, double res, QDomDocument & document, QString & svgString, int ix, const QString & layerName) { image.fill(0xffffffff); QByteArray svg = TextUtils::removeXMLEntities(document.toString()).toUtf8(); QSvgRenderer renderer(svg); QPainter painter; painter.begin(&image); renderer.render(&painter, target); painter.end(); image.invertPixels(); // need white pixels on a black background for GroundPlaneGenerator #ifndef QT_NO_DEBUG image.save(QString("%2/output%1.png").arg(ix).arg(FolderUtils::getTopLevelUserDataStorePath())); #else Q_UNUSED(ix); #endif GroundPlaneGenerator gpg; gpg.setLayerName(layerName); gpg.setMinRunSize(1, 1); gpg.scanOutline(image, image.width(), image.height(), GraphicsUtils::StandardFritzingDPI / res, GraphicsUtils::StandardFritzingDPI, "#000000", false, false, QSizeF(0, 0), 0); if (gpg.newSVGs().count() > 0) { svgString = gpg.mergeSVGs(svgString, ""); } } QString GerberGenerator::makePath(QImage & image, double unit, const QString & colorString) { double halfUnit = unit / 2; QString paths; int lineCount = 0; for (int y = 0; y < image.height(); y++) { bool inWhite = false; int whiteStart = 0; for (int x = 0; x < image.width(); x++) { QRgb current = image.pixel(x, y); if (inWhite) { if (current == 0xffffffff) { // another white pixel, keep moving continue; } // got black: close up this segment; inWhite = false; paths += QString("M%1,%2L%3,%2 ").arg(whiteStart + halfUnit).arg(y + halfUnit).arg(x - 1 + halfUnit); if (++lineCount == 10) { lineCount = 0; paths += "\n"; } } else { if (current != 0xffffffff) { // another black pixel, keep moving continue; } inWhite = true; whiteStart = x; } } } QString path = QString("\n"; } bool GerberGenerator::dealWithMultipleContours(QDomElement & root, bool displayMessageBoxes) { bool multipleContours = false; bool contoursOK = true; // split path into multiple contours QDomNodeList paths = root.elementsByTagName("path"); // should only be one for (int p = 0; p < paths.count() && contoursOK; p++) { QDomElement path = paths.at(p).toElement(); QString originalPath = path.attribute("d", "").trimmed(); if (MultipleZs.indexIn(originalPath) < 0) continue; multipleContours = true; QStringList subpaths = path.attribute("d").split("z", QString::SkipEmptyParts); foreach (QString subpath, subpaths) { if (!subpath.trimmed().startsWith("m", Qt::CaseInsensitive)) { contoursOK = false; break; } } } if (!multipleContours) return false; if (!contoursOK) { QString msg = QObject::tr("Fritzing is unable to process the cutouts in this custom PCB shape. ") + QObject::tr("You may need to reload the shape SVG. ") + QObject::tr("Fritzing requires that you make cutouts using a shape 'subtraction' or 'difference' operation in your vector graphics editor."); displayMessage(msg, displayMessageBoxes); return false; } for (int p = 0; p < paths.count(); p++) { QDomElement path = paths.at(p).toElement(); QString originalPath = path.attribute("d", "").trimmed(); if (MultipleZs.indexIn(originalPath) >= 0) { QStringList subpaths = path.attribute("d").split("z", QString::SkipEmptyParts); QString priorM; MFinder.indexIn(subpaths.at(0).trimmed()); priorM += MFinder.cap(1) + MFinder.cap(2) + "," + MFinder.cap(3) + " "; for (int i = 1; i < subpaths.count(); i++) { QDomElement newPath = path.cloneNode(true).toElement(); QString z = ((i < subpaths.count() - 1) || originalPath.endsWith("z", Qt::CaseInsensitive)) ? "z" : ""; QString d = subpaths.at(i).trimmed() + z; MFinder.indexIn(d); if (d.startsWith("m", Qt::CaseSensitive)) { d = priorM + d; } priorM += MFinder.cap(1) + MFinder.cap(2) + "," + MFinder.cap(3) + " "; newPath.setAttribute("d", d); path.parentNode().appendChild(newPath); } path.setAttribute("d", subpaths.at(0) + "z"); } } return true; } void GerberGenerator::exportPickAndPlace(const QString & prefix, const QString & exportDir, ItemBase * board, PCBSketchWidget * sketchWidget, bool displayMessageBoxes) { QPointF bottomLeft = board->sceneBoundingRect().bottomLeft(); QSet itemBases; foreach (QGraphicsItem * item, sketchWidget->scene()->collidingItems(board)) { ItemBase * itemBase = dynamic_cast(item); if (itemBase == NULL) continue; if (itemBase == board) continue; if (itemBase->itemType() == ModelPart::Wire) continue; itemBase = itemBase->layerKinChief(); if (!itemBase->isEverVisible()) continue; if (itemBase == board) continue; itemBases.insert(itemBase->layerKinChief()); } QString outname = exportDir + "/" + prefix + "_pnp.txt"; QFile out(outname); if (!out.open(QIODevice::WriteOnly | QIODevice::Text)) { displayMessage(QObject::tr("Unable to save pick and place file: %2").arg(outname), displayMessageBoxes); return; } QTextStream stream(&out); stream << "*Pick And Place List\n" << "*Company=\n" << "*Author=\n" //*Tel= //*Fax= << "*eMail=\n" << "*\n" << QString("*Project=%1\n").arg(prefix) // *Variant= << QString("*Date=%1\n").arg(QTime::currentTime().toString()) << QString("*CreatedBy=Fritzing %1\n").arg(Version::versionString()) << "*\n" << "*\n*Coordinates in mm, always center of component\n" << "*Origin 0/0=Lower left corner of PCB\n" << "*Rotation in degree (0-360, math. pos.)\n" << "*\n" << "*No;Value;Package;X;Y;Rotation;Side;Name\n" ; QStringList valueKeys; valueKeys << "resistance" << "capacitance" << "inductance" << "voltage" << "current" << "power"; int ix = 1; foreach (ItemBase * itemBase, itemBases) { QString value; foreach (QString valueKey, valueKeys) { value = itemBase->modelPart()->localProp(valueKey).toString(); if (!value.isEmpty()) break; value = itemBase->modelPart()->properties().value(valueKey); if (!value.isEmpty()) break; } QPointF loc = itemBase->sceneBoundingRect().center(); QTransform transform = itemBase->transform(); // doesn't account for scaling double angle = atan2(transform.m12(), transform.m11()) * 180 / M_PI; // No;Value;Package;X;Y;Rotation;Side;Name QString string = QString("%1;%2;%3;%4;%5;%6;%7;%8\n") .arg(ix++) .arg(value) .arg(itemBase->modelPart()->properties().value("package")) .arg(GraphicsUtils::pixels2mm(loc.x() - bottomLeft.x(), GraphicsUtils::SVGDPI)) .arg(GraphicsUtils::pixels2mm(loc.y() - bottomLeft.y(), GraphicsUtils::SVGDPI)) .arg(angle) .arg(itemBase->viewLayerID() == ViewLayer::Copper1 ? "Top" : "Bottom") .arg(itemBase->instanceTitle()) ; stream << string; stream.flush(); } out.close(); } void GerberGenerator::handleDonuts(QDomElement & root1, QMultiHash & treatAsCircle) { // most of this would not be necessary if we cached cleaned SVGs static const QString unique("%%%%%%%%%%%%%%%%%%%%%%%%_________________________________%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"); QDomNodeList nodeList = root1.elementsByTagName("path"); if (treatAsCircle.count() > 0) { QStringList ids; foreach (ConnectorItem * connectorItem, treatAsCircle.values()) { ItemBase * itemBase = connectorItem->attachedTo(); SvgIdLayer * svgIdLayer = connectorItem->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID()); DebugDialog::debug(QString("treat as circle %1").arg(svgIdLayer->m_svgId)); ids << svgIdLayer->m_svgId; } for (int n = 0; n < nodeList.count(); n++) { QDomElement path = nodeList.at(n).toElement(); QString id = path.attribute("id"); if (id.isEmpty()) continue; DebugDialog::debug(QString("checking for %1").arg(id)); if (!ids.contains(id)) continue; QString pid; ConnectorItem * connectorItem = NULL; for (QDomElement parent = path.parentNode().toElement(); !parent.isNull(); parent = parent.parentNode().toElement()) { pid = parent.attribute("partID"); if (pid.isEmpty()) continue; QList connectorItems = treatAsCircle.values(pid.toLong()); if (connectorItems.count() == 0) break; foreach (ConnectorItem * candidate, connectorItems) { ItemBase * itemBase = candidate->attachedTo(); SvgIdLayer * svgIdLayer = candidate->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID()); if (svgIdLayer->m_svgId == id) { connectorItem = candidate; break; } } if (connectorItem) break; } if (connectorItem == NULL) continue; //QString string; //QTextStream stream(&string); //path.save(stream, 0); //DebugDialog::debug("path " + string); connectorItem->debugInfo("make path"); path.setAttribute("id", unique); QSvgRenderer renderer; renderer.load(root1.ownerDocument().toByteArray()); QRectF bounds = renderer.boundsOnElement(unique); path.removeAttribute("id"); QDomElement circle = root1.ownerDocument().createElement("circle"); path.parentNode().insertBefore(circle, path); circle.setAttribute("id", id); QPointF p = bounds.center(); circle.setAttribute("cx", QString::number(p.x())); circle.setAttribute("cy", QString::number(p.y())); circle.setAttribute("r", QString::number(connectorItem->radius() * GraphicsUtils::StandardFritzingDPI / GraphicsUtils::SVGDPI)); circle.setAttribute("stroke-width", QString::number(connectorItem->strokeWidth() * GraphicsUtils::StandardFritzingDPI / GraphicsUtils::SVGDPI)); } } } QString GerberGenerator::renderTo(const LayerList & layers, ItemBase * board, PCBSketchWidget * sketchWidget, bool & empty) { RenderThing renderThing; renderThing.printerScale = GraphicsUtils::SVGDPI; renderThing.blackOnly = true; renderThing.dpi = GraphicsUtils::StandardFritzingDPI; renderThing.hideTerminalPoints = true; renderThing.selectedItems = renderThing.renderBlocker = false; QString svg = sketchWidget->renderToSVG(renderThing, board, layers); empty = renderThing.empty; return svg; } fritzing-app-0.9.3b/src/svg/gerbergenerator.h000066400000000000000000000111611272363601300212160ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6955 $: $Author: irascibl@gmail.com $: $Date: 2013-04-06 23:14:37 +0200 (Sa, 06. Apr 2013) $ ********************************************************************/ #ifndef GERBERGENERATOR_H #define GERBERGENERATOR_H #include #include "../viewlayer.h" #include "svg2gerber.h" class GerberGenerator { public: static void exportToGerber(const QString & prefix, const QString & exportDir, class ItemBase * board, class PCBSketchWidget *, bool displayMessageBoxes); static QString clipToBoard(QString svgString, QRectF & boardRect, const QString & layerName, SVG2gerber::ForWhy, const QString & clipString, bool displayMessageBoxes, QMultiHash & treatAsCircle); static QString clipToBoard(QString svgString, ItemBase * board, const QString & layerName, SVG2gerber::ForWhy, const QString & clipString, bool displayMessageBoxes, QMultiHash & treatAsCircle); static int doEnd(const QString & svg, int boardLayers, const QString & layerName, SVG2gerber::ForWhy forWhy, QSizeF svgSize, const QString & exportDir, const QString & prefix, const QString & suffix, bool displayMessageBoxes); static QString cleanOutline(const QString & svgOutline); public: static const QString SilkTopSuffix; static const QString SilkBottomSuffix; static const QString CopperTopSuffix; static const QString CopperBottomSuffix; static const QString MaskTopSuffix; static const QString MaskBottomSuffix; static const QString PasteMaskTopSuffix; static const QString PasteMaskBottomSuffix; static const QString DrillSuffix; static const QString OutlineSuffix; static const QString MagicBoardOutlineID; static const double MaskClearanceMils; static const QRegExp MultipleZs; protected: static int doSilk(LayerList silkLayerIDs, const QString & silkName, const QString & gerberSuffix, ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes, const QString & clipString); static int doMask(LayerList maskLayerIDs, const QString & maskName, const QString & gerberSuffix, ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes, QString & clipString); static int doPasteMask(LayerList maskLayerIDs, const QString & maskName, const QString & gerberSuffix, ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes); static int doCopper(ItemBase * board, PCBSketchWidget * sketchWidget, LayerList & viewLayerIDs, const QString & copperName, const QString & copperSuffix, const QString & filename, const QString & exportDir, bool displayMessageBoxes); static int doDrill(ItemBase * board, PCBSketchWidget * sketchWidget, const QString & filename, const QString & exportDir, bool displayMessageBoxes); static void displayMessage(const QString & message, bool displayMessageBoxes); static bool saveEnd(const QString & layerName, const QString & exportDir, const QString & prefix, const QString & suffix, bool displayMessageBoxes, SVG2gerber & gerber); static void mergeOutlineElement(QImage & image, QRectF & target, double res, QDomDocument & document, QString & svgString, int ix, const QString & layerName); static QString makePath(QImage & image, double unit, const QString & colorString); static bool dealWithMultipleContours(QDomElement & root, bool displayMessageBoxes); static void exportPickAndPlace(const QString & prefix, const QString & exportDir, ItemBase * board, PCBSketchWidget * sketchWidget, bool displayMessageBoxes); static void handleDonuts(QDomElement & root1, QMultiHash & treatAsCircle); static QString renderTo(const LayerList &, ItemBase * board, PCBSketchWidget * sketchWidget, bool & empty); }; #endif // GERBERGENERATOR_H fritzing-app-0.9.3b/src/svg/groundplanegenerator.cpp000066400000000000000000001070431272363601300226260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, orF (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6941 $: $Author: irascibl@gmail.com $: $Date: 2013-03-26 15:03:18 +0100 (Di, 26. Mrz 2013) $ ********************************************************************/ #include "groundplanegenerator.h" #include "svgfilesplitter.h" #include "../fsvgrenderer.h" #include "../debugdialog.h" #include "../version/version.h" #include "../utils/folderutils.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../items/wire.h" #include "../processeventblocker.h" #include "../autoroute/drc.h" #include #include #include #include #include #include #include #include static const double BORDERINCHES = 0.04; const QString GroundPlaneGenerator::KeepoutSettingName("GPG_Keepout"); const double GroundPlaneGenerator::KeepoutDefaultMils = 10; inline int OFFSET(int x, int y, QImage * image) { return (y * image->width()) + x; } QString GroundPlaneGenerator::ConnectorName = "connector0pad"; // !!!!!!!!!!!!!!!!!!! // !!!!!!!!!!!!!!!!!!! IMPORTANT NOTE: QRect::right() and QRect::bottom() are off by one--this is a known Qt problem // !!!!!!!!!!!!!!!!!!! // !!!!!!!!!!!!!!!!!!! one workaround might be to switch to QRectF // !!!!!!!!!!!!!!!!!!! GroundPlaneGenerator::GroundPlaneGenerator() { m_strokeWidthIncrement = 0; m_minRiseSize = m_minRunSize = 1; } GroundPlaneGenerator::~GroundPlaneGenerator() { } bool GroundPlaneGenerator::getBoardRects(const QByteArray & boardByteArray, QGraphicsItem * board, double res, double keepoutSpace, QList & rects) { QRectF br = board->sceneBoundingRect(); double bWidth = res * br.width() / GraphicsUtils::SVGDPI; double bHeight = res * br.height() / GraphicsUtils::SVGDPI; QImage image(bWidth, bHeight, QImage::Format_Mono); image.setDotsPerMeterX(res * GraphicsUtils::InchesPerMeter); image.setDotsPerMeterY(res * GraphicsUtils::InchesPerMeter); image.fill(0xffffffff); QSvgRenderer renderer(boardByteArray); QPainter painter; painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing, false); renderer.render(&painter); painter.end(); #ifndef QT_NO_DEBUG image.save(FolderUtils::getTopLevelUserDataStorePath() + "/getBoardRects.png"); #endif QColor keepaway(255,255,255); // now add keepout area to the border QImage image2 = image.copy(); painter.begin(&image2); painter.setRenderHint(QPainter::Antialiasing, false); painter.fillRect(0, 0, image2.width(), keepoutSpace, keepaway); painter.fillRect(0, image2.height() - keepoutSpace, image2.width(), keepoutSpace, keepaway); painter.fillRect(0, 0, keepoutSpace, image2.height(), keepaway); painter.fillRect(image2.width() - keepoutSpace, 0, keepoutSpace, image2.height(), keepaway); for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb current = image.pixel(x, y); if (current != 0xffffffff) { continue; } painter.fillRect(x - keepoutSpace, y - keepoutSpace, keepoutSpace + keepoutSpace, keepoutSpace + keepoutSpace, keepaway); } } painter.end(); #ifndef QT_NO_DEBUG image2.save(FolderUtils::getTopLevelUserDataStorePath() + "/getBoardRects2.png"); #endif scanLines(image2, bWidth, bHeight, rects); // combine parallel equal-sized rects int ix = 0; while (ix < rects.count()) { QRect r = rects.at(ix++); for (int j = ix; j < rects.count(); j++) { QRect s = rects.at(j); if (s.bottom() == r.bottom()) { // on same row; keep going continue; } if (s.top() > r.bottom() + 1) { // skipped row, can't join break; } if (s.left() == r.left() && s.right() == r.right()) { // join these r.setBottom(s.bottom()); rects.removeAt(j); ix--; rects.replace(ix, r); break; } } } return true; } bool GroundPlaneGenerator::generateGroundPlaneUnit(const QString & boardSvg, QSizeF boardImageSize, const QString & svg, QSizeF copperImageSize, QStringList & exceptions, QGraphicsItem * board, double res, const QString & color, QPointF whereToStart, double keepoutMils) { GPGParams params; params.boardSvg = boardSvg; params.boardImageSize = boardImageSize; params.svg = svg; params.copperImageSize = copperImageSize; params.exceptions = exceptions; params.board = board; params.res = res; params.color = color; params.keepoutMils = keepoutMils; double bWidth, bHeight; QList rects; QImage * image = generateGroundPlaneAux(params, bWidth, bHeight, rects); if (image == NULL) return false; QRectF bsbr = board->sceneBoundingRect(); QPoint s(qRound(res * (whereToStart.x() - bsbr.topLeft().x()) / GraphicsUtils::SVGDPI), qRound(res * (whereToStart.y() - bsbr.topLeft().y()) / GraphicsUtils::SVGDPI)); QBitArray redMarker(image->height() * image->width(), false); QRgb pixel = image->pixel(s); //DebugDialog::debug(QString("unit %1").arg(pixel, 0, 16)); if (pixel != 0xffffffff) { // starting off in bad territory delete image; return false; } // step 1 flood fill white to "red" (keep max locations) int minY = image->height(); int maxY = 0; int minX = image->width(); int maxX = 0; QList stack; stack << s; while (stack.count() > 0) { QPoint p = stack.takeFirst(); if (p.x() < 0) continue; if (p.y() < 0) continue; if (p.x() >= image->width()) continue; if (p.y() >= image->height()) continue; if (redMarker.testBit(OFFSET(p.x(), p.y(), image))) continue; // already been here QRgb pixel = image->pixel(p); if (pixel != 0xffffffff) continue; // black; bail redMarker.setBit(OFFSET(p.x(), p.y(), image), true); if (p.x() > maxX) maxX = p.x(); if (p.x() < minX) minX = p.x(); if (p.y() > maxY) maxY = p.y(); if (p.y() < minY) minY = p.y(); stack.append(QPoint(p.x() - 1, p.y())); stack.append(QPoint(p.x() + 1, p.y())); stack.append(QPoint(p.x(), p.y() - 1)); stack.append(QPoint(p.x(), p.y() + 1)); } //image->save("testPoly1.png"); // step 2 replace white with black image->fill(0); // step 3 replace "red" with white for (int y = 0; y < image->height(); y++) { for (int x = 0; x < image->width(); x++) { if (redMarker.testBit(OFFSET(x, y, image))) { image->setPixel(x, y, 1); } } } #ifndef QT_NO_DEBUG image->save(FolderUtils::getTopLevelUserDataStorePath() + "/testGroundPlaneUnit3.png"); #endif scanImage(*image, bWidth, bHeight, GraphicsUtils::StandardFritzingDPI / res, res, color, true, true, QSizeF(.05, .05), 1 / GraphicsUtils::SVGDPI, QPointF(0,0)); delete image; return true; } bool GroundPlaneGenerator::generateGroundPlane(const QString & boardSvg, QSizeF boardImageSize, const QString & svg, QSizeF copperImageSize, QStringList & exceptions, QGraphicsItem * board, double res, const QString & color, double keepoutMils) { GPGParams params; params.boardSvg = boardSvg; params.keepoutMils = keepoutMils; params.boardImageSize = boardImageSize; params.svg = svg; params.copperImageSize = copperImageSize; params.exceptions = exceptions; params.board = board; params.res = res; params.color = color; QFuture future = QtConcurrent::run(this, &GroundPlaneGenerator::generateGroundPlaneFn, params); while (!future.isFinished()) { ProcessEventBlocker::processEvents(200); } return future.result(); } bool GroundPlaneGenerator::generateGroundPlaneFn(GPGParams & params) { double bWidth, bHeight; QList rects; QImage * image = generateGroundPlaneAux(params, bWidth, bHeight, rects); if (image == NULL) return false; double pixelFactor = GraphicsUtils::StandardFritzingDPI / params.res; scanImage(*image, bWidth, bHeight, pixelFactor, params.res, params.color, true, true, QSizeF(.05, .05), 1 / GraphicsUtils::SVGDPI, QPointF(0,0)); if (rects.count() > 0) { foreach (QRectF r, rects) { // add the rects separately as tiny SVGs which don't get clipped (since they are connected) QList polygons; QPolygon polygon; polygon << QPoint(r.left() * pixelFactor, r.top() * pixelFactor) << QPoint(r.right() * pixelFactor, r.top() * pixelFactor) << QPoint(r.right() * pixelFactor, r.bottom() * pixelFactor) << QPoint(r.left() * pixelFactor, r.bottom() * pixelFactor); polygons.append(polygon); makePolySvg(polygons, params.res, bWidth, bHeight, pixelFactor, params.color, false, true, QSizeF(0, 0), 0, QPointF(0, 0)); } } delete image; return true; } QImage * GroundPlaneGenerator::generateGroundPlaneAux(GPGParams & params, double & bWidth, double & bHeight, QList & rects) { QByteArray boardByteArray; QString tempColor("#ffffff"); if (!SvgFileSplitter::changeColors(params.boardSvg, tempColor, params.exceptions, boardByteArray)) { return NULL; } //QFile file0("testGroundFillBoard.svg"); //file0.open(QIODevice::WriteOnly); //QTextStream out0(&file0); //out0 << boardByteArray; //file0.close(); /* QByteArray copperByteArray; if (!SvgFileSplitter::changeStrokeWidth(params.svg, m_strokeWidthIncrement, false, true, copperByteArray)) { return NULL; } */ QString errorStr; int errorLine; int errorColumn; QDomDocument doc; doc.setContent(params.svg, &errorStr, &errorLine, &errorColumn); QDomElement root = doc.documentElement(); SvgFileSplitter::forceStrokeWidth(root, 2 * params.keepoutMils, "#000000", true, true); QByteArray copperByteArray = doc.toByteArray(0); //QFile file1("testGroundFillCopper.svg"); //file1.open(QIODevice::WriteOnly); //QTextStream out1(&file1); //out1 << copperByteArray; //file1.close(); double svgWidth = params.res * qMax(params.boardImageSize.width(), params.copperImageSize.width()) / GraphicsUtils::SVGDPI; double svgHeight = params.res * qMax(params.boardImageSize.height(), params.copperImageSize.height()) / GraphicsUtils::SVGDPI; QRectF br = params.board->sceneBoundingRect(); bWidth = params.res * br.width() / GraphicsUtils::SVGDPI; bHeight = params.res * br.height() / GraphicsUtils::SVGDPI; QImage * image = new QImage(qMax(svgWidth, bWidth), qMax(svgHeight, bHeight), QImage::Format_Mono); // image->setDotsPerMeterX(params.res * GraphicsUtils::InchesPerMeter); image->setDotsPerMeterY(params.res * GraphicsUtils::InchesPerMeter); image->fill(0x0); QSvgRenderer renderer(boardByteArray); QPainter painter; painter.begin(image); painter.setRenderHint(QPainter::Antialiasing, false); QRectF boardBounds(0, 0, params.res * params.boardImageSize.width() / GraphicsUtils::SVGDPI, params.res * params.boardImageSize.height() / GraphicsUtils::SVGDPI); DebugDialog::debug("boardbounds", boardBounds); renderer.render(&painter, boardBounds); painter.end(); #ifndef QT_NO_DEBUG image->save(FolderUtils::getTopLevelUserDataStorePath() + "/testGroundFillBoard.png"); #endif DRC::extendBorder(BORDERINCHES * params.res, image); GraphicsUtils::drawBorder(image, BORDERINCHES * params.res); QImage boardImage = image->copy(); /* for (double m = 0; m < BORDERINCHES; m += (1.0 / params.res)) { // 1 mm QList points; collectBorderPoints(*image, points); #ifndef QT_NO_DEBUG // for debugging //double pixelFactor = GraphicsUtils::StandardFritzingDPI / res; //QPolygon polygon; //foreach(QPoint p, points) { // polygon.append(QPoint(p.x() * pixelFactor, p.y() * pixelFactor)); //} //QList polygons; //polygons.append(polygon); //QPointF offset; //this //QString pSvg = makePolySvg(polygons, res, bWidth, bHeight, pixelFactor, "#ffffff", false, NULL, QSizeF(0,0), 0, QPointF(0, 0)); #endif foreach (QPoint p, points) image->setPixel(p, 0); } */ #ifndef QT_NO_DEBUG image->save(FolderUtils::getTopLevelUserDataStorePath() + "/testGroundFillBoardBorder.png"); #endif QSvgRenderer renderer2(copperByteArray); painter.begin(image); painter.setRenderHint(QPainter::Antialiasing, false); QRectF bounds(0, 0, params.res * params.copperImageSize.width() / GraphicsUtils::SVGDPI, params.res * params.copperImageSize.height() / GraphicsUtils::SVGDPI); DebugDialog::debug("copperbounds", bounds); renderer2.render(&painter, bounds); painter.end(); #ifndef QT_NO_DEBUG image->save(FolderUtils::getTopLevelUserDataStorePath() + "/testGroundFillCopper.png"); #endif emit postImageSignal(this, image, &boardImage, params.board, &rects); return image; } void GroundPlaneGenerator::scanImage(QImage & image, double bWidth, double bHeight, double pixelFactor, double res, const QString & colorString, bool makeConnectorFlag, bool makeOffset, QSizeF minAreaInches, double minDimensionInches, QPointF polygonOffset) { QList rects; scanLines(image, bWidth, bHeight, rects); QList< QList * > pieces; splitScanLines(rects, pieces); foreach (QList * piece, pieces) { QList polygons; QList newRects; foreach (int i, *piece) { QRect r = rects.at(i); newRects.append(QRect(r.x() * pixelFactor, r.y() * pixelFactor, (r.width() * pixelFactor) + 1, pixelFactor + 1)); // + 1 is for off-by-one converting rects to polys } // note: there is always one joinScanLines(newRects, polygons); makePolySvg(polygons, res, bWidth, bHeight, pixelFactor, colorString, makeConnectorFlag, makeOffset, minAreaInches, minDimensionInches, polygonOffset); } /* QString newSvg = QString("\n") .arg(bWidth / res) .arg(bHeight / res) .arg(bWidth * MILS) .arg(bHeight * MILS); newSvg += "\n"; // ?split each line into two lines (l1, l2) and add a terminal point at the left of l1 and the right of l2? ix = 0; foreach (QRectF r, rects) { newSvg += QString("\n") .arg(r.left() * MILS) .arg(r.top() * MILS) .arg(r.width() * MILS) .arg(MILS) .arg(ix++). .arg(ViewLayer::Copper0Color); } newSvg += "\n\n"; */ } void GroundPlaneGenerator::scanLines(QImage & image, int bWidth, int bHeight, QList & rects) { if (m_minRiseSize > 1) { for (int x = 0; x < bWidth; x++) { bool inWhite = false; int whiteStart = 0; for (int y = 0; y < bHeight; y++) { QRgb current = image.pixel(x, y); if (inWhite) { if (current == 0xffffffff) { // qBlue(current) == 0xff gray > 128 // another white pixel, keep moving continue; } // got black: close up this segment; inWhite = false; if (y - whiteStart < m_minRiseSize) { for (int j = whiteStart; j <= y; j++) { image.setPixel(x, j, 0); } continue; } } else { if (current != 0xffffffff) { // qBlue(current) != 0xff // another black pixel, keep moving continue; } inWhite = true; whiteStart = y; } } if (inWhite) { // close up the last segment if (bHeight - whiteStart < m_minRiseSize) { for (int j = whiteStart; j <= bHeight; j++) { image.setPixel(x, j, 0); } } } } } // threshold should be between 0 and 255 exclusive; smaller will include more of the svg for (int y = 0; y < bHeight; y++) { bool inWhite = false; int whiteStart = 0; for (int x = 0; x < bWidth; x++) { QRgb current = image.pixel(x, y); //if (current != 0xff000000 && current != 0xffffffff) { //DebugDialog::debug(QString("current %1").arg(current,0,16)); //} //DebugDialog::debug(QString("current %1 %2").arg(current,0,16).arg(gray, 0, 16)); if (inWhite) { if (current == 0xffffffff) { // qBlue(current) == 0xff gray > 128 // another white pixel, keep moving continue; } // got black: close up this segment; inWhite = false; if (x - whiteStart < m_minRunSize) { // not a big enough section continue; } rects.append(QRect(whiteStart, y, x - whiteStart, 1)); } else { if (current != 0xffffffff) { // qBlue(current) != 0xff // another black pixel, keep moving continue; } inWhite = true; whiteStart = x; } } if (inWhite) { // close up the last segment if (bWidth - whiteStart >= m_minRunSize) { rects.append(QRect(whiteStart, y, bWidth - whiteStart, 1)); } } } } void GroundPlaneGenerator::splitScanLines(QList & rects, QList< QList * > & pieces) { // combines vertically adjacent scanlines into "pieces" int ix = 0; int prevFirst = -1; int prevLast = -1; while (ix < rects.count()) { int first = ix; QRectF firstR = rects.at(ix); while (++ix < rects.count()) { QRectF nextR = rects.at(ix); if (nextR.y() != firstR.y()) { break; } } int last = ix - 1; // this was a lookahead so step back one if (prevFirst >= 0) { for (int i = first; i <= last; i++) { QRectF candidate = rects.at(i); int gotCount = 0; for (int j = prevFirst; j <= prevLast; j++) { QRectF prev = rects.at(j); if (prev.y() + 1 != candidate.y()) { // skipped a line; no intersection possible break; } if ((prev.x() + prev.width() <= candidate.x()) || (candidate.x() + candidate.width() <= prev.x())) { // candidate and prev didn't intersect continue; } if (++gotCount > 1) { QList * piecei = NULL; QList * piecej = NULL; foreach (QList * piece, pieces) { if (piece->contains(j)) { piecej = piece; break; } } foreach (QList * piece, pieces) { if (piece->contains(i)) { piecei = piece; break; } } if (piecei != NULL && piecej != NULL) { if (piecei != piecej) { foreach (int b, *piecej) { piecei->append(b); } piecej->clear(); pieces.removeOne(piecej); delete piecej; } piecei->append(i); } else { DebugDialog::debug("we are really screwed here, what should we do about it?"); } } else { // put the candidate (i) in j's piece foreach (QList * piece, pieces) { if (piece->contains(j)) { piece->append(i); break; } } } } if (gotCount == 0) { // candidate is an orphan line at this point QList * piece = new QList; piece->append(i); pieces.append(piece); } } } else { for (int i = first; i <= last; i++) { QList * piece = new QList; piece->append(i); pieces.append(piece); } } prevFirst = first; prevLast = last; } foreach (QList * piece, pieces) { qSort(*piece); } } void GroundPlaneGenerator::joinScanLines(QList & rects, QList & polygons) { QList< QList * > pieces; int ix = 0; int prevFirst = -1; int prevLast = -1; while (ix < rects.count()) { int first = ix; QRectF firstR = rects.at(ix); while (++ix < rects.count()) { QRectF nextR = rects.at(ix); if (nextR.y() != firstR.y()) { break; } } int last = ix - 1; if (prevFirst >= 0) { QVector holdPrevs(last - first + 1); QVector gotCounts(last - first + 1); for (int i = first; i <= last; i++) { int index = i - first; holdPrevs[index] = 0; gotCounts[index] = 0; QRectF candidate = rects.at(i); for (int j = prevFirst; j <= prevLast; j++) { QRectF prev = rects.at(j); if ((prev.x() + prev.width() <= candidate.x()) || (candidate.x() + candidate.width() <= prev.x())) { // candidate and prev didn't intersect continue; } holdPrevs[index] = j; gotCounts[index]++; } if (gotCounts[index] > 1) { holdPrevs[index] = -1; // clear this to allow one of the others in this scanline to capture a previous } } for (int i = first; i <= last; i++) { int index = i - first; bool gotOne = false; if (gotCounts[index] == 1) { bool unique = true; for (int j = first; j <= last; j++) { if (j - first == index) continue; // don't compare against yourself if (holdPrevs[index] == holdPrevs[j - first]) { unique = false; break; } } if (unique) { // add this to the previous chunk gotOne = true; foreach (QList * piece, pieces) { if (piece->contains(holdPrevs[index])) { piece->append(i); break; } } } } if (!gotOne) { // start a new chunk holdPrevs[index] = -1; // allow others to capture the prev QList * piece = new QList; piece->append(i); pieces.append(piece); } } } else { for (int i = first; i <= last; i++) { QList * piece = new QList; piece->append(i); pieces.append(piece); } } prevFirst = first; prevLast = last; } foreach (QList * piece, pieces) { //QPolygon poly(rects.at(piece->at(0)), true); //for (int i = 1; i < piece->length(); i++) { //QPolygon temp(rects.at(piece->at(i)), true); //poly = poly.united(temp); //} // no need to close polygon; SVG automatically closes path QPolygon poly; // left side for (int i = 0; i < piece->length(); i++) { QRect r = rects.at(piece->at(i)); if ((poly.count() > 0) && (poly.last().x() == r.left())) { poly.pop_back(); } else { poly.append(QPoint(r.left(), r.top())); } poly.append(QPoint(r.left(), r.bottom())); } // right side for (int i = piece->length() - 1; i >= 0; i--) { QRect r = rects.at(piece->at(i)); if ((poly.count() > 0) && (poly.last().x() == r.right())) { poly.pop_back(); } else { poly.append(QPoint(r.right(), r.bottom())); } poly.append(QPoint(r.right(), r.top())); } polygons.append(poly); delete piece; } } void GroundPlaneGenerator::makePolySvg(QList & polygons, double res, double bWidth, double bHeight, double pixelFactor, const QString & colorString, bool makeConnectorFlag, bool makeOffset, QSizeF minAreaInches, double minDimensionInches, QPointF polygonOffset) { QPointF offset; QString pSvg = makePolySvg(polygons, res, bWidth, bHeight, pixelFactor, colorString, makeConnectorFlag, makeOffset ? &offset : NULL, minAreaInches, minDimensionInches, polygonOffset); if (pSvg.isEmpty()) return; m_newSVGs.append(pSvg); if (makeOffset) { offset *= GraphicsUtils::SVGDPI; m_newOffsets.append(offset); // offset now in pixels } /* QFile file4("testPoly.svg"); file4.open(QIODevice::WriteOnly); QTextStream out4(&file4); out4 << pSvg; file4.close(); */ } QString GroundPlaneGenerator::makePolySvg(QList & polygons, double res, double bWidth, double bHeight, double pixelFactor, const QString & colorString, bool makeConnectorFlag, QPointF * offset, QSizeF minAreaInches, double minDimensionInches, QPointF polygonOffset) { int minX = 0; int minY = 0; if (offset != NULL) { minY = std::numeric_limits::max(); int maxY = std::numeric_limits::min(); minX = minY; int maxX = maxY; foreach (QPolygon polygon, polygons) { foreach (QPoint p, polygon) { if (p.x() > maxX) maxX = p.x(); if (p.x() < minX) minX = p.x(); if (p.y() > maxY) maxY = p.y(); if (p.y() < minY) minY = p.y(); } } bWidth = (maxX - minX) / pixelFactor; bHeight = (maxY - minY) / pixelFactor; offset->setX(minX / (res * pixelFactor)); // inches offset->setY(minY / (res * pixelFactor)); // inches } if ((bWidth / res < minAreaInches.width()) && (bHeight / res < minAreaInches.height())) { return ""; } if ((bWidth / res < minDimensionInches) || (bHeight / res < minDimensionInches)) { return ""; } QString pSvg = QString("\n") .arg(bWidth / res) .arg(bHeight / res) .arg(bWidth * pixelFactor) .arg(bHeight * pixelFactor); QString transform; if (polygonOffset.x() != 0 || polygonOffset.y() != 0) { transform = QString("transform='translate(%1, %2)'").arg(polygonOffset.x()).arg(polygonOffset.y()); } pSvg += QString("\n").arg(m_layerName).arg(transform); if (makeConnectorFlag) { makeConnector(polygons, res, pixelFactor, colorString, minX, minY, pSvg); } else { foreach (QPolygon poly, polygons) { pSvg += makeOnePoly(poly, colorString, "", minX, minY); } } pSvg += "\n\n"; return pSvg; } void GroundPlaneGenerator::makeConnector(QList & polygons, double res, double pixelFactor, const QString & colorString, int minX, int minY, QString & pSvg) { // see whether the standard circular connector will fit somewhere inside a polygon: // http://stackoverflow.com/questions/4279478/maximum-circle-inside-a-non-convex-polygon // or maybe this is useful, e.g. treating the circle as a square: // http://stackoverflow.com/questions/4833802/check-if-polygon-is-inside-a-polygon // code presently uses a version of the Poles of Inaccessibility algorithm: static const double standardConnectorWidth = .075; // inches double targetDiameter = res * pixelFactor * standardConnectorWidth; double targetDiameterAnd = targetDiameter * 1.25; double targetRadius = targetDiameter / 2; double targetRadiusAnd = targetDiameterAnd / 2; double targetRadiusAndSquared = targetRadiusAnd * targetRadiusAnd; foreach (QPolygon poly, polygons) { QRect boundingRect = poly.boundingRect(); if (boundingRect.width() < targetDiameterAnd) continue; if (boundingRect.height() < targetDiameterAnd) continue; QList polyLines; int count = poly.count(); for (int i = 0; i < count; i++) { QLineF lp(poly[i], poly[(i + 1) % count]); polyLines.append(lp); } int xDivisor = qRound(boundingRect.width() / targetRadius); int yDivisor = qRound(boundingRect.height() / targetRadius); double dx = (boundingRect.width() - targetDiameterAnd) / xDivisor; double dy = (boundingRect.height() - targetDiameterAnd) / yDivisor; double x; double y = boundingRect.top() + targetRadiusAnd - dy; for (int iy = 0; iy <= yDivisor; iy++) { y += dy; x = boundingRect.left() + targetRadiusAnd - dx; for (int ix = 0; ix <= xDivisor; ix++) { x += dx; if (!poly.containsPoint(QPoint(qRound(x), qRound(y)), Qt::OddEvenFill)) continue; bool gotOne = true; foreach (QLineF line, polyLines) { double distance, dx, dy; bool atEndpoint; GraphicsUtils::distanceFromLine(x, y, line.p1().x(), line.p1().y(), line.p2().x(), line.p2().y(), dx, dy, distance, atEndpoint); if (distance <= targetRadiusAndSquared) { gotOne = false; break; } } if (!gotOne) continue; foreach (QPolygon poly, polygons) { pSvg += makeOnePoly(poly, colorString, "", minX, minY); } pSvg += QString("\n") .arg(ConnectorName) .arg(x - minX) .arg(y - minY) .arg(targetRadius) .arg(colorString); return; } } } // couldn't find anything big enough above, so // try to find a poly with an area that's big enough to click, but not so big as to get in the way int useIndex = -1; QList areas; double divisor = res * pixelFactor * res * pixelFactor; foreach (QPolygon poly, polygons) { areas.append(calcArea(poly) / divisor); } for (int i = 0; i < areas.count(); i++) { if (areas.at(i) > 0.1 && areas.at(i) < 0.25) { useIndex = i; break; } } if (useIndex < 0) { for (int i = 0; i < areas.count(); i++) { if (areas.at(i) > 0.1) { useIndex = i; break; } } } if (useIndex < 0) { pSvg += QString("\n").arg(ConnectorName); foreach (QPolygon poly, polygons) { pSvg += makeOnePoly(poly, colorString, "", minX, minY); } pSvg += ""; } else { int ix = 0; for (int i = 0; i < polygons.count(); i++) { if (i == useIndex) { // has to appear inside a g element pSvg += QString("\n").arg(ConnectorName); pSvg += makeOnePoly(polygons.at(i), colorString, "", minX, minY); pSvg += ""; } else { pSvg += makeOnePoly(polygons.at(i), colorString, FSvgRenderer::NonConnectorName + QString::number(ix++), minX, minY); } } } } double GroundPlaneGenerator::calcArea(QPolygon & poly) { double total = 0; for (int ix = 0; ix < poly.count(); ix++) { QPoint p0 = poly.at(ix); QPoint p1 = poly.at((ix + 1) % poly.count()); total += (p0.x() * p1.y() - p1.x() * p0.y()); } return qAbs(total / 2.0); } QString GroundPlaneGenerator::makeOnePoly(const QPolygon & poly, const QString & colorString, const QString & id, int minX, int minY) { QString idString; if (!id.isEmpty()) { idString = QString("id='%1'").arg(id); } QString polyString = QString("\n"; return polyString; } const QStringList & GroundPlaneGenerator::newSVGs() { return m_newSVGs; } const QList & GroundPlaneGenerator::newOffsets() { return m_newOffsets; } void removeRedundant(QList & points) { QPoint current = points.last(); int ix = points.count() - 2; int soFar = 1; while (ix > 0) { if (points.at(ix).x() != current.x()) { current = points.at(ix--); soFar = 1; continue; } if (++soFar > 2) { points.removeAt(ix + 1); } ix--; } } bool GroundPlaneGenerator::collectBorderPoints(QImage & image, QList & points) { // background is black int currentX = 0, currentY = 0; bool gotSomething = false; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb current = image.pixel(x, y); if (current != 0xffffffff) { // another black pixel, keep moving continue; } currentX = x; currentY = y; //DebugDialog::debug(QString("first point %1 %2").arg(currentX).arg(currentY)); points.append(QPoint(currentX, currentY)); gotSomething = true; break; } if (gotSomething) break; } if (!gotSomething) { DebugDialog::debug("first border point not found"); return false; } bool done = false; long maxPoints = image.height() * image.width() / 2; for (long inc = 0; inc < maxPoints; inc++) { if (try8(currentX, currentY, image, points)) ; else { QPoint p = points.first(); if (qAbs(p.x() - currentX) < 4 && qAbs(p.y() - currentY) < 4) { // we're near the beginning again done = true; break; } bool keepGoing = false; for (int ix = points.count() - 2; ix >= 0; ix--) { QPoint p = points.at(ix); if (try8(p.x(), p.y(), image, points)) { keepGoing = true; break; } } if (!keepGoing) break; } QPoint p = points.last(); currentX = p.x(); currentY = p.y(); //DebugDialog::debug(QString("next point %1 %2").arg(currentX).arg(currentY)); //if (inc % 100 == 0) { //DebugDialog::debug("\n"); //} } return done; } void GroundPlaneGenerator::scanOutline(QImage & image, double bWidth, double bHeight, double pixelFactor, double res, const QString & colorString, bool makeConnectorFlag, bool makeOffset, QSizeF minAreaInches, double minDimensionInches) { QList points; bool result = collectBorderPoints(image, points); if (points.count() == 0 || !result) { DebugDialog::debug("no border points"); return; } removeRedundant(points); QPolygon polygon; foreach(QPoint p, points) { polygon.append(QPoint(p.x() * pixelFactor, p.y() * pixelFactor)); } QList polygons; polygons.append(polygon); makePolySvg(polygons, res, bWidth, bHeight, pixelFactor, colorString, makeConnectorFlag, makeOffset, minAreaInches, minDimensionInches, QPointF(0,0)); /* QFile file4("testPoly.svg"); file4.open(QIODevice::WriteOnly); QTextStream out4(&file4); out4 << pSvg; file4.close(); */ } bool GroundPlaneGenerator::try8(int x, int y, QImage & image, QList & points) { if (tryNextPoint(x, y + 1, image, points)) return true; else if (tryNextPoint(x + 1, y, image, points)) return true; else if (tryNextPoint(x, y - 1, image, points)) return true; else if (tryNextPoint(x - 1, y, image, points)) return true; else if (tryNextPoint(x + 1, y + 1, image, points)) return true; else if (tryNextPoint(x - 1, y + 1, image, points)) return true; else if (tryNextPoint(x + 1, y - 1, image, points)) return true; else if (tryNextPoint(x - 1, y - 1, image, points)) return true; return false; } bool GroundPlaneGenerator::tryNextPoint(int x, int y, QImage & image, QList & points) { if (x < 0) return false; if (y < 0) return false; if (x >= image.width()) return false; if (y >= image.height()) return false; foreach (QPoint p, points) { if (p.x() == x && p.y() == y) { // already visited return false; } if (qAbs(p.x() - x) > 3 && qAbs(p.y() - y) > 3) { // too far away from the start of the polygon break; } } for (int i = points.count() - 1; i >= 0; i--) { QPoint p = points.at(i); if (p.x() == x && p.y() == y) { // already visited return false; } if (qAbs(p.x() - x) > 3 && qAbs(p.y() - y) > 3) { // too far away from from the current point break; } } QRgb pixel = image.pixel(x, y); //DebugDialog::debug(QString("pixel %1,%2 %3").arg(x).arg(y).arg(pixel, 0, 16)); if (pixel != 0xffffffff) { // empty pixel, not on the border return false; } if (x + 1 == image.width()) { points.append(QPoint(x, y)); return true; } pixel = image.pixel(x + 1, y); if (pixel != 0xffffffff) { points.append(QPoint(x, y)); return true; } if (y + 1 == image.height()) { points.append(QPoint(x, y)); return true; } pixel = image.pixel(x, y + 1); if (pixel != 0xffffffff) { points.append(QPoint(x, y)); return true; } if (x - 1 < 0) { points.append(QPoint(x, y)); return true; } pixel = image.pixel(x - 1, y); if (pixel != 0xffffffff) { points.append(QPoint(x, y)); return true; } if (y - 1 < 0) { points.append(QPoint(x, y)); return true; } pixel = image.pixel(x, y - 1); if (pixel != 0xffffffff) { points.append(QPoint(x, y)); return true; } return false; } void GroundPlaneGenerator::setStrokeWidthIncrement(double swi) { m_strokeWidthIncrement = swi; } void GroundPlaneGenerator::setLayerName(const QString & layerName) { m_layerName = layerName; } const QString & GroundPlaneGenerator::layerName() { return m_layerName; } void GroundPlaneGenerator::setMinRunSize(int mrus, int mris) { m_minRunSize = mrus; m_minRiseSize = mris; } QString GroundPlaneGenerator::mergeSVGs(const QString & initialSVG, const QString & layerName) { QDomDocument doc; if (!initialSVG.isEmpty()) { TextUtils::mergeSvg(doc, initialSVG, layerName); } foreach (QString newSvg, m_newSVGs) { TextUtils::mergeSvg(doc, newSvg, layerName); } return TextUtils::mergeSvgFinish(doc); } fritzing-app-0.9.3b/src/svg/groundplanegenerator.h000066400000000000000000000113721272363601300222720ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6941 $: $Author: irascibl@gmail.com $: $Date: 2013-03-26 15:03:18 +0100 (Di, 26. Mrz 2013) $ ********************************************************************/ #ifndef GROUNDPLANEGENERATOR_H #define GROUNDPLANEGENERATOR_H #include #include #include #include #include #include #include struct GPGParams { QString boardSvg; QSizeF boardImageSize; QString svg; QSizeF copperImageSize; QStringList exceptions; QGraphicsItem * board; double res; QString color; double keepoutMils; }; class GroundPlaneGenerator : public QObject { Q_OBJECT public: GroundPlaneGenerator(); ~GroundPlaneGenerator(); bool generateGroundPlane(const QString & boardSvg, QSizeF boardImageSize, const QString & svg, QSizeF copperImageSize, QStringList & exceptions, QGraphicsItem * board, double res, const QString & color, double keepoutMils); bool generateGroundPlaneUnit(const QString & boardSvg, QSizeF boardImageSize, const QString & svg, QSizeF copperImageSize, QStringList & exceptions, QGraphicsItem * board, double res, const QString & color, QPointF whereToStart, double keepoutMils); void scanImage(QImage & image, double bWidth, double bHeight, double pixelFactor, double res, const QString & colorString, bool makeConnector, bool makeOffset, QSizeF minAreaInches, double minDimensionInches, QPointF offsetPolygons); void scanOutline(QImage & image, double bWidth, double bHeight, double pixelFactor, double res, const QString & colorString, bool makeConnector, bool makeOffset, QSizeF minAreaInches, double minDimensionInches); void scanLines(QImage & image, int bWidth, int bHeight, QList & rects); bool getBoardRects(const QByteArray & boardByteArray, QGraphicsItem * board, double res, double keepoutSpace, QList & rects); const QStringList & newSVGs(); const QList & newOffsets(); void setStrokeWidthIncrement(double); void setLayerName(const QString &); const QString & layerName(); void setMinRunSize(int minRunSize, int minRiseSize); QString mergeSVGs(const QString & initialSVG, const QString & layerName); public: static QString ConnectorName; signals: void postImageSignal(GroundPlaneGenerator *, QImage * copperImage, QImage * boardImage, QGraphicsItem * board, QList *); protected: void splitScanLines(QList & rects, QList< QList * > & pieces); void joinScanLines(QList & rects, QList & polygons); QString makePolySvg(QList & polygons, double res, double bWidth, double bHeight, double pixelFactor, const QString & colorString, bool makeConnectorFlag, QPointF * offset, QSizeF minAreaInches, double minDimensionInches, QPointF polygonOffset); void makePolySvg(QList & polygons, double res, double bWidth, double bHeight, double pixelFactor, const QString & colorString, bool makeConnectorFlag, bool makeOffset, QSizeF minAreaInches, double minDimensionInches, QPointF polygonOffset); QString makeOnePoly(const QPolygon & poly, const QString & colorString, const QString & id, int minX, int minY); double calcArea(QPolygon & poly); QImage * generateGroundPlaneAux(GPGParams &, double & bWidth, double & bHeight, QList &); void makeConnector(QList & polygons, double res, double pixelFactor, const QString & colorString, int minX, int minY, QString & svg); bool tryNextPoint(int x, int y, QImage & image, QList & points); bool collectBorderPoints(QImage & image, QList & points); bool try8(int x, int y, QImage & image, QList & points); bool generateGroundPlaneFn(GPGParams &); protected: QStringList m_newSVGs; QList m_newOffsets; double m_blurBy; QString m_layerName; double m_strokeWidthIncrement; int m_minRunSize; int m_minRiseSize; public: static const QString KeepoutSettingName; static const double KeepoutDefaultMils; }; #endif fritzing-app-0.9.3b/src/svg/kicad2svg.cpp000066400000000000000000000053311272363601300202530ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "kicad2svg.h" #include "../utils/textutils.h" #include "../version/version.h" #include #include #include Kicad2Svg::Kicad2Svg() { } QString Kicad2Svg::makeMetadata(const QString & filename, const QString & type, const QString & name) { QFileInfo fileInfo(filename); QDateTime now = QDateTime::currentDateTime(); QString dt = now.toString("dd/MM/yyyy hh:mm:ss"); m_title = QString("%1\n").arg(fileInfo.fileName()); m_description = QString("Kicad %3 '%2' from file '%1' converted by Fritzing\n") .arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(fileInfo.fileName()))) .arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(name))) .arg(type); QString metadata("\n"); metadata += ""; metadata += "\n"; metadata += m_attribute.arg("kicad filename").arg(fileInfo.fileName()); metadata += m_attribute.arg(QString("kicad %1").arg(type)) .arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(name))); metadata += m_attribute.arg("fritzing version").arg(Version::versionString()); metadata += m_attribute.arg("conversion date").arg(dt); metadata += m_attribute.arg("dist-license").arg("GPL"); metadata += m_attribute.arg("use-license").arg("unlimited"); metadata += m_attribute.arg("author").arg("KICAD project"); metadata += m_attribute.arg("license-url").arg("http://www.gnu.org/licenses/gpl.html"); return metadata; } QString Kicad2Svg::endMetadata() { QString metadata = ""; metadata += ""; metadata += ""; return metadata; } fritzing-app-0.9.3b/src/svg/kicad2svg.h000066400000000000000000000024451272363601300177230ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef KICAD2SVG_H #define KICAD2SVG_H #include "x2svg.h" class Kicad2Svg : public X2Svg { public: Kicad2Svg(); QString makeMetadata(const QString & filename, const QString & type, const QString & name); QString endMetadata(); protected: QString m_title; QString m_description; }; #endif // KICAD2SVG_H fritzing-app-0.9.3b/src/svg/kicadmodule2svg.cpp000066400000000000000000000667431272363601300214770ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "kicadmodule2svg.h" #include "../utils/textutils.h" #include "../debugdialog.h" #include "../viewlayer.h" #include "../fsvgrenderer.h" #include #include #include #include #include #include #include #include #include #define KicadSilkscreenTop 21 #define KicadSilkscreenBottom 20 // TODO: // non-centered drill holes? // trapezoidal pads (shape delta may or may not be a separate issue)? // non-copper holes? // find true bounding box of arcs instead of using the whole circle double checkStrokeWidth(double w) { if (w >= 0) return w; DebugDialog::debug("stroke width < 0"); return 0; } KicadModule2Svg::KicadModule2Svg() : Kicad2Svg() { } QStringList KicadModule2Svg::listModules(const QString & filename) { QStringList modules; QFile file(filename); if (!file.open(QFile::ReadOnly)) return modules; QTextStream textStream(&file); bool gotIndex = false; while (true) { QString line = textStream.readLine(); if (line.isNull()) break; if (line.compare("$INDEX") == 0) { gotIndex = true; break; } } if (!gotIndex) return modules; while (true) { QString line = textStream.readLine(); if (line.isNull()) break; if (line.compare("$EndINDEX") == 0) { return modules; } modules.append(line); } modules.clear(); return modules; } QString KicadModule2Svg::convert(const QString & filename, const QString & moduleName, bool allowPadsAndPins) { m_nonConnectorNumber = 0; initLimits(); QFile file(filename); if (!file.open(QFile::ReadOnly)) { throw QObject::tr("unable to open %1").arg(filename); } QString text; QTextStream textStream(&file); QString metadata = makeMetadata(filename, "module", moduleName); bool gotModule = false; while (true) { QString line = textStream.readLine(); if (line.isNull()) { break; } if (line.contains("$MODULE") && line.contains(moduleName, Qt::CaseInsensitive)) { gotModule = true; break; } } if (!gotModule) { throw QObject::tr("footprint %1 not found in %2").arg(moduleName).arg(filename); } bool gotT0; QString line; while (true) { line = textStream.readLine(); if (line.isNull()) { throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename); } if (line.startsWith("T0") || line.startsWith("DS") || line.startsWith("DA") || line.startsWith("DC")) { gotT0 = true; break; } else if (line.startsWith("Cd")) { metadata += m_comment.arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(line.remove(0,3)))); } else if (line.startsWith("Kw")) { QStringList keywords = line.split(" "); for (int i = 1; i < keywords.count(); i++) { metadata += m_attribute.arg("keyword").arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(keywords[i]))); } } } metadata += endMetadata(); if (!gotT0) { throw QObject::tr("unexpected format (1) in %1 from %2").arg(moduleName).arg(filename); } while (line.startsWith("T")) { line = textStream.readLine(); if (line.isNull()) { throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename); } } bool done = false; QString copper0; QString copper1; QString silkscreen0; QString silkscreen1; while (true) { if (line.startsWith("$PAD")) break; if (line.startsWith("$EndMODULE")) { done = true; break; } int layer = 0; QString svgElement; if (line.startsWith("DS")) { layer = drawDSegment(line, svgElement); } else if (line.startsWith("DA")) { layer = drawDArc(line, svgElement); } else if (line.startsWith("DC")) { layer = drawDCircle(line, svgElement); } switch (layer) { case KicadSilkscreenTop: silkscreen1 += svgElement; break; case KicadSilkscreenBottom: silkscreen0 += svgElement; break; default: break; } line = textStream.readLine(); if (line.isNull()) { throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename); } } if (!done) { QList numbers; for (int i = 0; i < 512; i++) { numbers << i; } int pads = 0; int pins = 0; while (!done) { try { QString pad; PadLayer padLayer = convertPad(textStream, pad, numbers); switch (padLayer) { case ToCopper0: copper0 += pad; pins++; break; case ToCopper1: copper1 += pad; pads++; break; default: break; } } catch (const QString & msg) { DebugDialog::debug(QString("kicad pad %1 conversion failed in %2: %3").arg(moduleName).arg(filename).arg(msg)); } while (true) { line = textStream.readLine(); if (line.isNull()) { throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename); } if (line.contains("$SHAPE3D")) { done = true; break; } if (line.contains("$EndMODULE")) { done = true; break; } if (line.contains("$PAD")) { break; } } } if (!allowPadsAndPins && pins > 0 && pads > 0) { throw QObject::tr("Sorry, Fritzing can't yet handle both pins and pads together (in %1 in %2)").arg(moduleName).arg(filename); } } if (!copper0.isEmpty()) { copper0 = offsetMin("\n" + copper0 + "\n"); } if (!copper1.isEmpty()) { copper1 = offsetMin("\n" + copper1 + "\n"); } if (!silkscreen1.isEmpty()) { silkscreen1 = offsetMin("\n" + silkscreen1 + "\n"); } if (!silkscreen0.isEmpty()) { silkscreen0 = offsetMin("\n" + silkscreen0 + "\n"); } QString svg = TextUtils::makeSVGHeader(10000, 10000, m_maxX - m_minX, m_maxY - m_minY) + m_title + m_description + metadata + copper0 + copper1 + silkscreen0 + silkscreen1 + ""; return svg; } int KicadModule2Svg::drawDCircle(const QString & ds, QString & circle) { // DC Xcentre Ycentre Xend Yend width layer QStringList params = ds.split(" "); if (params.count() < 7) return -1; int cx = params.at(1).toInt(); int cy = params.at(2).toInt(); int x2 = params.at(3).toInt(); int y2 = params.at(4).toInt(); double radius = qSqrt((cx - x2) * (cx - x2) + (cy - y2) * (cy - y2)); int w = params.at(5).toInt(); double halfWidth = w / 2.0; checkXLimit(cx + radius + halfWidth); checkXLimit(cx - radius - halfWidth); checkYLimit(cy + radius + halfWidth); checkYLimit(cy - radius - halfWidth); int layer = params.at(6).toInt(); circle = QString("") .arg(cx) .arg(cy) .arg(radius) .arg(checkStrokeWidth(w)); return layer; } int KicadModule2Svg::drawDSegment(const QString & ds, QString & line) { // DS Xstart Ystart Xend Yend Width Layer QStringList params = ds.split(" "); if (params.count() < 7) return -1; int x1 = params.at(1).toInt(); int y1 = params.at(2).toInt(); int x2 = params.at(3).toInt(); int y2 = params.at(4).toInt(); checkXLimit(x1); checkXLimit(x2); checkYLimit(y1); checkYLimit(y2); int layer = params.at(6).toInt(); line = QString("") .arg(x1) .arg(y1) .arg(x2) .arg(y2) .arg(checkStrokeWidth(params.at(5).toDouble())); return layer; } int KicadModule2Svg::drawDArc(const QString & ds, QString & arc) { //DA x0 y0 x1 y1 angle width layer QStringList params = ds.split(" "); if (params.count() < 8) return -1; int cx = params.at(1).toInt(); int cy = params.at(2).toInt(); int x2 = params.at(3).toInt(); int y2 = params.at(4).toInt(); int width = params.at(6).toInt(); double diffAngle = (params.at(5).toInt() % 3600) / 10.0; double radius = qSqrt((cx - x2) * (cx - x2) + (cy - y2) * (cy - y2)); double endAngle = asin((y2 - cy) / radius); if (x2 < cx) { endAngle += M_PI; } double startAngle = endAngle + (diffAngle * M_PI / 180.0); double x1 = (radius * cos(startAngle)) + cx; double y1 = (radius * sin(startAngle)) + cy; // TODO: figure out bounding box for circular arc and set min and max accordingly /* You have radius R, start angle S, end angle T, and I'll assume that the arc is swept counterclockwise from S to T. start.x = R * cos(S) start.y = R * sin(S) end.x = R * cos(T) end.y = R * sin(T) Determine the axis crossings by analyzing the start and end angles. For discussion sake, I'll describe angles using degrees. Provide a function, wrap(angle), that returns an angle in the range [0 to 360). cross0 = wrap(S) > wrap(T) cross90 = wrap(S-90) > wrap(T-90) cross180 = wrap(S-180) > wrap(T-180) cross270 = wrap(S-270) > wrap(T-270) Now the axis aligned bounding box is defined by: right = cross0 ? +R : max(start.x, end.x) top = cross90 ? +R : max(start.y, end.y) left = cross180 ? -R : min(start.x, end.x) bottom = cross270 ? -R : min(start.y, end.y) */ checkXLimit(cx + radius); checkXLimit(cx - radius); checkYLimit(cy + radius); checkYLimit(cy - radius); int layer = params.at(7).toInt(); arc = QString("") .arg(checkStrokeWidth(width / 2.0)) .arg(x1) .arg(y1) .arg(radius) .arg(radius) .arg(qAbs(diffAngle) >= 180 ? 1 : 0) .arg(diffAngle > 0 ? 0 : 1) .arg(x2 - x1) .arg(y2 - y1); return layer; } KicadModule2Svg::PadLayer KicadModule2Svg::convertPad(QTextStream & stream, QString & pad, QList & numbers) { PadLayer padLayer = UnableToTranslate; QStringList padStrings; while (true) { QString line = stream.readLine(); if (line.isNull()) { throw QObject::tr("unexpected end of file"); } if (line.contains("$EndPAD")) { break; } padStrings.append(line); } QString shape; QString drill; QString attributes; QString position; foreach (QString string, padStrings) { if (string.startsWith("Sh")) { shape = string; } else if (string.startsWith("Po")) { position = string; } else if (string.startsWith("At")) { attributes = string; } else if (string.startsWith("Dr")) { drill = string; } } if (drill.isEmpty()) { throw QObject::tr("pad missing drill"); } if (attributes.isEmpty()) { throw QObject::tr("pad missing attributes"); } if (position.isEmpty()) { throw QObject::tr("pad missing position"); } if (shape.isEmpty()) { throw QObject::tr("pad missing shape"); } QStringList positionStrings = position.split(" "); if (positionStrings.count() < 3) { throw QObject::tr("position missing params"); } int posX = positionStrings.at(1).toInt(); int posY = positionStrings.at(2).toInt(); QStringList drillStrings = drill.split(" "); if (drillStrings.count() < 4) { throw QObject::tr("drill missing params"); } int drillX = drillStrings.at(1).toInt(); int drillXOffset = drillStrings.at(2).toInt(); int drillYOffset = drillStrings.at(3).toInt(); int drillY = drillX; if (drillXOffset != 0 || drillYOffset != 0) { throw QObject::tr("drill offset not implemented"); } if (drillStrings.count() > 4) { if (drillStrings.at(4) == "O") { if (drillStrings.count() < 7) { throw QObject::tr("drill missing ellipse params"); } drillY = drillStrings.at(6).toInt(); } } QStringList attributeStrings = attributes.split(" "); if (attributeStrings.count() < 4) { throw QObject::tr("attributes missing params"); } bool ok; int layerMask = attributeStrings.at(3).toInt(&ok, 16); if (!ok) { throw QObject::tr("bad layer mask parameter"); } QString padType = attributeStrings.at(1); if (padType == "MECA") { // seems to be the same thing padType = "STD"; } if (padType == "STD") { padLayer = ToCopper0; } else if (padType == "SMD") { padLayer = ToCopper1; } else if (padType == "CONN") { if (layerMask & 1) { padLayer = ToCopper0; } else { padLayer = ToCopper1; } } else if (padType == "HOLE") { padLayer = ToCopper0; } else { throw QObject::tr("Sorry, can't handle pad type %1").arg(padType); } QStringList shapeStrings = shape.split(" "); if (shapeStrings.count() < 8) { throw QObject::tr("pad shape missing params"); } QString padName = unquote(shapeStrings.at(1)); int padNumber = padName.toInt(&ok) - 1; if (!ok) { padNumber = padName.isEmpty() ? -1 : numbers.takeFirst(); //DebugDialog::debug(QString("name:%1 padnumber %2").arg(padName).arg(padNumber)); } else { numbers.removeOne(padNumber); } QString shapeIdentifier = shapeStrings.at(2); int xSize = shapeStrings.at(3).toInt(); int ySize = shapeStrings.at(4).toInt(); if (ySize <= 0) { DebugDialog::debug(QString("ySize is zero %1").arg(padName)); ySize = xSize; } if (xSize <= 0) { throw QObject::tr("pad shape size is invalid"); } int xDelta = shapeStrings.at(5).toInt(); int yDelta = shapeStrings.at(6).toInt(); int orientation = shapeStrings.at(7).toInt(); if (shapeIdentifier == "T") { throw QObject::tr("trapezoidal pads not implemented"); // eventually polygon? } if (xDelta != 0 || yDelta != 0) { // note: so far, all cases of non-zero delta go with shape "T" throw QObject::tr("shape delta not implemented"); } if (padType == "HOLE") { if (shapeIdentifier != "C") { throw QObject::tr("non-circular holes not implemented"); } if (drillX == xSize) { throw QObject::tr("non-copper holes not implemented"); } } if (shapeIdentifier == "C") { checkLimits(posX, xSize, posY, ySize); pad += drawCPad(posX, posY, xSize, ySize, drillX, drillY, padName, padNumber, padType, padLayer); } else if (shapeIdentifier == "R") { checkLimits(posX, xSize, posY, ySize); pad += drawRPad(posX, posY, xSize, ySize, drillX, drillY, padName, padNumber, padType, padLayer); } else if (shapeIdentifier == "O") { checkLimits(posX, xSize, posY, ySize); QString id = getID(padNumber, padLayer); pad += QString("") .arg(id).arg(padName) + drawOblong(posX, posY, xSize, ySize, drillX, drillY, padType, padLayer) + ""; } else { throw QObject::tr("unable to handle pad shape %1").arg(shapeIdentifier); } if (orientation != 0) { if (orientation < 0) { orientation = (orientation % 3600) + 3600; } orientation = 3600 - (orientation % 3600); QTransform t = QTransform().translate(-posX, -posY) * QTransform().rotate(orientation / 10.0) * QTransform().translate(posX, posY); pad = TextUtils::svgTransform(pad, t, true, QString("_x='%1' _y='%2' _r='%3'").arg(posX).arg(posY).arg(orientation / 10.0)); } return padLayer; } QString KicadModule2Svg::drawVerticalOblong(int posX, int posY, double xSize, double ySize, int drillX, int drillY, const QString & padType, KicadModule2Svg::PadLayer padLayer) { QString color = getColor(padLayer); double rad = xSize / 4.0; QString bot; if (drillX == drillY) { bot = QString("") .arg(posX - rad - rad) .arg(posY - (ySize / 2.0) + (xSize / 2.0)) .arg(rad * 2) .arg(rad * 4) .arg(color); bot += QString("") .arg(posX + rad + rad) .arg(posY + (ySize / 2.0) - (xSize / 2.0)) .arg(rad * 2) .arg(-rad * 4) .arg(color); } else { double w = (ySize - drillY) / 2.0; double newrad = rad - w / 4; bot = QString("").arg(checkStrokeWidth(drillX)); bot += QString("") .arg(posX - rad - rad + (w / 2)) .arg(posY - (ySize / 2.0) + (xSize / 2.0)) .arg(newrad * 2) .arg(newrad * 4) .arg(color) .arg(checkStrokeWidth(w)); bot += QString("") .arg(posX + rad + rad - (w / 2)) .arg(posY + (ySize / 2.0) - (xSize / 2.0)) .arg(newrad * 2) .arg(-newrad * 4) .arg(color) .arg(checkStrokeWidth(w)); bot += QString("") .arg(posX).arg(posY - (ySize / 2.0) + (xSize / 2.0)).arg(posX).arg(posY + (ySize / 2.0) - (xSize / 2.0)); bot += ""; } QString middle; if (padType == "SMD") { middle = QString("") .arg(posX - (xSize / 2.0)) .arg(posY - (ySize / 2.0) + (xSize / 2.0)) .arg(xSize) .arg(ySize - xSize) .arg(color); } else { if (drillX == drillY) { middle = QString("") .arg(posX) .arg(posY) .arg((qMin(xSize, ySize) / 2.0) - (drillX / 4.0)) .arg(checkStrokeWidth(drillX / 2.0)) .arg(color); } middle += QString("") .arg(posX - (xSize / 2.0) + (drillX / 4.0)) .arg(posY - (ySize / 2.0) + (xSize / 2.0)) .arg(posY + (ySize / 2.0) - (xSize / 2.0)) .arg(checkStrokeWidth(drillX / 2.0)) .arg(color); middle += QString("") .arg(posX + (xSize / 2.0) - (drillX / 4.0)) .arg(posY - (ySize / 2.0) + (xSize / 2.0)) .arg(posY + (ySize / 2.0) - (xSize / 2.0)) .arg(checkStrokeWidth(drillX / 2.0)) .arg(color); } return middle + bot; } QString KicadModule2Svg::drawHorizontalOblong(int posX, int posY, double xSize, double ySize, int drillX, int drillY, const QString & padType, KicadModule2Svg::PadLayer padLayer) { QString color = getColor(padLayer); double rad = ySize / 4.0; QString bot; if (drillX == drillY) { bot = QString("") .arg(posX - (xSize / 2.0) + (ySize / 2.0)) .arg(posY - rad - rad) .arg(rad * 2) .arg(rad * 4) .arg(color); bot += QString("") .arg(posX + (xSize / 2.0) - (ySize / 2.0)) .arg(posY + rad + rad) .arg(rad * 2) .arg(-rad * 4) .arg(color); } else { double w = (xSize - drillX) / 2.0; double newrad = rad - w / 4; bot = QString("").arg(checkStrokeWidth(drillY)); bot += QString("") .arg(posX - (xSize / 2.0) + (ySize / 2.0)) .arg(posY - rad - rad + (w / 2)) .arg(newrad * 2) .arg(newrad * 4) .arg(color) .arg(checkStrokeWidth(w)); bot += QString("") .arg(posX + (xSize / 2.0) - (ySize / 2.0)) .arg(posY + rad + rad - (w / 2)) .arg(newrad * 2) .arg(-newrad * 4) .arg(color) .arg(checkStrokeWidth(w)); bot += QString("") .arg(posX - (xSize / 2.0) + (ySize / 2.0)).arg(posY).arg(posX + (xSize / 2.0) - (ySize / 2.0)).arg(posY); bot += ""; } QString middle; bool gotID = false; if (padType == "SMD") { middle = QString("") .arg(posX - (xSize / 2.0) + (ySize / 2.0)) .arg(posY - (ySize / 2.0)) .arg(xSize - ySize) .arg(ySize) .arg(color); } else { if (drillX == drillY) { gotID = true; middle = QString("") .arg(posX) .arg(posY) .arg((qMin(xSize, ySize) / 2.0) - (drillY / 4.0)) .arg(checkStrokeWidth(drillY / 2.0)) .arg(color); } middle += QString("") .arg(posX - (xSize / 2.0) + (ySize / 2.0)) .arg(posY - (ySize / 2.0) + (drillY / 4.0)) .arg(posX + (xSize / 2.0) - (ySize / 2.0)) .arg(checkStrokeWidth(drillY / 2.0)) .arg(color); middle += QString("") .arg(posX - (xSize / 2.0) + (ySize / 2.0)) .arg(posY + (ySize / 2.0) - (drillY / 4.0)) .arg(posX + (xSize / 2.0) - (ySize / 2.0)) .arg(checkStrokeWidth(drillY / 2.0)) .arg(color); } return middle + bot; } void KicadModule2Svg::checkLimits(int posX, int xSize, int posY, int ySize) { checkXLimit(posX - (xSize / 2.0)); checkXLimit(posX + (xSize / 2.0)); checkYLimit(posY - (ySize / 2.0)); checkYLimit(posY + (ySize / 2.0)); } QString KicadModule2Svg::drawCPad(int posX, int posY, int xSize, int ySize, int drillX, int drillY, const QString & padName, int padNumber, const QString & padType, KicadModule2Svg::PadLayer padLayer) { QString color = getColor(padLayer); QString id = getID(padNumber, padLayer); Q_UNUSED(ySize); if (padType == "SMD") { return QString("") .arg(posX) .arg(posY) .arg(xSize / 2.0) .arg(id) .arg(color) .arg(padName); } if (drillX == drillY) { double w = (xSize - drillX) / 2.0; QString pad = QString("").arg(id).arg(padName); pad += QString("") .arg(posX) .arg(posY) .arg((drillX / 2.0) + (w / 2)) .arg(checkStrokeWidth(w)) .arg(color); if (drillX > 500) { pad += QString("") .arg(posX) .arg(posY) .arg(drillX / 2.0); } pad += ""; return pad; } QString pad = QString("").arg(id); double w = (xSize - qMax(drillX, drillY)) / 2.0; pad += QString("") .arg(posX) .arg(posY) .arg((xSize / 2.0) - (w / 2)) .arg(checkStrokeWidth(w)) .arg(color); pad += drawOblong(posX, posY, drillX + w, drillY + w, drillX, drillY, "", padLayer); // now fill the gaps between the oblong and the circle if (drillX >= drillY) { double angle = asin(((drillY + w) / 2) / (ySize / 2.0)); double opp = (ySize / 2.0) * cos(angle); pad += QString("") .arg(color) .arg(posX) .arg(posY - (ySize / 2.0)) .arg(posX - opp) .arg(posY - (drillY / 2.0)) .arg(posX + opp) .arg(posY - (drillY / 2.0)); pad += QString("") .arg(color) .arg(posX) .arg(posY + (ySize / 2.0)) .arg(posX - opp) .arg(posY + (drillY / 2.0)) .arg(posX + opp) .arg(posY + (drillY / 2.0)); } else { double angle = acos(((drillX + w) / 2) / (xSize / 2.0)); double adj = (xSize / 2.0) * sin(angle); pad += QString("") .arg(color) .arg(posX - (xSize / 2.0)) .arg(posY) .arg(posX - (drillX / 2.0)) .arg(posY - adj) .arg(posX - (drillX / 2.0)) .arg(posY + adj); pad += QString("") .arg(color) .arg(posX + (xSize / 2.0)) .arg(posY) .arg(posX + (drillX / 2.0)) .arg(posY - adj) .arg(posX + (drillX / 2.0)) .arg(posY + adj); } pad += ""; return pad; } QString KicadModule2Svg::drawRPad(int posX, int posY, int xSize, int ySize, int drillX, int drillY, const QString & padName, int padNumber, const QString & padType, KicadModule2Svg::PadLayer padLayer) { QString color = getColor(padLayer); QString id = getID(padNumber, padLayer); if (padType == "SMD") { return QString("") .arg(posX - (xSize / 2.0)) .arg(posY - (ySize / 2.0)) .arg(xSize) .arg(ySize) .arg(id) .arg(color) .arg(padName); } QString pad = QString("").arg(id).arg(padName); if (drillX == drillY) { double w = (qMin(xSize, ySize) - drillX) / 2.0; pad += QString("") .arg(posX) .arg(posY) .arg((w / 2) + (drillX / 2.0)) .arg(checkStrokeWidth(w)) .arg(color); } else { double w = (drillX >= drillY) ? (xSize - drillX) / 2.0 : (ySize - drillY) / 2.0 ; pad += QString("") .arg(posX) .arg(posY) .arg((w / 2) + (qMax(drillX, drillY) / 2.0)) .arg(checkStrokeWidth(w)) .arg(color); pad += drawOblong(posX, posY, drillX + w, drillY + w, drillX, drillY, "", padLayer); } // draw 4 lines otherwise there may be gaps if one pair of sides is much longer than the other pair of sides double w = (ySize - drillY) / 2.0; double tlx = posX - xSize / 2.0; double tly = posY - ySize / 2.0; pad += QString("") .arg(tlx) .arg(tly + w / 2) .arg(tlx + xSize) .arg(checkStrokeWidth(w)) .arg(color); pad += QString("") .arg(tlx) .arg(tly + ySize - w / 2) .arg(tlx + xSize) .arg(checkStrokeWidth(w)) .arg(color); w = (xSize - drillX) / 2.0; pad += QString("") .arg(tlx + w / 2) .arg(tly) .arg(tly + ySize) .arg(checkStrokeWidth(w)) .arg(color); pad += QString("") .arg(tlx + xSize - w / 2) .arg(tly) .arg(tly + ySize) .arg(checkStrokeWidth(w)) .arg(color); pad += ""; return pad; } QString KicadModule2Svg::drawOblong(int posX, int posY, double xSize, double ySize, int drillX, int drillY, const QString & padType, KicadModule2Svg::PadLayer padLayer) { if (xSize <= ySize) { return drawVerticalOblong(posX, posY, xSize, ySize, drillX, drillY, padType, padLayer); } else { return drawHorizontalOblong(posX, posY, xSize, ySize, drillX, drillY, padType, padLayer); } } QString KicadModule2Svg::getID(int padNumber, KicadModule2Svg::PadLayer padLayer) { if (padNumber < 0) { return QString("id='%1%2'").arg(FSvgRenderer::NonConnectorName).arg(m_nonConnectorNumber++); } return QString("id='connector%1%2'").arg(padNumber).arg((padLayer == ToCopper1) ? "pad" : "pin"); } QString KicadModule2Svg::getColor(KicadModule2Svg::PadLayer padLayer) { switch (padLayer) { case ToCopper0: return ViewLayer::Copper0Color; break; case ToCopper1: return ViewLayer::Copper1Color; break; default: DebugDialog::debug("kicad getcolor with unknown layer"); return "#FF0000"; } } fritzing-app-0.9.3b/src/svg/kicadmodule2svg.h000066400000000000000000000053421272363601300211300ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef KICADMODULE2SVG_H #define KICADMODULE2SVG_H #include #include #include #include "kicad2svg.h" class KicadModule2Svg : public Kicad2Svg { public: KicadModule2Svg(); QString convert(const QString & filename, const QString & moduleName, bool allowPadsAndPins); public: static QStringList listModules(const QString & filename); public: enum PadLayer { ToCopper0, ToCopper1, UnableToTranslate }; protected: KicadModule2Svg::PadLayer convertPad(QTextStream & stream, QString & pad, QList & numbers); int drawDSegment(const QString & ds, QString & line); int drawDArc(const QString & ds, QString & arc); int drawDCircle(const QString & ds, QString & arc); QString drawOblong(int posX, int posY, double xSize, double ySize, int drillX, int drillY, const QString & padType, KicadModule2Svg::PadLayer); QString drawVerticalOblong(int posX, int posY, double xSize, double ySize, int drillX, int drillY, const QString & padType, KicadModule2Svg::PadLayer); QString drawHorizontalOblong(int posX, int posY, double xSize, double ySize, int drillX, int drillY, const QString & padType, KicadModule2Svg::PadLayer); void checkLimits(int posX, int xSize, int posY, int ySize); QString drawRPad(int posX, int posY, int xSize, int ySize, int drillX, int drillY, const QString & padName, int padNumber, const QString & padType, KicadModule2Svg::PadLayer); QString drawCPad(int posX, int posY, int xSize, int ySize, int drillX, int drillY, const QString & padName, int padNumber, const QString & padType, KicadModule2Svg::PadLayer); QString getColor(KicadModule2Svg::PadLayer padLayer); QString getID(int padNumber, KicadModule2Svg::PadLayer padLayer); protected: int m_nonConnectorNumber; }; #endif // KICADMODULE2SVG_H fritzing-app-0.9.3b/src/svg/kicadschematic2svg.cpp000066400000000000000000000410041272363601300221310ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "kicadschematic2svg.h" #include "../utils/textutils.h" #include "../utils/graphicsutils.h" #include "../version/version.h" #include "../debugdialog.h" #include "../viewlayer.h" #include "../fsvgrenderer.h" #include "../utils/misc.h" #include #include #include #include #include #include #include // TODO: // pin shape: invert, etc. KicadSchematic2Svg::KicadSchematic2Svg() : Kicad2Svg() { } QStringList KicadSchematic2Svg::listDefs(const QString & filename) { QStringList defs; QFile file(filename); if (!file.open(QFile::ReadOnly)) return defs; QTextStream textStream(&file); while (true) { QString line = textStream.readLine(); if (line.isNull()) break; if (line.startsWith("DEF")) { QStringList linedefs = line.split(" ", QString::SkipEmptyParts); if (linedefs.count() > 1) { defs.append(linedefs[1]); } } } return defs; } QString KicadSchematic2Svg::convert(const QString & filename, const QString & defName) { initLimits(); QFile file(filename); if (!file.open(QFile::ReadOnly)) { throw QObject::tr("unable to open %1").arg(filename); } QTextStream textStream(&file); QString metadata = makeMetadata(filename, "schematic part", defName); metadata += endMetadata(); QString reference; int textOffset = 0; bool drawPinNumber = true; bool drawPinName = true; bool gotDef = false; while (true) { QString line = textStream.readLine(); if (line.isNull()) { break; } if (line.startsWith("DEF") && line.contains(defName, Qt::CaseInsensitive)) { QStringList defs = splitLine(line); if (defs.count() < 8) { throw QObject::tr("bad schematic definition %1").arg(filename); } reference = defs[2]; textOffset = defs[4].toInt(); drawPinName = defs[6] == "Y"; drawPinNumber = defs[5] == "Y"; gotDef = true; break; } } if (!gotDef) { throw QObject::tr("schematic part %1 not found in %2").arg(defName).arg(filename); } QString contents = "\n"; bool inFPLIST = false; while (true) { QString fline = textStream.readLine(); if (fline.isNull()) { throw QObject::tr("schematic %1 unexpectedly ends (1) in %2").arg(defName).arg(filename); } if (fline.contains("ENDDEF")) { throw QObject::tr("schematic %1 unexpectedly ends (2) in %2").arg(defName).arg(filename); } if (fline.startsWith("DRAW")) { break; } if (fline.startsWith("ALIAS")) continue; if (fline.startsWith("F")) { contents += convertField(fline); continue; } if (fline.startsWith("$FPLIST")) { inFPLIST = true; break; } } while (inFPLIST) { QString fline = textStream.readLine(); if (fline.isNull()) { throw QObject::tr("schematic %1 unexpectedly ends (1) in %2").arg(defName).arg(filename); } if (fline.startsWith("$ENDFPLIST")) { inFPLIST = false; break; } if (fline.contains("ENDDEF")) { throw QObject::tr("schematic %1 unexpectedly ends (2) in %2").arg(defName).arg(filename); } } int pinIndex = 0; while (true) { QString line = textStream.readLine(); if (line.isNull()) { throw QObject::tr("schematic %1 unexpectedly ends (3) in %2").arg(defName).arg(filename); } if (line.startsWith("DRAW")) { continue; } if (line.contains("ENDDEF")) { break; } if (line.contains("ENDDRAW")) { break; } if (line.startsWith("S")) { contents += convertRect(line); } else if (line.startsWith("X")) { // need to look at them all before formatting (I think) contents += convertPin(line, textOffset, drawPinName, drawPinNumber, pinIndex++); } else if (line.startsWith("C")) { contents += convertCircle(line); } else if (line.startsWith("P")) { contents += convertPoly(line); } else if (line.startsWith("A")) { contents += convertArc(line); } else if (line.startsWith("T")) { contents += convertText(line); } else { DebugDialog::debug("Unknown line " + line); } } contents += "\n"; QString svg = TextUtils::makeSVGHeader(GraphicsUtils::StandardFritzingDPI, GraphicsUtils::StandardFritzingDPI, m_maxX - m_minX, m_maxY - m_minY) + m_title + m_description + metadata + offsetMin(contents) + ""; return svg; } QString KicadSchematic2Svg::convertText(const QString & line) { QStringList fs = splitLine(line); if (fs.count() < 8) { DebugDialog::debug("bad text " + line); return ""; } return convertField(fs[2], fs[3], fs[4], fs[1], "C", "C", fs[8]); } QString KicadSchematic2Svg::convertField(const QString & line) { QStringList fs = splitLine(line); if (fs.count() < 7) { DebugDialog::debug("bad field " + line); return ""; } if (fs[6] == "I") { // invisible return ""; } while (fs.count() < 9) { fs.append(""); } return convertField(fs[2], fs[3], fs[4], fs[5], fs[7], fs[8], fs[1]); } QString KicadSchematic2Svg::convertField(const QString & xString, const QString & yString, const QString & fontSizeString, const QString &orientation, const QString & hjustify, const QString & vjustify, const QString & t) { QString text = t; bool notName = false; if (text.startsWith("~")) { notName = true; text.remove(0, 1); } int x = xString.toInt(); int y = -yString.toInt(); // KiCad flips y-axis w.r.t. svg int fontSize = fontSizeString.toInt(); bool rotate = (orientation == "V"); QString rotation; QMatrix m; if (rotate) { m = QMatrix().translate(-x, -y) * QMatrix().rotate(-90) * QMatrix().translate(x, y); // store x, y, and r so they can be shifted correctly later rotation = QString("transform='%1' _x='%2' _y='%3' _r='-90'").arg(TextUtils::svgMatrix(m)).arg(x).arg(y); } QFont font; font.setFamily(OCRAFontName); font.setWeight(QFont::Normal); font.setPointSizeF(72.0 * fontSize / GraphicsUtils::StandardFritzingDPI); QString style; if (vjustify.contains("I")) { style += "font-style='italic' "; font.setStyle(QFont::StyleItalic); } if (vjustify.endsWith("B")) { style += "font-weight='bold' "; font.setWeight(QFont::Bold); } QString anchor = "middle"; if (vjustify.startsWith("T")) { anchor = "end"; } else if (vjustify.startsWith("B")) { anchor = "start"; } if (hjustify.contains("L")) { anchor = "start"; } else if (hjustify.contains("R")) { anchor = "end"; } QFontMetricsF metrics(font); QRectF bri = metrics.boundingRect(text); // convert back to 1000 dpi QRectF brf(0, 0, bri.width() * GraphicsUtils::StandardFritzingDPI / GraphicsUtils::SVGDPI, bri.height() * GraphicsUtils::StandardFritzingDPI / GraphicsUtils::SVGDPI); if (anchor == "start") { brf.translate(x, y - (brf.height() / 2)); } else if (anchor == "end") { brf.translate(x - brf.width(), y - (brf.height() / 2)); } else if (anchor == "middle") { brf.translate(x - (brf.width() / 2), y - (brf.height() / 2)); } if (rotate) { brf = m.map(QPolygonF(brf)).boundingRect(); } checkXLimit(brf.left()); checkXLimit(brf.right()); checkYLimit(brf.top()); checkYLimit(brf.bottom()); QString s = QString("%7\n") .arg(x) .arg(y + (fontSize / 3)) .arg(fontSize) .arg(anchor) .arg(style) .arg(rotation) .arg(TextUtils::escapeAnd(unquote(text))) .arg(OCRAFontName) ; if (notName) { s += QString("\n") .arg(brf.left()) .arg(brf.top()) .arg(rotate ? brf.left() : brf.right()) .arg(rotate ? brf.bottom() : brf.top()); } return s; } QString KicadSchematic2Svg::convertRect(const QString & line) { QStringList s = splitLine(line); if (s.count() < 8) { DebugDialog::debug(QString("bad rectangle %1").arg(line)); return ""; } if (s.count() < 9) { s.append("N"); // assume it's unfilled } int x = s[1].toInt(); int y = -s[2].toInt(); // KiCad flips y-axis w.r.t. svg int x2 = s[3].toInt(); int y2 = -s[4].toInt(); // KiCad flips y-axis w.r.t. svg checkXLimit(x); checkXLimit(x2); checkYLimit(y); checkYLimit(y2); QString rect = QString(" 12 && l[12] == "N") { // don't draw this return ""; } int unit = l[9].toInt(); if (unit > 1) { // don't draw this return ""; } QChar orientation = l[6].at(0); QString name = l[1]; if (name == "~") { name = ""; } bool pinNumberOK; int pinNumber = l[2].toInt(&pinNumberOK); if (!pinNumberOK) { pinNumber = pinIndex; } int nFontSize = l[7].toInt(); int x1 = l[3].toInt(); int y1 = -l[4].toInt(); // KiCad flips y-axis w.r.t. svg int length = l[5].toInt(); int x2 = x1; int y2 = y1; int x3 = x1; int y3 = y1; int x4 = x1; int y4 = y1; QString justify = "C"; bool rotate = false; switch (orientation.toLatin1()) { case 'D': y2 = y1 + length; y3 = y1 + (length / 2); if (textOffset == 0) { x3 += nFontSize / 2; x4 -= nFontSize / 2; y4 = y3; justify = "C"; } else { x3 -= nFontSize / 2; y4 = y2; justify = "R"; } rotate = true; break; case 'U': y2 = y1 - length; y3 = y1 - (length / 2); if (textOffset == 0) { x3 += nFontSize / 2; x4 -= nFontSize / 2; y4 = y3; justify = "C"; } else { x3 -= nFontSize / 2; y4 = y2; justify = "L"; } rotate = true; break; case 'L': x2 = x1 - length; x3 = x1 - (length / 2); if (textOffset == 0) { y3 += nFontSize / 2; y4 -= nFontSize / 2; x4 = x3; justify = "C"; } else { y3 -= nFontSize / 2; x4 = x2; justify = "R"; } break; case 'R': x2 = x1 + length; x3 = x1 + (length / 2); if (textOffset == 0) { y3 += nFontSize / 2; y4 -= nFontSize / 2; x4 = x3; justify = "C"; } else { y3 -= nFontSize / 2; x4 = x2; justify = "L"; } break; default: DebugDialog::debug(QString("bad orientation %1").arg(line)); break; } checkXLimit(x1); checkXLimit(x2); checkYLimit(y1); checkYLimit(y2); int thickness = 1; QString pin = QString("\n") .arg(x1) .arg(y1) .arg(x2) .arg(y2) .arg(thickness) .arg(pinNumber) .arg(TextUtils::escapeAnd(name)); pin += QString("\n") .arg(x1) .arg(y1) .arg(pinNumber); if (drawPinNumber) { pin += convertField(QString::number(x3), QString::number(-y3), l[7], rotate ? "V" : "H", "C", "C", l[2]); } if (drawPinName && !name.isEmpty()) { pin += convertField(QString::number(x4), QString::number(-y4), l[8], rotate ? "V" : "H", justify, "C", name); } return pin; } QString KicadSchematic2Svg::convertCircle(const QString & line) { QStringList s = splitLine(line); if (s.count() < 8) { DebugDialog::debug(QString("bad circle %1").arg(line)); return ""; } int x = s[1].toInt(); int y = -s[2].toInt(); // KiCad flips y-axis w.r.t. svg int r = s[3].toInt(); checkXLimit(x + r); checkXLimit(x - r); checkYLimit(y + r); checkYLimit(y - r); QString circle = QString(" 180 sweeps double diffAngle = endAngle - startAngle; if (diffAngle > 180) diffAngle -= 360; else if (diffAngle < -180) diffAngle += 360; // TODO: use actual bounding box of arc for clipping checkXLimit(x + r); checkXLimit(x - r); checkYLimit(y + r); checkYLimit(y - r); QString arc = QString("= 180 ? 1 : 0) .arg(diffAngle > 0 ? 0 : 1) .arg(x2 - x1) .arg(y2 - y1); arc += addFill(line, s[9], s[8]); arc += " />\n"; return arc; } QString KicadSchematic2Svg::convertPoly(const QString & line) { QStringList s = splitLine(line); if (s.count() < 6) { DebugDialog::debug(QString("bad poly %1").arg(line)); return ""; } int np = s[1].toInt(); if (np < 2) { DebugDialog::debug(QString("degenerate poly %1").arg(line)); return ""; } if (s.count() < (np * 2) + 5) { DebugDialog::debug(QString("bad poly (2) %1").arg(line)); return ""; } if (s.count() < (np * 2) + 6) { s.append("N"); // assume unfilled } int ix = 5; if (np == 2) { int x1 = s[ix++].toInt(); int y1 = -s[ix++].toInt(); // KiCad flips y-axis w.r.t. svg int x2 = s[ix++].toInt(); int y2 = -s[ix++].toInt(); // KiCad flips y-axis w.r.t. svg checkXLimit(x1); checkYLimit(y1); checkXLimit(x2); checkYLimit(y2); QString line = QString(" 0; i--) { QString s = strs[i]; if (s[s.length() - 1] != '"') continue; if (s[0] == '"' && s.length() > 1) continue; // space in a quoted string: combine strs[i - 1] += strs[i]; strs.removeAt(i); } return strs; } fritzing-app-0.9.3b/src/svg/kicadschematic2svg.h000066400000000000000000000041531272363601300216020ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef KICADSCHEMATIC2SVG_H #define KICADSCHEMATIC2SVG_H #include #include #include #include #include "kicad2svg.h" class KicadSchematic2Svg : public Kicad2Svg { public: KicadSchematic2Svg(); QString convert(const QString & filename, const QString &defName); public: static QStringList listDefs(const QString & filename); protected: QString convertField(const QString & line); QString convertText(const QString & line); QString convertField(const QString & xString, const QString & yString, const QString & fontSizeString, const QString & orientation, const QString & hjustify, const QString & vjustify, const QString & text); QString convertRect(const QString & line); QString convertCircle(const QString & line); QString convertPin(const QString & line, int textOffset, bool drawPinName, bool drawPinNumber, int pinIndex); QString convertArc(const QString & line); QString convertPoly(const QString & line); QString addFill(const QString & line, const QString & NF, const QString & strokeString); QStringList splitLine(const QString & line); }; #endif // KICADSCHEMATIC2SVG_H fritzing-app-0.9.3b/src/svg/svg2gerber.cpp000066400000000000000000000746051272363601300204600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "svg2gerber.h" #include "../debugdialog.h" #include "svgflattener.h" #include #include #include static const double MaskClearance = 0.005; // 5 mils clearance bool fillNotStroke(QDomElement & element, SVG2gerber::ForWhy forWhy) { if (forWhy == SVG2gerber::ForOutline) return false; if (forWhy == SVG2gerber::ForMask) return true; QString fill = element.attribute("fill"); if (fill.isEmpty()) return false; if (fill.compare("none") == 0) return false; QString stroke = element.attribute("stroke"); if (stroke.isEmpty()) return true; if (stroke.compare("none") == 0) return true; // both fill and stroke // note: this originally returned 'false' which suggests there may be some parts that will surprise us next time they are exported to gerber return true; } //TODO: currently only supports one board per sketch (i.e. multiple board outlines will mess you up) SVG2gerber::SVG2gerber() { } int SVG2gerber::convert(const QString & svgStr, bool doubleSided, const QString & mainLayerName, ForWhy forWhy, QSizeF boardSize) { m_boardSize = boardSize; m_SVGDom = QDomDocument("svg"); QString errorStr; int errorLine; int errorColumn; bool result = m_SVGDom.setContent(svgStr, &errorStr, &errorLine, &errorColumn); if (!result) { DebugDialog::debug(QString("gerber svg failed %2 %3 %4 %1").arg(svgStr).arg(errorStr).arg(errorLine).arg(errorColumn)); } #ifndef QT_NO_DEBUG QString temp = m_SVGDom.toString(); #endif normalizeSVG(); #ifndef QT_NO_DEBUG temp = m_SVGDom.toString(); #endif return renderGerber(doubleSided, mainLayerName, forWhy); } QString SVG2gerber::getGerber(){ return m_gerber_header + m_gerber_paths; } int SVG2gerber::renderGerber(bool doubleSided, const QString & mainLayerName, ForWhy forWhy) { if (forWhy != ForDrill) { // human readable description comments m_gerber_header = "G04 MADE WITH FRITZING*\n"; m_gerber_header += "G04 WWW.FRITZING.ORG*\n"; m_gerber_header += QString("G04 %1 SIDED*\n").arg(doubleSided ? "DOUBLE" : "SINGLE"); m_gerber_header += QString("G04 HOLES%1PLATED*\n").arg(doubleSided ? " " : " NOT "); m_gerber_header += "G04 CONTOUR ON CENTER OF CONTOUR VECTOR*\n"; // initialize axes m_gerber_header += "%ASAXBY*%\n"; // NOTE: this currently forces a 1 mil grid // format coordinates to drop leading zeros with 2,3 digits m_gerber_header += "%FSLAX23Y23*%\n"; // set units to inches m_gerber_header += "%MOIN*%\n"; // no offset m_gerber_header += "%OFA0B0*%\n"; // scale factor 1x1 m_gerber_header += "%SFA1.0B1.0*%\n"; } else { // deal with header at the end } // define apertures and draw them int invalidCount = allPaths2gerber(forWhy); if (forWhy == ForDrill) { static const int initialHoleIndex = 1; static const int offset = 100; int initialPlatedIndex = (((m_holeApertures.uniqueKeys().count() + initialHoleIndex - 1) / offset) + 1) * offset; m_gerber_header = ""; m_gerber_header += QString("; NON-PLATED HOLES START AT T%1\n").arg(initialHoleIndex); m_gerber_header += QString("; THROUGH (PLATED) HOLES START AT T%1\n").arg(initialPlatedIndex); // setup drill file header m_gerber_header += "M48\n"; // set to english (inches) units, with trailing zeros m_gerber_header += "INCH\n"; int ix = initialHoleIndex; foreach (QString aperture, m_holeApertures.uniqueKeys()) { m_gerber_header += QString("T%1%2\n").arg(ix).arg(aperture); m_gerber_paths += QString("T%1\n").arg(ix); foreach (QString loc, m_holeApertures.values(aperture).toSet()) { m_gerber_paths += loc + "\n"; } ix++; } ix = initialPlatedIndex; foreach (QString aperture, m_platedApertures.uniqueKeys()) { m_gerber_header += QString("T%1%2\n").arg(ix).arg(aperture); m_gerber_paths += QString("T%1\n").arg(ix); foreach (QString loc, m_platedApertures.values(aperture).toSet()) { m_gerber_paths += loc + "\n"; } ix++; } m_gerber_header += "%\n"; // closes the header //m_gerber_paths += m_drill_slots; // from handleOblong, not up to date // drill file unload tool and end of program m_gerber_paths += "T00\n"; m_gerber_paths += "M30\n"; } else { // label our layers m_gerber_header += QString("%LN%1*%\n").arg(mainLayerName.toUpper()); //just to be safe: G90 (absolute coords) and G70 (inches) m_gerber_header += "G90*\nG70*\n"; // now write the footer // comment to indicate end-of-sketch m_gerber_paths += QString("G04 End of %1*\n").arg(mainLayerName); // write gerber end-of-program m_gerber_paths += "M02*"; } return invalidCount; } void SVG2gerber::normalizeSVG(){ QDomElement root = m_SVGDom.documentElement(); // convert to paths convertShapes2paths(root); // get rid of transforms SvgFlattener flattener; flattener.flattenChildren(root); } void SVG2gerber::convertShapes2paths(QDomNode node){ // I'm a leaf node. make me a path //TODO: this should strip svg: namspaces if(!node.hasChildNodes()) { QString tag = node.nodeName().toLower(); QDomElement element = node.toElement(); QDomElement path; //DebugDialog::debug("converting child to path: " + tag); if(tag=="polygon"){ path = element; } else if(tag=="polyline"){ path = element; } else if(tag=="rect"){ path = element; } else if(tag=="circle"){ path = element; } else if(tag=="line"){ path = element; } else if(tag=="ellipse"){ path = ellipse2path(element); } else if((tag=="path") || (tag=="svg:path")){ path = element; } else if (tag == "g") { // no op } else if (tag == "#comment") { // no op } else if (tag == "#text") { // no op } else { DebugDialog::debug("svg2gerber ignoring SVG element: " + tag); } copyStyles(element, path); // add the path and delete the primitive element (is this ok for paths?) QDomNode parent = node.parentNode(); parent.replaceChild(path, node); return; } // recurse the children QDomNodeList tagList = node.childNodes(); //DebugDialog::debug("child nodes: " + QString::number(tagList.length())); for(int i = 0; i < tagList.length(); i++){ convertShapes2paths(tagList.item(i)); } } void SVG2gerber::copyStyles(QDomElement source, QDomElement dest){ QStringList attrList; attrList << "stroke" << "fill" << "stroke-width" << "style"; for (int i = 0; i < attrList.size(); ++i) { if (source.hasAttribute(attrList.at(i))) dest.setAttribute(attrList.at(i), source.attribute(attrList.at(i))); } } QMatrix SVG2gerber::parseTransform(QDomElement element){ QMatrix transform = QMatrix(); QString svgTransform = element.attribute("transform"); return transform; } int SVG2gerber::allPaths2gerber(ForWhy forWhy) { int invalidPathsCount = 0; QHash apertureMap; QString current_dcode; int dcode_index = 10; bool light_on = false; int currentx = -1; int currenty = -1; m_holeApertures.clear(); m_platedApertures.clear(); // iterates through all circles, rects, lines and paths // 1. check if we already have an aperture // if aperture does not exist, add it to the header // 2. switch to this aperture // 3. draw it at the correct path/location QDomNodeList circleList = m_SVGDom.elementsByTagName("circle"); //DebugDialog::debug("circles to gerber: " + QString::number(circleList.length())); QDomNodeList rectList = m_SVGDom.elementsByTagName("rect"); //DebugDialog::debug("rects to gerber: " + QString::number(rectList.length())); QDomNodeList polyList = m_SVGDom.elementsByTagName("polygon"); //DebugDialog::debug("polygons to gerber: " + QString::number(polyList.length())); QDomNodeList polyLineList = m_SVGDom.elementsByTagName("polyline"); //DebugDialog::debug("polylines to gerber: " + QString::number(polyLineList.length())); QDomNodeList lineList = m_SVGDom.elementsByTagName("line"); //DebugDialog::debug("lines to gerber: " + QString::number(lineList.length())); QDomNodeList pathList = m_SVGDom.elementsByTagName("path"); //DebugDialog::debug("paths to gerber: " + QString::number(pathList.length())); // if this is the board outline, use it as the contour if (forWhy == ForOutline) { //DebugDialog::debug("drawing board outline"); // switch aperture to the only one used for contour: note this is the last one on the list: the aperture is added at the end of this function m_gerber_paths += "G54D10*\n"; } // circles for(int i = 0; i < circleList.length(); i++){ QDomElement circle = circleList.item(i).toElement(); double centerx = circle.attribute("cx").toDouble(); double centery = circle.attribute("cy").toDouble(); double r = circle.attribute("r").toDouble(); if (r == 0) continue; QString drillAttribute = circle.attribute("drill", ""); bool noDrill = (drillAttribute.compare("0") == 0 || drillAttribute.compare("no", Qt::CaseInsensitive) == 0 || drillAttribute.compare("false", Qt::CaseInsensitive) == 0); double stroke_width = circle.attribute("stroke-width").toDouble(); double hole = ((2*r) - stroke_width) / 1000; // convert mils (standard fritzing resolution) to inches if (forWhy == ForDrill) { if (noDrill) continue; QString drill_cx = QString("%1").arg((int) (flipxNoRound(centerx) * 10), 6, 10, QChar('0')); // drill file is in inches 00.0000, converting mils to 10000ths QString drill_cy = QString("%1").arg((int) (flipyNoRound(centery) * 10), 6, 10, QChar('0')); // drill file is in inches 00.0000, converting mils to 10000ths QString aperture = QString("C%1").arg(hole, 0, 'f'); QString loc = "X" + drill_cx + "Y" + drill_cy; if (stroke_width == 0) m_holeApertures.insert(aperture, loc); else m_platedApertures.insert(aperture, loc); continue; } QString aperture; QString cx = QString::number(flipx(centerx)); QString cy = QString::number(flipy(centery)); QString fill = circle.attribute("fill"); double diam = ((2*r) + stroke_width)/1000; if (forWhy == ForMask) { diam += 2 * MaskClearance; } if ((forWhy != ForCopper && fill=="none" && forWhy != ForMask) || (forWhy == ForCopper && noDrill)) { aperture = QString("C,%1X%2").arg(diam, 0, 'f').arg(hole); } else { aperture = QString("C,%1").arg(diam, 0, 'f'); } // add aperture to defs if we don't have it yet if(!apertureMap.contains(aperture)){ apertureMap[aperture] = QString::number(dcode_index); m_gerber_header += "%ADD" + QString::number(dcode_index) + aperture + "*%\n"; dcode_index++; } if (forWhy != ForOutline) { QString dcode = apertureMap[aperture]; if(current_dcode != dcode){ //switch to correct aperture m_gerber_paths += "G54D" + dcode + "*\n"; current_dcode = dcode; } //flash m_gerber_paths += "X" + cx + "Y" + cy + "D03*\n"; } else { standardAperture(circle, apertureMap, current_dcode, dcode_index, 0); // create circle outline m_gerber_paths += QString("G01X%1Y%2D02*\n" "G75*\n" "G03X%1Y%2I%3J0D01*\n") .arg(QString::number(flipx(centerx + r))) .arg(QString::number(flipy(centery))) .arg(QString::number(qRound(-r))); m_gerber_paths += "G01*\n"; } } if (forWhy != ForDrill) { // rects for(int j = 0; j < rectList.length(); j++){ QDomElement rect = rectList.item(j).toElement(); QString aperture; double width = rect.attribute("width").toDouble(); double height = rect.attribute("height").toDouble(); double rx = rect.attribute("rx", "0").toDouble(); double ry = rect.attribute("ry", "0").toDouble(); if (rx != 0 || ry != 0) { // not sure how to do rounded rects in gerber invalidPathsCount++; continue; } if (width == 0) continue; if (height == 0) continue; double x = rect.attribute("x").toDouble(); double y = rect.attribute("y").toDouble(); double centerx = x + (width/2); double centery = y + (height/2); QString cx = QString::number(flipx(centerx)); QString cy = QString::number(flipy(centery)); QString fill = rect.attribute("fill"); double stroke_width = rect.attribute("stroke-width").toDouble(); double totalx = (width + stroke_width)/1000; double totaly = (height + stroke_width)/1000; double holex = (width - stroke_width)/1000; double holey = (height - stroke_width)/1000; if (forWhy == ForMask) { totalx += 2 * MaskClearance; totaly += 2 * MaskClearance; } if(forWhy != ForCopper && fill=="none" && forWhy != ForMask) { aperture = QString("R,%1X%2X%3X%4").arg(totalx, 0, 'f').arg(totaly, 0, 'f').arg(holex, 0, 'f').arg(holey, 0, 'f'); } else { aperture = QString("R,%1X%2").arg(totalx, 0, 'f').arg(totaly, 0, 'f'); } // add aperture to defs if we don't have it yet if(!apertureMap.contains(aperture)){ apertureMap[aperture] = QString::number(dcode_index); m_gerber_header += "%ADD" + QString::number(dcode_index) + aperture + "*%\n"; dcode_index++; } bool doLines = false; if (forWhy == ForOutline) doLines = true; else if (forWhy == ForSilk && fill == "none") doLines = true; if (!doLines) { QString dcode = apertureMap[aperture]; if(current_dcode != dcode){ //switch to correct aperture m_gerber_paths += "G54D" + dcode + "*\n"; current_dcode = dcode; } //flash m_gerber_paths += "X" + cx + "Y" + cy + "D03*\n"; } else { // draw 4 lines standardAperture(rect, apertureMap, current_dcode, dcode_index, 0); m_gerber_paths += "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y)) + "D02*\n"; m_gerber_paths += "X" + QString::number(flipx(x+width)) + "Y" + QString::number(flipy(y)) + "D01*\n"; m_gerber_paths += "X" + QString::number(flipx(x+width)) + "Y" + QString::number(flipy(y+height)) + "D01*\n"; m_gerber_paths += "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y+height)) + "D01*\n"; m_gerber_paths += "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y)) + "D01*\n"; m_gerber_paths += "D02*\n"; } } // lines - NOTE: this assumes a circular aperture for(int k = 0; k < lineList.length(); k++){ QDomElement line = lineList.item(k).toElement(); // Note: should be no forWhy == ForMask cases double x1 = line.attribute("x1").toDouble(); double y1 = line.attribute("y1").toDouble(); double x2 = line.attribute("x2").toDouble(); double y2 = line.attribute("y2").toDouble(); standardAperture(line, apertureMap, current_dcode, dcode_index, 0); // turn off light if we are not continuing along a path if ((y1 != currenty) || (x1 != currentx)) { if (light_on) { m_gerber_paths += "D02*\n"; light_on = false; } } //go to start - light off m_gerber_paths += "X" + QString::number(flipx(x1)) + "Y" + QString::number(flipy(y1)) + "D02*\n"; //go to end point - light on m_gerber_paths += "X" + QString::number(flipx(x2)) + "Y" + QString::number(flipy(y2)) + "D01*\n"; light_on = true; currentx = x2; currenty = y2; } // polys - NOTE: assumes comma- or space- separated formatting for(int p = 0; p < polyList.length(); p++) { QDomElement polygon = polyList.item(p).toElement(); doPoly(polygon, forWhy, true, apertureMap, current_dcode, dcode_index); } for(int p = 0; p < polyLineList.length(); p++) { QDomElement polygon = polyLineList.item(p).toElement(); doPoly(polygon, forWhy, false, apertureMap, current_dcode, dcode_index); } } // paths - NOTE: this assumes circular aperture for(int n = 0; n < pathList.length(); n++){ QDomElement path = pathList.item(n).toElement(); if (forWhy == ForDrill) { handleOblongPath(path, dcode_index); // this is currently a no-op continue; } QString data = path.attribute("d").trimmed(); const char * slot = SLOT(path2gerbCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.x = 0; pathUserData.y = 0; pathUserData.pathStarting = true; pathUserData.string = ""; SvgFlattener flattener; bool invalid = false; try { flattener.parsePath(data, slot, pathUserData, this, true); } catch (const QString & msg) { DebugDialog::debug("flattener.parsePath failed " + msg); invalid = true; } catch (char const *str) { DebugDialog::debug("flattener.parsePath failed " + QString(str)); invalid = true; } catch (...) { DebugDialog::debug("flattener.parsePath failed"); invalid = true; } // only add paths if they contained gerber-izable path commands (NO CURVES!) // TODO: display some informative error for the user if (invalid || pathUserData.string.contains("INVALID")) { invalidPathsCount++; continue; } if (path.attribute("stroke-linecap") == "square") { double stroke_width = path.attribute("stroke-width").toDouble(); if (stroke_width != 0) { QString aperture = QString("R,%1X%1").arg(stroke_width/1000, 0, 'f'); // add aperture to defs if we don't have it yet if (!apertureMap.contains(aperture)) { apertureMap[aperture] = QString::number(dcode_index); m_gerber_header += "%ADD" + QString::number(dcode_index) + aperture + "*%\n"; dcode_index++; } QString dcode = apertureMap[aperture]; if (current_dcode != dcode) { //switch to correct aperture m_gerber_paths += "G54D" + dcode + "*\n"; current_dcode = dcode; } } } else { standardAperture(path, apertureMap, current_dcode, dcode_index, 0); } // set poly fill if this is actually a filled in shape bool polyFill = fillNotStroke(path, forWhy); if(polyFill) { // start poly fill m_gerber_paths += "G36*\n"; } m_gerber_paths += pathUserData.string; //DebugDialog::debug("path id: " + path.attribute("id")); // stop poly fill if this is actually a filled in shape if(polyFill){ // stop poly fill m_gerber_paths += "G37*\n"; } if (forWhy == ForMask) { // draw the outline, G36 only does the fill standardAperture(path, apertureMap, current_dcode, dcode_index, path.attribute("stroke-width").toDouble() + (MaskClearance * 2 * 1000)); m_gerber_paths += pathUserData.string; } // light off m_gerber_paths += "D02*\n"; } if (forWhy == ForOutline) { // add circular aperture with 0 width m_gerber_header += "%ADD10C,0.008*%\n"; } return invalidPathsCount; } void SVG2gerber::doPoly(QDomElement & polygon, ForWhy forWhy, bool closedCurve, QHash & apertureMap, QString & current_dcode, int & dcode_index) { //QString temp; //QTextStream tempStream(&temp); //polygon.save(tempStream, 1); QString points = polygon.attribute("points"); QStringList pointList = points.split(QRegExp("\\s+|,"), QString::SkipEmptyParts); QString aperture; QString pointString; double startx = pointList.at(0).toDouble(); double starty = pointList.at(1).toDouble(); // move to start - light off pointString += "X" + QString::number(flipx(startx)) + "Y" + QString::number(flipy(starty)) + "D02*\n"; // iterate through all other points - light on for(int pt = 2; pt < pointList.length(); pt +=2){ double ptx = pointList.at(pt).toDouble(); double pty = pointList.at(pt+1).toDouble(); pointString += "X" + QString::number(flipx(ptx)) + "Y" + QString::number(flipy(pty)) + "D01*\n"; } if (closedCurve) { // move back to start point pointString += "X" + QString::number(flipx(startx)) + "Y" + QString::number(flipy(starty)) + "D01*\n"; } standardAperture(polygon, apertureMap, current_dcode, dcode_index, 0); bool polyFill = fillNotStroke(polygon, forWhy); // set poly fill if this is actually a filled in shape if (polyFill) { // start poly fill m_gerber_paths += "G36*\n"; } m_gerber_paths += pointString; // stop poly fill if this is actually a filled in shape if(polyFill){ // stop poly fill m_gerber_paths += "G37*\n"; } if (forWhy == ForMask) { // draw the outline, G36 only does the fill standardAperture(polygon, apertureMap, current_dcode, dcode_index, polygon.attribute("stroke-width").toDouble() + (MaskClearance * 2 * 1000)); m_gerber_paths += pointString; } // light off m_gerber_paths += "D02*\n"; } QString SVG2gerber::standardAperture(QDomElement & element, QHash & apertureMap, QString & current_dcode, int & dcode_index, double stroke_width) { if (stroke_width == 0) { stroke_width = element.attribute("stroke-width").toDouble(); } if (stroke_width == 0) return ""; QString aperture = QString("C,%1").arg(stroke_width/1000, 0, 'f'); // add aperture to defs if we don't have it yet if (!apertureMap.contains(aperture)){ apertureMap[aperture] = QString::number(dcode_index); m_gerber_header += "%ADD" + QString::number(dcode_index) + aperture + "*%\n"; dcode_index++; } QString dcode = apertureMap[aperture]; if (current_dcode != dcode) { //switch to correct aperture m_gerber_paths += "G54D" + dcode + "*\n"; current_dcode = dcode; } return aperture; } void SVG2gerber::handleOblongPath(QDomElement & path, int & dcode_index) { // this code has not been tested in a long time and is probably obsolete return; // look for oblong paths QDomElement parent = path.parentNode().toElement(); if (parent.attribute("id").compare("oblong") != 0) return; QDomElement nextPath = path.nextSiblingElement("path"); if (nextPath.isNull()) return; QDomElement nextLine = nextPath.nextSiblingElement("line"); if (nextLine.isNull()) return; double diameter = parent.attribute("stroke-width").toDouble(); double cx1 = nextLine.attribute("x1").toDouble(); double cy1 = nextLine.attribute("y1").toDouble(); double cx2 = nextLine.attribute("x2").toDouble(); double cy2 = nextLine.attribute("y2").toDouble(); QString drill_aperture = QString("C%1").arg(diameter / 1000, 0, 'f') + "\n"; // convert mils to inches if (!m_gerber_header.contains(drill_aperture)) { m_gerber_header += "T" + QString::number(dcode_index++) + drill_aperture; } int ix = m_gerber_header.indexOf(drill_aperture); int it = m_gerber_header.lastIndexOf("T", ix); m_drill_slots += QString("%1\nX%2Y%3G85X%4Y%5\nG05\n") .arg(m_gerber_header.mid(it, ix - it), 0, 'f') .arg((int) (flipxNoRound(cx1) * 10), 6, 10, QChar('0')) .arg((int) (flipyNoRound(cy1) * 10), 6, 10, QChar('0')) .arg((int) (flipxNoRound(cx2) * 10), 6, 10, QChar('0')) .arg((int) (flipyNoRound(cy2) * 10), 6, 10, QChar('0')); } QDomElement SVG2gerber::ellipse2path(QDomElement ellipseElement){ // TODO return ellipseElement; } QString SVG2gerber::path2gerber(QDomElement pathElement){ Q_UNUSED(pathElement); QString d; return d; } void SVG2gerber::path2gerbCommandSlot(QChar command, bool relative, QList & args, void * userData) { QString gerb_path; int x, y; PathUserData * pathUserData = (PathUserData *) userData; int argIndex = 0; while (argIndex < args.count()) { switch(command.toLatin1()) { case 'a': case 'A': case 'c': case 'C': case 'q': case 'Q': case 's': case 'S': case 't': case 'T': // TODO: implement elliptical arc, etc. pathUserData->string.append("INVALID"); argIndex = args.count(); break; case 'm': case 'M': x = args[argIndex]; y = args[argIndex + 1]; if (relative && !pathUserData->pathStarting) { x += pathUserData->x; y += pathUserData->y; } if (argIndex == 0) { // treat first 'm' arg pair as a move to gerb_path = "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y)) + "D02*\n"; m_pathstart_x = x; m_pathstart_y = y; } else { // treat subsequent 'm' arg pair as line to gerb_path = "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y)) + "D01*\n"; } pathUserData->x = x; pathUserData->y = y; pathUserData->pathStarting = false; pathUserData->string.append(gerb_path); argIndex += 2; break; case 'v': case 'V': DebugDialog::debug("'v' and 'V' are now removed by preprocessing; shouldn't be here"); break; case 'h': case 'H': DebugDialog::debug("'h' and 'H' are now removed by preprocessing; shouldn't be here"); break; case 'l': case 'L': x = args[argIndex]; y = args[argIndex + 1]; if (relative) { x += pathUserData->x; y += pathUserData->y; } gerb_path = "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y)) + "D01*\n"; pathUserData->x = x; pathUserData->y = y; pathUserData->string.append(gerb_path); argIndex += 2; break; case 'z': case 'Z': gerb_path = "X" + QString::number(flipx(m_pathstart_x)) + "Y" + QString::number(flipy(m_pathstart_y)) + "D01*\n"; gerb_path += "D02*\n"; pathUserData->string.append(gerb_path); pathUserData->pathStarting = true; break; default: argIndex = args.count(); pathUserData->string.append("INVALID"); break; } } } int SVG2gerber::flipx(double x) { return qRound(x); } int SVG2gerber::flipy(double y) { return qRound(m_boardSize.height() - y); } double SVG2gerber::flipxNoRound(double x) { return x; } double SVG2gerber::flipyNoRound(double y) { return m_boardSize.height() - y; } fritzing-app-0.9.3b/src/svg/svg2gerber.h000066400000000000000000000053551272363601300201210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SVG2GERBER_H #define SVG2GERBER_H #include #include #include #include #include class SVG2gerber : public QObject { Q_OBJECT public: SVG2gerber(); public: enum ForWhy { ForCopper, ForSilk, ForOutline, ForMask, ForDrill, ForPasteMask }; int convert(const QString & svgStr, bool doubleSided, const QString & mainLayerName, ForWhy, QSizeF boardSize); QString getGerber(); protected: QDomDocument m_SVGDom; QString m_gerber_header; QString m_gerber_paths; QString m_drill_slots; QSizeF m_boardSize; QMultiHash m_platedApertures; QMultiHash m_holeApertures; double m_pathstart_x; double m_pathstart_y; protected: void normalizeSVG(); void convertShapes2paths(QDomNode); void flattenSVG(QDomNode); QMatrix parseTransform(QDomElement); QDomElement ellipse2path(QDomElement); void copyStyles(QDomElement, QDomElement); int renderGerber(bool doubleSided, const QString & mainLayerName, ForWhy); int allPaths2gerber(ForWhy); QString path2gerber(QDomElement); void handleOblongPath(QDomElement & path, int & dcode_index); QString standardAperture(QDomElement & element, QHash & apertureMap, QString & current_dcode, int & dcode_index, double stroke_width); int flipx(double x); int flipy(double y); double flipxNoRound(double x); double flipyNoRound(double y); void doPoly(QDomElement & polygon, ForWhy forWhy, bool closedCurve, QHash & apertureMap, QString & current_dcode, int & dcode_index); protected slots: void path2gerbCommandSlot(QChar command, bool relative, QList & args, void * userData); }; #endif // SVG2GERBER_H fritzing-app-0.9.3b/src/svg/svgfilesplitter.cpp000066400000000000000000001231111272363601300216210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #include "svgfilesplitter.h" #include "../utils/misc.h" #include "../utils/textutils.h" //#include "../debugdialog.h" #include "svgpathparser.h" #include "svgpathlexer.h" #include "svgpathrunner.h" #include #include #include #include struct HVConvertData { double x; double y; double subX; double subY; QString path; }; void appendPair(QString & path, double a1, double a2) { path.append(QString::number(a1)); path.append(','); path.append(QString::number(a2)); path.append(','); } ////////////////////////////////////////////////// SvgFileSplitter::SvgFileSplitter() { } bool SvgFileSplitter::split(const QString & filename, const QString & elementID) { m_byteArray.clear(); QFile file(filename); if (!file.open(QFile::ReadOnly | QFile::Text)) { return false; } QString contents = file.readAll(); file.close(); return splitString(contents, elementID); } bool SvgFileSplitter::splitString(QString & contents, const QString & elementID) { m_byteArray.clear(); // gets rid of some crap inserted by illustrator which can screw up polygons and paths contents.replace('\t', ' '); contents.replace('\n', ' '); contents.replace('\r', ' '); // get rid of inkscape stuff too TextUtils::cleanSodipodi(contents); QString errorStr; int errorLine; int errorColumn; if (!m_domDocument.setContent(contents, true, &errorStr, &errorLine, &errorColumn)) { //DebugDialog::debug(QString("parse error: %1 l:%2 c:%3\n\n%4").arg(errorStr).arg(errorLine).arg(errorColumn).arg(contents)); return false; } QDomElement root = m_domDocument.documentElement(); if (root.isNull()) { return false; } if (root.tagName() != "svg") { return false; } root.removeAttribute("space"); QDomElement element = TextUtils::findElementWithAttribute(root, "id", elementID); if (element.isNull()) { return false; } QStringList superTransforms; QDomNode parent = element.parentNode(); while (!parent.isNull()) { QDomElement e = parent.toElement(); if (!e.isNull()) { QString transform = e.attribute("transform"); if (!transform.isEmpty()) { superTransforms.append(transform); } } parent = parent.parentNode(); } if (!superTransforms.isEmpty()) { element.removeAttribute("id"); } QDomDocument document; QDomNode node = document.importNode(element, true); document.appendChild(node); QString elementText = document.toString(); if (!superTransforms.isEmpty()) { for (int i = 0; i < superTransforms.count() - 1; i++) { elementText = QString("%2").arg(superTransforms[i]).arg(elementText); } elementText = QString("%3") .arg(elementID) .arg(superTransforms[superTransforms.count() - 1]) .arg(elementText); } while (!root.firstChild().isNull()) { root.removeChild(root.firstChild()); } // at this point the document should contain only the element and possibly svg info strings QString svgOnly = m_domDocument.toString(); int ix = svgOnly.lastIndexOf("/>"); if (ix < 0) return false; svgOnly[ix] = ' '; svgOnly += elementText; svgOnly += ""; if (!m_domDocument.setContent(svgOnly, true, &errorStr, &errorLine, &errorColumn)) { return false; } m_byteArray = m_domDocument.toByteArray(); //QString s = m_domDocument.toString(); //DebugDialog::debug(s); return true; } const QByteArray & SvgFileSplitter::byteArray() { return m_byteArray; } const QDomDocument & SvgFileSplitter::domDocument() { return m_domDocument; } QString SvgFileSplitter::elementString(const QString & elementID) { QDomElement root = m_domDocument.documentElement(); QDomElement mainElement = TextUtils::findElementWithAttribute(root, "id", elementID); if (mainElement.isNull()) return ___emptyString___; QDomDocument document; QDomNode node = document.importNode(mainElement, true); document.appendChild(node); return document.toString(); } bool SvgFileSplitter::normalize(double dpi, const QString & elementID, bool blackOnly, double & factor) { // get the viewbox and the width and height // then normalize them // then normalize all the internal stuff // if there are translations, we're probably ok factor = 1; QDomElement root = m_domDocument.documentElement(); if (root.isNull()) return false; double sWidth, sHeight, vbWidth, vbHeight; if (!TextUtils::getSvgSizes(m_domDocument, sWidth, sHeight, vbWidth, vbHeight)) return false; root.setAttribute("width", QString("%1in").arg(sWidth)); root.setAttribute("height", QString("%1in").arg(sHeight)); root.setAttribute("viewBox", QString("%1 %2 %3 %4").arg(0).arg(0).arg(sWidth * dpi).arg(sHeight * dpi) ); QDomElement mainElement = root; if (!elementID.isEmpty()) { mainElement = TextUtils::findElementWithAttribute(root, "id", elementID); if (mainElement.isNull()) return false; } normalizeChild(mainElement, sWidth * dpi, sHeight * dpi, vbWidth, vbHeight, blackOnly); factor = sWidth * dpi / vbWidth; return true; } QPainterPath SvgFileSplitter::painterPath(double dpi, const QString & elementID) { // only partially implemented because so far we only use this for polygons generated for the groundplane QPainterPath ppath; double factor; bool result = normalize(dpi, elementID, false, factor); if (!result) return ppath; QDomElement root = m_domDocument.documentElement(); if (root.isNull()) return ppath; QDomElement mainElement = TextUtils::findElementWithAttribute(root, "id", elementID); if (mainElement.isNull()) return ppath; QDomElement childElement = mainElement.firstChildElement(); while (!childElement.isNull()) { painterPathChild(childElement, ppath); childElement = childElement.nextSiblingElement(); } return ppath; } void SvgFileSplitter::painterPathChild(QDomElement & element, QPainterPath & ppath) { // only partially implemented if (element.nodeName().compare("circle") == 0) { double cx = element.attribute("cx").toDouble(); double cy = element.attribute("cy").toDouble(); double r = element.attribute("r").toDouble(); double stroke = element.attribute("stroke-width").toDouble(); ppath.addEllipse(QRectF(cx - r - (stroke / 2), cy - r - (stroke / 2), (r * 2) + stroke, (r * 2) + stroke)); } else if (element.nodeName().compare("line") == 0) { /* double x1 = element.attribute("x1").toDouble(); double y1 = element.attribute("y1").toDouble(); double x2 = element.attribute("x2").toDouble(); double y2 = element.attribute("y2").toDouble(); double stroke = element.attribute("stroke-width").toDouble(); // treat as parallel lines stroke width apart? */ } else if (element.nodeName().compare("rect") == 0) { double width = element.attribute("width").toDouble(); double height = element.attribute("height").toDouble(); double x = element.attribute("x").toDouble(); double y = element.attribute("y").toDouble(); double stroke = element.attribute("stroke-width").toDouble(); double rx = element.attribute("rx").toDouble(); double ry = element.attribute("ry").toDouble(); if (rx != 0 || ry != 0) { ppath.addRoundedRect(x - (stroke / 2), y - (stroke / 2), width + stroke, height + stroke, rx, ry); } else { ppath.addRect(x - (stroke / 2), y - (stroke / 2), width + stroke, height + stroke); } } else if (element.nodeName().compare("ellipse") == 0) { double cx = element.attribute("cx").toDouble(); double cy = element.attribute("cy").toDouble(); double rx = element.attribute("rx").toDouble(); double ry = element.attribute("ry").toDouble(); double stroke = element.attribute("stroke-width").toDouble(); ppath.addEllipse(QRectF(cx - rx - (stroke / 2), cy - ry - (stroke / 2), (rx * 2) + stroke, (ry * 2) + stroke)); } else if (element.nodeName().compare("polygon") == 0 || element.nodeName().compare("polyline") == 0) { QString data = element.attribute("points"); if (!data.isEmpty()) { const char * slot = SLOT(painterPathCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.pathStarting = true; pathUserData.painterPath = &ppath; if (parsePath(data, slot, pathUserData, this, false)) { } } } else if (element.nodeName().compare("path") == 0) { /* QString data = element.attribute("d").trimmed(); if (!data.isEmpty()) { const char * slot = SLOT(normalizeCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.pathStarting = true; pathUserData.sNewHeight = sNewHeight; pathUserData.sNewWidth = sNewWidth; pathUserData.vbHeight = vbHeight; pathUserData.vbWidth = vbWidth; if (parsePath(data, slot, pathUserData, this, true)) { element.setAttribute("d", pathUserData.string); } } */ } else { QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { painterPathChild(childElement, ppath); childElement = childElement.nextSiblingElement(); } } } void SvgFileSplitter::normalizeTranslation(QDomElement & element, double sNewWidth, double sNewHeight, double vbWidth, double vbHeight) { QString attr = element.attribute("transform"); if (attr.isEmpty()) return; QMatrix matrix = TextUtils::elementToMatrix(element); if (matrix.dx() == 0 && matrix.dy() == 0) return; double dx = matrix.dx() * sNewWidth / vbWidth; double dy = matrix.dy() * sNewHeight / vbHeight; if (dx == 0 && dy == 0) return; matrix.setMatrix(matrix.m11(), matrix.m12(), matrix.m21(), matrix.m22(), dx, dy); TextUtils::setSVGTransform(element, matrix); } void SvgFileSplitter::normalizeChild(QDomElement & element, double sNewWidth, double sNewHeight, double vbWidth, double vbHeight, bool blackOnly) { normalizeTranslation(element, sNewWidth, sNewHeight, vbWidth, vbHeight); bool doChildren = false; QString nodeName = element.nodeName(); if (nodeName.compare("g") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); normalizeAttribute(element, "font-size", sNewWidth, vbWidth); setStrokeOrFill(element, blackOnly, "black", false); doChildren = true; } else if (nodeName.compare("circle") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "cx", sNewWidth, vbWidth); normalizeAttribute(element, "cy", sNewHeight, vbHeight); normalizeAttribute(element, "r", sNewWidth, vbWidth); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); setStrokeOrFill(element, blackOnly, "black", false); } else if (nodeName.compare("line") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "x1", sNewWidth, vbWidth); normalizeAttribute(element, "y1", sNewHeight, vbHeight); normalizeAttribute(element, "x2", sNewWidth, vbWidth); normalizeAttribute(element, "y2", sNewHeight, vbHeight); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); setStrokeOrFill(element, blackOnly, "black", false); } else if (nodeName.compare("rect") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "width", sNewWidth, vbWidth); normalizeAttribute(element, "height", sNewHeight, vbHeight); normalizeAttribute(element, "x", sNewWidth, vbWidth); normalizeAttribute(element, "y", sNewHeight, vbHeight); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); // rx, ry for rounded rects if (!element.attribute("rx").isEmpty()) { normalizeAttribute(element, "rx", sNewWidth, vbWidth); } if (!element.attribute("ry").isEmpty()) { normalizeAttribute(element, "ry", sNewHeight, vbHeight); } setStrokeOrFill(element, blackOnly, "black", false); } else if (nodeName.compare("ellipse") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "cx", sNewWidth, vbWidth); normalizeAttribute(element, "cy", sNewHeight, vbHeight); normalizeAttribute(element, "rx", sNewWidth, vbWidth); normalizeAttribute(element, "ry", sNewHeight, vbHeight); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); setStrokeOrFill(element, blackOnly, "black", false); } else if (nodeName.compare("polygon") == 0 || nodeName.compare("polyline") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); QString data = element.attribute("points"); if (!data.isEmpty()) { const char * slot = SLOT(normalizeCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.pathStarting = true; pathUserData.sNewHeight = sNewHeight; pathUserData.sNewWidth = sNewWidth; pathUserData.vbHeight = vbHeight; pathUserData.vbWidth = vbWidth; if (parsePath(data, slot, pathUserData, this, false)) { pathUserData.string.remove(0, 1); // get rid of the "M" element.setAttribute("points", pathUserData.string); } } setStrokeOrFill(element, blackOnly, "black", false); } else if (nodeName.compare("path") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); setStrokeOrFill(element, blackOnly, "black", false); QString data = element.attribute("d").trimmed(); if (!data.isEmpty()) { const char * slot = SLOT(normalizeCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.pathStarting = true; pathUserData.sNewHeight = sNewHeight; pathUserData.sNewWidth = sNewWidth; pathUserData.vbHeight = vbHeight; pathUserData.vbWidth = vbWidth; if (parsePath(data, slot, pathUserData, this, true)) { element.setAttribute("d", pathUserData.string); } } } else if (nodeName.compare("text") == 0) { TextUtils::fixStyleAttribute(element); normalizeAttribute(element, "x", sNewWidth, vbWidth); normalizeAttribute(element, "y", sNewHeight, vbHeight); normalizeAttribute(element, "stroke-width", sNewWidth, vbWidth); normalizeAttribute(element, "font-size", sNewWidth, vbWidth); setStrokeOrFill(element, blackOnly, "black", false); } else if (nodeName.compare("linearGradient") == 0) { if (element.attribute("gradientUnits").compare("userSpaceOnUse") == 0) { normalizeAttribute(element, "x1", sNewWidth, vbWidth); normalizeAttribute(element, "y1", sNewWidth, vbWidth); normalizeAttribute(element, "x2", sNewWidth, vbWidth); normalizeAttribute(element, "y2", sNewWidth, vbWidth); } else { //DebugDialog::debug(QString("unable to handle linearGradient with gradientUnits=%1").arg(element.attribute("gradientUnits"))); } } else if (nodeName.compare("radialGradient") == 0) { if (element.attribute("gradientUnits").compare("userSpaceOnUse") == 0) { normalizeAttribute(element, "cx", sNewWidth, vbWidth); normalizeAttribute(element, "cy", sNewWidth, vbWidth); normalizeAttribute(element, "fx", sNewWidth, vbWidth); normalizeAttribute(element, "fy", sNewWidth, vbWidth); normalizeAttribute(element, "r", sNewWidth, vbWidth); } else { //DebugDialog::debug(QString("unable to handle radialGradient with gradientUnits=%1").arg(element.attribute("gradientUnits"))); } } else { doChildren = true; } if (doChildren) { QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { normalizeChild(childElement, sNewWidth, sNewHeight, vbWidth, vbHeight, blackOnly); childElement = childElement.nextSiblingElement(); } } } bool SvgFileSplitter::normalizeAttribute(QDomElement & element, const char * attributeName, double num, double denom) { QString attributeValue = element.attribute(attributeName); if (attributeValue.isEmpty()) return true; bool ok; double n = attributeValue.toDouble(&ok) * num / denom; if (!ok) { QString string; QTextStream stream(&string); element.save(stream, 0); //DebugDialog::debug("bad attribute " + string); } element.setAttribute(attributeName, QString::number(n)); return ok; } QString SvgFileSplitter::shift(double x, double y, const QString & elementID, bool shiftTransforms) { QDomElement root = m_domDocument.documentElement(); QDomElement mainElement = TextUtils::findElementWithAttribute(root, "id", elementID); if (mainElement.isNull()) return ""; QDomElement childElement = mainElement.firstChildElement(); while (!childElement.isNull()) { shiftChild(childElement, x, y, shiftTransforms); childElement = childElement.nextSiblingElement(); } QDomDocument document; QDomNode node = document.importNode(mainElement, true); document.appendChild(node); return document.toString(); } bool SvgFileSplitter::shiftTranslation(QDomElement & element, double x, double y) { QString attr = element.attribute("transform"); if (attr.isEmpty()) return false; QMatrix m0 = TextUtils::elementToMatrix(element); bool ok1, ok2, ok3; double _x = element.attribute("_x").toDouble(&ok1); double _y = element.attribute("_y").toDouble(&ok2); double _r = element.attribute("_r").toDouble(&ok3); if (ok1 && ok2 && ok3) { QMatrix mx = QMatrix().translate(-_x - x, -_y - y) * QMatrix().rotate(_r) * QMatrix().translate(_x + x, _y + y); TextUtils::setSVGTransform(element, mx); element.removeAttribute("_x"); element.removeAttribute("_y"); element.removeAttribute("_r"); return false; } // calculate the original cx and cy and retranslate (there must be an easier way) // and this doesn't seem to work... double sinTheta = m0.m21(); double cosTheta = m0.m11(); double cosThetaMinusOne = cosTheta - 1; double cx = (m0.dx() + (sinTheta * m0.dy() / cosThetaMinusOne)) / (cosThetaMinusOne + (sinTheta * sinTheta / cosThetaMinusOne)); double cy = (m0.dy() - (sinTheta * cx)) / cosThetaMinusOne; cx += x; cy += y; QMatrix m1(m0.m11(), m0.m12(), m0.m21(), m0.m22(), 0, 0); QMatrix mx = QMatrix().translate(-cx, -cy) * m1 * QMatrix().translate(cx, cy); TextUtils::setSVGTransform(element, mx); return false; } void SvgFileSplitter::shiftChild(QDomElement & element, double x, double y, bool shiftTransforms) { if (shiftTransforms) { shiftTranslation(element, x, y); } QString nodeName = element.nodeName(); if (nodeName.compare("circle") == 0 || nodeName.compare("ellipse") == 0) { shiftAttribute(element, "cx", x); shiftAttribute(element, "cy", y); } else if (nodeName.compare("line") == 0) { shiftAttribute(element, "x1", x); shiftAttribute(element, "y1", y); shiftAttribute(element, "x2", x); shiftAttribute(element, "y2", y); } else if (nodeName.compare("rect") == 0) { shiftAttribute(element, "x", x); shiftAttribute(element, "y", y); } else if (nodeName.compare("text") == 0) { shiftAttribute(element, "x", x); shiftAttribute(element, "y", y); } else if (nodeName.compare("polygon") == 0 || nodeName.compare("polyline") == 0) { QString data = element.attribute("points"); if (!data.isEmpty()) { const char * slot = SLOT(shiftCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.pathStarting = true; pathUserData.x = x; pathUserData.y = y; if (parsePath(data, slot, pathUserData, this, false)) { pathUserData.string.remove(0, 1); // get rid of the "M" element.setAttribute("points", pathUserData.string); } } } else if (nodeName.compare("path") == 0) { QString data = element.attribute("d").trimmed(); if (!data.isEmpty()) { const char * slot = SLOT(shiftCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.pathStarting = true; pathUserData.x = x; pathUserData.y = y; if (parsePath(data, slot, pathUserData, this, true)) { element.setAttribute("d", pathUserData.string); } } } else { QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { shiftChild(childElement, x, y, shiftTransforms); childElement = childElement.nextSiblingElement(); } } } void SvgFileSplitter::normalizeCommandSlot(QChar command, bool relative, QList & args, void * userData) { Q_UNUSED(relative); // just normalizing here, so relative is not used PathUserData * pathUserData = (PathUserData *) userData; double d; pathUserData->string.append(command); switch(command.toLatin1()) { case 'v': case 'V': //DebugDialog::debug("'v' and 'V' are now removed by preprocessing; shouldn't be here"); /* for (int i = 0; i < args.count(); i++) { d = args[i] * pathUserData->sNewHeight / pathUserData->vbHeight; pathUserData->string.append(QString::number(d)); if (i < args.count() - 1) { pathUserData->string.append(','); } } */ break; case 'h': case 'H': //DebugDialog::debug("'h' and 'H' are now removed by preprocessing; shouldn't be here"); /* for (int i = 0; i < args.count(); i++) { d = args[i] * pathUserData->sNewWidth / pathUserData->vbWidth; pathUserData->string.append(QString::number(d)); if (i < args.count() - 1) { pathUserData->string.append(','); } } */ break; case 'a': case 'A': for (int i = 0; i < args.count(); i++) { switch (i % 7) { case 0: case 5: d = args[i] * pathUserData->sNewWidth / pathUserData->vbWidth; break; case 1: case 6: d = args[i] * pathUserData->sNewHeight / pathUserData->vbHeight; break; default: d = args[i]; break; } pathUserData->string.append(QString::number(d)); if (i < args.count() - 1) { pathUserData->string.append(','); } } break; case SVGPathLexer::FakeClosePathChar: break; default: for (int i = 0; i < args.count(); i++) { if (i % 2 == 0) { d = args[i] * pathUserData->sNewWidth / pathUserData->vbWidth; } else { d = args[i] * pathUserData->sNewHeight / pathUserData->vbHeight; } pathUserData->string.append(QString::number(d)); if (i < args.count() - 1) { pathUserData->string.append(','); } } break; } } void SvgFileSplitter::painterPathCommandSlot(QChar command, bool relative, QList & args, void * userData) { Q_UNUSED(relative); // just normalizing here, so relative is not used Q_UNUSED(command) // note: painterPathCommandSlot is only partially implemented PathUserData * pathUserData = (PathUserData *) userData; double dx, dy; for (int i = 0; i < args.count(); i += 2) { dx = args[i]; dy = args[i + 1]; if (i == 0) { pathUserData->painterPath->moveTo(dx, dy); } else { pathUserData->painterPath->lineTo(dx, dy); } } pathUserData->painterPath->closeSubpath(); } void SvgFileSplitter::shiftCommandSlot(QChar command, bool relative, QList & args, void * userData) { Q_UNUSED(relative); // just normalizing here, so relative is not used PathUserData * pathUserData = (PathUserData *) userData; double d; pathUserData->string.append(command); switch(command.toLatin1()) { case 'v': case 'V': //DebugDialog::debug("'v' and 'V' are now removed by preprocessing; shouldn't be here"); /* d = args[0]; if (!relative) { d += pathUserData->y; } pathUserData->string.append(QString::number(d)); */ break; case 'h': case 'H': //DebugDialog::debug("'h' and 'H' are now removed by preprocessing; shouldn't be here"); /* d = args[0]; if (!relative) { d += pathUserData->x; } pathUserData->string.append(QString::number(d)); */ break; case 'z': case 'Z': pathUserData->pathStarting = true; break; case SVGPathLexer::FakeClosePathChar: pathUserData->pathStarting = true; break; case 'a': case 'A': for (int i = 0; i < args.count(); i++) { d = args[i]; if (i % 7 == 5) { if (!relative) { d += pathUserData->x; } } else if (i % 7 == 6) { if (!relative) { d += pathUserData->y; } } pathUserData->string.append(QString::number(d)); if (i < args.count() - 1) { pathUserData->string.append(','); } } break; case 'm': case'M': standardArgs(relative, pathUserData->pathStarting, args, pathUserData); pathUserData->pathStarting = false; break; default: standardArgs(relative, false, args, pathUserData); break; } } void SvgFileSplitter::standardArgs(bool relative, bool starting, QList & args, PathUserData * pathUserData) { for (int i = 0; i < args.count(); i++) { double d = args[i]; if (i % 2 == 0) { if (!relative || (starting && i == 0)) { d += pathUserData->x; } } else { if (!relative || (starting && i == 1)) { d += pathUserData->y; } } pathUserData->string.append(QString::number(d)); if (i < args.count() - 1) { pathUserData->string.append(','); } } } QVector SvgFileSplitter::simpleParsePath(const QString & data) { static QVector emptyStack; QString dataCopy(data); if (!dataCopy.startsWith('M', Qt::CaseInsensitive)) { dataCopy.prepend('M'); } while (dataCopy.at(dataCopy.length() - 1).isSpace()) { dataCopy.remove(dataCopy.length() - 1, 1); } QChar last = dataCopy.at(dataCopy.length() - 1); if (last != 'z' && last != 'Z' && last != SVGPathLexer::FakeClosePathChar) { dataCopy.append(SVGPathLexer::FakeClosePathChar); } SVGPathLexer lexer(dataCopy); SVGPathParser parser; bool result = parser.parse(&lexer); if (!result) { //DebugDialog::debug(QString("svg path parse failed %1").arg(dataCopy)); return emptyStack; } return parser.symStack(); } bool SvgFileSplitter::parsePath(const QString & dataString, const char * slot, PathUserData & pathUserData, QObject * slotTarget, bool convertHV) { QVector symStack = simpleParsePath(dataString); if (convertHV && (dataString.contains("h", Qt::CaseInsensitive) || dataString.contains("v", Qt::CaseInsensitive))) { SVGPathRunner svgPathRunner; HVConvertData data; data.x = data.y = data.subX = data.subY = 0; data.path = ""; connect(&svgPathRunner, SIGNAL(commandSignal(QChar, bool, QList &, void *)), this, SLOT(convertHVSlot(QChar, bool, QList &, void *)), Qt::DirectConnection); svgPathRunner.runPath(symStack, &data); return parsePath(data.path, slot, pathUserData, slotTarget, false); } SVGPathRunner svgPathRunner; connect(&svgPathRunner, SIGNAL(commandSignal(QChar, bool, QList &, void *)), slotTarget, slot, Qt::DirectConnection); return svgPathRunner.runPath(symStack, &pathUserData); } void SvgFileSplitter::convertHVSlot(QChar command, bool relative, QList & args, void * userData) { Q_UNUSED(relative); HVConvertData * data = (HVConvertData *) userData; double x, y; switch(command.toLatin1()) { case 'M': data->path.append(command); for (int i = 0; i < args.count(); i += 2) { data->x = data->subX = args[i]; data->y = data->subY = args[i + 1]; appendPair(data->path, args[i], args[i + 1]); } data->path.chop(1); break; case 'm': data->path.append(command); x = data->x; y = data->y; for (int i = 0; i < args.count(); i += 2) { data->subX = data->x = (x + args[i]); data->subY = data->y = (y + args[i + 1]); appendPair(data->path, args[i], args[i + 1]); } data->path.chop(1); break; case 'L': case 'T': data->path.append(command); for (int i = 0; i < args.count(); i += 2) { data->x = args[i]; data->y = args[i + 1]; appendPair(data->path, args[i], args[i + 1]); } data->path.chop(1); break; case 'l': case 't': data->path.append(command); x = data->x; y = data->y; for (int i = 0; i < args.count(); i += 2) { data->x = x + args[i]; data->y = y + args[i + 1]; appendPair(data->path, args[i], args[i + 1]); } data->path.chop(1); break; case 'C': data->path.append(command); for (int i = 0; i < args.count(); i += 6) { data->x = args[i + 4]; data->y = args[i + 5]; appendPair(data->path, args[i], args[i + 1]); appendPair(data->path, args[i + 2], args[i + 3]); appendPair(data->path, args[i + 4], args[i + 5]); } data->path.chop(1); break; case 'c': data->path.append(command); x = data->x; y = data->y; for (int i = 0; i < args.count(); i += 6) { data->x = x + args[i + 4]; data->y = y + args[i + 5]; appendPair(data->path, args[i], args[i + 1]); appendPair(data->path, args[i + 2], args[i + 3]); appendPair(data->path, args[i + 4], args[i + 5]); } data->path.chop(1); break; case 'S': case 'Q': data->path.append(command); for (int i = 0; i < args.count(); i += 4) { data->x = args[i + 2]; data->y = args[i + 3]; appendPair(data->path, args[i], args[i + 1]); appendPair(data->path, args[i + 2], args[i + 3]); } data->path.chop(1); break; case 's': case 'q': data->path.append(command); x = data->x; y = data->y; for (int i = 0; i < args.count(); i += 4) { data->x = x + args[i + 2]; data->y = y + args[i + 3]; appendPair(data->path, args[i], args[i + 1]); appendPair(data->path, args[i + 2], args[i + 3]); } data->path.chop(1); break; case 'Z': case 'z': data->path.append(command); data->x = data->subX; data->y = data->subY; break; case SVGPathLexer::FakeClosePathChar: data->path.append(command); break; case 'A': data->path.append(command); for (int i = 0; i < args.count(); i += 7) { data->x = args[i + 5]; data->y = args[i + 6]; appendPair(data->path, args[i], args[i + 1]); appendPair(data->path, args[i + 2], args[i + 3]); appendPair(data->path, args[i + 4], args[i + 5]); data->path.append(QString::number(args[i + 6])); data->path.append(','); } data->path.chop(1); break; case 'a': data->path.append(command); x = data->x; y = data->y; for (int i = 0; i < args.count(); i += 7) { data->x = x + args[i + 5]; data->y = y + args[i + 6]; appendPair(data->path, args[i], args[i + 1]); appendPair(data->path, args[i + 2], args[i + 3]); appendPair(data->path, args[i + 4], args[i + 5]); data->path.append(QString::number(args[i + 6])); data->path.append(','); } data->path.chop(1); break; case 'v': data->path.append('l'); y = data->y; for (int i = 0; i < args.count(); i++) { data->y = y + args[i]; appendPair(data->path, 0, args[i]); } data->path.chop(1); break; case 'h': data->path.append('l'); x = data->x; for (int i = 0; i < args.count(); i++) { data->x = x + args[i]; appendPair(data->path, args[i], 0); } data->path.chop(1); break; case 'H': data->path.append('L'); for (int i = 0; i < args.count(); i++) { data->x = args[i]; appendPair(data->path, args[i], data->y); } data->path.chop(1); break; case 'V': data->path.append('L'); for (int i = 0; i < args.count(); i++) { data->y = args[i]; appendPair(data->path, data->x, args[i]); } data->path.chop(1); break; default: //DebugDialog::debug(QString("unknown path command %1").arg(command)); data->path.append(command); for (int i = 0; i < args.count(); i++) { data->path.append(QString::number(args[i])); data->path.append(','); } data->path.chop(1); break; } } void SvgFileSplitter::setStrokeOrFill(QDomElement & element, bool blackOnly, const QString & color, bool force) { if (!blackOnly) return; // if stroke attribute is not empty make it black // if fill attribute is not empty and not "none" make it black QString stroke = element.attribute("stroke"); if (!stroke.isEmpty() || force) { if (stroke.compare("none") != 0) { element.setAttribute("stroke", color); } } QString fill = element.attribute("fill"); if (!fill.isEmpty()) { if (fill.compare("none") != 0) { element.setAttribute("fill", color); } } } void SvgFileSplitter::fixStyleAttributeRecurse(QDomElement & element) { TextUtils::fixStyleAttribute(element); QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { fixStyleAttributeRecurse(childElement); childElement = childElement.nextSiblingElement(); } } void SvgFileSplitter::fixColorRecurse(QDomElement & element, const QString & newColor, const QStringList & exceptions) { TextUtils::fixStyleAttribute(element); bool gotException = false; QString s = element.attribute("stroke"); QString f = element.attribute("fill"); QString id = element.attribute("id"); foreach (QString e, exceptions) { if (s.isEmpty()) { if (f.isEmpty()) { } else { if (exceptions.contains(f)) { gotException = true; break; } } } else { if (f.isEmpty()) { if (exceptions.contains(s)) { gotException = true; break; } } else { if (exceptions.contains(s) && exceptions.contains(f)) { gotException = true; break; } } } } if (!gotException) { setStrokeOrFill(element, true, newColor, !id.isEmpty()); } QDomElement childElement = element.firstChildElement(); while (!childElement.isNull()) { fixColorRecurse(childElement, newColor, exceptions); childElement = childElement.nextSiblingElement(); } } bool SvgFileSplitter::getSvgSizeAttributes(const QString & svg, QString & width, QString & height, QString & viewBox) { QXmlStreamReader xml(svg); xml.setNamespaceProcessing(false); while (!xml.atEnd()) { switch (xml.readNext()) { case QXmlStreamReader::StartElement: if (xml.name().toString().compare("svg") == 0) { width = xml.attributes().value("width").toString(); height = xml.attributes().value("height").toString(); viewBox = xml.attributes().value("viewBox").toString(); return true; } default: break; } } return false; } bool SvgFileSplitter::changeStrokeWidth(const QString & svg, double delta, bool absolute, bool changeOpacity, QByteArray & byteArray) { QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(svg, true, &errorStr, &errorLine, &errorColumn)) { return false; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { return false; } if (root.tagName() != "svg") { return false; } changeStrokeWidth(root, delta, absolute, changeOpacity); byteArray = domDocument.toByteArray(); return true; } void SvgFileSplitter::changeStrokeWidth(QDomElement & element, double delta, bool absolute, bool changeOpacity) { bool ok; double sw = element.attribute("stroke-width").toDouble(&ok); if (ok) { element.setAttribute("stroke-width", QString::number((absolute) ? delta : sw + delta)); } if (changeOpacity) { QString fillo = element.attribute("fill-opacity"); if (!fillo.isEmpty()) { element.setAttribute("fill-opacity", "1.0"); } QString sillo = element.attribute("stroke-opacity"); if (!sillo.isEmpty()) { element.setAttribute("stroke-opacity", "1.0"); } } QDomElement child = element.firstChildElement(); while (!child.isNull()) { changeStrokeWidth(child, delta, absolute, changeOpacity); child = child.nextSiblingElement(); } } void SvgFileSplitter::forceStrokeWidth(QDomElement & element, double delta, const QString & stroke, bool recurse, bool fill) { bool ok; double sw = element.attribute("stroke-width").toDouble(&ok); if (!ok) sw = 0; element.setAttribute("stroke-width", QString::number(qMax(0, sw + delta))); element.setAttribute("stroke", stroke); if (fill) element.setAttribute("fill", stroke); if (recurse) { QDomElement child = element.firstChildElement(); while (!child.isNull()) { forceStrokeWidth(child, delta, stroke, recurse, fill); child = child.nextSiblingElement(); } } } bool SvgFileSplitter::changeColors(const QString & svg, QString & toColor, QStringList & exceptions, QByteArray & byteArray) { QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(svg, true, &errorStr, &errorLine, &errorColumn)) { return false; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { return false; } if (root.tagName() != "svg") { return false; } changeColors(root, toColor, exceptions); byteArray = domDocument.toByteArray(); return true; } void SvgFileSplitter::changeColors(QDomElement & element, QString & toColor, QStringList & exceptions) { QString c = element.attribute("stroke"); if (!exceptions.contains(c)) { element.setAttribute("stroke", toColor); } c = element.attribute("fill"); if (!exceptions.contains(c)) { element.setAttribute("fill", toColor); } QString fillo = element.attribute("fill-opacity"); if (!fillo.isEmpty()) { element.setAttribute("fill-opacity", "1.0"); } QString sillo = element.attribute("stroke-opacity"); if (!sillo.isEmpty()) { element.setAttribute("stroke-opacity", "1.0"); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { changeColors(child, toColor, exceptions); child = child.nextSiblingElement(); } } bool SvgFileSplitter::shiftAttribute(QDomElement & element, const char * attributeName, double d) { double n = element.attribute(attributeName).toDouble() + d; element.setAttribute(attributeName, QString::number(n)); return true; } bool SvgFileSplitter::load(const QString string) { QString errorStr; int errorLine; int errorColumn; return m_domDocument.setContent(string, true, &errorStr, &errorLine, &errorColumn); } bool SvgFileSplitter::load(const QString * filename) { QFile file(*filename); return load(&file); } bool SvgFileSplitter::load(QFile * file) { QString errorStr; int errorLine; int errorColumn; return m_domDocument.setContent(file, true, &errorStr, &errorLine, &errorColumn); } QString SvgFileSplitter::toString() { return TextUtils::removeXMLEntities(m_domDocument.toString()); } void SvgFileSplitter::gWrap(const QHash & attributes) { TextUtils::gWrap(m_domDocument, attributes); } void SvgFileSplitter::gReplace(const QString & id) { QDomElement element = TextUtils::findElementWithAttribute(m_domDocument.documentElement(), "id", id); if (element.isNull()) return; element.setTagName("g"); } QByteArray SvgFileSplitter::hideText(const QString & filename) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; QFile file(filename); if (!doc.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { return QByteArray(); } QDomElement root = doc.documentElement(); hideTextAux(root, false); return TextUtils::removeXMLEntities(doc.toString()).toUtf8(); } QByteArray SvgFileSplitter::hideText2(const QByteArray & svg) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { return QByteArray(); } QDomElement root = doc.documentElement(); hideTextAux(root, false); return TextUtils::removeXMLEntities(doc.toString()).toUtf8(); } QString SvgFileSplitter::hideText3(const QString & svg) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { return ""; } QDomElement root = doc.documentElement(); hideTextAux(root, false); return TextUtils::removeXMLEntities(doc.toString()); } void SvgFileSplitter::hideTextAux(QDomElement & parent, bool hideChildren) { if (hideChildren || parent.tagName() == "text") { parent.setTagName("g"); hideChildren = true; } QDomElement child = parent.firstChildElement(); while (!child.isNull()) { hideTextAux(child, hideChildren); child = child.nextSiblingElement(); } } QByteArray SvgFileSplitter::showText(const QString & filename, bool & hasText) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; QFile file(filename); if (!doc.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { return QByteArray(); } QDomElement root = doc.documentElement(); showTextAux(root, hasText, true); if (!hasText) { return QByteArray(); } return TextUtils::removeXMLEntities(doc.toString()).toUtf8(); } QByteArray SvgFileSplitter::showText2(const QByteArray & svg, bool & hasText) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { return QByteArray(); } QDomElement root = doc.documentElement(); showTextAux(root, hasText, true); if (!hasText) { return QByteArray(); } return TextUtils::removeXMLEntities(doc.toString()).toUtf8(); } QString SvgFileSplitter::showText3(const QString & svg, bool & hasText) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) { return ""; } QDomElement root = doc.documentElement(); showTextAux(root, hasText, true); if (!hasText) { return ""; } return TextUtils::removeXMLEntities(doc.toString()); } void SvgFileSplitter::showTextAux(QDomElement & parent, bool & hasText, bool root) { if (parent.tagName() == "text") { hasText = true; return; } if (!root) { parent.setTagName("g"); } QDomElement child = parent.firstChildElement(); while (!child.isNull()) { showTextAux(child, hasText, false); child = child.nextSiblingElement(); } } fritzing-app-0.9.3b/src/svg/svgfilesplitter.h000066400000000000000000000122641272363601300212740ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6980 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 01:45:43 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef SVGFILESPLITTER_H_ #define SVGFILESPLITTER_H_ #include #include #include #include #include #include #include #include struct PathUserData { QString string; QMatrix transform; double sNewWidth; double sNewHeight; double vbWidth; double vbHeight; double x; double y; bool pathStarting; QPainterPath * painterPath; }; class SvgFileSplitter : public QObject { Q_OBJECT public: SvgFileSplitter(); bool split(const QString & filename, const QString & elementID); bool splitString(QString & contents, const QString & elementID); const QByteArray & byteArray(); const QDomDocument & domDocument(); bool normalize(double dpi, const QString & elementID, bool blackOnly, double & factor); QString shift(double x, double y, const QString & elementID, bool shiftTransforms); QString elementString(const QString & elementID); virtual bool parsePath(const QString & data, const char * slot, PathUserData &, QObject * slotTarget, bool convertHV); QVector simpleParsePath(const QString & data); QPainterPath painterPath(double dpi, const QString & elementID); // note: only partially implemented void shiftChild(QDomElement & element, double x, double y, bool shiftTransforms); bool load(const QString * filename); bool load(QFile *); bool load(const QString string); QString toString(); void gWrap(const QHash & attributes); void gReplace(const QString & id); public: static bool getSvgSizeAttributes(const QString & svg, QString & width, QString & height, QString & viewBox); static bool changeStrokeWidth(const QString & svg, double delta, bool absolute, bool changeOpacity, QByteArray &); static void changeStrokeWidth(QDomElement & element, double delta, bool absolute, bool changeOpacity); static void forceStrokeWidth(QDomElement & element, double delta, const QString & stroke, bool recurse, bool fill); static bool changeColors(const QString & svg, QString & toColor, QStringList & exceptions, QByteArray &); static void changeColors(QDomElement & element, QString & toColor, QStringList & exceptions); static void fixStyleAttributeRecurse(QDomElement & element); static void fixColorRecurse(QDomElement & element, const QString & newColor, const QStringList & exceptions); static QByteArray hideText(const QString & filename); static QByteArray showText(const QString & filename, bool & hasText); static QByteArray hideText2(const QByteArray & svg); static QByteArray showText2(const QByteArray & svg, bool & hasText); static QString hideText3(const QString & svg); static QString showText3(const QString & svg, bool & hasText); protected: void normalizeChild(QDomElement & childElement, double sNewWidth, double sNewHeight, double vbWidth, double vbHeight, bool blackOnly); bool normalizeAttribute(QDomElement & element, const char * attributeName, double num, double denom); void painterPathChild(QDomElement & element, QPainterPath & ppath); // note: only partially implemented void normalizeTranslation(QDomElement & element, double sNewWidth, double sNewHeight, double vbWidth, double vbHeight); bool shiftTranslation(QDomElement & element, double x, double y); void standardArgs(bool relative, bool starting, QList & args, PathUserData * pathUserData); protected: static bool shiftAttribute(QDomElement & element, const char * attributeName, double d); static void setStrokeOrFill(QDomElement & element, bool doIt, const QString & color, bool force); static void hideTextAux(QDomElement & parent, bool hideChildren); static void showTextAux(QDomElement & parent, bool & hasText, bool root); protected slots: void normalizeCommandSlot(QChar command, bool relative, QList & args, void * userData); void shiftCommandSlot(QChar command, bool relative, QList & args, void * userData); virtual void rotateCommandSlot(QChar, bool, QList &, void *){} void painterPathCommandSlot(QChar command, bool relative, QList & args, void * userData); void convertHVSlot(QChar command, bool relative, QList & args, void * userData); protected: QByteArray m_byteArray; QDomDocument m_domDocument; }; #endif fritzing-app-0.9.3b/src/svg/svgflattener.cpp000066400000000000000000000316011272363601300211010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6435 $: $Author: cohen@irascible.com $: $Date: 2012-09-16 23:31:00 +0200 (So, 16. Sep 2012) $ ********************************************************************/ #include "svgflattener.h" #include "svgpathlexer.h" #include "../utils/graphicsutils.h" #include "../utils/textutils.h" #include "../debugdialog.h" #include #include #include #include SvgFlattener::SvgFlattener() : SvgFileSplitter() { } void SvgFlattener::flattenChildren(QDomElement &element){ // recurse the children QDomNodeList childList = element.childNodes(); for(int i = 0; i < childList.length(); i++){ QDomElement child = childList.item(i).toElement(); flattenChildren(child); } //do translate if(hasTranslate(element)){ QList params = TextUtils::getTransformFloats(element); if (params.size() == 2) { shiftChild(element, params.at(0), params.at(1), false); //DebugDialog::debug(QString("translating %1 %2").arg(params.at(0)).arg(params.at(1))); } else if (params.size() == 6) { shiftChild(element, params.at(4), params.at(5), false); } else if (params.size() == 1) { shiftChild(element, params.at(0), 0, false); //DebugDialog::debug(QString("translating %1").arg(params.at(0))); } else { DebugDialog::debug("weird transform found"); } } else if(hasOtherTransform(element)) { QMatrix transform = TextUtils::transformStringToMatrix(element.attribute("transform")); //DebugDialog::debug(QString("rotating %1 %2 %3 %4 %5 %6").arg(params.at(0)).arg(params.at(1)).arg(params.at(2)).arg(params.at(3)).arg(params.at(4)).arg(params.at(5))); unRotateChild(element, transform); } // remove transform element.removeAttribute("transform"); } void SvgFlattener::unRotateChild(QDomElement & element, QMatrix transform) { // TODO: missing ellipse element if(!element.hasChildNodes()) { QString sw = element.attribute("stroke-width"); if (!sw.isEmpty()) { bool ok; double strokeWidth = sw.toDouble(&ok); if (ok) { QLineF line(0, 0, strokeWidth, 0); QLineF newLine = transform.map(line); element.setAttribute("stroke-width", QString::number(newLine.length())); } } // I'm a leaf node. QString tag = element.nodeName().toLower(); if(tag == "path"){ QString data = element.attribute("d").trimmed(); if (!data.isEmpty()) { const char * slot = SLOT(rotateCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.transform = transform; if (parsePath(data, slot, pathUserData, this, true)) { element.setAttribute("d", pathUserData.string); } } } else if ((tag == "polygon") || (tag == "polyline")) { QString data = element.attribute("points"); if (!data.isEmpty()) { const char * slot = SLOT(rotateCommandSlot(QChar, bool, QList &, void *)); PathUserData pathUserData; pathUserData.transform = transform; if (parsePath(data, slot, pathUserData, this, false)) { pathUserData.string.remove(0, 1); // get rid of the "M" element.setAttribute("points", pathUserData.string); } } } else if(tag == "rect"){ float x = element.attribute("x").toFloat(); float y = element.attribute("y").toFloat(); float width = element.attribute("width").toFloat(); float height = element.attribute("height").toFloat(); QRectF r(x, y, width, height); QPolygonF poly = transform.map(r); if (GraphicsUtils::isRect(poly)) { QRectF rect = GraphicsUtils::getRect(poly); element.setAttribute("x", QString::number(rect.left())); element.setAttribute("y", QString::number(rect.top())); element.setAttribute("width", QString::number(rect.width())); element.setAttribute("height", QString::number(rect.height())); } else { element.setTagName("polygon"); QString pts; for (int i = 1; i < poly.count(); i++) { QPointF p = poly.at(i); pts += QString("%1,%2 ").arg(p.x()).arg(p.y()); } pts.chop(1); element.setAttribute("points", pts); } } else if(tag == "circle"){ float cx = element.attribute("cx").toFloat(); float cy = element.attribute("cy").toFloat(); float r = element.attribute("r").toFloat(); QPointF point = transform.map(QPointF(cx,cy)); element.setAttribute("cx", QString::number(point.x())); element.setAttribute("cy", QString::number(point.y())); QLineF line(0, 0, r, 0); QLineF newLine = transform.map(line); element.setAttribute("r", QString::number(newLine.length())); } else if(tag == "line") { float x1 = element.attribute("x1").toFloat(); float y1 = element.attribute("y1").toFloat(); QPointF p1 = transform.map(QPointF(x1,y1)); element.setAttribute("x1", QString::number(p1.x())); element.setAttribute("y1", QString::number(p1.y())); float x2 = element.attribute("x2").toFloat(); float y2 = element.attribute("y2").toFloat(); QPointF p2 = transform.map(QPointF(x2,y2)); element.setAttribute("x2", QString::number(p2.x())); element.setAttribute("y2", QString::number(p2.y())); } else if (tag == "g") { // no op } else if (tag.isEmpty()) { } else { DebugDialog::debug("Warning! Can't rotate element: " + tag); } return; } // recurse the children QDomNodeList childList = element.childNodes(); for(int i = 0; i < childList.length(); i++){ QDomElement child = childList.item(i).toElement(); unRotateChild(child, transform); } } bool SvgFlattener::hasTranslate(QDomElement & element) { QString transform = element.attribute("transform"); if (transform.isEmpty()) return false; if (transform.startsWith("translate")) return true; if (transform.startsWith("matrix")) { QMatrix matrix = TextUtils::transformStringToMatrix(transform); matrix.translate(-matrix.dx(), -matrix.dy()); if (matrix.isIdentity()) return true; } return false; } bool SvgFlattener::hasOtherTransform(QDomElement & element) { QString transform = element.attribute("transform"); if (transform.isEmpty()) return false; // NOTE: doesn't handle multiple transform attributes... return (!transform.contains("translate")); } void SvgFlattener::rotateCommandSlot(QChar command, bool relative, QList & args, void * userData) { Q_UNUSED(relative); // just normalizing here, so relative is not used PathUserData * pathUserData = (PathUserData *) userData; pathUserData->string.append(command); double x; double y; QPointF point; for (int i = 0; i < args.count(); ) { switch(command.toLatin1()) { case 'v': case 'V': DebugDialog::debug("'v' and 'V' are now removed by preprocessing; shouldn't be here"); /* y = args[i]; x = 0; // what is x, really? DebugDialog::debug("Warning! Can't rotate path with V"); */ i++; break; case 'h': case 'H': DebugDialog::debug("'h' and 'H' are now removed by preprocessing; shouldn't be here"); /* x = args[i]; y = 0; // what is y, really? DebugDialog::debug("Warning! Can't rotate path with H"); */ i++; break; case SVGPathLexer::FakeClosePathChar: break; case 'a': case 'A': // TODO: test whether this is correct for (int j = 0; j < 5; j++) { pathUserData->string.append(QString::number(args[j])); pathUserData->string.append(','); } x = args[5]; y = args[6]; i += 7; point = pathUserData->transform.map(QPointF(x,y)); pathUserData->string.append(QString::number(point.x())); pathUserData->string.append(','); pathUserData->string.append(QString::number(point.y())); if (i < args.count()) { pathUserData->string.append(','); } break; default: x = args[i]; y = args[i+1]; i += 2; point = pathUserData->transform.map(QPointF(x,y)); pathUserData->string.append(QString::number(point.x())); pathUserData->string.append(','); pathUserData->string.append(QString::number(point.y())); if (i < args.count()) { pathUserData->string.append(','); } } } } void SvgFlattener::replaceElementID(const QString & filename, const QString & svg, QDomDocument & domDocument, const QString & elementID, const QString & altElementID) { if (!loadDocIf(filename, svg, domDocument)) return; QDomElement root = domDocument.documentElement(); QDomElement element = TextUtils::findElementWithAttribute(root, "id", elementID); if (!element.isNull()) { element.setAttribute("id", altElementID); } } void SvgFlattener::flipSMDSvg(const QString & filename, const QString & svg, QDomDocument & flipDoc, const QString & elementID, const QString & altElementID, double printerScale, Qt::Orientations orientation) { QDomDocument domDocument; if (!loadDocIf(filename, svg, domDocument)) return; QDomElement root = domDocument.documentElement(); QDomElement element = TextUtils::findElementWithAttribute(root, "id", elementID); if (!element.isNull()) { QDomElement altElement = TextUtils::findElementWithAttribute(root, "id", altElementID); QString svg = flipSMDElement(domDocument, element, elementID, altElement, altElementID, printerScale, orientation); if (svg.length() > 0) { flipDoc.setContent(svg); } } } QString SvgFlattener::flipSMDElement(QDomDocument & domDocument, QDomElement & element, const QString & att, QDomElement altElement, const QString & altAtt, double printerScale, Qt::Orientations orientation) { Q_UNUSED(printerScale); Q_UNUSED(att); QMatrix m; //QRectF bounds = renderer.boundsOnElement(att); QRectF bounds; // want part bounds, not layer bounds double w, h; TextUtils::ensureViewBox(domDocument, 1, bounds, false, w, h, false); m.translate(bounds.center().x(), bounds.center().y()); QMatrix mMinus = m.inverted(); QMatrix cm = mMinus * ((orientation & Qt::Vertical) ? QMatrix().scale(-1, 1) : QMatrix().scale(1, -1)) * m; QDomElement newElement = element.cloneNode(true).toElement(); #ifndef QT_NODEBUG QString string; QTextStream textStream(&string); newElement.save(textStream, 1); #endif newElement.removeAttribute("id"); QDomElement pElement = domDocument.createElement("g"); pElement.setAttribute("id", altAtt); pElement.setAttribute("flipped", true); QDomElement mElement = domDocument.createElement("g"); TextUtils::setSVGTransform(mElement, cm); pElement.appendChild(mElement); QDomElement tElementChild = domDocument.createElement("g"); QDomElement tElementParent = tElementChild; QDomElement parent = element.parentNode().toElement(); while (!parent.isNull()) { QString transform = parent.attribute("transform"); if (!transform.isEmpty()) { QDomElement t = domDocument.createElement("g"); t.setAttribute("transform", transform); t.appendChild(tElementParent); tElementParent = t; } parent = parent.parentNode().toElement(); } mElement.appendChild(tElementParent); tElementChild.appendChild(newElement); if (!altElement.isNull()) { tElementChild.appendChild(altElement); altElement.removeAttribute("id"); } domDocument.documentElement().appendChild(pElement); return domDocument.toString(); } bool SvgFlattener::loadDocIf(const QString & filename, const QString & svg, QDomDocument & domDocument) { if (domDocument.isNull()) { QString errorStr; int errorLine; int errorColumn; bool result; if (filename.isEmpty()) { result = domDocument.setContent(svg, &errorStr, &errorLine, &errorColumn); } else { QFile file(filename); result = domDocument.setContent(&file, &errorStr, &errorLine, &errorColumn); } if (!result) { domDocument.clear(); // probably redundant return false; } } return true; } fritzing-app-0.9.3b/src/svg/svgflattener.h000066400000000000000000000042731272363601300205530ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SVGFLATTENER_H #define SVGFLATTENER_H #include "svgfilesplitter.h" #include #include class SvgFlattener : public SvgFileSplitter { public: SvgFlattener(); void flattenChildren(QDomElement & element); void unRotateChild(QDomElement & element,QMatrix transform); public: static void flipSMDSvg(const QString & filename, const QString & svg, QDomDocument & flipDoc, const QString & elementID, const QString & altElementID, double printerScale, Qt::Orientations); static void replaceElementID(const QString & filename, const QString & svg, QDomDocument & flipDoc, const QString & elementID, const QString & altElementID); protected: static QString flipSMDElement(QDomDocument & domDocument, QDomElement & element, const QString & att, QDomElement altAtt, const QString & altElementID, double printerScale, Qt::Orientations); static bool hasOtherTransform(QDomElement & element); static bool hasTranslate(QDomElement & element); static bool loadDocIf(const QString & filename, const QString & svg, QDomDocument & domDocument); protected slots: void rotateCommandSlot(QChar command, bool relative, QList & args, void * userData); }; #endif // SVGFLATTENER_H fritzing-app-0.9.3b/src/svg/svgpath.g000066400000000000000000000342711272363601300175230ustar00rootroot00000000000000%parser SVGPathGrammar %decl svgpathparser.h %impl svgpathparser.cpp %token EM %token ZEE %token EKS %token EL %token AITCH %token VEE %token CEE %token ESS %token KYU %token TEE %token AE %token COMMA %token NUMBER %token WHITESPACE %start path_data /: /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fachhochschule Potsdam - http://fh-potsdam.de Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 3454 $: $Author: cohen@irascible.com $: $Date: 2009-09-11 19:39:43 +0200 (Fr, 11. Sep 2009) $ ********************************************************************/ #ifndef SVGPATHPARSER_H #define SVGPATHPARSER_H #include #include #include "svgpathgrammar_p.h" class SVGPathLexer; class SVGPathParser: public $table { public: SVGPathParser(); ~SVGPathParser(); bool parse(SVGPathLexer *lexer); QVector & symStack(); QString errorMessage() const; QVariant result() const; private: void reallocateStack(); int m_tos; QVector m_stateStack; QVector m_symStack; QString m_errorMessage; QVariant m_result; }; #endif // SVGPATHPARSER_H :/ /. /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fachhochschule Potsdam - http://fh-potsdam.de Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 3454 $: $Author: cohen@irascible.com $: $Date: 2009-09-11 19:39:43 +0200 (Fr, 11. Sep 2009) $ ********************************************************************/ #include #include "svgpathparser.h" #include "svgpathlexer.h" SVGPathParser::SVGPathParser() { } SVGPathParser::~SVGPathParser() { } QVector & SVGPathParser::symStack() { return m_symStack; } void SVGPathParser::reallocateStack() { int size = m_stateStack.size(); if (size == 0) size = 128; else size <<= 1; m_stateStack.resize(size); } QString SVGPathParser::errorMessage() const { return m_errorMessage; } QVariant SVGPathParser::result() const { return m_result; } bool SVGPathParser::parse(SVGPathLexer *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.at(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; yytoken = -1; } else if (act < 0) { int r = - act - 1; m_tos -= rhs [r]; act = m_stateStack.at(m_tos++); switch (r) { ./ path_data ::= moveto_drawto_command_groups ; /. case $rule_number: { qDebug() << " got path_data "; } break; ./ moveto_drawto_command_groups ::= moveto_drawto_command_group | moveto_drawto_command_group moveto_drawto_command_groups ; /. case $rule_number: { //qDebug() << " got moveto_drawto_command_groups "; } break; ./ moveto_drawto_command_group ::= moveto drawto_commands | moveto; /. case $rule_number: { //qDebug() << " got moveto_drawto_command_group "; } break; ./ drawto_commands ::= drawto_command | drawto_command drawto_commands ; /. case $rule_number: { //qDebug() << " got drawto_commands "; } break; ./ drawto_command ::= fakeclosepath | closepath | lineto | horizontal_lineto | vertical_lineto | curveto | smooth_curveto | quadratic_bezier_curveto | smooth_quadratic_bezier_curveto | elliptical_arc; /. case $rule_number: { //qDebug() << " got drawto_command "; } break; ./ moveto ::= moveto_command moveto_argument_sequence ; /. case $rule_number: { qDebug() << " got moveto "; } break; ./ moveto_argument_sequence ::= coordinate_pair | coordinate_pair comma_wsp moveto_argument_sequence | coordinate_pair moveto_argument_sequence ; /. case $rule_number: { //qDebug() << " got moveto_argument_sequence "; } break; ./ lineto ::= lineto_command lineto_argument_sequence ; /. case $rule_number: { qDebug() << " got lineto "; } break; ./ lineto_argument_sequence ::= coordinate_pair | coordinate_pair comma_wsp lineto_argument_sequence | coordinate_pair lineto_argument_sequence ; /. case $rule_number: { //qDebug() << " got lineto_argument_sequence "; } break; ./ horizontal_lineto ::= horizontal_lineto_command horizontal_lineto_argument_sequence ; /. case $rule_number: { qDebug() << " got horizontal_lineto "; } break; ./ horizontal_lineto_argument_sequence ::= coordinate | coordinate comma_wsp horizontal_lineto_argument_sequence | coordinate horizontal_lineto_argument_sequence ; /. case $rule_number: { //qDebug() << " got horizontal_lineto_argument_sequence "; } break; ./ vertical_lineto ::= vertical_lineto_command vertical_lineto_argument_sequence ; /. case $rule_number: { qDebug() << " got vertical_lineto "; } break; ./ vertical_lineto_argument_sequence ::= coordinate | coordinate comma_wsp vertical_lineto_argument_sequence | coordinate vertical_lineto_argument_sequence ; /. case $rule_number: { //qDebug() << " got vertical_lineto_argument_sequence "; } break; ./ curveto ::= curveto_command curveto_argument_sequence ; /. case $rule_number: { qDebug() << " got curveto "; } break; ./ curveto_argument_sequence ::= curveto_argument | curveto_argument curveto_argument_sequence | curveto_argument comma_wsp curveto_argument_sequence ; /. case $rule_number: { //qDebug() << " got curveto_argument_sequence 3 "; } break; ./ curveto_argument ::= coordinate_pair comma_wsp coordinate_pair comma_wsp coordinate_pair | coordinate_pair comma_wsp coordinate_pair coordinate_pair | coordinate_pair coordinate_pair comma_wsp coordinate_pair | coordinate_pair coordinate_pair coordinate_pair ; /. case $rule_number: { //qDebug() << " got curveto_argument "; } break; ./ smooth_curveto ::= smooth_curveto_command smooth_curveto_argument_sequence ; /. case $rule_number: { qDebug() << " got smooth_curveto "; } break; ./ smooth_curveto_argument_sequence ::= smooth_curveto_argument | smooth_curveto_argument smooth_curveto_argument_sequence | smooth_curveto_argument comma_wsp smooth_curveto_argument_sequence ; /. case $rule_number: { //qDebug() << " got smooth_curveto_argument_sequence 3 "; } break; ./ smooth_curveto_argument ::= coordinate_pair coordinate_pair | coordinate_pair comma_wsp coordinate_pair ; /. case $rule_number: { //qDebug() << " got smooth_curveto_argument "; } break; ./ quadratic_bezier_curveto ::= quadratic_bezier_curveto_command quadratic_bezier_curveto_argument_sequence ; /. case $rule_number: { qDebug() << " got quadratic_bezier_curveto "; } break; ./ quadratic_bezier_curveto_argument_sequence ::= quadratic_bezier_curveto_argument | quadratic_bezier_curveto_argument quadratic_bezier_curveto_argument_sequence | quadratic_bezier_curveto_argument comma_wsp quadratic_bezier_curveto_argument_sequence ; /. case $rule_number: { //qDebug() << " got quadratic_bezier_curveto_argument "; } break; ./ quadratic_bezier_curveto_argument ::= coordinate_pair comma_wsp coordinate_pair | coordinate_pair coordinate_pair ; /. case $rule_number: { //qDebug() << " got quadratic_bezier_curveto_argument "; } break; ./ elliptical_arc ::= elliptical_arc_command elliptical_arc_argument_sequence ; /. case $rule_number: { qDebug() << " got elliptical_arc "; } break; ./ elliptical_arc_argument_sequence ::= elliptical_arc_argument | elliptical_arc_argument elliptical_arc_argument_sequence | elliptical_arc_argument comma_wsp elliptical_arc_argument_sequence ; /. case $rule_number: { //qDebug() << " got elliptical_arc_argument_sequence "; } break; ./ elliptical_arc_argument ::= nonnegative_number comma_wsp nonnegative_number comma_wsp number comma_wsp flag comma_wsp flag comma_wsp coordinate_pair ; /. case $rule_number: { //qDebug() << " got elliptical_arc_argument "; } break; ./ smooth_quadratic_bezier_curveto ::= smooth_quadratic_bezier_curveto_command smooth_quadratic_bezier_curveto_argument_sequence ; /. case $rule_number: { qDebug() << " got smooth_quadratic_bezier_curveto "; } break; ./ smooth_quadratic_bezier_curveto_argument_sequence ::= coordinate_pair | coordinate_pair comma_wsp smooth_quadratic_bezier_curveto_argument_sequence | coordinate_pair smooth_quadratic_bezier_curveto_argument_sequence ; /. case $rule_number: { //qDebug() << " got smooth_quadratic_bezier_curveto_argument_sequence 3 "; } break; ./ coordinate_pair ::= x_coordinate comma_wsp y_coordinate | x_coordinate y_coordinate ; /. case $rule_number: { //qDebug() << " got coordinate_pair "; } break; ./ x_coordinate ::= coordinate; /. case $rule_number: { //qDebug() << " got x coordinate "; } break; ./ y_coordinate ::= coordinate; /. case $rule_number: { //qDebug() << " got y coordinate "; } break; ./ comma_wsp ::= wspplus | COMMA ; /. case $rule_number: { //qDebug() << " got comma_wsp 3 "; } break; ./ wspplus ::= WHITESPACE ; /. case $rule_number: { //qDebug() << " got wspplus "; } break; ./ coordinate ::= NUMBER ; /. case $rule_number: { //qDebug() << " got coordinate "; m_symStack.append(lexer->currentNumber()); } break; ./ nonnegative_number ::= NUMBER ; /. case $rule_number: { //qDebug() << " got nonnegative_number "; //not presently checking this is non-negative m_symStack.append(lexer->currentNumber()); } break; ./ number ::= NUMBER ; /. case $rule_number: { //qDebug() << " got number "; m_symStack.append(lexer->currentNumber()); } break; ./ flag ::= NUMBER ; /. case $rule_number: { //qDebug() << " got flag "; //not presently checking this is only 0 or 1 m_symStack.append(lexer->currentNumber()); } break; ./ moveto_command ::= EM ; /. case $rule_number: { //qDebug() << " got moveto command "; m_symStack.append(lexer->currentCommand()); } break; ./ lineto_command ::= EL ; /. case $rule_number: { //qDebug() << " got lineto command "; m_symStack.append(lexer->currentCommand()); } break; ./ horizontal_lineto_command ::= AITCH ; /. case $rule_number: { //qDebug() << " got horizontal_lineto command "; m_symStack.append(lexer->currentCommand()); } break; ./ vertical_lineto_command ::= VEE ; /. case $rule_number: { //qDebug() << " got vertical_lineto command "; m_symStack.append(lexer->currentCommand()); } break; ./ curveto_command ::= CEE ; /. case $rule_number: { //qDebug() << " got curveto command "; m_symStack.append(lexer->currentCommand()); } break; ./ smooth_curveto_command ::= ESS ; /. case $rule_number: { //qDebug() << " got smooth curveto command "; m_symStack.append(lexer->currentCommand()); } break; ./ quadratic_bezier_curveto_command ::= KYU ; /. case $rule_number: { //qDebug() << " got quadratic_bezier_curveto_command command "; m_symStack.append(lexer->currentCommand()); } break; ./ smooth_quadratic_bezier_curveto_command ::= TEE ; /. case $rule_number: { //qDebug() << " got smooth_quadratic_bezier_curveto_command command "; m_symStack.append(lexer->currentCommand()); } break; ./ elliptical_arc_command ::= AE ; /. case $rule_number: { //qDebug() << " got elliptical_arc_command "; m_symStack.append(lexer->currentCommand()); } break; ./ closepath ::= ZEE ; /. case $rule_number: { qDebug() << " got closepath "; m_symStack.append(lexer->currentCommand()); } break; ./ fakeclosepath ::= EKS ; /. case $rule_number: { qDebug() << " got fakeclosepath "; } break; ./ /. } // 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_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; } ./ fritzing-app-0.9.3b/src/svg/svgpathgrammar.cpp000066400000000000000000000165501272363601300214260ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! #include "svgpathgrammar_p.h" const char *const SVGPathGrammar::spell [] = { "end of file", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #ifndef QLALR_NO_SVGPATHGRAMMAR_DEBUG_INFO "path_data", "moveto_drawto_command_groups", "moveto_drawto_command_group", "moveto", "drawto_commands", "drawto_command", "fakeclosepath", "closepath", "lineto", "horizontal_lineto", "vertical_lineto", "curveto", "smooth_curveto", "quadratic_bezier_curveto", "smooth_quadratic_bezier_curveto", "elliptical_arc", "moveto_command", "moveto_argument_sequence", "coordinate_pair", "comma_wsp", "lineto_command", "lineto_argument_sequence", "horizontal_lineto_command", "horizontal_lineto_argument_sequence", "coordinate", "vertical_lineto_command", "vertical_lineto_argument_sequence", "curveto_command", "curveto_argument_sequence", "curveto_argument", "smooth_curveto_command", "smooth_curveto_argument_sequence", "smooth_curveto_argument", "quadratic_bezier_curveto_command", "quadratic_bezier_curveto_argument_sequence", "quadratic_bezier_curveto_argument", "elliptical_arc_command", "elliptical_arc_argument_sequence", "elliptical_arc_argument", "nonnegative_number", "number", "flag", "smooth_quadratic_bezier_curveto_command", "smooth_quadratic_bezier_curveto_argument_sequence", "x_coordinate", "y_coordinate", "wspplus", "$accept" #endif // QLALR_NO_SVGPATHGRAMMAR_DEBUG_INFO }; const int SVGPathGrammar::lhs [] = { 15, 16, 16, 17, 17, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 18, 32, 32, 32, 23, 36, 36, 36, 24, 38, 38, 38, 25, 41, 41, 41, 26, 43, 43, 43, 44, 44, 44, 44, 27, 46, 46, 46, 47, 47, 28, 49, 49, 49, 50, 50, 30, 52, 52, 52, 53, 29, 58, 58, 58, 33, 33, 59, 60, 34, 34, 61, 39, 54, 55, 56, 31, 35, 37, 40, 42, 45, 48, 57, 51, 22, 21, 62}; const int SVGPathGrammar:: rhs[] = { 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 3, 2, 2, 1, 3, 2, 2, 1, 3, 2, 2, 1, 3, 2, 2, 1, 2, 3, 5, 4, 4, 3, 2, 1, 2, 3, 2, 3, 2, 1, 2, 3, 3, 2, 2, 1, 2, 3, 11, 2, 1, 3, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2}; #ifndef QLALR_NO_SVGPATHGRAMMAR_DEBUG_INFO const int SVGPathGrammar::rule_info [] = { 15, 16 , 16, 17 , 16, 17, 16 , 17, 18, 19 , 17, 18 , 19, 20 , 19, 20, 19 , 20, 21 , 20, 22 , 20, 23 , 20, 24 , 20, 25 , 20, 26 , 20, 27 , 20, 28 , 20, 29 , 20, 30 , 18, 31, 32 , 32, 33 , 32, 33, 34, 32 , 32, 33, 32 , 23, 35, 36 , 36, 33 , 36, 33, 34, 36 , 36, 33, 36 , 24, 37, 38 , 38, 39 , 38, 39, 34, 38 , 38, 39, 38 , 25, 40, 41 , 41, 39 , 41, 39, 34, 41 , 41, 39, 41 , 26, 42, 43 , 43, 44 , 43, 44, 43 , 43, 44, 34, 43 , 44, 33, 34, 33, 34, 33 , 44, 33, 34, 33, 33 , 44, 33, 33, 34, 33 , 44, 33, 33, 33 , 27, 45, 46 , 46, 47 , 46, 47, 46 , 46, 47, 34, 46 , 47, 33, 33 , 47, 33, 34, 33 , 28, 48, 49 , 49, 50 , 49, 50, 49 , 49, 50, 34, 49 , 50, 33, 34, 33 , 50, 33, 33 , 30, 51, 52 , 52, 53 , 52, 53, 52 , 52, 53, 34, 52 , 53, 54, 34, 54, 34, 55, 34, 56, 34, 56, 34, 33 , 29, 57, 58 , 58, 33 , 58, 33, 34, 58 , 58, 33, 58 , 33, 59, 34, 60 , 33, 59, 60 , 59, 39 , 60, 39 , 34, 61 , 34, 12 , 61, 14 , 39, 13 , 54, 13 , 55, 13 , 56, 13 , 31, 1 , 35, 4 , 37, 5 , 40, 6 , 42, 7 , 45, 8 , 48, 9 , 57, 10 , 51, 11 , 22, 2 , 21, 3 , 62, 15, 0}; const int SVGPathGrammar::rule_index [] = { 0, 2, 4, 7, 10, 12, 14, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 40, 42, 46, 49, 52, 54, 58, 61, 64, 66, 70, 73, 76, 78, 82, 85, 88, 90, 93, 97, 103, 108, 113, 117, 120, 122, 125, 129, 132, 136, 139, 141, 144, 148, 152, 155, 158, 160, 163, 167, 179, 182, 184, 188, 191, 195, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238}; #endif // QLALR_NO_SVGPATHGRAMMAR_DEBUG_INFO const int SVGPathGrammar::action_default [] = { 0, 74, 5, 0, 2, 1, 0, 82, 76, 78, 84, 75, 79, 80, 81, 77, 83, 9, 13, 0, 6, 4, 17, 0, 8, 11, 0, 10, 0, 15, 0, 14, 0, 16, 0, 12, 0, 70, 65, 0, 35, 34, 0, 68, 69, 0, 0, 67, 0, 0, 39, 38, 0, 41, 40, 0, 36, 37, 0, 66, 64, 63, 7, 71, 55, 54, 0, 0, 56, 57, 0, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 58, 27, 26, 0, 29, 28, 23, 22, 0, 25, 24, 0, 49, 48, 0, 53, 52, 0, 50, 51, 0, 43, 42, 0, 46, 47, 0, 44, 45, 60, 59, 0, 62, 61, 31, 30, 0, 33, 32, 19, 18, 0, 21, 20, 3, 85}; const int SVGPathGrammar::goto_default [] = { 6, 5, 4, 2, 21, 20, 24, 17, 27, 25, 35, 18, 31, 29, 33, 22, 3, 121, 39, 45, 28, 88, 26, 83, 38, 36, 116, 19, 41, 40, 32, 103, 102, 30, 94, 93, 23, 65, 64, 66, 74, 77, 34, 111, 42, 60, 47, 0}; const int SVGPathGrammar::action_index [] = { 16, -15, 29, -13, 0, -15, 2, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, -13, 29, -15, -15, -9, -15, -15, -1, -15, -5, -15, -3, -15, -7, -15, -1, -15, -1, -15, -15, 10, 10, -15, 10, -15, -15, -13, 10, -15, 10, -1, -15, -15, -1, -15, -15, -1, -15, -15, -1, -15, -15, -15, -15, -15, 13, -15, 4, -2, -15, -15, -6, 7, -4, -15, 7, -8, -15, 7, 1, 7, -1, -15, 10, -15, -1, -15, -15, 10, -15, -13, -15, -15, 10, 10, -15, -13, -15, -15, -13, -15, -15, 10, 10, -15, -13, -15, -15, -13, -15, -15, 10, -15, -10, -15, -15, 10, -15, -13, -15, -15, 10, -15, -13, -15, -15, -15, -15, -48, -48, -48, -12, 43, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, -4, -48, -48, -48, -48, -48, -5, -48, 6, -48, 3, -48, -13, -48, -9, -48, -21, -48, -48, -6, 20, -48, 16, -48, -48, -3, 13, -48, 19, -8, -48, -48, -7, -48, -48, -14, -48, -48, 10, -48, -48, -48, -48, -48, 8, -48, -11, -30, -48, -48, -26, 1, -48, -48, 4, -48, -48, -15, -25, -2, 0, -48, 50, -48, 18, -48, -48, 42, -48, 15, -48, -48, 33, 38, -48, -16, -48, -48, 25, -48, -48, 28, 46, -48, -17, -48, -48, 31, -48, -48, 35, -48, 7, -48, -48, 72, -48, 2, -48, -48, 49, -48, 12, -48, -48, -48, -48}; const int SVGPathGrammar::action_info [] = { 37, 1, 126, 37, 63, 76, 37, 63, 37, 73, 37, 63, 37, 0, 76, 0, 43, 1, 44, 43, 0, 44, 43, 37, 44, 43, 63, 44, 0, 0, 0, 16, 10, 11, 8, 15, 9, 12, 13, 14, 7, 0, 0, 0, 62, 106, 97, 115, 78, 101, 120, 69, 70, 110, 51, 54, 46, 71, 57, 48, 79, 80, 81, 82, 72, 92, 0, 75, 87, 110, 115, 67, 119, 124, 120, 53, 52, 87, 59, 58, 91, 50, 49, 55, 59, 86, 82, 92, 125, 68, 105, 104, 56, 101, 114, 96, 95, 110, 112, 61, 92, 98, 0, 100, 87, 89, 109, 90, 101, 107, 123, 120, 122, 84, 0, 0, 99, 85, 82, 0, 0, 108, 113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 0, 0, 115, 0, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const int SVGPathGrammar::action_check [] = { 13, 1, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, -1, 13, -1, 12, 1, 14, 12, -1, 14, 12, 13, 14, 12, 13, 14, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, 4, 18, 18, 24, 19, 18, 18, 37, 19, 18, 18, 18, 18, 39, 28, 18, 41, 19, 18, 24, 19, 18, -1, 19, 18, 18, 24, 19, 26, 17, 18, 18, 19, 18, 24, 19, 21, 18, 19, 19, 24, 23, 24, 18, 1, 37, 18, 19, 28, 18, 43, 18, 19, 18, 19, 45, 18, 19, -1, 34, 18, 19, 31, 21, 18, 19, 17, 18, 19, 19, -1, -1, 34, 23, 24, -1, -1, 31, 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19, -1, -1, -1, -1, 24, -1, 26, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; fritzing-app-0.9.3b/src/svg/svgpathgrammar_p.h000066400000000000000000000032761272363601300214130ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! #ifndef SVGPATHGRAMMAR_P_H #define SVGPATHGRAMMAR_P_H class SVGPathGrammar { public: enum { EOF_SYMBOL = 0, AE = 11, AITCH = 5, CEE = 7, COMMA = 12, EKS = 3, EL = 4, EM = 1, ESS = 8, KYU = 9, NUMBER = 13, TEE = 10, VEE = 6, WHITESPACE = 14, ZEE = 2, ACCEPT_STATE = 126, RULE_COUNT = 85, STATE_COUNT = 127, TERMINAL_COUNT = 15, NON_TERMINAL_COUNT = 48, GOTO_INDEX_OFFSET = 127, GOTO_INFO_OFFSET = 44, GOTO_CHECK_OFFSET = 44 }; static const char *const spell []; static const int lhs []; static const int rhs []; #ifndef QLALR_NO_SVGPATHGRAMMAR_DEBUG_INFO static const int rule_index []; static const int rule_info []; #endif // QLALR_NO_SVGPATHGRAMMAR_DEBUG_INFO static const int goto_default []; static const int action_default []; static const int action_index []; static const int action_info []; static const int action_check []; static inline int nt_action (int state, int nt) { const int *const goto_index = &action_index [GOTO_INDEX_OFFSET]; const int *const goto_check = &action_check [GOTO_CHECK_OFFSET]; const int yyn = goto_index [state] + nt; if (yyn < 0 || goto_check [yyn] != nt) return goto_default [nt]; const int *const goto_info = &action_info [GOTO_INFO_OFFSET]; return goto_info [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]; } }; #endif // SVGPATHGRAMMAR_P_H fritzing-app-0.9.3b/src/svg/svgpathlexer.cpp000066400000000000000000000133501272363601300211120ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "svgpathlexer.h" #include "svgpathgrammar_p.h" #include "../utils/textutils.h" #include static const QRegExp findWhitespaceBefore(" ([AaCcMmVvTtQqSsLlVvHhZz,])"); static const QRegExp findWhitespaceAfter("([AaCcMmVvTtQqSsLlVvHhZz,]) "); static const QRegExp findWhitespaceAtEnd(" $"); SVGPathLexer::SVGPathLexer(const QString &source) { m_source = clean(source); m_chars = m_source.unicode(); m_size = m_source.size(); //qDebug() << m_source; m_pos = 0; m_current = next(); } SVGPathLexer::~SVGPathLexer() { } QString SVGPathLexer::clean(const QString & source) { // SVG path grammar is ambiguous, so clean it up to make it easier to parse // the rules are WSP* , WSP* ==> , // WSP* command WSP* ==> command // WSP+ ==> WSP // where WSP is whitespace, command is one of the path commands such as M C V ... QString s1 = source; s1.replace(TextUtils::FindWhitespace, " "); s1.replace(findWhitespaceBefore, "\\1"); // replace with the captured character s1.replace(findWhitespaceAfter, "\\1"); // replace with the captured character s1.replace(findWhitespaceAtEnd, ""); return s1; } int SVGPathLexer::lex() { if (TextUtils::floatingPointMatcher.indexIn(m_source, m_pos - 1) == m_pos - 1) { m_currentNumber = m_source.mid(m_pos - 1, TextUtils::floatingPointMatcher.matchedLength()).toDouble(); m_pos += TextUtils::floatingPointMatcher.matchedLength() - 1; next(); return SVGPathGrammar::NUMBER; } if (m_current.isSpace()) { next(); return SVGPathGrammar::WHITESPACE; } else if (m_current.isNull()) { return SVGPathGrammar::EOF_SYMBOL; } else if (m_current == QLatin1Char('V')) { m_currentCommand = m_current; next(); return SVGPathGrammar::VEE; } else if (m_current == QLatin1Char('v')) { m_currentCommand = m_current; next(); return SVGPathGrammar::VEE; } else if (m_current == QLatin1Char('T')) { m_currentCommand = m_current; next(); return SVGPathGrammar::TEE; } else if (m_current == QLatin1Char('t')) { m_currentCommand = m_current; next(); return SVGPathGrammar::TEE; } else if (m_current == QLatin1Char(FakeClosePathChar)) { m_currentCommand = m_current; next(); return SVGPathGrammar::EKS; } else if (m_current == QLatin1Char('C')) { m_currentCommand = m_current; next(); return SVGPathGrammar::CEE; } else if (m_current == QLatin1Char('c')) { m_currentCommand = m_current; next(); return SVGPathGrammar::CEE; } else if (m_current == QLatin1Char('Q')) { m_currentCommand = m_current; next(); return SVGPathGrammar::KYU; } else if (m_current == QLatin1Char('q')) { m_currentCommand = m_current; next(); return SVGPathGrammar::KYU; } else if (m_current == QLatin1Char('S')) { m_currentCommand = m_current; next(); return SVGPathGrammar::ESS; } else if (m_current == QLatin1Char('s')) { m_currentCommand = m_current; next(); return SVGPathGrammar::ESS; } else if (m_current == QLatin1Char('H')) { m_currentCommand = m_current; next(); return SVGPathGrammar::AITCH; } else if (m_current == QLatin1Char('h')) { m_currentCommand = m_current; next(); return SVGPathGrammar::AITCH; } else if (m_current == QLatin1Char('L')) { m_currentCommand = m_current; next(); return SVGPathGrammar::EL; } else if (m_current == QLatin1Char('l')) { m_currentCommand = m_current; next(); return SVGPathGrammar::EL; } else if (m_current == QLatin1Char('M')) { m_currentCommand = m_current; next(); return SVGPathGrammar::EM; } else if (m_current == QLatin1Char('m')) { m_currentCommand = m_current; next(); return SVGPathGrammar::EM; } else if (m_current == QLatin1Char('A')) { m_currentCommand = m_current; next(); return SVGPathGrammar::AE; } else if (m_current == QLatin1Char('a')) { m_currentCommand = m_current; next(); return SVGPathGrammar::AE; } else if (m_current == QLatin1Char('Z')) { m_currentCommand = m_current; next(); return SVGPathGrammar::ZEE; } else if (m_current == QLatin1Char('z')) { m_currentCommand = m_current; next(); return SVGPathGrammar::ZEE; } else if (m_current == QLatin1Char(',')) { next(); return SVGPathGrammar::COMMA; } return -1; } QChar SVGPathLexer::next() { if (m_pos < m_size) m_current = m_chars[m_pos++]; else m_current = QChar(); return m_current; } QChar SVGPathLexer::currentCommand() { return m_currentCommand; } double SVGPathLexer::currentNumber() { return m_currentNumber; } fritzing-app-0.9.3b/src/svg/svgpathlexer.h000066400000000000000000000031151272363601300205550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SVGPATHLEXER_H #define SVGPATHLEXER_H #include #include #include #include #include class SVGPathLexer { public: SVGPathLexer(const QString &source); ~SVGPathLexer(); int lex(); QChar currentCommand(); double currentNumber(); public: static const char FakeClosePathChar = 'x'; protected: QChar next(); QString clean(const QString & source); protected: QString m_source; const QChar *m_chars; int m_size; int m_pos; QChar m_current; QChar m_currentCommand; double m_currentNumber; }; #endif fritzing-app-0.9.3b/src/svg/svgpathparser.cpp000066400000000000000000000171301272363601300212670ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 4147 $: $Author: cohen@irascible.com $: $Date: 2010-04-25 19:00:40 +0200 (So, 25. Apr 2010) $ ********************************************************************/ #include #include "svgpathparser.h" #include "svgpathlexer.h" SVGPathParser::SVGPathParser() { } SVGPathParser::~SVGPathParser() { } QVector & SVGPathParser::symStack() { return m_symStack; } void SVGPathParser::reallocateStack() { int size = m_stateStack.size(); if (size == 0) size = 128; else size <<= 1; m_stateStack.resize(size); } QString SVGPathParser::errorMessage() const { return m_errorMessage; } QVariant SVGPathParser::result() const { return m_result; } bool SVGPathParser::parse(SVGPathLexer *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.at(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; yytoken = -1; } else if (act < 0) { int r = - act - 1; m_tos -= rhs [r]; act = m_stateStack.at(m_tos++); switch (r) { case 0: { //qDebug() << " got path_data "; } break; case 2: { //qDebug() << " got moveto_drawto_command_groups "; } break; case 4: { //qDebug() << " got moveto_drawto_command_group "; } break; case 6: { //qDebug() << " got drawto_commands "; } break; case 16: { //qDebug() << " got drawto_command "; } break; case 17: { //qDebug() << " got moveto "; } break; case 20: { //qDebug() << " got moveto_argument_sequence "; } break; case 21: { //qDebug() << " got lineto "; } break; case 24: { //qDebug() << " got lineto_argument_sequence "; } break; case 25: { //qDebug() << " got horizontal_lineto "; } break; case 28: { //qDebug() << " got horizontal_lineto_argument_sequence "; } break; case 29: { //qDebug() << " got vertical_lineto "; } break; case 32: { //qDebug() << " got vertical_lineto_argument_sequence "; } break; case 33: { //qDebug() << " got curveto "; } break; case 36: { //qDebug() << " got curveto_argument_sequence 3 "; } break; case 40: { //qDebug() << " got curveto_argument "; } break; case 41: { //qDebug() << " got smooth_curveto "; } break; case 44: { //qDebug() << " got smooth_curveto_argument_sequence 3 "; } break; case 46: { //qDebug() << " got smooth_curveto_argument "; } break; case 47: { //qDebug() << " got quadratic_bezier_curveto "; } break; case 50: { //qDebug() << " got quadratic_bezier_curveto_argument "; } break; case 52: { //qDebug() << " got quadratic_bezier_curveto_argument "; } break; case 53: { //qDebug() << " got elliptical_arc "; } break; case 56: { //qDebug() << " got elliptical_arc_argument_sequence "; } break; case 57: { //qDebug() << " got elliptical_arc_argument "; } break; case 58: { //qDebug() << " got smooth_quadratic_bezier_curveto "; } break; case 61: { //qDebug() << " got smooth_quadratic_bezier_curveto_argument_sequence 3 "; } break; case 63: { //qDebug() << " got coordinate_pair "; } break; case 64: { //qDebug() << " got x coordinate "; } break; case 65: { //qDebug() << " got y coordinate "; } break; case 67: { //qDebug() << " got comma_wsp 3 "; } break; case 68: { //qDebug() << " got wspplus "; } break; case 69: { //qDebug() << " got coordinate "; m_symStack.append(lexer->currentNumber()); } break; case 70: { //qDebug() << " got nonnegative_number "; //not presently checking this is non-negative m_symStack.append(lexer->currentNumber()); } break; case 71: { //qDebug() << " got number "; m_symStack.append(lexer->currentNumber()); } break; case 72: { //qDebug() << " got flag "; //not presently checking this is only 0 or 1 m_symStack.append(lexer->currentNumber()); } break; case 73: { //qDebug() << " got moveto command "; m_symStack.append(lexer->currentCommand()); } break; case 74: { //qDebug() << " got lineto command "; m_symStack.append(lexer->currentCommand()); } break; case 75: { //qDebug() << " got horizontal_lineto command "; m_symStack.append(lexer->currentCommand()); } break; case 76: { //qDebug() << " got vertical_lineto command "; m_symStack.append(lexer->currentCommand()); } break; case 77: { //qDebug() << " got curveto command "; m_symStack.append(lexer->currentCommand()); } break; case 78: { //qDebug() << " got smooth curveto command "; m_symStack.append(lexer->currentCommand()); } break; case 79: { //qDebug() << " got quadratic_bezier_curveto_command command "; m_symStack.append(lexer->currentCommand()); } break; case 80: { //qDebug() << " got smooth_quadratic_bezier_curveto_command command "; m_symStack.append(lexer->currentCommand()); } break; case 81: { //qDebug() << " got elliptical_arc_command "; m_symStack.append(lexer->currentCommand()); } break; case 82: { //qDebug() << " got closepath "; m_symStack.append(lexer->currentCommand()); } break; case 83: { //qDebug() << " got fakeclosepath "; } break; } // 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_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; } fritzing-app-0.9.3b/src/svg/svgpathparser.h000066400000000000000000000031311272363601300207300ustar00rootroot00000000000000// This file was generated by qlalr - DO NOT EDIT! /******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-08 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 3442 $: $Author: cohen@irascible.com $: $Date: 2009-09-10 04:49:21 +0200 (Do, 10. Sep 2009) $ ********************************************************************/ #ifndef SVGPATHPARSER_H #define SVGPATHPARSER_H #include #include #include "svgpathgrammar_p.h" class SVGPathLexer; class SVGPathParser: public SVGPathGrammar { public: SVGPathParser(); ~SVGPathParser(); bool parse(SVGPathLexer *lexer); QVector & symStack(); QString errorMessage() const; QVariant result() const; private: void reallocateStack(); int m_tos; QVector m_stateStack; QVector m_symStack; QString m_errorMessage; QVariant m_result; }; #endif // SVGPATHPARSER_H fritzing-app-0.9.3b/src/svg/svgpathrunner.cpp000066400000000000000000000135471272363601300213140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include "svgpathrunner.h" QHash SVGPathRunner::pathCommands; SVGPathRunner::SVGPathRunner() { if (pathCommands.size() == 0) { initStates(); } } SVGPathRunner::~SVGPathRunner() { } bool SVGPathRunner::runPath(QVector & pathData, void * userData) { PathCommand * currentCommand = NULL; QList args; foreach (QVariant variant, pathData) { if (variant.type() == QVariant::Char) { PathCommand * newCommand = pathCommands.value(variant.toChar(), NULL); if (newCommand == NULL) return false; if (currentCommand != NULL) { if (currentCommand->argCount == 0) { if (args.count() != 0) return false; } else if (args.count() % currentCommand->argCount != 0) return false; emit commandSignal(currentCommand->command, currentCommand->relative, args, userData); } args.clear(); currentCommand = newCommand; } else if (variant.type() == QVariant::Double) { if (currentCommand == NULL) return false; args.append(variant.toDouble()); } } if (currentCommand != NULL) { if (currentCommand->argCount == 0) { if (args.count() != 0) return false; } else if (args.count() % currentCommand->argCount != 0) return false; emit commandSignal(currentCommand->command, currentCommand->relative, args, userData); } return true; } void SVGPathRunner::initStates() { pathCommands.clear(); PathCommand * pathCommand = new PathCommand; pathCommand->command = 'M'; pathCommand->relative = false; pathCommand->argCount = 2; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'm'; pathCommand->relative = true; pathCommand->argCount = 2; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'A'; pathCommand->relative = false; pathCommand->argCount = 7; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'a'; pathCommand->relative = true; pathCommand->argCount = 7; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'Z'; pathCommand->relative = false; pathCommand->argCount = 0; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'z'; pathCommand->relative = true; pathCommand->argCount = 0; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'L'; pathCommand->relative = false; pathCommand->argCount = 2; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'l'; pathCommand->relative = true; pathCommand->argCount = 2; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'H'; pathCommand->relative = false; pathCommand->argCount = 1; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'h'; pathCommand->relative = true; pathCommand->argCount = 1; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'V'; pathCommand->relative = false; pathCommand->argCount = 1; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'v'; pathCommand->relative = true; pathCommand->argCount = 1; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'C'; pathCommand->relative = false; pathCommand->argCount = 6; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'c'; pathCommand->relative = true; pathCommand->argCount = 6; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'S'; pathCommand->relative = false; pathCommand->argCount = 4; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 's'; pathCommand->relative = true; pathCommand->argCount = 4; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'Q'; pathCommand->relative = false; pathCommand->argCount = 4; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'q'; pathCommand->relative = true; pathCommand->argCount = 4; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 'T'; pathCommand->relative = false; pathCommand->argCount = 2; pathCommands.insert(pathCommand->command, pathCommand); pathCommand = new PathCommand; pathCommand->command = 't'; pathCommand->relative = true; pathCommand->argCount = 2; pathCommands.insert(pathCommand->command, pathCommand); } fritzing-app-0.9.3b/src/svg/svgpathrunner.h000066400000000000000000000032221272363601300207460ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SVGPATHRUNNER_H #define SVGPATHRUNNER_H #include #include struct PathCommand { bool relative; int argCount; QChar command; }; class SVGPathRunner : public QObject { Q_OBJECT public: SVGPathRunner(); ~SVGPathRunner(); public: bool runPath(QVector & pathData, void * userData); signals: // note: must connect to this signal via Qt::DirectConnection since args is modified immediately after the signal void commandSignal(QChar command, bool relative, QList & args, void * userData); protected: static void initStates(); protected: static QHash pathCommands; }; #endif // SVGPATHRUNNER_H fritzing-app-0.9.3b/src/svg/x2svg.cpp000066400000000000000000000051671272363601300174560ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "x2svg.h" #include "../utils/textutils.h" #include "svgfilesplitter.h" #include #include #include #include #include #include #include #include #include static const int MAX_INT = std::numeric_limits::max(); static const int MIN_INT = std::numeric_limits::min(); X2Svg::X2Svg() { m_attribute = "%2\n"; m_comment = "%2\n"; } void X2Svg::initLimits() { m_maxX = MIN_INT; m_maxY = MIN_INT; m_minX = MAX_INT; m_minY = MAX_INT; } void X2Svg::checkXLimit(double x) { if (x < m_minX) m_minX = x; if (x > m_maxX) m_maxX = x; } void X2Svg::checkYLimit(double y) { if (y < m_minY) m_minY = y; if (y > m_maxY) m_maxY = y; } QString X2Svg::offsetMin(const QString & svg) { if (m_minX == 0 && m_minY == 0) return svg; QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(svg, true, &errorStr, &errorLine, &errorColumn)) { throw QObject::tr("failure in svg conversion 1: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn); } QDomElement root = domDocument.documentElement(); if (root.isNull()) { throw QObject::tr("failure in svg conversion 2: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn); } SvgFileSplitter splitter; splitter.shiftChild(root, -m_minX, -m_minY, true); return TextUtils::removeXMLEntities(domDocument.toString()); } QString X2Svg::unquote(const QString & string) { QString s = string; if (s.endsWith('"')) { s.chop(1); } if (s.startsWith('"')) { s = s.remove(0, 1); } return s; } fritzing-app-0.9.3b/src/svg/x2svg.h000066400000000000000000000025631272363601300171200ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef X2SVG_H #define X2SVG_H #include class X2Svg { public: X2Svg(); void checkXLimit(double x); void checkYLimit(double y); protected: QString offsetMin(const QString & svg); void initLimits(); QString unquote(const QString &); protected: double m_maxX; double m_maxY; double m_minX; double m_minY; QString m_attribute; QString m_comment; }; #endif // X2SVG_H fritzing-app-0.9.3b/src/utils/000077500000000000000000000000001272363601300162315ustar00rootroot00000000000000fritzing-app-0.9.3b/src/utils/abstractstatesbutton.h000066400000000000000000000042131272363601300226650ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ABSTRACTSTATESBUTTON_H_ #define ABSTRACTSTATESBUTTON_H_ #include #include #include "../debugdialog.h" class AbstractStatesButton { public: virtual ~AbstractStatesButton() {} // clears compiler warning void setEnabledIcon() { setImage(m_enabledImage); } void setDisabledIcon() { setImage(m_disabledImage); } void setPressedIcon() { setImage(m_pressedImage); } protected: virtual QString imagePrefix() = 0; virtual QString imageSubfix() { return "_icon.png"; } virtual void setImage(const QPixmap & pixmap) = 0; virtual void setupIcons(const QString &imageName, bool hasStates=true) { if(hasStates) { m_enabledImage = QPixmap(imagePrefix()+imageName+"Enabled"+imageSubfix()); m_disabledImage = QPixmap(imagePrefix()+imageName+"Disabled"+imageSubfix()); m_pressedImage = QPixmap(imagePrefix()+imageName+"Pressed"+imageSubfix()); } else { m_enabledImage = QPixmap(imagePrefix()+imageName+imageSubfix()); m_disabledImage = m_enabledImage; m_pressedImage = m_enabledImage; } } protected: QPixmap m_enabledImage; QPixmap m_disabledImage; QPixmap m_pressedImage; }; #endif /* ABSTRACTSTATESBUTTON_H_ */ fritzing-app-0.9.3b/src/utils/autoclosemessagebox.cpp000066400000000000000000000103711272363601300230130ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "autoclosemessagebox.h" #include "../debugdialog.h" #include "../mainwindow/mainwindow.h" static const int Interval = 30; static const int Steps = 7; static const int Wait = 100; AutoCloseMessageBox::AutoCloseMessageBox( QWidget * parent ) : QLabel(parent) { setWordWrap(true); } void AutoCloseMessageBox::setStartPos(int x, int y) { m_startX = x; m_startY = y; QRect r = this->geometry(); r.moveTo(x, y); setGeometry(r); } void AutoCloseMessageBox::setEndPos(int x, int y) { m_endX = x; m_endY = y; } void AutoCloseMessageBox::start() { m_counter = Steps; m_animationTimer.setInterval(Interval); m_animationTimer.setSingleShot(false); connect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(moveOut())); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) m_animationTimer.setTimerType(Qt::PreciseTimer); #endif m_movingState = MovingOut; m_animationTimer.start(); show(); } void AutoCloseMessageBox::mousePressEvent(QMouseEvent * event) { Q_UNUSED(event); if (m_movingState != MovingBack) { m_animationTimer.stop(); if (m_movingState == MovingOut) { disconnect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(moveOut())); } else if (m_movingState == Waiting) { disconnect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(wait())); } prepMoveBack(); } } void AutoCloseMessageBox::moveOut() { if (--m_counter == 0) { m_animationTimer.stop(); disconnect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(moveOut())); m_movingState = Waiting; QRect r = geometry(); r.moveTo(m_endX, m_endY); setGeometry(r); m_counter = Wait; connect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(wait())); m_animationTimer.start(); return; } QRect r = geometry(); int dx = (m_endX - r.x()) / m_counter; int dy = (m_endY - r.y()) / m_counter; r.translate(dx, dy); setGeometry(r); } void AutoCloseMessageBox::moveBack() { if (--m_counter == 0) { m_animationTimer.stop(); disconnect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(moveOut())); QRect r = geometry(); r.moveTo(m_startX, m_startY); setGeometry(r); this->deleteLater(); return; } QRect r = geometry(); int dx = (m_startX - r.x()) / m_counter; int dy = (m_startY - r.y()) / m_counter; r.translate(dx, dy); setGeometry(r); } void AutoCloseMessageBox::wait() { if (--m_counter == 0) { m_animationTimer.stop(); disconnect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(wait())); prepMoveBack(); } } void AutoCloseMessageBox::prepMoveBack() { m_counter = Steps; m_movingState = MovingBack; connect(&m_animationTimer, SIGNAL(timeout()), this, SLOT(moveBack())); m_animationTimer.start(); } void AutoCloseMessageBox::showMessage(QWidget *window, const QString &message) { MainWindow * mainWindow = qobject_cast(window); if (mainWindow == NULL) return; QStatusBar * statusBar = mainWindow->realStatusBar(); if (statusBar == NULL) return; AutoCloseMessageBox * acmb = new AutoCloseMessageBox(mainWindow); acmb->setText(message); QRect dest = statusBar->geometry(); // toolbar->geometry(); QRect r = mainWindow->geometry(); acmb->setFixedSize(QSize(dest.width(), dest.height())); QPoint p(dest.x(), dest.y()); p = statusBar->parentWidget()->mapTo(mainWindow, p); acmb->setStartPos(p.x(), r.height()); acmb->setEndPos(p.x(), p.y()); acmb->start(); } fritzing-app-0.9.3b/src/utils/autoclosemessagebox.h000066400000000000000000000032561272363601300224640ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef AUTOCLOSEMESSAGEBOX_H #define AUTOCLOSEMESSAGEBOX_H #include #include class AutoCloseMessageBox : public QLabel { Q_OBJECT public: AutoCloseMessageBox(QWidget * parent); void setStartPos(int x, int y); void setEndPos(int x, int y); void start(); static void showMessage(QWidget * window, const QString & message); protected: void mousePressEvent(QMouseEvent *); void prepMoveBack(); protected slots: void moveOut(); void moveBack(); void wait(); protected: enum MovingState { MovingOut, Waiting, MovingBack }; volatile MovingState m_movingState; int m_endX; int m_endY; int m_startX; int m_startY; QTimer m_animationTimer; int m_counter; }; #endif fritzing-app-0.9.3b/src/utils/bendpointaction.cpp000066400000000000000000000034751272363601300221260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "bendpointaction.h" #include "../items/itembase.h" #include "../connectors/connectoritem.h" BendpointAction::BendpointAction(const QString & text, QObject * parent) : QAction(text, parent) { m_lastHoverEnterConnectorItem = NULL; m_lastHoverEnterItem = NULL; } void BendpointAction::setLastHoverEnterConnectorItem(ConnectorItem * connectorItem) { m_lastHoverEnterConnectorItem = connectorItem; } void BendpointAction::setLastHoverEnterItem(ItemBase * itemBase) { m_lastHoverEnterItem = itemBase; } void BendpointAction::setLastLocation(QPointF p) { m_lastLocation = p; } ConnectorItem * BendpointAction::lastHoverEnterConnectorItem() { return m_lastHoverEnterConnectorItem; } ItemBase * BendpointAction::lastHoverEnterItem() { return m_lastHoverEnterItem; } QPointF BendpointAction::lastLocation() { return m_lastLocation; } fritzing-app-0.9.3b/src/utils/bendpointaction.h000066400000000000000000000031401272363601300215600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef BENDPOINTACTION_H #define BENDPOINTACTION_H #include #include #include class BendpointAction : public QAction { Q_OBJECT public: BendpointAction(const QString & text, QObject * parent); void setLastHoverEnterConnectorItem(class ConnectorItem *); void setLastHoverEnterItem(class ItemBase *); void setLastLocation(QPointF); class ConnectorItem * lastHoverEnterConnectorItem(); class ItemBase * lastHoverEnterItem(); QPointF lastLocation(); protected: class ConnectorItem * m_lastHoverEnterConnectorItem; class ItemBase * m_lastHoverEnterItem; QPointF m_lastLocation; }; #endif fritzing-app-0.9.3b/src/utils/bezier.cpp000066400000000000000000001063541272363601300202260ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "bezier.h" #include "graphicsutils.h" #include "../debugdialog.h" #include #include ///////////////////////////////////////////// // borrowed liberally from the following sources: // // http://processingjs.nihongoresources.com/bezierinfo/ // http://www.tinaja.com/text/bezmath.html, // http://www.lemoda.net/maths/bezier-length/index.html, // http://www.flong.com/texts/code/shapers_bez/ // http://steve.hollasch.net/cgindex/curves/cbezarclen.html ///////////////////////////////////////////// // utilities from http://www.flong.com/texts/code/shapers_bez/ double B0 (double t){ return (1-t)*(1-t)*(1-t); } double B1 (double t){ return 3*t* (1-t)*(1-t); } double B2 (double t){ return 3*t*t* (1-t); } double B3 (double t){ return t*t*t; } ///////////////////////////////////////////// // utilities from http://processingjs.nihongoresources.com/bezierinfo/sketchsource.php?sketch=cubicGaussQuadrature double base3(double t, double p1, double p2, double p3, double p4) { double t1 = -3*p1 + 9*p2 - 9*p3 + 3*p4; double t2 = t*t1 + 6*p1 - 12*p2 + 6*p3; return t*t2 - 3*p1 + 3*p2; } // Legendre-Gauss abscissae (xi values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x)) double Tvalues[25][24] = { {}, {}, {-0.5773502691896257310588680411456152796745,0.5773502691896257310588680411456152796745}, {0.0000000000000000000000000000000000000000,-0.7745966692414834042779148148838430643082,0.7745966692414834042779148148838430643082}, {-0.3399810435848562573113440521410666406155,0.3399810435848562573113440521410666406155,-0.8611363115940525725378051902225706726313,0.8611363115940525725378051902225706726313}, {0.0000000000000000000000000000000000000000,-0.5384693101056831077144693153968546539545,0.5384693101056831077144693153968546539545,-0.9061798459386639637003213465504813939333,0.9061798459386639637003213465504813939333}, {0.6612093864662644815410885712481103837490,-0.6612093864662644815410885712481103837490,-0.2386191860831969047129774708082550205290,0.2386191860831969047129774708082550205290,-0.9324695142031520500580654697841964662075,0.9324695142031520500580654697841964662075}, {0.0000000000000000000000000000000000000000,0.4058451513773971841558818596240598708391,-0.4058451513773971841558818596240598708391,-0.7415311855993944600839995473506860435009,0.7415311855993944600839995473506860435009,-0.9491079123427584862682238053821492940187,0.9491079123427584862682238053821492940187}, {-0.1834346424956498078362443493460887111723,0.1834346424956498078362443493460887111723,-0.5255324099163289908176466269651427865028,0.5255324099163289908176466269651427865028,-0.7966664774136267279658341067261062562466,0.7966664774136267279658341067261062562466,-0.9602898564975362871720676594122778624296,0.9602898564975362871720676594122778624296}, {0.0000000000000000000000000000000000000000,-0.8360311073266357695388251158874481916428,0.8360311073266357695388251158874481916428,-0.9681602395076260858530758923734538257122,0.9681602395076260858530758923734538257122,-0.3242534234038089158147499801998492330313,0.3242534234038089158147499801998492330313,-0.6133714327005903577116896485676988959312,0.6133714327005903577116896485676988959312}, {-0.1488743389816312157059030596428783610463,0.1488743389816312157059030596428783610463,-0.4333953941292472133994806426926515996456,0.4333953941292472133994806426926515996456,-0.6794095682990244355892173189204186201096,0.6794095682990244355892173189204186201096,-0.8650633666889845363456856830453034490347,0.8650633666889845363456856830453034490347,-0.9739065285171717434309357486199587583542,0.9739065285171717434309357486199587583542}, {0.0000000000000000000000000000000000000000,-0.2695431559523449593918087430211016908288,0.2695431559523449593918087430211016908288,-0.5190961292068118071441062966187018901110,0.5190961292068118071441062966187018901110,-0.7301520055740493564400139803183265030384,0.7301520055740493564400139803183265030384,-0.8870625997680953167545681026240345090628,0.8870625997680953167545681026240345090628,-0.9782286581460569729884468870295677334070,0.9782286581460569729884468870295677334070}, {-0.1252334085114689132822718420356977730989,0.1252334085114689132822718420356977730989,-0.3678314989981801841345543380157323554158,0.3678314989981801841345543380157323554158,-0.5873179542866174829285341729701030999422,0.5873179542866174829285341729701030999422,-0.7699026741943046925342741815256886184216,0.7699026741943046925342741815256886184216,-0.9041172563704749087776235683122649788857,0.9041172563704749087776235683122649788857,-0.9815606342467192435563561048184055835009,0.9815606342467192435563561048184055835009}, {0.0000000000000000000000000000000000000000,-0.2304583159551348015003924274424207396805,0.2304583159551348015003924274424207396805,-0.4484927510364468683512484403763664886355,0.4484927510364468683512484403763664886355,-0.6423493394403402279024817289609927684069,0.6423493394403402279024817289609927684069,-0.8015780907333098781464286730624735355377,0.8015780907333098781464286730624735355377,-0.9175983992229779229177211163914762437344,0.9175983992229779229177211163914762437344,-0.9841830547185881350458203087328001856804,0.9841830547185881350458203087328001856804}, {-0.1080549487073436676354276642086915671825,0.1080549487073436676354276642086915671825,-0.3191123689278897446186533670697826892138,0.3191123689278897446186533670697826892138,-0.5152486363581540995681962158414535224438,0.5152486363581540995681962158414535224438,-0.6872929048116854788830210054584313184023,0.6872929048116854788830210054584313184023,-0.8272013150697650196718768711434677243233,0.8272013150697650196718768711434677243233,-0.9284348836635735180422557277779560536146,0.9284348836635735180422557277779560536146,-0.9862838086968123141318187663273420184851,0.9862838086968123141318187663273420184851}, {0.0000000000000000000000000000000000000000,-0.2011940939974345143870237961891689337790,0.2011940939974345143870237961891689337790,-0.3941513470775633853904196257644798606634,0.3941513470775633853904196257644798606634,-0.5709721726085388304738899023504927754402,0.5709721726085388304738899023504927754402,-0.7244177313601700696210627938853576779366,0.7244177313601700696210627938853576779366,-0.8482065834104272061821916395274456590414,0.8482065834104272061821916395274456590414,-0.9372733924007059513883177714888006448746,0.9372733924007059513883177714888006448746,-0.9879925180204853774057482951320707798004,0.9879925180204853774057482951320707798004}, {-0.0950125098376374405129141109682677779347,0.0950125098376374405129141109682677779347,-0.2816035507792589154263396267197094857693,0.2816035507792589154263396267197094857693,-0.4580167776572273696800152720243204385042,0.4580167776572273696800152720243204385042,-0.6178762444026437705701937375124543905258,0.6178762444026437705701937375124543905258,-0.7554044083550029986540153004170861095190,0.7554044083550029986540153004170861095190,-0.8656312023878317551961458775622304528952,0.8656312023878317551961458775622304528952,-0.9445750230732326002680565579794347286224,0.9445750230732326002680565579794347286224,-0.9894009349916499385102497399202547967434,0.9894009349916499385102497399202547967434}, {0.0000000000000000000000000000000000000000,-0.1784841814958478545261044700964703224599,0.1784841814958478545261044700964703224599,-0.3512317634538763000406902392569463700056,0.3512317634538763000406902392569463700056,-0.5126905370864769384553483178024180233479,0.5126905370864769384553483178024180233479,-0.6576711592166907260903485621383879333735,0.6576711592166907260903485621383879333735,-0.7815140038968013680431567991035990417004,0.7815140038968013680431567991035990417004,-0.8802391537269859123071569229068700224161,0.8802391537269859123071569229068700224161,-0.9506755217687677950166857954172883182764,0.9506755217687677950166857954172883182764,-0.9905754753144173641032921295845881104469,0.9905754753144173641032921295845881104469}, {-0.0847750130417353059408824833553808275610,0.0847750130417353059408824833553808275610,-0.2518862256915054831374334298743633553386,0.2518862256915054831374334298743633553386,-0.4117511614628426297457508553634397685528,0.4117511614628426297457508553634397685528,-0.5597708310739475390249708652845583856106,0.5597708310739475390249708652845583856106,-0.6916870430603532238222896921797655522823,0.6916870430603532238222896921797655522823,-0.8037049589725231424353069087374024093151,0.8037049589725231424353069087374024093151,-0.8926024664975557021406871172075625509024,0.8926024664975557021406871172075625509024,-0.9558239495713977129653926567698363214731,0.9558239495713977129653926567698363214731,-0.9915651684209308980300079383596312254667,0.9915651684209308980300079383596312254667}, {0.0000000000000000000000000000000000000000,-0.1603586456402253668240831530056311748922,0.1603586456402253668240831530056311748922,-0.3165640999636298302810644145210972055793,0.3165640999636298302810644145210972055793,-0.4645707413759609383241411251219687983394,0.4645707413759609383241411251219687983394,-0.6005453046616809897884081692609470337629,0.6005453046616809897884081692609470337629,-0.7209661773352293856476080691209062933922,0.7209661773352293856476080691209062933922,-0.8227146565371428188484514976153150200844,0.8227146565371428188484514976153150200844,-0.9031559036148179009373393455462064594030,0.9031559036148179009373393455462064594030,-0.9602081521348300174878431789693422615528,0.9602081521348300174878431789693422615528,-0.9924068438435843519940249279898125678301,0.9924068438435843519940249279898125678301}, {-0.0765265211334973383117130651953630149364,0.0765265211334973383117130651953630149364,-0.2277858511416450681963397073559463024139,0.2277858511416450681963397073559463024139,-0.3737060887154195487624974703066982328892,0.3737060887154195487624974703066982328892,-0.5108670019508271264996324134699534624815,0.5108670019508271264996324134699534624815,-0.6360536807265150249790508496516849845648,0.6360536807265150249790508496516849845648,-0.7463319064601507957235071444301865994930,0.7463319064601507957235071444301865994930,-0.8391169718222187823286617458506952971220,0.8391169718222187823286617458506952971220,-0.9122344282513259461353527512983419001102,0.9122344282513259461353527512983419001102,-0.9639719272779138092843709273438435047865,0.9639719272779138092843709273438435047865,-0.9931285991850948846604296704754233360291,0.9931285991850948846604296704754233360291}, {0.0000000000000000000000000000000000000000,-0.1455618541608950933241573011400760151446,0.1455618541608950933241573011400760151446,-0.2880213168024011172185794293909566476941,0.2880213168024011172185794293909566476941,-0.4243421202074387776903563462838064879179,0.4243421202074387776903563462838064879179,-0.5516188358872198271853903861483559012413,0.5516188358872198271853903861483559012413,-0.6671388041974123384036943207320291548967,0.6671388041974123384036943207320291548967,-0.7684399634756778896260698275000322610140,0.7684399634756778896260698275000322610140,-0.8533633645833172964856316866644192487001,0.8533633645833172964856316866644192487001,-0.9200993341504007938524978271743748337030,0.9200993341504007938524978271743748337030,-0.9672268385663063128276917268522083759308,0.9672268385663063128276917268522083759308,-0.9937521706203894522602126926358323544264,0.9937521706203894522602126926358323544264}, {-0.0697392733197222253194169638845778536052,0.0697392733197222253194169638845778536052,-0.2078604266882212725509049278116435743868,0.2078604266882212725509049278116435743868,-0.3419358208920842412403828802780481055379,0.3419358208920842412403828802780481055379,-0.4693558379867570073962212973128771409392,0.4693558379867570073962212973128771409392,-0.5876404035069116016387624767958186566830,0.5876404035069116016387624767958186566830,-0.6944872631866827461522007070016115903854,0.6944872631866827461522007070016115903854,-0.7878168059792081123759999172762036323547,0.7878168059792081123759999172762036323547,-0.8658125777203001804949167308222968131304,0.8658125777203001804949167308222968131304,-0.9269567721871739829353487039043102413416,0.9269567721871739829353487039043102413416,-0.9700604978354286922481719557254109531641,0.9700604978354286922481719557254109531641,-0.9942945854823992402060639506089501082897,0.9942945854823992402060639506089501082897}, {0.0000000000000000000000000000000000000000,-0.1332568242984661088801345840693102218211,0.1332568242984661088801345840693102218211,-0.2641356809703449548543119362875586375594,0.2641356809703449548543119362875586375594,-0.3903010380302908144400930723350029438734,0.3903010380302908144400930723350029438734,-0.5095014778460075222099590064317453652620,0.5095014778460075222099590064317453652620,-0.6196098757636461229481028567533940076828,0.6196098757636461229481028567533940076828,-0.7186613631319501704908248029823880642653,0.7186613631319501704908248029823880642653,-0.8048884016188398993207897547108586877584,0.8048884016188398993207897547108586877584,-0.8767523582704416229560706597112584859133,0.8767523582704416229560706597112584859133,-0.9329710868260161493736859483760781586170,0.9329710868260161493736859483760781586170,-0.9725424712181152120393790028174407780170,0.9725424712181152120393790028174407780170,-0.9947693349975521570627279288601130247116,0.9947693349975521570627279288601130247116}, {-0.0640568928626056299791002857091370970011,0.0640568928626056299791002857091370970011,-0.1911188674736163106704367464772076345980,0.1911188674736163106704367464772076345980,-0.3150426796961633968408023065421730279922,0.3150426796961633968408023065421730279922,-0.4337935076260451272567308933503227308393,0.4337935076260451272567308933503227308393,-0.5454214713888395626995020393223967403173,0.5454214713888395626995020393223967403173,-0.6480936519369755455244330732966773211956,0.6480936519369755455244330732966773211956,-0.7401241915785543579175964623573236167431,0.7401241915785543579175964623573236167431,-0.8200019859739029470802051946520805358887,0.8200019859739029470802051946520805358887,-0.8864155270044010714869386902137193828821,0.8864155270044010714869386902137193828821,-0.9382745520027327978951348086411599069834,0.9382745520027327978951348086411599069834,-0.9747285559713094738043537290650419890881,0.9747285559713094738043537290650419890881,-0.9951872199970213106468008845695294439793,0.9951872199970213106468008845695294439793}}; // Legendre-Gauss weights (wi values, defined by a function linked to in the Bezier primer article) double Cvalues[25][24] = { {}, {}, {1.0000000000000000000000000000000000000000,1.0000000000000000000000000000000000000000}, {0.8888888888888888395456433499930426478386,0.5555555555555555802271783250034786760807,0.5555555555555555802271783250034786760807}, {0.6521451548625460947761212082696147263050,0.6521451548625460947761212082696147263050,0.3478548451374538497127275604725582525134,0.3478548451374538497127275604725582525134}, {0.5688888888888888883954564334999304264784,0.4786286704993664709029133064177585765719,0.4786286704993664709029133064177585765719,0.2369268850561890848993584768322762101889,0.2369268850561890848993584768322762101889}, {0.3607615730481386062677984227775596082211,0.3607615730481386062677984227775596082211,0.4679139345726910370615314604947343468666,0.4679139345726910370615314604947343468666,0.1713244923791703566706701167277060449123,0.1713244923791703566706701167277060449123}, {0.4179591836734694032529091600736137479544,0.3818300505051189230876218516641529276967,0.3818300505051189230876218516641529276967,0.2797053914892766446342875497066415846348,0.2797053914892766446342875497066415846348,0.1294849661688697028960604029634851031005,0.1294849661688697028960604029634851031005}, {0.3626837833783619902128236844873754307628,0.3626837833783619902128236844873754307628,0.3137066458778872690693617641954915598035,0.3137066458778872690693617641954915598035,0.2223810344533744820516574236535234376788,0.2223810344533744820516574236535234376788,0.1012285362903762586661571276636095717549,0.1012285362903762586661571276636095717549}, {0.3302393550012597822629345500899944454432,0.1806481606948573959137149813614087179303,0.1806481606948573959137149813614087179303,0.0812743883615744122650426106702070683241,0.0812743883615744122650426106702070683241,0.3123470770400028628799304897256661206484,0.3123470770400028628799304897256661206484,0.2606106964029354378098446431977208703756,0.2606106964029354378098446431977208703756}, {0.2955242247147528700246255084493895992637,0.2955242247147528700246255084493895992637,0.2692667193099963496294435572053771466017,0.2692667193099963496294435572053771466017,0.2190863625159820415877476307286997325718,0.2190863625159820415877476307286997325718,0.1494513491505805868886369580650352872908,0.1494513491505805868886369580650352872908,0.0666713443086881379917585377370414789766,0.0666713443086881379917585377370414789766}, {0.2729250867779006162194832540990319103003,0.2628045445102466515230332788632949814200,0.2628045445102466515230332788632949814200,0.2331937645919904822378043718344997614622,0.2331937645919904822378043718344997614622,0.1862902109277342621584949711177614517510,0.1862902109277342621584949711177614517510,0.1255803694649046120535018644659430719912,0.1255803694649046120535018644659430719912,0.0556685671161736631007421749472996452823,0.0556685671161736631007421749472996452823}, {0.2491470458134027732288728884668671526015,0.2491470458134027732288728884668671526015,0.2334925365383548057085505433860816992819,0.2334925365383548057085505433860816992819,0.2031674267230659247651658461109036579728,0.2031674267230659247651658461109036579728,0.1600783285433462210800570346691529266536,0.1600783285433462210800570346691529266536,0.1069393259953184266430881166343169752508,0.1069393259953184266430881166343169752508,0.0471753363865118277575838590109924552962,0.0471753363865118277575838590109924552962}, {0.2325515532308738975153517003491288051009,0.2262831802628972321933531475224299356341,0.2262831802628972321933531475224299356341,0.2078160475368885096170146198346628807485,0.2078160475368885096170146198346628807485,0.1781459807619457380578609217991470359266,0.1781459807619457380578609217991470359266,0.1388735102197872495199959530509659089148,0.1388735102197872495199959530509659089148,0.0921214998377284516317686779984796885401,0.0921214998377284516317686779984796885401,0.0404840047653158771612247335269785253331,0.0404840047653158771612247335269785253331}, {0.2152638534631577948985636794532183557749,0.2152638534631577948985636794532183557749,0.2051984637212956041896205761076998896897,0.2051984637212956041896205761076998896897,0.1855383974779378219999159682629397138953,0.1855383974779378219999159682629397138953,0.1572031671581935463599677404999965801835,0.1572031671581935463599677404999965801835,0.1215185706879031851679329179205524269491,0.1215185706879031851679329179205524269491,0.0801580871597602079292599341897584963590,0.0801580871597602079292599341897584963590,0.0351194603317518602714208952875196700916,0.0351194603317518602714208952875196700916}, {0.2025782419255612865072180284187197685242,0.1984314853271115786093048427574103698134,0.1984314853271115786093048427574103698134,0.1861610000155622113293674146916600875556,0.1861610000155622113293674146916600875556,0.1662692058169939202105780395868350751698,0.1662692058169939202105780395868350751698,0.1395706779261543240000520427201990969479,0.1395706779261543240000520427201990969479,0.1071592204671719394948325998484506271780,0.1071592204671719394948325998484506271780,0.0703660474881081243747615872052847407758,0.0703660474881081243747615872052847407758,0.0307532419961172691358353148416426847689,0.0307532419961172691358353148416426847689}, {0.1894506104550685021692402187909465283155,0.1894506104550685021692402187909465283155,0.1826034150449235837765371570640127174556,0.1826034150449235837765371570640127174556,0.1691565193950025358660127494658809155226,0.1691565193950025358660127494658809155226,0.1495959888165767359691216142891789786518,0.1495959888165767359691216142891789786518,0.1246289712555338768940060845125117339194,0.1246289712555338768940060845125117339194,0.0951585116824927856882254673109855502844,0.0951585116824927856882254673109855502844,0.0622535239386478936318702892549481475726,0.0622535239386478936318702892549481475726,0.0271524594117540964133272751723779947497,0.0271524594117540964133272751723779947497}, {0.1794464703562065333031227964966092258692,0.1765627053669926449508409405098063871264,0.1765627053669926449508409405098063871264,0.1680041021564500358653759803928551264107,0.1680041021564500358653759803928551264107,0.1540457610768102836296122859494062140584,0.1540457610768102836296122859494062140584,0.1351363684685254751283167706787935458124,0.1351363684685254751283167706787935458124,0.1118838471934039680011352402289048768580,0.1118838471934039680011352402289048768580,0.0850361483171791776580761279547004960477,0.0850361483171791776580761279547004960477,0.0554595293739872027827253475606994470581,0.0554595293739872027827253475606994470581,0.0241483028685479314545681006620725383982,0.0241483028685479314545681006620725383982}, {0.1691423829631436004383715498988749459386,0.1691423829631436004383715498988749459386,0.1642764837458327298325144738555536605418,0.1642764837458327298325144738555536605418,0.1546846751262652419622867228099494241178,0.1546846751262652419622867228099494241178,0.1406429146706506538855308008351130411029,0.1406429146706506538855308008351130411029,0.1225552067114784593471199514169711619616,0.1225552067114784593471199514169711619616,0.1009420441062871681703327908508072141558,0.1009420441062871681703327908508072141558,0.0764257302548890515847546112127020023763,0.0764257302548890515847546112127020023763,0.0497145488949697969549568199454370187595,0.0497145488949697969549568199454370187595,0.0216160135264833117019200869890482863411,0.0216160135264833117019200869890482863411}, {0.1610544498487836984068621859478298574686,0.1589688433939543399375793342187535017729,0.1589688433939543399375793342187535017729,0.1527660420658596696075193221986410208046,0.1527660420658596696075193221986410208046,0.1426067021736066031678547005867585539818,0.1426067021736066031678547005867585539818,0.1287539625393362141547726196222356520593,0.1287539625393362141547726196222356520593,0.1115666455473339896409257221421285066754,0.1115666455473339896409257221421285066754,0.0914900216224499990280705219447554554790,0.0914900216224499990280705219447554554790,0.0690445427376412262931992813719261903316,0.0690445427376412262931992813719261903316,0.0448142267656995996194524423117400147021,0.0448142267656995996194524423117400147021,0.0194617882297264781221723950466184760444,0.0194617882297264781221723950466184760444}, {0.1527533871307258372951309866039082407951,0.1527533871307258372951309866039082407951,0.1491729864726037413369397199858212843537,0.1491729864726037413369397199858212843537,0.1420961093183820411756101975697674788535,0.1420961093183820411756101975697674788535,0.1316886384491766370796739238357986323535,0.1316886384491766370796739238357986323535,0.1181945319615184120110029653005767613649,0.1181945319615184120110029653005767613649,0.1019301198172404415709380032240005675703,0.1019301198172404415709380032240005675703,0.0832767415767047547436874310733401216567,0.0832767415767047547436874310733401216567,0.0626720483341090678353069165495980996639,0.0626720483341090678353069165495980996639,0.0406014298003869386621822457072994438931,0.0406014298003869386621822457072994438931,0.0176140071391521178811867542890468030237,0.0176140071391521178811867542890468030237}, {0.1460811336496904144777175815761438570917,0.1445244039899700461138110085812513716519,0.1445244039899700461138110085812513716519,0.1398873947910731496691028041823301464319,0.1398873947910731496691028041823301464319,0.1322689386333374683690777828815043903887,0.1322689386333374683690777828815043903887,0.1218314160537285334440227302366110961884,0.1218314160537285334440227302366110961884,0.1087972991671483785625085261017375160009,0.1087972991671483785625085261017375160009,0.0934444234560338621298214434318651910871,0.0934444234560338621298214434318651910871,0.0761001136283793039316591944043466355652,0.0761001136283793039316591944043466355652,0.0571344254268572049326735395879950374365,0.0571344254268572049326735395879950374365,0.0369537897708524937234741969405149575323,0.0369537897708524937234741969405149575323,0.0160172282577743345377552230957007850520,0.0160172282577743345377552230957007850520}, {0.1392518728556319806966001806358690373600,0.1392518728556319806966001806358690373600,0.1365414983460151721050834794368711300194,0.1365414983460151721050834794368711300194,0.1311735047870623838139891859100316651165,0.1311735047870623838139891859100316651165,0.1232523768105124178928733158500108402222,0.1232523768105124178928733158500108402222,0.1129322960805392156435900119504367467016,0.1129322960805392156435900119504367467016,0.1004141444428809648581335522976587526500,0.1004141444428809648581335522976587526500,0.0859416062170677286236042391465161927044,0.0859416062170677286236042391465161927044,0.0697964684245204886048341563764552120119,0.0697964684245204886048341563764552120119,0.0522933351526832859712534684604179346934,0.0522933351526832859712534684604179346934,0.0337749015848141515006020085820637177676,0.0337749015848141515006020085820637177676,0.0146279952982721998810955454928262042813,0.0146279952982721998810955454928262042813}, {0.1336545721861061852830943053049850277603,0.1324620394046966131984532921705977059901,0.1324620394046966131984532921705977059901,0.1289057221880821613169132433540653437376,0.1289057221880821613169132433540653437376,0.1230490843067295336776822978208656422794,0.1230490843067295336776822978208656422794,0.1149966402224113642960290349037677515298,0.1149966402224113642960290349037677515298,0.1048920914645414120824895576333801727742,0.1048920914645414120824895576333801727742,0.0929157660600351542612429511791560798883,0.0929157660600351542612429511791560798883,0.0792814117767189491248203125906002242118,0.0792814117767189491248203125906002242118,0.0642324214085258499151720457120973151177,0.0642324214085258499151720457120973151177,0.0480376717310846690356385124687221832573,0.0480376717310846690356385124687221832573,0.0309880058569794447631551292943186126649,0.0309880058569794447631551292943186126649,0.0134118594871417712993677540112003043760,0.0134118594871417712993677540112003043760}, {0.1279381953467521593204025975865079089999,0.1279381953467521593204025975865079089999,0.1258374563468283025002847352880053222179,0.1258374563468283025002847352880053222179,0.1216704729278033914052770114722079597414,0.1216704729278033914052770114722079597414,0.1155056680537255991980671865348995197564,0.1155056680537255991980671865348995197564,0.1074442701159656343712356374453520402312,0.1074442701159656343712356374453520402312,0.0976186521041138843823858906034729443491,0.0976186521041138843823858906034729443491,0.0861901615319532743431096832864568568766,0.0861901615319532743431096832864568568766,0.0733464814110802998392557583429152145982,0.0733464814110802998392557583429152145982,0.0592985849154367833380163688161701429635,0.0592985849154367833380163688161701429635,0.0442774388174198077483545432642131345347,0.0442774388174198077483545432642131345347,0.0285313886289336633705904233693217975087,0.0285313886289336633705904233693217975087,0.0123412297999872001830201639904771582223,0.0123412297999872001830201639904771582223}}; ///////////////////////////////////////////// Bezier::Bezier(QPointF cp0, QPointF cp1) { m_cp0 = cp0; m_cp1 = cp1; m_isEmpty = false; } Bezier::Bezier() { m_isEmpty = true; } bool Bezier::isEmpty() const { return m_isEmpty; } void Bezier::clear() { m_isEmpty = true; } QPointF Bezier::cp0() const { return m_cp0; } QPointF Bezier::cp1() const { return m_cp1; } QPointF Bezier::endpoint0() const { return m_endpoint0; } QPointF Bezier::endpoint1() const { return m_endpoint1; } void Bezier::set_cp0(QPointF cp0) { m_cp0 = cp0; m_isEmpty = false; } void Bezier::set_cp1(QPointF cp1) { m_cp1 = cp1; m_isEmpty = false; } void Bezier::set_endpoints(QPointF ep0, QPointF ep1) { m_endpoint0 = ep0; m_endpoint1 = ep1; } Bezier Bezier::fromElement(QDomElement & element) { Bezier bezier; if (element.tagName().compare("bezier") != 0) return bezier; QDomElement point = element.firstChildElement("cp0"); if (point.isNull()) return bezier; double x = point.attribute("x").toDouble(); double y = point.attribute("y").toDouble(); bezier.set_cp0(QPointF(x, y)); point = element.firstChildElement("cp1"); x = point.attribute("x").toDouble(); y = point.attribute("y").toDouble(); bezier.set_cp1(QPointF(x, y)); return bezier; } void Bezier::write(QXmlStreamWriter & streamWriter) { if (isEmpty()) return; streamWriter.writeStartElement("bezier"); streamWriter.writeStartElement("cp0"); streamWriter.writeAttribute("x", QString::number(m_cp0.x())); streamWriter.writeAttribute("y", QString::number(m_cp0.y())); streamWriter.writeEndElement(); streamWriter.writeStartElement("cp1"); streamWriter.writeAttribute("x", QString::number(m_cp1.x())); streamWriter.writeAttribute("y", QString::number(m_cp1.y())); streamWriter.writeEndElement(); streamWriter.writeEndElement(); } bool Bezier::operator==(const Bezier & other) const { if (m_isEmpty != other.isEmpty()) return false; if (m_cp0 != other.cp0()) return false; if (m_cp1 != other.cp1()) return false; return true; } bool Bezier::operator!=(const Bezier & other) const { if (m_isEmpty != other.isEmpty()) return true; if (m_cp0 != other.cp0()) return true; if (m_cp1 != other.cp1()) return true; return false; } void Bezier::recalc(QPointF p) { // http://www.flong.com/texts/code/shapers_bez/ // http://www.lemoda.net/maths/bezier-length/index.html, // arbitrary but reasonable t-values for interior control points double t0 = 0.3; double t1 = 0.7; if (m_drag_cp0) { double x = (p.x() - m_endpoint0.x() * B0(t0) - m_cp1.x() * B2(t0) - m_endpoint1.x() * B3(t0)) / B1(t0); double y = (p.y() - m_endpoint0.y() * B0(t0) - m_cp1.y() * B2(t0) - m_endpoint1.y() * B3(t0)) / B1(t0); m_cp0 = QPointF(x, y); } else { double x = (p.x() - m_endpoint0.x() * B0(t1) - m_cp0.x() * B1(t1) - m_endpoint1.x() * B3(t1)) / B2(t1); double y = (p.y() - m_endpoint0.y() * B0(t1) - m_cp0.y() * B1(t1) - m_endpoint1.y() * B3(t1)) / B2(t1); m_cp1 = QPointF(x, y); } /* DebugDialog::debug(QString("ix:%1 p0x:%2,p0y:%3 p1x:%4,p1y:%5 px:%6,py:%7") .arg(m_drag_cp0) .arg(m_endpoint0.x()) .arg(m_endpoint0.y()) .arg(m_endpoint1.x()) .arg(m_endpoint1.y()) .arg(p.x()) .arg(p.y()) ); */ m_isEmpty = false; } void Bezier::initToEnds(QPointF cp0, QPointF cp1) { m_endpoint0 = m_cp0 = cp0; m_endpoint1 = m_cp1 = cp1; m_isEmpty = false; } double Bezier::xFromT(double t) const { // http://www.lemoda.net/maths/bezier-length/index.html return m_endpoint0.x() * B0(t) + m_cp0.x() * B1(t) + m_cp1.x() * B2(t) + m_endpoint1.x() * B3(t); } double Bezier::xFromTPrime(double t) const { return base3(t, m_endpoint0.x(), m_cp0.x(), m_cp1.x(), m_endpoint1.x()); } double Bezier::yFromT(double t) const { // http://www.lemoda.net/maths/bezier-length/index.html return m_endpoint0.y() * B0(t) + m_cp0.y() * B1(t) + m_cp1.y() * B2(t) + m_endpoint1.y() * B3(t); } void Bezier::split(double t, Bezier & left, Bezier & right) const { // http://processingjs.nihongoresources.com/bezierinfo/sketchsource.php?sketch=CubicDeCasteljau // interpolate from 4 to 3 points QPointF p5((1-t)*m_endpoint0.x() + t*m_cp0.x(), (1-t)*m_endpoint0.y() + t*m_cp0.y()); QPointF p6((1-t)*m_cp0.x() + t*m_cp1.x(), (1-t)*m_cp0.y() + t*m_cp1.y()); QPointF p7((1-t)*m_cp1.x() + t*m_endpoint1.x(), (1-t)*m_cp1.y() + t*m_endpoint1.y()); // interpolate from 3 to 2 points QPointF p8((1-t)*p5.x() + t*p6.x(), (1-t)*p5.y() + t*p6.y()); QPointF p9((1-t)*p6.x() + t*p7.x(), (1-t)*p6.y() + t*p7.y()); // interpolate from 2 points to 1 point QPointF p10((1-t)*p8.x() + t*p9.x(), (1-t)*p8.y() + t*p9.y()); // we now have all the values we need to build the subcurves left.m_endpoint0 = m_endpoint0; left.m_cp0 = p5; left.m_cp1 = p8; right.m_endpoint0 = left.m_endpoint1 = p10; right.m_cp0 = p9; right.m_cp1 = p7; right.m_endpoint1 = m_endpoint1; left.m_isEmpty = right.m_isEmpty = false; } void Bezier::initControlIndex(QPointF p, double width) { double t = findSplit(p, width); double totalLen = computeCubicCurveLength(1, 24); double len = computeCubicCurveLength(t, 24); //double d0 = GraphicsUtils::distanceSqd(p, m_cp0); //double d1 = GraphicsUtils::distanceSqd(p, m_cp1); m_drag_cp0 = (len <= totalLen / 2); } /** * Gauss quadrature for cubic Bezier curves */ double Bezier::computeCubicCurveLength(double z, int n) const { // http://processingjs.nihongoresources.com/bezierinfo/sketchsource.php?sketch=cubicGaussQuadrature double z2 = z/2.0; double sum = 0; for (int i = 0; i < n; i++) { double corrected_t = z2 * Tvalues[n][i] + z2; sum += Cvalues[n][i] * cubicF(corrected_t); } return z2 * sum; } double Bezier::cubicF(double t) const { double xbase = base3(t, m_endpoint0.x(), m_cp0.x(), m_cp1.x(), m_endpoint1.x()); double ybase = base3(t, m_endpoint0.y(), m_cp0.y(), m_cp1.y(), m_endpoint1.y()); double combined = xbase*xbase + ybase*ybase; return qSqrt(combined); } void Bezier::copy(const Bezier * other) { if (other == NULL) { m_isEmpty = true; return; } m_cp0 = other->m_cp0; m_cp1 = other->m_cp1; m_endpoint0 = other->m_endpoint0; m_endpoint1 = other->m_endpoint1; m_isEmpty = other->m_isEmpty; m_drag_cp0 = other->m_drag_cp0; } double Bezier::findSplit(QPointF p, double minDistance) const { double bestT = 0; double lastDistance = std::numeric_limits::max(); double blen = computeCubicCurveLength(1.0, 24); double increment = 1.0 / blen; double minDSqd = minDistance * minDistance; for (double t = 0; t <= 1; t += increment) { double x = xFromT(t); double y = yFromT(t); double d = GraphicsUtils::distanceSqd(p, QPointF(x, y)); if (d >= lastDistance) { if (d > minDSqd) continue; break; } bestT = t; lastDistance = d; } return bestT; } void Bezier::translateToZero() { m_cp0 -= m_endpoint0; m_cp1 -= m_endpoint0; m_endpoint1 -= m_endpoint0; m_endpoint0 -= m_endpoint0; } void Bezier::translate(QPointF p) { m_cp0 += p; m_cp1 += p; m_endpoint1 += p; m_endpoint0 += p; } Bezier Bezier::join(const Bezier * other) const { Bezier bezier; bool otherIsEmpty = (other == NULL || other->isEmpty()); if (isEmpty() && otherIsEmpty) { return bezier; } else { if (isEmpty()) { bezier.set_cp0(m_endpoint0); bezier.set_cp1(other->cp1()); } else if (other->isEmpty()) { bezier.set_cp1(other->m_endpoint1); bezier.set_cp0(cp0()); } else { bezier.set_cp0(cp0()); bezier.set_cp1(other->cp1()); } } return bezier; } bool Bezier::drag0() { return m_drag_cp0; } fritzing-app-0.9.3b/src/utils/bezier.h000066400000000000000000000044351272363601300176700ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef BEZIER_H #define BEZIER_H #include #include #include // cubic bezier auxiliary implementation class Bezier { public: Bezier(QPointF cp1, QPointF cp2); Bezier(); QPointF cp0() const; QPointF cp1() const; QPointF endpoint0() const; QPointF endpoint1() const; void set_cp0(QPointF); void set_cp1(QPointF); void set_endpoints(QPointF, QPointF); bool isEmpty() const; void clear(); void write(QXmlStreamWriter &); bool operator==(const Bezier &) const; bool operator!=(const Bezier &) const; void recalc(QPointF p); void initToEnds(QPointF cp0, QPointF cp1); double xFromT(double t) const; double xFromTPrime(double t) const; double yFromT(double t) const; void split(double t, Bezier & left, Bezier & right) const; void initControlIndex(QPointF fromPoint, double width); double computeCubicCurveLength(double z, int n) const; void copy(const Bezier *); double findSplit(QPointF p, double minDistance) const; void translateToZero(); void translate(QPointF); Bezier join(const Bezier * other) const; bool drag0(); protected: double cubicF(double t) const; public: static Bezier fromElement(QDomElement & element); protected: QPointF m_endpoint0; QPointF m_endpoint1; QPointF m_cp0; QPointF m_cp1; bool m_isEmpty; bool m_drag_cp0; }; #endif fritzing-app-0.9.3b/src/utils/bezierdisplay.cpp000066400000000000000000000113111272363601300216000ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "bezierdisplay.h" #include "../viewlayer.h" #include "../debugdialog.h" #include "../processeventblocker.h" #include "graphicsutils.h" #include #include #include BezierDisplay::BezierDisplay() { m_itemL0 = m_itemL1 = NULL; m_itemE0 = m_itemE1 = NULL; } BezierDisplay::~BezierDisplay() { //DebugDialog::debug("removing bezier display"); if (m_itemL0) { m_itemL0->scene()->removeItem(m_itemL0); delete m_itemL0; } if (m_itemL1) { m_itemL1->scene()->removeItem(m_itemL1); delete m_itemL1; } //if (m_itemE0) { // m_itemE0->scene()->removeItem(m_itemE0); // delete m_itemE0; //} //if (m_itemE1) { // m_itemE1->scene()->removeItem(m_itemE1); // delete m_itemE1; //} } void BezierDisplay::initDisplay(QGraphicsItem * master, Bezier *bezier) { //DebugDialog::debug("adding bezier display"); static int activeColor = 0xffffff; static int inactiveColor = 0xb0b0b0; QPen pen; pen.setWidth(0); QGraphicsItem * parent = master; while (parent->parentItem()) { parent = master->parentItem(); } // put the feeback on top double z = parent->zValue() + 100; // (ViewLayer::getZIncrement() / 2) m_itemL0 = new QGraphicsLineItem(); pen.setColor(QColor(bezier->drag0() ? activeColor : inactiveColor)); m_itemL0->setPen(pen); m_itemL0->setPos(0, 0); m_itemL0->setZValue(z); master->scene()->addItem(m_itemL0); //m_itemE0 = new QGraphicsEllipseItem(); //m_itemE0->setPen(pen); //m_itemE0->setPos(0, 0); //m_itemE0->setZValue(z); //master->scene()->addItem(m_itemE0); m_itemL1 = new QGraphicsLineItem(); pen.setColor(QColor(bezier->drag0() == false ? activeColor : inactiveColor)); m_itemL1->setPen(pen); m_itemL1->setPos(0, 0); m_itemL1->setZValue(z); master->scene()->addItem(m_itemL1); //m_itemE1 = new QGraphicsEllipseItem(); //m_itemE1->setPen(pen); //m_itemE1->setPos(0, 0); //m_itemE1->setZValue(z); //master->scene()->addItem(m_itemE1); updateDisplay(master, bezier); ProcessEventBlocker::processEvents(); } void BezierDisplay::updateDisplay(QGraphicsItem * master, Bezier *bezier) { if (m_itemL0 == NULL) return; if (m_itemL1 == NULL) return; //if (m_itemE0 == NULL) return; //if (m_itemE1 == NULL) return; if (bezier == NULL || bezier->isEmpty()) { m_itemL0->setVisible(false); m_itemL1->setVisible(false); //m_itemE0->setVisible(false); //m_itemE1->setVisible(false); return; } //static double minD = 5; //static double radius = 6; //static double minDSqd = minD * minD; QRectF sr = master->scene()->sceneRect(); double x1, y1, x2, y2; QPointF p0 = master->mapToScene(bezier->endpoint0()); QPointF p1 = master->mapToScene(bezier->cp0()); GraphicsUtils::liangBarskyLineClip(p0.x(), p0.y(), p1.x(), p1.y(), sr.left(), sr.right(), sr.top(), sr.bottom(), x1, y1, x2, y2); m_itemL0->setLine(x1, y1, x2, y2); //if (GraphicsUtils::distanceSqd(bezier->endpoint0(), bezier->cp0()) > minDSqd) { //m_itemE0->setVisible(false); //} //else { //QRectF r(master->mapToScene(bezier->endpoint0()), QSizeF(0,0)); //r.adjust(-radius, -radius, radius, radius); //m_itemE0->setRect(r); //m_itemE0->setVisible(true); //} p0 = master->mapToScene(bezier->endpoint1()); p1 = master->mapToScene(bezier->cp1()); GraphicsUtils::liangBarskyLineClip(p0.x(), p0.y(), p1.x(), p1.y(), sr.left(), sr.right(), sr.top(), sr.bottom(), x1, y1, x2, y2); m_itemL1->setLine(x1, y1, x2, y2); //if (GraphicsUtils::distanceSqd(bezier->endpoint1(), bezier->cp1()) > minDSqd) { // m_itemE1->setVisible(false); //} //else { // QRectF r(master->mapToScene(bezier->endpoint1()), QSizeF(0,0)); // r.adjust(-radius, -radius, radius, radius); // m_itemE1->setRect(r); // m_itemE1->setVisible(true); //} m_itemL0->setVisible(true); m_itemL1->setVisible(true); } fritzing-app-0.9.3b/src/utils/bezierdisplay.h000066400000000000000000000026671272363601300212630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef BEZIERDISPLAY_H #define BEZIERDISPLAY_H #include #include #include "bezier.h" class BezierDisplay { public: BezierDisplay(); ~BezierDisplay(); void initDisplay(QGraphicsItem * master, Bezier *); void updateDisplay(QGraphicsItem * master, Bezier *); protected: QGraphicsLineItem * m_itemL0; QGraphicsLineItem * m_itemL1; QGraphicsEllipseItem * m_itemE0; QGraphicsEllipseItem * m_itemE1; }; #endif fritzing-app-0.9.3b/src/utils/boundedregexpvalidator.h000066400000000000000000000043071272363601300231470ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef BOUNDEDREGEXPVALIDATOR_H #define BOUNDEDREGEXPVALIDATOR_H #include #include typedef double (*Converter)(const QString &, const QString & symbol); class BoundedRegExpValidator : public QRegExpValidator { public: BoundedRegExpValidator(QObject * parent) : QRegExpValidator(parent) { m_max = std::numeric_limits::max(); m_min = std::numeric_limits::min(); m_converter = NULL; m_symbol = ""; } void setBounds(double min, double max) { m_min = min; m_max = max; } void setConverter(Converter converter) { m_converter = converter; } void setSymbol(const QString & symbol) { m_symbol = symbol; } QValidator::State validate ( QString & input, int & pos ) const { QValidator::State state = QRegExpValidator::validate(input, pos); if (state == QValidator::Invalid) return state; if (state == QValidator::Intermediate) return state; if (m_converter == NULL) return state; double converted = m_converter(input, m_symbol); if (converted < m_min) return QValidator::Invalid; if (converted > m_max) return QValidator::Invalid; return QValidator::Acceptable; } protected: double m_min; double m_max; QString m_symbol; Converter m_converter; }; #endif fritzing-app-0.9.3b/src/utils/bundler.h000066400000000000000000000006101272363601300200320ustar00rootroot00000000000000 #ifndef BUNDLER_H_ #define BUNDLER_H_ class Bundler { public: virtual ~Bundler() {} virtual bool saveAsAux(const QString &filename) = 0; virtual bool loadBundledAux(QDir &dir, QList mps) {Q_UNUSED(dir); Q_UNUSED(mps); return true;}; virtual bool preloadBundledAux(QDir &dir, bool dontAsk) {Q_UNUSED(dir); Q_UNUSED(dontAsk); return true;}; }; #endif /* BUNDLER_H_ */ fritzing-app-0.9.3b/src/utils/clickablelabel.cpp000066400000000000000000000023221272363601300216450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "clickablelabel.h" ClickableLabel::ClickableLabel(const QString & text, QWidget * parent) : QLabel(text, parent) { } void ClickableLabel::mousePressEvent(QMouseEvent * e) { QLabel::mousePressEvent(e); emit clicked(); } fritzing-app-0.9.3b/src/utils/clickablelabel.h000066400000000000000000000024121272363601300213120ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef CLICKABLELABEL_H #define CLICKABLELABEL_H #include class ClickableLabel : public QLabel { Q_OBJECT public: ClickableLabel(const QString & text = "", QWidget * parent = NULL); protected: void mousePressEvent(QMouseEvent * e); signals: void clicked(); }; #endif fritzing-app-0.9.3b/src/utils/cursormaster.cpp000066400000000000000000000174231272363601300214750ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6141 $: $Author: cohen@irascible.com $: $Date: 2012-07-04 21:20:05 +0200 (Mi, 04. Jul 2012) $ ********************************************************************/ #include "cursormaster.h" #include "../debugdialog.h" #include #include #include #include #include #include #include #include #include #include #include QCursor * CursorMaster::BendpointCursor = NULL; QCursor * CursorMaster::NewBendpointCursor = NULL; QCursor * CursorMaster::MakeWireCursor = NULL; QCursor * CursorMaster::MakeCurveCursor = NULL; QCursor * CursorMaster::RubberbandCursor = NULL; QCursor * CursorMaster::MoveCursor = NULL; QCursor * CursorMaster::BendlegCursor = NULL; QCursor * CursorMaster::RotateCursor = NULL; QCursor * CursorMaster::ScaleCursor = NULL; //static QTimer timer; CursorMaster CursorMaster::TheCursorMaster; static QList Listeners; static QHash CursorItems; static QList Cursors; CursorMaster::CursorMaster() : QObject() { m_blocked = false; } void CursorMaster::cleanup() { foreach (QCursor ** cursor, Cursors) { delete *cursor; } Cursors.clear(); } void CursorMaster::initCursors() { if (BendpointCursor == NULL) { //timer.setSingleShot(true); //timer.setInterval(0); //connect(&timer, SIGNAL(timeout()), &TheCursorMaster, SLOT(moveCursor())); QStringList names; QStringList masks; names << ":resources/images/cursor/bendpoint.bmp" << ":resources/images/cursor/new_bendpoint.bmp" << ":resources/images/cursor/make_wire.bmp" << ":resources/images/cursor/curve.bmp" << ":resources/images/cursor/rubberband_move.bmp" << ":resources/images/cursor/part_move.bmp" << ":resources/images/cursor/bendleg.bmp" << ":resources/images/cursor/rotate.bmp" << ":resources/images/cursor/scale.bmp"; masks << ":resources/images/cursor/bendpoint_mask.bmp" << ":resources/images/cursor/new_bendpoint_mask.bmp" << ":resources/images/cursor/make_wire_mask.bmp" << ":resources/images/cursor/curve_mask.bmp" << ":resources/images/cursor/rubberband_move_mask.bmp" << ":resources/images/cursor/part_move_mask.bmp" << ":resources/images/cursor/bendleg_mask.bmp" << ":resources/images/cursor/rotate_mask.bmp" << ":resources/images/cursor/scale_mask.bmp"; Cursors << &BendpointCursor << &NewBendpointCursor << &MakeWireCursor << &MakeCurveCursor << &RubberbandCursor << &MoveCursor << &BendlegCursor << &RotateCursor << &ScaleCursor; for (int i = 0; i < Cursors.count(); i++) { QBitmap bitmap1(names.at(i)); QBitmap bitmap1m(masks.at(i)); *Cursors.at(i) = new QCursor(bitmap1, bitmap1m, 0, 0); } QApplication::instance()->installEventFilter(instance()); } } CursorMaster * CursorMaster::instance() { return &TheCursorMaster; } void CursorMaster::addCursor(QObject * object, const QCursor & cursor) { if (m_blocked) return; if (object == NULL) return; /* QGraphicsItem * item = dynamic_cast(object); if (item == NULL) return; QGraphicsScene * scene = item->scene(); if (scene == NULL) return; QGraphicsView * view = dynamic_cast(scene->parent()); if (view == NULL) return; QGraphicsPixmapItem * pixmapItem = CursorItems.value(scene, NULL); if (pixmapItem == NULL) { pixmapItem = new QGraphicsPixmapItem(); pixmapItem->setZValue(10000); // always on top pixmapItem->setVisible(true); pixmapItem->setAcceptedMouseButtons(0); pixmapItem->setAcceptDrops(false); pixmapItem->setAcceptTouchEvents(false); pixmapItem->setAcceptHoverEvents(false); pixmapItem->setEnabled(false); pixmapItem->setFlags(QGraphicsItem::ItemIgnoresTransformations); CursorItems.insert(scene, pixmapItem); scene->addItem(pixmapItem); scene->installEventFilter(this); } pixmapItem->setPixmap(*cursor.mask()); pixmapItem->setPos(view->mapToScene(view->mapFromGlobal(QCursor::pos())) + cursor.hotSpot()); */ if (Listeners.contains(object)) { if (Listeners.first() != object) { Listeners.removeOne(object); Listeners.push_front(object); } //DebugDialog::debug(QString("changing cursor %1").arg((long) object)); QApplication::changeOverrideCursor(cursor); return; } Listeners.push_front(object); connect(object, SIGNAL(destroyed(QObject *)), this, SLOT(deleteCursor(QObject *))); QApplication::setOverrideCursor(cursor); //DebugDialog::debug(QString("addding cursor %1").arg((long) object)); } void CursorMaster::removeCursor(QObject * object) { if (object == NULL) return; if (Listeners.contains(object)) { disconnect(object, SIGNAL(destroyed(QObject *)), this, SLOT(deleteCursor(QObject *))); Listeners.removeOne(object); QApplication::restoreOverrideCursor(); //DebugDialog::debug(QString("removing cursor %1").arg((long) object)); } /* QGraphicsItem * item = dynamic_cast(object); if (item == NULL) return; QGraphicsScene * scene = item->scene(); if (scene == NULL) return; QGraphicsPixmapItem * pixmapItem = CursorItems.value(scene, NULL); if (pixmapItem == NULL) return; pixmapItem->hide(); */ } void CursorMaster::deleteCursor(QObject * object) { removeCursor(object); } bool CursorMaster::eventFilter(QObject * object, QEvent * event) { Q_UNUSED(object); //QGraphicsScene * scene = NULL; switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: //scene = dynamic_cast(object); //DebugDialog::debug(QString("event filter %1").arg(object->metaObject()->className())); //if (scene) { QKeyEvent *keyEvent = static_cast(event); foreach (QObject * listener, Listeners) { if (listener) { dynamic_cast(listener)->cursorKeyEvent(keyEvent->modifiers()); break; } } } break; /* case QEvent::GraphicsSceneMouseMove: scene = dynamic_cast(object); if (scene) { QGraphicsPixmapItem * pixmapItem = CursorItems.value(scene, NULL); if (pixmapItem) { timer.setProperty("loc", ((QGraphicsSceneMouseEvent *) event)->scenePos()); timer.setUserData(1, (QObjectUserData *) pixmapItem); } } break; case QEvent::Leave: scene = dynamic_cast(object); if (scene) { QGraphicsPixmapItem * pixmapItem = CursorItems.value(scene, NULL); if (pixmapItem) { //DebugDialog::debug("pos", ((QGraphicsSceneMouseEvent *) event)->scenePos()); pixmapItem->hide(); } } break; */ default: break; } return false; } void CursorMaster::moveCursor() { //QObject * t = sender(); //if (t == NULL) return; //QPointF p = t->property("loc").toPointF(); //QGraphicsPixmapItem * item = (QGraphicsPixmapItem *) t->userData(1); //DebugDialog::debug("move", p); //item->setPos(p); // + cursor->hotspot //item->show(); } void CursorMaster::block() { m_blocked = true; } void CursorMaster::unblock() { m_blocked = false; } fritzing-app-0.9.3b/src/utils/cursormaster.h000066400000000000000000000037711272363601300211430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6141 $: $Author: cohen@irascible.com $: $Date: 2012-07-04 21:20:05 +0200 (Mi, 04. Jul 2012) $ ********************************************************************/ #ifndef CURSORMASTER_H #define CURSORMASTER_H #include #include class CursorKeyListener { public: virtual void cursorKeyEvent(Qt::KeyboardModifiers) = 0; }; class CursorMaster : public QObject { Q_OBJECT protected: CursorMaster(); public: static CursorMaster * instance(); static void initCursors(); void addCursor(QObject * listener, const QCursor & cursor); void removeCursor(QObject * listener); void block(); void unblock(); protected: bool eventFilter(QObject *obj, QEvent *event); protected slots: void deleteCursor(QObject *); void moveCursor(); public: static void cleanup(); public: static QCursor * BendpointCursor; static QCursor * NewBendpointCursor; static QCursor * MakeWireCursor; static QCursor * MakeCurveCursor; static QCursor * RubberbandCursor; static QCursor * MoveCursor; static QCursor * BendlegCursor; static QCursor * RotateCursor; static QCursor * ScaleCursor; protected: static CursorMaster TheCursorMaster; bool m_blocked; }; #endif fritzing-app-0.9.3b/src/utils/expandinglabel.cpp000066400000000000000000000040721272363601300217150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "expandinglabel.h" ExpandingLabel::ExpandingLabel(QWidget *parent, int minSize) : QTextEdit(parent) { setMinimumWidth(minSize); setReadOnly(true); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } void ExpandingLabel::setLabelText(const QString& theText) { document()->setHtml(theText); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setAlignment(Qt::AlignCenter); setContextMenuPolicy(Qt::NoContextMenu); } void ExpandingLabel::allTextVisible() { QTextDocument *doc = document(); doc->setTextWidth(width()); int height = doc->documentLayout()->documentSize().toSize().height(); setFixedHeight(height); } void ExpandingLabel::mouseMoveEvent(QMouseEvent * event) { QAbstractScrollArea::mouseMoveEvent(event); } void ExpandingLabel::mousePressEvent(QMouseEvent *event) { emit mousePressSignal(event); QAbstractScrollArea::mousePressEvent(event); } void ExpandingLabel::mouseReleaseEvent(QMouseEvent *event) { emit mouseReleaseSignal(event); QAbstractScrollArea::mouseReleaseEvent(event); } fritzing-app-0.9.3b/src/utils/expandinglabel.h000066400000000000000000000031471272363601300213640ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef EXPANDINGLABEL_H_ #define EXPANDINGLABEL_H_ #include #include #include #include "../debugdialog.h" class ExpandingLabel : public QTextEdit { Q_OBJECT public: ExpandingLabel(QWidget *parent, int minSize=100); void setLabelText(const QString& theText); public slots: void allTextVisible(); signals: void mousePressSignal(QMouseEvent *event); void mouseReleaseSignal(QMouseEvent *event); protected: void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); }; #endif /* EXPANDINGLABEL_H_ */ fritzing-app-0.9.3b/src/utils/familypropertycombobox.h000066400000000000000000000033351272363601300232250ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FAMILYPROPERTYCOMBOBOX_H #define FAMILYPROPERTYCOMBOBOX_H #include #include "../debugdialog.h" #include "focusoutcombobox.h" class FamilyPropertyComboBox : public FocusOutComboBox { Q_OBJECT public: FamilyPropertyComboBox(const QString & family, const QString & prop, QWidget * parent = 0) : FocusOutComboBox(parent) { m_family = family; m_prop = prop; setEditable(false); } ~FamilyPropertyComboBox() { } void hidePopup() { //DebugDialog::debug(QString("hide popup %1").arg((long) this, 0, 16)); QComboBox::hidePopup(); } const QString & prop() { return m_prop; } const QString & family() { return m_family; } protected: QString m_family; QString m_prop; }; #endif fritzing-app-0.9.3b/src/utils/fileprogressdialog.cpp000066400000000000000000000142771272363601300226340ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "fileprogressdialog.h" #include "../debugdialog.h" #include "../processeventblocker.h" #include #include #include #include #include #include #include #include #include #include #include #include ///////////////////////////////////// FileProgressDialog::FileProgressDialog(const QString & title, int initialMaximum, QWidget * parent) : QDialog(parent) { m_incValueMod = 2; QSplashScreen *splash = NULL; foreach (QWidget *widget, QApplication::topLevelWidgets()) { splash = qobject_cast(widget); if (splash) { break; } } init(title, initialMaximum); setModal(splash == NULL); // OS X Lion doesn't seem to like modal dialogs during splash time show(); if (splash) { QRect sr = splash->geometry(); QRect r = this->geometry(); QRect fr = this->frameGeometry(); int offset = sr.bottom() - r.top() + (fr.height() - r.height()); r.adjust(0, offset, 0, offset); this->setGeometry(r); } ProcessEventBlocker::processEvents(); } FileProgressDialog::FileProgressDialog(QWidget *parent) : QDialog(parent) { init(QObject::tr("File Progress..."), 0); } FileProgressDialog::~FileProgressDialog() { m_timer.stop(); } void FileProgressDialog::init(const QString & title, int initialMaximum) { Qt::WindowFlags flags = windowFlags(); flags |= Qt::CustomizeWindowHint; flags ^= Qt::WindowCloseButtonHint; flags ^= Qt::WindowSystemMenuHint; flags ^= Qt::WindowContextHelpButtonHint; //flags ^= Qt::WindowTitleHint; setWindowFlags(flags); this->setWindowTitle(title); QVBoxLayout * vLayout = new QVBoxLayout(this); m_message = new QLabel(this); m_message->setMinimumWidth(300); vLayout->addWidget(m_message); m_progressBar = new QProgressBar(this); m_progressBar->setMinimum(0); m_progressBar->setMaximum(initialMaximum); vLayout->addWidget(m_progressBar); //QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); //buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); //buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); //connect(buttonBox, SIGNAL(rejected()), this, SLOT(sendCancel())); //vLayout->addWidget(buttonBox); this->setLayout(vLayout); } void FileProgressDialog::setMinimum(int minimum) { m_progressBar->setMinimum(minimum); } void FileProgressDialog::setMaximum(int maximum) { m_progressBar->setMaximum(maximum); //DebugDialog::debug(QString("set maximum:%1").arg(maximum)); } void FileProgressDialog::addMaximum(int maximum) { if (maximum != 0) { m_progressBar->setMaximum(m_progressBar->maximum() + maximum); //DebugDialog::debug(QString("set maximum:%1").arg(maximum)); } } void FileProgressDialog::setValue(int value) { m_progressBar->setValue(value); ProcessEventBlocker::processEvents(); } void FileProgressDialog::incValue() { m_progressBar->setValue(m_progressBar->value() + 1); if (m_progressBar->value() % m_incValueMod == 0) { ProcessEventBlocker::processEvents(); } } int FileProgressDialog::value() { return m_progressBar->value(); } void FileProgressDialog::sendCancel() { emit cancel(); } void FileProgressDialog::closeEvent(QCloseEvent *event) { event->ignore(); } void FileProgressDialog::setMessage(const QString & message) { m_message->setText(message); ProcessEventBlocker::processEvents(); } void FileProgressDialog::setBinLoadingCount(int count) { m_binLoadingCount = count; m_binLoadingIndex = -1; m_binLoadingStart = value(); } void FileProgressDialog::setBinLoadingChunk(int chunk) { m_binLoadingChunk = chunk; } void FileProgressDialog::loadingInstancesSlot(class ModelBase *, QDomElement & instances) { m_binLoadingValue = m_binLoadingStart + (++m_binLoadingIndex * m_binLoadingChunk / (double) m_binLoadingCount); setValue(m_binLoadingValue); int count = instances.childNodes().count(); // * 3 comes from: once for model part load, once for list view, once for icon view m_binLoadingInc = m_binLoadingChunk / (double) (m_binLoadingCount * 3 * count); } void FileProgressDialog::loadingInstanceSlot(class ModelBase *, QDomElement & instance) { Q_UNUSED(instance); //QString text; //QTextStream textStream(&text); //instance.save(textStream, 0); //DebugDialog::debug(QString("loading %1").arg(text)); //DebugDialog::debug(QString("loading %1").arg(instance.attribute("path"))); settingItemSlot(); } void FileProgressDialog::settingItemSlot() { m_binLoadingValue += m_binLoadingInc; if ((int) m_binLoadingValue > value()) { setValue(m_binLoadingValue); } } void FileProgressDialog::resizeEvent(QResizeEvent * event) { QDialog::resizeEvent(event); QRect scr = QApplication::desktop()->screenGeometry(); move( scr.center() - rect().center() ); } void FileProgressDialog::setIncValueMod(int mod) { m_incValueMod = mod; } void FileProgressDialog::setIndeterminate() { m_progressBar->setRange(0, 0); m_timer.setSingleShot(false); m_timer.setInterval(1000); connect(&m_timer, SIGNAL(timeout()), this, SLOT(updateIndeterminate())); m_timer.start(); } void FileProgressDialog::updateIndeterminate() { if (m_progressBar) { m_progressBar->setValue(0); } } fritzing-app-0.9.3b/src/utils/fileprogressdialog.h000066400000000000000000000044771272363601300223020ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FILEPROGRESSDIALOG_H #define FILEPROGRESSDIALOG_H #include #include #include #include #include class FileProgressDialog : public QDialog { Q_OBJECT public: FileProgressDialog(QWidget *parent = 0); FileProgressDialog(const QString & title, int initialMaximum, QWidget *parent); ~FileProgressDialog(); int value(); void setBinLoadingCount(int); void setBinLoadingChunk(int); void setIncValueMod(int); void setIndeterminate(); protected: void closeEvent(QCloseEvent *); void resizeEvent(QResizeEvent *); public slots: void setMinimum(int); void setMaximum(int); void addMaximum(int); void setValue(int); void incValue(); void setMessage(const QString & message); void sendCancel(); void loadingInstancesSlot(class ModelBase *, QDomElement & instances); void loadingInstanceSlot(class ModelBase *, QDomElement & instance); void settingItemSlot(); protected slots: void updateIndeterminate(); signals: void cancel(); protected: void init(const QString & title, int initialMaximum); protected: QProgressBar * m_progressBar; QLabel * m_message; int m_binLoadingCount; int m_binLoadingIndex; int m_binLoadingStart; int m_binLoadingChunk; int m_incValueMod; double m_binLoadingInc; double m_binLoadingValue; QTimer m_timer; }; #endif fritzing-app-0.9.3b/src/utils/flineedit.cpp000066400000000000000000000033211272363601300206770ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "flineedit.h" FLineEdit::FLineEdit(QWidget * parent) : QLineEdit(parent) { editingFinishedSlot(); connect(this, SIGNAL(editingFinished()), this, SLOT(editingFinishedSlot())); } FLineEdit::~FLineEdit() { } void FLineEdit::editingFinishedSlot() { m_readOnly = true; emit editable(false); setCursor(Qt::IBeamCursor); } void FLineEdit::mousePressEvent ( QMouseEvent * event ) { if (m_readOnly) { m_readOnly = false; emit editable(true); } QLineEdit::mousePressEvent(event); } void FLineEdit::enterEvent(QEvent * event) { QLineEdit::enterEvent(event); if (m_readOnly) { emit mouseEnter(); } } void FLineEdit::leaveEvent(QEvent * event) { QLineEdit::leaveEvent(event); if (m_readOnly) { emit mouseLeave(); } } fritzing-app-0.9.3b/src/utils/flineedit.h000066400000000000000000000027661272363601300203600ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FLINEEDIT_H #define FLINEEDIT_H #include #include class FLineEdit : public QLineEdit { Q_OBJECT public: FLineEdit(QWidget * parent = NULL); ~FLineEdit(); protected: void mousePressEvent( QMouseEvent * event ); void enterEvent( QEvent *); void leaveEvent( QEvent *); signals: void mouseEnter(); void mouseLeave(); void editable(bool); protected slots: void editingFinishedSlot(); protected: bool m_readOnly; // was using readOnly() but mac didn't like it }; #endif fritzing-app-0.9.3b/src/utils/fmessagebox.cpp000066400000000000000000000055321272363601300212450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "fmessagebox.h" #include "../debugdialog.h" bool FMessageBox::BlockMessages = false; FMessageBox::FMessageBox(QWidget * parent) : QMessageBox(parent) { } int FMessageBox::exec() { if (BlockMessages) return QMessageBox::Cancel; return QMessageBox::exec(); } QMessageBox::StandardButton FMessageBox::critical( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons, StandardButton defaultButton) { if (BlockMessages) { DebugDialog::debug("critcal " + title); DebugDialog::debug(text); return QMessageBox::Cancel; } return QMessageBox::critical(parent, title, text, buttons, defaultButton); } QMessageBox::StandardButton FMessageBox::information( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons, StandardButton defaultButton) { if (BlockMessages) { DebugDialog::debug("information " + title); DebugDialog::debug(text); return QMessageBox::Cancel; } return QMessageBox::information(parent, title, text, buttons, defaultButton); } QMessageBox::StandardButton FMessageBox::question( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons, StandardButton defaultButton) { if (BlockMessages) { DebugDialog::debug("question " + title); DebugDialog::debug(text); return QMessageBox::Cancel; } return QMessageBox::question(parent, title, text, buttons, defaultButton); } QMessageBox::StandardButton FMessageBox::warning( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons, StandardButton defaultButton) { if (BlockMessages) { DebugDialog::debug("warning " + title); DebugDialog::debug(text); return QMessageBox::Cancel; } return QMessageBox::warning(parent, title, text, buttons, defaultButton); } fritzing-app-0.9.3b/src/utils/fmessagebox.h000066400000000000000000000037161272363601300207140ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FMESSAGEBOX_H #define FMESSAGEBOX_H #include class FMessageBox : public QMessageBox { Q_OBJECT public: FMessageBox ( QWidget * parent = 0 ); int exec (); public: static QMessageBox::StandardButton critical ( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); static QMessageBox::StandardButton information ( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); static QMessageBox::StandardButton question ( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); static QMessageBox::StandardButton warning ( QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); public: static bool BlockMessages; }; #endif fritzing-app-0.9.3b/src/utils/focusoutcombobox.cpp000066400000000000000000000046431272363601300223440ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "focusoutcombobox.h" #include "../debugdialog.h" FocusOutComboBox::FocusOutComboBox(QWidget * parent) : QComboBox(parent) { setEditable(true); m_wasOut = true; lineEdit()->installEventFilter( this ); } FocusOutComboBox::~FocusOutComboBox() { } void FocusOutComboBox::focusInEvent(QFocusEvent * e) { //DebugDialog::debug("focus in"); QComboBox::focusInEvent(e); checkSelectAll(); } void FocusOutComboBox::focusOutEvent(QFocusEvent * e) { //DebugDialog::debug("focus out"); m_wasOut = true; QComboBox::focusOutEvent(e); QString t = this->currentText(); QString it = this->itemText(this->currentIndex()); if (t.compare(it) != 0) { int ix = findText(t); if (ix == -1) { addItem(t); ix = count() - 1; } setCurrentIndex(ix); } } bool FocusOutComboBox::eventFilter( QObject *target, QEvent *event ) { // subclassing mouseReleaseEvent doesn't seem to work so use eventfilter instead if( target == lineEdit() && event->type() == QEvent::MouseButtonRelease ) { if (m_wasOut) { // only select all the first time the focused lineEdit is clicked, not every time, // otherwise you can't move the selection point with the mouse checkSelectAll(); m_wasOut = false; } } return false; } void FocusOutComboBox::checkSelectAll() { if(lineEdit() && !lineEdit()->hasSelectedText() && isEnabled()) { lineEdit()->selectAll(); } } fritzing-app-0.9.3b/src/utils/focusoutcombobox.h000066400000000000000000000027221272363601300220050ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FOCUSOUTCOMBOBOX_H #define FOCUSOUTCOMBOBOX_H #include #include #include #include class FocusOutComboBox : public QComboBox { Q_OBJECT public: FocusOutComboBox(QWidget * parent = NULL); ~FocusOutComboBox(); protected: void focusInEvent(QFocusEvent *); void focusOutEvent(QFocusEvent *); bool eventFilter( QObject *target, QEvent *event ); void checkSelectAll(); protected: bool m_wasOut; }; #endif fritzing-app-0.9.3b/src/utils/folderutils.cpp000066400000000000000000000560771272363601300213100ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 7004 $: $Author: irascibl@gmail.com $: $Date: 2013-04-29 13:10:59 +0200 (Mo, 29. Apr 2013) $ ********************************************************************/ #include "folderutils.h" #include "lockmanager.h" #include "textutils.h" #include "fmessagebox.h" #include #include #include #include #include #include #include #include #include #include #include "../debugdialog.h" #ifdef QUAZIP_INSTALLED #include #include #else #include "../lib/quazip/quazip.h" #include "../lib/quazip/quazipfile.h" #endif #include "../lib/qtsysteminfo/QtSystemInfo.h" FolderUtils* FolderUtils::singleton = NULL; QString FolderUtils::m_openSaveFolder = ""; FolderUtils::FolderUtils() { m_openSaveFolder = ___emptyString___; m_userFolders << "partfactory" << "backup" << "fzz"; m_documentFolders << "bins" << "parts/user" << "parts/contrib" << "parts/svg/user/icon" << "parts/svg/user/breadboard" << "parts/svg/user/schematic" << "parts/svg/user/pcb" << "parts/svg/contrib/icon" << "parts/svg/contrib/breadboard" << "parts/svg/contrib/schematic" << "parts/svg/contrib/pcb"; } FolderUtils::~FolderUtils() { } // finds a subfolder of the application directory searching backward up the tree QDir FolderUtils::getApplicationSubFolder(QString search) { if (singleton == NULL) { singleton = new FolderUtils(); } QString path = singleton->applicationDirPath(); path += "/" + search; //DebugDialog::debug(QString("path %1").arg(path) ); QDir dir(path); while (!dir.exists()) { // if we're running from the debug or release folder, go up one to find things dir.cdUp(); dir.cdUp(); if (dir.isRoot()) return QDir(); // didn't find the search folder dir.setPath(dir.absolutePath() + "/" + search); } return dir; } QString FolderUtils::getApplicationSubFolderPath(QString search) { if (singleton == NULL) { singleton = new FolderUtils(); } QDir dir = getApplicationSubFolder(search); return dir.path(); } QString FolderUtils::getAppPartsSubFolderPath(QString search) { if (singleton == NULL) { singleton = new FolderUtils(); } QDir dir = getAppPartsSubFolder(search); return dir.path(); } QDir FolderUtils::getAppPartsSubFolder(QString search) { if (singleton == NULL) { singleton = new FolderUtils(); } return singleton->getAppPartsSubFolder2(search); } QDir FolderUtils::getAppPartsSubFolder2(QString search) { if (m_partsPath.isEmpty()) { QDir dir = getApplicationSubFolder("fritzing-parts"); if (dir.exists()) { m_partsPath = dir.absolutePath(); } else { QDir dir = getApplicationSubFolder("parts"); if (dir.exists()) { m_partsPath = dir.absolutePath(); } } } QString path = search.isEmpty() ? m_partsPath : m_partsPath + "/" + search; //DebugDialog::debug(QString("path %1").arg(path) ); QDir dir(path); return dir; } QString FolderUtils::getTopLevelUserDataStorePath() { QString path = QSettings(QSettings::IniFormat,QSettings::UserScope,"Fritzing","Fritzing").fileName(); return QFileInfo(path).dir().absolutePath(); } QString FolderUtils::getTopLevelDocumentsPath() { // must add a fritzing subfolder QDir dir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); return dir.absoluteFilePath("Fritzing"); } QString FolderUtils::getUserBinsPath() { QDir dir(getTopLevelDocumentsPath()); return dir.absoluteFilePath("bins"); } QString FolderUtils::getUserPartsPath() { QDir dir(getTopLevelDocumentsPath()); return dir.absoluteFilePath("parts"); } bool FolderUtils::createFolderAndCdIntoIt(QDir &dir, QString newFolder) { if(!dir.mkdir(newFolder)) return false; if(!dir.cd(newFolder)) return false; return true; } bool FolderUtils::setApplicationPath(const QString & path) { if (singleton == NULL) { singleton = new FolderUtils(); } return singleton->setApplicationPath2(path); } bool FolderUtils::setAppPartsPath(const QString & path) { if (singleton == NULL) { singleton = new FolderUtils(); } return singleton->setPartsPath2(path); } void FolderUtils::cleanup() { if (singleton) { delete singleton; singleton = NULL; } } const QString FolderUtils::getLibraryPath() { if (singleton == NULL) { singleton = new FolderUtils(); } return singleton->libraryPath(); } const QString FolderUtils::libraryPath() { #ifdef Q_OS_MAC // mac plugins are always in the bundle return QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../lib"); #endif return QDir::cleanPath(applicationDirPath() + "/lib"); } const QString FolderUtils::applicationDirPath() { if (m_appPath.isEmpty()) { #ifdef Q_OS_WIN m_appPath = QCoreApplication::applicationDirPath(); #else // look in standard Fritzing location (applicationDirPath and parent folders) then in standard linux locations QStringList candidates; candidates.append(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath()); if (dir.cdUp()) { candidates.append(dir.absolutePath()); if (dir.cdUp()) { candidates.append(dir.absolutePath()); if (dir.cdUp()) { candidates.append(dir.absolutePath()); } } } #ifdef PKGDATADIR candidates.append(QLatin1String(PKGDATADIR)); #else candidates.append("/usr/share/fritzing"); candidates.append("/usr/local/share/fritzing"); #endif candidates.append(QDir::homePath() + "/.local/share/fritzing"); foreach (QString candidate, candidates) { //DebugDialog::debug(QString("candidate:%1").arg(candidate)); QDir dir(candidate); if (!dir.exists("translations")) continue; if (dir.exists("help")) { m_appPath = candidate; return m_appPath; } } m_appPath = QCoreApplication::applicationDirPath(); DebugDialog::debug("data folders not found"); #endif } return m_appPath; } bool FolderUtils::setApplicationPath2(const QString & path) { QDir dir(path); if (!dir.exists()) return false; m_appPath = path; return true; } bool FolderUtils::setPartsPath2(const QString & path) { QDir dir(path); if (!dir.exists()) return false; m_partsPath = path; return true; } void FolderUtils::setOpenSaveFolder(const QString& path) { setOpenSaveFolderAux(path); QSettings settings; settings.setValue("openSaveFolder", m_openSaveFolder); } void FolderUtils::setOpenSaveFolderAux(const QString& path) { QFileInfo fileInfo(path); if(fileInfo.isDir()) { m_openSaveFolder = path; } else { m_openSaveFolder = fileInfo.path().remove(fileInfo.fileName()); } } const QString FolderUtils::openSaveFolder() { if(m_openSaveFolder.isEmpty()) { QSettings settings; QString tempFolder = settings.value("openSaveFolder").toString(); if (!tempFolder.isEmpty()) { QFileInfo fileInfo(tempFolder); if (fileInfo.exists()) { m_openSaveFolder = tempFolder; return m_openSaveFolder; } else { settings.remove("openSaveFolder"); } } return getTopLevelDocumentsPath(); } else { return m_openSaveFolder; } } QString FolderUtils::getOpenFileName( QWidget * parent, const QString & caption, const QString & dir, const QString & filter, QString * selectedFilter, QFileDialog::Options options ) { QString result = QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options); if (!result.isNull()) { setOpenSaveFolder(result); } return result; } QStringList FolderUtils::getOpenFileNames( QWidget * parent, const QString & caption, const QString & dir, const QString & filter, QString * selectedFilter, QFileDialog::Options options ) { QStringList result = QFileDialog::getOpenFileNames(parent, caption, dir, filter, selectedFilter, options); if (result.count() > 0) { setOpenSaveFolder(result.at(0)); } return result; } QString FolderUtils::getSaveFileName( QWidget * parent, const QString & caption, const QString & dir, const QString & filter, QString * selectedFilter, QFileDialog::Options options ) { //DebugDialog::debug(QString("getopenfilename %1 %2 %3 %4").arg(caption).arg(dir).arg(filter).arg(*selectedFilter)); QString result = QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options); if (!result.isNull()) { setOpenSaveFolder(result); } return result; } bool FolderUtils::isEmptyFileName(const QString &fileName, const QString &untitledFileName) { return (fileName.isEmpty() || fileName.isNull() || fileName.startsWith(untitledFileName)); } void FolderUtils::replicateDir(QDir srcDir, QDir targDir) { // copy all files from srcDir source to targDir QStringList files = srcDir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); for(int i=0; i < files.size(); i++) { QFile tempFile(srcDir.path() + "/" +files.at(i)); DebugDialog::debug(QObject::tr("Copying file %1").arg(tempFile.fileName())); QFileInfo fi(files.at(i)); QString newFilePath = targDir.path() + "/" + fi.fileName(); if(QFileInfo(tempFile.fileName()).isDir()) { QDir newTargDir = QDir(newFilePath); newTargDir.mkpath(newTargDir.absolutePath()); newTargDir.cd(files.at(i)); replicateDir(QDir(tempFile.fileName()),newTargDir); } else { if(!tempFile.copy(newFilePath)) { DebugDialog::debug(QObject::tr("File %1 already exists: it won't be overwritten").arg(newFilePath)); } } } } // NOTE: This function cannot remove directories that have non-empty name filters set on it. void FolderUtils::rmdir(const QString &dirPath) { QDir dir = QDir(dirPath); rmdir(dir); } void FolderUtils::rmdir(QDir & dir) { //DebugDialog::debug(QString("removing folder: %1").arg(dir.path())); QStringList files = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); for(int i=0; i < files.size(); i++) { QFile tempFile(dir.path() + "/" +files.at(i)); //DebugDialog::debug(QString("removing from original folder: %1").arg(tempFile.fileName())); if(QFileInfo(tempFile.fileName()).isDir()) { QDir dir = QDir(tempFile.fileName()); rmdir(dir); } else { tempFile.remove(tempFile.fileName()); } } dir.rmdir(dir.path()); } bool FolderUtils::createFZAndSaveTo(const QDir &dirToCompress, const QString &filepath, const QStringList & skipSuffixes) { DebugDialog::debug("saveASfz "+dirToCompress.path()+" into "+filepath); QFileInfoList files=dirToCompress.entryInfoList(); QFile inFile; char c; QString currFolderBU = QDir::currentPath(); QDir::setCurrent(dirToCompress.path()); foreach(QFileInfo file, files) { if(!file.isFile()||file.fileName()==filepath) continue; if (file.fileName().contains(LockManager::LockedFileName)) continue; bool skip = false; foreach (QString suffix, skipSuffixes) { if (file.fileName().endsWith(suffix)) { skip = true; break; } } if (skip) continue; inFile.setFileName(file.fileName()); if(!inFile.open(QIODevice::ReadOnly)) { qWarning("inFile.open(): %s", inFile.errorString().toLocal8Bit().constData()); return false; } QString destination = QFileInfo(filepath).dir().filePath(inFile.fileName()); if (QFileInfo(destination).exists()) QFile::remove(destination); DebugDialog::debug("Destination " + destination); inFile.copy(destination); inFile.close(); } QDir::setCurrent(currFolderBU); return true; } bool FolderUtils::createZipAndSaveTo(const QDir &dirToCompress, const QString &filepath, const QStringList & skipSuffixes) { DebugDialog::debug("zipping "+dirToCompress.path()+" into "+filepath); QString tempZipFile = QDir::temp().path()+"/"+TextUtils::getRandText()+".zip"; DebugDialog::debug("temp file: "+tempZipFile); QuaZip zip(tempZipFile); if(!zip.open(QuaZip::mdCreate)) { qWarning("zip.open(): %d", zip.getZipError()); return false; } QFileInfoList files=dirToCompress.entryInfoList(); QFile inFile; QuaZipFile outFile(&zip); char c; QString currFolderBU = QDir::currentPath(); QDir::setCurrent(dirToCompress.path()); foreach(QFileInfo file, files) { if(!file.isFile()||file.fileName()==filepath) continue; if (file.fileName().contains(LockManager::LockedFileName)) continue; bool skip = false; foreach (QString suffix, skipSuffixes) { if (file.fileName().endsWith(suffix)) { skip = true; break; } } if (skip) continue; //#pragma message("remove fzz check") //if (file.fileName().endsWith(".fzz")) continue; inFile.setFileName(file.fileName()); if(!inFile.open(QIODevice::ReadOnly)) { qWarning("inFile.open(): %s", inFile.errorString().toLocal8Bit().constData()); return false; } if(!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(inFile.fileName(), inFile.fileName()))) { qWarning("outFile.open(): %d", outFile.getZipError()); return false; } while(inFile.getChar(&c)&&outFile.putChar(c)){} if(outFile.getZipError()!=UNZ_OK) { qWarning("outFile.putChar(): %d", outFile.getZipError()); return false; } outFile.close(); if(outFile.getZipError()!=UNZ_OK) { qWarning("outFile.close(): %d", outFile.getZipError()); return false; } inFile.close(); } zip.close(); QDir::setCurrent(currFolderBU); if(QFileInfo(filepath).exists()) { // if we're here the usr has already accepted to overwrite QFile::remove(filepath); } QFile file(tempZipFile); FolderUtils::slamCopy(file, filepath); file.remove(); if(zip.getZipError()!=0) { qWarning("zip.close(): %d", zip.getZipError()); return false; } return true; } bool FolderUtils::unzipTo(const QString &filepath, const QString &dirToDecompress, QString & error) { static QChar badCharacters[] = { '\\', '/', ':', '*', '?', '"', '<', '>', '|' }; static QChar underscore('_'); QuaZip zip(filepath); if(!zip.open(QuaZip::mdUnzip)) { error = QString("zip.open(): %d").arg(zip.getZipError()); DebugDialog::debug(error); return false; } zip.setFileNameCodec("IBM866"); DebugDialog::debug(QString("unzipping %1 entries from %2").arg(zip.getEntriesCount()).arg(filepath)); QuaZipFileInfo info; QuaZipFile file(&zip); QFile out; QString name; char c; for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { if(!zip.getCurrentFileInfo(&info)) { error = QString("getCurrentFileInfo(): %d\n").arg(zip.getZipError()); DebugDialog::debug(error); return false; } if(!file.open(QIODevice::ReadOnly)) { error = QString("file.open(): %d").arg(file.getZipError()); DebugDialog::debug(error); return false; } name=file.getActualFileName(); if(file.getZipError()!=UNZ_OK) { error = QString("file.getFileName(): %d").arg(file.getZipError()); DebugDialog::debug(error); return false; } out.setFileName(dirToDecompress+"/"+name); // this will fail if "name" contains subdirectories, but we don't mind that if(!out.open(QIODevice::WriteOnly)) { for (int i = 0; i < name.length(); i++) { if (name[i].unicode() < 32) { name.replace(i, 1, &underscore, 1); } else for (unsigned int j = 0; j < (sizeof(badCharacters) / sizeof(QChar)); j++) { if (name[i] == badCharacters[j]) { name.replace(i, 1, &underscore, 1); break; } } } out.setFileName(dirToDecompress+"/"+name); if(!out.open(QIODevice::WriteOnly)) { error = QString("out.open(): %s").arg(out.errorString().toLocal8Bit().constData()); DebugDialog::debug(error); return false; } } // Slow like hell (on GNU/Linux at least), but it is not my fault. // Not ZIP/UNZIP package's fault either. // The slowest thing here is out.putChar(c). // TODO: now that out.putChar has been replaced with a buffered write, is it still slow under Linux? #define BUFFERSIZE 1024 char buffer[BUFFERSIZE]; int ix = 0; while(file.getChar(&c)) { buffer[ix++] = c; if (ix == BUFFERSIZE) { out.write(buffer, ix); ix = 0; } } if (ix > 0) { out.write(buffer, ix); } out.close(); if(file.getZipError()!=UNZ_OK) { error = QString("file.getFileName(): %d").arg(file.getZipError()); DebugDialog::debug(error); return false; } if(!file.atEnd()) { error = "read all but not EOF"; DebugDialog::debug(error); return false; } file.close(); if(file.getZipError()!=UNZ_OK) { error = QString("file.close(): %d").arg(file.getZipError()); DebugDialog::debug(error); return false; } } zip.close(); if(zip.getZipError()!=UNZ_OK) { error = QString("zip.close(): %d").arg(zip.getZipError()); DebugDialog::debug(error); return false; } return true; } void FolderUtils::collectFiles(const QDir & parent, QStringList & filters, QStringList & files, bool recursive) { QFileInfoList fileInfoList = parent.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks); foreach (QFileInfo fileInfo, fileInfoList) { files.append(fileInfo.absoluteFilePath()); } if (recursive) { QFileInfoList dirList = parent.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks); foreach (QFileInfo dirInfo, dirList) { QDir dir(dirInfo.filePath()); //DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath())); collectFiles(dir, filters, files, recursive); } } } void FolderUtils::makePartFolderHierarchy(const QString & prefixFolder, const QString & destFolder) { QDir dir(prefixFolder); dir.mkdir(destFolder); dir.mkdir("svg"); dir.cd("svg"); dir.mkdir(destFolder); dir.cd(destFolder); dir.mkdir("icon"); dir.mkdir("breadboard"); dir.mkdir("schematic"); dir.mkdir("pcb"); } void FolderUtils::copyBin(const QString & dest, const QString & source) { if(QFileInfo(dest).exists()) return; // this copy action, is not working on windows, because is a resources file if(!QFile(source).copy(dest)) { #ifdef Q_OS_WIN // may not be needed from qt 4.5.2 on DebugDialog::debug("Failed to copy a file from the resources"); QDir binsFolder = QFileInfo(dest).dir().absolutePath(); QStringList binFiles = binsFolder.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); foreach(QString binName, binFiles) { if(binName.startsWith("qt_temp.")) { QString filePath = binsFolder.absoluteFilePath(binName); bool success = QFile(filePath).rename(dest); Q_UNUSED(success); break; } } #endif } QFlags ps = QFile::permissions(dest); QFile::setPermissions( dest, QFile::WriteOwner | QFile::WriteUser | ps #ifdef Q_OS_WIN | QFile::WriteOther | QFile::WriteGroup #endif ); } bool FolderUtils::slamCopy(QFile & file, const QString & dest) { QFileInfo info(file); if (info.absoluteFilePath() == dest) { // source = dest return true; } bool result = file.copy(dest); if (result) return result; file.remove(dest); return file.copy(dest); } void FolderUtils::showInFolder(const QString & path) { // http://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt // http://stackoverflow.com/questions/9581330/change-selection-in-explorer-window // Mac, Windows support folder or file. #if defined(Q_OS_WIN) const QString explorer = "explorer.exe"; QString param = QLatin1String("/e,/select,"); param += QDir::toNativeSeparators(path); QProcess::startDetached(explorer, QStringList(param)); #elif defined(Q_OS_MAC) QStringList scriptArgs; scriptArgs << QLatin1String("-e") << QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"") .arg(path); QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs); scriptArgs.clear(); scriptArgs << QLatin1String("-e") << QLatin1String("tell application \"Finder\" to activate"); QProcess::execute("/usr/bin/osascript", scriptArgs); #else QDesktopServices::openUrl( QUrl::fromLocalFile( QFileInfo(path).absolutePath() ) ); #endif } void FolderUtils::createUserDataStoreFolders() { // make sure that the folder structure for parts and bins, exists if (singleton == NULL) { singleton = new FolderUtils(); } QDir userDataStore(getTopLevelUserDataStorePath()); foreach(QString folder, singleton->m_userFolders) { QString path = userDataStore.absoluteFilePath(folder); if(!QFileInfo(path).exists()) { userDataStore.mkpath(folder); } } QDir documents(getTopLevelDocumentsPath()); QStringList documentFolders(singleton->m_documentFolders); foreach(QString folder, documentFolders) { QString path = documents.absoluteFilePath(folder); if(!QFileInfo(path).exists()) { documents.mkpath(folder); } } // in older versions of Fritzing, local parts and bins were in userDataStore QList toRemove; QStringList folders; folders << "bins" << "parts"; bool foundOld = false; foreach(QString folder, folders ) { foundOld || QFileInfo(userDataStore.absoluteFilePath(folder)).exists(); } if (foundOld) { // inform user about the move FMessageBox::information(NULL, QCoreApplication::translate("FolderUtils", "Moving your custom parts"), QCoreApplication::translate("FolderUtils", "

    Your custom-made parts and bins are moved from the hidden " "app data folder to your fritzing documents folder at

    %1

    " "This way we hope to make it easier for you to find and edit them manually.

    ") .arg(documents.absolutePath())); // copy these into the new locations foreach(QString folder, folders ) { QDir source(userDataStore.absoluteFilePath(folder)); QDir target(documents.absoluteFilePath(folder)); if (source.exists()) { replicateDir(source, target); toRemove << source; } } // now remove the obsolete locations foreach (QDir dir, toRemove) { rmdir(dir); } } } fritzing-app-0.9.3b/src/utils/folderutils.h000066400000000000000000000100531272363601300207350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef FOLDERUTILS_H #define FOLDERUTILS_H #include #include #include #include #include "misc.h" class FolderUtils { public: static QDir getApplicationSubFolder(QString); static QString getApplicationSubFolderPath(QString); static QDir getAppPartsSubFolder(QString); static QString getAppPartsSubFolderPath(QString); static QString getTopLevelUserDataStorePath(); static QString getTopLevelDocumentsPath(); static QString getUserBinsPath(); static QString getUserPartsPath(); static bool createFolderAndCdIntoIt(QDir &dir, QString newFolder); static bool setApplicationPath(const QString & path); static bool setAppPartsPath(const QString & path); static const QString getLibraryPath(); static QString getOpenFileName( QWidget * parent = 0, const QString & caption = QString(), const QString & dir = QString(), const QString & filter = QString(), QString * selectedFilter = 0, QFileDialog::Options options = 0 ); static QStringList getOpenFileNames( QWidget * parent = 0, const QString & caption = QString(), const QString & dir = QString(), const QString & filter = QString(), QString * selectedFilter = 0, QFileDialog::Options options = 0 ); static QString getSaveFileName( QWidget * parent = 0, const QString & caption = QString(), const QString & dir = QString(), const QString & filter = QString(), QString * selectedFilter = 0, QFileDialog::Options options = 0 ); static void setOpenSaveFolder(const QString& path); static void setOpenSaveFolderAux(const QString& path); static const QString openSaveFolder(); static bool isEmptyFileName(const QString &filename, const QString &unsavedFilename); static void rmdir(const QString &dirPath); static void rmdir(QDir & dir); static bool createZipAndSaveTo(const QDir &dirToCompress, const QString &filename, const QStringList & skipSuffixes); static bool createFZAndSaveTo(const QDir &dirToCompress, const QString &filename, const QStringList & skipSuffixes); static bool unzipTo(const QString &filepath, const QString &dirToDecompress, QString & error); static void replicateDir(QDir srcDir, QDir targDir); static void cleanup(); static void collectFiles(const QDir & parent, QStringList & filters, QStringList & files, bool recursive); static void makePartFolderHierarchy(const QString & prefixFolder, const QString & destFolder); static void copyBin(const QString & source, const QString & dest); static bool slamCopy(QFile &, const QString & dest); static void showInFolder(const QString & path); static void createUserDataStoreFolders(); protected: FolderUtils(); ~FolderUtils(); const QStringList & userDataStoreFolders(); bool setApplicationPath2(const QString & path); bool setPartsPath2(const QString & path); const QString applicationDirPath(); const QString libraryPath(); QDir getAppPartsSubFolder2(QString); protected: static FolderUtils* singleton; static QString m_openSaveFolder; protected: QStringList m_userFolders; QStringList m_documentFolders; QString m_appPath; QString m_partsPath; }; #endif fritzing-app-0.9.3b/src/utils/fsizegrip.cpp000066400000000000000000000024471272363601300207460ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "fsizegrip.h" #include "misc.h" FSizeGrip::FSizeGrip(QMainWindow *parent) : QSizeGrip(parent) { m_mainWindow = parent; resize(sizeHint()); rearrange(); } void FSizeGrip::rearrange() { int x = m_mainWindow->width()-this->width(); int y = m_mainWindow->height()-this->height(); move(x,y); raise(); } fritzing-app-0.9.3b/src/utils/fsizegrip.h000066400000000000000000000023531272363601300204070ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef FSIZEGRIP_H_ #define FSIZEGRIP_H_ #include #include class FSizeGrip : public QSizeGrip { public: FSizeGrip(QMainWindow *parent); void rearrange(); protected: QMainWindow *m_mainWindow; }; #endif /* FSIZEGRIP_H_ */ fritzing-app-0.9.3b/src/utils/graphicsutils.cpp000066400000000000000000000365471272363601300216350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6926 $: $Author: irascibl@gmail.com $: $Date: 2013-03-10 21:27:34 +0100 (So, 10. Mrz 2013) $ ********************************************************************/ #include "graphicsutils.h" #include #include #include #include #include const double GraphicsUtils::IllustratorDPI = 72; const double GraphicsUtils::StandardFritzingDPI = 1000; const double GraphicsUtils::SVGDPI = 90; const double GraphicsUtils::InchesPerMeter = 39.370078; const double GraphicsUtils::StandardSchematicSeparationMils = 295.275591; // 7.5mm const double GraphicsUtils::StandardSchematicSeparation10thinMils = 100; // 0.1 inches void GraphicsUtils::distanceFromLine(double cx, double cy, double ax, double ay, double bx, double by, double & dx, double & dy, double &distanceSegment, bool & atEndpoint) { // http://www.codeguru.com/forum/showthread.php?t=194400 // // find the distance from the point (cx,cy) to the line // determined by the points (ax,ay) and (bx,by) // double r_numerator = (cx-ax)*(bx-ax) + (cy-ay)*(by-ay); double r_denomenator = (bx-ax)*(bx-ax) + (by-ay)*(by-ay); double r = r_numerator / r_denomenator; if ( (r >= 0) && (r <= 1) ) { dx = ax + r*(bx-ax); dy = ay + r*(by-ay); distanceSegment = (cx-dx)*(cx-dx) + (cy-dy)*(cy-dy); atEndpoint = false; } else { atEndpoint = true; double dist1 = (cx-ax)*(cx-ax) + (cy-ay)*(cy-ay); double dist2 = (cx-bx)*(cx-bx) + (cy-by)*(cy-by); if (dist1 < dist2) { dx = ax; dy = ay; distanceSegment = dist1; } else { dx = bx; dy = by; distanceSegment = dist2; } } return; } struct PD { QPointF p; double d; }; bool pdLessThan(PD* pd1, PD* pd2) { return pd1->d < pd2->d; } QPointF GraphicsUtils::calcConstraint(QPointF initial, QPointF current) { QList pds; PD * pd = new PD; pd->p.setX(current.x()); pd->p.setY(initial.y()); pd->d = (current.y() - initial.y()) * (current.y() - initial.y()); pds.append(pd); pd = new PD; pd->p.setX(initial.x()); pd->p.setY(current.y()); pd->d = (current.x() - initial.x()) * (current.x() - initial.x()); pds.append(pd); double dx, dy, d; bool atEndpoint; QLineF plus45(initial.x() - 10000, initial.y() - 10000, initial.x() + 10000, initial.y() + 10000); distanceFromLine(current.x(), current.y(), plus45.p1().x(), plus45.p1().y(), plus45.p2().x(), plus45.p2().y(), dx, dy, d, atEndpoint); pd = new PD; pd->p.setX(dx); pd->p.setY(dy); pd->d = d; pds.append(pd); QLineF minus45(initial.x() + 10000, initial.y() - 10000, initial.x() - 10000, initial.y() + 10000); distanceFromLine(current.x(), current.y(), minus45.p1().x(), minus45.p1().y(), minus45.p2().x(), minus45.p2().y(), dx, dy, d, atEndpoint); pd = new PD; pd->p.setX(dx); pd->p.setY(dy); pd->d = d; pds.append(pd); qSort(pds.begin(), pds.end(), pdLessThan); QPointF result = pds[0]->p; foreach (PD* pd, pds) { delete pd; } return result; } double GraphicsUtils::pixels2mils(double p, double dpi) { return p * 1000.0 / dpi; } double GraphicsUtils::pixels2ins(double p, double dpi) { return p / dpi; } double GraphicsUtils::distanceSqd(QPointF p1, QPointF p2) { return ((p1.x() - p2.x()) * (p1.x() - p2.x())) + ((p1.y() - p2.y()) * (p1.y() - p2.y())); } double GraphicsUtils::distanceSqd(QPoint p1, QPoint p2) { double dpx = p1.x() - p2.x(); double dpy = p1.y() - p2.y(); return (dpx * dpx) + (dpy * dpy); } double GraphicsUtils::mm2mils(double mm) { return (mm / 25.4 * 1000); } double GraphicsUtils::mm2pixels(double mm) { return (90 * mm / 25.4); } double GraphicsUtils::pixels2mm(double p, double dpi) { return (p / dpi * 25.4); } double GraphicsUtils::mils2pixels(double m, double dpi) { return (dpi * m / 1000); } void GraphicsUtils::saveTransform(QXmlStreamWriter & streamWriter, const QTransform & transform) { if (transform.isIdentity()) return; streamWriter.writeStartElement("transform"); streamWriter.writeAttribute("m11", QString::number(transform.m11())); streamWriter.writeAttribute("m12", QString::number(transform.m12())); streamWriter.writeAttribute("m13", QString::number(transform.m13())); streamWriter.writeAttribute("m21", QString::number(transform.m21())); streamWriter.writeAttribute("m22", QString::number(transform.m22())); streamWriter.writeAttribute("m23", QString::number(transform.m23())); streamWriter.writeAttribute("m31", QString::number(transform.m31())); streamWriter.writeAttribute("m32", QString::number(transform.m32())); streamWriter.writeAttribute("m33", QString::number(transform.m33())); streamWriter.writeEndElement(); } bool GraphicsUtils::loadTransform(const QDomElement & transformElement, QTransform & transform) { if (transformElement.isNull()) return false; double m11 = transform.m11(); double m12 = transform.m12(); double m13 = transform.m13(); double m21 = transform.m21(); double m22 = transform.m22(); double m23 = transform.m23(); double m31 = transform.m31(); double m32 = transform.m32(); double m33 = transform.m33(); bool ok; double temp; temp = transformElement.attribute("m11").toDouble(&ok); if (ok) m11 = temp; temp = transformElement.attribute("m12").toDouble(&ok); if (ok) m12 = temp; temp = transformElement.attribute("m13").toDouble(&ok); if (ok) m13 = temp; temp = transformElement.attribute("m21").toDouble(&ok); if (ok) m21 = temp; temp = transformElement.attribute("m22").toDouble(&ok); if (ok) m22 = temp; temp = transformElement.attribute("m23").toDouble(&ok); if (ok) m23 = temp; temp = transformElement.attribute("m31").toDouble(&ok); if (ok) m31 = temp; temp = transformElement.attribute("m32").toDouble(&ok); if (ok) m32 = temp; temp = transformElement.attribute("m33").toDouble(&ok); if (ok) m33 = temp; transform.setMatrix(m11, m12, m13, m21, m22, m23, m31, m32, m33); return true; } double GraphicsUtils::getNearestOrdinate(double ordinate, double units) { double lo = qFloor(ordinate / units) * units; double hi = qCeil(ordinate / units) * units; return (qAbs(lo - ordinate) <= qAbs(hi - ordinate)) ? lo : hi; } void GraphicsUtils::shortenLine(QPointF & p1, QPointF & p2, double d1, double d2) { double angle1 = atan2(p2.y() - p1.y(), p2.x() - p1.x()); double angle2 = angle1 - M_PI; double dx1 = d1 * cos(angle1); double dy1 = d1 * sin(angle1); double dx2 = d2 * cos(angle2); double dy2 = d2 * sin(angle2); p1.setX(p1.x() + dx1); p1.setY(p1.y() + dy1); p2.setX(p2.x() + dx2); p2.setY(p2.y() + dy2); } bool GraphicsUtils::isRect(const QPolygonF & poly) { if (poly.count() != 5) return false; if (poly.at(0) != poly.at(4)) return false; // either we start running across top or running along side if (poly.at(0).x() == poly.at(1).x() && poly.at(1).y() == poly.at(2).y() && poly.at(2).x() == poly.at(3).x() && poly.at(3).y() == poly.at(4).y()) return true; if (poly.at(0).y() == poly.at(1).y() && poly.at(1).x() == poly.at(2).x() && poly.at(2).y() == poly.at(3).y() && poly.at(3).x() == poly.at(4).x()) return true; return false; } QRectF GraphicsUtils::getRect(const QPolygonF & poly) { // assumes poly is known to be a rect double minX, maxX, minY, maxY; minX = maxX = poly.at(0).x(); minY = maxY = poly.at(0).y(); for (int i = 1; i < 4; i++) { QPointF p = poly.at(i); if (p.x() > maxX) maxX = p.x(); if (p.x() < minX) minX = p.x(); if (p.y() > maxY) maxY = p.y(); if (p.y() < minY) minY = p.y(); } return QRectF(minX, minY, maxX - minX, maxY - minY); } // based on code from http://code-heaven.blogspot.com/2009/05/c-program-for-liang-barsky-line.html bool GraphicsUtils::liangBarskyLineClip(double x1, double y1, double x2, double y2, double wxmin, double wxmax, double wymin, double wymax, double & x11, double & y11, double & x22, double & y22) { double p1 = -(x2 - x1 ); double q1 = x1 - wxmin; double p2 = ( x2 - x1 ); double q2 = wxmax - x1; double p3 = - ( y2 - y1 ); double q3 = y1 - wymin; double p4 = ( y2 - y1 ); double q4 = wymax - y1; x11 = x1; y11 = y1; x22 = x2; y22 = y2; if( ( ( p1 == 0.0 ) && ( q1 < 0.0 ) ) || ( ( p2 == 0.0 ) && ( q2 < 0.0 ) ) || ( ( p3 == 0.0 ) && ( q3 < 0.0 ) ) || ( ( p4 == 0.0 ) && ( q4 < 0.0 ) ) ) { return false; } double u1 = 0.0, u2 = 1.0; if( p1 != 0.0 ) { double r1 = q1 /p1 ; if( p1 < 0 ) u1 = qMax(r1, u1 ); else u2 = qMin(r1, u2 ); } if( p2 != 0.0 ) { double r2 = q2 /p2 ; if( p2 < 0 ) u1 = qMax(r2, u1 ); else u2 = qMin(r2, u2 ); } if( p3 != 0.0 ) { double r3 = q3 /p3 ; if( p3 < 0 ) u1 = qMax(r3, u1 ); else u2 = qMin(r3, u2 ); } if( p4 != 0.0 ) { double r4 = q4 /p4 ; if( p4 < 0 ) u1 = qMax(r4, u1 ); else u2 = qMin(r4, u2 ); } if( u1 > u2 ) { return false; } x11 = x1 + u1 * ( x2 - x1 ) ; y11 = y1 + u1 * ( y2 - y1 ) ; x22 = x1 + u2 * ( x2 - x1 ); y22 = y1 + u2 * ( y2 - y1 ); return true; } QString GraphicsUtils::toHtmlImage(QPixmap *pixmap, const char* format) { QByteArray bytes; QBuffer buffer(&bytes); buffer.open(QIODevice::WriteOnly); pixmap->save(&buffer, format); // writes pixmap into bytes in PNG format return QString("data:image/%1;base64,%2").arg(QString(format).toLower()).arg(QString(bytes.toBase64())); } QPainterPath GraphicsUtils::shapeFromPath(const QPainterPath &path, const QPen &pen, double shapeStrokeWidth, bool includeOriginalPath) { // this function mostly copied from QGraphicsItem::qt_graphicsItem_shapeFromPath // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0 // if we pass a value of 0.0 to QPainterPathStroker::setWidth() static const double penWidthZero = double(0.00000001); if (path == QPainterPath()) return path; QPainterPathStroker ps; ps.setCapStyle(pen.capStyle()); //ps.setCapStyle(Qt::FlatCap); if (shapeStrokeWidth <= 0.0) ps.setWidth(penWidthZero); else ps.setWidth(shapeStrokeWidth); ps.setJoinStyle(pen.joinStyle()); ps.setMiterLimit(pen.miterLimit()); QPainterPath p = ps.createStroke(path); if (includeOriginalPath) { p.addPath(path); } return p; } void GraphicsUtils::qt_graphicsItem_highlightSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF & boundingRect, const QPainterPath & path) { const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); if (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1)) return; const QRectF mbrect = painter->transform().mapRect(boundingRect); if (qMin(mbrect.width(), mbrect.height()) < double(1.0)) return; double itemPenWidth = 1.0; const double pad = itemPenWidth / 2; const double penWidth = 0; // cosmetic pen const QColor fgcolor = option->palette.windowText().color(); const QColor bgcolor( // ensure good contrast against fgcolor fgcolor.red() > 127 ? 0 : 255, fgcolor.green() > 127 ? 0 : 255, fgcolor.blue() > 127 ? 0 : 255); painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine)); painter->setBrush(Qt::NoBrush); if (path.isEmpty()) { painter->drawRect(boundingRect.adjusted(pad, pad, -pad, -pad)); } else { painter->drawPath(path); } painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine)); painter->setBrush(Qt::NoBrush); if (path.isEmpty()) { painter->drawRect(boundingRect.adjusted(pad, pad, -pad, -pad)); } else { painter->drawPath(path); } } QPointF GraphicsUtils::calcRotation(QTransform & rotation, QPointF rCenter, QPointF p, QPointF pCenter) { QPointF dp = rCenter - p; QTransform tp = QTransform().translate(-dp.x(), -dp.y()) * rotation * QTransform().translate(dp.x(), dp.y()); QTransform tc = QTransform().translate(-pCenter.x(), -pCenter.y()) * rotation * QTransform().translate(pCenter.x(), pCenter.y()); QPointF mp = tp.map(QPointF(0,0)); QPointF mc = tc.map(QPointF(0,0)); return (p + mp - mc); } void GraphicsUtils::drawBorder(QImage * image, int border) { int halfBorder = border / 2; QPainter painter; painter.begin(image); painter.setRenderHint(QPainter::Antialiasing, false); QPen pen = painter.pen(); pen.setWidth(border); pen.setColor(0xff000000); painter.setPen(pen); painter.drawLine(0, halfBorder, image->width() - 1, halfBorder); painter.drawLine(0, image->height() - halfBorder, image->width() - 1, image->height() - halfBorder); painter.drawLine(halfBorder, 0, halfBorder, image->height() - 1); painter.drawLine(image->width() - halfBorder, 0, image->width() - halfBorder, image->height() - 1); painter.end(); } bool almostEqual(qreal a, qreal b) { static qreal nearly = 0.001; return (qAbs(a - b) < nearly); } bool GraphicsUtils::isFlipped(const QMatrix & matrix, double & rotation) { static qreal halfSqrt2 = 0.7071; // flipped means flipped horizontally (around the vertical axis) // rotation values are ccw and upright is zero if (almostEqual(matrix.m11(), 1.0)) { if (almostEqual(matrix.m22(), 1.0)) { rotation = 0; return false; } rotation = 180; return true; } if (almostEqual(matrix.m11(), -1.0)) { if (almostEqual(matrix.m22(), -1.0)) { rotation = 180; return false; } rotation = 0; return true; } if (almostEqual(matrix.m12(), 1.0)) { if (almostEqual(matrix.m21(), -1.0)) { rotation = 90; return false; } rotation = 270; return true; } if (almostEqual(matrix.m12(), -1.0)) { if (almostEqual(matrix.m21(), 1)) { rotation = 270; return false; } rotation = 90; return true; } if (almostEqual(matrix.m11(), halfSqrt2)) { if (almostEqual(matrix.m22(), halfSqrt2)) { if (almostEqual(matrix.m12(), -halfSqrt2)) { rotation = 315; return false; } rotation = 45; return false; } if (almostEqual(matrix.m12(), halfSqrt2)) { rotation = 225; return true; } rotation = 135; return true; } if (almostEqual(matrix.m11(), -halfSqrt2)) { if (almostEqual(matrix.m22(), -halfSqrt2)) { if (almostEqual(matrix.m12(), -halfSqrt2)) { rotation = 225; return false; } rotation = 135; return false; } if (almostEqual(matrix.m12(), -halfSqrt2)) { rotation = 45; return true; } rotation = 315; return true; } qWarning() << QString("unknown matrix %1 %2 %3 %4").arg(matrix.m11()).arg(matrix.m12()).arg(matrix.m21()).arg(matrix.m22()); rotation = 0; return false; } fritzing-app-0.9.3b/src/utils/graphicsutils.h000066400000000000000000000065621272363601300212740ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef GRAPHICSUTILS_H #define GRAPHICSUTILS_H #include #include #include #include #include #include #include #include #include #include #include class GraphicsUtils { public: static void distanceFromLine(double cx, double cy, double ax, double ay, double bx, double by, double & dx, double & dy, double &distanceSegment, bool & atEndpoint); static QPointF calcConstraint(QPointF initial, QPointF current); static double pixels2mils(double p, double dpi); static double pixels2ins(double p, double dpi); static double distanceSqd(QPointF p1, QPointF p2); static double distanceSqd(QPoint p1, QPoint p2); static double getNearestOrdinate(double ordinate, double units); static double mm2mils(double mm); static double pixels2mm(double p, double dpi); static double mm2pixels(double mm); static double mils2pixels(double m, double dpi); static void saveTransform(QXmlStreamWriter & streamWriter, const QTransform & transform); static bool loadTransform(const QDomElement & transformElement, QTransform & transform); static bool isRect(const QPolygonF & poly); static QRectF getRect(const QPolygonF & poly); static void shortenLine(QPointF & p1, QPointF & p2, double d1, double d2); static bool liangBarskyLineClip(double x1, double y1, double x2, double y2, double wxmin, double wxmax, double wymin, double wymax, double & x11, double & y11, double & x22, double & y22); static QString toHtmlImage(QPixmap *pixmap, const char* format = "PNG"); static QPainterPath shapeFromPath(const QPainterPath &path, const QPen &pen, double shapeStrokeWidth, bool includeOriginalPath); static void qt_graphicsItem_highlightSelected(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF & boundingRect, const QPainterPath & path); static QPointF calcRotation(QTransform & rotation, QPointF rCenter, QPointF p, QPointF pCenter); static void drawBorder(QImage * image, int border); static bool isFlipped(const QMatrix & matrix, double & rotation); public: static const double IllustratorDPI; static const double StandardFritzingDPI; static const double SVGDPI; static const double InchesPerMeter; static const double StandardSchematicSeparationMils; static const double StandardSchematicSeparation10thinMils; }; #endif fritzing-app-0.9.3b/src/utils/graphutils.cpp000066400000000000000000000473521272363601300211320ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6954 $: $Author: irascibl@gmail.com $: $Date: 2013-04-05 10:22:00 +0200 (Fr, 05. Apr 2013) $ ********************************************************************/ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4503) #pragma warning(disable:4100) // disable scary-looking compiler warnings in Boost library #pragma warning(disable:4181) #endif #include #include #include // #include // kolmogorov_max_flow is probably more efficient, but it doesn't compile #include #include #ifdef _MSC_VER #pragma warning(pop) // restore warning state #endif #include "graphutils.h" #include "../fsvgrenderer.h" #include "../items/wire.h" #include "../items/jumperitem.h" #include "../sketch/sketchwidget.h" #include "../debugdialog.h" void ConnectorEdge::setHeadTail(int h, int t) { head = h; tail = t; } ConnectorEdge * makeConnectorEdge(QList & edges, ConnectorItem * ci, ConnectorItem * cj, int weight, Wire * wire) { ConnectorEdge * connectorEdge = new ConnectorEdge; connectorEdge->c0 = ci; connectorEdge->c1 = cj; connectorEdge->weight = weight; connectorEdge->wire = wire; connectorEdge->visible = true; edges.append(connectorEdge); return connectorEdge; } static int LastVertex = 0; #define appendVertIf(ci, vertices, verts) { \ int ix = vertices.value(ci, -1); \ if (ix == -1) { \ ix = vertices.count(); \ vertices.insert(ci, ix); \ verts.append(add_vertex(g)); \ } \ LastVertex = ix; \ } void GraphUtils::minCut(QList & connectorItems, QList & foreignSketchWidgets, ConnectorItem * source, ConnectorItem * sink, QList & minCut) { // this helped: http://boost.2283326.n4.nabble.com/graph-edmund-karp-max-flow-vs-kolmogorov-max-flow-color-map-td2565611.html using namespace boost; typedef adjacency_list_traits < vecS, vecS, directedS > Traits; typedef property < vertex_color_t, boost::default_color_type > COLOR; typedef property < vertex_index_t, long, COLOR > VERTEX; typedef property < edge_reverse_t, Traits::edge_descriptor > REVERSE; typedef property < edge_residual_capacity_t, long, REVERSE > RESIDUAL; typedef property < edge_capacity_t, long, RESIDUAL > EDGE; typedef adjacency_list < listS, vecS, directedS, VERTEX, EDGE > Graph; Graph g; property_map < Graph, edge_capacity_t >::type capacity = get(edge_capacity, g); property_map < Graph, edge_residual_capacity_t >::type residual_capacity = get(edge_residual_capacity, g); property_map < Graph, edge_reverse_t >::type reverse = get(edge_reverse, g); property_map < Graph, vertex_color_t >::type color = get(vertex_color, g); property_map < Graph, vertex_index_t >::type index = get(vertex_index, g); Traits::vertex_descriptor s, t; QList visitedWires; QList indexes; QHash vertices; QList edges; QVector verts; vertices.insert(source, 0); vertices.insert(sink, 1); verts.append(s = add_vertex(g)); verts.append(t = add_vertex(g)); foreach (ConnectorItem * connectorItem, connectorItems) { //connectorItem->debugInfo("input"); if (connectorItem->attachedToItemType() == ModelPart::Wire) { Wire * wire = qobject_cast(connectorItem->attachedTo()); if (visitedWires.contains(wire)) continue; QList wires; QList ends; wire->collectChained(wires, ends); visitedWires.append(wires); if (ends.count() < 2) continue; foreach (ConnectorItem * end, ends) { appendVertIf(end, vertices, verts); } for (int i = 0; i < ends.count(); i++) { ConnectorItem * end = ends[i]; for (int j = i + 1; j < ends.count(); j++) { ConnectorEdge * connectorEdge = makeConnectorEdge(edges, end, ends[j], 1000, wire); connectorEdge->setHeadTail(vertices.value(connectorEdge->c0), vertices.value(connectorEdge->c1)); } } continue; } if (connectorItem->connectorType() == Connector::Female) { appendVertIf(connectorItem, vertices, verts); int ix = LastVertex; foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) { if (toConnectorItem->attachedToItemType() == ModelPart::Wire) { // deal with the wire continue; } appendVertIf(toConnectorItem, vertices, verts); int jx = LastVertex; ConnectorEdge * connectorEdge = makeConnectorEdge(edges, connectorItem, toConnectorItem, 1000, NULL); connectorEdge->setHeadTail(ix, jx); } } } // don't forget to add edges from bus connections QList originalKeys = vertices.keys(); // make cross-view connections QList foreignEdges; foreach (ConnectorEdge * ce, edges) { if (ce->wire && !ce->wire->isEverVisible()) { ce->visible = false; foreach (SketchWidget * foreignSketchWidget, foreignSketchWidgets) { ItemBase * foreignItemBase = foreignSketchWidget->findItem(ce->wire->id()); if (foreignItemBase && foreignItemBase->isEverVisible()) { ConnectorItem * fc0 = foreignSketchWidget->findConnectorItem(ce->c0); ConnectorItem * fc1 = foreignSketchWidget->findConnectorItem(ce->c1); if (fc0 && fc1) { appendVertIf(fc0, vertices, verts); int ix = LastVertex; appendVertIf(fc1, vertices, verts); int jx = LastVertex; ConnectorEdge * fce = makeConnectorEdge(foreignEdges, fc0, fc1, 1, qobject_cast(foreignItemBase)); fce->setHeadTail(ix, jx); fce = makeConnectorEdge(foreignEdges, ce->c0, fc0, 1000, NULL); fce->setHeadTail(ce->head, ix); fce = makeConnectorEdge(foreignEdges, ce->c1, fc1, 1000, NULL); fce->setHeadTail(ce->tail, jx); } else { ce->c0->debugInfo("missing foreign connector"); } break; } } } if (ce->wire) continue; if (!ce->c0->attachedTo()->isEverVisible()) { ce->visible = false; foreach (SketchWidget * foreignSketchWidget, foreignSketchWidgets) { ConnectorItem * fc0 = foreignSketchWidget->findConnectorItem(ce->c0); if (fc0 == NULL) { ce->c0->debugInfo("missing foreign connector"); continue; } if (!fc0->attachedTo()->isEverVisible()) continue; ConnectorItem * fc1 = foreignSketchWidget->findConnectorItem(ce->c1); if (fc0 && fc1) { appendVertIf(fc0, vertices, verts); int ix = LastVertex; appendVertIf(fc1, vertices, verts); int jx = LastVertex; ConnectorEdge * fce = makeConnectorEdge(foreignEdges, fc0, fc1, 1, NULL); fce->setHeadTail(ix, jx); fce = makeConnectorEdge(foreignEdges, ce->c0, fc0, 1000, NULL); fce->setHeadTail(ce->head, ix); fce = makeConnectorEdge(foreignEdges, ce->c1, fc1, 1000, NULL); fce->setHeadTail(ce->tail, jx); } else { ce->c0->debugInfo("missing foreign connector"); } } } } while (originalKeys.count() > 0) { ConnectorItem * key = originalKeys.takeFirst(); if (key->attachedToItemType() == ModelPart::Wire) continue; if (key->bus() == NULL) continue; int ix = vertices.value(key); QList bcis; key->attachedTo()->busConnectorItems(key->bus(), key, bcis); foreach (ConnectorItem * bci, bcis) { if (bci == key) continue; originalKeys.removeOne(bci); appendVertIf(bci, vertices, verts); int jx = LastVertex; ConnectorEdge * connectorEdge = makeConnectorEdge(edges, key, bci, 1000, NULL); connectorEdge->setHeadTail(ix, jx); } } edges.append(foreignEdges); // add cross-layer edges QList crossVisited; foreach (ConnectorEdge * ce, edges) { QList from; from << ce->c0; from << ce->c1; foreach (ConnectorItem * ci, from) { if (!crossVisited.contains(ci)) { ConnectorItem * cross = ci->getCrossLayerConnectorItem(); if (cross == NULL) continue; appendVertIf(cross, vertices, verts); int jx = LastVertex; ConnectorEdge * connectorEdge = makeConnectorEdge(edges, ci, cross, 1000, NULL); connectorEdge->setHeadTail(vertices.value(ci), jx); } } } //foreach(ConnectorItem * connectorItem, vertices.keys()) { // connectorItem->debugInfo(QString("vertex %1").arg(vertices.value(connectorItem))); //} foreach(ConnectorEdge * ce, edges) { if (!ce->visible) continue; Traits::edge_descriptor e1, e2; bool in1, in2; tie(e1, in1) = add_edge(verts[ce->head], verts[ce->tail], g); tie(e2, in2) = add_edge(verts[ce->tail], verts[ce->head], g); capacity[e2] = capacity[e1] = ce->weight; reverse[e1] = e2; reverse[e2] = e1; //ce->c0->debugInfo(QString("head %1").arg(ce->weight)); //ce->c1->debugInfo("\ttail"); } /* foreach(ConnectorEdge * ce, edges) { connectorItems.at(ce->head)->debugInfo(QString("%1").arg(ce->weight)); } DebugDialog::debug(""); DebugDialog::debug(""); foreach(ConnectorEdge * ce, edges) { partConnectorItems.at(ce->tail)->debugInfo(QString("%1").arg(ce->weight)); } */ // if color_map parameter not specified, colors are not set long flow = edmonds_karp_max_flow(g, s, t, color_map(color)); Q_UNUSED(flow); //DebugDialog::debug(QString("flow %1, s%2, t%3").arg(flow).arg(index(s)).arg(index(t))); //for (int i = 0; i < verts.count(); ++i) { // DebugDialog::debug(QString("index %1 %2").arg(index(verts[i])).arg(color(verts[i]))); //} typedef property_traits::type>::value_type tColorValue; typedef boost::color_traits tColorTraits; foreach (ConnectorEdge * ce, edges) { bool addIt = false; if (ce->visible) { if (color(verts[ce->head]) == tColorTraits::white() && color(verts[ce->tail]) != tColorTraits::white()) { addIt = true; } else if (color(verts[ce->head]) != tColorTraits::white() && color(verts[ce->tail]) == tColorTraits::white()) { addIt = true; } if (addIt) { minCut << ce; //DebugDialog::debug(QString("edge %1 %2 w:%3").arg(ce->head).arg(ce->tail).arg(ce->weight)); } } else { delete ce; } } } bool GraphUtils::chooseRatsnestGraph(const QList * partConnectorItems, ViewGeometry::WireFlags flags, ConnectorPairHash & result) { using namespace boost; typedef adjacency_list < vecS, vecS, undirectedS, property, property < edge_weight_t, double > > Graph; typedef std::pair < int, int >E; if (partConnectorItems->count() < 2) return false; QList temp(*partConnectorItems); //DebugDialog::debug("__________________"); int tix = 0; while (tix < temp.count()) { ConnectorItem * connectorItem = temp[tix++]; //connectorItem->debugInfo("check cross"); ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem(); if (crossConnectorItem) { // it doesn't matter which one on which layer we remove // when we check equal potential both of them will be returned //crossConnectorItem->debugInfo("\tremove cross"); temp.removeOne(crossConnectorItem); } } QList locs; foreach (ConnectorItem * connectorItem, temp) { locs << connectorItem->sceneAdjustedTerminalPoint(NULL); } QList < QList > wiredTo; int num_nodes = temp.count(); int num_edges = num_nodes * (num_nodes - 1) / 2; E * edges = new E[num_edges]; double * weights = new double[num_edges]; int ix = 0; QVector< QVector > reverseWeights(num_nodes, QVector(num_nodes, 0)); for (int i = 0; i < num_nodes; i++) { ConnectorItem * c1 = temp.at(i); //c1->debugInfo("c1"); for (int j = i + 1; j < num_nodes; j++) { edges[ix].first = i; edges[ix].second = j; ConnectorItem * c2 = temp.at(j); if ((c1->attachedTo() == c2->attachedTo()) && (c1->bus() != NULL) && (c1->bus() == c2->bus())) { weights[ix++] = 0; continue; } bool already = false; bool checkWiredTo = true; foreach (QList list, wiredTo) { if (list.contains(c1)) { checkWiredTo = false; if (list.contains(c2)) { weights[ix++] = 0; already = true; } break; } } if (already) continue; //c2->debugInfo("\tc2"); if (checkWiredTo) { QList cwConnectorItems; cwConnectorItems.append(c1); ConnectorItem::collectEqualPotential(cwConnectorItems, true, flags); wiredTo.append(cwConnectorItems); //foreach (ConnectorItem * cx, cwConnectorItems) { //cx->debugInfo("\t\tcx"); //} if (cwConnectorItems.contains(c2)) { weights[ix++] = 0; continue; } } //DebugDialog::debug("c2 not eliminated"); double dx = locs[i].x() - locs[j].x(); double dy = locs[i].y() - locs[j].y(); weights[ix++] = reverseWeights[i][j] = reverseWeights[j][i] = (dx * dx) + (dy * dy); } } bool retval = false; try { Graph g(edges, edges + num_edges, weights, num_nodes); property_map::type weightmap = get(edge_weight, g); std::vector < graph_traits < Graph >::vertex_descriptor > p(num_vertices(g)); prim_minimum_spanning_tree(g, &p[0]); for (std::size_t i = 0; i != p.size(); ++i) { if (i == p[i]) continue; if (reverseWeights[(int) i][(int) p[i]] == 0) continue; result.insert(temp[(int) i], temp[(int) p[i]]); } retval = true; } catch ( const std::exception& e ) { // catch an error in boost 1.54 DebugDialog::debug(QString("boost spanning tree failure: %1").arg(e.what())); } catch(...) { DebugDialog::debug("boost spanning tree failure"); } delete edges; delete weights; return retval; } #define add_edge_d(i, j, g) \ add_edge(verts[i], verts[j], g); \ //partConnectorItems[i]->debugInfo(QString("edge from %1").arg(i)); //partConnectorItems[j]->debugInfo(QString("\tto %1").arg(j)); bool GraphUtils::scoreOneNet(QList & partConnectorItems, ViewGeometry::WireFlags myTrace, RoutingStatus & routingStatus) { using namespace boost; int num_nodes = partConnectorItems.count(); typedef property < vertex_index_t, std::size_t > Index; typedef adjacency_list < listS, listS, directedS, Index > graph_t; typedef graph_traits < graph_t >::vertex_descriptor vertex_t; typedef graph_traits < graph_t >::edge_descriptor edge_t; graph_t G; std::vector < vertex_t > verts(num_nodes); for (int i = 0; i < num_nodes; ++i) { verts[i] = add_vertex(Index(i), G); } //std::pair pair; bool gotUserConnection = false; for (int i = 0; i < num_nodes; i++) { add_edge_d(i, i, G); ConnectorItem * from = partConnectorItems[i]; for (int j = i + 1; j < num_nodes; j++) { ConnectorItem * to = partConnectorItems[j]; if (from->isCrossLayerConnectorItem(to)) { add_edge_d(i, j, G); add_edge_d(j, i, G); continue; } if (from->attachedToItemType() == ModelPart::Symbol && to->attachedToItemType() == ModelPart::Symbol && from->attachedTo()->isEverVisible() && to->attachedTo()->isEverVisible()) { // equipotential symbols are treated as if they were connected by wires add_edge_d(i, j, G); add_edge_d(j, i, G); gotUserConnection = true; continue; } if (to->attachedTo() != from->attachedTo()) { gotUserConnection = true; continue; } if ((to->bus() != NULL) && (to->bus() == from->bus())) { add_edge_d(i, j, G); add_edge_d(j, i, G); continue; } gotUserConnection = true; } } if (!gotUserConnection) { return false; } routingStatus.m_netCount++; for (int i = 0; i < num_nodes; i++) { ConnectorItem * fromConnectorItem = partConnectorItems[i]; if (fromConnectorItem->attachedToItemType() == ModelPart::Jumper) { routingStatus.m_jumperItemCount++; } foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) { switch (toConnectorItem->attachedToItemType()) { case ModelPart::Wire: break; case ModelPart::Breadboard: if (toConnectorItem->attachedTo()->isEverVisible()) { QList ends; collectBreadboard(toConnectorItem, partConnectorItems, ends); foreach (ConnectorItem * end, ends) { if (end == fromConnectorItem) continue; int j = partConnectorItems.indexOf(end); if (j >= 0) { add_edge_d(i, j, G); add_edge_d(j, i, G); } } } continue; } Wire * wire = qobject_cast(toConnectorItem->attachedTo()); if (wire == NULL) continue; if (!(wire->getViewGeometry().wireFlags() & myTrace)) { // don't add edge if the connection isn't traced with my kind of trace continue; } QList wires; QList ends; wire->collectChained(wires, ends); foreach (ConnectorItem * end, ends) { if (end == fromConnectorItem) continue; int j = partConnectorItems.indexOf(end); if (j >= 0) { add_edge_d(i, j, G); add_edge_d(j, i, G); } } } } adjacency_list <> TC; transitive_closure(G, TC); QVector check(num_nodes, true); bool anyMissing = false; for (int i = 0; i < num_nodes - 1; i++) { if (!check[i]) continue; check[i] = false; bool missingOne = false; for (int j = i + 1; j < num_nodes; j++) { if (!check[j]) continue; if (edge(i, j, TC).second) { check[j] = false; } else { // we can minimally span the set with n-1 wires, so even if multiple connections are missing from a given connector, count it as one anyMissing = missingOne = true; //ConnectorItem * ci = partConnectorItems.at(i); //ConnectorItem * cj = partConnectorItems.at(j); //ci->debugInfo("missing one ci"); //cj->debugInfo("\t\tcj"); } } if (missingOne) { routingStatus.m_connectorsLeftToRoute++; } } if (!anyMissing) { routingStatus.m_netRoutedCount++; } return true; } void GraphUtils::collectBreadboard(ConnectorItem * connectorItem, QList & partConnectorItems, QList & ends) { QList itemsToGo; itemsToGo.append(connectorItem); for (int i = 0; i < itemsToGo.count(); i++) { ConnectorItem * candidate = itemsToGo.at(i); if (partConnectorItems.contains(candidate)) { if (!ends.contains(candidate)) { ends.append(candidate); } continue; } Bus * bus = candidate->bus(); if (bus) { QList busConnectorItems; candidate->attachedTo()->busConnectorItems(bus, candidate, busConnectorItems); foreach (ConnectorItem * bci, busConnectorItems) { if (!itemsToGo.contains(bci)) { itemsToGo.append(bci); } } } foreach (ConnectorItem * to, candidate->connectedToItems()) { if (!itemsToGo.contains(to)) { itemsToGo.append(to); } } } } fritzing-app-0.9.3b/src/utils/graphutils.h000066400000000000000000000036321272363601300205700ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6145 $: $Author: cohen@irascible.com $: $Date: 2012-07-05 19:17:11 +0200 (Do, 05. Jul 2012) $ ********************************************************************/ #ifndef GRAPHUTILS_H #define GRAPHUTILS_H #include "../connectors/connectoritem.h" #include "../routingstatus.h" struct ConnectorEdge { int head; int tail; int weight; ConnectorItem * c0; ConnectorItem * c1; class Wire * wire; bool visible; void setHeadTail(int head, int tail); }; class GraphUtils { public: static bool chooseRatsnestGraph(const QList * equipotentials, ViewGeometry::WireFlags, ConnectorPairHash & result); static bool scoreOneNet(QList & partConnectorItems, ViewGeometry::WireFlags, RoutingStatus & routingStatus); static void minCut(QList & connectorItems, QList & foreighSketchWidgets, ConnectorItem * source, ConnectorItem * sink, QList & cutSet); protected: static void collectBreadboard(ConnectorItem * connectorItem, QList & partConnectorItems, QList & ends); }; #endif fritzing-app-0.9.3b/src/utils/lockmanager.cpp000066400000000000000000000150141272363601300212210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "lockmanager.h" #include "folderutils.h" #include "textutils.h" #include #include #include #include #include const QString LockManager::LockedFileName = "___lockfile___.txt"; const long LockManager::FastTime = 2000; const long LockManager::SlowTime = 240000; static LockManager TheLockManager; static QHash > TheTimers; static QMultiHash TheLockedFiles; static QMutex TheMutex; LockedFile::LockedFile(const QString & filename, long freq) { file.setFileName(filename); frequency = freq; } bool LockedFile::touch() { if (file.open(QFile::WriteOnly)) { file.write("a"); file.close(); return true; } return false; } ///////////////////////////////////////////////// LockManager::LockManager() : QObject() { } LockManager::~LockManager() { foreach (QTimer * timer, TheTimers) { if (timer) timer->stop(); } TheTimers.clear(); } void LockManager::cleanup() { foreach (QTimer * timer, TheTimers) { if (timer) { timer->stop(); delete timer; } } TheTimers.clear(); } void LockManager::touchFiles() { QTimer * timer = qobject_cast(sender()); if (timer == NULL) return; QMutexLocker locker(&TheMutex); foreach (LockedFile * lockedFile, TheLockedFiles.values(timer->interval())) { lockedFile->touch(); } } void LockManager::initLockedFiles(const QString & prefix, QString & folder, QHash & lockedFiles, long touchFrequency) { // first create our own unique folder and lock it QDir backupDir(FolderUtils::getTopLevelUserDataStorePath()); backupDir.cd(prefix); QString lockedSubfolder = TextUtils::getRandText(); backupDir.mkdir(lockedSubfolder); folder = backupDir.absoluteFilePath(lockedSubfolder); if (touchFrequency > 0) { LockedFile * lockedFile = makeLockedFile(folder + "/" + LockedFileName, touchFrequency); lockedFiles.insert(lockedSubfolder, lockedFile); } } LockedFile * LockManager::makeLockedFile(const QString & path, long touchFrequency) { LockedFile * lockedFile = new LockedFile(path, touchFrequency); lockedFile->touch(); TheMutex.lock(); TheLockedFiles.insert(touchFrequency, lockedFile); TheMutex.unlock(); QTimer * timer = TheTimers.value(touchFrequency, NULL); if (timer == NULL) { timer = new QTimer(); timer->setInterval(touchFrequency); timer->setSingleShot(false); QObject::connect(timer, SIGNAL(timeout()), &TheLockManager, SLOT(touchFiles())); timer->start(); TheTimers.insert(touchFrequency, timer); } return lockedFile; } void LockManager::releaseLockedFiles(const QString & folder, QHash & lockedFiles) { // remove backup files; this is a clean exit releaseLockedFiles(folder, lockedFiles, true); } void LockManager::releaseLockedFiles(const QString & folder, QHash & lockedFiles, bool remove) { QDir backupDir(folder); backupDir.cdUp(); foreach (QString sub, lockedFiles.keys()) { LockedFile * lockedFile = lockedFiles.value(sub); TheMutex.lock(); TheLockedFiles.remove(lockedFile->frequency, lockedFile); TheMutex.unlock(); if (remove) { FolderUtils::rmdir(backupDir.absoluteFilePath(sub)); } delete lockedFile; } lockedFiles.clear(); } void LockManager::checkLockedFiles(const QString & prefix, QFileInfoList & backupList, QHash & lockedFiles, bool recurse, long touchFrequency) { QDir backupDir(FolderUtils::getTopLevelUserDataStorePath()); backupDir.cd(prefix); QFileInfoList dirList = backupDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks); foreach (QFileInfo dirInfo, dirList) { QDir dir(dirInfo.filePath()); QStringList filters; //DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath())); QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks); bool gotRecurse = false; if (recurse && fileInfoList.isEmpty()) { gotRecurse = checkLockedFilesAux(dir, filters); } if (fileInfoList.isEmpty() && !gotRecurse) { // could mean this backup folder is just being created by another process // could also mean it's leftover crap. // check the date and only delete if it's old QDateTime lastModified = dirInfo.lastModified(); if (lastModified < QDateTime::currentDateTime().addMSecs(-2000 - touchFrequency)) { FolderUtils::rmdir(dirInfo.filePath()); } continue; } QFileInfo lockInfo(dir.absoluteFilePath(LockedFileName)); if (lockInfo.exists()) { QDateTime lastModified = lockInfo.lastModified(); if (lastModified >= QDateTime::currentDateTime().addMSecs(-2000 - touchFrequency)) { // somebody else owns the file continue; } } // we own the file QString folder; LockedFile * lockedFile = makeLockedFile(dir.absoluteFilePath(LockedFileName), touchFrequency); lockedFiles.insert(dirInfo.fileName(), lockedFile); foreach (QFileInfo fileInfo, fileInfoList) { backupList << fileInfo; } } } bool LockManager::checkLockedFilesAux(const QDir & parent, QStringList & filters) { QFileInfoList dirList = parent.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::NoSymLinks); foreach (QFileInfo dirInfo, dirList) { QDir dir(dirInfo.filePath()); //DebugDialog::debug(QString("looking in backup dir %1").arg(dir.absolutePath())); QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files | QDir::Hidden | QDir::NoSymLinks); if (!fileInfoList.isEmpty()) return true; if (checkLockedFilesAux(dir, filters)) return true; } return false; } fritzing-app-0.9.3b/src/utils/lockmanager.h000066400000000000000000000042201272363601300206630ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6141 $: $Author: cohen@irascible.com $: $Date: 2012-07-04 21:20:05 +0200 (Mi, 04. Jul 2012) $ ********************************************************************/ #ifndef LOCKMANAGER_H #define LOCKMANAGER_H #include #include #include struct LockedFile { QFile file; long frequency; LockedFile(const QString & filename, long freq); bool touch(); }; class LockManager : public QObject { Q_OBJECT public: LockManager(); ~LockManager(); public: static void initLockedFiles(const QString & prefix, QString & folder, QHash & lockedFiles, long touchFrequency); static void releaseLockedFiles(const QString & folder, QHash & lockedFiles); static void checkLockedFiles(const QString & prefix, QFileInfoList & backupList, QHash & lockedFiles, bool recurse, long touchFrequency); static void cleanup(); public: static const QString LockedFileName; static const long FastTime; static const long SlowTime; public slots: void touchFiles(); protected: static bool checkLockedFilesAux(const QDir & parent, QStringList & filters); static void releaseLockedFiles(const QString & folder, QHash & lockedFiles, bool remove); static LockedFile * makeLockedFile(const QString & folder, long touchFrequency); }; #endif fritzing-app-0.9.3b/src/utils/misc.cpp000066400000000000000000000045071272363601300176760ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6181 $: $Author: cohen@irascible.com $: $Date: 2012-07-18 15:52:11 +0200 (Mi, 18. Jul 2012) $ ********************************************************************/ // misc Fritzing utility functions #include "misc.h" #include #include #include #include #include #include static QStringList p_fritzingExtensions; static QStringList p_fritzingBundleExtensions; static inline void initializeExtensionList() { if (p_fritzingExtensions.count() == 0) { p_fritzingExtensions << FritzingSketchExtension << FritzingBinExtension << FritzingPartExtension << FritzingBundleExtension << FritzingBundledPartExtension << FritzingBundledBinExtension; p_fritzingBundleExtensions << FritzingBundleExtension << FritzingBundledPartExtension << FritzingBundledBinExtension; } return; } const QStringList & fritzingExtensions() { initializeExtensionList(); return p_fritzingExtensions; } const QStringList & fritzingBundleExtensions() { initializeExtensionList(); return p_fritzingBundleExtensions; } bool isParent(QObject * candidateParent, QObject * candidateChild) { QObject * parent = candidateChild->parent(); while (parent) { if (parent == candidateParent) return true; parent = parent->parent(); } return false; } Qt::KeyboardModifier altOrMetaModifier() { #ifdef LINUX_32 return Qt::MetaModifier; #endif #ifdef LINUX_64 return Qt::MetaModifier; #endif return Qt::AltModifier; } fritzing-app-0.9.3b/src/utils/misc.h000066400000000000000000000067201272363601300173420ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef MISC_H #define MISC_H #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // TODO: this debugging hack seems to break in Qt 5; needs investigation #ifdef Q_OS_WIN #ifndef QT_NO_DEBUG #ifdef _MSC_VER // just for the MS compiler // windows hack for finding memory leaks // the 'new' redefinition breaks QHash and QVector so they are included beforehand. #define _CRTDBG_MAP_ALLOC #include #include #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #define new DEBUG_NEW #endif #endif #endif #endif #ifdef Q_OS_WIN #define getenvUser() getenv("USERNAME") #else #define getenvUser() getenv("USER") #endif #include #include #include #include #include #include #include #define ALLMOUSEBUTTONS (Qt::LeftButton | Qt::MidButton | Qt::RightButton | Qt::XButton1 | Qt::XButton2) typedef QPair RealPair; static QString ___emptyString___; static QDomElement ___emptyElement___; static QStringList ___emptyStringList___; static QHash ___emptyStringHash___; static QDir ___emptyDir___; static QByteArray ___emptyByteArray___; static const QString OCRAFontName("OCRA"); static const QString ResourcePath(":/resources/"); bool isParent(QObject * candidateParent, QObject * candidateChild); static const QString FritzingSketchExtension(".fz"); static const QString FritzingBundleExtension(".fzz"); static const QString FritzingBinExtension(".fzb"); static const QString FritzingBundledBinExtension(".fzbz"); static const QString FritzingPartExtension(".fzp"); static const QString FritzingBundledPartExtension(".fzpz"); inline double qMin(float f, double d) { return qMin((double) f, d); } inline double qMin(double d, float f) { return qMin((double) f, d); } inline double qMax(float f, double d) { return qMax((double) f, d); } inline double qMax(double d, float f) { return qMax((double) f, d); } const QStringList & fritzingExtensions(); const QStringList & fritzingBundleExtensions(); static const QString FemaleSymbolString = QString("%1").arg(QChar(0x2640)); static const QString MaleSymbolString = QString("%1").arg(QChar(0x2642)); Qt::KeyboardModifier altOrMetaModifier(); static QRegExp IntegerFinder("\\d+"); static const int PartsBinHeightDefault = 240; static const int InfoViewHeightDefault = 220; static const int InfoViewMinHeight = 50; static const int DockWidthDefault = 300; static const int DockHeightDefault = 50; #endif fritzing-app-0.9.3b/src/utils/ratsnestcolors.cpp000066400000000000000000000170351272363601300220300ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #include "ratsnestcolors.h" #include "../debugdialog.h" #include QHash RatsnestColors::m_viewList; QHash RatsnestColors::m_allNames; QColor ErrorColor(0, 0, 0); ////////////////////////////////////////////////////// class RatsnestColor { RatsnestColor(const QDomElement &); ~RatsnestColor(); bool matchColor(const QString &); friend class RatsnestColors; protected: QString m_name; QString m_wire; QColor m_ratsnest; QString m_shadow; QStringList m_connectorNames; QList m_obsoleteList; }; ////////////////////////////////////////////////////// RatsnestColor::RatsnestColor(const QDomElement & color) { m_name = color.attribute("name"); //DebugDialog::debug("color name " + m_name); m_ratsnest.setNamedColor(color.attribute("ratsnest")); m_wire = color.attribute("wire"); m_shadow = color.attribute("shadow"); QDomElement connector = color.firstChildElement("connector"); while (!connector.isNull()) { m_connectorNames.append(connector.attribute("name")); connector = connector.nextSiblingElement("connector"); } QDomElement obsolete = color.firstChildElement("obsolete"); while (!obsolete.isNull()) { m_obsoleteList << new RatsnestColor(obsolete); obsolete = obsolete.nextSiblingElement("obsolete"); } } RatsnestColor::~RatsnestColor() { foreach (RatsnestColor * ratsnestColor, m_obsoleteList) { delete ratsnestColor; } m_obsoleteList.clear(); } bool RatsnestColor::matchColor(const QString & string) { if (m_wire.compare(string, Qt::CaseInsensitive) == 0) return true; foreach (RatsnestColor * obsolete, m_obsoleteList) { if (obsolete->m_wire.compare(string, Qt::CaseInsensitive) == 0) return true; } return false; } ////////////////////////////////////////////////////// RatsnestColors::RatsnestColors(const QDomElement & view) { m_viewID = ViewLayer::idFromXmlName(view.attribute("name")); m_backgroundColor.setNamedColor(view.attribute("background")); m_index = 0; QDomElement color = view.firstChildElement("color"); while (!color.isNull()) { RatsnestColor * ratsnestColor = new RatsnestColor(color); m_ratsnestColorHash.insert(ratsnestColor->m_name, ratsnestColor); m_ratsnestColorList.append(ratsnestColor); foreach (QString name, ratsnestColor->m_connectorNames) { m_allNames.insert(name.toLower(), ratsnestColor); } color = color.nextSiblingElement("color"); } } RatsnestColors::~RatsnestColors() { foreach (RatsnestColor * ratsnestColor, m_ratsnestColorHash.values()) { delete ratsnestColor; } m_ratsnestColorHash.clear(); m_ratsnestColorList.clear(); } void RatsnestColors::initNames() { QFile file(":/resources/ratsnestcolors.xml"); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { return; } if (root.tagName() != "colors") { return; } QDomElement view = root.firstChildElement("view"); while (!view.isNull()) { RatsnestColors * ratsnestColors = new RatsnestColors(view); m_viewList.insert(ratsnestColors->m_viewID, ratsnestColors); view = view.nextSiblingElement("view"); } } void RatsnestColors::cleanup() { foreach (RatsnestColors * ratsnestColors, m_viewList.values()) { delete ratsnestColors; } m_viewList.clear(); } const QColor & RatsnestColors::netColor(ViewLayer::ViewID viewID) { RatsnestColors * ratsnestColors = m_viewList.value(viewID, NULL); if (ratsnestColors == NULL) return ErrorColor; return ratsnestColors->getNextColor(); } const QColor & RatsnestColors::getNextColor() { if (m_ratsnestColorList.count() <= 0) return ErrorColor; int resetCount = 0; while (true) { if (m_index < 0 || m_index >= m_ratsnestColorList.count()) { m_index = 0; if (++resetCount > 2) { // prevent infinite loops if all the colors in the list are for particular connectors return ErrorColor; } } RatsnestColor * ratsnestColor = m_ratsnestColorList[m_index++]; if (ratsnestColor->m_connectorNames.length() > 0) { // don't use colors designated for particular connectors m_index++; continue; } if (!ratsnestColor->m_ratsnest.isValid()){ m_index++; continue; } return ratsnestColor->m_ratsnest; } } bool RatsnestColors::findConnectorColor(const QStringList & names, QColor & color) { foreach (QString name, names) { RatsnestColor * ratsnestColor = m_allNames.value(name.toLower(), NULL); if (ratsnestColor == NULL) continue; color = ratsnestColor->m_ratsnest; return true; } return false; } bool RatsnestColors::isConnectorColor(ViewLayer::ViewID m_viewID, const QColor & color) { RatsnestColors * ratsnestColors = m_viewList.value(m_viewID, NULL); if (ratsnestColors == NULL) return false; foreach (RatsnestColor * ratsnestColor, ratsnestColors->m_ratsnestColorList) { if (ratsnestColor->m_ratsnest == color) { return (ratsnestColor->m_connectorNames.length() > 0); } } return false; } void RatsnestColors::reset(ViewLayer::ViewID m_viewID) { RatsnestColors * ratsnestColors = m_viewList.value(m_viewID, NULL); if (ratsnestColors == NULL) return; ratsnestColors->m_index = 0; } QColor RatsnestColors::backgroundColor(ViewLayer::ViewID viewID) { RatsnestColors * ratsnestColors = m_viewList.value(viewID, NULL); if (ratsnestColors == NULL) return QColor(); return ratsnestColors->m_backgroundColor; } const QString & RatsnestColors::shadowColor(ViewLayer::ViewID viewID, const QString & name) { RatsnestColors * ratsnestColors = m_viewList.value(viewID, NULL); if (ratsnestColors == NULL) return ___emptyString___; RatsnestColor * ratsnestColor = ratsnestColors->m_ratsnestColorHash.value(name.toLower(), NULL); if (ratsnestColor == NULL) return ___emptyString___; return ratsnestColor->m_shadow; } QString RatsnestColors::wireColor(ViewLayer::ViewID viewID, QString & string) { RatsnestColors * ratsnestColors = m_viewList.value(viewID, NULL); if (ratsnestColors == NULL) return ___emptyString___; RatsnestColor * ratsnestColor = ratsnestColors->m_ratsnestColorHash.value(string.toLower(), NULL); if (ratsnestColor != NULL) { QString w = ratsnestColor->m_wire; if (!w.isEmpty()) return w; } // reverse lookup foreach (QString cname, ratsnestColors->m_ratsnestColorHash.keys()) { RatsnestColor * candidate = ratsnestColors->m_ratsnestColorHash.value(cname); if (candidate->matchColor(string)) { string = cname; return candidate->m_wire; } } return ___emptyString___; } fritzing-app-0.9.3b/src/utils/ratsnestcolors.h000066400000000000000000000041121272363601300214650ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6912 $: $Author: irascibl@gmail.com $: $Date: 2013-03-09 08:18:59 +0100 (Sa, 09. Mrz 2013) $ ********************************************************************/ #ifndef RATSNESTCOLORS_H #define RATSNESTCOLORS_H #include #include #include #include #include "../viewlayer.h" class RatsnestColors { public: RatsnestColors(const QDomElement &); ~RatsnestColors(); static void initNames(); static void cleanup(); static const QColor & netColor(ViewLayer::ViewID m_viewID); static bool findConnectorColor(const QStringList & names, QColor & color); static bool isConnectorColor(ViewLayer::ViewID m_viewID, const QColor &); static void reset(ViewLayer::ViewID m_viewID); static QColor backgroundColor(ViewLayer::ViewID); static const QString & shadowColor(ViewLayer::ViewID, const QString& name); static QString wireColor(ViewLayer::ViewID, QString& name); protected: const QColor & getNextColor(); protected: ViewLayer::ViewID m_viewID; QColor m_backgroundColor; int m_index; QHash m_ratsnestColorHash; QList m_ratsnestColorList; static QHash m_viewList; static QHash m_allNames; }; #endif fritzing-app-0.9.3b/src/utils/resizehandle.cpp000066400000000000000000000062071272363601300214170ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "resizehandle.h" #include "../sketch/zoomablegraphicsview.h" #include "../debugdialog.h" #include "../sketch/fgraphicsscene.h" #include ResizeHandle::ResizeHandle(const QPixmap &pixmap, const QCursor & cursor, bool ignoresTransforms, QGraphicsItem *parent) : QGraphicsPixmapItem(pixmap, parent) { setCursor(cursor); setVisible(true); setFlag(QGraphicsItem::ItemIgnoresTransformations, ignoresTransforms); } ResizeHandle::~ResizeHandle() { } void ResizeHandle::mousePressEvent(QGraphicsSceneMouseEvent * event) { event->accept(); emit mousePressSignal(event, this); } void ResizeHandle::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { event->accept(); emit mouseMoveSignal(event, this); } void ResizeHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { event->accept(); emit mouseReleaseSignal(event, this); } void ResizeHandle::setResizeOffset(QPointF p) { m_resizeOffset = p; } QPointF ResizeHandle::resizeOffset() { return m_resizeOffset; } QVariant ResizeHandle::itemChange(GraphicsItemChange change, const QVariant &value) { switch (change) { case QGraphicsItem::ItemSceneHasChanged: if (scaling()) { ZoomableGraphicsView *sw = dynamic_cast(scene()->parent()); if (sw) { connect(sw, SIGNAL(zoomChanged(double)), this, SLOT(zoomChangedSlot(double))); } } break; default: break; } return QGraphicsPixmapItem::itemChange(change, value); } void ResizeHandle::zoomChangedSlot(double scale) { emit zoomChangedSignal(scale); } double ResizeHandle::currentScale() { if (scaling()) { ZoomableGraphicsView *sw = dynamic_cast(scene()->parent()); if(sw) { return sw->currentZoom()/100; } } return 1; } void ResizeHandle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if(scene()) { FGraphicsScene * fscene = qobject_cast(scene()); if (fscene != NULL && fscene->displayHandles()) { QGraphicsPixmapItem::paint(painter, option, widget); } } } bool ResizeHandle::scaling() { return (this->flags() & QGraphicsItem::ItemIgnoresTransformations) && (scene() != NULL); } fritzing-app-0.9.3b/src/utils/resizehandle.h000066400000000000000000000042201272363601300210550ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef RESIZEHANDLE_H #define RESIZEHANDLE_H #include #include // currently used by Note.cpp class ResizeHandle : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: ResizeHandle(const QPixmap & pixmap, const QCursor &, bool ignoresTransforms, QGraphicsItem * parent = 0); ~ResizeHandle(); QPointF resizeOffset(); void setResizeOffset(QPointF); double currentScale(); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); public slots: void zoomChangedSlot(double scale); protected: void mousePressEvent ( QGraphicsSceneMouseEvent * event ); void mouseMoveEvent ( QGraphicsSceneMouseEvent * event ); void mouseReleaseEvent ( QGraphicsSceneMouseEvent * event ); QVariant itemChange(GraphicsItemChange change, const QVariant &value); bool scaling(); signals: void mousePressSignal(QGraphicsSceneMouseEvent * event, ResizeHandle *); void mouseMoveSignal(QGraphicsSceneMouseEvent * event, ResizeHandle *); void mouseReleaseSignal(QGraphicsSceneMouseEvent * event, ResizeHandle *); void zoomChangedSignal(double scale); protected: QPointF m_resizeOffset; }; #endif fritzing-app-0.9.3b/src/utils/s2s.cpp000066400000000000000000001014701272363601300174470ustar00rootroot00000000000000#include "s2s.h" #include "stdio.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../src/utils/textutils.h" #include "../../src/utils/schematicrectconstants.h" #include ///////////////////////////////// bool xLessThan(ConnectorLocation * cl1, ConnectorLocation * cl2) { return cl1->terminalPoint.x() < cl2->terminalPoint.x(); } bool yLessThan(ConnectorLocation * cl1, ConnectorLocation * cl2) { return cl1->terminalPoint.y() < cl2->terminalPoint.y(); } double getY(const QPointF & p) { return p.y(); } double getX(const QPointF & p) { return p.x(); } void setHiddenAux(QList & allConnectorLocations, QList & sideConnectorLocations, double (*get)(const QPointF &), double fudge) { int i = 0; while (i < sideConnectorLocations.count() - 1) { QList same; ConnectorLocation * basis = sideConnectorLocations.at(i); same << basis; for (int j = i + 1; j < sideConnectorLocations.count(); j++) { i = j; ConnectorLocation * next = sideConnectorLocations.at(j); if (qAbs(get(basis->terminalPoint )- get(next->terminalPoint)) < fudge) { same << next; } else { break; } } if (same.count() > 1) { foreach (ConnectorLocation * connectorLocation, same) { connectorLocation->hidden = true; } for (int ix = allConnectorLocations.count() - 1; ix >= 0; ix--) { ConnectorLocation * that = allConnectorLocations.at(ix); if (same.contains(that)) { // assume the topmost is the one at the end of allConnectorLocations // the rest are hidden that->hidden = false; sideConnectorLocations.removeOne(that); int maxIndex = -1; foreach (ConnectorLocation * s, same) { int ix = sideConnectorLocations.indexOf(s); if (ix > maxIndex) maxIndex = ix; } // visible one should be the topmost of the set sideConnectorLocations.insert(maxIndex + 1, that); break; } } } } } ///////////////////////////////// static QRegExp IntegerFinder("\\d+"); static const double ImageFactor = 5; static const double FudgeDivisor = 300; static const QRegExp VersionRegexp("[ -_][vV][\\d]+"); /////////////////////////////////////////////////////// QString makePinNumber(const ConnectorLocation * connectorLocation, double x1, double y1, double x2, double y2) { if (connectorLocation->id < 0) return ""; if (connectorLocation->hidden) return ""; if (!connectorLocation->displayPinNumber) return ""; QString text; double tx = 0; double ty = 0; if (x1 == x2) { text += QString("\n") .arg(x2 - SchematicRectConstants::PinWidth + SchematicRectConstants::PinSmallTextVert) .arg((y2 + y1) / 2) .arg(270) ; } else { tx = (x2 + x1) / 2; ty = y2 - SchematicRectConstants::PinWidth + SchematicRectConstants::PinSmallTextVert; } text += QString("%5\n") .arg(SchematicRectConstants::PinSmallTextHeight) .arg(tx) .arg(ty) .arg("middle") .arg(connectorLocation->id) .arg(0) // SW(width) .arg(SchematicRectConstants::PinTextColor) .arg(SchematicRectConstants::FontFamily) ; if (x1 == x2) { text += "\n"; } return text; } QString makePinText(const ConnectorLocation * connectorLocation, double x1, double y1, double x2, double y2, bool anchorAtStart) { if (connectorLocation->hidden) return ""; QString text; bool rotate = false; double xOffset = 0, yOffset = 0; if (x1 == x2) { rotate = true; yOffset = (anchorAtStart ? -SchematicRectConstants::PinTextIndent : SchematicRectConstants::PinTextIndent); xOffset = SchematicRectConstants::PinTextVert; } else if (y1 == y2) { // horizontal pin xOffset = (anchorAtStart ? SchematicRectConstants::PinTextIndent : -SchematicRectConstants::PinTextIndent); yOffset = SchematicRectConstants::PinTextVert; } else { return ""; } if (rotate) { text += QString("\n") .arg(x2 + xOffset) .arg(y2 + yOffset) .arg(270); x2 = 0; y2 = 0; xOffset = yOffset = 0; } text += QString("%5\n") .arg(SchematicRectConstants::PinBigTextHeight) .arg(x2 + xOffset) .arg(y2 + yOffset) .arg(anchorAtStart ? "start" : "end") .arg(TextUtils::escapeAnd(connectorLocation->name)) .arg(0) // SW(width) .arg(SchematicRectConstants::PinTextColor) .arg(SchematicRectConstants::FontFamily) ; if (rotate) { text += "\n"; } return text; } QString makePin(const ConnectorLocation * connectorLocation, double x1, double y1, double x2, double y2) { return QString("\n") .arg(x1) .arg(y1) .arg(x2) .arg(y2) .arg(connectorLocation->hidden ? "none" : SchematicRectConstants::PinColor) .arg(connectorLocation->hidden ? 0 : SchematicRectConstants::PinWidth) .arg(connectorLocation->svgID) ; } QString makeTerminal(const ConnectorLocation * connectorLocation, double x, double y) { return QString("\n") .arg(x - (SchematicRectConstants::PinWidth / 2)) .arg(y - (SchematicRectConstants::PinWidth / 2)) .arg(SchematicRectConstants::PinWidth) .arg(SchematicRectConstants::PinWidth) .arg(connectorLocation->terminalID) ; } /////////////////////////////////////////////////////// S2S::S2S(bool fzpzStyle) : QObject() { m_image = new QImage(50 * ImageFactor, 5 * ImageFactor, QImage::Format_Mono); m_fzpzStyle = fzpzStyle; } void S2S::message(const QString & msg) { // QTextStream cout(stdout); // cout << msg; // cout.flush(); qDebug() << msg; emit messageSignal(msg); } void S2S::saveFile(const QString & content, const QString & path) { QFile file(path); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&file); out.setCodec("UTF-8"); out << content; file.close(); } } bool S2S::onefzp(QString & fzpFilePath, QString & schematicFilePath) { if (!fzpFilePath.endsWith(".fzp")) { return false; } bool createSchematicFile = schematicFilePath.isEmpty(); QString newSchematicFilePath = schematicFilePath; m_lefts.clear(); m_rights.clear(); m_tops.clear(); m_bottoms.clear(); QFile file(fzpFilePath); QString errorStr; int errorLine; int errorColumn; QDomDocument dom; if (!dom.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { message(tr("Failed loading '%1', %2 line:%3 col:%4").arg(fzpFilePath).arg(errorStr).arg(errorLine).arg(errorColumn)); return false; } QDomElement root = dom.documentElement(); QDomElement titleElement = root.firstChildElement("title"); QString title; TextUtils::findText(titleElement, title); //int ix = VersionRegexp.lastIndexIn(title); //if (ix > 0 && ix + VersionRegexp.cap(0).count() == title.count()) { // title.chop(VersionRegexp.cap(0).count()); //} QStringList titles; titles << title; TextUtils::resplit(titles, " "); TextUtils::resplit(titles, "_"); TextUtils::resplit(titles, "-"); if (createSchematicFile) { QString schematicFileName; QDomNodeList nodeList = root.elementsByTagName("schematicView"); for (int i = 0; i < nodeList.count(); i++) { QDomElement schematicView = nodeList.at(i).toElement(); QDomElement layers = schematicView.firstChildElement("layers"); schematicFileName = layers.attribute("image"); if (!schematicFileName.isEmpty()) break; } if (schematicFileName.isEmpty()) { message(tr("Schematic not found for '%1'").arg(fzpFilePath)); return false; } if (m_fzpzStyle) { schematicFileName.replace("/", "."); schematicFileName = "svg." + schematicFileName; } schematicFilePath = m_oldSvgDir.absoluteFilePath(schematicFileName); newSchematicFilePath = m_newSvgDir.absoluteFilePath(schematicFileName); } if (!ensureTerminalPoints(fzpFilePath, schematicFilePath, root)) { return false; } qDebug() << schematicFilePath; QSvgRenderer renderer; bool loaded = renderer.load(schematicFilePath); if (!loaded) { message(tr("Unable to load schematic '%1' for '%2'").arg(schematicFilePath).arg(fzpFilePath)); return false; } QList connectorLocations = initConnectors(root, renderer, fzpFilePath, schematicFilePath); QRectF viewBox = renderer.viewBoxF(); if (viewBox.isEmpty()) { qDebug() << "\tempty viewbox"; } double oldUnit = lrtb(connectorLocations, viewBox); if (qAbs(oldUnit - SchematicRectConstants::NewUnit) < (SchematicRectConstants::NewUnit / 25)) { message(tr("Schematic '%1' is already using the 0.1inch standard.").arg(schematicFilePath)); return false; } setHidden(connectorLocations); double minPinV = 0; double maxPinV = 0; if (m_lefts.count() && m_rights.count()) { minPinV = qMin(m_lefts.first()->terminalPoint.y(), m_rights.first()->terminalPoint.y()); maxPinV = qMax(m_lefts.last()->terminalPoint.y(), m_rights.last()->terminalPoint.y()); } else if (m_rights.count()) { minPinV = m_rights.first()->terminalPoint.y(); maxPinV = m_rights.last()->terminalPoint.y(); } else if (m_lefts.count()) { minPinV = m_lefts.first()->terminalPoint.y(); maxPinV = m_lefts.last()->terminalPoint.y(); } int vPinUnits = qRound((maxPinV - minPinV) / oldUnit) + 2; int vUnits = vPinUnits; double minPinH = 0; double maxPinH = 0; if (m_bottoms.count() && m_tops.count()) { minPinH = qMin(m_bottoms.first()->terminalPoint.x(), m_tops.first()->terminalPoint.x()); maxPinH = qMax(m_bottoms.last()->terminalPoint.x(), m_tops.last()->terminalPoint.x()); } else if (m_bottoms.count()) { minPinH = m_bottoms.first()->terminalPoint.x(); maxPinH = m_bottoms.last()->terminalPoint.x(); } else if (m_tops.count()) { minPinH = m_tops.first()->terminalPoint.x(); maxPinH = m_tops.last()->terminalPoint.x(); } int hPinUnits = qRound((maxPinH - minPinH) / oldUnit) + 2; int hUnits = hPinUnits; double hPinTextMax = 0; for (int i = 0; i < m_lefts.count(); i++) { double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_lefts.at(i)->name); if (w > hPinTextMax) hPinTextMax = w; } for (int i = 0; i < m_rights.count(); i++) { double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_rights.at(i)->name); if (w > hPinTextMax) hPinTextMax = w; } int leftTextUnits = qCeil(hPinTextMax / SchematicRectConstants::NewUnit); int rightTextUnits = leftTextUnits; double vTopPinTextMax = 0; for (int i = 0; i < m_tops.count(); i++) { double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_tops.at(i)->name); if (w > vTopPinTextMax) vTopPinTextMax = w; } int topTextUnits = qCeil(vTopPinTextMax / SchematicRectConstants::NewUnit); double vBottomPinTextMax = 0; for (int i = 0; i < m_bottoms.count(); i++) { double w = stringWidthMM(SchematicRectConstants::PinBigTextHeight, m_bottoms.at(i)->name); if (w > vBottomPinTextMax) vBottomPinTextMax = w; } int bottomTextUnits = qCeil(vBottomPinTextMax / SchematicRectConstants::NewUnit); double labelTextMax = spaceTitle(titles, hUnits - leftTextUnits - rightTextUnits - 2); double textWidth = hPinTextMax + hPinTextMax + labelTextMax + (6 * SchematicRectConstants::PinTextIndent); // PTI text PTI PTI title PTI PTI text PTI double hTextUnits = qCeil(textWidth / SchematicRectConstants::NewUnit); if (hTextUnits > hUnits) hUnits = hTextUnits; textWidth = titles.count() * (SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace); double vTextUnits = qCeil((textWidth / SchematicRectConstants::NewUnit) + (2 * SchematicRectConstants::PinTextIndent)); if (vTextUnits > vUnits) vUnits = vTextUnits; vUnits += topTextUnits; vUnits += bottomTextUnits; double fullWidth = hUnits; if (m_lefts.count()) fullWidth += 2; if (m_rights.count()) fullWidth += 2; double fullHeight = vUnits; if (m_tops.count()) fullHeight += 2; if (m_bottoms.count()) fullHeight += 2; // construct svg QString svg = TextUtils::makeSVGHeader(25.4, 25.4, fullWidth * SchematicRectConstants::NewUnit, fullHeight * SchematicRectConstants::NewUnit); double rectL = SchematicRectConstants::RectStrokeWidth / 2; double rectT = rectL; if (m_lefts.count()) rectL += 2 * SchematicRectConstants::NewUnit; if (m_tops.count()) rectT += 2 * SchematicRectConstants::NewUnit; svg += "\n"; svg += QString("\n") .arg(rectL) .arg(rectT) .arg((hUnits * SchematicRectConstants::NewUnit) - SchematicRectConstants::RectStrokeWidth) .arg((vUnits * SchematicRectConstants::NewUnit) - SchematicRectConstants::RectStrokeWidth) .arg(SchematicRectConstants::RectFillColor) .arg(SchematicRectConstants::RectStrokeColor) .arg(SchematicRectConstants::RectStrokeWidth) ; double vPinOffset = 0; if (m_tops.count()) vPinOffset = 2 * SchematicRectConstants::NewUnit; vPinOffset += topTextUnits * SchematicRectConstants::NewUnit; int space = vUnits - vPinUnits - topTextUnits - bottomTextUnits; if (space > 1) { vPinOffset += (space / 2) * SchematicRectConstants::NewUnit; } foreach (ConnectorLocation * connectorLocation, m_rights) { int units = qRound((connectorLocation->terminalPoint.y() - minPinV) / oldUnit) + 1; double y = units * SchematicRectConstants::NewUnit + vPinOffset; double x1 = ((fullWidth - 2) * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2); double x2 = (fullWidth * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2); svg += makePin(connectorLocation, x1, y, x2, y); svg += makePinNumber(connectorLocation, x1, y, x2, y); svg += makePinText(connectorLocation, x2, y, x1, y, false); svg += makeTerminal(connectorLocation, fullWidth * SchematicRectConstants::NewUnit, y); } foreach (ConnectorLocation * connectorLocation, m_lefts) { int units = qRound((connectorLocation->terminalPoint.y() - minPinV) / oldUnit) + 1; double y = units * SchematicRectConstants::NewUnit + vPinOffset; double x1 = SchematicRectConstants::PinWidth / 2; double x2 = (2 * SchematicRectConstants::NewUnit) + (SchematicRectConstants::PinWidth / 2); svg += makePin(connectorLocation, x1, y, x2, y); svg += makePinNumber(connectorLocation, x1, y, x2, y); svg += makePinText(connectorLocation, x1, y, x2, y, true); svg += makeTerminal(connectorLocation, 0, y); } double hPinOffset = 0; if (m_lefts.count()) hPinOffset = 2 * SchematicRectConstants::NewUnit; space = hUnits - hPinUnits; if (space > 1) { hPinOffset += (space / 2) * SchematicRectConstants::NewUnit; } foreach (ConnectorLocation * connectorLocation, m_bottoms) { int units = qRound((connectorLocation->terminalPoint.x() - minPinH) / oldUnit) + 1; double x = units * SchematicRectConstants::NewUnit + hPinOffset; double y1 = ((fullHeight - 2) * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2); double y2 = (fullHeight * SchematicRectConstants::NewUnit) - (SchematicRectConstants::PinWidth / 2); svg += makePin(connectorLocation, x, y1, x, y2); svg += makePinNumber(connectorLocation, x, y1, x, y2); svg += makePinText(connectorLocation, x, y2, x, y1, true); svg += makeTerminal(connectorLocation, x, fullHeight * SchematicRectConstants::NewUnit); } foreach (ConnectorLocation * connectorLocation, m_tops) { int units = qRound((connectorLocation->terminalPoint.x() - minPinH) / oldUnit) + 1; double x = units * SchematicRectConstants::NewUnit + hPinOffset; double y1 = SchematicRectConstants::PinWidth / 2; double y2 = (2 * SchematicRectConstants::NewUnit) + (SchematicRectConstants::PinWidth / 2); svg += makePin(connectorLocation, x, y1, x, y2); svg += makePinNumber(connectorLocation, x, y1, x, y2); svg += makePinText(connectorLocation, x, y1, x, y2, false); svg += makeTerminal(connectorLocation, x, 0); } double y = vUnits * SchematicRectConstants::NewUnit / 2; y -= titles.count() * (SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace) / 2; y += rectT; y = qMax(y, rectT + (topTextUnits * SchematicRectConstants::NewUnit) + SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace + SchematicRectConstants::PinTextIndent); // y seems to be the location of the baseline so add a line foreach (QString subTitle, titles) { svg += QString("%6\n") .arg(SchematicRectConstants::LabelTextHeight) .arg(((hUnits * SchematicRectConstants::NewUnit) / 2) + rectL) .arg(y) .arg(0) // SW(width) .arg(SchematicRectConstants::TitleColor) .arg(TextUtils::escapeAnd(subTitle)) .arg(SchematicRectConstants::FontFamily) ; y += (SchematicRectConstants::LabelTextHeight + SchematicRectConstants::LabelTextSpace); } svg +="\n"; svg +="\n"; TextUtils::writeUtf8(newSchematicFilePath, svg); qDebug() << newSchematicFilePath; qDebug() << ""; return true; } double S2S::stringWidthMM(double fontSize, const QString & string) { /* // tried using FontMetrics but the result is always too short QFont font("Droid Sans"); font.setPointSizeF(fontSize * 72 / 25.4); QFontMetricsF fontMetrics(font); double pixels = fontMetrics.width(string); double mm = 25.4 * pixels / 90; */ QString svg = TextUtils::makeSVGHeader(25.4, 25.4, 50, 5); svg += QString("%3") .arg(SchematicRectConstants::FontFamily).arg(fontSize).arg(TextUtils::escapeAnd(string)).arg(2.5); svg += ""; QSvgRenderer renderer(svg.toUtf8()); m_image->fill(0xffffffff); QPainter painter; painter.begin(m_image); painter.setRenderHint(QPainter::Antialiasing, false); painter.setRenderHint(QPainter::SmoothPixmapTransform, false); renderer.render(&painter); painter.end(); //QDir dir(QCoreApplication::applicationDirPath()); //dir.cdUp(); //image.save(dir.absoluteFilePath("bloody_font.png")); int bestX = 0; for (int y = 0; y < m_image->height(); y++) { for (int x = bestX; x < m_image->width(); x++) { if (m_image->pixel(x, y) == 0xff000000) { bestX = x; } } } //if (mm < bestX / ImageFactor) { // qDebug() << "string width" << mm << (bestX / ImageFactor) << string; //} return bestX / ImageFactor; } QList S2S::initConnectors(const QDomElement & root, const QSvgRenderer & renderer, const QString & fzpFilename, const QString & svgFilename) { QList connectorLocations; m_minLeft = std::numeric_limits::max(); m_minTop = std::numeric_limits::max(); m_maxRight = std::numeric_limits::min(); m_maxBottom = std::numeric_limits::min(); QDomElement connectors = root.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); QBitArray idlist; int noIDCount = 0; while (!connector.isNull()) { QDomElement schematicView = connector.firstChildElement("views").firstChildElement("schematicView"); QString svgID = schematicView.firstChildElement("p").attribute("svgId"); if (svgID.isEmpty()) { message(tr("Missing connector %1 in '%2' schematic of '%3'").arg(connector.attribute("id")).arg(svgFilename).arg(fzpFilename)); } else { ConnectorLocation * connectorLocation = new ConnectorLocation; connectorLocation->id = -1; connectorLocation->hidden = false; connectorLocation->displayPinNumber = false; QString id = connector.attribute("id"); int ix = IntegerFinder.indexIn(id); if (ix > 0) { connectorLocation->id = IntegerFinder.cap(0).toInt(); if (idlist.size() < connectorLocation->id + 1) { int oldsize = idlist.size(); idlist.resize(connectorLocation->id + 1); for (int i = oldsize; i < idlist.size(); i++) idlist.setBit(i, false); } idlist.setBit(connectorLocation->id, true); } else { noIDCount++; } connectorLocation->name = connector.attribute("name"); connectorLocation->terminalID = schematicView.firstChildElement("p").attribute("terminalId"); connectorLocation->svgID = svgID; QRectF bounds = renderer.boundsOnElement(svgID); QMatrix m = renderer.matrixForElement(svgID); connectorLocation->bounds = m.mapRect(bounds); connectorLocation->terminalPoint = connectorLocation->bounds.center(); if (!connectorLocation->terminalID.isEmpty()) { bounds = renderer.boundsOnElement(connectorLocation->terminalID); m = renderer.matrixForElement(connectorLocation->terminalID); connectorLocation->terminalPoint = m.mapRect(bounds).center(); } connectorLocations.append(connectorLocation); if (connectorLocation->terminalPoint.x() < m_minLeft) { m_minLeft = connectorLocation->terminalPoint.x(); } if (connectorLocation->terminalPoint.x() > m_maxRight) { m_maxRight = connectorLocation->terminalPoint.x(); } if (connectorLocation->terminalPoint.y() < m_minTop) { m_minTop = connectorLocation->terminalPoint.y(); } if (connectorLocation->terminalPoint.y() > m_maxBottom) { m_maxBottom = connectorLocation->terminalPoint.y(); } } connector = connector.nextSiblingElement("connector"); } if (noIDCount > 0) { qDebug() << "no id count" << noIDCount; } bool display = true; if (!idlist.at(0) && !idlist.at(1)) { display = false; } else { int present = 0; for (int i = 0; i < idlist.size(); i++) { if (idlist.at(i)) present++; } display = (present >= .66 * idlist.size()); } foreach (ConnectorLocation * connectorLocation, connectorLocations) { connectorLocation->displayPinNumber = display; } if (display && idlist.at(0)) { foreach (ConnectorLocation * connectorLocation, connectorLocations) { connectorLocation->id++; } } return connectorLocations; } double S2S::lrtb(QList & connectorLocations, const QRectF & viewBox) { m_fudge = qMax(m_maxRight - m_minLeft, m_maxBottom - m_minTop) / FudgeDivisor; foreach (ConnectorLocation * connectorLocation, connectorLocations) { double d[4]; d[0] = connectorLocation->terminalPoint.x() - viewBox.left(); d[1] = connectorLocation->terminalPoint.y() - viewBox.top(); d[2] = viewBox.right() - connectorLocation->terminalPoint.x(); d[3] = viewBox.bottom() - connectorLocation->terminalPoint.y(); int ix = 0; int dx = d[0]; for (int i = 1; i < 4; i++) { if (d[i] < dx) { dx = d[i]; ix = i; } } switch (ix) { case 0: m_lefts << connectorLocation; break; case 1: m_tops << connectorLocation; break; case 2: m_rights << connectorLocation; break; case 3: m_bottoms << connectorLocation; break; default: qDebug() << "shouldn't happen" << ix; } } qSort(m_lefts.begin(), m_lefts.end(), yLessThan); qSort(m_rights.begin(), m_rights.end(), yLessThan); qSort(m_tops.begin(), m_tops.end(), xLessThan); qSort(m_bottoms.begin(), m_bottoms.end(), xLessThan); QList distances; for (int i = 1; i < m_lefts.count(); i++) { ConnectorLocation * l1 = m_lefts.at(i - 1); ConnectorLocation * l2 = m_lefts.at(i); distances << l2->terminalPoint.y() - l1->terminalPoint.y(); } for (int i = 1; i < m_rights.count(); i++) { ConnectorLocation * l1 = m_rights.at(i - 1); ConnectorLocation * l2 = m_rights.at(i); distances << l2->terminalPoint.y() - l1->terminalPoint.y(); } for (int i = 1; i < m_tops.count(); i++) { ConnectorLocation * l1 = m_tops.at(i - 1); ConnectorLocation * l2 = m_tops.at(i); distances << l2->terminalPoint.x() - l1->terminalPoint.x(); } for (int i = 1; i < m_bottoms.count(); i++) { ConnectorLocation * l1 = m_bottoms.at(i - 1); ConnectorLocation * l2 = m_bottoms.at(i); distances << l2->terminalPoint.x() - l1->terminalPoint.x(); } qSort(distances.begin(), distances.end()); int totalPins = m_lefts.count() + m_rights.count() + m_bottoms.count() + m_tops.count(); int most = 0; foreach (double distance, distances) { int d = qRound(distance / m_fudge); if (d > most) most = d; } QVector dbins(most + 2, 0); foreach (double distance, distances) { int d = qRound(distance / m_fudge); dbins[d] += 1; } int biggest = dbins[1]; int biggestIndex = 1; for (int i = 2; i < dbins.count(); i++) { if (dbins[i] > biggest) { biggest = dbins[i]; biggestIndex = i; } } if (biggest == 0) { qDebug() << "\tbiggest 0" << totalPins; } double oldUnit = biggestIndex * m_fudge; qDebug() << QString("\tunit is roughly %1mm, bin:%2 pins:%3 fudge:%4").arg(oldUnit).arg(biggest).arg(totalPins).arg(m_fudge); qDebug() << QString("\tl:%1 t:%2 r:%3 b:%4").arg(m_lefts.count()).arg(m_tops.count()).arg(m_rights.count()).arg(m_bottoms.count()); return oldUnit; } void S2S::setHidden(QList & connectorLocations) { setHiddenAux(connectorLocations, m_lefts, getY, m_fudge); setHiddenAux(connectorLocations, m_rights, getY, m_fudge); setHiddenAux(connectorLocations, m_tops, getX, m_fudge); setHiddenAux(connectorLocations, m_bottoms, getX, m_fudge); } bool S2S::ensureTerminalPoints(const QString & fzpFilePath, const QString & svgFilePath, QDomElement & fzpRoot) { QList missing; QDomElement connectors = fzpRoot.firstChildElement("connectors"); QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement views = connector.firstChildElement("views"); QDomElement schematicView = views.firstChildElement("schematicView"); QDomElement p = schematicView.firstChildElement("p"); QString terminalID = p.attribute("terminalId"); if (terminalID.isEmpty()) { missing << p; } connector = connector.nextSiblingElement("connector"); } if (missing.count() == 0) return true; QSvgRenderer renderer; bool loaded = renderer.load(svgFilePath); if (!loaded) { message(tr("Uunable to load schematic '%1' for '%2'").arg(svgFilePath).arg(fzpFilePath)); return false; } QString errorStr; int errorLine; int errorColumn; QFile file(svgFilePath); QDomDocument dom; if (!dom.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { message(tr("Failed loading schematic '%1', %2 line:%3 col:%4").arg(svgFilePath).arg(errorStr).arg(errorLine).arg(errorColumn)); return false; } QDomElement svgRoot = dom.documentElement(); bool fzpChanged = false; bool svgChanged = false; foreach (QDomElement p, missing) { QDomElement connector = p.parentNode().parentNode().parentNode().toElement(); QString name = connector.attribute("id"); if (name.isEmpty()) { qDebug() << "empty name in connector"; } else { // assumes the file is well behaved, and the terminalID isn't already in use QString terminalName = name + "terminal"; p.setAttribute("terminalId", terminalName); fzpChanged = true; QDomElement terminalElement = TextUtils::findElementWithAttribute(svgRoot, "id", terminalName); if (terminalElement.isNull()) { QString svgID = p.attribute("svgId"); QDomElement connectorElement = TextUtils::findElementWithAttribute(svgRoot, "id", svgID); QRectF bounds = renderer.boundsOnElement(svgID); QDomElement rect = svgRoot.ownerDocument().createElement("rect"); connectorElement.parentNode().insertAfter(rect, connectorElement); rect.setAttribute("x", QString::number(bounds.left())); rect.setAttribute("y", QString::number(bounds.top())); rect.setAttribute("width", QString::number(bounds.width())); rect.setAttribute("height", QString::number(bounds.height())); rect.setAttribute("fill", "none"); rect.setAttribute("stroke", "none"); rect.setAttribute("stroke-width", 0); rect.setAttribute("id", terminalName); svgChanged = true; } } } if (svgChanged) { TextUtils::writeUtf8(svgFilePath, TextUtils::removeXMLEntities(dom.toString(4))); } if (fzpChanged) { TextUtils::writeUtf8(fzpFilePath, TextUtils::removeXMLEntities(fzpRoot.ownerDocument().toString(4))); } return true; } double S2S::spaceTitle(QStringList & titles, int openUnits) { double labelTextMax = 0; foreach (QString title, titles) { double w = stringWidthMM(SchematicRectConstants::LabelTextHeight, title); if (w > labelTextMax) labelTextMax = w; } double useMax = qMax(openUnits * SchematicRectConstants::NewUnit, labelTextMax); bool changed = false; int ix = 0; while (ix < titles.count() - 1) { QString combined = titles.at(ix); if (!(combined.endsWith("_") || combined.endsWith("-"))) { combined.append(" "); } combined.append(titles.at(ix + 1)); double w = stringWidthMM(SchematicRectConstants::LabelTextHeight, combined); if (w <= useMax) { titles[ix] = combined; titles.removeAt(ix + 1); changed = true; continue; } ix++; } if (!changed) return labelTextMax; labelTextMax = 0; foreach (QString title, titles) { double w = stringWidthMM(SchematicRectConstants::LabelTextHeight, title); if (w > labelTextMax) labelTextMax = w; } return labelTextMax; } void S2S::setSvgDirs(QDir & oldDir, QDir & newDir) { m_oldSvgDir = oldDir; m_newSvgDir = newDir; } fritzing-app-0.9.3b/src/utils/s2s.h000066400000000000000000000033271272363601300171160ustar00rootroot00000000000000#ifndef S2S_H #define S2S_H #include #include #include #include #include #include struct ConnectorLocation { QString svgID; QString terminalID; QString name; int id; QRectF bounds; QPointF terminalPoint; bool hidden; bool displayPinNumber; enum Side { Unknown, Left, Top, Right, Bottom }; }; class S2S : public QObject { Q_OBJECT public: S2S(bool fzpzStyle); bool onefzp(QString & fzpFilePath, QString & schematicFilePath); void setSvgDirs(QDir & oldDir, QDir & newDir); signals: void messageSignal(const QString & message); protected: void message(const QString &); void saveFile(const QString & content, const QString & path); double stringWidthMM(double fontSize, const QString & string); QList initConnectors(const QDomElement & root, const QSvgRenderer &, const QString & fzpFilename, const QString & svgFilename); double lrtb(QList &, const QRectF & viewBox); void setHidden(QList &); bool ensureTerminalPoints(const QString & fzpFilename, const QString & svgFilename, QDomElement & fzpRoot); double spaceTitle(QStringList & titles, int openUnits); protected: bool m_fzpzStyle; QString m_andPath; double m_minLeft; double m_minTop; double m_maxRight; double m_maxBottom; QList m_lefts; QList m_tops; QList m_rights; QList m_bottoms; double m_fudge; QDir m_oldSvgDir; QDir m_newSvgDir; QImage * m_image; }; #endif // S2S_H fritzing-app-0.9.3b/src/utils/schematicrectconstants.cpp000066400000000000000000000465261272363601300235250ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "schematicrectconstants.h" #include "textutils.h" // all measurements in millimeters const double SchematicRectConstants::PinWidth = 0.246944; // millimeters const double SchematicRectConstants::PinSmallTextHeight = 0.881944444; const double SchematicRectConstants::PinBigTextHeight = 1.23472222; const double SchematicRectConstants::PinTextIndent = PinWidth * 2; // was PinWidth * 3; const double SchematicRectConstants::PinTextVert = PinWidth * 1; const double SchematicRectConstants::PinSmallTextVert = -PinWidth / 2; const double SchematicRectConstants::LabelTextHeight = 1.49930556; const double SchematicRectConstants::LabelTextSpace = 0.1; const double SchematicRectConstants::RectStrokeWidth = 0.3175; const double SchematicRectConstants::NewUnit = 0.1 * 25.4; // .1in in mm const QString SchematicRectConstants::PinColor("#787878"); const QString SchematicRectConstants::PinTextColor("#8c8c8c"); const QString SchematicRectConstants::TitleColor("#000000"); const QString SchematicRectConstants::RectStrokeColor("#000000"); const QString SchematicRectConstants::RectFillColor("#FFFFFF"); const QString SchematicRectConstants::FontFamily("'Droid Sans'"); /////////////////////////////////////////// #include #include #include #include /////////////////////////////////////////// inline QString SW(qreal strokeWidth) { return QString("%1").arg(strokeWidth); } QString schematicPinNumber(qreal x, qreal y, qreal pinSmallTextHeight, const QString & cid, bool rotate, bool gotZero, bool gotOne) { if (!gotZero && !gotOne) return ""; QString id = cid; if (gotZero) { id = QString::number(cid.toInt() + 1); } QString text; qreal useX = x; qreal offset = 1000 * (SchematicRectConstants::PinWidth - SchematicRectConstants::PinSmallTextVert) / 25.4; qreal useY = y - offset; if (rotate) { text += QString("\n") .arg(useX - offset) .arg(useY + offset) .arg(270); useX = 0; useY = 0; } text += QString("%5\n") .arg(pinSmallTextHeight) .arg(useX) .arg(useY) .arg("middle") .arg(id) .arg(0) // SW(width) .arg(SchematicRectConstants::PinTextColor) .arg(SchematicRectConstants::FontFamily) ; if (rotate) { text += "\n"; } return text; } QString schematicPinText(const QString & id, const QString & signal, qreal x, qreal y, qreal bigPinFontSize, const QString & anchor, bool rotate) { QString text; if (rotate) { qreal yOffset = SchematicRectConstants::PinTextIndent + SchematicRectConstants::RectStrokeWidth; if (anchor == "start") yOffset = -yOffset; qreal xOffset = SchematicRectConstants::PinTextVert + SchematicRectConstants::PinWidth; xOffset = 1000 * xOffset / 25.4; yOffset = 1000 * yOffset / 25.4; text += QString("\n") .arg(x + xOffset) .arg(y + yOffset) .arg(270); x = 0; y = 0; //xOffset = yOffset = 0; } text += QString("%3\n") .arg(id) .arg(y) .arg(TextUtils::escapeAnd(signal)) .arg(bigPinFontSize) .arg(x) .arg(SchematicRectConstants::PinTextColor) .arg(SchematicRectConstants::FontFamily) .arg(anchor) ; if (rotate) { text += "\n"; } return text; } QString SchematicRectConstants::simpleGetConnectorName(const QDomElement & element) { return TextUtils::escapeAnd(element.attribute("name")); } QString SchematicRectConstants::genSchematicDIP(QList & powers, QList & grounds, QList & lefts, QList & rights, QList & vias, QStringList & busNames, QString & boardName, bool usingParam, bool genericSMD, QString (*getConnectorName)(const QDomElement &)) { int powersBuses = 0, groundsBuses = 0, leftsBuses = 0, rightsBuses = 0; QList busContacts; QList all = powers; all.append(lefts); all.append(rights); all.append(grounds); bool gotZero = false; bool gotOne = false; foreach (QDomElement contact, all) { bool ok; if (contact.attribute("connectorIndex").toInt() == 1) gotOne = true; if (contact.attribute("connectorIndex").toInt(&ok) == 0 && ok) gotZero = true; } foreach (QDomElement contact, powers) { contact.setAttribute("ltrb", "power"); if (contact.attribute("bus", 0).compare("1") == 0) { busContacts.append(contact); powersBuses++; } } foreach (QDomElement contact, grounds) { contact.setAttribute("ltrb", "ground"); if (contact.attribute("bus", 0).compare("1") == 0) { busContacts.append(contact); groundsBuses++; } } foreach (QDomElement contact, lefts) { contact.setAttribute("ltrb", "left"); if (contact.attribute("bus", 0).compare("1") == 0) { busContacts.append(contact); leftsBuses++; } } foreach (QDomElement contact, rights) { contact.setAttribute("ltrb", "right"); if (contact.attribute("bus", 0).compare("1") == 0) { busContacts.append(contact); rightsBuses++; } } //foreach(QDomElement contact, busContacts) { //qDebug() << "sch bus" << contact.attribute("signal") << contact.attribute("name") << contact.attribute("connectorIndex"); //} qreal bigFontSize = 1000 * SchematicRectConstants::PinBigTextHeight / 25.4; qreal bigPinFontSize = 1000 * SchematicRectConstants::PinBigTextHeight / 25.4; qreal smallPinFontSize = 1000 * SchematicRectConstants::PinSmallTextHeight / 25.4; qreal pinThickness = 1000 * SchematicRectConstants::PinWidth / 25.4; qreal halfPinThickness = pinThickness / 2; qreal unitLength = 1000 * SchematicRectConstants::NewUnit / 25.4; qreal pinLength = 2 * unitLength; qreal pinTextIndent = 1000 * SchematicRectConstants::PinTextIndent / 25.4; qreal pinTextVert = 1000 * SchematicRectConstants::PinTextVert / 25.4; qreal rectThickness = 1000 * SchematicRectConstants::RectStrokeWidth / 25.4; QStringList titles; if (genericSMD) { titles.append(boardName); } else { boardName.replace(" - ", " "); boardName.replace("_", " "); titles = boardName.split(" "); if (!usingParam) { QRegExp version("^[vV][\\d]+"); if (titles.last().contains(version)) { titles.takeLast(); } } } qreal titleWidth = 0; qreal leftWidth = 0; qreal rightWidth = 0; qreal topWidth = 0; qreal bottomWidth = 0; QFont bigFont("DroidSans"); bigFont.setPointSizeF(bigFontSize * 72 / 1000.0); QFontMetricsF bigFontMetrics(bigFont); foreach (QString title, titles) { qreal w = bigFontMetrics.width(title); if (w > titleWidth) titleWidth = w; } QFont pinTextFont("DroidSans"); pinTextFont.setPointSizeF(bigPinFontSize * 72 / 1000.0); QFontMetricsF smallFontMetrics(pinTextFont); foreach (QDomElement element, lefts) { qreal w = smallFontMetrics.width(getConnectorName(element)); if (w > leftWidth) leftWidth = w; } foreach (QDomElement element, rights) { qreal w = smallFontMetrics.width(getConnectorName(element)); if (w > rightWidth) rightWidth = w; } foreach (QDomElement element, powers) { qreal w = smallFontMetrics.width(getConnectorName(element)); if (w > topWidth) topWidth = w; } foreach (QDomElement element, grounds) { qreal w = smallFontMetrics.width(getConnectorName(element)); if (w > bottomWidth) bottomWidth = w; } // title is symmetric so leave max room on both sides if (leftWidth > rightWidth && rightWidth > 0) rightWidth = leftWidth; if (rightWidth > leftWidth && leftWidth > 0) leftWidth = rightWidth; // convert from pixels (which seem to be points) back to mils leftWidth = 1000 * leftWidth / 72.0; rightWidth = 1000 * rightWidth / 72.0; titleWidth = 1000 * titleWidth / 72.0; topWidth = 1000 * topWidth / 72.0; bottomWidth = 1000 * bottomWidth / 72.0; qreal textWidth = (leftWidth + rightWidth + titleWidth + (rightWidth > 0 ? pinTextIndent : 0) + (leftWidth > 0 ? pinTextIndent : 0)); qreal width = (qCeil(textWidth / unitLength) * unitLength); if (lefts.count() > 0) width += pinLength; if (rights.count() > 0) width += pinLength; int room = qFloor(width / unitLength) - 4; if (powers.count() - powersBuses > room) width += (pinLength * (powers.count() - powersBuses - room)); if (grounds.count() - groundsBuses > room) width += (pinLength * (grounds.count() - groundsBuses - room)); qreal rectTop = rectThickness / 2; qreal height = 0; qreal startTitle = 0; if (powers.count() > 0) { startTitle = pinLength + qCeil(topWidth / unitLength) * unitLength; height += startTitle; rectTop += pinLength - rectThickness / 2; } else { height += unitLength; startTitle = unitLength; } qreal rectBottom = rectThickness / 2; if (grounds.count() > 0) { qreal dh = pinLength + qCeil(bottomWidth / unitLength) * unitLength; rectBottom += pinLength - (rectThickness / 2); height += dh; } else height += unitLength; int l1 = qMax(0, lefts.count() - leftsBuses - 1); l1 = qMax(l1, rights.count() - rightsBuses - 1); l1 = qMax(l1, qCeil(titles.count() * bigFontSize / unitLength)); height += l1 * unitLength; QString svg = TextUtils::makeSVGHeader(1000, 1000, width, height); svg += "Fritzing schematic generated by brd2svg\n"; svg += "\n"; qreal rectLeft = rectThickness / 2; qreal rectMinus = 0; if (lefts.count()) { rectLeft = pinLength; rectMinus += pinLength; } if (rights.count()) { rectMinus += pinLength; } svg += QString("\n") .arg(width - rectMinus - (rectThickness / 2)) .arg(height - rectBottom - rectTop) .arg(rectTop) .arg(rectLeft) .arg(SW(rectThickness)); qreal y = qFloor((height / 2) / unitLength) * unitLength; y -= bigFontSize * qCeil(titles.count() / 2.0); if (y < startTitle) y = startTitle; foreach (QString title, titles) { svg += QString("%2\n") .arg(rectLeft + (width - rectMinus) / 2) .arg(TextUtils::escapeAnd(title)) .arg(y) .arg(bigFontSize) .arg(SchematicRectConstants::FontFamily) ; y += bigFontSize; } QHash busMids; // do vias first, so they are hidden beneath other connectors qreal viaX = 0; qreal viaY = 0; foreach (QDomElement contact, vias) { QString signal = getConnectorName(contact); svg += QString("\n") .arg(contact.attribute("connectorIndex")).arg(signal); svg += QString("\n") .arg(contact.attribute("connectorIndex")); } qreal ly = startTitle; foreach (QDomElement contact, lefts) { bool bus = contact.attribute("bus", 0).compare("1") == 0; if (!contact.isNull() && !bus) { QString signal = getConnectorName(contact); svg += QString("\n") .arg(ly).arg(SW(pinThickness)).arg(pinLength).arg(halfPinThickness).arg(SchematicRectConstants::PinColor); viaY = ly; viaX = halfPinThickness; QString mid = QString("\n") .arg(ly - halfPinThickness).arg(contact.attribute("connectorIndex")).arg(pinLength).arg(pinThickness).arg(signal); mid += QString("\n") .arg(ly - halfPinThickness).arg(contact.attribute("connectorIndex")).arg(pinThickness); svg += mid; if (busNames.contains(contact.attribute("signal"), Qt::CaseInsensitive)) { busMids.insert(contact.attribute("signal").toLower(), mid); } svg += schematicPinText(contact.attribute("connectorIndex"), signal, pinLength + pinTextIndent, ly + pinTextVert, bigPinFontSize, "start", false); svg += schematicPinNumber(pinLength / 2, ly, smallPinFontSize, contact.attribute("connectorIndex"), false, gotZero, gotOne); } if (!bus) ly += unitLength; } ly = startTitle; foreach (QDomElement contact, rights) { bool bus = contact.attribute("bus", 0).compare("1") == 0; if (!contact.isNull() && !bus) { QString signal = getConnectorName(contact); svg += QString("\n") .arg(width - pinLength - halfPinThickness) .arg(ly) .arg(width - halfPinThickness) .arg(SW(pinThickness)) .arg(SchematicRectConstants::PinColor) ; viaY = ly; viaX = width - pinLength - halfPinThickness; QString mid = QString("\n") .arg(width - pinLength) .arg(ly - halfPinThickness) .arg(contact.attribute("connectorIndex")) .arg(pinLength) .arg(pinThickness) .arg(signal); mid += QString("\n") .arg(width) .arg(ly - halfPinThickness) .arg(contact.attribute("connectorIndex")) .arg(pinThickness); svg += mid; if (busNames.contains(contact.attribute("signal"), Qt::CaseInsensitive)) { busMids.insert(contact.attribute("signal").toLower(), mid); } svg += schematicPinText(contact.attribute("connectorIndex"), signal, width - pinLength - pinTextIndent, ly + pinTextVert, bigPinFontSize, "end", false); svg += schematicPinNumber(width - (pinLength / 2) , ly, smallPinFontSize, contact.attribute("connectorIndex"), false, gotZero, gotOne); } if (!bus) ly += unitLength; } qreal lx = width / 2; if (powers.count() - powersBuses > 2) { lx -= (unitLength * (powers.count() - powersBuses) / 2); } lx = qFloor(lx / unitLength) * unitLength; foreach (QDomElement contact, powers) { bool bus = contact.attribute("bus", 0).compare("1") == 0; if (!contact.isNull() && !bus) { QString signal = getConnectorName(contact); svg += QString("\n") .arg(lx).arg(halfPinThickness).arg(pinLength).arg(SW(pinThickness)).arg(SchematicRectConstants::PinColor); viaY = halfPinThickness; viaX = lx; QString mid = QString("\n") .arg(lx - halfPinThickness).arg(0).arg(contact.attribute("connectorIndex")).arg(pinThickness).arg(pinLength).arg(signal); mid += QString("\n") .arg(lx - halfPinThickness).arg(0).arg(contact.attribute("connectorIndex")).arg(pinThickness); svg += mid; if (busNames.contains(contact.attribute("signal"), Qt::CaseInsensitive)) { busMids.insert(contact.attribute("signal").toLower(), mid); } svg += schematicPinText(contact.attribute("connectorIndex"), signal, lx, pinLength - pinTextVert, bigPinFontSize, "end", true); svg += schematicPinNumber(lx, pinLength / 2, smallPinFontSize, contact.attribute("connectorIndex"), true, gotZero, gotOne); } if (!bus) lx += unitLength; } lx = width / 2; if (grounds.count() - groundsBuses > 2) { lx -= (unitLength * (grounds.count() - groundsBuses) / 2); } lx = qFloor(lx / unitLength) * unitLength; foreach (QDomElement contact, grounds) { bool bus = contact.attribute("bus", 0).compare("1") == 0; if (!contact.isNull() && !bus) { QString signal = getConnectorName(contact); svg += QString("\n") .arg(lx).arg(height - pinLength).arg(height - halfPinThickness).arg(SW(pinThickness)).arg(SchematicRectConstants::PinColor); viaY = height - pinLength; viaX = lx; QString mid = QString("\n") .arg(lx - halfPinThickness).arg(height - pinLength).arg(contact.attribute("connectorIndex")).arg(pinThickness).arg(pinLength).arg(signal); mid += QString("\n") .arg(lx - halfPinThickness).arg(height).arg(contact.attribute("connectorIndex")).arg(pinThickness); svg += mid; if (busNames.contains(contact.attribute("signal"), Qt::CaseInsensitive)) { busMids.insert(contact.attribute("signal").toLower(), mid); } svg += schematicPinText(contact.attribute("connectorIndex"), signal, lx, height - (pinLength - pinTextVert), bigPinFontSize, "start", true); svg += schematicPinNumber(lx, height - (pinLength / 2), smallPinFontSize, contact.attribute("connectorIndex"), true, gotZero, gotOne); } if (!bus) lx += unitLength; } svg.replace("viax=''", QString("x='%1'").arg(viaX)); svg.replace("viay=''", QString("y='%1'").arg(viaY)); QRegExp pin("connector[\\d]+pin"); QRegExp terminal("connector[\\d]+terminal"); foreach (QDomElement contact, busContacts) { QString mid = busMids.value(contact.attribute("signal").toLower(), ""); if (mid.isEmpty()) continue; QString index = contact.attribute("connectorIndex"); mid.replace(pin, QString("connector%1pin").arg(index)); mid.replace(terminal, QString("connector%1terminal").arg(index)); //qDebug() << "got bus connector" << index << contact.attribute("signal") << contact.attribute("connectorIndex"); svg += mid; } svg += "\n"; svg += "\n"; return svg; } fritzing-app-0.9.3b/src/utils/schematicrectconstants.h000066400000000000000000000043371272363601300231640ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef SCHEMATICRECTCONTANTS_H #define SCHEMATICRECTCONTANTS_H #include #include #include // all measurements in millimeters class SchematicRectConstants { public: static const double PinWidth; static const double PinSmallTextHeight; static const double PinBigTextHeight; static const double PinTextIndent; static const double PinTextVert; static const double PinSmallTextVert; static const double LabelTextHeight; static const double LabelTextSpace; static const double RectStrokeWidth; static const QString PinColor; static const QString PinTextColor; static const QString TitleColor; static const QString RectStrokeColor; static const QString RectFillColor; static const double NewUnit; static const QString FontFamily; public: static QString genSchematicDIP(QList & powers, QList & grounds, QList & lefts, QList & rights, QList & vias, QStringList & busNames, QString & boardName, bool usingParam, bool genericSMD, QString (*getConnectorName)(const QDomElement &)); static QString simpleGetConnectorName(const QDomElement & element); }; #endif fritzing-app-0.9.3b/src/utils/textutils.cpp000066400000000000000000001551451272363601300210150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ //#include #include #include #include "textutils.h" #include "misc.h" #include "../installedfonts.h" //#include "../debugdialog.h" #include #include #include #include #include #include #include #include #include #include QSet InstalledFonts::InstalledFontsList; QMultiHash InstalledFonts::InstalledFontsNameMapper; // family name to filename; SVG files seem to have to use filename const QString TextUtils::CreatedWithFritzingString("Created with Fritzing (http://www.fritzing.org/)"); const QString TextUtils::CreatedWithFritzingXmlComment("\n"); const QRegExp TextUtils::FindWhitespace("[\\s]+"); static const QRegExp SodipodiAttributeDetector("(inkscape|sodipodi):[^=\\s]+=\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""); static const QRegExp SodipodiElementDetector("]+>"); const QString TextUtils::SMDFlipSuffix("___"); const QString TextUtils::RegexFloatDetector = "[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"; const QRegExp TextUtils::floatingPointMatcher(RegexFloatDetector); static const QRegExp HexExpr("&#x[0-9a-fA-F];"); // static const QRegExp Xmlns("xmlns=([\"|'])[^\"']*\\1"); const ushort TextUtils::MicroSymbolCode = 181; const QString TextUtils::MicroSymbol = QString::fromUtf16(&MicroSymbolCode, 1); const QString TextUtils::AdobeIllustratorIdentifier = "Generator: Adobe Illustrator"; QList PowerPrefixes; QList PowerPrefixValues; const QString TextUtils::PowerPrefixesString = QString("pnmkMGTu\\x%1").arg(MicroSymbolCode, 4, 16, QChar('0')); typedef QHash FixedFontsHash; ////////////////////////////////////////////// QSet getAttrFontFamilies(const QString &fileContent) { /* * font-family defined as attr example: A0 */ static QString pattern = "font-family\\s*=\\s*\"([^\"]*)\""; return TextUtils::getRegexpCaptures(pattern,fileContent); } QSet getFontFamiliesInsideStyleTag(const QString &fileContent) { /* * regexp: font-family\s*:\s*(.|[^;"]*).*" * font-family defined in a style attr example: style="font-size:9;-inkscape-font-specification:Droid Sans;font-family:Droid Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal" style="font-size:144px;font-style:normal;font-weight:normal;line-height:100%;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans" x="18.000002" */ static QString pattern = "font-family\\s*:\\s*([^;\"]*)"; return TextUtils::getRegexpCaptures(pattern,fileContent); } FixedFontsHash fixFontsMapping(const QSet fontsTofix, const QString & destFont) { FixedFontsHash retval; QSet tempFontsToFix; tempFontsToFix = fontsTofix - InstalledFonts::InstalledFontsList; foreach (QString fontFileName, InstalledFonts::InstalledFontsNameMapper.values()) { // SVG uses filename which may not match family name (e.g. "Droid Sans" and "Droid Sans") tempFontsToFix.remove(fontFileName); } foreach (QString broken, tempFontsToFix) { retval.insert(broken, destFont); } return retval; } bool removeFontFamilySingleQuotes(QString &fileContent) { static QString pattern = "font-family=\"('[^']*')\""; QSet wrongFontFamilies = TextUtils::getRegexpCaptures(pattern, fileContent); foreach(QString ff, wrongFontFamilies) { QString wrongFF = ff; QString fixedFF = ff.remove('\''); fileContent.replace(wrongFF,fixedFF); } return wrongFontFamilies.size() > 0; } bool fixUnavailableFontFamilies(QString &fileContent, const QString & destFont) { QSet definedFFs; definedFFs.unite(getAttrFontFamilies(fileContent)); definedFFs.unite(getFontFamiliesInsideStyleTag(fileContent)); FixedFontsHash fixedFonts = fixFontsMapping(definedFFs, destFont); foreach(QString oldF, fixedFonts.keys()) { QString newF = fixedFonts[oldF]; fileContent.replace(oldF,newF); } return fixedFonts.size() > 0; } QString makeDashString(bool dashed, const QVector & pattern, double dpi, double printerScale) { QString dash; if (dashed && pattern.count() > 0) { dash = "stroke-dasharray='"; foreach (qreal p, pattern) { dash += QString::number(p * dpi / printerScale); dash += ","; } dash.chop(1); dash.append("'"); } return dash; } /////////////////////////////////////// void TextUtils::findElementsWithAttribute(QDomElement & element, const QString & att, QList & elements) { if (!element.attribute(att).isEmpty()) { elements.append(element); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { findElementsWithAttribute(child, att, elements); child = child.nextSiblingElement(); } } QDomElement TextUtils::findElementWithAttribute(QDomElement element, const QString & attributeName, const QString & attributeValue) { if (element.hasAttribute(attributeName)) { if (element.attribute(attributeName).compare(attributeValue) == 0) return element; } for(QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { QDomElement result = findElementWithAttribute(e, attributeName, attributeValue); if (!result.isNull()) return result; } return ___emptyElement___; } QSet TextUtils::getRegexpCaptures(const QString &pattern, const QString &textToSearchIn) { QRegExp re(pattern); QSet captures; int pos = 0; while ((pos = re.indexIn(textToSearchIn, pos)) != -1) { captures << re.cap(1); pos += re.matchedLength(); } return captures; } double TextUtils::convertToInches(const QString & s, bool * ok, bool isIllustrator) { QString string = s; double divisor = 1.0; int chop = 2; if (string.endsWith("cm", Qt::CaseInsensitive)) { divisor = 2.54; } else if (string.endsWith("mm", Qt::CaseInsensitive)) { divisor = 25.4; } else if (string.endsWith("in", Qt::CaseInsensitive)) { divisor = 1.0; } else if (string.endsWith("px", Qt::CaseInsensitive)) { divisor = isIllustrator? 72.0: 90.0; } else if (string.endsWith("mil", Qt::CaseInsensitive)) { divisor = 1000.0; chop = 3; } else if (string.endsWith("pt", Qt::CaseInsensitive)) { divisor = 72.0; } else if (string.endsWith("pc", Qt::CaseInsensitive)) { divisor = 6.0; } else { chopNotDigits(string); divisor = 90.0; // default to Qt's standard internal units if all else fails chop = 0; } if (chop) string.chop(chop); bool fine; double result = string.toDouble(&fine); if (!fine) { if (ok) *ok = false; return 0; } if (ok) *ok = true; return result / divisor; } void TextUtils::chopNotDigits(QString & string) { for (int ix = string.count() - 1; ix >= 0; ix--) { QChar ch = string.at(ix); if (ch.isDigit()) return; if (ch == '.') return; string.chop(1); } } bool TextUtils::squashElement(QDomDocument & doc, const QString & elementName, const QString &attName, const QRegExp &matchContent) { bool result = false; QDomElement root = doc.documentElement(); QDomNodeList domNodeList = root.elementsByTagName(elementName); for (int i = 0; i < domNodeList.count(); i++) { QDomElement node = domNodeList.item(i).toElement(); if (node.isNull()) continue; if (!attName.isEmpty()) { QString att = node.attribute(attName); if (att.isEmpty()) continue; if (!matchContent.isEmpty()) { if (matchContent.indexIn(att) < 0) continue; } } node.setTagName("g"); result = true; } return result; } QString TextUtils::replaceTextElement(const QString & svg, const QString & id, const QString & newValue) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) return svg; QDomElement root = doc.documentElement(); QDomNodeList domNodeList = root.elementsByTagName("text"); for (int i = 0; i < domNodeList.count(); i++) { QDomElement node = domNodeList.item(i).toElement(); if (node.isNull()) continue; if (node.attribute("id").compare(id) != 0) continue; replaceChildText(node, newValue); return doc.toString(); } return svg; } QByteArray TextUtils::replaceTextElement(const QByteArray & svg, const QString & id, const QString & newValue) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) return svg; QDomElement root = doc.documentElement(); QDomNodeList domNodeList = root.elementsByTagName("text"); for (int i = 0; i < domNodeList.count(); i++) { QDomElement node = domNodeList.item(i).toElement(); if (node.isNull()) continue; if (node.attribute("id").compare(id) != 0) continue; replaceChildText(node, newValue); return doc.toByteArray(); } return svg; } QString TextUtils::replaceTextElements(const QString & svg, const QHash & hash) { QString errorStr; int errorLine; int errorColumn; QDomDocument doc; if (!doc.setContent(svg, &errorStr, &errorLine, &errorColumn)) return svg; bool changed = false; QDomElement root = doc.documentElement(); QDomNodeList domNodeList = root.elementsByTagName("text"); for (int i = 0; i < domNodeList.count(); i++) { QDomElement node = domNodeList.item(i).toElement(); if (node.isNull()) continue; foreach (QString id, hash.keys()) { if (node.attribute("id").compare(id) != 0) continue; replaceChildText(node, hash.value(id)); changed = true; break; } } if (!changed) return svg; return doc.toString(); } void TextUtils::replaceElementChildText(QDomElement & root, const QString & elementName, const QString & text) { QDomElement element = root.firstChildElement(elementName); if (element.isNull()) { element = root.ownerDocument().createElement(elementName); root.appendChild(element); } replaceChildText(element, text); } void TextUtils::replaceChildText(QDomNode & node, const QString & text) { node.normalize(); QDomNodeList childList = node.childNodes(); for (int j = 0; j < childList.count(); j++) { QDomNode child = childList.item(j); if (child.isText()) { child.setNodeValue(text); return; } } QDomText t = node.ownerDocument().createTextNode(text); node.appendChild(t); } bool TextUtils::mergeSvg(QDomDocument & doc1, const QString & svg, const QString & id) { QString errorStr; int errorLine; int errorColumn; if (doc1.isNull()) { return doc1.setContent(svg, &errorStr, &errorLine, &errorColumn); } QDomDocument doc2; if (!doc2.setContent(svg, &errorStr, &errorLine, &errorColumn)) return false; QDomElement root1 = doc1.documentElement(); if (root1.tagName() != "svg") return false; QDomElement root2 = doc2.documentElement(); if (root2.tagName() != "svg") return false; QDomElement id1; if (!id.isEmpty()) { id1 = findElementWithAttribute(root1, "id", id); } if (id1.isNull()) id1 = root1; QDomElement id2; if (!id.isEmpty()) { id2 = findElementWithAttribute(root2, "id", id); } if (id2.isNull()) id2 = root2; QDomNode node = id2.firstChild(); while (!node.isNull()) { QDomNode nextNode = node.nextSibling(); id1.appendChild(node); node = nextNode; } return true; } QString TextUtils::mergeSvgFinish(QDomDocument & doc) { return removeXMLEntities(doc.toString()); } QString TextUtils::mergeSvg(const QString & svg1, const QString & svg2, const QString & id, bool flip) { QDomDocument doc1; if (!svg1.isEmpty()) { if (!TextUtils::mergeSvg(doc1, svg1, id)) return ___emptyString___; } if (!TextUtils::mergeSvg(doc1, svg2, id)) return ___emptyString___; if (flip) { QDomElement svg = doc1.documentElement(); QString viewBox = svg.attribute("viewBox"); QStringList coords = viewBox.split(" ", QString::SkipEmptyParts); double width = coords[2].toDouble(); QMatrix matrix; matrix.translate(width / 2, 0); matrix.scale(-1, 1); matrix.translate(-width / 2, 0); QHash attributes; attributes.insert("transform", svgMatrix(matrix)); gWrap(doc1, attributes); } return mergeSvgFinish(doc1); } QString TextUtils::makeSVGHeader(double printerScale, double dpi, double width, double height) { double trueWidth = width / printerScale; double trueHeight = height / printerScale; return QString("\n%5" "\n" ) .arg(trueWidth) .arg(trueHeight) .arg(trueWidth * dpi) .arg(trueHeight * dpi) .arg(TextUtils::CreatedWithFritzingXmlComment); } bool TextUtils::isIllustratorFile(const QString &fileContent) { return fileContent.contains(AdobeIllustratorIdentifier, Qt::CaseInsensitive); } bool TextUtils::isIllustratorFile(const QByteArray & fileContent) { return fileContent.contains(AdobeIllustratorIdentifier.toUtf8()); } bool TextUtils::isIllustratorDoc(const QDomDocument & doc) { QDomNodeList children = doc.childNodes(); for ( int i=0; i < children.count(); ++i ) { QDomNode child = children.at(i); if (child.nodeType() == QDomNode::CommentNode) { if (isIllustratorFile(child.nodeValue())) { return true; } } } return false; } QString TextUtils::removeXMLEntities(QString svgContent) { return svgNSOnly(svgContent.remove(HexExpr)); } QString TextUtils::killXMLNS(QString svgContent) { // TODO: this is a bug in Qt, it would be nice to fix it there svgContent.remove(Xmlns); return svgContent; } QString TextUtils::svgNSOnly(QString svgContent) { svgContent.remove(Xmlns); int ix = svgContent.indexOf("= 0) { svgContent.insert(ix + 5, "xmlns=\"http://www.w3.org/2000/svg\" "); } return svgContent; } bool TextUtils::cleanSodipodi(QString &content) { // clean out sodipodi stuff // TODO: don't bother with the core parts int l1 = content.length(); content.remove(SodipodiAttributeDetector); // remove attributes first content.remove(SodipodiElementDetector); if (content.length() != l1) { return true; } return false; /* QString errorStr; int errorLine; int errorColumn; QDomDocument doc; bool result = doc.setContent(bytes, &errorStr, &errorLine, &errorColumn); m_svgXml.clear(); if (!result) { return false; } SvgFlattener flattener; QDomElement root = doc.documentElement(); flattener.flattenChildren(root); SvgFileSplitter::fixStyleAttributeRecurse(root); return doc.toByteArray(); */ } bool TextUtils::fixPixelDimensionsIn(QString &fileContent) { bool isIllustrator = isIllustratorFile(fileContent); if (!isIllustrator) return false; QDomDocument svgDom; QString errorMsg; int errorLine; int errorCol; if(!svgDom.setContent(fileContent, true, &errorMsg, &errorLine, &errorCol)) { return false; } bool fileHasChanged = false; if(isIllustrator) { QDomElement elem = svgDom.firstChildElement("svg"); fileHasChanged = pxToInches(elem,"width",isIllustrator); fileHasChanged |= pxToInches(elem,"height",isIllustrator); } if (fileHasChanged) { fileContent = removeXMLEntities(svgDom.toString()); } return fileHasChanged; } bool TextUtils::pxToInches(QDomElement &elem, const QString &attrName, bool isIllustrator) { if (!isIllustrator) return false; QString attrValue = elem.attribute(attrName); if(attrValue.endsWith("px")) { bool ok; double value = TextUtils::convertToInches(attrValue, &ok, isIllustrator); if(ok) { QString newValue = QString("%1in").arg(value); elem.setAttribute(attrName,newValue); return true; } } return false; } QString TextUtils::svgMatrix(const QMatrix & matrix) { return QString("matrix(%1, %2, %3, %4, %5, %6)").arg(matrix.m11()).arg(matrix.m12()).arg(matrix.m21()).arg(matrix.m22()).arg(matrix.dx()).arg(matrix.dy()); } QString TextUtils::svgMatrix(const QTransform & matrix) { return QString("matrix(%1, %2, %3, %4, %5, %6)").arg(matrix.m11()).arg(matrix.m12()).arg(matrix.m21()).arg(matrix.m22()).arg(matrix.dx()).arg(matrix.dy()); } void TextUtils::setSVGTransform(QDomElement & element, QMatrix & matrix) { element.setAttribute("transform", svgMatrix(matrix)); } QString TextUtils::svgTransform(const QString & svg, QTransform & transform, bool translate, const QString extras) { if (transform.isIdentity()) return svg; return QString("%7") .arg(transform.m11()) .arg(transform.m12()) .arg(transform.m21()) .arg(transform.m22()) .arg(translate ? transform.dx() : 0.0) .arg(translate ? transform.dy() : 0.0) .arg(svg) .arg(extras); } bool TextUtils::getSvgSizes(QDomDocument & doc, double & sWidth, double & sHeight, double & vbWidth, double & vbHeight) { bool isIllustrator = isIllustratorDoc(doc); QDomElement root = doc.documentElement(); QString swidthStr = root.attribute("width"); if (swidthStr.isEmpty()) return false; QString sheightStr = root.attribute("height"); if (sheightStr.isEmpty()) return false; bool ok; sWidth = TextUtils::convertToInches(swidthStr, &ok, isIllustrator); if (!ok) return false; sHeight = TextUtils::convertToInches(sheightStr, &ok, isIllustrator); if (!ok) return false; bool vbWidthOK = false; bool vbHeightOK = false; QString sviewboxStr = root.attribute("viewBox"); if (!sviewboxStr.isEmpty()) { QStringList strings = sviewboxStr.split(" "); if (strings.size() == 4) { double tempWidth = strings[2].toDouble(&vbWidthOK); if (vbWidthOK) { vbWidth = tempWidth; } double tempHeight= strings[3].toDouble(&vbHeightOK); if (vbHeightOK) { vbHeight = tempHeight; } } } if (vbWidthOK && vbHeightOK) return true; // assume that if there's no viewBox, the viewbox is at the right dpi? // or should the assumption be 90 or 100? Illustrator would be 72... int multiplier = 90; if (isIllustratorFile(doc.toString())) { multiplier = 72; } vbWidth = sWidth * multiplier; vbHeight = sHeight * multiplier; return true; } bool TextUtils::findText(const QDomNode & node, QString & text) { if (node.isText()) { text = node.nodeValue(); return true; } QDomNode cnode = node.firstChild(); while (!cnode.isNull()) { if (findText(cnode, text)) return true; cnode = cnode.nextSibling(); } return false; } double TextUtils::convertToInches(const QString & string) { bool ok; double retval = TextUtils::convertToInches(string, &ok, false); if (!ok) return 0; return retval; } QString TextUtils::escapeAnd(const QString & string) { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QString s = Qt::escape(string); #else QString s = string.toHtmlEscaped(); #endif s.replace('\'', "'"); return s; } QString TextUtils::convertExtendedChars(const QString & str) { QString result; foreach (QChar c, str) { if (c < 128) { result.append(c); } else { result.append(QString("&#x%1;").arg(c.unicode(), 0, 16)); } } return result; } QString TextUtils::stripNonValidXMLCharacters(const QString & str) { QString result; QChar hs; bool in_hs = false; foreach (QChar c, str) { if (c.isHighSurrogate()) { hs = c; in_hs = true; continue; } if ((c == 0x9) || (c == 0xA) || (c == 0xD) || ((c >= 0x20) && (c <= 0xD7FF)) || ((c >= 0xE000) && (c <= 0xFFFD))) { if (in_hs) { result.append(hs); in_hs = false; } if (c > 255) { result.append(QString("&#%1").arg(c, 0, 16)); } else { result.append(c); } } else { in_hs = false; } } return result; } bool TextUtils::addCopper1(const QString & filename, QDomDocument & domDocument, const QString & srcAtt, const QString & destAtt) { QString errorStr; int errorLine; int errorColumn; QFile file(filename); bool result = domDocument.setContent(&file, &errorStr, &errorLine, &errorColumn); if (!result) { domDocument.clear(); // probably redundant return false; } QDomElement root = domDocument.documentElement(); QList elements; findElementsWithAttribute(root, "id", elements); for (int i = 0; i < elements.count(); i++) { QDomElement node = elements.at(i); if (node.isNull()) continue; QString att = node.attribute("id"); if (att == destAtt) { // already have copper1 domDocument.clear(); return false; } } result = false; for (int i = 0; i < elements.count(); i++) { QDomElement node = elements.at(i); if (node.isNull()) continue; QString att = node.attribute("id"); if (att == srcAtt) { QDomElement g = domDocument.createElement("g"); g.setAttribute("id", destAtt); node.parentNode().insertAfter(g, node); g.appendChild(node); result = true; } } if (!result) { domDocument.clear(); } //else { //QString test = domDocument.toString(); //DebugDialog::debug("test " + test); //} return result; } QString TextUtils::convertToPowerPrefix(double q) { initPowerPrefixes(); for (int i = 0; i < PowerPrefixes.count(); i++) { if (q < 100 * PowerPrefixValues[i]) { q /= PowerPrefixValues[i]; return QString::number(q) + PowerPrefixes[i]; } } return QString::number(q); } double TextUtils::convertFromPowerPrefixU(QString & val, const QString & symbol) { val.replace('u', MicroSymbol); return convertFromPowerPrefix(val, symbol); } double TextUtils::convertFromPowerPrefix(const QString & val, const QString & symbol) { initPowerPrefixes(); double multiplier = 1; QString temp = val; if (temp.endsWith(symbol)) { temp.chop(symbol.length()); } for (int i = 0; i < PowerPrefixes.count(); i++) { if (PowerPrefixes[i].isEmpty()) continue; if (temp.endsWith(PowerPrefixes[i], Qt::CaseSensitive)) { multiplier = PowerPrefixValues[i]; temp.chop(PowerPrefixes[i].length()); break; } } temp = temp.trimmed(); return temp.toDouble() * multiplier; } void TextUtils::initPowerPrefixes() { if (PowerPrefixes.count() == 0) { PowerPrefixes << "p" << "n" << MicroSymbol << "u" << "m" << "" << "k" << "M" << "G" << "T"; PowerPrefixValues << 0.000000000001 << 0.000000001 << 0.000001 << 0.000001 << 0.001 << 1 << 1000 << 1000000 << 1000000000. << 1000000000000.; } } void TextUtils::collectLeaves(QDomElement & element, int & index, QVector & leaves) { if (element.hasChildNodes()) { element.removeAttribute("id"); QDomElement c = element.firstChildElement(); while (!c.isNull()) { collectLeaves(c, index, leaves); c = c.nextSiblingElement(); } } else { leaves.insert(index, element); element.setAttribute("id", QString::number(index++)); } } void TextUtils::collectLeaves(QDomElement & element, QList & leaves) { if (element.hasChildNodes()) { QDomElement c = element.firstChildElement(); while (!c.isNull()) { collectLeaves(c, leaves); c = c.nextSiblingElement(); } } else { leaves.append(element); } } QMatrix TextUtils::elementToMatrix(QDomElement & element) { QString transform = element.attribute("transform"); if (transform.isEmpty()) return QMatrix(); return transformStringToMatrix(transform); } QMatrix TextUtils::transformStringToMatrix(const QString & transform) { // doesn't handle multiple transform attributes QList floats = getTransformFloats(transform); if (transform.startsWith("translate")) { return QMatrix().translate(floats[0], (floats.length() > 1) ? floats[1] : 0); } else if (transform.startsWith("rotate")) { if (floats.length() == 1) { return QMatrix().rotate(floats[0]); } else if (floats.length() == 3) { return QMatrix().translate(-floats[1], -floats[2]) * QMatrix().rotate(floats[0]) * QMatrix().translate(floats[1], floats[2]); } } else if (transform.startsWith("matrix")) { return QMatrix(floats[0], floats[1], floats[2], floats[3], floats[4], floats[5]); } else if (transform.startsWith("scale")) { return QMatrix().scale(floats[0], floats[1]); } else if (transform.startsWith("skewX")) { return QMatrix().shear(floats[0], 0); } else if (transform.startsWith("skewY")) { return QMatrix().shear(0, floats[0]); } return QMatrix(); } QList TextUtils::getTransformFloats(QDomElement & element){ return getTransformFloats(element.attribute("transform")); } QList TextUtils::getTransformFloats(const QString & transform){ QList list; int pos = 0; while ((pos = TextUtils::floatingPointMatcher.indexIn(transform, pos)) != -1) { list << transform.mid(pos, TextUtils::floatingPointMatcher.matchedLength()).toDouble(); pos += TextUtils::floatingPointMatcher.matchedLength(); } #ifndef QT_NO_DEBUG // QString dbg = "got transform params: \n"; //dbg += transform + "\n"; //for(int i=0; i < list.size(); i++){ //dbg += QString::number(list.at(i)) + " "; // } //DebugDialog::debug(dbg); #endif return list; } void TextUtils::gWrap(QDomDocument & domDocument, const QHash & attributes) { QDomElement g = domDocument.createElement("g"); foreach (QString key, attributes.keys()) { g.setAttribute(key, attributes.value(key, "")); } QDomNodeList nodeList = domDocument.documentElement().childNodes(); QList nodes; for (int i = 0; i < nodeList.count(); i++) { nodes.append(nodeList.item(i)); } domDocument.documentElement().appendChild(g); foreach (QDomNode node, nodes) { g.appendChild(node); } } bool TextUtils::fixInternalUnits(QString & svg) { // float detector is a little weak static QRegExp findInternalUnits("[\"']([\\d,\\.]+)(px|mm|cm|in|pt|pc)[\"']"); static QRegExp findStrokeWidth("stroke-width:([\\d,\\.]+)(px|mm|cm|in|pt|pc)"); int sw = 0; int iu = 0; int jx = svg.indexOf("= 0) { sw = iu = svg.indexOf(">", jx + 4); } bool firstTime = true; QSizeF size; QRectF viewBox; bool result = false; while (true) { iu = findInternalUnits.indexIn(svg, iu); if (iu < 0) break; result = true; if (firstTime) { // assumes width dpi = height dpi size = parseForWidthAndHeight(svg, viewBox, true); if (size.width() == 0) { // svg is messed up return false; } firstTime = false; } QString old = findInternalUnits.cap(1) + findInternalUnits.cap(2); double in = convertToInches(old); double replacement = in * viewBox.width() / size.width(); svg.replace(iu + 1, old.length(), QString::number(replacement)); } while (true) { sw = findStrokeWidth.indexIn(svg, sw); if (sw < 0) break; result = true; if (firstTime) { // assumes width dpi = height dpi size = parseForWidthAndHeight(svg, viewBox, true); if (size.width() == 0) { // svg is messed up return false; } } QString old = findStrokeWidth.cap(1) + findStrokeWidth.cap(2); double in = convertToInches(old); double replacement = in * viewBox.width() / size.width(); svg.replace(sw + 13, old.length(), QString::number(replacement)); } return result; } bool TextUtils::fixMuch(QString &svg, bool fixStrokeWidthFlag) { bool result = cleanSodipodi(svg); result |= fixInternalUnits(svg); QDomDocument svgDom; QString errorMsg; int errorLine; int errorCol; if(!svgDom.setContent(svg, true, &errorMsg, &errorLine, &errorCol)) { return result; } QDomElement root = svgDom.documentElement(); result |= fixViewBox(root); QStringList strings; strings << "pattern" << "marker" << "clipPath"; foreach (QString string, strings) { if (svg.contains("<" + string)) { result |= noPatternAux(svgDom, string); } } if (svg.contains(" todo; todo << svgDoc.documentElement(); while (todo.count() > 0) { QDomElement element = todo.takeFirst(); QDomElement child = element.firstChildElement(); while (!child.isNull()) { todo.append(child); child = child.nextSiblingElement(); } QString stroke, strokeWidth; stroke = element.attribute("stroke"); if (stroke.isEmpty()) { QString style = element.attribute("style"); if (style.contains("stroke")) { fixStyleAttribute(element); stroke = element.attribute("stroke"); } } strokeWidth = element.attribute("stroke-width"); if (stroke.isEmpty()) continue; if (stroke == "none") continue; if (!strokeWidth.isEmpty()) continue; getStrokeWidth(element, 1); result = true; } return result; } bool TextUtils::noPatternAux(QDomDocument & svgDom, const QString & tag) { bool result = false; QDomNodeList nodeList = svgDom.elementsByTagName(tag); for (int i = 0; i < nodeList.count(); i++) { QDomNode pattern = nodeList.at(i); pattern.parentNode().removeChild(pattern); result = true; } return result; } bool TextUtils::tspanRemoveAux(QDomDocument & svgDom) { QList texts; QDomNodeList textNodeList = svgDom.elementsByTagName("text"); for (int i = 0; i < textNodeList.count(); i++) { QDomElement text = textNodeList.item(i).toElement(); QDomElement tspan = text.firstChildElement("tspan"); if (tspan.isNull()) continue; texts.append(text); } if (texts.count() == 0) return false; foreach (QDomElement text, texts) { QDomElement g = svgDom.createElement("g"); text.parentNode().replaceChild(g, text); QDomNamedNodeMap attributes = text.attributes(); for (int i = 0; i < attributes.count(); i++) { QDomNode attribute = attributes.item(i); g.setAttribute(attribute.nodeName(), attribute.nodeValue()); } QString defaultX = g.attribute("x"); QString defaultY = g.attribute("y"); g.removeAttribute("x"); g.removeAttribute("y"); copyText(svgDom, g, text, defaultX, defaultY, false); QDomElement tspan = text.firstChildElement("tspan"); while (!tspan.isNull()) { copyText(svgDom, g, tspan, defaultX, defaultY, true); tspan = tspan.nextSiblingElement("tspan"); } } return true; } bool TextUtils::noUseAux(QDomDocument & svgDom) { QDomElement root = svgDom.documentElement(); if (root.isNull()) return false; QList uses; QDomNodeList useNodeList = svgDom.elementsByTagName("use"); for (int i = 0; i < useNodeList.count(); i++) { QDomElement use = useNodeList.item(i).toElement(); QString refid = use.attribute("href"); if (refid.isEmpty()) continue; QString id = use.attribute("id"); if (id.isEmpty()) continue; uses.append(use); } if (uses.count() == 0) return false; foreach (QDomElement use, uses) { QString transform = use.attribute("transform"); QString refid = use.attribute("href"); QString id = use.attribute("id"); QDomElement g = svgDom.createElement("g"); use.parentNode().replaceChild(g, use); g.setAttribute("transform", transform); if (refid.startsWith("#")) { refid.remove(0, 1); if (refid.isEmpty()) continue; } QDomElement toCopy = findElementWithAttribute(root, "id", refid); if (toCopy.isNull()) continue; QDomElement copy = toCopy.cloneNode(true).toElement(); g.appendChild(copy); copy.setAttribute("id", id); } return true; } QDomElement TextUtils::copyText(QDomDocument & svgDom, QDomElement & parent, QDomElement & text, const QString & defaultX, const QString & defaultY, bool copyAttributes) { QDomNode cnode = text.firstChild(); while (!cnode.isNull()) { if (cnode.isText()) { QDomElement newText = svgDom.createElement("text"); parent.appendChild(newText); newText.setAttribute("x", defaultX); newText.setAttribute("y", defaultY); QDomNode textValue = svgDom.createTextNode(cnode.nodeValue()); newText.appendChild(textValue); if (copyAttributes) { QDomNamedNodeMap attributes = text.attributes(); for (int i = 0; i < attributes.count(); i++) { QDomNode attribute = attributes.item(i); newText.setAttribute(attribute.nodeName(), attribute.nodeValue()); } } // normalize means there's only one text child node, so we can return now return newText; } cnode = cnode.nextSibling(); } return ___emptyElement___; } QString TextUtils::slamStrokeAndFill(const QString & svg, const QString & stroke, const QString & strokeWidth, const QString & fill) { QDomDocument doc; if (doc.setContent(svg)) { QDomElement root = doc.documentElement(); slamStrokeAndFill(root, stroke, strokeWidth, fill); return doc.toString(); } return svg; } void TextUtils::slamStrokeAndFill(QDomElement & element, const QString & stroke, const QString & strokeWidth, const QString & fill) { // assumes style elements have been normalized already QString strokeAtt = element.attribute("stroke"); QString fillAtt = element.attribute("fill"); QString strokeWidthAtt = element.attribute("stroke-width"); if (!strokeAtt.isEmpty() || !fillAtt.isEmpty() || !strokeWidthAtt.isEmpty()) { element.setAttribute("stroke", stroke); element.setAttribute("fill", fill); element.setAttribute("stroke-width", strokeWidth); } QDomElement child = element.firstChildElement(); while (!child.isNull()) { slamStrokeAndFill(child, stroke, strokeWidth, fill); child = child.nextSiblingElement(); } } struct MatchThing { int pos; int len; double val; }; QString TextUtils::incrementTemplate(const QString & filename, int pins, double increment, MultiplyPinFunction multiFun, CopyPinFunction copyFun, void * userData) { QFile file(filename); file.open(QFile::ReadOnly); QString templateString = file.readAll(); file.close(); return incrementTemplateString(templateString, pins, increment, multiFun, copyFun, userData); } QString TextUtils::incrementTemplateString(const QString & templateString, int pins, double increment, MultiplyPinFunction multiFun, CopyPinFunction copyFun, void * userData) { QString string; QRegExp uMatcher("\\[([\\.\\d]+)\\]"); MatchThing matchThings[32]; int pos = 0; unsigned int matchThingIndex = 0; while ((pos = uMatcher.indexIn(templateString, pos)) != -1) { MatchThing * mt = &matchThings[matchThingIndex++]; mt->pos = pos; mt->len = uMatcher.matchedLength(); mt->val = uMatcher.cap(1).toDouble(); pos += uMatcher.matchedLength(); if (matchThingIndex >= sizeof(matchThings) / sizeof(MatchThing)) break; } for (int i = 0; i < pins; i++) { QString argCopy(templateString); for (int j = matchThingIndex - 1; j >= 0; j--) { MatchThing * mt = &matchThings[j]; argCopy.replace(mt->pos, mt->len, (*multiFun)(i, increment, mt->val)); } string += (*copyFun)(i, argCopy, userData); } return string; } QString TextUtils::standardCopyPinFunction(int pin, const QString & argString, void *) { return argString.arg(pin); } QString TextUtils::standardMultiplyPinFunction(int pin, double increment, double value) { return QString::number(value + (pin * increment)); } QString TextUtils::incMultiplyPinFunction(int pin, double increment, double value) { return QString::number(value + ((pin + 1) * increment)); } QString TextUtils::incCopyPinFunction(int pin, const QString & argString, void *) { return argString.arg(pin + 1); } QString TextUtils::noCopyPinFunction(int, const QString & argString, void *) { return argString; } QString TextUtils::negIncCopyPinFunction(int pin, const QString & argString, void * userData) { int pins = *((int *) userData); int offset = *(((int *) userData) + 1); return argString.arg(pins - (pin + offset)); } double TextUtils::getViewBoxCoord(const QString & svg, int coord) { QRegExp re("viewBox=['\\\"]([^'\\\"]+)['\\\"]"); int ix = re.indexIn(svg); if (ix < 0) return 0; QString vb = re.cap(1); QStringList coords = vb.split(" "); QString c = coords.at(coord); return c.toDouble(); } QString TextUtils::makeLineSVG(QPointF p1, QPointF p2, double width, QString colorString, double dpi, double printerScale, bool blackOnly, bool dashed, const QVector & pattern) { p1.setX(p1.x() * dpi / printerScale); p1.setY(p1.y() * dpi / printerScale); p2.setX(p2.x() * dpi / printerScale); p2.setY(p2.y() * dpi / printerScale); QString dash = makeDashString(dashed, pattern, dpi, printerScale); QString stroke = (blackOnly) ? "black" : colorString; return QString("") .arg(p1.x()) .arg(p1.y()) .arg(p2.x()) .arg(p2.y()) .arg(width * dpi / printerScale) .arg(stroke) .arg(dash) ; } QString TextUtils::makeCubicBezierSVG(const QPolygonF & poly, double width, QString colorString, double dpi, double printerScale, bool blackOnly, bool dashed, const QVector & pattern) { QString dash = makeDashString(dashed, pattern, dpi, printerScale); QString stroke = (blackOnly) ? "black" : colorString; return QString("") .arg(width * dpi / printerScale) .arg(stroke) .arg(poly.at(0).x() * dpi / printerScale) .arg(poly.at(0).y() * dpi / printerScale) .arg(poly.at(1).x() * dpi / printerScale) .arg(poly.at(1).y() * dpi / printerScale) .arg(poly.at(2).x() * dpi / printerScale) .arg(poly.at(2).y() * dpi / printerScale) .arg(poly.at(3).x() * dpi / printerScale) .arg(poly.at(3).y() * dpi / printerScale) .arg(dash) ; } QString TextUtils::makeRectSVG(QRectF r, QPointF offset, double dpi, double printerScale) { r.translate(-offset.x(), -offset.y()); double l = r.left() * dpi / printerScale; double t = r.top() * dpi / printerScale; double w = r.width() * dpi / printerScale; double h = r.height() * dpi / printerScale; QString stroke = "black"; return QString("") .arg(l) .arg(t) .arg(w) .arg(h) .arg(1 * dpi / printerScale) .arg(stroke); } QString TextUtils::makePolySVG(const QPolygonF & poly, QPointF offset, double width, QString colorString, double dpi, double printerScale, bool blackOnly) { QString polyString = QString("\n"; return polyString; } QPolygonF TextUtils::polygonFromElement(QDomElement & element) { QPolygonF poly; QDomElement point = element.firstChildElement("point"); while (!point.isNull()) { double x = point.attribute("x").toDouble(); double y = point.attribute("y").toDouble(); poly.append(QPointF(x, y)); point = point.nextSiblingElement("point"); } return poly; } QString TextUtils::pointToSvgString(QPointF p, QPointF offset, double dpi, double printerScale) { QString point; point += QString::number((p.x() - offset.x()) * dpi / printerScale); point += ","; point += QString::number((p.y() - offset.y()) * dpi / printerScale); return point; } QString TextUtils::removeSVGHeader(QString & string) { int ix = string.indexOf("", ix); if (ix < 0) return string; int jx = string.indexOf(""); if (jx < 0) return string; string.remove(jx, 6); string.remove(0, ix + 1); return string; } /* QString TextUtils::getMacAddress() { // http://stackoverflow.com/questions/7609953/obtaining-mac-address-on-windows-in-qt foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) { // Return only the first non-loopback MAC Address if (!(interface.flags() & QNetworkInterface::IsLoopBack)) return interface.hardwareAddress(); } return QString(); } */ QString TextUtils::expandAndFill(const QString & svg, const QString & color, double expandBy) { QDomDocument domDocument; QString errorStr; int errorLine; int errorColumn; bool result = domDocument.setContent(svg, &errorStr, &errorLine, &errorColumn); if (!result) { return ""; } QDomElement root = domDocument.documentElement(); expandAndFillAux(root, color, expandBy); return domDocument.toString(); } void TextUtils::expandAndFillAux(QDomElement & element, const QString & color, double expandBy) { bool gotChildren = false; QDomElement child = element.firstChildElement(); while (!child.isNull()) { gotChildren = true; expandAndFillAux(child, color, expandBy); child = child.nextSiblingElement(); } if (gotChildren) return; QString fill = element.attribute("fill"); QString stroke = element.attribute("stroke"); QString strokeWidth = element.attribute("stroke-width"); if (stroke.isEmpty() && fill.isEmpty()) { return; } element.setAttribute("fill", color); element.setAttribute("stroke", color); if (strokeWidth.isEmpty()) { strokeWidth = "0"; } double sw = strokeWidth.toDouble(); sw += expandBy; element.setAttribute("stroke-width", QString::number(sw)); } bool TextUtils::writeUtf8(const QString & fileName, const QString & text) { QFile file(fileName); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream out(&file); out.setCodec("UTF-8"); out << text; file.close(); return true; } return false; } int TextUtils::getPinsAndSpacing(const QString & expectedFileName, QString & spacingString) { QStringList pieces = expectedFileName.split("_"); int pix = 0; foreach (QString piece, pieces) { bool ok; piece.toInt(&ok); if (ok) break; pix++; } if (pix >= pieces.count()) return 0; int pins = pieces.at(pix).toInt(); spacingString = "100mil"; for (++pix; pix < pieces.count(); pix++) { if (QRegExp("\\d").indexIn(pieces.at(pix)) == 0) { spacingString = pieces.at(pix); return pins; } } return pins; } QSizeF TextUtils::parseForWidthAndHeight(const QString & svg) { QRectF viewBox; return parseForWidthAndHeight(svg, viewBox, false); } QString TextUtils::parseForModuleID(const QString & fzpXmlString) { QXmlStreamReader streamReader(fzpXmlString); streamReader.setNamespaceProcessing(false); while (!streamReader.atEnd()) { switch (streamReader.readNext()) { case QXmlStreamReader::StartElement: if (streamReader.name().toString().compare("module") == 0) { return streamReader.attributes().value("moduleId").toString(); } break; default: break; } } return ""; } QString TextUtils::parseFileForModuleID(const QString & fzpPath) { QString moduleId; QFile file(fzpPath); if (!file.open(QFile::ReadOnly)) return moduleId; QXmlStreamReader streamReader(&file); streamReader.setNamespaceProcessing(false); while (!streamReader.atEnd()) { switch (streamReader.readNext()) { case QXmlStreamReader::StartElement: if (streamReader.name().toString().compare("module") == 0) { file.close(); return streamReader.attributes().value("moduleId").toString(); } break; default: break; } } file.close(); return moduleId; } QSizeF TextUtils::parseForWidthAndHeight(const QString & svg, QRectF & viewBox, bool getViewBox) { QXmlStreamReader streamReader(svg); return parseForWidthAndHeight(streamReader, viewBox, getViewBox); } QSizeF TextUtils::parseForWidthAndHeight(QXmlStreamReader & svg) { QRectF viewBox; return parseForWidthAndHeight(svg, viewBox, false); } QSizeF TextUtils::parseForWidthAndHeight(QXmlStreamReader & svg, QRectF & viewBox, bool getViewBox) { svg.setNamespaceProcessing(false); QSizeF size(0, 0); viewBox.setRect(0, 0, 0, 0); bool isIllustrator = false; bool bad = false; while (!svg.atEnd() && !bad) { switch (svg.readNext()) { case QXmlStreamReader::Comment: if (!isIllustrator) { isIllustrator = TextUtils::isIllustratorFile(svg.text().toString()); } break; case QXmlStreamReader::StartElement: if (svg.name().toString().compare("svg") == 0) { QString ws = svg.attributes().value("width").toString(); QString hs = svg.attributes().value("height").toString(); bool okw, okh; double w = TextUtils::convertToInches(ws, &okw, isIllustrator); double h = TextUtils::convertToInches(hs, &okh, isIllustrator); if (!okw || qIsNaN(w) || qIsInf(w) || !okh || qIsNaN(h) || qIsInf(h)) { bad = true; break; } size.setWidth(w); size.setHeight(h); if (getViewBox) { bool gotViewBox = false; QString vb = svg.attributes().value("viewBox").toString(); QStringList vbs = vb.split(QRegExp(",| ")); if (vbs.count() == 4) { bool ok = false; double d[4]; for (int i = 0; i < 4; i++) { d[i] = vbs.at(i).toDouble(&ok); if (!ok) break; } if (ok) { gotViewBox = true; viewBox.setRect(d[0], d[1], d[2], d[3]); } } if (!gotViewBox) { chopNotDigits(hs); chopNotDigits(ws); viewBox.setRect(0, 0, ws.toDouble(), hs.toDouble()); } } return size; } break; default: break; } } return size; } void TextUtils::gornTree(QDomDocument & doc) { QDomElement root = doc.documentElement(); QString oldid = root.attribute("id"); root.setAttribute("id", "0"); root.setAttribute("gorn", "0"); root.setAttribute("oldid", oldid); gornTreeAux(root); } void TextUtils::gornTreeAux(QDomElement & root) { QString prefix = root.attribute("id"); QDomElement child = root.firstChildElement(); int index = 0; while (!child.isNull()) { QString oldid = child.attribute("id"); QString newid = QString("%1.%2").arg(prefix).arg(index++); child.setAttribute("id", newid); child.setAttribute("gorn", newid); child.setAttribute("oldid", oldid); gornTreeAux(child); child = child.nextSiblingElement(); } } bool TextUtils::elevateTransform(QDomElement & root) { QList transforms; collectTransforms(root, transforms); if (transforms.length() == 0) return false; foreach (QDomElement element, transforms) { QString transform = element.attribute("transform"); element.removeAttribute("transform"); QDomElement g = element.ownerDocument().createElement("g"); g.setAttribute("transform", transform); element.parentNode().insertBefore(g, element); g.appendChild(element); } return true; } void TextUtils::collectTransforms(QDomElement & root, QList & transforms) { QString transform = root.attribute("transform"); if (!transform.isEmpty()) { transforms.append(root); } QDomElement child = root.firstChildElement(); while (!child.isNull()) { collectTransforms(child, transforms); child = child.nextSiblingElement(); } } bool TextUtils::fixFonts(QString & svg, const QString & destFont, bool & reallyFixed) { bool changed = removeFontFamilySingleQuotes(svg); reallyFixed = fixUnavailableFontFamilies(svg, destFont); changed |= reallyFixed; return changed; } void TextUtils::fixStyleAttribute(QDomElement & element) { QString style = element.attribute("style"); if (style.isEmpty()) return; fixStyleAttribute(element, style, "stroke-width"); fixStyleAttribute(element, style, "stroke"); fixStyleAttribute(element, style, "fill"); fixStyleAttribute(element, style, "fill-opacity"); fixStyleAttribute(element, style, "stroke-opacity"); fixStyleAttribute(element, style, "font-size"); if (style.trimmed().isEmpty()) { element.removeAttribute("style"); } else { element.setAttribute("style", style); } //QString deleteMe; //QTextStream stream(&deleteMe); //stream << element; //DebugDialog::debug(deleteMe); } void TextUtils::fixStyleAttribute(QDomElement & element, QString & style, const QString & attributeName) { static const QString findStyle("%1[\\s]*:[\\s]*([^;]*)[;]?"); QString str = findStyle.arg(attributeName); QRegExp sw(str); if (sw.indexIn(style) >= 0) { QString value = sw.cap(1); style.remove(sw); element.setAttribute(attributeName, value); } } QString TextUtils::getRandText() { QString rand = QUuid::createUuid().toString(); QString randext = QCryptographicHash::hash(rand.toLatin1(),QCryptographicHash::Md4).toHex(); return randext; } /*QString TextUtils::getBase64RandText() { QString rand = QUuid::createUuid().toString(); QString randext = QCryptographicHash::hash(rand.toLatin1(),QCryptographicHash::Md4).toHex(); return randext; }*/ bool TextUtils::ensureViewBox(QDomDocument doc, double dpi, QRectF & rect, bool convert, double & w, double & h, bool getwh) { bool isIllustrator = TextUtils::isIllustratorDoc(doc); QDomElement root = doc.documentElement(); QString viewBox = root.attribute("viewBox"); if (viewBox.isEmpty() || getwh) { bool ok1, ok2; if (convert) { w = TextUtils::convertToInches(root.attribute("width"), &ok1, isIllustrator) * dpi; h = TextUtils::convertToInches(root.attribute("height"), &ok2, isIllustrator) * dpi; } else { w = root.attribute("width").toDouble(&ok1); h = root.attribute("height").toDouble(&ok2); } if (!ok1 || !ok2) { return false; } } if (viewBox.isEmpty()) { root.setAttribute("viewBox", QString("0 0 %1 %2").arg(w).arg(h)); rect.setRect(0, 0, w, h); return true; } QStringList coords = viewBox.split(QRegExp(" |,")); if (coords.count() != 4) return false; rect.setRect(coords.at(0).toDouble(), coords.at(1).toDouble(), coords.at(2).toDouble(), coords.at(3).toDouble()); return true; } QString TextUtils::findAnchor(const QDomElement & text) { if (text.isNull()) return "start"; QString anchor = text.attribute("text-anchor"); if (!anchor.isEmpty()) return anchor; return findAnchor(text.parentNode().toElement()); } void TextUtils::resplit(QStringList & names, const QString & split) { QStringList result; QString appender = split; if (appender == " ") appender = ""; foreach (QString name, names) { QStringList sub = name.split(split, QString::SkipEmptyParts); for (int i = 0; i < sub.count(); i++) { QString s = sub.at(i); if (i < sub.count() - 1) s.append(appender); result.append(s); } } names.clear(); names.append(result); } QString TextUtils::elementToString(const QDomElement & element) { QString string = "<"; string += element.tagName(); QDomNamedNodeMap attributes = element.attributes(); for (int i = 0; i < attributes.count(); i++) { QDomNode attribute = attributes.item(i); string += QString(" %1='%2'").arg(attribute.nodeName()).arg(attribute.nodeValue()); } string +="/>"; return string; } fritzing-app-0.9.3b/src/utils/textutils.h000066400000000000000000000227521272363601300204570ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6984 $: $Author: irascibl@gmail.com $: $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $ ********************************************************************/ #ifndef TEXTUTILS_H #define TEXTUTILS_H #include #include #include #include #include #include typedef QString (*CopyPinFunction)(int pin, const QString & argString, void * userData); typedef QString (*MultiplyPinFunction)(int pin, double increment, double value); class TextUtils { public: static QSet getRegexpCaptures(const QString &pattern, const QString &textToSearchIn); static QDomElement findElementWithAttribute(QDomElement element, const QString & attributeName, const QString & attributeValue); static void findElementsWithAttribute(QDomElement & element, const QString & att, QList & elements); static double convertToInches(const QString & string, bool * ok, bool isIllustrator); static double convertToInches(const QString & string); static QString convertToPowerPrefix(double); static double convertFromPowerPrefix(const QString & val, const QString & symbol); static double convertFromPowerPrefixU(QString & val, const QString & symbol); static QString replaceTextElement(const QString & svg, const QString & id, const QString & newValue); static QByteArray replaceTextElement(const QByteArray & svg, const QString & id, const QString & newValue); static QString replaceTextElements(const QString & svg, const QHash &); static bool squashElement(QDomDocument &, const QString & elementName, const QString & attName, const QRegExp & matchContent); static QString mergeSvg(const QString & svg1, const QString & svg2, const QString & id, bool flip); static QString mergeSvgFinish(QDomDocument & doc); static bool mergeSvg(QDomDocument & doc1, const QString & svg, const QString & id); static QString makeSVGHeader(double printerscale, double dpi, double width, double height); static bool isIllustratorFile(const QString &fileContent); static bool isIllustratorFile(const QByteArray &fileContent); static bool isIllustratorDoc(const QDomDocument & doc); static QString removeXMLEntities(QString svgContent); static bool cleanSodipodi(QString &bytes); static bool fixPixelDimensionsIn(QString &fileContent); static bool addCopper1(const QString & filename, QDomDocument & doc, const QString & srcAtt, const QString & destAtt); static void setSVGTransform(QDomElement &, QMatrix &); static QString svgMatrix(const QMatrix &); static QString svgMatrix(const QTransform &); static QString svgTransform(const QString & svg, QTransform & transform, bool translate, const QString extra); static bool getSvgSizes(QDomDocument & doc, double & sWidth, double & sHeight, double & vbWidth, double & vbHeight); static bool findText(const QDomNode & node, QString & text); static QString stripNonValidXMLCharacters(const QString & str); static QString convertExtendedChars(const QString & str); static QString escapeAnd(const QString &); static QMatrix elementToMatrix(QDomElement & element); static QMatrix transformStringToMatrix(const QString & transform); static QList getTransformFloats(QDomElement & element); static QList getTransformFloats(const QString & transform); static QString svgNSOnly(QString svgContent); static QString killXMLNS(QString svgContent); static void gWrap(QDomDocument & domDocument, const QHash & attributes); static void slamStrokeAndFill(QDomElement &, const QString & stroke, const QString & strokeWidth, const QString & fill); static QString slamStrokeAndFill(const QString & svg, const QString & stroke, const QString & strokeWidth, const QString & fill); static QString incrementTemplate(const QString & filename, int pins, double unitIncrement, MultiplyPinFunction, CopyPinFunction, void * userData); static QString incrementTemplateString(const QString & templateString, int pins, double increment, MultiplyPinFunction, CopyPinFunction, void * userData); static QString standardCopyPinFunction(int pin, const QString & argString, void * userData); static QString standardMultiplyPinFunction(int pin, double increment, double value); static QString noCopyPinFunction(int pin, const QString & argString, void * userData); static QString incMultiplyPinFunction(int pin, double increment, double value); static QString incCopyPinFunction(int pin, const QString & argString, void * userData); static QString negIncCopyPinFunction(int pin, const QString & argString, void * userData); static double getViewBoxCoord(const QString & svg, int coord); static QString makeLineSVG(QPointF p1, QPointF p2, double width, QString colorString, double dpi, double printerScale, bool blackOnly, bool dashed, const QVector &); static QString makeCubicBezierSVG(const QPolygonF & controlPoints, double width, QString colorString, double dpi, double printerScale, bool blackOnly, bool dashed, const QVector &); static QString makeRectSVG(QRectF r, QPointF offset, double dpi, double printerscale); static QString makePolySVG(const QPolygonF & poly, QPointF offset, double width, QString colorString, double dpi, double printerScale, bool blackOnly); static QPolygonF polygonFromElement(QDomElement & element); static QString pointToSvgString(QPointF p, QPointF offset, double dpi, double printerScale); static void replaceChildText(QDomNode & node, const QString & text); static void replaceElementChildText(QDomElement & root, const QString & elementName, const QString & text); static QString removeSVGHeader(QString & string); //static QString getMacAddress(); static QString expandAndFill(const QString & svg, const QString & color, double expandBy); static void expandAndFillAux(QDomElement &, const QString & color, double expandBy); static bool writeUtf8(const QString & fileName, const QString & text); static int getPinsAndSpacing(const QString & expectedFileName, QString & spacingString); static QSizeF parseForWidthAndHeight(QXmlStreamReader &, QRectF & viewBox, bool getViewBox); static QSizeF parseForWidthAndHeight(QXmlStreamReader &); static QSizeF parseForWidthAndHeight(const QString & svg, QRectF & viewBox, bool getViewBox); static QSizeF parseForWidthAndHeight(const QString & svg); static void gornTree(QDomDocument &); static bool elevateTransform(QDomElement &); static bool fixMuch(QString &svg, bool fixStrokeWidth); static bool fixInternalUnits(QString & svg); static bool fixFonts(QString & svg, const QString & destFont, bool & reallyFixed); static void fixStyleAttribute(QDomElement & element); static QString parseForModuleID(const QString & fzpXmlString); static QString parseFileForModuleID(const QString & fzpPath); static QString getRandText(); static bool ensureViewBox(QDomDocument doc, double dpi, QRectF & rect, bool toInches, double & w, double & h, bool getwh); static QString findAnchor(const QDomElement & text); static double getStrokeWidth(QDomElement &, double defaultValue); static void resplit(QStringList & names, const QString & split); static QString elementToString(const QDomElement &); public: static const QRegExp FindWhitespace; static const QString SMDFlipSuffix; static const QString MicroSymbol; static const ushort MicroSymbolCode; static const QString PowerPrefixesString; static const QString CreatedWithFritzingString; static const QString CreatedWithFritzingXmlComment; static void collectLeaves(QDomElement & element, int & index, QVector & leaves); static void collectLeaves(QDomElement & element, QList & leaves); static const QRegExp floatingPointMatcher; static const QString RegexFloatDetector; static const QString AdobeIllustratorIdentifier; protected: static bool pxToInches(QDomElement &elem, const QString &attrName, bool isIllustrator); static void squashNotElement(QDomElement & element, const QString & elementName, const QString & attName, const QRegExp & matchContent, bool & result); static void initPowerPrefixes(); static QDomElement copyText(QDomDocument & svgDom, QDomElement & parent, QDomElement & text, const QString & defaultX, const QString & defaultY, bool copyAttributes); static void gornTreeAux(QDomElement &); static bool noPatternAux(QDomDocument & svgDom, const QString & tag); static bool noUseAux(QDomDocument & svgDom); static bool tspanRemoveAux(QDomDocument & svgDom); static void fixStyleAttribute(QDomElement & element, QString & style, const QString & attributeName); static bool fixStrokeWidth(QDomDocument & svgDoc); static bool fixViewBox(QDomElement & root); static void chopNotDigits(QString &); static void collectTransforms(QDomElement & root, QList & transforms); }; #endif fritzing-app-0.9.3b/src/utils/zoomslider.cpp000066400000000000000000000200051272363601300211210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include #include #include #include #include #include #include #include #include #include "zoomslider.h" #include "../debugdialog.h" double ZoomSlider::ZoomStep; QList ZoomSlider::ZoomFactors; static const int MIN_VALUE = 10; static const int STARTING_VALUE = 100; static const int HEIGHT = 16; static const int STEP = 100; static int AutoRepeatDelay = 0; static int AutoRepeatInterval = 0; /////////////////////////////////////////////// ZoomLabel::ZoomLabel(QWidget * parent) : QLabel(parent) { if (AutoRepeatDelay == 0) { QPushButton button; AutoRepeatInterval = button.autoRepeatInterval(); AutoRepeatDelay = button.autoRepeatDelay(); } m_autoRepeat = m_mouseIsDown = m_mouseIsIn = m_repeated = false; m_timer.setSingleShot(false); m_timer.setInterval(AutoRepeatInterval); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) m_timer.setTimerType(Qt::PreciseTimer); #endif connect(&m_timer, SIGNAL(timeout()), this, SLOT(repeat())); } ZoomLabel::~ZoomLabel() { } void ZoomLabel::setAutoRepeat(bool autoRepeat) { m_autoRepeat = autoRepeat; } void ZoomLabel::setImages(const QString & normal, const QString & pressed) { m_normal.load(normal); m_pressed.load(pressed); setPixmap(m_normal); } void ZoomLabel::repeat() { if (m_mouseIsIn && m_mouseIsDown) { m_repeated = true; emit clicked(); } } void ZoomLabel::mousePressEvent(QMouseEvent * event) { m_timer.stop(); this->setMouseTracking(true); this->setPixmap(m_pressed); QLabel::mousePressEvent(event); m_mouseIsDown = m_mouseIsIn = true; m_repeated = false; if (m_autoRepeat) { m_timer.start(AutoRepeatDelay); } } void ZoomLabel::mouseMoveEvent(QMouseEvent * event) { QLabel::mouseMoveEvent(event); if (m_mouseIsDown) { QPoint p = event->pos(); bool mouseIsIn = p.x() >= 0 && p.y() >= 0 && p.x() <= width() && p.y() <= height(); if (m_mouseIsIn && !mouseIsIn) { m_timer.stop(); this->setPixmap(m_normal); m_mouseIsIn = false; } else if (!m_mouseIsIn && mouseIsIn) { this->setPixmap(m_pressed); m_mouseIsIn = true; m_timer.start(AutoRepeatDelay); } } } void ZoomLabel::mouseReleaseEvent(QMouseEvent * event) { m_timer.stop(); this->setMouseTracking(false); m_mouseIsDown = false; this->setPixmap(m_normal); QLabel::mouseReleaseEvent(event); if (!m_repeated && m_mouseIsIn) { emit clicked(); } } ///////////////////////////////////////////// ZoomSlider::ZoomSlider(int maxValue, QWidget * parent) : QFrame(parent) { // layout doesn't seem to work: the slider appears too far down in the status bar // because the status bar layout is privileged for the message text m_firstTime = true; if (ZoomFactors.size() == 0) { loadFactors(); } this->setObjectName("ZoomSliderFrame"); m_lineEdit = new QLineEdit(this); m_lineEdit->setObjectName("ZoomSliderValue"); m_lineEdit->setText(QString("%1").arg(STARTING_VALUE)); m_lineEdit->setValidator(new QIntValidator(MIN_VALUE, maxValue + MIN_VALUE, this)); m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, 0); m_lineEdit->setAlignment(Qt::AlignRight | Qt::AlignVCenter); m_suffix = new QLabel(tr("%"), this); m_suffix->setObjectName("ZoomSliderLabel"); m_minusButton = new ZoomLabel(this); m_minusButton->setImages(":/resources/images/icons/zoomSliderMinus.png", ":/resources/images/icons/zoomSliderMinusPressed.png"); m_minusButton->setAutoRepeat(true); m_minusButton->setObjectName("ZoomSliderButton"); connect(m_minusButton, SIGNAL(clicked()), this, SLOT(minusClicked())); m_slider = new QSlider(this); m_slider->setObjectName("ZoomSliderSlider"); m_slider->setOrientation(Qt::Horizontal); m_slider->setRange(MIN_VALUE, maxValue + MIN_VALUE); m_slider->setValue(STARTING_VALUE); m_slider->setTickPosition(QSlider::TicksBelow); m_slider->setTickInterval(500); m_plusButton = new ZoomLabel(this); m_plusButton->setImages(":/resources/images/icons/zoomSliderPlus.png", ":/resources/images/icons/zoomSliderPlusPressed.png"); m_plusButton->setAutoRepeat(true); m_plusButton->setObjectName("ZoomSliderButton"); connect(m_plusButton, SIGNAL(clicked()), this, SLOT(plusClicked())); connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); connect(m_lineEdit, SIGNAL(textEdited(const QString &)), this, SLOT(sliderTextEdited(const QString &))); //m_userStillWriting = false; //m_valueBackup = editText(); //m_indexBackup = itemIndex(m_valueBackup); } void ZoomSlider::loadFactors() { QFile file(":/resources/zoomfactors.txt"); file.open(QFile::ReadOnly); QTextStream stream( &file ); int lineNumber = 0; while(!stream.atEnd()) { QString line = stream.readLine(); if(lineNumber != 0) { ZoomFactors << line.toDouble(); } else { ZoomStep = line.toDouble(); } lineNumber++; } file.close(); } void ZoomSlider::setValue(double value) { QString newText = QString("%1").arg(qRound(value)); m_lineEdit->setText(newText); sliderTextEdited(newText, false); } double ZoomSlider::value() { return m_lineEdit->text().toDouble(); } void ZoomSlider::minusClicked() { step(-1); } void ZoomSlider::plusClicked() { step(1); } void ZoomSlider::step(int direction) { int minIndex = 0; double minDiff = std::numeric_limits::max(); double v = value(); for (int i = 0; i < ZoomFactors.count(); i++) { double f = ZoomFactors[i]; if (qAbs(f - v) < minDiff) { minDiff = qAbs(f - v); minIndex = i; } } minIndex += direction; if (minIndex < 0) { minIndex = 0; } else if (minIndex >= ZoomFactors.count()) { minIndex = ZoomFactors.count() - 1; } setValue(ZoomFactors[minIndex]); emit zoomChanged(ZoomFactors[minIndex]); } void ZoomSlider::sliderValueChanged(int newValue) { newValue = (newValue / 10) * 10; // make it so moving the slider outputs nice rounded values QString newText = QString("%1").arg(newValue); if (newText.compare(m_lineEdit->text()) != 0) { m_lineEdit->setText(newText); emit zoomChanged(newValue); } } void ZoomSlider::sliderTextEdited(const QString & newText) { sliderTextEdited(newText, true); } void ZoomSlider::sliderTextEdited(const QString & newText, bool doEmit) { int value = newText.toInt(); if (m_slider->value() != value) { disconnect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); m_slider->setValue(value); connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int))); if (doEmit) emit zoomChanged(value); } } void ZoomSlider::zoomIn () { plusClicked(); } void ZoomSlider::zoomOut () { minusClicked(); } void ZoomSlider::showEvent(QShowEvent * event) { // can't get QHLayout to work, so shoving widgets into place here // because widths aren't set at constructor time QFrame::showEvent(event); if (m_firstTime) { m_firstTime = false; int soFar = 0; m_lineEdit->move(soFar, -2); soFar += m_lineEdit->width(); m_suffix->move(soFar, 0); soFar += m_suffix->width(); m_minusButton->move(soFar, 0); soFar += m_minusButton->width(); m_slider->move(soFar, 0); soFar += m_slider->width(); m_plusButton->move(soFar, 0); } } fritzing-app-0.9.3b/src/utils/zoomslider.h000066400000000000000000000053551272363601300206010ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef ZOOMSLIDER_H_ #define ZOOMSLIDER_H_ #include #include #include #include #include #include #include #include class ZoomLabel : public QLabel { Q_OBJECT public: ZoomLabel(QWidget * parent); ~ZoomLabel(); void setImages(const QString & normal, const QString & pressed); void setAutoRepeat(bool); void mousePressEvent(QMouseEvent *); void mouseMoveEvent(QMouseEvent *); void mouseReleaseEvent(QMouseEvent *); signals: void clicked(); protected slots: void repeat(); protected: QTimer m_timer; QPixmap m_pressed; QPixmap m_normal; bool m_autoRepeat; bool m_mouseIsDown; bool m_mouseIsIn; bool m_repeated; }; class ZoomSlider: public QFrame { Q_OBJECT public: ZoomSlider(int maxValue, QWidget * parent=0); void setValue(double); double value(); void zoomOut(); void zoomIn(); protected slots: //void emitZoomChanged(); //void updateBackupFieldsIfOptionSelected(int index); void sliderValueChanged(int newValue); void sliderTextEdited(const QString & newText); void minusClicked(); void plusClicked(); protected: void step(int direction); void sliderTextEdited(const QString & newValue, bool doEmit); void showEvent(QShowEvent * event); static void loadFactors(); protected: //int itemIndex(QString value); //void updateBackupFields(); static QList ZoomFactors; //bool m_userStillWriting; //QString m_valueBackup; //int m_indexBackup; QSlider * m_slider; QLineEdit * m_lineEdit; ZoomLabel * m_plusButton; ZoomLabel * m_minusButton; QLabel * m_suffix; bool m_firstTime; public: static double ZoomStep; signals: void zoomChanged(double newZoom); }; #endif /* ZOOMSLIDER_H_ */ fritzing-app-0.9.3b/src/version/000077500000000000000000000000001272363601300165565ustar00rootroot00000000000000fritzing-app-0.9.3b/src/version/modfiledialog.cpp000066400000000000000000000024531272363601300220650ustar00rootroot00000000000000#include "modfiledialog.h" #include "ui_modfiledialog.h" #include ModFileDialog::ModFileDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ModFileDialog) { ui->setupUi(this); ui->decision->setText(tr("Fritzing can proceed with the update, " "but the set of files listed below must first be cleaned (removed or reset). " "It may take a few minutes. " "

    Do you want to proceed with cleaning these files?

    ")); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Clean files")); } ModFileDialog::~ModFileDialog() { delete ui; } void ModFileDialog::setText(const QString & text) { ui->label->setText(text); } void ModFileDialog::addList(const QString &header, const QStringList &list) { if (list.isEmpty()) return; if (ui->listWidget->count() > 0) { // add a separator ui->listWidget->addItem(" "); } ui->listWidget->addItem(header); foreach (QString string, list) { ui->listWidget->addItem(" " + string); } } void ModFileDialog::accept() { ui->buttonBox->setDisabled(true); ui->label->setText(tr("Now cleaning files. Please don't interrupt the process.")); QApplication::processEvents(); emit cleanRepo(this); } fritzing-app-0.9.3b/src/version/modfiledialog.h000066400000000000000000000011341272363601300215250ustar00rootroot00000000000000#ifndef MODFILEDIALOG_H #define MODFILEDIALOG_H #include namespace Ui { class ModFileDialog; } class ModFileDialog : public QDialog { Q_OBJECT public: explicit ModFileDialog(QWidget *parent = 0); ~ModFileDialog(); void setText(const QString & text); void addList(const QString & header, const QStringList & list); protected slots: /** * override the default closing behavior to send the cleanRepo signal */ void accept(); signals: void cleanRepo(ModFileDialog * modFileDialog); private: Ui::ModFileDialog *ui; }; #endif // MODFILEDIALOG_H fritzing-app-0.9.3b/src/version/modfiledialog.ui000066400000000000000000000034141272363601300217160ustar00rootroot00000000000000 ModFileDialog 0 0 400 300 Modified files decision Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() ModFileDialog accept() 248 254 157 274 buttonBox rejected() ModFileDialog reject() 316 260 286 274 fritzing-app-0.9.3b/src/version/partschecker.cpp000066400000000000000000000566771272363601300217650ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "partschecker.h" #include "version.h" #include "../debugdialog.h" #include "../utils/fmessagebox.h" #include #include #include #include #include #include #include #include #include #include #include static QString sBoilerPlate1; static QString sBoilerPlate2; static QString sNotInMasterBranch; static QString sDamaged; static QString sModifiedByGit; static QString sModified; static QString sUnreadable; void PartsCheckerResult::reset() { partsCheckerError = PARTS_CHECKER_ERROR_NONE; errorMessage.clear(); untrackedFiles.clear(); changedFiles.clear(); gitChangedFiles.clear(); unreadableFiles.clear(); } /** * Check whether the last commit in the remote repository matches the last commit stored in the database, * or check if the last commit in the remote repository matches the commit saved last time newPartsAvailable returned true * * repoPath is the local parts folder * remoteSha is returned */ bool PartsChecker::newPartsAvailable(const QString &repoPath, const QString & shaFromDatabase, bool atUserRequest, QString & remoteSha, PartsCheckerResult &partsCheckerResult) { // from https://github.com/libgit2/libgit2/blob/master/examples/network/ls-remote.c if (sBoilerPlate1.isEmpty()) { sBoilerPlate1 = "

    Fritzing cannot update the parts folder automatically.

    "; sBoilerPlate2 = sBoilerPlate1 + "

    If you used git to make changes, use git to update and then regenerate the database
    " "by selecting Part > Regenerate parts database....

    " "

    If you did not use git to make changes, then you may need to reinstall Fritzing.

    "; sNotInMasterBranch = QObject::tr("The parts folder '%1' has been changed--it is not in the master branch (%2). %3"); sDamaged = QObject::tr("The parts folder '%1' may have been damaged (%2). %3"); sModifiedByGit = QObject::tr("The parts folder '%1' has been changed (%2). %3"); sModified = QObject::tr("There are new or modified files in the parts folder '%1'. These changes will be discarded by the update."); sUnreadable = QObject::tr("There are unreadable files in the parts folder '%1'. %2"); } remoteSha = ""; git_repository *repository = NULL; git_remote *remote = NULL; int error; const git_remote_head **remote_heads; size_t remote_heads_len, i; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; bool available = false; partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_NONE; git_libgit2_init(); error = git_repository_open(&repository, repoPath.toUtf8().constData()); if (error) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = QObject::tr("Unable to open parts folder '%1' for update. %2").arg(repoPath).arg(sBoilerPlate1); goto cleanup; } if (git_repository_is_bare(repository)) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = QObject::tr("Parts folder repo '%1' is empty. %2").arg(repoPath).arg(sBoilerPlate1); goto cleanup; } // Find the remote by name error = git_remote_lookup(&remote, repository, "origin"); if (error) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = QObject::tr("Unable to determine network site for '%1'. %2").arg(repoPath).arg(sBoilerPlate1); goto cleanup; } /** * Connect to the remote. */ error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks); if (error) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_REMOTE; partsCheckerResult.errorMessage = QObject::tr("Unable to access network site for '%1'. %2").arg(repoPath).arg(sBoilerPlate1); goto cleanup; } /** * Get the list of references on the remote */ error = git_remote_ls(&remote_heads, &remote_heads_len, remote); if (error) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_REMOTE; partsCheckerResult.errorMessage = QObject::tr("Unable to retrieve network references for '%1'. %2").arg(repoPath).arg(sBoilerPlate1); goto cleanup; } for (i = 0; i < remote_heads_len; i++) { // we only care about the master branch if (strcmp(remote_heads[i]->name, "refs/heads/master") == 0) { char oid[GIT_OID_HEXSZ + 1] = {0}; git_oid_fmt(oid, &remote_heads[i]->oid); QString soid(oid); remoteSha = soid; QSettings settings; QString lastPartsSha = settings.value("lastPartsSha", "").toString(); settings.setValue("lastPartsSha", soid); // if soid matches database or matches the last time we notified the user, don't notify again available = atUserRequest ? (soid != shaFromDatabase) : (soid != lastPartsSha && soid != shaFromDatabase); if (available) { checkIfClean(repoPath, shaFromDatabase, repository, partsCheckerResult); } goto cleanup; } } partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_REMOTE; partsCheckerResult.errorMessage = QObject::tr("Unable to retrieve master network reference for '%1'. %2").arg(repoPath).arg(sBoilerPlate1); cleanup: if (remote) git_remote_free(remote); if (repository) git_repository_free(repository); git_libgit2_shutdown(); return available; } /** * looks for modified or untracked files in the repo */ bool PartsChecker::checkIfClean(const QString & repoPath, const QString & shaFromDatabase, git_repository * repository, PartsCheckerResult & partsCheckerResult) { // check local branch is master // check local git status is ok // check that last commit matches the one in the database git_reference *head = NULL; git_reference * resolved_head = NULL; git_ref_t ref_type; QString branchName; bool result = false; git_status_list *status_list = NULL; git_status_options status_options = GIT_STATUS_OPTIONS_INIT; int errorNumber = 0; QString currentSha; char buffer[GIT_OID_HEXSZ + 1] = {0}; errorNumber++; int error = git_repository_head(&head, repository); if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND) { // HEAD state changed, assume a git operation partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_USED_GIT; partsCheckerResult.errorMessage = sNotInMasterBranch.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } errorNumber++; if (error) { // still dealing with git_repository_head error partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = sDamaged.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } errorNumber++; branchName = git_reference_shorthand(head); if (branchName != "master") { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_USED_GIT; partsCheckerResult.errorMessage = sNotInMasterBranch.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } errorNumber++; ref_type = git_reference_type(head); if (ref_type == GIT_REF_SYMBOLIC) { error = git_reference_resolve(&resolved_head, head); if (error) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = sDamaged.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } const git_oid * oid = git_reference_target(resolved_head); git_oid_fmt(buffer, oid); currentSha = buffer; } else if (ref_type == GIT_REF_OID) { // Get the hash const git_oid * oid = git_reference_target(head); git_oid_fmt(buffer, oid); currentSha = buffer; } else { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = sDamaged.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } if (currentSha != shaFromDatabase) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_USED_GIT; partsCheckerResult.errorMessage = sModifiedByGit.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } errorNumber++; status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; error = git_status_list_new(&status_list, repository, &status_options); if (error) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = sDamaged.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } getChanges(status_list, partsCheckerResult); if (partsCheckerResult.unreadableFiles.count() > 0) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_DAMAGE; partsCheckerResult.errorMessage = sUnreadable.arg(repoPath).arg(sBoilerPlate1); goto cleanup; } if (partsCheckerResult.gitChangedFiles.count() > 0) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_USED_GIT; partsCheckerResult.errorMessage = sModifiedByGit.arg(repoPath).arg(errorNumber).arg(sBoilerPlate2); goto cleanup; } if (partsCheckerResult.changedFiles.count() > 0) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_MODS; partsCheckerResult.errorMessage = sModified.arg(repoPath); result = true; goto cleanup; } if (partsCheckerResult.untrackedFiles.count() > 0) { partsCheckerResult.partsCheckerError = PARTS_CHECKER_ERROR_LOCAL_MODS; partsCheckerResult.errorMessage = sModified.arg(repoPath); result = true; goto cleanup; } result = true; cleanup: if (head) git_reference_free(head); if (resolved_head) git_reference_free(resolved_head); if (status_list) git_status_list_free(status_list); return result; } /** * gets the details about modified or new files in the repo */ void PartsChecker::getChanges(git_status_list *status_list, PartsCheckerResult & partsCheckerResult) { // from https://libgit2.github.com/libgit2/ex/HEAD/status.html size_t i, maxi = git_status_list_entrycount(status_list); const git_status_entry *s; for (i = 0; i < maxi; ++i) { s = git_status_byindex(status_list, i); if (s->status == GIT_STATUS_CURRENT) { continue; } if (s->status == GIT_STATUS_IGNORED) { continue; } if (s->status == GIT_STATUS_WT_NEW) { partsCheckerResult.untrackedFiles << s->index_to_workdir->old_file.path; continue; } QString path; if (s->head_to_index) { path = s->head_to_index->old_file.path; } if (s->index_to_workdir) { if (path.isEmpty()) { path = s->index_to_workdir->old_file.path; } } if ((s->status & GIT_STATUS_INDEX_NEW) || (s->status & GIT_STATUS_INDEX_MODIFIED) || (s->status & GIT_STATUS_INDEX_DELETED) || (s->status & GIT_STATUS_INDEX_RENAMED) || (s->status & GIT_STATUS_INDEX_TYPECHANGE) || (s->status & GIT_STATUS_CONFLICTED)) { partsCheckerResult.gitChangedFiles << path; continue; } if ((s->status & GIT_STATUS_WT_MODIFIED) || (s->status & GIT_STATUS_WT_DELETED) || (s->status & GIT_STATUS_WT_RENAMED) || (s->status & GIT_STATUS_WT_TYPECHANGE)) { partsCheckerResult.changedFiles << path; continue; } if (s->status & GIT_STATUS_WT_UNREADABLE) { partsCheckerResult.unreadableFiles << path; continue; } } } /** * git reset --HARD and delete untracked files in the repo */ bool PartsChecker::cleanRepo(const QString & repoPath, const PartsCheckerResult & partsCheckerResult) { git_repository *repository = NULL; git_reference *head = NULL; bool result = false; git_oid oid; /* the SHA1 for last commit */ git_object *object = NULL; QDir repoDir(repoPath); git_libgit2_init(); int error = git_repository_open(&repository, repoPath.toUtf8().constData()); if (error) { goto cleanup; } error = git_reference_name_to_id(&oid, repository, "HEAD" ); if (error) { goto cleanup; } error = git_object_lookup(&object, repository, &oid, GIT_OBJ_ANY); if (error) { goto cleanup; } error = git_reset(repository, object, GIT_RESET_HARD, NULL); if (error) { goto cleanup; } foreach (QString fileName, partsCheckerResult.untrackedFiles) { bool success = QFile::remove(repoDir.absoluteFilePath(fileName)); if (!success) { goto cleanup; } } result = true; cleanup: if (object) git_object_free(object); if (head) git_reference_free(head); if (repository) git_repository_free(repository); git_libgit2_shutdown(); return result; } /** * gets the sha1 hash for the HEAD */ QString PartsChecker::getSha(const QString & repoPath) { QString sha; git_repository * repository = NULL; char buffer[GIT_OID_HEXSZ + 1] = {0}; git_libgit2_init(); int error = git_repository_open(&repository, repoPath.toUtf8().constData()); if (error) { FMessageBox::warning(NULL, QObject::tr("Regenerating parts database"), QObject::tr("Unable to find parts git repository")); goto cleanup; } git_oid oid; /* hold the SHA1 for last commit */ /* resolve HEAD into a SHA1 */ error = git_reference_name_to_id( &oid, repository, "HEAD" ); if (error) { FMessageBox::warning(NULL, QObject::tr("Regenerating parts database"), QObject::tr("Unable to find parts git repository HEAD")); goto cleanup; } git_oid_fmt(buffer, &oid); sha = QString(buffer); cleanup: git_repository_free(repository); git_libgit2_shutdown(); return sha; } static int transfer_progress_cb(const git_transfer_progress *stats, void *payload) { double progress = 0; if (stats->received_objects == stats->total_objects) { progress = 0.5 + (0.5 * stats->indexed_deltas / stats->total_deltas); } else if (stats->total_objects > 0) { progress = 0.5 * stats->received_objects / stats->total_objects; } if (payload) { PartsCheckerUpdateInterface * interface = static_cast(payload); if (interface) { interface->updateProgress(progress); } } return 0; } /** * git pull origin master */ bool PartsChecker::updateParts(const QString & repoPath, const QString & remoteSha, PartsCheckerUpdateInterface * interface) { // from https://github.com/libgit2/libgit2/blob/master/examples/network/fetch.c // useful for testing: http://stackoverflow.com/questions/3489173/how-to-clone-git-repository-with-specific-revision-changeset static char *refspec = const_cast("master:refs/heads/master"); bool ok = false; git_remote *remote = NULL; git_repository * repository = NULL; git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; if (remoteSha.isEmpty()) { DebugDialog::debug("Missing remoteSha"); return false; } git_libgit2_init(); int error = git_repository_open(&repository, repoPath.toUtf8().constData()); if (error) { DebugDialog::debug("unable to open repo " + repoPath); goto cleanup; } // Find the remote by name error = git_remote_lookup(&remote, repository, "origin"); if (error) { DebugDialog::debug("unable to lookup repo " + repoPath); goto cleanup; } // Set up the callbacks fetch_opts.callbacks.transfer_progress = transfer_progress_cb; fetch_opts.callbacks.payload = interface; /** * Perform the fetch with the configured refspecs from the * config. Update the reflog for the updated references with * "fetch". */ git_strarray refspecs; refspecs.count = 1; refspecs.strings = &refspec; error = git_remote_fetch(remote, NULL /* &refspecs */, &fetch_opts, "fetch"); if (error) { DebugDialog::debug("unable to fetch " + repoPath); goto cleanup; } // for debugging //const git_transfer_progress *stats; //stats = git_remote_stats(remote); error = doMerge(repository, remoteSha); ok = (error == 0); cleanup: git_remote_free(remote); git_repository_free(repository); git_libgit2_shutdown(); return ok; } /** * merge after the fetch */ int PartsChecker::doMerge(git_repository * repository, const QString & remoteSha) { // from https://github.com/libgit2/libgit2/blob/master/tests/merge/workdir/simple.c // https://github.com/MichaelBoselowitz/pygit2-examples/blob/68e889e50a592d30ab4105a2e7b9f28fac7324c8/examples.py#L58 git_oid their_oids[1]; git_annotated_commit *their_heads[1]; git_merge_options merge_options = GIT_MERGE_OPTIONS_INIT; git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT; bool afterMerge = false; git_commit *head_commit = NULL; git_commit *remote_commit = NULL; git_signature * signature = NULL; git_tree * saved_tree = NULL; git_reference * master_reference = NULL; git_reference * new_master_reference = NULL; git_reference * head_reference = NULL; git_reference * new_head_reference = NULL; git_index * index = NULL; //char buffer[GIT_OID_HEXSZ + 1] = {0}; merge_options.file_favor = GIT_MERGE_FILE_FAVOR_THEIRS; checkout_options.checkout_strategy = GIT_CHECKOUT_FORCE; int error = git_oid_fromstr(&their_oids[0], remoteSha.toUtf8().constData()); if (error) { DebugDialog::debug("unable to convert their oid"); goto cleanup; } error = git_annotated_commit_lookup(&their_heads[0], repository, &their_oids[0]); if (error) { DebugDialog::debug("unable to find their commit"); goto cleanup; } git_merge_analysis_t merge_analysis_t; git_merge_preference_t merge_preference_t; error = git_merge_analysis(&merge_analysis_t, &merge_preference_t, repository, (const git_annotated_commit **) their_heads, 1); if (error) { DebugDialog::debug("analysis failed"); goto cleanup; } if (merge_analysis_t & GIT_MERGE_ANALYSIS_UP_TO_DATE) { // no more work needs to be done DebugDialog::debug("already up to date"); goto cleanup; } error = git_commit_lookup(&remote_commit, repository, &their_oids[0]); if (error) { DebugDialog::debug("unable to find remote parent commit"); goto cleanup; } if (merge_analysis_t & GIT_MERGE_ANALYSIS_FASTFORWARD || merge_analysis_t & GIT_MERGE_ANALYSIS_UNBORN) { error = git_checkout_tree(repository, (git_object *) remote_commit, &checkout_options); if (error) { DebugDialog::debug("ff checkout failed"); goto cleanup; } error = git_reference_lookup(&master_reference, repository, "refs/heads/master"); if (error) { DebugDialog::debug("master ref lookup failed"); goto cleanup; } error = git_reference_set_target(&new_master_reference, master_reference, &their_oids[0], "Update parts"); if (error) { DebugDialog::debug("ref set target failed"); goto cleanup; } error = git_repository_head(&head_reference, repository); if (error) { DebugDialog::debug("head ref lookup failed"); goto cleanup; } error = git_reference_set_target(&new_head_reference, head_reference, &their_oids[0], "Update parts"); if (error) { DebugDialog::debug("head ref set target failed"); goto cleanup; } goto cleanup; } error = git_merge(repository, (const git_annotated_commit **) their_heads, 1, &merge_options, &checkout_options); afterMerge = true; if (error) { DebugDialog::debug("merge failed"); goto cleanup; } error = git_repository_index(&index, repository); if (error) { DebugDialog::debug("unable to get index"); goto cleanup; } // we are ignoring conflicts... error = git_signature_now(&signature, "Fritzing", "info@fritzing.org"); if (error) { DebugDialog::debug("signature failed"); goto cleanup; } git_oid saved_oid; error = git_index_write_tree(&saved_oid, index); if (error) { DebugDialog::debug("save index failed"); goto cleanup; } error = git_tree_lookup(&saved_tree, repository, &saved_oid); if (error) { DebugDialog::debug("save tree lookup failed"); goto cleanup; } error = git_repository_head(&head_reference, repository); if (error) { DebugDialog::debug("head ref lookup failed"); goto cleanup; } { // put initialization in scope due to compiler complaints const git_oid * head_oid = git_reference_target(head_reference); error = git_commit_lookup(&head_commit, repository, head_oid); if (error) { DebugDialog::debug("head lookup failed"); goto cleanup; } } git_oid new_commit_id; error = git_commit_create_v(&new_commit_id, repository, "HEAD", signature, signature, NULL, "Update parts", saved_tree, 2, head_commit, remote_commit); if (error) { DebugDialog::debug("final commit failed"); goto cleanup; } cleanup: if (afterMerge) { git_repository_state_cleanup(repository); } git_index_free(index); git_reference_free(master_reference); git_reference_free(new_master_reference); git_reference_free(head_reference); git_reference_free(new_head_reference); git_signature_free(signature); git_tree_free(saved_tree); git_commit_free(head_commit); git_commit_free(remote_commit); git_annotated_commit_free(their_heads[0]); return error; } fritzing-app-0.9.3b/src/version/partschecker.h000066400000000000000000000057111272363601300214110ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef PARTSCHECKER_H #define PARTSCHECKER_H #include #include class PartsCheckerUpdateInterface { public: // progress ranges from 0 to 1 virtual void updateProgress(double progress) = 0; }; enum PartsCheckerError { PARTS_CHECKER_ERROR_NONE = 0, PARTS_CHECKER_ERROR_REMOTE, PARTS_CHECKER_ERROR_USED_GIT, PARTS_CHECKER_ERROR_LOCAL_DAMAGE, PARTS_CHECKER_ERROR_LOCAL_MODS }; struct PartsCheckerResult { PartsCheckerError partsCheckerError; QString errorMessage; QStringList untrackedFiles; QStringList changedFiles; QStringList gitChangedFiles; QStringList unreadableFiles; void reset(); }; class PartsChecker { public: /** * returns the sha1 for the last commit; used only when creating the parts db */ static QString getSha(const QString & repoPath); /** * Check if there are any new parts available (since the last new parts check) * * remoteSha and PartsErrorResult are also returned */ static bool newPartsAvailable(const QString &repoPath, const QString &shaFromDataBase, bool atUserRequest, QString &remoteSha, PartsCheckerResult & partsCheckerResult); /** * trigger the update, with progress going to the PartsCheckerUpdateInterface */ static bool updateParts(const QString & repoPath, const QString & remoteSha, PartsCheckerUpdateInterface *); /** * Remove untracked files (as listed in partsCheckerResult) and perform git reset --hard */ static bool cleanRepo(const QString & repoPath, const PartsCheckerResult & partsCheckerResult); protected: static int doMerge(struct git_repository * repository, const QString & remoteSha); static bool checkIfClean(const QString & repoPath, const QString &shaFromDatabase, struct git_repository *repository, PartsCheckerResult &partsCheckerResult); static void getChanges(struct git_status_list *status_list, PartsCheckerResult & partsCheckerResult); }; #endif // PARTSCHECKER_H fritzing-app-0.9.3b/src/version/updatedialog.cpp000066400000000000000000000347231272363601300217350ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ // much code borrowed from Qt's rsslisting example #include "updatedialog.h" #include "version.h" #include "versionchecker.h" #include "modfiledialog.h" #include "../debugdialog.h" #include #include #include #include #include #include #include static const int s_maxProgress = 1000; static QString sUpdatePartsMessage; UpdateDialog::UpdateDialog(QWidget *parent) : QDialog(parent) { m_versionChecker = NULL; m_doQuit = false; m_doClose = true; this->setWindowTitle(QObject::tr("Check for updates")); if (sUpdatePartsMessage.isEmpty()) { sUpdatePartsMessage = tr("

    There is a parts library update available!

    " "

    Would you like Fritzing to download and install the update now?
    " "See the list of changes here.

    " "

    Note: the update may take some minutes and you will have to restart Fritzing.
    " "You can also update later via the Help → Check for Updates menu.

    "); } QVBoxLayout * vLayout = new QVBoxLayout(this); m_feedbackLabel = new QLabel(); m_feedbackLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); m_feedbackLabel->setOpenExternalLinks(true); m_feedbackLabel->setTextFormat(Qt::RichText); vLayout->addWidget(m_feedbackLabel); m_progressBar = new QProgressBar(); m_progressBar->setMinimum(0); m_progressBar->setMaximum(s_maxProgress); vLayout->addWidget(m_progressBar); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close")); m_buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Update parts")); connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(stopClose())); connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(updateParts())); vLayout->addWidget(m_buttonBox); this->setLayout(vLayout); } UpdateDialog::~UpdateDialog() { if (m_versionChecker) { delete m_versionChecker; } } bool UpdateDialog::setAvailableReleases(const QList & availableReleases) { AvailableRelease * interimRelease = NULL; AvailableRelease * mainRelease = NULL; foreach (AvailableRelease * availableRelease, availableReleases) { if (availableRelease->interim && (interimRelease == NULL)) { interimRelease = availableRelease; continue; } if (!availableRelease->interim && (mainRelease == NULL)) { mainRelease = availableRelease; continue; } if (mainRelease != NULL && interimRelease != NULL) break; } if (mainRelease == NULL && interimRelease == NULL) { if (m_atUserRequest) { m_feedbackLabel->setText(tr("

    No new versions found.

    ")); } return false; } QString style; QFile css(":/resources/styles/updatedialog.css"); if (css.open(QIODevice::ReadOnly)) { style = css.readAll(); css.close(); } QString text = QString("").arg(style); QSettings settings; if (mainRelease) { text += genTable(tr("A new main release is available for downloading:"), mainRelease); settings.setValue("lastMainVersionChecked", mainRelease->versionString); } if (interimRelease) { text += genTable(tr("A new interim release is available for downloading:"), interimRelease); settings.setValue("lastInterimVersionChecked", interimRelease->versionString); } text += ""; m_feedbackLabel->setText(text); return true; } void UpdateDialog::setVersionChecker(VersionChecker * versionChecker) { if (m_versionChecker != NULL) { m_versionChecker->stop(); delete m_versionChecker; m_versionChecker = NULL; } m_progressBar->setVisible(false); m_progressBar->setValue(0); // hide the update button, since it is only available under certain circumstances m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(false); m_feedbackLabel->setText(tr("

    Checking for new releases...

    ")); m_buttonBox->setEnabled(false); m_versionChecker = versionChecker; connect(m_versionChecker, SIGNAL(releasesAvailable()), this, SLOT(releasesAvailableSlot())); connect(m_versionChecker, SIGNAL(xmlError(QXmlStreamReader::Error)), this, SLOT(xmlErrorSlot(QXmlStreamReader::Error))); connect(m_versionChecker, SIGNAL(httpError(QNetworkReply::NetworkError)), this, SLOT(httpErrorSlot(QNetworkReply::NetworkError))); m_versionChecker->fetch(); } void UpdateDialog::releasesAvailableSlot() { bool available = setAvailableReleases(m_versionChecker->availableReleases()); if (available) { m_buttonBox->setEnabled(true); m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(true); if (!this->isVisible()) { this->exec(); } return; } bool canWrite = false; QDir repoDir(m_repoPath); QFile permissionTest(repoDir.absoluteFilePath("test.txt")); if (permissionTest.open(QFile::WriteOnly)) { qint64 count = permissionTest.write("a"); permissionTest.close(); permissionTest.remove(); if (count > 0) { QFile db(repoDir.absoluteFilePath("parts.db")); if (db.open(QFile::Append)) { canWrite = true; db.close(); } } } if (!canWrite) { m_feedbackLabel->setText(tr("

    Fritzing is unable to check for--and update--new parts.
    " "If you want this functionality, please enable write permission on this folder:
    '%1'.

    " ).arg(m_repoPath)); m_buttonBox->setEnabled(true); m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(false); if (!this->isVisible()) { // we are doing the parts check silently, so enable manual update by sending signal // otherwise manual update is enabled by closing the dialog emit enableAgainSignal(true); } return; } m_feedbackLabel->setText(tr("

    Checking for new parts...

    ")); m_doClose = false; m_partsCheckerResult.reset(); available = PartsChecker::newPartsAvailable(m_repoPath, m_shaFromDataBase, m_atUserRequest, m_remoteSha, m_partsCheckerResult); m_doClose = true; if (!available) { m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(false); m_buttonBox->setEnabled(true); if (m_partsCheckerResult.errorMessage.isEmpty()) { m_feedbackLabel->setText(tr("

    No new releases or new parts found

    ")); } else { m_feedbackLabel->setText(m_partsCheckerResult.errorMessage); } if (!this->isVisible()) { // we are doing the parts check silently, so enable manual update by sending signal // otherwise manual update is enabled by closing the dialog emit enableAgainSignal(true); } return; } switch (m_partsCheckerResult.partsCheckerError) { case PARTS_CHECKER_ERROR_NONE: m_feedbackLabel->setText(sUpdatePartsMessage.arg(m_shaFromDataBase)); m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(true); break; case PARTS_CHECKER_ERROR_REMOTE: case PARTS_CHECKER_ERROR_LOCAL_DAMAGE: case PARTS_CHECKER_ERROR_USED_GIT: m_feedbackLabel->setText(m_partsCheckerResult.errorMessage); break; case PARTS_CHECKER_ERROR_LOCAL_MODS: // pop up secondary dialog listing the files { ModFileDialog modFileDialog(this->parentWidget()); modFileDialog.setText(m_partsCheckerResult.errorMessage); modFileDialog.addList(tr("New files:"), m_partsCheckerResult.untrackedFiles); modFileDialog.addList(tr("Modified Files:"), m_partsCheckerResult.changedFiles); connect(&modFileDialog, SIGNAL(cleanRepo(ModFileDialog *)), this, SLOT(onCleanRepo(ModFileDialog *))); int result = modFileDialog.exec(); if (result == QDialog::Rejected) { if (this->isVisible()) { this->hide(); } // we are doing the parts check silently, so enable manual update by sending signal // otherwise manual update is enabled by closing the dialog emit enableAgainSignal(true); return; } // if we got here, then cleaning the repo worked and we can proceed to the update } break; } m_buttonBox->setEnabled(true); if (!this->isVisible()) { this->exec(); } } void UpdateDialog::onCleanRepo(ModFileDialog * modFileDialog) { if (!PartsChecker::cleanRepo(m_repoPath, m_partsCheckerResult)) { QMessageBox::warning(this->parentWidget(), "Update Parts", tr("Fritzing was unable to clean the files, so the update cannot proceed.
    " "You may have to reinstall Fritzing.")); // we are doing the parts check silently, so enable manual update by sending signal // otherwise manual update is enabled by closing the dialog emit enableAgainSignal(true); modFileDialog->done(QDialog::Rejected); return; } modFileDialog->done(QDialog::Accepted); m_feedbackLabel->setText(sUpdatePartsMessage.arg(m_shaFromDataBase)); m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(true); } void UpdateDialog::httpErrorSlot(QNetworkReply::NetworkError) { handleError(); } void UpdateDialog::xmlErrorSlot(QXmlStreamReader::Error errorCode) { Q_UNUSED(errorCode); handleError(); } void UpdateDialog::handleError() { DebugDialog::debug("handle error"); m_feedbackLabel->setText(tr("

    Sorry, unable to retrieve update info

    ")); emit enableAgainSignal(true); DebugDialog::debug("handle error done"); } void UpdateDialog::httpPartsErrorSlot(QString error) { handlePartsError(error); } void UpdateDialog::jsonPartsErrorSlot(QString error) { handlePartsError(error); } void UpdateDialog::handlePartsError(const QString & error) { DebugDialog::debug("handle error " + error); m_feedbackLabel->setText(tr("

    Sorry, unable to retrieve parts update info

    ")); emit enableAgainSignal(true); } void UpdateDialog::setAtUserRequest(bool atUserRequest) { m_atUserRequest = atUserRequest; } void UpdateDialog::stopClose() { m_versionChecker->stop(); this->close(); emit enableAgainSignal(true); } void UpdateDialog::closeEvent(QCloseEvent * event) { if (!m_doClose) { event->ignore(); return; } if (m_doQuit) { QTimer::singleShot(1, Qt::PreciseTimer, qApp, SLOT(quit())); } QDialog::closeEvent(event); } QString UpdateDialog::genTable(const QString & title, AvailableRelease * release) { return QString( "

    " "

    %1

    " "
    " "" "" "" "" "
    Version %2(%3)" "
    " "
    %5
    " "
    " "

    " ) .arg(title) .arg(release->versionString) .arg(release->dateTime.toString(Qt::DefaultLocaleShortDate)) .arg(release->link) .arg(release->summary.replace("changelog:", "", Qt::CaseInsensitive)); } void UpdateDialog::setRepoPath(const QString & repoPath, const QString & shaFromDataBase) { m_repoPath = repoPath; m_shaFromDataBase = shaFromDataBase; } void UpdateDialog::updateParts() { m_doClose = false; m_buttonBox->setDisabled(true); m_progressBar->setValue(0); m_progressBar->setVisible(true); m_feedbackLabel->setText(tr("

    Downloading new parts...

    ")); bool result = PartsChecker::updateParts(m_repoPath, m_remoteSha, this); m_buttonBox->button(QDialogButtonBox::Ok)->setVisible(false); if (!result) { m_doClose = true; m_progressBar->setVisible(false); m_buttonBox->setEnabled(true); m_feedbackLabel->setText(tr("

    Sorry, unable to download new parts

    ")); return; } m_feedbackLabel->setText(tr("

    Installing new parts. This may take a few minutes.
    Please do not interrupt the process, as your parts folder could be damaged.

    ")); m_progressBar->setValue(0); m_progressBar->setMinimum(0); m_progressBar->setMaximum(0); emit installNewParts(); } void UpdateDialog::updateProgress(double progress) { m_progressBar->setValue(progress * s_maxProgress); qApp->processEvents(); } void UpdateDialog::installFinished(const QString & error) { m_progressBar->setVisible(false); m_buttonBox->setEnabled(true); if (error.isEmpty()) { m_feedbackLabel->setText(tr("

    New parts successfully installed!

    " "

    Fritzing must be restarted, so the 'Close' button will close Fritzing.
    " "The new parts will be available when you run Fritzing again.

    ")); } else { m_feedbackLabel->setText(tr("

    Sorry, unable to install new parts: %1
    " "Fritzing must nevertheless be restarted, " "so the 'Close' button will close Fritzing.

    ").arg(error)); } m_doQuit = m_doClose = true; } fritzing-app-0.9.3b/src/version/updatedialog.h000066400000000000000000000052211272363601300213710ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef UPDATEDIALOG_H #define UPDATEDIALOG_H #include #include #include #include #include #include #include #include "partschecker.h" class UpdateDialog : public QDialog, public PartsCheckerUpdateInterface { Q_OBJECT public: UpdateDialog(QWidget *parent = 0); ~UpdateDialog(); void setVersionChecker(class VersionChecker *); void setAtUserRequest(bool); void setRepoPath(const QString & repoPath, const QString & shaFromDataBase); void updateProgress(double progress); void installFinished(const QString & error); signals: void enableAgainSignal(bool enable); void installNewParts(); protected slots: void releasesAvailableSlot(); void xmlErrorSlot(QXmlStreamReader::Error errorCode); void httpErrorSlot(QNetworkReply::NetworkError); void jsonPartsErrorSlot(QString error); void httpPartsErrorSlot(QString error); void stopClose(); void updateParts(); void onCleanRepo(class ModFileDialog *modFileDialog); protected: bool setAvailableReleases(const QList & availableReleases); void handleError(); void handlePartsError(const QString & error); QString genTable(const QString & title, struct AvailableRelease *); void closeEvent(QCloseEvent *); protected: class VersionChecker * m_versionChecker; bool m_atUserRequest; QString m_repoPath; QString m_shaFromDataBase; QString m_remoteSha; QLabel * m_feedbackLabel; QDialogButtonBox * m_buttonBox; QProgressBar * m_progressBar; PartsCheckerResult m_partsCheckerResult; bool m_doQuit; bool m_doClose; }; #endif fritzing-app-0.9.3b/src/version/version.cpp000066400000000000000000000150141272363601300207500ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty ofswap 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 Fritzing. If not, see . ********************************************************************* $Revision: 9268 $: $Author: irascibl@gmail.com $: $Date: 2014-06-11 17:47:21 +0200 (We, 11. Jun 2013) $ ********************************************************************/ #include "version.h" #include #include #include #include #include "../debugdialog.h" #include "../utils/textutils.h" #include "../lib/qtsysteminfo/QtSystemInfo.h" QString Version::m_majorVersion("0"); QString Version::m_minorVersion("9"); QString Version::m_minorSubVersion("3"); QString Version::m_modifier("b"); QString Version::m_gitCommit("5c895d327c44a3114e5fcc9d8260daf0cbb52806"); QString Version::m_gitDate("2016-04-19T21:25:37Z"); // want standard ISO form QString Version::m_revision; QString Version::m_date; QString Version::m_shortDate; QString Version::m_versionString; QString Version::m_year; QStringList Version::m_modifiers; Version * Version::m_singleton = new Version(); QString Version::FirstVersionWithDetachedUserData = "0.3.1b.05.26.3016"; Version::Version() { if (m_modifiers.count() == 0) { m_modifiers << "a" << "b" << "rc" << ""; } m_revision = m_gitCommit; QStringList strings; strings = m_gitDate.split("T", QString::SkipEmptyParts); if (strings.size() >= 2) { m_date = strings[0]; strings = m_date.split("-", QString::SkipEmptyParts); if (strings.size() >= 3) { m_shortDate = strings[1] + "." + strings[2]; m_year = strings[0]; } } m_versionString = QString("%1.%2.%3%4.%5.%6").arg(m_majorVersion).arg(m_minorVersion).arg(m_minorSubVersion).arg(m_modifier).arg(m_shortDate).arg(m_revision); } const QString & Version::majorVersion() { return m_majorVersion; } const QString & Version::minorVersion() { return m_minorVersion; } const QString & Version::minorSubVersion() { return m_minorSubVersion; } const QString & Version::revision() { return m_revision; } const QString & Version::modifier() { return m_modifier; } const QString & Version::versionString() { return m_versionString; } const QString & Version::fullDate() { return m_gitDate; } const QString & Version::date() { return m_date; } const QString & Version::shortDate() { return m_shortDate; } const QString & Version::year() { return m_year; } bool Version::candidateGreaterThanCurrent(const VersionThing & candidateVersionThing) { VersionThing myVersionThing; myVersionThing.majorVersion = majorVersion().toInt(); myVersionThing.minorVersion = minorVersion().toInt(); myVersionThing.minorSubVersion = minorSubVersion().toInt(); myVersionThing.releaseModifier = modifier(); return greaterThan(myVersionThing, candidateVersionThing); } bool Version::greaterThan(const QString & myVersionStr, const QString & yourVersionStr) { VersionThing myVersionThing; Version::toVersionThing(myVersionStr,myVersionThing); VersionThing yourVersionThing; Version::toVersionThing(yourVersionStr,yourVersionThing); return greaterThan(myVersionThing,yourVersionThing); } bool Version::greaterThan(const VersionThing & myVersionThing, const VersionThing & yourVersionThing) { // yourVersionThing > myVersionThing bool newOne = false; if (yourVersionThing.majorVersion > myVersionThing.majorVersion) { newOne = true; } else if (yourVersionThing.majorVersion == myVersionThing.majorVersion) { if (yourVersionThing.minorVersion > myVersionThing.minorVersion) { newOne = true; } else if (yourVersionThing.minorVersion == myVersionThing.minorVersion) { if (yourVersionThing.minorSubVersion > myVersionThing.minorSubVersion) { newOne = true; } else if (yourVersionThing.minorSubVersion == myVersionThing.minorSubVersion) { newOne = modifierGreaterThan(myVersionThing.releaseModifier, yourVersionThing.releaseModifier); } } } return newOne; } bool Version::modifierGreaterThan(const QString & myReleaseModifier, const QString & yourReleaseModifier) { int yourIndex = m_modifiers.indexOf(yourReleaseModifier); int myIndex = m_modifiers.indexOf(myReleaseModifier); return yourIndex > myIndex; } void Version::toVersionThing(const QString & candidate, VersionThing & versionThing) { versionThing.ok = false; QString modString; foreach (QString s, m_modifiers) { modString += s + "|"; } modString.chop(1); QRegExp sw(QString("([\\d]+)\\.([\\d]+)\\.([\\d]+)[\\.]{0,1}(%1)").arg(modString)); if (sw.indexIn(candidate) != 0) { return; } versionThing.majorVersion = sw.cap(1).toInt(&versionThing.ok); if (!versionThing.ok) return; versionThing.minorVersion = sw.cap(2).toInt(&versionThing.ok); if (!versionThing.ok) return; versionThing.minorSubVersion = sw.cap(3).toInt(&versionThing.ok); if (!versionThing.ok) return; versionThing.releaseModifier = sw.cap(4); } void Version::cleanup() { if (m_singleton) { delete m_singleton; m_singleton = NULL; } } QString Version::makeRequestParamsString(bool withID) { QSettings settings; if (settings.value("pid").isNull()) { settings.setValue("pid", TextUtils::getRandText()); } QString id; if (withID) id = QString("&pid=%1").arg(settings.value("pid").toString()); QtSystemInfo systemInfo(NULL); QString siVersion(QUrl::toPercentEncoding(Version::versionString())); QString siSystemName(QUrl::toPercentEncoding(systemInfo.systemName())); QString siSystemVersion(QUrl::toPercentEncoding(systemInfo.systemVersion())); QString siKernelName(QUrl::toPercentEncoding(systemInfo.kernelName())); QString siKernelVersion(QUrl::toPercentEncoding(systemInfo.kernelVersion())); QString siArchitecture(QUrl::toPercentEncoding(systemInfo.architectureName())); QString string = QString("?version=%2&sysname=%3&kernname=%4&kernversion=%5&arch=%6&sysversion=%7%8") .arg(siVersion) .arg(siSystemName) .arg(siKernelName) .arg(siKernelVersion) .arg(siArchitecture) .arg(siSystemVersion) .arg(id) ; return string; } fritzing-app-0.9.3b/src/version/version.h000066400000000000000000000051261272363601300204200ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef VERSION_H #define VERSION_H #include struct VersionThing { int majorVersion; int minorVersion; int minorSubVersion; QString releaseModifier; bool ok; }; class Version { public: static const QString & majorVersion(); static const QString & minorVersion(); static const QString & minorSubVersion(); static const QString & modifier(); static const QString & revision(); static const QString & versionString(); static const QString & date(); static const QString & fullDate(); static const QString & shortDate(); static const QString & year(); static bool candidateGreaterThanCurrent(const VersionThing &); static bool greaterThan(const VersionThing & myVersionThing, const VersionThing & yourVersionThing); static bool greaterThan(const QString & myVersionStr, const QString & yourVersionStr); static bool modifierGreaterThan(const QString & myReleaseModifier, const QString & yourReleaseModifier); static void toVersionThing(const QString & candidate, VersionThing & versionThing); static void cleanup(); static QString makeRequestParamsString(bool withID); public: static QString FirstVersionWithDetachedUserData; protected: Version(); protected: static QString m_majorVersion; static QString m_minorVersion; static QString m_minorSubVersion; static QString m_gitCommit; static QString m_revision; static QString m_modifier; static QString m_versionString; static QString m_gitDate; static QString m_date; static QString m_shortDate; static QString m_year; static Version * m_singleton; static QStringList m_modifiers; }; #endif fritzing-app-0.9.3b/src/version/versionchecker.cpp000066400000000000000000000170741272363601300223050ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ // much code borrowed from Qt's rsslisting example #include "versionchecker.h" #include "version.h" #include "../debugdialog.h" #include #include #include VersionChecker::VersionChecker() : QObject() { m_networkReply = NULL; m_depth = 0; m_inSummary = m_inUpdated = m_inTitle = m_inEntry = false; m_ignoreInterimVersion.ok = m_ignoreMainVersion.ok = false; } VersionChecker::~VersionChecker() { foreach (AvailableRelease * availableRelease, m_availableReleases) { delete availableRelease; } m_availableReleases.clear(); } void VersionChecker::fetch() { DebugDialog::debug("http check new version"); m_xml.clear(); QUrl url(m_urlString); QNetworkAccessManager * manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(finished(QNetworkReply *))); QNetworkReply * reply = manager->get(QNetworkRequest(url)); QMutexLocker locker(&m_networkReplyLock); m_networkReply = reply; } void VersionChecker::finished(QNetworkReply * networkReply) { int responseCode = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (responseCode == 200) { m_xml.addData(networkReply->readAll()); parseXml(); DebugDialog::debug("http check new version no error"); emit releasesAvailable(); } else { DebugDialog::debug(QString("http check new version error %1").arg(networkReply->errorString())); emit httpError(networkReply->error()); } QMutexLocker locker(&m_networkReplyLock); if (networkReply == m_networkReply) { m_networkReply = NULL; } networkReply->manager()->deleteLater(); networkReply->deleteLater(); } void VersionChecker::parseXml() { //DebugDialog::debug("parsing xml"); while (!m_xml.atEnd()) { m_xml.readNext(); if (m_xml.isStartElement()) { QString elementName = m_xml.name().toString(); if (elementName.compare("entry") == 0) { m_inEntry = true; m_inSummary = m_inTitle = m_inUpdated = false; m_currentLinkHref = ""; m_currentCategoryTerm = ""; m_currentTitle = ""; m_currentUpdated = ""; } else if (m_inEntry && elementName.compare("title") == 0) { m_inTitle = true; } else if (m_inEntry && elementName.compare("link") == 0) { if (m_xml.attributes().value("rel").toString().compare("enclosure") == 0) { m_currentLinkHref = m_xml.attributes().value("href").toString(); } } else if (m_inEntry && elementName.compare("category") == 0) { m_currentCategoryTerm = m_xml.attributes().value("term").toString(); } else if (m_inEntry && elementName.compare("updated") == 0) { m_inUpdated = true; } else if (m_inEntry && elementName.compare("summary") == 0) { m_inSummary = true; } //DebugDialog::debug(QString("%1<%2>").arg(QString(m_depth * 4, ' ')).arg(elementName)); m_depth++; //foreach (QXmlStreamAttribute attribute, m_xml.attributes()) { //DebugDialog::debug(QString("%1%2: %3").arg(QString((m_depth + 1) * 4, ' ')).arg(attribute.name().toString()).arg(attribute.value().toString())); //} } else if (m_xml.isEndElement()) { QString elementName = m_xml.name().toString(); if (m_inEntry && elementName.compare("entry") == 0) { m_inEntry = false; parseEntry(); } else if (m_inTitle && elementName.compare("title") == 0) { m_inTitle = false; } else if (m_inUpdated && elementName.compare("updated") == 0) { m_inUpdated = false; } else if (m_inSummary && elementName.compare("summary") == 0) { m_inSummary = false; } m_depth--; //DebugDialog::debug(QString("%1").arg(QString(m_depth * 4, ' ')).arg(elementName)); } else if (m_xml.isCharacters() && !m_xml.isWhitespace()) { QString t = m_xml.text().toString(); t.replace(QRegExp("[\\s]+"), " "); //DebugDialog::debug(QString("%1%2").arg(QString((m_depth + 1) * 4, ' ')).arg(t)); if (m_inTitle) { m_currentTitle = m_xml.text().toString(); } else if (m_inUpdated) { m_currentUpdated = m_xml.text().toString(); } else if (m_inSummary) { m_currentSummary = m_xml.text().toString(); } } } if (m_xml.error() && m_xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { emit xmlError(m_xml.error()); return; } } void VersionChecker::setUrl(const QString & url) { m_urlString = url; } void VersionChecker::parseEntry() { if (m_currentLinkHref.isEmpty()) return; if (m_currentCategoryTerm.isEmpty()) return; if (m_currentUpdated.isEmpty()) return; #ifndef QT_NO_DEBUG // hack for testing if (m_currentCategoryTerm.compare("main") == 0) { if (m_currentTitle.compare("0.11b") == 0) { m_currentTitle = "0.1.11b"; } else if (m_currentTitle.compare("0.1b") == 0) { m_currentTitle = "0.1.1b"; } } #endif VersionThing entryVersionThing; Version::toVersionThing(m_currentTitle, entryVersionThing); if (!entryVersionThing.ok) { // older versions that don't conform, bail return; } if (!Version::candidateGreaterThanCurrent(entryVersionThing)) { return; } bool interim = (m_currentCategoryTerm.compare("interim") == 0); if (interim) { if (m_ignoreInterimVersion.ok) { if (!Version::greaterThan(m_ignoreInterimVersion, entryVersionThing)) { return; // candidate version is earlier or the same as interim version already in prefs } } } else { if (m_ignoreMainVersion.ok) { if (!Version::greaterThan(m_ignoreMainVersion, entryVersionThing)) { return; // candidate version is earlier or the same as main version already in prefs } } } AvailableRelease * availableRelease = new AvailableRelease(); availableRelease->versionString = m_currentTitle; availableRelease->link = m_currentLinkHref; availableRelease->interim = interim; availableRelease->summary = m_currentSummary; QStringList temp = m_currentUpdated.split('+'); // conversion is confused by the +timezone suffix from the site xml availableRelease->dateTime = QDateTime::fromString(temp[0], "yyyy-MM-ddThh:mm:ss"); m_availableReleases.append(availableRelease); } const QList & VersionChecker::availableReleases() { return m_availableReleases; } void VersionChecker::stop() { if (m_networkReplyLock.tryLock(1)) { if (m_networkReply) { m_networkReply = NULL; } m_networkReplyLock.unlock(); } } void VersionChecker::ignore(const QString & version, bool interim) { if (interim) { Version::toVersionThing(version, m_ignoreInterimVersion); } else { Version::toVersionThing(version, m_ignoreMainVersion); } } fritzing-app-0.9.3b/src/version/versionchecker.h000066400000000000000000000045211272363601300217430ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #ifndef VERSIONCHECKER_H #define VERSIONCHECKER_H #include #include #include #include #include #include "version.h" // much code borrowed from Qt's rsslisting example struct AvailableRelease { bool interim; QString versionString; QString link; QString summary; QDateTime dateTime; }; class VersionChecker : public QObject { Q_OBJECT public: VersionChecker(); ~VersionChecker(); void setUrl(const QString & url); const QList & availableReleases(); void stop(); void ignore(const QString & version, bool interim); signals: void httpError(QNetworkReply::NetworkError); void xmlError(QXmlStreamReader::Error errorCode); void releasesAvailable(); public slots: void fetch(); void finished(QNetworkReply *); protected: void parseXml(); void parseEntry(); protected: QString m_urlString; QXmlStreamReader m_xml; QString m_version; int m_depth; bool m_inEntry; bool m_inTitle; bool m_inUpdated; bool m_inSummary; QList m_availableReleases; QString m_currentLinkHref; QString m_currentCategoryTerm; QString m_currentTitle; QString m_currentUpdated; QString m_currentSummary; VersionThing m_ignoreMainVersion; VersionThing m_ignoreInterimVersion; QNetworkReply * m_networkReply; QMutex m_networkReplyLock; }; #endif fritzing-app-0.9.3b/src/viewgeometry.cpp000066400000000000000000000117761272363601300203370ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #include "viewgeometry.h" #include "utils/graphicsutils.h" ViewGeometry::ViewGeometry( ) { m_z = -1; m_loc.setX(-1); m_loc.setY(-1); m_line.setLine(-1,-1,-1,-1); m_wireFlags = NoFlag; } ViewGeometry::ViewGeometry(const ViewGeometry & that ) { set(that); } ViewGeometry::ViewGeometry(QDomElement & geometry) { m_wireFlags = (WireFlags) geometry.attribute("wireFlags").toInt(); m_z = geometry.attribute("z").toDouble(); m_loc.setX(geometry.attribute("x").toDouble()); m_loc.setY(geometry.attribute("y").toDouble()); QString x1 = geometry.attribute("x1"); if (!x1.isEmpty()) { m_line.setLine( geometry.attribute("x1").toDouble(), geometry.attribute("y1").toDouble(), geometry.attribute("x2").toDouble(), geometry.attribute("y2").toDouble() ); } QString w = geometry.attribute("width"); if (!w.isEmpty()) { m_rect.setX(geometry.attribute("x").toDouble()); m_rect.setY(geometry.attribute("y").toDouble()); m_rect.setWidth(geometry.attribute("width").toDouble()); m_rect.setHeight(geometry.attribute("height").toDouble()); } GraphicsUtils::loadTransform(geometry.firstChildElement("transform"), m_transform); } void ViewGeometry::setZ(double z) { m_z = z; } double ViewGeometry::z() const { return m_z ; } void ViewGeometry::setLoc(QPointF loc) { m_loc = loc; } QPointF ViewGeometry::loc() const { return m_loc ; } void ViewGeometry::setLine(QLineF loc) { m_line = loc; } QLineF ViewGeometry::line() const { return m_line; } void ViewGeometry::offset(double x, double y) { m_loc.setX(x + m_loc.x()); m_loc.setY(y + m_loc.y()); } bool ViewGeometry::selected() { return m_selected; } void ViewGeometry::setSelected(bool selected) { m_selected = selected; } QRectF ViewGeometry::rect() const { return m_rect; } void ViewGeometry::setRect(double x, double y, double width, double height) { m_rect.setRect(x, y, width, height); } void ViewGeometry::setRect(const QRectF & r) { setRect(r.x(), r.y(), r.width(), r.height()); } void ViewGeometry::setTransform(QTransform transform) { m_transform = transform; } QTransform ViewGeometry::transform() const { return m_transform; } void ViewGeometry::set(const ViewGeometry & that) { m_z = that.m_z; m_line = that.m_line; m_loc = that.m_loc; m_transform = that.m_transform; m_wireFlags = that.m_wireFlags; m_rect = that.m_rect; } void ViewGeometry::setRouted(bool routed) { setWireFlag(routed, RoutedFlag); } void ViewGeometry::setPCBTrace(bool trace) { setWireFlag(trace, PCBTraceFlag); } void ViewGeometry::setSchematicTrace(bool trace) { setWireFlag(trace, SchematicTraceFlag); } void ViewGeometry::setRatsnest(bool ratsnest) { setWireFlag(ratsnest, RatsnestFlag); } void ViewGeometry::setNormal(bool normal) { setWireFlag(normal, NormalFlag); } void ViewGeometry::setAutoroutable(bool autoroutable) { setWireFlag(autoroutable, AutoroutableFlag); } bool ViewGeometry::getRouted() const { return m_wireFlags.testFlag(RoutedFlag); } bool ViewGeometry::getNormal() const { return m_wireFlags.testFlag(NormalFlag); } bool ViewGeometry::getPCBTrace() const { return m_wireFlags.testFlag(PCBTraceFlag); } bool ViewGeometry::getAnyTrace() const { return getPCBTrace() || getSchematicTrace(); } bool ViewGeometry::getSchematicTrace() const { return m_wireFlags.testFlag(SchematicTraceFlag); } bool ViewGeometry::getRatsnest() const { return m_wireFlags.testFlag(RatsnestFlag); } bool ViewGeometry::getAutoroutable() const { return m_wireFlags.testFlag(AutoroutableFlag); } void ViewGeometry::setWireFlag(bool setting, WireFlag flag) { if (setting) { m_wireFlags |= flag; } else { m_wireFlags &= ~flag; } } int ViewGeometry::flagsAsInt() const { return (int) m_wireFlags; } void ViewGeometry::setWireFlags(WireFlags wireFlags) { m_wireFlags = wireFlags; } bool ViewGeometry::hasFlag(ViewGeometry::WireFlag flag) { return (m_wireFlags & flag) ? true : false; } bool ViewGeometry::hasAnyFlag(ViewGeometry::WireFlags flags) { return (m_wireFlags & flags) ? true : false; } ViewGeometry::WireFlags ViewGeometry::wireFlags() const { return m_wireFlags; } fritzing-app-0.9.3b/src/viewgeometry.h000066400000000000000000000053171272363601300177760ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6112 $: $Author: cohen@irascible.com $: $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $ ********************************************************************/ #ifndef VIEWGEOMETRY_H #define VIEWGEOMETRY_H #include #include #include #include #include class ViewGeometry { public: ViewGeometry(); ViewGeometry(QDomElement &); ViewGeometry(const ViewGeometry &); ~ViewGeometry() {} public: enum WireFlag { NoFlag = 0, RoutedFlag = 2, PCBTraceFlag = 4, ObsoleteJumperFlag = 8, RatsnestFlag = 16, AutoroutableFlag = 32, NormalFlag = 64, SchematicTraceFlag = 128 }; Q_DECLARE_FLAGS(WireFlags, WireFlag) protected: void setWireFlag(bool setting, WireFlag flag); public: void setZ(double); double z() const; void setLoc(QPointF); QPointF loc() const; void setLine(QLineF); QLineF line() const; void offset(double x, double y); bool selected(); void setSelected(bool); QRectF rect() const; void setRect(double x, double y, double width, double height); void setRect(const QRectF &); void setTransform(QTransform); QTransform transform() const; void set(const ViewGeometry &); void setRouted(bool); bool getRouted() const; void setPCBTrace(bool); bool getPCBTrace() const; bool getAnyTrace() const; void setSchematicTrace(bool); bool getSchematicTrace() const; void setRatsnest(bool); bool getRatsnest() const; void setAutoroutable(bool); bool getAutoroutable() const; void setNormal(bool); bool getNormal() const; void setWireFlags(WireFlags); bool hasFlag(WireFlag); bool hasAnyFlag(WireFlags); int flagsAsInt() const; ViewGeometry::WireFlags wireFlags() const; protected: double m_z; QPointF m_loc; QLineF m_line; QRectF m_rect; bool m_selected; WireFlags m_wireFlags; QTransform m_transform; }; Q_DECLARE_OPERATORS_FOR_FLAGS(ViewGeometry::WireFlags) //Q_DECLARE_METATYPE(ViewGeometry *); #endif fritzing-app-0.9.3b/src/viewlayer.cpp000066400000000000000000000434351272363601300176150ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #include "viewlayer.h" #include "debugdialog.h" #include double ViewLayer::zIncrement = 0.00001; // 0.000000001; QHash ViewLayer::names; QMultiHash ViewLayer::alternatives; QMultiHash ViewLayer::unconnectables; QHash ViewLayer::xmlHash; const QString ViewLayer::Copper0Color = "#f9a435"; // "#FFCB33"; //Bottom const QString ViewLayer::Copper1Color = "#fdde68"; // "#F28A00"; //Top const QString ViewLayer::Copper0WireColor = "#f28a00"; const QString ViewLayer::Copper1WireColor = "#f2c600"; const QString ViewLayer::Silkscreen0Color = "#444444"; const QString ViewLayer::Silkscreen1Color = "#000000"; const QString ViewLayer::BoardColor = "#ffffff"; static LayerList CopperBottomLayers; static LayerList CopperTopLayers; static LayerList NonCopperLayers; // just NonCopperLayers in pcb view static LayerList IconViewLayerList; static LayerList BreadboardViewLayerList; static LayerList SchematicViewLayerList; static LayerList PCBViewLayerList; static LayerList EmptyLayerList; static LayerList PCBViewFromBelowLayerList; NamePair::NamePair(QString xml, QString display) { xmlName = xml; displayName = display; } ////////////////////////////////////////////// class NameTriple { public: NameTriple(const QString & _xmlName, const QString & _viewName, const QString & _naturalName) { m_xmlName = _xmlName; m_viewName = _viewName; m_naturalName = _naturalName; } QString & xmlName() { return m_xmlName; } QString & viewName() { return m_viewName; } QString & naturalName() { return m_naturalName; } protected: QString m_xmlName; QString m_naturalName; QString m_viewName; }; ////////////////////////////////////////////// ViewLayer::ViewLayer(ViewLayerID viewLayerID, bool visible, double initialZ) { m_fromBelow = false; m_viewLayerID = viewLayerID; m_visible = visible; m_action = NULL; m_initialZFromBelow = m_initialZ = initialZ; m_nextZ = 0; m_parentLayer = NULL; m_active = true; m_includeChildLayers = true; } ViewLayer::~ViewLayer() { } void ViewLayer::initNames() { if (CopperBottomLayers.isEmpty()) { CopperBottomLayers << ViewLayer::GroundPlane0 << ViewLayer::Copper0 << ViewLayer::Copper0Trace; } if (CopperTopLayers.isEmpty()) { CopperTopLayers << ViewLayer::GroundPlane1 << ViewLayer::Copper1 << ViewLayer::Copper1Trace; } if (NonCopperLayers.isEmpty()) { NonCopperLayers << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen0Label << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label << ViewLayer::PartImage; } if (names.count() == 0) { // xmlname, displayname names.insert(ViewLayer::Icon, new NamePair("icon", QObject::tr("Icon"))); names.insert(ViewLayer::BreadboardBreadboard, new NamePair("breadboardbreadboard", QObject::tr("Breadboard"))); names.insert(ViewLayer::Breadboard, new NamePair("breadboard", QObject::tr("Parts"))); names.insert(ViewLayer::BreadboardWire, new NamePair("breadboardWire", QObject::tr("Wires"))); names.insert(ViewLayer::BreadboardLabel, new NamePair("breadboardLabel", QObject::tr("Part Labels"))); names.insert(ViewLayer::BreadboardRatsnest, new NamePair("breadboardRatsnest", QObject::tr("Ratsnest"))); names.insert(ViewLayer::BreadboardNote, new NamePair("breadboardNote", QObject::tr("Notes"))); names.insert(ViewLayer::BreadboardRuler, new NamePair("breadboardRuler", QObject::tr("Rulers"))); names.insert(ViewLayer::SchematicFrame, new NamePair("schematicframe", QObject::tr("Frame"))); names.insert(ViewLayer::Schematic, new NamePair("schematic", QObject::tr("Parts"))); names.insert(ViewLayer::SchematicText, new NamePair("schematicText", QObject::tr("Text"))); names.insert(ViewLayer::SchematicWire, new NamePair("schematicWire",QObject::tr("Ratsnest"))); names.insert(ViewLayer::SchematicTrace, new NamePair("schematicTrace",QObject::tr("Wires"))); names.insert(ViewLayer::SchematicLabel, new NamePair("schematicLabel", QObject::tr("Part Labels"))); names.insert(ViewLayer::SchematicNote, new NamePair("schematicNote", QObject::tr("Notes"))); names.insert(ViewLayer::SchematicRuler, new NamePair("schematicRuler", QObject::tr("Rulers"))); names.insert(ViewLayer::Board, new NamePair("board", QObject::tr("Board"))); names.insert(ViewLayer::Silkscreen1, new NamePair("silkscreen", QObject::tr("Silkscreen Top"))); // really should be silkscreen1 names.insert(ViewLayer::Silkscreen1Label, new NamePair("silkscreenLabel", QObject::tr("Silkscreen Top (Part Labels)"))); names.insert(ViewLayer::GroundPlane0, new NamePair("groundplane", QObject::tr("Copper Fill Bottom"))); names.insert(ViewLayer::Copper0, new NamePair("copper0", QObject::tr("Copper Bottom"))); names.insert(ViewLayer::Copper0Trace, new NamePair("copper0trace", QObject::tr("Copper Bottom Trace"))); names.insert(ViewLayer::GroundPlane1, new NamePair("groundplane1", QObject::tr("Copper Fill Top"))); names.insert(ViewLayer::Copper1, new NamePair("copper1", QObject::tr("Copper Top"))); names.insert(ViewLayer::Copper1Trace, new NamePair("copper1trace", QObject::tr("Copper Top Trace"))); names.insert(ViewLayer::PcbRatsnest, new NamePair("ratsnest", QObject::tr("Ratsnest"))); names.insert(ViewLayer::Silkscreen0, new NamePair("silkscreen0", QObject::tr("Silkscreen Bottom"))); names.insert(ViewLayer::Silkscreen0Label, new NamePair("silkscreen0Label", QObject::tr("Silkscreen Bottom (Part Labels)"))); //names.insert(ViewLayer::Soldermask, new NamePair("soldermask", QObject::tr("Solder mask"))); //names.insert(ViewLayer::Outline, new NamePair("outline", QObject::tr("Outline"))); //names.insert(ViewLayer::Keepout, new NamePair("keepout", QObject::tr("Keep out"))); names.insert(ViewLayer::PartImage, new NamePair("partimage", QObject::tr("Part Image"))); names.insert(ViewLayer::PcbNote, new NamePair("pcbNote", QObject::tr("Notes"))); names.insert(ViewLayer::PcbRuler, new NamePair("pcbRuler", QObject::tr("Rulers"))); foreach (ViewLayerID key, names.keys()) { xmlHash.insert(names.value(key)->xmlName, key); } names.insert(ViewLayer::UnknownLayer, new NamePair("unknown", QObject::tr("Unknown Layer"))); alternatives.insert(ViewLayer::Copper0, ViewLayer::Copper1); alternatives.insert(ViewLayer::Copper1, ViewLayer::Copper0); LayerList l0, l1; l0 << ViewLayer::Copper0 << ViewLayer::Copper0Trace << ViewLayer::GroundPlane0; l1 << ViewLayer::Copper1 << ViewLayer::Copper1Trace << ViewLayer::GroundPlane1; foreach (ViewLayer::ViewLayerID viewLayerID0, l0) { foreach (ViewLayer::ViewLayerID viewLayerID1, l1) { unconnectables.insert(viewLayerID0, viewLayerID1); unconnectables.insert(viewLayerID1, viewLayerID0); } } } if (ViewIDNames.count() == 0) { ViewIDNames.insert(ViewLayer::IconView, new NameTriple("iconView", QObject::tr("icon view"), "icon")); ViewIDNames.insert(ViewLayer::BreadboardView, new NameTriple("breadboardView", QObject::tr("breadboard view"), "breadboard")); ViewIDNames.insert(ViewLayer::SchematicView, new NameTriple("schematicView", QObject::tr("schematic view"), "schematic")); ViewIDNames.insert(ViewLayer::PCBView, new NameTriple("pcbView", QObject::tr("pcb view"), "pcb")); } if (BreadboardViewLayerList.count() == 0) { IconViewLayerList << ViewLayer::Icon; BreadboardViewLayerList << ViewLayer::BreadboardBreadboard << ViewLayer::Breadboard << ViewLayer::BreadboardWire << ViewLayer::BreadboardLabel << ViewLayer::BreadboardRatsnest << ViewLayer::BreadboardNote << ViewLayer::BreadboardRuler; SchematicViewLayerList << ViewLayer::SchematicFrame << ViewLayer::Schematic << ViewLayer::SchematicText << ViewLayer::SchematicWire << ViewLayer::SchematicTrace << ViewLayer::SchematicLabel << ViewLayer::SchematicNote << ViewLayer::SchematicRuler; PCBViewLayerList << ViewLayer::Board << ViewLayer::GroundPlane0 << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen0Label << ViewLayer::Copper0 << ViewLayer::Copper0Trace << ViewLayer::GroundPlane1 << ViewLayer::Copper1 << ViewLayer::Copper1Trace << ViewLayer::PcbRatsnest << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label << ViewLayer::PartImage << ViewLayer::PcbNote << ViewLayer::PcbRuler; PCBViewFromBelowLayerList << ViewLayer::Board << ViewLayer::GroundPlane1 << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label << ViewLayer::Copper1 << ViewLayer::Copper1Trace << ViewLayer::GroundPlane0 << ViewLayer::Copper0 << ViewLayer::Copper0Trace << ViewLayer::PcbRatsnest << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen0Label << ViewLayer::PartImage << ViewLayer::PcbNote << ViewLayer::PcbRuler; } } QString & ViewLayer::displayName() { return names[m_viewLayerID]->displayName; } void ViewLayer::setAction(QAction * action) { m_action = action; } QAction* ViewLayer::action() { return m_action; } bool ViewLayer::visible() { return m_visible; } void ViewLayer::setVisible(bool visible) { m_visible = visible; if (m_action) { m_action->setChecked(visible); } } double ViewLayer::nextZ() { double temp = m_nextZ + (m_fromBelow ? m_initialZFromBelow : m_initialZ); m_nextZ += zIncrement; return temp; } ViewLayer::ViewLayerID ViewLayer::viewLayerIDFromXmlString(const QString & viewLayerName) { return xmlHash.value(viewLayerName, ViewLayer::UnknownLayer); } ViewLayer::ViewLayerID ViewLayer::viewLayerID() { return m_viewLayerID; } double ViewLayer::incrementZ(double z) { return (z + zIncrement); } double ViewLayer::getZIncrement() { return zIncrement; } const QString & ViewLayer::viewLayerNameFromID(ViewLayerID viewLayerID) { NamePair * sp = names.value(viewLayerID); if (sp == NULL) return ___emptyString___; return sp->displayName; } const QString & ViewLayer::viewLayerXmlNameFromID(ViewLayerID viewLayerID) { NamePair * sp = names.value(viewLayerID); if (sp == NULL) return ___emptyString___; return sp->xmlName; } ViewLayer * ViewLayer::parentLayer() { return m_parentLayer; } void ViewLayer::setParentLayer(ViewLayer * parent) { m_parentLayer = parent; if (parent) { parent->m_childLayers.append(this); } } const QList & ViewLayer::childLayers() { return m_childLayers; } bool ViewLayer::alreadyInLayer(double z) { if (m_fromBelow) { return (z >= m_initialZFromBelow && z <= m_initialZFromBelow + m_nextZ); } return (z >= m_initialZ && z <= m_initialZ + m_nextZ); } void ViewLayer::cleanup() { foreach (NamePair * sp, names.values()) { delete sp; } names.clear(); foreach (NameTriple * nameTriple, ViewIDNames) { delete nameTriple; } ViewIDNames.clear(); } void ViewLayer::resetNextZ(double z) { m_nextZ = z - floor(z); } LayerList ViewLayer::findAlternativeLayers(ViewLayer::ViewLayerID id) { LayerList alts = alternatives.values(id); return alts; } bool ViewLayer::canConnect(ViewLayer::ViewLayerID v1, ViewLayer::ViewLayerID v2) { if (v1 == v2) return true; LayerList uncs = unconnectables.values(v1); return (!uncs.contains(v2)); } bool ViewLayer::isActive() { return m_active; } void ViewLayer::setActive(bool a) { m_active = a; } ViewLayer::ViewLayerPlacement ViewLayer::specFromID(ViewLayer::ViewLayerID viewLayerID) { switch (viewLayerID) { case Copper1: case Copper1Trace: case GroundPlane1: return ViewLayer::NewTop; default: return ViewLayer::NewBottom; } } bool ViewLayer::isCopperLayer(ViewLayer::ViewLayerID viewLayerID) { if (CopperTopLayers.contains(viewLayerID)) return true; if (CopperBottomLayers.contains(viewLayerID)) return true; return false; } const LayerList & ViewLayer::copperLayers(ViewLayer::ViewLayerPlacement viewLayerPlacement) { if (viewLayerPlacement == ViewLayer::NewTop) return CopperTopLayers; return CopperBottomLayers; } const LayerList & ViewLayer::maskLayers(ViewLayer::ViewLayerPlacement viewLayerPlacement) { static LayerList bottom; static LayerList top; if (bottom.isEmpty()) { bottom << ViewLayer::Copper0; } if (top.isEmpty()) { top << ViewLayer::Copper1; } if (viewLayerPlacement == ViewLayer::NewTop) return top; return bottom; } const LayerList & ViewLayer::silkLayers(ViewLayer::ViewLayerPlacement viewLayerPlacement) { static LayerList bottom; static LayerList top; if (bottom.isEmpty()) { bottom << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen0Label; } if (top.isEmpty()) { top << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label; } if (viewLayerPlacement == ViewLayer::NewTop) return top; return bottom; } const LayerList & ViewLayer::outlineLayers() { static LayerList layerList; if (layerList.isEmpty()) { layerList << ViewLayer::Board; } return layerList; } const LayerList & ViewLayer::drillLayers() { static LayerList layerList; if (layerList.isEmpty()) { layerList << ViewLayer::Copper0 << ViewLayer::Copper1; } return layerList; } const LayerList & ViewLayer::silkLayers() { static LayerList layerList; if (layerList.isEmpty()) { layerList << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen1; } return layerList; } const LayerList & ViewLayer::topLayers() { static LayerList layerList; if (layerList.isEmpty()) { layerList << ViewLayer::Copper1 << ViewLayer::Copper1Trace << ViewLayer::Silkscreen1 << ViewLayer::Silkscreen1Label << ViewLayer::GroundPlane1; } return layerList; } const LayerList & ViewLayer::bottomLayers() { static LayerList layerList; if (layerList.isEmpty()) { layerList << ViewLayer::Copper0 << ViewLayer::Copper0Trace << ViewLayer::Silkscreen0 << ViewLayer::Silkscreen0Label << ViewLayer::GroundPlane0; } return layerList; } bool ViewLayer::isNonCopperLayer(ViewLayer::ViewLayerID viewLayerID) { // for pcb view layers only return NonCopperLayers.contains(viewLayerID); } bool ViewLayer::includeChildLayers() { return m_includeChildLayers; } void ViewLayer::setIncludeChildLayers(bool incl) { m_includeChildLayers = incl; } ///////////////////////////////// QHash ViewLayer::ViewIDNames; QString & ViewLayer::viewIDName(ViewLayer::ViewID viewID) { if (viewID < 0 || viewID >= ViewLayer::ViewCount) { throw "ViewLayer::viewIDName bad identifier"; } return ViewIDNames[viewID]->viewName(); } QString & ViewLayer::viewIDXmlName(ViewLayer::ViewID viewID) { if (viewID < 0 || viewID >= ViewLayer::ViewCount) { throw "ViewLayer::viewIDXmlName bad identifier"; } return ViewIDNames[viewID]->xmlName(); } QString & ViewLayer::viewIDNaturalName(ViewLayer::ViewID viewID) { if (viewID < 0 || viewID >= ViewLayer::ViewCount) { throw "ViewLayer::viewIDNaturalName bad identifier"; } return ViewIDNames[viewID]->naturalName(); } ViewLayer::ViewID ViewLayer::idFromXmlName(const QString & name) { foreach (ViewID id, ViewIDNames.keys()) { NameTriple * nameTriple = ViewIDNames.value(id); if (name.compare(nameTriple->xmlName()) == 0) return id; } return UnknownView; } const LayerList & ViewLayer::layersForView(ViewLayer::ViewID viewID) { switch(viewID) { case IconView: return IconViewLayerList; case BreadboardView: return BreadboardViewLayerList; case SchematicView: return SchematicViewLayerList; case PCBView: return PCBViewLayerList; default: return EmptyLayerList; } } const LayerList & ViewLayer::layersForViewFromBelow(ViewLayer::ViewID viewID) { switch(viewID) { case IconView: return IconViewLayerList; case BreadboardView: return BreadboardViewLayerList; case SchematicView: return SchematicViewLayerList; case PCBView: return PCBViewFromBelowLayerList; default: return EmptyLayerList; } } bool ViewLayer::viewHasLayer(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) { return layersForView(viewID).contains(viewLayerID); } QDomElement ViewLayer::getConnectorPElement(const QDomElement & element, ViewLayer::ViewID viewID) { QString viewName = ViewLayer::viewIDXmlName(viewID); QDomElement views = element.firstChildElement("views"); QDomElement view = views.firstChildElement(viewName); return view.firstChildElement("p"); } bool ViewLayer::getConnectorSvgIDs(QDomElement & element, ViewLayer::ViewID viewID, QString & id, QString & terminalID) { QDomElement p = getConnectorPElement(element, viewID); if (p.isNull()) { return false; } id = p.attribute("svgId"); if (id.isEmpty()) { return false; } terminalID = p.attribute("terminalId"); return true; } bool ViewLayer::fromBelow() { return m_fromBelow; } void ViewLayer::setFromBelow(bool fromBelow) { m_fromBelow = fromBelow; } void ViewLayer::setInitialZFromBelow(double initialZ) { m_initialZFromBelow = initialZ; } double ViewLayer::getZFromBelow(double currentZ, bool fromBelow) { double frac = currentZ - m_initialZ; if (qAbs(frac) > 1) { frac = currentZ - m_initialZFromBelow; } return frac + (fromBelow ? m_initialZFromBelow : m_initialZ); } fritzing-app-0.9.3b/src/viewlayer.h000066400000000000000000000131011272363601300172450ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6976 $: $Author: irascibl@gmail.com $: $Date: 2013-04-21 09:50:09 +0200 (So, 21. Apr 2013) $ ********************************************************************/ #ifndef VIEWLAYER_H #define VIEWLAYER_H #include #include #include #include #include struct NamePair { QString xmlName; QString displayName; NamePair(QString xml, QString display); }; class ViewLayer : public QObject { Q_OBJECT public: enum ViewLayerID { Icon, BreadboardBreadboard, Breadboard, BreadboardWire, BreadboardLabel, BreadboardRatsnest, BreadboardNote, BreadboardRuler, SchematicFrame, Schematic, SchematicText, SchematicWire, SchematicTrace, SchematicLabel, SchematicNote, SchematicRuler, Board, GroundPlane0, Silkscreen0, Silkscreen0Label, Copper0, Copper0Trace, GroundPlane1, Copper1, Copper1Trace, Silkscreen1, Silkscreen1Label, PartImage, PcbRatsnest, PcbNote, PcbRuler, UnknownLayer, ViewLayerCount }; enum ViewLayerPlacement { NewTop, NewBottom, NewTopAndBottom, UnknownPlacement }; enum ViewID { IconView, BreadboardView, SchematicView, PCBView, AllViews, UnknownView, ViewCount }; public: static const QString HolesColor; static const QString Copper0Color; static const QString Copper1Color; static const QString Copper0WireColor; static const QString Copper1WireColor; static const QString Silkscreen1Color; static const QString Silkscreen0Color; static const QString BoardColor; protected: static double zIncrement; static QHash names; static QMultiHash alternatives; static QMultiHash unconnectables; static QHash xmlHash; public: ViewLayer(ViewLayerID, bool visible, double initialZ); ~ViewLayer(); void setAction(QAction *); QAction* action(); QString & displayName(); bool visible(); void setVisible(bool); double nextZ(); ViewLayerID viewLayerID(); double incrementZ(double); ViewLayer * parentLayer(); void setParentLayer(ViewLayer *); const QList & childLayers(); bool alreadyInLayer(double z); void resetNextZ(double z); void setActive(bool); bool isActive(); bool includeChildLayers(); void setIncludeChildLayers(bool); void setFromBelow(bool); bool fromBelow(); void setInitialZFromBelow(double); double getZFromBelow(double currentZ, bool fromBelow); public: static ViewLayerID viewLayerIDFromXmlString(const QString &); static const QString & viewLayerNameFromID(ViewLayerID); static const QString & viewLayerXmlNameFromID(ViewLayerID); static void initNames(); static double getZIncrement(); static void cleanup(); static QList findAlternativeLayers(ViewLayerID); static bool canConnect(ViewLayerID, ViewLayerID); static ViewLayer::ViewLayerPlacement specFromID(ViewLayer::ViewLayerID); static const QList & copperLayers(ViewLayer::ViewLayerPlacement); static const QList & maskLayers(ViewLayer::ViewLayerPlacement); static const QList & silkLayers(ViewLayer::ViewLayerPlacement); static const QList & outlineLayers(); static const QList & drillLayers(); static const QList & topLayers(); static const QList & bottomLayers(); static const QList & silkLayers(); static bool isCopperLayer(ViewLayer::ViewLayerID); static bool isNonCopperLayer(ViewLayer::ViewLayerID viewLayerID); // for pcb view layers only static QString & viewIDName(ViewLayer::ViewID); static QString & viewIDXmlName(ViewLayer::ViewID); static QString & viewIDNaturalName(ViewLayer::ViewID); static ViewID idFromXmlName(const QString & name); static const QList & layersForView(ViewLayer::ViewID); static const QList & layersForViewFromBelow(ViewLayer::ViewID); static bool viewHasLayer(ViewID, ViewLayer::ViewLayerID); static bool getConnectorSvgIDs(QDomElement & connector, ViewLayer::ViewID, QString & id, QString & terminalID); static QDomElement getConnectorPElement(const QDomElement & connector, ViewLayer::ViewID); protected: bool m_visible; ViewLayerID m_viewLayerID; QAction* m_action; double m_nextZ; double m_initialZ; double m_initialZFromBelow; QList m_childLayers; ViewLayer * m_parentLayer; bool m_active; bool m_includeChildLayers; bool m_fromBelow; static QHash ViewIDNames; }; typedef QHash LayerHash; typedef QList LayerList; Q_DECLARE_METATYPE( ViewLayer* ); #endif fritzing-app-0.9.3b/src/waitpushundostack.cpp000066400000000000000000000107511272363601300213610ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6904 $: $Author: irascibl@gmail.com $: $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $ ********************************************************************/ #include "waitpushundostack.h" #include "utils/misc.h" #include "utils/folderutils.h" #include "commands.h" #include #include CommandTimer::CommandTimer(QUndoCommand * command, int delayMS, WaitPushUndoStack * undoStack) : QTimer() { m_undoStack = undoStack; m_command = command; m_undoStack->addTimer(this); setSingleShot(true); setInterval(delayMS); connect(this, SIGNAL(timeout()), this, SLOT(timedout())); start(); } void CommandTimer::timedout() { if (m_undoStack) { m_undoStack->push(m_command); m_undoStack->deleteTimer(this); } } ///////////////////////////////// WaitPushUndoStack::WaitPushUndoStack(QObject * parent) : QUndoStack(parent) { m_temporary = NULL; #ifndef QT_NO_DEBUG QString path = FolderUtils::getTopLevelUserDataStorePath(); path += "/undostack.txt"; m_file.setFileName(path); m_file.remove(); #endif } WaitPushUndoStack::~WaitPushUndoStack() { clearLiveTimers(); clearDeadTimers(); } void WaitPushUndoStack::push(QUndoCommand * cmd) { #ifndef QT_NO_DEBUG writeUndo(cmd, 0, NULL); #endif if (m_temporary == cmd) { m_temporary->redo(); return; } QUndoStack::push(cmd); } void WaitPushUndoStack::waitPush(QUndoCommand * command, int delayMS) { clearDeadTimers(); if (delayMS <= 0) { push(command); return; } new CommandTimer(command, delayMS, this); } void WaitPushUndoStack::waitPushTemporary(QUndoCommand * command, int delayMS) { m_temporary = command; waitPush(command, delayMS); } void WaitPushUndoStack::clearDeadTimers() { clearTimers(m_deadTimers); } void WaitPushUndoStack::clearLiveTimers() { clearTimers(m_liveTimers); } void WaitPushUndoStack::clearTimers(QList & timers) { QMutexLocker locker(&m_mutex); foreach (QTimer * timer, timers) { delete timer; } timers.clear(); } void WaitPushUndoStack::deleteTimer(QTimer * timer) { QMutexLocker locker(&m_mutex); m_deadTimers.append(timer); m_liveTimers.removeOne(timer); } void WaitPushUndoStack::addTimer(QTimer * timer) { QMutexLocker locker(&m_mutex); m_liveTimers.append(timer); } bool WaitPushUndoStack::hasTimers() { QMutexLocker locker(&m_mutex); return m_liveTimers.count() > 0; } void WaitPushUndoStack::resolveTemporary() { TemporaryCommand * tc = dynamic_cast(m_temporary); m_temporary = NULL; if (tc) { tc->setEnabled(false); push(tc); tc->setEnabled(true); } } void WaitPushUndoStack::deleteTemporary() { if (m_temporary != NULL) { delete m_temporary; m_temporary = NULL; } } #ifndef QT_NO_DEBUG void WaitPushUndoStack::writeUndo(const QUndoCommand * cmd, int indent, const BaseCommand * parent) { const BaseCommand * bcmd = dynamic_cast(cmd); QString cmdString; QString indexString; if (bcmd == NULL) { cmdString = cmd->text(); } else { cmdString = bcmd->getDebugString(); indexString = QString::number(bcmd->index()) + " "; } if (m_file.open(QIODevice::Append | QIODevice::Text)) { QTextStream out(&m_file); QString indentString(indent, QChar(' ')); if (parent) { indentString += QString("(%1) ").arg(parent->index()); } indentString += indexString; out << indentString << cmdString << "\n"; m_file.close(); } for (int i = 0; i < cmd->childCount(); i++) { writeUndo(cmd->child(i), indent + 4, NULL); } if (bcmd) { for (int i = 0; i < bcmd->subCommandCount(); i++) { writeUndo(bcmd->subCommand(i), indent + 4, bcmd); } } } #endif fritzing-app-0.9.3b/src/waitpushundostack.h000066400000000000000000000042301272363601300210210ustar00rootroot00000000000000/******************************************************************* Part of the Fritzing project - http://fritzing.org Copyright (c) 2007-2016 Fritzing Fritzing is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fritzing is distributed in the hope that 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 Fritzing. If not, see . ******************************************************************** $Revision: 6469 $: $Author: irascibl@gmail.com $: $Date: 2012-09-23 00:54:30 +0200 (So, 23. Sep 2012) $ ********************************************************************/ #ifndef WAITPUSHUNDOSTACK_H #define WAITPUSHUNDOSTACK_H #include #include #include #include #include class WaitPushUndoStack : public QUndoStack { Q_OBJECT public: WaitPushUndoStack(QObject * parent = 0); ~WaitPushUndoStack(); void waitPush(QUndoCommand *, int delayMS); void waitPushTemporary(QUndoCommand *, int delayMS); void resolveTemporary(); void deleteTemporary(); void deleteTimer(QTimer *); void addTimer(QTimer *); void push(QUndoCommand *); bool hasTimers(); #ifndef QT_NO_DEBUG public: void writeUndo(const QUndoCommand *, int indent, const class BaseCommand * parent); protected: QFile m_file; #endif protected: void clearDeadTimers(); void clearLiveTimers(); void clearTimers(QList &); protected: QList m_deadTimers; QList m_liveTimers; QMutex m_mutex; QUndoCommand * m_temporary; }; class CommandTimer : public QTimer { Q_OBJECT public: CommandTimer(QUndoCommand * command, int delayMS, WaitPushUndoStack * undoStack); protected slots: void timedout(); protected: QUndoCommand * m_command; QPointer m_undoStack; }; #endif fritzing-app-0.9.3b/tools/000077500000000000000000000000001272363601300154425ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/Fritzing.dmgCanvas/000077500000000000000000000000001272363601300211405ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/Fritzing.dmgCanvas/Disk Image000066400000000000000000075567421272363601300230120ustar00rootroot00000000000000bplist00T$topX$objectsX$versionY$archiverTroot5 TYfghipxyz{>U$null ZNS.objectsV$classWNS.keys4YdiskImage!  !"#$%&'()*+,-./0123456785:;<5>?@AB>CDEF5HIJ>KHMNOP5RF_internetEnabled[buildTargetXiconSize^toolbarVisible^windowContentsXlicenses_backgroundColor_statusBarVisibleZbackground_windowContentWidth_suggestedDiskImageFilePath_backgroundObject\nameFontSizeZviewLayout_backgroundImageZvolumeName_volumeSizeOption_defaultLicenseLanguage_iconPreviewVisibleZencryptionXautoopen_windowContentHeight]labelPositionVformatXpassword_suggestedDiskImageNameZvolumeIcon[finalFormatYwindowTop_itemInfoVisibleZwindowLeftZvolumeSize%3&2 *0( ,'(1+)xȀ UVWX  Z[\]^_`5bcd5Yinvisible_relativeFilePathXfilePathXlocationYisAnAliasd _../../../deploy/Fritzing.app_C/Users/andre/Documents/FritzingOrg/dev/fritzing/deploy/Fritzing.app_{100, 174.05469}jkloX$classesZ$classnamemn_DBDiskContentItemXNSObject_DBDiskContentItem Z[\]^q`5tuvId  _'../../../../../../../../../Applications]/Applications_{372, 176.83203}jk|}}~n^NSMutableArrayWNSArray ><C_compositedBackgroundImageTtypeUcolor]gradientAngle[endingColorUimageWobjects$"C jk~~n VNSRepsWNSColor\NSImageFlags U  _NSTIFFRepresentationOMM* ,*@[0SPs HV HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Kmjkn_NSBitmapImageRepZNSImageRep_NSBitmapImageRep \NSColorSpaceWNSWhiteD0 0jknWNSColorjknWNSImage UNSRGBO'0.9571161866 0.9571161866 0.9571161866 D0.5 VNSSize  Z{544, 364} Ù̀! ЀҀ" Հ#O MM*  l  < RS s "  appl mntrRGB XYZ   acspAPPLappl-appl descodscmxlcprt8wtptrXYZ0gXYZDbXYZXrTRClchad|,bTRClgTRCldescGeneric RGB ProfileGeneric RGB Profilemluc skSK(xhrHR(caES$ptBR&ukUA*frFU(Vaeobecn RGB profilGeneri ki RGB profilPerfil RGB genricPerfil RGB Genrico030;L=89 ?@>D09; RGBProfil gnrique RVBu( RGB r_icϏProfilo RGB genericoGenerisk RGB-profil| RGB \ |Obecn RGB profil RGB Allgemeines RGB-Profilltalnos RGB profilfn RGB cϏeNN, RGB 000000Profil RGB generic  RGBPerfil RGB genricoAlgemeen RGB-profielB#D%L RGB 1H'DGenel RGB ProfiliYleinen RGB-profiiliUniwersalny profil RGB1I89 ?@>D8;L RGBEDA *91JA RGB 'D9'EGeneric RGB ProfileGenerel RGB-beskrivelsetextCopyright 2007 Apple Inc., all rights reserved.XYZ RXYZ tM=XYZ Zus4XYZ (6curvsf32 B&ljkڢn_DBDiskImageBackground_DBDiskImageBackground UހTHFS+PTUDBZXFritzing , U- . /O 6MM* 2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>eYeYeYeY뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑范eYeY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>eY뙑뙑XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>rgrg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eYrg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XKL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"uXK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"ueY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rgu2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rgXK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK范L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑뙑范eY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑ueYeYeY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XKXK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"urg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0eY范뙑뙑eYL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0rguL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg范?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0范L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0뙑L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0uL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XKrg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>u范XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eYeYrg뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑뙑ueYeY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK߰j`xM@b/ KV xM@xM@ǵ2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"ﰗb/ KKKKKKKKKKKKj`Ŀ?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"賂V KKKKKKKKKKKKKKKKxM@Ŀu2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XKb/ KKKKKKKKKKKKKKKKKKKKyp2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"KKKKKKKKKKKKKKKKKKKKKKKm>0?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"j`KKKKKKKKKKKKKKKKKKKKKKKKKV rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0KKKKKKKKKKKKKKKKKKKKKKKKKKKV 2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rgKKKKKKKKKKKKKKKKKKKKKKKKKKKKKm>02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑V KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKyp2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"xM@KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKĿeY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"ĿKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKm>0u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"xM@KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKĿ뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eYKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK[P2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eYypKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范m>0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKyp2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑ĿKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKxM@2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKV 2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑ĿKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKb/ 2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKxM@2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"뙑KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg[PKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKĿ2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eYKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKV 2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKyp2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"j`KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKj`rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKV L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范m>0KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKǵ2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eYV KKKKKKKKKKKKKKKKKKKKKKKKKKKKyp2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"V KKKKKKKKKKKKKKKKKKKKKKKKKKj`뙑2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"V KKKKKKKKKKKKKKKKKKKKKKKKj`XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范m>0KKKKKKKKKKKKKKKKKKKKKK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>KKKKKKKKKKKKKKKKKKKm>02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"ߏj`KKKKKKKKKKKKKKKb/ rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范߰m>0KKKKKKKKKV j`ǵ2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0Ŀǵ2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XK2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"范L>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"u?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>范2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"?0eY2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"uL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"uL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY뙑?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>范rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"L>范rg2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"eY뙑uL>2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"XKeY范뙑뙑뙑뙑뙑뙑rgeY?02"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"2"--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  U S s H     HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmYAutomatic_Fritzing0.9.0b.dmg_'/Users/andre/Desktop/Fritzing0.9.0b.dmgjkn[DBDiskImage[DBDiskImagejkn\NSDictionary_NSKeyedArchiver'0:?DF}-@MXju".8JU`adfhikmopruwy{}   0v/=PY`ow #*2?ACEJSUXZcejlnw6?FYdw #8?ACEGLW`begprwy{4=BZrt}ˀ˅ˆˋ˔˥˧˩˫˰˹˻˾'<fotـٌٕٚ٧٬پfritzing-app-0.9.3b/tools/Fritzing.dmgCanvas/QuickLook/000077500000000000000000000000001272363601300230415ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/Fritzing.dmgCanvas/QuickLook/Preview.jpg000066400000000000000000000511131272363601300251650ustar00rootroot00000000000000JFIF XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmLExifMM*i,C      C  ," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?x+<0((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((/O<}(д}#h݄5H3ƥȸ 3{ !io8߈2j?U%~}i'ܚv?y%)fxxՌa>k\{W'&{W'&]+[s ? iϨ_ ?_ v?|!_ ж ;I ;Ihm?*_? iϨ_ ?_ v?|!_ ж ;I ;Ihm?*_? iϨ_ ?_ v?|!_ ж ;I ;Ihm?*_? iϨ_ ?_ v?|!_ ж ;I?} YrOTP9>9\>xJ"|:оxZDqU!/R{{گ0PP^޼_g>N_>W5v~÷~:5qe߈׃5 (&'2Tbk޳Y]?Yi 6d6 jxup~ʴ^|쑖}y\ܤnnZN2{x9މ#*+>%m3U"aER(+|c&Q]'4*2_+=n+IgQ_ՇQEQEQEQEQEQEQExKn#^UKmҴnY,pv'&k!i;0g';ךa!mŌ<:\ wK 3HkɋFxU'oGѭv؋$|;||/|< պ=03x%l}2kڌ?يd/&XaQ\nɷv߱CM]\4\ҊJ*.M?K?7=_ oѾ ^ BMFWQ+  *`v:kj++qm^h(s<\'W]4%4i''$Osh(iwG\Mv߳Oo?dӿ:ީ?^~.R9Gy&IH&۝ƪqץ[ݨbh У'F $}#isɪh ӌs?H_+v:rW =}#K|ᚚg>5N}i򋜸k 1 n$'Ҽľ2^F13-НAʐGnGTn, E=rxᎋ$iEo.h-תF$oú kv+$wS80s } ]gt_||sgc?4J`E9Ӟ?)X&(n1jqi*V}AgَHM>V.kZn>nǜXBǯ1 ǭ5s1(c.I~sGn2y땶jgo+S<((+|c&Q]'4*2_+=n+IgQ_ՇQEk;մ+1o4pE(UZ稩pբIgC W^G-]{Msu zGb9T# ?} KFVOv{O|F1+޹j7W:V'oROqʱDX!}|uR4{)=?gx~-uħSYU ̫bM#QErpӬQ?һ> v$ºpƉˍ =[8 }~bE޺GQE҅Q@Q@~Bq?dJz_[N7I___?QE~QE|(|Aŭj2EdC1~♼ =#[ӆ )kJN*qsRTf~޶?+K4k-&$%acEU@W3 L]X,Iwծ;fYeFڛ*ar'߆tϊK넹H-S8>rA{Fkuo?t j^+]k^k,\H_ HaL7gk#ژ<>k5W]wߩowWP>/5 Huh-\yXYQu%@ N} /~ዋmNB>do5bpJ$gB[| md+;sW?ՄPA$ICIbN8e?e&L68s x]eLzecOeG%W hX5m=ӛEe'neQ_aEPEP]'4*f8ɧTuٗR4yYq_K?^?((/Wl5A|)5uagsMsO,B'?J WV^TzjEE:QZҫQEQEWD/:o]y(v7-5GĨڮwok,2'9⩥ncrY.[f eΈk76̔$&:&#-.5ɛyA ·#p]:ml\ya@w=xVX+`_6Aԣxz9tGI7uӂK1<|*PyZ˪}U.P Qb?85OCҹ_l#ɴ"9% xUބIj[o߾8ZrNh5o CDH } `Cingrz=?2lwLތqZIU?{Inڨ $+[=ϥrsW9;r2:g{3໷=Wjܤ8_@xbZO.lGɞEWTQEQE K@Qҩ+!io8߈2j?U%~_O?~3׸GEWQEQE/ a~{Ɩ -̦_F\{r'  M>xJF̻Sg4gЬ~zC(vSIPVW4_xC^%eJ''Ŧ~gO,/Z,rxY|]1Ap"~Y?jo7~*p]LRlpr=>Ҷ?L[LkrzŒ^܋zdO)YE$=:%I'ުv{"ax-`wt=?JwK7֨:zp)(VR͞pqrh?^;<-|#UreOX\~ ^Nr^=b_F+可 ( ( /x%A@J޿_ZM_l.O?~˳ loxG6oWhѶ_,zQ_/3g}Mm?6Əo5{/0YShͿ_F~m|F?w>6oWhѶ_,zQGϯ7?m8`zQg}Mm?6Əo5{/0YShͿ_F~m|F?w>6oWhѶ_,zQGϯ7f{w'ĺpH ?QXh Gk/K_fsĮy~&>xO_ENaEPEPEPEPEPEP\8_]yǠZY/iPKZ_3_V1ky7Z]$K!Om=݉Qӏ2ҕ5V\cixWxm?Jl^[MbCh" f"cr$>:|Wⱟ0C,2,CNԷ)L:> Gk éGCBHiA li 32n6_|@hڏ-FV-nxEn ́͞ZNQ^*)+}cX1 _=K ?xLj1b_PϤ~fa2z!;VxW23ױ?0GVs?§RڧOKǫ֊>_Oχ_hkmS-mխFܟ3u߇֚n%q$n,ʼnɮ՗-I)ahP|Ԡ (7 ( ( {m/<_?9k]5BĚޣ/?o Örs+gGhvf$$I&WzҧNy[z} Eaf몱TQE~=Q@Q@Q@Q@Q@Q@q|k{BQ)޽şɴ1Ӳ׎6ΊR*3pwCbaR PvE ( ( ( ( ( ( ( ( ( ( ( ( ( ( +v({?Y_-~374Y$Ic thF$!IQ__ L|rNq⏋WFg >߷Ia'2%a,QQs#K95rܩ]?EI*Omc+|RR$ vhz}Y[i6sh˺m`l1@N7>N~0ԵMjJ-̫bD &0+ή"4iJWӥҵ΢m}ͫ6>+<_Czo=O_+Y4 P[bB-X]oļEUkҿj/S/P:$]zEԁa"HZ-G6B&t**o{k/'|km>g#]WĚ?}OU}3uƟ&^faKxMV~`0~m↗>-|9oǡY–ךqkcBۖ# n͔'΢V[N<+Iշ>?j:>xi?|}?]xB= iֶ?w9,):-y8h['\%Z (((((((((((( ?n+ciB]=/M(aqw VFڧ?߈0xhg^1:E{ ڥdv[Ȓʣ ڃ4=G>$q5= SB3_H,E,/ 3_gnH'wjM;o =+H -K]GVg.yBWZSJuJ^.N(ejz^WKK6}/?t _V_Ky6זl.$be^Hr?ړᗏ|/.oִX,5KXg1­*HTPO&f|Wg|k|: h ZiaCa[jmo5rT]Ѣ 77/B!i*6W~>E |q|㏏R'jρ9xS]Э |;cg.yy ؾRml;.gS?=Ke x}^_|#eɯߦ{":%H*H?1?gO4 +<z\7VVzvmkoi rGE-WeEO$mki|ҽrtSt7Z'OMmVEeP[y2,hF1&`1^E<7Q|U ~x+/Oj({i'|?i-l rbhb(ILimg7ׯ_ sKRuVZj^K^GbVƪI'd&k;6`_t?|/I尿LEzItMw (*($F %s^EIY(I7' (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ Ἅ%U*\TZfkY%moimvb4\*jQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEuJt|A>&K?QKOo[ɊO:T&>M5%t&]QEQEV;oѕ൴&dm{UczdQҬr\Ђҡ'b6r( (dO%ns뜚ɤciŠ( (~l觍_vGugh)|n9+amQLAEhxfVHIq%RK\U;Ď;V͋®B1˞JvtQE1Vǥid_@epړvvM̺(l/#(<7b"OxwIѯR[ӯXU=*QBy j:auZMn$V|)1.;Ğ ZmEka {Vq~4D/- C\17}s}}5ޝ$`=lWV`0EggĖvz]ƝKygġ I22pxuix~]7V1tp{m9q֛ۯ%%˿O}R ѮMIɕ7$Th޲D˘v0>ƒAiXimV'tcx|LWg`Xi T:+NNJ\&s+kzf[ϦTN, $pB)i:mͭaj1`*tCV^+[9=EXi%v(fݕ#9Ih`y-%|=^#Q7?4CڳI .tMc }w" Mj ݷ\v1w_2%$FMQZQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@-DOZ\2 BQE$op)(((((((((((((((((((((((((((((((((((((((((((fritzing-app-0.9.3b/tools/deploy_fritzing_mac.sh000077500000000000000000000026151272363601300220350ustar00rootroot00000000000000#!/bin/bash QTBIN=/Users/andre/Qt/5.6/clang_64/bin toolsdir=`dirname $BASH_SOURCE` cd $toolsdir cd .. workingdir=$(pwd) echo ">> working directory" echo $workingdir deploydir=$workingdir/../deploy-app echo ">> deploy directory" echo $deploydir rm -rf $deploydir mkdir $deploydir builddir=$workingdir/../release64 # this is pre-defined by Qt echo ">> build directory" echo $builddir echo ">> building fritzing from working directory" $QTBIN/qmake -o Makefile phoenix.pro make release # release is the type of build cp -r $builddir/Fritzing.app $deploydir echo ">> add .app dependencies" cd $deploydir $QTBIN/macdeployqt Fritzing.app -verbose=2 supportdir=$deploydir/Fritzing.app/Contents/MacOS echo ">> support directory" echo $supportdir echo ">> copy support files" cd $workingdir cp -rf sketches help translations readme.md LICENSE.CC-BY-SA LICENSE.GPL2 LICENSE.GPL3 $supportdir/ echo ">> clean translations" cd $supportdir rm ./translations/*.ts # remove translation xml files, since we only need the binaries in the release find ./translations -name "*.qm" -size -128c -delete # delete empty translation binaries echo ">> clone parts repository" git clone https://github.com/fritzing/fritzing-parts.git echo ">> build parts database and run fritzing" ./Fritzing -db "fritzing-parts/parts.db" # -pp "fritzing-parts" -f "." echo ">> launch Fritzing" cd $deploydir open Fritzing.app echo ">> done!" fritzing-app-0.9.3b/tools/fixfz/000077500000000000000000000000001272363601300165705ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/fixfz/fixfz.cpp000066400000000000000000000104141272363601300204220ustar00rootroot00000000000000#include "fixfz.h" #include #include #include #include #include #include #include #include QDir globalDir; void usage() { std::cout << "update fz files:" << std::endl << std::endl; std::cout << "usage: fixfz partsfolder" << std::endl << std::endl; std::cout << std::endl; } bool findFile(QDir & dir, QFile & resultFile, QString filename) { if (!dir.exists()) return false; QFileInfoList files = dir.entryInfoList(); foreach(QFileInfo fileInfo, files) { if (fileInfo.isFile()) { if (fileInfo.absoluteFilePath().endsWith(filename)) { resultFile.setFileName(fileInfo.absoluteFilePath()); return true; } continue; } if (fileInfo.fileName().compare(".") == 0) continue; if (fileInfo.fileName().compare("..") == 0) continue; if (fileInfo.isDir()) { QDir temp(fileInfo.absoluteFilePath()); if (findFile(temp, resultFile, filename)) { return true; } } } return false; } void handleFile(const QString & filename) { std::cout << "\t" << filename.toStdString() << std::endl; QFile file(filename); QString errorStr; int errorLine; int errorColumn; QDomDocument domDocument; if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { std::cout << "file isn't fritzing file (1)" << std::endl; return; } QDomElement root = domDocument.documentElement(); if (root.isNull()) { std::cout << "file isn't fritzing file (2)" << std::endl; return; } if (root.tagName() != "module") { std::cout << "can't find module tag" << std::endl; return; } QDomElement views = root.firstChildElement("views"); if (views.isNull()) { std::cout << "can't find views element" << std::endl; return; } QHash viewImages; QDomElement view = views.firstChildElement(); while (!view.isNull()) { QDomElement layers = view.firstChildElement("layers"); if (!layers.isNull()) { QDomElement layer = layers.firstChildElement("layer"); while (!layer.isNull()) { QString image = layer.attribute("image"); if (!image.isEmpty()) { layers.setAttribute("image", image); layer.removeAttribute("image"); } layer = layer.nextSiblingElement("layer"); } } viewImages.insert(view.tagName(), layers.attribute("image")); view = view.nextSiblingElement(); } QDomElement connectors = root.firstChildElement("connectors"); if (views.isNull()) { std::cout << "can't find connectors element" << std::endl; return; } QDomElement connector = connectors.firstChildElement("connector"); while (!connector.isNull()) { QDomElement views = connector.firstChildElement("views"); if (views.isNull()) { std::cout << "can't find views element" << std::endl; } QDomElement view = views.firstChildElement(); while (!view.isNull()) { QDomElement pin = view.firstChildElement("pin"); if (!pin.isNull()) { pin.setTagName("p"); QString id = pin.attribute("svgId"); QString image = viewImages.value(view.tagName()); QFile imageFile; if (findFile(globalDir, imageFile, image)) { if (imageFile.open(QIODevice::ReadOnly)) { QString temp = imageFile.readAll(); imageFile.close(); if (!temp.contains(id)) { std::cout << "--- missing " << id.toStdString() << " in " << view.tagName().toStdString() << " in " << imageFile.fileName().toStdString() << " ---" << std::endl; } } } } view = view.nextSiblingElement(); } connector = connector.nextSiblingElement("connector"); } QFile data(filename); if (data.open(QFile::WriteOnly | QFile::Truncate)) { QTextStream out(&data); out << domDocument.toString(); data.close(); } } void runDir(QDir & dir) { if (!dir.exists()) return; QFileInfoList files = dir.entryInfoList(); foreach(QFileInfo file, files) { if (file.isFile()) { if (file.fileName().endsWith(".fz")) { handleFile(file.absoluteFilePath()); } continue; } if (file.fileName().compare(".") == 0) continue; if (file.fileName().compare("..") == 0) continue; if (file.isDir()) { QDir temp(file.absoluteFilePath()); runDir(temp); } } } int main(int argc, char * argv[]) { if ((argc < 2)) { usage(); return 0; } globalDir.setPath(argv[1]); runDir(globalDir); } fritzing-app-0.9.3b/tools/fixfz/fixfz.h000066400000000000000000000000641272363601300200670ustar00rootroot00000000000000#ifndef FIXFZ_H #define FIXFZ_H #endif // FIXFZ_H fritzing-app-0.9.3b/tools/fixfz/fixfz.pro000066400000000000000000000001511272363601300204350ustar00rootroot00000000000000TEMPLATE = app TARGET = fxfz HEADERS = fixfz.h SOURCES = fixfz.cpp CONFIG += console QT += core gui xml fritzing-app-0.9.3b/tools/linguist_release.bat000066400000000000000000000001441272363601300214670ustar00rootroot00000000000000cd %~dp0 cd .. C:\Qt\Qt5.2.1-64bit\5.2.1\msvc2012_64\bin\lrelease -removeidentical phoenix.pro fritzing-app-0.9.3b/tools/linguist_translate.bat000066400000000000000000000001361272363601300220450ustar00rootroot00000000000000cd %~dp0 cd .. C:\Qt\Qt5.2.1-64bit\5.2.1\msvc2012_64\bin\lupdate -noobsolete phoenix.pro fritzing-app-0.9.3b/tools/linux_release_script/000077500000000000000000000000001272363601300216655ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/linux_release_script/obsolete/000077500000000000000000000000001272363601300235015ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/linux_release_script/obsolete/release.all.sh000066400000000000000000000004101272363601300262170ustar00rootroot00000000000000#!/bin/bash schroot -c hardy_i386 -u root cd /home/mariano/release ./release.sh exit schroot -c hardy_amd64 -u root cd /home/mariano/release ./release.sh exit cd /home/mariano/workspace/fritzing/release sudo chown mariano fritzing* sudo chgrp mariano fritzing* fritzing-app-0.9.3b/tools/linux_release_script/release.sh000077500000000000000000000063621272363601300236530ustar00rootroot00000000000000#!/bin/bash arch_aux=`uname -m` echo script_path="$(readlink -f "$0")" script_folder=$(dirname $script_path) echo "script_folder $script_folder" current_dir=$(pwd) echo "" echo "NOTE: Don't forget to set this script's QT_HOME variable" echo " Execute this script from outside the fritzing-app folder" echo "" if [ "$arch_aux" == 'x86_64' ] ; then arch='AMD64' QT_HOME="/home/andre/Qt/5.6/gcc_64" else arch='i386' QT_HOME="/home/andre/Qt/5.5/gcc" fi if [ "$1" = "" ] ; then echo "Usage: $0 " exit else relname=$1 #`date +%Y.%m.%d` fi PKG_OK=$(dpkg-query -W --showformat='${Status}\n' libquazip-dev) quazip='QUAZIP_LIB' if [ "`expr index "$PKG_OK" installed`" -gt 0 ] ; then quazip='QUAZIP_INSTALLED' echo "using installed quazip" else echo "using src/lib/quazip" fi app_folder=$(dirname $script_folder) app_folder=$(dirname $app_folder) cd $app_folder echo "appfolder $app_folder" echo "compiling... if this is not taking a long time, something is probably wrong" $QT_HOME/bin/qmake CONFIG+=release DEFINES+=$quazip make release_name=fritzing-$relname.linux.$arch release_folder="$current_dir/$release_name" echo "making release folder: $release_folder" mkdir $release_folder echo "copying release files" cp -rf sketches/ help/ translations/ Fritzing.sh Fritzing.1 fritzing.desktop fritzing.rc fritzing.appdata.xml install_fritzing.sh readme.md LICENSE.CC-BY-SA LICENSE.GPL2 LICENSE.GPL3 $release_folder/ mkdir $release_folder/icons cp resources/system_icons/linux/* $release_folder/icons/ mv Fritzing $release_folder/ chmod +x $release_folder/install_fritzing.sh cd $release_folder echo "cleaning translations" rm ./translations/*.ts # remove translation xml files, since we only need the binaries in the release find ./translations -name "*.qm" -size -128c -delete # delete empty translation binaries git clone https://github.com/fritzing/fritzing-parts.git echo "making library folders" mkdir lib mkdir lib/imageformats mkdir lib/sqldrivers mkdir lib/platforms cd lib echo "copying qt libraries" cp -d $QT_HOME/lib/libicudata.so* $QT_HOME/lib/libicui18n.so* $QT_HOME/lib/libicuuc.so.* $QT_HOME/lib/libQt5Concurrent.so* $QT_HOME/lib/libQt5Core.so* $QT_HOME/lib/libQt5DBus.so* $QT_HOME/lib/libQt5Gui.so* $QT_HOME/lib/libQt5Network.so* $QT_HOME/lib/libQt5SerialPort.so* $QT_HOME/lib/libQt5PrintSupport.so* $QT_HOME/lib/libQt5Sql.so* $QT_HOME/lib/libQt5Svg.so* $QT_HOME/lib/libQt5Xml.so* $QT_HOME/lib/libQt5Widgets.so* $QT_HOME/lib/libQt5XmlPatterns.so* $QT_HOME/lib/libQt5XcbQpa.so* . echo "copying qt plugins" cp $QT_HOME/plugins/imageformats/libqjpeg.so imageformats cp $QT_HOME/plugins/sqldrivers/libqsqlite.so sqldrivers cp $QT_HOME/plugins/platforms/libqxcb.so platforms echo "copying libgit2" cp -d $app_folder/../libgit2/build/libgit2.so* . mv ../Fritzing . # hide the executable in the lib folder mv ../Fritzing.sh ../Fritzing # rename Fritzing.sh to Fritzing chmod +x ../Fritzing LD_LIBRARY_PATH=$(pwd) export LD_LIBRARY_PATH ./Fritzing -db "$release_folder/fritzing-parts/parts.db" -pp "$release_folder/fritzing-parts" -f "$release_folder" cd $current_dir echo "compressing...." tar -cjf ./$release_name.tar.bz2 $release_name echo "cleaning up" rm -rf $release_folder echo "done!" fritzing-app-0.9.3b/tools/make.bat000066400000000000000000000006251272363601300170520ustar00rootroot00000000000000cd /d %~dp0 cd .. rem C:\Qt\qt-everywhere-opensource-src-4.8.5\bin\qmake.exe -t vcapp -spec win32-msvc2012 phoenix.pro C:\Qt\qt-everywhere-opensource-src-5.2.1-32\qtbase\bin\qmake.exe -t vcapp -spec win32-msvc2012 phoenix.pro -o Fritzing32.vcxproj C:\Qt\qt-everywhere-opensource-src-5.2.1-64\qtbase\bin\qmake.exe "QMAKE_TARGET.arch=x86_64" -t vcapp -spec win32-msvc2012 phoenix.pro -o Fritzing64.vcxproj fritzing-app-0.9.3b/tools/release_fritzing.bat000066400000000000000000000147151272363601300214760ustar00rootroot00000000000000echo off echo . echo you must start this script from the Visual Studio Command Line Window echo find this under the start menu at (depending on your version of Visual Studio): echo All Programs / Microsoft Visual Studio 2012 / Visual Studio Tools / Developer Command Prompt echo for the 64-bit build, use the 64-bit prompt: echo All Programs / Microsoft Visual Studio 2012 / Visual Studio Tools / VS2012 x64 Cross Tools Command Prompt echo. echo the script also assumes you have cloned the Fritzing git repository and are launching this script from within that repository echo. echo for a full release, run the script twice, once for a 64-built build, once for a 32-bit build echo you may need to change the script variable QTBIN to point to your Qt folder (once for 64-bit, once for 32-bit) echo. echo you may need to set the PATH in the script to your git for windows installation echo. echo you may need to change the script variable LIBGIT2 to find git2.dll echo. IF .%1 == . ( echo first parameter--release version--is missing, should be something like 0.8.6b EXIT /B ) IF .%2 == . ( echo second parameter--target architecture--is missing, should be either "32" for a 32-bit build or "64" for a 64-bit build EXIT /B ) IF .%3 == . ( echo third parameter--visual studio version--is missing, should be "2012", "2013", "2015" EXIT /B ) echo add the path for your git installation if it's not already there set PATH=%PATH%;"C:\Program Files (x86)\Git\bin"; echo set the path to the qt sdk bin folder IF %2==64 ( IF %3==2012 ( set QTBIN=C:\Qt\5.6\msvc2012_64\bin ) ELSE IF %3==2013 ( set QTBIN=C:\Qt\5.6\msvc2013_64\bin ) ELSE IF %3==2015 ( set QTBIN=C:\Qt\5.6\msvc2015_64\bin ) set arch=""QMAKE_TARGET.arch=x86_64"" ) ELSE ( IF %2==32 ( IF %3==2012 ( set QTBIN=C:\Qt\5.6\msvc2012\bin ) ELSE IF %3==2013 ( set QTBIN=C:\Qt\5.6\msvc2013\bin ) ELSE IF %3==2015 ( set QTBIN=C:\Qt\5.6\msvc2015\bin ) set arch=. ) ELSE ( echo second parameter--target architecture--should be either "32" for a 32-bit build or "64" for a 64-bit build EXIT /B ) ) set QMAKE=%QTBIN%\qmake.exe if not exist %QMAKE% echo '%QMAKE%' not found--please change the path to Qt\bin if not exist %QMAKE% EXIT /B n echo found qmake.exe echo. cd /d %~dp0 cd .. set LIBGIT2=%~dp0..\..\libgit2\build%2 rem set environment variable for qmake phoenix.pro set RELEASE_SCRIPT="release_script" %QMAKE% -o Makefile phoenix.pro %arch% echo building fritzing nmake release set DESTDIR=..\release%2 rem get the absolute path of DESTDIR pushd %DESTDIR% set DESTDIR=%CD% popd set RELEASE_NAME=%DESTDIR%\forzip\fritzing.%1.%2.pc echo setting up deploy folder. ignore any "The system cannot find ..." messages rmdir %DESTDIR%\deploy /s /q rmdir %DESTDIR%\forzip /s /q if exist %RELEASE_NAME%.zip ( del %RELEASE_NAME%.zip ) mkdir %DESTDIR%\deploy mkdir %DESTDIR%\forzip mkdir %DESTDIR%\deploy\platforms mkdir %DESTDIR%\deploy\lib mkdir %DESTDIR%\deploy\lib\imageformats mkdir %DESTDIR%\deploy\lib\sqldrivers mkdir %DESTDIR%\deploy\lib\printsupport echo deploy folder ready. any further "The system cannot find ..." messages represent significant problems with the script echo. echo copy qt libraries copy %QTBIN%\libEGL.dll %DESTDIR%\deploy\libEGL.dll copy %QTBIN%\libGLESv2.dll %DESTDIR%\deploy\libGLESv2.dll copy %QTBIN%\Qt5Core.dll %DESTDIR%\deploy\Qt5Core.dll copy %QTBIN%\Qt5Gui.dll %DESTDIR%\deploy\Qt5Gui.dll copy %QTBIN%\Qt5Network.dll %DESTDIR%\deploy\Qt5Network.dll copy %QTBIN%\Qt5PrintSupport.dll %DESTDIR%\deploy\Qt5PrintSupport.dll copy %QTBIN%\Qt5Sql.dll %DESTDIR%\deploy\Qt5Sql.dll copy %QTBIN%\Qt5Svg.dll %DESTDIR%\deploy\Qt5Svg.dll copy %QTBIN%\Qt5Widgets.dll %DESTDIR%\deploy\Qt5Widgets.dll copy %QTBIN%\Qt5Xml.dll %DESTDIR%\deploy\Qt5Xml.dll copy %QTBIN%\Qt5SerialPort.dll %DESTDIR%\deploy\Qt5SerialPort.dll xcopy /q %QTBIN%\icu*.dll %DESTDIR%\deploy /E /I copy %QTBIN%\..\plugins\imageformats\qjpeg.dll %DESTDIR%\deploy\lib\imageformats\qjpeg.dll copy %QTBIN%\..\plugins\sqldrivers\qsqlite.dll %DESTDIR%\deploy\lib\sqldrivers\qsqlite.dll copy %QTBIN%\..\plugins\platforms\qwindows.dll %DESTDIR%\deploy\platforms\qwindows.dll copy %QTBIN%\..\plugins\printsupport\windowsprintersupport.dll %DESTDIR%\deploy\lib\printsupport\windowsprintersupport.dll echo copying git2.dll from %LIBGIT2% copy %LIBGIT2%\git2.dll %DESTDIR%\deploy\git2.dll echo copying sketches, translations, help, README, LICENSE echo. copy %DESTDIR%\Fritzing.exe %DESTDIR%\deploy\Fritzing.exe xcopy /q .\translations %DESTDIR%\deploy\translations /E /I xcopy /q .\sketches %DESTDIR%\deploy\sketches /E /I xcopy /q .\help %DESTDIR%\deploy\help /E /I copy .\readme.md %DESTDIR%\deploy\readme.md copy .\LICENSE.GPL2 %DESTDIR%\deploy\LICENSE.GPL2 copy .\LICENSE.GPL3 %DESTDIR%\deploy\LICENSE.GPL3 copy .\LICENSE.CC-BY-SA %DESTDIR%\deploy\LICENSE.CC-BY-SA echo removing empty translation files echo. del %DESTDIR%\deploy\translations\*.ts set CURRENTDIR=%cd% cd %DESTDIR% cd deploy git clone https://github.com/fritzing/fritzing-parts.git del/s placeholder.txt cd translations for /f "usebackq delims=;" %%A in (`dir /b *.qm`) do If %%~zA LSS 1024 del "%%A" cd %CURRENTDIR% IF %2==32 ( echo make the executable compatible with windows xp "%VCINSTALLDIR%bin\editbin.exe" %DESTDIR%\deploy\Fritzing.exe /SUBSYSTEM:WINDOWS,5.01 /OSVERSION:5.1 ) echo copying vc redist files IF %2==32 ( set XFOLDER=x86 ) ELSE ( set XFOLDER=x64 ) IF %3==2012 ( copy "%VCINSTALLDIR%redist\%XFOLDER%\Microsoft.VC140.CRT\msvcp110.dll" %DESTDIR%\deploy\msvcp110.dll copy "%VCINSTALLDIR%redist\%XFOLDER%\Microsoft.VC140.CRT\msvcr110.dll" %DESTDIR%\deploy\msvcr110.dll ) ELSE IF %3==2013 ( copy "%VCINSTALLDIR%redist\%XFOLDER%\Microsoft.VC140.CRT\msvcp120.dll" %DESTDIR%\deploy\msvcp120.dll copy "%VCINSTALLDIR%redist\%XFOLDER%\Microsoft.VC140.CRT\msvcr120.dll" %DESTDIR%\deploy\msvcr120.dll ) ELSE IF %3==2015 ( copy "%VCINSTALLDIR%redist\%XFOLDER%\Microsoft.VC140.CRT\msvcp140.dll" %DESTDIR%\deploy\msvcp140.dll copy "%VCINSTALLDIR%redist\%XFOLDER%\Microsoft.VC140.CRT\vcruntime140.dll" %DESTDIR%\deploy\vcruntime140.dll ) echo run fritzing to create parts.db %DESTDIR%\deploy\Fritzing.exe -pp %DESTDIR%\deploy\fritzing-parts -db %DESTDIR%\deploy\fritzing-parts\parts.db echo moving deploy to %RELEASE_NAME% move %DESTDIR%\deploy %RELEASE_NAME% echo create zip file FOR /F %%i IN ("%DESTDIR%\forzip") DO SET SRC=%%~fi FOR /F %%i IN ("%DESTDIR%\fritzing.%1.%2.pc.zip") DO SET DEST=%%~fi CScript .\tools\zip.vbs %SRC% %DEST% echo donefritzing-app-0.9.3b/tools/schem2schem/000077500000000000000000000000001272363601300176435ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/schem2schem/.gitignore000066400000000000000000000013471272363601300216400ustar00rootroot00000000000000# Visual Studio folders and files # ##################### debug/ release/ *.opensdf *.user *.sln *.sdf *.suo *.vcx* *.pdb ipch/ # testing # ##################### working/ obsolete/ new_schematics/ *.txt # Compiled source # ################### *.pyc *.com *.class *.dll *.exe *.o *.so # Temporary files # ############### *.py~ *.xml~ # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods #*.7z #*.dmg #*.gz #*.iso #*.jar #*.rar #*.tar #*.zip # Logs and databases # ###################### #*.log #*.sql #*.sqlite # OS generated files # ###################### .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db .bash_history saved_state fritzing-app-0.9.3b/tools/schem2schem/main.cpp000066400000000000000000000001661272363601300212760ustar00rootroot00000000000000 #include "s2sapplication.h" int main(int argc, char *argv[]) { S2SApplication a(argc, argv); a.start(); } fritzing-app-0.9.3b/tools/schem2schem/make.bat000066400000000000000000000001461272363601300212510ustar00rootroot00000000000000cd %~dp0 C:\Qt\qt-everywhere-opensource-src-4.8.5\bin\qmake.exe -t vcapp -spec win32-msvc2012 s2s.pro fritzing-app-0.9.3b/tools/schem2schem/readme.md000066400000000000000000000002051272363601300214170ustar00rootroot00000000000000Fritzing tool for converting old-style to new-style (0.1" grid) schematics See http://blog.fritzing.org/2014/01/23/0-8-6-schematics/fritzing-app-0.9.3b/tools/schem2schem/s2s.pro000066400000000000000000000012161272363601300210740ustar00rootroot00000000000000# ------------------------------------------------- # Project created by QtCreator 2011-02-16T09:56:30 # ------------------------------------------------- QT += xml \ gui \ svg \ TARGET = s2s CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp \ s2sapplication.cpp \ ../../src/utils/s2s.cpp \ ../../src/utils/textutils.cpp \ ../../src/utils/schematicrectconstants.cpp \ HEADERS += s2sapplication.h \ ../../src/utils/s2s.h \ ../../src/utils/textutils.h \ ../../src/utils/schematicrectconstants.h \ RESOURCES += ../../phoenixresources.qrc win32 { DEFINES += _CRT_SECURE_NO_DEPRECATE } fritzing-app-0.9.3b/tools/schem2schem/s2sapplication.cpp000066400000000000000000000117171272363601300233110ustar00rootroot00000000000000#include "s2sapplication.h" #include "stdio.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../src/utils/textutils.h" #include "../../src/utils/schematicrectconstants.h" #include "../../src/utils/s2s.h" #include ///////////////////////////////// // // TODO: // // pin numbers in brd2svg--something to add to params files? // // copy old schematics to obsolete (with prefix) // readonly old style vs. read/write new style // // update wire thickness in fritzing schematic view // update part label size (and font?) in fritzing // // controller_wiringmini a0 pin is fucked // // update internal schematics generators // update brd2svg schematics generator // /////////////////////////////////////////////////////// S2SApplication::S2SApplication(int argc, char *argv[]) : QCoreApplication(argc, argv) { m_fzpzStyle = false; } void S2SApplication::start() { if (!initArguments()) { usage(); return; } int ix = QFontDatabase::addApplicationFont(":/resources/fonts/DroidSans.ttf"); if (ix < 0) return; ix = QFontDatabase::addApplicationFont(":/resources/fonts/DroidSans-Bold.ttf"); if (ix < 0) return; ix = QFontDatabase::addApplicationFont(":/resources/fonts/DroidSansMono.ttf"); if (ix < 0) return; ix = QFontDatabase::addApplicationFont(":/resources/fonts/OCRA.ttf"); if (ix < 0) return; m_fzpDir.setPath(m_fzpPath); m_oldSvgDir.setPath(m_oldSvgPath); m_newSvgDir.setPath(m_newSvgPath); QFile file(m_filePath); if (!file.open(QFile::ReadOnly)) { message(QString("unable to open %1").arg(m_filePath)); return; } S2S s2s(m_fzpzStyle); s2s.setSvgDirs(m_oldSvgDir, m_newSvgDir); QTextStream in(&file); while ( !in.atEnd() ) { QString line = in.readLine().trimmed(); QString schematicFileName; s2s.onefzp(m_fzpDir.absoluteFilePath(line), schematicFileName); } } void S2SApplication::usage() { message("\nusage: s2s " "-ff " "-f " "-os " "-ns " "[-fzpz (translate to fzpz style filenames)] " "\n" ); } bool S2SApplication::initArguments() { QStringList args = QCoreApplication::arguments(); for (int i = 0; i < args.length(); i++) { if ((args[i].compare("-h", Qt::CaseInsensitive) == 0) || (args[i].compare("-help", Qt::CaseInsensitive) == 0) || (args[i].compare("--help", Qt::CaseInsensitive) == 0)) { return false; } if (args[i].compare("-fzpz", Qt::CaseInsensitive) == 0) { m_fzpzStyle = true; continue; } if (i + 1 < args.length()) { if ((args[i].compare("-ff", Qt::CaseInsensitive) == 0) || (args[i].compare("--ff", Qt::CaseInsensitive) == 0)) { m_fzpPath = args[++i]; } else if ((args[i].compare("-os", Qt::CaseInsensitive) == 0) || (args[i].compare("--os", Qt::CaseInsensitive) == 0)) { m_oldSvgPath = args[++i]; } else if ((args[i].compare("-ns", Qt::CaseInsensitive) == 0) || (args[i].compare("--ns", Qt::CaseInsensitive) == 0)) { m_newSvgPath = args[++i]; } else if ((args[i].compare("-f", Qt::CaseInsensitive) == 0) || (args[i].compare("--f", Qt::CaseInsensitive) == 0)) { m_filePath = args[++i]; } } } if (m_fzpPath.isEmpty()) { message("-ff fzp path parameter missing"); usage(); return false; } if (m_oldSvgPath.isEmpty()) { message("-os old svg path parameter missing"); usage(); return false; } if (m_newSvgPath.isEmpty()) { message("-ns new svg path parameter missing"); usage(); return false; } if (m_filePath.isEmpty()) { message("-f fzp list file parameter missing"); usage(); return false; } QStringList paths; paths << m_fzpPath << m_oldSvgPath << m_newSvgPath; foreach (QString path, paths) { QDir directory(path); if (!directory.exists()) { message(QString("path '%1' not found").arg(path)); return false; } } QFile file(m_filePath); if (!file.exists()) { message(QString("file path '%1' not found").arg(m_filePath)); return false; } return true; } void S2SApplication::message(const QString & msg) { // QTextStream cout(stdout); // cout << msg; // cout.flush(); qDebug() << msg; } fritzing-app-0.9.3b/tools/schem2schem/s2sapplication.h000066400000000000000000000012611272363601300227470ustar00rootroot00000000000000#ifndef APPLICATION_H #define APPLICATION_H #include #include #include #include #include #include class S2SApplication : public QCoreApplication { Q_OBJECT public: S2SApplication(int argc, char *argv[]); void start(); protected: void usage(); void message(const QString &); bool initArguments(); void saveFile(const QString & content, const QString & path); protected: QString m_fzpPath; QDir m_fzpDir; QString m_oldSvgPath; QDir m_oldSvgDir; QString m_newSvgPath; QDir m_newSvgDir; QString m_filePath; bool m_fzpzStyle; }; #endif // APPLICATION_H fritzing-app-0.9.3b/tools/scripts/000077500000000000000000000000001272363601300171315ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/checkResources.py000066400000000000000000000062051272363601300224560ustar00rootroot00000000000000 import getopt, sys, os, re def usage(): print """ usage: checkResources.py -r -q -s directory is a folder containing resource files. In each file in the directory or its subfolders, check whether the file is listed in the .qrc file then for each entry in qrc, check that it exists in a file in the sources """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hr:q:s:", ["help", "resources", "qrc", "sources"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() return resourcesDir = None sourcesDir = None qrcPath = None for o, a in opts: #print o #print a if o in ("-r", "--resources"): resourcesDir = a elif o in ("-s", "--sources"): sourcesDir = a elif o in ("-q", "--qrc"): qrcPath = a elif o in ("-h", "--help"): usage() return else: assert False, "unhandled option" if not(resourcesDir): print "missing resources directory" usage() return if not(sourcesDir): print "missing sources directory" usage() return if not(qrcPath): print "missing qrc path" usage() return qrc = "" try: qrcFile = open(qrcPath, "r") qrc = qrcFile.read() qrcFile.close() except: pass if len(qrc) == 0: print "unable to read qrc file", qrcPath return for root, dirs, files in os.walk(resourcesDir, topdown=False): for filename in files: suffix = root.replace(resourcesDir, "resources") lookfor = (suffix + "/" + filename).replace("\\", "/"); if not lookfor in qrc: print "resource file not in qrc", os.path.join(root, filename) print "" print "" print "" lines = qrc.split("\n") lines2 = [] for line in lines: if not "" in line: continue lix = line.index("") rix = line.index(" # # is a folder, with subfolders, containing .cpp and .h files. # update Copyright (c) 2007-xxxx Fritzing import getopt, sys, os, re, time def usage(): print """ usage: checkcopyright.py -d is a folder, with subfolders, containing .cpp and .h files. update Copyright (c) 2007-xxxx Fritzing """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hd:", ["help", "directory"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) outputDir = None findtext = "" gotnot = 0 for o, a in opts: #print o #print a if o in ("-d", "--directory"): outputDir = a elif o in ("-h", "--help"): usage() sys.exit(2) else: assert False, "unhandled option" if(not(outputDir)): usage() sys.exit(2) for root, dirs, files in os.walk(outputDir, topdown=False): for filename in files: if filename.endswith(".h") or filename.endswith(".cpp"): try: path = os.path.join(root, filename) infile = open(path, "r") ttuple = time.localtime(os.path.getmtime(path)) year = ttuple[0] txt = infile.read(); infile.close(); pattern = 'Copyright \(c\) (\d\d\d\d)-(\d\d\d\d) Fritzing' m = re.search(pattern, txt) if (m): if (year > int(m.group(2))): newtxt = re.sub(pattern, 'Copyright (c) ' + m.group(1) + '-' + str(year) + ' Fritzing', txt) print "{0} {1} {2}".format(path, year, m.group(2)) #print "{0}".format(newtxt[:150]) infile = open(path, "w") infile.write(newtxt) infile.close(); except IOError as (errno, strerror): print "exception {0} {1}".format(path, strerror) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/checkino.py000066400000000000000000000064271272363601300212770ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # lots of borrowing from http://code.activestate.com/recipes/252508-file-unzip/ import getopt, sys, os, os.path, re, zipfile, shutil, xml.dom.minidom, xml.dom import xml.etree.ElementTree as ET def usage(): print """ usage: checkino.py -f [from directory] -e [=yes extract .ino files] For each fzz file in the from directory, see whether the fz specifies an ino file and if it matches the ino file name. Probably safest to make a copy of the from directory first """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "e:f:", ["extract", "pull"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() return inputDir = None extract = None for o, a in opts: #print o #print a if o in ("-f", "--from"): inputDir = a elif o in ("-e", "--extract"): extract = a else: print "unhandled option",o usage() return if not(inputDir): print "missing 'from' argument" usage() return try: import zlib compression = zipfile.ZIP_DEFLATED except: compression = zipfile.ZIP_STORED for root, dirs, files in os.walk(inputDir, topdown=False): for fzz in files: if not fzz.endswith('.fzz'): continue #print fzz fzzpath = os.path.join(root, fzz) tempDir = inputDir + os.sep + "___temp___" shutil.rmtree(tempDir, 1) os.mkdir(tempDir) zf = zipfile.ZipFile(fzzpath) zf.extractall(tempDir) zf.close() fzzbase = os.path.splitext(fzz)[0] renamed = False inos = [] for fz in os.listdir(tempDir): if fz.endswith(".ino"): if extract == "yes": inoPath = os.path.join(tempDir, fz) shutil.copyfile(inoPath, os.path.join(inputDir, fz)) print "copy", inoPath, os.path.join(inputDir, fz) inos.append(fz) continue if not fz.endswith(".fz"): continue try: fzpath = os.path.join(tempDir, fz) tree = ET.parse(fzpath) top = tree.getroot() for program in top.iter('program'): print "program", program.text, fzzpath except: print "exception", fzpath, sys.exc_info()[0] return for ino in inos: print " ino", ino, fzzpath if renamed: os.remove(fzzpath) zf = zipfile.ZipFile(fzzpath, mode='w') for fn in os.listdir(tempDir): zf.write(os.path.join(tempDir, fn), fn, compression) zf.close() shutil.rmtree(tempDir, 1) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/copyif.py000066400000000000000000000024721272363601300210010ustar00rootroot00000000000000 import getopt, sys, os, re, time, shutil def usage(): print """ usage: copyif.py -s -d copies files from source into dest, if the file already exists in dest """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hs:d:", ["help", "source","dest"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() return source = None dest = None for o, a in opts: #print o #print a if o in ("-d", "--dest"): dest = a elif o in ("-s", "--source"): source = a elif o in ("-h", "--help"): usage() return else: print "unknown option", o return if not dest: print "missing -d (dest) argument" usage() return if not source: print "missing -s (source) argument" usage() return fs = [] for f in os.listdir(source): df = os.path.join(dest, f) if os.path.exists(df) and os.path.isfile(df): shutil.copy(os.path.join(source,f), df) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/fixino.py000066400000000000000000000102011272363601300207710ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # lots of borrowing from http://code.activestate.com/recipes/252508-file-unzip/ import getopt, sys, os, os.path, re, zipfile, shutil, xml.dom.minidom, xml.dom import xml.etree.ElementTree as ET def usage(): print """ usage: fix.py -f [from directory] For each fzz file in the from directory, remove all .ino files inside the fzz and elements in the internal fz file. if the fzz has a matching .ino file (external, in the same folder) then copy the ino file inside the fzz and add a element to the fz Afterwards you must delete the external.ino files Probably safest to make a copy of the from directory first """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "f:", ["from"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() return fromDir = None for o, a in opts: #print o #print a if o in ("-f", "--from"): fromDir = a else: print "unhandled option",o usage() return if not(fromDir): print "missing 'from' argument" usage() return try: import zlib compression = zipfile.ZIP_DEFLATED except: compression = zipfile.ZIP_STORED for root, dirs, files in os.walk(fromDir, topdown=False): for fzz in files: if not fzz.endswith('.fzz'): continue #print fzz fzzpath = os.path.join(root, fzz) inopath = fzzpath.replace(".fzz", ".ino") ino = os.path.basename(inopath) gotIno = os.path.isfile(inopath) tempDir =fromDir + os.sep + "___temp___" shutil.rmtree(tempDir, 1) os.mkdir(tempDir) zf = zipfile.ZipFile(fzzpath) zf.extractall(tempDir) zf.close() modified = False print fzzpath # remove old ino files first for fino in os.listdir(tempDir): if fino.endswith(".ino"): print " remove old", fino os.remove(os.path.join(tempDir, fino)) modified = True for fz in os.listdir(tempDir): if not fz.endswith(".fz"): continue try: fzpath = os.path.join(tempDir, fz) tree = ET.parse(fzpath) top = tree.getroot() programs = [] for ps in top.findall('programs'): programs.append(ps) for ps in programs: fzmodified = True print " remove old programs" top.remove(ps) if gotIno: shutil.copyfile(inopath, os.path.join(tempDir, ino)) ps = ET.SubElement(top, "programs") ps.set("pid", "") program = ET.SubElement(ps, "program") program.set("language", "Arduino") program.set("programmer", "") program.text = ino fzmodified = True print " copy", ino if fzmodified: modified = True tree.write(fzpath) except: print "exception", fzpath, sys.exc_info()[0] return if modified: os.remove(fzzpath) zf = zipfile.ZipFile(fzzpath, mode='w') for fn in os.listdir(tempDir): zf.write(os.path.join(tempDir, fn), fn, compression) zf.close() shutil.rmtree(tempDir, 1) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/fzzrename.py000066400000000000000000000054301272363601300215060ustar00rootroot00000000000000# usage: # fzzrename.py -f [fzz original path] -t [fzz new path] # renames fzz file as well as internal fz file. if the -t parameter is not a directory, the fzz is renamed but not copied # lots of borrowing from http://code.activestate.com/recipes/252508-file-unzip/ import getopt, sys, os, os.path, re, zipfile, shutil def usage(): print """ usage: fzzrename.py -f [fzz original path] -t [fzz new path] renames fzz file as well as internal fz file. if the -t parameter is not a directory, the fzz is renamed but not copied """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hf:t:", ["help", "from", "to"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) fromName = None toName = None for o, a in opts: #print o #print a if o in ("-f", "--from"): fromName = a elif o in ("-t", "--to"): toName = a elif o in ("-h", "--help"): usage() sys.exit(2) else: assert False, "unhandled option" if(not(fromName)): usage() sys.exit(2) if not(fromName.endswith(".fzz")): print "-f is not .fzz" usage() sys.exit(2) if(not(toName)): usage() sys.exit(2) if not(toName.endswith(".fzz")): print "-t is not .fzz" usage() sys.exit(2) outputDir = os.path.dirname(toName) if len(outputDir) == 0: outputDir = os.path.dirname(fromName) toName = outputDir + "/" + toName else: if not outputDir.endswith(':') and not os.path.exists(outputDir): os.mkdir(outputDir) tempDir = outputDir + "/" + "___temp___" shutil.rmtree(tempDir, 1) os.mkdir(tempDir) zf = zipfile.ZipFile(fromName) zf.extractall(tempDir) fromBaseName = os.path.basename(fromName) toBaseName = os.path.basename(toName) fromFzName = os.path.splitext(fromBaseName)[0] + ".fz" toFzName = os.path.splitext(toBaseName)[0] + ".fz" print os.path.join(tempDir, fromFzName), os.path.join(tempDir, toFzName) os.rename(os.path.join(tempDir, fromFzName), os.path.join(tempDir, toFzName)) # helpful examples in http://www.doughellmann.com/PyMOTW/zipfile/ try: import zlib compression = zipfile.ZIP_DEFLATED except: compression = zipfile.ZIP_STORED zf = zipfile.ZipFile(outputDir + "/" + toBaseName, mode='w') for fn in os.listdir(tempDir): zf.write(os.path.join(tempDir, fn), fn, compression) zf.close() shutil.rmtree(tempDir, 1) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/getboardsize.py000066400000000000000000000124021272363601300221640ustar00rootroot00000000000000# usage: # getboardsize.py -f # # Unzip and parse the fzzfile looking for a board. # If only one board is found, return its size (x by y) in millimeters. # Otherwise return an error. import getopt, sys, os, re, xml.dom.minidom, xml.dom, zipfile def usage(): print """ usage: getboardsize.py -f Unzip and parse the fzzfile looking for a board. If only one board is found, return its size (x by y) in millimeters. Otherwise return an error. """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hd:f:", ["help", "file"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) fzz = None for o, a in opts: #print o #print a if o in ("-f", "--file", "--fzz"): fzz = a elif o in ("-h", "--help"): usage() sys.exit(2) else: assert False, "unhandled option" if not(fzz): usage() sys.exit(2) if not os.path.exists(fzz): assert False, "'" + fzz + "' not found" if not fzz.endswith("fzz"): assert False, "'" + fzz + "' doesn't end with 'fzz'" zf = None try: zf = zipfile.ZipFile(fzz) except: assert False, "'" + fzz + "' is not a zip file" fzString = None for i, name in enumerate(zf.namelist()): if name.endswith('fz'): fzString = zf.read(name) break assert fzString, "no 'fz' file found in '" + fzz + "'" fzdom = None #try: fzdom = xml.dom.minidom.parseString(fzString) #except: #assert False, "unable to parse '" + fzz + "'" root = fzdom.documentElement assert root.tagName == "module", "'" + fzz + "' has no 'module' root" instances = root.getElementsByTagName("instance") assert instances.length > 0, "'" + fzz + "' has no instances" pairs = [] customPairs = {} #now look for custom boards for i, name in enumerate(zf.namelist()): if name.endswith('fzp'): fzpString = zf.read(name) dom = None try: dom = xml.dom.minidom.parseString(fzpString) except: continue if dom == None: continue root = dom.documentElement if root.tagName != 'module': continue custom = None titles = root.getElementsByTagName("title") for title in titles: titleString = getText(title.childNodes) if "Custom PCB" in titleString: custom = 1 break if not custom: continue descriptions = root.getElementsByTagName("description") for description in descriptions: descriptionString = getText(description.childNodes) floatMatchString = r'([0-9]*\.?[0-9]+)' fullMatchString = floatMatchString + r"x" + floatMatchString + r"mm" match = re.search(fullMatchString, descriptionString) if not match: continue #print "match", match.groups() if len(match.groups()) < 2: continue w = float(match.group(1)) h = float(match.group(2)) customPairs[root.getAttribute("moduleId")] = [w,h] break #now look for board instances in the fz file: for instance in instances: id = instance.getAttribute('moduleIdRef') if id == 'RectanglePCBModuleID' or id == 'TwoLayerRectanglePCBModuleID': w = None h = None properties = instance.getElementsByTagName("property") for property in properties: pname = property.getAttribute("name") if pname == "width": w = float(property.getAttribute("value")) elif pname == "height": h = float(property.getAttribute("value")) pairs.append(w) pairs.append(h) elif id == '423120090505' or id == '423120090505_2': #arduino shield pairs.append(69.215) #width="2.725in" pairs.append(53.37556) #height="2.1014in" else: pair = None try: pair = customPairs[id] except: pass if pair != None: pairs.append(pair[0]) pairs.append(pair[1]) print "pairs", pairs assert len(pairs) >= 2, "no boards found in '" + fzz + "'" assert len(pairs) == 2, "multiple boards found in '" + fzz + "'" print "result", pairs return pairs def getText(nodelist): rc = [] for node in nodelist: if node.nodeType == node.TEXT_NODE: rc.append(node.data) return ''.join(rc) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/multifind.py000066400000000000000000000042641272363601300215040ustar00rootroot00000000000000# usage: # multifind.py -d [directory] [-f [text]]+ -s [suffix] # # directory is a folder containing [suffix] files. # In each [suffix] file in the directory or its subfolders, # display all files that match all the texts ('and' search) import getopt, sys, os, re def usage(): print """ usage: multifind.py -d [directory] [-f [text]]+ -s [suffix] directory is a folder containing [suffix] files. In each [suffix] file in the directory or its subfolders, display all files that match all the texts ('and' search) """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hd:f:s:", ["help", "directory", "find", "suffix"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) inputDir = None findtexts = [] suffix = "" for o, a in opts: #print o #print a if o in ("-d", "--directory"): inputDir = a elif o in ("-h", "--help"): usage() sys.exit(2) elif o in ("-f", "--find"): findtexts.append(a) elif o in ("-s", "--suffix"): suffix = a; else: assert False, "unhandled option" if(not(inputDir)): usage() sys.exit(2) if len(findtexts) <= 0: usage() sys.exit(2) for root, dirs, files in os.walk(inputDir, topdown=False): for filename in files: if (filename.endswith(suffix)): infile = open(os.path.join(root, filename), "r") content = infile.read(); infile.close(); found = 0 for findtext in findtexts: rslt = content.find(findtext) if (rslt >= 0): found += 1 else: break if found == len(findtexts): print "found {0}".format(os.path.join(root, filename)) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/obsolete/000077500000000000000000000000001272363601300207455ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/875fzpzs.py000066400000000000000000000060521272363601300227420ustar00rootroot00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- import urllib2 import sys import os import traceback import re import getopt import HTMLParser def usage(): print """ usage: 875fzpzs.py -d is a to contain downloaded fzpz files. """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hd:", ["help", "directory"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() return outputDir = None for o, a in opts: #print o #print a if o in ("-d", "--directory"): outputDir = a elif o in ("-h", "--help"): usage() return else: assert False, "unhandled option" if not(outputDir): print "missing -d {directory} parameter" usage() return if not os.path.exists(outputDir): print "output directory doesn't exist" usage() return mainUrl = "https://code.google.com/p/fritzing/issues/detail?id=875" increment = 0 try: req = urllib2.Request(mainUrl) #print "req", req response = urllib2.urlopen(req) print "response", response html = response.read() print "got html" parser = HTMLParser.HTMLParser() pattern = r'download' regex = re.compile(pattern, re.IGNORECASE) pattern2 = r'name=([^\&]*)' regex2 = re.compile(pattern2, re.IGNORECASE) pattern3 = r'(\d*)[^\d]' regex3 = re.compile(pattern3, re.IGNORECASE) commentPhrase = 'href="/p/fritzing/issues/detail?id=875#c' for match in regex.finditer(html): url = "http:" + parser.unescape(match.group(1)) try: prefix = "" ix = html.rfind(commentPhrase, 0, match.start()) if (ix > 0): match3 = re.search(regex3, html[ix + len(commentPhrase):]) try: int(match3.group(1)) prefix = match3.group(1).zfill(4) except: pass if prefix == "": prefix = "0000" match2 = re.search(regex2, url) basename = match2.group(1) print "downloading", url, "to", basename f = urllib2.urlopen(url) path = os.path.join(outputDir, prefix + "_" + basename) with open(path, "wb") as local_file: local_file.write(f.read()) except: info = sys.exc_info() traceback.print_exception(info[0], info[1], info[2]) except: info = sys.exc_info() traceback.print_exception(info[0], info[1], info[2]) if __name__ == '__main__': main() fritzing-app-0.9.3b/tools/scripts/obsolete/checksvnkywds.py000066400000000000000000000042161272363601300242100ustar00rootroot00000000000000# usage: # checksvnkywds.py -d # # is a folder, with subfolders, containing .cpp and .h files. # see which .cpp or .h files are missing svn keywords import getopt, sys, os, re def usage(): print """ usage: checksvnkywds.py -d [directory] directory is a folder, with subfolders, containing .cpp or .h files. see which .cpp or .h files are missing svn keywords """ def main(): try: opts, args = getopt.getopt(sys.argv[1:], "hd:", ["help", "directory"]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) outputDir = None findtext = "" gotnot = 0 for o, a in opts: #print o #print a if o in ("-d", "--directory"): outputDir = a elif o in ("-h", "--help"): usage() sys.exit(2) else: assert False, "unhandled option" if(not(outputDir)): usage() sys.exit(2) for root, dirs, files in os.walk(outputDir, topdown=False): for filename in files: if filename.endswith(".h") or filename.endswith(".cpp"): svgFilename = os.path.join(root, ".svn", "prop-base", filename + ".svn-base"); gotIt = 1 try: infile = open(svgFilename, "r") txt = infile.read(); infile.close(); if not txt.find("svn:keywords"): gotIt = 0 elif not txt.find("Date"): gotIt = 0 elif not txt.find("Author"): gotIt = 0 elif not txt.find("Revision"): gotIt = 0 except IOError as (errno, strerror): gotIt = 0 if (not gotIt): print "{0}".format(os.path.join(root, filename)) if __name__ == "__main__": main() fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/000077500000000000000000000000001272363601300240165ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/README000066400000000000000000000001561272363601300247000ustar00rootroot00000000000000How to use the part-o-matic. The part-o-matic is a small python script that can be used to quickly generate fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/000077500000000000000000000000001272363601300254465ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/arduino-pcb.cfg000066400000000000000000000001201272363601300303230ustar00rootroot00000000000000[arduino-shield-pcb.svg] holeRadius: 24 strokeWidth: 21 DATE: $DATE UUID: $UUID fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/basic-resistor-025-svg.cfg000066400000000000000000000043771272363601300321740ustar00rootroot00000000000000[resistor_10.svg] stripe1: #8A3D06 stripe2: #000000 stripe3: #000000 [resistor_15.svg] stripe1: #8A3D06 stripe2: #00A33D stripe3: #000000 [resistor_22.svg] stripe1: #C40808 stripe2: #C40808 stripe3: #000000 [resistor_33.svg] stripe1: #FF4D00 stripe2: #FF4D00 stripe3: #000000 [resistor_47.svg] stripe1: #FFD500 stripe2: #8210D2 stripe3: #000000 [resistor_68.svg] stripe1: #0060B6 stripe2: #8C8C8C stripe3: #000000 [resistor_100.svg] stripe1: #8A3D06 stripe2: #000000 stripe3: #8A3D06 [resistor_150.svg] stripe1: #8A3D06 stripe2: #00A33D stripe3: #8A3D06 [resistor_220.svg] stripe1: #C40808 stripe2: #C40808 stripe3: #8A3D06 [resistor_330.svg] stripe1: #FF4D00 stripe2: #FF4D00 stripe3: #8A3D06 [resistor_470.svg] stripe1: #FFD500 stripe2: #8210D2 stripe3: #8A3D06 [resistor_680.svg] stripe1: #0060B6 stripe2: #8C8C8C stripe3: #8A3D06 [resistor_1k.svg] stripe1: #8A3D06 stripe2: #000000 stripe3: #C40808 [resistor_1.5k.svg] stripe1: #8A3D06 stripe2: #00A33D stripe3: #C40808 [resistor_2.2k.svg] stripe1: #C40808 stripe2: #C40808 stripe3: #C40808 [resistor_3.3k.svg] stripe1: #FF4D00 stripe2: #FF4D00 stripe3: #C40808 [resistor_4.7k.svg] stripe1: #FFD500 stripe2: #8210D2 stripe3: #C40808 [resistor_6.8k.svg] stripe1: #0060B6 stripe2: #8C8C8C stripe3: #C40808 [resistor_10k.svg] stripe1: #8A3D06 stripe2: #000000 stripe3: #FF4D00 [resistor_15k.svg] stripe1: #8A3D06 stripe2: #00A33D stripe3: #FF4D00 [resistor_22k.svg] stripe1: #C40808 stripe2: #C40808 stripe3: #FF4D00 [resistor_27k.svg] stripe1: #C40808 stripe2: #8210D2 stripe3: #FF4D00 [resistor_33k.svg] stripe1: #FF4D00 stripe2: #FF4D00 stripe3: #FF4D00 [resistor_47k.svg] stripe1: #FFD500 stripe2: #8210D2 stripe3: #FF4D00 [resistor_68k.svg] stripe1: #0060B6 stripe2: #8C8C8C stripe3: #FF4D00 [resistor_100k.svg] stripe1: #8A3D06 stripe2: #000000 stripe3: #FFD500 [resistor_150k.svg] stripe1: #8A3D06 stripe2: #00A33D stripe3: #FFD500 [resistor_220k.svg] stripe1: #C40808 stripe2: #C40808 stripe3: #FFD500 [resistor_330k.svg] stripe1: #FF4D00 stripe2: #FF4D00 stripe3: #FFD500 [resistor_470k.svg] stripe1: #FFD500 stripe2: #8210D2 stripe3: #FFD500 [resistor_680k.svg] stripe1: #0060B6 stripe2: #8C8C8C stripe3: #FFD500 [resistor_1M.svg] stripe1: #8A3D06 stripe2: #000000 stripe3: #00A33D fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/basic-resistor-025.cfg000066400000000000000000000060171272363601300313700ustar00rootroot00000000000000[resistor_10_mini.fzp] resistance: 10 DATE: $DATE UUID: 9e2d86a6-ca08-11dd-9c4a-001f5b3a17a5 [resistor_15_mini.fzp] resistance: 15 DATE: $DATE UUID: 9e38226e-ca08-11dd-9c4a-001f5b3a17a5 [resistor_22_mini.fzp] resistance: 22 DATE: $DATE UUID: 9e385b12-ca08-11dd-9c4a-001f5b3a17a5 [resistor_33_mini.fzp] resistance: 33 DATE: $DATE UUID: 9e3891c2-ca08-11dd-9c4a-001f5b3a17a5 [resistor_47_mini.fzp] resistance: 47 DATE: $DATE UUID: 9e38cc0a-ca08-11dd-9c4a-001f5b3a17a5 [resistor_68_mini.fzp] resistance: 68 DATE: $DATE UUID: 9e3907b0-ca08-11dd-9c4a-001f5b3a17a5 [resistor_100_mini.fzp] resistance: 100 DATE: $DATE UUID: 9e393f46-ca08-11dd-9c4a-001f5b3a17a5 [resistor_150_mini.fzp] resistance: 150 DATE: $DATE UUID: 9e39792a-ca08-11dd-9c4a-001f5b3a17a5 [resistor_220_mini.fzp] resistance: 220 DATE: $DATE UUID: 9e39adf0-ca08-11dd-9c4a-001f5b3a17a5 [resistor_330_mini.fzp] resistance: 330 DATE: $DATE UUID: 9e39e220-ca08-11dd-9c4a-001f5b3a17a5 [resistor_470_mini.fzp] resistance: 470 DATE: $DATE UUID: 9e3a1b46-ca08-11dd-9c4a-001f5b3a17a5 [resistor_680_mini.fzp] resistance: 680 DATE: $DATE UUID: 9e3a520a-ca08-11dd-9c4a-001f5b3a17a5 [resistor_1k_mini.fzp] resistance: 1k DATE: $DATE UUID: 9e3a8702-ca08-11dd-9c4a-001f5b3a17a5 [resistor_1_5k_mini.fzp] resistance: 1.5k DATE: $DATE UUID: 9e3abb64-ca08-11dd-9c4a-001f5b3a17a5 [resistor_2_2k_mini.fzp] resistance: 2.2k DATE: $DATE UUID: 9e3aef76-ca08-11dd-9c4a-001f5b3a17a5 [resistor_3_3k_mini.fzp] resistance: 3.3k DATE: $DATE UUID: 9e3b2504-ca08-11dd-9c4a-001f5b3a17a5 [resistor_4_7k_mini.fzp] resistance: 4.7k DATE: $DATE UUID: 9e3b58e4-ca08-11dd-9c4a-001f5b3a17a5 [resistor_6_8k_mini.fzp] resistance: 6.8k DATE: $DATE UUID: 9e3b8ad0-ca08-11dd-9c4a-001f5b3a17a5 [resistor_10k_mini.fzp] resistance: 10k DATE: $DATE UUID: 9e3bbe56-ca08-11dd-9c4a-001f5b3a17a5 [resistor_15k_mini.fzp] resistance: 15k DATE: $DATE UUID: 9e3bfa92-ca08-11dd-9c4a-001f5b3a17a5 [resistor_22k_mini.fzp] resistance: 22k DATE: $DATE UUID: 9e3c2d5a-ca08-11dd-9c4a-001f5b3a17a5 [resistor_27k_mini.fzp] resistance: 27k DATE: $DATE UUID: 4a2459c0-9ae8-11de-8cac-0013ce8bc1d5 [resistor_33k_mini.fzp] resistance: 33k DATE: $DATE UUID: 9e3c61a8-ca08-11dd-9c4a-001f5b3a17a5 [resistor_47k_mini.fzp] resistance: 47k DATE: $DATE UUID: 9e3c95a6-ca08-11dd-9c4a-001f5b3a17a5 [resistor_68k_mini.fzp] resistance: 68k DATE: $DATE UUID: 9e3cc832-ca08-11dd-9c4a-001f5b3a17a5 [resistor_100k_mini.fzp] resistance: 100k DATE: $DATE UUID: 9e3cfb7c-ca08-11dd-9c4a-001f5b3a17a5 [resistor_150k_mini.fzp] resistance: 150k DATE: $DATE UUID: 9e3d2fb6-ca08-11dd-9c4a-001f5b3a17a5 [resistor_220k_mini.fzp] resistance: 220k DATE: $DATE UUID: 9e3d65b2-ca08-11dd-9c4a-001f5b3a17a5 [resistor_330k_mini.fzp] resistance: 330k DATE: $DATE UUID: 9e3d994c-ca08-11dd-9c4a-001f5b3a17a5 [resistor_470k_mini.fzp] resistance: 470k DATE: $DATE UUID: 9e3dccaa-ca08-11dd-9c4a-001f5b3a17a5 [resistor_680k_mini.fzp] resistance: 680k DATE: $DATE UUID: 9e3e00c6-ca08-11dd-9c4a-001f5b3a17a5 [resistor_1M_mini.fzp] resistance: 1M DATE: $DATE UUID: 9e3e33e8-ca08-11dd-9c4a-001f5b3a17a5 fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/dip_cfg.cfg000066400000000000000000000041571272363601300275310ustar00rootroot00000000000000 # using only one schematic config to generate the set of schematics since all dips with the same pin count use the same schematic # using only one fzp config because fritzing internally generates the fzps for the different width dips # only some dip packages are generated in breadboard view (300, 400, 600, 900) # dip pcb footprints 100 - 900 are generated [dip_schem_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.6 suffix: svg prepin: generic_ic_dip_ postpin: _schem [dip_300mil_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.3 suffix: fzp step: 2 prepin: generic_ic_dip_ postpin: _300mil [dip_600mil_bread_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.6 suffix: svg prepin: generic_ic_dip_ postpin: _600mil_bread [dip_600mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.6 suffix: svg prepin: dip_ postpin: _600mil_pcb [dip_400mil_bread_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.4 suffix: svg prepin: generic_ic_dip_ postpin: _400mil_bread [dip_400mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.4 suffix: svg prepin: dip_ postpin: _400mil_pcb [dip_300mil_bread_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.3 suffix: svg prepin: generic_ic_dip_ postpin: _300mil_bread [dip_300mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.3 suffix: svg prepin: dip_ postpin: _300mil_pcb [dip_900mil_bread_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.9 suffix: svg prepin: generic_ic_dip_ postpin: _900mil_bread [dip_900mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.9 suffix: svg prepin: dip_ postpin: _900mil_pcb [dip_500mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.5 suffix: svg prepin: dip_ postpin: _500mil_pcb [dip_700mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.7 suffix: svg prepin: dip_ postpin: _700mil_pcb [dip_800mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.8 suffix: svg prepin: dip_ postpin: _800mil_pcb [dip_100mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.1 suffix: svg prepin: dip_ postpin: _100mil_pcb [dip_200mil_pcb_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.2 suffix: svg prepin: dip_ postpin: _200mil_pcb fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/mystery_part_cfg.cfg000066400000000000000000000060251272363601300315130ustar00rootroot00000000000000 [mystery_part_fzp.cfg] firstpin: 1 lastpin: 64 pitch: 0.1 suffix: fzp step: 1 prepin: mystery_part_ postpin: _sip_100mil package: Single Row [mystery_part_100_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.1 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_100mil package: Double Row [mystery_part_200_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.2 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_200mil package: Double Row [mystery_part_300_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.3 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_300mil package: Double Row [mystery_part_400_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.4 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_400mil package: Double Row [mystery_part_500_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.5 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_500mil package: Double Row [mystery_part_600_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.6 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_600mil package: Double Row [mystery_part_700_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.7 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_700mil package: Double Row [mystery_part_800_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.8 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_800mil package: Double Row [mystery_part_900_fzp.cfg] firstpin: 4 lastpin: 64 pitch: 0.9 suffix: fzp step: 2 prepin: mystery_part_ postpin: _dip_900mil package: Double Row [mystery_part_bread_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: mystery_part_ postpin: _sip_100mil_bread package: [mystery_part_bread_100_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.1 suffix: svg prepin: mystery_part_ postpin: _dip_100mil_bread package: [mystery_part_bread_200_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.2 suffix: svg prepin: mystery_part_ postpin: _dip_200mil_bread package: [mystery_part_bread_300_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.3 suffix: svg prepin: mystery_part_ postpin: _dip_300mil_bread package: [mystery_part_bread_400_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.4 suffix: svg prepin: mystery_part_ postpin: _dip_400mil_bread package: [mystery_part_bread_500_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.5 suffix: svg prepin: mystery_part_ postpin: _dip_500mil_bread package: [mystery_part_bread_600_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.6 suffix: svg prepin: mystery_part_ postpin: _dip_600mil_bread package: [mystery_part_bread_700_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.7 suffix: svg prepin: mystery_part_ postpin: _dip_700mil_bread package: [mystery_part_bread_800_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.8 suffix: svg prepin: mystery_part_ postpin: _dip_800mil_bread package: [mystery_part_bread_900_svg.cfg] firstpin: 4 lastpin: 64 step: 2 pitch: 0.9 suffix: svg prepin: mystery_part_ postpin: _dip_900mil_bread package: [mystery_part_schem_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 view: bread suffix: svg prepin: mystery_part_ postpin: _schem package: fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/pin_header_cfg.cfg000066400000000000000000000030641272363601300310470ustar00rootroot00000000000000[generic_female_pin_header_100mil_fzp.cfg] firstpin: 1 lastpin: 64 pitch: 0.1 suffix: fzp step: 1 prepin: generic_female_pin_header_ postpin: _100mil width: 0 voffset: 0 [generic_female_pin_header_100mil_bread_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: generic_female_pin_header_ postpin: _100mil_bread width: 0 voffset: 0 [generic_female_pin_header_100mil_schem_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: generic_female_pin_header_ postpin: _100mil_schem width: 0 voffset: 0 [jumper_100mil_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: jumper_ postpin: _100mil_pcb width: 0 voffset: 0 [generic_rounded_female_pin_header_100mil_fzp.cfg] firstpin: 1 lastpin: 64 pitch: 0.1 suffix: fzp step: 1 prepin: generic_rounded_female_pin_header_ postpin: _100mil width: 0 voffset: 0 [generic_rounded_female_pin_header_100mil_bread_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: generic_rounded_female_pin_header_ postpin: _100mil_bread width: 0 voffset: 0 [generic_male_pin_header_100mil_fzp.cfg] firstpin: 1 lastpin: 64 pitch: 0.1 suffix: fzp step: 1 prepin: generic_male_pin_header_ postpin: _100mil width: 0 voffset: 0 [generic_male_pin_header_100mil_bread_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: generic_male_pin_header_ postpin: _100mil_bread width: 0 voffset: 0 [generic_male_pin_header_100mil_schem_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.1 suffix: svg prepin: generic_male_pin_header_ postpin: _100mil_schem width: 0 voffset: 0 fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/screw_terminal_cfg.cfg000066400000000000000000000030541272363601300317660ustar00rootroot00000000000000 [screw_terminal_100mil_fzp.cfg] firstpin: 2 lastpin: 12 pitch: 0.1 suffix: fzp step: 1 prepin: screw_terminal_ postpin: _100mil width: 0 voffset: 0 [screw_terminal_200mil_fzp.cfg] firstpin: 2 lastpin: 12 pitch: 0.2 suffix: fzp step: 1 prepin: screw_terminal_ postpin: _200mil width: 0 voffset: 0 [screw_terminal_300mil_fzp.cfg] firstpin: 2 lastpin: 12 pitch: 0.3 suffix: fzp step: 1 prepin: screw_terminal_ postpin: _300mil width: 0 voffset: 0 [screw_terminal_3.5mm_fzp.cfg] firstpin: 2 lastpin: 12 pitch: 0.137795276 suffix: fzp step: 1 prepin: screw_terminal_ postpin: _3.5mm width: 0 voffset: 0 [screw_terminal_100mil_bread_svg.cfg] firstpin: 2 lastpin: 12 step: 1 pitch: 0.1 suffix: svg prepin: screw_terminal_ postpin: _100mil_bread width: 0 voffset: 0 [screw_terminal_100mil_schem_svg.cfg] firstpin: 2 lastpin: 12 step: 1 pitch: 0.1 suffix: svg prepin: screw_terminal_ postpin: _100mil_schem width: 0 voffset: 0 [screw_terminal_100mil_pcb_svg.cfg] firstpin: 2 lastpin: 12 step: 1 pitch: 0.1 suffix: svg prepin: screw_terminal_ postpin: _100mil_pcb width: .256 voffset: .04 [screw_terminal_200mil_pcb_svg.cfg] firstpin: 2 lastpin: 12 step: 1 pitch: 0.2 suffix: svg prepin: screw_terminal_ postpin: _200mil_pcb width: .480 voffset: .08 [screw_terminal_300mil_pcb_svg.cfg] firstpin: 2 lastpin: 12 step: 1 pitch: 0.3 suffix: svg prepin: screw_terminal_ postpin: _300mil_pcb width: .480 voffset: .08 [screw_terminal_3.5mm_pcb_svg.cfg] firstpin: 2 lastpin: 12 step: 1 pitch: 0.137795276 suffix: svg prepin: screw_terminal_ postpin: _3.5mm_pcb width: .256 voffset: .04 fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/configs/sip_cfg.cfg000066400000000000000000000006051272363601300275420ustar00rootroot00000000000000 [sip_300mil_fzp.cfg] firstpin: 2 lastpin: 64 pitch: 0.3 suffix: fzp step: 1 prepin: generic_sip_ postpin: _300mil [sip_300mil_bread_svg.cfg] firstpin: 2 lastpin: 64 step: 1 pitch: 0.3 suffix: svg prepin: generic_sip_ postpin: _300mil_bread [sip_300mil_schem_svg.cfg] firstpin: 1 lastpin: 64 step: 1 pitch: 0.3 view: bread suffix: svg prepin: generic_sip_ postpin: _300mil_schem fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/000077500000000000000000000000001272363601300256025ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/bread/000077500000000000000000000000001272363601300266575ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/bread/placeholder.txt000066400000000000000000000000001272363601300316700ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/config/000077500000000000000000000000001272363601300270475ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/config/placeholder.txt000066400000000000000000000000001272363601300320600ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/fzp/000077500000000000000000000000001272363601300264015ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/fzp/placeholder.txt000066400000000000000000000000001272363601300314120ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/pcb/000077500000000000000000000000001272363601300263465ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/pcb/placeholder.txt000066400000000000000000000000001272363601300313570ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/schem/000077500000000000000000000000001272363601300267015ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/deleteme/schem/placeholder.txt000066400000000000000000000000001272363601300317120ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/dip.bat000066400000000000000000000040401272363601300252600ustar00rootroot00000000000000cd "C:\Users\jonathan\fritzing2\part-gen-scripts\cheetah scripts" C:\Python26\python.exe partomatic.py -c .\configs\dip_cfg.cfg -t .\templates\dip.cfg -o .\deleteme\config C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_300mil_fzp.cfg -t .\templates\dip.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_schem_svg.cfg -t .\templates\dip_schem.svg -o .\deleteme\schem C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_300mil_bread_svg.cfg -t .\templates\dip_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_300mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_400mil_bread_svg.cfg -t .\templates\dip_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_400mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_600mil_bread_svg.cfg -t .\templates\dip_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_600mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_900mil_bread_svg.cfg -t .\templates\dip_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_900mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_100mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_200mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_500mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_700mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb C:\Python26\python.exe partomatic.py -c .\deleteme\config\dip_800mil_pcb_svg.cfg -t .\templates\dip_pcb.svg -o .\deleteme\pcb fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/mystery_part.bat000066400000000000000000000061421272363601300272530ustar00rootroot00000000000000cd "C:\Users\jonathan\fritzing2\part-gen-scripts\cheetah scripts" C:\Python26\python.exe partomatic.py -c .\configs\mystery_part_cfg.cfg -t .\templates\mystery_part.cfg -o .\deleteme\config C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_svg.cfg -t .\templates\mystery_part_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_schem_svg.cfg -t .\templates\mystery_part_schem.svg -o .\deleteme\schem C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_100_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_200_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_300_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_400_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_500_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_600_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_700_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_800_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_900_fzp.cfg -t .\templates\mystery_part.fzp -o .\deleteme\fzp C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_100_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_200_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_300_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_400_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_500_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_600_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_700_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_800_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread C:\Python26\python.exe partomatic.py -c .\deleteme\config\mystery_part_bread_900_svg.cfg -t .\templates\mystery_part_dual_bread.svg -o .\deleteme\bread fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/output/000077500000000000000000000000001272363601300253565ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/output/breadboard/000077500000000000000000000000001272363601300274435ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/output/breadboard/placeholder.txt000066400000000000000000000000001272363601300324540ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/output/icon/000077500000000000000000000000001272363601300263065ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/output/icon/placeholder.txt000066400000000000000000000000001272363601300313170ustar00rootroot00000000000000fritzing-app-0.9.3b/tools/scripts/obsolete/cheetah scripts/partomatic.py000066400000000000000000000072711272363601300265420ustar00rootroot00000000000000# generic wrapper for cheetah templates # usage: # partomatic.py -c -o -t

    fritzing-app-0.9.3b/resources/templates/perfboard_connectorTemplate.txt000066400000000000000000000001741272363601300265670ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/perfboard_fzpTemplate.txt000066400000000000000000000025301272363601300253720ustar00rootroot00000000000000 4 JC basic single-sided perfboard 2011-06-02 Perfboard Prototyping Board Perfboard %1.%2 prototyping.perfboard.perfboard A perfboard for easily prototyping circuits %3 fritzing-app-0.9.3b/resources/templates/schematic_frame_template.txt000066400000000000000000000050111272363601300260550ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/screw_terminal_bread_template.txt000066400000000000000000000070641272363601300271300ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/templates/screw_terminal_fzpTemplate.txt000066400000000000000000000030031272363601300264400ustar00rootroot00000000000000 4 Lionel Michel Screw terminal - %1 pins 2009-10-23 fritzing core Screw Terminal %1 .percent.2 .percent.3 THT Screw terminal with %1 pins %2 fritzing-app-0.9.3b/resources/templates/stripboards.xml000066400000000000000000000073521272363601300233770ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/vias.xml000066400000000000000000000032761272363601300200100ustar00rootroot00000000000000 fritzing-app-0.9.3b/resources/zoomfactors.txt000066400000000000000000000002641272363601300214250ustar00rootroot000000000000006.25 8.3 12.5 16.6 25 33.3 50 66.6 100 150 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2700 2800 2900 3000 fritzing-app-0.9.3b/sketches/000077500000000000000000000000001272363601300161135ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/000077500000000000000000000000001272363601300170435ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/555TouchSwitch.fzz000066400000000000000000000441411272363601300223050ustar00rootroot00000000000000PK8Dk+G}555TouchSwitch.fz}F1wd۶v̒`(zL*;nZ?2&YY>Ծ>F@d@3%WxƝl>N 'hrů|5wӫwaz6Z|dW$mBhDB^ˋh$ {e|;ϮrO]h2xw~;.:bR.WflW:QԂz"ۙ+kDژ4o"J+J5~KP\Ӈ&W۾]_:COzظzLgoKL?_z =Z9oUqpV ˭ն[ wcCIXtLH/:0xB%˯PucvOKJ{]?zձҠ=N{lJX}5e[6 _z͆黙El1G}o/ޢn4p6 Dڇ@>>04M@`Dgfo{e/v}a1;w/g]<TP `o vd}Q(OlpIfYN8n.pV<1là/mR.Nnd8Y|,sڰjBf[Wo"5WRej)QAUzz6R|X]{-vEkX"ԛ'_Hn (Tc&'KH춨T'Q"!J8T4"Fi# 2GWXU2]#}b[ пmǂ]ernb3OU+(2'-sʾl+\eZz8P͹ɜTЂ3^ߵZUsnY)Bvx+8Z'vxo7Cj+1J[;Ahdmqn)['v*lw;s9,`\U4t/Nn_EN曕^Uu߇l3u"tk|n_J+q%(m;d匟_}Pm>jy8[0yUn–8?<(=C#'g/v{Ѭ&'?'l}ۜt0x R甅g>)@\%~3P{(Ns*{4Є}~5~ FXr3x{'7&vMAbI#s;5=)D>)MwML ݦrdZK ݚcRVg-(Uµg>])@>JJ*( &*({R?)JAJ J5;;g'=kwOD-69i#na3 ]|R2?IJA̧)bD}~|@FrޓyQ|t`','v)K"Ju;&-=m)L?Kx掩KቻFs0弪Z4< LyOa S{Sx*nGe4nc"Sp>Lቻڽc2S3)[Bg>)H?iMA'6) 6){zS"? Nʼ)N;;'9f2`w{Md0,G7p eqUEo6Oy?[mg{{qr~}\ܽX9cUڂOmcqIiե&QhP5ij ʇi`tDb؋s~;̯i|XhlOf,aªxj]thabG۾6z;4npjnQh`hx„P[~ڽd6uy`?Ja7H)^uj;`ƫMj5Qi YuJt UI5j J;P4M وn[%o]H*z@{È`sW>!a:8O8LAѪ : #xTzhC!v(w> 鬚|_ټOz׳hrSq k6vϦ;оA=E?~7tF&߮+g0/7k|~%Wz/gRq)]^d˦m3 ̓tv׹2awex7;~ǝHb'w'o^sG! ɜ,|<n1`W!wԼ.>5leosAns{[M  )Km£eIjRQfP#2Ҹk5Iځ6׉sf!,x1?Х} +,5]dlٻap"P;cj\_χ wZFr;~XEz:Yu6˺{CG,=BaY cc7H/Km-hn!Q6Qz|qʐRCJ!RLBX,ZJ5Rb}GXlW&DSr*ˆ N9;shp\W -6צmjU> h0ȌeiXkIcyMh3 t;`2sC 2ƛ7 &ZYV7x-|`D=-] ;o:ޓȢϧ:}bԸĘ#!zǎ9M{}1[s cڑI^ #Q*$.0"lC.DЬ!ZW :7/9' ~G^h7Ot] 3k3wM~ ̓yb:JM+ǦϬ0$Ę3Debq&.,~V>1f,`Lirq HReiD0|e1F\Lr0C %qRZ!cͪIN\֪N.Atp4aO*iF'Z k[\cK6O]$u6a`fe vf9Wc Z1Nbd#= SQ>z۷Rr n\!r' 4ܯԳz:)\+xjs4e[}ڵTN8 #Xo  j}m>BRãJ>eT4 2fE6Bu&Ÿ.Έ$U޻$|qG.Leo 0L7$εou?6$OXRnxkѤ96hFTPqr6`Bʘ5\/D7OD#Z 6$F,i[&1PnbxJ2. 0وR*qK+!{]8)TsPSI< ع[N?1~nx9DnNddJ2DRdSφH") 4BIb0iJv NJIÜMUI#o[csQʘF7OC|~&u0m85GP_TG`qC.-BFadGχDj㠴 4Hb$i, (fE]HJVx]imCC7I ޷Eޔ뢟/6 l6G$G6|g{Q6ɚ@#I-&G!ae"F%| 4 KIRi(9& *m [ wbωP eI<(C.NzD!=n/xyZĐv (Q3G>^N n(bUΪOZ24CG)7}\!a>=%q!ͽy3ySZįxzd\:0$f #lHm1 <4BIb0i zUADP`Nz3yK?U"RfJ '\F.7Obbn_*`(_w!ؘb"IhD8R IJ%Icdԅ@F.^YBڭWF.*e/!lMS(-Txb(]GN7O, Kn'e8)΂Zd)C(~PhĐM>6^!$"x{atz;~q⍄Jz2ɭZr~_ÅH7r?MbbaʻH.:⯣O JF{+~>o#K׶/(H (ӏJ/ oOY霄l-=7O9;hή8ݧ7^4X&lqԔi)! c32M I!1?{}}m6G=z|$ڻ.PA{0LQb%TT: J" wؘ}1QdW##/d4-A"ˀA(8r:bUj0PwM%Nj뿼?}_w~?曆ϻ^_ƣ"!7ƣE=O/?~Uvq77_FErx_e/w˶; ?-oN({{x]. =Gy;Z s ٰ~ֿ6=Ec@d{ݷg_Ϧ_g|i"N|?.^B^/&]nߗnFbz.ӻҮzQ1?ݷ*O{Ҳk܎ g?v搀 FZ33ά/:?q8O߃ ̇񼳰G=tt~M:Z6`xN@ G>3ޝߎӧgm`o@ 2s ۋ^)Ozt-8-ԏw8HU=w8!(%a&D 3H]6f?Di-(ޜ&Cd[ ĵ\ڊ]Uysst0+zOvX`٦ & ;@bf-tO5ܨ6.XA^Ru$˅LJOܱg˹G5H>EzB̅,T2a7 b7FWQ-ij1Zݛ8k6ޖ6.[" VD AˉҔyĹ H&)L )U}j$qG$qqEM%F]t-4enIL[Hܭ1#wQ:4iDYQ%ٲ6\Mr! rDՈ{:M3 FrîB0$߁J [EV3juL.u 9)y8Jj, % U#UG<+\bopHBA@1Ii THɶ);|#G Eje>$Rsd .?JVcZ˚^ÔJ6pwi"K kEb"C;X#ֿ!Ig5ȳ͘MFh3u-vLݞL0YZYqf"+kzVļYUo'\ƃf8MCGz5R5s]CgDUbDOlwE^>O5i}r.IRg& p"jrdE&@~BRv T^blq%jeh/G&i>Iju`Gj5Q˜'G:ijGUwǪGdf5t8ֿH$c;.c5bE$asc-v`q8X0+ظ#+; SXFͩn|PE2ԯAy"ı= 8bbMQzP6;">"BeN hFie_gLW?h}OD;x,,| ) ٙ|bL3"2!(;-dys„&CgO2D3Gdg5@H3g@bƬ#,hmwe@i풶 x6_UيA7 IA*5qYYbMc]le.I$k5GL D50QiB eۦwM#[gC3opCgGܪ*F- *J"oRTHX"Ģǚy=F{sz ,m40KI)KApB"Z0"(IBOGV1$'VAfvpHVtp[*G S$IN%3M'$ɕ{sz0ő%=< %lcp1}x#&tE Fr-#ЛS-$4D/ibE#K$q[+Չ)MH;&6DWDW$fKJ@Td.-LmZk#Y^F WٛT &!r,%7{rz:Q㹮:");")+&.yL>6=p׿5l/}*ljAy3 x?Z^twu=zptsȯW ')ij \o?LJqa8/> 3 W[\wgǛ% 뿼?}ә?t~?|_z_uoЄzg^{Lg7f`޳'܉rcnd8[yjva<~7_U I%4͇liä7h/:Dh|!tXIL_曆׻ຉ;"$R*GCvߓIڧ?wߚ *Sy1Mְ{ߍ_?OB̭KGW ~:8ߎݷ7IvUNE/{;dB.Ͳh|0''n>wT]n@wGLH "kGa%Ё[GKҥe$"۩ݦn.Odc2Qt`]/uw5ŐS&'ԝM;wH/BS]F\M?l/)nv-NTAm] vq>RΕW nZ eJw2ߓJIACffح[ ^ZAqZ:}=4 xTI)D; ߶M8;wy Νc~V&P^[~+mMP'Krf Om:L"MeI>u2 sieI@7߆ P_ۣ+k]sԾ۸7~Y'V}_ءCA)P@ oÿc'TwD_(+\DnIZcj%*%}Yy[ȅyZy {.3ձGƙ>e' +-4uspB%um]H V޵8:zε+ucMMdB7`bUr'X< a8'. Gœ=) iT70\ jARVLdo x'L4_|r{7[x AaGS0EF<hn)S[0)PL0` XЂ-@_W/GXBa DC' It?Nc3;9 /n@С 9K9lDNm` 6Sep}mx2>@Y@ 0 )<{G00̻[K/ W92oK)MڷZO c1Ds PX0IL.`˚5 h@J1̋po5Y 4Nj4NhuqH Ai4cjl+vD3BspT_`DQP M@yR&4 ce+ qn;I#~ (3)u6@N 3s1A:H[:S: @AcsH!meẉG'U %g9 wQT6(h\='{zJb,Ř@M@@Qo^XeA=nQ,} Is@[j 栝L>P4\b0 0k% *5R+F(69S\A\" Dfn%h V 0tAȷSb/ ځq*Z*t 1 N҇B>iDAI1wA,?umH&5Ʀ@F 4Ck `y.sjCѭL' `=ʱ;] 04F LP|۹63.KrB0O{N}Kr*{mAj:XSv]Ib*Y C#afRWa\#9B7r3A;[(2!]BRa 6Xq0ۑ>'`m&w _Q9+ߞ{yc.P;b=7M5 `hvi C^L"֔U1G!'L^?a,Z?+Ex;(4U;(Zb*] H,@qJA(yBz|2h€O/l5R?&G@CFt3۶7M̩W]SH)ry1@án*&GkaG%t:c,"]/H"RK-%|P^(5]J&ca0*R y#avʠ' NC"A Nό*"ܶ| qbcxH<P/&0@ (}ߪԷ4\Wav(&͑e ' (1!ݙ;t\2%/A9^ѯ!>HzCT!Cшyi?A\ #Cdx`-uߛ[IqQ"Dyh 8.5&G(h铃 &"Cxg 0ȶ`K0"R5:v6T9tݗ@29Ǻ"оBɥwT  vL .=@|9"1Hk-ᇦRy ]hv-EO1܂bcЛаǹĉq: f{}}KI1¸KS,P1r|XJT^# !yn!D Hb FtD[ gcf/P<äϘB,;K+8"xX9#:jYjƕ /[̣SˌaNq?rS@ov?M2[A?K,Efq{*Pa,h͜rx&M,s7 @(fm(5 'B `>,K^VfR4&IQ,;`4CFߑ6&g`ˠ9"3ܶF& 1B^/8A2"8?~c. _B A8;aD}\D$F0UWڊ KT4e$tP8Ѭ1T 'jWGW 7_5v l;FOhT`Ё@;;&r c۱n[tV K !X덦 )ηMɁ"?k 0ߖI-㵻,WwiМ./+{V(hVem5=Ek%Q} nqoOp`ql2o 8G AjN|/4ޏA!%YH0Hs/A"H 誷NL2/|BaE`l ^<C܄M7AP2t%-{x񑮼A+@VpëRfqnDAn7&h lE<۾D6-$Q#:.;ueFʎҔ0'0xU*β%+i ͇rF.K~'R?y2qflҌXM2('Q'70߃fC[IaR߀z#,mwqq)KU劃W؂GX Rctiܶ2[Uӊ ]fdžZ}S}YO/YGUQ[Tl)vi/k- Um\ UP-,B T h䈁NLc.x8S^[M&@@sS+Jn$l)SFL> ɗ#COX~&If,s}K1^0őe6@s'Oefốb*\]b ( d^@S.k vFa7mw*Th@fI]J,7Ybn(vMo6!z'%ЗdOP/`b)v\8`y8 s|\ncя*x|A8v>^QДd>WkT7{2δgљY l5_wZї-7[w7鷰J4DLJ#Kĉ4SFEf. Dz1ޅM},#V! #0 `<x<;%|_x998 0y Q ~dx?.q 9$)(0~;҅,vW1݂^9p(u .^}f7]wW9u/! 4&SLfC~>roL2q_ <4OD.՞ak{Vwnէl%BW:?}=~v6Z|Mnzoonq:;oc3[~cjy?)t6M㿕V$c0Ǚ7]}yI2(pRъpm~g꽖t)\#nndjw,x\j~Q bn8n=vQGȯi)au7ttYMjף^<[{L'vGR1~-v,%$]-W)bFb2|aLm Q\tv׹6edQw/Sv-]Fqq^]UV}>xΪ_DVg&xڸ \u6ŋ6ijˈ݂#OZkVj^V]+^C4MY"`2U]ت> (ܬ$ |Vyo-t$zoNv)l'Lo Նu}G%w+fܪ>U9,ƝȞ-c[)cŵsZP&5H.ޅ%Ż< ;z# wu9G7Xypt_呷5;ROO}>;VM{B ᙑ5!njG>`"I @jf ?ہUAX&kw?"5=j .+ہ 1Vu4hBWzZGvIJHRf"Ikx F!ZlB҈hN2E¶$fT -E`TR@`IY$n"1V$rQ[)RCd%: ta.ZPô *(jfi5AdzΧVBU;q6HʂƎM?b6m飐20)+>sq!춪R}PbB[QNYux~Q1׬aDx:Rdn;$[+ CD"pUpCIL{sz8å5 ԤXoI<)FN?[8z[)YpY@ p"넒Y<H[4}8LHʽ9=DV#70}OԀG%փ[9:R+#i Xz@]ҮXB881T +$k]!)3+ I/Ix"vD~vz%5H,,0e 3SZ! M7"n"? NUs4+Qh (@icWϛGk"dBY ?cewޜ$q$7*eZw)u*'g5$G=B,)"|EuI$';)?duzp?d'OŨUBhm.zb°FW'ru 6\IW`QTbqpi]He/]; &`$H)]A\sf3w.dc"U HbܛӃI{jG{^"Wc,.}_Εk4rNc %k7!5SS/}M5,ƝW"S*:(4Ia<(4#";$ҸrojJx'q.:L:291+ό,!b fu M/6 úI$n'XY<<'4+~:Dvzu5"I,'f&mBR&t^6=^BZ<6_h5͏ak4kudzMУN{2| KU{~ĴZ7ǐU+Z0AoQl+8j}E86=#gU+iP$cḤ;WU. "YHҶ{,Z8heѸ%Nma{#K[f 2.77ȋai 4 hgLáib/F#ʽ9CR0E;k28 :j K"mf(vZUD˅mbb-_ Tk5N=AhAذKt" 1V#'@^ɟpE-0)(L"!0b-`bDo|0]Ja R+%6a)bNzk]Ϛf_ ]*IS!%ߚvaSޜ),W?/n Xo҄64$ƁeeR.̾TL?7Р iWW>L^q0\% CXd'S$NzS?jEJ|z:=A[˽9=xޒ 7̝$ڳzgJ4CXTW4CY&rx) : DvzrDtV4y|?/RyXaŘ>H"a<=DVM hrvnÝnPK8Dk+G}555TouchSwitch.fzPK? Hfritzing-app-0.9.3b/sketches/core/7Segment_direct.fzz000066400000000000000000001141351272363601300226260ustar00rootroot00000000000000PKm5D|*h7Segment_direct.fz}s8>} _m",/ V/ggwbw KUv]/L$Y0 >'3_^~7X]Eu;~<~ wO뻏/o^DfO滟_?߬߼W߼zy2_ra$WϷw.^^M?'o_/WY2Wxzns}p;ӷȻ~|zOo^G~[[[0uOwOO_]<>\OOY?lY#<ܮ?]׏_^?o|u0//f/=]߬7Ow?ܯqUz%nq3EW;z33a󞝆>_b<۩o^돟֟_6y}g\痫|yxzڼxۗھx|i}-o?'~{|)E m7շ\uT~~yEphͫoD1;3lG \|^ߘ0F+ q ZG?XaaL7g\ɢs`=VnqP &rmnSTm4{{3pg\J%RKjs)S:\bsK#v(X*$ɛ-v*''o1kyEwM<|~l//tlM¾<}1ƞ3yՊ%"Hr;_7e2Dr<=?+׋)¶ ڹyNx9"DΓcN!nIrSsѝ<;>"/r'x,ۺ(e;Nf0EO5\m%0 4cW?w~/m*LvT~G9$@f4Wx>vwwprH̟f2g+}/mLYy\$&_ʉ/$(.\nˮ]@Cp~:wP mPSqn^IM&"Ou|Dj[ZL ɔ:*<ɔ:UNd+ O{2b`,Pn9J\r:)ꁟꔉcI)s9RDIRG†'7?/ bTىۺTSQ?h j`Q59gG8ޣ?;k?l`(Q^{TFOL.+wzD\SHD=!$2r?S.Tҡ=R? =%c@!wPRz*#4s$sJEWM1}&7{0{8|$=:(Ȝa4SU]$FcsFSh&FIv)DQ48朼c|lL:&luGn FWO/O7+t|FMv6C<m$]b;?_|M J! Ey$TM6*Ӑbh箯mGݵ{2rջ̰0xx{1=;3ֿݭ nnn%XIá7_+Q^,LפWZelsV#8#?rnKFiحF\˴1ޗƱ,BjH \{V$Wz*3d8 TPN:6PivG33BIӸG [ HAƎx; xB9e12&O^f<4gqk&F:t|SdM%HM.d`%ahP@E ߬hC\\q%,^T$Oa;׆j2 u#lpɚdIР=!Jv%F;ӴH2 L&B g{h<ѴMNυ] >fg'C t>eϰQd:)i'tgCiW~?wtgog^H֗.:K7_Γ9S|isYF|JڦyK̈́n9 lӊ? G[ҽ*MydI m[3L,ǽ ]T&AN^5ӄh(%;Hd12rMGyk7A=96d:42#L@HHf6m3$HfCb63sӻDwÇ4''\7[:̭Jke"6MHVtDӣi(ħ>eMX]R#y(FTىrV)"Ҹ8M>%0Ͱcv9?x:9s0""T2(g(2ēFvigg!DՈg-&#Ùf♪Dj T, )OҼpEѴmKy,E!T̟=YJGU\YrOh{,QH<ۛvI$-C9S?9?xWEy0Q$O`E#zZ ۼ K<+I!]g)ֱL,>E-JK5ffZ= dZOՉLX Xnl1!*&fZ'&m55#r5Lӳ?ԎbYbW$uc֛EU}-EJ+TcZLƅ2Jǎ(X<ܘr6>&1.OjKJl(8Zj4w*rchNK4fZ= YU"r5N !ۏx!2"Uc->j1ȱ3'r#LgJo0NE{ĪC+t@:76Hk1BT87YGfOHHEj:< M]D)P1V E2VHp%Y5kڱDԈkԑ`#B2?LUu:2,@vz7﬏FDo E86ndbnFǿ@b_ .;WN՗탙cQ8=bR%Ip, #**+\VmƑaN d0IgF̰}vHGWBAp cxU\ΚdƄjZKh\*,ߴ~z#r5̫dwxɣ?aW}E7VlKq \"kl: KވR8;%GfOHHˮEj:. M]n @"5GUn Ԭ QvԟYmz oTDHT]M[Fb$LHc}Pbk%YRAPZ׮]>idsN؈eCQ9x މ"XfNE/X8KBB]W vHUL.ȴ\Mz@^D "ffZ@Et`(\ch2K)X] Z"r5C#!ۏ鸖IrDJK.ot0r|D gtېn,)HO$KaYP c bLcCS%Qp tVNQ'QJv1B{ֹg;%;\eL_ADN{?*+榁:և|qlDrFfxׄdR]$hH}ql JMHVdFjS HV]MgUn؞D pf,oz_Et`(ZchP"@17;޵@F_#/2:WG`BtIɕ"BlJ7̆jkErRZ>ō/ENkghUQȬ\M#8j%S+IDjO.v`"_ `5W^>,>%W"CWx;fgZD"Cd$s,[dYYQ~+"1!z'4٨滢Sy}+[qW0~?]@Np-_ 榁Hׇ(1ܽQ%Wv94scTWT`j:'JF 6 NHZ]M&PdlσϜlMR M67$  "H1wV[$xSd1#$c@7Zbbip-mKwIyiY'7N9\#Y%C_*4CϨ kȯ\M#8ZTc:%'Li$7-WDks} #Z -"3&8";WsӁ(\chlv1pE}|(ddl|ؘjRlRSIrecLuY7Fj:xc+vWY#%jGv^^>ye琲{ߤjLiزNkH;F"ZŨz||rFϏB2_-IW߈b=>NuG&68m@Cm;x] _]M SaE4XƷ)d#~GntVTq -btQcn@_.LuG9g/s?wkB+T#SƦ[~R*لC-iesӁB Z\dxsD"pcE g)r_|/ȕ398-WH.Nnlܝ&rL %•IyK/aRrme*ջ;2榑qQxiWH\M#8]T`c:'>.'g=]Gj:{y=R,hҭ?<,,Y:8sׁ+lfEz'Et Qūo+]{_o Ye=*]Mʱ?ͥP؉.)[୪o2dwZGjL6 l߶Pu8ĝ "x_ y൥kKar%`K)xlKSH; "ٓIٷr "9i7TxM `LG4( [a1P]gI[%"q{#r5:cX~C#W؛~/b{eW%͒: ࢰp84[}yg%iC(JcA!W} YQBɖ(ldloE:&cZ0&eZ9 t:6<>$[ Gj2/5St}J\wT}-h NJ' dHTy̫i'hc1±'"sgxvb+E5B8*1!8s Kl/)*I[Kѭۃ#r5ۚ+\(o'+ܠb1Pm7}XUۥUMmT!ڧ||LzzȽ Ej:8E12_'Ԙ|iA´J18Wͳs"JpI376#:p;Hd+c5Ks&U8Tj}jǸRH$E"j:Xdlh"D)ol*5yij*b$VDo `g#Hwp$d}2i@h2U ,"wDV4ϒQHzxn9(h,Tc:c*݌'w"<;td} #|DN.oc鸗ߝ{$OE?.3v_CyfPL)h?Q rzrNQŎ b#'>6[ŹH\MبBFPKy i)9A{y)^ilb]w.Gj2"c9Ӌs2R#N=h1TQ-(o&@xwPK,R]]U"r5턋xw`L-RhMJH]S패BNaÑr1GC2'%:|Ju|xāKs繨BG8DPcf@_-2-WȈEHDc:eg҄L`$ T͊{j$H\M,սң?􎦩76JҎ@+s͹4;ܽEpnc c!JFbSH7Ti:=`) VP_a+CpT }4hU_F#=Xd<QXĮY$xT} nLGNJrK 8-WiBGGPB#s۳I]^»3Zh58ћ7١TVRS" 57G!/IhSH7ׯBMH]ԕӨm"UOq UAu#su5 &yU˜#ROMzd {D4V}ȋt& u"s5 &uUxɠdd pREB)T{ 漏ltph)$c{툨l8"o{WMd]MbG8ɤ&i LQ~-"1!ٚ:Ec(,f[ɷXtzqTV}E~4vN̉ݎΚ@J8@ܼBܡI:LF'goT)ۚ.*͝;!-4)ԕ"P!܋A(+$k"u`vX#m#au5^!H3lLchȴQc5ػ 2QX x\I\MWČe nLGژXiAiAHGDC,z35TC$wMG'woD)$JsȞZwHM O]En_ZJMYD%` \A41xW$DӑzaN`Ӓcaq٪I3{`\\MT0vu%RlbX͂LGj:8-H4=tD6M˰P;MȵŊp 7#sӬ2oiΛE"ӑlbEF*M4C#r5muFJ>+,C5kyg"abwV?")HTk56D+jG(ҢtdՊ[N-]OgQV>HH0-[һpb=*+u#r5͚zal˓9oefu$h VLf94{2(Tch&K%&X| $Xe"r5" !ۏ`0-H0L1ՈU X&VYGfL+ol*`gOOHzEt ܽpnܼ(DB_o-EWtqlh(8tsMv:MQQվHtY QZn<{#Wu5 >s厒; U_Աw@M)q-$4ϕdH\$rPjkodY3& 3mqzaγHlkf&팧;4: NyMph"S8է(ͮ>9ZdScS' P!\U" 5!a-&&$*Wg]ƓѭXGx$fVԨ"<$Mбp\9VŲȱ\MhEHƎu0҉e02Maq]id߳X+jX-I\:KѯYߠ+fޒyAv=@'TK`OpvQ7E,VcՊ' 7ǻ0ˢ1-5B~FBpР cQ0jdY3Y)M!5S:9J6"F}VTw~iQDϯ,[ڊtp(dd#ؘaMM+g`]="Лb2Ej:b|)3 kƋ)Vp~ivE&17"s q-cWۊt0"!E7&XiH{'-5R,W$PHXYlZ>2ܔTVoȳY4"C,*8AgHlEj:fфOt4+,AfMe\Tnoj$ZÉL1Fl#1¥DR]1(U0+"txZGt(vƺzyȻlEj:wD] Ҏ'$]ĤKR ]\gIjŘNqtZUiI(T^{4OkI^V TJoG^eY޴Xsa9A 7s>԰*ǜM׃]t1J1!z QETl.vƲyߒi[~a?=9CLi̓Y#H4mCPYcn49}>E1<$𘛓 AW]tմ5(dl#^ODR H`}0E*;aݑ9ϋ$ZTҰ âaGR->9KhG8&aD$߁[`3b4#$cˆDŽ,i+(,E8Uw)qx#s5JnOZV}E.6+nm"aّKXf*Hc Tdq&sӱI"MAd 'd4ek\2"j y^e7FZj:,uI,)Բs׫,~+Q]ZsӡD&c^K O/@ypF>B2v @(_4'I 20K^j"cN%U@f}Fó^;O;fe'zh))3 [X<3cD#C22*1;#'h(dl^FDe 'e/Tڍ;0|ϟFYpVF3X`e3B5 km|*YhYܯ/~3ŢӧKO[;3Q7kERs$-,2I4;џv!YE? 㵓Fg>_nl@qBdIz2ne_f!L.vuFg\&}IfŌnYwrІ$Ђ۸%B)f'\C $\UO#r5];^DcB5y<xx.ōǴ5qG$Y}C0eMp.Gh9qzT+28UܫȽeBp ]FK 3ff_Ǻjؑɑk9.#!-BMz`Ʃ;>r+d@5l$JX!H\M0ǝld<@񤥆CV򼷋 CG1t !s2ffD#$c{'`t,ChsexJ$i)IPR&3琅d#*Mm*)N@ H W_QEp I 驘ׁzi`C)UG# )9;TdF9jȆ|O6, rƨ9΋"EN\*9kaQ1[)eT ŞlXJE z]Q鼖|1.܊t7B 5iGEҰ.K@ccrSH9p fX}\Twi]ev`~"Ъba3m-&bnV_k"jn:(`cho F,f`5,c)p=`aD0dB&fa$)Ce#W}}f_sEI^l~ǼzF=6"jn:zEp y\B(ff] 5L4-gl(ddl?H"1 yfC.?hV3>9؇wL&ɑ}]y|_[?ퟞ^~jϷ/o_nooW6W/߿C[7}SY!gvᬱ`"<(IQܞᒞpw9]GMԯ#!d[{U8.~ C&u[, 'I>F{EPK-)2x%dL╴8WaoU2SB9U_;ER2E3%GP"=]>h#2CF4,( L}&c3iGhs6Sչx%D:$%PP!{%MtJW$+CIۨ3J "iLIΖV|BSdBvJ|Ө:dW]#bsqJ;?ӝSL4" eSYF!$ "0\|"O?s*yL#:5ҳ2AbC${S"RTv(Y)Ei v&$#f!$uR4JBI<뤨:d?#hD;HlK$C^"=vy-!bW#2YsDXdȐY@GAmi|DH_h9(аp"Bc ?wS:sh;i\bU$r\y'=D߽O' ! >r"f>FH<)G!/~Be4ܧ֋߸OoH9B2Ls"3;d bqڧσȼBB~ D*mHx#_D;BxHRHy!4\l~TY&LRk3ot뗻y{sTTϊHcm]?l0Bjrݠ{Bt7*K!,|VT(r wTJBe:M嘡R.D`&KOB$|,'!Y޶N( Q "s5DhBi 2fA!IȘe5T"Vu.> ھN{9/D>,r! b!X\|"u&> ƅuv">Bܡm3d"Z(跅`}?̶‰"LBt܂Z[h*;dį :=,G!Y[0:dzT WF"L}BdB!c_}KMB˦R&"@JXY9>[,D:)BCH3FO,D,b/N\8 l y},!b#x.:D<i0r:D }.xM#{&:/D"Pg BE@兆Etȹig9B!_J#rԹ29d h115 x.D`g @39i">) @${.Dһg @d3 #!#~t">oF\T/萗Gw2h3asQ RP ǰ";d!bگVυΜ~. Djs/: h XL\*3'rsr(^̄$ ?Owwjޫ}7{2p_?Zw:{\\|`1 z~}r/WotWo~yr~{znNǽ~Tm+|kٞ9?ncLXD"is󿿙McY3A{}CǛwcLJYV$=ʌN'뫲v޴k3݄ֆL=o|꿿zZ??|yY?_=^?<4m,ǝW; mW0]3c_8@/ͫU[zs#g #񗗧wƢǹõbks??ݛQa o^w߭[;%htȎ[9mY4?px̣뫭!ZZQ?5=*Q\z(Jnd{Ng.uq_9l 6iýYr D±NA杔T<TlAÊ֌0l ȑA H{VARfo R&RZ@9xОC!a;\c91}\٨9RLFhӵe p]t}vY$<^q 1)4\" (fPr$cS ra &BH0D&d6O7),h0L{1 [(%1g)u#+7=#]t~n]l$$JMG[${Cىf/8"t6].tapcJOK0%A0DMac0 &6aػK.),om)*C E@,Åٙe@zx#LDp.LH0LN`Jvk" ̚s[v+YCpR^l K0u ]qllEl=TzDt0y`A.`"cpdJM> i U{/|SDKQ3:qf0:}ӣ!ҋ }:.M {w4GVZ8`T~1ya$k "Ρ(E"t6]0apdBif&) ޙ^ރJ I@E q4l]6-T<9^bdM^-w׉t~nq{N#~,kԳw\ Ƒ .=ˆV邁${ %SωܔTYm.HT 3 Bz=jnکXCt"f\$MAX;\옝l5L,RLgG @)3(b?ٿd^mfhԫu f,渫@yWs˼aUMC)H2#,d "́(馳邁${ %͌fNntjB7WvDt^Pg1g`bpu!)mU?+|9p:.Av1; H5{dDl` ȔT3jjTS:SWT4{*9 + {pOÚiꦽV{l7n!ˣTs \tF)f>1,HpM96jr[뱪gӭ5/Jzjk]c?*|10*ԇ Us#"?9$W#Z7].l`pAcnby7S퀡ں{Hh?eQ7].r`pqcvF8R㏔t))e614(e[餔*SJ)e(`K0쭂9a7{/P e{G6fP:.9°w1%,&fl'\,#lΗa; &h+쏅S7]0r`pqcvmt)9S*Rá/KyQR&g&/+4f̧K0] ssK^$v7UFnci2\ܘ[5L-:KgcG=$zbr A'쿕h3+3P"%J1\Dq1ipLʢ"xKcb 57A"+Cل)c28e1;<6P*#pǽB.tLI(Ӊ A(3֓P?mX+b|2Y!R;S&tbت34*ÅYq7hDNl`ǔ2S48eʢ aD U^KN)XY"$wH,=b %57rݽ_FX 9f}C LEl`DŽ$ Ib4Hye{g|$DK wb <ܓ9?(ms5~b#v,_>Ёt$Φ 00|LG0s/cjeIo7Lr/WotWo~yr~{zM7'燷pxӃY^~|i{~up`It78acT%QY~|?4d|jӑcoo1;Lnd٘%JLȻ凿l>x|gca\u-/W?-R3;]hu!c{ 7^0WvB<c]~}v3wջ~|zy?Yy4OkCe k|^oޒ4Ŷ7ށ|~ca_yeRKlepJ,I¼.kەL*IURIo̽cre~'5.\@ܼ,3oKndQTُ2ri?Ρ"Ddm.ˏeoW' 4fW2;+`B^ں y#Czzgv0@|] e~oB2о~Z\ni"SqoLť1g3'Ls^ҶHR hĵ kӅ_7`"D||#-_ߪH33nEf7wƝBo%̗o=%.u2YߌAp]̨.63L̴> aLTn&[3Yy/w>7kت| kÙqscLe;C ޏH%ƞ0)@05{cGlj?/kOŎՎɍ=f150LJrg$䛿3h4yQ3y>޹:rc:f_t6ب2rɋD }@gv| ǂ/.PLGG wq0GzҢ} P&y+`UPAm05m20i![ý_c9 y(lomURNTh|yk^JD"Q55{i\  z]y}񮭪r im UՑ^"ƛ/2;CCl^kx;)VBtue -o)o>\` /s. 2~k¢+Z$31cѝe @6.*k,U8E9em l S8Na\PUmEUr_sk\V kVX6q\m_0k5X$`\% y¹^=2kFQ^9L) x5Xۇ1Iܱg#g~pZViXw Qy R2uj uo_'wXG;BŊs[>ðvyOq@X6@ÇcUxC` ! t~5F-t޺HEpZLaQi ~q P 0_0@ٕHEx _߂pp#kx7fML1g#7\ݵ~FkW4vڕ~ǷɍXv/uv2YbpMEKl[B׆$rJZb$L28), lbI]dpD8|-/(fv )[хy {qQ6AyAhjAݣQ5Fm=Xa)kKAY kX Tm)^.0@SnBCNM H.: qsf:4*Å%m6.m `!6gv*Rx Wf',`a!\G[P]&pk[5+.kOY35˾֩yR͑n;`@dJnӧ ^loX9.\"f09FC`}3` omS9.N#?&XXp9l+@g@3l3!|!{'LBl t1s&S4WN@ fc (G0H[vÈ @i <9 @! xƆd^֙6N#J3V_iZ;&DjB ڌgs p 4ٚq)( U,NId~W`,r p<++L0W 1@x R``JA@HJM L34%$Seۼqϒd( N L3s;ׅb_f6e] NLBf]GxVq>aFGzH Xx5w5_Uw'[BLU97x?aIG9Ô.\f<=u"NSxҡ p3X3'lRF`,E!U[o<|?oZ!z^B,yn憟Ad@t%eSpdxRYt۽Bb_(R D b1f?`rcF=N$|׈gho)st93 Tvo7eG #0B$ `x|aKWY*ݔn3O,n`tK OR*O0³$ +33`V x6e d>aG;>Cg>¬ e` IB8@yHMa zV "73207$ B&пx-'ךaܱl$Й>F9S(ћOC԰bee5+xX*/]mja!d`\Y3.&܂_ڠ,aT),1C$p8BY#:|XbbMaqtID0.p/ *"xxQʳ$[A[b!VpXЊ^L 2_}.2KY\A%IHc{aB0OXVs|=$ `ymSN -pcS`#.C o`幬 ~n`J8# x޶)plx4FVkԀN&əׂyy t>&93:v]fTc$+Yϼ5%9"a{c$, ,6@a`ĢK3{E;(6!3u) @Ê(`x9 5SD_Q,s;n{]Y" ׻aͥ]!pӔPC.a߼`DZ"W!5 #Đ@/տY4~4^wd@^9.v' K|;(n{TX?o ꎯ*I:KjDkOP YNͳ1 eY1却cʗF|P*GS] g,5Q^3(LmM]B~?CcPDst,4Fek)av+ $8Q=dL?\28 iy8E`GHn_>Sn&1q_bϛꅷ< xw(9p4EW<[ʊַo]U-Or  pL#Do.F ҝ)J$/C;jGE^LE; `@"(4ߎ_ .P O~=VZc7b;ߖR2Ǩ@,DUh 8.׺B( h, LD1D4pRFcd_`'cAc|X,Ȟ7mr@?zYv`|-%֝W0,U:xÀDȬ,Yy}7o$FiM'ᇮ wA>k:0r=[pq)sdp[1L.1͋ĉTXVgׇuDR0n Ttqok%>C`(9+<&lXI`ʕCzFGJJ2Ę~LoxL*k@h6ðMqɆ66` tN`my{?pZ$yD* #@Fg!)`irR`NTX0 `W>%o H,ru-QsAߓۨ_y9aN \pdpdgȍ@~R>@4Agmazn p A֋E.b}˜^1.K52X`D^Ks)otU!8…B2eR poR"03 ƉE`[Jy_=1AfVuoU7̿b rHT\nKu5H|r liПElnEfQ7[Snr&M,u?i;W 3ca6ՌY X>d{ٖ#X'p6OK{%t1ImEtqXwe|3۠9"3|mş2ӈƄVZ`F09BD?\.>zNzy[TynY%5pdA08@2Qq$mݕfz˩W[ڊ K$,/󾊃# ٵruQ۟GW _XI; _Ca ױlU[Ёu ?J_ֱa3Qc2jau7]?uBJW.]E2eir +Ôm]+jpr~p/W+44V^5=J<.kpK"x ,n@}]V}*.qţ33++8`ŹFpHmcǦdHc`$C`j:lL/7( _JE`(-2!]d'Ipyq^M_ (÷830 7J)#]U " >~ñ^-Uj>["ZUXÝk``u,{TKdӫڏTkT~]WsC>ݷHeª~pXΊ-+k[>ahRRrd2Q-c[p&XmUmRT[ cdÓ'0?VFt .HFonhu~Jܮ}tu\j*W^:5S bOզm&uf_g0t&T33Լ6׬~9Rˈ5?6#|hV( 4 [TleFm^fP'kԬŨjkLl*xͨ%Q`*kFQu1Љc̥βr2]iϭ_$ŝ F 6Ekr[߈Sc)O8$қ@WL2a\ QYXfJ{p(I9~YKFjȴ&V:O#<rUfDgqd<0lgDFw"X ؗeU{r}]5\RED:<&Wldu0!jd?n82:ϼ\~»|52lxڐûئHqJlҕ—v xfiX DjxJCYы,ݲUyeOmozˋXçLVED+Twuh\ee2!Od+% `^vuPmҒ,=iYqkϢ=VϢNC cY.J}Y*w\6exPt`noZs{1%z fbtR6Uk(WW{qRu fQx00;Ȱ0̃g;/c '/O^Xv?S͏ 1kjHm.%kUΨJt;q|,j`-<=]Vek[m;5H&bRY"n1 [Ki!`T.',ǥs<~Tv`Ei'h˘&ѹeCT+ɂ-3u['ILA^ 3 }:L;anUc$i[arvajQ|R[p;0f]1Dܙuͫqɽ,enkUY[Jn `>Gej _:zykÍϮZEy YNc2ʐ_B;"Цb8.o(qJy. :^yU}ڼ)ubvmƑ13T{yg.r~}A/%M8>}ǎ"=žw%ɠ]J?)5׬#62 MʔE8<'ºf6u7*z<Ɠoc<j2E(E^"xQ/E<E^"xvQ/ୢ^"xۥ (EVQo:n`hYX]|"xQ/E(E"xQ/E<E^5{^"xQ/E(Ǣ^"xQܯ(E^"xQ/Ej!"xQ/Ej"xQ/E(E6+G"xQ/E(E^2'Ec SJڭ LS!qcjR||mHϪbO܀/w/k7C>N]5)6e&oݾs)A,qrD2󇇧O87 MNBoH*V/i>]I#$%J o>^߼<<5Eo^U(oF30.k19ߴea{Zy^2%Tv(9aJbiokW/_^}0(^]|{{wo>yblzZ'6}~1o2xy|1G'mDѽ{~4_/6ȪZ-`'><]߄ ʬSPluQ#y⢌Zzӯl0\Y1wpH@Q һ.U_s`=PlȽӻ߼VuCߤՀ_oZ7Wk6/pIg@fk&]9DI4v)[L=5Fb7Ʈ_Icׯu$kIcubҬ~ fؕj]~ؕjDR-q]5ƎvITI4vMZ6vMZF&ma3iB4v!ZLZLWuƸgTM&aQ2ݓx7(Wif\Ϧi!|\ Jف㳹HKyR/oڴ\V$ڼ(2"v_<]?_6}?qS S߿7*QX.=<^ܽꔟp͋F[ơa8FPH[+iI[=NL^4e)ࣰ5-u@ȇ7lL!R9? xXТrG8F>:YW5EM!np v(WHSy:^<4ꩁjC }D LH G~b߁߆-+6L@"\{A (g|ǡ,M$x{Co$UҾԋ Zzy6 wo؂i`JlQϵȀƄ\K񉹖W=ϩ*TzPy"kk$`ki[7f|7k-ڗ H7D"71#&ښ2;mT0jvRps[a[RaZu`sNy̚g!rljW~ ^X2 V~+yHu5CMJ#LWbx @׾e)r̗|XîH7FS)4cD#$k))ش1le$jp(mB;Ⱥ3B$㼯^aHʼsPc`ǂ޻b#;m$ _eHID`c`ϰX'#g|ZJ#(R~JOYoE"ugk )kWb#a-~m`6IBl^nBcEnٖ >rGD6n5|Ϛ]Kѵ6܋V-)Lq&]lع[f'ޅo覝-38@S7 4"P jZi}8.Q헦oyÐ} ]k]څ2Z A%y1e)Y.w0t gCFeMFe5c2iηN)I{({:acvd;q>K|dh2].;2_r m'sH%6UU/X q/l_Th2PrrMuӽtdj@K o]7EU-[v0*v"eO_jma]tP.3'՞šzڦG9P^ 5)+iŔsnx)1{2PwCAH<놊RL+AljV,]n(d#Cm,!^KgSa[ZMek[rj|o3'b[]ZI|0|c*@OOHSr|R\&{VM hoꋚ^)jr8yCYa#B-QɑO l H QT_wyE7Y#l5"Wyzj!K,?Oƅj\ĭLJqU[Ci8?ظhd)YDhf"L;PPqfbE5cEi8 w,P\ -dDt,yH#1KLTQϙX!DXlwPzFLk 8Fn b"BCB#L&)\>`$<<_ BÇ#l@<η-9L\ 'e-;*Ƅ`Nv/k%&[[NjXg@O]+[08`x#.Yسg@Sk[0ЈZG56õB},V ,2tҖúXvҬ{ִH`vc6,"4 |2;Z$)T+*t*&,rA`D]vՂ ŸvOj8lW7ٯ2VHNPƪ@S&d :,XDhh dڹB䂵t7` i9OJ4,Xˡ+PX +`D*dnlJU!^*M1W}2j_zOݲJ~K PPO dլuLWfX%GsE-PrmUG7%M=X8{Gsb"CKZfa?lo(i)~Y۠1?['U-X5RzII-=(9r ="*<}렕K^(b"CË\ mwju:pSZ…ʋ|2*\08ZleyOaˢBËZ)F.li{L rx4Ih’a1I%l)$9kJ-Y1:X'Ύ5<`GV ,彻h[͊X) [ҢTkWs5v,V$aNGzm3w^dMC% 9e{d#d}2KK S%jU~ oLkdCCɡNgشЎAPji5_іu9,rA**6rʶZX60:bVo "VYa薔.Jc eW7EW)%c"-zFQɡټ …F>kErDRSjB-&󬦐eɓ @eGN 嫊PX{/]r }O@v.*v&ꓹkgaŸ;hw"gd-eWt,2-ɲ[_!)Wɋfm?Ecdd5cj1īPjy4l޵S៽k^0+9e{p ֩k#4F:b dٹP`5M큠 4B,to7ظB(GN `1բL5@ڮwqc0ues^ p)^K`*t] BI5CZTvel<DԼؓмpeW -x;AojYurc#pC` Y|}O?~9nIG{zAx#1Gsiz/,[rle7NǝnMCbR`"7:>\5$7ODVIY$ A.`)҄Mf #BIM6'*B*6ƭ-PurjP{lETr B6iX@5LΰiAl'Bw0aG6LsY!2J 40{ha$Lsw*/"/a^;yMCw0/L\30Ȉg^eȤۜiϴ%;lWn/` ^37Bт~TRjf\Ho4tĭ-ۗ۠\Cx\}y?aɚoQSGL*#H|ۥW%gG`@,26L{^=~6v谒8N_oYJY0٥WgX}REovIRC U.D˻͋ ݡE5)6h谦 V."4… |2] 8εB `ˢBwna#lo1-h3JVqMK3ˬۻA-UILJg'{5j=N.[DXfKI'['3$N,nŭڛOJ4]ˡڅmxrՑP.BdT__o9v.pW6ٵjx}VF5b#tu z93|2rL4ț+]^Ё7Y졫_ß5W{*r1'׵@>}QSɞ%x\ - VU䧺ݭz@^xo.fg""xo³XC7j/'V +*l)Æ\% 1*v eLh ņ\5 d0+YЁYCwj/gV 2%Nha~lVTh;~s4+\D4ZF6+qTn~@[U/=Ǜ  25[!nW=>{u奤1(ە)F'^ט+WŤ30bUZ8\y e" {l2+W_0c\9[Mz/[YlVJE\%Ym o_Ӭ@8fȗ7W{9XzsŊ aobbqO[jYu դf.dVh1>ɭj3-JUKć|oR3W{*r1#;fHB=>XQ`4s'*<"PE^ѢLXj7ا,悯S z Zkn^N@S}/߱MQ9^Ҧ@PѦ٦V{6Ef7|M)cZ?,ئP"@lji`)"4]K*bT'Z bSO @6*2tdstD(%7*`谂6[j)ٰ\9Dz k n,ج *Ηg\D46|e>f5 bo.frcV*Pq]˙&jf mih~s+ZD+:FNjdI[Ŋ'T$b咽d9[JU>aB٫o1W& HU>Pa//sbe ]sʾTyEb.TzdR+/TBw1@M\ 1 ĈXmdR芵6(p/aB+aHڨ@xѭ'jmV ^#^+X-^C}=z5 cozR+wRFٯ rW Չ'1WTh~s+`Kjp)UZŸHO;wb ,Բj .Kh~j#WO__cn\^Jp"]b+ƟxiXCWh_L<"\El!A[ :Vdh[~st+>[d1ZCj/V RՑG0lVDh;~s4+\D4*rJVf:2lVdh~s4+=Y /sY~s3+L@a6+"4K."U•p #SfEZVݲYd/YAڬnhYb6奤1&ȉ㲕LUP,Vˡa+bR1*+R"mt3~'L4HC4X&݀ޣE٬P6+pܬnf; VT(UyEDRV%;Wi ׾uDˢ4gP,ocYḻ^%Z_~ۧ?_fC]A,i('!plB#bp,ZіM(dcTh`d>f]5.AbZY@.iQ;).-ae8d+#CeAtPNvtD;| x|To •{3nDh*L(Δ1.{ R7Q6UuvNɢjmTX2T9eMwպՂ*ve\j ,n'=+i̶]Q (ג' 'zY3XGA=,,6["UR?0_J@_}[ࡸ*mebվ3QcV6<ݝX¬XT׈5"3q5~?>} __>}}~Ͽշ/_<}H1q-~?PKm5D7Segment_direct.inoVmoF_1::X\"UJ6.xro~;B!vpgFo痰&܂0y PdI3+Vi-`h28 h l6o㜉/,KzTzԭՖ{ 1UxUue]МJDr.niL+O:Zf 8w69pJ{)|ŵ4yl *\m0D\׋_R٫㈦|c7j(i.MS:8U㗿E(V9Ǯ!iR '9Th fд5&9iY3Nj) u`E܊^ }Nq] 7= |e,ue:q4ϦT1R旺\#ES|e׭ S^ ug,ٔo^nGj;Y;1ŹLXýѱX#_ޓ7Dܰ<!WK7\̰uOaiq1Eo'$|K-[z(_*x5KPKm5D|*h7Segment_direct.fzPKm5D7Segment_direct.inoPKƗfritzing-app-0.9.3b/sketches/core/Amplification.fzz000066400000000000000000000175201272363601300223620ustar00rootroot00000000000000PKm5D 8*Amplification.fz][6~_Ƃp'۳ۉd8K-Qj%Q#nH"%$^ĶD!|~afqWo )8}W@}^}qm==/~Lmnƽ_Mߺ wolFfsU_7[׆`5^{~V1fֵ!G+AGhQW~oԽVJ<4",}$DFFoCxȐ-um.}C1ֵ8 :!W( R6C Q԰{GD4+qEӰͬW Ēkpnywu7{;Z76Xrf37BռW)rh~\[`Wp @zW䷶@^(b\ <`|{}C|ѕی۱pp̛lLu\PRa 0֝]'崟[ctUd }OH_-0YWcُ_8hX)UHJulF쀪Kx(Rƽ%#B4C8 S(؈XilD ]1ll %xKcG̕a 'N[XmfSQ!EpyiȣbG4]hgdjݏ_Tɧ1OKL#ș}l?@B!Pq@,KiE `wx6eP\oҵ|*ZVbJ물Sÿ*åF wOROPӫQyc^v}l_(X lgITvQ?9}"@pF&Zh7M_7^z3-VP`|3(X* SIf{]yA;<6nun꼷oj)\lL~0,i QM[wͅL@@[k]+u~|uNL*4mzPj<]ڒDuGo#ڭꌵ!Lo. l݃eA{,h ^ˌyNq`qLb8R&GKbl%Hb +n ُGrj(SKX~ykoamt˧:,YAl;Қt/<6ݺG¦[x[tMq, cfD!/'CI@D@)Rp""pf(".4{`D0DQTD,~7 JØ8d~cJ=yL2_GP\C 'AI\DP z8ͯe(dv(Ludix7=ט 5!S>1m9-<# b|27 l n@~/L8>F%܍S:R#** 9.A0MXw4bD:nB.0 iI9!Z}k^KKXaVK=̐-1Z AzWa_ 0a:FjčI#Zrk%̈́cp6#E{&kT;N38Ψ. ls88o >('Qvr8%=ƩPO-݇8|jۨ}D( Wh ԑT>"+4h0ji_b85" Px]eX z,m< Hq1)p-/1) a3a*QGacj4T>" רg4 +gƪJDu)h;l`լ>(h)hK4K*$A!!yBcP(!˲UZ%XSBĄ`(dH(G,dDѓwFgtB F$o%QZLԚ"l+9E9tfye]*ZS=+z]9~T$fU|/,Q)Q6,F-kZn $pND}?a)>Ƀ пEOد@ ]5gh*%"i#:%!!eC, msQiJF7tQhZa RKkbY\RraU^r{iX8\ͩNfcm0rwq=LRd&Ņrye[@QK紋"﷕1ʳzRSZxU4]i TN(O VSɃ׊Ps FsDOS$ i8 fHw Ek*1,g'HLQ'3..[ג`Ft pu–#BE(f([-zvPK_??{Iom$iAҰTR$M `\h9TP[b s4C/* )ZNzvuo쑲;9b{p?"7vq/7~xq~/8`1 0 oߠx[1 Z<A3/@5*r9/`J*w*T =8Sn;b8vw^tmRy4k;{2|].Vsz/?C@38ӰY|N6:,/b' kz Y,Cទj]?O[USzphL ;<Iڋw[5ṵjAq?ÄlNhw#-ORMy_@a#XREmɫ{^Eu$ H>É@*%~C7Su0+k TeJZ B(~Ov4%Js-ew#og!Gޠ)ț,- /APf 0??2as`$p?Ϻ9# LϝQE;t&tSKeZzFUL;S3U,dyE ?S"ohW 0f!\PB! ] b=+'膦Md*":{D϶ҋ0V)qqU)XE{'^yE ]P) 񒇡u 'J\n% r#\hJ:?WYW7@>Hfk2|fm m0I 5%`htR͠0PUD ۪4ʮ0=7gGܴ»JlI)fTfIN\kOeVQÆ (2S~sr:ɭ pZwq谒)xa纄*:h'{ t))$ S PRp9 C"s>[X\H<쒶O]m͙»"Iē>U ,bМ =ʻ(Gw4]3V)X^SLgK> 07q xhGhD?Q>%T WmfHTeT -]Plb0i. +Դ)a+is6GOVaǕjir%Ga!4уaňXF{l͐Z')vJ~PeJAEByV, HNRCGV -33g_Ӯxoz#L AgtjF)0ry@H ;/>v:ӮܢݙK}e̫䶯dpl}ʋ(}ȂC3^z"2ƺ* س]|Upk4zk dO36XEc'^yE3W]Myk8i.sFJ{bbF  “0U\6X~܋cYLhu ڌVY^T蒶Sڈ$1c  oC0 9f١0223SeǖGR)pJVˬ8g*L]kLY*ZT=+Z4S۵UaVu[$ 8%S{B l "R{=+Zf 6.8f[ MgÇf3hUc(jtGӥI):;2XE'}yECK> r0`^-BDylA>h@DOS.!WObm:6UDY)b'dE+4;҄hUVyY^N !] i^;w^eBwt4޸ j_<8SfeW,EE9^{xލvt./f븫jN{o6{o~l旟G݇w}|?@F?;[7=<<|~3^9HꢑB`M'*u(Wsz/!`+8,9Tԍ*k6{zNY׿e#LNKK`TNOǣ+KP{XX^ov<<߸iXz+"~烁d,>o'^.0̜N?H֟P]~ ?qP=/ $w/H EP?_;Uw HoD +$/0/b|@P P\n}HP/0 NJF߂%5;'Dw bm+ꭣ?Ukݎ=rZ޹RP^@8)eE[Nn/\t*}_zf؅F6#)XA†Az^eBmttqd"<IٔD 5uxkD'ZߥC^Ľz2ilR%o{g~SBvwA6C~y~^0Paa]J1vn% a, ~c憎@x j$jI4"Bҋiɥ=MҢLF[\bDm]fCWbťF:5SѪ.ݪ9e.F*,;ST}Xj(B_DfӓlvA?_Tt`B'T\^?0 uח3Lb IQv%zuAܩ֊"5W-ht*4,HY}߿yV}m^֙|ާUC>a%:+l D~ĻD()U]W!DZ;no)U4@ↄ!W$#{Oը+YR^fV@ei̮dOEϴ 9;YE]4 񔞆,DWՐńlΆl@ L ĪItQ`! c{,{[%*,Vt c v!˃Eį5>;3'+_7[=zor|q%E֚ߠlk{JB  zyܝ)il~|՝r.!~;\d? <ɵy^pslÛЅǂTuMT&tlAJU L(*˲d'@ G\~8:(,8XuP`u7ffOH\ް(^QiQ4eqa|CyQ89\Q8Gja|C㥎Č%*,;#k]ԌzDn՟ė(e)Ȕ"Oy4'A<<_5;2RTz.uC]h5XQ~AѪE[i8~>Հ!ԧ t$LŐk`i+S tI^)9H}DOuL2 ִꆯJTuqЂFB,\DA)<۠LE'EB@\xK|RO V'bH׵m0M +GO /OUHaM͐\_B4)r^.q/8Κu=]bSspJw  #mdJBΪ6W8b 9]5iDKqq/颒3Q9ŌBw% LG5.s%Ue0PLL LR@-X3,Pn3',\~\'Z˪g48z vM>nXI\lnODK.I2cP}BV:hI=%mVgK.Oˌ&i~Fi7 [AbGB-;bOױ. [ĥ26>}0L#|[HB:fȿ. <;\9E+E\5\ U=( CG´@/2f&.q}$`hGM6"Sw^m1jrʞjtR(Jd9Sks=]I ٭7/iռ4H+%>Njl1JLAʃ FG^9?kfc:bFqk\źU<Beͅ.i䴀)vOb(-47#Zh0dUWiPKm5D 8*Amplification.fzPK>fritzing-app-0.9.3b/sketches/core/AnalogInputFSR.fzz000066400000000000000000000360711272363601300224010ustar00rootroot00000000000000PK8DAd*8PAnalogInputFSR.fz}rGwpbT!YK-=Y{~)@Hb h$oC+m9Y$nU`PUywwN^͠uLY+FՋ>_v.'ٟ$6> ߴo/^{?EM5ɋ֯uW/]1;_Gvܿ]8S1RY:_]^ɘ+NV3Wjҽ}WM2䒲^\_**8c=rtY!֠;^=knɋzmѰ;;ߏǯn{d1s+/BsKeO''=.ѵp|;+y'2mW]?< ְ{}>I ˳ydt;x5\&_&ix֚^>4g50>"~n^&gH ;RNW|D>O%xt=p;f~h t{wcL_vӡ3 ٠|\VB/u-r%Ëˋ3v㮯۲r_fʼt18Fsޝ{.qqTA`ZyQshSd70XPU2If?_XY7TИr֯/"eP%XIw8MnZ7&PC9_=sΪZwj09)]rUncI.K [Wu]wu K綬'ɽZ]޸?D {|+[hR<;>i4Fh .1ק]]_z]_;J Lg”|xH<, >-:pYU493UkҳG=!Pဲe͑ɴ1ٗ )bMA묈Q`bTID,RF O"REzp]EΚ`.68֡6>L6;UjFi,:KNw?9y0!qXܳ$n'dw')>ծCm6`k5F/HY8lj7Z;|~tN1jCo3M0{ܿ3}Y|Whg_w/|<<7&KYhѱzSxڞ6}SN.E#9[F&d%b`|__.ȘE FZH ɸi**C%Ɣ,P ̕/Ohh$%ZIT M(@uG4>l#ڤTsZ#yZv#z;:l&eVJr\M0'#VQ)kȨy f|~ZWoy;}u:WWN燿lsǣU^E׳ lGp v*EW^%Yo;AҚ}G;t}֠߭YM&ZI}tߵϪVtN!磋-9Ԥu]v}Ͽ~NU}~LqzgL_l8t/|n0K|4n$/&P&,:zD}Cw-/죻)˸;,565O[ɗx[?u&Vw\'Gfɰu;M.Z(d4_Ukҝ%ѥٻ^-[Wk'%"r뉃?^|^ta2 dؙ&éӎJA#.kgGg[נ;oO,TaXnL8~7g GR8rfW}2Ocg rsVWB۱m҂f-8䘊)<Ƒj[F#| &$9sLADZt+Ey]ՔͰ(=[dEY+2Q|xl 4d͌lfD L,;c+RS8fvHhY񜙭W?eH&63b#J#VjIbXC8 A+RZ86x>3,"Ta`_|2V`{l )sKˏ}da>bc!|lF tˊEAX)ZUB`S{o:<yy6!hUX!=3i{=4*[SjrZO64;<_eh]+/{JLN'bx1k-J"?ujL1 -0_9'k1zǂ̣NOZ+WNJR3]0 Ԧjf m(yy2:xF(elʧRd:BsR!))3/K j..RQf⎽X퓛ê;-3e*t0 i {>b,,v8$<93x 1:bGǐzX"=HfP~iL"͹Q\h~A)$D&*SNJ;Yk증ϋy ꁖ"SvfsGV*LȿK&ڲ U&qsjIZK䤼Km%^%O*H~cO?*VL瞧]ryDU\?NN⏞"}xp}z̧:_+}==D~SrjR9hJO ®ypRz嬓H&Ngǃ&:wdӏ5Rd jVq-Xep ̔SgUQF٬*bw#xXߧƪjtZ} I*(^UP'[E pT6ҰΧc@ѐCFC:84;8\9щ;e2?<"5 #G4#)Օ$BeLl2k!bOc5{xe8f8v@-"q lF1tjCaG5k⛞l7͑&'a"e16QCIE̔-nmol@W>!)jb<۔m@GZ邃-4lȻq( l^3|l`G&EXb J'JBL;cLz\ U1ʘZ6fJ;zprޣʹ6;"U3jl<TRTR?EfZӆOzxh!a۠HurdO>4Ku~<9l5$/Y;uْ G~Ua Dzq]s!]FW7ݫq55{߳I|_̮N*fV0#`u=˞W1'Ik!>^x~z' f͠u4?V2.*vvoo?_}_~hMZWoNwyu׏?]t~ogNg&W&u7;FPǽq̋ŷEQdLδ=k}1:OZo jQ;hC?|&/۫×iZFzy\_Z(+7Ib8}?oG!{ :2Lb)b? ?~I ߤƏKwW3CeJr"-#[ܤ&|}{ʕ6a TR?f׏Xv!+2hDZh0d_*V*P?`RiTTXDR\/hy (Y8u#`eLzϏv ɤ Ag)TU)2&w5m\o?E)-hyF(|y/Wی>%`Չ嵟H^o{ m|)C<46Rc+ עwTQV m6|`˜"]ͯZBC4дsݥ"-3&vʍ>id̓B՞-4ɺ^^&JHT` Op*Wa=TYMy9ڗoﴗh. OxzIb@ݵ7# +9ʫ[O"g B0@BrX =92rB?@z1`w Q}IE^Ɂshl@}Xa!5m`D00܀<,rDJΏȮ5ZaJ#=A Hʊw}v@1`G KCHdt(h(x} p x9܈WN)9s~s8sE׎3Vvĵ.\;]l?t4{ Lx &Q_%@ J+R?x1HeB('Ly}Ѧ،O 8 c[1ލA'LԂGYvɝ{7)>oX rpGS0EF<{4X td Dj SJL,V0` h0P((+) BtjtDsީb,wbq:څTh_2a#ط Kyج^!eN}<8`Ct420=@ kF% y2aXS o@T!0W@]a,:?#|G䕢alA4 Mj~h` %Ә B rLxp'}M6"JM^>rG5hjNvYSH)|H(}p]C?^N׆J9gLϋHDċ"RT.\B9\BvS+j܇Qhg E`T F2ǕAO(2E& NmFkNϯBancŘ.%,`ꅯ 0^6^ƚ t)Isd-C0JLwKA ^⃄kX; `@"8fy# l&"{$Hc7,n$5FExHp\jLP03$%LDD4pF#gX 0ȶ`a EX5M+V6r@?zv`f Ua; HWA12IS C]s#1ⅳ19WEY<]hv͢ AnArdЭ&]Vq>qbBS Egn#y\c~. a<^5 * \඘B@0=d/ ̗2cL? =@C0gI@hu;#~Q^ 8M6*]: {@`]WYl̈́`a #ijC bCگJ94`0 `DAxw*БY`/F}<h{ T nX3sgQbZhrpהAD4nX^[!zE 6Hav1^jbRхR9dsB@Qa#4lޗngxI1(&X wW;@tl 1A`PˢT=0t7̿o1N9$*)#dȵn&1vxD4pȝEjv0NhMrh&NQnx]hʼn@ F2"t0+惇K>]A4xW'Y"FS:hLQ,;`rD!fC|30Xl4Gd~k,~T hDaB A.-8 #we(C(C7rqa: [dkDw &3Pw'  pXd#""11,~ mݕ(Uo{PubVLX)C-7zpAn@=Q*g_^+j܇m;FhTTc@1XT0c8Qnpv7]?kt!%u2Pdw2@ ei6TrEBsl*| =(\8HFoAn(u꣈ѢRD2A + NlTCPp Si2;UӊUU|͂ õۇ5?ҚӟӊBEiP;8ڧ\aP \Be TPy&Tq#:1qLmdF7ށN&,2 5)SFL! '~D& If,u/Uri6@XXkX POh;5հ8[ ].*R]Pfy2Cxk<fMJcFa7ðFd p."S"`r$[u/7ԙ g;˧c蚘B`҈LV+?Q+v`9y-:4?\vQaµ#'釽c,8e\)XpNAX5G-Fp&! xJLEo7Z^ݐoJ k{՗ B9z8/&g"#S8HB_q.s`Fݣ`r0åK\oF$6U8 aE\6_.ES0XXt) R%.Y +nYrL:׻blĐcHBɂZ0m_;߇h0q*v.`0m8 É9!nbc%c7/7 wű@4>^Sb!q犂FEi:ӹ8K@gV@g . .<הּ/[o"oᔈi:1),i\ )\ė0b kC,SvPV @Py@Gp<X|jyN25_/\bˣseCTgȉ0`I|Sm\BNp~ >0CI'`&TmFXpPR?`~9몖"73 EC\# |=(w&$VCR2^ cr 8 Q?ۋ ~\`48p4nE0! 4&SJfB~!ra(S'C0zAs ]6 )-Am?e;m~yLƝO?>_wgfɧd0(sƽsXEw)v[ѤvH`3c˻zd0]JEWn8nch9s>G}cڀ1ߜjpOH {?LhŇKȡS2h|Ű'w?._l8_ΚDw~NƣD[R_R{̖kMhrӺ e Q/z>?Cr3?r4ww#gZ#Ļ,vKqnZg9Wg=GGx>5y+Ѧ(x+C#Hجb(VYG?A)wvJbgB蕇;~x^~zx`8#V:i!r%GIQe<<;Tx rhUT%H* ?>Hv8wUԮ_׻ץP~e53~ \ߡg!{M$Țέu^{eޔ4îa}jz`GrM0KV55.AB+FadU]և*,lV{Yǖ>ҽbl8tă:}mIw^4sҝ?wMrܿ[?cfQ?N\:ƚ͝]LYكPŌ px15o&eU~dT Wk&l?ţT<оt9԰>0@ġ)͑9mY~Py-(hY qhkϞ_υi L8g;71=wtLf#Hߪ!4,l!Nyy0{L™ 0}#`vFꏰQ )p M bc, ,e7,0e}cVCw%g/]@IoP= AqӦ h9,uF'V{@g0fY0kf֬a5%{ҎԥX"N*┩U:5r0 @~%<"8E 70Txm]} rp>^]ydMI"*$ 6m_HkE] Z%)qe=z4llѰSad*cbtNUGLCƪѴJ*IK. }dB6Ǭ B[7ܬU-lَ$^6mVDwi(Nm;WȻU :gR zT3%Xv@ٿeeܞ#yTH.t6qar-Չe269Qqih~Y*x xڜL=:6;tď-YٱwQDK.V}X:isPLXfכmI&M'Ml62D$iLUB4|m#ڶL.ֶI~dXp2>. %65/f>rҲ2 Boi2iK:h";' 롣I >N vɎ߈XZ 0)7p\UlR{gbOWDFwif̎4lksz Bf#l]PK8D tAnalogInputFSR.inoTMO@ﯘcL)Nz  тP;qVZZqZT;*.͛-.p۞\`|GQvˊ OQko].D;Ck=(>zNNa +l, G. *x -i&#P0_!& 6&΅_9XఒؤRZ L&|- *LBO64HAj 3؊#9H ;Fj9I/*IR5Fh9BBȽ Đ|` N_{VF.bVГLYUV!-XJB:hzC+`[G[bQ(VAGs!+sy(B^Gv잎ᬯx<(U0anZ3.mpޅTrER玎D_{ jWQ*ey 2)eF+֗|!o6"a84`2M1٬ՠCSf 7 2>bJK!?*doiy~~{?N?hp)zހj !- sqx ʉo1Zzvy2˺Rh1Q7Iσ_v/鄘AFb3w,A?y]<&wx'PK8DAd*8PAnalogInputFSR.fzPK8D t8AnalogInputFSR.inoPK;fritzing-app-0.9.3b/sketches/core/AnalogInputOutput.fzz000066400000000000000000000373421272363601300232510ustar00rootroot00000000000000PK8DS4X>{BAnalogInputOutput.fz}ےG?`mi3d"J6Tzdj6$Q*P@5*p Pd&*֥q܏{\qa84&d0M_^_"s?|};;WwX +y q0.SMˋ}s?L7-×/ם^}몙>兊J n/:7兌┺zIz}0~,4љo?̆K(}yv=O._Mˋ?\ltC/"Gf{E?]OMϾ̦ӏu-nhKw}f?} h1|?꿙fbz ~ s_ev!_':UЅT[^e4_? ?L.ik/:w}ߴp>Gż7Ά\=P=TbYUUθy8kr}p1%!Oݫ0fb.]}~yI"D>>146P$V!9eܿvݠDA b}Zh< TPqљgС}'K߄5E  gJ@QWbߗs׶;w7Ż[:`כnv(u_2+v28_WX^yM?^I]j 5bE6fS}7_cjE%]=޲MK M7iգ"y%F%|D)BYV$mӻ. ;NIvp]$+M@5ely:jRS/CM֡S0[D)oo']j! ~+I/m!0l!ԈXC5^rNla[H#D5oS()2뭄$z ݋MYܔUr2ON :FvAX+wa$ f*֔n-;T._vl@#DӲӥ{V٩Ba@YzLy ѲQkGٲS%0b0+blKN[#HKN9S^194KSElԎWWWfu0VbjNILԘ0<Jtmk~9oKRN%̛u4`f?GZ~f@yTm "Z زCNVkI馲g #@ҒR/f=t^U1+Rav+"2Z)x8dK{tZpB*ʘQ/R98--/7\Ɩ6\wLXJ!cjK'YSȐeuG|σ6FNFGqP$qc*Ti*+c9jbɖMNΐm>:xD9+tYݽ[d~Z{Ou޼m秿W/y?]|zWwW^⯡bqcuϽ{,nǂh/XW/2p4!vzq]NW%]r7o]3ufFs08}_ަ&_s~-\W?ǿuv3;eu>}L%?tvϮGh1{A>?LmjQbo.h4zㅞ~t0,nFSn${b1ץ$6T -+9ا\pbDbɕ5rLRm}37)4IΛrMS@llmԢK] )z8jM>6icC4|4wmA0H^}>{5/^cduPC)M`/>-o^8XՌFz!w1gc6d?Cϭ@f.r(~hX\o1bE^~6n:,vA 'lU4 b1#V{L)[X[ٳ>T[P;ƙEś=s)f(9@1W\>w6};=ƳìhJ:ްαb'uw7B`@!j"-8P[x,OjaaߦWs=3_Mg[J=qB 4r/,`N1Zrˇo9K~xT*/z̐괄͇޺:;cÊf梤 4 1Dž/]JgD%SAd%E풵JCMvfsHX B )O!{+ceÈv>BqGgG3:0 tsyF;YlnMmobVLBNcv64̊sq4 +sb E>a3>yY_$Mk[o/έ%:YKv{~\XS.K<06c7U8/hh"q nUμŢgٕzWΓ6vibC7p6Jʒ-g٦ߖ$YT$W =8yvhYh=&Ro3 lyi [ϰ ^SyH1Xte{]LBs {GKZsmͤc D ƅHFF]YaKVGP9lS:os`n9i[bڑ$ +jIsŘIZɧ3f13|ϝU[h9[ gaz?dq90kG5IoҶ &ϛ1i0kی#|ϝ-cIK ԍbaLK 4"l &?L&oo`mlw'ݏɒu#0/I)D{LY[ ϛ:2{%e)p8:X173TFςv_uLYƍX)#3œ$260v%[-`rZ\SHZ#f #" T %'K3)K&A Ed9Kpg͢!GKΘה)_@Lmu9/*<-<,jEw +Pzϋ|ϝQe6XR5rڄJiKcZ$ڰPکgKx4mƏvc5ǐD+?v~%4۫χy₩Eһ&%wйGp6DRnw7:^|LCWQ}yxw+}XOpdS*E9nE+ hNPJ(Ͻ#jx߽I(>Տe sRo֐K?t}U]5'l..ziSN%h8,Gy䓌r3:$c==ac,D{DL-J)$rN9st9 3[ekx#\h=(D<٢@4КEsO9V@~3# caf XXlc/ߤ+'tB4ymu 1瞜&8B_8Ҭ ,Yߌ&Mt=h7 N1"|ʶ V(ts9Yesjհ-[p@Byڤn%(i a乮U:$-m;>ؚ#sZc;i%"L4Ɵx8ÍNw|n=yC: |enfV9Lǖ p$iZk'gj aJIZVlѤM9qu1 (X5sEfZv=Lhֵ83xSݒT>ѐ@JwҏϹ3Ib3P7i)pPZ R8C+para~ew8s$/?# ,m֭>|sK݊9dLR iiڍŊ 3hHe+KڶJKN(ϛ*<DZVlс%nE@g 51E$}(Y7A)!K>>Vߎ4AAmMkzYa=]wеؘ0mxٛ=iIpNZܛÉ99 o(SV+1g~3vd |ώi c)_&t G--o/Li)X"+sLZv|i[5a W `%1_Rʮ"z(=VacjWfmZOh)2@[6b\~q ff)k:'Βp,Trte+2qoIܕǙorݴiF{YtO%fcxT7DFK{ S^gҐ7 NΐزGd2_uT*N7\&ol_+n.osMk0lgMq}Vx#N~n̓-,lQRR&[vXzyE|OxJJ.R7Y7[7'Y&g_ܵ:j.Zfqb>J-%x#IzA9gTOnw2}ީyQjgEB_ئPjatwR9$ɗ[(vu{s"~>?}ɔJ9R7L9J-S*'Uj*fI 9RO>J+TNN;RK?˳ )zx-6KެrFrɳw;ۻSRԀNaQ܊{i۲}T]?j!x.nMahVA͞Q_>RT蠉6w!A_2Y+WKǎItMR'OI&sǫ̏u=ݸw%- (D2tT*Jg6Tt>u;T FldM˷ޮo4jz+7{[g &y2Vꣃ{RZ"cRlJ: + RS%8=Ζ>o-A@oRh?+5)J bRҸ Y?¤I-?zlH9=SZڢJdh\CԊ Rm7a-}FS+uac%k[PDSMT4U$1`$$8-LZkU6|xj1dLˋ~;LQS|08FL!&&Lʱ"kCIpRxzI]-ekCYHK[UTֽ-|ۄ慙`jb}13*~]*^Ρ֖8Ȫ>4cjKm(f(ה=V'KK6 QZvB4B!l elǐ[styvw0[ gtaooPڽRlv_a4)%J>atDXiAbx]?EԬqVk)cZ__I<2bi&lByk)~ PJAsJ**To:8rh%^FN9` "Ԅlѱ vG&sKNRe4,BZ{3r˹'Uk^kj:t1v Fݎ\j&-Ԥxzwmi[Gf : u4g*c%dm,b &o>1\HWV?*UQ7+|0}Z2էjO%J_S}q0xvrfKLssɤ|].n䉈)ueHFYf8Yˤs \,Y[~ӿr7__0G/iL 'hrUd׋%_z?}י?\w~ox2~z^wi_|yXܽ>~tv~ֿ =W zaƗ/EQd8;yraao G+%;.pG{vV!#;$Wx}GBh.=o=$4p8o׳$)SR xd(spjH*ƿp\p}IR(s5'B%~&&wv]@ ]HKb!!n"2RGЁ;#G+ilsP@KNS_D%n(J3 g#_ 9p־YLA% +7,\ۜ-OT9w -Tt϶a TR?fܷXq!콫2hDZh07d*|I+\~0@4Bw*a|*l "^B-Pv(Y8u#`cLrϏv ɤ Ag)T U u:n%5m\OE)-S^C vu4,SS$@~WA|xA/h "-3&vʍ>idq-Cd]/J/%$*0 s lr L3'`^Va0 YT Ϧݬz 4"8^`6].mw͈y  0Of}!Gr0@KQ!_=s$-{S"'t/e^9K,rRULH,ahƷPa z%:`[Pycc!IΏH5ZcZ#] )a"b̜P}@44KDfA) onxO5)܀6pSP1g8C1wz?ckgK\;s>s8:z޵+4eD]V Lx &Q_%H o%qDxq)o"y{ Rt7E& Өn^`)A"60NXVwcF (Q^;n~ ]nhr ȜG#3x#tKLΙHLZc T)9Srl (BBf D&@' It?Nc5o<1.thBC-m\f5 p oǃ( HS >>_ }MpCܵ7nvsl+.uۑjt}W zo0@[EaAL\`5 h@J1,p=+dZ]@P yp4 ptP\{"рGCP2lB0kVk\,8w3|!0HRm@f@0b~>U1tܑu ux1 cC\Fؼ𦊡ǀ$Eا":&DG த݀=+b h1 QC? @@Qxn֫~b+Bɢ!2ipKACK@ f DFR*uhj0<`r4hKD@lk =ȐEcj#łO UTy`c.)!#Uԇ::hG`H xxuWaFK ]Pq!f@+5<`SH1} w*NA #2PB8 XpA`8 'cޣ~-h~tD1|1-" DK?wM'BSܰJ>| 1\v -UeA#vAX6=w?VqP3 XHvSS5X,/.caImqF QU&y$ dU]L>-:1b$vW 0n gFГyd$HUDbaQtl8I1y\Hpz+†pXQ `b郣DbFC PkXĂڀ ؿ5%t A"u\=Nc= {`eHMp2 {%< ];! /I"tиSA4Ԇf?[G1RN@z6ʱ/Ϻ86 $GQ2MK:0r8<Xr_+Y {,əϼ%9"azc$L7m +4Ex! tg0l ސGgwYI1` @쑜D9g3OaN}b ]{z­u=Ph;bs7M5 `i C^L"֔U1G!'LvS0 \#J0vv  Gǎa4 IL~9&PwvRx>JGCnQ4/* D@01y>:W6"JL^sG5hbNvYSH)馾|B $yBB8IRt!p~w!B3"9T*"(]x&!浻aohڙ.G%cCѱ0m)0;|qe'L` WAwFkNϮ}BancŘ.ϓg0 n@Ɂ n/{ʊ_cJ}Cc:{p΀Ť9L0JLwyKA ^⃄kX; `@"8Y# |M9;DHnXIk ꦡ5Ԙ88` P2$%LDD4pF#gX > 0ȶ`a EX5O66r@?zv`ft Ma; HWA12I C]s#1ⅳ19GUY<]hv͢ AnArdЭ&mq>qb«Rܠv) )F4 *Ǝ\@xG)kD!T6$P?Pm1;a2Ҥ ̗2cL?@C0gI@hu7+? (/P&ʄah4Ʀ 2ݡS@0X^x=h[723!Xa,D@Fk8$R`NM* ؅!p t$FXE1h|Ϣ~)^@8ULal[ujxDnwkY>@4Ag(5'I`m^l(rQ/C{$v1^j #:% s.1GxQ`Y_溻&eB`)8p$IJ^]N.ӱ9t89C-~P |<¸Q:0"<8吨&L6#i hC)9(3( *Pa8N8EF{ @(NM5 7B `>xdSDyPqR*0:8A0$sEaaEhDz؃৶bMjy(n}!T ( vv%MN_*{#LtsOi~4l z*e>Qps$R/5i:N> ll:=$QoAU툝*?o#Fe!̉ ^~pHpgY(Ju <Ѣ̼O 5/  /v3d,5cYW̌XQN)RAL߃EUa!{P-ph1>"ނP{koieǥ2UW؜G8`Kէ>Fk ew/4U|͜ 5ۇ5?v\Ci:8ڧ\ahQjPe&T22j~*lPyN<Ѹ8\ p2UBS[' \JpވlM_)FL! ɳ'~D& If,m/Uri6@XXkX Nh71Ͱ8[ ].&$R]Pfy2oCxo<fMJcFa7°Fds) eY\K0 9}szsN?G,&&WL֔0YɪL:`c yn,'E3kZBB.J*wQhy*0b1$*$:8q2Va= & N 1\ DuLi(HX +ⲁry,XΊcQc5$HToaT7?Vf)fy0OۋikC`#opR %Knoxki>>DS‘Ӧ) 2O!v~=Vzl㫯 3j{OZQohJ+ %oЙ:/Y%( (XQLlN٪NS"OLĤ4D\xEq"0(bpp0s_:È . .FNAE0*0 :SѴ`]s@L~ep{`/ Q !'Ȃ%aLuq 9ISP`j+xa&0턹]$~+LHR6 6h0‚אЖy]PլZͺbѬN- q0ɚ-a .M-c0QH^Fهkqp.:xWyY`1V2 /{C,qp@\< Qox儰oX On5jSV8o]׿$$~/~] ec1~cr8r<)vemt6M_ ;1XSܒ &Khfdx?~Os*V{zߑP< &rh0o 7t'imNU(AAL >Uû<,v,h2?_>dCiHel8,}юh.JFݿˆ$LIƓQ 푙߾ʟW{$sѡeuJC g='>HSj+*h.OѲm؎v>hݫ9_ŖO>ze vISՓ,B}^T#7e\2'Nnf`#| _o6}hTd)U8o6αS%pV^!4_˫enWආ^9i^r(+i4I?U!ZUs s6mZ؝X}pUtOsx1tyѥ;izW6@GU2ֻŰiHn0u4*6ŪOK%ьR>@}-8u lXCkBMxGwSTCϝ6/?"y~jP9!}>>1?3Xl+_/ýYO|>9n<܎s?u,wMLS\^3F0 C rX8IIsUuN9yyϹ17sz8i "3jGĸ>|MlSxD؆Ldj'g &E{C j`&+iܪSX0lZb* v?EFt%X#IW;%mUlb&W3Vp8RAd#T;+VP6U HRSli)QlIZʶʦ+lf)Te3Z #ʓU'ͲU=`Ț +׆J endzbH$Q L-+ZMVnB*eL #rHr(_0,P/OpuG"C2%-͘^Ov!ƹ':V3&$-;,amJkC E`2cCIJT(lw4 nV}>td<HfhY)!$ LL"4TF9Fgav#Gj݈SMJV"XPRk遤fGvQ![N>%z)S(ɌNEc`@ }^3:9&V?ٟVo n%.JO+ې.Ƹop +Y 5d'Jڬ݉Y35 r@Z#2iYZѣMڝAZ*Zۤ,!cy=evUѲU6]Wl͑PQ'dѲG;%r}w'UXTwKzRj)j'R*R[}ތ4x$om&]F L?ULJϓ*A+sK@YPY-xtiLx 1 \1d =iiY(ܣg[ڹ5m{T*-=:H%DЄPҲbk 剹\W" 7OT;d&oX0bTKߪƴ;nV#x[_t";bvȨ@͙~mix b̰!~Sgo|/>+%~ċ6V80i4 ի[q#} W&6tcbҦ;sQ&!.;sUOTgQP`)SRTiS'vnA5LJ E0 R{BAnalogInputOutput.fzPKB>fritzing-app-0.9.3b/sketches/core/AnalogInputPot.fzz000066400000000000000000000336541272363601300225150ustar00rootroot00000000000000PKH8DVT(7uAnalogInputPot.fz}rFwj#힉qvl{ag9%"5$[˜g: E$^@  P/*T}Oףv0 'g\nƗg$7=_噄˳40OҩLgW,˳Е'ݰz2г+N{B-sPзWlw}ᡁ RuU ڐ,˳_t340>| g?Knzи`60 Y?7gz.?]q]e-mjASQ`Z_x/|94cWëH3C}99xy 2% ALAN.C\ C>ͧYr=Bd";KQ}xM?F z >6x.Idp0]SKojjMT3[oLe nNȴ6koq_wK*pv<_UbTR?B/>HS4.kͺ-Xhn&yu7hZQhN9?.?]06B]DǺ"oӟ |f#psMPElj9uDM3A%h2 4W>xۘ"J}7@#xߥ1B8vc ш!nC 6C8wی9wpLlaQ|c )ަ1Pw^w))hੴT Kڀjb{^ֹ#U!j|/ՙx`Jtek8? Zbzeb(* *Qk{S!p*ҴE ?=^z4Rt^*K rRiNeÏŅ[[mL\~sBBLJ7;Iuep뿜 ̋wY 3KUEBjyKH[Hyn@yTo "Ꙑ C%[F=-?9JztG[RBٌ)b\|!$әfՓY10B^Rz Z R^E+'_*MGR6Fo~2O_ͧ6C""bȒ@٤&p63wN`p5߈PlТ2Zhy(EGL?x$%VGNaNqцv>]QG9F%yЂVͼNZJ%70ْ6)˄@o;8."Εl<'|%mYK5 b4߾Woo|o*&nWtUeot5o^t?~>l2wҫHl>q9_?h %8DqmOIOgxClK`^ t u"ҏWY&]3o@2'[\_|//W(vt/ }PE/Ɠuo&ҾYluoz9 vM©bpbM3?Ѥ{: <gJ@Pld~5%OѠL#Pir}̒t!HP|:pyɗFp%o[Wqa68OTd'2dr{ݷY`Μyz4낾k~;wvިK a|a^#jMbA(A0,C44lTEEZW,WGF ذ,=e 6eb+2krV=$rWmxG.%,!-"3 &NIegʎU:ٺK"+u)amJu Lܫש1%b!]W|bIKF)Gz[$z]mɥ֣ɳ뮯__\0W/ É޸{1/kn O/tOCf: 8>Li3Ĕ3f0*8"pԶ,+6 *3uǹk'a4ܡmgyo*l{d6kv@Ƴ:D9r98tBpZB7H3ok_?":_;p3-m;?;$5TG;y%^׭L|<̼]JoDfpp7Ȝ·0'[O`ӓ:0uqB#$FSxg6fpY1S k@mل->9[VӆeqOb0Zj'֢J(ϖC'-{9m5޸pn{gΙ;sq.3=0޸ãOuot[m!<@/2)Of$|zӀf8/njLC]mw̿=1{U\'o}R:hǭK.T\'os ,k_2?nojk1G-Zoz~=j yR"7y}ܰ6S_OI)鋟4L?nSܡkNǭMX:iǬS#g<:o1.3} ܵiiw~Aۿx}i'ާznzv޹}YJsMqʺs}& q͊MN;@>Dst=YC?)iṠ*Dyq~Q{x}ډi⧝lnv*~ FS͔qʺs#}ifsMSN;H>s=gc!Q_d{Lׂc2RpzS~ 1Lg5!2ߘrk㭁+ueWfED Bqh}計:SDʜMInUiiQ%KC$+Y]ñ{_w8j>0Q ss0* ÐOT:% f}'DƔhI< |[ΦO'w˔ʼ)7 `4=E@6P ))#{J-ďQFI )-'mAd~԰r8jj@M)'T*4E(Y?%|V>!mO}`ގΜ4: y߹j-O]՚A|YT(xim Ou(E1!̈́*U=6-QmAeMO5싨CBJKT[P9:5쎨JÛ~'T`t,)Slv^UT68 e_so染qz ΒC̾"17⡴G[Ĕ!˜",ug5m8889<\?LX -~p}Q0êڭ$o%{mg{$gb|rl>DDe^řduU4<"JƔ5B^c8k27 T^rLPAs7s8R#G֡;V)z}zyvot3KÅ|0Mo&s}B8Ob˵l®{@is~wo>` 0Z>Aq8?31zhvl Ҁdߣ*VսQ͆j*iɅXx|;4Aq+TlPx]b ePP̓BNdGWn3"AhA6ϔao>l0ɬAgМU$[ %vnpvD_P.ƞ= ,.=irNf5T6 Vԭ UIw5/%DI;Q}W C;ٺL JLs%H)30d1"d6[ 534 '8[j2F qըms'y`&Q.9ǕzoBmls4Xj?z58/>\N~]ZȽvL-  9 D}ϯʱ'q.w+wo /& sLJS3iM9h՟?]t6_~3e2'/?/R埿ſsow2߼Ue*_u߾6 x߾Lo^t?~>l2~?\ n(؅nx4~0L{AG^$9$?FfsLBe8;zI(Ȓo> G $IӅe$Ƴ_drEtt=Y6Bͧx&0 o^*P*h) Jͤ aӉty /ߕtyP.(&X{uxy[(Dr:0KOjjen B p6>O݂KTz N.\HėQ6t,swX)ñJeLi U~;&Sˡ| pV`%3P3t%\X((D뭅f x9.=_'<U<b0!ñ-re5Hު>俸p0q2r%t3mP /T`W y,\P.*@ =yA8lx<^+yeBv Cb }١T<ƿtxB9Q>A C#5L4O[$;uA[2; ^Jc*43 1r#]Д?6e `:`1CApv*Wu*DD. }:LevDʠTwg%AoϓUYLz>Wp5%T.; =YtϜd` \s0|BBë &ޅڹ@QF̂E[!<݀(F *(C kG(ɯsi-t܆^ QYu 1#w1G.?k׈BL8zF02Th)–œ6i+.αoi"` N@oA OYfi j:3¡$$ BSaB=^[sc_,ZZ\Q貪O$ ?xDhG/ hq8v7@POg9moSTS. A2пEx@Wje_,HtZhɇ^ 4) ,+ * K5練)UG+Uy_Aւ|Ac)LۤviʺP1Aa{"'em|#ޜ#xȢWUJ ,5e; ~rcjȉR]B@F_aQZ8v jHBx\@c#*GhLF-W60.Л4Vc>2Ǻ&DC(IႧGpv^𕝀ThJ!|@1p G'b1DHu#|Q ;FVtsQxW jS7 * lqׄvŸU}lch~I [hmaG*uU"W.4&aI_R"L 28 @\ THc"+)!6qO%u}@k3x&lbE8}=z\ǁ܆{ATwD4wPht%eDCuVA5V\A Ap+W+Ggm˗VJ++Uh~.5v]\LռA . h!G񒃀)d6a*cpBeA xF.=|)@L6cpnUw*VV`` x~2H!-P}b차In\cm՛sʆ*rg(_Eا 2qڒs"K&ID3HWEP0 )@=L/bhшG2T/Jl".(55H\ L` (d AY{ et2&wSh zgnHg!(XH@k`8v?"HARIhEԧ*&:&LD'݈=ş+1hR1OqE8 `Hq|n٫`bH%:"%"8*ƂJH[Z 9jзbAM4 Bf$B&R)tlj(=`jJrhK̐Pmc{F؞d"3G(D5 Q{-c/Ay+!Z< ARA舶J)($?&AzզE=A BG)_u{Q>Ғ4aZ/ AJZ:!,OT$fU KZ*`|`_^9:8bA! LY7Ja^Va #02FZZV ^!!b N~0E$&E" C#nHPȊږBNC ]KMBC$.VNQVI|l!rFq0/z9nb/LB؁2%nJ+ZPLAmI`v`+ ). jg2ӛC RLFIA"+`+I  :XTp#4g\s;ү7 8D"[5wցO['5Lճy"z)ߕD8[A W&qh =YƣF ;T@0ՍvMP|/]0NP1L`@)`6 nN &MdRn e!аSP^^cchH9COGP^trl4 "GQuq M 9m%g#YP & xC\L8mtm&A[ g]&,Tʄ,癰lù( cr9Da0aQgJ/T Ef[ui "ŔRKJ@1hϔ0DcqL`A޵!vgG:aj8$1#A0f9'!hG[ q4x\Ď +'ٝl*:Olb :yNK ~0:A;)j@qI4/#ƖJHaJ`~_٩4wtjx'; I9 겴+ yTSE )gi~ P8A+r 1(0~uQ!"ɪL!"S.TDoڽШlp]JfS`(+.RJyivAv(D JP̉#惯AL*ޞ"Y>qRޗrTJy9˟!(Ջn4leẀ4W,f12_{JKE|qEY3dHbgl9=UoID/% #A"3 DrNQyqIGl3ԌřFr 98iǡmO@iF)X$\(S46--KXે92Qd*N,ﴢy)PJo"{ a1Z S~2EEԩbw*iӻ+٥JCWJDMC Qi2+ `c f\h45ƚ͵nj5?vyCMPu:e$BqP RFB""TʊPe!Tg#GJt1g&j "3T*=LїxmK *ܲHspJ=єD#b4AndF h✧|(I,?fJoI9ɝs 8b3IqTC):XhNAj{4cLW RFP9) RX*Y|vC}[QIXOW}#Y}PI5'˗ȣ8!*e {].&BycR %v'oM8m_[Cr4;/Dt8}AFs8 6Ń_OԼ&yS|jx̉JƁȕ1W5*,3_3^": :kTp\Pp~Ucي~q~DLLAiba%fc8E,uE|-G!eNaE0*@#(!:BSɵfE䤣&R4" Ї1g@xev.lj 9- ?6աOO4=~m/l D1 ;.?+NLR9 6(BF-cȗ<^F^PQW KM Ŕ.xì,4df1GHi#d*ʁm"2N^ҿCzSū '-{o >']bkԛWVof'7m7m~5j0mˎc7Xc||ϟ2s2^ǽѯ + Y5fWzqI2வ٤&nÏoiSeo?`Lal'@8f{溴͚2!K uZP@L!_є$E;)?.syBVvl^g>~7 m)x`V{@Xm׮<Ƴ:洇55[p8D 99a!`? BO OYB^G x'wuwEH;݆xܵaVAՁR9v[X麭V{SĮ{5fݼ^-ShWø*^^#:u7Ngxn5fכ~\Qabs%mҦ~MڀrvG'1]s\w!v,l¶vBVQg{ٓhoeaZC5xnh2K\}Bkv$<2UZ ,kw^Hlݣ7{xuwS}MV4;6U,o V\Ţ-?ʀ Iq݄L#05&W|$`SSଚ^鯣,@})0Yr=`M ܏p+σLnz3Nx =_[ߋz3-˯j$nPQPOChzYJLmT/L8;y Vo 80Xm"e 6z@Um&Ni\x^!ݣi%n  C {KI5H"T|(zO1Ek%Sl&.~FZ?+ ɟYt [6}xR'`u ưۃHsw")V?GBs`kX.ߢ 2z6H>zFYvA3%-%9pl6 -c:1![*hibkv"?0$k˖Ex#9k٭[Ȣx\o$n/ 0QiADAuLi1"C'ᓇj- l2P5Or7m|{ Zϼɞ"Cn3\s|lLqek lT&x(\f>?Q=bH;qY4Of6B:jMH2Cim(>)J֮C)c;vl98p(M滨LHf6 hYٺCvl5GٶV=I"\کI0IyJ,KZʶs$i)ێ&0E YKIppl`VT.fO (7冔u瞾ZvcWe6SNl<Co^ $-9*Ϛq l-#ҳue|=BH̖Ogf,R3eiC;29%~ F[F ?Z:shGϛmx|H-N4.ٲ5e"bkv$-5[VƧS3y`j&rE֊cf52l.C髶ypC[m 73>ZrOxl#GK[{h 2lTiDP :ԯ n5$Ըe&;v~q-%|R46!\iR=<˝K8.U (mjp& %*蠹 Xey}%?PKH8DVT(7uAnalogInputPot.fzPK?W7fritzing-app-0.9.3b/sketches/core/AnalogInputThermistor.fzz000066400000000000000000000354501272363601300241070ustar00rootroot00000000000000PK{8DU:AnalogInputThermistor.fz}G1wȩאJ6DR=26ŞEQ*P@5*ZۇW'Q#3Qɶ.%_G`:N/hB΢?9/_؜_ߎt8]>AO  t!Ϣa_HnƗgӤ7=_؝'dz`t~͇x%;~}3{㾻~|~L%Hg`xy5q&8T7PwF OD޵{wp'ϢOd48;.x9EY'ٳ7^O߾뗃{&W|Hw}dvozӯ?}_u{v>q7]8~]x;﯆ǃ_|UjW=/\]]S ̴&/x4<>|yq aN$7=_`6wvoz۟Lݱ{:P .߿ͿC/o_{W7̿WHHjj>y~9̓]Hl>r|]#:_="|?+| BMTyJnlՁM۟>_ xvTyt3ğo](/-[lȧ.DEz8/w4ףˈ= WgNkɛI!-Ǔuof9,794' F‰)TǟFi؝6~A%w;wtX~ ]w|hJƑ*KANUj;u3o kJ_kGh/ 7p $Iˎq돹w50}N-ʤ|WyrpuZ˒YD "W({z4p v3:[>Zԧt[b0biK =F?v,-ρ/BVP .c]w g7M J>hq匟ji^yEO*2GZR_aYڎ93d+'Bz' Kud*MBC|\zυ@!EɄy%"DЇ e>sf(C#>yf[Hka5FޚT_C' TI@k 7i#S (H@}3 P)NtF. k5lc5ƠT_s7 TOM5Я6?L;UΏequJ$D oь}|AlI! d:qԻj#oዳ3uyu~93=<1WzNC$,7PY~Ҟl]s_ͧ>Smq2?ѷ )>a)NYZs5_+"!RG߯H,_ .K,Bǐ-Όm\T@FX&i{%)e_6Lfш]o)66K%3rRdj5%!4,h64?tH$9ǑX7_#!xCECG>'WOhGKіwhbE_&栓R}c"6 d٤ehjsh䥻Vհ38I&3qd9ׇeRExs8L mIFBVqtYוqY3S1%T|=jHFL$>A;gVX_ݑ-+ SE^\ZSf Kl4X"atiyZM"1]xDc #myVyU.&?-۟$s}(.fT'%&8"/CV,aqE^ |%V2L5lojO׺mzmjn4ɉ~ '8iYeas+4=PL'(vu۾%=d}rdIN;_K7k'vvov0V e}rIk'xuL1 #5k)fGkjȾj#}\d~N>_|t~>9Qt 'ᯩ̛cCӎRO;_O7;Z?X-sċzy)ň-E~1Zģ5yXmɎZ$;RKɎ(+O}}a5GM_)3nfٹ_WOUZY0f<_ jl>~Py u؉"hn|X[P*wU?Wx³<^5q-g7Un.LY2 U"~>D&]N )v4Rznr"n2wIiBݿb龪Am,T+uZ |kH m +4URjA*F$D>- iȷ@R} |g**&b:IQw@OETf15H4I+mRIt0)RPn?ݲu-?;;v~عe,5` #|d Ro[bʺI݀(O`%-4I[CIKRJRm_ /$8*-EI _4BmGd9*e_}+ ۞ ,N,I78r=Oe:10M HïT U ZW׆Ο*-+B{)XliKŽ-ɰf#読RZڂJdhX}UU1_؞P*k2'-άMKIXf݉6 $himȪ-e-EK032ʈV )bJHij *ya:jVulC{T!&) qU/jfd¬FVWHP}npOCRU6V]ڟMMcTPR&TzR&K;ZzpH9bO6CKi5 R#H>d.h(9u Ǔxp0:/!q ,|ٗRٛhܻ8gg]ot~.뫗=K g>WiCM)՞Ufa^>\N~],0B6̓{=t**W 5 f^{_ Wypb¹*{Ϯz7oFoz9UN/ф<g<7o%z_._{Wѳ~:"-w?Yj>y~9̓Ӵws5ϺƮ=u/s.baq4=7ҟGߟO> GNET%$!(/7_ 7_™Tӵ:tg} *{WDFFܤ&^v?*)92s`zJy_Άg`0;ҭxoD8tt1BiY;L.$I![w쩡$9_QX&]g;ƺcʝߢC7HrYMN!_SP)Up[u5oQD15U+PAX0Hh}{A+@ 4\wie`ˌɎ]r#ztE_!hMD ̂\I5﫰U* U»i'/^A weZ /V0X/IL^;f<uzne]%@"FACHD +nD^FrcM7M,3áݘ+v|ҵ%-vx||ڹ_=-TeD]V Lx &Q_%@ J+R?x1HeB('Ly}Ѧ،O 8 c[1ލA'LԂGYvɝ{7)hX r)G# =,p-1` 2K"5j)P%@L+Z4j||!:5:aHu~1; 8xBF*4/ۂ%@Y@ 0 |1 :co5* FY9W;꘷#E]. A8LgVnyM2;V8h@sUҌ@`A}Y_ w)88!/`gAGg 0=<Xct1_\elo҈_"C1 p , dt#&w[Ci XgPg(h,I@06uvlqRPBxǀ&Eا"8&DG ੴހ=ٟ 1p4Ř@E`kLH(7U?EJt qJd_x0fL=R`P0d{@,a Y()FQJt]1F * Qd *4=ls2t@`Ȏh8Cj-FXn rJHc!ꅎN! =^]8UOCɩ_tZ^ÒP5aRfWLBt1v6A?-!zqX2G!(U[rV2 y{':7 v;OV;~,qt$e82 <)oy <`"g$ }Tq2yHT$yCkD AY`@[˩0P]0Q E h+H>8 ?A?/X9`nϠƄ(#7A 0.pc@^Fha,1n+vBŅNzl|ygѧ#c(GA;#h(G4d$pJpFmA O5|@zm;!!؊qhz@t|Z4-UU^Cg+@{=LMa z,hN#ԑ4u8пgkUw 71t&!0+7)n!3E, @bi a aN"R9g39 K]{\­udpDh3X{tx~ 1q_]Y^8M`` (}{U+h.كpv(N#Do'^ (qB$368}t\R% ҿ׀^h@$ \òCT!CшYViן; .f#A"a·ws')1*PQ' ǥdm;Q \H!?F/ f[0ƇKp EX5K+V6r@?zXvx9;!+0G81vcdpX;c`Gb Wc rDy׻ BEO1܂%̑AbcЛڷEhXյ f(إa;k9Q6*HPy!nC&G%@` r`:>ah*I} F[w{0aʀd=Be i )  o;{ew%֍LV2B< Atz[)oW#vb~0'0xU e9+i -2Fs)4l|0C23wX"4e('Q_O^"`a~m='1R[J! -m|qq)qUvǕ'QTCPp\ G~OL(QܩV|Pk|l5E>D!ִ^N+ ;-š~։. U̅X.TF B / BE-*/gB579b'q.8SA[M"@@w୓ K %?nl͔)P r|}#c.ID?"xIf,u/U8őlT' @ʡNjaq*\dUHc8@Q8+Γy[;てjRYk@?1 O-Å,.a(\؝0Ao\sC`q)YE |5%L$g*Nc En,'OE+c\ZBB.N=LZ8v+`0w~ ?+~ )H3tD1"kc(+z wWA7۲Ҵ}]%de]k\G3S8HD!Wq-s`1F`r0á[\o?Im0Eq,Šm ??Ma,XcgcQdcU$HToaT8Vf)fy/_ۋ.dg #F2तCx }b‘fS<d0'̃+[X#{pXI{mq>񔘭j8J MIsAeBQ%3+A K wVZї-7[vWpJ48),7iܧ )\ė0b kC,SvPV @Py@Gp<,d;P%k>_9== p/ Q \!'Ȃ-aLup 9IV`$p vz诰#I" ۄ  jä<ԯ%X_uU˳"83 EC\# |=(w'$V,keX[7 1(L^E|pp18qBHpt2̫,,8v2 _(82eq6q@/hn?D-Ֆ –?q7YSlzځe v}=>t?f|K>~?ίQYj8H;*n| $TyKbV YZV3ԍ֬g8PGkvDU6 eY%uYCLRT<$Y@2G-Gch30ĞTO7GeWcjGL #KiJQZb5mlC+^ \B#@uWiӓ z6pO=թ_ kW2KiAWҶiVKîi^m6xU؄}/o+GEj"(޻}q!n+Nn!XxXLiCGͣ"hu2GEk߽͑v ]cwVUM~=3ެdf',Por`nd Wge0>CmE`o(2XXg/gSs~n+v\i=,SL^֖Z?mRSYi2O_ͧ`,{zsPx◢KH.yU1,ȱ暽F t=1Ir^ UG.p~],s2gq<}2: ?)Nˢ#v,YǏ-]24(mb .V&!ƒ WbeiHՈ%Mejlu6"A>oMqƔaL C2 ?)I|b=v}S3P92Kq(f RIĦ @rt\!c~xE|j0\%VP:Eb˞?Ϳbժ)ľi@)P,i\tBA%g>sX(k,==\n3o&!4c8nukV[Ƅ<$tQwMncJ?xH|Z]ZstlTGX5;x2֍p.N]I! {DvXOv") !GwjJkwKq=d-vɰ$tI꿟$gdRAL|KSSRRJXw$3`z16-f kO4y#ڷخ) v]lu80jcD3~_aw..d,, e+ukss}r+u.JvSa=aۄm\C}UʉVDHY/H)'BI{dU?%Ok S͗TzE8 kDTَbϝhjs(x\٘`%$-{փY]hOmOT%&DZXgj9.ewXqB}"ҵcZaQ,.adUT#8Kkn<$R8i *Q3IJGclahһc[J݀K1X[O0AHs6#r6f*lHڸU2TJ9*Ѫj.Y\*P#!ȓe'qTM(ni6>'Z6KT7H;ɶ<3aU'z i9I 80/SiJ>SS%nuje,[im| ϲU '-i;8lG3FF> $8iksp,ivܪNӍaoz&8uawȖ $UU9}aqRhiYeqG'Hڍn$iybmNqka}9;x%61bUŕ asj|]VT`8Z*6xYMkCߖI&-;"dTU SL6l4_l#O k5k7`Ӝ)b %mcE;ՆRQZXVFkȣeSZF5p_&CC刟2ZнmZ'gCk4u4ܱȘHe霊$2C%hTb( mx҆jsph97PR=+)M5C1_`9$e샬jOS]!*R>*'l-]8R-[[Ủ<&8a9O.wXB vb~vJ^ubb[$iAI $-IkIZF}E?PK{8DU:AnalogInputThermistor.fzPKF:fritzing-app-0.9.3b/sketches/core/AnalogInputToServo.fzz000066400000000000000000000360431272363601300233470ustar00rootroot00000000000000PK8D"q7;$AnalogInputToServo.fz}rGwr"tډ|V3h_IA@˜g:U7ĥ`ibPUe~uoǝl>N^^Д\tr4~ywNs5-~w IД~M/ܝr>t?^|H7Gi2f4_^txOKouW߹j&E2pityyRcRۋpt}xy!S8^֠^Eޏ*_t&[aw_^t>]Ϧ'Ez6 Eg~3I'g_ǟg'wO޺7b4xLw}fv? wӏ SWb~3٥/?;|w36?*%S(Bt ٗ N <_~P_}?/fyv4vM= 3F/DNޗ kb>6R;_7)Fmvf7+]M8KnmlܷB.8 NוWs.s"ZeM/1G"V(nYo兦Aye3Ky+_*Q*D .{n8 olN뱓kNjHHV a$4MZa4u $ol}Qe13bJ}÷ֈޥ5  ?("=%P(DTҗvr\go#'!$@I/^o%(˘Nx`WvʚeԮS`& rZ .1`)V=RR٦Fv\ 1'!NYe&6U"( nrigGOgKOENS!xH pRUdE678٩bLNM'.WJ<(Y<,KR2ʬwFN)5] JiܔGG5L5Fj5{#mI Au?򶅛9 6!i9̹ԋrdTnyғǑ0}M2G>=6R&% r6 SBOOPGR:]!Pf`wnby_|_v~wo|o^{5*R}W5Y,^z>}J?t:X|1 u)o/迅kٵ>N;twNWe_]v7qGt3Z l|j{쬢^_:ss嚙\jytt[2:2>|aﲂ`9 AH֋tvNw*o$YL^bz[j·}6/]%cҙ^upv?u p_;B/:^R %!`Nz4Q68(^45"p/* e @ss6?&i+i ,#T{O tOyy}| (OXA=QX"z$zi9IE-WhޤPrJr$i -X,5D=`*էIĕ;,J[%=2aL)'5HRe8MS*9Wڼs'nN&FJT ՒҊկR<5<'!)ъVVGwp@yNF7)\U|$nJ"[;80W60&Z->REgAi ZnN[}vZX@ 5I)u>&$K qMkFic|fn Gas<'x2)Jث6_mꗴ6vĖvQb^_Y_e 'mǥM +H `oiq 1'T9$C{u oBH [߶؎?Y_3Q\j~I)"W*V^%U? IB{?vy< {Ofq8wa<9;syLx{Al#eqI|I[`*ŠhOVi l){FCIZqr5-p~V"쇟gYG6)i9 Vq3$>*Nϋ_ϋFOu+WeX=}3$S\ML+Z)6V\-Q[-3mR⧍'3[“sNkQϋ׵\ώӵ8Fi'XfP*6 -v3OcP<2>/s A9-WN^3W:f֖}Z}k?wRڜjA6矿jy2?mTٜjs}^}v~ƹvJ6>?YSm3hhyN~Y֩yўўiwn+nʸŦ26lVdd'1|TJǭk<y|**CNPf秪ѩ1g228;u0ugnMZz&ǟ=n2%i= $Tu :urLSUD\8ezZp6\h5-DKX'S \/_rOVv9 gRHj@JOnP7k>Sd&elqzuG5;Ām+[~F4׷Um4Ice-^%BpD#OgSOQSmyjӓ)Jc<#*e<ұjdpLk3rP,rM+|eTb= fޘxTn9ՓU!7 IwV24U$e\U[ +UZ$' *˂zxmk0k">fj )svZŚ<`"U0ra3+)#٪9<|<[eb )^]fZp̦ HڄZCḱW,0RGӄ8:EkFHҎ&;fH6} DVmA$fԪ)6܁q0_CpdD,tʹ14vMqZdPRqV\k)D Rk6 KJL}\VHq㏤y!Iy$yd6Dvxs $fwJ6DHe*2\Y *-<ڃy !&j[s 4M6[JL-R ZSj*#Dh;&#F'ܭ0a$rͪ'&[>`UmζYl%T^rJj8]#I"uh'Ez-$uìM,o$RM8HeGḿBZ3p5%P::Fujv[h> »rV=qR+|J-*| qZګm)K^EnY{q Ѯͤ>q ХdzmzGFx`{=g䟳MrqS$DJ[\D-~3],[I,4s~ӿ+ru_s?G˯iJ 'hr돋|ǯ|J7|3u_z߽^~f{})ìw3{`t7(M/_W+Kpw:a|_uJIJP靈EI'/㫽踂CIJEe$&_tj!7@G$R*G쇻LѽuJqh&v^鿛tH7Y^k_GBh.=o=$5p8o׳IV)~P >Ԑ˻~]T8fdd(-Q |tj4΄KduE(eu$$5vIB]g.Iںk Ixj$THTс;G+i hwɉr_sꋨ EildO?k(Zf1(𳻝[INX_TPrA¸f% kŀtOT7V J ~_("Ѭ[TnD; m/I5;mܿ4e*eB ׮ /s/=ow OvED7odz]WJ.K1 >+.3 r'ԡnE~d[*OjF8:tAj/FBw+yi +Jq+nBJS~{a w  JUP|P/[%eˊȝ<[gL2וN)n%NgƟE r[ſ7_s3}nʕ.a TR?fܷXq!콫2hDZh07d*|I+\~0@4Bw*a|*l "^B-Hy(Y8u#`cLvϏv ɤ Ag)TUu:n%5m\OE)-STu4,SS$@OWA|xA/h "-3&vʍ>idq}d]/J/%$*0 K lJ L '`^VaU0 ,YT Ϧݢz 4"8^`6].mw͈y  0ONf}!Gr0@KQ!_9~pYĹVsȩW:Ћϗ2^ĀE/DD%Fx*&x$GV؀C4A[(-0vyI2a0-a(<,rDAA$EG1zEʆHJ}v@1fw G KCHdt(xQ+;t{X hS7s~?s؍kk];[ђkWw ])%ڔM_gB{`01*,@A0,~+# ‹KY$c"+)΅6QOFuM qǂb 0O@_q{w7oSjX zGS2eF<Yx4X t Df SjL,V2d h0@bb5Д!:5:aHYuj&yu@Y@ 0 ^k஽ytt`[efK^QvcގTSuZ;q" '/ d;V8h@sUҌA`AY_!hhh0̃3ՠ@W܈@g3l0=<XV`t1_\el i/|Ez8~^l2:;𩊡㎴3U34Vh :68vpixSPBXc@x"S?X`ڣUpWnBO 1p4)Ę@U`cNH(Gp;Z  I!,vҠG0Q[31QVmg?D:"[9MOkt@t)nXeK q DCj ѕvL;T~SG ,CỀ^¸c(Й~FYI,@)z)b,bkV𰤶8~* z5'WC@#u`x5#Pα+!!P ]0b 0<9HrfrœŐȻ|9[zvg:nd9j 5# 0D)' bCN킫?a,:?}G䕢afA4 Mj~h` 5u B rLxp/}lDv5j̜1 첤+S,}H$p]C?VׅF;gLWEK"e\DQ!!:BBv[-kw(3]Jca0*R y#avʠ' NC"A N*"^\;>^;8t11],%,g`ꅯ 0^6^ t.Isd-K?` L (%(G/?5 9 C/װ0x9;!+0G81vcdfpX``Gb gc rDy  BEO1܂%Ƞ[1L1M24|xuW3䞹A_R@R0qiT8Rx׈B$<&lH~rbve/ d"ǘ~6`:Β! *hnQ#~Q^ 8M6 .ChMCd0C`60+{,nfB0Y!!ȍqHT0 C" <;XH,`]bs#<^DBShpZ7l Ԉąγz/1}ohrPjN "7X/-tdP_Hb FtDj ]Ucf,uw73Lʄ)D1RpH(]csqsZxq|aEy4p!Q͘&o$Fg 4$ (RpQfQAU8I947s4q(M&Q "k$A of%|{)7oˠ:+3TyˆbPIU *EtLA(8lyocr-/xʣA(LAb:(B30r_qA2"8?vc. EAdAqj|`2u~q `H0">."#RF]I2FU'Omń%* 2P0<C4 PDʚTxtmY*vcnPw+1z@;C5,`ǒ1H +K !X뵦 )ίMɁ"/5E  6,e`RE!˕ OGkFk:ckkU)[+zz-܀$uhax6#HIiD1 sFPHm㔏lH0Hsc $C&p 45XA& &{Ȍ"0f A` 7/r܃jP8P|F t hxUʌ}8H f_c =Ak` uD}vtRzHnr :-;U~t0?GʍRBY*βPЕ4%y W%yɟj^@_<ΐ܌]p 3V2bY'%M ~Rz[ITXA¡$F0z rCc?PEMW")W+jWp\ipbK5d-W6 גo"Z~J {B`ƖjryU5BP+hIeI5B%\F"G tbs)$ Ȍn2V jOoLX*pe(Ay# 4iLb`q0#'h$/F8pJ"@W7$A̷ pbTa6#AbaM`C;8#4l5tUțJw1@Q@ɼ-@S5)}Џ=E>-JD0Edq.$v7 H͙Aas5:,&Qa蚘R`҄LV+?Q-v`9y-:4?\vIaҵ#'{!c,8,eŃr^e,8 #i8 DJ <%xP.V"J7 nַiKdUkGs)$Q!qï89I0HpJƍR%`O'APLƢ(RX cєƢ,"vV"95A*4z R6KaM<ض0KI~fW^LM_ xQ.Yr{[5Lۗ!LD06O``WĜW~Fc5Ƕ=zopм-T0@ Дd>W4*,3u1^::KPpQp~絕}ْ~Uq~ DLӱIidN:E`QN`$|,\Xb]:0a8U^a@tΣiŻ$݁&B8" 3$^ ,8CNKØr8VZaMa sHpM=Wb mBva5˯!)7-x 6YWuYW[(aA5! 5d)[,\9[+ `񃱽$KLƱo !]t0Nc2%d&!_($2eE8t Sy4ޖj a+,>]/bkܟ/|p6_?^-w_Ɠ]馿uq3f8םێcw~_ΧCI+`;hRّg>ζnIܥTf32<ǿSڔ?x [G [ ~xZnwOq؜jvQƠ]ҫ8,FtZ$/~8,j7'_.?&_g)쫺z6e',}#ϲBH*A>fi6KLa&o]%Lfi7oMΝV)=AK2KC=W@8ԝFsZeq8vVSnZu:z|8*ce-fIY)WU:OF9~YYg;'s\ӔsbZ{NVyvspm<쓻C.?T(ļn\bIG~*S_? ? yܿ#zؗ#h<)P?;̉o0O] t?-@6Cr qU7ǵ ]Nldg@#! XP1\a,Fl'dv)Iz3tl+=U~Q^IxF(Yv$_gR.Ne%_ʴ(ۉPl'F7e^ ) o E燥JaSucƒFMU"fysNH;$I$L87cȳ%i@GȚ_H76PAEqZiqNTfl50[ >XQv }+çZ6UZ.m k}N|rg } ܊udRnQ{&~{8蒗䯢lǁt }mU* W^Xqhv绺aVu9f9όoBTu[q°ϜF1%҃#JLT6-b^TV<0£UkxR*ԯmլ͎Y#AV䊫M\1laHUG]gnŃ) q1cKxSEDzxx8I#"m#C&$#RZ#&!fgfxf #Yg.Eֺi@qV,) y$ ϛ飂GdeGEYU45LzgG i͎5형pRænTtZdjJzgԂDvpD~Ң-jz5I GU@-5ʸ2A|,29p8Zuy"YP$GkNގ&,NG[QR)t"El.q6r-NQ泾8`CT|-Zht\RNʾX'+ʩ3qikmBbOnn9RA7#u[ך,ԭLL`%rjk+Ϟ5'M"y;&y7V/~ɛ`1ijHnPEgf'tFҶvy{5tXxҲ΃7;GJ왳F$rãIpl/QHh&=‰neD1tŭG0(nےճOK\ "#O jkN@'Ե&?'Trizk{.) _R6wPK8D"q7;$AnalogInputToServo.fzPKC;fritzing-app-0.9.3b/sketches/core/Arduino-no-FTDI.fzz000066400000000000000000001054051272363601300223420ustar00rootroot00000000000000PK%8D Arduino-no-FTDI.fzr#9&zfl;;fb@TgXWVUWͩmʞٛ4D443֎پ>ƹ8/Or8 HQ$ O0?w?zq|pu{ ڐͻ/_C?o?\Ww_4mChDC^3Esw|{;?f ߟO?>MF7yՏ/ף~04~mzl/{`<ٯ/d(Qv<ˏWOsu nFޏGps/zI^ݼ<\~ `t}[xo.׷'+pv9~?\ ׿;{_0k?|+MƯF^{_|?: ;jϗWg?jO {G˜4o:'\sJxKXʼp9jGJȍ<عu7i7"/ߞޏO]_es݋(Hǯїq?sz?v?f==Ż75PmJ`,g+ BMOp:?!A;w4>ǧD%~|CX1pws6OpS=(J ҋ#SS*)A>6Wβ]P/=yxlvG-Q)nC[RܺlG% (qT4,-"sF9" #. #e;0g%y# )mq/ bgwpLFFW@ ^qW%o qFv &vǐwn\fF(`]hٍc^@fl/pTi^`Y~ - ,e{QA455mYڨi%+mZI6=.ȋKE_@U8|d1MOhSjX򲨞Eѳ›^Ңқ^C#9˦ƑmophB~=+6K;__wEEEfE^6jOȴ(q$Aۼl>B[wpb7Euew]6jCYx^ZTMVx8uKYJ+"dYXYgyѩEcupWaPoV(Īܲbrc+,UjPxhFtqkb,xceX,CuX ,Q1[&$kNnjsq75w[{buibnLHQ7-XPb6'VcxfwDD/O޾}pv9~?\y.F# qQqmTR"/"slE{j5}L?2RlgRPʜCX/>TV_ZldjYHƜWTf1Ǫʼ5]հ -ښj]v iOe鬆N܅ᱲ^1NYz f=z ›5MzɻlK:qއaM|.a'{M'|.״b@'n^v'J.Fc.;]X܅X״'=5/ɻlOpM tVev#oU6x&p.X;&[ KA*bp.-( ;D:qDӔm0R f=J(-Ci>lEEiXq̶l'%VrvR ;)mNJie;)Q4A줴ew8)W̲"2GIaùmZ +KڲќTMY~8+-ݒqt-MeQ43Rvs#5 &-'.sѲC|B<h>DҲ}|ZOXN h',A-C+C۲I-haLVx8 ozp}k[@U0CH:@E`o~hRf6d, h̄hN]jU6D]z܈/=hEbKQxOfZXSfVxSey#< L 1tזZ-Vx`ZX`Vxey#7 ud bEľB`fAA*[L˲"?`\Q@DŽ}FsM[*oY6x8bYx _X8G)xW7l.Kڜ0!bo L&A EqZx(+nN&ENx؉^Njiሗ߽Sxx?z{w?/|?OO_~h7߆.'/NO?}|ӟ:WkAN&Wg|r?;ʿݽ;܌ޏ3=񥻏0'IOO?L>_wOO6j?.&Ý/wwt Soo?aT {Ë_۫_G7<\64zw9B}7ޏ] 'w__N&>_L> c2|{}{UNӷz fx6`t3篿]^508߾L.=/jڟ 'n޹3!oA< w;~\ݸtanv$wwૉ=nrk?wtvuj ~dOsy`o|v<Md~ ~_&s;;<)6䮹8pvm?q~5L~N{{g׷_ؿr^U?rM?t`6=@jvLmN/I 0 Hb g$\+m%u=>];DD4ٮK?D-Ԁ0B,N[%:{5tec&Eu&<N~09f7-Xoo`Q+d~@E3]Tx{=˿[Jz[+Fü08TftZAs^N-_X{g)qχ n`Qy W'$| :QhՎ2]'oZzi2p!zznBSb߮:mp4Tw񄋽<]w޲}halj8.7oW4 3ʶ`J'vϛޔ5qEOp@ߢjZ[a~kophߌ%z<{mQrw5Y47$d7̺FcuS<&<&10InwDF"Mۏ+nA9|}7[]oκti1?,ex Y| &Eѯc2TZha2pY+eqަLi-.4\piKFH^8I ˾p,cG\k^l ֻ {W~Ʋm8#l2ƃULT4uW$r3|*IS݅ BcHht(0NQeƇ9U[.n<ꂌ}ƛcfl}¿U4ǬReuA_}pB1"w "Ӵ]+bŒ?ObNֵbSlTSLcb[8( l`2.'dƐٶ4uQ:R$jZepXoκ Yj5Ƞr8WT0\赟1ȟگ.X4=/wxsy{ͪ<$7yH+۬X?n9WN^,>3NgP.""xn:!.HQ_YD$ui-hvG R4"h1wӵmeVC(5=TC߇ ]j؀x2yq?WRb8-[㹢O2UzO ;<}dVnӊ! C&g|^s*.ڷBj9j"-'4]xHpvPΒRR{0>Cj5tSl"q4eMvXTH`&u&k 3d{W}LtuhCToT8{TٚXHdij>ڢncBM } zr3-,89@r,ABXIl  2aFL "777߿ %u,,Vtwh2:vEGϧDZv5r,NܜyKB,zmJz.2o?؛:U=eYen"Imm}h]ҷH ()WHZ~A"V'E>S&qTBꤱMYɪ("$bu2HIq26B@Nmu4EQd.R־whW"kl6^ei 3c²F1 _DٺSI{&&EqNVǐ(05B**c>)ROEʂ'"掋}ҍ"ܵh_^x""ZY]Wi!Z":ѲR M*$'aʲv4ۧSnraͅ UO^W9UjP4u)C=LjX*!b$c4Dcē[S<.HPM,T}KJ>XRvZ"b=ʒ_gO`hYvxDbE˜`Gl7'1Ս4eQ&>XZR7 _O2 GEd bcm 4m&htXފF>3tU;>WH& 迁r|n.sKws 6aL&Ҧ{ǽAm˜MMb IMI2vEi²T\X6D} n٨ Lr_i%EoD*;Ot>bM21 U! Ȍ :+ P/qT-7ƊDTq-ԡ,Ha(b+lZTemHu/GNE:))7˸}Pߏ_ݍή(~C^|:n Ϻk9a~bcw.=ޏ&\^%l񊒌;g@9ea*l چ.۾F UI4J Mzv6޴Vw`)v~C4dË<];"+fUNĆ cIa?5[iMI\D1 n2k'+߾4e۷悂1 ɶi%[cp%vbɸPuog_q>XJj Z#p@\ ;`KрiO,h-WXk_;9h,\wT!CɤlM&T bٷ?¡$ )w($/4;3n7?S ܾ+?w$oZ# X<4 }D{IޅIӶ)HfWsY#]X7m1hsF Ѕц]١4D()*2FPXHFDlmKȈz-.h"Mzv놉V>ǙE^EbNpd;M܌AnCcr7'QZo:gqx-HmX5@CP$3 et^@%X>Iy6Ԉ9*_C j{EWG\n|3:{sa=~c5ͥը*P_%?}=NwAobn)QF]h͵ 5fgcn2f`p]x?+pp? }#mo+_Tu[j,Bu" .U'Y:X4uRiNEANݬr)թEN4v4ƈ T(ca˄ISH V'Cbm>7H7iY>1$Dtz(zvMW6X)dik&uXD,}k>%XI.b)w3W` <6fᩈ-+3ɲd&c}l'Y`O\lL%zQ!M'^{P_rIVw E#5=:֦Jh 1KC3&#N( c+6)2Hڄ1mH'SW6aեЦlbmYB!i=B҆R)BV!+R<.eyV( {:x.Hyc$ Uj(q.A ١d3Q.IlO xprGMBLB2 :i}wE7R?[ݴTX(Q Lth @)}HL+Ra4EcV`#oU:0n؇Bd,4 TZq,$ |Ö溱 !DF1jOCF6pCwF9?oce)LWHnD(*p4-Jq.]' x2K@oJV `[@eCJ*%r$I%))D[( d 1..Z{vd4-q}E1j|<Ɠ> RjTh8i@Iwr74'U;+/hjKŒd4+TjZ( (٘34wu  Ň/p/1QGLH!eF]˛WVj4!=RH)%XxаҀ/~"d"Al`TeMV`HiXfJ+$%+쀕rGdM+zc\dZ[% ikOLg_y^ӾR-Tt#Yl**7pjCN d͉&V<)J#J&v9":w"Edss|VŕђJkᵼuhAh[}PSqR14H #;dOE5> :Z,m&D"IY/KrQeDԴ43Ki!]ѫ{NYhSYhۨjXTCZVr4 =b]!PHҮF-ߞ%3*T8Y7áI2*JsI%OxjOH[2R8%&os YQմw|5KU<9amCGU@F2fPVuv:;tV$Y3áH2*2IeJx`l2rHݮeXh=hB T mmjR<}1'v HEBuFKc =ц @uh5>&Z0$c<'T:Z,(y('\JQQţ+Zݷ0b 'XQDɞIEa2ڋv^Nb>W0$#p"'TW((AU+,s;bvFYI4.&WH'lD*p,MdqhO _ATYgEذ#ɸ̅$U,)KI&Fy,Ft54zE?kK0IE#)GIz&]r9?odTb6bI7W,r7H10qp]`6qmC#*!Bܑ!"*W-# R(Jk@#0|Gh) dz7HA0iKYr&33X;*7=aYfYQ 7#N&|1(J*ѥ:Ya7~fFh)Dp/HlIHbIkܑR Hek)Q)[Sd'l-QFh(DԊ0x̪景[b?j 5F@eK4VڪcRr\Hj 2RڔipF*S+;PRZJqJ"cXj Rڲ qIjhZ.wrRZJdmJ.ZC fVJ$dcjO^Ɵ#{cguڪcÏF-tlGf RRٔif:YYlainfF t\Z{yigfRh˂D &5 G4L-~TC*S%;$IfQVKK$1)<֊ړPreIDRrlH%k Rڔ443Y VTV {@!?j?@@ReAJRR\Hej 2Rڔ4,3S!Exj%JRJ{աR'N{i˒DuuZ*8iJHg)Aҳ)=;iyfz&-Y[I(ACڵL)fV%4Z*94ZFWRRZJ\mNzpقTV(y0%w[Zd uW*Y+BJArHj) r0Rڔ4ZؓJ]%^G(:S\mWVX$%kJR`H%k)q)[3&253]H#H#C=U{.5[\mh"nrEFҁEfxrW|-28r|-;RZ l-%T6ekn:l]l T\ڪL%UV_&‘ekܑR Hek)Q1#&/[ 1p*N|0r`vQeflnd]"he~c>8$ըXSytRvY[|`l Zml.G%e 𣒲R3hǝ&tf>.8R+KF>EFD|=[+۩AyV傔Z.C]w9rLQIZ y' wOvPZCPŽnUlƊewѕ9DFÓKG]Pq?2u려u"HM$cM@*cH5C*=32>][zhlfuSre˂m(=< 3|m2Q)`Ԩ0!r㔌fɞP$J$s*fVVxVVSi5=yzn{Q~Ou uh7~Tjvl?䃣f0r7͂T^S(WEAfbc AWRȕͲc~{C{&dT"Vw%b}JRGe```defyXq h&b8F=fیw#P[.:v;0pXs"%RBHiSFNx謌n WwX#Mc>88mរ0l #7)Un5c-5a娄,~TBC*nwLg\nWAX ԍVf/]+o BdCFpdn6*%KG \6ףҲRiYBn/5 ݴ.FHJ% PVYycᔚ.f(LRŸԋ< W,Dv<FR7tJ*sK!%PH.H[K26 xcZaL*J-kWҶ)8R,KѦXhE kIpE5JFּۣTi^qC (%’l9}\TF+uRod~USvhWb _ľRh#ZyaCP#U?iuzBj^"8J.Qjg ԬOB4z mIr[p)9Y󯬯*QÅV'uq>Ys,yY''5B8yTP$ <m NH%h #gY<Ԥڜ-[@@ʁ/zi%eHc*S)+SfO,k1ri5m,[F3j5B*MK ǝWTҖL*i֖;`nmffʗVШev>-?BBnelYP~%$YM؆RJ{T–=pC*QK"͏f@rtDO?[^}tzW_=Bu= fCZ؞|܌ޏ|G|]oْ|r9=^r]ջza52lZ ѧ/6Ax^wZѴ'-WT:L>)}vQ^؆m2 ,JfZ4uvOQm3۳U{w/ZFfb߬j&fb[x#ŕ[*%I{m۔J $@ZU;; r=OKn'fECm˪Yt(Xb%o{B0 o<*Ara TSrH KTҖA*iQr7F.j˼v5zud .͚6 Ӽu+khSBDt'NֵFL6*\5D3ŕ=<,$l9}Hm4K*q[LXq{e2HҸ$yC6m%nB\Uɷh2đ7$|QNsu+C *5PP*K&5 7?JrˢܓTRuCJ%)B̝RYy3X2]촗hcvrUn&åDp=J`Ip?T @v3)\c1-Yp1"J ?p("pn!R9$:\G}`TCnhq"R"G .ɡRɱTH*FDv㹛LJ6ba}7*yC6]M!¥95 LrJen=OjSDPr\>I%m 064I#56/~-:] 䞷DR$%e}jm_ME՞{Z۹?<ܨ۹Čp+h#%KqT"9jl~4G=1MϽ,DIQV"b4BfѫQ ,c5?+md=B*'K|7gT֖GjXR 2a˼U6Bv.ylّUfUmJ4ph1T #$IړWu~!7&ӥ&)nԕj Wet?*E;=C>ևJEC.(ZZ𪴀r>%3d/} j* +Yrպu&b gPRڣÐB-%~7]TFjh%-ft-"5bqB$u͗ڰi6ć.PI[t4@JW8LEX MX#bh":0!im%m1qmEr"%G,KԦz&23a(n/4lDxa!GW[Zt4{ j=75쪑Hi?*MKǝ[TF*K֖qT-w/1Y\k3TRWC eI;<(y~"D*S{yQ׫%&i@H]F*A[; h:/Ak`Cڕ1,Rh9"E T?#gV2 w#=<YqŎ}@G͜͏fQglC419X>j NK uﶴXl0n8Yy&$>|)o|0z:8cmXZ0{^c:_R[(INjmNwDfm[i f Emj4 qjOg2WzFnTʶ퐤R(rI,Hek PIjmyS6-^6]P]lC6\ Ik#ȕHf%:61i k@JG#u%[J9V#ܑHl?T6'5]&msմԕ NMV. DRNr'}>8]4𘈭q$;l4ѿHel)!h{dC*UK 8,lYfI7I tmo:!:=/WpdHórG$;y\J H%RL:pV/(n6IÄ[2lVk RUK>j="QbmcRJUVb=lu9(%Vw\FgrD͏fVi2^0ev82մwC^*ձ`l"Dz)NJFqDNA$!ľۆ:Z(B0'Œ&y\ HEx+.X MLHB)W_08jͻȋ@Fw#Yo]Fhj^7@nhT%8#_sꆉoFOWX[dLôE,݊GN:)&%:UZմl|L,xq,x(~\0[ϘD:$xȷv\pYZc:ueWrS$JG .1wRaڲԉ"'xd,@hab>R?T>$$#(c ^}-I5Z`JlYrq.UxA2E .r{oqA;cy8$X#Q` Q}*}9M3S8!tEPDO4JOe8leU,1}+QSYX%w&%pJQ,)3P"ˑ2uIZ ⶃ[-.J _Hm9{ZܫkY$7`T@+b^c&mWk)O"qK">%^}bY<)7xx*B +,$NL Oey,7'"SaJ}*,St>IӲq'cO-bq >!)_v _-R <$$ 5Lj %G6 CO(CD͏{ ݢSvir/8|>|ΩۄEW^rwʞ(ݠWZ4eY;̤.]on,ʦh ~kc4$iQ8Iԩ1fu*uBqۯmB$E\[4uTHڍ,W7ٸa_|m#|u׊n4gӻ~|nq&\MN e/OA#~1WIe.+|P2i++wN 1ߛiw^=|BnaXØ+>;Ajvde~|TCZL2M \Az [ģKb7D7D10iF"fqΔ[);6ڲP"G1; !5CkƠ!s{!7I6LQb bDIkʸҵ%J@H i, WWyZ" Rx,KQxI(I GH 4)h$5q -QY pBHY^"-32JQSQ2|a,?e ɺS13Qg)q#1 {%@0$9 FJʔj )YI $hSE#1<\3p!靖,:ĬHaN/_L?l*[I*s4͌4RqX'xwD.Rk6}WTA*UQh7O?ܾݣmݒ4{k{+w(<<}% D΋_d{*2LP-Yx_OO|?\|ڐ? 7gW7Ç~n?۫/ >嗯~ӿW_௯< ==_0drOOwé=htjOFis>9ņ7Ց/:};|}a?PՐ~d k/@2`0]݌×?.B0`pk _}qk0w>tԮɩnvJ20rMwaw _Wƿ)m[sqytߌ^oGgw;* r/)1wvʌ?+L=p7WIcD> =;MtQ(~tcِ42 ih־O!odײReiC/uo}%Mk_kC=~̩;Du/T挸z=pV[ݰQהy /*;r aܾfR㎕~~⌜ttZN?ipE?5;]]޵U+(ܕN cݤ4V)s j„NNzˍzyʏ}p" ^_ʦʎ^Gq+mmyx}FOȉu#{\{"چ[ioNøKeIͼ^vb*<}18*߿]c/ ?o0N:LXjSzyUP̍ }Oz߄ݷlNrNV-`7QZk̍Ċ{kA"퉼z?T~W{qfN2wg +-'m 0OkH_^Z^2;£nFkA?SLN*ܑTOP* I=~~* Wh|Ox 8#ӽtfɤ ý`Pա~$k@ڸv_Π?yF({_1~B;̸aرGN 0t)3ҾD Aqxj7$1Yݠ^? %E>UbU~ > +>5蹋=[H&0$͇<𖍁^Vqpg;$kpn ޼!D-b-Į=zK# `<`H $3w!>5# .X I,v7vNba aa/\Cz/ EފB/ &~T@,;"5ޠ3`29 *9>Rwҋf܊\DVB{EnznCtGBhϟc}mS\!.6I bS^F^:`)ys26t%uRo=6xv"%9mAk9t@JF#a뒜[,?D”3"asCمq@/r`36^s & ~$!Џ;ܱ0b cx59".99!'0'st% uq1ԏ9فy7M)j=@o_{`Z€WkJU!G!'HxWڛsпēW cwGT1FyJ']Ly Ak+<ѐ'CoA4>4߂I_[#aCe# 3vnB p&VHypԗ ap\3Ï?;N>ZgLϋHD"RSfr}>P~kܛgCtY* 8`K! d<W{mkm rӅ/t( Bc'<0=%\Vp{ك }1Wa(94Dg-^+>5Ba vSd~; |{'auf O?$!*Ep̌Qow! ?YY k FtP8 @ fTxAhϦ(C(z=0nԃtO"dqj|@2t'p0d""11,~ mܕa |t%SZ7 ~)]*0?NЮnӛ\%xC_<ߎIag8`Ǡ# gTœvl3c0ÙDpJDO_MHN@ 2ɤfuk83\`Η W| =X+4X06Vǡnq@`@qZ`9XX0aR$TQ-x9FPچKȰ2"_{2xt։t/A(ό0f>2p%1M-rA:Á9Z8"]@}=]c^c ps M; ? :}ٴ:3 lw(;@ 쪶N(7/e! ^AUwHpg P4=S&FL >/fԌ3>3c=#=aO)3ۛ~C_dO WO`O"`!/]8HDo@PE.:0/p渪vǕ'QC!(8l踺cĴQ e 5=Oמo">э *>R܏ڥ}P ʙP ўP˞P`#B=PE1RH3#3p P^s[+XJnl4SG1 g0BѣH>zD#7nH2{276s-Kd8Ɠ*DH'3hZ \.Cb d~OgてjRG?6 , 6"#sY(aEda.$IA7kfzjSѵsL\ӻ0Y:$3&fҁ?,/wWF oy@w5v&5s~ Gى\)x閱@MAX5G[4(f@ yOɟh*V" wNCmz5~zKɚ'Vr.&OEf,pDD)Z u JB n_ D>:_ IEџ",\7eo.bgsQLN$HTo`IRXqsҟl%yW+zn/]dO_{10HB75e!L( f74qXA9!\Fcc_`П;ϟSxGVOu LIs F ; Lz6_::K"(8[bIg=Zߗ7[vWzkWIi`pu܃"w &3=Mr1mC,"V0``P*oA@΃iO zzN2?$3|8_9c`{@o ,G5BNć9uK 'qHAyV[ > ʿ!4+BM0.f>)/g]rBkzjhk)ݭ YJje^ Cpr< Q? 7FzK_3 Ys0,o!V2 /pDȔᱱ˾?~ՆaK,~޿u]ֶغ=L,Ԏݻi_'wovu{hfr9~s9>辟cv0zk?6qt>|ߝǬջ:1ƙ垈~|K2_.fdлLwzFw*th|Ívۢ9F,U_i63.߼bXOmtY|jTBں+ ATnv8y-ؙ׺7Kºk;ԒT=&];[nU>nY uǫMDVĉHwͣr-|mm30u%y"e^"u;@R7Ѵv'4Mgi|޿"ܶ٣4ifJ"%citF, @x: ,-!T6eiz[[f241^iZYZIXYZ:qW2H%iǐJђ|`iҌXv)7Iei3w,MW?Zrx]gy',yg7ŎmrlQY"8ZjY8Yndɳ37bn%ĐfLQ9ʹO9UF0++䟨liVVj4;,2@*+?|TV9yV QOX%Đfl'A莚lS%cNmOE'*[jWVj4;RFee%BeebŁ#+I2Vѣ,?*+K!XNP͇z̩ҮѲpHܰ"UC$L+U7S13D*3?Tf+I/J؎mp585'4,(y")33GTYdRUY,(JevAeqiasH[S?n 'SG|R!Ѧ O y /q5'NL,%tܔi9?|rn/U$c4Sѵ hH!@*1KGK1[ 9oGaT[akvG[m[cfM1&`E (`ۤ?ã`CLN(Xfln3#R: 46nQ 3RGdX)|9HcđRbIf3nvTυǹńgḂG 66 R)2Tq %V G]QĸRٌulն5m(3Trsj/&OL rۥ Wy`H%ihIZay @zm? ɳ3٫ -vQZJ mvђD]!k-E~ŏ3$.Haa |TzB*=ѳ]t%4wH3Mk\Z,vBFqW (N*iK$GK"i;i00>*iK !uMӝ,Tcs>pz%RZYVUޖҘڬ7IGc{vtl+S. Y#(ohN% p'E,sQ%_n-ӰV:1h3H14Ĥ}|Pgœ&p+G+U&AoG7p H z zu|RZ2_t$ M7b!b-_Z1ͣ |gP9J˭ٮu r:1䘁xd1x&9>9!!ק2rNQ Nr~@k܂fKs(h]J{ JI ,(P|5P- 6mSDZJ\zy`o}\uWClvކ3- μq/W _MS Su]{6=~Lo| t -uhj,<9'e rcηRԽȂ;ԑcko}-oM`1?F_>12;0˰`wwiqZ[_zp/WC/o=74QFIwIL#q+a7>/toq54:í[ z6:)!䜤6gl$d&#ȕ^/<cgpEՖ[Ռkjɝ(G6TE7\ۡ&aW{o-hǛ'w#m%R`n@A$&Lέo~ZoZzΜ17{гm{}qu-AR81^ @2)[Y=o-iP8|j D.D]3yIdbBJ <~֞oT'*ԶZdb) Ӭsw'9>`5A=h8S߄qgFN ˬ/F8y|H:5u2>ZľWHgu#jl) D(vĬ;=5*] !7>sOK 25h$gRj+aB`ysNpMY,!6njřK@+V$VYlkTۮF7t8#},VA@p{S}8-4R7mMz@8فxgMn6!ev38§>5ٗ!<櫡PRfKo;w6-pNVdjAkj*# 2H,D+BqNsGkq*xr{Fk~F*D+ҪJq+Dإƒ;DSKKnwG?H Djh oD8(9G*7/`S,Ozsg'3)a"֏:=AI'gcDp]h,S ט:?e(άؒ $ ɨVsԎGݸ`#@QԚ;&) [)vފ'xE1'\y ݽBs`!6_Qu,D ]리~?ѻ"/c cYJPĮ^Co|X s }K7m.)9 NITѹ9 ӿ, O rF 3ab0hU`G/=Y/WCO.rkvPs=SzJXN[ST7s6h?;ٮ-9<o s`'= jʭaQa@µg? $-_ =T i(t9g o`kÅY)sS6~nGLƭ>DlA˞J8MYjb|zSIW5A+==PX p7sY6MsjFb뗴peex)7v< JnXKt-t@YMhTԏ[->u^θ|cnSc98Y A,_ =L0N#,4v2uOVwv;덍Ҫwch>HjDȹ#g: "drsi3#=l^og+mj'az }wPeoT7f> 2gSr}phaTp'95I:wHR-S-wl\{3lA;У;8r^vm=bR3kos ̶YMi#`*@֊4_,yp7RjsJNjMZ\RWhilI\{z@Smmz@%ƦXŤ$4MuN|l} f?b.4YU$:kQ%/Bh%6dcV=7_ ={Rn[,ǫ@a#HbP! |(]@r?qz}* !r-m./9j ZYWwt5Z ke^ݘ6v#CYuw-ozpA}v!}Q$Ta1>] MpܺtX{+|<4)23Fݬs@t@j?7Fz̔L=Jl$GiucFm+ަp}܋ T,ߵۅt||M|@Qo|S8=bCB(ej(i7HL^]" -f/3lj9zr/k.[dJWC kYU\ɚ8%v 8M,o^r3,=H9& inMѧ']U Gk:3_JnڍيV.gEJPx7>ia#;uN^oMl]\hi|:dq|9WNf|j>$Ԗ'>C{l/8#9;ofj!MULQ_`묝7U(BKxO9dvKݕ2l辙 :^辙}o6>i+ X0:fu% W4j(vXeA ?=ئl)(^=5a/*&n=a5MTmȜp~")DS!I*Ͷ)U,\gnt0Oz܀H[›sO>›pn4YeO/LH8[EJBN~o :@gM;i䐜1I8]z ι޸Yn,v6喍 /V_Fr G4ITˏ5,pALjhn'1nUZJ˽/` J?\LI'N婵 s WL]r T1_ĊV;\)(^)c_ͧ8>_&}8&a6L ̺ŋ;ݍnxz\-+c-k2ٹwcRqP/pW[uJEBERN?=5`\̀pUi}W|ЭĀmEmu $9#xOjg;%w/v_4R/!0r*&ȡ !<ɳoH?MϢc*rvv= ~bҏ$܋U"˜[gb^8K8:wv-9F`jrZSū0Uv3i~)ҳ~Vl~ZliQ..z'm#Q|,䫡(܍88jlL9S0|&ONd ' 䫡 |,fl)AXɆ&/(pOvL0~}9\i*km8\3@a5M/ts[82fXh ._ =Rp˭٪pژƙj'cV4"FNZiPɶ>^!nPHBn7ۀFc >!v6=E`}gxΤi'g"kh[_ m"N`lY6X g,-ܾolVZ EȚi9AjQy4q״i/y$WrҴc-cV%(oUyiy={x$g[ʹu `Z v[v[Ҷo<Mؾc_ hOXZE|ҲݟYڰ\SKnJS[nU6 Yun3'bxb[)85E *%jrڬosDh$u5iҢՇUoo_PK%8D Arduino-no-FTDI.fzPK@fritzing-app-0.9.3b/sketches/core/ArduinoISP.fzz000066400000000000000000000754541272363601300215720ustar00rootroot00000000000000PK8D`uz2 ArduinoISP.fzrƑ.D{3ލ`]Y k$KޕYG \3щ-+{* @7PUã@DSdfe~/?}X<<^~lq{y/ſ{f1{p0~UZ0QWo}rf>uw1[}2//_'o|xuybttoٟdz?^~kׯx cR^̮ox! 8j@}z1<=kWp˻/^bY8ǫ/f7oo[wݻ/7w'[xyxwt}y3oͶ+s׼2Ͽ?}ӿ-w?xtg/ۻg~pu}W߿\f gyT e>\7>cҼ2515M){C޼"rJe^7}p; 7N\ܞWӪWwo/nR//j#埿\D??֎[մl(>V֯<ݻç/O$!ZY ̹,(_>Yi"*57R^, +'FT󧰫X7-_ӿ8dOk®͑/ЦP͹$z#[tpcz2/S߲9Rsz2O\):kM3&dhP^y+x~\C䝹.$2oPQxҢ _x89P-e2or:<;\1yv+&k |wP[?y^D2œfGWYVO< ;ٳ ɓwKL3[ēWbLp4 UrKb4sT-m˦);sD-eC斱Ҳh&#m45C(y<*XD*;O&\p#$kݨ3rII=DޫfU9=#\60!|gȓGq7څm%UțfمӲwLXF`O'Y6䲁8K=3%-m2/y޶a+L6drrvG束ߪ)uxҪmWCS2ߣܪ*aϛkfFx[g➞=|eE"r,vxv!|_rv'l&nz-5m]8=%iqyrT~'(YfxkfjrSy[rr48ͼx˭xͼDnu$hu$Xnu$(\2g\Wa툶p YvQn}ywxOXbo'W-0yE:AO-4H|$mߎGI;m ;s H2Sv"JLn 8Щw`PIMyoh"LoXE;w+u$&i|NL95L=&قO)ɴ=');/@|LWԤގ)ɴ;')?$|LWk0< 3]}SVL8;EsvC%>via{9& &i4|]W;w]=8&́$㩷nNLkW៚F펦ͩ {r7HIGy'@tkNLygujNImutjNL3Ѧ9Ev{zvEBڝFi)ě3%iwfNI&ޗ91;2%iwdNIݏ9AOsŜOsj; sbžv 洄=).o辜v(;Z/'&펾izz_\NRy[ “w[NKng9Aμ`LGĔ{r6IvdݕɎ jwebyʉ {r ovOrbμCVG91iO®vC/./{ḻce釄lPNT擫Зh'Hy)dĝQ(9ygG7;qg޹I{;tz\;\E{M4V9=R:+;}1ӄHTyxG6BrT=iBʴ,yCJW=9˰ޓ)MHvDe7+ojp9MHvDe>dYjgrz)@y'wv4_NSņހ95,z =$)jÜgysrv+ĝ9d0&ΉIYn$Lsr9I ϼMfuh2/C99qOg|aS7xNNޙȬNԛ<''iy]ܵ#Nya7߿~>Ws_zqz]}yo߲'K9^;`]JKAB]猉B¹\)ҭ곭kqjsW}vx{pxoiӧ}{]?\bxomfLJ;8ת.|^Ww?a7v7o>}_|q/WO Bݹ>{xwq.7/3rwnoq0yHô<87h 2vJ Qo`c C(IЕu E1Ļ*( HU[ap+K^;kCyV˭Y= q$dF8kkRwAH+@!ϕrgw= !n+nc.d-BTkY8.JeU*䘀c^$] t6qv -_0{}IKӨ8|ί}ծ=.e>]<\_ Dˈ77wg(}O[o"n3 ޥsFIAU961PSjfBjReٝˍtV3.Ƃ IVKSp\ 3L8ަZCN8{Gd uZZgg"S{, c.#&1(3O\1eJNMt63 FiA$PJa͐m܇>߾g.-@pe ZX6) Å]C]r, o=^ݾjky?y}Jm/"ŏ^geR:6X2^g8b}L30UW[1k.1<^4ӌY["~}̱3fF ݔc;S?b̍6IJZSp3K^{.\uEA-Riި6ݞ`kYWn:|/g^F%iRs2_b).q8ۛ'; >x{S;#y{co?J3/ްKQ+55nЂk]\m`1;>;Ktf>D*M;7^*YGN#H;UKOF1+x)'qh^'mn:|RCՐ|-)an _&pL`y&fo?'ޞDmRpE YLߖn/"t+=)+εc7˴=II,,k{>qgķ[-{Ĺ,grJ>z9gkF$KNċ%eLg-Qq<#;kixzW2GbӺ0=_yEdqJ=A}O<♺T:qݥ';<>!t˂WiEKSILVNe$?O[,6;o'ZvL[c Ssz,eV'VPto> `KDFr.PD\)ۣsxpwݳΡ_#w.C;? ܃scQV\/Id:։ 7;y~i/Q>4ȦFjܻ y f-l2yLigFYnMJW,oΌMZ#YkdfNi^k$~Ljm_pT}kaby;3`D=%IkZy~RG?2U)IŊ2,\T +3,ܥvsw-BܸyI'8߷#=ƔM4oC BHG7fN#]Heu:hʮݺڶ|vn *8oR:[s^wQN2NQ-FFiMUo1rQIkFV'6%`dpj6IXh^k{)F/gFڔb{dNɀ.:N)G{89d [2w;R,+u.=}@9;P7* 4@9{22T |fk=EF5 ԇ_06 w2ruY4+Xfd=H{22t$e lJ@ٖl%2B*eƖբ4P:P#%W߂+:7a5vZaw_>>]^.Vox>jYpOW_axrx6=1?,?o~_I9 YfߔMM^ɲ8ߝzEM|zĢ(Ҫϰ9gELB0;) eEw7ox}O~zx3V0"ܗa /fwO|WVv)ѫ/׋]珺'f%+^yG]-A Vm+ =q;J.٩Guӕ剣A`eQҾٟRYS)хT i u@^1HSASu M^B.VWHSVzb4 TNҨ@<$y &S<xI#)!@X WD*-™3J4Uag2I aY$u)n͑U_WJ,u7,$5(#gu TmÞKe,& IE(G{ %K},2N TF*+KU2!iio9T§JɁ$uh>% $u7$5&" $t@mS#r$#|o Ar"xr*boF QINZSM%NY!n~ 1]z&jVP"w3[dRi"k@_o6٩ZgI0,pɂqi"t {j TN@,$ &R,UXjyC*`Ԁ.5їBLRr H֝sVZ<m9j(_NcBޫE#M]&M,p oZS@ 'f"Xj,K#RIX$uIRKReܪIĴpr;G5Rܰ_hcŰ# ?5p.NH #7u\Mj؟MN.ynA @ie!j8CNhuYchTE$'9;_@FMMLM6}#-9t/&JV$ 4ɷcKhAs|[^$c{lq)4"6n1ԕwat7T53]D ;n++]OLoVU=nAu4-f"K@_QIo6HjIWY61,dgHPR {b PN@)H>HN~x~AP>xx<[_o߶ CD+iv{n 8 {cXJG9o?8qiཀyke.J sU3tY}|sn* P>|5wE nmݑ[$Y EZd7 sp ^./0'ւgeH}ϸ2HI_z/md 2. vA;1,Xڽl^_TE 1i! c"!D2x0IRSnXڻ+#F~ƶ8a BxA?wW?3Q{R?x*lPi&ߤ0KtG D 6 ld4IG" <[?%2&D!$/ "Ѹkw442T1mYMk3Y)RY,#"u\wD^D אּQYԖO(01dװq27jqo盧D7(%S) #J;jFs\@rdsS- ;FzT@‰𑓼hS'5q g2eWA BH{rHF)bNmOj^.Ϸպ( pӺP\wl Tt)5A 6#0^& QaOT  [ps dqA>kؓ!hԌHH?x>)gi銑[)+FJώ*UYE[RL>#}( Շ=!)Rw2/Qr 5 uC,*iq:䡈(و;_<)BJVT"QSԴqQ UvLUَ0U%`3r@f:>f)0bͤ K3![n&xyTÑv li= 8}= 1&^-Z[I*Ƣge)v3iBHofx$ Y(@ NeJ70ryN"$rk+%QpKd_&*N,%d%#ӄ':"MXv@dtP8Ya3CԲ$jՂui.)]RA^>Z4+2 ˂</Nn6Gr7p#f1Hr0f`lLnCѲ\cˌwe*ZLY͈Rқmc^ns_9 FUzֺAY*qfZ{ބHtAhkw2+$fJ mp2L3?=;60j6a"51de#̚ N2,F疢Ew#UZ.odeQ\mD1wigeл(XW/KLe2فlPو h#%4v"cBI9څ[yU#v 'c%(c6IdVսJ̙FF$f`lL80`s !?0JQ@"n Uٯ,V_ PS+1fw`̢Hؾ#`;!KLXGӎ6#d#FDf2#Ȅ!.J sb1peHe4 ^2RF2*d.Εr[οHqD2?!FgM~h/,rmUkv%*}1U O0 ͒DZY0\c}j#<;v8&`lc@V6`cs4ã16V UjګJ휉B3ʱB~LX!m47 HEZɏl6KJJXnjSx2@f# DO^BZ\bApz3Rxg&FJVn@)!n.2ԼX> ƧXWwOOwx~?xqFWU쵁.uE2̍dA(:q+iINNU.qn,[s4 Me,N.%!в$P*`r0Nؐ0i]zrC5 5zǀ>?i\ tt\%Q0IPrDŲOƍ"Im(LZn'Ttr&qO6b #o9BA+(љD}mX22ANn !m,vyEj+eêRV%5URJ$k[F3;KNPMfSIuȁGDFD ܬ9iA]߬wS۞p@ bUiJX2({s(L<&#a ҶG V" =NEdmc`?#6l-v-RGkNe] VVlPXZkAfv04ZmTJFvP5N%ZF@5G3<`R%E_T( ՗B9i ;J Iٶ Q͢yP^)is?'Kt 9AYC{`g+32 9Df3anc2Ybn#k1hceLŜ~pο#GBol j M<0qDƆh%<-Jѕ4ڮGsD6k@ڶ ROڳyL&iDge+i8Ѵhjx"Vkfx A~)Y֕e]6K Z=> h ,KCdx11]@z6&djٸvRYs4ÃP>ZQ{_ZUVFn?Vo7' YjTnq!k 2Un [26D\$eIC1adIYh iv ;5G3<~ k||[pYiV薶m9C>60K3k>KCN | ':N;Ql 1@5G3<`cC4Z>ؙܞH>3!j@I{}ꃞIkFFȱY< AHr #Ɇd4@6`-Qh+VBb.ޫ9Ҝm7($%֜def`2ǁd-|ȽF^KhrF ˽D+Wڒ7M,O|ŤC/ݻ/^<=JbFŒ31?ƭ !9I@­$1="` 70i9 يʌ=D.ֽ YԙJOa$ 2F&G+2)PyU(AHlBGPs9жZ Hjf sRBx WEipZ !XqL8Սhl\ȁ̬9z)IY(eK /KdZa~0,mıVchq\yalx 2vSd6~BT0<7<9#V$ E\RtÀfR+ ɜ#3Y. G6E G %5 YP,@+6i{4 aVD&UZڶc '[SE4E#Qkfx,KFI/|-rxF#J4,iqOyN4M\F ܶcl67&t9v8z {k4I(Ap!MeE· k^TG$4!aj̉[U'e*}H"}vaXn%Q I0r홸ѣEm<I&클Ȥm[ 2!VlPt ?g 'D׶{`mL(эi#FؚA0?rl@r%FnPɑP:yƕ sKJ05r$9m,A6`b8|m|+L[ZmmLȟ;=9RI25R#2#W҄ %?;1DmZs4#&F.M.V(V]hR!4tZhg[)O7MDS U{`1㤛-[9o _J5G3<`pmS{ mKE #k{A.C6%gҲMt-#TK?%˒9$k)Bv 5NX1P GDh#s⹘[haf qO%8!n*j#F{'iZ 11T;gs9 l0Zs4#=ؙ셝Ů_nfp*Zz{Z%ڶVᘚx>%Hv!Hď%j-0FF$Hښjc~ɸrgi5a -T eQ7MDn;DncʩRhȁm A6&s]|]t쪥gscs,2?}^[|맫7nQZ-Ke ?Z\zZ^DrÅΦ?^]/?ow7/~M /~_~7J~~g/ٯ/ϿٟfF z~z뫧?~X|o./@s{2JOݼ|>fqx:췯^-fy?QUXd?ٟn޿vV[lf$/_߼ƅXI>~~a3~a3 R_߆o~>9|R[8rxy^j5cyb_-O?#3#gTuߝӲ4N:?~~-n/^,..muY? ٲxꂏ7|X,n~$8ٛJH@_ZNL~tN32)(Ҿϒ9/d7Re%)s/uo}%Mi_k%'~̩;DU/,撸R ^*oS"9/\C#wgQnD; ׌K?7Tjܱҏ^Z\wZ'}fg˰+Qpn.Ϭ8sW:'̏YrwҸ R[Tq_^K8;s+/7>Y*?щO/Dx}%R(;zۧǭ%u>#g`xR:`g3Q t謡Q* .q'5FJaz]ډQo=bpTxiCtl Fq.~/0:LX3ʭ<ԡ^)@@^Txt#vBu7{-k\6Dni3* o죴J >kĊz+A"k?T~g{qfNָBA- $L|7Anq7>RWV@eZN~لqIbVm_eĮyďŃWO;հsݝS)%"QTȗ+\}ǹ>kNC#DNtsG'b V5֗ȵ{mbƽ?S6xah ƍP6okӛy3a8&ycYce!/tC_`komYEa[6ݦZs|@q#OW}ʼvi?to^zHdEF@RJrx};7" p#^Z)n6-ᦡbΰsnvVӮl1hʹ vKkhk;ξ`9Ӯ1VҦmKȷLLJ  3,V9vN\t?C s뱗GWo(q.ziP7~) ן[+ts L2z-_[no-S ڂ-Yhʢk|+t[][ +_ @mY TR kK-а8b5 Ihj~J4j..3b.0hKlDx]zc `qM"kgV}8yef㩟7~,o0p1ny2V ʳ,kG`%uFےj tӾ6 a,3 «Op^X$$~{8o]˙ , sG)qR.LI8WcjٞcxӞHhG+&^yJ&X<ܕ5.@\7*lҀ_p"b)og)r2t<&`wYh_̓)b~6 q|mN Q™,g*.;`UA_UP}Ou+-ÍG+ ?%ykzt󮞪8(E[Ł1dCO<*,(n{dxxMn.}~Ϭ()xA9mAi:XSc%[=a]Az- )cg`YyCcq@o `#6ތc &{V] 67bp cxpHbv.1!&Аws^:ޙ@&5O z=0-a {5 #@ϼChhz_/mG+bw{`-cXCݕ* @$1=uV yx!N^hk{ٓh?R_#o#qCWKW[AMa# !o9[#>ΫubR0y{ž;agޙ>)"]H-EV"ZIݎ]JB"ݍ7GCvT0 ^q.`-7fo`^=Bz#L@Tį`kGV 6>]O%?&~ Sre=: wS 0 ;2Z7'|A[Iq ^E<Ζ5q1KmGࠄAQ͛0B"p2oh 5 "XZD*r;zH;`x909 yUvL .ށh__Á /Z#?%R%| ֵV~-&wuh\8u ꗡ27`u) )xW~i *qrXJT^ !y Cfu4˓KO?848!KRa$,lu xm2@#,0 xd^4|`0uFgma-V+4VaZrz꫕=9 qxΖׂU G3# F| ~9FPچ*KȰ2"_{2x=_P`28;8dJ!c Kˡe 0p&x'4{4l :*e>Aǵ~\eXמM{C=<πMk'YQK^ ]ܺ-Sg+r,-YpsWMY0%Mmu &FO)TEr%TVBe&&TPRF&GptBb)$ dW( + %?7RB.o)SFLk$_pp=AJ"S7`ǹ QXfI {` ܏zV-(![ L.C| dO@S5)]ڣnƳrvzdkᑅ6,l%> 1fB\r Y#'LLS3iatNVLVV X&E]].ۍ㙆2!ݼ0i%'?2Oϱ*Iqp\hn ]sOhbVDjb+2XpgT1TۖW U_H֒>+u y XRaBHTtp[e`^yXr0AJKGӿ]}$S(sVms梬ŕ9r.8`{,K>WRqsVl{,KI.+jf/] e/_{10¯ 7)>#kfokxe=CX0!/~,!p2H qpu5gk=&wsD4ΟjO+b}znT6E{9˚s9̘`–SvB00H!uRaiO z\ZN2;~HfpHr3 ;Xj@3os O {moX+tv.?SWHO* 6av&5[AXK]/a7~36" E_#L8WjOߵRj[,C lM-`3Q#׳^}i0wςK p0Sdu)a'3 .ϗOL*p SySмCwu3!lSAgu' nYd/Ow?۷Ջ>^]uwk?mֳ*0p{*Ӻi@:5,uLTsoQwy<|G9gԝ˽`/9w'to;V(=naXmz7 X84 =.81m;3T-$xG5%xA[76)X ӔA{j"O֪eM1Q\.RfZmS<j|fdbFlZmGP<m 8D_cS{jɩ֨S-9&asu?jYyj6 ZRjkYКj%oC>^Qyd[F5ժZ#Z wȕ*BD`(5 vxn{Lj7G ix_\f"d6E2t.{7&57YcL]6k]L^TVzGCT7G4r 6,/ؑÐp- V0C}b[RvDYl'+eֳ֧6P) #<ޣ rfBԖ̝>uQɌOIʩSG288.Z2BĦO.?kӭ QTW`H}OʹE,9Ҝێɂߵ,iH;=>waGQ,r Vk&)U(ojwuC9Wuj9#Ή\٪"qBshKL'LĢ;4txYT<^CR%SCg07nIVĒ$J#˫̒=B,{ m-j #Q9rY* 4~zώciJԁJtpD7{edq{!$sOFVdid>}t<HGCܡ>Aajb :*eQ2jlM $XkuD0ަhgkGfkn/95HSkJ<-zdx(F&rYћCBXG "FEMhЅтqh%Ŧ{z MkfF](LBg ~TR ggwIx8NJ'HӆL~ =N:!eר./ZY#FDzj:2U+uԘbܥ)ydj}*)O2vi2M # 'Ҟc Ghixqum=NuGhpmp0A6&6w[طn_Zd9fȚLBO; ei$X"),,Ix lx}jcaƩ|5p=[7-a,`Ao\WBܧ6&`9FXY=j#kwwW2RY2 @*~c 2XqLmAt,Byq.A6& S[15 S3XG@Ew&ErݭLma&SS1`Lm,8Y4&SKɋ;t!21QZ(B梩5kC9q4Zڦmw(n&xEdhnE@Ng >6"ioޘ#3ư?pGZs4cR@mW=K ()16?Wպ?i{smcC i#I Yz{V5 mDZ" FD"&qSkWLXe5mH+ؚF ;mspL>qVWēʱx ( %'oSn@yii`ܭ9n+K5E#ӷmx\G#^7n-zőQYxqnָ e=%an#B 6EkcY[ښ+p9,lW*OZG3TVAdG>4 UF06&6]kم #Hֆ5G3< a[#lԊTcW{QܦF 6 BK쉽&to%Ci"d^,ݸf2M@n RQd#}FeQ2jbq؊#HܚJ07rE&"S%u*8owm$EYcr V62QhKm>9&bRA!Gj iڮc̅F0)XXӓ%-diX0u -Y;I# qCjcbiv:<ʲ#%ڣ\ZC$)㺀L/Icd^\%q^s4wϔan\};Op >w yd-lMp,ȿmVaTtplV&?삂nh aH0ˎ]cbb:@.-u7%WNc' _ơ,Zǿ$lbBضـNP43& 3@ ;HA?_% H̚D0)ə!qə#Xitl!;KoDv6;+SagR "gci3ia3{. %{hq,mxAP,ms,$,2K +̮dBXv+uIіTvwnsHF13Cw =ǓQJ#Q܂,_D2JƦFq - o2Eh膊Ex\ufVIM {6G3Uܳ)~2v*.Jҵz.E~i28)nP8U}Rɑا'H?LbU*fk:d6)BZtXI2MnN//p:mXHA=!HRsZFI a'B)uLf$o Q&yK!AtHRFB䇃 G&lr/a Ԟ #Fjk%T\ QBٹ!PԜD֔h:.-Ĉ^h070a3`=M }g']SkxcSC=PC2΃l4f#!jI}'AP "Hci3,c9Ԫ T26]$4.)#t^ÿmw펫l,ܒ.tYzt>G~| w Bj:~:·2!dk`#aHOw{f=,z>.+*,z‘܇3\Ð!VuYƾ؝݆-O(X)ۤ}J9> BpaoRV@uQѲٸ)!m+C#;[ϋW\X5@+8q52z!WU#pR,rX̐ &6] nP( BmR>> B jn`\P 0Cm4Pa \Fǧ9 mA£@H+D6scKS%;Ą O28c#!2qsAČ݈!+/ë^2pױd6G3<`Nfc,D\SsJT ܴ؞-_e; 6 Nz3ym[ y趆'bǰ7)v 1뤣LU?'4LLߙUȂ0 !=d2#gʈt+`T;ax2msFoWhkfYp80 tpLA`cFt2|ӢW+NHim-rqRcC,d % z(ds4)FG&l2-0D6H? N2| lw+ Ǿ!= OIzTNM\tT⸇}Ch$|)OW$|K,M28cPi8B}@6$1x 9iB}ȧZ*$r25郓#oON٪[Sڍ9| 7'6F2uz1O 3s(rF؝{z{ŋ֖ .-|\$H4ZFwR|-AwJVRѪ{7qН=VHN/`DPrLug.P$/Zс#F\ rJWj垥x)4vU1m4!$9ϴ9+HGo#I_iZ&&1[bTSIH^hLD0ۈ8mS4EyT.[BeJV7ES"Uع)4<[I>y >IggGFLHbD꣞x~tx ;JkJ`-mb% "Hך{ga"r+$zBQ!V83&rʉv"mKm:v{F3$s{.mJEo# ;MZS^ǭY ^wVe}nC bs4@7x#wtU`XMGdp# DNڔ-`='T`ٛ4;:{rDNmD&r!9&-bRD*uCfnDF390tԑT6}Ã栎 ئod/DCH$PoNm5S;1.2dc9P]>1m 0ɥ& #HpcNܖA+1e_ʘtgGVvaQhf+jGuf" $w-@[KȞG;+ovj;&(%L?$KX`^Y]34-q5;"MOYѲq:~7sc%gpMJ6bFmt%K>}p2Ozd jVW:BfUUF2rS;@ -"@ͬCd1:BQ7,vZG!MQ#K -pE !@遤y{^>Lo i8)w@(\ IcAHuKkuw @G}^։39KWؘ]Xlh$0f 0Av<vF7fPō&WOtl}8JKcEvJ_k#|n$&*zff맷ɏ|ɯ g.d{Ҵ~#;:Z'8ZfFXFźg늪?]jQs؂\r?gY#iI+yzL­bZir7g M >-w/K6'[8`]ȁ$BSrguP,ɪa*aY5j F^3fİ+/لDӤhA"bnՊ@A] y"/tw!AkF+¸Z%M֯ymO'r`Vnix$[Fn:lms^P%ѽI ֙+Chl7Dߒ݀:\u.Fдɛedž"8Ml]w=gg~*RCI,K=#-K$$ AA#IVsϠ ~:H^[-;*UH5' GhLқH _S(V.1hu=Jʾc"TƳqzE"%+PH4.}4V4<4VovK pcHȷBWݯERԁ4L݃5:TG;goK%m^Yh\j,PttyHl<K7=5y^lnXq&a|oWJ.=8vXq2)?!Hs c 5Gу4aE~Q$ 5>@.}Y^OАELɋfT- Yxʌem=mjC8Z3DY9Bq.Q6NsO? @ D@|C-{ MPK8D`uz2 ArduinoISP.fzPK;zfritzing-app-0.9.3b/sketches/core/Barebones Arduino (stripboard).fzz000066400000000000000000001215361272363601300253430ustar00rootroot00000000000000PKt8DQ}:j!Barebones Arduino (stripboard).fzkr#I&Dh٪nޏnzTudeeo A`U$&"s9 Q$k7:, 7suSUS{/?ތ>Ogwo_1B_w糋뻏o_ߎ_.>LGw?.~A%pI؇Kjt} w__7fꯧOFx/?NN'wo~?o}5_oo_U_~<ϓ7Sݣ7_W/󫷯qFi{~jt5x5Ji #|z1nra:}Ż^>>\_i{5z} hrsYۋ=tۇo7/ᓳ5xM;k(C'>=N~#<Կ^^J#(Q}xE^Txń^\lL:^XxY^xxSE^lxqp|MLÔXU|W_]-%2K8"G$DL&f,caOoKL^Lx_\X KaI4,ɆEKed /:bË#qHq|5 YHNVUW_m|uipiPiLiH[i"5KŒ^tXaQ@,JE(#5񽍯.]_HUQUQU1v~b7I}īxQH7]xtk.^8*)Zʢ,**Sc'<^ǫx3)bqR8XV,+'3+kᅦ?hӅ O.Gß.\iN"( \z6=Mg`3a4KxfUxń^\%`lRQ 6JQ.BXP#QBոֱ LI2I-Mjib0KR1Gy(k̢ØR*Ő S}444_u|5W_Hyuqmiea]$D4$,.KcO-\'\|nŧf l [qi1 J5N]U|W_]UZP ӄʼny3my5{yv7܌ؾ@4iqgYld~7M0ӻU[aytus R o㌙__w xӍ0`G.C R$3UE L2E)Dqc=O*'n=%Ð5?rDCڎ1Ş={6A"Nc*D!M@V$BCˁ R˞٢/~:~¸Z*2H><[\8mw$(f*ן;-S6 ~𸭚£jqoh8seKr9voV Yqj"50t]ΫV$ EQ.wGD+=K {[6;/!=HbK95$He8P(B/HCMX4b7g$whF+x@[X&8Rѣ lj2A&ߚ{F$^;LQ1(MA3RsI8N=4Ɗ g=91f+fPu2Q17%?<|ϟ>>{̞As?jZlf@D e@4VvN 52L%vnQPgNF*ٍވiٍp]ca2ym/r9[uH;+d,9nVtTc3rnkYTl ]C7bG a%82CG9| 1TsYurḣŏt6%E<ڂƑ@E>etTsi;`E;];ў [mBd; ro9a r!!A s܎U=Gx2Z+/|N1O*daV$#CKX "}*>ޯɰ;Z&odXK1|xd`uHג},HD橑7=ə ek]mt{I~lTC5X0 cA~[vCZK*KN_[b_J1y,LUzS(v%s)`!t_)u[Ia#Oу G.%{vu"@]91+"GA3Hsη/V =fg<e/fDR2ؗ9+2 >#L}AB]F/YtR;@VR ހAf }1}1} \v֑7בo̬72aZ< n60mzj)uqR;WkЛ?^O{OI-9xDrKT|2ήě#o`Q"֦mTZ>"`;{&<T샬=AqsnLqC)uٟ9wԑC3̂;nɅ#7O+i:vaz-П$j;6N`{܀+=I0aNCN[86 }`!]`DI`'c@`U;]crP(‡Lcg9`{RTNg3L^X2X##wHd@(!(^fPR.")Nqf^*4VcCKv Bǔ٢\HVvgD͈ ;+ki:dmx}ygmo- [q3Le`۽ pʜzKw&MNQ4AL&<ѣ?&+S|!ڌA"h a8"vGj3raJ -5y `HdnNw'f3џp @OR@LV˒ixQ9YP* -3}bd2Jm7QirǍ25:_2u=par=j=bN"D;4m/a#{sY2нaIjF E&}Q͜+_RCwk*d2Se8T2/| 3(}A1˝߷AbTWSd8 S#dVta8Зa8s8Ř)yn̪c$CŜaT`M%@-a 7#?.Şj-rzsя/+-ޜ}q>;?\|O/߾ t"/t4 2js?wߜqxٝ`rwvp=cӳ?ϾLF7>˟W5z~3FYhM/x '+NBA+իDS6)rp;e,-:AHpEs>$;鈋EhNߜ-Fkڡ]Z(,6S񈙕B\[=-:\ɹ)UN+$p9w1JLjU\sM?ԍ w @E @F MQĮ_>3Ї4SYdzfJcsp>]{*m+OS*DOؙz2H,yo̵$rϱ$KI D4R-3ώ!x«綯.u*SM͍n^ѪwӇ7w>R^䓎ֹ}d>9~6 >.$3/_ mTZ= %K^燖?̼6:N߾ ɫ'cz'KzU)%Qh10{gm_NӟRbox@Y. uDɔ5 7mp2v#a`b7a<= ;^' y96{O5DO7^g}RΠPU 6 ][os3;;t f 0eF7Ӳ>0)ILlQE4V4G 9*+AeOW *-FPm풆U@`'ZFϕ=|kZ0}nnqo3nk+1|yľ%{c>׈e\o{NϗOg)=|^@&[h}+7BQ+a V(r0f2P) j-( N02@939VDr4m4Dd4r.9!?r]r4h ތm`5%x('. q4>at9Wp쬦sIWjtň GgY1-)YW#P.Bu_!TV'ZEQ$ L-ju;LZbP nVA9 DBS]{/,x_0Ku\Ou{X 8كuʌHLZЀJw[SPB(3: 9?meF+ `$U A)in*`cnFS6O_Noa xlL}2vz7۵'u8#L򰫰ӭ;; u׏Q}TU"vk'ZQ1 ܉V *IW@n$]e2`NM]ӎA&]?4$=&]IK3S8ZǧsaJREǑQ+ur ˜6[ 3°vU$FrjpJvGB<>/L3FlD>0ܷE>agyuZ K F 5.Q Ojf̯I󯣻/WϓO/Qh]j=՟_M?^͗XÔ<4yz]Ykg#e":T[dt 2Ea\osqw?./>^]^m2}Ȩɒ쯏^g&wg>f_|HZ?}LJRUgtys WO-Y}OO4lC:a&;Pgґc&8ab}<[bCphBσ6~?^_}|=}تJ9{.jZrkkI5Fu?OPjoONFa)3{Y3S3snmtuCwsJGs ~ z՛u^GEm4Ѵɭp¾ȍNvƉ͈7arx9{ݲɼe!%@΢s?{^"]8yV &:?Z˪7q@ .3bz)]t[;޺|[5l=os}؁֭>xa=ծwcn3E8kHnlc=0L -`hq ; aӚ%xf;y DX?Zcv«C+CIOW>系N;"{LH bJ;\`D>q'^0xi+)z[tAqV5{Mt8]BY 4 "訒UQ~ЪR(w>EY=f:L$f ']ů!:%q-IQՙĕ| 4T7ХێjZm` ]T FLj?ܰN6\m.YSp*v5sbRz:fÕ D߯ b!w@EՍ`rC4/;x L/@"^M3V!wjj~@. 7LVRMOXP*C{z+nA˟W,;gu}7z >.&) Ո3П*1 Z?gD#PV7od xqe(]kp7=/4To`EǹN_ބ$pA΅&j{ d-:n'Jwj&Z8&F,.!\1* q`)If*E#8h |]hҩ; /.V t:r.*Ŧbq; È|QЄe7(؄zl p w{Q˸GϹi|9O.'n,Ǝ'ZS3lsNyk֭_C~w#ٞ߮3c@MvfA\C;e~0D;0%ZqUԆs"{=g$QøQLIi&ZdyUfwYOOdJaTq PR^F|$H"J5%$~`V6A c };~ƢAाjSϭ&U9E[1δI=#q%Zݮ&"3SrhPW㧛7͛+a ;jEzI"P~} DhCPGdϸ$\B*B~"݉VCs^P(Z W b& !i3E,`u/Em=jVCc噻7QOǴily"LcȗJL=/Sݨۻt~,5ll&|2rabX\\(Y_ꢟ}yֿK^~TeQiY(AK߶R BpSVÞ>;I$MIOׅ#Z'ώ2[Jv yn=TXevkr QR4 P+?u|YI*C4a3>M,TFw4M* wm%/:\]qŽ{S K'ǎ=奋<ӁC(c`W,5 Koc&\"H فR, kly tf oߢ䳛Cªϫ޽]v8lP柉Tݙ+M[ a̕J؁ Zw4=pe 5'VA oDTfx$uHL"ԉZw\/Da}Q7@ 윊9uaY(1q|x-yL/6+%*lPDOi SH Ii ۛG.7w?uqt{}^yK\^^gK5.&^&}dזaOKn@a*&.a:\'t=C6kK&p,<0QÌ%}V9r P8%P_Խ37xN^QB%dòap  (y]"G!`K@BS6"u(lUXMmBB%`òap #ABl=䢤=m< T!`"/,MXQڶ{AG!`]!l¿¿K Gԝ6QӮa¿ֶ : 5NQ7f08pͨQXQؒ ^C\uH!`kFs@ߴ < [v/(+8Y& .R5 9 [`.OqqVYB֌ i07x^QXWqL0 \`3j%r$`Q*lmKDSH,T%h6(`EAe&+ElC  u'x?03h"c5 !-@j"-)0pisAeR 1 (vFBVd @ 1;N1[ Z]}̃#f!%1@D2HG2D /[%L(8Y^fA;>Rxقl%U1[xY⦅rQYB̺%f3QYwRق]YUW>iv@BVd @ 1;Nb`&fQ<8bB 1[bud 6 28{̝6Gj RaGa_k}u'˾tߐEJ0Qׂ}$%1󴍖KND"J!PWg04h r)}rG(4 iH8RXڶ{H!eɲ2 >`!Hf)C) mP_ܱvX" Qyp,]cOfUQڶ!Gb݀217d07p1g@Я%@5EY x;i8i=J}ɾmBS%_ 0 \| 8 Zik`|'BuMQڶ!Ga_݀Ʃas.B hpd_(y^8SB3V.0"c y@;A)l (~: t,@M 3!U!$%V'yhQ%UWF>1̞v̚ū-jfD}̃cf103c((=n=NqQVNRܹs> 3{2v̚u'L?P3r )lLVvF+qTٓf%L(̬8Yf`003(pomTV!#nÖ[ppـ;ȅs O{^bGtL^аmBBºƝ@6@F6F^!ʽ@"cF  /BaƯ hAkgJ k \JQBbޞNܔ Î`uG` AmƐSi],La3r5ی%*T_4*F+ t&JL@L"^L-0D1 r@؈,(R>˲ׇ<8f*la%BpTZzE@Rf`iT1EӴRgݡH 5hB%l4pPF͉yl!{+\aՕ2ԇ=xƔ2}+p2pGD 3[v/),&F?J9cH!g rP'TIZjFdtl^A2$f ᎸXC+%U%q2Uh("Iz(0S0L )an0yU\0&HZw@RZK2ZBGYt4.77 qLxXZv4FgLK(Dm G C[v/)i !3lQk0RHZC5O$#/|XbQ*dhC #pOJWv$vkk$u"'[7kd` {ɓ_fcp+4D:͜J(Kq``#.`}giaaj#f;Ѽ# +4 n#j!ChRO(Rj-ɠhU#.s x%.߂p=JC&@]Ϣ>34-e^bW -D9S6ď>;N6|VL$a3tYs4G4[j۴DHEYeDYg E 401,tVёKq[vhVv0M3# ,LW.tl2v"[ N{Vي5ڄImBz9>pֻ`v}ċτ1ĨqskD7ף;C@ߡ_(ta#+ EJh7rҹC3- !%Y;)ɢM64#rOgE`>(<ڹsOw8 9[~B^Q"u"I2c၌l/kQXK2XUAE J~*KbX舋%dRmJeBǞCn  "lB 7>N6R d85Gst)Kj6tFj"` hrG jy&Ra゜+(Kh)-| "fzg5D`GIc:Nb{e/v-R领$NV9TA-ZBš:e'(\ܠ- P4%mB"Ml>ݾ}5h%ԙq81+93q(20 PioN &9oFƝ@n1\SW،u%TiUwvtˉoEnaU)"J%reٺ'ջQ(q~XR"gk$%C9fʂ .FJncs4GZ\;cf!1Pjext}Ӈć{d kv*98ܠSg3z`1`%Vb`ph:@ jF'j]ШٚI<]CU2>Bѳt)XdRhk %!xvM0\ a3zJ1y]n;r$ {C=9`l'*ȒBЪXtddaEˈ 1X8i(( A g3‰DpB}=hHu6_Gw"w(\:_ӟ۷w?FOgg.ޝ}÷[Iw]ߜ}|dϾj~{#|N./>_By|Wnr;}~RWasOշlכh~ӳH_nG|:~:ft0yj,^Y}f_Gѥr7av}17?Oq_HJ߇k}s7{j N>^ߍoO˿SY㣛C@d^7I.􍞆d}ɣ_?\|p~54MF?ί?fFbϷn4&ǫAaa7O;H|K'ܖK^|3GlێMFW~+9??ޝzyѼSwͻ7ў}q".8E8㻀}OX!_‚s(7bXٶΗe4"TTq©.˩$1 s74Yw. j,4dDVhieKegwމtw<1~܆ϮPӟ[5]C< 4xPx5ǔfb߬cd_4':S&[YMBlt-[ͲwU2%l }W~"Vfv["P%heKuc" +l M%h[8>.;eꒉ'>A\OϬ Jݞ_s9*ߘ+Mn'(%Lsͦu3D uH pxVmiRU{kYZq8V=b\r3{3Joo~#`N擳g,kʢH RZB\Kˢ۵_Fy#ާ~q?Q7 aPzzs8+ƚhV!QZ8كZ(jUݹZ]>j-& \,bJc.92}ݾpb*"ł%ܫn8' P(j`q("DQlW FF7ÝRCJ +\ei`p<4' &O]4ʕ&<ÔN5?% ΤRzc5QvΚm%ڊ&Q M `oP`/ (OzV+1\Рnng t >XqfUCzצV!̪GX vU}\{ FLf!oƝz B,dqNḻKu2xO>(;WUKڣZ[#%HJo\R.3kX-U 2ʉC"Vt&w5Ǎ(}7-񳷊Zv6 >#G\+6A4pcIgMu&8LpJ_7Y?C7d*g2$6v[-̫"֙Wm=֡3ڼMBַV qQN7ϠHan!Ian BX3m,,Fx=]-̭Cqch37 W q[vOT)ĭ@9Y3MBPQP%q3h7V)McY[( sPܢ0Z̍[R{Jn ʩR7nꆊL<"u$-[0 SEvDk)f{:{62 3/U%P7pͦPhbxvHfd?MCַ [ VXJg,ʽtxd sDk8 u|Pv{\db3|! 鍾 HQ"14Nݭiq.-7#a؛.o6yӫe j{)~ E:DMPuJPN58ΰz0ބ*Pf!Jl*u''1iV:{2jDI`:]ۺ?J䂬!Nps¤Y_ط[db/gv$V6s(>"*-36aspmٺ”%m,aVeH%ocC ,qoo[7Hao]"Io k-{)OOL%|lJ--;CcТnֶ^RX[WpreX;6]bnkf)K$)m&D;U(̭9\[v4H][f mƀAKo,u)JD )17Db3%doC8L,Q 6,,& w%ݎO^[v4GpG0n涾^R[Wr̍s%MlܺP̎E9n5Jܭ9K w Mn1[;e_h)m}۽0dބ*7Tb3%oU"+K?J5R$J3SmamE @ kD kXRx)%E%- .ڲ9Kdn m֦AKam+u('T kC$ k"-YFf\^H[V n B2chvqpmBٺl:S$f)K )mj! ۤ21E\*Tn*Ѷh*g&n涾^R[Wrʹm̖h&@ kD k[$Q (\RBJebؒ$Tl*-;C--36msm•ۺmۢ6\b3%ޖxl{lmPrDՍP8 {֒22Vl+n k֖]H[BYHR{aJalɩ6|>c6(͆zNQ%Y kmViN6fXZ57^Zv4v!k m Jak*u' Ysp5,a3t!|j4q5!8!IJ0D+XDP (rY.?˪\- 2u@{PڛhldHGqau$@H5jA GwE0 {3Tw@e6ʽ\ յ=+K鎂Kbw{26DY^=nN6 jF~l,йQ!Gԫ,]mrD證[|vY} Y0-\h3hHE [6GstG`oPE.Xɔŕ#Tg7CS¬eL7Z-^Ÿ9 q kOC;!̨QYBBy׶ :;0٭ka3P2eF -݌V%_a3fې@U 4w FnOCb\KC۷Wn*3H$:εm;f1|B F 9kI=9OTUН۴ <n4"  G\_GaULRH?I#U~- ")+VZDJhs4GBh(. F DEJrhE6>SaA0mK~&`84 \GBRY*gLؓ [r"xJ&E&F86$Hx9ʡWU =0C$EK:x굶^QW7q/ၔlFźCj29c \|\4 *ġ,\$059Jvt| >`:m3Ɲ@A,vV> AB AkI=A0:"zar"U8搙F Yh'lGgA6͑`1G ;[ on" '.La3>֢'JxDQ5Gst)%ڕOq=h*[8bP+\!}tM[MNRfuBkX2$$D6HahbHJ8+)c،5Gst)ZhPCUHÜl)͉"[H礪 _ܺaȉ/O0Bв9Jr.[wP`GGJ1w)^&ёҚlmAT%K7'+\S…tfPBmWV>kʭUƵEwCx^U(bu37iR`\f*9#ތ]z l̨6B+brl JA1.60{WmܫX}؃}WC}Πej O8*[:V{JBP?uX#ȥ8cIOq1$r@s/}{ S;|#8ICqr&< )gBt!'tX 8f(F}lI=)3|u; lE2+ZK ˆA}l}`Wk\%08{d1P `[f*h;x;6\eCM0!TXYtĠkM\3#yU!"v+"P(Z]=N.)>z>Jh;)Ԭ%Ԭ:}Hat68rV^XAvCF\l!H_X7D-ոB. 3[3?ct`LBֶ I MEN:&abqеh%%$W9HVAaDiH&U᠛tWԩMD"yAG\nU2Fw-ogВẒw AW Z{c_N:U,P&\< [UdBb95= z ݤ}۷CˈQ1:0B1IΛc9! E ½-1 .. 2N)Az型5~d̃nr;}jv3=^mϓOӐoo_S"no[B; o[,bgD-i=ժJXy[K Bj[8a&9}>5b(ڴ=2-WR*Ypx|rS}k!zjqZ^BÜ&-|޸r\S'-j!"6>-eΓ v!KvSU\WXVp"uJicRq Q$8u r8[D"D $M۽PCBZy1Y'M n".5fqWHZXJh14CS}H!f9JUQ՝F!dGR9;zcؖ!-B%HC[G? Fj*A #VV&k{'@Jm,s6X[hsSVm$\AMDHj"%]:kI\M՞<\V,a61]Q{6ٛ]RHؚ9:nG*-*/Ib$Zʳ5TݎZ{pM7Ό&+q0qcpfJ,8[s4GGBךtMϭ͝!io(MI,ΝFJV%ZU*KCIm m[3#qHx6sbg,AI9^B7!zڕ۸;HakIJRd] _NDDma\'7zD*J\ B 9k0RBi v&{fhUMQjRu.,]?4oP(Q%()ZÐ BGhDHiGBӚ9:Z 5&{^ݚd徒@>JNhʵFCʦ] 1ٛB̆Ѐb"raK,3k"jz4uH&^(m"$:;0DN9Ƅ)Qjt %$.e(mb~XT($1Rܪ+ y-`4='z\Z׍˱40CF{C.0dtL_)lHu!'Gcav|(h!%VB4(4QY\\<4d~ ԇ=x~V9iJ-l0^ u'>3+гcHgGJ9d䂵86$fڨ^Ўr9Ja=@(0^ u'Є)RYs4GGg?^OV =C>_Oaxut7}u5}|Poo_S"no[BفJ/)N9Mn#rOvMZƵE?ZR]4Jʓp8n;cvVo7p.eآ`xrh:lN}k5RHyN2?NUԏ5ץ0Pb#I/{h=CB}љJWf=8HB3Y𵓽Eop!p ]Mgvqj7,~ ѫp* }^Չv*U^ƒXyU%5 KSiv4xxYc/tQ`;R:VD[)t*da3Y`4\O ctFUvI!Bޚ99 鍵Cqz{*Em־Dha(0*Y9LxO#@vp2,M|60,cjib1.^#e Rqʻgǝ ^z@>HPML9Y1Q)ͺWPl6Hm EZ:* &*W[nHu!h'_q/H!kLJKkPR/8]=ӵn,\C Vol,;MTcxM˔`x*Gu/@R(/BB:D>cHgGN.fS%/ . s=Sn*쯏Ӈdzˇw3o/>$b7N?N|Vx=Rb̒+E:6…|Ofi}ڜq!;s/;j=ϵڛmls+1]$xpjQeGrF?Efe;x;mu*JXcV cvǽP^uX{Rr ^rX{Kw ^\ 2{?͝񊡰W^cjE^X<7J%RީbnJt^{th9cE+3tf8wɦcXq$/| &{H6K"`'q{1>c#g[)ͥvz5jV"]x@+"ŨU5 hn݇[8 ٵE@ z@y(Cr;7 5[q #P6[nFK1v!%?;zFmז"H[Î^ޅ~9T4DLjKC# -T@ve%<9Il9/[0R' C= FbYՄhg6*4PZde Wc;"J!xB[hH$G<0 htN!Ca{KJ-63iY<[ sieC0ijV} dWm'$q/Ɛ6{xZq4a~Zs4GB_J8=\+]yx߅uDҺ4ȁ SsDGfçpD,.p1X(D E(Hs(8&`sұPP63uGfc1^q~dVk'pYqJyw/}vq+Z\΍uN?<"Ͽ&ӷ\_̯^>On>M8a3:ôn}WW%RP*vT:ӆ?^M˟ffyx={KF/GӻǷ4_/>`W{珣?7û/gg.ޝ}÷ξ_~y5svEdz=L|ó_b_6;GM&^Eba:ͧyh4~a>ͧ?܍Htkߌ|C:ͧ뛋hj9Jxyifly-;ёU#a[ƿOL&0th~xɪͽ1ӇUKy?>ux}n4$~f!_u$1kn|L53{MsWcA7HeB;S1,HNYVP?,4ы~**{NVW&&6NWgLױDpX0jP.{.yI*cL/QOt2|毟Woq={ SlEEIeCxF.6b6|!mwdgϽXq1U_"Nj?zǭuz΂_לpkd>Sz(tDx$zШG . FE|ZUÌL~9Ow~0w:i=uSEH<85|c^׾UC^-!+euc$^䋿.\V"w5Xuo+5wν˿ntb 5sr&~ L0ixEu?##Qf4K?38T2tJǷ?tq2e ^|Ÿ|^>`UD8Wx=Y# Q#pdoٮ"br`Šk"%T-SWq$i "\+ⳍW&I|%mE&̆a)Bybi4LSn+V$$ $KO-ۦn-B= " Z|I\\LzjR/;*lڦuА0{^=Ǐ‹DXfqN q jy^=x2ޠ}v Zfp]T_#z6f!͆STA&ZR`QS`[S`a,48_UajAUUkOпl|[ѨijUbf}XFGX"HqU0)NiX_l%)FdQJ& a* nO/\GNF lqMV25֗(Lxoe&-o?~lkS/q4aڼp\ތ:,v $XX{k( DdžaH |M5YH7x˖۔`Qkٛ(n_z ]&="xHJ@_E.Ft{8MKi3avbִsӎLxmmʴdӮ1%[_Bĺ%8IDljahqd+ . 5 $,Q]mĹjL,'$u g?P ӯe8Ekź {? $*sc|s4xp~﹟_hjAݢQ5F=Y4K/R`jKr% b)kY Tm)൥V-q,f|l$I :LgӨ1/ǣv%F鴐G{Ⱦ]4"va&l7ACe|̫O,Q6"Kdwj D[tҙ"(]"E<|C,[m=N5OJTW(=SY9Rw#*iUWqGIP} moD'1.sL&%;*vGL3RDXS 0=yŢJURD,*yB&#"=)rK%L\}f:Q,QӣKF`22(J%8u,$ᷡ!+>ݒ40k/ 3FX'<\0NA!cM0 d+]{)+`i姥X'|!,0,I)#\ܥ@6 ay襍f`H4(XC>Ճ S0`i3].G{Gw @fgH G kN ?%Cb>Ιc=$Hh;lC`YS#{@@k# 0xO@b]N}SY_bkG mGUЩ;(\pѰStΧR b0>5&Hw|b0$ \eR _x 0:1x6̦["u{[N۹> |d[1䍲_%-߷ eӽE;4MWGDL;Q?ZcOȏH].z(}]C.z7ݓ.FJ&H4,zŵ](? h !Rh!KT2 |+`;"=n{(OūL/g(t¯L|4A5^$+~5>U=xf@cGbU&ZH~(J 9@~Då+?hhAP(rcPO*N`^FˡI0 ~M9w ݨag붧GHSwƕ!`I68Y08H40 z#ѳ&a`A}|,@kSXX!|v`|5+d< MB?1>0hGϤpIC}$z0e/hí ]Ph ZDGw ..#NJnrNoߍѰLKUay@[%")zS.iOVR!B<lI?rm1:@x!+H D'g:#oL,I/pK\p zm^00l 0tG4\ \wht?x7~*[Ъ (,$9 ;@/~ 8hJiUE+QcA@GOXڋ+\ [з-{zd<A<[ @mړz7^[h \CPbCQ2{hE~c.K*XHdzh=%!ዂq?xAV F݅D`;; ډE`1[kϘ@PBm> | 0o1`b rT4^Hwc($?-PŒRn$n *.hAIXh;jocp&KHf|.5߻Dؙp 33s_qǞ2"ũ$c~E`w4p|`0  t8lB1QqHJ O&\t?Rj+,qR.B3 }) p6ډkrGS _chh1ch@:Dh39Af f0CXKJ߿/]Ec%!ѽK?=rm ݅*i>W+h\aZ 9=8&ni7 $&܀" Wq+,.fԋL#Xpq&;d6|tЛ(1E_<@JcJ|NQ? 2` e @F 8!w_O!7e`c80  y$ B/@8FxMN}bw'lm3,/'@-d_ƆG826Zĺ ~neQM'h?<`߃ۂ k='S[7u|{n Wf388pdĎ,j 5d Nq.v0PNOGM%[sdct}j)G҇]CݨrP:rZ^m!OtکnOڡS:UQSNգN}щc™dF3o L|lD5w D6K+FzoD>F'H=pt=aJ"dMKAfX/uҨr4UDEډv|g28TѦoB+Eb$@b_`TnR2mc5'#za&Jh!FXJA}I.]Eߌ> C?cg]>-DzhWFq}ӌ( thWe&UA\L~cxiAw o'27f<GnE60ܞa4h.\4\lqROXx%q0l',KI)kFf/]0eSFdtAl5#=C\01^8a}㸃 s(R'_zSNzzx\>C;a8.%͵YN~/Dg5nh= a|w۲vM}۴Hy1(*7iܧFE$s '22a#s;"p(ty\Z0]/di݁&a`\gA;G*!ghfxS\`~, wx'0성]~'iHBA 5.Vk p-My=QWuEѨW(I~ 0=!)R nM m B0з={2pAJ28qK p4z, YNc0v2 rQ }?}1=6Ep@.?Yin̫v~T[^}J?ӷ/oǯק_~}z~~{c?~L?'55G~M )n}yZ ܭuR~Vor*'6b4X 2ՍfݝQv(NuRaJn-zm=Wt6eQ}Ӕ*҃jQ.t[)]cw[c913EL{Tͅuk AJ¥r +S!|վk"< <LWؑ#8lUnb"=mr:guڳuӗ#=UM?VfxSiSl<"K#;ʧ8b}9bX`Y(|pIj7!1v$w};"$;̊-ӣkd+9gvźʺb}xw}e ,`< u'!Na6V-vߺLL>dpOW"Kzd9vGUC![}ztXw;t~)|֠s)^}r<^)Nrdvߺ ޢ-'bk-cOLDe0+fRl2AXIXڸݷ.Ke0'ˤMUeFɰڸ7&ն٥7E,):ZOC%}cmj^T `tr+MβsqeguJK3K'\+"uMuX=x5*QVf $$e rAE- fΐtD9[IBZڳ˱CzBKА+mi>tмհ)\9)'k&©SZYyR e) rL@Rg5T_mz#6>ȪmM0k֔HV:|5\Jz,aV$~ E6L5)t9>1JI*9SOY}Uu)#(Mt D=C)߰^Fi:&qmIcS*8]'r;Po Y#t%n+ۮbX-6;$BpPoՌ޴5V[Q͢ kČEuH*n"ߙrMkO[VXǸw WW U=,,M-i!dEu+v3t+I7 ,]{=8 AXv R֕jvˁ%E8XًYsbޯ5n}_f-d\J%’8@8R$<[n<U4Aي$$VU,VM[S',Ӧ]>\>7RBS JM %- 9ɶ8lM K5[ $,܊cۂs ʦ`;񍄢Cj _7DXM[S$|Lf'CeX{2e,mZk,ђZk8C8^^3aš4J8q!w-n?lxͦ(\IjmCמN\!Y՚\9igk.ז v߃P: đ8GXԖ$} B UX6Rщ 8kIVk=&[t ։q‘ąHr>^]^/YƕG G +]w NUpnǽ%>|_VnQ{֔#DY%9R8E8$A[@Jb(#r,VM[S)v MWltkiI/&$`+a{lS`W#Ԣ aV ,֖});DQt2+Vf֔+823ٖU<*=K3\\Ve֔bjTb`AV,Ȗ] nie9V,Ǧ)OcӮIgX^.l)+)Q1tH0;\RY-lqv 3PbovW{)*aqr]v2̢^ a^!oʫY7=>J ]{^)Ndfl-r͡~kRt6dM6޴7?3oXOt5T ƳnRYӚc տXgX/I{UFwꉕa ӝXO[S['p6Xu>! 7JU-+kĜ,b!ZǕs I8 +nyd$iI*B^moRCF(vPjit%qeczLl֔fm t,3YJubV{fYø qۮ` Zq\M4:zIzg8 ORJx|Ƣm7j~_k$5ho5ՊWa $\˫l*U{b,s֟>=꨺.rV9mMy`%P(m]i>eeo$DwTMpL[&;3)afHώ^p%Xm OXq])hitJp#b$EzÕCaA~'> Y۲Tĺ.tYKy2@kʓ|pTY%s.B݌U3|aic ݸ7Cs=~ɒ+עc )N>aIwҟ7a!n˵ mS;6ni#0VTW"BeFKeQ5$/,TG,qoNH8"ը֣:xH۹{ngjӧA{Vm+HTapm2 gk^fV>_tۅ.=uZ).V}s:-wK50EXg%qߡ5KÛsQQ(%zh9mښ,Vq*R6VtC3k;$+KX, #"$( 6]&ۊuےkf]"FHѸKjOmK?$%$۴5qmkfntNʴ䘵uI+n2 =i6U"؊3ۂtH;AB_&ƼVVj8>F9\;tya6mMqp6d? ]EB+^IJZRQ-d]bV ! Xjy˱6mԅ +H::A;aĭuEȃ=@- TuZ]n9+V֔785jX ܤ@uf]i#jeeu2niťGӈ=OZ|WDX.d b8L8737WY%8̗1Ye[SǕ;.|MXg %9ra791Jh65۪ymښLV0X̢j6@~.A_plgQ1$yvMc'mMuIԢD.V{mKybiDQvj x14s IWsκеgk9uok󋃘qx֕˂upI35ƽ_fl1.H/<,=V ^*Wɼޮ7^dJV@ a6mMypr5AU>{2Ƥ{1`fYE9,$y#S-ν!"5E,93ohoU:hZ4-N˹GXy5::Nϼe [;$+FU`gSݏ$Uw9p"YO[SK'dtܯ`TezI43;HaiݙWXBZF^)wIśܗń)S+.MSrHBܵ6,b)BMmvC ] Źx>ŷ`묘AU>ts&$dՙ`#s*uzګOH±`%+v+zŁa_%ڊikX--]V+lkcCDHkB`hAOkB5^i?OqrPZ+’d.m\u_&jtuZ'u¸ɷƜ՘?8(X,Ȗ 2*l\*t]%fp[,z1f FVcT-?Y - cAgSURezp5)WMv"MUrfy/_Cxǧ}~vҥ[7Mo+nǿ\3,dsm_Ns>aѓ?sUSNZ:ܙe O9)L S{o$T;5ʡl&lU++!n5*F9GE8%jK8Q5.L:%<&jcҭa)ך_ =~4.V"ד|} w9^9#vFM[.j>Xc\WbUa#ᶔ|y-Z(w_kӋ#0sjTĔe*aH9ʋFí J;#1rx]T6ڙ9u'ЮFHayZ(]5~KQĶ,S۴5IAݮA .H8(樵L%+fyre],8;V&,݊ۂP_֧"Ωma6mMqpm&b#rHΐ̞!#l2DRLj+9vjdLnymIܫvIN%I5OsU m '[(3!4^ >lcQ[657Y&ڊUے\IY"c۴5QɒmanR꺲͒lːw7Vw'Ƌjr+ɐ"ܲ)I~pr!f}5x+N x[$w-l3iU.'@BٺNy^5:2n@a7mMyF=GM1!Mov5A5R[a4ڄ-:t\ƳU:_YpVx<Ԛtt_`k*GbuIrk~9>tҝ׫i页p_XO[S_:36+o<1iL#I]6ۓD y [=iM !98 F-͓U̸7.Pr߱ͮށb5WO -z 1˷ʁLwZdd)Z\ٔMld}Ymδ;D,cMMV&,֊.&abbM Bwݚ Ą"++ϺmEC{$o8L86 ^nrjt.-/uːy`Yʀϸ7v{u| w>dL|o17͢_b>F#P-+ 4[}F>_p#a*2OEG,`dI+o}s=B/`vJXjF}L+in<E=UN3F딨.\XFaq{oLjBx!DHMv )A]5?8TYө+8#*2E'>#GIcLuÒօ &ߺzsRE8"٧‰r :0;3CE1I(7Jh9gp'W@ m9p)3 P_-}PKt8DQ}:j!Barebones Arduino (stripboard).fzPKOfritzing-app-0.9.3b/sketches/core/Button.fzz000066400000000000000000000352431272363601300210600ustar00rootroot00000000000000PK,8Dm&HAuGU=AQcԐOx艑A2?ul8scvIǃp|?~Kd7Qڹw,qr3 K}A|;:ovb~w:/9Gp<2]Yg>oξ'_vׯ|6ټ?'?|^̯93ژY:^]Ͽ9ӱ4sW)jڿun,} c%b<4\I!"۳rE«Q|u׿* { n7ggz['?}{{]2xۻt)]\]??k0@]^qsz 9ݸ*q|/Pͳytr7x=M|uË_Shu>ufד?LAd^jۄ~{5,2ܼJGpuzӟ[̟?!zr3OϺ?kN><}7쿙Rvo 'iP~*p8~y]|dShz_f ͼ;^zKkSmϭ`"Kvytjg<8H?B2i~kyo07BϷ历%E8ӫ7RJQUuIBjK  b9Č6`r{Nx ǧh7X Q6xy+jiaw%t@ivࢁ?E~ cEТY#͒+$'سzT8Dݳʴ*$o>/P7o ŧhȏ-XEip}Z^ X4RާhO;hL7Xh L3b$,؛fYdOp  +!R w֙'\uƅhiyy>Y1aekŝۄ5-'QKVңc/=ZB0; } ד%a(ik𿌢$}$[dR7U>M)752ggR<,99XyRMJߢ[ )@r^FB]O{<ց !잡CʘVg : [)q^>ZZ nCe<*)b h d(_Hdr{Cjޕ,ްw-Ϯ,əqS&z>\WmuU !Cl7dn}jӮh⢳mqp E 궰[%xs,wjA Er|߫zfn倕a p[4Nes8?Z ccy:iYh $ˊ}24ado<49 4EbɁ(-ܘ LDX# Ŏqn24Ul܌r{qGANxpyq%`v'&j\U ZUDPE5d.dʛ%$ήٛC~5mi53ā]#!Yɚ -'۵qIDLEZ迖6Oeb)R_/2h쫲9ؗ:0rU4U 9cab-;h']\2% $rNuЄ5>Z'8J^9Z*Ghoy >vrVDN% :TF:SdMFcp$ q:^ح%1Ө3bvb3x6= i/$-]~G!0rE:ve"WS"mZӀq[y0 YdM}S6DƁױ5OZO,Z?_%i{UK{H&GIYAGH|~Ypϟ0?wxם?n2:)_z=U1= އ2LzfXo6b~Ap5 r ]{ ƿ!_\t<Oy^?m>foѰ;4i}o*a}W.S\ӋN_Wl9}nb,{z1Lo궚Mz5G v1M^o(WnL1[k&ߣW5􉊂8̯Ns;J~nv}~wYz&߹^ Qg8w;HU<㸃]VW(oZEzs;t!`O ][^tԟVme\O=TzUw$ z۝Ǿa{Yjx]^r_ث's +\E 0 0k[&'o7"Tp*0ZkV0̉wu h*0]ԙ\iY|?0y^{L8O ׯ|s/Lipߒ֬g ݆[.|\_7*sjw*ֈv-D[Zfҧ8!7ݞPF(5G 6]^K]~H/ G o4{v%{eJ܁Wgz8YW >_0B(~\I(لI"r]cvhLfYXrirjV$:w6ײGm+-'|sꤴTn`Rjiv(_|Utnӹo$&imRi\ g}t 4e/*Ɠڽ(-'|DIK&-fqjMj/H?wuT|+W1r+V/1}:.7dNheRޟ]ou]7}: 'o1ꛯ旑8?8\dvPw?txaWWY'ABRsX#U5(iȋηht771Y3d:<~wbΫDQ%#xͲE-1 si7`9 3}n?;Ò8[i!e6)وkK%F^D*q]/() g ,sPIm!V H0@p,(Ogn"Ck>*K)[mp= .XXS{Zt\6r!V4s'0ki,O'5Il56&N@ʄVX/A5>`,TCv7H2Vz@]G(/xwJABU0 TxtKOsx**"z덩7]+%ݭċk!||\(D#@Np@iQ)6xa~` u\c4TB\}+$QS%D'Z^ZBcVv -6JRdmfr5(M ,˒ےBdU*LσA%ۼ[ï!w|@c*L]+bEIynk0#1, +>tł>K%Ԝ#8Ȣ!W:_R&A݊7n= _"rb;` !0v /( jHxEGhLJ+ЇvD0$ NXxob0!?p]%F:Yj+~V BEW߿GW8>] KDP9"8M@N!ܨ7ÍznPr3Ñ>Wvr?ckj\;^r>kIreİ}.e"7L"!}K @0 Kʐ(+q/P a`Q8? 6/hfz[!%?@/`6Ae}=.kM[ҾȪA%M=]htPon)HJ h 2S`Sc t)%[1W^^ȗtB[.IgթI)74bp9E6d_1ۡۆ!5<#l6#Pet}kW'T@ɥ @aȫn,AЭn#+yrϼ=D7/[ҭ7 1] D:O^ 4VvvtY]Rf㜢!h;Be!C jN8ե.IDI gf#]=I3m=`F< Cq&2bA\Щl̀D }P=CB?`@9̐QĄmA 6#R$70 $zv'Hi$!JTe؄(d=}!AnQ&W$?0 j 扔 ZޠX DפDD'_X2%!'䖉 9jbAM0 vE)2H @MTJ4NPx d!%CQ =*PKƀ>#'7G~$8j2hi( 'I]ܵWB#U҇;+BAz%^}&Qު؟!A/ B/?CaI,G0ʣBjfhv"LtLCYi;C!(I;.Psժ|{Ugk+Ѓ`TC,U{g2D%0.БI`}--DL1x#I?P_"fGoC"QC ]#nHQȊ  57} ;")87!Cxp,Jc 3S(&K$4P]~ )ȧ È}I`t۠+ ).ƌ jedC OFIA;f#j( 1A8vXjp#aw&m! 4Ǿ#z[Ѷء3r0=NTڷj+E*] D?BgK@<MQ R55B$^# t`Ie ;D tH19FS"ɛGOBh ŮX҉eDlKǑ8@6Hr1 ]BO:r5a %0<$H$ _3 , .#~l`XE.v'7EEC7*.B[4Y.B+J"!^L,T%X!R1t+84W2dL%fMjB3H]%{'HvM0DzBYa,?2.t‡">FQI)_!B,?u mD&MP:d9#tsjËq, "'`h=@BҋB]bj_kS:AΤK%>9Aky茿mtm$l gyn.?EŒ=eJQa\1"7Gc:E?2 PIyDpׅSH-tVꠈ! $V `D@0&m Q cy׶) A\ ñt4-r԰=H "kF%x1aXs_ T1B 9h]Ǿh:WN;(2Ut;A(9u A@KG`t8&RwR0J8!ݑh04_F%5%Z#C#9V٪(3tsx'=9}bdQ!V&)I7R Ϣ¡p\cQ??VB{g,()HEdrBD:BBn]-kwШo<-'RT\*&2%O(A2'&C?VL*֞"Y\{>. Ecӥ/t)rr}CPo3 Ժc#%G.bUurD*r-AN2[JI2J L 8e$GK/?5 yA(r2A^!JIh,/4//= DHQ5s ݈B÷L(*P$Q7!%mapPSgA(Fh$۟"2-㣩a4Тe4 vg9Ot=L;`0EgtEGQ I4A)23 Cb_q!)E1(=OÏY9IZoA3,Hƍr@֋t\0F6X/},a⥖,U)]58/ȞhNh*L#pĊ"-ڲ</hP&b-EI;A7 Itl)(1AP벷T<د(o4пq4tq19l^Hzzo+2i dC*U8$3( k*Ta9X'M"ʣ]jH|'rF X#rhT͇ bj~Ch 'X*{0:$4Hb D%H-hGGM m⊠9!3 ]{g49h  //qA2":?rX.u=N,:45! ;PC3H98* 2GQq ?Z6NJ7=:0,IIȔD䆑q0YfT,'iWVRU˷eM܎NVc|vD%;F1?*1kXE%3F}>VfXt#N.$I~]olyA.nHa!/;;)⵫,WT.2狆+ 8nO֊M*4֪SV8@[;$&4H"+.’e&0ӈSl#8MS>(M"(eG\: 2Qn&0%E(C`vPNHX=AA;{rA^@fP gB+{BHCO@#XWZg $"h 7:kl _#F$zjĦGr(tm&ؙ}-`N0'(xEU`+iK-r:F )䀼4,|4C&r3V4™,XɈe"4/d+,Q' 0٢ G)-ʍNZSq6zRTQḚ\qHʃ[i!(8MXqc5pmP^#+EMJE fZPRCYI㬠TP^*[,2K ^8O*TDP/ 5. F$T &ȑ4pLc), ڂLn2e jw^&"V2мG <0D1(-ԙB5R=BO4%MQʆ8(^jh4J! aMc98#l5rUʋJ wi$,hT'܅7ƃI ֈ~bep#0" 2"[",Ma\F0o\ C`i)TɒkbK>&#V0YSH?Di3Y۲aG)cy 񚦖2=9y'`l(#Rt XhNAX5Eh&-_#@(] CXɋ nHj>͐osK檯q$+O" ]Ӽ|?|YDb (nop~#Ŷpu5GvgoIykj]KsT #h |9dzsi.p.-Fx 9Ϟ@~ƃt^V6*KD_U52\؉g6EfVI+]}nRp=(:QvP[QEHyvZQ.k^gbZ;{;cWUx?+yCNwvFNTXh8>|/9ҡkd5τ{r;ۥ M^9=hMW>pc%m OQ:ȣ¤ㇶ6,bUቇ﬑ סTrWٲUbj‰h*Օ骴"zTz.קѻ=~hLUl>5Yj0X;S3Hy%aO( tsq"$72`"D,W.l{*8?s'h@̙`32vW!c ]69/2?09dq .JHP8&I= 1g? ` s8"` q7_;ޅ\n{ LEehnpQ!lRm"j Z)G1XoJC@ej;N;N NX_6QLK!9`T>CS(5!UK)([dpHv"ڞ9DV#g?`-qD-]k0#l{v h́Z% aEڑ sFchlю:l9Z6{hϪ=|hV(Yr`J*X?!&MBT-;Q2<6ɳ=5- 9fd{/vZ. K!, 6%g'\'_Xе\w^;Y Pd-jTL|\g_ۑ^a;Guny؆ewaHm2l2!%S wp9.BZJGiê;Z^q鰼LfDŽJIBZI+)Kxl_KK\x/ 9Cv ,9瓛oӻ R 8ڻVd#~+C[xSDe`d*Nj6d-.YJҞ tlt WwQzBNS:O?ZI Dl LM$lr'dی$␴5򭵒^8F.=b  f򘣍}< X򝒟s2Yk[ ϕ0$̟0h*v2L)2f\?DžP\('cg'AȪw? GiA5gB A \a//g(ӧF<87gxqa] g~f-՚O]l5#I5[tO| n s V~Ccj]#nG pc psp\1,[Ff+\=nKfZ*<>ON\g#DŽەZEjx(Aझs09Z!mayl'"<֎ᡣlhWj53ds-la[o(AfVK_n7x fvcҜ"9nB .5:{k4YEC=y뗴k$(8[rs)l|sմveyNoYڡ׳f(粒2ۿ g#ĜYoca5C+ryVEZV(Ydާ$ǀ~vZVУuώ Dka`}r z7(VZsPiO2;[fZ>DT CZhig]VKӨ4OسXP=*PK8D&KU Button.ino}O0W-K: 4h+M,&فk>$GFk@lUV ZjDmj(-l 5FJ@KktpFz %t5 $,D 1hS\v9YX֓M.:>i ȑvhJ#WEs KNkc`zځl{%{+XBz +{I veG0wv&W >6]:'AK]ifpkMA7'U1:eR%/Z]@iR`bS%E*=Ee>T4m;C(ij( _eS6~PE #kzyXLGoV"mZUF5eox'd?+Ic-ZEb'R }AE'͇y }7_h8v;'}'f>z@f/7H8m%Y/]V/#noE(~~T VinoV *=;zƝhIxE?E w2~>5nP?u*loSU[b0B<PK,8Dm&H,nzE j=}իy?Ǘ.,fO~*A S7}s)_ͮW37wg'3/zuf>y}{7ǫūWofg~I_>^_<]}JH߽]-^=}J\qJGNWFǕfg<,.܇foypW} /"o_>~>u}5;~{񇇻w_/n>ONj9ϸ{7ڣtw],޻7~iw7 xw4ǫ_nWn^8K]/KZQJa_׏y^b=VW3?_MuݞS!ܡUm@0\?J'޳#V_jogg!w7;^W_?YOQ_o +SovsyP"~vqnyB! ̽pJHj+ )UۮwOxynRPw}jN~X;|/ܟ.f娷pvqԡIXgͪ#nl{ rS@EB ">Qiw׷Ozzש `og-P[\?w˻c,F6߿_?,{95xӜhnVSr#R)¯N{8dC'W!=&;yn̴ƙids)٤Cδ.骏tJp_8w> ww0}VP@9%ú090U=̩v}W>7_==_Cj)E!<8APEQ|\8/Ād. 9E}Z|z*fwOV]\?޻7ϳ_nq7)Ha(okUW[}pģ'|mFe(Jl| F8W ]. (Fva! "TL K\hQS>q|`Z(y7nT=`?x3c+֧:Vr@M4Lɻk` JxDŽX\n h7hv^L|0S0E;Viv))4vtka1QZggF#eg9<V 9¼aIr0#̸W2R#9iDz_t>LF9g] Áj#>=0L- X /9v-̩=9jԬp`,G# S$72F .Er.O%TɃy#2ݿݚ'b*90۩GІxN/Wxz3-M0q&C/.je3]6sc<qֹYHY.@:U8H.oDI.]-t6bx rx9^gC cssmnMv b-~??"*Py²ntaztBcʂ70i{%9bsb֗1Rl80- 񋻫(ʾRBV[phOPF(vPR]#9=eT$:G|n[0[wG?/OxG4Fz;* !573'aϞq qV?n>~ʱFʟ'=TܿWN ш_^^o|3p7gNmʃpO`akl* S{D)j#ߜQ'~uh_.2|HMG l6pV3bfO0J1-DL8![fLp58CN8~]iέ'.MFĕvI:pvX_{]w]{yɃF?:h L!xJε,! 3;s0#)va2N'ZvvI#X#:.]@*l?-ETnnyۇ]1;^rGXJmrlah1b,|*aY!M<'tb)@҈\%&o٬-t?'`)7Z7'%B+% *bnHHa.[6FU^l|qgBZD)ZaC"YZ?r14Z=ue.7 28Q0l֔a2g֌o\=ckfn|n֘,QϸwBw.xe osU8f׾30zQB҂;4|N p3 Za(Vˏ!0J|2^|·$B7wo9m=7]9)m?qXѸVl-ɻOph}o}|N"_#P%lC .} `նTΫ6օR{d$Ɉjz:vQ*cOe$Nt7eIUatY =Dgʍ 9ׅQ~HKk7el;AZy7aN&N; -G Tp@y5H.ANlF8ɩ*5bFL]bkzmTBgIAGO^R߭ ܺ9R9?v #[}{=PS>h9)dNl}t3{[H3ĦM%M{bǐ1De+^c_S1C|Ey")t-O$y"'&xI6O7BN^v&jݥzJlfmpR xTA>26Zyy`a54="urh8w`4>]虽5/EGLؐ;YY@% F{R,)R;uEhX?׼P[+|naTZhM i0 Ee$:|}W) oځq=ºPh;?QB$v+HcBKgd-Z_\Ɖ߉xDwC Mѝ89PNg:U8) ٛK%z#/Rϩ>cVF%b׋_f-e!@x8e0^P6"ۥm.`5lܱn~]$ "iMz&!IPB^ܡ#04aGi맍>h?bi/~pETϊ7y?wﺀT-&m%s^hlۮWL/V=x]u""8=*@+ꥣjD-+-?rrzK]0p S}]0be C##-AHuym g\^[mp*d?7IfFo$$K'vQ6Jn {Ow‘8 rOaхk'?C{ ]uvul_.2|зMQ ~uS ѓ$Le?}EB}`BM2IBiD͹ 1RE})ߦJ{mvE W)u<48؈RJn LX7w)..Ja5ٙ_ N'Mƈݯ#܅ Ahmn#c)#8m9< ĸ?}h"&.q0˘َ)qչ,rr!0WR4Eat4G~H-"8|*:@N4A\' b sȡb5}E)2ZΩ焀+׻)zɁ#FD&)`SVL%hMaSC "div>Z 'EI0)HwSzV(V(hvo %3+ ]Rb i HVp#Q`ltoHtQ ⥵O%q) %Pª5R]N b8oK΍ FS"%; Dz:_BM|b|M̗b .6|s_&9e<\GOԸ$U|gMxtp+T})-WtXJdJ}4]dZ׻dJ0}]¨?# @S!BH}E 3!ݔh P|T% 133K mê3K2x2մkNGfPssŖSc;͗GCKaL{tmvt*y7efr؄s2q@ v-%U>ABa& )GCP#-SD))~HF@xC9;Uj:޲'>%KJ+S`Z lIOE}U#cZ,Ɵ5Zabǡ 0RHZco.F#BKta^EgŸ UNVJBrq x֪թq4,Ңl y C=)#-zЎ9I8 f+tLtY;4iVՓ5RBjtY 3Vx:O9ě(}{%X¤U"|ȶB ɜl8Ҍ ݆0CEGZaXdB6`XId'T̎\B*ERnHę,8eŴ@C3޴=uILu(/%}^ AD6iWI0hqy0e&lÿHE&]FNmH`ɸI^j\)gi/(V=J6 s_cppm$\ct4"1 (XWͬk`VjUaj,t2qK}ؓ`W>U|쥆㇨ѯƾPf zb2Аá( ;QSk 2ʚxA;nd":ޱ#VZAHLU˿A'B)V\,Spc4Vʼnb"f4G3:r0q0J tDT\7+YٜRiY ÐېᴌLBY*7؜V@Z)>5v]  Eu"6 rdF· ivȩxmHp5r;D[n^k}cyfÞkZVFBZ J9ťhꃞkGL{F^GZ72[Mؑ++-'bM +Ϲ,Uy@nL?vXGRt|d,{1i%E $Tq8$%rSUnG%mS1pta }y!mg]LbT@}]u8ZڥWh[7̼+2 G|wUC;2 9K\-.Ł3q/!R8ơhiO ̼̼;b-R+K6{9f0ޱ"xuѬޒ Kxc ﬇9yJdWhzSOOЫ{z{իm W睲(dEifkRsAV$l1OlnI`cڮ m‌iB΅5wȋV$CHّp%\=yqV˄J*ln8n>ceDL=;#(B))$f\BAa SKI Brfl8n}Ciά( 5䠪Ъ,$< ehǏLφÐL6$s8=y簭oY'}Z a !H0*C((ʔ rlY8xdA>#b*s1@#w8"1!( l b0e`"4+% *؂):TZԠ ԇ=9.iV4h`q 1K Bʵns_ Ǻ^Lrqiv,lu4F^9ViaEhhyQUmBmJd7UY vCtnUXq(vr1/3;ri0q0ItNK*2LHbN8܎kdGZnېlBUY$0HAStjva  4Fk,lXk/B 5v7ba82ڐk&ˆf-5Leyn1Tm3aO5-N&110k{`Vqiy'ڡX+,txȴ)}7|@[=ǧfc H#q9)9E>8#|Z(2,2*|Q:2B d>1.Eiy>^㇣`%RV~8nEkBk(vJaʓ#1q̎HZ a`d>6h9q ]BNhYZEiE|݅>6&MSƨ8jI̠>Ӱ:5 z A#XM؊*(j*Fb8CnV L.8=A}GDŽGdxB;dR6 7)NHbD923[M葙نdgf.+ 6غCH{q1\L+4Dػ^Շ},.63$1Ў& $ϲ+chLVG;d!)\49l)-r2:܀,dL3l"cs8Ë7~># @B 8taSAigmtU}Г瞛۬_{eIX5c#m@E!E?+Vpܼ[ zkƥ6J914;RJ7g0k yMf;2,Vƕ ''Ʋy9/&y3BJ20yhq$!0 y4DPaC& X FۂB[=)?$-ЎpS9ّ9ZqZ+&e.&JZT!f%>K@ީq3͆LǶ"&}l=zd#-rЎ9Y:# a9 ݥi*8L4)]  aCaG`[}rd 6 j7m1#|mHp5VoT.Rp+eb<& OMbM8vv>Ѱ/cnh  EFGvqr3AOE0a;8@;Xd:#/>(l0E4:OsZp d M|4-85yӳD i !*N:ٝ$,󝓌EJ-R5'鎎E$)h0rm* ! ȫ† ېl]Bqv;O7T eb1Ee| G2|bTV ŎSRb 툒8&MͲC"sh6[Mچdjr\*ivc"V*-Ѐ'oÞ:= =RYH)MDR؀q]1{iсvku4CF.[őo ܋qW{Nt(x-*lo[M?q)=6>I(E IJLKr00r2 Ž6y&ivȵ!HNmHp6rrH]fEUqhz\8e$)фvY!9e`A u@]0Y}buaXv8dv:K[%b], #,xR y)V܃1-RP=:)X0['D=3+8 P=3mJzt,B [ "đivYmHpFed!EsZFl*-T,mM7z-*h8 OTL"=/)C_sh(3=P2=Luoh)'_hLVG3xŊuʃ-Y QC`XGX[ׇ<9B]<׌-{$iNlX;xw}FZL-rpb9xXv 3VGôAC$F&L(XwQ IJKrq٘9z'$-9:# NԸ,@1L7WQ1ɦFiQ&8;KSQp C#dQ5;ڣĒHB;dF6 7#G~EA#mHp66l6<ej.&J 4AIIYbqYC3pOhL&ea#vBҢ yhzǎVTvÌpՇ 9-l,sZ<'iq) oDi !feǮa5%HNJ5CI^R6 7;{*XE")22)ې̎Ll J4gx"].'ȑ$%nЎ#9M6 7;v#-~9K:ޡ# LȔk"m)i&i&<;QSQбkE#c$%# !GqCFZԠ42[M؆dbtd.F-[3N7@?hű5a!= LdapɭO'CI&eq$Fڑ#3mHpfFff^7o_ƹ,E`qM}ؓfJr4aL_4c':%. ,዇e$9fȻEPH]?aQFT7P"DtGdĬ>ѵ(lܬ;҄-1VPa2LŜL[R %J;I/Bd8ȩUɂw1V5CJ#lFa)#c'-$e #)vɹ丩>jHFΓwLlC23j RJU71hCWmMOx86ѣƑA;d*6 73G i1vTlu4Gb9t+Z!mL[J49ѭ CQHJE2A٣A;hd*:ށ#S NF6 vW?J4a؉ AtL=ڑ$SQ)YH3lځ#ӲmHpZ6+,"lThZ6,E`qM}ؓcfkp$h qVwCɜš?. oSY;zkEXL'bH)hzGUB/?}I1. pœW,R-g*mQH/p>:&E6$@}ȓ^ !)?&ǽh/ʴPbdXs<ۯH3عGlE$RH[uXP- KCګѻ!q,a"wH*2*p5:2/mHd}juaz5E)dރ*&FϾԇ| tKm}Xq ʥrqQJעZIWLܘwɤoXyXc(楐yA-E8|Yy bFlk"C c_Ls)bN1iV4$ MF|q[V`W$x$)n<&4(u+l \LUtm@fl!Lͽ{ 7Z˦¼HT-)Eg2zPcK}&\ 0. BȤ|@;ju]/b#hDΆ36Py~hzC1ױ/i.LA 3 " `j[t^>smH2DگhA;nd6:ޱ# nl~8%Rl<)Df%6; CRiuM(4u,qcZoSv^G( [p@(9NBdH4GfiA2K[QR4bCbi"%ǴibZT!hNԇ}LAX7a$%"  G1FZ22 [Mﰑi* /_"AI3DCB&('!jRUiM`乌qhXSm#!*a(JmCۺ?LOUp"T* E#D\8 zZ26q&tE!#y!YFf\"z!7k( e_{|N*D1hQ5KL/P46-FŸU= )D68cbȱ̳ IhVG;x+6V&v[VNpŧ7?z~o~9=p?~,>9˷WOn9}|z>*..߼}p<ة+ ]q} iǧ7珏M?\ǫbwguħpFA<]gn3֟7w>6|x {_qq AHwn~M|wvtwTG7˧on_;uneXNZjbvpӯ׷og~/>{v1uj_wOy{|9]>}{z@ܿ=_Ufpu*28Sc9g¤w䑨9 ͯջl `b:`fviK=Lw^÷ߴX{\<<ݭ҄7hLo swF9O_r}8ybw{CD"{Ȯ{n_F\߻SԽnEo/Qꛡ;So{G9/Xϩz堇>kt QJFnoo¯,~^+$ͳt3Z{~/^~׷5ʠ9T7?!56w=N"8Nt31Idg=p7T<3Nɱg\oCDb<U:/uQw҃Yq.wC~~~8h9DxN90ܻ~yxZs)CLWȶh.[/ w\ys2֜gc-^ |Sԗ >?5Xb3ItpHzI`Rܗ"q@+Q\yj%[EexzQPWYf)(4LWD1%a{xn߿{xx1򼟼xX>fvj~uMڋ{~/n~Ѵro.CӋ Rm<0tƀZgdh/#_NwFjBsX}83ڥ:ErX1nGu>bd s3|[XρuFDwa>&{ܯa{~/m3}t,הRrqBB's@YDSNƦ:-1]H&:VAXuNtn{ 懅ZhPD"Ej4r4?2#p{k؃ltpS}@|ECB:r⻇vRHWlh7@BaO`EUW|E`u Ŧʈo~6xlC}_}SMfMCiƙGd^ @P-$SSw'QO*m/FaJ|ͧآO)liA %X,xB[ƆBƳs,?d;46Q˱U)ʱX7O6_X9؝!حHa/gq18#s-sst8'>htn{lώo#&_,Oy.O13]/X]?<ߋ P,l8P>!oJ|KƷg^-{ˏ؆pܱK};Ac`]v-g#ɣ%͙9(3|*p6T)էaV_U1mdLNiC'd|qk1yc7!scdN8XP~0Xn rl|;Ώ_w(OkK1Geh90[T]);pqx{px<&UWvd}'=^U 1$fy# <^p"|Fɬ,Gl7o?9qAPsq<89sh=D)h`ɼ8;v3]mch/{$Im C8YG|k$0L(/EKv<D,c'@Rv;|';^qoz4/kq*|-12'M!i$#KR FL$MI6g*YU3 edk% BjʻV(Q%227#m bi%yMR6Y%ĝ%J8߃!s$sh9 ,,mu414/YHƨ b.4]7D.=[BN|3PX@+(h'PՈўZNliN>se^*',92O[ϛd\2Ϥe-9eOZz 15?IIcʾ9}cwI{UbSob%q?LIܞїo5V~Aqxǯ2mW|G1ĜO-ljF-m[[H#rʄeFs Wq[[K3cBD~¸ŝlr E"n;=żRf2or V鹅ѫx^acċHi{k@.H;mI\pÙ˔$h=D&.шŽsF'O=nTr'/iKG1ں`>O !,9'ᱵIړ)"Wpv 7#)qQb/Q'%reSBIړ G-OS(l=='0rt?(>9'PӜhdҭcufdvQ; iȵ;mpG޻?24IT7Lԓ׺Lj"s%.՞vd=Nk#điM'D.iȵ"ɴ{GN;&nӎKn!bO-d=1j;ǥnrOt+$7;GۓnO=q)Gܓ'Hǂ%nӦ6=Gnfqɴ;Gۓ'nX&b?2Qm!%7׏Z`t/OCϯΞϻkDng8\#o#Ji׏Ro̧by4Ҟ\"jiMG'h'z'諽~|6l?N tHE>=P *n6M7&/loO%^'|?RO% 'iO%ZiI{MD ǧs =ya{30%mގ?:aO^k'LJ[{M;Mݘ?>oe*ךF'BqJQ|-ch~Oml=Q[ǦnO}\)m'Yi& {mDi7SiCJ{+ eISsYEnӶMcG n?hЗ+/OonY/KnҘ4o B J%|CBYeԾݟ=]}{\<<ݭ۷ oc>,N?y+⟾N_Gk?zly@a [)`!QO<]C)aˆ;2H0?sx)߯Rʀ 5}kͺVYٽ͝i3QQYE)";p_Rb+~OͣYܿWto ܴ'7 gޜ9I_Aů׋6gP) AyAM\)jYpns7~EeݬnڗKl;7nž!ՅG6F8É`ݝk) >TmZ8Dy]k5<0,BJ^ꂛ ZYr̤3/ٹ"dfnpנ7'gEt\Gws"@"D7:'My #Q$ i%R)-DG03`2PѤd0.*LB3'd4.Y\TE&t0.:Pn[*^8;楤"虍ńO}b) Fq:HL 6q>.YƔ")#`T)fPIO)xTvM҄µ7IL#3ХتK_ PXEX`xDA̩* l6Ҷ66-C"@K-) Lc/]!˼4#JBOOfD@4CJb"OTF#r^+.IbJEd´ݷS[JڗӐqU9iys;)P4)ѱ(/`#mf0idiP21#I&NRyʀ2)]dK%J)rwl{4f6v^܆n4 M鈐id2FNi'S;29 c&NN-`,T nuw@RӁtd2QC#tltLM$dȧCM)I2#XSF)S0R5Rt'ϮuL)i2:Nӑyʘ?UuNa <Е2P>PѤd0*ʇGLE3'd,*RQJb)Rd֥+]xNoѹ,᡾qqH޾th &+X22c!td6Bd.qui`\T #f INICIDԁ=ۻ?^]/n.6Ur{\<<ݭ۷ o~x3Ⱦ?9yv{n+8{&W ϯ~3k}ЗzX<>8+gY3`"`mH4~ư+$l~6ò\\-^tn5i,㾷Vik``8,7 %s08 F6)Q*q1rLsdIℒ H³DCQ]LGgG.$&,) $ lMPrG i{:( }\F7'3Yg8Ym#;"h,,i{Q4R0M]XZ φw>2-:2-[MqLv񇻷w|wv93rJv3wwk}2/'8vlq|ϯo߫ˤUCtWg翹y?zw3xx귴 -n.o~Osۧ(_v?v~zwfogzzǏG^==ԝxOt?:uxwÙӑ/f{|4 9~ioOh_܉d뛋߳|r#0N_=ns؎?L~/2L )b6|S0:|yL8Pե}W`FfFθ)o ߭ԧOB<:k?gon7g翼}{[p/{7e*yϼ.ox}bq~3ƩxnO#?g7$LA?aڽz>'̩{BXwlݱ/WМBH=>pGXw!'}̩?En*JsNFRpS_K_5ksnjpSu#WQ~D' c9jqNNʿZVX)#)Kmؕ(`7NDB'(N 1kQRe<*e$ 'Q&[x{s'/?> ohB+8r*ܽUs=ɉi@|"_Kw=Nqa :tQJ )'5 #ހ^&^B|=3<ó1| oG݋q7W܄ W/:L8ʝ<ԡ( P~^}R&|e+"+"yΘ:#zܫtJ>YyrϢEk?/U}>9sڇ?YBC a' TR??f܏X8r!'ww@w~Fk,f 0$@QϴR! J(tR~O%OcAC,:D wLyv ɤ Ak)TU%,q#\Ƶp(w W#<~o_1?ҟfu C'~2W>aH.O[POձ߹^4y*_ yu#N4 ϔ?9`7 29EDs/h~<|x@hNKkP̘ؽnFX(UvH"N+ @35Y565K`8^WaTa>TYMOWA geZ^`6q\@ݱ7# +|\yzalAȑ?9LEVj?,s$|"ߞ ݎz9= g/b"u \c#T7H.ԇզ1N?BIlmކ9 ͼplS،qǂb 02Q eձ&ߞ{_5ftAAݣ5F.=I),-@5Y3f h` `,ޫP,g!2 MmS$Nn6{y v/.@С 9K;lDvp 0OՍWep}cx2>@Y@ 0$#aTIn;][5'Y֌``5N uۑjtE[]q" '//,hY%0en@)FC@w3Dxoߐhh h0̃3ՠ@W܌@g3l0=<X&4 c p  n?I#~ (3)u6@N 3s1A:H[:S: @A1 cC\WFX@ OJߩ`ڣUr܀=ʧ_!/Rep Qp |p'PFs|~W *qRS+T"$ cȤ9C N- OsN&?P4\b0 0k% *5R+F(s (GD* Ͷ;qK2t@`Q[b_ UTy`KQ]SB]Gs^ ҃)սU=*'{!otZ^\Ò04aRV #- 70P  O 0UĒKJyV׆'/oηvvYpv$e82 <)oK$&+8#wR" Q 3|(pB!H<! hkt9*Pw5nLDTpxԄ+^V|!p~0L_r")7 YFN(.üyRJSa bF R5 F} } 0B0r$}SrB$!&,pA`: c~BaEC#Bд@t|Z2-{ D MZ >n![@L;T~SG ,C᳀^¸c83 |x̓!jX!^\/,ƮXÒDU!l#9'pm b+x~ܢ(S, !Cbh'1}3b|k=YG@VAĚJc5'M(yv^:e1yp"/V ".9E%y1HvY\3 .mx0d߯9Y%RsԝԢd?1Dz{`>"!DzBBXb,?0.t|=bhpX~ʐk$LjM@:h)A 9 ]~n`J8!O+oDdž0J`ZV޵teSׂy:}S8<X痭:>V1+Yϼ5%9"aH2n22.z3'7C@#fFcW8)*'%!P-;Ma $'(`x9Hrfrœ;!'"Ʒ_i% މ cGl~vCIV`0"-aȫ CĚR*1)po{xΏ9W#J0A4 Mj~h` w:)c*] H,1㱓st4 aEy2h€/5R?&G@CFK?qG5hiNRFOʗF8/BB8Kщ#]f stMD."UH-E]RBv_P?FrT0 Qq.Жbx8 zD1)2Ld>4̨"`)cGN071]bL?`ꅏ L|ab%.+SVߪW\  PL#Do*OĄHpglpq) ~ F/DA50 TF%[Y ֻ BEO1܂%bcЛа,<<*y.$2.M@_ qTQ QDŽ T.p[! x9T ̗2cL? tB@DO`6 d9a@PA4cp2ݡS@0X[9܃fuoi& FB$)`iFnS"(tФ] 5(~17˨_xm/Z @@#S[=5b<"7wcY$1}Ohrp#)hzn FІ Ł"509Gb`,>0S)p{ٜ Vp ]^Ig Sbp)Pxt81,cso {A5 Jϫ-)DI0 0s-^[I}?MJ[Ar8K,Ei|UX[:LX"J?+MX M"[i$I Of%|{Y ~rxY"fSKuИ$QKDEY &1}GۘbAsDf|/xşG0Qpp&1a+!HA!s80 Zdx]W,(@ Lf0O@@p FDEDbcX8@j(+RQAՉK[1aJ Fc ew_ 55kMHѷk#=).jAŖ0jr:U-BK**55"6ׄ+1Љc̥g*h 3x P(P{xd +C ֍X6ꡙ25Qnob|91%Go]ݐd2?69R(,?,Rš(\#)q@OJ0 jpQ "b&,0+Γ>MjR5EgÕ,04Edq.$IAj3KC]`~} En &k,%UK`ɂWQgVxXZBBn^zvOW[9Va82 U\)*wu(4yA8|́1>& `_"^4Y`ow}*?bAUu {)o<ݟrWgO??]-~Z4m`~lCJ;g8^ߞ}#5ΌmW[Anݼٛ757ޫC$orhfN{lN߶Rtٺrn_m*a\B0x־R]cOS* ioJ6b?_?^^[:w]}Zzz^ˢ^&iݮn/Ql2hVrQG{_~w L?>m*:ihi+iK~I{܏nLaꜬi%X4dSM꟬ܩ2BA'`CO?ࠊ%XRO)m`EL ޒ3M_,)~Xlzb5%4i6+ Shm X<듥5BOSЬiA>1dMC0Ni;tq2}MiH6ɄX ^н% 84 }TYbzDfCj֤BK^c=BVcp$DP&XNb`[hNޗ;XW`}t۹W7ZGy\!o= ]Kdžo'=#ƽTlUllڻ{)w#U隿kx%QhOE fc z=JI4]%= f.e豓=|?>܏v,‡77^==_E8/n;zӿ_^>.͔*3^ܹK^.nzl-~w/ w׷q_1}⡥:z;guj]t?vEۉxAşyÑxڴJ#jĄV=ԭW@3u_#RզA6WmUb֦݊ZZ(w]uY68M灴) KgtR۫kU̓zUs4pZ+eD#~Rd^o{0gHtZ_Y,d^<6>c;c֦IDݵJaHˣ#%4SVC97iS{M d5u+iqigbbO7 ^OWc?/Nw4 ~_ran$}m( O3'߼g?ܜ}tp?ܚ}qѷRC]Psqa_ί>Cw7gNmօYBE)NR#CMO=.ٹ#>۔a$RHIξt3%ǭv Դ `=P>PTW y{F/*[?s.GGĸ)jDdD2OCt mm 1L,#w$u4P ѮJfCV/7jɈfPwәE&' "X/O:462OC4aRheYSF/g|U\]h{c'X0r(&GF Dt<|MҶ!F#B@q_O4 Gڬa;r&mH\3 ;#3QI"Z=9=3+ YSH1maÚ (EdžLL:s+uY:Z@,| 2?//sn˹/ttrnnN؟%o_TabՑbP{Gh<2:2qƝq? CwF36:RMGV*SL6u@h '|'Mty&V4R XQ@UbǡX G2 E2 х 7(hg,4 {&laej1']&/$Yѡ*-f 2$EiUf:: dIP[C }㪱Lb3e恱&)$%isOu济l#~!4%t<)zGtk2[M'9?5=֢]H1mMOXzUaToT37jcYR=\ˢكN9;:e7[& ꍼg+y+Z"t&GeqOp$ӾQd P^y=456&WoD kE܄AQ8>ɑ]Hwa_٢6f c[lTYލaS"Fee4=؜8jDzǏ͆ĎΖyiptYȑhG4MlQ ?Z<*YtGEwN{8W)}y>S][ yE2A[?&,Gg}`rf2 CZV gMMyJdlQ cT Rfhz<`ml'[-ߣٍ+?Nв|7/VX*jFYMt:c6#ىO+ie?b{9h!zi 0k)J34[JVwc5!; &frɜom(EK@$Β1x,2OzmÜza7=۸'qe7O5cf"Afdǹ3g1=G('|,Hu'OTOTHqߥ}*=zH-~g%fG0-#*@Κ2>{<m@h*)dֳiM2Xr [a>ճCo(%`yܟ>j܂S[>xfA?D[r^?KkAy lwh* {]Sgn}i"z+rHvl<\N6c$,&8]Ӧn6$^{4zz6t4dѥwr\=mECEΝkDLCG p"VgMMS$o ']OrKJж !K!b˰1=Ζ&kTVAyr2P؏#@oX5r@ȹȌӪK7o0\yx/1x@X 9EbeX@ŘX^ߟjňx݇Ab.N9=`d5q*E[ 01zrh*ti*ctmth;+kR:LOae{^3VV"P3r@jj6 ?(i/x1r@h5[/M5Sjf,>UtNX˶!Xqtull1P2rx@* lX3oHS7{1h1e$ ܟe;6$qvW஬qro ;kQ DV%56??de{1@h*v`JH[dhY%*d;~EGP}fXF445Zq4+jFY~nH|oS.J :STFO*_ )cdiֆ=6x聁"Bj<+(Pqn?]Gl7uG4U Ap!p4r|[r5V֤-;Jj7h.ō^f8wd4z|  (eVQ΢qm 3'̞x,ۊ}>C3XY@rb}LxvdթgSl77|@*Xn =` -1(ƚc[qy._r:}cԌM]8ܳL0w j "In |I((~a1C@{@yҼ,biܭU3XZa(((ٛ6cd6<`b0wY}jÞzz[^<,Z]0x[>;`aW EZ:1*=,\Icvʠ,tM01\>EF֋M=.Ͽs@qL@ɨYٌP}Tn1i5{u^:lЅ3 0;432~fJLsĞKCН^9>ZzM OEmֹQFbgz c j Q% +0JH9&~dSjVwZY{6m&[UD-]/Jd>&uG\]*HQ-͍Z!omQ2DeclT|ņ)VߨDsJnú<̺]Hwk^Vptib,nyМV#Ni])F ;B21l1БG4S`jDUɹMM%SS /aj耩UL-Y'K sCFa:᲏U3fPqKWls3[/$[ '[`ndUʹͤ& qk^VtitQtpGÕƖsDS~G,2|M:.`5B0=:m:;#N_ x/i؇@0fE "cV31m>U] Tfmݮ=e!b#g5\Q#jVU<;hO+~;vt.,5]͊ agx7سo+yzqF|4D@ ,֒2|@ǨÍX&5}ǻke!df%K77*![wܡF֋`1\Q#zVg3<:PǸM\ vfv7",sGپƴt[wF 'ag?Ze1C- w5oLqFZ7A@ ?;>]0>- 9Di7MsE:8iAa9hom  \4DCAzFYzVqm^jV PX-4Mۑ!_[02B>XS0xrTq>]+Z􀃑<38u|yF!:ʹ|h4Jx8/{__'Τd\֜3C㚓7D*-_ Ubcv æv MxG7 qi 5%ge2.0ᚔ,>`[yO.zUnJ*wpJ̛o6-MZGl2>}oA)3}N]# NE`},`ANd;n08T<`Ȑ?ߌ O ?v d۔>K GVvYy'^ #<`e-׳{к1v ݸ:: R>¢& OLrΣxMun7f~c,+拃}3$(/쪑l|t T8!Tx}]5sR ͺs6 9ɖ ז a*0ϝ|Cv;}Ԍ{6UXQsO*jljj ~am3N.> jՅnjybc{$BS!4E}qǙh%7Y k 0%*@&olP'تԍ]TR_ު;1A  -InM3,ak*\KG`g[(.萓FiދĈVZ䀐Qs 0{S')/ T* ^Wb?@yҸGc&6^TƸ.?PK*8DjE58 CosmicRayGun.fzPK=efritzing-app-0.9.3b/sketches/core/Countdown.fzz000066400000000000000000001340301272363601300215570ustar00rootroot00000000000000PK8D^{ Countdown.fzr8};rmタp>eta3uM23TL)MRuu[+}!2RpuQR$CDswǧo1B]?>|wwyӏWwϿTXb>}U> _W}zp坫o}aWQ~w뷯_WzXk+{wݧyJ_]o)"`z`D>>|x]5[?|Aҽp7]|s7߿ UO_߼{wߟiχn<\K5׿k'_ٞ}`>P? |o~=N= v_{wWquͧzF\ǿ|XO>|~F_o׻>Tݿz?i^-%=>|O<}a[pjMYԯ_s_~?_~e3=|wǛh5Z'_OOS÷/޿;g7߼3OoOxioY뜩7~x}{X̣~pqkê؎uAkEe G˻ǧ;|×WTSOշk?RcuBcuu jy7ZiuBƉw'b;Muo]֯wWM$GM?=}f?_e_`{\H`+h| F/ig(+r{e{!˭ ^G6ƕ-TMWsO:ߴa_Y?gChe>^YqO-'7[1|qh|Wέ;9oݗ9yz·ѵJFgjI`|fc\s⦢tvHz>睮aGy|ˇ#4_7A{_OŮ܃jZ'XM?iEWMt'݊>ɓYM+C_M`o\OyЯ5GEЦy^N1%ӊ"%&qӸ@݊K^<6ͭHˢR<;]g1ixD¯%J`97߷&&Q |UxFFs2L#912"Z[bUlNO9Ԝ8˗oB718`-RbiKLfW P-E(i[bA:OWi7tqjҗoB7)hfztiIĔItZbArqygS\rPPGS28)M[O-xRuESb)e[Q\ω-{g[W u ,' jvMmk*{i{!4(,d+}RiZ<\y~E̼њ\/"owWVĸiWx.^P#e *1f4PP`A%F)qbXPI<K*Eq8}W /λȓl17aջ"|ɫWE};6BA9eqVE/ߘ76${PEoރT!XopϱjQ\=}X1I՗62VruNjN6X =Wt c߼Qb+{CUJ]2B~-_% o;c=N0?jޚ"Q0}1l&,}[^9Ѭm_Cç??E>?EO}r<З.i]2Rk+Ɨ<׿"lxG)g Ҟ]]BJ(SDD+ش*5jޞ<[mwq:i2N386*?‘XN(' Rˉ b6 3ĵ.o.ٷ8x-Z@(ybJz:Zt=}cFY}n2-^MvHWcF:'X m̃8m™^OL=hd.bxg_\ rVv:YLosҕ^eN>)ͳt3Z'c WV= ϡ/mO~*sŹiGx=)z29]3|0e?t}H(N=gYNO4R(Yz+?ZOǃ1:k)VOjTo92H-߼IB-G} ~R~yAnܻ?3<\@nG0Ы:M7foD{_AZ@,zZh2ӄ**d~KB*'IbH ZVV! |-z$ ET&#*4U :} ̖Or@q˜77ALȤ&߭GȤG3i oS2)֙^IS6.}EP6˥TbIZ]B-Sd!A0m[V.ܱS=lȪ}u] ̬)Mߚ˘$F51VSitj4KP4-LwsPO[08ZLgVq``F֥$AJՆhvR,.#K!+ SfS;bj`mݐU0eFYVbe!yco Ce[Bd'8%g#?vbpRi*aR͈ReE$J5G"A3fSĽyͬ O.55ϽI|Ѹ)XIwщp}OVr7;U!}&q%/0,kVe]y\>5DB{$[XhSY1YVP%=i"u-pϩOqEl.oϗL5H\Z Ýo>D-BXl+M5H_M usڷ.$ɘ3>2nsFED9`7IF.uKpim\{qZSKTxK1P"徉_ی.tM[}y$ө\$DBSOH0d[>ypv.ʍPz:=UP6V'`H- K!5*ɖ[!D*mٿ?z?|֯?>w?<`cz l-o='x1NSum. ?gƿ>>To ?|?AwQmt?֜b9FǪuxsz?|NPӀƟmbP $)׏׽M[NjgyvP|o3Fn*fc~1*-c_ ^ehׇ緈|&YC'6%Ӽ~}4 Ѥ8V%MhW򽽒[{T3H}|.j+7F ep?h`ۍ'pezb_}^YUֽ wx}IJ>]h+f#Pk]ɶnZ:Ft3̈ $Z[E6EIuۥ=&8>%`1>S=wJ>NJfEkg'Av;ˆkX+k;[[M-3"i qUKm3F}@`eE:ze'\u?cPf -wT-5~'`4)|WSgw%tWU~+ ֞ݺ$+[-iQ _htU6I$Xy$MC.TPn*ZϽ#%x1!,m*%SH $1o% qi]%\`s4#&4Շ/m`=Kr{@ |ϋB5N7-kJv^T(ڕ LV9y2ZNj"(sȕq> ZjU)W *+^W-պE`r^z4YV&vݍ,Hisdž(Dz&{Mwa\(-Ǟ, ?ޕ⼨r(X*Bo\>~˥u="4^_:tZDfYSv mŲ&|, *I]8 z!N{va1n?: CiC-).2]0p8MtFd,E23VUSa&0_2G,%*EZ0N^{u͎ r(2;}Y"R6hS)ZQʌuߦ%kr0\JeB&la x72pTu&Wki;֪2i1i74891{ͦ N{`n]rY拊թ&R!Rm.ѤLM'%]L^k@εj(lZ@~US.UcY; e0EDĐ=KD2{̄@f>&+.Mxt>+R$, eG]0yEpVIhTqQxfܴ &p/CS N^R=K\0FWtm$^$3QjYwQRq@n̾E㘃zۨ&eje\x\ΧqX=6 r%-éȝꐑc$s<1.:(*8}Y )f}20W2uAЍRݡX*׫$L;?Qja f DŶBa4qiF$㙅sb 34H~B"S& "g 2ʦے)k[2j>I?wu6pR!F2) F23%rHŵn5nQ̵I;ͱpBx#y88S˾ 򢂢bǞa "r`Wɗ#Z5jEte-G z$)'f}19Ktj~dA"O+;ØDrK ,mA8>,efQt 6)eeJ`3`s03/Ap:35VqpzSIIK 8V!LbE a.t%0cbHǞa B|ĤL^[ a򦟯U+>.UE=WIB<LZ<X3wUqfLa¢<-$|>, hbBJ'^FnvV"ht p4ѵ*~מZ!/޼TMRSQ#%1VȽS#*1f/{ bȽǞ%aȽ kp/SR;C:;MtF'k衢lxmWZI: ô&B=K0b쓓{䄴ijPW1cϒ0ބ{{i:+UNȾp w8z}{;d_me}Law<u Iw8Zte0>\].aZ7e$ 8#FcI[^~v|^70cR0ބ{υao^YB!DB&V%1{/^9f& B׆ܵʳ{pN~ rgIroBJ{Ī~ :=eF܋=aArp4ѵ*ͦ܎ ܫ܍hUTS4LlL"Vwj4J+$ /L6z!N{v!&ԭC ; MFxsQtUh t]t37m4Xf `|!g1*>YȬbTk6#^MWB=SUl66 kt 2%pquӦ|`_t6AMfI6N}ckmq'%Bk5x^37]`cY 2is4 pس Oڼ5hVV9픊Y O) OG]X ..UVI1#8# ʰ0Ódz0< E]h "g F'Өȶ,5V iusGq&pd:AJ唦3SZEj{Ŝ:5@R0]C? =N8_SLPd0c0 L&ICVC)z69 W0@9MtJ /T.&3E!vkm(a! 5*aS4biK yA4>Lqe54)aӐrpp7TTXSĶ\m635X_kJ͹τuw80VM4xݱ((8}Y҅Qʄ JiFMM%'|R%2r'hp4*I ~{28%[(g02 Cy]!tecHQMSG!I IʶA)wt!BG\ if4BRsW>I ÌMLm,kCasǝaaϪaǞ`L^ :o6 ZHfPj$ W0B9MtJԹCp.,/a8 uq,AX;wĸ% =攟 R g (ȶ c2X[ke^'fx`2 frN93j^:w9a |iWoGT(eJ@P@P1Dc0S&ԯqʼe|hr,_j(z).Yi┹_ui1N9LNAqJz\"iI]^]A>LU8e4*6S`ahfwKt6IMf&55hn3ca4ޘJ{֍!N{a2|%T3϶6mX9=Tx Lar8R$@i>ڱW%W^1h%+I1@y<|0R"Q% Bg (ȦPa`: PN%Le: AR夦3(fQ, dBJ<3:m6{;_#!Qr!e;h^0J9MtJ<;*rl\3yM^:Y38n_:.PKqm5#85!&*eͶ= l#Y`Tμ#TWIHGxHw:EV%K +(JyzΒ,2BX+T։Y6\i(X^QVSs fh掔mIikfkPHj $͔@)c!iCjMz_x I8xEV(fIMf=yj Z`6AJZ:@TkQMj6iwRR$l͸pmZ%b_( NƘZh4Ov~.jE5E%<&j Z"%,t.(ޫ1q⁌P@:Hcb Hs!q~;-H㙪g_O -M 3#yPMEX-.j118RYVkj8޴AKй96P'G]A:X Ti1,s1M&f12kcj. C0tALj*T˚ѮKfOјk@D%hiddvMcj"WRmʣw_ Ѐ!e1+ځN?:@f@235ȹ:NkЄ2l}\a8'8bZ;6(1-0(Ѵ6ƉU-I>GmE y*2ETfy Cj4"hG^ky:b^5xJccVa;9lUp!wgG\:GU1 }̂kdYijL.'iǞ&؈ lljIn`5] f vA];"yDWD*2>;9Ӥv;ZZg ;*J/Z ImطdcT-48liXjTou> YQ0FL66us7c4ۚj˜d! ֛M֘k@&o*" 8.u$-N Ӯ.T~G&KpUYqw||'J؂ߨ)(әdقRF Z%6 4Gey@TK!jY2BNux )mV :KIJapx 018H|}҄pNȄ/,D02liO8ۼhm?%XмPaRp4~ Iwt% ̩m)IO#p~(,$gG:_~ {ur&A^v/P~ze=/]Z <|?1jFf ^Ѭ[b[%1sśi[.c'~Q7tJXS ˚p_j!*FuȻ e@?P()'Br?j)38Ib2&0~MHF;R`'4>ʢy@ &f  2XƩ"1- Nj}\0T,B8O:6ӣ 8aVF+9gHqqڂ鮈^SǞ'a?a׼^`\0uXPbU FYV>tzٟ lƿJ]\4#&ڍ*Eg'6p&#.e;e*hdsz7N,V W1~p#Z* pd 0bO8ۼ^m7:CVmgEFbh:Jaۗ-%X+m{i&ʺ4}WڭI˂]bgp4QdXS6cTxIuoIVk7dOx: 5dD) Ls@.HfC2[d?[xQEa6GqX%k~[>E c}T.71DD4*!εwAQAj/"S+&F j "ZB 6)4FVk}AX&  kuY RVB}X`cY0:ySao sC"ZֻX\R+Ag!B'4[JdyF*c؛eB^z]fmoq.C"`*Ά 1oC1Pe naL B0) ^QSNEF3s98dc-dtBLWGqh^?)0%wjkeZM5xTò׶sމڜW~{u; 9G:H4.$!Tzp J`R(HAL!fAyt( alTg*ZX:[U aoҘ1ѩ~L8v:Ji+l%$c Ud<7kz6Mt@j!Szk² &ws7a;5yYP/ \jFFY` p G]4F3pŚ=M.M(B;ܜ Iҡ.M5FCH@KF0m,,W h+r`)E R)YC&-opm0,&Z<`Ȳ(YO00RSFPHLgYi%#*&+ zlĆ54Albv*s=JfDn~YȀ!eaA=esh)jEk+&Lm1"  b c( cDWR\x^mO3 ‚}0a~#!_Ǟ%_ cv͌`^1?L2m M>>T͗7~xze7*[ݏejp Ϛ=IAuT4oE4o S+F%|V^cב8ƊNYhAZ-g]^N(ۂ}&ncGP{Ǟw݅7 YoPr o]ǺM?\N!+{ҳ٥ҿalYN\p7 +5sݦ"T{};Ɏm$CŪ@яM;ӻ[B&$t |0[ؚK[F۲N:컭$lO\vM,Α 5Ӑ 5II/p jFNeIE#zTHِ0a/cMRsy%CLVH_%t<((ka/\ [ڵ6c~ۘPMT]WGʤ<,$[0u"ۨ}Q?d8HK/2n{L5G˲C"h&F)".h gM{d g%mgez5MtRLA n񶥵 h7huY@ M]aoTEqkس$q+\l$۪Q?[D ĭR\27 ni5DIĺ,^?MW"яجdذ|N9gVZQ>JǦ)L0< y@ &| *B.dy1OO /hm.bTFUPVײfz)_mwϏ?]N]~?\%n gWUԗ?CCZ #TeT+ܑrεኮ.'̶:L .Uu1n/ ᱪd ;!?Ձo=JRT2!uvVue!ZI:Ʉ逤,,7_I0>9Mt5Anv^$>i3ekD[ת TU)ij/C%;?~vv&|7r['r4a91k֜Hb[B 6| b[|%Al& .myqY+A%D;KnXH Jjn,bh8@KQNf_r[B 4| _Hۆ.&mlI `Όm]J/sKpBҚ,qq6X*{ ؆Ymȷɉ&Nom'b[B 6| r[|%An& 5Kt1Fd6j[T^nJŭ A-]ʏ""_*eM85L@t úgu ;.ҏ}]; clƨIhu Wwer EgCR#LZ "WD!"T~6{ 0f' ]Lc& ;. %MIUQ,x`\l8<6Z VqCEU1I\ 8q y(Ulu| %mMgl8"p6,o!NQfVLq7!Hm4L>0;]G:3KZ.Eځ|P7gA3~h0d3`*:6IP̱E{ZcJB:k I$2`>R618EmYXDvQn&)~_ _ @-[<dͳ ;#j7ffP˛Ⱥf}iԺ*i[YV/]gW@U$ S#؆.!':{91֚MV}/nA,&c]VƤ7!,* PfoU `d-t-U_D0a2`m43 p[ d'V6cIN&Dl6KXf_) ɶɍgĶ2hK hf`̛ݚHI 9o:+{kqWp!{k_o|B[msw[,%aJ \B9jYMD:7Mt-nE"of;'M&&%\f/lfLfupYMUYlɭL(2FLx$*9nY2ꠐwK򩴄Z//ǣe1658wٴI)jl dӼ]r@J/ _& frT4/pmpΉc[i#=aB>C̋<4c^+䌱Uj +X'Hk dI&Ekp4!Hοjiηqb%gR0% e]m21Z1#.0m{ҫk2J^mu`Vd=t͸lF B\ tJ\q27#1Me"&Wn6ctE7id'6Ո`  G-`YM,MeV.`_/ĩEV;Lʘ;O?qZ%s1LV͊ҥF6]ykd]#;.RMG. $kkkYtfXkpRLwRTK6 pb5 GIswÃk')[j;ڥ6HP0PLj,E_@06MtA\f⚑#q4s`(a`*챞7AkqhJRI YKȶAͦErp459m43 pZk);8uFH;L9˺r8ٜbWȚ#TZ6TX]͹T RZ|@J&p6:\fZFk-)⋯R\^ieP?a[r1_- IQEa nXK&[ [ -k$cktZmN[}&M2D: tkᶵHjjCZ'* PkSF0PB6P㔝((|Ɇ.!=-,o#8zHuH5‰LM1qj4qtѮ5Lƺv։lur %T-) ځ46Mt@-h,o)j@payoP}R7c`x`Z!;`5!QB%ˠI]JHI3=/ F3A(K!X$l gf.gD֤JsB@h[cVfV)Eh,4Fd7)z; eQUX/D.c|aSǞ%a"D0R+KBXB}c-9hk:i>ڹ6U@-F4F uZܩP NIXeIӚX(XeDc4I-$ʁlN=0@6Zw9YϘUԪHkhesRۚ^&$DqFJwtqD5xڈ Fǥ,ҿ#$Hi)-zl:fx@D/"hD2s RD:=#jلQ %Ʋ&;×`eDEݭǶL -Ir٨[\F3g<މ%LiaB(UcgѬ@?SB"HzY|hp4#`@&2ZmVw zU%Gf ⺬4Cf"jDVufFR4aяmGTZK,t`,|`lT=b@fmUoWԤV\*cݶ\GX1Li_?qUAM0Ž:RhS@~dh#sK0"$i!-zlt@J!HiDR\5k6QBU[ֱIOSDXY1{К*(Hm-lLNAz%HoDQ*sGVĥm &%;U$[6%ma7QNf_[B 6|_Iۆ&mR خ4v}m9أr `˿_ 9KGf_ VO53IRZ|Tm83l@(X%ufAN% V⸸{ :D;p0 !qLg-*S/u)1 ȒjV ;A>E&`*ϤgM/=L +*Q4k&;/ܵ.^WE4GIf_IYBvMfQD"6Mt!8h).i_ k:#4X0TǩK0&) " d4mNSY-`c:ALYN4J;g9%t=Mo^ji5őIfFbcglX5-Җ,Q x`<-pl6QmW&5AN?Ӳy!p4GM̸\a7V+53PȤG &REE5ٸ>]r #h chic1qMئ2Y|Y:Yf8["ъ5kuё.\X>ƻŽjfRS`31ϱ3N[+C `-l8 $JQh b۠"fȋmM~ߖ룗62sgY,NCRC9`0͜X(` &6r*HW|@&j`cGnmYdiV*(>o9J7@wjLd &_a%Dpn_Hb˄Shƶ#c*d %PL`L ̌Y)d+< ܅^ `0;OBPXBl`L7<Ɇ. #69Ė2mJd7#Bq,w1a ɳr{uhB^Blƙzhgay5^X<!mu TRgb_G׆%l}!4\$ɉկDw Ej658 &ATDmbuDQu= h cɍLA+p4UGKrBӹCjXDڹoA Ci䱮M$y̝V ф:1'$+ gh -zld#- G]FFA`][D;]]ݗ:r}%ńkpn7h Cv \#D itcp"/)%hBAJV I'#g8mT6nD65 b|A0 RnO"FK3h,aCu1ЄP0 \6!AH}] EK݋siBZ| (p4e,@ff!MK}µ yqa.4p5MYh\W$EdP:6BCa󯋔#c Uc.f ŗ G]>0=  9m VkunlѠJbuwjHsh^?5~y&Ylu %TMɲhY| A>&`l/iV̐&k+jVF ",ud CfSSz7@DV #h-vlxz@> gDWQ|f_vZr7ՅuK RGfD9Sc q4 P~d!mL},RD}fidۄ&2Zg1m.oLFkS9{4Q͢Hjınrv+Η&a[ɣ\$MrrDz Y 4d:_2ao{bQyq8},XMzyamj"@LۦAZ8p45lfˤfnHZ2S5mܹZD#Q? Y|`eEtP;Mg:t Whh).g,3ʌY|z[4vWjH}Bf Ƀ}n1;@%Ay5Qf0Vv^4m,3o ,.t"T6ykx3Ekj2͛V!^.um娹ĺh"Cps{6/4!a}xt8tKCP%1p}IllYZ4`B+kmE: |) Lj5t,RC Uch!0(xi(B4Ǩ/̔n<)1D4ʱ;Y8:Ѹ %TxE70N;00j&宑nS $xYLhHKe\q$2Pxԯ,4-zF*6M`+^ y $(Ǘdc3r]iobBJipweKa-ođ1ư%Xnf aф[L4 LE`/Z8h4SJۭPJIJE"% V4h W,0Mt%*\lfXk3NdX;G*󪫋k=AZ&<{WGkcV'6FAX#QD09Z6Mt=pf"2S[mΛ>uʷwWrE,S2M%2hԕq[d6%{r^IU`uUzg[B 6| r[|%An& ]m嶮TH%m51 }?>]}/w~CwV?=U뻧o>oo_v?4$TL*Ck s͉Ol!kKQS[[nSO:pN2^d3ӽ~;s|_Mh_ ff햣DԕJ嘑ur#s>-9}>x;.tv9"eqXZ-7zzw^[nT"|EKG9A8jO!"f?׻mKw4#l(}Bsz{Vyu Bнcd7:}#~Υ\竿ALwI#@ Y[NϛR[϶2K;`];~_ȴLSZӘG>8}`_0V{Ŀ0|~ǴKԋ glo1E#V߿z̤ {⟺{OjDӚ&Ƿ5'TGHiϛk̼:[NG$|?B3!^] M7FwVVz[O':]+̊YNir*/⥋'Ȟn]/R%0^6^*e/ޓ`xv9<6 )6yZm[=>61Ǒ2水wL^=WX%L`K>{B)c1Z|6+;5gP+bkK/\ Wś#.kA8W g Cr1y^-™kÊ}cK{yB*ڸHou,|{qqWnyǚUewFk a`|xU00~tH$y'ۀOg=0+< ^N}3Eb;?Eenc(W'<)/>YR8]k Ih(Q t]ېQkz{|6^i-=% ǙuŐ>T.dmL z5x&k1Sr_f0Jwm5Ie7:% Q,fuQoNcJa wz5}1~JfaamяvlnmNiS乙fcs?uIakIlǝ%^Yǥ{Y[5>x4~c bܢZ&MoZt=7^O ${eRl\ś˾lu)|rk:=ªO fg{1|c Uxf NcmK5 Nѹ}w`lLvxw2~-L2֜Pݵh!"(c;mǍ_׋b!clKl +oma:GTOˍ]_։` J;U"N}Bڮ*j;-5MZCu`' `MQ˩o%YNK7Qv?QɈO[zvNL%|$DR}8ho um 3Qmkiȴ23Z.' Ur`Y_~TsV7.zC}3+s>=m˚c@[N$pus>@jUs:.[RNؒ˞o^|yա90; '😾gs @۬|*I1*LwH IQ̪TCsmn\)f{ ?4mْrCZ5dMkܭ0uB)٥2AܳP8[.<o99w]5p–9C/<0eLOέkA?9YhΙО(]r>Чʞ@B7SD񇐙b)͔6SxY½)^<U=2cRC^a L eA61r9)Wt(['Nj"6ma]6Ybk( p pP)l7^;^#xr׫`)W /&]A&{vw~ÞnLNuչ-C uBU6hf{}Ɂ @񲟙T WJ 8.L S=6 /yWhϺ23tp}!ʹ+?s8ey ,*C; *퀀嫲}gdsKrz2? O\78QʒV8ݩx?;QcdeC-b}|uȞo>B}q(/Ohơƙ, 2 T<Ff /lA彮0Glu^&d^M *d *;eU* .|u:^plox]R·mɵ9`JL ߀}N9,6kyA,H`w[Bn@/ogDžpQ]tBKxvIn?geP~SBt"zJg _k:˲f5̇ޡ-= eH4?Te~`lz `;He8ev<muэo9ځ5e?'C9a'^0}NhlΙ, uu:e se /unA%2.Gn^% ^ آt ..L|!e[T|ȳ>Sz.>p,XDL1T8)_u;{GÈ|}=OVwv?QɈO[zv]<ͻouOǧ|޸}{_?}n>~V/_ y~융F8Q&$FnjJ .!"~t?YX{п B㒧+*hDfEqכfT}~f?uK/RwOhQ/OUOkTRE*$b6.0~1IIuWyoϏ7OW_>WsNKjG&Y|{FRM5۟t>WZ߅8e]wq{f/3pV_!?>jErù,̱Q1ż HI 1;o~t~|uȞktULFxذbjµi3RQ8_2#zH-)?2Ʉ(w*"tV#OUkȢ!BӐTאӦq<0U=#EvJ K˱Xj8 @Ȧ.ʦ"~o58*(G!SYB'Leπ]3 Θ4qWk!ԛ!ՇUPb)t]A,ݸ$RQINSh )I>8f 82bY0ıPiBB EEjh=Iƥ6 6EJ68pxᴉˍ%ǙЬ#ú_FpX-yF떭&8lvllΌh_fis qse~YUӥd́l*&T523Gv^%E͆Gd8jY4RL&N5# G5 Q9j|֠2G5zdD{\uRڋeP>7\jhhs[ QGSՄE>jJTR*&^dΣ3IwÏKcaIMf&YXvޟ{z}/gGB}6'p"ueV]9Kg.5~DOX: )dHB0qzyiF ?^:k<% )3Aeԍ3t99͖Qd*OX)|:,˥O2/!e{ˏPczDDL5cd>kTZwTa=emʮ 4HSuIS(BYc "Y\IU 31` _%3WܚnlqU|2\X)EaEjC`B(K2Y͘GCVg(q1%Ռ*sqe4!g#U U(nRc5z~Ws@U`J'3`I&T}lܚH.5[~DgLJ&BLk*ɩ)1N*Rxniԥ*)Zt%{\uKk̈́k&CPmBIUP c I2ÀGCP 'T?dev25DPe\uo*yҕR%Aݘ8]ʘ\iRrͤ|Xf#sӕTpSJ2= -Oxܬd("'Hpy\|֐2qc'4S;*ms2hJBX[Kg\DDO8lS19'<,,RNJH%R]2G͠GQ )qT3&dQe~23FTj &&pv-oخ[Z7>Ep5/xɥ(hder3]v^ =]8j6تrC)T2iwv/Wf(ᨳ8J27`23NFJ1r-lڞ\ ;#*qDs"s\j2}i/8ILܬ\{q5qy` #e %c6i1 52oi=2 iۊ0nAɹ\%miuC ʵ @f 1e{ˏQp~DHt.d>oX9vm'TcӹTPEE}R.41!&N̜ar(>RӼjl\^TXRB 'Lrj0$.d>k@Z7rus˫kRLͽ/wu{89pw𷫛W>N$N6_Nn>]|xq}Y"|gaء矂9³NJTɒik >chF'EL,"|!ܡ7ϕΝf &rL$󷣝Klxk9wr`emε8/rRі ѷu(q@FT;J-Iz@`mx$#vkGpk-y6B#Lv,,}j%\ 93m[[s`VVm.>$_L+md6 <>I&l`H&l#.vv~~{[~}>m`no9ބ/nN?]=c?u<} 0٧otէ/N]B:y<~ߵJ\̡YXcֶ"'v|s~lՍp[?YWY͏܄ϝbD{5,5ʏQaWQX,þ6{@7}d*,ل@Q.CSq^zTeiSu~}`f7n>| {8x D> >eVSU".?xFl |ǫ/Rt,g siLk=,,ѕ/MP,??I ѿN>*t|Σl! OcF8j!\8//dthҨ&[O[A$:tA(FeR/Z_;{ 71cÍtVSp#T\p'HL:Qh~uK"gB"х]^ZD^~6ay\%=7Y4/U'ԈSxڇ/Z[DhYp_81ǁⱐ}eӹ QFmFP44xS!IAkZsMӠ-@SC/X}I(GYu1Q \y/vM)t 'Ig LU%-g\RKkmw2^9--x@OYi.6KXvKDO@Y2J Qm0t#]JWDD4sHq4#1j +tߞ?.s-sYt<+&`')m%{/ކoFNԁQ|,]x!6 /M(m Ǻ&xC$C'&<UkHۍF+;_SâUz֒"..H,5KDI9፡% @KnTӧÍzǻ nH*iX3aØkvrѵovx||څO^tZM,1o]ӄM&7 &aZF&a [qE~SQ\ pJ$a ]M1p.",-6Ch`3VqǓ|} 4OH'_8pptgd AaG0MF7<]{4x*H؆), t0a xd -ѫQQ7 BpjtҐvj6>: "q8 C 9K.:lLvHM0π:2>L1YPN,$aAiKO/(|Kk2Ay1,7/K'00.)\DkX."+p/J&?*l"0b@vY\3`'kFL0GE\0KRK-QwB^"Fd-IOX +q>d{'%Eh", ?uu ѽm )IƝ38hxNgQQ^ScR%Nz(A6u<_,ql GQ j]GצtnЕINt6IN x`4Ppd}; qtELg)/{zkF6QrJBkR٣PG,2TDB$3>9p\J%A/? /DNA&:QIR$CѩѼn  I.kcI"݈Zӽeɔ ZT5ܹ $9`:3%5 Q FB4-Gd_!9`mAa`(oƒݦ*섒8u'LR"9t0"$mGdpXb8>G#B5htK8(/4\Pvi%${[`\Rar7oаbEu=OJfhYy>٥0qic-0AQ-P%G†%G*-C@ѮIb,1L`:$c _0vQ6EʀGl:'5]&XĦ0 )`֦ߣ;6{eZ>L(*)$O)@I)ӁI%A4$ҽUDdA$P_Fи~9l/,j.nhAl-gZ`<@\8; g:랕AD7!Ei%XSO4IEĶ`M*U(8 RѮ@})bT^0'^\YQ \Iװn)|P40mYN K2QdmFeC)*AZߑ6EUzpK=z,pO"R- RG?0:qŸTq5@ʓjH b':111L<6PȠjVSml4\Z >E)QtQ\64 UQŖ :ѶPZFB!!TlPeC@'ȥg&i !3d<ԞGBaf(n#5(ܲo`J1FBOHYpP?"ypuS+b<( e6DD9"rj'(sr8R3 ueF);pk|uL/YdM-?D#,mSڍ唅py2?NQZRnYzq;+ 4"=p;oTGrp ͚f4T: 8)э*‹Ln:+YuC9zCXLVED*].ɗӈE $*%:$8Z /aBItuT$I:4ƢjFXձcQ7b;kEUМ  }Y2Xi4flO0K󠢃XU ilPcHMNJIlMoc*1! &JH$Aœ7ۦXx뉎O=zcмmp3Y@M`JJk4jYYBgފ3z$t t֤*)ؠjMke-]M ˯ca"`xXG1OQQ1SR7W4b S,iP;z CG @:yx<<'5՚"ɉ3@%MQ Tȩ4hJ|S L!gOHAaO腭!.SoD"mRaa1%Xگg]z!YW[Y(i圐%5e)m-֩zS7G^a9qG)5Je%˓ObpcRH8p_Ci$SLfB~)/ǩD2P/XDm ־Yb3}>_\b!@ET]/g<˳./~ڐ cˏOW;᜝xusvqb}3M^ ܵ6c12l?|ӓV1f}~ 1H;W{fTOj0+λ4:m׮)"Y]2 TJB6fw\x!3rϝͺeR%:Ki2Sc_N#M[zi饌`4aVd]M~Mt:[Q ڿj>r_W+?\.4~M] ۱kj{b#S,[ny޿_<_ؠzc@H^Kg!4ǦWkYvX/6ռ]W9ưyUW+Wkrj S}>wēS,7oZS`(,lD2f91:223zҧf>}fltIYNڿUsVއm,6燻OWqT@T!{>_dz ۆ66/qJf,o\Zh?D|4)GrFڑ)[4yu]k8bƂ^3:OUqb 9 T#Ӥ. tWfq NA`̰9%`t$.f! Ю& v.ܑ4nN}b0ʜwʜWA][/x >k5'"(0 Z u^:$bvkzIbƮTq2o& )Uf6BcN>Pd`WjM8/affxȴo(4{l^Cd-w(Y8Cr&:Vx4iYgAH&d!!dZLȰ^4wN6"["BgJ6 {UݯJ'FAzǐ0?8)z\ҎТ .pHQy>pw=rݚ1+–o[ WTabJ0Pl!Ʃ:Lڔ9YwPwSp(OtCII6znOE([.r+ +:d.3mBrn@ڢ1\͚}*K.LxYe^=-8=,398d~7dv[{ힿ^7tO)- EI})uymM&U $Mndp '-@rsMgȘ؀灴VWdq((@LƏLi`^_fs_B.we1qӁBc6[|c!#²^JnwIŒW\.{c95t8u7d6;%4S Pp)㝳ycNj1$07N˭d6a ɻvk;Uu˸J$uE^/Sb` ݲ4=VO JmIJb  P 퓐%,NC| ԣ#̉JmhMO⭋PL :ζ=$z'uDɇLzC$ Countdown.inoWio8_1ŶR#ǒs5uEzM[,64D:7N77Cv(+Z[hJX%RR,_zX4l>(L퀰p6H|MC5'3+ag8V*՛ 94\O_&Q|cO̗I]<0'n\'Ct**Ғ 2H0sHQxVatGAʰx3 c5t-%gz1YF$_Y 1F2Ir] m$cϛr^KM,-/8r{ oф!kEBJE=a?qtgv^2ELr2QtcU5L'MS2ֹuUGiG)XӹC9͑5^2pCJS2??`n PZSkFTR07J#˷MO_om+VVkr/ '-HPвOo%b߽c`N!HR2\x^saPAQ@@n0QujhP/WF@ O%rUл>iv`k|-;hllv4l*բ jԽ*P Ck(?H,9>Ә\.gz| p wG-?|[|vƙ'㌖Lg- rA9)G\*gapPB7EឦZISb}69mf7h' ]M!IQUyR7OUa"+]x&]gy‹a}e{Y'P'%>硤=ҹV0}җrN Ue`Wcr:][sq UcQe޹hUtѳxvckk-s}>zڇΙYU $,*GϞ$J/_'298'8=xNһQ;wҫ~[z}]'.4Uu&Ndыl/UTI]-z6I C$ Countdown.inoPKufritzing-app-0.9.3b/sketches/core/DC_motor_control.fzz000066400000000000000000000561321272363601300230530ustar00rootroot00000000000000PK8DE[YDC_motor_control.fzmƱ&}#?pDZ֞&X/c霰FY-{͞ݧəQ/_v2 $Hhv[ ~y;8{\zE j4_}o?~76_ՇqSy)LL|5OݻWʑǫEfwWo#/n۫W|y;_џ|횹[,/W}_-oz cRF7Wd}5);4їO_.߻}]^?/_^N՛ǯ^{_uKݫܷNFww?ޯ>6{fջ,79k߽}-sG'5M?f s|X޻ޕ| Ǜw__TnVkԧ =(\i_f_\R2lLؘz-K/]%?]~wp;o 7z&s_\M1Uspv2Mno͊_^U:A|Cڽ=<{*vK ϖG:ƽ{LJ̫g_f *7jzw{cu=k:P.vлۊԓntS/<珳'hԎla~ljf Q5mUa/}/'Ih/XiEw-(p$Wp9EtKh{BC3\g H=:W/V.gBhġi:'@" }{SS XQ yӥ:qӖpu$}R6 x3#'x[ud򶩋vފW&yyׁ^np:ޒ-"mCx]v+E]M Eނ';ⱭH`X"ӧVpէt;~czƏ燋/vw:\1Zj-WD}ө:%vv/Xn'4yqG2@_di3$buRɨScXj,ص1kKTcGNvI@CwI@Cx] >7QC۲b[QD<MVz x DMu/"=uj SnN2fṂe9FZv&fmJ'<|h[_%fKu_MWm|)?l K ^Ogbp\^ w{S{ >AV8dOiȞBs?*i C`_|F 7>* ?wi|\oɌ{3S.g(I~Ϛ+5} P9= ҈Ux(\14^失o_NbwJ_0eYݸgUɚ{H튽Uw>2Cz "T!#F!ǣ^ :4NgK%+8a%~8 ``n0a3+bC— F1#KCҍp&pld ewSh8V=; ) 4pԼ %h)/TD "13zpMoٮӛhpƟ(HAa IJG`>ǐp< -[!ISFywDA:'Q3 {&g2>Ma M60- "'S6 ,lŰ> vؑ3؏9N>h-bH02*?{GޡFX7 eaS(RG´xj3M֊)H^JլDU e *$%zЌ%u#gKj5jH6Єa!GZL;2+?23+ ٞ(eQQYpZ_X ZQ~EBL2;;8}ʋv ;כ,EAI!Wd|9F$9da;k0:Ⱥ B 72e著UCO\DLEe d-Tt6& f~iQ&]gXg,>$q3 ]ELa“~4 9ozmi~3m`1y1*j ,**uaIx2EM;: .BѤ h0Q=Ӳ4#wɁ%NYZ6Gfi#$ Nzޜ\F \;2Q "2&2QM;wH\DD5P& ӇLѶ65EFE2KM YK;Asv$` -][fB0-ư[VCKs/C7Ws,J,;‘s-""Oh\(7vc8S12|- WpZh9i]0+x2M+NmsL 'QF0t; 9됙9c ؑYfoƏ-Vģٙ"P{ҏJ岍1K|Ŵ 4Weg*IjL2;HΛeH-4bGfgi?2;:;3=3R L .e! 鱑X3,tL66Tm\'>k&{ǑPB;+jVP`9LJd)x:mBI=!y%Zg0{'`ogwvdBsi5 KyqyފDIJT}l'e"D$׺Ci," 2f޴ mP4sZSiql iɛLCoyG^'tHpmhLѺhI04G&hi@2APlk@ CU0˙Wp(3xCMYyC׃i~XKIؤ !Ъ~9VbDȘrpE.u, ɠMT{=8-ʽOtC'~W΀܁uh;A2i`컴c׃#n[n-E4;4R*UXA*)QFLt;<98[pd$AЈ mm vlȞ e.FUV[f؝%87kpOBǴ8D :slvJ-g{BJ_Š`%%"*u(Mx6LE^TU9ж\jdm;IdhФtiZtyӀ1-O@dm5-6 +PB5h`%%**u(gMT6LEHџjρLji8Ez$yS*WgoE kIZ<ƫכVJ3Ûq6I3 U`%3 嬙C*(foFݶs[MB$[Pb覻J i qo^)-R"t&ڊ!aIJl!'5>՗A)Y?R 9ᵍm x>f{cIM摖b!bZTֺrĬ7Fʴ*e9"YaH5),F4!\3ͳl*-Nȑ޴!9q3`SUpvԼ*[0icf/9 V&U#ȾhYlHA9Hj-vFL`?XX.1ݒ[=Ya#3E +fQ[@~i-֮y411>P+zV?dcAacZt736_X!]_0/Jl(E#d)+}+wJdA2yM([INQz[@xFRrH-fm\1ɆOMԀc,{S=00+q^9V3Ka3R bR[rMgeX*^=#D~' |Zvz?џ|3~_n]N˫i=;X~\G>w|(>(fW&7..&߼ol gKow}]i8rw_ 8 c۩UDg |13g%B%+'Pv{7e/'r8>o^O=.&s_\M_h9xzchvr3+<PzpòkvuFo?yv{N9TJvAl…_e +mNgP"6ng10w瘯pٯAI^/'O?k9^^IxsnDRhf ۽.pcZ$[Y㌫ԘH@V~O}sqmz״f\Z,p@S1UhV )S0!˥zB|O8~(x3>^}sb=2){q#vWX" iis9"6Pk[q> R UAy0^D8<)]F1vblTp2u:l28FJ@nF~oo?٭n|s8nۗl>җaqm~,/obc[rS L$W{>i5Ml=т?#k.W{ޞ4 m_Ox =9zX9hZЖ}Xq=R^">ƙe>%%/ X6S¦WvUgxYv,b-ˆ2]$Mh#o7Hc'/nL qøëM,{ ΰLbƗnw=\-Y*)u'sCJM'![{N{-q !muɁU<\`Pd5I-+2mkLɽi')A v(S2ikQrd7`r|"A6xs&ktNכ#MɨS+r4h wp$)3cHeHdǹ&>OE/P\wu9S,M3=5#և- ַb~;[̙xE6W{}~E"KD)4`WU$FpM(ŝ64W L7K%! Hd=2"j(AAIEԫ}=!9'DJ(zbPdUCKD.\.$Fi==_ylRH\rvcEB"#Nۼ~"yڋ+~D]#N pՏH64Ry7WM% Gt>Qj+)AJ HtN;: QHުJ q;m޿D 6,n5$=z QȰ+D)SiOWí ]$Jlmt;qI6'zBH"v8E\duB+Jp86jn$=!QȰkD)W SiO߃7O#v(E>! \.ozob;qu~{S\5_.ٿ~:9~ +6TX) ;eVMlvP D_t^F={珳?IZE~=y-?W#^[|JUi|\oɌ{0=o{5Η/N^:_.4aٯY\gMڱ{Dj-);`ryxrW׺Է/'k;w?=KYV7g*ӊj6,"[J"_h5)Q@ I#FQCD%9,#uZGαNJ J}2F l^l[ "]f`, s--Vxsofu%IsWL;~$!s?d'_z,WUWUz?WeB\BNTxnjY0jTNV/ɪ'q fHBHTfU^6FZ؉'"Řj[} VΖ>$ &)`8lucl<l(w/ˋz*G#$8,Tu6o;$nW.gz7G/@3OE~-["rI C.5)TJUcmp1[KlQ:> ^:tFOMiD4FQZ)*S /H> KgoZAm3ʹ깵bjDKmʈ-ehEz.,V:|$E!Vf(i`h@} I&Kyh5D%,L5B I>ȪkLA_0S'OYɑSF(£rF %,uH )-!ZeBaiIW'f@IO)CJoSyi9^JVai+e/r+J(뛭.̼ gF:2{2x I j˖Бd:qáIHgtv#f$IO)cIc53'כ5LE~?XE #-qGbΔS̰˂QKf@i`ip5eI&QyҘE͊}%/̓l;\@sr73.ߛ@h8Uʸ5ŝS,4erN n ( 5MN:?<4#Jb"OS'Y] SB#+2Ӎb 5P{s\G(T8U)nʑrL%Z|Y[3DT`!s ( hm4 J)H,&,4崌vp]0ae1%)'(qT#dA%9' +TXcwG>^gW;\ڒy=b}qy7~/߽9xzdlt3Ⱦ><޻jytw~+8qj~H|͞ˈl<| 1H,q I=L.L"{wO?ܿw\ZXrpuZ>ͯ7BEñp+z%m]~3YW'I:J8W5/yluoo_ߎ>:U}-W_|X^w7_߾_o~?;Z||7߾oF_'77?~3O9|_Y.^O&>}*>7bNEw3gw_-ll<av7{t#Wog?~X,U)4/џo?nT?᣽۫hh 2Ab*ݯ73Hu/5 _ltW ʝ<С( P /*lCrCN魛L$zܫtJm_lĉ(rDn+"wQ}:נF_}>FcG +-I~PI'q?bƉV##Z fL0M?J* P* E;0>-hy(Y8u #УIRThtnzљk(w w#<=@ Oh+iwKZapSfrD"w[֠ߚ4z][+ E[~AgQQ!:^5@B|Ly A|LaL x(Zwi`.3fu7"GWOItcЌMֽEeDfAyEMEZ)5UXŲM/߾|p2p+L/I̺-mw7 +yzq>#r b gQ!8~pYĹؐo߆GNz10"u\c#TX$~@c*C$2n͸p) \iT70\ jA,X+zٻ# (`Q:v䣻S|sB` AOOX4jȊE# pҭ te*X0AS3@VV he.a2x_Bt 4AFMNh~n5.fF@^܎vA#N`/oۂ%c?k: UA0{Qef+Po^)H7'ư>$ ێx42p PXIf\`l:N+p:*inA`AS|ARqR䄳.I!(yp4h p4PL{"рGCP؄S:CoYq`T|6'i/Ez8v dt=&hw]Ci ToP"oձQƱ;P#HÓ*³<;(w*lG8+7`B!O` 1p4YtQC; 62"p[߼(Tzܠ!Y?PU<3D&́qnUP0d#X@%@QR P#)h7b30UE9NhKD@pv`xM4.Nx+fu8c' ځ~rVTy`ě0VQ0X(y4H"WVxVa} wN #2B8 X|JpFmA /5ÆmHGcih#ZJVuV# .ýhw{M#z,hN#ԑ4pu8}YNU1И~FYI1!&!ڷ_) zޅ }GlvvC`IB : ;`DZW!5pDU C`R0 c8^َ+ʼndw Q8Uh1FPESAb@b@ >J!N](p[DCO:|@f#atcl4?lĸ"صwTөW6^CH)rW=DG]KHp/T1xp?2'ܭn]@>:cLoHWDī"R+ _K*ڽhhM]Jaa+ΥF2ǕAK(2E&Uf{4џS(Oų ]5o7 le@-5sNW| =V(h8xZTg+zr0q#x`[p8 !ӈo&/4BhS>T &a2"/A"HL j(E)>Bd!0[dt;8d%xIx q"iGC 5l h#LtrOiz4l z*e>Q͑>]M2L&4H}lz\jZ(x=UvN](?oϑrԲD:0LY4%Mev F՝B KMdl5__OcI|RM2wH釪ȎBw =hL8?"ނPP?BvmV W؊E pC>Fc ew誮t5ؚkECOѶk=(.;*AƖkr2Iqxo`J_Ƃ9ai(f@D{ rRĊVdvY­5Vi= x՗Z'Vr1/S>_OOغ\,UP?L-]Y<^>,-& T;"Iꗽ֞Xnr$'Fujj^՝[mZ-O>[L+޷=?FVNrd9SA?۟Z/MVOcWOwQvxkl(q2)4|wH![onDƬJ5bIL6ҋAYjtz!Ll2NPXLm&67~Yҷ7]Ė}X ƬL5&]Xr95Z1Lɋʽz~M'8^Og",vu 2)2 ~6 D[WJm +->~a'U(i~_RJun/-l}r1z?]u~Ѫ=o5WWnΗaK'~"ެaiwW\]4QyNqAC^B(]#5*BzGC+q$(MR1  /x0@ %z"a}?fc{1nXQhNw8L8"&4Q5>ch$ MЮ*uP%F" -.NbiqmǛ%,A.Sx6WӽbJj#J$k 3yxpO-=Cg!tDX dPq*-Revٛm >O4=D 4 \ߝC;7cgbJ]i0U?|1&z`R2=lP- I/M(H;QLܚ}!tjy--iӾM~I]᭵xZ#ba /-fF)H&.ljF]15'd6d t,hȼ!F9{x:2{O@bБYZw/$ɗq`66֟VˢMKΫ޷Ή@$sR/ww`M=TgPHthe22Q]ќ/`1l1+`T-i`TX&ʻt` %Q2'lOJ[=x%)X%iш'P$MHCw+EM˞I z9])\Xe™*כY[cԎ##.hz8A}}-bdl7G* YQPI5TԐ6eB6~!!!cG&dG&d]"y2`+aT}|c$Y!rpSAҗۛv1@`e:\=`f@u|dX*SxFXl#{>ne$LWӞؗ-l>_dj\1Bl.}%¼<?bnX-je6fud߁q' x?ch0 s %TA{c\@C ^<㕧ɵ;+7\1WlMH#3A> +v'&94%7O<.9AFOn}dmǞdmpdwP9Sh#v[,m8,gPX~9ۢϱC2ukHkiDr=R `iк~ԡ#WzͶACC-{pLeNf+ u #8]sТMhFL6{*zdVAqs2c8Lmn,RzʙzblXwvp &׻ +8]HfcH#8JH=Ӳq !yi[0wEsr&z>gU$]F9.7m"Ϭ[ďLZG"9Κm9Dٛ#MPr>la-؂ߊ(w}5'sbe.a$A$GsM_,]OaF%Vvg__Oe︑[x 9rkFNTό `e-5ΜNQ8z)/>$6a$lY;5Ueod#GZLa?vdF%~d^V hD7պ%c۔FUܨ2Q5eTZDAfFvZKZ^-?%CJTa=G P"MSqk%jP8!ȕ{?=[2n'>d#WYl:2>rd (=S2fT2QZ;BPv@UZ|!YݳD2 1!ecRhW:l^Z^^"SVQ#S#Ȫ8N52g"4xYÕ/J$M(Y5'h=S4) TQ-la|1TTT☕e吚iYdgMaxqd>q:d-LA+~ߙmfZj3 N$ 4$3P涶\8|UhP^y\Tih*š B*, Q) qd!$!gB)!/wA.ob;qu~-Rիݓ5?SSՃy2YhzN#M#;t(x|?zO|;{ ?0|ι,)Lz=,R p|/N=k Ў?=F2w` \SW G_p<->枃°o mgI(Ov'2A xGIۈ`|xC/rOgxYL$&ĸ!Ȗ@A2B A 4 f5)>+mLV;}Z緶p%B] {cڐКS(EuU/Ix~{ t֞JHQ^Lm`_G⑒/)Ĝ,VK*^g*ʥ)bl2rrc7ee\N@xKxXV*gWiU\I;643G)޴P*b䤞$3$3SYh :ܳ j *T U]b'L*-&)>&9.+C3=,춰 wd]eMu0.cjς"S(bbx+Ēp!iHUnjMdmH3iv d>޴$MLT B>Km[Tjeo1"ejG5r-H2kkF2k4weKsDCY63c@2cM 2,/'PK8DE[YDC_motor_control.fzPKA\fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/000077500000000000000000000000001272363601300234765ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/000077500000000000000000000000001272363601300263105ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/000077500000000000000000000000001272363601300321745ustar00rootroot00000000000000000077500000000000000000000000001272363601300331545ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/BlinkBlink.ino000066400000000000000000000012731272363601300347250ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Blink/* Blink Schaltet eine LED im Rythmus von einer Sekunde an und aus Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // ganzzahlige Variable led wird deklariert void setup() { // die setup()-Methode wird einmal ausgeführt pinMode(led, OUTPUT); } void loop() { // die loop()-Methode wird immer wiederholt digitalWrite(led, HIGH); // schaltet die LED ein delay(1000); // hält das Programm für 1000 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(1000); // hält das Programm für 1000 Millisekunden an } 000077500000000000000000000000001272363601300333055ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/FadingFading.ino000066400000000000000000000020001272363601300351740ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Fading/* Fading dimmt eine LED langsam an und aus Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int ledPin = 9; // ledPin wird deklariert void setup(){ // die analoge Ausgabe muss nicht im Setup initialisiert werden } void loop(){ for(int leuchtwert = 0; leuchtwert <= 255; leuchtwert++) { // fadeValue wird in Fünferschritten hochgezählt analogWrite(ledPin, leuchtwert); // und als analoger Wert an die LED übertragen delay(2); // kurze Wartezeit } for(int leuchtwert = 255; leuchtwert >= 0; leuchtwert--) { // fadeValue wird in Fünferschritten runtergezählt analogWrite(ledPin, leuchtwert); // und als analoger Wert an die LED übertragen delay(2); // kurze Wartezeit } } 000077500000000000000000000000001272363601300336305ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/FlipFlopFlipFlop.ino000066400000000000000000000020661272363601300360560ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/FlipFlop/* Flip Flop schaltet mittels eines Tasters zwischen zwei LEDs hin und her Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int tasterPin=2; // Pin an dem der Taster angeschlossen ist int grueneLED=9; // Pin an dem die grüne LED angeschlossen ist int roteLED=10; // Pin an dem die rote LED angeschlossen ist void setup(){ pinMode(tasterPin, INPUT); // Taster Pin wird als INPUT initialisiert pinMode(grueneLED, OUTPUT); // LED Pin wird als OUTPUT initialisiert pinMode(roteLED, OUTPUT); // LED Pin wird als OUTPUT initialisiert } void loop(){ if (digitalRead(tasterPin)==LOW){ // wenn der Taster gedrückt ist (LOW) digitalWrite(grueneLED, HIGH); // grüne LED einschalten digitalWrite(roteLED, LOW); // rote LED abschalten } else { // sonst digitalWrite(grueneLED, LOW); // grüne LED abschalten digitalWrite(roteLED, HIGH); // rote LED einschalten } } 000077500000000000000000000000001272363601300340305ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/LauflichtLauflicht.ino000066400000000000000000000022051272363601300364510ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Lauflicht/* Lauflicht Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int pinsGesamt=8; // deklariert die ganzzahlige Variable pinsGesamt int pins[] = {2,3,4,5,6,7,8,9}; // deklariert das Array pins[] void setup() { for (int i=0; i0; i=i-1){ // Lauflicht Zurück (die äußeren LEDs werden nicht angesteuert) digitalWrite(pins[i], HIGH); // LED an der Stelle i einschalten delay(100); // hält das Programm für 100 Millisekunden an digitalWrite(pins[i], LOW); // LED an der Stelle i ausschalten } } 000077500000000000000000000000001272363601300350775ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/LichtautomatikLichtautomatik.ino000066400000000000000000000031341272363601300405710ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Lichtautomatik/* Lichtautomatik schaltet die Lichter eines Autos ja nach Helligkeit an und ab Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int lichtsensorPin = 0; // lichtsensorPin wird deklariert int scheinwerferPin = 2; // scheinwerferPin wird deklariert int schaltgrenze = 300; // ab diesem Wert wird »Dunkelheit« definiert int wartezeit = 1000; // Timer-Timeout sind 1000 Millisekunden long timer = 0; // Timervariabel wird deklariert int lichtwert; // speichert die Lichtsittuation void setup(){ Serial.begin(9600); // startet die serielle Kommunikation pinMode(scheinwerferPin,OUTPUT); // scheinwerferPin wird als OUTPUT initialisiert } void loop(){ lichtwert = analogRead(lichtsensorPin); // lichtwert wird eingelesen Serial.println(lichtwert); // lichtwert wird an die serielle Schnittstelle gesendet if (lichtwert>schaltgrenze){ // wenn die Schaltgrenze unterschritten wird timer=millis(); // timer wird auf aktuelle Zeit gesetzt digitalWrite(scheinwerferPin, HIGH); // Scheinwerfer werden eingeschalten } else if (millis()>wartezeit+timer){ // sonst wenn er Timer abgelaufen ist digitalWrite(scheinwerferPin, LOW); // werden die Scheinwerfer abgeschalten } delay(10); // Pause } 000077500000000000000000000000001272363601300333615ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/MatrixMatrix.ino000066400000000000000000000036331272363601300353410ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Matrix/* Matrix LED-Matrix zeigt ein Bild Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // Pinbelegung für Arduino UNO int reihenPins[8] = {9,3,2,12,15,11,7,6}; // Anschlüsse der Reihenpins am Arduino int spaltenPins[8] = {13,8,17,10,5,16,4,14}; // Anschlüsse der Spaltenpins am Arduino // Pinbelegung für Arduino MEGA // int reihenPins[8] = {9,3,2,12,55,11,7,6}; // Anschlüsse der Reihenpins am Arduino // int spaltenPins[8] = {13,8,57,10,5,56,4,54}; // Anschlüsse der Spaltenpins am Arduino int image[8][8]={ // Bild, das auf der Matrix gezeigt wird. 1 = LED an, 0 = LED aus {0,1,0,0,0,0,1,0}, {1,1,1,0,0,1,1,1}, {0,1,0,0,0,0,1,0}, {0,0,0,1,1,0,0,0}, {0,0,0,1,1,0,0,0}, {1,0,0,0,0,0,0,1}, {0,1,1,1,1,1,1,0}, {0,0,1,1,1,1,0,0} }; void setup(){ for (int i=0; i<8; i++){ // Alle Pins werden als OUTPUT declariert pinMode(reihenPins[i],OUTPUT); pinMode(spaltenPins[i],OUTPUT); } } void loop(){ for (int y=0; y<8; y++){ // zeilenweise werden for (int x=0; x<8; x++){ // von links nach rechts alle Einträge des Arrays geprüft if (image[x][y]==1){ // ist der Eintrag = 1 digitalWrite(spaltenPins[x],HIGH); // wird der Spaltenpin eingeschalten } else { // sonst digitalWrite(spaltenPins[x],LOW); // abgeschalten } } digitalWrite(reihenPins[y],LOW); // nun wird der Reihenpin auf LOW geschalten (da es die Kathode der LED betrifft bedeutet LOW eingeschalten: LOW = GND) delayMicroseconds(100); // das Programm hällt 100 Mikrosekunden an digitalWrite(reihenPins[y],HIGH); // danach wird der Reihenpin wieder auf HIGH (in diesem Fall also abge-) schalten } } 000077500000000000000000000000001272363601300332155ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/MotorMotor.ino000066400000000000000000000013331272363601300350240ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Motor/* Motor dreht einen Motor erst rechts, dann links herum Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int motor_A=5; int motor_B=4; int motor_Speed=3; void setup(){ pinMode(motor_A,OUTPUT); pinMode(motor_B,OUTPUT); } void loop(){ digitalWrite(motor_A,HIGH); digitalWrite(motor_B,LOW); for (int i=0; i<256; i+=5){ analogWrite(motor_Speed,i); delay(20); } for (int i=255; i>0; i-=5){ analogWrite(motor_Speed,i); delay(20); } digitalWrite(motor_A,LOW); digitalWrite(motor_B,HIGH); for (int i=0; i<256; i+=5){ analogWrite(motor_Speed,i); delay(20); } for (int i=255; i>0; i-=5){ analogWrite(motor_Speed,i); delay(20); } } 000077500000000000000000000000001272363601300341765ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/NachtlichtNachtlicht.ino000066400000000000000000000052311272363601300367670ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Nachtlicht/* Nachtlicht blendet Farben durch Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int LEDGreen=9; // LEDGreen Pins wird deklariert int LEDBlue=10; // LEDBlue Pins wird deklariert int LEDRed=11; // LEDRed Pins wird deklariert void setup(){ pinMode(LEDRed,OUTPUT); // Pin wird als OUTPUT initialisiert pinMode(LEDGreen,OUTPUT); // Pin wird als OUTPUT initialisiert pinMode(LEDBlue,OUTPUT); // Pin wird als OUTPUT initialisiert } void loop(){ for (int frame=0; frame<900; frame++){ // frame wird von 0 bis 900 hochgezählt if (frame<150) { // wenn frame < 150 > rot analogWrite(LEDRed,255); // LED wird eingeschaltet analogWrite(LEDBlue,0); // LED wird abgeschaltet analogWrite(LEDGreen,0); // LED wird abgeschaltet } else if (frame<300) { // wenn frame < 300 analogWrite(LEDRed,map(frame,150,300,255,0)); // LED wird ausgedimmt analogWrite(LEDBlue,map(frame,150,300,0,255)); // LED wird angedimmt analogWrite(LEDGreen,0); // LED wird abgeschaltet } else if (frame<450) { // wenn frame < 450 > blau analogWrite(LEDRed,0); // LED wird abgeschaltet analogWrite(LEDBlue,255); // LED wird eingeschaltet analogWrite(LEDGreen,0); // LED wird abgeschaltet } else if (frame<600) { // wenn frame < 600 analogWrite(LEDRed,0); // LED wird abgeschaltet analogWrite(LEDBlue,map(frame,450,600,255,0)); // LED wird ausgedimmt analogWrite(LEDGreen,map(frame,450,600,0,255)); // LED wird angedimmt } else if (frame<750) { // wenn frame < 750 > grün analogWrite(LEDRed,0); // LED wird abgeschaltet analogWrite(LEDBlue,0); // LED wird abgeschaltet analogWrite(LEDGreen,255); // LED wird abgeschaltet } else if (frame<900) { // wenn frame < 900 analogWrite(LEDRed,map(frame,750,900,0,255)); // LED wird angedimmt analogWrite(LEDBlue,0); // LED wird abgeschaltet analogWrite(LEDGreen,map(frame,750,900,255,0)); // LED wird ausgedimmt } delay(10); // kurze Pause } } 000077500000000000000000000000001272363601300336365ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/PingPongPingPong.ino000066400000000000000000000222321272363601300360670ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/PingPong/* Ping Pong Spiel LED-Matrix und zwei Potis für ein Pingpong Spiel Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // Pinbelegung für Arduino UNO int reihenPins[8] = {9,3,2,12,15,11,7,6}; // Anschlüsse der Reihenpins am Arduino int spaltenPins[8] = {13,8,17,10,5,16,4,14}; // Anschlüsse der Spaltenpins am Arduino // Pinbelegung für Arduino MEGA // int reihenPins[8] = {9,3,2,12,55,11,7,6}; // Anschlüsse der Reihenpins am Arduino // int spaltenPins[8] = {13,8,57,10,5,56,4,54}; // Anschlüsse der Spaltenpins am Arduino int pot1Pin=18; // Pin an dem das Potentiometer von Spieler 1 angeschlossen ist int pot2Pin=19; // Pin an dem das Potentiometer von Spieler 2 angeschlossen ist int image[8][8]={ // Bild, das auf der Matrix gezeigt wird. 1 = LED an, 0 = LED aus {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0} }; int death[8][8]={ // Alles an {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1} }; int death2[8][8]={ // Totenkopf {0,1,1,1,1,1,0,0}, {1,1,1,1,1,1,1,0}, {1,0,0,1,0,0,1,0}, {1,1,1,1,1,1,1,0}, {0,1,1,1,1,1,0,0}, {0,1,0,1,0,1,0,0}, {0,1,0,1,0,1,0,0}, {0,0,0,0,0,0,0,0} }; int blank[8][8]={ // alles aus {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0} }; long theTimer; // Timervariable int gameState=0; // speichert den Spielmodus int animations=300; // speichert die Dauer der Zwischenbilder float ballXSpeed = 1; // speichert die Beschleunigung in X-Richtung float ballXPosition=4; // speichert die Ballposition als reale Zahl float ballYSpeed = 0; // speichert die Beschleunigung in Y-Richtung float ballYPosition=4; // speichert die Ballposition als reale Zahl int imageYPosition; // speichert die Ballposition als ganze Zahl int imageXPosition; // speichert die Ballposition als ganze Zahl int player1Position=3; // speichert die Paddel Position von Player1 int player2Position=3; // speichert die Paddel Position von Player2 int gameSpeed; // speichert die aktuelle Spielgeschwindigkeit void setup(){ for (int i=0; i<8; i++){ // Alle Pins werden als OUTPUT deklariert pinMode(reihenPins[i],OUTPUT); pinMode(spaltenPins[i],OUTPUT); } } void draw(){ for (int y=0; y<8; y++){ // zeilenweise werden for (int x=0; x<8; x++){ // von links nach rechts alle Einträge des Arrays geprüft if (image[x][y]==1){ // ist der Eintrag = 1 digitalWrite(spaltenPins[x],HIGH); // wird der Spaltenpin eingeschalten } else { // sonst digitalWrite(spaltenPins[x],LOW); // abgeschalten } } digitalWrite(reihenPins[y],LOW); // nun wird der Reihenpin auf LOW geschalten (da es die Kathode der LED betrifft bedeutet LOW eingeschalten: LOW = GND) delayMicroseconds(1000); // das Programm hällt 1000 Mikrosekunden an digitalWrite(reihenPins[y],HIGH); // danach wird der Reihenpin wieder auf HIGH (in diesem Fall also abge-) schalten } } void update(){ switch (gameState) { // in welchem Modus befindet sich das Programm case 0: // neues Spiel memcpy(image,blank,sizeof(blank)); // Bildschirm wird gelöscht gameSpeed=300; // Spieltempo wird festgelegt ballXPosition=3; // festlegen der Ballposition ballYPosition=3; // festlegen der Ballposition ballYSpeed=0; // Ball fliegt am Anfang gerade if (random(0,2)>0){ // per Zufall nach Links oder Rechts ballXSpeed=1; } else { ballXSpeed=-1; } theTimer=millis(); gameState=1; break; case 1: // Spiel aktiv image[player1Position][0]=0; // Paddel Player 1 alte Position löschen image[player1Position+1][0]=0; // Paddel Player 1 alte Position löschen image[player2Position][7]=0; // Paddel Player 2 alte Position löschen image[player2Position+1][7]=0; // Paddel Player 2 alte Position löschen player1Position=map(analogRead(pot1Pin),0,1023,0,6); // liest die Position von Spieler 1 aus player2Position=map(analogRead(pot2Pin),0,1023,0,6); // liest die Position von Spieler 2 aus image[player1Position][0]=1; // Paddel Player 1 darstellen image[player1Position+1][0]=1; // Paddel Player 1 darstellen image[player2Position][7]=1; // Paddel Player 2 darstellen image[player2Position+1][7]=1; // Paddel Player 2 darstellen if (millis()>theTimer+gameSpeed){ // timer für Spielgeschwindigkeit if (gameSpeed>50) gameSpeed-=3; // spiel wird beschleunigt theTimer=millis(); // neuer Timer wird gesetzt image[imageYPosition][imageXPosition]=0; // alte Position wird überschrieben ballXPosition+=ballXSpeed; // Update der Position ballYPosition+=ballYSpeed; // Update der Position if (ballYPosition>=7) ballYSpeed*=-1; // Koolision unterer Rand if (ballYPosition<=0) ballYSpeed*=-1; // Koolision oberer Rand ballYPosition=constrain(ballYPosition,0,7); // verhindert, dass Werte kleiner 0 oder größer 7 erreicht werden ballXPosition=constrain(ballXPosition,0,7); // verhindert, dass Werte kleiner 0 oder größer 7 erreicht werden imageYPosition=round(ballYPosition); imageXPosition=round(ballXPosition); if ((ballXPosition>=6)&&(image[imageYPosition][7]==1)) { // wenn der Ball den Rand erreicht und das Paddel trifft ballXSpeed*=-1; // Ball wird reflektiert ballYSpeed=random(-2,3); // Abprallwinkel bekommt Zufallskomponente } if ((ballXPosition<=1)&&(image[imageYPosition][0]==1)) { // wenn der Ball den Rand erreicht und das Paddel trifft ballXSpeed*=-1; // Ball wird reflektiert ballYSpeed=random(-2,3); // Abprallwinkel bekommt Zufallskomponente } if (ballXPosition>=7){ // Ball im Aus gameState=2; // Spiel verloren Modus wird aktiviert theTimer=millis(); // neuer Timer wird gesetzt } if (ballXPosition<=0){ // Ball im Aus gameState=2; // Spiel verloren Modus wird aktiviert theTimer=millis(); // neuer Timer wird gesetzt } image[imageYPosition][imageXPosition]=1; // neue Position wird eingeschalten } break; case 2: // Spiel verloren, kurze Nachleuchtzeit if (millis()>theTimer+gameSpeed){ theTimer=millis(); gameState=3; // Spiel verloren Anzeigemodus aufrufen } break; case 3: // Spiel verloren Anzeigemodus memcpy(image,death,sizeof(death)); // Spiel verloren Bild wird aufgerufen, memcpy ist eine Funktion, die ein Array einem anderen Array gleich setzt if (millis()>theTimer+animations){ // Wartzeit gameState=0; // Spiel starten von vorn } break; } } void loop(){ update(); // alle Berechnungen sind in der update-Methode draw(); // Darstellung ist in der draw-Methode } 000077500000000000000000000000001272363601300347535ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/PotentiometerPotentiometer.ino000066400000000000000000000017131272363601300403220ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Potentiometer/* Potentiometer zwei LEDs werden mit einem Potentiometer gesteuert Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int ledGreen = 6; // LED Pin wird deklariert int ledRed = 5; // LED Pin wird deklariert int potPin = 0; // Potentiometer wird deklariert void setup(){ // werder analoge Ein- noch Ausgaben müssen im Setup initialisiert werden } void loop(){ int value = analogRead(potPin); // die Variable value speichert den Wert des Potentiometers int redValue = map(value,0,1023,0,255); // diser wird mit der Map-Funktion umgerechnet - hier von 0 bis 255, int greenValue = map(value,0,1023,255,0); // hier von 255 - 0 analogWrite(ledRed,redValue); // die umgerechneten Werte analogWrite(ledGreen,greenValue); // werden an beide LEDs gesendet } 000077500000000000000000000000001272363601300332135ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/ServoServo.ino000066400000000000000000000016161272363601300350240ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Servo/* Servo ein Potentiometer steuert die Position eines Servos Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ #include // einbinden der Servo Bibliothek (library) Servo myservo; // erzeugt ein Servo-Objekt int potpin = 0 ; // Potentiometer-Pin wird deklariert int val; // val speichert den Wert des Potentiometers zwischen void setup() { myservo.attach(9); // verbindet das Servoobjekt an Pin 9 } void loop() { val = analogRead(potpin); // Potentiometer-Wert wird ausgelesen val = map(val, 0, 1023, 0, 179); // und in den Wertebereich von 0 bis 179 überführt myservo.write(val); // stellt den Servo auf den Wert val delay(15); // wartet, damit sich der Servo drehen kann } 000077500000000000000000000000001272363601300344445ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/SynthesizerSynthesizer.ino000066400000000000000000000031771272363601300375120ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Synthesizer/* Synthesizer erzeugt eine Frequenzschema aus den Werten zweier Potientiometer Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int buzzerPin = 8; // deklariert den Pin des Piezos int potPin1 = 0; // Potentiometer 0 Pin int potPin2 = 1; // Potentiometer 1 Pin int toneHeight; // speichert die Tonhöhe zwischen int lfo; // speichert die Frequensgeschwindigkeit /* lfo bezeichnet einen "niederfrequenten Oszillator" - also eine zeitliche Frequenzänderung */ void setup() { pinMode(buzzerPin, OUTPUT); // buzzerPin wird als OUTPUT deklariert } void play(int myToneHeight) { // play-Methode digitalWrite(buzzerPin, HIGH); // Piezo wird angeschalten delayMicroseconds(myToneHeight); // Wartezeit wird durch toneHeight beeinflusst digitalWrite(buzzerPin, LOW); // Piezo wird abgeschalten delayMicroseconds(myToneHeight); // Wartezeit wird durch toneHeight beeinflusst } void loop() { toneHeight=analogRead(potPin1); // Tonhöhe ist Wert vom Potentiometer 1 lfo=analogRead(potPin2); // lfo ist Wert vom Potentiometer 1 for (int i = (lfo/10); i > 0; i--) { // Tonhöhe steigend in Abhängigkeit des lfo Wertes play(toneHeight); } delayMicroseconds(lfo); // Pause for (int i = 0; i < lfo/10; i++) { // Tonhöhe sinkend in Abhängigkeit des lfo Wertes play(toneHeight); } } 000077500000000000000000000000001272363601300336435ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung1_1Uebung1_1.ino000066400000000000000000000023631272363601300361040ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung1_1/* Übung 1_1 Blink Rhythmus Dieses programm lässt eine LED in einem Rhythmus blinken. Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // ganzzahlige Variable led wird deklariert void setup() { // die setup()-Methode wird einmal ausgeführt pinMode(led, OUTPUT); } void loop() { // die loop()-Methode wird immer wiederholt digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(1000); // hält das Programm für 1000 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(1000); // hält das Programm für 1000 Millisekunden an } 000077500000000000000000000000001272363601300340055ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung1_2aUebung1_2a.ino000066400000000000000000000055761272363601300364210ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung1_2a/* Übung 1_2 Blink SOS Blinkt ein SOS Signal: * kurz-kurz-kurz = S * lang-lang-lang = O * kurz-kurz-kurz = S Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // ganzzahlige Variable led wird deklariert void setup() { // die setup()-Methode wird einmal ausgeführt pinMode(led, OUTPUT); } void loop() { // die loop()-Methode wird immer wiederholt digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(500); // hält das Programm für 500 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(500); // hält das Programm für 500 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(500); // hält das Programm für 500 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(500); // hält das Programm für 500 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(500); // hält das Programm für 500 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(500); // hält das Programm für 500 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, HIGH); // schaltet die LED ein delay(200); // hält das Programm für 200 Millisekunden an digitalWrite(led, LOW); // schaltet die LED ab delay(2000); // hält das Programm für 2000 Millisekunden an } 000077500000000000000000000000001272363601300340065ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung1_2bUebung1_2b.ino000066400000000000000000000026001272363601300364040ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung1_2b/* Übung 1_2b Blink SOS alternativer Weg Blinkt ein SOS Signal: * kurz-kurz-kurz = S * lang-lang-lang = O * kurz-kurz-kurz = S Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // ganzzahlige Variable led wird deklariert char signals[] = {'.','.','.','-','-','-','.','.','.'}; // variable speichert die Signale: . = kurz, - = lang void setup() { // die setup()-Methode wird einmal ausgeführt pinMode(led, OUTPUT); } void loop() { // die loop()-Methode wird immer wiederholt for (int i=0; i=1; i=i-1){ // Lauflicht her digitalWrite(pins[i], HIGH); // LED an der Stelle i einschalten digitalWrite(pins[7-i], HIGH); // LED an der Stelle 7-i einschalten delay(100); // hält das Programm für 100 Millisekunden an digitalWrite(pins[i], LOW); // LED an der Stelle i ausschalten digitalWrite(pins[7-i], LOW); // LED an der Stelle 7-i ausschalten } } 000077500000000000000000000000001272363601300336455ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung3_1Uebung3_1.ino000066400000000000000000000036501272363601300361100ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung3_1/* Matrix Übung LED-Matrix zeigt ein anderes Bild Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // Pinbelegung für Arduino UNO int reihenPins[8] = {9,3,2,12,15,11,7,6}; // Anschlüsse der Reihenpins am Arduino int spaltenPins[8] = {13,8,17,10,5,16,4,14}; // Anschlüsse der Spaltenpins am Arduino // Pinbelegung für Arduino MEGA // int reihenPins[8] = {9,3,2,12,55,11,7,6}; // Anschlüsse der Reihenpins am Arduino // int spaltenPins[8] = {13,8,57,10,5,56,4,54}; // Anschlüsse der Spaltenpins am Arduino int image[8][8]={ // Bild, das auf der Matrix gezeigt wird. 1 = LED an, 0 = LED aus {0,0,1,1,0,1,1,0}, {0,1,0,0,1,0,0,1}, {0,1,0,0,0,0,0,1}, {0,0,1,0,0,0,1,0}, {0,0,0,1,0,1,0,0}, {0,0,0,0,1,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0} }; void setup(){ for (int i=0; i<8; i++){ // Alle Pins werden als OUTPUT declariert pinMode(reihenPins[i],OUTPUT); pinMode(spaltenPins[i],OUTPUT); } } void loop(){ for (int y=0; y<8; y++){ // zeilenweise werden for (int x=0; x<8; x++){ // von links nach rechts alle Einträge des Arrays geprüft if (image[x][y]==1){ // ist der Eintrag = 1 digitalWrite(spaltenPins[x],HIGH); // wird der Spaltenpin eingeschalten } else { // sonst digitalWrite(spaltenPins[x],LOW); // abgeschalten } } digitalWrite(reihenPins[y],LOW); // nun wird der Reihenpin auf LOW geschalten (da es die Kathode der LED betrifft bedeutet LOW eingeschalten: LOW = GND) delayMicroseconds(100); // das Programm hällt 100 Mikrosekunden an digitalWrite(reihenPins[y],HIGH); // danach wird der Reihenpin wieder auf HIGH (in diesem Fall also abge-) schalten } } 000077500000000000000000000000001272363601300336465ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung3_2Uebung3_2.ino000066400000000000000000000053741272363601300361170ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Uebung3_2/* Matrix Übung LED-Matrix zeigt eine kleine Animation Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // Pinbelegung für Arduino UNO int reihenPins[8] = {9,3,2,12,15,11,7,6}; // Anschlüsse der Reihenpins am Arduino int spaltenPins[8] = {13,8,17,10,5,16,4,14}; // Anschlüsse der Spaltenpins am Arduino // Pinbelegung für Arduino MEGA // int reihenPins[8] = {9,3,2,12,55,11,7,6}; // Anschlüsse der Reihenpins am Arduino // int spaltenPins[8] = {13,8,57,10,5,56,4,54}; // Anschlüsse der Spaltenpins am Arduino int imageNr=0; // welches Bild wird gerade angezeigt long timer=0; // Timervariable int timeOut=200; // wie lange soll jedes Bild angezeigt werden (ms) int image[2][8][8]={{ // erstes Bild, das auf der Matrix gezeigt wird {0,1,0,0,0,0,1,0}, {1,1,1,0,0,1,1,1}, {0,1,0,0,0,0,1,0}, {0,0,0,1,1,0,0,0}, {0,0,0,1,1,0,0,0}, {1,0,0,0,0,0,0,1}, {0,1,1,1,1,1,1,0}, {0,0,1,1,1,1,0,0} },{ // zweites Bild, das auf der Matrix gezeigt wird {1,0,1,0,0,1,0,1}, {0,1,0,0,0,0,1,0}, {1,0,1,0,0,1,0,1}, {0,0,0,1,1,0,0,0}, {0,0,0,1,1,0,0,0}, {1,0,0,0,0,0,0,1}, {0,1,1,1,1,1,1,0}, {0,0,1,1,1,1,0,0} } }; void setup(){ for (int i=0; i<8; i++){ // Alle Pins werden als OUTPUT deklariert pinMode(reihenPins[i],OUTPUT); pinMode(spaltenPins[i],OUTPUT); } } void loop(){ if (timer+timeOut= 0; leuchtwert--) { // fadeValue wird in Fünferschritten runtergezählt analogWrite(led1Pin, leuchtwert); // und als analoger Wert an die LED übertragen analogWrite(led2Pin, 255-leuchtwert); // 255-leuchtwert erzeugt genau das umgekehrte Verhalten delay(2); // kurze Wartezeit } } 000077500000000000000000000000001272363601300340405ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/WahrsagerWahrsager.ino000066400000000000000000000021351272363601300364730ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing Creator Kit/Wahrsager/* Wahrsager schaltet zwischen fünf LEDs um die Zukunft vorher zu sagen Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int pins[] = {8,9,10,11,12}; // Pins der einzelnen LEDs werden in einem Array gespeichert int tiltPin = 3; // Pin-Nummer des Neigungsschalters wird deklariert void setup() { for (int i=0; i<5; i=i+1){ pinMode(pins[i] , OUTPUT); // LED Pins werden als OUTPUTS deklariert } pinMode(tiltPin,INPUT); // Pin des Neigungsschalters wird als INPUT deklariert randomSeed(analogRead(0)); } void loop() { if (digitalRead(tiltPin)==HIGH){ // wenn der Tilt-Switch geöffnet ist for (int i=0; i<5; i=i+1){ // werden alle LEDs digitalWrite(pins[i],LOW); // abgeschalten } // sonst int myRandom=random(0,5); // eine zufällige LED wird ausgewählt digitalWrite(pins[myRandom],HIGH); // und angeschalten delay(20); // Programm macht eine kurze Pause } } fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/000077500000000000000000000000001272363601300301045ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Blink.fzz000066400000000000000000000241261272363601300317030ustar00rootroot00000000000000PK8Dg9\.&Blink.fz]ks7UڪN3SpjT(J$-; ~P$%zQ<5 ؍n{_>N:|<=jO^gt6ώ?8:Gc\|z!X熧{d՞iUK=zq>8eP H>8rhp4{K;qٽN>u:}_9bl8>?vNLg>~ 8jwPjCR~ņ^sCT.ƣKO:gۃhpi:dz黳×4?os<cq&]:fIbQ~6=f4Բ܁dt:X~ԼM-k[nY؛7u4:/xƆ7dTۜN QYꗨ&O_FyTKeLjRm-xQYϒZTket۟x۽Tc4p!vNH}:GVlԏ.GgxXzGFVh-O0u)z0y2:K+"mxeh@j6L9zǣh11Jgn%i]u~j|p+ql4s:u)j#_>k.OFTAlƸ`q?Tû)Zލ!m26AFkh>Z$ӓLժ#2އ>,*,os4=[+u89ί؃+F4N(N}6o)xnp] M2Dwsz{9doR}!z;Uy#+"8[Ft\%'TXfdFᎴ0=N)˞pa/{7* ۶;1٢s`45?vFaOVo:on'Gx{k|3 ptF|dle_OUtS*Ge6O͆y?aIⵙg&ӽ2m؁nz֛$KzAFGao8 U0 ) ]S݇($V⿽0S#a Kx J*DŽUb\N@mR| Teu4m]Kt9Ra#˒Hw\ٯ–E폚yޱD%-^_5kR6Gy,M‡)S$5-p$6LRDߣv\N$2}޹<o7]W2b1:|E}O;Z>2my[鱛E27FۅkQ9\_4y OS- yxwBIy46D U$xM-ANt 8̚Ӕt <ՖTbi<Қ e2Uu('*j9E>vL?i-֑p{u9D:-m _d夸 Ę0mOcjǟwvǟ]D|~ն{;m9d[w=Z3/mU}agB6Ua&fS:U|v{{>.d{pf}}}lOm ْhnGŧ?>?v?Mw4DBȁyb4)W9HK/(c J~Ϫ=0 y7O[t#3s~Ȭ٣ԓF^kUQ.?*g<>V^9*+Dkd9( ݾ&Aك!5({H=f<d˽g=d>v :m͇6ۙ~x?} o"T_)R+3$K6TGAJ~&7pi<ߟO<otV^Y5g3Nf^zZdt8RtiT~u.]׻eڭKO?LZ 'h>ŻQJt|:fǗ/~_eo;xCGd߾|qX^x8NJT1^ԏ7w8b>5ednt=]4݇ly/;|n<9?v:EPL?߻OVQuS7y>?DZJGRx`ukevI鿙 tD'؎Ui_ƿ?eT~Ry|0_m`2*OZQ;kZuΣɌfZޝ$x|8.W\STe5Gp(th<_ҞQ 1,]N"1d ZvEA̖]ve,גBU30ΝmsBEĒ e,{ >p񰖩mt:tڣRT>uK94pt\,'Tt!|P:cҸ{pxUԹ4Lh̀Ʉ.Xh\:ص4.Z/cg9'&6tȍwdQ& ^w#T2SPmY$r$(u3NTFdݥDW]En˖Kt+Չg?b*jo]jB.AKW, ?2HeSDIPobRAw Hd4T `,,0Ԥu(?\(ԭK t ErgDuL p{L =/Yb*`"h=N[ 0r5TUhm8AO."56 NWI݄`$P/xZHݲep Nh]LU`$ژHo*YCg- P/(ʺ\Z9w9kE}giR!Y iyȌЁ\ҐdıACJ %lJdE>Ij9̓ 0tݕBq~_F(RHҖ]hLT`X8X6 `/kTC-jqomڑ''A.ӴIpi ɍD'a=QwpV( %0@jIc_FɸJ'g w}Kӯ"'ėj$wBoAؗ,j$:e'IjјFzhEٲl]IoJg >yccP1u)(I~L]5F'ċ00 59DEc%O uK@*x0PIi ,nLƼSnM5,Xphsmj7Ԯ\Cde2rcv&Kn+%M>]B^Z B[b^!L+!E+h ' ~<-Kl?%J(-0'P&YIUclrU^vzp i6-FcF6^(5\A *W6ؖ+P-W [@fg$V # -#$ $\&5*^f^%Ҡlj::r0K6`yAlvʠ> EIe0^u9J }޽fj!XmYz+[^Dj2F1 7/guɬo !bD6Ph&,kRX.`젬o{3d6k _g"rfS*r8ui!CD $VdG=GT 6ѥ+oouѩH|KD-"W5"j$ ۊhK?n GC^$YqmKf0A d@H(E,p'0AL3UޞH7Q6Μ.鲖)b !9bYcH{<~:YD6${; ؑ\4gIA'"J.d:Sf<$q@⯠ÿ5 O#?(YnP!$*24`ue$(0a9<̞aF5:ﭣgVPQN!ƕ!A$ -(ADu0Ff@q bJ7:; ] ,5C<uybN33)+QF1N$3^܍9nIJy\߅9ZI =-t.y 4f[mhIH:C*jf22%7rj6R$D!U 6"~Pru@MW`t^QȜL.I ݦ3~L8Q'wSFhH 0Kǰ36Id]*v$2٫`"YPٻf/Fмn~y%}/=\P%3{6=1M nˑY\>(&=stnlQ/-R0A¨e,8G,s~1`&:U˭f9)@SyE4eku*^qQ&" OA3஖D]'W`[k=-&mdaW/h XV0$g-yoCXc;O@4tʹi"eF2jܤhݭH9DQY46oB$I6D#*kT0b䍫p:^h'՘ H RQE *"@h G&w9K$͉̜#Y|dk0тrPEТp3w\G9QF0PAC7riz\N‚zBq}p1:;1#JKDR9,v?6IRTMgU"mm%Z+Q<Mj T`GOjWzPIXokҏhql-?&яH Ǹ##~cְlُ-7F-ƋqwIM$#X!]W5E>,1&_{*Ew긲RhV4UU,8zq&xf rVn^\y>"4-y9g0Bbi[>&aHk\<$`(rAWwVJyK(!Gd& Iq-edK0-=2LWHCШK85ůV'ѭf v6;k̄ˈ!unhhhݤQ5Gai RɯعK}hiN&~PjT2[N)F+R)4?<|s3Gj7LL7nĪ.ZJQ Z62~)EvETě X^CpO2S-Fs 7瓧-.Wڥ)jšve*3m1j.5df-kZu̴0\*;(Tp:wz5[SrFi:]uԵ:*[5XEci:-{^cPFN5BU% %TPu-#\8Zp沶I j//LTPAᾑ rb0˭4czB#ucL=qK"yDfyUoR((lTLP9(G?drΙpnܭFM݅*WJJ,*@&|k<&mZkč4ik3p)#- 2|Wp D[tQ>8k:ef)%5 oɆ5,DɺF:\PBmnStI,l(skibUvq`c&=X+ 1)OɒfLwf|*+0%ܨ+YdfVYzn͗(۫JV>T&|ʟ._"%QyCq{sTP0^dˀ[B7FѤGawZF/$Q͵lѴmVcM--ږ-6e[4-IRс-ܒm7ݶm"jkZ.ܲ2`|"fR]qQmHsHɭոhpe8 ƜnsSk^+~vno!Stmj,UFWRq"kTQ,\3^2::[( 6(YQF\j"noHy9I((6i>F"f:Ecֶ%ږd [\~Өvhq@QL@Guk<%f.FynrB>s\ks9FV;L,<mG'i.A秭0`9cofM(Lw1BVu4گUW]u9MrWτ5V)}[l>cs.3G;K:#"ꕙ죬<.)]p%u7?Ŕ$)E(T5 yT2`.x冰#\OՁk_5jGW;~_*}2X]ޞ&ukbgAzX~S/ֹx|6kd 8+JM׿Z~%6[Wwޭ^:'ҹQ? ^:W`7u0n^N$>7gS&7zέ}!j7tްza|k^˗ɷUn^o e*TZ5-~*x?}|qVr_>iV6\9+:RoiW){xeMt<._'ӽc'Y>y:>X])[f>ݷY%t~ .׷}-á =o3<{1)bշethyw]n/u|wL)V z G\PqyiOM7-LRanNWhiE{Tv|F\/m{H车ϫ}={[4s GL4v|B⫒[4sh6o>],}e+S-Y3Rpܵ=^Aآk@_]/̋]7;Gw- [_v㭻IQؗOIL] 2'{~+=ҩY/vm/E^O'}]_h\M@JdzkO|ZjQt'2, e7a K{Md.wAӢ7mL"WFoezooy?zό~A}fci*}:lT>+!h9P{dkMҵ;#ZJM|Z_8] TC$=µk01 nMʴ})s8Dϟ]v |kĺTE\,?PK8DGrX Blink.inoQN1So/sd-i 0sziҤ3"wI#@)|.-0i 4 bJС>Sx䏤#(B,lJ(\vB A;] }GBz>=9whUgzxG/G?s1ΒL|rt[7'[_tNGzGӗ&uN3:;J#9E+-zq>̇gطe8vhtX. WR;ʇޑxko2\ O⥳|/b{6.O7 /ᙗ(sVis9beG~AJ}M/wEÊÉtx{8χG$wAp8d>Mf8yu>Ad^d *~ξ'`Cl~~mYҿ՚з ״ߍofx:;]u>^ z/~=~EU[UUokSu5xQϊ^'K%?=:#1;>d:7qx>qz-eҷ|ZǥFy>x32=|^9j)!_- RW~?;VGW.qSsN%H>yiȔ1W|`MEv>hJڗpk| Նꛮ߶}+:5 JovJ/֔>4=|䡬z ).M_GOWuWˮVU[n2 ]xܰ-+ wى\6-~ĭe5YGOQT͈bȬvO!>5VK으ۑCzJ55NI޺;ih85ľ8nB;Ae;)S]3N[Ǔ%Q_xme>+cd|>P_\&SAR7Gw2s^R6r$?g6@Bw6oU `~j^ nølnsdFCU,IdX`Ӷ'ofvf֐V/blQa5K3{: +RiR\nvO.s9^^&EG :e@,‚P?gudqG +Gk\U0w;MRB_?/ν@Vl`7]_3(֬ܘjo5[TwcnATc*UbzcKZ1O+}V5,5c}p=;Aan78;ms7nA-ܡ+ܬ}mKvWvLM+vL{3Bw]:7_:?=9ɧ|.g/}.ϸ47v&ys_.)Cd~i)퐕K=Ŵ*,<|jv GbpcӿHr;;Plg}YSո~ gq3K3&͌z!=bPgqS¥g =sbNmJMghmsRa%R#W|K?ةdS8nXlzp6g;+^A\-'D*yЩT;F o"EMgt n#Qv <;_tOpo~_|qrܞߚŴԬ>oxiVT]@詽/uS&Kq[S,[u޽'5BZ\Qy*w>wڭߏ)SDV.iHOle7e;$Ƒ].tvPtdol4y6|-٨AT2)4XIx'G#'eRdd"pϮgn0S!ST*)ϴ<+ ՛xlqkxj,F,Ɋб!4 LS[(;cgCQ.쌢}aHGQw" ӧ$4s}~Hr`|T"i6se{G'A[>UzVגHw~-+ΖY\z #Ik3kGZiy^ܖM,,,q^JȸTJpItw,mGgG:C,?El-;aPqSZ,5j5qhZ `)R\|')n)EH>$ED<&mSn@ICv}tf_'g<)kn-3?󔙈җB: zcʛ۹ -XM-+E\$d4ql7xʤupJ +ZBF5Knq㋞=m 1] GGv|NԞ+Z,Ji+C뮡UG'bĝZX\nҰ$ X^&+Xr(Xڵ!V?w43Jhcm;@+铐 hM6g&Mu.hIƕ cG!ѵMywe+ФîKۀIGZ!]%~|k F: jkLy+qB̞)[(\%ymZkm}/˫60$>r.ͦ[*fUs_kk!tm(e#etm5$_e(0b<%s,NAFS׃|1lO xy<=i88?:+@=IgҗzY .zɅ^X*o շZ• ĶlN/bM_Rݨlt?!x:g{,.]dΟ(jVv|TLY|G[U̎?{|ӫU⋴Ln&u\9@]fSٻJW +OI7>L:ښ/sv|o)g lޕ+eX'KOc[n0Cu.Ìfu3#u{ݞ'_6{ S:B}}I0:}2L\z+y!]baΈcs-{TiܾR g@ґkT]ʗUutґI7+ښn=s).5ZrYrkTkmjc3޽_C""Rz7Qtjʨ+ܢ -D]BdPh?qIv&{[m#J7 )70*jNj=Di'D>-7Brkt@l]ttm5%_]{;;\nln/FL3[mH˖w?{|-O˛'$xi>>9]V{Ն |8'glÇ'yzz T:OX^fNJ]y= ﷳ?x ;鍏bxb4ՂT2WxdOD'xfer<~&^ NzǀSg㣣I/M I<»<^;/{|z_#tGx.3z%PTql )GԒTBYMm8j/ ƅi+HØ0:`.hĨLu%P*W1P+7+M t}.|\(B#fE[E!2@(& Ud ~d\;P5٧A[8Z.(X}J(G:T Gb#W ֮ 10$=]֚ȔPUQZrCh6\`2uPQ]]^ |sW"yzżDW!ͅa( 4XD5v5)4TףC5ټ_OC٥ 򁃦Rtb%vi檺Hqp#1j +]ݡ''UBe}hz S2FȢC)+6WF񿤴Qy{cfI@J " ѶQ}iEiñSAFIA/+hH}D0BͲF&&)tFgFXhb`h!Q1FW"b] =҇(&»o.H,5KD.rC% 4ǧܨ7Íz's%ܐ6pb>6W8cch5vŸ㕏}chIz![im@*2.Dnr!o=dLB_ E&a [qEqSA\& 7H a ])L1p.S|",-Y4w+8KQ {o dA4&Lby!D#`1ƒ9C[wЯ+v8D{"[=1=HDkG蕦x*~b1OSSHDTvo4BX&0$Rd{P@QA1&( F!CS5y,#B5+EXfߤ nr4`BV#b(0j` %FQzHI~W Ga tEkF^*aMȰ8¼,)"ӼI-]dc|=Kac(Ed1_],TH,ĀRB3`'U>4ٔ S0WF\j]O_8n "t::k ^&c=(>tR{HoNo MSHy+DLs"VSb8+*@A(j-J o%{)k ^_|u;G&J7V X͉Ԕ4d;.O?,fUK!|=!{Kr1q;i+R%UjJŁv Qc!*8lI ΄cC f\i55ƚ̵cƚEK墡P^k[j2Z|iPJFTB&&TlPeMxNLc.E83Q[& R(R{D o2֍dmeq5,7˜bГ4RV$"B8LmsQ,(# ")v"9ǀ#6#j5*RM(r]pۺ ,U:Zx49\b CSF/2Lb+}zT9GwU2cV,BWL50YɚJ:dIor8yN 3+1K׎=9y'J?XH8rA%~P 5)o f4Dq"5BXPD*Ŋ(2FTY| C~[Y@XW}M3Y%}P!|YU.X)cC#7)+ao5iپ.ab)v/X"9̬AboaaN7aFq'׻Fn;7Da f&q+)b$jTR,3_3^"::kRp\lPp~cٚ~q~+DIXIiDxXe$0,b `RWrf1GԫU@ðTH`<\ x2ܵ(#'5U`E1gI*;Y6e5BNEˢWM 9$)(rm+5 10 wiodE(Hw!1M a/CW~}լϺ"E|㬫-=yhx<)wNHBYJz&DnDH(r{I1اcaaܰp[L"GU`AddJ|Ŕ_ܓrDd*8l"p(X#z[ǫ-zqi/ߗdPp:\_Odp\\/6x 04<{'5ovd<N#wXߙuoV$Zް6׷ty3vcgm絵a6K¾1 z7gE 4+/?MtR<xz__8ս_h_Mx*+::4s͇ ^,t:[VzCUG8)r06}wƱGu-xxV8D y" +l,tpخL𔍻..ݹ§ k>D#dd'h#͠J\e+uDZLoxSPgȔ4 i (#l!wܦD.HB7vuNnoYKPBShB0~Xt[do,2OZn M#>-:RBaGG["%,oMԮࣣd㫷Qc= 7:<>xtsd@X\]YLTT βܗxٞ5,$i\v|rFz5{=>ľ=o %l 5Ҙ%nx)G&O؃Κ7'qm~L t3f+k<=W9~!%"u>KVz$@=>tmGeRjَ?j47 kuHӎfM ѴɳiՉPDPK8DG br Fading.inoN1}GcBD֕^fntHm| wI3wιkFR 6{hir'KH`dp'{&GnΤSJ,ET%+G?TͳISE'&C\ '˶5[iibG>[e>*J*'7BI%^`ÞfPUd Q]U[ie[M=`jݭnSU@Me2SnRDz jRX'WlBcZ \ҰY'D⎕yTGaY XZO b7ڭc=޽`y FlipFlop.fz}r#95wm!GuִUe;e3'M(SpG\dɈ lkUDxOwq:sFSr&WoĜ_xu70G/w$5L MH<s^]N.SM7g}at?Iy2 Wgh6~s-|w~~uYjv͙JJ z3r)u_g :n{\!S\Sv9\ 2׊ ؀\ /YE­͇g񛳳yߧNl5^ܟŝA_P_iWh" Ӓa/-9q_'gF_7| xtsv"*~|}7O>oΗ4`:]F=O)߲9LU~:>L>8t7|M\Eϓ0{+b0*6ջ~sv4~8[^ոz)Z_ւWEe>4^7 4ꊘ+ͯ(eȡʜ/WO>\V/ju WB-/A-hB9~5Wn^ @--q3 g{_[tABg Q [tYĄefǓd}1zvF?q[{wQ {^\4$Z"eRve}ZNnc@[ʎ(=1V}bݪK%jәϷKOCVbL4':e오O8-Ir%Mg~\g}Z濜bn~c>f$}™͎IӚ[ Z<|]]ir:>df}©.JBZbvLЧv8vy")g/'Izm޲e[.|R/*oQW g.;']vM֧eC)/ܧ윬O89Yps><rVe2Mvp6>-"QgvRާO8MyXluyΉ\N6O8I>f0MYpnc^/u7q8|x )ݼbk]lMua-&ZvtYJwO}/L*qDI qf3fh 32̩n~.Yop1>N԰*~ W S(5x ֦ojR/{nr,5aa$_lݫB0ˊTR˱s+U׫ _cwxWi),ЯLA4m 4Q"T#f ,ҔR?&kf.R<,(D|b@R 1w rl;Enm4 xf~VcбQ{c H:@U 4LN7)xXT%D:`dzP\==QjRÑnJrPRAɩz*1)kt*\4L e,KHFWRB% rO5x4Bmhѩ4УƐ߆ջ$} ϯgzf)ʳxx{p+B'߆UT>ǩFA{Mͫ7wV"!HHXIaZO'w;m{G- @xcP,],⮔ϛ2'kp)ڟg<5 X(fE*Œ+w7dt?.d+HXʈw hʺSȚ]=ogT][w+N]9`ecq>--< ?gT~xki4}2&Q}o$482dށ (,cѩ{pW#){tt9Y6=IÅN'grr9Ʀφf&w=mU"WJj4}pϽ߇/Yyf|+s0(c' 챵4icܚ2RsKeaMր;gYUYr`S,Y$cLZ˟I٘|ׂy*He*۽e[bV.k֎6!ww`l$G%I A$W [X 6Ck*,U,]>wqp*R a3\Z[S. '~KpBc09J w?Qߪflmaa[/~̯qWE)hM=kQl1kqv*H¹^ TlY_lSL8R6v53 KR W_7N)"YzC "o0?)bQJ Q2m)\JR9cqtd>H)KBkJ4҄פobCʞJHb͉vC1 qn ֽ$ ,X]c)'0uU,b\]]”Sxpy3tBKP9A[eř~a?Yw-G洛lf?y1)[~5Hz {$/B_0`8==ؾ?61rlȾC@78Cb$F{.RL8^:3Jw1o]# $Gޯ #ŀJ"pe/pR%ss7[j~m 4 X)[<}m1 <±=N}!d hZMt81eI//-y.XR `y̅7` ߢ#5{xRŽ1w v<;vc43uLQmHv1!@H;xj*BqZR-׬UsUre\'=S3֩_XISV2P".D) [BHEHE_:2~&l_l6On'/yze[={]Wt{a0W:V&Xds8d7rs*T闱r: 7ؘ: ų7x ɳ8tFN- gHIoڍN ˎHU<*J.&84q[^gs楉\㱄8BCƮd9yFl3: b# %sg G#&XJ4[m*ȘSڃ˿HS-On+؝4b`ZT#q²Vw'NT&nPpt^t@s*:qckiM8s;nP}Lb-/F;"T`B46駅m'j03XێnF,19vd)kR'* ̘˚}2c+̏Hs]n MJ>(l(3aa GIj+qql>/Aqd6UoXx}|PcYkN>% 9x#̻(2o3$+}:4xa1HC#Kڠ4ԑi(YܚTQY$%KbtDTR9aewÚa%\rڅ5ܚ6d6܅i~* t.$M,9f)a)waDíTR3o3\e 6[7q[L7ur`±M81<*3#22rwo#,OlN-Dvx!`9ŰD8v:5Z6L Y\ ٜ-9c}`>4GmϺpɄ16jE "L$qǗHSU&Dnh)[G|bq[G!oJXO/LjJ$O)N?E`] <ɺbvB$jDUH<9ZXb9bS3%;sX5s5@®4M12Mõ' $ =0 ],8+(*h W>bkNnYU'],MfF(REw&e'b-. kሻr{O}"-k{+M__3(| "RH=;hk!& E`Ecz9+(Ke s#+[/Eay̬g$ؤ ;"A x3A]9RBzE*)B1EVf7(Ψ+78YvqoYit֞Д0VHNn\'G:54uwa-o(h<աblQ\^eu[4ClJe*fVG r6\N&Fc#g-3" +j&4LHܬ!jtf]#73LBRKhX4Y7%r& rݐ˚˚r^&>xhʊ  ]0?nN%#󆇪zidzh{kA=z $ XCQy GcDY#ҰCF` BG<ĬJ^V@LEY"4uj2F>К:XCCZ%Y! WFS ]ɚkИ YSsbKNLL^&>ѕoHlxؖ TgjY.ʴ{0r4<8D B^$G<xs mscCs%M)t=2[2#p0:m"ljit:&U/w挧֯=-RLju9~='P&+<ڣ i W F&4xb'4{K"n G^*n!g.6"xW[s` yI{ cwgleƆ0?Ⱥvn+m5ˠᩌD4WQ+؟:ʼn| Z+9_[ ,$i,w<iRb։wv@w~Dk,# bF%Tp ~ ߩ-x <^PƳp%<"G0ޝ'-82٥G??% && 0R9Teh/긅<9׀"q ?Asз4LEƏb~B_,i]} j?L[wc}!Aנ{Ϳ@=eVjs_4Z ϢB4+C6Ly ]A|LaL)^-KK/P̘k7"GW丮DD+ef|7Y׋D ̂B4UX**KU³iA 2p+Ml$ t]{3lH@ nqy{1t9rE~1`w Q}IE^ɱ46>4qJbK6a]^oLgb @ x,x7QP7`5FK^j+~a58,RxsP3=BA@(tp/]7 ^JrF%<nk?pڔMEŜ\ٵqhɵ ~kh;}y׮ҔvUXm&/3!?{`01*,@A0,~+# ‹KY$c"+)΅6QOFuM qǂb 0O@_q'w7?;x AaGS2eF<Yx4XI)-%@՘Y2d h` -ޫ((K) BtjtDU~1Vwbq8څTh_2a#ط Kyج^!͜xpe 3x [xGK`Inh]AЭ~i#-zMSۧy;RMnOjeX a:2p P(:,h Z]f!WI3g}̗RqRqBCJ3Tf]q#ÀkOBB#bhcYMh<| p  n/AbY zݳ  FL*;ToPX"`l( MC a)I0OE`uMBkVA]Y{ ?V/ Фc~T:2"ܢWĞW */-*EE_Ń1Cd!'Z9h'ObM0 DI12 @T4`xThB%Qv>`{R! C'uG~m%G(h2h0 Q]SB]G V/tt ю@zU'yx>`LN 1x 2ypUPiNhTH2h *a`V) o w[ʼ6 <O3ovvYpt$e82 <)oE `"g$ }Lq2yHT$yCkD A-Y`@[˩0PY0Q E h+H>8 ?A?/X9`nϠń(#e@1 /RJ #} 40Dx\BHVjx&h=|y{ѧ#c(GA;T#h(G4d$pJpFmA 4ǼCzSѶ鈐cl84=` Z:>Eѕa}.Wb4) DW*YʂF04RLIP7l {~x ㎡Cgf%̓!jX!^\,ƮXÒM26MH @Ȫ8/}Џ[t%b %`!<İI $`@r'Ȳ4K)â$sc&p"/V ".1E'G$ A  AFmMh.2KR D(;ƝHY !zBcq>IG J=x `v)C^nD2162qZh8ϩ -~DBc0  lc_plH&e~]Й%@%9u)i>'9vo:ZHغ$g9V䬋!0eY$Ҕ} bN/ F,obFcW8(r'xEB*(;ua $ (`x59HrfrœŐȻf>[{vg:nd9j 5# 0D)' bCN탫?a,:?}G䕢agA4 Mj~h` 5 B rLx$}lDv5j̜1 첤+S,}H$p]C?FׇF=9gLWEK"e\DQ!!:BBvS)kw(3]Jca0*R y#avʠ' NC"A N*"^\;>N;8t11],%,f`ꅯ 0^6^ t.Isd-K?` L (%(G/?5 9 C/װ0x9;!+0G81vcdfpX``Gb gc r Dy  BEO1܂%Ƞ[1L1M24,|xuW3䞹A_R@R0qiT8Rx׈B$<&lH~rbve/ d"ǘ~6`:ΒO! *h~Q#~Q^ 8M6 .ChMCd0C`60MWvYj+̄`a #ijC bCگJ94`0 `DAxv*БY`/Fyh{ T n2lԩ gNb} 䞡ԔdAD4nX^[!zE |7xA/蔉.`! E}Y~g Sbpg)PxtA;1,N朏 ,{A5 J'xhC19"LH=\hkWIf @4OQD,3׃@q@shn;!hQ-+M81hD6H7J!RL-w?#)r P #B4&ITh0 `w!,#2cs)a40P A G&1a9$ڍ\zNzB A 6!È(H` H-BEw%T'?$hPC!tP8Ѭ2T@l+krRU7eM|ݎAޭdJJv c8# ~Rc"װ0&KJf >").1&`.$:7]& I?$ذPIu+) Ed9a *t֪SV8‰[;$&8IB+¢mF0ӈbl)T &a2"/A"HL hjNL "L| BaOE` : ^!n¥_ y9Ԡ -p33!ҕ=AAа9 Dq3͑>; zA8(>F> ^ RޮjGT_})7J- aN 8` g@; @AWҔ[) 6\%RxiX<|!c+ fdIJNJJ 2w/,ݓ'p ߃jC[IaR~=މT{K;8.ER8*WԮĖ?yL6_+P(S5xk|l%-E>D!5TJK O-š>Z DBUK* *VђP˒P`kKBPE1RH3d Ԟz:TPF,fhL(7/7` 1F@OH^p =D#7nH2`oxlGNRš(\#)v@qGhjp7!"b&,0+ΓyS;て0kR5[EîȖa%"S"`r$_0Թ g'0YtML-a4!Ut0JdAor(XNr3 gx8]yt;ga/&a8KA?+~ )H3tD1E55O R£mym]%drªP5GÈ8WXAQ$\$L8%p)u0ۧѓ I(JcQb)hJcQb;EEל PYRX)_l%y?+Jn/]p&ϯA FdI(t̹K &N G"Lx00+pbNȃ+?[X#X_/8hޔx{ψRQohJ2+ doЙV⌺/Y %( Ζ(XPBʾlI٢LS"OLĤ4D\xXDq"0(bpp0sY_>È . >FNAE0*0 :SѴ`]s@L~ep{` Q !'Ȃ%aLq 9ISP`j+xa&0턹]$~+LHR1 6h0‚אЖy]QŬZ̺bxmUFxPlMHo YJ0{&n 1(Fa`l/#}pp58qBHptr̫ô,ӘL +I={LY88 .T;7epB7_fp}8~8}6y8cvvvvv8ҽś߅/WǑCI͵s8_Qّgf]v-`.=s;osEӿ9s.p#:;'V~V{on/"O))$4B ?_>qɡ{8Zϊ 0{6Ҝa43f8_%zx׻OeyQ,Nڇ{'8ǧ}ϳ8̲n]s'gvny67 Zc8B՝rx,=`7מn~NBqlEnRqf RRw.>GW0Qb$) IyHC1WD4 (yǼRiZ7HTMGb_[c8J nOM=읍ib;16)QgR4w\kXV~NuUNu9!ď`|#[YIt37R۫GgVM*[؉)_/Jȅ_k۽NL' nP_ÇErN`}nªae2G?2ItYj)<谴KDX] |$UM3@A£GQ7IM^ʱ>[oݿbv W)DJ߮T~"Ndļã;?I xk8Gㆡn_OO{wO{kL\]9d<\ F0‰j(w%ZGp$8Nv8.;"P7é3Dhl$:vф )- !&\j'4{K:Л+8ޣ3 ۍQDnPmS ~ųSJ`@f\i'HK/% +@]35CHtdKDVz5XxT -Z˃'>jܭM<.צ7x)Lbݚ`%ҶÃJdlkP 궭o¬{@ƹD' ILUEcl3dCC^"nxh NG֥r8i{Itg8h<"/;,tDNv^X^T^6Ul2cw~R70vbԏlB*%о2Dڷ(iނ[Ҿ@&̆8Z74*(*gϭlA"<8~  sǰm)OuK#N'SL탗y&24nEOb.nIkj{Yag&=!Ga1wR;DcUA'`"3vbx%ݍ:x^b͖yniȅF0 % b 4Ե`3t,VbsuY0SadDp$몭iKbήN.sL1&k@Tl(Wҵ=V>]7KZY4' .'Ee5-5\t`됥aFQn8$JNȥmۂo ;L37AZǏ$fD̾_bΑPK8DF!}6 FlipFlop.inoN@}9AޔpQQp^١L`osZVltw~7ApR&O*edx#fL= 0VyX$' s20$^s$4#^I¡b y FlipFlop.fzPK8DF!}6 >FlipFlop.inoPKs@fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/H-Bridge.fzz000066400000000000000000003427741272363601300322410ustar00rootroot00000000000000PK/xEf H-Bridge.fzmsȑ.}non3ώ~/jVX-=/Ve@P E2pDB"|2ϟ>ܝ}?>>Wݗoofǿ|X+rZ{ˌv7+c_K[}؍RWon⏫ǿ㋷O3|,|a(gOowoΞo_ ~sW_6*|o~~~S9/Jy"uݟ/>>>{t6䆙vn.Nk1S_]޿tn7q ŋW>.I"㻋狫?oggMog}~7HtǶ|gBކ[\>|s>_pٻp2/޿9{zT/noÇ~\to7}Ьu7p,ߘ|>=5‹ xy5Po~/Z}{󧧗_~p7n.޿GD[ϚMxy\\3wm5ݼeznqob?>3z2Cdv!˻ Y50M~/eIy7+?|??|KDL8*02ʿ99DV1ֹ]hTr79*9͸5{w_- ]Jn-uxqr^t.w3nL ~~pΚ3oWoj(Tיl9rx>u1ܯv [ [`؅Z3eK8$1 $}\`yѡ#2K&ƃl{SA!NegƏbՃdYd}6c^4XبX1Xrzd}(=Z 84R4'āi1,Mu`qr>`&>B4QD}]yi`Ib>vgiqīC@VAq KÁՀ,N Ia1/,I51,MG4Q꿂p$r*Yzī Rc^Wy_vZuGAnدy?+e:~!C}d֒'#^Wy_>..[ǼDi=(&6GDi0Z,, D,D>q`)`QZ}̋Sc]X,,OG$@awHc^X+Yz}P=U}dq``yqK C@~QrqAu{`yqK˓/,OG\(>e Meg %c^88| -\Z ֯z>?no|7B#~uWX/qjt)tq~cquK/./|Оem@JXaLHWqt:~Q??Wv".T2qŇS\Kvdl/>ޅBT"o=׫+sK ؃y;v3FGKQy/YuevxP} 'Yq'O+Elxq׳/."YӢOWɯQELsztjo^#j lkO̒e(hbcإ@9JwaLpW%hJ-q4׸x#%cpq+;cJDHR"X.ヅWVxaneN )&++6ߪKDre^/Ed'S2J@(AD)P.$ _BjF\%v(pGR{MY(9%r{sf0>Sve5uSf{SnpԵO'O&2I'K5k7)K&ZKew?|'\G*yE@~p>oow7Ǐm 7X92MR@jHΙ鈒TRl7 4qqNqe fKf`p?p[/vRweh^z؋VR3PBHZ_>-aZd":}iNEpȖE9]s`oMV_ln[akWmE8/DذcעPAuH;G0T .E/Ux JƠ]Oն˧Ux{tubzaet@G_׺:l?g˷軬B6P(cz&}Yqht@lk5(r@M֖ w@NW]$yS ׭O~#pP&K|tnOt_Ap%)X}yܫ9O77O/9=OT%}O_EǛ|\>} g>}'YӨլ6N39/s)\%8b量НL+R LIhlu4m$g"Sdl-❧kLP e7.g2iQrI4yO ןv.AFp.42vџ"]K w:j_te|aeMV`U7ؤ TyYciYIU2_Qjԍ"h{ks]1׻*:tVDz=%JQ`}24lX(.6mR 4|[&dd*Ogj_vㄍa$QE5?z9!DEhyBCWWW8FGsţÓ\(8. Tyx$Bam* ,` "]>yxDN'b31b44SG(4ba@9cp 7i(dR6k7=_lڑAc\DQ!%L8Tl!DjpFIu}>6yБN e_v#EaDrI;%Dؤ{3[ ݎ9UJ{ ?2RZzb}1CɁKuLR! ?Ck:p)0ʧ" 8^8XF&)& b}M9D{b1o&p!"~a~7Ͽ޿㻋狛6ۼCxk[¹y9ո M#2a`W{kxϯ#dl.?+ n~/.t4_jrr-w2-Foyi6cKJ :vx꥖QlF-_~K'Uq}s]ǕfY>ok` O7>p)L)G.񿂧@/dӘTFnҿ;kxt2\"|<~~hTkX!lbad&4妓k6wnS}$ gvBfD60dWw"KDJa*ǜ{4Y)F dy\6?g*fc$.M2x돂+5[Zgbh60=eE,XȔU$ J*kn%d,hQ6:Kr7%\R\6l\]ɷ{/y\eM Wx F"鷮~Fr:D,rX S֡S-띉S)O*2qJ+N5k7-N&bLC'ci,_ͻ߬xތ  %aN[.Ş$<2xFWNlb.nbG1Y ˩^W_FUXaEOGA#3)'S6 hTF' :|rcI !.?ZknzxQ7FPJiw@)'vh^$ZvR eq{PJ]YQ@)cҸE4F0 Qve Лc%tgR%|Suz>wMhY5dFtPc&0^]`d"~* J׬81|IoD(qhB'$r.d]X(P|e^Q }uwh*Y|X7+L n*DF)tL`aF_8)Se+J׀b@eENȃ<\R }M Js=MJboVHÜsuY]K쿽|~ x{p3z6W_MUq3LCE\ Q}E>,'ZUȺxqF^3؃H+VQW|XM%u,u֪;j.my8U#˘@#9HpkJ]#欌F2NR}ڭ# HmKcd-2SNz+ԒUBɵ+e!]TtSXl۾xM]s*}/%?b/ IjSr!"Dsݍ2<*빷WRѮ.E%EWs燻wk*o*/ۼrϷ+|4ڜIUo)SM20MKdޯUeL^4'"*aj~cޗx_)6ѲI7Ζ ?@%ajCW_F-(1Q$LNeNy NY ֽ:%,q}0EG6)\)%M5k7=R(_ԛ-Ah9eqoc0>.voX8,?[\mg ؗ)Plʬ3KOVm\vzVNb|ʳ1d ѱ6-#zIб磢qd }M.ƀqvo&]Δ;s?ƯTf&냯_?:Q#*V^-kסY}^ `"|1.=kYWl8+R:uGQ4yG1ir;zdiG/xvNܱErW)ki>KFY lm"11@gU5Y{G"~C+LqT$:z&0DZWY["Y!!Vql!%*%UٛGFP"Xu71SZmZ16C)Kj'J EhXfLˢB_]]QTJ*yEblL ׻y 7eM}2EƸHKZBFBFSC(ey9FbJԠtf-U?M옆\ږSC\X B/%]3}gonˡlN#dW͹%wV5ŶcIr}\yelbV,X[1!-(+LLd"?Wޟ+v06^a!FpwXǓ!y,Of$^𞽭JXqmYl/L 'v0-! LpN~dk>M )SjMhZP0YxAE"(ք.1 UwV^Aa*ϔ#U}ܶ^Yw,SwjRYAN|S$Y]g@RUZ9=?mQ<GSuk2sXW_vp /*5-M,~eWu, Fr24Promue8\.N&t$eIb\)=hj%ZPxk@qdkxV_v-Ⴂ[Y 8Vh\mDR HP3>tx{4㵮02j_i &)\Ixa+;> ĕZa%.`5k7)(-(0+ ~eWX{J"v,_yTj6Ӛjkb dm UĢپU[3i*ΌYCvøIG6ĸ&7{5k73(7(Wܑd,lVZT&TKӉ3t%d]扛!j1j=ڕHPѩ.4 '{U'qik$&=5k72e a,lBnj| b *&`Z"8-eҵ\ r`aM!7"ļ쬠f_r+AYvXAIhWK&iפ MJ2תb&8U^@bl 6_,L[Lb}cw,~`||)q*}'t;z nXE2NqSZTD2nQ#>oC*ZI7W[>kUF4 Hܑx3oFjw-jbRnB/Y!RYf˃'H345k7=VźXW`];)uX^6{LP8* ;|\+]՘0GHr͒oqq4R9*Y(ZP%ũV ׬t;РrY6kz)MM,Nĭ-r֧۝>n~u V"D\WׄbrM=J7%5t;Р:y*N5k7 (|$(|pNMqOp2m,X}bXӔ}_"}SM&GA$ C#S*6(w88tf[űűj%w@x-LL % row3ί-'5^D:՝0h+g<[(PXyTYV&Â8U_v`bYxUͫ.xՔU0|}E@ w"UNk;}_ ]2E6 HV>`U8U~ ׬t; 3hd2hMYU%M 3!Pw:grJp;j[1/;.c\+AS6iuUwbYELHqz/ını2j'YnJ~C RCsl]s1tTbrSx13|(Vi)`qAE0oikn%ZP,Ao$ypҲix/*@&dc,eĹ&8kP¹Mql5ugIV;EeDⰃ8T94f[",A+ͻUBh)(HK:w)_ˢ˚R3YPP@ f175k7(?0FPz x4iQA{sTLM ]<;R:A .|%mg Vqt(yeA|jvA|*djt*qAYv(A*RPD_Y3cIppڄvdMNh Yiͺ$KנQnp7TCU8Uz ׬t; 3h%.hᮀ2jcl]ԹL6h"\ӤA+Zt5=,ژrڼ3ߠw6-* 9hޡ4Ytf[b-ʂļjvԄ̫AC[a*Ph0fGO\'=1K,$W#4Mh`.)N2b[yDYV6˂XU_vpbZUbVY).M(UtTV1c+GQqbZT3],W):J'5#`iAl+^\) ^}M ^a%w&܌z)\q&W`hdX$4jNrbhמ}x~~Oa41/1RfʧC D8sxtf[%fA Dl'3&ܵ ^0[ǽY;iw{YM"bxZ~1ZZ 5XbHX«Zd3* Vxd'.; kǩ"a}MC C"b5vr팛d"j4vGF_5v}MtKd_b#qS^׫9*# P`{S^XDnʂķ2XM1gffIo_E1vq1e.3.[n|n*H^XLDa [MC xAS%] \}M s ]5ﲻ]\'FT,үY>3*.xܔs[1O+&Kkj` X$%<̤ȗc tItd  o cS8$ޡTt1f[E . N +vA\UvYECC3!G|$մ"bTIXTufQ]ˮω)m-+ B(*>rBxؔ1fADN"QOHĘly%YDdym군rb``*Zݗr~#!q媔SȁS bhAA "X}M te V[]p+9%2MW^aX$B+uV*|q,e-]`e!e+mI\:.fi=Wljĺn!'Ƚ3<5k75(•9j4LMIjIK CS]r/|!/IִE6TSɎ5* EvA\ ׬t; *h]WZr+k=%j~f zb t[l5k7}WG_Sx(nLſt^a匵F1V. >x偎S%^ZDnDZ&jdLI6( }Wey"aem^&au\L.D&&3.eɗҁ3 5Je+8AYvhA,#bP@aS/V~c]Jыgڝ>.TJwb,.mS("aW9HDf|AD.6Z6SnuOa,%= ඉE| DI}="Nu\,U&/sHee N+*X ' .׬t; .8(&7M'ML>YO^T~) {'ۉ*Z~j*DL%UJ/rgŔpP4h!AN5 ׬t; 5(d=ؔ[TTZhAAu3կ)ъI=qR Zї`!GX$K#px1p,+Sݪ D(/Fh]΅) b\}M b\pW͸vr0j[)Ƈp&E%* WG_Zp8P=vu46v偌S]SDnDЮ=m:ōC%855LJ?_~&6}sSo}o7RFjfr?7?j)~ݿ=wg?}O/.~GY=< ?Ƭ\Xnӗ3v3wcտwٿŎ;N-nt84=twowçLJgKy=2O/n.ÄPO_7 _?ǠLwwepԿK 4}/֬.5ß is<(f| Ud1V,&&FHbedD0>+ p<#廵~>C;Dl@/$E\8WiJ, ywɸo?à8<|}Wb:_=\޿WofWoߝE,:򫋋_ϯ~P"4 Ï~f珗aVopw)`C茛* 9w?}|x|>ݧw?ܟUh> o?]uv6-Z K .x7kmC`}&fx Zdkx{7LJOuuߵ=!N⊿F@N-JSl(`_Usa`,VlKsV)ùᙹz?NWJۙ oYδ h`éd&|,ylb0bkÿ\Ư-4 9뭍nb\ϥ>6U<<^~0 ΅WГUb[ }⹷ꊝqgn>ZG2e0ۈJ)σwglEe|H W\FqP.[DG ?x$Bʄ+x&|*GysQ @|*ÿ_p=-΅ ZFrVk Oi\|>mhN2AbT&÷JW3x;^L.|+zo<r`W3JU0=B7X^y' z0*RϗzD^m(rD["So 7AK7-Ti"s_9q5s!cAӀ#I`2^+݋m @vq%Sձ>t8zupx>JDpHq|koo->@=7^[|{f6~Z*g͍$?|aA]7@|Q 21Dϳ(h>Uz Н/t 2~ #GtEl@ ]-4_oQGQER`R`R`P8xj *l*ߧ UfT Z1eZQ0mb4s{n89A_$_ 0PwʳEϳ09-sYtlŕ*`_R$%y~{2|=rU/0/rQV-K,JϝR5B?" g;Pz8hb5q0]Qof05@AXkbaR£s0Lv$d#;SU 9(DzT GBHtxP=w7*lpe7M ,X3aØkvrִov*>6؎ɴ I4iԕcֵn IXW-`&a [pvSQ\`H| Rt38'nQ`Fl_+8 Zq>>AN@[8[~t`5ʺ̷|ţQ{# Vhp@׉[$%Mr;޶g3q]hwK"7o(b+-NN8 XEgnAƀDc g |!{ xA1,bM8 y;5P\ p&E JpgS`gxm@f@cv^4ܑ 6CR$`40 Iۜ; u$4<(!l1 ] FS?x;&pDl_A(xol!@& 19P~hG-}e@qoހX yK=ޣ!Y_pU" D&+qniMP03X@ͥ@Qr P#)h7m=`jJA%QΣ3?ق Y] R 04Ahν8c_ hi ӟDuh:XE}`BG$=^[x6pL$͒npJˠ[4LTA530zyMth1vsM*!a` ]ZXKa}UoWN!|_q"8ޖ;X603$/`I4&ZFb? _L$ky$7Qp 7(A MN ]7w熣ՆhDO>8 ?C;/r*%$3_@Q4.øyRkhScIwۀ+ *. hk2GWP-`O=H8Zg IB8 X|ƂpFmA 5}ݒCt B|hU#ZVm^JV-GtA|ZC :!4襛4q@ f6g;V~H1 |ȀvsRD5XF ^$\/<XD l9##p@V#ĄW4:GQ#<vI<+03"6^֌\OV3O gX{wr.kafQtN4ρ3%NbV L3 _nz||]dJMrrSw}/dAD= K{`eXM06@J= `i8SfF2i72 fŗhq T0=9^+04F) P<6-skrږ3&Q}d r6<]%t@gFwz†[WnF=aK]"^ UEGmh  @mGwI1` @b &RL ̙20&b ,ɻȬ"}Gwdg':f j8 9# 0x9+ b]N=Sh8yc;2 ' FTZa'Xk B`rLxYǘ(OqBGрkޣ $zj3 0 G`#q!xdf@5x=> |eX1䍴cƖoz4’;4~khăQ=. #:9cvYD%"iDd"ZH(>.r!!kkQ0u*Y9CT8xr8Z`D 92 8d>`4̨"0ۣQ$灏~oBal}E.R^>ꅏ |i`%.)RV~ߪyeBc~4G24D0d"y A ttH-rcI)0S*LKC" \;*"ѳ PcAɅ/as/@s*̠o{ σe < =S'V;qr;&9Y/vÇϡ?}~/u`jѥ&sWJcfWLf w7=@`b:1@amk?Z a\Y3088S$~Fk+phOz.Ѡ=rj" ῞~U˜М[L"gV߻+M☘ ! "hA OF%`H-"|'i$OQ,b,E4܂qvɿ,~4GdXD4H wJp`z01ՠ Czš$עzȂN A v@ C(ڸH H-BGseV7=:scj+,QIp*C-O Co`8D뛁`G]uZ\M%#H=iѷNɎ!)uH0Hc/A"H\ jjQZ)% E|(Cbvr% M8wA(83x3 /p,kZq^OWʹd o7AD(sdӳE9Q[D U97qt1G*R/¬~0X2gPДt-) 6\6[L/͕WD3-^\LcI~߳R4 ][dヨ*%Xmù)B- !lig/ Wj0\M8]pɈmYjH 110L<7PȠjȥVWٲat?mG뺣Q [dl ak]Y#T j~*lPeK8X Kpf2рA&R2!`ވhoZע喋FL )#]OvD&MAf}/5r4 U"5"r'1[ M.ԾR]pFxrߵh<&uZA/,Fb< 7GE< )oaZLk:c Jd4qɺ&gldB:G` rnfθ; KS.&L A1D :HaY$43cVeӗcѵƢnŅly,f,t8pLK=VRZqslLK򀤃oًaLٔ9a0G d0{S9k9C01~.0a&ăq\A9)nbMF'6 nYrWcEuxZ@LI+Yr}ּ2qzm"2[]6L[{vt/~w?|󻻋椣[,ُ+uz{͇wwXF&`+EWrdݮ᫅ȰW뮀{Q9۴bYu_?\}0cz|wq= Ǐ}7\o/?=ovOgNx_%v5Ti{~xytĿ'"}<`'Uc,8nAxu/gͩe*.[nr[n;|1ns9匕oax *GJuo_ *&*յU恨TJ.jXv ؔR]} *R˫RV?Q)ԶC776 9YːʭRY KR]@Tːr9g!kN+0:Ymm'-_R壔ՇRgߧS˖Rmi)"7?m3K|}}{79\F]).F/oaO77O總?nI_0O l׸྾}?s%L~^J/\6r 8ܤTG(Md<:,T+d&eXC񀝛]0zS a+zeñeʊP2&p7&*ɹoY,15ӇDz,ס\(zF-7߀ X˥nϭ;X Ndѣ.~˹:0=Hȁ턉kb֣,,xQٕGh)5"R#LjTJdmRȇȢGY﷜Qu h b̲k+mK'I8 =|3rCz;Q;!-GvRhU1]6qj^4c5h5?]Vߚ*Wp½=.s Ᾱ|@Z'tl/>=ŕfB6W]_ >|} rջ[ͤ,~|`EZb\Tn"K8xܛ*γ5p܈+M"\eQfRĘCd)/ mS/ a7 ˢt[q/ꖑquWcF[chQ*\Šb,Xq 1Z; ƽH[C%$" qf: -) H]'9i2Xĸ {Llk-W,ĵ5wy@Źpb\-?w)yX@͌bޥDJ>_hض-6n@\yT)+̌ Zь "\8Ԡ@W> ҿKNI%t@D%dFsWAhxUcxMY6x@W8U' ]8 ޕ8w2ɂ.5%Itcm|k2NI5E"Uν_J{RܠAZ8^z7iNeXKn`)GnvDY,bt) Fgdt &՗o][1]A-E}96\1\11(v|eXi|pq5M +-c$[Nɱzg+I]gPcRUfVCe<'Z?aK(ͫfx*ݰv՚ہL4rSqW;P0&䎊Wk@ < YwaTD/YЂ"V;s!1T\o0uH4ATGZc&bU$VP\, ^92旒֭BJ{ISrKT/r=\;a1c\;Rjzgkjf`zƛ@cq0T 0h93fYfi+V*])/t']R W[MI8AUƔ,g"XvB "\;0SNwS a50-dg.e*@w.ft@cej(Vd:)+K;^1:wCͱ?BpH4Y.y+}ف;&)H^183N0fjU1]SCج)KEzDE9z]i{@bzQZQƌ}9jvt xOtiV16GpsV^KjW2"D41 GF ΅7sMXڟ6p˞U\,=mF` fA*ї=L} >#j?aQ3=%_g:O,k3 ;|\ +/٠(U1(1TsDt3*ī^Ȳ@Y@[~cJ3UF:bU}RK++ 3ZU&8LmK+Fc bDVnD2Ȱj -{ )bLį&TY/,̄İA,WUvNjxɭn.5V+M [*V9w@,20_SÇhTWjvߏVuE5e1c$l=NlǬ @:z h?on` , d2M@PxqU_ /{7N26aѷcRPaN wJ;#M @RƂerJ24ֻ$CaJb THȠdx) ,26)]az#Y1i M+vr\: >1LR~Xv2NRE* KAdjZJqѷ$`*Ns?ơErҷƎKԚAPS)7%rKSb;z4v!骰ATj/$3J2̃DqHAt*Jj bR8 &)(2U) :姤SKSIazMukA.+)YCEj:,dsZї=Eq+ۇ_y~#&erFfʩ\cזh]NnT-ɳd(DF]΅)L,L?ba+M ba`XXK&Yg0ՁRZ$tmo6 8 K"a}2IF} 5?}e_SDqA+jo9|zj %3SuMlx;J3*Z՗ bD`KM&Ćn" ȩ2 18"d]TbJB&y@a"."u>$>4X ~2,!:6t;!:@NMf}8|Alfc;lLX<^UU\)3 ArG_j֔S1*,ap!ѰɅD ǩҰa8 ;4킆)i&lK+" W)c:Rz!l'U$ iA'Ĩ` &:8I0bjo`ǩr)mc8 :>%:.蘞RRYgZVX^# FlH!;}l,I3BKdc|M4TX+bj`ǩ M"c8 2=d2)5iCX#" 1˞SdL 5EASd DqA,z%))T/I(¹UkQڡM0@djT!{ٗ5RG{͜C.M 9d.ĿqA+vB?)ZP͆2{CV_1o<Q\g)(}G_ڿ1g|-cuu47Se`YDqA,x)SRTz~@u,`Fh'JJjPRb9դ 18"d5!3; dMHpa2omo6c @d.\jw; y7|PHlvA!.ՙO>TAkvA+bjk*kx16w{K|)W8fB <,t'B_OZ)TC4j˃ Ǧ? 〃Y> BV2 Bf$d)H)G U"!#V F%$їx̤a|V4ġ!!ASMCjV8aQ|xAԪ㩕Zٕؿ1$ll\PX mSDJ8LiWG_ɳrD xP퀃xW8U5A,l$vܔ,cg+7*IDe!HXG_  $39|jn˃8 〃(X> VS姤`nQ~T,< v"<Sj* 5t; 1N~Mem8|A䫥ɗd/6^w+捧W1Hb)N]}O&<,Ȏĸnĸʼn2 L Z8 4k\k}I>%JjwSֺ̾RRtF y)HW)詼aW_~ ,ohիdɓ=MP`恐o8Blqb/)diGzn*ؿ戴QU}dQI7[+!Ƨ 55(1N56AlDD.rJvWbbYŸDJD'Qվu,Xϻ6a^S̔Bѱ E2AIz"#/ !f6p bfy"c +[}q?PK/xEDj" H-Bridge.inoMK@+j<#E+Z*ң,i24 [Jh0232J*4$P 5>.%d$RۍpРd?H&pGPE$}N{k}0F¦\zav#4dMv{OwQNrz şgY'h`2,=jp?p8Zr^y*ErrK=[T3m8 *_ VGj !}PK/xEug6=$part.arduino-shield_r3_two_layer.fzpTQk1 ~߯0~iCa0ǥS}}i_?پdIۅX'O7a[ɔ3kѮ}(ٹv0+ krɑK]K7ɫv^e& B2[|rVM'EvЅ5L|z̆jvTkTs\z?qWLrYpu%&3=u!Kݡq0-{ѥ]g@8S'GQB_uN06Nq269Ҙbi>e--1܋_b`(J!aԃcP]G'— 0;<')a6XO۝\ Q828fM焃XqidhM.$?e+q(ͨv*C'?!.whuڸmOuNLfZ4oCtmYc/a?\9ZYŒ&:@aٝnohɇY:MeS/Y4ZܙӈvEcDъO`Ix$hBc6Csb ڳ]t xX򶍮1:h<~&V&?r?F=.tRMJE_(3ߞA2,<3P%h" ")IoDl*j&#;]]DKFgLPȀ%h&IKTdO:@_8 Jd ^BL*%%E5Ey*tV"\pTfnZH*Xctqws љɊf,^wFbaٔ$GT"o t,p ddu fqd _@ g84I$CI~#&[}SL(tLfftM +K4Di][% Z usLamoeLC'B PGy,yƪ  ¡b[L8k!ل~&&h~)dF#na0< @˪YmX֟lEJxuT,oVYwLef{:o]ÌmE)ytg/oA7Tx|J*V­D2/rCO7\jGíY*lf3|uzjZ6:+az SUd]naîFFqحaW/z*ܛ.8K95rhš]C[/v=Mq+8ra({U8WC{S:5](-4ţSGG/N=M 5GiGl X/ H-c0ҹlIH{?xTkKRzylT:b[2U4>j{V'0N |;s"щRq }_^H'Lv0)IIcSq!e IH`D1A'}!:rH`@K}VֽWsp8)I󱁃 ‰W#Tb>͚{o&=057&vv־_sI`Nf\pJ/Bd:1j4[`FONqn<[`O`Ny66<#nJ#m\bʿőh HiJJq\ʿ1*Q#R2 +tusW:5㚗UlJmMtj]B\2!|k^'T7F(j[bkdvڵB ^,4f!&2{ցD',.I%.5G0ɥ¿1.f_G ܯɣ¿1?tDfV"Rs5T7F?TRs8jRoJ1OY@*}!5AOǺt# t2iVT͑R3ңoMkݖ3) t2iVTqR3ΣoM>H]bV\TqR3oM\w0 'frHX!5c9*ƨrR"4!7DsԌߨqH{4+zC*Eo Q7FeTT!b8Ds ԌDuhVTR3~oF bҿ7蚵q+m\{Fpḕv8[sobӻ+ZHɣY{ռqkmT7+\4n=yOӨ5k֮ƭq5jܚc<Ѭhnhhnh*xyumP&@MA͛h*x,n}.xHdumP>@}]u Sϒ`3n45Gx/~x5ƈlv*ѻ>lZfE z j+j 1- b^WT9JrzWȉŸ=B 9F 9V<BQԫ,WD:?^}z]/Ŵ:V,?PK/xEE]Ş part.Battery block 9V.fzpVM0WXVX>$R%E 9:qwDoo̲߱$Gr%.%*nI|:G*:H L?hYWp2Cw^M&㺊t6 dmFlOx4 % s1ޫF$&=H攨qBKp!;@,1<鯟c事Fc朁2-fYm305nڦTBFCJ9n f*L hZ'tc!O£9*%^%=ANGf"L%jD<Ȃ1wNf㱃n6Jֈ]NxDHwI 'ssNYa~4 &ByE4up &N Qa1"K|?:E74 rE!ƻ݌6뢎6#k֊|At;קN,ȀGB--ڽ C=qӇ oq`(BZq5HĹEf3J)E%\nk;H."Lft5:\;=lE> %6Zdbm]12 bX$\Mjx},ܪ3l w[&ϴSMLU.mzVmzM6զoڼnm6tc PK/xEQVC{Y part.dc_motor.fzpVMo0 WײbŶXa+SlU YёCi|GwJ2bq(T9Rl?'/_=|o?'>8y_7>}vhgpz{On>[zEz}rOZzOOE柳7wٝ_zyg/Ngwח.~ɣO]|rv_gw|y}ݛW ڧqv/|^x?λ;~yyˋ?_v积.uqzWogw~6g~5|Qo6ū//>~/xsxrAW׿r˗~ux#sy9>?nM'ynsl^?ݧ8*z1:Wyg2^=/*~y(޼W/L^1h%~n:ͥn^ٽ^\/ΟW^kyw Q/NOn~?7=8}S{~@_]=(x?7Wzrݛ?G!vgk/em?Ƿ39/SK_K*z_a_Rҿ^>mX׻ͼoO.;/SKK.zaRҿ^>oX~o^)av)y?D/0,=JǹatCCCYJER*}.r]GPhRs">""GJF9949T\0ȡȡ,"E}D Ef)>9.r#rh(r4KatCCCYJE)=E.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29Sz\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K2]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhNu.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CY:"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GtJsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef9Ů =z' j~rkћ9w8wy#{7 a.tkћ1:w(Lwy#{7 anukћ:wx`wy#{7 avkћq;wtwy#{7 awkћ3^v2lix4LyrxN- i5^ox0 /;^4<Grn~KEoHbx07ݻ} y?FG4l]J?/~~߰`^?)av)KSCQz}g g:R*}>s}fz64KLgzp,3=GggÙNtJ/^3ӋLYJgz>ӋL/3f)>%L/>3tT|3b8iNu>k^}fz54KLgzp,3FgWÙNJ}W^ g:)gz>ӛLo3f)>-Lo>3tT|3f8iRޢ3ӛLY:Lgp,3}DgÙNJ}> g:R*}>G>|f04Kt8̇ZڏУwo0x-z>.~yOrn~?^޼ϔ'{7ϜfC9Ns,< j~΃Ӝ9Oôg<4r09<8y4Lyd<4r09<:y4Lyfc9Ns-< j~ΣӜG9OÔg'րӑ5`yf&Lyf·?έ˃k6aZ3s>5tx X^ jlMR̆wMV̜MV̜6MV̜vMR&x >MV̜ƒfMV̜6ăMV̜)vŃMRx3ƃMV̜=ǃMV̜G6Ƀ.MV̜QvʃVMR=Ns[y<3{9ߝ|4Lyfs;n9iV̜|wr0Cq 8t,X jqpMV̜>8'l´g|Cr<&g<21yt?7aZ;l9iV̜|vr094圧aJ3`sr<'g<3ßN9909t:'-لi599hyN&Lyf99霜dyN&Lyf·?''9,ل?on<{뫋wtUq{͟zrݼ޷͋/h_JnJrגywzu-xz'{$7ps|+py5d5d5d5d5j@j@j@j@j@jՐWC$Y Id5Ր$!IVC$Y Ij˫!KVC,Y YjȒՐ%!KVC,Y yy5P$HVC"Y ee5j(P$HVCjP%JVCd5TjP%JVC]^ my54jh$IVC[Y Md54jh$-d5tj%.Y ]d5tjЗWX^ Cd5 j0VVÐ!Y Cd5 j˫tAP@DuX2": r+C2Ѥ&e82 (eB)#uuR)A JXJQ%DX "WºW:XH,AD 2K%,%Dn "\.a.a/A K%D "bH1AĘ LQ&,D K "g4A$ "MX7MXGM&XD "؄%m6A M&&'D "r’r9A NI'ֱ֭D "wA O'D "uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@Q{_ԔW5e)e͵֔5e)Mٯl(( E"E⒁@Qd(2P(   E"E@q@Qd(2P( E"E@Qdd(2P( E"uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@q@q@Qd(2P(  E"E@Qdnn(2P( EK"E@Qd(2P\7дnIdIdIdIdi@@@@@@Ӻ~μ|gY.y+_caVXoMow}wֿo!qm\_M{o~ӯIdV)t)9{In޾ 8|F@߅Տ؞>DwovbN}?xQ *9>Dwgj5FPfen-#o$>$wPSvO=>6oq7woo||z؞=>Dw/>Di`jMtm_&BQ#(yȻɳ7zao?rt/|{oWoSr7ͷov]X~I>=lOeoQtwzo4Q }@&?|df˒/ZTG}I_G:y~gw?PK/xE"&_1part.L293D.fzp[8),Z&` 7ЇYmV52Id~:B$YSw]MF1>- B#M8tGBϻٶe>/1IU寍8#M4][(Ox譿sA?*dԪPT,fARf^KYjPx$mE}{yoUaPm]ڝ6'ʦ&).\=>ufOÜ_V)J( +W<ĄVNq;JO#r_+_ hy8qc:h*k֗o͟H\h*b)Y('5p9Jv4jX-Zn(P%XNP{W]KGÍ:fEsU,Q=dmhi,7tiĆ_kkrMʆ~8YzSpo֨OB|֫? 2>V+q8`K{k|c!f1C ]QJ^!r #V[ʛ@[gkKv8!DiDB&QABVUNXzgRqa5'J{Οkn;Baoh)zN[JBDA~@􊼿@0c8{PTCKB%DTT4**ՍE,H)Hh+q,>"IEinFν{y| /Q4?†@χ) E#l#l Dx`?cI(1?cTNJMu" yE7'o"iEp\hBR F|m$9D>UAkI_\ۭV{K i"(K*kHf鬆6ɒ/Y9ؗ!a-~p@7cYjZ'PK/xE>6Kz.svg.breadboard.arduino_Uno_Rev3_breadboard.svgkݶ(}{>djox8-;$sKiw,U:-$v~ZII-56 7/^~R8\^ᗷ8???_o|zxg=~<<߯_-t (x>oC?6,C_d,\o>x?~ѯZʊemqqOo|A Í$/n/}|QF痯O«ڻ׿~^>Jh=A z_>?}7]]_^ŏ Kg>?~[C|?_z}?ߟ~~supxׯ-?{qs}?zJzOiV#:a|g_ώ?'}\Q0¾Xzyv}~Nj˄(?S̐]{A/K;Y -豼Gтߨ B+󄖢  l3yUI!8Y(r:_NvYʫnǗ\7?|^U)('ӢQLEYp'JaYt3*<`K|z[rq#\^:a_:aoO8:ş2w"n"r Z$@Ky' +;p$Qnuox^Z<`$!$yIꞶHIi$ 1b.k!F67Is h7V,@!\߈yӝ46-KQcjr5?/u3c[´$,T " A/ oiy៖ - OA쪭=x_D:5okc >:64.:{; ~*?? Mp)=~j2:5ֽ m [KUCkݛgftBtNdg,ITAYޛq2}S!5ֽ +G 8 q"22 =;H7!eķ8WWoܐxmp0~<. o5z! p^Sh(%5J5zc صnMxN):'[g#矨H=ۆ܋Gy7("x'jyZ MS .1QlWMc Xÿ!^aYT,cz&dXܢ  0.) b2 nXJ20I1LDCOD6!cB;Cʬ1d 5Z_`7 C n= 0bm1 Lx`xx! n0< Q a%aa_2@Qt_5t}J׻aFc5(4.G2 z\,҂7p١^YB=Np t`BU#}Z+S\ig'U ~j?׏1>xejb`S}\#eԸWih]KzmyVP;\;!~t ~U ~^)jNw_)r?G<t_ 5ܩ3jO>1^ 4`ܨq#V4붘L 4ܨp a&]p nTs~V)wƷkV 7Aau='k2P& :ڝ-$[de5 VT.{г-Qe(l&ǯ(X<Lp=_.D UP`E¯(cm85ioj `|5_&_Vc]u5$vk0 g*P>j/ZjNw.L`?O<ـ9IcV]faklpgZXqyOAvnMsH9)7@'}<ȱ(rAz5̞{; !h2yvqT\Q?6Xb oe<š)aR 0Cfa!#[H |ݺ7  V {2Dj=~{_|mBo'#tJ/\Wy _o~@ɟ>^/ۗ>CB۹| Bd8y??]8kx[Ѫɧ:oE _'~{?~/&G?'" xX =z4<^Co_BD"!z_/^Gf#kx>/o£"!xz/Gݯ|'ݾ_޾~{A k/xFC(V⨎"YCaB[Qz?O"@g/-zѿH"G{ń?fkCƌxNWpVhq':_z!qRF ƽ$]GtNuuT.)}qK]t=a&(5M]( EדҒ1&GwϒD(W>1RgE+WiWQW>ٽrM&+7QfzDxfT^0fc^yqξpNW>4^=ŎYoontvĔSףڍϜϜrkGb{Jy ?U!$ha8L}j>$ϋ^>>F>F#%$cN,)ؽɸ.'}t2e.Gi@ |(@Geۢ>D.݁^NzL0K"꥛IߴIߨINzЙ V:_k7I6qUN;tZI*f8Q&8nCuȥm. D]KkSuSߟmBwjiԟVOqSlJEݟ*fSQ&8?ˇwt's]҉<-<7t7`g7:۾ _=b]|\>:|ѣJ>z Ξ`>D.ݯ5 \r_zjd:Mbu-79l6`f7:?;$E_`iyfO""Sg`8h?~9AX{wy޿q.F(m;}:}ٍ¢}ڽP֍]\/h"thS8pWϮ?^d_q4; 54i xċ>KU h"$x-1Yx[Ȧ'ƀ6m'9KF2@ȧ9?b!YK,x{ɬ%?!LbY?q&X<`28ȟ}/>EfM #3v,KfMc &3~E,Qf-G/ JC28ft"A1J_O m3]C&HO?7tܨpv{L-}N+w_ZƂy' v͉̀t,596'"ILDɋ?}enA jH;ѸuImD)u!#%6=.6ڭin1ޞ3GںxIc٧Vh /n5{3h58{N8{8{vm-=Vueɳ1K ãkx<{PtL՗K"t4h\_,h^E9#nDBʑ52^/Joaq^8}%ZDmӷ&Ƶ8?](Βksv 9zFV<\"+H\ 9FQ~KDɎ d$!<ۗKX$I/ɀƍP|ŲHrZeYf7wXL,2EL͢}^0&Yd8@cXAPŜm=Bk Ns<īDm<׫/f~~ y;ٗrh, &_ XcQ >s'K1SRNVV 4Ɋ0,&S(\䇓(c *ew𥾽3=\ubj$OǕ-A?=XF-Ie 7;ZIOB'ZD}C\{#/ mv~g,dlIMn4wzBR"tz94>-WɘY'(dA,OkH嘨7Z&^)Möu=&ENu8 TeG{.:EϊvQYK~$9ُI|4]LQ7;D1AtM7)n> M#Ҝ#X? Ys~c 'Nჷ4%ߘkQ>HXWfIƷ5KrM4Kƈ?>|-[iR*wQ9MB}z;SV5xdU |ݣ'}c F,:6 G`>;&#ؐCD,EM_|i*mhr W66w{tfl~..S6t/*fAbB4P&׷~s}/0ZU9}5 $Ml/I!8W'+%8PBKa?cib)%U m(TR wmz:\+K}im!Ƞ c^s5J4,mE?J%-âɑpbu\wZd-#-=%qoO_މ%i|mtDoMMЦk;hvю0FN'bdwIN' TµR]'yz {8//Ǽ'Ivbv"X}|ԣ֣ @uڣPZN-(Ԕ"UGU6( LG1$0ƣD ʣ( c8.K8 wIgy`aKk2`/ ū7!o­IBIP\](gp]oBƛ 2:/ ;Hݏݏ8VIs?iSsR(BAB'y[}YCv$kp\1Lg)Nzt (@lX'A[~~Ҙ~~3,01$1U]eIܵ$v6~_mwVs_Y $#sO̸n2z}t!5hɍ0FkLsoH@~EU_;,&lpQygz3`?8l\\31:#ѣUw$ L#1$0őF %~D@ONX+9Fp?6)4S']N0Εxew+~iw+BLÒwai4O/ݬ^ر{JkB=9okflk5<GV 4`# k-}dVhh|iJ*\Ͷؙ@ls]<-6맟~ivZgt-Ƕd[KƢ|V>~4'ۏGǚ辽|ئ5lpӚ8Ao#Z̫gW|_K9˂9g9G1RrTqq7Gj,5ċ$jo̸q⍟6ى`9UL7y Ww8+8;bi,P#fZě<ר;~x qnsexgtǏċ>34io´Ǐ/''^lƀ-L8h>syYx:x,NیrW<Ό+x|D闩D<! wAXP!r Ax"UBл_,9qhI0I[NloFXH!rB )LŃ T?N⁙g2>c)| }b$AK2F3zJMq&8GFSY( MabN" azm3F3gJ^[=iNl:سf7ںTIqQ4b7q_k9.qb+"g_1N|_/ 8b<cQL8bqJ1N 6qQYq7C-bP" YXC m&Cmllnyl#ⱍȏmcX^~Iٔp#Z9_|y&9"?9Gts_IO0qb9"'5G6ᆋ.?7G.ʉtd刜or$9"?1Gn#cnˑ̹&Y92?+Gv?ӓOqò-.>E2'Gӧ.c<$!eX@,sb ~ef}r"bU閩T<&V1ě- tN0Hn* hXu7[̙W9ᰢmenSʏU}S HV d*?PJ3a*H@B2*?P8ۯ;>kXrB !˝uB[~Y!TN Z1.CBuܿn8b T+|>%*?vPO7vz.@BsxCխt,RAD=|*?xPofϐ:=Afj8t~;7_O~7<ۏ/O|9~"FQ %t+kHBGI|$Nl pXBK-wC JN(|bqΉ#t+[d:?Н8~գ~bA "t+"t~e}W,bqΉ#t+Y#t~q椋:'Э \0(BGE< է5(ï/~z VT/d . m:iAvl\Bq`#u80\qy8auoث,]/o/WvX"Ia`-w@vGK7[׶-'%<4??HKsIۖFwDÝ%6N=gOO:\f_>,?| w+X=̟ ғbwH&+dG}-qs+:L^A,^d+Gi.z!DO7 QTpuaN/W'N.7CB`bw)m`֦CW&JZYEeip洡9B Mm$M#{p(/"ȢZ:QpduԁGeQǬI8::bU =.w#9db5r4B#jjxW0# ; ;~Hύ n^{`"PǪ@Ei rC4pkӈ Q^(JҊnȊ5 Vz",ǫipFTD2D  H ̚nDϊztNu*[xi}Y|a-ʅg XƔ(!XGh`3r̷܀ظ76 F FO*B5 ,x6`hlAlso IھY [߿';6Z3ϱz 3Z9Nvlyc`NO}l;(d?A3r:t#8a_#g6 oI2ǾYQUb?B3eyat$#򓁴oi WW^Q3OX""-ȩ>BaMƁjhXWdjE 9Finv#hDc[@VF atd:Yr#&ՔkH"ZdH xF_@$r tdG7! .+eXAVh 5XGh`F,.|oHG6Sg͋6{ZwfD/sB>uO`B?OF[2wZc~V84\9 ..<|n>sB8{-VwM&Taeel;]SS m h%K6hyhsX_yH 2v>g6^ϸa˜)E 5㺈'"T|о.B,tQ ũZl\]:@0j/,WFbLL2xW;tZp,0]&ʤG_P1[\b9&U;0"!Z:΢͉(1 'Ng;[Nz燮w,MMG﵈2æ'˅"˕#Ց~^,RiF^zSx;$^wr'X8^.ٽݨ˝N~8{-^.jLn䡺Hjd77:?BAGy-T0 1*3d 0 `/ ktb}tSL#i/D2rZr%*npH ܨ ,8!;khNVoƿTPӘ%,JFpGN 0;F=;"q$ QsC0rҒ pʺL5k)%;_`B@RH~d€ \aO^cv5HQmP̸$ap$F` S$Z!Z2r wz]%8PB"̥z&O6o;Zr,>>=O(rJUjLCQ 'M>B,9Yf7IJbpE F%X`7%_j )r%Gā7+'E&3htfUS"d氵2 +̂1iPA ځʼnf`0T9iƎFDcYcuGYT'#kF"Xr5P+UjA2,܂ j(\Kc)$E66@J㾬0gH,-hB{+ͱ͸l+g+&6p܁Pf=pcNDנf[i4Bqy۝FW@5a/-v$ d.0WiUȄjybZXe UhSՃZ3MRNJFe\#h>#Ac ݴ%((QF&)a[}!y:٬씷ú襭dq֜Xre'wM 1PLnD[((V =dSKvE Xӕdؕ]S i68;+A_~-;NXZwnT@G&+~!Up.ZlTh޳ {ԼPڰ@ < M! K,z ;x |.YXYRFA) @gDAP) @ #<:alD-  @I QB1M? LL! *RXv$H$qg6)Bؔ1~ǣتc4-U TS _% 0Ӕ 'z5[ x_s^ؿ; Xi T֘[U;GA+PAR%ƜQ XFBPckmCDP@Ƙ7_@P!j yc5,Z `֙ 2cȆTQҡ1vrH95U:.14'C)0cckS-"i` T hQj+D-jVkvVgͶp7Ϣtk,&cqq%(X,4~D蟉r3nm׈?S2n%2{D/UY*LѸQkp8!?V@Z[F!3QVX,P@e24"BbIGQDrdm _-9|A51obOy/a ےrl_Lp-K:U,Rq7q\@:K\9B 2=!LUdHnXbB~d%M݂}KlxhJZOFbpY[؆kjLl-Q#*GVWjWTê  ⊢8bAv2kx>Ǿ: úRF*VqXE*wX#fIy{(⡉ a'u -aaï{W l1'u -aa˯{vXGA랬GAlUӖ_d㟍=g@luO68sYh P^J2z$ XG1Eʆ @APW[ q%lb N5 /gdbuyeË 3$fõw' ϡc^CK]0I Kv%2ٿ.‡Kb x4 W(X 0u32]PAa0 p I.:1yoP~ " *ƤV4?Y0Ĥa;3ρg@65,k.#g;H9Ip8lX?`_ C:W!0XLUg*Lۂq9Q\s0.cEzj'Fd85sR |Uh\2F!X)L׫XHC:\Bץ21Crq2%>r_bC1Ƶȵg 5wk!U n K.IX8\vҘ)5`)pm S}U-XwPuGA3A( ~e0+js7J+e$qeW (5tNVu0`Qq`8c8 P)ٍeAX/gSg; d YvD}ZD ?]0KXQ)S!"D:Xgϳ87Ye0UaIq!θxD\`9[CV ᮬim%J yNp̵ $#";YbT1ef$ Jna~B#K=7یe Fs4ݭ㏖Ϸ^__=l2X Ta=+8|ր"62ؤq{Q+V2\$^e^ _zu b:+B_P)&it-4_Z8lampZ?ol(C&㎜dlRm*9Z`#H>8)+b >lǣ«Ip6 |W LDvǭqFbL34q7?Cq4?{ܳAө琺ko&o'ᘇm8ed4#YpQ Aw׉V|g`J!1YN0e8dwmބT q6Eض/{H~s̩F(Ċ/;ǰPuie !hhv!pf8UUu#llR8vZ F1߸.j]8T) lýc8YYj9jln囹kv6nH'8`]#Ă'LU[{r{8lݾ@w '-% sC}+wv׮Wj|SL1,x1HvlXdɘc8Hy_EĂ'sI/=+kX4ЫCޮWÂ[n J{r+duuUtu*Igܤ>?`\\-8oca˭@S79I|[]sdr]TWJ8 tpqx:e–+^Ű.n/W₼ŦѥNN # CeخoXqhan} ~gX,L|Xh3Amfȹ98M>Tl@lnJNx!$GJyov7.Ѷ"U +*gV V+xٔȰ5.9#boR`/AʰA_$`p{Β9̞)4(/4TA$^& m$  ֙Ȱ.#b$^& m$C_{Al3,|̷ӜNkI帱Ѧ&Q2f{ 7#PNs 7>T]zq9n)k(TiAw ƫt@~<%MBH*:CfRfۂ̶!e6(&,G_V2UA'4ScRĉ :| 4<7qL1aHu3!QX5zT)|N}x k`PBt^c>2!koF'}ǀ"dBYwk6Q _AF\7D힙; )05.LMT=FI}mAle߸)Mn7Q>8Ȏ) n7QT0lr&03tFNVR,i)ĖͲ́ԝ\)Sܝ&Vӳ\PtMB S)SvJ/o/R!&{;TaZ OBA6OVعQ}J:A}8A^vJQ:ۤCi*(urwW6qkb'jp֖Q>cG,B{ZҭX7pճ됉~"Kg;S]x;6OXA*_%DC)DZ+Ո#|p9Ak͈~+Tt=D٪qfW*lJDn38`*N !W㖥2RI .~"Q$R@#kM:6;(:EJl&`f;(dM' 艘ڌ鉍\>yt)~_g+\Jl .#Wxb¤ֽ7}t+9mB,:OW0ؑyePeQ\bN( 1)!b"hhRB 4,Zy4*- k /e=~R%3@Jh( D `RRvUul|ീwW Gwrs)dH/&jU8)A+ ݖZa0f,6os&f<+ IA)LلtF),XÈtMNx\308 g$AOVMh]_k-\NNP%s((QDzHw-;| 6 S;AC%j+Pڭx\Yޙ6Y8ͅQ*X\'FilOGa]2.ü!OWDO^ǮeG"܌sƢt!Pl' lv(e!/s(ݳDtVs[#j爡92Uhb Jו5w&VM@X>s Y{C2e;Mx6j?F{Ama2LnR>mAZAV%h]@kиlA׵L< {kkiVO'AfۈZЬ=AU[(9ij5ܿhzFh4\*s oly,=lI{q0s7dɰꗉc@QAYHK_E$Œ`Q|oOvϟ_ֶP?;߃P-'܃1PAts!ߔplQ7À^tɟy{eeRiP1R> /.Zu;CT ŷ"^v}`M'K[VWps"B[ᖷr\U}VZw6Fx2b&B* Ҋ_U zHHHɳYcI+!{)Gz 86(,/xw-*V Y $Ryx݀*(J` '*89NTё|r6ld\%IƾÖh 9*@KЀ\:$51I`AMwn+;וd X1'tZS3iJkJQ:)K)c34%5FNu0Y/;6PKC2kej9 Wp&=(]Mޥ 7@|pQv =s B;m`c(y]P/$p⦠3?eszBpk[p9T=l;zq'v*J0vt ePO( uPOH[{6&oֳP&KCPO*ۈUU}-l zB0ʫqI"S儲^&tLv6WOPJj"];&x5A-W))'LDrPv_۔Ϡnl=;g)Hi' Ofx_ h^hYO~9<ݪh gDyzւI`'Z I&4zYh=$A= ePMȂrB1PN8TO(OzB;`•'k_OH X<,-DZ^>,MH7yiIu-'w%c 'XStdiPҦ"@ҁzZV=!H=!'1zAfZ6Y<MQ'Z Jw WOʯ[P8gA݂B}Z܀i@hPB>TP4P۝kw,"T7\vV ng\SSPŅP6V"-.q > _6mbi̧n>mt_]5ҤP?i|-OkAna?P,((⽂B{\ M8jϒ$ ޟ>Bؑ]ٟ9$Gˇ>>;g+ n=#lODuWB~eOܤ)  cN_J!Qw;(a `̟J)&"Hqgv٠ YP6h|٠US5(|ՠW sE4D$LYxgts sh̏L$ReIbE _4kH͠ jTA͠ijdWEݹlj>棚v fٙjP0?,Q#[yigTޑE#e5.1LV ;+IgcAH[GE7%D&OM`]48$(s5.VQreW$^4ؚ,g+ldB@\5X2(;jr_K{7\bH0v+ U%*^g޼}qOs+S@}TeRA]o J{ć[*8:P3L0B Du­(:\l0J)nh%,\^vh.܋!:|H_'R~%ثHU<iVzTYE+qX;wJ*kX{EByvSHBKV*UpR-"ŴHQ;u`{T&TUhF؄D= pp8k$&0/ È) W# ¶VuXO\$)8kOj%֩drdyt[w[řNB怬a8<æZY%iF䏪B𱌩QtM2 #7͸n 'j1%/&X!o d___jLRc LvTux)k;VH)= n=S .W` L R8'lhrF?аlPCf24pl8>ٚVT@Kq6"XS *i2϶C "Ïs "-JJ_5KB$f!aڱLRd֣J7ppŚ8 d@ 'G; QSn܅hIu~TR& |`Pv Z-&+j2F#/dZAm%,- )ƹoҜ P~$ ع85+HdxE1XkTcSATcBLߪ c !F:œœpVZJ\e; ʮB![, ߭/WG쳸0H#f+'^Ku{RIy"ݤ')CMs 5C:[3t.S3^\1a& 'dٚOT1d8eC:|Re C7sH$ s& ʍ,(& 4ihbݤ!zT' 1i9C:+ ж3[ Vΐ9C(!g)C KL7eH}x)Cd ֣! 9Cy9CGAr( jimRsL5ȏ:!a( ۅ00n_'#Qح\tİncW0'I0<Ó;grGi݀(P`F9Q(3k ➽&i׹0[&h\p9Ah3Z-4a_4p<4v 3[&dL6?xڌk 6odOu9=ҩn -ң @5ZNw7t-qDu㧕k66k[8qj#Dդt7cu pðZqcw_I%嗫yYf[j8I'QLדʾ5rnu&X/3Dž#W a濣qt/r#2F9dӞN' e6& Sr+I3d h$/$ FFjǑ8Bd!I`5uic4XqS@VbpTaDτq)Q)h$7|IFc.6 $GM< _ŦÛ=| LL7NtBK,]l ^ @8t3t!h܀$` ȕ yR5Ә\^o7/Y6 p #.`jԫk3 `m@k$᯽XE[ CZ D/fb #W@ZLF,fL XJDL[l "bYAD,dDb6-Du&o6:bV-8r $DĞΣŬ[lpJxt2"1+G]abRѫXUg4̢]\=tsSK#]\uR7C43?z10ܢyGx ķQczv͢D5ҘI:AUj"t*e~dS2 ͋*E2H#q'p $[@opu9kɦGM&ڟ(ȳQnT^Zfx*8וRfJ5t44ZpsO%w#AGa^*2.{ ip%w3w$U(NRp22 + TCvmFv띉!%gknijnբz6Q֦Xsڞ3Qp5XZpaKl|5yf8N#Ɉh[[htD{mIT`guT9uE%|l3ܨZW,]fQ]ht!*~":(x v{p;OaPpou j+Y(%gxe9㏊!9Nú0DXWY27+AB !4!FCBgk[<()[(U,˒3P%ֿBS*dXSC$OU5WU66Q pk E*$a4*(d;D qK6Vp dΰ Ӣ ʸ'6<1m^}~ &w&Q*_5NEJ9rVK91_x=.$s 2+w YDPMθ*=g50~1!ݵEU,s>ڐlRwK1&y[T6*Di r`gJp#Bt.<瀔@RեgCʘi\y*#Vw.M; #U<@#܄st"P4o؝byn;ZF,Zuw"!ǕC@@LZZ$ ueԝhi P0s YɉC2m"oh u[ҔfM!hRusFHqc *b8rU(Ap, |`8׀5DpՓvJܺ*7YG(?l5ܿ0Gu+єR9ի7B`8O~`&ԟJGVnRc[^Zk*ir#ٵwèɂ @*ƾNaa%üF6OMHu!' @r0}0rSHO-S=bm%3AQ/KPCL MxiE~]hUX^Aŷ"^v}`M¸A}d|.w.Bh+"QRh^ЂQpKBh;9_RFK$J`oX6_U {HJH3c+!v!- "Cz'Q>KEDG.6T ?7^M'aR ȁ(ڃ *ރ#Z?g-4ܝqX .})ޭ6,*4rF+YH1!\"={Qg$e+N4Tr!ۿ$v{QUTJVr*Jfm1*ќ@$ݷg*% D !JQd1 D}kOWlT8>ze@x\-lZ8YS#<>om$S+3+^Z`159{Τ|4J=$SRW ~S`UhW Hzt7b DѪ@wQ8E^:>++elc{ͩ^8>n̏ުQEez)ܤ)@$ ׫X~"=o 7?P;?4IN;{&QuoNW cJ@> iLATQjչk ?.Aco\ⱼGu!jcV݌sJ[YmQ)b5imJ'R|1+F<[9W_H|t(GlM{V(rDyiWt#n^ػ, o( j5혊vPw[X[\_Z2iOs!Ү\Rb`eC 6;S} ܴ&,Z_q('YZ@g)^!R(^rL.1Twq/QZ'!^|qXYq%.!uٷGk%)1K*^4E7qob5řbybQdQ#WCc`[" 1W<_b!8ȕ+լgzp=W@2;E_;bPYPxMVFp \Y6Qpa)mH *$Z`iy5 #%pI |oW |K -ķSl8 D A/\qW & fNj"$Pn!ZT&U BɈ(;DWvL3 "XjJF ̖.Az@h(B9bv۶3XƥnR*0DPOBMay! FK/AtLlrPnfة%ʝ9?ZY2r wּ!0M{4x4Hڠ8TdII|5mK \NXil%^Eg* 25R4Bgsz;`=)N#WV'jhk=t{NBseoJ[P6dHW/~Wn~%$6W,`Ŕ>]lUi'E'(N#@`Ob ?@胯KeYiެ<I7y o:ւ,r0uf92w1VManMv&) S1`A-qUHޤ>qf rB2ZȐY)ll%EWݸ1: 6?SH8c&=X{jn@*0#[y6: ͦo Menɳ֭ڟG?2̢<Ã6sTժZN.ϰXze`pf^) x7@x}Ҁ;@]-XqT^apJDee#o 8:rDF"m!;ۓ˿:iU_`GY:ث,q]{q)yl;FL&uBT u-X,Ą>:7 .>ۉ*_Ta% {Ra5tXj9<mhi+tDydyOtnf{>?ąm=4v[}ѷxsۣ2#H' ܟ8>U:4}?;G`_ܓBXfhlO!OIE8Ǟ UT&Sfe=AejU*ɊO"l)f\2[}ƣb h1$1 6=l{k Z41eI `Iܙ7,8 a]WD92+: )R!|Al&09!pA@s5re޵=~UH$?C!8@pQ+C`ΐc|C\S1Gѡ+tnBnR2JK⃈Z t!_2l˄TDlF+n"nr 9GBƙ?1M6dt`N&Vr Ɔ; h$v7p#S8R?SƦ x3á ,1ߙMlr3OyؽV)>1oSrnG[1=U{SV637WW54uЊE K|ZrܬLҭإ[@ۂ$jbvg&־57>M-.,H`FZ *=R 9$W17z2x=*6*ٷ񐜠j||X Ʌ'W,o!drsūJ7[Ϟ5NsiJ,!|5Q•sWƯoi||ҜS#'͵ W`wC4[ ^ErX{Va< r~䶼ײ^jE|o hvP;27yO.tdX;JIt0ծC*eZd- wNlSJ~|{z ~XC|+4I<.jmæ"69MAS@GϦ{n3nbp밙c;.W.)ETj 5 yLs7}Goon vkH7Edl` 76BGK+uמ3z!IhӢЎ$%[_s= ZybJ,&LkZ0pF|:_t+3_m$//䍯WpjPA|{^4_2_n&7yR6suFteO|S`+-#xB||$xZ2$Ukۙ kl4"C 4sv#W%QX}N(nc.KԗM{ju]ԗ/b@$lR0fã(tvײމ6ģ4 S!0F#nl#ҊBnUwD,"-(/4P> .VMVW/ d yR-ٴuZnR@lTjR+6⳹^9/N ?L|o߯7V^97Ӥ?脅6 tAJވ|oegaa?B|FDt?=?|*KNJ4;pAB?_.  |B} o@+/VהӛLTympT|^eU6%fAOƧG[ך .d:2u? 2AKrXG(-sـ:!Ɔ()6*` +!~o8]|,:|Rp!ii[Fsت*OܩU< (v)!KH8x0qj^jUkXLyI/N%~õ1ռ`_ -[8dQdi["ѭR݉!]mCU:)v:w[mLZ%a-}ӄ?3GƤL l+tr5&^P 7ba?M~rP9 K2Ĭ@R ;@tz8f+ rx8@-u _=KKDe:eeB0IºT܅[<زRunjV,I݅%vbV4n`!@t-UVwD ~-wF5OrqUD:nսBsX;\`Y僩OgGReY僩x ]r?gX>x~J-w"@6U[:U/NԖ*lݲ!` [Q/+j`yuu+S` b`)$ CU. -RtKJ4/ưJ|+T -ªL±)U^//uzV沣.C̊-L^= P%=N5-Y@z -"t 1J5fqc# [(Du/4; |3v bV,n$unBd ݒUUTiV6Н)X@[d L(ԋPN4 -T܅kXy>+hx0uqnP{* -UT^.,?p-@[i@WbDQ[ HSlm,NԾ9\]\^--(ˋ syeފ/[ZJT7gL+9y{|wq*Y7ɘ"<&ј_W27#6rw _dC ^~aŰ{ͶkT=V:z%~V2!ļ17cJ,ǔhݘ˰j;ѹz6CөW{52:[U,|_/؉%>5( J6>{}ݴ+*_!f7]/Ѹ+}d*y?8CP}]Na{s灻4 R>1 kumBB߳gǔ=kN[}NzW'1[ 87Wگ+_kP<ʝ" lay7˾4OiPAP&m'6 D7Mrz4& krg;pX(MYQci*\l)MưCӰiV^~re1+b֖$]:Odǜ5 2JF8Ĉ<|p,` ܹ(KCzmPCTD#Z=T+2 jp,I(˕2AY D"/UB2Iuyp^ʿ%I!\j/F9e13Z hXN쩱wb,eYU6!NLEd/ ^滛5!nܘ $915:F&1\688"ـHnAПitbrԓ0T)*qNs/CaEJ 6gZdmM«"^-%!Eo7|\Sq%F[ h,B.'9f#WƬ 1xL xTǀH+2({72~\2ݥIÌit#9 0;;RlMj!T ȉMITUyB&c 4xjPJ3+V U.iAtXY -2=}H\c>NBG%%Q=½f٘;xxd+ĩ$<9E"YB|@`%*+LQyʪ+dQ1=[&Kt=6~!gKF3=c` 0STl2C;Xg8*v13[rw-qJ\Im2JZp_-d[)콼z,ypL@I+7|b>\.١nm2-XPKuu E$ޫ>Ȕy"תY) Yh\ %!T<e"u$djb}A7*\1 AY-swc+Cʺi[B$nE9{<zjLf(Y ҿ ,J&FL,xiH<T0_Xvl%0*a*n2vѥa `ĺL̅niPgd ԟ=Ԩo& I! T,J(:@QRLJX paa^X1%R\,ڍ|ӔZy<5;٢B䗦 X{tpaԡŝJhE#,FBǢ,8RRĽ7^ yvieʺ7(!ӽ#\9FtRVV6NG$ľ u5y7b ҭݺFٸ&as1;e|0P /d^ul0p (8?q*3_298Pk G1Uh޲D^ [$5k i=x?3@4Vj|>`Urߞ~aOמaaа0h"g J Ӑ,6mm )B)ϵɼ얠ޫEj0i(IDZ, 6a&Ӵɿ[*ɵ쓡kXAasdvL 1XF)bp'o2\}Uvg9Ӈ{l7M/i}I>M6npB`Bd#݅ +Ο)O=xy#I ˰Sh@lu8~ў g{mYo e@Z,؏j e#*lF=ZHi{nO"kabY $QjmA (OvWDRRT!磍YƙZM~D6j%Kŵ/'EBA샲β~q>1 X< 60B ;mPHAAuY0:h+yQFhOd`#FNN(؎/Y&kt>ؘ2y0ߑN푶Zs΅O;tCڵ8xDp)>`jvQ_'q&ylKvct9x>.5a⤼%XYuD|Bm)pgݘx; kFίIZhK Ӿ}#{mZ(5^s~3/ۥ"K$,S[p:.:~9KT@^GQ*4a (35(bnp-RRN9O@kTX(p3>um]lstLf54UqŠt1A6`SLc5sN2à}R=6_IJ݇hY+ DvyKsx&YY 4͵葲ͧOcOFOQ mQ 6vgXfz@t2(pyP![s.KP"ml* Y:D]Jo;vHKcQ6.uj YD]aRf~:C3ܫN,{ S[ujAr/4.Y#sK9ͺjcczb@Ui U:w ZP Zu(_2qc SbcaLvtH)4* G񵜶 .M:C|ȍ']hރz_$:؈>Dv}Kq 6o1S#8q7gnOzuyq3:z\۫˫?W\w?@agfRTܔ\?XvT/s5v7__PK/xEC$p~svg.breadboard.dc_motor_leg.svgXn#}~E0/TK`{:6;*ȣGVB!)>HMR}TH/b_m7QA̷r~}-eQ|zqa8SGO7o Q~FPoSn9ywXGoGb,.H ƋW|b^:|Xm]z/P0tR á"AkoI c_^sb7(&ѫ~X~@-\ח1ZM̷ ;Hk:"#diU*3V4P _ Kce&L"Li"94^j#m#~:FʼK UW" 92CΠ@ wbSVxVIƆ9 Ic eX#BZA1%V&cųyPs FȈ2ohk'[) 0s(m+pJ̓YɠrWJ23B!"BNpw4t\T6VyeIpY״=k*Z`BYkE3=[ aİI;;ԥL %Ct!GRpIm lqD<FԿSG4੬.Y)$}+m&mi*.O U"J>Zɷ:&wŴI7XRֳ{zkcJUnR~Zx%Э5@4^L2A9 !a,l^@҅T=.NC[[riRQM&]m84A,(_ۑPp^(Do,3B!z2Rř E%34"\3ڥNZqvӯA9FEY ^"TjL"XMEidIKI詶qr5\u-MDqS}PkǨhHPtRȭ}j],,ȸjX+IsDJsVFܐDioS~5UBHvB&j ! ! C(`=N\)|SD&tvȞFDQ:Q9.&pPCu+!d;!L!2V61ɰ.iwL;,6%hu MuqP)O)E\8f&v-WCM\cj ={rė |F%sdͦ EU>95"7ɡCTJv^vO z ћn?DxÅ5b6hTkK4p+M{G/R[<^T1m!EoduR:nGg> kVƒQTYT,>޺OڋJ1>a]S*vپ߲E f^mye,NL^,;V%'zҼ[`9? 5"W;O}LnPtpGg4{,Y"XΔұ"`/5K cEYz9auR%HcsHWFYԈʗg41unPK/xE%%k.r%svg.breadboard.Half_breadboard56a.svgݒFze557L (Gvf;$Hdhugo_ϯ߾o={v_||7勿O?믾_o޴x_ճg~7WWWu~z?_~lw}?!|yױƛi9}?~OTMu>7û??G}߼z~廷Qg߿xwy~o^nӘ7Ô< W߿{-޿~o!g<Y~)!Oϯ 7 )Я{?~^"?_ۿVAI7urvMw/0O)Ƴ+W~~~xr1&p02Mir]!z~]˗/E|6:c;1m};xO_p3eWa/?ϫ,\-E~F_V^b(/k"_k|ϯo:yym{ߞS?q0ۿ:~6kd?^\2{\_ 5q6sģb..q$ vpqOkCsOE)6L+7'k܇bēb..q&B?Opŷ^^,vqgP/aVh+\ŷY|+]<% zZ\^7Yl/)1@WozC{5| G*tqЫO`⛆ bCPc1BE Q!bE tԎ8j>@-ax*KR+vDS# mO wAEbQ jGOL5DojOQjŮ(jP'AmA5>赮Va_ ළ5Ӈ6J5LPYwAUUxpZ ZkN5p֠ O[+֐6D\<ms յRu 3tƝkP'-AU.Gi`{ 3qڡ\%?A/'vH_UIU՟>襾vD_ ׎*A z׎k#}UU(AoWU}P_;z%56׆k6D_jCm} .q˔܉FUU]k5;רzP}[_*A}K}m܁Ff;רJP}[_j@}K}m܁kN5 pר P1׃G5tt‘(lT=~?P8AFՅRU_jl46&?ب*Q[ejE}KU'*U3JolT~?8CFՏRU ~ ?Q GJRߠ"a/'Z[6HkUE Vu>6Dm TH!{ުԇƞmz#U%)doP\Ւ=QQդ措(uaN3v3w| VU__jnO> vð3 U~?1B措:5wT~?1B措A5wT%~?qUJokTRs~5';Qը>6@s5'; Qu>6@s5';Q>6@sjncO4wT)djO}Kmf=\UBU YjnJ\s39\UOe܎hn#U)o\UͭPs;~B ZTRs;~jnO4wR-*ſ*R}KN܎hzT[s'դ͝FhZT[s'U͝h*T~[s'ա͝"ƞhS~[s'U͝F=IN=a/5;MPs{N!m͝Ty>K͝'57\s'Uҟ͝T_jn4wJPs;~O)m͝T_jnO3܎UJ[3OYjnO;3܎ijQ)~"܎hn#U=*o\դ-Ps}E jTRs{jnc4WUAsU^jncO4Bmܤ TRs{iM<57ԇhn Ps{IU&U/Rs57eUknR*<ܤSRs;~)Rͭ\sO)mMB/5?ܤ*T[sjQ}K&d|TJF}T_jnOV}?Q=*ſI5>6@s2'T oknR5{=ܤ &UBۚT^jnc47E({IAsU^jncO4W C#U)do\՞UPHsUu 4W.i_j'j ~);4WKڣ?/5',7hP}Kj6ʂh*T[sgբ͝t?YΪH/5;( ~Q)~ÄjR}KmɆZ2G#E KFa/5; ){P!m͝U^jnc4wV3RDsg՟Bۚ;Շh Ϊ<5wV{=YI͝Uu okSa[YJѯ{Uuṳ*PKj\ŸT[tgU:U StUJLWNvDu̔?R]դRUe麝?q]5e\WuU_nOdWM#U]*o]U:jqnVM*mYUN6@w2'U onVE| >ݬ fբB۾U_ o7*|"YfURx0`jj fY5 Y5NjʄoVe*¿YNjʄoV}*¿mYNjʄnV*¿-YUNjʄnV*¿YUNv@vZ'UJonVu{=q]5EeadWtU^ngOTWMQY#UE*ao]U:USTHuUJܢjT[sP_J5)* {EuU:jnQSTDs*P m-=ubAs+ܢ,՞ۚ[TuVEMQY-;%5ԇ57EmQU: ܎h2G Tߠ?u¯5'L7hPk9*~FE ZT'Zs;~jʄi*RmͭFu¯5[>ܪjT[sPkmV5EeaO4ܪ T'Zs{UMQYͭ@%5ԉhnUSTDsjO mͭ:ub5[=ܪS~[sjN}OZscƚ[*q[U{NOk_82nUSt{tZ2%@L&-UN h ٭jʔݪTVե:%}'|]*SwSi[UꔀVޞPުL 孪T WN hm UTV7jUkmq* ~$U ZU'}~jʂjU!~V ߆诚GZU`Uu¯ o' \;5x̓yp<؃"(m^dB/wM@8 {!l+r's⤝xk*="e? aۊàZX/7Me@x lr'3͸%8 jʒQ*܍&eL妩/Wz0m=ja~~4*ܐ&meLVRd5qeI)jaaEV=OV)PdtA {YULe"6)=jSdU,:Y""+SLU-KC(jfBЊC@L! E!9v)=A`B@T;KC0(rPSZ[DZ²d9nF`P䠺Y""5eI)rP,LAN hEn Ej˒RZYAjeЊ 0%AU0"U:%%@9y,KH*daEI kENWVV#T%A;9R)=AeB@T+KC0(rPSZ{D29b`P䠪Y""5e )fiEVSZ{Hz)Ȫ!XYճN!hEn! EV Z "nF`Qd:%%YmhY`YEU1딀VRd5eI)jeaEuJ@+rK(rTkZ"GU U%딀VQ&, E 9F'y"G5AݡQUEu A+r(rT["G U/VQfL! E!9j)=QmB@U7KC0(rTSZ{D29v`PY""Ge)rT,QuN hEn Ejz˒R䨊YA*fЊ %Q0"V)-j˒SdU,*Y""1.KLU! (jd}Zsܡjk5\(d3i"R)=jSd,zY""jRQ4"u A+r(򨆺L! EU7KC0(Y""jRQ4"u A+r (6, EU7 #0(Y""j˒RQ0"uJ@+rK(ƻ, EU+ 0(ZY""j˒RQU0"uJ@+rK(򨆼, EU! 0(YȣZ LZC<CgUɮg $q抜XjcܡYUU)VQL! EΪ!9^)=Y텙B@U1KC0(rVլSZ{D 39n`PY""gf )rV, YճN!hEn!Ej.̒R䬺YAfЊ V%Y0"gU:%%@90KHjeaEΪuJ@+rK(rV[a"gU U%딀VRdfI)*daEVOV1Pd5(d3EVSZ{H^)Ȫ!XYN!hE! EV{a"b`QdU:Y텙B@\T7KC0(rQSZ{D 3v`PY""5f)rQ,EuN hEn E.j+̒R䢊YA*fЊ \V%E0":%%@0KHdaE.uJ@+rK(rQ[a"U \T#@֊2N^\dy~X;wȔž/2ML~<;yr'ɣ=% {:bD^gzz wtoc?buɄ] ;8\S{kPU}xOG,YxO.,Î=ၥNxOG,+㽓G;UWG)tJ#WϞmG (a';OS{kPUp1da>O;w:4=;LjQ#Lɣ%uxCI[#&L~zxtz wOryd"/3ӽ#O㵣'~+G&xoޯ~݃}GYA5}FuqxѨ}yX)#zyw䀟QG/Go8#6l zy`tz x<`tos?b];8g6[]Ǯ~נz?b=#> Î3!9ZQh0ͧCE6?G<*:g<^*:o9)їݳOC}{ {Pto?bȆ^=9.Q'k੽o8X#2q㸧cDa=# q 8 щ>; }q㞮=='C6{9ɣOo82qן} aLJf5O?xl*|3dP3:g=:o8C6CPN =}COSiTGL:yrg,tq2q/ˇq. i/ /LNq}qĈ>G|Bw>aΟmGL~ogɣo8X 2q/χiǂޟG~}3A';Ag-A6[.==^: dC I2P'{< t?b]LҎ3] iOާG>x>5}v93% }}'V};@w>agΟmG>73q7GL}#? ] sqڿz刭}}w/tDO7刑}}ܟ}N==ڳ'>6oq_O'Ow}>>&T!0U>!4U>!S#|lqw|1}v53!}}gBw|}?OsU=9~O'{ 9g=9o9X}sxF> >+~qNx>xq$ ^w_"޻/Gl_8ww_ϰx}9bFq߻xg}3<z1cCW<3+_{ #Î+ {޻|w';6zwd}œ;a{oܧo8S #FGvw_U[ m'AG}mWU{Րp$1!ji|{{Փo|ܫԇ>Î!LqR;}qR|&ۣc={ѫ>$jB(57!u?f㎏YU5ִJ~1z cVUu>qw|̪&}vܫNrܫԇ>#UUMGWm)x JkǬ:zrܫ& ytܫ pܫԅ~>N;޻jiwvB/Wx*ݟ~i{eN OegW3NGWm)xѫ>'rTj'}TQjy/S ~>&rhӎ`M4ԇ>~iji{e&r2[ݳY'ۣOثq?[R;`{˯DB;II*cd VڌVM+<ӎzDjD+ӷ(M{br쵙lzRt$5d!OR3>ԅU,cV)UeǬRP=:>f\ቜv:'}PO&+7A>q_Ǭ{b{z {Ֆ'߀vq,qR3xqR%RSM8//> _> _/ۿ%Syg_Ӵ\'sc;~?|?Ë_ݛWWcᛯno~#nAypv=dnwGtv˞۝z;tG}]gt l= lft~´B=Ivo4m=AXq>A췻AGʽ_b^?qw췻A_a+:EݏZ? /?ow7pW{h"7G{W{#Eݍ6v^ӑhA_d~}]GEݏzۋ_}__|ՇPK/xEм"$#svg.breadboard.L293D_breadboard.svgŚߎ6.i@$Y[UTҪ} a DNf{ lF‰/06p>6I^sV, ")7Y]zyP:/ti5YstYC_zvuf1NAYmg?#_M&va;K#ky=hp6n ͇dEӾKV]udzFp#,=C-6Aj/b5}s#dgXMstViRFDtEscvޖ;fP`SVPWoI1<}"eʨRh(JcYڇ*j_o۲Чˬ! c)\=-SxN[t%R@*5&~6v>B?Є"SD5)fCĭ >|eEQPEk5jYjՔNM,RŮi]y Njੈ > BDe@O8 >!M?A߮4"JU 5范tRDSY$pjk74P HrH WkI #􇆪$ѓQRa(Ra(RItJp |_M8)R)R)]Erҥ'r OT`1JC4NJC$ً$E3*5R3*5jPYMPK/xE; % f)"svg.icon.arduino_Uno_Rev3_icon.svgZko vɝ68րSȫ" _{ TDjh`\rx>ϽsW_oVWG3ݪY\\.ώfw6q_{-/۫oW_[ Vo>f緷/󇇇Wjq3sYLt[@O=VG_ˬb#\޼0)˛r݋rRJ=򷱅xKfoauz{~43$?Η򋷾_-^_' qɗedu\>T,KH4^/o vX-.׋Wuu! Ee_^ زj<~Xme߬ZZd߳5|kbUkV`-z4[OqP>A(N񇃺qؒqjuaԵ)A8m=4 txg%)j~=RߙAS*33Df&TtTqKەSD渀U@'_R'jh`w)&mrGU6'8Q`&dew*8Ql*\laiOq2c%Ӆ5{Z`(:xf0qOYQ)˄A{:Ng"j!s}`"ب jHؚP`YjD,VU"9 'OyQ(V5RLכFi慕_,5^h,5=/:杬u5{Po;g"0K;z"9P~|gfSe|F^\6QT3ywru1pٸڦkZy1NÅ]_m ;Mb̈u/1ԗd~1iŌ=N{ np}_^ @ ;KANԖ6ؗJ{%*-m:,O 8L>zI 8y yJuN.T8@^™Kܔ<+;c@&Ĭ!Po;S֩^j&jAHސ nXCVېzysULKaT,z!o(|F" X;D5bҞ5QfWj:6 %HOkrKseQ!ڟ<;qpΟ~͐?q~ǔ7q .IzLC緖ÈI{iƏM&LR|k64hh@{]Z>;۵|&GKpN޿~$wqMY93CPA>:usCPauQA[g]nv>CzMs>|܋1Hf(݇ڜFL riDwWkFL9yLOTrTCn (M 0[psύdPc7l,G Rn(q!-D! '3s5[۲բL!dQNW}#|މh@aUʉȧ*ckeb\Jqw%NBg/ ^ȶ+ʉ!#dѡabg;}4e'OM.%!t4 +m>sPϖ]9YSFqBE> IL`ݹiD( cm9J@zA6ȠٔحPyhDpAVؐMd5Tq\9Hsi[5$6f# m")˂+(*b /JиgAI0uVKD3 m#ZR 3 CXfO3M{ xN6<$sCGupE4P(C[0:+rjd'Aj-iڒ|~+6ruΰ~j^\˽o-*cJ -9b2Q!&!.%\Z` }̽ l(wC#ctVSGh+EB:1:Td@|.$S(r0".ngF<\*H` #@낕SL7eG2:fT{K"FG#K|a!@MH @(&:ws0`.kPBy,ͽ7Bɠ L$,gw,.-)K܅1:[XH݀e0f` 3DNa[خ=J}/Yz dZr``]ad}OݹD757:Pf#b|,SO}_PK/xE} svg.icon.Battery_block_9V82.svgY[S~_1Q^U3ҹ_\X\\ ߬֯?_>ΚwWW_n6h7Vc66'׫y{lƸy&2^5aTqSt8ѯz^6/Q7M&ǿof4l_M&www;;^/'ofB '^3`Εz,.GtwԐ7p5JvɫOiH]ߏnqr.fwoVJk?ǫY|zeIj.gwMyus}>׳v&^¤to{t;>̛OϨUM!zCvU9[5. E1I3*=l)U* F_%"(q;#n8t&R$$:om\K0cP"S(/4e0'4EX[磠 FdD Ym-BЎ}R^ 5-yc ^P"U B Y359Lbs*mCc^_ڽđ'29v(,6{) 7QB) 5b[˧-/ kX.=J%@[{KkN"J"p/''OHe@=J02R[ p \a)`.bg[c/E{ RsEr#40 FWy(hJd7Z0u ZgQ TzJ) GTcR>%Z ,I9<Jj>;.vp X.䒼2 RRO!u[!5vf%}Ђk_ T}`-ӮkY@I@0}f`4#d%g-笫JeQHQ[J-,V$;kF0r&.QC>ʶ@6r̶IE|֑[f6#GSPvꊄ#-,-p_0+mAJ] nڦ{uA퀓zO O(]Eo8S$ iA51Ac% F.*^s=nW?Hh#3增w$& PPlCt= FV:FօSLuEQ5ڰ6clJE(u{Nnqq6Zyq3$A YyW͹Ӥ*3(N a|BpPrˉJ0U!aW;n8*A5Xlk$XG)BE%THQ%PԶ $BꍓHvʞjvT"GlɴsaĤHM\fܓi uyB(hFJUmp&H63h(?d*K wsk/yQsb1dى_𯀦 6,A)^VE.@F9^%Z#L|`pm[fgV jAֳp:1bP{㖫l4c9«Þ\36SǷ}:Ztʑ@4gD@bQc2`D7aܾ>'T2SŹJjr",M+y|P <ždҿA:2M9Y{*q)*YJ헁3* /w˹\gT>SK bxV!;4&ny952#9nJNC=O1E.mi9=#Ӿ#0 Š`b:>T[o KQsw3?h'L7Cd *:Yk<]byg^>@ ].4֞L- 20耈>-mMu:f1w9񇃭{PA0 = 7Hoxd'{TbN0 f qgB&4.oTEŶk/f agFF*\܊P~i)Y"[ :Z%)G< :QUtm)) VWUr[Z+̅Sg0 2,k-U[6;Zw]Q;Q+;:fvi6ͧfƯɄ*4GM}Z-7ݧϋ+xZ\4Wyp:!FއPK/xE\pvsvg.icon.dc_motor.svgYr}.%a-yX)".Js{tRU&˹ o߿vw|}|O_n6_nOB{o W2zcDIgwoLp_X}s UJ)O?v7bȟ" Be_lZ+)_J^wHae.)ּ)MwR_JBϟ'}R*hܕ†8mǁ`LgL*HX<)i o MRYN&ϫy@ˀ1& ocEhӫ#藒 EgH{ #jޗ/U: '| WAX㌝H9aU|700 4ըfKB Ɏ4V5$2(<'"E3'#Cx6mŸqV0@# N \Dd87x`j =)aDs~ux.tMUUyO*\| 2܏W6* Tȅ T`'eV& D*"`4O&Lg%vAH!t2С}q@V'&# [P1Eg{6q!ut e5j$J4EXԩ7r=T_E5BZrlV׷-}VPҹza%Nh\APo&%-MMCznK{ +.4҈LmUڊhiQ1@?D6'uh3։ȍ6[OR@h2HᝩRhi{߃+y! I9IV c5O}-50ƥY+r\>c{cB:D |&#U8@sB4yw+N& u`7a6$g_eڼ C5F뛒fCf`l;nфyHڥ[yYVp*_Yw“B(:GbiS |DIԵ0º2 m L$.PP(0ҿ06.Bs3Zg#YokټrNzg` _@Ä0MxCh^"ƨ%sVlѱ A٢0Y #MѴhڵH\16U<:3/Ș=?w Y"bbBcEve% uUP?OlUmOlOj•IcT7*F;McLD%BPs͎tn.6vʼc{?b^9ѭF ar&00Q* H6ogu#j&j™yΜ|,` 9f]hDB ղ)BF~E7bT掮t${FEAS U7*5vÂ{v>tC3;ʢ]2/+W 3 '9nִZ^0'ĖY9G ̽zȮ`!1:5-9ZgWrQdOg!thSSȏj h^eQm9R>[#֔1n+iwtl9m m$pM./\| Tjg؊qm!Xy!%؍bjO-dФ2fYX;"7Ž 1Zl;Ό['t3< 7fIb+2-zgCO+(4)XW2jrV^-9Nrx% 뗜SP"DLouyAXO)05.cf% HSb 8b\:R` };׹PcъQ:iek;'5;s!-&au aDc,Q3[=S~"\(T ᐩ6WA+BW^Q'-OS~PK/xEƚ5d-svg.icon.L293D_icon.svgUn@}W~x/qdU~w-q"-f3snwyJ"*r IbN&~? (WjʴJ`VksnJغ"uDbpL,I,y `g羉9bå4^`F$a't%C\G\wM0p,,O=}e?drkaWE[5c2jO/,6 ɁMqACpi:ymc X}ȥ ?xܝ:wee.f И{l Hq4e(attl狄RpP[&%ßLoqGjf—ŷ J[ ˛^cC,Wx9dtSy7ϖja) \'q*W=9YeEbf8$ETZ:DlfCaq0$C#F[ +鄴oA*G -I\LR^x2$ZHW8ӵZe!uň+IFerԢV-2:BWG pF8o9t$eYeɞ;{q_fL%$I!&41p7DRG<%8B2MF%l5Z(e"f0N-W; re??PK/xE^y svg.pcb.arduino_Uno_Rev3_pcb.svgŝG?E/! z>D9͡$-`o$4BQbv5ZϗF菏1jYxY]\| ?{.ogw+ EH.˿J mѫ8j>m?_tZE8f6yuهoWl>tv~ֱ^Vls'7ݥOWْw1Ќjsɘudzp$cûܨIlG6el >6D|{^wv \|.uxVpzsA+cuOm#BB,ӂ #M$iBb뻫U<w*|!muw\l d[]2JdAU$9ٖo JdbSeJ!v`+`UxDckV֙ 2KN%%3P/pd'fF^6F^k_J#B 0h V0U Mrvsrà'.@ gm)!iTg\殀A#O +V(6C,+C_E>h|k2eU\ N\m梎ޕ˫J"/|@b#,%Ll%Iqn*mLW\g~c,&z+Ȍ6I2GRʕtXzofڞ}ҏսIo/%!Amm- E7>t შ[9u:`#'jȔ".H*`NR񙗤6EJ)'~!S)XlQ՗*1*:È-zИ|+ A3Yt~(-!2H0`X"ņJL&Y|\/iyfɗ=+bHNUaJOv)%Uӡ4-Ȅ'j[p DxZa˗fx-[ 2je *ʶWjA%Zj3΀2F*np KEO`]ѐT(,ָ)HsR?_ Dtei:`e2 s7ԱtZ:3i^KF'RgXåk{X,K 9TjE0v䰶qRgXO4Bt(43,(ʠU1ޖW!cWb xF9l5<0O^C Q*j,h(. mp{0jߡcO3y9`j %jߗ#W&oʑm.#.]_`By#=׀j0{Ey̨VkY,PQd @?lgyTV3l@-38{h(.`pUF/}6f- QH-b}Pcv;&y @fjf} | gvu0{Ey/+JKUfϰO*f}`Qddb_VOTXV X= B%=~mUJBы|`NMGg I gi0zj>ԻҐ}5,/7үcL;d_Ș)~#2FHVJ"{̫nv~y垙l^ם__ m̼;Ъ-m&T6}P=ݠKӭC;kh70 rխC{$e^yH;m^yHhb/XL uU{wp%c]!W*zZ6_}FL Kj_/kҏ6zOYRzun䘀hK $1첩e{6;wt!lHW'er쓝.6={6@}y<=`~EX$Wl߃AbݔS1q|?|~=RpԹ!!ي_[oKmfd8K*)c$(GwL4\@@OF|y5%=\n{mvW5z4l19 rz0M dLݓ(#dH%SJ}rSf& a\m׊|+ZL 0^@d_yD"_uA/:M kO.6}J94gC(^zO>!ȳAߓAF?lCFp dk -W p8+gy)ꔜgzEya/!_4./W==¶}\gl\xxFzu}z{5z{^?+BH!l¸m&M~))V|tyoŧg?}Wmť5V"2>}hJI}2{ `׊`'s 0gqO:92Q4q?6ǎF(bAB1w*CK bsYFlQĊ؅E)c2̞JQĆX~P / xvM Cd12VI|# XB#3&"at8vZ{pLw{JwrQy 8-<-/}~<<g^<坅yZf[PK/xE>7Ղsvg.pcb.jumper_2_100mil_pcb.svgSMO0 +L.IiCM8B$% tv6!l'{ω/jxUkkF@iڔ^Fk 3}Ե$hhM B5JTZiO7a$ٿ߱C6JJ''-UdeaF(FH4+f;;';FPuܖ}H˩By51!& %Bo.*]̾,1ƛ #E]Gȶ5ٷs,g ¶9>309m5rjEV̹j%vK1F6@0c5(,jeγbqȑaSp0ټvGX곹fL'{ڴԗE+uSii,fu{V/BVJ`frqK2Wq;0] ( 8)5}XPO:y`>~U_"b*iM+VQq3-.ۖ(u+@^x$ڱ/!S ;7$ ^O}J|L(೬Ml4@q澂 @;~Hl7M/VxߊמI\EhwUuKWz~# P?@:DzF277RWPK/xERN1svg.schematic.arduino_Uno_Rev3(fix)_schematic.svg՜YsHWqU1 ȩovsxHXV"_=\"֠]Q ==7K.HVau۰t-,cň*`G׿FYi0a.0XW^Bh'ddm3nt QL, cj8].H\a@'V8d }vdq$ Lrfxd="!2čGm3#=Hqh>11Ǚh}6;Lrϵ4=&Nn|rs/)2\ygmue2Vj3BڒMӹjN6uGG0q 2e*$X`T+$s)+ZIƾ}\ f6g>%6h;̲]_rY"rښ|cqY{6'?8e`Y߾رv$|x0+s) 2%a*S]׷ PK/xE.S svg.schematic.dc_motor.svgVn8}v>ZIQ7rqqt7@[}Z(2m%C/Rb'cú sfkgh%Jy<)&*ExYO,t)sYuQ_V,[V1!Jh}_͢(kt-gU1GΖ* ze?}@jn>]_ l3O&#̶/uz&k̾,EG[;&1F&S'HM"|p4U=9TG'NmM}, C۬kbѷV:8BߧR:<$.JY!ҋZ6mbCu\n\Zĉ𢔕,WR33tTI*q0uNTeYm>}C`Sb>AenB-Noah B X.bV.N'$`N;}w!vN$KbSձ<&KulT# %vY@ &kSwlk>j MY|+3jZiڼc HSG)SE^[x2PyYG]wT4 lȋ\֤&"¦@w]t@lW@ k } \؎b`bxǨM`3Ug9s#B 4c: wmNcqxh0CxN~ ]fQXD[teBoE~>4ۭߢ >?(GRJhE6~qx/0[8)|bժ;{MX߹o}:Oz~OPK/xEf H-Bridge.fzPK/xEDj" fH-Bridge.inoPK/xEug6=$hpart.arduino-shield_r3_two_layer.fzpPK/xEW Cjpart.arduino_Uno_Rev3(fix).fzpPK/xEE]Ş upart.Battery block 9V.fzpPK/xEQVC{Y xpart.dc_motor.fzpPK/xEmY(H"kU{part.Half_breadboard.fzpPK/xE"&_1ӝpart.L293D.fzpPK/xE>6Kz.͢svg.breadboard.arduino_Uno_Rev3_breadboard.svgPK/xEx )7)svg.breadboard.Battery_block_9V85_leg.svgPK/xEC$p~ 1svg.breadboard.dc_motor_leg.svgPK/xE%%k.r%8svg.breadboard.Half_breadboard56a.svgPK/xEм"$#egsvg.breadboard.L293D_breadboard.svgPK/xE; % f)"lsvg.icon.arduino_Uno_Rev3_icon.svgPK/xE}  xsvg.icon.Battery_block_9V82.svgPK/xE\pv;svg.icon.dc_motor.svgPK/xEƚ5d-svg.icon.L293D_icon.svgPK/xE nsvg.icon.plain_pcb.svgPK/xE^y svg.pcb.arduino_Uno_Rev3_pcb.svgPK/xEqM svg.pcb.dip_16_300mil_pcb.svgPK/xE>7Ղsvg.pcb.jumper_2_100mil_pcb.svgPK/xEr#bsvg.pcb.pcb-arduino-shield-rev3.svgPK/xERN1svg.schematic.arduino_Uno_Rev3(fix)_schematic.svgPK/xE4$²svg.schematic.Battery_block_9V82.svgPK/xE.S 8svg.schematic.dc_motor.svgPK/xEGq+!Asvg.schematic.L293D_schematic.svgPKAfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Lauflicht.fzz000066400000000000000000000575211272363601300325640ustar00rootroot00000000000000PKj8D&n'\1 Lauflicht.fzrH&wNϘP#PS5cY]U}&{f)SJTVRf]j_aDn$@R$AW[g}/헏wϋۇxf_opn1y}c ̾τ}6 _d/gwO] 'nOrvqw}6{}[|u{fWx7~lˇL6t&tQ4Ѣ/?]~|=\ a;ݍJ qŮ~#񧳻.W=ů>.:;7_\ٛ/_>\_˗ˋ.?z^|{qo׌qQW7WwL3}}^+yՇ_ˋx[*>|ˏ`gwW݃o?_o[of~)X?˻?<{I<=|EG>,>^^ue +3KϾ˷?bɻŏ=-ܵxZ':P|W_5e[$,)i{Z1r6#Z=䯛+ gY{ G۫ -҃fh͹Ü,=Tޒ'&bT'ڦS Zzba% +%LxrmaqSkSs쉅ń-=X’'<61KuiS SߙcO-,k鉅E,XOxmb0t}zʳgL@6\ȫfѦii~革AjIzZ_}gj<689cڶV|uFyɳKG|yXݗ/Wg?-t櫳ۈˇξxZ<?-.~?n~{\~_ܾ-5[yoXnsEBa.;֧K(agjp G"Weօ>wLZ'.|wO,񗗧ۻSzp ;=<^^ݾ־MXA.T8ꑅn%x.SFqEzko̩~Ϥ\//ֶˋ0]գCի<8P.g֣j_cwFۦչ=D$ 14퐥l9T n2ݎ5Ի%Up,YޡJ1ߪ HZZx/ kfY@\AYAv\5$GKqJ$n e$o7O/޿oyxyS<+W}|z8⫳Sh'<%bա9wlE"=?4 sϓExEZ^=V?<<}}yp0Q>~.*˕8dM'_v |Df8miŶwk ~l~C E&X//6A[U ݗץ6WdW^plU]LE߾ ӧEM%2.V-yɬM.>Ly@,~yy,_oWkv}Կ/~}[Nw2g' D%TxtCM-PS _M"=>f`Q;No[N>+c5^P Gw԰z-Ih@_m7m<'"I84?~ǯ0wٟz~qxm#BC%_\HyDaea#:Dz!=ܶ43fq( l 6_q5˭ܴ9TogOB*3{7˸r`+hKnͫe&08Ba*؝gݷ-A'g& :21f˜X*o]eQE,OKޅ_m3$W\ K? ]WZ^tq8 ZlOj.yHelOVx Bj4# '+ UYY޺kun#XSXdR3OR[$ YBiz,GExs-IDu*~ݥ -"&[ڻE y3籧3 "(2}"2w"Ȭkг$0I&#Ӡ, '+#UY]f53Dm]GVua3|3IG>Z9E9QG#'ґԞFԑ9&"\;tcWWʥVGv9 F}zt-]*jUjZ[j:<ґqܺ #6WӺԑa6I@J|$dd* {vZrI" "˜gZ'HY ru<ո62,  F?||m_`"bsku*TBߙ|$ a$ vzI!Pkj%I@暟\S@ C.8<|B*򑄄UWD,3KFFcHT޶~FjǮ$vǞܐAmXtuZ޽z3Z8! Hjǫm4޺ՙksЎ9uXdR Z}h$0gH[26rHzI9^R8vwjfRdVvPq&Ўȸ~C A^7"L# ifh5>Yo$$"JvEVa`C6~FVzi@9'h%4"L Y7CIS kHvLjgZ(O/wR?[UzgxX2??{~-}ſ#&%y.`'W:,&8 -5\*z^8@Z^3icCx|*IE2-,agEzU3{:7)t27hA,H5*ɜ+9^,lNC:̻rP Cq-Nn}S+wp(mV`R _yN#ͺM.C\7y yzd%H$=:Vx6atU7RmB\x33{m2)]:6g4cՠaTC2 1:G!zI{68-Z(@E\*I-Ha f5os_g) t1N[\5۹4!ʬ>ڹGZmfUn4W" r' zL\>@JbUVX,T2c\wKZ6~&q$#$薧DĂ@ IЕFq "I$AOKVI0Ou-acϬ$@'K"s/'u$h]j'@]6̸3~Xhr S ?'+@UZÍ5aح@+%H>b:9Ffc\{"trAbڌ]HXVJ# -#i& &x x]Ƕtr;KeOHVQ姠?;j+pt nY?g& y- >gqH~&yZK!iRGsed ,}rbc/-6NAa 6n[F@g&=t*tA'k2tqR0:3æ\9ҡWNѡ- ^H{]hM*)ڎ(E0FHRh~nq,Fz:Ytr믆LHB;}v ZB qhhzS >'+A Hak$h.A܆*$A`$A W2(1"2ן"%?Q)TBf?OLNb3~h0v}&y&Hv^WΎ]IA4geH>f9\)՟WWgͳR!ǫ?'v히\Fc2FԟҞCY/j|[eiùl0@m~sw3e;CT8쏿9hA+΄qƅd_̽-XB(flG:x?1Ϥu.6ˋ뼣<8[ .(Rm+vlM2a2S?tlK\ȭ,CfXFy<d/bz%6 wٍ'GYoU #^E#qj[eX"XV&tTl5M8 k7|9suy {^9MܵGds剕igsnDE;5;N9"3xiih)蜫_>g!-{GfOwV֦K83C;Yֵ#T4qPfOn $St8<|$n$8oD>#@>|/ԖAMuAa_DE(,~U|a\ug3bfr\\brЮ&CPAr(K+2XS qeg_9qLZ28K#" q~8Pr n:~ӳXWθhh4Q DϐY{zF^h5JbZHzFgDni3O-J(i<͌-DiC 4LJr|<䞫(qw;.q\!1mI[DҐ]'izde*.jw~%ap7D:Jkz05~fGF"jb !dI&%ܹr; jӈ;v 6Uf2I͉ANDGkD8¿y˥t%ȶ;Iԧ|͍-DiCaR5Ӝ^P[Og>2Gq q1uAmH y!KjD"MDd*\{mHP6Q Dž=YkmMBaJ*5Y>"GO %'lsZZ;?bZQuDцgHAY>2=CەGXRCD`bi]tUj[7Q4<.SxTB{Gà ~!Phr5g})f,3#! hB_];k;v v6$CCpH>@U۵GjAw)i))D[ӇhƜ&1lx4!~/?FNY;7pFlݱ;QhҲ ̌J0*_kEg%+zښCV hibL!/Y YNf)o("+;XhXDՆk@,ӇksQo$i]uE"ښL hMr&eB Ѳ!PT)YAʊZ;)fcw"fC2 e./)sFLp-+F!Kk1ק7xDL"b+D  D"_C ɒ/i_I|29;$sW]q&E'`u! ;wqFpk?AC-h 4DRkkM(ikf"`!d .fnMz"`M Al(!,̍>,,BĥEu~-g.S2/r)ILꏐwnz*u^ןy8Ȍ6~A˘Y7.6ˋ1ڭE͟ |w_B|ֺLth'_72fZDJT)u&$/2?2P6\oU*^#Q`4Ԍ#_I[eFǑbG詣8„½d#3R\x@iPloˀDF?KU&S5E%ˉ ը6;:|2PrDX$oZAN$%9 da6#8tfl&:qqL5E&^`H4urW#h+ S(DɆdA:5(Y%ЄT!(ِA,<@-g|dJsb͸3>-EgVU)'iS{J-ԛ?9h*1GAg Q!d$yDZion-Sl 6l=h۱f8D.3q鸐M1վz,W+6ǮLG)@[kqR}oweÒ񳴺)d[Qn&M`Q??!~lMBi{і\232?_̱jYrZK2.A3lMPclMqP1AKd-=9C]Dm{0n3T9G"*CTٚFqPP@1(lUȤ x||\Gmq!۽qP`dY*b2uE`rμ > PC0@Gf|5yymD̸nӺeQQУE" 8L*lNuނ$S}JOyX`]a2m3'wd]#v};\0p]GyjŃޱUDۉ pWKIU~SU;CcIol@^N'cQtz ."fkOns~?uK76xߞOp?y}}ˋwk~z?XUx|zaWgp'V<^,%X=\=ӧUuRuDǬfQFpe)& e3cZUd5g0t $T朧[WVD&s,LQ.qxஸH&僂ɩY>FJ"e&Q!1fk@WUtOF^0n,n',,kTm0ѧcoґ˄j]+7Q\S pJq*(GE|Puz'Z6:vU2Godb%DA;NL&O>5YY~*>r\Ug e+ovQH_ZD,kuA6xvm;zN= TANf U˚EݟY62G3+'Vm{4h=#R4H !AgkgCBi3=pAl!vlM Blu3>2; vEXYy3iqY n?8ҽ'ڡpڿڐq GB5[?G[M░Ɩho ˭)hMSl> ֶJ)r4iVpD# цhv8Bٚa8Z4Gɑ9ZX 6JC53&*,G5`Vb9E~V egkgCbi37hAl jlMBlɃP35.5+N%4e9cYӒ#v՚qeYRag2G@ Mφh@QfkhK ;S[=2E#5gmU8ZӔSoIp4;,p4#&su"p8ڿ8ڐqMEA5[?EkR4}eMTBJ:BefmKj`m^TyuTș!X댫Vz*0bgI8mzk'ũnC-hѲv> >h銆fF;2 +F3 ȭEDZMFom]xܦVmdq(+!vDÆabthXA4l( VѰ,Fa\cc#FQK LXBVebqZ6IbbK^;27#!dy3 *7z AT̰P|d*w4a@1RCcCarOqiXBU8hl; 6x5Q#:d-N bb}1XТc#33; YBF-bb W,<}&6@dBkfi%ss:z)8NS"e}HYE#2[-=[!c\2VEhYB=V *psTh2"iY@a9Ѳsu Z6p,-3&D"fCA!fbdbJwm4qF,*G OiZ2SbFV3)rfX3 zk4-%q2d}q8YNdrdN4vw"[5+-􁍆li50v3 cq!`d93 R7z) A)qI*g\%vU #0<+bd)H:ZsxSvĬB]jOr.C˸f{&Jo J6nx 1ـ8B[Cfb25-^Ŷ=ȽCk,v:Fx|Gfr̞PO`I<ٚsGw֎&/.䇘*Ȼ*bw hYofJiZ59ї ݭcר&Cy%%nR`BFÃ!VŘPxb5=# ՂLvƮQ9\dZ%MsQZRf֚GIsrH[M،+R2W?N(GLHe;Fo͍L(2sn*mF--clG[[ӇF2(KmszY YC dj45jng䢢e gzuq*Ɉ6ua;3xmPv RwMVoy(X\\43F@Tkg'#]w|մzMqkzd ivO- .WMs7$A\7J  ]x[bJ;[Ӈ5ꮠ6^P"achƕ)2ڿ@`r5=3xAl ~lMAE2;l@WrBltamqYfLB83eYbh)W 5=)XC/ԚNC1W:M3&Ӆha{[+vĄ(Eٚku86#s{5Y2|iz"!UٚDBhRD A7(/]2h0Bt092>!R8v)lg:e鈋>Ab2FM5k,&X\YHZI$ٚ~d &N.E'Cݫ!*$j'(TqP|̔H)cR.F'̆Aذ mrUʄ*DV{lK'@ gSBM`DYA3Q-)̙$% @NLO?=H`{2FLELk~Kfr{>K 1ZMc Q3"jR3)1!䤫x5xXcDڪiFcBLsUBe?,c\޳bYG nqV{cSB(z艢KD1꘳jzM&[C |ԴN[[zF-!Q-b0أ:n`0Nx < 8Z@ܬaImڰ#F9RtTr(^^KìGvV ȵzM+P :}[Fd4ΎLZrV| mM1MQa5=H]rPaQɺ &"C-Q"nfkzS}x◗.ƭH۝?Nj?G+q{|z⫳p'?C/ʌZp m/EƹQVK._?_^?s_؅? A,8o3lZ5sR ó/36ٌK|^o|X/G5+Ωtԯ<⋇ͥO_sϽ}?x{}}_M|zs~"~lMy%g n"i{xXF|~Zw{N#>~OVOjZecT~ B9,ܚ,/2jϦr~s?}|ߩ 0ڴܲ?Om86, ǝ̕p>YL&Y.t;Ǥ4˿0xz}wo\h_Yh -q#K(X By0kt?;Ep@e|OE=伻[]^ç(]GlPg"΁T| aZq.?.?Wy&AЇ۬0Ҹp 0& __6Qo=} <*p0FxFS~ps^s@+ -F 7h\7L6~냩?JxFKɋMsyuэ }x64Zy95B^~aGu.wG[!r@i*k/?L\;D{l "|OrПwG8 @831h+^ ]b `I{Zk#SBUF5>@K>Ԁ#I`2+٘0'/8E^xB ,a.4KAp+`YD$ )O [P\[|s6~j.$0CB']C|Q 2>"D`h!U|p nНWk7P̹o|U;Ov;--4S`4:,kj+`x¦ra>T]ϫgbص/Y!lb4sյF|c hW'8jV{ycC29L GqxsNJ-|ްO/w#]ENxQ.9h[(Ⱦ4SF2?~S6؁ZC4NZY^뷱C ~3/|F@/|#: A 1QkH: ҍnىp p>}@q'|Q04>]%@9 59p#kx=ܨ7rW xS 7 yO8sNv]ޒZj7>>w'1 ])%_"BdWyIXW-a$L28 L~1Xb8'¢f,Oي˃}_qQtygd3 Z@aCFS F2]e4xnK(PPFE(0]  x- -!^^;:uBLj"tBuy1^滐4x&4` |Ʌ)`9mR3fs\R5ܙwN,`aϡn <͡nh»WѷjzY;53oxHjϹgޞTsOѭ 1]D:r p(^T,zk 쐲9! hw&'.(Q2a0EmA& x;|oOe Zd}UP"0d= < :@;@iEn]":t0B.vpUA bt(_ @d'1b1ݰel\"#r#WՏ'p&[ G=BDW t S01Y/6Ûϡ=5?c]K2XaDt1y91GQ`ճ=RXSP1!RDpH8X] cK)g50- )Byre#ss<X 4+bi (U%h3"(Q ="){B!|^ݷ_,hH ,fO@*"" 6?Z6ʼpo4{tu>XD'P^4 @;ѻ&7US%|I71)1y-aq GDğ*=,0}O1.q&b`m;CR[n]$ 1k3JUp31Z1ZŇuԣF?cpD n@:/Q\bی H#/ 1C>`M$`eE⿨ŃE . SqK4 QlQ; 2x%xHp{q^I:w-(+h x#LTʱ'4{4b "u>EH*þ/_ z|Fm#NRƨ2G!:T;snB )KseNPQ*Ί P0t2<ΆUx)4N z] eX-{^sмJo&ۿx\R8OR- !> $/ռJ\M8]1q1eXjL\C.1CaڙڀC9#Y#M\cCw~l)QhyuPSk(5T}-Fl e/j'40jDeTkFkFEm1UFu'9Љcg&z 3x p(p{Dd g27c7-Q Te5;S=#eQz!GlSXdÊ6R(,?fJ9Q8G@DR<9&9VTE V&Om èIր~b O%nTdk䰡6E'2,b+p}U2pN簘j0YLM\킯`9Q+x& ~[{!rL<~pw(͋ ^{r#)@+GD%~T _i,8 Κ9*p$ ! ^ ȔDY1n<*Y<gKiok| u9C1`'_=q*h'8'Gx:Y68BNŞSc:) 'I,AA`+aNe'"9oTA((cB;(_CQ~4eۯV]j%UW[F(uŜ95T)m[9[0+'G1Q,BAmo^*yLpkasc ?.&x Xp Nc1%dfQ/|"m*q|l"2 偧`e#@:^m9 lYoϋ_/~ŋ~pˇOww7勖5*L^~_}շv><ݾƊd8 (kk.Iܵ6e12\kOεբs!wda+_`?"j4\؜Xu r CE Uw FXڗg~GS[&O~Y/<2Jby7_._Suw؅ZfC?\~zY}Oko:.l2ºp|yyx8q\s"~<aޅe(?JҹEqܺf9}"aӶZ]|Wj҉7yb>> RsjK+˜_oT(%5zZN/fhʏér{< nB3خa3$r}-_g_+)b4nJYm>4.l[Hooښ|$f^'kf}lf^'~k6r`f^iљ&kfwf%ɚ٭Bfޠ$ki GfyO̼kO2^myWžߓG{lHڄ[|^76YA)HjzRCB:ފ/8l0fp쿸xZ~ sYWcfᶾ| \w!_^.go Wq<˫ۗ_aKou@ 5J1q NȦC72Q ?{"@2lbb1g#867:nlgU+K;ЃHI3!ivLVSa=1pahP 4(Zz}TqF/m HyM-$C֛ltI qX{X Ls!^1[Å'ai%slCHw l| ue^apˊ2W_pk3)AQ"pv"^LqpBzW-.|euuԵ+[!USRy橈y&h5&^mg"[8 n>2Օa[3Kl7>JgcǑ(S(GS( Lauflicht.inon@~9ʼn]  (4VDK%PxbYGFMg%ћ_cǘ$X>3 <3G'L,'S&[i 9>B5fΨd& ] =)k{J'w܅OJBdL1U|xhCq=X<^/08 WNi=.,|a;2~[e9E]7 E]g^Ro2 ܲ# ,V7\\].-sD5;Fv NME߮iuBl%u޲MeY?,YW1'P]7OeǓ I0E"(wnD+dFţɄsV s]Z<7OmŠQRviK{nZx #|RŒLɪ@ YG ? }PKj8D&n'\1 Lauflicht.fzPK8D>( \Lauflicht.inoPKu^Lichtautomatik.fzz000066400000000000000000000456771272363601300335650ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/FritzingPK8D ܳOH+Lichtautomatik.fz}#Ǒ5wl3 +"glǨ5QQ*(@Wo50O8d&( 3#s#<p6M'__ф\uf47տ__=Loޏh6$&B&V^uFC8rWvW<|ʿ]qy?L{3ޛߏ㛫b շ|џ ܯ#|,R1RU~8_|}%8Cץ}8oLrKٻ CVQa\$\'wwyg:ᧇ뫫}/s'L'}rwEi ϟ er0CA٠?P]4g{~>Wס)Jx ?^3?n 7 ˫λ෻t}FN׵o;?_4>N;tY ߎ?NCu7`wTl&4hd8U~s↤`WU:vJ~ʟ91CiuC̍7[rȡ\Ӈ&7Ïn>}W6SǛw׃lxc|kx*Ikjeuѧn~c46?Zn;p0\>u>{ؔN=.%U瓓O!q ˝:%hAD%пP`hX=PsW^T~gPz=T ;8`28Yф{>G@OaS6yrϳtIu6lW;FW;!;i:{zUHm7u&+\6'-*֬嘢9iW8Q2dxqE6Jq\'NR[挜6WJq[3&>nxv28myWqvO-iQaHYFxL+eD v>m/Jmi;ɶ9mqWůZ-ssN%Ψ_R& CpzE?L3=YmŹ's_6%Cē/x6a_Vl#VJ3[f P{Iۄ}~ɖ&mĒiBy%-3UBivyO(ivt6 }-Iڅ&=e>7wɬ9$5ל3H֟$+zSgg},Lmy#i/uPEQ{.IKe~-IZ+=}>meˌv"y)i|VI`畴S{fIKe~-sKZ+=}>w)-6q V1i|I癴R{I;E~ᖹ&mÔm:ilx댓"+=m p~؟-װ%w}wTX<3=j?,̞}KQ?^(Oz?OOo7~?3ni睇؝XߕSAܟM?->kN7 hiYGjmB9&Jݽ()vIrwo덽:̺S6Yt%q3XX]`_NI?Ko2%ΡzUi.is$zaT%2 h)_&ɍ)V2'k8+dJ1HEw(5)Kz9X+tpභYAn\d^'!#2ʯPn%8' c1)2}>n ? ѝ$sUbωVdjيg0YӀɜ S3 1,{HVSB)Q}ªH~ƚXQ_/& f/$ DbψBV 5TeIOAR%fVjwcrEdNNF' &R$Df~ˆ3%fU8GLɖU%\X0TRX*sP͟N3C;5NJ~'+v)n[N1g iK*V&A2҈#}6Q6Z*G#OJG+?~_ OFZ_Ӊ?GǛwZ{wp6\q6uh3? ^]uGa矕?k ׵B|waVƴ#& v#t:NgJ5N>Pe4>=wSpSsZN^?E|8H)Ph<"9W!ԥ8I&W5=?A`ӗ;,joh]+:Rqc4c 6x8"Qq洺ƙnb:D_j€0*21 R>{ y@/==%B{;~>h>z7vb~Pr_un,-pSEU`:oϮº S緡k~cܱ1 F_ m}_\xVsMFZF-z3h+.g'jcD?𤬵ǜ&z *REe{:D N.a'>&QsUZ!/L.PKTy"Йx7ͪmUѮo˖}$l 6WHQ&6)z@hEWEg]x8BV#Ka[7˗9}or]'u SyL&򊄫gI ;>$ZL _ϯ*>6[+Y(n ))kˈoP2;j /&UB: fK׷N V3jB2tvרV&t@o9x"XӨN%odL~ue$?-\[?SKWܥ&dJu"s 2ˬ.cXIfowzR6aD2kh!X[+kJ @充hP hbbdcN m-DvލIlsJ* aXN$*&bRaMT`5΋/hP!|UuǙn v H^sGl01rƠ:f1N&J%#1Դֱ,oxbքE$44KI41iriIVZǹlz̚]g) ^tf"Jpq"EL,EZ.QEN Yaz$r$U6V@X}OU6XgZeiӋڐ%uymb xBM<#9T%DnlI ;lIU7ֱxחY2Zm1u2!].8V "+N [[&5::H 1'#gYL,YˉlPC}7:T4!,qT8MI1)1x*TJSJ=kM9 ()l"&)hYϫ|ҀNQE"yX(+#At^U}K!s!h9ZiL*ڂj¶F]E5jjo"IARwƓ,j C"7(r2Gz[9bALUgOS.gY_3iFH#tJ577謔_Ϸ@V;, 83abc"/]ۛ oyi^.V;Gfؐ4ɺn-݂&JuCIi0IiiI>ΰ$ QhpRW9%U'N2HpTmozZP6H`^-N<iWQKy7],a.]xn&GNZ,aKr^)[L7<1X8`-鿓ӦKQ;(v|DVZܽ?wkz+¢r.ctw!vmh2nqVzs,iWml L6tѱ%r`bŕD :"w>"w7>*E-na172L%,tz|ϯP,]#T;ޙ~_Slplxytj7Hr^4>a$!IϕYWlDȀ"IrHHn:eHrZdOSe4OWE?a 6Ne Ve$\2e5:^{˵s>o"U\ӛ↧&&@,ţCw)XduŦUiڧi|ꐖRM"1mZ0}0ʨp7G,$4<4uv!+vYAFPUdIŻ75IdgF+h$줥݉:HvD:xYbώm\*Rn3xSf[)1m7Ǒx+/[JX!%2:byz `EOc}Jj{s|YT* I58w3$l K(TWvȨJ8jt{vm9nicM&%KR6h E߂B 1M,L {$%pm0—ԭG,R#\\JwtyCG޸)y{/l~}џ\Fϣ];vb:ǿrBrlzGŇэkuМo'~\ Uv:Ym5Yf7{M~6t]G9nRV 3zFWfxgPӰH4Tle^`zo:OUb>G7zrTpS-\[҂sRP%WyOtw& gm3[/g4EyU[Lii6l֣Mimj0p>vOR瞅&*cۏT:lp{yy߻o?˰u߶^3]v Vxa &TI4hI%Y "O0c #V6^ `LtV@IZwɶA?ȥۇ+'W%ە9>D&*% 39UF>K# F ?e6xY}J[D2w"yi8*8IJ'hD\挲a@pV9T_n..tkFx^XL1_ZD:.BV-nT_qb%\!n6 :R]IO:%4_4vŴ4jFG"0ysU(Dzt|ԱNl,' L:VO &Kp(7jz$Rro(1*}yi|!]X"ѭhk0~_ 6g•㎎*Չ(T`ΰ ?1G$nAb.@؛ ,g&DqUlY@imTxp}o=ivR:a,£X-P# K u,$I"$,D*K}!YDPUxœ{̰Grc%moҭ&%IW$0$.vGbyYzjN*x +% {kDj^ ˔TH-.{-en)2%'C)belJ$p\ɩI{ Č۪h@t4s;jBmI% )cђJ|[I@x$ά)X{ޗچ<ɺkyۢF&2!KdruegT0\ h\7G[UP7鶫PIlP7c]7GƳ-qJnCDne!{s|mU@dbe0jTRq"u;Ren9R6l'uBLqCHD˦q%ꃑj@ʽ9>DF N5hf\+SqǂǦi}4qRg*@JgR]Ϸ Kque8[o[ [7GHVM7LXݧ6ĈqW `Iì,lBK.kɔ#Mҭe+1l7GvdfU̬!Dl~PYM(r7 M56W"R,]dg͑8۱]$ ,-Unh"G m7\TGQ;IZ$i'&-g8Lsfp VƝZ!UBInʼZ̰ ] 3`&RVK\V+\lM6D:WUkԀ&1RBlmޭ0W:c<iYǷWٱJi7l'U B"-vZMqz`"R"fjDK"]ӻOKIأgSIgSËOIaՇ Pí`F/_~?/U‰eZcܨQl0ø4G_Є|Nӛ//n{%]篿|W/z篮{]ooЄ^_?|qX<~y}ÇOf`~N'(Mn7_:? 'Yȗon|ա*! B8-:4$[|/;Dh|!z8IL_.mL&ށN&R1>??<~ ߤƏV;n̈́+Ӭ&m?—ߵ~}M\{;S5 n5=vp8{f,ֿ[sݐΞB ;^wy`6NO\&Y9-~Q |tv4N;!Ҷ,LB2^Hb8u/vI"uI}'FL=(:p{ᎤX38ACNSJPf@F)?k8)j4jrnpSu=wQD' czJ?WB_xlnJ2JI EܚufؽH`c'"m 3߲I;agoCJPVROp5a±kK˝|(*违{B{X\ wop<^]e 3'1JIeƝg& u[ҨϝTø{JeI@7d8 PwK_3s1<+\g(9^k\qGNK:1;=BnD.K"۝:chWzD~VnY"[w^[>UkP#oqo-5Pҥ61LJ>nj NЀ_>S^C v h_|LaLZˇ.-< B2cc~_q?ޡ+JB"Nr@35Y6j0/UU,{n'"8^`6_-mw͈y  0wOOzSKY? a'up$ߞ ]z9= g/b"u\c#Tc<?>-Khl@}Xa!=m`Ra0-a8ycYc!}kHF#S"Ehr1sGK(>e]%@"FX@܈7Íxǚ n@2)3áݘ+v|ֵ-vx||ڹO=ڕX"]V LȏLLJ' WťO$c"+)΅>QOFuMqǂb 02Q eٱ&O*n~tOA` `,x42hn)SY0)PkL0` XЂ-@_WP䣐0ѩ CϲS7̏i`݋1.thBC-m\f5 p Oǃ( HSw} 1  }p޼k:U@0-jSwj1oG)MZ{~GC+ M2er\V tb4tW9*i Y g)88!7`gAGg a'R!|!{ xDA14,&4 ce˂Sx7ßFbY ޺g '9߁wU w-`B9$E FٱQƱ;P#HÓ*³<;(w*Bl@Z{ J ؃P>=`%B 1܀|Mr19*P~GA>6&PFsU?+Bɢ!2ipKA'CK@ f DFR*uhf0<`r4hKD@lc ! oC'̽J >Q3V Ra{Ļ0VQz0X)D;Cë{UOCɩ_/ %kAä\T3GZ0P  O 0U9 ARޒ|ojyex;u!f8J0,nNba a^!\Cl=haC(` xQ0QyD#!n8kXA܆ր ~M7.2Kx*9{dow"3jуV.O:~PR ]KݱOr@p/p$I)H;%DXnFmh#u:)U``C<YWdž0J`Zf޵)YtiSy:}SkhtJNmKr3oUIΪHk Sƍ@FJ]9q@j`Ģ f9v"s7$QHzeCT!Cшyi; .DŽCd`:͝ƨ@ nZKF1gA(h$?F67 -éahV* G@29y'D}&BA@'$)\!rEb gc rD wA>5N+cKȑk09Ǡ7аNuP/ `b1KSb8 p«F"A1aC C&?G&@` r`:^a h,Ih`6 '?ɆsBh4Ʀd0C`6<0_[߃fu/7FB$)`iFnC"(tФ] 5(~17y/a-ι@@=S[A18yVo%O)Mз@Κ4 [ t ]06Y/v?c~/5`RхV9ds+1GxP`,ng Sbp)PxtA?1,N朏 ,zA5 J-)DE7erD{-$fǮOhПF2CO2W*CwS A^A?$6Ad3d0yìb/rw#"(8)* RޮjGTW})7J- aN 8` g@;r@AW[d) 6\KvҰx2yef,Ug<7c#^A)2A\߂^EUf!{,ph1>"ނPPoE-;8.E/w\U8]q-xԘj vt\}>1m?VPFqjZRWu,0\ z}XC#i79*t:* { [C}扖UBU,*"V31Љc̥g*h 3(=u2aX6졙2Qnob<z)Go]ݐd279R(,?f5QFRD?vS U"B+ELEY`V'8Ma֤Yk@?5 O-RD0TEdq.$v7 H͙An35:O(1YtMLg0YSdiLV+?Q.v`9y-:4?väcGN2?t,3oGp/c9ai3QL@D pSebE/2xdVmYk_%d2ªP5GÈ8WXA`r0áKܫ> Ta,X,q@~<Ma,Xcg(SBw0K[+EVtm<t'ov{EŴ N1! 78))%7<5ô}i;DS‘f) 2O!ȞxWG4cgh|<݌l-PC(}ohJR4>Y@gZ3|tft"(8[bEsZ+f.:Nc?1qaueA|X#&69uj !``p<*ÀNGӂ9W3I]2 pE Cxyt.lj 9F,cK O☂V[k-Oi'"5o$P0l: #,8Yv ImK%_ͺլ+f]ufhk!Nׄ!Kbf҄1݂^a9p( ~\`48p4nE0! 4&SJfB~!reoLY8p@\<4o^j a+,>O[KlڡUںe>ͯwoFϣuzx|}`1,=8xˏuܿv>§wJ9[9F壘d 83Z q?Kw)(F0Ũw* I(d`~_7YYne+' U Gڹkzx5WBٺ;%Z*%A@~o CG%VDGY 9?LYF%ܨOQ՗c:q_'6=77-TS`_ Gh䍫9`\+=тP 3uwG'@3DNr̨?`w?0_S3yMTKOa d2,*Ɏ~* K*ȟiS:|Ӳ;vf*pkvh, *MAK_W;>;i:G?'jJh7U`:2 Yd1_ PHݐI=i@)) WOXTHwhbHKRen8iZ gTGQNB9W< R]{[zVXe9<#Z:g5א|"/vZIBe7!Tpb-^[k%MNM[FPgjn˾eb2ip(O#:W_ՎJa!e '(D~iMP`4<= *5tX:曆nТZTsB}md? RheOHRyXZ]]ӓvT@q[7^j8':,[Vߣ 50 C"}n\hkԺ"-k;yz/Iη%8# p!R >!$Z#akY TS"a<[gKFWV֋lZ6 oU%(=J;㔥Y2Z #+c: ) q9-C")+8IYI@li5 !)S$Ҳ =EZ*HU9˥QϦfFq2ӐhՈ:"+:.FQ0)cBU`\`Ĭm訉XXU"E"9;:ĬYq,]ִ7N)lAʽ9>zDn*p~&7 ^UIfrl8H*{sUȕB[вz#2CGdduƥc$5GddGʽ9>z WHPKn8DDX|\Lichtautomatik.inoTN@|W죡% T@jZ*@ oxl}9GwkEeCH!DIķ3MNe8VSi91*ñJp=8 6pLlqTx –L( }3q9}lt"O:ϻ^!IvI"N6JPsqt@̫]gPQXㅽFPj#yP`o{[AvEWiAU` > Em{["ŅL5eq5C0.%ѭc_3$ SFM"AT+tIr]JF;7\n;ťz;- ڸ#L'dR9)Ze_]^_^<)$jTwcr- 8c',] 2}EzE܃9SQX.n16|Ŧp&4h7D+dD˫sf7ryA51i b<&11G6mՈLUYF4Bg{ߎW_:><~~6љ{Շ~ŭzDtA[c F@!%OϮV^Gs7˚'ߺJPK8D ܳOH+Lichtautomatik.fzPKn8DDX|\~HLichtautomatik.inoPK*Kfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Matrix.fzz000066400000000000000000001566701272363601300321220ustar00rootroot00000000000000PKj5Dzʗ Matrix.fzr8v.>owxtWЅzSPVP]q?? Wo>7.>޽+UPG{駧O7_oH_]]W?#O}u||o/ txa?\7.~{W0vwUW_n}q}~s?>7_wW։}zu[x3|z|_E'@GL绛qo߼?=/&޽`?=?)>x}s_ &jxw-LwY-?>=1b"w?͛c~WWGV몰}_> NSvޅV7Z%mRޑwoUFW,ɮۧ??|z/^nﯶ p7W7W]߿{ӳo_&_'?vvO/݂իë7}x* -W\7+ZyR3Ak,j}&y]kɶx&I] :e s9kd.j}&\}GZ@:$2P ouNZ d;Pt@u_VHjh)[q윲s9ik:sE1gnSޏh5p&[l,YE/:e"j<}&)\%z&I\)qU;'r#ojI*W/jcQ3I~H63IJB)H $I]&ugoĮl$3IJB)v`IzW/`M6Q `y%s;SY{Jɕ ɕ*EQ?r\?'hb]$eh7`'2-N:厠quUbI+wbga$`1T1rˤ;ùN8%_JZ;U1pkiʝqI@HfēV6#VwÕ$m&e6ivq#$ {h$ @R0vI(Pq3iґ_f#%-LtĀH$>F#kFA[FsFkNrШ'mo37zSMmįgmʎƣy6YLR{:'4ΛЈ_$x3&^I"c^4y0:g0Λ_$} ɋsCzSCLwTrNߙ,!5>8g_/$QG9MqF8wHF/'8ىg|}'f ř3S#t;g)ΛnIbhgnIb/nlyS3Wm$nͳXLA$gsY)-v\7s1>^$g/,w_\Dg֝G5IG@ߙd1#6><8gɘ_b#qI6cs>J&_bcj\ʧw?Wxaۿz}zӕ?5g;lEbS}sVZO.OS2ٿPi_mto>s KR:맋7Vo_=ۛjow5|&Gnx S ~&*$|jLZ,_]N(#b8舶$4$T@"g®m'cwv6՟^ i$0(I ã =kBHiA<.J߂ w(ƌE݆a҈◆bHݱ>mNx(Ȍ  5<&X^teY⠗L1zigGC&on\^_ NURl/ =>4 O@;_.[mEuv٭]6۰#Yead2b 6x1帥A-Iy-K㖔j{ޙOKKZDBٲKn23U}Ù]z|yhwh2]-;IZً~;S"ST2ud9hiYgF+l$ 6x<ӵ[gZhR}!uhrS0kDsm$YQV$KE^3@wDSh%qy怡7Lvҙg.4 ɫD X9fc*> <2ǜ0䴼/C2vhp~ւ*$KaRh/UF2-jŷIEdl)/ 3/ =`4>aоrwI\Ԣ Gã{dvep!%ozw-QeMyˤ*ߘ8_*ݽow{_fX꒼}'5Vj- 7ǻ7Wo?>ݼ~gk<^ 5w%S}=`m$t.>;ËO?}|sO<@{<+ۢyIGkfњHQ,AԱjIW]E 9e #ͩeVx1uX*l-pXٌ7$ ^MU%Jd|7DpEXR) K%ohqt`}֝*]vXJo83P1]l5Uj+AJ* 8Lc &cZU2%6$满SffIJJs6u翡3X W&V4b]rvwݭ1K OR[h伴nhuPh.%KV?k-Q5xVd-ѶhuI7| ~~5Hg׋ʽ1(5zjS}KͅcNLTOTa'<%:67 81Vy@:)KKq=wf.aס-IK=:4e —V*aW'*}ccˀohYNR޵. >]Ŀ=mrTm9jPY4Wx+,v6Ϝ+xxD#V[y&g!ApK0S+㭅͘QwOPVP)R8|Z+(ъAMna:h&Sb#q +;tZfYKRH6 ׼yPuw&tlJLաϸ{h? ٵVs@z!uoGg%v x>{#3H'~E:nR,;rt_䝽ӽj}ozI ؄45g:LOǐ;Ūz[sF gNa \`LW+:Ӆ*x3=>[g:#^ ;_ttp0 *vtz eDOlXٗmY`_:-g-_:'lߗmK/ts}.6;ӳ3L}:ӑOtn"#X%&;p;QKZsgozHfj>w:ۇNOޝN͑ޚ+li×4+u>?]B1Yֵ!kcIk򟳺kt ;<.vUPǕ(\Yqqa(ٕF /}}UChL}gzhgzIts3tLt${Oou;% hi7QW]4ނtF;}w^ߝntB vV7={S!M3ӟ`LOߙ=7}~G䦗+5=in^MW/27XLN_G}.:9} UN8eYыno:m]ROOWٟ8vgr:N_'ϼelyKzyM7{өԬ=9}|O]ۗ?r˕tPvP?qΥ<";ݬ ;G邆tž?=C8ST.IY !ELAEOl0;ݮϾcqVkbvϟt󲙟ٟeO~:ԑ4 u~[^o~:MoI9<ꔜ Uo\Dezv]_)f0tf٥]C[V)l᧑2v1zEB+]ohQ =i>t2zbǥ>.utNO˗:g-HKS٥~]8^ϥ~&Iz[̽ԱlzKq ԭ٠ .u]0gdz;=_0LQOvaNRf(ٖ2uݴUWk!$u ؞K`13VDR.K,Um m;1NarVsFח5K]/~xv#{z]s,u3Mœ׫X6%]Py\.uN%UHfz'S}j CKw٧ SgD+ԭ1[ǧ~^^:w(T9]xQrMDm}Q0ƥkez@pIo=IX6<ܒ6 `nEp_d7N6& &#e hf[^o8MoAylz<?=>]M ?ۡm.j/Ty=nb~䓩av_/gt%-ۿD(~މ k+=]|&lo] Ɯ9͟Һ\oMګIZ `ϛ燏[,Gy=k8(-$1@j;2o `9 \0!tB\9'=ؐ9GžaTf Ī#d ƹ j,EBKib AppCIAgAˊiVs2ӗe%,脋OK`Z5T愎ӳt_S-VHǷ(`K0Zʔ4 ')Xre)WggRSu4"[Ru̷ָ-nurيyg?uxRHGEGbq-&9)tiJQ+6t0RFEfODZ|6K+$a-N:[J!+GAXsa_X#-8/%I̱`>9"sk Yd5cIv%.LbCWp"FJdyu٬$YADTPiϙ*LwgY, .>3Y-HN5:uT|9ST愎ӳtϊj h _ u-Qɔ+6700όr hc9e\}|౔q[P ɾeB}L$B3:ɛ.c+5$oE|&~B ^o>S" ZH[82vRɴyrZl픆9NY/&.JQʰ&2ʂqQBQ۷xhj[9`̜32\1$Q' r2 =WIC؉ȂuIZs sZx9- 9߽;{ /eQ*9g9^φsR>[9`̜32\1$ QrѩsF+$"l>!,=c0rRYEBi)LA2l*4!^ PV `qú*uw혵TG;BOl'C2v[]'dE8{cӀ%Y҅PN?Gd%az)k3CNg]U$I ! NMeħADX4uIgBsewgjٴ\D4`ӿU.7J.S3uΧ4N; L2K: SDM̊@F 8Y@9Q# Ź(+6Oe< XNeD I 4X 0cg1^HƗwg":1$-2EDML,2tC $ Yd&6ʺߦd5&6oXl$.0\s1̢\Kgg\RUK|J%W-eE-ff"N,}Z\Bp6)U rP9w]>}2Hq}̴m:."^rΔ~@ *0-Po0 ^Բ/g `3@$_vL% `CNe/#y 60o}lJuH6-ׂ$*PARI "sfdf{$BZ{ -NpԇtstnD0fT^"n޿tpȉ-pƆCG&ɼDXpe{r$'f_<[T5iPFtsk67TтFRO5 )}{T#wsJe 3r(/NO$E݂#tΏr*sE݂/t.LPmPQ:_leZt~ چ]l,d,>CDtܗ| @P4N'vS,vf8#%AbY+[8)[SDҾYO9bZeJ 6+BOٟ+Y"i퓙r^a F'Ly9N/':<60JD2wH$I yfUo L!UCӇ=_б~؉!r/:=03>0,9;lA)I3f\Y4dYVf[3k]X炱hQSrx 3+V=hy瀡32)YYD5q1J8鈧V2<]`39 IX҈c!=tdy(iGF NM!dfsǁ4q{k y~xeFM??>X~ۯ_W_ti^xm eRI;L ڊǞ }PkpL=mǎ-fjh.2'[n;v) ywvY  :!)(g#džN^Oδ=*N94y9a5+y *vGiDmh*/:c?$'IsG%HC;j}6{DNG2 cHJ8 (S;qx%- בт*a,4w*6޳Zlhˆi>Jӂw,! ʲ(ţBkQѴf6JN_X h0du6 LbCCc}/!! ӳ7j69㴐5^9 }53+I753qPi}Ԭɺʜl^IG҃y:@D $Љ13 eXdIR&e)c\B"q8. ʤ,:tzZ0Lf!enZ5)s'Uv5݀T\iH$IOkD#ks)fdn:=)x$cb"f'ZCHd展g!hVwe6EU7;{fD((Zgы֦hh$IhXpdd{DNG2S bHJ>a%ڢ'eXfp05ZJe mԉ%uWTmUZthİ\z\DPz0tA AʆL)DujdڳDNS{6MbC#E&b#Y-z'`hRAmMe:MOIGpȹΟuq`p]6ӱUDґ`~@ 9,6tz)R IIGP4,8 EctaPP4'nydqJI8k1F 8Xy<Y98$IqD#3BD#$sqGJ>& x,ɖ(8m !$Re(ʼ,:t:/c8O MIG2ҙ.y) b"9P᪷,zh}^Kґ`^G6DDC$s )x$#Y m޺pMqʈ. ѼFN9ZthǬM9Cς?,9㲠Dwj8'ZNk^䊳шBPTgȉvHh "Y7 ,‡Q+-A3yT2wģW@wImOujM35;̪f'.$#D* ]w}*yA E}HUEmɴl^ IG҃X?q"'Z!ņN5C )IvEdg 58ځF?j$PH.'P!E1wkgBiيrZ"fKdhYcf91zr|!IXȧHCN4>2# >t#qAϠ.)2ȡIyXF'e>:=T&qbJ>&TǓw9 '0how&cCɐG:&CɦY rIYl0RIIDŽɐȒlK\ꀔNZT|F#[kAdC R KD!u媲EɐBHB>5GFrXlRH\Чfa (9F؁J `VaɬڅQWbV:7`"~Y J$d4d:>tjFPɐl"'!ņN;R12$ 1dab&p3D Nn;9S8RXeN6'HNZp(qD*XѱPX1X;D#)18X-]>&Qp0t>8 ˊXtZK>3cs;IVHN Zh{0YdH$Iװc՛O5FDc$2𑒴kŇ1AȒ M- VC LҊlLPR43;%iQRWfh($;Hivç O*aE3HWaA)tkDNF2G ѐbHJ> ,Ӗ.3+Q4#:QcZ13O!Rq}g\T=FbK hGY}@l&kC'pI:IoȉH&j3pGJҮ! I)=, (ՠn ͨLϢC'wg xJF0nvRUơbZ3f AlUhgxfC'6 %Hzh 8 LbC'7NJ)I{x ,HJ,98Ε%-OJP4( kZX3Ge0m>еt#S~A.Ig-b -5@iԓX#L0_O^3d *k*-RU !7,va3hkX5iȉvHeہ E]Îާyoe8鷥Y0ΡrSsB@Nr6ڿYth,C,sJFM\߼,2Vxr%,fы-ʊiXCQ6hdb9`̪)X dIbV.L̐4oEJEE(L"-,g19= )InJ3)Ҍ>(ӳS+͐BI:LѰv5"'Z#ņN4C !)IFizYfga HQt9 v5}0Qiѡ]r G  xTaՑE;Z"iH%$:b Yl"'!ņGE&jYQprJa_ Ia*IHKo$_[ugb:L?$mlXTi2-k: +#rY-9ɔ,6trVHI5xf)CA?DOvdK;s@ >)I[ +1eT}o`D/Zat$]1@D$S?bFJҮ btGAr;h?UYf;ZYd֬02+J 3ً@6m1ӦEnheNJґ 3,0(kDNF2E \aBRfX`dIɑ=g8`ڮqhjEvLI';%Q0nΟ5@iXZD3T-p&Fg>JrYT֫0C!4CD;$~fɬYaB$grariO RJlI B2/(W̨+IPE HT"Rc4*E hoxhC6"Ŕt$]C85"'Z#ņNpD)I/"rEf|an1CIW8pR %]9Yt P>nl|liǏZ_|G/Zy8BVhed:) …8$\ja…Ltr '~#~ce6-D#s"_8a#%i\ r0lEJQuk-4B΋_{Xtt2se2v۩da'(rXYR IG}a4l8lȉHgRHIa%b =(H-;ss9YK )Iĵ6Ԝ !/oL,"h9p#ɉX@cm6ܐ͍̼bC'%'"}Dr"Xy-G*!KVFdb_K3!"3O %it/:1cMJZW:D-#F7\}NIG  YMBD+$a8#%i̐ Ȃ̬$ 33oVת~㥢uz6>ӳv$V7,*.p&,r#E3(c4يC1CNA29 NΐGJm AeK ypi$yc=2QJ>"R֦5a9H YS paJN8?pFJQ^V^\=L> G7}:/P20402iȉ&Hb3yč= .J BpHBdz]Gg>:13;%i+U9۳Xf׭iHM #D.3Pt8葎;/rMFrёXld6R!1,б$[K``5 ^4+Xui2\gBW`.1.[EExRJS][f:9œӁt%S<֝aS Q'b 1%OJ"֌3Є6Z0f9uh?0"ƍtL,z `8#I!&v!'ņ É)IHY 1fsP"g2:C5NUJ>&Of,W>T,z /^#a0$X! q!'}ņP;R10$  7Jڭ:"` l (pűB vEviXlC6%iAC>N^0-#2 mDO7>zb=R$IGځEV&hs#r9Zl8RIIG05,@$S 355RP:t"2uv',2 +7Q ffݹ3Ξf{RRahَ#q]dcf:t#a!w1$ȱ:grȉ&Gf`gf$.N7$_ja0FFrw$|ƝWwIBfrDVi}/!9c,LBBnnUإZRZfRA4Ѽ5:6t}N,IG9YMDDK$Bc8#%iΐ Ȓ,J f oUɈ ǻ]qѢCgh+>3pmLZT=RiׄP@vݶ:S i;ڔ2EYRx Ԍ8ѐJ:f-gȉHh)RHI,SKraf0P4(/iȖt望REFw,:tZnIђ>1LDŽ)8RP"Pي%5+̢-fH#a!"acM9+6tRRH\Чf$_zY% U@Ntr"NHLIǐ0$i0@h'jjyX181$:`iFHd3pGJ>!yZxYV[Ke25kNf=jyhcJ>E*fQz\tiײnAe]%X8Q#Iwh^8q#9\,6taeH#%iŰ`Ȓ\l20rĢ6ewL&^"ΦKĔ k`3L66:,s֡C^$,S]CCN442 7I)iW0hF:1%^>/ɬpɘtzw(FdLD 팆D#)) YIA1/&X]tDN4A2/ >Rv? ! 2~Y L?Е9z XN IG҃YX4#DN4B27 }7"Kr 4n1 fڀN LʢC'&+5OLQhŖdE-.[JYxB:OM{ёVhud:-Y'h$.SS003؆BjXڷ1e&:ƙ5`c8nvllhΜ}ԖH$I&fX`d9 -6tzOiX`d)FKF9?T Ͽ^|x+7ŗywo?TTYGʴW6/p{su=+(U07yӇϷՅxOO__~zxϟu??#\a{L SV}L4LsT r?Ӆaߟnoo?5A]lp_&>ݵ%>t߽:nǻooއ78dgb_<=?>v'T~^Ij{&/Oo*ӳέҍÅa);_ދ#Motw=^+uh ,J]Cf24cnv%Nw'TX@{F0- S=YZJT_v \|}|{hA~wqݧ_w߿ã?~o.~/շ?|{?w;D??^pwte^"V>6?~}[/_KӽuAUa|?E~/@rǟ^qqh"J)xV- (hv#̄e-/ڧ;+g_9ݲ~Ś777ӧ(-wMu$9 cor{BDKVG@o6М^;j\Bc_>M*>lx%FgGKFWRd~@i_rۜ!j.EoXK/ a5eŔWS>s?l,N7TP;s-̓k*nsצ7r;F н_.waD"0?})w?RjweA} txmodV^n~(?DD; ^^Gykxx}C/%sH}]2mǙKa n:tQ]l5_}d^O@?L tb*<˽7`Tx?NnG탱7W\_M7XWV4n6+ȫ 7nNI}D.DnIn>Jp˽XoVv"75eOޜY탯ܻdr咚B3 -*[Ľf͏a?ڻSuGx4"[ѥ \W ~Iݗ 7H_zP?R ]n?үOs._ܡ=G9UG}G_V3p/.=b2`Bsc)= ht?/4mt*JfI<~o_1~J7LiIc'R`-Sٯu᩹桞2T,ᙫ?ڦ * Mu#N??h@h )!nk!ƿ;_S$@te?8 @.@2i]"CW˲s{e@1t2QBtnl5ؿnک0|6TYQnzoN>AQ2,p6a^ݽnn{s `nnZ/i/S "(*7+K/ =yO~zkh7r韃N|nvB `ȲQy^kᤪwxڏ_WjK$n{ӛVg_a<౬㱲֐ݔQQ&]vn{vɚ)"Ek>[wM(zkWKx]oH }F5<7?FWp㵩=vu'M=ӎwi1hʹ v7)L;Cϙv{Su-wm!i{bb%N0aa1oq+pR]C sGWo(q.̉znNjS W qmZ+?Lx[&_U\gmecԶE#kY4pnVPֶj'v+P[j dm+`6?gU(vv,B][`K}p翎yF*ȽFgƛm;0OսSeo__fǁW/a1E΍9o@3 }ݪ!v35SPg^R˼-@7_>սe=$t!-M/voy +`;UR{u7dn'K؜`ׅ% _蕦1sLKٞc~= ؃_Wx1lM3y;+k\7*l_E (3SR;exLoU w-~w t6 %H󫁀okew,T1rɯ"x"8v# v{j;o=ޫc`h9G( xk==e:G[P"  cTrO=qܲTaCqk'_(|t藁ڋg5R +x(sL64KDyQym۾v~lbuB =$ڧZnדFUM j׀[O̓G0)MP3W4~(¯t?c@ۉ3N0$xVŸ_XrBR%~֪Qͼ,ٿF`adKf e7dT}-^1 I^_{@\֡Em(oQBv &Gn /DPp7t+ Vl ^[9x =''`w9^)< f>3&V$ e.=RJѯ%[\zZY$େ__N@Z)>} ̛^bY!8DFn,uNGzG&KUyW_~gc`3-NVc f%aM"EeߵVoalpiGF<*&^:7`0J-ƻL> +o 0@@Om_Նâ`HzwSs]z/FE~w__XRyRb  a`n 'citYGs=u5/ُ[3ȖE׽geqިP'-~pԾ)C ]l@&KMyyN <~2RC0xyƳK9_@OK)GFOb"ൽÏR N]% ~WO7,lO=^(+H߽ OUhQrƞ 2 C!h,eg  .[%_r@]+ȋ +W*v%UIS5i?@қ@~_9 ݰ·VR"Dks\uP` ~dJX,EѼ  3< n_ 0mAC0i;A`~Y`]i'> 9"`&PS?1<0O" I:w 7pA 1 ~+9.@}̀կw l.!F+98 @r h7mhSYl}/$/M!AE{ aWR % *Wm!:wc$0_Ȁ_|W%Y~ a%ln xm2@#l~L<2a/|~ 06`  ;P3@~xAx`@# 6. #ÂR F\lT/.$A`W~0XۛyGNQS ^~_פ?cvQV10 `쏀>&* k"c6kb D{3L?sJRV۷.]E5eH½S_۷smvlW| =V hح[rz껕=9 qx.{.a#@ꉄL# yxO#mC6KȰ2"_{ҁxf+ zDH| Ba_|d!0[;/]4C 3U4c^r>AۍeجkϦ!Q`ۗ7/F:}_)ةKVr*5,9+ , A=hR}S祡xGqݣpwXm>MM)*AӝKȦQ'_<`!oM8? "z9'~EvMpu)6;UU WؚE 0pu{tikJ+nUT|oem֬~t?lL/o'j/>zg3va/;k'/T!Tb;2Zj~Y*lPyM.8X pxd3nʫ=u2qmZhtba1GO|)`GkLdenn KD8 Ɠ*jFR vn0 R]PDxחd<(Ϛ.jяza7ٹa{=5r瑅6,b_jv}ۨ=8lאuJFbɂik7<&K7ddN:`e^ݨ9y 4l? %$6[ ^[r#s~&;FUL)xX TQf E` ^3o)/ VdvèWǰoV@XU_HVEX `]C^>|^ۅ7A~EQ_5" ^6עEY[;ZZiqRZؖe}ԷPqsY_l%ky7{E셰 l#xЗa_ }ھ4_3 R V0M8TAbN+]_#;籖dž<~E}}L_S-1j8>7J6&hf:=?c[/Y Wpu(8P~Ww۲5f&b~ D.1p睆:E{9˚VL0aM_ԪL REOa:@UO$þ㧤w_AN~ v޹W2DXY$>K(!'!7PmpN`A b@? ] m„a ,jVA.0"E_#,zۚ>JYbW4!psUqBhԯm*  \zi0kV↵. .]0 0O;4SB%3 .o'@l8p\<)!:^ L;x2=ڮ_^Oﯶ/^nm?^4ws]z;ojo֘Oבgd>]JE[AG6ko?\Y6qb;7וVnnp=smD^ɋRm?j;h" V , T"@/}wضgU}Vݧ@Ϣʻwk z~'HrHJ w/>Rh2"#7k pz(sv|nz;UzeV7\QʄjmEMV,Ϻ[P+< Z 멕lx*juJl0/Yo,Պ%VG\U!d[ɔVǠP"Wh0o=VmNEp =>!xCabV7[RU h7, `ip:0+ǙEK}ճ V+TKβZUQV}'y5;8(X.M#I*"e>ۯq4;ڝFxuͽ'+g/P޽{}7`AJtuѦd; ,cvo>[I bOpP\>S"@9jp;z;jgLd$⌤8#̤&H'QiGHây' NkJRȭdR$P;Hx=g$aUGUWo`O =Ļ Ysn^ߤ['ʄj?nWFoG1~C>DTi(uSK]T m4w}k/,aoMDoq=ӸntiJ^w:?]|W._%LKDK׬K׬*/֚ӥ Uui朿^]Z.^]ڿakDt\$U򢊗KʾPgj"t{^Wŧ,) gK Tk*V*-Pfڋsz.-P[_[PYgK((.(A,˝-ᭆ%\m_oGJǛۧpފ?g;(E5QCq'# Vn/p/WB(8#9P>w4^tWFśk+շ;nB=[{wOdw7wϿFc[F磠AzuB?y᭲k" ؍rGO w(SGt:)M![!,CB&V;c!$xicT Jȴc`SJ>&y j,e9c37mT>n1EemW^LNVb#wxX&wT6ґvTct5t"q\n7͍c۵ !'4$#*ƒN-~]~} 7<ߛHHj̮;;3,5\Æ(ͬn>VRJ 6Vw$`Zd>ט,1V$,N'V'X@QvE_,5mg՜":;iXfiAbph{gc"XLu5OÔ> E*65 IuWHf:֘ʚ5(A]Í}k(\1ʝȚY79Rv?ń 22W(#H.o{eH._a>+ ENgeZuz hb933 ̐"I:l3 ''{wؑZ^/,Iϖ;8a4~%\#\,79y-*uIc#cLÎL"Cd+ℍ|ju`jnjUENX  LL/L4wlJmX# &6ڥXt 2ؘacHߢ$fZ=Ӳɱ1 s3$#@vȉeE! ")IHdiEYZ=T҂ @͊N-:t:GprqhK;'1.&fZjiSyR IX҈EAG{ޕ< RH\ڈdIntJJ,WoJj ZJWRmjl*SЮsD$0:~'ӱBfQĎ30L"C'ŰH:B֏) -RHYd8ENaŌ}D"XӲ$f f$Ł1UBjB[kTR H98SpN(ڜl2x>F VIGCXQZ}fQ@68RpR<$e qs9jp(h0 $,uS8_. nvdX򕏃> vp>߳"^}B5N)PZ5PbrkS{r|Ylh?@aR<,$[>gH( &v{L>LC'XBufxHzۮ‡fKe:1І:ґp6ga-ѷÆ`Fg :9҆9R14 z,In9b=S5 6>7S(μ=BnvltivT_NHGC8j>zjr>0S(NHI)XrQ0H(W#כ`f7u5̷Cg[l|k%i[G㆙fAe:lČt$=l!Y R$"3-h![HcI&[ j)pY*QH["oCe:{1ȘZLJME3͓YdTFIґt GN_Ӈ2: ˧D#fHa#%i׀C)iA˸l!ջ!ׯ'a;ZqCE12rCγSY/6672:t@FB>5EG4XA ,k˸&τ{Y |EVo!Z3ъ1Fct8|&^SqFB>5EǻZ aDL"Ceℌ}j{Je]]m$< ]/Y`ޅC IWd ɬ15]8#I^H@!4(ׄbL‰)Iޅ3]t:C )I/"Љ!.x'bXc?ګbX}2aņNma}D!Xe-8X3ak2k U'TG3nX\Q/gt% вǵgZ:5)#Cpv|94݂ƐņNV';: #惊(踼6 *wJٿ );( E:Xq#aeYS=eO%HUDG?O }:|YbD<>ڂG5B'ASm汁Q*_SvZ?U cRNbDU&<>묪Wm|k/0oְhB>s?8W,g=˜,)1!FKV!tS}^Xc) u`V1&{\Ѳv!cRǑY Z&.vAVjgDV^BOaa⢧lq,dt,_ٱaonq5wxWbZ9lx[/]m>2r%H$ bZpK{0-ڈ )خi>pǑ_Яip-}^̨]A]tOw߼;Z)`e]ٿz0SA]4${Rka(' H^8#(ߙa7*l0Mkb) 8 J1a 1Έ-X/lt^dXɊk`ɹM?_{Jt4#ە~ 0( "PM-Q^eǶ;5Eiz"5 ryk1 ,=k=]MZC[=u| V WZIOu-̀P!VV@O7 :WOpV ؟m1C0P\*;q,tB!b* _#{ !vDqfB(ٺ0O7[ 3.8hfP.==aVpիzua⢧l.dT+[Naa7@vGU %k0yS|-i|X DBG?N׿͂1 _'a 5 =e;Nj_ [ s o'0*fou=,;X1t *(vO^ClQtD"VBk_+ҮC5!cyk0>H5ļNZmyCf_*R"4%JaL'Z#ƑR[J'R2Io^ /oųBOa"l'xZ[uI {sVn0-//<gbF3CԈ sֻB!X8W;4r=]xS+(=\g*R`bD_WW֊NawIN04Iɽ=ص'L`O[ͦ>fL3h'[m _¨,For#R 0S{ 4jzVn,ϵM=0ٳt2-ƞugtmY1ykF,P^Š sxBIk/o*)j t%GO#\ '\Z¥1˫EӾ]mx, W1,\*|| I¥zaQ1#ҚpB…ʋ~2U`XѾ ,ן`͚5 =e{f0fU fYJAdZn;ijg {–njY;i`*R+['ӛ+[(h.ZrqXǢ׳@ySwԳPQS|eѲ ؀༅[LDI,ZPhiQ=e{hihFUY TF?*Z0h/ZKZLƴl8B8>zgKtEGO\0\0˚}R>C%`*ҽpa]lX#М4"'{W!]'%0++WR?[,.سtEEO5KWV,XN:4U1VsaO֬V'":RBJ ~2YJXScw^ɚ"wPV⢧lk֪\U ZWzN~yZVPZ"l︖e6;j>f-,@BE?ެY(hY`1 (Be⢧l,dԴC,m2\4e=dq Pf5lqfb,@BE?ޮX hX{Xaϼ5@qS(2j*V^a(r,YPz!+`)%Kε<>;J>fL- dze]gb͚+Y)5 5KWnw zOG'Ta>"ON[旷jCw c*+[uOXqy#zQ82_1Tek  oQA 74y?kJ6W@SD6Vyx6bvqȹ\҇V OkROkGɫ5]{i`*n}~`qׄ]ފME{RcWdAG^Z|ԔN$$T&ux]Jvjb>9i}u hD5V!ZF?\(-,'KV)^FOaZ(ШhZiY bi1K2ẁ5F?Si5:GfZ;i7@ٴ dF?nZ h3c-K 6y Ǵ@QӴj_r%}U"Z'7h-BOF_WP`\-ZefDR7S֬B(Y0qO"AUf= [<EOsr5-0cA,Dw`Z:4["6z)7]̈|YPrK L^Š [Ya`l,fԬ-õeӃBG7aݻ`A(hd1jx``zzv%Ԙ1zV R,^ǟ~?~Yn +0=3SȔHU~|`.tbs d2k ('+\4sV#liPlo4 T4*[Zİ bEq酌\J}5ouCf0zKCپҨL,Z--J4P͖F%%F tK%HOai(iiaiJ 3w^[-cYg"/|_C4Ignヾm!y3a 湘V|S{M%IǙHsC;a%bi+vB(@yRQϜg C2V#cgCOгFSҧ~I=S l\ mAaag|;8igU|(0AqP?۹ąY!;GFHM;U1 vv 3 -ǥRmL|\+yIY ROqQ6  PB*U4Ox~ %.KC(i-i%G !);N9`Un4M_!q?28J JlhPzLb)[ }cpVI1qCJoj_9eH?ff@@0 K\Be3PvJďJk ETXU}v\ICDڙܬ)ۛkg! ,jaLiZ;eI?X;H{CG,q=¢V@S7p0RSj,M_=-a32&vx&,Z1Z; ;@$3wgh(/鵳fPZ "dzs  jqb+vB(xˎxN+;p̑7)4p{p"ؤ~4145G5}off mexsj9;>Ĉ,J3Pti` L>۵ UY!ng<(fa,fBmb{{ldc qT%7Wc3+Y-ms*i7zTFHrwHfDɴ3nR|Sk)EH?_Y3Z[} bKֲB((>zʶːRle/ ^f&в۬eP ȋer:nfiD' ON%.HX dICGON*KZ4mEQ=15sK/d3#ULO5F4PI ƒVeI->ĒJ(Qtөa B(]@S~)iQ"H);f46;Z1BdYd9#e3nX|4Lo.F a?+PDHO^Bʎ-m5oeٶ8#I,i i zI WWJ) gIˇ+h('ӛ% #%NK\BOal4Դ4_KS;;]n/`~~B %ǖ0M-0,Ml_]1ǔw BS(UlN٬gP2Lo3~4H]X2J3PzBzzfgBϔΪao5*ES?;<z{|Y~&A\;,d3<*;bMˇ5 %dz`4r#i< !=e{`UO }MD4ӌ!77ߤ89CjS%}I$&x=>K(S5gU^@AO inhtlXBJW4P|w!5h1,meƗAI(~~$!g&CO+=c9۝m`*Uer3V|nɑ3 >jX2լzaңlʳPƎ(.h;Ҋl&pSlVb jy3l;32MHpx_?k e$>'ԪaBL'yh`j4}FƆ%.BX '&>zvoTiCY$HQ3Ιܾ{zـĞh Q!2 cPL|;ppq8,qBV (@z{f()eSǀ e7:UhCvvLҧ~]ӍQayce6HADc?ò$eG?a(h=^Pz2lP/nTT鈡^%C^OǷ-( iW>XĊ\,b 1N#=&;< ,JBLo30 `470cPz) =e{Q՝/DHG):_5JLC.JaU>u1XlE{XDږ7Y>,g8h}B K[BO2Lvtm`~4TFlNm-n6b(&{d8H[#CdT1_`ˇRU dzIEn4128,q>V(@zc((ecݵ R" #;-R0+;|J20NElm?(aC2:}kZ{To.@7'C,q~V%*Azc(g.lRFco9)gPz=e{F1G)^di<9Ɩ~2P0fi_F,q5–V7%HOai(ii K^0 {ʚҊŴvxߛ-JF*SF2--N?9eCU4PU443:!gPz =e{O "5,V3ۇi#[p1/ACghRҧ~]?3ƳtgD9Jҁ^],iK qPL-v %&C6;Gs;#B,qrV=A0SDjʙ,gC ^\Rh`ŽuJFu2ӫh%C[Olx{W4:\&_;i755\( F%.JX ) A^Ѿ U-`ZfDjex)'5}oi;;Z.^+hMqh\X̮3TQ#mb6B(PSwrDHE;E ;, t: +G4=hOܗlh!WWѸ~M $dzP㞵%EX ){ "5U4%AM0.R,ƛۅ*Z͔_X~Wg Z{AÅH?\FC]K\BB@ SwP(R*ϬV?fq`לOp5b(0E=~frLy7Ѡ, %dz`ҾZXj-Jl4Դʓ4#YL~~ W7Ҋ'XȞ<Ȱȋ--ÔR Shta2Lo3~32!g =e{P#гE~VXgߣg%CO3#=e{TLItM>Ė%[&JvKHkK#F,q5–V=0 SXEjZZaXڍknv*^s1ѴMִiivTҧ~QM܃VP<'@YOyG{aQ+E !=e{aU\7^l;kݝ o;ŭƭ}\L~  'BF4Gϩ@NP@ <5a~ha_Fn A+43Ddv-l!*AB=8%MñR QP2xX* ]Y?wL%o E7N\.]v <*xsJc`sZNk^YyYeVZtx:pD@içڊqQB \1(e8 K,dMsƐS;)[GkLs7?qf Sd+Avs!%פ>VK鵽_WVURSpiې\ 5*_-03QgҒE,i2IV.i%?#H$<' Pm7ZA%iF_c'aC|`He]=~k`BMo8Wᦠ^cedӑζE28;Tc)u(>4UF52S\A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$XMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKj5Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgn8@\C@DԅT`HMSl؅oc12dN]|+#<IM(Oo?2:͗KN%Y|q姛K.e^\$kz2ʮȿ/*/J-{_փ&5ܽYB_.כ9,cgQkntӼ{1 m#HKZu^Mgz F; ӣXy]/cޜ}!NCхN狲 6a72rׯ<]oEf2];$M`ssAF^i(sH%6E{X~x:OӲH߾?'R|j y'*W>?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tqzR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKj5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKj5Dzʗ Matrix.fzPKj5Dlԋ Matrix.inoPKj5Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPKj5D,搮#]svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKj5Do#QVsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKj5D'0 Msvg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKj5DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPKfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Nachtlicht.fzz000066400000000000000000000441531272363601300327270ustar00rootroot00000000000000PK8DK BE7P Nachtlicht.fz}r#ɑ5wrlẁdGOȺǨtXUjA<@7ڟ LyJ H/swolrYo|7]}_/__{Wgۻ L_dfLdJ&#Cpw}칿.w볇}~rw?xd<<'o__|s7FW>L.7_w3\̿>WRyޠZzx~zז3eeх c~qxhΪ/.Mweczgg͜y7 οyxn8|?u2=>scM5ÑCr%33WO|:qdi_oSd5q<s_~w~z/Wwֻ~\uy{Nz?N'wgJ?~;ߜhlt3'=>>NV=O~xpީ=g>~7x?+8JDr3v7U9q5Tj._#{~eX .RvE.+̙?}8^Zbv:֠mqZ8oQK,-]z|;?~}) L {3Bi\&%#קM\>W\Ng/iK|8<5g]ϭ7C Wܨ-ZUtBqQ.[SlxOǑ/n ooI#p}4oF#}ΏOG??jň|ǭ)SM}*|Xt dn'S0A#QJ8^%WNcq Ȅ}aiGcTӎƦާ {C02av0F>al}7 c^c'')Qǭ /Wa+Onf!TO;X'v0:?a*~!TP5l$CqU|C1Jvtk*_|4xxv0|ϰ:w񚒬jXa61Q^7i}PTCG:f$ԟ?qtx=աT?d:7˘;sFTPqֻ&OuЉ7` ]?OF,5z mͤS{$Bo1A W* mWXVVZ=,ō;F$+z emSʸYTgc|0ӖTG :lð01l _:y+9Z^13$dZ&M$Ē.Hh 6!=hyƹ/ ?v2\eABG!c?g{n3-2vM1<><޻gw7}6}+zO{?/euSnDoNmZ$?}Sk@x"aw2\Kc`ͮo{[`9|"s[9ϔakVy3gaEiL x68p??*$[Z<`2Fc:{f/8~JW!V5ު 𕱘k 78# OkL;ZbRۄ(Iwa RfJY!wѱ=)Ik2?L˝ n3槶\Ԣ J<kzۋ.Ȱ~^|h@.L/ƬMN1צ"2ѝŹxb?1ɔ,X*cxQc0vcB3]0XD]B&B%suՎXvZm8ccq)ڈE5#ʽWqxfİ Si(VGlVHƆBV5EwV!ʬNU!QLP!qོ?D !2ZBh\:!UHB;xkJ$ ,rFn7bgT,1^XI_5V-t $nbP&z8鎦}T-ME22BHsMYjHP̚=2TA=8CH1h厷e*fVɄ5.x2q kIcŤjOSv\9,&+`sYM?YI?M٧ARS$n`x5 *n; smb%A%HPP*'ڄ̬jٕC͵ Ҳ0zՉ;znCB< C]f;3NFd#vrw '$s%|L<5˪fO+n$Մ%#$ՂjHd5θ L3Y0Ғ}QעH*%$5Kng4Dʰt$HS#!넨4)mvb҈,-Enz&fdUO7FOi`iJWڙ}1 ma;%rgܨF6$ ʌ5u.ݷ5< RqjOUfmE ZĽ6ddv$f(M겪QHz,x:um#c:_dej<ㆇz #m>-xb kTM,V\ ɬ"Nu6ӧZy7:2*]T:#3NmXK^"QUD®p .9"g:&rrgG o0|ITbX"r]d^|X";D2ȵ*ȉSiLf%?8W ˽AkEs9:(rFY&B}iga56$vpHIt%4ytMZsRbp k5r\at$"|$TP)uߙ鮓! .ymA߱mm 6'S뷞O;zr[z"E?-w,HS.EHϒ_ ;V7d88^hr#h~{X}Mٜ~=򕌓L(M7G#*|"Ɓ&`yRp/~I\՚H4h}Z|۱t,7I3!V <^htp#Q΄G%Ŏ3NܙftC d>,2"N;tF$SKN/G<;^<7mSqҖ7 *nٌF.HxH$}"Gbm`Gʳ}5{|v{p #y s!WPqno]`|o(nq0ʽ]8. :dFʘ͖AםW=w Ozƪ>YՓX)4Vm9l4$V%M(էBŐ~<&8yoigN? JO#ΰtQhp:L"OtkZvz'Oϴ/h_șT7X7A<4*q1I{=kė=.=΍ri%DkZ3EZK59]sP>yz\ϔh_'zXvL!Ʉt۸A4>6+Db%pjͧOg,e~O*PmKx򺹢̹b(W$#cRH9-"Hbluш1mnj-TAӊY\V@r@Y'E5r#}8ܢs)3݂LqZ”׺Hn"vsd%>\#rݹG!Ymɑ 0QkQНy8;xfu1"xLd,ݢQā>9flt9v( qH{Z fcn&ɨbRiE$)Gޚ$bMw'mVtyʢiph!*%چ2@&궗ʌIԄԢX%LW1m!ŭml pUtf l<71o,&m+,qVhDM-t4CF4'Rd8.juƏ߹/wWurx}1]߱}ͭpCB3LDP̵̮LkCa~Pxd=_o3'wa nu؆|W_u/5ŨaeW?o R2٤O*Ӑ5J\~5hdNkmX-VfrLy_E 7HeNkm.FYk4-4ya(k2F٪Ze*̼80aIF"Vnf ŹJQTfI2 1ûm  F'wXL[Ή3?CTsJ{d6:(LSJS+@#1 V$wֻu=l.'[6vX󲵗ioP⎝͎F-⎍VE۱K"-gu`w~=V==T`R5:2:Vr2 r|rܷQˢM/xB Xqvz ^񈵩A#֦mn5FM *6h93U.ht\.O#ٔqweUWBp]gb]b񯻖xemQRշ8B<]'bSQqqbnO ҌE"&BdIa[΄}u3 "`$FlUDY[ɱq6>$vpLI̭M~<-oxJ#{xwh>Իގ>~ܾ98Ɵc܏Rn蛶Zotd!B.]5Ɏ;L$q IvZd(Mk[=˩DY`^"o^UmV' mDVWp"~mynԐqܾ6TrBSVy1zFq hpj2KliMs{Fm,6~ixK oټִh^e)2=ň1Zm_V :hV>h6,ߩ<S5æf!jYu; A&P)d~JBfQFZCf毶 K/Z@Bf-!nD3kGRlY4{`k'fҤ1pY]۩2ᄁM|8Dl 2RMx2X$f(Y5Ǎ%+Pe]f*33Jw*a:Z;e7 $USO`bqӘ3lcJA&,.#6uTH"l-I"lPRlYT-,D}%Q U5m~Llә|HFҴ"DZlwFH"j-H"j-BI"ju}5J&j-rq%YHõi)C'Gw<` A GcckQ$b?6[iY5GTo̬"T,-ov|;s NH1ڸeOv'[R3q4juƨiTUn*:N4/N=[;?}_Y>L.7ŅTe-w#gf^AFZ}Xrv3|tﳧ_~3?|y~Çϙ3i{N&~.æ?9s#=J8~k{e׽e8q66=w'™zM% d: _ jf?iڍt~.8^K'/gnkfƃ MM>WRRp_};.>Gg5c:͝S C0T2* CeoEr yKYPǢn+}0rBlH "20C mΟ ,*2i1Kwn,ܤ5 /zj`]Mkw~{o9?z}Cf2\8|fsȫS wǡ9}sy14wCs@LJy/׃z|_˞;}?^ȿzAA;R1^hs\  ,\Dh&v5mdf_W?:9 o5oÛxp1vx.?)~k`GdT݈ 6u"8Ov:' ڬMIi,DApwu>nN^}iμh]!̬PB]BvZG˻O̙'1FIgƝgf u_ӨϽ\ø{KeI@P2Cg.H(T~w{gx/gck %?qW܄kM#C 'xާ  JUP{_tKj"5{[7I\W:gk-q"?+7,\ۊ-;|ӯ~5|Wpo߯=lgr塥Omfā|#ox{G}24"ڏh-4 `2CL7L+~0@4Bw*a|*l "S\/hy(Y8u #УIRr4ϸ<9Ӏ"q Asзp7LE?fI=Nl:ÔS^C vFA`7 21ED링4j9 /(M]ZyA2ˌ)]q#ztEO!'կ}d]/J/%$*0 + l* LK'`8^TaU0&TYQ%ܛ||p2p+L.IL,mw7h +yP6˿=} HߜX"YT7+\}\ k޹mj 2^VK,r폍RULNHahƷP[a f Ql |8*+g @~hL^9I"xek 0Xb#%T2. M YE xEHD nD^FqcM7MTnUM;XiӎVL`wqvc0'7jM},hMu ᫦=0F} O@A0,v+# ‹KYIA`(8D>a6HfZi ֊^n,>el=ʊcMU踟 U&G0TP0ASZ1@VV he.a2x/G(G!]1MeQ$uF1?zy vc]hH9KlD`vp 0Oԫ2>D1SN, aOas /|k7]QЭ ~e5#zCQۧy;RMnOjmX a:2p P(;,h `Z]@)zC@wNg _%D8žd,'5NN8hgAGc a'R!|!{ xDA14屬&<(. F8@g FQgS`g)u6@N 3s1AH[`v ux!)b0.2ݱ:AT11 F|`8pYAUy{ ދ[!/I)9G(x  (T~E=nP,(*"@8*L(~v2D> tps 0A=@HJ%Mt*'4%P@T?8mc }P!  F J3|gg%@KVN p8%$t0ꅎF!ΣAz0$<^ՃWa^"1 BD&IBB_?x@z @ GP`QBx|Bhr*tTjn DTpoj劢Նh+GO>8 ?A;/r")3A Q(.øyRJhScw[*.h{{Ǘm 0BЧr$|SrB$<!,vҠG0Q[s1Pִmg;D:"[M舖Ok;tsGt)nX+5qtDCh ѵ,UeA#vl {vX $$3JI 5"aR "2`w  ' a_d `YG@wqDLiXp1lyW` 0f gFГUd'iE̳H=;tp\b%ΰ](` Qb铣Db$,b ͖sxx0d".U9O]%{Ǹ锹~`#EzBXb,?0.t҆B>xAIѵwA,u?eu lH&5@F )A `Ԇ?YG1RN@` O+|ah 9ű7m*@gV]g@pTڽ:8Sk%=a[S)F=acr@/ `Ģt/G1v0^2 BRn 4Xq7!&LApb5y7B) z^C #6;;!0$+P00UȄcM)QcrhL/XcpJGCnQ4h/*t !F h^وA>E{;ͧS76ч]6(Ŋ!oO|Bt$ wMSKш#{p#zrƘHWDī"RT)RBuv᥄"z[ڇSG%3CѰ0Rty#nvʠ% FC "A F*=E@4Ag8kFr'"Nn`@#8 ņ"52=G`,0S.`{ќ8/ lJw W<àLf wW;@`b:6|Zg 07Q˪T-0o`b rTs&G)k=$ @4hO2C'e<]*Iz͡Q)tPxg4bb6_tH7JL-w?"r|"FS:h QqZDs4#m `Ny4 O g!h /acvT Azš$nnYP܁(>0:8A0$sEaaEhh rFU'ɧb2pa h0pT h'jWۣoT^O-XW_'Obڗa1i:&cG6=ܤ Dtz[)?jGT_}|7':yY`*`βPД4-) 6\6(N KMdʮ84VT4-e^HmUdQ'p7 ߃ǂ s='S[J!z'2gR JU W؊E CSi2;Uӊך+M fƆZRyKyCUP[hdl)a/j U,X)TF+B /+BEm*B59K!T@f4PɄeWmK3e*ܼo|z”D#5n2`oEhGbaM`C;|rGhl54U(Jw1@Q@ɼ@SȚ>j 6za7)ݰ=rXb CG bC kt=*'b5f'_TԘ,&g0Yd途LV?QgQ9y-4L??cj nAnaʱ#'^1';2F~S 2)EO3tf2B嘁7*ĊVdvY­mӴKdUkG !$Q!qïX0"a”áK\A$4U:+nE\6P.ES2KY},b,48p[LK_+i)WӒ< '7{EŰ lPb̑ 7)975CھOqTp$„i:q&8 :ٳ_5mu>񜘭jSIns F 7 Lk~F]ά\g+\,)xӪlEٲJS"'&bPY".<,ӸN1S8a_~=NA0LW@ayZ0cYa90@LxLrsp{` ^ ̐ad0`~LaV$0성]$~+'LBAMh0]aA͊c 8<^ͮrU-G]E1ꪋFx{P|MːK7p ZqB0~з7(<}0p`18q˂K p4 ̫ì,1V2 /2ee8t < wQoxeB7_N[(Eߍg>=f8u~3f<6\.0qx9}8\mpZE2k\0zI2HpRѕ/.:׼WV h ܳP OdaR<<<Xy[uX!8\P|׿<~jVCs|/->wO~o%~[]VV>ϛ~rRߴ!ϋ{hqlaNJ{ljFrlv#zV1=jZ,z=T-H^ Mx<= ݒVAJZ MR [9EZK UVlT-ٶjxBlS5K)fG: ײ^VAi>fՒTZڜ^<Ǿ]zq;TwLe.Ob:Í\~"!!竫xwS7x WeǙ\)0mƷQ{:8*<{C,wP$q[|n[VtnlX|wfWl3~ O f=b6 >|L {%r8؛JIR?S<-rd~c@]L -`mwIH\IޚJ Xv'WMRM$IR$˗F.MlruzIk# sG93i߉XeFl(Q)Q[sxHyN$ɃCI S #',}uPKʓ8DAN^E Nachtlicht.inor0~=BB m!MC6Ig[ISަ[^k@3m` ~Z>x_82} fjD#W5jDDC3V, Bfby Ţ=ٌcvvՔw<. |p*=:m raU@mLSy^]5qBc0v+27P)d=<&7+]v[A^'Ɋ{"WHhz"f˫צếp#1Y7tT0 #AA,Wbte§(OQt0 r轼rVMsR!DזoEe9_hi*cS$WwHB&YO5Шl<%_bqD\_>G?s//[70]CF|nwk|g R=Ea Ƚu)ϡ9\VT]|V(/.3 ]1 W!oYΙ^N11]bisט3{N-fEJVYhL*5\[ǵ/v g|=*q}IaolY<({PD~aFciIΆ~:/U!+xÍ 3G ?A3)t}/[sM<Ŕ"kDfv6O8OI} %jLyԗd^wlbZN`3R$.^~XbS.o_+/ח_n_6?(?~??㇏?}U˅ˋW^o_ǯ9b07.~\wp?wj/O^^/}Ӯ݊kKs^ҕxյ}\K/>v^ǧw_=^z/ǫ>?{v}W}}uW#o4ڈ߯[ʭ˫֟D~_}\<^?Y]Wwo7O|] À?x^B_~**_^}|9䪧7?o|v?߼{gQ|ߙ?}ÿVO/m~*r}rHs~Woz} o6o`Xo`?_!])[joeѻT]|_|#| ꟯?V?~}yUhPks=7?ićů2g]`NջKm ֵ\_|x{z5{Ow41~);dW{vyvaPgX#jîh'l9VAo'q"'ߝ1;Tp1\p 3d9T tDSp;Jtxd<*ǸI?:j'1.v' :as?;WD \m .K Rks[ВB}3P*nW/-r/ϼ_Bc{ 4=Ξvž :{[{ @ @37Ξ6)(lxϻ=b+ @#mxO{@i{O`==T8]76 :{I{@@7Ξ6 z~Iq 9@Ѓ{޵gN kpϻ,={@ioYS`ϻ w9>bf(3qxO<佧="K3rxO{:g߻כWo7mg_{πno>hH_9_vž{KY{386..nϻ !>. oO{ i|Oip=0>s9]"6 2{Y{ K86 z{~y i9lB|e@g[)mϻt>{io{S>dϻ.w ]>"f+3/#sO<$!>R ̋ @3/'tO{r@>[RU7`/~?l˫׷ǛUqWwĝ+e[}KwZiVm8ۏ_拫^^?<߾eu~ r^_Ŀ*u^s$ڲWX+ƫw؞'nfD>ܿ@y01A6'm46->M7v3 ߳ ɕ>-ķH)An&>7'.Fr&r;AA>MHRnڃMmO|Fl1Iwy.uog2v{ zϐp[c8áG n?6WߜXC1vwϏc){MٓKI\g8jyV%sS l|O9'y:%=\J{晔(!'QΠ8g? l|;yy$=I{YS&!-Zؾ3ef0np_O{%di;|~fKPh2 I`}=?r ;9bώ\=X4w^$3GKDV k,cFdߵ'fDB+^Hc|ّ#agHΑ9g% n;Oy$1>\Ic|ْ%aZ4f8ntoO{&Adޙ0|~g%N(=?Czp0|voAcʼ(=7rC -mO <ӧUq2;#$g__6CEQ*<\0J0g_aX i YK? |p~y{~x/pǏOC7{w|}s׾oC v _W⪛g> k]~\K6p=\;J~yq_^mw@GQeRҼbpS7j;)JgLӤk NWӕW(2ǤZSKeB@;n)8yҁݎDĚ}K&,`q쮜)!P~B-/;=nW"?b> <Gs}Ԙ>Z (;Guf`F}?oi 2/tB(_-$Vi֜03yL7\(!%(dGf{0XՉف.Nt^s]vQS`[qqQ2zwwgW(#*W-p2pssW=Hahy5$:|އ6ڵQVN)=.Kd1\!ElܜtL&@PL!!b*w22-TTن GLەDr#]&=ƍz=wxw\nE1u]x9<&oE1zMWLyN6yw}""SrRDɮfLJdRjS1? (R3 i+}JRiNq2M V+hqq֜3y L7B2G2]͞buԥE(c:~0>PFk "*-E2N7mW :Q QzOV.eyu-F{[sf;9$:|># mkJUڝ}+zR(4RɮfHdF1? (k)5n)U2F]pB%H%Q)* H)u餂+ \F_E!o͙f D7Vuf0PL.id|&)±PC C#_''nVwkVܾ7+yn=4o꿞~O~rkCػ!?_Q}k5waJZc7+aSz}Bܿz\p?GC^~iԳ(|0?N.67b<wɇ$P )N602w x .\\,IS,x<(IԡI &6'՘p2N,vO:"I>9> A)R,+ NJM"V dduIibO; "M/ &{v ft7x'Oc-ӹ.92E;7*\?=<\<2;DΊΫ﫟 V]\o_rB/M$wA]!qջ돯矅!al7Xn %3[*C'V}S ۿHg狿wӽjo^ݞѳ1TWZ}V7 1o' @q1mq' 8bhHR @8>-Oy;bvWq1AxMiAӺ I]`$\)Dʔ ALJdO H3R#3q:1u+ԙ}΢MĜ㨙wMj d4 M1K /FȿY0S'];[*@ᵯXYPI@Zkݟ%lj%#Ik E7H!}*I6Rf2`iL{LGV#< ?lܩ) 'S=SSW&9TiԘSM,l b&U8e/lTSM+`GSGĜD hh9h4MM<@̛cIMz"1NL)l b.Y$ӂČ4Lb4Mx8A+K3u:}ݙH),΅|XhwwQxhDOooO_{{tVt+Bȸ!73VZ] |J&Q{xR~6 ;O`@D$(D%;C⊭.üd8bZkEW:;En`dZ%n[IfwݍTƙ4K8K![fHߺ|Mֳ,B #2e}J(/@\;W\8Wu% p%D\YɖQJ9vN$-n|x\Bu\JfJ ;L p (ƕ$ힽy`!ݭny6:Vp-%R7Zv6P.[3>;N=dfc++ P) Ll41bp%Kr߯]BA[^܊ TXHYVuX!5T^ܘ@!*[5;W6Y**RIio%RG4&1Kݯy\YCŭ+n~XNR}1<ZCsFM[xK<=<\m Y'Sk],7q<<Ay5TXScN eW*OX gOiwgG?>'I,a*oww7$N*lBwwOo~n_/_V/oS>E.Xt6i|{=<}敿t2k(8%JEޘţſC"S)5UV.|7k3E+?:.?=_F7|y[ezCCБpJ`0m=~?Θ&3%´BvM1>2䓬x]D?'/ gӿrL:gR7 Sܷ:.#5'^8: g2##9D!>Du ";|A| wDv4Q o.p{G`<0?߇_p~<0 盌rۮ1FcQ'apr00wm>2grwro7 r!g=o~!'9D>z^z,6:u^f[c\Nl]bl}s9֧$J+r>5קfH-MtfW~L )"u5_xHW'gr7|M<|ac 1?h~dkɝ](K-=&0wm>f#{(9^xΗq44 c,BAXݞa |-6/o,/̎|c4kKU'|1?_|bVg3)7Ru$}:hl, y̤ ZA+KDZ$.23:&.=C7tA7̅M给l܌UP3G(yZ[IA\W2W-Hjyqus#T$wNޛ7ahV8q@5ٛgVKfDb_廮v˫MǠ6J,)Xq*.XZv[t5ޤ!ڕUM.D2L9 NG&l3ݙNEĚ}B$82kMd4'Lçl,2j9h\ml註yPzZE=/EqHe{*͞r Q iJ\x2.MIDI(S"%rRK%:^WzG8*8ݝoAqTf5#@BER3rT8EG΅'n Q:u.@LCojqTD+Iɨ)~kҐ[lYwDh7_^5&*US\fp\fJo_+114DLJNi* 9 y4b_jE,NF/fHVAc,@I24G (KI6IlIՙ`2"TIr I4D S*307!jZ:h}4u.d̴/iD<&꺨aiLJ&(bd$S#)H^tR=NU]/JDP4xi]+y饊™FUD++ xy^dTTP.Z鈟jyјKrG0:PILdNj3!u4DPGL:d<[GɭAu2T5)NxyBH xh&Vhm1 QJEo:l^D1LUI+"?+ӔHBRi! ҔG1*'xfQ,bSHA9]4C)A=]ֲzmy(ލ]|<,iXj6{.Z(P`:PU=erSzr +Ϩ2TFwʔ8*rr%I2?%ݻq!J M:jуȦR1&:IJ(+yXKGeHW<-jk{ؙ<&o7壣v )9c[zh NMs4L-%:*R3s=LYʜAGELe :Q 먹6T+wT榸r qryrT"RD #PR{[sC!0_,R(ZV2-l--nCEkApU:*d:~(KQ7::j歨BjdO2AX6waX9em#!^laE7C_9&Rju7Tm",-sH4=5sVEeqi 񁺶ֵ@t-9$4t he Գ5fojyE߇APZp=3L&V(a#-9Y(e CRmXi-Y:~NLtLIE':Rt0X]z>gl|[3>G0ѱ5r3ʐ7̊$52"Qܷl,rPZ3@+Lv~MdN\IKJ&RY9!Ώi r&FMlBHF*`5QcsZGMEHeASX\7Kq^2gtR#[IZsR깸܎NU8"TWD 4SdʮVCO%RKHSdPzi:5z)Lq5qq06.zh%DHJF:7`rz=Uꨟ Vi5hok" TA}Pfy5TD+W0/1ʪR)\e b DاYe^x?O>ȣc!rFmw@g4!> ^R8,pcn40};4(J% 98zuYULSuw'L6:,p*4 9Yh ?[ Ob|1fUa']1g 4 Qw6FV[i{~h~XH) ndپVmJU#}n¨buɪNk- xm[hp[@3`Ar9,tsW\ 8iVZ9-ɨ9:J(  YЗpQd62@ϭaZ j2RBMQ A \苶fF?tA)"З 5]Uv5̉n AKkFΕҜY-u=* j%<:1BiKe5L3 Ϋ/Ӓ.(-v^׊ϝ ;rxwo/Ë.K8֋{NlcptN,+S=PP oeXmݚy6\,M*eվAyT!-Ar-ZsNr߶7cfTr13Gt8 ;Ve8+AKLLTn<8W׻hԜ⌄g ^+K4۽{&w*P{;/PSWC{I\KIE+79AK\kf|u\T^ 'Ƙ}ěV}^Z\O2wr_݋8a2#HlJ\AKɔe7GAKvk \kKru'@jkP3ź +5=;| P'Fp4s,a.)]±:ub%(v$IVR79/uyR*R!a#LF$Ns)@BZ:ӚB_w@d>-X$ڗ )^'hp @n0`dfsJRL 03-8wm$#,s2:#m@Cـ ; kNek6!AqK@p$l]T^g+HN\=E|%Rb*eKŶr[D݋m8pEX2Qp-%Mog'(p X+o֌n `q:UNtZhS8̞l24.88u$^I3Are.%S-s:e.KPڭ'(sݮ@Lf5(9ۗm K# qNDKDv}M:y4f&@Z.-ob2Tݚ0&@t6UpD*bE4)ɬ!Dw$@9r}HsGƱu *P %bf%@ J]5 pR|sZݾg\G߮}Ų+:f,,q0V6dtYNadɕ.t}-BIB`Ne7npiY,RZ&*pʉr"hlcĸ֌I6.(o; .Dʢ ..Ipͭݚi",ruE9Npr(&BKsvA9T}e(\U jHK5|:((o)alycf%m n Aog6IPL+wr6X 9Է\IԷ1A{E,PKXAKeK7EAKtvk ܖA\Ndv9l^ ɰ*=L9)Af@j DN9dz BI&˶44p 1Ҷ%m*efiwK4(4(=J,>2fP" 6R'Li`XM:B $OPd&LIP -!FPٶmn*p:^Q"q-# P>Nc|t1?Fc`0Kkb8i1O&OdƦLI -!FƇɌ|tf$#{մҞqw*>S:FХvN|[qFLUaiSYWf,ZvhNv&HT(I8(Z7z)Yj$նa2զ܂;RA={*lL-2Gk5,#`тjXE5gzꖂ!n 9jmm2ጳԶH߲߲! +q[SfL:C|[PdgOPR0.!GPnH62*<D@:@dx`d'AngPpX Z\",\I \ %D ܖ RDd8a:b]8A{φ{q@`)$qDݻg 3` .Imp:-CPrmKg2+B &$ĉ !A`̈\;E0PhZٟLiqG \",X :\ %$ :\<>.߽xOno]4? *mHwMЁB,&%kZ՚ K)Nn*C:*lxlg'$j ؁hj[6̮܇Rtb &Z*.( Opv\ h{ڦՓ.I-]B2bNFWM(H䰈flqSjɧ&(o H'^i<έ zK ;qʔ!9{fJBas=4s<1~ҵޟR|Gd ɔM.%dZ[ uIlʜHk& dSn&q \@K+?v/AԤν tMI+I~n}knkn)57o"%nMR*b[řLڻԡ-g Zh+G4۽s1OFsHbn o)ily3&'o Pn֌n qO0զ`e|?(w3qNCI}v/Cʣ4J`AK eۜ8KAKvk V ).IoiA{3JRb} \֍ju"F\2J鄦C%v~GtdI \Y \ε[%3Z (άr(H<%2$z:7(s5=;ƽr8~b.g ί6,^3y*(u pRnH5vטAΈψ3b vO25<:47۽3.tZKXAKe1 t$q֌O`lf3&iJ;6ԇ>*8*(o; VLDe{[t% LHkO6.b ')źP\+E4۽o+vru(0\4r0y+n)Yls٦&ho PKn֌Tnלp^r6z ]Bu* Rk{'s)qH`Xo \J,dY \ϵ[3>P]3e^J;TVXdfg'sZa#x0*/=tAKJe%\5 P*771rWТP(Nb¼b?K z\vϥTE9xVowH1; dT&TT*rUT+ $Op;\",t*[~f AYuKge5qU*M7&FG7 5hkc>1zJtE%'R-@h&4%-@PB%m#it(k 55Y (Wfsi q.2ĕ%J3b(kd5IZ3n_sT.ٲ7nu*B.`ɲ\s$k֌ODp|N5NN u7fӘ x[_kƂcnU)nE"Ly* 2VLPdfEPW=mӆټ2{ZW*RUP- ,a.).c$fglݧR\T& )B'+~OjBY-hƌf]:+)K͒嘝%.[3>J0Yrmpz\g6reP@H-6ENXrD*܅lAAU#V]ђP8yc"Gs%mj  XRBeo<%.IPڭ&lrl= Ɠ \2'g5#CHkcA*9*0xM`'d&H'Rmk f3 e= -#J6` qo= 8jnUyNj5*ML(ꖒ&˖7ov%n ˡGlyvqewe0V"9p;(lj}מl,m9r81S3 i ;ٶ,bi<&E\;҄=Vg20/9.fXetM!J.{٬]-oW|(̖&]f&o @l$ .m "p"H3\Of Xݾ_q5#Ÿ2eup,ޜ-(Rcl.$=-Dp-!Hpiv BfִroKaL\ӐIFgTCS'e9ך8٘+Nk{]lOD S%,J%.J2MPڭ'hq[]8֧1.e1*GnBw2XlP>N!2PwR<حdJ(D2Ԣ[ߵtKDe[2AaK@\qKHtj:{ Zj*h jmXkz䙧XH4{Z\yɂKo)lf)hr XKo֌Թ`tÔ4uή[T-Etū9qWQm']ƚ^͉yh]C2EwAPỶlst(Akf|m a6.W΅ޓM*څveEq]-*p0PB Tpam w%C"66~&@!hiݐZZ]I)7$a+ 864۽Q+QԺVnmMXh3Xc9*L\bKIe 0-AcK\\kf|FS>IW*qk/ "ϰLL0w#hpPJE[3R+ZfwVZeԖi){݆L=%x[J,zZ J\ĵ[3>QP$nne8sL0u ݝ@ؐ&cs]{;r&87(<-6-j& -A012!EPζL"g}\]zwO0g0^\7m:i5DeYDXMXUONng5?hu} ; sKrRVT7†p3fq~@ i"u@sx7]dnHZrn]$eFv)G,J EQ\`i50NTE͆+WF"T8b4D`*Q"x,;.-I0>#K%(nݮ@rîX7i˔ t}d"°36ڢ SmXmYN H`2QJ~,[dJ[`RQ5ŭ5[>9G(gn/&%RQ@̽l6UhmSD⨼ޚTF AwK eۑ pN%n֌TnꖹZvnX‰!7Q$Z@FTM-*meJ`y92̰X%,4;ѱlM٦i~L͂3=~V]Վ䈮뙠S'wujwo/CIظ~AĭX0 (ht0L;'^dE3Ig e':Vcl5hY׬KM5_ s*FPb%3u,-(6R~A fjzxV5Kɕ-@pf`l@AI2c&'o Xɏ֌n `q6(#л=DYCq )s4=;s|l7ft40N.tIɒxŢA)(t @BnM$ X 22˜Bi2 2Ǐm%H̡%9.7Ckp]t.gp9M3Α7p=+u!w#302Զ0Yt^eƦ:[[3>H0r 2]$.R"T/݊P '{? LNN#@82^Pv~LYlST(Akf|ukrrfe|Ϳ leq^W^2ũ(s֌@J˔}BN"zGR*`2OtKɏED [[3>PPݶm3t-*OB,̅պ2E47X,G4={s+hfjng-QSmL>x"xsk2@2\J,{N曣%` \5e5\tf-qlINF/ʪ*eh@fw=Τ<")F`9JV.@BM.%P[De%Z\5ka p*UD#V8U))Qr ,F;Pࢭ)eE$pġ$ [J,{N盝%` \5]3#YO;Y*.wB5.ښ@p>w]99^%X.+r)l3(r X.nNP]3*.$LjrUKyˈ.΁j@Iɔ@}e\< /UifB=:(hq)ilf'q Xn(;Y.CU9@vX^&AG )BɤM坑%@ [5ŭ57Y-> [m,Fp}+A4}hsWE--eJ!XV "qkX4hs19:<ΥʲuNg\ϵ[3>P纻79łR:U H鿑b-KxfךH)zR)bs}ny`AK eH0Akf|u9D'' .#Q̰GG<'5c1wt8ƺqGf zί`ޖ$6j nAWVlfWCYQ<[_k£@Y5ԗC[{l ZJ,\jt% Z5SumK׆G2ZG "-s|="`H47hX5#2ƣLPDkc;-@ʎ :٪ 83h0¶} "!S=> zr5q.֖#hm֌<n#lԝt4U"N*kiғ3NrVVfz ΘcHer4={_+zSE(R3UvKYv~)VL,Akf|m %,s.Qᴔ} k^g6"q|q1N(v2&0:*9q>&s֌ϙA P:ʂ&RlIJdMl˺Ax(QR~V>X5t9%K(ILu/E t0Ye% \55,p;@,a[tHe\@fHF:?/hJx)jӡ \J,beMRmݚy65 63\]^nLkb*bN.e'Uӎ2mdRX0#r,YPv~Ը8> $38 (p (n$f f2[؋D$񙗂eh 7i7h kH) 6w)UɎ 7z PX*Sc*m[[3>Opɭ5hͬmP◡E 'Z[5cqR tDGT R]{JPRbdJݚqerlTD(()*KRttű%@R^j{H>0f#J^%@Ǣ-|-Kڭ'hnݮ98MGH)#$pyt m,R^j{vVxl{;\qB4"`n BV(`LXy \[3>Kpz:>VM!N;/̵x9inY?8-J%})bJV]d@(D)}~$Z[ߵ-%D3JPխݚ񁂋o[]7l;\#rBsic0e֌Hro1cn$ {8Z[ߵA-%@mm"\}-Fڭ%k]Df_'Mo.+"[HIdp,ڢtǼmme٣u?%b4Zћ*6!@ęP KPRrd&3NLPݚ NU8ݙoqib\)QgC ^iyN507Iֶ*ws% [J,TI [[3>Nݺn,ߜPm> m{v/t6%hl;c[J~,t|%Z5e;5Yb&qSxmX) q6(n;C [J,[b5RQ .Lڭ(hp[7đܜW6yRܕԎPg_MD8wN-8vn}kWp0PMּ 2GPR2dfMIPխݚqfVr?p+e)j@ +,\0劲OhoPYޢ+3&K{8h"11 !hn)ls;HPݚ59er(qf2J4={y3 gSmqGoEq^$ί*&K=[眙%J\5O^TnF3[EIuQb%3f9¨ %]Xr*|wo/Ìny f3f(`lN/Q`eJ-Gd6rNTPuݚa:ѹAf,Ε#oo1ҮUq4Fb֌\|;OIxiš]{WPR2%Go19o%q֌̭ܒANf<̕AVK9RG@<۬dZ8xtݢE؜kU3mNŗ1kT]{XPRBe:'2R}ݚ>99mŗR8^3\@Nb -t:Ø]sP!l*]ߵ.%Yf)QP]ݚIϯ/^V/Ooas㛝s|q==\?^ݽܿz!E%~/Oo/V_{Å.~oUIrh^nb֚xuW|zpXD_Ϊ^`a46%k#|B nzJGuń]&){{7~X?IohtK<'M҉I@5hbP`O8 f{4&=.%^)MXȕ!֨q3R?'307ӟ~ũ̑qnDU']k3RYG-քu%GsT A\eyKGnusFJJ-B2՟WzYY*2«&Ls ' %uVf%09!nڷ/oTiZ|{z|_ힶ޿>࿬>_msu ޯ^|Ǿ$Ŵ:>y. Ύ{G)ް5C֊ bI1Z2)H bg'̳,Ėx,ڢ\֤p oA9 WӓkڞVQ)'1-dR2 t-*u `9a„RS!#LpamI+C 2sZu]/ qʼnޑw[NܤtZkp/,rYIpZ\hR/:J =J iǖQ'My4B-^Zˎb2k *zLx5*ۍl䶪xux4LvzZrƈ@2͉ҌaX &gtI:"`Cfrf7wGe_ O:gË XpBɯ*L}ێ#ɑ4/eZ`гP =(egM&jtR }q#EV&La#`1;fkByX&|~# 'b>4|!|~t:VZ`$y2s-Ɗ8zs#6_yoO58apinNuDd|$Ait2/Eu~H \4xTI'ɐ0[dg$<> yFLJ,"Y>I0qiSiV1nӿQK$g1ΦYQ!7&tuh3Ш|hNOB~>4Lh̀ ]>Ѹ2x.ų1`|;&&0ҡ|3?hol:$ME2 ߛ<,)o@v ri.9эJS FDEǞ#}}ިN>扅o7{P2pwAY~6ie6|wI!tWRbnq5&y% ~xki!4LN.ûtD$#9kwGKE< G9c^,Kؔ(Y.M|<M24%])OHIk:9]M.ALqйSE~m9 KG0&6%&S@ E+LV5vGh/Y ~=")f+ ^@DMkJM L A rXjy^7\cm2H\BYբF@c#i""FZJ\!SN,nGx6v qӰ;l]3SFkdӦɩH,ԝI 6D%wA2̊kC_ʔ7xIU`$D!K2A)LgPGP3˼/s0ѩ]k ^K5LnU`\{Ӧ2b,&Gd5=}oee Ћ 4$ 7^^Σ J#sA#$J\,4I!e"/sP(rPwiD!7 —"(0[2H` I)&K6GaedgJ]{B!rhxچ}RqkoZoLlR"zQam"jcOtzW}"vnuߩF*YiT%ɄW\ՏPApVuP2[IFvRxiyy|uLn*Xω5)E+h<=;Hm_d/ ]dJ}EG_$3EBn:̀{gv>{~J4.puPJ*KۋYj( g똹0\>vPtR5`7kblkOHۗ5H7@]o7P-F[NcԹFuC#BPꄪdOmOPu+#," -@fɼ j/sLPAẑjlZУrn}#HY8SO\8D uKUyl|c5J02*Uމ;rq4ιehɕwY  ۾ ?ĪI@?u2 K(KÞ #% cY+Xޔh}Ksԭ.Nc!gLI+lar+:&:zd!}nStd, b8Qv&”DNީҏk1dzMw?J( ddƕ(+ 2^# "%\+햳ʓKaڻ=|շdIB5k˧u+b,DB[e.`ˬ\&. a&"˿s}[$\M{iE6b٢b;ڢim1 g$IEpKnӷ[*olv[J_{M/eمK6u{ 1t%Hi(F셽ye6>9Rhp-017ȸ0]^KQ}㩑S?>|i4o|C 9.bvQ8j据E͓t<쥠ꡳꈂ[?: IJ Y&K䋇]v)j%1k_kbJK.iR;܈qʣp<] k`o>5lusWiǿۇ7?kfswc{nX [|~?L\i99O|Ϗ}q]ÖdXn#ؼL#볺E .ۢxK4輷mctMuְ+9p1.he#Hs/mj%Sd(5˝~rW9Mwz9lq _OV[.~m_]1߂.ZtQ|#ڼj/ysx߾?/I>Rǟ~?h}wϷg5/v;c(y׬.8<]ϡ} 7 ~&c~/FGw%9krZ&HߐH)!cAf_lQŖ{bs;_v[liU2%od}Gw^Gwx*cNʈ>L#tO#{c;v\fL9IS^[_fٱQ^j% mes{d՚sOlJvm-SbgɤZUO􁁥8EPnJ2KMq X0WgDƨYUC*zMqQ՚[Hu{aHR-=kBE!<:.'pVzi6ܔ3=v0fS&F'm}2"TSn3B3n8'w([7U@pݨu(xg%l9fn{Mfr&'YuarLÜ48U8фϴy!ߜIڹ:\E&ףZc)c-1{)S{QԚ7ryQ+^E?[3w'YC*:BC9{r.ëXYtAh9wC+ +#%X8eLY-uiJSg^of}_"ϷɌݰoߨ=)vZCd0Zq/oWb#KS|ͺIA_*A?v{/[vr}~bV5WcNIc%m]{kVܦv {+;&vk>UJ t:\NX4Xèe}kDr_^IKDAz^W^k^w^>Q\J_?+W.p6ZDmjUׂN֥hY) SRphJI?cMTYk)b27k7\v5t,/͢NrߊɎiP{ڊwD*W7.j^Ϯ=:m#1٤dN N^}K[KMfM!O-ZPt5t3L433/Z>bm}CG͓oⰫ'76OmlVO/!s1l|6 >=#9'ݭYFFyfwgθՅ+֧pt,q {-֬W'}V=nq˺WO#iӉ^e,O+V'{ qwrxmDtd _DX:+ޝhE6OLYm9Ŋ%ay'=g2t={g|1}ݷo>?l>Mn~/#ȝo/Û!7O}珟owVZ}?aõ#Iޥxw5;bJJ߅؜R|HIt|/tR;ZOS#O?;|I?}/rȮ OntkȪ\ISj,/)▄)wR |P7N!Yt7zm6>⠹KyQ!E m ڹV,cVGnXu eՊiQrrLQ/M BƑRr6H)ۅ\TqQuI|V%Q\zD(T89\*E|&nftsIAʷJV ]ipr]>! p60gC]bSx h- vEyDܯ-jYU*wSjoNMKp͸uk+ .ʝ7Vn=Tu9Tb8Ya}qJd*),,$N#uu4_D~%xlUs"urhejxnalq509뤜䉕.jh&iP))\ O*kMj-oZ$ìMcUwR7-=sZ,CU- G3}S 2^ ;aFZ,rb祮yb?72h 1\F*'Dj%o&x$MR]vѪuM7#T'x݊ѓeQX49 [xKX/M~q$ͺow JjI- %33pҪ-v,s̤|!GH@Z]f4S:Em ӥ_.}HQ9҆:vJa񺥽 憐&ZP ;^wAMnÁ^OZVHjYP4gqὼKpOcAIB\%g[}ubuI|tWIRRkhshjVlzuhG1\ɅYή`)+Y\2m:4~lKWI@RY0RYEmTw{|$0cz+D`rnq.r*+f=Vx 4{eXuB\bڡdl4 9ᣒ. 7^7+ g2zE}ܧH]D9#TJv( P+RXjdYfnYV~}q~tqt5,%M$X7'n2NJ,gbGhӃG G3=Tv( 50QSd\^U.m *J&GJDMDt6))-E]츼RJ؆F*a5ؘ͈Gj]Vk\ =QLm vs6gnxi;?T695R*u5$ qU²$LKfKeon*/;iHU[TJG %(\c mK7h]q4u{hn4T89Ԋ,0k -ȋrV M09*u[VM -w{pC˃ߏ%] Ty[ռud2w q_0T69jޤwƝN`$vO}xj5oXVEsJt7K$߿XY儇\ItQ7:;߀\  IH͉!5 %w [tYڪAH*]SKlIgҝ.}AHUgIJȝ Fۭ4'ӕ8j[X U*S*O^KaH8 ;ɻs+MQᆣK̖,\eesM4;؋Dl+E[)۱\@RR6?+T69T6'Zּb;v;4W"ENHiL% v?M Ӵ\5 NXl.^w*X^GwsYo~/~sRRRۜpZɛ2CH/ prh=v.ݩ`H^ALu9Z܌BB{7Jrѣr9u5kUGbL M×9FwRNTf+;ى.qb&RRٜZ+e9::[s G3=ZYEv6 7}q)b#ĞJt[F5 N!ڪt X>A*WBj oF*%8UOnAYۨD~uIVkaw!h"F_8] V=gVI|~~/N㌋NeWJ;dǝTRRL*!O.J0`RSsu4R>}))ev_d?hWlK:YrL θ0 K3KL.v\U*ŜS*ŜOhĤ<2IY9]MPw"r RN*;2rZݕ[^eDЍ962疅JfJ冣W|{mn)lnrdnVTUld.HTDnY{, $@]j#(uoN 8T6vPs"J-$M#+LR oD\m^5Ei8 P)1RkK@R su%nwRf>'wBD^痃fZv%2ҷ^0Y^%}țp᫻p^5qFN,H^K;bYU.s}e*ҹ_肝>C~ԭ$rjt*߭mDT. *΀*P9Mm#:!KII-S $/hA>7$oPK8D nb$ PingPong.inoZr۸+b:8$LV'gR^@&(D*(7*;t7(fqeGHt/fءTcvTy,U 4,&Msw_ # {-"fhi?ZphNl6=;=:uw7'>myҌD$)&W:HJؗ=,B g'lȮ߇w7/2gcLO4{ sl5Spw~pyE:x[{A/l'l%Y[2D&li_N+23LbaXu,s-(N_c#˭ڃ yz&4U$"(W)3ŭJy o3 Ox.=/[ v|4"oŲܪHhxܷ ]a:LDOP-24{3`y$o, -ZJ` ,Ќh dq=B mKg< 'Q(: XFMxMw);IDHN0#l:t!1!Ў/!?U:*6} n/|0<9AN%w'}QRAcf4-˿l|Ȩ"|5Q ^ "^DEբمB(Aj$Awҿ:)itޓOy@2]D˝bc^8 cEtJ2 3n"7}ȵ&V;Pܙ˥NNd M Xa8t#FnS J)vדb6JqI{ ͥ1vGA'ku S07YY0+Tw C0(VaÆ\]iUKiVu?hy{j&/g{lqXG^`e \^ACjTŶJ3\ʢvXMR<sI8矮F.dI1-\cB."\RFw|~YEI-׌zpx\s fN l=vevK>͋'cp٦q;:͜jz5%cgU$$N#u Ql#‚V/[!NC^Eec642Wr@GyoE.!2^0׹:#8Yi(sHw2SR=HnaéE?#4Pw T9{Q|O;WEZOd?"&'1y|X;y+Idf!:cѣ dŪp.xP4BS?PӀLnPSJt6 ՐZn3kk.[DDT`шNꥋFg &nmI==T3ftT޼xX:&eZCgA{ ?A7s HVg*;K2~W gdGT>WT>"af4of~` *u}+"4W Hu):c8 `01P E<$c ґo`#BEu >R*H@:I $!)È>~ %6Ll d$ROä?" $6#G ҕo`E> P!lB p|tR p|X$&~6>GaS>B!= +H1a|#oFMu >|jkD8HP0E6,ht;WȰGaQ.f'u? CPy@ ݹE}0G-1[sCLKo2a\y{FBphl}?p>ɬ{dTT?hFfs^T2˳ 򂇫4qмvxy@5xःfT}HI` o.Gr'ch&s8b/8^aWPSsnF N~(w}?~( eBr8AA7PKV8Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgn8@\C@DԅT`HMSl؅oc12dN]|+#<IM(Oo?2:͗KN%Y|q姛K.e^\$kz2ʮȿ/*/J-{_փ&5ܽYB_.כ9,cgQkntӼ{1 m#HKZu^Mgz F; ӣXy]/cޜ}!NCхN狲 6a72rׯ<]oEf2];$M`ssAF^i(sH%6E{X~x:OӲH߾?'R|j y'*W>?rH˟BO|Z$d޼:!0[aA|BuvgdͪpĮx0_E@n\Nh\r~rGw!TC2ͬ*\nͩEK gQM4{lN90x! 0㵥M%w㿯(?βt΂A2n`콺TJ!ܒAgO Q?BPj 7%|0 4Vo@w >f| 2|/{*Zt xjac) E<$8b(70bbQ?hIHA:HC,R@)b>F.Ml d$/21I?"$6G8bG(72ĆĦ:~?dР@0~ G߿>"y`^!Ώ E?l Mu ~I?`|9,vpÂPCe!Ei 9\tqzR{˨y0A2\uq'wGliޘC/oLkrj&L:[;Wt/ӣ?PKV8DZU+Usvg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svg}]FZCc;i3Όp7s}ҮJЕe{~fd1YXrI7UɏdDƉ_/p՛dßvwo~xWO;vvpo}_=~ڙx~m/}?^vGŮ8ӫ?~ܕ<<~ox>ol_~,1ټzx&W}}wx3ߵ|܌c~qig_5A<3GlՏ/cOc?oݰsy<ưs)~x2~}{C9?v;p޼ݗv(_Ӊ|߿{ˋo?s,B-rCgXÛ޻7zhط^Eg.ƣE.wLg;:L9p Ϟ[dwmњ=|Oړm#Kz0K݃O3Y^mFCZ>I;>/ zk?yzd=\73ۭml|s߾y#q4q/o0ag;.OygC>ʿ1qgSa N=h·R?Zվ~<'_ٕm- w[C^ٻ7?=poϤ@CKݽzw'&л_I;aw)o`F{ڹ!ۋؽٹtFs?ZUݸ)OO70{fw;[Y*{a7GgUC" /*r/U2as7X$cj;m%zw ͗<9Cv͗.F@W'iѢyC3Pnى4Fs |A(Lm,8DQqpyL{ObVY3%r\RQi\f;[l|).֛͸(gU,7娏G&xLMީ9͹UH'W$ߚ8 L6)R}ljTׄ;h͌A]ӨԨ.b.N%D(ׅkiMU ǔM|37@l7x ~pFUD3jTDh;oKU4s8xaKXfEaF$1hfqQ6ZrxEd3]̉n'->dfX'v(mf Π)O gWG+ga33wRO ռ)T h@y3Q֌Qq4k0Ű2m+SYw;umI1b_i)ɋTL3;L.Q,4Y(I!S"iimT1l3Lki&;IsܝfƁabv7zIN1{\T݅ji|Q"Q_QӨ؏*qWL01k9۩kSET`W'V hPAYa9AqBgW iT]=! nMՉ9+uBV߯Vg4qC+룘Hd"| iQ9 i0_ 7O[J= .4W!$2C4kʤQ ѠԏVD]?^ 2+ iW?+m*oB}KFYtmJTHógy M5i ӠWLʰbVnuӻtN1 ѴtUu@~xRY3DCŵZ.f2ó:pV|YU0`?XlBs :Y_ P uW * Ǡ ̊bQ09׍*AGUILIJi)|@1t]S'x4&Ͱ JQ&At+hAGݬn|NW|ƱCyMC"SNVh}8iTX:"5U>ߗ 9N)NBwU~)`7`,lC+'4T>)+-W׸TrR7H*N3'Y_Rfl&/ϕ$DA/! DhDA5%D(n`oLzƫ|NvӦ{\YQ#",O߇B'<ŲdukSԑO)2CZ|)XܞOQG>Mzb#$馨zf.HwLCl䓴˓lon9{qS伱jיӕM=O՟M5~IV޻6&r/P7.Dwx n݌[>tl&͸Qѝ+:Lpd7g$;pY)?-]([م;ok](v1ZPzaB~&uc NPm5fX Bp'JZPk_mODvۺLt_lk`mݸ}xb}E.wCmI -Y nW1+I!{v%FMu2n0j^CHQƵZX7^;`hb6-wCmIt7t႙ml?UX[ju(uz1J?uWO՝Q:z1۩lKGufS+jU{fju&u)LlnJ Mխ[OՍԧsvl9qSgQ'lN:]::\}TI.JR/CխVn-USžRf]8mETSz]?Uن'5n'n\S5~bvӗRpsV tƼQaHg l]!+ 2%SD"ƕJ6n\Dm#O*Qۈӻ®OQz_LQiA2ETV>JTƕJTƕJ}ä–ӻIs/&)X*I7M8%D$(NӘq(UZ;ePY'(n a:p#޾R [Rh_J[*Оńn+gL|:;qlk$PTrdWݾvUF|(VFm|{+&ŬnTtyB){̶F)lmkB6)B)r m[vvZ/3-f[r u7N՜~)Ծ))q%jcPe'Y+*?uC$V@a3bhJ+d+J+hY+wWZunn7TY X/ cV+2b^\Y*V*16޲vqkC&tK6=Skvs UK^[^Hh/XJ$N]Bhdnm%>xAњ|]NBi֖wwAiv9A;i;+oNŴnHEPT/"ֻpM&6TJԎOA-i9AMi9tAmUִ4^8cM{?;@*S&YN<Zݨ]?F}!@Gݔn;PL/D+`ܞv~vu:z͵\c_\Ez]1a? rea]6=͸=Ӏu-jb@Oڟ`b@OoIZYPWgzp!޳n2 ڵ^6W>]kksyڵU>mQڶݙ|@V{. $zǻ!fZMujroZtv|nks1Yp~F #ɋnP?Q(V MZN>A)u;p1?@9Yګc;瓟o;u~]XViLNE{Z YթGdh{rGƾű7p=h{8_x,ww4aԛI/;`?$TA #'axȘ Y, `?? C4E+62G 0Y(g9x=ņtF/Cg.Zy,AkXLsq\^Oۑo-$of˛(8 ÷lq)#L* /7[cY,yM$qB5d`ly(礁LY,+1r(˖փlJmws28f6*j"ď<[@`L+0BQMּe"KCdLߨDU/7*1HD7>S"98SCܠ{5k}:9:sh7h"ýH"SAߞ ufcٸ(bg+ȋ4#/..5zAX{Ȅ9)) y *X!q$*3V&HX9&#Lt'6JjeP}o/qzz3hRJu@FQ<:r3'LJ00c:`T1n Xϣ"qY:8Ɉ m>tlc:L'̙L' a"= %=l=t^j&<)9.9fFso9CΫ!OIv5NI2Wۡ5:5õ] <>ϙ:H J& e[ _w(04F@&B 2`<ɐw{GbY^" ,rdAz,PxWX/6a ;Xp9$ՉoV7;(8'#S+MT!1r%!3.8,Bb9#hG1<C`FlS”B82D.p ?WU  4ech Kʰlvw5,~$Nsp+I` Xl@ 1EAj +h]h.3FÕEN2Z1G8S<"VW\k(X#&iKa6`N##xX9~yB^rՊc17!0T!(R]b#y><1D0Bz^C7#a  / fSH2EEX ~x0K8@~ )cD(J1`YĵHEsCD[,TDzNBG l,d^PYZ2cHyz$4q"hT3Đn<0e9}U{؍gz" "e, `^ИQRXC8ే`7A elApJb p> !5#Nb0ږ0NVyYR|)>ӛ.YBh%c=JL6r4;8&}B 48D>hnT8 K5rqЏݢI ",@M-!Zf2\OHK݁ @\%)gYN"oTĤ;bɓ>])=dVmVRl 2e2Q6EeM Z"T XJLd^epD{bcsd-3 Jm1m[DX|9X p2>60txUˊdz%ߑ ´ ;\';g>Vȃ9ǃe$ 󺫉t΅(sWJe_c_LmQd)OHG+]@7jAGхՁLY;Ȩ$0!85Xx{V,ښV޷"n0W0X{&4i39:{ r`pEoPɲ&S20kn sYs|+s6X.r\XH`&7AWU:Ot$sx#1Y7XJbDʯD&jMLι&ƚ1S^kbE!># aB^]Cy_ a<<17Rc˜ c.`ZB$Z/,~صR()Z P:L/,~9R[_Zi RV7M=)qߊ_Wo-~/[}3>U*Gr}!Q3xLeq4|Ӭ|F,4ڪU0Y Y?7Tk0ZӍMN+1؏ MQ3by} XAWǀZ#<)U28YOz'%&-^J=M Pq,֔v<CL25»2+7Xn3r= &CdMmZYΒ}pXWĀ @DRQK7>OO|' `hHU׺ c"7͙Z=L7ucj8[Ɲڍvj[D@Ovtc*EvOu퍂M\`0s2@''3nruK78X[/B`$k2w P[#DN*#tO9BǙ#U=#d4C?y|#3G0/Ubt<|9;f^rf<r(wl'Abg |̘K13c$FWt ]k܄ 􇼴0f(˭,3gf TGi{%QfX3ط`%J07=0ѳ<`g؉0%]=+ZOfc•;/Va,ji`/FȪsF \#s,Zr6v3kxHZm~R,|THq*0IfNF~J_, &.}J*ӣ4XÉj ~_Mppȝr;q`O,#*A ?gyܕ}`8XUFR5{ vd %\}5e40CM @ZgxM+= -OơNngE-.=lXA(MhҪc'*Ԗ|!㋔ZBE^VwM05Rr;nξ)nP]#cNuw2L*Xbh܇ub3xyڠ_R8)(X$dYAuId&]͑q{61Ok?e3k?y]尥mviIo<#dS5G6\>XcSV4N'l+m%t+:.PrOm&R%p?_{Dy|]?psz+6yAϘ of )U7;p'̢;yI.scJ>O8^IAQzw#3q4]ؕ4r4uJ ֬p);MmgM:$ 9 zz@ 6X|ˆ꠸ x~fdHg^>S ㈖d42߿Db`,fLa`Iu@d-05ʘ)!TPO =r_ *9-c}• r"w0Nu[wbdp)-C-)*f̍9rn:yȖrfT<706L4cf X/Jط;(dݿL 8FagRN#9?0# Kd$>rO0!> %\~:FfA# |O<&rIvy*wT"%SW-6 ;"l7*4׫^pC9 BjC~2[fm zi<w&Ulȼi_qHc<~icKlǰ?v\\M*8 +e0Џz/~TqI?ۄ,ZWÉ_?&7}ᅉ/~5BV/0u}_mo}^{qӣsp<#sv-_.^PKV8Dt#Spart.generic_female_pin_header_2_100mil.fzp_f222793d96fa5f12689e28b7e2567b9d_10.fzpPKI8D=*j PingPong.fzPK8D nb$ KPingPong.inoPKV8D,搮#]<svg.breadboard.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_breadboard.svgPKV8Do#Qsvg.icon.LED_Matrix_LBT2088AH Kopie.svg_b6cfe6628138023898052f2d0749a3e4_icon.svgPKV8D'0 M̹svg.pcb.LED_Matrix_LBT2088AH_pcb.svg_b6cfe6628138023898052f2d0749a3e4_pcb.svgPKV8DZU+U"svg.schematic.LED_Matrix_LBT2088AH.svg_b6cfe6628138023898052f2d0749a3e4_schematic.svgPKPotentiometer.fzz000066400000000000000000000446111272363601300334240ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/FritzingPKl8Dn_DFpPotentiometer.fz}r#G?`3d24mZML-U>X XD$J?p 3g23"==qO >f×4'hz=yx?%3_^n&O7$7~3ӷ7b0LJwʁz>U _y8zoOf`>ߍ32;|=Wa~uz~ʍJ/ɻ2S꾺,:tOwOWʎ2䆲[A퍢36"+sQpwUg/ӗ˟gNl>\o.ս_p/o>}7RW\]+jV Aʛ+st>~O)w ~q,x[_ ]>A*oܗW_=M?\M{\}i_><\ fӏ>yɹpu7y?;mMiznsj>}j_e"{kh>~3nz7{׾j4*6 ~tdx6W}殃^6i(Ce9*߸7ƌyk51ך_Sn["*sO}p=up+e)|h4JO=Tʓy^OCݏh~=>aSI 2W^ ~w9!D6TemS{J\R ˨VbEBm5Ꞻ'ۿF'_]}WkP0y?/^g E?xnFYMO[(eo,{¦s_%O 4bi'ncTNFާQmFާQO;YO4m}‰(}崓Qjs Iu{CʶsioLnQh_|\˧li4]>^=gGwetuVɱ3RTMZ龧|ߘ;1s9D(0/wWf>:oOlp?s'33%w1>^&a=WNް:z;4pjQfFS{(,Es%uy΄X5\y{_\VYu|,)o ޱ:lԝcwE*4ms )4jN (*»!Uy;jиJ?hIiN/*+-S>%ۈoY HZz, skx6!C9etl'eA&hq>mwLFqe*ʔC+ Fj20MXKb^z(/UH" HK %#z /K_}~l4}qpy4>M=>x{Es|{>8_^Jྮ o3a LΙ)^ڮ%Pf7ӧ='}`dSe4޳=wSG<\Ź?k%^] > |h`+'a#7ƻmq:y?~*d['ds 3r4rImYEŋ{Qj p{5Z0# 9@9>q*i+jCηe7]HLA Mx?qv 6-OHiZ8!P,TfYu3YkH!x(o2<(U! 2Dh|c`zSDU6ӹ nU#sxd t9҂2.rEgW"J}d<xFy$_H+)mqG[/C"3wG?䥨 . N[5zVgՑR%rJL?t.|,P[ SEM[X=|uʩ +1TKqoaA2W\@;q&cq_[4;GSU͍Ы""70Jsf8*BGd;ov* z|j|:Ob"-(/S0#gЦ]/lP~.كm{]Tۧa/R%v iYi,D4Sow:h1fNΖڞ $"#ma$Ҫ쁗*3/ؿ3Șq!s&YF? FAigx\mIS>9s}檣;x;ϧa6R}K؀.3(]]:1&(+8ISl+2*L:"X?gzD^$5($]q$nS3UEY8Y9T-(,&//'D[̛S]nl7 {E-zOmZgT\XhZZZ= B䆛DF4mCI4E»s@!bUrm.]{l9ۑę%QKJu ,g *SW8bhnB hIT$ᚽI:plNNVH;rS)X`RYX-@2 3YBD-)wN\ns.HN2$:D[i33%Qqi#]2WFp1ʉ9kZq ^m"R<iɚp!'$wplI<#X9tiP8"qƌ WY\$ɱ~$!$\c \@j ], qesx"p+D˸հj"pK9~)K"pǖD:%p1. HC I$p$y”|jGSd#T4nUoT ^3Rʦ:ēD&i[Hr֥+X.^D5*xl@O6Xs@$vk@%E;Wyƛe8m KFkK"wHۡ˕c[45>9:[HU<8=lK̴K:ܡXZe5'{hL1OȜ39MLE+fo^)iÝTBvCdKI]fR'{)zOI\˔lCկUlYA Y@wk S =Tjg^ g\ °<- vUrLbn TY0M Z`;8"Վl:(\ E\KXa@sNkvH|>Ѹs_">C I .08C6p]Lد9[ƴrg0Etm(uk&vP#9Mo *IFTpQ!XDbkۜ{$ck䬫3{H8w'Rms;Ի|rDm2e8陭r&i|<+slY.ZWJϜwX;v: $J'aS#%nayKF˝Ŵ$ tBS"o KUΛ$"fo'-=, cyϓ$_I.iNtY :LDE'O"*XԉeDcelnIR-&7 ~(cs'\StKt]B˹P$>$> ֬K>('ˇ0}KBsJY.l1[T+Awp;Pԫu۷D&qMxDlXD!(ӎ.8p%& R"qq(yVRΖ$$$ѸJ>h\ۤ˲u [Bu5qR?9 V@WjQ,0 RiZYbŌҬ\DZ\ؒu&]Uكoh[u!r9~;oy³4s%1X*?$Sŗ(|[ћM f}OE7Jx+J(E.!q%eߺĔ&p}:)u&)%h6qMLnr#I.IkexNœKm;4;$o7Il$w=38RCHU:%RLy2ѰȆF%қCIO) Dj4l'$Ih\koCH7k^J?)j)=Ezë" kYjKLNnqB"&lIٶ䬧$ \8f-Jy+A˺62!0F[fV0E3R/FX.`GmbV0–}WRKL9o ןX\PpN[Vݙɱe&V1$&@ھE'xNpNTNZQ=H aUډĭ XTΛ$$$ѷeeܝٞo7"';YڜbJJ5EL-\H!ag$@s#*p=DдHqK"q]y|D:D嚽9<$*;S9Ezr!bnIN37ɍ%aoKX\\#}O̪,X\HYfh+'ZT&Lh P˘Jɸ8%.ay\/JpHpI[1;=S79!\l.iO)ZdXp'&c`Α8Z.iXW+YY Er`Imn[hr?$q$qfo'ՕZG.KyÒGsBr:027ZEZﷁ!0RLZYv@ v6RѰ[\, ܲ6+[-{u&h818ui$P0jHGx4nB SBHٳ؆h"_+zs۾)dΞMFAXFr´r_G&6ƢƣQ2ۡߑxXaG& ؑrbˢy2I΋$/rC%*/d; %1T2E-sX+S #+5lV&)|;ub5#F Zb*[4 F^8 ]O+Li7W*m2N Y1[%ܲy8R\X}2U5lH}*Oug3S,j*yn [%\qF5/UO*g 0rތ$xzO-IoˢS=7Q[Gn;Yҹ^;,iaֲJyս9LIE4pXWAhO-B IܭK9orIsIskh۲h.C rMsch',aBIP+ ٛh`+p'4Imn0[r˷4k$Q$Qfo&i%Z{`mg֦%nI /;Ly.W|<1;l^glQl -{"UM>sɵ,KqJKL9~Jp IpMRI( =90)dN8IYLV sXr%{zOy4{aTNҎv;%.I;I4,I4ٛIqˢٝƩ73-@ENt{f (C!Ѹ (vK4N&%g:E,u *TNJbs`IbsO[څ5˚CJ&QlbsmlX-Xn3\2Ι;,ڿݰ%.q OI,IٛI"tˢcr=KY[J\jXe/B;QTY9Ɖ-gaK[j\SOΛę:9<$,=083+vb"'LmuiRr2(N},=^H ,ܐvLnn8\hr$$$ѸgBz盅hq[^.mnyRPieF~پQ,h{8iH i>WDjKx;LMTnn\rTG%1$1fo'D`{YDM<\ݏmr~;1puD Xjp+Dfj*+sM F3Xuj;t0>N\0&=noMLK.__Ҡ߇hsD٢x#9J)A 5Į A4 ٞW01MEȍX'970MލCi. KH w R"xPF~/oWjL|rucǿ~W?e9:D1z+d7uq4 W >bmڿ6m"֦$"֦8fش]s!3.Ofu[J호 m`<_ԷI`2iV~xճ+"\gh}%&t.4DnUU^E2dŢV\,P+ŰFkmSϹEsݛ3sֱ壣"FcY8kHg)H*{eFF2׿\ۡ/.+YtuBM}̾Ӓ˻#VK62~v*BcK!HES^zRL8sQK{k  ptV˻E4_1a{k8MS|ڞg:Ʃ֞pպ(vJO)Իrm1h8`Nܬ$uМ*%MXZmWZ'4U_Ę8R3ѩIzOmKngX3=ӕOX݊$N&Qh"ndSbh/ uj M. '[SX497gikv>CgGD:j-(҉OX[@q)1Ww>m&E~\oFû/?{?gû)}W?>W5,uyO_7ˏ?y>}zwd4t'^EfϚe?_OߎݽU7=N޿˾|D2z@u0ȲFCa$f_m欍&>N&哇oo Gߗ;.o͜kUyk_Md`䀛aW>%u sb8\:׼hKۻqj뻧,?[sݐ?}-xM~WINs_|_=B ф(09Q|ȴ{tp:$u/I.uiT6~rNg2Wt3pGXw!'}ͩ?En(J3"g#~u$_SЈ)?˹ z|?O@wnjwBj1":e?5VͰ[sC'(|N'A))o@YJ?I Ǯ /s/=/w OvED7 os(뽀WBvZG{H,gƀD8_Iw?Ɇ̸P؜; iPhwO>h 2Ab*aw{fx.gc %7G݋q+nBFW>|a )wtCR@T8G^=~eC/B^ȝ2*RbFOȋʍ?"A&rj ~ j5Μ퇍v*WZI~PI'q?b։7u@w~Dk,^# bFgZCJAS Sa_Zr r0SG @8<1)=.10a8=,%*@a-s (B4WQ F4 yF(|{/-i]=qpSfrD"w[֠ߚ4z-Εe:,j*D!No |A쎍A`˜" z(Ͱ]Z{@2eƔr#ztEO!DDefk-J/%$*0 k lj L+`8^TaU0 YT êz۷h8. Wx~Ib@ݱ7# +9埞~Ȝ39')HP/Ss$e]%@"FX@Ƿ܈7Íxǚn@Ji3áݘv|ҵ-vx||ڹO=5X"]V LwƯ jWɽ`$b8 ")y{ Rt7E' Өn^`)@"60NXVwcFP&jף_|LpCܱ7nkl +!uۑjt}6 ׳bVn d;V׭8h@sU܁Gf}̟@FV$F9fF:`+?מHF4Dz Ф3y(. N@ f FAQgSg)xm@f@0b~U1tܑu ux1 cC\FX@ OJߩl Qh*7`B!{O 1p4X@M@@QoV7@, UbXS[T"$cȤ9C N- sN&?P4\b0 0k% *5R+F(69S\A\" DfgYE4. @P3:c+9Z,DA;0PX%@KV?8bo:XE}`zSv4H"WVuVUOCɩ_tZ^Ò5aR #- pc`l*a` U)*o 7[ʼ2 <O3o vvYpt$e82 <)oU \`"g$ }Bq2yHT$yCkD A-Y`@[˩0Pu` &4/W64 #C$``D8R n>/P ]q (0Ч@cAt[*.Čh{} 0B0r$Cc>)b9!xDCF@/\iPp#Ψ-7_ڶ"ՈCZh)[E 7ϵ@8[A W!5Fc;KUYF*?#iqYOUa1Й~FYI 04F LPػ653+HrZ0O^}Or*wu~Y)MIz- sC#aʸhHX+0._X4>#a@=ǮpPN@= T%t=#Pw@H1 $+Pbr">9;!'Аw/r% C #6?;!p$+Q00UȄ!bM)Qsr\;Ƣ8w$@^)aDT1FPҰ t1 _ N yѐ'[ -@ >_8k09Lć Ǯ7Q ZS761,Ċ)oO|C $YV\BB8)C?nZ7N}pΘnHD"RT%JBqwᕄ"z]Ƈ@ E`T F2ǕAO(2E&U=:E:v|v qbcx|V܃a&0@톁 (}5Uoi@g  PL#Do*OĄHpglpGJ@_r4H_z C|0r +s  B$Ӵ?wA\ #  t>ܛ;IqQ"D2S"b AQ \H!3lod[0ƇahV͊* U#]e' cαPa2 *tqb|a@" ڎIZe`x/_$Fi3%:Д`` -!ЮYtZA4-h\B ^+9}]8'NWQxT0CY.$cXb읯8A(Wy(Dc†*-f1.K 2X|`DBt{ٜ P8 lz=,C 2!c QL8"% w.'frl]9buo?za\ob rHT &G);kMby4)li4,Q9(3(xUX[8LX"Ju~I 6AdKd0yìb/Ui;u|*A0AcDUfS &1}Gۘb9"3"ނPPoDW"WU*jWp\ipbk5c1O6 ךo"(z:,~.:jAŖkr:ѦP JUBe&&TPy)Tq#:1qLmdF7ށN&,83 `݈l-)SU}#c$ tuC|,Kf8H sp ~98UB+ELEY`V'>wMjR5E na#"% mYK0= 9R}szTsU_9N4,&3ia4#Ut0jdAԯrn,'ϹEa1 ֎9y|cE,3nTGpOc0kZ4KJS%_C@3FXы n8+0+wu(4yA8́duςE„%!7CW>Ii6E},Š8m:\6em,VXXti R[%5Y 3nYrbnvM^*xRP!Yp{S1KC4X]<D0L``gaaNȃ+_C=X/?w8h^x/JQД>W4*+,3mu5^::KPpPp~}ٚ~eq~ DL;_Iid8Nc {m>0 IP0l: #,8Yy Im%/g]rCP45ۃrsB2xjRj_[,C NM-c0QH^VFهcqp.:xWYY`1f2 /{V2eU86qJy-hnDՖaK,~>U/6.uw5;OJ[?Oxx|x_&Ow/ooǿ܎.o\vu{ˏ zu=|8/s6s4y7yGcE2s[^q5$w)mY i։$f9Xz/p#[W{T[+l!SܱD@M>=wYg4Y)|]9|~]ƿ-˷i{b&c%𷟾_7lT]͏y/2vqd˵# /EGnVBy6|^L䮸o~s;s*w;} ?Oz;jUl4u5Yl򩸒Vk(X߷Cۡ0'~(gCPR!y@iC@{A6@ * 9nNYH:Dk|ÊA2}>QrR-*iNlx}%i$M(rN<8a??+ă!R% [c"kmg 3MjVڣ+ $b2X_8|L2@{3^/zާ}e%^pX^t[D)JCTa_*-\6'4j֘^,5Hy`Cmtm8ԊE /~d0Vh53p]G3D%`ձ9Dc4]ݏToā A5/h!Liox npc֧ n ăS#X?>-c֧> ,|d^yK؄ waܲ C`Sufk c{Er ZrjWLF  Y*s4]g&RCzXk%_Q6G]QgmsP$AG,, r0e6Qbz'W9KVԜш *z2F"f:IkM\M셫ɞ9d9zO+ySE'6(ڜd[?@a$ڮdk neEhnHښ0i|cubnQCJbnVR&%ٞRmOV:7COYJk7mǕE =9B@J܂>x;ϧ_^̟o*;a`ihp(XD9wހ&5kYXdliAu @ck rŸ%r6wPKN8Dˋ[Potentiometer.inon0EYځl)E,H )iVX"cZtǨ#ǹw,N2{`N{0}ij8ؠJ|@䐼W O`2jR'-ng6}uP:pF}OuUV'k26j[pN_t)hPNVy(CNkC\:c~ӲeMkG_q!|[GʨVk62vWj2rC? fSmHmmܨD9h|<C3QBRoUԈ4|t$V ,~W3k ˕A $FtI $<)V7QS_2)r!%Oҹ ]Y9._ͽem-bPKl8Dn_DFpPotentiometer.fzPKN8Dˋ[GPotentiometer.inoPK}HProcessingRGB.fzz000066400000000000000000003324741272363601300332440ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/FritzingPKxE3 part.alps-starter-pot9mm.fzpVMO0+,CEզA\".W$nGK)q>I ʥIf̛_&PH'rY: MoWx)IT`Yeh9匬{O0*Mr =N$Q qڋfoΐ_\v V !E^>4|t/4M5 1!_v0'!o/`j1w۹$&s%Ț!!.ֶ 9m%ULi_0OCgRdTjFb JIg9# [_ ћdv8/ye*Ay/DH-63G:p:٦DKܱ&CPgbM%s(V;D0m &JWIIEFK2?6QMWdLENHU ꑱ:Va2mɢ:Uy+ɅS5{(E"}aA6v%T6TfR/s\ G\ƍ"]d̩&Mf>-bѳ)Ka( .z"NI߭EF~()CAd'_=M ꭧS\LrJ#39v3P\,o8Fs:wZn̻3S(Fd޶2͞&:Cqz 6=49tg?wZkan%e.O$HVHY{_"UW}1z';R6şPKxEug6=$part.arduino-shield_r3_two_layer.fzpTQk1 ~߯0~iCa0ǥS}}i_?پdIۅX'O7a[ɔ3kѮ}(ٹv0+ krɑK]K7ɫv^e& B2[|rVM'EvЅ5L|z̆jvTkTs\z?qWLrYpu%&3=u!Kݡq0-{ѥ]g@8S'GQB_uN06Nq269Ҙbi>e--1܋_b`(J!aԃcP]G'— 0;<')a6XO۝\ Q828fM焃XqidhM.$?e+q(ͨv*C'?!.whuڸmOuNLfZ4oCtmYc/a?\9ZYŒ&:@aٝnohɇY:MeS/Y4ZܙӈvEcDъO`Ix$hBc6Csb ڳ]t xX򶍮1:h<~&V&?r?F=.tRMJE_(3ߞA2,<3P%h" ")IoDl*j&#;]]DKFgLPȀ%h&IKTdO:@_8 Jd ^BL*%%E5Ey*tV"\pTfnZH*Xctqws љɊf,^wFbaٔ$GT"o t,p ddu fqd _@ g84I$CI~#&[}SL(tLfftM +K4Di][% Z usLamoeLC'B PGy,yƪ  ¡b[L8k!ل~&&h~)dF#na0< @˪YmX֟lEJxuT,oVYwLef{:o]ÌmE)ytg/oA7Tx|J*V­D2/rCO7\jGíY*lf3|uzjZ6:+az SUd]naîFFqحaW/z*ܛ.8K95rhš]C[/v=Mq+8ra({U8WC{S:5](-4ţSGG/N=M 5GiGl X/ H-c0ҹlIH{?xTkKRzylT:b[2U4>j{V'0N |;s"щRq }_^H'Lv0)IIcSq!e IH`D1A'}!:rH`@K}VֽWsp8)I󱁃 ‰W#Tb>͚{o&=057&vv־_sI`Nf\pJ/Bd:1j4[`FONqn<[`O`Ny66<#nJ#m\bʿőh HiJJq\ʿ1*Q#R2 +tusW:5㚗UlJmMtj]B\2!|k^'T7F(j[bkdvڵB ^,4f!&2{ցD',.I%.5G0ɥ¿1.f_G ܯɣ¿1?tDfV"Rs5T7F?TRs8jRoJ1OY@*}!5AOǺt# t2iVT͑R3ңoMkݖ3) t2iVTqR3ΣoM>H]bV\TqR3oM\w0 'frHX!5c9*ƨrR"4!7DsԌߨqH{4+zC*Eo Q7FeTT!b8Ds ԌDuhVTR3~oF bҿ7蚵q+m\{Fpḕv8[sobӻ+ZHɣY{ռqkmT7+\4n=yOӨ5k֮ƭq5jܚc<Ѭhnhhnh*xyumP&@MA͛h*x,n}.xHdumP>@}]u Sϒ`3n45Gx/~x5ƈlv*ѻ>lZfE z j+j 1- b^WT9JrzWȉŸ=B 9F 9V<BQԫ,WD:?^}z]/Ŵ:V,?PKxEmY(H"kpart.Half_breadboard.fzpn]G?'/_=|o?'>8y_7>}vhgpz{On>[zEz}rOZzOOE柳7wٝ_zyg/Ngwח.~ɣO]|rv_gw|y}ݛW ڧqv/|^x?λ;~yyˋ?_v积.uqzWogw~6g~5|Qo6ū//>~/xsxrAW׿r˗~ux#sy9>?nM'ynsl^?ݧ8*z1:Wyg2^=/*~y(޼W/L^1h%~n:ͥn^ٽ^\/ΟW^kyw Q/NOn~?7=8}S{~@_]=(x?7Wzrݛ?G!vgk/em?Ƿ39/SK_K*z_a_Rҿ^>mX׻ͼoO.;/SKK.zaRҿ^>oX~o^)av)y?D/0,=JǹatCCCYJER*}.r]GPhRs">""GJF9949T\0ȡȡ,"E}D Ef)>9.r#rh(r4KatCCCYJE)=E.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29Sz\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K2]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhNu.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CY:"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GtJsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef9Ů =z' j~rkћ9w8wy#{7 a.tkћ1:w(Lwy#{7 anukћ:wx`wy#{7 avkћq;wtwy#{7 awkћ3^v2lix4LyrxN- i5^ox0 /;^4<Grn~KEoHbx07ݻ} y?FG4l]J?/~~߰`^?)av)KSCQz}g g:R*}>s}fz64KLgzp,3=GggÙNtJ/^3ӋLYJgz>ӋL/3f)>%L/>3tT|3b8iNu>k^}fz54KLgzp,3FgWÙNJ}W^ g:)gz>ӛLo3f)>-Lo>3tT|3f8iRޢ3ӛLY:Lgp,3}DgÙNJ}> g:R*}>G>|f04Kt8̇ZڏУwo0x-z>.~yOrn~?^޼ϔ'{7ϜfC9Ns,< j~΃Ӝ9Oôg<4r09<8y4Lyd<4r09<:y4Lyfc9Ns-< j~ΣӜG9OÔg'րӑ5`yf&Lyf·?έ˃k6aZ3s>5tx X^ jlMR̆wMV̜MV̜6MV̜vMR&x >MV̜ƒfMV̜6ăMV̜)vŃMRx3ƃMV̜=ǃMV̜G6Ƀ.MV̜QvʃVMR=Ns[y<3{9ߝ|4Lyfs;n9iV̜|wr0Cq 8t,X jqpMV̜>8'l´g|Cr<&g<21yt?7aZ;l9iV̜|vr094圧aJ3`sr<'g<3ßN9909t:'-لi599hyN&Lyf99霜dyN&Lyf·?''9,ل?on<{뫋wtUq{͟zrݼ޷͋/h_JnJrגywzu-xz'{$7ps|+py5d5d5d5d5j@j@j@j@j@jՐWC$Y Id5Ր$!IVC$Y Ij˫!KVC,Y YjȒՐ%!KVC,Y yy5P$HVC"Y ee5j(P$HVCjP%JVCd5TjP%JVC]^ my54jh$IVC[Y Md54jh$-d5tj%.Y ]d5tjЗWX^ Cd5 j0VVÐ!Y Cd5 j˫tAP@DuX2": r+C2Ѥ&e82 (eB)#uuR)A JXJQ%DX "WºW:XH,AD 2K%,%Dn "\.a.a/A K%D "bH1AĘ LQ&,D K "g4A$ "MX7MXGM&XD "؄%m6A M&&'D "r’r9A NI'ֱ֭D "wA O'D "uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@Q{_ԔW5e)e͵֔5e)Mٯl(( E"E⒁@Qd(2P(   E"E@q@Qd(2P( E"E@Qdd(2P( E"uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@q@q@Qd(2P(  E"E@Qdnn(2P( EK"E@Qd(2P\7дnIdIdIdIdi@@@@@@Ӻ~μ|gY.y+_caVXoMow}wֿo!qm\_M{o~ӯIdV)t)9{In޾ 8|F@߅Տ؞>DwovbN}?xQ *9>Dwgj5FPfen-#o$>$wPSvO=>6oq7woo||z؞=>Dw/>Di`jMtm_&BQ#(yȻɳ7zao?rt/|{oWoSr7ͷov]X~I>=lOeoQtwzo4Q }@&?|df˒/ZTG}I_G:y~gw?PKxE:!part.led-rgb-4pin-cathode_rbg.fzpŘmo:SX~ud]Ӟ[Rlo`Z5-;` ! cm#聩rƈ(\Kyx_g~ҍ`hx4 97giM9[83.lNt׾Yx&̷GA6]`k"JUG`7;O&&(;G*h !H}K?0% \^ u} #8׏M^ nz#R^.)wT1Up?d"IIp0GI_]VN[-ZqREL㨌TUHR٬ØhƈUr F"@r&v}H(!|0Fayq(VID$E0e)3>5o)CGpu1p}y/ۅjP*'@"K0j3X:HbM%Ye6{Nc+~aJ=q'qjt{C= FGv"\`uZC;pG\)n518:OON4j8D'*dp)WBP\4V[o0<Zù,!\I Փn\IE1i !n5>L%gtG gnzJ}M0 IdJnO=hdy %Q*56%>7 y"La[ dŋg-p/ YIJŴO^aQ 5l8C/gI5LRh7]ɝEj[^j ,rVq#l\OĮRG,^^c"['36Ά*ˁ(%-b{m.mt*V3(Fv9tkSg}OO#5׵&_Q~oq-NC_Y?j%?k@gHpPKxEyg"HProcessingRGB.fz}r#G|c1C&~閺UjdS=I}@$ɪ~~FG&@B\2@K 2#|Owx2=|uAsr~z_@r99bWj&W};ٞZEg77è7.2ay6.:OçW_:?ם|*y<W^t>nP1REv0}B\qJW9|8݌{е䚨^o06Z}5=f a^T"ٹ= B;{׮+xˋ·^n4_s3^<}{.:Ƿc/'~vvjɵpǞo]I{||]]שB׿?V.//bs ?Emw`\ӫŻ 7iMI`'\ {DsB3 > jXS^ۋ-Z@;?ύUY.?YOYۤm*ˌ 44ּ?A uNSrJ='i)ZdD;~#rLOG4bx%M8XҔM\p4Y숵z&-5cJ)JlhD5% 5ȹ:'Z儈uxuԡ?z|׷'9v S8D'mOO 'i8N5:G 2& 1D'S 0D'B^\f' Slvb](K4xTd# 1鐲ㄣ QǩS 3$}E}цE}B2ohA"3q3Jb!I=-Q-a-vamIF:TҿQd#j<2*e>|WGk"1TÐ!RHe|ĸ&ؤZ*R豶zj1O'~LGl}  #Bƥ֧PO5BN,#5ȸ}T)IsR>b|^lV--dS_|S;.Zx0=Iu1GwrkabUp!5~9;\ar!s?Oۻćhd}}z&+X߅S;}5?|V(͇ S\01;SkGB"<^)s%.W5˩LŮרH6P'lx*lh$]~%4]^pgR|¨T0RbQM}@kfzd)*"UnQ 6fl.A#H8T(dŏj (8ܧb#- 1Al7%1X=[yH!U}qS&hwI|`H" G#}*^=Ŷ88D͢\5,"WL`?MT2F}OK'd4%Mhbբ' !y$la+M/mih [Pl:5#L\S:i%ɍ>I5]>$D'^YӖ=iks﷖D~-t9aY!nL &#g QL"l$X)xf4 '%TΜAy`q2,Z4| \Qyr)D-|7xGÇW܄t}33b+rk'n0Z[2>Cy "f`qJ;|d}Ț_F'ɑߐuѲ"SSOVեD8fs0d˜&svowDsxd{2[bE5s;rb$ ج1}M戰>4&T]e+v5yx0Gz|9+{)m[7g";nU5Z-4E36-4M!d5uK!dAs<ŐS #]<oӮPob5|d5Qڳ!6N>LlNJo$rAa4b-k"6kgfFhcןH"sjftH5gHrfIJ85t sMSڂGgASEXrSW,PSqjŭ!NmڊS\&rZY)N}t4"!M} ַ8q #YxpK:8j85NwdJ"R kqyW4>Dz>+mklƫbڟSZL!Lj,b((ltU:)b}dA3VcFB:BLĺռG|{@j@b= amiF,ߑd ȌJ>GvJɵXZynyO^D' IF7P'cl3 B$ ۄD%Hr[Fi-mc'.qlbբ' zchv{`znN[,2Kj}VGՕ=8ZdR熫g[$P^]X&Vhvz ,EVɋMtq%n"(&V'*נJb$ZXbF5΂Vjqͥ+LjDjݳW=[ћ{Zus772?'~YT=?*<$?rDn9:9fXќ#$WfuqMu3opu4'z[&~RzW}E$qXqZW5S` ꫛh2qvW6RMm!:bujeWu^ؤqX%.yW꼷Ss~5U`5$ז&x9 qFm_Tr}v-2vfѣl^ȤEq51rth _GٱhO9]Xkpp8$1?ChhNͅ ۮU/0k<(}5Fϲ5ȴE9m> 0z-0N؈sve7LqMQ5w ~J07j](& f{[бr yՂMQq(PreF&-X_(8K=8DMW.ސc,u 8?j}l,=Q8A'R4jC4iƪbբ' !ㄋv(63j]2ƨɍ ǃQ<f0$QFH1mXCKm? µ,5p"6CZďDVjsOumD^yN+cDג-㶵%Jl6񓁍e-bi:ell/K\ƟmQ.+.!9ႩD1mT5Ikߎ(((S5 M "ق1mAynw}}mZ#q\mIsyH03lwc8^MJMܘՐ8ӹd@W"yU.CCH^ Kq<$oGI,,}}-t,̲Dw{|xL *n$voHq&CY&̚2AFߊ(j,ҤIܟhgڤYtN&j[;9_GIMƄu# 'U 4b3:8\Œ >5ð2S&ODW EWJȯb4\ړgժbբ`{QT[{Zj>EHj W~0cbW C;oHHq0W(g76OesFR֪q-UZi'q{h3=įV1xaG:?FtK8X8dݒ@j0)'8u6}~ ?1[bS"vx3iX8~emn3¡$*WK\LF$1X[n;QmiQ:Yr^.4t$vo7hT|J o h+B̰6VV;S s@KǕ)b=(H|--:M8ȡ"$nENΊ1Df)v 9AsZH\kn&+/ΕfkrkzY~"*CHtkI"eĴ*sW%wCˤFhut:o)W()OŶ(ķ-ڢ>ۥZ6X+P݀*eҚy *L~=;BYlGT Oމ1*sZTFXq9JYuޢ9J\{)x\V,8^qy<2.Hck-1j5bsIGWl̳ppw~2u`m5JTGbUnD#~ڜ#dmnOl0JܒڭsS5I$9O)((U7g My7 Er$vȑ2^s%^a'#UŪEj.Ϫdw%Y!Oi`Ve'M,pKĹ"=y_m9P$c%Dޣ9 (n) h+2-:*V-vWsX?-[=!وl lƱLb_Q ΔkaQ&1Y!.A'E)up8Wev3ZUZt;H9F"QLvZ_UK<I(d-Z ?>R%QoaO:FhHQ5tW=x$µv^ ƙ.ڢÑתbբAGw5l$L_|1cwF$7|ZqGfJuͶ0A(w.^@GNx>oAr+}9oȐ40Ş:8v+ k*V-L\W#PhVE&ӬV76 "ܴ:QbMLm c %ڷA5[?[UZh_F[)X$U,8QvFkYPK[-LNl,*4ul,< &V7쌑M$#8 }4q\Ly$Xvy6샂6)XHrR9.^(hIb^cյ0HjL ߢ@V)i m6d"X+(!#Gxq4"^3V4ıdEVUnZ5ZUd;jsgbX͵]/ADؔe:ptOpR-Yx&#>-7̵,ȹzO$USt;hT|k>ox~Kp$Xv62˂67gba\Fܵ?Yݣ,ٜH(XT.-6(F93 &ƧgS+j!6BR=E=-Jr4k8Ҫ0< mwƈ5sq׼~,ɥ"ubځqmQQ+>-Vҭca’XHXTHX3q1֚تbբ!Gbb͡GJ2cm<(t)m,mS?/XФXM[a==wdEgRjH/)D"D3L$FXvYcPwfc-y) uZwS|-[ [UZt;H\9H\u\ͭ#yPb I:-v7a$QuEÐDŚsb%H\lUjp#q#q}p6M,vkOϭCf C2]Inoe5t`+a4Qnlw+V$N0ݢ8תbբaF\ FZ_:;גmr-`B8 'Kn~>q5[`N{rԴXXG`mH"`n 5gHL`تbբaF"` F"`}0& b4M6,O H$_pXӖC_DmƱ#uED3$ZaHjUjp",UqOq8zKDCu`p}=dz|zz&J`Wɣ|6p/Ǚh4).E%y-GBG#tXPbtݠ5JߑO5uwN3$քy59̳4GqSN[gKLE._y5rjflq 5xʽl'u>8YL:m.y.\u-s(EF]8RjQGE}Dl0nt3w| f{Ν{?g4&rJ[ 9oÛۧvsb%8'5u4?]e0 G_}AsEg] n:3_7OÿR'7׏L^n:?w߿|]^o:?ۡ9|񏗗?~?|4|;=KWt7]Q_=]}1[YuS?vz50|w{s"v3=?sکaGϿN."AW]P)T^ox0x/$Y(e_|_ܹ/RhwQ߅ɉ]`i?#K2vI.u]w6㹑NBL]gᮤZ3(%'}ͩ/K7_ nvZ}JMy-^f&/*k ڿa]3Tj|Y mբO'*}Z+%?/h֝VnEs"v85;mܿ4e*eB ׮ /s/=ow OvED7oenr}zڭpݧ^]uL'YWv͹PwF~[*OjF8:tAj/FBwg~wwbX*\o\c(::U wg;˭NK:5-Bn͈\Έ0;e/n uwgZD^|VnY"[wR[PkP#gW9sڇTP<ms$|?@~͸opyC{W;e;Јh?`o 1 #?TV*P?`RiTTDZ^\/hy (Y8u # ңIR 4N븅8׀"q ?Asз4LEƏb~Bÿ3YҺYNP6a ^޺KH 6k)C<4Rc+ ײwTYY m6_A}#e c/4j/(Mwe`ˌ)]q#ztEO!DDgF>n6S`Wa5Ua=TYMz譯߾h/4P. wxvIbunkoF# hWݍ'xr6m{9gr$oNS,dJO w=9"rB?@zR H]($"/U_3hl@}Xe!-Vm`d00@AXVX9oo#kpiHEʆH/}v@qǜ Bh{ ,"Q$V"-w7" p#8ЦnfTnU];X׎V\wqvcp* n)%TM_fB3`bUr/X,0`X VG_A,I E@WpS m0F$b3-4oX/{7`A2Q e&/.nsOY*A5MT=Yhԣ:N5@WLAi @@-1Ȋ)`S@+cm^ B1t 4ANMN~:5z_|uw G:Q,'hHB0`Õ<ڂ@^hynFvCfhE ZJ׫D4 s% NåhxHMa z,hN#ԑ4u8пg.'ת0 1t&!aV )n0,n '00oV .[b%Z.+"!^L,}qT@,{AJp+(4;6`!Ȩ-NT gn%Jq';= {`eHMp>IG J=x `v)C^nD2162qZh- n`J8!O+|yah 9wm*@g]ԕ`@pTڽ :8S~vY [f꒜u0W=14eEx! tg0l ^F ]T0b 0<ؚD9g3OaN}b Ȼ|]s >Ct4J0=@ kF% y2aXS O@T!0WXt~4\gp]+E-h*;(k1.$A @IE( yBhE@hޢ $Z0KF @|^و0xjxՠ9ucceTFOHb N¡ tUL/E' w~W.49czVD""^*E"Jȿ.|*!U{(3]J憢ca0*R y#avʠ' NC"A N*">v|v qbcXJX>)0 _ ػa`%.+l)+J*-5]8;2[  (1!>:.P ҿ׀^h@$ \!HIšhĤl4; .ׄCd`Mu><;IqQ"D2S"bJA(h$# +0`&EƂ݆Y#]N ל"о npT c*h;F&iup >vv$Fp6!'Ht*˱`|$ZC]贂i=[иt+9}WprO`37] Hi\c\@Rx׈B$<&lH~rbvLHS>$0_ D1L;ly t%_B@UiaF!x2G4P&4 uD16 e@;w^݃fuoj& 3!HR;(~DP I0$³U 5(~17Ө_a-ι@@-S[A1x;KL[ o3" k t ]06Y/6=c~/5` хZ9dsB@Ua#(i_V[&eB`)8p$EJ^]N.ӱ9wqsZVxqraEy4p!Q-l$Fg 4嵫Ӥ (Rb, @q@shn;!hQM[7^W Dqb67l 1:oCe:]h >Nʡ 3@E'(1If3ALb6<ķ19N/xʣA(LAb:(B30r_q~2"8?vc. EEdAqj|`2u~q `H0">."#RF] FU'Omń%* 2P02C PD*\*廪&|ݎAޭb*v c8# ~Vc԰0&*f >"Z1]bMDZ/5]HIty]oL e~)nHa.3)kYljМ/ +{V(hV36V9=Uk%Q@/Aa`]ݲ.-,f 3(xq~< mQLi")c.X"ނP{koiyǥȦ*+848S Aq2OD!Q[\4TUJ+ /-š>Z DgUNTV_V[#T^*/j4.r@'&1B-&cP;ɄW7b1@˗fT(Ft~#c'~D& If,m/Uri6@XXkX Nh0Ͱ8[ ].&R]Pfy2Cxg<fMJckLðk#r8) uY\K0 9R}s?5ԥ g꒘a蚘J`ҌLJpLD*w?ۍ9<`~pwpSKHeI+׎g~b㰔OTGp/c9aiNÙ(f@D rSbE/2xTx(b}[YߡvW} >*wQhy)0b1$*$:8q2Va= & N 1\ DuLi,HXձX +ⲁX4(+cq;95A*4zձR5KaM:60KI~jWT^LM^^6!']2k/C48x/`0m8 É9!nbկj^mz|A:vvSxA̖5$~CSR\YШxt&Ψ%3A >ﲶ/[o".oᔈiz'&bRY".\3Ǯ-tGǫt:Lv)a%3 !Bge0)&C0zAs ]6sb[wɓCځTnoߞFg]ysmmcCœrWf]3Gwϙpflae7nRōDv/m3wa'O~cZ}r1~^tOME)񵲸ɯ~jm}^)"h8F;ډoӪ]]|~ vEK矿Oo^s~glEᆿr>?g]Iz]ZAy߫w݌N;Bڌ d98k n{7inzv~/އ]r46f!䷶&~o ׽]Qo~f-u  8t?L_xVlwʌX @򟿼wO%> \`a?q=o5s4_]M4םL߷L3|7,l%q?NdWqulW`coYdI~Bd>Z}v4Ok2YDv2'1[IGOtam9y#7hֿ[A}ur:>է:M#W׵Hzx14mpC_]Q~hn8 c9"D쒃m?>ϵGqD=ΓB+4hepZnl2^8.d\aKq,4d<7~mɛ 5^kxq89~Sq~rojtDE}2 ܹP#'V\XT8GDMv\B%vU5Df3o4DD5MZV~b96!41RTsiAM)yx%kM UչNV|aC8&ղ B3L_>?+;m8,؁59in 8όũ?)q*AՕc?| Sj?V4cunBD\ fb5[S5QvihHwkx9iƯ%_HknhW3Ѹ#^CFFs4cH1-AZ(%J46XH,`YހeQBdY! {E3կ:[d@csI%ɣY5+@Ng6eǍZj,W3q4k .RV9H|-CV=q ȩȍd 3m렐 "ƁщsE8׫~h{#qfL% : d( i1DÍD*2ٝ6^5NEkS3NhK4,P,ͷe{"ZAG 6a#Fb_ !"ZLF!'HknWCq[jxÎ6Y}r˝8W$Pӡs>P/R,o?Zz$ rkK8H^L~\^sEĽdw5>`k|(jr" O,H,ͷ:AG͐MS`# 3_jk{&ך0#pt2_ϮЗH/PKxEqProcessingRGB.ino5A @ EsTJ-6̔L7.xUJPKxE>6Kz.svg.breadboard.arduino_Uno_Rev3_breadboard.svgkݶ(}{>djox8-;$sKiw,U:-$v~ZII-56 7/^~R8\^ᗷ8???_o|zxg=~<<߯_-t (x>oC?6,C_d,\o>x?~ѯZʊemqqOo|A Í$/n/}|QF痯O«ڻ׿~^>Jh=A z_>?}7]]_^ŏ Kg>?~[C|?_z}?ߟ~~supxׯ-?{qs}?zJzOiV#:a|g_ώ?'}\Q0¾Xzyv}~Nj˄(?S̐]{A/K;Y -豼Gтߨ B+󄖢  l3yUI!8Y(r:_NvYʫnǗ\7?|^U)('ӢQLEYp'JaYt3*<`K|z[rq#\^:a_:aoO8:ş2w"n"r Z$@Ky' +;p$Qnuox^Z<`$!$yIꞶHIi$ 1b.k!F67Is h7V,@!\߈yӝ46-KQcjr5?/u3c[´$,T " A/ oiy៖ - OA쪭=x_D:5okc >:64.:{; ~*?? Mp)=~j2:5ֽ m [KUCkݛgftBtNdg,ITAYޛq2}S!5ֽ +G 8 q"22 =;H7!eķ8WWoܐxmp0~<. o5z! p^Sh(%5J5zc صnMxN):'[g#矨H=ۆ܋Gy7("x'jyZ MS .1QlWMc Xÿ!^aYT,cz&dXܢ  0.) b2 nXJ20I1LDCOD6!cB;Cʬ1d 5Z_`7 C n= 0bm1 Lx`xx! n0< Q a%aa_2@Qt_5t}J׻aFc5(4.G2 z\,҂7p١^YB=Np t`BU#}Z+S\ig'U ~j?׏1>xejb`S}\#eԸWih]KzmyVP;\;!~t ~U ~^)jNw_)r?G<t_ 5ܩ3jO>1^ 4`ܨq#V4붘L 4ܨp a&]p nTs~V)wƷkV 7Aau='k2P& :ڝ-$[de5 VT.{г-Qe(l&ǯ(X<Lp=_.D UP`E¯(cm85ioj `|5_&_Vc]u5$vk0 g*P>j/ZjNw.L`?O<ـ9IcV]faklpgZXqyOAvnMsH9)7@'}<ȱ(rAz5̞{; !h2yvqT\Q?6Xb oe<š)aR 0Cfa!#[H |ݺ7  V {2Dj=~{_|mBo'#tJ/\Wy _o~@ɟ>^/ۗ>CB۹| Bd8y??]8kx[Ѫɧ:oE _'~{?~/&G?'" xX =z4<^Co_BD"!z_/^Gf#kx>/o£"!xz/Gݯ|'ݾ_޾~{A k/xFC(V⨎"YCaB[Qz?O"@g/-zѿH"G{ń?fkCƌxNWpVhq':_z!qRF ƽ$]GtNuuT.)}qK]t=a&(5M]( EדҒ1&GwϒD(W>1RgE+WiWQW>ٽrM&+7QfzDxfT^0fc^yqξpNW>4^=ŎYoontvĔSףڍϜϜrkGb{Jy ?U!$ha8L}j>$ϋ^>>F>F#%$cN,)ؽɸ.'}t2e.Gi@ |(@Geۢ>D.݁^NzL0K"꥛IߴIߨINzЙ V:_k7I6qUN;tZI*f8Q&8nCuȥm. D]KkSuSߟmBwjiԟVOqSlJEݟ*fSQ&8?ˇwt's]҉<-<7t7`g7:۾ _=b]|\>:|ѣJ>z Ξ`>D.ݯ5 \r_zjd:Mbu-79l6`f7:?;$E_`iyfO""Sg`8h?~9AX{wy޿q.F(m;}:}ٍ¢}ڽP֍]\/h"thS8pWϮ?^d_q4; 54i xċ>KU h"$x-1Yx[Ȧ'ƀ6m'9KF2@ȧ9?b!YK,x{ɬ%?!LbY?q&X<`28ȟ}/>EfM #3v,KfMc &3~E,Qf-G/ JC28ft"A1J_O m3]C&HO?7tܨpv{L-}N+w_ZƂy' v͉̀t,596'"ILDɋ?}enA jH;ѸuImD)u!#%6=.6ڭin1ޞ3GںxIc٧Vh /n5{3h58{N8{8{vm-=Vueɳ1K ãkx<{PtL՗K"t4h\_,h^E9#nDBʑ52^/Joaq^8}%ZDmӷ&Ƶ8?](Βksv 9zFV<\"+H\ 9FQ~KDɎ d$!<ۗKX$I/ɀƍP|ŲHrZeYf7wXL,2EL͢}^0&Yd8@cXAPŜm=Bk Ns<īDm<׫/f~~ y;ٗrh, &_ XcQ >s'K1SRNVV 4Ɋ0,&S(\䇓(c *ew𥾽3=\ubj$OǕ-A?=XF-Ie 7;ZIOB'ZD}C\{#/ mv~g,dlIMn4wzBR"tz94>-WɘY'(dA,OkH嘨7Z&^)Möu=&ENu8 TeG{.:EϊvQYK~$9ُI|4]LQ7;D1AtM7)n> M#Ҝ#X? Ys~c 'Nჷ4%ߘkQ>HXWfIƷ5KrM4Kƈ?>|-[iR*wQ9MB}z;SV5xdU |ݣ'}c F,:6 G`>;&#ؐCD,EM_|i*mhr W66w{tfl~..S6t/*fAbB4P&׷~s}/0ZU9}5 $Ml/I!8W'+%8PBKa?cib)%U m(TR wmz:\+K}im!Ƞ c^s5J4,mE?J%-âɑpbu\wZd-#-=%qoO_މ%i|mtDoMMЦk;hvю0FN'bdwIN' TµR]'yz {8//Ǽ'Ivbv"X}|ԣ֣ @uڣPZN-(Ԕ"UGU6( LG1$0ƣD ʣ( c8.K8 wIgy`aKk2`/ ū7!o­IBIP\](gp]oBƛ 2:/ ;Hݏݏ8VIs?iSsR(BAB'y[}YCv$kp\1Lg)Nzt (@lX'A[~~Ҙ~~3,01$1U]eIܵ$v6~_mwVs_Y $#sO̸n2z}t!5hɍ0FkLsoH@~EU_;,&lpQygz3`?8l\\31:#ѣUw$ L#1$0őF %~D@ONX+9Fp?6)4S']N0Εxew+~iw+BLÒwai4O/ݬ^ر{JkB=9okflk5<GV 4`# k-}dVhh|iJ*\Ͷؙ@ls]<-6맟~ivZgt-Ƕd[KƢ|V>~4'ۏGǚ辽|ئ5lpӚ8Ao#Z̫gW|_K9˂9g9G1RrTqq7Gj,5ċ$jo̸q⍟6ى`9UL7y Ww8+8;bi,P#fZě<ר;~x qnsexgtǏċ>34io´Ǐ/''^lƀ-L8h>syYx:x,NیrW<Ό+x|D闩D<! wAXP!r Ax"UBл_,9qhI0I[NloFXH!rB )LŃ T?N⁙g2>c)| }b$AK2F3zJMq&8GFSY( MabN" azm3F3gJ^[=iNl:سf7ںTIqQ4b7q_k9.qb+"g_1N|_/ 8b<cQL8bqJ1N 6qQYq7C-bP" YXC m&Cmllnyl#ⱍȏmcX^~Iٔp#Z9_|y&9"?9Gts_IO0qb9"'5G6ᆋ.?7G.ʉtd刜or$9"?1Gn#cnˑ̹&Y92?+Gv?ӓOqò-.>E2'Gӧ.c<$!eX@,sb ~ef}r"bU閩T<&V1ě- tN0Hn* hXu7[̙W9ᰢmenSʏU}S HV d*?PJ3a*H@B2*?P8ۯ;>kXrB !˝uB[~Y!TN Z1.CBuܿn8b T+|>%*?vPO7vz.@BsxCխt,RAD=|*?xPofϐ:=Afj8t~;7_O~7<ۏ/O|9~"FQ %t+kHBGI|$Nl pXBK-wC JN(|bqΉ#t+[d:?Н8~գ~bA "t+"t~e}W,bqΉ#t+Y#t~q椋:'Э \0(BGE< է5(ï/~z VT/d . m:iAvl\Bq`#u80\qy8auoث,]/o/WvX"Ia`-w@vGK7[׶-'%<4??HKsIۖFwDÝ%6N=gOO:\f_>,?| w+X=̟ ғbwH&+dG}-qs+:L^A,^d+Gi.z!DO7 QTpuaN/W'N.7CB`bw)m`֦CW&JZYEeip洡9B Mm$M#{p(/"ȢZ:QpduԁGeQǬI8::bU =.w#9db5r4B#jjxW0# ; ;~Hύ n^{`"PǪ@Ei rC4pkӈ Q^(JҊnȊ5 Vz",ǫipFTD2D  H ̚nDϊztNu*[xi}Y|a-ʅg XƔ(!XGh`3r̷܀ظ76 F FO*B5 ,x6`hlAlso IھY [߿';6Z3ϱz 3Z9Nvlyc`NO}l;(d?A3r:t#8a_#g6 oI2ǾYQUb?B3eyat$#򓁴oi WW^Q3OX""-ȩ>BaMƁjhXWdjE 9Finv#hDc[@VF atd:Yr#&ՔkH"ZdH xF_@$r tdG7! .+eXAVh 5XGh`F,.|oHG6Sg͋6{ZwfD/sB>uO`B?OF[2wZc~V84\9 ..<|n>sB8{-VwM&Taeel;]SS m h%K6hyhsX_yH 2v>g6^ϸa˜)E 5㺈'"T|о.B,tQ ũZl\]:@0j/,WFbLL2xW;tZp,0]&ʤG_P1[\b9&U;0"!Z:΢͉(1 'Ng;[Nz燮w,MMG﵈2æ'˅"˕#Ց~^,RiF^zSx;$^wr'X8^.ٽݨ˝N~8{-^.jLn䡺Hjd77:?BAGy-T0 1*3d 0 `/ ktb}tSL#i/D2rZr%*npH ܨ ,8!;khNVoƿTPӘ%,JFpGN 0;F=;"q$ QsC0rҒ pʺL5k)%;_`B@RH~d€ \aO^cv5HQmP̸$ap$F` S$Z!Z2r wz]%8PB"̥z&O6o;Zr,>>=O(rJUjLCQ 'M>B,9Yf7IJbpE F%X`7%_j )r%Gā7+'E&3htfUS"d氵2 +̂1iPA ځʼnf`0T9iƎFDcYcuGYT'#kF"Xr5P+UjA2,܂ j(\Kc)$E66@J㾬0gH,-hB{+ͱ͸l+g+&6p܁Pf=pcNDנf[i4Bqy۝FW@5a/-v$ d.0WiUȄjybZXe UhSՃZ3MRNJFe\#h>#Ac ݴ%((QF&)a[}!y:٬씷ú襭dq֜Xre'wM 1PLnD[((V =dSKvE Xӕdؕ]S i68;+A_~-;NXZwnT@G&+~!Up.ZlTh޳ {ԼPڰ@ < M! K,z ;x |.YXYRFA) @gDAP) @ #<:alD-  @I QB1M? LL! *RXv$H$qg6)Bؔ1~ǣتc4-U TS _% 0Ӕ 'z5[ x_s^ؿ; Xi T֘[U;GA+PAR%ƜQ XFBPckmCDP@Ƙ7_@P!j yc5,Z `֙ 2cȆTQҡ1vrH95U:.14'C)0cckS-"i` T hQj+D-jVkvVgͶp7Ϣtk,&cqq%(X,4~D蟉r3nm׈?S2n%2{D/UY*LѸQkp8!?V@Z[F!3QVX,P@e24"BbIGQDrdm _-9|A51obOy/a ےrl_Lp-K:U,Rq7q\@:K\9B 2=!LUdHnXbB~d%M݂}KlxhJZOFbpY[؆kjLl-Q#*GVWjWTê  ⊢8bAv2kx>Ǿ: úRF*VqXE*wX#fIy{(⡉ a'u -aaï{W l1'u -aa˯{vXGA랬GAlUӖ_d㟍=g@luO68sYh P^J2z$ XG1Eʆ @APW[ q%lb N5 /gdbuyeË 3$fõw' ϡc^CK]0I Kv%2ٿ.‡Kb x4 W(X 0u32]PAa0 p I.:1yoP~ " *ƤV4?Y0Ĥa;3ρg@65,k.#g;H9Ip8lX?`_ C:W!0XLUg*Lۂq9Q\s0.cEzj'Fd85sR |Uh\2F!X)L׫XHC:\Bץ21Crq2%>r_bC1Ƶȵg 5wk!U n K.IX8\vҘ)5`)pm S}U-XwPuGA3A( ~e0+js7J+e$qeW (5tNVu0`Qq`8c8 P)ٍeAX/gSg; d YvD}ZD ?]0KXQ)S!"D:Xgϳ87Ye0UaIq!θxD\`9[CV ᮬim%J yNp̵ $#";YbT1ef$ Jna~B#K=7یe Fs4ݭ㏖Ϸ^__=l2X Ta=+8|ր"62ؤq{Q+V2\$^e^ _zu b:+B_P)&it-4_Z8lampZ?ol(C&㎜dlRm*9Z`#H>8)+b >lǣ«Ip6 |W LDvǭqFbL34q7?Cq4?{ܳAө琺ko&o'ᘇm8ed4#YpQ Aw׉V|g`J!1YN0e8dwmބT q6Eض/{H~s̩F(Ċ/;ǰPuie !hhv!pf8UUu#llR8vZ F1߸.j]8T) lýc8YYj9jln囹kv6nH'8`]#Ă'LU[{r{8lݾ@w '-% sC}+wv׮Wj|SL1,x1HvlXdɘc8Hy_EĂ'sI/=+kX4ЫCޮWÂ[n J{r+duuUtu*Igܤ>?`\\-8oca˭@S79I|[]sdr]TWJ8 tpqx:e–+^Ű.n/W₼ŦѥNN # CeخoXqhan} ~gX,L|Xh3Amfȹ98M>Tl@lnJNx!$GJyov7.Ѷ"U +*gV V+xٔȰ5.9#boR`/AʰA_$`p{Β9̞)4(/4TA$^& m$  ֙Ȱ.#b$^& m$C_{Al3,|̷ӜNkI帱Ѧ&Q2f{ 7#PNs 7>T]zq9n)k(TiAw ƫt@~<%MBH*:CfRfۂ̶!e6(&,G_V2UA'4ScRĉ :| 4<7qL1aHu3!QX5zT)|N}x k`PBt^c>2!koF'}ǀ"dBYwk6Q _AF\7D힙; )05.LMT=FI}mAle߸)Mn7Q>8Ȏ) n7QT0lr&03tFNVR,i)ĖͲ́ԝ\)Sܝ&Vӳ\PtMB S)SvJ/o/R!&{;TaZ OBA6OVعQ}J:A}8A^vJQ:ۤCi*(urwW6qkb'jp֖Q>cG,B{ZҭX7pճ됉~"Kg;S]x;6OXA*_%DC)DZ+Ո#|p9Ak͈~+Tt=D٪qfW*lJDn38`*N !W㖥2RI .~"Q$R@#kM:6;(:EJl&`f;(dM' 艘ڌ鉍\>yt)~_g+\Jl .#Wxb¤ֽ7}t+9mB,:OW0ؑyePeQ\bN( 1)!b"hhRB 4,Zy4*- k /e=~R%3@Jh( D `RRvUul|ീwW Gwrs)dH/&jU8)A+ ݖZa0f,6os&f<+ IA)LلtF),XÈtMNx\308 g$AOVMh]_k-\NNP%s((QDzHw-;| 6 S;AC%j+Pڭx\Yޙ6Y8ͅQ*X\'FilOGa]2.ü!OWDO^ǮeG"܌sƢt!Pl' lv(e!/s(ݳDtVs[#j爡92Uhb Jו5w&VM@X>s Y{C2e;Mx6j?F{Ama2LnR>mAZAV%h]@kиlA׵L< {kkiVO'AfۈZЬ=AU[(9ij5ܿhzFh4\*s oly,=lI{q0s7dɰꗉc@QAYHK_E$Œ`Q|oOvϟ_ֶP?;߃P-'܃1PAts!ߔplQ7À^tɟy{eeRiP1R> /.Zu;CT ŷ"^v}`M'K[VWps"B[ᖷr\U}VZw6Fx2b&B* Ҋ_U zHHHɳYcI+!{)Gz 86(,/xw-*V Y $Ryx݀*(J` '*89NTё|r6ld\%IƾÖh 9*@KЀ\:$51I`AMwn+;וd X1'tZS3iJkJQ:)K)c34%5FNu0Y/;6PKC2kej9 Wp&=(]Mޥ 7@|pQv =s B;m`c(y]P/$p⦠3?eszBpk[p9T=l;zq'v*J0vt ePO( uPOH[{6&oֳP&KCPO*ۈUU}-l zB0ʫqI"S儲^&tLv6WOPJj"];&x5A-W))'LDrPv_۔Ϡnl=;g)Hi' Ofx_ h^hYO~9<ݪh gDyzւI`'Z I&4zYh=$A= ePMȂrB1PN8TO(OzB;`•'k_OH X<,-DZ^>,MH7yiIu-'w%c 'XStdiPҦ"@ҁzZV=!H=!'1zAfZ6Y<MQ'Z Jw WOʯ[P8gA݂B}Z܀i@hPB>TP4P۝kw,"T7\vV ng\SSPŅP6V"-.q > _6mbi̧n>mt_]5ҤP?i|-OkAna?P,((⽂B{\ M8jϒ$ ޟ>Bؑ]ٟ9$Gˇ>>;g+ n=#lODuWB~eOܤ)  cN_J!Qw;(a `̟J)&"Hqgv٠ YP6h|٠US5(|ՠW sE4D$LYxgts sh̏L$ReIbE _4kH͠ jTA͠ijdWEݹlj>棚v fٙjP0?,Q#[yigTޑE#e5.1LV ;+IgcAH[GE7%D&OM`]48$(s5.VQreW$^4ؚ,g+ldB@\5X2(;jr_K{7\bH0v+ U%*^g޼}qOs+S@}TeRA]o J{ć[*8:P3L0B Du­(:\l0J)nh%,\^vh.܋!:|H_'R~%ثHU<iVzTYE+qX;wJ*kX{EByvSHBKV*UpR-"ŴHQ;u`{T&TUhF؄D= pp8k$&0/ È) W# ¶VuXO\$)8kOj%֩drdyt[w[řNB怬a8<æZY%iF䏪B𱌩QtM2 #7͸n 'j1%/&X!o d___jLRc LvTux)k;VH)= n=S .W` L R8'lhrF?аlPCf24pl8>ٚVT@Kq6"XS *i2϶C "Ïs "-JJ_5KB$f!aڱLRd֣J7ppŚ8 d@ 'G; QSn܅hIu~TR& |`Pv Z-&+j2F#/dZAm%,- )ƹoҜ P~$ ع85+HdxE1XkTcSATcBLߪ c !F:œœpVZJ\e; ʮB![, ߭/WG쳸0H#f+'^Ku{RIy"ݤ')CMs 5C:[3t.S3^\1a& 'dٚOT1d8eC:|Re C7sH$ s& ʍ,(& 4ihbݤ!zT' 1i9C:+ ж3[ Vΐ9C(!g)C KL7eH}x)Cd ֣! 9Cy9CGAr( jimRsL5ȏ:!a( ۅ00n_'#Qح\tİncW0'I0<Ó;grGi݀(P`F9Q(3k ➽&i׹0[&h\p9Ah3Z-4a_4p<4v 3[&dL6?xڌk 6odOu9=ҩn -ң @5ZNw7t-qDu㧕k66k[8qj#Dդt7cu pðZqcw_I%嗫yYf[j8I'QLדʾ5rnu&X/3Dž#W a濣qt/r#2F9dӞN' e6& Sr+I3d h$/$ FFjǑ8Bd!I`5uic4XqS@VbpTaDτq)Q)h$7|IFc.6 $GM< _ŦÛ=| LL7NtBK,]l ^ @8t3t!h܀$` ȕ yR5Ә\^o7/Y6 p #.`jԫk3 `m@k$᯽XE[ CZ D/fb #W@ZLF,fL XJDL[l "bYAD,dDb6-Du&o6:bV-8r $DĞΣŬ[lpJxt2"1+G]abRѫXUg4̢]\=tsSK#]\uR7C43?z10ܢyGx ķQczv͢D5ҘI:AUj"t*e~dS2 ͋*E2H#q'p $[@opu9kɦGM&ڟ(ȳQnT^Zfx*8וRfJ5t44ZpsO%w#AGa^*2.{ ip%w3w$U(NRp22 + TCvmFv띉!%gknijnբz6Q֦Xsڞ3Qp5XZpaKl|5yf8N#Ɉh[[htD{mIT`guT9uE%|l3ܨZW,]fQ]ht!*~":(x v{p;OaPpou j+Y(%gxe9㏊!9Nú0DXWY27+AB !4!FCBgk[<()[(U,˒3P%ֿBS*dXSC$OU5WU66Q pk E*$a4*(d;D qK6Vp dΰ Ӣ ʸ'6<1m^}~ &w&Q*_5NEJ9rVK91_x=.$s 2+w YDPMθ*=g50~1!ݵEU,s>ڐlRwK1&y[T6*Di r`gJp#Bt.<瀔@RեgCʘi\y*#Vw.M; #U<@#܄st"P4o؝byn;ZF,Zuw"!ǕC@@LZZ$ ueԝhi P0s YɉC2m"oh u[ҔfM!hRusFHqc *b8rU(Ap, |`8׀5DpՓvJܺ*7YG(?l5ܿ0Gu+єR9ի7B`8O~`&ԟJGVnRc[^Zk*ir#ٵwèɂ @*ƾNaa%üF6OMHu!' @r0}0rSHO-S=bm%3AQ/KPCL MxiE~]hUX^Aŷ"^v}`M¸A}d|.w.Bh+"QRh^ЂQpKBh;9_RFK$J`oX6_U {HJH3c+!v!- "Cz'Q>KEDG.6T ?7^M'aR ȁ(ڃ *ރ#Z?g-4ܝqX .})ޭ6,*4rF+YH1!\"={Qg$e+N4Tr!ۿ$v{QUTJVr*Jfm1*ќ@$ݷg*% D !JQd1 D}kOWlT8>ze@x\-lZ8YS#<>om$S+3+^Z`159{Τ|4J=$SRW ~S`UhW Hzt7b DѪ@wQ8E^:>++elc{ͩ^8>n̏ުQEez)ܤ)@$ ׫X~"=o 7?P;?4IN;{&QuoNW cJ@> iLATQjչk ?.Aco\ⱼGu!jcV݌sJ[YmQ)b5imJ'R|1+F<[9W_H|t(GlM{V(rDyiWt#n^ػ, o( j5혊vPw[X[\_Z2iOs!Ү\Rb`eC 6;S} ܴ&,Z_q('YZ@g)^!R(^rL.1Twq/QZ'!^|qXYq%.!uٷGk%)1K*^4E7qob5řbybQdQ#WCc`[" 1W<_b!8ȕ+լgzp=W@2;E_;bPYPxMVFp \Y6Qpa)mH *$Z`iy5 #%pI |oW |K -ķSl8 D A/\qW & fNj"$Pn!ZT&U BɈ(;DWvL3 "XjJF ̖.Az@h(B9bv۶3XƥnR*0DPOBMay! FK/AtLlrPnfة%ʝ9?ZY2r wּ!0M{4x4Hڠ8TdII|5mK \NXil%^Eg* 25R4Bgsz;`=)N#WV'jhk=t{NBseoJ[P6dHW/~Wn~%$6W,`Ŕ>]lUi'E'(N#@`Ob ?@胯KeYiެ<I7y o:ւ,r0uf92w1VManMv&) S1`A-qUHޤ>qf rB2ZȐY)ll%EWݸ1: 6?SH8c&=X{jn@*0#[y6: ͦo Menɳ֭ڟG?2̢<Ã6sTժZN.ϰXze`pf^) x7@x}Ҁ;@]-XqT^apJDee#o 8:rDF"m!;ۓ˿:iU_`GY:ث,q]{q)yl;FL&uBT u-X,Ą>:7 .>ۉ*_Ta% {Ra5tXj9<mhi+tDydyOtnf{>?ąm=4v[}ѷxsۣ2#H' ܟ8>U:4}?;G`_ܓBXfhlO!OIE8Ǟ UT&Sfe=AejU*ɊO"l)f\2[}ƣb h1$1 6=l{k Z41eI `Iܙ7,8 a]WD92+: )R!|Al&09!pA@s5re޵=~UH$?C!8@pQ+C`ΐc|C\S1Gѡ+tnBnR2JK⃈Z t!_2l˄TDlF+n"nr 9GBƙ?1M6dt`N&Vr Ɔ; h$v7p#S8R?SƦ x3á ,1ߙMlr3OyؽV)>1oSrnG[1=U{SV637WW54uЊE K|ZrܬLҭإ[@ۂ$jbvg&־57>M-.,H`FZ *=R 9$W17z2x=*6*ٷ񐜠j||X Ʌ'W,o!drsūJ7[Ϟ5NsiJ,!|5Q•sWƯoi||ҜS#'͵ W`wC4[ ^ErX{Va< r~䶼ײ^jE|o hvP;27yO.tdX;JIt0ծC*eZd- wNlSJ~|{z ~XC|+4I<.jmæ"69MAS@GϦ{n3nbp밙c;.W.)ETj 5 yLs7}Goon vkH7Edl` 76BGK+uמ3z!IhӢЎ$%[_s= ZybJ,&LkZ0pF|:_t+3_m$//䍯WpjPA|{^4_2_n&7yR6suFteO|S`+-#xB||$xZ2$Ukۙ kl4"C 4sv#W%QX}N(nc.KԗM{ju]ԗ/b@$lR0fã(tvײމ6ģ4 S!0F#nl#ҊBnUwD,"-(/4P> .VMVW/ d yR-ٴuZnR@lTjR+6⳹^9/N ?L|o߯7V^97Ӥ?脅6 tAJވ|oegaa?B|FDt?=?|*KNJ4;pAB?_.  |B} o@+/VהӛLTympT|^eU6%fAOƧG[ך .d:2u? 2AKrXG(-sـ:!Ɔ()6*` +!~o8]|,:|Rp!ii[Fsت*OܩU< (v)!KH8x0qj^jUkXLyI/N%~õ1ռ`_ -[8dQdi["ѭR݉!]mCU:)v:w[mLZ%a-}ӄ?3GƤL l+tr5&^P 7ba?M~rP9 K2Ĭ@R ;@tz8f+ rx8@-u _=KKDe:eeB0IºT܅[<زRunjV,I݅%vbV4n`!@t-UVwD ~-wF5OrqUD:nսBsX;\`Y僩OgGReY僩x ]r?gX>x~J-w"@6U[:U/NԖ*lݲ!` [Q/+j`yuu+S` b`)$ CU. -RtKJ4/ưJ|+T -ªL±)U^//uzV沣.C̊-L^= P%=N5-Y@z -"t 1J5fqc# [(Du/4; |3v bV,n$unBd ݒUUTiV6Н)X@[d L(ԋPN4 -T܅kXy>+hx0uqnP{* -UT^.,?p-@[i@WbDQ[go_ϯ߾o={v_||7勿O?믾_o޴x_ճg~7WWWu~z?_~lw}?!|yױƛi9}?~OTMu>7û??G}߼z~廷Qg߿xwy~o^nӘ7Ô< W߿{-޿~o!g<Y~)!Oϯ 7 )Я{?~^"?_ۿVAI7urvMw/0O)Ƴ+W~~~xr1&p02Mir]!z~]˗/E|6:c;1m};xO_p3eWa/?ϫ,\-E~F_V^b(/k"_k|ϯo:yym{ߞS?q0ۿ:~6kd?^\2{\_ 5q6sģb..q$ vpqOkCsOE)6L+7'k܇bēb..q&B?Opŷ^^,vqgP/aVh+\ŷY|+]<% zZ\^7Yl/)1@WozC{5| G*tqЫO`⛆ bCPc1BE Q!bE tԎ8j>@-ax*KR+vDS# mO wAEbQ jGOL5DojOQjŮ(jP'AmA5>赮Va_ ළ5Ӈ6J5LPYwAUUxpZ ZkN5p֠ O[+֐6D\<ms յRu 3tƝkP'-AU.Gi`{ 3qڡ\%?A/'vH_UIU՟>襾vD_ ׎*A z׎k#}UU(AoWU}P_;z%56׆k6D_jCm} .q˔܉FUU]k5;רzP}[_*A}K}m܁Ff;רJP}[_j@}K}m܁kN5 pר P1׃G5tt‘(lT=~?P8AFՅRU_jl46&?ب*Q[ejE}KU'*U3JolT~?8CFՏRU ~ ?Q GJRߠ"a/'Z[6HkUE Vu>6Dm TH!{ުԇƞmz#U%)doP\Ւ=QQդ措(uaN3v3w| VU__jnO> vð3 U~?1B措:5wT~?1B措A5wT%~?qUJokTRs~5';Qը>6@s5'; Qu>6@s5';Q>6@sjncO4wT)djO}Kmf=\UBU YjnJ\s39\UOe܎hn#U)o\UͭPs;~B ZTRs;~jnO4wR-*ſ*R}KN܎hzT[s'դ͝FhZT[s'U͝h*T~[s'ա͝"ƞhS~[s'U͝F=IN=a/5;MPs{N!m͝Ty>K͝'57\s'Uҟ͝T_jn4wJPs;~O)m͝T_jnO3܎UJ[3OYjnO;3܎ijQ)~"܎hn#U=*o\դ-Ps}E jTRs{jnc4WUAsU^jncO4Bmܤ TRs{iM<57ԇhn Ps{IU&U/Rs57eUknR*<ܤSRs;~)Rͭ\sO)mMB/5?ܤ*T[sjQ}K&d|TJF}T_jnOV}?Q=*ſI5>6@s2'T oknR5{=ܤ &UBۚT^jnc47E({IAsU^jncO4W C#U)do\՞UPHsUu 4W.i_j'j ~);4WKڣ?/5',7hP}Kj6ʂh*T[sgբ͝t?YΪH/5;( ~Q)~ÄjR}KmɆZ2G#E KFa/5; ){P!m͝U^jnc4wV3RDsg՟Bۚ;Շh Ϊ<5wV{=YI͝Uu okSa[YJѯ{Uuṳ*PKj\ŸT[tgU:U StUJLWNvDu̔?R]դRUe麝?q]5e\WuU_nOdWM#U]*o]U:jqnVM*mYUN6@w2'U onVE| >ݬ fբB۾U_ o7*|"YfURx0`jj fY5 Y5NjʄoVe*¿YNjʄoV}*¿mYNjʄnV*¿-YUNjʄnV*¿YUNv@vZ'UJonVu{=q]5EeadWtU^ngOTWMQY#UE*ao]U:USTHuUJܢjT[sP_J5)* {EuU:jnQSTDs*P m-=ubAs+ܢ,՞ۚ[TuVEMQY-;%5ԇ57EmQU: ܎h2G Tߠ?u¯5'L7hPk9*~FE ZT'Zs;~jʄi*RmͭFu¯5[>ܪjT[sPkmV5EeaO4ܪ T'Zs{UMQYͭ@%5ԉhnUSTDsjO mͭ:ub5[=ܪS~[sjN}OZscƚ[*q[U{NOk_82nUSt{tZ2%@L&-UN h ٭jʔݪTVե:%}'|]*SwSi[UꔀVޞPުL 孪T WN hm UTV7jUkmq* ~$U ZU'}~jʂjU!~V ߆诚GZU`Uu¯ o' \;5x̓yp<؃"(m^dB/wM@8 {!l+r's⤝xk*="e? aۊàZX/7Me@x lr'3͸%8 jʒQ*܍&eL妩/Wz0m=ja~~4*ܐ&meLVRd5qeI)jaaEV=OV)PdtA {YULe"6)=jSdU,:Y""+SLU-KC(jfBЊC@L! E!9v)=A`B@T;KC0(rPSZ[DZ²d9nF`P䠺Y""5eI)rP,LAN hEn Ej˒RZYAjeЊ 0%AU0"U:%%@9y,KH*daEI kENWVV#T%A;9R)=AeB@T+KC0(rPSZ{D29b`P䠪Y""5e )fiEVSZ{Hz)Ȫ!XYճN!hEn! EV Z "nF`Qd:%%YmhY`YEU1딀VRd5eI)jeaEuJ@+rK(rTkZ"GU U%딀VQ&, E 9F'y"G5AݡQUEu A+r(rT["G U/VQfL! E!9j)=QmB@U7KC0(rTSZ{D29v`PY""Ge)rT,QuN hEn Ejz˒R䨊YA*fЊ %Q0"V)-j˒SdU,*Y""1.KLU! (jd}Zsܡjk5\(d3i"R)=jSd,zY""jRQ4"u A+r(򨆺L! EU7KC0(Y""jRQ4"u A+r (6, EU7 #0(Y""j˒RQ0"uJ@+rK(ƻ, EU+ 0(ZY""j˒RQU0"uJ@+rK(򨆼, EU! 0(YȣZ LZC<CgUɮg $q抜XjcܡYUU)VQL! EΪ!9^)=Y텙B@U1KC0(rVլSZ{D 39n`PY""gf )rV, YճN!hEn!Ej.̒R䬺YAfЊ V%Y0"gU:%%@90KHjeaEΪuJ@+rK(rV[a"gU U%딀VRdfI)*daEVOV1Pd5(d3EVSZ{H^)Ȫ!XYN!hE! EV{a"b`QdU:Y텙B@\T7KC0(rQSZ{D 3v`PY""5f)rQ,EuN hEn E.j+̒R䢊YA*fЊ \V%E0":%%@0KHdaE.uJ@+rK(rQ[a"U \T#@֊2N^\dy~X;wȔž/2ML~<;yr'ɣ=% {:bD^gzz wtoc?buɄ] ;8\S{kPU}xOG,YxO.,Î=ၥNxOG,+㽓G;UWG)tJ#WϞmG (a';OS{kPUp1da>O;w:4=;LjQ#Lɣ%uxCI[#&L~zxtz wOryd"/3ӽ#O㵣'~+G&xoޯ~݃}GYA5}FuqxѨ}yX)#zyw䀟QG/Go8#6l zy`tz x<`tos?b];8g6[]Ǯ~נz?b=#> Î3!9ZQh0ͧCE6?G<*:g<^*:o9)їݳOC}{ {Pto?bȆ^=9.Q'k੽o8X#2q㸧cDa=# q 8 щ>; }q㞮=='C6{9ɣOo82qן} aLJf5O?xl*|3dP3:g=:o8C6CPN =}COSiTGL:yrg,tq2q/ˇq. i/ /LNq}qĈ>G|Bw>aΟmGL~ogɣo8X 2q/χiǂޟG~}3A';Ag-A6[.==^: dC I2P'{< t?b]LҎ3] iOާG>x>5}v93% }}'V};@w>agΟmG>73q7GL}#? ] sqڿz刭}}w/tDO7刑}}ܟ}N==ڳ'>6oq_O'Ow}>>&T!0U>!4U>!S#|lqw|1}v53!}}gBw|}?OsU=9~O'{ 9g=9o9X}sxF> >+~qNx>xq$ ^w_"޻/Gl_8ww_ϰx}9bFq߻xg}3<z1cCW<3+_{ #Î+ {޻|w';6zwd}œ;a{oܧo8S #FGvw_U[ m'AG}mWU{Րp$1!ji|{{Փo|ܫԇ>Î!LqR;}qR|&ۣc={ѫ>$jB(57!u?f㎏YU5ִJ~1z cVUu>qw|̪&}vܫNrܫԇ>#UUMGWm)x JkǬ:zrܫ& ytܫ pܫԅ~>N;޻jiwvB/Wx*ݟ~i{eN OegW3NGWm)xѫ>'rTj'}TQjy/S ~>&rhӎ`M4ԇ>~iji{e&r2[ݳY'ۣOثq?[R;`{˯DB;II*cd VڌVM+<ӎzDjD+ӷ(M{br쵙lzRt$5d!OR3>ԅU,cV)UeǬRP=:>f\ቜv:'}PO&+7A>q_Ǭ{b{z {Ֆ'߀vq,qR3xqR%RSM8//> _> _/ۿ%Syg_Ӵ\'sc;~?|?Ë_ݛWWcᛯno~#nAypv=dnwGtv˞۝z;tG}]gt l= lft~´B=Ivo4m=AXq>A췻AGʽ_b^?qw췻A_a+:EݏZ? /?ow7pW{h"7G{W{#Eݍ6v^ӑhA_d~}]GEݏzۋ_}__|ՇPKxE-P˧D)svg.breadboard.led-rgb-4pin-anode-leg.svgWrE}vbX^;e*p *B(YX*:%qpU3;sN_ۧumn1|{\5nK'=vCwi_LN?t3/ YNӘfy<>N앷nX842"{qhphh=9'cޜ5n1''juq\bd_m\.G$3jqV6gۜgcԘ'fuqּ//^otu~l on'p*sc~^gfY4ts25uljQΏloj0olE+绳fmsHO΁üIHdwѠ}ƖRc=x!E!U50Ւ9j,ҹ/kCꂹ7 T,NKKsg5 >V;9km>hN{q-Xc{;]>XWA.( p M4alDEv ;( _^`< |Y C-DHD[9$t=<aXTR_‰ :|0_n$:qD(QNuy f)wV㤷NhNdFF샙.bKb}1!l8Ӈp> G(k5&[M:q]. ы$\$X".RUSVX!+V~zL3/s,7f߷EDL+тͨ_cvxо!,|6P'p6$uNHN7#XgQq2 fk20q*4ek%h{b /8^j0 zuopIua*7KqF ]p}Jըr7tO*E+)D*l U8AT¥@/iE8t 1D{&t/.)o*7>\\슆$NeOۚ?`Z ќAV` ^4hP-ߎ "oT.%bZDge5HwǠ:q<&'`ZEk,kH Eq=ҡdGjDO+A1}]담6TvHqI0 iݎ&p{kw-dNDWnء~AI=$ͮtQRwK"m3'Ͻ5_-E,EҷrZugwa>NO CןPKxẼ svg.breadboard.resistor_220.svgVnH }BP^4"N&-!]#+dJ%v8!UbxMޮf=o'7ۗq-ڧ.fq_5;eoMݎkTrΧ,\_:b`U_ŴVME.{Žx*v[<HKgVAZǮXeo'MW==ۦeD@dZm8"أijb]hwEfwsX{3$fƸUb, ܔ̀0~N9 p@HfoF6˾zs,jPZyKxtKiW\Gmp\3T™q.OA3+U*2`idd֐OT?bV> > UIg‘%DžɈZ5\|<Jϼˇ}2"80/sHցD@ ,sXBQtۥ?K,AHNG&z2Ɗ?Tt<^-$RKq2&8gh ykylsѭߞUэ`J9 n Yf,.w;VX ؃V,A@ J1-VTv,p9tE)@|*,v!$FҢ  s,QYUOV!9 AIX=a(Cxxۄ"%m, ),Ȟr2lztG,C#!hp=)g{AɈGVICX#O1 w{yEj`Jjn:8$߰d6( ;6 W 5*FW`KMkJڀYAα2Uq?)tdK!\p|ƔrD $ ŝbNdl@+'%k-WFm GkT_Df~ k,^ƱJ#a?=ZR:JVq T<\z1Yf&I)gP&QP)nЫwʏ4@$A;, $p!?9Nfy^uy]x:-z[Вdf'zU@xi+O#)\S\ |1@@pRct _5\PKxEie0svg.breadboard.starter-poti-small_breadboard.svg]Yo$Ǒ~m?yŒ i,Yh/'i PIqdzGDY%7 hgEeEy͟~y>o~{Ys?~gǟw_7뚿맇͗~84H 7=Y/׿||8>5}*EYϷwytB>}?B0. Ǯi>ݾycbZ1y{Ͼxx?}<.~./} ӏbvzsp:ᝲXW]{|}o5?\?;>x{R~ܾ?yo@ٮy{ys>LJ[у\TbaX&>DN?x>Oퟲby%mp; z4߳l1((O !KDR?><7=VHk=z 61[x؅2>i.N+(½!|_s,L0) tI%rՃHTO e\DOElTF%8W> sJ p2N^![-$6:LdDE90㛋Z?QX˕G/?#ڈkRi>B AKzjIH4%F4WzGvD8VvDɶV mrlѷNΈ٤rgyy)#Mf0*eTJv╥YJ؍P^MBh?io](hj}+[Ѡ[ I^ &\˙\~O":R37J\?wTd~H m%n(GbKᅆqj>=o9|GJaX# !򁰀v"; 1 Co7Ґ| ?ŋAJ80/nN@ɘ 4-\TF/{GłFY$RBl\[`FA6k(OPQPa X6`&qE\|FAJ&]Ns Luqp8AS77G9C*\N=lM Pϖ-ff\92V'777FSZE6eV%25 {aVxN~<^!ôF|+dK +Z/*͊xg,B̙7`2\)!r64[ Fsc62%jE3U-i,;C i1^ V acj(hA]+ynh8ȑ QW4˜ΊBV+wY axg<%Nܴr-j&+ҝ߈؅p tVd"9dU@ ʎ YB+g֢D^[CA00YC/|v7v0_9B/9/{<4FꤲN&9 o N\1#>!tw )9E4m.Z`_JY^ |gTD5w[C06EЪw.L)XS2LJ9VAXV3;zXA]kҝ Pപ%\j]zb;GƷa9>NOa((lMs̍)0zh 5w2-%hm,6[ ͓`y. ^ ab,(lMse,ыҖʬ" [Fc]qW;ay@ ֶK_{ɚ|9٪Ú-)lMs Z"23bc/Y7Oګz7dzZak,o),].'x;@Rؚ|5"˥;ZakcfsD֍H:-H % lFscrsG'.ߧ /d~ @h6fwW]t_ RȜ/3'w:7=lᚐi?jrj~.ʛF0Y{ up[@pU݀)ձc mD36~R#xMkj #q39$6ϽsW_oVWG3ݪY\\.ώfw6q_{-/۫oW_[ Vo>f緷/󇇇Wjq3sYLt[@O=VG_ˬb#\޼0)˛r݋rRJ=򷱅xKfoauz{~43$?Η򋷾_-^_' qɗedu\>T,KH4^/o vX-.׋Wuu! Ee_^ زj<~Xme߬ZZd߳5|kbUkV`-z4[OqP>A(N񇃺qؒqjuaԵ)A8m=4 txg%)j~=RߙAS*33Df&TtTqKەSD渀U@'_R'jh`w)&mrGU6'8Q`&dew*8Ql*\laiOq2c%Ӆ5{Z`(:xf0qOYQ)˄A{:Ng"j!s}`"ب jHؚP`YjD,VU"9 'OyQ(V5RLכFi慕_,5^h,5=/:杬u5{Po;g"0K;z"9P~|gfSe|F^\6QT3ywru1pٸڦkZy1NÅ]_m ;Mb̈u/1ԗd~1iŌ=N{ np}_^ @ ;KANԖ6ؗJ{%*-m:,O 8L>zI 8y yJuN.T8@^™Kܔ<+;c@&Ĭ!Po;S֩^j&jAHސ nXCVېzysULKaT,z!o(|F" X;D5bҞ5QfWj:6 %HOkrKseQ!ڟ<;qpΟ~͐?q~ǔ7q .IzLC緖ÈI{iƏM&LR|k64hh@{]Z>;۵|&GKpN޿~$wqMY93CPA>:usCPauQA[g]nv>CzMs>|܋1Hf(݇ڜFL riDwWkFL9yLOTrTCn (M 0[psύdPc7l,G Rn(q!-D! '3s5[۲բL!dQNW}#|މh@aUʉȧ*ckeb\Jqw%NBg/ ^ȶ+ʉ!#dѡabg;}4e'OM.%!t4 +m>sPϖ]9YSFqBE> IL`ݹiD( cm9J@zA6ȠٔحPyhDpAVؐMd5Tq\9Hsi[5$6f# m")˂+(*b /JиgAI0uVKD3 m#ZR 3 CXfO3M{ xN6<$sCGupE4P(C[0:+rjd'Aj-iڒ|~+6ruΰ~j^\˽o-*cJ -9b2Q!&!.%\Z` }̽ l(wC#ctVSGh+EB:1:Td@|.$S(r0".ngF<\*H` #@낕SL7eG2:fT{K"FG#K|a!@MH @(&:ws0`.kPBy,ͽ7Bɠ L$,gw,.-)K܅1:[XH݀e0f` 3DNa[خ=J}/Yz dZr``]ad}OݹD757:Pf#b|,SO}_PKxE svg.icon.led-rgb-4pin-anode.svgWn7}v>~16@R}*\IJ$NgZi&-"!9g8Ϟً|VQ|9]ͺbT5z7M#^Ηn9VūKh+TxsR\ܭWx,WK!8A*%tLh]N?Bl WcQ5msnۋ+Q]vӶV6j}M-4Z8Zv !lTM7T,G<y: 9 9bRJ/ONm7]*kz-wCkohPqj:U|; qr`tjw-u}?McV~H2S[3Ӥ-MNښI R{}7~j:L2s~-mBj+sFI1ؚum _U,*:RpP2%Ӡ"Nzc1{g \AC8Ll;c;mvTHK~y=H;:rqy<\t< PcPR^~ *AUyP5 ;Ѧ]OֵF.32 y@qMV-6Ac]l{i=k>}oep :/s^bl!!lzS Wod)0 \'8n?^cTb @lc%6g0M|rpS[8. 6ab@+acZMxz‡-l|l,(9= ˠw 85Nh2`%衡|qz 0*V< C8n(q^pps̓VHE" fPH(IHMΤڞk҄4FAQ T]%CO횏nҨD=Cc T2Ds<{JÖsZ5tn*K&}9zoiw(h=j(#xQCdMڒj*VY/2d`"FJfer(zkyK(1tGQB3ӆg6" |I{@X kݒ^Aaop6rr`ucvijIzБz)V|2tAm6*Ϸ&'`2A0+J8>K=Pl(Y:c&')qS۳obs1x. }PYtoPKxE nsvg.icon.plain_pcb.svg1o@ ʗ#];tjv[:ra i__*vl9H=~Nܼ<-s4vZj޴ϣ\MWjjiѮ9wiE<.Ç!#'[Lz cwxW˴0%TN#ͤyOR|`&~GM|mfY.ǗY*̳OnQ/Y=}9z<DL!jzl2766j*m lJ^=Bd}/I4Gx1!@UQu_EtTmC) B1Ƥn< G%!HEցz"<BHAa4X }RΔ(rxrO5c< {q_fL%$I!&41p7DRG<%8B2MF%l5Z(e"f0N-W; re??PKxEcE svg.icon.resistor_icon_220.svgWKoF>ۿeKξ7$p IS$(-U$Abd3E9Njȝ=xqeYF)2Ml=oVQ}Oy|]BͲJ^Tj;myELY;[V]׫_ڤ7mf[Cz_ﮒ~qEç7 ($v(ĝxnf+ FJovhj7zYeIHYdzvlV? !wy4yy[R%顮EMujM2Q:ivm 9XldzʹͦYRr&Wh)ΪO*,/EYj\jUSfYf%3+HievO|R񝟝]kaxL&B W DK/d2#~;PD+ N.cIG|%uiS*b݃B5F_C)M$cBP9a$K#.+\̈ #[4]JD9rk,9xTP (h;SKuhgIdd`iIf@!g9gl遶Ed>?Zһ e9GQrUˏq4Ld!tN7FKjr><]SjVRc*W5>s^^`pla5A'0ٵ=X1Ԭ*Zsiΰ?Еq7(nWjՈ~,6݄dv|?v*.'G!XGZ~ YMɗ˶,j{/ei>r|}&k MS Vw<}JͻovC/c\WÛp8=vW=W|y(<%Xv?GlWKɅvn皡἗kUÏow;(o~5ww~n^?N\.OSwxӃ^_Tb[^O1JA hڼ~4;쪂VUo|w:+e;KksE=s.4hv@̴wN6W<(l}dfG#c +kstW@t[.0r[@~X9=J6A#1hɑRK$'%'J<]CveKqe )쀹c !E*y$fFDTܴ&9;3x9H,mHodJ5첔cV{E㿧ʐw\|E}-~@uѕ1[w*Yh+#z@0X诲9odݛ:X-Y[ 1… n…P q? (?{#tBnV:hSlƿe :5`&ˌFFg l/,8dxxdVu,YaH~@th[d9rpQ"Ey8RO[1E>fNM|ml̖kj׻]Ejbx|hˢ{ Ϩ՗X~w{[Gӈ^L5W!uKވw4äbmsa;h@N7BJ߂47`^M+3n dπɰ۪ Zw%P VlE2;;DZVFYY3ō6|T^CUaJ]e2 3 p2 n5Nv>^?n>@`:pulcw?vLjPu0p8XTC=zK-R/=u=Sg*eZ|6>OXVReHm.?J AMY!k!Y}q!4*%j5A6P%igrKK>w9x [ 50ʁQ*0+<v¬%aD4 m!&{NjTΪV陕 i܉5#Xʢ=iWY#h*CsMîhq^f T9[?t-03+|pM2P嶦XߜCz4{<\OA FXo9WV̳8Bev3E701(LalCJM*,XW Su 6Fc#0 rrbsjhTcmv;VV`Ў.f6@\0 &kܸbvfOj D $cUP/ &% uM HI*p[d T0]T >w2 ATjWd:7Rl)GLRG4Eft0RSik$fE[DW81R'h -(q,5$߄QT 'oge.%SBNkx'ZXeĉOF1;K#*eX>k@p9eUd#ĦM)M ZhR:݀ |anc3gOK\Nu=?˜<yh͘fuaZ1Qd*дӀGT)9Y!M"Ќ#۠j1"@3&s 44&Di\/Cu"H&Mr={ÚD ڐ@v-:bx9akd%$ّ,FR.hIVn ڊAϰL# $5DsZ"YiLt:ɲ wĈ^Ȑj 8 JuvBUr'U#+="icKf4De>HLeeH5fy'e  pE+EУʺb+ی` T1B{4B"?L-rЕ?*4Qтƒ+4}Yd) -o^[rA?Fi24_3-4.vܤ$--;E>Z |P77n$ڠ04DFM4k⦯#'[Qఢe$هg_Jt8jDCT4@'TS$kRxXmsqOe @B7ӂ\\9d5hfGC'xZ6{H n&,16\tG+ӿ7Wk%љVaEH.!Ĩ lή.*NJhJRpvcpnMద%$E l= R8)z70F4 ly6Lɚ<ִ̓;[[D](M# '*e s%t[G*lmr8Ő4/%fF2T)7k7U9&.̈fz8A`[G+lebkǚT)y,5! NB{b\iU c㔍dc2h)BK8y,5aK1] cJ0JsL1]Qq [s:8\C\$dz rz%\H&je|].|ȅO$yN&,Ub6`.(%vƷ6rbJ 奂=U,@Eֆ.c[0ŔEJ7[[`i?FK/15) ${R3[[jI+w# r9ksF/q5aKZKFyiIO2Z2Kl om%-+W"reKVdڨe܁yĵd:6jkYx\뙊EZ\5H[(I::j+B` Hʵو8$V,tMF,c:o_:vKl om2v_%ݪ%"Yژe9,ަJK7ލG:|.=^#80^:TUiꝱzSUkq"s0Kc乗f[L{kln٢LWq|"N@Oh֙:尐M%s~nКXq:"/s4\&\VOҫ3;2Az+EBdž0oGwfiyr9(0i^7q'>ݦRsW_}wMo@>O5w].hgLߥ|^Xqc|$m _Mvnl }ݸi~VEdq/茀x(pGZ64H3]rd3jf繎; :pw7 |Ҿ* ѡtc~k)UHoN wAጕ Z0UZ309ՇmWEiD%E[ZnMw|"b}d 83?] Do&hF,kgL_ᛐ^V5Pq~j.?bj>#g:5=pHq CL#QN#ibz PS_ۢ(uٙpf0J,VBclHc6 |NaTf۲t_7kj}Bja +dz";20xD!)sCw);C%\OT1g5X#)BJV)ՙ+vRوI -3Ug]憗J?H/PKxEOg#svg.pcb.alps-starter-pot9mm_pcb.svgT]o0}Nŝ4HZ-Rn۞\bdHHekhTlskۇ6V01P&)Rm{/"Gw( ;iq2Uݦ@(^ٶ,l :. 6yiLfk?x?rr MfW'@<ƾ'ޜ > ˺.'5MC6cvQ.uR12E Ʉip)4&pL51~ǧ+&㈙ŃE[\rΝ5f9ǬИrK౟˘!%Jge0BVͬpˀC.W"YeXѨ٤$&U(k$)RY w46U7F%xԆqMabg*AWƗDRE%'=yDWv.hG>37>0N ]]P Q94v>S|\u\ ]T:_UUʴS#s(^>}6ś۔ou4i0]Й]+GPKxE^y svg.pcb.arduino_Uno_Rev3_pcb.svgŝG?E/! z>D9͡$-`o$4BQbv5ZϗF菏1jYxY]\| ?{.ogw+ EH.˿J mѫ8j>m?_tZE8f6yuهoWl>tv~ֱ^Vls'7ݥOWْw1Ќjsɘudzp$cûܨIlG6el >6D|{^wv \|.uxVpzsA+cuOm#BB,ӂ #M$iBb뻫U<w*|!muw\l d[]2JdAU$9ٖo JdbSeJ!v`+`UxDckV֙ 2KN%%3P/pd'fF^6F^k_J#B 0h V0U Mrvsrà'.@ gm)!iTg\殀A#O +V(6C,+C_E>h|k2eU\ N\m梎ޕ˫J"/|@b#,%Ll%Iqn*mLW\g~c,&z+Ȍ6I2GRʕtXzofڞ}ҏսIo/%!Amm- E7>t შ[9u:`#'jȔ".H*`NR񙗤6EJ)'~!S)XlQ՗*1*:È-zИ|+ A3Yt~(-!2H0`X"ņJL&Y|\/iyfɗ=+bHNUaJOv)%Uӡ4-Ȅ'j[p DxZa˗fx-[ 2je *ʶWjA%Zj3΀2F*np KEO`]ѐT(,ָ)HsR?_ Dtei:`e2 s7ԱtZ:3i^KF'RgXåk{X,K 9TjE0v䰶qRgXO4Bt(43,(ʠU1ޖW!cWb xF9l5<0O^C Q*j,h(. mp{0jߡcO3y9`j %jߗ#W&oʑm.#.]_`By#=׀j0{Ey̨VkY,PQd @?lgyTV3l@-38{h(.`pUF/}6f- QH-b}Pcv;&y @fjf} | gvu0{Ey/+JKUfϰO*f}`Qddb_VOTXV X= B%=~mUJBы|`NMGg I gi0zj>ԻҐ}5,/7үcL;d_Ș)~#2FHVJ"{̫nv~y垙l^ם__ m̼;Ъ-m&T6}P=ݠKӭC;kh70 rխC{$e^yH;m^yHhb/XL uU{wp%c]!W*zZ6_}FL Kj_/kҏ6zOYRzun䘀hK $1첩e{6;wt!lHW'er쓝.6={6@}y<=`~EX$Wl߃AbݔS1q|?|~=RpԹ!!ي_[oKmfd8K*)c$(GwL4\@@OF|y5%=\n{mvW5z4l19 rz0M dLݓ(#dH%SJ}rSf& a\m׊|+ZL 0^@d_yD"_uA/:M kO.6}J94gC(^zO>!ȳAߓAF?lCFp dk -W p8+gy)ꔜgzEya/!_4./W==¶}\gl\xxFzu}z{5z{^?+BH!l¸m&M~))V|tyoŧg?}Wmť5V"2>}hJI}2{ `׊`'s 0gqO:92Q4q?6ǎF(bAB1w*CK bsYFlQĊ؅E)c2̞JQĆX~P / xvM Cd12VI|# XB#3&"at8vZ{pLw{JwrQy 8-<O~5B|Oj-o+38dsc] ʱs2 'xdUsW[ʊq^+|g{=|? i1}WoUr 5 TJo+QSWtB];\mipVܸީȚEfmico~ PKxEr#svg.pcb.pcb-arduino-shield-rev3.svgU[o0~N~>M B5JTZiO7a$ٿ߱C6JJ''-UdeaF(FH4+f;;';FPuܖ}H˩By51!& %Bo.*]̾,1ƛ #E]Gȶ5ٷs,g ¶9>309m5rjEV̹j%vK1F6@0c5(,jeγbqȑaSp0ټvGX곹fL'{ڴԗE+uSii,fu{V/BVJ`frqK2Wq;0] ( 8)5}XPO:y`>~U_"b*iM+VQq3-.ۖ(u+@^x$ڱ/!S ;7$ ^O}J|L(೬Ml4@q澂 @;~Hl7M/VxߊמI\EhwUuKWz~# P?@:DzF277RWPKxERN1svg.schematic.arduino_Uno_Rev3(fix)_schematic.svg՜YsHWqU1 ȩovsxHXV"_=\"֠]Q ==7K.HVau۰t-,cň*`G׿FYi0a.0XW^Bh'ddm3nt QL, cj8].H\a@'V8d }vdq$ Lrfxd="!2čGm3#=Hqh>11Ǚh}6;Lrϵ4=&Nn|rs/)2\ygmue2Vj3BڒMӹjN6uGG0q 2e*$X`T+$s)+ZIƾ}\ f6g>%6h;̲]_rY"rښ|cqY{6'?8e`Y߾رv$|x0+sc]|#*@,3l&US&f24dκ.>3r`1>#xD&KT嘹䜻̇ 1f]43 MBbe$ wL6zsV979Rh߬qIFFYkGQd˼3SF*F sS"=^= ߗi|43WC퐸 <SbFiu8 `t26QT ^_٢BID>8A`BQd?_juvQէWI3@'q|-&b8e 1F1tFW0={~AOi =]$ILWQmP>Cm-d>WA9Jpi5"Qhgg3%6EuysE}L,{c *O(EiRe4nU>GGa5E-I"$Q`Y/ Ts*Rf]-5faMdH8֜PR"oAwx:+W; XKFosIJT޴.{s];:^=aFX4 =i[%8TWM#e_X?m]S[AC/|O4.n{t."_'ɯؿgyYlvP 5w%xE R[/I t„R̦QDr{Ϯo-Eh=$JQ@SW񅾈6Rj-IRqmA=kBjim- {xnfeB̼4ANS3jI$UNCQ%2$Nm+l'iqT׹Cd䑹U# {F:jJ:Hj^ 8AfJ܉;.G!⣤/tAj*em[-u3;b;8]a 8XjE UvC rpBǤ`95:V=]u]H|Lb9Deߪ3vY Lm 獻)[jXwI2 I:15oJ@wݨߘr|oNtj17~G`&nM?&2,emAT].jd^m(LĴqGW31i0_̦ԦfwNm`p6h)N[A{gwSO؟=7|L5kzJXt7r?WǪ \n̙hV٨-(x4VOV|@Yp/ ]n/k%|Y+"N. lqž@)=1KTA$0%UEvn:`Vi Ō@a;ؐNlM#w R ܗ8%݂:q<%6C~l$H5QtV%ɅT_BbX.ofAp;WHB#T> ͬm^ޝx/o "x~v*ǔm7zPs_PKxETZ svg.schematic.resistor.svgݖr0} 59= $!Ȅdܙ@A1b)#8Msh9ذZ/vӢ:mM)SZu~$F/M]֨\"ѩҫ7iл8ĺuE4Mww>!SJcûS9FN}vN@y5 Lq7pO3CQz2 6D&F.pW5jSz]a(Te7ʁ2+0'i `n΁r *0,gg9>c :0h;8=$&E#lJ1I,Pżf^żf^J.$6"%-$6"%r̋̋d")FW)C }ulM-e:ml/f2\\xÈhSs)=&8z{Iwˆ+~0y="zHGwF䬮mi:|eB~RMJ6x{ e;s@Y&a G u*|/c鯊vE?t-^eGOIW) PKxE3 part.alps-starter-pot9mm.fzpPKxEug6=$Npart.arduino-shield_r3_two_layer.fzpPKxEW part.arduino_Uno_Rev3(fix).fzpPKxEmY(H"k"part.Half_breadboard.fzpPKxE:!3part.led-rgb-4pin-cathode_rbg.fzpPKxE;=R a7part.resistor.fzpPKxEyg"H:ProcessingRGB.fzPKxEqProcessingRGB.inoPKxE>6Kz.svg.breadboard.arduino_Uno_Rev3_breadboard.svgPKxE%%k.r%svg.breadboard.Half_breadboard56a.svgPKxE-P˧D)--svg.breadboard.led-rgb-4pin-anode-leg.svgPKxẼ 3svg.breadboard.resistor_220.svgPKxEie08svg.breadboard.starter-poti-small_breadboard.svgPKxE; % f)"Msvg.icon.arduino_Uno_Rev3_icon.svgPKxE 2Ysvg.icon.led-rgb-4pin-anode.svgPKxE n^svg.icon.plain_pcb.svgPKxEcE *asvg.icon.resistor_icon_220.svgPKxE2H#Wa$gsvg.icon.starter-poti-small_icon.svgPKxEOg#,{svg.pcb.alps-starter-pot9mm_pcb.svgPKxE^y }svg.pcb.arduino_Uno_Rev3_pcb.svgPKxE)/ʰ"svg.pcb.axial_lay_2_400mil_pcb.svgPKxE)-{svg.pcb.LED-TRICOLOR-THROUGHHOLE_SparkFun.svgPKxEr#zsvg.pcb.pcb-arduino-shield-rev3.svgPKxERN1svg.schematic.arduino_Uno_Rev3(fix)_schematic.svgPKxEX,.ڟsvg.schematic.basic_poti.svgPKxE9&Bsvg.schematic.led-rbg-4pin-cathode.svgPKxETZ Ksvg.schematic.resistor.svgPKIݬfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Servo.fzz000066400000000000000000000415521272363601300317440ustar00rootroot00000000000000PK8D@֩Servo.fz}r#G5wZ;fd܇Z5mRLRWH$F P*Ѿ>FG^D"lR"??zh:ysASrN7ݛ\_aza a/zp_~ySN'`roO߂r/orpԭ$P 7 s37Û4u}u ?.h}7<8if h;yѻ\v7~ܼN?En6yW:M.zog^cNn;+Em6}z8~g.W0> Ow(Yު9]r߆g+g1|?|7W_f?Ѩ>_G׿MGmQ qm҉ZX|O*yix뾈3ʨbn4wnCeOn;Y^e-|z:^~7ZXezc u~O=Szx?FZ-n8}.f{{ܔN iYKTN׮b\f&HR??o)Y'{?HzjVI͆B:sUuI\n뜟g+.0ַ͞A$A|RWNZݶp{\]pI&.$JwO;F?]JNUB9)~e5ATwK(w wD%PI *uWbɣpF>ڕ|ީ.jG:3NuP2Bo!Q^jA>lQSE]4ӎJ6$:3Ou Nc~@,[A).1]tleEFz 8 }o Z̹cNeeʐ2Sc8¢vI뀑Q[PaMC4ԸP $MnB))5NSD" tw9QYs!aN'ng8 $+IF!XRHC% AAO<yL S'!m0|ݡƴLGo˔KI:QCeLݾ9!tS0VpijiU)K}Fl;6Ey"P6j*GCFdg=OJ㞲M,,4)t5CmsqN=kVxoj#rw$5Fzꈨ$呇|oDgDDaT'&jb67D~, aI14*φ04FBU+0gRBIKSE0('f uh@r΃PqRTP~@*z{{kVG[~!'R-O~02ΡJLũ9 R1YeG}ii[.-՝AН!RTi*E-hZF xҡOn %vrn >"/m@&A$N?!m{(&a`D_7ed3Ia jnnW*Ԧ*F(5sd),vf11u4GVR>磲Z A2fraS22v#(0yԴ F 9}o"ib|)))ѩ2 "1"U~ ]jz(a$ex̴1<-WZ:\T@ 0w5],o.!"GEn:rcݿx,O 40f ɜf>^;5!1s:*x1t ]DN" Bɫ7_֐nEw>'kxYVJqXHEa| Xn(qv-2%~w8M4Nx 5/Bnݡ.7An7;- \B1MYP` vQprR?FYSlCY ^޻lmu͛H)˽ՁέMKҤLl/`{sq N;̉j=6x3'$iJb;XU`2zSA"rͨ 'Nn~r/Ϯ/3W쳞Fx0_XߦSu3[ڌ6D6 qegy;>E3*GLM]RڙܓRڄŔYQn9F J۔eoޖKo%ed)\H|HP{ӛ|xκclݎ2v=آ q i?R{ӆh4 GͩU9Fji~{o״\¹8Xn._nnDV&. zH*q·dx2 7)U]~yp6ĝL.og] ,uq+(=Φ{8= ,9;O;|ϭ#pX9+2;%6Z*p<VKRnzH M,62"m˺Z#n&QDxIwXHecSuXҎwҺNI(]VMtҦB)sd%/Y'V(3rSǖ;6DvtDI4y]=1g"&T:L)q H:"U9> `p-o~AC8W[iqN=mmѺtǶ@Wj,:,n"R(ErxtkbTnVY3u$]f[&TTY8U݉Vݖ˪ٛ˶RlF`2e:vsKUyVJsݖZNZL(*6&dݖ)f3=xg$ݪL^Sjf2kؽcޱՔMwo>a`rlϟ8Yٔq`Tr#ptw(fMo]E0şOwOtˏ?y:]2Kw^f\P5=-LqD}~=]"=@ɟz^&7_φN_S읜>6@_ٍ"8O gdੇx."Wq8all8{ʞ/f߆IEeQ߳'))>M˻7Օh|3rXn:\(-$ugVMPInl<pDo>Bi;S< )e;Pirc%t{؇Py>Ut ۗ!wځXrM-5<:;iG;g] H砞8WWF=\< aB)ݾ CN^$nM щ,^'1oJKv vZ׮ K$:ő82x\h͈og9$[M&%U1D!qJO h45Рᨃs&5# sE>A#1sYkyVWT@3:Df+J#-V5BsSz"A;:V궷\ Ac*bL6 E ~%5DD DVk52_6w0XƤh4~Dcsdb*i\~w9PH-n,8 L-N]u˰ᩤe|3y;5DxD DVhk,9&[ha^É..T2 )Zs۔e~MU7)ZTjTD*WQ5ԘMų=#S Dnx<9nORU05c!ug C[HZnZqcRYgI~wjGI6S+4nI%OɊD#4&Tfc<2NbK\I\y %$.[`\ʙP92] f~L1hI$pkqP*i37AAؚf"Ye5B^7Ok6}l G$fUb&3aʹLtXU}u6"n8r4GEΔCl;hm9Qa#I=ǸȪ{0)rtyk4Dbv;E~Ĭ-̶LLXiFpF.#0(K<,(C 0i?Js,<;35X!%uIڠe_Y{>񵒵8*$ҷeOiٰoLu,2)"WFD1@OYWA. 3};, d"uC"qk?^7qk1ĭ,ĭ*$gxY_L)1J\EVd$,mTδM,uFHo}D$j^X[H[UIdo˪9{kylFqRRYpv%{EXYh*iqcIygXVD1tRX'5% ({k/,*MPS7q[n5+rNqvzJnȶg_>;V;m"Dζo? IyݽnaHm @ImUi'wnekF3RS2pHj&q1ܑ +ʏ&V[_dz+܍]h*t4PJTo0g@[ j I"}kI"}Js|4n;<s-37T&^)'|ĭCmnH܎!E )K"wkG^w[Io IoUi(m@T$N./_RD \.cAd%`4G9;CJ tєl[d71$&uV"H@H֪I"Y[652Yxe+%seR*#"g;R*imwqva\1gQiʳDye(ԭIyԭ͸$2$24પۍ[Rn5'!q:v'm,7?-2I0]MÛv JK2c,9it$Ht|J7唴 Y·tLs)5#UiJ䷂d7^o0Ximʫ7&7+}vn9յiޢ*mWq7bo%[\&GmFޗAXMNޢ FvvERfERZƷ(pr.e oƺѰ?4K pQ`hUI\]؏*|zLˍr鄑ei`b+aI HUi$-[Xkm֨+ڸJi4h?v,[(ѩjYYܪ{_+5)ŵD*D*Wp\U5ʵ3WO3Z-N,\iWf˔>;.glX%ہT%|\ZD2D2W, +x"z-g׀dp_MzcJ#d۫t%!J$nMk%.*$-[[q@JV*nUv(5mӉܯQgZH+5)ƵD2D2Wx3؃bypBފ|P6&3N!%#6r 6ؙ]ڣ;Juު{_$q&QUjX \` \UJ$p˪93-8H/ W\R&d8c[XH)#GN"dJ 9HLaDEA4$I$y$$$4HUsg[&q tj=RwTQ!Cڠ܏3 ?0;8,o_%wѻN?~wȵJ]Es﷡+wVJr u:#+[iqWtN h6s&)Z&Tnr4󥔊r]i<)+҄H!K9YߵM&Tm H[MXQ6D$ ~n?l7p5㮏d:l41 )G!;f(pC::kM]ޘ6ds:lL5ktט5LOmn2nz7apjԤM{{F=},4R\A sIKk*2bSC8wǢ7şa{4%_hr|?-?P'懷ڛ?~"'{y/~~m/z_/_^^~1~t=t7^Ce7/%Id88հxaS=R>OS8p|?p=~ڗ=w#}a4Rte xn&R1> ™L}u8yqj&u连H7YtK ѵ&6 7,݊OԜSAnGLIC0)Qϴzp' um$ºc]mRqpXBD^#q8a:[hqw8I{% _T紖+$ Ebؽ#}(|&u@f_GJ PVRop50ugN_^> s"θ'D8~C)'PU[iz}Iqac@}"ÿo{d}f}/lʝBІYw_>XPJcL ܯ\wW7w1+NJ/q+nB(c |qa))wtMQT8^*\VT'M9u9>~ Pv"Sy[gQ"ܖTnyL|>3w5ΜV^"ݻmjĆ7| N=wS#-Z fhL@KJ*4P* P?N%O !jA{@9h™#O A{wHpdC~K@L&=Lv0R9Teh/ʸI\h@ڸo?E)-hyF(|y//N`?| J@щo;D&A@n? 2@Acc+5ֹP/k-ɯ`BxYL&yA޻4 g[o])3 h~:T>|4vi}ZZuC28VZ!j׉ %6%ƓY0?7aU0^USǼH7/jҬ 0DZOH^,T4vY.{3q] ‚ f}ߥF^$F;fZ`+ОHZ4DzХ3y. A$@ g FAQgSg)u6@O3s1ߪH[;S6 @AcqH!ccw,G/U 5wy oVu*BlDz{ A(xɾb!n@&?x  ּ(4~<ш, c Is@[jo`L>Q|,\r 0kP% 5R+f(9S\C\" TngIA4.:|( @03W=`gȼ+Ds=$X1(D?MëU ^AH19KmP+ J`ZD&e)h=҂X?Ng0'%dU1 K(JQDKwѪUaA0Da{`dg30n@A$ (^"1BD ${!2 Q-b(B%H<% Lhk 9&*v5V7 "875@[k H & $&[8 *P ]v (0@mAv[A.XwėWc 0B0z$lCa>)b9%xDCF @484gr{wk;9VC#&Zdh)],]5w) WÕhzDt,X"oԑ4HuC;0g-'@Ԫ0nbL@@1`VoR`y1E K1ċpb",-$# a?dY@OqDLD,Ob@YdYfk?e*!u(y6ɂ,ҼI)]C|=aC( |QpQyD'!n@hXA‡Ҁ $X_T$g~)Kq1s|FVC섄=2X`\$&uE |=bjނpX~ I"ԘvйSA4ϩ -.bXGARN@{>ʱplhHvP؇6%3+.ԥd@pɩ3:etp@i%k3a:9=ouu0W<1k6˄UD9s@/ E{0m#PcW( xM@9 Ttr@9K!)4Xj@CmOL;Ob軶|]r>CAvg:ad9j 1# 0d)7 b! h8N8 W;(tUhx;(Kg9.$ x9&Pwو$sxJxՠ;umsXeIVFO~b"Γҡ*&bG{;Z^zrHT*RTBCs vᅆ*zWʏQhL `V F2ەH(2E&?|2 "|3x{ xq{ 1y_]y^8M`톆 }kU{j.0؃p&v(v#h>*O@QbHgl1pɌ¿hǸBc* a0 TlFsi!?wAB -Cf`6͝Ƭ@D<#b  vDD4f#QB3,_`a@j  ~F9`t= ;`<ǝFC 0O$`혙\!莯r"1ㅣ19WbEY<]ha ~b AnAȠZ1M1M24|ljz>PGc}KI1X)P1vrl8JM^# `aC {t0 aDAxw*PY`ž_A"jEc.$лd s (]d^b | =]s%ѹa znaFЇ EA8Hb FtTJ Л CuY~g)zL! "Gu+rb"D8s>[ sԲ7#+ _ķ؏A9tT3&GɅZ|@r|E1UNU *E LA(8lyoc 4[$^GQPpᅙ;>{ AzAnŦIСEGdAuj}`g \v0$sa"Q W̼QԉyC[]Zy 0<M4+ p rue"UC%<|WʏkJ~я0@??'%?&r m"ư +‹a"~zBJzerU >,eycREvJ Dž|q]yΧ٣BE *D;cpG n@eЋs<RsFQ-BxqG6d}/A#HL5xL)" | JaO3-2&!2XT|C܄C?AZzAZ@f gB+{Hg@ÞXW_'9nDଡ&h~ l豎#NJ/}TwN vNȍrԲĄ&pT? $ %MɻABchI^7Ѓ/vsd,wcEUXVI(rEB߃ޕU' B,A¡$f0{ zCc=PEM.ER*7Ė"jj \}ci2;SӊWD%QCYl?H}XC#]U%AiIP R^Nj'ZUZTY(UB_[T^R*ϕj4NrD'vc_ p2cEAS[ Jp܈4SD10ؘ͋B,-SO8$M`:A8g1_7#baN`~Cİ8Z C.r\).P `dޕx)Џm"nSa7fdK䰒! uYK H>͹Q>8kuJNd141w`҄LVVpLTw?ڍ)<~pwhpCKHH%YIKǎg~l.s](ǏJᡟƂc ¬9j1 #QLсBA/ՊQdv]«myi~%dYkGs]8HBG&Wq,s` fݓr0áS\oߍMmE-Ҋ8m8|M-R[,rgն(ĩIR[%/[ 3nƶ[r :ׇb 1 7)gaoj:DC–]<d80'+?[\#X?/whޕ |{ψJЕd1W,*ɾ,3u^::K0p 0pd}祕cْ}q} gDLӱqay!eI}y[-&69uf!``8T>aBlΣk>OI"8" !gxEv.lj9ZLmS O=̶ K˜oΩ ÿ1T BM}fXQ:~/A+f몖{]1EUFhz0lNH_ ڏ-a NM[0* /d5 s{I!KCLcƶ )] 10oOcgJLB/~ޓFǡXe}W[[:ijӯظx0_8_J[appbx߲z_?.}}`1' .=x}}p3}9ޔ{69FqflyUW] Khbd!fs%/־Ͷݽ`#bq4a}}b\{ pû/y8,Wv Q4_źee|~m_zFۯg8*85۰-Ks:\ */>ΤY$=[WTdVƳկ|Fpѿ;q6v?Ңwp2vb6mTXeWwe:YnmK .чC#|P;n>&_ѧY^|u}p4>߬]vJ"lÌϙJ(S~rKy|j,,4wdg3u;{u~7U ;rc,GZ: nWF3.]=ǬfbCnd/sf 5YӲNijtT=ƇuNWUÉ[1K RS}r,p'x{6j>pbnhT=b+Sg+Vʮ.u_EL!q5Y }׷*+&QUr-ΞyoqVʜ1K4SRpub@Mk|tϯ{`۰ ?$ aH~ŽOawNxp7w1Ѵya4h?F):l.qqqp=Z|nͺ}Sݲ ;nI֭m`};z+b1}xs}TnU_s Un:Tq ߀)mބ'Y+/jcC CijedVR:S$% |1XgK98!v\Vj'Ät&0)uWQ}%ҶI*}@Tl* Lߥ~o$ngGߞ ܨ BѵoIlKHRC֮Ugz.+8>qJ +#4m+PQ8j5a$dg^q㴸h*E>;b=Ύ'm_]"3sPK8D Servo.inouRMo0 W);0 ]aVl刱9˒!1aQV>45 HW^e?oHer2zzM(@1 C* R% OH'4;wdkQ#X\%.2ސ]A#M,E ^~e;jSG@! YY v pHáIf-gdzǽ.CGIki5ʓ9!my#@VʹWsdYsHC@|ٟЅbV&]`t'1AA-d/s'd)tt:y!4̤إF#GN氜tp]QG`#]BE!󓼯(Èb{c7jX uѨm~Xx.vb'j6hQPK8D@֩Servo.fzPK8D @Servo.inoPKmBfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Synthesizer.fzz000066400000000000000000000567321272363601300332030ustar00rootroot00000000000000PK8DK! Tpart.sparkfun-electromechanical-buzzer-ns.fzp_69129ed5bcec2b4b26eec3be62bb1125_4.fzpVMo0+YqۭIʪm˶^ j( 6 b<ϼ癱/a@SG."/U4t K؟@&Ze+t=~[ϑ1`3Sx)sN'0i`Xi"d8ID-gh;J{Ub¿6BZd#8$<S$GI-t ^%W݈vjVCM ;%NsZ ;ũYI̲XŢW]U/SNKGRO$3مt 9at> {6VH̐,S½܉͜[Wيo':N@g Lr$,cik:0w&"T;Z(rAn<^}FY(A[n |`aK —rS#K&BAj9KU[)~)݀,jRCPBJȔ&n x_xOR ޢhi=O<٦](-8b%TS!L~-\yqԦ<}bkw\AnEr8#$d"fJO*Ǜǘ~Hp7wFԒ$*9kz?I:Tʺݐ6 -INZo}񛾦瓮[NVi(AdYNvH9X+g)SPK8D0; wsvg.breadboard.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_breadboard.svgVYk@~ϯi Z IIB@!-ZWf%N~}G+-@]"ujp=2U68#;<8mJ.ur((fUmB<nRP2^1ǖ>gy=m^2@!X㺞g> N)up0z<(q} E&WMD/jz܇7EQx"M*& ta+a1-x]F2u0}l$ӆq%t9 UՂ<Ņ:Me\Iu5LٲIˉLƱʓxޤLlU= a`A.N1''`hZZķ:߰ALa:n6&4d#Q'y-`>,:KwV"}f<$b^d[pVi"'RYj:.9B'ۧADŽ  #{X%dd>0> #Czlv-{#[keyXy GUyUso' 3.J@ 4:apFdmUlkw2:x^딶4xp/]olkc4@{\G7nxś]aⶰ ;nwE1sTɮv1zP$snp<(:AEbvX~Gߕf/PK8D/= {C ksvg.icon.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_icon.svgVkoH_q~H+a<-;UJJR~01*N~} u]$.sϹ͢'VkF5Hy :mk=ۼJRU!`ϷpYֺժp( 61P wʁ~p]o @"8]t/Y3+)'$,q,H$`ww$vdRYkD Wb~mDMυ%?ϒ hW4aX؂![ ٮڝ0AakX$/]-]p$(`ny؟Yl{ۚdZJ×:3 jmPdXۘIQށH[]n RM" MH kJD :as'dCVlx2=Nh Zkb3Y;E++ Y$2:*Dc7"Cb$=KcE-=fYjoX/[ iN~6` :K|5~L^OPK8D;2bsvg.pcb.sparkfun-electromechanical_buzzer-12mm-ns_pcb.svg_69129ed5bcec2b4b26eec3be62bb1125_pcb.svg͔Qo0+l 6$$ )r+`GM](Iej |}뇦r&AismlNJzarQ[(}G):%a}+CS^֕G+] iڌǽ&b  &,hCv#.ADqx[)]VHiVI2O'LkѪoV<9j b檕ԩB9CPlm{CUwQFKQ߭(n+.# 'H~7vU%@*@:#' > fS q!~İΈ)_n oENuMgr@kWa8LHa~ N)sd-Z,BeoyU5TAn:LyQ㕟t1y_ϗ} utAiϕ`q&Vxkp[iJ+-;KXPK8DZfF Synthesizer.fz}r#G5wm[;f@2C6u[ML?2@o?6= F523"==>#?~>L ~ݿ{u_22_[\O7 D_%&L$F^ fCp]9pZS}~v=v6__ V|[e3|=?f>ҋՅJJM.ٻի p)u_],蛇Żr0s})SPv5\ 2W7 ؄\O ||]ًAn_}.ǫߍW˟;'˿^|ryon뱔ZC򱤆ML.|.g_k\C_|M?ށbp?s}zA&oݗwׯngרtv1X.>~^x>{w?;]Mqq|snMN>}BN_e"}LN)W jv6a1 8LjDr;v?].U:렗MwM:rJʐ~ɟ791Si&N 5\9UV t$ᮛcMu{rx^0+cr8ڠ $%~v޺SRv7r?tq7]=~|) b$02Q^ >;iJ*WĽӪ^n5Ȏ0gxo>'dWvyrϳ=j^<=Nne'-)p8]8%k8#Z,pj(DT,aoޕ#젴9CAqw~[>';gjs^Ԟ`ú7%~U&t>aV`~>`7~n1k3wFF]nwϝ=w=]ny;g!~q?7~ynʻ+SIys?la<3vRa7=Y{oް\;0+~.ZDgeXUm,:DD׃l'eDv||}^U vEغ^7!1V0kg9ɻh{pu}q"F&넫u4R3XzWNJꚰꖤ ^pm?W7gru.KuNW}nWd}ΎW'q]QԿUarq2]^>WK̦~tH{S'O!{6ƧMwDQ?;Qpĉ"g|n?ϟ?Zfsw2c*$*`0Vaի.~Mw U8k@QNZsz((.2QlhrgᄍS^'Jvv(Tnn= V;E 퓼ƬWU9=JqCPPSPDVs҄RSafLe= (9D|b@R>F³t9[@ C mt&MAF y 3厥e *2‘g4t2iՎyfn, hDېFHw.l4i 4$r'(xL yhafTyp^GcCZٱTq25dTDX^ ܊p)#qܘɸP 4ZLE9:XEcBt{oX!R<4 qQF"6[|IJt$eB_4n4FCM)hǏV((,u,唶LA&z+eV(=5\_)}Yg8d eZ'4% FK9ԱB[ey-Ei qS)Tf΍<̀gE6Y@lE#HclԶ!6"x izGe- !~8vueOːy=ͥ,т6z3DǏDt[oB!U*hoa" Kd|.",_}ґF,3"EjI"X'yMaJ(E(LMl,^rQB0 LybáTD#ћ#4 @ne?6Dy@Gw  DAd[φBy 3H_i|t䓷\\<<*NZrT&B38MͅǏt[ouv@gDA)ꪀ"lP'(JT_0KИ8gB{"P D"_/CD 9my4ί.Jj>Sд֩ #;]8R&#aL?-7EAqK*uVUomeFHeDd[φ{>㞼Y3"H[ it}k'e),+ڪ%†)y}㟌$Y|Z(B[wmD\hGo}vT's"i\fd(/ay c$ybb*4n/4FGEhiē~ 9eMNEѰ2ݡeLTlxi PL3Z:jy.TJw5[Pk-{sUdURݜ)_#_"8w}8KmIdM@I\HI}rtVT'?lev ch><.%Z}܏羚.Vba<G~6QMZ)`]v ģȳf=CD$ʤv/FqE-.lKvCbvz UH:5k`DN<}3\~}j^NE;fq4,=eǜ`LIoaut}hgvayg[vu3-<[LF=fF8ֵ3Xfx7(tG9~f[!N 8<=i﷝#~-gWs=Srר9rBvs~9~Z̑~kClпL?~w>Bi .>h%Wó{0Ů@7OlpwƲm*svŨ鵼L'J\15NT+JUkAoƗ?-w>,?KbhhTm7)7WoqvutWv~4;<1q&/҇;~:J}m"xXi }_̀{4"Tj&g)3(K68M-:oHrY{ zn^슕_Y/, )"X¸_tV-] ~)^*ҰFBJ$1%*c$:FU/C1z&I/RhWOA@Wb{eJJSڄPZ§V$2}PJA0-X ѯn vtl1z|q3^}Wwv' pVJ;PhuA4~hae9:de&jm.z1?qfPעf&-#ILЉ3A}Y AHÂO@ Eer'1c *rD?aq8g}6 ͳף)ly  '*'3 HZ4DőZ’HE4M铔;Rw)s3^&HÂ4,h(SDZg؅ H-ҳa9r?{44Y3g؆VM"Uu NR>NT@TՉtD uC&! ŸN̮u WĐ:>X+ _S,,Dy'g8zy'^4t_N1t>^ԕu2aP]k3hg(ýJKJ|)$Ie$\t~*M;5"1߃tS4 LB4M%GTXV*G^Sgg3b Muǰb*m'gI~I&b[<5]l,ًɲ]&{@,٦ Hh؅_ϜYz'"~Y݃3gT9+:*MdKy({'5Wt^rEXC^:3~T^Pp/)㗧򂺘T MUM9 gǼ`to?N*MI4xQӦ^i5S@ӂQ9y?_hy;ί+L 6__A$e|^ɓfg)*쿠h"di#$azN6߼uk85y.\ҭ. Fyz^#(B}3 3w`foN?Q\g۩>H_ں5!h8ZVR8X IUɜV|rsAF1,DQhu}'U iC b6 ;&'ڈrqd>^J6> akyQ,UTSqyэ#m4WLUgQv!S)ad61BJ`wfF4UƛklÊ 6S ٔws\ 8j4,*=IA5!b+OF=x2?{qƳ_5h/aJF I(k-&̓"5^:W{S?E~)c2?&+K#$rᮡp9bϟySSDpD7NTScd(e"/ D$lؐ;Ia9Z7cGhp42G X:Im8IR:ԓ!r. v`lLsJ̲K&D$ԙmy&5!{S?@̍[)*RnU9@D[*d$mzszcm=NDtDEdkēÒK՚wE"Kk:"K+f'he&#Q _Ev֩~sgfYx&@nf$rC MC ELL+DsIlPWූaL%<*kLdgeqv$`%dU,x̝UQ)T'Vm HU]w2]7$ ɏ\m0%Ó?k%9rojC9 ]k{MV"A@G"S; ؕ?;V5uQަoH]&[6s~`a8"0VVS0w#%&0~I -kʰ"VrQ}#nLgkjvU⚚M"ʋ^ $R0$RroǑH6ԭMÉ:iO/N#4BZ9u (9(HKڕ6ҷ*VZiI8ZN2m>P a&n-%5#q$r5OZNbq͎APc֭7HW9rs\F4MVR! ΁TUm;yoM"KԮ$Ҷ#ʽAZ$mӧm-o \xdLQU֒DsŶp"k"Zm"!6Sn<;fmBYY[YDښDwM"ik<"i+~-'mYĴLF=ūY3wMM5\"9-"ʅQXR a\"yڶs':!(yՑ{#5(AyqEӏo?ލm'l3wTDžnzu{10wH(e!+zCNgnW*2&¹b{_ގ忟OwrMWdq=ꏿG{σwOo~|=jty_MWח?~L>dlt'^Ef&׫ʍFDZӑ^/|5*! Bx\ 6n /}=p'OhTj?fM}/2L)b|v~)|J?8s85N;TvkZd 7iv>ƿ%u sb4\:׼kj>]'{\Or?hke8 B&lr8ޯLq*[l wH0 Q|ȴ{t2ۋ!Q$BX?I'FL#(:qK#pGXw/~?En(J3!g#acmBVk-WS~vsǷ#֟*빿 ?a|@O|WJ+t2L?QZfX)#)[a  lD$:Aq[v:' MHi| xTIN L8vMx{yZK/"qWp|+}^P{VJ !K1 >k'ِwm@P%2H5T? 4pxJcx R{1 ^=3<ó1u難 K]#C 'x>s:1{_KJ"%;yΘ:#:dU:Ķ8E.mA. ߧ~ jgNRcG +-Cj$|?@~̸pxC[:e;Јh?`/ 1 #T3Tp~ ߩ/x|poA[@9©#O xwȤhLz0~HeP0ozk(w w#<=@ Oh+iwKZaO:Ôe]%@"FX@Ƿ܈7Íxǚ n@2)3áݘ+v|յ-vx||ڹO=ڕrX"]V LLLJ' WťO$c"+)΅>QOFuMqǂb 02Q eٱ&U;e AW=M=Yhd`?' S ` RSUa d)[@1xGG!2MaS$eF򋱙<c]H%6"}[p;'jU\"̩'P00ȹ|#cytt`vXef ^ScގTSuo4 0DZO^/,h) Z]f!WI3N?KI .I!(s<8S tōt8:V= h# (e96Ig-+5.P\ @ƻ$70HR=9̀ab#mL 6!)b0ƆΎ2ݱ:AT11 FS?`ڣUpUoBO 1p4Ř@M@@QoV7@, UbXP[T"$cȤ9C N- sN&?P4\b0 0k% *5R+F(69S\A\" Dfg i]4x+:fudVrXv`!J #,7 p:%$u0ꅎN! =^[գx>`LNFA7%m0, ]&eઠ)=҂ب=Nf0xOxJbQZ"{`vk`މ 1VnjϜe` G7H@\V#RZ7/V!"qF?7L(DE‘>FT$߂5 _7 "*8ojBrEkC1=NO VN#%318 HIe@1 /RJ #} 40Dx\BHVj'h=>|ykѧ#c(GA;4#h(G4d$p•<ڂ@jymG!!؊qhZc Z:>EҽUK]q*\D -pk RS.5vT`i:n` ZI pHgtēh7Eo'9vOLK6,:V2/Y̼U%9"ayc$L7m +uEx! tg0l( ޑ(F.fwUI1` @lON"R'0'>yG1J|{][zwg:ndj 1# 0D); bCN?a,:?Gp+E8Lh*;([1.$A @I9P:uмEH4Tag)LcG#s!xd#Fc[MͩW6Ŋ)oOf|C $(p]C?nF7N}pΘ.HDċ"RT.\BqwṄ"zS҇QhgL E`T F2ǕAO(2E&U=:Eܛ;IqQ"D,!b AQ \H!3lod[0ƇahV-* U#]e' cαPa2 *tqb|a@" ڎIe`x/_$FiK%:Д0\h,:~d4.!GԾ)BX'ƫ$<*Q.$cXbrl8JU^# !n!D#Mv|I 09C04ӱJRa 4F[s0aʀŏh!|e i8 )  VYb̈́`Y!!ȍqHT0 C" ;XxY`/F~<7Lš T n3sgVb} %Ihܰ`@#hCbG|#1O R )]h58/C6'h*!8ƒeuP<äLȘB,E;M+ E`s>Fs3Բ@7#+=_GÔaNaZ@v?MR[Ar8K" ?5xNef OQL1IrDEi#mL`y ;ghF# Z g!h /asv Azߘš$H"EdAqj|`2u~q`H0">."#RF]bN_ڊ KT4e$tP8Ѵ1T 'jWۣoTps=[:hǰ# `DaaL;6*1CxQnŰĀjt (&HA 2L*\p94\AV)hPh8Z5=Ek%QC nq@p`qlkVE $҈blƒdHx!8$/A(dF@ C@[7Or|9T q3x3!ҕ՞ t hxUʔ}8H df_c ofAXD(!Q&|<M]Վة)7J- aN 8` U e9+i -2F%Shxi<|4C23*3Ki.soAJo";>*|=Y4 .zOb 7:[hoiG+K1W)jWp\ipb 5c1O}' (TM+^.t5kACOѷk=?UBGBbKqO{^ChYjPe.Tr2Zj~Y*lPyA<88\ p2MBS['Jnbf͔)P rS1zF|c Kя! eos QYXf9Q8GRD?S jpu!"b&,0+ΓySGてP5)}Џ"nƓaFd ,N%9s= ufƪS'JL]Sh LT0Y:"9Ut0 dAorn,'OEa1&-;rJ?| 0"= Yf(ÏJᡟƂ5ai(&O P8f)2pVxp+x 5m@a2Y}baV(GQhy&0b1$*$:8qk+a}L.&, 1 ?E:(iQ$ cQb!h cQb;+EEם P}YRX)0fXl%y@с/ۋi,1! 78))57<5Cپ;DsHiq<:ٓU<;Mq?mjH MIsF' LKqFά\g[\l(xYkE_lEܦ)t 1),'i )\ė0b kC,Svv KEPy@GtM x,^<'t䚏7 '8'&\` 9FLcjS 'qLAa–Чôvzx_#Iy mBva5ˎ!)-u]R̺ͬbxoUgFx{PtNPS7t zqR0~07"}pp18qBHpt2̫2,ӘL 3I}2ey86qJy-hnD)Ձa߬>U/.5/WjSߗ忾nmx/՗׏կ鯷&=;e\gp鱇ɕ_~ltq>S~>XǮ.YIouAq;+iI=]d. p'~oRtwòigbIwUڥE}@k{r\opOdL[ۢCwk{nNXQf5QohpO6#FV#fW ~eZKa5Leae"ץRު7@Хrުú\*%%մ.qioy>'mG9; )АJ<$3 TB+ >c)g' :_ޜ2[qK_짧:, j]d Wc']DIQO+K$ ~ɽ2VU})}CфA|d*E6Q&pSĒ(^ 2Nē}@K{`$Գ3IRP}Da(ex(C@e>Ʉq0!n #T9&VK{0$͸ jpDwF:8\##/˺`Nay8v0VJ^ ]% /(!zBJ,%R-׉pжL Ra??c yF(#|-n^+=6^{vw6% Q}X0 ]{GW&ܱ:?ttb{_>i!wS5CZk~J:vNUZAHou* DG_ҮBqk4=$ځ$&Z;A0"l!:ǐ^kGbzmmj>]#)v&>:j!Q 7:Zm7D$k4}4͍Whm)]tWjqb0@Q(]c*&-zV"sׅ /̸3H iZn񉴭Y$R#ʽ@"_|^\'JU1[02"qMM|Λa-nCI(,o")rNBIjMjx$Տ"{S?DV '6"1 0 ĭk8*~?qB-8`&WJ&2k]"8XLIHyLE%R$RroꇓH6TNLXRq,wro=)B2NDZ#Pa+zSS̋Ĥ\Ex\np%r&1es}ꇒʽN"[rlyqbT z1u zIO1uDTsuae֛H0'><$=-%99b$Wi&Y5>hy.H(Չ$nfrǹĊyDgGEdmy4)7n-Ƚ7e2)Ūlxԡ^8gki"cg"cm㷓&2Z/"k&NZl(l  "rGk,`a>L7_ߘ*rIEZXY;&Q iwQ[q/>ZVNI,~H˓lζ<ykyn7rzK;~zSD%v5%$5 "/=+tD"a+~ ,}m DY+D$i1)),†>"ytM$#Q:!"$}@K{EPaĵIJ%r8@%,]:DSo'c TW*{+`6DbX"CF۞e)vZ8-em`Idj IW$ykLI v*ig'`h]fH|#",H3J}u-v*=1k q5.D[71V?mk P"Nb-ؚ%p$STɓ|qb Dڶm\ٸUQ-V!엝ccIkMI\ȷ7CKL)(1W-Ÿ%r6wPK8D{?JmSynthesizer.inon0GJ Ыi]'6 6*9$G;"26}@_lN Q4i(|ki34TK,R H2\0Am\!ҒZ~>4䄢\2lI2c4|! VUg^(Fm{A iaZ,qDn Df}Kve +x^,=M9"zr~76P7>Go(W#\Ncq.g6Q" Bz]fq X5{DG4V_XnqomtG71c-yFCN'J;? {I.M& PK8DK! Tpart.sparkfun-electromechanical-buzzer-ns.fzp_69129ed5bcec2b4b26eec3be62bb1125_4.fzpPK8D0; wnsvg.breadboard.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_breadboard.svgPK8D/= {C k>svg.icon.sparkfun-electromechanical_buzzer-12mm-ns_breadboard.svg_69129ed5bcec2b4b26eec3be62bb1125_icon.svgPK8D;2b svg.pcb.sparkfun-electromechanical_buzzer-12mm-ns_pcb.svg_69129ed5bcec2b4b26eec3be62bb1125_pcb.svgPK8DHnUl svg.schematic.sparkfun-electromechanical_buzzer_schematic.svg_69129ed5bcec2b4b26eec3be62bb1125_schematic.svgPK8DZfF Synthesizer.fzPK8D{?JmWSynthesizer.inoPKcaZTwitterSaurus.fzz000066400000000000000000002472621272363601300334420ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/FritzingPKxEug6=$part.arduino-shield_r3_two_layer.fzpTQk1 ~߯0~iCa0ǥS}}i_?پdIۅX'O7a[ɔ3kѮ}(ٹv0+ krɑK]K7ɫv^e& B2[|rVM'EvЅ5L|z̆jvTkTs\z?qWLrYpu%&3=u!Kݡq0-{ѥ]g@8S'GQB_uN06Nq269Ҙbi>e--1܋_b`(J!aԃcP]G'— 0;<')a6XO۝\ Q828fM焃XqidhM.$?e+q(ͨv*C'?!.whuڸmOuNLfZ4oCtmYc/a?\9ZYŒ&:@aٝnohɇY:MeS/Y4ZܙӈvEcDъO`Ix$hBc6Csb ڳ]t xX򶍮1:h<~&V&?r?F=.tRMJE_(3ߞA2,<3P%h" ")IoDl*j&#;]]DKFgLPȀ%h&IKTdO:@_8 Jd ^BL*%%E5Ey*tV"\pTfnZH*Xctqws љɊf,^wFbaٔ$GT"o t,p ddu fqd _@ g84I$CI~#&[}SL(tLfftM +K4Di][% Z usLamoeLC'B PGy,yƪ  ¡b[L8k!ل~&&h~)dF#na0< @˪YmX֟lEJxuT,oVYwLef{:o]ÌmE)ytg/oA7Tx|J*V­D2/rCO7\jGíY*lf3|uzjZ6:+az SUd]naîFFqحaW/z*ܛ.8K95rhš]C[/v=Mq+8ra({U8WC{S:5](-4ţSGG/N=M 5GiGl X/ H-c0ҹlIH{?xTkKRzylT:b[2U4>j{V'0N |;s"щRq }_^H'Lv0)IIcSq!e IH`D1A'}!:rH`@K}VֽWsp8)I󱁃 ‰W#Tb>͚{o&=057&vv־_sI`Nf\pJ/Bd:1j4[`FONqn<[`O`Ny66<#nJ#m\bʿőh HiJJq\ʿ1*Q#R2 +tusW:5㚗UlJmMtj]B\2!|k^'T7F(j[bkdvڵB ^,4f!&2{ցD',.I%.5G0ɥ¿1.f_G ܯɣ¿1?tDfV"Rs5T7F?TRs8jRoJ1OY@*}!5AOǺt# t2iVT͑R3ңoMkݖ3) t2iVTqR3ΣoM>H]bV\TqR3oM\w0 'frHX!5c9*ƨrR"4!7DsԌߨqH{4+zC*Eo Q7FeTT!b8Ds ԌDuhVTR3~oF bҿ7蚵q+m\{Fpḕv8[sobӻ+ZHɣY{ռqkmT7+\4n=yOӨ5k֮ƭq5jܚc<Ѭhnhhnh*xyumP&@MA͛h*x,n}.xHdumP>@}]u Sϒ`3n45Gx/~x5ƈlv*ѻ>lZfE z j+j 1- b^WT9JrzWȉŸ=B 9F 9V<BQԫ,WD:?^}z]/Ŵ:V,?PKxEmY(H"kpart.Half_breadboard.fzpn]G?'/_=|o?'>8y_7>}vhgpz{On>[zEz}rOZzOOE柳7wٝ_zyg/Ngwח.~ɣO]|rv_gw|y}ݛW ڧqv/|^x?λ;~yyˋ?_v积.uqzWogw~6g~5|Qo6ū//>~/xsxrAW׿r˗~ux#sy9>?nM'ynsl^?ݧ8*z1:Wyg2^=/*~y(޼W/L^1h%~n:ͥn^ٽ^\/ΟW^kyw Q/NOn~?7=8}S{~@_]=(x?7Wzrݛ?G!vgk/em?Ƿ39/SK_K*z_a_Rҿ^>mX׻ͼoO.;/SKK.zaRҿ^>oX~o^)av)y?D/0,=JǹatCCCYJER*}.r]GPhRs">""GJF9949T\0ȡȡ,"E}D Ef)>9.r#rh(r4KatCCCYJE)=E.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29Sz\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K2]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhNu.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CY:"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GtJsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef9Ů =z' j~rkћ9w8wy#{7 a.tkћ1:w(Lwy#{7 anukћ:wx`wy#{7 avkћq;wtwy#{7 awkћ3^v2lix4LyrxN- i5^ox0 /;^4<Grn~KEoHbx07ݻ} y?FG4l]J?/~~߰`^?)av)KSCQz}g g:R*}>s}fz64KLgzp,3=GggÙNtJ/^3ӋLYJgz>ӋL/3f)>%L/>3tT|3b8iNu>k^}fz54KLgzp,3FgWÙNJ}W^ g:)gz>ӛLo3f)>-Lo>3tT|3f8iRޢ3ӛLY:Lgp,3}DgÙNJ}> g:R*}>G>|f04Kt8̇ZڏУwo0x-z>.~yOrn~?^޼ϔ'{7ϜfC9Ns,< j~΃Ӝ9Oôg<4r09<8y4Lyd<4r09<:y4Lyfc9Ns-< j~ΣӜG9OÔg'րӑ5`yf&Lyf·?έ˃k6aZ3s>5tx X^ jlMR̆wMV̜MV̜6MV̜vMR&x >MV̜ƒfMV̜6ăMV̜)vŃMRx3ƃMV̜=ǃMV̜G6Ƀ.MV̜QvʃVMR=Ns[y<3{9ߝ|4Lyfs;n9iV̜|wr0Cq 8t,X jqpMV̜>8'l´g|Cr<&g<21yt?7aZ;l9iV̜|vr094圧aJ3`sr<'g<3ßN9909t:'-لi599hyN&Lyf99霜dyN&Lyf·?''9,ل?on<{뫋wtUq{͟zrݼ޷͋/h_JnJrגywzu-xz'{$7ps|+py5d5d5d5d5j@j@j@j@j@jՐWC$Y Id5Ր$!IVC$Y Ij˫!KVC,Y YjȒՐ%!KVC,Y yy5P$HVC"Y ee5j(P$HVCjP%JVCd5TjP%JVC]^ my54jh$IVC[Y Md54jh$-d5tj%.Y ]d5tjЗWX^ Cd5 j0VVÐ!Y Cd5 j˫tAP@DuX2": r+C2Ѥ&e82 (eB)#uuR)A JXJQ%DX "WºW:XH,AD 2K%,%Dn "\.a.a/A K%D "bH1AĘ LQ&,D K "g4A$ "MX7MXGM&XD "؄%m6A M&&'D "r’r9A NI'ֱ֭D "wA O'D "uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@Q{_ԔW5e)e͵֔5e)Mٯl(( E"E⒁@Qd(2P(   E"E@q@Qd(2P( E"E@Qdd(2P( E"uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@q@q@Qd(2P(  E"E@Qdnn(2P( EK"E@Qd(2P\7дnIdIdIdIdi@@@@@@Ӻ~μ|gY.y+_caVXoMow}wֿo!qm\_M{o~ӯIdV)t)9{In޾ 8|F@߅Տ؞>DwovbN}?xQ *9>Dwgj5FPfen-#o$>$wPSvO=>6oq7woo||z؞=>Dw/>Di`jMtm_&BQ#(yȻɳ7zao?rt/|{oWoSr7ͷov]X~I>=lOeoQtwzo4Q }@&?|df˒/ZTG}I_G:y~gw?PKxEXRH] part.servo.fzpVM0WX0CnWUJ9fº'wT.|< (L%tЩT&b'!⏧tuC$kj$w,lM}&y|SpX؄`!yQЇ0Wz#)Q^P2<𬍤jنFڿx'"6U1V]|= {^$Mm0x^)Ie% Xƍ]CM Kdj˺J;B9 脣&rMrTZI_'/*u_ tI(ψ=3tU nb|W0]\PR1:(vQK5rv{/BX/jR>7/_+ T n@Kd]}K4 TY k}P$7EӠRduvZ#k2(}ʳJR|jgzI[)ےotޗDF^S*xGeD'1(1$1Mb$[C^;"טdGeg99I6dyePKxE>6Kz.svg.breadboard.arduino_Uno_Rev3_breadboard.svgkݶ(}{>djox8-;$sKiw,U:-$v~ZII-56 7/^~R8\^ᗷ8???_o|zxg=~<<߯_-t (x>oC?6,C_d,\o>x?~ѯZʊemqqOo|A Í$/n/}|QF痯O«ڻ׿~^>Jh=A z_>?}7]]_^ŏ Kg>?~[C|?_z}?ߟ~~supxׯ-?{qs}?zJzOiV#:a|g_ώ?'}\Q0¾Xzyv}~Nj˄(?S̐]{A/K;Y -豼Gтߨ B+󄖢  l3yUI!8Y(r:_NvYʫnǗ\7?|^U)('ӢQLEYp'JaYt3*<`K|z[rq#\^:a_:aoO8:ş2w"n"r Z$@Ky' +;p$Qnuox^Z<`$!$yIꞶHIi$ 1b.k!F67Is h7V,@!\߈yӝ46-KQcjr5?/u3c[´$,T " A/ oiy៖ - OA쪭=x_D:5okc >:64.:{; ~*?? Mp)=~j2:5ֽ m [KUCkݛgftBtNdg,ITAYޛq2}S!5ֽ +G 8 q"22 =;H7!eķ8WWoܐxmp0~<. o5z! p^Sh(%5J5zc صnMxN):'[g#矨H=ۆ܋Gy7("x'jyZ MS .1QlWMc Xÿ!^aYT,cz&dXܢ  0.) b2 nXJ20I1LDCOD6!cB;Cʬ1d 5Z_`7 C n= 0bm1 Lx`xx! n0< Q a%aa_2@Qt_5t}J׻aFc5(4.G2 z\,҂7p١^YB=Np t`BU#}Z+S\ig'U ~j?׏1>xejb`S}\#eԸWih]KzmyVP;\;!~t ~U ~^)jNw_)r?G<t_ 5ܩ3jO>1^ 4`ܨq#V4붘L 4ܨp a&]p nTs~V)wƷkV 7Aau='k2P& :ڝ-$[de5 VT.{г-Qe(l&ǯ(X<Lp=_.D UP`E¯(cm85ioj `|5_&_Vc]u5$vk0 g*P>j/ZjNw.L`?O<ـ9IcV]faklpgZXqyOAvnMsH9)7@'}<ȱ(rAz5̞{; !h2yvqT\Q?6Xb oe<š)aR 0Cfa!#[H |ݺ7  V {2Dj=~{_|mBo'#tJ/\Wy _o~@ɟ>^/ۗ>CB۹| Bd8y??]8kx[Ѫɧ:oE _'~{?~/&G?'" xX =z4<^Co_BD"!z_/^Gf#kx>/o£"!xz/Gݯ|'ݾ_޾~{A k/xFC(V⨎"YCaB[Qz?O"@g/-zѿH"G{ń?fkCƌxNWpVhq':_z!qRF ƽ$]GtNuuT.)}qK]t=a&(5M]( EדҒ1&GwϒD(W>1RgE+WiWQW>ٽrM&+7QfzDxfT^0fc^yqξpNW>4^=ŎYoontvĔSףڍϜϜrkGb{Jy ?U!$ha8L}j>$ϋ^>>F>F#%$cN,)ؽɸ.'}t2e.Gi@ |(@Geۢ>D.݁^NzL0K"꥛IߴIߨINzЙ V:_k7I6qUN;tZI*f8Q&8nCuȥm. D]KkSuSߟmBwjiԟVOqSlJEݟ*fSQ&8?ˇwt's]҉<-<7t7`g7:۾ _=b]|\>:|ѣJ>z Ξ`>D.ݯ5 \r_zjd:Mbu-79l6`f7:?;$E_`iyfO""Sg`8h?~9AX{wy޿q.F(m;}:}ٍ¢}ڽP֍]\/h"thS8pWϮ?^d_q4; 54i xċ>KU h"$x-1Yx[Ȧ'ƀ6m'9KF2@ȧ9?b!YK,x{ɬ%?!LbY?q&X<`28ȟ}/>EfM #3v,KfMc &3~E,Qf-G/ JC28ft"A1J_O m3]C&HO?7tܨpv{L-}N+w_ZƂy' v͉̀t,596'"ILDɋ?}enA jH;ѸuImD)u!#%6=.6ڭin1ޞ3GںxIc٧Vh /n5{3h58{N8{8{vm-=Vueɳ1K ãkx<{PtL՗K"t4h\_,h^E9#nDBʑ52^/Joaq^8}%ZDmӷ&Ƶ8?](Βksv 9zFV<\"+H\ 9FQ~KDɎ d$!<ۗKX$I/ɀƍP|ŲHrZeYf7wXL,2EL͢}^0&Yd8@cXAPŜm=Bk Ns<īDm<׫/f~~ y;ٗrh, &_ XcQ >s'K1SRNVV 4Ɋ0,&S(\䇓(c *ew𥾽3=\ubj$OǕ-A?=XF-Ie 7;ZIOB'ZD}C\{#/ mv~g,dlIMn4wzBR"tz94>-WɘY'(dA,OkH嘨7Z&^)Möu=&ENu8 TeG{.:EϊvQYK~$9ُI|4]LQ7;D1AtM7)n> M#Ҝ#X? Ys~c 'Nჷ4%ߘkQ>HXWfIƷ5KrM4Kƈ?>|-[iR*wQ9MB}z;SV5xdU |ݣ'}c F,:6 G`>;&#ؐCD,EM_|i*mhr W66w{tfl~..S6t/*fAbB4P&׷~s}/0ZU9}5 $Ml/I!8W'+%8PBKa?cib)%U m(TR wmz:\+K}im!Ƞ c^s5J4,mE?J%-âɑpbu\wZd-#-=%qoO_މ%i|mtDoMMЦk;hvю0FN'bdwIN' TµR]'yz {8//Ǽ'Ivbv"X}|ԣ֣ @uڣPZN-(Ԕ"UGU6( LG1$0ƣD ʣ( c8.K8 wIgy`aKk2`/ ū7!o­IBIP\](gp]oBƛ 2:/ ;Hݏݏ8VIs?iSsR(BAB'y[}YCv$kp\1Lg)Nzt (@lX'A[~~Ҙ~~3,01$1U]eIܵ$v6~_mwVs_Y $#sO̸n2z}t!5hɍ0FkLsoH@~EU_;,&lpQygz3`?8l\\31:#ѣUw$ L#1$0őF %~D@ONX+9Fp?6)4S']N0Εxew+~iw+BLÒwai4O/ݬ^ر{JkB=9okflk5<GV 4`# k-}dVhh|iJ*\Ͷؙ@ls]<-6맟~ivZgt-Ƕd[KƢ|V>~4'ۏGǚ辽|ئ5lpӚ8Ao#Z̫gW|_K9˂9g9G1RrTqq7Gj,5ċ$jo̸q⍟6ى`9UL7y Ww8+8;bi,P#fZě<ר;~x qnsexgtǏċ>34io´Ǐ/''^lƀ-L8h>syYx:x,NیrW<Ό+x|D闩D<! wAXP!r Ax"UBл_,9qhI0I[NloFXH!rB )LŃ T?N⁙g2>c)| }b$AK2F3zJMq&8GFSY( MabN" azm3F3gJ^[=iNl:سf7ںTIqQ4b7q_k9.qb+"g_1N|_/ 8b<cQL8bqJ1N 6qQYq7C-bP" YXC m&Cmllnyl#ⱍȏmcX^~Iٔp#Z9_|y&9"?9Gts_IO0qb9"'5G6ᆋ.?7G.ʉtd刜or$9"?1Gn#cnˑ̹&Y92?+Gv?ӓOqò-.>E2'Gӧ.c<$!eX@,sb ~ef}r"bU閩T<&V1ě- tN0Hn* hXu7[̙W9ᰢmenSʏU}S HV d*?PJ3a*H@B2*?P8ۯ;>kXrB !˝uB[~Y!TN Z1.CBuܿn8b T+|>%*?vPO7vz.@BsxCխt,RAD=|*?xPofϐ:=Afj8t~;7_O~7<ۏ/O|9~"FQ %t+kHBGI|$Nl pXBK-wC JN(|bqΉ#t+[d:?Н8~գ~bA "t+"t~e}W,bqΉ#t+Y#t~q椋:'Э \0(BGE< է5(ï/~z VT/d . m:iAvl\Bq`#u80\qy8auoث,]/o/WvX"Ia`-w@vGK7[׶-'%<4??HKsIۖFwDÝ%6N=gOO:\f_>,?| w+X=̟ ғbwH&+dG}-qs+:L^A,^d+Gi.z!DO7 QTpuaN/W'N.7CB`bw)m`֦CW&JZYEeip洡9B Mm$M#{p(/"ȢZ:QpduԁGeQǬI8::bU =.w#9db5r4B#jjxW0# ; ;~Hύ n^{`"PǪ@Ei rC4pkӈ Q^(JҊnȊ5 Vz",ǫipFTD2D  H ̚nDϊztNu*[xi}Y|a-ʅg XƔ(!XGh`3r̷܀ظ76 F FO*B5 ,x6`hlAlso IھY [߿';6Z3ϱz 3Z9Nvlyc`NO}l;(d?A3r:t#8a_#g6 oI2ǾYQUb?B3eyat$#򓁴oi WW^Q3OX""-ȩ>BaMƁjhXWdjE 9Finv#hDc[@VF atd:Yr#&ՔkH"ZdH xF_@$r tdG7! .+eXAVh 5XGh`F,.|oHG6Sg͋6{ZwfD/sB>uO`B?OF[2wZc~V84\9 ..<|n>sB8{-VwM&Taeel;]SS m h%K6hyhsX_yH 2v>g6^ϸa˜)E 5㺈'"T|о.B,tQ ũZl\]:@0j/,WFbLL2xW;tZp,0]&ʤG_P1[\b9&U;0"!Z:΢͉(1 'Ng;[Nz燮w,MMG﵈2æ'˅"˕#Ց~^,RiF^zSx;$^wr'X8^.ٽݨ˝N~8{-^.jLn䡺Hjd77:?BAGy-T0 1*3d 0 `/ ktb}tSL#i/D2rZr%*npH ܨ ,8!;khNVoƿTPӘ%,JFpGN 0;F=;"q$ QsC0rҒ pʺL5k)%;_`B@RH~d€ \aO^cv5HQmP̸$ap$F` S$Z!Z2r wz]%8PB"̥z&O6o;Zr,>>=O(rJUjLCQ 'M>B,9Yf7IJbpE F%X`7%_j )r%Gā7+'E&3htfUS"d氵2 +̂1iPA ځʼnf`0T9iƎFDcYcuGYT'#kF"Xr5P+UjA2,܂ j(\Kc)$E66@J㾬0gH,-hB{+ͱ͸l+g+&6p܁Pf=pcNDנf[i4Bqy۝FW@5a/-v$ d.0WiUȄjybZXe UhSՃZ3MRNJFe\#h>#Ac ݴ%((QF&)a[}!y:٬씷ú襭dq֜Xre'wM 1PLnD[((V =dSKvE Xӕdؕ]S i68;+A_~-;NXZwnT@G&+~!Up.ZlTh޳ {ԼPڰ@ < M! K,z ;x |.YXYRFA) @gDAP) @ #<:alD-  @I QB1M? LL! *RXv$H$qg6)Bؔ1~ǣتc4-U TS _% 0Ӕ 'z5[ x_s^ؿ; Xi T֘[U;GA+PAR%ƜQ XFBPckmCDP@Ƙ7_@P!j yc5,Z `֙ 2cȆTQҡ1vrH95U:.14'C)0cckS-"i` T hQj+D-jVkvVgͶp7Ϣtk,&cqq%(X,4~D蟉r3nm׈?S2n%2{D/UY*LѸQkp8!?V@Z[F!3QVX,P@e24"BbIGQDrdm _-9|A51obOy/a ےrl_Lp-K:U,Rq7q\@:K\9B 2=!LUdHnXbB~d%M݂}KlxhJZOFbpY[؆kjLl-Q#*GVWjWTê  ⊢8bAv2kx>Ǿ: úRF*VqXE*wX#fIy{(⡉ a'u -aaï{W l1'u -aa˯{vXGA랬GAlUӖ_d㟍=g@luO68sYh P^J2z$ XG1Eʆ @APW[ q%lb N5 /gdbuyeË 3$fõw' ϡc^CK]0I Kv%2ٿ.‡Kb x4 W(X 0u32]PAa0 p I.:1yoP~ " *ƤV4?Y0Ĥa;3ρg@65,k.#g;H9Ip8lX?`_ C:W!0XLUg*Lۂq9Q\s0.cEzj'Fd85sR |Uh\2F!X)L׫XHC:\Bץ21Crq2%>r_bC1Ƶȵg 5wk!U n K.IX8\vҘ)5`)pm S}U-XwPuGA3A( ~e0+js7J+e$qeW (5tNVu0`Qq`8c8 P)ٍeAX/gSg; d YvD}ZD ?]0KXQ)S!"D:Xgϳ87Ye0UaIq!θxD\`9[CV ᮬim%J yNp̵ $#";YbT1ef$ Jna~B#K=7یe Fs4ݭ㏖Ϸ^__=l2X Ta=+8|ր"62ؤq{Q+V2\$^e^ _zu b:+B_P)&it-4_Z8lampZ?ol(C&㎜dlRm*9Z`#H>8)+b >lǣ«Ip6 |W LDvǭqFbL34q7?Cq4?{ܳAө琺ko&o'ᘇm8ed4#YpQ Aw׉V|g`J!1YN0e8dwmބT q6Eض/{H~s̩F(Ċ/;ǰPuie !hhv!pf8UUu#llR8vZ F1߸.j]8T) lýc8YYj9jln囹kv6nH'8`]#Ă'LU[{r{8lݾ@w '-% sC}+wv׮Wj|SL1,x1HvlXdɘc8Hy_EĂ'sI/=+kX4ЫCޮWÂ[n J{r+duuUtu*Igܤ>?`\\-8oca˭@S79I|[]sdr]TWJ8 tpqx:e–+^Ű.n/W₼ŦѥNN # CeخoXqhan} ~gX,L|Xh3Amfȹ98M>Tl@lnJNx!$GJyov7.Ѷ"U +*gV V+xٔȰ5.9#boR`/AʰA_$`p{Β9̞)4(/4TA$^& m$  ֙Ȱ.#b$^& m$C_{Al3,|̷ӜNkI帱Ѧ&Q2f{ 7#PNs 7>T]zq9n)k(TiAw ƫt@~<%MBH*:CfRfۂ̶!e6(&,G_V2UA'4ScRĉ :| 4<7qL1aHu3!QX5zT)|N}x k`PBt^c>2!koF'}ǀ"dBYwk6Q _AF\7D힙; )05.LMT=FI}mAle߸)Mn7Q>8Ȏ) n7QT0lr&03tFNVR,i)ĖͲ́ԝ\)Sܝ&Vӳ\PtMB S)SvJ/o/R!&{;TaZ OBA6OVعQ}J:A}8A^vJQ:ۤCi*(urwW6qkb'jp֖Q>cG,B{ZҭX7pճ됉~"Kg;S]x;6OXA*_%DC)DZ+Ո#|p9Ak͈~+Tt=D٪qfW*lJDn38`*N !W㖥2RI .~"Q$R@#kM:6;(:EJl&`f;(dM' 艘ڌ鉍\>yt)~_g+\Jl .#Wxb¤ֽ7}t+9mB,:OW0ؑyePeQ\bN( 1)!b"hhRB 4,Zy4*- k /e=~R%3@Jh( D `RRvUul|ീwW Gwrs)dH/&jU8)A+ ݖZa0f,6os&f<+ IA)LلtF),XÈtMNx\308 g$AOVMh]_k-\NNP%s((QDzHw-;| 6 S;AC%j+Pڭx\Yޙ6Y8ͅQ*X\'FilOGa]2.ü!OWDO^ǮeG"܌sƢt!Pl' lv(e!/s(ݳDtVs[#j爡92Uhb Jו5w&VM@X>s Y{C2e;Mx6j?F{Ama2LnR>mAZAV%h]@kиlA׵L< {kkiVO'AfۈZЬ=AU[(9ij5ܿhzFh4\*s oly,=lI{q0s7dɰꗉc@QAYHK_E$Œ`Q|oOvϟ_ֶP?;߃P-'܃1PAts!ߔplQ7À^tɟy{eeRiP1R> /.Zu;CT ŷ"^v}`M'K[VWps"B[ᖷr\U}VZw6Fx2b&B* Ҋ_U zHHHɳYcI+!{)Gz 86(,/xw-*V Y $Ryx݀*(J` '*89NTё|r6ld\%IƾÖh 9*@KЀ\:$51I`AMwn+;וd X1'tZS3iJkJQ:)K)c34%5FNu0Y/;6PKC2kej9 Wp&=(]Mޥ 7@|pQv =s B;m`c(y]P/$p⦠3?eszBpk[p9T=l;zq'v*J0vt ePO( uPOH[{6&oֳP&KCPO*ۈUU}-l zB0ʫqI"S儲^&tLv6WOPJj"];&x5A-W))'LDrPv_۔Ϡnl=;g)Hi' Ofx_ h^hYO~9<ݪh gDyzւI`'Z I&4zYh=$A= ePMȂrB1PN8TO(OzB;`•'k_OH X<,-DZ^>,MH7yiIu-'w%c 'XStdiPҦ"@ҁzZV=!H=!'1zAfZ6Y<MQ'Z Jw WOʯ[P8gA݂B}Z܀i@hPB>TP4P۝kw,"T7\vV ng\SSPŅP6V"-.q > _6mbi̧n>mt_]5ҤP?i|-OkAna?P,((⽂B{\ M8jϒ$ ޟ>Bؑ]ٟ9$Gˇ>>;g+ n=#lODuWB~eOܤ)  cN_J!Qw;(a `̟J)&"Hqgv٠ YP6h|٠US5(|ՠW sE4D$LYxgts sh̏L$ReIbE _4kH͠ jTA͠ijdWEݹlj>棚v fٙjP0?,Q#[yigTޑE#e5.1LV ;+IgcAH[GE7%D&OM`]48$(s5.VQreW$^4ؚ,g+ldB@\5X2(;jr_K{7\bH0v+ U%*^g޼}qOs+S@}TeRA]o J{ć[*8:P3L0B Du­(:\l0J)nh%,\^vh.܋!:|H_'R~%ثHU<iVzTYE+qX;wJ*kX{EByvSHBKV*UpR-"ŴHQ;u`{T&TUhF؄D= pp8k$&0/ È) W# ¶VuXO\$)8kOj%֩drdyt[w[řNB怬a8<æZY%iF䏪B𱌩QtM2 #7͸n 'j1%/&X!o d___jLRc LvTux)k;VH)= n=S .W` L R8'lhrF?аlPCf24pl8>ٚVT@Kq6"XS *i2϶C "Ïs "-JJ_5KB$f!aڱLRd֣J7ppŚ8 d@ 'G; QSn܅hIu~TR& |`Pv Z-&+j2F#/dZAm%,- )ƹoҜ P~$ ع85+HdxE1XkTcSATcBLߪ c !F:œœpVZJ\e; ʮB![, ߭/WG쳸0H#f+'^Ku{RIy"ݤ')CMs 5C:[3t.S3^\1a& 'dٚOT1d8eC:|Re C7sH$ s& ʍ,(& 4ihbݤ!zT' 1i9C:+ ж3[ Vΐ9C(!g)C KL7eH}x)Cd ֣! 9Cy9CGAr( jimRsL5ȏ:!a( ۅ00n_'#Qح\tİncW0'I0<Ó;grGi݀(P`F9Q(3k ➽&i׹0[&h\p9Ah3Z-4a_4p<4v 3[&dL6?xڌk 6odOu9=ҩn -ң @5ZNw7t-qDu㧕k66k[8qj#Dդt7cu pðZqcw_I%嗫yYf[j8I'QLדʾ5rnu&X/3Dž#W a濣qt/r#2F9dӞN' e6& Sr+I3d h$/$ FFjǑ8Bd!I`5uic4XqS@VbpTaDτq)Q)h$7|IFc.6 $GM< _ŦÛ=| LL7NtBK,]l ^ @8t3t!h܀$` ȕ yR5Ә\^o7/Y6 p #.`jԫk3 `m@k$᯽XE[ CZ D/fb #W@ZLF,fL XJDL[l "bYAD,dDb6-Du&o6:bV-8r $DĞΣŬ[lpJxt2"1+G]abRѫXUg4̢]\=tsSK#]\uR7C43?z10ܢyGx ķQczv͢D5ҘI:AUj"t*e~dS2 ͋*E2H#q'p $[@opu9kɦGM&ڟ(ȳQnT^Zfx*8וRfJ5t44ZpsO%w#AGa^*2.{ ip%w3w$U(NRp22 + TCvmFv띉!%gknijnբz6Q֦Xsڞ3Qp5XZpaKl|5yf8N#Ɉh[[htD{mIT`guT9uE%|l3ܨZW,]fQ]ht!*~":(x v{p;OaPpou j+Y(%gxe9㏊!9Nú0DXWY27+AB !4!FCBgk[<()[(U,˒3P%ֿBS*dXSC$OU5WU66Q pk E*$a4*(d;D qK6Vp dΰ Ӣ ʸ'6<1m^}~ &w&Q*_5NEJ9rVK91_x=.$s 2+w YDPMθ*=g50~1!ݵEU,s>ڐlRwK1&y[T6*Di r`gJp#Bt.<瀔@RեgCʘi\y*#Vw.M; #U<@#܄st"P4o؝byn;ZF,Zuw"!ǕC@@LZZ$ ueԝhi P0s YɉC2m"oh u[ҔfM!hRusFHqc *b8rU(Ap, |`8׀5DpՓvJܺ*7YG(?l5ܿ0Gu+єR9ի7B`8O~`&ԟJGVnRc[^Zk*ir#ٵwèɂ @*ƾNaa%üF6OMHu!' @r0}0rSHO-S=bm%3AQ/KPCL MxiE~]hUX^Aŷ"^v}`M¸A}d|.w.Bh+"QRh^ЂQpKBh;9_RFK$J`oX6_U {HJH3c+!v!- "Cz'Q>KEDG.6T ?7^M'aR ȁ(ڃ *ރ#Z?g-4ܝqX .})ޭ6,*4rF+YH1!\"={Qg$e+N4Tr!ۿ$v{QUTJVr*Jfm1*ќ@$ݷg*% D !JQd1 D}kOWlT8>ze@x\-lZ8YS#<>om$S+3+^Z`159{Τ|4J=$SRW ~S`UhW Hzt7b DѪ@wQ8E^:>++elc{ͩ^8>n̏ުQEez)ܤ)@$ ׫X~"=o 7?P;?4IN;{&QuoNW cJ@> iLATQjչk ?.Aco\ⱼGu!jcV݌sJ[YmQ)b5imJ'R|1+F<[9W_H|t(GlM{V(rDyiWt#n^ػ, o( j5혊vPw[X[\_Z2iOs!Ү\Rb`eC 6;S} ܴ&,Z_q('YZ@g)^!R(^rL.1Twq/QZ'!^|qXYq%.!uٷGk%)1K*^4E7qob5řbybQdQ#WCc`[" 1W<_b!8ȕ+լgzp=W@2;E_;bPYPxMVFp \Y6Qpa)mH *$Z`iy5 #%pI |oW |K -ķSl8 D A/\qW & fNj"$Pn!ZT&U BɈ(;DWvL3 "XjJF ̖.Az@h(B9bv۶3XƥnR*0DPOBMay! FK/AtLlrPnfة%ʝ9?ZY2r wּ!0M{4x4Hڠ8TdII|5mK \NXil%^Eg* 25R4Bgsz;`=)N#WV'jhk=t{NBseoJ[P6dHW/~Wn~%$6W,`Ŕ>]lUi'E'(N#@`Ob ?@胯KeYiެ<I7y o:ւ,r0uf92w1VManMv&) S1`A-qUHޤ>qf rB2ZȐY)ll%EWݸ1: 6?SH8c&=X{jn@*0#[y6: ͦo Menɳ֭ڟG?2̢<Ã6sTժZN.ϰXze`pf^) x7@x}Ҁ;@]-XqT^apJDee#o 8:rDF"m!;ۓ˿:iU_`GY:ث,q]{q)yl;FL&uBT u-X,Ą>:7 .>ۉ*_Ta% {Ra5tXj9<mhi+tDydyOtnf{>?ąm=4v[}ѷxsۣ2#H' ܟ8>U:4}?;G`_ܓBXfhlO!OIE8Ǟ UT&Sfe=AejU*ɊO"l)f\2[}ƣb h1$1 6=l{k Z41eI `Iܙ7,8 a]WD92+: )R!|Al&09!pA@s5re޵=~UH$?C!8@pQ+C`ΐc|C\S1Gѡ+tnBnR2JK⃈Z t!_2l˄TDlF+n"nr 9GBƙ?1M6dt`N&Vr Ɔ; h$v7p#S8R?SƦ x3á ,1ߙMlr3OyؽV)>1oSrnG[1=U{SV637WW54uЊE K|ZrܬLҭإ[@ۂ$jbvg&־57>M-.,H`FZ *=R 9$W17z2x=*6*ٷ񐜠j||X Ʌ'W,o!drsūJ7[Ϟ5NsiJ,!|5Q•sWƯoi||ҜS#'͵ W`wC4[ ^ErX{Va< r~䶼ײ^jE|o hvP;27yO.tdX;JIt0ծC*eZd- wNlSJ~|{z ~XC|+4I<.jmæ"69MAS@GϦ{n3nbp밙c;.W.)ETj 5 yLs7}Goon vkH7Edl` 76BGK+uמ3z!IhӢЎ$%[_s= ZybJ,&LkZ0pF|:_t+3_m$//䍯WpjPA|{^4_2_n&7yR6suFteO|S`+-#xB||$xZ2$Ukۙ kl4"C 4sv#W%QX}N(nc.KԗM{ju]ԗ/b@$lR0fã(tvײމ6ģ4 S!0F#nl#ҊBnUwD,"-(/4P> .VMVW/ d yR-ٴuZnR@lTjR+6⳹^9/N ?L|o߯7V^97Ӥ?脅6 tAJވ|oegaa?B|FDt?=?|*KNJ4;pAB?_.  |B} o@+/VהӛLTympT|^eU6%fAOƧG[ך .d:2u? 2AKrXG(-sـ:!Ɔ()6*` +!~o8]|,:|Rp!ii[Fsت*OܩU< (v)!KH8x0qj^jUkXLyI/N%~õ1ռ`_ -[8dQdi["ѭR݉!]mCU:)v:w[mLZ%a-}ӄ?3GƤL l+tr5&^P 7ba?M~rP9 K2Ĭ@R ;@tz8f+ rx8@-u _=KKDe:eeB0IºT܅[<زRunjV,I݅%vbV4n`!@t-UVwD ~-wF5OrqUD:nսBsX;\`Y僩OgGReY僩x ]r?gX>x~J-w"@6U[:U/NԖ*lݲ!` [Q/+j`yuu+S` b`)$ CU. -RtKJ4/ưJ|+T -ªL±)U^//uzV沣.C̊-L^= P%=N5-Y@z -"t 1J5fqc# [(Du/4; |3v bV,n$unBd ݒUUTiV6Н)X@[d L(ԋPN4 -T܅kXy>+hx0uqnP{* -UT^.,?p-@[i@WbDQ[go_ϯ߾o={v_||7勿O?믾_o޴x_ճg~7WWWu~z?_~lw}?!|yױƛi9}?~OTMu>7û??G}߼z~廷Qg߿xwy~o^nӘ7Ô< W߿{-޿~o!g<Y~)!Oϯ 7 )Я{?~^"?_ۿVAI7urvMw/0O)Ƴ+W~~~xr1&p02Mir]!z~]˗/E|6:c;1m};xO_p3eWa/?ϫ,\-E~F_V^b(/k"_k|ϯo:yym{ߞS?q0ۿ:~6kd?^\2{\_ 5q6sģb..q$ vpqOkCsOE)6L+7'k܇bēb..q&B?Opŷ^^,vqgP/aVh+\ŷY|+]<% zZ\^7Yl/)1@WozC{5| G*tqЫO`⛆ bCPc1BE Q!bE tԎ8j>@-ax*KR+vDS# mO wAEbQ jGOL5DojOQjŮ(jP'AmA5>赮Va_ ළ5Ӈ6J5LPYwAUUxpZ ZkN5p֠ O[+֐6D\<ms յRu 3tƝkP'-AU.Gi`{ 3qڡ\%?A/'vH_UIU՟>襾vD_ ׎*A z׎k#}UU(AoWU}P_;z%56׆k6D_jCm} .q˔܉FUU]k5;רzP}[_*A}K}m܁Ff;רJP}[_j@}K}m܁kN5 pר P1׃G5tt‘(lT=~?P8AFՅRU_jl46&?ب*Q[ejE}KU'*U3JolT~?8CFՏRU ~ ?Q GJRߠ"a/'Z[6HkUE Vu>6Dm TH!{ުԇƞmz#U%)doP\Ւ=QQդ措(uaN3v3w| VU__jnO> vð3 U~?1B措:5wT~?1B措A5wT%~?qUJokTRs~5';Qը>6@s5'; Qu>6@s5';Q>6@sjncO4wT)djO}Kmf=\UBU YjnJ\s39\UOe܎hn#U)o\UͭPs;~B ZTRs;~jnO4wR-*ſ*R}KN܎hzT[s'դ͝FhZT[s'U͝h*T~[s'ա͝"ƞhS~[s'U͝F=IN=a/5;MPs{N!m͝Ty>K͝'57\s'Uҟ͝T_jn4wJPs;~O)m͝T_jnO3܎UJ[3OYjnO;3܎ijQ)~"܎hn#U=*o\դ-Ps}E jTRs{jnc4WUAsU^jncO4Bmܤ TRs{iM<57ԇhn Ps{IU&U/Rs57eUknR*<ܤSRs;~)Rͭ\sO)mMB/5?ܤ*T[sjQ}K&d|TJF}T_jnOV}?Q=*ſI5>6@s2'T oknR5{=ܤ &UBۚT^jnc47E({IAsU^jncO4W C#U)do\՞UPHsUu 4W.i_j'j ~);4WKڣ?/5',7hP}Kj6ʂh*T[sgբ͝t?YΪH/5;( ~Q)~ÄjR}KmɆZ2G#E KFa/5; ){P!m͝U^jnc4wV3RDsg՟Bۚ;Շh Ϊ<5wV{=YI͝Uu okSa[YJѯ{Uuṳ*PKj\ŸT[tgU:U StUJLWNvDu̔?R]դRUe麝?q]5e\WuU_nOdWM#U]*o]U:jqnVM*mYUN6@w2'U onVE| >ݬ fբB۾U_ o7*|"YfURx0`jj fY5 Y5NjʄoVe*¿YNjʄoV}*¿mYNjʄnV*¿-YUNjʄnV*¿YUNv@vZ'UJonVu{=q]5EeadWtU^ngOTWMQY#UE*ao]U:USTHuUJܢjT[sP_J5)* {EuU:jnQSTDs*P m-=ubAs+ܢ,՞ۚ[TuVEMQY-;%5ԇ57EmQU: ܎h2G Tߠ?u¯5'L7hPk9*~FE ZT'Zs;~jʄi*RmͭFu¯5[>ܪjT[sPkmV5EeaO4ܪ T'Zs{UMQYͭ@%5ԉhnUSTDsjO mͭ:ub5[=ܪS~[sjN}OZscƚ[*q[U{NOk_82nUSt{tZ2%@L&-UN h ٭jʔݪTVե:%}'|]*SwSi[UꔀVޞPުL 孪T WN hm UTV7jUkmq* ~$U ZU'}~jʂjU!~V ߆诚GZU`Uu¯ o' \;5x̓yp<؃"(m^dB/wM@8 {!l+r's⤝xk*="e? aۊàZX/7Me@x lr'3͸%8 jʒQ*܍&eL妩/Wz0m=ja~~4*ܐ&meLVRd5qeI)jaaEV=OV)PdtA {YULe"6)=jSdU,:Y""+SLU-KC(jfBЊC@L! E!9v)=A`B@T;KC0(rPSZ[DZ²d9nF`P䠺Y""5eI)rP,LAN hEn Ej˒RZYAjeЊ 0%AU0"U:%%@9y,KH*daEI kENWVV#T%A;9R)=AeB@T+KC0(rPSZ{D29b`P䠪Y""5e )fiEVSZ{Hz)Ȫ!XYճN!hEn! EV Z "nF`Qd:%%YmhY`YEU1딀VRd5eI)jeaEuJ@+rK(rTkZ"GU U%딀VQ&, E 9F'y"G5AݡQUEu A+r(rT["G U/VQfL! E!9j)=QmB@U7KC0(rTSZ{D29v`PY""Ge)rT,QuN hEn Ejz˒R䨊YA*fЊ %Q0"V)-j˒SdU,*Y""1.KLU! (jd}Zsܡjk5\(d3i"R)=jSd,zY""jRQ4"u A+r(򨆺L! EU7KC0(Y""jRQ4"u A+r (6, EU7 #0(Y""j˒RQ0"uJ@+rK(ƻ, EU+ 0(ZY""j˒RQU0"uJ@+rK(򨆼, EU! 0(YȣZ LZC<CgUɮg $q抜XjcܡYUU)VQL! EΪ!9^)=Y텙B@U1KC0(rVլSZ{D 39n`PY""gf )rV, YճN!hEn!Ej.̒R䬺YAfЊ V%Y0"gU:%%@90KHjeaEΪuJ@+rK(rV[a"gU U%딀VRdfI)*daEVOV1Pd5(d3EVSZ{H^)Ȫ!XYN!hE! EV{a"b`QdU:Y텙B@\T7KC0(rQSZ{D 3v`PY""5f)rQ,EuN hEn E.j+̒R䢊YA*fЊ \V%E0":%%@0KHdaE.uJ@+rK(rQ[a"U \T#@֊2N^\dy~X;wȔž/2ML~<;yr'ɣ=% {:bD^gzz wtoc?buɄ] ;8\S{kPU}xOG,YxO.,Î=ၥNxOG,+㽓G;UWG)tJ#WϞmG (a';OS{kPUp1da>O;w:4=;LjQ#Lɣ%uxCI[#&L~zxtz wOryd"/3ӽ#O㵣'~+G&xoޯ~݃}GYA5}FuqxѨ}yX)#zyw䀟QG/Go8#6l zy`tz x<`tos?b];8g6[]Ǯ~נz?b=#> Î3!9ZQh0ͧCE6?G<*:g<^*:o9)їݳOC}{ {Pto?bȆ^=9.Q'k੽o8X#2q㸧cDa=# q 8 щ>; }q㞮=='C6{9ɣOo82qן} aLJf5O?xl*|3dP3:g=:o8C6CPN =}COSiTGL:yrg,tq2q/ˇq. i/ /LNq}qĈ>G|Bw>aΟmGL~ogɣo8X 2q/χiǂޟG~}3A';Ag-A6[.==^: dC I2P'{< t?b]LҎ3] iOާG>x>5}v93% }}'V};@w>agΟmG>73q7GL}#? ] sqڿz刭}}w/tDO7刑}}ܟ}N==ڳ'>6oq_O'Ow}>>&T!0U>!4U>!S#|lqw|1}v53!}}gBw|}?OsU=9~O'{ 9g=9o9X}sxF> >+~qNx>xq$ ^w_"޻/Gl_8ww_ϰx}9bFq߻xg}3<z1cCW<3+_{ #Î+ {޻|w';6zwd}œ;a{oܧo8S #FGvw_U[ m'AG}mWU{Րp$1!ji|{{Փo|ܫԇ>Î!LqR;}qR|&ۣc={ѫ>$jB(57!u?f㎏YU5ִJ~1z cVUu>qw|̪&}vܫNrܫԇ>#UUMGWm)x JkǬ:zrܫ& ytܫ pܫԅ~>N;޻jiwvB/Wx*ݟ~i{eN OegW3NGWm)xѫ>'rTj'}TQjy/S ~>&rhӎ`M4ԇ>~iji{e&r2[ݳY'ۣOثq?[R;`{˯DB;II*cd VڌVM+<ӎzDjD+ӷ(M{br쵙lzRt$5d!OR3>ԅU,cV)UeǬRP=:>f\ቜv:'}PO&+7A>q_Ǭ{b{z {Ֆ'߀vq,qR3xqR%RSM8//> _> _/ۿ%Syg_Ӵ\'sc;~?|?Ë_ݛWWcᛯno~#nAypv=dnwGtv˞۝z;tG}]gt l= lft~´B=Ivo4m=AXq>A췻AGʽ_b^?qw췻A_a+:EݏZ? /?ow7pW{h"7G{W{#Eݍ6v^ӑhA_d~}]GEݏzۋ_}__|ՇPKxEݾsvg.breadboard.servo.svgXnF}_Z\X N i>:b U$CRd'_3KhΜ9suN~Yz3_-'`,fu9_^Mʯϵ/zt?N?U]|qiQ_t<~eB01,v{lBK&1O]6ABkvx .k]%.y| .CaX>,8, =-6QRjǸ `ކJ09-SNll>hPRh \îAuJ0+a}0*ăVw?qL;SE >֚i!,'I0&WoWe2b_*>eq/jd*0e!SNEp wG fFێFbC/i2W&dYe^|3g;J }tuҘh\P_R̆Z Ýh^ҲC*}Hq))孔u#0Vc֬)>f + Ck_|,,~WHOt8ۃt;IHK( ;*#43;I~A6L+ñ%#Yr*g?~YxILP) (MU/2bTK!?o]lqA!(<qCnяÄ.h=BfKUnSA_t7jf0ej͙gpte@aJc)QS*f]Hʯa ނrhz +uA Jj%7}^5y7 Ư26Hqu35Reb~Əy4 .HMpkpYNJXci|_;2ŚhmoHhNp(8?4i׈1$_UnQRKcdGr*C>|q~FHbBRdk[`8kǽI_7tǢTߨdl-b8?=bӋ6I]|/417 VJOLѥ<,>Wwu45$PWꤤ̋;%5q|A UnB 3KЂw%:`FgIyO .#q9&hQ3 a׵ :3F3q'#ig/:}挾l ]ʩ㌉B6m,M5iA=Z1Hp?YawqgK?C{SZ&Z:8/|k >_~PKxE; % f)"svg.icon.arduino_Uno_Rev3_icon.svgZko vɝ68րSȫ" _{ TDjh`\rx>ϽsW_oVWG3ݪY\\.ώfw6q_{-/۫oW_[ Vo>f緷/󇇇Wjq3sYLt[@O=VG_ˬb#\޼0)˛r݋rRJ=򷱅xKfoauz{~43$?Η򋷾_-^_' qɗedu\>T,KH4^/o vX-.׋Wuu! Ee_^ زj<~Xme߬ZZd߳5|kbUkV`-z4[OqP>A(N񇃺qؒqjuaԵ)A8m=4 txg%)j~=RߙAS*33Df&TtTqKەSD渀U@'_R'jh`w)&mrGU6'8Q`&dew*8Ql*\laiOq2c%Ӆ5{Z`(:xf0qOYQ)˄A{:Ng"j!s}`"ب jHؚP`YjD,VU"9 'OyQ(V5RLכFi慕_,5^h,5=/:杬u5{Po;g"0K;z"9P~|gfSe|F^\6QT3ywru1pٸڦkZy1NÅ]_m ;Mb̈u/1ԗd~1iŌ=N{ np}_^ @ ;KANԖ6ؗJ{%*-m:,O 8L>zI 8y yJuN.T8@^™Kܔ<+;c@&Ĭ!Po;S֩^j&jAHސ nXCVېzysULKaT,z!o(|F" X;D5bҞ5QfWj:6 %HOkrKseQ!ڟ<;qpΟ~͐?q~ǔ7q .IzLC緖ÈI{iƏM&LR|k64hh@{]Z>;۵|&GKpN޿~$wqMY93CPA>:usCPauQA[g]nv>CzMs>|܋1Hf(݇ڜFL riDwWkFL9yLOTrTCn (M 0[psύdPc7l,G Rn(q!-D! '3s5[۲բL!dQNW}#|މh@aUʉȧ*ckeb\Jqw%NBg/ ^ȶ+ʉ!#dѡabg;}4e'OM.%!t4 +m>sPϖ]9YSFqBE> IL`ݹiD( cm9J@zA6ȠٔحPyhDpAVؐMd5Tq\9Hsi[5$6f# m")˂+(*b /JиgAI0uVKD3 m#ZR 3 CXfO3M{ xN6<$sCGupE4P(C[0:+rjd'Aj-iڒ|~+6ruΰ~j^\˽o-*cJ -9b2Q!&!.%\Z` }̽ l(wC#ctVSGh+EB:1:Td@|.$S(r0".ngF<\*H` #@낕SL7eG2:fT{K"FG#K|a!@MH @(&:ws0`.kPBy,ͽ7Bɠ L$,gw,.-)K܅1:[XH݀e0f` 3DNa[خ=J}/Yz dZr``]ad}OݹD757:Pf#b|,SO}_PKxEA.nsvg.icon.basic_servo.svgXmo\+;V6HmDDiR iGί3s{w' 󞇻;Oݗ;WJU=vտM\uf^~ͻxr8OWn._Vg֒*>cX+RZ ӻRVEg)m%)K-,HgH42W zߪL?_öp9#* ԚlH05ypǢ[IZTul@kbo.i)iwam)AZkѦBkV<<. mKXAF pdQ*%7LWWo%2pLU|X(L`%TJ+a& HypƐ,B 1)e2u4L$\0Kma#6,KNY-R`T!2bXpWsFcBq,ID=6b|KNԒgb>i5d::-H)<ZԈcd!;bEyd_~FF5y^5M,ޚm]7fn͵g#yIIVff;mϫ\R"`f5jvwT*¡JZMʹVg:Ug7'VL3腶3-0g7[oQvc jYhkk\u/U0 D{;sfu۴OsE[VP:cD HNZljI͆g""d]3~I>+ۇIVB~{<-^NMx~+:a",f0;Y^ iy{ݱݙTc$UBs %hi)!87ǽ S21d- Umh*idOӄw Wczi*k ƃ#o(3fB5=SgXWd}/ ǎq@Ty uŒ( .aa98DY e]S\d*vY5 =O%f&Nǒ Q1VQCЩQpq ZD5Y6D 3)@̤/V[8_k?gJThm2Q+Ld-um2aNNxpW)Ʉ 'q_z]4sPKxE nsvg.icon.plain_pcb.svg1o@ ʗ#];tjv[:ra i__*vl9H=~Nܼ<-s4vZj޴ϣ\MWjjiѮ9wiE<.Ç!#'[Lz cwxW˴0%TN#ͤyOR|`&~GM|mfY.ǗY*̳OnQ/Y=}9z<DL!jzl2766j*m lJ^=Bd}/I4Gx1!@UQu_EtTmC) B1Ƥn< G%!HEցz"<BHAa4X }RΔ(rxrO5c< {q_fL%$I!&41p7DRG<%8B2MF%l5Z(e"f0N-W; re??PKxE^y svg.pcb.arduino_Uno_Rev3_pcb.svgŝG?E/! z>D9͡$-`o$4BQbv5ZϗF菏1jYxY]\| ?{.ogw+ EH.˿J mѫ8j>m?_tZE8f6yuهoWl>tv~ֱ^Vls'7ݥOWْw1Ќjsɘudzp$cûܨIlG6el >6D|{^wv \|.uxVpzsA+cuOm#BB,ӂ #M$iBb뻫U<w*|!muw\l d[]2JdAU$9ٖo JdbSeJ!v`+`UxDckV֙ 2KN%%3P/pd'fF^6F^k_J#B 0h V0U Mrvsrà'.@ gm)!iTg\殀A#O +V(6C,+C_E>h|k2eU\ N\m梎ޕ˫J"/|@b#,%Ll%Iqn*mLW\g~c,&z+Ȍ6I2GRʕtXzofڞ}ҏսIo/%!Amm- E7>t შ[9u:`#'jȔ".H*`NR񙗤6EJ)'~!S)XlQ՗*1*:È-zИ|+ A3Yt~(-!2H0`X"ņJL&Y|\/iyfɗ=+bHNUaJOv)%Uӡ4-Ȅ'j[p DxZa˗fx-[ 2je *ʶWjA%Zj3΀2F*np KEO`]ѐT(,ָ)HsR?_ Dtei:`e2 s7ԱtZ:3i^KF'RgXåk{X,K 9TjE0v䰶qRgXO4Bt(43,(ʠU1ޖW!cWb xF9l5<0O^C Q*j,h(. mp{0jߡcO3y9`j %jߗ#W&oʑm.#.]_`By#=׀j0{Ey̨VkY,PQd @?lgyTV3l@-38{h(.`pUF/}6f- QH-b}Pcv;&y @fjf} | gvu0{Ey/+JKUfϰO*f}`Qddb_VOTXV X= B%=~mUJBы|`NMGg I gi0zj>ԻҐ}5,/7үcL;d_Ș)~#2FHVJ"{̫nv~y垙l^ם__ m̼;Ъ-m&T6}P=ݠKӭC;kh70 rխC{$e^yH;m^yHhb/XL uU{wp%c]!W*zZ6_}FL Kj_/kҏ6zOYRzun䘀hK $1첩e{6;wt!lHW'er쓝.6={6@}y<=`~EX$Wl߃AbݔS1q|?|~=RpԹ!!ي_[oKmfd8K*)c$(GwL4\@@OF|y5%=\n{mvW5z4l19 rz0M dLݓ(#dH%SJ}rSf& a\m׊|+ZL 0^@d_yD"_uA/:M kO.6}J94gC(^zO>!ȳAߓAF?lCFp dk -W p8+gy)ꔜgzEya/!_4./W==¶}\gl\xxFzu}z{5z{^?+BH!l¸m&M~))V|tyoŧg?}Wmť5V"2>}hJI}2{ `׊`'s 0gqO:92Q4q?6ǎF(bAB1w*CK bsYFlQĊ؅E)c2̞JQĆX~P / xvM Cd12VI|# XB#3&"at8vZ{pLw{JwrQy 8-<cP  9mm:X¤8|,.h &eMK_B^kgw[ȘF$=&[Ƽ< ޓa렿v̲-ӀLHtErEq%iF9N +wNשz^:EϮXxf4pK]:Jl|'u0 AgB7ΜKd@bgގƠ5X l_)+(/v0xsPKxEr#svg.pcb.pcb-arduino-shield-rev3.svgU[o0~N~>M B5JTZiO7a$ٿ߱C6JJ''-UdeaF(FH4+f;;';FPuܖ}H˩By51!& %Bo.*]̾,1ƛ #E]Gȶ5ٷs,g ¶9>309m5rjEV̹j%vK1F6@0c5(,jeγbqȑaSp0ټvGX곹fL'{ڴԗE+uSii,fu{V/BVJ`frqK2Wq;0] ( 8)5}XPO:y`>~U_"b*iM+VQq3-.ۖ(u+@^x$ڱ/!S ;7$ ^O}J|L(೬Ml4@q澂 @;~Hl7M/VxߊמI\EhwUuKWz~# P?@:DzF277RWPKxERN1svg.schematic.arduino_Uno_Rev3(fix)_schematic.svg՜YsHWqU1 ȩovsxHXV"_=\"֠]Q ==7K.HVau۰t-,cň*`G׿FYi0a.0XW^Bh'ddm3nt QL, cj8].H\a@'V8d }vdq$ Lrfxd="!2čGm3#=Hqh>11Ǚh}6;Lrϵ4=&Nn|rs/)2\ygmue2Vj3BڒMӹjN6uGG0q 2e*$X`T+$s)+ZIƾ}\ f6g>%6h;̲]_rY"rښ|cqY{6'?8e`Y߾رv$|x0+s b q]z+|o?\zA0yWPBgM{zfkŪ:d1ӥ:$`fB!֠\J9t.MEǞ! %Yf6~LxVLcnN#܈0}>du*Aoi /P_. 6KS?7kKgf4y ޠ6iciUЯjKTF`QyQMUxVY;'xgɓN`+L 3_" |xAIV'sLw\m!fϧ{ ;dIŐ|b˝ '>y;]8鰎0¿_c#q(0W[98~+U(;ӫ dMV YMuh?$ tR5rR Py m}l+Yt}+$SZ;QPp@G->8KKZ 2$Cr&TUhH8:(ﴞ8])@J|(+@V)В1rCjUhm{ k"yNP0cGL_< ҼN#,CF k2qE2PTZqZUS50m6NV|HmHNA~vyNCwϱ!Ac6;:߼DW@]w¢U8ƕ=w*wU>!wZa$\DBE'!_3Y"}rPG؅ 3/~,K3MGo)$N8N\y/* }\N2˂Y,Y]/PKxE)C-WTwitterSaurus.fz}ksGXNnQGX-k-˻)ɿV~M$͉]UY'dUW?a?ƣ'Ο̝뛿=^`W>sssw*NhwO{]{}ؙR֠oίF'w'SNPOzpz0gM/Ik6 O+t߆JFYw WO~08]^N[WВ3f~̝3κuZwLx~RE5ίUKO> RtG~w}ͦwN7ޤߝ'fY~8W0Cדi*,Q<?ԅFЮSp|2Ǔ'?dp~}>?Lh6*/ew6=`ZW{ͭx_zFϠw37ntTP{;Š?G~VuY<+ Ź~=?c0/OicC箺_w&zڹN>A?󳿮jA~n|" *[ÜԟԵ8>d>?&Z=?f $9W)@! #}>0>VA~k]Ia< QuED,jc!_d}*Իb C]*לuM[4,ǭ5bmwΦ=TYS"xQ{MB(G]|n, -XtMŲ -Wdk:ToСr柣2Z]ٳkV`gWHHo|u՟Yt6GO %˝vfX3opIyD1ƕuC>0~B˲a_&3L>jbR\71"A`)̄ = $4=O.[K._r" !҉tA &K>W{؝kU`:xgg>\nT OwN1L|v6B{m])'l83NZg zzNӫ04s]gt,=}KT=<;=no Sċ*ىX"[Զ%o@+~ND<@mg9p-\InUk_b}>eeᘍk/'G勶 jcF G<~}9wЩV-w/jw8V5ZMQţ]G}8jt K>sɧK>&߄t7? V/S0.y,S(-ܼTae<CzS<jsE[zۿ+W2OCҢ-##\„^cċ}:7KZٹ xHvCsSzY˹xK90E+;$S4/[ߞ[ƿ9=8SHLQɜ ^l~/J ^Թo\ڰEoj^\j8S{8XCXRXWq>o5%^4>>T<aՋ iH4se5b0: Ӌ0e+bbΌ3.־t FNO3vCzۿ,u\{<[a[dz25p H1)r?3F!G̾>~7GBhבze&$N3Ra?*x43ڽw改S+O9g=}4`<о,ówU}[Kc 'wYqw396L=rNjB-\`+׿$$>CXf;qb펋N!"݄,!赾]j<ύﲚKg=spVL>~I`78~fæbMѢySx[+1dY8æ>A8 bWH?uT`:`ѰMEwC}=iݛr27 jaJ@+-62P o|[`d<Ɨٛ9Xm!kב!FþO}0hצbAGC z/Iv m|S|a]DŽKCHHxP{.'%@{m+l4k?T"sm*V/d4kt8W,D/׃8lz1O7|V[wwna侊]MgZe nS,Mz}.kclF录wXgYzl.OKo!a/<$ )h2vШVIiHls0u ;fh8أcS`9XpGj27;'ژ[xtiGhV.Qx<+]c9ud,$Z<+i4kSzpYM?xW&^//hgCV]gͮt>|xr;0˭dһ(]cqZ\tw3c쟭ز4N{~ l|y}dg?Ct6Ͳ~:.uc(ůN辞Pu4?tY[}Ȃ^_wSa~wMB뜗g6v~UȲG^B @63k^A}?o j5znXYgr!ݞ^C?D8{>hJ(A|-0iXTιH"m,& sLJ_4Z7t0=VFϿeśV_~/Z_eEoohw:WIbЛvBN,ntv|eY?OQ}}px"jq|jS}Of_ُVN'ߢk_BAz0>sui1Թ6ӹ0"Lf_g *`ád&<1a0Ez,^v/[*9뭍*1t7r]z_d&|,xhy|`6 =jIl*wRqvx<%C =ZjąʥømLk3Am2vRXE,wr8TeA^} Ox:PB^zECa0ncÿ^nȅs$>:wBpͣx,dltxP;;ЈhVh"d*!IAʵ \}C&rd*#=OCF- 8raDhSNe L U:.%7!46i&wqNcKOcF^pE/v)KXfiUNT΢ Ëp(Dᱱ5jèŮs< t2xFȢc)􋀕6_R$=z1|=req⋥\swh[(4ة(U#0$ď794v>fz@cMlffɆwE dŞ0: @ M <UShI۵Nz...;(`*ՕoBb(QNႴR)$Ht(r_P|Gpnԋ?ޕpCT͜8#~==\=㌵_Zh!!vL]%n)%F]\B^{Șj L28 7H$a ])L1p.S|",-Y4w+8K)Zq>>Y )qQnrU~F;QsvCA=ѵFW xtW[s' Q@\^<9jKj|eB]$$D9Mq1y8 =Nv!%6}{ ;&gf3LStL= $,9ԁ8F1n2A{t`]kf^Զy`ށTsQ3{ ."N܂'%/K;ֽ8G6tW|vCXa#+! ^&Xr"3DWE G=@MxI1,#&tGIqy *(B9fBQJp3) zd$'9dLwF pm! txEu E!7$my N$ԑiJE HO"+˜AuMWPtWn@!{O{J0ܑ| M*19 ?QTƃCuejTb @%5, Jcd!'$͜SXH)ͥ(HfY(92P jR4j#05E%3P2v8y۳ Y]8< , Gy쵄Ǣ_y+ZdD _%d1t$[>ddQ G$^èڬUG#!/K($5#ie)TOXTҩ 1h;#!hP$eU I+Z`|oC{E";#9 ,xa`$ FyYEH$hiU1"B($~0k H@oȀ䡡H<(4B8 !+$-BNDtb,2D(87wxH[%2B$8̗JGF b7)b&a$ ]&$"J5Yb 8F%AmCѮ4P\JKd֣1EL Frc (|$3 BF*b%'<"s qo_qD(0z?b"Z>mU:-sM fU%7b6OSSHDUvoO!lH Fnl@_(N eF!)RD<t#wMJD6MBL8@j$BLØ[bCM𓘾`@8 (X3pV[O #Y$Ɣj"E _M"H |Qr&DkJ1 ̀|x`J2Zu`4&%93ԵkYBE3R',cĸXM  |2="GO\〟:uRY6iIɈZ4iԆWqj T0Jxz=E[&9"Rq mj@]1ikes΄F̄mϼ\ S0"6הp.*9B ,j~FiQA zz* R4Z2V(axJ-s* )D¯(99yu_"}#pj;9ىQEtABpLZ")c9=9B0ڦPCCM##<;la 5 KEI?Ncuqst<R=DCyxH4 CFHcb.;.ǡ2P ^`a`ȲJ(.cH$iVR:4@9xJ? '£_`ymjM켈lMD."ST"$C$o+uqohT6@%sX8dť/Ed<!$ J B`2q`>e T3T="Y>8r"Ri T/fd dDɉ*WL#eiIb {zۊ:l jBLi'zj 6]ȰEyLt|i\9_u5˳Hou)L'. ɨ4KibVՄ1݂0=8Q%An/+38\b<t,,oK)],ô,ӘLIo2K_{VLE5qM$p.R+[u傰3'ewd``t)}..a笸^-nS0__Zaq3ڽ_&ݥR*8aG!d\=f|eLxhG>ԅ}屾rUh*$}Q{әm ^nzwOrܢ-ZX?9V|sӣzzOп0h}fz%ۋ-]ꛏGGQd[(>JR>O91'hMs2.*12.V~jm>:ߖGDYE}tj rVf7%&]ԃ;D|uc :}pY+?[/-F>Z 8z='Vo. -X~D݆O⣨ۈ׶ކx/V}/YhLD>4W"_G _-37ˢ^Pᰣa]$~Ik h !f8#!<0DŽu_2PoEF3Uɽ<$*0-2jy![GoH(+Ōdmb4S[Aʴ̮+g!XGaYZK,Ke䆯^rUIg=[Ջ^W_6:Dqp-q(hXFúOm=b-Y]5knxaFwoʥITZZG>P},0~0߰-uGQ_6*/OzDfmCS*"cBv7_k½`6UuPKxE) oTwittersaurus.ino5 0D9(Y?[]hl79y03O xal#Kqz! WjROK]czQl'뛒o|07ulÙ%dи.*ԿJiPKxEug6=$part.arduino-shield_r3_two_layer.fzpPKxEW Apart.arduino_Uno_Rev3(fix).fzpPKxEmY(H"k part.Half_breadboard.fzpPKxEXRH] R0part.servo.fzpPKxE>6Kz.2svg.breadboard.arduino_Uno_Rev3_breadboard.svgPKxE%%k.r%ƭsvg.breadboard.Half_breadboard56a.svgPKxEݾtsvg.breadboard.servo.svgPKxE; % f)"Xsvg.icon.arduino_Uno_Rev3_icon.svgPKxEA.nsvg.icon.basic_servo.svgPKxE nasvg.icon.plain_pcb.svgPKxE^y svg.pcb.arduino_Uno_Rev3_pcb.svgPKxEb9_M svg.pcb.jumper_3_100mil_pcb.svgPKxEr# svg.pcb.pcb-arduino-shield-rev3.svgPKxERN1<svg.schematic.arduino_Uno_Rev3(fix)_schematic.svgPKxEwPl;H !ssvg.schematic.servo_schematic.svgPKxE)C-WTwitterSaurus.fzPKxE) oHTwittersaurus.inoPKIVerstaerkung.fzz000066400000000000000000003131701272363601300332450ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/FritzingPKxEug6=$part.arduino-shield_r3_two_layer.fzpTQk1 ~߯0~iCa0ǥS}}i_?پdIۅX'O7a[ɔ3kѮ}(ٹv0+ krɑK]K7ɫv^e& B2[|rVM'EvЅ5L|z̆jvTkTs\z?qWLrYpu%&3=u!Kݡq0-{ѥ]g@8S'GQB_uN06Nq269Ҙbi>e--1܋_b`(J!aԃcP]G'— 0;<')a6XO۝\ Q828fM焃XqidhM.$?e+q(ͨv*C'?!.whuڸmOuNLfZ4oCtmYc/a?\9ZYŒ&:@aٝnohɇY:MeS/Y4ZܙӈvEcDъO`Ix$hBc6Csb ڳ]t xX򶍮1:h<~&V&?r?F=.tRMJE_(3ߞA2,<3P%h" ")IoDl*j&#;]]DKFgLPȀ%h&IKTdO:@_8 Jd ^BL*%%E5Ey*tV"\pTfnZH*Xctqws љɊf,^wFbaٔ$GT"o t,p ddu fqd _@ g84I$CI~#&[}SL(tLfftM +K4Di][% Z usLamoeLC'B PGy,yƪ  ¡b[L8k!ل~&&h~)dF#na0< @˪YmX֟lEJxuT,oVYwLef{:o]ÌmE)ytg/oA7Tx|J*V­D2/rCO7\jGíY*lf3|uzjZ6:+az SUd]naîFFqحaW/z*ܛ.8K95rhš]C[/v=Mq+8ra({U8WC{S:5](-4ţSGG/N=M 5GiGl X/ H-c0ҹlIH{?xTkKRzylT:b[2U4>j{V'0N |;s"щRq }_^H'Lv0)IIcSq!e IH`D1A'}!:rH`@K}VֽWsp8)I󱁃 ‰W#Tb>͚{o&=057&vv־_sI`Nf\pJ/Bd:1j4[`FONqn<[`O`Ny66<#nJ#m\bʿőh HiJJq\ʿ1*Q#R2 +tusW:5㚗UlJmMtj]B\2!|k^'T7F(j[bkdvڵB ^,4f!&2{ցD',.I%.5G0ɥ¿1.f_G ܯɣ¿1?tDfV"Rs5T7F?TRs8jRoJ1OY@*}!5AOǺt# t2iVT͑R3ңoMkݖ3) t2iVTqR3ΣoM>H]bV\TqR3oM\w0 'frHX!5c9*ƨrR"4!7DsԌߨqH{4+zC*Eo Q7FeTT!b8Ds ԌDuhVTR3~oF bҿ7蚵q+m\{Fpḕv8[sobӻ+ZHɣY{ռqkmT7+\4n=yOӨ5k֮ƭq5jܚc<Ѭhnhhnh*xyumP&@MA͛h*x,n}.xHdumP>@}]u Sϒ`3n45Gx/~x5ƈlv*ѻ>lZfE z j+j 1- b^WT9JrzWȉŸ=B 9F 9V<BQԫ,WD:?^}z]/Ŵ:V,?PKxE>L[Ԇ4 part.basic_fet_n.fzpVn0}WX~'`JPݪaҾLS' [@ HKk CRj,H('R5~MD<٦4 #xJGۻ%.{Goe[ubĊ)(tƔ~&+%G_bͻDw%l);2 3" >y!PV ێKxV(L4f7y'"kєmF B)\:!21l-p?a~ďO7+t^wƩ=fWxs;p i"JeaKbQ "JR\Ad4E2αY^YE=fgW[=vevW]&Ԃ%˜d|8a}kpcZZȘ|lWCLA#"Z)M7582}ՅhOڟt?S(39P"Yu#8EȺy3 we-Йa{sG詍zRa,$à!{G |bid4=CtI(YҶɠ2qəd&La#so5@::<ᙄO:$R%E 9:qwDoo̲߱$Gr%.%*nI|:G*:H L?hYWp2Cw^M&㺊t6 dmFlOx4 % s1ޫF$&=H攨qBKp!;@,1<鯟c事Fc朁2-fYm305nڦTBFCJ9n f*L hZ'tc!O£9*%^%=ANGf"L%jD<Ȃ1wNf㱃n6Jֈ]NxDHwI 'ssNYa~4 &ByE4up &N Qa1"K|?:E74 rE!ƻ݌6뢎6#k֊|At;קN,ȀGB--ڽ C=qӇ oq`(BZq5HĹEf3J)E%\nk;H."Lft5:\;=lE> %6Zdbm]12 bX$\Mjx},ܪ3l w[&ϴSMLU.mzVmzM6զoڼnm6tc PKxEQVC{Y part.dc_motor.fzpVMo0 WײbŶXa+SlU YёCi|GwJ2bq(T9Rl?'/_=|o?'>8y_7>}vhgpz{On>[zEz}rOZzOOE柳7wٝ_zyg/Ngwח.~ɣO]|rv_gw|y}ݛW ڧqv/|^x?λ;~yyˋ?_v积.uqzWogw~6g~5|Qo6ū//>~/xsxrAW׿r˗~ux#sy9>?nM'ynsl^?ݧ8*z1:Wyg2^=/*~y(޼W/L^1h%~n:ͥn^ٽ^\/ΟW^kyw Q/NOn~?7=8}S{~@_]=(x?7Wzrݛ?G!vgk/em?Ƿ39/SK_K*z_a_Rҿ^>mX׻ͼoO.;/SKK.zaRҿ^>oX~o^)av)y?D/0,=JǹatCCCYJER*}.r]GPhRs">""GJF9949T\0ȡȡ,"E}D Ef)>9.r#rh(r4KatCCCYJE)=E.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29Sz\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K2]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhNu.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CY:"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GtJsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef9Ů =z' j~rkћ9w8wy#{7 a.tkћ1:w(Lwy#{7 anukћ:wx`wy#{7 avkћq;wtwy#{7 awkћ3^v2lix4LyrxN- i5^ox0 /;^4<Grn~KEoHbx07ݻ} y?FG4l]J?/~~߰`^?)av)KSCQz}g g:R*}>s}fz64KLgzp,3=GggÙNtJ/^3ӋLYJgz>ӋL/3f)>%L/>3tT|3b8iNu>k^}fz54KLgzp,3FgWÙNJ}W^ g:)gz>ӛLo3f)>-Lo>3tT|3f8iRޢ3ӛLY:Lgp,3}DgÙNJ}> g:R*}>G>|f04Kt8̇ZڏУwo0x-z>.~yOrn~?^޼ϔ'{7ϜfC9Ns,< j~΃Ӝ9Oôg<4r09<8y4Lyd<4r09<:y4Lyfc9Ns-< j~ΣӜG9OÔg'րӑ5`yf&Lyf·?έ˃k6aZ3s>5tx X^ jlMR̆wMV̜MV̜6MV̜vMR&x >MV̜ƒfMV̜6ăMV̜)vŃMRx3ƃMV̜=ǃMV̜G6Ƀ.MV̜QvʃVMR=Ns[y<3{9ߝ|4Lyfs;n9iV̜|wr0Cq 8t,X jqpMV̜>8'l´g|Cr<&g<21yt?7aZ;l9iV̜|vr094圧aJ3`sr<'g<3ßN9909t:'-لi599hyN&Lyf99霜dyN&Lyf·?''9,ل?on<{뫋wtUq{͟zrݼ޷͋/h_JnJrגywzu-xz'{$7ps|+py5d5d5d5d5j@j@j@j@j@jՐWC$Y Id5Ր$!IVC$Y Ij˫!KVC,Y YjȒՐ%!KVC,Y yy5P$HVC"Y ee5j(P$HVCjP%JVCd5TjP%JVC]^ my54jh$IVC[Y Md54jh$-d5tj%.Y ]d5tjЗWX^ Cd5 j0VVÐ!Y Cd5 j˫tAP@DuX2": r+C2Ѥ&e82 (eB)#uuR)A JXJQ%DX "WºW:XH,AD 2K%,%Dn "\.a.a/A K%D "bH1AĘ LQ&,D K "g4A$ "MX7MXGM&XD "؄%m6A M&&'D "r’r9A NI'ֱ֭D "wA O'D "uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@Q{_ԔW5e)e͵֔5e)Mٯl(( E"E⒁@Qd(2P(   E"E@q@Qd(2P( E"E@Qdd(2P( E"uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@q@q@Qd(2P(  E"E@Qdnn(2P( EK"E@Qd(2P\7дnIdIdIdIdi@@@@@@Ӻ~μ|gY.y+_caVXoMow}wֿo!qm\_M{o~ӯIdV)t)9{In޾ 8|F@߅Տ؞>DwovbN}?xQ *9>Dwgj5FPfen-#o$>$wPSvO=>6oq7woo||z؞=>Dw/>Di`jMtm_&BQ#(yȻɳ7zao?rt/|{oWoSr7ͷov]X~I>=lOeoQtwzo4Q }@&?|df˒/ZTG}I_G:y~gw?PKxE>6Kz.svg.breadboard.arduino_Uno_Rev3_breadboard.svgkݶ(}{>djox8-;$sKiw,U:-$v~ZII-56 7/^~R8\^ᗷ8???_o|zxg=~<<߯_-t (x>oC?6,C_d,\o>x?~ѯZʊemqqOo|A Í$/n/}|QF痯O«ڻ׿~^>Jh=A z_>?}7]]_^ŏ Kg>?~[C|?_z}?ߟ~~supxׯ-?{qs}?zJzOiV#:a|g_ώ?'}\Q0¾Xzyv}~Nj˄(?S̐]{A/K;Y -豼Gтߨ B+󄖢  l3yUI!8Y(r:_NvYʫnǗ\7?|^U)('ӢQLEYp'JaYt3*<`K|z[rq#\^:a_:aoO8:ş2w"n"r Z$@Ky' +;p$Qnuox^Z<`$!$yIꞶHIi$ 1b.k!F67Is h7V,@!\߈yӝ46-KQcjr5?/u3c[´$,T " A/ oiy៖ - OA쪭=x_D:5okc >:64.:{; ~*?? Mp)=~j2:5ֽ m [KUCkݛgftBtNdg,ITAYޛq2}S!5ֽ +G 8 q"22 =;H7!eķ8WWoܐxmp0~<. o5z! p^Sh(%5J5zc صnMxN):'[g#矨H=ۆ܋Gy7("x'jyZ MS .1QlWMc Xÿ!^aYT,cz&dXܢ  0.) b2 nXJ20I1LDCOD6!cB;Cʬ1d 5Z_`7 C n= 0bm1 Lx`xx! n0< Q a%aa_2@Qt_5t}J׻aFc5(4.G2 z\,҂7p١^YB=Np t`BU#}Z+S\ig'U ~j?׏1>xejb`S}\#eԸWih]KzmyVP;\;!~t ~U ~^)jNw_)r?G<t_ 5ܩ3jO>1^ 4`ܨq#V4붘L 4ܨp a&]p nTs~V)wƷkV 7Aau='k2P& :ڝ-$[de5 VT.{г-Qe(l&ǯ(X<Lp=_.D UP`E¯(cm85ioj `|5_&_Vc]u5$vk0 g*P>j/ZjNw.L`?O<ـ9IcV]faklpgZXqyOAvnMsH9)7@'}<ȱ(rAz5̞{; !h2yvqT\Q?6Xb oe<š)aR 0Cfa!#[H |ݺ7  V {2Dj=~{_|mBo'#tJ/\Wy _o~@ɟ>^/ۗ>CB۹| Bd8y??]8kx[Ѫɧ:oE _'~{?~/&G?'" xX =z4<^Co_BD"!z_/^Gf#kx>/o£"!xz/Gݯ|'ݾ_޾~{A k/xFC(V⨎"YCaB[Qz?O"@g/-zѿH"G{ń?fkCƌxNWpVhq':_z!qRF ƽ$]GtNuuT.)}qK]t=a&(5M]( EדҒ1&GwϒD(W>1RgE+WiWQW>ٽrM&+7QfzDxfT^0fc^yqξpNW>4^=ŎYoontvĔSףڍϜϜrkGb{Jy ?U!$ha8L}j>$ϋ^>>F>F#%$cN,)ؽɸ.'}t2e.Gi@ |(@Geۢ>D.݁^NzL0K"꥛IߴIߨINzЙ V:_k7I6qUN;tZI*f8Q&8nCuȥm. D]KkSuSߟmBwjiԟVOqSlJEݟ*fSQ&8?ˇwt's]҉<-<7t7`g7:۾ _=b]|\>:|ѣJ>z Ξ`>D.ݯ5 \r_zjd:Mbu-79l6`f7:?;$E_`iyfO""Sg`8h?~9AX{wy޿q.F(m;}:}ٍ¢}ڽP֍]\/h"thS8pWϮ?^d_q4; 54i xċ>KU h"$x-1Yx[Ȧ'ƀ6m'9KF2@ȧ9?b!YK,x{ɬ%?!LbY?q&X<`28ȟ}/>EfM #3v,KfMc &3~E,Qf-G/ JC28ft"A1J_O m3]C&HO?7tܨpv{L-}N+w_ZƂy' v͉̀t,596'"ILDɋ?}enA jH;ѸuImD)u!#%6=.6ڭin1ޞ3GںxIc٧Vh /n5{3h58{N8{8{vm-=Vueɳ1K ãkx<{PtL՗K"t4h\_,h^E9#nDBʑ52^/Joaq^8}%ZDmӷ&Ƶ8?](Βksv 9zFV<\"+H\ 9FQ~KDɎ d$!<ۗKX$I/ɀƍP|ŲHrZeYf7wXL,2EL͢}^0&Yd8@cXAPŜm=Bk Ns<īDm<׫/f~~ y;ٗrh, &_ XcQ >s'K1SRNVV 4Ɋ0,&S(\䇓(c *ew𥾽3=\ubj$OǕ-A?=XF-Ie 7;ZIOB'ZD}C\{#/ mv~g,dlIMn4wzBR"tz94>-WɘY'(dA,OkH嘨7Z&^)Möu=&ENu8 TeG{.:EϊvQYK~$9ُI|4]LQ7;D1AtM7)n> M#Ҝ#X? Ys~c 'Nჷ4%ߘkQ>HXWfIƷ5KrM4Kƈ?>|-[iR*wQ9MB}z;SV5xdU |ݣ'}c F,:6 G`>;&#ؐCD,EM_|i*mhr W66w{tfl~..S6t/*fAbB4P&׷~s}/0ZU9}5 $Ml/I!8W'+%8PBKa?cib)%U m(TR wmz:\+K}im!Ƞ c^s5J4,mE?J%-âɑpbu\wZd-#-=%qoO_މ%i|mtDoMMЦk;hvю0FN'bdwIN' TµR]'yz {8//Ǽ'Ivbv"X}|ԣ֣ @uڣPZN-(Ԕ"UGU6( LG1$0ƣD ʣ( c8.K8 wIgy`aKk2`/ ū7!o­IBIP\](gp]oBƛ 2:/ ;Hݏݏ8VIs?iSsR(BAB'y[}YCv$kp\1Lg)Nzt (@lX'A[~~Ҙ~~3,01$1U]eIܵ$v6~_mwVs_Y $#sO̸n2z}t!5hɍ0FkLsoH@~EU_;,&lpQygz3`?8l\\31:#ѣUw$ L#1$0őF %~D@ONX+9Fp?6)4S']N0Εxew+~iw+BLÒwai4O/ݬ^ر{JkB=9okflk5<GV 4`# k-}dVhh|iJ*\Ͷؙ@ls]<-6맟~ivZgt-Ƕd[KƢ|V>~4'ۏGǚ辽|ئ5lpӚ8Ao#Z̫gW|_K9˂9g9G1RrTqq7Gj,5ċ$jo̸q⍟6ى`9UL7y Ww8+8;bi,P#fZě<ר;~x qnsexgtǏċ>34io´Ǐ/''^lƀ-L8h>syYx:x,NیrW<Ό+x|D闩D<! wAXP!r Ax"UBл_,9qhI0I[NloFXH!rB )LŃ T?N⁙g2>c)| }b$AK2F3zJMq&8GFSY( MabN" azm3F3gJ^[=iNl:سf7ںTIqQ4b7q_k9.qb+"g_1N|_/ 8b<cQL8bqJ1N 6qQYq7C-bP" YXC m&Cmllnyl#ⱍȏmcX^~Iٔp#Z9_|y&9"?9Gts_IO0qb9"'5G6ᆋ.?7G.ʉtd刜or$9"?1Gn#cnˑ̹&Y92?+Gv?ӓOqò-.>E2'Gӧ.c<$!eX@,sb ~ef}r"bU閩T<&V1ě- tN0Hn* hXu7[̙W9ᰢmenSʏU}S HV d*?PJ3a*H@B2*?P8ۯ;>kXrB !˝uB[~Y!TN Z1.CBuܿn8b T+|>%*?vPO7vz.@BsxCխt,RAD=|*?xPofϐ:=Afj8t~;7_O~7<ۏ/O|9~"FQ %t+kHBGI|$Nl pXBK-wC JN(|bqΉ#t+[d:?Н8~գ~bA "t+"t~e}W,bqΉ#t+Y#t~q椋:'Э \0(BGE< է5(ï/~z VT/d . m:iAvl\Bq`#u80\qy8auoث,]/o/WvX"Ia`-w@vGK7[׶-'%<4??HKsIۖFwDÝ%6N=gOO:\f_>,?| w+X=̟ ғbwH&+dG}-qs+:L^A,^d+Gi.z!DO7 QTpuaN/W'N.7CB`bw)m`֦CW&JZYEeip洡9B Mm$M#{p(/"ȢZ:QpduԁGeQǬI8::bU =.w#9db5r4B#jjxW0# ; ;~Hύ n^{`"PǪ@Ei rC4pkӈ Q^(JҊnȊ5 Vz",ǫipFTD2D  H ̚nDϊztNu*[xi}Y|a-ʅg XƔ(!XGh`3r̷܀ظ76 F FO*B5 ,x6`hlAlso IھY [߿';6Z3ϱz 3Z9Nvlyc`NO}l;(d?A3r:t#8a_#g6 oI2ǾYQUb?B3eyat$#򓁴oi WW^Q3OX""-ȩ>BaMƁjhXWdjE 9Finv#hDc[@VF atd:Yr#&ՔkH"ZdH xF_@$r tdG7! .+eXAVh 5XGh`F,.|oHG6Sg͋6{ZwfD/sB>uO`B?OF[2wZc~V84\9 ..<|n>sB8{-VwM&Taeel;]SS m h%K6hyhsX_yH 2v>g6^ϸa˜)E 5㺈'"T|о.B,tQ ũZl\]:@0j/,WFbLL2xW;tZp,0]&ʤG_P1[\b9&U;0"!Z:΢͉(1 'Ng;[Nz燮w,MMG﵈2æ'˅"˕#Ց~^,RiF^zSx;$^wr'X8^.ٽݨ˝N~8{-^.jLn䡺Hjd77:?BAGy-T0 1*3d 0 `/ ktb}tSL#i/D2rZr%*npH ܨ ,8!;khNVoƿTPӘ%,JFpGN 0;F=;"q$ QsC0rҒ pʺL5k)%;_`B@RH~d€ \aO^cv5HQmP̸$ap$F` S$Z!Z2r wz]%8PB"̥z&O6o;Zr,>>=O(rJUjLCQ 'M>B,9Yf7IJbpE F%X`7%_j )r%Gā7+'E&3htfUS"d氵2 +̂1iPA ځʼnf`0T9iƎFDcYcuGYT'#kF"Xr5P+UjA2,܂ j(\Kc)$E66@J㾬0gH,-hB{+ͱ͸l+g+&6p܁Pf=pcNDנf[i4Bqy۝FW@5a/-v$ d.0WiUȄjybZXe UhSՃZ3MRNJFe\#h>#Ac ݴ%((QF&)a[}!y:٬씷ú襭dq֜Xre'wM 1PLnD[((V =dSKvE Xӕdؕ]S i68;+A_~-;NXZwnT@G&+~!Up.ZlTh޳ {ԼPڰ@ < M! K,z ;x |.YXYRFA) @gDAP) @ #<:alD-  @I QB1M? LL! *RXv$H$qg6)Bؔ1~ǣتc4-U TS _% 0Ӕ 'z5[ x_s^ؿ; Xi T֘[U;GA+PAR%ƜQ XFBPckmCDP@Ƙ7_@P!j yc5,Z `֙ 2cȆTQҡ1vrH95U:.14'C)0cckS-"i` T hQj+D-jVkvVgͶp7Ϣtk,&cqq%(X,4~D蟉r3nm׈?S2n%2{D/UY*LѸQkp8!?V@Z[F!3QVX,P@e24"BbIGQDrdm _-9|A51obOy/a ےrl_Lp-K:U,Rq7q\@:K\9B 2=!LUdHnXbB~d%M݂}KlxhJZOFbpY[؆kjLl-Q#*GVWjWTê  ⊢8bAv2kx>Ǿ: úRF*VqXE*wX#fIy{(⡉ a'u -aaï{W l1'u -aa˯{vXGA랬GAlUӖ_d㟍=g@luO68sYh P^J2z$ XG1Eʆ @APW[ q%lb N5 /gdbuyeË 3$fõw' ϡc^CK]0I Kv%2ٿ.‡Kb x4 W(X 0u32]PAa0 p I.:1yoP~ " *ƤV4?Y0Ĥa;3ρg@65,k.#g;H9Ip8lX?`_ C:W!0XLUg*Lۂq9Q\s0.cEzj'Fd85sR |Uh\2F!X)L׫XHC:\Bץ21Crq2%>r_bC1Ƶȵg 5wk!U n K.IX8\vҘ)5`)pm S}U-XwPuGA3A( ~e0+js7J+e$qeW (5tNVu0`Qq`8c8 P)ٍeAX/gSg; d YvD}ZD ?]0KXQ)S!"D:Xgϳ87Ye0UaIq!θxD\`9[CV ᮬim%J yNp̵ $#";YbT1ef$ Jna~B#K=7یe Fs4ݭ㏖Ϸ^__=l2X Ta=+8|ր"62ؤq{Q+V2\$^e^ _zu b:+B_P)&it-4_Z8lampZ?ol(C&㎜dlRm*9Z`#H>8)+b >lǣ«Ip6 |W LDvǭqFbL34q7?Cq4?{ܳAө琺ko&o'ᘇm8ed4#YpQ Aw׉V|g`J!1YN0e8dwmބT q6Eض/{H~s̩F(Ċ/;ǰPuie !hhv!pf8UUu#llR8vZ F1߸.j]8T) lýc8YYj9jln囹kv6nH'8`]#Ă'LU[{r{8lݾ@w '-% sC}+wv׮Wj|SL1,x1HvlXdɘc8Hy_EĂ'sI/=+kX4ЫCޮWÂ[n J{r+duuUtu*Igܤ>?`\\-8oca˭@S79I|[]sdr]TWJ8 tpqx:e–+^Ű.n/W₼ŦѥNN # CeخoXqhan} ~gX,L|Xh3Amfȹ98M>Tl@lnJNx!$GJyov7.Ѷ"U +*gV V+xٔȰ5.9#boR`/AʰA_$`p{Β9̞)4(/4TA$^& m$  ֙Ȱ.#b$^& m$C_{Al3,|̷ӜNkI帱Ѧ&Q2f{ 7#PNs 7>T]zq9n)k(TiAw ƫt@~<%MBH*:CfRfۂ̶!e6(&,G_V2UA'4ScRĉ :| 4<7qL1aHu3!QX5zT)|N}x k`PBt^c>2!koF'}ǀ"dBYwk6Q _AF\7D힙; )05.LMT=FI}mAle߸)Mn7Q>8Ȏ) n7QT0lr&03tFNVR,i)ĖͲ́ԝ\)Sܝ&Vӳ\PtMB S)SvJ/o/R!&{;TaZ OBA6OVعQ}J:A}8A^vJQ:ۤCi*(urwW6qkb'jp֖Q>cG,B{ZҭX7pճ됉~"Kg;S]x;6OXA*_%DC)DZ+Ո#|p9Ak͈~+Tt=D٪qfW*lJDn38`*N !W㖥2RI .~"Q$R@#kM:6;(:EJl&`f;(dM' 艘ڌ鉍\>yt)~_g+\Jl .#Wxb¤ֽ7}t+9mB,:OW0ؑyePeQ\bN( 1)!b"hhRB 4,Zy4*- k /e=~R%3@Jh( D `RRvUul|ീwW Gwrs)dH/&jU8)A+ ݖZa0f,6os&f<+ IA)LلtF),XÈtMNx\308 g$AOVMh]_k-\NNP%s((QDzHw-;| 6 S;AC%j+Pڭx\Yޙ6Y8ͅQ*X\'FilOGa]2.ü!OWDO^ǮeG"܌sƢt!Pl' lv(e!/s(ݳDtVs[#j爡92Uhb Jו5w&VM@X>s Y{C2e;Mx6j?F{Ama2LnR>mAZAV%h]@kиlA׵L< {kkiVO'AfۈZЬ=AU[(9ij5ܿhzFh4\*s oly,=lI{q0s7dɰꗉc@QAYHK_E$Œ`Q|oOvϟ_ֶP?;߃P-'܃1PAts!ߔplQ7À^tɟy{eeRiP1R> /.Zu;CT ŷ"^v}`M'K[VWps"B[ᖷr\U}VZw6Fx2b&B* Ҋ_U zHHHɳYcI+!{)Gz 86(,/xw-*V Y $Ryx݀*(J` '*89NTё|r6ld\%IƾÖh 9*@KЀ\:$51I`AMwn+;וd X1'tZS3iJkJQ:)K)c34%5FNu0Y/;6PKC2kej9 Wp&=(]Mޥ 7@|pQv =s B;m`c(y]P/$p⦠3?eszBpk[p9T=l;zq'v*J0vt ePO( uPOH[{6&oֳP&KCPO*ۈUU}-l zB0ʫqI"S儲^&tLv6WOPJj"];&x5A-W))'LDrPv_۔Ϡnl=;g)Hi' Ofx_ h^hYO~9<ݪh gDyzւI`'Z I&4zYh=$A= ePMȂrB1PN8TO(OzB;`•'k_OH X<,-DZ^>,MH7yiIu-'w%c 'XStdiPҦ"@ҁzZV=!H=!'1zAfZ6Y<MQ'Z Jw WOʯ[P8gA݂B}Z܀i@hPB>TP4P۝kw,"T7\vV ng\SSPŅP6V"-.q > _6mbi̧n>mt_]5ҤP?i|-OkAna?P,((⽂B{\ M8jϒ$ ޟ>Bؑ]ٟ9$Gˇ>>;g+ n=#lODuWB~eOܤ)  cN_J!Qw;(a `̟J)&"Hqgv٠ YP6h|٠US5(|ՠW sE4D$LYxgts sh̏L$ReIbE _4kH͠ jTA͠ijdWEݹlj>棚v fٙjP0?,Q#[yigTޑE#e5.1LV ;+IgcAH[GE7%D&OM`]48$(s5.VQreW$^4ؚ,g+ldB@\5X2(;jr_K{7\bH0v+ U%*^g޼}qOs+S@}TeRA]o J{ć[*8:P3L0B Du­(:\l0J)nh%,\^vh.܋!:|H_'R~%ثHU<iVzTYE+qX;wJ*kX{EByvSHBKV*UpR-"ŴHQ;u`{T&TUhF؄D= pp8k$&0/ È) W# ¶VuXO\$)8kOj%֩drdyt[w[řNB怬a8<æZY%iF䏪B𱌩QtM2 #7͸n 'j1%/&X!o d___jLRc LvTux)k;VH)= n=S .W` L R8'lhrF?аlPCf24pl8>ٚVT@Kq6"XS *i2϶C "Ïs "-JJ_5KB$f!aڱLRd֣J7ppŚ8 d@ 'G; QSn܅hIu~TR& |`Pv Z-&+j2F#/dZAm%,- )ƹoҜ P~$ ع85+HdxE1XkTcSATcBLߪ c !F:œœpVZJ\e; ʮB![, ߭/WG쳸0H#f+'^Ku{RIy"ݤ')CMs 5C:[3t.S3^\1a& 'dٚOT1d8eC:|Re C7sH$ s& ʍ,(& 4ihbݤ!zT' 1i9C:+ ж3[ Vΐ9C(!g)C KL7eH}x)Cd ֣! 9Cy9CGAr( jimRsL5ȏ:!a( ۅ00n_'#Qح\tİncW0'I0<Ó;grGi݀(P`F9Q(3k ➽&i׹0[&h\p9Ah3Z-4a_4p<4v 3[&dL6?xڌk 6odOu9=ҩn -ң @5ZNw7t-qDu㧕k66k[8qj#Dդt7cu pðZqcw_I%嗫yYf[j8I'QLדʾ5rnu&X/3Dž#W a濣qt/r#2F9dӞN' e6& Sr+I3d h$/$ FFjǑ8Bd!I`5uic4XqS@VbpTaDτq)Q)h$7|IFc.6 $GM< _ŦÛ=| LL7NtBK,]l ^ @8t3t!h܀$` ȕ yR5Ә\^o7/Y6 p #.`jԫk3 `m@k$᯽XE[ CZ D/fb #W@ZLF,fL XJDL[l "bYAD,dDb6-Du&o6:bV-8r $DĞΣŬ[lpJxt2"1+G]abRѫXUg4̢]\=tsSK#]\uR7C43?z10ܢyGx ķQczv͢D5ҘI:AUj"t*e~dS2 ͋*E2H#q'p $[@opu9kɦGM&ڟ(ȳQnT^Zfx*8וRfJ5t44ZpsO%w#AGa^*2.{ ip%w3w$U(NRp22 + TCvmFv띉!%gknijnբz6Q֦Xsڞ3Qp5XZpaKl|5yf8N#Ɉh[[htD{mIT`guT9uE%|l3ܨZW,]fQ]ht!*~":(x v{p;OaPpou j+Y(%gxe9㏊!9Nú0DXWY27+AB !4!FCBgk[<()[(U,˒3P%ֿBS*dXSC$OU5WU66Q pk E*$a4*(d;D qK6Vp dΰ Ӣ ʸ'6<1m^}~ &w&Q*_5NEJ9rVK91_x=.$s 2+w YDPMθ*=g50~1!ݵEU,s>ڐlRwK1&y[T6*Di r`gJp#Bt.<瀔@RեgCʘi\y*#Vw.M; #U<@#܄st"P4o؝byn;ZF,Zuw"!ǕC@@LZZ$ ueԝhi P0s YɉC2m"oh u[ҔfM!hRusFHqc *b8rU(Ap, |`8׀5DpՓvJܺ*7YG(?l5ܿ0Gu+єR9ի7B`8O~`&ԟJGVnRc[^Zk*ir#ٵwèɂ @*ƾNaa%üF6OMHu!' @r0}0rSHO-S=bm%3AQ/KPCL MxiE~]hUX^Aŷ"^v}`M¸A}d|.w.Bh+"QRh^ЂQpKBh;9_RFK$J`oX6_U {HJH3c+!v!- "Cz'Q>KEDG.6T ?7^M'aR ȁ(ڃ *ރ#Z?g-4ܝqX .})ޭ6,*4rF+YH1!\"={Qg$e+N4Tr!ۿ$v{QUTJVr*Jfm1*ќ@$ݷg*% D !JQd1 D}kOWlT8>ze@x\-lZ8YS#<>om$S+3+^Z`159{Τ|4J=$SRW ~S`UhW Hzt7b DѪ@wQ8E^:>++elc{ͩ^8>n̏ުQEez)ܤ)@$ ׫X~"=o 7?P;?4IN;{&QuoNW cJ@> iLATQjչk ?.Aco\ⱼGu!jcV݌sJ[YmQ)b5imJ'R|1+F<[9W_H|t(GlM{V(rDyiWt#n^ػ, o( j5혊vPw[X[\_Z2iOs!Ү\Rb`eC 6;S} ܴ&,Z_q('YZ@g)^!R(^rL.1Twq/QZ'!^|qXYq%.!uٷGk%)1K*^4E7qob5řbybQdQ#WCc`[" 1W<_b!8ȕ+լgzp=W@2;E_;bPYPxMVFp \Y6Qpa)mH *$Z`iy5 #%pI |oW |K -ķSl8 D A/\qW & fNj"$Pn!ZT&U BɈ(;DWvL3 "XjJF ̖.Az@h(B9bv۶3XƥnR*0DPOBMay! FK/AtLlrPnfة%ʝ9?ZY2r wּ!0M{4x4Hڠ8TdII|5mK \NXil%^Eg* 25R4Bgsz;`=)N#WV'jhk=t{NBseoJ[P6dHW/~Wn~%$6W,`Ŕ>]lUi'E'(N#@`Ob ?@胯KeYiެ<I7y o:ւ,r0uf92w1VManMv&) S1`A-qUHޤ>qf rB2ZȐY)ll%EWݸ1: 6?SH8c&=X{jn@*0#[y6: ͦo Menɳ֭ڟG?2̢<Ã6sTժZN.ϰXze`pf^) x7@x}Ҁ;@]-XqT^apJDee#o 8:rDF"m!;ۓ˿:iU_`GY:ث,q]{q)yl;FL&uBT u-X,Ą>:7 .>ۉ*_Ta% {Ra5tXj9<mhi+tDydyOtnf{>?ąm=4v[}ѷxsۣ2#H' ܟ8>U:4}?;G`_ܓBXfhlO!OIE8Ǟ UT&Sfe=AejU*ɊO"l)f\2[}ƣb h1$1 6=l{k Z41eI `Iܙ7,8 a]WD92+: )R!|Al&09!pA@s5re޵=~UH$?C!8@pQ+C`ΐc|C\S1Gѡ+tnBnR2JK⃈Z t!_2l˄TDlF+n"nr 9GBƙ?1M6dt`N&Vr Ɔ; h$v7p#S8R?SƦ x3á ,1ߙMlr3OyؽV)>1oSrnG[1=U{SV637WW54uЊE K|ZrܬLҭإ[@ۂ$jbvg&־57>M-.,H`FZ *=R 9$W17z2x=*6*ٷ񐜠j||X Ʌ'W,o!drsūJ7[Ϟ5NsiJ,!|5Q•sWƯoi||ҜS#'͵ W`wC4[ ^ErX{Va< r~䶼ײ^jE|o hvP;27yO.tdX;JIt0ծC*eZd- wNlSJ~|{z ~XC|+4I<.jmæ"69MAS@GϦ{n3nbp밙c;.W.)ETj 5 yLs7}Goon vkH7Edl` 76BGK+uמ3z!IhӢЎ$%[_s= ZybJ,&LkZ0pF|:_t+3_m$//䍯WpjPA|{^4_2_n&7yR6suFteO|S`+-#xB||$xZ2$Ukۙ kl4"C 4sv#W%QX}N(nc.KԗM{ju]ԗ/b@$lR0fã(tvײމ6ģ4 S!0F#nl#ҊBnUwD,"-(/4P> .VMVW/ d yR-ٴuZnR@lTjR+6⳹^9/N ?L|o߯7V^97Ӥ?脅6 tAJވ|oegaa?B|FDt?=?|*KNJ4;pAB?_.  |B} o@+/VהӛLTympT|^eU6%fAOƧG[ך .d:2u? 2AKrXG(-sـ:!Ɔ()6*` +!~o8]|,:|Rp!ii[Fsت*OܩU< (v)!KH8x0qj^jUkXLyI/N%~õ1ռ`_ -[8dQdi["ѭR݉!]mCU:)v:w[mLZ%a-}ӄ?3GƤL l+tr5&^P 7ba?M~rP9 K2Ĭ@R ;@tz8f+ rx8@-u _=KKDe:eeB0IºT܅[<زRunjV,I݅%vbV4n`!@t-UVwD ~-wF5OrqUD:nսBsX;\`Y僩OgGReY僩x ]r?gX>x~J-w"@6U[:U/NԖ*lݲ!` [Q/+j`yuu+S` b`)$ CU. -RtKJ4/ưJ|+T -ªL±)U^//uzV沣.C̊-L^= P%=N5-Y@z -"t 1J5fqc# [(Du/4; |3v bV,n$unBd ݒUUTiV6Н)X@[d L(ԋPN4 -T܅kXy>+hx0uqnP{* -UT^.,?p-@[i@WbDQ[ HSlm,NԾ9\]\^--(ˋ syeފ/[ZJT7gL+9y{|wq*Y7ɘ"<&ј_W27#6rw _dC ^~aŰ{ͶkT=V:z%~V2!ļ17cJ,ǔhݘ˰j;ѹz6CөW{52:[U,|_/؉%>5( J6>{}ݴ+*_!f7]/Ѹ+}d*y?8CP}]Na{s灻4 R>1 kumBB߳gǔ=kN[}NzW'1[ 87Wگ+_kP<ʝ" lay7˾4OiPAP&m'6 D7Mrz4& krg;pX(MYQci*\l)MưCӰiV^~re1+b֖$]:Odǜ5 2JF8Ĉ<|p,` ܹ(KCzmPCTD#Z=T+2 jp,I(˕2AY D"/UB2Iuyp^ʿ%I!\j/F9e13Z hXN쩱wb,eYU6!NLEd/ ^滛5!nܘ $915:F&1\688"ـHnAПitbrԓ0T)*qNs/CaEJ 6gZdmM«"^-%!Eo7|\Sq%F[ h,B.'9f#WƬ 1xL xTǀH+2({72~\2ݥIÌit#9 0;;RlMj!T ȉMITUyB&c 4xjPJ3+V U.iAtXY -2=}H\c>NBG%%Q=½f٘;xxd+ĩ$<9E"YB|@`%*+LQyʪ+dQ1=[&Kt=6~!gKF3=c` 0STl2C;Xg8*v13[rw-qJ\Im2JZp_-d[)콼z,ypL@I+7|b>\.١nm2-XPKuu E$ޫ>Ȕy"תY) Yh\ %!T<e"u$djb}A7*\1 AY-swc+Cʺi[B$nE9{<zjLf(Y ҿ ,J&FL,xiH<T0_Xvl%0*a*n2vѥa `ĺL̅niPgd ԟ=Ԩo& I! T,J(:@QRLJX paa^X1%R\,ڍ|ӔZy<5;٢B䗦 X{tpaԡŝJhE#,FBǢ,8RRĽ7^ yvieʺ7(!ӽ#\9FtRVV6NG$ľ u5y7b ҭݺFٸ&as1;e|0P /d^ul0p (8?q*3_298Pk G1Uh޲D^ [$5k i=x?3@4Vj|>`Urߞ~aOמaaа0h"g J Ӑ,6mm )B)ϵɼ얠ޫEj0i(IDZ, 6a&Ӵɿ[*ɵ쓡kXAasdvL 1XF)bp'o2\}Uvg9Ӈ{l7M/i}I>M6npB`Bd#݅ +Ο)O=xy#I ˰Sh@lu8~ў g{mYo e@Z,؏j e#*lF=ZHi{nO"kabY $QjmA (OvWDRRT!磍YƙZM~D6j%Kŵ/'EBA샲β~q>1 X< 60B ;mPHAAuY0:h+yQFhOd`#FNN(؎/Y&kt>ؘ2y0ߑN푶Zs΅O;tCڵ8xDp)>`jvQ_'q&ylKvct9x>.5a⤼%XYuD|Bm)pgݘx; kFίIZhK Ӿ}#{mZ(5^s~3/ۥ"K$,S[p:.:~9KT@^GQ*4a (35(bnp-RRN9O@kTX(p3>um]lstLf54UqŠt1A6`SLc5sN2à}R=6_IJ݇hY+ DvyKsx&YY 4͵葲ͧOcOFOQ mQ 6vgXfz@t2(pyP![s.KP"ml* Y:D]Jo;vHKcQ6.uj YD]aRf~:C3ܫN,{ S[ujAr/4.Y#sK9ͺjcczb@Ui U:w ZP Zu(_2qc SbcaLvtH)4* G񵜶 .M:C|ȍ']hރz_$:؈>Dv}Kq 6o1S#8q7gnOzuyq3:z\۫˫?W\w?@agfRTܔ\?XvT/s5v7__PKxEC$p~svg.breadboard.dc_motor_leg.svgXn#}~E0/TK`{:6;*ȣGVB!)>HMR}TH/b_m7QA̷r~}-eQ|zqa8SGO7o Q~FPoSn9ywXGoGb,.H ƋW|b^:|Xm]z/P0tR á"AkoI c_^sb7(&ѫ~X~@-\ח1ZM̷ ;Hk:"#diU*3V4P _ Kce&L"Li"94^j#m#~:FʼK UW" 92CΠ@ wbSVxVIƆ9 Ic eX#BZA1%V&cųyPs FȈ2ohk'[) 0s(m+pJ̓YɠrWJ23B!"BNpw4t\T6VyeIpY״=k*Z`BYkE3=[ aİI;;ԥL %Ct!GRpIm lqD<FԿSG4੬.Y)$}+m&mi*.O U"J>Zɷ:&wŴI7XRֳ{zkcJUnR~Zx%Э5@4^L2A9 !a,l^@҅T=.NC[[riRQM&]m84A,(_ۑPp^(Do,3B!z2Rř E%34"\3ڥNZqvӯA9FEY ^"TjL"XMEidIKI詶qr5\u-MDqS}PkǨhHPtRȭ}j],,ȸjX+IsDJsVFܐDioS~5UBHvB&j ! ! C(`=N\)|SD&tvȞFDQ:Q9.&pPCu+!d;!L!2V61ɰ.iwL;,6%hu MuqP)O)E\8f&v-WCM\cj ={rė |F%sdͦ EU>95"7ɡCTJv^vO z ћn?DxÅ5b6hTkK4p+M{G/R[<^T1m!EoduR:nGg> kVƒQTYT,>޺OڋJ1>a]S*vپ߲E f^mye,NL^,;V%'zҼ[`9? 5"W;O}LnPtpGg4{,Y"XΔұ"`/5K cEYz9auR%HcsHWFYԈʗg41unPKxE%%k.r%svg.breadboard.Half_breadboard56a.svgݒFze557L (Gvf;$Hdhugo_ϯ߾o={v_||7勿O?믾_o޴x_ճg~7WWWu~z?_~lw}?!|yױƛi9}?~OTMu>7û??G}߼z~廷Qg߿xwy~o^nӘ7Ô< W߿{-޿~o!g<Y~)!Oϯ 7 )Я{?~^"?_ۿVAI7urvMw/0O)Ƴ+W~~~xr1&p02Mir]!z~]˗/E|6:c;1m};xO_p3eWa/?ϫ,\-E~F_V^b(/k"_k|ϯo:yym{ߞS?q0ۿ:~6kd?^\2{\_ 5q6sģb..q$ vpqOkCsOE)6L+7'k܇bēb..q&B?Opŷ^^,vqgP/aVh+\ŷY|+]<% zZ\^7Yl/)1@WozC{5| G*tqЫO`⛆ bCPc1BE Q!bE tԎ8j>@-ax*KR+vDS# mO wAEbQ jGOL5DojOQjŮ(jP'AmA5>赮Va_ ළ5Ӈ6J5LPYwAUUxpZ ZkN5p֠ O[+֐6D\<ms յRu 3tƝkP'-AU.Gi`{ 3qڡ\%?A/'vH_UIU՟>襾vD_ ׎*A z׎k#}UU(AoWU}P_;z%56׆k6D_jCm} .q˔܉FUU]k5;רzP}[_*A}K}m܁Ff;רJP}[_j@}K}m܁kN5 pר P1׃G5tt‘(lT=~?P8AFՅRU_jl46&?ب*Q[ejE}KU'*U3JolT~?8CFՏRU ~ ?Q GJRߠ"a/'Z[6HkUE Vu>6Dm TH!{ުԇƞmz#U%)doP\Ւ=QQդ措(uaN3v3w| VU__jnO> vð3 U~?1B措:5wT~?1B措A5wT%~?qUJokTRs~5';Qը>6@s5'; Qu>6@s5';Q>6@sjncO4wT)djO}Kmf=\UBU YjnJ\s39\UOe܎hn#U)o\UͭPs;~B ZTRs;~jnO4wR-*ſ*R}KN܎hzT[s'դ͝FhZT[s'U͝h*T~[s'ա͝"ƞhS~[s'U͝F=IN=a/5;MPs{N!m͝Ty>K͝'57\s'Uҟ͝T_jn4wJPs;~O)m͝T_jnO3܎UJ[3OYjnO;3܎ijQ)~"܎hn#U=*o\դ-Ps}E jTRs{jnc4WUAsU^jncO4Bmܤ TRs{iM<57ԇhn Ps{IU&U/Rs57eUknR*<ܤSRs;~)Rͭ\sO)mMB/5?ܤ*T[sjQ}K&d|TJF}T_jnOV}?Q=*ſI5>6@s2'T oknR5{=ܤ &UBۚT^jnc47E({IAsU^jncO4W C#U)do\՞UPHsUu 4W.i_j'j ~);4WKڣ?/5',7hP}Kj6ʂh*T[sgբ͝t?YΪH/5;( ~Q)~ÄjR}KmɆZ2G#E KFa/5; ){P!m͝U^jnc4wV3RDsg՟Bۚ;Շh Ϊ<5wV{=YI͝Uu okSa[YJѯ{Uuṳ*PKj\ŸT[tgU:U StUJLWNvDu̔?R]դRUe麝?q]5e\WuU_nOdWM#U]*o]U:jqnVM*mYUN6@w2'U onVE| >ݬ fբB۾U_ o7*|"YfURx0`jj fY5 Y5NjʄoVe*¿YNjʄoV}*¿mYNjʄnV*¿-YUNjʄnV*¿YUNv@vZ'UJonVu{=q]5EeadWtU^ngOTWMQY#UE*ao]U:USTHuUJܢjT[sP_J5)* {EuU:jnQSTDs*P m-=ubAs+ܢ,՞ۚ[TuVEMQY-;%5ԇ57EmQU: ܎h2G Tߠ?u¯5'L7hPk9*~FE ZT'Zs;~jʄi*RmͭFu¯5[>ܪjT[sPkmV5EeaO4ܪ T'Zs{UMQYͭ@%5ԉhnUSTDsjO mͭ:ub5[=ܪS~[sjN}OZscƚ[*q[U{NOk_82nUSt{tZ2%@L&-UN h ٭jʔݪTVե:%}'|]*SwSi[UꔀVޞPުL 孪T WN hm UTV7jUkmq* ~$U ZU'}~jʂjU!~V ߆诚GZU`Uu¯ o' \;5x̓yp<؃"(m^dB/wM@8 {!l+r's⤝xk*="e? aۊàZX/7Me@x lr'3͸%8 jʒQ*܍&eL妩/Wz0m=ja~~4*ܐ&meLVRd5qeI)jaaEV=OV)PdtA {YULe"6)=jSdU,:Y""+SLU-KC(jfBЊC@L! E!9v)=A`B@T;KC0(rPSZ[DZ²d9nF`P䠺Y""5eI)rP,LAN hEn Ej˒RZYAjeЊ 0%AU0"U:%%@9y,KH*daEI kENWVV#T%A;9R)=AeB@T+KC0(rPSZ{D29b`P䠪Y""5e )fiEVSZ{Hz)Ȫ!XYճN!hEn! EV Z "nF`Qd:%%YmhY`YEU1딀VRd5eI)jeaEuJ@+rK(rTkZ"GU U%딀VQ&, E 9F'y"G5AݡQUEu A+r(rT["G U/VQfL! E!9j)=QmB@U7KC0(rTSZ{D29v`PY""Ge)rT,QuN hEn Ejz˒R䨊YA*fЊ %Q0"V)-j˒SdU,*Y""1.KLU! (jd}Zsܡjk5\(d3i"R)=jSd,zY""jRQ4"u A+r(򨆺L! EU7KC0(Y""jRQ4"u A+r (6, EU7 #0(Y""j˒RQ0"uJ@+rK(ƻ, EU+ 0(ZY""j˒RQU0"uJ@+rK(򨆼, EU! 0(YȣZ LZC<CgUɮg $q抜XjcܡYUU)VQL! EΪ!9^)=Y텙B@U1KC0(rVլSZ{D 39n`PY""gf )rV, YճN!hEn!Ej.̒R䬺YAfЊ V%Y0"gU:%%@90KHjeaEΪuJ@+rK(rV[a"gU U%딀VRdfI)*daEVOV1Pd5(d3EVSZ{H^)Ȫ!XYN!hE! EV{a"b`QdU:Y텙B@\T7KC0(rQSZ{D 3v`PY""5f)rQ,EuN hEn E.j+̒R䢊YA*fЊ \V%E0":%%@0KHdaE.uJ@+rK(rQ[a"U \T#@֊2N^\dy~X;wȔž/2ML~<;yr'ɣ=% {:bD^gzz wtoc?buɄ] ;8\S{kPU}xOG,YxO.,Î=ၥNxOG,+㽓G;UWG)tJ#WϞmG (a';OS{kPUp1da>O;w:4=;LjQ#Lɣ%uxCI[#&L~zxtz wOryd"/3ӽ#O㵣'~+G&xoޯ~݃}GYA5}FuqxѨ}yX)#zyw䀟QG/Go8#6l zy`tz x<`tos?b];8g6[]Ǯ~נz?b=#> Î3!9ZQh0ͧCE6?G<*:g<^*:o9)їݳOC}{ {Pto?bȆ^=9.Q'k੽o8X#2q㸧cDa=# q 8 щ>; }q㞮=='C6{9ɣOo82qן} aLJf5O?xl*|3dP3:g=:o8C6CPN =}COSiTGL:yrg,tq2q/ˇq. i/ /LNq}qĈ>G|Bw>aΟmGL~ogɣo8X 2q/χiǂޟG~}3A';Ag-A6[.==^: dC I2P'{< t?b]LҎ3] iOާG>x>5}v93% }}'V};@w>agΟmG>73q7GL}#? ] sqڿz刭}}w/tDO7刑}}ܟ}N==ڳ'>6oq_O'Ow}>>&T!0U>!4U>!S#|lqw|1}v53!}}gBw|}?OsU=9~O'{ 9g=9o9X}sxF> >+~qNx>xq$ ^w_"޻/Gl_8ww_ϰx}9bFq߻xg}3<z1cCW<3+_{ #Î+ {޻|w';6zwd}œ;a{oܧo8S #FGvw_U[ m'AG}mWU{Րp$1!ji|{{Փo|ܫԇ>Î!LqR;}qR|&ۣc={ѫ>$jB(57!u?f㎏YU5ִJ~1z cVUu>qw|̪&}vܫNrܫԇ>#UUMGWm)x JkǬ:zrܫ& ytܫ pܫԅ~>N;޻jiwvB/Wx*ݟ~i{eN OegW3NGWm)xѫ>'rTj'}TQjy/S ~>&rhӎ`M4ԇ>~iji{e&r2[ݳY'ۣOثq?[R;`{˯DB;II*cd VڌVM+<ӎzDjD+ӷ(M{br쵙lzRt$5d!OR3>ԅU,cV)UeǬRP=:>f\ቜv:'}PO&+7A>q_Ǭ{b{z {Ֆ'߀vq,qR3xqR%RSM8//> _> _/ۿ%Syg_Ӵ\'sc;~?|?Ë_ݛWWcᛯno~#nAypv=dnwGtv˞۝z;tG}]gt l= lft~´B=Ivo4m=AXq>A췻AGʽ_b^?qw췻A_a+:EݏZ? /?ow7pW{h"7G{W{#Eݍ6v^ӑhA_d~}]GEݏzۋ_}__|ՇPKxEu56J#svg.breadboard.mosfet_n_channel.svgW[6~~yuvVìT]OBT `_eS"%c5jaTTzQV9qkYٮc[(#v_`͗p8Ѓv9!]$^Fm^0zMr_MsWVgUw!NԔa\CP0Dbl\϶ 8{}Uve(jƃg#\U鑸S:oѸ*? 'Z8쿂uYu!Eo甅0E;{ϩp<"l~%[HM~ݏ?5MJOՓ}# i\QaRg5Tdjg OIRZ0 $8ʜݭ;i $o1-jOT9TK¨HU h&'Tyi2k)? h>f:P-q':EN:{МoO#}_7uYW4ټ+OѦ.JQErF Gw$# %cf"IR}"/"q\ІgThвԪbC),\.M+% 24qsk:JI8wHšb[vH5+;aD8גNIm a?c2p.k}i yĺ z#&ND!2`(O,L1eNK5sAST:ˏ! .<@E }kBl1y)@CeA_ns"O}7>kcN< lri5։GۗՀ*ަ3 DoQA%vǬcȲYhO#Ղӄ*qA1KqGsJr`eUR:̱O)s/H_ޑ/\JCZf /<X3KX@e\=,H'>Na\VkOr-Q󤀜I+7f+ T(.uU&/ULHn2$I>4qxM@ϽsW_oVWG3ݪY\\.ώfw6q_{-/۫oW_[ Vo>f緷/󇇇Wjq3sYLt[@O=VG_ˬb#\޼0)˛r݋rRJ=򷱅xKfoauz{~43$?Η򋷾_-^_' qɗedu\>T,KH4^/o vX-.׋Wuu! Ee_^ زj<~Xme߬ZZd߳5|kbUkV`-z4[OqP>A(N񇃺qؒqjuaԵ)A8m=4 txg%)j~=RߙAS*33Df&TtTqKەSD渀U@'_R'jh`w)&mrGU6'8Q`&dew*8Ql*\laiOq2c%Ӆ5{Z`(:xf0qOYQ)˄A{:Ng"j!s}`"ب jHؚP`YjD,VU"9 'OyQ(V5RLכFi慕_,5^h,5=/:杬u5{Po;g"0K;z"9P~|gfSe|F^\6QT3ywru1pٸڦkZy1NÅ]_m ;Mb̈u/1ԗd~1iŌ=N{ np}_^ @ ;KANԖ6ؗJ{%*-m:,O 8L>zI 8y yJuN.T8@^™Kܔ<+;c@&Ĭ!Po;S֩^j&jAHސ nXCVېzysULKaT,z!o(|F" X;D5bҞ5QfWj:6 %HOkrKseQ!ڟ<;qpΟ~͐?q~ǔ7q .IzLC緖ÈI{iƏM&LR|k64hh@{]Z>;۵|&GKpN޿~$wqMY93CPA>:usCPauQA[g]nv>CzMs>|܋1Hf(݇ڜFL riDwWkFL9yLOTrTCn (M 0[psύdPc7l,G Rn(q!-D! '3s5[۲բL!dQNW}#|މh@aUʉȧ*ckeb\Jqw%NBg/ ^ȶ+ʉ!#dѡabg;}4e'OM.%!t4 +m>sPϖ]9YSFqBE> IL`ݹiD( cm9J@zA6ȠٔحPyhDpAVؐMd5Tq\9Hsi[5$6f# m")˂+(*b /JиgAI0uVKD3 m#ZR 3 CXfO3M{ xN6<$sCGupE4P(C[0:+rjd'Aj-iڒ|~+6ruΰ~j^\˽o-*cJ -9b2Q!&!.%\Z` }̽ l(wC#ctVSGh+EB:1:Td@|.$S(r0".ngF<\*H` #@낕SL7eG2:fT{K"FG#K|a!@MH @(&:ws0`.kPBy,ͽ7Bɠ L$,gw,.-)K܅1:[XH݀e0f` 3DNa[خ=J}/Yz dZr``]ad}OݹD757:Pf#b|,SO}_PKxE} svg.icon.Battery_block_9V82.svgY[S~_1Q^U3ҹ_\X\\ ߬֯?_>ΚwWW_n6h7Vc66'׫y{lƸy&2^5aTqSt8ѯz^6/Q7M&ǿof4l_M&www;;^/'ofB '^3`Εz,.GtwԐ7p5JvɫOiH]ߏnqr.fwoVJk?ǫY|zeIj.gwMyus}>׳v&^¤to{t;>̛OϨUM!zCvU9[5. E1I3*=l)U* F_%"(q;#n8t&R$$:om\K0cP"S(/4e0'4EX[磠 FdD Ym-BЎ}R^ 5-yc ^P"U B Y359Lbs*mCc^_ڽđ'29v(,6{) 7QB) 5b[˧-/ kX.=J%@[{KkN"J"p/''OHe@=J02R[ p \a)`.bg[c/E{ RsEr#40 FWy(hJd7Z0u ZgQ TzJ) GTcR>%Z ,I9<Jj>;.vp X.䒼2 RRO!u[!5vf%}Ђk_ T}`-ӮkY@I@0}f`4#d%g-笫JeQHQ[J-,V$;kF0r&.QC>ʶ@6r̶IE|֑[f6#GSPvꊄ#-,-p_0+mAJ] nڦ{uA퀓zO O(]Eo8S$ iA51Ac% F.*^s=nW?Hh#3增w$& PPlCt= FV:FօSLuEQ5ڰ6clJE(u{Nnqq6Zyq3$A YyW͹Ӥ*3(N a|BpPrˉJ0U!aW;n8*A5Xlk$XG)BE%THQ%PԶ $BꍓHvʞjvT"GlɴsaĤHM\fܓi uyB(hFJUmp&H63h(?d*K wsk/yQsb1dى_𯀦 6,A)^VE.@F9^%Z#L|`pm[fgV jAֳp:1bP{㖫l4c9«Þ\36SǷ}:Ztʑ@4gD@bQc2`D7aܾ>'T2SŹJjr",M+y|P <ždҿA:2M9Y{*q)*YJ헁3* /w˹\gT>SK bxV!;4&ny952#9nJNC=O1E.mi9=#Ӿ#0 Š`b:>T[o KQsw3?h'L7Cd *:Yk<]byg^>@ ].4֞L- 20耈>-mMu:f1w9񇃭{PA0 = 7Hoxd'{TbN0 f qgB&4.oTEŶk/f agFF*\܊P~i)Y"[ :Z%)G< :QUtm)) VWUr[Z+̅Sg0 2,k-U[6;Zw]Q;Q+;:fvi6ͧfƯɄ*4GM}Z-7ݧϋ+xZ\4Wyp:!FއPKxE\pvsvg.icon.dc_motor.svgYr}.%a-yX)".Js{tRU&˹ o߿vw|}|O_n6_nOB{o W2zcDIgwoLp_X}s UJ)O?v7bȟ" Be_lZ+)_J^wHae.)ּ)MwR_JBϟ'}R*hܕ†8mǁ`LgL*HX<)i o MRYN&ϫy@ˀ1& ocEhӫ#藒 EgH{ #jޗ/U: '| WAX㌝H9aU|700 4ըfKB Ɏ4V5$2(<'"E3'#Cx6mŸqV0@# N \Dd87x`j =)aDs~ux.tMUUyO*\| 2܏W6* Tȅ T`'eV& D*"`4O&Lg%vAH!t2С}q@V'&# [P1Eg{6q!ut e5j$J4EXԩ7r=T_E5BZrlV׷-}VPҹza%Nh\APo&%-MMCznK{ +.4҈LmUڊhiQ1@?D6'uh3։ȍ6[OR@h2HᝩRhi{߃+y! I9IV c5O}-50ƥY+r\>c{cB:D |&#U8@sB4yw+N& u`7a6$g_eڼ C5F뛒fCf`l;nфyHڥ[yYVp*_Yw“B(:GbiS |DIԵ0º2 m L$.PP(0ҿ06.Bs3Zg#YokټrNzg` _@Ä0MxCh^"ƨ%sVlѱ A٢0Y #MѴhڵH\16U<:3/Ș=?w Y"bbBcEve% uUP?OlUmOlOj•IcT7*F;McLD%BPs͎tn.6vʼc{?b^9ѭF ar&00Q* H6ogu#j&j™yΜ|,` 9f]hDB ղ)BF~E7bT掮t${FEAS U7*5vÂ{v>tC3;ʢ]2/+W 3 '9nִZ^0'ĖY9G ̽zȮ`!1:5-9ZgWrQdOg!thSSȏj h^eQm9R>[#֔1n+iwtl9m m$pM./\| Tjg؊qm!Xy!%؍bjO-dФ2fYX;"7Ž 1Zl;Ό['t3< 7fIb+2-zgCO+(4)XW2jrV^-9Nrx% 뗜SP"DLouyAXO)05.cf% HSb 8b\:R` };׹PcъQ:iek;'5;s!-&au aDc,Q3[=S~"\(T ᐩ6WA+BW^Q'-OS~PKxEwی "svg.icon.mosfet_n_channel_icon.svgVnF}b> W{VHm ^fD(H4e;{Iɔ=;{,?Am?TvPLTlڮgc#3w7wdXl}Soч{>m6!1$E>?vA_7a? HaB< *0!e@qhCGXG)fISmT=0I{{yMN#>r\SF\r3 oEM{̐Zk~Lg =%9?VM9K8sJYT벉PUqn "3\}[Cov[z nwߔ6%/f0*X[kZ S9ǒɔ smS#4 e CL2~BflL&%ecV%^d{K+FS9ajSKK"n ),P/`@h" | 蔀Utj_䍏O( n4<^K>eL`T)PGe'~q]y^[eJ:%hWW8Ul)- 2W\(.hru4zN5fXyYT1*.~\Ba PBcE4t:(%Q WG ^'s`#6T4 c{=RţYO>p!rĔ3ƬMANrdQ7&u}` tjv ;*ŕ1(gPHЀ 7`=7k%6V,>)0+JD-Q sjR91qu:輌k(`g$s, ' )"m ci? #~pp!6ovI96;#[-VU[z1Ћ>WdRM0ؗ}*6q\Jھzw-JHzC]dR@: =uӣ0;+dn{}57v- c WS?/!fd$Nµ̲zV[o^@Z:=s$} V'V5|@t}&ƹU łl8%--p \ N:/]|4Yr`^(V?CnH|#1LvlIeX_ ̝O8xbJ#{ /<7mX%E; aDƘ)$?ͿPKxE nsvg.icon.plain_pcb.svg1o@ ʗ#];tjv[:ra i__*vl9H=~Nܼ<-s4vZj޴ϣ\MWjjiѮ9wiE<.Ç!#'[Lz cwxW˴0%TN#ͤyOR|`&~GM|mfY.ǗY*̳OnQ/Y=}9z<DL!jzl2766j*m lJ^=Bd}/I4Gx1!@UQu_EtTmC) B1Ƥn< G%!HEցz"<BHAa4X }RΔ(rxrO5c< {q_fL%$I!&41p7DRG<%8B2MF%l5Z(e"f0N-W; re??PKxE^y svg.pcb.arduino_Uno_Rev3_pcb.svgŝG?E/! z>D9͡$-`o$4BQbv5ZϗF菏1jYxY]\| ?{.ogw+ EH.˿J mѫ8j>m?_tZE8f6yuهoWl>tv~ֱ^Vls'7ݥOWْw1Ќjsɘudzp$cûܨIlG6el >6D|{^wv \|.uxVpzsA+cuOm#BB,ӂ #M$iBb뻫U<w*|!muw\l d[]2JdAU$9ٖo JdbSeJ!v`+`UxDckV֙ 2KN%%3P/pd'fF^6F^k_J#B 0h V0U Mrvsrà'.@ gm)!iTg\殀A#O +V(6C,+C_E>h|k2eU\ N\m梎ޕ˫J"/|@b#,%Ll%Iqn*mLW\g~c,&z+Ȍ6I2GRʕtXzofڞ}ҏսIo/%!Amm- E7>t შ[9u:`#'jȔ".H*`NR񙗤6EJ)'~!S)XlQ՗*1*:È-zИ|+ A3Yt~(-!2H0`X"ņJL&Y|\/iyfɗ=+bHNUaJOv)%Uӡ4-Ȅ'j[p DxZa˗fx-[ 2je *ʶWjA%Zj3΀2F*np KEO`]ѐT(,ָ)HsR?_ Dtei:`e2 s7ԱtZ:3i^KF'RgXåk{X,K 9TjE0v䰶qRgXO4Bt(43,(ʠU1ޖW!cWb xF9l5<0O^C Q*j,h(. mp{0jߡcO3y9`j %jߗ#W&oʑm.#.]_`By#=׀j0{Ey̨VkY,PQd @?lgyTV3l@-38{h(.`pUF/}6f- QH-b}Pcv;&y @fjf} | gvu0{Ey/+JKUfϰO*f}`Qddb_VOTXV X= B%=~mUJBы|`NMGg I gi0zj>ԻҐ}5,/7үcL;d_Ș)~#2FHVJ"{̫nv~y垙l^ם__ m̼;Ъ-m&T6}P=ݠKӭC;kh70 rխC{$e^yH;m^yHhb/XL uU{wp%c]!W*zZ6_}FL Kj_/kҏ6zOYRzun䘀hK $1첩e{6;wt!lHW'er쓝.6={6@}y<=`~EX$Wl߃AbݔS1q|?|~=RpԹ!!ي_[oKmfd8K*)c$(GwL4\@@OF|y5%=\n{mvW5z4l19 rz0M dLݓ(#dH%SJ}rSf& a\m׊|+ZL 0^@d_yD"_uA/:M kO.6}J94gC(^zO>!ȳAߓAF?lCFp dk -W p8+gy)ꔜgzEya/!_4./W==¶}\gl\xxFzu}z{5z{^?+BH!l¸m&M~))V|tyoŧg?}Wmť5V"2>}hJI}2{ `׊`'s 0gqO:92Q4q?6ǎF(bAB1w*CK bsYFlQĊ؅E)c2̞JQĆX~P / xvM Cd12VI|# XB#3&"at8vZ{pLw{JwrQy 8-<7Ղsvg.pcb.jumper_2_100mil_pcb.svgSMO0 +L.IiCM8B$% tv6!l'{ω/jxUkkF@iڔ^Fk 3}Ե$hhM B5JTZiO7a$ٿ߱C6JJ''-UdeaF(FH4+f;;';FPuܖ}H˩By51!& %Bo.*]̾,1ƛ #E]Gȶ5ٷs,g ¶9>309m5rjEV̹j%vK1F6@0c5(,jeγbqȑaSp0ټvGX곹fL'{ڴԗE+uSii,fu{V/BVJ`frqK2Wq;0] ( 8)5}XPO:y`>~U_"b*iM+VQq3-.ۖ(u+@^x$ڱ/!S ;7$ ^O}J|L(೬Ml4@q澂 @;~Hl7M/VxߊמI\EhwUuKWz~# P?@:DzF277RWPKxE渻 svg.pcb.TO-220.svgQo )=mRqx2Rj`}"5i'cOqVp`hm\UU/x`:Tk4mT'ra,r$ ܚŒ:)%S ۠e0L&t{p.n~/m65 VA{D2Y,DVd{hMDABL_ O -$f'09 gӎΨ?lQ uv(1Ye0Y+ʬ7v}^@jwQGF)⾽g9]0Q ƿK['ddm3nt QL, cj8].H\a@'V8d }vdq$ Lrfxd="!2čGm3#=Hqh>11Ǚh}6;Lrϵ4=&Nn|rs/)2\ygmue2Vj3BڒMӹjN6uGG0q 2e*$X`T+$s)+ZIƾ}\ f6g>%6h;̲]_rY"rښ|cqY{6'?8e`Y߾رv$|x0+s) 2%a*S]׷ PKxE.S svg.schematic.dc_motor.svgVn8}v>ZIQ7rqqt7@[}Z(2m%C/Rb'cú sfkgh%Jy<)&*ExYO,t)sYuQ_V,[V1!Jh}_͢(kt-gU1GΖ* ze?}@jn>]_ l3O&#̶/uz&k̾,EG[;&1F&S'HM"|p4U=9TG'NmM}, C۬kbѷV:8BߧR:<$.JY!ҋZ6mbCu\n\Zĉ𢔕,WR33tTI*q0uNTeYm>}C`Sb>AenB-Noah B X.bV.N'$`N;}w!vN$KbSձ<&KulT# %vY@ &kSwlk>j MY|+3jZiڼc HSG)SE^[x2PyYG]wT4 lȋ\֤&"¦@w]t@lW@ k } \؎b`bxǨM`3Ug9s#B 4c: wmNcqxh0CxN~ ]PcO8qv㉍>sH<]J PyUHL?qj"++%R*~C̈lYOZ&fe|٠C%r~f3{gVSU ;7Aӏb =ޥCN!/]Ғvvb ~70+{<"j%re:Ĕ)6*w3q=;%E |%֙9Uy)@^fJhr;Fbv@0w }u߱bֲA`U:(je͉c(ajEō)hk`kƎ~)Z)ȳ*u8E/P@c8q_Uyq숶B;!dА=Ï0|:At-ObgQ9YcqYrjsǴ~ŇM”D/8]P ,;id8"prѪu8p2`D|:-DsgQN\w&{72qQ{?Mi8򶊻H# xX#s=vïɤ4ꊘ+ͯ(eLYe.|lUٵp5<Ǖy\SwpThtAmQb3_̔޻q%ţ<p Kz߉!2U^8"c+L7DPν4*h)Bkn]ԐݧoTfz3v\xΎsP: w>9h|VqN@bVϷ3`1K;[t:8T;91[>nWQf8J؜ɼ5!uaӕt,OWbcSA39IZI^u:a5u1u}Qa$F0~ZGT*pH(XibmoG0h9P;YuGj'+b(i;j$36 78P#>@C|QЫ MFi UE /.# 5{*P?Iq>a"Pr>@C}ơjn8P?8azU_w ^\Sug^6׊|r/^ N j(HY#!(Hүw? C]9^ $Vpjs0ތ},faŗdW1߂^~x<|/FeG?[2DP@ AWo6,5:3)3L/UgY^uJɛOS&5Ug`6l@q譗Q|<0-sY+]emKc eAS][{k:Xá 68v -XTI`yL|ʔ^d9& ~̟/c?HBcBT89ky8- zrHc6>O]ܵN.s̮'g_Z3׊ž2lBaː9lʽFm+?mP4jk M̡繂ud8TnToh1,Ncz+RJ)#EN1>ҺF2rxdcL Zn0u5KZîDkggGڥ႖sͨ 1/Rk=V̈2pA1,UR~J4[>/Dq6I4K{U- fFj&e*]pcjh嚷MGM> 1X_za0y)Ui5>S)KK4f lL7 Cf#, (IJe `"-iJ uu-%u5WpN^:%d {[o@~v3Ql z%v .#:zL;rK[+RvY!˂rf*r8M0YʊYC݌="3j~$?oT$8=eS 21;dxǙ$d~C8ToFpﵒ,ϓmZ$e,%)&'b洓ܤ-=s3/0(, 礼dJ}0{<엉M&L֫MjN=i]Cq$a{@.gZVg{D|m86S*d ԧ#؊MkmK9'2H +ף›gq|"z֣;) 5&&ܦ+&ge *UU &|~"'PKPa@lF&ۧ>ZfPM>S<7&\eEAE V8cG|)B2ݑjG0Ev̑^3]5"R˸€1שU+7?ʩ#5hPY)R0E+?qK玘ȥNńzTFLc! hL7X x,R 0*X\ypq *i \V,.zq>M8 L]y$<҆YIMrVv0`[HNh)6s嵵 oN;ZO`Kc,U`([00l[ |πB/p"%޸X\Lq^)^]y9V@3*0:|iwE;xt?gV?&EqOs@4{mֿ80"sfv?&?ݴ> ǯ_n';O>x:t!44&)߇_~ίHj [[|%iܴWи?8l ^A2h1K.OЩ ]yp*)ʟ//魳xu5_nZL7ݓXx⚘_*̫{$ܠ;3l;hI:[^F?MDuHCq7z線ҩ^Ԧ $P~ϓBB-WgBޤAy7Ge!ƘR[?¬N{AjKgHaΐ0r_ `z_ni9jiG&ny̯W9 [dxv?]3TLa5hhw QrQ#-[ xys'Ye⢧&ny1cE=1Ɏɢ )3_2UFpjFU05dT\?jh[{dhhֲbqS.nӬf&vYi.@BfHk(VLmy9I@ ]]1lJIXtMM :UW46 ]F&nEê:ŪVEᯜZ}P+yLj"_$@ʆ[(a4F?*|YCT;O*| iGdS".*UC*n x|FCn'%zj4+q19V KU:i(!kWE[TM 0╯4YyF$^TVHX (d40pRQt-+4u8hHWXe2եIR2x b(uöV̨JUR&"P ?IWFHtY(DRyF!5)`7)&ٱ2: ZV,.%R4Af7=ܝfcҬ|GP )%)NU H3GY*  UZPZڢM.a0: ^քbhhֲbqѓe}^Tk`4[b̲Qw.amȦV,uˀtóVm Ϛo󘜭WnQ_U6(˚;cf#thPUE6:aw GˊECO6f0Z;$U`lI0Ӹ!n97Hlwa-H8n4trUJd4oYvFC"۝pu#}cMMXw2%R/w UHdG݊0nnY uThתGÿ/3X\t;hh0J0oe&+οhqQǻ1F_G7s~'݇]vއl~1gwk&)\H,SKx!yƍ67&uT5W:n=d 7]f:%C C5磱+֯Ug){anbS׶Z@Vƶ3'Gݮ$nFxFuJ>ĨƨdoΥ zPnU`17Dv3^YGA[w٨Vlg[}"vB3>5U:Ͼk&fTW%$JTGguy[7X%i59- 4Dz(KvMa{%&H rv  "sh|..5pD$8)e|`2j5IM%O\"yq22^q=2Rn4ˊE; yh|똟h($&S$1aДuI0۬KNImXC6c622F 0q6 c ZV,.vuBL(رQSOY{LV8ӔkDqR*?gTk   cwL<> X@ @D |abB5^*;0eaG >+2'@Rґ2QJ=9R [Ez=nq_;*D~ \D¯ 9&vxL^GĖn ;~4L,LYظ j[TKqTQ*t`bhl[E]|*0!F/hزbqa`ÏE30vL&3&qNãҁ/zO]fT X`_ʿk4kYv!^<Ɉ'~6B qyeXӇ04r"pɗ}.{}d]oopmS[r4x1ISpjjLHm؁]f.uu뛫ey0xtOJQqNѼ~&%v6IxI&[wޙۄFLԉLU%_'ᎤP@CN;ͩ/C7H/BݿVk,wS.۹Eu-OQD7 cyZJ/+@Ww59Ƿ*Y\yhiS눛ā|#ox{j tG9@h07d*|I+~0@4B:0>' ZxNy"#@pݹa#z]b:z z0RTh.븅<9׀"q/R[x&"Po_1?_,i]} j?L[wc}!Aנ{Ϳ@=eVjs_4ZR\AT&EE޺4 gkGe cO4j /(M] B2cc׿oG3=T/퐌U{jЌ&zQz(!QYP`) Op<ªTa ,gvY'JeZ/V0.ILY;f<mvn#؀h!-F6a]^o\gb @ x,ctjȄd4^X a8/.e Xē0oA(8D?a67Hf[i ފ^n,>e|=ʊcM]丟EAWMdbFF,='[a td K"7j)P@FEFcm^ /G(G!]0M4ѩ CiF/󋱚<1.thBC-m\f5 p oǃ( HS >>_xMpCܱ7nEf(F0zCSۦy;RMn_԰$tie>!xQvX$3M.`jul@)FC@w3f "a /@FV$>F9fF:`+ԿמHF4DzФ3y(. N@ f _H#~ (3)g '9߁OU w-`B:$E FűQƱ;P#HÛ*R“`a v oX C 7 _@Ryp QP<ʈts^{^XD;R[T"$E_Ń1Cd!'Z9h'ObM0 DI12 @T4`xThB%Qv>`{R! C'uGql%G(hrh0JQ]SB]G V/tt ю@zU'Ex>`LN 1x xypUPiNhTH2h *a`V)Jo [ʼ1 <h_3oSw G:Q,'hHB0`Õ<ڂ@kyvJvC#Bqhhy ZJ׫:DO5 w^B @{0,nMr'100ot|-lp/ &>9*Ot h= f -mx `2jkesYBL R׎dw"0Gd5AOH+CEEnSWP'݃H?())W sB,p$I)H;%DXvAmhy: )U``C<-c@r%pZ޵,|SG<N{~S{hv˦>2j3y䬊!0ey$l)0.9>B7g9vpLQrB@3]BRa 17Xq0[1')œOᜀ0'0%ޛI=[k{vg:ndj 1# 0D)' !p h8N W;(4Ux;(kj1.$ArLx$( tмEH4Ta' `Nc'G@CF$cW{ͩ.KJ7~Eڅ/_ t䷐'U1ſx?2'ܣ 7Ш'iHD<*DJ /%dX~ B;dj(:\-Ő7f8 zD1)21|iQEڣScGkP871]bLK 3z46PrBƞiUoi@g  P4G2Tx qɕ¿hGB" aV0 TFFӲIp&=$V|x6cT vZK)F1%Q \Hl!?FV f[0Ƈa8" T  vGN`29Ǽ"оBI;8tƉÀDp#4:8q;;#^F1<~r wA>5N+cK#n09Ǡ7ochXĉ f()إa,T\@xP G)kD!T'lH~r")HS<$0_ D1L;lq t̒O! @hu,? (/PL~D eBPA4cP )  /x4K},d2B< ]%<|kԏ";Fю$c@쏀IdDaaL;Df >")fpC MR]W.SEQWlXˌx*˕ OGkFk:cmkUJ@/Lށ 0$9XW 8G #AjLB/4AajS>T &a2E 2\:rQv.ž`f!0[d B;d&$x= ~ P2ā)-g "A3@fp,R 7G"hx02k Zc=Q`mdIF> 6)oW#ve>GʍRB¬~pHpgY (JȺAa`hIi^'0/63d0ceWҌEF,$RBд{ vD>=,DT .zOb 7:C^[dqq)qUvǕ'6q!(8&Fc ewj|dž~h)!b 񏼥rP5F O0{ -š~˵DUBU*PHBPy!Tq#:qRH3d Ԟz:TPy#ghLb`8B4#CO~D& X8g1^p6#AlT& @ʡvna1[ ].&R] ( @ɼx)dMJ?k VFa7)ð+#9b CUDn9R}sWsYo.*,&&p&k*,MHdU)`c 귱}Sny`wp%$äѱ#'^1'a8K^?+~ Usb: 3QL9BtSbE/2xTx(|}[Q?vW} 3Y}baU(GQhy!0bq IT8*2V`= & `_"ҿ4z$ T4E<".(gǢƢb;9A*4z XRXqӎfyt"r{qS6yq b(1l$C NJNd oǐ/C4D08 Ĝ0|F뱊cpмx{ωBД>W4*,33rtf:KPp-Pp1~žllE\)tqRY".<,ӸNA1S8W0b kC,SvP6 SEPy@G0uM Xk\xN2h)5ANPNѹ!r",XTO8= kBpn I*P0l #,8Yq pRKSj??g]1DrUFxP|MHo ːK7 1(L^RD|pp18qBHpt ̫ø,8V2 _(2eq6qRy4xfBܙϲ=[؈!߄؍ç6p ŦAžn~+_WYS|ʝf9ov#c™t6>o4h˟q/km5GB¶x j.p#t]9tgm\܏y+9dJY!<ar={߳; .67X|_G,U1a9d+WQ~|=ݵ(}Gu}G9f?p"\CP7~*I,&9ѮJ΋;\R_R|l-uk^6:˛;:ؙp YGgL{Hrbofߊ󭯃̸(}bfTvQAE9 =Z<{% ;NuF^j}4ΟhciI[U|.+]曠Ϩ -=³4VT- %A6]QtK/RG8C]"'2aR=JXe^\w%Tй[DI_j'$PqX1ד[J[]J Vj '捍jxFwPhH݀sbr+@͕yƴLBjkٵGGrW`F]Obf Uowvɮx)d{<$*ZUNS!Q3rX\t1ʫޤXxBr 7*¦1ѣ_vÎfamQ#2o7h&VNnweI45VR'[>4[˵w]`p[dm>EE3L^l tj ,K(.yBF3tIrL&'ׂA㿽 CO4 g5Į&`x,EGs9rone{UQ3Sog7cnEՍׄDDɗb`KFf$)s%5%Dh`C$n2o-Yl}ѐ]9 yWC\Sp5j[R!)\/Mb~ħfzf:p0̆F6أ9 T}pZ|?؆Cζ68X4qnPK"d)+vL*iH&3nYl HC=$;Ǎ>oBuoAapRvCK!^]pRت wceé.Nu88 +|PKxEY)| Verstaerkung.inoMO#1 Y!qC\J+DiX$A6C83KlǩN pOt#.4 ;jxd[4qѠ =#tAIbJ8=0ypR.Q[a8)d.wiZlԜTpPe?pk|*xrEgqy$j̿{\`&|Rm然K [-TГ8 fq2*>P3g__O_op~q8=0e9,My?e#5 ki쳜Agܲ 6drρuKtmPKxEug6=$part.arduino-shield_r3_two_layer.fzpPKxEW Apart.arduino_Uno_Rev3(fix).fzpPKxE>L[Ԇ4  part.basic_fet_n.fzpPKxEE]Ş part.Battery block 9V.fzpPKxEQVC{Y apart.dc_motor.fzpPKxEmY(H"k part.Half_breadboard.fzpPKxE>6Kz.8svg.breadboard.arduino_Uno_Rev3_breadboard.svgPKxEx )7)tsvg.breadboard.Battery_block_9V85_leg.svgPKxEC$p~svg.breadboard.dc_motor_leg.svgPKxE%%k.r%ssvg.breadboard.Half_breadboard56a.svgPKxEu56J#!svg.breadboard.mosfet_n_channel.svgPKxE; % f)"svg.icon.arduino_Uno_Rev3_icon.svgPKxE} g svg.icon.Battery_block_9V82.svgPKxE\pvsvg.icon.dc_motor.svgPKxEwی "y!svg.icon.mosfet_n_channel_icon.svgPKxE n~&svg.icon.plain_pcb.svgPKxE^y (svg.pcb.arduino_Uno_Rev3_pcb.svgPKxE>7Ղj;svg.pcb.jumper_2_100mil_pcb.svgPKxEr#)=svg.pcb.pcb-arduino-shield-rev3.svgPKxE渻 R@svg.pcb.TO-220.svgPKxERN1/Bsvg.schematic.arduino_Uno_Rev3(fix)_schematic.svgPKxE4$fIsvg.schematic.Battery_block_9V82.svgPKxE.S Ksvg.schematic.dc_motor.svgPKxE8iOsvg.schematic.fet_n.svgPKxEB9SVerstaerkung.fzPKxEY)| Verstaerkung.inoPKfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Fritzing/Wahrsager.fzz000066400000000000000000002644171272363601300326000ustar00rootroot00000000000000PK#8Dyjf) Wahrsager.fzr\Ir0dcL}G$JRP6u1x"yHVj^alwfYdɬ6c'{{Duo?^/_| Ãg/>_a_7?|ɳiۿ/뾞C\O'W/0d'/~Oo:{/W/>8y~}5/޼}lc|pO탶vビΟoun9Wzx鎾y^zxӓ'%'Z,9q ֓/ۛ}pwzoof=/_<~ի{߾|,Ӈ}OzI)Ocq?>ˣ>igq < _/GZ~翿O%˟^<{pOmN~mR_K~w}?~ߞ_{n?>~3~w9ɯzӼ%7Eo|yqg__?>;6u*y7wodkrn.m嗛gsg?ӳIm.?͋o26_=~w?lw^zų݌gz{t7Օ湴sG;n_]G|x'S54Xne~YyB^.-y/ZYno6`y_KgM\]躇{O{;{~[v [lVbWl^?x"|KnGZχ굗|CuF<<}w#nks׿Ta |Tmnm7SsPwowZWww{-~ggﷰ}}egʾuJYox`Fr\F؇e$GK,2{.3uo#yj#z;Y2Ӹ.K/>, ߲>0sd y.)/ܽq:,gM}e}$|o"ս򾛊8Ja9h쯰sao?  o=^ U> 7ZWvvb)i3œ/;;N:d_d}XM{췬wlnbsB98rCʞAp B\1! ܂oiwj?cUGxW[kzq폟$5?~mtȍ#n{/z7IZg%}`~ǽTP~mCnu7IYm7IYgIYpCxq}XFqn}X6q}`&_:c͸{/f3uh߸~>ȸ>6"øo>,_~%}u/>,o7.kq,޾ɗ5,[q}XAnܞ b=a>Ԥ(׶>}ayyr{A5q}_|[ُ=$n_HmrW' #C}m7q#exYևgYXgCqOv>ԣoOևe wOևe [f wz@rmW;Qym;QukQq ckAێŽ\{wC{)C`7]r='[۽P*rZip/Sp?PG{Nԇ6흨+kQC{pomCn?YϷpz;R7sxonyś_r=Oyg>/٫o>goz_??˾y_goÕ+۽{}㦆bO-Y-U-׭=CdHK_:..:?i޾~)߃y#^J=8y̛YO>4:{ccgJq_E$5?w/C?7wí`w7Wdf|ݨ@_9qM{`tvU};L@/v+Sx/;jI:sQTYƸze,t_J>F|HP$@FYFs|*UOcTG(:r^nMu|Qy\s<?Z}z*mOcP4mwNĠ)u_Z)š(U,e]>kzvK,_<:vܾ5QhO-c3:{Dݰvwo{<Aʙ,.h#vކ+} Dro<3{i G{s/>{ퟞ>!Z}mɋ?ˋvN~7[wK!Ͼo'oLO~wK<#iG1)x1i=vV/<{ǓlVa}Jǘ4>LŬc2cuJߖ 1. ~IwiOt{n?:$-R.ZG/οo?E]u Vi/~?gdQgwA[^&-H|U$j\9:gٗʲ7n\)SI%cHAEwUi'i.Ҵgzy=>{|\٧uO,y]Al?_Ϟ9;#7 ixA=8yy>,߼W鿝xG3_z]Oğ/`Hxg^:M.6g\O̾>&s3Sꎦa.9}Y2L?aQb*4:1zcTVV1X|ؠO2(_'/~ڟ/cw1_x%/lLb}2|gٕ(Yvr DC(-Iӧi%Fwom+}eF]O9/8׺N"T⺡Dr o;W{r+wF@Cٻ}@XFhsDhwQ(p<>bY7Gl[7ʏCB~.Ͱa_mP*&% |v_ѼwUd^/Y1=*XZFcУ&gŠOShgiE#> O9<5էױ ߤ__=yo~@jW]kkԔu maTiZuɸJPZn.wZnϦ>|/ K\~q]$ֵxY[w50Q\Q\y7 k'8}V(*Mmio78'|uJkq#̗Pa#鸳wۇt[q)ߕ&9bus!aV{oSRH|b uL' mHGwۤYNl)qyڽ탊DsO7hsX[;m9}Ҕu1=jVZ0ZPѣ:g .j hMw_ir$0;xwk⓴ y ħY\UK똼3se|՘Р2ݘ?cv},whwV7Lංi_ZӺ_?@s֜Gh0uqW@n-BwAmޯ^Q`:} }xm䣬CO:m]h|.&M=Ucz_7cHBvoPBѭM؛ꅻIⵚym$ nqLj÷}G+[NOdRԣ*?her= Pg]~' 8m= P1,zu/M|||l 'azO?thXD o>ƧWn|:(ѻNiLcQ-2uFW[R!ǘD?ZcRb)~;II`h4N۸tj5. }BmcFn ͌򺥟A]{;&`ZrK睸^eC[2}ՎWNs M*=w}h(62wEQy&9~V88%1Fe NTetZzԱ5mq*F ,6MR:nw; yom@=WnxUYo!_ޒ|Mw}hh66wAqQy9ƥ>Ȧ9M;n66$.^Oƴe?2Lo'?N_>"˴n˺c3ܾǀC^{͸Ўӫy7"MWפjqאkq鄸1 =!&_kDZDѣ ?h%r H|󀴦;z¿uZV{K>+&zYwCЋGL, V޽Z[SǀPwmJ@טl+Y)}<BQGu qں/)țf?}:\?ދ8a/YcxIT3/Qճ{۷/?}^iWĴnuKY)]KjS{>ܯi9(ϱZ>Ijh&齉#9Fl_Zq9ȡ~[ߺӲS~+wfWÆ;=W&/QW[>&NՉՋuc* `?;KkLc v1ghc;e0śe]7_x7O㣢!zY#FZ&s;k{xR {=Fw\z}ݺq̾8rLޢ8Fd3DdwD%uI=Uo{Jy>1]?`>9Y$qe*ݦ;tGq-cvn>9m3m6ܬ2M?(NcHHOu hP>1ldQ[YO1.nױzݦcsI-cvn:9n3nC7zPJYјG^r=g@XsS]cg @}p[ $?5 nj\JoW{zzJ 1Kr nUr?pw#[#܂.%__ `79s^~9+g߿?V'>ql:/7oaNO}xۓo^~u2_NOOqrZ]7do˷۷~2~"i=j, 73c\'߹o>9|¿}pKIu_~s\nΞW}G2̹S?'J__͛]1~țJ; '#,gAw.O)2 y6cT~FtZGوG}o+mC;ﱜ8t:oi_O!4 (>_C47cj*S|QtM5a*AS_Vqi=#vQt?1Ji~]JZ°bu&tQn& n7VҔ~Hy: I>/:<5yQk}3{ri B:3*)%*k0fBrTjk4>V^bhiiY:}֢%A>%uѕyyNcGۤl}z˺FK=M,maK_|Ő|#I{!Z3-0-ahC4A^.!yC$7ƶqCyy]bIEwl^dq$a+JS8Z&΢USvRG,`Vm97itl&\$0Ϙ\Xe =bs ͦ G[W̊6R\͚K{T$1gbrM&GJfR\.--=sNoEZQ)iW7he ǁ6 hhc1 ~1+)2$,=HE')R6yAM%j}fd$L\] 39ɵ(tJV'CfSNTOt7W}6Q6dv mipfm2_mLߑEejED]Qq%M]g]^|ҧf\ώt>߭ etOՈR"RzFɨH:xMO DzmL<&;~r-Ҙ :&%O:M a`*%*HwލmhnbTѕ}pm5yw=W+JGbtd ->P;i5QMɓ!,Ʌ=kCNY7ټo NFm^ℿ}!_!k I9T'$qO_X$;#!ۘWE_*Lr)B(*;qjxaauEH&ߩ6mr>{8ʷU+UԉAYHvDh41W0,я; QG.hwR]LiT&V*r%Q4͓R'!S^2`mhB8jrNj;[?"_ʹf~D ,FL"rp+&^{II鵮$FOd2Y"7/!5x4vR_-7(abb}| _gfƜE+J)͞4dD̸dRV1ʥᒒ"X$:V.ʔ0^D a%*s .{oDڤ&%i_w*J]gZ,&eg i٬D;U/i8fzh8LbU z, K^$`&KP1gԓIwEUȩy`"WDxVЍɊ\fuf_4V:U "Z< TQm`&{20uEE.Ґ8|V')GOy*jHԙNRyzx~5a!;j ,0\)qv .b #۴x-uk2@kB-yX\{,d21&"R}s-0o1.-^Y;W DUIz_DBIQTQcVe,c(w3)!YTiM[?ASQ&[մms 1 IE$i jFi:FPwFҖIņ&4z))q:U-U:YFe&ɶ(mvm9E^j'4*28 V=eܴ_XQힻ<]q:xjl?adi梐#B#+Y)mnK-SmyC Q!Tr *6j^DPBcvU3YT*Eœ^5N4jmaiDEEU4 /m,%4tdeyE=hյ*%'*+so1F+M X6:D?,TyҦK&5alE]S$Ay Z$=U&F:5>Ib4S\/;83y{hAwT9 זl*d39Aՙ?RXK mmTxՁ%\pHOZLƧ}Ks6!SXAmSRqítwΌsӖzR]T{4>()"mTJ#jOrd|ty䐥{g3E,z<5A[ȋc"ITCJj (<)8IUiYѹvg8u9QE)Da+Vc S?VԘ?bi>:J(B䴆We;?WLv*hאڔs:\y d@b9+ԉ7 212ҵe{qG&neH D oJ NctMAČNDH*/`!b$M90oG6%CD4a< ↾t$7m?GKP42:F#u*-EٲJ=Qc v`3UyOr$efVK2i 5CN#3Lv'Â.~^(4dI;+$n>lD)8E(l<9ld2ՕK10$"fRHٲAacC#$gT)F26mMZAL&B逥cS֛0rHALRCv= `c\#;9 5s(e; ^REsG;"m8oYi%FXAdA#[l3 @,%K68 Ӛ0Z#}սx(iڕ)£좌"89vZŰ<&7K*:NLr;RSF̧yc usKMA&U!6F&Z[y$S˾]Ӓa[!4-Cȫ%;:P,V5B=^ EZqZRI5%xsr'guWqoA\tH 4kAKw0 \ a ע?fhP^ҠQT' u~[۬"VSU_m99HY˪g)-7I&[m`s g %mBYjjѓ2HdkH蚤[FlRV]sS"N+~ KOSC&V7VJ|RўTp>)Ymym\NDw>}1csy^&bGPJ)PX\ڲ Pusc%ªڂ'`,(xOL(pQ9;DQ\^Q|y:)J&?6RMa$Ha)Hh_PM s:Q{Q"[SNT LEjR?v זּd aL'h8%Kl^XIw{N֬dDj_QXYͳj#$N|hOhSJ~v_M %Ao\ Ef#)]'s2j˄讣 cg ѕ{0nI.F.. /LmF˪ `+OK9@cOYPGL68NSG[+Ai+B.(XHAGox#'7f#xE𶎩Bn/RDd7GMB,dMYӔ(4'auԩk%4겳VK R.mO P{ .}*g137G!\M*3S{Ne;>9RAݳͦѳwຸGK2c-ve`oSi+m<rh*2X̝9Eu^"GLԁ*-TZa ,i7!"PyQ k  \Ai/(Ti*9T?"iSvNVb VlJaB|%)aIOTU-6E^  FN*kg%޼ջZ b5l.hi3uWN|1BZ_G55ϴP3ms=&09qT2J'kgu=G–@ 655w4zBd$bϕ0YE[VI*͊oCI ,~*`6d<%apEx \UE|e K rبM!u(Ի!%pY`*}<,ѕn 3%H M+zC !(:Oْie $qNFiF)iZXPfz pt]-8jdBDxHSuმHp!4+a&f#ç`ڧyc {!K>B'im{$VeۜM2P?¦I_RؒA p&3C;w0x\GPgQFuDc3!*;`qD8dQ uO dl1t SՕpe]@`S섖>I ܴI*Ȳzخ,% q YѥجN:kqzRp+BJ8#Mayǭ+I\eӾ3QpfM4\ՍN[:MV&4U7px-\ r :8zrW|~:XF1y܇3aꊯ ['MLFvT ԍ t*ĘGfw2(=^ Yw蒂߁NGDEJawdu'έrG`QE, 1454?*Jie΁#6H$`I[%Q>)v>ED^2*Z3qkUl̖G:zj.osUMd5m6G JbIY{c@@o`#{+q1DR DO'A ʡ t ``8ʑi3pP(>BOtK|,Eu a/DTS9ӣ].5 O x̜{a+0B8Ie7v2-&|=.e.3fr7ö#P/`(58W5VT69RQ3iiTQlj4z}t׊Qe{a-7GUtϥL7@!i<DZ@eSq% ][kBC+wZ[w~%2B8ɲIBTLAe P* Z!*&Arxyd,(צ h L\bNHc/:5 lq0A ON2x(݉xۆ͋*ܒUVcE2#6 rai'R ܚB˷YqhP8yCOH$I~~;&sdgzmp#1+ש¤?%$iMA _qMN}NWImxԶ1Q6A#q$`J]- }!-fJmO5~X' 'Y7ٱBD )°D1M'!z{Ŭ٥: CsQ!M̏:v Tݞ;ݱC=VXf1f b̒$e~50?-7Wk( M( HħؽIq?-ES a7Qe U!uۿ ˥)J@^lC)l_S5 a! t07#aGk};6 l8]zT댰8T, jIX2k+ 4C+:Kѕ3E7ð# ) 5AKqwS'\ACuu]E1N03Ճ\DZM59р̖@OKUcSM=$A t o]6J8cԩ1-ʏڙH:dJm6϶ ^Y陬);R%74A<[6+dȫ{❐+o\bɮ08%)髄)yfd+kx4G;2y2699MMYv4&&_- Q!nv/pPvI*"*ɏVKZ 90j[ n.-]i oȢBǔ ڇt2LJejbHe `n;%hTH%ž#:5M3 Ct_ WT\]M V([qp6 szz>^Yl7;օbiE*ڿBPX+/:A+vsU D$B43Z$ImFLGM^=A\doEƾ9c}\U$d%km!lsҔM'F1`V6y8_ҙ*–x 4 R8'K[A0tFN ؐ(M1kb0>T-boW`f"6UÁ'tպ7* Y`Hh7N؇ 0)4w526gf+rrX[3ֆ&fpp6w)ю>oDBV*FԐ ? H}R-JD>4:A vDPl̋h_E"o].h;NO:4`Y&Cb-8]iαLJmt(Pv0]HCJbH='1.k~4W(-#N-߼*r%0atΨaiN> nj7] j9B SrR;gO9~T1)#S{paL4 oʺRS{ޝxMX]QtB-*D@- G&i|CR3<7'&F{+JM69U6vyl JiR\mOr*W\[WK;yOR84B{wl ;O(4S;rBܖj˚ NPr"KmG"58NvWr[6N;ݒ>4Qn)a mn2vKt5duV$-23V>QkCGWv3\ƴ̱%J6m# D^&|r<3*HɠE.Fl*N:C "`J;+ F>,0mHw٦شaݍ~沤nNM3? I95EyagTmHKls &zWe'kUs86w/y.r'&'M2^}1Zy'MeF|(B+(th\^QԂbCjhcb3md}>jg𙺱 OB]ħ-I0dq]fʎ;\592uzHPǥ06d;1ޫ~H@1qEjV0%Ǭ- in }Q*lpF%eK{״y~x dn* )BX(&wް0? =ږs"[5m|-Y; tFU8ft (nobz4XJ!mS(#F';PAcq(0.MQKmD0=М8م.^њ(y '{* !*S[Sh7LbԼGԒT˜:E:Yb%ܟL3> =,#.'ԍև6HxPc[<qeY [9G,Y8 VK,%ib-B.>TVACI52:7PUAtc#${RԔ邥i]<(g<: >^(5"87Rc EQBmz&*%C4^gwl*fSǸ`QGx^RyBNpBغ5<ǡo}D8e~eXJ`4d^X,E g=3Y:1N99R2$^ d#-T=h]-(hL·\XrIeg dHRPZE^Gc!>s4ϰ؁D@(KX5K\n܌bo3Gjmp:M U hM?:B%t Hb 7c*)LAU> jӥ< eTmQK [фK5)!Oyl^Ӫ);}[#RСR*hg%CUt[5DnEǑpG]:N| Pg'.sqχ<] af'"Ly(lp"UZLpk'G۰j893 Dip.Δ' ?BS{Σ+!^̲ 쿌;hAJO}zt8Cczr0jY:)L'/ܬ/%2,K',1gH. N yiLAџz(VntH1Q`#:չ|;5HH EuQڌS%5B=$*B[21 3҆Ggl-)Z {{teߜkԦ&gP؁Ȭ6'(`Qqx"iKi@ ,ٶ݉EVE1ګlDщYc>z%:FXm }3\dRwUt4}`.}(KVզ1m2Ɠ2`reU m)Ȫм d!v*9!Աub􍺯f9˨( 9 BB4Q))z6y2Ray$ok TRϩHtN)fenRICPfPuZaqXhyu'Dǩv2(x)?<o 5<@`{tDhl7K$u'D iiArH t\"M'N(ҚY](bRTGVO!AɒVsIEdkKIu@e#,4tb&Н͙uNa%#҇,jdtҡr.h:#Nq5z/.}s~iQi qB@^UH v&1$  ~0~I T3Cת4,d"J|ZʘQ(܏.o퐠$ 20cqT5 4B'  -1^O&xFz,c|]e9!jwz[] Ѿ= 'H9rdGMK uҦ4f `tEXB?b6҂ܜJYTNק^YhHFFJã3 RD& q@s3䝌@Q t(jBIozhbj# ZDhWZt܉tr<:0 P^^i2E2>) =#LT0iK+%_adno}q*bሯ4J5Gg$`$|'g1n>[sc>KQT,җ`fZntAI<d$f8~`%=8B5#.$uFjˬ(+(ѕ.ElbvŽ 7D|c9w9GtS/b9ҹjN[\'G6;ZLȍ[@1YN1JR!Lס*c$& pET7V?[ ZL @'@>ɿu!ߪ0Ylf-J%3bJzJ.ς'F: u9K蝂ƙq ۥh8H >J@7#- <ҺVAl s]9GA.(:N%$Կ0Agܶpd,m(>RBsxD^B|2k&EJO/Op-#C)Ŗ16̓>iZ{x:2Ԩ 퍼ѭX;tN/sՂ-$%0YXIU.2 kO.˓BTwL΀wJS6 9V¼qN= 9EIBdZnm%Ϙ}E-_ͿZO,bѢS#=)'{fA{fD%~sBѪn [Շ`ڡI)&Cf]b=_jNrd%4D~&Aݢ,(*|,1cTs.ѱN xPcF3GT+%>䁞Vzډ@ĩ$$F^-yK~:%$Ԗf#1?gIz 8 baKz!:_Adtxdp¸[^;_p.˳Mx!3dR"kD469y<4T[{?y˦f21GճBY]Eb:"W V>m#ѥs=n|Y(8b)E.c"'hv{m̬ %:\5-M6 f!U`@D8᳴e1CC־3YTT?mM6szEV7'5ۖ0YI)^f1 8W+1ﶎ^ihyf؝ש7k^*E'4f:1-N,~do!űOvca,CِO.LR_`%MR`ڠNr$ͻY8SM0:!j*/,*:q!=ZP뙄(bצn }VN_,s''=%bPRC9JBg/ ;q+Gp= s2B|6`ZigU;ܩgJÐ[@R.s1A;^4\լKxwnsV~NabKŤX9RRIg*q3YhqdL[QL$=!&R ?;e&-TH01O*Nu:iVmhţr=`.1+ PMЈ RP7|hO?!^Vr*򒎦:ϓ75-sh Jz+ + >e4$Wshs r[?@;JT sB<=ب-}DOOm`#\j2Ƈ)wZkcӲ^gSJ1wԸуʖ(^Ju[ޭHeEuG,Y"迃& iC;Bǩc uC)8qaZ^Z2H<6 .A +!*E,ܵr{I)y?*DÖCV1HEKҌ+;CX|48YpK6 O*驒s]F\FY&wj&ll ; BFׅފi' iP2Gb.T * g! &J;9` |K,paZ4]Bǿ^vZ;N2Bo-=Gh{['m'3X6bk9K;?9}w. (CV'|b;I%|Uc˶xB_O$[.9{"t \/6iWQ9øcE}$jeKg4r_w;y'ݩ7ds&ߌXգ1EM5& LRr*$"I `5FѢ )^d'@,:DܬgVBy+"E_.6֤́ e P'PЈ@6 '.5ޱZ8bD| T?)o;[|>؉ t7uzjwF]QԈKbcGl~zaF:;xZ,m !.'z텝Vfh(iF9_G4 acQUZ&ؔ|zx1Oih:_B+sz.4R ^^$g+D3E# !9Tz Mqc?e)i Nhӓ􉱺xNaʶ\a uFP&ѽy5o+b#ab"&MKH!X94,@P> l'(ǖ^p`R6+fi!9J= @ѕ]`'b W3FpqGnT:"ģ+MnE *6nz-P@Y߆ JyB)孹Ǣ҅g}?e0A0ψP+?X}RmE9O286/d:X4v|[Qsע:Ԇ4FN::0ti (U0!02" `b)?䳸P,fRٗLTRž+RE`,:{Ed ]P)Bg*l+jQ_hj)bVxfx z)"U:iYBXWLWPʛU;Unc3Z䜼jwv:9Л49MipExo4*]7fobeenjAeM̲5M+Ϋ cgu-\K+^,)i%%7|RVY[q`wC! R7FX/{ w"ɳA㙭vcG|CC{ ov8 Dr'/>\l9879M2LBRc]4ZtETA\("#)kŁ'{ˑ^,t; iq2hۋ#6@B5~ ZHo5_͹' cdB [ĥ[ "eD[)je*qƒp& Q},YP|.ÇW^@k~z㡓fk jtc2SڄQ !J^ui-c+ov@FZ^BƟ^r;,i^|GsÁy~& )B;q"BM60FЮ@|8C`kɅ ԭ8'?E48__w%fv_[g.F EM!5uMLN)q2X25Q#5KİIej^q1YM ?Ф#PTh4.,Y`YJP ,}3ǐWusS^<F?^ɐ4-,tvzҽ>)ӕ:NkTZ('S`懹ީMq!z*72u>>9JO Ɇ$eMUa.BpJއ 0SXks.e˳:icSx1Qm`Wi2F ;w<)sIV$FLjT%pjbZ]D']*.Up¡4CL”_nڻBqԁ{eLĖZ rhm'Rp9/ZVO4In0ih=|P1}ic @?0֓|QHQS|/g!! O'SKphUK6LNV~OB<<SeSHW孪 "|t$$y;{H%YPZȣy_ hnj;*`@UhaO;+32c,tͰ]ÀpT%I 3 Nx̀ΦECp Q7ǸAh}׋eB&1a8s mr*؜MC&3S,c)݈3OǤZpU ދQIJj[Et\cṘ2.J^sX ev xD|Bzo- VRNa/FHc(彔αγ"l*n|,hI{*~i*APc;H(P%P nNk\xb &&;!O:@%}[O^X)ҡQ "UpÉlsfnH`A gHU(5D_(<1>ƶ bMX䂺af WY[;u#a8 %%F?I9ut [D00Jn RP͸fW9S}Vҡz<rkHke}y^&隔X W/\邰W@lu<}0>땂)?v\ R&1/L+\$?kyowex>B1ժ Jb+pE7CUVTtB'n4]MwT/ Y'? ;G]ON$ EipG;m;0+p?6yW6ׅYX0D<*Įyn<5}1]=Q:EVpW 5L ;AU] \5@n.RSNѤq&TW$L"zW,/1tk}2" u~rH+Fg%2UpHyVن߹CEI U4˧HaEh9uT$uOV 97{eL &=@+h7PpcN@ 3 i1b~g7@078Бmґ&A ݹ<4X8`(_\T(f’FB6,ϗtpZg0u79q@E/J*}| ,Sm͂t!T?03a'1m/A, ]P|m@`J\e- J0v;B.0'e`m^tG`i2sv#N͕. Ԡ~ `eEgfmEJ.Tږ#\÷.r_z.>XEZ$Jt$D:|Bl0Yi@?WUwdYR dP BŠt[ [k E.PzKivQ)=ո PJ`')Ɠu6>;0Tz@6 Y1՚3l~)ef8֒fakt|ph\N=EFӄ?G?ގ1)8A)~ir? (#߾l m3hfl>Wf{AB2 $Y" ]=ϰ(cɸl:lJ}S }Lwjȸ qbɐ+*H >\"S UoL<:JOXY"IY0Lf'r RԦyJU!Fhs$cypW(𬔇 06J#A !vù˲~՟|`Qv>.(6x`޼QzƸL"-yڅn,x53ɂM<:@zg?~c)`&|B^ -<йAWXcoaL tq'Y8k0b AS?^ b6EV d0 %~T=Gyx/vGDLTۨݹv 3Gخ:7vt 7t>ڢ c< l$=`C]tž}2Rc;tLN)Jz/ˇ=0 CX=#F,Aڲ/-i{`$rg.Ă4;sU9wzܙȷe!-5mJj@`T?w%J Mbse{+ zҢrS#'"11|Gp#Uc&p¿%qӒwM@t?@֭AANpeSVQ6Ød ɃSEmš GPNÉZ+USJ Q0}xJ!#y˨0s> ?]Ão2:sCfs[Df< 9Nwmzl`}y3͢aRv1 `,'Fkݳ+EJ`jJC3FTIKA' +\i7l)ڲ}uMC3N MQ#P*A7}naF[Tڧ`Hݯ]lX鱯]>!p8to,+<LC|"=f2ܼOPC&g?SWNѹT`^9XAͫnkaWBWj%ܬMV bH;^gLXO+Uc`,^Vd8# .pRŇwc۞"g܋lX.|(;] @*"CIANz^|ؿ,7Ũr):w]ٯd"H*tT폘2ZUg܍Zqi ?zyO' ~YxZJث Zq^S,SƦ91~[_vfi\u$<YGQs+Gb죦`oXnuLk7 mD bƕ/j$v+1&{J)U|pp :  AK@1XOF~t>ʌJ0B ;k>#z;cHr(H#Xwny)? K(1{[pjMnWatO`:$6(~97@^fHS@֋ZekJT;ѳ.rGT&ur[Um.˩Rr%YZ$ye!\yrE?|h+6KWâ%In/C],lb>m.GG[VQ㶗= `<;1=NfҬybcH-#b6ۗE kx-2>2h' ??L;6VZܦ"@s5,kgF{HB2H.S%AfLq":a1vFmZCuz_WeHъ WAozmyG53Z]{yKjĵ) [p-+ƢxfP@JaqB$Gcgۍ^oBXCݛ{BYH,QK%PXp[W/P>Pjsϊ:• JW }X<3!3@nq|× 7ht),esEJL֛&Fă[QҍV& *jM_BP]ftL1xƍ0Rb[bZ ;rY4k4җqRJv*BxGrALap(v{Tܑ(v8qxNB+&BhQ|6!D(mK؂O "&>fL߫m0fXl~kXF}r6[-iO>vQzz~W^zHJl}&zd[e#&K˼#3PSh2~6+XZ"<Qe)L ^Nnр{x}޶?Z-" =#DS:<{2 1+7p)k#7-6L%$%Q= {Th&+a$4 mD@#N t 3_ G$my<<|V"^l4 0N_X:'p'@߲A?f2@'#F~Rss5 ~:>WKIEg2 vmX%+|0!UF.s@D3DhBuf*|-A!(?uF/k֟-53ۗ+4El dkcX>?Ad+/Pv6K :vItL]5U% a3zN1֩/Þ;M1Pe;_bL gi[45E3GI*XLFpʇذ/د\ y1ܛYߢxUt *E8H0YPqr2-cEÖ:{yB`f<5XjZZzQ7jڬQ;A JT ȿ fK&g"[A]4Hɯ#+SlKb0ol󺹢>OsR9kUy/ ۠lE%$he-|Rp#3yV>W9rf+2賀5;o#!bJ S㵻nf]&)ްl.5Vdiw߳G Nf *7RAB ߱0Dp3% Ɔ#)ya &5sy񦯯hB9⪐^  c. yp < Cvm b]" 8s<<5(^Uϗ7T Zft#-J-nԦLV4Զ0}4 E!W+@>T#m8tW>h[ <@ Хu/~ZZ,gTU'J {禹F $JSkIӵb4yUr{/jGK}S^07O8SuA#r :[{)`k좱.c '@Re荦ޯ J#=x%r$W) Tl%)9cJ!*̨ Fg%*EU月 * WgF6Uu䪩^ե?tЪ (R䩯L4W,ʥuKGi"[`N &R1PRc5 k4A ;˴V14$7o$/!x#΃`PeǮR`v(r ᝁ޴ kгFUZq&Zj.FUƠYۗbu-h/m;+*BEr3 ĺA+Z;B tZ?9Yot8Or؇]m]z4n= Ɲؤ 8&xwAZEә^fL7 q[ZqȓK׳v G&k֓Y[k=댭? ~9{&ΝG$`ir7=󂧀&OSЗ~8*HB:y.HV~vO 1Ơ;+*qd!Q5;y}5qfLӇ7%Gyf&KЄݲ"&IDj9bFa̛7A47ުq`.oHM +}`ܷẰoW+ip%krj'J14cXW6M[S4h/\`61!c4r%մ*C|k,9nv֡qSl /ɿ0ic[ MoɬBӸEM ;J |ocP`dWTR;VRjJaJ͇8/˟$kc:qkQx?1ijrl*?ղYBx]S(VABAx;Р䕍_r^=ڌZ]OyxC6肉=1E dERa E9WI"; XЏl%Qh Cgu.af:l\BX˛JeM+ MI)<0bpD%o+)^6-z~87`0mJ1h:R3xanӜoY[# (M6e>Cк!$:؊L:zZtϛNU3B0]LNŀ+(}:|M"왬31a I( # z4 $7\<3I0xR=9ERRiA¿=w\7;p]^N5fvvf_KL`@eMA+ߘngjcLO2Wt:;11d 9H$a&]_1cF`b1(܎"A;9P$6NuBp֦X( * Cw&Ř1EŶU$ &C,kx#_ni"= (7TAABIn* dtOJڱ(%g-7Q1)mo_vZ! ]BF"0vy;u`Š:hMFWv+7Kа}ڡ,li2p qeB$I{Aón ;^I-4{啦 `,>a6EXZSUC("7]32\ M5#\쯬fau5H|_;B31[b7(p?4Ҁ^JKL敓eǻfg*.ꐶ)kU1>i!X".yA# {E~V̔Pba3ڧ uMELX,إ;l`kޘ_zT=LOFT`ag΄2\즤*:H@49)R@[x<40e|3u0Ѵ>贊/fÆ#ř;MHpx BaF.zDqF"h"uQ|P*! ‚~|J9Mn۶,Ll|N1Sr9 @N=K"-BxfϞ {ͳO䃻- qa!|(5eP/wln1GpOD0vdK׺tLRR<(ӊ˸}Y8-zQ|FE[\qFVC؆1l!,b$])ɰ,ɠ4 +Nk ˰#Pmox s6;_l=RA7' a[UKblEL"%5LǖkӃ}ֳY6+iԷWF}~t?N$LdFv7ԨOEHi|22 }߂hQi߰AZPSU:)(unyQ=CUHLrV} Zij }h.j'9:V t 2v]aFmZ`e?TR|Eh[.ƂL'Na؛`/*)ֲr}aZHf*%RbȨy;9[3X Rsn83P8iy'Z UAz೼bϐf΢iqc]QYΪ/HCtlܡx:qmaSI @[nΨw +A%*@lnbO_`1ldx6)ʞ}%k&(atzUrȖ9 u76$є>\J`f(иEqʌdTl)4Kؖ, [E?e;D%p\=qv[#IS&V4]ֽ7I98e%!K߀Fժ8D;SЖ]I^@cfᴪ9qHppF[&pG )TR+cĘ8a_bN"ʤS-Ok"=w@an1x`]}sOZD" :aPɏ(ʆݶW.Z>a?w04u\STso%C28صOh* Ve6DfE7c2XFQuD vUܠ-h^>~x#^7xJ#X{-n4f|#ִer/ql/s1>y" ^R5L5#J(s14RuJtfZ<"񺬳7)|RciGU-STTVܲHԺӃ$! Jl㉙ժ86hP*S2 BNok/)PbA Sۗ.KjWm֮uj9܍z]H (wweF 05"p47 1I0Vql @M݌v~x5Cek%3?.V浥bC@OaFb:/E (=^M`8wo :S hNJc <: 1\W.PllsԵ QL-rЉ,H;T\l2 7^*J޵uՙU" ٯ̈́O9m+QW+\Xewl:mWqVʞMe(Y0tAcp6 sE o47N.;0Ώ#[uNN$L!ہO_ZT2mS`Zlߨ䖑q.kX_1k6NvD Þ\ pK~> w2hA{}hd;"\!ZEA0^o-`ZHyiC ܓ딘` 1zcwX[`L kS}ѮDWБFȽXE!tE&I)DTz]f" vhs#ҤC5H`B`դy.}E邾2"d\Kr^l2g0Ic0#OЀh(Ћe0Q,wr͊U9s59RsrUg3V@ V@ic>H7odǹcS HWafwη#δqH!*I1qq֮A4$P!GfG2-b\bsloMCo`#ˆ Դ0@k>&lX} (=P-Nf+i3ib1Z[@}dMA4 !܍ w u䃕 ǺGHd֋ޕ"~[Sf[cZeCVN0 10KlLZC1EEkxWt=n9jZ8QHJ4fPuk]\:XͲ /bOs([HŜ: &3!#KM:=}21G@qHРD A5-)KY+6nf7?-3􃴖<b X}8 *-Vpw+S+=Q.C58]mkd@To( Z[j>R4e'A!+H:dz@ fvӿX5K]4MSh~% fQq܅&]zp]PD fi'rKl[JϫfAcdǟdøBi.)a.{84àdz8a(;Ґt.OgmBn"YRJI (XU=֥-$,do_'+-r!wiưJɨ}Y) HIt ;n4A'8Q9 ǢlZ1}v':0 F0mo"\;UX&z @J#u/UBRHZtp Fߌs_sѷFZ>dW OL{`6ɏUΗ.6<)(0wA0_,Cx&Qgq':Bj^is@$,75bB> sZK2wGr_m -YN*Yzz$YsP71Ly ƾ>ކ:L0-OMy.ҕg4h;,U |=6Xv79.L knL `kcaBzz9:nql ތdklƒ pA!.f C0<,<70x=Rw 0ʄ6e"FfĘy[ s4+() l Nו 0vrr(_'n`abހ]M2kGI<HgE*rUCbZ:is834#gB2(+3|i[/nGC`zYI:Qm)(o>d\44>oDAAvVg}|'AA51NdܧoE <ÌF5.CN7b+v=7 Y^&">s;ӺE T/xS?lh2.^ N3tnچ[˲':̢(,1ĩ ɸ{|bON=uɒ`!bւmP9dtr/<8G+z^Og,,&g>EP?SV2 FTF*FoVdS$!7hV]H)٬MjxxQzk ci[שDJ[тFzrd_]3\ÛW gvxT ζ7-5 m],.{E3jl~cP3&)̘||Zѳ(6fQCT 7E!yZV9']dt>#(mLHt]p4AQQZPhS82FTOuH(:7ځ>1B9\:j;J>+JfZGj +ȑUD Xh6qዟ'E%W>gy0̩|gɆVVk 3ڒ{F7FRpKhX{ŶgQ6 #TFE8"'X%*f]'/Q6ya(hso?ܧ*H^#ZϑYbFGhIVl<{; ʉv%Ήۄ]g`?d@168J%6F+H?:+r>(U,v}F0[۷6:#p;7aڱ??YW07 JsenݥzPjh!B=}g*p$<8U7*u66} fѯ g@DotGP%_k]WQ ڙϫi<^B!ƴ'b#8eU YkS1هqHX^Eu`  {S, )_$\!E䮙$M~4+־}>ۿ7שZJ` 'ES+t% ɦɹ;8E丑'DgfRWoMV:|ܦ?@T#.!8 N_XtOaV1WO4Nin)XUMu_qlgMdlj"'c[ň՝"h~*V.&8ja x$QʄX0Z](zFro4f{Q Vm@  pew.|-J4@R5\l-u l͗XFӐ.}Gz911P0kZ`V4Z?8Zyapk6htǴ5:p҆a-u<*#5*cv)tY+a0'z)< }~H`]Ks=nlSnjNV=N18Jhx Uc/'B؁CŴ7<40NB{ w% v} Fbd/uZ~ebP&ѳi? *=S.@Kg>|ٓtDAۗ?\y e?Q'0Ív_J?)j;O PPQ]YH Fohla<~9~o7)MUfEamEx8#Fc ^^~P%zzp7aܱD+ {})=)sjd%Fz Ah``lg]?K=`wLj,Zf^Pʁ XJOA69px*Q m[wΎקM(AR?$$_w?Z:\kxq\_bVnV&%'}⌀:.2i)8 KXN3"B;w3:lڛW<9YݮR Yrnx5z^gΜ@]&ŖPu xؼ&oD/S&8L v t8\]ʥɎV] ɣ) DC+L]RuKJwW vܮ6wP6qۑmP!Q~C9YHY%=ipczs) kzZR$slEk%^C77zJ.ʮU;a-wRڜvEcmncoz]3mdT} Y<~_8plĄFf\,Kso:7 A-5:fl0ّRxx0zMc 1QBfI<3 :ѻop9Wnj(3:YzOQ_\G\畹X_П^ȹ)BsP=zT./?V. M`x:AG=mؙ5шu'>ʸppTg 7ȿhE#yS<$/%^i?/*OT4!{#Y0*Q!2͆zt1G)vfɥa / `DEP0321 4UqCr3@+4TuJ玤܈5}<nL~Dڷ;A#"8PeQs L0Q:w0]9'D~^Ҧpݔ@ֻ-Bŋ(h9Rsd!~6vn /0H@|X u^y:ˑ!i26X*xvZO%~(OH؎36xXWF7ћ%ixг:v qUS/xI7P):NxG7z@ z*>zP~^9:<(ԋy#@n\~Ü)L^.(͒мiRgv3̧i.$": zF?Vg+U1Qa( JC,CrڻZ66'\dQJg9m(힙] WLkl&0VX0ch@D+MpOHSU`a ̀Y!"Vϟ (k%Eu啝K;..>s6_$ҋe Wm,bRA V{/|6M@bK׈e& >+ڔxUB rjhTzڱHVw6"z3ȒRDq `8r;L 4 =ll)=Ljbd`n5  l`k'x~z3zM|{`ɵ(2w5 dHZuR.vE,Pk-'J09̪4 7ysϒZ-N9)g[Z6\gg3~fMXa=l,a, ;̎Gm0[*G0[0k: :=;Φgo^Z>lIqqZ#ձwK@@h=[:fh8>ny}5mhȉ4:xJac( ~\ɵunEE:(A Y[\%hp60a;dݾ`ZOpό[ljl=.:wz2'qF[yWl9]z(zaCtS~ ZlnL:9к2Cz /:u:YA6?5ib^~ y`q`릞Q8oz_& 6^,ֵ%fpTS,;:)>hgFZn'F@בult^/,|] VVut]?opl}'N;nc';__ /Qkӷ;y s?7g~^~̺;Txb}Iwwnco]w;zS ngh: Tѣlg\u: `7& n²S'f aw[7C0/Րh u$ ,>&d;`Y0S7,.e=;0G 2Nik5\BɼǪ-0`YlwUxd I(l[]aZh3>nԝ]H7I{Ay׸_A]lF.Vx*M{zڜ ԕR?:WA#"w=]-n}}p(GQ7 #5!qc>I&\):s^Wyd73 *{}]^LY8:kA9^tXlU Pp!CY7!4)uh ~@OR#[lԠch1Ͳ=UrRJ ׺jy%Yvɽ_7{)J4]i`S/1ٶpF}cߍ>Ve*i/mA6<ܙ &a61M6_׏cJ[$_sqf}L'5K$ b | hF}Wȓ{8yؓ@,װo [vVr4 $ XEFO}H" $&eZĉert1G,* l6MK4)cnRuٟJLA?oF#B=ernf׫3O!Uݺ\<˃g AEu3 bWrpk O%ta6A U22Z_ztN7cȿօBºp/+[lA+[Z\5*^@F~h)K0HX7tMs7jhF΄ Reop4!t#~CG;8}QXpӛߢN?g_kϘ'pyS}/iuAa Wt-idfk>OKn8+Pc;Bv'`3D8c7?1j߀ m$ƨ) Qnuy`Q9 :=@{z>!jY5 jQgNH=g#4D Q|g11₨z?邨\g;woo9Zm-8}3wp3.ĩAUAu$ NIA:nliRNv" 8mWo?1ശ ~~#@ fNjN~tަ&hoz '|Ebo3U8:ƙ(o<_dh冧ןg3|N7]*:\Er{5b`$rzL >=_m&n76K0}S ܳv9mWޓ \ si}~ { T)eIЉr+5i5Ӵ\B:0?a: (r?vN-m>2U0*D&@: PhRiY4X!14}^S%}$b`N$AwDV"86XߕL&/ F+ 3Q:1 [4% KS , U1RP*_?%Rκwғ3<{4E9azA׊MΣ Y '̈mS/Źf4޾#e,4wN^ZJLvܩ+f> EEOd'3[o(FˣKNoa_0yNv?P:1}n\ $4RByt{z=-H뙙o-l F] >Daz1`5'9E~>vDN"o${:p /-9|(9O+]wn?/.ԎPJQa 㢶Bƀ_R48X :c#Ӎ:hO0;>u?Mr7|Bu]%y՜0ᾒ}"]Jx| &Q06w8vh,S0a> 7ï,yNzj8Ͱw*3 =#+-CvmA Iw\RpA=t`M$Ɵqp·5ӯ:` 5,-ѡgZ(q{N9<\g)̐yQŃBp8 +_Z7i38 r(>wɆ3˞A7 4,iR8n1Z1[.>N:PԆ:-3]&?}z?_EI K"̲Bfd-0K_ɆFp y69A|msc)^חDC&ߥ@|G<)TяDeYxqB o S$:;ɃhxJtQEXL#۠G1NDfKg5Q@mGz+N^ 7@YKD/opw*g€&l)-^QZ2buH!I1KM!8ȘGa plވ~6d(_@y}ɓ1sºs-54"ݰ ѼSE˻CSf4ΪOٵVy13|upHd) #oϊ=s,Nzؓz3+nșWyDgsAWѱLd܋[l#p߭Acjc :.|D10s6ZK|0φ"V>Yؚ`sY_ ݦ|M d5g ̲yչ(z@J'\~<Հ\![%iYGN(JE.3:"-Vd;W\ac3;C ߠmEVcvgQyVY`dEhn+L'LNN,(3HN'g&ӊ~M49&+P3l&*H3#"DT 7";AW{xhoF%y`"M&7ADXn^AO[,GC,>tDP3!'"ADbtEБD$D;:J{;2.*}qSkgr"ӧ@I+DP:"x>zmDw4!=]"Tf* n-pkT@"(197{[靾"?W;ω92+VE+@Nt>Kpɵ!| m_ˁ| 59Hb'O ;s{o%|.bzoOlammV& 7c|Vt._u%9Eg4lXI2E^9kׯ6Exys> ZrT+،n>r&?"1m2 rhŌ!p]t <;$jɓ `#WʩJݸߕr:+L4;6^ݓ9oid2~F;urmI6_kb$͛d@&{3~/eo6h0SnKٜTZo4n:ڰ\ɉ `KJ q1Pd|S ruj+ųKgs'gLj:+1Nltl_JwjPo:?|/jvXW_4ܐS8`R^@C%}df2OcT~ܭ%wg1_ t%%$kz=}"щ+i,g&NϱSz7ql&jvlPpkFij31DZu $19dd=x8T{D(05dS 2p (dy]qx%JBG1s`(=/#U @ )mÅsО0*&G0~u=Q@f v; e%E814(n"WEiQd&0p[dlL9 Ym"FSžjʈz:m@<FS&]=uÔbXdp+?= $22QV>C^ /yw3dh/j,=j6PϾ3{>c6 CJWpT=B7$L&s%]&ԡpRyǪ!1wq!aw=[J<Ѕ nJ:/nL_s==Kb"JVN g5QJ[$gn:͏Aɝ)c  Zߨ_Pv==f4 a8!Y=HNpK d<"#r0bD<;Ɂfʄx,h;11p3uxBJL<2%x~ӷ/iOAAhCXu-06$aNbiǾp6Hlk㵘H mokO HYn<WaͳDg!֯6pa,Vd=GMCz$^pT/YN~.FbE]15%?E! ]aX9au1e!2is!R-f˄aô'ô땛YeLW W75hJb1Cd&Yq0$d/Jfó)MM@\>vP `;vnψ1~\{ibq%{"L~2K=몵r/ >ַSc" }m ]3sS'|{6iKKh4Ŷ`Ϩ;D{>!W }A0tN= ;ʡm}=ftؓۏxP傎i0L|6Iv,u;[+@N.ަm4-Y۠F u^?vȓP7(.(}tA-4_1j!<~.UNYAvkw{{ ](* km !c9)YCN 1K*JhUt'܇ <M - Jo`7̜:o8T̠^6U8ގS`FmZV%4^)+M!xCn"c-, ;#})B=7ZĘjc(`^zFI M@RO'Q:w#+lvʞgyBʫ(o^,)aū=D` @2e[+IE:Ԟt6Lox&kޠRbV3] qW%C7Y+hc9{GF\"ᖽ2C)<]º/Bd I],5h{q-9Hkg5 XU$!HC?|=>/zW7sDc\ ,,(mˉDoI9M1ylܼxfGAjЦS_]nkro,DibPqPC"퇍M CvAS*dB:ȼ7TFxQq9)؅+5Tҗ"ʀ*"ĤVaN~JçP t'+ŐS^i1/;++!si+JVSX )Dfr Y1ds_}5UCZ@ GN?݅0RX5$C!RjȡPϪ!%(T 饉jȺv"y0G4ĐB d &|TC 6!G}kCnEФKL!շMͰP! "EDcn 7dB)TYbB;z=hVWzHɗMeFzH7 r>Vi &oaw22|m;{Rsj x*"OO`h֕`7G<ޅVȝJK(,Џj&%OjF*ϼU9[zI_is\\MCOWm }VfFC OIUm\*xƫܵmz>Y)&8dF+`P[Kfus ] ١ApP )]D%W#`hZҿSf3@PVލh,34m=4\.K(;ZG1&Pn=ʵZټ 5)X4i^6߱RxkT"7q u禨քr(h /ohLr+ g|kuk%߶1*^O[@|Q*0EG0mAP* U L6.yS6tiFnJ1x+MF˽%*cpźF`%*dc iVWyUeK(].}&K;0T~ #87dȔ*AT١]ajO47/RO xp"CͱL0KrND.J]|ZbEqmohBLEo=a-T2Ws ,TH4"VHEĖZ dE@:"!Tݎ2"1XnBl*"TA!!E6VE$y* `T Պ-POo;“SE!YzEMiTH;cuW u ;^Wq I;-^%qAed-$ݳ\ - Tgl^j ˉq1,'&-03KU*_,fFK:FO0,$,;ݳk/adX#Υh!b[N[YAg-U׋]k0RxIW!d  :=%z-Mƹc[(JI+dTRj>?x%Dj4_Pb&YЩŀ\J61QJ/fP+;VwL45ꈃ.Nɳj<[+#[ fBE}AWLV)$F9e)$Ờbjڸ4 )&}LW܉bJNm!;oeCYI*oO"2,iPIGy5mj,jv\Q[*9lBͨ /a v{j2<+f=] -3/(騲 )mvB^\E1I4nM҂W],<~Z,. QȪ7,NLF3бY٠Kowg|;҂m:Ƽdzܞ.L`h{ d)8s 1FdɊ 5[ 4VC@ɋ,ߕg+nC r }if I) AelrØ?(jp7w}GE?0¸8jdB2J` 47ukA0f#a"tfuD"0 ԋQFQpKhfH[{CdY.VI}*`Mnd9 Cqiw l.RI}S`so`햆na6WwfwMfk{zZ!QfhoWw7D3R7S>$nW{2~Ң 6zr gJw ({PXSg@W{ِ Ts6|r ,Gg@]Vwx \)0Z3G_*S5Ï\0҄.9p V|Q B2J0Yp 8҆S r"pu .oho OhMF'R%I հ[RxZ{)ЁF*Hn֯&=eEؐ15!oH*'eC*ДH*``< "f #7=P,V+8 (B@ hY:$e*0@580x1_VmdԹb0c`"!c .FN71 .@QwS$jUQFrPpّF̣r(*z%+,!JS Fw;v8ئ(ㅤq,AwP YqG!EM6(/r7 ^ W)C>mJup F_P ($T@E}>SWwJXvq HX!jp !N]ʚdҬFlG^댱@w0^,5M',oJ$ gm E$@'x 㞿u&J! .TDLuFTzDeL dVs~ҵ<)eM;|sq%%V^߇3##\!@YZAr-kYԐ,~\( >۱*,c9sl.?QkQdBF{/{qZ?DoǙGoCl6 ]7\{gO4@ԎNSlqff0>.h X6:'t)Btۼu]=6iq4h\Zt{jʾF h1 %5G@`uzd4{cT<[%G ܳA mzt. ̐x:ѪS}TH(-/ۚNL@MZKZDsbA&6]o nFn2mcϾ #U`9r~TVǮq=Do)AT .`kw2m45ۯ.C1UIEvIT#:hn2hnrރ1%F}a@q$HghJ\L*ؓ9XN`ۘP+e:b`җ5߁E64P:sA@pl .aaA +D)z}^^IXfQV#+ V DYQ%mB TmLvϵUF#BVUm7ӀER'&8iM@б00\|M(x\pd\9)[o]cg@،Vƶ*V @Ij}`cDU]c,С,mbq$+p; 6 p r!l XhfEXXCv&QQ|d!*62Z/x_jSucޡ{J v,&v٢0q}'"~(5ޭmLCOb41D@8]6Z@62%T/͆ UњJ=3_ PhDQSG!EF!RAڗPL6ߔQjm̩=]mM{x(0SgOzEAJ{^pű_-DȚ !)c"Sx Mb(Fh4rhW3LZHs`3T >{EC1.QG2Wd =Rdwc/3 { DC m7@5M x`̔l 'H$S醆鞑W2.UfL~06_o+}+9k H)GC\ԍ\0RZ/\I(Qjh(]0֓l V+H!f3V-jg3MorV0ZuhL=Uvj~B x=(.ДX&Z)(2~DbH0 =E1d$6Yػd>jVZͪqV}EZ 0zQ!| [ ?! E[%rڂ2.QʞwrGMtFcyf{A$/V@NeBC &T8bFquURAl@7WCĄYT, +I1`O[iظrUEAG,znvh`pAH2z&@6Bp U͂j@P.TTTlb ɻ-!ƑB;dL.BɁ%&=^-di+| QORN' #8$GEaJ\ T Ŗo>-\.=!9+it8 XUduwj+*6)p*Q$t 9[J)#ه @Ȉm)H~we>iH{ʇY'dMšS0LkqN*-Dp*xٌmO%1E;2saP0AL_VO} gfGGboHu& r e$ "5%Xp+b?b<4Z쎗&2#M W&D1d,aǚI[q#NM`EUqxxaúzQ0{+QTE" Tn^%ĕ#SPbGbɨ8%:XU TkJ/DAe#ҹ6 2/sXp`QdJ0'ȯ6q9n+`lHrOB ?B9X` q+ X=?& qkW)76^,d R PEDL'6sAA8O"H;a7T}',=M[j.Gy4V, /GpہTRw$T5uFزUU75QnwCFΓ^kM F%9U\B3 '~ZE,ZMTDTUN{=f֌S(/kVW ]J)3GSk{JL#30 .3pٛE4H8 W@[l."v LE.D }:  oT6Njr23I,aO I$`"(2j!f,P7zSQTCxѫUСnDUDf.J!gPU3Wzd+j}GU]&U՗<WU#~+Xp-Ja7$  *B81bHQt7W442@9̈IIZ8p8&l?b$\Li`x]rȊ%F$39C;RPr3 cٯt2zK{/\6$EYC(dğL>R֪-xS{c q۟W^󣬜|/ 8\qZHmE`.Grk rbdZnGyՎ*$+p$)iQUhUޘ!+ _״zNLG!&D cn$S x"%]R^ImqW2֖R :Jð:^㱖(@"Y',mu%(E[KuS=43u3CEEX>zHLXU3aހ!͙A9 Ȩ A%Yq9cR_8vߣV-T@yd c!%U (dD23˨KwƎɏ&ŞA7au )P4NW dWВ)0^yO| H0\4l[E26dX_ ԋ+8)OՓ҄)2RɂgZ ))ԇA0&<qjș WQ L9Om%o"e 4*bs4 ё Ym7KCceСpFd0'9G - |^Mx0 4u 4 H'q #K"ݽX:+s5X/[={:=8 J/D V}C\Y[Rvb3C ;ߋ~T_.MUP*x颏)')C$p'#r!z^qK9X/b,"[*ԧ* CD}E6J*qiTIRcV,! 0^ WD7%f`i͉I%Zn64%hfqTZWO(:q >RMk7Bb/Qw:g$?9xxcf'\-]P%l,8 B0ڄ{?dC D7!gh8'˄M&*53[vFFQJhVrx/tW-/).-1$(}SU0ei8q+6zm20+QzRo:+.K}ɂ#Y?ſTe}٨Pucyx{313Q$-V-Mw aX-<8[GEmLpt@bԪj{F|vck(Z J;דW#`XTIMѠd 5ڹG|P@|d_rbfؚ&BV3RjDnГHٕ@BYaw@S 5Fb cd# %FzaM+#z(d|gyG**HG ^" ѫM"BYa'I3E BdDQ ,WT(ŗm/h? QEpjL1{X_,eVIqI(䗸$#3DXyLgķzfã)K1ɀUdȵa ڨX^3 I:憦YqĂwbL"O$-GVNb:Q0$`tYHJι7X{AnXK|#QrK&ś[^㬼J±fU!' !X(ځ aF%3UPhu֯ӥ@D@,\.LPVg %0]7M䧼򌏳;DaK JAV:Ψ(j3nhB,r4j[, R1ѐ!&Z!YĀkU| =rՓ#5*!N*g?qëwRD8XT-I@^;lN8 @"ɸmdwXGJuI@Y HVmz՞l`>)DK z?Ze ZԠ1HCc&+FIwA'e,0+ݑT-HGuQ`]*SU-bE-*M*;LgՈS%?:"H.T1΁L\o" `Dsy1uXlj Q@XLLz0*`Ahc^w$b{k UΚ5nvUɷ*a̴E'l RiBЉy\5@MĠX[;-!vbr's Iv"͓mMuSLuka5Ao+!r:dQ7¯}xt? \E%ZCWFVʪs ^\]`.̎9:9fVY]ԓrUtV^ FSb #P({2]vaC/Ș@a= H@/;Sak':B"R]!Qb-!DLMX`IFN ٬[ W1*Q9HSHʫ 8@ UDG8haHp-;;R U;Az21;YWYE@u(`1!@㙯?3 0XPU&*]Je׊*4I&FEr$'t6SoM9K"aA`|3L`!*lͶ̌ǁeVG8ҏr F&K+K]`dFBoo ]ApQ@F(8t|'+2):]7'$Br"˂gֿ5[#IYKg}vb;,̈́1QC`kFf&!E1Gm+/S~^4 D0d , qa01vԓ 7pU|`% !ׁ;fQϯ 1(PPlx CH i}UK<E69XC|xh4Bv (ghEn^Dӕm(< > *cE|lOQ;~8aMo0&c6m;zԬ ͐Yذb|%]gc{EޫURG 6{#hZ .jpJkw ds] ?pH;mu$ Õpc2a {Itq=48mC$1UiGǡKY+rf)t C<^ȗfsEП#eL@#V Gt&7bq]ґi1ϲEu<s; ص`yGHQ3d4ǽZrh2Ix=k1ɫu>8\I+^?<3@ N0 JWF۱?x>\x+ho&,|mL[>Xv/nr] b~i i7~ (ri|13l֧383qv`u^7M#sdLJ.m~B׏;uly*O?8m>j; l.3:`;E98m綍Ðv^.A3Wm๝qGhRn̨Y"*Xm:"i a?GTv8L 繨AGz*S~#Ցcsdf0 n$uV;cEhQئWCH\߃("̔8#skD_OpTmȨ;9Ϗ=hKנv +j\<Ͼmmj9}ǥxh|17OfisQ}L d^@G~uZey-Yj٫(,2rx!8n"2 >wÏ]d#qT<~G|68iGe##3Wǃf<7sV*:EYt-_εee=T/V =d12_iNY3ae<&Q1=|/՛w~8ח~.mw䇓W1ֶ8>_m.@+\\?+_xr+._oWz|񫯾U~Ǘ]W_U[_~Vf;Ȩ词x}]_h6A[y ]}Y{wð즻ν;zsGQ9d9ں՗'g._M? [mpjjS"޽99>}s|m-5t_-W/i?:{w//_ tt8wٻmMkOw5eyq)~6 Z6:bzvs 6;ex|qqw/~l2^iem?~?~;e[2бcsb.r\H7=v,+|Տ}D/oOC叿|^?4]uTNrS,bə֪$Mqn}>(n-j<2n'C>t#~ ֿ??\}y7vק1wǧm~|u4X?ev|f3bk(Nv|r͉]S{]ݚ_r*"kV\E}TϐE۬ۄQC`OpH$s(f:jɂZS!|m7,7ur~_4㳣M+?]~*[ ?_^. ߿\bſ/^~㷿[4ŋ_,~q]^ՏoN!^GXmu|훭V_<_wOmO M-yſg5 0.~S3/֍*fS>bt?e6&7||FlDٻ_o|o^4;**0&r;=^}?\t6|-Ŏ"X?V}fw7`rO,$ Xo:,Q$+oNlпEy US{5V͗}t]QЖA g-d촳`6P|e'8!bLWMxPۆ%>WtZG$G Mo"@ͧ`rp̽pŀ].[|b0B[-\k_a XyEdtjaE1[W!LC.XzUrkEm10`61.LkA 1 ^n'(;*g/@Bvi2gm e'<>[!LN), MRي ~;/t֔DsqhIN潗[#iS>)w0j2e9t@}"0L_nnWk%/ȟ£߂٫bN0W%1࢟l=Icmbuuom2spZ3R@""R4y豬QT3Mt޲EY-=ڽV{SotcK_ĉFi^R$zv.sTC2U$ r}~C& y&ĺ↫i7[K{LpqiWwvnbڙPe+CM7Cy^MUHI|n&k&5xQT95#/2ˮrvIeȔQ,V(? #o$f:RjDk *2d&Yu6 OB[!aLԢI&m,k0PD@-LTAޡ D*p]Lp,jrڅq L6ܒ2?6XXx9ji/l]TJmFTI1I6Zw]g>YT.kCU7wcv^Nz}zIp5`u9e%Xmu(_R{9EY| SPbl%m炛&kڌk)µ8966GzL=BS/$- X +;҄?_g42Qރ1T42jC7I*+;VtƠ@l<$DdžC(?(DTw)b"CW;lxN2Z3Ԣ)VC {R#>7H!rhҷlTWJjXKт?;y"5G}gxt48^2\tE$495x ^O?njgeuUKyay"N KI%@}KNe;]•0ZKpSd*tT, )4%UDn4}q"eKqd-v8ChB? ա8à(-N4h7^wd{UaYk"Pzf[B5魪={Uk*Y~yD/5 Ipţ˲ei}J+rjZjU{dI$`n_jhrn!V $%@u־4rT"YM!Ũ4j7Kk7d-\ƌDs)Z'G ʦ<&VF7cOh1Dg6u7[vH}ފCON:щ.KP@>2,bV+t qL+1wP@u wf94;GlF5oovEQr UDI(u)5&T~ӒIEA\DՅ۸u1`̶('h2X⿮05Xmd]7 p'`^QB@ݸ".`[LA\ŶurkbzKH).rdCHUj.¹WErdj@ zT4| p'F;J5-"[z$"@ӡsƧ&h.R)%l:s̷UvH@£lx|;sbL/gt$RJZ;)l%~wl@zeZÜ^i~G Cw2 :kzL-hFgI[ BE', 3`g[ mZūeiW*7byvҽ@\hIHidY4&IIn Ͳ6)}§4m!ӾRnޠnt^ ThOROOvÒB`N]xl{$f7rc(+)cSF1)L89CJ >A1ikϧoō9> KX =f`$4uso ~\INl W&xSV#Xe-o4f*' |Yp;r$*rRXNYVrnz/܌o3FM&oIhiȝqԣQtr+\[\m=Pגudtk>yN8 ˙SDT6^\1, ߜZ6'se5,oM-U K-2r01-:܌ ~A8!L%}f M'z}B=W=&BzlUacq\a'L&jL{HXm%fL? F%?V]e㷎)Yڄ{wfʛx]kQ\R|m/mjjulJ/9H&7ѽ㽤@S㊍YB]gH#g=G堜͎cZp6 sԿsF  8/ia*nؤ̌J0gK ѝx^rQjyy!eFl]EF$~Ex}j:rK8~2e797\dQ_K:}3RЫawfjz s*P4:3gFȔ,fQ=fS5j^+Z3~̏jl(FMWE1Ny{o9KLITtE,hoh$ oKN^IEi oo}W5 G WgFĢVh"<&;ye|\P%Ojv}l{X31t9'urtDlQ#FM&5m&5ͤM 4T &I-TXYB,3Yw0kl<%TGuT*P;lLc Г #̚kIfN(Y٨ /G`6k5QsrPfp0j2UNh-V*L\(+ |3#j <3 +a& {gDvnEdaUn$rXVE=`SBh7[Y{l)oͅFa>EsUfuo$&=-%^hVYfڷR| C}x~zg*T ێU GN%:8 l^JQ\9L(n̏%hXc,<^ӽ8 +l`^,&{q;ދq܋m8;TRZt}Z@8z'f.l3a###>z0Rz]1{TJa ='C4|s[d>(b{1"KיϾ,+\ha9ۈ,i%Se4񍨓d ނa&$ [C"ꛩ@@S?|RӋ/4W|s7ǧH*s_>^|$p>~,+)zo޹|'GF<'>S#k:y;&t@ B!7Ѭ~BW Y$O.nbE|?dk7e?h }v%kkǮvoNXC;W?kG:x+|xn}{X5!ѕׯW_\WU׸n[b\bv=ٽEtX_ v+ї~ד=0KjL:ՐO6Փo WG?6uڵ-z//ˋ>L~dʏ~oñCȏ}yuO.?mÞ@EsǞgNq_Kn&E\v ~Ibl^~G>ټ>'='9Pf6 S< VS w\I`Y߉{wǘy̝l;>eGQ30)"׌_5ޙݒ%"Yv:f;dIxIQ>C&dz io#;=!:!O.Cp0)G׌&If_mX̾2:5D=nJy^; dnvԞt"d-Rw9As6SIȄ{j0jfG H~"20Y2eάIl=ʡ}yoOḫ>Ȝbۣ(yGpݷ>>V'v߄. B+9wFJЮ+3qh>`̀}}kU$JAWK7lU7M93V*<"y81Ԣcv(>fzbT G+ 1h \l܉zL<@nڭ=Li{_lyӚ"12קm>mX]O%d)d$QT2e5|*ݝg#lHUݕDȺ+{6מݏ!s>Ǘ/Sc>d>?1}~uf.ն஥+' w>Lin/[fjL YB'Ȏ&=f'lNz;E“'Nz&Csb\oETEo uiS@u'4O~n\>"'&+MfDIvW tݜ;HaQޮUAE32>]2' Jfh>8j۷/ovqJʇ{K^h;xҫώ/NVv;+)TƆߥ2ͩ<^Y_WoO.׏9~tA,·+˹mCg?xx };G:6ǁ{ۇ]_ٜ< 7߿DItKO}N/> VS([IË"Ǔ30.{$nm"s=?;xnt? KC~Vq+q 3]mS+%|zsx0?}D9`և|`qk36McәݺKh1gtСj{r>vwO}+ZyWm]5n:rvO)6]\c֞ 7)vt`y>߳n`nxv7Wa與n)/.w'}ob#Y(]Cח'+?-~]sgm/!vuH+ãw٠ei>c ޻u|NLs,,A>8 #c~N{af8Ǿa.];=;[׽31_@P]{}c}yb՗ ;[QG?Hk :UAʾ)()G %SӮkt\tWA6C ]q.YPdE#Hw?yw鱁_WoyP}m7ܾe|KBCNP_V6A|iΎ}'k7KNw?Wߏqŭ ڷQ8wO Vn*j]s|`P}/gXIƿn..u۫USY'?^' F#.RBo~|w 1!Ƈ OaS"wU7D=u+g7\0~I :89?mxzwhO}+:4H:U{担kM>DͰxn7!zRs Or yN:gۯkWѰc9<0:qv4 Mv44WSJ:Bnn~a܅&BMy]Kl9g1wɒ~˕}ni@<_u\UOw=Kسꝱf|3Kn|~dn"d~tvx `1@flf 2Ҷ5z08*TYFÓƬ_tEAOmmKy>,y{5^0'vx^zPC'm|qQEl"Ĩ`]7Flu';k wS\qP=0ks7ș0qպ&|0y E0 23Mz|,iOl@=>M|I% `PF//]g~hvu R0738.Nw\=Gʯ}Wfo~I{m34UF thpg q; XЬ.ֵ <=8 ]fA %ýPaLOT. ?̻C wŻhvA9 = wFg,/uDps(:˿|fu *s\WZ|~vǓ>yc{U?QܺЮ}Oo;'nx-2w3xPUɖ[Ζz^C^Mhtx4W> GO}b4M4ukw]wd`=s殃W~s XS:t{"*~ns7;۾|A;}؅ttj*O]7M|`wE>i7=>n:̯n:}.z>ŭbB!n {b6V%B3nzi[U|&{=71wyF*k]{ܧU}ȫ=uyj Chg?"uO\ Q Jv-6ף?u* d;PQzQ\ q /1B|Z1o!6TY[,§#[f ή~8 _箮Q'*:vsԝszjwgax35owMU;\vMizRdn³W 6yJkd؃y^)g1wɒeZw8fZ)5wgGZ7Z(c>EI\]l>K}8f:N~]uYtgvnG= ~f Iۿ1fZt)=/xGM{}iV^"pP.viC6FMFF#&Xť=W8=*ڣubuyݧ^=%퇦4\~G0*̽iy+=jG<]0wsMA}o&9t&~G#[?!-<\fwrVBi,(:==?3:9@+׭ZA>|>fEh\]P*Ï"@.a'}hpc}vB.L7x׶wjԃn<Q-Vc)KKZO$[\z9Aj{?vwǾZy)^A񽑩uF72{S+Wئw5fxA C;%=s 3{YS b߁,.c n]|7 =AeFg y~G ?۾mv(0~=S]t:+\ӌ,>|U4@Q}N!rwU)_סU`1 LqrJShqs~ 0`|^:XM}98{}'#U[⺹(Y!:/Nozk|D.tܟ;? @[gk:)J,wu"otuw?N9(>lȦN\ݦk-|Iqs7)=@"G[9x݈~sU++m[\ks7⃕"s7J/#12gжGQ2gѶ|ه;l{b-Y k3]Hc}2jXB:P|خ&Ҙ\K5=J/c{Bkdv Gfm{4{%˶;x[yZ-[.Av=!hȃ̎'k{6O=@h="Vdڀ{ܣ %!}uM=ɏ/c>䘑{3qkjRyc|b,I,xpkBKH5怃5{h#":TMO[ x(Zߛ,qպ%ng.YAʐ97&ўsYܶȳM05^8NvY%5U٥$5%Mi4>&Hyō\o\$`xovFMzhn<ޛݺ2p1% n۠ɪrkY-* Q ƕmٰŗ3e\Kyv83HHyǮaw \ĘGz@*~sFN)nZ!PIh=G)Ӛj[@6 *E, 5ܨUA@yǮUY3a fW볪^gprgov_i}||9==Yeܗo^vCpߊZGO[R+O/ߊ˾p׷ւ@L4,EV{~ <˯= |-|53$GNqL癘#ź%PYnLOaa)˟s/oO̿f ]e?̑c`} mG8BPU%cCjͫ}9A5[T _$Izqg>oeBğB`Uudy&>ox޺(Q+=oQ=9+h14S#gy19S6nm1ioTz7Apk63ٝQ$Q 59rAKc^GBXwگKd9(S`| AwUg 3u7 $Ozp qZcjWShc}O{-Nd!+ @ċ[Fb2^S3LͰ̋fbfo*F\p bLKrɶsfǼQ}Oi} Dc^LdzstBt%,\JHa] Qu@ Rֆ=][ *G ,3! ɕvG ړN%F I`6b8Xgwpmjr΄:>>I-o!ύ.^"\AUQ|fF"Ȳ&=qHE EonRH, mUs&"5v,Pʱ[D+zdHnydSRUlFI݊ةew#f"|bg~޺l#JLscyZd9m6&q4j#z_:-G§95`r^Zv'XT %A,Pjn uAI of[zc1,*9&u9]'[> h< X'\e˪@gSMj5G\6,t3K)Uc bT!0bY{8}q [nl" s6i+l7w]Ö=w+B/V-Z6GX!tp|p]`.aIi/!_jd9hq\5ǽ9)e*}NV4P@: Jd%G3Flr̲m)*A#FLlh<wMN*iKǹQ5v<^:Zk>8aA>р#Y #$go| 7l3@9ހM0B[ %d`2ɜt5Q𽊻B4!;R,A3U="3K=I)4YS7aM!MZEH1){bvM%JtE㣤0#-Y0D a"" W`^ lF? 2u󈆍da :2vFm:B ˚+ G4ҒC! b?x[IPK 8Dkm)/] Wahrsager.inon@~9WT >ZѦQ*%ۣVm ]HZ"fo~;;f"Fl晐-+3*HvUO 9Ą\*pW 5KWwJhG$$ J1E]JFa ȾI:͛hgA<%)"ؼ ߆~8~9{i@Rk;Ϥ[XAf#jXM:2ϹMsR妹C )4]A1`48z k7@O/W`(j(ECt|3 ܳ_&Ѐi)iɾKOeZf&u]*. b %d^~ mF#ٔSJVH6SRљTdW(^^2uZ48z^\sɋԠ_l_:Sd/_];NQ'wZRg%_}&S귡8lJť|R~ hjǺHsI ʥIf̛_&PH'rY: MoWx)IT`Yeh9匬{O0*Mr =N$Q qڋfoΐ_\v V !E^>4|t/4M5 1!_v0'!o/`j1w۹$&s%Ț!!.ֶ 9m%ULi_0OCgRdTjFb JIg9# [_ ћdv8/ye*Ay/DH-63G:p:٦DKܱ&CPgbM%s(V;D0m &JWIIEFK2?6QMWdLENHU ꑱ:Va2mɢ:Uy+ɅS5{(E"}aA6v%T6TfR/s\ G\ƍ"]d̩&Mf>-bѳ)Ka( .z"NI߭EF~()CAd'_=M ꭧS\LrJ#39v3P\,o8Fs:wZn̻3S(Fd޶2͞&:Cqz 6=49tg?wZkan%e.O$HVHY{_"UW}1z';R6şPKǁxEug6=$part.arduino-shield_r3_two_layer.fzpTQk1 ~߯0~iCa0ǥS}}i_?پdIۅX'O7a[ɔ3kѮ}(ٹv0+ krɑK]K7ɫv^e& B2[|rVM'EvЅ5L|z̆jvTkTs\z?qWLrYpu%&3=u!Kݡq0-{ѥ]g@8S'GQB_uN06Nq269Ҙbi>e--1܋_b`(J!aԃcP]G'— 0;<')a6XO۝\ Q828fM焃XqidhM.$?e+q(ͨv*C'?!.whuڸmOuNLfZ4oCtmYc/a?\9ZYŒ&:@aٝnohɇY:MeS/Y4ZܙӈvEcDъO`Ix$hBc6Csb ڳ]t xX򶍮1:h<~&V&?r?F=.tRMJE_(3ߞA2,<3P%h" ")IoDl*j&#;]]DKFgLPȀ%h&IKTdO:@_8 Jd ^BL*%%E5Ey*tV"\pTfnZH*Xctqws љɊf,^wFbaٔ$GT"o t,p ddu fqd _@ g84I$CI~#&[}SL(tLfftM +K4Di][% Z usLamoeLC'B PGy,yƪ  ¡b[L8k!ل~&&h~)dF#na0< @˪YmX֟lEJxuT,oVYwLef{:o]ÌmE)ytg/oA7Tx|J*V­D2/rCO7\jGíY*lf3|uzjZ6:+az SUd]naîFFqحaW/z*ܛ.8K95rhš]C[/v=Mq+8ra({U8WC{S:5](-4ţSGG/N=M 5GiGl X/ H-c0ҹlIH{?xTkKRzylT:b[2U4>j{V'0N |;s"щRq }_^H'Lv0)IIcSq!e IH`D1A'}!:rH`@K}VֽWsp8)I󱁃 ‰W#Tb>͚{o&=057&vv־_sI`Nf\pJ/Bd:1j4[`FONqn<[`O`Ny66<#nJ#m\bʿőh HiJJq\ʿ1*Q#R2 +tusW:5㚗UlJmMtj]B\2!|k^'T7F(j[bkdvڵB ^,4f!&2{ցD',.I%.5G0ɥ¿1.f_G ܯɣ¿1?tDfV"Rs5T7F?TRs8jRoJ1OY@*}!5AOǺt# t2iVT͑R3ңoMkݖ3) t2iVTqR3ΣoM>H]bV\TqR3oM\w0 'frHX!5c9*ƨrR"4!7DsԌߨqH{4+zC*Eo Q7FeTT!b8Ds ԌDuhVTR3~oF bҿ7蚵q+m\{Fpḕv8[sobӻ+ZHɣY{ռqkmT7+\4n=yOӨ5k֮ƭq5jܚc<Ѭhnhhnh*xyumP&@MA͛h*x,n}.xHdumP>@}]u Sϒ`3n45Gx/~x5ƈlv*ѻ>lZfE z j+j 1- b^WT9JrzWȉŸ=B 9F 9V<BQԫ,WD:?^}z]/Ŵ:V,?PKǁxEmY(H"kpart.Half_breadboard.fzpn]G?'/_=|o?'>8y_7>}vhgpz{On>[zEz}rOZzOOE柳7wٝ_zyg/Ngwח.~ɣO]|rv_gw|y}ݛW ڧqv/|^x?λ;~yyˋ?_v积.uqzWogw~6g~5|Qo6ū//>~/xsxrAW׿r˗~ux#sy9>?nM'ynsl^?ݧ8*z1:Wyg2^=/*~y(޼W/L^1h%~n:ͥn^ٽ^\/ΟW^kyw Q/NOn~?7=8}S{~@_]=(x?7Wzrݛ?G!vgk/em?Ƿ39/SK_K*z_a_Rҿ^>mX׻ͼoO.;/SKK.zaRҿ^>oX~o^)av)y?D/0,=JǹatCCCYJER*}.r]GPhRs">""GJF9949T\0ȡȡ,"E}D Ef)>9.r#rh(r4KatCCCYJE)=E.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29T\RtK>" Ef)>]䒏%CYJE.E#rPhRsKE.\29Sz\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K"}D.R*}.r9eˆ"GJ\.rG䲡,"\l(r4K2]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhRs+E\19T\Jt+>"W Ef)>]䊏CYJED#rPhNu.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,"W\j(r4K"W}DR*}.r5U"GJ\.rG䪡,\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CYJEE#rPhRskE\39T\Ztk>" Ef)>]䚏5CY:"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GJ\.rG亡,"ף\n(r4K"}DR*}.r=u"GtJsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef)>]䆏 CYJEnD#rPhRsEn09T\Ft>"7 Ef9Ů =z' j~rkћ9w8wy#{7 a.tkћ1:w(Lwy#{7 anukћ:wx`wy#{7 avkћq;wtwy#{7 awkћ3^v2lix4LyrxN- i5^ox0 /;^4<Grn~KEoHbx07ݻ} y?FG4l]J?/~~߰`^?)av)KSCQz}g g:R*}>s}fz64KLgzp,3=GggÙNtJ/^3ӋLYJgz>ӋL/3f)>%L/>3tT|3b8iNu>k^}fz54KLgzp,3FgWÙNJ}W^ g:)gz>ӛLo3f)>-Lo>3tT|3f8iRޢ3ӛLY:Lgp,3}DgÙNJ}> g:R*}>G>|f04Kt8̇ZڏУwo0x-z>.~yOrn~?^޼ϔ'{7ϜfC9Ns,< j~΃Ӝ9Oôg<4r09<8y4Lyd<4r09<:y4Lyfc9Ns-< j~ΣӜG9OÔg'րӑ5`yf&Lyf·?έ˃k6aZ3s>5tx X^ jlMR̆wMV̜MV̜6MV̜vMR&x >MV̜ƒfMV̜6ăMV̜)vŃMRx3ƃMV̜=ǃMV̜G6Ƀ.MV̜QvʃVMR=Ns[y<3{9ߝ|4Lyfs;n9iV̜|wr0Cq 8t,X jqpMV̜>8'l´g|Cr<&g<21yt?7aZ;l9iV̜|vr094圧aJ3`sr<'g<3ßN9909t:'-لi599hyN&Lyf99霜dyN&Lyf·?''9,ل?on<{뫋wtUq{͟zrݼ޷͋/h_JnJrגywzu-xz'{$7ps|+py5d5d5d5d5j@j@j@j@j@jՐWC$Y Id5Ր$!IVC$Y Ij˫!KVC,Y YjȒՐ%!KVC,Y yy5P$HVC"Y ee5j(P$HVCjP%JVCd5TjP%JVC]^ my54jh$IVC[Y Md54jh$-d5tj%.Y ]d5tjЗWX^ Cd5 j0VVÐ!Y Cd5 j˫tAP@DuX2": r+C2Ѥ&e82 (eB)#uuR)A JXJQ%DX "WºW:XH,AD 2K%,%Dn "\.a.a/A K%D "bH1AĘ LQ&,D K "g4A$ "MX7MXGM&XD "؄%m6A M&&'D "r’r9A NI'ֱ֭D "wA O'D "uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@Q{_ԔW5e)e͵֔5e)Mٯl(( E"E⒁@Qd(2P(   E"E@q@Qd(2P( E"E@Qdd(2P( E"uuE@Qd(2P\2P( E"E⺁⺁@Qd(2P(.( E"E@q@q@Qd(2P(  E"E@Qdnn(2P( EK"E@Qd(2P\7дnIdIdIdIdi@@@@@@Ӻ~μ|gY.y+_caVXoMow}wֿo!qm\_M{o~ӯIdV)t)9{In޾ 8|F@߅Տ؞>DwovbN}?xQ *9>Dwgj5FPfen-#o$>$wPSvO=>6oq7woo||z؞=>Dw/>Di`jMtm_&BQ#(yȻɳ7zao?rt/|{oWoSr7ͷov]X~I>=lOeoQtwzo4Q }@&?|df˒/ZTG}I_G:y~gw?PKǁxE;=R part.resistor.fzpWk0޿Bl!]3l˺A,Y;Gv}z{{O&1VL&=tDR/&= FGv"\`uZC;pG\)n518:OON4j8D'*dp)WBP\4V[o0<Zù,!\I Փn\IE1i !n5>L%gtG gnzJ}M0 IdJnO=hdy %Q*56%>7 y"La[ dŋg-p/ YIJŴO^aQ 5l8C/gI5LRh7]ɝEj[^j ,rVq#l\OĮRG,^^c"['36Ά*ˁ(%-b{m.mt*V3(Fv9tkSg}OO#5׵&_Q~oq-NC_Y?j%?k@gHpPKǁxEzZ part.Tilt switch.fzpVM0ﯰ|'v%zRWtq |(1l|Pz!似?ϼ`cv2/T1uF2i\||Əh%Z7G>q<:CכM)Fq{IS@W$WObU[Sw>o&̓HH Bi^ޕZȾ<:xa]X0F!OZU>.ji&sda6G xw9J"A\/\~~x_KۢŭdS-_4y:HŐu$(6ˢkٓ" V9M%.%f?2yMla:VEPȧL%iC5I#׌:=T40#X%\w_qgPqnƕ 'CG&J߱nyނmA_,ւ=HoAzzZPKǁxE>6Kz.svg.breadboard.arduino_Uno_Rev3_breadboard.svgkݶ(}{>djox8-;$sKiw,U:-$v~ZII-56 7/^~R8\^ᗷ8???_o|zxg=~<<߯_-t (x>oC?6,C_d,\o>x?~ѯZʊemqqOo|A Í$/n/}|QF痯O«ڻ׿~^>Jh=A z_>?}7]]_^ŏ Kg>?~[C|?_z}?ߟ~~supxׯ-?{qs}?zJzOiV#:a|g_ώ?'}\Q0¾Xzyv}~Nj˄(?S̐]{A/K;Y -豼Gтߨ B+󄖢  l3yUI!8Y(r:_NvYʫnǗ\7?|^U)('ӢQLEYp'JaYt3*<`K|z[rq#\^:a_:aoO8:ş2w"n"r Z$@Ky' +;p$Qnuox^Z<`$!$yIꞶHIi$ 1b.k!F67Is h7V,@!\߈yӝ46-KQcjr5?/u3c[´$,T " A/ oiy៖ - OA쪭=x_D:5okc >:64.:{; ~*?? Mp)=~j2:5ֽ m [KUCkݛgftBtNdg,ITAYޛq2}S!5ֽ +G 8 q"22 =;H7!eķ8WWoܐxmp0~<. o5z! p^Sh(%5J5zc صnMxN):'[g#矨H=ۆ܋Gy7("x'jyZ MS .1QlWMc Xÿ!^aYT,cz&dXܢ  0.) b2 nXJ20I1LDCOD6!cB;Cʬ1d 5Z_`7 C n= 0bm1 Lx`xx! n0< Q a%aa_2@Qt_5t}J׻aFc5(4.G2 z\,҂7p١^YB=Np t`BU#}Z+S\ig'U ~j?׏1>xejb`S}\#eԸWih]KzmyVP;\;!~t ~U ~^)jNw_)r?G<t_ 5ܩ3jO>1^ 4`ܨq#V4붘L 4ܨp a&]p nTs~V)wƷkV 7Aau='k2P& :ڝ-$[de5 VT.{г-Qe(l&ǯ(X<Lp=_.D UP`E¯(cm85ioj `|5_&_Vc]u5$vk0 g*P>j/ZjNw.L`?O<ـ9IcV]faklpgZXqyOAvnMsH9)7@'}<ȱ(rAz5̞{; !h2yvqT\Q?6Xb oe<š)aR 0Cfa!#[H |ݺ7  V {2Dj=~{_|mBo'#tJ/\Wy _o~@ɟ>^/ۗ>CB۹| Bd8y??]8kx[Ѫɧ:oE _'~{?~/&G?'" xX =z4<^Co_BD"!z_/^Gf#kx>/o£"!xz/Gݯ|'ݾ_޾~{A k/xFC(V⨎"YCaB[Qz?O"@g/-zѿH"G{ń?fkCƌxNWpVhq':_z!qRF ƽ$]GtNuuT.)}qK]t=a&(5M]( EדҒ1&GwϒD(W>1RgE+WiWQW>ٽrM&+7QfzDxfT^0fc^yqξpNW>4^=ŎYoontvĔSףڍϜϜrkGb{Jy ?U!$ha8L}j>$ϋ^>>F>F#%$cN,)ؽɸ.'}t2e.Gi@ |(@Geۢ>D.݁^NzL0K"꥛IߴIߨINzЙ V:_k7I6qUN;tZI*f8Q&8nCuȥm. D]KkSuSߟmBwjiԟVOqSlJEݟ*fSQ&8?ˇwt's]҉<-<7t7`g7:۾ _=b]|\>:|ѣJ>z Ξ`>D.ݯ5 \r_zjd:Mbu-79l6`f7:?;$E_`iyfO""Sg`8h?~9AX{wy޿q.F(m;}:}ٍ¢}ڽP֍]\/h"thS8pWϮ?^d_q4; 54i xċ>KU h"$x-1Yx[Ȧ'ƀ6m'9KF2@ȧ9?b!YK,x{ɬ%?!LbY?q&X<`28ȟ}/>EfM #3v,KfMc &3~E,Qf-G/ JC28ft"A1J_O m3]C&HO?7tܨpv{L-}N+w_ZƂy' v͉̀t,596'"ILDɋ?}enA jH;ѸuImD)u!#%6=.6ڭin1ޞ3GںxIc٧Vh /n5{3h58{N8{8{vm-=Vueɳ1K ãkx<{PtL՗K"t4h\_,h^E9#nDBʑ52^/Joaq^8}%ZDmӷ&Ƶ8?](Βksv 9zFV<\"+H\ 9FQ~KDɎ d$!<ۗKX$I/ɀƍP|ŲHrZeYf7wXL,2EL͢}^0&Yd8@cXAPŜm=Bk Ns<īDm<׫/f~~ y;ٗrh, &_ XcQ >s'K1SRNVV 4Ɋ0,&S(\䇓(c *ew𥾽3=\ubj$OǕ-A?=XF-Ie 7;ZIOB'ZD}C\{#/ mv~g,dlIMn4wzBR"tz94>-WɘY'(dA,OkH嘨7Z&^)Möu=&ENu8 TeG{.:EϊvQYK~$9ُI|4]LQ7;D1AtM7)n> M#Ҝ#X? Ys~c 'Nჷ4%ߘkQ>HXWfIƷ5KrM4Kƈ?>|-[iR*wQ9MB}z;SV5xdU |ݣ'}c F,:6 G`>;&#ؐCD,EM_|i*mhr W66w{tfl~..S6t/*fAbB4P&׷~s}/0ZU9}5 $Ml/I!8W'+%8PBKa?cib)%U m(TR wmz:\+K}im!Ƞ c^s5J4,mE?J%-âɑpbu\wZd-#-=%qoO_މ%i|mtDoMMЦk;hvю0FN'bdwIN' TµR]'yz {8//Ǽ'Ivbv"X}|ԣ֣ @uڣPZN-(Ԕ"UGU6( LG1$0ƣD ʣ( c8.K8 wIgy`aKk2`/ ū7!o­IBIP\](gp]oBƛ 2:/ ;Hݏݏ8VIs?iSsR(BAB'y[}YCv$kp\1Lg)Nzt (@lX'A[~~Ҙ~~3,01$1U]eIܵ$v6~_mwVs_Y $#sO̸n2z}t!5hɍ0FkLsoH@~EU_;,&lpQygz3`?8l\\31:#ѣUw$ L#1$0őF %~D@ONX+9Fp?6)4S']N0Εxew+~iw+BLÒwai4O/ݬ^ر{JkB=9okflk5<GV 4`# k-}dVhh|iJ*\Ͷؙ@ls]<-6맟~ivZgt-Ƕd[KƢ|V>~4'ۏGǚ辽|ئ5lpӚ8Ao#Z̫gW|_K9˂9g9G1RrTqq7Gj,5ċ$jo̸q⍟6ى`9UL7y Ww8+8;bi,P#fZě<ר;~x qnsexgtǏċ>34io´Ǐ/''^lƀ-L8h>syYx:x,NیrW<Ό+x|D闩D<! wAXP!r Ax"UBл_,9qhI0I[NloFXH!rB )LŃ T?N⁙g2>c)| }b$AK2F3zJMq&8GFSY( MabN" azm3F3gJ^[=iNl:سf7ںTIqQ4b7q_k9.qb+"g_1N|_/ 8b<cQL8bqJ1N 6qQYq7C-bP" YXC m&Cmllnyl#ⱍȏmcX^~Iٔp#Z9_|y&9"?9Gts_IO0qb9"'5G6ᆋ.?7G.ʉtd刜or$9"?1Gn#cnˑ̹&Y92?+Gv?ӓOqò-.>E2'Gӧ.c<$!eX@,sb ~ef}r"bU閩T<&V1ě- tN0Hn* hXu7[̙W9ᰢmenSʏU}S HV d*?PJ3a*H@B2*?P8ۯ;>kXrB !˝uB[~Y!TN Z1.CBuܿn8b T+|>%*?vPO7vz.@BsxCխt,RAD=|*?xPofϐ:=Afj8t~;7_O~7<ۏ/O|9~"FQ %t+kHBGI|$Nl pXBK-wC JN(|bqΉ#t+[d:?Н8~գ~bA "t+"t~e}W,bqΉ#t+Y#t~q椋:'Э \0(BGE< է5(ï/~z VT/d . m:iAvl\Bq`#u80\qy8auoث,]/o/WvX"Ia`-w@vGK7[׶-'%<4??HKsIۖFwDÝ%6N=gOO:\f_>,?| w+X=̟ ғbwH&+dG}-qs+:L^A,^d+Gi.z!DO7 QTpuaN/W'N.7CB`bw)m`֦CW&JZYEeip洡9B Mm$M#{p(/"ȢZ:QpduԁGeQǬI8::bU =.w#9db5r4B#jjxW0# ; ;~Hύ n^{`"PǪ@Ei rC4pkӈ Q^(JҊnȊ5 Vz",ǫipFTD2D  H ̚nDϊztNu*[xi}Y|a-ʅg XƔ(!XGh`3r̷܀ظ76 F FO*B5 ,x6`hlAlso IھY [߿';6Z3ϱz 3Z9Nvlyc`NO}l;(d?A3r:t#8a_#g6 oI2ǾYQUb?B3eyat$#򓁴oi WW^Q3OX""-ȩ>BaMƁjhXWdjE 9Finv#hDc[@VF atd:Yr#&ՔkH"ZdH xF_@$r tdG7! .+eXAVh 5XGh`F,.|oHG6Sg͋6{ZwfD/sB>uO`B?OF[2wZc~V84\9 ..<|n>sB8{-VwM&Taeel;]SS m h%K6hyhsX_yH 2v>g6^ϸa˜)E 5㺈'"T|о.B,tQ ũZl\]:@0j/,WFbLL2xW;tZp,0]&ʤG_P1[\b9&U;0"!Z:΢͉(1 'Ng;[Nz燮w,MMG﵈2æ'˅"˕#Ց~^,RiF^zSx;$^wr'X8^.ٽݨ˝N~8{-^.jLn䡺Hjd77:?BAGy-T0 1*3d 0 `/ ktb}tSL#i/D2rZr%*npH ܨ ,8!;khNVoƿTPӘ%,JFpGN 0;F=;"q$ QsC0rҒ pʺL5k)%;_`B@RH~d€ \aO^cv5HQmP̸$ap$F` S$Z!Z2r wz]%8PB"̥z&O6o;Zr,>>=O(rJUjLCQ 'M>B,9Yf7IJbpE F%X`7%_j )r%Gā7+'E&3htfUS"d氵2 +̂1iPA ځʼnf`0T9iƎFDcYcuGYT'#kF"Xr5P+UjA2,܂ j(\Kc)$E66@J㾬0gH,-hB{+ͱ͸l+g+&6p܁Pf=pcNDנf[i4Bqy۝FW@5a/-v$ d.0WiUȄjybZXe UhSՃZ3MRNJFe\#h>#Ac ݴ%((QF&)a[}!y:٬씷ú襭dq֜Xre'wM 1PLnD[((V =dSKvE Xӕdؕ]S i68;+A_~-;NXZwnT@G&+~!Up.ZlTh޳ {ԼPڰ@ < M! K,z ;x |.YXYRFA) @gDAP) @ #<:alD-  @I QB1M? LL! *RXv$H$qg6)Bؔ1~ǣتc4-U TS _% 0Ӕ 'z5[ x_s^ؿ; Xi T֘[U;GA+PAR%ƜQ XFBPckmCDP@Ƙ7_@P!j yc5,Z `֙ 2cȆTQҡ1vrH95U:.14'C)0cckS-"i` T hQj+D-jVkvVgͶp7Ϣtk,&cqq%(X,4~D蟉r3nm׈?S2n%2{D/UY*LѸQkp8!?V@Z[F!3QVX,P@e24"BbIGQDrdm _-9|A51obOy/a ےrl_Lp-K:U,Rq7q\@:K\9B 2=!LUdHnXbB~d%M݂}KlxhJZOFbpY[؆kjLl-Q#*GVWjWTê  ⊢8bAv2kx>Ǿ: úRF*VqXE*wX#fIy{(⡉ a'u -aaï{W l1'u -aa˯{vXGA랬GAlUӖ_d㟍=g@luO68sYh P^J2z$ XG1Eʆ @APW[ q%lb N5 /gdbuyeË 3$fõw' ϡc^CK]0I Kv%2ٿ.‡Kb x4 W(X 0u32]PAa0 p I.:1yoP~ " *ƤV4?Y0Ĥa;3ρg@65,k.#g;H9Ip8lX?`_ C:W!0XLUg*Lۂq9Q\s0.cEzj'Fd85sR |Uh\2F!X)L׫XHC:\Bץ21Crq2%>r_bC1Ƶȵg 5wk!U n K.IX8\vҘ)5`)pm S}U-XwPuGA3A( ~e0+js7J+e$qeW (5tNVu0`Qq`8c8 P)ٍeAX/gSg; d YvD}ZD ?]0KXQ)S!"D:Xgϳ87Ye0UaIq!θxD\`9[CV ᮬim%J yNp̵ $#";YbT1ef$ Jna~B#K=7یe Fs4ݭ㏖Ϸ^__=l2X Ta=+8|ր"62ؤq{Q+V2\$^e^ _zu b:+B_P)&it-4_Z8lampZ?ol(C&㎜dlRm*9Z`#H>8)+b >lǣ«Ip6 |W LDvǭqFbL34q7?Cq4?{ܳAө琺ko&o'ᘇm8ed4#YpQ Aw׉V|g`J!1YN0e8dwmބT q6Eض/{H~s̩F(Ċ/;ǰPuie !hhv!pf8UUu#llR8vZ F1߸.j]8T) lýc8YYj9jln囹kv6nH'8`]#Ă'LU[{r{8lݾ@w '-% sC}+wv׮Wj|SL1,x1HvlXdɘc8Hy_EĂ'sI/=+kX4ЫCޮWÂ[n J{r+duuUtu*Igܤ>?`\\-8oca˭@S79I|[]sdr]TWJ8 tpqx:e–+^Ű.n/W₼ŦѥNN # CeخoXqhan} ~gX,L|Xh3Amfȹ98M>Tl@lnJNx!$GJyov7.Ѷ"U +*gV V+xٔȰ5.9#boR`/AʰA_$`p{Β9̞)4(/4TA$^& m$  ֙Ȱ.#b$^& m$C_{Al3,|̷ӜNkI帱Ѧ&Q2f{ 7#PNs 7>T]zq9n)k(TiAw ƫt@~<%MBH*:CfRfۂ̶!e6(&,G_V2UA'4ScRĉ :| 4<7qL1aHu3!QX5zT)|N}x k`PBt^c>2!koF'}ǀ"dBYwk6Q _AF\7D힙; )05.LMT=FI}mAle߸)Mn7Q>8Ȏ) n7QT0lr&03tFNVR,i)ĖͲ́ԝ\)Sܝ&Vӳ\PtMB S)SvJ/o/R!&{;TaZ OBA6OVعQ}J:A}8A^vJQ:ۤCi*(urwW6qkb'jp֖Q>cG,B{ZҭX7pճ됉~"Kg;S]x;6OXA*_%DC)DZ+Ո#|p9Ak͈~+Tt=D٪qfW*lJDn38`*N !W㖥2RI .~"Q$R@#kM:6;(:EJl&`f;(dM' 艘ڌ鉍\>yt)~_g+\Jl .#Wxb¤ֽ7}t+9mB,:OW0ؑyePeQ\bN( 1)!b"hhRB 4,Zy4*- k /e=~R%3@Jh( D `RRvUul|ീwW Gwrs)dH/&jU8)A+ ݖZa0f,6os&f<+ IA)LلtF),XÈtMNx\308 g$AOVMh]_k-\NNP%s((QDzHw-;| 6 S;AC%j+Pڭx\Yޙ6Y8ͅQ*X\'FilOGa]2.ü!OWDO^ǮeG"܌sƢt!Pl' lv(e!/s(ݳDtVs[#j爡92Uhb Jו5w&VM@X>s Y{C2e;Mx6j?F{Ama2LnR>mAZAV%h]@kиlA׵L< {kkiVO'AfۈZЬ=AU[(9ij5ܿhzFh4\*s oly,=lI{q0s7dɰꗉc@QAYHK_E$Œ`Q|oOvϟ_ֶP?;߃P-'܃1PAts!ߔplQ7À^tɟy{eeRiP1R> /.Zu;CT ŷ"^v}`M'K[VWps"B[ᖷr\U}VZw6Fx2b&B* Ҋ_U zHHHɳYcI+!{)Gz 86(,/xw-*V Y $Ryx݀*(J` '*89NTё|r6ld\%IƾÖh 9*@KЀ\:$51I`AMwn+;וd X1'tZS3iJkJQ:)K)c34%5FNu0Y/;6PKC2kej9 Wp&=(]Mޥ 7@|pQv =s B;m`c(y]P/$p⦠3?eszBpk[p9T=l;zq'v*J0vt ePO( uPOH[{6&oֳP&KCPO*ۈUU}-l zB0ʫqI"S儲^&tLv6WOPJj"];&x5A-W))'LDrPv_۔Ϡnl=;g)Hi' Ofx_ h^hYO~9<ݪh gDyzւI`'Z I&4zYh=$A= ePMȂrB1PN8TO(OzB;`•'k_OH X<,-DZ^>,MH7yiIu-'w%c 'XStdiPҦ"@ҁzZV=!H=!'1zAfZ6Y<MQ'Z Jw WOʯ[P8gA݂B}Z܀i@hPB>TP4P۝kw,"T7\vV ng\SSPŅP6V"-.q > _6mbi̧n>mt_]5ҤP?i|-OkAna?P,((⽂B{\ M8jϒ$ ޟ>Bؑ]ٟ9$Gˇ>>;g+ n=#lODuWB~eOܤ)  cN_J!Qw;(a `̟J)&"Hqgv٠ YP6h|٠US5(|ՠW sE4D$LYxgts sh̏L$ReIbE _4kH͠ jTA͠ijdWEݹlj>棚v fٙjP0?,Q#[yigTޑE#e5.1LV ;+IgcAH[GE7%D&OM`]48$(s5.VQreW$^4ؚ,g+ldB@\5X2(;jr_K{7\bH0v+ U%*^g޼}qOs+S@}TeRA]o J{ć[*8:P3L0B Du­(:\l0J)nh%,\^vh.܋!:|H_'R~%ثHU<iVzTYE+qX;wJ*kX{EByvSHBKV*UpR-"ŴHQ;u`{T&TUhF؄D= pp8k$&0/ È) W# ¶VuXO\$)8kOj%֩drdyt[w[řNB怬a8<æZY%iF䏪B𱌩QtM2 #7͸n 'j1%/&X!o d___jLRc LvTux)k;VH)= n=S .W` L R8'lhrF?аlPCf24pl8>ٚVT@Kq6"XS *i2϶C "Ïs "-JJ_5KB$f!aڱLRd֣J7ppŚ8 d@ 'G; QSn܅hIu~TR& |`Pv Z-&+j2F#/dZAm%,- )ƹoҜ P~$ ع85+HdxE1XkTcSATcBLߪ c !F:œœpVZJ\e; ʮB![, ߭/WG쳸0H#f+'^Ku{RIy"ݤ')CMs 5C:[3t.S3^\1a& 'dٚOT1d8eC:|Re C7sH$ s& ʍ,(& 4ihbݤ!zT' 1i9C:+ ж3[ Vΐ9C(!g)C KL7eH}x)Cd ֣! 9Cy9CGAr( jimRsL5ȏ:!a( ۅ00n_'#Qح\tİncW0'I0<Ó;grGi݀(P`F9Q(3k ➽&i׹0[&h\p9Ah3Z-4a_4p<4v 3[&dL6?xڌk 6odOu9=ҩn -ң @5ZNw7t-qDu㧕k66k[8qj#Dդt7cu pðZqcw_I%嗫yYf[j8I'QLדʾ5rnu&X/3Dž#W a濣qt/r#2F9dӞN' e6& Sr+I3d h$/$ FFjǑ8Bd!I`5uic4XqS@VbpTaDτq)Q)h$7|IFc.6 $GM< _ŦÛ=| LL7NtBK,]l ^ @8t3t!h܀$` ȕ yR5Ә\^o7/Y6 p #.`jԫk3 `m@k$᯽XE[ CZ D/fb #W@ZLF,fL XJDL[l "bYAD,dDb6-Du&o6:bV-8r $DĞΣŬ[lpJxt2"1+G]abRѫXUg4̢]\=tsSK#]\uR7C43?z10ܢyGx ķQczv͢D5ҘI:AUj"t*e~dS2 ͋*E2H#q'p $[@opu9kɦGM&ڟ(ȳQnT^Zfx*8וRfJ5t44ZpsO%w#AGa^*2.{ ip%w3w$U(NRp22 + TCvmFv띉!%gknijnբz6Q֦Xsڞ3Qp5XZpaKl|5yf8N#Ɉh[[htD{mIT`guT9uE%|l3ܨZW,]fQ]ht!*~":(x v{p;OaPpou j+Y(%gxe9㏊!9Nú0DXWY27+AB !4!FCBgk[<()[(U,˒3P%ֿBS*dXSC$OU5WU66Q pk E*$a4*(d;D qK6Vp dΰ Ӣ ʸ'6<1m^}~ &w&Q*_5NEJ9rVK91_x=.$s 2+w YDPMθ*=g50~1!ݵEU,s>ڐlRwK1&y[T6*Di r`gJp#Bt.<瀔@RեgCʘi\y*#Vw.M; #U<@#܄st"P4o؝byn;ZF,Zuw"!ǕC@@LZZ$ ueԝhi P0s YɉC2m"oh u[ҔfM!hRusFHqc *b8rU(Ap, |`8׀5DpՓvJܺ*7YG(?l5ܿ0Gu+єR9ի7B`8O~`&ԟJGVnRc[^Zk*ir#ٵwèɂ @*ƾNaa%üF6OMHu!' @r0}0rSHO-S=bm%3AQ/KPCL MxiE~]hUX^Aŷ"^v}`M¸A}d|.w.Bh+"QRh^ЂQpKBh;9_RFK$J`oX6_U {HJH3c+!v!- "Cz'Q>KEDG.6T ?7^M'aR ȁ(ڃ *ރ#Z?g-4ܝqX .})ޭ6,*4rF+YH1!\"={Qg$e+N4Tr!ۿ$v{QUTJVr*Jfm1*ќ@$ݷg*% D !JQd1 D}kOWlT8>ze@x\-lZ8YS#<>om$S+3+^Z`159{Τ|4J=$SRW ~S`UhW Hzt7b DѪ@wQ8E^:>++elc{ͩ^8>n̏ުQEez)ܤ)@$ ׫X~"=o 7?P;?4IN;{&QuoNW cJ@> iLATQjչk ?.Aco\ⱼGu!jcV݌sJ[YmQ)b5imJ'R|1+F<[9W_H|t(GlM{V(rDyiWt#n^ػ, o( j5혊vPw[X[\_Z2iOs!Ү\Rb`eC 6;S} ܴ&,Z_q('YZ@g)^!R(^rL.1Twq/QZ'!^|qXYq%.!uٷGk%)1K*^4E7qob5řbybQdQ#WCc`[" 1W<_b!8ȕ+լgzp=W@2;E_;bPYPxMVFp \Y6Qpa)mH *$Z`iy5 #%pI |oW |K -ķSl8 D A/\qW & fNj"$Pn!ZT&U BɈ(;DWvL3 "XjJF ̖.Az@h(B9bv۶3XƥnR*0DPOBMay! FK/AtLlrPnfة%ʝ9?ZY2r wּ!0M{4x4Hڠ8TdII|5mK \NXil%^Eg* 25R4Bgsz;`=)N#WV'jhk=t{NBseoJ[P6dHW/~Wn~%$6W,`Ŕ>]lUi'E'(N#@`Ob ?@胯KeYiެ<I7y o:ւ,r0uf92w1VManMv&) S1`A-qUHޤ>qf rB2ZȐY)ll%EWݸ1: 6?SH8c&=X{jn@*0#[y6: ͦo Menɳ֭ڟG?2̢<Ã6sTժZN.ϰXze`pf^) x7@x}Ҁ;@]-XqT^apJDee#o 8:rDF"m!;ۓ˿:iU_`GY:ث,q]{q)yl;FL&uBT u-X,Ą>:7 .>ۉ*_Ta% {Ra5tXj9<mhi+tDydyOtnf{>?ąm=4v[}ѷxsۣ2#H' ܟ8>U:4}?;G`_ܓBXfhlO!OIE8Ǟ UT&Sfe=AejU*ɊO"l)f\2[}ƣb h1$1 6=l{k Z41eI `Iܙ7,8 a]WD92+: )R!|Al&09!pA@s5re޵=~UH$?C!8@pQ+C`ΐc|C\S1Gѡ+tnBnR2JK⃈Z t!_2l˄TDlF+n"nr 9GBƙ?1M6dt`N&Vr Ɔ; h$v7p#S8R?SƦ x3á ,1ߙMlr3OyؽV)>1oSrnG[1=U{SV637WW54uЊE K|ZrܬLҭإ[@ۂ$jbvg&־57>M-.,H`FZ *=R 9$W17z2x=*6*ٷ񐜠j||X Ʌ'W,o!drsūJ7[Ϟ5NsiJ,!|5Q•sWƯoi||ҜS#'͵ W`wC4[ ^ErX{Va< r~䶼ײ^jE|o hvP;27yO.tdX;JIt0ծC*eZd- wNlSJ~|{z ~XC|+4I<.jmæ"69MAS@GϦ{n3nbp밙c;.W.)ETj 5 yLs7}Goon vkH7Edl` 76BGK+uמ3z!IhӢЎ$%[_s= ZybJ,&LkZ0pF|:_t+3_m$//䍯WpjPA|{^4_2_n&7yR6suFteO|S`+-#xB||$xZ2$Ukۙ kl4"C 4sv#W%QX}N(nc.KԗM{ju]ԗ/b@$lR0fã(tvײމ6ģ4 S!0F#nl#ҊBnUwD,"-(/4P> .VMVW/ d yR-ٴuZnR@lTjR+6⳹^9/N ?L|o߯7V^97Ӥ?脅6 tAJވ|oegaa?B|FDt?=?|*KNJ4;pAB?_.  |B} o@+/VהӛLTympT|^eU6%fAOƧG[ך .d:2u? 2AKrXG(-sـ:!Ɔ()6*` +!~o8]|,:|Rp!ii[Fsت*OܩU< (v)!KH8x0qj^jUkXLyI/N%~õ1ռ`_ -[8dQdi["ѭR݉!]mCU:)v:w[mLZ%a-}ӄ?3GƤL l+tr5&^P 7ba?M~rP9 K2Ĭ@R ;@tz8f+ rx8@-u _=KKDe:eeB0IºT܅[<زRunjV,I݅%vbV4n`!@t-UVwD ~-wF5OrqUD:nսBsX;\`Y僩OgGReY僩x ]r?gX>x~J-w"@6U[:U/NԖ*lݲ!` [Q/+j`yuu+S` b`)$ CU. -RtKJ4/ưJ|+T -ªL±)U^//uzV沣.C̊-L^= P%=N5-Y@z -"t 1J5fqc# [(Du/4; |3v bV,n$unBd ݒUUTiV6Н)X@[d L(ԋPN4 -T܅kXy>+hx0uqnP{* -UT^.,?p-@[i@WbDQ[go_ϯ߾o={v_||7勿O?믾_o޴x_ճg~7WWWu~z?_~lw}?!|yױƛi9}?~OTMu>7û??G}߼z~廷Qg߿xwy~o^nӘ7Ô< W߿{-޿~o!g<Y~)!Oϯ 7 )Я{?~^"?_ۿVAI7urvMw/0O)Ƴ+W~~~xr1&p02Mir]!z~]˗/E|6:c;1m};xO_p3eWa/?ϫ,\-E~F_V^b(/k"_k|ϯo:yym{ߞS?q0ۿ:~6kd?^\2{\_ 5q6sģb..q$ vpqOkCsOE)6L+7'k܇bēb..q&B?Opŷ^^,vqgP/aVh+\ŷY|+]<% zZ\^7Yl/)1@WozC{5| G*tqЫO`⛆ bCPc1BE Q!bE tԎ8j>@-ax*KR+vDS# mO wAEbQ jGOL5DojOQjŮ(jP'AmA5>赮Va_ ළ5Ӈ6J5LPYwAUUxpZ ZkN5p֠ O[+֐6D\<ms յRu 3tƝkP'-AU.Gi`{ 3qڡ\%?A/'vH_UIU՟>襾vD_ ׎*A z׎k#}UU(AoWU}P_;z%56׆k6D_jCm} .q˔܉FUU]k5;רzP}[_*A}K}m܁Ff;רJP}[_j@}K}m܁kN5 pר P1׃G5tt‘(lT=~?P8AFՅRU_jl46&?ب*Q[ejE}KU'*U3JolT~?8CFՏRU ~ ?Q GJRߠ"a/'Z[6HkUE Vu>6Dm TH!{ުԇƞmz#U%)doP\Ւ=QQդ措(uaN3v3w| VU__jnO> vð3 U~?1B措:5wT~?1B措A5wT%~?qUJokTRs~5';Qը>6@s5'; Qu>6@s5';Q>6@sjncO4wT)djO}Kmf=\UBU YjnJ\s39\UOe܎hn#U)o\UͭPs;~B ZTRs;~jnO4wR-*ſ*R}KN܎hzT[s'դ͝FhZT[s'U͝h*T~[s'ա͝"ƞhS~[s'U͝F=IN=a/5;MPs{N!m͝Ty>K͝'57\s'Uҟ͝T_jn4wJPs;~O)m͝T_jnO3܎UJ[3OYjnO;3܎ijQ)~"܎hn#U=*o\դ-Ps}E jTRs{jnc4WUAsU^jncO4Bmܤ TRs{iM<57ԇhn Ps{IU&U/Rs57eUknR*<ܤSRs;~)Rͭ\sO)mMB/5?ܤ*T[sjQ}K&d|TJF}T_jnOV}?Q=*ſI5>6@s2'T oknR5{=ܤ &UBۚT^jnc47E({IAsU^jncO4W C#U)do\՞UPHsUu 4W.i_j'j ~);4WKڣ?/5',7hP}Kj6ʂh*T[sgբ͝t?YΪH/5;( ~Q)~ÄjR}KmɆZ2G#E KFa/5; ){P!m͝U^jnc4wV3RDsg՟Bۚ;Շh Ϊ<5wV{=YI͝Uu okSa[YJѯ{Uuṳ*PKj\ŸT[tgU:U StUJLWNvDu̔?R]դRUe麝?q]5e\WuU_nOdWM#U]*o]U:jqnVM*mYUN6@w2'U onVE| >ݬ fբB۾U_ o7*|"YfURx0`jj fY5 Y5NjʄoVe*¿YNjʄoV}*¿mYNjʄnV*¿-YUNjʄnV*¿YUNv@vZ'UJonVu{=q]5EeadWtU^ngOTWMQY#UE*ao]U:USTHuUJܢjT[sP_J5)* {EuU:jnQSTDs*P m-=ubAs+ܢ,՞ۚ[TuVEMQY-;%5ԇ57EmQU: ܎h2G Tߠ?u¯5'L7hPk9*~FE ZT'Zs;~jʄi*RmͭFu¯5[>ܪjT[sPkmV5EeaO4ܪ T'Zs{UMQYͭ@%5ԉhnUSTDsjO mͭ:ub5[=ܪS~[sjN}OZscƚ[*q[U{NOk_82nUSt{tZ2%@L&-UN h ٭jʔݪTVե:%}'|]*SwSi[UꔀVޞPުL 孪T WN hm UTV7jUkmq* ~$U ZU'}~jʂjU!~V ߆诚GZU`Uu¯ o' \;5x̓yp<؃"(m^dB/wM@8 {!l+r's⤝xk*="e? aۊàZX/7Me@x lr'3͸%8 jʒQ*܍&eL妩/Wz0m=ja~~4*ܐ&meLVRd5qeI)jaaEV=OV)PdtA {YULe"6)=jSdU,:Y""+SLU-KC(jfBЊC@L! E!9v)=A`B@T;KC0(rPSZ[DZ²d9nF`P䠺Y""5eI)rP,LAN hEn Ej˒RZYAjeЊ 0%AU0"U:%%@9y,KH*daEI kENWVV#T%A;9R)=AeB@T+KC0(rPSZ{D29b`P䠪Y""5e )fiEVSZ{Hz)Ȫ!XYճN!hEn! EV Z "nF`Qd:%%YmhY`YEU1딀VRd5eI)jeaEuJ@+rK(rTkZ"GU U%딀VQ&, E 9F'y"G5AݡQUEu A+r(rT["G U/VQfL! E!9j)=QmB@U7KC0(rTSZ{D29v`PY""Ge)rT,QuN hEn Ejz˒R䨊YA*fЊ %Q0"V)-j˒SdU,*Y""1.KLU! (jd}Zsܡjk5\(d3i"R)=jSd,zY""jRQ4"u A+r(򨆺L! EU7KC0(Y""jRQ4"u A+r (6, EU7 #0(Y""j˒RQ0"uJ@+rK(ƻ, EU+ 0(ZY""j˒RQU0"uJ@+rK(򨆼, EU! 0(YȣZ LZC<CgUɮg $q抜XjcܡYUU)VQL! EΪ!9^)=Y텙B@U1KC0(rVլSZ{D 39n`PY""gf )rV, YճN!hEn!Ej.̒R䬺YAfЊ V%Y0"gU:%%@90KHjeaEΪuJ@+rK(rV[a"gU U%딀VRdfI)*daEVOV1Pd5(d3EVSZ{H^)Ȫ!XYN!hE! EV{a"b`QdU:Y텙B@\T7KC0(rQSZ{D 3v`PY""5f)rQ,EuN hEn E.j+̒R䢊YA*fЊ \V%E0":%%@0KHdaE.uJ@+rK(rQ[a"U \T#@֊2N^\dy~X;wȔž/2ML~<;yr'ɣ=% {:bD^gzz wtoc?buɄ] ;8\S{kPU}xOG,YxO.,Î=ၥNxOG,+㽓G;UWG)tJ#WϞmG (a';OS{kPUp1da>O;w:4=;LjQ#Lɣ%uxCI[#&L~zxtz wOryd"/3ӽ#O㵣'~+G&xoޯ~݃}GYA5}FuqxѨ}yX)#zyw䀟QG/Go8#6l zy`tz x<`tos?b];8g6[]Ǯ~נz?b=#> Î3!9ZQh0ͧCE6?G<*:g<^*:o9)їݳOC}{ {Pto?bȆ^=9.Q'k੽o8X#2q㸧cDa=# q 8 щ>; }q㞮=='C6{9ɣOo82qן} aLJf5O?xl*|3dP3:g=:o8C6CPN =}COSiTGL:yrg,tq2q/ˇq. i/ /LNq}qĈ>G|Bw>aΟmGL~ogɣo8X 2q/χiǂޟG~}3A';Ag-A6[.==^: dC I2P'{< t?b]LҎ3] iOާG>x>5}v93% }}'V};@w>agΟmG>73q7GL}#? ] sqڿz刭}}w/tDO7刑}}ܟ}N==ڳ'>6oq_O'Ow}>>&T!0U>!4U>!S#|lqw|1}v53!}}gBw|}?OsU=9~O'{ 9g=9o9X}sxF> >+~qNx>xq$ ^w_"޻/Gl_8ww_ϰx}9bFq߻xg}3<z1cCW<3+_{ #Î+ {޻|w';6zwd}œ;a{oܧo8S #FGvw_U[ m'AG}mWU{Րp$1!ji|{{Փo|ܫԇ>Î!LqR;}qR|&ۣc={ѫ>$jB(57!u?f㎏YU5ִJ~1z cVUu>qw|̪&}vܫNrܫԇ>#UUMGWm)x JkǬ:zrܫ& ytܫ pܫԅ~>N;޻jiwvB/Wx*ݟ~i{eN OegW3NGWm)xѫ>'rTj'}TQjy/S ~>&rhӎ`M4ԇ>~iji{e&r2[ݳY'ۣOثq?[R;`{˯DB;II*cd VڌVM+<ӎzDjD+ӷ(M{br쵙lzRt$5d!OR3>ԅU,cV)UeǬRP=:>f\ቜv:'}PO&+7A>q_Ǭ{b{z {Ֆ'߀vq,qR3xqR%RSM8//> _> _/ۿ%Syg_Ӵ\'sc;~?|?Ë_ݛWWcᛯno~#nAypv=dnwGtv˞۝z;tG}]gt l= lft~´B=Ivo4m=AXq>A췻AGʽ_b^?qw췻A_a+:EݏZ? /?ow7pW{h"7G{W{#Eݍ6v^ӑhA_d~}]GEݏzۋ_}__|ՇPKǁxẼ svg.breadboard.resistor_220.svgVnH }BP^4"N&-!]#+dJ%v8!UbxMޮf=o'7ۗq-ڧ.fq_5;eoMݎkTrΧ,\_:b`U_ŴVME.{Žx*v[<HKgVAZǮXeo'MW==ۦeD@dZm8"أijb]hwEfwsX{3$fƸUb, ܔ̀0~N9 p@HfoF6˾zs,jPZyKxtKiW\Gmp\3T™q.OA3+U*2`idd֐OT?bV> > UIg‘%DžɈZ5\|<Jϼˇ}2"80/sHցD@ ,sXBQtۥ?K,AHNG&z2Ɗ?Tt<^-$RKq2&8gh ykylsѭߞUэ`J9 n Yf,.w;VX ؃V,A@ J1-VTv,p9tE)@|*,v!$FҢ  s,QYUOV!9 AIX=a(Cxxۄ"%m, ),Ȟr2lztG,C#!hp=)g{AɈGVICX#O1 w{yEj`Jjn:8$߰d6( ;6 W 5*FW`KMkJڀYAα2Uq?)tdK!\p|ƔrD $ ŝbNdl@+'%k-WFm GkT_Df~ k,^ƱJ#a?=ZR:JVq T<\z1Yf&I)gP&QP)nЫwʏ4@$A;, $p!?9Nfy^uy]x:-z[Вdf'zU@xi+O#)\S\ |1@@pRct _5\PKǁxEie0svg.breadboard.starter-poti-small_breadboard.svg]Yo$Ǒ~m?yŒ i,Yh/'i PIqdzGDY%7 hgEeEy͟~y>o~{Ys?~gǟw_7뚿맇͗~84H 7=Y/׿||8>5}*EYϷwytB>}?B0. Ǯi>ݾycbZ1y{Ͼxx?}<.~./} ӏbvzsp:ᝲXW]{|}o5?\?;>x{R~ܾ?yo@ٮy{ys>LJ[у\TbaX&>DN?x>Oퟲby%mp; z4߳l1((O !KDR?><7=VHk=z 61[x؅2>i.N+(½!|_s,L0) tI%rՃHTO e\DOElTF%8W> sJ p2N^![-$6:LdDE90㛋Z?QX˕G/?#ڈkRi>B AKzjIH4%F4WzGvD8VvDɶV mrlѷNΈ٤rgyy)#Mf0*eTJv╥YJ؍P^MBh?io](hj}+[Ѡ[ I^ &\˙\~O":R37J\?wTd~H m%n(GbKᅆqj>=o9|GJaX# !򁰀v"; 1 Co7Ґ| ?ŋAJ80/nN@ɘ 4-\TF/{GłFY$RBl\[`FA6k(OPQPa X6`&qE\|FAJ&]Ns Luqp8AS77G9C*\N=lM Pϖ-ff\92V'777FSZE6eV%25 {aVxN~<^!ôF|+dK +Z/*͊xg,B̙7`2\)!r64[ Fsc62%jE3U-i,;C i1^ V acj(hA]+ynh8ȑ QW4˜ΊBV+wY axg<%Nܴr-j&+ҝ߈؅p tVd"9dU@ ʎ YB+g֢D^[CA00YC/|v7v0_9B/9/{<4FꤲN&9 o N\1#>!tw )9E4m.Z`_JY^ |gTD5w[C06EЪw.L)XS2LJ9VAXV3;zXA]kҝ Pപ%\j]zb;GƷa9>NOa((lMs̍)0zh 5w2-%hm,6[ ͓`y. ^ ab,(lMse,ыҖʬ" [Fc]qW;ay@ ֶK_{ɚ|9٪Ú-)lMs Z"23bc/Y7Oګz7dzZak,o),].'x;@Rؚ|5"˥;ZakcfsD֍H:-H % lFscrsG'.ߧ /d~ @h6fwW]t_ RȜ/3'w:7=lᚐi?jrj~.ʛF0Y{ up[@pU݀)ձc mD36~R#xMkj #q39$6*# aDRl_3KY7 P,D.2sY=Љvخ Ѯ~Z/'ݻ*Ϟp߯R|TU]eu}B I|]5uBI]___k#aYUrZ$Q7nQ{9Etj߭vR䵅X-&V1iT}MZh!<[I67_KպjyYZ=Jh-rffhprˌoQ mM^ja)fH࢛~jIN~,ݚJ3k0 Zܮпo+oI1 #lx8}42R52))aoa$]ԟc:OL6~ %%}ӇPTy)IF$6!t2(6l,4vFmIN*zr2ڥl7ŠB-+2Ta6EXVcӶ̾{vQ{m /!/aI%l2J [ɶ;O%LJK.I:M̎{tm׭6Vz>LݰQI'{Q/H2Х}d^p+Ds5ᔉ #"bŘ|R6pqD~H(j#X;(AY7BQ2$7B1`#6A?>6\[cumTR$ۭHPL޲JmVI0YkJ}H 4$&@IJ:XHٴ!TYvRbMV9im@%[eל&qvuTglJ&?CP3P?@ę,?)XĤBd^.; qʓ̛\Ȳ˩Pr̎b֙;:f\4 f; Ꞌy;.$q)6zY$01ɠ@3 DA,ha@e.h N*HY'GLa2 vGPoKa}ZkWiPZ0k^|%@U&ȴ7E' ]V 4 Vl^1z^o̎ϽsW_oVWG3ݪY\\.ώfw6q_{-/۫oW_[ Vo>f緷/󇇇Wjq3sYLt[@O=VG_ˬb#\޼0)˛r݋rRJ=򷱅xKfoauz{~43$?Η򋷾_-^_' qɗedu\>T,KH4^/o vX-.׋Wuu! Ee_^ زj<~Xme߬ZZd߳5|kbUkV`-z4[OqP>A(N񇃺qؒqjuaԵ)A8m=4 txg%)j~=RߙAS*33Df&TtTqKەSD渀U@'_R'jh`w)&mrGU6'8Q`&dew*8Ql*\laiOq2c%Ӆ5{Z`(:xf0qOYQ)˄A{:Ng"j!s}`"ب jHؚP`YjD,VU"9 'OyQ(V5RLכFi慕_,5^h,5=/:杬u5{Po;g"0K;z"9P~|gfSe|F^\6QT3ywru1pٸڦkZy1NÅ]_m ;Mb̈u/1ԗd~1iŌ=N{ np}_^ @ ;KANԖ6ؗJ{%*-m:,O 8L>zI 8y yJuN.T8@^™Kܔ<+;c@&Ĭ!Po;S֩^j&jAHސ nXCVېzysULKaT,z!o(|F" X;D5bҞ5QfWj:6 %HOkrKseQ!ڟ<;qpΟ~͐?q~ǔ7q .IzLC緖ÈI{iƏM&LR|k64hh@{]Z>;۵|&GKpN޿~$wqMY93CPA>:usCPauQA[g]nv>CzMs>|܋1Hf(݇ڜFL riDwWkFL9yLOTrTCn (M 0[psύdPc7l,G Rn(q!-D! '3s5[۲բL!dQNW}#|މh@aUʉȧ*ckeb\Jqw%NBg/ ^ȶ+ʉ!#dѡabg;}4e'OM.%!t4 +m>sPϖ]9YSFqBE> IL`ݹiD( cm9J@zA6ȠٔحPyhDpAVؐMd5Tq\9Hsi[5$6f# m")˂+(*b /JиgAI0uVKD3 m#ZR 3 CXfO3M{ xN6<$sCGupE4P(C[0:+rjd'Aj-iڒ|~+6ruΰ~j^\˽o-*cJ -9b2Q!&!.%\Z` }̽ l(wC#ctVSGh+EB:1:Td@|.$S(r0".ngF<\*H` #@낕SL7eG2:fT{K"FG#K|a!@MH @(&:ws0`.kPBy,ͽ7Bɠ L$,gw,.-)K܅1:[XH݀e0f` 3DNa[خ=J}/Yz dZr``]ad}OݹD757:Pf#b|,SO}_PKǁxE nsvg.icon.plain_pcb.svg1o@ ʗ#];tjv[:ra i__*vl9H=~Nܼ<-s4vZj޴ϣ\MWjjiѮ9wiE<.Ç!#'[Lz cwxW˴0%TN#ͤyOR|`&~GM|mfY.ǗY*̳OnQ/Y=}9z<DL!jzl2766j*m lJ^=Bd}/I4Gx1!@UQu_EtTmC) B1Ƥn< G%!HEցz"<BHAa4X }RΔ(rxrO5c< {q_fL%$I!&41p7DRG<%8B2MF%l5Z(e"f0N-W; re??PKǁxEcE svg.icon.resistor_icon_220.svgWKoF>ۿeKξ7$p IS$(-U$Abd3E9Njȝ=xqeYF)2Ml=oVQ}Oy|]BͲJ^Tj;myELY;[V]׫_ڤ7mf[Cz_ﮒ~qEç7 ($v(ĝxnf+ FJovhj7zYeIHYdzvlV? !wy4yy[R%顮EMujM2Q:ivm 9XldzʹͦYRr&Wh)ΪO*,/EYj\jUSfYf%3+HievO|R񝟝]kaxL&B W DK/d2#~;PD+ N.cIG|%uiS*b݃B5F_C)M$cBP9a$K#.+\̈ #[4]JD9rk,9xTP (h;SKuhgIdd`iIf@!g9gl遶Ed>?Zһ e9GQrUˏq4Ld!tN7FKjr><]SjVRc*W5>s^^`pla5A'0ٵ=X1Ԭ*Zsiΰ?Еq7(nWjՈ~,6݄dv|?v*.'G!XGZ~ YMɗ˶,j{/ei>r|}&k MS Vw<}JͻovC/c\WÛp8=vW=W|y(<%Xv?GlWKɅvn皡἗kUÏow;(o~5ww~n^?N\.OSwxӃ^_Tb[^O1JA hڼ~4;쪂VUo|w:+e;KksE=s.4hv@̴wN6W<(l}dfG#c +kstW@t[.0r[@~X9=J6A#1hɑRK$'%'J<]CveKqe )쀹c !E*y$fFDTܴ&9;3x9H,mHodJ5첔cV{E㿧ʐw\|E}-~@uѕ1[w*Yh+#z@0X诲9odݛ:X-Y[ 1… n…P q? (?{#tBnV:hSlƿe :5`&ˌFFg l/,8dxxdVu,YaH~@th[d9rpQ"Ey8RO[1E>fNM|ml̖kj׻]Ejbx|hˢ{ Ϩ՗X~w{[Gӈ^L5W!uKވw4äbmsa;h@N7BJ߂47`^M+3n dπɰ۪ Zw%P VlE2;;DZVFYY3ō6|T^CUaJ]e2 3 p2 n5Nv>^?n>@`:pulcw?vLjPu0p8XTC=zK-R/=u=Sg*eZ|6>OXVReHm.?J AMY!k!Y}q!4*%j5A6P%igrKK>w9x [ 50ʁQ*0+<v¬%aD4 m!&{NjTΪV陕 i܉5#Xʢ=iWY#h*CsMîhq^f T9[?t-03+|pM2P嶦XߜCz4{<\OA FXo9WV̳8Bev3E701(LalCJM*,XW Su 6Fc#0 rrbsjhTcmv;VV`Ў.f6@\0 &kܸbvfOj D $cUP/ &% uM HI*p[d T0]T >w2 ATjWd:7Rl)GLRG4Eft0RSik$fE[DW81R'h -(q,5$߄QT 'oge.%SBNkx'ZXeĉOF1;K#*eX>k@p9eUd#ĦM)M ZhR:݀ |anc3gOK\Nu=?˜<yh͘fuaZ1Qd*дӀGT)9Y!M"Ќ#۠j1"@3&s 44&Di\/Cu"H&Mr={ÚD ڐ@v-:bx9akd%$ّ,FR.hIVn ڊAϰL# $5DsZ"YiLt:ɲ wĈ^Ȑj 8 JuvBUr'U#+="icKf4De>HLeeH5fy'e  pE+EУʺb+ی` T1B{4B"?L-rЕ?*4Qтƒ+4}Yd) -o^[rA?Fi24_3-4.vܤ$--;E>Z |P77n$ڠ04DFM4k⦯#'[Qఢe$هg_Jt8jDCT4@'TS$kRxXmsqOe @B7ӂ\\9d5hfGC'xZ6{H n&,16\tG+ӿ7Wk%љVaEH.!Ĩ lή.*NJhJRpvcpnMద%$E l= R8)z70F4 ly6Lɚ<ִ̓;[[D](M# '*e s%t[G*lmr8Ő4/%fF2T)7k7U9&.̈fz8A`[G+lebkǚT)y,5! NB{b\iU c㔍dc2h)BK8y,5aK1] cJ0JsL1]Qq [s:8\C\$dz rz%\H&je|].|ȅO$yN&,Ub6`.(%vƷ6rbJ 奂=U,@Eֆ.c[0ŔEJ7[[`i?FK/15) ${R3[[jI+w# r9ksF/q5aKZKFyiIO2Z2Kl om%-+W"reKVdڨe܁yĵd:6jkYx\뙊EZ\5H[(I::j+B` Hʵو8$V,tMF,c:o_:vKl om2v_%ݪ%"Yژe9,ަJK7ލG:|.=^#80^:TUiꝱzSUkq"s0Kc乗f[L{kln٢LWq|"N@Oh֙:尐M%s~nКXq:"/s4\&\VOҫ3;2Az+EBdž0oGwfiyr9(0i^7q'>ݦRsW_}wMo@>O5w].hgLߥ|^Xqc|$m _Mvnl }ݸi~VEdq/茀x(pGZ64H3]rd3jf繎; :pw7 |Ҿ* ѡtc~k)UHoN wAጕ Z0UZ309ՇmWEiD%E[ZnMw|"b}d 83?] Do&hF,kgL_ᛐ^V5Pq~j.?bj>#g:5=pHq CL#QN#ibz PS_ۢ(uٙpf0J,VBclHc6 |NaTf۲t_7kj}Bja +dz";20xD!)sCw);C%\OT1g5X#)BJV)ՙ+vRوI -3Ug]憗J?H/PKǁxEB#U svg.icon.tilt_switch.svgVn7}vݾ4.ᝆ hO#+Ѝ$HJ{W1dhAcpfΙ us7j9nDF͗by3n>uy]͗n9VoW}6:_6;u/c1ˢV޹^*u4qßjF]~:}?QM7&هEӫb6Ҩv[Fwwwfns]̶# Ϋ#>]70G+Q8_lV|TF-|4/ZnXZi~9A))DǍY7zw;nvW0u1;]2YU[ 7|uNh7ld}U@ՏOp4 ?K6Vti}79|jE]V%6v6k:mǼH.ɶڽ8::ԛ`XYPc<͜vu8cnu.˹顳4'rqAP\!sykۢ3\I@QZ\c!;܊b/ؙpdvP"|,1[w LD[#e{Ѩ/E;1Gr6)L PIRc vN3m@.!&(TD2`DG1 OG˘*rWUT jdK2\{>͢1n 2d}Uv/%?\|Og)omVB!"- ڡY%u&m\@$`΄I*'窕C:^gC}V/qGVG>S;y±O TqAE{0s |ṵ̑CF<$ ̽u=傊6`A{SR2$c_ǦBϑ NpgbWY11mT:NF7'PKǁxEOg#svg.pcb.alps-starter-pot9mm_pcb.svgT]o0}Nŝ4HZ-Rn۞\bdHHekhTlskۇ6V01P&)Rm{/"Gw( ;iq2Uݦ@(^ٶ,l :. 6yiLfk?x?rr MfW'@<ƾ'ޜ > ˺.'5MC6cvQ.uR12E Ʉip)4&pL51~ǧ+&㈙ŃE[\rΝ5f9ǬИrK౟˘!%Jge0BVͬpˀC.W"YeXѨ٤$&U(k$)RY w46U7F%xԆqMabg*AWƗDRE%'=yDWv.hG>37>0N ]]P Q94v>S|\u\ ]T:_UUʴS#s(^>}6ś۔ou4i0]Й]+GPKǁxE^y svg.pcb.arduino_Uno_Rev3_pcb.svgŝG?E/! z>D9͡$-`o$4BQbv5ZϗF菏1jYxY]\| ?{.ogw+ EH.˿J mѫ8j>m?_tZE8f6yuهoWl>tv~ֱ^Vls'7ݥOWْw1Ќjsɘudzp$cûܨIlG6el >6D|{^wv \|.uxVpzsA+cuOm#BB,ӂ #M$iBb뻫U<w*|!muw\l d[]2JdAU$9ٖo JdbSeJ!v`+`UxDckV֙ 2KN%%3P/pd'fF^6F^k_J#B 0h V0U Mrvsrà'.@ gm)!iTg\殀A#O +V(6C,+C_E>h|k2eU\ N\m梎ޕ˫J"/|@b#,%Ll%Iqn*mLW\g~c,&z+Ȍ6I2GRʕtXzofڞ}ҏսIo/%!Amm- E7>t შ[9u:`#'jȔ".H*`NR񙗤6EJ)'~!S)XlQ՗*1*:È-zИ|+ A3Yt~(-!2H0`X"ņJL&Y|\/iyfɗ=+bHNUaJOv)%Uӡ4-Ȅ'j[p DxZa˗fx-[ 2je *ʶWjA%Zj3΀2F*np KEO`]ѐT(,ָ)HsR?_ Dtei:`e2 s7ԱtZ:3i^KF'RgXåk{X,K 9TjE0v䰶qRgXO4Bt(43,(ʠU1ޖW!cWb xF9l5<0O^C Q*j,h(. mp{0jߡcO3y9`j %jߗ#W&oʑm.#.]_`By#=׀j0{Ey̨VkY,PQd @?lgyTV3l@-38{h(.`pUF/}6f- QH-b}Pcv;&y @fjf} | gvu0{Ey/+JKUfϰO*f}`Qddb_VOTXV X= B%=~mUJBы|`NMGg I gi0zj>ԻҐ}5,/7үcL;d_Ș)~#2FHVJ"{̫nv~y垙l^ם__ m̼;Ъ-m&T6}P=ݠKӭC;kh70 rխC{$e^yH;m^yHhb/XL uU{wp%c]!W*zZ6_}FL Kj_/kҏ6zOYRzun䘀hK $1첩e{6;wt!lHW'er쓝.6={6@}y<=`~EX$Wl߃AbݔS1q|?|~=RpԹ!!ي_[oKmfd8K*)c$(GwL4\@@OF|y5%=\n{mvW5z4l19 rz0M dLݓ(#dH%SJ}rSf& a\m׊|+ZL 0^@d_yD"_uA/:M kO.6}J94gC(^zO>!ȳAߓAF?lCFp dk -W p8+gy)ꔜgzEya/!_4./W==¶}\gl\xxFzu}z{5z{^?+BH!l¸m&M~))V|tyoŧg?}Wmť5V"2>}hJI}2{ `׊`'s 0gqO:92Q4q?6ǎF(bAB1w*CK bsYFlQĊ؅E)c2̞JQĆX~P / xvM Cd12VI|# XB#3&"at8vZ{pLw{JwrQy 8-<7Ղsvg.pcb.jumper_2_100mil_pcb.svgSMO0 +L.IiCM8B$% tv6!l'{ω/jxUkkF@iڔ^Fk 3}Ե$hhM B5JTZiO7a$ٿ߱C6JJ''-UdeaF(FH4+f;;';FPuܖ}H˩By51!& %Bo.*]̾,1ƛ #E]Gȶ5ٷs,g ¶9>309m5rjEV̹j%vK1F6@0c5(,jeγbqȑaSp0ټvGX곹fL'{ڴԗE+uSii,fu{V/BVJ`frqK2Wq;0] ( 8)5}XPO:y`>~U_"b*iM+VQq3-.ۖ(u+@^x$ڱ/!S ;7$ ^O}J|L(೬Ml4@q澂 @;~Hl7M/VxߊמI\EhwUuKWz~# P?@:DzF277RWPKǁxERN1svg.schematic.arduino_Uno_Rev3(fix)_schematic.svg՜YsHWqU1 ȩovsxHXV"_=\"֠]Q ==7K.HVau۰t-,cň*`G׿FYi0a.0XW^Bh'ddm3nt QL, cj8].H\a@'V8d }vdq$ Lrfxd="!2čGm3#=Hqh>11Ǚh}6;Lrϵ4=&Nn|rs/)2\ygmue2Vj3BڒMӹjN6uGG0q 2e*$X`T+$s)+ZIƾ}\ f6g>%6h;̲]_rY"rښ|cqY{6'?8e`Y߾رv$|x0+sc]|#*@,3l&US&f24dκ.>3r`1>#xD&KT嘹䜻̇ 1f]43 MBbe$ wL6zsV979Rh߬qIFFYkGQd˼3SF*F sS"=^= ߗi|43WC퐸 <SbFiu8 `t26QT !SJcûS9FN}vN@y5 Lq7pO3CQz2 6D&F.pW5jSz]a(Te7ʁ2+0'i `n΁r *0,gg9>c :0h;8=$&E#lJ1I,Pżf^żf^J.$6"%-$6"%r̋̋d")FW)C }ulM-e:ml/f2\\xÈhSs)=&8z{Iwˆ+~0y="zHGwF䬮mi:|eB~RMJ6x{ e;s@Y&a G u*|/c鯊vE?t-^eGOIW) PKǁxEe svg.schematic.tilt_switch.svgUn6}vb>.`QBVA -PdZ&"D~}GMq=29:#5<7 nJ0M[??pv3aa5wzS Po12S/ˏ]CX/?^ WyW<< "@Vε0nt++ڕ)/9]:9 Zg_M4bj<ȳ, )愵;_ykn 6aF4z{a[@&U:/ʇj;aZ-JӽJu~6W{*ʕ^ΔhL.ipm;t6MQeLS乤*U1mFK3l O`_PӓkQykHi£vD/b+*ճLNgGxpOa[[Agxj?Cp$%=e@kMPإ"VSFc! $b*i)q'iog\vW+q; ?>=Wݻu ~_@ e߾ʽ^^Owd6s{U{~c@F]s5| r2`k3OWjv &˧+tW|5eOQCU%4[lN2s6}`ztx5vx?x݃$D؋CBߜHxAaà^]M u#@b/>gzǪƬh|VW_جPC9ay6r}2 Sik IJIZ3'$jJ5#!`dC  FIc1LVH#WG GSSϧMJOG B -U}TeM84!>Xhj||q}R]^ !ΚS3S9g4@nO TJO5 O6O9Nl04A>fÚphZ:}̬lU@4E>!qUH4AQpP4-8հhz:}ʁtXb:}*8XMPL^HBS ҧ$MO'&MP'e+uҙl5r=;[wYXw9Sy(K|VuD'H20h4{{^1pkt89%:bXU5$'k&̧j&G L֔d|jrqjʜ<Mڢ[ NXM*j }kb }İdMKʧj&j6-eMxW_o꒣0C?ę~ V@Oq`Z󙢢?+¨-vo'RTǃoGWo/5}?v4rҹ\Y >Np/pAǧ7Õo 0}8X-Ps*]f8 枙Z'=Q JY(i>0\wi_^NerkTo2'>_m{G#1 B <6-xFĠ(#%)-(no"KQ@t"̰1 `#u9o AW:3@!#;1Pl$E!fmP7~&ӎRI!F1n҈!C)̞idO=0VY$ A fq2ENm? 6斦7A"m M \b2 6!e*r4oyfdT>i[}LMGҙDfo0ɣčh 82\U,.zq>RhO*>i䓸u1 TԦΰ#0P \ 61L-@ lm>*ɎK :kP& +$2C\OC)`4!e㈑bqŌqF ۴+y䭦^#ű-Ce!B9-[#ENkkN*$m> '4(d/D NPϨr8́Ȍm> yTmF&EO6C -IC@$ED2ߕ^E"Gy\S,ostm)(Q P駉+XBK ;fkip#n-2i\U,.zxq$>Jh4TF*i4Ӎ0{GЯZ`gT~RdG`riB4ɂrW IU^-OyW<ӠW: Jݏ5?zYZj@d"g&!y3i62\U,.%pw<_UanhrqW%onnriO JZYP7^'r3iV-c<#Iozder6K$"y7DQ#33e0)bq"331kFT\kG|7ל\ˁ}y=TZv`>|zrybpܙ|>пy&ɦ{I AMXڱ3r(<#zM?@Ff ʅsv8|x~EWdnRrPwJ\3-2(gw\vfUq̖}_ӷ*a4R -Hum8;9MB?GGbY`.|We8~ \ZHwXUl Y~rσo!<><}\NίW_δI*'{iz߾;}{>dew`M}9 mo㫇sOѽrO8(A t5|$k5Kv5h,śq [)0FhX[qRN٪[ .nS2=:8Oy]}Rբ-F([Т\?&ÉsɴM\PQ_i,:Wެ ƾG?Ͽ(!i|guX?H墌(gB,_<- ]bO!,rX1|^`>̶m z[8K* ݓU =gq4 Rd-Rkwsy '| ȼ{?dDQr0kW@b!SsDZ`H"{FNs833)VEy {:m$0q3a]x>jUg "9k]GNø6_9G  񴍢E乪X\D8D}kl5k)o98+jVh))(#vS"ٓ퓩Եe OB(oFrFcKԯ2`F60byu|r=DJ}WZ&'lq6iUZPۺBmSlMS#vDjDA?Fz-m>Go,tXˆVú5)U&'؀o>NOĉgrPy6Xss@&m׼mj C7y qi(X3H߬ҫcaM TC{(OjjuY̽l4FܢFm{ItZ ooAFgf{nn`u6s_~K(@zO&>( MM۪m$Et[pؠE' 9຋Yo!|І݂6h~Bb604^ v(iB9d9Cd0A f3Ta8NCIz$9zi<:Uo4fit#1641gYtK7,9n~LSÇޘ&wzD aM/ϟoLr*–NW=8bB9%LXL]0ŐS5KLìQC6WNd4ت&gUSj&qBa$wTΝVڶ<3 /R/du>gtFRl]FCZ#+E =!]К%ATj{9DVD_"n ARD*hRi,ѢXU,.%ZL}xqDr^8em4vYIpZEBr-v Xݒ`OMDkR#2jZ1 DzZ%"L HΐHiBd*X\t 4xiQv4IYKkZTВB) r10:' beJp2f װn gk+N&5{#U[F&]z!_};Rm/1$֊A4LR$F4j3Xҳ$EpHz3J .Ҝ{b"d5"UFOpILhy۝h:' a^k5NXC.,ffZ a Hō>iUY=iM}zСX!L/2j+5՞9תbq#s#JZMSOBW"$ rcsR]֙|&/2rs a 5 m_ f jƒĹү̿Vn 9+Krar2SH~d j(IW2~([nx[9P1BePPW qYm\*+rp! Īՠxv/*Er9AX闬n\[`n3 awj^Ժ{RRܰDFrXCXs aaZdzX\t;ȡF # K6.s9.ĉ*Y4+) +LJ\mL|^I3ZhaNGNfϏo.7i͒.?X7Lt`b{b]*E,iucԘy0!՚תbq0#SFp#SzcYBfp+lAheD`h c6-–~}Nč>- flRdblr2ݘkd+{ΕIYMq7:0iڵD23[U,.p#LtsZ>m(7ؚ(ecWiu ѧd-Nph_ Y yȒ2,<(d!9_RηAmk@+3fLcUX*^G,7o >e"F8c6Y\k[.ZR4GrY\r7)_&[BfIbS|js qmLVM*bӧcj铤 1"5oEHd~fq ez&)r-e Ok> Srr/5$8ΦLVnt59}lE| &^#-U#L1`K{-׺aH|5gj\lUv`aG9.Y`%M~U.l "*U2RL1m9}Sc6&"35dtclXRX?8x4vVn9"hX؝R"DOg F>'2mH]n9 v+jkUv`F_οd,:c3*FM[k /dvW3q#UAF&_F&_ɗj|aΐ " NBwM'mP,X2xr-CAjM x5zbDs7jn 7\hkUvWsؑW$݉nxĴ*aIg5/TFju4 |)noïLjnz5gJ352ZU,.pdxddwe^t"lk2Db~[č>-U щmZŶa42k0\?զתbq`##/yTOz|i= p?3P,7e9A@^s#ʩڒymP_G۲wum- m1*:RRW*؏d9 eBmP|ʤl:CF٢ bqur@T& Tfw;WduF!+E˕sEk:mkwgI_*}'BzH(QgdnپJef{Cǹ&qizdڷX\t;49yLvcM:&j0iR u#Ҷ.k7זqߙy+hd `aq#3UaEfZYļ=>4hvdzGz?:}pu?xs^t^FݗO_}w"캫ۻrVPprw4M^n;G7>>>./?}T|9Khy(L M$QbNtt(up &s&Myܺvסd8u W9.]}㯃3S7ϽAΜrAY_U8D ʫB /h3Oy]US;7O`vWJ_K+Xɳkd[bq>po@?y#(ob!P yh!P FUJ:[b@O~]q nFw/ߡ@4(Asp+ N,C8W[ʟ\ ƓÛ/hA Ç7_||/4훿Hw?|ѻ{yOt-0n؟\ȫS/f+: +7:AS¡S*so:Wt8]wȿv:LEFby{;/0ld0kpZ{;0>\?~|(SSϿN."AGsY hA$Y(ej~|o^e] wv}Ge ]s%mBXwlݱ{f*z0d!BѾ{#_B!ܑ4k;D9ETy躢4}O iB5Vk,WSN˹㻞 ۇFrA¸;f% cEtoT봖/V H Npk֝VD9 mNPnA))@YJ/d50دB}˝|(*hxB;YX\ ~woڭky8S/.鲂~uqlW؂;ug4Na=2faP2]ڋQ3Y\ Kc85髣Ÿ7LS~pua w)wt]R@T8}b/TFw3?fD.gD:R0w{N)LK?"A6ſn{:lP#roߝlgr塥KmaĎ;w|#x{j tG{.& _J*tP* P⠺3efk-J/%$*0 #6өSj0ϫ0FT i=o_P.c Wx°Lv~qcH@ yןPwνiӗ B)`%,җBuX}ƹ{oC#^DNx|)@ hHAX_`klbr俀E Pu=Dc4ب߆> cכ^3 z <1+g @Mr',?:h 3v;17w G KCHdt(xP+;Gx9܈Nsv38siǗKM;[cȴ v[ghg;}hyn)%/Ual8iWɽ`€a1[ pv^\ 'a~<) \hiT7\ jA,X+zٻ= (`QV;n{X4 aE E##FN-{[3h(F ʡ@- TP EChji/K::!5:K9kUv1V7bv p {x`6. byUӇ8's $)trn;6_:@ mp~x״t+B0ۍzY=[.uۑjt}tA8LgVn4̒ `Zf!3f "2_JI%4cjl+G1Bs0T_GQP M@y,b }f˂Qx3҈_x#pC1 p ,oݳ  zL*;B:$E zAձQƱ;P#HÓ*R;6# G{\US>=`%B 1܀|Mb,sTЎ*}mNH(wV}7@, U*E!2ipK€9h'/bM7 tDI3 @T4ՠ{T8.Ja{g)8@`hQ[qĂo hDuwN 7>zQht D^V?y `LNzA7`%m- M&e`)=8R=x ,=a=qXrBPSku/fo~ ffY3720$A/+a`I0Z:^"1 BDIBB_x@z @ {P`QBx|Bhr*tTj|x/舨 E V|h!pv0t_D8R f> (/P ]~ (Цk+T\)J $'// -`O=H|SrB$T_ҠGНQ[K1QhvtD1M舖OkQ:toU虦nU~+d 1\v:g*YʂF04RLIW7 ZC!$p f%h̓.jC ',X’bDU!l đ 8c hb+x~ܢ(S`X"C p̀YdB/`#u(yW1y{Hw/VE0p!8*Ot 8{AJ0+( hv:ڀ '~Mw\dJUprS׍dw"1jpуVnjC!tt<ڃ \c2:^`6I$}S #Ak 0t+jC'Ѭ ' 0zc#_uplHeuM+:rtګANqx9<.Atd'l]39 bL/ F,n@bD +" {Gb @bb n &&)BL0&yC1f]RvOb(9ف&YtA@w !B& kJ#D@`ꏨ{xΎW#Jq`쎁7 * ;(k>.$A@IE( qBhEрkޢ $Z 0) G`#q!xd#zǮQ Zo_Yo*V y#X[U ё@'ܡ4ULG;z^1=+""RTDS ۅO%d7ѻXg BQP4, zŹ]Hƃ`2h BBd0c# 3hF;>^;8t>],%,`ꅟ t|c%.+l)+JoU;Zh.؃0;2Z O` r R (%(G/?5 9 C+װ*v@*xR+1M H $1y ݰ·{s')+PC[րRc*`Q6@Y08EtG>Jf`A}|,aդlbX!|!x9+dt M_gpX;/@Z#n?Te9twA6kVP? Gw .!FԾaLյ^ea\ HƩ_b# ȱN(Wy(Dc*-F<̴4M% @GtVL,I TNˠ6 qȆ2APA4}P ( ̿.~+[ЬM B #C|CگcJ1Ra]aTF׀4ޏO~ S{qœ T ݰe } 2S##r#Om?|b]dNBx9@U!q ]F[eB0RpHː(]N.؜Qc@-ck?Z_^0"8LS5χkiR qPXD!2W*Iz͡Qτ)7}nx]hl J#tbx"40*ij;] OeAУ "*h) 0!fȃ3YiȌ^y4 5H RZ0 =wW(C(Ch7ra8 YP܁(>0:硟 a9􈢍"Q4Wzz؃S[1`JCjy(f}.ZV ( vvMjz{4]I3_v\pcE8aG@^4JBX/ư-zQ K h!XCR]]TI7$a.3:k׍\d9n G269=h%QC&nq@`r[Յ8f F} 0xxBۘQZ4 1A2d'@S/EA BK {(C`vp`M&IҚC ) h#LtU'4x=6c^=d(n9ps$a_M1Q`]dӽ&U(x=ӻS]{9RZܜp@f@9˦F>p6Zo:Tw 5/  /^7j*3>ƢA|߽H)*AөC)E{U|w =L8?"ނPPEHۛ7\_pUvÕ#6164\}>0m?VPFqjZꨩ֌ll讑m<([-O UQCiP[hdl)a/jZ"T9bS2 5[#T WB5'9K!T@f4 P(P{ xd 3C X6ꡙ2@/77bg >F@OH>z”D#5n2`oEhGbaN`C;rpGhl54UPJw1@Q@ɼx)dMJckLݰk=9b CG' bwC Tspݟk:/~I1d41Q`2Y5?XpLD.v0r[4i~0;t~8v¤ѱ#'÷}LCw2*i,SfQfxibDcܨ+Z “[k(Ui= kw՗ɪ B9Zט!W"=C8HBcXA0"a”áSܫoFIB1Eȭ}D}QF}q;틢ꋮ95N*@Ò}%Œn6I>+".ɫc#`dH))t̙}LL. {" cgabN+X/>i}gh?݌l)PC|o86W/hT|tϨ%3YlwU[lFMe-1MG>1q;9x9H|U_=&69uj!``0UCtSqhū&$øM2S`EgAw.lj`= ć>)8``šЦðvzx_!=I$tۄ؅Ԭ:\c%_Ũ+F]u5B]o] SCRbWpjBnA0q؏BhzG.^}8f;7]YpWeyuL! i $B ޫCi86qRy-hnDjÄ_8l?O|f0o߇/yr7.oAͺn~)\]:;ծ6vp5jd f83˿] ۥTtq!2\]|ʕ׽d92 Uz~nd2s]M{U SW,}j>BR+@`4[\vA]?_Ohx8_bW׮o|x?xpݥ^^^Իzzp__}| G{m_At js|0VXq|߹=eF=Q+~?>[Y*+sly5Kكřo`NG Jޯ ޯ'U9^}&-y+~Sqȶe2~OCշ**}}2u"9}i\l+msm;oP߂ +-m;ƍ~ծ]GQzW0ԶT:IyNѰNd{f5^JtpP6jj1p^(t4~nb#$Y"^Pm-YyD֢jhﻺǾ[D4'vi9r9^'W6:|}~dr^*yuB^jF׷c$DklF+vߠ5nTfפho ,}SX j[=mJbT#:{kR-PKz۲}2XrLewJoF 8 7`~n !='|>v]C~rL7Z[N|r|1v.ڶsF2\ecKc$)ڛɵYx))v^zb/A(oA_)ܰ򛧷 HpQ s$G#u$ 6|Ӧx= r/%68eHL0V/u1e)FKzY&3@HBl0i1Rn)'DXC( Ѥ7xh&B&+=(2Z)ƞZ9U}=O a[k쑏+l*)OL= Rz Q#՟0#$ghD|-ݠ5tt]Tԣ` +w@y:SAEs EkΜɴ.ҿikрdaa-&Ro ڋ`DS⶟8 &-4eYώx-k- Ҡ(5eV+>RpR9R52NHH܊[&e`KDLmۆlM\:B6B.@ Os jLv]K N{_s!&aoX9|df̋ɼ7Ib3,Etlg7ȁ(\LU)5V=nde{ EckrB%s;ɓmitR 2fy Nr}+ck[ƍ>-^''IxT ܭ9t!* ))rO1$!#LPv^RYNaohgeJJU m<-@-0j lW7vٔS/QP@xǨ=n5}󵶔(@enBh^#(=T 2z<ֶcq5fudX&i @ Jƭ=}š dpuhLqgLpͮ4ukY2 kxDk-ƍ=}Ζ9TLvPu-LU槤+T*bk|J4T9ײ8Wre6 BX$gZ`L˓jMa}06ב$dHa@O5aeuea6 +Wiv}kvsFt9n9X3&Dp$ KI¦ -Kd;XFs2KD[_e>n3YS޴!XmJЊZd _5@H=|SLGZ,E[+-u[(ATn $Fo3 t5d |! %ɡJV^"*2;Sޫ{g7ti69dw[{y jBVڭpšʚ}@Bb;p[9nБmMs 2^,9x%|fS@ G[&_Kn9t}͓}/L6¨`Duj\0t*x%s3_o[ڀLVvL3$_&ĽL HİF^k>F^dL{ycw暈a W}3Uh+;0EwkUhUd򵳬rr/vdrd j۶j 3kvWsċ6O3mzGC l PHc9esm9L޵w28dd`YdNJs܅)5vRaR )2Z *F"O 2lq& 33 :Nc֪bqWlmմ%Jj#՟ݠ#Ƕrlkzj9\DKoPz$W*DV%j=2[U,..ZZV^Ms{ "!9up8o62[7r,8X+%?PKǁxE1oZaubertafel.ino5; AD9E 7163kZm v ^=ܥ?!+h#K2u'nGUE 7oOR~ Giۘ9j^$dи*4PKǁxE3 part.alps-starter-pot9mm.fzpPKǁxEug6=$Npart.arduino-shield_r3_two_layer.fzpPKǁxEW part.arduino_Uno_Rev3(fix).fzpPKǁxEmY(H"k"part.Half_breadboard.fzpPKǁxE;=R 3part.resistor.fzpPKǁxEzZ 6part.Tilt switch.fzpPKǁxE>6Kz.o9svg.breadboard.arduino_Uno_Rev3_breadboard.svgPKǁxE%%k.r%Zsvg.breadboard.Half_breadboard56a.svgPKǁxẼ svg.breadboard.resistor_220.svgPKǁxEie0svg.breadboard.starter-poti-small_breadboard.svgPKǁxE svg.breadboard.tilt_switch.svgPKǁxE; % f)"svg.icon.arduino_Uno_Rev3_icon.svgPKǁxE nxsvg.icon.plain_pcb.svgPKǁxEcE svg.icon.resistor_icon_220.svgPKǁxE2H#Wa$svg.icon.starter-poti-small_icon.svgPKǁxEB#U *svg.icon.tilt_switch.svgPKǁxEOg#0svg.pcb.alps-starter-pot9mm_pcb.svgPKǁxE^y 73svg.pcb.arduino_Uno_Rev3_pcb.svgPKǁxE)/ʰ"Esvg.pcb.axial_lay_2_400mil_pcb.svgPKǁxE>7ՂGsvg.pcb.jumper_2_100mil_pcb.svgPKǁxEr#Isvg.pcb.pcb-arduino-shield-rev3.svgPKǁxERN1Lsvg.schematic.arduino_Uno_Rev3(fix)_schematic.svgPKǁxEX,.Ssvg.schematic.basic_poti.svgPKǁxETZ eWsvg.schematic.resistor.svgPKǁxEe Ysvg.schematic.tilt_switch.svgPKǁxEdcD K<]Zaubertafel.fzPKǁxE1oˡZaubertafel.inoPKgfritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/000077500000000000000000000000001272363601300304245ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/RGB/000077500000000000000000000000001272363601300310365ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/RGB/RGB.pde000066400000000000000000000026451272363601300321510ustar00rootroot00000000000000import processing.serial.*; import cc.arduino.*; Arduino meinArduino; int potPin=0; int value; int redX = 20; int greenX = 20; int blueX = 20; int redPin = 9; int greenPin = 10; int bluePin = 11; int oldGreen; int oldRed; int oldBlue; void setup() { size(255,300,P3D); println(Serial.list()); // List all the available serial ports: meinArduino = new Arduino(this, Arduino.list()[4], 57600); noStroke(); } void graphics(int theValue){ colorMode(RGB,255); background(0); fill(100); rect(0,20,width,20); rect(0,60,width,20); rect(0,100,width,20); fill(255,0,0); rect(0,20,redX,20); fill(0,255,0); rect(0,60,greenX,20); fill(0,0,255); rect(0,100,blueX,20); colorMode(HSB, 100); fill(value,100,100); rect(0,160,width,height); } void draw(){ value=meinArduino.analogRead(potPin); value=(int)map(value,0,1023,0,100); if (mousePressed==true){ if ((mouseX>0)&&(mouseX<=width)){ if ((mouseY>20)&&(mouseY<40)) { redX = mouseX; } if ((mouseY>60)&&(mouseY<80)) { greenX = mouseX; } if ((mouseY>100)&&(mouseY<120)) { blueX = mouseX; } } } graphics(value); if (redX!=oldRed){ meinArduino.analogWrite(redPin,redX); oldRed=redX; } if (greenX!=oldGreen){ meinArduino.analogWrite(greenPin,greenX); oldGreen=greenX; } if (blueX!=oldBlue){ meinArduino.analogWrite(bluePin,blueX); oldBlue=blueX; } } 000077500000000000000000000000001272363601300332125ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurusTwitterSaurus.pde000066400000000000000000000144721272363601300365610ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurusimport processing.serial.*; // Einbinden der Serial Programmbibliothek import cc.arduino.*; // Einbinden der Arduino Programmbibliothek import ddf.minim.*; // Einbinden der Minim Programmbibliothek für die Soundausgabe /* TwitterSaurus Dieses Programm sucht nach einem Suchbegriff auf Twitter. Wenn ein neuer Beitrag geschrieben wird, wird ein Sound ausgelöst und ein Servo dreht sich. Er bewegt das Maul des Dinosauriers des Fritzing Creator Kits. Dieses Beispiel aus dem Fritzing Creator Kit: www.fritzing.org/creator-kit. */ String myQuery="Dino"; // Begriff, nach dem gesucht werden soll Twitter twitter; // Erzeugt ein Twitter-Objekt Query query; // Erzeugt ein Query-Objekt Arduino arduino; // Erzeugt ein Arduino-Objekt Minim minim; // Erzeugt ein Minim-Objekt AudioSample sound1; // Erzeugt ein AudioSample-Objekt PFont font; // Erzeugt ein PFont-Objekt long timer; // Timer wird angelegt int timeout=1000; // Timeout – wie oft soll geprüft werden? String lastMessage=""; // speichert die letzte Twittermeldung String currentMessage=""; // speichert die aktuelle Twittermeldung TwitterFactory twitterFactory; // Erstelle ein Twitter-Factory Objekt int servoPin=10; // Servo an Arduino-Pin void setup(){ size(400,200); // legt die Größe des Programmfensters fest ConfigurationBuilder cb = new ConfigurationBuilder(); // ConfigurationBuilder für Twitter cb.setOAuthConsumerKey("uzr17kGYqhgNDjTBNQd1qA"); // Hier muss Dein ConsumerKey eingegeben werden cb.setOAuthConsumerSecret("ZZUfCHruwv4d6Tn9uGz0UGebxfn4oQDikv3NeCbd14"); // Hier muss Dein ConsumerSecret eingegeben werden cb.setOAuthAccessToken("221060254-8MJIyVpXotDhemKaJKVN88L1FlCcndToB8y143LU"); // Hier muss Dein AccessToken eingegeben werden cb.setOAuthAccessTokenSecret("A2R7IkcSniMnBHYeRK02umHoIvrsHQAdx4NaMq6toY"); // Hier muss Dein AccessTokenSecret eingegeben werden twitterFactory = new TwitterFactory(cb.build()); // öffnet eine Verbindung zu Twitter twitter = twitterFactory.getInstance(); minim = new Minim(this); // Minim (Bibliothek für die Soundausgabe) wird initialisiert sound1 = minim.loadSample("sound1.wav"); // Sounddatei wird geladen font = loadFont("OCRAStd-14.vlw"); // Font-Datei wird geladen textFont(font); // Font wird initialisiert println(Arduino.list()); // Alle seriellen Geräte werden in einer Liste ausgegeben, die Nummer für das Arduino muss arduino = new Arduino(this, Arduino.list()[4]); // hier übergeben werden: Arduino.list()[nummer] arduino.pinMode(servoPin, Arduino.OUTPUT); // Servo Pin wird im Arduino als Output festgelegt } void alertMe(){ // Methode wird aufgerufen, wenn es eine neue Nachricht gibt sound1.trigger(); // Soundsample wird abgespielt arduino.analogWrite(servoPin, 120); // Servo wird gedreht delay(500); // warten arduino.analogWrite(servoPin, 20); // Servo wird gedreht delay(500); // warten } void twitterConnect(){ // Methode für die Verbindung zu Twitter try { // versuche Query query = new Query(myQuery); QueryResult result = twitter.search(query); for (Status status : result.getTweets()) { println("@" + status.getUser().getScreenName() + ":" + status.getText()); currentMessage="@" + status.getUser().getScreenName() + ":" + status.getText(); } } catch (TwitterException e) { // bei Fehler println("Couldn't connect: " + e); // Fehlermeldung wird ausgegeben }; } void draw(){ // draw Methode ist wie loop in Arduino if (millis()>timer+timeout){ // wenn wartezeit vorüber twitterConnect(); // Methodenaufruf timer=millis(); // timer wird neu gestartet } if (currentMessage.equals(lastMessage) == false) { // wenn aktuelle Nachricht anders als letzte Nachricht ist background(0); // Hindergrund füllen text(currentMessage, 10, 20,width-20,height-40); // Tweet als Text ausgeben alertMe(); // Methodenaufruf lastMessage=currentMessage; // letzteNachricht auf aktuelle Nachricht senden } } 000077500000000000000000000000001272363601300341245ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurus/code000077500000000000000000000000001272363601300341235ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurus/dataOCRAStd-14.vlw000066400000000000000000000752661272363601300363160ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurus/data8  !  " #  $  %  &  ' (  )  *  + , - . /  0  1  2  3  4  5  6  7  8  9  :  ;  < = > ?  @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^ _ ` a b  c d  e f  g  h  i  j k  l  m n o p  q  r s t  u v w x y  z {  | }  ~                                                                                                                                                                        1 9:=  >  A B  CD  GH  PQ  R  S T U  X Y  Z[  ^ _ `  a  b  c d e  no  pq  x  yz  {|  }  ~                                 !   "  &  0 9   :   D   !" "  "  " "  " " "+ "H "`  "d "e %      -22 v gH27(2-T (gnN7#2#gNBvv-ZT}(g`7}2 TT}#2(gg2N``T}}#TTZTZ 7722-g27Bnv`n``7#g #N`=7gZ(}g2Z(}#vnn T2((v=-2N#n(gZn} g g g `Z`Z7#n g ggHg#7ZHT``}(g(Z`2BN`nH``=`nZg7(v g`222#7 B7`g`Z77777H22-(HvT#(H} }NH7-22222222222-27Hgggg g g g7 `(2 `-2Z#2gѾ2(g7-2-2Z``Z2 -g7-2-2g`Z27#T2-2-2g7T gN g n g g gg -B 7===N` g`2-2ѣ2-2-2Z`B72N2-2#-27T=222227===n2(22-22#(-272--HN2 v g g`(22#Z-#Z}T2N2-2-27N(2(222=22222-22(27B=H72B7n#H722#7 B7`g`n=HvnB-77g7v 7(B====B-((((-7(BBBBBH nB #nN= vngn(HnHB`Z7g27#Z#72-`-}#` Hv=-22`(2n`(2g`(2g `(2g `(2gZZ(gN}љN#} `7 v=ggg(((B772``g-(2N7#B--7v22H- g-(}72-22g222-222#2----g -`g(n---vZ72727=n2(2(g(-2222(2``Z22N2-22222(--gѣg g g g g gggѣ-gg g g g2 g n g g g ggn2 =(n# Zљ }`#B(-g2-}22BB2T(22222222222(-Z#-22H2222g--2-2Z2(22=22T#v==Z2-2v-##--T`-222272#B}22n727=2222(-22Ѱ2=g-g(-2-}Z =}`#(-7}2vH2-Tn(7# 22nv` # g g g g g g g`(-2222222222222222}(`(-22-2`-2`7Z`-7=Z(-2222}7-2 g(2 `(2 `(2 `(2n22(v#-}N# ZvB7BZTB}}H##((-22g #ZT}- ` g g g`2g#=} vH##Nv }--`ggggg`#(BTv#-}H Nv(#(-`2-v22v22v22v22v22v2-v2`2-v=H gZ Z}gN=vg````````T7-g#7vn}ggg} gя-nggggvZg-g-gnTgZцv gvgggn } 77-BBng 2g 2nBH} = =nNgnZZvgg2 HцT 772#n7Ѿnggggg`Bg Bgg -ggnn# 7=22nv-nggHBg ggg ggTg- g2Z(g`nn Zvgg g g`n7T}Tn ZN=gg g g g g gg nH#n-nggZg2Hgn7ggvg(g ZZ (7=n2gg g g g g`n7TTѰggg=ggZ`gZBg`( g#`-ZT#Z2Bg ggg ggTg- g2Z(BBngg -g -g`HHTg=gggg 7g-gTgngggNHn Bng 2g 2gBHя-2# ZZgg=7g(=g ggZT2 77#vTg v`#22ZHB2--222222- ц`TZ( g2g2g2g2g B =}BZ( g 7`- }g(7(Z(g 2gn2- g`Zg(gZ=n}g(T B T# }-B=v(N(Z H T( g 7`2 }n#2NBTN=Ѿ vg `HnZ n- ѣ gg=`}`H2gg g ѣn-}H n g g g g g g g g g g g g n--gngg`g7 `ggn-`(2 g7N==v77(2# g g 22-`` }}Z7Ѱv2-2T#(--HZH2gTB-(gg=g`ng7 nH g-7 =gn==N7--27B`` (-}T# `nH7-Z-77(BN7Bg g`}H n g g g nn= n g g g g n- gvB((#(vN(2NZvN}Tn}gTn#-77v`}Tn}}=T}`n`ngv g72 g  gngnTZNgnv}`} }-`2Z2#v=27 }NT(TZN gB7v7` BH(g#`7- 2gg````vgg g nZ77777}Tn=` n-g2g#g27`g2g2-g2gg7#Z` H(777= 7v(vn `g `gZgn}-`gnѣ#( g nB#-Z`--(ZBZZgggngљTN-`-2 -2 22222222222222}#- }-2T`72#g n g Ng}H }NTZH#TT#NZ` Hg -Z}v7TT`B}7n}v- 7v7}gZZBN}v `7nT2- }N2 nH#v=-TH n-}--- }N2 nH#v=-(vg( 2Z g-2- }N2 nH#v=-ng(7N(2- }N2 nH#v=-#-77gvv7 2- }N2 nH#v=-=яnn}ng-vѣ`}`=}=ggg((2--T-`#7nN22T(-2#-B--7v22H-gNNѾvN}v T-`}nv#Z`(n-THn#`}nv#Z`(n-(vg(2T`}vn#Z`(n--Tnn22Z#`}}#Z`(n-N}v T-Z}nvv#g g g gggѣ-THn#ZnvvѾ#g g g gggѣ-(vg(2TZ}nnv#g g g gggѣ--Tnn22Z#Zv}v#g g g gggѣ-ng(7(#(22}22ѣT(222-2B-N}v T7(}-n`n`B7nZ# gn7ZTH n-N`-n`n`B7nZ# gn7Z(vg( 2T-v`-n`n`B7nZ# gn7Zng(72((-n`n`B7nZ# gn7Z#-27gg-n(n`n`B7nZ# gn7Zg=N(`77v-27Z-=TH(###}nB} 2=}(2n}#Z((}ZN}v `7Z`2722222222-2-`TH}-Z` 2722222222-2-`(vg(=`Z}2722222222-2-`-Z--`T H-722222222-2-`TH}-Z` 27N-#n}2`g g`#=7(vZ Ѱ-Z##Zg#`B gn gv g  g==Z-g#7vn}ggg} gя`--(TB#Ѿ7vn}ggg} gя7`#v -HѾ2vn}ggg} gяn2( `#BѰ2vn}ggg} gя#-77n`Ѱ2vn}ggg} gя7 ng7n- `2}--- ZTg gяцnv-`TѰNvn} BяZ љv gvgggn } 72#-g=я=nNgnZZvgg2 HцT `--(TB==nNgnZZvgg2 HцT 7`#v -N7ц=nNgnZZvgg2 HцT #-77g`7ц =nNgnZZvgg2 HцT nvѾTvgg g g`n7T-цgTvgg g g`n7TnZ #HT Nvgg g g`n7TTT nn72Tvgg g g`n7Tn2(` #BT2ѾBg ggg ggTg- g2Z(-gBяBngg -g -g`HH`--(TBBBngg -g -g`HH7`#v -N=ѾBngg -g -g`HHn2( Z#=7цBngg -g -g`HH#-77g`7цBngg -g -g`HH2v7ZTNNT`7 -(-2 Bя}B`n ggZ(`T` -gZ( g2g2g2g2g B =}B`--(ZBZ( g2g2g2g2g B =}B7`#v -NT g2g2g2g2g B =}B#-77n`Z g2g2g2g2g B =}B`--(ZBT( g 7`2 }n#2NBTN=#-77n`Z g 7`2 }n#2NBTN=  ѰѰ}} 2#v`(N}g=N}TB gnnHvZ2-}-2g22g=2g 2HH2=Ѿ2#v`(N}g=N}TB} g #7g( HѰvZ2-}-2g22g=2g 2HH-7gTnT-7Hn=TB Z-N#n( 2ZT-g `#2=ZT#`2#( =(2--#2HHN2#N(N n-2v(g-=7B7Z-N#n( 2ZT-g `#2=ZT#`2#( =7(-2HHN2#N(N n-2v(g-B`T7vZ-v2}T2-}(22n22g2222n22n2-g22}T-n}n(# = N 2722-2=2`Z2#-2T22g =2n2=B2vB2-2}N2-`-22nBBg7TNv--n2-`72}T- = 7Z`(n7=-ZZ}22B 2g 2g22B2n#2 NTTH ` g n  n } g ` NNTT `2=(`vN(g7-B7T`NNNT-HH72 #(-TTBBgg NTTH ` g n  n } g ` TZZT  g#g(n`vN(g7-B7T`NNNT-HN72  Zvgg g g`n7Tg}ZZZZ7 ѾHvHn7=22222222222222-2n=#-nHg2 2`}ZZZZ=7= n7=22-2Z#2gv2=B2gZ2-2-2n=#-n-(22222-=-`(2 NHg2--2g2(7=n2g}TTNT` =g`n7T-v#nn` 2g 2g- 2` 2g22g# 2g`T2g 2gN2g2` 72Nn=gTH#`N2n2222=22222222222222----HZN=n``# 2g 2g- 2` 2g22g# 2g`T2g 2gN2g2` 72-(BH##`N2n2222=22222222222222----  Z=(NTN(- (2N=T7gv`(=H(2# ((  T2(T#BB- NT`-BZ ggn-`(-2(T-n-=}---`27v(-N#-2v-T-nљ7vg `g TѰg g} BяZ#22ѣ2}N2-v22g2-22N2(22=22B2- (gTn(gg2g7ggggg=(v22ѣ2}N2-v22g2-22N2(22=22B2- }`(n`Tv(gg2g7gggggNnHZ7Z2n77= n#}#v2(2--(B((Nn=nTT#NTBvT v7-v#2HZ##2n77= g#}#v2(2---H7nn-#NTBvT v2(v#2HT`2-B}T }HvvvB-v#-}(NT##7# я-Zv22T2 77#vTg v` nvvvv`vg-(ѣ72 }B`((TZHZN=n` nvvvv`BN72=n -}H`(((77 vn` 2g 2g 2g 2g 2g 2g 2g 27#(72g2 ((-#77 (#2222222222722 22}=2=-TvngHv-n` 2g 2g 2g 2g 2g 2g 2g 27#(72g2 ((}`T B-}Hg(2222222222722 22}=2=--Tnn22Z-Zg27N-#n}2`g g`-v#vn22ZTTT--``27 ZNZZ``27n#}v( }vn =N}`ngNZTZ``222ZTTT--``27 ZNZZ``2( }vn =N}`ngNZTZ``2B}T }HZ}vn#vѾ#Tg7=n-я-Zv22  vg `HnZ B-(gg=g`ng7 nH 7TvZvNѣ 7`#} -Nя-Zv-2 =HZTNT=g`nn Bng2nZ}2-}2Nng}H}n2( g #H=(#}((( #-N=T=nn2H`#g=H` TZ `g--g22v22Ѱ22-Z22-- ZZZZ`B7B`ZZZZZZ``N =n-vgZ ggggn--n ` ggggg7H--}v2-#(Z72vvBvZ-(2-2}2=7N} HZ 2- 2(2}2B2Z#n2ngѣZ` g gnH2ngѣZ`T-nH Z72272=gn`- Z`(  7` `n(TH7}=}}=}2v(`7(gnn}-=} 2}7n vTn-=7(7v2BT `B}2nN#Zg22 #gH(}-# (TnZ(TT( g2TZ-2Ng2 2v72nZ`N-gZgZ(gN}љN( H7 v=B(( }Z--222222222222222227NTvѣgN}=HgBggZT =vnnvBN-HH(n } gvZ722=BZ7gg}   T nvѰ N TT`gg g T-22g v`v(#B Z`#}gZ(`( #7nZ-g# B7(BNBB(`#-7#g=BH `7=}v} vn#T-B Tv`B v#BNB2`- Zv2#n7(7=(v(}#-gNvn ZBv`H=v#==g  -Zggg gg gg gg g nH#=7=gn g-TgBggg gg gg gg g nHOCRAStdOCRAStdsound1.wav000066400000000000000000000240201272363601300360510ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurus/dataRIFF(WAVEfmt MMdata'~~zzx|sx{z~||}vz{~~uotswxvx~zw|~}|tkhiqzuxz|uejmtsfdokjhs{trnigccdsvoh_ebcbesoZYb^Xbjt}rlfcUSS^by{~{vm^UQIS^nq|{iXTHJRfio{of[JMMQfdn||oeUFGQWZi{~td[IDHRfjt~r_UMHKR]hnxqcWJ?IZbir}tgI;DR^^fsû~wdSDDQVXdn[J>LY^aenrYCADRaZh~Ƶvh_QMDPW_crlXI:ELXcd{{fM>BLZZ_ksŹkL?DLMSdtÿt\JFMSYV_lzq[L5CT[bjo{ǿubH?FOTZi{þztXIAPOValy~oROHKL]dfsbOBEKTYju~obHGV\LZclyõt_NDEHT\m{~m\H>JTYlrv~wqaPMTRVZ_woG1BIVfqwȿ~|zlZNLMOR`j{]H9HK]^iyxh[IKKY^\guoWDADNXix}mbVJNSP\fnzn\MHBTaekkunV?OUL[aguxbOHQH[dioxŶhSGHHSVfoq^X[USIT^ktƹwlZOF>O_pt||o_UTKMSdlktv]N>DK[djs{wi_MELU^hkr÷|hSEJGZ^iu}lN=9GXfjwbRQOMUV_`u½nW<9?Nddnwƹ|cNDBP\ZdqĴlcKAHOOUk~re_OCIWW]kxmUOFROTXtzeaS?ALSftjQINOQWbbn{ƽ~hZNDDKQbhrŶ~_KEJLTUeju}{fV?EKKQfo{j^JHJHTXfy{nYOEMRNXix{u]HKGJTeqzpJ:BLWbtrxŶ|t\KGKIR^qpVJ?HVahho~þ|rogO8L[ajgitze??GKYdmvvhMDJZ\_hcn|dL?CTV`fu~ǾzeQIDHKYfwf[TQKO^_dtgLIFMRegn~~r]ZVRORXks~s^K>FL\ivxljdZVJSXbgw¶xhXEA?Tgjy}wee^OJBZ^mtvƶyvfNHCFZbhv|kaF:G[aegujOH>L]Zdri^LFFY^^blw]I@?GYfn{¶zjZSLEK[^ktyiQ=DNX^_jzƷyhH8EGN_jwIJwrfYMBGZX^lrXF:?L_iev~žzo\FKEHRZfw~tm\LDEEZbq|x_QGCJW_hnsĿxsYFAJQUjorgREBTX^iqwsfUKMR[Z^i{y]F9F]_cgo¶~kZRGOMTZivr_KDGMXejtxycOFKJK^iv|jWKKOZSesq~tVEILNUcqld[NHLTant~zwnYCCNR_grxrdICNSY]sxvfVJQQ\bei~|lXJHNZhw}lbNDHMTbbm{þ¾v\G?JQY_t{hVDFTdnoleciؿoUE0-8K_qtshabvɭ|[?2,.>Wjvxnedm޿lL4($.Jet}ymefvѬ|X/$,E[mz}wkbq߼d?&"'6Ogw|oijϮrL+"-1A]tvljw~[?+#$9Ul{}tko̪nI.$".E_w|qkv㽞~c8#:UnqfmάoF(&Dc|zlft㽘~\52Trrhlө}~tL%&Fd|~nes⼚{]84Usuko~ͨ|~vJ)'Bcz~qkv澜{a<$3Rowhhҫ~|uN++D\y~nitƢz_>*-Kjyoqܴ|{vU4':Yuxqtȣ~eB*!#-Jh~{rtڶ|_;&!:VoynrͪtL*'Ca}uls߻~[:$'6QlzosӮqO4&!.C]uxv|äfJ-$%5Jfztr~ײxV1!)D[rytrƦc:$)8Hdz~srܹ~xO4$$(9Sn|rv̪~fD,""*Fbzrmݸ{|T7$"6Vttlsɦ|jD&#.Fd~yoo⺘||X8'$7TswntΩlG,*Ddz{nmz޹^86UosknѪtI%*B]x~rkzğ~^<$-MlxmqկvR8$;YtxrvȦlG(%+?]ytr|侜}}Y;!"/QnxquϫiD*#.B_w~stܾ|V8((39Jdwzqz׷jI4)'),#%1Jh|}swشtS9%,:Wqxuy˫kF0#"*B`ysn}޼yS5%!%7SlwoyέlH-$/?[uxv|ã~{[;,&!,Ng~|vwزsS8!$6Vpyv}ƤiI+-Id{zv~ڷ|\:&%6Uo{tqͩlH*.Dbxus}ܻ~\<("(7Nh~{uvҳrO3%".?Voxu}ǦbB*$1Mgw}trطvS6""/=Xoxv}̮hI,%&2E^vxt~ܻ|Y<*(+6Mh{yrxԲrS4#/BYpxu~˪gE-"$4Hdxvvۼ~uY;.'*7Tl|x~ϬqR8'!/B_szx}¤cD.(#1Kh~|urճoS9&$,=Wryx}̪fH/$$,Dby~xx޾y\=)"'3Lh~|z|Բ|lO8,**8Ws{y˧}eG/$$/Hdy||غ~vY9(()6Smxx̬}gG2&)Faxxv߻}|a>)$%1Mj~wvײ{xW8("(7Wt|svͩ~}jK6*&,B_zwx߿|cH0'%4Ri~}zѱxZ6#$3I\o~{z}ũjF,&.5Khz{xڶ~]='!,?Wo|urϮztM2%#*D^ywr|ägG,&(1Fe}~wwܸ~xY<(#%6Wt{ux̥~mO0#$1Cbyxxž~dD.!"6Lh|xyն|z]<&"-Pf{||պzbH1-4?Vn|ҳ~z^A.*2G]r{~ƫoT;-*7Kex}{ֺ|eI4.5FZfx}̷v[F84:JZm|~ȱs]D62;N]s~~ªnV>45DXht~ʽoWB;>JZiv}õ|lXIBDK[kx}~ûxl\OHLTany}}vndZUV]gqx}~wqjebchnty|}|vokhimsz~ztnjjlry~ytnllqw|}xtomnrw|~|xsooosy}|yusrtvy~~{xvvwy|~|zxxz{~}|zz{~~||}~~~~000077500000000000000000000000001272363601300332745ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurus2TwitterSaurus2.pde000066400000000000000000000060561272363601300367240ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-de/Processing/TwitterSaurus2/* Just a simple Processing and Twitter thingy majiggy RobotGrrl.com Code licensed under: CC-BY */ // First step is to register your Twitter application at dev.twitter.com // Once registered, you will have the info for the OAuth tokens // You can get the Access token info by clicking on the button on the // right on your twitter app's page // Good luck, and have fun! // This is where you enter your Oauth info static String OAuthConsumerKey = "uzr17kGYqhgNDjTBNQd1qA"; static String OAuthConsumerSecret = "ZZUfCHruwv4d6Tn9uGz0UGebxfn4oQDikv3NeCbd14"; // This is where you enter your Access Token info static String AccessToken = "221060254-8MJIyVpXotDhemKaJKVN88L1FlCcndToB8y143LU"; static String AccessTokenSecret = "A2R7IkcSniMnBHYeRK02umHoIvrsHQAdx4NaMq6toY"; // Just some random variables kicking around String myTimeline; java.util.List statuses = null; User[] friends; TwitterFactory twitterFactory; //Twitter twitter; Twitter twitter = TwitterFactory.getSingleton(); RequestToken requestToken; String[] theSearchTweets = new String[11]; void setup() { size(100,100); background(0); connectTwitter(); //sendTweet("Hey from Simple Processing woop woop #loadedsith #robotgirl"); //getTimeline(); getSearchTweets(); } void draw() { background(0); } // Initial connection void connectTwitter() { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setOAuthConsumerKey(OAuthConsumerKey); cb.setOAuthConsumerSecret( OAuthConsumerSecret ); cb.setOAuthAccessToken( AccessToken); cb.setOAuthAccessTokenSecret( AccessTokenSecret ); twitterFactory = new TwitterFactory(cb.build()); twitter = twitterFactory.getInstance(); println("connected"); } // Sending a tweet void sendTweet(String t) { try { Status status = twitter.updateStatus(t); println("Successfully updated the status to [" + status.getText() + "]."); } catch(TwitterException e) { println("Send tweet: " + e + " Status code: " + e.getStatusCode()); } } // Loading up the access token private static AccessToken loadAccessToken(){ return new AccessToken(AccessToken, AccessTokenSecret); } // Get your tweets void getTimeline() { try { statuses = twitter.getUserTimeline(); } catch(TwitterException e) { println("Get timeline: " + e + " Status code: " + e.getStatusCode()); } for(int i=0; ischaltgrenze){ // wenn die Schaltgrenze unterschritten wird timer=millis(); // timer wird auf aktuelle Zeit gesetzt digitalWrite(scheinwerferPin, HIGH); // Scheinwerfer werden eingeschalten } else if (millis()>wartezeit+timer){ // sonst wenn er Timer abgelaufen ist digitalWrite(scheinwerferPin, LOW); // werden die Scheinwerfer abgeschalten } delay(10); // Pause } 000077500000000000000000000000001272363601300331665ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/BlinkBlink.ino000066400000000000000000000012671272363601300347420ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Blink/* Blink Switching a LED on and off This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // integer variable led is declared void setup() { // the setup() method is executed only once pinMode(led, OUTPUT); // the led PIN is declared as digital output } void loop() { // the loop() method is repeated digitalWrite(led, HIGH); // switching on the led delay(1000); // stopping the program for 1000 milliseconds digitalWrite(led, LOW); // switching off the led delay(1000); // stopping the program for 1000 milliseconds } 000077500000000000000000000000001272363601300345475ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/ChaserlightsChaserlights.ino000066400000000000000000000021501272363601300376740ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Chaserlights/* chaserlights This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int pinsCount=8; // declaring the integer variable pinsCount int pins[] = {2,3,4,5,6,7,8,9}; // declaring the array pins[] void setup() { for (int i=0; i0; i=i-1){ // chasing left (except the outer leds) digitalWrite(pins[i], HIGH); // switching the LED at index i on delay(100); // stopping the program for 100 milliseconds digitalWrite(pins[i], LOW); // switching the LED at index i off } } 000077500000000000000000000000001272363601300341775ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise1_1Exercise1_1.ino000066400000000000000000000023651272363601300367640ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise1_1/* Exercise 1_1 Blink Rhythm This program is blinking an LED a rhythm. This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // integer variable led is declared void setup() { // the setup() method is executed only once pinMode(led, OUTPUT); // the led PIN is declared as digital output } void loop() { // the loop() method is repeated digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(1000); // stopping the program for 1000 milliseconds digitalWrite(led, LOW); // switching off the led delay(1000); // stopping the program for 1000 milliseconds } 000077500000000000000000000000001272363601300343415ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise1_2aExercise1_2a.ino000066400000000000000000000056741272363601300372760ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise1_2a/* Exercise 1_2 Blink SOS This program blinks SOS in Morse code with an LED: * short-short-short = S * long-long-long = O * short-short-short = S This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // integer variable led is declared void setup() { // the setup() method is executed only once pinMode(led, OUTPUT); // the led PIN is declared as digital output } void loop() { // the loop() method is repeated digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(500); // stopping the program for 500 milliseconds digitalWrite(led, LOW); // switching off the led delay(500); // stopping the program for 500 milliseconds digitalWrite(led, HIGH); // switching on the led delay(500); // stopping the program for 500 milliseconds digitalWrite(led, LOW); // switching off the led delay(500); // stopping the program for 500 milliseconds digitalWrite(led, HIGH); // switching on the led delay(500); // stopping the program for 500 milliseconds digitalWrite(led, LOW); // switching off the led delay(500); // stopping the program for 500 milliseconds digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, HIGH); // switching on the led delay(200); // stopping the program for 200 milliseconds digitalWrite(led, LOW); // switching off the led delay(2000); // stopping the program for 2000 milliseconds to have a longer break } 000077500000000000000000000000001272363601300343425ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise1_2bExercise1_2b.ino000066400000000000000000000027051272363601300372700ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise1_2b/* Exercise 1_2 Blink SOS an alternative way This program blinks SOS in Morse code with an LED: * short-short-short = S * long-long-long = O * short-short-short = S This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int led = 13; // integer variable led is declared char signals[] = {'.','.','.','-','-','-','.','.','.'}; // variable storing the signals: . = short, - = long void setup() { // the setup() method is executed only once pinMode(led, OUTPUT); // the led PIN is declared as digital output } void loop() { // the loop() method is repeated for (int i=0; i=1; i=i-1){ // chasing left (except the outer leds) digitalWrite(pins[i], HIGH); // switching the LED at position i on digitalWrite(pins[7-i], HIGH); // switching the LED at position 7-i on delay(100); // stopping the program for 100 milliseconds digitalWrite(pins[i], LOW); // switching the LED at position i off digitalWrite(pins[7-i], LOW); // switching the LED at position 7-i off } } 000077500000000000000000000000001272363601300342015ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise3_1Exercise3_1.ino000066400000000000000000000034261272363601300367670ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise3_1/* Matrix Exercise LED-Matrix displaying a different image This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // pin order for Arduino UNO int rowPins[8] = {9,3,2,12,15,11,7,6}; // matrix rows connected to the Arduino int colPins[8] = {13,8,17,10,5,16,4,14}; // matrix columns connected to the Arduino // pin order for Arduino MEGA // int rowPins[8] = {9,3,2,12,55,11,7,6}; // matrix rows connected to the Arduino // int colPins[8] = {13,8,57,10,5,56,4,54}; // matrix columns connected to the Arduino int image[8][8]={ // the image shown on the matrix : 1 = LED on, 0 = LED off {0,0,1,1,0,1,1,0}, {0,1,0,0,1,0,0,1}, {0,1,0,0,0,0,0,1}, {0,0,1,0,0,0,1,0}, {0,0,0,1,0,1,0,0}, {0,0,0,0,1,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0} }; void setup(){ for (int i=0; i<8; i++){ // all pins are declared as outputs pinMode(rowPins[i],OUTPUT); pinMode(colPins[i],OUTPUT); } } void loop(){ for (int y=0; y<8; y++){ // rowwise for (int x=0; x<8; x++){ // check all entries of the array from left to right if (image[x][y]==1){ // is the entry 1 digitalWrite(colPins[x],HIGH); // switch on column pin } else { // else digitalWrite(colPins[x],LOW); // switch it off } } digitalWrite(rowPins[y],LOW); // switch the row pin to LOW (because it is the cathode of the LED; LOW means ON) delayMicroseconds(100); // stop the program for 100 milliseconds digitalWrite(colPins[y],HIGH); // switch the row pin to HIGH (this means OFF) } } 000077500000000000000000000000001272363601300342025ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise3_2Exercise3_2.ino000066400000000000000000000051471272363601300367730ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Exercise3_2/* Matrix LED-Matrix displaying a little animation This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // pin order for Arduino UNO int rowPins[8] = {9,3,2,12,15,11,7,6}; // matrix rows connected to the Arduino int colPins[8] = {13,8,17,10,5,16,4,14}; // matrix columns connected to the Arduino // pin order for Arduino MEGA // int rowPins[8] = {9,3,2,12,55,11,7,6}; // matrix rows connected to the Arduino // int colPins[8] = {13,8,57,10,5,56,4,54}; // matrix columns connected to the Arduino int imageNr=0; // stores what image is currently displayed long timer=0; // timer variable int timeOut=200; // time an image should be displayed (ms) int image[2][8][8]={{ // the first image displayed on the matrix {0,1,0,0,0,0,1,0}, {1,1,1,0,0,1,1,1}, {0,1,0,0,0,0,1,0}, {0,0,0,1,1,0,0,0}, {0,0,0,1,1,0,0,0}, {1,0,0,0,0,0,0,1}, {0,1,1,1,1,1,1,0}, {0,0,1,1,1,1,0,0} },{ // the second image displayed on the matrix {1,0,1,0,0,1,0,1}, {0,1,0,0,0,0,1,0}, {1,0,1,0,0,1,0,1}, {0,0,0,1,1,0,0,0}, {0,0,0,1,1,0,0,0}, {1,0,0,0,0,0,0,1}, {0,1,1,1,1,1,1,0}, {0,0,1,1,1,1,0,0} } }; void setup(){ for (int i=0; i<8; i++){ // all pins are declared as outputs pinMode(rowPins[i],OUTPUT); pinMode(colPins[i],OUTPUT); } } void loop(){ if (timer+timeOut= 0; fadeValue -= 5) { // fadeValue is counted down in steps of five analogWrite(led1Pin, fadeValue); // and sent to the led as an analog value analogWrite(led2Pin, 255-fadeValue); // 255-fadeValue produces the opposite fading behavior delay(2); // short stop } } 000077500000000000000000000000001272363601300333175ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/FadingFading.ino000066400000000000000000000017251272363601300352230ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Fading/* Fading fading an LED slowly on and off again This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int ledPin = 9; // ledPin is declared void setup(){ // no need to declare the analog output in the setup } void loop(){ for(int fadeValue = 0; fadeValue <= 255; fadeValue += 5) { // fadeValue is counted up in steps of five analogWrite(ledPin, fadeValue); // and sent to the led as an analog value delay(2); // short stop } for(int fadeValue = 255; fadeValue >= 0; fadeValue -= 5) { // fadeValue is counted down in steps of five analogWrite(ledPin, fadeValue); // and sent to the led as an analog value delay(2); // short stop } } 000077500000000000000000000000001272363601300336425ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/FlipFlopFlipFlop.ino000066400000000000000000000020261272363601300360640ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/FlipFlop/* Flip Flop switching between two LEDs using a button This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int buttonPin=2; // Pin where the button is attached int greenLED=9; // Pin where the green LED is attached int redLED=10; // Pin where the red LED is attached void setup(){ pinMode(buttonPin, INPUT); // button is initialised as INPUT pinMode(greenLED, OUTPUT); // green LED pin is initialised as OUTPUT pinMode(redLED, OUTPUT); // red LED pin is initialised as OUTPUT } void loop(){ if (digitalRead(buttonPin)==LOW){ // if the button is pressed (LOW) digitalWrite(greenLED, HIGH); // switch on green LED digitalWrite(redLED, LOW); // switch off red LED } else { // else (if button is not pressed) digitalWrite(greenLED, LOW); // switch off green LED digitalWrite(redLED, HIGH); // switch on red LED } } 000077500000000000000000000000001272363601300347615ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/FortunetellerFortuneteller.ino000066400000000000000000000021651272363601300403260ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Fortuneteller/* fortune teller switching between five LEDs to tell the future This example is part of the Fritzing Creator Kit: www.fritzing.org/creatorkit. */ int pins[] = {8,9,10,11,12}; // the pins of the LEDs are stored in an array int tiltPin = 3; // pin number where the tilt switch is attached void setup() { for (int i=0; i<5; i=i+1){ pinMode(pins[i] , OUTPUT); // LED pins are declared as outputs } pinMode(tiltPin,INPUT); // pin of the tilt switch is declared as input randomSeed(analogRead(0)); // initialize random number generator based on random state of pin zero } void loop() { if (digitalRead(tiltPin)==HIGH){ // when the tilt switch is open for (int i=0; i<5; i=i+1){ // all the LEDs digitalWrite(pins[i],LOW); // get switched off } // int myRandom=random(0,5); // a random LED digitalWrite(pins[myRandom],HIGH); // is switched on delay(20); // the program is waiting to breath } } 000077500000000000000000000000001272363601300333735ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/MatrixMatrix.ino000066400000000000000000000034171272363601300353530ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Matrix/* Matrix LED-Matrix displaying image This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // pin order for Arduino UNO int rowPins[8] = {9,3,2,12,15,11,7,6}; // matrix rows connected to the Arduino int colPins[8] = {13,8,17,10,5,16,4,14}; // matrix columns connected to the Arduino // pin order for Arduino MEGA // int rowPins[8] = {9,3,2,12,55,11,7,6}; // matrix rows connected to the Arduino // int colPins[8] = {13,8,57,10,5,56,4,54}; // matrix columns connected to the Arduino int image[8][8]={ // the image displayed on the matrix : 1 = LED on, 0 = LED off {0,1,0,0,0,0,1,0}, {1,1,1,0,0,1,1,1}, {0,1,0,0,0,0,1,0}, {0,0,0,1,1,0,0,0}, {0,0,0,1,1,0,0,0}, {1,0,0,0,0,0,0,1}, {0,1,1,1,1,1,1,0}, {0,0,1,1,1,1,0,0} }; void setup(){ for (int i=0; i<8; i++){ // all pins are declared as outputs pinMode(rowPins[i],OUTPUT); pinMode(colPins[i],OUTPUT); } } void loop(){ for (int y=0; y<8; y++){ // rowwise for (int x=0; x<8; x++){ // check all entries of the array from left to right if (image[x][y]==1){ // is the entry 1 digitalWrite(colPins[x],HIGH); // switch on column pin } else { // else digitalWrite(colPins[x],LOW); // switch it off } } digitalWrite(rowPins[y],LOW); // switch the row pin to LOW (because it is the cathode of the LED; LOW means ON) delayMicroseconds(100); // stop the program for 100 milliseconds digitalWrite(rowPins[y],HIGH); // switch the row pin to HIGH (this means OFF) } } 000077500000000000000000000000001272363601300332275ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/MotorMotor.ino000066400000000000000000000027561272363601300350500ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Motor/* Motor turning a motor clock then counter clock wise This example is part of the Fritzing Creator Kit: www.fritzing.org/creatorkit. */ int motor_A=6; // motor A Pin int motor_B=5; // motor B Pin int motor_Speed=3; // speed Pin void setup(){ pinMode(motor_A,OUTPUT); // pin A declared as OUTPUT pinMode(motor_B,OUTPUT); // pin B declared as OUTPUT } void loop(){ digitalWrite(motor_A,HIGH); // motor A pin switched to HIGH (+5V) digitalWrite(motor_B,LOW); // motor B pin switched to LOW (GND) for (int i=0; i<256; i+=5){ // count up to 255 in steps of five analogWrite(motor_Speed,i); // and write it as speed to the speed pin delay(20); // wait 20 ms } for (int i=255; i>0; i-=5){ // count down from 255 to 0 by 5 analogWrite(motor_Speed,i); // and write it as speed to the speed pin delay(20); // wait 20 ms } digitalWrite(motor_A,LOW); // motor A pin switched to HIGH (GND) digitalWrite(motor_B,HIGH); // motor B pin switched to LOW (+5V) for (int i=0; i<256; i+=5){ // count up to 255 in steps of five analogWrite(motor_Speed,i); // and write it as speed to the speed pin delay(20); // wait 20 ms } for (int i=255; i>0; i-=5){ // count down from 255 to 0 by 5 analogWrite(motor_Speed,i); // and write it as speed to the speed pin delay(20); // wait 20 ms } } 000077500000000000000000000000001272363601300342305ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/NightlightNightlight.ino000066400000000000000000000047161272363601300370500ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Nightlight/* night light fading through the colors of a rainbow This example is part of the Fritzing Creator Kit: www.fritzing.org/creatorkit. */ int LEDGreen=9; // LEDGreen pin declared int LEDBlue=10; // LEDBlue pin declared int LEDRed=11; // LEDRed pin declared void setup(){ pinMode(LEDRed,OUTPUT); // pin is output pinMode(LEDGreen,OUTPUT); // pin is output pinMode(LEDBlue,OUTPUT); // pin is output } void loop(){ for (int frame=0; frame<900; frame++){ // frame is counted up from 0 to 900 if (frame<150) { // if frame < 150 => red analogWrite(LEDRed,255); // LED switched on analogWrite(LEDBlue,0); // switch off LED analogWrite(LEDGreen,0); // switch off LED } else if (frame<300) { // if frame < 300 analogWrite(LEDRed,map(frame,150,300,255,0)); // fade off LED analogWrite(LEDBlue,map(frame,150,300,0,255)); // fade on LED analogWrite(LEDGreen,0); // switch off LED } else if (frame<450) { // if frame < 450 => blue analogWrite(LEDRed,0); // switch off LED analogWrite(LEDBlue,255); // switch on LED analogWrite(LEDGreen,0); // switch off LED } else if (frame<600) { // if frame < 600 analogWrite(LEDRed,0); // switch off LED analogWrite(LEDBlue,map(frame,450,600,255,0)); // fade off LED analogWrite(LEDGreen,map(frame,450,600,0,255)); // fade on LED } else if (frame<750) { // if frame < 750 => green analogWrite(LEDRed,0); // switch off LED analogWrite(LEDBlue,0); // switch off LED analogWrite(LEDGreen,255); // switch off LED } else if (frame<900) { // if frame < 900 analogWrite(LEDRed,map(frame,750,900,0,255)); // fade on LED analogWrite(LEDBlue,0); // switch off LED analogWrite(LEDGreen,map(frame,750,900,255,0)); // fade off LED } delay(10); // short break } } 000077500000000000000000000000001272363601300336505ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/PingPongPingPong.ino000066400000000000000000000206431272363601300361050ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/PingPong/* Ping Pong game LED-Matrix is used to play Ping Pong This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ // pin order for Arduino UNO int rowPins[8] = {9,3,2,12,15,11,7,6}; // matrix rows connected to the Arduino int colPins[8] = {13,8,17,10,5,16,4,14}; // matrix columns connected to the Arduino // pin order for Arduino MEGA // int rowPins[8] = {9,3,2,12,55,11,7,6}; // matrix rows connected to the Arduino // int colPins[8] = {13,8,57,10,5,56,4,54}; // matrix columns connected to the Arduino int pot1Pin=18; // declaring the pin for player 1's potentiometer int pot2Pin=19; // declaring the pin for player 2's potentiometer int image[8][8]={ // clear {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0} }; int death[8][8]={ // all on {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1}, {1,1,1,1,1,1,1,1} }; int death2[8][8]={ // skull {0,1,1,1,1,1,0,0}, {1,1,1,1,1,1,1,0}, {1,0,0,1,0,0,1,0}, {1,1,1,1,1,1,1,0}, {0,1,1,1,1,1,0,0}, {0,1,0,1,0,1,0,0}, {0,1,0,1,0,1,0,0}, {0,0,0,0,0,0,0,0} }; int blank[8][8]={ // all off {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0} }; long theTimer; // timer variable int gameState=0; // storing the game status int animations=300; // storing the duration of the images between the games float ballXSpeed = 1; // storing the x acceleration float ballXPosition=4; // storing the ball position as number float ballYSpeed = 0; // storing the y acceleration float ballYPosition=4; // storing the ball position as number int imageYPosition; // storing the image position as number int imageXPosition; // storing the image position as number int player1Position=3; // storing the paddle positoin of player 1 int player2Position=3; // storing the paddle positoin of player 2 int gameSpeed; // storing the current game speed void setup(){ for (int i=0; i<8; i++){ // all pins are outputs pinMode(rowPins[i],OUTPUT); pinMode(colPins[i],OUTPUT); } } void draw(){ for (int y=0; y<8; y++){ // rowwise for (int x=0; x<8; x++){ // from left to right, entries are checked if (image[x][y]==1){ // if entry equals 1 digitalWrite(colPins[x],HIGH); // the column pin is switched on } else { // else digitalWrite(colPins[x],LOW); // the column pin is switched off } } digitalWrite(rowPins[y],LOW); // switch the row pin to LOW (because it is the cathod of the LED LOW menas ON) delayMicroseconds(1000); // stop the program for 1 seconds digitalWrite(rowPins[y],HIGH); // switch the row pin to HIGH (what means OFF) } } void update(){ switch (gameState) { // switching game mode (called state machine) case 0: // new game memcpy(image,blank,sizeof(blank)); // clear screen gameSpeed=300; // set the game speed ballXPosition=3; // set ball position ballYPosition=3; // set ball position ballYSpeed=0; // ball should fly straight if (random(0,2)>0){ // but randomly left or right ballXSpeed=1; } else { ballXSpeed=-1; } theTimer=millis(); gameState=1; break; case 1: // game active image[player1Position][0]=0; // paddle player 1 clear old position image[player1Position+1][0]=0; // paddle player 1 clear old position image[player2Position][7]=0; // paddle player 2 clear old position image[player2Position+1][7]=0; // paddle player 2 clear old position player1Position=map(analogRead(pot1Pin),0,1023,0,6); // reading the position of player 1 player2Position=map(analogRead(pot2Pin),0,1023,0,6); // reading the position of player 2 image[player1Position][0]=1; // paddle player 1 display image[player1Position+1][0]=1; // paddle player 1 display image[player2Position][7]=1; // paddle player 2 display image[player2Position+1][7]=1; // paddle player 2 display if (millis()>theTimer+gameSpeed){ // timer for game speed if (gameSpeed>50) gameSpeed-=3; // accelerate game theTimer=millis(); // set new timer image[imageYPosition][imageXPosition]=0; // overwrite old position ballXPosition+=ballXSpeed; // update position ballYPosition+=ballYSpeed; // update position if (ballYPosition>=7) ballYSpeed*=-1; // collision bottom border if (ballYPosition<=0) ballYSpeed*=-1; // collision top border ballYPosition=constrain(ballYPosition,0,7); // constrain values between 0 and 7 ballXPosition=constrain(ballXPosition,0,7); // constrain values between 0 and 7 imageYPosition=round(ballYPosition); imageXPosition=round(ballXPosition); if ((ballXPosition>=6)&&(image[imageYPosition][7]==1)) { // if ball hits a paddle ballXSpeed*=-1; // reflect the ball ballYSpeed=random(-2,3); // random reflection angle } if ((ballXPosition<=1)&&(image[imageYPosition][0]==1)) { // if ball hits a paddle ballXSpeed*=-1; // reflect the ball ballYSpeed=random(-2,3); // random reflection angle } if (ballXPosition>=7){ // ball out gameState=2; // change status to lost game theTimer=millis(); // new timer is set } if (ballXPosition<=0){ // ball out gameState=2; // change status to lost game theTimer=millis(); // new timer is set } image[imageYPosition][imageXPosition]=1; // set new image position } break; case 2: // game was lost if (millis()>theTimer+gameSpeed){ // wait for a short time theTimer=millis(); gameState=3; // game state to lost game display } break; case 3: // lost game display memcpy(image,death,sizeof(death)); // show image of lost game, memcpy is a function copying one array to another if (millis()>theTimer+animations){ // wait again gameState=0; // game state change to »start a game« } break; } } void loop(){ update(); // all calculations are in the update method draw(); // all display methods are in here } 000077500000000000000000000000001272363601300347655ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/PotentiometerPotentiometer.ino000066400000000000000000000017611272363601300403370ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Potentiometer/* potentiometer two LEDs are controlled by a potentiometer This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int ledGreen = 6; // LED pin is declared int ledRed = 5; // LED pin is declared int potPin = 0; // potentiometer pin is declared void setup(){ // neither analog inputs nor outputs have to be declared in the setup } void loop(){ int value = analogRead(potPin); // this variable stores the value from the potentiometer int redValue = map(value,0,1023,0,255); // mapped from a range of 0 to 1023 to a range of 0 to 255 for the red LED, int greenValue = map(value,0,1023,255,0); // and for the green mapped to the reverse range from 255 to 0 analogWrite(ledRed,redValue); // the recalculated values are analogWrite(ledGreen,greenValue); // send to the LEDs as analog values } 000077500000000000000000000000001272363601300332255ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/ServoServo.ino000066400000000000000000000015301272363601300350310ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Servo/* Servo a potentiometer controls the position of a servo This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ #include // include the library Servo.h Servo myservo; // creates a servo object int potpin = 0 ; // potentiometer pin declaration int val; // val stores the position of the potentiometer void setup() { myservo.attach(9); // connects a servo object to pin 9 } void loop() { val = analogRead(potpin); // potentiometer value is read out val = map(val, 0, 1023, 0, 179); // and mapped to the range 0 to 179 myservo.write(val); // turning the servo to the angle in val delay(15); // give the servo time to react } 000077500000000000000000000000001272363601300344565ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/SynthesizerSynthesizer.ino000066400000000000000000000031641272363601300375200ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing Creator Kit/Synthesizer/* Synthesizer creating sound from electronics This example is part of the Fritzing Creator Kit: www.fritzing.org/creator-kit. */ int buzzerPin = 8; // piezo pin declaration int potPin1 = 0; // potentiometer 1 pin declaration int potPin2 = 1; // potentiometer 2 pin declaration int toneHeight; // store the tone height int lfo; // store the frequency speed /* lfo is a "low frequency oscillator" - so a frequency change depending on time */ void setup() { pinMode(buzzerPin, OUTPUT); // buzzerPin as output } void play(int myToneHeight) { // play method digitalWrite(buzzerPin, HIGH); // piezo is switched on delayMicroseconds(myToneHeight); // stopping time influenced by toneHeight digitalWrite(buzzerPin, LOW); // piezo is switched off delayMicroseconds(myToneHeight); // stopping time influenced by toneHeight } void loop() { toneHeight=analogRead(potPin1); // toneheight is value of potentiometer 1 lfo=analogRead(potPin2); // lfo is value of potentiometer 2 for (int i = (lfo/10); i > 0; i--) { // toneheight rising depending on lfo play(toneHeight); // execute play method } delayMicroseconds(lfo); // break for (int i = 0; i < lfo/10; i++) { // toneheight sinking depending on lfo play(toneHeight); // execute play method } } fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/000077500000000000000000000000001272363601300301165ustar00rootroot00000000000000Amplification.fzz000066400000000000000000000352671272363601300333660ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/FritzingPKl5D]uݏywՙ>?k*-0? gv f/?'xa6vM N(]x7q<dՋ:z04N} K,;w'xy-W|)aY)s^#nܫ. upjw 1ꖘ[o)ewȁ\uTz!ۇ޷osalggѨOGw K~*/忿ЯzxZln0#Νq0|8%\Jثg'DBUw'ThEmXR"S"Rh]BrEZѼPKx֭^ sޛ^ F:b UԃK; ӠrI u#'N}E@ʶ7E%mp2($TiҠ] ˌ'ƿBL}sa^kkEMÈ<qwnVV/(Ev(E;#8}(HAll>gfgMsh]*,KkkCy03nKZsaሃ".$Qm$(Ju)aBbF ѠDžꕺ >5R_H!^w#:ȼ3޶q9á/{:iGRhK?\Hb1#)v4r!Qu!8žHD4.qWrǶOO|}eo2?Liz2']_QBd}?|ktJnu^]1` PC B"ʕ3\2+-BTx]vHc(po~UmZCK{<Ԟߗ4n7W/"!bW5*ocFeʨ㢍q~ &)L8L~^wף)/Ncd(שсAJ)%*b7I\o]}$46q^mUĦoU}3ŵ S<آ搉k25j ɬ:ʜ' DjJZ&Z7W=`ZB܋jFhyh0Gݍ!oMTh[?ۼE7D6_FZØXs#[ܛTնOmVP^ˠ􉁱mkg!,ƴq^1d0KOV}PwRhv1\?X7׳)hc%( 5{Dm2̨ wt(@cK,Q);ĒmE}T<уc'zİHml;(waYc y!M‹I6N2Je7H+t>^N+'! )KUH*gw(Oz.JsbS|t.]58{7kҟ"gѧ|Ӧ#]Q`KwMo]Z0\aXr:/b"=2WKKQk-c6c])ڌHԙRݿ:Ryopx}'r+%H!Ʀ>4X9 6[N $̗Z=fʫߧ҃'9f*Uɠ-lPt}C붒t}#Kꁭ#+;L5{Øyb:˹q>S865>]WP8Y)rZB^%* j<ɹ%_x,\y{ƿC m9kK% f|[.iNk8 \+!(6jilZEQUYiѼZRYSR"zQo4] TPnrSK%^*Z71F$ Sso㷃OdrAsq\> CL[Y~MMӇA~ϟGL_;GOW_f/{?y:!45&)>lPq|}3]u/ߜ6GMi ɞ)CMo~ GpOw/0~%w[ׯEĩЃCx<8 弅(t2r@=; L^{AR^)SXxow{k`t=9 \U=pPԝY|8FN-GњQ~lUh$,I귛ӖB4ߊTF9Kt,oԆR٤uԯˌ9-O:(kv4NZ4nx+4NZ4қ*d9 IY4:Wv軜}{Q'U(--z>1V_b&$w`P%]PrEoԲEeNb^l 2Os6FKꊞTn1 >%hn dK1n:>4Gҍ9_,qtη5bݝD$ (X,q@ )؉WѦfcr&x֮ 9[2wijؾر֊"v7i;/p'x3XlG;-1+eNℐs,91 1Я"-s:L{v0m6~~2>|e?>\Q.< OI߽/[?>wd$u.}'kl0o?7Ol w ~&@bCEg wFnZX۔BNGCBtKWvR޼H:% jTKcԙ6`+ST0l[}c sj~Mj 9ѦmMWm2%9 R&UVU<-:PJnn:4(Bݔt;>.s緁{RU0*-xJzG;HgW1Vڧ'j7w *>=P5VD&|&6RJJp93NGs^2$pg"XqDڂש2npQ=JVXn.bї/7I\:uuI[}FݢW{ QZHQ9VlTNUu jOqDjBY*K5h|)g"ŵk3\{΃\o7ka]#^Zqf<3:,j4J(Q;fIvtZ-X>GʋxYۺr7ףMn+.m$jsֈA x LUlCSWe̖6qjXm4eLsCڍ:)}l:)0fH|$cHp4lgC֑b9I{ #C2gm$̌\EE* qRw݁JTef 9.3;R?&3; s`6Kι0rEwfqH7 9v@Hʼn!$ !=(7(K%.(%D3 }{UʭsH ͈N-S5$[\Ua$ȹY-7[ZtE:cEtsn r"}q7D聴ܬE!$MY$8r@n&W77p3F*S- (SmʐTQ*tiv^T-7-'"7ylnEJdu7,R8I7d )8nh9Y]=,'7d!980'cqp%Yd$ƚ}Z26ߘ㒱# rdHƖ$ U!|fX]M'`dqBH9#6b!w;Z*VWtT,N8'ioB"Q1#<s`8iLx2}F.x?gs1ءsq M:/ףLff)9/.x 45 Ԙ\(VӬ!~E&Ͽu;n8=Q$G-qբ:`x0+O3yEspT4~o/u<"oE}m~Дz8a؟\/n*vvE$0xLe͠hNCU ~y_zQ _m '4͒5Gl:Ô<|p={ mSx(hl~l>WEc%uT/ $6Ly APN?,SS$@~IW~|xA/h "-3&?vo~id̯+Ѳ@3EeDfAyIMIi~d * ʒEl-~|@C)C+ f%)vތ8G-Юpݍ'xrR4˿=}I397)H_ a_ nE; ]z9|)@ ]B.Ax_`klbrGxd + =Dc4Ҹ clכ$|82+ @Mr,:?"?h+t/W6̿EPWqsG[(>e]%@"FX@܈7FvcM7M9TT>ЍkW+];ĵ%.\;]JSvK:Ua)ʄh4^X a8/.e Xē0oA(8D?a67Hf[i ފ^n,>e|=cM^]踟UJA/7k<9({4£:~NKL.LZa S KL-{5Pt 4ANMN~V]c5?zy vc]H%6"}[p;'jU\"̩'P00ȹ}}@6co5*!Fr%Swj1oG)M^ A8LgVnEM2Ur\V tb4tW9*iF >R 4Nj4NhuqHAi4cjl+nD3B{pT_`DQP M@y,+ M:ØYqT@b64>P"=?KA{rÈ TqG֙*m CR`4 qecu84b(!,1 < F Qh*+k7`B!{'{{Jbb̂sTЏ*?1P'PFs[ ĢP%%x@%B/PU<3D&́qnU0(~v2B9 ts 0A#@HJ%MX LpQ p(T2mw㞁'h Q 0tA\wVrXv`!J #0 p>8%$u`BGh DWu*'{!/ A/aIhG0)WLJt(Xj;Hb,Pj{Uk`މ 1VnjϜe` G7H@\V#RZZT/ V!"qF(DE‘>FT$?5 P]Q,rC aH & ZL80P:P ]q (0Ч@cAt[*.Čhg} 0B0r$Ce>)b9!xDCF*@4(8 gLs;ԯ7m9VCZdh)],]iVR NWÕhx@t,hN#ԑ4u8пg.'ת0 1t&!0+h7Eo _8kpr8y>:W6"LqG5hfNvYRS,}H$p?Rt!p~#B^3"%T."U]x!!TDo]34 LXsCHC~2 BCdp WAwFkN/}B8t11],%,f`n@Ɂ n/zʊW~= 3 0CqY&zR~'D;cG%S  ~ F/DA5,; `@"8捦E# |M9x"{$Hc7l׶Ǩ@D<#bJA'"B3̶`Sp EX566d9t=d0sy'D} npT c*h;F&iup >vv$F0iSx$:P#.D }kVP? p 0G݊arAojߔaL'ƫk?*$ `bKSLP1vs9Q>*HPy!nC(#M|I 09C0 41KRUݢ F0l@0M6 .ChMCd0C`60MWvYj+̄`!Ya bCگJs:hR`@. `U#1u (s87E/0Eۋs.P%кaePFGF .| U֪)[+zr0q#x`]ݼ.-,f) Fc `8?hmLR-@HA81A2d'@SugL!%(C`v`MIz qGҚT`p&DD} ñJOG73`'h~ lIGmwM'6*QGD7evU;bʏs(,91+Ww)Y>Sl5ZRI2xaJF,襤i!s@Jo"}U| {P-ph1>"ނP;joiyǥH U劃WؒGS A1G~OL(QܩVT]jj5K>6 גo"Z.g URC B cKqhr<ѪP BBe$$T%B%\FGĉcK!T@ft P(P{xdR_8@fʔ(Fy߈)=A#y11)Go]0 ems g80H D7,rh'pfXVCW Yqg(gy2oCxg<&ckaص9Dd1aYD?%8 s$͙~as5f^0YtML YdiB & +M8& ~Sv`9y-:41]yt;3`eIzR_<(ǏJs Wsb: 3QL1B阁Ŋ^dvCfQ mym]%d􉅯B9zטGsS8HD!W19I0H0%p(' O'APLƢ(RX?(Ǣ)EYE:E>]sЀfIucl7`k`I>ٿ%]0e #F2dKx iҾ>D{Hi) dO!6~=X㋯#7屳@4>3ba(74%ϕJ7 L+qF]ά\g+\,(xyme_lE\)tqRY"~xXD;E`QN`$|,\Xb]:0a*0 :hZp]s@LLr2{$^ ,!'ȂOØ'8NAa_[ k5ON8 I*P0l #,8Y~ pRK_uU"^;s EC\# |<(wMHo ː&n-c0Q񃱽$هcqp.:xWiY`q2%|LB/~ܓGʔǡXeH^ܾzSƫ a gOf,J_A!|˽͵s<GHg\usw)]O?蚭6P 7ɦw) Y櫨o΋7"~kKKhnE)˅ s^RRέ_}O_/?KVZX|[+GHI=hPjli5WUWnq>R^Ќh rSф JDƊkI* q>B^ϹMMKg`#Sy| DrabT%Dfڿof-[^9k::'Ao0 zͧH؞k01 sA:6ޒڢ{HTQbK{Ӧ^mvyV݌& X#Ɖ㳭9 i P!/Zl[ˋ!]3N8siG[|KG÷R/OATjUF֊DBnA^{c ׎3mki\S"rXlv(#9-!+E:IL$dDBJO~mjWm9d`9]}tDl3@֥[W]65"i N`|)&hA $7Y-/;6IKΉ:H/$wDJARzr)(&Ur."rrw)r/EȪ'xͣj^MѦKPHOw[? jG,X˸e8c:AutomaticLightingSystem.fzz000066400000000000000000000457701272363601300354300ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/FritzingPK|8Dj ]H AutomaticLightingSystem.fz}r#Ǒqv#f/<= @P3V d*4.$Ptx/*8<gtMUg8LoG/=soan6Z|rK IMBhDBoUg4?$O+we}q3nSMWOh2xo0o:b}&7Z)pg>e/>+b4x]bMg>`h[}/:lؿs_^un_g7=s?44j@Gaw3/CB)~vzv6}j8濹^ӁaOv we{i駻Kbn~: <G g|0:|ߜ:k~._f{~gPnRvGn*̕?}8ar;:ISwxU2M_9xLg_r]L>=_N_\WK-{C%]wr-b$Dwq(a:4xB%np]D)A#?%.)VbMCUG_ڿR' ] ~W+=kQ*_9 XUWdG-&߫t}>"\{}Kcxfíڡ5r־AI^/=K5U֨Gj7Y暷Yߜ[cI˼BljmW&sA5>m_ŕ:m?eUO̥l9mV8#^wg&sM|,2d#߭q@[v  i3~?ř>mW]2$Vq}^xv +m-s⮊_Z7J.Q9 Me.a2>= gt'{ɋڊsOJn J|ʼn's_7m>B![F,99'ig*8it -sMڈ%=Ӥ?y&3dF>)&i[L0i쒶 ڥTp}JZg<>9%PB>$mi[& M{&Id}nvYsHj9g?3HWssH^ZER:uH2E=}>A!ND9$-i#&i|}-3Jډ(=2?Y%m3WN?%-i_-iޥp}3LZ g<Ǥ>Y&mgJ ?&i{[暴 S{I}nw3Ng* w z6OS_Ö@7-Z'OQaa[$Dy}~q~ଟqOb֟wGcw2c sWNq6A:ӧ`o}6hiY'jmB9&Jݽ()vIrwo덽:̺S6Yt%q3XX]`_NI?Ko2%ΡzUi.is$zaT%2 h)_&ɍ)V1'k8+dJ1HEw(5)Kz9X+tpභYAn\d^'!#2ʯPn%8' c1)2}>n ? ѝ$sUbωVdjيg0YӀɜ S3 1,{HVSB)Q}ªH~ƚXQ_/& f/$ DbψBV 5TeIOAR%fVjwcrEdNNF' &R$Df~ˆ3%fU8GLɖU%\X0TRX*sP_N3C;5NJ~'+v)n[N1g iK*V&A2҈#}6Q6Z*G#OJG?~~Z'X#-}Ӊ?Fdd8 zJ4:{ؙ^\:{ŰݟeZf;0+~c@zTim:H靈#'sk)2UYMd)\Ź?k-^]"OXKbvS4Od{tR$Z^۟ K57W.`81Ԍ1 hQ(E8[sZ]L71VJjI㌯ nze@hmyPjVϘP)_=<QhȒ@hb}!pKy4݌'W')Kkn>\}~stu1x'ti5YX{A~:toxcۘ;6F k+{/j^ըS4^B|mťD@[uTCXQ2A_lO'H)2%1YYb$Ƥeqm"5Wzrhlղ&Ty#Йfֶjevhī|Me־}6E۫Lmdr(_hE=^$|4wn .t<^f1oO˰u˜w>7ϮThM)}("Ez0C0IV,s@ΏM2kKY3pY:Jב#&,)Pf SKkepMsA-3'n=|x936hz7&s+K*H\^JbY=pTI5 ~R8:>ACbV g-.g۵ UROc8s+]c8SZQYB9\JD,i iB$MJDĤɥ%MZiя^1krvbOc6xi](iʼnTQj2$ i1XE)VQ:.Lf1$ ^P$QxZmc>e^`i#M/jCnY%,< ׫6Z[P%VZ;%U8?[!^_rfɼŸQʄt:Lu*lũJ?T֦`SU۬6tmm*'$HntS)1%rRf1Yב3,Ku` ťD6nt*fkgr8cwwY+;PuXr'ݝY&]zѦR$[6\)ՉpXDpR웎ɷ[ތUЄ762]A,n4LD;}T}XRևI0; F89%y04S9OY*WiCu4gM)g%M6EQ-Ւ0y)H$+|e$+jm)${}.5W-G+ZdB[PQ =RVFMP3-U$QFUpU53'[$RfհF".%=ܛgc#dG7zrp W8;vK 6IA1[z bkzgz-FK",Qg:LyK _Opn+0&ZנbO*N:}&vgjg0Aqֆ0I*!oeBq2a"kדzPr(e@M E;7=-)Ff bE7͕Z8S3{l=Z257<<0Xf|*CV]:5yM|mv!U~KKWmN/"++{s| LnU4{39Kfr=},SYGB~lQTdH5 pUXqNC͈XeÀiᬆ]Osé"]jYSl3I𼮘b.LuZ]Zw=,"pBԹCPc05V!l(uC'c HuJhh$ii"E,SE|}a檺QLcrYNtVM"Q0<1-*.ItPbsU4:C!vE"[#DadYF3l4+U"Q.6)aW=b$n-FHʽ9>\\)7gYM* ݫؘ4}WO}]^5m]BNX'EVxtej$U}.rD"Id4e]7 s_\5F x]ojHS VjZjӖBRKㄔڴ1pB^/_]:V]/ҍw TF v:PaŸ*VBJuޙ.Ix H]tENT?I*oxqjݙi-0[75@In%EܟنY$Etb*rIgr4xZҮXƮb~ pEvxtت;|fp bJ粣K(sQk= e`DסU,Z#bՐ.cV\f*I/;/$%#$tV%pzsLm.TJHCە|MsV8K/-mA&z #ˍ8逩缵ɴ7h8]JjLVgL'n6Z|M+nom`/ߦ+lP|3?WVL`}Ջ -yq)lV3z\lӉ!VV:cpnXop<5ōLYSn" *R-hU̠ʊ[Wr#nZn\0E8y_\ߺo?!#Vh.p\xsXeDB2^ЄJ(iVLXSOacwMo2QtμLV SF7+ DeOYb:Q墷< S̚rT"NbNܛCD:qRp]V2()&Yqq-[˦ u薒:# )`)Tn5E8ڌg[Sq5Hn9ukz$sji Y[*UX#sp yMtU6J!V~X$mDQ.UM:cI[ snF"q[MGOi'8}iM'RVEX4(H\|f ]X]֨x3p[R׼eSqO"I"+h, UHTd48I㑐TWρdĕڒ1SH8+c} g6*Չ(xI\ @I\7HrgB^v$TJDǞUpU׾#1?`6 *#{k#o5eϡ 8& q[ gP@dô-Df3qY+EtTdlM4,mRbƭN8l&jo+P۪2ٛIc[6PL%yy[бORƔ6& $TZ Z7Vq^lmGnw#qͱ&%4l)qcLdqCDfqI4ܛ#JteuJ'tَo*rNN/ V~q zI[Qj )땳*>D*K}!YDPUxœ{̰Grc%moҭ&%IW$0$.vGbyYzjN*x +% {kDj^ ˔TH-.{-en)2%'C)belJ$p\ɩI{ Č۪h@t4s;jBmI% )cђJ2x~3(-gP։)MQ"Kro'ͭl%YEpOu ,lI*cb/1V}ڂ08bwĔlcXXY[8r٬DVDVA$RUنZCxHՖ"tl:SePI34۪[(Չ&MX.I$l5H$lD"a[͐ [a.Y8kS'PҸkR"};V6)[PRՇUf^.:epq[T$PgbN%y㍺'Հ$ʕ{˖Ra4i [PpQ+rutm트J%;H.oU Y/0[d™wFuJr|쬛#T숥Ijďp[j T5LLP w4$LGM H_qIcqu9VR-hof[.EGJL:bmQ$$ʽ9>|[I@x$ά)X{ޗچ<ɺkyۢF&2!KdruegT0\ h\7G[UP7鶫PIlP7c]7GƳ-qJnCDne!{s|mU@dbe0jTRq"u;Ren9R6l'uBLqCHD˦q%ꃑj@ʽ9>DF N5hf\+SqǂǦi}4qRg*@JgR]Ϸ Kque8[o[ [7GHVM7LXݧ6ĈqW `Iì,lBK.kɔ#Mҭe+1l7GvdfU̬!Dl~PYM(r7 M56W"R,]dg͑8۱]$ ,-Unh"G m7\TGQ;IZ$i'&-g8Lsfp VƝZ!UBInʼZ̰ ] 3`&RVK\V+\lM6D:WUkԀ&1RBlmޭ0W:c<iYǷWٱJi7l'U B"-vZMqz`"R"fjDK"]ooɗf[Gwߧ=ͦ;˫Fs~+&2C W|ptȮW 'IkqFOߏp6M'_~FYg8LoG/?{?w_˛t?|ֻ;s}_w~z]&boƓYa4_kw3Jgzwpw:y珷ӛa|_uJHB7E&|yǝH:_o;_:^Ԑ'V͜i;"$R*GT=uʙ|٭pڡ[{j5!#;ܤ G1oSksgmG_'wz?g9T92pZƿp|3ʩxno+?;wq* gaxi Gi{ٶK!;ؽM*z<1e"Dс{wԟw$uǚ rלSTz膢46KYIN!ܿVk-WS~vs=֟*빿 ?a3Uj cŀtOT봖7V H ~_("֬7D; mNPN >kSR߀2?]^^z_GAVA܋o"? z/U({oݿ.K1 >7O.3<6N CݒF}S*OjF8%1L /[Y~:CQb\㊛xԕO8_=tp]ʝ<С( P߁Ew $rY/SgD̽Jv'rϢE-7_?ƙ>}BCK0*3G,>8εN4"ڏh-4 `2CL7L+~0@4Bw*a|* "B+-h(Y8u#УIR R4m<@Ok@ڸ?Rxp7L3@?nI:9#]'~2#ŷ6Sx(hlvlw4o ϢB5@B|Ly | 21ED4j9/Pne`ˌɎ}z(U 8F!d[^&JHT` Op*Wa=TYMy;oh8. Wx~Ib@ݱ7# +ܹw?=}9gr$ONS,d,*. q8Aݒ~{6t=rU/Ћϟe^K,r폍RULH ,ahP[a zKu(>e@ N]r?Xt~Dv #_lLٹ(̙. vi:̂.R @b%9?p#7k2mতbpvc~ZVv}k>qtkWcvMXm&3!{`01*,0`X VG_A<œ0oA(8D?a6HfZi ފ^n,wD-zeǎD1FK ]Pq!f@+5ܓ`SH1} wNA #2B8 X|JpFmA O5ǼCz[Ҷ鈐cl84=b Z:>EҽUK]q*\Do 5pm RS.5T`i:n` ZI pHgtēh7Eo"!DzBBXb,?0.t܇B>DAI1wA,u?eumH&5Ʀ@F 4Ck `yQ T Arg]#( j{צtf ХIN]tګINqx9yG1J|{]r >Ct420=@ cF% y2aXS w@T!0WLXt~48 W;(4U=:v Q5ԷMc*] H,1㱓³9P:uмEH4Ta)LcG#s!xd#zc;MͩWŊ)oOf|C $y/p]Kщ#p# zvΘ.HDċ"RT.\BqwṄ"z[҇QhgL E`T F2ǕAO(2E&U=:E@4Ag8kN "7lX/-tdQHb FtJEZ AY(aR&dL! "GD@"x8s>Fs3Բ@7#+_GݔaNaZ@v?MR[Ar8K" ?5xxSo^VjT$*GT4Q0q; s)a40pᅑ;Lb2!HA!s80 Zdx],(@ Lf0N@@ FDEDbcX@j(+TQAՉyS[1aJ ..!'8?c {Xm>0 ӿ1OBM0.f1$~/n~5V"ޚuՙ!;]҃,seK7t zqR0~0"}pp18qBHpt2̫<,ӘL +Iʽ2ey86q[ܾz[ī'|g>_l-5jSViol~ﯿ]Lv6Z|MӃ_~{/~Y< ywscO_~!|zĝ~4^H`3c]}$LpRъbdX?GSzB*~ Vucvq[Y}P z{GWó'PϽbU;$ҺanSʮShZSya,R[{RϟOy(Xg* P-OZjk*YNs"3\(ǚ8PJ_7βyZ.ɳZOn:Yqz_wϩt~|ގWߍ7N:lWd+ѧJZXd`c|mVKɘPm:ϟ_ GG~ d>}e\r]2Y7}џTܰ[l-|2nɛWny5¯DB/$e֟靈#9Rn3^/|=='5Pa.UZ?}u'8b2,*Ɏ~* k*iS:|Ӳ;ff*pkvh, *eAK_;>;i:G<^QM5QozAM4y~U*0hjӆ, Y☯yAfn$ʞ^U _',wo*^ ;4X1%2GҌ4Y3*Iˣcd'N!+ֻطx筰xnsx Gu8/j!D^6?n\CĵS{[JxM5{14w}e2QF`Mu>ëo*šC|Lu7X^`C>ک*r,wK,K~hɜ 8m `րeϷp*mV&{zʴm+]U2xNw-v7N[V53`dLfxk+=Ȼp htr YV5>>" ?wW;ж?;ٕS=5Oh&o7Q RLГ氶'qAYô#_KfXLZ- E5-E ;f4-%5]<=iA% Q^yXMD ԳU 83}n[`kԸ-k;yz/Gί6# l!P>h Mb:@_=,΋%~ntmKaalQ5,lV]^Bң;NYڑ! eܰX8`@i-X7Jzx&f͝DE@ WJ#27DXXgėSi ,?$NC[ JV#rD>v||ܛGϸ$uRFIä0r.VDrU"&:b\ۛccU$ffSeuEvCr;8-9"7+٪h^M,xU:X$U˱ "5͑Wm 0BȎ :y 9"#+qv/i_\#sPKZ8D|`gAutomaticLightingSystem.inoTN1|@KZR[T@ oNnsE'@|Q?o|YǾ@ٝug;#Ri91*:b ] `dR.H1`Dlc%r9>w p+K [2U'݋DoD?Q]]3hNwƢlel$ ?G{L/|:ՊZ|.xa bg^BjT(Xxv7 ݍ~T;@K4*7f > 9EmM~vŹL߲Bb!\ <2[W7*}Ln?'I SFMTc R[,*%ZM[[\n}.ĵ>~v6K4#LGdR97N:|<=jO^gt6ώ?8:GczBB)ӓGv3d'{q0WsV3c߈C1TGWG 6:ދ7ٯы~\5x~Blp=ƛxqs0z<;;|9L#y{㏣4r|יL0KLg7}|eÓ`1>RQ6ogaou營x|.c|b;ZތQն9=m|= GT+/QL^N~˦HjR#m)xQiϒXTket ۟x۽Tc4p!'>HJQ-+Ãp:cl4]y[*E;i&"w/9N4x Nj>_Vt#oIi<MOGo;=+p{)|OrW{ =[n%h'}q:u)j#քCOg*v>-R͋kSl:ТSe7/׏rͳzέgyuor7wc0NuCl SJmmsnmڶjZ7)tE#kXky]UNϣ!۶e i}hAUtUZb1͏ΩЇSR;yAIxJx]^C]~E p|~w1R`jC8ڨemdKh NO2Uzb6-:gNF]cJd[zȖ}YfvӞ=9Ƣ^cӶOݥ톣{(3d+/'c+*-}}:ErV9/|n6 LB<3iVSȈt;Pg Ld>0l/eOfvr*2[ :bzm]KlF 0H#Kw\د–E폚yޱD%-^_5kR6Gy#wmz%`e>DOR%0)o#e'E=:lUlm5+ T-zM=l/;UM!dė?=ܛצ=ʶ)dKuȦ9d+/Z`M=lF_MG{!ELך)Fz'K1k% y7U[snÑL?ndQI#Y٪lߛjEe+oJ?Ge6;n FxZ1}vB/sP`H{ y3ej7~Oنo-FNxn󕢪vv.O߂|'0}%mɥC9UQPҭu nFOǛ mVLb;6"*ȠKtq~e~(-n,_z~~28Տa7=EM/-_pS?ۿ|??י_w~7?|Q/o_Ǜ:'E狓~7 Oy?V짊~lLΊCV Ώɻ:DOtwΧEɻdzNĊͻ#RGi0g曆?&2y??DZGR`մ2Q:\tou Cq—aO}b}Q~t|Oj"pt68{uVΚ޿DyT"P~t8O~g力k۾ .%'yU_?BUS%rCƠeWlX.c9QbN VMlX_؞8wBL%X |,je-SW*0v)`G(o}"K9tv|\,'THyj%:O#Jr $*CkA+df(/isJiMt UF6jYd|ٍ*e2 4ԨN!uJJ*$CaB..Bz<+'ҁrX$+L㬂nRP&~wEWT`e}ic{VuUʮ){:22]Jt i|+HИgs Y>Ѹ<ݥlHZb\)lNƉ;r%R>R:DԑQu$ (\a815֝|?UK,K,1\7hD*NeJ]$d,/[,/w޺m_D'b(}l٭],{AY*~RieR|J'DIdPobRAv H4T@,405jR:.T@V~:B`\Y(}6QўiP%m@LeL mkF M'"Ц}!tj[&<3$"~~MB"˖IIMU`$sL$ͦJFr@wଥ"CYK9c^*ycc!0ġb")( h_!iX*Et#PLT4=&>"! ]ý$(7@ ;p[x=ܘQ~P %~Ft8|Թk7]rk'[];^FG1v񗦣\%RnK(}hɄ 12)Oy:1@Koq a\`%{ .]ᦸ4I'Sܒ<@f>mqS[ e}h$KzR\ħtxc? j \ѴAۣ-6 xe"wט2%L` S`[@LlhI^ 7Zh-0NMN$e_WiP ġ9Cc 96a}pۀM(e,O' 8JK Q (@=L`AC,'0V nK#X0R&+cjp3qV "+@dislɋf²$mk̆@v laClPZg=.U Bh<̱J,=$J: _# Shsg\ӆ*T Mr3R%ObCHC) 8?aS>g1VΰY: 6gw A07}].Nei)r䖠ES?؄D=SB!SX _H 4iX%!G AIjR@μ[E tCWI`L^# #tI!jGEA*=s)Oa#9}1e1~Ggp!uYȄhHrp(,'y8Li!WP~PyhyhioDqV}^"%U"@o 7&cXb"z[sՕD2iHuC`{űOw̕ 8$MYVrnIo"S԰XN/2.F5%UGH#E'8@du.&~h)0KP"=H2?=A϶PU?dEe .w#,I7,*'04oJ.6`E.|Q6"s`I# n%+$ Z"d%7trYbJ]Nru[Y7JGNT2)zȉȩ{DeX?".eN]C12R X?m^Ā&`37;4RС D!,Omdsn1\REL `= !xvꫮA `p2\u96- Zd@3|]ă2eKή̈́]^y[ȹ.G0U&l0.&Ыkz5D7X~EiRN ,P +m{]A1@fEYR׬ #`M^6S X G1 ,{]^7AgpnU5j` @X02,Q #c-%Z r)'.vOd2{"s|n[GNiϬHnZC+CJ6f^T. a6bW섍j s|,@)ݼ") ]m'X fYk;;LQ`Ĝ0eigfRVpb,99Hfas4I]RFRfPN^@M5JC$($sF"c[cCH|E9Let9!Kmm0Ǵ#xA&u2A20X㙛FF0H)LWރV2j_c&;gf{T,\ӡI@.DA &kFQrmt3,i{i"AFs }zb<@,G FA4o3ך*H"ܢ &hCPƢuc̗F0ѩb]5;/jNt71PD\[e)4jIp:۱֓Zcmo?= +_hpʱPѭ"9aBM$g-ٶI˱O5is-82,s9[9A[Deьۼ IV$InF%RA1":o\9l-WD6(@G9fD`砖&xYEH[[`I!)Jt(u*ZuD*IH4OW$-Jֲc+Zvwd/Zvu"۱eƨClhwt~bHzjk kt3C }yL^gpќ̫9=Mk'ckki[+K9{^ܸ 9@psխ״4f\F/#X斏j`\Z$20 9&_@jNÊ[1"o|LQX%0 9!Þ0I"n^re֠lp` f l#Lt{O4}>//c^Sjm}pkAkoLȺh\&zQV#}_ 쪏뺤'*푊ZZd+ꇫ ;@+Z-j󔕍oy[= ./`̐ڌ5S8ӍkjPԌ ߀Km~դ{.m=1{ 뜇<diˎ+jteUf'Qs! 87l鸦IZL @yH-R򱡮-ϔҷk?*JP"T5Xuر4N^j]fT0թJ߶J]Tb<_rd \K\ 3dv'gf pHYZ 1F*S1=!p%~D&Ef0V%ڀs%󥎫QQ?nAP;Q|G@t8g#Qr]mj\)q+V[A:zmZkč4ik3p)#- 2|Wp D[4P68k:?5 oɆ5,Dɺ;\PɂmnStIYfYQvEaV9'oTYUc0l#R,X 5'Kf1i܉DkVYSBC5[Efo7#Ma|̺[dojzܗOYc */th8r/s f݋lr0qK(@:lNE$..Ҋ|m)^E&wHΚ$ ,n[Wf)qm+f)zt6_enulh06RxR]qͣfmy[qQa0zqAƍ9yܥ >Ս Om?<:WT^Cʧtmj,UFSR\EYFggdtV-tp\mp"|׽}ٖ|Uq|(DIژEiF|N=E 0B1STfm[u9kLva˜2s0H`ńd[iZSy9lw@Rh$ pE3"^Q6!gfS]B.?Is =m{Ǟqىk N=+of&ل´M&*Xi KbVW]"vJFV6 )0jR+|5!\nWg2zF?uF׳WfgeXNU;`I dNs1%,r/~E=# ܭurs?}wCՌ#й"p܍0n0g53JukfǿZ_>/äU(]k8v)~*b`՟r~-*>]s-ʆ+N#xKU쥓{z4=[Gz/:LmQN|t|x8Y9n28M:Ʈ3_̦%>I?VO1u,D}UXI[ ~}cA?eyЧ9W$շethy.sŗ:f;}1SJU§Q!Tt7Vk>ORigN?;aRR=ustdꎾڸEO+ϟ3ruzA3u!Wi={[ts GL*7v|B⫒[tsh6o>],}e+S-70:g$\ ;k)^| nOsl5 wï|B̋]7;.Gw-: [_v㭻IQ藼OIdNs~+ҭY/vm/E^O'}]_h\> tjO|ZjVt'2, e7a s{Md.wAӢ7t&[Gmy"GG^|<^>;wnYQJ_cOJb#}Y[StֈRw&fF7fWAG!#6şYv (h]hl 9k6"Mԥ>gPŎ$~bG0ʬ\+se20k@l\Y8O'=q(G^ڣ$A%uMY+E]щP@cWпE)Jp*r64 MF `\Y|t<[\ l1o~z+H~L`+r݁M[oqwИ.~0Ay Brz!Y.˗ǻWg`g7Swo__~|t7<ݾ+\aBݍ>^/gᗓ廇˧nM'nOrvvw}6y}}u{fWx7~ ߆lˇL6t!<|tQ6Ѣ/?]~|<ކ\0pb3p%b׳KwVO=>Ξ:;7_\9/_>\_˗ˋ.?^|{qof;!7FϘ2N _y/| {o"]>~navs䢣Wf/_nx}캲教~'۟}6{|2Cfx?<]^ZÇ۫Ϟ㯾䡁6s?/ˮpêS~? !oiΙk殭\ܰwLό7,>H&C3/c.f]4wzw(q>kO]o55c֜M(g_:ֆ7E m$SqFRsrk TG۾kԽk4b_,!ExӿNj,<^WEzf.G*X[#T Eg4~&~@u1"jϪ6/ly-8f~iU4:i[=*+Z'csewT12eklaD(ո* $M= G$]θ-G$Hq%q6 iF$NȂdW,gڮG$Hq%̃[9tc Y{dARv\A2ď9{`#HZQF"2VL#H62?1;q_ָ=;&PmNnۜq{dw#+g#5m(!9RjK3=JSmrҿm=iGlgmk>6ee)"cM㊑ζ:ƿn4e-,uRogO%.Kz_ ޠ94~snsȢRmzKfh_YzSms #hc-,k鑅E-Xq2ɵ9OͱG3b} Kf,}c`VM1,lL}~gn=GE`=9YzdÌ1Ϟ0{d HƖYm7)9؆ܘ\1ϖ#k-XlMaqdU|}zdϐ089Z{\^}f^ 2@ƆWWGVk1ψ0$y>ln= vl;k}`gCN1il|mvfell=GjSQȤzG<699dζYt }Y'##g{d2k[+@֧dgϐG>f<Ȇnc 9zdRd #+G6(g|,1ϘϹGdm%#'~c7'hm7l=2жd GVٯGf,s#EǕէw%#dmq%~)zdjvΦ|,g yz\q5k6&.aw_^?\_t7fn#z/:ij|x49qarr7xnY8bsj( wٱ>EXFk_p.3㬇_8:R.N5?w8ո{bq<쏿<]>O>=G*֫pX%%eul*z7vQW,ԇLB}ï <:IK_^lݗstU@QRYBmV.%0HЄǶC6o2 nzM A3kpKBG.Y'Xfy(|f$ ii@rFҽd]eNorR2mgpِ-<)gϷ]D4 oϳz\_<ݾv{;O\!˯ˏ^fOgϗw'hNeZY~S)"-|ߔz#ll'sEZ^V?<<}|yp0Q>}.JNZaw2/O;_>0*bPM5B6ѡ_ˋ կ`uM'ոc>ZixUqQ@#&>xK[wENzy ._8N,/B"sXL]ЅP:%SnCSH-:>}TࢼM]=qRL?nng/*irTXx&YgWZ1<:,`ŝ6_Mme8#.HfN.燚[N%|5 XhEY#;qjr:K@s =L0"l r}'m=:V'>=kouN-ǡj-oٌ=)OZOg#`?~%>Ѽ4\n  YzOFܥ k Й-`>Vl辿jۖʐ.v( In2Ē%,[_o~UzSKݗ[޴9!|ϞUaR~~ݬq(-56( ŎsǙTCw˾m :ɾ!30IC_b8u% 8V7}-\Sz&5" a"w@G Ha:Ud^>M1p «M(izJx#U1[Wx݉B WU-JhR0٨rVxGBX}buIt{P!ꑆ~*Wҫa"b@ZVhҫ燑^u<8nvkx AƈaOWMڮyPZbtRr8 ZJ5u2gWGxBzi)GNVB''!4eĜd.!B0M㝏VBΒ{2R=ICk`IDnYZDJD6n`ۧ~OZ2PFiQ>$9F2rױ* ַ\[dg$#opr9MҐiL܉$#ϢLau!>ge -]P8Y!Y, R,@Bt-R5!qFB 9~w:R!9֓R9Gً\ 5 o)(2A9{yOگ+R=)ȵHA:v|m.=z$)x)]mrH,;dq5 qnSOCAV wE^Zт"g? !HBK*'&"{gSDV󮾞/ID>R9GًWg$"5$"KWbaf8XRku2fAqȖ!w>~F*.A6Xv,N>y뵘G$$pP!$ sܱ4rf~G8.BRąSU+7ݩ q-" *96 Ar9R^$[֐񅶞字d> EY B¿llԓ-&'Wu\:}/$]ϯ˽;'z)_ 3' &cjP;^%w/iiI3||$d6x6ܺc%Y0Bz4iǙ!~6g!I;niȢZOУn@";^0],6Uo=rp *C&xH8=^˙֫KjY١kBi|'!KQpuA4y߀04xfգP@'X%Z.~hUXӽ u0OÝW#4nu3d=ְ֏dm~ (Y?{}ЫU^οPp7{M[~hN.˫wۻ.]{1*[-Ghl/TgڕᰅX 2+{Hk2u)m,tLOE 9`^2,agU{Y)3{:79t2kA,ЬH5O*ɔ+Q8^,/B"wXL]C,iőϐ:L M},jZԧDm}U*[& z\pݏhHs[CF#z}_(?/rƥ?gHKE3ҟ)bmK$^6N6Drɶ^}c&w[WF?Rէ?;Wp?+׾-@s%l_H>|1UŲ ,8NmN.:Dj@X{[ # = [ON@6$@h5q-H&z\"R6zI^yt,;R ]A֕2{aE܁hgΌ;gf ^H1>|svh݋\n$>5D$>o ;V8. '>;G3R]) ҫH}}vl,x]N>yUsef)6.!ٚmoRGNU.dM'wXL2uhߐd㕡~dhG2tKC'zR^RmTD{hBRǥD,o ZAJte#dwɭ/&w[/3"ZUh "Bqhh~c >'@%=(6v0s=7h]k$@\$@ 2ѓ2W%>P}SB.>L}bÎlKaM m(ɝēKaʐ|svc >'>/¡gպu8g+?gv]G~CW2#뒞L(=eOOWϨ-~ܪ+F-طd qx+csgi+W(/Ȫ~" %)S62x]Uux sT\/z͍q!/{w}[ʅPX u1}`Si/Ӈ%w+ϫ:0[-(Rm+vhI2cSwl\H6J@Vx3u #<~2җm1 q FYme #^2E#qbeX="X'tjq>nszιz^9MڰG6-K1UXv9Fif:R$m# rƭY:~l6oQcpk%O,HR+y|E Iϔ?jcw'Ռ}+yhkHf~E t7 l4d\iFV4{ls *}OB1A3O)|6*dm"3k2uL.X!׽gDAWf0GayDA~LA%VL'$/IOeP ]$'KCyA\x 1qâDV<77AE; J _Vc:hk*uTb>Ӹ^l5p昙XIA-=Tz* ҊB:VT$ddtYlWNm0S6 .BxB(9Y~7b? YMYdgZn4Q\(Z[k DϐYz^h5IbZHzFDni 3揎-J(i< -D,D@ix|<˰(q;.q\!1mI[DҐ]w'iz`o SK }(%_ lMl-+QEĭ&ԚY ? b >dI&%ܹj; j][uAT/!TeS5͝H־NyDxg}^:2PTd۝deSP~G憁ii}aR5Ӝ^P[恧2uܸ p㸘* 'zOKPT)YIZ;)gc"f}2 e.+)sFLpN-+G!˭k!ק7xDL"bKD D D@%_ji}Ӿ8cIefSO ipMz{GOB,06⌜"* 1Z єhȉe5"֚C͖@>'Kw1slbv IqܴD"`@<.fna.f"-e- |Z\/s)WHL:![CfVT8ꑽ?=xmfW0Y7.V6ˋ%c[|gk՛?x{~uN2oZDJT)u!$/҇=TM=Wr٦yJL,}0 5C WqVrq:$!.zh2D8pgF*ZÆ6: I3R0IT%lk>f|r5C5(厣 _l)Q4V7[47ڙ$GA#6 "l}q2 MdZ'.MiU5(  Uo$J{dEd}R2٠dS,hB]AO Jtt3Lm:vKхUᔓ)=UM[ F4nAq(Z@r SVڛk]jT12D,{)y;t 'H ShYr劼B+iS{tdAO'9b " >d [ZcX#uܺDjHՌj[v sRoHn_;]iX#~W7El+ mqm>- "I )2ِWF' 3sHӸv: n_{;r,Q2Z(dűA mS4-1̪ TJʶg(&sf ht\gu/%"kRRݽӯ_Yĭ!J#ck0k.;Bkxgwٍ@6Mc0ڒKf5/+$s$-1 ujȁPB= !*ĺFqPP@1(lY^R<>>Iƣf^0(B 0,B1`do2@>bWN nf͋AٚQJ (/goܶ.@˞ 6{<8:àq<YU{Sy\.[R@%wjCٚcEƴ;Sn`'26XO;;8J;٠c_o !ޗE2kx_o0u7o_^ߢm5[s` W?[I\Qiխ98E6oy@/(B/nˁadh-/ &޷5 }%grPkfGm*E,3Sr eelfc8 dGк(UG(cAW7xt|Ujc'Y%iZ&X@,j$6W7XOqe2AAG*YT*P([|! & v\2K#J5L@ !)8ce)4W89")!Ќfkz@9~f9[^0-,!4lFHy3y"qYo YoU^ CvqW.$o$!yLNq= fkzqs LJޕ!-̢X.rzGZ% ͓kbC6_:BXGl- n  bdTGk&Bؘ[4hd.Ɋ3x9{WOJhهX%ev&*Dq(B,1mږhق z!bl= КE6wDfmiWnř, -"j[|0SLvLx\oǸ[""t/!o7!'x6C3 zhF-&hiHc+|]ҷq)G6\vGk[%94K8"#V h8ZBٚqibGs4>Ia5%ljh-fU\8YkkʷrL-A+F ?M\QY5f LaYtt qbgMKYWkC/˒ ;!zo7!z'x6=;!Dњ9<E[h{ٙ)WU|d:hMS惏vzN'd"™։${h+vh}Is4{ B!lQ(Z,hhbm/c_H!\.$\F$N Ee GnA7˅`%.jgl&A, Gփm\oX*8m%, Zvh蠝zN^/V⢂?a&ͣ 6.S`#mum*uF,^cqp1hv[J6c|x~~.XK0y|khLT!PAzPJӪىTn!z Nn6ϱrQLht(Bt79ي2!bOlHBݝkiTVDISh;̗Tq#bk"$fk9uivdfzC&lT)C]K&$Dو UږMnza nTRٔ6@ #R0r E#⊭9F2F(+ܭSzY Y} dj4ijnEEˆ| T!, m!TvBڠ0@7GPv5:di gMO"FiV".i.py 1;q[*].fnIvands|*vEm)iT?l!Aw͆@셒kE36LDV"Dzg=@fk TXk,];/Ct*i+(Fצi%lD)s_,C˩lխ9BuZ/hj)M?l1.DrxaoNbhL]DI9<~2`T\~{f`.vO6&v2#/O$J:[sP8Z1QC:. Ƹ^ F!'[t D {"?N٠s3džf:bHX%jS V#/BIk0gmIYTC!U vX}*հU|2"RJ-Ta6,fi4*> ?s`! ͦd0,QzY֖ʔ D ZԽ{@=H`;2LELk~Kfj{[]v%F -ަ`W͆W)IEÜAZrUR<@JBX1Z| PQm4{`o1}hycV0.qY_Ŭ`#7`+T =E!M= DQKD1꘳jzMf[} |ԴN[[X'[Bf[L{@~F;G;=v*.*`6pxpje[^-qImڰ#G9Rt\/^^KG};h }{Z&HrڕVy܄hjv-" U2Mgqe-@B+֦JfY֨ٚH]rPaQɪ &V"zC-A"n=fk %Tw{//][)֑ppVpWgس˻O͛~?~?}]2O~P,Qf?_?޽_>?˿|xyy.~ .p'p`{`㪯XtK݄M>8Ilu:WOpd|^Fk>]_<ܿLo.C (o}2{\CYj]02fio_>6 Xoo/9pm_Ķ5mvzo*?Xw/HnΉ]%d]Q_+p,P(L?N",ox7M/.S' /&[o.?^=_/Gsw\\yt~tb_?Mtw)t$ssy|xz'|#p d26.#jV(K ; #1ZfL;=no_;osp.ƾS9fwW<*8+uz$[: |;tw)@t"0ϯpi0YjՇ,wPNuaUxvLN<?Qv>d&|,y<Ĕ/CWůrpPp66K_s?_PCYLlGHaBB+hIl*wX mko;/qg>Z[2e0ˈJ)σW>glIeIWp\ă<]B:\"OmlG{sQ|*8.ϕ/d0yã~&]O[$<kx F3*ymg/Gt&\H.~ho.:Bs.=Ch p^ÿWѨ{^&} ޚoC0 A\G}hI0yބ*L'K &7..٨Dh9pBcǏk;>c^~apu.wG[!<zP4xRK:(Qkۂ/*SgΧjxL Zpʗbo׀BGpTPU%Cjڤ_0`ell"^/w%S0}hpT tpCx)iASv;|j@<tzgn,<OmZ}o תB|Z]H O =$v]G,sS,A<[^7wy 9WM>+Z5D܎͜7z>mbFɁé]́܁c)=^/0~ET  G1eZѬ6]FBc hW>'8tެx4>es GqsyOx'M>o{Oû.#'<x|(]BAd_`QکhU#]|I?)h}D!c-mLKЋ=a:;yc]c!0cUԨ#H ȋ a*]k w"5J#pe^$ o (w7*p n.p꩝3:S;ߒZj7!!wL]x'1ы])%:\[W!+|$,Ѱx&a [pyS\!x Vt48&nFl_+8 ![q>>AL\u&ï.p?#Amk2Z8g4yFN분[ U$Q LK(еP jbO8~ ռNj%1l&5vU^.& "qxpp:ޅ 6!_r1ac ط Cjyl+C;  ,,9tr39:@uL@61[~|`̼1=yR͑nw<I?II.Cm=sTCw3 LxO6^Pģ x0bX"3@WBd@b2lp3m=@<x1& 5xe&CRh61͈Y/<P#=8 \(#9?0A8t"$ Jcd%C N-I%vsN?s<<d. tLQDFR1uE5eP05E%4%fT?{&>! O$,<#3Q Ҡ O YL?LFcRq4Y"@TzПb Spi d p%iônRt3G{/13UBBsj5ϖB1d޸7N@ww X@,5;d6P {7X. I}-_^#1x$a T( `=r(HA#h|Pжr*w->n<tDtpxܥ ƟYVJ>8 ?</D9^%ݤ4_@^A.CJ5)00B_nvAȀWZ8'C< _@Sh>1*HA^g@ F$]!:,>pc; ~DzP`luPցO[U ZjK!єЭ5!zA| Bt8=Pn\lg!lH;0$ nZ $$5&FAc6"J@H _xԮX!cD l9#$pc b#P2~c¨S ,1& Xd@8HQAOPoxYX3sMO ceX*I""/^cMrDQ'(X9Pyf !n:AjX!y OW&DFysY#5I䜂Rw^S~2N "kA?aIV%߄nC! t~pR,WuOjpCp.H4I3LhDqh T0J@ A./plHu:65s@W9mM̳ lt&& wLV%l]^yk+r)a f\聾TMGpQ@/i0b+NQ%+ uh^@=n :+vP$55PJ53e,aM b 4>DGvg':ij8 kF%y2aP93 b%',CC_ y4*wd@^9 P0T Tkxy t ~ ꎯ>XNMh=@)_x `q,ACpNM~j2>ܬXF*ZB?aI`9& ? '©ߪΡQC2f&5ɺLe"37Bv ws MMݍ7;Cc,¡*.R'`r BB%0Cd-CҠ=@ǤH_> qtQEMR^>( :>ԱU ^)+Z?~OURa(p,#ln*OĂHJg|J0q)ÿ %Ǽb&} rj`TAIŮsh>o AR =e%v#>-EUHy%U%!68`q !APGe_EdA5>,s@#aHӰ(k)q S_ayJT` ԉ&y;*:$>|xh 5J?)1Ky wA95I+ۣ܂%ౢL.QmX8q]ҭBV\RBRT4*ű] .oXry,ذD"]u|Y"%jIL_<8J~Np.A۞ϏAe΀1d1AЇ4i8 Hw0k q?3hQaB4,DT@U:1vX %¹STʂH5(~6s/=a#@-3-`1x;Y|whrt3+ED nx`@0cCbC:ڣQ;E!Ft*M`ϩ\* Cp6;}^I2b *&D wY7=@pl)]cB Bl?f _i߰ :$P8/Sj$~zs+ pO2i0ƠRdm_e|}U>*paМ[&pO8DO~tW@O80hCDH7J@I{-C4x[JxXe*z0swX$1sDŰ逛 !1sGm,@gs &AhLrP: A {BXW0$'ڍ\zXNzz,:=,hD ,fO@*"" 6?Z6ʴto4{ru>ǡXD'P^4 @;ѻ&W75?=JmݓovcS{1~8f8 1Zc'R!Ce!1[dAN ^ ܞ@܄q3ءe0^H8j " >/Xj]O4y%A'S'8QbȦT1QwNo  Μػ?8F*R/̉W8RY1L%]-% OlJ`7:>̏*ڒbTyj*AJ+OIl-RCrp aKw \h55嚵kSK1O5eK˯ˆZCy3Tou0bHhu,{V8ѦQMQܨF̍*xͨ uͨ-F5ʨ$G:p$od4/ncMDpf(Cq# ia\b-3%es&k򇨕?S=v9e!=&<:?ơ%,v2ׁ(H?| Az(Qj~ ?R*/4SfqJ3<4DjdJpʬEl7, C9ߡvw} >4+Tbveec$*:$vq2'V0E}B.&1Tq@xt0;ѧɒpEU5Y _.EW닺Y//洈T@aɜJ=,7ζAX  :Wbl5a#oJRJ }tk9>C 8/0}UAagT7qMZx퉖ߞ\Ӽ;zj8,Y'PC|nJʜki稜 !``p:@A|ch_=WNq枏'Np N|O\b٠j9zLO}3$=Ͷ9JÿQIBA&5c*,ةEq/f7~j(*Bk..LᮡJibFԄͱ܂Ya:9q؏Rij{JS d^ nYt1īJ;Y0K]y|?]|={O<ݾv{|._~z0zѲ[\ϗ׳Ϸ3·wXLg!֯SXw oY ךӟ}s o\Yؖ ,:( ;V{ܻ-4j7.ÃZ}\//De!n`O?-gO7|(%|=܇.[ϼ#].zBnO;,F 2cNEV-G5O%<+q[..,cA8ZQxΕ/;8̭{+ﲝX9$B0m[,յZWNliQ8Sm}W[Xh\z3~S؝q9oC!5};?j38&}/ثS+U-̩l\t*RNSiֹK!x5tOrrhx}ܫx 26 8/*W[@zu6sN%Awl[_0e+v,kFNNq^/@,oo;:{y4i_%y~z"Vy,nLޤ!qll׸_י^Y}~ _'+(a4:Yr,?<]N5H̼J̼JKm<63|ͼƣ3sM\K5[rͼF7z ꞯűyukkG!η:߄Z RPT\dUC7 5U7Cv}Fsb4֠h=I*Or[@bzoaR]ǓqxRYrUIu'OINGIFIǬlqXΞ ͓þz$}eI=iqWnܭFںnU&p?(=H>VOj}}[7>X3~u\/.ffi) Cs)BJ)0nb,e_Acp_Lv?UȶIo.?;]q񗗧ۻ8?>.:UWj2zUS)uQbӺê9~ 8jQtŪ4uP1` Xx1Xn])3uRIkpodTh [8ME;/l3!af[b1vybqY{ zI[ if/$IT\M,8\E˯W"n6YkҼmv-I_6`×!|]lj-.K[1;kin&ċ8Ftc$3lwm}:2.+[s|W;b`%.BL*duPHz\b/~H{Wc0ml@x"\E5׫h[[b>β*:UYsʁSQ8% Sʣ᪇M"ĺAn&Z{~`+K 081gnn}[s;G$oL!zpD9rqZ{MƵk m%o!˓5 4r{i}WX .͚58Zf43Λ-hh؍'&TINhv-0K lڲ3 =]9sӮy詊6xGmk:vYz;-.X*BD )aFEOH5ut,QC58EٚcW-]D+޿;r`kzU<?#՚=bu!t fQΔf D=LҬ&>^?p!?^ҎDOq$kptfkVVGafy!Y$n_ $"d l \ /=z!2}W~Zn#'qÖy;N^2`SPvbgUƌwK +f.-F:H%S<W=g} W띴޾A!H[o&曘h2_ۺTm ̼yzZkx艻vգ'a_^ /PK8DIhChaserLights.inoMO@s;-_m. 8TZ3T"g,}݉+eL\ ܢbc,a֝ ͣ=<dtf^t oڍ&{ }%77&=1Af b`'^ܘCOXDJbRFS @ GRtKiۑEurb]ژw#Q;z8rb䮷/_\nK['ņB^QoNCŦɭ5=H)HK<2u{o݁OeMx;Y1 FYܤ5h Kc2m3g_ >b2PK8D\ʑChaserLights.fzPK8DIh\ChaserLights.inoPK{^fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/Fading.fzz000066400000000000000000000304301272363601300320410ustar00rootroot00000000000000PK8D.ں Fading.fz}rFwjbg"UtτݶӞqx잳 cԒVΓmU~ EPn$FO TUVe /lһl젗OGogI;w,u=LOJ>GBz>=9whUgzxG/G?s1ΒL|rt[7'[_tNGzGӗ&uN3:;J#9E+-zq>̇gطe8vhtX. WR;ʇޑxko2\ O⥳|/b{6.O7 /ᙗ((e0fZhry<"q'z'l2M1;Gè8}x="N'_g(T|vm>}ggx[UoS͒̄}ݽ?~Ԍq+^ԩQALczE VT$j烶ϨTjΩ} ƷPmh`ڊ>Pm۷҈c?XýAfJ/ZbMnL||0Gʚ^WQ0r.|$lt;\מ|jlUuF)Х lѲl~7e"OZܼAZ\ßx-oIی(!+n!{Sk%ɫ9t\ZC9_DSKy3 |u4w'/UmTg&+$~]PEdE yS+bsN\n_[dϛW"o6b,ک SVE+EyIy3V;WY sb?wMN ľb>|2k~g^Ex&Y[p{Hg9T(y iI3fƄdf}q9.zg/,D*XXAov>h _@q~R9fY%KQyI畻9$E8$-7\ۺJ0PF}+e)0Ap_[OI`+B ^IPnYgRD7nZc%) <n6bdKÑVo#O@[HKky+c9Xc2{OۅY;.<Zx< yo _%_fZ6XMf ;T]5n˒kYLF>m;{ffaf ium"6Yk4lLK,j,EfM90e_x0S t콿?˒(,x/qFXKg|r~e_t-%I oo Vz\ѵ9kBkɍYkVEu7 >>^tLE:R%-7=V{`CҷhUzR:׳o,{Ӌ|6pDAr*ڷdg{^i7Դhʴ\>;#wץssӓ|ǣ׸MVf /Q?ӳ?mʲ:g䊅M*ҬTZ#x6?qLKW_Ascg"<[j2LOvGҩY)iSLKRkS˷fgypt!7Fi۲q=Il%OJOLF&gAݒnM%cq[29mJ㑍8dwqz4wqR&eNF-2z32J2LnlC/ڻPψʶNIbĒbLt<--~6B(tu(Ҫݜ0?}H/~J3?,K~< 7 YG rm3GQwD|5 ߀&ݤڊ>i{&mZW0&Ti\ >vtm-]Ԛ{|QM*0:캴 ttѵ]Ǘ=`ck;g" 왲o2kP ;6z?_kCn _IrC)HlڼJbV1Wձ"Iv"_6kKEfO7/@D|OwteQ5c:z]e-Hd\fRǕ޻e6t}CڭaL1JH#y|@HM|.{>gjr@]R&pe:0#to̭ sndΕ;ͱL7*Z:6¼׾IEϟ-p.nKp-@%|X]G'try|0YYp.3rRSneJ^$("w#E-޽B$^%|/D tmhұ<>tpoޟ}ca R 6~41>_#A$ؙvBӒx#D,`Iڮw?X.fmn/ѵHGV[Pѵٯb?c韱Ն4l|_߸'͇)KqRLߍeN=>Zm+7ns:<8Yyqyx6.^]̖>].Ͽ >|~l~20KS4^졁eڕ~;X\ﱞa=.'Ͻа(['/F<^-H%xgJOdx2_qa6]&C [oīکw 8u6>:r0ē^_/sig~ϓ9֛.0,w*I~*Nܨb饓_nۇ&lćd'?iC_nTv*4v֚>4 +M~<lJ?'y "KhsḓN"I%˄2瘔.Wo|Ϧ/)OGW}_կ{~۷?} ~w{<:ROǣ^5G_V$408\x zܤTgeI㴗w=_OzO^TtO  Yh  H+W By0`=*P.7v?z;^aA!`z9p|v1-Jy5~ q4dkEQ~ u\ʌ 뇀:,~}qhruӛNIc2|/Pf?.VP%3䡈))j7ba - f֎s|T2^2 E-O1̆0!rD- M.Vj 3F_Z3VK.)zWՈSJ%&^Df}/()B^'u&6N ?[xߡ?+ Qvf2xwE*#xL_i<-rY_eW4s0{i\蟶4I1 &邶A\W}_1]Ùq?0r#]ҔϰMG(/xӛ'HL< pkWpU\˓y;ѾCuWZE^62\Eg5gF5 }x|{ U-}Nh~0y0| cz0"4b6XU" dBiPJf!ᇉAʵ \}Cҏէrdʫ#=O}$F- 8ra@`SNe L U:N%>46i& wqNcKOcF^pE/v")Ku(BtrXNT΂ S(D5jès< tYyi17bԒ*˪B<)+_! =T 2'be-OZSu+ȗ9W}-" __KtZ\̏21JCET`YS`WS`^)p0B Jq=:T]󨚞U=4ԟ] 8h*HaZ Vrhft7}HP" uoOzrR5+_&9@0%c,:JXiqeKJ%_G/Aq"?E+. @DcB3(=7wI&|A:Qs!A=ѵFW x( lD W`\Qs xtԖP Ue|"I&s5ŨMDb?p8AڅF8r\ؘ"QF!5<6IPe }cWN,$aAeFcb &丩 8wGQjkV֌`Y +>ۓjq?+f}opAd# ɋj&UrAN!kfތt#BHF ,p Pʐi  Ii,c[l+" HVx臣Оi{ &<d&tGIqeTPr6!, 4gSg6HN$3sȘ S@Bޙ 6@YCnHٝ#S# ! DV1U1(o订݄=BO{J0ܑ| M*19 ?QTƃCuejTb @%58, Jcd!'D̜SKXH)ͥ(HfY(92P jR4j#05E%3P2ܶ?yۓ Y]8< Gyi E8i W"4< AKvꕐБl#(#xjrTɞB DW$E %iQô.;O ̐E, 3 Eb\dXB!U*ZGqBWp>a[u$e2")oUĄn"F$)@_e@& d5!& aH [b$'1}Ep"?+-@OP?x1fe֔ #˒"H,21͛E6d6ȋ"]D6EKO噍N>@ n,(4VprhYcmcf_ׯ fICjb3L]%{'Dq!#k)EOzbX0OEݤ'=ݣL?))R{a?\'uEaMH:p!u@y~Imxua`J8#g'+d GQ Ժ<M +&9m-g#٠>2Lr{ q :x3э&93oMM0_= 3Y [iC8\O! @x}F>n`e|̈́@= TM@=nJ# ֌J@9hϔa"sWG/Ypj;9ّQEtABLZ")c9=9B0ڧP}}M ##<;la 5 KEI?NcuDZ¥st<R3Re"ROBL##P~TRIO5xNm"!K*bH"Iq p+r~OGUԨKUٚd]DDTI(tbYI(VDoڽШw]J#pȊK_7xLؕC$HIA@d0|4gy{E:| qtENT&3Rt`ta%'.^1ڞJ` $v8&2-S"F"1b$2_DN?&qGVc2$eL+T%WeCH.aȠ$4dm|x[G"ͼUnBˆY@8t&s:p0(aM" =;zHdDE14'_a 5P x7 QW}dNcz4AlK-XDsC zn!c!`h(CgQpRZOL>aL4'H^aU? ,btO6ZRIWsdtcPʍ՜X1IM)JAJHJo"$ b,Q#?L$7HGS 6.URThW \y bk5c1ky01L86PHjȕZScY\k[>f)Q_\.jj 嵆Kъ-#a˷։ ljD%TkBkB6Uք*K:1RX3a2 "!ϼLDf(`H^vZWrj}#) =I#eeH=aI"M ԍ$XF8!_j0%ra *߉;"rj'/s 8b32VC"Uل"W L,0+ɽrZ5ì51 ܈JޘÕ,04ed.$v?A7G.}ptXu/>j"4q od] QLD.ڍ甩<~;?ci i0yؓw"(C)7TG p^cϐiAJWM "^ ԎEJR"cKŞG0﷕ u4U' .˗ȣb $*NtH8]Z dݓrA$yc:T?t;L'QTlQmVkU[t5[5[rgJ[iHR-ܒm7m|Ab^U {1%<&1TA>RxcRP>^㘖2!&b%)$yppۘkhؾ{r{o`4osCt!& bi7"JF%E":<%&WQWk ^wY[=X7J$,KċUv)J#"p ,uM|-G!ls}dNQE 4 KE Põ`']2rP\x_D9QsīseSV+T,z%>T3O"߶[a s ~x+.F&ZMl0|2,0jQӤ_24{Eg]+R7κC׈&Ǔr$kamWjBnATd(N A.^}: Ŕ.p^eaQAiL7YL/=)GD@8&"8i);urAڙ'go|b]}9;a>^~OOi4La OãaxRoW9O_+; zYY7fuK2Zவ az}M7j9F{{^[hhӹ$#ްܭwNxV@ӛt-~o!A'N(c})Ax:{/?]ݛO|ԟNCOc=|2OHwAo74\Quө"GꌽnCp8zg{4qA['q߂gO /Bؐ ¦2LZϤ Oٸn>q 30h-7z&OXM;4w0j֍tؿyr#M6nGU;W/0isE 'xx1>v9Q&b7;Ktُ8>^RpWH_ s@J3*}uGŹOs_5Nwy>H-Uیx&R U5Zvc[޾XşѨjOF7h*xxk0=^Aӝ+|߀擝A42{Ov Ov‰f1 iĥYYi,.\=̴񶎗?u6|LIЙր>v <ym >Ot:$D[pcW_E9+n= $ ;/yZ_CZ<9oEIF˂>-eV4c#e-O~t5+R2rL >:J౏9z%8A̮pa[mG7G>dLuDO,}^B5׭m'P>-7-hg]Q30XCIKK)Iر[BP#Y舗~da,a= ;|<-p|gԮH7cH3Iq׮)qR"R#+۱m5XZ[@[ٸ$(HGv_&E8^PFhqFZ4xYn!O8"M!<;V^ @PK 8DD[P Fading.inoRN1+PXCA/*=x0zn,4feE¿;E A/{o^7(`S)uxv 9T[/8yg6>{RAƥ$ăW뺗7lvlN̓r?Y%K&Nd5^& Ody \{^;XTBP+4Ty˔sJ%:zѮ"~1?wFv W>-)I%߫Hͪ;D-e q,݄:;oV'!I֣ZaW1謎xAݟh$*&ʕR PK8D.ں Fading.fzPK 8DD[P /Fading.inoPKo0fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/Fritzing/FlipFlop.fzz000066400000000000000000000404561272363601300323750ustar00rootroot00000000000000PKB8DMr>y FlipFlop.fz}r#95wm!GuִUe;e3'(SpG\dȈ lkUDxOa6}sFSr oĜ_xu;0/w$5H MHŕ<덇s^]LOE0&<݌G˳|<9 ;Wl>_Gz8|sRcR۳h|}3L\qJWY+-zu0~zcז1ee #~q h`/nMwEcz7ggϜkz7 ο~06:{~9ld@$R]ȋK{5l 88ާW确7ʾx>V:ѻܺ_< w˳`d+wֻ~_mezǟ\ &뻷SWdsv0X?ݡ~|jZ3Wןht?4a>uGƃQlq{[`8*6ջ~sf4]7Wƌ0꒘K/)eWȑʜ/wO|*ˋat`rP}yAK廼}u_-[*jg?PoźG\I 2Už>;i"+ʗ?ڈ"A9>S"Rh_Br-+2l>k*X%;JWz KJPj; AY]K+w4}>6^4ڳ͋e-?ԼoѤ&y&]6;1awY-yFOG-Eޝvr^n"t-[mq^kiZ-j[G &S>-6Ax萼>n/e~{ZO6 ")ymY'=-Жa7JO vl*Rtfo\Ee;͉r':;&svKҧDIӹ%q/'ا오O81Ipfc>Nfq%5_첹?f}Z젴O9Aqp*>x)-G넳i9]pH}ˮ)au{ޘlxY~)k`ԋ[}ԕ{™s])g/sY퐷u)f0;'avN'윬O8ܢu&j6'즼OH󙝔)g4;)ivS'*/%ۺxp^s>-ӱmvO9=>1nS3ۘ/ ~}Mx>ͦa>]v?l-.&Y}ZN. ZݷI%5vv(1;?~ g>:i0nWgNpsAzp< ;_ aus12PjxOMM)'ԤB Z_DYj¾I7:_ۺW`VY˥VcN? MAj$eӞSX_ hh EUGXʧ%)~.MW J<,(F|b@R-5w r:Enm4 xv~Vcб[Q{k H:>vh.0nRRKuȨ,1¹F{dá{88 *#6ŷ堆rST~c/R 60%*7ThI X#0"W`vKE=jh#&SicG!#f㙫w%;!_zMYjz7Dxv:_G{\k^F`QNzAjckePEF.~3eV2eʚ wϲ,H-s\IYc7bjKIj?319T T{˶;]4צ mj1C ooؼIX${tr==n\$o?+n*t-5~*˲Yy6}bL*'A#e$D&'bPKc2ؠk򺳈V\tS* rjm Od ӯ/-1ڦ"iJw|*I* FD}vI{QWf oLf!N_‹Rxњ{֢"آc&ϳSiѥsX]߉ lٲ4p, 47D]jfVj-%8[dax[hjR"wE%D6`-UPR"!J61}TWΝgM%kKYLjlvVvLqnDu˶;7O]>R P,&8V?mm!,|T߰0h1آlm d)ڶs]pi*FR%84q(yNSxӏ۶cxs{h4/C Cbд@Spc_<^Z(TN/򊙁7` ߲#5{xRŽ5w v<9vk#`>AZ$,uޅdwY[H2 C>x(-PI W"3VёrYpфT^]]#a_=G3QN[cdʕˮhd%E h5aHJBwڬp$)Y.rnIäӠǍqP,tDx4Nm>y ~Es({.l7i  (h-@mf$TPsyiNBYH<5G᦭ZnXr岫P rO{ڣgS)3nR TA_24EAyKhKǑ1QF}\ۄMϧwaezHO;٣kxއ:Bno3 &JJ!֤R5lDz;Ɣ$ eelD* 6NB:Bqe;w9Qs i)Rە qcSòCRO@ {k"MEقyib3x0b3Аm+>pniv؈C\:~BtVb>(DƜX(D2ny~t[ZRQڙ\Q:lrM-Md>qw(XR0v`?TXl7kӂp 6Lwl Z^q{$jE6 v ilO n`rތ_[scrr?l'4U"~oũ1QW5dtWݙ C8ATU#]w}P42E[Q4f^/G:i_ l zFqƪ֜(}J4TgG*>TeɼkX,CH6# @٧CCF44"q x ?:r2 E7[*?]$pn;NLN#y9HFfF,[y,j i"y;W$<)fZi$dFgE'"3[n-*7Y̨EX*`DD"M/*ĉX CĚv8"'zޓg_*2aGKJf`hSCWL:J=42n3Zs qCe:&2X^ ,>-3 pikѲvE^,3k,.A;6iÎHr'4TBXL- kc5qfHuI2BL,Yۊ7Xnֆ+Yؤw؊}ce~2Pd!bUڄiґ=Dܬ+"tfy)rUHj -#7&DnXnִyYyYSyY$/-CY!GV֭IadPU/x qo2(qZ$u@"k;*h5wDvh؈A與UJaRs(ˢS$&NVǖZsQǽk6"~qT+$r;DQsjuA 9Ybb-83"!k 7bN b{ɉ閉Xثڄ@YDŽ؂ 7:#;NK, n)OTc`"n[{,Ѩr{Ozm<ꨗ2!tMذ,ICZL!*2N`Y/W I3F?K:GИ&DR칊M;%55 qhoܥJ V3:pRaI}H3!>u3gu$-ZŊܙ`5O*ݚ8fσ68Dէb5$ҵУQnSXj2f[^\kR;@0j2Z&w4aWn؛G-C  )"+\6+뎔Ea\Ti XRaJ(,LU,5>ClH$gkƅfݒ"<ڲhOlJ*9TjLiιn"xi\A̤ն0ʗ FCd;%Ӛx`p/LզŬH1Vm#ҶhrjOi5ab8gx]wڣ=JH޺3#s;PQ: >>ѵoHlؖ UgzY.˴{0r4<8DB^G<xngs=kzܝ_=_w5ײ:=?Lr?oΠ8|pRv^ౡù &LRI?u86Tmcjy~4A:H*׌;IsSמcc:ةxMv^x˧t_i bQv+MĖr;ًXYvc8RYvI<ĻښK"|Ʀ[flHxCk"V H:;Ja{kSאeBRKj/C ʭ=1 L^,Uk B'6aGdgٙiyzjE)ބRDD:"gjdy1rB- K@Q;_U2Ǔʺ #1$MkF"IkFbښCɋzvi)M3[MHue?ޏoԅ>/7}adQ!|y7ͼRf>(&?{n'wo?L>?cӇsF9w=&tU]M&s&M烈{gIrOO=߰2'φbA(;=SP ƓIe?L얿vYv|y9&NI҉뙳۽Scvl0}TXnl%s|Uu>˾بl4rX8F;$u߬>wvĦ%q1>2_rw~\ )@X/6x,2C wrb¹z a6}MWpz9|Cӯ/{_%Ͽ}o~єo_Z7<M^qr\/V$FwU_^zL&_J>O_'_z)|_+Hz}O.{䏽^T*z.JQ4O H% j @@9NaνB."?g ts1%wY)SGmIT˖z0)Qϴ:LHj8A} aݵu~)Ixj$THTѡ;G+i hwɉr_sꋨ EilKYC!7)DݔMp(5|-MƗVh ^[-}r̿RWRBfv#R.ĉHhwtNYr B Q&\*̽˷<ۙ ߸;Dʵ^@W(UhzHRf OkZIgƕ}aS:ԯhԗ^aܽ2faC(3Ab*tpս3R~CQ1φ5~)_p?y0;yCA)P@ *\ߡoEK"rYs*Θ:#g+RbJKȳʍ?"A$rgo3q5*gNJe*WZԦI~PI'q>bƉwv@w~Dk,# bF%Tp ~ ߩ-x <^PƳp%<"G0ޝ'-82٥G??% && 0R9Teh/긁<:׀"q ?Asз4LEƏb~B_,i]} j?Lwc}!Aנ{Ϳ@=eVjs_4Z ϢB4+} v>S^C vu4,SS$@~LWa|xA/h "-3&vڍ>9?'hM2QBP`?x2 EV ʒEl/7~|@C)C+ f%)vތ8G-ЮO;h{8gr$oNS,dJO ~E=92rB?@zR H]($"/U PVz8h| %q0.7I3 F pqtkWivUXm&2!?{`01*,@A0,~+# ‹KY$c"+)΅6QOFuM qǂb 0O@_qGw7?;x AaGS2eF<Yx4XI)-@՘Y2d h` -ޫ((+) BtjtDu~1Vwbq8څTh_2a#ط Kyج&^!͜xpe 3x [xGK`Inh]aЭ~i#-zMSۧy;RMnOjeX?$tie>!xQtX$S%0el@)FC@w3f "b /@FV$>F9fF:`+ԿמHF4DzФ3y(. N@ f _H#~ (3)g '9߁OU w-`B:$E FQƱ;P#HÛ*R“`a v _!/I!,8G(ueD:GE=@, U_RT"$cȤ9C N- sN&)>a.1@5bdIDi.Ѡ.JA}3? C@> N J >Q3dV RaĻ0VQ^ ҃!N^}`/ 0$%A7%c0, M&eઠ)=҂Xe @m'`3 0U ARޒ|jymx;w!f8*w G:Q,'hHB0`Õ<ڂ@iymC#BhY ZJ׫: DWU^Cg+@{<0]RU4i`Haec3|kUw :0+h7Eo:*Ot h= f -lx `2jkEsYBL R/E1D:ajуVO:~PR SKݵOrB,p$I)H;%D!X~Nmh#u)U``Cɩ={;etp@JF6%9˙$g]$U)F"aøOs|9n0bQ3A;k(P@ !)0X8@Cm @D@p6D'(@E޵0S@q mGl~vCI`]0"-aȫ CĚRx*1)>^Ƣ:w$@^)~DT1FP_S? t1 ~_ N <@hB-Be R/\5R?&G@CF$kW;̩.K bi?"ܗ[:K[H/U1_N<oy}hԣstUD$"^E  /$dћvW><vQPt, FŹ@[!o$!p\!b!Sd21|iQEڣSċkGiPǘ.}1 C56PrBfثRTs܅3 0C1i,ex@?"A)hGB" aAPH*E#fyi~$@_F=$V|x6w"E<$yh 8.5&G(h AQ \Hl!?FV -éahVͲ* G@29y'D}&BA@'UvL .@tCHxl 5BCUc?O`|$ZC]贂i=[иt+9}SprO`37] H".Mq?0Q QDŽ T.p[! x4C% @t7LY1/P%m/`6 '?Ɇ2Ae i( Bw ֆ?cw)=hZ7 3!Xa,D@Fk8$R`NM* ؅!p t$FXE1h|/~)^@8ULal[ujxDnwkY>@4Ag(5#Y`m^l(rQ/C{$v1^j #:e s.1GxQ`E_&eB`)8p$YJ^]N.ӱ9|89C-~P |<¸I>0"<8吨gL7#i hC)Q8(3(0*Pa9N8E&{w @(N5 7B `>xSψoʠ:+3TyˆbPIU *EtLA(8lyocr-/xwʣA(LAb:(B30r_qa2"8?vc. EAdAqj|`2u~q `H0">."#RF]I2FU'Omń%* 2P0<C4 PDʚTxtMY*oܠpVcvL%%;1?)1kX%%3cV gpC+MR_כ.E^jlXx&˕ OGkFk:ckkU)[+zz-܀$uhax6#HHiD1 sFPHm㔏lH0Hsc $C&p 45Xa& &GȌ"0f A` 7/r<jP8P|F thxUʌ}8H f_c =Ak` uD}vtRzHnrFtzS)oW#va~0'0xU e+iJ-rFK ?)4, ͐܌]p 3V2bY'%M ~RzS'p ߃jC[IaR~=މT{K,:8.ER8*WԮĖ?yL6_+P(S5xk|l%-E>D!5TJK -š>Z DBU+* *VђP˒P`kKBPE1RH3d Ԟz:TPF,fhL(7/7` 1F@OH^p =D#7nH2`oxlGNRš(\#)v@qGhjp7!"b&,0+ΓySMa֤Yk@?1 O-JD0Edq.$v? H͙aas5:~3KQa蚘R;0YSdiB & `ɂߔgQ<gqj a$0iڑw^1Mp2A9~W /2SVQf4b"kk<(+z ›GA7dڴ|՗ B9z8/&"#S8HB_q.s` Fݓ`r0åK\oFO$*EQ".(.Ǣ)EYE:E>]sjTh@fIclŠ~ymadn(v)<16!'%}k/C48;{/`0m8 É9!nblc5Ƕ=z?yS;SxFV5$~CS\IШ${δgx J,AEPpBŒVeK͖]U-1M'~b"&%":9D;˒FLpam1r*BTxT:s<垓 vd  '( CxEt.lj 9F,cK O☂V[ k5Oi'"5_a7FI( Fۅ,܀%/g]rC:P45ǃrgkBxkRj?X+4!psLW8G!5 c{I gcB耣;`^fddJXLB/dPI# eʊq6q@/hnD)Ֆ–Y0;odRwHo~ .Gcٓ7}[ha|=LQّgƖf]u-`Jz-ﬤKNqJ6գ3A\&z-ID/eJKH /|Ӎ^n'|Jz`Ǔm/"Km9'}7Pa2#ufR6$L5ȔiftX%qҮ>*Ħ GQ#(Ф}SVG/XmXu7_3?^[yoن+y"oׂL??k'}|bQG[m5 gQaۗcx27~Խ5xf] /׍=qW*qD!G2cd㒸#u3:OL6HbajMߐMP:l#evB QJ#=:"O[E6J[<A? TP:l'evB(2r\H$5|filiJZ e"[kyG풻us_%Fo;I;s7DvxPiL[|bmA;BvMvo [(8(X$ɶhmlshK-[-iAغ4P[8'm/ GGeKK@6Kyj6˦~M&uoVP&BNq~u5QvSHWFHElG/"4 ?!έl{Mfm'>9 Ι{s+[@Hq>eDp3c[#Lf:$4I Eb$Φ gSL탘6!24nOb2nEkj{]ag6=!G$b1wR[DcUm@[g`&C SDJ{uc-ƚ| K`T ( mMN]+;Cia%&Z/0Y'#:5Z6JIo> Gbښ$* ?9\f)5$a HRǪߨTpfI+ $syt>~s6lfAg9 F YfoݎC\ڕ-&dؑ>sE&_q\ A2 :NƆE */,A IE={Ny FlipFlop.fzPK!8Dˠ >FlipFlop.inoPKs@Fortuneteller.fzz000066400000000000000000002644211272363601300334350ustar00rootroot00000000000000fritzing-app-0.9.3b/sketches/core/Fritzing Creator Kit DE+EN/creator-kit-en/FritzingPK8D2" f) Fortuneteller.fzr\Ir0dcL}G$JVϡld xE򐬮~yyپ>ߙ dH$ڌ $vf˿?pqWxNΟxw_??ۿ_~xNj󓧯X}=}q:~>8yvfX|݃'_}'+'|o|ٷ=zWv~ɛgo.ο~w_ɿ|<OϞm{m'ߟ?7_?rKzx鎾zw^zxӷggߖp}b)'\<~ݏ篾~o=x?w/_7f> ?ӳPӷOBi~<9os/^W z_y屖K`/<|߾:DR'>>w^7/.^OO߃^={|n=p?In^Ͼ{o/}M~⇿?x7yv ?~;]ߜo7o/_8o ?y1ћx^\;۫g7n)??f l~wZΥͰ9;:?G~y$'S~cz~y?/~ y͗O7^>ًWn?=oSiE񫇼t庫}}.mOW{ߝͫ?f !'V[,!K[n/u4u{sQSoۯۥIw&.÷te=nϧ=]`5x~~6+nN6?Gx<|KnGZ굗|KuF<~?~۾oX6S-Zؿ~kgﷰs}egʾuJYox`Fr\F؇e$GK,2{.3uo#yj-#z;Y2Ӹ.K/>, ߲>0sd y.)ϕܽq:,gM}e}$|o"ս򾛊8Ja9h쯰sao?  o=^ -U> ZWvvb)i3œ/;;V:d_d}XM{췬wlvbsB98rCʞAp oB\1! ܂ohwj?cUGx/˗kzq%5?~mtȍn{/z7IZg%}`~ǽTz_~mCnu7IYm7IYgIYpCxq}XFqn}X6q}`&;c͸{/f3uh߸~ȸ>6"øo>,_~%}u/>,o7.kq,޾ɗ5,[q}XAnܞ b=aפ(׶>}ayyr{A5q}|[ُ=$nHmrWG }}m7q#exYևgYXgCqOvףoOևe wOևe [f wz@rmW;Qym;QukQq ckAێŽ\{w}{)C`7]r='[۽_*rZip/Sp?y_G{Nԇ6흨+kQ}{pomCn?YϷpz;R7sx劯a~q}_=|w_\^O~w6cy|/~|uvǯ޼oß7]՛go.>~_\q775]rmj٬j\žne$4|#c_/}{*o1u0\=/:?i߼zg)߃y#^/J=8yٳ7̛dz-o}{geг9TK+~~ܽ {Wy%o_Qew~]7zGUIޛ__2>{KLԶ!jsZ$ŶE]nWRe:F_w*m*-ƣ"?@igTW<QUܲy>Ϻ5a|[Gq]`q면=A 3{q :!{ ji5^SWuqY,|8tjqzG=K'A3qP{zw^ݽ B{R$S)gcߺ#;>^+[Ckc(zcP5:󅢿; G%WNj ?|ٛ??{5?|-+k#֗^̦͟N?o^\[|p?ίoZo+./}߼#N^z%SmoxwSGӎ~cS\czL1쬮~'?º1iL}Y'dbrJߖ 1. ~Iwiot;n?:(-R.Z/οųo>F]t Vi/~?'dQ'·A[^&-H|U$j\>9:gʲ3v\)SI%cHAEwUi'i.Ҵ'fy?<{ۋ}O8X&`>}}f5G2O+ i7|A=8y}/'^W鿝䣿/=ӻӳ'OΟog}m'[='YNK퓍p飾.>G唺iŻFNעd%ՏX+KD4\:Wd1[kTZmǨĸQeT_!zA(N÷nj>[z>O-3v3m {|^1eW"cwכ"r*wի+ZH_>e7YO;/-Xo߽ߖ=Xu=`]:]Re#ut_q ҭ܍9ugWO1Hșxoemqh?.ߊCϳ;u77ۆ}?}@!RZ Jc^/YEVyfHsc5rk!h=rA >I=OY%4|>I=>q&MX럞9=ѫ]uwMQS֝3dugRi=%+1Ci_ӺJw @sdōw\`[nIܛ9[];>i{S68q>}xŇwpbׇltmKunx{OyW7`Uց8>mϱ"pU}ƞ$.ӏ}.:h)ږulx}6h#_bVW ;V^>5O|mZRsxul^ئKwz:O:j^ebWވ7c;mN~ǻi?q\csJǕTu9mxo7q_\}HH hn+ѮE0_kuߎX~mioŽ?X|Wv:ͽ{ۇv[i?AMI#Z4{o3s(9#Mof9}'c:ܼnTAEߛHz,߯l>i:_ɻo{Kn--wLQ3[rh5|x;GIڴ9vuI;Iڅ<_zŷT,.骥uLލֹ2>|jLkPn|1H}u׾k"T?+̛aeW{p[괁/Miݯ9uAk#c8V ; 6tqG(0>E`zԄ>¶Q!w )OuJ8.4{*rdIS }1$}L!}UH}(ަMH]kz]$wZټ68FG>-''2mwHQ2.j'j6@8=Oʺ&><>Ma6}[{Q=Ƨnڷ?+y;>W]4X1uyP}ۇ:b#ӫ~-cLzT"GF1)18&mcR5X(> .6.}KC_}DXQ>f[5H3ng}^ b+y+.WuЖ~eW#ƼSCӣJy]Zd3 勍Lictz#FI;Nz? :mقSoz{ulzM[Bp; M۽B[P2cUnkW-$&-cӣ>y]Zl3䋍Mhc\z!FEq)%}f鎛M ?'Sĥ1mُ [>iDPeZ؇CYy7ie]ݱunc@vf\oC o~iǂ&獫kR[8kH5tB܀k/5"w@Q9K@ZSy@Zy=ߺWG-RRS=כּ!aţ&DcZkvq s+ae{-⭩c@vy% kkL6z͕qt(ףVú8RZUm{؟<wE{ߗ${Y=ś7/~W?~^iWĴnuKY)]KjS{>ܯi9 ϱZ>Ijh&齉#9Fl[q9ȡ~[ߺӲwS~+wfWÆ;=W&/QW[>$NՉՋuc* `?;skLc v1'hc;e0śe]7^x7Oモ!zYFZ&S;k{xR {=Fw\z}ݺq>8rLޢ8FdoDdwk ѦM6U(>#1H{׵LE{_BvK +9mKw1|{[4 |wmiR\f652C>D%uI=Eo{Jy>1]?`>)Y$qۻe*ݦ;tGq-cvn>>9mom6ܬ2M?(NcHHOu hP>2ldQ[YO1.nױzݦcsI-cvn>:9nonC7zPJYјG^r=g@XsS]cg @}p[$?6 nj\JoW;zzJ 1Kr nUr?pw#[#܂.%w/~ 7?bg9Fs/_?<~zNGkߟ?7׼f3_y?\g/Wqً'Ϟ_wo/"7ooΟz駟?W=L!eDzXʳ?5$gtRG]W}zŹ_?-7gOw>vm_f쉿/{OLm?߿>{u~ꅺN}EˇsʳBN(Q^v~=89Ӹν?זNm5MSe=M.͘isu(M0?VqzvG:ٽcq柧R]Ԫ]]kK.$.ʹ̈́aJ[rOv)Og>)A石G=nM?O<(5}3{ri BsgvUS)KUb;l=aͺOURŮ)T}׵U]`GKCO3"Lׄ1C]( )1?]>48DDŽ\_b7nMrCS{nm29clG=D7%\tKfZ [ITU0j7v=of[lI+ek0庿%xYvx皵e*khY 3 ڷ1ä;5mk$;aKOl?>N1,iU -R em5)=ԁI_V¡9[a?OeRٖ cݮtcNZO͓^3詒TUբӅT/RojFݪ>lmtyUzG*8 5. ?u9ٻg.:RQҥvQ Cڹ-s$I:*lXY=~F1ti^zh(?}+tw_z6I]#KЩ:qV[+?av&iѴWe&邦 }Bi &!IGID)'- d븰z9Ici yhyu^iPd6$!fe$dX$3+As%q؏e[kgvE;hWK[nv#KL5&lpdJyEʬ(.l,լ9̽IE:JRIwC{v-!7idzYۭmm*%^E>'V%U>KКvuqf_X.Pp?i9S0襌YOM%qXf -&A*=Iqʷ:-fnG$/IU385&#aIES7:2[rjΦ~奻q!i+N%4-h2h$ Pf*t(.p u%V+ Ҡ\߈+it2>>5\0$}vDnm-{,UM~F|M70HFEmw~9mc1Y.Vs,E`.,(`퍬cR}sФ&@R{؆6-Fű]7VSwlsb t$HJPa V#ێT[(Qn< R?R\ٳ1嫌Oq/nm@$i&%NڇCE|BґZ$< cgw$d3"{Cݼ r@U.eQOŞyrg߂265S /=n;5ӦMgG1_̴M\(GXNݩ@MptgpG z̏HBE݈ZD|yr}(fTt@oPHi`dgKIb^.O|)Ag))H,dMY͏l%&рN!ʙVO7܊r!;OK&z$*mCa6yU^XaѹIrGxFJ?b 3=cizˮ{di` xlP 䎦K҈0dCt8Kc  RD&p=(at䂒 }"/GLH~D2,JYk e养Nvl0R}S,B77ƎS%^IuoGId(nHi'Xͧ@ES6<ʤ&";Ws֮MyNkggzvE%h"J M+ۊL'1 NmQމ^ p8b5i[䣧C^J;;Vf afO׉OpRtFz+*!L֠jw^WMEJX@pF)YYA1q+ƴ;Tdg +ۦ Ħ$'WB7=ZIi4=[dVfK* dKNb ۪芊>RhCѕ)W;ur#7&HdqcB߄Q5`naisBҖoMо#= Ƞo𻢪<0\+^"[b+dExtEv.L̯Y#(MNՂ!=OhTɞ4 <.v]G4$Iӟd>ǃZ:$-uT^e._M{iȎ#Z3 b c0AWƎ!kUh#Ų9QU'rWb0=&PdRhUp9U* )LJH"jZmSREJxJ5bq&a*Jٰ +b-k weX&haǚn 5M&|t,ъJ8R-^68hO蓲 sAh-[BX֥fOOaiETlA5mj\BB@itomhQeZNjԝQqRၻ5 eJJNUK*NQI-v][sI{)0 rUszAr>7'u;VB.O{&Zg} 04osQHFؑx,6͖)ض<ա\ɨnXUmOKZEzov\5nH"(1ߘ,b~OfvaN'e@ȶ4ݎwUaz*by}.K[;K2  Y^Gq*Zum pt I|\cLJFyhrNozZrҽmN=-|SXH퐇R227īZ/bD* 5Ϡ#x;N6L*%(H]JEzFyv켠R"rTŪjjc|iLq{^(Iپ҅,LZC͐F S7ɰ`9DfWårǟLMnJ!.(J7i=%}R l2|HHctYLrŎR8OJDcae]iq/ JU 1yOCXPP"P:crv4ۉmR7;V4u fSHL~9lVH:SоXR-83t4/lE@qAڋ:~b{ Ym-90MvØ Op"JN*uN֬dDj_QXYͳj#$N|hOhSJ~v_M %Ao\ Ef#)]'s2j˄讣 cg ѕ{0nI.9RAݳͦѳwຸGK2c-ve`oSi+m<rh*2X̝9Eu^"GLԁ*-TZa ,i7!"PyQ k  \Ai/(Ti*9T?"iSvNVb VlJaB|%)aIOTU-6E^  FN*kg%޼ջZ b5l.hi3uWNxZsIr!#ٚgZ6j8 k*%z5{ó:#NaKX RY xٚٻz@Hch1'o%}!] awL ]bzFYx˖1]ٳ7>5)nRu`8J l,|0؅0]7E`\)ڣGcs@ "\;iWZ ҕ;'T7u=$h(EL(DEf00@A d@t* ( +ս[SB*P h1zQꚢcS=jv1J,\zRĢ-$eefE·!tZ?0cq ip08S"e"%9qilTېϋŋ:8@0]wҾ D`NlyՒ`ܦBl4tW'ݴY4-D2$cj$!E(D [ r&~?y gNUƤ"[kr5.hN­fҁ:cKIBH%H^pY l#r"=N:P3nZ el+EBY-H( a{hӶkRa*3<gzcPBjF7O@3A/gwt 0{@W+VK؎^@T;J!+ș>ѡ ; L Z{ID#A{AJѴU4ThijcdUΖ\"=ᐲwj*'i# EƯlzycH|f ymz".֙3a r<NQ$Bƴ #A[5BW@mE8H4  DjPE7&aX&:\|_uG,R"⛐ewJPTnT,uOdk7]J NJ{JlJޡrv pT }4+v#jңrݾm~f֚ Mv/[@5ы*7d%. F} nJBX1xbo:(Ӆ F UvQg,9UԺ#LM J]aTx?g vFwv!}Tɖư* 6. R0,aX-4f-Es94[ wMPUUfXT?g.+V}"&oYڪ3sT&dAwkEQfW]%VK`4rZieAqx %MTC1&-#-2* qCx/wT2[M :<&Q4/*#U׋+*ôq8$+9;[py/^тMk$ XHp@MAs#.@5"[QbMq҄yՌZ/=u&7WoBP,XI^ދo \UWr78ՒxTgjQ*ZȚ/P*TwAφQNy3N2=oNPUH),@ltIP@mL8xMdttZ.Gx͹7IG1,jxQ A<ڔwFJ>XPfz pt]-8jdBDxHSuმHp!4+a&f#ç`ڧyc {!K>B'im{$VeۜM2P?¦I_RؒA p&3C;w0x\GPgQFuDc3!*;`qD8dQ uO dl1t SՕpe]@`S섖>I ܴI*Ȳzخ,% q YѥجN:kqzRp+BJ8#Mayǭ+I\eӾ1QpfM4\ՍN[:MV&4U7px-\ r :8zrW|~:XF1y܇3aꊯ ['MLFvT ԍ t*ĘGfw2(=~V%͝ JN[ o FAKlPJVn[*Uj%s첗;8 Rb'G@oDEyu2dTs"g֪**Й-!ztx(<^KPQd4g1|v]⫚3gd5m6G JbIY{c@@o`#{+q1DR DO'A ʡ t HԠ Clt@#éyO/Jѕd -fYN5i@ˉ|;m}VEk41,euws+בR1l=CW J֝_ 7dlҿAPYT ;!DHʡIx. ^;J{9a(X N u<zLѶ8EYlh X1U-Frt)TQu?`QRFP'fXy@chtε`$m,֧*֬ٴBAp R.p-(Ƒ8T8p5H0:]hPzQ|mñ%?9$Z@+Ur#UgA(>FP"j@`mj#:ZVs+N7 PΥ%U~+FT H-XASC(Aj-|L%f39;MN'j m%DR @bsT!ƬtFPU$ŦSk, F˂9KKBTZeB¥%Dg 0I!pҀv:EH ʢu0F{ IjEGPW\D,$Y *RWK/b_3@w!YRheSII~Vh#mv,s_Bʼ0=*QL y^d?|1Avi\ETDNf:.`Nw,Po7w}DX#9) (!?[3sm)evIg:+#Sbj$$03fQ6PKw9JxDX+ ~ L*@'KaV|ԅ%d`SZLCe 8tOigmf&  HZt O3^KJhjsŒq϶Z/Q`<I夦6PN?FV 8]#L$vL֨JHTx@ d3xSH]Ḏ'}Pt`)2cQ: )t,:͐ZQ訓 O*6ê2ӴG ?D@ de(AXy=t>H9Mq 'Z'olMȊ4sOY߭}ynED5 aEi͕@5BJçodj!R#,)A;vo!eFfOKvFrBmTY:DlU`ori/>!P$TMB4o})̍HZ_G/rΡ$mׁ):#,2K#AZ銧'E,Ё*Βmt% nQ0(CJBM|e\}1qp^Yl7;օbiE*ڿBPX+/:A+vsU D$B43Z$ImFLGM^=A\doEƾ9c}\U$d%km!lsҔM'F1`V6y8_ҙ*–x 4 R8'K[A0tFN ؐ(M1kb0>T-boW`f"6UÁ'tպ7* Y`Hh7N؇ 0)4w526gf+rrX[3ֆ&fpp6w)ю>+P@SY!+eHjHg>%a"RC UXiF"hHPZsEC7i.4sSUv0B!CDӖ^´Qq%6:`K;[.$ %1G5g?+prVo^9Z^°T:gx04'c.LPAY!N}Pr)u9r ❳CҜ ST=80e &x 7Pe) ƃ)=GWT &Y, n.Cf(:JSV"DTÖg Rf#4>r!Ufndm=j@^U&jcyoX dP"BqJl*N:C "`J;+ F>,0mH7٦شaݍ~沤nNM3? I95EyagTmHKls &zWe'kUs86w/y.r'&'M2^}1Zy'MeF|(B+(th\^QԂbCjhcb3md}>jg𙺱 OB]ħ-I0dq]fʎ;\592uzHPǥ06d;1ޫ~H@1qEjV0%Ǭ- in }Q*lpF%eK{״y~x dn* )BX(&wް0? =ږs"[5m|-Y; tFU8ft (nobz4XJ!mS(#F';PAcq(0.MQKmD0=М8م.^њ(y '{* !*S[Sh7LbԼGԒT˜:E:Yb%ܟL3> =,#.'ԍև6HxPc[<Bړ]O,Mh4@m+<݀q4Զ;BF&K\鐺 ;SBBeK>ORo¿@aݲX)m$TBmGDvYiu%zOM Ȑ0<W)th6䲚lNt:bxtt?Gȳ.OhJ)EonMqNԃ!N+!ҳ6@Q.%c & 5/qeY [9G,Y8 VK,'5KH #Z]^} j4d uny@UE`)K ӄx&Q0Uy00Z9t=N}Q$j6DpOoڢ09CMTܣK4iv΢.&+UU̦q'qj,8lq1**]i'_ )!)TA/C 60A]9g6K]W9Q^D$r¤4ȡ'TJd!0X}};L`PRPZE^Gc!>s4ϰ؁D@(KX5K\n܌bo3Gjmp:M U hM?:B%t Hb 7c*)LAU> jӥ< eTmQK [фK5)!Oyl~UR]wGӥ oC;B'T(/*J kpE"ByKYkh ً#Ꮊ$u0{.,N\6yhA 42NGEVv1GQ2Dn5?ֆOapMsfP_\2N)O!P =z!b-GWBqIL)ɝN쮤eiܐhVtvO)os8`.\kЅ^%H :$Hqiz)ʀ"E&9 .'XI8U!0K1 St ldEAXE'"g6wzeyf:#)2ӐOaܶwCU8nnZD^ܬ*OS H6 %VRҘoT_`(ZX MtnHV'ko[DEOPѼpϚd<%UIb)_u,A$2Cl8nʸ ]_)M[ =\<+ z ƞb)f ͯ*c,iM8|SMc6Ft svj$'8SU@/ŋLiJkdzIT\m-dFc@f %"ZPSʆ9VWMM>?Ym2O6Qh/J#rDF6,0<(Xm#: u.bW6qe=׍HK}J\uf0ɤ ի|cAh:\